1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds * INET An implementation of the TCP/IP protocol suite for the LINUX
41da177e4SLinus Torvalds * operating system. INET is implemented using the BSD Socket
51da177e4SLinus Torvalds * interface as the means of communication with the user level.
61da177e4SLinus Torvalds *
71da177e4SLinus Torvalds * The IP to API glue.
81da177e4SLinus Torvalds *
91da177e4SLinus Torvalds * Authors: see ip.c
101da177e4SLinus Torvalds *
111da177e4SLinus Torvalds * Fixes:
121da177e4SLinus Torvalds * Many : Split from ip.c , see ip.c for history.
131da177e4SLinus Torvalds * Martin Mares : TOS setting fixed.
141da177e4SLinus Torvalds * Alan Cox : Fixed a couple of oopses in Martin's
151da177e4SLinus Torvalds * TOS tweaks.
161da177e4SLinus Torvalds * Mike McLagan : Routing by source
171da177e4SLinus Torvalds */
181da177e4SLinus Torvalds
191da177e4SLinus Torvalds #include <linux/module.h>
201da177e4SLinus Torvalds #include <linux/types.h>
211da177e4SLinus Torvalds #include <linux/mm.h>
221da177e4SLinus Torvalds #include <linux/skbuff.h>
231da177e4SLinus Torvalds #include <linux/ip.h>
241da177e4SLinus Torvalds #include <linux/icmp.h>
2514c85021SArnaldo Carvalho de Melo #include <linux/inetdevice.h>
261da177e4SLinus Torvalds #include <linux/netdevice.h>
275a0e3ad6STejun Heo #include <linux/slab.h>
281da177e4SLinus Torvalds #include <net/sock.h>
291da177e4SLinus Torvalds #include <net/ip.h>
301da177e4SLinus Torvalds #include <net/icmp.h>
31d83d8461SArnaldo Carvalho de Melo #include <net/tcp_states.h>
321da177e4SLinus Torvalds #include <linux/udp.h>
331da177e4SLinus Torvalds #include <linux/igmp.h>
341da177e4SLinus Torvalds #include <linux/netfilter.h>
351da177e4SLinus Torvalds #include <linux/route.h>
361da177e4SLinus Torvalds #include <linux/mroute.h>
372c67e9acSMaciej Żenczykowski #include <net/inet_ecn.h>
381da177e4SLinus Torvalds #include <net/route.h>
391da177e4SLinus Torvalds #include <net/xfrm.h>
40dae50295SDavid L Stevens #include <net/compat.h>
41ad6f939aSTom Herbert #include <net/checksum.h>
42dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6)
431da177e4SLinus Torvalds #include <net/transp_v6.h>
441da177e4SLinus Torvalds #endif
4535ebf65eSDavid S. Miller #include <net/ip_fib.h>
461da177e4SLinus Torvalds
471da177e4SLinus Torvalds #include <linux/errqueue.h>
487c0f6ba6SLinus Torvalds #include <linux/uaccess.h>
491da177e4SLinus Torvalds
501da177e4SLinus Torvalds /*
511da177e4SLinus Torvalds * SOL_IP control messages.
521da177e4SLinus Torvalds */
531da177e4SLinus Torvalds
ip_cmsg_recv_pktinfo(struct msghdr * msg,struct sk_buff * skb)541da177e4SLinus Torvalds static void ip_cmsg_recv_pktinfo(struct msghdr *msg, struct sk_buff *skb)
551da177e4SLinus Torvalds {
56d826eb14SEric Dumazet struct in_pktinfo info = *PKTINFO_SKB_CB(skb);
571da177e4SLinus Torvalds
58eddc9ec5SArnaldo Carvalho de Melo info.ipi_addr.s_addr = ip_hdr(skb)->daddr;
591da177e4SLinus Torvalds
601da177e4SLinus Torvalds put_cmsg(msg, SOL_IP, IP_PKTINFO, sizeof(info), &info);
611da177e4SLinus Torvalds }
621da177e4SLinus Torvalds
ip_cmsg_recv_ttl(struct msghdr * msg,struct sk_buff * skb)631da177e4SLinus Torvalds static void ip_cmsg_recv_ttl(struct msghdr *msg, struct sk_buff *skb)
641da177e4SLinus Torvalds {
65eddc9ec5SArnaldo Carvalho de Melo int ttl = ip_hdr(skb)->ttl;
661da177e4SLinus Torvalds put_cmsg(msg, SOL_IP, IP_TTL, sizeof(int), &ttl);
671da177e4SLinus Torvalds }
681da177e4SLinus Torvalds
ip_cmsg_recv_tos(struct msghdr * msg,struct sk_buff * skb)691da177e4SLinus Torvalds static void ip_cmsg_recv_tos(struct msghdr *msg, struct sk_buff *skb)
701da177e4SLinus Torvalds {
71eddc9ec5SArnaldo Carvalho de Melo put_cmsg(msg, SOL_IP, IP_TOS, 1, &ip_hdr(skb)->tos);
721da177e4SLinus Torvalds }
731da177e4SLinus Torvalds
ip_cmsg_recv_opts(struct msghdr * msg,struct sk_buff * skb)741da177e4SLinus Torvalds static void ip_cmsg_recv_opts(struct msghdr *msg, struct sk_buff *skb)
751da177e4SLinus Torvalds {
761da177e4SLinus Torvalds if (IPCB(skb)->opt.optlen == 0)
771da177e4SLinus Torvalds return;
781da177e4SLinus Torvalds
79eddc9ec5SArnaldo Carvalho de Melo put_cmsg(msg, SOL_IP, IP_RECVOPTS, IPCB(skb)->opt.optlen,
80eddc9ec5SArnaldo Carvalho de Melo ip_hdr(skb) + 1);
811da177e4SLinus Torvalds }
821da177e4SLinus Torvalds
831da177e4SLinus Torvalds
ip_cmsg_recv_retopts(struct net * net,struct msghdr * msg,struct sk_buff * skb)8491ed1e66SPaolo Abeni static void ip_cmsg_recv_retopts(struct net *net, struct msghdr *msg,
8591ed1e66SPaolo Abeni struct sk_buff *skb)
861da177e4SLinus Torvalds {
871da177e4SLinus Torvalds unsigned char optbuf[sizeof(struct ip_options) + 40];
881da177e4SLinus Torvalds struct ip_options *opt = (struct ip_options *)optbuf;
891da177e4SLinus Torvalds
901da177e4SLinus Torvalds if (IPCB(skb)->opt.optlen == 0)
911da177e4SLinus Torvalds return;
921da177e4SLinus Torvalds
9391ed1e66SPaolo Abeni if (ip_options_echo(net, opt, skb)) {
941da177e4SLinus Torvalds msg->msg_flags |= MSG_CTRUNC;
951da177e4SLinus Torvalds return;
961da177e4SLinus Torvalds }
971da177e4SLinus Torvalds ip_options_undo(opt);
981da177e4SLinus Torvalds
991da177e4SLinus Torvalds put_cmsg(msg, SOL_IP, IP_RETOPTS, opt->optlen, opt->__data);
1001da177e4SLinus Torvalds }
1011da177e4SLinus Torvalds
ip_cmsg_recv_fragsize(struct msghdr * msg,struct sk_buff * skb)10270ecc248SWillem de Bruijn static void ip_cmsg_recv_fragsize(struct msghdr *msg, struct sk_buff *skb)
10370ecc248SWillem de Bruijn {
10470ecc248SWillem de Bruijn int val;
10570ecc248SWillem de Bruijn
10670ecc248SWillem de Bruijn if (IPCB(skb)->frag_max_size == 0)
10770ecc248SWillem de Bruijn return;
10870ecc248SWillem de Bruijn
10970ecc248SWillem de Bruijn val = IPCB(skb)->frag_max_size;
11070ecc248SWillem de Bruijn put_cmsg(msg, SOL_IP, IP_RECVFRAGSIZE, sizeof(val), &val);
11170ecc248SWillem de Bruijn }
11270ecc248SWillem de Bruijn
ip_cmsg_recv_checksum(struct msghdr * msg,struct sk_buff * skb,int tlen,int offset)113ad6f939aSTom Herbert static void ip_cmsg_recv_checksum(struct msghdr *msg, struct sk_buff *skb,
11410df8e61SEric Dumazet int tlen, int offset)
115ad6f939aSTom Herbert {
116ad6f939aSTom Herbert __wsum csum = skb->csum;
117ad6f939aSTom Herbert
118ad6f939aSTom Herbert if (skb->ip_summed != CHECKSUM_COMPLETE)
119ad6f939aSTom Herbert return;
120ad6f939aSTom Herbert
121ca4ef457SPaolo Abeni if (offset != 0) {
122ca4ef457SPaolo Abeni int tend_off = skb_transport_offset(skb) + tlen;
123ca4ef457SPaolo Abeni csum = csum_sub(csum, skb_checksum(skb, tend_off, offset, 0));
124ca4ef457SPaolo Abeni }
125ad6f939aSTom Herbert
126ad6f939aSTom Herbert put_cmsg(msg, SOL_IP, IP_CHECKSUM, sizeof(__wsum), &csum);
127ad6f939aSTom Herbert }
128ad6f939aSTom Herbert
ip_cmsg_recv_security(struct msghdr * msg,struct sk_buff * skb)1292c7946a7SCatherine Zhang static void ip_cmsg_recv_security(struct msghdr *msg, struct sk_buff *skb)
1302c7946a7SCatherine Zhang {
1312c7946a7SCatherine Zhang char *secdata;
132dc49c1f9SCatherine Zhang u32 seclen, secid;
1332c7946a7SCatherine Zhang int err;
1342c7946a7SCatherine Zhang
135dc49c1f9SCatherine Zhang err = security_socket_getpeersec_dgram(NULL, skb, &secid);
136dc49c1f9SCatherine Zhang if (err)
137dc49c1f9SCatherine Zhang return;
138dc49c1f9SCatherine Zhang
139dc49c1f9SCatherine Zhang err = security_secid_to_secctx(secid, &secdata, &seclen);
1402c7946a7SCatherine Zhang if (err)
1412c7946a7SCatherine Zhang return;
1422c7946a7SCatherine Zhang
1432c7946a7SCatherine Zhang put_cmsg(msg, SOL_IP, SCM_SECURITY, seclen, secdata);
144dc49c1f9SCatherine Zhang security_release_secctx(secdata, seclen);
1452c7946a7SCatherine Zhang }
1462c7946a7SCatherine Zhang
ip_cmsg_recv_dstaddr(struct msghdr * msg,struct sk_buff * skb)14721d1a161SHarvey Harrison static void ip_cmsg_recv_dstaddr(struct msghdr *msg, struct sk_buff *skb)
148e8b2dfe9SBalazs Scheidler {
1494a06fa67SWillem de Bruijn __be16 _ports[2], *ports;
150e8b2dfe9SBalazs Scheidler struct sockaddr_in sin;
151e8b2dfe9SBalazs Scheidler
152e8b2dfe9SBalazs Scheidler /* All current transport protocols have the port numbers in the
153e8b2dfe9SBalazs Scheidler * first four bytes of the transport header and this function is
154e8b2dfe9SBalazs Scheidler * written with this assumption in mind.
155e8b2dfe9SBalazs Scheidler */
1564a06fa67SWillem de Bruijn ports = skb_header_pointer(skb, skb_transport_offset(skb),
1574a06fa67SWillem de Bruijn sizeof(_ports), &_ports);
1584a06fa67SWillem de Bruijn if (!ports)
1594a06fa67SWillem de Bruijn return;
160e8b2dfe9SBalazs Scheidler
161e8b2dfe9SBalazs Scheidler sin.sin_family = AF_INET;
16264199fc0SEric Dumazet sin.sin_addr.s_addr = ip_hdr(skb)->daddr;
163e8b2dfe9SBalazs Scheidler sin.sin_port = ports[1];
164e8b2dfe9SBalazs Scheidler memset(sin.sin_zero, 0, sizeof(sin.sin_zero));
165e8b2dfe9SBalazs Scheidler
166e8b2dfe9SBalazs Scheidler put_cmsg(msg, SOL_IP, IP_ORIGDSTADDR, sizeof(sin), &sin);
167e8b2dfe9SBalazs Scheidler }
1681da177e4SLinus Torvalds
ip_cmsg_recv_offset(struct msghdr * msg,struct sock * sk,struct sk_buff * skb,int tlen,int offset)169ad959036SPaolo Abeni void ip_cmsg_recv_offset(struct msghdr *msg, struct sock *sk,
170ad959036SPaolo Abeni struct sk_buff *skb, int tlen, int offset)
1711da177e4SLinus Torvalds {
172c274af22SEric Dumazet unsigned long flags = inet_cmsg_flags(inet_sk(sk));
173c274af22SEric Dumazet
174c274af22SEric Dumazet if (!flags)
175c274af22SEric Dumazet return;
1761da177e4SLinus Torvalds
1771da177e4SLinus Torvalds /* Ordered by supposed usage frequency */
178c44d13d6STom Herbert if (flags & IP_CMSG_PKTINFO) {
1791da177e4SLinus Torvalds ip_cmsg_recv_pktinfo(msg, skb);
1801da177e4SLinus Torvalds
181c44d13d6STom Herbert flags &= ~IP_CMSG_PKTINFO;
182c44d13d6STom Herbert if (!flags)
183c44d13d6STom Herbert return;
184c44d13d6STom Herbert }
185c44d13d6STom Herbert
186c44d13d6STom Herbert if (flags & IP_CMSG_TTL) {
1871da177e4SLinus Torvalds ip_cmsg_recv_ttl(msg, skb);
1881da177e4SLinus Torvalds
189c44d13d6STom Herbert flags &= ~IP_CMSG_TTL;
190c44d13d6STom Herbert if (!flags)
191c44d13d6STom Herbert return;
192c44d13d6STom Herbert }
193c44d13d6STom Herbert
194c44d13d6STom Herbert if (flags & IP_CMSG_TOS) {
1951da177e4SLinus Torvalds ip_cmsg_recv_tos(msg, skb);
1961da177e4SLinus Torvalds
197c44d13d6STom Herbert flags &= ~IP_CMSG_TOS;
198c44d13d6STom Herbert if (!flags)
199c44d13d6STom Herbert return;
200c44d13d6STom Herbert }
201c44d13d6STom Herbert
202c44d13d6STom Herbert if (flags & IP_CMSG_RECVOPTS) {
2031da177e4SLinus Torvalds ip_cmsg_recv_opts(msg, skb);
2041da177e4SLinus Torvalds
205c44d13d6STom Herbert flags &= ~IP_CMSG_RECVOPTS;
206c44d13d6STom Herbert if (!flags)
207c44d13d6STom Herbert return;
208c44d13d6STom Herbert }
209c44d13d6STom Herbert
210c44d13d6STom Herbert if (flags & IP_CMSG_RETOPTS) {
21191ed1e66SPaolo Abeni ip_cmsg_recv_retopts(sock_net(sk), msg, skb);
2122c7946a7SCatherine Zhang
213c44d13d6STom Herbert flags &= ~IP_CMSG_RETOPTS;
214c44d13d6STom Herbert if (!flags)
215c44d13d6STom Herbert return;
216c44d13d6STom Herbert }
217c44d13d6STom Herbert
218c44d13d6STom Herbert if (flags & IP_CMSG_PASSSEC) {
2192c7946a7SCatherine Zhang ip_cmsg_recv_security(msg, skb);
220e8b2dfe9SBalazs Scheidler
221c44d13d6STom Herbert flags &= ~IP_CMSG_PASSSEC;
222c44d13d6STom Herbert if (!flags)
223e8b2dfe9SBalazs Scheidler return;
224c44d13d6STom Herbert }
225c44d13d6STom Herbert
226ad6f939aSTom Herbert if (flags & IP_CMSG_ORIGDSTADDR) {
227e8b2dfe9SBalazs Scheidler ip_cmsg_recv_dstaddr(msg, skb);
228e8b2dfe9SBalazs Scheidler
229ad6f939aSTom Herbert flags &= ~IP_CMSG_ORIGDSTADDR;
230ad6f939aSTom Herbert if (!flags)
231ad6f939aSTom Herbert return;
232ad6f939aSTom Herbert }
233ad6f939aSTom Herbert
234ad6f939aSTom Herbert if (flags & IP_CMSG_CHECKSUM)
23510df8e61SEric Dumazet ip_cmsg_recv_checksum(msg, skb, tlen, offset);
23670ecc248SWillem de Bruijn
23770ecc248SWillem de Bruijn if (flags & IP_CMSG_RECVFRAGSIZE)
23870ecc248SWillem de Bruijn ip_cmsg_recv_fragsize(msg, skb);
2391da177e4SLinus Torvalds }
2405961de9fSTom Herbert EXPORT_SYMBOL(ip_cmsg_recv_offset);
2411da177e4SLinus Torvalds
ip_cmsg_send(struct sock * sk,struct msghdr * msg,struct ipcm_cookie * ipc,bool allow_ipv6)24224025c46SSoheil Hassas Yeganeh int ip_cmsg_send(struct sock *sk, struct msghdr *msg, struct ipcm_cookie *ipc,
243c8e6ad08SHannes Frederic Sowa bool allow_ipv6)
2441da177e4SLinus Torvalds {
245f02db315SFrancesco Fusco int err, val;
2461da177e4SLinus Torvalds struct cmsghdr *cmsg;
24724025c46SSoheil Hassas Yeganeh struct net *net = sock_net(sk);
2481da177e4SLinus Torvalds
249f95b414eSGu Zheng for_each_cmsghdr(cmsg, msg) {
2501da177e4SLinus Torvalds if (!CMSG_OK(msg, cmsg))
2511da177e4SLinus Torvalds return -EINVAL;
2525337b5b7SEric Dumazet #if IS_ENABLED(CONFIG_IPV6)
253c8e6ad08SHannes Frederic Sowa if (allow_ipv6 &&
254c8e6ad08SHannes Frederic Sowa cmsg->cmsg_level == SOL_IPV6 &&
255c8e6ad08SHannes Frederic Sowa cmsg->cmsg_type == IPV6_PKTINFO) {
256c8e6ad08SHannes Frederic Sowa struct in6_pktinfo *src_info;
257c8e6ad08SHannes Frederic Sowa
258c8e6ad08SHannes Frederic Sowa if (cmsg->cmsg_len < CMSG_LEN(sizeof(*src_info)))
259c8e6ad08SHannes Frederic Sowa return -EINVAL;
260c8e6ad08SHannes Frederic Sowa src_info = (struct in6_pktinfo *)CMSG_DATA(cmsg);
261c8e6ad08SHannes Frederic Sowa if (!ipv6_addr_v4mapped(&src_info->ipi6_addr))
262c8e6ad08SHannes Frederic Sowa return -EINVAL;
2631cbec076SDavid Ahern if (src_info->ipi6_ifindex)
264c8e6ad08SHannes Frederic Sowa ipc->oif = src_info->ipi6_ifindex;
265c8e6ad08SHannes Frederic Sowa ipc->addr = src_info->ipi6_addr.s6_addr32[3];
266c8e6ad08SHannes Frederic Sowa continue;
267c8e6ad08SHannes Frederic Sowa }
268c8e6ad08SHannes Frederic Sowa #endif
26924025c46SSoheil Hassas Yeganeh if (cmsg->cmsg_level == SOL_SOCKET) {
270233baf9aSxu xin err = __sock_cmsg_send(sk, cmsg, &ipc->sockc);
2712632616bSEric Dumazet if (err)
2722632616bSEric Dumazet return err;
27324025c46SSoheil Hassas Yeganeh continue;
27424025c46SSoheil Hassas Yeganeh }
27524025c46SSoheil Hassas Yeganeh
2761da177e4SLinus Torvalds if (cmsg->cmsg_level != SOL_IP)
2771da177e4SLinus Torvalds continue;
2781da177e4SLinus Torvalds switch (cmsg->cmsg_type) {
2791da177e4SLinus Torvalds case IP_RETOPTS:
2801ff8cebfSyuan linyu err = cmsg->cmsg_len - sizeof(struct cmsghdr);
28191948309SEric Dumazet
28291948309SEric Dumazet /* Our caller is responsible for freeing ipc->opt */
283de40a3e8SChristoph Hellwig err = ip_options_get(net, &ipc->opt,
284de40a3e8SChristoph Hellwig KERNEL_SOCKPTR(CMSG_DATA(cmsg)),
2854d52cfbeSEric Dumazet err < 40 ? err : 40);
2861da177e4SLinus Torvalds if (err)
2871da177e4SLinus Torvalds return err;
2881da177e4SLinus Torvalds break;
2891da177e4SLinus Torvalds case IP_PKTINFO:
2901da177e4SLinus Torvalds {
2911da177e4SLinus Torvalds struct in_pktinfo *info;
2921da177e4SLinus Torvalds if (cmsg->cmsg_len != CMSG_LEN(sizeof(struct in_pktinfo)))
2931da177e4SLinus Torvalds return -EINVAL;
2941da177e4SLinus Torvalds info = (struct in_pktinfo *)CMSG_DATA(cmsg);
2951cbec076SDavid Ahern if (info->ipi_ifindex)
2961da177e4SLinus Torvalds ipc->oif = info->ipi_ifindex;
2971da177e4SLinus Torvalds ipc->addr = info->ipi_spec_dst.s_addr;
2981da177e4SLinus Torvalds break;
2991da177e4SLinus Torvalds }
300f02db315SFrancesco Fusco case IP_TTL:
301f02db315SFrancesco Fusco if (cmsg->cmsg_len != CMSG_LEN(sizeof(int)))
302f02db315SFrancesco Fusco return -EINVAL;
303f02db315SFrancesco Fusco val = *(int *)CMSG_DATA(cmsg);
304f02db315SFrancesco Fusco if (val < 1 || val > 255)
305f02db315SFrancesco Fusco return -EINVAL;
306f02db315SFrancesco Fusco ipc->ttl = val;
307f02db315SFrancesco Fusco break;
308f02db315SFrancesco Fusco case IP_TOS:
309e895cdceSEric Dumazet if (cmsg->cmsg_len == CMSG_LEN(sizeof(int)))
310f02db315SFrancesco Fusco val = *(int *)CMSG_DATA(cmsg);
311e895cdceSEric Dumazet else if (cmsg->cmsg_len == CMSG_LEN(sizeof(u8)))
312e895cdceSEric Dumazet val = *(u8 *)CMSG_DATA(cmsg);
313e895cdceSEric Dumazet else
314e895cdceSEric Dumazet return -EINVAL;
315f02db315SFrancesco Fusco if (val < 0 || val > 255)
316f02db315SFrancesco Fusco return -EINVAL;
317f02db315SFrancesco Fusco ipc->tos = val;
318f02db315SFrancesco Fusco ipc->priority = rt_tos2priority(ipc->tos);
319f02db315SFrancesco Fusco break;
3203632679dSNicolas Dichtel case IP_PROTOCOL:
3213632679dSNicolas Dichtel if (cmsg->cmsg_len != CMSG_LEN(sizeof(int)))
3223632679dSNicolas Dichtel return -EINVAL;
3233632679dSNicolas Dichtel val = *(int *)CMSG_DATA(cmsg);
3243632679dSNicolas Dichtel if (val < 1 || val > 255)
3253632679dSNicolas Dichtel return -EINVAL;
3263632679dSNicolas Dichtel ipc->protocol = val;
3273632679dSNicolas Dichtel break;
3281da177e4SLinus Torvalds default:
3291da177e4SLinus Torvalds return -EINVAL;
3301da177e4SLinus Torvalds }
3311da177e4SLinus Torvalds }
3321da177e4SLinus Torvalds return 0;
3331da177e4SLinus Torvalds }
3341da177e4SLinus Torvalds
ip_ra_destroy_rcu(struct rcu_head * head)335592fcb9dSEric Dumazet static void ip_ra_destroy_rcu(struct rcu_head *head)
33666018506SEric Dumazet {
337592fcb9dSEric Dumazet struct ip_ra_chain *ra = container_of(head, struct ip_ra_chain, rcu);
338592fcb9dSEric Dumazet
339592fcb9dSEric Dumazet sock_put(ra->saved_sk);
340592fcb9dSEric Dumazet kfree(ra);
34166018506SEric Dumazet }
3421da177e4SLinus Torvalds
ip_ra_control(struct sock * sk,unsigned char on,void (* destructor)(struct sock *))3434d52cfbeSEric Dumazet int ip_ra_control(struct sock *sk, unsigned char on,
3444d52cfbeSEric Dumazet void (*destructor)(struct sock *))
3451da177e4SLinus Torvalds {
34643a951e9SEric Dumazet struct ip_ra_chain *ra, *new_ra;
34743a951e9SEric Dumazet struct ip_ra_chain __rcu **rap;
3485796ef75SKirill Tkhai struct net *net = sock_net(sk);
3491da177e4SLinus Torvalds
350c720c7e8SEric Dumazet if (sk->sk_type != SOCK_RAW || inet_sk(sk)->inet_num == IPPROTO_RAW)
3511da177e4SLinus Torvalds return -EINVAL;
3521da177e4SLinus Torvalds
3531da177e4SLinus Torvalds new_ra = on ? kmalloc(sizeof(*new_ra), GFP_KERNEL) : NULL;
354425aa0e1SGen Zhang if (on && !new_ra)
355425aa0e1SGen Zhang return -ENOMEM;
3561da177e4SLinus Torvalds
357d9ff3049SKirill Tkhai mutex_lock(&net->ipv4.ra_mutex);
3585796ef75SKirill Tkhai for (rap = &net->ipv4.ra_chain;
35976d3e153SKirill Tkhai (ra = rcu_dereference_protected(*rap,
360d9ff3049SKirill Tkhai lockdep_is_held(&net->ipv4.ra_mutex))) != NULL;
36143a951e9SEric Dumazet rap = &ra->next) {
3621da177e4SLinus Torvalds if (ra->sk == sk) {
3631da177e4SLinus Torvalds if (on) {
364d9ff3049SKirill Tkhai mutex_unlock(&net->ipv4.ra_mutex);
3651da177e4SLinus Torvalds kfree(new_ra);
3661da177e4SLinus Torvalds return -EADDRINUSE;
3671da177e4SLinus Torvalds }
368592fcb9dSEric Dumazet /* dont let ip_call_ra_chain() use sk again */
369592fcb9dSEric Dumazet ra->sk = NULL;
3708e380f00SEric Dumazet RCU_INIT_POINTER(*rap, ra->next);
371d9ff3049SKirill Tkhai mutex_unlock(&net->ipv4.ra_mutex);
3721da177e4SLinus Torvalds
3731da177e4SLinus Torvalds if (ra->destructor)
3741da177e4SLinus Torvalds ra->destructor(sk);
375592fcb9dSEric Dumazet /*
376592fcb9dSEric Dumazet * Delay sock_put(sk) and kfree(ra) after one rcu grace
377592fcb9dSEric Dumazet * period. This guarantee ip_call_ra_chain() dont need
378592fcb9dSEric Dumazet * to mess with socket refcounts.
379592fcb9dSEric Dumazet */
380592fcb9dSEric Dumazet ra->saved_sk = sk;
381592fcb9dSEric Dumazet call_rcu(&ra->rcu, ip_ra_destroy_rcu);
3821da177e4SLinus Torvalds return 0;
3831da177e4SLinus Torvalds }
3841da177e4SLinus Torvalds }
38576d3e153SKirill Tkhai if (!new_ra) {
386d9ff3049SKirill Tkhai mutex_unlock(&net->ipv4.ra_mutex);
3871da177e4SLinus Torvalds return -ENOBUFS;
38876d3e153SKirill Tkhai }
3891da177e4SLinus Torvalds new_ra->sk = sk;
3901da177e4SLinus Torvalds new_ra->destructor = destructor;
3911da177e4SLinus Torvalds
3928e380f00SEric Dumazet RCU_INIT_POINTER(new_ra->next, ra);
39366018506SEric Dumazet rcu_assign_pointer(*rap, new_ra);
3941da177e4SLinus Torvalds sock_hold(sk);
395d9ff3049SKirill Tkhai mutex_unlock(&net->ipv4.ra_mutex);
3961da177e4SLinus Torvalds
3971da177e4SLinus Torvalds return 0;
3981da177e4SLinus Torvalds }
3991da177e4SLinus Torvalds
ipv4_icmp_error_rfc4884(const struct sk_buff * skb,struct sock_ee_data_rfc4884 * out)400178c49d9SWillem de Bruijn static void ipv4_icmp_error_rfc4884(const struct sk_buff *skb,
401178c49d9SWillem de Bruijn struct sock_ee_data_rfc4884 *out)
402178c49d9SWillem de Bruijn {
403178c49d9SWillem de Bruijn switch (icmp_hdr(skb)->type) {
404178c49d9SWillem de Bruijn case ICMP_DEST_UNREACH:
405178c49d9SWillem de Bruijn case ICMP_TIME_EXCEEDED:
406178c49d9SWillem de Bruijn case ICMP_PARAMETERPROB:
407178c49d9SWillem de Bruijn ip_icmp_error_rfc4884(skb, out, sizeof(struct icmphdr),
408178c49d9SWillem de Bruijn icmp_hdr(skb)->un.reserved[1] * 4);
409178c49d9SWillem de Bruijn }
410178c49d9SWillem de Bruijn }
411178c49d9SWillem de Bruijn
ip_icmp_error(struct sock * sk,struct sk_buff * skb,int err,__be16 port,u32 info,u8 * payload)4121da177e4SLinus Torvalds void ip_icmp_error(struct sock *sk, struct sk_buff *skb, int err,
41335986b32SAl Viro __be16 port, u32 info, u8 *payload)
4141da177e4SLinus Torvalds {
4151da177e4SLinus Torvalds struct sock_exterr_skb *serr;
4161da177e4SLinus Torvalds
4171da177e4SLinus Torvalds skb = skb_clone(skb, GFP_ATOMIC);
4181da177e4SLinus Torvalds if (!skb)
4191da177e4SLinus Torvalds return;
4201da177e4SLinus Torvalds
4211da177e4SLinus Torvalds serr = SKB_EXT_ERR(skb);
4221da177e4SLinus Torvalds serr->ee.ee_errno = err;
4231da177e4SLinus Torvalds serr->ee.ee_origin = SO_EE_ORIGIN_ICMP;
42488c7664fSArnaldo Carvalho de Melo serr->ee.ee_type = icmp_hdr(skb)->type;
42588c7664fSArnaldo Carvalho de Melo serr->ee.ee_code = icmp_hdr(skb)->code;
4261da177e4SLinus Torvalds serr->ee.ee_pad = 0;
4271da177e4SLinus Torvalds serr->ee.ee_info = info;
4281da177e4SLinus Torvalds serr->ee.ee_data = 0;
42988c7664fSArnaldo Carvalho de Melo serr->addr_offset = (u8 *)&(((struct iphdr *)(icmp_hdr(skb) + 1))->daddr) -
430d56f90a7SArnaldo Carvalho de Melo skb_network_header(skb);
4311da177e4SLinus Torvalds serr->port = port;
4321da177e4SLinus Torvalds
43300db4124SIan Morris if (skb_pull(skb, payload - skb->data)) {
4348e8cfb11SEric Dumazet if (inet_test_bit(RECVERR_RFC4884, sk))
435178c49d9SWillem de Bruijn ipv4_icmp_error_rfc4884(skb, &serr->ee.ee_rfc4884);
436eba75c58SWillem de Bruijn
437bd82393cSArnaldo Carvalho de Melo skb_reset_transport_header(skb);
438bd82393cSArnaldo Carvalho de Melo if (sock_queue_err_skb(sk, skb) == 0)
439bd82393cSArnaldo Carvalho de Melo return;
440bd82393cSArnaldo Carvalho de Melo }
4411da177e4SLinus Torvalds kfree_skb(skb);
4421da177e4SLinus Torvalds }
44342fb06b3SDavid Howells EXPORT_SYMBOL_GPL(ip_icmp_error);
4441da177e4SLinus Torvalds
ip_local_error(struct sock * sk,int err,__be32 daddr,__be16 port,u32 info)4450579016eSAl Viro void ip_local_error(struct sock *sk, int err, __be32 daddr, __be16 port, u32 info)
4461da177e4SLinus Torvalds {
4471da177e4SLinus Torvalds struct sock_exterr_skb *serr;
4481da177e4SLinus Torvalds struct iphdr *iph;
4491da177e4SLinus Torvalds struct sk_buff *skb;
4501da177e4SLinus Torvalds
4516b5f43eaSEric Dumazet if (!inet_test_bit(RECVERR, sk))
4521da177e4SLinus Torvalds return;
4531da177e4SLinus Torvalds
4541da177e4SLinus Torvalds skb = alloc_skb(sizeof(struct iphdr), GFP_ATOMIC);
4551da177e4SLinus Torvalds if (!skb)
4561da177e4SLinus Torvalds return;
4571da177e4SLinus Torvalds
4582ca9e6f2SArnaldo Carvalho de Melo skb_put(skb, sizeof(struct iphdr));
4592ca9e6f2SArnaldo Carvalho de Melo skb_reset_network_header(skb);
460eddc9ec5SArnaldo Carvalho de Melo iph = ip_hdr(skb);
4611da177e4SLinus Torvalds iph->daddr = daddr;
4621da177e4SLinus Torvalds
4631da177e4SLinus Torvalds serr = SKB_EXT_ERR(skb);
4641da177e4SLinus Torvalds serr->ee.ee_errno = err;
4651da177e4SLinus Torvalds serr->ee.ee_origin = SO_EE_ORIGIN_LOCAL;
4661da177e4SLinus Torvalds serr->ee.ee_type = 0;
4671da177e4SLinus Torvalds serr->ee.ee_code = 0;
4681da177e4SLinus Torvalds serr->ee.ee_pad = 0;
4691da177e4SLinus Torvalds serr->ee.ee_info = info;
4701da177e4SLinus Torvalds serr->ee.ee_data = 0;
471d56f90a7SArnaldo Carvalho de Melo serr->addr_offset = (u8 *)&iph->daddr - skb_network_header(skb);
4721da177e4SLinus Torvalds serr->port = port;
4731da177e4SLinus Torvalds
47427a884dcSArnaldo Carvalho de Melo __skb_pull(skb, skb_tail_pointer(skb) - skb->data);
475bd82393cSArnaldo Carvalho de Melo skb_reset_transport_header(skb);
4761da177e4SLinus Torvalds
4771da177e4SLinus Torvalds if (sock_queue_err_skb(sk, skb))
4781da177e4SLinus Torvalds kfree_skb(skb);
4791da177e4SLinus Torvalds }
4801da177e4SLinus Torvalds
48134b99df4SJulian Anastasov /* For some errors we have valid addr_offset even with zero payload and
48234b99df4SJulian Anastasov * zero port. Also, addr_offset should be supported if port is set.
48334b99df4SJulian Anastasov */
ipv4_datagram_support_addr(struct sock_exterr_skb * serr)48434b99df4SJulian Anastasov static inline bool ipv4_datagram_support_addr(struct sock_exterr_skb *serr)
48534b99df4SJulian Anastasov {
48634b99df4SJulian Anastasov return serr->ee.ee_origin == SO_EE_ORIGIN_ICMP ||
48734b99df4SJulian Anastasov serr->ee.ee_origin == SO_EE_ORIGIN_LOCAL || serr->port;
48834b99df4SJulian Anastasov }
48934b99df4SJulian Anastasov
490c247f053SWillem de Bruijn /* IPv4 supports cmsg on all imcp errors and some timestamps
491c247f053SWillem de Bruijn *
492c247f053SWillem de Bruijn * Timestamp code paths do not initialize the fields expected by cmsg:
493c247f053SWillem de Bruijn * the PKTINFO fields in skb->cb[]. Fill those in here.
494c247f053SWillem de Bruijn */
ipv4_datagram_support_cmsg(const struct sock * sk,struct sk_buff * skb,int ee_origin)495c247f053SWillem de Bruijn static bool ipv4_datagram_support_cmsg(const struct sock *sk,
496c247f053SWillem de Bruijn struct sk_buff *skb,
497829ae9d6SWillem de Bruijn int ee_origin)
498829ae9d6SWillem de Bruijn {
499c247f053SWillem de Bruijn struct in_pktinfo *info;
500829ae9d6SWillem de Bruijn
501c247f053SWillem de Bruijn if (ee_origin == SO_EE_ORIGIN_ICMP)
502c247f053SWillem de Bruijn return true;
503c247f053SWillem de Bruijn
504c247f053SWillem de Bruijn if (ee_origin == SO_EE_ORIGIN_LOCAL)
505c247f053SWillem de Bruijn return false;
506c247f053SWillem de Bruijn
507c247f053SWillem de Bruijn /* Support IP_PKTINFO on tstamp packets if requested, to correlate
5081862d620SWillem de Bruijn * timestamp with egress dev. Not possible for packets without iif
509c247f053SWillem de Bruijn * or without payload (SOF_TIMESTAMPING_OPT_TSONLY).
510c247f053SWillem de Bruijn */
5111862d620SWillem de Bruijn info = PKTINFO_SKB_CB(skb);
512e3390b30SEric Dumazet if (!(READ_ONCE(sk->sk_tsflags) & SOF_TIMESTAMPING_OPT_CMSG) ||
5131862d620SWillem de Bruijn !info->ipi_ifindex)
514829ae9d6SWillem de Bruijn return false;
515829ae9d6SWillem de Bruijn
516829ae9d6SWillem de Bruijn info->ipi_spec_dst.s_addr = ip_hdr(skb)->saddr;
517829ae9d6SWillem de Bruijn return true;
518829ae9d6SWillem de Bruijn }
519829ae9d6SWillem de Bruijn
5201da177e4SLinus Torvalds /*
5211da177e4SLinus Torvalds * Handle MSG_ERRQUEUE
5221da177e4SLinus Torvalds */
ip_recv_error(struct sock * sk,struct msghdr * msg,int len,int * addr_len)52385fbaa75SHannes Frederic Sowa int ip_recv_error(struct sock *sk, struct msghdr *msg, int len, int *addr_len)
5241da177e4SLinus Torvalds {
5251da177e4SLinus Torvalds struct sock_exterr_skb *serr;
526364a9e93SWillem de Bruijn struct sk_buff *skb;
527342dfc30SSteffen Hurrle DECLARE_SOCKADDR(struct sockaddr_in *, sin, msg->msg_name);
5281da177e4SLinus Torvalds struct {
5291da177e4SLinus Torvalds struct sock_extended_err ee;
5301da177e4SLinus Torvalds struct sockaddr_in offender;
5311da177e4SLinus Torvalds } errhdr;
5321da177e4SLinus Torvalds int err;
5331da177e4SLinus Torvalds int copied;
5341da177e4SLinus Torvalds
5351da177e4SLinus Torvalds err = -EAGAIN;
536364a9e93SWillem de Bruijn skb = sock_dequeue_err_skb(sk);
53751456b29SIan Morris if (!skb)
5381da177e4SLinus Torvalds goto out;
5391da177e4SLinus Torvalds
5401da177e4SLinus Torvalds copied = skb->len;
5411da177e4SLinus Torvalds if (copied > len) {
5421da177e4SLinus Torvalds msg->msg_flags |= MSG_TRUNC;
5431da177e4SLinus Torvalds copied = len;
5441da177e4SLinus Torvalds }
54551f3d02bSDavid S. Miller err = skb_copy_datagram_msg(skb, 0, msg, copied);
546960a2628SEric Dumazet if (unlikely(err)) {
547960a2628SEric Dumazet kfree_skb(skb);
548960a2628SEric Dumazet return err;
549960a2628SEric Dumazet }
5501da177e4SLinus Torvalds sock_recv_timestamp(msg, sk, skb);
5511da177e4SLinus Torvalds
5521da177e4SLinus Torvalds serr = SKB_EXT_ERR(skb);
5531da177e4SLinus Torvalds
55434b99df4SJulian Anastasov if (sin && ipv4_datagram_support_addr(serr)) {
5551da177e4SLinus Torvalds sin->sin_family = AF_INET;
556d56f90a7SArnaldo Carvalho de Melo sin->sin_addr.s_addr = *(__be32 *)(skb_network_header(skb) +
557d56f90a7SArnaldo Carvalho de Melo serr->addr_offset);
5581da177e4SLinus Torvalds sin->sin_port = serr->port;
5591da177e4SLinus Torvalds memset(&sin->sin_zero, 0, sizeof(sin->sin_zero));
56085fbaa75SHannes Frederic Sowa *addr_len = sizeof(*sin);
5611da177e4SLinus Torvalds }
5621da177e4SLinus Torvalds
5631da177e4SLinus Torvalds memcpy(&errhdr.ee, &serr->ee, sizeof(struct sock_extended_err));
5641da177e4SLinus Torvalds sin = &errhdr.offender;
565f812116bSWillem de Bruijn memset(sin, 0, sizeof(*sin));
566829ae9d6SWillem de Bruijn
567c247f053SWillem de Bruijn if (ipv4_datagram_support_cmsg(sk, skb, serr->ee.ee_origin)) {
5681da177e4SLinus Torvalds sin->sin_family = AF_INET;
569eddc9ec5SArnaldo Carvalho de Melo sin->sin_addr.s_addr = ip_hdr(skb)->saddr;
570c274af22SEric Dumazet if (inet_cmsg_flags(inet_sk(sk)))
5711da177e4SLinus Torvalds ip_cmsg_recv(msg, skb);
5721da177e4SLinus Torvalds }
5731da177e4SLinus Torvalds
5741da177e4SLinus Torvalds put_cmsg(msg, SOL_IP, IP_RECVERR, sizeof(errhdr), &errhdr);
5751da177e4SLinus Torvalds
5761da177e4SLinus Torvalds /* Now we could try to dump offended packet options */
5771da177e4SLinus Torvalds
5781da177e4SLinus Torvalds msg->msg_flags |= MSG_ERRQUEUE;
5791da177e4SLinus Torvalds err = copied;
5801da177e4SLinus Torvalds
581960a2628SEric Dumazet consume_skb(skb);
5821da177e4SLinus Torvalds out:
5831da177e4SLinus Torvalds return err;
5841da177e4SLinus Torvalds }
5851da177e4SLinus Torvalds
__ip_sock_set_tos(struct sock * sk,int val)586878d951cSEric Dumazet void __ip_sock_set_tos(struct sock *sk, int val)
5876ebf71baSChristoph Hellwig {
588878d951cSEric Dumazet u8 old_tos = inet_sk(sk)->tos;
589e08d0b3dSEric Dumazet
5906ebf71baSChristoph Hellwig if (sk->sk_type == SOCK_STREAM) {
5916ebf71baSChristoph Hellwig val &= ~INET_ECN_MASK;
592e08d0b3dSEric Dumazet val |= old_tos & INET_ECN_MASK;
5936ebf71baSChristoph Hellwig }
594e08d0b3dSEric Dumazet if (old_tos != val) {
595e08d0b3dSEric Dumazet WRITE_ONCE(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 }
600878d951cSEric Dumazet
ip_sock_set_tos(struct sock * sk,int val)601878d951cSEric Dumazet void ip_sock_set_tos(struct sock *sk, int val)
602878d951cSEric Dumazet {
60306497763SYonghong Song sockopt_lock_sock(sk);
604878d951cSEric Dumazet __ip_sock_set_tos(sk, val);
60506497763SYonghong Song sockopt_release_sock(sk);
606878d951cSEric Dumazet }
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;
625ceaa7141SEric Dumazet WRITE_ONCE(inet_sk(sk)->pmtudisc, val);
6262de569bdSChristoph Hellwig return 0;
6272de569bdSChristoph Hellwig }
6282de569bdSChristoph Hellwig EXPORT_SYMBOL(ip_sock_set_mtu_discover);
6292de569bdSChristoph Hellwig
ip_sock_set_pktinfo(struct sock * sk)630c1f9ec57SChristoph Hellwig void ip_sock_set_pktinfo(struct sock *sk)
631c1f9ec57SChristoph Hellwig {
632c274af22SEric Dumazet inet_set_bit(PKTINFO, sk);
633c1f9ec57SChristoph Hellwig }
634c1f9ec57SChristoph Hellwig EXPORT_SYMBOL(ip_sock_set_pktinfo);
635c1f9ec57SChristoph Hellwig
6361da177e4SLinus Torvalds /*
6374d52cfbeSEric Dumazet * Socket option code for IP. This is the end of the line after any
6384d52cfbeSEric Dumazet * TCP,UDP etc options on an IP socket.
6391da177e4SLinus Torvalds */
setsockopt_needs_rtnl(int optname)640baf606d9SMarcelo Ricardo Leitner static bool setsockopt_needs_rtnl(int optname)
641baf606d9SMarcelo Ricardo Leitner {
642baf606d9SMarcelo Ricardo Leitner switch (optname) {
643baf606d9SMarcelo Ricardo Leitner case IP_ADD_MEMBERSHIP:
644baf606d9SMarcelo Ricardo Leitner case IP_ADD_SOURCE_MEMBERSHIP:
64554ff9ef3SMarcelo Ricardo Leitner case IP_BLOCK_SOURCE:
646baf606d9SMarcelo Ricardo Leitner case IP_DROP_MEMBERSHIP:
64754ff9ef3SMarcelo Ricardo Leitner case IP_DROP_SOURCE_MEMBERSHIP:
64854ff9ef3SMarcelo Ricardo Leitner case IP_MSFILTER:
64954ff9ef3SMarcelo Ricardo Leitner case IP_UNBLOCK_SOURCE:
65054ff9ef3SMarcelo Ricardo Leitner case MCAST_BLOCK_SOURCE:
65154ff9ef3SMarcelo Ricardo Leitner case MCAST_MSFILTER:
652baf606d9SMarcelo Ricardo Leitner case MCAST_JOIN_GROUP:
65354ff9ef3SMarcelo Ricardo Leitner case MCAST_JOIN_SOURCE_GROUP:
654baf606d9SMarcelo Ricardo Leitner case MCAST_LEAVE_GROUP:
65554ff9ef3SMarcelo Ricardo Leitner case MCAST_LEAVE_SOURCE_GROUP:
65654ff9ef3SMarcelo Ricardo Leitner case MCAST_UNBLOCK_SOURCE:
657baf606d9SMarcelo Ricardo Leitner return true;
658baf606d9SMarcelo Ricardo Leitner }
659baf606d9SMarcelo Ricardo Leitner return false;
660baf606d9SMarcelo Ricardo Leitner }
6611da177e4SLinus Torvalds
set_mcast_msfilter(struct sock * sk,int ifindex,int numsrc,int fmode,struct sockaddr_storage * group,struct sockaddr_storage * list)662e986d4daSAl Viro static int set_mcast_msfilter(struct sock *sk, int ifindex,
663e986d4daSAl Viro int numsrc, int fmode,
664e986d4daSAl Viro struct sockaddr_storage *group,
665e986d4daSAl Viro struct sockaddr_storage *list)
666e986d4daSAl Viro {
667e986d4daSAl Viro struct ip_msfilter *msf;
668e986d4daSAl Viro struct sockaddr_in *psin;
669e986d4daSAl Viro int err, i;
670e986d4daSAl Viro
6714167a960SGustavo A. R. Silva msf = kmalloc(IP_MSFILTER_SIZE(numsrc), GFP_KERNEL);
672e986d4daSAl Viro if (!msf)
673e986d4daSAl Viro return -ENOBUFS;
674e986d4daSAl Viro
675e986d4daSAl Viro psin = (struct sockaddr_in *)group;
676e986d4daSAl Viro if (psin->sin_family != AF_INET)
677e986d4daSAl Viro goto Eaddrnotavail;
678e986d4daSAl Viro msf->imsf_multiaddr = psin->sin_addr.s_addr;
679e986d4daSAl Viro msf->imsf_interface = 0;
680e986d4daSAl Viro msf->imsf_fmode = fmode;
681e986d4daSAl Viro msf->imsf_numsrc = numsrc;
682e986d4daSAl Viro for (i = 0; i < numsrc; ++i) {
683e986d4daSAl Viro psin = (struct sockaddr_in *)&list[i];
684e986d4daSAl Viro
685e986d4daSAl Viro if (psin->sin_family != AF_INET)
686e986d4daSAl Viro goto Eaddrnotavail;
6872d3e5cafSGustavo A. R. Silva msf->imsf_slist_flex[i] = psin->sin_addr.s_addr;
688e986d4daSAl Viro }
689e986d4daSAl Viro err = ip_mc_msfilter(sk, msf, ifindex);
690e986d4daSAl Viro kfree(msf);
691e986d4daSAl Viro return err;
692e986d4daSAl Viro
693e986d4daSAl Viro Eaddrnotavail:
694e986d4daSAl Viro kfree(msf);
695e986d4daSAl Viro return -EADDRNOTAVAIL;
696e986d4daSAl Viro }
697e986d4daSAl Viro
copy_group_source_from_sockptr(struct group_source_req * greqs,sockptr_t optval,int optlen)69889654c5fSChristoph Hellwig static int copy_group_source_from_sockptr(struct group_source_req *greqs,
69989654c5fSChristoph Hellwig sockptr_t optval, int optlen)
7002bbf8c1eSAl Viro {
701b6238c04SChristoph Hellwig if (in_compat_syscall()) {
702b6238c04SChristoph Hellwig struct compat_group_source_req gr32;
703b6238c04SChristoph Hellwig
704b6238c04SChristoph Hellwig if (optlen != sizeof(gr32))
705b6238c04SChristoph Hellwig return -EINVAL;
70689654c5fSChristoph Hellwig if (copy_from_sockptr(&gr32, optval, sizeof(gr32)))
707b6238c04SChristoph Hellwig return -EFAULT;
708b6238c04SChristoph Hellwig greqs->gsr_interface = gr32.gsr_interface;
709b6238c04SChristoph Hellwig greqs->gsr_group = gr32.gsr_group;
710b6238c04SChristoph Hellwig greqs->gsr_source = gr32.gsr_source;
711b6238c04SChristoph Hellwig } else {
712b6238c04SChristoph Hellwig if (optlen != sizeof(*greqs))
713b6238c04SChristoph Hellwig return -EINVAL;
71489654c5fSChristoph Hellwig if (copy_from_sockptr(greqs, optval, sizeof(*greqs)))
715b6238c04SChristoph Hellwig return -EFAULT;
716b6238c04SChristoph Hellwig }
717b6238c04SChristoph Hellwig
718b6238c04SChristoph Hellwig return 0;
719b6238c04SChristoph Hellwig }
720b6238c04SChristoph Hellwig
do_mcast_group_source(struct sock * sk,int optname,sockptr_t optval,int optlen)721b6238c04SChristoph Hellwig static int do_mcast_group_source(struct sock *sk, int optname,
72289654c5fSChristoph Hellwig sockptr_t optval, int optlen)
723b6238c04SChristoph Hellwig {
724b6238c04SChristoph Hellwig struct group_source_req greqs;
7252bbf8c1eSAl Viro struct ip_mreq_source mreqs;
7262bbf8c1eSAl Viro struct sockaddr_in *psin;
7272bbf8c1eSAl Viro int omode, add, err;
7282bbf8c1eSAl Viro
72989654c5fSChristoph Hellwig err = copy_group_source_from_sockptr(&greqs, optval, optlen);
730b6238c04SChristoph Hellwig if (err)
731b6238c04SChristoph Hellwig return err;
732b6238c04SChristoph Hellwig
733b6238c04SChristoph Hellwig if (greqs.gsr_group.ss_family != AF_INET ||
734b6238c04SChristoph Hellwig greqs.gsr_source.ss_family != AF_INET)
7352bbf8c1eSAl Viro return -EADDRNOTAVAIL;
7362bbf8c1eSAl Viro
737b6238c04SChristoph Hellwig psin = (struct sockaddr_in *)&greqs.gsr_group;
7382bbf8c1eSAl Viro mreqs.imr_multiaddr = psin->sin_addr.s_addr;
739b6238c04SChristoph Hellwig psin = (struct sockaddr_in *)&greqs.gsr_source;
7402bbf8c1eSAl Viro mreqs.imr_sourceaddr = psin->sin_addr.s_addr;
7412bbf8c1eSAl Viro mreqs.imr_interface = 0; /* use index for mc_source */
7422bbf8c1eSAl Viro
7432bbf8c1eSAl Viro if (optname == MCAST_BLOCK_SOURCE) {
7442bbf8c1eSAl Viro omode = MCAST_EXCLUDE;
7452bbf8c1eSAl Viro add = 1;
7462bbf8c1eSAl Viro } else if (optname == MCAST_UNBLOCK_SOURCE) {
7472bbf8c1eSAl Viro omode = MCAST_EXCLUDE;
7482bbf8c1eSAl Viro add = 0;
7492bbf8c1eSAl Viro } else if (optname == MCAST_JOIN_SOURCE_GROUP) {
7502bbf8c1eSAl Viro struct ip_mreqn mreq;
7512bbf8c1eSAl Viro
752b6238c04SChristoph Hellwig psin = (struct sockaddr_in *)&greqs.gsr_group;
7532bbf8c1eSAl Viro mreq.imr_multiaddr = psin->sin_addr;
7542bbf8c1eSAl Viro mreq.imr_address.s_addr = 0;
755b6238c04SChristoph Hellwig mreq.imr_ifindex = greqs.gsr_interface;
7562bbf8c1eSAl Viro err = ip_mc_join_group_ssm(sk, &mreq, MCAST_INCLUDE);
7572bbf8c1eSAl Viro if (err && err != -EADDRINUSE)
7582bbf8c1eSAl Viro return err;
759b6238c04SChristoph Hellwig greqs.gsr_interface = mreq.imr_ifindex;
7602bbf8c1eSAl Viro omode = MCAST_INCLUDE;
7612bbf8c1eSAl Viro add = 1;
7622bbf8c1eSAl Viro } else /* MCAST_LEAVE_SOURCE_GROUP */ {
7632bbf8c1eSAl Viro omode = MCAST_INCLUDE;
7642bbf8c1eSAl Viro add = 0;
7652bbf8c1eSAl Viro }
766b6238c04SChristoph Hellwig return ip_mc_source(add, omode, sk, &mreqs, greqs.gsr_interface);
7672bbf8c1eSAl Viro }
7682bbf8c1eSAl Viro
ip_set_mcast_msfilter(struct sock * sk,sockptr_t optval,int optlen)76989654c5fSChristoph Hellwig static int ip_set_mcast_msfilter(struct sock *sk, sockptr_t optval, int optlen)
770d62c38f6SChristoph Hellwig {
771d62c38f6SChristoph Hellwig struct group_filter *gsf = NULL;
772d62c38f6SChristoph Hellwig int err;
773d62c38f6SChristoph Hellwig
774d62c38f6SChristoph Hellwig if (optlen < GROUP_FILTER_SIZE(0))
775d62c38f6SChristoph Hellwig return -EINVAL;
776f5769faeSEric Dumazet if (optlen > READ_ONCE(sock_net(sk)->core.sysctl_optmem_max))
777d62c38f6SChristoph Hellwig return -ENOBUFS;
778d62c38f6SChristoph Hellwig
77989654c5fSChristoph Hellwig gsf = memdup_sockptr(optval, optlen);
780d62c38f6SChristoph Hellwig if (IS_ERR(gsf))
781d62c38f6SChristoph Hellwig return PTR_ERR(gsf);
782d62c38f6SChristoph Hellwig
783d62c38f6SChristoph Hellwig /* numsrc >= (4G-140)/128 overflow in 32 bits */
784d62c38f6SChristoph Hellwig err = -ENOBUFS;
785d62c38f6SChristoph Hellwig if (gsf->gf_numsrc >= 0x1ffffff ||
7866ae0f2e5SKuniyuki Iwashima gsf->gf_numsrc > READ_ONCE(sock_net(sk)->ipv4.sysctl_igmp_max_msf))
787d62c38f6SChristoph Hellwig goto out_free_gsf;
788d62c38f6SChristoph Hellwig
789d62c38f6SChristoph Hellwig err = -EINVAL;
790d62c38f6SChristoph Hellwig if (GROUP_FILTER_SIZE(gsf->gf_numsrc) > optlen)
791d62c38f6SChristoph Hellwig goto out_free_gsf;
792d62c38f6SChristoph Hellwig
793d62c38f6SChristoph Hellwig err = set_mcast_msfilter(sk, gsf->gf_interface, gsf->gf_numsrc,
794db243b79SGustavo A. R. Silva gsf->gf_fmode, &gsf->gf_group,
795db243b79SGustavo A. R. Silva gsf->gf_slist_flex);
796d62c38f6SChristoph Hellwig out_free_gsf:
797d62c38f6SChristoph Hellwig kfree(gsf);
798d62c38f6SChristoph Hellwig return err;
799d62c38f6SChristoph Hellwig }
800d62c38f6SChristoph Hellwig
compat_ip_set_mcast_msfilter(struct sock * sk,sockptr_t optval,int optlen)80189654c5fSChristoph Hellwig static int compat_ip_set_mcast_msfilter(struct sock *sk, sockptr_t optval,
802d62c38f6SChristoph Hellwig int optlen)
803d62c38f6SChristoph Hellwig {
804db243b79SGustavo A. R. Silva const int size0 = offsetof(struct compat_group_filter, gf_slist_flex);
805d62c38f6SChristoph Hellwig struct compat_group_filter *gf32;
806d62c38f6SChristoph Hellwig unsigned int n;
807d62c38f6SChristoph Hellwig void *p;
808d62c38f6SChristoph Hellwig int err;
809d62c38f6SChristoph Hellwig
810d62c38f6SChristoph Hellwig if (optlen < size0)
811d62c38f6SChristoph Hellwig return -EINVAL;
812f5769faeSEric Dumazet if (optlen > READ_ONCE(sock_net(sk)->core.sysctl_optmem_max) - 4)
813d62c38f6SChristoph Hellwig return -ENOBUFS;
814d62c38f6SChristoph Hellwig
815d62c38f6SChristoph Hellwig p = kmalloc(optlen + 4, GFP_KERNEL);
816d62c38f6SChristoph Hellwig if (!p)
817d62c38f6SChristoph Hellwig return -ENOMEM;
818db243b79SGustavo A. R. Silva gf32 = p + 4; /* we want ->gf_group and ->gf_slist_flex aligned */
819d62c38f6SChristoph Hellwig
820d62c38f6SChristoph Hellwig err = -EFAULT;
82189654c5fSChristoph Hellwig if (copy_from_sockptr(gf32, optval, optlen))
822d62c38f6SChristoph Hellwig goto out_free_gsf;
823d62c38f6SChristoph Hellwig
824d62c38f6SChristoph Hellwig /* numsrc >= (4G-140)/128 overflow in 32 bits */
825d62c38f6SChristoph Hellwig n = gf32->gf_numsrc;
826d62c38f6SChristoph Hellwig err = -ENOBUFS;
827d62c38f6SChristoph Hellwig if (n >= 0x1ffffff)
828d62c38f6SChristoph Hellwig goto out_free_gsf;
829d62c38f6SChristoph Hellwig
830d62c38f6SChristoph Hellwig err = -EINVAL;
831db243b79SGustavo A. R. Silva if (offsetof(struct compat_group_filter, gf_slist_flex[n]) > optlen)
832d62c38f6SChristoph Hellwig goto out_free_gsf;
833d62c38f6SChristoph Hellwig
834d62c38f6SChristoph Hellwig /* numsrc >= (4G-140)/128 overflow in 32 bits */
835d62c38f6SChristoph Hellwig err = -ENOBUFS;
8366ae0f2e5SKuniyuki Iwashima if (n > READ_ONCE(sock_net(sk)->ipv4.sysctl_igmp_max_msf))
837b6238c04SChristoph Hellwig goto out_free_gsf;
838d62c38f6SChristoph Hellwig err = set_mcast_msfilter(sk, gf32->gf_interface, n, gf32->gf_fmode,
839db243b79SGustavo A. R. Silva &gf32->gf_group, gf32->gf_slist_flex);
840d62c38f6SChristoph Hellwig out_free_gsf:
841d62c38f6SChristoph Hellwig kfree(p);
842d62c38f6SChristoph Hellwig return err;
843d62c38f6SChristoph Hellwig }
844d62c38f6SChristoph Hellwig
ip_mcast_join_leave(struct sock * sk,int optname,sockptr_t optval,int optlen)84502caad7cSChristoph Hellwig static int ip_mcast_join_leave(struct sock *sk, int optname,
84689654c5fSChristoph Hellwig sockptr_t optval, int optlen)
84702caad7cSChristoph Hellwig {
84802caad7cSChristoph Hellwig struct ip_mreqn mreq = { };
84902caad7cSChristoph Hellwig struct sockaddr_in *psin;
85002caad7cSChristoph Hellwig struct group_req greq;
85102caad7cSChristoph Hellwig
85202caad7cSChristoph Hellwig if (optlen < sizeof(struct group_req))
85302caad7cSChristoph Hellwig return -EINVAL;
85489654c5fSChristoph Hellwig if (copy_from_sockptr(&greq, optval, sizeof(greq)))
85502caad7cSChristoph Hellwig return -EFAULT;
85602caad7cSChristoph Hellwig
85702caad7cSChristoph Hellwig psin = (struct sockaddr_in *)&greq.gr_group;
85802caad7cSChristoph Hellwig if (psin->sin_family != AF_INET)
85902caad7cSChristoph Hellwig return -EINVAL;
86002caad7cSChristoph Hellwig mreq.imr_multiaddr = psin->sin_addr;
86102caad7cSChristoph Hellwig mreq.imr_ifindex = greq.gr_interface;
86202caad7cSChristoph Hellwig if (optname == MCAST_JOIN_GROUP)
86302caad7cSChristoph Hellwig return ip_mc_join_group(sk, &mreq);
86402caad7cSChristoph Hellwig return ip_mc_leave_group(sk, &mreq);
86502caad7cSChristoph Hellwig }
86602caad7cSChristoph Hellwig
compat_ip_mcast_join_leave(struct sock * sk,int optname,sockptr_t optval,int optlen)86702caad7cSChristoph Hellwig static int compat_ip_mcast_join_leave(struct sock *sk, int optname,
86889654c5fSChristoph Hellwig sockptr_t optval, int optlen)
86902caad7cSChristoph Hellwig {
87002caad7cSChristoph Hellwig struct compat_group_req greq;
87102caad7cSChristoph Hellwig struct ip_mreqn mreq = { };
87202caad7cSChristoph Hellwig struct sockaddr_in *psin;
87302caad7cSChristoph Hellwig
87402caad7cSChristoph Hellwig if (optlen < sizeof(struct compat_group_req))
87502caad7cSChristoph Hellwig return -EINVAL;
87689654c5fSChristoph Hellwig if (copy_from_sockptr(&greq, optval, sizeof(greq)))
87702caad7cSChristoph Hellwig return -EFAULT;
87802caad7cSChristoph Hellwig
87902caad7cSChristoph Hellwig psin = (struct sockaddr_in *)&greq.gr_group;
88002caad7cSChristoph Hellwig if (psin->sin_family != AF_INET)
88102caad7cSChristoph Hellwig return -EINVAL;
88202caad7cSChristoph Hellwig mreq.imr_multiaddr = psin->sin_addr;
88302caad7cSChristoph Hellwig mreq.imr_ifindex = greq.gr_interface;
88402caad7cSChristoph Hellwig
88502caad7cSChristoph Hellwig if (optname == MCAST_JOIN_GROUP)
886b6238c04SChristoph Hellwig return ip_mc_join_group(sk, &mreq);
887b6238c04SChristoph Hellwig return ip_mc_leave_group(sk, &mreq);
88802caad7cSChristoph Hellwig }
88902caad7cSChristoph Hellwig
890020e71a3SEric Dumazet DEFINE_STATIC_KEY_FALSE(ip4_min_ttl);
891020e71a3SEric Dumazet
do_ip_setsockopt(struct sock * sk,int level,int optname,sockptr_t optval,unsigned int optlen)892ee7f1e13SMartin KaFai Lau int do_ip_setsockopt(struct sock *sk, int level, int optname,
89389654c5fSChristoph Hellwig sockptr_t optval, unsigned int optlen)
8941da177e4SLinus Torvalds {
8951da177e4SLinus Torvalds struct inet_sock *inet = inet_sk(sk);
896166b6b2dSNikolay Borisov struct net *net = sock_net(sk);
897*eeb78df4SJuntong Deng int val = 0, err, retv;
898baf606d9SMarcelo Ricardo Leitner bool needs_rtnl = setsockopt_needs_rtnl(optname);
8991da177e4SLinus Torvalds
9000c9f79beSXi Wang switch (optname) {
9010c9f79beSXi Wang case IP_PKTINFO:
9020c9f79beSXi Wang case IP_RECVTTL:
9030c9f79beSXi Wang case IP_RECVOPTS:
9040c9f79beSXi Wang case IP_RECVTOS:
9050c9f79beSXi Wang case IP_RETOPTS:
9060c9f79beSXi Wang case IP_TOS:
9070c9f79beSXi Wang case IP_TTL:
9080c9f79beSXi Wang case IP_HDRINCL:
9090c9f79beSXi Wang case IP_MTU_DISCOVER:
9100c9f79beSXi Wang case IP_RECVERR:
9110c9f79beSXi Wang case IP_ROUTER_ALERT:
9120c9f79beSXi Wang case IP_FREEBIND:
9130c9f79beSXi Wang case IP_PASSSEC:
9140c9f79beSXi Wang case IP_TRANSPARENT:
9150c9f79beSXi Wang case IP_MINTTL:
9160c9f79beSXi Wang case IP_NODEFRAG:
91790c337daSEric Dumazet case IP_BIND_ADDRESS_NO_PORT:
9180c9f79beSXi Wang case IP_UNICAST_IF:
9190c9f79beSXi Wang case IP_MULTICAST_TTL:
9200c9f79beSXi Wang case IP_MULTICAST_ALL:
9210c9f79beSXi Wang case IP_MULTICAST_LOOP:
9220c9f79beSXi Wang case IP_RECVORIGDSTADDR:
923ad6f939aSTom Herbert case IP_CHECKSUM:
92470ecc248SWillem de Bruijn case IP_RECVFRAGSIZE:
925eba75c58SWillem de Bruijn case IP_RECVERR_RFC4884:
92691d0b78cSJakub Sitnicki case IP_LOCAL_PORT_RANGE:
9271da177e4SLinus Torvalds if (optlen >= sizeof(int)) {
92889654c5fSChristoph Hellwig if (copy_from_sockptr(&val, optval, sizeof(val)))
9291da177e4SLinus Torvalds return -EFAULT;
9301da177e4SLinus Torvalds } else if (optlen >= sizeof(char)) {
9311da177e4SLinus Torvalds unsigned char ucval;
9321da177e4SLinus Torvalds
93389654c5fSChristoph Hellwig if (copy_from_sockptr(&ucval, optval, sizeof(ucval)))
9341da177e4SLinus Torvalds return -EFAULT;
9351da177e4SLinus Torvalds val = (int) ucval;
9361da177e4SLinus Torvalds }
9371da177e4SLinus Torvalds }
9381da177e4SLinus Torvalds
9391da177e4SLinus Torvalds /* If optlen==0, it is equivalent to val == 0 */
9401da177e4SLinus Torvalds
941*eeb78df4SJuntong Deng if (optname == IP_ROUTER_ALERT) {
942*eeb78df4SJuntong Deng retv = ip_ra_control(sk, val ? 1 : 0, NULL);
943*eeb78df4SJuntong Deng if (retv == 0)
944*eeb78df4SJuntong Deng inet_assign_bit(RTALERT, sk, val);
945*eeb78df4SJuntong Deng return retv;
946*eeb78df4SJuntong Deng }
9476a9fb947SPavel Emelyanov if (ip_mroute_opt(optname))
94889654c5fSChristoph Hellwig return ip_mroute_setsockopt(sk, optname, optval, optlen);
9491da177e4SLinus Torvalds
950b4d84bceSEric Dumazet /* Handle options that can be set without locking the socket. */
951b4d84bceSEric Dumazet switch (optname) {
952b4d84bceSEric Dumazet case IP_PKTINFO:
953b4d84bceSEric Dumazet inet_assign_bit(PKTINFO, sk, val);
954b4d84bceSEric Dumazet return 0;
955b4d84bceSEric Dumazet case IP_RECVTTL:
956b4d84bceSEric Dumazet inet_assign_bit(TTL, sk, val);
957b4d84bceSEric Dumazet return 0;
958b4d84bceSEric Dumazet case IP_RECVTOS:
959b4d84bceSEric Dumazet inet_assign_bit(TOS, sk, val);
960b4d84bceSEric Dumazet return 0;
961b4d84bceSEric Dumazet case IP_RECVOPTS:
962b4d84bceSEric Dumazet inet_assign_bit(RECVOPTS, sk, val);
963b4d84bceSEric Dumazet return 0;
964b4d84bceSEric Dumazet case IP_RETOPTS:
965b4d84bceSEric Dumazet inet_assign_bit(RETOPTS, sk, val);
966b4d84bceSEric Dumazet return 0;
967b4d84bceSEric Dumazet case IP_PASSSEC:
968b4d84bceSEric Dumazet inet_assign_bit(PASSSEC, sk, val);
969b4d84bceSEric Dumazet return 0;
970b4d84bceSEric Dumazet case IP_RECVORIGDSTADDR:
971b4d84bceSEric Dumazet inet_assign_bit(ORIGDSTADDR, sk, val);
972b4d84bceSEric Dumazet return 0;
973b4d84bceSEric Dumazet case IP_RECVFRAGSIZE:
974b4d84bceSEric Dumazet if (sk->sk_type != SOCK_RAW && sk->sk_type != SOCK_DGRAM)
975b4d84bceSEric Dumazet return -EINVAL;
976b4d84bceSEric Dumazet inet_assign_bit(RECVFRAGSIZE, sk, val);
977b4d84bceSEric Dumazet return 0;
9786b5f43eaSEric Dumazet case IP_RECVERR:
9796b5f43eaSEric Dumazet inet_assign_bit(RECVERR, sk, val);
9806b5f43eaSEric Dumazet if (!val)
9810f158b32SEric Dumazet skb_errqueue_purge(&sk->sk_error_queue);
9826b5f43eaSEric Dumazet return 0;
9838e8cfb11SEric Dumazet case IP_RECVERR_RFC4884:
9848e8cfb11SEric Dumazet if (val < 0 || val > 1)
9858e8cfb11SEric Dumazet return -EINVAL;
9868e8cfb11SEric Dumazet inet_assign_bit(RECVERR_RFC4884, sk, val);
9878e8cfb11SEric Dumazet return 0;
9883f7e7532SEric Dumazet case IP_FREEBIND:
9893f7e7532SEric Dumazet if (optlen < 1)
9903f7e7532SEric Dumazet return -EINVAL;
9913f7e7532SEric Dumazet inet_assign_bit(FREEBIND, sk, val);
9923f7e7532SEric Dumazet return 0;
993cafbe182SEric Dumazet case IP_HDRINCL:
994cafbe182SEric Dumazet if (sk->sk_type != SOCK_RAW)
995cafbe182SEric Dumazet return -ENOPROTOOPT;
996cafbe182SEric Dumazet inet_assign_bit(HDRINCL, sk, val);
997cafbe182SEric Dumazet return 0;
998b09bde5cSEric Dumazet case IP_MULTICAST_LOOP:
999b09bde5cSEric Dumazet if (optlen < 1)
1000b09bde5cSEric Dumazet return -EINVAL;
1001b09bde5cSEric Dumazet inet_assign_bit(MC_LOOP, sk, val);
1002b09bde5cSEric Dumazet return 0;
1003307b4ac6SEric Dumazet case IP_MULTICAST_ALL:
1004307b4ac6SEric Dumazet if (optlen < 1)
1005307b4ac6SEric Dumazet return -EINVAL;
1006307b4ac6SEric Dumazet if (val != 0 && val != 1)
1007307b4ac6SEric Dumazet return -EINVAL;
1008307b4ac6SEric Dumazet inet_assign_bit(MC_ALL, sk, val);
1009307b4ac6SEric Dumazet return 0;
10104bd0623fSEric Dumazet case IP_TRANSPARENT:
10114bd0623fSEric Dumazet if (!!val && !sockopt_ns_capable(sock_net(sk)->user_ns, CAP_NET_RAW) &&
10128be6f88bSEric Dumazet !sockopt_ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN))
10138be6f88bSEric Dumazet return -EPERM;
10144bd0623fSEric Dumazet if (optlen < 1)
10158be6f88bSEric Dumazet return -EINVAL;
10164bd0623fSEric Dumazet inet_assign_bit(TRANSPARENT, sk, val);
10174bd0623fSEric Dumazet return 0;
1018f04b8d34SEric Dumazet case IP_NODEFRAG:
1019f04b8d34SEric Dumazet if (sk->sk_type != SOCK_RAW)
1020f04b8d34SEric Dumazet return -ENOPROTOOPT;
1021f04b8d34SEric Dumazet inet_assign_bit(NODEFRAG, sk, val);
1022f04b8d34SEric Dumazet return 0;
1023ca571e2eSEric Dumazet case IP_BIND_ADDRESS_NO_PORT:
1024ca571e2eSEric Dumazet inet_assign_bit(BIND_ADDRESS_NO_PORT, sk, val);
1025ca571e2eSEric Dumazet return 0;
102610f42426SEric Dumazet case IP_TTL:
102710f42426SEric Dumazet if (optlen < 1)
102810f42426SEric Dumazet return -EINVAL;
102910f42426SEric Dumazet if (val != -1 && (val < 1 || val > 255))
103010f42426SEric Dumazet return -EINVAL;
103110f42426SEric Dumazet WRITE_ONCE(inet->uc_ttl, val);
103210f42426SEric Dumazet return 0;
103312af7326SEric Dumazet case IP_MINTTL:
103412af7326SEric Dumazet if (optlen < 1)
103512af7326SEric Dumazet return -EINVAL;
103612af7326SEric Dumazet if (val < 0 || val > 255)
103712af7326SEric Dumazet return -EINVAL;
103812af7326SEric Dumazet
103912af7326SEric Dumazet if (val)
104012af7326SEric Dumazet static_branch_enable(&ip4_min_ttl);
104112af7326SEric Dumazet
104212af7326SEric Dumazet WRITE_ONCE(inet->min_ttl, val);
104312af7326SEric Dumazet return 0;
1044c9746e6aSEric Dumazet case IP_MULTICAST_TTL:
1045c9746e6aSEric Dumazet if (sk->sk_type == SOCK_STREAM)
1046c9746e6aSEric Dumazet return -EINVAL;
1047c9746e6aSEric Dumazet if (optlen < 1)
1048c9746e6aSEric Dumazet return -EINVAL;
1049c9746e6aSEric Dumazet if (val == -1)
1050c9746e6aSEric Dumazet val = 1;
1051c9746e6aSEric Dumazet if (val < 0 || val > 255)
1052c9746e6aSEric Dumazet return -EINVAL;
1053c9746e6aSEric Dumazet WRITE_ONCE(inet->mc_ttl, val);
1054c9746e6aSEric Dumazet return 0;
1055ceaa7141SEric Dumazet case IP_MTU_DISCOVER:
1056ceaa7141SEric Dumazet return ip_sock_set_mtu_discover(sk, val);
1057e08d0b3dSEric Dumazet case IP_TOS: /* This sets both TOS and Precedence */
1058e08d0b3dSEric Dumazet ip_sock_set_tos(sk, val);
1059e08d0b3dSEric Dumazet return 0;
1060d9f28735SDavid Laight case IP_LOCAL_PORT_RANGE:
1061d9f28735SDavid Laight {
1062d9f28735SDavid Laight u16 lo = val;
1063d9f28735SDavid Laight u16 hi = val >> 16;
1064d9f28735SDavid Laight
1065d9f28735SDavid Laight if (optlen != sizeof(u32))
1066d9f28735SDavid Laight return -EINVAL;
1067d9f28735SDavid Laight if (lo != 0 && hi != 0 && lo > hi)
1068d9f28735SDavid Laight return -EINVAL;
1069d9f28735SDavid Laight
1070d9f28735SDavid Laight WRITE_ONCE(inet->local_port_range, val);
1071d9f28735SDavid Laight return 0;
1072d9f28735SDavid Laight }
1073b4d84bceSEric Dumazet }
1074b4d84bceSEric Dumazet
10751da177e4SLinus Torvalds err = 0;
1076baf606d9SMarcelo Ricardo Leitner if (needs_rtnl)
1077baf606d9SMarcelo Ricardo Leitner rtnl_lock();
10781df055d3SMartin KaFai Lau sockopt_lock_sock(sk);
10791da177e4SLinus Torvalds
10801da177e4SLinus Torvalds switch (optname) {
10811da177e4SLinus Torvalds case IP_OPTIONS:
10821da177e4SLinus Torvalds {
1083f6d8bd05SEric Dumazet struct ip_options_rcu *old, *opt = NULL;
1084f6d8bd05SEric Dumazet
108565a1c4ffSroel kluin if (optlen > 40)
10861da177e4SLinus Torvalds goto e_inval;
108789654c5fSChristoph Hellwig err = ip_options_get(sock_net(sk), &opt, optval, optlen);
10881da177e4SLinus Torvalds if (err)
10891da177e4SLinus Torvalds break;
1090f6d8bd05SEric Dumazet old = rcu_dereference_protected(inet->inet_opt,
10911e1d04e6SHannes Frederic Sowa lockdep_sock_is_held(sk));
1092b1c0356aSEric Dumazet if (inet_test_bit(IS_ICSK, sk)) {
1093d83d8461SArnaldo Carvalho de Melo struct inet_connection_sock *icsk = inet_csk(sk);
1094dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6)
10951da177e4SLinus Torvalds if (sk->sk_family == PF_INET ||
10961da177e4SLinus Torvalds (!((1 << sk->sk_state) &
10971da177e4SLinus Torvalds (TCPF_LISTEN | TCPF_CLOSE)) &&
1098c720c7e8SEric Dumazet inet->inet_daddr != LOOPBACK4_IPV6)) {
10991da177e4SLinus Torvalds #endif
1100f6d8bd05SEric Dumazet if (old)
1101f6d8bd05SEric Dumazet icsk->icsk_ext_hdr_len -= old->opt.optlen;
11021da177e4SLinus Torvalds if (opt)
1103f6d8bd05SEric Dumazet icsk->icsk_ext_hdr_len += opt->opt.optlen;
1104d83d8461SArnaldo Carvalho de Melo icsk->icsk_sync_mss(sk, icsk->icsk_pmtu_cookie);
1105dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6)
11061da177e4SLinus Torvalds }
11071da177e4SLinus Torvalds #endif
11081da177e4SLinus Torvalds }
1109f6d8bd05SEric Dumazet rcu_assign_pointer(inet->inet_opt, opt);
1110f6d8bd05SEric Dumazet if (old)
1111605b4afeSPaul E. McKenney kfree_rcu(old, rcu);
11121da177e4SLinus Torvalds break;
11131da177e4SLinus Torvalds }
1114ad6f939aSTom Herbert case IP_CHECKSUM:
1115ad6f939aSTom Herbert if (val) {
1116c274af22SEric Dumazet if (!(inet_test_bit(CHECKSUM, sk))) {
1117ad6f939aSTom Herbert inet_inc_convert_csum(sk);
1118c274af22SEric Dumazet inet_set_bit(CHECKSUM, sk);
1119ad6f939aSTom Herbert }
1120ad6f939aSTom Herbert } else {
1121c274af22SEric Dumazet if (inet_test_bit(CHECKSUM, sk)) {
1122ad6f939aSTom Herbert inet_dec_convert_csum(sk);
1123c274af22SEric Dumazet inet_clear_bit(CHECKSUM, sk);
1124ad6f939aSTom Herbert }
1125ad6f939aSTom Herbert }
1126ad6f939aSTom Herbert break;
112776e21053SErich E. Hoover case IP_UNICAST_IF:
112876e21053SErich E. Hoover {
112976e21053SErich E. Hoover struct net_device *dev = NULL;
113076e21053SErich E. Hoover int ifindex;
11319515a2e0SDavid Ahern int midx;
113276e21053SErich E. Hoover
113376e21053SErich E. Hoover if (optlen != sizeof(int))
113476e21053SErich E. Hoover goto e_inval;
113576e21053SErich E. Hoover
113676e21053SErich E. Hoover ifindex = (__force int)ntohl((__force __be32)val);
113776e21053SErich E. Hoover if (ifindex == 0) {
1138959d5c11SEric Dumazet WRITE_ONCE(inet->uc_index, 0);
113976e21053SErich E. Hoover err = 0;
114076e21053SErich E. Hoover break;
114176e21053SErich E. Hoover }
114276e21053SErich E. Hoover
114376e21053SErich E. Hoover dev = dev_get_by_index(sock_net(sk), ifindex);
114476e21053SErich E. Hoover err = -EADDRNOTAVAIL;
114576e21053SErich E. Hoover if (!dev)
114676e21053SErich E. Hoover break;
11479515a2e0SDavid Ahern
11489515a2e0SDavid Ahern midx = l3mdev_master_ifindex(dev);
114976e21053SErich E. Hoover dev_put(dev);
115076e21053SErich E. Hoover
115176e21053SErich E. Hoover err = -EINVAL;
1152fdf1923bSMiaohe Lin if (sk->sk_bound_dev_if && midx != sk->sk_bound_dev_if)
115376e21053SErich E. Hoover break;
115476e21053SErich E. Hoover
1155959d5c11SEric Dumazet WRITE_ONCE(inet->uc_index, ifindex);
115676e21053SErich E. Hoover err = 0;
115776e21053SErich E. Hoover break;
115876e21053SErich E. Hoover }
11591da177e4SLinus Torvalds case IP_MULTICAST_IF:
11601da177e4SLinus Torvalds {
11611da177e4SLinus Torvalds struct ip_mreqn mreq;
11621da177e4SLinus Torvalds struct net_device *dev = NULL;
11637bb387c5SDavid Ahern int midx;
11641da177e4SLinus Torvalds
11651da177e4SLinus Torvalds if (sk->sk_type == SOCK_STREAM)
11661da177e4SLinus Torvalds goto e_inval;
11671da177e4SLinus Torvalds /*
11681da177e4SLinus Torvalds * Check the arguments are allowable
11691da177e4SLinus Torvalds */
11701da177e4SLinus Torvalds
11710915921bSShan Wei if (optlen < sizeof(struct in_addr))
11720915921bSShan Wei goto e_inval;
11730915921bSShan Wei
11741da177e4SLinus Torvalds err = -EFAULT;
11751da177e4SLinus Torvalds if (optlen >= sizeof(struct ip_mreqn)) {
117689654c5fSChristoph Hellwig if (copy_from_sockptr(&mreq, optval, sizeof(mreq)))
11771da177e4SLinus Torvalds break;
11781da177e4SLinus Torvalds } else {
11791da177e4SLinus Torvalds memset(&mreq, 0, sizeof(mreq));
11803a084ddbSJiri Pirko if (optlen >= sizeof(struct ip_mreq)) {
118189654c5fSChristoph Hellwig if (copy_from_sockptr(&mreq, optval,
11823a084ddbSJiri Pirko sizeof(struct ip_mreq)))
11833a084ddbSJiri Pirko break;
11843a084ddbSJiri Pirko } else if (optlen >= sizeof(struct in_addr)) {
118589654c5fSChristoph Hellwig if (copy_from_sockptr(&mreq.imr_address, optval,
11864d52cfbeSEric Dumazet sizeof(struct in_addr)))
11871da177e4SLinus Torvalds break;
11881da177e4SLinus Torvalds }
11893a084ddbSJiri Pirko }
11901da177e4SLinus Torvalds
11911da177e4SLinus Torvalds if (!mreq.imr_ifindex) {
1192e6f1cebfSAl Viro if (mreq.imr_address.s_addr == htonl(INADDR_ANY)) {
119302715925SEric Dumazet WRITE_ONCE(inet->mc_index, 0);
119402715925SEric Dumazet WRITE_ONCE(inet->mc_addr, 0);
11951da177e4SLinus Torvalds err = 0;
11961da177e4SLinus Torvalds break;
11971da177e4SLinus Torvalds }
11983b1e0a65SYOSHIFUJI Hideaki dev = ip_dev_find(sock_net(sk), mreq.imr_address.s_addr);
119955b80503SEric Dumazet if (dev)
12001da177e4SLinus Torvalds mreq.imr_ifindex = dev->ifindex;
12011da177e4SLinus Torvalds } else
120255b80503SEric Dumazet dev = dev_get_by_index(sock_net(sk), mreq.imr_ifindex);
12031da177e4SLinus Torvalds
12041da177e4SLinus Torvalds
12051da177e4SLinus Torvalds err = -EADDRNOTAVAIL;
12061da177e4SLinus Torvalds if (!dev)
12071da177e4SLinus Torvalds break;
12087bb387c5SDavid Ahern
12097bb387c5SDavid Ahern midx = l3mdev_master_ifindex(dev);
12107bb387c5SDavid Ahern
121155b80503SEric Dumazet dev_put(dev);
12121da177e4SLinus Torvalds
12131da177e4SLinus Torvalds err = -EINVAL;
12141da177e4SLinus Torvalds if (sk->sk_bound_dev_if &&
12157bb387c5SDavid Ahern mreq.imr_ifindex != sk->sk_bound_dev_if &&
1216fdf1923bSMiaohe Lin midx != sk->sk_bound_dev_if)
12171da177e4SLinus Torvalds break;
12181da177e4SLinus Torvalds
121902715925SEric Dumazet WRITE_ONCE(inet->mc_index, mreq.imr_ifindex);
122002715925SEric Dumazet WRITE_ONCE(inet->mc_addr, mreq.imr_address.s_addr);
12211da177e4SLinus Torvalds err = 0;
12221da177e4SLinus Torvalds break;
12231da177e4SLinus Torvalds }
12241da177e4SLinus Torvalds
12251da177e4SLinus Torvalds case IP_ADD_MEMBERSHIP:
12261da177e4SLinus Torvalds case IP_DROP_MEMBERSHIP:
12271da177e4SLinus Torvalds {
12281da177e4SLinus Torvalds struct ip_mreqn mreq;
12291da177e4SLinus Torvalds
1230a96fb49bSFlavio Leitner err = -EPROTO;
1231b1c0356aSEric Dumazet if (inet_test_bit(IS_ICSK, sk))
1232a96fb49bSFlavio Leitner break;
1233a96fb49bSFlavio Leitner
12341da177e4SLinus Torvalds if (optlen < sizeof(struct ip_mreq))
12351da177e4SLinus Torvalds goto e_inval;
12361da177e4SLinus Torvalds err = -EFAULT;
12371da177e4SLinus Torvalds if (optlen >= sizeof(struct ip_mreqn)) {
123889654c5fSChristoph Hellwig if (copy_from_sockptr(&mreq, optval, sizeof(mreq)))
12391da177e4SLinus Torvalds break;
12401da177e4SLinus Torvalds } else {
12411da177e4SLinus Torvalds memset(&mreq, 0, sizeof(mreq));
124289654c5fSChristoph Hellwig if (copy_from_sockptr(&mreq, optval,
124389654c5fSChristoph Hellwig sizeof(struct ip_mreq)))
12441da177e4SLinus Torvalds break;
12451da177e4SLinus Torvalds }
12461da177e4SLinus Torvalds
12471da177e4SLinus Torvalds if (optname == IP_ADD_MEMBERSHIP)
124854ff9ef3SMarcelo Ricardo Leitner err = ip_mc_join_group(sk, &mreq);
12491da177e4SLinus Torvalds else
125054ff9ef3SMarcelo Ricardo Leitner err = ip_mc_leave_group(sk, &mreq);
12511da177e4SLinus Torvalds break;
12521da177e4SLinus Torvalds }
12531da177e4SLinus Torvalds case IP_MSFILTER:
12541da177e4SLinus Torvalds {
12551da177e4SLinus Torvalds struct ip_msfilter *msf;
12561da177e4SLinus Torvalds
12574167a960SGustavo A. R. Silva if (optlen < IP_MSFILTER_SIZE(0))
12581da177e4SLinus Torvalds goto e_inval;
1259f5769faeSEric Dumazet if (optlen > READ_ONCE(net->core.sysctl_optmem_max)) {
12601da177e4SLinus Torvalds err = -ENOBUFS;
12611da177e4SLinus Torvalds break;
12621da177e4SLinus Torvalds }
126389654c5fSChristoph Hellwig msf = memdup_sockptr(optval, optlen);
1264a2c841d9SAl Viro if (IS_ERR(msf)) {
1265a2c841d9SAl Viro err = PTR_ERR(msf);
12661da177e4SLinus Torvalds break;
12671da177e4SLinus Torvalds }
12681da177e4SLinus Torvalds /* numsrc >= (1G-4) overflow in 32 bits */
12691da177e4SLinus Torvalds if (msf->imsf_numsrc >= 0x3ffffffcU ||
12706ae0f2e5SKuniyuki Iwashima msf->imsf_numsrc > READ_ONCE(net->ipv4.sysctl_igmp_max_msf)) {
12711da177e4SLinus Torvalds kfree(msf);
12721da177e4SLinus Torvalds err = -ENOBUFS;
12731da177e4SLinus Torvalds break;
12741da177e4SLinus Torvalds }
12754167a960SGustavo A. R. Silva if (IP_MSFILTER_SIZE(msf->imsf_numsrc) > optlen) {
12761da177e4SLinus Torvalds kfree(msf);
12771da177e4SLinus Torvalds err = -EINVAL;
12781da177e4SLinus Torvalds break;
12791da177e4SLinus Torvalds }
12801da177e4SLinus Torvalds err = ip_mc_msfilter(sk, msf, 0);
12811da177e4SLinus Torvalds kfree(msf);
12821da177e4SLinus Torvalds break;
12831da177e4SLinus Torvalds }
12841da177e4SLinus Torvalds case IP_BLOCK_SOURCE:
12851da177e4SLinus Torvalds case IP_UNBLOCK_SOURCE:
12861da177e4SLinus Torvalds case IP_ADD_SOURCE_MEMBERSHIP:
12871da177e4SLinus Torvalds case IP_DROP_SOURCE_MEMBERSHIP:
12881da177e4SLinus Torvalds {
12891da177e4SLinus Torvalds struct ip_mreq_source mreqs;
12901da177e4SLinus Torvalds int omode, add;
12911da177e4SLinus Torvalds
12921da177e4SLinus Torvalds if (optlen != sizeof(struct ip_mreq_source))
12931da177e4SLinus Torvalds goto e_inval;
129489654c5fSChristoph Hellwig if (copy_from_sockptr(&mreqs, optval, sizeof(mreqs))) {
12951da177e4SLinus Torvalds err = -EFAULT;
12961da177e4SLinus Torvalds break;
12971da177e4SLinus Torvalds }
12981da177e4SLinus Torvalds if (optname == IP_BLOCK_SOURCE) {
12991da177e4SLinus Torvalds omode = MCAST_EXCLUDE;
13001da177e4SLinus Torvalds add = 1;
13011da177e4SLinus Torvalds } else if (optname == IP_UNBLOCK_SOURCE) {
13021da177e4SLinus Torvalds omode = MCAST_EXCLUDE;
13031da177e4SLinus Torvalds add = 0;
13041da177e4SLinus Torvalds } else if (optname == IP_ADD_SOURCE_MEMBERSHIP) {
13051da177e4SLinus Torvalds struct ip_mreqn mreq;
13061da177e4SLinus Torvalds
13071da177e4SLinus Torvalds mreq.imr_multiaddr.s_addr = mreqs.imr_multiaddr;
13081da177e4SLinus Torvalds mreq.imr_address.s_addr = mreqs.imr_interface;
13091da177e4SLinus Torvalds mreq.imr_ifindex = 0;
13106e2059b5SHangbin Liu err = ip_mc_join_group_ssm(sk, &mreq, MCAST_INCLUDE);
13118cdaaa15SDavid L Stevens if (err && err != -EADDRINUSE)
13121da177e4SLinus Torvalds break;
13131da177e4SLinus Torvalds omode = MCAST_INCLUDE;
13141da177e4SLinus Torvalds add = 1;
13151da177e4SLinus Torvalds } else /* IP_DROP_SOURCE_MEMBERSHIP */ {
13161da177e4SLinus Torvalds omode = MCAST_INCLUDE;
13171da177e4SLinus Torvalds add = 0;
13181da177e4SLinus Torvalds }
13191da177e4SLinus Torvalds err = ip_mc_source(add, omode, sk, &mreqs, 0);
13201da177e4SLinus Torvalds break;
13211da177e4SLinus Torvalds }
13221da177e4SLinus Torvalds case MCAST_JOIN_GROUP:
13231da177e4SLinus Torvalds case MCAST_LEAVE_GROUP:
1324b6238c04SChristoph Hellwig if (in_compat_syscall())
1325b6238c04SChristoph Hellwig err = compat_ip_mcast_join_leave(sk, optname, optval,
1326b6238c04SChristoph Hellwig optlen);
1327b6238c04SChristoph Hellwig else
132802caad7cSChristoph Hellwig err = ip_mcast_join_leave(sk, optname, optval, optlen);
13291da177e4SLinus Torvalds break;
13301da177e4SLinus Torvalds case MCAST_JOIN_SOURCE_GROUP:
13311da177e4SLinus Torvalds case MCAST_LEAVE_SOURCE_GROUP:
13321da177e4SLinus Torvalds case MCAST_BLOCK_SOURCE:
13331da177e4SLinus Torvalds case MCAST_UNBLOCK_SOURCE:
1334b6238c04SChristoph Hellwig err = do_mcast_group_source(sk, optname, optval, optlen);
13351da177e4SLinus Torvalds break;
13361da177e4SLinus Torvalds case MCAST_MSFILTER:
1337b6238c04SChristoph Hellwig if (in_compat_syscall())
1338b6238c04SChristoph Hellwig err = compat_ip_set_mcast_msfilter(sk, optval, optlen);
1339b6238c04SChristoph Hellwig else
1340d62c38f6SChristoph Hellwig err = ip_set_mcast_msfilter(sk, optval, optlen);
13411da177e4SLinus Torvalds break;
13421da177e4SLinus Torvalds case IP_IPSEC_POLICY:
13431da177e4SLinus Torvalds case IP_XFRM_POLICY:
13446fc0b4a7SHerbert Xu err = -EPERM;
13451df055d3SMartin KaFai Lau if (!sockopt_ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN))
13466fc0b4a7SHerbert Xu break;
134789654c5fSChristoph Hellwig err = xfrm_user_policy(sk, optname, optval, optlen);
13481da177e4SLinus Torvalds break;
13491da177e4SLinus Torvalds
13501da177e4SLinus Torvalds default:
13511da177e4SLinus Torvalds err = -ENOPROTOOPT;
13521da177e4SLinus Torvalds break;
13531da177e4SLinus Torvalds }
13541df055d3SMartin KaFai Lau sockopt_release_sock(sk);
1355baf606d9SMarcelo Ricardo Leitner if (needs_rtnl)
1356baf606d9SMarcelo Ricardo Leitner rtnl_unlock();
13571da177e4SLinus Torvalds return err;
13581da177e4SLinus Torvalds
13591da177e4SLinus Torvalds e_inval:
13601df055d3SMartin KaFai Lau sockopt_release_sock(sk);
1361baf606d9SMarcelo Ricardo Leitner if (needs_rtnl)
1362baf606d9SMarcelo Ricardo Leitner rtnl_unlock();
13631da177e4SLinus Torvalds return -EINVAL;
13641da177e4SLinus Torvalds }
13651da177e4SLinus Torvalds
1366f84af32cSEric Dumazet /**
1367829ae9d6SWillem de Bruijn * ipv4_pktinfo_prepare - transfer some info from rtable to skb
1368f84af32cSEric Dumazet * @sk: socket
1369f84af32cSEric Dumazet * @skb: buffer
1370e622502cSNicolas Dichtel * @drop_dst: if true, drops skb dst
1371f84af32cSEric Dumazet *
137235ebf65eSDavid S. Miller * To support IP_CMSG_PKTINFO option, we store rt_iif and specific
137335ebf65eSDavid S. Miller * destination in skb->cb[] before dst drop.
13748e3bff96Sstephen hemminger * This way, receiver doesn't make cache line misses to read rtable.
1375f84af32cSEric Dumazet */
ipv4_pktinfo_prepare(const struct sock * sk,struct sk_buff * skb,bool drop_dst)1376e622502cSNicolas Dichtel void ipv4_pktinfo_prepare(const struct sock *sk, struct sk_buff *skb, bool drop_dst)
1377f84af32cSEric Dumazet {
1378d826eb14SEric Dumazet struct in_pktinfo *pktinfo = PKTINFO_SKB_CB(skb);
1379c274af22SEric Dumazet bool prepare = inet_test_bit(PKTINFO, sk) ||
13804b261c75SHannes Frederic Sowa ipv6_sk_rxinfo(sk);
1381d826eb14SEric Dumazet
13824b261c75SHannes Frederic Sowa if (prepare && skb_rtable(skb)) {
13830b922b7aSDavid Ahern /* skb->cb is overloaded: prior to this point it is IP{6}CB
13840b922b7aSDavid Ahern * which has interface index (iif) as the first member of the
13850b922b7aSDavid Ahern * underlying inet{6}_skb_parm struct. This code then overlays
13860b922b7aSDavid Ahern * PKTINFO_SKB_CB and in_pktinfo also has iif as the first
1387f0c16ba8SWei Zhang * element so the iif is picked up from the prior IPCB. If iif
1388f0c16ba8SWei Zhang * is the loopback interface, then return the sending interface
1389f0c16ba8SWei Zhang * (e.g., process binds socket to eth0 for Tx which is
1390f0c16ba8SWei Zhang * redirected to loopback in the rtable/dst).
13910b922b7aSDavid Ahern */
1392cbea8f02SDavid Ahern struct rtable *rt = skb_rtable(skb);
1393cbea8f02SDavid Ahern bool l3slave = ipv4_l3mdev_skb(IPCB(skb)->flags);
1394cbea8f02SDavid Ahern
1395cbea8f02SDavid Ahern if (pktinfo->ipi_ifindex == LOOPBACK_IFINDEX)
1396f0c16ba8SWei Zhang pktinfo->ipi_ifindex = inet_iif(skb);
1397cbea8f02SDavid Ahern else if (l3slave && rt && rt->rt_iif)
1398cbea8f02SDavid Ahern pktinfo->ipi_ifindex = rt->rt_iif;
1399f0c16ba8SWei Zhang
140035ebf65eSDavid S. Miller pktinfo->ipi_spec_dst.s_addr = fib_compute_spec_dst(skb);
1401d826eb14SEric Dumazet } else {
1402d826eb14SEric Dumazet pktinfo->ipi_ifindex = 0;
1403d826eb14SEric Dumazet pktinfo->ipi_spec_dst.s_addr = 0;
1404f84af32cSEric Dumazet }
1405e622502cSNicolas Dichtel if (drop_dst)
1406d826eb14SEric Dumazet skb_dst_drop(skb);
1407d826eb14SEric Dumazet }
1408f84af32cSEric Dumazet
ip_setsockopt(struct sock * sk,int level,int optname,sockptr_t optval,unsigned int optlen)1409a7b75c5aSChristoph Hellwig int ip_setsockopt(struct sock *sk, int level, int optname, sockptr_t optval,
1410a7b75c5aSChristoph Hellwig unsigned int optlen)
14113fdadf7dSDmitry Mishin {
14123fdadf7dSDmitry Mishin int err;
14133fdadf7dSDmitry Mishin
14143fdadf7dSDmitry Mishin if (level != SOL_IP)
14153fdadf7dSDmitry Mishin return -ENOPROTOOPT;
14163fdadf7dSDmitry Mishin
1417a7b75c5aSChristoph Hellwig err = do_ip_setsockopt(sk, level, optname, optval, optlen);
14183fdadf7dSDmitry Mishin #ifdef CONFIG_NETFILTER
14193fdadf7dSDmitry Mishin /* we need to exclude all possible ENOPROTOOPTs except default case */
14203fdadf7dSDmitry Mishin if (err == -ENOPROTOOPT && optname != IP_HDRINCL &&
14216a9fb947SPavel Emelyanov optname != IP_IPSEC_POLICY &&
14226a9fb947SPavel Emelyanov optname != IP_XFRM_POLICY &&
14233f34cfaeSPaolo Abeni !ip_mroute_opt(optname))
1424a7b75c5aSChristoph Hellwig err = nf_setsockopt(sk, PF_INET, optname, optval, optlen);
14253fdadf7dSDmitry Mishin #endif
14263fdadf7dSDmitry Mishin return err;
14273fdadf7dSDmitry Mishin }
14284d52cfbeSEric Dumazet EXPORT_SYMBOL(ip_setsockopt);
14293fdadf7dSDmitry Mishin
14301da177e4SLinus Torvalds /*
14314d52cfbeSEric Dumazet * Get the options. Note for future reference. The GET of IP options gets
14324d52cfbeSEric Dumazet * the _received_ ones. The set sets the _sent_ ones.
14331da177e4SLinus Torvalds */
14341da177e4SLinus Torvalds
getsockopt_needs_rtnl(int optname)143587e9f031SWANG Cong static bool getsockopt_needs_rtnl(int optname)
143687e9f031SWANG Cong {
143787e9f031SWANG Cong switch (optname) {
143887e9f031SWANG Cong case IP_MSFILTER:
143987e9f031SWANG Cong case MCAST_MSFILTER:
144087e9f031SWANG Cong return true;
144187e9f031SWANG Cong }
144287e9f031SWANG Cong return false;
144387e9f031SWANG Cong }
144487e9f031SWANG Cong
ip_get_mcast_msfilter(struct sock * sk,sockptr_t optval,sockptr_t optlen,int len)1445728f064cSMartin KaFai Lau static int ip_get_mcast_msfilter(struct sock *sk, sockptr_t optval,
1446728f064cSMartin KaFai Lau sockptr_t optlen, int len)
144749e74c24SChristoph Hellwig {
1448db243b79SGustavo A. R. Silva const int size0 = offsetof(struct group_filter, gf_slist_flex);
144949e74c24SChristoph Hellwig struct group_filter gsf;
1450728f064cSMartin KaFai Lau int num, gsf_size;
145149e74c24SChristoph Hellwig int err;
145249e74c24SChristoph Hellwig
145349e74c24SChristoph Hellwig if (len < size0)
145449e74c24SChristoph Hellwig return -EINVAL;
1455728f064cSMartin KaFai Lau if (copy_from_sockptr(&gsf, optval, size0))
145649e74c24SChristoph Hellwig return -EFAULT;
145749e74c24SChristoph Hellwig
145849e74c24SChristoph Hellwig num = gsf.gf_numsrc;
1459728f064cSMartin KaFai Lau err = ip_mc_gsfget(sk, &gsf, optval,
1460728f064cSMartin KaFai Lau offsetof(struct group_filter, gf_slist_flex));
146149e74c24SChristoph Hellwig if (err)
146249e74c24SChristoph Hellwig return err;
146349e74c24SChristoph Hellwig if (gsf.gf_numsrc < num)
146449e74c24SChristoph Hellwig num = gsf.gf_numsrc;
1465728f064cSMartin KaFai Lau gsf_size = GROUP_FILTER_SIZE(num);
1466728f064cSMartin KaFai Lau if (copy_to_sockptr(optlen, &gsf_size, sizeof(int)) ||
1467728f064cSMartin KaFai Lau copy_to_sockptr(optval, &gsf, size0))
146849e74c24SChristoph Hellwig return -EFAULT;
146949e74c24SChristoph Hellwig return 0;
147049e74c24SChristoph Hellwig }
147149e74c24SChristoph Hellwig
compat_ip_get_mcast_msfilter(struct sock * sk,sockptr_t optval,sockptr_t optlen,int len)1472728f064cSMartin KaFai Lau static int compat_ip_get_mcast_msfilter(struct sock *sk, sockptr_t optval,
1473728f064cSMartin KaFai Lau sockptr_t optlen, int len)
147449e74c24SChristoph Hellwig {
1475db243b79SGustavo A. R. Silva const int size0 = offsetof(struct compat_group_filter, gf_slist_flex);
147649e74c24SChristoph Hellwig struct compat_group_filter gf32;
147749e74c24SChristoph Hellwig struct group_filter gf;
147849e74c24SChristoph Hellwig int num;
1479b6238c04SChristoph Hellwig int err;
148049e74c24SChristoph Hellwig
148149e74c24SChristoph Hellwig if (len < size0)
148249e74c24SChristoph Hellwig return -EINVAL;
1483728f064cSMartin KaFai Lau if (copy_from_sockptr(&gf32, optval, size0))
148449e74c24SChristoph Hellwig return -EFAULT;
148549e74c24SChristoph Hellwig
148649e74c24SChristoph Hellwig gf.gf_interface = gf32.gf_interface;
148749e74c24SChristoph Hellwig gf.gf_fmode = gf32.gf_fmode;
148849e74c24SChristoph Hellwig num = gf.gf_numsrc = gf32.gf_numsrc;
148949e74c24SChristoph Hellwig gf.gf_group = gf32.gf_group;
149049e74c24SChristoph Hellwig
1491728f064cSMartin KaFai Lau err = ip_mc_gsfget(sk, &gf, optval,
1492728f064cSMartin KaFai Lau offsetof(struct compat_group_filter, gf_slist_flex));
149349e74c24SChristoph Hellwig if (err)
149449e74c24SChristoph Hellwig return err;
149549e74c24SChristoph Hellwig if (gf.gf_numsrc < num)
149649e74c24SChristoph Hellwig num = gf.gf_numsrc;
149749e74c24SChristoph Hellwig len = GROUP_FILTER_SIZE(num) - (sizeof(gf) - sizeof(gf32));
1498728f064cSMartin KaFai Lau if (copy_to_sockptr(optlen, &len, sizeof(int)) ||
1499728f064cSMartin KaFai Lau copy_to_sockptr_offset(optval, offsetof(struct compat_group_filter, gf_fmode),
1500728f064cSMartin KaFai Lau &gf.gf_fmode, sizeof(gf.gf_fmode)) ||
1501728f064cSMartin KaFai Lau copy_to_sockptr_offset(optval, offsetof(struct compat_group_filter, gf_numsrc),
1502728f064cSMartin KaFai Lau &gf.gf_numsrc, sizeof(gf.gf_numsrc)))
150349e74c24SChristoph Hellwig return -EFAULT;
150449e74c24SChristoph Hellwig return 0;
150549e74c24SChristoph Hellwig }
150649e74c24SChristoph Hellwig
do_ip_getsockopt(struct sock * sk,int level,int optname,sockptr_t optval,sockptr_t optlen)1507fd969f25SMartin KaFai Lau int do_ip_getsockopt(struct sock *sk, int level, int optname,
1508728f064cSMartin KaFai Lau sockptr_t optval, sockptr_t optlen)
15091da177e4SLinus Torvalds {
15101da177e4SLinus Torvalds struct inet_sock *inet = inet_sk(sk);
151187e9f031SWANG Cong bool needs_rtnl = getsockopt_needs_rtnl(optname);
151287e9f031SWANG Cong int val, err = 0;
15131da177e4SLinus Torvalds int len;
15141da177e4SLinus Torvalds
15151da177e4SLinus Torvalds if (level != SOL_IP)
15161da177e4SLinus Torvalds return -EOPNOTSUPP;
15171da177e4SLinus Torvalds
15186a9fb947SPavel Emelyanov if (ip_mroute_opt(optname))
15191da177e4SLinus Torvalds return ip_mroute_getsockopt(sk, optname, optval, optlen);
15201da177e4SLinus Torvalds
1521728f064cSMartin KaFai Lau if (copy_from_sockptr(&len, optlen, sizeof(int)))
15221da177e4SLinus Torvalds return -EFAULT;
15231da177e4SLinus Torvalds if (len < 0)
15241da177e4SLinus Torvalds return -EINVAL;
15251da177e4SLinus Torvalds
1526b4d84bceSEric Dumazet /* Handle options that can be read without locking the socket. */
1527b4d84bceSEric Dumazet switch (optname) {
1528b4d84bceSEric Dumazet case IP_PKTINFO:
1529b4d84bceSEric Dumazet val = inet_test_bit(PKTINFO, sk);
1530b4d84bceSEric Dumazet goto copyval;
1531b4d84bceSEric Dumazet case IP_RECVTTL:
1532b4d84bceSEric Dumazet val = inet_test_bit(TTL, sk);
1533b4d84bceSEric Dumazet goto copyval;
1534b4d84bceSEric Dumazet case IP_RECVTOS:
1535b4d84bceSEric Dumazet val = inet_test_bit(TOS, sk);
1536b4d84bceSEric Dumazet goto copyval;
1537b4d84bceSEric Dumazet case IP_RECVOPTS:
1538b4d84bceSEric Dumazet val = inet_test_bit(RECVOPTS, sk);
1539b4d84bceSEric Dumazet goto copyval;
1540b4d84bceSEric Dumazet case IP_RETOPTS:
1541b4d84bceSEric Dumazet val = inet_test_bit(RETOPTS, sk);
1542b4d84bceSEric Dumazet goto copyval;
1543b4d84bceSEric Dumazet case IP_PASSSEC:
1544b4d84bceSEric Dumazet val = inet_test_bit(PASSSEC, sk);
1545b4d84bceSEric Dumazet goto copyval;
1546b4d84bceSEric Dumazet case IP_RECVORIGDSTADDR:
1547b4d84bceSEric Dumazet val = inet_test_bit(ORIGDSTADDR, sk);
1548b4d84bceSEric Dumazet goto copyval;
1549b4d84bceSEric Dumazet case IP_CHECKSUM:
1550b4d84bceSEric Dumazet val = inet_test_bit(CHECKSUM, sk);
1551b4d84bceSEric Dumazet goto copyval;
1552b4d84bceSEric Dumazet case IP_RECVFRAGSIZE:
1553b4d84bceSEric Dumazet val = inet_test_bit(RECVFRAGSIZE, sk);
1554b4d84bceSEric Dumazet goto copyval;
15556b5f43eaSEric Dumazet case IP_RECVERR:
15566b5f43eaSEric Dumazet val = inet_test_bit(RECVERR, sk);
15576b5f43eaSEric Dumazet goto copyval;
15588e8cfb11SEric Dumazet case IP_RECVERR_RFC4884:
15598e8cfb11SEric Dumazet val = inet_test_bit(RECVERR_RFC4884, sk);
15608e8cfb11SEric Dumazet goto copyval;
15613f7e7532SEric Dumazet case IP_FREEBIND:
15623f7e7532SEric Dumazet val = inet_test_bit(FREEBIND, sk);
15633f7e7532SEric Dumazet goto copyval;
1564cafbe182SEric Dumazet case IP_HDRINCL:
1565cafbe182SEric Dumazet val = inet_test_bit(HDRINCL, sk);
1566cafbe182SEric Dumazet goto copyval;
1567b09bde5cSEric Dumazet case IP_MULTICAST_LOOP:
1568b09bde5cSEric Dumazet val = inet_test_bit(MC_LOOP, sk);
1569b09bde5cSEric Dumazet goto copyval;
1570307b4ac6SEric Dumazet case IP_MULTICAST_ALL:
1571307b4ac6SEric Dumazet val = inet_test_bit(MC_ALL, sk);
1572307b4ac6SEric Dumazet goto copyval;
15734bd0623fSEric Dumazet case IP_TRANSPARENT:
15744bd0623fSEric Dumazet val = inet_test_bit(TRANSPARENT, sk);
15754bd0623fSEric Dumazet goto copyval;
1576f04b8d34SEric Dumazet case IP_NODEFRAG:
1577f04b8d34SEric Dumazet val = inet_test_bit(NODEFRAG, sk);
1578f04b8d34SEric Dumazet goto copyval;
1579ca571e2eSEric Dumazet case IP_BIND_ADDRESS_NO_PORT:
1580ca571e2eSEric Dumazet val = inet_test_bit(BIND_ADDRESS_NO_PORT, sk);
1581ca571e2eSEric Dumazet goto copyval;
1582*eeb78df4SJuntong Deng case IP_ROUTER_ALERT:
1583*eeb78df4SJuntong Deng val = inet_test_bit(RTALERT, sk);
1584*eeb78df4SJuntong Deng goto copyval;
158510f42426SEric Dumazet case IP_TTL:
158610f42426SEric Dumazet val = READ_ONCE(inet->uc_ttl);
158710f42426SEric Dumazet if (val < 0)
158810f42426SEric Dumazet val = READ_ONCE(sock_net(sk)->ipv4.sysctl_ip_default_ttl);
158910f42426SEric Dumazet goto copyval;
159012af7326SEric Dumazet case IP_MINTTL:
159112af7326SEric Dumazet val = READ_ONCE(inet->min_ttl);
159212af7326SEric Dumazet goto copyval;
1593c9746e6aSEric Dumazet case IP_MULTICAST_TTL:
1594c9746e6aSEric Dumazet val = READ_ONCE(inet->mc_ttl);
1595c9746e6aSEric Dumazet goto copyval;
1596ceaa7141SEric Dumazet case IP_MTU_DISCOVER:
1597ceaa7141SEric Dumazet val = READ_ONCE(inet->pmtudisc);
1598ceaa7141SEric Dumazet goto copyval;
1599e08d0b3dSEric Dumazet case IP_TOS:
1600e08d0b3dSEric Dumazet val = READ_ONCE(inet->tos);
1601e08d0b3dSEric Dumazet goto copyval;
16021da177e4SLinus Torvalds case IP_OPTIONS:
16031da177e4SLinus Torvalds {
16041da177e4SLinus Torvalds unsigned char optbuf[sizeof(struct ip_options)+40];
16051da177e4SLinus Torvalds struct ip_options *opt = (struct ip_options *)optbuf;
1606f6d8bd05SEric Dumazet struct ip_options_rcu *inet_opt;
1607f6d8bd05SEric Dumazet
1608a4725d0dSEric Dumazet rcu_read_lock();
1609a4725d0dSEric Dumazet inet_opt = rcu_dereference(inet->inet_opt);
16101da177e4SLinus Torvalds opt->optlen = 0;
1611f6d8bd05SEric Dumazet if (inet_opt)
1612f6d8bd05SEric Dumazet memcpy(optbuf, &inet_opt->opt,
16131da177e4SLinus Torvalds sizeof(struct ip_options) +
1614f6d8bd05SEric Dumazet inet_opt->opt.optlen);
1615a4725d0dSEric Dumazet rcu_read_unlock();
16161da177e4SLinus Torvalds
1617728f064cSMartin KaFai Lau if (opt->optlen == 0) {
1618728f064cSMartin KaFai Lau len = 0;
1619728f064cSMartin KaFai Lau return copy_to_sockptr(optlen, &len, sizeof(int));
1620728f064cSMartin KaFai Lau }
16211da177e4SLinus Torvalds
16221da177e4SLinus Torvalds ip_options_undo(opt);
16231da177e4SLinus Torvalds
16241da177e4SLinus Torvalds len = min_t(unsigned int, len, opt->optlen);
1625728f064cSMartin KaFai Lau if (copy_to_sockptr(optlen, &len, sizeof(int)))
16261da177e4SLinus Torvalds return -EFAULT;
1627728f064cSMartin KaFai Lau if (copy_to_sockptr(optval, opt->__data, len))
16281da177e4SLinus Torvalds return -EFAULT;
16291da177e4SLinus Torvalds return 0;
16301da177e4SLinus Torvalds }
16311da177e4SLinus Torvalds case IP_MTU:
16321da177e4SLinus Torvalds {
16331da177e4SLinus Torvalds struct dst_entry *dst;
16341da177e4SLinus Torvalds val = 0;
16351da177e4SLinus Torvalds dst = sk_dst_get(sk);
16361da177e4SLinus Torvalds if (dst) {
16371da177e4SLinus Torvalds val = dst_mtu(dst);
16381da177e4SLinus Torvalds dst_release(dst);
16391da177e4SLinus Torvalds }
16403523bc91SEric Dumazet if (!val)
16411da177e4SLinus Torvalds return -ENOTCONN;
16423523bc91SEric Dumazet goto copyval;
16431da177e4SLinus Torvalds }
1644c4480eb5SEric Dumazet case IP_PKTOPTIONS:
1645c4480eb5SEric Dumazet {
1646c4480eb5SEric Dumazet struct msghdr msg;
1647c4480eb5SEric Dumazet
1648c4480eb5SEric Dumazet if (sk->sk_type != SOCK_STREAM)
1649c4480eb5SEric Dumazet return -ENOPROTOOPT;
1650c4480eb5SEric Dumazet
1651c4480eb5SEric Dumazet if (optval.is_kernel) {
1652c4480eb5SEric Dumazet msg.msg_control_is_user = false;
1653c4480eb5SEric Dumazet msg.msg_control = optval.kernel;
1654c4480eb5SEric Dumazet } else {
1655c4480eb5SEric Dumazet msg.msg_control_is_user = true;
1656c4480eb5SEric Dumazet msg.msg_control_user = optval.user;
1657c4480eb5SEric Dumazet }
1658c4480eb5SEric Dumazet msg.msg_controllen = len;
1659c4480eb5SEric Dumazet msg.msg_flags = in_compat_syscall() ? MSG_CMSG_COMPAT : 0;
1660c4480eb5SEric Dumazet
1661c4480eb5SEric Dumazet if (inet_test_bit(PKTINFO, sk)) {
1662c4480eb5SEric Dumazet struct in_pktinfo info;
1663c4480eb5SEric Dumazet
1664c4480eb5SEric Dumazet info.ipi_addr.s_addr = READ_ONCE(inet->inet_rcv_saddr);
1665c4480eb5SEric Dumazet info.ipi_spec_dst.s_addr = READ_ONCE(inet->inet_rcv_saddr);
1666c4480eb5SEric Dumazet info.ipi_ifindex = READ_ONCE(inet->mc_index);
1667c4480eb5SEric Dumazet put_cmsg(&msg, SOL_IP, IP_PKTINFO, sizeof(info), &info);
1668c4480eb5SEric Dumazet }
1669c4480eb5SEric Dumazet if (inet_test_bit(TTL, sk)) {
1670c4480eb5SEric Dumazet int hlim = READ_ONCE(inet->mc_ttl);
1671c4480eb5SEric Dumazet
1672c4480eb5SEric Dumazet put_cmsg(&msg, SOL_IP, IP_TTL, sizeof(hlim), &hlim);
1673c4480eb5SEric Dumazet }
1674c4480eb5SEric Dumazet if (inet_test_bit(TOS, sk)) {
1675c4480eb5SEric Dumazet int tos = READ_ONCE(inet->rcv_tos);
1676c4480eb5SEric Dumazet put_cmsg(&msg, SOL_IP, IP_TOS, sizeof(tos), &tos);
1677c4480eb5SEric Dumazet }
1678c4480eb5SEric Dumazet len -= msg.msg_controllen;
1679c4480eb5SEric Dumazet return copy_to_sockptr(optlen, &len, sizeof(int));
1680c4480eb5SEric Dumazet }
1681959d5c11SEric Dumazet case IP_UNICAST_IF:
1682959d5c11SEric Dumazet val = (__force int)htonl((__u32) READ_ONCE(inet->uc_index));
1683959d5c11SEric Dumazet goto copyval;
16841da177e4SLinus Torvalds case IP_MULTICAST_IF:
16851da177e4SLinus Torvalds {
16861da177e4SLinus Torvalds struct in_addr addr;
16871da177e4SLinus Torvalds len = min_t(unsigned int, len, sizeof(struct in_addr));
168802715925SEric Dumazet addr.s_addr = READ_ONCE(inet->mc_addr);
16891da177e4SLinus Torvalds
1690728f064cSMartin KaFai Lau if (copy_to_sockptr(optlen, &len, sizeof(int)))
16911da177e4SLinus Torvalds return -EFAULT;
1692728f064cSMartin KaFai Lau if (copy_to_sockptr(optval, &addr, len))
16931da177e4SLinus Torvalds return -EFAULT;
16941da177e4SLinus Torvalds return 0;
16951da177e4SLinus Torvalds }
1696d9f28735SDavid Laight case IP_LOCAL_PORT_RANGE:
1697d9f28735SDavid Laight val = READ_ONCE(inet->local_port_range);
1698d9f28735SDavid Laight goto copyval;
169902715925SEric Dumazet }
170002715925SEric Dumazet
170102715925SEric Dumazet if (needs_rtnl)
170202715925SEric Dumazet rtnl_lock();
170302715925SEric Dumazet sockopt_lock_sock(sk);
170402715925SEric Dumazet
170502715925SEric Dumazet switch (optname) {
17061da177e4SLinus Torvalds case IP_MSFILTER:
17071da177e4SLinus Torvalds {
17081da177e4SLinus Torvalds struct ip_msfilter msf;
17091da177e4SLinus Torvalds
17104167a960SGustavo A. R. Silva if (len < IP_MSFILTER_SIZE(0)) {
171187e9f031SWANG Cong err = -EINVAL;
171287e9f031SWANG Cong goto out;
17131da177e4SLinus Torvalds }
1714728f064cSMartin KaFai Lau if (copy_from_sockptr(&msf, optval, IP_MSFILTER_SIZE(0))) {
171587e9f031SWANG Cong err = -EFAULT;
171687e9f031SWANG Cong goto out;
17171da177e4SLinus Torvalds }
1718728f064cSMartin KaFai Lau err = ip_mc_msfget(sk, &msf, optval, optlen);
171987e9f031SWANG Cong goto out;
17201da177e4SLinus Torvalds }
17211da177e4SLinus Torvalds case MCAST_MSFILTER:
1722b6238c04SChristoph Hellwig if (in_compat_syscall())
1723b6238c04SChristoph Hellwig err = compat_ip_get_mcast_msfilter(sk, optval, optlen,
1724b6238c04SChristoph Hellwig len);
1725b6238c04SChristoph Hellwig else
172649e74c24SChristoph Hellwig err = ip_get_mcast_msfilter(sk, optval, optlen, len);
172787e9f031SWANG Cong goto out;
17283632679dSNicolas Dichtel case IP_PROTOCOL:
17293632679dSNicolas Dichtel val = inet_sk(sk)->inet_num;
17303632679dSNicolas Dichtel break;
17311da177e4SLinus Torvalds default:
17321985320cSMartin KaFai Lau sockopt_release_sock(sk);
17331da177e4SLinus Torvalds return -ENOPROTOOPT;
17341da177e4SLinus Torvalds }
17351985320cSMartin KaFai Lau sockopt_release_sock(sk);
1736b4d84bceSEric Dumazet copyval:
1737951e07c9SDavid S. Miller if (len < sizeof(int) && len > 0 && val >= 0 && val <= 255) {
17381da177e4SLinus Torvalds unsigned char ucval = (unsigned char)val;
17391da177e4SLinus Torvalds len = 1;
1740728f064cSMartin KaFai Lau if (copy_to_sockptr(optlen, &len, sizeof(int)))
17411da177e4SLinus Torvalds return -EFAULT;
1742728f064cSMartin KaFai Lau if (copy_to_sockptr(optval, &ucval, 1))
17431da177e4SLinus Torvalds return -EFAULT;
17441da177e4SLinus Torvalds } else {
17451da177e4SLinus Torvalds len = min_t(unsigned int, sizeof(int), len);
1746728f064cSMartin KaFai Lau if (copy_to_sockptr(optlen, &len, sizeof(int)))
17471da177e4SLinus Torvalds return -EFAULT;
1748728f064cSMartin KaFai Lau if (copy_to_sockptr(optval, &val, len))
17491da177e4SLinus Torvalds return -EFAULT;
17501da177e4SLinus Torvalds }
17511da177e4SLinus Torvalds return 0;
175287e9f031SWANG Cong
175387e9f031SWANG Cong out:
17541985320cSMartin KaFai Lau sockopt_release_sock(sk);
175587e9f031SWANG Cong if (needs_rtnl)
175687e9f031SWANG Cong rtnl_unlock();
175787e9f031SWANG Cong return err;
17581da177e4SLinus Torvalds }
17591da177e4SLinus Torvalds
ip_getsockopt(struct sock * sk,int level,int optname,char __user * optval,int __user * optlen)17603fdadf7dSDmitry Mishin int ip_getsockopt(struct sock *sk, int level,
17613fdadf7dSDmitry Mishin int optname, char __user *optval, int __user *optlen)
17623fdadf7dSDmitry Mishin {
17633fdadf7dSDmitry Mishin int err;
17643fdadf7dSDmitry Mishin
1765728f064cSMartin KaFai Lau err = do_ip_getsockopt(sk, level, optname,
1766728f064cSMartin KaFai Lau USER_SOCKPTR(optval), USER_SOCKPTR(optlen));
1767b6238c04SChristoph Hellwig
17683fdadf7dSDmitry Mishin #ifdef CONFIG_NETFILTER
17693fdadf7dSDmitry Mishin /* we need to exclude all possible ENOPROTOOPTs except default case */
17706a9fb947SPavel Emelyanov if (err == -ENOPROTOOPT && optname != IP_PKTOPTIONS &&
17716a9fb947SPavel Emelyanov !ip_mroute_opt(optname)) {
17723fdadf7dSDmitry Mishin int len;
17733fdadf7dSDmitry Mishin
17743fdadf7dSDmitry Mishin if (get_user(len, optlen))
17753fdadf7dSDmitry Mishin return -EFAULT;
17763fdadf7dSDmitry Mishin
177701ea306fSPaolo Abeni err = nf_getsockopt(sk, PF_INET, optname, optval, &len);
17783fdadf7dSDmitry Mishin if (err >= 0)
17793fdadf7dSDmitry Mishin err = put_user(len, optlen);
17803fdadf7dSDmitry Mishin return err;
17813fdadf7dSDmitry Mishin }
17823fdadf7dSDmitry Mishin #endif
17833fdadf7dSDmitry Mishin return err;
17843fdadf7dSDmitry Mishin }
17854d52cfbeSEric Dumazet EXPORT_SYMBOL(ip_getsockopt);
1786