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