xref: /linux/net/ipv4/ip_sockglue.c (revision 1cbec07649ec8c267cdfb486ab4ee73949974ca4)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  * INET		An implementation of the TCP/IP protocol suite for the LINUX
41da177e4SLinus Torvalds  *		operating system.  INET is implemented using the  BSD Socket
51da177e4SLinus Torvalds  *		interface as the means of communication with the user level.
61da177e4SLinus Torvalds  *
71da177e4SLinus Torvalds  *		The IP to API glue.
81da177e4SLinus Torvalds  *
91da177e4SLinus Torvalds  * Authors:	see ip.c
101da177e4SLinus Torvalds  *
111da177e4SLinus Torvalds  * Fixes:
121da177e4SLinus Torvalds  *		Many		:	Split from ip.c , see ip.c for history.
131da177e4SLinus Torvalds  *		Martin Mares	:	TOS setting fixed.
141da177e4SLinus Torvalds  *		Alan Cox	:	Fixed a couple of oopses in Martin's
151da177e4SLinus Torvalds  *					TOS tweaks.
161da177e4SLinus Torvalds  *		Mike McLagan	:	Routing by source
171da177e4SLinus Torvalds  */
181da177e4SLinus Torvalds 
191da177e4SLinus Torvalds #include <linux/module.h>
201da177e4SLinus Torvalds #include <linux/types.h>
211da177e4SLinus Torvalds #include <linux/mm.h>
221da177e4SLinus Torvalds #include <linux/skbuff.h>
231da177e4SLinus Torvalds #include <linux/ip.h>
241da177e4SLinus Torvalds #include <linux/icmp.h>
2514c85021SArnaldo Carvalho de Melo #include <linux/inetdevice.h>
261da177e4SLinus Torvalds #include <linux/netdevice.h>
275a0e3ad6STejun Heo #include <linux/slab.h>
281da177e4SLinus Torvalds #include <net/sock.h>
291da177e4SLinus Torvalds #include <net/ip.h>
301da177e4SLinus Torvalds #include <net/icmp.h>
31d83d8461SArnaldo Carvalho de Melo #include <net/tcp_states.h>
321da177e4SLinus Torvalds #include <linux/udp.h>
331da177e4SLinus Torvalds #include <linux/igmp.h>
341da177e4SLinus Torvalds #include <linux/netfilter.h>
351da177e4SLinus Torvalds #include <linux/route.h>
361da177e4SLinus Torvalds #include <linux/mroute.h>
372c67e9acSMaciej Żenczykowski #include <net/inet_ecn.h>
381da177e4SLinus Torvalds #include <net/route.h>
391da177e4SLinus Torvalds #include <net/xfrm.h>
40dae50295SDavid L Stevens #include <net/compat.h>
41ad6f939aSTom Herbert #include <net/checksum.h>
42dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6)
431da177e4SLinus Torvalds #include <net/transp_v6.h>
441da177e4SLinus Torvalds #endif
4535ebf65eSDavid S. Miller #include <net/ip_fib.h>
461da177e4SLinus Torvalds 
471da177e4SLinus Torvalds #include <linux/errqueue.h>
487c0f6ba6SLinus Torvalds #include <linux/uaccess.h>
491da177e4SLinus Torvalds 
501da177e4SLinus Torvalds /*
511da177e4SLinus Torvalds  *	SOL_IP control messages.
521da177e4SLinus Torvalds  */
531da177e4SLinus Torvalds 
541da177e4SLinus Torvalds static void ip_cmsg_recv_pktinfo(struct msghdr *msg, struct sk_buff *skb)
551da177e4SLinus Torvalds {
56d826eb14SEric Dumazet 	struct in_pktinfo info = *PKTINFO_SKB_CB(skb);
571da177e4SLinus Torvalds 
58eddc9ec5SArnaldo Carvalho de Melo 	info.ipi_addr.s_addr = ip_hdr(skb)->daddr;
591da177e4SLinus Torvalds 
601da177e4SLinus Torvalds 	put_cmsg(msg, SOL_IP, IP_PKTINFO, sizeof(info), &info);
611da177e4SLinus Torvalds }
621da177e4SLinus Torvalds 
631da177e4SLinus Torvalds static void ip_cmsg_recv_ttl(struct msghdr *msg, struct sk_buff *skb)
641da177e4SLinus Torvalds {
65eddc9ec5SArnaldo Carvalho de Melo 	int ttl = ip_hdr(skb)->ttl;
661da177e4SLinus Torvalds 	put_cmsg(msg, SOL_IP, IP_TTL, sizeof(int), &ttl);
671da177e4SLinus Torvalds }
681da177e4SLinus Torvalds 
691da177e4SLinus Torvalds static void ip_cmsg_recv_tos(struct msghdr *msg, struct sk_buff *skb)
701da177e4SLinus Torvalds {
71eddc9ec5SArnaldo Carvalho de Melo 	put_cmsg(msg, SOL_IP, IP_TOS, 1, &ip_hdr(skb)->tos);
721da177e4SLinus Torvalds }
731da177e4SLinus Torvalds 
741da177e4SLinus Torvalds static void ip_cmsg_recv_opts(struct msghdr *msg, struct sk_buff *skb)
751da177e4SLinus Torvalds {
761da177e4SLinus Torvalds 	if (IPCB(skb)->opt.optlen == 0)
771da177e4SLinus Torvalds 		return;
781da177e4SLinus Torvalds 
79eddc9ec5SArnaldo Carvalho de Melo 	put_cmsg(msg, SOL_IP, IP_RECVOPTS, IPCB(skb)->opt.optlen,
80eddc9ec5SArnaldo Carvalho de Melo 		 ip_hdr(skb) + 1);
811da177e4SLinus Torvalds }
821da177e4SLinus Torvalds 
831da177e4SLinus Torvalds 
8491ed1e66SPaolo Abeni static void ip_cmsg_recv_retopts(struct net *net, struct msghdr *msg,
8591ed1e66SPaolo Abeni 				 struct sk_buff *skb)
861da177e4SLinus Torvalds {
871da177e4SLinus Torvalds 	unsigned char optbuf[sizeof(struct ip_options) + 40];
881da177e4SLinus Torvalds 	struct ip_options *opt = (struct ip_options *)optbuf;
891da177e4SLinus Torvalds 
901da177e4SLinus Torvalds 	if (IPCB(skb)->opt.optlen == 0)
911da177e4SLinus Torvalds 		return;
921da177e4SLinus Torvalds 
9391ed1e66SPaolo Abeni 	if (ip_options_echo(net, opt, skb)) {
941da177e4SLinus Torvalds 		msg->msg_flags |= MSG_CTRUNC;
951da177e4SLinus Torvalds 		return;
961da177e4SLinus Torvalds 	}
971da177e4SLinus Torvalds 	ip_options_undo(opt);
981da177e4SLinus Torvalds 
991da177e4SLinus Torvalds 	put_cmsg(msg, SOL_IP, IP_RETOPTS, opt->optlen, opt->__data);
1001da177e4SLinus Torvalds }
1011da177e4SLinus Torvalds 
10270ecc248SWillem de Bruijn static void ip_cmsg_recv_fragsize(struct msghdr *msg, struct sk_buff *skb)
10370ecc248SWillem de Bruijn {
10470ecc248SWillem de Bruijn 	int val;
10570ecc248SWillem de Bruijn 
10670ecc248SWillem de Bruijn 	if (IPCB(skb)->frag_max_size == 0)
10770ecc248SWillem de Bruijn 		return;
10870ecc248SWillem de Bruijn 
10970ecc248SWillem de Bruijn 	val = IPCB(skb)->frag_max_size;
11070ecc248SWillem de Bruijn 	put_cmsg(msg, SOL_IP, IP_RECVFRAGSIZE, sizeof(val), &val);
11170ecc248SWillem de Bruijn }
11270ecc248SWillem de Bruijn 
113ad6f939aSTom Herbert static void ip_cmsg_recv_checksum(struct msghdr *msg, struct sk_buff *skb,
11410df8e61SEric Dumazet 				  int tlen, int offset)
115ad6f939aSTom Herbert {
116ad6f939aSTom Herbert 	__wsum csum = skb->csum;
117ad6f939aSTom Herbert 
118ad6f939aSTom Herbert 	if (skb->ip_summed != CHECKSUM_COMPLETE)
119ad6f939aSTom Herbert 		return;
120ad6f939aSTom Herbert 
121ca4ef457SPaolo Abeni 	if (offset != 0) {
122ca4ef457SPaolo Abeni 		int tend_off = skb_transport_offset(skb) + tlen;
123ca4ef457SPaolo Abeni 		csum = csum_sub(csum, skb_checksum(skb, tend_off, offset, 0));
124ca4ef457SPaolo Abeni 	}
125ad6f939aSTom Herbert 
126ad6f939aSTom Herbert 	put_cmsg(msg, SOL_IP, IP_CHECKSUM, sizeof(__wsum), &csum);
127ad6f939aSTom Herbert }
128ad6f939aSTom Herbert 
1292c7946a7SCatherine Zhang static void ip_cmsg_recv_security(struct msghdr *msg, struct sk_buff *skb)
1302c7946a7SCatherine Zhang {
1312c7946a7SCatherine Zhang 	char *secdata;
132dc49c1f9SCatherine Zhang 	u32 seclen, secid;
1332c7946a7SCatherine Zhang 	int err;
1342c7946a7SCatherine Zhang 
135dc49c1f9SCatherine Zhang 	err = security_socket_getpeersec_dgram(NULL, skb, &secid);
136dc49c1f9SCatherine Zhang 	if (err)
137dc49c1f9SCatherine Zhang 		return;
138dc49c1f9SCatherine Zhang 
139dc49c1f9SCatherine Zhang 	err = security_secid_to_secctx(secid, &secdata, &seclen);
1402c7946a7SCatherine Zhang 	if (err)
1412c7946a7SCatherine Zhang 		return;
1422c7946a7SCatherine Zhang 
1432c7946a7SCatherine Zhang 	put_cmsg(msg, SOL_IP, SCM_SECURITY, seclen, secdata);
144dc49c1f9SCatherine Zhang 	security_release_secctx(secdata, seclen);
1452c7946a7SCatherine Zhang }
1462c7946a7SCatherine Zhang 
14721d1a161SHarvey Harrison static void ip_cmsg_recv_dstaddr(struct msghdr *msg, struct sk_buff *skb)
148e8b2dfe9SBalazs Scheidler {
149e8b2dfe9SBalazs Scheidler 	struct sockaddr_in sin;
150b71d1d42SEric Dumazet 	const struct iphdr *iph = ip_hdr(skb);
15121d1a161SHarvey Harrison 	__be16 *ports = (__be16 *)skb_transport_header(skb);
152e8b2dfe9SBalazs Scheidler 
15339b2dd76SWillem de Bruijn 	if (skb_transport_offset(skb) + 4 > (int)skb->len)
154e8b2dfe9SBalazs Scheidler 		return;
155e8b2dfe9SBalazs Scheidler 
156e8b2dfe9SBalazs Scheidler 	/* All current transport protocols have the port numbers in the
157e8b2dfe9SBalazs Scheidler 	 * first four bytes of the transport header and this function is
158e8b2dfe9SBalazs Scheidler 	 * written with this assumption in mind.
159e8b2dfe9SBalazs Scheidler 	 */
160e8b2dfe9SBalazs Scheidler 
161e8b2dfe9SBalazs Scheidler 	sin.sin_family = AF_INET;
162e8b2dfe9SBalazs Scheidler 	sin.sin_addr.s_addr = iph->daddr;
163e8b2dfe9SBalazs Scheidler 	sin.sin_port = ports[1];
164e8b2dfe9SBalazs Scheidler 	memset(sin.sin_zero, 0, sizeof(sin.sin_zero));
165e8b2dfe9SBalazs Scheidler 
166e8b2dfe9SBalazs Scheidler 	put_cmsg(msg, SOL_IP, IP_ORIGDSTADDR, sizeof(sin), &sin);
167e8b2dfe9SBalazs Scheidler }
1681da177e4SLinus Torvalds 
169ad959036SPaolo Abeni void ip_cmsg_recv_offset(struct msghdr *msg, struct sock *sk,
170ad959036SPaolo Abeni 			 struct sk_buff *skb, int tlen, int offset)
1711da177e4SLinus Torvalds {
172ad959036SPaolo Abeni 	struct inet_sock *inet = inet_sk(sk);
17395c96174SEric Dumazet 	unsigned int flags = inet->cmsg_flags;
1741da177e4SLinus Torvalds 
1751da177e4SLinus Torvalds 	/* Ordered by supposed usage frequency */
176c44d13d6STom Herbert 	if (flags & IP_CMSG_PKTINFO) {
1771da177e4SLinus Torvalds 		ip_cmsg_recv_pktinfo(msg, skb);
1781da177e4SLinus Torvalds 
179c44d13d6STom Herbert 		flags &= ~IP_CMSG_PKTINFO;
180c44d13d6STom Herbert 		if (!flags)
181c44d13d6STom Herbert 			return;
182c44d13d6STom Herbert 	}
183c44d13d6STom Herbert 
184c44d13d6STom Herbert 	if (flags & IP_CMSG_TTL) {
1851da177e4SLinus Torvalds 		ip_cmsg_recv_ttl(msg, skb);
1861da177e4SLinus Torvalds 
187c44d13d6STom Herbert 		flags &= ~IP_CMSG_TTL;
188c44d13d6STom Herbert 		if (!flags)
189c44d13d6STom Herbert 			return;
190c44d13d6STom Herbert 	}
191c44d13d6STom Herbert 
192c44d13d6STom Herbert 	if (flags & IP_CMSG_TOS) {
1931da177e4SLinus Torvalds 		ip_cmsg_recv_tos(msg, skb);
1941da177e4SLinus Torvalds 
195c44d13d6STom Herbert 		flags &= ~IP_CMSG_TOS;
196c44d13d6STom Herbert 		if (!flags)
197c44d13d6STom Herbert 			return;
198c44d13d6STom Herbert 	}
199c44d13d6STom Herbert 
200c44d13d6STom Herbert 	if (flags & IP_CMSG_RECVOPTS) {
2011da177e4SLinus Torvalds 		ip_cmsg_recv_opts(msg, skb);
2021da177e4SLinus Torvalds 
203c44d13d6STom Herbert 		flags &= ~IP_CMSG_RECVOPTS;
204c44d13d6STom Herbert 		if (!flags)
205c44d13d6STom Herbert 			return;
206c44d13d6STom Herbert 	}
207c44d13d6STom Herbert 
208c44d13d6STom Herbert 	if (flags & IP_CMSG_RETOPTS) {
20991ed1e66SPaolo Abeni 		ip_cmsg_recv_retopts(sock_net(sk), msg, skb);
2102c7946a7SCatherine Zhang 
211c44d13d6STom Herbert 		flags &= ~IP_CMSG_RETOPTS;
212c44d13d6STom Herbert 		if (!flags)
213c44d13d6STom Herbert 			return;
214c44d13d6STom Herbert 	}
215c44d13d6STom Herbert 
216c44d13d6STom Herbert 	if (flags & IP_CMSG_PASSSEC) {
2172c7946a7SCatherine Zhang 		ip_cmsg_recv_security(msg, skb);
218e8b2dfe9SBalazs Scheidler 
219c44d13d6STom Herbert 		flags &= ~IP_CMSG_PASSSEC;
220c44d13d6STom Herbert 		if (!flags)
221e8b2dfe9SBalazs Scheidler 			return;
222c44d13d6STom Herbert 	}
223c44d13d6STom Herbert 
224ad6f939aSTom Herbert 	if (flags & IP_CMSG_ORIGDSTADDR) {
225e8b2dfe9SBalazs Scheidler 		ip_cmsg_recv_dstaddr(msg, skb);
226e8b2dfe9SBalazs Scheidler 
227ad6f939aSTom Herbert 		flags &= ~IP_CMSG_ORIGDSTADDR;
228ad6f939aSTom Herbert 		if (!flags)
229ad6f939aSTom Herbert 			return;
230ad6f939aSTom Herbert 	}
231ad6f939aSTom Herbert 
232ad6f939aSTom Herbert 	if (flags & IP_CMSG_CHECKSUM)
23310df8e61SEric Dumazet 		ip_cmsg_recv_checksum(msg, skb, tlen, offset);
23470ecc248SWillem de Bruijn 
23570ecc248SWillem de Bruijn 	if (flags & IP_CMSG_RECVFRAGSIZE)
23670ecc248SWillem de Bruijn 		ip_cmsg_recv_fragsize(msg, skb);
2371da177e4SLinus Torvalds }
2385961de9fSTom Herbert EXPORT_SYMBOL(ip_cmsg_recv_offset);
2391da177e4SLinus Torvalds 
24024025c46SSoheil Hassas Yeganeh int ip_cmsg_send(struct sock *sk, struct msghdr *msg, struct ipcm_cookie *ipc,
241c8e6ad08SHannes Frederic Sowa 		 bool allow_ipv6)
2421da177e4SLinus Torvalds {
243f02db315SFrancesco Fusco 	int err, val;
2441da177e4SLinus Torvalds 	struct cmsghdr *cmsg;
24524025c46SSoheil Hassas Yeganeh 	struct net *net = sock_net(sk);
2461da177e4SLinus Torvalds 
247f95b414eSGu Zheng 	for_each_cmsghdr(cmsg, msg) {
2481da177e4SLinus Torvalds 		if (!CMSG_OK(msg, cmsg))
2491da177e4SLinus Torvalds 			return -EINVAL;
2505337b5b7SEric Dumazet #if IS_ENABLED(CONFIG_IPV6)
251c8e6ad08SHannes Frederic Sowa 		if (allow_ipv6 &&
252c8e6ad08SHannes Frederic Sowa 		    cmsg->cmsg_level == SOL_IPV6 &&
253c8e6ad08SHannes Frederic Sowa 		    cmsg->cmsg_type == IPV6_PKTINFO) {
254c8e6ad08SHannes Frederic Sowa 			struct in6_pktinfo *src_info;
255c8e6ad08SHannes Frederic Sowa 
256c8e6ad08SHannes Frederic Sowa 			if (cmsg->cmsg_len < CMSG_LEN(sizeof(*src_info)))
257c8e6ad08SHannes Frederic Sowa 				return -EINVAL;
258c8e6ad08SHannes Frederic Sowa 			src_info = (struct in6_pktinfo *)CMSG_DATA(cmsg);
259c8e6ad08SHannes Frederic Sowa 			if (!ipv6_addr_v4mapped(&src_info->ipi6_addr))
260c8e6ad08SHannes Frederic Sowa 				return -EINVAL;
261*1cbec076SDavid Ahern 			if (src_info->ipi6_ifindex)
262c8e6ad08SHannes Frederic Sowa 				ipc->oif = src_info->ipi6_ifindex;
263c8e6ad08SHannes Frederic Sowa 			ipc->addr = src_info->ipi6_addr.s6_addr32[3];
264c8e6ad08SHannes Frederic Sowa 			continue;
265c8e6ad08SHannes Frederic Sowa 		}
266c8e6ad08SHannes Frederic Sowa #endif
26724025c46SSoheil Hassas Yeganeh 		if (cmsg->cmsg_level == SOL_SOCKET) {
2682632616bSEric Dumazet 			err = __sock_cmsg_send(sk, msg, cmsg, &ipc->sockc);
2692632616bSEric Dumazet 			if (err)
2702632616bSEric Dumazet 				return err;
27124025c46SSoheil Hassas Yeganeh 			continue;
27224025c46SSoheil Hassas Yeganeh 		}
27324025c46SSoheil Hassas Yeganeh 
2741da177e4SLinus Torvalds 		if (cmsg->cmsg_level != SOL_IP)
2751da177e4SLinus Torvalds 			continue;
2761da177e4SLinus Torvalds 		switch (cmsg->cmsg_type) {
2771da177e4SLinus Torvalds 		case IP_RETOPTS:
2781ff8cebfSyuan linyu 			err = cmsg->cmsg_len - sizeof(struct cmsghdr);
27991948309SEric Dumazet 
28091948309SEric Dumazet 			/* Our caller is responsible for freeing ipc->opt */
2814d52cfbeSEric Dumazet 			err = ip_options_get(net, &ipc->opt, CMSG_DATA(cmsg),
2824d52cfbeSEric Dumazet 					     err < 40 ? err : 40);
2831da177e4SLinus Torvalds 			if (err)
2841da177e4SLinus Torvalds 				return err;
2851da177e4SLinus Torvalds 			break;
2861da177e4SLinus Torvalds 		case IP_PKTINFO:
2871da177e4SLinus Torvalds 		{
2881da177e4SLinus Torvalds 			struct in_pktinfo *info;
2891da177e4SLinus Torvalds 			if (cmsg->cmsg_len != CMSG_LEN(sizeof(struct in_pktinfo)))
2901da177e4SLinus Torvalds 				return -EINVAL;
2911da177e4SLinus Torvalds 			info = (struct in_pktinfo *)CMSG_DATA(cmsg);
292*1cbec076SDavid Ahern 			if (info->ipi_ifindex)
2931da177e4SLinus Torvalds 				ipc->oif = info->ipi_ifindex;
2941da177e4SLinus Torvalds 			ipc->addr = info->ipi_spec_dst.s_addr;
2951da177e4SLinus Torvalds 			break;
2961da177e4SLinus Torvalds 		}
297f02db315SFrancesco Fusco 		case IP_TTL:
298f02db315SFrancesco Fusco 			if (cmsg->cmsg_len != CMSG_LEN(sizeof(int)))
299f02db315SFrancesco Fusco 				return -EINVAL;
300f02db315SFrancesco Fusco 			val = *(int *)CMSG_DATA(cmsg);
301f02db315SFrancesco Fusco 			if (val < 1 || val > 255)
302f02db315SFrancesco Fusco 				return -EINVAL;
303f02db315SFrancesco Fusco 			ipc->ttl = val;
304f02db315SFrancesco Fusco 			break;
305f02db315SFrancesco Fusco 		case IP_TOS:
306e895cdceSEric Dumazet 			if (cmsg->cmsg_len == CMSG_LEN(sizeof(int)))
307f02db315SFrancesco Fusco 				val = *(int *)CMSG_DATA(cmsg);
308e895cdceSEric Dumazet 			else if (cmsg->cmsg_len == CMSG_LEN(sizeof(u8)))
309e895cdceSEric Dumazet 				val = *(u8 *)CMSG_DATA(cmsg);
310e895cdceSEric Dumazet 			else
311e895cdceSEric Dumazet 				return -EINVAL;
312f02db315SFrancesco Fusco 			if (val < 0 || val > 255)
313f02db315SFrancesco Fusco 				return -EINVAL;
314f02db315SFrancesco Fusco 			ipc->tos = val;
315f02db315SFrancesco Fusco 			ipc->priority = rt_tos2priority(ipc->tos);
316f02db315SFrancesco Fusco 			break;
317f02db315SFrancesco Fusco 
3181da177e4SLinus Torvalds 		default:
3191da177e4SLinus Torvalds 			return -EINVAL;
3201da177e4SLinus Torvalds 		}
3211da177e4SLinus Torvalds 	}
3221da177e4SLinus Torvalds 	return 0;
3231da177e4SLinus Torvalds }
3241da177e4SLinus Torvalds 
3251da177e4SLinus Torvalds 
3261da177e4SLinus Torvalds /* Special input handler for packets caught by router alert option.
3271da177e4SLinus Torvalds    They are selected only by protocol field, and then processed likely
3281da177e4SLinus Torvalds    local ones; but only if someone wants them! Otherwise, router
3291da177e4SLinus Torvalds    not running rsvpd will kill RSVP.
3301da177e4SLinus Torvalds 
3311da177e4SLinus Torvalds    It is user level problem, what it will make with them.
3321da177e4SLinus Torvalds    I have no idea, how it will masquearde or NAT them (it is joke, joke :-)),
3331da177e4SLinus Torvalds    but receiver should be enough clever f.e. to forward mtrace requests,
3341da177e4SLinus Torvalds    sent to multicast group to reach destination designated router.
3351da177e4SLinus Torvalds  */
33643a951e9SEric Dumazet struct ip_ra_chain __rcu *ip_ra_chain;
33766018506SEric Dumazet 
338592fcb9dSEric Dumazet 
339592fcb9dSEric Dumazet static void ip_ra_destroy_rcu(struct rcu_head *head)
34066018506SEric Dumazet {
341592fcb9dSEric Dumazet 	struct ip_ra_chain *ra = container_of(head, struct ip_ra_chain, rcu);
342592fcb9dSEric Dumazet 
343592fcb9dSEric Dumazet 	sock_put(ra->saved_sk);
344592fcb9dSEric Dumazet 	kfree(ra);
34566018506SEric Dumazet }
3461da177e4SLinus Torvalds 
3474d52cfbeSEric Dumazet int ip_ra_control(struct sock *sk, unsigned char on,
3484d52cfbeSEric Dumazet 		  void (*destructor)(struct sock *))
3491da177e4SLinus Torvalds {
35043a951e9SEric Dumazet 	struct ip_ra_chain *ra, *new_ra;
35143a951e9SEric Dumazet 	struct ip_ra_chain __rcu **rap;
3521da177e4SLinus Torvalds 
353c720c7e8SEric Dumazet 	if (sk->sk_type != SOCK_RAW || inet_sk(sk)->inet_num == IPPROTO_RAW)
3541da177e4SLinus Torvalds 		return -EINVAL;
3551da177e4SLinus Torvalds 
3561da177e4SLinus Torvalds 	new_ra = on ? kmalloc(sizeof(*new_ra), GFP_KERNEL) : NULL;
3571da177e4SLinus Torvalds 
35843a951e9SEric Dumazet 	for (rap = &ip_ra_chain;
359ba3f571dSWANG Cong 	     (ra = rtnl_dereference(*rap)) != NULL;
36043a951e9SEric Dumazet 	     rap = &ra->next) {
3611da177e4SLinus Torvalds 		if (ra->sk == sk) {
3621da177e4SLinus Torvalds 			if (on) {
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);
3691da177e4SLinus Torvalds 
3701da177e4SLinus Torvalds 			if (ra->destructor)
3711da177e4SLinus Torvalds 				ra->destructor(sk);
372592fcb9dSEric Dumazet 			/*
373592fcb9dSEric Dumazet 			 * Delay sock_put(sk) and kfree(ra) after one rcu grace
374592fcb9dSEric Dumazet 			 * period. This guarantee ip_call_ra_chain() dont need
375592fcb9dSEric Dumazet 			 * to mess with socket refcounts.
376592fcb9dSEric Dumazet 			 */
377592fcb9dSEric Dumazet 			ra->saved_sk = sk;
378592fcb9dSEric Dumazet 			call_rcu(&ra->rcu, ip_ra_destroy_rcu);
3791da177e4SLinus Torvalds 			return 0;
3801da177e4SLinus Torvalds 		}
3811da177e4SLinus Torvalds 	}
382ba3f571dSWANG Cong 	if (!new_ra)
3831da177e4SLinus Torvalds 		return -ENOBUFS;
3841da177e4SLinus Torvalds 	new_ra->sk = sk;
3851da177e4SLinus Torvalds 	new_ra->destructor = destructor;
3861da177e4SLinus Torvalds 
3878e380f00SEric Dumazet 	RCU_INIT_POINTER(new_ra->next, ra);
38866018506SEric Dumazet 	rcu_assign_pointer(*rap, new_ra);
3891da177e4SLinus Torvalds 	sock_hold(sk);
3901da177e4SLinus Torvalds 
3911da177e4SLinus Torvalds 	return 0;
3921da177e4SLinus Torvalds }
3931da177e4SLinus Torvalds 
3941da177e4SLinus Torvalds void ip_icmp_error(struct sock *sk, struct sk_buff *skb, int err,
39535986b32SAl Viro 		   __be16 port, u32 info, u8 *payload)
3961da177e4SLinus Torvalds {
3971da177e4SLinus Torvalds 	struct sock_exterr_skb *serr;
3981da177e4SLinus Torvalds 
3991da177e4SLinus Torvalds 	skb = skb_clone(skb, GFP_ATOMIC);
4001da177e4SLinus Torvalds 	if (!skb)
4011da177e4SLinus Torvalds 		return;
4021da177e4SLinus Torvalds 
4031da177e4SLinus Torvalds 	serr = SKB_EXT_ERR(skb);
4041da177e4SLinus Torvalds 	serr->ee.ee_errno = err;
4051da177e4SLinus Torvalds 	serr->ee.ee_origin = SO_EE_ORIGIN_ICMP;
40688c7664fSArnaldo Carvalho de Melo 	serr->ee.ee_type = icmp_hdr(skb)->type;
40788c7664fSArnaldo Carvalho de Melo 	serr->ee.ee_code = icmp_hdr(skb)->code;
4081da177e4SLinus Torvalds 	serr->ee.ee_pad = 0;
4091da177e4SLinus Torvalds 	serr->ee.ee_info = info;
4101da177e4SLinus Torvalds 	serr->ee.ee_data = 0;
41188c7664fSArnaldo Carvalho de Melo 	serr->addr_offset = (u8 *)&(((struct iphdr *)(icmp_hdr(skb) + 1))->daddr) -
412d56f90a7SArnaldo Carvalho de Melo 				   skb_network_header(skb);
4131da177e4SLinus Torvalds 	serr->port = port;
4141da177e4SLinus Torvalds 
41500db4124SIan Morris 	if (skb_pull(skb, payload - skb->data)) {
416bd82393cSArnaldo Carvalho de Melo 		skb_reset_transport_header(skb);
417bd82393cSArnaldo Carvalho de Melo 		if (sock_queue_err_skb(sk, skb) == 0)
418bd82393cSArnaldo Carvalho de Melo 			return;
419bd82393cSArnaldo Carvalho de Melo 	}
4201da177e4SLinus Torvalds 	kfree_skb(skb);
4211da177e4SLinus Torvalds }
4221da177e4SLinus Torvalds 
4230579016eSAl Viro void ip_local_error(struct sock *sk, int err, __be32 daddr, __be16 port, u32 info)
4241da177e4SLinus Torvalds {
4251da177e4SLinus Torvalds 	struct inet_sock *inet = inet_sk(sk);
4261da177e4SLinus Torvalds 	struct sock_exterr_skb *serr;
4271da177e4SLinus Torvalds 	struct iphdr *iph;
4281da177e4SLinus Torvalds 	struct sk_buff *skb;
4291da177e4SLinus Torvalds 
4301da177e4SLinus Torvalds 	if (!inet->recverr)
4311da177e4SLinus Torvalds 		return;
4321da177e4SLinus Torvalds 
4331da177e4SLinus Torvalds 	skb = alloc_skb(sizeof(struct iphdr), GFP_ATOMIC);
4341da177e4SLinus Torvalds 	if (!skb)
4351da177e4SLinus Torvalds 		return;
4361da177e4SLinus Torvalds 
4372ca9e6f2SArnaldo Carvalho de Melo 	skb_put(skb, sizeof(struct iphdr));
4382ca9e6f2SArnaldo Carvalho de Melo 	skb_reset_network_header(skb);
439eddc9ec5SArnaldo Carvalho de Melo 	iph = ip_hdr(skb);
4401da177e4SLinus Torvalds 	iph->daddr = daddr;
4411da177e4SLinus Torvalds 
4421da177e4SLinus Torvalds 	serr = SKB_EXT_ERR(skb);
4431da177e4SLinus Torvalds 	serr->ee.ee_errno = err;
4441da177e4SLinus Torvalds 	serr->ee.ee_origin = SO_EE_ORIGIN_LOCAL;
4451da177e4SLinus Torvalds 	serr->ee.ee_type = 0;
4461da177e4SLinus Torvalds 	serr->ee.ee_code = 0;
4471da177e4SLinus Torvalds 	serr->ee.ee_pad = 0;
4481da177e4SLinus Torvalds 	serr->ee.ee_info = info;
4491da177e4SLinus Torvalds 	serr->ee.ee_data = 0;
450d56f90a7SArnaldo Carvalho de Melo 	serr->addr_offset = (u8 *)&iph->daddr - skb_network_header(skb);
4511da177e4SLinus Torvalds 	serr->port = port;
4521da177e4SLinus Torvalds 
45327a884dcSArnaldo Carvalho de Melo 	__skb_pull(skb, skb_tail_pointer(skb) - skb->data);
454bd82393cSArnaldo Carvalho de Melo 	skb_reset_transport_header(skb);
4551da177e4SLinus Torvalds 
4561da177e4SLinus Torvalds 	if (sock_queue_err_skb(sk, skb))
4571da177e4SLinus Torvalds 		kfree_skb(skb);
4581da177e4SLinus Torvalds }
4591da177e4SLinus Torvalds 
46034b99df4SJulian Anastasov /* For some errors we have valid addr_offset even with zero payload and
46134b99df4SJulian Anastasov  * zero port. Also, addr_offset should be supported if port is set.
46234b99df4SJulian Anastasov  */
46334b99df4SJulian Anastasov static inline bool ipv4_datagram_support_addr(struct sock_exterr_skb *serr)
46434b99df4SJulian Anastasov {
46534b99df4SJulian Anastasov 	return serr->ee.ee_origin == SO_EE_ORIGIN_ICMP ||
46634b99df4SJulian Anastasov 	       serr->ee.ee_origin == SO_EE_ORIGIN_LOCAL || serr->port;
46734b99df4SJulian Anastasov }
46834b99df4SJulian Anastasov 
469c247f053SWillem de Bruijn /* IPv4 supports cmsg on all imcp errors and some timestamps
470c247f053SWillem de Bruijn  *
471c247f053SWillem de Bruijn  * Timestamp code paths do not initialize the fields expected by cmsg:
472c247f053SWillem de Bruijn  * the PKTINFO fields in skb->cb[]. Fill those in here.
473c247f053SWillem de Bruijn  */
474c247f053SWillem de Bruijn static bool ipv4_datagram_support_cmsg(const struct sock *sk,
475c247f053SWillem de Bruijn 				       struct sk_buff *skb,
476829ae9d6SWillem de Bruijn 				       int ee_origin)
477829ae9d6SWillem de Bruijn {
478c247f053SWillem de Bruijn 	struct in_pktinfo *info;
479829ae9d6SWillem de Bruijn 
480c247f053SWillem de Bruijn 	if (ee_origin == SO_EE_ORIGIN_ICMP)
481c247f053SWillem de Bruijn 		return true;
482c247f053SWillem de Bruijn 
483c247f053SWillem de Bruijn 	if (ee_origin == SO_EE_ORIGIN_LOCAL)
484c247f053SWillem de Bruijn 		return false;
485c247f053SWillem de Bruijn 
486c247f053SWillem de Bruijn 	/* Support IP_PKTINFO on tstamp packets if requested, to correlate
4871862d620SWillem de Bruijn 	 * timestamp with egress dev. Not possible for packets without iif
488c247f053SWillem de Bruijn 	 * or without payload (SOF_TIMESTAMPING_OPT_TSONLY).
489c247f053SWillem de Bruijn 	 */
4901862d620SWillem de Bruijn 	info = PKTINFO_SKB_CB(skb);
4911862d620SWillem de Bruijn 	if (!(sk->sk_tsflags & SOF_TIMESTAMPING_OPT_CMSG) ||
4921862d620SWillem de Bruijn 	    !info->ipi_ifindex)
493829ae9d6SWillem de Bruijn 		return false;
494829ae9d6SWillem de Bruijn 
495829ae9d6SWillem de Bruijn 	info->ipi_spec_dst.s_addr = ip_hdr(skb)->saddr;
496829ae9d6SWillem de Bruijn 	return true;
497829ae9d6SWillem de Bruijn }
498829ae9d6SWillem de Bruijn 
4991da177e4SLinus Torvalds /*
5001da177e4SLinus Torvalds  *	Handle MSG_ERRQUEUE
5011da177e4SLinus Torvalds  */
50285fbaa75SHannes Frederic Sowa int ip_recv_error(struct sock *sk, struct msghdr *msg, int len, int *addr_len)
5031da177e4SLinus Torvalds {
5041da177e4SLinus Torvalds 	struct sock_exterr_skb *serr;
505364a9e93SWillem de Bruijn 	struct sk_buff *skb;
506342dfc30SSteffen Hurrle 	DECLARE_SOCKADDR(struct sockaddr_in *, sin, msg->msg_name);
5071da177e4SLinus Torvalds 	struct {
5081da177e4SLinus Torvalds 		struct sock_extended_err ee;
5091da177e4SLinus Torvalds 		struct sockaddr_in	 offender;
5101da177e4SLinus Torvalds 	} errhdr;
5111da177e4SLinus Torvalds 	int err;
5121da177e4SLinus Torvalds 	int copied;
5131da177e4SLinus Torvalds 
5147ce875e5SWillem de Bruijn 	WARN_ON_ONCE(sk->sk_family == AF_INET6);
5157ce875e5SWillem de Bruijn 
5161da177e4SLinus Torvalds 	err = -EAGAIN;
517364a9e93SWillem de Bruijn 	skb = sock_dequeue_err_skb(sk);
51851456b29SIan Morris 	if (!skb)
5191da177e4SLinus Torvalds 		goto out;
5201da177e4SLinus Torvalds 
5211da177e4SLinus Torvalds 	copied = skb->len;
5221da177e4SLinus Torvalds 	if (copied > len) {
5231da177e4SLinus Torvalds 		msg->msg_flags |= MSG_TRUNC;
5241da177e4SLinus Torvalds 		copied = len;
5251da177e4SLinus Torvalds 	}
52651f3d02bSDavid S. Miller 	err = skb_copy_datagram_msg(skb, 0, msg, copied);
527960a2628SEric Dumazet 	if (unlikely(err)) {
528960a2628SEric Dumazet 		kfree_skb(skb);
529960a2628SEric Dumazet 		return err;
530960a2628SEric Dumazet 	}
5311da177e4SLinus Torvalds 	sock_recv_timestamp(msg, sk, skb);
5321da177e4SLinus Torvalds 
5331da177e4SLinus Torvalds 	serr = SKB_EXT_ERR(skb);
5341da177e4SLinus Torvalds 
53534b99df4SJulian Anastasov 	if (sin && ipv4_datagram_support_addr(serr)) {
5361da177e4SLinus Torvalds 		sin->sin_family = AF_INET;
537d56f90a7SArnaldo Carvalho de Melo 		sin->sin_addr.s_addr = *(__be32 *)(skb_network_header(skb) +
538d56f90a7SArnaldo Carvalho de Melo 						   serr->addr_offset);
5391da177e4SLinus Torvalds 		sin->sin_port = serr->port;
5401da177e4SLinus Torvalds 		memset(&sin->sin_zero, 0, sizeof(sin->sin_zero));
54185fbaa75SHannes Frederic Sowa 		*addr_len = sizeof(*sin);
5421da177e4SLinus Torvalds 	}
5431da177e4SLinus Torvalds 
5441da177e4SLinus Torvalds 	memcpy(&errhdr.ee, &serr->ee, sizeof(struct sock_extended_err));
5451da177e4SLinus Torvalds 	sin = &errhdr.offender;
546f812116bSWillem de Bruijn 	memset(sin, 0, sizeof(*sin));
547829ae9d6SWillem de Bruijn 
548c247f053SWillem de Bruijn 	if (ipv4_datagram_support_cmsg(sk, skb, serr->ee.ee_origin)) {
5491da177e4SLinus Torvalds 		sin->sin_family = AF_INET;
550eddc9ec5SArnaldo Carvalho de Melo 		sin->sin_addr.s_addr = ip_hdr(skb)->saddr;
551f812116bSWillem de Bruijn 		if (inet_sk(sk)->cmsg_flags)
5521da177e4SLinus Torvalds 			ip_cmsg_recv(msg, skb);
5531da177e4SLinus Torvalds 	}
5541da177e4SLinus Torvalds 
5551da177e4SLinus Torvalds 	put_cmsg(msg, SOL_IP, IP_RECVERR, sizeof(errhdr), &errhdr);
5561da177e4SLinus Torvalds 
5571da177e4SLinus Torvalds 	/* Now we could try to dump offended packet options */
5581da177e4SLinus Torvalds 
5591da177e4SLinus Torvalds 	msg->msg_flags |= MSG_ERRQUEUE;
5601da177e4SLinus Torvalds 	err = copied;
5611da177e4SLinus Torvalds 
562960a2628SEric Dumazet 	consume_skb(skb);
5631da177e4SLinus Torvalds out:
5641da177e4SLinus Torvalds 	return err;
5651da177e4SLinus Torvalds }
5661da177e4SLinus Torvalds 
5671da177e4SLinus Torvalds 
5681da177e4SLinus Torvalds /*
5694d52cfbeSEric Dumazet  *	Socket option code for IP. This is the end of the line after any
5704d52cfbeSEric Dumazet  *	TCP,UDP etc options on an IP socket.
5711da177e4SLinus Torvalds  */
572baf606d9SMarcelo Ricardo Leitner static bool setsockopt_needs_rtnl(int optname)
573baf606d9SMarcelo Ricardo Leitner {
574baf606d9SMarcelo Ricardo Leitner 	switch (optname) {
575baf606d9SMarcelo Ricardo Leitner 	case IP_ADD_MEMBERSHIP:
576baf606d9SMarcelo Ricardo Leitner 	case IP_ADD_SOURCE_MEMBERSHIP:
57754ff9ef3SMarcelo Ricardo Leitner 	case IP_BLOCK_SOURCE:
578baf606d9SMarcelo Ricardo Leitner 	case IP_DROP_MEMBERSHIP:
57954ff9ef3SMarcelo Ricardo Leitner 	case IP_DROP_SOURCE_MEMBERSHIP:
58054ff9ef3SMarcelo Ricardo Leitner 	case IP_MSFILTER:
58154ff9ef3SMarcelo Ricardo Leitner 	case IP_UNBLOCK_SOURCE:
58254ff9ef3SMarcelo Ricardo Leitner 	case MCAST_BLOCK_SOURCE:
58354ff9ef3SMarcelo Ricardo Leitner 	case MCAST_MSFILTER:
584baf606d9SMarcelo Ricardo Leitner 	case MCAST_JOIN_GROUP:
58554ff9ef3SMarcelo Ricardo Leitner 	case MCAST_JOIN_SOURCE_GROUP:
586baf606d9SMarcelo Ricardo Leitner 	case MCAST_LEAVE_GROUP:
58754ff9ef3SMarcelo Ricardo Leitner 	case MCAST_LEAVE_SOURCE_GROUP:
58854ff9ef3SMarcelo Ricardo Leitner 	case MCAST_UNBLOCK_SOURCE:
5891215e51eSWANG Cong 	case IP_ROUTER_ALERT:
590baf606d9SMarcelo Ricardo Leitner 		return true;
591baf606d9SMarcelo Ricardo Leitner 	}
592baf606d9SMarcelo Ricardo Leitner 	return false;
593baf606d9SMarcelo Ricardo Leitner }
5941da177e4SLinus Torvalds 
5953fdadf7dSDmitry Mishin static int do_ip_setsockopt(struct sock *sk, int level,
596b7058842SDavid S. Miller 			    int optname, char __user *optval, unsigned int optlen)
5971da177e4SLinus Torvalds {
5981da177e4SLinus Torvalds 	struct inet_sock *inet = inet_sk(sk);
599166b6b2dSNikolay Borisov 	struct net *net = sock_net(sk);
6001da177e4SLinus Torvalds 	int val = 0, err;
601baf606d9SMarcelo Ricardo Leitner 	bool needs_rtnl = setsockopt_needs_rtnl(optname);
6021da177e4SLinus Torvalds 
6030c9f79beSXi Wang 	switch (optname) {
6040c9f79beSXi Wang 	case IP_PKTINFO:
6050c9f79beSXi Wang 	case IP_RECVTTL:
6060c9f79beSXi Wang 	case IP_RECVOPTS:
6070c9f79beSXi Wang 	case IP_RECVTOS:
6080c9f79beSXi Wang 	case IP_RETOPTS:
6090c9f79beSXi Wang 	case IP_TOS:
6100c9f79beSXi Wang 	case IP_TTL:
6110c9f79beSXi Wang 	case IP_HDRINCL:
6120c9f79beSXi Wang 	case IP_MTU_DISCOVER:
6130c9f79beSXi Wang 	case IP_RECVERR:
6140c9f79beSXi Wang 	case IP_ROUTER_ALERT:
6150c9f79beSXi Wang 	case IP_FREEBIND:
6160c9f79beSXi Wang 	case IP_PASSSEC:
6170c9f79beSXi Wang 	case IP_TRANSPARENT:
6180c9f79beSXi Wang 	case IP_MINTTL:
6190c9f79beSXi Wang 	case IP_NODEFRAG:
62090c337daSEric Dumazet 	case IP_BIND_ADDRESS_NO_PORT:
6210c9f79beSXi Wang 	case IP_UNICAST_IF:
6220c9f79beSXi Wang 	case IP_MULTICAST_TTL:
6230c9f79beSXi Wang 	case IP_MULTICAST_ALL:
6240c9f79beSXi Wang 	case IP_MULTICAST_LOOP:
6250c9f79beSXi Wang 	case IP_RECVORIGDSTADDR:
626ad6f939aSTom Herbert 	case IP_CHECKSUM:
62770ecc248SWillem de Bruijn 	case IP_RECVFRAGSIZE:
6281da177e4SLinus Torvalds 		if (optlen >= sizeof(int)) {
6291da177e4SLinus Torvalds 			if (get_user(val, (int __user *) optval))
6301da177e4SLinus Torvalds 				return -EFAULT;
6311da177e4SLinus Torvalds 		} else if (optlen >= sizeof(char)) {
6321da177e4SLinus Torvalds 			unsigned char ucval;
6331da177e4SLinus Torvalds 
6341da177e4SLinus Torvalds 			if (get_user(ucval, (unsigned char __user *) optval))
6351da177e4SLinus Torvalds 				return -EFAULT;
6361da177e4SLinus Torvalds 			val = (int) ucval;
6371da177e4SLinus Torvalds 		}
6381da177e4SLinus Torvalds 	}
6391da177e4SLinus Torvalds 
6401da177e4SLinus Torvalds 	/* If optlen==0, it is equivalent to val == 0 */
6411da177e4SLinus Torvalds 
6426a9fb947SPavel Emelyanov 	if (ip_mroute_opt(optname))
6431da177e4SLinus Torvalds 		return ip_mroute_setsockopt(sk, optname, optval, optlen);
6441da177e4SLinus Torvalds 
6451da177e4SLinus Torvalds 	err = 0;
646baf606d9SMarcelo Ricardo Leitner 	if (needs_rtnl)
647baf606d9SMarcelo Ricardo Leitner 		rtnl_lock();
6481da177e4SLinus Torvalds 	lock_sock(sk);
6491da177e4SLinus Torvalds 
6501da177e4SLinus Torvalds 	switch (optname) {
6511da177e4SLinus Torvalds 	case IP_OPTIONS:
6521da177e4SLinus Torvalds 	{
653f6d8bd05SEric Dumazet 		struct ip_options_rcu *old, *opt = NULL;
654f6d8bd05SEric Dumazet 
65565a1c4ffSroel kluin 		if (optlen > 40)
6561da177e4SLinus Torvalds 			goto e_inval;
6573b1e0a65SYOSHIFUJI Hideaki 		err = ip_options_get_from_user(sock_net(sk), &opt,
658cb84663eSDenis V. Lunev 					       optval, optlen);
6591da177e4SLinus Torvalds 		if (err)
6601da177e4SLinus Torvalds 			break;
661f6d8bd05SEric Dumazet 		old = rcu_dereference_protected(inet->inet_opt,
6621e1d04e6SHannes Frederic Sowa 						lockdep_sock_is_held(sk));
663d83d8461SArnaldo Carvalho de Melo 		if (inet->is_icsk) {
664d83d8461SArnaldo Carvalho de Melo 			struct inet_connection_sock *icsk = inet_csk(sk);
665dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6)
6661da177e4SLinus Torvalds 			if (sk->sk_family == PF_INET ||
6671da177e4SLinus Torvalds 			    (!((1 << sk->sk_state) &
6681da177e4SLinus Torvalds 			       (TCPF_LISTEN | TCPF_CLOSE)) &&
669c720c7e8SEric Dumazet 			     inet->inet_daddr != LOOPBACK4_IPV6)) {
6701da177e4SLinus Torvalds #endif
671f6d8bd05SEric Dumazet 				if (old)
672f6d8bd05SEric Dumazet 					icsk->icsk_ext_hdr_len -= old->opt.optlen;
6731da177e4SLinus Torvalds 				if (opt)
674f6d8bd05SEric Dumazet 					icsk->icsk_ext_hdr_len += opt->opt.optlen;
675d83d8461SArnaldo Carvalho de Melo 				icsk->icsk_sync_mss(sk, icsk->icsk_pmtu_cookie);
676dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6)
6771da177e4SLinus Torvalds 			}
6781da177e4SLinus Torvalds #endif
6791da177e4SLinus Torvalds 		}
680f6d8bd05SEric Dumazet 		rcu_assign_pointer(inet->inet_opt, opt);
681f6d8bd05SEric Dumazet 		if (old)
682605b4afeSPaul E. McKenney 			kfree_rcu(old, rcu);
6831da177e4SLinus Torvalds 		break;
6841da177e4SLinus Torvalds 	}
6851da177e4SLinus Torvalds 	case IP_PKTINFO:
6861da177e4SLinus Torvalds 		if (val)
6871da177e4SLinus Torvalds 			inet->cmsg_flags |= IP_CMSG_PKTINFO;
6881da177e4SLinus Torvalds 		else
6891da177e4SLinus Torvalds 			inet->cmsg_flags &= ~IP_CMSG_PKTINFO;
6901da177e4SLinus Torvalds 		break;
6911da177e4SLinus Torvalds 	case IP_RECVTTL:
6921da177e4SLinus Torvalds 		if (val)
6931da177e4SLinus Torvalds 			inet->cmsg_flags |=  IP_CMSG_TTL;
6941da177e4SLinus Torvalds 		else
6951da177e4SLinus Torvalds 			inet->cmsg_flags &= ~IP_CMSG_TTL;
6961da177e4SLinus Torvalds 		break;
6971da177e4SLinus Torvalds 	case IP_RECVTOS:
6981da177e4SLinus Torvalds 		if (val)
6991da177e4SLinus Torvalds 			inet->cmsg_flags |=  IP_CMSG_TOS;
7001da177e4SLinus Torvalds 		else
7011da177e4SLinus Torvalds 			inet->cmsg_flags &= ~IP_CMSG_TOS;
7021da177e4SLinus Torvalds 		break;
7031da177e4SLinus Torvalds 	case IP_RECVOPTS:
7041da177e4SLinus Torvalds 		if (val)
7051da177e4SLinus Torvalds 			inet->cmsg_flags |=  IP_CMSG_RECVOPTS;
7061da177e4SLinus Torvalds 		else
7071da177e4SLinus Torvalds 			inet->cmsg_flags &= ~IP_CMSG_RECVOPTS;
7081da177e4SLinus Torvalds 		break;
7091da177e4SLinus Torvalds 	case IP_RETOPTS:
7101da177e4SLinus Torvalds 		if (val)
7111da177e4SLinus Torvalds 			inet->cmsg_flags |= IP_CMSG_RETOPTS;
7121da177e4SLinus Torvalds 		else
7131da177e4SLinus Torvalds 			inet->cmsg_flags &= ~IP_CMSG_RETOPTS;
7141da177e4SLinus Torvalds 		break;
7152c7946a7SCatherine Zhang 	case IP_PASSSEC:
7162c7946a7SCatherine Zhang 		if (val)
7172c7946a7SCatherine Zhang 			inet->cmsg_flags |= IP_CMSG_PASSSEC;
7182c7946a7SCatherine Zhang 		else
7192c7946a7SCatherine Zhang 			inet->cmsg_flags &= ~IP_CMSG_PASSSEC;
7202c7946a7SCatherine Zhang 		break;
721e8b2dfe9SBalazs Scheidler 	case IP_RECVORIGDSTADDR:
722e8b2dfe9SBalazs Scheidler 		if (val)
723e8b2dfe9SBalazs Scheidler 			inet->cmsg_flags |= IP_CMSG_ORIGDSTADDR;
724e8b2dfe9SBalazs Scheidler 		else
725e8b2dfe9SBalazs Scheidler 			inet->cmsg_flags &= ~IP_CMSG_ORIGDSTADDR;
726e8b2dfe9SBalazs Scheidler 		break;
727ad6f939aSTom Herbert 	case IP_CHECKSUM:
728ad6f939aSTom Herbert 		if (val) {
729ad6f939aSTom Herbert 			if (!(inet->cmsg_flags & IP_CMSG_CHECKSUM)) {
730ad6f939aSTom Herbert 				inet_inc_convert_csum(sk);
731ad6f939aSTom Herbert 				inet->cmsg_flags |= IP_CMSG_CHECKSUM;
732ad6f939aSTom Herbert 			}
733ad6f939aSTom Herbert 		} else {
734ad6f939aSTom Herbert 			if (inet->cmsg_flags & IP_CMSG_CHECKSUM) {
735ad6f939aSTom Herbert 				inet_dec_convert_csum(sk);
736ad6f939aSTom Herbert 				inet->cmsg_flags &= ~IP_CMSG_CHECKSUM;
737ad6f939aSTom Herbert 			}
738ad6f939aSTom Herbert 		}
739ad6f939aSTom Herbert 		break;
74070ecc248SWillem de Bruijn 	case IP_RECVFRAGSIZE:
74170ecc248SWillem de Bruijn 		if (sk->sk_type != SOCK_RAW && sk->sk_type != SOCK_DGRAM)
74270ecc248SWillem de Bruijn 			goto e_inval;
74370ecc248SWillem de Bruijn 		if (val)
74470ecc248SWillem de Bruijn 			inet->cmsg_flags |= IP_CMSG_RECVFRAGSIZE;
74570ecc248SWillem de Bruijn 		else
74670ecc248SWillem de Bruijn 			inet->cmsg_flags &= ~IP_CMSG_RECVFRAGSIZE;
74770ecc248SWillem de Bruijn 		break;
7481da177e4SLinus Torvalds 	case IP_TOS:	/* This sets both TOS and Precedence */
7491da177e4SLinus Torvalds 		if (sk->sk_type == SOCK_STREAM) {
7502c67e9acSMaciej Żenczykowski 			val &= ~INET_ECN_MASK;
7512c67e9acSMaciej Żenczykowski 			val |= inet->tos & INET_ECN_MASK;
7521da177e4SLinus Torvalds 		}
7531da177e4SLinus Torvalds 		if (inet->tos != val) {
7541da177e4SLinus Torvalds 			inet->tos = val;
7551da177e4SLinus Torvalds 			sk->sk_priority = rt_tos2priority(val);
7561da177e4SLinus Torvalds 			sk_dst_reset(sk);
7571da177e4SLinus Torvalds 		}
7581da177e4SLinus Torvalds 		break;
7591da177e4SLinus Torvalds 	case IP_TTL:
7601da177e4SLinus Torvalds 		if (optlen < 1)
7611da177e4SLinus Torvalds 			goto e_inval;
762c9be4a5cSCong Wang 		if (val != -1 && (val < 1 || val > 255))
7631da177e4SLinus Torvalds 			goto e_inval;
7641da177e4SLinus Torvalds 		inet->uc_ttl = val;
7651da177e4SLinus Torvalds 		break;
7661da177e4SLinus Torvalds 	case IP_HDRINCL:
7671da177e4SLinus Torvalds 		if (sk->sk_type != SOCK_RAW) {
7681da177e4SLinus Torvalds 			err = -ENOPROTOOPT;
7691da177e4SLinus Torvalds 			break;
7701da177e4SLinus Torvalds 		}
7711da177e4SLinus Torvalds 		inet->hdrincl = val ? 1 : 0;
7721da177e4SLinus Torvalds 		break;
7737b2ff18eSJiri Olsa 	case IP_NODEFRAG:
7747b2ff18eSJiri Olsa 		if (sk->sk_type != SOCK_RAW) {
7757b2ff18eSJiri Olsa 			err = -ENOPROTOOPT;
7767b2ff18eSJiri Olsa 			break;
7777b2ff18eSJiri Olsa 		}
7787b2ff18eSJiri Olsa 		inet->nodefrag = val ? 1 : 0;
7797b2ff18eSJiri Olsa 		break;
78090c337daSEric Dumazet 	case IP_BIND_ADDRESS_NO_PORT:
78190c337daSEric Dumazet 		inet->bind_address_no_port = val ? 1 : 0;
78290c337daSEric Dumazet 		break;
7831da177e4SLinus Torvalds 	case IP_MTU_DISCOVER:
7841b346576SHannes Frederic Sowa 		if (val < IP_PMTUDISC_DONT || val > IP_PMTUDISC_OMIT)
7851da177e4SLinus Torvalds 			goto e_inval;
7861da177e4SLinus Torvalds 		inet->pmtudisc = val;
7871da177e4SLinus Torvalds 		break;
7881da177e4SLinus Torvalds 	case IP_RECVERR:
7891da177e4SLinus Torvalds 		inet->recverr = !!val;
7901da177e4SLinus Torvalds 		if (!val)
7911da177e4SLinus Torvalds 			skb_queue_purge(&sk->sk_error_queue);
7921da177e4SLinus Torvalds 		break;
7931da177e4SLinus Torvalds 	case IP_MULTICAST_TTL:
7941da177e4SLinus Torvalds 		if (sk->sk_type == SOCK_STREAM)
7951da177e4SLinus Torvalds 			goto e_inval;
7961da177e4SLinus Torvalds 		if (optlen < 1)
7971da177e4SLinus Torvalds 			goto e_inval;
7981da177e4SLinus Torvalds 		if (val == -1)
7991da177e4SLinus Torvalds 			val = 1;
8001da177e4SLinus Torvalds 		if (val < 0 || val > 255)
8011da177e4SLinus Torvalds 			goto e_inval;
8021da177e4SLinus Torvalds 		inet->mc_ttl = val;
8031da177e4SLinus Torvalds 		break;
8041da177e4SLinus Torvalds 	case IP_MULTICAST_LOOP:
8051da177e4SLinus Torvalds 		if (optlen < 1)
8061da177e4SLinus Torvalds 			goto e_inval;
8071da177e4SLinus Torvalds 		inet->mc_loop = !!val;
8081da177e4SLinus Torvalds 		break;
80976e21053SErich E. Hoover 	case IP_UNICAST_IF:
81076e21053SErich E. Hoover 	{
81176e21053SErich E. Hoover 		struct net_device *dev = NULL;
81276e21053SErich E. Hoover 		int ifindex;
8139515a2e0SDavid Ahern 		int midx;
81476e21053SErich E. Hoover 
81576e21053SErich E. Hoover 		if (optlen != sizeof(int))
81676e21053SErich E. Hoover 			goto e_inval;
81776e21053SErich E. Hoover 
81876e21053SErich E. Hoover 		ifindex = (__force int)ntohl((__force __be32)val);
81976e21053SErich E. Hoover 		if (ifindex == 0) {
82076e21053SErich E. Hoover 			inet->uc_index = 0;
82176e21053SErich E. Hoover 			err = 0;
82276e21053SErich E. Hoover 			break;
82376e21053SErich E. Hoover 		}
82476e21053SErich E. Hoover 
82576e21053SErich E. Hoover 		dev = dev_get_by_index(sock_net(sk), ifindex);
82676e21053SErich E. Hoover 		err = -EADDRNOTAVAIL;
82776e21053SErich E. Hoover 		if (!dev)
82876e21053SErich E. Hoover 			break;
8299515a2e0SDavid Ahern 
8309515a2e0SDavid Ahern 		midx = l3mdev_master_ifindex(dev);
83176e21053SErich E. Hoover 		dev_put(dev);
83276e21053SErich E. Hoover 
83376e21053SErich E. Hoover 		err = -EINVAL;
8349515a2e0SDavid Ahern 		if (sk->sk_bound_dev_if &&
8359515a2e0SDavid Ahern 		    (!midx || midx != 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 		}
945a2c841d9SAl Viro 		msf = memdup_user(optval, optlen);
946a2c841d9SAl Viro 		if (IS_ERR(msf)) {
947a2c841d9SAl Viro 			err = PTR_ERR(msf);
9481da177e4SLinus Torvalds 			break;
9491da177e4SLinus Torvalds 		}
9501da177e4SLinus Torvalds 		/* numsrc >= (1G-4) overflow in 32 bits */
9511da177e4SLinus Torvalds 		if (msf->imsf_numsrc >= 0x3ffffffcU ||
952166b6b2dSNikolay Borisov 		    msf->imsf_numsrc > net->ipv4.sysctl_igmp_max_msf) {
9531da177e4SLinus Torvalds 			kfree(msf);
9541da177e4SLinus Torvalds 			err = -ENOBUFS;
9551da177e4SLinus Torvalds 			break;
9561da177e4SLinus Torvalds 		}
9571da177e4SLinus Torvalds 		if (IP_MSFILTER_SIZE(msf->imsf_numsrc) > optlen) {
9581da177e4SLinus Torvalds 			kfree(msf);
9591da177e4SLinus Torvalds 			err = -EINVAL;
9601da177e4SLinus Torvalds 			break;
9611da177e4SLinus Torvalds 		}
9621da177e4SLinus Torvalds 		err = ip_mc_msfilter(sk, msf, 0);
9631da177e4SLinus Torvalds 		kfree(msf);
9641da177e4SLinus Torvalds 		break;
9651da177e4SLinus Torvalds 	}
9661da177e4SLinus Torvalds 	case IP_BLOCK_SOURCE:
9671da177e4SLinus Torvalds 	case IP_UNBLOCK_SOURCE:
9681da177e4SLinus Torvalds 	case IP_ADD_SOURCE_MEMBERSHIP:
9691da177e4SLinus Torvalds 	case IP_DROP_SOURCE_MEMBERSHIP:
9701da177e4SLinus Torvalds 	{
9711da177e4SLinus Torvalds 		struct ip_mreq_source mreqs;
9721da177e4SLinus Torvalds 		int omode, add;
9731da177e4SLinus Torvalds 
9741da177e4SLinus Torvalds 		if (optlen != sizeof(struct ip_mreq_source))
9751da177e4SLinus Torvalds 			goto e_inval;
9761da177e4SLinus Torvalds 		if (copy_from_user(&mreqs, optval, sizeof(mreqs))) {
9771da177e4SLinus Torvalds 			err = -EFAULT;
9781da177e4SLinus Torvalds 			break;
9791da177e4SLinus Torvalds 		}
9801da177e4SLinus Torvalds 		if (optname == IP_BLOCK_SOURCE) {
9811da177e4SLinus Torvalds 			omode = MCAST_EXCLUDE;
9821da177e4SLinus Torvalds 			add = 1;
9831da177e4SLinus Torvalds 		} else if (optname == IP_UNBLOCK_SOURCE) {
9841da177e4SLinus Torvalds 			omode = MCAST_EXCLUDE;
9851da177e4SLinus Torvalds 			add = 0;
9861da177e4SLinus Torvalds 		} else if (optname == IP_ADD_SOURCE_MEMBERSHIP) {
9871da177e4SLinus Torvalds 			struct ip_mreqn mreq;
9881da177e4SLinus Torvalds 
9891da177e4SLinus Torvalds 			mreq.imr_multiaddr.s_addr = mreqs.imr_multiaddr;
9901da177e4SLinus Torvalds 			mreq.imr_address.s_addr = mreqs.imr_interface;
9911da177e4SLinus Torvalds 			mreq.imr_ifindex = 0;
99254ff9ef3SMarcelo Ricardo Leitner 			err = ip_mc_join_group(sk, &mreq);
9938cdaaa15SDavid L Stevens 			if (err && err != -EADDRINUSE)
9941da177e4SLinus Torvalds 				break;
9951da177e4SLinus Torvalds 			omode = MCAST_INCLUDE;
9961da177e4SLinus Torvalds 			add = 1;
9971da177e4SLinus Torvalds 		} else /* IP_DROP_SOURCE_MEMBERSHIP */ {
9981da177e4SLinus Torvalds 			omode = MCAST_INCLUDE;
9991da177e4SLinus Torvalds 			add = 0;
10001da177e4SLinus Torvalds 		}
10011da177e4SLinus Torvalds 		err = ip_mc_source(add, omode, sk, &mreqs, 0);
10021da177e4SLinus Torvalds 		break;
10031da177e4SLinus Torvalds 	}
10041da177e4SLinus Torvalds 	case MCAST_JOIN_GROUP:
10051da177e4SLinus Torvalds 	case MCAST_LEAVE_GROUP:
10061da177e4SLinus Torvalds 	{
10071da177e4SLinus Torvalds 		struct group_req greq;
10081da177e4SLinus Torvalds 		struct sockaddr_in *psin;
10091da177e4SLinus Torvalds 		struct ip_mreqn mreq;
10101da177e4SLinus Torvalds 
10111da177e4SLinus Torvalds 		if (optlen < sizeof(struct group_req))
10121da177e4SLinus Torvalds 			goto e_inval;
10131da177e4SLinus Torvalds 		err = -EFAULT;
10141da177e4SLinus Torvalds 		if (copy_from_user(&greq, optval, sizeof(greq)))
10151da177e4SLinus Torvalds 			break;
10161da177e4SLinus Torvalds 		psin = (struct sockaddr_in *)&greq.gr_group;
10171da177e4SLinus Torvalds 		if (psin->sin_family != AF_INET)
10181da177e4SLinus Torvalds 			goto e_inval;
10191da177e4SLinus Torvalds 		memset(&mreq, 0, sizeof(mreq));
10201da177e4SLinus Torvalds 		mreq.imr_multiaddr = psin->sin_addr;
10211da177e4SLinus Torvalds 		mreq.imr_ifindex = greq.gr_interface;
10221da177e4SLinus Torvalds 
10231da177e4SLinus Torvalds 		if (optname == MCAST_JOIN_GROUP)
102454ff9ef3SMarcelo Ricardo Leitner 			err = ip_mc_join_group(sk, &mreq);
10251da177e4SLinus Torvalds 		else
102654ff9ef3SMarcelo Ricardo Leitner 			err = ip_mc_leave_group(sk, &mreq);
10271da177e4SLinus Torvalds 		break;
10281da177e4SLinus Torvalds 	}
10291da177e4SLinus Torvalds 	case MCAST_JOIN_SOURCE_GROUP:
10301da177e4SLinus Torvalds 	case MCAST_LEAVE_SOURCE_GROUP:
10311da177e4SLinus Torvalds 	case MCAST_BLOCK_SOURCE:
10321da177e4SLinus Torvalds 	case MCAST_UNBLOCK_SOURCE:
10331da177e4SLinus Torvalds 	{
10341da177e4SLinus Torvalds 		struct group_source_req greqs;
10351da177e4SLinus Torvalds 		struct ip_mreq_source mreqs;
10361da177e4SLinus Torvalds 		struct sockaddr_in *psin;
10371da177e4SLinus Torvalds 		int omode, add;
10381da177e4SLinus Torvalds 
10391da177e4SLinus Torvalds 		if (optlen != sizeof(struct group_source_req))
10401da177e4SLinus Torvalds 			goto e_inval;
10411da177e4SLinus Torvalds 		if (copy_from_user(&greqs, optval, sizeof(greqs))) {
10421da177e4SLinus Torvalds 			err = -EFAULT;
10431da177e4SLinus Torvalds 			break;
10441da177e4SLinus Torvalds 		}
10451da177e4SLinus Torvalds 		if (greqs.gsr_group.ss_family != AF_INET ||
10461da177e4SLinus Torvalds 		    greqs.gsr_source.ss_family != AF_INET) {
10471da177e4SLinus Torvalds 			err = -EADDRNOTAVAIL;
10481da177e4SLinus Torvalds 			break;
10491da177e4SLinus Torvalds 		}
10501da177e4SLinus Torvalds 		psin = (struct sockaddr_in *)&greqs.gsr_group;
10511da177e4SLinus Torvalds 		mreqs.imr_multiaddr = psin->sin_addr.s_addr;
10521da177e4SLinus Torvalds 		psin = (struct sockaddr_in *)&greqs.gsr_source;
10531da177e4SLinus Torvalds 		mreqs.imr_sourceaddr = psin->sin_addr.s_addr;
10541da177e4SLinus Torvalds 		mreqs.imr_interface = 0; /* use index for mc_source */
10551da177e4SLinus Torvalds 
10561da177e4SLinus Torvalds 		if (optname == MCAST_BLOCK_SOURCE) {
10571da177e4SLinus Torvalds 			omode = MCAST_EXCLUDE;
10581da177e4SLinus Torvalds 			add = 1;
10591da177e4SLinus Torvalds 		} else if (optname == MCAST_UNBLOCK_SOURCE) {
10601da177e4SLinus Torvalds 			omode = MCAST_EXCLUDE;
10611da177e4SLinus Torvalds 			add = 0;
10621da177e4SLinus Torvalds 		} else if (optname == MCAST_JOIN_SOURCE_GROUP) {
10631da177e4SLinus Torvalds 			struct ip_mreqn mreq;
10641da177e4SLinus Torvalds 
10651da177e4SLinus Torvalds 			psin = (struct sockaddr_in *)&greqs.gsr_group;
10661da177e4SLinus Torvalds 			mreq.imr_multiaddr = psin->sin_addr;
10671da177e4SLinus Torvalds 			mreq.imr_address.s_addr = 0;
10681da177e4SLinus Torvalds 			mreq.imr_ifindex = greqs.gsr_interface;
106954ff9ef3SMarcelo Ricardo Leitner 			err = ip_mc_join_group(sk, &mreq);
10708cdaaa15SDavid L Stevens 			if (err && err != -EADDRINUSE)
10711da177e4SLinus Torvalds 				break;
10721da177e4SLinus Torvalds 			greqs.gsr_interface = mreq.imr_ifindex;
10731da177e4SLinus Torvalds 			omode = MCAST_INCLUDE;
10741da177e4SLinus Torvalds 			add = 1;
10751da177e4SLinus Torvalds 		} else /* MCAST_LEAVE_SOURCE_GROUP */ {
10761da177e4SLinus Torvalds 			omode = MCAST_INCLUDE;
10771da177e4SLinus Torvalds 			add = 0;
10781da177e4SLinus Torvalds 		}
10791da177e4SLinus Torvalds 		err = ip_mc_source(add, omode, sk, &mreqs,
10801da177e4SLinus Torvalds 				   greqs.gsr_interface);
10811da177e4SLinus Torvalds 		break;
10821da177e4SLinus Torvalds 	}
10831da177e4SLinus Torvalds 	case MCAST_MSFILTER:
10841da177e4SLinus Torvalds 	{
10851da177e4SLinus Torvalds 		struct sockaddr_in *psin;
10861da177e4SLinus Torvalds 		struct ip_msfilter *msf = NULL;
10871da177e4SLinus Torvalds 		struct group_filter *gsf = NULL;
10881da177e4SLinus Torvalds 		int msize, i, ifindex;
10891da177e4SLinus Torvalds 
10901da177e4SLinus Torvalds 		if (optlen < GROUP_FILTER_SIZE(0))
10911da177e4SLinus Torvalds 			goto e_inval;
10921da177e4SLinus Torvalds 		if (optlen > sysctl_optmem_max) {
10931da177e4SLinus Torvalds 			err = -ENOBUFS;
10941da177e4SLinus Torvalds 			break;
10951da177e4SLinus Torvalds 		}
1096a2c841d9SAl Viro 		gsf = memdup_user(optval, optlen);
1097a2c841d9SAl Viro 		if (IS_ERR(gsf)) {
1098a2c841d9SAl Viro 			err = PTR_ERR(gsf);
10991da177e4SLinus Torvalds 			break;
11001da177e4SLinus Torvalds 		}
11014d52cfbeSEric Dumazet 
11021da177e4SLinus Torvalds 		/* numsrc >= (4G-140)/128 overflow in 32 bits */
11031da177e4SLinus Torvalds 		if (gsf->gf_numsrc >= 0x1ffffff ||
1104166b6b2dSNikolay Borisov 		    gsf->gf_numsrc > net->ipv4.sysctl_igmp_max_msf) {
11051da177e4SLinus Torvalds 			err = -ENOBUFS;
11061da177e4SLinus Torvalds 			goto mc_msf_out;
11071da177e4SLinus Torvalds 		}
11081da177e4SLinus Torvalds 		if (GROUP_FILTER_SIZE(gsf->gf_numsrc) > optlen) {
11091da177e4SLinus Torvalds 			err = -EINVAL;
11101da177e4SLinus Torvalds 			goto mc_msf_out;
11111da177e4SLinus Torvalds 		}
11121da177e4SLinus Torvalds 		msize = IP_MSFILTER_SIZE(gsf->gf_numsrc);
11138b3a7005SKris Katterjohn 		msf = kmalloc(msize, GFP_KERNEL);
1114cfcabdccSStephen Hemminger 		if (!msf) {
11151da177e4SLinus Torvalds 			err = -ENOBUFS;
11161da177e4SLinus Torvalds 			goto mc_msf_out;
11171da177e4SLinus Torvalds 		}
11181da177e4SLinus Torvalds 		ifindex = gsf->gf_interface;
11191da177e4SLinus Torvalds 		psin = (struct sockaddr_in *)&gsf->gf_group;
11201da177e4SLinus Torvalds 		if (psin->sin_family != AF_INET) {
11211da177e4SLinus Torvalds 			err = -EADDRNOTAVAIL;
11221da177e4SLinus Torvalds 			goto mc_msf_out;
11231da177e4SLinus Torvalds 		}
11241da177e4SLinus Torvalds 		msf->imsf_multiaddr = psin->sin_addr.s_addr;
11251da177e4SLinus Torvalds 		msf->imsf_interface = 0;
11261da177e4SLinus Torvalds 		msf->imsf_fmode = gsf->gf_fmode;
11271da177e4SLinus Torvalds 		msf->imsf_numsrc = gsf->gf_numsrc;
11281da177e4SLinus Torvalds 		err = -EADDRNOTAVAIL;
11291da177e4SLinus Torvalds 		for (i = 0; i < gsf->gf_numsrc; ++i) {
11301da177e4SLinus Torvalds 			psin = (struct sockaddr_in *)&gsf->gf_slist[i];
11311da177e4SLinus Torvalds 
11321da177e4SLinus Torvalds 			if (psin->sin_family != AF_INET)
11331da177e4SLinus Torvalds 				goto mc_msf_out;
11341da177e4SLinus Torvalds 			msf->imsf_slist[i] = psin->sin_addr.s_addr;
11351da177e4SLinus Torvalds 		}
11361da177e4SLinus Torvalds 		kfree(gsf);
11371da177e4SLinus Torvalds 		gsf = NULL;
11381da177e4SLinus Torvalds 
11391da177e4SLinus Torvalds 		err = ip_mc_msfilter(sk, msf, ifindex);
11401da177e4SLinus Torvalds mc_msf_out:
11411da177e4SLinus Torvalds 		kfree(msf);
11421da177e4SLinus Torvalds 		kfree(gsf);
11431da177e4SLinus Torvalds 		break;
11441da177e4SLinus Torvalds 	}
1145f771bef9SNivedita Singhvi 	case IP_MULTICAST_ALL:
1146f771bef9SNivedita Singhvi 		if (optlen < 1)
1147f771bef9SNivedita Singhvi 			goto e_inval;
1148f771bef9SNivedita Singhvi 		if (val != 0 && val != 1)
1149f771bef9SNivedita Singhvi 			goto e_inval;
1150f771bef9SNivedita Singhvi 		inet->mc_all = val;
1151f771bef9SNivedita Singhvi 		break;
11521da177e4SLinus Torvalds 	case IP_ROUTER_ALERT:
11531da177e4SLinus Torvalds 		err = ip_ra_control(sk, val ? 1 : 0, NULL);
11541da177e4SLinus Torvalds 		break;
11551da177e4SLinus Torvalds 
11561da177e4SLinus Torvalds 	case IP_FREEBIND:
11571da177e4SLinus Torvalds 		if (optlen < 1)
11581da177e4SLinus Torvalds 			goto e_inval;
11591da177e4SLinus Torvalds 		inet->freebind = !!val;
11601da177e4SLinus Torvalds 		break;
11611da177e4SLinus Torvalds 
11621da177e4SLinus Torvalds 	case IP_IPSEC_POLICY:
11631da177e4SLinus Torvalds 	case IP_XFRM_POLICY:
11646fc0b4a7SHerbert Xu 		err = -EPERM;
116552e804c6SEric W. Biederman 		if (!ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN))
11666fc0b4a7SHerbert Xu 			break;
11671da177e4SLinus Torvalds 		err = xfrm_user_policy(sk, optname, optval, optlen);
11681da177e4SLinus Torvalds 		break;
11691da177e4SLinus Torvalds 
1170f5715aeaSKOVACS Krisztian 	case IP_TRANSPARENT:
117152e804c6SEric W. Biederman 		if (!!val && !ns_capable(sock_net(sk)->user_ns, CAP_NET_RAW) &&
117252e804c6SEric W. Biederman 		    !ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN)) {
1173f5715aeaSKOVACS Krisztian 			err = -EPERM;
1174f5715aeaSKOVACS Krisztian 			break;
1175f5715aeaSKOVACS Krisztian 		}
1176f5715aeaSKOVACS Krisztian 		if (optlen < 1)
1177f5715aeaSKOVACS Krisztian 			goto e_inval;
1178f5715aeaSKOVACS Krisztian 		inet->transparent = !!val;
1179f5715aeaSKOVACS Krisztian 		break;
1180f5715aeaSKOVACS Krisztian 
1181d218d111SStephen Hemminger 	case IP_MINTTL:
1182d218d111SStephen Hemminger 		if (optlen < 1)
1183d218d111SStephen Hemminger 			goto e_inval;
1184d218d111SStephen Hemminger 		if (val < 0 || val > 255)
1185d218d111SStephen Hemminger 			goto e_inval;
1186d218d111SStephen Hemminger 		inet->min_ttl = val;
1187d218d111SStephen Hemminger 		break;
1188d218d111SStephen Hemminger 
11891da177e4SLinus Torvalds 	default:
11901da177e4SLinus Torvalds 		err = -ENOPROTOOPT;
11911da177e4SLinus Torvalds 		break;
11921da177e4SLinus Torvalds 	}
11931da177e4SLinus Torvalds 	release_sock(sk);
1194baf606d9SMarcelo Ricardo Leitner 	if (needs_rtnl)
1195baf606d9SMarcelo Ricardo Leitner 		rtnl_unlock();
11961da177e4SLinus Torvalds 	return err;
11971da177e4SLinus Torvalds 
11981da177e4SLinus Torvalds e_inval:
11991da177e4SLinus Torvalds 	release_sock(sk);
1200baf606d9SMarcelo Ricardo Leitner 	if (needs_rtnl)
1201baf606d9SMarcelo Ricardo Leitner 		rtnl_unlock();
12021da177e4SLinus Torvalds 	return -EINVAL;
12031da177e4SLinus Torvalds }
12041da177e4SLinus Torvalds 
1205f84af32cSEric Dumazet /**
1206829ae9d6SWillem de Bruijn  * ipv4_pktinfo_prepare - transfer some info from rtable to skb
1207f84af32cSEric Dumazet  * @sk: socket
1208f84af32cSEric Dumazet  * @skb: buffer
1209f84af32cSEric Dumazet  *
121035ebf65eSDavid S. Miller  * To support IP_CMSG_PKTINFO option, we store rt_iif and specific
121135ebf65eSDavid S. Miller  * destination in skb->cb[] before dst drop.
12128e3bff96Sstephen hemminger  * This way, receiver doesn't make cache line misses to read rtable.
1213f84af32cSEric Dumazet  */
1214fbf8866dSShawn Bohrer void ipv4_pktinfo_prepare(const struct sock *sk, struct sk_buff *skb)
1215f84af32cSEric Dumazet {
1216d826eb14SEric Dumazet 	struct in_pktinfo *pktinfo = PKTINFO_SKB_CB(skb);
12174b261c75SHannes Frederic Sowa 	bool prepare = (inet_sk(sk)->cmsg_flags & IP_CMSG_PKTINFO) ||
12184b261c75SHannes Frederic Sowa 		       ipv6_sk_rxinfo(sk);
1219d826eb14SEric Dumazet 
12204b261c75SHannes Frederic Sowa 	if (prepare && skb_rtable(skb)) {
12210b922b7aSDavid Ahern 		/* skb->cb is overloaded: prior to this point it is IP{6}CB
12220b922b7aSDavid Ahern 		 * which has interface index (iif) as the first member of the
12230b922b7aSDavid Ahern 		 * underlying inet{6}_skb_parm struct. This code then overlays
12240b922b7aSDavid Ahern 		 * PKTINFO_SKB_CB and in_pktinfo also has iif as the first
1225f0c16ba8SWei Zhang 		 * element so the iif is picked up from the prior IPCB. If iif
1226f0c16ba8SWei Zhang 		 * is the loopback interface, then return the sending interface
1227f0c16ba8SWei Zhang 		 * (e.g., process binds socket to eth0 for Tx which is
1228f0c16ba8SWei Zhang 		 * redirected to loopback in the rtable/dst).
12290b922b7aSDavid Ahern 		 */
1230cbea8f02SDavid Ahern 		struct rtable *rt = skb_rtable(skb);
1231cbea8f02SDavid Ahern 		bool l3slave = ipv4_l3mdev_skb(IPCB(skb)->flags);
1232cbea8f02SDavid Ahern 
1233cbea8f02SDavid Ahern 		if (pktinfo->ipi_ifindex == LOOPBACK_IFINDEX)
1234f0c16ba8SWei Zhang 			pktinfo->ipi_ifindex = inet_iif(skb);
1235cbea8f02SDavid Ahern 		else if (l3slave && rt && rt->rt_iif)
1236cbea8f02SDavid Ahern 			pktinfo->ipi_ifindex = rt->rt_iif;
1237f0c16ba8SWei Zhang 
123835ebf65eSDavid S. Miller 		pktinfo->ipi_spec_dst.s_addr = fib_compute_spec_dst(skb);
1239d826eb14SEric Dumazet 	} else {
1240d826eb14SEric Dumazet 		pktinfo->ipi_ifindex = 0;
1241d826eb14SEric Dumazet 		pktinfo->ipi_spec_dst.s_addr = 0;
1242f84af32cSEric Dumazet 	}
1243d826eb14SEric Dumazet 	skb_dst_drop(skb);
1244d826eb14SEric Dumazet }
1245f84af32cSEric Dumazet 
12463fdadf7dSDmitry Mishin int ip_setsockopt(struct sock *sk, int level,
1247b7058842SDavid S. Miller 		int optname, char __user *optval, unsigned int optlen)
12483fdadf7dSDmitry Mishin {
12493fdadf7dSDmitry Mishin 	int err;
12503fdadf7dSDmitry Mishin 
12513fdadf7dSDmitry Mishin 	if (level != SOL_IP)
12523fdadf7dSDmitry Mishin 		return -ENOPROTOOPT;
12533fdadf7dSDmitry Mishin 
12543fdadf7dSDmitry Mishin 	err = do_ip_setsockopt(sk, level, optname, optval, optlen);
12553fdadf7dSDmitry Mishin #ifdef CONFIG_NETFILTER
12563fdadf7dSDmitry Mishin 	/* we need to exclude all possible ENOPROTOOPTs except default case */
12573fdadf7dSDmitry Mishin 	if (err == -ENOPROTOOPT && optname != IP_HDRINCL &&
12586a9fb947SPavel Emelyanov 			optname != IP_IPSEC_POLICY &&
12596a9fb947SPavel Emelyanov 			optname != IP_XFRM_POLICY &&
12603f34cfaeSPaolo Abeni 			!ip_mroute_opt(optname))
12613fdadf7dSDmitry Mishin 		err = nf_setsockopt(sk, PF_INET, optname, optval, optlen);
12623fdadf7dSDmitry Mishin #endif
12633fdadf7dSDmitry Mishin 	return err;
12643fdadf7dSDmitry Mishin }
12654d52cfbeSEric Dumazet EXPORT_SYMBOL(ip_setsockopt);
12663fdadf7dSDmitry Mishin 
12673fdadf7dSDmitry Mishin #ifdef CONFIG_COMPAT
1268543d9cfeSArnaldo Carvalho de Melo int compat_ip_setsockopt(struct sock *sk, int level, int optname,
1269b7058842SDavid S. Miller 			 char __user *optval, unsigned int optlen)
12703fdadf7dSDmitry Mishin {
12713fdadf7dSDmitry Mishin 	int err;
12723fdadf7dSDmitry Mishin 
12733fdadf7dSDmitry Mishin 	if (level != SOL_IP)
12743fdadf7dSDmitry Mishin 		return -ENOPROTOOPT;
12753fdadf7dSDmitry Mishin 
1276dae50295SDavid L Stevens 	if (optname >= MCAST_JOIN_GROUP && optname <= MCAST_MSFILTER)
1277dae50295SDavid L Stevens 		return compat_mc_setsockopt(sk, level, optname, optval, optlen,
1278dae50295SDavid L Stevens 			ip_setsockopt);
1279dae50295SDavid L Stevens 
12803fdadf7dSDmitry Mishin 	err = do_ip_setsockopt(sk, level, optname, optval, optlen);
12813fdadf7dSDmitry Mishin #ifdef CONFIG_NETFILTER
12823fdadf7dSDmitry Mishin 	/* we need to exclude all possible ENOPROTOOPTs except default case */
12833fdadf7dSDmitry Mishin 	if (err == -ENOPROTOOPT && optname != IP_HDRINCL &&
12846a9fb947SPavel Emelyanov 			optname != IP_IPSEC_POLICY &&
12856a9fb947SPavel Emelyanov 			optname != IP_XFRM_POLICY &&
12863f34cfaeSPaolo Abeni 			!ip_mroute_opt(optname))
12873f34cfaeSPaolo Abeni 		err = compat_nf_setsockopt(sk, PF_INET, optname, optval,
12883f34cfaeSPaolo Abeni 					   optlen);
12893fdadf7dSDmitry Mishin #endif
12903fdadf7dSDmitry Mishin 	return err;
12913fdadf7dSDmitry Mishin }
1292543d9cfeSArnaldo Carvalho de Melo EXPORT_SYMBOL(compat_ip_setsockopt);
12933fdadf7dSDmitry Mishin #endif
12943fdadf7dSDmitry Mishin 
12951da177e4SLinus Torvalds /*
12964d52cfbeSEric Dumazet  *	Get the options. Note for future reference. The GET of IP options gets
12974d52cfbeSEric Dumazet  *	the _received_ ones. The set sets the _sent_ ones.
12981da177e4SLinus Torvalds  */
12991da177e4SLinus Torvalds 
130087e9f031SWANG Cong static bool getsockopt_needs_rtnl(int optname)
130187e9f031SWANG Cong {
130287e9f031SWANG Cong 	switch (optname) {
130387e9f031SWANG Cong 	case IP_MSFILTER:
130487e9f031SWANG Cong 	case MCAST_MSFILTER:
130587e9f031SWANG Cong 		return true;
130687e9f031SWANG Cong 	}
130787e9f031SWANG Cong 	return false;
130887e9f031SWANG Cong }
130987e9f031SWANG Cong 
13103fdadf7dSDmitry Mishin static int do_ip_getsockopt(struct sock *sk, int level, int optname,
131195c96174SEric Dumazet 			    char __user *optval, int __user *optlen, unsigned int flags)
13121da177e4SLinus Torvalds {
13131da177e4SLinus Torvalds 	struct inet_sock *inet = inet_sk(sk);
131487e9f031SWANG Cong 	bool needs_rtnl = getsockopt_needs_rtnl(optname);
131587e9f031SWANG Cong 	int val, err = 0;
13161da177e4SLinus Torvalds 	int len;
13171da177e4SLinus Torvalds 
13181da177e4SLinus Torvalds 	if (level != SOL_IP)
13191da177e4SLinus Torvalds 		return -EOPNOTSUPP;
13201da177e4SLinus Torvalds 
13216a9fb947SPavel Emelyanov 	if (ip_mroute_opt(optname))
13221da177e4SLinus Torvalds 		return ip_mroute_getsockopt(sk, optname, optval, optlen);
13231da177e4SLinus Torvalds 
13241da177e4SLinus Torvalds 	if (get_user(len, optlen))
13251da177e4SLinus Torvalds 		return -EFAULT;
13261da177e4SLinus Torvalds 	if (len < 0)
13271da177e4SLinus Torvalds 		return -EINVAL;
13281da177e4SLinus Torvalds 
132987e9f031SWANG Cong 	if (needs_rtnl)
133087e9f031SWANG Cong 		rtnl_lock();
13311da177e4SLinus Torvalds 	lock_sock(sk);
13321da177e4SLinus Torvalds 
13331da177e4SLinus Torvalds 	switch (optname) {
13341da177e4SLinus Torvalds 	case IP_OPTIONS:
13351da177e4SLinus Torvalds 	{
13361da177e4SLinus Torvalds 		unsigned char optbuf[sizeof(struct ip_options)+40];
13371da177e4SLinus Torvalds 		struct ip_options *opt = (struct ip_options *)optbuf;
1338f6d8bd05SEric Dumazet 		struct ip_options_rcu *inet_opt;
1339f6d8bd05SEric Dumazet 
1340f6d8bd05SEric Dumazet 		inet_opt = rcu_dereference_protected(inet->inet_opt,
13411e1d04e6SHannes Frederic Sowa 						     lockdep_sock_is_held(sk));
13421da177e4SLinus Torvalds 		opt->optlen = 0;
1343f6d8bd05SEric Dumazet 		if (inet_opt)
1344f6d8bd05SEric Dumazet 			memcpy(optbuf, &inet_opt->opt,
13451da177e4SLinus Torvalds 			       sizeof(struct ip_options) +
1346f6d8bd05SEric Dumazet 			       inet_opt->opt.optlen);
13471da177e4SLinus Torvalds 		release_sock(sk);
13481da177e4SLinus Torvalds 
13491da177e4SLinus Torvalds 		if (opt->optlen == 0)
13501da177e4SLinus Torvalds 			return put_user(0, optlen);
13511da177e4SLinus Torvalds 
13521da177e4SLinus Torvalds 		ip_options_undo(opt);
13531da177e4SLinus Torvalds 
13541da177e4SLinus Torvalds 		len = min_t(unsigned int, len, opt->optlen);
13551da177e4SLinus Torvalds 		if (put_user(len, optlen))
13561da177e4SLinus Torvalds 			return -EFAULT;
13571da177e4SLinus Torvalds 		if (copy_to_user(optval, opt->__data, len))
13581da177e4SLinus Torvalds 			return -EFAULT;
13591da177e4SLinus Torvalds 		return 0;
13601da177e4SLinus Torvalds 	}
13611da177e4SLinus Torvalds 	case IP_PKTINFO:
13621da177e4SLinus Torvalds 		val = (inet->cmsg_flags & IP_CMSG_PKTINFO) != 0;
13631da177e4SLinus Torvalds 		break;
13641da177e4SLinus Torvalds 	case IP_RECVTTL:
13651da177e4SLinus Torvalds 		val = (inet->cmsg_flags & IP_CMSG_TTL) != 0;
13661da177e4SLinus Torvalds 		break;
13671da177e4SLinus Torvalds 	case IP_RECVTOS:
13681da177e4SLinus Torvalds 		val = (inet->cmsg_flags & IP_CMSG_TOS) != 0;
13691da177e4SLinus Torvalds 		break;
13701da177e4SLinus Torvalds 	case IP_RECVOPTS:
13711da177e4SLinus Torvalds 		val = (inet->cmsg_flags & IP_CMSG_RECVOPTS) != 0;
13721da177e4SLinus Torvalds 		break;
13731da177e4SLinus Torvalds 	case IP_RETOPTS:
13741da177e4SLinus Torvalds 		val = (inet->cmsg_flags & IP_CMSG_RETOPTS) != 0;
13751da177e4SLinus Torvalds 		break;
13762c7946a7SCatherine Zhang 	case IP_PASSSEC:
13772c7946a7SCatherine Zhang 		val = (inet->cmsg_flags & IP_CMSG_PASSSEC) != 0;
13782c7946a7SCatherine Zhang 		break;
1379e8b2dfe9SBalazs Scheidler 	case IP_RECVORIGDSTADDR:
1380e8b2dfe9SBalazs Scheidler 		val = (inet->cmsg_flags & IP_CMSG_ORIGDSTADDR) != 0;
1381e8b2dfe9SBalazs Scheidler 		break;
1382ad6f939aSTom Herbert 	case IP_CHECKSUM:
1383ad6f939aSTom Herbert 		val = (inet->cmsg_flags & IP_CMSG_CHECKSUM) != 0;
1384ad6f939aSTom Herbert 		break;
138570ecc248SWillem de Bruijn 	case IP_RECVFRAGSIZE:
138670ecc248SWillem de Bruijn 		val = (inet->cmsg_flags & IP_CMSG_RECVFRAGSIZE) != 0;
138770ecc248SWillem de Bruijn 		break;
13881da177e4SLinus Torvalds 	case IP_TOS:
13891da177e4SLinus Torvalds 		val = inet->tos;
13901da177e4SLinus Torvalds 		break;
13911da177e4SLinus Torvalds 	case IP_TTL:
1392fa50d974SNikolay Borisov 	{
1393fa50d974SNikolay Borisov 		struct net *net = sock_net(sk);
13941da177e4SLinus Torvalds 		val = (inet->uc_ttl == -1 ?
1395fa50d974SNikolay Borisov 		       net->ipv4.sysctl_ip_default_ttl :
13961da177e4SLinus Torvalds 		       inet->uc_ttl);
13971da177e4SLinus Torvalds 		break;
1398fa50d974SNikolay Borisov 	}
13991da177e4SLinus Torvalds 	case IP_HDRINCL:
14001da177e4SLinus Torvalds 		val = inet->hdrincl;
14011da177e4SLinus Torvalds 		break;
1402a89b4763SMichael Kerrisk 	case IP_NODEFRAG:
1403a89b4763SMichael Kerrisk 		val = inet->nodefrag;
1404a89b4763SMichael Kerrisk 		break;
140590c337daSEric Dumazet 	case IP_BIND_ADDRESS_NO_PORT:
140690c337daSEric Dumazet 		val = inet->bind_address_no_port;
140790c337daSEric Dumazet 		break;
14081da177e4SLinus Torvalds 	case IP_MTU_DISCOVER:
14091da177e4SLinus Torvalds 		val = inet->pmtudisc;
14101da177e4SLinus Torvalds 		break;
14111da177e4SLinus Torvalds 	case IP_MTU:
14121da177e4SLinus Torvalds 	{
14131da177e4SLinus Torvalds 		struct dst_entry *dst;
14141da177e4SLinus Torvalds 		val = 0;
14151da177e4SLinus Torvalds 		dst = sk_dst_get(sk);
14161da177e4SLinus Torvalds 		if (dst) {
14171da177e4SLinus Torvalds 			val = dst_mtu(dst);
14181da177e4SLinus Torvalds 			dst_release(dst);
14191da177e4SLinus Torvalds 		}
14201da177e4SLinus Torvalds 		if (!val) {
14211da177e4SLinus Torvalds 			release_sock(sk);
14221da177e4SLinus Torvalds 			return -ENOTCONN;
14231da177e4SLinus Torvalds 		}
14241da177e4SLinus Torvalds 		break;
14251da177e4SLinus Torvalds 	}
14261da177e4SLinus Torvalds 	case IP_RECVERR:
14271da177e4SLinus Torvalds 		val = inet->recverr;
14281da177e4SLinus Torvalds 		break;
14291da177e4SLinus Torvalds 	case IP_MULTICAST_TTL:
14301da177e4SLinus Torvalds 		val = inet->mc_ttl;
14311da177e4SLinus Torvalds 		break;
14321da177e4SLinus Torvalds 	case IP_MULTICAST_LOOP:
14331da177e4SLinus Torvalds 		val = inet->mc_loop;
14341da177e4SLinus Torvalds 		break;
143576e21053SErich E. Hoover 	case IP_UNICAST_IF:
143676e21053SErich E. Hoover 		val = (__force int)htonl((__u32) inet->uc_index);
143776e21053SErich E. Hoover 		break;
14381da177e4SLinus Torvalds 	case IP_MULTICAST_IF:
14391da177e4SLinus Torvalds 	{
14401da177e4SLinus Torvalds 		struct in_addr addr;
14411da177e4SLinus Torvalds 		len = min_t(unsigned int, len, sizeof(struct in_addr));
14421da177e4SLinus Torvalds 		addr.s_addr = inet->mc_addr;
14431da177e4SLinus Torvalds 		release_sock(sk);
14441da177e4SLinus Torvalds 
14451da177e4SLinus Torvalds 		if (put_user(len, optlen))
14461da177e4SLinus Torvalds 			return -EFAULT;
14471da177e4SLinus Torvalds 		if (copy_to_user(optval, &addr, len))
14481da177e4SLinus Torvalds 			return -EFAULT;
14491da177e4SLinus Torvalds 		return 0;
14501da177e4SLinus Torvalds 	}
14511da177e4SLinus Torvalds 	case IP_MSFILTER:
14521da177e4SLinus Torvalds 	{
14531da177e4SLinus Torvalds 		struct ip_msfilter msf;
14541da177e4SLinus Torvalds 
14551da177e4SLinus Torvalds 		if (len < IP_MSFILTER_SIZE(0)) {
145687e9f031SWANG Cong 			err = -EINVAL;
145787e9f031SWANG Cong 			goto out;
14581da177e4SLinus Torvalds 		}
14591da177e4SLinus Torvalds 		if (copy_from_user(&msf, optval, IP_MSFILTER_SIZE(0))) {
146087e9f031SWANG Cong 			err = -EFAULT;
146187e9f031SWANG Cong 			goto out;
14621da177e4SLinus Torvalds 		}
14631da177e4SLinus Torvalds 		err = ip_mc_msfget(sk, &msf,
14641da177e4SLinus Torvalds 				   (struct ip_msfilter __user *)optval, optlen);
146587e9f031SWANG Cong 		goto out;
14661da177e4SLinus Torvalds 	}
14671da177e4SLinus Torvalds 	case MCAST_MSFILTER:
14681da177e4SLinus Torvalds 	{
14691da177e4SLinus Torvalds 		struct group_filter gsf;
14701da177e4SLinus Torvalds 
14711da177e4SLinus Torvalds 		if (len < GROUP_FILTER_SIZE(0)) {
147287e9f031SWANG Cong 			err = -EINVAL;
147387e9f031SWANG Cong 			goto out;
14741da177e4SLinus Torvalds 		}
14751da177e4SLinus Torvalds 		if (copy_from_user(&gsf, optval, GROUP_FILTER_SIZE(0))) {
147687e9f031SWANG Cong 			err = -EFAULT;
147787e9f031SWANG Cong 			goto out;
14781da177e4SLinus Torvalds 		}
14791da177e4SLinus Torvalds 		err = ip_mc_gsfget(sk, &gsf,
14804d52cfbeSEric Dumazet 				   (struct group_filter __user *)optval,
14814d52cfbeSEric Dumazet 				   optlen);
148287e9f031SWANG Cong 		goto out;
14831da177e4SLinus Torvalds 	}
1484f771bef9SNivedita Singhvi 	case IP_MULTICAST_ALL:
1485f771bef9SNivedita Singhvi 		val = inet->mc_all;
1486f771bef9SNivedita Singhvi 		break;
14871da177e4SLinus Torvalds 	case IP_PKTOPTIONS:
14881da177e4SLinus Torvalds 	{
14891da177e4SLinus Torvalds 		struct msghdr msg;
14901da177e4SLinus Torvalds 
14911da177e4SLinus Torvalds 		release_sock(sk);
14921da177e4SLinus Torvalds 
14931da177e4SLinus Torvalds 		if (sk->sk_type != SOCK_STREAM)
14941da177e4SLinus Torvalds 			return -ENOPROTOOPT;
14951da177e4SLinus Torvalds 
1496c54a5e02SKaroly Kemeny 		msg.msg_control = (__force void *) optval;
14971da177e4SLinus Torvalds 		msg.msg_controllen = len;
1498dd23198eSDaniel Baluta 		msg.msg_flags = flags;
14991da177e4SLinus Torvalds 
15001da177e4SLinus Torvalds 		if (inet->cmsg_flags & IP_CMSG_PKTINFO) {
15011da177e4SLinus Torvalds 			struct in_pktinfo info;
15021da177e4SLinus Torvalds 
1503c720c7e8SEric Dumazet 			info.ipi_addr.s_addr = inet->inet_rcv_saddr;
1504c720c7e8SEric Dumazet 			info.ipi_spec_dst.s_addr = inet->inet_rcv_saddr;
15051da177e4SLinus Torvalds 			info.ipi_ifindex = inet->mc_index;
15061da177e4SLinus Torvalds 			put_cmsg(&msg, SOL_IP, IP_PKTINFO, sizeof(info), &info);
15071da177e4SLinus Torvalds 		}
15081da177e4SLinus Torvalds 		if (inet->cmsg_flags & IP_CMSG_TTL) {
15091da177e4SLinus Torvalds 			int hlim = inet->mc_ttl;
15101da177e4SLinus Torvalds 			put_cmsg(&msg, SOL_IP, IP_TTL, sizeof(hlim), &hlim);
15111da177e4SLinus Torvalds 		}
15124c507d28SJiri Benc 		if (inet->cmsg_flags & IP_CMSG_TOS) {
15134c507d28SJiri Benc 			int tos = inet->rcv_tos;
15144c507d28SJiri Benc 			put_cmsg(&msg, SOL_IP, IP_TOS, sizeof(tos), &tos);
15154c507d28SJiri Benc 		}
15161da177e4SLinus Torvalds 		len -= msg.msg_controllen;
15171da177e4SLinus Torvalds 		return put_user(len, optlen);
15181da177e4SLinus Torvalds 	}
15191da177e4SLinus Torvalds 	case IP_FREEBIND:
15201da177e4SLinus Torvalds 		val = inet->freebind;
15211da177e4SLinus Torvalds 		break;
1522f5715aeaSKOVACS Krisztian 	case IP_TRANSPARENT:
1523f5715aeaSKOVACS Krisztian 		val = inet->transparent;
1524f5715aeaSKOVACS Krisztian 		break;
1525d218d111SStephen Hemminger 	case IP_MINTTL:
1526d218d111SStephen Hemminger 		val = inet->min_ttl;
1527d218d111SStephen Hemminger 		break;
15281da177e4SLinus Torvalds 	default:
15291da177e4SLinus Torvalds 		release_sock(sk);
15301da177e4SLinus Torvalds 		return -ENOPROTOOPT;
15311da177e4SLinus Torvalds 	}
15321da177e4SLinus Torvalds 	release_sock(sk);
15331da177e4SLinus Torvalds 
1534951e07c9SDavid S. Miller 	if (len < sizeof(int) && len > 0 && val >= 0 && val <= 255) {
15351da177e4SLinus Torvalds 		unsigned char ucval = (unsigned char)val;
15361da177e4SLinus Torvalds 		len = 1;
15371da177e4SLinus Torvalds 		if (put_user(len, optlen))
15381da177e4SLinus Torvalds 			return -EFAULT;
15391da177e4SLinus Torvalds 		if (copy_to_user(optval, &ucval, 1))
15401da177e4SLinus Torvalds 			return -EFAULT;
15411da177e4SLinus Torvalds 	} else {
15421da177e4SLinus Torvalds 		len = min_t(unsigned int, sizeof(int), len);
15431da177e4SLinus Torvalds 		if (put_user(len, optlen))
15441da177e4SLinus Torvalds 			return -EFAULT;
15451da177e4SLinus Torvalds 		if (copy_to_user(optval, &val, len))
15461da177e4SLinus Torvalds 			return -EFAULT;
15471da177e4SLinus Torvalds 	}
15481da177e4SLinus Torvalds 	return 0;
154987e9f031SWANG Cong 
155087e9f031SWANG Cong out:
155187e9f031SWANG Cong 	release_sock(sk);
155287e9f031SWANG Cong 	if (needs_rtnl)
155387e9f031SWANG Cong 		rtnl_unlock();
155487e9f031SWANG Cong 	return err;
15551da177e4SLinus Torvalds }
15561da177e4SLinus Torvalds 
15573fdadf7dSDmitry Mishin int ip_getsockopt(struct sock *sk, int level,
15583fdadf7dSDmitry Mishin 		  int optname, char __user *optval, int __user *optlen)
15593fdadf7dSDmitry Mishin {
15603fdadf7dSDmitry Mishin 	int err;
15613fdadf7dSDmitry Mishin 
1562dd23198eSDaniel Baluta 	err = do_ip_getsockopt(sk, level, optname, optval, optlen, 0);
15633fdadf7dSDmitry Mishin #ifdef CONFIG_NETFILTER
15643fdadf7dSDmitry Mishin 	/* we need to exclude all possible ENOPROTOOPTs except default case */
15656a9fb947SPavel Emelyanov 	if (err == -ENOPROTOOPT && optname != IP_PKTOPTIONS &&
15666a9fb947SPavel Emelyanov 			!ip_mroute_opt(optname)) {
15673fdadf7dSDmitry Mishin 		int len;
15683fdadf7dSDmitry Mishin 
15693fdadf7dSDmitry Mishin 		if (get_user(len, optlen))
15703fdadf7dSDmitry Mishin 			return -EFAULT;
15713fdadf7dSDmitry Mishin 
15723fdadf7dSDmitry Mishin 		lock_sock(sk);
15733fdadf7dSDmitry Mishin 		err = nf_getsockopt(sk, PF_INET, optname, optval,
15743fdadf7dSDmitry Mishin 				&len);
15753fdadf7dSDmitry Mishin 		release_sock(sk);
15763fdadf7dSDmitry Mishin 		if (err >= 0)
15773fdadf7dSDmitry Mishin 			err = put_user(len, optlen);
15783fdadf7dSDmitry Mishin 		return err;
15793fdadf7dSDmitry Mishin 	}
15803fdadf7dSDmitry Mishin #endif
15813fdadf7dSDmitry Mishin 	return err;
15823fdadf7dSDmitry Mishin }
15834d52cfbeSEric Dumazet EXPORT_SYMBOL(ip_getsockopt);
15843fdadf7dSDmitry Mishin 
15853fdadf7dSDmitry Mishin #ifdef CONFIG_COMPAT
1586543d9cfeSArnaldo Carvalho de Melo int compat_ip_getsockopt(struct sock *sk, int level, int optname,
1587543d9cfeSArnaldo Carvalho de Melo 			 char __user *optval, int __user *optlen)
15883fdadf7dSDmitry Mishin {
158942908c69SDavid L Stevens 	int err;
159042908c69SDavid L Stevens 
159142908c69SDavid L Stevens 	if (optname == MCAST_MSFILTER)
159242908c69SDavid L Stevens 		return compat_mc_getsockopt(sk, level, optname, optval, optlen,
159342908c69SDavid L Stevens 			ip_getsockopt);
159442908c69SDavid L Stevens 
1595dd23198eSDaniel Baluta 	err = do_ip_getsockopt(sk, level, optname, optval, optlen,
1596dd23198eSDaniel Baluta 		MSG_CMSG_COMPAT);
159742908c69SDavid L Stevens 
15983fdadf7dSDmitry Mishin #ifdef CONFIG_NETFILTER
15993fdadf7dSDmitry Mishin 	/* we need to exclude all possible ENOPROTOOPTs except default case */
16006a9fb947SPavel Emelyanov 	if (err == -ENOPROTOOPT && optname != IP_PKTOPTIONS &&
16016a9fb947SPavel Emelyanov 			!ip_mroute_opt(optname)) {
16023fdadf7dSDmitry Mishin 		int len;
16033fdadf7dSDmitry Mishin 
16043fdadf7dSDmitry Mishin 		if (get_user(len, optlen))
16053fdadf7dSDmitry Mishin 			return -EFAULT;
16063fdadf7dSDmitry Mishin 
16073fdadf7dSDmitry Mishin 		lock_sock(sk);
1608543d9cfeSArnaldo Carvalho de Melo 		err = compat_nf_getsockopt(sk, PF_INET, optname, optval, &len);
16093fdadf7dSDmitry Mishin 		release_sock(sk);
16103fdadf7dSDmitry Mishin 		if (err >= 0)
16113fdadf7dSDmitry Mishin 			err = put_user(len, optlen);
16123fdadf7dSDmitry Mishin 		return err;
16133fdadf7dSDmitry Mishin 	}
16143fdadf7dSDmitry Mishin #endif
16153fdadf7dSDmitry Mishin 	return err;
16163fdadf7dSDmitry Mishin }
1617543d9cfeSArnaldo Carvalho de Melo EXPORT_SYMBOL(compat_ip_getsockopt);
16183fdadf7dSDmitry Mishin #endif
1619