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
ip_cmsg_recv_pktinfo(struct msghdr * msg,struct sk_buff * skb)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
ip_cmsg_recv_ttl(struct msghdr * msg,struct sk_buff * skb)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
ip_cmsg_recv_tos(struct msghdr * msg,struct sk_buff * skb)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
ip_cmsg_recv_opts(struct msghdr * msg,struct sk_buff * skb)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
ip_cmsg_recv_retopts(struct net * net,struct msghdr * msg,struct sk_buff * skb)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
ip_cmsg_recv_fragsize(struct msghdr * msg,struct sk_buff * skb)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
ip_cmsg_recv_checksum(struct msghdr * msg,struct sk_buff * skb,int tlen,int offset)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
ip_cmsg_recv_security(struct msghdr * msg,struct sk_buff * skb)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
ip_cmsg_recv_dstaddr(struct msghdr * msg,struct sk_buff * skb)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
ip_cmsg_recv_offset(struct msghdr * msg,struct sock * sk,struct sk_buff * skb,int tlen,int offset)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
ip_cmsg_send(struct sock * sk,struct msghdr * msg,struct ipcm_cookie * ipc,bool allow_ipv6)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
ip_ra_destroy_rcu(struct rcu_head * head)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
ip_ra_control(struct sock * sk,unsigned char on,void (* destructor)(struct sock *))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
ipv4_icmp_error_rfc4884(const struct sk_buff * skb,struct sock_ee_data_rfc4884 * out)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
ip_icmp_error(struct sock * sk,struct sk_buff * skb,int err,__be16 port,u32 info,u8 * payload)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
ip_local_error(struct sock * sk,int err,__be32 daddr,__be16 port,u32 info)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 */
ipv4_datagram_support_addr(struct sock_exterr_skb * serr)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 */
ipv4_datagram_support_cmsg(const struct sock * sk,struct sk_buff * skb,int ee_origin)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 */
ip_recv_error(struct sock * sk,struct msghdr * msg,int len,int * addr_len)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
__ip_sock_set_tos(struct sock * sk,int val)5884f47d5d5SPoorva Sonparote void __ip_sock_set_tos(struct sock *sk, int val)
5896ebf71baSChristoph Hellwig {
5906ebf71baSChristoph Hellwig if (sk->sk_type == SOCK_STREAM) {
5916ebf71baSChristoph Hellwig val &= ~INET_ECN_MASK;
5926ebf71baSChristoph Hellwig val |= inet_sk(sk)->tos & INET_ECN_MASK;
5936ebf71baSChristoph Hellwig }
5946ebf71baSChristoph Hellwig if (inet_sk(sk)->tos != val) {
5956ebf71baSChristoph Hellwig inet_sk(sk)->tos = val;
5968bf43be7SEric Dumazet WRITE_ONCE(sk->sk_priority, rt_tos2priority(val));
5976ebf71baSChristoph Hellwig sk_dst_reset(sk);
5986ebf71baSChristoph Hellwig }
5996ebf71baSChristoph Hellwig }
6006ebf71baSChristoph Hellwig
ip_sock_set_tos(struct sock * sk,int val)6016ebf71baSChristoph Hellwig void ip_sock_set_tos(struct sock *sk, int val)
6026ebf71baSChristoph Hellwig {
6036ebf71baSChristoph Hellwig lock_sock(sk);
6046ebf71baSChristoph Hellwig __ip_sock_set_tos(sk, val);
6056ebf71baSChristoph Hellwig release_sock(sk);
6066ebf71baSChristoph Hellwig }
6076ebf71baSChristoph Hellwig EXPORT_SYMBOL(ip_sock_set_tos);
6081da177e4SLinus Torvalds
ip_sock_set_freebind(struct sock * sk)609c4e446bfSChristoph Hellwig void ip_sock_set_freebind(struct sock *sk)
610c4e446bfSChristoph Hellwig {
6113f7e7532SEric Dumazet inet_set_bit(FREEBIND, sk);
612c4e446bfSChristoph Hellwig }
613c4e446bfSChristoph Hellwig EXPORT_SYMBOL(ip_sock_set_freebind);
614c4e446bfSChristoph Hellwig
ip_sock_set_recverr(struct sock * sk)615db45c0efSChristoph Hellwig void ip_sock_set_recverr(struct sock *sk)
616db45c0efSChristoph Hellwig {
6176b5f43eaSEric Dumazet inet_set_bit(RECVERR, sk);
618db45c0efSChristoph Hellwig }
619db45c0efSChristoph Hellwig EXPORT_SYMBOL(ip_sock_set_recverr);
620db45c0efSChristoph Hellwig
ip_sock_set_mtu_discover(struct sock * sk,int val)6212de569bdSChristoph Hellwig int ip_sock_set_mtu_discover(struct sock *sk, int val)
6222de569bdSChristoph Hellwig {
6232de569bdSChristoph Hellwig if (val < IP_PMTUDISC_DONT || val > IP_PMTUDISC_OMIT)
6242de569bdSChristoph Hellwig return -EINVAL;
6252de569bdSChristoph Hellwig lock_sock(sk);
6262de569bdSChristoph Hellwig inet_sk(sk)->pmtudisc = val;
6272de569bdSChristoph Hellwig release_sock(sk);
6282de569bdSChristoph Hellwig return 0;
6292de569bdSChristoph Hellwig }
6302de569bdSChristoph Hellwig EXPORT_SYMBOL(ip_sock_set_mtu_discover);
6312de569bdSChristoph Hellwig
ip_sock_set_pktinfo(struct sock * sk)632c1f9ec57SChristoph Hellwig void ip_sock_set_pktinfo(struct sock *sk)
633c1f9ec57SChristoph Hellwig {
634c274af22SEric Dumazet inet_set_bit(PKTINFO, sk);
635c1f9ec57SChristoph Hellwig }
636c1f9ec57SChristoph Hellwig EXPORT_SYMBOL(ip_sock_set_pktinfo);
637c1f9ec57SChristoph Hellwig
6381da177e4SLinus Torvalds /*
6394d52cfbeSEric Dumazet * Socket option code for IP. This is the end of the line after any
6404d52cfbeSEric Dumazet * TCP,UDP etc options on an IP socket.
6411da177e4SLinus Torvalds */
setsockopt_needs_rtnl(int optname)642baf606d9SMarcelo Ricardo Leitner static bool setsockopt_needs_rtnl(int optname)
643baf606d9SMarcelo Ricardo Leitner {
644baf606d9SMarcelo Ricardo Leitner switch (optname) {
645baf606d9SMarcelo Ricardo Leitner case IP_ADD_MEMBERSHIP:
646baf606d9SMarcelo Ricardo Leitner case IP_ADD_SOURCE_MEMBERSHIP:
64754ff9ef3SMarcelo Ricardo Leitner case IP_BLOCK_SOURCE:
648baf606d9SMarcelo Ricardo Leitner case IP_DROP_MEMBERSHIP:
64954ff9ef3SMarcelo Ricardo Leitner case IP_DROP_SOURCE_MEMBERSHIP:
65054ff9ef3SMarcelo Ricardo Leitner case IP_MSFILTER:
65154ff9ef3SMarcelo Ricardo Leitner case IP_UNBLOCK_SOURCE:
65254ff9ef3SMarcelo Ricardo Leitner case MCAST_BLOCK_SOURCE:
65354ff9ef3SMarcelo Ricardo Leitner case MCAST_MSFILTER:
654baf606d9SMarcelo Ricardo Leitner case MCAST_JOIN_GROUP:
65554ff9ef3SMarcelo Ricardo Leitner case MCAST_JOIN_SOURCE_GROUP:
656baf606d9SMarcelo Ricardo Leitner case MCAST_LEAVE_GROUP:
65754ff9ef3SMarcelo Ricardo Leitner case MCAST_LEAVE_SOURCE_GROUP:
65854ff9ef3SMarcelo Ricardo Leitner case MCAST_UNBLOCK_SOURCE:
659baf606d9SMarcelo Ricardo Leitner return true;
660baf606d9SMarcelo Ricardo Leitner }
661baf606d9SMarcelo Ricardo Leitner return false;
662baf606d9SMarcelo Ricardo Leitner }
6631da177e4SLinus Torvalds
set_mcast_msfilter(struct sock * sk,int ifindex,int numsrc,int fmode,struct sockaddr_storage * group,struct sockaddr_storage * list)664e986d4daSAl Viro static int set_mcast_msfilter(struct sock *sk, int ifindex,
665e986d4daSAl Viro int numsrc, int fmode,
666e986d4daSAl Viro struct sockaddr_storage *group,
667e986d4daSAl Viro struct sockaddr_storage *list)
668e986d4daSAl Viro {
669e986d4daSAl Viro struct ip_msfilter *msf;
670e986d4daSAl Viro struct sockaddr_in *psin;
671e986d4daSAl Viro int err, i;
672e986d4daSAl Viro
6734167a960SGustavo A. R. Silva msf = kmalloc(IP_MSFILTER_SIZE(numsrc), GFP_KERNEL);
674e986d4daSAl Viro if (!msf)
675e986d4daSAl Viro return -ENOBUFS;
676e986d4daSAl Viro
677e986d4daSAl Viro psin = (struct sockaddr_in *)group;
678e986d4daSAl Viro if (psin->sin_family != AF_INET)
679e986d4daSAl Viro goto Eaddrnotavail;
680e986d4daSAl Viro msf->imsf_multiaddr = psin->sin_addr.s_addr;
681e986d4daSAl Viro msf->imsf_interface = 0;
682e986d4daSAl Viro msf->imsf_fmode = fmode;
683e986d4daSAl Viro msf->imsf_numsrc = numsrc;
684e986d4daSAl Viro for (i = 0; i < numsrc; ++i) {
685e986d4daSAl Viro psin = (struct sockaddr_in *)&list[i];
686e986d4daSAl Viro
687e986d4daSAl Viro if (psin->sin_family != AF_INET)
688e986d4daSAl Viro goto Eaddrnotavail;
6892d3e5cafSGustavo A. R. Silva msf->imsf_slist_flex[i] = psin->sin_addr.s_addr;
690e986d4daSAl Viro }
691e986d4daSAl Viro err = ip_mc_msfilter(sk, msf, ifindex);
692e986d4daSAl Viro kfree(msf);
693e986d4daSAl Viro return err;
694e986d4daSAl Viro
695e986d4daSAl Viro Eaddrnotavail:
696e986d4daSAl Viro kfree(msf);
697e986d4daSAl Viro return -EADDRNOTAVAIL;
698e986d4daSAl Viro }
699e986d4daSAl Viro
copy_group_source_from_sockptr(struct group_source_req * greqs,sockptr_t optval,int optlen)70089654c5fSChristoph Hellwig static int copy_group_source_from_sockptr(struct group_source_req *greqs,
70189654c5fSChristoph Hellwig sockptr_t optval, int optlen)
7022bbf8c1eSAl Viro {
703b6238c04SChristoph Hellwig if (in_compat_syscall()) {
704b6238c04SChristoph Hellwig struct compat_group_source_req gr32;
705b6238c04SChristoph Hellwig
706b6238c04SChristoph Hellwig if (optlen != sizeof(gr32))
707b6238c04SChristoph Hellwig return -EINVAL;
70889654c5fSChristoph Hellwig if (copy_from_sockptr(&gr32, optval, sizeof(gr32)))
709b6238c04SChristoph Hellwig return -EFAULT;
710b6238c04SChristoph Hellwig greqs->gsr_interface = gr32.gsr_interface;
711b6238c04SChristoph Hellwig greqs->gsr_group = gr32.gsr_group;
712b6238c04SChristoph Hellwig greqs->gsr_source = gr32.gsr_source;
713b6238c04SChristoph Hellwig } else {
714b6238c04SChristoph Hellwig if (optlen != sizeof(*greqs))
715b6238c04SChristoph Hellwig return -EINVAL;
71689654c5fSChristoph Hellwig if (copy_from_sockptr(greqs, optval, sizeof(*greqs)))
717b6238c04SChristoph Hellwig return -EFAULT;
718b6238c04SChristoph Hellwig }
719b6238c04SChristoph Hellwig
720b6238c04SChristoph Hellwig return 0;
721b6238c04SChristoph Hellwig }
722b6238c04SChristoph Hellwig
do_mcast_group_source(struct sock * sk,int optname,sockptr_t optval,int optlen)723b6238c04SChristoph Hellwig static int do_mcast_group_source(struct sock *sk, int optname,
72489654c5fSChristoph Hellwig sockptr_t optval, int optlen)
725b6238c04SChristoph Hellwig {
726b6238c04SChristoph Hellwig struct group_source_req greqs;
7272bbf8c1eSAl Viro struct ip_mreq_source mreqs;
7282bbf8c1eSAl Viro struct sockaddr_in *psin;
7292bbf8c1eSAl Viro int omode, add, err;
7302bbf8c1eSAl Viro
73189654c5fSChristoph Hellwig err = copy_group_source_from_sockptr(&greqs, optval, optlen);
732b6238c04SChristoph Hellwig if (err)
733b6238c04SChristoph Hellwig return err;
734b6238c04SChristoph Hellwig
735b6238c04SChristoph Hellwig if (greqs.gsr_group.ss_family != AF_INET ||
736b6238c04SChristoph Hellwig greqs.gsr_source.ss_family != AF_INET)
7372bbf8c1eSAl Viro return -EADDRNOTAVAIL;
7382bbf8c1eSAl Viro
739b6238c04SChristoph Hellwig psin = (struct sockaddr_in *)&greqs.gsr_group;
7402bbf8c1eSAl Viro mreqs.imr_multiaddr = psin->sin_addr.s_addr;
741b6238c04SChristoph Hellwig psin = (struct sockaddr_in *)&greqs.gsr_source;
7422bbf8c1eSAl Viro mreqs.imr_sourceaddr = psin->sin_addr.s_addr;
7432bbf8c1eSAl Viro mreqs.imr_interface = 0; /* use index for mc_source */
7442bbf8c1eSAl Viro
7452bbf8c1eSAl Viro if (optname == MCAST_BLOCK_SOURCE) {
7462bbf8c1eSAl Viro omode = MCAST_EXCLUDE;
7472bbf8c1eSAl Viro add = 1;
7482bbf8c1eSAl Viro } else if (optname == MCAST_UNBLOCK_SOURCE) {
7492bbf8c1eSAl Viro omode = MCAST_EXCLUDE;
7502bbf8c1eSAl Viro add = 0;
7512bbf8c1eSAl Viro } else if (optname == MCAST_JOIN_SOURCE_GROUP) {
7522bbf8c1eSAl Viro struct ip_mreqn mreq;
7532bbf8c1eSAl Viro
754b6238c04SChristoph Hellwig psin = (struct sockaddr_in *)&greqs.gsr_group;
7552bbf8c1eSAl Viro mreq.imr_multiaddr = psin->sin_addr;
7562bbf8c1eSAl Viro mreq.imr_address.s_addr = 0;
757b6238c04SChristoph Hellwig mreq.imr_ifindex = greqs.gsr_interface;
7582bbf8c1eSAl Viro err = ip_mc_join_group_ssm(sk, &mreq, MCAST_INCLUDE);
7592bbf8c1eSAl Viro if (err && err != -EADDRINUSE)
7602bbf8c1eSAl Viro return err;
761b6238c04SChristoph Hellwig greqs.gsr_interface = mreq.imr_ifindex;
7622bbf8c1eSAl Viro omode = MCAST_INCLUDE;
7632bbf8c1eSAl Viro add = 1;
7642bbf8c1eSAl Viro } else /* MCAST_LEAVE_SOURCE_GROUP */ {
7652bbf8c1eSAl Viro omode = MCAST_INCLUDE;
7662bbf8c1eSAl Viro add = 0;
7672bbf8c1eSAl Viro }
768b6238c04SChristoph Hellwig return ip_mc_source(add, omode, sk, &mreqs, greqs.gsr_interface);
7692bbf8c1eSAl Viro }
7702bbf8c1eSAl Viro
ip_set_mcast_msfilter(struct sock * sk,sockptr_t optval,int optlen)77189654c5fSChristoph Hellwig static int ip_set_mcast_msfilter(struct sock *sk, sockptr_t optval, int optlen)
772d62c38f6SChristoph Hellwig {
773d62c38f6SChristoph Hellwig struct group_filter *gsf = NULL;
774d62c38f6SChristoph Hellwig int err;
775d62c38f6SChristoph Hellwig
776d62c38f6SChristoph Hellwig if (optlen < GROUP_FILTER_SIZE(0))
777d62c38f6SChristoph Hellwig return -EINVAL;
7787de6d09fSKuniyuki Iwashima if (optlen > READ_ONCE(sysctl_optmem_max))
779d62c38f6SChristoph Hellwig return -ENOBUFS;
780d62c38f6SChristoph Hellwig
78189654c5fSChristoph Hellwig gsf = memdup_sockptr(optval, optlen);
782d62c38f6SChristoph Hellwig if (IS_ERR(gsf))
783d62c38f6SChristoph Hellwig return PTR_ERR(gsf);
784d62c38f6SChristoph Hellwig
785d62c38f6SChristoph Hellwig /* numsrc >= (4G-140)/128 overflow in 32 bits */
786d62c38f6SChristoph Hellwig err = -ENOBUFS;
787d62c38f6SChristoph Hellwig if (gsf->gf_numsrc >= 0x1ffffff ||
7886ae0f2e5SKuniyuki Iwashima gsf->gf_numsrc > READ_ONCE(sock_net(sk)->ipv4.sysctl_igmp_max_msf))
789d62c38f6SChristoph Hellwig goto out_free_gsf;
790d62c38f6SChristoph Hellwig
791d62c38f6SChristoph Hellwig err = -EINVAL;
792d62c38f6SChristoph Hellwig if (GROUP_FILTER_SIZE(gsf->gf_numsrc) > optlen)
793d62c38f6SChristoph Hellwig goto out_free_gsf;
794d62c38f6SChristoph Hellwig
795d62c38f6SChristoph Hellwig err = set_mcast_msfilter(sk, gsf->gf_interface, gsf->gf_numsrc,
796db243b79SGustavo A. R. Silva gsf->gf_fmode, &gsf->gf_group,
797db243b79SGustavo A. R. Silva gsf->gf_slist_flex);
798d62c38f6SChristoph Hellwig out_free_gsf:
799d62c38f6SChristoph Hellwig kfree(gsf);
800d62c38f6SChristoph Hellwig return err;
801d62c38f6SChristoph Hellwig }
802d62c38f6SChristoph Hellwig
compat_ip_set_mcast_msfilter(struct sock * sk,sockptr_t optval,int optlen)80389654c5fSChristoph Hellwig static int compat_ip_set_mcast_msfilter(struct sock *sk, sockptr_t optval,
804d62c38f6SChristoph Hellwig int optlen)
805d62c38f6SChristoph Hellwig {
806db243b79SGustavo A. R. Silva const int size0 = offsetof(struct compat_group_filter, gf_slist_flex);
807d62c38f6SChristoph Hellwig struct compat_group_filter *gf32;
808d62c38f6SChristoph Hellwig unsigned int n;
809d62c38f6SChristoph Hellwig void *p;
810d62c38f6SChristoph Hellwig int err;
811d62c38f6SChristoph Hellwig
812d62c38f6SChristoph Hellwig if (optlen < size0)
813d62c38f6SChristoph Hellwig return -EINVAL;
8147de6d09fSKuniyuki Iwashima if (optlen > READ_ONCE(sysctl_optmem_max) - 4)
815d62c38f6SChristoph Hellwig return -ENOBUFS;
816d62c38f6SChristoph Hellwig
817d62c38f6SChristoph Hellwig p = kmalloc(optlen + 4, GFP_KERNEL);
818d62c38f6SChristoph Hellwig if (!p)
819d62c38f6SChristoph Hellwig return -ENOMEM;
820db243b79SGustavo A. R. Silva gf32 = p + 4; /* we want ->gf_group and ->gf_slist_flex aligned */
821d62c38f6SChristoph Hellwig
822d62c38f6SChristoph Hellwig err = -EFAULT;
82389654c5fSChristoph Hellwig if (copy_from_sockptr(gf32, optval, optlen))
824d62c38f6SChristoph Hellwig goto out_free_gsf;
825d62c38f6SChristoph Hellwig
826d62c38f6SChristoph Hellwig /* numsrc >= (4G-140)/128 overflow in 32 bits */
827d62c38f6SChristoph Hellwig n = gf32->gf_numsrc;
828d62c38f6SChristoph Hellwig err = -ENOBUFS;
829d62c38f6SChristoph Hellwig if (n >= 0x1ffffff)
830d62c38f6SChristoph Hellwig goto out_free_gsf;
831d62c38f6SChristoph Hellwig
832d62c38f6SChristoph Hellwig err = -EINVAL;
833db243b79SGustavo A. R. Silva if (offsetof(struct compat_group_filter, gf_slist_flex[n]) > optlen)
834d62c38f6SChristoph Hellwig goto out_free_gsf;
835d62c38f6SChristoph Hellwig
836d62c38f6SChristoph Hellwig /* numsrc >= (4G-140)/128 overflow in 32 bits */
837d62c38f6SChristoph Hellwig err = -ENOBUFS;
8386ae0f2e5SKuniyuki Iwashima if (n > READ_ONCE(sock_net(sk)->ipv4.sysctl_igmp_max_msf))
839b6238c04SChristoph Hellwig goto out_free_gsf;
840d62c38f6SChristoph Hellwig err = set_mcast_msfilter(sk, gf32->gf_interface, n, gf32->gf_fmode,
841db243b79SGustavo A. R. Silva &gf32->gf_group, gf32->gf_slist_flex);
842d62c38f6SChristoph Hellwig out_free_gsf:
843d62c38f6SChristoph Hellwig kfree(p);
844d62c38f6SChristoph Hellwig return err;
845d62c38f6SChristoph Hellwig }
846d62c38f6SChristoph Hellwig
ip_mcast_join_leave(struct sock * sk,int optname,sockptr_t optval,int optlen)84702caad7cSChristoph Hellwig static int ip_mcast_join_leave(struct sock *sk, int optname,
84889654c5fSChristoph Hellwig sockptr_t optval, int optlen)
84902caad7cSChristoph Hellwig {
85002caad7cSChristoph Hellwig struct ip_mreqn mreq = { };
85102caad7cSChristoph Hellwig struct sockaddr_in *psin;
85202caad7cSChristoph Hellwig struct group_req greq;
85302caad7cSChristoph Hellwig
85402caad7cSChristoph Hellwig if (optlen < sizeof(struct group_req))
85502caad7cSChristoph Hellwig return -EINVAL;
85689654c5fSChristoph Hellwig if (copy_from_sockptr(&greq, optval, sizeof(greq)))
85702caad7cSChristoph Hellwig return -EFAULT;
85802caad7cSChristoph Hellwig
85902caad7cSChristoph Hellwig psin = (struct sockaddr_in *)&greq.gr_group;
86002caad7cSChristoph Hellwig if (psin->sin_family != AF_INET)
86102caad7cSChristoph Hellwig return -EINVAL;
86202caad7cSChristoph Hellwig mreq.imr_multiaddr = psin->sin_addr;
86302caad7cSChristoph Hellwig mreq.imr_ifindex = greq.gr_interface;
86402caad7cSChristoph Hellwig if (optname == MCAST_JOIN_GROUP)
86502caad7cSChristoph Hellwig return ip_mc_join_group(sk, &mreq);
86602caad7cSChristoph Hellwig return ip_mc_leave_group(sk, &mreq);
86702caad7cSChristoph Hellwig }
86802caad7cSChristoph Hellwig
compat_ip_mcast_join_leave(struct sock * sk,int optname,sockptr_t optval,int optlen)86902caad7cSChristoph Hellwig static int compat_ip_mcast_join_leave(struct sock *sk, int optname,
87089654c5fSChristoph Hellwig sockptr_t optval, int optlen)
87102caad7cSChristoph Hellwig {
87202caad7cSChristoph Hellwig struct compat_group_req greq;
87302caad7cSChristoph Hellwig struct ip_mreqn mreq = { };
87402caad7cSChristoph Hellwig struct sockaddr_in *psin;
87502caad7cSChristoph Hellwig
87602caad7cSChristoph Hellwig if (optlen < sizeof(struct compat_group_req))
87702caad7cSChristoph Hellwig return -EINVAL;
87889654c5fSChristoph Hellwig if (copy_from_sockptr(&greq, optval, sizeof(greq)))
87902caad7cSChristoph Hellwig return -EFAULT;
88002caad7cSChristoph Hellwig
88102caad7cSChristoph Hellwig psin = (struct sockaddr_in *)&greq.gr_group;
88202caad7cSChristoph Hellwig if (psin->sin_family != AF_INET)
88302caad7cSChristoph Hellwig return -EINVAL;
88402caad7cSChristoph Hellwig mreq.imr_multiaddr = psin->sin_addr;
88502caad7cSChristoph Hellwig mreq.imr_ifindex = greq.gr_interface;
88602caad7cSChristoph Hellwig
88702caad7cSChristoph Hellwig if (optname == MCAST_JOIN_GROUP)
888b6238c04SChristoph Hellwig return ip_mc_join_group(sk, &mreq);
889b6238c04SChristoph Hellwig return ip_mc_leave_group(sk, &mreq);
89002caad7cSChristoph Hellwig }
89102caad7cSChristoph Hellwig
892020e71a3SEric Dumazet DEFINE_STATIC_KEY_FALSE(ip4_min_ttl);
893020e71a3SEric Dumazet
do_ip_setsockopt(struct sock * sk,int level,int optname,sockptr_t optval,unsigned int optlen)894ee7f1e13SMartin KaFai Lau int do_ip_setsockopt(struct sock *sk, int level, int optname,
89589654c5fSChristoph Hellwig sockptr_t optval, unsigned int optlen)
8961da177e4SLinus Torvalds {
8971da177e4SLinus Torvalds struct inet_sock *inet = inet_sk(sk);
898166b6b2dSNikolay Borisov struct net *net = sock_net(sk);
8991da177e4SLinus Torvalds int val = 0, err;
900baf606d9SMarcelo Ricardo Leitner bool needs_rtnl = setsockopt_needs_rtnl(optname);
9011da177e4SLinus Torvalds
9020c9f79beSXi Wang switch (optname) {
9030c9f79beSXi Wang case IP_PKTINFO:
9040c9f79beSXi Wang case IP_RECVTTL:
9050c9f79beSXi Wang case IP_RECVOPTS:
9060c9f79beSXi Wang case IP_RECVTOS:
9070c9f79beSXi Wang case IP_RETOPTS:
9080c9f79beSXi Wang case IP_TOS:
9090c9f79beSXi Wang case IP_TTL:
9100c9f79beSXi Wang case IP_HDRINCL:
9110c9f79beSXi Wang case IP_MTU_DISCOVER:
9120c9f79beSXi Wang case IP_RECVERR:
9130c9f79beSXi Wang case IP_ROUTER_ALERT:
9140c9f79beSXi Wang case IP_FREEBIND:
9150c9f79beSXi Wang case IP_PASSSEC:
9160c9f79beSXi Wang case IP_TRANSPARENT:
9170c9f79beSXi Wang case IP_MINTTL:
9180c9f79beSXi Wang case IP_NODEFRAG:
91990c337daSEric Dumazet case IP_BIND_ADDRESS_NO_PORT:
9200c9f79beSXi Wang case IP_UNICAST_IF:
9210c9f79beSXi Wang case IP_MULTICAST_TTL:
9220c9f79beSXi Wang case IP_MULTICAST_ALL:
9230c9f79beSXi Wang case IP_MULTICAST_LOOP:
9240c9f79beSXi Wang case IP_RECVORIGDSTADDR:
925ad6f939aSTom Herbert case IP_CHECKSUM:
92670ecc248SWillem de Bruijn case IP_RECVFRAGSIZE:
927eba75c58SWillem de Bruijn case IP_RECVERR_RFC4884:
92891d0b78cSJakub Sitnicki case IP_LOCAL_PORT_RANGE:
9291da177e4SLinus Torvalds if (optlen >= sizeof(int)) {
93089654c5fSChristoph Hellwig if (copy_from_sockptr(&val, optval, sizeof(val)))
9311da177e4SLinus Torvalds return -EFAULT;
9321da177e4SLinus Torvalds } else if (optlen >= sizeof(char)) {
9331da177e4SLinus Torvalds unsigned char ucval;
9341da177e4SLinus Torvalds
93589654c5fSChristoph Hellwig if (copy_from_sockptr(&ucval, optval, sizeof(ucval)))
9361da177e4SLinus Torvalds return -EFAULT;
9371da177e4SLinus Torvalds val = (int) ucval;
9381da177e4SLinus Torvalds }
9391da177e4SLinus Torvalds }
9401da177e4SLinus Torvalds
9411da177e4SLinus Torvalds /* If optlen==0, it is equivalent to val == 0 */
9421da177e4SLinus Torvalds
9430526947fSKirill Tkhai if (optname == IP_ROUTER_ALERT)
9440526947fSKirill Tkhai return ip_ra_control(sk, val ? 1 : 0, NULL);
9456a9fb947SPavel Emelyanov if (ip_mroute_opt(optname))
94689654c5fSChristoph Hellwig return ip_mroute_setsockopt(sk, optname, optval, optlen);
9471da177e4SLinus Torvalds
948b4d84bceSEric Dumazet /* Handle options that can be set without locking the socket. */
949b4d84bceSEric Dumazet switch (optname) {
950b4d84bceSEric Dumazet case IP_PKTINFO:
951b4d84bceSEric Dumazet inet_assign_bit(PKTINFO, sk, val);
952b4d84bceSEric Dumazet return 0;
953b4d84bceSEric Dumazet case IP_RECVTTL:
954b4d84bceSEric Dumazet inet_assign_bit(TTL, sk, val);
955b4d84bceSEric Dumazet return 0;
956b4d84bceSEric Dumazet case IP_RECVTOS:
957b4d84bceSEric Dumazet inet_assign_bit(TOS, sk, val);
958b4d84bceSEric Dumazet return 0;
959b4d84bceSEric Dumazet case IP_RECVOPTS:
960b4d84bceSEric Dumazet inet_assign_bit(RECVOPTS, sk, val);
961b4d84bceSEric Dumazet return 0;
962b4d84bceSEric Dumazet case IP_RETOPTS:
963b4d84bceSEric Dumazet inet_assign_bit(RETOPTS, sk, val);
964b4d84bceSEric Dumazet return 0;
965b4d84bceSEric Dumazet case IP_PASSSEC:
966b4d84bceSEric Dumazet inet_assign_bit(PASSSEC, sk, val);
967b4d84bceSEric Dumazet return 0;
968b4d84bceSEric Dumazet case IP_RECVORIGDSTADDR:
969b4d84bceSEric Dumazet inet_assign_bit(ORIGDSTADDR, sk, val);
970b4d84bceSEric Dumazet return 0;
971b4d84bceSEric Dumazet case IP_RECVFRAGSIZE:
972b4d84bceSEric Dumazet if (sk->sk_type != SOCK_RAW && sk->sk_type != SOCK_DGRAM)
973b4d84bceSEric Dumazet return -EINVAL;
974b4d84bceSEric Dumazet inet_assign_bit(RECVFRAGSIZE, sk, val);
975b4d84bceSEric Dumazet return 0;
9766b5f43eaSEric Dumazet case IP_RECVERR:
9776b5f43eaSEric Dumazet inet_assign_bit(RECVERR, sk, val);
9786b5f43eaSEric Dumazet if (!val)
9790f158b32SEric Dumazet skb_errqueue_purge(&sk->sk_error_queue);
9806b5f43eaSEric Dumazet return 0;
9818e8cfb11SEric Dumazet case IP_RECVERR_RFC4884:
9828e8cfb11SEric Dumazet if (val < 0 || val > 1)
9838e8cfb11SEric Dumazet return -EINVAL;
9848e8cfb11SEric Dumazet inet_assign_bit(RECVERR_RFC4884, sk, val);
9858e8cfb11SEric Dumazet return 0;
9863f7e7532SEric Dumazet case IP_FREEBIND:
9873f7e7532SEric Dumazet if (optlen < 1)
9883f7e7532SEric Dumazet return -EINVAL;
9893f7e7532SEric Dumazet inet_assign_bit(FREEBIND, sk, val);
9903f7e7532SEric Dumazet return 0;
991cafbe182SEric Dumazet case IP_HDRINCL:
992cafbe182SEric Dumazet if (sk->sk_type != SOCK_RAW)
993cafbe182SEric Dumazet return -ENOPROTOOPT;
994cafbe182SEric Dumazet inet_assign_bit(HDRINCL, sk, val);
995cafbe182SEric Dumazet return 0;
996b09bde5cSEric Dumazet case IP_MULTICAST_LOOP:
997b09bde5cSEric Dumazet if (optlen < 1)
998b09bde5cSEric Dumazet return -EINVAL;
999b09bde5cSEric Dumazet inet_assign_bit(MC_LOOP, sk, val);
1000b09bde5cSEric Dumazet return 0;
1001307b4ac6SEric Dumazet case IP_MULTICAST_ALL:
1002307b4ac6SEric Dumazet if (optlen < 1)
1003307b4ac6SEric Dumazet return -EINVAL;
1004307b4ac6SEric Dumazet if (val != 0 && val != 1)
1005307b4ac6SEric Dumazet return -EINVAL;
1006307b4ac6SEric Dumazet inet_assign_bit(MC_ALL, sk, val);
1007307b4ac6SEric Dumazet return 0;
10084bd0623fSEric Dumazet case IP_TRANSPARENT:
10094bd0623fSEric Dumazet if (!!val && !sockopt_ns_capable(sock_net(sk)->user_ns, CAP_NET_RAW) &&
10108be6f88bSEric Dumazet !sockopt_ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN))
10118be6f88bSEric Dumazet return -EPERM;
10124bd0623fSEric Dumazet if (optlen < 1)
10138be6f88bSEric Dumazet return -EINVAL;
10144bd0623fSEric Dumazet inet_assign_bit(TRANSPARENT, sk, val);
10154bd0623fSEric Dumazet return 0;
1016f04b8d34SEric Dumazet case IP_NODEFRAG:
1017f04b8d34SEric Dumazet if (sk->sk_type != SOCK_RAW)
1018f04b8d34SEric Dumazet return -ENOPROTOOPT;
1019f04b8d34SEric Dumazet inet_assign_bit(NODEFRAG, sk, val);
1020f04b8d34SEric Dumazet return 0;
1021ca571e2eSEric Dumazet case IP_BIND_ADDRESS_NO_PORT:
1022ca571e2eSEric Dumazet inet_assign_bit(BIND_ADDRESS_NO_PORT, sk, val);
1023ca571e2eSEric Dumazet return 0;
102410f42426SEric Dumazet case IP_TTL:
102510f42426SEric Dumazet if (optlen < 1)
102610f42426SEric Dumazet return -EINVAL;
102710f42426SEric Dumazet if (val != -1 && (val < 1 || val > 255))
102810f42426SEric Dumazet return -EINVAL;
102910f42426SEric Dumazet WRITE_ONCE(inet->uc_ttl, val);
103010f42426SEric Dumazet return 0;
103112af7326SEric Dumazet case IP_MINTTL:
103212af7326SEric Dumazet if (optlen < 1)
103312af7326SEric Dumazet return -EINVAL;
103412af7326SEric Dumazet if (val < 0 || val > 255)
103512af7326SEric Dumazet return -EINVAL;
103612af7326SEric Dumazet
103712af7326SEric Dumazet if (val)
103812af7326SEric Dumazet static_branch_enable(&ip4_min_ttl);
103912af7326SEric Dumazet
104012af7326SEric Dumazet WRITE_ONCE(inet->min_ttl, val);
104112af7326SEric Dumazet return 0;
1042b4d84bceSEric Dumazet }
1043b4d84bceSEric Dumazet
10441da177e4SLinus Torvalds err = 0;
1045baf606d9SMarcelo Ricardo Leitner if (needs_rtnl)
1046baf606d9SMarcelo Ricardo Leitner rtnl_lock();
10471df055d3SMartin KaFai Lau sockopt_lock_sock(sk);
10481da177e4SLinus Torvalds
10491da177e4SLinus Torvalds switch (optname) {
10501da177e4SLinus Torvalds case IP_OPTIONS:
10511da177e4SLinus Torvalds {
1052f6d8bd05SEric Dumazet struct ip_options_rcu *old, *opt = NULL;
1053f6d8bd05SEric Dumazet
105465a1c4ffSroel kluin if (optlen > 40)
10551da177e4SLinus Torvalds goto e_inval;
105689654c5fSChristoph Hellwig err = ip_options_get(sock_net(sk), &opt, optval, optlen);
10571da177e4SLinus Torvalds if (err)
10581da177e4SLinus Torvalds break;
1059f6d8bd05SEric Dumazet old = rcu_dereference_protected(inet->inet_opt,
10601e1d04e6SHannes Frederic Sowa lockdep_sock_is_held(sk));
1061b1c0356aSEric Dumazet if (inet_test_bit(IS_ICSK, sk)) {
1062d83d8461SArnaldo Carvalho de Melo struct inet_connection_sock *icsk = inet_csk(sk);
1063dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6)
10641da177e4SLinus Torvalds if (sk->sk_family == PF_INET ||
10651da177e4SLinus Torvalds (!((1 << sk->sk_state) &
10661da177e4SLinus Torvalds (TCPF_LISTEN | TCPF_CLOSE)) &&
1067c720c7e8SEric Dumazet inet->inet_daddr != LOOPBACK4_IPV6)) {
10681da177e4SLinus Torvalds #endif
1069f6d8bd05SEric Dumazet if (old)
1070f6d8bd05SEric Dumazet icsk->icsk_ext_hdr_len -= old->opt.optlen;
10711da177e4SLinus Torvalds if (opt)
1072f6d8bd05SEric Dumazet icsk->icsk_ext_hdr_len += opt->opt.optlen;
1073d83d8461SArnaldo Carvalho de Melo icsk->icsk_sync_mss(sk, icsk->icsk_pmtu_cookie);
1074dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6)
10751da177e4SLinus Torvalds }
10761da177e4SLinus Torvalds #endif
10771da177e4SLinus Torvalds }
1078f6d8bd05SEric Dumazet rcu_assign_pointer(inet->inet_opt, opt);
1079f6d8bd05SEric Dumazet if (old)
1080605b4afeSPaul E. McKenney kfree_rcu(old, rcu);
10811da177e4SLinus Torvalds break;
10821da177e4SLinus Torvalds }
1083ad6f939aSTom Herbert case IP_CHECKSUM:
1084ad6f939aSTom Herbert if (val) {
1085c274af22SEric Dumazet if (!(inet_test_bit(CHECKSUM, sk))) {
1086ad6f939aSTom Herbert inet_inc_convert_csum(sk);
1087c274af22SEric Dumazet inet_set_bit(CHECKSUM, sk);
1088ad6f939aSTom Herbert }
1089ad6f939aSTom Herbert } else {
1090c274af22SEric Dumazet if (inet_test_bit(CHECKSUM, sk)) {
1091ad6f939aSTom Herbert inet_dec_convert_csum(sk);
1092c274af22SEric Dumazet inet_clear_bit(CHECKSUM, sk);
1093ad6f939aSTom Herbert }
1094ad6f939aSTom Herbert }
1095ad6f939aSTom Herbert break;
10961da177e4SLinus Torvalds case IP_TOS: /* This sets both TOS and Precedence */
10976ebf71baSChristoph Hellwig __ip_sock_set_tos(sk, val);
10981da177e4SLinus Torvalds break;
10991da177e4SLinus Torvalds case IP_MTU_DISCOVER:
11001b346576SHannes Frederic Sowa if (val < IP_PMTUDISC_DONT || val > IP_PMTUDISC_OMIT)
11011da177e4SLinus Torvalds goto e_inval;
11021da177e4SLinus Torvalds inet->pmtudisc = val;
11031da177e4SLinus Torvalds break;
11041da177e4SLinus Torvalds case IP_MULTICAST_TTL:
11051da177e4SLinus Torvalds if (sk->sk_type == SOCK_STREAM)
11061da177e4SLinus Torvalds goto e_inval;
11071da177e4SLinus Torvalds if (optlen < 1)
11081da177e4SLinus Torvalds goto e_inval;
11091da177e4SLinus Torvalds if (val == -1)
11101da177e4SLinus Torvalds val = 1;
11111da177e4SLinus Torvalds if (val < 0 || val > 255)
11121da177e4SLinus Torvalds goto e_inval;
11131da177e4SLinus Torvalds inet->mc_ttl = val;
11141da177e4SLinus Torvalds break;
111576e21053SErich E. Hoover case IP_UNICAST_IF:
111676e21053SErich E. Hoover {
111776e21053SErich E. Hoover struct net_device *dev = NULL;
111876e21053SErich E. Hoover int ifindex;
11199515a2e0SDavid Ahern int midx;
112076e21053SErich E. Hoover
112176e21053SErich E. Hoover if (optlen != sizeof(int))
112276e21053SErich E. Hoover goto e_inval;
112376e21053SErich E. Hoover
112476e21053SErich E. Hoover ifindex = (__force int)ntohl((__force __be32)val);
112576e21053SErich E. Hoover if (ifindex == 0) {
112676e21053SErich E. Hoover inet->uc_index = 0;
112776e21053SErich E. Hoover err = 0;
112876e21053SErich E. Hoover break;
112976e21053SErich E. Hoover }
113076e21053SErich E. Hoover
113176e21053SErich E. Hoover dev = dev_get_by_index(sock_net(sk), ifindex);
113276e21053SErich E. Hoover err = -EADDRNOTAVAIL;
113376e21053SErich E. Hoover if (!dev)
113476e21053SErich E. Hoover break;
11359515a2e0SDavid Ahern
11369515a2e0SDavid Ahern midx = l3mdev_master_ifindex(dev);
113776e21053SErich E. Hoover dev_put(dev);
113876e21053SErich E. Hoover
113976e21053SErich E. Hoover err = -EINVAL;
1140fdf1923bSMiaohe Lin if (sk->sk_bound_dev_if && midx != sk->sk_bound_dev_if)
114176e21053SErich E. Hoover break;
114276e21053SErich E. Hoover
114376e21053SErich E. Hoover inet->uc_index = ifindex;
114476e21053SErich E. Hoover err = 0;
114576e21053SErich E. Hoover break;
114676e21053SErich E. Hoover }
11471da177e4SLinus Torvalds case IP_MULTICAST_IF:
11481da177e4SLinus Torvalds {
11491da177e4SLinus Torvalds struct ip_mreqn mreq;
11501da177e4SLinus Torvalds struct net_device *dev = NULL;
11517bb387c5SDavid Ahern int midx;
11521da177e4SLinus Torvalds
11531da177e4SLinus Torvalds if (sk->sk_type == SOCK_STREAM)
11541da177e4SLinus Torvalds goto e_inval;
11551da177e4SLinus Torvalds /*
11561da177e4SLinus Torvalds * Check the arguments are allowable
11571da177e4SLinus Torvalds */
11581da177e4SLinus Torvalds
11590915921bSShan Wei if (optlen < sizeof(struct in_addr))
11600915921bSShan Wei goto e_inval;
11610915921bSShan Wei
11621da177e4SLinus Torvalds err = -EFAULT;
11631da177e4SLinus Torvalds if (optlen >= sizeof(struct ip_mreqn)) {
116489654c5fSChristoph Hellwig if (copy_from_sockptr(&mreq, optval, sizeof(mreq)))
11651da177e4SLinus Torvalds break;
11661da177e4SLinus Torvalds } else {
11671da177e4SLinus Torvalds memset(&mreq, 0, sizeof(mreq));
11683a084ddbSJiri Pirko if (optlen >= sizeof(struct ip_mreq)) {
116989654c5fSChristoph Hellwig if (copy_from_sockptr(&mreq, optval,
11703a084ddbSJiri Pirko sizeof(struct ip_mreq)))
11713a084ddbSJiri Pirko break;
11723a084ddbSJiri Pirko } else if (optlen >= sizeof(struct in_addr)) {
117389654c5fSChristoph Hellwig if (copy_from_sockptr(&mreq.imr_address, optval,
11744d52cfbeSEric Dumazet sizeof(struct in_addr)))
11751da177e4SLinus Torvalds break;
11761da177e4SLinus Torvalds }
11773a084ddbSJiri Pirko }
11781da177e4SLinus Torvalds
11791da177e4SLinus Torvalds if (!mreq.imr_ifindex) {
1180e6f1cebfSAl Viro if (mreq.imr_address.s_addr == htonl(INADDR_ANY)) {
11811da177e4SLinus Torvalds inet->mc_index = 0;
11821da177e4SLinus Torvalds inet->mc_addr = 0;
11831da177e4SLinus Torvalds err = 0;
11841da177e4SLinus Torvalds break;
11851da177e4SLinus Torvalds }
11863b1e0a65SYOSHIFUJI Hideaki dev = ip_dev_find(sock_net(sk), mreq.imr_address.s_addr);
118755b80503SEric Dumazet if (dev)
11881da177e4SLinus Torvalds mreq.imr_ifindex = dev->ifindex;
11891da177e4SLinus Torvalds } else
119055b80503SEric Dumazet dev = dev_get_by_index(sock_net(sk), mreq.imr_ifindex);
11911da177e4SLinus Torvalds
11921da177e4SLinus Torvalds
11931da177e4SLinus Torvalds err = -EADDRNOTAVAIL;
11941da177e4SLinus Torvalds if (!dev)
11951da177e4SLinus Torvalds break;
11967bb387c5SDavid Ahern
11977bb387c5SDavid Ahern midx = l3mdev_master_ifindex(dev);
11987bb387c5SDavid Ahern
119955b80503SEric Dumazet dev_put(dev);
12001da177e4SLinus Torvalds
12011da177e4SLinus Torvalds err = -EINVAL;
12021da177e4SLinus Torvalds if (sk->sk_bound_dev_if &&
12037bb387c5SDavid Ahern mreq.imr_ifindex != sk->sk_bound_dev_if &&
1204fdf1923bSMiaohe Lin midx != sk->sk_bound_dev_if)
12051da177e4SLinus Torvalds break;
12061da177e4SLinus Torvalds
12071da177e4SLinus Torvalds inet->mc_index = mreq.imr_ifindex;
12081da177e4SLinus Torvalds inet->mc_addr = mreq.imr_address.s_addr;
12091da177e4SLinus Torvalds err = 0;
12101da177e4SLinus Torvalds break;
12111da177e4SLinus Torvalds }
12121da177e4SLinus Torvalds
12131da177e4SLinus Torvalds case IP_ADD_MEMBERSHIP:
12141da177e4SLinus Torvalds case IP_DROP_MEMBERSHIP:
12151da177e4SLinus Torvalds {
12161da177e4SLinus Torvalds struct ip_mreqn mreq;
12171da177e4SLinus Torvalds
1218a96fb49bSFlavio Leitner err = -EPROTO;
1219b1c0356aSEric Dumazet if (inet_test_bit(IS_ICSK, sk))
1220a96fb49bSFlavio Leitner break;
1221a96fb49bSFlavio Leitner
12221da177e4SLinus Torvalds if (optlen < sizeof(struct ip_mreq))
12231da177e4SLinus Torvalds goto e_inval;
12241da177e4SLinus Torvalds err = -EFAULT;
12251da177e4SLinus Torvalds if (optlen >= sizeof(struct ip_mreqn)) {
122689654c5fSChristoph Hellwig if (copy_from_sockptr(&mreq, optval, sizeof(mreq)))
12271da177e4SLinus Torvalds break;
12281da177e4SLinus Torvalds } else {
12291da177e4SLinus Torvalds memset(&mreq, 0, sizeof(mreq));
123089654c5fSChristoph Hellwig if (copy_from_sockptr(&mreq, optval,
123189654c5fSChristoph Hellwig sizeof(struct ip_mreq)))
12321da177e4SLinus Torvalds break;
12331da177e4SLinus Torvalds }
12341da177e4SLinus Torvalds
12351da177e4SLinus Torvalds if (optname == IP_ADD_MEMBERSHIP)
123654ff9ef3SMarcelo Ricardo Leitner err = ip_mc_join_group(sk, &mreq);
12371da177e4SLinus Torvalds else
123854ff9ef3SMarcelo Ricardo Leitner err = ip_mc_leave_group(sk, &mreq);
12391da177e4SLinus Torvalds break;
12401da177e4SLinus Torvalds }
12411da177e4SLinus Torvalds case IP_MSFILTER:
12421da177e4SLinus Torvalds {
12431da177e4SLinus Torvalds struct ip_msfilter *msf;
12441da177e4SLinus Torvalds
12454167a960SGustavo A. R. Silva if (optlen < IP_MSFILTER_SIZE(0))
12461da177e4SLinus Torvalds goto e_inval;
12477de6d09fSKuniyuki Iwashima if (optlen > READ_ONCE(sysctl_optmem_max)) {
12481da177e4SLinus Torvalds err = -ENOBUFS;
12491da177e4SLinus Torvalds break;
12501da177e4SLinus Torvalds }
125189654c5fSChristoph Hellwig msf = memdup_sockptr(optval, optlen);
1252a2c841d9SAl Viro if (IS_ERR(msf)) {
1253a2c841d9SAl Viro err = PTR_ERR(msf);
12541da177e4SLinus Torvalds break;
12551da177e4SLinus Torvalds }
12561da177e4SLinus Torvalds /* numsrc >= (1G-4) overflow in 32 bits */
12571da177e4SLinus Torvalds if (msf->imsf_numsrc >= 0x3ffffffcU ||
12586ae0f2e5SKuniyuki Iwashima msf->imsf_numsrc > READ_ONCE(net->ipv4.sysctl_igmp_max_msf)) {
12591da177e4SLinus Torvalds kfree(msf);
12601da177e4SLinus Torvalds err = -ENOBUFS;
12611da177e4SLinus Torvalds break;
12621da177e4SLinus Torvalds }
12634167a960SGustavo A. R. Silva if (IP_MSFILTER_SIZE(msf->imsf_numsrc) > optlen) {
12641da177e4SLinus Torvalds kfree(msf);
12651da177e4SLinus Torvalds err = -EINVAL;
12661da177e4SLinus Torvalds break;
12671da177e4SLinus Torvalds }
12681da177e4SLinus Torvalds err = ip_mc_msfilter(sk, msf, 0);
12691da177e4SLinus Torvalds kfree(msf);
12701da177e4SLinus Torvalds break;
12711da177e4SLinus Torvalds }
12721da177e4SLinus Torvalds case IP_BLOCK_SOURCE:
12731da177e4SLinus Torvalds case IP_UNBLOCK_SOURCE:
12741da177e4SLinus Torvalds case IP_ADD_SOURCE_MEMBERSHIP:
12751da177e4SLinus Torvalds case IP_DROP_SOURCE_MEMBERSHIP:
12761da177e4SLinus Torvalds {
12771da177e4SLinus Torvalds struct ip_mreq_source mreqs;
12781da177e4SLinus Torvalds int omode, add;
12791da177e4SLinus Torvalds
12801da177e4SLinus Torvalds if (optlen != sizeof(struct ip_mreq_source))
12811da177e4SLinus Torvalds goto e_inval;
128289654c5fSChristoph Hellwig if (copy_from_sockptr(&mreqs, optval, sizeof(mreqs))) {
12831da177e4SLinus Torvalds err = -EFAULT;
12841da177e4SLinus Torvalds break;
12851da177e4SLinus Torvalds }
12861da177e4SLinus Torvalds if (optname == IP_BLOCK_SOURCE) {
12871da177e4SLinus Torvalds omode = MCAST_EXCLUDE;
12881da177e4SLinus Torvalds add = 1;
12891da177e4SLinus Torvalds } else if (optname == IP_UNBLOCK_SOURCE) {
12901da177e4SLinus Torvalds omode = MCAST_EXCLUDE;
12911da177e4SLinus Torvalds add = 0;
12921da177e4SLinus Torvalds } else if (optname == IP_ADD_SOURCE_MEMBERSHIP) {
12931da177e4SLinus Torvalds struct ip_mreqn mreq;
12941da177e4SLinus Torvalds
12951da177e4SLinus Torvalds mreq.imr_multiaddr.s_addr = mreqs.imr_multiaddr;
12961da177e4SLinus Torvalds mreq.imr_address.s_addr = mreqs.imr_interface;
12971da177e4SLinus Torvalds mreq.imr_ifindex = 0;
12986e2059b5SHangbin Liu err = ip_mc_join_group_ssm(sk, &mreq, MCAST_INCLUDE);
12998cdaaa15SDavid L Stevens if (err && err != -EADDRINUSE)
13001da177e4SLinus Torvalds break;
13011da177e4SLinus Torvalds omode = MCAST_INCLUDE;
13021da177e4SLinus Torvalds add = 1;
13031da177e4SLinus Torvalds } else /* IP_DROP_SOURCE_MEMBERSHIP */ {
13041da177e4SLinus Torvalds omode = MCAST_INCLUDE;
13051da177e4SLinus Torvalds add = 0;
13061da177e4SLinus Torvalds }
13071da177e4SLinus Torvalds err = ip_mc_source(add, omode, sk, &mreqs, 0);
13081da177e4SLinus Torvalds break;
13091da177e4SLinus Torvalds }
13101da177e4SLinus Torvalds case MCAST_JOIN_GROUP:
13111da177e4SLinus Torvalds case MCAST_LEAVE_GROUP:
1312b6238c04SChristoph Hellwig if (in_compat_syscall())
1313b6238c04SChristoph Hellwig err = compat_ip_mcast_join_leave(sk, optname, optval,
1314b6238c04SChristoph Hellwig optlen);
1315b6238c04SChristoph Hellwig else
131602caad7cSChristoph Hellwig err = ip_mcast_join_leave(sk, optname, optval, optlen);
13171da177e4SLinus Torvalds break;
13181da177e4SLinus Torvalds case MCAST_JOIN_SOURCE_GROUP:
13191da177e4SLinus Torvalds case MCAST_LEAVE_SOURCE_GROUP:
13201da177e4SLinus Torvalds case MCAST_BLOCK_SOURCE:
13211da177e4SLinus Torvalds case MCAST_UNBLOCK_SOURCE:
1322b6238c04SChristoph Hellwig err = do_mcast_group_source(sk, optname, optval, optlen);
13231da177e4SLinus Torvalds break;
13241da177e4SLinus Torvalds case MCAST_MSFILTER:
1325b6238c04SChristoph Hellwig if (in_compat_syscall())
1326b6238c04SChristoph Hellwig err = compat_ip_set_mcast_msfilter(sk, optval, optlen);
1327b6238c04SChristoph Hellwig else
1328d62c38f6SChristoph Hellwig err = ip_set_mcast_msfilter(sk, optval, optlen);
13291da177e4SLinus Torvalds break;
13301da177e4SLinus Torvalds case IP_IPSEC_POLICY:
13311da177e4SLinus Torvalds case IP_XFRM_POLICY:
13326fc0b4a7SHerbert Xu err = -EPERM;
13331df055d3SMartin KaFai Lau if (!sockopt_ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN))
13346fc0b4a7SHerbert Xu break;
133589654c5fSChristoph Hellwig err = xfrm_user_policy(sk, optname, optval, optlen);
13361da177e4SLinus Torvalds break;
13371da177e4SLinus Torvalds
133891d0b78cSJakub Sitnicki case IP_LOCAL_PORT_RANGE:
133991d0b78cSJakub Sitnicki {
134091d0b78cSJakub Sitnicki const __u16 lo = val;
134191d0b78cSJakub Sitnicki const __u16 hi = val >> 16;
134291d0b78cSJakub Sitnicki
134391d0b78cSJakub Sitnicki if (optlen != sizeof(__u32))
134491d0b78cSJakub Sitnicki goto e_inval;
134591d0b78cSJakub Sitnicki if (lo != 0 && hi != 0 && lo > hi)
134691d0b78cSJakub Sitnicki goto e_inval;
134791d0b78cSJakub Sitnicki
134891d0b78cSJakub Sitnicki inet->local_port_range.lo = lo;
134991d0b78cSJakub Sitnicki inet->local_port_range.hi = hi;
135091d0b78cSJakub Sitnicki break;
135191d0b78cSJakub Sitnicki }
13521da177e4SLinus Torvalds default:
13531da177e4SLinus Torvalds err = -ENOPROTOOPT;
13541da177e4SLinus Torvalds break;
13551da177e4SLinus Torvalds }
13561df055d3SMartin KaFai Lau sockopt_release_sock(sk);
1357baf606d9SMarcelo Ricardo Leitner if (needs_rtnl)
1358baf606d9SMarcelo Ricardo Leitner rtnl_unlock();
13591da177e4SLinus Torvalds return err;
13601da177e4SLinus Torvalds
13611da177e4SLinus Torvalds e_inval:
13621df055d3SMartin KaFai Lau sockopt_release_sock(sk);
1363baf606d9SMarcelo Ricardo Leitner if (needs_rtnl)
1364baf606d9SMarcelo Ricardo Leitner rtnl_unlock();
13651da177e4SLinus Torvalds return -EINVAL;
13661da177e4SLinus Torvalds }
13671da177e4SLinus Torvalds
1368f84af32cSEric Dumazet /**
1369829ae9d6SWillem de Bruijn * ipv4_pktinfo_prepare - transfer some info from rtable to skb
1370f84af32cSEric Dumazet * @sk: socket
1371f84af32cSEric Dumazet * @skb: buffer
1372*dcaafdbaSNicolas Dichtel * @drop_dst: if true, drops skb dst
1373f84af32cSEric Dumazet *
137435ebf65eSDavid S. Miller * To support IP_CMSG_PKTINFO option, we store rt_iif and specific
137535ebf65eSDavid S. Miller * destination in skb->cb[] before dst drop.
13768e3bff96Sstephen hemminger * This way, receiver doesn't make cache line misses to read rtable.
1377f84af32cSEric Dumazet */
ipv4_pktinfo_prepare(const struct sock * sk,struct sk_buff * skb,bool drop_dst)1378*dcaafdbaSNicolas Dichtel void ipv4_pktinfo_prepare(const struct sock *sk, struct sk_buff *skb, bool drop_dst)
1379f84af32cSEric Dumazet {
1380d826eb14SEric Dumazet struct in_pktinfo *pktinfo = PKTINFO_SKB_CB(skb);
1381c274af22SEric Dumazet bool prepare = inet_test_bit(PKTINFO, sk) ||
13824b261c75SHannes Frederic Sowa ipv6_sk_rxinfo(sk);
1383d826eb14SEric Dumazet
13844b261c75SHannes Frederic Sowa if (prepare && skb_rtable(skb)) {
13850b922b7aSDavid Ahern /* skb->cb is overloaded: prior to this point it is IP{6}CB
13860b922b7aSDavid Ahern * which has interface index (iif) as the first member of the
13870b922b7aSDavid Ahern * underlying inet{6}_skb_parm struct. This code then overlays
13880b922b7aSDavid Ahern * PKTINFO_SKB_CB and in_pktinfo also has iif as the first
1389f0c16ba8SWei Zhang * element so the iif is picked up from the prior IPCB. If iif
1390f0c16ba8SWei Zhang * is the loopback interface, then return the sending interface
1391f0c16ba8SWei Zhang * (e.g., process binds socket to eth0 for Tx which is
1392f0c16ba8SWei Zhang * redirected to loopback in the rtable/dst).
13930b922b7aSDavid Ahern */
1394cbea8f02SDavid Ahern struct rtable *rt = skb_rtable(skb);
1395cbea8f02SDavid Ahern bool l3slave = ipv4_l3mdev_skb(IPCB(skb)->flags);
1396cbea8f02SDavid Ahern
1397cbea8f02SDavid Ahern if (pktinfo->ipi_ifindex == LOOPBACK_IFINDEX)
1398f0c16ba8SWei Zhang pktinfo->ipi_ifindex = inet_iif(skb);
1399cbea8f02SDavid Ahern else if (l3slave && rt && rt->rt_iif)
1400cbea8f02SDavid Ahern pktinfo->ipi_ifindex = rt->rt_iif;
1401f0c16ba8SWei Zhang
140235ebf65eSDavid S. Miller pktinfo->ipi_spec_dst.s_addr = fib_compute_spec_dst(skb);
1403d826eb14SEric Dumazet } else {
1404d826eb14SEric Dumazet pktinfo->ipi_ifindex = 0;
1405d826eb14SEric Dumazet pktinfo->ipi_spec_dst.s_addr = 0;
1406f84af32cSEric Dumazet }
1407*dcaafdbaSNicolas Dichtel if (drop_dst)
1408d826eb14SEric Dumazet skb_dst_drop(skb);
1409d826eb14SEric Dumazet }
1410f84af32cSEric Dumazet
ip_setsockopt(struct sock * sk,int level,int optname,sockptr_t optval,unsigned int optlen)1411a7b75c5aSChristoph Hellwig int ip_setsockopt(struct sock *sk, int level, int optname, sockptr_t optval,
1412a7b75c5aSChristoph Hellwig unsigned int optlen)
14133fdadf7dSDmitry Mishin {
14143fdadf7dSDmitry Mishin int err;
14153fdadf7dSDmitry Mishin
14163fdadf7dSDmitry Mishin if (level != SOL_IP)
14173fdadf7dSDmitry Mishin return -ENOPROTOOPT;
14183fdadf7dSDmitry Mishin
1419a7b75c5aSChristoph Hellwig err = do_ip_setsockopt(sk, level, optname, optval, optlen);
142097adaddaSTaehee Yoo #if IS_ENABLED(CONFIG_BPFILTER_UMH)
1421d2ba09c1SAlexei Starovoitov if (optname >= BPFILTER_IPT_SO_SET_REPLACE &&
1422d2ba09c1SAlexei Starovoitov optname < BPFILTER_IPT_SET_MAX)
1423a7b75c5aSChristoph Hellwig err = bpfilter_ip_set_sockopt(sk, optname, optval, optlen);
1424d2ba09c1SAlexei Starovoitov #endif
14253fdadf7dSDmitry Mishin #ifdef CONFIG_NETFILTER
14263fdadf7dSDmitry Mishin /* we need to exclude all possible ENOPROTOOPTs except default case */
14273fdadf7dSDmitry Mishin if (err == -ENOPROTOOPT && optname != IP_HDRINCL &&
14286a9fb947SPavel Emelyanov optname != IP_IPSEC_POLICY &&
14296a9fb947SPavel Emelyanov optname != IP_XFRM_POLICY &&
14303f34cfaeSPaolo Abeni !ip_mroute_opt(optname))
1431a7b75c5aSChristoph Hellwig err = nf_setsockopt(sk, PF_INET, optname, optval, optlen);
14323fdadf7dSDmitry Mishin #endif
14333fdadf7dSDmitry Mishin return err;
14343fdadf7dSDmitry Mishin }
14354d52cfbeSEric Dumazet EXPORT_SYMBOL(ip_setsockopt);
14363fdadf7dSDmitry Mishin
14371da177e4SLinus Torvalds /*
14384d52cfbeSEric Dumazet * Get the options. Note for future reference. The GET of IP options gets
14394d52cfbeSEric Dumazet * the _received_ ones. The set sets the _sent_ ones.
14401da177e4SLinus Torvalds */
14411da177e4SLinus Torvalds
getsockopt_needs_rtnl(int optname)144287e9f031SWANG Cong static bool getsockopt_needs_rtnl(int optname)
144387e9f031SWANG Cong {
144487e9f031SWANG Cong switch (optname) {
144587e9f031SWANG Cong case IP_MSFILTER:
144687e9f031SWANG Cong case MCAST_MSFILTER:
144787e9f031SWANG Cong return true;
144887e9f031SWANG Cong }
144987e9f031SWANG Cong return false;
145087e9f031SWANG Cong }
145187e9f031SWANG Cong
ip_get_mcast_msfilter(struct sock * sk,sockptr_t optval,sockptr_t optlen,int len)1452728f064cSMartin KaFai Lau static int ip_get_mcast_msfilter(struct sock *sk, sockptr_t optval,
1453728f064cSMartin KaFai Lau sockptr_t optlen, int len)
145449e74c24SChristoph Hellwig {
1455db243b79SGustavo A. R. Silva const int size0 = offsetof(struct group_filter, gf_slist_flex);
145649e74c24SChristoph Hellwig struct group_filter gsf;
1457728f064cSMartin KaFai Lau int num, gsf_size;
145849e74c24SChristoph Hellwig int err;
145949e74c24SChristoph Hellwig
146049e74c24SChristoph Hellwig if (len < size0)
146149e74c24SChristoph Hellwig return -EINVAL;
1462728f064cSMartin KaFai Lau if (copy_from_sockptr(&gsf, optval, size0))
146349e74c24SChristoph Hellwig return -EFAULT;
146449e74c24SChristoph Hellwig
146549e74c24SChristoph Hellwig num = gsf.gf_numsrc;
1466728f064cSMartin KaFai Lau err = ip_mc_gsfget(sk, &gsf, optval,
1467728f064cSMartin KaFai Lau offsetof(struct group_filter, gf_slist_flex));
146849e74c24SChristoph Hellwig if (err)
146949e74c24SChristoph Hellwig return err;
147049e74c24SChristoph Hellwig if (gsf.gf_numsrc < num)
147149e74c24SChristoph Hellwig num = gsf.gf_numsrc;
1472728f064cSMartin KaFai Lau gsf_size = GROUP_FILTER_SIZE(num);
1473728f064cSMartin KaFai Lau if (copy_to_sockptr(optlen, &gsf_size, sizeof(int)) ||
1474728f064cSMartin KaFai Lau copy_to_sockptr(optval, &gsf, size0))
147549e74c24SChristoph Hellwig return -EFAULT;
147649e74c24SChristoph Hellwig return 0;
147749e74c24SChristoph Hellwig }
147849e74c24SChristoph Hellwig
compat_ip_get_mcast_msfilter(struct sock * sk,sockptr_t optval,sockptr_t optlen,int len)1479728f064cSMartin KaFai Lau static int compat_ip_get_mcast_msfilter(struct sock *sk, sockptr_t optval,
1480728f064cSMartin KaFai Lau sockptr_t optlen, int len)
148149e74c24SChristoph Hellwig {
1482db243b79SGustavo A. R. Silva const int size0 = offsetof(struct compat_group_filter, gf_slist_flex);
148349e74c24SChristoph Hellwig struct compat_group_filter gf32;
148449e74c24SChristoph Hellwig struct group_filter gf;
148549e74c24SChristoph Hellwig int num;
1486b6238c04SChristoph Hellwig int err;
148749e74c24SChristoph Hellwig
148849e74c24SChristoph Hellwig if (len < size0)
148949e74c24SChristoph Hellwig return -EINVAL;
1490728f064cSMartin KaFai Lau if (copy_from_sockptr(&gf32, optval, size0))
149149e74c24SChristoph Hellwig return -EFAULT;
149249e74c24SChristoph Hellwig
149349e74c24SChristoph Hellwig gf.gf_interface = gf32.gf_interface;
149449e74c24SChristoph Hellwig gf.gf_fmode = gf32.gf_fmode;
149549e74c24SChristoph Hellwig num = gf.gf_numsrc = gf32.gf_numsrc;
149649e74c24SChristoph Hellwig gf.gf_group = gf32.gf_group;
149749e74c24SChristoph Hellwig
1498728f064cSMartin KaFai Lau err = ip_mc_gsfget(sk, &gf, optval,
1499728f064cSMartin KaFai Lau offsetof(struct compat_group_filter, gf_slist_flex));
150049e74c24SChristoph Hellwig if (err)
150149e74c24SChristoph Hellwig return err;
150249e74c24SChristoph Hellwig if (gf.gf_numsrc < num)
150349e74c24SChristoph Hellwig num = gf.gf_numsrc;
150449e74c24SChristoph Hellwig len = GROUP_FILTER_SIZE(num) - (sizeof(gf) - sizeof(gf32));
1505728f064cSMartin KaFai Lau if (copy_to_sockptr(optlen, &len, sizeof(int)) ||
1506728f064cSMartin KaFai Lau copy_to_sockptr_offset(optval, offsetof(struct compat_group_filter, gf_fmode),
1507728f064cSMartin KaFai Lau &gf.gf_fmode, sizeof(gf.gf_fmode)) ||
1508728f064cSMartin KaFai Lau copy_to_sockptr_offset(optval, offsetof(struct compat_group_filter, gf_numsrc),
1509728f064cSMartin KaFai Lau &gf.gf_numsrc, sizeof(gf.gf_numsrc)))
151049e74c24SChristoph Hellwig return -EFAULT;
151149e74c24SChristoph Hellwig return 0;
151249e74c24SChristoph Hellwig }
151349e74c24SChristoph Hellwig
do_ip_getsockopt(struct sock * sk,int level,int optname,sockptr_t optval,sockptr_t optlen)1514fd969f25SMartin KaFai Lau int do_ip_getsockopt(struct sock *sk, int level, int optname,
1515728f064cSMartin KaFai Lau sockptr_t optval, sockptr_t optlen)
15161da177e4SLinus Torvalds {
15171da177e4SLinus Torvalds struct inet_sock *inet = inet_sk(sk);
151887e9f031SWANG Cong bool needs_rtnl = getsockopt_needs_rtnl(optname);
151987e9f031SWANG Cong int val, err = 0;
15201da177e4SLinus Torvalds int len;
15211da177e4SLinus Torvalds
15221da177e4SLinus Torvalds if (level != SOL_IP)
15231da177e4SLinus Torvalds return -EOPNOTSUPP;
15241da177e4SLinus Torvalds
15256a9fb947SPavel Emelyanov if (ip_mroute_opt(optname))
15261da177e4SLinus Torvalds return ip_mroute_getsockopt(sk, optname, optval, optlen);
15271da177e4SLinus Torvalds
1528728f064cSMartin KaFai Lau if (copy_from_sockptr(&len, optlen, sizeof(int)))
15291da177e4SLinus Torvalds return -EFAULT;
15301da177e4SLinus Torvalds if (len < 0)
15311da177e4SLinus Torvalds return -EINVAL;
15321da177e4SLinus Torvalds
1533b4d84bceSEric Dumazet /* Handle options that can be read without locking the socket. */
1534b4d84bceSEric Dumazet switch (optname) {
1535b4d84bceSEric Dumazet case IP_PKTINFO:
1536b4d84bceSEric Dumazet val = inet_test_bit(PKTINFO, sk);
1537b4d84bceSEric Dumazet goto copyval;
1538b4d84bceSEric Dumazet case IP_RECVTTL:
1539b4d84bceSEric Dumazet val = inet_test_bit(TTL, sk);
1540b4d84bceSEric Dumazet goto copyval;
1541b4d84bceSEric Dumazet case IP_RECVTOS:
1542b4d84bceSEric Dumazet val = inet_test_bit(TOS, sk);
1543b4d84bceSEric Dumazet goto copyval;
1544b4d84bceSEric Dumazet case IP_RECVOPTS:
1545b4d84bceSEric Dumazet val = inet_test_bit(RECVOPTS, sk);
1546b4d84bceSEric Dumazet goto copyval;
1547b4d84bceSEric Dumazet case IP_RETOPTS:
1548b4d84bceSEric Dumazet val = inet_test_bit(RETOPTS, sk);
1549b4d84bceSEric Dumazet goto copyval;
1550b4d84bceSEric Dumazet case IP_PASSSEC:
1551b4d84bceSEric Dumazet val = inet_test_bit(PASSSEC, sk);
1552b4d84bceSEric Dumazet goto copyval;
1553b4d84bceSEric Dumazet case IP_RECVORIGDSTADDR:
1554b4d84bceSEric Dumazet val = inet_test_bit(ORIGDSTADDR, sk);
1555b4d84bceSEric Dumazet goto copyval;
1556b4d84bceSEric Dumazet case IP_CHECKSUM:
1557b4d84bceSEric Dumazet val = inet_test_bit(CHECKSUM, sk);
1558b4d84bceSEric Dumazet goto copyval;
1559b4d84bceSEric Dumazet case IP_RECVFRAGSIZE:
1560b4d84bceSEric Dumazet val = inet_test_bit(RECVFRAGSIZE, sk);
1561b4d84bceSEric Dumazet goto copyval;
15626b5f43eaSEric Dumazet case IP_RECVERR:
15636b5f43eaSEric Dumazet val = inet_test_bit(RECVERR, sk);
15646b5f43eaSEric Dumazet goto copyval;
15658e8cfb11SEric Dumazet case IP_RECVERR_RFC4884:
15668e8cfb11SEric Dumazet val = inet_test_bit(RECVERR_RFC4884, sk);
15678e8cfb11SEric Dumazet goto copyval;
15683f7e7532SEric Dumazet case IP_FREEBIND:
15693f7e7532SEric Dumazet val = inet_test_bit(FREEBIND, sk);
15703f7e7532SEric Dumazet goto copyval;
1571cafbe182SEric Dumazet case IP_HDRINCL:
1572cafbe182SEric Dumazet val = inet_test_bit(HDRINCL, sk);
1573cafbe182SEric Dumazet goto copyval;
1574b09bde5cSEric Dumazet case IP_MULTICAST_LOOP:
1575b09bde5cSEric Dumazet val = inet_test_bit(MC_LOOP, sk);
1576b09bde5cSEric Dumazet goto copyval;
1577307b4ac6SEric Dumazet case IP_MULTICAST_ALL:
1578307b4ac6SEric Dumazet val = inet_test_bit(MC_ALL, sk);
1579307b4ac6SEric Dumazet goto copyval;
15804bd0623fSEric Dumazet case IP_TRANSPARENT:
15814bd0623fSEric Dumazet val = inet_test_bit(TRANSPARENT, sk);
15824bd0623fSEric Dumazet goto copyval;
1583f04b8d34SEric Dumazet case IP_NODEFRAG:
1584f04b8d34SEric Dumazet val = inet_test_bit(NODEFRAG, sk);
1585f04b8d34SEric Dumazet goto copyval;
1586ca571e2eSEric Dumazet case IP_BIND_ADDRESS_NO_PORT:
1587ca571e2eSEric Dumazet val = inet_test_bit(BIND_ADDRESS_NO_PORT, sk);
1588ca571e2eSEric Dumazet goto copyval;
158910f42426SEric Dumazet case IP_TTL:
159010f42426SEric Dumazet val = READ_ONCE(inet->uc_ttl);
159110f42426SEric Dumazet if (val < 0)
159210f42426SEric Dumazet val = READ_ONCE(sock_net(sk)->ipv4.sysctl_ip_default_ttl);
159310f42426SEric Dumazet goto copyval;
159412af7326SEric Dumazet case IP_MINTTL:
159512af7326SEric Dumazet val = READ_ONCE(inet->min_ttl);
159612af7326SEric Dumazet goto copyval;
1597b4d84bceSEric Dumazet }
1598b4d84bceSEric Dumazet
159987e9f031SWANG Cong if (needs_rtnl)
160087e9f031SWANG Cong rtnl_lock();
16011985320cSMartin KaFai Lau sockopt_lock_sock(sk);
16021da177e4SLinus Torvalds
16031da177e4SLinus Torvalds switch (optname) {
16041da177e4SLinus Torvalds case IP_OPTIONS:
16051da177e4SLinus Torvalds {
16061da177e4SLinus Torvalds unsigned char optbuf[sizeof(struct ip_options)+40];
16071da177e4SLinus Torvalds struct ip_options *opt = (struct ip_options *)optbuf;
1608f6d8bd05SEric Dumazet struct ip_options_rcu *inet_opt;
1609f6d8bd05SEric Dumazet
1610f6d8bd05SEric Dumazet inet_opt = rcu_dereference_protected(inet->inet_opt,
16111e1d04e6SHannes Frederic Sowa lockdep_sock_is_held(sk));
16121da177e4SLinus Torvalds opt->optlen = 0;
1613f6d8bd05SEric Dumazet if (inet_opt)
1614f6d8bd05SEric Dumazet memcpy(optbuf, &inet_opt->opt,
16151da177e4SLinus Torvalds sizeof(struct ip_options) +
1616f6d8bd05SEric Dumazet inet_opt->opt.optlen);
16171985320cSMartin KaFai Lau sockopt_release_sock(sk);
16181da177e4SLinus Torvalds
1619728f064cSMartin KaFai Lau if (opt->optlen == 0) {
1620728f064cSMartin KaFai Lau len = 0;
1621728f064cSMartin KaFai Lau return copy_to_sockptr(optlen, &len, sizeof(int));
1622728f064cSMartin KaFai Lau }
16231da177e4SLinus Torvalds
16241da177e4SLinus Torvalds ip_options_undo(opt);
16251da177e4SLinus Torvalds
16261da177e4SLinus Torvalds len = min_t(unsigned int, len, opt->optlen);
1627728f064cSMartin KaFai Lau if (copy_to_sockptr(optlen, &len, sizeof(int)))
16281da177e4SLinus Torvalds return -EFAULT;
1629728f064cSMartin KaFai Lau if (copy_to_sockptr(optval, opt->__data, len))
16301da177e4SLinus Torvalds return -EFAULT;
16311da177e4SLinus Torvalds return 0;
16321da177e4SLinus Torvalds }
16331da177e4SLinus Torvalds case IP_TOS:
16341da177e4SLinus Torvalds val = inet->tos;
16351da177e4SLinus Torvalds break;
16361da177e4SLinus Torvalds case IP_MTU_DISCOVER:
16371da177e4SLinus Torvalds val = inet->pmtudisc;
16381da177e4SLinus Torvalds break;
16391da177e4SLinus Torvalds case IP_MTU:
16401da177e4SLinus Torvalds {
16411da177e4SLinus Torvalds struct dst_entry *dst;
16421da177e4SLinus Torvalds val = 0;
16431da177e4SLinus Torvalds dst = sk_dst_get(sk);
16441da177e4SLinus Torvalds if (dst) {
16451da177e4SLinus Torvalds val = dst_mtu(dst);
16461da177e4SLinus Torvalds dst_release(dst);
16471da177e4SLinus Torvalds }
16481da177e4SLinus Torvalds if (!val) {
16491985320cSMartin KaFai Lau sockopt_release_sock(sk);
16501da177e4SLinus Torvalds return -ENOTCONN;
16511da177e4SLinus Torvalds }
16521da177e4SLinus Torvalds break;
16531da177e4SLinus Torvalds }
16541da177e4SLinus Torvalds case IP_MULTICAST_TTL:
16551da177e4SLinus Torvalds val = inet->mc_ttl;
16561da177e4SLinus Torvalds break;
165776e21053SErich E. Hoover case IP_UNICAST_IF:
165876e21053SErich E. Hoover val = (__force int)htonl((__u32) inet->uc_index);
165976e21053SErich E. Hoover break;
16601da177e4SLinus Torvalds case IP_MULTICAST_IF:
16611da177e4SLinus Torvalds {
16621da177e4SLinus Torvalds struct in_addr addr;
16631da177e4SLinus Torvalds len = min_t(unsigned int, len, sizeof(struct in_addr));
16641da177e4SLinus Torvalds addr.s_addr = inet->mc_addr;
16651985320cSMartin KaFai Lau sockopt_release_sock(sk);
16661da177e4SLinus Torvalds
1667728f064cSMartin KaFai Lau if (copy_to_sockptr(optlen, &len, sizeof(int)))
16681da177e4SLinus Torvalds return -EFAULT;
1669728f064cSMartin KaFai Lau if (copy_to_sockptr(optval, &addr, len))
16701da177e4SLinus Torvalds return -EFAULT;
16711da177e4SLinus Torvalds return 0;
16721da177e4SLinus Torvalds }
16731da177e4SLinus Torvalds case IP_MSFILTER:
16741da177e4SLinus Torvalds {
16751da177e4SLinus Torvalds struct ip_msfilter msf;
16761da177e4SLinus Torvalds
16774167a960SGustavo A. R. Silva if (len < IP_MSFILTER_SIZE(0)) {
167887e9f031SWANG Cong err = -EINVAL;
167987e9f031SWANG Cong goto out;
16801da177e4SLinus Torvalds }
1681728f064cSMartin KaFai Lau if (copy_from_sockptr(&msf, optval, IP_MSFILTER_SIZE(0))) {
168287e9f031SWANG Cong err = -EFAULT;
168387e9f031SWANG Cong goto out;
16841da177e4SLinus Torvalds }
1685728f064cSMartin KaFai Lau err = ip_mc_msfget(sk, &msf, optval, optlen);
168687e9f031SWANG Cong goto out;
16871da177e4SLinus Torvalds }
16881da177e4SLinus Torvalds case MCAST_MSFILTER:
1689b6238c04SChristoph Hellwig if (in_compat_syscall())
1690b6238c04SChristoph Hellwig err = compat_ip_get_mcast_msfilter(sk, optval, optlen,
1691b6238c04SChristoph Hellwig len);
1692b6238c04SChristoph Hellwig else
169349e74c24SChristoph Hellwig err = ip_get_mcast_msfilter(sk, optval, optlen, len);
169487e9f031SWANG Cong goto out;
16951da177e4SLinus Torvalds case IP_PKTOPTIONS:
16961da177e4SLinus Torvalds {
16971da177e4SLinus Torvalds struct msghdr msg;
16981da177e4SLinus Torvalds
16991985320cSMartin KaFai Lau sockopt_release_sock(sk);
17001da177e4SLinus Torvalds
17011da177e4SLinus Torvalds if (sk->sk_type != SOCK_STREAM)
17021da177e4SLinus Torvalds return -ENOPROTOOPT;
17031da177e4SLinus Torvalds
1704728f064cSMartin KaFai Lau if (optval.is_kernel) {
1705728f064cSMartin KaFai Lau msg.msg_control_is_user = false;
1706728f064cSMartin KaFai Lau msg.msg_control = optval.kernel;
1707728f064cSMartin KaFai Lau } else {
17081f466e1fSChristoph Hellwig msg.msg_control_is_user = true;
1709728f064cSMartin KaFai Lau msg.msg_control_user = optval.user;
1710728f064cSMartin KaFai Lau }
17111da177e4SLinus Torvalds msg.msg_controllen = len;
1712b6238c04SChristoph Hellwig msg.msg_flags = in_compat_syscall() ? MSG_CMSG_COMPAT : 0;
17131da177e4SLinus Torvalds
1714c274af22SEric Dumazet if (inet_test_bit(PKTINFO, sk)) {
17151da177e4SLinus Torvalds struct in_pktinfo info;
17161da177e4SLinus Torvalds
1717c720c7e8SEric Dumazet info.ipi_addr.s_addr = inet->inet_rcv_saddr;
1718c720c7e8SEric Dumazet info.ipi_spec_dst.s_addr = inet->inet_rcv_saddr;
17191da177e4SLinus Torvalds info.ipi_ifindex = inet->mc_index;
17201da177e4SLinus Torvalds put_cmsg(&msg, SOL_IP, IP_PKTINFO, sizeof(info), &info);
17211da177e4SLinus Torvalds }
1722c274af22SEric Dumazet if (inet_test_bit(TTL, sk)) {
17231da177e4SLinus Torvalds int hlim = inet->mc_ttl;
17241da177e4SLinus Torvalds put_cmsg(&msg, SOL_IP, IP_TTL, sizeof(hlim), &hlim);
17251da177e4SLinus Torvalds }
1726c274af22SEric Dumazet if (inet_test_bit(TOS, sk)) {
17274c507d28SJiri Benc int tos = inet->rcv_tos;
17284c507d28SJiri Benc put_cmsg(&msg, SOL_IP, IP_TOS, sizeof(tos), &tos);
17294c507d28SJiri Benc }
17301da177e4SLinus Torvalds len -= msg.msg_controllen;
1731728f064cSMartin KaFai Lau return copy_to_sockptr(optlen, &len, sizeof(int));
17321da177e4SLinus Torvalds }
173391d0b78cSJakub Sitnicki case IP_LOCAL_PORT_RANGE:
173491d0b78cSJakub Sitnicki val = inet->local_port_range.hi << 16 | inet->local_port_range.lo;
173591d0b78cSJakub Sitnicki break;
17363632679dSNicolas Dichtel case IP_PROTOCOL:
17373632679dSNicolas Dichtel val = inet_sk(sk)->inet_num;
17383632679dSNicolas Dichtel break;
17391da177e4SLinus Torvalds default:
17401985320cSMartin KaFai Lau sockopt_release_sock(sk);
17411da177e4SLinus Torvalds return -ENOPROTOOPT;
17421da177e4SLinus Torvalds }
17431985320cSMartin KaFai Lau sockopt_release_sock(sk);
1744b4d84bceSEric Dumazet copyval:
1745951e07c9SDavid S. Miller if (len < sizeof(int) && len > 0 && val >= 0 && val <= 255) {
17461da177e4SLinus Torvalds unsigned char ucval = (unsigned char)val;
17471da177e4SLinus Torvalds len = 1;
1748728f064cSMartin KaFai Lau if (copy_to_sockptr(optlen, &len, sizeof(int)))
17491da177e4SLinus Torvalds return -EFAULT;
1750728f064cSMartin KaFai Lau if (copy_to_sockptr(optval, &ucval, 1))
17511da177e4SLinus Torvalds return -EFAULT;
17521da177e4SLinus Torvalds } else {
17531da177e4SLinus Torvalds len = min_t(unsigned int, sizeof(int), len);
1754728f064cSMartin KaFai Lau if (copy_to_sockptr(optlen, &len, sizeof(int)))
17551da177e4SLinus Torvalds return -EFAULT;
1756728f064cSMartin KaFai Lau if (copy_to_sockptr(optval, &val, len))
17571da177e4SLinus Torvalds return -EFAULT;
17581da177e4SLinus Torvalds }
17591da177e4SLinus Torvalds return 0;
176087e9f031SWANG Cong
176187e9f031SWANG Cong out:
17621985320cSMartin KaFai Lau sockopt_release_sock(sk);
176387e9f031SWANG Cong if (needs_rtnl)
176487e9f031SWANG Cong rtnl_unlock();
176587e9f031SWANG Cong return err;
17661da177e4SLinus Torvalds }
17671da177e4SLinus Torvalds
ip_getsockopt(struct sock * sk,int level,int optname,char __user * optval,int __user * optlen)17683fdadf7dSDmitry Mishin int ip_getsockopt(struct sock *sk, int level,
17693fdadf7dSDmitry Mishin int optname, char __user *optval, int __user *optlen)
17703fdadf7dSDmitry Mishin {
17713fdadf7dSDmitry Mishin int err;
17723fdadf7dSDmitry Mishin
1773728f064cSMartin KaFai Lau err = do_ip_getsockopt(sk, level, optname,
1774728f064cSMartin KaFai Lau USER_SOCKPTR(optval), USER_SOCKPTR(optlen));
1775b6238c04SChristoph Hellwig
177697adaddaSTaehee Yoo #if IS_ENABLED(CONFIG_BPFILTER_UMH)
1777d2ba09c1SAlexei Starovoitov if (optname >= BPFILTER_IPT_SO_GET_INFO &&
1778d2ba09c1SAlexei Starovoitov optname < BPFILTER_IPT_GET_MAX)
1779d2ba09c1SAlexei Starovoitov err = bpfilter_ip_get_sockopt(sk, optname, optval, optlen);
1780d2ba09c1SAlexei Starovoitov #endif
17813fdadf7dSDmitry Mishin #ifdef CONFIG_NETFILTER
17823fdadf7dSDmitry Mishin /* we need to exclude all possible ENOPROTOOPTs except default case */
17836a9fb947SPavel Emelyanov if (err == -ENOPROTOOPT && optname != IP_PKTOPTIONS &&
17846a9fb947SPavel Emelyanov !ip_mroute_opt(optname)) {
17853fdadf7dSDmitry Mishin int len;
17863fdadf7dSDmitry Mishin
17873fdadf7dSDmitry Mishin if (get_user(len, optlen))
17883fdadf7dSDmitry Mishin return -EFAULT;
17893fdadf7dSDmitry Mishin
179001ea306fSPaolo Abeni err = nf_getsockopt(sk, PF_INET, optname, optval, &len);
17913fdadf7dSDmitry Mishin if (err >= 0)
17923fdadf7dSDmitry Mishin err = put_user(len, optlen);
17933fdadf7dSDmitry Mishin return err;
17943fdadf7dSDmitry Mishin }
17953fdadf7dSDmitry Mishin #endif
17963fdadf7dSDmitry Mishin return err;
17973fdadf7dSDmitry Mishin }
17984d52cfbeSEric Dumazet EXPORT_SYMBOL(ip_getsockopt);
1799