xref: /linux/net/ipv4/ip_sockglue.c (revision 959d5c11601b2b337c364b2e3102d392365e3dd3)
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 
50d2ba09c1SAlexei Starovoitov #include <linux/bpfilter.h>
51d2ba09c1SAlexei Starovoitov 
521da177e4SLinus Torvalds /*
531da177e4SLinus Torvalds  *	SOL_IP control messages.
541da177e4SLinus Torvalds  */
551da177e4SLinus Torvalds 
561da177e4SLinus Torvalds static void ip_cmsg_recv_pktinfo(struct msghdr *msg, struct sk_buff *skb)
571da177e4SLinus Torvalds {
58d826eb14SEric Dumazet 	struct in_pktinfo info = *PKTINFO_SKB_CB(skb);
591da177e4SLinus Torvalds 
60eddc9ec5SArnaldo Carvalho de Melo 	info.ipi_addr.s_addr = ip_hdr(skb)->daddr;
611da177e4SLinus Torvalds 
621da177e4SLinus Torvalds 	put_cmsg(msg, SOL_IP, IP_PKTINFO, sizeof(info), &info);
631da177e4SLinus Torvalds }
641da177e4SLinus Torvalds 
651da177e4SLinus Torvalds static void ip_cmsg_recv_ttl(struct msghdr *msg, struct sk_buff *skb)
661da177e4SLinus Torvalds {
67eddc9ec5SArnaldo Carvalho de Melo 	int ttl = ip_hdr(skb)->ttl;
681da177e4SLinus Torvalds 	put_cmsg(msg, SOL_IP, IP_TTL, sizeof(int), &ttl);
691da177e4SLinus Torvalds }
701da177e4SLinus Torvalds 
711da177e4SLinus Torvalds static void ip_cmsg_recv_tos(struct msghdr *msg, struct sk_buff *skb)
721da177e4SLinus Torvalds {
73eddc9ec5SArnaldo Carvalho de Melo 	put_cmsg(msg, SOL_IP, IP_TOS, 1, &ip_hdr(skb)->tos);
741da177e4SLinus Torvalds }
751da177e4SLinus Torvalds 
761da177e4SLinus Torvalds static void ip_cmsg_recv_opts(struct msghdr *msg, struct sk_buff *skb)
771da177e4SLinus Torvalds {
781da177e4SLinus Torvalds 	if (IPCB(skb)->opt.optlen == 0)
791da177e4SLinus Torvalds 		return;
801da177e4SLinus Torvalds 
81eddc9ec5SArnaldo Carvalho de Melo 	put_cmsg(msg, SOL_IP, IP_RECVOPTS, IPCB(skb)->opt.optlen,
82eddc9ec5SArnaldo Carvalho de Melo 		 ip_hdr(skb) + 1);
831da177e4SLinus Torvalds }
841da177e4SLinus Torvalds 
851da177e4SLinus Torvalds 
8691ed1e66SPaolo Abeni static void ip_cmsg_recv_retopts(struct net *net, struct msghdr *msg,
8791ed1e66SPaolo Abeni 				 struct sk_buff *skb)
881da177e4SLinus Torvalds {
891da177e4SLinus Torvalds 	unsigned char optbuf[sizeof(struct ip_options) + 40];
901da177e4SLinus Torvalds 	struct ip_options *opt = (struct ip_options *)optbuf;
911da177e4SLinus Torvalds 
921da177e4SLinus Torvalds 	if (IPCB(skb)->opt.optlen == 0)
931da177e4SLinus Torvalds 		return;
941da177e4SLinus Torvalds 
9591ed1e66SPaolo Abeni 	if (ip_options_echo(net, opt, skb)) {
961da177e4SLinus Torvalds 		msg->msg_flags |= MSG_CTRUNC;
971da177e4SLinus Torvalds 		return;
981da177e4SLinus Torvalds 	}
991da177e4SLinus Torvalds 	ip_options_undo(opt);
1001da177e4SLinus Torvalds 
1011da177e4SLinus Torvalds 	put_cmsg(msg, SOL_IP, IP_RETOPTS, opt->optlen, opt->__data);
1021da177e4SLinus Torvalds }
1031da177e4SLinus Torvalds 
10470ecc248SWillem de Bruijn static void ip_cmsg_recv_fragsize(struct msghdr *msg, struct sk_buff *skb)
10570ecc248SWillem de Bruijn {
10670ecc248SWillem de Bruijn 	int val;
10770ecc248SWillem de Bruijn 
10870ecc248SWillem de Bruijn 	if (IPCB(skb)->frag_max_size == 0)
10970ecc248SWillem de Bruijn 		return;
11070ecc248SWillem de Bruijn 
11170ecc248SWillem de Bruijn 	val = IPCB(skb)->frag_max_size;
11270ecc248SWillem de Bruijn 	put_cmsg(msg, SOL_IP, IP_RECVFRAGSIZE, sizeof(val), &val);
11370ecc248SWillem de Bruijn }
11470ecc248SWillem de Bruijn 
115ad6f939aSTom Herbert static void ip_cmsg_recv_checksum(struct msghdr *msg, struct sk_buff *skb,
11610df8e61SEric Dumazet 				  int tlen, int offset)
117ad6f939aSTom Herbert {
118ad6f939aSTom Herbert 	__wsum csum = skb->csum;
119ad6f939aSTom Herbert 
120ad6f939aSTom Herbert 	if (skb->ip_summed != CHECKSUM_COMPLETE)
121ad6f939aSTom Herbert 		return;
122ad6f939aSTom Herbert 
123ca4ef457SPaolo Abeni 	if (offset != 0) {
124ca4ef457SPaolo Abeni 		int tend_off = skb_transport_offset(skb) + tlen;
125ca4ef457SPaolo Abeni 		csum = csum_sub(csum, skb_checksum(skb, tend_off, offset, 0));
126ca4ef457SPaolo Abeni 	}
127ad6f939aSTom Herbert 
128ad6f939aSTom Herbert 	put_cmsg(msg, SOL_IP, IP_CHECKSUM, sizeof(__wsum), &csum);
129ad6f939aSTom Herbert }
130ad6f939aSTom Herbert 
1312c7946a7SCatherine Zhang static void ip_cmsg_recv_security(struct msghdr *msg, struct sk_buff *skb)
1322c7946a7SCatherine Zhang {
1332c7946a7SCatherine Zhang 	char *secdata;
134dc49c1f9SCatherine Zhang 	u32 seclen, secid;
1352c7946a7SCatherine Zhang 	int err;
1362c7946a7SCatherine Zhang 
137dc49c1f9SCatherine Zhang 	err = security_socket_getpeersec_dgram(NULL, skb, &secid);
138dc49c1f9SCatherine Zhang 	if (err)
139dc49c1f9SCatherine Zhang 		return;
140dc49c1f9SCatherine Zhang 
141dc49c1f9SCatherine Zhang 	err = security_secid_to_secctx(secid, &secdata, &seclen);
1422c7946a7SCatherine Zhang 	if (err)
1432c7946a7SCatherine Zhang 		return;
1442c7946a7SCatherine Zhang 
1452c7946a7SCatherine Zhang 	put_cmsg(msg, SOL_IP, SCM_SECURITY, seclen, secdata);
146dc49c1f9SCatherine Zhang 	security_release_secctx(secdata, seclen);
1472c7946a7SCatherine Zhang }
1482c7946a7SCatherine Zhang 
14921d1a161SHarvey Harrison static void ip_cmsg_recv_dstaddr(struct msghdr *msg, struct sk_buff *skb)
150e8b2dfe9SBalazs Scheidler {
1514a06fa67SWillem de Bruijn 	__be16 _ports[2], *ports;
152e8b2dfe9SBalazs Scheidler 	struct sockaddr_in sin;
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 	 */
1584a06fa67SWillem de Bruijn 	ports = skb_header_pointer(skb, skb_transport_offset(skb),
1594a06fa67SWillem de Bruijn 				   sizeof(_ports), &_ports);
1604a06fa67SWillem de Bruijn 	if (!ports)
1614a06fa67SWillem de Bruijn 		return;
162e8b2dfe9SBalazs Scheidler 
163e8b2dfe9SBalazs Scheidler 	sin.sin_family = AF_INET;
16464199fc0SEric Dumazet 	sin.sin_addr.s_addr = ip_hdr(skb)->daddr;
165e8b2dfe9SBalazs Scheidler 	sin.sin_port = ports[1];
166e8b2dfe9SBalazs Scheidler 	memset(sin.sin_zero, 0, sizeof(sin.sin_zero));
167e8b2dfe9SBalazs Scheidler 
168e8b2dfe9SBalazs Scheidler 	put_cmsg(msg, SOL_IP, IP_ORIGDSTADDR, sizeof(sin), &sin);
169e8b2dfe9SBalazs Scheidler }
1701da177e4SLinus Torvalds 
171ad959036SPaolo Abeni void ip_cmsg_recv_offset(struct msghdr *msg, struct sock *sk,
172ad959036SPaolo Abeni 			 struct sk_buff *skb, int tlen, int offset)
1731da177e4SLinus Torvalds {
174c274af22SEric Dumazet 	unsigned long flags = inet_cmsg_flags(inet_sk(sk));
175c274af22SEric Dumazet 
176c274af22SEric Dumazet 	if (!flags)
177c274af22SEric Dumazet 		return;
1781da177e4SLinus Torvalds 
1791da177e4SLinus Torvalds 	/* Ordered by supposed usage frequency */
180c44d13d6STom Herbert 	if (flags & IP_CMSG_PKTINFO) {
1811da177e4SLinus Torvalds 		ip_cmsg_recv_pktinfo(msg, skb);
1821da177e4SLinus Torvalds 
183c44d13d6STom Herbert 		flags &= ~IP_CMSG_PKTINFO;
184c44d13d6STom Herbert 		if (!flags)
185c44d13d6STom Herbert 			return;
186c44d13d6STom Herbert 	}
187c44d13d6STom Herbert 
188c44d13d6STom Herbert 	if (flags & IP_CMSG_TTL) {
1891da177e4SLinus Torvalds 		ip_cmsg_recv_ttl(msg, skb);
1901da177e4SLinus Torvalds 
191c44d13d6STom Herbert 		flags &= ~IP_CMSG_TTL;
192c44d13d6STom Herbert 		if (!flags)
193c44d13d6STom Herbert 			return;
194c44d13d6STom Herbert 	}
195c44d13d6STom Herbert 
196c44d13d6STom Herbert 	if (flags & IP_CMSG_TOS) {
1971da177e4SLinus Torvalds 		ip_cmsg_recv_tos(msg, skb);
1981da177e4SLinus Torvalds 
199c44d13d6STom Herbert 		flags &= ~IP_CMSG_TOS;
200c44d13d6STom Herbert 		if (!flags)
201c44d13d6STom Herbert 			return;
202c44d13d6STom Herbert 	}
203c44d13d6STom Herbert 
204c44d13d6STom Herbert 	if (flags & IP_CMSG_RECVOPTS) {
2051da177e4SLinus Torvalds 		ip_cmsg_recv_opts(msg, skb);
2061da177e4SLinus Torvalds 
207c44d13d6STom Herbert 		flags &= ~IP_CMSG_RECVOPTS;
208c44d13d6STom Herbert 		if (!flags)
209c44d13d6STom Herbert 			return;
210c44d13d6STom Herbert 	}
211c44d13d6STom Herbert 
212c44d13d6STom Herbert 	if (flags & IP_CMSG_RETOPTS) {
21391ed1e66SPaolo Abeni 		ip_cmsg_recv_retopts(sock_net(sk), msg, skb);
2142c7946a7SCatherine Zhang 
215c44d13d6STom Herbert 		flags &= ~IP_CMSG_RETOPTS;
216c44d13d6STom Herbert 		if (!flags)
217c44d13d6STom Herbert 			return;
218c44d13d6STom Herbert 	}
219c44d13d6STom Herbert 
220c44d13d6STom Herbert 	if (flags & IP_CMSG_PASSSEC) {
2212c7946a7SCatherine Zhang 		ip_cmsg_recv_security(msg, skb);
222e8b2dfe9SBalazs Scheidler 
223c44d13d6STom Herbert 		flags &= ~IP_CMSG_PASSSEC;
224c44d13d6STom Herbert 		if (!flags)
225e8b2dfe9SBalazs Scheidler 			return;
226c44d13d6STom Herbert 	}
227c44d13d6STom Herbert 
228ad6f939aSTom Herbert 	if (flags & IP_CMSG_ORIGDSTADDR) {
229e8b2dfe9SBalazs Scheidler 		ip_cmsg_recv_dstaddr(msg, skb);
230e8b2dfe9SBalazs Scheidler 
231ad6f939aSTom Herbert 		flags &= ~IP_CMSG_ORIGDSTADDR;
232ad6f939aSTom Herbert 		if (!flags)
233ad6f939aSTom Herbert 			return;
234ad6f939aSTom Herbert 	}
235ad6f939aSTom Herbert 
236ad6f939aSTom Herbert 	if (flags & IP_CMSG_CHECKSUM)
23710df8e61SEric Dumazet 		ip_cmsg_recv_checksum(msg, skb, tlen, offset);
23870ecc248SWillem de Bruijn 
23970ecc248SWillem de Bruijn 	if (flags & IP_CMSG_RECVFRAGSIZE)
24070ecc248SWillem de Bruijn 		ip_cmsg_recv_fragsize(msg, skb);
2411da177e4SLinus Torvalds }
2425961de9fSTom Herbert EXPORT_SYMBOL(ip_cmsg_recv_offset);
2431da177e4SLinus Torvalds 
24424025c46SSoheil Hassas Yeganeh int ip_cmsg_send(struct sock *sk, struct msghdr *msg, struct ipcm_cookie *ipc,
245c8e6ad08SHannes Frederic Sowa 		 bool allow_ipv6)
2461da177e4SLinus Torvalds {
247f02db315SFrancesco Fusco 	int err, val;
2481da177e4SLinus Torvalds 	struct cmsghdr *cmsg;
24924025c46SSoheil Hassas Yeganeh 	struct net *net = sock_net(sk);
2501da177e4SLinus Torvalds 
251f95b414eSGu Zheng 	for_each_cmsghdr(cmsg, msg) {
2521da177e4SLinus Torvalds 		if (!CMSG_OK(msg, cmsg))
2531da177e4SLinus Torvalds 			return -EINVAL;
2545337b5b7SEric Dumazet #if IS_ENABLED(CONFIG_IPV6)
255c8e6ad08SHannes Frederic Sowa 		if (allow_ipv6 &&
256c8e6ad08SHannes Frederic Sowa 		    cmsg->cmsg_level == SOL_IPV6 &&
257c8e6ad08SHannes Frederic Sowa 		    cmsg->cmsg_type == IPV6_PKTINFO) {
258c8e6ad08SHannes Frederic Sowa 			struct in6_pktinfo *src_info;
259c8e6ad08SHannes Frederic Sowa 
260c8e6ad08SHannes Frederic Sowa 			if (cmsg->cmsg_len < CMSG_LEN(sizeof(*src_info)))
261c8e6ad08SHannes Frederic Sowa 				return -EINVAL;
262c8e6ad08SHannes Frederic Sowa 			src_info = (struct in6_pktinfo *)CMSG_DATA(cmsg);
263c8e6ad08SHannes Frederic Sowa 			if (!ipv6_addr_v4mapped(&src_info->ipi6_addr))
264c8e6ad08SHannes Frederic Sowa 				return -EINVAL;
2651cbec076SDavid Ahern 			if (src_info->ipi6_ifindex)
266c8e6ad08SHannes Frederic Sowa 				ipc->oif = src_info->ipi6_ifindex;
267c8e6ad08SHannes Frederic Sowa 			ipc->addr = src_info->ipi6_addr.s6_addr32[3];
268c8e6ad08SHannes Frederic Sowa 			continue;
269c8e6ad08SHannes Frederic Sowa 		}
270c8e6ad08SHannes Frederic Sowa #endif
27124025c46SSoheil Hassas Yeganeh 		if (cmsg->cmsg_level == SOL_SOCKET) {
272233baf9aSxu xin 			err = __sock_cmsg_send(sk, cmsg, &ipc->sockc);
2732632616bSEric Dumazet 			if (err)
2742632616bSEric Dumazet 				return err;
27524025c46SSoheil Hassas Yeganeh 			continue;
27624025c46SSoheil Hassas Yeganeh 		}
27724025c46SSoheil Hassas Yeganeh 
2781da177e4SLinus Torvalds 		if (cmsg->cmsg_level != SOL_IP)
2791da177e4SLinus Torvalds 			continue;
2801da177e4SLinus Torvalds 		switch (cmsg->cmsg_type) {
2811da177e4SLinus Torvalds 		case IP_RETOPTS:
2821ff8cebfSyuan linyu 			err = cmsg->cmsg_len - sizeof(struct cmsghdr);
28391948309SEric Dumazet 
28491948309SEric Dumazet 			/* Our caller is responsible for freeing ipc->opt */
285de40a3e8SChristoph Hellwig 			err = ip_options_get(net, &ipc->opt,
286de40a3e8SChristoph Hellwig 					     KERNEL_SOCKPTR(CMSG_DATA(cmsg)),
2874d52cfbeSEric Dumazet 					     err < 40 ? err : 40);
2881da177e4SLinus Torvalds 			if (err)
2891da177e4SLinus Torvalds 				return err;
2901da177e4SLinus Torvalds 			break;
2911da177e4SLinus Torvalds 		case IP_PKTINFO:
2921da177e4SLinus Torvalds 		{
2931da177e4SLinus Torvalds 			struct in_pktinfo *info;
2941da177e4SLinus Torvalds 			if (cmsg->cmsg_len != CMSG_LEN(sizeof(struct in_pktinfo)))
2951da177e4SLinus Torvalds 				return -EINVAL;
2961da177e4SLinus Torvalds 			info = (struct in_pktinfo *)CMSG_DATA(cmsg);
2971cbec076SDavid Ahern 			if (info->ipi_ifindex)
2981da177e4SLinus Torvalds 				ipc->oif = info->ipi_ifindex;
2991da177e4SLinus Torvalds 			ipc->addr = info->ipi_spec_dst.s_addr;
3001da177e4SLinus Torvalds 			break;
3011da177e4SLinus Torvalds 		}
302f02db315SFrancesco Fusco 		case IP_TTL:
303f02db315SFrancesco Fusco 			if (cmsg->cmsg_len != CMSG_LEN(sizeof(int)))
304f02db315SFrancesco Fusco 				return -EINVAL;
305f02db315SFrancesco Fusco 			val = *(int *)CMSG_DATA(cmsg);
306f02db315SFrancesco Fusco 			if (val < 1 || val > 255)
307f02db315SFrancesco Fusco 				return -EINVAL;
308f02db315SFrancesco Fusco 			ipc->ttl = val;
309f02db315SFrancesco Fusco 			break;
310f02db315SFrancesco Fusco 		case IP_TOS:
311e895cdceSEric Dumazet 			if (cmsg->cmsg_len == CMSG_LEN(sizeof(int)))
312f02db315SFrancesco Fusco 				val = *(int *)CMSG_DATA(cmsg);
313e895cdceSEric Dumazet 			else if (cmsg->cmsg_len == CMSG_LEN(sizeof(u8)))
314e895cdceSEric Dumazet 				val = *(u8 *)CMSG_DATA(cmsg);
315e895cdceSEric Dumazet 			else
316e895cdceSEric Dumazet 				return -EINVAL;
317f02db315SFrancesco Fusco 			if (val < 0 || val > 255)
318f02db315SFrancesco Fusco 				return -EINVAL;
319f02db315SFrancesco Fusco 			ipc->tos = val;
320f02db315SFrancesco Fusco 			ipc->priority = rt_tos2priority(ipc->tos);
321f02db315SFrancesco Fusco 			break;
3223632679dSNicolas Dichtel 		case IP_PROTOCOL:
3233632679dSNicolas Dichtel 			if (cmsg->cmsg_len != CMSG_LEN(sizeof(int)))
3243632679dSNicolas Dichtel 				return -EINVAL;
3253632679dSNicolas Dichtel 			val = *(int *)CMSG_DATA(cmsg);
3263632679dSNicolas Dichtel 			if (val < 1 || val > 255)
3273632679dSNicolas Dichtel 				return -EINVAL;
3283632679dSNicolas Dichtel 			ipc->protocol = val;
3293632679dSNicolas Dichtel 			break;
3301da177e4SLinus Torvalds 		default:
3311da177e4SLinus Torvalds 			return -EINVAL;
3321da177e4SLinus Torvalds 		}
3331da177e4SLinus Torvalds 	}
3341da177e4SLinus Torvalds 	return 0;
3351da177e4SLinus Torvalds }
3361da177e4SLinus Torvalds 
337592fcb9dSEric Dumazet static void ip_ra_destroy_rcu(struct rcu_head *head)
33866018506SEric Dumazet {
339592fcb9dSEric Dumazet 	struct ip_ra_chain *ra = container_of(head, struct ip_ra_chain, rcu);
340592fcb9dSEric Dumazet 
341592fcb9dSEric Dumazet 	sock_put(ra->saved_sk);
342592fcb9dSEric Dumazet 	kfree(ra);
34366018506SEric Dumazet }
3441da177e4SLinus Torvalds 
3454d52cfbeSEric Dumazet int ip_ra_control(struct sock *sk, unsigned char on,
3464d52cfbeSEric Dumazet 		  void (*destructor)(struct sock *))
3471da177e4SLinus Torvalds {
34843a951e9SEric Dumazet 	struct ip_ra_chain *ra, *new_ra;
34943a951e9SEric Dumazet 	struct ip_ra_chain __rcu **rap;
3505796ef75SKirill Tkhai 	struct net *net = sock_net(sk);
3511da177e4SLinus Torvalds 
352c720c7e8SEric Dumazet 	if (sk->sk_type != SOCK_RAW || inet_sk(sk)->inet_num == IPPROTO_RAW)
3531da177e4SLinus Torvalds 		return -EINVAL;
3541da177e4SLinus Torvalds 
3551da177e4SLinus Torvalds 	new_ra = on ? kmalloc(sizeof(*new_ra), GFP_KERNEL) : NULL;
356425aa0e1SGen Zhang 	if (on && !new_ra)
357425aa0e1SGen Zhang 		return -ENOMEM;
3581da177e4SLinus Torvalds 
359d9ff3049SKirill Tkhai 	mutex_lock(&net->ipv4.ra_mutex);
3605796ef75SKirill Tkhai 	for (rap = &net->ipv4.ra_chain;
36176d3e153SKirill Tkhai 	     (ra = rcu_dereference_protected(*rap,
362d9ff3049SKirill Tkhai 			lockdep_is_held(&net->ipv4.ra_mutex))) != NULL;
36343a951e9SEric Dumazet 	     rap = &ra->next) {
3641da177e4SLinus Torvalds 		if (ra->sk == sk) {
3651da177e4SLinus Torvalds 			if (on) {
366d9ff3049SKirill Tkhai 				mutex_unlock(&net->ipv4.ra_mutex);
3671da177e4SLinus Torvalds 				kfree(new_ra);
3681da177e4SLinus Torvalds 				return -EADDRINUSE;
3691da177e4SLinus Torvalds 			}
370592fcb9dSEric Dumazet 			/* dont let ip_call_ra_chain() use sk again */
371592fcb9dSEric Dumazet 			ra->sk = NULL;
3728e380f00SEric Dumazet 			RCU_INIT_POINTER(*rap, ra->next);
373d9ff3049SKirill Tkhai 			mutex_unlock(&net->ipv4.ra_mutex);
3741da177e4SLinus Torvalds 
3751da177e4SLinus Torvalds 			if (ra->destructor)
3761da177e4SLinus Torvalds 				ra->destructor(sk);
377592fcb9dSEric Dumazet 			/*
378592fcb9dSEric Dumazet 			 * Delay sock_put(sk) and kfree(ra) after one rcu grace
379592fcb9dSEric Dumazet 			 * period. This guarantee ip_call_ra_chain() dont need
380592fcb9dSEric Dumazet 			 * to mess with socket refcounts.
381592fcb9dSEric Dumazet 			 */
382592fcb9dSEric Dumazet 			ra->saved_sk = sk;
383592fcb9dSEric Dumazet 			call_rcu(&ra->rcu, ip_ra_destroy_rcu);
3841da177e4SLinus Torvalds 			return 0;
3851da177e4SLinus Torvalds 		}
3861da177e4SLinus Torvalds 	}
38776d3e153SKirill Tkhai 	if (!new_ra) {
388d9ff3049SKirill Tkhai 		mutex_unlock(&net->ipv4.ra_mutex);
3891da177e4SLinus Torvalds 		return -ENOBUFS;
39076d3e153SKirill Tkhai 	}
3911da177e4SLinus Torvalds 	new_ra->sk = sk;
3921da177e4SLinus Torvalds 	new_ra->destructor = destructor;
3931da177e4SLinus Torvalds 
3948e380f00SEric Dumazet 	RCU_INIT_POINTER(new_ra->next, ra);
39566018506SEric Dumazet 	rcu_assign_pointer(*rap, new_ra);
3961da177e4SLinus Torvalds 	sock_hold(sk);
397d9ff3049SKirill Tkhai 	mutex_unlock(&net->ipv4.ra_mutex);
3981da177e4SLinus Torvalds 
3991da177e4SLinus Torvalds 	return 0;
4001da177e4SLinus Torvalds }
4011da177e4SLinus Torvalds 
402178c49d9SWillem de Bruijn static void ipv4_icmp_error_rfc4884(const struct sk_buff *skb,
403178c49d9SWillem de Bruijn 				    struct sock_ee_data_rfc4884 *out)
404178c49d9SWillem de Bruijn {
405178c49d9SWillem de Bruijn 	switch (icmp_hdr(skb)->type) {
406178c49d9SWillem de Bruijn 	case ICMP_DEST_UNREACH:
407178c49d9SWillem de Bruijn 	case ICMP_TIME_EXCEEDED:
408178c49d9SWillem de Bruijn 	case ICMP_PARAMETERPROB:
409178c49d9SWillem de Bruijn 		ip_icmp_error_rfc4884(skb, out, sizeof(struct icmphdr),
410178c49d9SWillem de Bruijn 				      icmp_hdr(skb)->un.reserved[1] * 4);
411178c49d9SWillem de Bruijn 	}
412178c49d9SWillem de Bruijn }
413178c49d9SWillem de Bruijn 
4141da177e4SLinus Torvalds void ip_icmp_error(struct sock *sk, struct sk_buff *skb, int err,
41535986b32SAl Viro 		   __be16 port, u32 info, u8 *payload)
4161da177e4SLinus Torvalds {
4171da177e4SLinus Torvalds 	struct sock_exterr_skb *serr;
4181da177e4SLinus Torvalds 
4191da177e4SLinus Torvalds 	skb = skb_clone(skb, GFP_ATOMIC);
4201da177e4SLinus Torvalds 	if (!skb)
4211da177e4SLinus Torvalds 		return;
4221da177e4SLinus Torvalds 
4231da177e4SLinus Torvalds 	serr = SKB_EXT_ERR(skb);
4241da177e4SLinus Torvalds 	serr->ee.ee_errno = err;
4251da177e4SLinus Torvalds 	serr->ee.ee_origin = SO_EE_ORIGIN_ICMP;
42688c7664fSArnaldo Carvalho de Melo 	serr->ee.ee_type = icmp_hdr(skb)->type;
42788c7664fSArnaldo Carvalho de Melo 	serr->ee.ee_code = icmp_hdr(skb)->code;
4281da177e4SLinus Torvalds 	serr->ee.ee_pad = 0;
4291da177e4SLinus Torvalds 	serr->ee.ee_info = info;
4301da177e4SLinus Torvalds 	serr->ee.ee_data = 0;
43188c7664fSArnaldo Carvalho de Melo 	serr->addr_offset = (u8 *)&(((struct iphdr *)(icmp_hdr(skb) + 1))->daddr) -
432d56f90a7SArnaldo Carvalho de Melo 				   skb_network_header(skb);
4331da177e4SLinus Torvalds 	serr->port = port;
4341da177e4SLinus Torvalds 
43500db4124SIan Morris 	if (skb_pull(skb, payload - skb->data)) {
4368e8cfb11SEric Dumazet 		if (inet_test_bit(RECVERR_RFC4884, sk))
437178c49d9SWillem de Bruijn 			ipv4_icmp_error_rfc4884(skb, &serr->ee.ee_rfc4884);
438eba75c58SWillem de Bruijn 
439bd82393cSArnaldo Carvalho de Melo 		skb_reset_transport_header(skb);
440bd82393cSArnaldo Carvalho de Melo 		if (sock_queue_err_skb(sk, skb) == 0)
441bd82393cSArnaldo Carvalho de Melo 			return;
442bd82393cSArnaldo Carvalho de Melo 	}
4431da177e4SLinus Torvalds 	kfree_skb(skb);
4441da177e4SLinus Torvalds }
44542fb06b3SDavid Howells EXPORT_SYMBOL_GPL(ip_icmp_error);
4461da177e4SLinus Torvalds 
4470579016eSAl Viro void ip_local_error(struct sock *sk, int err, __be32 daddr, __be16 port, u32 info)
4481da177e4SLinus Torvalds {
4491da177e4SLinus Torvalds 	struct sock_exterr_skb *serr;
4501da177e4SLinus Torvalds 	struct iphdr *iph;
4511da177e4SLinus Torvalds 	struct sk_buff *skb;
4521da177e4SLinus Torvalds 
4536b5f43eaSEric Dumazet 	if (!inet_test_bit(RECVERR, sk))
4541da177e4SLinus Torvalds 		return;
4551da177e4SLinus Torvalds 
4561da177e4SLinus Torvalds 	skb = alloc_skb(sizeof(struct iphdr), GFP_ATOMIC);
4571da177e4SLinus Torvalds 	if (!skb)
4581da177e4SLinus Torvalds 		return;
4591da177e4SLinus Torvalds 
4602ca9e6f2SArnaldo Carvalho de Melo 	skb_put(skb, sizeof(struct iphdr));
4612ca9e6f2SArnaldo Carvalho de Melo 	skb_reset_network_header(skb);
462eddc9ec5SArnaldo Carvalho de Melo 	iph = ip_hdr(skb);
4631da177e4SLinus Torvalds 	iph->daddr = daddr;
4641da177e4SLinus Torvalds 
4651da177e4SLinus Torvalds 	serr = SKB_EXT_ERR(skb);
4661da177e4SLinus Torvalds 	serr->ee.ee_errno = err;
4671da177e4SLinus Torvalds 	serr->ee.ee_origin = SO_EE_ORIGIN_LOCAL;
4681da177e4SLinus Torvalds 	serr->ee.ee_type = 0;
4691da177e4SLinus Torvalds 	serr->ee.ee_code = 0;
4701da177e4SLinus Torvalds 	serr->ee.ee_pad = 0;
4711da177e4SLinus Torvalds 	serr->ee.ee_info = info;
4721da177e4SLinus Torvalds 	serr->ee.ee_data = 0;
473d56f90a7SArnaldo Carvalho de Melo 	serr->addr_offset = (u8 *)&iph->daddr - skb_network_header(skb);
4741da177e4SLinus Torvalds 	serr->port = port;
4751da177e4SLinus Torvalds 
47627a884dcSArnaldo Carvalho de Melo 	__skb_pull(skb, skb_tail_pointer(skb) - skb->data);
477bd82393cSArnaldo Carvalho de Melo 	skb_reset_transport_header(skb);
4781da177e4SLinus Torvalds 
4791da177e4SLinus Torvalds 	if (sock_queue_err_skb(sk, skb))
4801da177e4SLinus Torvalds 		kfree_skb(skb);
4811da177e4SLinus Torvalds }
4821da177e4SLinus Torvalds 
48334b99df4SJulian Anastasov /* For some errors we have valid addr_offset even with zero payload and
48434b99df4SJulian Anastasov  * zero port. Also, addr_offset should be supported if port is set.
48534b99df4SJulian Anastasov  */
48634b99df4SJulian Anastasov static inline bool ipv4_datagram_support_addr(struct sock_exterr_skb *serr)
48734b99df4SJulian Anastasov {
48834b99df4SJulian Anastasov 	return serr->ee.ee_origin == SO_EE_ORIGIN_ICMP ||
48934b99df4SJulian Anastasov 	       serr->ee.ee_origin == SO_EE_ORIGIN_LOCAL || serr->port;
49034b99df4SJulian Anastasov }
49134b99df4SJulian Anastasov 
492c247f053SWillem de Bruijn /* IPv4 supports cmsg on all imcp errors and some timestamps
493c247f053SWillem de Bruijn  *
494c247f053SWillem de Bruijn  * Timestamp code paths do not initialize the fields expected by cmsg:
495c247f053SWillem de Bruijn  * the PKTINFO fields in skb->cb[]. Fill those in here.
496c247f053SWillem de Bruijn  */
497c247f053SWillem de Bruijn static bool ipv4_datagram_support_cmsg(const struct sock *sk,
498c247f053SWillem de Bruijn 				       struct sk_buff *skb,
499829ae9d6SWillem de Bruijn 				       int ee_origin)
500829ae9d6SWillem de Bruijn {
501c247f053SWillem de Bruijn 	struct in_pktinfo *info;
502829ae9d6SWillem de Bruijn 
503c247f053SWillem de Bruijn 	if (ee_origin == SO_EE_ORIGIN_ICMP)
504c247f053SWillem de Bruijn 		return true;
505c247f053SWillem de Bruijn 
506c247f053SWillem de Bruijn 	if (ee_origin == SO_EE_ORIGIN_LOCAL)
507c247f053SWillem de Bruijn 		return false;
508c247f053SWillem de Bruijn 
509c247f053SWillem de Bruijn 	/* Support IP_PKTINFO on tstamp packets if requested, to correlate
5101862d620SWillem de Bruijn 	 * timestamp with egress dev. Not possible for packets without iif
511c247f053SWillem de Bruijn 	 * or without payload (SOF_TIMESTAMPING_OPT_TSONLY).
512c247f053SWillem de Bruijn 	 */
5131862d620SWillem de Bruijn 	info = PKTINFO_SKB_CB(skb);
514e3390b30SEric Dumazet 	if (!(READ_ONCE(sk->sk_tsflags) & SOF_TIMESTAMPING_OPT_CMSG) ||
5151862d620SWillem de Bruijn 	    !info->ipi_ifindex)
516829ae9d6SWillem de Bruijn 		return false;
517829ae9d6SWillem de Bruijn 
518829ae9d6SWillem de Bruijn 	info->ipi_spec_dst.s_addr = ip_hdr(skb)->saddr;
519829ae9d6SWillem de Bruijn 	return true;
520829ae9d6SWillem de Bruijn }
521829ae9d6SWillem de Bruijn 
5221da177e4SLinus Torvalds /*
5231da177e4SLinus Torvalds  *	Handle MSG_ERRQUEUE
5241da177e4SLinus Torvalds  */
52585fbaa75SHannes Frederic Sowa int ip_recv_error(struct sock *sk, struct msghdr *msg, int len, int *addr_len)
5261da177e4SLinus Torvalds {
5271da177e4SLinus Torvalds 	struct sock_exterr_skb *serr;
528364a9e93SWillem de Bruijn 	struct sk_buff *skb;
529342dfc30SSteffen Hurrle 	DECLARE_SOCKADDR(struct sockaddr_in *, sin, msg->msg_name);
5301da177e4SLinus Torvalds 	struct {
5311da177e4SLinus Torvalds 		struct sock_extended_err ee;
5321da177e4SLinus Torvalds 		struct sockaddr_in	 offender;
5331da177e4SLinus Torvalds 	} errhdr;
5341da177e4SLinus Torvalds 	int err;
5351da177e4SLinus Torvalds 	int copied;
5361da177e4SLinus Torvalds 
5371da177e4SLinus Torvalds 	err = -EAGAIN;
538364a9e93SWillem de Bruijn 	skb = sock_dequeue_err_skb(sk);
53951456b29SIan Morris 	if (!skb)
5401da177e4SLinus Torvalds 		goto out;
5411da177e4SLinus Torvalds 
5421da177e4SLinus Torvalds 	copied = skb->len;
5431da177e4SLinus Torvalds 	if (copied > len) {
5441da177e4SLinus Torvalds 		msg->msg_flags |= MSG_TRUNC;
5451da177e4SLinus Torvalds 		copied = len;
5461da177e4SLinus Torvalds 	}
54751f3d02bSDavid S. Miller 	err = skb_copy_datagram_msg(skb, 0, msg, copied);
548960a2628SEric Dumazet 	if (unlikely(err)) {
549960a2628SEric Dumazet 		kfree_skb(skb);
550960a2628SEric Dumazet 		return err;
551960a2628SEric Dumazet 	}
5521da177e4SLinus Torvalds 	sock_recv_timestamp(msg, sk, skb);
5531da177e4SLinus Torvalds 
5541da177e4SLinus Torvalds 	serr = SKB_EXT_ERR(skb);
5551da177e4SLinus Torvalds 
55634b99df4SJulian Anastasov 	if (sin && ipv4_datagram_support_addr(serr)) {
5571da177e4SLinus Torvalds 		sin->sin_family = AF_INET;
558d56f90a7SArnaldo Carvalho de Melo 		sin->sin_addr.s_addr = *(__be32 *)(skb_network_header(skb) +
559d56f90a7SArnaldo Carvalho de Melo 						   serr->addr_offset);
5601da177e4SLinus Torvalds 		sin->sin_port = serr->port;
5611da177e4SLinus Torvalds 		memset(&sin->sin_zero, 0, sizeof(sin->sin_zero));
56285fbaa75SHannes Frederic Sowa 		*addr_len = sizeof(*sin);
5631da177e4SLinus Torvalds 	}
5641da177e4SLinus Torvalds 
5651da177e4SLinus Torvalds 	memcpy(&errhdr.ee, &serr->ee, sizeof(struct sock_extended_err));
5661da177e4SLinus Torvalds 	sin = &errhdr.offender;
567f812116bSWillem de Bruijn 	memset(sin, 0, sizeof(*sin));
568829ae9d6SWillem de Bruijn 
569c247f053SWillem de Bruijn 	if (ipv4_datagram_support_cmsg(sk, skb, serr->ee.ee_origin)) {
5701da177e4SLinus Torvalds 		sin->sin_family = AF_INET;
571eddc9ec5SArnaldo Carvalho de Melo 		sin->sin_addr.s_addr = ip_hdr(skb)->saddr;
572c274af22SEric Dumazet 		if (inet_cmsg_flags(inet_sk(sk)))
5731da177e4SLinus Torvalds 			ip_cmsg_recv(msg, skb);
5741da177e4SLinus Torvalds 	}
5751da177e4SLinus Torvalds 
5761da177e4SLinus Torvalds 	put_cmsg(msg, SOL_IP, IP_RECVERR, sizeof(errhdr), &errhdr);
5771da177e4SLinus Torvalds 
5781da177e4SLinus Torvalds 	/* Now we could try to dump offended packet options */
5791da177e4SLinus Torvalds 
5801da177e4SLinus Torvalds 	msg->msg_flags |= MSG_ERRQUEUE;
5811da177e4SLinus Torvalds 	err = copied;
5821da177e4SLinus Torvalds 
583960a2628SEric Dumazet 	consume_skb(skb);
5841da177e4SLinus Torvalds out:
5851da177e4SLinus Torvalds 	return err;
5861da177e4SLinus Torvalds }
5871da177e4SLinus Torvalds 
588e08d0b3dSEric Dumazet void ip_sock_set_tos(struct sock *sk, int val)
5896ebf71baSChristoph Hellwig {
590e08d0b3dSEric Dumazet 	u8 old_tos = READ_ONCE(inet_sk(sk)->tos);
591e08d0b3dSEric Dumazet 
5926ebf71baSChristoph Hellwig 	if (sk->sk_type == SOCK_STREAM) {
5936ebf71baSChristoph Hellwig 		val &= ~INET_ECN_MASK;
594e08d0b3dSEric Dumazet 		val |= old_tos & INET_ECN_MASK;
5956ebf71baSChristoph Hellwig 	}
596e08d0b3dSEric Dumazet 	if (old_tos != val) {
597e08d0b3dSEric Dumazet 		WRITE_ONCE(inet_sk(sk)->tos, val);
5988bf43be7SEric Dumazet 		WRITE_ONCE(sk->sk_priority, rt_tos2priority(val));
5996ebf71baSChristoph Hellwig 		sk_dst_reset(sk);
6006ebf71baSChristoph Hellwig 	}
6016ebf71baSChristoph Hellwig }
6026ebf71baSChristoph Hellwig EXPORT_SYMBOL(ip_sock_set_tos);
6031da177e4SLinus Torvalds 
604c4e446bfSChristoph Hellwig void ip_sock_set_freebind(struct sock *sk)
605c4e446bfSChristoph Hellwig {
6063f7e7532SEric Dumazet 	inet_set_bit(FREEBIND, sk);
607c4e446bfSChristoph Hellwig }
608c4e446bfSChristoph Hellwig EXPORT_SYMBOL(ip_sock_set_freebind);
609c4e446bfSChristoph Hellwig 
610db45c0efSChristoph Hellwig void ip_sock_set_recverr(struct sock *sk)
611db45c0efSChristoph Hellwig {
6126b5f43eaSEric Dumazet 	inet_set_bit(RECVERR, sk);
613db45c0efSChristoph Hellwig }
614db45c0efSChristoph Hellwig EXPORT_SYMBOL(ip_sock_set_recverr);
615db45c0efSChristoph Hellwig 
6162de569bdSChristoph Hellwig int ip_sock_set_mtu_discover(struct sock *sk, int val)
6172de569bdSChristoph Hellwig {
6182de569bdSChristoph Hellwig 	if (val < IP_PMTUDISC_DONT || val > IP_PMTUDISC_OMIT)
6192de569bdSChristoph Hellwig 		return -EINVAL;
620ceaa7141SEric Dumazet 	WRITE_ONCE(inet_sk(sk)->pmtudisc, val);
6212de569bdSChristoph Hellwig 	return 0;
6222de569bdSChristoph Hellwig }
6232de569bdSChristoph Hellwig EXPORT_SYMBOL(ip_sock_set_mtu_discover);
6242de569bdSChristoph Hellwig 
625c1f9ec57SChristoph Hellwig void ip_sock_set_pktinfo(struct sock *sk)
626c1f9ec57SChristoph Hellwig {
627c274af22SEric Dumazet 	inet_set_bit(PKTINFO, sk);
628c1f9ec57SChristoph Hellwig }
629c1f9ec57SChristoph Hellwig EXPORT_SYMBOL(ip_sock_set_pktinfo);
630c1f9ec57SChristoph Hellwig 
6311da177e4SLinus Torvalds /*
6324d52cfbeSEric Dumazet  *	Socket option code for IP. This is the end of the line after any
6334d52cfbeSEric Dumazet  *	TCP,UDP etc options on an IP socket.
6341da177e4SLinus Torvalds  */
635baf606d9SMarcelo Ricardo Leitner static bool setsockopt_needs_rtnl(int optname)
636baf606d9SMarcelo Ricardo Leitner {
637baf606d9SMarcelo Ricardo Leitner 	switch (optname) {
638baf606d9SMarcelo Ricardo Leitner 	case IP_ADD_MEMBERSHIP:
639baf606d9SMarcelo Ricardo Leitner 	case IP_ADD_SOURCE_MEMBERSHIP:
64054ff9ef3SMarcelo Ricardo Leitner 	case IP_BLOCK_SOURCE:
641baf606d9SMarcelo Ricardo Leitner 	case IP_DROP_MEMBERSHIP:
64254ff9ef3SMarcelo Ricardo Leitner 	case IP_DROP_SOURCE_MEMBERSHIP:
64354ff9ef3SMarcelo Ricardo Leitner 	case IP_MSFILTER:
64454ff9ef3SMarcelo Ricardo Leitner 	case IP_UNBLOCK_SOURCE:
64554ff9ef3SMarcelo Ricardo Leitner 	case MCAST_BLOCK_SOURCE:
64654ff9ef3SMarcelo Ricardo Leitner 	case MCAST_MSFILTER:
647baf606d9SMarcelo Ricardo Leitner 	case MCAST_JOIN_GROUP:
64854ff9ef3SMarcelo Ricardo Leitner 	case MCAST_JOIN_SOURCE_GROUP:
649baf606d9SMarcelo Ricardo Leitner 	case MCAST_LEAVE_GROUP:
65054ff9ef3SMarcelo Ricardo Leitner 	case MCAST_LEAVE_SOURCE_GROUP:
65154ff9ef3SMarcelo Ricardo Leitner 	case MCAST_UNBLOCK_SOURCE:
652baf606d9SMarcelo Ricardo Leitner 		return true;
653baf606d9SMarcelo Ricardo Leitner 	}
654baf606d9SMarcelo Ricardo Leitner 	return false;
655baf606d9SMarcelo Ricardo Leitner }
6561da177e4SLinus Torvalds 
657e986d4daSAl Viro static int set_mcast_msfilter(struct sock *sk, int ifindex,
658e986d4daSAl Viro 			      int numsrc, int fmode,
659e986d4daSAl Viro 			      struct sockaddr_storage *group,
660e986d4daSAl Viro 			      struct sockaddr_storage *list)
661e986d4daSAl Viro {
662e986d4daSAl Viro 	struct ip_msfilter *msf;
663e986d4daSAl Viro 	struct sockaddr_in *psin;
664e986d4daSAl Viro 	int err, i;
665e986d4daSAl Viro 
6664167a960SGustavo A. R. Silva 	msf = kmalloc(IP_MSFILTER_SIZE(numsrc), GFP_KERNEL);
667e986d4daSAl Viro 	if (!msf)
668e986d4daSAl Viro 		return -ENOBUFS;
669e986d4daSAl Viro 
670e986d4daSAl Viro 	psin = (struct sockaddr_in *)group;
671e986d4daSAl Viro 	if (psin->sin_family != AF_INET)
672e986d4daSAl Viro 		goto Eaddrnotavail;
673e986d4daSAl Viro 	msf->imsf_multiaddr = psin->sin_addr.s_addr;
674e986d4daSAl Viro 	msf->imsf_interface = 0;
675e986d4daSAl Viro 	msf->imsf_fmode = fmode;
676e986d4daSAl Viro 	msf->imsf_numsrc = numsrc;
677e986d4daSAl Viro 	for (i = 0; i < numsrc; ++i) {
678e986d4daSAl Viro 		psin = (struct sockaddr_in *)&list[i];
679e986d4daSAl Viro 
680e986d4daSAl Viro 		if (psin->sin_family != AF_INET)
681e986d4daSAl Viro 			goto Eaddrnotavail;
6822d3e5cafSGustavo A. R. Silva 		msf->imsf_slist_flex[i] = psin->sin_addr.s_addr;
683e986d4daSAl Viro 	}
684e986d4daSAl Viro 	err = ip_mc_msfilter(sk, msf, ifindex);
685e986d4daSAl Viro 	kfree(msf);
686e986d4daSAl Viro 	return err;
687e986d4daSAl Viro 
688e986d4daSAl Viro Eaddrnotavail:
689e986d4daSAl Viro 	kfree(msf);
690e986d4daSAl Viro 	return -EADDRNOTAVAIL;
691e986d4daSAl Viro }
692e986d4daSAl Viro 
69389654c5fSChristoph Hellwig static int copy_group_source_from_sockptr(struct group_source_req *greqs,
69489654c5fSChristoph Hellwig 		sockptr_t optval, int optlen)
6952bbf8c1eSAl Viro {
696b6238c04SChristoph Hellwig 	if (in_compat_syscall()) {
697b6238c04SChristoph Hellwig 		struct compat_group_source_req gr32;
698b6238c04SChristoph Hellwig 
699b6238c04SChristoph Hellwig 		if (optlen != sizeof(gr32))
700b6238c04SChristoph Hellwig 			return -EINVAL;
70189654c5fSChristoph Hellwig 		if (copy_from_sockptr(&gr32, optval, sizeof(gr32)))
702b6238c04SChristoph Hellwig 			return -EFAULT;
703b6238c04SChristoph Hellwig 		greqs->gsr_interface = gr32.gsr_interface;
704b6238c04SChristoph Hellwig 		greqs->gsr_group = gr32.gsr_group;
705b6238c04SChristoph Hellwig 		greqs->gsr_source = gr32.gsr_source;
706b6238c04SChristoph Hellwig 	} else {
707b6238c04SChristoph Hellwig 		if (optlen != sizeof(*greqs))
708b6238c04SChristoph Hellwig 			return -EINVAL;
70989654c5fSChristoph Hellwig 		if (copy_from_sockptr(greqs, optval, sizeof(*greqs)))
710b6238c04SChristoph Hellwig 			return -EFAULT;
711b6238c04SChristoph Hellwig 	}
712b6238c04SChristoph Hellwig 
713b6238c04SChristoph Hellwig 	return 0;
714b6238c04SChristoph Hellwig }
715b6238c04SChristoph Hellwig 
716b6238c04SChristoph Hellwig static int do_mcast_group_source(struct sock *sk, int optname,
71789654c5fSChristoph Hellwig 		sockptr_t optval, int optlen)
718b6238c04SChristoph Hellwig {
719b6238c04SChristoph Hellwig 	struct group_source_req greqs;
7202bbf8c1eSAl Viro 	struct ip_mreq_source mreqs;
7212bbf8c1eSAl Viro 	struct sockaddr_in *psin;
7222bbf8c1eSAl Viro 	int omode, add, err;
7232bbf8c1eSAl Viro 
72489654c5fSChristoph Hellwig 	err = copy_group_source_from_sockptr(&greqs, optval, optlen);
725b6238c04SChristoph Hellwig 	if (err)
726b6238c04SChristoph Hellwig 		return err;
727b6238c04SChristoph Hellwig 
728b6238c04SChristoph Hellwig 	if (greqs.gsr_group.ss_family != AF_INET ||
729b6238c04SChristoph Hellwig 	    greqs.gsr_source.ss_family != AF_INET)
7302bbf8c1eSAl Viro 		return -EADDRNOTAVAIL;
7312bbf8c1eSAl Viro 
732b6238c04SChristoph Hellwig 	psin = (struct sockaddr_in *)&greqs.gsr_group;
7332bbf8c1eSAl Viro 	mreqs.imr_multiaddr = psin->sin_addr.s_addr;
734b6238c04SChristoph Hellwig 	psin = (struct sockaddr_in *)&greqs.gsr_source;
7352bbf8c1eSAl Viro 	mreqs.imr_sourceaddr = psin->sin_addr.s_addr;
7362bbf8c1eSAl Viro 	mreqs.imr_interface = 0; /* use index for mc_source */
7372bbf8c1eSAl Viro 
7382bbf8c1eSAl Viro 	if (optname == MCAST_BLOCK_SOURCE) {
7392bbf8c1eSAl Viro 		omode = MCAST_EXCLUDE;
7402bbf8c1eSAl Viro 		add = 1;
7412bbf8c1eSAl Viro 	} else if (optname == MCAST_UNBLOCK_SOURCE) {
7422bbf8c1eSAl Viro 		omode = MCAST_EXCLUDE;
7432bbf8c1eSAl Viro 		add = 0;
7442bbf8c1eSAl Viro 	} else if (optname == MCAST_JOIN_SOURCE_GROUP) {
7452bbf8c1eSAl Viro 		struct ip_mreqn mreq;
7462bbf8c1eSAl Viro 
747b6238c04SChristoph Hellwig 		psin = (struct sockaddr_in *)&greqs.gsr_group;
7482bbf8c1eSAl Viro 		mreq.imr_multiaddr = psin->sin_addr;
7492bbf8c1eSAl Viro 		mreq.imr_address.s_addr = 0;
750b6238c04SChristoph Hellwig 		mreq.imr_ifindex = greqs.gsr_interface;
7512bbf8c1eSAl Viro 		err = ip_mc_join_group_ssm(sk, &mreq, MCAST_INCLUDE);
7522bbf8c1eSAl Viro 		if (err && err != -EADDRINUSE)
7532bbf8c1eSAl Viro 			return err;
754b6238c04SChristoph Hellwig 		greqs.gsr_interface = mreq.imr_ifindex;
7552bbf8c1eSAl Viro 		omode = MCAST_INCLUDE;
7562bbf8c1eSAl Viro 		add = 1;
7572bbf8c1eSAl Viro 	} else /* MCAST_LEAVE_SOURCE_GROUP */ {
7582bbf8c1eSAl Viro 		omode = MCAST_INCLUDE;
7592bbf8c1eSAl Viro 		add = 0;
7602bbf8c1eSAl Viro 	}
761b6238c04SChristoph Hellwig 	return ip_mc_source(add, omode, sk, &mreqs, greqs.gsr_interface);
7622bbf8c1eSAl Viro }
7632bbf8c1eSAl Viro 
76489654c5fSChristoph Hellwig static int ip_set_mcast_msfilter(struct sock *sk, sockptr_t optval, int optlen)
765d62c38f6SChristoph Hellwig {
766d62c38f6SChristoph Hellwig 	struct group_filter *gsf = NULL;
767d62c38f6SChristoph Hellwig 	int err;
768d62c38f6SChristoph Hellwig 
769d62c38f6SChristoph Hellwig 	if (optlen < GROUP_FILTER_SIZE(0))
770d62c38f6SChristoph Hellwig 		return -EINVAL;
7717de6d09fSKuniyuki Iwashima 	if (optlen > READ_ONCE(sysctl_optmem_max))
772d62c38f6SChristoph Hellwig 		return -ENOBUFS;
773d62c38f6SChristoph Hellwig 
77489654c5fSChristoph Hellwig 	gsf = memdup_sockptr(optval, optlen);
775d62c38f6SChristoph Hellwig 	if (IS_ERR(gsf))
776d62c38f6SChristoph Hellwig 		return PTR_ERR(gsf);
777d62c38f6SChristoph Hellwig 
778d62c38f6SChristoph Hellwig 	/* numsrc >= (4G-140)/128 overflow in 32 bits */
779d62c38f6SChristoph Hellwig 	err = -ENOBUFS;
780d62c38f6SChristoph Hellwig 	if (gsf->gf_numsrc >= 0x1ffffff ||
7816ae0f2e5SKuniyuki Iwashima 	    gsf->gf_numsrc > READ_ONCE(sock_net(sk)->ipv4.sysctl_igmp_max_msf))
782d62c38f6SChristoph Hellwig 		goto out_free_gsf;
783d62c38f6SChristoph Hellwig 
784d62c38f6SChristoph Hellwig 	err = -EINVAL;
785d62c38f6SChristoph Hellwig 	if (GROUP_FILTER_SIZE(gsf->gf_numsrc) > optlen)
786d62c38f6SChristoph Hellwig 		goto out_free_gsf;
787d62c38f6SChristoph Hellwig 
788d62c38f6SChristoph Hellwig 	err = set_mcast_msfilter(sk, gsf->gf_interface, gsf->gf_numsrc,
789db243b79SGustavo A. R. Silva 				 gsf->gf_fmode, &gsf->gf_group,
790db243b79SGustavo A. R. Silva 				 gsf->gf_slist_flex);
791d62c38f6SChristoph Hellwig out_free_gsf:
792d62c38f6SChristoph Hellwig 	kfree(gsf);
793d62c38f6SChristoph Hellwig 	return err;
794d62c38f6SChristoph Hellwig }
795d62c38f6SChristoph Hellwig 
79689654c5fSChristoph Hellwig static int compat_ip_set_mcast_msfilter(struct sock *sk, sockptr_t optval,
797d62c38f6SChristoph Hellwig 		int optlen)
798d62c38f6SChristoph Hellwig {
799db243b79SGustavo A. R. Silva 	const int size0 = offsetof(struct compat_group_filter, gf_slist_flex);
800d62c38f6SChristoph Hellwig 	struct compat_group_filter *gf32;
801d62c38f6SChristoph Hellwig 	unsigned int n;
802d62c38f6SChristoph Hellwig 	void *p;
803d62c38f6SChristoph Hellwig 	int err;
804d62c38f6SChristoph Hellwig 
805d62c38f6SChristoph Hellwig 	if (optlen < size0)
806d62c38f6SChristoph Hellwig 		return -EINVAL;
8077de6d09fSKuniyuki Iwashima 	if (optlen > READ_ONCE(sysctl_optmem_max) - 4)
808d62c38f6SChristoph Hellwig 		return -ENOBUFS;
809d62c38f6SChristoph Hellwig 
810d62c38f6SChristoph Hellwig 	p = kmalloc(optlen + 4, GFP_KERNEL);
811d62c38f6SChristoph Hellwig 	if (!p)
812d62c38f6SChristoph Hellwig 		return -ENOMEM;
813db243b79SGustavo A. R. Silva 	gf32 = p + 4; /* we want ->gf_group and ->gf_slist_flex aligned */
814d62c38f6SChristoph Hellwig 
815d62c38f6SChristoph Hellwig 	err = -EFAULT;
81689654c5fSChristoph Hellwig 	if (copy_from_sockptr(gf32, optval, optlen))
817d62c38f6SChristoph Hellwig 		goto out_free_gsf;
818d62c38f6SChristoph Hellwig 
819d62c38f6SChristoph Hellwig 	/* numsrc >= (4G-140)/128 overflow in 32 bits */
820d62c38f6SChristoph Hellwig 	n = gf32->gf_numsrc;
821d62c38f6SChristoph Hellwig 	err = -ENOBUFS;
822d62c38f6SChristoph Hellwig 	if (n >= 0x1ffffff)
823d62c38f6SChristoph Hellwig 		goto out_free_gsf;
824d62c38f6SChristoph Hellwig 
825d62c38f6SChristoph Hellwig 	err = -EINVAL;
826db243b79SGustavo A. R. Silva 	if (offsetof(struct compat_group_filter, gf_slist_flex[n]) > optlen)
827d62c38f6SChristoph Hellwig 		goto out_free_gsf;
828d62c38f6SChristoph Hellwig 
829d62c38f6SChristoph Hellwig 	/* numsrc >= (4G-140)/128 overflow in 32 bits */
830d62c38f6SChristoph Hellwig 	err = -ENOBUFS;
8316ae0f2e5SKuniyuki Iwashima 	if (n > READ_ONCE(sock_net(sk)->ipv4.sysctl_igmp_max_msf))
832b6238c04SChristoph Hellwig 		goto out_free_gsf;
833d62c38f6SChristoph Hellwig 	err = set_mcast_msfilter(sk, gf32->gf_interface, n, gf32->gf_fmode,
834db243b79SGustavo A. R. Silva 				 &gf32->gf_group, gf32->gf_slist_flex);
835d62c38f6SChristoph Hellwig out_free_gsf:
836d62c38f6SChristoph Hellwig 	kfree(p);
837d62c38f6SChristoph Hellwig 	return err;
838d62c38f6SChristoph Hellwig }
839d62c38f6SChristoph Hellwig 
84002caad7cSChristoph Hellwig static int ip_mcast_join_leave(struct sock *sk, int optname,
84189654c5fSChristoph Hellwig 		sockptr_t optval, int optlen)
84202caad7cSChristoph Hellwig {
84302caad7cSChristoph Hellwig 	struct ip_mreqn mreq = { };
84402caad7cSChristoph Hellwig 	struct sockaddr_in *psin;
84502caad7cSChristoph Hellwig 	struct group_req greq;
84602caad7cSChristoph Hellwig 
84702caad7cSChristoph Hellwig 	if (optlen < sizeof(struct group_req))
84802caad7cSChristoph Hellwig 		return -EINVAL;
84989654c5fSChristoph Hellwig 	if (copy_from_sockptr(&greq, optval, sizeof(greq)))
85002caad7cSChristoph Hellwig 		return -EFAULT;
85102caad7cSChristoph Hellwig 
85202caad7cSChristoph Hellwig 	psin = (struct sockaddr_in *)&greq.gr_group;
85302caad7cSChristoph Hellwig 	if (psin->sin_family != AF_INET)
85402caad7cSChristoph Hellwig 		return -EINVAL;
85502caad7cSChristoph Hellwig 	mreq.imr_multiaddr = psin->sin_addr;
85602caad7cSChristoph Hellwig 	mreq.imr_ifindex = greq.gr_interface;
85702caad7cSChristoph Hellwig 	if (optname == MCAST_JOIN_GROUP)
85802caad7cSChristoph Hellwig 		return ip_mc_join_group(sk, &mreq);
85902caad7cSChristoph Hellwig 	return ip_mc_leave_group(sk, &mreq);
86002caad7cSChristoph Hellwig }
86102caad7cSChristoph Hellwig 
86202caad7cSChristoph Hellwig static int compat_ip_mcast_join_leave(struct sock *sk, int optname,
86389654c5fSChristoph Hellwig 		sockptr_t optval, int optlen)
86402caad7cSChristoph Hellwig {
86502caad7cSChristoph Hellwig 	struct compat_group_req greq;
86602caad7cSChristoph Hellwig 	struct ip_mreqn mreq = { };
86702caad7cSChristoph Hellwig 	struct sockaddr_in *psin;
86802caad7cSChristoph Hellwig 
86902caad7cSChristoph Hellwig 	if (optlen < sizeof(struct compat_group_req))
87002caad7cSChristoph Hellwig 		return -EINVAL;
87189654c5fSChristoph Hellwig 	if (copy_from_sockptr(&greq, optval, sizeof(greq)))
87202caad7cSChristoph Hellwig 		return -EFAULT;
87302caad7cSChristoph Hellwig 
87402caad7cSChristoph Hellwig 	psin = (struct sockaddr_in *)&greq.gr_group;
87502caad7cSChristoph Hellwig 	if (psin->sin_family != AF_INET)
87602caad7cSChristoph Hellwig 		return -EINVAL;
87702caad7cSChristoph Hellwig 	mreq.imr_multiaddr = psin->sin_addr;
87802caad7cSChristoph Hellwig 	mreq.imr_ifindex = greq.gr_interface;
87902caad7cSChristoph Hellwig 
88002caad7cSChristoph Hellwig 	if (optname == MCAST_JOIN_GROUP)
881b6238c04SChristoph Hellwig 		return ip_mc_join_group(sk, &mreq);
882b6238c04SChristoph Hellwig 	return ip_mc_leave_group(sk, &mreq);
88302caad7cSChristoph Hellwig }
88402caad7cSChristoph Hellwig 
885020e71a3SEric Dumazet DEFINE_STATIC_KEY_FALSE(ip4_min_ttl);
886020e71a3SEric Dumazet 
887ee7f1e13SMartin KaFai Lau int do_ip_setsockopt(struct sock *sk, int level, int optname,
88889654c5fSChristoph Hellwig 		     sockptr_t optval, unsigned int optlen)
8891da177e4SLinus Torvalds {
8901da177e4SLinus Torvalds 	struct inet_sock *inet = inet_sk(sk);
891166b6b2dSNikolay Borisov 	struct net *net = sock_net(sk);
8921da177e4SLinus Torvalds 	int val = 0, err;
893baf606d9SMarcelo Ricardo Leitner 	bool needs_rtnl = setsockopt_needs_rtnl(optname);
8941da177e4SLinus Torvalds 
8950c9f79beSXi Wang 	switch (optname) {
8960c9f79beSXi Wang 	case IP_PKTINFO:
8970c9f79beSXi Wang 	case IP_RECVTTL:
8980c9f79beSXi Wang 	case IP_RECVOPTS:
8990c9f79beSXi Wang 	case IP_RECVTOS:
9000c9f79beSXi Wang 	case IP_RETOPTS:
9010c9f79beSXi Wang 	case IP_TOS:
9020c9f79beSXi Wang 	case IP_TTL:
9030c9f79beSXi Wang 	case IP_HDRINCL:
9040c9f79beSXi Wang 	case IP_MTU_DISCOVER:
9050c9f79beSXi Wang 	case IP_RECVERR:
9060c9f79beSXi Wang 	case IP_ROUTER_ALERT:
9070c9f79beSXi Wang 	case IP_FREEBIND:
9080c9f79beSXi Wang 	case IP_PASSSEC:
9090c9f79beSXi Wang 	case IP_TRANSPARENT:
9100c9f79beSXi Wang 	case IP_MINTTL:
9110c9f79beSXi Wang 	case IP_NODEFRAG:
91290c337daSEric Dumazet 	case IP_BIND_ADDRESS_NO_PORT:
9130c9f79beSXi Wang 	case IP_UNICAST_IF:
9140c9f79beSXi Wang 	case IP_MULTICAST_TTL:
9150c9f79beSXi Wang 	case IP_MULTICAST_ALL:
9160c9f79beSXi Wang 	case IP_MULTICAST_LOOP:
9170c9f79beSXi Wang 	case IP_RECVORIGDSTADDR:
918ad6f939aSTom Herbert 	case IP_CHECKSUM:
91970ecc248SWillem de Bruijn 	case IP_RECVFRAGSIZE:
920eba75c58SWillem de Bruijn 	case IP_RECVERR_RFC4884:
92191d0b78cSJakub Sitnicki 	case IP_LOCAL_PORT_RANGE:
9221da177e4SLinus Torvalds 		if (optlen >= sizeof(int)) {
92389654c5fSChristoph Hellwig 			if (copy_from_sockptr(&val, optval, sizeof(val)))
9241da177e4SLinus Torvalds 				return -EFAULT;
9251da177e4SLinus Torvalds 		} else if (optlen >= sizeof(char)) {
9261da177e4SLinus Torvalds 			unsigned char ucval;
9271da177e4SLinus Torvalds 
92889654c5fSChristoph Hellwig 			if (copy_from_sockptr(&ucval, optval, sizeof(ucval)))
9291da177e4SLinus Torvalds 				return -EFAULT;
9301da177e4SLinus Torvalds 			val = (int) ucval;
9311da177e4SLinus Torvalds 		}
9321da177e4SLinus Torvalds 	}
9331da177e4SLinus Torvalds 
9341da177e4SLinus Torvalds 	/* If optlen==0, it is equivalent to val == 0 */
9351da177e4SLinus Torvalds 
9360526947fSKirill Tkhai 	if (optname == IP_ROUTER_ALERT)
9370526947fSKirill Tkhai 		return ip_ra_control(sk, val ? 1 : 0, NULL);
9386a9fb947SPavel Emelyanov 	if (ip_mroute_opt(optname))
93989654c5fSChristoph Hellwig 		return ip_mroute_setsockopt(sk, optname, optval, optlen);
9401da177e4SLinus Torvalds 
941b4d84bceSEric Dumazet 	/* Handle options that can be set without locking the socket. */
942b4d84bceSEric Dumazet 	switch (optname) {
943b4d84bceSEric Dumazet 	case IP_PKTINFO:
944b4d84bceSEric Dumazet 		inet_assign_bit(PKTINFO, sk, val);
945b4d84bceSEric Dumazet 		return 0;
946b4d84bceSEric Dumazet 	case IP_RECVTTL:
947b4d84bceSEric Dumazet 		inet_assign_bit(TTL, sk, val);
948b4d84bceSEric Dumazet 		return 0;
949b4d84bceSEric Dumazet 	case IP_RECVTOS:
950b4d84bceSEric Dumazet 		inet_assign_bit(TOS, sk, val);
951b4d84bceSEric Dumazet 		return 0;
952b4d84bceSEric Dumazet 	case IP_RECVOPTS:
953b4d84bceSEric Dumazet 		inet_assign_bit(RECVOPTS, sk, val);
954b4d84bceSEric Dumazet 		return 0;
955b4d84bceSEric Dumazet 	case IP_RETOPTS:
956b4d84bceSEric Dumazet 		inet_assign_bit(RETOPTS, sk, val);
957b4d84bceSEric Dumazet 		return 0;
958b4d84bceSEric Dumazet 	case IP_PASSSEC:
959b4d84bceSEric Dumazet 		inet_assign_bit(PASSSEC, sk, val);
960b4d84bceSEric Dumazet 		return 0;
961b4d84bceSEric Dumazet 	case IP_RECVORIGDSTADDR:
962b4d84bceSEric Dumazet 		inet_assign_bit(ORIGDSTADDR, sk, val);
963b4d84bceSEric Dumazet 		return 0;
964b4d84bceSEric Dumazet 	case IP_RECVFRAGSIZE:
965b4d84bceSEric Dumazet 		if (sk->sk_type != SOCK_RAW && sk->sk_type != SOCK_DGRAM)
966b4d84bceSEric Dumazet 			return -EINVAL;
967b4d84bceSEric Dumazet 		inet_assign_bit(RECVFRAGSIZE, sk, val);
968b4d84bceSEric Dumazet 		return 0;
9696b5f43eaSEric Dumazet 	case IP_RECVERR:
9706b5f43eaSEric Dumazet 		inet_assign_bit(RECVERR, sk, val);
9716b5f43eaSEric Dumazet 		if (!val)
9720f158b32SEric Dumazet 			skb_errqueue_purge(&sk->sk_error_queue);
9736b5f43eaSEric Dumazet 		return 0;
9748e8cfb11SEric Dumazet 	case IP_RECVERR_RFC4884:
9758e8cfb11SEric Dumazet 		if (val < 0 || val > 1)
9768e8cfb11SEric Dumazet 			return -EINVAL;
9778e8cfb11SEric Dumazet 		inet_assign_bit(RECVERR_RFC4884, sk, val);
9788e8cfb11SEric Dumazet 		return 0;
9793f7e7532SEric Dumazet 	case IP_FREEBIND:
9803f7e7532SEric Dumazet 		if (optlen < 1)
9813f7e7532SEric Dumazet 			return -EINVAL;
9823f7e7532SEric Dumazet 		inet_assign_bit(FREEBIND, sk, val);
9833f7e7532SEric Dumazet 		return 0;
984cafbe182SEric Dumazet 	case IP_HDRINCL:
985cafbe182SEric Dumazet 		if (sk->sk_type != SOCK_RAW)
986cafbe182SEric Dumazet 			return -ENOPROTOOPT;
987cafbe182SEric Dumazet 		inet_assign_bit(HDRINCL, sk, val);
988cafbe182SEric Dumazet 		return 0;
989b09bde5cSEric Dumazet 	case IP_MULTICAST_LOOP:
990b09bde5cSEric Dumazet 		if (optlen < 1)
991b09bde5cSEric Dumazet 			return -EINVAL;
992b09bde5cSEric Dumazet 		inet_assign_bit(MC_LOOP, sk, val);
993b09bde5cSEric Dumazet 		return 0;
994307b4ac6SEric Dumazet 	case IP_MULTICAST_ALL:
995307b4ac6SEric Dumazet 		if (optlen < 1)
996307b4ac6SEric Dumazet 			return -EINVAL;
997307b4ac6SEric Dumazet 		if (val != 0 && val != 1)
998307b4ac6SEric Dumazet 			return -EINVAL;
999307b4ac6SEric Dumazet 		inet_assign_bit(MC_ALL, sk, val);
1000307b4ac6SEric Dumazet 		return 0;
10014bd0623fSEric Dumazet 	case IP_TRANSPARENT:
10024bd0623fSEric Dumazet 		if (!!val && !sockopt_ns_capable(sock_net(sk)->user_ns, CAP_NET_RAW) &&
10038be6f88bSEric Dumazet 		    !sockopt_ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN))
10048be6f88bSEric Dumazet 			return -EPERM;
10054bd0623fSEric Dumazet 		if (optlen < 1)
10068be6f88bSEric Dumazet 			return -EINVAL;
10074bd0623fSEric Dumazet 		inet_assign_bit(TRANSPARENT, sk, val);
10084bd0623fSEric Dumazet 		return 0;
1009f04b8d34SEric Dumazet 	case IP_NODEFRAG:
1010f04b8d34SEric Dumazet 		if (sk->sk_type != SOCK_RAW)
1011f04b8d34SEric Dumazet 			return -ENOPROTOOPT;
1012f04b8d34SEric Dumazet 		inet_assign_bit(NODEFRAG, sk, val);
1013f04b8d34SEric Dumazet 		return 0;
1014ca571e2eSEric Dumazet 	case IP_BIND_ADDRESS_NO_PORT:
1015ca571e2eSEric Dumazet 		inet_assign_bit(BIND_ADDRESS_NO_PORT, sk, val);
1016ca571e2eSEric Dumazet 		return 0;
101710f42426SEric Dumazet 	case IP_TTL:
101810f42426SEric Dumazet 		if (optlen < 1)
101910f42426SEric Dumazet 			return -EINVAL;
102010f42426SEric Dumazet 		if (val != -1 && (val < 1 || val > 255))
102110f42426SEric Dumazet 			return -EINVAL;
102210f42426SEric Dumazet 		WRITE_ONCE(inet->uc_ttl, val);
102310f42426SEric Dumazet 		return 0;
102412af7326SEric Dumazet 	case IP_MINTTL:
102512af7326SEric Dumazet 		if (optlen < 1)
102612af7326SEric Dumazet 			return -EINVAL;
102712af7326SEric Dumazet 		if (val < 0 || val > 255)
102812af7326SEric Dumazet 			return -EINVAL;
102912af7326SEric Dumazet 
103012af7326SEric Dumazet 		if (val)
103112af7326SEric Dumazet 			static_branch_enable(&ip4_min_ttl);
103212af7326SEric Dumazet 
103312af7326SEric Dumazet 		WRITE_ONCE(inet->min_ttl, val);
103412af7326SEric Dumazet 		return 0;
1035c9746e6aSEric Dumazet 	case IP_MULTICAST_TTL:
1036c9746e6aSEric Dumazet 		if (sk->sk_type == SOCK_STREAM)
1037c9746e6aSEric Dumazet 			return -EINVAL;
1038c9746e6aSEric Dumazet 		if (optlen < 1)
1039c9746e6aSEric Dumazet 			return -EINVAL;
1040c9746e6aSEric Dumazet 		if (val == -1)
1041c9746e6aSEric Dumazet 			val = 1;
1042c9746e6aSEric Dumazet 		if (val < 0 || val > 255)
1043c9746e6aSEric Dumazet 			return -EINVAL;
1044c9746e6aSEric Dumazet 		WRITE_ONCE(inet->mc_ttl, val);
1045c9746e6aSEric Dumazet 		return 0;
1046ceaa7141SEric Dumazet 	case IP_MTU_DISCOVER:
1047ceaa7141SEric Dumazet 		return ip_sock_set_mtu_discover(sk, val);
1048e08d0b3dSEric Dumazet 	case IP_TOS:	/* This sets both TOS and Precedence */
1049e08d0b3dSEric Dumazet 		ip_sock_set_tos(sk, val);
1050e08d0b3dSEric Dumazet 		return 0;
1051b4d84bceSEric Dumazet 	}
1052b4d84bceSEric Dumazet 
10531da177e4SLinus Torvalds 	err = 0;
1054baf606d9SMarcelo Ricardo Leitner 	if (needs_rtnl)
1055baf606d9SMarcelo Ricardo Leitner 		rtnl_lock();
10561df055d3SMartin KaFai Lau 	sockopt_lock_sock(sk);
10571da177e4SLinus Torvalds 
10581da177e4SLinus Torvalds 	switch (optname) {
10591da177e4SLinus Torvalds 	case IP_OPTIONS:
10601da177e4SLinus Torvalds 	{
1061f6d8bd05SEric Dumazet 		struct ip_options_rcu *old, *opt = NULL;
1062f6d8bd05SEric Dumazet 
106365a1c4ffSroel kluin 		if (optlen > 40)
10641da177e4SLinus Torvalds 			goto e_inval;
106589654c5fSChristoph Hellwig 		err = ip_options_get(sock_net(sk), &opt, optval, optlen);
10661da177e4SLinus Torvalds 		if (err)
10671da177e4SLinus Torvalds 			break;
1068f6d8bd05SEric Dumazet 		old = rcu_dereference_protected(inet->inet_opt,
10691e1d04e6SHannes Frederic Sowa 						lockdep_sock_is_held(sk));
1070b1c0356aSEric Dumazet 		if (inet_test_bit(IS_ICSK, sk)) {
1071d83d8461SArnaldo Carvalho de Melo 			struct inet_connection_sock *icsk = inet_csk(sk);
1072dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6)
10731da177e4SLinus Torvalds 			if (sk->sk_family == PF_INET ||
10741da177e4SLinus Torvalds 			    (!((1 << sk->sk_state) &
10751da177e4SLinus Torvalds 			       (TCPF_LISTEN | TCPF_CLOSE)) &&
1076c720c7e8SEric Dumazet 			     inet->inet_daddr != LOOPBACK4_IPV6)) {
10771da177e4SLinus Torvalds #endif
1078f6d8bd05SEric Dumazet 				if (old)
1079f6d8bd05SEric Dumazet 					icsk->icsk_ext_hdr_len -= old->opt.optlen;
10801da177e4SLinus Torvalds 				if (opt)
1081f6d8bd05SEric Dumazet 					icsk->icsk_ext_hdr_len += opt->opt.optlen;
1082d83d8461SArnaldo Carvalho de Melo 				icsk->icsk_sync_mss(sk, icsk->icsk_pmtu_cookie);
1083dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6)
10841da177e4SLinus Torvalds 			}
10851da177e4SLinus Torvalds #endif
10861da177e4SLinus Torvalds 		}
1087f6d8bd05SEric Dumazet 		rcu_assign_pointer(inet->inet_opt, opt);
1088f6d8bd05SEric Dumazet 		if (old)
1089605b4afeSPaul E. McKenney 			kfree_rcu(old, rcu);
10901da177e4SLinus Torvalds 		break;
10911da177e4SLinus Torvalds 	}
1092ad6f939aSTom Herbert 	case IP_CHECKSUM:
1093ad6f939aSTom Herbert 		if (val) {
1094c274af22SEric Dumazet 			if (!(inet_test_bit(CHECKSUM, sk))) {
1095ad6f939aSTom Herbert 				inet_inc_convert_csum(sk);
1096c274af22SEric Dumazet 				inet_set_bit(CHECKSUM, sk);
1097ad6f939aSTom Herbert 			}
1098ad6f939aSTom Herbert 		} else {
1099c274af22SEric Dumazet 			if (inet_test_bit(CHECKSUM, sk)) {
1100ad6f939aSTom Herbert 				inet_dec_convert_csum(sk);
1101c274af22SEric Dumazet 				inet_clear_bit(CHECKSUM, sk);
1102ad6f939aSTom Herbert 			}
1103ad6f939aSTom Herbert 		}
1104ad6f939aSTom Herbert 		break;
110576e21053SErich E. Hoover 	case IP_UNICAST_IF:
110676e21053SErich E. Hoover 	{
110776e21053SErich E. Hoover 		struct net_device *dev = NULL;
110876e21053SErich E. Hoover 		int ifindex;
11099515a2e0SDavid Ahern 		int midx;
111076e21053SErich E. Hoover 
111176e21053SErich E. Hoover 		if (optlen != sizeof(int))
111276e21053SErich E. Hoover 			goto e_inval;
111376e21053SErich E. Hoover 
111476e21053SErich E. Hoover 		ifindex = (__force int)ntohl((__force __be32)val);
111576e21053SErich E. Hoover 		if (ifindex == 0) {
1116*959d5c11SEric Dumazet 			WRITE_ONCE(inet->uc_index, 0);
111776e21053SErich E. Hoover 			err = 0;
111876e21053SErich E. Hoover 			break;
111976e21053SErich E. Hoover 		}
112076e21053SErich E. Hoover 
112176e21053SErich E. Hoover 		dev = dev_get_by_index(sock_net(sk), ifindex);
112276e21053SErich E. Hoover 		err = -EADDRNOTAVAIL;
112376e21053SErich E. Hoover 		if (!dev)
112476e21053SErich E. Hoover 			break;
11259515a2e0SDavid Ahern 
11269515a2e0SDavid Ahern 		midx = l3mdev_master_ifindex(dev);
112776e21053SErich E. Hoover 		dev_put(dev);
112876e21053SErich E. Hoover 
112976e21053SErich E. Hoover 		err = -EINVAL;
1130fdf1923bSMiaohe Lin 		if (sk->sk_bound_dev_if && midx != sk->sk_bound_dev_if)
113176e21053SErich E. Hoover 			break;
113276e21053SErich E. Hoover 
1133*959d5c11SEric Dumazet 		WRITE_ONCE(inet->uc_index, ifindex);
113476e21053SErich E. Hoover 		err = 0;
113576e21053SErich E. Hoover 		break;
113676e21053SErich E. Hoover 	}
11371da177e4SLinus Torvalds 	case IP_MULTICAST_IF:
11381da177e4SLinus Torvalds 	{
11391da177e4SLinus Torvalds 		struct ip_mreqn mreq;
11401da177e4SLinus Torvalds 		struct net_device *dev = NULL;
11417bb387c5SDavid Ahern 		int midx;
11421da177e4SLinus Torvalds 
11431da177e4SLinus Torvalds 		if (sk->sk_type == SOCK_STREAM)
11441da177e4SLinus Torvalds 			goto e_inval;
11451da177e4SLinus Torvalds 		/*
11461da177e4SLinus Torvalds 		 *	Check the arguments are allowable
11471da177e4SLinus Torvalds 		 */
11481da177e4SLinus Torvalds 
11490915921bSShan Wei 		if (optlen < sizeof(struct in_addr))
11500915921bSShan Wei 			goto e_inval;
11510915921bSShan Wei 
11521da177e4SLinus Torvalds 		err = -EFAULT;
11531da177e4SLinus Torvalds 		if (optlen >= sizeof(struct ip_mreqn)) {
115489654c5fSChristoph Hellwig 			if (copy_from_sockptr(&mreq, optval, sizeof(mreq)))
11551da177e4SLinus Torvalds 				break;
11561da177e4SLinus Torvalds 		} else {
11571da177e4SLinus Torvalds 			memset(&mreq, 0, sizeof(mreq));
11583a084ddbSJiri Pirko 			if (optlen >= sizeof(struct ip_mreq)) {
115989654c5fSChristoph Hellwig 				if (copy_from_sockptr(&mreq, optval,
11603a084ddbSJiri Pirko 						      sizeof(struct ip_mreq)))
11613a084ddbSJiri Pirko 					break;
11623a084ddbSJiri Pirko 			} else if (optlen >= sizeof(struct in_addr)) {
116389654c5fSChristoph Hellwig 				if (copy_from_sockptr(&mreq.imr_address, optval,
11644d52cfbeSEric Dumazet 						      sizeof(struct in_addr)))
11651da177e4SLinus Torvalds 					break;
11661da177e4SLinus Torvalds 			}
11673a084ddbSJiri Pirko 		}
11681da177e4SLinus Torvalds 
11691da177e4SLinus Torvalds 		if (!mreq.imr_ifindex) {
1170e6f1cebfSAl Viro 			if (mreq.imr_address.s_addr == htonl(INADDR_ANY)) {
11711da177e4SLinus Torvalds 				inet->mc_index = 0;
11721da177e4SLinus Torvalds 				inet->mc_addr  = 0;
11731da177e4SLinus Torvalds 				err = 0;
11741da177e4SLinus Torvalds 				break;
11751da177e4SLinus Torvalds 			}
11763b1e0a65SYOSHIFUJI Hideaki 			dev = ip_dev_find(sock_net(sk), mreq.imr_address.s_addr);
117755b80503SEric Dumazet 			if (dev)
11781da177e4SLinus Torvalds 				mreq.imr_ifindex = dev->ifindex;
11791da177e4SLinus Torvalds 		} else
118055b80503SEric Dumazet 			dev = dev_get_by_index(sock_net(sk), mreq.imr_ifindex);
11811da177e4SLinus Torvalds 
11821da177e4SLinus Torvalds 
11831da177e4SLinus Torvalds 		err = -EADDRNOTAVAIL;
11841da177e4SLinus Torvalds 		if (!dev)
11851da177e4SLinus Torvalds 			break;
11867bb387c5SDavid Ahern 
11877bb387c5SDavid Ahern 		midx = l3mdev_master_ifindex(dev);
11887bb387c5SDavid Ahern 
118955b80503SEric Dumazet 		dev_put(dev);
11901da177e4SLinus Torvalds 
11911da177e4SLinus Torvalds 		err = -EINVAL;
11921da177e4SLinus Torvalds 		if (sk->sk_bound_dev_if &&
11937bb387c5SDavid Ahern 		    mreq.imr_ifindex != sk->sk_bound_dev_if &&
1194fdf1923bSMiaohe Lin 		    midx != sk->sk_bound_dev_if)
11951da177e4SLinus Torvalds 			break;
11961da177e4SLinus Torvalds 
11971da177e4SLinus Torvalds 		inet->mc_index = mreq.imr_ifindex;
11981da177e4SLinus Torvalds 		inet->mc_addr  = mreq.imr_address.s_addr;
11991da177e4SLinus Torvalds 		err = 0;
12001da177e4SLinus Torvalds 		break;
12011da177e4SLinus Torvalds 	}
12021da177e4SLinus Torvalds 
12031da177e4SLinus Torvalds 	case IP_ADD_MEMBERSHIP:
12041da177e4SLinus Torvalds 	case IP_DROP_MEMBERSHIP:
12051da177e4SLinus Torvalds 	{
12061da177e4SLinus Torvalds 		struct ip_mreqn mreq;
12071da177e4SLinus Torvalds 
1208a96fb49bSFlavio Leitner 		err = -EPROTO;
1209b1c0356aSEric Dumazet 		if (inet_test_bit(IS_ICSK, sk))
1210a96fb49bSFlavio Leitner 			break;
1211a96fb49bSFlavio Leitner 
12121da177e4SLinus Torvalds 		if (optlen < sizeof(struct ip_mreq))
12131da177e4SLinus Torvalds 			goto e_inval;
12141da177e4SLinus Torvalds 		err = -EFAULT;
12151da177e4SLinus Torvalds 		if (optlen >= sizeof(struct ip_mreqn)) {
121689654c5fSChristoph Hellwig 			if (copy_from_sockptr(&mreq, optval, sizeof(mreq)))
12171da177e4SLinus Torvalds 				break;
12181da177e4SLinus Torvalds 		} else {
12191da177e4SLinus Torvalds 			memset(&mreq, 0, sizeof(mreq));
122089654c5fSChristoph Hellwig 			if (copy_from_sockptr(&mreq, optval,
122189654c5fSChristoph Hellwig 					      sizeof(struct ip_mreq)))
12221da177e4SLinus Torvalds 				break;
12231da177e4SLinus Torvalds 		}
12241da177e4SLinus Torvalds 
12251da177e4SLinus Torvalds 		if (optname == IP_ADD_MEMBERSHIP)
122654ff9ef3SMarcelo Ricardo Leitner 			err = ip_mc_join_group(sk, &mreq);
12271da177e4SLinus Torvalds 		else
122854ff9ef3SMarcelo Ricardo Leitner 			err = ip_mc_leave_group(sk, &mreq);
12291da177e4SLinus Torvalds 		break;
12301da177e4SLinus Torvalds 	}
12311da177e4SLinus Torvalds 	case IP_MSFILTER:
12321da177e4SLinus Torvalds 	{
12331da177e4SLinus Torvalds 		struct ip_msfilter *msf;
12341da177e4SLinus Torvalds 
12354167a960SGustavo A. R. Silva 		if (optlen < IP_MSFILTER_SIZE(0))
12361da177e4SLinus Torvalds 			goto e_inval;
12377de6d09fSKuniyuki Iwashima 		if (optlen > READ_ONCE(sysctl_optmem_max)) {
12381da177e4SLinus Torvalds 			err = -ENOBUFS;
12391da177e4SLinus Torvalds 			break;
12401da177e4SLinus Torvalds 		}
124189654c5fSChristoph Hellwig 		msf = memdup_sockptr(optval, optlen);
1242a2c841d9SAl Viro 		if (IS_ERR(msf)) {
1243a2c841d9SAl Viro 			err = PTR_ERR(msf);
12441da177e4SLinus Torvalds 			break;
12451da177e4SLinus Torvalds 		}
12461da177e4SLinus Torvalds 		/* numsrc >= (1G-4) overflow in 32 bits */
12471da177e4SLinus Torvalds 		if (msf->imsf_numsrc >= 0x3ffffffcU ||
12486ae0f2e5SKuniyuki Iwashima 		    msf->imsf_numsrc > READ_ONCE(net->ipv4.sysctl_igmp_max_msf)) {
12491da177e4SLinus Torvalds 			kfree(msf);
12501da177e4SLinus Torvalds 			err = -ENOBUFS;
12511da177e4SLinus Torvalds 			break;
12521da177e4SLinus Torvalds 		}
12534167a960SGustavo A. R. Silva 		if (IP_MSFILTER_SIZE(msf->imsf_numsrc) > optlen) {
12541da177e4SLinus Torvalds 			kfree(msf);
12551da177e4SLinus Torvalds 			err = -EINVAL;
12561da177e4SLinus Torvalds 			break;
12571da177e4SLinus Torvalds 		}
12581da177e4SLinus Torvalds 		err = ip_mc_msfilter(sk, msf, 0);
12591da177e4SLinus Torvalds 		kfree(msf);
12601da177e4SLinus Torvalds 		break;
12611da177e4SLinus Torvalds 	}
12621da177e4SLinus Torvalds 	case IP_BLOCK_SOURCE:
12631da177e4SLinus Torvalds 	case IP_UNBLOCK_SOURCE:
12641da177e4SLinus Torvalds 	case IP_ADD_SOURCE_MEMBERSHIP:
12651da177e4SLinus Torvalds 	case IP_DROP_SOURCE_MEMBERSHIP:
12661da177e4SLinus Torvalds 	{
12671da177e4SLinus Torvalds 		struct ip_mreq_source mreqs;
12681da177e4SLinus Torvalds 		int omode, add;
12691da177e4SLinus Torvalds 
12701da177e4SLinus Torvalds 		if (optlen != sizeof(struct ip_mreq_source))
12711da177e4SLinus Torvalds 			goto e_inval;
127289654c5fSChristoph Hellwig 		if (copy_from_sockptr(&mreqs, optval, sizeof(mreqs))) {
12731da177e4SLinus Torvalds 			err = -EFAULT;
12741da177e4SLinus Torvalds 			break;
12751da177e4SLinus Torvalds 		}
12761da177e4SLinus Torvalds 		if (optname == IP_BLOCK_SOURCE) {
12771da177e4SLinus Torvalds 			omode = MCAST_EXCLUDE;
12781da177e4SLinus Torvalds 			add = 1;
12791da177e4SLinus Torvalds 		} else if (optname == IP_UNBLOCK_SOURCE) {
12801da177e4SLinus Torvalds 			omode = MCAST_EXCLUDE;
12811da177e4SLinus Torvalds 			add = 0;
12821da177e4SLinus Torvalds 		} else if (optname == IP_ADD_SOURCE_MEMBERSHIP) {
12831da177e4SLinus Torvalds 			struct ip_mreqn mreq;
12841da177e4SLinus Torvalds 
12851da177e4SLinus Torvalds 			mreq.imr_multiaddr.s_addr = mreqs.imr_multiaddr;
12861da177e4SLinus Torvalds 			mreq.imr_address.s_addr = mreqs.imr_interface;
12871da177e4SLinus Torvalds 			mreq.imr_ifindex = 0;
12886e2059b5SHangbin Liu 			err = ip_mc_join_group_ssm(sk, &mreq, MCAST_INCLUDE);
12898cdaaa15SDavid L Stevens 			if (err && err != -EADDRINUSE)
12901da177e4SLinus Torvalds 				break;
12911da177e4SLinus Torvalds 			omode = MCAST_INCLUDE;
12921da177e4SLinus Torvalds 			add = 1;
12931da177e4SLinus Torvalds 		} else /* IP_DROP_SOURCE_MEMBERSHIP */ {
12941da177e4SLinus Torvalds 			omode = MCAST_INCLUDE;
12951da177e4SLinus Torvalds 			add = 0;
12961da177e4SLinus Torvalds 		}
12971da177e4SLinus Torvalds 		err = ip_mc_source(add, omode, sk, &mreqs, 0);
12981da177e4SLinus Torvalds 		break;
12991da177e4SLinus Torvalds 	}
13001da177e4SLinus Torvalds 	case MCAST_JOIN_GROUP:
13011da177e4SLinus Torvalds 	case MCAST_LEAVE_GROUP:
1302b6238c04SChristoph Hellwig 		if (in_compat_syscall())
1303b6238c04SChristoph Hellwig 			err = compat_ip_mcast_join_leave(sk, optname, optval,
1304b6238c04SChristoph Hellwig 							 optlen);
1305b6238c04SChristoph Hellwig 		else
130602caad7cSChristoph Hellwig 			err = ip_mcast_join_leave(sk, optname, optval, optlen);
13071da177e4SLinus Torvalds 		break;
13081da177e4SLinus Torvalds 	case MCAST_JOIN_SOURCE_GROUP:
13091da177e4SLinus Torvalds 	case MCAST_LEAVE_SOURCE_GROUP:
13101da177e4SLinus Torvalds 	case MCAST_BLOCK_SOURCE:
13111da177e4SLinus Torvalds 	case MCAST_UNBLOCK_SOURCE:
1312b6238c04SChristoph Hellwig 		err = do_mcast_group_source(sk, optname, optval, optlen);
13131da177e4SLinus Torvalds 		break;
13141da177e4SLinus Torvalds 	case MCAST_MSFILTER:
1315b6238c04SChristoph Hellwig 		if (in_compat_syscall())
1316b6238c04SChristoph Hellwig 			err = compat_ip_set_mcast_msfilter(sk, optval, optlen);
1317b6238c04SChristoph Hellwig 		else
1318d62c38f6SChristoph Hellwig 			err = ip_set_mcast_msfilter(sk, optval, optlen);
13191da177e4SLinus Torvalds 		break;
13201da177e4SLinus Torvalds 	case IP_IPSEC_POLICY:
13211da177e4SLinus Torvalds 	case IP_XFRM_POLICY:
13226fc0b4a7SHerbert Xu 		err = -EPERM;
13231df055d3SMartin KaFai Lau 		if (!sockopt_ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN))
13246fc0b4a7SHerbert Xu 			break;
132589654c5fSChristoph Hellwig 		err = xfrm_user_policy(sk, optname, optval, optlen);
13261da177e4SLinus Torvalds 		break;
13271da177e4SLinus Torvalds 
132891d0b78cSJakub Sitnicki 	case IP_LOCAL_PORT_RANGE:
132991d0b78cSJakub Sitnicki 	{
133091d0b78cSJakub Sitnicki 		const __u16 lo = val;
133191d0b78cSJakub Sitnicki 		const __u16 hi = val >> 16;
133291d0b78cSJakub Sitnicki 
133391d0b78cSJakub Sitnicki 		if (optlen != sizeof(__u32))
133491d0b78cSJakub Sitnicki 			goto e_inval;
133591d0b78cSJakub Sitnicki 		if (lo != 0 && hi != 0 && lo > hi)
133691d0b78cSJakub Sitnicki 			goto e_inval;
133791d0b78cSJakub Sitnicki 
133891d0b78cSJakub Sitnicki 		inet->local_port_range.lo = lo;
133991d0b78cSJakub Sitnicki 		inet->local_port_range.hi = hi;
134091d0b78cSJakub Sitnicki 		break;
134191d0b78cSJakub Sitnicki 	}
13421da177e4SLinus Torvalds 	default:
13431da177e4SLinus Torvalds 		err = -ENOPROTOOPT;
13441da177e4SLinus Torvalds 		break;
13451da177e4SLinus Torvalds 	}
13461df055d3SMartin KaFai Lau 	sockopt_release_sock(sk);
1347baf606d9SMarcelo Ricardo Leitner 	if (needs_rtnl)
1348baf606d9SMarcelo Ricardo Leitner 		rtnl_unlock();
13491da177e4SLinus Torvalds 	return err;
13501da177e4SLinus Torvalds 
13511da177e4SLinus Torvalds e_inval:
13521df055d3SMartin KaFai Lau 	sockopt_release_sock(sk);
1353baf606d9SMarcelo Ricardo Leitner 	if (needs_rtnl)
1354baf606d9SMarcelo Ricardo Leitner 		rtnl_unlock();
13551da177e4SLinus Torvalds 	return -EINVAL;
13561da177e4SLinus Torvalds }
13571da177e4SLinus Torvalds 
1358f84af32cSEric Dumazet /**
1359829ae9d6SWillem de Bruijn  * ipv4_pktinfo_prepare - transfer some info from rtable to skb
1360f84af32cSEric Dumazet  * @sk: socket
1361f84af32cSEric Dumazet  * @skb: buffer
1362f84af32cSEric Dumazet  *
136335ebf65eSDavid S. Miller  * To support IP_CMSG_PKTINFO option, we store rt_iif and specific
136435ebf65eSDavid S. Miller  * destination in skb->cb[] before dst drop.
13658e3bff96Sstephen hemminger  * This way, receiver doesn't make cache line misses to read rtable.
1366f84af32cSEric Dumazet  */
1367fbf8866dSShawn Bohrer void ipv4_pktinfo_prepare(const struct sock *sk, struct sk_buff *skb)
1368f84af32cSEric Dumazet {
1369d826eb14SEric Dumazet 	struct in_pktinfo *pktinfo = PKTINFO_SKB_CB(skb);
1370c274af22SEric Dumazet 	bool prepare = inet_test_bit(PKTINFO, sk) ||
13714b261c75SHannes Frederic Sowa 		       ipv6_sk_rxinfo(sk);
1372d826eb14SEric Dumazet 
13734b261c75SHannes Frederic Sowa 	if (prepare && skb_rtable(skb)) {
13740b922b7aSDavid Ahern 		/* skb->cb is overloaded: prior to this point it is IP{6}CB
13750b922b7aSDavid Ahern 		 * which has interface index (iif) as the first member of the
13760b922b7aSDavid Ahern 		 * underlying inet{6}_skb_parm struct. This code then overlays
13770b922b7aSDavid Ahern 		 * PKTINFO_SKB_CB and in_pktinfo also has iif as the first
1378f0c16ba8SWei Zhang 		 * element so the iif is picked up from the prior IPCB. If iif
1379f0c16ba8SWei Zhang 		 * is the loopback interface, then return the sending interface
1380f0c16ba8SWei Zhang 		 * (e.g., process binds socket to eth0 for Tx which is
1381f0c16ba8SWei Zhang 		 * redirected to loopback in the rtable/dst).
13820b922b7aSDavid Ahern 		 */
1383cbea8f02SDavid Ahern 		struct rtable *rt = skb_rtable(skb);
1384cbea8f02SDavid Ahern 		bool l3slave = ipv4_l3mdev_skb(IPCB(skb)->flags);
1385cbea8f02SDavid Ahern 
1386cbea8f02SDavid Ahern 		if (pktinfo->ipi_ifindex == LOOPBACK_IFINDEX)
1387f0c16ba8SWei Zhang 			pktinfo->ipi_ifindex = inet_iif(skb);
1388cbea8f02SDavid Ahern 		else if (l3slave && rt && rt->rt_iif)
1389cbea8f02SDavid Ahern 			pktinfo->ipi_ifindex = rt->rt_iif;
1390f0c16ba8SWei Zhang 
139135ebf65eSDavid S. Miller 		pktinfo->ipi_spec_dst.s_addr = fib_compute_spec_dst(skb);
1392d826eb14SEric Dumazet 	} else {
1393d826eb14SEric Dumazet 		pktinfo->ipi_ifindex = 0;
1394d826eb14SEric Dumazet 		pktinfo->ipi_spec_dst.s_addr = 0;
1395f84af32cSEric Dumazet 	}
1396d826eb14SEric Dumazet 	skb_dst_drop(skb);
1397d826eb14SEric Dumazet }
1398f84af32cSEric Dumazet 
1399a7b75c5aSChristoph Hellwig int ip_setsockopt(struct sock *sk, int level, int optname, sockptr_t optval,
1400a7b75c5aSChristoph Hellwig 		unsigned int optlen)
14013fdadf7dSDmitry Mishin {
14023fdadf7dSDmitry Mishin 	int err;
14033fdadf7dSDmitry Mishin 
14043fdadf7dSDmitry Mishin 	if (level != SOL_IP)
14053fdadf7dSDmitry Mishin 		return -ENOPROTOOPT;
14063fdadf7dSDmitry Mishin 
1407a7b75c5aSChristoph Hellwig 	err = do_ip_setsockopt(sk, level, optname, optval, optlen);
140897adaddaSTaehee Yoo #if IS_ENABLED(CONFIG_BPFILTER_UMH)
1409d2ba09c1SAlexei Starovoitov 	if (optname >= BPFILTER_IPT_SO_SET_REPLACE &&
1410d2ba09c1SAlexei Starovoitov 	    optname < BPFILTER_IPT_SET_MAX)
1411a7b75c5aSChristoph Hellwig 		err = bpfilter_ip_set_sockopt(sk, optname, optval, optlen);
1412d2ba09c1SAlexei Starovoitov #endif
14133fdadf7dSDmitry Mishin #ifdef CONFIG_NETFILTER
14143fdadf7dSDmitry Mishin 	/* we need to exclude all possible ENOPROTOOPTs except default case */
14153fdadf7dSDmitry Mishin 	if (err == -ENOPROTOOPT && optname != IP_HDRINCL &&
14166a9fb947SPavel Emelyanov 			optname != IP_IPSEC_POLICY &&
14176a9fb947SPavel Emelyanov 			optname != IP_XFRM_POLICY &&
14183f34cfaeSPaolo Abeni 			!ip_mroute_opt(optname))
1419a7b75c5aSChristoph Hellwig 		err = nf_setsockopt(sk, PF_INET, optname, optval, optlen);
14203fdadf7dSDmitry Mishin #endif
14213fdadf7dSDmitry Mishin 	return err;
14223fdadf7dSDmitry Mishin }
14234d52cfbeSEric Dumazet EXPORT_SYMBOL(ip_setsockopt);
14243fdadf7dSDmitry Mishin 
14251da177e4SLinus Torvalds /*
14264d52cfbeSEric Dumazet  *	Get the options. Note for future reference. The GET of IP options gets
14274d52cfbeSEric Dumazet  *	the _received_ ones. The set sets the _sent_ ones.
14281da177e4SLinus Torvalds  */
14291da177e4SLinus Torvalds 
143087e9f031SWANG Cong static bool getsockopt_needs_rtnl(int optname)
143187e9f031SWANG Cong {
143287e9f031SWANG Cong 	switch (optname) {
143387e9f031SWANG Cong 	case IP_MSFILTER:
143487e9f031SWANG Cong 	case MCAST_MSFILTER:
143587e9f031SWANG Cong 		return true;
143687e9f031SWANG Cong 	}
143787e9f031SWANG Cong 	return false;
143887e9f031SWANG Cong }
143987e9f031SWANG Cong 
1440728f064cSMartin KaFai Lau static int ip_get_mcast_msfilter(struct sock *sk, sockptr_t optval,
1441728f064cSMartin KaFai Lau 				 sockptr_t optlen, int len)
144249e74c24SChristoph Hellwig {
1443db243b79SGustavo A. R. Silva 	const int size0 = offsetof(struct group_filter, gf_slist_flex);
144449e74c24SChristoph Hellwig 	struct group_filter gsf;
1445728f064cSMartin KaFai Lau 	int num, gsf_size;
144649e74c24SChristoph Hellwig 	int err;
144749e74c24SChristoph Hellwig 
144849e74c24SChristoph Hellwig 	if (len < size0)
144949e74c24SChristoph Hellwig 		return -EINVAL;
1450728f064cSMartin KaFai Lau 	if (copy_from_sockptr(&gsf, optval, size0))
145149e74c24SChristoph Hellwig 		return -EFAULT;
145249e74c24SChristoph Hellwig 
145349e74c24SChristoph Hellwig 	num = gsf.gf_numsrc;
1454728f064cSMartin KaFai Lau 	err = ip_mc_gsfget(sk, &gsf, optval,
1455728f064cSMartin KaFai Lau 			   offsetof(struct group_filter, gf_slist_flex));
145649e74c24SChristoph Hellwig 	if (err)
145749e74c24SChristoph Hellwig 		return err;
145849e74c24SChristoph Hellwig 	if (gsf.gf_numsrc < num)
145949e74c24SChristoph Hellwig 		num = gsf.gf_numsrc;
1460728f064cSMartin KaFai Lau 	gsf_size = GROUP_FILTER_SIZE(num);
1461728f064cSMartin KaFai Lau 	if (copy_to_sockptr(optlen, &gsf_size, sizeof(int)) ||
1462728f064cSMartin KaFai Lau 	    copy_to_sockptr(optval, &gsf, size0))
146349e74c24SChristoph Hellwig 		return -EFAULT;
146449e74c24SChristoph Hellwig 	return 0;
146549e74c24SChristoph Hellwig }
146649e74c24SChristoph Hellwig 
1467728f064cSMartin KaFai Lau static int compat_ip_get_mcast_msfilter(struct sock *sk, sockptr_t optval,
1468728f064cSMartin KaFai Lau 					sockptr_t optlen, int len)
146949e74c24SChristoph Hellwig {
1470db243b79SGustavo A. R. Silva 	const int size0 = offsetof(struct compat_group_filter, gf_slist_flex);
147149e74c24SChristoph Hellwig 	struct compat_group_filter gf32;
147249e74c24SChristoph Hellwig 	struct group_filter gf;
147349e74c24SChristoph Hellwig 	int num;
1474b6238c04SChristoph Hellwig 	int err;
147549e74c24SChristoph Hellwig 
147649e74c24SChristoph Hellwig 	if (len < size0)
147749e74c24SChristoph Hellwig 		return -EINVAL;
1478728f064cSMartin KaFai Lau 	if (copy_from_sockptr(&gf32, optval, size0))
147949e74c24SChristoph Hellwig 		return -EFAULT;
148049e74c24SChristoph Hellwig 
148149e74c24SChristoph Hellwig 	gf.gf_interface = gf32.gf_interface;
148249e74c24SChristoph Hellwig 	gf.gf_fmode = gf32.gf_fmode;
148349e74c24SChristoph Hellwig 	num = gf.gf_numsrc = gf32.gf_numsrc;
148449e74c24SChristoph Hellwig 	gf.gf_group = gf32.gf_group;
148549e74c24SChristoph Hellwig 
1486728f064cSMartin KaFai Lau 	err = ip_mc_gsfget(sk, &gf, optval,
1487728f064cSMartin KaFai Lau 			   offsetof(struct compat_group_filter, gf_slist_flex));
148849e74c24SChristoph Hellwig 	if (err)
148949e74c24SChristoph Hellwig 		return err;
149049e74c24SChristoph Hellwig 	if (gf.gf_numsrc < num)
149149e74c24SChristoph Hellwig 		num = gf.gf_numsrc;
149249e74c24SChristoph Hellwig 	len = GROUP_FILTER_SIZE(num) - (sizeof(gf) - sizeof(gf32));
1493728f064cSMartin KaFai Lau 	if (copy_to_sockptr(optlen, &len, sizeof(int)) ||
1494728f064cSMartin KaFai Lau 	    copy_to_sockptr_offset(optval, offsetof(struct compat_group_filter, gf_fmode),
1495728f064cSMartin KaFai Lau 				   &gf.gf_fmode, sizeof(gf.gf_fmode)) ||
1496728f064cSMartin KaFai Lau 	    copy_to_sockptr_offset(optval, offsetof(struct compat_group_filter, gf_numsrc),
1497728f064cSMartin KaFai Lau 				   &gf.gf_numsrc, sizeof(gf.gf_numsrc)))
149849e74c24SChristoph Hellwig 		return -EFAULT;
149949e74c24SChristoph Hellwig 	return 0;
150049e74c24SChristoph Hellwig }
150149e74c24SChristoph Hellwig 
1502fd969f25SMartin KaFai Lau int do_ip_getsockopt(struct sock *sk, int level, int optname,
1503728f064cSMartin KaFai Lau 		     sockptr_t optval, sockptr_t optlen)
15041da177e4SLinus Torvalds {
15051da177e4SLinus Torvalds 	struct inet_sock *inet = inet_sk(sk);
150687e9f031SWANG Cong 	bool needs_rtnl = getsockopt_needs_rtnl(optname);
150787e9f031SWANG Cong 	int val, err = 0;
15081da177e4SLinus Torvalds 	int len;
15091da177e4SLinus Torvalds 
15101da177e4SLinus Torvalds 	if (level != SOL_IP)
15111da177e4SLinus Torvalds 		return -EOPNOTSUPP;
15121da177e4SLinus Torvalds 
15136a9fb947SPavel Emelyanov 	if (ip_mroute_opt(optname))
15141da177e4SLinus Torvalds 		return ip_mroute_getsockopt(sk, optname, optval, optlen);
15151da177e4SLinus Torvalds 
1516728f064cSMartin KaFai Lau 	if (copy_from_sockptr(&len, optlen, sizeof(int)))
15171da177e4SLinus Torvalds 		return -EFAULT;
15181da177e4SLinus Torvalds 	if (len < 0)
15191da177e4SLinus Torvalds 		return -EINVAL;
15201da177e4SLinus Torvalds 
1521b4d84bceSEric Dumazet 	/* Handle options that can be read without locking the socket. */
1522b4d84bceSEric Dumazet 	switch (optname) {
1523b4d84bceSEric Dumazet 	case IP_PKTINFO:
1524b4d84bceSEric Dumazet 		val = inet_test_bit(PKTINFO, sk);
1525b4d84bceSEric Dumazet 		goto copyval;
1526b4d84bceSEric Dumazet 	case IP_RECVTTL:
1527b4d84bceSEric Dumazet 		val = inet_test_bit(TTL, sk);
1528b4d84bceSEric Dumazet 		goto copyval;
1529b4d84bceSEric Dumazet 	case IP_RECVTOS:
1530b4d84bceSEric Dumazet 		val = inet_test_bit(TOS, sk);
1531b4d84bceSEric Dumazet 		goto copyval;
1532b4d84bceSEric Dumazet 	case IP_RECVOPTS:
1533b4d84bceSEric Dumazet 		val = inet_test_bit(RECVOPTS, sk);
1534b4d84bceSEric Dumazet 		goto copyval;
1535b4d84bceSEric Dumazet 	case IP_RETOPTS:
1536b4d84bceSEric Dumazet 		val = inet_test_bit(RETOPTS, sk);
1537b4d84bceSEric Dumazet 		goto copyval;
1538b4d84bceSEric Dumazet 	case IP_PASSSEC:
1539b4d84bceSEric Dumazet 		val = inet_test_bit(PASSSEC, sk);
1540b4d84bceSEric Dumazet 		goto copyval;
1541b4d84bceSEric Dumazet 	case IP_RECVORIGDSTADDR:
1542b4d84bceSEric Dumazet 		val = inet_test_bit(ORIGDSTADDR, sk);
1543b4d84bceSEric Dumazet 		goto copyval;
1544b4d84bceSEric Dumazet 	case IP_CHECKSUM:
1545b4d84bceSEric Dumazet 		val = inet_test_bit(CHECKSUM, sk);
1546b4d84bceSEric Dumazet 		goto copyval;
1547b4d84bceSEric Dumazet 	case IP_RECVFRAGSIZE:
1548b4d84bceSEric Dumazet 		val = inet_test_bit(RECVFRAGSIZE, sk);
1549b4d84bceSEric Dumazet 		goto copyval;
15506b5f43eaSEric Dumazet 	case IP_RECVERR:
15516b5f43eaSEric Dumazet 		val = inet_test_bit(RECVERR, sk);
15526b5f43eaSEric Dumazet 		goto copyval;
15538e8cfb11SEric Dumazet 	case IP_RECVERR_RFC4884:
15548e8cfb11SEric Dumazet 		val = inet_test_bit(RECVERR_RFC4884, sk);
15558e8cfb11SEric Dumazet 		goto copyval;
15563f7e7532SEric Dumazet 	case IP_FREEBIND:
15573f7e7532SEric Dumazet 		val = inet_test_bit(FREEBIND, sk);
15583f7e7532SEric Dumazet 		goto copyval;
1559cafbe182SEric Dumazet 	case IP_HDRINCL:
1560cafbe182SEric Dumazet 		val = inet_test_bit(HDRINCL, sk);
1561cafbe182SEric Dumazet 		goto copyval;
1562b09bde5cSEric Dumazet 	case IP_MULTICAST_LOOP:
1563b09bde5cSEric Dumazet 		val = inet_test_bit(MC_LOOP, sk);
1564b09bde5cSEric Dumazet 		goto copyval;
1565307b4ac6SEric Dumazet 	case IP_MULTICAST_ALL:
1566307b4ac6SEric Dumazet 		val = inet_test_bit(MC_ALL, sk);
1567307b4ac6SEric Dumazet 		goto copyval;
15684bd0623fSEric Dumazet 	case IP_TRANSPARENT:
15694bd0623fSEric Dumazet 		val = inet_test_bit(TRANSPARENT, sk);
15704bd0623fSEric Dumazet 		goto copyval;
1571f04b8d34SEric Dumazet 	case IP_NODEFRAG:
1572f04b8d34SEric Dumazet 		val = inet_test_bit(NODEFRAG, sk);
1573f04b8d34SEric Dumazet 		goto copyval;
1574ca571e2eSEric Dumazet 	case IP_BIND_ADDRESS_NO_PORT:
1575ca571e2eSEric Dumazet 		val = inet_test_bit(BIND_ADDRESS_NO_PORT, sk);
1576ca571e2eSEric Dumazet 		goto copyval;
157710f42426SEric Dumazet 	case IP_TTL:
157810f42426SEric Dumazet 		val = READ_ONCE(inet->uc_ttl);
157910f42426SEric Dumazet 		if (val < 0)
158010f42426SEric Dumazet 			val = READ_ONCE(sock_net(sk)->ipv4.sysctl_ip_default_ttl);
158110f42426SEric Dumazet 		goto copyval;
158212af7326SEric Dumazet 	case IP_MINTTL:
158312af7326SEric Dumazet 		val = READ_ONCE(inet->min_ttl);
158412af7326SEric Dumazet 		goto copyval;
1585c9746e6aSEric Dumazet 	case IP_MULTICAST_TTL:
1586c9746e6aSEric Dumazet 		val = READ_ONCE(inet->mc_ttl);
1587c9746e6aSEric Dumazet 		goto copyval;
1588ceaa7141SEric Dumazet 	case IP_MTU_DISCOVER:
1589ceaa7141SEric Dumazet 		val = READ_ONCE(inet->pmtudisc);
1590ceaa7141SEric Dumazet 		goto copyval;
1591e08d0b3dSEric Dumazet 	case IP_TOS:
1592e08d0b3dSEric Dumazet 		val = READ_ONCE(inet->tos);
1593e08d0b3dSEric Dumazet 		goto copyval;
15941da177e4SLinus Torvalds 	case IP_OPTIONS:
15951da177e4SLinus Torvalds 	{
15961da177e4SLinus Torvalds 		unsigned char optbuf[sizeof(struct ip_options)+40];
15971da177e4SLinus Torvalds 		struct ip_options *opt = (struct ip_options *)optbuf;
1598f6d8bd05SEric Dumazet 		struct ip_options_rcu *inet_opt;
1599f6d8bd05SEric Dumazet 
1600a4725d0dSEric Dumazet 		rcu_read_lock();
1601a4725d0dSEric Dumazet 		inet_opt = rcu_dereference(inet->inet_opt);
16021da177e4SLinus Torvalds 		opt->optlen = 0;
1603f6d8bd05SEric Dumazet 		if (inet_opt)
1604f6d8bd05SEric Dumazet 			memcpy(optbuf, &inet_opt->opt,
16051da177e4SLinus Torvalds 			       sizeof(struct ip_options) +
1606f6d8bd05SEric Dumazet 			       inet_opt->opt.optlen);
1607a4725d0dSEric Dumazet 		rcu_read_unlock();
16081da177e4SLinus Torvalds 
1609728f064cSMartin KaFai Lau 		if (opt->optlen == 0) {
1610728f064cSMartin KaFai Lau 			len = 0;
1611728f064cSMartin KaFai Lau 			return copy_to_sockptr(optlen, &len, sizeof(int));
1612728f064cSMartin KaFai Lau 		}
16131da177e4SLinus Torvalds 
16141da177e4SLinus Torvalds 		ip_options_undo(opt);
16151da177e4SLinus Torvalds 
16161da177e4SLinus Torvalds 		len = min_t(unsigned int, len, opt->optlen);
1617728f064cSMartin KaFai Lau 		if (copy_to_sockptr(optlen, &len, sizeof(int)))
16181da177e4SLinus Torvalds 			return -EFAULT;
1619728f064cSMartin KaFai Lau 		if (copy_to_sockptr(optval, opt->__data, len))
16201da177e4SLinus Torvalds 			return -EFAULT;
16211da177e4SLinus Torvalds 		return 0;
16221da177e4SLinus Torvalds 	}
16231da177e4SLinus Torvalds 	case IP_MTU:
16241da177e4SLinus Torvalds 	{
16251da177e4SLinus Torvalds 		struct dst_entry *dst;
16261da177e4SLinus Torvalds 		val = 0;
16271da177e4SLinus Torvalds 		dst = sk_dst_get(sk);
16281da177e4SLinus Torvalds 		if (dst) {
16291da177e4SLinus Torvalds 			val = dst_mtu(dst);
16301da177e4SLinus Torvalds 			dst_release(dst);
16311da177e4SLinus Torvalds 		}
16323523bc91SEric Dumazet 		if (!val)
16331da177e4SLinus Torvalds 			return -ENOTCONN;
16343523bc91SEric Dumazet 		goto copyval;
16351da177e4SLinus Torvalds 	}
1636*959d5c11SEric Dumazet 	case IP_UNICAST_IF:
1637*959d5c11SEric Dumazet 		val = (__force int)htonl((__u32) READ_ONCE(inet->uc_index));
1638*959d5c11SEric Dumazet 		goto copyval;
16391da177e4SLinus Torvalds 	}
16403523bc91SEric Dumazet 
16413523bc91SEric Dumazet 	if (needs_rtnl)
16423523bc91SEric Dumazet 		rtnl_lock();
16433523bc91SEric Dumazet 	sockopt_lock_sock(sk);
16443523bc91SEric Dumazet 
16453523bc91SEric Dumazet 	switch (optname) {
16461da177e4SLinus Torvalds 	case IP_MULTICAST_IF:
16471da177e4SLinus Torvalds 	{
16481da177e4SLinus Torvalds 		struct in_addr addr;
16491da177e4SLinus Torvalds 		len = min_t(unsigned int, len, sizeof(struct in_addr));
16501da177e4SLinus Torvalds 		addr.s_addr = inet->mc_addr;
16511985320cSMartin KaFai Lau 		sockopt_release_sock(sk);
16521da177e4SLinus Torvalds 
1653728f064cSMartin KaFai Lau 		if (copy_to_sockptr(optlen, &len, sizeof(int)))
16541da177e4SLinus Torvalds 			return -EFAULT;
1655728f064cSMartin KaFai Lau 		if (copy_to_sockptr(optval, &addr, len))
16561da177e4SLinus Torvalds 			return -EFAULT;
16571da177e4SLinus Torvalds 		return 0;
16581da177e4SLinus Torvalds 	}
16591da177e4SLinus Torvalds 	case IP_MSFILTER:
16601da177e4SLinus Torvalds 	{
16611da177e4SLinus Torvalds 		struct ip_msfilter msf;
16621da177e4SLinus Torvalds 
16634167a960SGustavo A. R. Silva 		if (len < IP_MSFILTER_SIZE(0)) {
166487e9f031SWANG Cong 			err = -EINVAL;
166587e9f031SWANG Cong 			goto out;
16661da177e4SLinus Torvalds 		}
1667728f064cSMartin KaFai Lau 		if (copy_from_sockptr(&msf, optval, IP_MSFILTER_SIZE(0))) {
166887e9f031SWANG Cong 			err = -EFAULT;
166987e9f031SWANG Cong 			goto out;
16701da177e4SLinus Torvalds 		}
1671728f064cSMartin KaFai Lau 		err = ip_mc_msfget(sk, &msf, optval, optlen);
167287e9f031SWANG Cong 		goto out;
16731da177e4SLinus Torvalds 	}
16741da177e4SLinus Torvalds 	case MCAST_MSFILTER:
1675b6238c04SChristoph Hellwig 		if (in_compat_syscall())
1676b6238c04SChristoph Hellwig 			err = compat_ip_get_mcast_msfilter(sk, optval, optlen,
1677b6238c04SChristoph Hellwig 							   len);
1678b6238c04SChristoph Hellwig 		else
167949e74c24SChristoph Hellwig 			err = ip_get_mcast_msfilter(sk, optval, optlen, len);
168087e9f031SWANG Cong 		goto out;
16811da177e4SLinus Torvalds 	case IP_PKTOPTIONS:
16821da177e4SLinus Torvalds 	{
16831da177e4SLinus Torvalds 		struct msghdr msg;
16841da177e4SLinus Torvalds 
16851985320cSMartin KaFai Lau 		sockopt_release_sock(sk);
16861da177e4SLinus Torvalds 
16871da177e4SLinus Torvalds 		if (sk->sk_type != SOCK_STREAM)
16881da177e4SLinus Torvalds 			return -ENOPROTOOPT;
16891da177e4SLinus Torvalds 
1690728f064cSMartin KaFai Lau 		if (optval.is_kernel) {
1691728f064cSMartin KaFai Lau 			msg.msg_control_is_user = false;
1692728f064cSMartin KaFai Lau 			msg.msg_control = optval.kernel;
1693728f064cSMartin KaFai Lau 		} else {
16941f466e1fSChristoph Hellwig 			msg.msg_control_is_user = true;
1695728f064cSMartin KaFai Lau 			msg.msg_control_user = optval.user;
1696728f064cSMartin KaFai Lau 		}
16971da177e4SLinus Torvalds 		msg.msg_controllen = len;
1698b6238c04SChristoph Hellwig 		msg.msg_flags = in_compat_syscall() ? MSG_CMSG_COMPAT : 0;
16991da177e4SLinus Torvalds 
1700c274af22SEric Dumazet 		if (inet_test_bit(PKTINFO, sk)) {
17011da177e4SLinus Torvalds 			struct in_pktinfo info;
17021da177e4SLinus Torvalds 
1703c720c7e8SEric Dumazet 			info.ipi_addr.s_addr = inet->inet_rcv_saddr;
1704c720c7e8SEric Dumazet 			info.ipi_spec_dst.s_addr = inet->inet_rcv_saddr;
17051da177e4SLinus Torvalds 			info.ipi_ifindex = inet->mc_index;
17061da177e4SLinus Torvalds 			put_cmsg(&msg, SOL_IP, IP_PKTINFO, sizeof(info), &info);
17071da177e4SLinus Torvalds 		}
1708c274af22SEric Dumazet 		if (inet_test_bit(TTL, sk)) {
1709c9746e6aSEric Dumazet 			int hlim = READ_ONCE(inet->mc_ttl);
1710c9746e6aSEric Dumazet 
17111da177e4SLinus Torvalds 			put_cmsg(&msg, SOL_IP, IP_TTL, sizeof(hlim), &hlim);
17121da177e4SLinus Torvalds 		}
1713c274af22SEric Dumazet 		if (inet_test_bit(TOS, sk)) {
17144c507d28SJiri Benc 			int tos = inet->rcv_tos;
17154c507d28SJiri Benc 			put_cmsg(&msg, SOL_IP, IP_TOS, sizeof(tos), &tos);
17164c507d28SJiri Benc 		}
17171da177e4SLinus Torvalds 		len -= msg.msg_controllen;
1718728f064cSMartin KaFai Lau 		return copy_to_sockptr(optlen, &len, sizeof(int));
17191da177e4SLinus Torvalds 	}
172091d0b78cSJakub Sitnicki 	case IP_LOCAL_PORT_RANGE:
172191d0b78cSJakub Sitnicki 		val = inet->local_port_range.hi << 16 | inet->local_port_range.lo;
172291d0b78cSJakub Sitnicki 		break;
17233632679dSNicolas Dichtel 	case IP_PROTOCOL:
17243632679dSNicolas Dichtel 		val = inet_sk(sk)->inet_num;
17253632679dSNicolas Dichtel 		break;
17261da177e4SLinus Torvalds 	default:
17271985320cSMartin KaFai Lau 		sockopt_release_sock(sk);
17281da177e4SLinus Torvalds 		return -ENOPROTOOPT;
17291da177e4SLinus Torvalds 	}
17301985320cSMartin KaFai Lau 	sockopt_release_sock(sk);
1731b4d84bceSEric Dumazet copyval:
1732951e07c9SDavid S. Miller 	if (len < sizeof(int) && len > 0 && val >= 0 && val <= 255) {
17331da177e4SLinus Torvalds 		unsigned char ucval = (unsigned char)val;
17341da177e4SLinus Torvalds 		len = 1;
1735728f064cSMartin KaFai Lau 		if (copy_to_sockptr(optlen, &len, sizeof(int)))
17361da177e4SLinus Torvalds 			return -EFAULT;
1737728f064cSMartin KaFai Lau 		if (copy_to_sockptr(optval, &ucval, 1))
17381da177e4SLinus Torvalds 			return -EFAULT;
17391da177e4SLinus Torvalds 	} else {
17401da177e4SLinus Torvalds 		len = min_t(unsigned int, sizeof(int), len);
1741728f064cSMartin KaFai Lau 		if (copy_to_sockptr(optlen, &len, sizeof(int)))
17421da177e4SLinus Torvalds 			return -EFAULT;
1743728f064cSMartin KaFai Lau 		if (copy_to_sockptr(optval, &val, len))
17441da177e4SLinus Torvalds 			return -EFAULT;
17451da177e4SLinus Torvalds 	}
17461da177e4SLinus Torvalds 	return 0;
174787e9f031SWANG Cong 
174887e9f031SWANG Cong out:
17491985320cSMartin KaFai Lau 	sockopt_release_sock(sk);
175087e9f031SWANG Cong 	if (needs_rtnl)
175187e9f031SWANG Cong 		rtnl_unlock();
175287e9f031SWANG Cong 	return err;
17531da177e4SLinus Torvalds }
17541da177e4SLinus Torvalds 
17553fdadf7dSDmitry Mishin int ip_getsockopt(struct sock *sk, int level,
17563fdadf7dSDmitry Mishin 		  int optname, char __user *optval, int __user *optlen)
17573fdadf7dSDmitry Mishin {
17583fdadf7dSDmitry Mishin 	int err;
17593fdadf7dSDmitry Mishin 
1760728f064cSMartin KaFai Lau 	err = do_ip_getsockopt(sk, level, optname,
1761728f064cSMartin KaFai Lau 			       USER_SOCKPTR(optval), USER_SOCKPTR(optlen));
1762b6238c04SChristoph Hellwig 
176397adaddaSTaehee Yoo #if IS_ENABLED(CONFIG_BPFILTER_UMH)
1764d2ba09c1SAlexei Starovoitov 	if (optname >= BPFILTER_IPT_SO_GET_INFO &&
1765d2ba09c1SAlexei Starovoitov 	    optname < BPFILTER_IPT_GET_MAX)
1766d2ba09c1SAlexei Starovoitov 		err = bpfilter_ip_get_sockopt(sk, optname, optval, optlen);
1767d2ba09c1SAlexei Starovoitov #endif
17683fdadf7dSDmitry Mishin #ifdef CONFIG_NETFILTER
17693fdadf7dSDmitry Mishin 	/* we need to exclude all possible ENOPROTOOPTs except default case */
17706a9fb947SPavel Emelyanov 	if (err == -ENOPROTOOPT && optname != IP_PKTOPTIONS &&
17716a9fb947SPavel Emelyanov 			!ip_mroute_opt(optname)) {
17723fdadf7dSDmitry Mishin 		int len;
17733fdadf7dSDmitry Mishin 
17743fdadf7dSDmitry Mishin 		if (get_user(len, optlen))
17753fdadf7dSDmitry Mishin 			return -EFAULT;
17763fdadf7dSDmitry Mishin 
177701ea306fSPaolo Abeni 		err = nf_getsockopt(sk, PF_INET, optname, optval, &len);
17783fdadf7dSDmitry Mishin 		if (err >= 0)
17793fdadf7dSDmitry Mishin 			err = put_user(len, optlen);
17803fdadf7dSDmitry Mishin 		return err;
17813fdadf7dSDmitry Mishin 	}
17823fdadf7dSDmitry Mishin #endif
17833fdadf7dSDmitry Mishin 	return err;
17843fdadf7dSDmitry Mishin }
17854d52cfbeSEric Dumazet EXPORT_SYMBOL(ip_getsockopt);
1786