xref: /linux/net/ipv4/ip_sockglue.c (revision fa50d974d104113630d68b7d03233a6686230d0c)
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)
109ad6f939aSTom Herbert 		csum = csum_sub(csum, csum_partial(skb->data, offset, 0));
110ad6f939aSTom Herbert 
111ad6f939aSTom Herbert 	put_cmsg(msg, SOL_IP, IP_CHECKSUM, sizeof(__wsum), &csum);
112ad6f939aSTom Herbert }
113ad6f939aSTom Herbert 
1142c7946a7SCatherine Zhang static void ip_cmsg_recv_security(struct msghdr *msg, struct sk_buff *skb)
1152c7946a7SCatherine Zhang {
1162c7946a7SCatherine Zhang 	char *secdata;
117dc49c1f9SCatherine Zhang 	u32 seclen, secid;
1182c7946a7SCatherine Zhang 	int err;
1192c7946a7SCatherine Zhang 
120dc49c1f9SCatherine Zhang 	err = security_socket_getpeersec_dgram(NULL, skb, &secid);
121dc49c1f9SCatherine Zhang 	if (err)
122dc49c1f9SCatherine Zhang 		return;
123dc49c1f9SCatherine Zhang 
124dc49c1f9SCatherine Zhang 	err = security_secid_to_secctx(secid, &secdata, &seclen);
1252c7946a7SCatherine Zhang 	if (err)
1262c7946a7SCatherine Zhang 		return;
1272c7946a7SCatherine Zhang 
1282c7946a7SCatherine Zhang 	put_cmsg(msg, SOL_IP, SCM_SECURITY, seclen, secdata);
129dc49c1f9SCatherine Zhang 	security_release_secctx(secdata, seclen);
1302c7946a7SCatherine Zhang }
1312c7946a7SCatherine Zhang 
13221d1a161SHarvey Harrison static void ip_cmsg_recv_dstaddr(struct msghdr *msg, struct sk_buff *skb)
133e8b2dfe9SBalazs Scheidler {
134e8b2dfe9SBalazs Scheidler 	struct sockaddr_in sin;
135b71d1d42SEric Dumazet 	const struct iphdr *iph = ip_hdr(skb);
13621d1a161SHarvey Harrison 	__be16 *ports = (__be16 *)skb_transport_header(skb);
137e8b2dfe9SBalazs Scheidler 
138e8b2dfe9SBalazs Scheidler 	if (skb_transport_offset(skb) + 4 > skb->len)
139e8b2dfe9SBalazs Scheidler 		return;
140e8b2dfe9SBalazs Scheidler 
141e8b2dfe9SBalazs Scheidler 	/* All current transport protocols have the port numbers in the
142e8b2dfe9SBalazs Scheidler 	 * first four bytes of the transport header and this function is
143e8b2dfe9SBalazs Scheidler 	 * written with this assumption in mind.
144e8b2dfe9SBalazs Scheidler 	 */
145e8b2dfe9SBalazs Scheidler 
146e8b2dfe9SBalazs Scheidler 	sin.sin_family = AF_INET;
147e8b2dfe9SBalazs Scheidler 	sin.sin_addr.s_addr = iph->daddr;
148e8b2dfe9SBalazs Scheidler 	sin.sin_port = ports[1];
149e8b2dfe9SBalazs Scheidler 	memset(sin.sin_zero, 0, sizeof(sin.sin_zero));
150e8b2dfe9SBalazs Scheidler 
151e8b2dfe9SBalazs Scheidler 	put_cmsg(msg, SOL_IP, IP_ORIGDSTADDR, sizeof(sin), &sin);
152e8b2dfe9SBalazs Scheidler }
1531da177e4SLinus Torvalds 
1545961de9fSTom Herbert void ip_cmsg_recv_offset(struct msghdr *msg, struct sk_buff *skb,
1555961de9fSTom Herbert 			 int offset)
1561da177e4SLinus Torvalds {
1571da177e4SLinus Torvalds 	struct inet_sock *inet = inet_sk(skb->sk);
15895c96174SEric Dumazet 	unsigned int flags = inet->cmsg_flags;
1591da177e4SLinus Torvalds 
1601da177e4SLinus Torvalds 	/* Ordered by supposed usage frequency */
161c44d13d6STom Herbert 	if (flags & IP_CMSG_PKTINFO) {
1621da177e4SLinus Torvalds 		ip_cmsg_recv_pktinfo(msg, skb);
1631da177e4SLinus Torvalds 
164c44d13d6STom Herbert 		flags &= ~IP_CMSG_PKTINFO;
165c44d13d6STom Herbert 		if (!flags)
166c44d13d6STom Herbert 			return;
167c44d13d6STom Herbert 	}
168c44d13d6STom Herbert 
169c44d13d6STom Herbert 	if (flags & IP_CMSG_TTL) {
1701da177e4SLinus Torvalds 		ip_cmsg_recv_ttl(msg, skb);
1711da177e4SLinus Torvalds 
172c44d13d6STom Herbert 		flags &= ~IP_CMSG_TTL;
173c44d13d6STom Herbert 		if (!flags)
174c44d13d6STom Herbert 			return;
175c44d13d6STom Herbert 	}
176c44d13d6STom Herbert 
177c44d13d6STom Herbert 	if (flags & IP_CMSG_TOS) {
1781da177e4SLinus Torvalds 		ip_cmsg_recv_tos(msg, skb);
1791da177e4SLinus Torvalds 
180c44d13d6STom Herbert 		flags &= ~IP_CMSG_TOS;
181c44d13d6STom Herbert 		if (!flags)
182c44d13d6STom Herbert 			return;
183c44d13d6STom Herbert 	}
184c44d13d6STom Herbert 
185c44d13d6STom Herbert 	if (flags & IP_CMSG_RECVOPTS) {
1861da177e4SLinus Torvalds 		ip_cmsg_recv_opts(msg, skb);
1871da177e4SLinus Torvalds 
188c44d13d6STom Herbert 		flags &= ~IP_CMSG_RECVOPTS;
189c44d13d6STom Herbert 		if (!flags)
190c44d13d6STom Herbert 			return;
191c44d13d6STom Herbert 	}
192c44d13d6STom Herbert 
193c44d13d6STom Herbert 	if (flags & IP_CMSG_RETOPTS) {
1941da177e4SLinus Torvalds 		ip_cmsg_recv_retopts(msg, skb);
1952c7946a7SCatherine Zhang 
196c44d13d6STom Herbert 		flags &= ~IP_CMSG_RETOPTS;
197c44d13d6STom Herbert 		if (!flags)
198c44d13d6STom Herbert 			return;
199c44d13d6STom Herbert 	}
200c44d13d6STom Herbert 
201c44d13d6STom Herbert 	if (flags & IP_CMSG_PASSSEC) {
2022c7946a7SCatherine Zhang 		ip_cmsg_recv_security(msg, skb);
203e8b2dfe9SBalazs Scheidler 
204c44d13d6STom Herbert 		flags &= ~IP_CMSG_PASSSEC;
205c44d13d6STom Herbert 		if (!flags)
206e8b2dfe9SBalazs Scheidler 			return;
207c44d13d6STom Herbert 	}
208c44d13d6STom Herbert 
209ad6f939aSTom Herbert 	if (flags & IP_CMSG_ORIGDSTADDR) {
210e8b2dfe9SBalazs Scheidler 		ip_cmsg_recv_dstaddr(msg, skb);
211e8b2dfe9SBalazs Scheidler 
212ad6f939aSTom Herbert 		flags &= ~IP_CMSG_ORIGDSTADDR;
213ad6f939aSTom Herbert 		if (!flags)
214ad6f939aSTom Herbert 			return;
215ad6f939aSTom Herbert 	}
216ad6f939aSTom Herbert 
217ad6f939aSTom Herbert 	if (flags & IP_CMSG_CHECKSUM)
218ad6f939aSTom Herbert 		ip_cmsg_recv_checksum(msg, skb, offset);
2191da177e4SLinus Torvalds }
2205961de9fSTom Herbert EXPORT_SYMBOL(ip_cmsg_recv_offset);
2211da177e4SLinus Torvalds 
222c8e6ad08SHannes Frederic Sowa int ip_cmsg_send(struct net *net, struct msghdr *msg, struct ipcm_cookie *ipc,
223c8e6ad08SHannes Frederic Sowa 		 bool allow_ipv6)
2241da177e4SLinus Torvalds {
225f02db315SFrancesco Fusco 	int err, val;
2261da177e4SLinus Torvalds 	struct cmsghdr *cmsg;
2271da177e4SLinus Torvalds 
228f95b414eSGu Zheng 	for_each_cmsghdr(cmsg, msg) {
2291da177e4SLinus Torvalds 		if (!CMSG_OK(msg, cmsg))
2301da177e4SLinus Torvalds 			return -EINVAL;
2315337b5b7SEric Dumazet #if IS_ENABLED(CONFIG_IPV6)
232c8e6ad08SHannes Frederic Sowa 		if (allow_ipv6 &&
233c8e6ad08SHannes Frederic Sowa 		    cmsg->cmsg_level == SOL_IPV6 &&
234c8e6ad08SHannes Frederic Sowa 		    cmsg->cmsg_type == IPV6_PKTINFO) {
235c8e6ad08SHannes Frederic Sowa 			struct in6_pktinfo *src_info;
236c8e6ad08SHannes Frederic Sowa 
237c8e6ad08SHannes Frederic Sowa 			if (cmsg->cmsg_len < CMSG_LEN(sizeof(*src_info)))
238c8e6ad08SHannes Frederic Sowa 				return -EINVAL;
239c8e6ad08SHannes Frederic Sowa 			src_info = (struct in6_pktinfo *)CMSG_DATA(cmsg);
240c8e6ad08SHannes Frederic Sowa 			if (!ipv6_addr_v4mapped(&src_info->ipi6_addr))
241c8e6ad08SHannes Frederic Sowa 				return -EINVAL;
242c8e6ad08SHannes Frederic Sowa 			ipc->oif = src_info->ipi6_ifindex;
243c8e6ad08SHannes Frederic Sowa 			ipc->addr = src_info->ipi6_addr.s6_addr32[3];
244c8e6ad08SHannes Frederic Sowa 			continue;
245c8e6ad08SHannes Frederic Sowa 		}
246c8e6ad08SHannes Frederic Sowa #endif
2471da177e4SLinus Torvalds 		if (cmsg->cmsg_level != SOL_IP)
2481da177e4SLinus Torvalds 			continue;
2491da177e4SLinus Torvalds 		switch (cmsg->cmsg_type) {
2501da177e4SLinus Torvalds 		case IP_RETOPTS:
2511da177e4SLinus Torvalds 			err = cmsg->cmsg_len - CMSG_ALIGN(sizeof(struct cmsghdr));
2524d52cfbeSEric Dumazet 			err = ip_options_get(net, &ipc->opt, CMSG_DATA(cmsg),
2534d52cfbeSEric Dumazet 					     err < 40 ? err : 40);
2541da177e4SLinus Torvalds 			if (err)
2551da177e4SLinus Torvalds 				return err;
2561da177e4SLinus Torvalds 			break;
2571da177e4SLinus Torvalds 		case IP_PKTINFO:
2581da177e4SLinus Torvalds 		{
2591da177e4SLinus Torvalds 			struct in_pktinfo *info;
2601da177e4SLinus Torvalds 			if (cmsg->cmsg_len != CMSG_LEN(sizeof(struct in_pktinfo)))
2611da177e4SLinus Torvalds 				return -EINVAL;
2621da177e4SLinus Torvalds 			info = (struct in_pktinfo *)CMSG_DATA(cmsg);
2631da177e4SLinus Torvalds 			ipc->oif = info->ipi_ifindex;
2641da177e4SLinus Torvalds 			ipc->addr = info->ipi_spec_dst.s_addr;
2651da177e4SLinus Torvalds 			break;
2661da177e4SLinus Torvalds 		}
267f02db315SFrancesco Fusco 		case IP_TTL:
268f02db315SFrancesco Fusco 			if (cmsg->cmsg_len != CMSG_LEN(sizeof(int)))
269f02db315SFrancesco Fusco 				return -EINVAL;
270f02db315SFrancesco Fusco 			val = *(int *)CMSG_DATA(cmsg);
271f02db315SFrancesco Fusco 			if (val < 1 || val > 255)
272f02db315SFrancesco Fusco 				return -EINVAL;
273f02db315SFrancesco Fusco 			ipc->ttl = val;
274f02db315SFrancesco Fusco 			break;
275f02db315SFrancesco Fusco 		case IP_TOS:
276f02db315SFrancesco Fusco 			if (cmsg->cmsg_len != CMSG_LEN(sizeof(int)))
277f02db315SFrancesco Fusco 				return -EINVAL;
278f02db315SFrancesco Fusco 			val = *(int *)CMSG_DATA(cmsg);
279f02db315SFrancesco Fusco 			if (val < 0 || val > 255)
280f02db315SFrancesco Fusco 				return -EINVAL;
281f02db315SFrancesco Fusco 			ipc->tos = val;
282f02db315SFrancesco Fusco 			ipc->priority = rt_tos2priority(ipc->tos);
283f02db315SFrancesco Fusco 			break;
284f02db315SFrancesco Fusco 
2851da177e4SLinus Torvalds 		default:
2861da177e4SLinus Torvalds 			return -EINVAL;
2871da177e4SLinus Torvalds 		}
2881da177e4SLinus Torvalds 	}
2891da177e4SLinus Torvalds 	return 0;
2901da177e4SLinus Torvalds }
2911da177e4SLinus Torvalds 
2921da177e4SLinus Torvalds 
2931da177e4SLinus Torvalds /* Special input handler for packets caught by router alert option.
2941da177e4SLinus Torvalds    They are selected only by protocol field, and then processed likely
2951da177e4SLinus Torvalds    local ones; but only if someone wants them! Otherwise, router
2961da177e4SLinus Torvalds    not running rsvpd will kill RSVP.
2971da177e4SLinus Torvalds 
2981da177e4SLinus Torvalds    It is user level problem, what it will make with them.
2991da177e4SLinus Torvalds    I have no idea, how it will masquearde or NAT them (it is joke, joke :-)),
3001da177e4SLinus Torvalds    but receiver should be enough clever f.e. to forward mtrace requests,
3011da177e4SLinus Torvalds    sent to multicast group to reach destination designated router.
3021da177e4SLinus Torvalds  */
30343a951e9SEric Dumazet struct ip_ra_chain __rcu *ip_ra_chain;
30466018506SEric Dumazet static DEFINE_SPINLOCK(ip_ra_lock);
30566018506SEric Dumazet 
306592fcb9dSEric Dumazet 
307592fcb9dSEric Dumazet static void ip_ra_destroy_rcu(struct rcu_head *head)
30866018506SEric Dumazet {
309592fcb9dSEric Dumazet 	struct ip_ra_chain *ra = container_of(head, struct ip_ra_chain, rcu);
310592fcb9dSEric Dumazet 
311592fcb9dSEric Dumazet 	sock_put(ra->saved_sk);
312592fcb9dSEric Dumazet 	kfree(ra);
31366018506SEric Dumazet }
3141da177e4SLinus Torvalds 
3154d52cfbeSEric Dumazet int ip_ra_control(struct sock *sk, unsigned char on,
3164d52cfbeSEric Dumazet 		  void (*destructor)(struct sock *))
3171da177e4SLinus Torvalds {
31843a951e9SEric Dumazet 	struct ip_ra_chain *ra, *new_ra;
31943a951e9SEric Dumazet 	struct ip_ra_chain __rcu **rap;
3201da177e4SLinus Torvalds 
321c720c7e8SEric Dumazet 	if (sk->sk_type != SOCK_RAW || inet_sk(sk)->inet_num == IPPROTO_RAW)
3221da177e4SLinus Torvalds 		return -EINVAL;
3231da177e4SLinus Torvalds 
3241da177e4SLinus Torvalds 	new_ra = on ? kmalloc(sizeof(*new_ra), GFP_KERNEL) : NULL;
3251da177e4SLinus Torvalds 
32666018506SEric Dumazet 	spin_lock_bh(&ip_ra_lock);
32743a951e9SEric Dumazet 	for (rap = &ip_ra_chain;
32843a951e9SEric Dumazet 	     (ra = rcu_dereference_protected(*rap,
32943a951e9SEric Dumazet 			lockdep_is_held(&ip_ra_lock))) != NULL;
33043a951e9SEric Dumazet 	     rap = &ra->next) {
3311da177e4SLinus Torvalds 		if (ra->sk == sk) {
3321da177e4SLinus Torvalds 			if (on) {
33366018506SEric Dumazet 				spin_unlock_bh(&ip_ra_lock);
3341da177e4SLinus Torvalds 				kfree(new_ra);
3351da177e4SLinus Torvalds 				return -EADDRINUSE;
3361da177e4SLinus Torvalds 			}
337592fcb9dSEric Dumazet 			/* dont let ip_call_ra_chain() use sk again */
338592fcb9dSEric Dumazet 			ra->sk = NULL;
3398e380f00SEric Dumazet 			RCU_INIT_POINTER(*rap, ra->next);
34066018506SEric Dumazet 			spin_unlock_bh(&ip_ra_lock);
3411da177e4SLinus Torvalds 
3421da177e4SLinus Torvalds 			if (ra->destructor)
3431da177e4SLinus Torvalds 				ra->destructor(sk);
344592fcb9dSEric Dumazet 			/*
345592fcb9dSEric Dumazet 			 * Delay sock_put(sk) and kfree(ra) after one rcu grace
346592fcb9dSEric Dumazet 			 * period. This guarantee ip_call_ra_chain() dont need
347592fcb9dSEric Dumazet 			 * to mess with socket refcounts.
348592fcb9dSEric Dumazet 			 */
349592fcb9dSEric Dumazet 			ra->saved_sk = sk;
350592fcb9dSEric Dumazet 			call_rcu(&ra->rcu, ip_ra_destroy_rcu);
3511da177e4SLinus Torvalds 			return 0;
3521da177e4SLinus Torvalds 		}
3531da177e4SLinus Torvalds 	}
35451456b29SIan Morris 	if (!new_ra) {
35566018506SEric Dumazet 		spin_unlock_bh(&ip_ra_lock);
3561da177e4SLinus Torvalds 		return -ENOBUFS;
3571da177e4SLinus Torvalds 	}
3581da177e4SLinus Torvalds 	new_ra->sk = sk;
3591da177e4SLinus Torvalds 	new_ra->destructor = destructor;
3601da177e4SLinus Torvalds 
3618e380f00SEric Dumazet 	RCU_INIT_POINTER(new_ra->next, ra);
36266018506SEric Dumazet 	rcu_assign_pointer(*rap, new_ra);
3631da177e4SLinus Torvalds 	sock_hold(sk);
36466018506SEric Dumazet 	spin_unlock_bh(&ip_ra_lock);
3651da177e4SLinus Torvalds 
3661da177e4SLinus Torvalds 	return 0;
3671da177e4SLinus Torvalds }
3681da177e4SLinus Torvalds 
3691da177e4SLinus Torvalds void ip_icmp_error(struct sock *sk, struct sk_buff *skb, int err,
37035986b32SAl Viro 		   __be16 port, u32 info, u8 *payload)
3711da177e4SLinus Torvalds {
3721da177e4SLinus Torvalds 	struct sock_exterr_skb *serr;
3731da177e4SLinus Torvalds 
3741da177e4SLinus Torvalds 	skb = skb_clone(skb, GFP_ATOMIC);
3751da177e4SLinus Torvalds 	if (!skb)
3761da177e4SLinus Torvalds 		return;
3771da177e4SLinus Torvalds 
3781da177e4SLinus Torvalds 	serr = SKB_EXT_ERR(skb);
3791da177e4SLinus Torvalds 	serr->ee.ee_errno = err;
3801da177e4SLinus Torvalds 	serr->ee.ee_origin = SO_EE_ORIGIN_ICMP;
38188c7664fSArnaldo Carvalho de Melo 	serr->ee.ee_type = icmp_hdr(skb)->type;
38288c7664fSArnaldo Carvalho de Melo 	serr->ee.ee_code = icmp_hdr(skb)->code;
3831da177e4SLinus Torvalds 	serr->ee.ee_pad = 0;
3841da177e4SLinus Torvalds 	serr->ee.ee_info = info;
3851da177e4SLinus Torvalds 	serr->ee.ee_data = 0;
38688c7664fSArnaldo Carvalho de Melo 	serr->addr_offset = (u8 *)&(((struct iphdr *)(icmp_hdr(skb) + 1))->daddr) -
387d56f90a7SArnaldo Carvalho de Melo 				   skb_network_header(skb);
3881da177e4SLinus Torvalds 	serr->port = port;
3891da177e4SLinus Torvalds 
39000db4124SIan Morris 	if (skb_pull(skb, payload - skb->data)) {
391bd82393cSArnaldo Carvalho de Melo 		skb_reset_transport_header(skb);
392bd82393cSArnaldo Carvalho de Melo 		if (sock_queue_err_skb(sk, skb) == 0)
393bd82393cSArnaldo Carvalho de Melo 			return;
394bd82393cSArnaldo Carvalho de Melo 	}
3951da177e4SLinus Torvalds 	kfree_skb(skb);
3961da177e4SLinus Torvalds }
3971da177e4SLinus Torvalds 
3980579016eSAl Viro void ip_local_error(struct sock *sk, int err, __be32 daddr, __be16 port, u32 info)
3991da177e4SLinus Torvalds {
4001da177e4SLinus Torvalds 	struct inet_sock *inet = inet_sk(sk);
4011da177e4SLinus Torvalds 	struct sock_exterr_skb *serr;
4021da177e4SLinus Torvalds 	struct iphdr *iph;
4031da177e4SLinus Torvalds 	struct sk_buff *skb;
4041da177e4SLinus Torvalds 
4051da177e4SLinus Torvalds 	if (!inet->recverr)
4061da177e4SLinus Torvalds 		return;
4071da177e4SLinus Torvalds 
4081da177e4SLinus Torvalds 	skb = alloc_skb(sizeof(struct iphdr), GFP_ATOMIC);
4091da177e4SLinus Torvalds 	if (!skb)
4101da177e4SLinus Torvalds 		return;
4111da177e4SLinus Torvalds 
4122ca9e6f2SArnaldo Carvalho de Melo 	skb_put(skb, sizeof(struct iphdr));
4132ca9e6f2SArnaldo Carvalho de Melo 	skb_reset_network_header(skb);
414eddc9ec5SArnaldo Carvalho de Melo 	iph = ip_hdr(skb);
4151da177e4SLinus Torvalds 	iph->daddr = daddr;
4161da177e4SLinus Torvalds 
4171da177e4SLinus Torvalds 	serr = SKB_EXT_ERR(skb);
4181da177e4SLinus Torvalds 	serr->ee.ee_errno = err;
4191da177e4SLinus Torvalds 	serr->ee.ee_origin = SO_EE_ORIGIN_LOCAL;
4201da177e4SLinus Torvalds 	serr->ee.ee_type = 0;
4211da177e4SLinus Torvalds 	serr->ee.ee_code = 0;
4221da177e4SLinus Torvalds 	serr->ee.ee_pad = 0;
4231da177e4SLinus Torvalds 	serr->ee.ee_info = info;
4241da177e4SLinus Torvalds 	serr->ee.ee_data = 0;
425d56f90a7SArnaldo Carvalho de Melo 	serr->addr_offset = (u8 *)&iph->daddr - skb_network_header(skb);
4261da177e4SLinus Torvalds 	serr->port = port;
4271da177e4SLinus Torvalds 
42827a884dcSArnaldo Carvalho de Melo 	__skb_pull(skb, skb_tail_pointer(skb) - skb->data);
429bd82393cSArnaldo Carvalho de Melo 	skb_reset_transport_header(skb);
4301da177e4SLinus Torvalds 
4311da177e4SLinus Torvalds 	if (sock_queue_err_skb(sk, skb))
4321da177e4SLinus Torvalds 		kfree_skb(skb);
4331da177e4SLinus Torvalds }
4341da177e4SLinus Torvalds 
43534b99df4SJulian Anastasov /* For some errors we have valid addr_offset even with zero payload and
43634b99df4SJulian Anastasov  * zero port. Also, addr_offset should be supported if port is set.
43734b99df4SJulian Anastasov  */
43834b99df4SJulian Anastasov static inline bool ipv4_datagram_support_addr(struct sock_exterr_skb *serr)
43934b99df4SJulian Anastasov {
44034b99df4SJulian Anastasov 	return serr->ee.ee_origin == SO_EE_ORIGIN_ICMP ||
44134b99df4SJulian Anastasov 	       serr->ee.ee_origin == SO_EE_ORIGIN_LOCAL || serr->port;
44234b99df4SJulian Anastasov }
44334b99df4SJulian Anastasov 
444c247f053SWillem de Bruijn /* IPv4 supports cmsg on all imcp errors and some timestamps
445c247f053SWillem de Bruijn  *
446c247f053SWillem de Bruijn  * Timestamp code paths do not initialize the fields expected by cmsg:
447c247f053SWillem de Bruijn  * the PKTINFO fields in skb->cb[]. Fill those in here.
448c247f053SWillem de Bruijn  */
449c247f053SWillem de Bruijn static bool ipv4_datagram_support_cmsg(const struct sock *sk,
450c247f053SWillem de Bruijn 				       struct sk_buff *skb,
451829ae9d6SWillem de Bruijn 				       int ee_origin)
452829ae9d6SWillem de Bruijn {
453c247f053SWillem de Bruijn 	struct in_pktinfo *info;
454829ae9d6SWillem de Bruijn 
455c247f053SWillem de Bruijn 	if (ee_origin == SO_EE_ORIGIN_ICMP)
456c247f053SWillem de Bruijn 		return true;
457c247f053SWillem de Bruijn 
458c247f053SWillem de Bruijn 	if (ee_origin == SO_EE_ORIGIN_LOCAL)
459c247f053SWillem de Bruijn 		return false;
460c247f053SWillem de Bruijn 
461c247f053SWillem de Bruijn 	/* Support IP_PKTINFO on tstamp packets if requested, to correlate
462c247f053SWillem de Bruijn 	 * timestamp with egress dev. Not possible for packets without dev
463c247f053SWillem de Bruijn 	 * or without payload (SOF_TIMESTAMPING_OPT_TSONLY).
464c247f053SWillem de Bruijn 	 */
465c247f053SWillem de Bruijn 	if ((!(sk->sk_tsflags & SOF_TIMESTAMPING_OPT_CMSG)) ||
466829ae9d6SWillem de Bruijn 	    (!skb->dev))
467829ae9d6SWillem de Bruijn 		return false;
468829ae9d6SWillem de Bruijn 
469c247f053SWillem de Bruijn 	info = PKTINFO_SKB_CB(skb);
470829ae9d6SWillem de Bruijn 	info->ipi_spec_dst.s_addr = ip_hdr(skb)->saddr;
471829ae9d6SWillem de Bruijn 	info->ipi_ifindex = skb->dev->ifindex;
472829ae9d6SWillem de Bruijn 	return true;
473829ae9d6SWillem de Bruijn }
474829ae9d6SWillem de Bruijn 
4751da177e4SLinus Torvalds /*
4761da177e4SLinus Torvalds  *	Handle MSG_ERRQUEUE
4771da177e4SLinus Torvalds  */
47885fbaa75SHannes Frederic Sowa int ip_recv_error(struct sock *sk, struct msghdr *msg, int len, int *addr_len)
4791da177e4SLinus Torvalds {
4801da177e4SLinus Torvalds 	struct sock_exterr_skb *serr;
481364a9e93SWillem de Bruijn 	struct sk_buff *skb;
482342dfc30SSteffen Hurrle 	DECLARE_SOCKADDR(struct sockaddr_in *, sin, msg->msg_name);
4831da177e4SLinus Torvalds 	struct {
4841da177e4SLinus Torvalds 		struct sock_extended_err ee;
4851da177e4SLinus Torvalds 		struct sockaddr_in	 offender;
4861da177e4SLinus Torvalds 	} errhdr;
4871da177e4SLinus Torvalds 	int err;
4881da177e4SLinus Torvalds 	int copied;
4891da177e4SLinus Torvalds 
4907ce875e5SWillem de Bruijn 	WARN_ON_ONCE(sk->sk_family == AF_INET6);
4917ce875e5SWillem de Bruijn 
4921da177e4SLinus Torvalds 	err = -EAGAIN;
493364a9e93SWillem de Bruijn 	skb = sock_dequeue_err_skb(sk);
49451456b29SIan Morris 	if (!skb)
4951da177e4SLinus Torvalds 		goto out;
4961da177e4SLinus Torvalds 
4971da177e4SLinus Torvalds 	copied = skb->len;
4981da177e4SLinus Torvalds 	if (copied > len) {
4991da177e4SLinus Torvalds 		msg->msg_flags |= MSG_TRUNC;
5001da177e4SLinus Torvalds 		copied = len;
5011da177e4SLinus Torvalds 	}
50251f3d02bSDavid S. Miller 	err = skb_copy_datagram_msg(skb, 0, msg, copied);
5031da177e4SLinus Torvalds 	if (err)
5041da177e4SLinus Torvalds 		goto out_free_skb;
5051da177e4SLinus Torvalds 
5061da177e4SLinus Torvalds 	sock_recv_timestamp(msg, sk, skb);
5071da177e4SLinus Torvalds 
5081da177e4SLinus Torvalds 	serr = SKB_EXT_ERR(skb);
5091da177e4SLinus Torvalds 
51034b99df4SJulian Anastasov 	if (sin && ipv4_datagram_support_addr(serr)) {
5111da177e4SLinus Torvalds 		sin->sin_family = AF_INET;
512d56f90a7SArnaldo Carvalho de Melo 		sin->sin_addr.s_addr = *(__be32 *)(skb_network_header(skb) +
513d56f90a7SArnaldo Carvalho de Melo 						   serr->addr_offset);
5141da177e4SLinus Torvalds 		sin->sin_port = serr->port;
5151da177e4SLinus Torvalds 		memset(&sin->sin_zero, 0, sizeof(sin->sin_zero));
51685fbaa75SHannes Frederic Sowa 		*addr_len = sizeof(*sin);
5171da177e4SLinus Torvalds 	}
5181da177e4SLinus Torvalds 
5191da177e4SLinus Torvalds 	memcpy(&errhdr.ee, &serr->ee, sizeof(struct sock_extended_err));
5201da177e4SLinus Torvalds 	sin = &errhdr.offender;
521f812116bSWillem de Bruijn 	memset(sin, 0, sizeof(*sin));
522829ae9d6SWillem de Bruijn 
523c247f053SWillem de Bruijn 	if (ipv4_datagram_support_cmsg(sk, skb, serr->ee.ee_origin)) {
5241da177e4SLinus Torvalds 		sin->sin_family = AF_INET;
525eddc9ec5SArnaldo Carvalho de Melo 		sin->sin_addr.s_addr = ip_hdr(skb)->saddr;
526f812116bSWillem de Bruijn 		if (inet_sk(sk)->cmsg_flags)
5271da177e4SLinus Torvalds 			ip_cmsg_recv(msg, skb);
5281da177e4SLinus Torvalds 	}
5291da177e4SLinus Torvalds 
5301da177e4SLinus Torvalds 	put_cmsg(msg, SOL_IP, IP_RECVERR, sizeof(errhdr), &errhdr);
5311da177e4SLinus Torvalds 
5321da177e4SLinus Torvalds 	/* Now we could try to dump offended packet options */
5331da177e4SLinus Torvalds 
5341da177e4SLinus Torvalds 	msg->msg_flags |= MSG_ERRQUEUE;
5351da177e4SLinus Torvalds 	err = copied;
5361da177e4SLinus Torvalds 
5371da177e4SLinus Torvalds out_free_skb:
5381da177e4SLinus Torvalds 	kfree_skb(skb);
5391da177e4SLinus Torvalds out:
5401da177e4SLinus Torvalds 	return err;
5411da177e4SLinus Torvalds }
5421da177e4SLinus Torvalds 
5431da177e4SLinus Torvalds 
5441da177e4SLinus Torvalds /*
5454d52cfbeSEric Dumazet  *	Socket option code for IP. This is the end of the line after any
5464d52cfbeSEric Dumazet  *	TCP,UDP etc options on an IP socket.
5471da177e4SLinus Torvalds  */
548baf606d9SMarcelo Ricardo Leitner static bool setsockopt_needs_rtnl(int optname)
549baf606d9SMarcelo Ricardo Leitner {
550baf606d9SMarcelo Ricardo Leitner 	switch (optname) {
551baf606d9SMarcelo Ricardo Leitner 	case IP_ADD_MEMBERSHIP:
552baf606d9SMarcelo Ricardo Leitner 	case IP_ADD_SOURCE_MEMBERSHIP:
55354ff9ef3SMarcelo Ricardo Leitner 	case IP_BLOCK_SOURCE:
554baf606d9SMarcelo Ricardo Leitner 	case IP_DROP_MEMBERSHIP:
55554ff9ef3SMarcelo Ricardo Leitner 	case IP_DROP_SOURCE_MEMBERSHIP:
55654ff9ef3SMarcelo Ricardo Leitner 	case IP_MSFILTER:
55754ff9ef3SMarcelo Ricardo Leitner 	case IP_UNBLOCK_SOURCE:
55854ff9ef3SMarcelo Ricardo Leitner 	case MCAST_BLOCK_SOURCE:
55954ff9ef3SMarcelo Ricardo Leitner 	case MCAST_MSFILTER:
560baf606d9SMarcelo Ricardo Leitner 	case MCAST_JOIN_GROUP:
56154ff9ef3SMarcelo Ricardo Leitner 	case MCAST_JOIN_SOURCE_GROUP:
562baf606d9SMarcelo Ricardo Leitner 	case MCAST_LEAVE_GROUP:
56354ff9ef3SMarcelo Ricardo Leitner 	case MCAST_LEAVE_SOURCE_GROUP:
56454ff9ef3SMarcelo Ricardo Leitner 	case MCAST_UNBLOCK_SOURCE:
565baf606d9SMarcelo Ricardo Leitner 		return true;
566baf606d9SMarcelo Ricardo Leitner 	}
567baf606d9SMarcelo Ricardo Leitner 	return false;
568baf606d9SMarcelo Ricardo Leitner }
5691da177e4SLinus Torvalds 
5703fdadf7dSDmitry Mishin static int do_ip_setsockopt(struct sock *sk, int level,
571b7058842SDavid S. Miller 			    int optname, char __user *optval, unsigned int optlen)
5721da177e4SLinus Torvalds {
5731da177e4SLinus Torvalds 	struct inet_sock *inet = inet_sk(sk);
574166b6b2dSNikolay Borisov 	struct net *net = sock_net(sk);
5751da177e4SLinus Torvalds 	int val = 0, err;
576baf606d9SMarcelo Ricardo Leitner 	bool needs_rtnl = setsockopt_needs_rtnl(optname);
5771da177e4SLinus Torvalds 
5780c9f79beSXi Wang 	switch (optname) {
5790c9f79beSXi Wang 	case IP_PKTINFO:
5800c9f79beSXi Wang 	case IP_RECVTTL:
5810c9f79beSXi Wang 	case IP_RECVOPTS:
5820c9f79beSXi Wang 	case IP_RECVTOS:
5830c9f79beSXi Wang 	case IP_RETOPTS:
5840c9f79beSXi Wang 	case IP_TOS:
5850c9f79beSXi Wang 	case IP_TTL:
5860c9f79beSXi Wang 	case IP_HDRINCL:
5870c9f79beSXi Wang 	case IP_MTU_DISCOVER:
5880c9f79beSXi Wang 	case IP_RECVERR:
5890c9f79beSXi Wang 	case IP_ROUTER_ALERT:
5900c9f79beSXi Wang 	case IP_FREEBIND:
5910c9f79beSXi Wang 	case IP_PASSSEC:
5920c9f79beSXi Wang 	case IP_TRANSPARENT:
5930c9f79beSXi Wang 	case IP_MINTTL:
5940c9f79beSXi Wang 	case IP_NODEFRAG:
59590c337daSEric Dumazet 	case IP_BIND_ADDRESS_NO_PORT:
5960c9f79beSXi Wang 	case IP_UNICAST_IF:
5970c9f79beSXi Wang 	case IP_MULTICAST_TTL:
5980c9f79beSXi Wang 	case IP_MULTICAST_ALL:
5990c9f79beSXi Wang 	case IP_MULTICAST_LOOP:
6000c9f79beSXi Wang 	case IP_RECVORIGDSTADDR:
601ad6f939aSTom Herbert 	case IP_CHECKSUM:
6021da177e4SLinus Torvalds 		if (optlen >= sizeof(int)) {
6031da177e4SLinus Torvalds 			if (get_user(val, (int __user *) optval))
6041da177e4SLinus Torvalds 				return -EFAULT;
6051da177e4SLinus Torvalds 		} else if (optlen >= sizeof(char)) {
6061da177e4SLinus Torvalds 			unsigned char ucval;
6071da177e4SLinus Torvalds 
6081da177e4SLinus Torvalds 			if (get_user(ucval, (unsigned char __user *) optval))
6091da177e4SLinus Torvalds 				return -EFAULT;
6101da177e4SLinus Torvalds 			val = (int) ucval;
6111da177e4SLinus Torvalds 		}
6121da177e4SLinus Torvalds 	}
6131da177e4SLinus Torvalds 
6141da177e4SLinus Torvalds 	/* If optlen==0, it is equivalent to val == 0 */
6151da177e4SLinus Torvalds 
6166a9fb947SPavel Emelyanov 	if (ip_mroute_opt(optname))
6171da177e4SLinus Torvalds 		return ip_mroute_setsockopt(sk, optname, optval, optlen);
6181da177e4SLinus Torvalds 
6191da177e4SLinus Torvalds 	err = 0;
620baf606d9SMarcelo Ricardo Leitner 	if (needs_rtnl)
621baf606d9SMarcelo Ricardo Leitner 		rtnl_lock();
6221da177e4SLinus Torvalds 	lock_sock(sk);
6231da177e4SLinus Torvalds 
6241da177e4SLinus Torvalds 	switch (optname) {
6251da177e4SLinus Torvalds 	case IP_OPTIONS:
6261da177e4SLinus Torvalds 	{
627f6d8bd05SEric Dumazet 		struct ip_options_rcu *old, *opt = NULL;
628f6d8bd05SEric Dumazet 
62965a1c4ffSroel kluin 		if (optlen > 40)
6301da177e4SLinus Torvalds 			goto e_inval;
6313b1e0a65SYOSHIFUJI Hideaki 		err = ip_options_get_from_user(sock_net(sk), &opt,
632cb84663eSDenis V. Lunev 					       optval, optlen);
6331da177e4SLinus Torvalds 		if (err)
6341da177e4SLinus Torvalds 			break;
635f6d8bd05SEric Dumazet 		old = rcu_dereference_protected(inet->inet_opt,
636f6d8bd05SEric Dumazet 						sock_owned_by_user(sk));
637d83d8461SArnaldo Carvalho de Melo 		if (inet->is_icsk) {
638d83d8461SArnaldo Carvalho de Melo 			struct inet_connection_sock *icsk = inet_csk(sk);
639dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6)
6401da177e4SLinus Torvalds 			if (sk->sk_family == PF_INET ||
6411da177e4SLinus Torvalds 			    (!((1 << sk->sk_state) &
6421da177e4SLinus Torvalds 			       (TCPF_LISTEN | TCPF_CLOSE)) &&
643c720c7e8SEric Dumazet 			     inet->inet_daddr != LOOPBACK4_IPV6)) {
6441da177e4SLinus Torvalds #endif
645f6d8bd05SEric Dumazet 				if (old)
646f6d8bd05SEric Dumazet 					icsk->icsk_ext_hdr_len -= old->opt.optlen;
6471da177e4SLinus Torvalds 				if (opt)
648f6d8bd05SEric Dumazet 					icsk->icsk_ext_hdr_len += opt->opt.optlen;
649d83d8461SArnaldo Carvalho de Melo 				icsk->icsk_sync_mss(sk, icsk->icsk_pmtu_cookie);
650dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6)
6511da177e4SLinus Torvalds 			}
6521da177e4SLinus Torvalds #endif
6531da177e4SLinus Torvalds 		}
654f6d8bd05SEric Dumazet 		rcu_assign_pointer(inet->inet_opt, opt);
655f6d8bd05SEric Dumazet 		if (old)
656605b4afeSPaul E. McKenney 			kfree_rcu(old, rcu);
6571da177e4SLinus Torvalds 		break;
6581da177e4SLinus Torvalds 	}
6591da177e4SLinus Torvalds 	case IP_PKTINFO:
6601da177e4SLinus Torvalds 		if (val)
6611da177e4SLinus Torvalds 			inet->cmsg_flags |= IP_CMSG_PKTINFO;
6621da177e4SLinus Torvalds 		else
6631da177e4SLinus Torvalds 			inet->cmsg_flags &= ~IP_CMSG_PKTINFO;
6641da177e4SLinus Torvalds 		break;
6651da177e4SLinus Torvalds 	case IP_RECVTTL:
6661da177e4SLinus Torvalds 		if (val)
6671da177e4SLinus Torvalds 			inet->cmsg_flags |=  IP_CMSG_TTL;
6681da177e4SLinus Torvalds 		else
6691da177e4SLinus Torvalds 			inet->cmsg_flags &= ~IP_CMSG_TTL;
6701da177e4SLinus Torvalds 		break;
6711da177e4SLinus Torvalds 	case IP_RECVTOS:
6721da177e4SLinus Torvalds 		if (val)
6731da177e4SLinus Torvalds 			inet->cmsg_flags |=  IP_CMSG_TOS;
6741da177e4SLinus Torvalds 		else
6751da177e4SLinus Torvalds 			inet->cmsg_flags &= ~IP_CMSG_TOS;
6761da177e4SLinus Torvalds 		break;
6771da177e4SLinus Torvalds 	case IP_RECVOPTS:
6781da177e4SLinus Torvalds 		if (val)
6791da177e4SLinus Torvalds 			inet->cmsg_flags |=  IP_CMSG_RECVOPTS;
6801da177e4SLinus Torvalds 		else
6811da177e4SLinus Torvalds 			inet->cmsg_flags &= ~IP_CMSG_RECVOPTS;
6821da177e4SLinus Torvalds 		break;
6831da177e4SLinus Torvalds 	case IP_RETOPTS:
6841da177e4SLinus Torvalds 		if (val)
6851da177e4SLinus Torvalds 			inet->cmsg_flags |= IP_CMSG_RETOPTS;
6861da177e4SLinus Torvalds 		else
6871da177e4SLinus Torvalds 			inet->cmsg_flags &= ~IP_CMSG_RETOPTS;
6881da177e4SLinus Torvalds 		break;
6892c7946a7SCatherine Zhang 	case IP_PASSSEC:
6902c7946a7SCatherine Zhang 		if (val)
6912c7946a7SCatherine Zhang 			inet->cmsg_flags |= IP_CMSG_PASSSEC;
6922c7946a7SCatherine Zhang 		else
6932c7946a7SCatherine Zhang 			inet->cmsg_flags &= ~IP_CMSG_PASSSEC;
6942c7946a7SCatherine Zhang 		break;
695e8b2dfe9SBalazs Scheidler 	case IP_RECVORIGDSTADDR:
696e8b2dfe9SBalazs Scheidler 		if (val)
697e8b2dfe9SBalazs Scheidler 			inet->cmsg_flags |= IP_CMSG_ORIGDSTADDR;
698e8b2dfe9SBalazs Scheidler 		else
699e8b2dfe9SBalazs Scheidler 			inet->cmsg_flags &= ~IP_CMSG_ORIGDSTADDR;
700e8b2dfe9SBalazs Scheidler 		break;
701ad6f939aSTom Herbert 	case IP_CHECKSUM:
702ad6f939aSTom Herbert 		if (val) {
703ad6f939aSTom Herbert 			if (!(inet->cmsg_flags & IP_CMSG_CHECKSUM)) {
704ad6f939aSTom Herbert 				inet_inc_convert_csum(sk);
705ad6f939aSTom Herbert 				inet->cmsg_flags |= IP_CMSG_CHECKSUM;
706ad6f939aSTom Herbert 			}
707ad6f939aSTom Herbert 		} else {
708ad6f939aSTom Herbert 			if (inet->cmsg_flags & IP_CMSG_CHECKSUM) {
709ad6f939aSTom Herbert 				inet_dec_convert_csum(sk);
710ad6f939aSTom Herbert 				inet->cmsg_flags &= ~IP_CMSG_CHECKSUM;
711ad6f939aSTom Herbert 			}
712ad6f939aSTom Herbert 		}
713ad6f939aSTom Herbert 		break;
7141da177e4SLinus Torvalds 	case IP_TOS:	/* This sets both TOS and Precedence */
7151da177e4SLinus Torvalds 		if (sk->sk_type == SOCK_STREAM) {
7162c67e9acSMaciej Żenczykowski 			val &= ~INET_ECN_MASK;
7172c67e9acSMaciej Żenczykowski 			val |= inet->tos & INET_ECN_MASK;
7181da177e4SLinus Torvalds 		}
7191da177e4SLinus Torvalds 		if (inet->tos != val) {
7201da177e4SLinus Torvalds 			inet->tos = val;
7211da177e4SLinus Torvalds 			sk->sk_priority = rt_tos2priority(val);
7221da177e4SLinus Torvalds 			sk_dst_reset(sk);
7231da177e4SLinus Torvalds 		}
7241da177e4SLinus Torvalds 		break;
7251da177e4SLinus Torvalds 	case IP_TTL:
7261da177e4SLinus Torvalds 		if (optlen < 1)
7271da177e4SLinus Torvalds 			goto e_inval;
728c9be4a5cSCong Wang 		if (val != -1 && (val < 1 || val > 255))
7291da177e4SLinus Torvalds 			goto e_inval;
7301da177e4SLinus Torvalds 		inet->uc_ttl = val;
7311da177e4SLinus Torvalds 		break;
7321da177e4SLinus Torvalds 	case IP_HDRINCL:
7331da177e4SLinus Torvalds 		if (sk->sk_type != SOCK_RAW) {
7341da177e4SLinus Torvalds 			err = -ENOPROTOOPT;
7351da177e4SLinus Torvalds 			break;
7361da177e4SLinus Torvalds 		}
7371da177e4SLinus Torvalds 		inet->hdrincl = val ? 1 : 0;
7381da177e4SLinus Torvalds 		break;
7397b2ff18eSJiri Olsa 	case IP_NODEFRAG:
7407b2ff18eSJiri Olsa 		if (sk->sk_type != SOCK_RAW) {
7417b2ff18eSJiri Olsa 			err = -ENOPROTOOPT;
7427b2ff18eSJiri Olsa 			break;
7437b2ff18eSJiri Olsa 		}
7447b2ff18eSJiri Olsa 		inet->nodefrag = val ? 1 : 0;
7457b2ff18eSJiri Olsa 		break;
74690c337daSEric Dumazet 	case IP_BIND_ADDRESS_NO_PORT:
74790c337daSEric Dumazet 		inet->bind_address_no_port = val ? 1 : 0;
74890c337daSEric Dumazet 		break;
7491da177e4SLinus Torvalds 	case IP_MTU_DISCOVER:
7501b346576SHannes Frederic Sowa 		if (val < IP_PMTUDISC_DONT || val > IP_PMTUDISC_OMIT)
7511da177e4SLinus Torvalds 			goto e_inval;
7521da177e4SLinus Torvalds 		inet->pmtudisc = val;
7531da177e4SLinus Torvalds 		break;
7541da177e4SLinus Torvalds 	case IP_RECVERR:
7551da177e4SLinus Torvalds 		inet->recverr = !!val;
7561da177e4SLinus Torvalds 		if (!val)
7571da177e4SLinus Torvalds 			skb_queue_purge(&sk->sk_error_queue);
7581da177e4SLinus Torvalds 		break;
7591da177e4SLinus Torvalds 	case IP_MULTICAST_TTL:
7601da177e4SLinus Torvalds 		if (sk->sk_type == SOCK_STREAM)
7611da177e4SLinus Torvalds 			goto e_inval;
7621da177e4SLinus Torvalds 		if (optlen < 1)
7631da177e4SLinus Torvalds 			goto e_inval;
7641da177e4SLinus Torvalds 		if (val == -1)
7651da177e4SLinus Torvalds 			val = 1;
7661da177e4SLinus Torvalds 		if (val < 0 || val > 255)
7671da177e4SLinus Torvalds 			goto e_inval;
7681da177e4SLinus Torvalds 		inet->mc_ttl = val;
7691da177e4SLinus Torvalds 		break;
7701da177e4SLinus Torvalds 	case IP_MULTICAST_LOOP:
7711da177e4SLinus Torvalds 		if (optlen < 1)
7721da177e4SLinus Torvalds 			goto e_inval;
7731da177e4SLinus Torvalds 		inet->mc_loop = !!val;
7741da177e4SLinus Torvalds 		break;
77576e21053SErich E. Hoover 	case IP_UNICAST_IF:
77676e21053SErich E. Hoover 	{
77776e21053SErich E. Hoover 		struct net_device *dev = NULL;
77876e21053SErich E. Hoover 		int ifindex;
77976e21053SErich E. Hoover 
78076e21053SErich E. Hoover 		if (optlen != sizeof(int))
78176e21053SErich E. Hoover 			goto e_inval;
78276e21053SErich E. Hoover 
78376e21053SErich E. Hoover 		ifindex = (__force int)ntohl((__force __be32)val);
78476e21053SErich E. Hoover 		if (ifindex == 0) {
78576e21053SErich E. Hoover 			inet->uc_index = 0;
78676e21053SErich E. Hoover 			err = 0;
78776e21053SErich E. Hoover 			break;
78876e21053SErich E. Hoover 		}
78976e21053SErich E. Hoover 
79076e21053SErich E. Hoover 		dev = dev_get_by_index(sock_net(sk), ifindex);
79176e21053SErich E. Hoover 		err = -EADDRNOTAVAIL;
79276e21053SErich E. Hoover 		if (!dev)
79376e21053SErich E. Hoover 			break;
79476e21053SErich E. Hoover 		dev_put(dev);
79576e21053SErich E. Hoover 
79676e21053SErich E. Hoover 		err = -EINVAL;
79776e21053SErich E. Hoover 		if (sk->sk_bound_dev_if)
79876e21053SErich E. Hoover 			break;
79976e21053SErich E. Hoover 
80076e21053SErich E. Hoover 		inet->uc_index = ifindex;
80176e21053SErich E. Hoover 		err = 0;
80276e21053SErich E. Hoover 		break;
80376e21053SErich E. Hoover 	}
8041da177e4SLinus Torvalds 	case IP_MULTICAST_IF:
8051da177e4SLinus Torvalds 	{
8061da177e4SLinus Torvalds 		struct ip_mreqn mreq;
8071da177e4SLinus Torvalds 		struct net_device *dev = NULL;
8081da177e4SLinus Torvalds 
8091da177e4SLinus Torvalds 		if (sk->sk_type == SOCK_STREAM)
8101da177e4SLinus Torvalds 			goto e_inval;
8111da177e4SLinus Torvalds 		/*
8121da177e4SLinus Torvalds 		 *	Check the arguments are allowable
8131da177e4SLinus Torvalds 		 */
8141da177e4SLinus Torvalds 
8150915921bSShan Wei 		if (optlen < sizeof(struct in_addr))
8160915921bSShan Wei 			goto e_inval;
8170915921bSShan Wei 
8181da177e4SLinus Torvalds 		err = -EFAULT;
8191da177e4SLinus Torvalds 		if (optlen >= sizeof(struct ip_mreqn)) {
8201da177e4SLinus Torvalds 			if (copy_from_user(&mreq, optval, sizeof(mreq)))
8211da177e4SLinus Torvalds 				break;
8221da177e4SLinus Torvalds 		} else {
8231da177e4SLinus Torvalds 			memset(&mreq, 0, sizeof(mreq));
8243a084ddbSJiri Pirko 			if (optlen >= sizeof(struct ip_mreq)) {
8253a084ddbSJiri Pirko 				if (copy_from_user(&mreq, optval,
8263a084ddbSJiri Pirko 						   sizeof(struct ip_mreq)))
8273a084ddbSJiri Pirko 					break;
8283a084ddbSJiri Pirko 			} else if (optlen >= sizeof(struct in_addr)) {
8293a084ddbSJiri Pirko 				if (copy_from_user(&mreq.imr_address, optval,
8304d52cfbeSEric Dumazet 						   sizeof(struct in_addr)))
8311da177e4SLinus Torvalds 					break;
8321da177e4SLinus Torvalds 			}
8333a084ddbSJiri Pirko 		}
8341da177e4SLinus Torvalds 
8351da177e4SLinus Torvalds 		if (!mreq.imr_ifindex) {
836e6f1cebfSAl Viro 			if (mreq.imr_address.s_addr == htonl(INADDR_ANY)) {
8371da177e4SLinus Torvalds 				inet->mc_index = 0;
8381da177e4SLinus Torvalds 				inet->mc_addr  = 0;
8391da177e4SLinus Torvalds 				err = 0;
8401da177e4SLinus Torvalds 				break;
8411da177e4SLinus Torvalds 			}
8423b1e0a65SYOSHIFUJI Hideaki 			dev = ip_dev_find(sock_net(sk), mreq.imr_address.s_addr);
84355b80503SEric Dumazet 			if (dev)
8441da177e4SLinus Torvalds 				mreq.imr_ifindex = dev->ifindex;
8451da177e4SLinus Torvalds 		} else
84655b80503SEric Dumazet 			dev = dev_get_by_index(sock_net(sk), mreq.imr_ifindex);
8471da177e4SLinus Torvalds 
8481da177e4SLinus Torvalds 
8491da177e4SLinus Torvalds 		err = -EADDRNOTAVAIL;
8501da177e4SLinus Torvalds 		if (!dev)
8511da177e4SLinus Torvalds 			break;
85255b80503SEric Dumazet 		dev_put(dev);
8531da177e4SLinus Torvalds 
8541da177e4SLinus Torvalds 		err = -EINVAL;
8551da177e4SLinus Torvalds 		if (sk->sk_bound_dev_if &&
8561da177e4SLinus Torvalds 		    mreq.imr_ifindex != sk->sk_bound_dev_if)
8571da177e4SLinus Torvalds 			break;
8581da177e4SLinus Torvalds 
8591da177e4SLinus Torvalds 		inet->mc_index = mreq.imr_ifindex;
8601da177e4SLinus Torvalds 		inet->mc_addr  = mreq.imr_address.s_addr;
8611da177e4SLinus Torvalds 		err = 0;
8621da177e4SLinus Torvalds 		break;
8631da177e4SLinus Torvalds 	}
8641da177e4SLinus Torvalds 
8651da177e4SLinus Torvalds 	case IP_ADD_MEMBERSHIP:
8661da177e4SLinus Torvalds 	case IP_DROP_MEMBERSHIP:
8671da177e4SLinus Torvalds 	{
8681da177e4SLinus Torvalds 		struct ip_mreqn mreq;
8691da177e4SLinus Torvalds 
870a96fb49bSFlavio Leitner 		err = -EPROTO;
871a96fb49bSFlavio Leitner 		if (inet_sk(sk)->is_icsk)
872a96fb49bSFlavio Leitner 			break;
873a96fb49bSFlavio Leitner 
8741da177e4SLinus Torvalds 		if (optlen < sizeof(struct ip_mreq))
8751da177e4SLinus Torvalds 			goto e_inval;
8761da177e4SLinus Torvalds 		err = -EFAULT;
8771da177e4SLinus Torvalds 		if (optlen >= sizeof(struct ip_mreqn)) {
8781da177e4SLinus Torvalds 			if (copy_from_user(&mreq, optval, sizeof(mreq)))
8791da177e4SLinus Torvalds 				break;
8801da177e4SLinus Torvalds 		} else {
8811da177e4SLinus Torvalds 			memset(&mreq, 0, sizeof(mreq));
8821da177e4SLinus Torvalds 			if (copy_from_user(&mreq, optval, sizeof(struct ip_mreq)))
8831da177e4SLinus Torvalds 				break;
8841da177e4SLinus Torvalds 		}
8851da177e4SLinus Torvalds 
8861da177e4SLinus Torvalds 		if (optname == IP_ADD_MEMBERSHIP)
88754ff9ef3SMarcelo Ricardo Leitner 			err = ip_mc_join_group(sk, &mreq);
8881da177e4SLinus Torvalds 		else
88954ff9ef3SMarcelo Ricardo Leitner 			err = ip_mc_leave_group(sk, &mreq);
8901da177e4SLinus Torvalds 		break;
8911da177e4SLinus Torvalds 	}
8921da177e4SLinus Torvalds 	case IP_MSFILTER:
8931da177e4SLinus Torvalds 	{
8941da177e4SLinus Torvalds 		struct ip_msfilter *msf;
8951da177e4SLinus Torvalds 
8961da177e4SLinus Torvalds 		if (optlen < IP_MSFILTER_SIZE(0))
8971da177e4SLinus Torvalds 			goto e_inval;
8981da177e4SLinus Torvalds 		if (optlen > sysctl_optmem_max) {
8991da177e4SLinus Torvalds 			err = -ENOBUFS;
9001da177e4SLinus Torvalds 			break;
9011da177e4SLinus Torvalds 		}
9028b3a7005SKris Katterjohn 		msf = kmalloc(optlen, GFP_KERNEL);
903cfcabdccSStephen Hemminger 		if (!msf) {
9041da177e4SLinus Torvalds 			err = -ENOBUFS;
9051da177e4SLinus Torvalds 			break;
9061da177e4SLinus Torvalds 		}
9071da177e4SLinus Torvalds 		err = -EFAULT;
9081da177e4SLinus Torvalds 		if (copy_from_user(msf, optval, optlen)) {
9091da177e4SLinus Torvalds 			kfree(msf);
9101da177e4SLinus Torvalds 			break;
9111da177e4SLinus Torvalds 		}
9121da177e4SLinus Torvalds 		/* numsrc >= (1G-4) overflow in 32 bits */
9131da177e4SLinus Torvalds 		if (msf->imsf_numsrc >= 0x3ffffffcU ||
914166b6b2dSNikolay Borisov 		    msf->imsf_numsrc > net->ipv4.sysctl_igmp_max_msf) {
9151da177e4SLinus Torvalds 			kfree(msf);
9161da177e4SLinus Torvalds 			err = -ENOBUFS;
9171da177e4SLinus Torvalds 			break;
9181da177e4SLinus Torvalds 		}
9191da177e4SLinus Torvalds 		if (IP_MSFILTER_SIZE(msf->imsf_numsrc) > optlen) {
9201da177e4SLinus Torvalds 			kfree(msf);
9211da177e4SLinus Torvalds 			err = -EINVAL;
9221da177e4SLinus Torvalds 			break;
9231da177e4SLinus Torvalds 		}
9241da177e4SLinus Torvalds 		err = ip_mc_msfilter(sk, msf, 0);
9251da177e4SLinus Torvalds 		kfree(msf);
9261da177e4SLinus Torvalds 		break;
9271da177e4SLinus Torvalds 	}
9281da177e4SLinus Torvalds 	case IP_BLOCK_SOURCE:
9291da177e4SLinus Torvalds 	case IP_UNBLOCK_SOURCE:
9301da177e4SLinus Torvalds 	case IP_ADD_SOURCE_MEMBERSHIP:
9311da177e4SLinus Torvalds 	case IP_DROP_SOURCE_MEMBERSHIP:
9321da177e4SLinus Torvalds 	{
9331da177e4SLinus Torvalds 		struct ip_mreq_source mreqs;
9341da177e4SLinus Torvalds 		int omode, add;
9351da177e4SLinus Torvalds 
9361da177e4SLinus Torvalds 		if (optlen != sizeof(struct ip_mreq_source))
9371da177e4SLinus Torvalds 			goto e_inval;
9381da177e4SLinus Torvalds 		if (copy_from_user(&mreqs, optval, sizeof(mreqs))) {
9391da177e4SLinus Torvalds 			err = -EFAULT;
9401da177e4SLinus Torvalds 			break;
9411da177e4SLinus Torvalds 		}
9421da177e4SLinus Torvalds 		if (optname == IP_BLOCK_SOURCE) {
9431da177e4SLinus Torvalds 			omode = MCAST_EXCLUDE;
9441da177e4SLinus Torvalds 			add = 1;
9451da177e4SLinus Torvalds 		} else if (optname == IP_UNBLOCK_SOURCE) {
9461da177e4SLinus Torvalds 			omode = MCAST_EXCLUDE;
9471da177e4SLinus Torvalds 			add = 0;
9481da177e4SLinus Torvalds 		} else if (optname == IP_ADD_SOURCE_MEMBERSHIP) {
9491da177e4SLinus Torvalds 			struct ip_mreqn mreq;
9501da177e4SLinus Torvalds 
9511da177e4SLinus Torvalds 			mreq.imr_multiaddr.s_addr = mreqs.imr_multiaddr;
9521da177e4SLinus Torvalds 			mreq.imr_address.s_addr = mreqs.imr_interface;
9531da177e4SLinus Torvalds 			mreq.imr_ifindex = 0;
95454ff9ef3SMarcelo Ricardo Leitner 			err = ip_mc_join_group(sk, &mreq);
9558cdaaa15SDavid L Stevens 			if (err && err != -EADDRINUSE)
9561da177e4SLinus Torvalds 				break;
9571da177e4SLinus Torvalds 			omode = MCAST_INCLUDE;
9581da177e4SLinus Torvalds 			add = 1;
9591da177e4SLinus Torvalds 		} else /* IP_DROP_SOURCE_MEMBERSHIP */ {
9601da177e4SLinus Torvalds 			omode = MCAST_INCLUDE;
9611da177e4SLinus Torvalds 			add = 0;
9621da177e4SLinus Torvalds 		}
9631da177e4SLinus Torvalds 		err = ip_mc_source(add, omode, sk, &mreqs, 0);
9641da177e4SLinus Torvalds 		break;
9651da177e4SLinus Torvalds 	}
9661da177e4SLinus Torvalds 	case MCAST_JOIN_GROUP:
9671da177e4SLinus Torvalds 	case MCAST_LEAVE_GROUP:
9681da177e4SLinus Torvalds 	{
9691da177e4SLinus Torvalds 		struct group_req greq;
9701da177e4SLinus Torvalds 		struct sockaddr_in *psin;
9711da177e4SLinus Torvalds 		struct ip_mreqn mreq;
9721da177e4SLinus Torvalds 
9731da177e4SLinus Torvalds 		if (optlen < sizeof(struct group_req))
9741da177e4SLinus Torvalds 			goto e_inval;
9751da177e4SLinus Torvalds 		err = -EFAULT;
9761da177e4SLinus Torvalds 		if (copy_from_user(&greq, optval, sizeof(greq)))
9771da177e4SLinus Torvalds 			break;
9781da177e4SLinus Torvalds 		psin = (struct sockaddr_in *)&greq.gr_group;
9791da177e4SLinus Torvalds 		if (psin->sin_family != AF_INET)
9801da177e4SLinus Torvalds 			goto e_inval;
9811da177e4SLinus Torvalds 		memset(&mreq, 0, sizeof(mreq));
9821da177e4SLinus Torvalds 		mreq.imr_multiaddr = psin->sin_addr;
9831da177e4SLinus Torvalds 		mreq.imr_ifindex = greq.gr_interface;
9841da177e4SLinus Torvalds 
9851da177e4SLinus Torvalds 		if (optname == MCAST_JOIN_GROUP)
98654ff9ef3SMarcelo Ricardo Leitner 			err = ip_mc_join_group(sk, &mreq);
9871da177e4SLinus Torvalds 		else
98854ff9ef3SMarcelo Ricardo Leitner 			err = ip_mc_leave_group(sk, &mreq);
9891da177e4SLinus Torvalds 		break;
9901da177e4SLinus Torvalds 	}
9911da177e4SLinus Torvalds 	case MCAST_JOIN_SOURCE_GROUP:
9921da177e4SLinus Torvalds 	case MCAST_LEAVE_SOURCE_GROUP:
9931da177e4SLinus Torvalds 	case MCAST_BLOCK_SOURCE:
9941da177e4SLinus Torvalds 	case MCAST_UNBLOCK_SOURCE:
9951da177e4SLinus Torvalds 	{
9961da177e4SLinus Torvalds 		struct group_source_req greqs;
9971da177e4SLinus Torvalds 		struct ip_mreq_source mreqs;
9981da177e4SLinus Torvalds 		struct sockaddr_in *psin;
9991da177e4SLinus Torvalds 		int omode, add;
10001da177e4SLinus Torvalds 
10011da177e4SLinus Torvalds 		if (optlen != sizeof(struct group_source_req))
10021da177e4SLinus Torvalds 			goto e_inval;
10031da177e4SLinus Torvalds 		if (copy_from_user(&greqs, optval, sizeof(greqs))) {
10041da177e4SLinus Torvalds 			err = -EFAULT;
10051da177e4SLinus Torvalds 			break;
10061da177e4SLinus Torvalds 		}
10071da177e4SLinus Torvalds 		if (greqs.gsr_group.ss_family != AF_INET ||
10081da177e4SLinus Torvalds 		    greqs.gsr_source.ss_family != AF_INET) {
10091da177e4SLinus Torvalds 			err = -EADDRNOTAVAIL;
10101da177e4SLinus Torvalds 			break;
10111da177e4SLinus Torvalds 		}
10121da177e4SLinus Torvalds 		psin = (struct sockaddr_in *)&greqs.gsr_group;
10131da177e4SLinus Torvalds 		mreqs.imr_multiaddr = psin->sin_addr.s_addr;
10141da177e4SLinus Torvalds 		psin = (struct sockaddr_in *)&greqs.gsr_source;
10151da177e4SLinus Torvalds 		mreqs.imr_sourceaddr = psin->sin_addr.s_addr;
10161da177e4SLinus Torvalds 		mreqs.imr_interface = 0; /* use index for mc_source */
10171da177e4SLinus Torvalds 
10181da177e4SLinus Torvalds 		if (optname == MCAST_BLOCK_SOURCE) {
10191da177e4SLinus Torvalds 			omode = MCAST_EXCLUDE;
10201da177e4SLinus Torvalds 			add = 1;
10211da177e4SLinus Torvalds 		} else if (optname == MCAST_UNBLOCK_SOURCE) {
10221da177e4SLinus Torvalds 			omode = MCAST_EXCLUDE;
10231da177e4SLinus Torvalds 			add = 0;
10241da177e4SLinus Torvalds 		} else if (optname == MCAST_JOIN_SOURCE_GROUP) {
10251da177e4SLinus Torvalds 			struct ip_mreqn mreq;
10261da177e4SLinus Torvalds 
10271da177e4SLinus Torvalds 			psin = (struct sockaddr_in *)&greqs.gsr_group;
10281da177e4SLinus Torvalds 			mreq.imr_multiaddr = psin->sin_addr;
10291da177e4SLinus Torvalds 			mreq.imr_address.s_addr = 0;
10301da177e4SLinus Torvalds 			mreq.imr_ifindex = greqs.gsr_interface;
103154ff9ef3SMarcelo Ricardo Leitner 			err = ip_mc_join_group(sk, &mreq);
10328cdaaa15SDavid L Stevens 			if (err && err != -EADDRINUSE)
10331da177e4SLinus Torvalds 				break;
10341da177e4SLinus Torvalds 			greqs.gsr_interface = mreq.imr_ifindex;
10351da177e4SLinus Torvalds 			omode = MCAST_INCLUDE;
10361da177e4SLinus Torvalds 			add = 1;
10371da177e4SLinus Torvalds 		} else /* MCAST_LEAVE_SOURCE_GROUP */ {
10381da177e4SLinus Torvalds 			omode = MCAST_INCLUDE;
10391da177e4SLinus Torvalds 			add = 0;
10401da177e4SLinus Torvalds 		}
10411da177e4SLinus Torvalds 		err = ip_mc_source(add, omode, sk, &mreqs,
10421da177e4SLinus Torvalds 				   greqs.gsr_interface);
10431da177e4SLinus Torvalds 		break;
10441da177e4SLinus Torvalds 	}
10451da177e4SLinus Torvalds 	case MCAST_MSFILTER:
10461da177e4SLinus Torvalds 	{
10471da177e4SLinus Torvalds 		struct sockaddr_in *psin;
10481da177e4SLinus Torvalds 		struct ip_msfilter *msf = NULL;
10491da177e4SLinus Torvalds 		struct group_filter *gsf = NULL;
10501da177e4SLinus Torvalds 		int msize, i, ifindex;
10511da177e4SLinus Torvalds 
10521da177e4SLinus Torvalds 		if (optlen < GROUP_FILTER_SIZE(0))
10531da177e4SLinus Torvalds 			goto e_inval;
10541da177e4SLinus Torvalds 		if (optlen > sysctl_optmem_max) {
10551da177e4SLinus Torvalds 			err = -ENOBUFS;
10561da177e4SLinus Torvalds 			break;
10571da177e4SLinus Torvalds 		}
10588b3a7005SKris Katterjohn 		gsf = kmalloc(optlen, GFP_KERNEL);
1059cfcabdccSStephen Hemminger 		if (!gsf) {
10601da177e4SLinus Torvalds 			err = -ENOBUFS;
10611da177e4SLinus Torvalds 			break;
10621da177e4SLinus Torvalds 		}
10631da177e4SLinus Torvalds 		err = -EFAULT;
10644d52cfbeSEric Dumazet 		if (copy_from_user(gsf, optval, optlen))
10651da177e4SLinus Torvalds 			goto mc_msf_out;
10664d52cfbeSEric Dumazet 
10671da177e4SLinus Torvalds 		/* numsrc >= (4G-140)/128 overflow in 32 bits */
10681da177e4SLinus Torvalds 		if (gsf->gf_numsrc >= 0x1ffffff ||
1069166b6b2dSNikolay Borisov 		    gsf->gf_numsrc > net->ipv4.sysctl_igmp_max_msf) {
10701da177e4SLinus Torvalds 			err = -ENOBUFS;
10711da177e4SLinus Torvalds 			goto mc_msf_out;
10721da177e4SLinus Torvalds 		}
10731da177e4SLinus Torvalds 		if (GROUP_FILTER_SIZE(gsf->gf_numsrc) > optlen) {
10741da177e4SLinus Torvalds 			err = -EINVAL;
10751da177e4SLinus Torvalds 			goto mc_msf_out;
10761da177e4SLinus Torvalds 		}
10771da177e4SLinus Torvalds 		msize = IP_MSFILTER_SIZE(gsf->gf_numsrc);
10788b3a7005SKris Katterjohn 		msf = kmalloc(msize, GFP_KERNEL);
1079cfcabdccSStephen Hemminger 		if (!msf) {
10801da177e4SLinus Torvalds 			err = -ENOBUFS;
10811da177e4SLinus Torvalds 			goto mc_msf_out;
10821da177e4SLinus Torvalds 		}
10831da177e4SLinus Torvalds 		ifindex = gsf->gf_interface;
10841da177e4SLinus Torvalds 		psin = (struct sockaddr_in *)&gsf->gf_group;
10851da177e4SLinus Torvalds 		if (psin->sin_family != AF_INET) {
10861da177e4SLinus Torvalds 			err = -EADDRNOTAVAIL;
10871da177e4SLinus Torvalds 			goto mc_msf_out;
10881da177e4SLinus Torvalds 		}
10891da177e4SLinus Torvalds 		msf->imsf_multiaddr = psin->sin_addr.s_addr;
10901da177e4SLinus Torvalds 		msf->imsf_interface = 0;
10911da177e4SLinus Torvalds 		msf->imsf_fmode = gsf->gf_fmode;
10921da177e4SLinus Torvalds 		msf->imsf_numsrc = gsf->gf_numsrc;
10931da177e4SLinus Torvalds 		err = -EADDRNOTAVAIL;
10941da177e4SLinus Torvalds 		for (i = 0; i < gsf->gf_numsrc; ++i) {
10951da177e4SLinus Torvalds 			psin = (struct sockaddr_in *)&gsf->gf_slist[i];
10961da177e4SLinus Torvalds 
10971da177e4SLinus Torvalds 			if (psin->sin_family != AF_INET)
10981da177e4SLinus Torvalds 				goto mc_msf_out;
10991da177e4SLinus Torvalds 			msf->imsf_slist[i] = psin->sin_addr.s_addr;
11001da177e4SLinus Torvalds 		}
11011da177e4SLinus Torvalds 		kfree(gsf);
11021da177e4SLinus Torvalds 		gsf = NULL;
11031da177e4SLinus Torvalds 
11041da177e4SLinus Torvalds 		err = ip_mc_msfilter(sk, msf, ifindex);
11051da177e4SLinus Torvalds mc_msf_out:
11061da177e4SLinus Torvalds 		kfree(msf);
11071da177e4SLinus Torvalds 		kfree(gsf);
11081da177e4SLinus Torvalds 		break;
11091da177e4SLinus Torvalds 	}
1110f771bef9SNivedita Singhvi 	case IP_MULTICAST_ALL:
1111f771bef9SNivedita Singhvi 		if (optlen < 1)
1112f771bef9SNivedita Singhvi 			goto e_inval;
1113f771bef9SNivedita Singhvi 		if (val != 0 && val != 1)
1114f771bef9SNivedita Singhvi 			goto e_inval;
1115f771bef9SNivedita Singhvi 		inet->mc_all = val;
1116f771bef9SNivedita Singhvi 		break;
11171da177e4SLinus Torvalds 	case IP_ROUTER_ALERT:
11181da177e4SLinus Torvalds 		err = ip_ra_control(sk, val ? 1 : 0, NULL);
11191da177e4SLinus Torvalds 		break;
11201da177e4SLinus Torvalds 
11211da177e4SLinus Torvalds 	case IP_FREEBIND:
11221da177e4SLinus Torvalds 		if (optlen < 1)
11231da177e4SLinus Torvalds 			goto e_inval;
11241da177e4SLinus Torvalds 		inet->freebind = !!val;
11251da177e4SLinus Torvalds 		break;
11261da177e4SLinus Torvalds 
11271da177e4SLinus Torvalds 	case IP_IPSEC_POLICY:
11281da177e4SLinus Torvalds 	case IP_XFRM_POLICY:
11296fc0b4a7SHerbert Xu 		err = -EPERM;
113052e804c6SEric W. Biederman 		if (!ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN))
11316fc0b4a7SHerbert Xu 			break;
11321da177e4SLinus Torvalds 		err = xfrm_user_policy(sk, optname, optval, optlen);
11331da177e4SLinus Torvalds 		break;
11341da177e4SLinus Torvalds 
1135f5715aeaSKOVACS Krisztian 	case IP_TRANSPARENT:
113652e804c6SEric W. Biederman 		if (!!val && !ns_capable(sock_net(sk)->user_ns, CAP_NET_RAW) &&
113752e804c6SEric W. Biederman 		    !ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN)) {
1138f5715aeaSKOVACS Krisztian 			err = -EPERM;
1139f5715aeaSKOVACS Krisztian 			break;
1140f5715aeaSKOVACS Krisztian 		}
1141f5715aeaSKOVACS Krisztian 		if (optlen < 1)
1142f5715aeaSKOVACS Krisztian 			goto e_inval;
1143f5715aeaSKOVACS Krisztian 		inet->transparent = !!val;
1144f5715aeaSKOVACS Krisztian 		break;
1145f5715aeaSKOVACS Krisztian 
1146d218d111SStephen Hemminger 	case IP_MINTTL:
1147d218d111SStephen Hemminger 		if (optlen < 1)
1148d218d111SStephen Hemminger 			goto e_inval;
1149d218d111SStephen Hemminger 		if (val < 0 || val > 255)
1150d218d111SStephen Hemminger 			goto e_inval;
1151d218d111SStephen Hemminger 		inet->min_ttl = val;
1152d218d111SStephen Hemminger 		break;
1153d218d111SStephen Hemminger 
11541da177e4SLinus Torvalds 	default:
11551da177e4SLinus Torvalds 		err = -ENOPROTOOPT;
11561da177e4SLinus Torvalds 		break;
11571da177e4SLinus Torvalds 	}
11581da177e4SLinus Torvalds 	release_sock(sk);
1159baf606d9SMarcelo Ricardo Leitner 	if (needs_rtnl)
1160baf606d9SMarcelo Ricardo Leitner 		rtnl_unlock();
11611da177e4SLinus Torvalds 	return err;
11621da177e4SLinus Torvalds 
11631da177e4SLinus Torvalds e_inval:
11641da177e4SLinus Torvalds 	release_sock(sk);
1165baf606d9SMarcelo Ricardo Leitner 	if (needs_rtnl)
1166baf606d9SMarcelo Ricardo Leitner 		rtnl_unlock();
11671da177e4SLinus Torvalds 	return -EINVAL;
11681da177e4SLinus Torvalds }
11691da177e4SLinus Torvalds 
1170f84af32cSEric Dumazet /**
1171829ae9d6SWillem de Bruijn  * ipv4_pktinfo_prepare - transfer some info from rtable to skb
1172f84af32cSEric Dumazet  * @sk: socket
1173f84af32cSEric Dumazet  * @skb: buffer
1174f84af32cSEric Dumazet  *
117535ebf65eSDavid S. Miller  * To support IP_CMSG_PKTINFO option, we store rt_iif and specific
117635ebf65eSDavid S. Miller  * destination in skb->cb[] before dst drop.
11778e3bff96Sstephen hemminger  * This way, receiver doesn't make cache line misses to read rtable.
1178f84af32cSEric Dumazet  */
1179fbf8866dSShawn Bohrer void ipv4_pktinfo_prepare(const struct sock *sk, struct sk_buff *skb)
1180f84af32cSEric Dumazet {
1181d826eb14SEric Dumazet 	struct in_pktinfo *pktinfo = PKTINFO_SKB_CB(skb);
11824b261c75SHannes Frederic Sowa 	bool prepare = (inet_sk(sk)->cmsg_flags & IP_CMSG_PKTINFO) ||
11834b261c75SHannes Frederic Sowa 		       ipv6_sk_rxinfo(sk);
1184d826eb14SEric Dumazet 
11854b261c75SHannes Frederic Sowa 	if (prepare && skb_rtable(skb)) {
118692101b3bSDavid S. Miller 		pktinfo->ipi_ifindex = inet_iif(skb);
118735ebf65eSDavid S. Miller 		pktinfo->ipi_spec_dst.s_addr = fib_compute_spec_dst(skb);
1188d826eb14SEric Dumazet 	} else {
1189d826eb14SEric Dumazet 		pktinfo->ipi_ifindex = 0;
1190d826eb14SEric Dumazet 		pktinfo->ipi_spec_dst.s_addr = 0;
1191f84af32cSEric Dumazet 	}
1192d826eb14SEric Dumazet 	skb_dst_drop(skb);
1193d826eb14SEric Dumazet }
1194f84af32cSEric Dumazet 
11953fdadf7dSDmitry Mishin int ip_setsockopt(struct sock *sk, int level,
1196b7058842SDavid S. Miller 		int optname, char __user *optval, unsigned int optlen)
11973fdadf7dSDmitry Mishin {
11983fdadf7dSDmitry Mishin 	int err;
11993fdadf7dSDmitry Mishin 
12003fdadf7dSDmitry Mishin 	if (level != SOL_IP)
12013fdadf7dSDmitry Mishin 		return -ENOPROTOOPT;
12023fdadf7dSDmitry Mishin 
12033fdadf7dSDmitry Mishin 	err = do_ip_setsockopt(sk, level, optname, optval, optlen);
12043fdadf7dSDmitry Mishin #ifdef CONFIG_NETFILTER
12053fdadf7dSDmitry Mishin 	/* we need to exclude all possible ENOPROTOOPTs except default case */
12063fdadf7dSDmitry Mishin 	if (err == -ENOPROTOOPT && optname != IP_HDRINCL &&
12076a9fb947SPavel Emelyanov 			optname != IP_IPSEC_POLICY &&
12086a9fb947SPavel Emelyanov 			optname != IP_XFRM_POLICY &&
12096a9fb947SPavel Emelyanov 			!ip_mroute_opt(optname)) {
12103fdadf7dSDmitry Mishin 		lock_sock(sk);
12113fdadf7dSDmitry Mishin 		err = nf_setsockopt(sk, PF_INET, optname, optval, optlen);
12123fdadf7dSDmitry Mishin 		release_sock(sk);
12133fdadf7dSDmitry Mishin 	}
12143fdadf7dSDmitry Mishin #endif
12153fdadf7dSDmitry Mishin 	return err;
12163fdadf7dSDmitry Mishin }
12174d52cfbeSEric Dumazet EXPORT_SYMBOL(ip_setsockopt);
12183fdadf7dSDmitry Mishin 
12193fdadf7dSDmitry Mishin #ifdef CONFIG_COMPAT
1220543d9cfeSArnaldo Carvalho de Melo int compat_ip_setsockopt(struct sock *sk, int level, int optname,
1221b7058842SDavid S. Miller 			 char __user *optval, unsigned int optlen)
12223fdadf7dSDmitry Mishin {
12233fdadf7dSDmitry Mishin 	int err;
12243fdadf7dSDmitry Mishin 
12253fdadf7dSDmitry Mishin 	if (level != SOL_IP)
12263fdadf7dSDmitry Mishin 		return -ENOPROTOOPT;
12273fdadf7dSDmitry Mishin 
1228dae50295SDavid L Stevens 	if (optname >= MCAST_JOIN_GROUP && optname <= MCAST_MSFILTER)
1229dae50295SDavid L Stevens 		return compat_mc_setsockopt(sk, level, optname, optval, optlen,
1230dae50295SDavid L Stevens 			ip_setsockopt);
1231dae50295SDavid L Stevens 
12323fdadf7dSDmitry Mishin 	err = do_ip_setsockopt(sk, level, optname, optval, optlen);
12333fdadf7dSDmitry Mishin #ifdef CONFIG_NETFILTER
12343fdadf7dSDmitry Mishin 	/* we need to exclude all possible ENOPROTOOPTs except default case */
12353fdadf7dSDmitry Mishin 	if (err == -ENOPROTOOPT && optname != IP_HDRINCL &&
12366a9fb947SPavel Emelyanov 			optname != IP_IPSEC_POLICY &&
12376a9fb947SPavel Emelyanov 			optname != IP_XFRM_POLICY &&
12386a9fb947SPavel Emelyanov 			!ip_mroute_opt(optname)) {
12393fdadf7dSDmitry Mishin 		lock_sock(sk);
1240543d9cfeSArnaldo Carvalho de Melo 		err = compat_nf_setsockopt(sk, PF_INET, optname,
1241543d9cfeSArnaldo Carvalho de Melo 					   optval, optlen);
12423fdadf7dSDmitry Mishin 		release_sock(sk);
12433fdadf7dSDmitry Mishin 	}
12443fdadf7dSDmitry Mishin #endif
12453fdadf7dSDmitry Mishin 	return err;
12463fdadf7dSDmitry Mishin }
1247543d9cfeSArnaldo Carvalho de Melo EXPORT_SYMBOL(compat_ip_setsockopt);
12483fdadf7dSDmitry Mishin #endif
12493fdadf7dSDmitry Mishin 
12501da177e4SLinus Torvalds /*
12514d52cfbeSEric Dumazet  *	Get the options. Note for future reference. The GET of IP options gets
12524d52cfbeSEric Dumazet  *	the _received_ ones. The set sets the _sent_ ones.
12531da177e4SLinus Torvalds  */
12541da177e4SLinus Torvalds 
125587e9f031SWANG Cong static bool getsockopt_needs_rtnl(int optname)
125687e9f031SWANG Cong {
125787e9f031SWANG Cong 	switch (optname) {
125887e9f031SWANG Cong 	case IP_MSFILTER:
125987e9f031SWANG Cong 	case MCAST_MSFILTER:
126087e9f031SWANG Cong 		return true;
126187e9f031SWANG Cong 	}
126287e9f031SWANG Cong 	return false;
126387e9f031SWANG Cong }
126487e9f031SWANG Cong 
12653fdadf7dSDmitry Mishin static int do_ip_getsockopt(struct sock *sk, int level, int optname,
126695c96174SEric Dumazet 			    char __user *optval, int __user *optlen, unsigned int flags)
12671da177e4SLinus Torvalds {
12681da177e4SLinus Torvalds 	struct inet_sock *inet = inet_sk(sk);
126987e9f031SWANG Cong 	bool needs_rtnl = getsockopt_needs_rtnl(optname);
127087e9f031SWANG Cong 	int val, err = 0;
12711da177e4SLinus Torvalds 	int len;
12721da177e4SLinus Torvalds 
12731da177e4SLinus Torvalds 	if (level != SOL_IP)
12741da177e4SLinus Torvalds 		return -EOPNOTSUPP;
12751da177e4SLinus Torvalds 
12766a9fb947SPavel Emelyanov 	if (ip_mroute_opt(optname))
12771da177e4SLinus Torvalds 		return ip_mroute_getsockopt(sk, optname, optval, optlen);
12781da177e4SLinus Torvalds 
12791da177e4SLinus Torvalds 	if (get_user(len, optlen))
12801da177e4SLinus Torvalds 		return -EFAULT;
12811da177e4SLinus Torvalds 	if (len < 0)
12821da177e4SLinus Torvalds 		return -EINVAL;
12831da177e4SLinus Torvalds 
128487e9f031SWANG Cong 	if (needs_rtnl)
128587e9f031SWANG Cong 		rtnl_lock();
12861da177e4SLinus Torvalds 	lock_sock(sk);
12871da177e4SLinus Torvalds 
12881da177e4SLinus Torvalds 	switch (optname) {
12891da177e4SLinus Torvalds 	case IP_OPTIONS:
12901da177e4SLinus Torvalds 	{
12911da177e4SLinus Torvalds 		unsigned char optbuf[sizeof(struct ip_options)+40];
12921da177e4SLinus Torvalds 		struct ip_options *opt = (struct ip_options *)optbuf;
1293f6d8bd05SEric Dumazet 		struct ip_options_rcu *inet_opt;
1294f6d8bd05SEric Dumazet 
1295f6d8bd05SEric Dumazet 		inet_opt = rcu_dereference_protected(inet->inet_opt,
1296f6d8bd05SEric Dumazet 						     sock_owned_by_user(sk));
12971da177e4SLinus Torvalds 		opt->optlen = 0;
1298f6d8bd05SEric Dumazet 		if (inet_opt)
1299f6d8bd05SEric Dumazet 			memcpy(optbuf, &inet_opt->opt,
13001da177e4SLinus Torvalds 			       sizeof(struct ip_options) +
1301f6d8bd05SEric Dumazet 			       inet_opt->opt.optlen);
13021da177e4SLinus Torvalds 		release_sock(sk);
13031da177e4SLinus Torvalds 
13041da177e4SLinus Torvalds 		if (opt->optlen == 0)
13051da177e4SLinus Torvalds 			return put_user(0, optlen);
13061da177e4SLinus Torvalds 
13071da177e4SLinus Torvalds 		ip_options_undo(opt);
13081da177e4SLinus Torvalds 
13091da177e4SLinus Torvalds 		len = min_t(unsigned int, len, opt->optlen);
13101da177e4SLinus Torvalds 		if (put_user(len, optlen))
13111da177e4SLinus Torvalds 			return -EFAULT;
13121da177e4SLinus Torvalds 		if (copy_to_user(optval, opt->__data, len))
13131da177e4SLinus Torvalds 			return -EFAULT;
13141da177e4SLinus Torvalds 		return 0;
13151da177e4SLinus Torvalds 	}
13161da177e4SLinus Torvalds 	case IP_PKTINFO:
13171da177e4SLinus Torvalds 		val = (inet->cmsg_flags & IP_CMSG_PKTINFO) != 0;
13181da177e4SLinus Torvalds 		break;
13191da177e4SLinus Torvalds 	case IP_RECVTTL:
13201da177e4SLinus Torvalds 		val = (inet->cmsg_flags & IP_CMSG_TTL) != 0;
13211da177e4SLinus Torvalds 		break;
13221da177e4SLinus Torvalds 	case IP_RECVTOS:
13231da177e4SLinus Torvalds 		val = (inet->cmsg_flags & IP_CMSG_TOS) != 0;
13241da177e4SLinus Torvalds 		break;
13251da177e4SLinus Torvalds 	case IP_RECVOPTS:
13261da177e4SLinus Torvalds 		val = (inet->cmsg_flags & IP_CMSG_RECVOPTS) != 0;
13271da177e4SLinus Torvalds 		break;
13281da177e4SLinus Torvalds 	case IP_RETOPTS:
13291da177e4SLinus Torvalds 		val = (inet->cmsg_flags & IP_CMSG_RETOPTS) != 0;
13301da177e4SLinus Torvalds 		break;
13312c7946a7SCatherine Zhang 	case IP_PASSSEC:
13322c7946a7SCatherine Zhang 		val = (inet->cmsg_flags & IP_CMSG_PASSSEC) != 0;
13332c7946a7SCatherine Zhang 		break;
1334e8b2dfe9SBalazs Scheidler 	case IP_RECVORIGDSTADDR:
1335e8b2dfe9SBalazs Scheidler 		val = (inet->cmsg_flags & IP_CMSG_ORIGDSTADDR) != 0;
1336e8b2dfe9SBalazs Scheidler 		break;
1337ad6f939aSTom Herbert 	case IP_CHECKSUM:
1338ad6f939aSTom Herbert 		val = (inet->cmsg_flags & IP_CMSG_CHECKSUM) != 0;
1339ad6f939aSTom Herbert 		break;
13401da177e4SLinus Torvalds 	case IP_TOS:
13411da177e4SLinus Torvalds 		val = inet->tos;
13421da177e4SLinus Torvalds 		break;
13431da177e4SLinus Torvalds 	case IP_TTL:
1344*fa50d974SNikolay Borisov 	{
1345*fa50d974SNikolay Borisov 		struct net *net = sock_net(sk);
13461da177e4SLinus Torvalds 		val = (inet->uc_ttl == -1 ?
1347*fa50d974SNikolay Borisov 		       net->ipv4.sysctl_ip_default_ttl :
13481da177e4SLinus Torvalds 		       inet->uc_ttl);
13491da177e4SLinus Torvalds 		break;
1350*fa50d974SNikolay Borisov 	}
13511da177e4SLinus Torvalds 	case IP_HDRINCL:
13521da177e4SLinus Torvalds 		val = inet->hdrincl;
13531da177e4SLinus Torvalds 		break;
1354a89b4763SMichael Kerrisk 	case IP_NODEFRAG:
1355a89b4763SMichael Kerrisk 		val = inet->nodefrag;
1356a89b4763SMichael Kerrisk 		break;
135790c337daSEric Dumazet 	case IP_BIND_ADDRESS_NO_PORT:
135890c337daSEric Dumazet 		val = inet->bind_address_no_port;
135990c337daSEric Dumazet 		break;
13601da177e4SLinus Torvalds 	case IP_MTU_DISCOVER:
13611da177e4SLinus Torvalds 		val = inet->pmtudisc;
13621da177e4SLinus Torvalds 		break;
13631da177e4SLinus Torvalds 	case IP_MTU:
13641da177e4SLinus Torvalds 	{
13651da177e4SLinus Torvalds 		struct dst_entry *dst;
13661da177e4SLinus Torvalds 		val = 0;
13671da177e4SLinus Torvalds 		dst = sk_dst_get(sk);
13681da177e4SLinus Torvalds 		if (dst) {
13691da177e4SLinus Torvalds 			val = dst_mtu(dst);
13701da177e4SLinus Torvalds 			dst_release(dst);
13711da177e4SLinus Torvalds 		}
13721da177e4SLinus Torvalds 		if (!val) {
13731da177e4SLinus Torvalds 			release_sock(sk);
13741da177e4SLinus Torvalds 			return -ENOTCONN;
13751da177e4SLinus Torvalds 		}
13761da177e4SLinus Torvalds 		break;
13771da177e4SLinus Torvalds 	}
13781da177e4SLinus Torvalds 	case IP_RECVERR:
13791da177e4SLinus Torvalds 		val = inet->recverr;
13801da177e4SLinus Torvalds 		break;
13811da177e4SLinus Torvalds 	case IP_MULTICAST_TTL:
13821da177e4SLinus Torvalds 		val = inet->mc_ttl;
13831da177e4SLinus Torvalds 		break;
13841da177e4SLinus Torvalds 	case IP_MULTICAST_LOOP:
13851da177e4SLinus Torvalds 		val = inet->mc_loop;
13861da177e4SLinus Torvalds 		break;
138776e21053SErich E. Hoover 	case IP_UNICAST_IF:
138876e21053SErich E. Hoover 		val = (__force int)htonl((__u32) inet->uc_index);
138976e21053SErich E. Hoover 		break;
13901da177e4SLinus Torvalds 	case IP_MULTICAST_IF:
13911da177e4SLinus Torvalds 	{
13921da177e4SLinus Torvalds 		struct in_addr addr;
13931da177e4SLinus Torvalds 		len = min_t(unsigned int, len, sizeof(struct in_addr));
13941da177e4SLinus Torvalds 		addr.s_addr = inet->mc_addr;
13951da177e4SLinus Torvalds 		release_sock(sk);
13961da177e4SLinus Torvalds 
13971da177e4SLinus Torvalds 		if (put_user(len, optlen))
13981da177e4SLinus Torvalds 			return -EFAULT;
13991da177e4SLinus Torvalds 		if (copy_to_user(optval, &addr, len))
14001da177e4SLinus Torvalds 			return -EFAULT;
14011da177e4SLinus Torvalds 		return 0;
14021da177e4SLinus Torvalds 	}
14031da177e4SLinus Torvalds 	case IP_MSFILTER:
14041da177e4SLinus Torvalds 	{
14051da177e4SLinus Torvalds 		struct ip_msfilter msf;
14061da177e4SLinus Torvalds 
14071da177e4SLinus Torvalds 		if (len < IP_MSFILTER_SIZE(0)) {
140887e9f031SWANG Cong 			err = -EINVAL;
140987e9f031SWANG Cong 			goto out;
14101da177e4SLinus Torvalds 		}
14111da177e4SLinus Torvalds 		if (copy_from_user(&msf, optval, IP_MSFILTER_SIZE(0))) {
141287e9f031SWANG Cong 			err = -EFAULT;
141387e9f031SWANG Cong 			goto out;
14141da177e4SLinus Torvalds 		}
14151da177e4SLinus Torvalds 		err = ip_mc_msfget(sk, &msf,
14161da177e4SLinus Torvalds 				   (struct ip_msfilter __user *)optval, optlen);
141787e9f031SWANG Cong 		goto out;
14181da177e4SLinus Torvalds 	}
14191da177e4SLinus Torvalds 	case MCAST_MSFILTER:
14201da177e4SLinus Torvalds 	{
14211da177e4SLinus Torvalds 		struct group_filter gsf;
14221da177e4SLinus Torvalds 
14231da177e4SLinus Torvalds 		if (len < GROUP_FILTER_SIZE(0)) {
142487e9f031SWANG Cong 			err = -EINVAL;
142587e9f031SWANG Cong 			goto out;
14261da177e4SLinus Torvalds 		}
14271da177e4SLinus Torvalds 		if (copy_from_user(&gsf, optval, GROUP_FILTER_SIZE(0))) {
142887e9f031SWANG Cong 			err = -EFAULT;
142987e9f031SWANG Cong 			goto out;
14301da177e4SLinus Torvalds 		}
14311da177e4SLinus Torvalds 		err = ip_mc_gsfget(sk, &gsf,
14324d52cfbeSEric Dumazet 				   (struct group_filter __user *)optval,
14334d52cfbeSEric Dumazet 				   optlen);
143487e9f031SWANG Cong 		goto out;
14351da177e4SLinus Torvalds 	}
1436f771bef9SNivedita Singhvi 	case IP_MULTICAST_ALL:
1437f771bef9SNivedita Singhvi 		val = inet->mc_all;
1438f771bef9SNivedita Singhvi 		break;
14391da177e4SLinus Torvalds 	case IP_PKTOPTIONS:
14401da177e4SLinus Torvalds 	{
14411da177e4SLinus Torvalds 		struct msghdr msg;
14421da177e4SLinus Torvalds 
14431da177e4SLinus Torvalds 		release_sock(sk);
14441da177e4SLinus Torvalds 
14451da177e4SLinus Torvalds 		if (sk->sk_type != SOCK_STREAM)
14461da177e4SLinus Torvalds 			return -ENOPROTOOPT;
14471da177e4SLinus Torvalds 
1448c54a5e02SKaroly Kemeny 		msg.msg_control = (__force void *) optval;
14491da177e4SLinus Torvalds 		msg.msg_controllen = len;
1450dd23198eSDaniel Baluta 		msg.msg_flags = flags;
14511da177e4SLinus Torvalds 
14521da177e4SLinus Torvalds 		if (inet->cmsg_flags & IP_CMSG_PKTINFO) {
14531da177e4SLinus Torvalds 			struct in_pktinfo info;
14541da177e4SLinus Torvalds 
1455c720c7e8SEric Dumazet 			info.ipi_addr.s_addr = inet->inet_rcv_saddr;
1456c720c7e8SEric Dumazet 			info.ipi_spec_dst.s_addr = inet->inet_rcv_saddr;
14571da177e4SLinus Torvalds 			info.ipi_ifindex = inet->mc_index;
14581da177e4SLinus Torvalds 			put_cmsg(&msg, SOL_IP, IP_PKTINFO, sizeof(info), &info);
14591da177e4SLinus Torvalds 		}
14601da177e4SLinus Torvalds 		if (inet->cmsg_flags & IP_CMSG_TTL) {
14611da177e4SLinus Torvalds 			int hlim = inet->mc_ttl;
14621da177e4SLinus Torvalds 			put_cmsg(&msg, SOL_IP, IP_TTL, sizeof(hlim), &hlim);
14631da177e4SLinus Torvalds 		}
14644c507d28SJiri Benc 		if (inet->cmsg_flags & IP_CMSG_TOS) {
14654c507d28SJiri Benc 			int tos = inet->rcv_tos;
14664c507d28SJiri Benc 			put_cmsg(&msg, SOL_IP, IP_TOS, sizeof(tos), &tos);
14674c507d28SJiri Benc 		}
14681da177e4SLinus Torvalds 		len -= msg.msg_controllen;
14691da177e4SLinus Torvalds 		return put_user(len, optlen);
14701da177e4SLinus Torvalds 	}
14711da177e4SLinus Torvalds 	case IP_FREEBIND:
14721da177e4SLinus Torvalds 		val = inet->freebind;
14731da177e4SLinus Torvalds 		break;
1474f5715aeaSKOVACS Krisztian 	case IP_TRANSPARENT:
1475f5715aeaSKOVACS Krisztian 		val = inet->transparent;
1476f5715aeaSKOVACS Krisztian 		break;
1477d218d111SStephen Hemminger 	case IP_MINTTL:
1478d218d111SStephen Hemminger 		val = inet->min_ttl;
1479d218d111SStephen Hemminger 		break;
14801da177e4SLinus Torvalds 	default:
14811da177e4SLinus Torvalds 		release_sock(sk);
14821da177e4SLinus Torvalds 		return -ENOPROTOOPT;
14831da177e4SLinus Torvalds 	}
14841da177e4SLinus Torvalds 	release_sock(sk);
14851da177e4SLinus Torvalds 
1486951e07c9SDavid S. Miller 	if (len < sizeof(int) && len > 0 && val >= 0 && val <= 255) {
14871da177e4SLinus Torvalds 		unsigned char ucval = (unsigned char)val;
14881da177e4SLinus Torvalds 		len = 1;
14891da177e4SLinus Torvalds 		if (put_user(len, optlen))
14901da177e4SLinus Torvalds 			return -EFAULT;
14911da177e4SLinus Torvalds 		if (copy_to_user(optval, &ucval, 1))
14921da177e4SLinus Torvalds 			return -EFAULT;
14931da177e4SLinus Torvalds 	} else {
14941da177e4SLinus Torvalds 		len = min_t(unsigned int, sizeof(int), len);
14951da177e4SLinus Torvalds 		if (put_user(len, optlen))
14961da177e4SLinus Torvalds 			return -EFAULT;
14971da177e4SLinus Torvalds 		if (copy_to_user(optval, &val, len))
14981da177e4SLinus Torvalds 			return -EFAULT;
14991da177e4SLinus Torvalds 	}
15001da177e4SLinus Torvalds 	return 0;
150187e9f031SWANG Cong 
150287e9f031SWANG Cong out:
150387e9f031SWANG Cong 	release_sock(sk);
150487e9f031SWANG Cong 	if (needs_rtnl)
150587e9f031SWANG Cong 		rtnl_unlock();
150687e9f031SWANG Cong 	return err;
15071da177e4SLinus Torvalds }
15081da177e4SLinus Torvalds 
15093fdadf7dSDmitry Mishin int ip_getsockopt(struct sock *sk, int level,
15103fdadf7dSDmitry Mishin 		  int optname, char __user *optval, int __user *optlen)
15113fdadf7dSDmitry Mishin {
15123fdadf7dSDmitry Mishin 	int err;
15133fdadf7dSDmitry Mishin 
1514dd23198eSDaniel Baluta 	err = do_ip_getsockopt(sk, level, optname, optval, optlen, 0);
15153fdadf7dSDmitry Mishin #ifdef CONFIG_NETFILTER
15163fdadf7dSDmitry Mishin 	/* we need to exclude all possible ENOPROTOOPTs except default case */
15176a9fb947SPavel Emelyanov 	if (err == -ENOPROTOOPT && optname != IP_PKTOPTIONS &&
15186a9fb947SPavel Emelyanov 			!ip_mroute_opt(optname)) {
15193fdadf7dSDmitry Mishin 		int len;
15203fdadf7dSDmitry Mishin 
15213fdadf7dSDmitry Mishin 		if (get_user(len, optlen))
15223fdadf7dSDmitry Mishin 			return -EFAULT;
15233fdadf7dSDmitry Mishin 
15243fdadf7dSDmitry Mishin 		lock_sock(sk);
15253fdadf7dSDmitry Mishin 		err = nf_getsockopt(sk, PF_INET, optname, optval,
15263fdadf7dSDmitry Mishin 				&len);
15273fdadf7dSDmitry Mishin 		release_sock(sk);
15283fdadf7dSDmitry Mishin 		if (err >= 0)
15293fdadf7dSDmitry Mishin 			err = put_user(len, optlen);
15303fdadf7dSDmitry Mishin 		return err;
15313fdadf7dSDmitry Mishin 	}
15323fdadf7dSDmitry Mishin #endif
15333fdadf7dSDmitry Mishin 	return err;
15343fdadf7dSDmitry Mishin }
15354d52cfbeSEric Dumazet EXPORT_SYMBOL(ip_getsockopt);
15363fdadf7dSDmitry Mishin 
15373fdadf7dSDmitry Mishin #ifdef CONFIG_COMPAT
1538543d9cfeSArnaldo Carvalho de Melo int compat_ip_getsockopt(struct sock *sk, int level, int optname,
1539543d9cfeSArnaldo Carvalho de Melo 			 char __user *optval, int __user *optlen)
15403fdadf7dSDmitry Mishin {
154142908c69SDavid L Stevens 	int err;
154242908c69SDavid L Stevens 
154342908c69SDavid L Stevens 	if (optname == MCAST_MSFILTER)
154442908c69SDavid L Stevens 		return compat_mc_getsockopt(sk, level, optname, optval, optlen,
154542908c69SDavid L Stevens 			ip_getsockopt);
154642908c69SDavid L Stevens 
1547dd23198eSDaniel Baluta 	err = do_ip_getsockopt(sk, level, optname, optval, optlen,
1548dd23198eSDaniel Baluta 		MSG_CMSG_COMPAT);
154942908c69SDavid L Stevens 
15503fdadf7dSDmitry Mishin #ifdef CONFIG_NETFILTER
15513fdadf7dSDmitry Mishin 	/* we need to exclude all possible ENOPROTOOPTs except default case */
15526a9fb947SPavel Emelyanov 	if (err == -ENOPROTOOPT && optname != IP_PKTOPTIONS &&
15536a9fb947SPavel Emelyanov 			!ip_mroute_opt(optname)) {
15543fdadf7dSDmitry Mishin 		int len;
15553fdadf7dSDmitry Mishin 
15563fdadf7dSDmitry Mishin 		if (get_user(len, optlen))
15573fdadf7dSDmitry Mishin 			return -EFAULT;
15583fdadf7dSDmitry Mishin 
15593fdadf7dSDmitry Mishin 		lock_sock(sk);
1560543d9cfeSArnaldo Carvalho de Melo 		err = compat_nf_getsockopt(sk, PF_INET, optname, optval, &len);
15613fdadf7dSDmitry Mishin 		release_sock(sk);
15623fdadf7dSDmitry Mishin 		if (err >= 0)
15633fdadf7dSDmitry Mishin 			err = put_user(len, optlen);
15643fdadf7dSDmitry Mishin 		return err;
15653fdadf7dSDmitry Mishin 	}
15663fdadf7dSDmitry Mishin #endif
15673fdadf7dSDmitry Mishin 	return err;
15683fdadf7dSDmitry Mishin }
1569543d9cfeSArnaldo Carvalho de Melo EXPORT_SYMBOL(compat_ip_getsockopt);
15703fdadf7dSDmitry Mishin #endif
1571