xref: /linux/net/ipv4/ip_sockglue.c (revision 960a26282f5b1f084313c59d22f76026e6637995)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  * INET		An implementation of the TCP/IP protocol suite for the LINUX
31da177e4SLinus Torvalds  *		operating system.  INET is implemented using the  BSD Socket
41da177e4SLinus Torvalds  *		interface as the means of communication with the user level.
51da177e4SLinus Torvalds  *
61da177e4SLinus Torvalds  *		The IP to API glue.
71da177e4SLinus Torvalds  *
81da177e4SLinus Torvalds  * Authors:	see ip.c
91da177e4SLinus Torvalds  *
101da177e4SLinus Torvalds  * Fixes:
111da177e4SLinus Torvalds  *		Many		:	Split from ip.c , see ip.c for history.
121da177e4SLinus Torvalds  *		Martin Mares	:	TOS setting fixed.
131da177e4SLinus Torvalds  *		Alan Cox	:	Fixed a couple of oopses in Martin's
141da177e4SLinus Torvalds  *					TOS tweaks.
151da177e4SLinus Torvalds  *		Mike McLagan	:	Routing by source
161da177e4SLinus Torvalds  */
171da177e4SLinus Torvalds 
181da177e4SLinus Torvalds #include <linux/module.h>
191da177e4SLinus Torvalds #include <linux/types.h>
201da177e4SLinus Torvalds #include <linux/mm.h>
211da177e4SLinus Torvalds #include <linux/skbuff.h>
221da177e4SLinus Torvalds #include <linux/ip.h>
231da177e4SLinus Torvalds #include <linux/icmp.h>
2414c85021SArnaldo Carvalho de Melo #include <linux/inetdevice.h>
251da177e4SLinus Torvalds #include <linux/netdevice.h>
265a0e3ad6STejun Heo #include <linux/slab.h>
271da177e4SLinus Torvalds #include <net/sock.h>
281da177e4SLinus Torvalds #include <net/ip.h>
291da177e4SLinus Torvalds #include <net/icmp.h>
30d83d8461SArnaldo Carvalho de Melo #include <net/tcp_states.h>
311da177e4SLinus Torvalds #include <linux/udp.h>
321da177e4SLinus Torvalds #include <linux/igmp.h>
331da177e4SLinus Torvalds #include <linux/netfilter.h>
341da177e4SLinus Torvalds #include <linux/route.h>
351da177e4SLinus Torvalds #include <linux/mroute.h>
362c67e9acSMaciej Żenczykowski #include <net/inet_ecn.h>
371da177e4SLinus Torvalds #include <net/route.h>
381da177e4SLinus Torvalds #include <net/xfrm.h>
39dae50295SDavid L Stevens #include <net/compat.h>
40ad6f939aSTom Herbert #include <net/checksum.h>
41dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6)
421da177e4SLinus Torvalds #include <net/transp_v6.h>
431da177e4SLinus Torvalds #endif
4435ebf65eSDavid S. Miller #include <net/ip_fib.h>
451da177e4SLinus Torvalds 
461da177e4SLinus Torvalds #include <linux/errqueue.h>
471da177e4SLinus Torvalds #include <asm/uaccess.h>
481da177e4SLinus Torvalds 
491da177e4SLinus Torvalds /*
501da177e4SLinus Torvalds  *	SOL_IP control messages.
511da177e4SLinus Torvalds  */
521da177e4SLinus Torvalds 
531da177e4SLinus Torvalds static void ip_cmsg_recv_pktinfo(struct msghdr *msg, struct sk_buff *skb)
541da177e4SLinus Torvalds {
55d826eb14SEric Dumazet 	struct in_pktinfo info = *PKTINFO_SKB_CB(skb);
561da177e4SLinus Torvalds 
57eddc9ec5SArnaldo Carvalho de Melo 	info.ipi_addr.s_addr = ip_hdr(skb)->daddr;
581da177e4SLinus Torvalds 
591da177e4SLinus Torvalds 	put_cmsg(msg, SOL_IP, IP_PKTINFO, sizeof(info), &info);
601da177e4SLinus Torvalds }
611da177e4SLinus Torvalds 
621da177e4SLinus Torvalds static void ip_cmsg_recv_ttl(struct msghdr *msg, struct sk_buff *skb)
631da177e4SLinus Torvalds {
64eddc9ec5SArnaldo Carvalho de Melo 	int ttl = ip_hdr(skb)->ttl;
651da177e4SLinus Torvalds 	put_cmsg(msg, SOL_IP, IP_TTL, sizeof(int), &ttl);
661da177e4SLinus Torvalds }
671da177e4SLinus Torvalds 
681da177e4SLinus Torvalds static void ip_cmsg_recv_tos(struct msghdr *msg, struct sk_buff *skb)
691da177e4SLinus Torvalds {
70eddc9ec5SArnaldo Carvalho de Melo 	put_cmsg(msg, SOL_IP, IP_TOS, 1, &ip_hdr(skb)->tos);
711da177e4SLinus Torvalds }
721da177e4SLinus Torvalds 
731da177e4SLinus Torvalds static void ip_cmsg_recv_opts(struct msghdr *msg, struct sk_buff *skb)
741da177e4SLinus Torvalds {
751da177e4SLinus Torvalds 	if (IPCB(skb)->opt.optlen == 0)
761da177e4SLinus Torvalds 		return;
771da177e4SLinus Torvalds 
78eddc9ec5SArnaldo Carvalho de Melo 	put_cmsg(msg, SOL_IP, IP_RECVOPTS, IPCB(skb)->opt.optlen,
79eddc9ec5SArnaldo Carvalho de Melo 		 ip_hdr(skb) + 1);
801da177e4SLinus Torvalds }
811da177e4SLinus Torvalds 
821da177e4SLinus Torvalds 
831da177e4SLinus Torvalds static void ip_cmsg_recv_retopts(struct msghdr *msg, struct sk_buff *skb)
841da177e4SLinus Torvalds {
851da177e4SLinus Torvalds 	unsigned char optbuf[sizeof(struct ip_options) + 40];
861da177e4SLinus Torvalds 	struct ip_options *opt = (struct ip_options *)optbuf;
871da177e4SLinus Torvalds 
881da177e4SLinus Torvalds 	if (IPCB(skb)->opt.optlen == 0)
891da177e4SLinus Torvalds 		return;
901da177e4SLinus Torvalds 
911da177e4SLinus Torvalds 	if (ip_options_echo(opt, skb)) {
921da177e4SLinus Torvalds 		msg->msg_flags |= MSG_CTRUNC;
931da177e4SLinus Torvalds 		return;
941da177e4SLinus Torvalds 	}
951da177e4SLinus Torvalds 	ip_options_undo(opt);
961da177e4SLinus Torvalds 
971da177e4SLinus Torvalds 	put_cmsg(msg, SOL_IP, IP_RETOPTS, opt->optlen, opt->__data);
981da177e4SLinus Torvalds }
991da177e4SLinus Torvalds 
100ad6f939aSTom Herbert static void ip_cmsg_recv_checksum(struct msghdr *msg, struct sk_buff *skb,
101ad6f939aSTom Herbert 				  int offset)
102ad6f939aSTom Herbert {
103ad6f939aSTom Herbert 	__wsum csum = skb->csum;
104ad6f939aSTom Herbert 
105ad6f939aSTom Herbert 	if (skb->ip_summed != CHECKSUM_COMPLETE)
106ad6f939aSTom Herbert 		return;
107ad6f939aSTom Herbert 
108ad6f939aSTom Herbert 	if (offset != 0)
10931c2e492SWillem de Bruijn 		csum = csum_sub(csum, csum_partial(skb_transport_header(skb),
11031c2e492SWillem de Bruijn 						   offset, 0));
111ad6f939aSTom Herbert 
112ad6f939aSTom Herbert 	put_cmsg(msg, SOL_IP, IP_CHECKSUM, sizeof(__wsum), &csum);
113ad6f939aSTom Herbert }
114ad6f939aSTom Herbert 
1152c7946a7SCatherine Zhang static void ip_cmsg_recv_security(struct msghdr *msg, struct sk_buff *skb)
1162c7946a7SCatherine Zhang {
1172c7946a7SCatherine Zhang 	char *secdata;
118dc49c1f9SCatherine Zhang 	u32 seclen, secid;
1192c7946a7SCatherine Zhang 	int err;
1202c7946a7SCatherine Zhang 
121dc49c1f9SCatherine Zhang 	err = security_socket_getpeersec_dgram(NULL, skb, &secid);
122dc49c1f9SCatherine Zhang 	if (err)
123dc49c1f9SCatherine Zhang 		return;
124dc49c1f9SCatherine Zhang 
125dc49c1f9SCatherine Zhang 	err = security_secid_to_secctx(secid, &secdata, &seclen);
1262c7946a7SCatherine Zhang 	if (err)
1272c7946a7SCatherine Zhang 		return;
1282c7946a7SCatherine Zhang 
1292c7946a7SCatherine Zhang 	put_cmsg(msg, SOL_IP, SCM_SECURITY, seclen, secdata);
130dc49c1f9SCatherine Zhang 	security_release_secctx(secdata, seclen);
1312c7946a7SCatherine Zhang }
1322c7946a7SCatherine Zhang 
13321d1a161SHarvey Harrison static void ip_cmsg_recv_dstaddr(struct msghdr *msg, struct sk_buff *skb)
134e8b2dfe9SBalazs Scheidler {
135e8b2dfe9SBalazs Scheidler 	struct sockaddr_in sin;
136b71d1d42SEric Dumazet 	const struct iphdr *iph = ip_hdr(skb);
13721d1a161SHarvey Harrison 	__be16 *ports = (__be16 *)skb_transport_header(skb);
138e8b2dfe9SBalazs Scheidler 
139e8b2dfe9SBalazs Scheidler 	if (skb_transport_offset(skb) + 4 > skb->len)
140e8b2dfe9SBalazs Scheidler 		return;
141e8b2dfe9SBalazs Scheidler 
142e8b2dfe9SBalazs Scheidler 	/* All current transport protocols have the port numbers in the
143e8b2dfe9SBalazs Scheidler 	 * first four bytes of the transport header and this function is
144e8b2dfe9SBalazs Scheidler 	 * written with this assumption in mind.
145e8b2dfe9SBalazs Scheidler 	 */
146e8b2dfe9SBalazs Scheidler 
147e8b2dfe9SBalazs Scheidler 	sin.sin_family = AF_INET;
148e8b2dfe9SBalazs Scheidler 	sin.sin_addr.s_addr = iph->daddr;
149e8b2dfe9SBalazs Scheidler 	sin.sin_port = ports[1];
150e8b2dfe9SBalazs Scheidler 	memset(sin.sin_zero, 0, sizeof(sin.sin_zero));
151e8b2dfe9SBalazs Scheidler 
152e8b2dfe9SBalazs Scheidler 	put_cmsg(msg, SOL_IP, IP_ORIGDSTADDR, sizeof(sin), &sin);
153e8b2dfe9SBalazs Scheidler }
1541da177e4SLinus Torvalds 
1555961de9fSTom Herbert void ip_cmsg_recv_offset(struct msghdr *msg, struct sk_buff *skb,
1565961de9fSTom Herbert 			 int offset)
1571da177e4SLinus Torvalds {
1581da177e4SLinus Torvalds 	struct inet_sock *inet = inet_sk(skb->sk);
15995c96174SEric Dumazet 	unsigned int flags = inet->cmsg_flags;
1601da177e4SLinus Torvalds 
1611da177e4SLinus Torvalds 	/* Ordered by supposed usage frequency */
162c44d13d6STom Herbert 	if (flags & IP_CMSG_PKTINFO) {
1631da177e4SLinus Torvalds 		ip_cmsg_recv_pktinfo(msg, skb);
1641da177e4SLinus Torvalds 
165c44d13d6STom Herbert 		flags &= ~IP_CMSG_PKTINFO;
166c44d13d6STom Herbert 		if (!flags)
167c44d13d6STom Herbert 			return;
168c44d13d6STom Herbert 	}
169c44d13d6STom Herbert 
170c44d13d6STom Herbert 	if (flags & IP_CMSG_TTL) {
1711da177e4SLinus Torvalds 		ip_cmsg_recv_ttl(msg, skb);
1721da177e4SLinus Torvalds 
173c44d13d6STom Herbert 		flags &= ~IP_CMSG_TTL;
174c44d13d6STom Herbert 		if (!flags)
175c44d13d6STom Herbert 			return;
176c44d13d6STom Herbert 	}
177c44d13d6STom Herbert 
178c44d13d6STom Herbert 	if (flags & IP_CMSG_TOS) {
1791da177e4SLinus Torvalds 		ip_cmsg_recv_tos(msg, skb);
1801da177e4SLinus Torvalds 
181c44d13d6STom Herbert 		flags &= ~IP_CMSG_TOS;
182c44d13d6STom Herbert 		if (!flags)
183c44d13d6STom Herbert 			return;
184c44d13d6STom Herbert 	}
185c44d13d6STom Herbert 
186c44d13d6STom Herbert 	if (flags & IP_CMSG_RECVOPTS) {
1871da177e4SLinus Torvalds 		ip_cmsg_recv_opts(msg, skb);
1881da177e4SLinus Torvalds 
189c44d13d6STom Herbert 		flags &= ~IP_CMSG_RECVOPTS;
190c44d13d6STom Herbert 		if (!flags)
191c44d13d6STom Herbert 			return;
192c44d13d6STom Herbert 	}
193c44d13d6STom Herbert 
194c44d13d6STom Herbert 	if (flags & IP_CMSG_RETOPTS) {
1951da177e4SLinus Torvalds 		ip_cmsg_recv_retopts(msg, skb);
1962c7946a7SCatherine Zhang 
197c44d13d6STom Herbert 		flags &= ~IP_CMSG_RETOPTS;
198c44d13d6STom Herbert 		if (!flags)
199c44d13d6STom Herbert 			return;
200c44d13d6STom Herbert 	}
201c44d13d6STom Herbert 
202c44d13d6STom Herbert 	if (flags & IP_CMSG_PASSSEC) {
2032c7946a7SCatherine Zhang 		ip_cmsg_recv_security(msg, skb);
204e8b2dfe9SBalazs Scheidler 
205c44d13d6STom Herbert 		flags &= ~IP_CMSG_PASSSEC;
206c44d13d6STom Herbert 		if (!flags)
207e8b2dfe9SBalazs Scheidler 			return;
208c44d13d6STom Herbert 	}
209c44d13d6STom Herbert 
210ad6f939aSTom Herbert 	if (flags & IP_CMSG_ORIGDSTADDR) {
211e8b2dfe9SBalazs Scheidler 		ip_cmsg_recv_dstaddr(msg, skb);
212e8b2dfe9SBalazs Scheidler 
213ad6f939aSTom Herbert 		flags &= ~IP_CMSG_ORIGDSTADDR;
214ad6f939aSTom Herbert 		if (!flags)
215ad6f939aSTom Herbert 			return;
216ad6f939aSTom Herbert 	}
217ad6f939aSTom Herbert 
218ad6f939aSTom Herbert 	if (flags & IP_CMSG_CHECKSUM)
219ad6f939aSTom Herbert 		ip_cmsg_recv_checksum(msg, skb, offset);
2201da177e4SLinus Torvalds }
2215961de9fSTom Herbert EXPORT_SYMBOL(ip_cmsg_recv_offset);
2221da177e4SLinus Torvalds 
22324025c46SSoheil Hassas Yeganeh int ip_cmsg_send(struct sock *sk, struct msghdr *msg, struct ipcm_cookie *ipc,
224c8e6ad08SHannes Frederic Sowa 		 bool allow_ipv6)
2251da177e4SLinus Torvalds {
226f02db315SFrancesco Fusco 	int err, val;
2271da177e4SLinus Torvalds 	struct cmsghdr *cmsg;
22824025c46SSoheil Hassas Yeganeh 	struct net *net = sock_net(sk);
2291da177e4SLinus Torvalds 
230f95b414eSGu Zheng 	for_each_cmsghdr(cmsg, msg) {
2311da177e4SLinus Torvalds 		if (!CMSG_OK(msg, cmsg))
2321da177e4SLinus Torvalds 			return -EINVAL;
2335337b5b7SEric Dumazet #if IS_ENABLED(CONFIG_IPV6)
234c8e6ad08SHannes Frederic Sowa 		if (allow_ipv6 &&
235c8e6ad08SHannes Frederic Sowa 		    cmsg->cmsg_level == SOL_IPV6 &&
236c8e6ad08SHannes Frederic Sowa 		    cmsg->cmsg_type == IPV6_PKTINFO) {
237c8e6ad08SHannes Frederic Sowa 			struct in6_pktinfo *src_info;
238c8e6ad08SHannes Frederic Sowa 
239c8e6ad08SHannes Frederic Sowa 			if (cmsg->cmsg_len < CMSG_LEN(sizeof(*src_info)))
240c8e6ad08SHannes Frederic Sowa 				return -EINVAL;
241c8e6ad08SHannes Frederic Sowa 			src_info = (struct in6_pktinfo *)CMSG_DATA(cmsg);
242c8e6ad08SHannes Frederic Sowa 			if (!ipv6_addr_v4mapped(&src_info->ipi6_addr))
243c8e6ad08SHannes Frederic Sowa 				return -EINVAL;
244c8e6ad08SHannes Frederic Sowa 			ipc->oif = src_info->ipi6_ifindex;
245c8e6ad08SHannes Frederic Sowa 			ipc->addr = src_info->ipi6_addr.s6_addr32[3];
246c8e6ad08SHannes Frederic Sowa 			continue;
247c8e6ad08SHannes Frederic Sowa 		}
248c8e6ad08SHannes Frederic Sowa #endif
24924025c46SSoheil Hassas Yeganeh 		if (cmsg->cmsg_level == SOL_SOCKET) {
25024025c46SSoheil Hassas Yeganeh 			if (__sock_cmsg_send(sk, msg, cmsg, &ipc->sockc))
25124025c46SSoheil Hassas Yeganeh 				return -EINVAL;
25224025c46SSoheil Hassas Yeganeh 			continue;
25324025c46SSoheil Hassas Yeganeh 		}
25424025c46SSoheil Hassas Yeganeh 
2551da177e4SLinus Torvalds 		if (cmsg->cmsg_level != SOL_IP)
2561da177e4SLinus Torvalds 			continue;
2571da177e4SLinus Torvalds 		switch (cmsg->cmsg_type) {
2581da177e4SLinus Torvalds 		case IP_RETOPTS:
2591da177e4SLinus Torvalds 			err = cmsg->cmsg_len - CMSG_ALIGN(sizeof(struct cmsghdr));
26091948309SEric Dumazet 
26191948309SEric Dumazet 			/* Our caller is responsible for freeing ipc->opt */
2624d52cfbeSEric Dumazet 			err = ip_options_get(net, &ipc->opt, CMSG_DATA(cmsg),
2634d52cfbeSEric Dumazet 					     err < 40 ? err : 40);
2641da177e4SLinus Torvalds 			if (err)
2651da177e4SLinus Torvalds 				return err;
2661da177e4SLinus Torvalds 			break;
2671da177e4SLinus Torvalds 		case IP_PKTINFO:
2681da177e4SLinus Torvalds 		{
2691da177e4SLinus Torvalds 			struct in_pktinfo *info;
2701da177e4SLinus Torvalds 			if (cmsg->cmsg_len != CMSG_LEN(sizeof(struct in_pktinfo)))
2711da177e4SLinus Torvalds 				return -EINVAL;
2721da177e4SLinus Torvalds 			info = (struct in_pktinfo *)CMSG_DATA(cmsg);
2731da177e4SLinus Torvalds 			ipc->oif = info->ipi_ifindex;
2741da177e4SLinus Torvalds 			ipc->addr = info->ipi_spec_dst.s_addr;
2751da177e4SLinus Torvalds 			break;
2761da177e4SLinus Torvalds 		}
277f02db315SFrancesco Fusco 		case IP_TTL:
278f02db315SFrancesco Fusco 			if (cmsg->cmsg_len != CMSG_LEN(sizeof(int)))
279f02db315SFrancesco Fusco 				return -EINVAL;
280f02db315SFrancesco Fusco 			val = *(int *)CMSG_DATA(cmsg);
281f02db315SFrancesco Fusco 			if (val < 1 || val > 255)
282f02db315SFrancesco Fusco 				return -EINVAL;
283f02db315SFrancesco Fusco 			ipc->ttl = val;
284f02db315SFrancesco Fusco 			break;
285f02db315SFrancesco Fusco 		case IP_TOS:
286f02db315SFrancesco Fusco 			if (cmsg->cmsg_len != CMSG_LEN(sizeof(int)))
287f02db315SFrancesco Fusco 				return -EINVAL;
288f02db315SFrancesco Fusco 			val = *(int *)CMSG_DATA(cmsg);
289f02db315SFrancesco Fusco 			if (val < 0 || val > 255)
290f02db315SFrancesco Fusco 				return -EINVAL;
291f02db315SFrancesco Fusco 			ipc->tos = val;
292f02db315SFrancesco Fusco 			ipc->priority = rt_tos2priority(ipc->tos);
293f02db315SFrancesco Fusco 			break;
294f02db315SFrancesco Fusco 
2951da177e4SLinus Torvalds 		default:
2961da177e4SLinus Torvalds 			return -EINVAL;
2971da177e4SLinus Torvalds 		}
2981da177e4SLinus Torvalds 	}
2991da177e4SLinus Torvalds 	return 0;
3001da177e4SLinus Torvalds }
3011da177e4SLinus Torvalds 
3021da177e4SLinus Torvalds 
3031da177e4SLinus Torvalds /* Special input handler for packets caught by router alert option.
3041da177e4SLinus Torvalds    They are selected only by protocol field, and then processed likely
3051da177e4SLinus Torvalds    local ones; but only if someone wants them! Otherwise, router
3061da177e4SLinus Torvalds    not running rsvpd will kill RSVP.
3071da177e4SLinus Torvalds 
3081da177e4SLinus Torvalds    It is user level problem, what it will make with them.
3091da177e4SLinus Torvalds    I have no idea, how it will masquearde or NAT them (it is joke, joke :-)),
3101da177e4SLinus Torvalds    but receiver should be enough clever f.e. to forward mtrace requests,
3111da177e4SLinus Torvalds    sent to multicast group to reach destination designated router.
3121da177e4SLinus Torvalds  */
31343a951e9SEric Dumazet struct ip_ra_chain __rcu *ip_ra_chain;
31466018506SEric Dumazet static DEFINE_SPINLOCK(ip_ra_lock);
31566018506SEric Dumazet 
316592fcb9dSEric Dumazet 
317592fcb9dSEric Dumazet static void ip_ra_destroy_rcu(struct rcu_head *head)
31866018506SEric Dumazet {
319592fcb9dSEric Dumazet 	struct ip_ra_chain *ra = container_of(head, struct ip_ra_chain, rcu);
320592fcb9dSEric Dumazet 
321592fcb9dSEric Dumazet 	sock_put(ra->saved_sk);
322592fcb9dSEric Dumazet 	kfree(ra);
32366018506SEric Dumazet }
3241da177e4SLinus Torvalds 
3254d52cfbeSEric Dumazet int ip_ra_control(struct sock *sk, unsigned char on,
3264d52cfbeSEric Dumazet 		  void (*destructor)(struct sock *))
3271da177e4SLinus Torvalds {
32843a951e9SEric Dumazet 	struct ip_ra_chain *ra, *new_ra;
32943a951e9SEric Dumazet 	struct ip_ra_chain __rcu **rap;
3301da177e4SLinus Torvalds 
331c720c7e8SEric Dumazet 	if (sk->sk_type != SOCK_RAW || inet_sk(sk)->inet_num == IPPROTO_RAW)
3321da177e4SLinus Torvalds 		return -EINVAL;
3331da177e4SLinus Torvalds 
3341da177e4SLinus Torvalds 	new_ra = on ? kmalloc(sizeof(*new_ra), GFP_KERNEL) : NULL;
3351da177e4SLinus Torvalds 
33666018506SEric Dumazet 	spin_lock_bh(&ip_ra_lock);
33743a951e9SEric Dumazet 	for (rap = &ip_ra_chain;
33843a951e9SEric Dumazet 	     (ra = rcu_dereference_protected(*rap,
33943a951e9SEric Dumazet 			lockdep_is_held(&ip_ra_lock))) != NULL;
34043a951e9SEric Dumazet 	     rap = &ra->next) {
3411da177e4SLinus Torvalds 		if (ra->sk == sk) {
3421da177e4SLinus Torvalds 			if (on) {
34366018506SEric Dumazet 				spin_unlock_bh(&ip_ra_lock);
3441da177e4SLinus Torvalds 				kfree(new_ra);
3451da177e4SLinus Torvalds 				return -EADDRINUSE;
3461da177e4SLinus Torvalds 			}
347592fcb9dSEric Dumazet 			/* dont let ip_call_ra_chain() use sk again */
348592fcb9dSEric Dumazet 			ra->sk = NULL;
3498e380f00SEric Dumazet 			RCU_INIT_POINTER(*rap, ra->next);
35066018506SEric Dumazet 			spin_unlock_bh(&ip_ra_lock);
3511da177e4SLinus Torvalds 
3521da177e4SLinus Torvalds 			if (ra->destructor)
3531da177e4SLinus Torvalds 				ra->destructor(sk);
354592fcb9dSEric Dumazet 			/*
355592fcb9dSEric Dumazet 			 * Delay sock_put(sk) and kfree(ra) after one rcu grace
356592fcb9dSEric Dumazet 			 * period. This guarantee ip_call_ra_chain() dont need
357592fcb9dSEric Dumazet 			 * to mess with socket refcounts.
358592fcb9dSEric Dumazet 			 */
359592fcb9dSEric Dumazet 			ra->saved_sk = sk;
360592fcb9dSEric Dumazet 			call_rcu(&ra->rcu, ip_ra_destroy_rcu);
3611da177e4SLinus Torvalds 			return 0;
3621da177e4SLinus Torvalds 		}
3631da177e4SLinus Torvalds 	}
36451456b29SIan Morris 	if (!new_ra) {
36566018506SEric Dumazet 		spin_unlock_bh(&ip_ra_lock);
3661da177e4SLinus Torvalds 		return -ENOBUFS;
3671da177e4SLinus Torvalds 	}
3681da177e4SLinus Torvalds 	new_ra->sk = sk;
3691da177e4SLinus Torvalds 	new_ra->destructor = destructor;
3701da177e4SLinus Torvalds 
3718e380f00SEric Dumazet 	RCU_INIT_POINTER(new_ra->next, ra);
37266018506SEric Dumazet 	rcu_assign_pointer(*rap, new_ra);
3731da177e4SLinus Torvalds 	sock_hold(sk);
37466018506SEric Dumazet 	spin_unlock_bh(&ip_ra_lock);
3751da177e4SLinus Torvalds 
3761da177e4SLinus Torvalds 	return 0;
3771da177e4SLinus Torvalds }
3781da177e4SLinus Torvalds 
3791da177e4SLinus Torvalds void ip_icmp_error(struct sock *sk, struct sk_buff *skb, int err,
38035986b32SAl Viro 		   __be16 port, u32 info, u8 *payload)
3811da177e4SLinus Torvalds {
3821da177e4SLinus Torvalds 	struct sock_exterr_skb *serr;
3831da177e4SLinus Torvalds 
3841da177e4SLinus Torvalds 	skb = skb_clone(skb, GFP_ATOMIC);
3851da177e4SLinus Torvalds 	if (!skb)
3861da177e4SLinus Torvalds 		return;
3871da177e4SLinus Torvalds 
3881da177e4SLinus Torvalds 	serr = SKB_EXT_ERR(skb);
3891da177e4SLinus Torvalds 	serr->ee.ee_errno = err;
3901da177e4SLinus Torvalds 	serr->ee.ee_origin = SO_EE_ORIGIN_ICMP;
39188c7664fSArnaldo Carvalho de Melo 	serr->ee.ee_type = icmp_hdr(skb)->type;
39288c7664fSArnaldo Carvalho de Melo 	serr->ee.ee_code = icmp_hdr(skb)->code;
3931da177e4SLinus Torvalds 	serr->ee.ee_pad = 0;
3941da177e4SLinus Torvalds 	serr->ee.ee_info = info;
3951da177e4SLinus Torvalds 	serr->ee.ee_data = 0;
39688c7664fSArnaldo Carvalho de Melo 	serr->addr_offset = (u8 *)&(((struct iphdr *)(icmp_hdr(skb) + 1))->daddr) -
397d56f90a7SArnaldo Carvalho de Melo 				   skb_network_header(skb);
3981da177e4SLinus Torvalds 	serr->port = port;
3991da177e4SLinus Torvalds 
40000db4124SIan Morris 	if (skb_pull(skb, payload - skb->data)) {
401bd82393cSArnaldo Carvalho de Melo 		skb_reset_transport_header(skb);
402bd82393cSArnaldo Carvalho de Melo 		if (sock_queue_err_skb(sk, skb) == 0)
403bd82393cSArnaldo Carvalho de Melo 			return;
404bd82393cSArnaldo Carvalho de Melo 	}
4051da177e4SLinus Torvalds 	kfree_skb(skb);
4061da177e4SLinus Torvalds }
4071da177e4SLinus Torvalds 
4080579016eSAl Viro void ip_local_error(struct sock *sk, int err, __be32 daddr, __be16 port, u32 info)
4091da177e4SLinus Torvalds {
4101da177e4SLinus Torvalds 	struct inet_sock *inet = inet_sk(sk);
4111da177e4SLinus Torvalds 	struct sock_exterr_skb *serr;
4121da177e4SLinus Torvalds 	struct iphdr *iph;
4131da177e4SLinus Torvalds 	struct sk_buff *skb;
4141da177e4SLinus Torvalds 
4151da177e4SLinus Torvalds 	if (!inet->recverr)
4161da177e4SLinus Torvalds 		return;
4171da177e4SLinus Torvalds 
4181da177e4SLinus Torvalds 	skb = alloc_skb(sizeof(struct iphdr), GFP_ATOMIC);
4191da177e4SLinus Torvalds 	if (!skb)
4201da177e4SLinus Torvalds 		return;
4211da177e4SLinus Torvalds 
4222ca9e6f2SArnaldo Carvalho de Melo 	skb_put(skb, sizeof(struct iphdr));
4232ca9e6f2SArnaldo Carvalho de Melo 	skb_reset_network_header(skb);
424eddc9ec5SArnaldo Carvalho de Melo 	iph = ip_hdr(skb);
4251da177e4SLinus Torvalds 	iph->daddr = daddr;
4261da177e4SLinus Torvalds 
4271da177e4SLinus Torvalds 	serr = SKB_EXT_ERR(skb);
4281da177e4SLinus Torvalds 	serr->ee.ee_errno = err;
4291da177e4SLinus Torvalds 	serr->ee.ee_origin = SO_EE_ORIGIN_LOCAL;
4301da177e4SLinus Torvalds 	serr->ee.ee_type = 0;
4311da177e4SLinus Torvalds 	serr->ee.ee_code = 0;
4321da177e4SLinus Torvalds 	serr->ee.ee_pad = 0;
4331da177e4SLinus Torvalds 	serr->ee.ee_info = info;
4341da177e4SLinus Torvalds 	serr->ee.ee_data = 0;
435d56f90a7SArnaldo Carvalho de Melo 	serr->addr_offset = (u8 *)&iph->daddr - skb_network_header(skb);
4361da177e4SLinus Torvalds 	serr->port = port;
4371da177e4SLinus Torvalds 
43827a884dcSArnaldo Carvalho de Melo 	__skb_pull(skb, skb_tail_pointer(skb) - skb->data);
439bd82393cSArnaldo Carvalho de Melo 	skb_reset_transport_header(skb);
4401da177e4SLinus Torvalds 
4411da177e4SLinus Torvalds 	if (sock_queue_err_skb(sk, skb))
4421da177e4SLinus Torvalds 		kfree_skb(skb);
4431da177e4SLinus Torvalds }
4441da177e4SLinus Torvalds 
44534b99df4SJulian Anastasov /* For some errors we have valid addr_offset even with zero payload and
44634b99df4SJulian Anastasov  * zero port. Also, addr_offset should be supported if port is set.
44734b99df4SJulian Anastasov  */
44834b99df4SJulian Anastasov static inline bool ipv4_datagram_support_addr(struct sock_exterr_skb *serr)
44934b99df4SJulian Anastasov {
45034b99df4SJulian Anastasov 	return serr->ee.ee_origin == SO_EE_ORIGIN_ICMP ||
45134b99df4SJulian Anastasov 	       serr->ee.ee_origin == SO_EE_ORIGIN_LOCAL || serr->port;
45234b99df4SJulian Anastasov }
45334b99df4SJulian Anastasov 
454c247f053SWillem de Bruijn /* IPv4 supports cmsg on all imcp errors and some timestamps
455c247f053SWillem de Bruijn  *
456c247f053SWillem de Bruijn  * Timestamp code paths do not initialize the fields expected by cmsg:
457c247f053SWillem de Bruijn  * the PKTINFO fields in skb->cb[]. Fill those in here.
458c247f053SWillem de Bruijn  */
459c247f053SWillem de Bruijn static bool ipv4_datagram_support_cmsg(const struct sock *sk,
460c247f053SWillem de Bruijn 				       struct sk_buff *skb,
461829ae9d6SWillem de Bruijn 				       int ee_origin)
462829ae9d6SWillem de Bruijn {
463c247f053SWillem de Bruijn 	struct in_pktinfo *info;
464829ae9d6SWillem de Bruijn 
465c247f053SWillem de Bruijn 	if (ee_origin == SO_EE_ORIGIN_ICMP)
466c247f053SWillem de Bruijn 		return true;
467c247f053SWillem de Bruijn 
468c247f053SWillem de Bruijn 	if (ee_origin == SO_EE_ORIGIN_LOCAL)
469c247f053SWillem de Bruijn 		return false;
470c247f053SWillem de Bruijn 
471c247f053SWillem de Bruijn 	/* Support IP_PKTINFO on tstamp packets if requested, to correlate
472c247f053SWillem de Bruijn 	 * timestamp with egress dev. Not possible for packets without dev
473c247f053SWillem de Bruijn 	 * or without payload (SOF_TIMESTAMPING_OPT_TSONLY).
474c247f053SWillem de Bruijn 	 */
475c247f053SWillem de Bruijn 	if ((!(sk->sk_tsflags & SOF_TIMESTAMPING_OPT_CMSG)) ||
476829ae9d6SWillem de Bruijn 	    (!skb->dev))
477829ae9d6SWillem de Bruijn 		return false;
478829ae9d6SWillem de Bruijn 
479c247f053SWillem de Bruijn 	info = PKTINFO_SKB_CB(skb);
480829ae9d6SWillem de Bruijn 	info->ipi_spec_dst.s_addr = ip_hdr(skb)->saddr;
481829ae9d6SWillem de Bruijn 	info->ipi_ifindex = skb->dev->ifindex;
482829ae9d6SWillem de Bruijn 	return true;
483829ae9d6SWillem de Bruijn }
484829ae9d6SWillem de Bruijn 
4851da177e4SLinus Torvalds /*
4861da177e4SLinus Torvalds  *	Handle MSG_ERRQUEUE
4871da177e4SLinus Torvalds  */
48885fbaa75SHannes Frederic Sowa int ip_recv_error(struct sock *sk, struct msghdr *msg, int len, int *addr_len)
4891da177e4SLinus Torvalds {
4901da177e4SLinus Torvalds 	struct sock_exterr_skb *serr;
491364a9e93SWillem de Bruijn 	struct sk_buff *skb;
492342dfc30SSteffen Hurrle 	DECLARE_SOCKADDR(struct sockaddr_in *, sin, msg->msg_name);
4931da177e4SLinus Torvalds 	struct {
4941da177e4SLinus Torvalds 		struct sock_extended_err ee;
4951da177e4SLinus Torvalds 		struct sockaddr_in	 offender;
4961da177e4SLinus Torvalds 	} errhdr;
4971da177e4SLinus Torvalds 	int err;
4981da177e4SLinus Torvalds 	int copied;
4991da177e4SLinus Torvalds 
5007ce875e5SWillem de Bruijn 	WARN_ON_ONCE(sk->sk_family == AF_INET6);
5017ce875e5SWillem de Bruijn 
5021da177e4SLinus Torvalds 	err = -EAGAIN;
503364a9e93SWillem de Bruijn 	skb = sock_dequeue_err_skb(sk);
50451456b29SIan Morris 	if (!skb)
5051da177e4SLinus Torvalds 		goto out;
5061da177e4SLinus Torvalds 
5071da177e4SLinus Torvalds 	copied = skb->len;
5081da177e4SLinus Torvalds 	if (copied > len) {
5091da177e4SLinus Torvalds 		msg->msg_flags |= MSG_TRUNC;
5101da177e4SLinus Torvalds 		copied = len;
5111da177e4SLinus Torvalds 	}
51251f3d02bSDavid S. Miller 	err = skb_copy_datagram_msg(skb, 0, msg, copied);
513*960a2628SEric Dumazet 	if (unlikely(err)) {
514*960a2628SEric Dumazet 		kfree_skb(skb);
515*960a2628SEric Dumazet 		return err;
516*960a2628SEric Dumazet 	}
5171da177e4SLinus Torvalds 	sock_recv_timestamp(msg, sk, skb);
5181da177e4SLinus Torvalds 
5191da177e4SLinus Torvalds 	serr = SKB_EXT_ERR(skb);
5201da177e4SLinus Torvalds 
52134b99df4SJulian Anastasov 	if (sin && ipv4_datagram_support_addr(serr)) {
5221da177e4SLinus Torvalds 		sin->sin_family = AF_INET;
523d56f90a7SArnaldo Carvalho de Melo 		sin->sin_addr.s_addr = *(__be32 *)(skb_network_header(skb) +
524d56f90a7SArnaldo Carvalho de Melo 						   serr->addr_offset);
5251da177e4SLinus Torvalds 		sin->sin_port = serr->port;
5261da177e4SLinus Torvalds 		memset(&sin->sin_zero, 0, sizeof(sin->sin_zero));
52785fbaa75SHannes Frederic Sowa 		*addr_len = sizeof(*sin);
5281da177e4SLinus Torvalds 	}
5291da177e4SLinus Torvalds 
5301da177e4SLinus Torvalds 	memcpy(&errhdr.ee, &serr->ee, sizeof(struct sock_extended_err));
5311da177e4SLinus Torvalds 	sin = &errhdr.offender;
532f812116bSWillem de Bruijn 	memset(sin, 0, sizeof(*sin));
533829ae9d6SWillem de Bruijn 
534c247f053SWillem de Bruijn 	if (ipv4_datagram_support_cmsg(sk, skb, serr->ee.ee_origin)) {
5351da177e4SLinus Torvalds 		sin->sin_family = AF_INET;
536eddc9ec5SArnaldo Carvalho de Melo 		sin->sin_addr.s_addr = ip_hdr(skb)->saddr;
537f812116bSWillem de Bruijn 		if (inet_sk(sk)->cmsg_flags)
5381da177e4SLinus Torvalds 			ip_cmsg_recv(msg, skb);
5391da177e4SLinus Torvalds 	}
5401da177e4SLinus Torvalds 
5411da177e4SLinus Torvalds 	put_cmsg(msg, SOL_IP, IP_RECVERR, sizeof(errhdr), &errhdr);
5421da177e4SLinus Torvalds 
5431da177e4SLinus Torvalds 	/* Now we could try to dump offended packet options */
5441da177e4SLinus Torvalds 
5451da177e4SLinus Torvalds 	msg->msg_flags |= MSG_ERRQUEUE;
5461da177e4SLinus Torvalds 	err = copied;
5471da177e4SLinus Torvalds 
548*960a2628SEric Dumazet 	consume_skb(skb);
5491da177e4SLinus Torvalds out:
5501da177e4SLinus Torvalds 	return err;
5511da177e4SLinus Torvalds }
5521da177e4SLinus Torvalds 
5531da177e4SLinus Torvalds 
5541da177e4SLinus Torvalds /*
5554d52cfbeSEric Dumazet  *	Socket option code for IP. This is the end of the line after any
5564d52cfbeSEric Dumazet  *	TCP,UDP etc options on an IP socket.
5571da177e4SLinus Torvalds  */
558baf606d9SMarcelo Ricardo Leitner static bool setsockopt_needs_rtnl(int optname)
559baf606d9SMarcelo Ricardo Leitner {
560baf606d9SMarcelo Ricardo Leitner 	switch (optname) {
561baf606d9SMarcelo Ricardo Leitner 	case IP_ADD_MEMBERSHIP:
562baf606d9SMarcelo Ricardo Leitner 	case IP_ADD_SOURCE_MEMBERSHIP:
56354ff9ef3SMarcelo Ricardo Leitner 	case IP_BLOCK_SOURCE:
564baf606d9SMarcelo Ricardo Leitner 	case IP_DROP_MEMBERSHIP:
56554ff9ef3SMarcelo Ricardo Leitner 	case IP_DROP_SOURCE_MEMBERSHIP:
56654ff9ef3SMarcelo Ricardo Leitner 	case IP_MSFILTER:
56754ff9ef3SMarcelo Ricardo Leitner 	case IP_UNBLOCK_SOURCE:
56854ff9ef3SMarcelo Ricardo Leitner 	case MCAST_BLOCK_SOURCE:
56954ff9ef3SMarcelo Ricardo Leitner 	case MCAST_MSFILTER:
570baf606d9SMarcelo Ricardo Leitner 	case MCAST_JOIN_GROUP:
57154ff9ef3SMarcelo Ricardo Leitner 	case MCAST_JOIN_SOURCE_GROUP:
572baf606d9SMarcelo Ricardo Leitner 	case MCAST_LEAVE_GROUP:
57354ff9ef3SMarcelo Ricardo Leitner 	case MCAST_LEAVE_SOURCE_GROUP:
57454ff9ef3SMarcelo Ricardo Leitner 	case MCAST_UNBLOCK_SOURCE:
575baf606d9SMarcelo Ricardo Leitner 		return true;
576baf606d9SMarcelo Ricardo Leitner 	}
577baf606d9SMarcelo Ricardo Leitner 	return false;
578baf606d9SMarcelo Ricardo Leitner }
5791da177e4SLinus Torvalds 
5803fdadf7dSDmitry Mishin static int do_ip_setsockopt(struct sock *sk, int level,
581b7058842SDavid S. Miller 			    int optname, char __user *optval, unsigned int optlen)
5821da177e4SLinus Torvalds {
5831da177e4SLinus Torvalds 	struct inet_sock *inet = inet_sk(sk);
584166b6b2dSNikolay Borisov 	struct net *net = sock_net(sk);
5851da177e4SLinus Torvalds 	int val = 0, err;
586baf606d9SMarcelo Ricardo Leitner 	bool needs_rtnl = setsockopt_needs_rtnl(optname);
5871da177e4SLinus Torvalds 
5880c9f79beSXi Wang 	switch (optname) {
5890c9f79beSXi Wang 	case IP_PKTINFO:
5900c9f79beSXi Wang 	case IP_RECVTTL:
5910c9f79beSXi Wang 	case IP_RECVOPTS:
5920c9f79beSXi Wang 	case IP_RECVTOS:
5930c9f79beSXi Wang 	case IP_RETOPTS:
5940c9f79beSXi Wang 	case IP_TOS:
5950c9f79beSXi Wang 	case IP_TTL:
5960c9f79beSXi Wang 	case IP_HDRINCL:
5970c9f79beSXi Wang 	case IP_MTU_DISCOVER:
5980c9f79beSXi Wang 	case IP_RECVERR:
5990c9f79beSXi Wang 	case IP_ROUTER_ALERT:
6000c9f79beSXi Wang 	case IP_FREEBIND:
6010c9f79beSXi Wang 	case IP_PASSSEC:
6020c9f79beSXi Wang 	case IP_TRANSPARENT:
6030c9f79beSXi Wang 	case IP_MINTTL:
6040c9f79beSXi Wang 	case IP_NODEFRAG:
60590c337daSEric Dumazet 	case IP_BIND_ADDRESS_NO_PORT:
6060c9f79beSXi Wang 	case IP_UNICAST_IF:
6070c9f79beSXi Wang 	case IP_MULTICAST_TTL:
6080c9f79beSXi Wang 	case IP_MULTICAST_ALL:
6090c9f79beSXi Wang 	case IP_MULTICAST_LOOP:
6100c9f79beSXi Wang 	case IP_RECVORIGDSTADDR:
611ad6f939aSTom Herbert 	case IP_CHECKSUM:
6121da177e4SLinus Torvalds 		if (optlen >= sizeof(int)) {
6131da177e4SLinus Torvalds 			if (get_user(val, (int __user *) optval))
6141da177e4SLinus Torvalds 				return -EFAULT;
6151da177e4SLinus Torvalds 		} else if (optlen >= sizeof(char)) {
6161da177e4SLinus Torvalds 			unsigned char ucval;
6171da177e4SLinus Torvalds 
6181da177e4SLinus Torvalds 			if (get_user(ucval, (unsigned char __user *) optval))
6191da177e4SLinus Torvalds 				return -EFAULT;
6201da177e4SLinus Torvalds 			val = (int) ucval;
6211da177e4SLinus Torvalds 		}
6221da177e4SLinus Torvalds 	}
6231da177e4SLinus Torvalds 
6241da177e4SLinus Torvalds 	/* If optlen==0, it is equivalent to val == 0 */
6251da177e4SLinus Torvalds 
6266a9fb947SPavel Emelyanov 	if (ip_mroute_opt(optname))
6271da177e4SLinus Torvalds 		return ip_mroute_setsockopt(sk, optname, optval, optlen);
6281da177e4SLinus Torvalds 
6291da177e4SLinus Torvalds 	err = 0;
630baf606d9SMarcelo Ricardo Leitner 	if (needs_rtnl)
631baf606d9SMarcelo Ricardo Leitner 		rtnl_lock();
6321da177e4SLinus Torvalds 	lock_sock(sk);
6331da177e4SLinus Torvalds 
6341da177e4SLinus Torvalds 	switch (optname) {
6351da177e4SLinus Torvalds 	case IP_OPTIONS:
6361da177e4SLinus Torvalds 	{
637f6d8bd05SEric Dumazet 		struct ip_options_rcu *old, *opt = NULL;
638f6d8bd05SEric Dumazet 
63965a1c4ffSroel kluin 		if (optlen > 40)
6401da177e4SLinus Torvalds 			goto e_inval;
6413b1e0a65SYOSHIFUJI Hideaki 		err = ip_options_get_from_user(sock_net(sk), &opt,
642cb84663eSDenis V. Lunev 					       optval, optlen);
6431da177e4SLinus Torvalds 		if (err)
6441da177e4SLinus Torvalds 			break;
645f6d8bd05SEric Dumazet 		old = rcu_dereference_protected(inet->inet_opt,
6461e1d04e6SHannes Frederic Sowa 						lockdep_sock_is_held(sk));
647d83d8461SArnaldo Carvalho de Melo 		if (inet->is_icsk) {
648d83d8461SArnaldo Carvalho de Melo 			struct inet_connection_sock *icsk = inet_csk(sk);
649dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6)
6501da177e4SLinus Torvalds 			if (sk->sk_family == PF_INET ||
6511da177e4SLinus Torvalds 			    (!((1 << sk->sk_state) &
6521da177e4SLinus Torvalds 			       (TCPF_LISTEN | TCPF_CLOSE)) &&
653c720c7e8SEric Dumazet 			     inet->inet_daddr != LOOPBACK4_IPV6)) {
6541da177e4SLinus Torvalds #endif
655f6d8bd05SEric Dumazet 				if (old)
656f6d8bd05SEric Dumazet 					icsk->icsk_ext_hdr_len -= old->opt.optlen;
6571da177e4SLinus Torvalds 				if (opt)
658f6d8bd05SEric Dumazet 					icsk->icsk_ext_hdr_len += opt->opt.optlen;
659d83d8461SArnaldo Carvalho de Melo 				icsk->icsk_sync_mss(sk, icsk->icsk_pmtu_cookie);
660dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6)
6611da177e4SLinus Torvalds 			}
6621da177e4SLinus Torvalds #endif
6631da177e4SLinus Torvalds 		}
664f6d8bd05SEric Dumazet 		rcu_assign_pointer(inet->inet_opt, opt);
665f6d8bd05SEric Dumazet 		if (old)
666605b4afeSPaul E. McKenney 			kfree_rcu(old, rcu);
6671da177e4SLinus Torvalds 		break;
6681da177e4SLinus Torvalds 	}
6691da177e4SLinus Torvalds 	case IP_PKTINFO:
6701da177e4SLinus Torvalds 		if (val)
6711da177e4SLinus Torvalds 			inet->cmsg_flags |= IP_CMSG_PKTINFO;
6721da177e4SLinus Torvalds 		else
6731da177e4SLinus Torvalds 			inet->cmsg_flags &= ~IP_CMSG_PKTINFO;
6741da177e4SLinus Torvalds 		break;
6751da177e4SLinus Torvalds 	case IP_RECVTTL:
6761da177e4SLinus Torvalds 		if (val)
6771da177e4SLinus Torvalds 			inet->cmsg_flags |=  IP_CMSG_TTL;
6781da177e4SLinus Torvalds 		else
6791da177e4SLinus Torvalds 			inet->cmsg_flags &= ~IP_CMSG_TTL;
6801da177e4SLinus Torvalds 		break;
6811da177e4SLinus Torvalds 	case IP_RECVTOS:
6821da177e4SLinus Torvalds 		if (val)
6831da177e4SLinus Torvalds 			inet->cmsg_flags |=  IP_CMSG_TOS;
6841da177e4SLinus Torvalds 		else
6851da177e4SLinus Torvalds 			inet->cmsg_flags &= ~IP_CMSG_TOS;
6861da177e4SLinus Torvalds 		break;
6871da177e4SLinus Torvalds 	case IP_RECVOPTS:
6881da177e4SLinus Torvalds 		if (val)
6891da177e4SLinus Torvalds 			inet->cmsg_flags |=  IP_CMSG_RECVOPTS;
6901da177e4SLinus Torvalds 		else
6911da177e4SLinus Torvalds 			inet->cmsg_flags &= ~IP_CMSG_RECVOPTS;
6921da177e4SLinus Torvalds 		break;
6931da177e4SLinus Torvalds 	case IP_RETOPTS:
6941da177e4SLinus Torvalds 		if (val)
6951da177e4SLinus Torvalds 			inet->cmsg_flags |= IP_CMSG_RETOPTS;
6961da177e4SLinus Torvalds 		else
6971da177e4SLinus Torvalds 			inet->cmsg_flags &= ~IP_CMSG_RETOPTS;
6981da177e4SLinus Torvalds 		break;
6992c7946a7SCatherine Zhang 	case IP_PASSSEC:
7002c7946a7SCatherine Zhang 		if (val)
7012c7946a7SCatherine Zhang 			inet->cmsg_flags |= IP_CMSG_PASSSEC;
7022c7946a7SCatherine Zhang 		else
7032c7946a7SCatherine Zhang 			inet->cmsg_flags &= ~IP_CMSG_PASSSEC;
7042c7946a7SCatherine Zhang 		break;
705e8b2dfe9SBalazs Scheidler 	case IP_RECVORIGDSTADDR:
706e8b2dfe9SBalazs Scheidler 		if (val)
707e8b2dfe9SBalazs Scheidler 			inet->cmsg_flags |= IP_CMSG_ORIGDSTADDR;
708e8b2dfe9SBalazs Scheidler 		else
709e8b2dfe9SBalazs Scheidler 			inet->cmsg_flags &= ~IP_CMSG_ORIGDSTADDR;
710e8b2dfe9SBalazs Scheidler 		break;
711ad6f939aSTom Herbert 	case IP_CHECKSUM:
712ad6f939aSTom Herbert 		if (val) {
713ad6f939aSTom Herbert 			if (!(inet->cmsg_flags & IP_CMSG_CHECKSUM)) {
714ad6f939aSTom Herbert 				inet_inc_convert_csum(sk);
715ad6f939aSTom Herbert 				inet->cmsg_flags |= IP_CMSG_CHECKSUM;
716ad6f939aSTom Herbert 			}
717ad6f939aSTom Herbert 		} else {
718ad6f939aSTom Herbert 			if (inet->cmsg_flags & IP_CMSG_CHECKSUM) {
719ad6f939aSTom Herbert 				inet_dec_convert_csum(sk);
720ad6f939aSTom Herbert 				inet->cmsg_flags &= ~IP_CMSG_CHECKSUM;
721ad6f939aSTom Herbert 			}
722ad6f939aSTom Herbert 		}
723ad6f939aSTom Herbert 		break;
7241da177e4SLinus Torvalds 	case IP_TOS:	/* This sets both TOS and Precedence */
7251da177e4SLinus Torvalds 		if (sk->sk_type == SOCK_STREAM) {
7262c67e9acSMaciej Żenczykowski 			val &= ~INET_ECN_MASK;
7272c67e9acSMaciej Żenczykowski 			val |= inet->tos & INET_ECN_MASK;
7281da177e4SLinus Torvalds 		}
7291da177e4SLinus Torvalds 		if (inet->tos != val) {
7301da177e4SLinus Torvalds 			inet->tos = val;
7311da177e4SLinus Torvalds 			sk->sk_priority = rt_tos2priority(val);
7321da177e4SLinus Torvalds 			sk_dst_reset(sk);
7331da177e4SLinus Torvalds 		}
7341da177e4SLinus Torvalds 		break;
7351da177e4SLinus Torvalds 	case IP_TTL:
7361da177e4SLinus Torvalds 		if (optlen < 1)
7371da177e4SLinus Torvalds 			goto e_inval;
738c9be4a5cSCong Wang 		if (val != -1 && (val < 1 || val > 255))
7391da177e4SLinus Torvalds 			goto e_inval;
7401da177e4SLinus Torvalds 		inet->uc_ttl = val;
7411da177e4SLinus Torvalds 		break;
7421da177e4SLinus Torvalds 	case IP_HDRINCL:
7431da177e4SLinus Torvalds 		if (sk->sk_type != SOCK_RAW) {
7441da177e4SLinus Torvalds 			err = -ENOPROTOOPT;
7451da177e4SLinus Torvalds 			break;
7461da177e4SLinus Torvalds 		}
7471da177e4SLinus Torvalds 		inet->hdrincl = val ? 1 : 0;
7481da177e4SLinus Torvalds 		break;
7497b2ff18eSJiri Olsa 	case IP_NODEFRAG:
7507b2ff18eSJiri Olsa 		if (sk->sk_type != SOCK_RAW) {
7517b2ff18eSJiri Olsa 			err = -ENOPROTOOPT;
7527b2ff18eSJiri Olsa 			break;
7537b2ff18eSJiri Olsa 		}
7547b2ff18eSJiri Olsa 		inet->nodefrag = val ? 1 : 0;
7557b2ff18eSJiri Olsa 		break;
75690c337daSEric Dumazet 	case IP_BIND_ADDRESS_NO_PORT:
75790c337daSEric Dumazet 		inet->bind_address_no_port = val ? 1 : 0;
75890c337daSEric Dumazet 		break;
7591da177e4SLinus Torvalds 	case IP_MTU_DISCOVER:
7601b346576SHannes Frederic Sowa 		if (val < IP_PMTUDISC_DONT || val > IP_PMTUDISC_OMIT)
7611da177e4SLinus Torvalds 			goto e_inval;
7621da177e4SLinus Torvalds 		inet->pmtudisc = val;
7631da177e4SLinus Torvalds 		break;
7641da177e4SLinus Torvalds 	case IP_RECVERR:
7651da177e4SLinus Torvalds 		inet->recverr = !!val;
7661da177e4SLinus Torvalds 		if (!val)
7671da177e4SLinus Torvalds 			skb_queue_purge(&sk->sk_error_queue);
7681da177e4SLinus Torvalds 		break;
7691da177e4SLinus Torvalds 	case IP_MULTICAST_TTL:
7701da177e4SLinus Torvalds 		if (sk->sk_type == SOCK_STREAM)
7711da177e4SLinus Torvalds 			goto e_inval;
7721da177e4SLinus Torvalds 		if (optlen < 1)
7731da177e4SLinus Torvalds 			goto e_inval;
7741da177e4SLinus Torvalds 		if (val == -1)
7751da177e4SLinus Torvalds 			val = 1;
7761da177e4SLinus Torvalds 		if (val < 0 || val > 255)
7771da177e4SLinus Torvalds 			goto e_inval;
7781da177e4SLinus Torvalds 		inet->mc_ttl = val;
7791da177e4SLinus Torvalds 		break;
7801da177e4SLinus Torvalds 	case IP_MULTICAST_LOOP:
7811da177e4SLinus Torvalds 		if (optlen < 1)
7821da177e4SLinus Torvalds 			goto e_inval;
7831da177e4SLinus Torvalds 		inet->mc_loop = !!val;
7841da177e4SLinus Torvalds 		break;
78576e21053SErich E. Hoover 	case IP_UNICAST_IF:
78676e21053SErich E. Hoover 	{
78776e21053SErich E. Hoover 		struct net_device *dev = NULL;
78876e21053SErich E. Hoover 		int ifindex;
78976e21053SErich E. Hoover 
79076e21053SErich E. Hoover 		if (optlen != sizeof(int))
79176e21053SErich E. Hoover 			goto e_inval;
79276e21053SErich E. Hoover 
79376e21053SErich E. Hoover 		ifindex = (__force int)ntohl((__force __be32)val);
79476e21053SErich E. Hoover 		if (ifindex == 0) {
79576e21053SErich E. Hoover 			inet->uc_index = 0;
79676e21053SErich E. Hoover 			err = 0;
79776e21053SErich E. Hoover 			break;
79876e21053SErich E. Hoover 		}
79976e21053SErich E. Hoover 
80076e21053SErich E. Hoover 		dev = dev_get_by_index(sock_net(sk), ifindex);
80176e21053SErich E. Hoover 		err = -EADDRNOTAVAIL;
80276e21053SErich E. Hoover 		if (!dev)
80376e21053SErich E. Hoover 			break;
80476e21053SErich E. Hoover 		dev_put(dev);
80576e21053SErich E. Hoover 
80676e21053SErich E. Hoover 		err = -EINVAL;
80776e21053SErich E. Hoover 		if (sk->sk_bound_dev_if)
80876e21053SErich E. Hoover 			break;
80976e21053SErich E. Hoover 
81076e21053SErich E. Hoover 		inet->uc_index = ifindex;
81176e21053SErich E. Hoover 		err = 0;
81276e21053SErich E. Hoover 		break;
81376e21053SErich E. Hoover 	}
8141da177e4SLinus Torvalds 	case IP_MULTICAST_IF:
8151da177e4SLinus Torvalds 	{
8161da177e4SLinus Torvalds 		struct ip_mreqn mreq;
8171da177e4SLinus Torvalds 		struct net_device *dev = NULL;
8181da177e4SLinus Torvalds 
8191da177e4SLinus Torvalds 		if (sk->sk_type == SOCK_STREAM)
8201da177e4SLinus Torvalds 			goto e_inval;
8211da177e4SLinus Torvalds 		/*
8221da177e4SLinus Torvalds 		 *	Check the arguments are allowable
8231da177e4SLinus Torvalds 		 */
8241da177e4SLinus Torvalds 
8250915921bSShan Wei 		if (optlen < sizeof(struct in_addr))
8260915921bSShan Wei 			goto e_inval;
8270915921bSShan Wei 
8281da177e4SLinus Torvalds 		err = -EFAULT;
8291da177e4SLinus Torvalds 		if (optlen >= sizeof(struct ip_mreqn)) {
8301da177e4SLinus Torvalds 			if (copy_from_user(&mreq, optval, sizeof(mreq)))
8311da177e4SLinus Torvalds 				break;
8321da177e4SLinus Torvalds 		} else {
8331da177e4SLinus Torvalds 			memset(&mreq, 0, sizeof(mreq));
8343a084ddbSJiri Pirko 			if (optlen >= sizeof(struct ip_mreq)) {
8353a084ddbSJiri Pirko 				if (copy_from_user(&mreq, optval,
8363a084ddbSJiri Pirko 						   sizeof(struct ip_mreq)))
8373a084ddbSJiri Pirko 					break;
8383a084ddbSJiri Pirko 			} else if (optlen >= sizeof(struct in_addr)) {
8393a084ddbSJiri Pirko 				if (copy_from_user(&mreq.imr_address, optval,
8404d52cfbeSEric Dumazet 						   sizeof(struct in_addr)))
8411da177e4SLinus Torvalds 					break;
8421da177e4SLinus Torvalds 			}
8433a084ddbSJiri Pirko 		}
8441da177e4SLinus Torvalds 
8451da177e4SLinus Torvalds 		if (!mreq.imr_ifindex) {
846e6f1cebfSAl Viro 			if (mreq.imr_address.s_addr == htonl(INADDR_ANY)) {
8471da177e4SLinus Torvalds 				inet->mc_index = 0;
8481da177e4SLinus Torvalds 				inet->mc_addr  = 0;
8491da177e4SLinus Torvalds 				err = 0;
8501da177e4SLinus Torvalds 				break;
8511da177e4SLinus Torvalds 			}
8523b1e0a65SYOSHIFUJI Hideaki 			dev = ip_dev_find(sock_net(sk), mreq.imr_address.s_addr);
85355b80503SEric Dumazet 			if (dev)
8541da177e4SLinus Torvalds 				mreq.imr_ifindex = dev->ifindex;
8551da177e4SLinus Torvalds 		} else
85655b80503SEric Dumazet 			dev = dev_get_by_index(sock_net(sk), mreq.imr_ifindex);
8571da177e4SLinus Torvalds 
8581da177e4SLinus Torvalds 
8591da177e4SLinus Torvalds 		err = -EADDRNOTAVAIL;
8601da177e4SLinus Torvalds 		if (!dev)
8611da177e4SLinus Torvalds 			break;
86255b80503SEric Dumazet 		dev_put(dev);
8631da177e4SLinus Torvalds 
8641da177e4SLinus Torvalds 		err = -EINVAL;
8651da177e4SLinus Torvalds 		if (sk->sk_bound_dev_if &&
8661da177e4SLinus Torvalds 		    mreq.imr_ifindex != sk->sk_bound_dev_if)
8671da177e4SLinus Torvalds 			break;
8681da177e4SLinus Torvalds 
8691da177e4SLinus Torvalds 		inet->mc_index = mreq.imr_ifindex;
8701da177e4SLinus Torvalds 		inet->mc_addr  = mreq.imr_address.s_addr;
8711da177e4SLinus Torvalds 		err = 0;
8721da177e4SLinus Torvalds 		break;
8731da177e4SLinus Torvalds 	}
8741da177e4SLinus Torvalds 
8751da177e4SLinus Torvalds 	case IP_ADD_MEMBERSHIP:
8761da177e4SLinus Torvalds 	case IP_DROP_MEMBERSHIP:
8771da177e4SLinus Torvalds 	{
8781da177e4SLinus Torvalds 		struct ip_mreqn mreq;
8791da177e4SLinus Torvalds 
880a96fb49bSFlavio Leitner 		err = -EPROTO;
881a96fb49bSFlavio Leitner 		if (inet_sk(sk)->is_icsk)
882a96fb49bSFlavio Leitner 			break;
883a96fb49bSFlavio Leitner 
8841da177e4SLinus Torvalds 		if (optlen < sizeof(struct ip_mreq))
8851da177e4SLinus Torvalds 			goto e_inval;
8861da177e4SLinus Torvalds 		err = -EFAULT;
8871da177e4SLinus Torvalds 		if (optlen >= sizeof(struct ip_mreqn)) {
8881da177e4SLinus Torvalds 			if (copy_from_user(&mreq, optval, sizeof(mreq)))
8891da177e4SLinus Torvalds 				break;
8901da177e4SLinus Torvalds 		} else {
8911da177e4SLinus Torvalds 			memset(&mreq, 0, sizeof(mreq));
8921da177e4SLinus Torvalds 			if (copy_from_user(&mreq, optval, sizeof(struct ip_mreq)))
8931da177e4SLinus Torvalds 				break;
8941da177e4SLinus Torvalds 		}
8951da177e4SLinus Torvalds 
8961da177e4SLinus Torvalds 		if (optname == IP_ADD_MEMBERSHIP)
89754ff9ef3SMarcelo Ricardo Leitner 			err = ip_mc_join_group(sk, &mreq);
8981da177e4SLinus Torvalds 		else
89954ff9ef3SMarcelo Ricardo Leitner 			err = ip_mc_leave_group(sk, &mreq);
9001da177e4SLinus Torvalds 		break;
9011da177e4SLinus Torvalds 	}
9021da177e4SLinus Torvalds 	case IP_MSFILTER:
9031da177e4SLinus Torvalds 	{
9041da177e4SLinus Torvalds 		struct ip_msfilter *msf;
9051da177e4SLinus Torvalds 
9061da177e4SLinus Torvalds 		if (optlen < IP_MSFILTER_SIZE(0))
9071da177e4SLinus Torvalds 			goto e_inval;
9081da177e4SLinus Torvalds 		if (optlen > sysctl_optmem_max) {
9091da177e4SLinus Torvalds 			err = -ENOBUFS;
9101da177e4SLinus Torvalds 			break;
9111da177e4SLinus Torvalds 		}
9128b3a7005SKris Katterjohn 		msf = kmalloc(optlen, GFP_KERNEL);
913cfcabdccSStephen Hemminger 		if (!msf) {
9141da177e4SLinus Torvalds 			err = -ENOBUFS;
9151da177e4SLinus Torvalds 			break;
9161da177e4SLinus Torvalds 		}
9171da177e4SLinus Torvalds 		err = -EFAULT;
9181da177e4SLinus Torvalds 		if (copy_from_user(msf, optval, optlen)) {
9191da177e4SLinus Torvalds 			kfree(msf);
9201da177e4SLinus Torvalds 			break;
9211da177e4SLinus Torvalds 		}
9221da177e4SLinus Torvalds 		/* numsrc >= (1G-4) overflow in 32 bits */
9231da177e4SLinus Torvalds 		if (msf->imsf_numsrc >= 0x3ffffffcU ||
924166b6b2dSNikolay Borisov 		    msf->imsf_numsrc > net->ipv4.sysctl_igmp_max_msf) {
9251da177e4SLinus Torvalds 			kfree(msf);
9261da177e4SLinus Torvalds 			err = -ENOBUFS;
9271da177e4SLinus Torvalds 			break;
9281da177e4SLinus Torvalds 		}
9291da177e4SLinus Torvalds 		if (IP_MSFILTER_SIZE(msf->imsf_numsrc) > optlen) {
9301da177e4SLinus Torvalds 			kfree(msf);
9311da177e4SLinus Torvalds 			err = -EINVAL;
9321da177e4SLinus Torvalds 			break;
9331da177e4SLinus Torvalds 		}
9341da177e4SLinus Torvalds 		err = ip_mc_msfilter(sk, msf, 0);
9351da177e4SLinus Torvalds 		kfree(msf);
9361da177e4SLinus Torvalds 		break;
9371da177e4SLinus Torvalds 	}
9381da177e4SLinus Torvalds 	case IP_BLOCK_SOURCE:
9391da177e4SLinus Torvalds 	case IP_UNBLOCK_SOURCE:
9401da177e4SLinus Torvalds 	case IP_ADD_SOURCE_MEMBERSHIP:
9411da177e4SLinus Torvalds 	case IP_DROP_SOURCE_MEMBERSHIP:
9421da177e4SLinus Torvalds 	{
9431da177e4SLinus Torvalds 		struct ip_mreq_source mreqs;
9441da177e4SLinus Torvalds 		int omode, add;
9451da177e4SLinus Torvalds 
9461da177e4SLinus Torvalds 		if (optlen != sizeof(struct ip_mreq_source))
9471da177e4SLinus Torvalds 			goto e_inval;
9481da177e4SLinus Torvalds 		if (copy_from_user(&mreqs, optval, sizeof(mreqs))) {
9491da177e4SLinus Torvalds 			err = -EFAULT;
9501da177e4SLinus Torvalds 			break;
9511da177e4SLinus Torvalds 		}
9521da177e4SLinus Torvalds 		if (optname == IP_BLOCK_SOURCE) {
9531da177e4SLinus Torvalds 			omode = MCAST_EXCLUDE;
9541da177e4SLinus Torvalds 			add = 1;
9551da177e4SLinus Torvalds 		} else if (optname == IP_UNBLOCK_SOURCE) {
9561da177e4SLinus Torvalds 			omode = MCAST_EXCLUDE;
9571da177e4SLinus Torvalds 			add = 0;
9581da177e4SLinus Torvalds 		} else if (optname == IP_ADD_SOURCE_MEMBERSHIP) {
9591da177e4SLinus Torvalds 			struct ip_mreqn mreq;
9601da177e4SLinus Torvalds 
9611da177e4SLinus Torvalds 			mreq.imr_multiaddr.s_addr = mreqs.imr_multiaddr;
9621da177e4SLinus Torvalds 			mreq.imr_address.s_addr = mreqs.imr_interface;
9631da177e4SLinus Torvalds 			mreq.imr_ifindex = 0;
96454ff9ef3SMarcelo Ricardo Leitner 			err = ip_mc_join_group(sk, &mreq);
9658cdaaa15SDavid L Stevens 			if (err && err != -EADDRINUSE)
9661da177e4SLinus Torvalds 				break;
9671da177e4SLinus Torvalds 			omode = MCAST_INCLUDE;
9681da177e4SLinus Torvalds 			add = 1;
9691da177e4SLinus Torvalds 		} else /* IP_DROP_SOURCE_MEMBERSHIP */ {
9701da177e4SLinus Torvalds 			omode = MCAST_INCLUDE;
9711da177e4SLinus Torvalds 			add = 0;
9721da177e4SLinus Torvalds 		}
9731da177e4SLinus Torvalds 		err = ip_mc_source(add, omode, sk, &mreqs, 0);
9741da177e4SLinus Torvalds 		break;
9751da177e4SLinus Torvalds 	}
9761da177e4SLinus Torvalds 	case MCAST_JOIN_GROUP:
9771da177e4SLinus Torvalds 	case MCAST_LEAVE_GROUP:
9781da177e4SLinus Torvalds 	{
9791da177e4SLinus Torvalds 		struct group_req greq;
9801da177e4SLinus Torvalds 		struct sockaddr_in *psin;
9811da177e4SLinus Torvalds 		struct ip_mreqn mreq;
9821da177e4SLinus Torvalds 
9831da177e4SLinus Torvalds 		if (optlen < sizeof(struct group_req))
9841da177e4SLinus Torvalds 			goto e_inval;
9851da177e4SLinus Torvalds 		err = -EFAULT;
9861da177e4SLinus Torvalds 		if (copy_from_user(&greq, optval, sizeof(greq)))
9871da177e4SLinus Torvalds 			break;
9881da177e4SLinus Torvalds 		psin = (struct sockaddr_in *)&greq.gr_group;
9891da177e4SLinus Torvalds 		if (psin->sin_family != AF_INET)
9901da177e4SLinus Torvalds 			goto e_inval;
9911da177e4SLinus Torvalds 		memset(&mreq, 0, sizeof(mreq));
9921da177e4SLinus Torvalds 		mreq.imr_multiaddr = psin->sin_addr;
9931da177e4SLinus Torvalds 		mreq.imr_ifindex = greq.gr_interface;
9941da177e4SLinus Torvalds 
9951da177e4SLinus Torvalds 		if (optname == MCAST_JOIN_GROUP)
99654ff9ef3SMarcelo Ricardo Leitner 			err = ip_mc_join_group(sk, &mreq);
9971da177e4SLinus Torvalds 		else
99854ff9ef3SMarcelo Ricardo Leitner 			err = ip_mc_leave_group(sk, &mreq);
9991da177e4SLinus Torvalds 		break;
10001da177e4SLinus Torvalds 	}
10011da177e4SLinus Torvalds 	case MCAST_JOIN_SOURCE_GROUP:
10021da177e4SLinus Torvalds 	case MCAST_LEAVE_SOURCE_GROUP:
10031da177e4SLinus Torvalds 	case MCAST_BLOCK_SOURCE:
10041da177e4SLinus Torvalds 	case MCAST_UNBLOCK_SOURCE:
10051da177e4SLinus Torvalds 	{
10061da177e4SLinus Torvalds 		struct group_source_req greqs;
10071da177e4SLinus Torvalds 		struct ip_mreq_source mreqs;
10081da177e4SLinus Torvalds 		struct sockaddr_in *psin;
10091da177e4SLinus Torvalds 		int omode, add;
10101da177e4SLinus Torvalds 
10111da177e4SLinus Torvalds 		if (optlen != sizeof(struct group_source_req))
10121da177e4SLinus Torvalds 			goto e_inval;
10131da177e4SLinus Torvalds 		if (copy_from_user(&greqs, optval, sizeof(greqs))) {
10141da177e4SLinus Torvalds 			err = -EFAULT;
10151da177e4SLinus Torvalds 			break;
10161da177e4SLinus Torvalds 		}
10171da177e4SLinus Torvalds 		if (greqs.gsr_group.ss_family != AF_INET ||
10181da177e4SLinus Torvalds 		    greqs.gsr_source.ss_family != AF_INET) {
10191da177e4SLinus Torvalds 			err = -EADDRNOTAVAIL;
10201da177e4SLinus Torvalds 			break;
10211da177e4SLinus Torvalds 		}
10221da177e4SLinus Torvalds 		psin = (struct sockaddr_in *)&greqs.gsr_group;
10231da177e4SLinus Torvalds 		mreqs.imr_multiaddr = psin->sin_addr.s_addr;
10241da177e4SLinus Torvalds 		psin = (struct sockaddr_in *)&greqs.gsr_source;
10251da177e4SLinus Torvalds 		mreqs.imr_sourceaddr = psin->sin_addr.s_addr;
10261da177e4SLinus Torvalds 		mreqs.imr_interface = 0; /* use index for mc_source */
10271da177e4SLinus Torvalds 
10281da177e4SLinus Torvalds 		if (optname == MCAST_BLOCK_SOURCE) {
10291da177e4SLinus Torvalds 			omode = MCAST_EXCLUDE;
10301da177e4SLinus Torvalds 			add = 1;
10311da177e4SLinus Torvalds 		} else if (optname == MCAST_UNBLOCK_SOURCE) {
10321da177e4SLinus Torvalds 			omode = MCAST_EXCLUDE;
10331da177e4SLinus Torvalds 			add = 0;
10341da177e4SLinus Torvalds 		} else if (optname == MCAST_JOIN_SOURCE_GROUP) {
10351da177e4SLinus Torvalds 			struct ip_mreqn mreq;
10361da177e4SLinus Torvalds 
10371da177e4SLinus Torvalds 			psin = (struct sockaddr_in *)&greqs.gsr_group;
10381da177e4SLinus Torvalds 			mreq.imr_multiaddr = psin->sin_addr;
10391da177e4SLinus Torvalds 			mreq.imr_address.s_addr = 0;
10401da177e4SLinus Torvalds 			mreq.imr_ifindex = greqs.gsr_interface;
104154ff9ef3SMarcelo Ricardo Leitner 			err = ip_mc_join_group(sk, &mreq);
10428cdaaa15SDavid L Stevens 			if (err && err != -EADDRINUSE)
10431da177e4SLinus Torvalds 				break;
10441da177e4SLinus Torvalds 			greqs.gsr_interface = mreq.imr_ifindex;
10451da177e4SLinus Torvalds 			omode = MCAST_INCLUDE;
10461da177e4SLinus Torvalds 			add = 1;
10471da177e4SLinus Torvalds 		} else /* MCAST_LEAVE_SOURCE_GROUP */ {
10481da177e4SLinus Torvalds 			omode = MCAST_INCLUDE;
10491da177e4SLinus Torvalds 			add = 0;
10501da177e4SLinus Torvalds 		}
10511da177e4SLinus Torvalds 		err = ip_mc_source(add, omode, sk, &mreqs,
10521da177e4SLinus Torvalds 				   greqs.gsr_interface);
10531da177e4SLinus Torvalds 		break;
10541da177e4SLinus Torvalds 	}
10551da177e4SLinus Torvalds 	case MCAST_MSFILTER:
10561da177e4SLinus Torvalds 	{
10571da177e4SLinus Torvalds 		struct sockaddr_in *psin;
10581da177e4SLinus Torvalds 		struct ip_msfilter *msf = NULL;
10591da177e4SLinus Torvalds 		struct group_filter *gsf = NULL;
10601da177e4SLinus Torvalds 		int msize, i, ifindex;
10611da177e4SLinus Torvalds 
10621da177e4SLinus Torvalds 		if (optlen < GROUP_FILTER_SIZE(0))
10631da177e4SLinus Torvalds 			goto e_inval;
10641da177e4SLinus Torvalds 		if (optlen > sysctl_optmem_max) {
10651da177e4SLinus Torvalds 			err = -ENOBUFS;
10661da177e4SLinus Torvalds 			break;
10671da177e4SLinus Torvalds 		}
10688b3a7005SKris Katterjohn 		gsf = kmalloc(optlen, GFP_KERNEL);
1069cfcabdccSStephen Hemminger 		if (!gsf) {
10701da177e4SLinus Torvalds 			err = -ENOBUFS;
10711da177e4SLinus Torvalds 			break;
10721da177e4SLinus Torvalds 		}
10731da177e4SLinus Torvalds 		err = -EFAULT;
10744d52cfbeSEric Dumazet 		if (copy_from_user(gsf, optval, optlen))
10751da177e4SLinus Torvalds 			goto mc_msf_out;
10764d52cfbeSEric Dumazet 
10771da177e4SLinus Torvalds 		/* numsrc >= (4G-140)/128 overflow in 32 bits */
10781da177e4SLinus Torvalds 		if (gsf->gf_numsrc >= 0x1ffffff ||
1079166b6b2dSNikolay Borisov 		    gsf->gf_numsrc > net->ipv4.sysctl_igmp_max_msf) {
10801da177e4SLinus Torvalds 			err = -ENOBUFS;
10811da177e4SLinus Torvalds 			goto mc_msf_out;
10821da177e4SLinus Torvalds 		}
10831da177e4SLinus Torvalds 		if (GROUP_FILTER_SIZE(gsf->gf_numsrc) > optlen) {
10841da177e4SLinus Torvalds 			err = -EINVAL;
10851da177e4SLinus Torvalds 			goto mc_msf_out;
10861da177e4SLinus Torvalds 		}
10871da177e4SLinus Torvalds 		msize = IP_MSFILTER_SIZE(gsf->gf_numsrc);
10888b3a7005SKris Katterjohn 		msf = kmalloc(msize, GFP_KERNEL);
1089cfcabdccSStephen Hemminger 		if (!msf) {
10901da177e4SLinus Torvalds 			err = -ENOBUFS;
10911da177e4SLinus Torvalds 			goto mc_msf_out;
10921da177e4SLinus Torvalds 		}
10931da177e4SLinus Torvalds 		ifindex = gsf->gf_interface;
10941da177e4SLinus Torvalds 		psin = (struct sockaddr_in *)&gsf->gf_group;
10951da177e4SLinus Torvalds 		if (psin->sin_family != AF_INET) {
10961da177e4SLinus Torvalds 			err = -EADDRNOTAVAIL;
10971da177e4SLinus Torvalds 			goto mc_msf_out;
10981da177e4SLinus Torvalds 		}
10991da177e4SLinus Torvalds 		msf->imsf_multiaddr = psin->sin_addr.s_addr;
11001da177e4SLinus Torvalds 		msf->imsf_interface = 0;
11011da177e4SLinus Torvalds 		msf->imsf_fmode = gsf->gf_fmode;
11021da177e4SLinus Torvalds 		msf->imsf_numsrc = gsf->gf_numsrc;
11031da177e4SLinus Torvalds 		err = -EADDRNOTAVAIL;
11041da177e4SLinus Torvalds 		for (i = 0; i < gsf->gf_numsrc; ++i) {
11051da177e4SLinus Torvalds 			psin = (struct sockaddr_in *)&gsf->gf_slist[i];
11061da177e4SLinus Torvalds 
11071da177e4SLinus Torvalds 			if (psin->sin_family != AF_INET)
11081da177e4SLinus Torvalds 				goto mc_msf_out;
11091da177e4SLinus Torvalds 			msf->imsf_slist[i] = psin->sin_addr.s_addr;
11101da177e4SLinus Torvalds 		}
11111da177e4SLinus Torvalds 		kfree(gsf);
11121da177e4SLinus Torvalds 		gsf = NULL;
11131da177e4SLinus Torvalds 
11141da177e4SLinus Torvalds 		err = ip_mc_msfilter(sk, msf, ifindex);
11151da177e4SLinus Torvalds mc_msf_out:
11161da177e4SLinus Torvalds 		kfree(msf);
11171da177e4SLinus Torvalds 		kfree(gsf);
11181da177e4SLinus Torvalds 		break;
11191da177e4SLinus Torvalds 	}
1120f771bef9SNivedita Singhvi 	case IP_MULTICAST_ALL:
1121f771bef9SNivedita Singhvi 		if (optlen < 1)
1122f771bef9SNivedita Singhvi 			goto e_inval;
1123f771bef9SNivedita Singhvi 		if (val != 0 && val != 1)
1124f771bef9SNivedita Singhvi 			goto e_inval;
1125f771bef9SNivedita Singhvi 		inet->mc_all = val;
1126f771bef9SNivedita Singhvi 		break;
11271da177e4SLinus Torvalds 	case IP_ROUTER_ALERT:
11281da177e4SLinus Torvalds 		err = ip_ra_control(sk, val ? 1 : 0, NULL);
11291da177e4SLinus Torvalds 		break;
11301da177e4SLinus Torvalds 
11311da177e4SLinus Torvalds 	case IP_FREEBIND:
11321da177e4SLinus Torvalds 		if (optlen < 1)
11331da177e4SLinus Torvalds 			goto e_inval;
11341da177e4SLinus Torvalds 		inet->freebind = !!val;
11351da177e4SLinus Torvalds 		break;
11361da177e4SLinus Torvalds 
11371da177e4SLinus Torvalds 	case IP_IPSEC_POLICY:
11381da177e4SLinus Torvalds 	case IP_XFRM_POLICY:
11396fc0b4a7SHerbert Xu 		err = -EPERM;
114052e804c6SEric W. Biederman 		if (!ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN))
11416fc0b4a7SHerbert Xu 			break;
11421da177e4SLinus Torvalds 		err = xfrm_user_policy(sk, optname, optval, optlen);
11431da177e4SLinus Torvalds 		break;
11441da177e4SLinus Torvalds 
1145f5715aeaSKOVACS Krisztian 	case IP_TRANSPARENT:
114652e804c6SEric W. Biederman 		if (!!val && !ns_capable(sock_net(sk)->user_ns, CAP_NET_RAW) &&
114752e804c6SEric W. Biederman 		    !ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN)) {
1148f5715aeaSKOVACS Krisztian 			err = -EPERM;
1149f5715aeaSKOVACS Krisztian 			break;
1150f5715aeaSKOVACS Krisztian 		}
1151f5715aeaSKOVACS Krisztian 		if (optlen < 1)
1152f5715aeaSKOVACS Krisztian 			goto e_inval;
1153f5715aeaSKOVACS Krisztian 		inet->transparent = !!val;
1154f5715aeaSKOVACS Krisztian 		break;
1155f5715aeaSKOVACS Krisztian 
1156d218d111SStephen Hemminger 	case IP_MINTTL:
1157d218d111SStephen Hemminger 		if (optlen < 1)
1158d218d111SStephen Hemminger 			goto e_inval;
1159d218d111SStephen Hemminger 		if (val < 0 || val > 255)
1160d218d111SStephen Hemminger 			goto e_inval;
1161d218d111SStephen Hemminger 		inet->min_ttl = val;
1162d218d111SStephen Hemminger 		break;
1163d218d111SStephen Hemminger 
11641da177e4SLinus Torvalds 	default:
11651da177e4SLinus Torvalds 		err = -ENOPROTOOPT;
11661da177e4SLinus Torvalds 		break;
11671da177e4SLinus Torvalds 	}
11681da177e4SLinus Torvalds 	release_sock(sk);
1169baf606d9SMarcelo Ricardo Leitner 	if (needs_rtnl)
1170baf606d9SMarcelo Ricardo Leitner 		rtnl_unlock();
11711da177e4SLinus Torvalds 	return err;
11721da177e4SLinus Torvalds 
11731da177e4SLinus Torvalds e_inval:
11741da177e4SLinus Torvalds 	release_sock(sk);
1175baf606d9SMarcelo Ricardo Leitner 	if (needs_rtnl)
1176baf606d9SMarcelo Ricardo Leitner 		rtnl_unlock();
11771da177e4SLinus Torvalds 	return -EINVAL;
11781da177e4SLinus Torvalds }
11791da177e4SLinus Torvalds 
1180f84af32cSEric Dumazet /**
1181829ae9d6SWillem de Bruijn  * ipv4_pktinfo_prepare - transfer some info from rtable to skb
1182f84af32cSEric Dumazet  * @sk: socket
1183f84af32cSEric Dumazet  * @skb: buffer
1184f84af32cSEric Dumazet  *
118535ebf65eSDavid S. Miller  * To support IP_CMSG_PKTINFO option, we store rt_iif and specific
118635ebf65eSDavid S. Miller  * destination in skb->cb[] before dst drop.
11878e3bff96Sstephen hemminger  * This way, receiver doesn't make cache line misses to read rtable.
1188f84af32cSEric Dumazet  */
1189fbf8866dSShawn Bohrer void ipv4_pktinfo_prepare(const struct sock *sk, struct sk_buff *skb)
1190f84af32cSEric Dumazet {
1191d826eb14SEric Dumazet 	struct in_pktinfo *pktinfo = PKTINFO_SKB_CB(skb);
11924b261c75SHannes Frederic Sowa 	bool prepare = (inet_sk(sk)->cmsg_flags & IP_CMSG_PKTINFO) ||
11934b261c75SHannes Frederic Sowa 		       ipv6_sk_rxinfo(sk);
1194d826eb14SEric Dumazet 
11954b261c75SHannes Frederic Sowa 	if (prepare && skb_rtable(skb)) {
119692101b3bSDavid S. Miller 		pktinfo->ipi_ifindex = inet_iif(skb);
119735ebf65eSDavid S. Miller 		pktinfo->ipi_spec_dst.s_addr = fib_compute_spec_dst(skb);
1198d826eb14SEric Dumazet 	} else {
1199d826eb14SEric Dumazet 		pktinfo->ipi_ifindex = 0;
1200d826eb14SEric Dumazet 		pktinfo->ipi_spec_dst.s_addr = 0;
1201f84af32cSEric Dumazet 	}
1202d826eb14SEric Dumazet 	skb_dst_drop(skb);
1203d826eb14SEric Dumazet }
1204f84af32cSEric Dumazet 
12053fdadf7dSDmitry Mishin int ip_setsockopt(struct sock *sk, int level,
1206b7058842SDavid S. Miller 		int optname, char __user *optval, unsigned int optlen)
12073fdadf7dSDmitry Mishin {
12083fdadf7dSDmitry Mishin 	int err;
12093fdadf7dSDmitry Mishin 
12103fdadf7dSDmitry Mishin 	if (level != SOL_IP)
12113fdadf7dSDmitry Mishin 		return -ENOPROTOOPT;
12123fdadf7dSDmitry Mishin 
12133fdadf7dSDmitry Mishin 	err = do_ip_setsockopt(sk, level, optname, optval, optlen);
12143fdadf7dSDmitry Mishin #ifdef CONFIG_NETFILTER
12153fdadf7dSDmitry Mishin 	/* we need to exclude all possible ENOPROTOOPTs except default case */
12163fdadf7dSDmitry Mishin 	if (err == -ENOPROTOOPT && optname != IP_HDRINCL &&
12176a9fb947SPavel Emelyanov 			optname != IP_IPSEC_POLICY &&
12186a9fb947SPavel Emelyanov 			optname != IP_XFRM_POLICY &&
12196a9fb947SPavel Emelyanov 			!ip_mroute_opt(optname)) {
12203fdadf7dSDmitry Mishin 		lock_sock(sk);
12213fdadf7dSDmitry Mishin 		err = nf_setsockopt(sk, PF_INET, optname, optval, optlen);
12223fdadf7dSDmitry Mishin 		release_sock(sk);
12233fdadf7dSDmitry Mishin 	}
12243fdadf7dSDmitry Mishin #endif
12253fdadf7dSDmitry Mishin 	return err;
12263fdadf7dSDmitry Mishin }
12274d52cfbeSEric Dumazet EXPORT_SYMBOL(ip_setsockopt);
12283fdadf7dSDmitry Mishin 
12293fdadf7dSDmitry Mishin #ifdef CONFIG_COMPAT
1230543d9cfeSArnaldo Carvalho de Melo int compat_ip_setsockopt(struct sock *sk, int level, int optname,
1231b7058842SDavid S. Miller 			 char __user *optval, unsigned int optlen)
12323fdadf7dSDmitry Mishin {
12333fdadf7dSDmitry Mishin 	int err;
12343fdadf7dSDmitry Mishin 
12353fdadf7dSDmitry Mishin 	if (level != SOL_IP)
12363fdadf7dSDmitry Mishin 		return -ENOPROTOOPT;
12373fdadf7dSDmitry Mishin 
1238dae50295SDavid L Stevens 	if (optname >= MCAST_JOIN_GROUP && optname <= MCAST_MSFILTER)
1239dae50295SDavid L Stevens 		return compat_mc_setsockopt(sk, level, optname, optval, optlen,
1240dae50295SDavid L Stevens 			ip_setsockopt);
1241dae50295SDavid L Stevens 
12423fdadf7dSDmitry Mishin 	err = do_ip_setsockopt(sk, level, optname, optval, optlen);
12433fdadf7dSDmitry Mishin #ifdef CONFIG_NETFILTER
12443fdadf7dSDmitry Mishin 	/* we need to exclude all possible ENOPROTOOPTs except default case */
12453fdadf7dSDmitry Mishin 	if (err == -ENOPROTOOPT && optname != IP_HDRINCL &&
12466a9fb947SPavel Emelyanov 			optname != IP_IPSEC_POLICY &&
12476a9fb947SPavel Emelyanov 			optname != IP_XFRM_POLICY &&
12486a9fb947SPavel Emelyanov 			!ip_mroute_opt(optname)) {
12493fdadf7dSDmitry Mishin 		lock_sock(sk);
1250543d9cfeSArnaldo Carvalho de Melo 		err = compat_nf_setsockopt(sk, PF_INET, optname,
1251543d9cfeSArnaldo Carvalho de Melo 					   optval, optlen);
12523fdadf7dSDmitry Mishin 		release_sock(sk);
12533fdadf7dSDmitry Mishin 	}
12543fdadf7dSDmitry Mishin #endif
12553fdadf7dSDmitry Mishin 	return err;
12563fdadf7dSDmitry Mishin }
1257543d9cfeSArnaldo Carvalho de Melo EXPORT_SYMBOL(compat_ip_setsockopt);
12583fdadf7dSDmitry Mishin #endif
12593fdadf7dSDmitry Mishin 
12601da177e4SLinus Torvalds /*
12614d52cfbeSEric Dumazet  *	Get the options. Note for future reference. The GET of IP options gets
12624d52cfbeSEric Dumazet  *	the _received_ ones. The set sets the _sent_ ones.
12631da177e4SLinus Torvalds  */
12641da177e4SLinus Torvalds 
126587e9f031SWANG Cong static bool getsockopt_needs_rtnl(int optname)
126687e9f031SWANG Cong {
126787e9f031SWANG Cong 	switch (optname) {
126887e9f031SWANG Cong 	case IP_MSFILTER:
126987e9f031SWANG Cong 	case MCAST_MSFILTER:
127087e9f031SWANG Cong 		return true;
127187e9f031SWANG Cong 	}
127287e9f031SWANG Cong 	return false;
127387e9f031SWANG Cong }
127487e9f031SWANG Cong 
12753fdadf7dSDmitry Mishin static int do_ip_getsockopt(struct sock *sk, int level, int optname,
127695c96174SEric Dumazet 			    char __user *optval, int __user *optlen, unsigned int flags)
12771da177e4SLinus Torvalds {
12781da177e4SLinus Torvalds 	struct inet_sock *inet = inet_sk(sk);
127987e9f031SWANG Cong 	bool needs_rtnl = getsockopt_needs_rtnl(optname);
128087e9f031SWANG Cong 	int val, err = 0;
12811da177e4SLinus Torvalds 	int len;
12821da177e4SLinus Torvalds 
12831da177e4SLinus Torvalds 	if (level != SOL_IP)
12841da177e4SLinus Torvalds 		return -EOPNOTSUPP;
12851da177e4SLinus Torvalds 
12866a9fb947SPavel Emelyanov 	if (ip_mroute_opt(optname))
12871da177e4SLinus Torvalds 		return ip_mroute_getsockopt(sk, optname, optval, optlen);
12881da177e4SLinus Torvalds 
12891da177e4SLinus Torvalds 	if (get_user(len, optlen))
12901da177e4SLinus Torvalds 		return -EFAULT;
12911da177e4SLinus Torvalds 	if (len < 0)
12921da177e4SLinus Torvalds 		return -EINVAL;
12931da177e4SLinus Torvalds 
129487e9f031SWANG Cong 	if (needs_rtnl)
129587e9f031SWANG Cong 		rtnl_lock();
12961da177e4SLinus Torvalds 	lock_sock(sk);
12971da177e4SLinus Torvalds 
12981da177e4SLinus Torvalds 	switch (optname) {
12991da177e4SLinus Torvalds 	case IP_OPTIONS:
13001da177e4SLinus Torvalds 	{
13011da177e4SLinus Torvalds 		unsigned char optbuf[sizeof(struct ip_options)+40];
13021da177e4SLinus Torvalds 		struct ip_options *opt = (struct ip_options *)optbuf;
1303f6d8bd05SEric Dumazet 		struct ip_options_rcu *inet_opt;
1304f6d8bd05SEric Dumazet 
1305f6d8bd05SEric Dumazet 		inet_opt = rcu_dereference_protected(inet->inet_opt,
13061e1d04e6SHannes Frederic Sowa 						     lockdep_sock_is_held(sk));
13071da177e4SLinus Torvalds 		opt->optlen = 0;
1308f6d8bd05SEric Dumazet 		if (inet_opt)
1309f6d8bd05SEric Dumazet 			memcpy(optbuf, &inet_opt->opt,
13101da177e4SLinus Torvalds 			       sizeof(struct ip_options) +
1311f6d8bd05SEric Dumazet 			       inet_opt->opt.optlen);
13121da177e4SLinus Torvalds 		release_sock(sk);
13131da177e4SLinus Torvalds 
13141da177e4SLinus Torvalds 		if (opt->optlen == 0)
13151da177e4SLinus Torvalds 			return put_user(0, optlen);
13161da177e4SLinus Torvalds 
13171da177e4SLinus Torvalds 		ip_options_undo(opt);
13181da177e4SLinus Torvalds 
13191da177e4SLinus Torvalds 		len = min_t(unsigned int, len, opt->optlen);
13201da177e4SLinus Torvalds 		if (put_user(len, optlen))
13211da177e4SLinus Torvalds 			return -EFAULT;
13221da177e4SLinus Torvalds 		if (copy_to_user(optval, opt->__data, len))
13231da177e4SLinus Torvalds 			return -EFAULT;
13241da177e4SLinus Torvalds 		return 0;
13251da177e4SLinus Torvalds 	}
13261da177e4SLinus Torvalds 	case IP_PKTINFO:
13271da177e4SLinus Torvalds 		val = (inet->cmsg_flags & IP_CMSG_PKTINFO) != 0;
13281da177e4SLinus Torvalds 		break;
13291da177e4SLinus Torvalds 	case IP_RECVTTL:
13301da177e4SLinus Torvalds 		val = (inet->cmsg_flags & IP_CMSG_TTL) != 0;
13311da177e4SLinus Torvalds 		break;
13321da177e4SLinus Torvalds 	case IP_RECVTOS:
13331da177e4SLinus Torvalds 		val = (inet->cmsg_flags & IP_CMSG_TOS) != 0;
13341da177e4SLinus Torvalds 		break;
13351da177e4SLinus Torvalds 	case IP_RECVOPTS:
13361da177e4SLinus Torvalds 		val = (inet->cmsg_flags & IP_CMSG_RECVOPTS) != 0;
13371da177e4SLinus Torvalds 		break;
13381da177e4SLinus Torvalds 	case IP_RETOPTS:
13391da177e4SLinus Torvalds 		val = (inet->cmsg_flags & IP_CMSG_RETOPTS) != 0;
13401da177e4SLinus Torvalds 		break;
13412c7946a7SCatherine Zhang 	case IP_PASSSEC:
13422c7946a7SCatherine Zhang 		val = (inet->cmsg_flags & IP_CMSG_PASSSEC) != 0;
13432c7946a7SCatherine Zhang 		break;
1344e8b2dfe9SBalazs Scheidler 	case IP_RECVORIGDSTADDR:
1345e8b2dfe9SBalazs Scheidler 		val = (inet->cmsg_flags & IP_CMSG_ORIGDSTADDR) != 0;
1346e8b2dfe9SBalazs Scheidler 		break;
1347ad6f939aSTom Herbert 	case IP_CHECKSUM:
1348ad6f939aSTom Herbert 		val = (inet->cmsg_flags & IP_CMSG_CHECKSUM) != 0;
1349ad6f939aSTom Herbert 		break;
13501da177e4SLinus Torvalds 	case IP_TOS:
13511da177e4SLinus Torvalds 		val = inet->tos;
13521da177e4SLinus Torvalds 		break;
13531da177e4SLinus Torvalds 	case IP_TTL:
1354fa50d974SNikolay Borisov 	{
1355fa50d974SNikolay Borisov 		struct net *net = sock_net(sk);
13561da177e4SLinus Torvalds 		val = (inet->uc_ttl == -1 ?
1357fa50d974SNikolay Borisov 		       net->ipv4.sysctl_ip_default_ttl :
13581da177e4SLinus Torvalds 		       inet->uc_ttl);
13591da177e4SLinus Torvalds 		break;
1360fa50d974SNikolay Borisov 	}
13611da177e4SLinus Torvalds 	case IP_HDRINCL:
13621da177e4SLinus Torvalds 		val = inet->hdrincl;
13631da177e4SLinus Torvalds 		break;
1364a89b4763SMichael Kerrisk 	case IP_NODEFRAG:
1365a89b4763SMichael Kerrisk 		val = inet->nodefrag;
1366a89b4763SMichael Kerrisk 		break;
136790c337daSEric Dumazet 	case IP_BIND_ADDRESS_NO_PORT:
136890c337daSEric Dumazet 		val = inet->bind_address_no_port;
136990c337daSEric Dumazet 		break;
13701da177e4SLinus Torvalds 	case IP_MTU_DISCOVER:
13711da177e4SLinus Torvalds 		val = inet->pmtudisc;
13721da177e4SLinus Torvalds 		break;
13731da177e4SLinus Torvalds 	case IP_MTU:
13741da177e4SLinus Torvalds 	{
13751da177e4SLinus Torvalds 		struct dst_entry *dst;
13761da177e4SLinus Torvalds 		val = 0;
13771da177e4SLinus Torvalds 		dst = sk_dst_get(sk);
13781da177e4SLinus Torvalds 		if (dst) {
13791da177e4SLinus Torvalds 			val = dst_mtu(dst);
13801da177e4SLinus Torvalds 			dst_release(dst);
13811da177e4SLinus Torvalds 		}
13821da177e4SLinus Torvalds 		if (!val) {
13831da177e4SLinus Torvalds 			release_sock(sk);
13841da177e4SLinus Torvalds 			return -ENOTCONN;
13851da177e4SLinus Torvalds 		}
13861da177e4SLinus Torvalds 		break;
13871da177e4SLinus Torvalds 	}
13881da177e4SLinus Torvalds 	case IP_RECVERR:
13891da177e4SLinus Torvalds 		val = inet->recverr;
13901da177e4SLinus Torvalds 		break;
13911da177e4SLinus Torvalds 	case IP_MULTICAST_TTL:
13921da177e4SLinus Torvalds 		val = inet->mc_ttl;
13931da177e4SLinus Torvalds 		break;
13941da177e4SLinus Torvalds 	case IP_MULTICAST_LOOP:
13951da177e4SLinus Torvalds 		val = inet->mc_loop;
13961da177e4SLinus Torvalds 		break;
139776e21053SErich E. Hoover 	case IP_UNICAST_IF:
139876e21053SErich E. Hoover 		val = (__force int)htonl((__u32) inet->uc_index);
139976e21053SErich E. Hoover 		break;
14001da177e4SLinus Torvalds 	case IP_MULTICAST_IF:
14011da177e4SLinus Torvalds 	{
14021da177e4SLinus Torvalds 		struct in_addr addr;
14031da177e4SLinus Torvalds 		len = min_t(unsigned int, len, sizeof(struct in_addr));
14041da177e4SLinus Torvalds 		addr.s_addr = inet->mc_addr;
14051da177e4SLinus Torvalds 		release_sock(sk);
14061da177e4SLinus Torvalds 
14071da177e4SLinus Torvalds 		if (put_user(len, optlen))
14081da177e4SLinus Torvalds 			return -EFAULT;
14091da177e4SLinus Torvalds 		if (copy_to_user(optval, &addr, len))
14101da177e4SLinus Torvalds 			return -EFAULT;
14111da177e4SLinus Torvalds 		return 0;
14121da177e4SLinus Torvalds 	}
14131da177e4SLinus Torvalds 	case IP_MSFILTER:
14141da177e4SLinus Torvalds 	{
14151da177e4SLinus Torvalds 		struct ip_msfilter msf;
14161da177e4SLinus Torvalds 
14171da177e4SLinus Torvalds 		if (len < IP_MSFILTER_SIZE(0)) {
141887e9f031SWANG Cong 			err = -EINVAL;
141987e9f031SWANG Cong 			goto out;
14201da177e4SLinus Torvalds 		}
14211da177e4SLinus Torvalds 		if (copy_from_user(&msf, optval, IP_MSFILTER_SIZE(0))) {
142287e9f031SWANG Cong 			err = -EFAULT;
142387e9f031SWANG Cong 			goto out;
14241da177e4SLinus Torvalds 		}
14251da177e4SLinus Torvalds 		err = ip_mc_msfget(sk, &msf,
14261da177e4SLinus Torvalds 				   (struct ip_msfilter __user *)optval, optlen);
142787e9f031SWANG Cong 		goto out;
14281da177e4SLinus Torvalds 	}
14291da177e4SLinus Torvalds 	case MCAST_MSFILTER:
14301da177e4SLinus Torvalds 	{
14311da177e4SLinus Torvalds 		struct group_filter gsf;
14321da177e4SLinus Torvalds 
14331da177e4SLinus Torvalds 		if (len < GROUP_FILTER_SIZE(0)) {
143487e9f031SWANG Cong 			err = -EINVAL;
143587e9f031SWANG Cong 			goto out;
14361da177e4SLinus Torvalds 		}
14371da177e4SLinus Torvalds 		if (copy_from_user(&gsf, optval, GROUP_FILTER_SIZE(0))) {
143887e9f031SWANG Cong 			err = -EFAULT;
143987e9f031SWANG Cong 			goto out;
14401da177e4SLinus Torvalds 		}
14411da177e4SLinus Torvalds 		err = ip_mc_gsfget(sk, &gsf,
14424d52cfbeSEric Dumazet 				   (struct group_filter __user *)optval,
14434d52cfbeSEric Dumazet 				   optlen);
144487e9f031SWANG Cong 		goto out;
14451da177e4SLinus Torvalds 	}
1446f771bef9SNivedita Singhvi 	case IP_MULTICAST_ALL:
1447f771bef9SNivedita Singhvi 		val = inet->mc_all;
1448f771bef9SNivedita Singhvi 		break;
14491da177e4SLinus Torvalds 	case IP_PKTOPTIONS:
14501da177e4SLinus Torvalds 	{
14511da177e4SLinus Torvalds 		struct msghdr msg;
14521da177e4SLinus Torvalds 
14531da177e4SLinus Torvalds 		release_sock(sk);
14541da177e4SLinus Torvalds 
14551da177e4SLinus Torvalds 		if (sk->sk_type != SOCK_STREAM)
14561da177e4SLinus Torvalds 			return -ENOPROTOOPT;
14571da177e4SLinus Torvalds 
1458c54a5e02SKaroly Kemeny 		msg.msg_control = (__force void *) optval;
14591da177e4SLinus Torvalds 		msg.msg_controllen = len;
1460dd23198eSDaniel Baluta 		msg.msg_flags = flags;
14611da177e4SLinus Torvalds 
14621da177e4SLinus Torvalds 		if (inet->cmsg_flags & IP_CMSG_PKTINFO) {
14631da177e4SLinus Torvalds 			struct in_pktinfo info;
14641da177e4SLinus Torvalds 
1465c720c7e8SEric Dumazet 			info.ipi_addr.s_addr = inet->inet_rcv_saddr;
1466c720c7e8SEric Dumazet 			info.ipi_spec_dst.s_addr = inet->inet_rcv_saddr;
14671da177e4SLinus Torvalds 			info.ipi_ifindex = inet->mc_index;
14681da177e4SLinus Torvalds 			put_cmsg(&msg, SOL_IP, IP_PKTINFO, sizeof(info), &info);
14691da177e4SLinus Torvalds 		}
14701da177e4SLinus Torvalds 		if (inet->cmsg_flags & IP_CMSG_TTL) {
14711da177e4SLinus Torvalds 			int hlim = inet->mc_ttl;
14721da177e4SLinus Torvalds 			put_cmsg(&msg, SOL_IP, IP_TTL, sizeof(hlim), &hlim);
14731da177e4SLinus Torvalds 		}
14744c507d28SJiri Benc 		if (inet->cmsg_flags & IP_CMSG_TOS) {
14754c507d28SJiri Benc 			int tos = inet->rcv_tos;
14764c507d28SJiri Benc 			put_cmsg(&msg, SOL_IP, IP_TOS, sizeof(tos), &tos);
14774c507d28SJiri Benc 		}
14781da177e4SLinus Torvalds 		len -= msg.msg_controllen;
14791da177e4SLinus Torvalds 		return put_user(len, optlen);
14801da177e4SLinus Torvalds 	}
14811da177e4SLinus Torvalds 	case IP_FREEBIND:
14821da177e4SLinus Torvalds 		val = inet->freebind;
14831da177e4SLinus Torvalds 		break;
1484f5715aeaSKOVACS Krisztian 	case IP_TRANSPARENT:
1485f5715aeaSKOVACS Krisztian 		val = inet->transparent;
1486f5715aeaSKOVACS Krisztian 		break;
1487d218d111SStephen Hemminger 	case IP_MINTTL:
1488d218d111SStephen Hemminger 		val = inet->min_ttl;
1489d218d111SStephen Hemminger 		break;
14901da177e4SLinus Torvalds 	default:
14911da177e4SLinus Torvalds 		release_sock(sk);
14921da177e4SLinus Torvalds 		return -ENOPROTOOPT;
14931da177e4SLinus Torvalds 	}
14941da177e4SLinus Torvalds 	release_sock(sk);
14951da177e4SLinus Torvalds 
1496951e07c9SDavid S. Miller 	if (len < sizeof(int) && len > 0 && val >= 0 && val <= 255) {
14971da177e4SLinus Torvalds 		unsigned char ucval = (unsigned char)val;
14981da177e4SLinus Torvalds 		len = 1;
14991da177e4SLinus Torvalds 		if (put_user(len, optlen))
15001da177e4SLinus Torvalds 			return -EFAULT;
15011da177e4SLinus Torvalds 		if (copy_to_user(optval, &ucval, 1))
15021da177e4SLinus Torvalds 			return -EFAULT;
15031da177e4SLinus Torvalds 	} else {
15041da177e4SLinus Torvalds 		len = min_t(unsigned int, sizeof(int), len);
15051da177e4SLinus Torvalds 		if (put_user(len, optlen))
15061da177e4SLinus Torvalds 			return -EFAULT;
15071da177e4SLinus Torvalds 		if (copy_to_user(optval, &val, len))
15081da177e4SLinus Torvalds 			return -EFAULT;
15091da177e4SLinus Torvalds 	}
15101da177e4SLinus Torvalds 	return 0;
151187e9f031SWANG Cong 
151287e9f031SWANG Cong out:
151387e9f031SWANG Cong 	release_sock(sk);
151487e9f031SWANG Cong 	if (needs_rtnl)
151587e9f031SWANG Cong 		rtnl_unlock();
151687e9f031SWANG Cong 	return err;
15171da177e4SLinus Torvalds }
15181da177e4SLinus Torvalds 
15193fdadf7dSDmitry Mishin int ip_getsockopt(struct sock *sk, int level,
15203fdadf7dSDmitry Mishin 		  int optname, char __user *optval, int __user *optlen)
15213fdadf7dSDmitry Mishin {
15223fdadf7dSDmitry Mishin 	int err;
15233fdadf7dSDmitry Mishin 
1524dd23198eSDaniel Baluta 	err = do_ip_getsockopt(sk, level, optname, optval, optlen, 0);
15253fdadf7dSDmitry Mishin #ifdef CONFIG_NETFILTER
15263fdadf7dSDmitry Mishin 	/* we need to exclude all possible ENOPROTOOPTs except default case */
15276a9fb947SPavel Emelyanov 	if (err == -ENOPROTOOPT && optname != IP_PKTOPTIONS &&
15286a9fb947SPavel Emelyanov 			!ip_mroute_opt(optname)) {
15293fdadf7dSDmitry Mishin 		int len;
15303fdadf7dSDmitry Mishin 
15313fdadf7dSDmitry Mishin 		if (get_user(len, optlen))
15323fdadf7dSDmitry Mishin 			return -EFAULT;
15333fdadf7dSDmitry Mishin 
15343fdadf7dSDmitry Mishin 		lock_sock(sk);
15353fdadf7dSDmitry Mishin 		err = nf_getsockopt(sk, PF_INET, optname, optval,
15363fdadf7dSDmitry Mishin 				&len);
15373fdadf7dSDmitry Mishin 		release_sock(sk);
15383fdadf7dSDmitry Mishin 		if (err >= 0)
15393fdadf7dSDmitry Mishin 			err = put_user(len, optlen);
15403fdadf7dSDmitry Mishin 		return err;
15413fdadf7dSDmitry Mishin 	}
15423fdadf7dSDmitry Mishin #endif
15433fdadf7dSDmitry Mishin 	return err;
15443fdadf7dSDmitry Mishin }
15454d52cfbeSEric Dumazet EXPORT_SYMBOL(ip_getsockopt);
15463fdadf7dSDmitry Mishin 
15473fdadf7dSDmitry Mishin #ifdef CONFIG_COMPAT
1548543d9cfeSArnaldo Carvalho de Melo int compat_ip_getsockopt(struct sock *sk, int level, int optname,
1549543d9cfeSArnaldo Carvalho de Melo 			 char __user *optval, int __user *optlen)
15503fdadf7dSDmitry Mishin {
155142908c69SDavid L Stevens 	int err;
155242908c69SDavid L Stevens 
155342908c69SDavid L Stevens 	if (optname == MCAST_MSFILTER)
155442908c69SDavid L Stevens 		return compat_mc_getsockopt(sk, level, optname, optval, optlen,
155542908c69SDavid L Stevens 			ip_getsockopt);
155642908c69SDavid L Stevens 
1557dd23198eSDaniel Baluta 	err = do_ip_getsockopt(sk, level, optname, optval, optlen,
1558dd23198eSDaniel Baluta 		MSG_CMSG_COMPAT);
155942908c69SDavid L Stevens 
15603fdadf7dSDmitry Mishin #ifdef CONFIG_NETFILTER
15613fdadf7dSDmitry Mishin 	/* we need to exclude all possible ENOPROTOOPTs except default case */
15626a9fb947SPavel Emelyanov 	if (err == -ENOPROTOOPT && optname != IP_PKTOPTIONS &&
15636a9fb947SPavel Emelyanov 			!ip_mroute_opt(optname)) {
15643fdadf7dSDmitry Mishin 		int len;
15653fdadf7dSDmitry Mishin 
15663fdadf7dSDmitry Mishin 		if (get_user(len, optlen))
15673fdadf7dSDmitry Mishin 			return -EFAULT;
15683fdadf7dSDmitry Mishin 
15693fdadf7dSDmitry Mishin 		lock_sock(sk);
1570543d9cfeSArnaldo Carvalho de Melo 		err = compat_nf_getsockopt(sk, PF_INET, optname, optval, &len);
15713fdadf7dSDmitry Mishin 		release_sock(sk);
15723fdadf7dSDmitry Mishin 		if (err >= 0)
15733fdadf7dSDmitry Mishin 			err = put_user(len, optlen);
15743fdadf7dSDmitry Mishin 		return err;
15753fdadf7dSDmitry Mishin 	}
15763fdadf7dSDmitry Mishin #endif
15773fdadf7dSDmitry Mishin 	return err;
15783fdadf7dSDmitry Mishin }
1579543d9cfeSArnaldo Carvalho de Melo EXPORT_SYMBOL(compat_ip_getsockopt);
15803fdadf7dSDmitry Mishin #endif
1581