xref: /linux/net/ipv4/ip_sockglue.c (revision 1862d6208db0aeca9c8ace44915b08d5ab2cd667)
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>
477c0f6ba6SLinus Torvalds #include <linux/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 
10070ecc248SWillem de Bruijn static void ip_cmsg_recv_fragsize(struct msghdr *msg, struct sk_buff *skb)
10170ecc248SWillem de Bruijn {
10270ecc248SWillem de Bruijn 	int val;
10370ecc248SWillem de Bruijn 
10470ecc248SWillem de Bruijn 	if (IPCB(skb)->frag_max_size == 0)
10570ecc248SWillem de Bruijn 		return;
10670ecc248SWillem de Bruijn 
10770ecc248SWillem de Bruijn 	val = IPCB(skb)->frag_max_size;
10870ecc248SWillem de Bruijn 	put_cmsg(msg, SOL_IP, IP_RECVFRAGSIZE, sizeof(val), &val);
10970ecc248SWillem de Bruijn }
11070ecc248SWillem de Bruijn 
111ad6f939aSTom Herbert static void ip_cmsg_recv_checksum(struct msghdr *msg, struct sk_buff *skb,
11210df8e61SEric Dumazet 				  int tlen, int offset)
113ad6f939aSTom Herbert {
114ad6f939aSTom Herbert 	__wsum csum = skb->csum;
115ad6f939aSTom Herbert 
116ad6f939aSTom Herbert 	if (skb->ip_summed != CHECKSUM_COMPLETE)
117ad6f939aSTom Herbert 		return;
118ad6f939aSTom Herbert 
119ca4ef457SPaolo Abeni 	if (offset != 0) {
120ca4ef457SPaolo Abeni 		int tend_off = skb_transport_offset(skb) + tlen;
121ca4ef457SPaolo Abeni 		csum = csum_sub(csum, skb_checksum(skb, tend_off, offset, 0));
122ca4ef457SPaolo Abeni 	}
123ad6f939aSTom Herbert 
124ad6f939aSTom Herbert 	put_cmsg(msg, SOL_IP, IP_CHECKSUM, sizeof(__wsum), &csum);
125ad6f939aSTom Herbert }
126ad6f939aSTom Herbert 
1272c7946a7SCatherine Zhang static void ip_cmsg_recv_security(struct msghdr *msg, struct sk_buff *skb)
1282c7946a7SCatherine Zhang {
1292c7946a7SCatherine Zhang 	char *secdata;
130dc49c1f9SCatherine Zhang 	u32 seclen, secid;
1312c7946a7SCatherine Zhang 	int err;
1322c7946a7SCatherine Zhang 
133dc49c1f9SCatherine Zhang 	err = security_socket_getpeersec_dgram(NULL, skb, &secid);
134dc49c1f9SCatherine Zhang 	if (err)
135dc49c1f9SCatherine Zhang 		return;
136dc49c1f9SCatherine Zhang 
137dc49c1f9SCatherine Zhang 	err = security_secid_to_secctx(secid, &secdata, &seclen);
1382c7946a7SCatherine Zhang 	if (err)
1392c7946a7SCatherine Zhang 		return;
1402c7946a7SCatherine Zhang 
1412c7946a7SCatherine Zhang 	put_cmsg(msg, SOL_IP, SCM_SECURITY, seclen, secdata);
142dc49c1f9SCatherine Zhang 	security_release_secctx(secdata, seclen);
1432c7946a7SCatherine Zhang }
1442c7946a7SCatherine Zhang 
14521d1a161SHarvey Harrison static void ip_cmsg_recv_dstaddr(struct msghdr *msg, struct sk_buff *skb)
146e8b2dfe9SBalazs Scheidler {
147e8b2dfe9SBalazs Scheidler 	struct sockaddr_in sin;
148b71d1d42SEric Dumazet 	const struct iphdr *iph = ip_hdr(skb);
14921d1a161SHarvey Harrison 	__be16 *ports = (__be16 *)skb_transport_header(skb);
150e8b2dfe9SBalazs Scheidler 
15139b2dd76SWillem de Bruijn 	if (skb_transport_offset(skb) + 4 > (int)skb->len)
152e8b2dfe9SBalazs Scheidler 		return;
153e8b2dfe9SBalazs Scheidler 
154e8b2dfe9SBalazs Scheidler 	/* All current transport protocols have the port numbers in the
155e8b2dfe9SBalazs Scheidler 	 * first four bytes of the transport header and this function is
156e8b2dfe9SBalazs Scheidler 	 * written with this assumption in mind.
157e8b2dfe9SBalazs Scheidler 	 */
158e8b2dfe9SBalazs Scheidler 
159e8b2dfe9SBalazs Scheidler 	sin.sin_family = AF_INET;
160e8b2dfe9SBalazs Scheidler 	sin.sin_addr.s_addr = iph->daddr;
161e8b2dfe9SBalazs Scheidler 	sin.sin_port = ports[1];
162e8b2dfe9SBalazs Scheidler 	memset(sin.sin_zero, 0, sizeof(sin.sin_zero));
163e8b2dfe9SBalazs Scheidler 
164e8b2dfe9SBalazs Scheidler 	put_cmsg(msg, SOL_IP, IP_ORIGDSTADDR, sizeof(sin), &sin);
165e8b2dfe9SBalazs Scheidler }
1661da177e4SLinus Torvalds 
167ad959036SPaolo Abeni void ip_cmsg_recv_offset(struct msghdr *msg, struct sock *sk,
168ad959036SPaolo Abeni 			 struct sk_buff *skb, int tlen, int offset)
1691da177e4SLinus Torvalds {
170ad959036SPaolo Abeni 	struct inet_sock *inet = inet_sk(sk);
17195c96174SEric Dumazet 	unsigned int flags = inet->cmsg_flags;
1721da177e4SLinus Torvalds 
1731da177e4SLinus Torvalds 	/* Ordered by supposed usage frequency */
174c44d13d6STom Herbert 	if (flags & IP_CMSG_PKTINFO) {
1751da177e4SLinus Torvalds 		ip_cmsg_recv_pktinfo(msg, skb);
1761da177e4SLinus Torvalds 
177c44d13d6STom Herbert 		flags &= ~IP_CMSG_PKTINFO;
178c44d13d6STom Herbert 		if (!flags)
179c44d13d6STom Herbert 			return;
180c44d13d6STom Herbert 	}
181c44d13d6STom Herbert 
182c44d13d6STom Herbert 	if (flags & IP_CMSG_TTL) {
1831da177e4SLinus Torvalds 		ip_cmsg_recv_ttl(msg, skb);
1841da177e4SLinus Torvalds 
185c44d13d6STom Herbert 		flags &= ~IP_CMSG_TTL;
186c44d13d6STom Herbert 		if (!flags)
187c44d13d6STom Herbert 			return;
188c44d13d6STom Herbert 	}
189c44d13d6STom Herbert 
190c44d13d6STom Herbert 	if (flags & IP_CMSG_TOS) {
1911da177e4SLinus Torvalds 		ip_cmsg_recv_tos(msg, skb);
1921da177e4SLinus Torvalds 
193c44d13d6STom Herbert 		flags &= ~IP_CMSG_TOS;
194c44d13d6STom Herbert 		if (!flags)
195c44d13d6STom Herbert 			return;
196c44d13d6STom Herbert 	}
197c44d13d6STom Herbert 
198c44d13d6STom Herbert 	if (flags & IP_CMSG_RECVOPTS) {
1991da177e4SLinus Torvalds 		ip_cmsg_recv_opts(msg, skb);
2001da177e4SLinus Torvalds 
201c44d13d6STom Herbert 		flags &= ~IP_CMSG_RECVOPTS;
202c44d13d6STom Herbert 		if (!flags)
203c44d13d6STom Herbert 			return;
204c44d13d6STom Herbert 	}
205c44d13d6STom Herbert 
206c44d13d6STom Herbert 	if (flags & IP_CMSG_RETOPTS) {
2071da177e4SLinus Torvalds 		ip_cmsg_recv_retopts(msg, skb);
2082c7946a7SCatherine Zhang 
209c44d13d6STom Herbert 		flags &= ~IP_CMSG_RETOPTS;
210c44d13d6STom Herbert 		if (!flags)
211c44d13d6STom Herbert 			return;
212c44d13d6STom Herbert 	}
213c44d13d6STom Herbert 
214c44d13d6STom Herbert 	if (flags & IP_CMSG_PASSSEC) {
2152c7946a7SCatherine Zhang 		ip_cmsg_recv_security(msg, skb);
216e8b2dfe9SBalazs Scheidler 
217c44d13d6STom Herbert 		flags &= ~IP_CMSG_PASSSEC;
218c44d13d6STom Herbert 		if (!flags)
219e8b2dfe9SBalazs Scheidler 			return;
220c44d13d6STom Herbert 	}
221c44d13d6STom Herbert 
222ad6f939aSTom Herbert 	if (flags & IP_CMSG_ORIGDSTADDR) {
223e8b2dfe9SBalazs Scheidler 		ip_cmsg_recv_dstaddr(msg, skb);
224e8b2dfe9SBalazs Scheidler 
225ad6f939aSTom Herbert 		flags &= ~IP_CMSG_ORIGDSTADDR;
226ad6f939aSTom Herbert 		if (!flags)
227ad6f939aSTom Herbert 			return;
228ad6f939aSTom Herbert 	}
229ad6f939aSTom Herbert 
230ad6f939aSTom Herbert 	if (flags & IP_CMSG_CHECKSUM)
23110df8e61SEric Dumazet 		ip_cmsg_recv_checksum(msg, skb, tlen, offset);
23270ecc248SWillem de Bruijn 
23370ecc248SWillem de Bruijn 	if (flags & IP_CMSG_RECVFRAGSIZE)
23470ecc248SWillem de Bruijn 		ip_cmsg_recv_fragsize(msg, skb);
2351da177e4SLinus Torvalds }
2365961de9fSTom Herbert EXPORT_SYMBOL(ip_cmsg_recv_offset);
2371da177e4SLinus Torvalds 
23824025c46SSoheil Hassas Yeganeh int ip_cmsg_send(struct sock *sk, struct msghdr *msg, struct ipcm_cookie *ipc,
239c8e6ad08SHannes Frederic Sowa 		 bool allow_ipv6)
2401da177e4SLinus Torvalds {
241f02db315SFrancesco Fusco 	int err, val;
2421da177e4SLinus Torvalds 	struct cmsghdr *cmsg;
24324025c46SSoheil Hassas Yeganeh 	struct net *net = sock_net(sk);
2441da177e4SLinus Torvalds 
245f95b414eSGu Zheng 	for_each_cmsghdr(cmsg, msg) {
2461da177e4SLinus Torvalds 		if (!CMSG_OK(msg, cmsg))
2471da177e4SLinus Torvalds 			return -EINVAL;
2485337b5b7SEric Dumazet #if IS_ENABLED(CONFIG_IPV6)
249c8e6ad08SHannes Frederic Sowa 		if (allow_ipv6 &&
250c8e6ad08SHannes Frederic Sowa 		    cmsg->cmsg_level == SOL_IPV6 &&
251c8e6ad08SHannes Frederic Sowa 		    cmsg->cmsg_type == IPV6_PKTINFO) {
252c8e6ad08SHannes Frederic Sowa 			struct in6_pktinfo *src_info;
253c8e6ad08SHannes Frederic Sowa 
254c8e6ad08SHannes Frederic Sowa 			if (cmsg->cmsg_len < CMSG_LEN(sizeof(*src_info)))
255c8e6ad08SHannes Frederic Sowa 				return -EINVAL;
256c8e6ad08SHannes Frederic Sowa 			src_info = (struct in6_pktinfo *)CMSG_DATA(cmsg);
257c8e6ad08SHannes Frederic Sowa 			if (!ipv6_addr_v4mapped(&src_info->ipi6_addr))
258c8e6ad08SHannes Frederic Sowa 				return -EINVAL;
259c8e6ad08SHannes Frederic Sowa 			ipc->oif = src_info->ipi6_ifindex;
260c8e6ad08SHannes Frederic Sowa 			ipc->addr = src_info->ipi6_addr.s6_addr32[3];
261c8e6ad08SHannes Frederic Sowa 			continue;
262c8e6ad08SHannes Frederic Sowa 		}
263c8e6ad08SHannes Frederic Sowa #endif
26424025c46SSoheil Hassas Yeganeh 		if (cmsg->cmsg_level == SOL_SOCKET) {
2652632616bSEric Dumazet 			err = __sock_cmsg_send(sk, msg, cmsg, &ipc->sockc);
2662632616bSEric Dumazet 			if (err)
2672632616bSEric Dumazet 				return err;
26824025c46SSoheil Hassas Yeganeh 			continue;
26924025c46SSoheil Hassas Yeganeh 		}
27024025c46SSoheil Hassas Yeganeh 
2711da177e4SLinus Torvalds 		if (cmsg->cmsg_level != SOL_IP)
2721da177e4SLinus Torvalds 			continue;
2731da177e4SLinus Torvalds 		switch (cmsg->cmsg_type) {
2741da177e4SLinus Torvalds 		case IP_RETOPTS:
2751ff8cebfSyuan linyu 			err = cmsg->cmsg_len - sizeof(struct cmsghdr);
27691948309SEric Dumazet 
27791948309SEric Dumazet 			/* Our caller is responsible for freeing ipc->opt */
2784d52cfbeSEric Dumazet 			err = ip_options_get(net, &ipc->opt, CMSG_DATA(cmsg),
2794d52cfbeSEric Dumazet 					     err < 40 ? err : 40);
2801da177e4SLinus Torvalds 			if (err)
2811da177e4SLinus Torvalds 				return err;
2821da177e4SLinus Torvalds 			break;
2831da177e4SLinus Torvalds 		case IP_PKTINFO:
2841da177e4SLinus Torvalds 		{
2851da177e4SLinus Torvalds 			struct in_pktinfo *info;
2861da177e4SLinus Torvalds 			if (cmsg->cmsg_len != CMSG_LEN(sizeof(struct in_pktinfo)))
2871da177e4SLinus Torvalds 				return -EINVAL;
2881da177e4SLinus Torvalds 			info = (struct in_pktinfo *)CMSG_DATA(cmsg);
2891da177e4SLinus Torvalds 			ipc->oif = info->ipi_ifindex;
2901da177e4SLinus Torvalds 			ipc->addr = info->ipi_spec_dst.s_addr;
2911da177e4SLinus Torvalds 			break;
2921da177e4SLinus Torvalds 		}
293f02db315SFrancesco Fusco 		case IP_TTL:
294f02db315SFrancesco Fusco 			if (cmsg->cmsg_len != CMSG_LEN(sizeof(int)))
295f02db315SFrancesco Fusco 				return -EINVAL;
296f02db315SFrancesco Fusco 			val = *(int *)CMSG_DATA(cmsg);
297f02db315SFrancesco Fusco 			if (val < 1 || val > 255)
298f02db315SFrancesco Fusco 				return -EINVAL;
299f02db315SFrancesco Fusco 			ipc->ttl = val;
300f02db315SFrancesco Fusco 			break;
301f02db315SFrancesco Fusco 		case IP_TOS:
302e895cdceSEric Dumazet 			if (cmsg->cmsg_len == CMSG_LEN(sizeof(int)))
303f02db315SFrancesco Fusco 				val = *(int *)CMSG_DATA(cmsg);
304e895cdceSEric Dumazet 			else if (cmsg->cmsg_len == CMSG_LEN(sizeof(u8)))
305e895cdceSEric Dumazet 				val = *(u8 *)CMSG_DATA(cmsg);
306e895cdceSEric Dumazet 			else
307e895cdceSEric Dumazet 				return -EINVAL;
308f02db315SFrancesco Fusco 			if (val < 0 || val > 255)
309f02db315SFrancesco Fusco 				return -EINVAL;
310f02db315SFrancesco Fusco 			ipc->tos = val;
311f02db315SFrancesco Fusco 			ipc->priority = rt_tos2priority(ipc->tos);
312f02db315SFrancesco Fusco 			break;
313f02db315SFrancesco Fusco 
3141da177e4SLinus Torvalds 		default:
3151da177e4SLinus Torvalds 			return -EINVAL;
3161da177e4SLinus Torvalds 		}
3171da177e4SLinus Torvalds 	}
3181da177e4SLinus Torvalds 	return 0;
3191da177e4SLinus Torvalds }
3201da177e4SLinus Torvalds 
3211da177e4SLinus Torvalds 
3221da177e4SLinus Torvalds /* Special input handler for packets caught by router alert option.
3231da177e4SLinus Torvalds    They are selected only by protocol field, and then processed likely
3241da177e4SLinus Torvalds    local ones; but only if someone wants them! Otherwise, router
3251da177e4SLinus Torvalds    not running rsvpd will kill RSVP.
3261da177e4SLinus Torvalds 
3271da177e4SLinus Torvalds    It is user level problem, what it will make with them.
3281da177e4SLinus Torvalds    I have no idea, how it will masquearde or NAT them (it is joke, joke :-)),
3291da177e4SLinus Torvalds    but receiver should be enough clever f.e. to forward mtrace requests,
3301da177e4SLinus Torvalds    sent to multicast group to reach destination designated router.
3311da177e4SLinus Torvalds  */
33243a951e9SEric Dumazet struct ip_ra_chain __rcu *ip_ra_chain;
33366018506SEric Dumazet static DEFINE_SPINLOCK(ip_ra_lock);
33466018506SEric Dumazet 
335592fcb9dSEric Dumazet 
336592fcb9dSEric Dumazet static void ip_ra_destroy_rcu(struct rcu_head *head)
33766018506SEric Dumazet {
338592fcb9dSEric Dumazet 	struct ip_ra_chain *ra = container_of(head, struct ip_ra_chain, rcu);
339592fcb9dSEric Dumazet 
340592fcb9dSEric Dumazet 	sock_put(ra->saved_sk);
341592fcb9dSEric Dumazet 	kfree(ra);
34266018506SEric Dumazet }
3431da177e4SLinus Torvalds 
3444d52cfbeSEric Dumazet int ip_ra_control(struct sock *sk, unsigned char on,
3454d52cfbeSEric Dumazet 		  void (*destructor)(struct sock *))
3461da177e4SLinus Torvalds {
34743a951e9SEric Dumazet 	struct ip_ra_chain *ra, *new_ra;
34843a951e9SEric Dumazet 	struct ip_ra_chain __rcu **rap;
3491da177e4SLinus Torvalds 
350c720c7e8SEric Dumazet 	if (sk->sk_type != SOCK_RAW || inet_sk(sk)->inet_num == IPPROTO_RAW)
3511da177e4SLinus Torvalds 		return -EINVAL;
3521da177e4SLinus Torvalds 
3531da177e4SLinus Torvalds 	new_ra = on ? kmalloc(sizeof(*new_ra), GFP_KERNEL) : NULL;
3541da177e4SLinus Torvalds 
35566018506SEric Dumazet 	spin_lock_bh(&ip_ra_lock);
35643a951e9SEric Dumazet 	for (rap = &ip_ra_chain;
35743a951e9SEric Dumazet 	     (ra = rcu_dereference_protected(*rap,
35843a951e9SEric Dumazet 			lockdep_is_held(&ip_ra_lock))) != NULL;
35943a951e9SEric Dumazet 	     rap = &ra->next) {
3601da177e4SLinus Torvalds 		if (ra->sk == sk) {
3611da177e4SLinus Torvalds 			if (on) {
36266018506SEric Dumazet 				spin_unlock_bh(&ip_ra_lock);
3631da177e4SLinus Torvalds 				kfree(new_ra);
3641da177e4SLinus Torvalds 				return -EADDRINUSE;
3651da177e4SLinus Torvalds 			}
366592fcb9dSEric Dumazet 			/* dont let ip_call_ra_chain() use sk again */
367592fcb9dSEric Dumazet 			ra->sk = NULL;
3688e380f00SEric Dumazet 			RCU_INIT_POINTER(*rap, ra->next);
36966018506SEric Dumazet 			spin_unlock_bh(&ip_ra_lock);
3701da177e4SLinus Torvalds 
3711da177e4SLinus Torvalds 			if (ra->destructor)
3721da177e4SLinus Torvalds 				ra->destructor(sk);
373592fcb9dSEric Dumazet 			/*
374592fcb9dSEric Dumazet 			 * Delay sock_put(sk) and kfree(ra) after one rcu grace
375592fcb9dSEric Dumazet 			 * period. This guarantee ip_call_ra_chain() dont need
376592fcb9dSEric Dumazet 			 * to mess with socket refcounts.
377592fcb9dSEric Dumazet 			 */
378592fcb9dSEric Dumazet 			ra->saved_sk = sk;
379592fcb9dSEric Dumazet 			call_rcu(&ra->rcu, ip_ra_destroy_rcu);
3801da177e4SLinus Torvalds 			return 0;
3811da177e4SLinus Torvalds 		}
3821da177e4SLinus Torvalds 	}
38351456b29SIan Morris 	if (!new_ra) {
38466018506SEric Dumazet 		spin_unlock_bh(&ip_ra_lock);
3851da177e4SLinus Torvalds 		return -ENOBUFS;
3861da177e4SLinus Torvalds 	}
3871da177e4SLinus Torvalds 	new_ra->sk = sk;
3881da177e4SLinus Torvalds 	new_ra->destructor = destructor;
3891da177e4SLinus Torvalds 
3908e380f00SEric Dumazet 	RCU_INIT_POINTER(new_ra->next, ra);
39166018506SEric Dumazet 	rcu_assign_pointer(*rap, new_ra);
3921da177e4SLinus Torvalds 	sock_hold(sk);
39366018506SEric Dumazet 	spin_unlock_bh(&ip_ra_lock);
3941da177e4SLinus Torvalds 
3951da177e4SLinus Torvalds 	return 0;
3961da177e4SLinus Torvalds }
3971da177e4SLinus Torvalds 
3981da177e4SLinus Torvalds void ip_icmp_error(struct sock *sk, struct sk_buff *skb, int err,
39935986b32SAl Viro 		   __be16 port, u32 info, u8 *payload)
4001da177e4SLinus Torvalds {
4011da177e4SLinus Torvalds 	struct sock_exterr_skb *serr;
4021da177e4SLinus Torvalds 
4031da177e4SLinus Torvalds 	skb = skb_clone(skb, GFP_ATOMIC);
4041da177e4SLinus Torvalds 	if (!skb)
4051da177e4SLinus Torvalds 		return;
4061da177e4SLinus Torvalds 
4071da177e4SLinus Torvalds 	serr = SKB_EXT_ERR(skb);
4081da177e4SLinus Torvalds 	serr->ee.ee_errno = err;
4091da177e4SLinus Torvalds 	serr->ee.ee_origin = SO_EE_ORIGIN_ICMP;
41088c7664fSArnaldo Carvalho de Melo 	serr->ee.ee_type = icmp_hdr(skb)->type;
41188c7664fSArnaldo Carvalho de Melo 	serr->ee.ee_code = icmp_hdr(skb)->code;
4121da177e4SLinus Torvalds 	serr->ee.ee_pad = 0;
4131da177e4SLinus Torvalds 	serr->ee.ee_info = info;
4141da177e4SLinus Torvalds 	serr->ee.ee_data = 0;
41588c7664fSArnaldo Carvalho de Melo 	serr->addr_offset = (u8 *)&(((struct iphdr *)(icmp_hdr(skb) + 1))->daddr) -
416d56f90a7SArnaldo Carvalho de Melo 				   skb_network_header(skb);
4171da177e4SLinus Torvalds 	serr->port = port;
4181da177e4SLinus Torvalds 
41900db4124SIan Morris 	if (skb_pull(skb, payload - skb->data)) {
420bd82393cSArnaldo Carvalho de Melo 		skb_reset_transport_header(skb);
421bd82393cSArnaldo Carvalho de Melo 		if (sock_queue_err_skb(sk, skb) == 0)
422bd82393cSArnaldo Carvalho de Melo 			return;
423bd82393cSArnaldo Carvalho de Melo 	}
4241da177e4SLinus Torvalds 	kfree_skb(skb);
4251da177e4SLinus Torvalds }
4261da177e4SLinus Torvalds 
4270579016eSAl Viro void ip_local_error(struct sock *sk, int err, __be32 daddr, __be16 port, u32 info)
4281da177e4SLinus Torvalds {
4291da177e4SLinus Torvalds 	struct inet_sock *inet = inet_sk(sk);
4301da177e4SLinus Torvalds 	struct sock_exterr_skb *serr;
4311da177e4SLinus Torvalds 	struct iphdr *iph;
4321da177e4SLinus Torvalds 	struct sk_buff *skb;
4331da177e4SLinus Torvalds 
4341da177e4SLinus Torvalds 	if (!inet->recverr)
4351da177e4SLinus Torvalds 		return;
4361da177e4SLinus Torvalds 
4371da177e4SLinus Torvalds 	skb = alloc_skb(sizeof(struct iphdr), GFP_ATOMIC);
4381da177e4SLinus Torvalds 	if (!skb)
4391da177e4SLinus Torvalds 		return;
4401da177e4SLinus Torvalds 
4412ca9e6f2SArnaldo Carvalho de Melo 	skb_put(skb, sizeof(struct iphdr));
4422ca9e6f2SArnaldo Carvalho de Melo 	skb_reset_network_header(skb);
443eddc9ec5SArnaldo Carvalho de Melo 	iph = ip_hdr(skb);
4441da177e4SLinus Torvalds 	iph->daddr = daddr;
4451da177e4SLinus Torvalds 
4461da177e4SLinus Torvalds 	serr = SKB_EXT_ERR(skb);
4471da177e4SLinus Torvalds 	serr->ee.ee_errno = err;
4481da177e4SLinus Torvalds 	serr->ee.ee_origin = SO_EE_ORIGIN_LOCAL;
4491da177e4SLinus Torvalds 	serr->ee.ee_type = 0;
4501da177e4SLinus Torvalds 	serr->ee.ee_code = 0;
4511da177e4SLinus Torvalds 	serr->ee.ee_pad = 0;
4521da177e4SLinus Torvalds 	serr->ee.ee_info = info;
4531da177e4SLinus Torvalds 	serr->ee.ee_data = 0;
454d56f90a7SArnaldo Carvalho de Melo 	serr->addr_offset = (u8 *)&iph->daddr - skb_network_header(skb);
4551da177e4SLinus Torvalds 	serr->port = port;
4561da177e4SLinus Torvalds 
45727a884dcSArnaldo Carvalho de Melo 	__skb_pull(skb, skb_tail_pointer(skb) - skb->data);
458bd82393cSArnaldo Carvalho de Melo 	skb_reset_transport_header(skb);
4591da177e4SLinus Torvalds 
4601da177e4SLinus Torvalds 	if (sock_queue_err_skb(sk, skb))
4611da177e4SLinus Torvalds 		kfree_skb(skb);
4621da177e4SLinus Torvalds }
4631da177e4SLinus Torvalds 
46434b99df4SJulian Anastasov /* For some errors we have valid addr_offset even with zero payload and
46534b99df4SJulian Anastasov  * zero port. Also, addr_offset should be supported if port is set.
46634b99df4SJulian Anastasov  */
46734b99df4SJulian Anastasov static inline bool ipv4_datagram_support_addr(struct sock_exterr_skb *serr)
46834b99df4SJulian Anastasov {
46934b99df4SJulian Anastasov 	return serr->ee.ee_origin == SO_EE_ORIGIN_ICMP ||
47034b99df4SJulian Anastasov 	       serr->ee.ee_origin == SO_EE_ORIGIN_LOCAL || serr->port;
47134b99df4SJulian Anastasov }
47234b99df4SJulian Anastasov 
473c247f053SWillem de Bruijn /* IPv4 supports cmsg on all imcp errors and some timestamps
474c247f053SWillem de Bruijn  *
475c247f053SWillem de Bruijn  * Timestamp code paths do not initialize the fields expected by cmsg:
476c247f053SWillem de Bruijn  * the PKTINFO fields in skb->cb[]. Fill those in here.
477c247f053SWillem de Bruijn  */
478c247f053SWillem de Bruijn static bool ipv4_datagram_support_cmsg(const struct sock *sk,
479c247f053SWillem de Bruijn 				       struct sk_buff *skb,
480829ae9d6SWillem de Bruijn 				       int ee_origin)
481829ae9d6SWillem de Bruijn {
482c247f053SWillem de Bruijn 	struct in_pktinfo *info;
483829ae9d6SWillem de Bruijn 
484c247f053SWillem de Bruijn 	if (ee_origin == SO_EE_ORIGIN_ICMP)
485c247f053SWillem de Bruijn 		return true;
486c247f053SWillem de Bruijn 
487c247f053SWillem de Bruijn 	if (ee_origin == SO_EE_ORIGIN_LOCAL)
488c247f053SWillem de Bruijn 		return false;
489c247f053SWillem de Bruijn 
490c247f053SWillem de Bruijn 	/* Support IP_PKTINFO on tstamp packets if requested, to correlate
491*1862d620SWillem de Bruijn 	 * timestamp with egress dev. Not possible for packets without iif
492c247f053SWillem de Bruijn 	 * or without payload (SOF_TIMESTAMPING_OPT_TSONLY).
493c247f053SWillem de Bruijn 	 */
494*1862d620SWillem de Bruijn 	info = PKTINFO_SKB_CB(skb);
495*1862d620SWillem de Bruijn 	if (!(sk->sk_tsflags & SOF_TIMESTAMPING_OPT_CMSG) ||
496*1862d620SWillem de Bruijn 	    !info->ipi_ifindex)
497829ae9d6SWillem de Bruijn 		return false;
498829ae9d6SWillem de Bruijn 
499829ae9d6SWillem de Bruijn 	info->ipi_spec_dst.s_addr = ip_hdr(skb)->saddr;
500829ae9d6SWillem de Bruijn 	return true;
501829ae9d6SWillem de Bruijn }
502829ae9d6SWillem de Bruijn 
5031da177e4SLinus Torvalds /*
5041da177e4SLinus Torvalds  *	Handle MSG_ERRQUEUE
5051da177e4SLinus Torvalds  */
50685fbaa75SHannes Frederic Sowa int ip_recv_error(struct sock *sk, struct msghdr *msg, int len, int *addr_len)
5071da177e4SLinus Torvalds {
5081da177e4SLinus Torvalds 	struct sock_exterr_skb *serr;
509364a9e93SWillem de Bruijn 	struct sk_buff *skb;
510342dfc30SSteffen Hurrle 	DECLARE_SOCKADDR(struct sockaddr_in *, sin, msg->msg_name);
5111da177e4SLinus Torvalds 	struct {
5121da177e4SLinus Torvalds 		struct sock_extended_err ee;
5131da177e4SLinus Torvalds 		struct sockaddr_in	 offender;
5141da177e4SLinus Torvalds 	} errhdr;
5151da177e4SLinus Torvalds 	int err;
5161da177e4SLinus Torvalds 	int copied;
5171da177e4SLinus Torvalds 
5187ce875e5SWillem de Bruijn 	WARN_ON_ONCE(sk->sk_family == AF_INET6);
5197ce875e5SWillem de Bruijn 
5201da177e4SLinus Torvalds 	err = -EAGAIN;
521364a9e93SWillem de Bruijn 	skb = sock_dequeue_err_skb(sk);
52251456b29SIan Morris 	if (!skb)
5231da177e4SLinus Torvalds 		goto out;
5241da177e4SLinus Torvalds 
5251da177e4SLinus Torvalds 	copied = skb->len;
5261da177e4SLinus Torvalds 	if (copied > len) {
5271da177e4SLinus Torvalds 		msg->msg_flags |= MSG_TRUNC;
5281da177e4SLinus Torvalds 		copied = len;
5291da177e4SLinus Torvalds 	}
53051f3d02bSDavid S. Miller 	err = skb_copy_datagram_msg(skb, 0, msg, copied);
531960a2628SEric Dumazet 	if (unlikely(err)) {
532960a2628SEric Dumazet 		kfree_skb(skb);
533960a2628SEric Dumazet 		return err;
534960a2628SEric Dumazet 	}
5351da177e4SLinus Torvalds 	sock_recv_timestamp(msg, sk, skb);
5361da177e4SLinus Torvalds 
5371da177e4SLinus Torvalds 	serr = SKB_EXT_ERR(skb);
5381da177e4SLinus Torvalds 
53934b99df4SJulian Anastasov 	if (sin && ipv4_datagram_support_addr(serr)) {
5401da177e4SLinus Torvalds 		sin->sin_family = AF_INET;
541d56f90a7SArnaldo Carvalho de Melo 		sin->sin_addr.s_addr = *(__be32 *)(skb_network_header(skb) +
542d56f90a7SArnaldo Carvalho de Melo 						   serr->addr_offset);
5431da177e4SLinus Torvalds 		sin->sin_port = serr->port;
5441da177e4SLinus Torvalds 		memset(&sin->sin_zero, 0, sizeof(sin->sin_zero));
54585fbaa75SHannes Frederic Sowa 		*addr_len = sizeof(*sin);
5461da177e4SLinus Torvalds 	}
5471da177e4SLinus Torvalds 
5481da177e4SLinus Torvalds 	memcpy(&errhdr.ee, &serr->ee, sizeof(struct sock_extended_err));
5491da177e4SLinus Torvalds 	sin = &errhdr.offender;
550f812116bSWillem de Bruijn 	memset(sin, 0, sizeof(*sin));
551829ae9d6SWillem de Bruijn 
552c247f053SWillem de Bruijn 	if (ipv4_datagram_support_cmsg(sk, skb, serr->ee.ee_origin)) {
5531da177e4SLinus Torvalds 		sin->sin_family = AF_INET;
554eddc9ec5SArnaldo Carvalho de Melo 		sin->sin_addr.s_addr = ip_hdr(skb)->saddr;
555f812116bSWillem de Bruijn 		if (inet_sk(sk)->cmsg_flags)
5561da177e4SLinus Torvalds 			ip_cmsg_recv(msg, skb);
5571da177e4SLinus Torvalds 	}
5581da177e4SLinus Torvalds 
5591da177e4SLinus Torvalds 	put_cmsg(msg, SOL_IP, IP_RECVERR, sizeof(errhdr), &errhdr);
5601da177e4SLinus Torvalds 
5611da177e4SLinus Torvalds 	/* Now we could try to dump offended packet options */
5621da177e4SLinus Torvalds 
5631da177e4SLinus Torvalds 	msg->msg_flags |= MSG_ERRQUEUE;
5641da177e4SLinus Torvalds 	err = copied;
5651da177e4SLinus Torvalds 
566960a2628SEric Dumazet 	consume_skb(skb);
5671da177e4SLinus Torvalds out:
5681da177e4SLinus Torvalds 	return err;
5691da177e4SLinus Torvalds }
5701da177e4SLinus Torvalds 
5711da177e4SLinus Torvalds 
5721da177e4SLinus Torvalds /*
5734d52cfbeSEric Dumazet  *	Socket option code for IP. This is the end of the line after any
5744d52cfbeSEric Dumazet  *	TCP,UDP etc options on an IP socket.
5751da177e4SLinus Torvalds  */
576baf606d9SMarcelo Ricardo Leitner static bool setsockopt_needs_rtnl(int optname)
577baf606d9SMarcelo Ricardo Leitner {
578baf606d9SMarcelo Ricardo Leitner 	switch (optname) {
579baf606d9SMarcelo Ricardo Leitner 	case IP_ADD_MEMBERSHIP:
580baf606d9SMarcelo Ricardo Leitner 	case IP_ADD_SOURCE_MEMBERSHIP:
58154ff9ef3SMarcelo Ricardo Leitner 	case IP_BLOCK_SOURCE:
582baf606d9SMarcelo Ricardo Leitner 	case IP_DROP_MEMBERSHIP:
58354ff9ef3SMarcelo Ricardo Leitner 	case IP_DROP_SOURCE_MEMBERSHIP:
58454ff9ef3SMarcelo Ricardo Leitner 	case IP_MSFILTER:
58554ff9ef3SMarcelo Ricardo Leitner 	case IP_UNBLOCK_SOURCE:
58654ff9ef3SMarcelo Ricardo Leitner 	case MCAST_BLOCK_SOURCE:
58754ff9ef3SMarcelo Ricardo Leitner 	case MCAST_MSFILTER:
588baf606d9SMarcelo Ricardo Leitner 	case MCAST_JOIN_GROUP:
58954ff9ef3SMarcelo Ricardo Leitner 	case MCAST_JOIN_SOURCE_GROUP:
590baf606d9SMarcelo Ricardo Leitner 	case MCAST_LEAVE_GROUP:
59154ff9ef3SMarcelo Ricardo Leitner 	case MCAST_LEAVE_SOURCE_GROUP:
59254ff9ef3SMarcelo Ricardo Leitner 	case MCAST_UNBLOCK_SOURCE:
5931215e51eSWANG Cong 	case IP_ROUTER_ALERT:
594baf606d9SMarcelo Ricardo Leitner 		return true;
595baf606d9SMarcelo Ricardo Leitner 	}
596baf606d9SMarcelo Ricardo Leitner 	return false;
597baf606d9SMarcelo Ricardo Leitner }
5981da177e4SLinus Torvalds 
5993fdadf7dSDmitry Mishin static int do_ip_setsockopt(struct sock *sk, int level,
600b7058842SDavid S. Miller 			    int optname, char __user *optval, unsigned int optlen)
6011da177e4SLinus Torvalds {
6021da177e4SLinus Torvalds 	struct inet_sock *inet = inet_sk(sk);
603166b6b2dSNikolay Borisov 	struct net *net = sock_net(sk);
6041da177e4SLinus Torvalds 	int val = 0, err;
605baf606d9SMarcelo Ricardo Leitner 	bool needs_rtnl = setsockopt_needs_rtnl(optname);
6061da177e4SLinus Torvalds 
6070c9f79beSXi Wang 	switch (optname) {
6080c9f79beSXi Wang 	case IP_PKTINFO:
6090c9f79beSXi Wang 	case IP_RECVTTL:
6100c9f79beSXi Wang 	case IP_RECVOPTS:
6110c9f79beSXi Wang 	case IP_RECVTOS:
6120c9f79beSXi Wang 	case IP_RETOPTS:
6130c9f79beSXi Wang 	case IP_TOS:
6140c9f79beSXi Wang 	case IP_TTL:
6150c9f79beSXi Wang 	case IP_HDRINCL:
6160c9f79beSXi Wang 	case IP_MTU_DISCOVER:
6170c9f79beSXi Wang 	case IP_RECVERR:
6180c9f79beSXi Wang 	case IP_ROUTER_ALERT:
6190c9f79beSXi Wang 	case IP_FREEBIND:
6200c9f79beSXi Wang 	case IP_PASSSEC:
6210c9f79beSXi Wang 	case IP_TRANSPARENT:
6220c9f79beSXi Wang 	case IP_MINTTL:
6230c9f79beSXi Wang 	case IP_NODEFRAG:
62490c337daSEric Dumazet 	case IP_BIND_ADDRESS_NO_PORT:
6250c9f79beSXi Wang 	case IP_UNICAST_IF:
6260c9f79beSXi Wang 	case IP_MULTICAST_TTL:
6270c9f79beSXi Wang 	case IP_MULTICAST_ALL:
6280c9f79beSXi Wang 	case IP_MULTICAST_LOOP:
6290c9f79beSXi Wang 	case IP_RECVORIGDSTADDR:
630ad6f939aSTom Herbert 	case IP_CHECKSUM:
63170ecc248SWillem de Bruijn 	case IP_RECVFRAGSIZE:
6321da177e4SLinus Torvalds 		if (optlen >= sizeof(int)) {
6331da177e4SLinus Torvalds 			if (get_user(val, (int __user *) optval))
6341da177e4SLinus Torvalds 				return -EFAULT;
6351da177e4SLinus Torvalds 		} else if (optlen >= sizeof(char)) {
6361da177e4SLinus Torvalds 			unsigned char ucval;
6371da177e4SLinus Torvalds 
6381da177e4SLinus Torvalds 			if (get_user(ucval, (unsigned char __user *) optval))
6391da177e4SLinus Torvalds 				return -EFAULT;
6401da177e4SLinus Torvalds 			val = (int) ucval;
6411da177e4SLinus Torvalds 		}
6421da177e4SLinus Torvalds 	}
6431da177e4SLinus Torvalds 
6441da177e4SLinus Torvalds 	/* If optlen==0, it is equivalent to val == 0 */
6451da177e4SLinus Torvalds 
6466a9fb947SPavel Emelyanov 	if (ip_mroute_opt(optname))
6471da177e4SLinus Torvalds 		return ip_mroute_setsockopt(sk, optname, optval, optlen);
6481da177e4SLinus Torvalds 
6491da177e4SLinus Torvalds 	err = 0;
650baf606d9SMarcelo Ricardo Leitner 	if (needs_rtnl)
651baf606d9SMarcelo Ricardo Leitner 		rtnl_lock();
6521da177e4SLinus Torvalds 	lock_sock(sk);
6531da177e4SLinus Torvalds 
6541da177e4SLinus Torvalds 	switch (optname) {
6551da177e4SLinus Torvalds 	case IP_OPTIONS:
6561da177e4SLinus Torvalds 	{
657f6d8bd05SEric Dumazet 		struct ip_options_rcu *old, *opt = NULL;
658f6d8bd05SEric Dumazet 
65965a1c4ffSroel kluin 		if (optlen > 40)
6601da177e4SLinus Torvalds 			goto e_inval;
6613b1e0a65SYOSHIFUJI Hideaki 		err = ip_options_get_from_user(sock_net(sk), &opt,
662cb84663eSDenis V. Lunev 					       optval, optlen);
6631da177e4SLinus Torvalds 		if (err)
6641da177e4SLinus Torvalds 			break;
665f6d8bd05SEric Dumazet 		old = rcu_dereference_protected(inet->inet_opt,
6661e1d04e6SHannes Frederic Sowa 						lockdep_sock_is_held(sk));
667d83d8461SArnaldo Carvalho de Melo 		if (inet->is_icsk) {
668d83d8461SArnaldo Carvalho de Melo 			struct inet_connection_sock *icsk = inet_csk(sk);
669dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6)
6701da177e4SLinus Torvalds 			if (sk->sk_family == PF_INET ||
6711da177e4SLinus Torvalds 			    (!((1 << sk->sk_state) &
6721da177e4SLinus Torvalds 			       (TCPF_LISTEN | TCPF_CLOSE)) &&
673c720c7e8SEric Dumazet 			     inet->inet_daddr != LOOPBACK4_IPV6)) {
6741da177e4SLinus Torvalds #endif
675f6d8bd05SEric Dumazet 				if (old)
676f6d8bd05SEric Dumazet 					icsk->icsk_ext_hdr_len -= old->opt.optlen;
6771da177e4SLinus Torvalds 				if (opt)
678f6d8bd05SEric Dumazet 					icsk->icsk_ext_hdr_len += opt->opt.optlen;
679d83d8461SArnaldo Carvalho de Melo 				icsk->icsk_sync_mss(sk, icsk->icsk_pmtu_cookie);
680dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6)
6811da177e4SLinus Torvalds 			}
6821da177e4SLinus Torvalds #endif
6831da177e4SLinus Torvalds 		}
684f6d8bd05SEric Dumazet 		rcu_assign_pointer(inet->inet_opt, opt);
685f6d8bd05SEric Dumazet 		if (old)
686605b4afeSPaul E. McKenney 			kfree_rcu(old, rcu);
6871da177e4SLinus Torvalds 		break;
6881da177e4SLinus Torvalds 	}
6891da177e4SLinus Torvalds 	case IP_PKTINFO:
6901da177e4SLinus Torvalds 		if (val)
6911da177e4SLinus Torvalds 			inet->cmsg_flags |= IP_CMSG_PKTINFO;
6921da177e4SLinus Torvalds 		else
6931da177e4SLinus Torvalds 			inet->cmsg_flags &= ~IP_CMSG_PKTINFO;
6941da177e4SLinus Torvalds 		break;
6951da177e4SLinus Torvalds 	case IP_RECVTTL:
6961da177e4SLinus Torvalds 		if (val)
6971da177e4SLinus Torvalds 			inet->cmsg_flags |=  IP_CMSG_TTL;
6981da177e4SLinus Torvalds 		else
6991da177e4SLinus Torvalds 			inet->cmsg_flags &= ~IP_CMSG_TTL;
7001da177e4SLinus Torvalds 		break;
7011da177e4SLinus Torvalds 	case IP_RECVTOS:
7021da177e4SLinus Torvalds 		if (val)
7031da177e4SLinus Torvalds 			inet->cmsg_flags |=  IP_CMSG_TOS;
7041da177e4SLinus Torvalds 		else
7051da177e4SLinus Torvalds 			inet->cmsg_flags &= ~IP_CMSG_TOS;
7061da177e4SLinus Torvalds 		break;
7071da177e4SLinus Torvalds 	case IP_RECVOPTS:
7081da177e4SLinus Torvalds 		if (val)
7091da177e4SLinus Torvalds 			inet->cmsg_flags |=  IP_CMSG_RECVOPTS;
7101da177e4SLinus Torvalds 		else
7111da177e4SLinus Torvalds 			inet->cmsg_flags &= ~IP_CMSG_RECVOPTS;
7121da177e4SLinus Torvalds 		break;
7131da177e4SLinus Torvalds 	case IP_RETOPTS:
7141da177e4SLinus Torvalds 		if (val)
7151da177e4SLinus Torvalds 			inet->cmsg_flags |= IP_CMSG_RETOPTS;
7161da177e4SLinus Torvalds 		else
7171da177e4SLinus Torvalds 			inet->cmsg_flags &= ~IP_CMSG_RETOPTS;
7181da177e4SLinus Torvalds 		break;
7192c7946a7SCatherine Zhang 	case IP_PASSSEC:
7202c7946a7SCatherine Zhang 		if (val)
7212c7946a7SCatherine Zhang 			inet->cmsg_flags |= IP_CMSG_PASSSEC;
7222c7946a7SCatherine Zhang 		else
7232c7946a7SCatherine Zhang 			inet->cmsg_flags &= ~IP_CMSG_PASSSEC;
7242c7946a7SCatherine Zhang 		break;
725e8b2dfe9SBalazs Scheidler 	case IP_RECVORIGDSTADDR:
726e8b2dfe9SBalazs Scheidler 		if (val)
727e8b2dfe9SBalazs Scheidler 			inet->cmsg_flags |= IP_CMSG_ORIGDSTADDR;
728e8b2dfe9SBalazs Scheidler 		else
729e8b2dfe9SBalazs Scheidler 			inet->cmsg_flags &= ~IP_CMSG_ORIGDSTADDR;
730e8b2dfe9SBalazs Scheidler 		break;
731ad6f939aSTom Herbert 	case IP_CHECKSUM:
732ad6f939aSTom Herbert 		if (val) {
733ad6f939aSTom Herbert 			if (!(inet->cmsg_flags & IP_CMSG_CHECKSUM)) {
734ad6f939aSTom Herbert 				inet_inc_convert_csum(sk);
735ad6f939aSTom Herbert 				inet->cmsg_flags |= IP_CMSG_CHECKSUM;
736ad6f939aSTom Herbert 			}
737ad6f939aSTom Herbert 		} else {
738ad6f939aSTom Herbert 			if (inet->cmsg_flags & IP_CMSG_CHECKSUM) {
739ad6f939aSTom Herbert 				inet_dec_convert_csum(sk);
740ad6f939aSTom Herbert 				inet->cmsg_flags &= ~IP_CMSG_CHECKSUM;
741ad6f939aSTom Herbert 			}
742ad6f939aSTom Herbert 		}
743ad6f939aSTom Herbert 		break;
74470ecc248SWillem de Bruijn 	case IP_RECVFRAGSIZE:
74570ecc248SWillem de Bruijn 		if (sk->sk_type != SOCK_RAW && sk->sk_type != SOCK_DGRAM)
74670ecc248SWillem de Bruijn 			goto e_inval;
74770ecc248SWillem de Bruijn 		if (val)
74870ecc248SWillem de Bruijn 			inet->cmsg_flags |= IP_CMSG_RECVFRAGSIZE;
74970ecc248SWillem de Bruijn 		else
75070ecc248SWillem de Bruijn 			inet->cmsg_flags &= ~IP_CMSG_RECVFRAGSIZE;
75170ecc248SWillem de Bruijn 		break;
7521da177e4SLinus Torvalds 	case IP_TOS:	/* This sets both TOS and Precedence */
7531da177e4SLinus Torvalds 		if (sk->sk_type == SOCK_STREAM) {
7542c67e9acSMaciej Żenczykowski 			val &= ~INET_ECN_MASK;
7552c67e9acSMaciej Żenczykowski 			val |= inet->tos & INET_ECN_MASK;
7561da177e4SLinus Torvalds 		}
7571da177e4SLinus Torvalds 		if (inet->tos != val) {
7581da177e4SLinus Torvalds 			inet->tos = val;
7591da177e4SLinus Torvalds 			sk->sk_priority = rt_tos2priority(val);
7601da177e4SLinus Torvalds 			sk_dst_reset(sk);
7611da177e4SLinus Torvalds 		}
7621da177e4SLinus Torvalds 		break;
7631da177e4SLinus Torvalds 	case IP_TTL:
7641da177e4SLinus Torvalds 		if (optlen < 1)
7651da177e4SLinus Torvalds 			goto e_inval;
766c9be4a5cSCong Wang 		if (val != -1 && (val < 1 || val > 255))
7671da177e4SLinus Torvalds 			goto e_inval;
7681da177e4SLinus Torvalds 		inet->uc_ttl = val;
7691da177e4SLinus Torvalds 		break;
7701da177e4SLinus Torvalds 	case IP_HDRINCL:
7711da177e4SLinus Torvalds 		if (sk->sk_type != SOCK_RAW) {
7721da177e4SLinus Torvalds 			err = -ENOPROTOOPT;
7731da177e4SLinus Torvalds 			break;
7741da177e4SLinus Torvalds 		}
7751da177e4SLinus Torvalds 		inet->hdrincl = val ? 1 : 0;
7761da177e4SLinus Torvalds 		break;
7777b2ff18eSJiri Olsa 	case IP_NODEFRAG:
7787b2ff18eSJiri Olsa 		if (sk->sk_type != SOCK_RAW) {
7797b2ff18eSJiri Olsa 			err = -ENOPROTOOPT;
7807b2ff18eSJiri Olsa 			break;
7817b2ff18eSJiri Olsa 		}
7827b2ff18eSJiri Olsa 		inet->nodefrag = val ? 1 : 0;
7837b2ff18eSJiri Olsa 		break;
78490c337daSEric Dumazet 	case IP_BIND_ADDRESS_NO_PORT:
78590c337daSEric Dumazet 		inet->bind_address_no_port = val ? 1 : 0;
78690c337daSEric Dumazet 		break;
7871da177e4SLinus Torvalds 	case IP_MTU_DISCOVER:
7881b346576SHannes Frederic Sowa 		if (val < IP_PMTUDISC_DONT || val > IP_PMTUDISC_OMIT)
7891da177e4SLinus Torvalds 			goto e_inval;
7901da177e4SLinus Torvalds 		inet->pmtudisc = val;
7911da177e4SLinus Torvalds 		break;
7921da177e4SLinus Torvalds 	case IP_RECVERR:
7931da177e4SLinus Torvalds 		inet->recverr = !!val;
7941da177e4SLinus Torvalds 		if (!val)
7951da177e4SLinus Torvalds 			skb_queue_purge(&sk->sk_error_queue);
7961da177e4SLinus Torvalds 		break;
7971da177e4SLinus Torvalds 	case IP_MULTICAST_TTL:
7981da177e4SLinus Torvalds 		if (sk->sk_type == SOCK_STREAM)
7991da177e4SLinus Torvalds 			goto e_inval;
8001da177e4SLinus Torvalds 		if (optlen < 1)
8011da177e4SLinus Torvalds 			goto e_inval;
8021da177e4SLinus Torvalds 		if (val == -1)
8031da177e4SLinus Torvalds 			val = 1;
8041da177e4SLinus Torvalds 		if (val < 0 || val > 255)
8051da177e4SLinus Torvalds 			goto e_inval;
8061da177e4SLinus Torvalds 		inet->mc_ttl = val;
8071da177e4SLinus Torvalds 		break;
8081da177e4SLinus Torvalds 	case IP_MULTICAST_LOOP:
8091da177e4SLinus Torvalds 		if (optlen < 1)
8101da177e4SLinus Torvalds 			goto e_inval;
8111da177e4SLinus Torvalds 		inet->mc_loop = !!val;
8121da177e4SLinus Torvalds 		break;
81376e21053SErich E. Hoover 	case IP_UNICAST_IF:
81476e21053SErich E. Hoover 	{
81576e21053SErich E. Hoover 		struct net_device *dev = NULL;
81676e21053SErich E. Hoover 		int ifindex;
81776e21053SErich E. Hoover 
81876e21053SErich E. Hoover 		if (optlen != sizeof(int))
81976e21053SErich E. Hoover 			goto e_inval;
82076e21053SErich E. Hoover 
82176e21053SErich E. Hoover 		ifindex = (__force int)ntohl((__force __be32)val);
82276e21053SErich E. Hoover 		if (ifindex == 0) {
82376e21053SErich E. Hoover 			inet->uc_index = 0;
82476e21053SErich E. Hoover 			err = 0;
82576e21053SErich E. Hoover 			break;
82676e21053SErich E. Hoover 		}
82776e21053SErich E. Hoover 
82876e21053SErich E. Hoover 		dev = dev_get_by_index(sock_net(sk), ifindex);
82976e21053SErich E. Hoover 		err = -EADDRNOTAVAIL;
83076e21053SErich E. Hoover 		if (!dev)
83176e21053SErich E. Hoover 			break;
83276e21053SErich E. Hoover 		dev_put(dev);
83376e21053SErich E. Hoover 
83476e21053SErich E. Hoover 		err = -EINVAL;
83576e21053SErich E. Hoover 		if (sk->sk_bound_dev_if)
83676e21053SErich E. Hoover 			break;
83776e21053SErich E. Hoover 
83876e21053SErich E. Hoover 		inet->uc_index = ifindex;
83976e21053SErich E. Hoover 		err = 0;
84076e21053SErich E. Hoover 		break;
84176e21053SErich E. Hoover 	}
8421da177e4SLinus Torvalds 	case IP_MULTICAST_IF:
8431da177e4SLinus Torvalds 	{
8441da177e4SLinus Torvalds 		struct ip_mreqn mreq;
8451da177e4SLinus Torvalds 		struct net_device *dev = NULL;
8467bb387c5SDavid Ahern 		int midx;
8471da177e4SLinus Torvalds 
8481da177e4SLinus Torvalds 		if (sk->sk_type == SOCK_STREAM)
8491da177e4SLinus Torvalds 			goto e_inval;
8501da177e4SLinus Torvalds 		/*
8511da177e4SLinus Torvalds 		 *	Check the arguments are allowable
8521da177e4SLinus Torvalds 		 */
8531da177e4SLinus Torvalds 
8540915921bSShan Wei 		if (optlen < sizeof(struct in_addr))
8550915921bSShan Wei 			goto e_inval;
8560915921bSShan Wei 
8571da177e4SLinus Torvalds 		err = -EFAULT;
8581da177e4SLinus Torvalds 		if (optlen >= sizeof(struct ip_mreqn)) {
8591da177e4SLinus Torvalds 			if (copy_from_user(&mreq, optval, sizeof(mreq)))
8601da177e4SLinus Torvalds 				break;
8611da177e4SLinus Torvalds 		} else {
8621da177e4SLinus Torvalds 			memset(&mreq, 0, sizeof(mreq));
8633a084ddbSJiri Pirko 			if (optlen >= sizeof(struct ip_mreq)) {
8643a084ddbSJiri Pirko 				if (copy_from_user(&mreq, optval,
8653a084ddbSJiri Pirko 						   sizeof(struct ip_mreq)))
8663a084ddbSJiri Pirko 					break;
8673a084ddbSJiri Pirko 			} else if (optlen >= sizeof(struct in_addr)) {
8683a084ddbSJiri Pirko 				if (copy_from_user(&mreq.imr_address, optval,
8694d52cfbeSEric Dumazet 						   sizeof(struct in_addr)))
8701da177e4SLinus Torvalds 					break;
8711da177e4SLinus Torvalds 			}
8723a084ddbSJiri Pirko 		}
8731da177e4SLinus Torvalds 
8741da177e4SLinus Torvalds 		if (!mreq.imr_ifindex) {
875e6f1cebfSAl Viro 			if (mreq.imr_address.s_addr == htonl(INADDR_ANY)) {
8761da177e4SLinus Torvalds 				inet->mc_index = 0;
8771da177e4SLinus Torvalds 				inet->mc_addr  = 0;
8781da177e4SLinus Torvalds 				err = 0;
8791da177e4SLinus Torvalds 				break;
8801da177e4SLinus Torvalds 			}
8813b1e0a65SYOSHIFUJI Hideaki 			dev = ip_dev_find(sock_net(sk), mreq.imr_address.s_addr);
88255b80503SEric Dumazet 			if (dev)
8831da177e4SLinus Torvalds 				mreq.imr_ifindex = dev->ifindex;
8841da177e4SLinus Torvalds 		} else
88555b80503SEric Dumazet 			dev = dev_get_by_index(sock_net(sk), mreq.imr_ifindex);
8861da177e4SLinus Torvalds 
8871da177e4SLinus Torvalds 
8881da177e4SLinus Torvalds 		err = -EADDRNOTAVAIL;
8891da177e4SLinus Torvalds 		if (!dev)
8901da177e4SLinus Torvalds 			break;
8917bb387c5SDavid Ahern 
8927bb387c5SDavid Ahern 		midx = l3mdev_master_ifindex(dev);
8937bb387c5SDavid Ahern 
89455b80503SEric Dumazet 		dev_put(dev);
8951da177e4SLinus Torvalds 
8961da177e4SLinus Torvalds 		err = -EINVAL;
8971da177e4SLinus Torvalds 		if (sk->sk_bound_dev_if &&
8987bb387c5SDavid Ahern 		    mreq.imr_ifindex != sk->sk_bound_dev_if &&
8997bb387c5SDavid Ahern 		    (!midx || midx != sk->sk_bound_dev_if))
9001da177e4SLinus Torvalds 			break;
9011da177e4SLinus Torvalds 
9021da177e4SLinus Torvalds 		inet->mc_index = mreq.imr_ifindex;
9031da177e4SLinus Torvalds 		inet->mc_addr  = mreq.imr_address.s_addr;
9041da177e4SLinus Torvalds 		err = 0;
9051da177e4SLinus Torvalds 		break;
9061da177e4SLinus Torvalds 	}
9071da177e4SLinus Torvalds 
9081da177e4SLinus Torvalds 	case IP_ADD_MEMBERSHIP:
9091da177e4SLinus Torvalds 	case IP_DROP_MEMBERSHIP:
9101da177e4SLinus Torvalds 	{
9111da177e4SLinus Torvalds 		struct ip_mreqn mreq;
9121da177e4SLinus Torvalds 
913a96fb49bSFlavio Leitner 		err = -EPROTO;
914a96fb49bSFlavio Leitner 		if (inet_sk(sk)->is_icsk)
915a96fb49bSFlavio Leitner 			break;
916a96fb49bSFlavio Leitner 
9171da177e4SLinus Torvalds 		if (optlen < sizeof(struct ip_mreq))
9181da177e4SLinus Torvalds 			goto e_inval;
9191da177e4SLinus Torvalds 		err = -EFAULT;
9201da177e4SLinus Torvalds 		if (optlen >= sizeof(struct ip_mreqn)) {
9211da177e4SLinus Torvalds 			if (copy_from_user(&mreq, optval, sizeof(mreq)))
9221da177e4SLinus Torvalds 				break;
9231da177e4SLinus Torvalds 		} else {
9241da177e4SLinus Torvalds 			memset(&mreq, 0, sizeof(mreq));
9251da177e4SLinus Torvalds 			if (copy_from_user(&mreq, optval, sizeof(struct ip_mreq)))
9261da177e4SLinus Torvalds 				break;
9271da177e4SLinus Torvalds 		}
9281da177e4SLinus Torvalds 
9291da177e4SLinus Torvalds 		if (optname == IP_ADD_MEMBERSHIP)
93054ff9ef3SMarcelo Ricardo Leitner 			err = ip_mc_join_group(sk, &mreq);
9311da177e4SLinus Torvalds 		else
93254ff9ef3SMarcelo Ricardo Leitner 			err = ip_mc_leave_group(sk, &mreq);
9331da177e4SLinus Torvalds 		break;
9341da177e4SLinus Torvalds 	}
9351da177e4SLinus Torvalds 	case IP_MSFILTER:
9361da177e4SLinus Torvalds 	{
9371da177e4SLinus Torvalds 		struct ip_msfilter *msf;
9381da177e4SLinus Torvalds 
9391da177e4SLinus Torvalds 		if (optlen < IP_MSFILTER_SIZE(0))
9401da177e4SLinus Torvalds 			goto e_inval;
9411da177e4SLinus Torvalds 		if (optlen > sysctl_optmem_max) {
9421da177e4SLinus Torvalds 			err = -ENOBUFS;
9431da177e4SLinus Torvalds 			break;
9441da177e4SLinus Torvalds 		}
9458b3a7005SKris Katterjohn 		msf = kmalloc(optlen, GFP_KERNEL);
946cfcabdccSStephen Hemminger 		if (!msf) {
9471da177e4SLinus Torvalds 			err = -ENOBUFS;
9481da177e4SLinus Torvalds 			break;
9491da177e4SLinus Torvalds 		}
9501da177e4SLinus Torvalds 		err = -EFAULT;
9511da177e4SLinus Torvalds 		if (copy_from_user(msf, optval, optlen)) {
9521da177e4SLinus Torvalds 			kfree(msf);
9531da177e4SLinus Torvalds 			break;
9541da177e4SLinus Torvalds 		}
9551da177e4SLinus Torvalds 		/* numsrc >= (1G-4) overflow in 32 bits */
9561da177e4SLinus Torvalds 		if (msf->imsf_numsrc >= 0x3ffffffcU ||
957166b6b2dSNikolay Borisov 		    msf->imsf_numsrc > net->ipv4.sysctl_igmp_max_msf) {
9581da177e4SLinus Torvalds 			kfree(msf);
9591da177e4SLinus Torvalds 			err = -ENOBUFS;
9601da177e4SLinus Torvalds 			break;
9611da177e4SLinus Torvalds 		}
9621da177e4SLinus Torvalds 		if (IP_MSFILTER_SIZE(msf->imsf_numsrc) > optlen) {
9631da177e4SLinus Torvalds 			kfree(msf);
9641da177e4SLinus Torvalds 			err = -EINVAL;
9651da177e4SLinus Torvalds 			break;
9661da177e4SLinus Torvalds 		}
9671da177e4SLinus Torvalds 		err = ip_mc_msfilter(sk, msf, 0);
9681da177e4SLinus Torvalds 		kfree(msf);
9691da177e4SLinus Torvalds 		break;
9701da177e4SLinus Torvalds 	}
9711da177e4SLinus Torvalds 	case IP_BLOCK_SOURCE:
9721da177e4SLinus Torvalds 	case IP_UNBLOCK_SOURCE:
9731da177e4SLinus Torvalds 	case IP_ADD_SOURCE_MEMBERSHIP:
9741da177e4SLinus Torvalds 	case IP_DROP_SOURCE_MEMBERSHIP:
9751da177e4SLinus Torvalds 	{
9761da177e4SLinus Torvalds 		struct ip_mreq_source mreqs;
9771da177e4SLinus Torvalds 		int omode, add;
9781da177e4SLinus Torvalds 
9791da177e4SLinus Torvalds 		if (optlen != sizeof(struct ip_mreq_source))
9801da177e4SLinus Torvalds 			goto e_inval;
9811da177e4SLinus Torvalds 		if (copy_from_user(&mreqs, optval, sizeof(mreqs))) {
9821da177e4SLinus Torvalds 			err = -EFAULT;
9831da177e4SLinus Torvalds 			break;
9841da177e4SLinus Torvalds 		}
9851da177e4SLinus Torvalds 		if (optname == IP_BLOCK_SOURCE) {
9861da177e4SLinus Torvalds 			omode = MCAST_EXCLUDE;
9871da177e4SLinus Torvalds 			add = 1;
9881da177e4SLinus Torvalds 		} else if (optname == IP_UNBLOCK_SOURCE) {
9891da177e4SLinus Torvalds 			omode = MCAST_EXCLUDE;
9901da177e4SLinus Torvalds 			add = 0;
9911da177e4SLinus Torvalds 		} else if (optname == IP_ADD_SOURCE_MEMBERSHIP) {
9921da177e4SLinus Torvalds 			struct ip_mreqn mreq;
9931da177e4SLinus Torvalds 
9941da177e4SLinus Torvalds 			mreq.imr_multiaddr.s_addr = mreqs.imr_multiaddr;
9951da177e4SLinus Torvalds 			mreq.imr_address.s_addr = mreqs.imr_interface;
9961da177e4SLinus Torvalds 			mreq.imr_ifindex = 0;
99754ff9ef3SMarcelo Ricardo Leitner 			err = ip_mc_join_group(sk, &mreq);
9988cdaaa15SDavid L Stevens 			if (err && err != -EADDRINUSE)
9991da177e4SLinus Torvalds 				break;
10001da177e4SLinus Torvalds 			omode = MCAST_INCLUDE;
10011da177e4SLinus Torvalds 			add = 1;
10021da177e4SLinus Torvalds 		} else /* IP_DROP_SOURCE_MEMBERSHIP */ {
10031da177e4SLinus Torvalds 			omode = MCAST_INCLUDE;
10041da177e4SLinus Torvalds 			add = 0;
10051da177e4SLinus Torvalds 		}
10061da177e4SLinus Torvalds 		err = ip_mc_source(add, omode, sk, &mreqs, 0);
10071da177e4SLinus Torvalds 		break;
10081da177e4SLinus Torvalds 	}
10091da177e4SLinus Torvalds 	case MCAST_JOIN_GROUP:
10101da177e4SLinus Torvalds 	case MCAST_LEAVE_GROUP:
10111da177e4SLinus Torvalds 	{
10121da177e4SLinus Torvalds 		struct group_req greq;
10131da177e4SLinus Torvalds 		struct sockaddr_in *psin;
10141da177e4SLinus Torvalds 		struct ip_mreqn mreq;
10151da177e4SLinus Torvalds 
10161da177e4SLinus Torvalds 		if (optlen < sizeof(struct group_req))
10171da177e4SLinus Torvalds 			goto e_inval;
10181da177e4SLinus Torvalds 		err = -EFAULT;
10191da177e4SLinus Torvalds 		if (copy_from_user(&greq, optval, sizeof(greq)))
10201da177e4SLinus Torvalds 			break;
10211da177e4SLinus Torvalds 		psin = (struct sockaddr_in *)&greq.gr_group;
10221da177e4SLinus Torvalds 		if (psin->sin_family != AF_INET)
10231da177e4SLinus Torvalds 			goto e_inval;
10241da177e4SLinus Torvalds 		memset(&mreq, 0, sizeof(mreq));
10251da177e4SLinus Torvalds 		mreq.imr_multiaddr = psin->sin_addr;
10261da177e4SLinus Torvalds 		mreq.imr_ifindex = greq.gr_interface;
10271da177e4SLinus Torvalds 
10281da177e4SLinus Torvalds 		if (optname == MCAST_JOIN_GROUP)
102954ff9ef3SMarcelo Ricardo Leitner 			err = ip_mc_join_group(sk, &mreq);
10301da177e4SLinus Torvalds 		else
103154ff9ef3SMarcelo Ricardo Leitner 			err = ip_mc_leave_group(sk, &mreq);
10321da177e4SLinus Torvalds 		break;
10331da177e4SLinus Torvalds 	}
10341da177e4SLinus Torvalds 	case MCAST_JOIN_SOURCE_GROUP:
10351da177e4SLinus Torvalds 	case MCAST_LEAVE_SOURCE_GROUP:
10361da177e4SLinus Torvalds 	case MCAST_BLOCK_SOURCE:
10371da177e4SLinus Torvalds 	case MCAST_UNBLOCK_SOURCE:
10381da177e4SLinus Torvalds 	{
10391da177e4SLinus Torvalds 		struct group_source_req greqs;
10401da177e4SLinus Torvalds 		struct ip_mreq_source mreqs;
10411da177e4SLinus Torvalds 		struct sockaddr_in *psin;
10421da177e4SLinus Torvalds 		int omode, add;
10431da177e4SLinus Torvalds 
10441da177e4SLinus Torvalds 		if (optlen != sizeof(struct group_source_req))
10451da177e4SLinus Torvalds 			goto e_inval;
10461da177e4SLinus Torvalds 		if (copy_from_user(&greqs, optval, sizeof(greqs))) {
10471da177e4SLinus Torvalds 			err = -EFAULT;
10481da177e4SLinus Torvalds 			break;
10491da177e4SLinus Torvalds 		}
10501da177e4SLinus Torvalds 		if (greqs.gsr_group.ss_family != AF_INET ||
10511da177e4SLinus Torvalds 		    greqs.gsr_source.ss_family != AF_INET) {
10521da177e4SLinus Torvalds 			err = -EADDRNOTAVAIL;
10531da177e4SLinus Torvalds 			break;
10541da177e4SLinus Torvalds 		}
10551da177e4SLinus Torvalds 		psin = (struct sockaddr_in *)&greqs.gsr_group;
10561da177e4SLinus Torvalds 		mreqs.imr_multiaddr = psin->sin_addr.s_addr;
10571da177e4SLinus Torvalds 		psin = (struct sockaddr_in *)&greqs.gsr_source;
10581da177e4SLinus Torvalds 		mreqs.imr_sourceaddr = psin->sin_addr.s_addr;
10591da177e4SLinus Torvalds 		mreqs.imr_interface = 0; /* use index for mc_source */
10601da177e4SLinus Torvalds 
10611da177e4SLinus Torvalds 		if (optname == MCAST_BLOCK_SOURCE) {
10621da177e4SLinus Torvalds 			omode = MCAST_EXCLUDE;
10631da177e4SLinus Torvalds 			add = 1;
10641da177e4SLinus Torvalds 		} else if (optname == MCAST_UNBLOCK_SOURCE) {
10651da177e4SLinus Torvalds 			omode = MCAST_EXCLUDE;
10661da177e4SLinus Torvalds 			add = 0;
10671da177e4SLinus Torvalds 		} else if (optname == MCAST_JOIN_SOURCE_GROUP) {
10681da177e4SLinus Torvalds 			struct ip_mreqn mreq;
10691da177e4SLinus Torvalds 
10701da177e4SLinus Torvalds 			psin = (struct sockaddr_in *)&greqs.gsr_group;
10711da177e4SLinus Torvalds 			mreq.imr_multiaddr = psin->sin_addr;
10721da177e4SLinus Torvalds 			mreq.imr_address.s_addr = 0;
10731da177e4SLinus Torvalds 			mreq.imr_ifindex = greqs.gsr_interface;
107454ff9ef3SMarcelo Ricardo Leitner 			err = ip_mc_join_group(sk, &mreq);
10758cdaaa15SDavid L Stevens 			if (err && err != -EADDRINUSE)
10761da177e4SLinus Torvalds 				break;
10771da177e4SLinus Torvalds 			greqs.gsr_interface = mreq.imr_ifindex;
10781da177e4SLinus Torvalds 			omode = MCAST_INCLUDE;
10791da177e4SLinus Torvalds 			add = 1;
10801da177e4SLinus Torvalds 		} else /* MCAST_LEAVE_SOURCE_GROUP */ {
10811da177e4SLinus Torvalds 			omode = MCAST_INCLUDE;
10821da177e4SLinus Torvalds 			add = 0;
10831da177e4SLinus Torvalds 		}
10841da177e4SLinus Torvalds 		err = ip_mc_source(add, omode, sk, &mreqs,
10851da177e4SLinus Torvalds 				   greqs.gsr_interface);
10861da177e4SLinus Torvalds 		break;
10871da177e4SLinus Torvalds 	}
10881da177e4SLinus Torvalds 	case MCAST_MSFILTER:
10891da177e4SLinus Torvalds 	{
10901da177e4SLinus Torvalds 		struct sockaddr_in *psin;
10911da177e4SLinus Torvalds 		struct ip_msfilter *msf = NULL;
10921da177e4SLinus Torvalds 		struct group_filter *gsf = NULL;
10931da177e4SLinus Torvalds 		int msize, i, ifindex;
10941da177e4SLinus Torvalds 
10951da177e4SLinus Torvalds 		if (optlen < GROUP_FILTER_SIZE(0))
10961da177e4SLinus Torvalds 			goto e_inval;
10971da177e4SLinus Torvalds 		if (optlen > sysctl_optmem_max) {
10981da177e4SLinus Torvalds 			err = -ENOBUFS;
10991da177e4SLinus Torvalds 			break;
11001da177e4SLinus Torvalds 		}
11018b3a7005SKris Katterjohn 		gsf = kmalloc(optlen, GFP_KERNEL);
1102cfcabdccSStephen Hemminger 		if (!gsf) {
11031da177e4SLinus Torvalds 			err = -ENOBUFS;
11041da177e4SLinus Torvalds 			break;
11051da177e4SLinus Torvalds 		}
11061da177e4SLinus Torvalds 		err = -EFAULT;
11074d52cfbeSEric Dumazet 		if (copy_from_user(gsf, optval, optlen))
11081da177e4SLinus Torvalds 			goto mc_msf_out;
11094d52cfbeSEric Dumazet 
11101da177e4SLinus Torvalds 		/* numsrc >= (4G-140)/128 overflow in 32 bits */
11111da177e4SLinus Torvalds 		if (gsf->gf_numsrc >= 0x1ffffff ||
1112166b6b2dSNikolay Borisov 		    gsf->gf_numsrc > net->ipv4.sysctl_igmp_max_msf) {
11131da177e4SLinus Torvalds 			err = -ENOBUFS;
11141da177e4SLinus Torvalds 			goto mc_msf_out;
11151da177e4SLinus Torvalds 		}
11161da177e4SLinus Torvalds 		if (GROUP_FILTER_SIZE(gsf->gf_numsrc) > optlen) {
11171da177e4SLinus Torvalds 			err = -EINVAL;
11181da177e4SLinus Torvalds 			goto mc_msf_out;
11191da177e4SLinus Torvalds 		}
11201da177e4SLinus Torvalds 		msize = IP_MSFILTER_SIZE(gsf->gf_numsrc);
11218b3a7005SKris Katterjohn 		msf = kmalloc(msize, GFP_KERNEL);
1122cfcabdccSStephen Hemminger 		if (!msf) {
11231da177e4SLinus Torvalds 			err = -ENOBUFS;
11241da177e4SLinus Torvalds 			goto mc_msf_out;
11251da177e4SLinus Torvalds 		}
11261da177e4SLinus Torvalds 		ifindex = gsf->gf_interface;
11271da177e4SLinus Torvalds 		psin = (struct sockaddr_in *)&gsf->gf_group;
11281da177e4SLinus Torvalds 		if (psin->sin_family != AF_INET) {
11291da177e4SLinus Torvalds 			err = -EADDRNOTAVAIL;
11301da177e4SLinus Torvalds 			goto mc_msf_out;
11311da177e4SLinus Torvalds 		}
11321da177e4SLinus Torvalds 		msf->imsf_multiaddr = psin->sin_addr.s_addr;
11331da177e4SLinus Torvalds 		msf->imsf_interface = 0;
11341da177e4SLinus Torvalds 		msf->imsf_fmode = gsf->gf_fmode;
11351da177e4SLinus Torvalds 		msf->imsf_numsrc = gsf->gf_numsrc;
11361da177e4SLinus Torvalds 		err = -EADDRNOTAVAIL;
11371da177e4SLinus Torvalds 		for (i = 0; i < gsf->gf_numsrc; ++i) {
11381da177e4SLinus Torvalds 			psin = (struct sockaddr_in *)&gsf->gf_slist[i];
11391da177e4SLinus Torvalds 
11401da177e4SLinus Torvalds 			if (psin->sin_family != AF_INET)
11411da177e4SLinus Torvalds 				goto mc_msf_out;
11421da177e4SLinus Torvalds 			msf->imsf_slist[i] = psin->sin_addr.s_addr;
11431da177e4SLinus Torvalds 		}
11441da177e4SLinus Torvalds 		kfree(gsf);
11451da177e4SLinus Torvalds 		gsf = NULL;
11461da177e4SLinus Torvalds 
11471da177e4SLinus Torvalds 		err = ip_mc_msfilter(sk, msf, ifindex);
11481da177e4SLinus Torvalds mc_msf_out:
11491da177e4SLinus Torvalds 		kfree(msf);
11501da177e4SLinus Torvalds 		kfree(gsf);
11511da177e4SLinus Torvalds 		break;
11521da177e4SLinus Torvalds 	}
1153f771bef9SNivedita Singhvi 	case IP_MULTICAST_ALL:
1154f771bef9SNivedita Singhvi 		if (optlen < 1)
1155f771bef9SNivedita Singhvi 			goto e_inval;
1156f771bef9SNivedita Singhvi 		if (val != 0 && val != 1)
1157f771bef9SNivedita Singhvi 			goto e_inval;
1158f771bef9SNivedita Singhvi 		inet->mc_all = val;
1159f771bef9SNivedita Singhvi 		break;
11601da177e4SLinus Torvalds 	case IP_ROUTER_ALERT:
11611da177e4SLinus Torvalds 		err = ip_ra_control(sk, val ? 1 : 0, NULL);
11621da177e4SLinus Torvalds 		break;
11631da177e4SLinus Torvalds 
11641da177e4SLinus Torvalds 	case IP_FREEBIND:
11651da177e4SLinus Torvalds 		if (optlen < 1)
11661da177e4SLinus Torvalds 			goto e_inval;
11671da177e4SLinus Torvalds 		inet->freebind = !!val;
11681da177e4SLinus Torvalds 		break;
11691da177e4SLinus Torvalds 
11701da177e4SLinus Torvalds 	case IP_IPSEC_POLICY:
11711da177e4SLinus Torvalds 	case IP_XFRM_POLICY:
11726fc0b4a7SHerbert Xu 		err = -EPERM;
117352e804c6SEric W. Biederman 		if (!ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN))
11746fc0b4a7SHerbert Xu 			break;
11751da177e4SLinus Torvalds 		err = xfrm_user_policy(sk, optname, optval, optlen);
11761da177e4SLinus Torvalds 		break;
11771da177e4SLinus Torvalds 
1178f5715aeaSKOVACS Krisztian 	case IP_TRANSPARENT:
117952e804c6SEric W. Biederman 		if (!!val && !ns_capable(sock_net(sk)->user_ns, CAP_NET_RAW) &&
118052e804c6SEric W. Biederman 		    !ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN)) {
1181f5715aeaSKOVACS Krisztian 			err = -EPERM;
1182f5715aeaSKOVACS Krisztian 			break;
1183f5715aeaSKOVACS Krisztian 		}
1184f5715aeaSKOVACS Krisztian 		if (optlen < 1)
1185f5715aeaSKOVACS Krisztian 			goto e_inval;
1186f5715aeaSKOVACS Krisztian 		inet->transparent = !!val;
1187f5715aeaSKOVACS Krisztian 		break;
1188f5715aeaSKOVACS Krisztian 
1189d218d111SStephen Hemminger 	case IP_MINTTL:
1190d218d111SStephen Hemminger 		if (optlen < 1)
1191d218d111SStephen Hemminger 			goto e_inval;
1192d218d111SStephen Hemminger 		if (val < 0 || val > 255)
1193d218d111SStephen Hemminger 			goto e_inval;
1194d218d111SStephen Hemminger 		inet->min_ttl = val;
1195d218d111SStephen Hemminger 		break;
1196d218d111SStephen Hemminger 
11971da177e4SLinus Torvalds 	default:
11981da177e4SLinus Torvalds 		err = -ENOPROTOOPT;
11991da177e4SLinus Torvalds 		break;
12001da177e4SLinus Torvalds 	}
12011da177e4SLinus Torvalds 	release_sock(sk);
1202baf606d9SMarcelo Ricardo Leitner 	if (needs_rtnl)
1203baf606d9SMarcelo Ricardo Leitner 		rtnl_unlock();
12041da177e4SLinus Torvalds 	return err;
12051da177e4SLinus Torvalds 
12061da177e4SLinus Torvalds e_inval:
12071da177e4SLinus Torvalds 	release_sock(sk);
1208baf606d9SMarcelo Ricardo Leitner 	if (needs_rtnl)
1209baf606d9SMarcelo Ricardo Leitner 		rtnl_unlock();
12101da177e4SLinus Torvalds 	return -EINVAL;
12111da177e4SLinus Torvalds }
12121da177e4SLinus Torvalds 
1213f84af32cSEric Dumazet /**
1214829ae9d6SWillem de Bruijn  * ipv4_pktinfo_prepare - transfer some info from rtable to skb
1215f84af32cSEric Dumazet  * @sk: socket
1216f84af32cSEric Dumazet  * @skb: buffer
1217f84af32cSEric Dumazet  *
121835ebf65eSDavid S. Miller  * To support IP_CMSG_PKTINFO option, we store rt_iif and specific
121935ebf65eSDavid S. Miller  * destination in skb->cb[] before dst drop.
12208e3bff96Sstephen hemminger  * This way, receiver doesn't make cache line misses to read rtable.
1221f84af32cSEric Dumazet  */
1222fbf8866dSShawn Bohrer void ipv4_pktinfo_prepare(const struct sock *sk, struct sk_buff *skb)
1223f84af32cSEric Dumazet {
1224d826eb14SEric Dumazet 	struct in_pktinfo *pktinfo = PKTINFO_SKB_CB(skb);
12254b261c75SHannes Frederic Sowa 	bool prepare = (inet_sk(sk)->cmsg_flags & IP_CMSG_PKTINFO) ||
12264b261c75SHannes Frederic Sowa 		       ipv6_sk_rxinfo(sk);
1227d826eb14SEric Dumazet 
12284b261c75SHannes Frederic Sowa 	if (prepare && skb_rtable(skb)) {
12290b922b7aSDavid Ahern 		/* skb->cb is overloaded: prior to this point it is IP{6}CB
12300b922b7aSDavid Ahern 		 * which has interface index (iif) as the first member of the
12310b922b7aSDavid Ahern 		 * underlying inet{6}_skb_parm struct. This code then overlays
12320b922b7aSDavid Ahern 		 * PKTINFO_SKB_CB and in_pktinfo also has iif as the first
1233f0c16ba8SWei Zhang 		 * element so the iif is picked up from the prior IPCB. If iif
1234f0c16ba8SWei Zhang 		 * is the loopback interface, then return the sending interface
1235f0c16ba8SWei Zhang 		 * (e.g., process binds socket to eth0 for Tx which is
1236f0c16ba8SWei Zhang 		 * redirected to loopback in the rtable/dst).
12370b922b7aSDavid Ahern 		 */
1238f0c16ba8SWei Zhang 		if (pktinfo->ipi_ifindex == LOOPBACK_IFINDEX)
1239f0c16ba8SWei Zhang 			pktinfo->ipi_ifindex = inet_iif(skb);
1240f0c16ba8SWei Zhang 
124135ebf65eSDavid S. Miller 		pktinfo->ipi_spec_dst.s_addr = fib_compute_spec_dst(skb);
1242d826eb14SEric Dumazet 	} else {
1243d826eb14SEric Dumazet 		pktinfo->ipi_ifindex = 0;
1244d826eb14SEric Dumazet 		pktinfo->ipi_spec_dst.s_addr = 0;
1245f84af32cSEric Dumazet 	}
124634b2cef2SEric Dumazet 	/* We need to keep the dst for __ip_options_echo()
124734b2cef2SEric Dumazet 	 * We could restrict the test to opt.ts_needtime || opt.srr,
124834b2cef2SEric Dumazet 	 * but the following is good enough as IP options are not often used.
124934b2cef2SEric Dumazet 	 */
125034b2cef2SEric Dumazet 	if (unlikely(IPCB(skb)->opt.optlen))
125134b2cef2SEric Dumazet 		skb_dst_force(skb);
125234b2cef2SEric Dumazet 	else
1253d826eb14SEric Dumazet 		skb_dst_drop(skb);
1254d826eb14SEric Dumazet }
1255f84af32cSEric Dumazet 
12563fdadf7dSDmitry Mishin int ip_setsockopt(struct sock *sk, int level,
1257b7058842SDavid S. Miller 		int optname, char __user *optval, unsigned int optlen)
12583fdadf7dSDmitry Mishin {
12593fdadf7dSDmitry Mishin 	int err;
12603fdadf7dSDmitry Mishin 
12613fdadf7dSDmitry Mishin 	if (level != SOL_IP)
12623fdadf7dSDmitry Mishin 		return -ENOPROTOOPT;
12633fdadf7dSDmitry Mishin 
12643fdadf7dSDmitry Mishin 	err = do_ip_setsockopt(sk, level, optname, optval, optlen);
12653fdadf7dSDmitry Mishin #ifdef CONFIG_NETFILTER
12663fdadf7dSDmitry Mishin 	/* we need to exclude all possible ENOPROTOOPTs except default case */
12673fdadf7dSDmitry Mishin 	if (err == -ENOPROTOOPT && optname != IP_HDRINCL &&
12686a9fb947SPavel Emelyanov 			optname != IP_IPSEC_POLICY &&
12696a9fb947SPavel Emelyanov 			optname != IP_XFRM_POLICY &&
12706a9fb947SPavel Emelyanov 			!ip_mroute_opt(optname)) {
12713fdadf7dSDmitry Mishin 		lock_sock(sk);
12723fdadf7dSDmitry Mishin 		err = nf_setsockopt(sk, PF_INET, optname, optval, optlen);
12733fdadf7dSDmitry Mishin 		release_sock(sk);
12743fdadf7dSDmitry Mishin 	}
12753fdadf7dSDmitry Mishin #endif
12763fdadf7dSDmitry Mishin 	return err;
12773fdadf7dSDmitry Mishin }
12784d52cfbeSEric Dumazet EXPORT_SYMBOL(ip_setsockopt);
12793fdadf7dSDmitry Mishin 
12803fdadf7dSDmitry Mishin #ifdef CONFIG_COMPAT
1281543d9cfeSArnaldo Carvalho de Melo int compat_ip_setsockopt(struct sock *sk, int level, int optname,
1282b7058842SDavid S. Miller 			 char __user *optval, unsigned int optlen)
12833fdadf7dSDmitry Mishin {
12843fdadf7dSDmitry Mishin 	int err;
12853fdadf7dSDmitry Mishin 
12863fdadf7dSDmitry Mishin 	if (level != SOL_IP)
12873fdadf7dSDmitry Mishin 		return -ENOPROTOOPT;
12883fdadf7dSDmitry Mishin 
1289dae50295SDavid L Stevens 	if (optname >= MCAST_JOIN_GROUP && optname <= MCAST_MSFILTER)
1290dae50295SDavid L Stevens 		return compat_mc_setsockopt(sk, level, optname, optval, optlen,
1291dae50295SDavid L Stevens 			ip_setsockopt);
1292dae50295SDavid L Stevens 
12933fdadf7dSDmitry Mishin 	err = do_ip_setsockopt(sk, level, optname, optval, optlen);
12943fdadf7dSDmitry Mishin #ifdef CONFIG_NETFILTER
12953fdadf7dSDmitry Mishin 	/* we need to exclude all possible ENOPROTOOPTs except default case */
12963fdadf7dSDmitry Mishin 	if (err == -ENOPROTOOPT && optname != IP_HDRINCL &&
12976a9fb947SPavel Emelyanov 			optname != IP_IPSEC_POLICY &&
12986a9fb947SPavel Emelyanov 			optname != IP_XFRM_POLICY &&
12996a9fb947SPavel Emelyanov 			!ip_mroute_opt(optname)) {
13003fdadf7dSDmitry Mishin 		lock_sock(sk);
1301543d9cfeSArnaldo Carvalho de Melo 		err = compat_nf_setsockopt(sk, PF_INET, optname,
1302543d9cfeSArnaldo Carvalho de Melo 					   optval, optlen);
13033fdadf7dSDmitry Mishin 		release_sock(sk);
13043fdadf7dSDmitry Mishin 	}
13053fdadf7dSDmitry Mishin #endif
13063fdadf7dSDmitry Mishin 	return err;
13073fdadf7dSDmitry Mishin }
1308543d9cfeSArnaldo Carvalho de Melo EXPORT_SYMBOL(compat_ip_setsockopt);
13093fdadf7dSDmitry Mishin #endif
13103fdadf7dSDmitry Mishin 
13111da177e4SLinus Torvalds /*
13124d52cfbeSEric Dumazet  *	Get the options. Note for future reference. The GET of IP options gets
13134d52cfbeSEric Dumazet  *	the _received_ ones. The set sets the _sent_ ones.
13141da177e4SLinus Torvalds  */
13151da177e4SLinus Torvalds 
131687e9f031SWANG Cong static bool getsockopt_needs_rtnl(int optname)
131787e9f031SWANG Cong {
131887e9f031SWANG Cong 	switch (optname) {
131987e9f031SWANG Cong 	case IP_MSFILTER:
132087e9f031SWANG Cong 	case MCAST_MSFILTER:
132187e9f031SWANG Cong 		return true;
132287e9f031SWANG Cong 	}
132387e9f031SWANG Cong 	return false;
132487e9f031SWANG Cong }
132587e9f031SWANG Cong 
13263fdadf7dSDmitry Mishin static int do_ip_getsockopt(struct sock *sk, int level, int optname,
132795c96174SEric Dumazet 			    char __user *optval, int __user *optlen, unsigned int flags)
13281da177e4SLinus Torvalds {
13291da177e4SLinus Torvalds 	struct inet_sock *inet = inet_sk(sk);
133087e9f031SWANG Cong 	bool needs_rtnl = getsockopt_needs_rtnl(optname);
133187e9f031SWANG Cong 	int val, err = 0;
13321da177e4SLinus Torvalds 	int len;
13331da177e4SLinus Torvalds 
13341da177e4SLinus Torvalds 	if (level != SOL_IP)
13351da177e4SLinus Torvalds 		return -EOPNOTSUPP;
13361da177e4SLinus Torvalds 
13376a9fb947SPavel Emelyanov 	if (ip_mroute_opt(optname))
13381da177e4SLinus Torvalds 		return ip_mroute_getsockopt(sk, optname, optval, optlen);
13391da177e4SLinus Torvalds 
13401da177e4SLinus Torvalds 	if (get_user(len, optlen))
13411da177e4SLinus Torvalds 		return -EFAULT;
13421da177e4SLinus Torvalds 	if (len < 0)
13431da177e4SLinus Torvalds 		return -EINVAL;
13441da177e4SLinus Torvalds 
134587e9f031SWANG Cong 	if (needs_rtnl)
134687e9f031SWANG Cong 		rtnl_lock();
13471da177e4SLinus Torvalds 	lock_sock(sk);
13481da177e4SLinus Torvalds 
13491da177e4SLinus Torvalds 	switch (optname) {
13501da177e4SLinus Torvalds 	case IP_OPTIONS:
13511da177e4SLinus Torvalds 	{
13521da177e4SLinus Torvalds 		unsigned char optbuf[sizeof(struct ip_options)+40];
13531da177e4SLinus Torvalds 		struct ip_options *opt = (struct ip_options *)optbuf;
1354f6d8bd05SEric Dumazet 		struct ip_options_rcu *inet_opt;
1355f6d8bd05SEric Dumazet 
1356f6d8bd05SEric Dumazet 		inet_opt = rcu_dereference_protected(inet->inet_opt,
13571e1d04e6SHannes Frederic Sowa 						     lockdep_sock_is_held(sk));
13581da177e4SLinus Torvalds 		opt->optlen = 0;
1359f6d8bd05SEric Dumazet 		if (inet_opt)
1360f6d8bd05SEric Dumazet 			memcpy(optbuf, &inet_opt->opt,
13611da177e4SLinus Torvalds 			       sizeof(struct ip_options) +
1362f6d8bd05SEric Dumazet 			       inet_opt->opt.optlen);
13631da177e4SLinus Torvalds 		release_sock(sk);
13641da177e4SLinus Torvalds 
13651da177e4SLinus Torvalds 		if (opt->optlen == 0)
13661da177e4SLinus Torvalds 			return put_user(0, optlen);
13671da177e4SLinus Torvalds 
13681da177e4SLinus Torvalds 		ip_options_undo(opt);
13691da177e4SLinus Torvalds 
13701da177e4SLinus Torvalds 		len = min_t(unsigned int, len, opt->optlen);
13711da177e4SLinus Torvalds 		if (put_user(len, optlen))
13721da177e4SLinus Torvalds 			return -EFAULT;
13731da177e4SLinus Torvalds 		if (copy_to_user(optval, opt->__data, len))
13741da177e4SLinus Torvalds 			return -EFAULT;
13751da177e4SLinus Torvalds 		return 0;
13761da177e4SLinus Torvalds 	}
13771da177e4SLinus Torvalds 	case IP_PKTINFO:
13781da177e4SLinus Torvalds 		val = (inet->cmsg_flags & IP_CMSG_PKTINFO) != 0;
13791da177e4SLinus Torvalds 		break;
13801da177e4SLinus Torvalds 	case IP_RECVTTL:
13811da177e4SLinus Torvalds 		val = (inet->cmsg_flags & IP_CMSG_TTL) != 0;
13821da177e4SLinus Torvalds 		break;
13831da177e4SLinus Torvalds 	case IP_RECVTOS:
13841da177e4SLinus Torvalds 		val = (inet->cmsg_flags & IP_CMSG_TOS) != 0;
13851da177e4SLinus Torvalds 		break;
13861da177e4SLinus Torvalds 	case IP_RECVOPTS:
13871da177e4SLinus Torvalds 		val = (inet->cmsg_flags & IP_CMSG_RECVOPTS) != 0;
13881da177e4SLinus Torvalds 		break;
13891da177e4SLinus Torvalds 	case IP_RETOPTS:
13901da177e4SLinus Torvalds 		val = (inet->cmsg_flags & IP_CMSG_RETOPTS) != 0;
13911da177e4SLinus Torvalds 		break;
13922c7946a7SCatherine Zhang 	case IP_PASSSEC:
13932c7946a7SCatherine Zhang 		val = (inet->cmsg_flags & IP_CMSG_PASSSEC) != 0;
13942c7946a7SCatherine Zhang 		break;
1395e8b2dfe9SBalazs Scheidler 	case IP_RECVORIGDSTADDR:
1396e8b2dfe9SBalazs Scheidler 		val = (inet->cmsg_flags & IP_CMSG_ORIGDSTADDR) != 0;
1397e8b2dfe9SBalazs Scheidler 		break;
1398ad6f939aSTom Herbert 	case IP_CHECKSUM:
1399ad6f939aSTom Herbert 		val = (inet->cmsg_flags & IP_CMSG_CHECKSUM) != 0;
1400ad6f939aSTom Herbert 		break;
140170ecc248SWillem de Bruijn 	case IP_RECVFRAGSIZE:
140270ecc248SWillem de Bruijn 		val = (inet->cmsg_flags & IP_CMSG_RECVFRAGSIZE) != 0;
140370ecc248SWillem de Bruijn 		break;
14041da177e4SLinus Torvalds 	case IP_TOS:
14051da177e4SLinus Torvalds 		val = inet->tos;
14061da177e4SLinus Torvalds 		break;
14071da177e4SLinus Torvalds 	case IP_TTL:
1408fa50d974SNikolay Borisov 	{
1409fa50d974SNikolay Borisov 		struct net *net = sock_net(sk);
14101da177e4SLinus Torvalds 		val = (inet->uc_ttl == -1 ?
1411fa50d974SNikolay Borisov 		       net->ipv4.sysctl_ip_default_ttl :
14121da177e4SLinus Torvalds 		       inet->uc_ttl);
14131da177e4SLinus Torvalds 		break;
1414fa50d974SNikolay Borisov 	}
14151da177e4SLinus Torvalds 	case IP_HDRINCL:
14161da177e4SLinus Torvalds 		val = inet->hdrincl;
14171da177e4SLinus Torvalds 		break;
1418a89b4763SMichael Kerrisk 	case IP_NODEFRAG:
1419a89b4763SMichael Kerrisk 		val = inet->nodefrag;
1420a89b4763SMichael Kerrisk 		break;
142190c337daSEric Dumazet 	case IP_BIND_ADDRESS_NO_PORT:
142290c337daSEric Dumazet 		val = inet->bind_address_no_port;
142390c337daSEric Dumazet 		break;
14241da177e4SLinus Torvalds 	case IP_MTU_DISCOVER:
14251da177e4SLinus Torvalds 		val = inet->pmtudisc;
14261da177e4SLinus Torvalds 		break;
14271da177e4SLinus Torvalds 	case IP_MTU:
14281da177e4SLinus Torvalds 	{
14291da177e4SLinus Torvalds 		struct dst_entry *dst;
14301da177e4SLinus Torvalds 		val = 0;
14311da177e4SLinus Torvalds 		dst = sk_dst_get(sk);
14321da177e4SLinus Torvalds 		if (dst) {
14331da177e4SLinus Torvalds 			val = dst_mtu(dst);
14341da177e4SLinus Torvalds 			dst_release(dst);
14351da177e4SLinus Torvalds 		}
14361da177e4SLinus Torvalds 		if (!val) {
14371da177e4SLinus Torvalds 			release_sock(sk);
14381da177e4SLinus Torvalds 			return -ENOTCONN;
14391da177e4SLinus Torvalds 		}
14401da177e4SLinus Torvalds 		break;
14411da177e4SLinus Torvalds 	}
14421da177e4SLinus Torvalds 	case IP_RECVERR:
14431da177e4SLinus Torvalds 		val = inet->recverr;
14441da177e4SLinus Torvalds 		break;
14451da177e4SLinus Torvalds 	case IP_MULTICAST_TTL:
14461da177e4SLinus Torvalds 		val = inet->mc_ttl;
14471da177e4SLinus Torvalds 		break;
14481da177e4SLinus Torvalds 	case IP_MULTICAST_LOOP:
14491da177e4SLinus Torvalds 		val = inet->mc_loop;
14501da177e4SLinus Torvalds 		break;
145176e21053SErich E. Hoover 	case IP_UNICAST_IF:
145276e21053SErich E. Hoover 		val = (__force int)htonl((__u32) inet->uc_index);
145376e21053SErich E. Hoover 		break;
14541da177e4SLinus Torvalds 	case IP_MULTICAST_IF:
14551da177e4SLinus Torvalds 	{
14561da177e4SLinus Torvalds 		struct in_addr addr;
14571da177e4SLinus Torvalds 		len = min_t(unsigned int, len, sizeof(struct in_addr));
14581da177e4SLinus Torvalds 		addr.s_addr = inet->mc_addr;
14591da177e4SLinus Torvalds 		release_sock(sk);
14601da177e4SLinus Torvalds 
14611da177e4SLinus Torvalds 		if (put_user(len, optlen))
14621da177e4SLinus Torvalds 			return -EFAULT;
14631da177e4SLinus Torvalds 		if (copy_to_user(optval, &addr, len))
14641da177e4SLinus Torvalds 			return -EFAULT;
14651da177e4SLinus Torvalds 		return 0;
14661da177e4SLinus Torvalds 	}
14671da177e4SLinus Torvalds 	case IP_MSFILTER:
14681da177e4SLinus Torvalds 	{
14691da177e4SLinus Torvalds 		struct ip_msfilter msf;
14701da177e4SLinus Torvalds 
14711da177e4SLinus Torvalds 		if (len < IP_MSFILTER_SIZE(0)) {
147287e9f031SWANG Cong 			err = -EINVAL;
147387e9f031SWANG Cong 			goto out;
14741da177e4SLinus Torvalds 		}
14751da177e4SLinus Torvalds 		if (copy_from_user(&msf, optval, IP_MSFILTER_SIZE(0))) {
147687e9f031SWANG Cong 			err = -EFAULT;
147787e9f031SWANG Cong 			goto out;
14781da177e4SLinus Torvalds 		}
14791da177e4SLinus Torvalds 		err = ip_mc_msfget(sk, &msf,
14801da177e4SLinus Torvalds 				   (struct ip_msfilter __user *)optval, optlen);
148187e9f031SWANG Cong 		goto out;
14821da177e4SLinus Torvalds 	}
14831da177e4SLinus Torvalds 	case MCAST_MSFILTER:
14841da177e4SLinus Torvalds 	{
14851da177e4SLinus Torvalds 		struct group_filter gsf;
14861da177e4SLinus Torvalds 
14871da177e4SLinus Torvalds 		if (len < GROUP_FILTER_SIZE(0)) {
148887e9f031SWANG Cong 			err = -EINVAL;
148987e9f031SWANG Cong 			goto out;
14901da177e4SLinus Torvalds 		}
14911da177e4SLinus Torvalds 		if (copy_from_user(&gsf, optval, GROUP_FILTER_SIZE(0))) {
149287e9f031SWANG Cong 			err = -EFAULT;
149387e9f031SWANG Cong 			goto out;
14941da177e4SLinus Torvalds 		}
14951da177e4SLinus Torvalds 		err = ip_mc_gsfget(sk, &gsf,
14964d52cfbeSEric Dumazet 				   (struct group_filter __user *)optval,
14974d52cfbeSEric Dumazet 				   optlen);
149887e9f031SWANG Cong 		goto out;
14991da177e4SLinus Torvalds 	}
1500f771bef9SNivedita Singhvi 	case IP_MULTICAST_ALL:
1501f771bef9SNivedita Singhvi 		val = inet->mc_all;
1502f771bef9SNivedita Singhvi 		break;
15031da177e4SLinus Torvalds 	case IP_PKTOPTIONS:
15041da177e4SLinus Torvalds 	{
15051da177e4SLinus Torvalds 		struct msghdr msg;
15061da177e4SLinus Torvalds 
15071da177e4SLinus Torvalds 		release_sock(sk);
15081da177e4SLinus Torvalds 
15091da177e4SLinus Torvalds 		if (sk->sk_type != SOCK_STREAM)
15101da177e4SLinus Torvalds 			return -ENOPROTOOPT;
15111da177e4SLinus Torvalds 
1512c54a5e02SKaroly Kemeny 		msg.msg_control = (__force void *) optval;
15131da177e4SLinus Torvalds 		msg.msg_controllen = len;
1514dd23198eSDaniel Baluta 		msg.msg_flags = flags;
15151da177e4SLinus Torvalds 
15161da177e4SLinus Torvalds 		if (inet->cmsg_flags & IP_CMSG_PKTINFO) {
15171da177e4SLinus Torvalds 			struct in_pktinfo info;
15181da177e4SLinus Torvalds 
1519c720c7e8SEric Dumazet 			info.ipi_addr.s_addr = inet->inet_rcv_saddr;
1520c720c7e8SEric Dumazet 			info.ipi_spec_dst.s_addr = inet->inet_rcv_saddr;
15211da177e4SLinus Torvalds 			info.ipi_ifindex = inet->mc_index;
15221da177e4SLinus Torvalds 			put_cmsg(&msg, SOL_IP, IP_PKTINFO, sizeof(info), &info);
15231da177e4SLinus Torvalds 		}
15241da177e4SLinus Torvalds 		if (inet->cmsg_flags & IP_CMSG_TTL) {
15251da177e4SLinus Torvalds 			int hlim = inet->mc_ttl;
15261da177e4SLinus Torvalds 			put_cmsg(&msg, SOL_IP, IP_TTL, sizeof(hlim), &hlim);
15271da177e4SLinus Torvalds 		}
15284c507d28SJiri Benc 		if (inet->cmsg_flags & IP_CMSG_TOS) {
15294c507d28SJiri Benc 			int tos = inet->rcv_tos;
15304c507d28SJiri Benc 			put_cmsg(&msg, SOL_IP, IP_TOS, sizeof(tos), &tos);
15314c507d28SJiri Benc 		}
15321da177e4SLinus Torvalds 		len -= msg.msg_controllen;
15331da177e4SLinus Torvalds 		return put_user(len, optlen);
15341da177e4SLinus Torvalds 	}
15351da177e4SLinus Torvalds 	case IP_FREEBIND:
15361da177e4SLinus Torvalds 		val = inet->freebind;
15371da177e4SLinus Torvalds 		break;
1538f5715aeaSKOVACS Krisztian 	case IP_TRANSPARENT:
1539f5715aeaSKOVACS Krisztian 		val = inet->transparent;
1540f5715aeaSKOVACS Krisztian 		break;
1541d218d111SStephen Hemminger 	case IP_MINTTL:
1542d218d111SStephen Hemminger 		val = inet->min_ttl;
1543d218d111SStephen Hemminger 		break;
15441da177e4SLinus Torvalds 	default:
15451da177e4SLinus Torvalds 		release_sock(sk);
15461da177e4SLinus Torvalds 		return -ENOPROTOOPT;
15471da177e4SLinus Torvalds 	}
15481da177e4SLinus Torvalds 	release_sock(sk);
15491da177e4SLinus Torvalds 
1550951e07c9SDavid S. Miller 	if (len < sizeof(int) && len > 0 && val >= 0 && val <= 255) {
15511da177e4SLinus Torvalds 		unsigned char ucval = (unsigned char)val;
15521da177e4SLinus Torvalds 		len = 1;
15531da177e4SLinus Torvalds 		if (put_user(len, optlen))
15541da177e4SLinus Torvalds 			return -EFAULT;
15551da177e4SLinus Torvalds 		if (copy_to_user(optval, &ucval, 1))
15561da177e4SLinus Torvalds 			return -EFAULT;
15571da177e4SLinus Torvalds 	} else {
15581da177e4SLinus Torvalds 		len = min_t(unsigned int, sizeof(int), len);
15591da177e4SLinus Torvalds 		if (put_user(len, optlen))
15601da177e4SLinus Torvalds 			return -EFAULT;
15611da177e4SLinus Torvalds 		if (copy_to_user(optval, &val, len))
15621da177e4SLinus Torvalds 			return -EFAULT;
15631da177e4SLinus Torvalds 	}
15641da177e4SLinus Torvalds 	return 0;
156587e9f031SWANG Cong 
156687e9f031SWANG Cong out:
156787e9f031SWANG Cong 	release_sock(sk);
156887e9f031SWANG Cong 	if (needs_rtnl)
156987e9f031SWANG Cong 		rtnl_unlock();
157087e9f031SWANG Cong 	return err;
15711da177e4SLinus Torvalds }
15721da177e4SLinus Torvalds 
15733fdadf7dSDmitry Mishin int ip_getsockopt(struct sock *sk, int level,
15743fdadf7dSDmitry Mishin 		  int optname, char __user *optval, int __user *optlen)
15753fdadf7dSDmitry Mishin {
15763fdadf7dSDmitry Mishin 	int err;
15773fdadf7dSDmitry Mishin 
1578dd23198eSDaniel Baluta 	err = do_ip_getsockopt(sk, level, optname, optval, optlen, 0);
15793fdadf7dSDmitry Mishin #ifdef CONFIG_NETFILTER
15803fdadf7dSDmitry Mishin 	/* we need to exclude all possible ENOPROTOOPTs except default case */
15816a9fb947SPavel Emelyanov 	if (err == -ENOPROTOOPT && optname != IP_PKTOPTIONS &&
15826a9fb947SPavel Emelyanov 			!ip_mroute_opt(optname)) {
15833fdadf7dSDmitry Mishin 		int len;
15843fdadf7dSDmitry Mishin 
15853fdadf7dSDmitry Mishin 		if (get_user(len, optlen))
15863fdadf7dSDmitry Mishin 			return -EFAULT;
15873fdadf7dSDmitry Mishin 
15883fdadf7dSDmitry Mishin 		lock_sock(sk);
15893fdadf7dSDmitry Mishin 		err = nf_getsockopt(sk, PF_INET, optname, optval,
15903fdadf7dSDmitry Mishin 				&len);
15913fdadf7dSDmitry Mishin 		release_sock(sk);
15923fdadf7dSDmitry Mishin 		if (err >= 0)
15933fdadf7dSDmitry Mishin 			err = put_user(len, optlen);
15943fdadf7dSDmitry Mishin 		return err;
15953fdadf7dSDmitry Mishin 	}
15963fdadf7dSDmitry Mishin #endif
15973fdadf7dSDmitry Mishin 	return err;
15983fdadf7dSDmitry Mishin }
15994d52cfbeSEric Dumazet EXPORT_SYMBOL(ip_getsockopt);
16003fdadf7dSDmitry Mishin 
16013fdadf7dSDmitry Mishin #ifdef CONFIG_COMPAT
1602543d9cfeSArnaldo Carvalho de Melo int compat_ip_getsockopt(struct sock *sk, int level, int optname,
1603543d9cfeSArnaldo Carvalho de Melo 			 char __user *optval, int __user *optlen)
16043fdadf7dSDmitry Mishin {
160542908c69SDavid L Stevens 	int err;
160642908c69SDavid L Stevens 
160742908c69SDavid L Stevens 	if (optname == MCAST_MSFILTER)
160842908c69SDavid L Stevens 		return compat_mc_getsockopt(sk, level, optname, optval, optlen,
160942908c69SDavid L Stevens 			ip_getsockopt);
161042908c69SDavid L Stevens 
1611dd23198eSDaniel Baluta 	err = do_ip_getsockopt(sk, level, optname, optval, optlen,
1612dd23198eSDaniel Baluta 		MSG_CMSG_COMPAT);
161342908c69SDavid L Stevens 
16143fdadf7dSDmitry Mishin #ifdef CONFIG_NETFILTER
16153fdadf7dSDmitry Mishin 	/* we need to exclude all possible ENOPROTOOPTs except default case */
16166a9fb947SPavel Emelyanov 	if (err == -ENOPROTOOPT && optname != IP_PKTOPTIONS &&
16176a9fb947SPavel Emelyanov 			!ip_mroute_opt(optname)) {
16183fdadf7dSDmitry Mishin 		int len;
16193fdadf7dSDmitry Mishin 
16203fdadf7dSDmitry Mishin 		if (get_user(len, optlen))
16213fdadf7dSDmitry Mishin 			return -EFAULT;
16223fdadf7dSDmitry Mishin 
16233fdadf7dSDmitry Mishin 		lock_sock(sk);
1624543d9cfeSArnaldo Carvalho de Melo 		err = compat_nf_getsockopt(sk, PF_INET, optname, optval, &len);
16253fdadf7dSDmitry Mishin 		release_sock(sk);
16263fdadf7dSDmitry Mishin 		if (err >= 0)
16273fdadf7dSDmitry Mishin 			err = put_user(len, optlen);
16283fdadf7dSDmitry Mishin 		return err;
16293fdadf7dSDmitry Mishin 	}
16303fdadf7dSDmitry Mishin #endif
16313fdadf7dSDmitry Mishin 	return err;
16323fdadf7dSDmitry Mishin }
1633543d9cfeSArnaldo Carvalho de Melo EXPORT_SYMBOL(compat_ip_getsockopt);
16343fdadf7dSDmitry Mishin #endif
1635