xref: /linux/net/ipv4/ip_sockglue.c (revision c247f0534cc5a5a547a343903f42295a471844e2)
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 	}
3541da177e4SLinus Torvalds 	if (new_ra == NULL) {
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 
390bd82393cSArnaldo Carvalho de Melo 	if (skb_pull(skb, payload - skb->data) != NULL) {
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 
435*c247f053SWillem de Bruijn /* IPv4 supports cmsg on all imcp errors and some timestamps
436*c247f053SWillem de Bruijn  *
437*c247f053SWillem de Bruijn  * Timestamp code paths do not initialize the fields expected by cmsg:
438*c247f053SWillem de Bruijn  * the PKTINFO fields in skb->cb[]. Fill those in here.
439*c247f053SWillem de Bruijn  */
440*c247f053SWillem de Bruijn static bool ipv4_datagram_support_cmsg(const struct sock *sk,
441*c247f053SWillem de Bruijn 				       struct sk_buff *skb,
442829ae9d6SWillem de Bruijn 				       int ee_origin)
443829ae9d6SWillem de Bruijn {
444*c247f053SWillem de Bruijn 	struct in_pktinfo *info;
445829ae9d6SWillem de Bruijn 
446*c247f053SWillem de Bruijn 	if (ee_origin == SO_EE_ORIGIN_ICMP)
447*c247f053SWillem de Bruijn 		return true;
448*c247f053SWillem de Bruijn 
449*c247f053SWillem de Bruijn 	if (ee_origin == SO_EE_ORIGIN_LOCAL)
450*c247f053SWillem de Bruijn 		return false;
451*c247f053SWillem de Bruijn 
452*c247f053SWillem de Bruijn 	/* Support IP_PKTINFO on tstamp packets if requested, to correlate
453*c247f053SWillem de Bruijn 	 * timestamp with egress dev. Not possible for packets without dev
454*c247f053SWillem de Bruijn 	 * or without payload (SOF_TIMESTAMPING_OPT_TSONLY).
455*c247f053SWillem de Bruijn 	 */
456*c247f053SWillem de Bruijn 	if ((!(sk->sk_tsflags & SOF_TIMESTAMPING_OPT_CMSG)) ||
457829ae9d6SWillem de Bruijn 	    (!skb->dev))
458829ae9d6SWillem de Bruijn 		return false;
459829ae9d6SWillem de Bruijn 
460*c247f053SWillem de Bruijn 	info = PKTINFO_SKB_CB(skb);
461829ae9d6SWillem de Bruijn 	info->ipi_spec_dst.s_addr = ip_hdr(skb)->saddr;
462829ae9d6SWillem de Bruijn 	info->ipi_ifindex = skb->dev->ifindex;
463829ae9d6SWillem de Bruijn 	return true;
464829ae9d6SWillem de Bruijn }
465829ae9d6SWillem de Bruijn 
4661da177e4SLinus Torvalds /*
4671da177e4SLinus Torvalds  *	Handle MSG_ERRQUEUE
4681da177e4SLinus Torvalds  */
46985fbaa75SHannes Frederic Sowa int ip_recv_error(struct sock *sk, struct msghdr *msg, int len, int *addr_len)
4701da177e4SLinus Torvalds {
4711da177e4SLinus Torvalds 	struct sock_exterr_skb *serr;
472364a9e93SWillem de Bruijn 	struct sk_buff *skb;
473342dfc30SSteffen Hurrle 	DECLARE_SOCKADDR(struct sockaddr_in *, sin, msg->msg_name);
4741da177e4SLinus Torvalds 	struct {
4751da177e4SLinus Torvalds 		struct sock_extended_err ee;
4761da177e4SLinus Torvalds 		struct sockaddr_in	 offender;
4771da177e4SLinus Torvalds 	} errhdr;
4781da177e4SLinus Torvalds 	int err;
4791da177e4SLinus Torvalds 	int copied;
4801da177e4SLinus Torvalds 
4817ce875e5SWillem de Bruijn 	WARN_ON_ONCE(sk->sk_family == AF_INET6);
4827ce875e5SWillem de Bruijn 
4831da177e4SLinus Torvalds 	err = -EAGAIN;
484364a9e93SWillem de Bruijn 	skb = sock_dequeue_err_skb(sk);
4851da177e4SLinus Torvalds 	if (skb == NULL)
4861da177e4SLinus Torvalds 		goto out;
4871da177e4SLinus Torvalds 
4881da177e4SLinus Torvalds 	copied = skb->len;
4891da177e4SLinus Torvalds 	if (copied > len) {
4901da177e4SLinus Torvalds 		msg->msg_flags |= MSG_TRUNC;
4911da177e4SLinus Torvalds 		copied = len;
4921da177e4SLinus Torvalds 	}
49351f3d02bSDavid S. Miller 	err = skb_copy_datagram_msg(skb, 0, msg, copied);
4941da177e4SLinus Torvalds 	if (err)
4951da177e4SLinus Torvalds 		goto out_free_skb;
4961da177e4SLinus Torvalds 
4971da177e4SLinus Torvalds 	sock_recv_timestamp(msg, sk, skb);
4981da177e4SLinus Torvalds 
4991da177e4SLinus Torvalds 	serr = SKB_EXT_ERR(skb);
5001da177e4SLinus Torvalds 
501*c247f053SWillem de Bruijn 	if (sin && serr->port) {
5021da177e4SLinus Torvalds 		sin->sin_family = AF_INET;
503d56f90a7SArnaldo Carvalho de Melo 		sin->sin_addr.s_addr = *(__be32 *)(skb_network_header(skb) +
504d56f90a7SArnaldo Carvalho de Melo 						   serr->addr_offset);
5051da177e4SLinus Torvalds 		sin->sin_port = serr->port;
5061da177e4SLinus Torvalds 		memset(&sin->sin_zero, 0, sizeof(sin->sin_zero));
50785fbaa75SHannes Frederic Sowa 		*addr_len = sizeof(*sin);
5081da177e4SLinus Torvalds 	}
5091da177e4SLinus Torvalds 
5101da177e4SLinus Torvalds 	memcpy(&errhdr.ee, &serr->ee, sizeof(struct sock_extended_err));
5111da177e4SLinus Torvalds 	sin = &errhdr.offender;
512f812116bSWillem de Bruijn 	memset(sin, 0, sizeof(*sin));
513829ae9d6SWillem de Bruijn 
514*c247f053SWillem de Bruijn 	if (ipv4_datagram_support_cmsg(sk, skb, serr->ee.ee_origin)) {
5151da177e4SLinus Torvalds 		sin->sin_family = AF_INET;
516eddc9ec5SArnaldo Carvalho de Melo 		sin->sin_addr.s_addr = ip_hdr(skb)->saddr;
517f812116bSWillem de Bruijn 		if (inet_sk(sk)->cmsg_flags)
5181da177e4SLinus Torvalds 			ip_cmsg_recv(msg, skb);
5191da177e4SLinus Torvalds 	}
5201da177e4SLinus Torvalds 
5211da177e4SLinus Torvalds 	put_cmsg(msg, SOL_IP, IP_RECVERR, sizeof(errhdr), &errhdr);
5221da177e4SLinus Torvalds 
5231da177e4SLinus Torvalds 	/* Now we could try to dump offended packet options */
5241da177e4SLinus Torvalds 
5251da177e4SLinus Torvalds 	msg->msg_flags |= MSG_ERRQUEUE;
5261da177e4SLinus Torvalds 	err = copied;
5271da177e4SLinus Torvalds 
5281da177e4SLinus Torvalds out_free_skb:
5291da177e4SLinus Torvalds 	kfree_skb(skb);
5301da177e4SLinus Torvalds out:
5311da177e4SLinus Torvalds 	return err;
5321da177e4SLinus Torvalds }
5331da177e4SLinus Torvalds 
5341da177e4SLinus Torvalds 
5351da177e4SLinus Torvalds /*
5364d52cfbeSEric Dumazet  *	Socket option code for IP. This is the end of the line after any
5374d52cfbeSEric Dumazet  *	TCP,UDP etc options on an IP socket.
5381da177e4SLinus Torvalds  */
5391da177e4SLinus Torvalds 
5403fdadf7dSDmitry Mishin static int do_ip_setsockopt(struct sock *sk, int level,
541b7058842SDavid S. Miller 			    int optname, char __user *optval, unsigned int optlen)
5421da177e4SLinus Torvalds {
5431da177e4SLinus Torvalds 	struct inet_sock *inet = inet_sk(sk);
5441da177e4SLinus Torvalds 	int val = 0, err;
5451da177e4SLinus Torvalds 
5460c9f79beSXi Wang 	switch (optname) {
5470c9f79beSXi Wang 	case IP_PKTINFO:
5480c9f79beSXi Wang 	case IP_RECVTTL:
5490c9f79beSXi Wang 	case IP_RECVOPTS:
5500c9f79beSXi Wang 	case IP_RECVTOS:
5510c9f79beSXi Wang 	case IP_RETOPTS:
5520c9f79beSXi Wang 	case IP_TOS:
5530c9f79beSXi Wang 	case IP_TTL:
5540c9f79beSXi Wang 	case IP_HDRINCL:
5550c9f79beSXi Wang 	case IP_MTU_DISCOVER:
5560c9f79beSXi Wang 	case IP_RECVERR:
5570c9f79beSXi Wang 	case IP_ROUTER_ALERT:
5580c9f79beSXi Wang 	case IP_FREEBIND:
5590c9f79beSXi Wang 	case IP_PASSSEC:
5600c9f79beSXi Wang 	case IP_TRANSPARENT:
5610c9f79beSXi Wang 	case IP_MINTTL:
5620c9f79beSXi Wang 	case IP_NODEFRAG:
5630c9f79beSXi Wang 	case IP_UNICAST_IF:
5640c9f79beSXi Wang 	case IP_MULTICAST_TTL:
5650c9f79beSXi Wang 	case IP_MULTICAST_ALL:
5660c9f79beSXi Wang 	case IP_MULTICAST_LOOP:
5670c9f79beSXi Wang 	case IP_RECVORIGDSTADDR:
568ad6f939aSTom Herbert 	case IP_CHECKSUM:
5691da177e4SLinus Torvalds 		if (optlen >= sizeof(int)) {
5701da177e4SLinus Torvalds 			if (get_user(val, (int __user *) optval))
5711da177e4SLinus Torvalds 				return -EFAULT;
5721da177e4SLinus Torvalds 		} else if (optlen >= sizeof(char)) {
5731da177e4SLinus Torvalds 			unsigned char ucval;
5741da177e4SLinus Torvalds 
5751da177e4SLinus Torvalds 			if (get_user(ucval, (unsigned char __user *) optval))
5761da177e4SLinus Torvalds 				return -EFAULT;
5771da177e4SLinus Torvalds 			val = (int) ucval;
5781da177e4SLinus Torvalds 		}
5791da177e4SLinus Torvalds 	}
5801da177e4SLinus Torvalds 
5811da177e4SLinus Torvalds 	/* If optlen==0, it is equivalent to val == 0 */
5821da177e4SLinus Torvalds 
5836a9fb947SPavel Emelyanov 	if (ip_mroute_opt(optname))
5841da177e4SLinus Torvalds 		return ip_mroute_setsockopt(sk, optname, optval, optlen);
5851da177e4SLinus Torvalds 
5861da177e4SLinus Torvalds 	err = 0;
5871da177e4SLinus Torvalds 	lock_sock(sk);
5881da177e4SLinus Torvalds 
5891da177e4SLinus Torvalds 	switch (optname) {
5901da177e4SLinus Torvalds 	case IP_OPTIONS:
5911da177e4SLinus Torvalds 	{
592f6d8bd05SEric Dumazet 		struct ip_options_rcu *old, *opt = NULL;
593f6d8bd05SEric Dumazet 
59465a1c4ffSroel kluin 		if (optlen > 40)
5951da177e4SLinus Torvalds 			goto e_inval;
5963b1e0a65SYOSHIFUJI Hideaki 		err = ip_options_get_from_user(sock_net(sk), &opt,
597cb84663eSDenis V. Lunev 					       optval, optlen);
5981da177e4SLinus Torvalds 		if (err)
5991da177e4SLinus Torvalds 			break;
600f6d8bd05SEric Dumazet 		old = rcu_dereference_protected(inet->inet_opt,
601f6d8bd05SEric Dumazet 						sock_owned_by_user(sk));
602d83d8461SArnaldo Carvalho de Melo 		if (inet->is_icsk) {
603d83d8461SArnaldo Carvalho de Melo 			struct inet_connection_sock *icsk = inet_csk(sk);
604dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6)
6051da177e4SLinus Torvalds 			if (sk->sk_family == PF_INET ||
6061da177e4SLinus Torvalds 			    (!((1 << sk->sk_state) &
6071da177e4SLinus Torvalds 			       (TCPF_LISTEN | TCPF_CLOSE)) &&
608c720c7e8SEric Dumazet 			     inet->inet_daddr != LOOPBACK4_IPV6)) {
6091da177e4SLinus Torvalds #endif
610f6d8bd05SEric Dumazet 				if (old)
611f6d8bd05SEric Dumazet 					icsk->icsk_ext_hdr_len -= old->opt.optlen;
6121da177e4SLinus Torvalds 				if (opt)
613f6d8bd05SEric Dumazet 					icsk->icsk_ext_hdr_len += opt->opt.optlen;
614d83d8461SArnaldo Carvalho de Melo 				icsk->icsk_sync_mss(sk, icsk->icsk_pmtu_cookie);
615dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6)
6161da177e4SLinus Torvalds 			}
6171da177e4SLinus Torvalds #endif
6181da177e4SLinus Torvalds 		}
619f6d8bd05SEric Dumazet 		rcu_assign_pointer(inet->inet_opt, opt);
620f6d8bd05SEric Dumazet 		if (old)
621605b4afeSPaul E. McKenney 			kfree_rcu(old, rcu);
6221da177e4SLinus Torvalds 		break;
6231da177e4SLinus Torvalds 	}
6241da177e4SLinus Torvalds 	case IP_PKTINFO:
6251da177e4SLinus Torvalds 		if (val)
6261da177e4SLinus Torvalds 			inet->cmsg_flags |= IP_CMSG_PKTINFO;
6271da177e4SLinus Torvalds 		else
6281da177e4SLinus Torvalds 			inet->cmsg_flags &= ~IP_CMSG_PKTINFO;
6291da177e4SLinus Torvalds 		break;
6301da177e4SLinus Torvalds 	case IP_RECVTTL:
6311da177e4SLinus Torvalds 		if (val)
6321da177e4SLinus Torvalds 			inet->cmsg_flags |=  IP_CMSG_TTL;
6331da177e4SLinus Torvalds 		else
6341da177e4SLinus Torvalds 			inet->cmsg_flags &= ~IP_CMSG_TTL;
6351da177e4SLinus Torvalds 		break;
6361da177e4SLinus Torvalds 	case IP_RECVTOS:
6371da177e4SLinus Torvalds 		if (val)
6381da177e4SLinus Torvalds 			inet->cmsg_flags |=  IP_CMSG_TOS;
6391da177e4SLinus Torvalds 		else
6401da177e4SLinus Torvalds 			inet->cmsg_flags &= ~IP_CMSG_TOS;
6411da177e4SLinus Torvalds 		break;
6421da177e4SLinus Torvalds 	case IP_RECVOPTS:
6431da177e4SLinus Torvalds 		if (val)
6441da177e4SLinus Torvalds 			inet->cmsg_flags |=  IP_CMSG_RECVOPTS;
6451da177e4SLinus Torvalds 		else
6461da177e4SLinus Torvalds 			inet->cmsg_flags &= ~IP_CMSG_RECVOPTS;
6471da177e4SLinus Torvalds 		break;
6481da177e4SLinus Torvalds 	case IP_RETOPTS:
6491da177e4SLinus Torvalds 		if (val)
6501da177e4SLinus Torvalds 			inet->cmsg_flags |= IP_CMSG_RETOPTS;
6511da177e4SLinus Torvalds 		else
6521da177e4SLinus Torvalds 			inet->cmsg_flags &= ~IP_CMSG_RETOPTS;
6531da177e4SLinus Torvalds 		break;
6542c7946a7SCatherine Zhang 	case IP_PASSSEC:
6552c7946a7SCatherine Zhang 		if (val)
6562c7946a7SCatherine Zhang 			inet->cmsg_flags |= IP_CMSG_PASSSEC;
6572c7946a7SCatherine Zhang 		else
6582c7946a7SCatherine Zhang 			inet->cmsg_flags &= ~IP_CMSG_PASSSEC;
6592c7946a7SCatherine Zhang 		break;
660e8b2dfe9SBalazs Scheidler 	case IP_RECVORIGDSTADDR:
661e8b2dfe9SBalazs Scheidler 		if (val)
662e8b2dfe9SBalazs Scheidler 			inet->cmsg_flags |= IP_CMSG_ORIGDSTADDR;
663e8b2dfe9SBalazs Scheidler 		else
664e8b2dfe9SBalazs Scheidler 			inet->cmsg_flags &= ~IP_CMSG_ORIGDSTADDR;
665e8b2dfe9SBalazs Scheidler 		break;
666ad6f939aSTom Herbert 	case IP_CHECKSUM:
667ad6f939aSTom Herbert 		if (val) {
668ad6f939aSTom Herbert 			if (!(inet->cmsg_flags & IP_CMSG_CHECKSUM)) {
669ad6f939aSTom Herbert 				inet_inc_convert_csum(sk);
670ad6f939aSTom Herbert 				inet->cmsg_flags |= IP_CMSG_CHECKSUM;
671ad6f939aSTom Herbert 			}
672ad6f939aSTom Herbert 		} else {
673ad6f939aSTom Herbert 			if (inet->cmsg_flags & IP_CMSG_CHECKSUM) {
674ad6f939aSTom Herbert 				inet_dec_convert_csum(sk);
675ad6f939aSTom Herbert 				inet->cmsg_flags &= ~IP_CMSG_CHECKSUM;
676ad6f939aSTom Herbert 			}
677ad6f939aSTom Herbert 		}
678ad6f939aSTom Herbert 		break;
6791da177e4SLinus Torvalds 	case IP_TOS:	/* This sets both TOS and Precedence */
6801da177e4SLinus Torvalds 		if (sk->sk_type == SOCK_STREAM) {
6812c67e9acSMaciej Żenczykowski 			val &= ~INET_ECN_MASK;
6822c67e9acSMaciej Żenczykowski 			val |= inet->tos & INET_ECN_MASK;
6831da177e4SLinus Torvalds 		}
6841da177e4SLinus Torvalds 		if (inet->tos != val) {
6851da177e4SLinus Torvalds 			inet->tos = val;
6861da177e4SLinus Torvalds 			sk->sk_priority = rt_tos2priority(val);
6871da177e4SLinus Torvalds 			sk_dst_reset(sk);
6881da177e4SLinus Torvalds 		}
6891da177e4SLinus Torvalds 		break;
6901da177e4SLinus Torvalds 	case IP_TTL:
6911da177e4SLinus Torvalds 		if (optlen < 1)
6921da177e4SLinus Torvalds 			goto e_inval;
693c9be4a5cSCong Wang 		if (val != -1 && (val < 1 || val > 255))
6941da177e4SLinus Torvalds 			goto e_inval;
6951da177e4SLinus Torvalds 		inet->uc_ttl = val;
6961da177e4SLinus Torvalds 		break;
6971da177e4SLinus Torvalds 	case IP_HDRINCL:
6981da177e4SLinus Torvalds 		if (sk->sk_type != SOCK_RAW) {
6991da177e4SLinus Torvalds 			err = -ENOPROTOOPT;
7001da177e4SLinus Torvalds 			break;
7011da177e4SLinus Torvalds 		}
7021da177e4SLinus Torvalds 		inet->hdrincl = val ? 1 : 0;
7031da177e4SLinus Torvalds 		break;
7047b2ff18eSJiri Olsa 	case IP_NODEFRAG:
7057b2ff18eSJiri Olsa 		if (sk->sk_type != SOCK_RAW) {
7067b2ff18eSJiri Olsa 			err = -ENOPROTOOPT;
7077b2ff18eSJiri Olsa 			break;
7087b2ff18eSJiri Olsa 		}
7097b2ff18eSJiri Olsa 		inet->nodefrag = val ? 1 : 0;
7107b2ff18eSJiri Olsa 		break;
7111da177e4SLinus Torvalds 	case IP_MTU_DISCOVER:
7121b346576SHannes Frederic Sowa 		if (val < IP_PMTUDISC_DONT || val > IP_PMTUDISC_OMIT)
7131da177e4SLinus Torvalds 			goto e_inval;
7141da177e4SLinus Torvalds 		inet->pmtudisc = val;
7151da177e4SLinus Torvalds 		break;
7161da177e4SLinus Torvalds 	case IP_RECVERR:
7171da177e4SLinus Torvalds 		inet->recverr = !!val;
7181da177e4SLinus Torvalds 		if (!val)
7191da177e4SLinus Torvalds 			skb_queue_purge(&sk->sk_error_queue);
7201da177e4SLinus Torvalds 		break;
7211da177e4SLinus Torvalds 	case IP_MULTICAST_TTL:
7221da177e4SLinus Torvalds 		if (sk->sk_type == SOCK_STREAM)
7231da177e4SLinus Torvalds 			goto e_inval;
7241da177e4SLinus Torvalds 		if (optlen < 1)
7251da177e4SLinus Torvalds 			goto e_inval;
7261da177e4SLinus Torvalds 		if (val == -1)
7271da177e4SLinus Torvalds 			val = 1;
7281da177e4SLinus Torvalds 		if (val < 0 || val > 255)
7291da177e4SLinus Torvalds 			goto e_inval;
7301da177e4SLinus Torvalds 		inet->mc_ttl = val;
7311da177e4SLinus Torvalds 		break;
7321da177e4SLinus Torvalds 	case IP_MULTICAST_LOOP:
7331da177e4SLinus Torvalds 		if (optlen < 1)
7341da177e4SLinus Torvalds 			goto e_inval;
7351da177e4SLinus Torvalds 		inet->mc_loop = !!val;
7361da177e4SLinus Torvalds 		break;
73776e21053SErich E. Hoover 	case IP_UNICAST_IF:
73876e21053SErich E. Hoover 	{
73976e21053SErich E. Hoover 		struct net_device *dev = NULL;
74076e21053SErich E. Hoover 		int ifindex;
74176e21053SErich E. Hoover 
74276e21053SErich E. Hoover 		if (optlen != sizeof(int))
74376e21053SErich E. Hoover 			goto e_inval;
74476e21053SErich E. Hoover 
74576e21053SErich E. Hoover 		ifindex = (__force int)ntohl((__force __be32)val);
74676e21053SErich E. Hoover 		if (ifindex == 0) {
74776e21053SErich E. Hoover 			inet->uc_index = 0;
74876e21053SErich E. Hoover 			err = 0;
74976e21053SErich E. Hoover 			break;
75076e21053SErich E. Hoover 		}
75176e21053SErich E. Hoover 
75276e21053SErich E. Hoover 		dev = dev_get_by_index(sock_net(sk), ifindex);
75376e21053SErich E. Hoover 		err = -EADDRNOTAVAIL;
75476e21053SErich E. Hoover 		if (!dev)
75576e21053SErich E. Hoover 			break;
75676e21053SErich E. Hoover 		dev_put(dev);
75776e21053SErich E. Hoover 
75876e21053SErich E. Hoover 		err = -EINVAL;
75976e21053SErich E. Hoover 		if (sk->sk_bound_dev_if)
76076e21053SErich E. Hoover 			break;
76176e21053SErich E. Hoover 
76276e21053SErich E. Hoover 		inet->uc_index = ifindex;
76376e21053SErich E. Hoover 		err = 0;
76476e21053SErich E. Hoover 		break;
76576e21053SErich E. Hoover 	}
7661da177e4SLinus Torvalds 	case IP_MULTICAST_IF:
7671da177e4SLinus Torvalds 	{
7681da177e4SLinus Torvalds 		struct ip_mreqn mreq;
7691da177e4SLinus Torvalds 		struct net_device *dev = NULL;
7701da177e4SLinus Torvalds 
7711da177e4SLinus Torvalds 		if (sk->sk_type == SOCK_STREAM)
7721da177e4SLinus Torvalds 			goto e_inval;
7731da177e4SLinus Torvalds 		/*
7741da177e4SLinus Torvalds 		 *	Check the arguments are allowable
7751da177e4SLinus Torvalds 		 */
7761da177e4SLinus Torvalds 
7770915921bSShan Wei 		if (optlen < sizeof(struct in_addr))
7780915921bSShan Wei 			goto e_inval;
7790915921bSShan Wei 
7801da177e4SLinus Torvalds 		err = -EFAULT;
7811da177e4SLinus Torvalds 		if (optlen >= sizeof(struct ip_mreqn)) {
7821da177e4SLinus Torvalds 			if (copy_from_user(&mreq, optval, sizeof(mreq)))
7831da177e4SLinus Torvalds 				break;
7841da177e4SLinus Torvalds 		} else {
7851da177e4SLinus Torvalds 			memset(&mreq, 0, sizeof(mreq));
7863a084ddbSJiri Pirko 			if (optlen >= sizeof(struct ip_mreq)) {
7873a084ddbSJiri Pirko 				if (copy_from_user(&mreq, optval,
7883a084ddbSJiri Pirko 						   sizeof(struct ip_mreq)))
7893a084ddbSJiri Pirko 					break;
7903a084ddbSJiri Pirko 			} else if (optlen >= sizeof(struct in_addr)) {
7913a084ddbSJiri Pirko 				if (copy_from_user(&mreq.imr_address, optval,
7924d52cfbeSEric Dumazet 						   sizeof(struct in_addr)))
7931da177e4SLinus Torvalds 					break;
7941da177e4SLinus Torvalds 			}
7953a084ddbSJiri Pirko 		}
7961da177e4SLinus Torvalds 
7971da177e4SLinus Torvalds 		if (!mreq.imr_ifindex) {
798e6f1cebfSAl Viro 			if (mreq.imr_address.s_addr == htonl(INADDR_ANY)) {
7991da177e4SLinus Torvalds 				inet->mc_index = 0;
8001da177e4SLinus Torvalds 				inet->mc_addr  = 0;
8011da177e4SLinus Torvalds 				err = 0;
8021da177e4SLinus Torvalds 				break;
8031da177e4SLinus Torvalds 			}
8043b1e0a65SYOSHIFUJI Hideaki 			dev = ip_dev_find(sock_net(sk), mreq.imr_address.s_addr);
80555b80503SEric Dumazet 			if (dev)
8061da177e4SLinus Torvalds 				mreq.imr_ifindex = dev->ifindex;
8071da177e4SLinus Torvalds 		} else
80855b80503SEric Dumazet 			dev = dev_get_by_index(sock_net(sk), mreq.imr_ifindex);
8091da177e4SLinus Torvalds 
8101da177e4SLinus Torvalds 
8111da177e4SLinus Torvalds 		err = -EADDRNOTAVAIL;
8121da177e4SLinus Torvalds 		if (!dev)
8131da177e4SLinus Torvalds 			break;
81455b80503SEric Dumazet 		dev_put(dev);
8151da177e4SLinus Torvalds 
8161da177e4SLinus Torvalds 		err = -EINVAL;
8171da177e4SLinus Torvalds 		if (sk->sk_bound_dev_if &&
8181da177e4SLinus Torvalds 		    mreq.imr_ifindex != sk->sk_bound_dev_if)
8191da177e4SLinus Torvalds 			break;
8201da177e4SLinus Torvalds 
8211da177e4SLinus Torvalds 		inet->mc_index = mreq.imr_ifindex;
8221da177e4SLinus Torvalds 		inet->mc_addr  = mreq.imr_address.s_addr;
8231da177e4SLinus Torvalds 		err = 0;
8241da177e4SLinus Torvalds 		break;
8251da177e4SLinus Torvalds 	}
8261da177e4SLinus Torvalds 
8271da177e4SLinus Torvalds 	case IP_ADD_MEMBERSHIP:
8281da177e4SLinus Torvalds 	case IP_DROP_MEMBERSHIP:
8291da177e4SLinus Torvalds 	{
8301da177e4SLinus Torvalds 		struct ip_mreqn mreq;
8311da177e4SLinus Torvalds 
832a96fb49bSFlavio Leitner 		err = -EPROTO;
833a96fb49bSFlavio Leitner 		if (inet_sk(sk)->is_icsk)
834a96fb49bSFlavio Leitner 			break;
835a96fb49bSFlavio Leitner 
8361da177e4SLinus Torvalds 		if (optlen < sizeof(struct ip_mreq))
8371da177e4SLinus Torvalds 			goto e_inval;
8381da177e4SLinus Torvalds 		err = -EFAULT;
8391da177e4SLinus Torvalds 		if (optlen >= sizeof(struct ip_mreqn)) {
8401da177e4SLinus Torvalds 			if (copy_from_user(&mreq, optval, sizeof(mreq)))
8411da177e4SLinus Torvalds 				break;
8421da177e4SLinus Torvalds 		} else {
8431da177e4SLinus Torvalds 			memset(&mreq, 0, sizeof(mreq));
8441da177e4SLinus Torvalds 			if (copy_from_user(&mreq, optval, sizeof(struct ip_mreq)))
8451da177e4SLinus Torvalds 				break;
8461da177e4SLinus Torvalds 		}
8471da177e4SLinus Torvalds 
8481da177e4SLinus Torvalds 		if (optname == IP_ADD_MEMBERSHIP)
8491da177e4SLinus Torvalds 			err = ip_mc_join_group(sk, &mreq);
8501da177e4SLinus Torvalds 		else
8511da177e4SLinus Torvalds 			err = ip_mc_leave_group(sk, &mreq);
8521da177e4SLinus Torvalds 		break;
8531da177e4SLinus Torvalds 	}
8541da177e4SLinus Torvalds 	case IP_MSFILTER:
8551da177e4SLinus Torvalds 	{
8561da177e4SLinus Torvalds 		struct ip_msfilter *msf;
8571da177e4SLinus Torvalds 
8581da177e4SLinus Torvalds 		if (optlen < IP_MSFILTER_SIZE(0))
8591da177e4SLinus Torvalds 			goto e_inval;
8601da177e4SLinus Torvalds 		if (optlen > sysctl_optmem_max) {
8611da177e4SLinus Torvalds 			err = -ENOBUFS;
8621da177e4SLinus Torvalds 			break;
8631da177e4SLinus Torvalds 		}
8648b3a7005SKris Katterjohn 		msf = kmalloc(optlen, GFP_KERNEL);
865cfcabdccSStephen Hemminger 		if (!msf) {
8661da177e4SLinus Torvalds 			err = -ENOBUFS;
8671da177e4SLinus Torvalds 			break;
8681da177e4SLinus Torvalds 		}
8691da177e4SLinus Torvalds 		err = -EFAULT;
8701da177e4SLinus Torvalds 		if (copy_from_user(msf, optval, optlen)) {
8711da177e4SLinus Torvalds 			kfree(msf);
8721da177e4SLinus Torvalds 			break;
8731da177e4SLinus Torvalds 		}
8741da177e4SLinus Torvalds 		/* numsrc >= (1G-4) overflow in 32 bits */
8751da177e4SLinus Torvalds 		if (msf->imsf_numsrc >= 0x3ffffffcU ||
8761da177e4SLinus Torvalds 		    msf->imsf_numsrc > sysctl_igmp_max_msf) {
8771da177e4SLinus Torvalds 			kfree(msf);
8781da177e4SLinus Torvalds 			err = -ENOBUFS;
8791da177e4SLinus Torvalds 			break;
8801da177e4SLinus Torvalds 		}
8811da177e4SLinus Torvalds 		if (IP_MSFILTER_SIZE(msf->imsf_numsrc) > optlen) {
8821da177e4SLinus Torvalds 			kfree(msf);
8831da177e4SLinus Torvalds 			err = -EINVAL;
8841da177e4SLinus Torvalds 			break;
8851da177e4SLinus Torvalds 		}
8861da177e4SLinus Torvalds 		err = ip_mc_msfilter(sk, msf, 0);
8871da177e4SLinus Torvalds 		kfree(msf);
8881da177e4SLinus Torvalds 		break;
8891da177e4SLinus Torvalds 	}
8901da177e4SLinus Torvalds 	case IP_BLOCK_SOURCE:
8911da177e4SLinus Torvalds 	case IP_UNBLOCK_SOURCE:
8921da177e4SLinus Torvalds 	case IP_ADD_SOURCE_MEMBERSHIP:
8931da177e4SLinus Torvalds 	case IP_DROP_SOURCE_MEMBERSHIP:
8941da177e4SLinus Torvalds 	{
8951da177e4SLinus Torvalds 		struct ip_mreq_source mreqs;
8961da177e4SLinus Torvalds 		int omode, add;
8971da177e4SLinus Torvalds 
8981da177e4SLinus Torvalds 		if (optlen != sizeof(struct ip_mreq_source))
8991da177e4SLinus Torvalds 			goto e_inval;
9001da177e4SLinus Torvalds 		if (copy_from_user(&mreqs, optval, sizeof(mreqs))) {
9011da177e4SLinus Torvalds 			err = -EFAULT;
9021da177e4SLinus Torvalds 			break;
9031da177e4SLinus Torvalds 		}
9041da177e4SLinus Torvalds 		if (optname == IP_BLOCK_SOURCE) {
9051da177e4SLinus Torvalds 			omode = MCAST_EXCLUDE;
9061da177e4SLinus Torvalds 			add = 1;
9071da177e4SLinus Torvalds 		} else if (optname == IP_UNBLOCK_SOURCE) {
9081da177e4SLinus Torvalds 			omode = MCAST_EXCLUDE;
9091da177e4SLinus Torvalds 			add = 0;
9101da177e4SLinus Torvalds 		} else if (optname == IP_ADD_SOURCE_MEMBERSHIP) {
9111da177e4SLinus Torvalds 			struct ip_mreqn mreq;
9121da177e4SLinus Torvalds 
9131da177e4SLinus Torvalds 			mreq.imr_multiaddr.s_addr = mreqs.imr_multiaddr;
9141da177e4SLinus Torvalds 			mreq.imr_address.s_addr = mreqs.imr_interface;
9151da177e4SLinus Torvalds 			mreq.imr_ifindex = 0;
9161da177e4SLinus Torvalds 			err = ip_mc_join_group(sk, &mreq);
9178cdaaa15SDavid L Stevens 			if (err && err != -EADDRINUSE)
9181da177e4SLinus Torvalds 				break;
9191da177e4SLinus Torvalds 			omode = MCAST_INCLUDE;
9201da177e4SLinus Torvalds 			add = 1;
9211da177e4SLinus Torvalds 		} else /* IP_DROP_SOURCE_MEMBERSHIP */ {
9221da177e4SLinus Torvalds 			omode = MCAST_INCLUDE;
9231da177e4SLinus Torvalds 			add = 0;
9241da177e4SLinus Torvalds 		}
9251da177e4SLinus Torvalds 		err = ip_mc_source(add, omode, sk, &mreqs, 0);
9261da177e4SLinus Torvalds 		break;
9271da177e4SLinus Torvalds 	}
9281da177e4SLinus Torvalds 	case MCAST_JOIN_GROUP:
9291da177e4SLinus Torvalds 	case MCAST_LEAVE_GROUP:
9301da177e4SLinus Torvalds 	{
9311da177e4SLinus Torvalds 		struct group_req greq;
9321da177e4SLinus Torvalds 		struct sockaddr_in *psin;
9331da177e4SLinus Torvalds 		struct ip_mreqn mreq;
9341da177e4SLinus Torvalds 
9351da177e4SLinus Torvalds 		if (optlen < sizeof(struct group_req))
9361da177e4SLinus Torvalds 			goto e_inval;
9371da177e4SLinus Torvalds 		err = -EFAULT;
9381da177e4SLinus Torvalds 		if (copy_from_user(&greq, optval, sizeof(greq)))
9391da177e4SLinus Torvalds 			break;
9401da177e4SLinus Torvalds 		psin = (struct sockaddr_in *)&greq.gr_group;
9411da177e4SLinus Torvalds 		if (psin->sin_family != AF_INET)
9421da177e4SLinus Torvalds 			goto e_inval;
9431da177e4SLinus Torvalds 		memset(&mreq, 0, sizeof(mreq));
9441da177e4SLinus Torvalds 		mreq.imr_multiaddr = psin->sin_addr;
9451da177e4SLinus Torvalds 		mreq.imr_ifindex = greq.gr_interface;
9461da177e4SLinus Torvalds 
9471da177e4SLinus Torvalds 		if (optname == MCAST_JOIN_GROUP)
9481da177e4SLinus Torvalds 			err = ip_mc_join_group(sk, &mreq);
9491da177e4SLinus Torvalds 		else
9501da177e4SLinus Torvalds 			err = ip_mc_leave_group(sk, &mreq);
9511da177e4SLinus Torvalds 		break;
9521da177e4SLinus Torvalds 	}
9531da177e4SLinus Torvalds 	case MCAST_JOIN_SOURCE_GROUP:
9541da177e4SLinus Torvalds 	case MCAST_LEAVE_SOURCE_GROUP:
9551da177e4SLinus Torvalds 	case MCAST_BLOCK_SOURCE:
9561da177e4SLinus Torvalds 	case MCAST_UNBLOCK_SOURCE:
9571da177e4SLinus Torvalds 	{
9581da177e4SLinus Torvalds 		struct group_source_req greqs;
9591da177e4SLinus Torvalds 		struct ip_mreq_source mreqs;
9601da177e4SLinus Torvalds 		struct sockaddr_in *psin;
9611da177e4SLinus Torvalds 		int omode, add;
9621da177e4SLinus Torvalds 
9631da177e4SLinus Torvalds 		if (optlen != sizeof(struct group_source_req))
9641da177e4SLinus Torvalds 			goto e_inval;
9651da177e4SLinus Torvalds 		if (copy_from_user(&greqs, optval, sizeof(greqs))) {
9661da177e4SLinus Torvalds 			err = -EFAULT;
9671da177e4SLinus Torvalds 			break;
9681da177e4SLinus Torvalds 		}
9691da177e4SLinus Torvalds 		if (greqs.gsr_group.ss_family != AF_INET ||
9701da177e4SLinus Torvalds 		    greqs.gsr_source.ss_family != AF_INET) {
9711da177e4SLinus Torvalds 			err = -EADDRNOTAVAIL;
9721da177e4SLinus Torvalds 			break;
9731da177e4SLinus Torvalds 		}
9741da177e4SLinus Torvalds 		psin = (struct sockaddr_in *)&greqs.gsr_group;
9751da177e4SLinus Torvalds 		mreqs.imr_multiaddr = psin->sin_addr.s_addr;
9761da177e4SLinus Torvalds 		psin = (struct sockaddr_in *)&greqs.gsr_source;
9771da177e4SLinus Torvalds 		mreqs.imr_sourceaddr = psin->sin_addr.s_addr;
9781da177e4SLinus Torvalds 		mreqs.imr_interface = 0; /* use index for mc_source */
9791da177e4SLinus Torvalds 
9801da177e4SLinus Torvalds 		if (optname == MCAST_BLOCK_SOURCE) {
9811da177e4SLinus Torvalds 			omode = MCAST_EXCLUDE;
9821da177e4SLinus Torvalds 			add = 1;
9831da177e4SLinus Torvalds 		} else if (optname == MCAST_UNBLOCK_SOURCE) {
9841da177e4SLinus Torvalds 			omode = MCAST_EXCLUDE;
9851da177e4SLinus Torvalds 			add = 0;
9861da177e4SLinus Torvalds 		} else if (optname == MCAST_JOIN_SOURCE_GROUP) {
9871da177e4SLinus Torvalds 			struct ip_mreqn mreq;
9881da177e4SLinus Torvalds 
9891da177e4SLinus Torvalds 			psin = (struct sockaddr_in *)&greqs.gsr_group;
9901da177e4SLinus Torvalds 			mreq.imr_multiaddr = psin->sin_addr;
9911da177e4SLinus Torvalds 			mreq.imr_address.s_addr = 0;
9921da177e4SLinus Torvalds 			mreq.imr_ifindex = greqs.gsr_interface;
9931da177e4SLinus Torvalds 			err = ip_mc_join_group(sk, &mreq);
9948cdaaa15SDavid L Stevens 			if (err && err != -EADDRINUSE)
9951da177e4SLinus Torvalds 				break;
9961da177e4SLinus Torvalds 			greqs.gsr_interface = mreq.imr_ifindex;
9971da177e4SLinus Torvalds 			omode = MCAST_INCLUDE;
9981da177e4SLinus Torvalds 			add = 1;
9991da177e4SLinus Torvalds 		} else /* MCAST_LEAVE_SOURCE_GROUP */ {
10001da177e4SLinus Torvalds 			omode = MCAST_INCLUDE;
10011da177e4SLinus Torvalds 			add = 0;
10021da177e4SLinus Torvalds 		}
10031da177e4SLinus Torvalds 		err = ip_mc_source(add, omode, sk, &mreqs,
10041da177e4SLinus Torvalds 				   greqs.gsr_interface);
10051da177e4SLinus Torvalds 		break;
10061da177e4SLinus Torvalds 	}
10071da177e4SLinus Torvalds 	case MCAST_MSFILTER:
10081da177e4SLinus Torvalds 	{
10091da177e4SLinus Torvalds 		struct sockaddr_in *psin;
10101da177e4SLinus Torvalds 		struct ip_msfilter *msf = NULL;
10111da177e4SLinus Torvalds 		struct group_filter *gsf = NULL;
10121da177e4SLinus Torvalds 		int msize, i, ifindex;
10131da177e4SLinus Torvalds 
10141da177e4SLinus Torvalds 		if (optlen < GROUP_FILTER_SIZE(0))
10151da177e4SLinus Torvalds 			goto e_inval;
10161da177e4SLinus Torvalds 		if (optlen > sysctl_optmem_max) {
10171da177e4SLinus Torvalds 			err = -ENOBUFS;
10181da177e4SLinus Torvalds 			break;
10191da177e4SLinus Torvalds 		}
10208b3a7005SKris Katterjohn 		gsf = kmalloc(optlen, GFP_KERNEL);
1021cfcabdccSStephen Hemminger 		if (!gsf) {
10221da177e4SLinus Torvalds 			err = -ENOBUFS;
10231da177e4SLinus Torvalds 			break;
10241da177e4SLinus Torvalds 		}
10251da177e4SLinus Torvalds 		err = -EFAULT;
10264d52cfbeSEric Dumazet 		if (copy_from_user(gsf, optval, optlen))
10271da177e4SLinus Torvalds 			goto mc_msf_out;
10284d52cfbeSEric Dumazet 
10291da177e4SLinus Torvalds 		/* numsrc >= (4G-140)/128 overflow in 32 bits */
10301da177e4SLinus Torvalds 		if (gsf->gf_numsrc >= 0x1ffffff ||
10311da177e4SLinus Torvalds 		    gsf->gf_numsrc > sysctl_igmp_max_msf) {
10321da177e4SLinus Torvalds 			err = -ENOBUFS;
10331da177e4SLinus Torvalds 			goto mc_msf_out;
10341da177e4SLinus Torvalds 		}
10351da177e4SLinus Torvalds 		if (GROUP_FILTER_SIZE(gsf->gf_numsrc) > optlen) {
10361da177e4SLinus Torvalds 			err = -EINVAL;
10371da177e4SLinus Torvalds 			goto mc_msf_out;
10381da177e4SLinus Torvalds 		}
10391da177e4SLinus Torvalds 		msize = IP_MSFILTER_SIZE(gsf->gf_numsrc);
10408b3a7005SKris Katterjohn 		msf = kmalloc(msize, GFP_KERNEL);
1041cfcabdccSStephen Hemminger 		if (!msf) {
10421da177e4SLinus Torvalds 			err = -ENOBUFS;
10431da177e4SLinus Torvalds 			goto mc_msf_out;
10441da177e4SLinus Torvalds 		}
10451da177e4SLinus Torvalds 		ifindex = gsf->gf_interface;
10461da177e4SLinus Torvalds 		psin = (struct sockaddr_in *)&gsf->gf_group;
10471da177e4SLinus Torvalds 		if (psin->sin_family != AF_INET) {
10481da177e4SLinus Torvalds 			err = -EADDRNOTAVAIL;
10491da177e4SLinus Torvalds 			goto mc_msf_out;
10501da177e4SLinus Torvalds 		}
10511da177e4SLinus Torvalds 		msf->imsf_multiaddr = psin->sin_addr.s_addr;
10521da177e4SLinus Torvalds 		msf->imsf_interface = 0;
10531da177e4SLinus Torvalds 		msf->imsf_fmode = gsf->gf_fmode;
10541da177e4SLinus Torvalds 		msf->imsf_numsrc = gsf->gf_numsrc;
10551da177e4SLinus Torvalds 		err = -EADDRNOTAVAIL;
10561da177e4SLinus Torvalds 		for (i = 0; i < gsf->gf_numsrc; ++i) {
10571da177e4SLinus Torvalds 			psin = (struct sockaddr_in *)&gsf->gf_slist[i];
10581da177e4SLinus Torvalds 
10591da177e4SLinus Torvalds 			if (psin->sin_family != AF_INET)
10601da177e4SLinus Torvalds 				goto mc_msf_out;
10611da177e4SLinus Torvalds 			msf->imsf_slist[i] = psin->sin_addr.s_addr;
10621da177e4SLinus Torvalds 		}
10631da177e4SLinus Torvalds 		kfree(gsf);
10641da177e4SLinus Torvalds 		gsf = NULL;
10651da177e4SLinus Torvalds 
10661da177e4SLinus Torvalds 		err = ip_mc_msfilter(sk, msf, ifindex);
10671da177e4SLinus Torvalds mc_msf_out:
10681da177e4SLinus Torvalds 		kfree(msf);
10691da177e4SLinus Torvalds 		kfree(gsf);
10701da177e4SLinus Torvalds 		break;
10711da177e4SLinus Torvalds 	}
1072f771bef9SNivedita Singhvi 	case IP_MULTICAST_ALL:
1073f771bef9SNivedita Singhvi 		if (optlen < 1)
1074f771bef9SNivedita Singhvi 			goto e_inval;
1075f771bef9SNivedita Singhvi 		if (val != 0 && val != 1)
1076f771bef9SNivedita Singhvi 			goto e_inval;
1077f771bef9SNivedita Singhvi 		inet->mc_all = val;
1078f771bef9SNivedita Singhvi 		break;
10791da177e4SLinus Torvalds 	case IP_ROUTER_ALERT:
10801da177e4SLinus Torvalds 		err = ip_ra_control(sk, val ? 1 : 0, NULL);
10811da177e4SLinus Torvalds 		break;
10821da177e4SLinus Torvalds 
10831da177e4SLinus Torvalds 	case IP_FREEBIND:
10841da177e4SLinus Torvalds 		if (optlen < 1)
10851da177e4SLinus Torvalds 			goto e_inval;
10861da177e4SLinus Torvalds 		inet->freebind = !!val;
10871da177e4SLinus Torvalds 		break;
10881da177e4SLinus Torvalds 
10891da177e4SLinus Torvalds 	case IP_IPSEC_POLICY:
10901da177e4SLinus Torvalds 	case IP_XFRM_POLICY:
10916fc0b4a7SHerbert Xu 		err = -EPERM;
109252e804c6SEric W. Biederman 		if (!ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN))
10936fc0b4a7SHerbert Xu 			break;
10941da177e4SLinus Torvalds 		err = xfrm_user_policy(sk, optname, optval, optlen);
10951da177e4SLinus Torvalds 		break;
10961da177e4SLinus Torvalds 
1097f5715aeaSKOVACS Krisztian 	case IP_TRANSPARENT:
109852e804c6SEric W. Biederman 		if (!!val && !ns_capable(sock_net(sk)->user_ns, CAP_NET_RAW) &&
109952e804c6SEric W. Biederman 		    !ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN)) {
1100f5715aeaSKOVACS Krisztian 			err = -EPERM;
1101f5715aeaSKOVACS Krisztian 			break;
1102f5715aeaSKOVACS Krisztian 		}
1103f5715aeaSKOVACS Krisztian 		if (optlen < 1)
1104f5715aeaSKOVACS Krisztian 			goto e_inval;
1105f5715aeaSKOVACS Krisztian 		inet->transparent = !!val;
1106f5715aeaSKOVACS Krisztian 		break;
1107f5715aeaSKOVACS Krisztian 
1108d218d111SStephen Hemminger 	case IP_MINTTL:
1109d218d111SStephen Hemminger 		if (optlen < 1)
1110d218d111SStephen Hemminger 			goto e_inval;
1111d218d111SStephen Hemminger 		if (val < 0 || val > 255)
1112d218d111SStephen Hemminger 			goto e_inval;
1113d218d111SStephen Hemminger 		inet->min_ttl = val;
1114d218d111SStephen Hemminger 		break;
1115d218d111SStephen Hemminger 
11161da177e4SLinus Torvalds 	default:
11171da177e4SLinus Torvalds 		err = -ENOPROTOOPT;
11181da177e4SLinus Torvalds 		break;
11191da177e4SLinus Torvalds 	}
11201da177e4SLinus Torvalds 	release_sock(sk);
11211da177e4SLinus Torvalds 	return err;
11221da177e4SLinus Torvalds 
11231da177e4SLinus Torvalds e_inval:
11241da177e4SLinus Torvalds 	release_sock(sk);
11251da177e4SLinus Torvalds 	return -EINVAL;
11261da177e4SLinus Torvalds }
11271da177e4SLinus Torvalds 
1128f84af32cSEric Dumazet /**
1129829ae9d6SWillem de Bruijn  * ipv4_pktinfo_prepare - transfer some info from rtable to skb
1130f84af32cSEric Dumazet  * @sk: socket
1131f84af32cSEric Dumazet  * @skb: buffer
1132f84af32cSEric Dumazet  *
113335ebf65eSDavid S. Miller  * To support IP_CMSG_PKTINFO option, we store rt_iif and specific
113435ebf65eSDavid S. Miller  * destination in skb->cb[] before dst drop.
11358e3bff96Sstephen hemminger  * This way, receiver doesn't make cache line misses to read rtable.
1136f84af32cSEric Dumazet  */
1137fbf8866dSShawn Bohrer void ipv4_pktinfo_prepare(const struct sock *sk, struct sk_buff *skb)
1138f84af32cSEric Dumazet {
1139d826eb14SEric Dumazet 	struct in_pktinfo *pktinfo = PKTINFO_SKB_CB(skb);
11404b261c75SHannes Frederic Sowa 	bool prepare = (inet_sk(sk)->cmsg_flags & IP_CMSG_PKTINFO) ||
11414b261c75SHannes Frederic Sowa 		       ipv6_sk_rxinfo(sk);
1142d826eb14SEric Dumazet 
11434b261c75SHannes Frederic Sowa 	if (prepare && skb_rtable(skb)) {
114492101b3bSDavid S. Miller 		pktinfo->ipi_ifindex = inet_iif(skb);
114535ebf65eSDavid S. Miller 		pktinfo->ipi_spec_dst.s_addr = fib_compute_spec_dst(skb);
1146d826eb14SEric Dumazet 	} else {
1147d826eb14SEric Dumazet 		pktinfo->ipi_ifindex = 0;
1148d826eb14SEric Dumazet 		pktinfo->ipi_spec_dst.s_addr = 0;
1149f84af32cSEric Dumazet 	}
1150d826eb14SEric Dumazet 	skb_dst_drop(skb);
1151d826eb14SEric Dumazet }
1152f84af32cSEric Dumazet 
11533fdadf7dSDmitry Mishin int ip_setsockopt(struct sock *sk, int level,
1154b7058842SDavid S. Miller 		int optname, char __user *optval, unsigned int optlen)
11553fdadf7dSDmitry Mishin {
11563fdadf7dSDmitry Mishin 	int err;
11573fdadf7dSDmitry Mishin 
11583fdadf7dSDmitry Mishin 	if (level != SOL_IP)
11593fdadf7dSDmitry Mishin 		return -ENOPROTOOPT;
11603fdadf7dSDmitry Mishin 
11613fdadf7dSDmitry Mishin 	err = do_ip_setsockopt(sk, level, optname, optval, optlen);
11623fdadf7dSDmitry Mishin #ifdef CONFIG_NETFILTER
11633fdadf7dSDmitry Mishin 	/* we need to exclude all possible ENOPROTOOPTs except default case */
11643fdadf7dSDmitry Mishin 	if (err == -ENOPROTOOPT && optname != IP_HDRINCL &&
11656a9fb947SPavel Emelyanov 			optname != IP_IPSEC_POLICY &&
11666a9fb947SPavel Emelyanov 			optname != IP_XFRM_POLICY &&
11676a9fb947SPavel Emelyanov 			!ip_mroute_opt(optname)) {
11683fdadf7dSDmitry Mishin 		lock_sock(sk);
11693fdadf7dSDmitry Mishin 		err = nf_setsockopt(sk, PF_INET, optname, optval, optlen);
11703fdadf7dSDmitry Mishin 		release_sock(sk);
11713fdadf7dSDmitry Mishin 	}
11723fdadf7dSDmitry Mishin #endif
11733fdadf7dSDmitry Mishin 	return err;
11743fdadf7dSDmitry Mishin }
11754d52cfbeSEric Dumazet EXPORT_SYMBOL(ip_setsockopt);
11763fdadf7dSDmitry Mishin 
11773fdadf7dSDmitry Mishin #ifdef CONFIG_COMPAT
1178543d9cfeSArnaldo Carvalho de Melo int compat_ip_setsockopt(struct sock *sk, int level, int optname,
1179b7058842SDavid S. Miller 			 char __user *optval, unsigned int optlen)
11803fdadf7dSDmitry Mishin {
11813fdadf7dSDmitry Mishin 	int err;
11823fdadf7dSDmitry Mishin 
11833fdadf7dSDmitry Mishin 	if (level != SOL_IP)
11843fdadf7dSDmitry Mishin 		return -ENOPROTOOPT;
11853fdadf7dSDmitry Mishin 
1186dae50295SDavid L Stevens 	if (optname >= MCAST_JOIN_GROUP && optname <= MCAST_MSFILTER)
1187dae50295SDavid L Stevens 		return compat_mc_setsockopt(sk, level, optname, optval, optlen,
1188dae50295SDavid L Stevens 			ip_setsockopt);
1189dae50295SDavid L Stevens 
11903fdadf7dSDmitry Mishin 	err = do_ip_setsockopt(sk, level, optname, optval, optlen);
11913fdadf7dSDmitry Mishin #ifdef CONFIG_NETFILTER
11923fdadf7dSDmitry Mishin 	/* we need to exclude all possible ENOPROTOOPTs except default case */
11933fdadf7dSDmitry Mishin 	if (err == -ENOPROTOOPT && optname != IP_HDRINCL &&
11946a9fb947SPavel Emelyanov 			optname != IP_IPSEC_POLICY &&
11956a9fb947SPavel Emelyanov 			optname != IP_XFRM_POLICY &&
11966a9fb947SPavel Emelyanov 			!ip_mroute_opt(optname)) {
11973fdadf7dSDmitry Mishin 		lock_sock(sk);
1198543d9cfeSArnaldo Carvalho de Melo 		err = compat_nf_setsockopt(sk, PF_INET, optname,
1199543d9cfeSArnaldo Carvalho de Melo 					   optval, optlen);
12003fdadf7dSDmitry Mishin 		release_sock(sk);
12013fdadf7dSDmitry Mishin 	}
12023fdadf7dSDmitry Mishin #endif
12033fdadf7dSDmitry Mishin 	return err;
12043fdadf7dSDmitry Mishin }
1205543d9cfeSArnaldo Carvalho de Melo EXPORT_SYMBOL(compat_ip_setsockopt);
12063fdadf7dSDmitry Mishin #endif
12073fdadf7dSDmitry Mishin 
12081da177e4SLinus Torvalds /*
12094d52cfbeSEric Dumazet  *	Get the options. Note for future reference. The GET of IP options gets
12104d52cfbeSEric Dumazet  *	the _received_ ones. The set sets the _sent_ ones.
12111da177e4SLinus Torvalds  */
12121da177e4SLinus Torvalds 
12133fdadf7dSDmitry Mishin static int do_ip_getsockopt(struct sock *sk, int level, int optname,
121495c96174SEric Dumazet 			    char __user *optval, int __user *optlen, unsigned int flags)
12151da177e4SLinus Torvalds {
12161da177e4SLinus Torvalds 	struct inet_sock *inet = inet_sk(sk);
12171da177e4SLinus Torvalds 	int val;
12181da177e4SLinus Torvalds 	int len;
12191da177e4SLinus Torvalds 
12201da177e4SLinus Torvalds 	if (level != SOL_IP)
12211da177e4SLinus Torvalds 		return -EOPNOTSUPP;
12221da177e4SLinus Torvalds 
12236a9fb947SPavel Emelyanov 	if (ip_mroute_opt(optname))
12241da177e4SLinus Torvalds 		return ip_mroute_getsockopt(sk, optname, optval, optlen);
12251da177e4SLinus Torvalds 
12261da177e4SLinus Torvalds 	if (get_user(len, optlen))
12271da177e4SLinus Torvalds 		return -EFAULT;
12281da177e4SLinus Torvalds 	if (len < 0)
12291da177e4SLinus Torvalds 		return -EINVAL;
12301da177e4SLinus Torvalds 
12311da177e4SLinus Torvalds 	lock_sock(sk);
12321da177e4SLinus Torvalds 
12331da177e4SLinus Torvalds 	switch (optname) {
12341da177e4SLinus Torvalds 	case IP_OPTIONS:
12351da177e4SLinus Torvalds 	{
12361da177e4SLinus Torvalds 		unsigned char optbuf[sizeof(struct ip_options)+40];
12371da177e4SLinus Torvalds 		struct ip_options *opt = (struct ip_options *)optbuf;
1238f6d8bd05SEric Dumazet 		struct ip_options_rcu *inet_opt;
1239f6d8bd05SEric Dumazet 
1240f6d8bd05SEric Dumazet 		inet_opt = rcu_dereference_protected(inet->inet_opt,
1241f6d8bd05SEric Dumazet 						     sock_owned_by_user(sk));
12421da177e4SLinus Torvalds 		opt->optlen = 0;
1243f6d8bd05SEric Dumazet 		if (inet_opt)
1244f6d8bd05SEric Dumazet 			memcpy(optbuf, &inet_opt->opt,
12451da177e4SLinus Torvalds 			       sizeof(struct ip_options) +
1246f6d8bd05SEric Dumazet 			       inet_opt->opt.optlen);
12471da177e4SLinus Torvalds 		release_sock(sk);
12481da177e4SLinus Torvalds 
12491da177e4SLinus Torvalds 		if (opt->optlen == 0)
12501da177e4SLinus Torvalds 			return put_user(0, optlen);
12511da177e4SLinus Torvalds 
12521da177e4SLinus Torvalds 		ip_options_undo(opt);
12531da177e4SLinus Torvalds 
12541da177e4SLinus Torvalds 		len = min_t(unsigned int, len, opt->optlen);
12551da177e4SLinus Torvalds 		if (put_user(len, optlen))
12561da177e4SLinus Torvalds 			return -EFAULT;
12571da177e4SLinus Torvalds 		if (copy_to_user(optval, opt->__data, len))
12581da177e4SLinus Torvalds 			return -EFAULT;
12591da177e4SLinus Torvalds 		return 0;
12601da177e4SLinus Torvalds 	}
12611da177e4SLinus Torvalds 	case IP_PKTINFO:
12621da177e4SLinus Torvalds 		val = (inet->cmsg_flags & IP_CMSG_PKTINFO) != 0;
12631da177e4SLinus Torvalds 		break;
12641da177e4SLinus Torvalds 	case IP_RECVTTL:
12651da177e4SLinus Torvalds 		val = (inet->cmsg_flags & IP_CMSG_TTL) != 0;
12661da177e4SLinus Torvalds 		break;
12671da177e4SLinus Torvalds 	case IP_RECVTOS:
12681da177e4SLinus Torvalds 		val = (inet->cmsg_flags & IP_CMSG_TOS) != 0;
12691da177e4SLinus Torvalds 		break;
12701da177e4SLinus Torvalds 	case IP_RECVOPTS:
12711da177e4SLinus Torvalds 		val = (inet->cmsg_flags & IP_CMSG_RECVOPTS) != 0;
12721da177e4SLinus Torvalds 		break;
12731da177e4SLinus Torvalds 	case IP_RETOPTS:
12741da177e4SLinus Torvalds 		val = (inet->cmsg_flags & IP_CMSG_RETOPTS) != 0;
12751da177e4SLinus Torvalds 		break;
12762c7946a7SCatherine Zhang 	case IP_PASSSEC:
12772c7946a7SCatherine Zhang 		val = (inet->cmsg_flags & IP_CMSG_PASSSEC) != 0;
12782c7946a7SCatherine Zhang 		break;
1279e8b2dfe9SBalazs Scheidler 	case IP_RECVORIGDSTADDR:
1280e8b2dfe9SBalazs Scheidler 		val = (inet->cmsg_flags & IP_CMSG_ORIGDSTADDR) != 0;
1281e8b2dfe9SBalazs Scheidler 		break;
1282ad6f939aSTom Herbert 	case IP_CHECKSUM:
1283ad6f939aSTom Herbert 		val = (inet->cmsg_flags & IP_CMSG_CHECKSUM) != 0;
1284ad6f939aSTom Herbert 		break;
12851da177e4SLinus Torvalds 	case IP_TOS:
12861da177e4SLinus Torvalds 		val = inet->tos;
12871da177e4SLinus Torvalds 		break;
12881da177e4SLinus Torvalds 	case IP_TTL:
12891da177e4SLinus Torvalds 		val = (inet->uc_ttl == -1 ?
12901da177e4SLinus Torvalds 		       sysctl_ip_default_ttl :
12911da177e4SLinus Torvalds 		       inet->uc_ttl);
12921da177e4SLinus Torvalds 		break;
12931da177e4SLinus Torvalds 	case IP_HDRINCL:
12941da177e4SLinus Torvalds 		val = inet->hdrincl;
12951da177e4SLinus Torvalds 		break;
1296a89b4763SMichael Kerrisk 	case IP_NODEFRAG:
1297a89b4763SMichael Kerrisk 		val = inet->nodefrag;
1298a89b4763SMichael Kerrisk 		break;
12991da177e4SLinus Torvalds 	case IP_MTU_DISCOVER:
13001da177e4SLinus Torvalds 		val = inet->pmtudisc;
13011da177e4SLinus Torvalds 		break;
13021da177e4SLinus Torvalds 	case IP_MTU:
13031da177e4SLinus Torvalds 	{
13041da177e4SLinus Torvalds 		struct dst_entry *dst;
13051da177e4SLinus Torvalds 		val = 0;
13061da177e4SLinus Torvalds 		dst = sk_dst_get(sk);
13071da177e4SLinus Torvalds 		if (dst) {
13081da177e4SLinus Torvalds 			val = dst_mtu(dst);
13091da177e4SLinus Torvalds 			dst_release(dst);
13101da177e4SLinus Torvalds 		}
13111da177e4SLinus Torvalds 		if (!val) {
13121da177e4SLinus Torvalds 			release_sock(sk);
13131da177e4SLinus Torvalds 			return -ENOTCONN;
13141da177e4SLinus Torvalds 		}
13151da177e4SLinus Torvalds 		break;
13161da177e4SLinus Torvalds 	}
13171da177e4SLinus Torvalds 	case IP_RECVERR:
13181da177e4SLinus Torvalds 		val = inet->recverr;
13191da177e4SLinus Torvalds 		break;
13201da177e4SLinus Torvalds 	case IP_MULTICAST_TTL:
13211da177e4SLinus Torvalds 		val = inet->mc_ttl;
13221da177e4SLinus Torvalds 		break;
13231da177e4SLinus Torvalds 	case IP_MULTICAST_LOOP:
13241da177e4SLinus Torvalds 		val = inet->mc_loop;
13251da177e4SLinus Torvalds 		break;
132676e21053SErich E. Hoover 	case IP_UNICAST_IF:
132776e21053SErich E. Hoover 		val = (__force int)htonl((__u32) inet->uc_index);
132876e21053SErich E. Hoover 		break;
13291da177e4SLinus Torvalds 	case IP_MULTICAST_IF:
13301da177e4SLinus Torvalds 	{
13311da177e4SLinus Torvalds 		struct in_addr addr;
13321da177e4SLinus Torvalds 		len = min_t(unsigned int, len, sizeof(struct in_addr));
13331da177e4SLinus Torvalds 		addr.s_addr = inet->mc_addr;
13341da177e4SLinus Torvalds 		release_sock(sk);
13351da177e4SLinus Torvalds 
13361da177e4SLinus Torvalds 		if (put_user(len, optlen))
13371da177e4SLinus Torvalds 			return -EFAULT;
13381da177e4SLinus Torvalds 		if (copy_to_user(optval, &addr, len))
13391da177e4SLinus Torvalds 			return -EFAULT;
13401da177e4SLinus Torvalds 		return 0;
13411da177e4SLinus Torvalds 	}
13421da177e4SLinus Torvalds 	case IP_MSFILTER:
13431da177e4SLinus Torvalds 	{
13441da177e4SLinus Torvalds 		struct ip_msfilter msf;
13451da177e4SLinus Torvalds 		int err;
13461da177e4SLinus Torvalds 
13471da177e4SLinus Torvalds 		if (len < IP_MSFILTER_SIZE(0)) {
13481da177e4SLinus Torvalds 			release_sock(sk);
13491da177e4SLinus Torvalds 			return -EINVAL;
13501da177e4SLinus Torvalds 		}
13511da177e4SLinus Torvalds 		if (copy_from_user(&msf, optval, IP_MSFILTER_SIZE(0))) {
13521da177e4SLinus Torvalds 			release_sock(sk);
13531da177e4SLinus Torvalds 			return -EFAULT;
13541da177e4SLinus Torvalds 		}
13551da177e4SLinus Torvalds 		err = ip_mc_msfget(sk, &msf,
13561da177e4SLinus Torvalds 				   (struct ip_msfilter __user *)optval, optlen);
13571da177e4SLinus Torvalds 		release_sock(sk);
13581da177e4SLinus Torvalds 		return err;
13591da177e4SLinus Torvalds 	}
13601da177e4SLinus Torvalds 	case MCAST_MSFILTER:
13611da177e4SLinus Torvalds 	{
13621da177e4SLinus Torvalds 		struct group_filter gsf;
13631da177e4SLinus Torvalds 		int err;
13641da177e4SLinus Torvalds 
13651da177e4SLinus Torvalds 		if (len < GROUP_FILTER_SIZE(0)) {
13661da177e4SLinus Torvalds 			release_sock(sk);
13671da177e4SLinus Torvalds 			return -EINVAL;
13681da177e4SLinus Torvalds 		}
13691da177e4SLinus Torvalds 		if (copy_from_user(&gsf, optval, GROUP_FILTER_SIZE(0))) {
13701da177e4SLinus Torvalds 			release_sock(sk);
13711da177e4SLinus Torvalds 			return -EFAULT;
13721da177e4SLinus Torvalds 		}
13731da177e4SLinus Torvalds 		err = ip_mc_gsfget(sk, &gsf,
13744d52cfbeSEric Dumazet 				   (struct group_filter __user *)optval,
13754d52cfbeSEric Dumazet 				   optlen);
13761da177e4SLinus Torvalds 		release_sock(sk);
13771da177e4SLinus Torvalds 		return err;
13781da177e4SLinus Torvalds 	}
1379f771bef9SNivedita Singhvi 	case IP_MULTICAST_ALL:
1380f771bef9SNivedita Singhvi 		val = inet->mc_all;
1381f771bef9SNivedita Singhvi 		break;
13821da177e4SLinus Torvalds 	case IP_PKTOPTIONS:
13831da177e4SLinus Torvalds 	{
13841da177e4SLinus Torvalds 		struct msghdr msg;
13851da177e4SLinus Torvalds 
13861da177e4SLinus Torvalds 		release_sock(sk);
13871da177e4SLinus Torvalds 
13881da177e4SLinus Torvalds 		if (sk->sk_type != SOCK_STREAM)
13891da177e4SLinus Torvalds 			return -ENOPROTOOPT;
13901da177e4SLinus Torvalds 
1391c54a5e02SKaroly Kemeny 		msg.msg_control = (__force void *) optval;
13921da177e4SLinus Torvalds 		msg.msg_controllen = len;
1393dd23198eSDaniel Baluta 		msg.msg_flags = flags;
13941da177e4SLinus Torvalds 
13951da177e4SLinus Torvalds 		if (inet->cmsg_flags & IP_CMSG_PKTINFO) {
13961da177e4SLinus Torvalds 			struct in_pktinfo info;
13971da177e4SLinus Torvalds 
1398c720c7e8SEric Dumazet 			info.ipi_addr.s_addr = inet->inet_rcv_saddr;
1399c720c7e8SEric Dumazet 			info.ipi_spec_dst.s_addr = inet->inet_rcv_saddr;
14001da177e4SLinus Torvalds 			info.ipi_ifindex = inet->mc_index;
14011da177e4SLinus Torvalds 			put_cmsg(&msg, SOL_IP, IP_PKTINFO, sizeof(info), &info);
14021da177e4SLinus Torvalds 		}
14031da177e4SLinus Torvalds 		if (inet->cmsg_flags & IP_CMSG_TTL) {
14041da177e4SLinus Torvalds 			int hlim = inet->mc_ttl;
14051da177e4SLinus Torvalds 			put_cmsg(&msg, SOL_IP, IP_TTL, sizeof(hlim), &hlim);
14061da177e4SLinus Torvalds 		}
14074c507d28SJiri Benc 		if (inet->cmsg_flags & IP_CMSG_TOS) {
14084c507d28SJiri Benc 			int tos = inet->rcv_tos;
14094c507d28SJiri Benc 			put_cmsg(&msg, SOL_IP, IP_TOS, sizeof(tos), &tos);
14104c507d28SJiri Benc 		}
14111da177e4SLinus Torvalds 		len -= msg.msg_controllen;
14121da177e4SLinus Torvalds 		return put_user(len, optlen);
14131da177e4SLinus Torvalds 	}
14141da177e4SLinus Torvalds 	case IP_FREEBIND:
14151da177e4SLinus Torvalds 		val = inet->freebind;
14161da177e4SLinus Torvalds 		break;
1417f5715aeaSKOVACS Krisztian 	case IP_TRANSPARENT:
1418f5715aeaSKOVACS Krisztian 		val = inet->transparent;
1419f5715aeaSKOVACS Krisztian 		break;
1420d218d111SStephen Hemminger 	case IP_MINTTL:
1421d218d111SStephen Hemminger 		val = inet->min_ttl;
1422d218d111SStephen Hemminger 		break;
14231da177e4SLinus Torvalds 	default:
14241da177e4SLinus Torvalds 		release_sock(sk);
14251da177e4SLinus Torvalds 		return -ENOPROTOOPT;
14261da177e4SLinus Torvalds 	}
14271da177e4SLinus Torvalds 	release_sock(sk);
14281da177e4SLinus Torvalds 
1429951e07c9SDavid S. Miller 	if (len < sizeof(int) && len > 0 && val >= 0 && val <= 255) {
14301da177e4SLinus Torvalds 		unsigned char ucval = (unsigned char)val;
14311da177e4SLinus Torvalds 		len = 1;
14321da177e4SLinus Torvalds 		if (put_user(len, optlen))
14331da177e4SLinus Torvalds 			return -EFAULT;
14341da177e4SLinus Torvalds 		if (copy_to_user(optval, &ucval, 1))
14351da177e4SLinus Torvalds 			return -EFAULT;
14361da177e4SLinus Torvalds 	} else {
14371da177e4SLinus Torvalds 		len = min_t(unsigned int, sizeof(int), len);
14381da177e4SLinus Torvalds 		if (put_user(len, optlen))
14391da177e4SLinus Torvalds 			return -EFAULT;
14401da177e4SLinus Torvalds 		if (copy_to_user(optval, &val, len))
14411da177e4SLinus Torvalds 			return -EFAULT;
14421da177e4SLinus Torvalds 	}
14431da177e4SLinus Torvalds 	return 0;
14441da177e4SLinus Torvalds }
14451da177e4SLinus Torvalds 
14463fdadf7dSDmitry Mishin int ip_getsockopt(struct sock *sk, int level,
14473fdadf7dSDmitry Mishin 		  int optname, char __user *optval, int __user *optlen)
14483fdadf7dSDmitry Mishin {
14493fdadf7dSDmitry Mishin 	int err;
14503fdadf7dSDmitry Mishin 
1451dd23198eSDaniel Baluta 	err = do_ip_getsockopt(sk, level, optname, optval, optlen, 0);
14523fdadf7dSDmitry Mishin #ifdef CONFIG_NETFILTER
14533fdadf7dSDmitry Mishin 	/* we need to exclude all possible ENOPROTOOPTs except default case */
14546a9fb947SPavel Emelyanov 	if (err == -ENOPROTOOPT && optname != IP_PKTOPTIONS &&
14556a9fb947SPavel Emelyanov 			!ip_mroute_opt(optname)) {
14563fdadf7dSDmitry Mishin 		int len;
14573fdadf7dSDmitry Mishin 
14583fdadf7dSDmitry Mishin 		if (get_user(len, optlen))
14593fdadf7dSDmitry Mishin 			return -EFAULT;
14603fdadf7dSDmitry Mishin 
14613fdadf7dSDmitry Mishin 		lock_sock(sk);
14623fdadf7dSDmitry Mishin 		err = nf_getsockopt(sk, PF_INET, optname, optval,
14633fdadf7dSDmitry Mishin 				&len);
14643fdadf7dSDmitry Mishin 		release_sock(sk);
14653fdadf7dSDmitry Mishin 		if (err >= 0)
14663fdadf7dSDmitry Mishin 			err = put_user(len, optlen);
14673fdadf7dSDmitry Mishin 		return err;
14683fdadf7dSDmitry Mishin 	}
14693fdadf7dSDmitry Mishin #endif
14703fdadf7dSDmitry Mishin 	return err;
14713fdadf7dSDmitry Mishin }
14724d52cfbeSEric Dumazet EXPORT_SYMBOL(ip_getsockopt);
14733fdadf7dSDmitry Mishin 
14743fdadf7dSDmitry Mishin #ifdef CONFIG_COMPAT
1475543d9cfeSArnaldo Carvalho de Melo int compat_ip_getsockopt(struct sock *sk, int level, int optname,
1476543d9cfeSArnaldo Carvalho de Melo 			 char __user *optval, int __user *optlen)
14773fdadf7dSDmitry Mishin {
147842908c69SDavid L Stevens 	int err;
147942908c69SDavid L Stevens 
148042908c69SDavid L Stevens 	if (optname == MCAST_MSFILTER)
148142908c69SDavid L Stevens 		return compat_mc_getsockopt(sk, level, optname, optval, optlen,
148242908c69SDavid L Stevens 			ip_getsockopt);
148342908c69SDavid L Stevens 
1484dd23198eSDaniel Baluta 	err = do_ip_getsockopt(sk, level, optname, optval, optlen,
1485dd23198eSDaniel Baluta 		MSG_CMSG_COMPAT);
148642908c69SDavid L Stevens 
14873fdadf7dSDmitry Mishin #ifdef CONFIG_NETFILTER
14883fdadf7dSDmitry Mishin 	/* we need to exclude all possible ENOPROTOOPTs except default case */
14896a9fb947SPavel Emelyanov 	if (err == -ENOPROTOOPT && optname != IP_PKTOPTIONS &&
14906a9fb947SPavel Emelyanov 			!ip_mroute_opt(optname)) {
14913fdadf7dSDmitry Mishin 		int len;
14923fdadf7dSDmitry Mishin 
14933fdadf7dSDmitry Mishin 		if (get_user(len, optlen))
14943fdadf7dSDmitry Mishin 			return -EFAULT;
14953fdadf7dSDmitry Mishin 
14963fdadf7dSDmitry Mishin 		lock_sock(sk);
1497543d9cfeSArnaldo Carvalho de Melo 		err = compat_nf_getsockopt(sk, PF_INET, optname, optval, &len);
14983fdadf7dSDmitry Mishin 		release_sock(sk);
14993fdadf7dSDmitry Mishin 		if (err >= 0)
15003fdadf7dSDmitry Mishin 			err = put_user(len, optlen);
15013fdadf7dSDmitry Mishin 		return err;
15023fdadf7dSDmitry Mishin 	}
15033fdadf7dSDmitry Mishin #endif
15043fdadf7dSDmitry Mishin 	return err;
15053fdadf7dSDmitry Mishin }
1506543d9cfeSArnaldo Carvalho de Melo EXPORT_SYMBOL(compat_ip_getsockopt);
15073fdadf7dSDmitry Mishin #endif
1508