xref: /linux/net/ipv4/ip_sockglue.c (revision de40a3e88311b6f0fc79b876a4768bf2d99f9aae)
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 {
174ad959036SPaolo Abeni 	struct inet_sock *inet = inet_sk(sk);
17595c96174SEric Dumazet 	unsigned int flags = inet->cmsg_flags;
1761da177e4SLinus Torvalds 
1771da177e4SLinus Torvalds 	/* Ordered by supposed usage frequency */
178c44d13d6STom Herbert 	if (flags & IP_CMSG_PKTINFO) {
1791da177e4SLinus Torvalds 		ip_cmsg_recv_pktinfo(msg, skb);
1801da177e4SLinus Torvalds 
181c44d13d6STom Herbert 		flags &= ~IP_CMSG_PKTINFO;
182c44d13d6STom Herbert 		if (!flags)
183c44d13d6STom Herbert 			return;
184c44d13d6STom Herbert 	}
185c44d13d6STom Herbert 
186c44d13d6STom Herbert 	if (flags & IP_CMSG_TTL) {
1871da177e4SLinus Torvalds 		ip_cmsg_recv_ttl(msg, skb);
1881da177e4SLinus Torvalds 
189c44d13d6STom Herbert 		flags &= ~IP_CMSG_TTL;
190c44d13d6STom Herbert 		if (!flags)
191c44d13d6STom Herbert 			return;
192c44d13d6STom Herbert 	}
193c44d13d6STom Herbert 
194c44d13d6STom Herbert 	if (flags & IP_CMSG_TOS) {
1951da177e4SLinus Torvalds 		ip_cmsg_recv_tos(msg, skb);
1961da177e4SLinus Torvalds 
197c44d13d6STom Herbert 		flags &= ~IP_CMSG_TOS;
198c44d13d6STom Herbert 		if (!flags)
199c44d13d6STom Herbert 			return;
200c44d13d6STom Herbert 	}
201c44d13d6STom Herbert 
202c44d13d6STom Herbert 	if (flags & IP_CMSG_RECVOPTS) {
2031da177e4SLinus Torvalds 		ip_cmsg_recv_opts(msg, skb);
2041da177e4SLinus Torvalds 
205c44d13d6STom Herbert 		flags &= ~IP_CMSG_RECVOPTS;
206c44d13d6STom Herbert 		if (!flags)
207c44d13d6STom Herbert 			return;
208c44d13d6STom Herbert 	}
209c44d13d6STom Herbert 
210c44d13d6STom Herbert 	if (flags & IP_CMSG_RETOPTS) {
21191ed1e66SPaolo Abeni 		ip_cmsg_recv_retopts(sock_net(sk), msg, skb);
2122c7946a7SCatherine Zhang 
213c44d13d6STom Herbert 		flags &= ~IP_CMSG_RETOPTS;
214c44d13d6STom Herbert 		if (!flags)
215c44d13d6STom Herbert 			return;
216c44d13d6STom Herbert 	}
217c44d13d6STom Herbert 
218c44d13d6STom Herbert 	if (flags & IP_CMSG_PASSSEC) {
2192c7946a7SCatherine Zhang 		ip_cmsg_recv_security(msg, skb);
220e8b2dfe9SBalazs Scheidler 
221c44d13d6STom Herbert 		flags &= ~IP_CMSG_PASSSEC;
222c44d13d6STom Herbert 		if (!flags)
223e8b2dfe9SBalazs Scheidler 			return;
224c44d13d6STom Herbert 	}
225c44d13d6STom Herbert 
226ad6f939aSTom Herbert 	if (flags & IP_CMSG_ORIGDSTADDR) {
227e8b2dfe9SBalazs Scheidler 		ip_cmsg_recv_dstaddr(msg, skb);
228e8b2dfe9SBalazs Scheidler 
229ad6f939aSTom Herbert 		flags &= ~IP_CMSG_ORIGDSTADDR;
230ad6f939aSTom Herbert 		if (!flags)
231ad6f939aSTom Herbert 			return;
232ad6f939aSTom Herbert 	}
233ad6f939aSTom Herbert 
234ad6f939aSTom Herbert 	if (flags & IP_CMSG_CHECKSUM)
23510df8e61SEric Dumazet 		ip_cmsg_recv_checksum(msg, skb, tlen, offset);
23670ecc248SWillem de Bruijn 
23770ecc248SWillem de Bruijn 	if (flags & IP_CMSG_RECVFRAGSIZE)
23870ecc248SWillem de Bruijn 		ip_cmsg_recv_fragsize(msg, skb);
2391da177e4SLinus Torvalds }
2405961de9fSTom Herbert EXPORT_SYMBOL(ip_cmsg_recv_offset);
2411da177e4SLinus Torvalds 
24224025c46SSoheil Hassas Yeganeh int ip_cmsg_send(struct sock *sk, struct msghdr *msg, struct ipcm_cookie *ipc,
243c8e6ad08SHannes Frederic Sowa 		 bool allow_ipv6)
2441da177e4SLinus Torvalds {
245f02db315SFrancesco Fusco 	int err, val;
2461da177e4SLinus Torvalds 	struct cmsghdr *cmsg;
24724025c46SSoheil Hassas Yeganeh 	struct net *net = sock_net(sk);
2481da177e4SLinus Torvalds 
249f95b414eSGu Zheng 	for_each_cmsghdr(cmsg, msg) {
2501da177e4SLinus Torvalds 		if (!CMSG_OK(msg, cmsg))
2511da177e4SLinus Torvalds 			return -EINVAL;
2525337b5b7SEric Dumazet #if IS_ENABLED(CONFIG_IPV6)
253c8e6ad08SHannes Frederic Sowa 		if (allow_ipv6 &&
254c8e6ad08SHannes Frederic Sowa 		    cmsg->cmsg_level == SOL_IPV6 &&
255c8e6ad08SHannes Frederic Sowa 		    cmsg->cmsg_type == IPV6_PKTINFO) {
256c8e6ad08SHannes Frederic Sowa 			struct in6_pktinfo *src_info;
257c8e6ad08SHannes Frederic Sowa 
258c8e6ad08SHannes Frederic Sowa 			if (cmsg->cmsg_len < CMSG_LEN(sizeof(*src_info)))
259c8e6ad08SHannes Frederic Sowa 				return -EINVAL;
260c8e6ad08SHannes Frederic Sowa 			src_info = (struct in6_pktinfo *)CMSG_DATA(cmsg);
261c8e6ad08SHannes Frederic Sowa 			if (!ipv6_addr_v4mapped(&src_info->ipi6_addr))
262c8e6ad08SHannes Frederic Sowa 				return -EINVAL;
2631cbec076SDavid Ahern 			if (src_info->ipi6_ifindex)
264c8e6ad08SHannes Frederic Sowa 				ipc->oif = src_info->ipi6_ifindex;
265c8e6ad08SHannes Frederic Sowa 			ipc->addr = src_info->ipi6_addr.s6_addr32[3];
266c8e6ad08SHannes Frederic Sowa 			continue;
267c8e6ad08SHannes Frederic Sowa 		}
268c8e6ad08SHannes Frederic Sowa #endif
26924025c46SSoheil Hassas Yeganeh 		if (cmsg->cmsg_level == SOL_SOCKET) {
2702632616bSEric Dumazet 			err = __sock_cmsg_send(sk, msg, cmsg, &ipc->sockc);
2712632616bSEric Dumazet 			if (err)
2722632616bSEric Dumazet 				return err;
27324025c46SSoheil Hassas Yeganeh 			continue;
27424025c46SSoheil Hassas Yeganeh 		}
27524025c46SSoheil Hassas Yeganeh 
2761da177e4SLinus Torvalds 		if (cmsg->cmsg_level != SOL_IP)
2771da177e4SLinus Torvalds 			continue;
2781da177e4SLinus Torvalds 		switch (cmsg->cmsg_type) {
2791da177e4SLinus Torvalds 		case IP_RETOPTS:
2801ff8cebfSyuan linyu 			err = cmsg->cmsg_len - sizeof(struct cmsghdr);
28191948309SEric Dumazet 
28291948309SEric Dumazet 			/* Our caller is responsible for freeing ipc->opt */
283*de40a3e8SChristoph Hellwig 			err = ip_options_get(net, &ipc->opt,
284*de40a3e8SChristoph Hellwig 					     KERNEL_SOCKPTR(CMSG_DATA(cmsg)),
2854d52cfbeSEric Dumazet 					     err < 40 ? err : 40);
2861da177e4SLinus Torvalds 			if (err)
2871da177e4SLinus Torvalds 				return err;
2881da177e4SLinus Torvalds 			break;
2891da177e4SLinus Torvalds 		case IP_PKTINFO:
2901da177e4SLinus Torvalds 		{
2911da177e4SLinus Torvalds 			struct in_pktinfo *info;
2921da177e4SLinus Torvalds 			if (cmsg->cmsg_len != CMSG_LEN(sizeof(struct in_pktinfo)))
2931da177e4SLinus Torvalds 				return -EINVAL;
2941da177e4SLinus Torvalds 			info = (struct in_pktinfo *)CMSG_DATA(cmsg);
2951cbec076SDavid Ahern 			if (info->ipi_ifindex)
2961da177e4SLinus Torvalds 				ipc->oif = info->ipi_ifindex;
2971da177e4SLinus Torvalds 			ipc->addr = info->ipi_spec_dst.s_addr;
2981da177e4SLinus Torvalds 			break;
2991da177e4SLinus Torvalds 		}
300f02db315SFrancesco Fusco 		case IP_TTL:
301f02db315SFrancesco Fusco 			if (cmsg->cmsg_len != CMSG_LEN(sizeof(int)))
302f02db315SFrancesco Fusco 				return -EINVAL;
303f02db315SFrancesco Fusco 			val = *(int *)CMSG_DATA(cmsg);
304f02db315SFrancesco Fusco 			if (val < 1 || val > 255)
305f02db315SFrancesco Fusco 				return -EINVAL;
306f02db315SFrancesco Fusco 			ipc->ttl = val;
307f02db315SFrancesco Fusco 			break;
308f02db315SFrancesco Fusco 		case IP_TOS:
309e895cdceSEric Dumazet 			if (cmsg->cmsg_len == CMSG_LEN(sizeof(int)))
310f02db315SFrancesco Fusco 				val = *(int *)CMSG_DATA(cmsg);
311e895cdceSEric Dumazet 			else if (cmsg->cmsg_len == CMSG_LEN(sizeof(u8)))
312e895cdceSEric Dumazet 				val = *(u8 *)CMSG_DATA(cmsg);
313e895cdceSEric Dumazet 			else
314e895cdceSEric Dumazet 				return -EINVAL;
315f02db315SFrancesco Fusco 			if (val < 0 || val > 255)
316f02db315SFrancesco Fusco 				return -EINVAL;
317f02db315SFrancesco Fusco 			ipc->tos = val;
318f02db315SFrancesco Fusco 			ipc->priority = rt_tos2priority(ipc->tos);
319f02db315SFrancesco Fusco 			break;
320f02db315SFrancesco Fusco 
3211da177e4SLinus Torvalds 		default:
3221da177e4SLinus Torvalds 			return -EINVAL;
3231da177e4SLinus Torvalds 		}
3241da177e4SLinus Torvalds 	}
3251da177e4SLinus Torvalds 	return 0;
3261da177e4SLinus Torvalds }
3271da177e4SLinus Torvalds 
328592fcb9dSEric Dumazet static void ip_ra_destroy_rcu(struct rcu_head *head)
32966018506SEric Dumazet {
330592fcb9dSEric Dumazet 	struct ip_ra_chain *ra = container_of(head, struct ip_ra_chain, rcu);
331592fcb9dSEric Dumazet 
332592fcb9dSEric Dumazet 	sock_put(ra->saved_sk);
333592fcb9dSEric Dumazet 	kfree(ra);
33466018506SEric Dumazet }
3351da177e4SLinus Torvalds 
3364d52cfbeSEric Dumazet int ip_ra_control(struct sock *sk, unsigned char on,
3374d52cfbeSEric Dumazet 		  void (*destructor)(struct sock *))
3381da177e4SLinus Torvalds {
33943a951e9SEric Dumazet 	struct ip_ra_chain *ra, *new_ra;
34043a951e9SEric Dumazet 	struct ip_ra_chain __rcu **rap;
3415796ef75SKirill Tkhai 	struct net *net = sock_net(sk);
3421da177e4SLinus Torvalds 
343c720c7e8SEric Dumazet 	if (sk->sk_type != SOCK_RAW || inet_sk(sk)->inet_num == IPPROTO_RAW)
3441da177e4SLinus Torvalds 		return -EINVAL;
3451da177e4SLinus Torvalds 
3461da177e4SLinus Torvalds 	new_ra = on ? kmalloc(sizeof(*new_ra), GFP_KERNEL) : NULL;
347425aa0e1SGen Zhang 	if (on && !new_ra)
348425aa0e1SGen Zhang 		return -ENOMEM;
3491da177e4SLinus Torvalds 
350d9ff3049SKirill Tkhai 	mutex_lock(&net->ipv4.ra_mutex);
3515796ef75SKirill Tkhai 	for (rap = &net->ipv4.ra_chain;
35276d3e153SKirill Tkhai 	     (ra = rcu_dereference_protected(*rap,
353d9ff3049SKirill Tkhai 			lockdep_is_held(&net->ipv4.ra_mutex))) != NULL;
35443a951e9SEric Dumazet 	     rap = &ra->next) {
3551da177e4SLinus Torvalds 		if (ra->sk == sk) {
3561da177e4SLinus Torvalds 			if (on) {
357d9ff3049SKirill Tkhai 				mutex_unlock(&net->ipv4.ra_mutex);
3581da177e4SLinus Torvalds 				kfree(new_ra);
3591da177e4SLinus Torvalds 				return -EADDRINUSE;
3601da177e4SLinus Torvalds 			}
361592fcb9dSEric Dumazet 			/* dont let ip_call_ra_chain() use sk again */
362592fcb9dSEric Dumazet 			ra->sk = NULL;
3638e380f00SEric Dumazet 			RCU_INIT_POINTER(*rap, ra->next);
364d9ff3049SKirill Tkhai 			mutex_unlock(&net->ipv4.ra_mutex);
3651da177e4SLinus Torvalds 
3661da177e4SLinus Torvalds 			if (ra->destructor)
3671da177e4SLinus Torvalds 				ra->destructor(sk);
368592fcb9dSEric Dumazet 			/*
369592fcb9dSEric Dumazet 			 * Delay sock_put(sk) and kfree(ra) after one rcu grace
370592fcb9dSEric Dumazet 			 * period. This guarantee ip_call_ra_chain() dont need
371592fcb9dSEric Dumazet 			 * to mess with socket refcounts.
372592fcb9dSEric Dumazet 			 */
373592fcb9dSEric Dumazet 			ra->saved_sk = sk;
374592fcb9dSEric Dumazet 			call_rcu(&ra->rcu, ip_ra_destroy_rcu);
3751da177e4SLinus Torvalds 			return 0;
3761da177e4SLinus Torvalds 		}
3771da177e4SLinus Torvalds 	}
37876d3e153SKirill Tkhai 	if (!new_ra) {
379d9ff3049SKirill Tkhai 		mutex_unlock(&net->ipv4.ra_mutex);
3801da177e4SLinus Torvalds 		return -ENOBUFS;
38176d3e153SKirill Tkhai 	}
3821da177e4SLinus Torvalds 	new_ra->sk = sk;
3831da177e4SLinus Torvalds 	new_ra->destructor = destructor;
3841da177e4SLinus Torvalds 
3858e380f00SEric Dumazet 	RCU_INIT_POINTER(new_ra->next, ra);
38666018506SEric Dumazet 	rcu_assign_pointer(*rap, new_ra);
3871da177e4SLinus Torvalds 	sock_hold(sk);
388d9ff3049SKirill Tkhai 	mutex_unlock(&net->ipv4.ra_mutex);
3891da177e4SLinus Torvalds 
3901da177e4SLinus Torvalds 	return 0;
3911da177e4SLinus Torvalds }
3921da177e4SLinus Torvalds 
3931da177e4SLinus Torvalds void ip_icmp_error(struct sock *sk, struct sk_buff *skb, int err,
39435986b32SAl Viro 		   __be16 port, u32 info, u8 *payload)
3951da177e4SLinus Torvalds {
3961da177e4SLinus Torvalds 	struct sock_exterr_skb *serr;
3971da177e4SLinus Torvalds 
3981da177e4SLinus Torvalds 	skb = skb_clone(skb, GFP_ATOMIC);
3991da177e4SLinus Torvalds 	if (!skb)
4001da177e4SLinus Torvalds 		return;
4011da177e4SLinus Torvalds 
4021da177e4SLinus Torvalds 	serr = SKB_EXT_ERR(skb);
4031da177e4SLinus Torvalds 	serr->ee.ee_errno = err;
4041da177e4SLinus Torvalds 	serr->ee.ee_origin = SO_EE_ORIGIN_ICMP;
40588c7664fSArnaldo Carvalho de Melo 	serr->ee.ee_type = icmp_hdr(skb)->type;
40688c7664fSArnaldo Carvalho de Melo 	serr->ee.ee_code = icmp_hdr(skb)->code;
4071da177e4SLinus Torvalds 	serr->ee.ee_pad = 0;
4081da177e4SLinus Torvalds 	serr->ee.ee_info = info;
4091da177e4SLinus Torvalds 	serr->ee.ee_data = 0;
41088c7664fSArnaldo Carvalho de Melo 	serr->addr_offset = (u8 *)&(((struct iphdr *)(icmp_hdr(skb) + 1))->daddr) -
411d56f90a7SArnaldo Carvalho de Melo 				   skb_network_header(skb);
4121da177e4SLinus Torvalds 	serr->port = port;
4131da177e4SLinus Torvalds 
41400db4124SIan Morris 	if (skb_pull(skb, payload - skb->data)) {
415eba75c58SWillem de Bruijn 		if (inet_sk(sk)->recverr_rfc4884)
416eba75c58SWillem de Bruijn 			ip_icmp_error_rfc4884(skb, &serr->ee.ee_rfc4884);
417eba75c58SWillem de Bruijn 
418bd82393cSArnaldo Carvalho de Melo 		skb_reset_transport_header(skb);
419bd82393cSArnaldo Carvalho de Melo 		if (sock_queue_err_skb(sk, skb) == 0)
420bd82393cSArnaldo Carvalho de Melo 			return;
421bd82393cSArnaldo Carvalho de Melo 	}
4221da177e4SLinus Torvalds 	kfree_skb(skb);
4231da177e4SLinus Torvalds }
4241da177e4SLinus Torvalds 
4250579016eSAl Viro void ip_local_error(struct sock *sk, int err, __be32 daddr, __be16 port, u32 info)
4261da177e4SLinus Torvalds {
4271da177e4SLinus Torvalds 	struct inet_sock *inet = inet_sk(sk);
4281da177e4SLinus Torvalds 	struct sock_exterr_skb *serr;
4291da177e4SLinus Torvalds 	struct iphdr *iph;
4301da177e4SLinus Torvalds 	struct sk_buff *skb;
4311da177e4SLinus Torvalds 
4321da177e4SLinus Torvalds 	if (!inet->recverr)
4331da177e4SLinus Torvalds 		return;
4341da177e4SLinus Torvalds 
4351da177e4SLinus Torvalds 	skb = alloc_skb(sizeof(struct iphdr), GFP_ATOMIC);
4361da177e4SLinus Torvalds 	if (!skb)
4371da177e4SLinus Torvalds 		return;
4381da177e4SLinus Torvalds 
4392ca9e6f2SArnaldo Carvalho de Melo 	skb_put(skb, sizeof(struct iphdr));
4402ca9e6f2SArnaldo Carvalho de Melo 	skb_reset_network_header(skb);
441eddc9ec5SArnaldo Carvalho de Melo 	iph = ip_hdr(skb);
4421da177e4SLinus Torvalds 	iph->daddr = daddr;
4431da177e4SLinus Torvalds 
4441da177e4SLinus Torvalds 	serr = SKB_EXT_ERR(skb);
4451da177e4SLinus Torvalds 	serr->ee.ee_errno = err;
4461da177e4SLinus Torvalds 	serr->ee.ee_origin = SO_EE_ORIGIN_LOCAL;
4471da177e4SLinus Torvalds 	serr->ee.ee_type = 0;
4481da177e4SLinus Torvalds 	serr->ee.ee_code = 0;
4491da177e4SLinus Torvalds 	serr->ee.ee_pad = 0;
4501da177e4SLinus Torvalds 	serr->ee.ee_info = info;
4511da177e4SLinus Torvalds 	serr->ee.ee_data = 0;
452d56f90a7SArnaldo Carvalho de Melo 	serr->addr_offset = (u8 *)&iph->daddr - skb_network_header(skb);
4531da177e4SLinus Torvalds 	serr->port = port;
4541da177e4SLinus Torvalds 
45527a884dcSArnaldo Carvalho de Melo 	__skb_pull(skb, skb_tail_pointer(skb) - skb->data);
456bd82393cSArnaldo Carvalho de Melo 	skb_reset_transport_header(skb);
4571da177e4SLinus Torvalds 
4581da177e4SLinus Torvalds 	if (sock_queue_err_skb(sk, skb))
4591da177e4SLinus Torvalds 		kfree_skb(skb);
4601da177e4SLinus Torvalds }
4611da177e4SLinus Torvalds 
46234b99df4SJulian Anastasov /* For some errors we have valid addr_offset even with zero payload and
46334b99df4SJulian Anastasov  * zero port. Also, addr_offset should be supported if port is set.
46434b99df4SJulian Anastasov  */
46534b99df4SJulian Anastasov static inline bool ipv4_datagram_support_addr(struct sock_exterr_skb *serr)
46634b99df4SJulian Anastasov {
46734b99df4SJulian Anastasov 	return serr->ee.ee_origin == SO_EE_ORIGIN_ICMP ||
46834b99df4SJulian Anastasov 	       serr->ee.ee_origin == SO_EE_ORIGIN_LOCAL || serr->port;
46934b99df4SJulian Anastasov }
47034b99df4SJulian Anastasov 
471c247f053SWillem de Bruijn /* IPv4 supports cmsg on all imcp errors and some timestamps
472c247f053SWillem de Bruijn  *
473c247f053SWillem de Bruijn  * Timestamp code paths do not initialize the fields expected by cmsg:
474c247f053SWillem de Bruijn  * the PKTINFO fields in skb->cb[]. Fill those in here.
475c247f053SWillem de Bruijn  */
476c247f053SWillem de Bruijn static bool ipv4_datagram_support_cmsg(const struct sock *sk,
477c247f053SWillem de Bruijn 				       struct sk_buff *skb,
478829ae9d6SWillem de Bruijn 				       int ee_origin)
479829ae9d6SWillem de Bruijn {
480c247f053SWillem de Bruijn 	struct in_pktinfo *info;
481829ae9d6SWillem de Bruijn 
482c247f053SWillem de Bruijn 	if (ee_origin == SO_EE_ORIGIN_ICMP)
483c247f053SWillem de Bruijn 		return true;
484c247f053SWillem de Bruijn 
485c247f053SWillem de Bruijn 	if (ee_origin == SO_EE_ORIGIN_LOCAL)
486c247f053SWillem de Bruijn 		return false;
487c247f053SWillem de Bruijn 
488c247f053SWillem de Bruijn 	/* Support IP_PKTINFO on tstamp packets if requested, to correlate
4891862d620SWillem de Bruijn 	 * timestamp with egress dev. Not possible for packets without iif
490c247f053SWillem de Bruijn 	 * or without payload (SOF_TIMESTAMPING_OPT_TSONLY).
491c247f053SWillem de Bruijn 	 */
4921862d620SWillem de Bruijn 	info = PKTINFO_SKB_CB(skb);
4931862d620SWillem de Bruijn 	if (!(sk->sk_tsflags & SOF_TIMESTAMPING_OPT_CMSG) ||
4941862d620SWillem de Bruijn 	    !info->ipi_ifindex)
495829ae9d6SWillem de Bruijn 		return false;
496829ae9d6SWillem de Bruijn 
497829ae9d6SWillem de Bruijn 	info->ipi_spec_dst.s_addr = ip_hdr(skb)->saddr;
498829ae9d6SWillem de Bruijn 	return true;
499829ae9d6SWillem de Bruijn }
500829ae9d6SWillem de Bruijn 
5011da177e4SLinus Torvalds /*
5021da177e4SLinus Torvalds  *	Handle MSG_ERRQUEUE
5031da177e4SLinus Torvalds  */
50485fbaa75SHannes Frederic Sowa int ip_recv_error(struct sock *sk, struct msghdr *msg, int len, int *addr_len)
5051da177e4SLinus Torvalds {
5061da177e4SLinus Torvalds 	struct sock_exterr_skb *serr;
507364a9e93SWillem de Bruijn 	struct sk_buff *skb;
508342dfc30SSteffen Hurrle 	DECLARE_SOCKADDR(struct sockaddr_in *, sin, msg->msg_name);
5091da177e4SLinus Torvalds 	struct {
5101da177e4SLinus Torvalds 		struct sock_extended_err ee;
5111da177e4SLinus Torvalds 		struct sockaddr_in	 offender;
5121da177e4SLinus Torvalds 	} errhdr;
5131da177e4SLinus Torvalds 	int err;
5141da177e4SLinus Torvalds 	int copied;
5151da177e4SLinus Torvalds 
5161da177e4SLinus Torvalds 	err = -EAGAIN;
517364a9e93SWillem de Bruijn 	skb = sock_dequeue_err_skb(sk);
51851456b29SIan Morris 	if (!skb)
5191da177e4SLinus Torvalds 		goto out;
5201da177e4SLinus Torvalds 
5211da177e4SLinus Torvalds 	copied = skb->len;
5221da177e4SLinus Torvalds 	if (copied > len) {
5231da177e4SLinus Torvalds 		msg->msg_flags |= MSG_TRUNC;
5241da177e4SLinus Torvalds 		copied = len;
5251da177e4SLinus Torvalds 	}
52651f3d02bSDavid S. Miller 	err = skb_copy_datagram_msg(skb, 0, msg, copied);
527960a2628SEric Dumazet 	if (unlikely(err)) {
528960a2628SEric Dumazet 		kfree_skb(skb);
529960a2628SEric Dumazet 		return err;
530960a2628SEric Dumazet 	}
5311da177e4SLinus Torvalds 	sock_recv_timestamp(msg, sk, skb);
5321da177e4SLinus Torvalds 
5331da177e4SLinus Torvalds 	serr = SKB_EXT_ERR(skb);
5341da177e4SLinus Torvalds 
53534b99df4SJulian Anastasov 	if (sin && ipv4_datagram_support_addr(serr)) {
5361da177e4SLinus Torvalds 		sin->sin_family = AF_INET;
537d56f90a7SArnaldo Carvalho de Melo 		sin->sin_addr.s_addr = *(__be32 *)(skb_network_header(skb) +
538d56f90a7SArnaldo Carvalho de Melo 						   serr->addr_offset);
5391da177e4SLinus Torvalds 		sin->sin_port = serr->port;
5401da177e4SLinus Torvalds 		memset(&sin->sin_zero, 0, sizeof(sin->sin_zero));
54185fbaa75SHannes Frederic Sowa 		*addr_len = sizeof(*sin);
5421da177e4SLinus Torvalds 	}
5431da177e4SLinus Torvalds 
5441da177e4SLinus Torvalds 	memcpy(&errhdr.ee, &serr->ee, sizeof(struct sock_extended_err));
5451da177e4SLinus Torvalds 	sin = &errhdr.offender;
546f812116bSWillem de Bruijn 	memset(sin, 0, sizeof(*sin));
547829ae9d6SWillem de Bruijn 
548c247f053SWillem de Bruijn 	if (ipv4_datagram_support_cmsg(sk, skb, serr->ee.ee_origin)) {
5491da177e4SLinus Torvalds 		sin->sin_family = AF_INET;
550eddc9ec5SArnaldo Carvalho de Melo 		sin->sin_addr.s_addr = ip_hdr(skb)->saddr;
551f812116bSWillem de Bruijn 		if (inet_sk(sk)->cmsg_flags)
5521da177e4SLinus Torvalds 			ip_cmsg_recv(msg, skb);
5531da177e4SLinus Torvalds 	}
5541da177e4SLinus Torvalds 
5551da177e4SLinus Torvalds 	put_cmsg(msg, SOL_IP, IP_RECVERR, sizeof(errhdr), &errhdr);
5561da177e4SLinus Torvalds 
5571da177e4SLinus Torvalds 	/* Now we could try to dump offended packet options */
5581da177e4SLinus Torvalds 
5591da177e4SLinus Torvalds 	msg->msg_flags |= MSG_ERRQUEUE;
5601da177e4SLinus Torvalds 	err = copied;
5611da177e4SLinus Torvalds 
562960a2628SEric Dumazet 	consume_skb(skb);
5631da177e4SLinus Torvalds out:
5641da177e4SLinus Torvalds 	return err;
5651da177e4SLinus Torvalds }
5661da177e4SLinus Torvalds 
5676ebf71baSChristoph Hellwig static void __ip_sock_set_tos(struct sock *sk, int val)
5686ebf71baSChristoph Hellwig {
5696ebf71baSChristoph Hellwig 	if (sk->sk_type == SOCK_STREAM) {
5706ebf71baSChristoph Hellwig 		val &= ~INET_ECN_MASK;
5716ebf71baSChristoph Hellwig 		val |= inet_sk(sk)->tos & INET_ECN_MASK;
5726ebf71baSChristoph Hellwig 	}
5736ebf71baSChristoph Hellwig 	if (inet_sk(sk)->tos != val) {
5746ebf71baSChristoph Hellwig 		inet_sk(sk)->tos = val;
5756ebf71baSChristoph Hellwig 		sk->sk_priority = rt_tos2priority(val);
5766ebf71baSChristoph Hellwig 		sk_dst_reset(sk);
5776ebf71baSChristoph Hellwig 	}
5786ebf71baSChristoph Hellwig }
5796ebf71baSChristoph Hellwig 
5806ebf71baSChristoph Hellwig void ip_sock_set_tos(struct sock *sk, int val)
5816ebf71baSChristoph Hellwig {
5826ebf71baSChristoph Hellwig 	lock_sock(sk);
5836ebf71baSChristoph Hellwig 	__ip_sock_set_tos(sk, val);
5846ebf71baSChristoph Hellwig 	release_sock(sk);
5856ebf71baSChristoph Hellwig }
5866ebf71baSChristoph Hellwig EXPORT_SYMBOL(ip_sock_set_tos);
5871da177e4SLinus Torvalds 
588c4e446bfSChristoph Hellwig void ip_sock_set_freebind(struct sock *sk)
589c4e446bfSChristoph Hellwig {
590c4e446bfSChristoph Hellwig 	lock_sock(sk);
591c4e446bfSChristoph Hellwig 	inet_sk(sk)->freebind = true;
592c4e446bfSChristoph Hellwig 	release_sock(sk);
593c4e446bfSChristoph Hellwig }
594c4e446bfSChristoph Hellwig EXPORT_SYMBOL(ip_sock_set_freebind);
595c4e446bfSChristoph Hellwig 
596db45c0efSChristoph Hellwig void ip_sock_set_recverr(struct sock *sk)
597db45c0efSChristoph Hellwig {
598db45c0efSChristoph Hellwig 	lock_sock(sk);
599db45c0efSChristoph Hellwig 	inet_sk(sk)->recverr = true;
600db45c0efSChristoph Hellwig 	release_sock(sk);
601db45c0efSChristoph Hellwig }
602db45c0efSChristoph Hellwig EXPORT_SYMBOL(ip_sock_set_recverr);
603db45c0efSChristoph Hellwig 
6042de569bdSChristoph Hellwig int ip_sock_set_mtu_discover(struct sock *sk, int val)
6052de569bdSChristoph Hellwig {
6062de569bdSChristoph Hellwig 	if (val < IP_PMTUDISC_DONT || val > IP_PMTUDISC_OMIT)
6072de569bdSChristoph Hellwig 		return -EINVAL;
6082de569bdSChristoph Hellwig 	lock_sock(sk);
6092de569bdSChristoph Hellwig 	inet_sk(sk)->pmtudisc = val;
6102de569bdSChristoph Hellwig 	release_sock(sk);
6112de569bdSChristoph Hellwig 	return 0;
6122de569bdSChristoph Hellwig }
6132de569bdSChristoph Hellwig EXPORT_SYMBOL(ip_sock_set_mtu_discover);
6142de569bdSChristoph Hellwig 
615c1f9ec57SChristoph Hellwig void ip_sock_set_pktinfo(struct sock *sk)
616c1f9ec57SChristoph Hellwig {
617c1f9ec57SChristoph Hellwig 	lock_sock(sk);
618c1f9ec57SChristoph Hellwig 	inet_sk(sk)->cmsg_flags |= IP_CMSG_PKTINFO;
619c1f9ec57SChristoph Hellwig 	release_sock(sk);
620c1f9ec57SChristoph Hellwig }
621c1f9ec57SChristoph Hellwig EXPORT_SYMBOL(ip_sock_set_pktinfo);
622c1f9ec57SChristoph Hellwig 
6231da177e4SLinus Torvalds /*
6244d52cfbeSEric Dumazet  *	Socket option code for IP. This is the end of the line after any
6254d52cfbeSEric Dumazet  *	TCP,UDP etc options on an IP socket.
6261da177e4SLinus Torvalds  */
627baf606d9SMarcelo Ricardo Leitner static bool setsockopt_needs_rtnl(int optname)
628baf606d9SMarcelo Ricardo Leitner {
629baf606d9SMarcelo Ricardo Leitner 	switch (optname) {
630baf606d9SMarcelo Ricardo Leitner 	case IP_ADD_MEMBERSHIP:
631baf606d9SMarcelo Ricardo Leitner 	case IP_ADD_SOURCE_MEMBERSHIP:
63254ff9ef3SMarcelo Ricardo Leitner 	case IP_BLOCK_SOURCE:
633baf606d9SMarcelo Ricardo Leitner 	case IP_DROP_MEMBERSHIP:
63454ff9ef3SMarcelo Ricardo Leitner 	case IP_DROP_SOURCE_MEMBERSHIP:
63554ff9ef3SMarcelo Ricardo Leitner 	case IP_MSFILTER:
63654ff9ef3SMarcelo Ricardo Leitner 	case IP_UNBLOCK_SOURCE:
63754ff9ef3SMarcelo Ricardo Leitner 	case MCAST_BLOCK_SOURCE:
63854ff9ef3SMarcelo Ricardo Leitner 	case MCAST_MSFILTER:
639baf606d9SMarcelo Ricardo Leitner 	case MCAST_JOIN_GROUP:
64054ff9ef3SMarcelo Ricardo Leitner 	case MCAST_JOIN_SOURCE_GROUP:
641baf606d9SMarcelo Ricardo Leitner 	case MCAST_LEAVE_GROUP:
64254ff9ef3SMarcelo Ricardo Leitner 	case MCAST_LEAVE_SOURCE_GROUP:
64354ff9ef3SMarcelo Ricardo Leitner 	case MCAST_UNBLOCK_SOURCE:
644baf606d9SMarcelo Ricardo Leitner 		return true;
645baf606d9SMarcelo Ricardo Leitner 	}
646baf606d9SMarcelo Ricardo Leitner 	return false;
647baf606d9SMarcelo Ricardo Leitner }
6481da177e4SLinus Torvalds 
649e986d4daSAl Viro static int set_mcast_msfilter(struct sock *sk, int ifindex,
650e986d4daSAl Viro 			      int numsrc, int fmode,
651e986d4daSAl Viro 			      struct sockaddr_storage *group,
652e986d4daSAl Viro 			      struct sockaddr_storage *list)
653e986d4daSAl Viro {
654e986d4daSAl Viro 	int msize = IP_MSFILTER_SIZE(numsrc);
655e986d4daSAl Viro 	struct ip_msfilter *msf;
656e986d4daSAl Viro 	struct sockaddr_in *psin;
657e986d4daSAl Viro 	int err, i;
658e986d4daSAl Viro 
659e986d4daSAl Viro 	msf = kmalloc(msize, GFP_KERNEL);
660e986d4daSAl Viro 	if (!msf)
661e986d4daSAl Viro 		return -ENOBUFS;
662e986d4daSAl Viro 
663e986d4daSAl Viro 	psin = (struct sockaddr_in *)group;
664e986d4daSAl Viro 	if (psin->sin_family != AF_INET)
665e986d4daSAl Viro 		goto Eaddrnotavail;
666e986d4daSAl Viro 	msf->imsf_multiaddr = psin->sin_addr.s_addr;
667e986d4daSAl Viro 	msf->imsf_interface = 0;
668e986d4daSAl Viro 	msf->imsf_fmode = fmode;
669e986d4daSAl Viro 	msf->imsf_numsrc = numsrc;
670e986d4daSAl Viro 	for (i = 0; i < numsrc; ++i) {
671e986d4daSAl Viro 		psin = (struct sockaddr_in *)&list[i];
672e986d4daSAl Viro 
673e986d4daSAl Viro 		if (psin->sin_family != AF_INET)
674e986d4daSAl Viro 			goto Eaddrnotavail;
675e986d4daSAl Viro 		msf->imsf_slist[i] = psin->sin_addr.s_addr;
676e986d4daSAl Viro 	}
677e986d4daSAl Viro 	err = ip_mc_msfilter(sk, msf, ifindex);
678e986d4daSAl Viro 	kfree(msf);
679e986d4daSAl Viro 	return err;
680e986d4daSAl Viro 
681e986d4daSAl Viro Eaddrnotavail:
682e986d4daSAl Viro 	kfree(msf);
683e986d4daSAl Viro 	return -EADDRNOTAVAIL;
684e986d4daSAl Viro }
685e986d4daSAl Viro 
686b6238c04SChristoph Hellwig static int copy_group_source_from_user(struct group_source_req *greqs,
687b6238c04SChristoph Hellwig 		void __user *optval, int optlen)
6882bbf8c1eSAl Viro {
689b6238c04SChristoph Hellwig 	if (in_compat_syscall()) {
690b6238c04SChristoph Hellwig 		struct compat_group_source_req gr32;
691b6238c04SChristoph Hellwig 
692b6238c04SChristoph Hellwig 		if (optlen != sizeof(gr32))
693b6238c04SChristoph Hellwig 			return -EINVAL;
694b6238c04SChristoph Hellwig 		if (copy_from_user(&gr32, optval, sizeof(gr32)))
695b6238c04SChristoph Hellwig 			return -EFAULT;
696b6238c04SChristoph Hellwig 		greqs->gsr_interface = gr32.gsr_interface;
697b6238c04SChristoph Hellwig 		greqs->gsr_group = gr32.gsr_group;
698b6238c04SChristoph Hellwig 		greqs->gsr_source = gr32.gsr_source;
699b6238c04SChristoph Hellwig 	} else {
700b6238c04SChristoph Hellwig 		if (optlen != sizeof(*greqs))
701b6238c04SChristoph Hellwig 			return -EINVAL;
702b6238c04SChristoph Hellwig 		if (copy_from_user(greqs, optval, sizeof(*greqs)))
703b6238c04SChristoph Hellwig 			return -EFAULT;
704b6238c04SChristoph Hellwig 	}
705b6238c04SChristoph Hellwig 
706b6238c04SChristoph Hellwig 	return 0;
707b6238c04SChristoph Hellwig }
708b6238c04SChristoph Hellwig 
709b6238c04SChristoph Hellwig static int do_mcast_group_source(struct sock *sk, int optname,
710b6238c04SChristoph Hellwig 		void __user *optval, int optlen)
711b6238c04SChristoph Hellwig {
712b6238c04SChristoph Hellwig 	struct group_source_req greqs;
7132bbf8c1eSAl Viro 	struct ip_mreq_source mreqs;
7142bbf8c1eSAl Viro 	struct sockaddr_in *psin;
7152bbf8c1eSAl Viro 	int omode, add, err;
7162bbf8c1eSAl Viro 
717b6238c04SChristoph Hellwig 	err = copy_group_source_from_user(&greqs, optval, optlen);
718b6238c04SChristoph Hellwig 	if (err)
719b6238c04SChristoph Hellwig 		return err;
720b6238c04SChristoph Hellwig 
721b6238c04SChristoph Hellwig 	if (greqs.gsr_group.ss_family != AF_INET ||
722b6238c04SChristoph Hellwig 	    greqs.gsr_source.ss_family != AF_INET)
7232bbf8c1eSAl Viro 		return -EADDRNOTAVAIL;
7242bbf8c1eSAl Viro 
725b6238c04SChristoph Hellwig 	psin = (struct sockaddr_in *)&greqs.gsr_group;
7262bbf8c1eSAl Viro 	mreqs.imr_multiaddr = psin->sin_addr.s_addr;
727b6238c04SChristoph Hellwig 	psin = (struct sockaddr_in *)&greqs.gsr_source;
7282bbf8c1eSAl Viro 	mreqs.imr_sourceaddr = psin->sin_addr.s_addr;
7292bbf8c1eSAl Viro 	mreqs.imr_interface = 0; /* use index for mc_source */
7302bbf8c1eSAl Viro 
7312bbf8c1eSAl Viro 	if (optname == MCAST_BLOCK_SOURCE) {
7322bbf8c1eSAl Viro 		omode = MCAST_EXCLUDE;
7332bbf8c1eSAl Viro 		add = 1;
7342bbf8c1eSAl Viro 	} else if (optname == MCAST_UNBLOCK_SOURCE) {
7352bbf8c1eSAl Viro 		omode = MCAST_EXCLUDE;
7362bbf8c1eSAl Viro 		add = 0;
7372bbf8c1eSAl Viro 	} else if (optname == MCAST_JOIN_SOURCE_GROUP) {
7382bbf8c1eSAl Viro 		struct ip_mreqn mreq;
7392bbf8c1eSAl Viro 
740b6238c04SChristoph Hellwig 		psin = (struct sockaddr_in *)&greqs.gsr_group;
7412bbf8c1eSAl Viro 		mreq.imr_multiaddr = psin->sin_addr;
7422bbf8c1eSAl Viro 		mreq.imr_address.s_addr = 0;
743b6238c04SChristoph Hellwig 		mreq.imr_ifindex = greqs.gsr_interface;
7442bbf8c1eSAl Viro 		err = ip_mc_join_group_ssm(sk, &mreq, MCAST_INCLUDE);
7452bbf8c1eSAl Viro 		if (err && err != -EADDRINUSE)
7462bbf8c1eSAl Viro 			return err;
747b6238c04SChristoph Hellwig 		greqs.gsr_interface = mreq.imr_ifindex;
7482bbf8c1eSAl Viro 		omode = MCAST_INCLUDE;
7492bbf8c1eSAl Viro 		add = 1;
7502bbf8c1eSAl Viro 	} else /* MCAST_LEAVE_SOURCE_GROUP */ {
7512bbf8c1eSAl Viro 		omode = MCAST_INCLUDE;
7522bbf8c1eSAl Viro 		add = 0;
7532bbf8c1eSAl Viro 	}
754b6238c04SChristoph Hellwig 	return ip_mc_source(add, omode, sk, &mreqs, greqs.gsr_interface);
7552bbf8c1eSAl Viro }
7562bbf8c1eSAl Viro 
757d62c38f6SChristoph Hellwig static int ip_set_mcast_msfilter(struct sock *sk, void __user *optval,
758d62c38f6SChristoph Hellwig 		int optlen)
759d62c38f6SChristoph Hellwig {
760d62c38f6SChristoph Hellwig 	struct group_filter *gsf = NULL;
761d62c38f6SChristoph Hellwig 	int err;
762d62c38f6SChristoph Hellwig 
763d62c38f6SChristoph Hellwig 	if (optlen < GROUP_FILTER_SIZE(0))
764d62c38f6SChristoph Hellwig 		return -EINVAL;
765d62c38f6SChristoph Hellwig 	if (optlen > sysctl_optmem_max)
766d62c38f6SChristoph Hellwig 		return -ENOBUFS;
767d62c38f6SChristoph Hellwig 
768d62c38f6SChristoph Hellwig 	gsf = memdup_user(optval, optlen);
769d62c38f6SChristoph Hellwig 	if (IS_ERR(gsf))
770d62c38f6SChristoph Hellwig 		return PTR_ERR(gsf);
771d62c38f6SChristoph Hellwig 
772d62c38f6SChristoph Hellwig 	/* numsrc >= (4G-140)/128 overflow in 32 bits */
773d62c38f6SChristoph Hellwig 	err = -ENOBUFS;
774d62c38f6SChristoph Hellwig 	if (gsf->gf_numsrc >= 0x1ffffff ||
775d62c38f6SChristoph Hellwig 	    gsf->gf_numsrc > sock_net(sk)->ipv4.sysctl_igmp_max_msf)
776d62c38f6SChristoph Hellwig 		goto out_free_gsf;
777d62c38f6SChristoph Hellwig 
778d62c38f6SChristoph Hellwig 	err = -EINVAL;
779d62c38f6SChristoph Hellwig 	if (GROUP_FILTER_SIZE(gsf->gf_numsrc) > optlen)
780d62c38f6SChristoph Hellwig 		goto out_free_gsf;
781d62c38f6SChristoph Hellwig 
782d62c38f6SChristoph Hellwig 	err = set_mcast_msfilter(sk, gsf->gf_interface, gsf->gf_numsrc,
783d62c38f6SChristoph Hellwig 				 gsf->gf_fmode, &gsf->gf_group, gsf->gf_slist);
784d62c38f6SChristoph Hellwig out_free_gsf:
785d62c38f6SChristoph Hellwig 	kfree(gsf);
786d62c38f6SChristoph Hellwig 	return err;
787d62c38f6SChristoph Hellwig }
788d62c38f6SChristoph Hellwig 
789d62c38f6SChristoph Hellwig static int compat_ip_set_mcast_msfilter(struct sock *sk, void __user *optval,
790d62c38f6SChristoph Hellwig 		int optlen)
791d62c38f6SChristoph Hellwig {
792d62c38f6SChristoph Hellwig 	const int size0 = offsetof(struct compat_group_filter, gf_slist);
793d62c38f6SChristoph Hellwig 	struct compat_group_filter *gf32;
794d62c38f6SChristoph Hellwig 	unsigned int n;
795d62c38f6SChristoph Hellwig 	void *p;
796d62c38f6SChristoph Hellwig 	int err;
797d62c38f6SChristoph Hellwig 
798d62c38f6SChristoph Hellwig 	if (optlen < size0)
799d62c38f6SChristoph Hellwig 		return -EINVAL;
800d62c38f6SChristoph Hellwig 	if (optlen > sysctl_optmem_max - 4)
801d62c38f6SChristoph Hellwig 		return -ENOBUFS;
802d62c38f6SChristoph Hellwig 
803d62c38f6SChristoph Hellwig 	p = kmalloc(optlen + 4, GFP_KERNEL);
804d62c38f6SChristoph Hellwig 	if (!p)
805d62c38f6SChristoph Hellwig 		return -ENOMEM;
806d62c38f6SChristoph Hellwig 	gf32 = p + 4; /* we want ->gf_group and ->gf_slist aligned */
807d62c38f6SChristoph Hellwig 
808d62c38f6SChristoph Hellwig 	err = -EFAULT;
809d62c38f6SChristoph Hellwig 	if (copy_from_user(gf32, optval, optlen))
810d62c38f6SChristoph Hellwig 		goto out_free_gsf;
811d62c38f6SChristoph Hellwig 
812d62c38f6SChristoph Hellwig 	/* numsrc >= (4G-140)/128 overflow in 32 bits */
813d62c38f6SChristoph Hellwig 	n = gf32->gf_numsrc;
814d62c38f6SChristoph Hellwig 	err = -ENOBUFS;
815d62c38f6SChristoph Hellwig 	if (n >= 0x1ffffff)
816d62c38f6SChristoph Hellwig 		goto out_free_gsf;
817d62c38f6SChristoph Hellwig 
818d62c38f6SChristoph Hellwig 	err = -EINVAL;
819d62c38f6SChristoph Hellwig 	if (offsetof(struct compat_group_filter, gf_slist[n]) > optlen)
820d62c38f6SChristoph Hellwig 		goto out_free_gsf;
821d62c38f6SChristoph Hellwig 
822d62c38f6SChristoph Hellwig 	/* numsrc >= (4G-140)/128 overflow in 32 bits */
823d62c38f6SChristoph Hellwig 	err = -ENOBUFS;
824d62c38f6SChristoph Hellwig 	if (n > sock_net(sk)->ipv4.sysctl_igmp_max_msf)
825b6238c04SChristoph Hellwig 		goto out_free_gsf;
826d62c38f6SChristoph Hellwig 	err = set_mcast_msfilter(sk, gf32->gf_interface, n, gf32->gf_fmode,
827d62c38f6SChristoph Hellwig 				 &gf32->gf_group, gf32->gf_slist);
828d62c38f6SChristoph Hellwig out_free_gsf:
829d62c38f6SChristoph Hellwig 	kfree(p);
830d62c38f6SChristoph Hellwig 	return err;
831d62c38f6SChristoph Hellwig }
832d62c38f6SChristoph Hellwig 
83302caad7cSChristoph Hellwig static int ip_mcast_join_leave(struct sock *sk, int optname,
83402caad7cSChristoph Hellwig 		void __user *optval, int optlen)
83502caad7cSChristoph Hellwig {
83602caad7cSChristoph Hellwig 	struct ip_mreqn mreq = { };
83702caad7cSChristoph Hellwig 	struct sockaddr_in *psin;
83802caad7cSChristoph Hellwig 	struct group_req greq;
83902caad7cSChristoph Hellwig 
84002caad7cSChristoph Hellwig 	if (optlen < sizeof(struct group_req))
84102caad7cSChristoph Hellwig 		return -EINVAL;
84202caad7cSChristoph Hellwig 	if (copy_from_user(&greq, optval, sizeof(greq)))
84302caad7cSChristoph Hellwig 		return -EFAULT;
84402caad7cSChristoph Hellwig 
84502caad7cSChristoph Hellwig 	psin = (struct sockaddr_in *)&greq.gr_group;
84602caad7cSChristoph Hellwig 	if (psin->sin_family != AF_INET)
84702caad7cSChristoph Hellwig 		return -EINVAL;
84802caad7cSChristoph Hellwig 	mreq.imr_multiaddr = psin->sin_addr;
84902caad7cSChristoph Hellwig 	mreq.imr_ifindex = greq.gr_interface;
85002caad7cSChristoph Hellwig 	if (optname == MCAST_JOIN_GROUP)
85102caad7cSChristoph Hellwig 		return ip_mc_join_group(sk, &mreq);
85202caad7cSChristoph Hellwig 	return ip_mc_leave_group(sk, &mreq);
85302caad7cSChristoph Hellwig }
85402caad7cSChristoph Hellwig 
85502caad7cSChristoph Hellwig static int compat_ip_mcast_join_leave(struct sock *sk, int optname,
85602caad7cSChristoph Hellwig 		void __user *optval, int optlen)
85702caad7cSChristoph Hellwig {
85802caad7cSChristoph Hellwig 	struct compat_group_req greq;
85902caad7cSChristoph Hellwig 	struct ip_mreqn mreq = { };
86002caad7cSChristoph Hellwig 	struct sockaddr_in *psin;
86102caad7cSChristoph Hellwig 
86202caad7cSChristoph Hellwig 	if (optlen < sizeof(struct compat_group_req))
86302caad7cSChristoph Hellwig 		return -EINVAL;
86402caad7cSChristoph Hellwig 	if (copy_from_user(&greq, optval, sizeof(greq)))
86502caad7cSChristoph Hellwig 		return -EFAULT;
86602caad7cSChristoph Hellwig 
86702caad7cSChristoph Hellwig 	psin = (struct sockaddr_in *)&greq.gr_group;
86802caad7cSChristoph Hellwig 	if (psin->sin_family != AF_INET)
86902caad7cSChristoph Hellwig 		return -EINVAL;
87002caad7cSChristoph Hellwig 	mreq.imr_multiaddr = psin->sin_addr;
87102caad7cSChristoph Hellwig 	mreq.imr_ifindex = greq.gr_interface;
87202caad7cSChristoph Hellwig 
87302caad7cSChristoph Hellwig 	if (optname == MCAST_JOIN_GROUP)
874b6238c04SChristoph Hellwig 		return ip_mc_join_group(sk, &mreq);
875b6238c04SChristoph Hellwig 	return ip_mc_leave_group(sk, &mreq);
87602caad7cSChristoph Hellwig }
87702caad7cSChristoph Hellwig 
8783fdadf7dSDmitry Mishin static int do_ip_setsockopt(struct sock *sk, int level,
879b7058842SDavid S. Miller 			    int optname, char __user *optval, unsigned int optlen)
8801da177e4SLinus Torvalds {
8811da177e4SLinus Torvalds 	struct inet_sock *inet = inet_sk(sk);
882166b6b2dSNikolay Borisov 	struct net *net = sock_net(sk);
8831da177e4SLinus Torvalds 	int val = 0, err;
884baf606d9SMarcelo Ricardo Leitner 	bool needs_rtnl = setsockopt_needs_rtnl(optname);
8851da177e4SLinus Torvalds 
8860c9f79beSXi Wang 	switch (optname) {
8870c9f79beSXi Wang 	case IP_PKTINFO:
8880c9f79beSXi Wang 	case IP_RECVTTL:
8890c9f79beSXi Wang 	case IP_RECVOPTS:
8900c9f79beSXi Wang 	case IP_RECVTOS:
8910c9f79beSXi Wang 	case IP_RETOPTS:
8920c9f79beSXi Wang 	case IP_TOS:
8930c9f79beSXi Wang 	case IP_TTL:
8940c9f79beSXi Wang 	case IP_HDRINCL:
8950c9f79beSXi Wang 	case IP_MTU_DISCOVER:
8960c9f79beSXi Wang 	case IP_RECVERR:
8970c9f79beSXi Wang 	case IP_ROUTER_ALERT:
8980c9f79beSXi Wang 	case IP_FREEBIND:
8990c9f79beSXi Wang 	case IP_PASSSEC:
9000c9f79beSXi Wang 	case IP_TRANSPARENT:
9010c9f79beSXi Wang 	case IP_MINTTL:
9020c9f79beSXi Wang 	case IP_NODEFRAG:
90390c337daSEric Dumazet 	case IP_BIND_ADDRESS_NO_PORT:
9040c9f79beSXi Wang 	case IP_UNICAST_IF:
9050c9f79beSXi Wang 	case IP_MULTICAST_TTL:
9060c9f79beSXi Wang 	case IP_MULTICAST_ALL:
9070c9f79beSXi Wang 	case IP_MULTICAST_LOOP:
9080c9f79beSXi Wang 	case IP_RECVORIGDSTADDR:
909ad6f939aSTom Herbert 	case IP_CHECKSUM:
91070ecc248SWillem de Bruijn 	case IP_RECVFRAGSIZE:
911eba75c58SWillem de Bruijn 	case IP_RECVERR_RFC4884:
9121da177e4SLinus Torvalds 		if (optlen >= sizeof(int)) {
9131da177e4SLinus Torvalds 			if (get_user(val, (int __user *) optval))
9141da177e4SLinus Torvalds 				return -EFAULT;
9151da177e4SLinus Torvalds 		} else if (optlen >= sizeof(char)) {
9161da177e4SLinus Torvalds 			unsigned char ucval;
9171da177e4SLinus Torvalds 
9181da177e4SLinus Torvalds 			if (get_user(ucval, (unsigned char __user *) optval))
9191da177e4SLinus Torvalds 				return -EFAULT;
9201da177e4SLinus Torvalds 			val = (int) ucval;
9211da177e4SLinus Torvalds 		}
9221da177e4SLinus Torvalds 	}
9231da177e4SLinus Torvalds 
9241da177e4SLinus Torvalds 	/* If optlen==0, it is equivalent to val == 0 */
9251da177e4SLinus Torvalds 
9260526947fSKirill Tkhai 	if (optname == IP_ROUTER_ALERT)
9270526947fSKirill Tkhai 		return ip_ra_control(sk, val ? 1 : 0, NULL);
9286a9fb947SPavel Emelyanov 	if (ip_mroute_opt(optname))
92901ccb5b4SChristoph Hellwig 		return ip_mroute_setsockopt(sk, optname, USER_SOCKPTR(optval),
93001ccb5b4SChristoph Hellwig 					    optlen);
9311da177e4SLinus Torvalds 
9321da177e4SLinus Torvalds 	err = 0;
933baf606d9SMarcelo Ricardo Leitner 	if (needs_rtnl)
934baf606d9SMarcelo Ricardo Leitner 		rtnl_lock();
9351da177e4SLinus Torvalds 	lock_sock(sk);
9361da177e4SLinus Torvalds 
9371da177e4SLinus Torvalds 	switch (optname) {
9381da177e4SLinus Torvalds 	case IP_OPTIONS:
9391da177e4SLinus Torvalds 	{
940f6d8bd05SEric Dumazet 		struct ip_options_rcu *old, *opt = NULL;
941f6d8bd05SEric Dumazet 
94265a1c4ffSroel kluin 		if (optlen > 40)
9431da177e4SLinus Torvalds 			goto e_inval;
944*de40a3e8SChristoph Hellwig 		err = ip_options_get(sock_net(sk), &opt, USER_SOCKPTR(optval),
945*de40a3e8SChristoph Hellwig 					      optlen);
9461da177e4SLinus Torvalds 		if (err)
9471da177e4SLinus Torvalds 			break;
948f6d8bd05SEric Dumazet 		old = rcu_dereference_protected(inet->inet_opt,
9491e1d04e6SHannes Frederic Sowa 						lockdep_sock_is_held(sk));
950d83d8461SArnaldo Carvalho de Melo 		if (inet->is_icsk) {
951d83d8461SArnaldo Carvalho de Melo 			struct inet_connection_sock *icsk = inet_csk(sk);
952dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6)
9531da177e4SLinus Torvalds 			if (sk->sk_family == PF_INET ||
9541da177e4SLinus Torvalds 			    (!((1 << sk->sk_state) &
9551da177e4SLinus Torvalds 			       (TCPF_LISTEN | TCPF_CLOSE)) &&
956c720c7e8SEric Dumazet 			     inet->inet_daddr != LOOPBACK4_IPV6)) {
9571da177e4SLinus Torvalds #endif
958f6d8bd05SEric Dumazet 				if (old)
959f6d8bd05SEric Dumazet 					icsk->icsk_ext_hdr_len -= old->opt.optlen;
9601da177e4SLinus Torvalds 				if (opt)
961f6d8bd05SEric Dumazet 					icsk->icsk_ext_hdr_len += opt->opt.optlen;
962d83d8461SArnaldo Carvalho de Melo 				icsk->icsk_sync_mss(sk, icsk->icsk_pmtu_cookie);
963dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6)
9641da177e4SLinus Torvalds 			}
9651da177e4SLinus Torvalds #endif
9661da177e4SLinus Torvalds 		}
967f6d8bd05SEric Dumazet 		rcu_assign_pointer(inet->inet_opt, opt);
968f6d8bd05SEric Dumazet 		if (old)
969605b4afeSPaul E. McKenney 			kfree_rcu(old, rcu);
9701da177e4SLinus Torvalds 		break;
9711da177e4SLinus Torvalds 	}
9721da177e4SLinus Torvalds 	case IP_PKTINFO:
9731da177e4SLinus Torvalds 		if (val)
9741da177e4SLinus Torvalds 			inet->cmsg_flags |= IP_CMSG_PKTINFO;
9751da177e4SLinus Torvalds 		else
9761da177e4SLinus Torvalds 			inet->cmsg_flags &= ~IP_CMSG_PKTINFO;
9771da177e4SLinus Torvalds 		break;
9781da177e4SLinus Torvalds 	case IP_RECVTTL:
9791da177e4SLinus Torvalds 		if (val)
9801da177e4SLinus Torvalds 			inet->cmsg_flags |=  IP_CMSG_TTL;
9811da177e4SLinus Torvalds 		else
9821da177e4SLinus Torvalds 			inet->cmsg_flags &= ~IP_CMSG_TTL;
9831da177e4SLinus Torvalds 		break;
9841da177e4SLinus Torvalds 	case IP_RECVTOS:
9851da177e4SLinus Torvalds 		if (val)
9861da177e4SLinus Torvalds 			inet->cmsg_flags |=  IP_CMSG_TOS;
9871da177e4SLinus Torvalds 		else
9881da177e4SLinus Torvalds 			inet->cmsg_flags &= ~IP_CMSG_TOS;
9891da177e4SLinus Torvalds 		break;
9901da177e4SLinus Torvalds 	case IP_RECVOPTS:
9911da177e4SLinus Torvalds 		if (val)
9921da177e4SLinus Torvalds 			inet->cmsg_flags |=  IP_CMSG_RECVOPTS;
9931da177e4SLinus Torvalds 		else
9941da177e4SLinus Torvalds 			inet->cmsg_flags &= ~IP_CMSG_RECVOPTS;
9951da177e4SLinus Torvalds 		break;
9961da177e4SLinus Torvalds 	case IP_RETOPTS:
9971da177e4SLinus Torvalds 		if (val)
9981da177e4SLinus Torvalds 			inet->cmsg_flags |= IP_CMSG_RETOPTS;
9991da177e4SLinus Torvalds 		else
10001da177e4SLinus Torvalds 			inet->cmsg_flags &= ~IP_CMSG_RETOPTS;
10011da177e4SLinus Torvalds 		break;
10022c7946a7SCatherine Zhang 	case IP_PASSSEC:
10032c7946a7SCatherine Zhang 		if (val)
10042c7946a7SCatherine Zhang 			inet->cmsg_flags |= IP_CMSG_PASSSEC;
10052c7946a7SCatherine Zhang 		else
10062c7946a7SCatherine Zhang 			inet->cmsg_flags &= ~IP_CMSG_PASSSEC;
10072c7946a7SCatherine Zhang 		break;
1008e8b2dfe9SBalazs Scheidler 	case IP_RECVORIGDSTADDR:
1009e8b2dfe9SBalazs Scheidler 		if (val)
1010e8b2dfe9SBalazs Scheidler 			inet->cmsg_flags |= IP_CMSG_ORIGDSTADDR;
1011e8b2dfe9SBalazs Scheidler 		else
1012e8b2dfe9SBalazs Scheidler 			inet->cmsg_flags &= ~IP_CMSG_ORIGDSTADDR;
1013e8b2dfe9SBalazs Scheidler 		break;
1014ad6f939aSTom Herbert 	case IP_CHECKSUM:
1015ad6f939aSTom Herbert 		if (val) {
1016ad6f939aSTom Herbert 			if (!(inet->cmsg_flags & IP_CMSG_CHECKSUM)) {
1017ad6f939aSTom Herbert 				inet_inc_convert_csum(sk);
1018ad6f939aSTom Herbert 				inet->cmsg_flags |= IP_CMSG_CHECKSUM;
1019ad6f939aSTom Herbert 			}
1020ad6f939aSTom Herbert 		} else {
1021ad6f939aSTom Herbert 			if (inet->cmsg_flags & IP_CMSG_CHECKSUM) {
1022ad6f939aSTom Herbert 				inet_dec_convert_csum(sk);
1023ad6f939aSTom Herbert 				inet->cmsg_flags &= ~IP_CMSG_CHECKSUM;
1024ad6f939aSTom Herbert 			}
1025ad6f939aSTom Herbert 		}
1026ad6f939aSTom Herbert 		break;
102770ecc248SWillem de Bruijn 	case IP_RECVFRAGSIZE:
102870ecc248SWillem de Bruijn 		if (sk->sk_type != SOCK_RAW && sk->sk_type != SOCK_DGRAM)
102970ecc248SWillem de Bruijn 			goto e_inval;
103070ecc248SWillem de Bruijn 		if (val)
103170ecc248SWillem de Bruijn 			inet->cmsg_flags |= IP_CMSG_RECVFRAGSIZE;
103270ecc248SWillem de Bruijn 		else
103370ecc248SWillem de Bruijn 			inet->cmsg_flags &= ~IP_CMSG_RECVFRAGSIZE;
103470ecc248SWillem de Bruijn 		break;
10351da177e4SLinus Torvalds 	case IP_TOS:	/* This sets both TOS and Precedence */
10366ebf71baSChristoph Hellwig 		__ip_sock_set_tos(sk, val);
10371da177e4SLinus Torvalds 		break;
10381da177e4SLinus Torvalds 	case IP_TTL:
10391da177e4SLinus Torvalds 		if (optlen < 1)
10401da177e4SLinus Torvalds 			goto e_inval;
1041c9be4a5cSCong Wang 		if (val != -1 && (val < 1 || val > 255))
10421da177e4SLinus Torvalds 			goto e_inval;
10431da177e4SLinus Torvalds 		inet->uc_ttl = val;
10441da177e4SLinus Torvalds 		break;
10451da177e4SLinus Torvalds 	case IP_HDRINCL:
10461da177e4SLinus Torvalds 		if (sk->sk_type != SOCK_RAW) {
10471da177e4SLinus Torvalds 			err = -ENOPROTOOPT;
10481da177e4SLinus Torvalds 			break;
10491da177e4SLinus Torvalds 		}
10501da177e4SLinus Torvalds 		inet->hdrincl = val ? 1 : 0;
10511da177e4SLinus Torvalds 		break;
10527b2ff18eSJiri Olsa 	case IP_NODEFRAG:
10537b2ff18eSJiri Olsa 		if (sk->sk_type != SOCK_RAW) {
10547b2ff18eSJiri Olsa 			err = -ENOPROTOOPT;
10557b2ff18eSJiri Olsa 			break;
10567b2ff18eSJiri Olsa 		}
10577b2ff18eSJiri Olsa 		inet->nodefrag = val ? 1 : 0;
10587b2ff18eSJiri Olsa 		break;
105990c337daSEric Dumazet 	case IP_BIND_ADDRESS_NO_PORT:
106090c337daSEric Dumazet 		inet->bind_address_no_port = val ? 1 : 0;
106190c337daSEric Dumazet 		break;
10621da177e4SLinus Torvalds 	case IP_MTU_DISCOVER:
10631b346576SHannes Frederic Sowa 		if (val < IP_PMTUDISC_DONT || val > IP_PMTUDISC_OMIT)
10641da177e4SLinus Torvalds 			goto e_inval;
10651da177e4SLinus Torvalds 		inet->pmtudisc = val;
10661da177e4SLinus Torvalds 		break;
10671da177e4SLinus Torvalds 	case IP_RECVERR:
10681da177e4SLinus Torvalds 		inet->recverr = !!val;
10691da177e4SLinus Torvalds 		if (!val)
10701da177e4SLinus Torvalds 			skb_queue_purge(&sk->sk_error_queue);
10711da177e4SLinus Torvalds 		break;
1072eba75c58SWillem de Bruijn 	case IP_RECVERR_RFC4884:
1073eba75c58SWillem de Bruijn 		if (val < 0 || val > 1)
1074eba75c58SWillem de Bruijn 			goto e_inval;
1075eba75c58SWillem de Bruijn 		inet->recverr_rfc4884 = !!val;
1076eba75c58SWillem de Bruijn 		break;
10771da177e4SLinus Torvalds 	case IP_MULTICAST_TTL:
10781da177e4SLinus Torvalds 		if (sk->sk_type == SOCK_STREAM)
10791da177e4SLinus Torvalds 			goto e_inval;
10801da177e4SLinus Torvalds 		if (optlen < 1)
10811da177e4SLinus Torvalds 			goto e_inval;
10821da177e4SLinus Torvalds 		if (val == -1)
10831da177e4SLinus Torvalds 			val = 1;
10841da177e4SLinus Torvalds 		if (val < 0 || val > 255)
10851da177e4SLinus Torvalds 			goto e_inval;
10861da177e4SLinus Torvalds 		inet->mc_ttl = val;
10871da177e4SLinus Torvalds 		break;
10881da177e4SLinus Torvalds 	case IP_MULTICAST_LOOP:
10891da177e4SLinus Torvalds 		if (optlen < 1)
10901da177e4SLinus Torvalds 			goto e_inval;
10911da177e4SLinus Torvalds 		inet->mc_loop = !!val;
10921da177e4SLinus Torvalds 		break;
109376e21053SErich E. Hoover 	case IP_UNICAST_IF:
109476e21053SErich E. Hoover 	{
109576e21053SErich E. Hoover 		struct net_device *dev = NULL;
109676e21053SErich E. Hoover 		int ifindex;
10979515a2e0SDavid Ahern 		int midx;
109876e21053SErich E. Hoover 
109976e21053SErich E. Hoover 		if (optlen != sizeof(int))
110076e21053SErich E. Hoover 			goto e_inval;
110176e21053SErich E. Hoover 
110276e21053SErich E. Hoover 		ifindex = (__force int)ntohl((__force __be32)val);
110376e21053SErich E. Hoover 		if (ifindex == 0) {
110476e21053SErich E. Hoover 			inet->uc_index = 0;
110576e21053SErich E. Hoover 			err = 0;
110676e21053SErich E. Hoover 			break;
110776e21053SErich E. Hoover 		}
110876e21053SErich E. Hoover 
110976e21053SErich E. Hoover 		dev = dev_get_by_index(sock_net(sk), ifindex);
111076e21053SErich E. Hoover 		err = -EADDRNOTAVAIL;
111176e21053SErich E. Hoover 		if (!dev)
111276e21053SErich E. Hoover 			break;
11139515a2e0SDavid Ahern 
11149515a2e0SDavid Ahern 		midx = l3mdev_master_ifindex(dev);
111576e21053SErich E. Hoover 		dev_put(dev);
111676e21053SErich E. Hoover 
111776e21053SErich E. Hoover 		err = -EINVAL;
11189515a2e0SDavid Ahern 		if (sk->sk_bound_dev_if &&
11199515a2e0SDavid Ahern 		    (!midx || midx != sk->sk_bound_dev_if))
112076e21053SErich E. Hoover 			break;
112176e21053SErich E. Hoover 
112276e21053SErich E. Hoover 		inet->uc_index = ifindex;
112376e21053SErich E. Hoover 		err = 0;
112476e21053SErich E. Hoover 		break;
112576e21053SErich E. Hoover 	}
11261da177e4SLinus Torvalds 	case IP_MULTICAST_IF:
11271da177e4SLinus Torvalds 	{
11281da177e4SLinus Torvalds 		struct ip_mreqn mreq;
11291da177e4SLinus Torvalds 		struct net_device *dev = NULL;
11307bb387c5SDavid Ahern 		int midx;
11311da177e4SLinus Torvalds 
11321da177e4SLinus Torvalds 		if (sk->sk_type == SOCK_STREAM)
11331da177e4SLinus Torvalds 			goto e_inval;
11341da177e4SLinus Torvalds 		/*
11351da177e4SLinus Torvalds 		 *	Check the arguments are allowable
11361da177e4SLinus Torvalds 		 */
11371da177e4SLinus Torvalds 
11380915921bSShan Wei 		if (optlen < sizeof(struct in_addr))
11390915921bSShan Wei 			goto e_inval;
11400915921bSShan Wei 
11411da177e4SLinus Torvalds 		err = -EFAULT;
11421da177e4SLinus Torvalds 		if (optlen >= sizeof(struct ip_mreqn)) {
11431da177e4SLinus Torvalds 			if (copy_from_user(&mreq, optval, sizeof(mreq)))
11441da177e4SLinus Torvalds 				break;
11451da177e4SLinus Torvalds 		} else {
11461da177e4SLinus Torvalds 			memset(&mreq, 0, sizeof(mreq));
11473a084ddbSJiri Pirko 			if (optlen >= sizeof(struct ip_mreq)) {
11483a084ddbSJiri Pirko 				if (copy_from_user(&mreq, optval,
11493a084ddbSJiri Pirko 						   sizeof(struct ip_mreq)))
11503a084ddbSJiri Pirko 					break;
11513a084ddbSJiri Pirko 			} else if (optlen >= sizeof(struct in_addr)) {
11523a084ddbSJiri Pirko 				if (copy_from_user(&mreq.imr_address, optval,
11534d52cfbeSEric Dumazet 						   sizeof(struct in_addr)))
11541da177e4SLinus Torvalds 					break;
11551da177e4SLinus Torvalds 			}
11563a084ddbSJiri Pirko 		}
11571da177e4SLinus Torvalds 
11581da177e4SLinus Torvalds 		if (!mreq.imr_ifindex) {
1159e6f1cebfSAl Viro 			if (mreq.imr_address.s_addr == htonl(INADDR_ANY)) {
11601da177e4SLinus Torvalds 				inet->mc_index = 0;
11611da177e4SLinus Torvalds 				inet->mc_addr  = 0;
11621da177e4SLinus Torvalds 				err = 0;
11631da177e4SLinus Torvalds 				break;
11641da177e4SLinus Torvalds 			}
11653b1e0a65SYOSHIFUJI Hideaki 			dev = ip_dev_find(sock_net(sk), mreq.imr_address.s_addr);
116655b80503SEric Dumazet 			if (dev)
11671da177e4SLinus Torvalds 				mreq.imr_ifindex = dev->ifindex;
11681da177e4SLinus Torvalds 		} else
116955b80503SEric Dumazet 			dev = dev_get_by_index(sock_net(sk), mreq.imr_ifindex);
11701da177e4SLinus Torvalds 
11711da177e4SLinus Torvalds 
11721da177e4SLinus Torvalds 		err = -EADDRNOTAVAIL;
11731da177e4SLinus Torvalds 		if (!dev)
11741da177e4SLinus Torvalds 			break;
11757bb387c5SDavid Ahern 
11767bb387c5SDavid Ahern 		midx = l3mdev_master_ifindex(dev);
11777bb387c5SDavid Ahern 
117855b80503SEric Dumazet 		dev_put(dev);
11791da177e4SLinus Torvalds 
11801da177e4SLinus Torvalds 		err = -EINVAL;
11811da177e4SLinus Torvalds 		if (sk->sk_bound_dev_if &&
11827bb387c5SDavid Ahern 		    mreq.imr_ifindex != sk->sk_bound_dev_if &&
11837bb387c5SDavid Ahern 		    (!midx || midx != sk->sk_bound_dev_if))
11841da177e4SLinus Torvalds 			break;
11851da177e4SLinus Torvalds 
11861da177e4SLinus Torvalds 		inet->mc_index = mreq.imr_ifindex;
11871da177e4SLinus Torvalds 		inet->mc_addr  = mreq.imr_address.s_addr;
11881da177e4SLinus Torvalds 		err = 0;
11891da177e4SLinus Torvalds 		break;
11901da177e4SLinus Torvalds 	}
11911da177e4SLinus Torvalds 
11921da177e4SLinus Torvalds 	case IP_ADD_MEMBERSHIP:
11931da177e4SLinus Torvalds 	case IP_DROP_MEMBERSHIP:
11941da177e4SLinus Torvalds 	{
11951da177e4SLinus Torvalds 		struct ip_mreqn mreq;
11961da177e4SLinus Torvalds 
1197a96fb49bSFlavio Leitner 		err = -EPROTO;
1198a96fb49bSFlavio Leitner 		if (inet_sk(sk)->is_icsk)
1199a96fb49bSFlavio Leitner 			break;
1200a96fb49bSFlavio Leitner 
12011da177e4SLinus Torvalds 		if (optlen < sizeof(struct ip_mreq))
12021da177e4SLinus Torvalds 			goto e_inval;
12031da177e4SLinus Torvalds 		err = -EFAULT;
12041da177e4SLinus Torvalds 		if (optlen >= sizeof(struct ip_mreqn)) {
12051da177e4SLinus Torvalds 			if (copy_from_user(&mreq, optval, sizeof(mreq)))
12061da177e4SLinus Torvalds 				break;
12071da177e4SLinus Torvalds 		} else {
12081da177e4SLinus Torvalds 			memset(&mreq, 0, sizeof(mreq));
12091da177e4SLinus Torvalds 			if (copy_from_user(&mreq, optval, sizeof(struct ip_mreq)))
12101da177e4SLinus Torvalds 				break;
12111da177e4SLinus Torvalds 		}
12121da177e4SLinus Torvalds 
12131da177e4SLinus Torvalds 		if (optname == IP_ADD_MEMBERSHIP)
121454ff9ef3SMarcelo Ricardo Leitner 			err = ip_mc_join_group(sk, &mreq);
12151da177e4SLinus Torvalds 		else
121654ff9ef3SMarcelo Ricardo Leitner 			err = ip_mc_leave_group(sk, &mreq);
12171da177e4SLinus Torvalds 		break;
12181da177e4SLinus Torvalds 	}
12191da177e4SLinus Torvalds 	case IP_MSFILTER:
12201da177e4SLinus Torvalds 	{
12211da177e4SLinus Torvalds 		struct ip_msfilter *msf;
12221da177e4SLinus Torvalds 
12231da177e4SLinus Torvalds 		if (optlen < IP_MSFILTER_SIZE(0))
12241da177e4SLinus Torvalds 			goto e_inval;
12251da177e4SLinus Torvalds 		if (optlen > sysctl_optmem_max) {
12261da177e4SLinus Torvalds 			err = -ENOBUFS;
12271da177e4SLinus Torvalds 			break;
12281da177e4SLinus Torvalds 		}
1229a2c841d9SAl Viro 		msf = memdup_user(optval, optlen);
1230a2c841d9SAl Viro 		if (IS_ERR(msf)) {
1231a2c841d9SAl Viro 			err = PTR_ERR(msf);
12321da177e4SLinus Torvalds 			break;
12331da177e4SLinus Torvalds 		}
12341da177e4SLinus Torvalds 		/* numsrc >= (1G-4) overflow in 32 bits */
12351da177e4SLinus Torvalds 		if (msf->imsf_numsrc >= 0x3ffffffcU ||
1236166b6b2dSNikolay Borisov 		    msf->imsf_numsrc > net->ipv4.sysctl_igmp_max_msf) {
12371da177e4SLinus Torvalds 			kfree(msf);
12381da177e4SLinus Torvalds 			err = -ENOBUFS;
12391da177e4SLinus Torvalds 			break;
12401da177e4SLinus Torvalds 		}
12411da177e4SLinus Torvalds 		if (IP_MSFILTER_SIZE(msf->imsf_numsrc) > optlen) {
12421da177e4SLinus Torvalds 			kfree(msf);
12431da177e4SLinus Torvalds 			err = -EINVAL;
12441da177e4SLinus Torvalds 			break;
12451da177e4SLinus Torvalds 		}
12461da177e4SLinus Torvalds 		err = ip_mc_msfilter(sk, msf, 0);
12471da177e4SLinus Torvalds 		kfree(msf);
12481da177e4SLinus Torvalds 		break;
12491da177e4SLinus Torvalds 	}
12501da177e4SLinus Torvalds 	case IP_BLOCK_SOURCE:
12511da177e4SLinus Torvalds 	case IP_UNBLOCK_SOURCE:
12521da177e4SLinus Torvalds 	case IP_ADD_SOURCE_MEMBERSHIP:
12531da177e4SLinus Torvalds 	case IP_DROP_SOURCE_MEMBERSHIP:
12541da177e4SLinus Torvalds 	{
12551da177e4SLinus Torvalds 		struct ip_mreq_source mreqs;
12561da177e4SLinus Torvalds 		int omode, add;
12571da177e4SLinus Torvalds 
12581da177e4SLinus Torvalds 		if (optlen != sizeof(struct ip_mreq_source))
12591da177e4SLinus Torvalds 			goto e_inval;
12601da177e4SLinus Torvalds 		if (copy_from_user(&mreqs, optval, sizeof(mreqs))) {
12611da177e4SLinus Torvalds 			err = -EFAULT;
12621da177e4SLinus Torvalds 			break;
12631da177e4SLinus Torvalds 		}
12641da177e4SLinus Torvalds 		if (optname == IP_BLOCK_SOURCE) {
12651da177e4SLinus Torvalds 			omode = MCAST_EXCLUDE;
12661da177e4SLinus Torvalds 			add = 1;
12671da177e4SLinus Torvalds 		} else if (optname == IP_UNBLOCK_SOURCE) {
12681da177e4SLinus Torvalds 			omode = MCAST_EXCLUDE;
12691da177e4SLinus Torvalds 			add = 0;
12701da177e4SLinus Torvalds 		} else if (optname == IP_ADD_SOURCE_MEMBERSHIP) {
12711da177e4SLinus Torvalds 			struct ip_mreqn mreq;
12721da177e4SLinus Torvalds 
12731da177e4SLinus Torvalds 			mreq.imr_multiaddr.s_addr = mreqs.imr_multiaddr;
12741da177e4SLinus Torvalds 			mreq.imr_address.s_addr = mreqs.imr_interface;
12751da177e4SLinus Torvalds 			mreq.imr_ifindex = 0;
12766e2059b5SHangbin Liu 			err = ip_mc_join_group_ssm(sk, &mreq, MCAST_INCLUDE);
12778cdaaa15SDavid L Stevens 			if (err && err != -EADDRINUSE)
12781da177e4SLinus Torvalds 				break;
12791da177e4SLinus Torvalds 			omode = MCAST_INCLUDE;
12801da177e4SLinus Torvalds 			add = 1;
12811da177e4SLinus Torvalds 		} else /* IP_DROP_SOURCE_MEMBERSHIP */ {
12821da177e4SLinus Torvalds 			omode = MCAST_INCLUDE;
12831da177e4SLinus Torvalds 			add = 0;
12841da177e4SLinus Torvalds 		}
12851da177e4SLinus Torvalds 		err = ip_mc_source(add, omode, sk, &mreqs, 0);
12861da177e4SLinus Torvalds 		break;
12871da177e4SLinus Torvalds 	}
12881da177e4SLinus Torvalds 	case MCAST_JOIN_GROUP:
12891da177e4SLinus Torvalds 	case MCAST_LEAVE_GROUP:
1290b6238c04SChristoph Hellwig 		if (in_compat_syscall())
1291b6238c04SChristoph Hellwig 			err = compat_ip_mcast_join_leave(sk, optname, optval,
1292b6238c04SChristoph Hellwig 							 optlen);
1293b6238c04SChristoph Hellwig 		else
129402caad7cSChristoph Hellwig 			err = ip_mcast_join_leave(sk, optname, optval, optlen);
12951da177e4SLinus Torvalds 		break;
12961da177e4SLinus Torvalds 	case MCAST_JOIN_SOURCE_GROUP:
12971da177e4SLinus Torvalds 	case MCAST_LEAVE_SOURCE_GROUP:
12981da177e4SLinus Torvalds 	case MCAST_BLOCK_SOURCE:
12991da177e4SLinus Torvalds 	case MCAST_UNBLOCK_SOURCE:
1300b6238c04SChristoph Hellwig 		err = do_mcast_group_source(sk, optname, optval, optlen);
13011da177e4SLinus Torvalds 		break;
13021da177e4SLinus Torvalds 	case MCAST_MSFILTER:
1303b6238c04SChristoph Hellwig 		if (in_compat_syscall())
1304b6238c04SChristoph Hellwig 			err = compat_ip_set_mcast_msfilter(sk, optval, optlen);
1305b6238c04SChristoph Hellwig 		else
1306d62c38f6SChristoph Hellwig 			err = ip_set_mcast_msfilter(sk, optval, optlen);
13071da177e4SLinus Torvalds 		break;
1308f771bef9SNivedita Singhvi 	case IP_MULTICAST_ALL:
1309f771bef9SNivedita Singhvi 		if (optlen < 1)
1310f771bef9SNivedita Singhvi 			goto e_inval;
1311f771bef9SNivedita Singhvi 		if (val != 0 && val != 1)
1312f771bef9SNivedita Singhvi 			goto e_inval;
1313f771bef9SNivedita Singhvi 		inet->mc_all = val;
1314f771bef9SNivedita Singhvi 		break;
13151da177e4SLinus Torvalds 
13161da177e4SLinus Torvalds 	case IP_FREEBIND:
13171da177e4SLinus Torvalds 		if (optlen < 1)
13181da177e4SLinus Torvalds 			goto e_inval;
13191da177e4SLinus Torvalds 		inet->freebind = !!val;
13201da177e4SLinus Torvalds 		break;
13211da177e4SLinus Torvalds 
13221da177e4SLinus Torvalds 	case IP_IPSEC_POLICY:
13231da177e4SLinus Torvalds 	case IP_XFRM_POLICY:
13246fc0b4a7SHerbert Xu 		err = -EPERM;
132552e804c6SEric W. Biederman 		if (!ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN))
13266fc0b4a7SHerbert Xu 			break;
1327c6d1b26aSChristoph Hellwig 		err = xfrm_user_policy(sk, optname, USER_SOCKPTR(optval),
1328c6d1b26aSChristoph Hellwig 				       optlen);
13291da177e4SLinus Torvalds 		break;
13301da177e4SLinus Torvalds 
1331f5715aeaSKOVACS Krisztian 	case IP_TRANSPARENT:
133252e804c6SEric W. Biederman 		if (!!val && !ns_capable(sock_net(sk)->user_ns, CAP_NET_RAW) &&
133352e804c6SEric W. Biederman 		    !ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN)) {
1334f5715aeaSKOVACS Krisztian 			err = -EPERM;
1335f5715aeaSKOVACS Krisztian 			break;
1336f5715aeaSKOVACS Krisztian 		}
1337f5715aeaSKOVACS Krisztian 		if (optlen < 1)
1338f5715aeaSKOVACS Krisztian 			goto e_inval;
1339f5715aeaSKOVACS Krisztian 		inet->transparent = !!val;
1340f5715aeaSKOVACS Krisztian 		break;
1341f5715aeaSKOVACS Krisztian 
1342d218d111SStephen Hemminger 	case IP_MINTTL:
1343d218d111SStephen Hemminger 		if (optlen < 1)
1344d218d111SStephen Hemminger 			goto e_inval;
1345d218d111SStephen Hemminger 		if (val < 0 || val > 255)
1346d218d111SStephen Hemminger 			goto e_inval;
1347d218d111SStephen Hemminger 		inet->min_ttl = val;
1348d218d111SStephen Hemminger 		break;
1349d218d111SStephen Hemminger 
13501da177e4SLinus Torvalds 	default:
13511da177e4SLinus Torvalds 		err = -ENOPROTOOPT;
13521da177e4SLinus Torvalds 		break;
13531da177e4SLinus Torvalds 	}
13541da177e4SLinus Torvalds 	release_sock(sk);
1355baf606d9SMarcelo Ricardo Leitner 	if (needs_rtnl)
1356baf606d9SMarcelo Ricardo Leitner 		rtnl_unlock();
13571da177e4SLinus Torvalds 	return err;
13581da177e4SLinus Torvalds 
13591da177e4SLinus Torvalds e_inval:
13601da177e4SLinus Torvalds 	release_sock(sk);
1361baf606d9SMarcelo Ricardo Leitner 	if (needs_rtnl)
1362baf606d9SMarcelo Ricardo Leitner 		rtnl_unlock();
13631da177e4SLinus Torvalds 	return -EINVAL;
13641da177e4SLinus Torvalds }
13651da177e4SLinus Torvalds 
1366f84af32cSEric Dumazet /**
1367829ae9d6SWillem de Bruijn  * ipv4_pktinfo_prepare - transfer some info from rtable to skb
1368f84af32cSEric Dumazet  * @sk: socket
1369f84af32cSEric Dumazet  * @skb: buffer
1370f84af32cSEric Dumazet  *
137135ebf65eSDavid S. Miller  * To support IP_CMSG_PKTINFO option, we store rt_iif and specific
137235ebf65eSDavid S. Miller  * destination in skb->cb[] before dst drop.
13738e3bff96Sstephen hemminger  * This way, receiver doesn't make cache line misses to read rtable.
1374f84af32cSEric Dumazet  */
1375fbf8866dSShawn Bohrer void ipv4_pktinfo_prepare(const struct sock *sk, struct sk_buff *skb)
1376f84af32cSEric Dumazet {
1377d826eb14SEric Dumazet 	struct in_pktinfo *pktinfo = PKTINFO_SKB_CB(skb);
13784b261c75SHannes Frederic Sowa 	bool prepare = (inet_sk(sk)->cmsg_flags & IP_CMSG_PKTINFO) ||
13794b261c75SHannes Frederic Sowa 		       ipv6_sk_rxinfo(sk);
1380d826eb14SEric Dumazet 
13814b261c75SHannes Frederic Sowa 	if (prepare && skb_rtable(skb)) {
13820b922b7aSDavid Ahern 		/* skb->cb is overloaded: prior to this point it is IP{6}CB
13830b922b7aSDavid Ahern 		 * which has interface index (iif) as the first member of the
13840b922b7aSDavid Ahern 		 * underlying inet{6}_skb_parm struct. This code then overlays
13850b922b7aSDavid Ahern 		 * PKTINFO_SKB_CB and in_pktinfo also has iif as the first
1386f0c16ba8SWei Zhang 		 * element so the iif is picked up from the prior IPCB. If iif
1387f0c16ba8SWei Zhang 		 * is the loopback interface, then return the sending interface
1388f0c16ba8SWei Zhang 		 * (e.g., process binds socket to eth0 for Tx which is
1389f0c16ba8SWei Zhang 		 * redirected to loopback in the rtable/dst).
13900b922b7aSDavid Ahern 		 */
1391cbea8f02SDavid Ahern 		struct rtable *rt = skb_rtable(skb);
1392cbea8f02SDavid Ahern 		bool l3slave = ipv4_l3mdev_skb(IPCB(skb)->flags);
1393cbea8f02SDavid Ahern 
1394cbea8f02SDavid Ahern 		if (pktinfo->ipi_ifindex == LOOPBACK_IFINDEX)
1395f0c16ba8SWei Zhang 			pktinfo->ipi_ifindex = inet_iif(skb);
1396cbea8f02SDavid Ahern 		else if (l3slave && rt && rt->rt_iif)
1397cbea8f02SDavid Ahern 			pktinfo->ipi_ifindex = rt->rt_iif;
1398f0c16ba8SWei Zhang 
139935ebf65eSDavid S. Miller 		pktinfo->ipi_spec_dst.s_addr = fib_compute_spec_dst(skb);
1400d826eb14SEric Dumazet 	} else {
1401d826eb14SEric Dumazet 		pktinfo->ipi_ifindex = 0;
1402d826eb14SEric Dumazet 		pktinfo->ipi_spec_dst.s_addr = 0;
1403f84af32cSEric Dumazet 	}
1404d826eb14SEric Dumazet 	skb_dst_drop(skb);
1405d826eb14SEric Dumazet }
1406f84af32cSEric Dumazet 
14073fdadf7dSDmitry Mishin int ip_setsockopt(struct sock *sk, int level,
1408b7058842SDavid S. Miller 		int optname, char __user *optval, unsigned int optlen)
14093fdadf7dSDmitry Mishin {
14103fdadf7dSDmitry Mishin 	int err;
14113fdadf7dSDmitry Mishin 
14123fdadf7dSDmitry Mishin 	if (level != SOL_IP)
14133fdadf7dSDmitry Mishin 		return -ENOPROTOOPT;
14143fdadf7dSDmitry Mishin 
14153fdadf7dSDmitry Mishin 	err = do_ip_setsockopt(sk, level, optname, optval, optlen);
141697adaddaSTaehee Yoo #if IS_ENABLED(CONFIG_BPFILTER_UMH)
1417d2ba09c1SAlexei Starovoitov 	if (optname >= BPFILTER_IPT_SO_SET_REPLACE &&
1418d2ba09c1SAlexei Starovoitov 	    optname < BPFILTER_IPT_SET_MAX)
1419b03afaa8SChristoph Hellwig 		err = bpfilter_ip_set_sockopt(sk, optname, USER_SOCKPTR(optval),
1420b03afaa8SChristoph Hellwig 					      optlen);
1421d2ba09c1SAlexei Starovoitov #endif
14223fdadf7dSDmitry Mishin #ifdef CONFIG_NETFILTER
14233fdadf7dSDmitry Mishin 	/* we need to exclude all possible ENOPROTOOPTs except default case */
14243fdadf7dSDmitry Mishin 	if (err == -ENOPROTOOPT && optname != IP_HDRINCL &&
14256a9fb947SPavel Emelyanov 			optname != IP_IPSEC_POLICY &&
14266a9fb947SPavel Emelyanov 			optname != IP_XFRM_POLICY &&
14273f34cfaeSPaolo Abeni 			!ip_mroute_opt(optname))
1428c2f12630SChristoph Hellwig 		err = nf_setsockopt(sk, PF_INET, optname, USER_SOCKPTR(optval),
1429c2f12630SChristoph Hellwig 				    optlen);
14303fdadf7dSDmitry Mishin #endif
14313fdadf7dSDmitry Mishin 	return err;
14323fdadf7dSDmitry Mishin }
14334d52cfbeSEric Dumazet EXPORT_SYMBOL(ip_setsockopt);
14343fdadf7dSDmitry Mishin 
14351da177e4SLinus Torvalds /*
14364d52cfbeSEric Dumazet  *	Get the options. Note for future reference. The GET of IP options gets
14374d52cfbeSEric Dumazet  *	the _received_ ones. The set sets the _sent_ ones.
14381da177e4SLinus Torvalds  */
14391da177e4SLinus Torvalds 
144087e9f031SWANG Cong static bool getsockopt_needs_rtnl(int optname)
144187e9f031SWANG Cong {
144287e9f031SWANG Cong 	switch (optname) {
144387e9f031SWANG Cong 	case IP_MSFILTER:
144487e9f031SWANG Cong 	case MCAST_MSFILTER:
144587e9f031SWANG Cong 		return true;
144687e9f031SWANG Cong 	}
144787e9f031SWANG Cong 	return false;
144887e9f031SWANG Cong }
144987e9f031SWANG Cong 
145049e74c24SChristoph Hellwig static int ip_get_mcast_msfilter(struct sock *sk, void __user *optval,
145149e74c24SChristoph Hellwig 		int __user *optlen, int len)
145249e74c24SChristoph Hellwig {
145349e74c24SChristoph Hellwig 	const int size0 = offsetof(struct group_filter, gf_slist);
145449e74c24SChristoph Hellwig 	struct group_filter __user *p = optval;
145549e74c24SChristoph Hellwig 	struct group_filter gsf;
145649e74c24SChristoph Hellwig 	int num;
145749e74c24SChristoph Hellwig 	int err;
145849e74c24SChristoph Hellwig 
145949e74c24SChristoph Hellwig 	if (len < size0)
146049e74c24SChristoph Hellwig 		return -EINVAL;
146149e74c24SChristoph Hellwig 	if (copy_from_user(&gsf, p, size0))
146249e74c24SChristoph Hellwig 		return -EFAULT;
146349e74c24SChristoph Hellwig 
146449e74c24SChristoph Hellwig 	num = gsf.gf_numsrc;
146549e74c24SChristoph Hellwig 	err = ip_mc_gsfget(sk, &gsf, p->gf_slist);
146649e74c24SChristoph Hellwig 	if (err)
146749e74c24SChristoph Hellwig 		return err;
146849e74c24SChristoph Hellwig 	if (gsf.gf_numsrc < num)
146949e74c24SChristoph Hellwig 		num = gsf.gf_numsrc;
147049e74c24SChristoph Hellwig 	if (put_user(GROUP_FILTER_SIZE(num), optlen) ||
147149e74c24SChristoph Hellwig 	    copy_to_user(p, &gsf, size0))
147249e74c24SChristoph Hellwig 		return -EFAULT;
147349e74c24SChristoph Hellwig 	return 0;
147449e74c24SChristoph Hellwig }
147549e74c24SChristoph Hellwig 
147649e74c24SChristoph Hellwig static int compat_ip_get_mcast_msfilter(struct sock *sk, void __user *optval,
1477b6238c04SChristoph Hellwig 		int __user *optlen, int len)
147849e74c24SChristoph Hellwig {
147949e74c24SChristoph Hellwig 	const int size0 = offsetof(struct compat_group_filter, gf_slist);
148049e74c24SChristoph Hellwig 	struct compat_group_filter __user *p = optval;
148149e74c24SChristoph Hellwig 	struct compat_group_filter gf32;
148249e74c24SChristoph Hellwig 	struct group_filter gf;
148349e74c24SChristoph Hellwig 	int num;
1484b6238c04SChristoph Hellwig 	int err;
148549e74c24SChristoph Hellwig 
148649e74c24SChristoph Hellwig 	if (len < size0)
148749e74c24SChristoph Hellwig 		return -EINVAL;
148849e74c24SChristoph Hellwig 	if (copy_from_user(&gf32, p, size0))
148949e74c24SChristoph Hellwig 		return -EFAULT;
149049e74c24SChristoph Hellwig 
149149e74c24SChristoph Hellwig 	gf.gf_interface = gf32.gf_interface;
149249e74c24SChristoph Hellwig 	gf.gf_fmode = gf32.gf_fmode;
149349e74c24SChristoph Hellwig 	num = gf.gf_numsrc = gf32.gf_numsrc;
149449e74c24SChristoph Hellwig 	gf.gf_group = gf32.gf_group;
149549e74c24SChristoph Hellwig 
149649e74c24SChristoph Hellwig 	err = ip_mc_gsfget(sk, &gf, p->gf_slist);
149749e74c24SChristoph Hellwig 	if (err)
149849e74c24SChristoph Hellwig 		return err;
149949e74c24SChristoph Hellwig 	if (gf.gf_numsrc < num)
150049e74c24SChristoph Hellwig 		num = gf.gf_numsrc;
150149e74c24SChristoph Hellwig 	len = GROUP_FILTER_SIZE(num) - (sizeof(gf) - sizeof(gf32));
150249e74c24SChristoph Hellwig 	if (put_user(len, optlen) ||
150349e74c24SChristoph Hellwig 	    put_user(gf.gf_fmode, &p->gf_fmode) ||
150449e74c24SChristoph Hellwig 	    put_user(gf.gf_numsrc, &p->gf_numsrc))
150549e74c24SChristoph Hellwig 		return -EFAULT;
150649e74c24SChristoph Hellwig 	return 0;
150749e74c24SChristoph Hellwig }
150849e74c24SChristoph Hellwig 
15093fdadf7dSDmitry Mishin static int do_ip_getsockopt(struct sock *sk, int level, int optname,
1510b6238c04SChristoph Hellwig 			    char __user *optval, int __user *optlen)
15111da177e4SLinus Torvalds {
15121da177e4SLinus Torvalds 	struct inet_sock *inet = inet_sk(sk);
151387e9f031SWANG Cong 	bool needs_rtnl = getsockopt_needs_rtnl(optname);
151487e9f031SWANG Cong 	int val, err = 0;
15151da177e4SLinus Torvalds 	int len;
15161da177e4SLinus Torvalds 
15171da177e4SLinus Torvalds 	if (level != SOL_IP)
15181da177e4SLinus Torvalds 		return -EOPNOTSUPP;
15191da177e4SLinus Torvalds 
15206a9fb947SPavel Emelyanov 	if (ip_mroute_opt(optname))
15211da177e4SLinus Torvalds 		return ip_mroute_getsockopt(sk, optname, optval, optlen);
15221da177e4SLinus Torvalds 
15231da177e4SLinus Torvalds 	if (get_user(len, optlen))
15241da177e4SLinus Torvalds 		return -EFAULT;
15251da177e4SLinus Torvalds 	if (len < 0)
15261da177e4SLinus Torvalds 		return -EINVAL;
15271da177e4SLinus Torvalds 
152887e9f031SWANG Cong 	if (needs_rtnl)
152987e9f031SWANG Cong 		rtnl_lock();
15301da177e4SLinus Torvalds 	lock_sock(sk);
15311da177e4SLinus Torvalds 
15321da177e4SLinus Torvalds 	switch (optname) {
15331da177e4SLinus Torvalds 	case IP_OPTIONS:
15341da177e4SLinus Torvalds 	{
15351da177e4SLinus Torvalds 		unsigned char optbuf[sizeof(struct ip_options)+40];
15361da177e4SLinus Torvalds 		struct ip_options *opt = (struct ip_options *)optbuf;
1537f6d8bd05SEric Dumazet 		struct ip_options_rcu *inet_opt;
1538f6d8bd05SEric Dumazet 
1539f6d8bd05SEric Dumazet 		inet_opt = rcu_dereference_protected(inet->inet_opt,
15401e1d04e6SHannes Frederic Sowa 						     lockdep_sock_is_held(sk));
15411da177e4SLinus Torvalds 		opt->optlen = 0;
1542f6d8bd05SEric Dumazet 		if (inet_opt)
1543f6d8bd05SEric Dumazet 			memcpy(optbuf, &inet_opt->opt,
15441da177e4SLinus Torvalds 			       sizeof(struct ip_options) +
1545f6d8bd05SEric Dumazet 			       inet_opt->opt.optlen);
15461da177e4SLinus Torvalds 		release_sock(sk);
15471da177e4SLinus Torvalds 
15481da177e4SLinus Torvalds 		if (opt->optlen == 0)
15491da177e4SLinus Torvalds 			return put_user(0, optlen);
15501da177e4SLinus Torvalds 
15511da177e4SLinus Torvalds 		ip_options_undo(opt);
15521da177e4SLinus Torvalds 
15531da177e4SLinus Torvalds 		len = min_t(unsigned int, len, opt->optlen);
15541da177e4SLinus Torvalds 		if (put_user(len, optlen))
15551da177e4SLinus Torvalds 			return -EFAULT;
15561da177e4SLinus Torvalds 		if (copy_to_user(optval, opt->__data, len))
15571da177e4SLinus Torvalds 			return -EFAULT;
15581da177e4SLinus Torvalds 		return 0;
15591da177e4SLinus Torvalds 	}
15601da177e4SLinus Torvalds 	case IP_PKTINFO:
15611da177e4SLinus Torvalds 		val = (inet->cmsg_flags & IP_CMSG_PKTINFO) != 0;
15621da177e4SLinus Torvalds 		break;
15631da177e4SLinus Torvalds 	case IP_RECVTTL:
15641da177e4SLinus Torvalds 		val = (inet->cmsg_flags & IP_CMSG_TTL) != 0;
15651da177e4SLinus Torvalds 		break;
15661da177e4SLinus Torvalds 	case IP_RECVTOS:
15671da177e4SLinus Torvalds 		val = (inet->cmsg_flags & IP_CMSG_TOS) != 0;
15681da177e4SLinus Torvalds 		break;
15691da177e4SLinus Torvalds 	case IP_RECVOPTS:
15701da177e4SLinus Torvalds 		val = (inet->cmsg_flags & IP_CMSG_RECVOPTS) != 0;
15711da177e4SLinus Torvalds 		break;
15721da177e4SLinus Torvalds 	case IP_RETOPTS:
15731da177e4SLinus Torvalds 		val = (inet->cmsg_flags & IP_CMSG_RETOPTS) != 0;
15741da177e4SLinus Torvalds 		break;
15752c7946a7SCatherine Zhang 	case IP_PASSSEC:
15762c7946a7SCatherine Zhang 		val = (inet->cmsg_flags & IP_CMSG_PASSSEC) != 0;
15772c7946a7SCatherine Zhang 		break;
1578e8b2dfe9SBalazs Scheidler 	case IP_RECVORIGDSTADDR:
1579e8b2dfe9SBalazs Scheidler 		val = (inet->cmsg_flags & IP_CMSG_ORIGDSTADDR) != 0;
1580e8b2dfe9SBalazs Scheidler 		break;
1581ad6f939aSTom Herbert 	case IP_CHECKSUM:
1582ad6f939aSTom Herbert 		val = (inet->cmsg_flags & IP_CMSG_CHECKSUM) != 0;
1583ad6f939aSTom Herbert 		break;
158470ecc248SWillem de Bruijn 	case IP_RECVFRAGSIZE:
158570ecc248SWillem de Bruijn 		val = (inet->cmsg_flags & IP_CMSG_RECVFRAGSIZE) != 0;
158670ecc248SWillem de Bruijn 		break;
15871da177e4SLinus Torvalds 	case IP_TOS:
15881da177e4SLinus Torvalds 		val = inet->tos;
15891da177e4SLinus Torvalds 		break;
15901da177e4SLinus Torvalds 	case IP_TTL:
1591fa50d974SNikolay Borisov 	{
1592fa50d974SNikolay Borisov 		struct net *net = sock_net(sk);
15931da177e4SLinus Torvalds 		val = (inet->uc_ttl == -1 ?
1594fa50d974SNikolay Borisov 		       net->ipv4.sysctl_ip_default_ttl :
15951da177e4SLinus Torvalds 		       inet->uc_ttl);
15961da177e4SLinus Torvalds 		break;
1597fa50d974SNikolay Borisov 	}
15981da177e4SLinus Torvalds 	case IP_HDRINCL:
15991da177e4SLinus Torvalds 		val = inet->hdrincl;
16001da177e4SLinus Torvalds 		break;
1601a89b4763SMichael Kerrisk 	case IP_NODEFRAG:
1602a89b4763SMichael Kerrisk 		val = inet->nodefrag;
1603a89b4763SMichael Kerrisk 		break;
160490c337daSEric Dumazet 	case IP_BIND_ADDRESS_NO_PORT:
160590c337daSEric Dumazet 		val = inet->bind_address_no_port;
160690c337daSEric Dumazet 		break;
16071da177e4SLinus Torvalds 	case IP_MTU_DISCOVER:
16081da177e4SLinus Torvalds 		val = inet->pmtudisc;
16091da177e4SLinus Torvalds 		break;
16101da177e4SLinus Torvalds 	case IP_MTU:
16111da177e4SLinus Torvalds 	{
16121da177e4SLinus Torvalds 		struct dst_entry *dst;
16131da177e4SLinus Torvalds 		val = 0;
16141da177e4SLinus Torvalds 		dst = sk_dst_get(sk);
16151da177e4SLinus Torvalds 		if (dst) {
16161da177e4SLinus Torvalds 			val = dst_mtu(dst);
16171da177e4SLinus Torvalds 			dst_release(dst);
16181da177e4SLinus Torvalds 		}
16191da177e4SLinus Torvalds 		if (!val) {
16201da177e4SLinus Torvalds 			release_sock(sk);
16211da177e4SLinus Torvalds 			return -ENOTCONN;
16221da177e4SLinus Torvalds 		}
16231da177e4SLinus Torvalds 		break;
16241da177e4SLinus Torvalds 	}
16251da177e4SLinus Torvalds 	case IP_RECVERR:
16261da177e4SLinus Torvalds 		val = inet->recverr;
16271da177e4SLinus Torvalds 		break;
1628eba75c58SWillem de Bruijn 	case IP_RECVERR_RFC4884:
1629eba75c58SWillem de Bruijn 		val = inet->recverr_rfc4884;
1630eba75c58SWillem de Bruijn 		break;
16311da177e4SLinus Torvalds 	case IP_MULTICAST_TTL:
16321da177e4SLinus Torvalds 		val = inet->mc_ttl;
16331da177e4SLinus Torvalds 		break;
16341da177e4SLinus Torvalds 	case IP_MULTICAST_LOOP:
16351da177e4SLinus Torvalds 		val = inet->mc_loop;
16361da177e4SLinus Torvalds 		break;
163776e21053SErich E. Hoover 	case IP_UNICAST_IF:
163876e21053SErich E. Hoover 		val = (__force int)htonl((__u32) inet->uc_index);
163976e21053SErich E. Hoover 		break;
16401da177e4SLinus Torvalds 	case IP_MULTICAST_IF:
16411da177e4SLinus Torvalds 	{
16421da177e4SLinus Torvalds 		struct in_addr addr;
16431da177e4SLinus Torvalds 		len = min_t(unsigned int, len, sizeof(struct in_addr));
16441da177e4SLinus Torvalds 		addr.s_addr = inet->mc_addr;
16451da177e4SLinus Torvalds 		release_sock(sk);
16461da177e4SLinus Torvalds 
16471da177e4SLinus Torvalds 		if (put_user(len, optlen))
16481da177e4SLinus Torvalds 			return -EFAULT;
16491da177e4SLinus Torvalds 		if (copy_to_user(optval, &addr, len))
16501da177e4SLinus Torvalds 			return -EFAULT;
16511da177e4SLinus Torvalds 		return 0;
16521da177e4SLinus Torvalds 	}
16531da177e4SLinus Torvalds 	case IP_MSFILTER:
16541da177e4SLinus Torvalds 	{
16551da177e4SLinus Torvalds 		struct ip_msfilter msf;
16561da177e4SLinus Torvalds 
16571da177e4SLinus Torvalds 		if (len < IP_MSFILTER_SIZE(0)) {
165887e9f031SWANG Cong 			err = -EINVAL;
165987e9f031SWANG Cong 			goto out;
16601da177e4SLinus Torvalds 		}
16611da177e4SLinus Torvalds 		if (copy_from_user(&msf, optval, IP_MSFILTER_SIZE(0))) {
166287e9f031SWANG Cong 			err = -EFAULT;
166387e9f031SWANG Cong 			goto out;
16641da177e4SLinus Torvalds 		}
16651da177e4SLinus Torvalds 		err = ip_mc_msfget(sk, &msf,
16661da177e4SLinus Torvalds 				   (struct ip_msfilter __user *)optval, optlen);
166787e9f031SWANG Cong 		goto out;
16681da177e4SLinus Torvalds 	}
16691da177e4SLinus Torvalds 	case MCAST_MSFILTER:
1670b6238c04SChristoph Hellwig 		if (in_compat_syscall())
1671b6238c04SChristoph Hellwig 			err = compat_ip_get_mcast_msfilter(sk, optval, optlen,
1672b6238c04SChristoph Hellwig 							   len);
1673b6238c04SChristoph Hellwig 		else
167449e74c24SChristoph Hellwig 			err = ip_get_mcast_msfilter(sk, optval, optlen, len);
167587e9f031SWANG Cong 		goto out;
1676f771bef9SNivedita Singhvi 	case IP_MULTICAST_ALL:
1677f771bef9SNivedita Singhvi 		val = inet->mc_all;
1678f771bef9SNivedita Singhvi 		break;
16791da177e4SLinus Torvalds 	case IP_PKTOPTIONS:
16801da177e4SLinus Torvalds 	{
16811da177e4SLinus Torvalds 		struct msghdr msg;
16821da177e4SLinus Torvalds 
16831da177e4SLinus Torvalds 		release_sock(sk);
16841da177e4SLinus Torvalds 
16851da177e4SLinus Torvalds 		if (sk->sk_type != SOCK_STREAM)
16861da177e4SLinus Torvalds 			return -ENOPROTOOPT;
16871da177e4SLinus Torvalds 
16881f466e1fSChristoph Hellwig 		msg.msg_control_is_user = true;
16891f466e1fSChristoph Hellwig 		msg.msg_control_user = optval;
16901da177e4SLinus Torvalds 		msg.msg_controllen = len;
1691b6238c04SChristoph Hellwig 		msg.msg_flags = in_compat_syscall() ? MSG_CMSG_COMPAT : 0;
16921da177e4SLinus Torvalds 
16931da177e4SLinus Torvalds 		if (inet->cmsg_flags & IP_CMSG_PKTINFO) {
16941da177e4SLinus Torvalds 			struct in_pktinfo info;
16951da177e4SLinus Torvalds 
1696c720c7e8SEric Dumazet 			info.ipi_addr.s_addr = inet->inet_rcv_saddr;
1697c720c7e8SEric Dumazet 			info.ipi_spec_dst.s_addr = inet->inet_rcv_saddr;
16981da177e4SLinus Torvalds 			info.ipi_ifindex = inet->mc_index;
16991da177e4SLinus Torvalds 			put_cmsg(&msg, SOL_IP, IP_PKTINFO, sizeof(info), &info);
17001da177e4SLinus Torvalds 		}
17011da177e4SLinus Torvalds 		if (inet->cmsg_flags & IP_CMSG_TTL) {
17021da177e4SLinus Torvalds 			int hlim = inet->mc_ttl;
17031da177e4SLinus Torvalds 			put_cmsg(&msg, SOL_IP, IP_TTL, sizeof(hlim), &hlim);
17041da177e4SLinus Torvalds 		}
17054c507d28SJiri Benc 		if (inet->cmsg_flags & IP_CMSG_TOS) {
17064c507d28SJiri Benc 			int tos = inet->rcv_tos;
17074c507d28SJiri Benc 			put_cmsg(&msg, SOL_IP, IP_TOS, sizeof(tos), &tos);
17084c507d28SJiri Benc 		}
17091da177e4SLinus Torvalds 		len -= msg.msg_controllen;
17101da177e4SLinus Torvalds 		return put_user(len, optlen);
17111da177e4SLinus Torvalds 	}
17121da177e4SLinus Torvalds 	case IP_FREEBIND:
17131da177e4SLinus Torvalds 		val = inet->freebind;
17141da177e4SLinus Torvalds 		break;
1715f5715aeaSKOVACS Krisztian 	case IP_TRANSPARENT:
1716f5715aeaSKOVACS Krisztian 		val = inet->transparent;
1717f5715aeaSKOVACS Krisztian 		break;
1718d218d111SStephen Hemminger 	case IP_MINTTL:
1719d218d111SStephen Hemminger 		val = inet->min_ttl;
1720d218d111SStephen Hemminger 		break;
17211da177e4SLinus Torvalds 	default:
17221da177e4SLinus Torvalds 		release_sock(sk);
17231da177e4SLinus Torvalds 		return -ENOPROTOOPT;
17241da177e4SLinus Torvalds 	}
17251da177e4SLinus Torvalds 	release_sock(sk);
17261da177e4SLinus Torvalds 
1727951e07c9SDavid S. Miller 	if (len < sizeof(int) && len > 0 && val >= 0 && val <= 255) {
17281da177e4SLinus Torvalds 		unsigned char ucval = (unsigned char)val;
17291da177e4SLinus Torvalds 		len = 1;
17301da177e4SLinus Torvalds 		if (put_user(len, optlen))
17311da177e4SLinus Torvalds 			return -EFAULT;
17321da177e4SLinus Torvalds 		if (copy_to_user(optval, &ucval, 1))
17331da177e4SLinus Torvalds 			return -EFAULT;
17341da177e4SLinus Torvalds 	} else {
17351da177e4SLinus Torvalds 		len = min_t(unsigned int, sizeof(int), len);
17361da177e4SLinus Torvalds 		if (put_user(len, optlen))
17371da177e4SLinus Torvalds 			return -EFAULT;
17381da177e4SLinus Torvalds 		if (copy_to_user(optval, &val, len))
17391da177e4SLinus Torvalds 			return -EFAULT;
17401da177e4SLinus Torvalds 	}
17411da177e4SLinus Torvalds 	return 0;
174287e9f031SWANG Cong 
174387e9f031SWANG Cong out:
174487e9f031SWANG Cong 	release_sock(sk);
174587e9f031SWANG Cong 	if (needs_rtnl)
174687e9f031SWANG Cong 		rtnl_unlock();
174787e9f031SWANG Cong 	return err;
17481da177e4SLinus Torvalds }
17491da177e4SLinus Torvalds 
17503fdadf7dSDmitry Mishin int ip_getsockopt(struct sock *sk, int level,
17513fdadf7dSDmitry Mishin 		  int optname, char __user *optval, int __user *optlen)
17523fdadf7dSDmitry Mishin {
17533fdadf7dSDmitry Mishin 	int err;
17543fdadf7dSDmitry Mishin 
1755b6238c04SChristoph Hellwig 	err = do_ip_getsockopt(sk, level, optname, optval, optlen);
1756b6238c04SChristoph Hellwig 
175797adaddaSTaehee Yoo #if IS_ENABLED(CONFIG_BPFILTER_UMH)
1758d2ba09c1SAlexei Starovoitov 	if (optname >= BPFILTER_IPT_SO_GET_INFO &&
1759d2ba09c1SAlexei Starovoitov 	    optname < BPFILTER_IPT_GET_MAX)
1760d2ba09c1SAlexei Starovoitov 		err = bpfilter_ip_get_sockopt(sk, optname, optval, optlen);
1761d2ba09c1SAlexei Starovoitov #endif
17623fdadf7dSDmitry Mishin #ifdef CONFIG_NETFILTER
17633fdadf7dSDmitry Mishin 	/* we need to exclude all possible ENOPROTOOPTs except default case */
17646a9fb947SPavel Emelyanov 	if (err == -ENOPROTOOPT && optname != IP_PKTOPTIONS &&
17656a9fb947SPavel Emelyanov 			!ip_mroute_opt(optname)) {
17663fdadf7dSDmitry Mishin 		int len;
17673fdadf7dSDmitry Mishin 
17683fdadf7dSDmitry Mishin 		if (get_user(len, optlen))
17693fdadf7dSDmitry Mishin 			return -EFAULT;
17703fdadf7dSDmitry Mishin 
177101ea306fSPaolo Abeni 		err = nf_getsockopt(sk, PF_INET, optname, optval, &len);
17723fdadf7dSDmitry Mishin 		if (err >= 0)
17733fdadf7dSDmitry Mishin 			err = put_user(len, optlen);
17743fdadf7dSDmitry Mishin 		return err;
17753fdadf7dSDmitry Mishin 	}
17763fdadf7dSDmitry Mishin #endif
17773fdadf7dSDmitry Mishin 	return err;
17783fdadf7dSDmitry Mishin }
17794d52cfbeSEric Dumazet EXPORT_SYMBOL(ip_getsockopt);
1780