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