1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0 21da177e4SLinus Torvalds /* 31da177e4SLinus Torvalds * INET An implementation of the TCP/IP protocol suite for the LINUX 41da177e4SLinus Torvalds * operating system. INET is implemented using the BSD Socket 51da177e4SLinus Torvalds * interface as the means of communication with the user level. 61da177e4SLinus Torvalds * 71da177e4SLinus Torvalds * The IP to API glue. 81da177e4SLinus Torvalds * 91da177e4SLinus Torvalds * Authors: see ip.c 101da177e4SLinus Torvalds * 111da177e4SLinus Torvalds * Fixes: 121da177e4SLinus Torvalds * Many : Split from ip.c , see ip.c for history. 131da177e4SLinus Torvalds * Martin Mares : TOS setting fixed. 141da177e4SLinus Torvalds * Alan Cox : Fixed a couple of oopses in Martin's 151da177e4SLinus Torvalds * TOS tweaks. 161da177e4SLinus Torvalds * Mike McLagan : Routing by source 171da177e4SLinus Torvalds */ 181da177e4SLinus Torvalds 191da177e4SLinus Torvalds #include <linux/module.h> 201da177e4SLinus Torvalds #include <linux/types.h> 211da177e4SLinus Torvalds #include <linux/mm.h> 221da177e4SLinus Torvalds #include <linux/skbuff.h> 231da177e4SLinus Torvalds #include <linux/ip.h> 241da177e4SLinus Torvalds #include <linux/icmp.h> 2514c85021SArnaldo Carvalho de Melo #include <linux/inetdevice.h> 261da177e4SLinus Torvalds #include <linux/netdevice.h> 275a0e3ad6STejun Heo #include <linux/slab.h> 281da177e4SLinus Torvalds #include <net/sock.h> 291da177e4SLinus Torvalds #include <net/ip.h> 301da177e4SLinus Torvalds #include <net/icmp.h> 31d83d8461SArnaldo Carvalho de Melo #include <net/tcp_states.h> 321da177e4SLinus Torvalds #include <linux/udp.h> 331da177e4SLinus Torvalds #include <linux/igmp.h> 341da177e4SLinus Torvalds #include <linux/netfilter.h> 351da177e4SLinus Torvalds #include <linux/route.h> 361da177e4SLinus Torvalds #include <linux/mroute.h> 372c67e9acSMaciej Żenczykowski #include <net/inet_ecn.h> 381da177e4SLinus Torvalds #include <net/route.h> 391da177e4SLinus Torvalds #include <net/xfrm.h> 40dae50295SDavid L Stevens #include <net/compat.h> 41ad6f939aSTom Herbert #include <net/checksum.h> 42dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6) 431da177e4SLinus Torvalds #include <net/transp_v6.h> 441da177e4SLinus Torvalds #endif 4535ebf65eSDavid S. Miller #include <net/ip_fib.h> 461da177e4SLinus Torvalds 471da177e4SLinus Torvalds #include <linux/errqueue.h> 487c0f6ba6SLinus Torvalds #include <linux/uaccess.h> 491da177e4SLinus Torvalds 501da177e4SLinus Torvalds /* 511da177e4SLinus Torvalds * SOL_IP control messages. 521da177e4SLinus Torvalds */ 531da177e4SLinus Torvalds 541da177e4SLinus Torvalds static void ip_cmsg_recv_pktinfo(struct msghdr *msg, struct sk_buff *skb) 551da177e4SLinus Torvalds { 56d826eb14SEric Dumazet struct in_pktinfo info = *PKTINFO_SKB_CB(skb); 571da177e4SLinus Torvalds 58eddc9ec5SArnaldo Carvalho de Melo info.ipi_addr.s_addr = ip_hdr(skb)->daddr; 591da177e4SLinus Torvalds 601da177e4SLinus Torvalds put_cmsg(msg, SOL_IP, IP_PKTINFO, sizeof(info), &info); 611da177e4SLinus Torvalds } 621da177e4SLinus Torvalds 631da177e4SLinus Torvalds static void ip_cmsg_recv_ttl(struct msghdr *msg, struct sk_buff *skb) 641da177e4SLinus Torvalds { 65eddc9ec5SArnaldo Carvalho de Melo int ttl = ip_hdr(skb)->ttl; 661da177e4SLinus Torvalds put_cmsg(msg, SOL_IP, IP_TTL, sizeof(int), &ttl); 671da177e4SLinus Torvalds } 681da177e4SLinus Torvalds 691da177e4SLinus Torvalds static void ip_cmsg_recv_tos(struct msghdr *msg, struct sk_buff *skb) 701da177e4SLinus Torvalds { 71eddc9ec5SArnaldo Carvalho de Melo put_cmsg(msg, SOL_IP, IP_TOS, 1, &ip_hdr(skb)->tos); 721da177e4SLinus Torvalds } 731da177e4SLinus Torvalds 741da177e4SLinus Torvalds static void ip_cmsg_recv_opts(struct msghdr *msg, struct sk_buff *skb) 751da177e4SLinus Torvalds { 761da177e4SLinus Torvalds if (IPCB(skb)->opt.optlen == 0) 771da177e4SLinus Torvalds return; 781da177e4SLinus Torvalds 79eddc9ec5SArnaldo Carvalho de Melo put_cmsg(msg, SOL_IP, IP_RECVOPTS, IPCB(skb)->opt.optlen, 80eddc9ec5SArnaldo Carvalho de Melo ip_hdr(skb) + 1); 811da177e4SLinus Torvalds } 821da177e4SLinus Torvalds 831da177e4SLinus Torvalds 8491ed1e66SPaolo Abeni static void ip_cmsg_recv_retopts(struct net *net, struct msghdr *msg, 8591ed1e66SPaolo Abeni struct sk_buff *skb) 861da177e4SLinus Torvalds { 871da177e4SLinus Torvalds unsigned char optbuf[sizeof(struct ip_options) + 40]; 881da177e4SLinus Torvalds struct ip_options *opt = (struct ip_options *)optbuf; 891da177e4SLinus Torvalds 901da177e4SLinus Torvalds if (IPCB(skb)->opt.optlen == 0) 911da177e4SLinus Torvalds return; 921da177e4SLinus Torvalds 9391ed1e66SPaolo Abeni if (ip_options_echo(net, opt, skb)) { 941da177e4SLinus Torvalds msg->msg_flags |= MSG_CTRUNC; 951da177e4SLinus Torvalds return; 961da177e4SLinus Torvalds } 971da177e4SLinus Torvalds ip_options_undo(opt); 981da177e4SLinus Torvalds 991da177e4SLinus Torvalds put_cmsg(msg, SOL_IP, IP_RETOPTS, opt->optlen, opt->__data); 1001da177e4SLinus Torvalds } 1011da177e4SLinus Torvalds 10270ecc248SWillem de Bruijn static void ip_cmsg_recv_fragsize(struct msghdr *msg, struct sk_buff *skb) 10370ecc248SWillem de Bruijn { 10470ecc248SWillem de Bruijn int val; 10570ecc248SWillem de Bruijn 10670ecc248SWillem de Bruijn if (IPCB(skb)->frag_max_size == 0) 10770ecc248SWillem de Bruijn return; 10870ecc248SWillem de Bruijn 10970ecc248SWillem de Bruijn val = IPCB(skb)->frag_max_size; 11070ecc248SWillem de Bruijn put_cmsg(msg, SOL_IP, IP_RECVFRAGSIZE, sizeof(val), &val); 11170ecc248SWillem de Bruijn } 11270ecc248SWillem de Bruijn 113ad6f939aSTom Herbert static void ip_cmsg_recv_checksum(struct msghdr *msg, struct sk_buff *skb, 11410df8e61SEric Dumazet int tlen, int offset) 115ad6f939aSTom Herbert { 116ad6f939aSTom Herbert __wsum csum = skb->csum; 117ad6f939aSTom Herbert 118ad6f939aSTom Herbert if (skb->ip_summed != CHECKSUM_COMPLETE) 119ad6f939aSTom Herbert return; 120ad6f939aSTom Herbert 121ca4ef457SPaolo Abeni if (offset != 0) { 122ca4ef457SPaolo Abeni int tend_off = skb_transport_offset(skb) + tlen; 123ca4ef457SPaolo Abeni csum = csum_sub(csum, skb_checksum(skb, tend_off, offset, 0)); 124ca4ef457SPaolo Abeni } 125ad6f939aSTom Herbert 126ad6f939aSTom Herbert put_cmsg(msg, SOL_IP, IP_CHECKSUM, sizeof(__wsum), &csum); 127ad6f939aSTom Herbert } 128ad6f939aSTom Herbert 1292c7946a7SCatherine Zhang static void ip_cmsg_recv_security(struct msghdr *msg, struct sk_buff *skb) 1302c7946a7SCatherine Zhang { 1312c7946a7SCatherine Zhang char *secdata; 132dc49c1f9SCatherine Zhang u32 seclen, secid; 1332c7946a7SCatherine Zhang int err; 1342c7946a7SCatherine Zhang 135dc49c1f9SCatherine Zhang err = security_socket_getpeersec_dgram(NULL, skb, &secid); 136dc49c1f9SCatherine Zhang if (err) 137dc49c1f9SCatherine Zhang return; 138dc49c1f9SCatherine Zhang 139dc49c1f9SCatherine Zhang err = security_secid_to_secctx(secid, &secdata, &seclen); 1402c7946a7SCatherine Zhang if (err) 1412c7946a7SCatherine Zhang return; 1422c7946a7SCatherine Zhang 1432c7946a7SCatherine Zhang put_cmsg(msg, SOL_IP, SCM_SECURITY, seclen, secdata); 144dc49c1f9SCatherine Zhang security_release_secctx(secdata, seclen); 1452c7946a7SCatherine Zhang } 1462c7946a7SCatherine Zhang 14721d1a161SHarvey Harrison static void ip_cmsg_recv_dstaddr(struct msghdr *msg, struct sk_buff *skb) 148e8b2dfe9SBalazs Scheidler { 149e8b2dfe9SBalazs Scheidler struct sockaddr_in sin; 150b71d1d42SEric Dumazet const struct iphdr *iph = ip_hdr(skb); 15121d1a161SHarvey Harrison __be16 *ports = (__be16 *)skb_transport_header(skb); 152e8b2dfe9SBalazs Scheidler 15339b2dd76SWillem de Bruijn if (skb_transport_offset(skb) + 4 > (int)skb->len) 154e8b2dfe9SBalazs Scheidler return; 155e8b2dfe9SBalazs Scheidler 156e8b2dfe9SBalazs Scheidler /* All current transport protocols have the port numbers in the 157e8b2dfe9SBalazs Scheidler * first four bytes of the transport header and this function is 158e8b2dfe9SBalazs Scheidler * written with this assumption in mind. 159e8b2dfe9SBalazs Scheidler */ 160e8b2dfe9SBalazs Scheidler 161e8b2dfe9SBalazs Scheidler sin.sin_family = AF_INET; 162e8b2dfe9SBalazs Scheidler sin.sin_addr.s_addr = iph->daddr; 163e8b2dfe9SBalazs Scheidler sin.sin_port = ports[1]; 164e8b2dfe9SBalazs Scheidler memset(sin.sin_zero, 0, sizeof(sin.sin_zero)); 165e8b2dfe9SBalazs Scheidler 166e8b2dfe9SBalazs Scheidler put_cmsg(msg, SOL_IP, IP_ORIGDSTADDR, sizeof(sin), &sin); 167e8b2dfe9SBalazs Scheidler } 1681da177e4SLinus Torvalds 169ad959036SPaolo Abeni void ip_cmsg_recv_offset(struct msghdr *msg, struct sock *sk, 170ad959036SPaolo Abeni struct sk_buff *skb, int tlen, int offset) 1711da177e4SLinus Torvalds { 172ad959036SPaolo Abeni struct inet_sock *inet = inet_sk(sk); 17395c96174SEric Dumazet unsigned int flags = inet->cmsg_flags; 1741da177e4SLinus Torvalds 1751da177e4SLinus Torvalds /* Ordered by supposed usage frequency */ 176c44d13d6STom Herbert if (flags & IP_CMSG_PKTINFO) { 1771da177e4SLinus Torvalds ip_cmsg_recv_pktinfo(msg, skb); 1781da177e4SLinus Torvalds 179c44d13d6STom Herbert flags &= ~IP_CMSG_PKTINFO; 180c44d13d6STom Herbert if (!flags) 181c44d13d6STom Herbert return; 182c44d13d6STom Herbert } 183c44d13d6STom Herbert 184c44d13d6STom Herbert if (flags & IP_CMSG_TTL) { 1851da177e4SLinus Torvalds ip_cmsg_recv_ttl(msg, skb); 1861da177e4SLinus Torvalds 187c44d13d6STom Herbert flags &= ~IP_CMSG_TTL; 188c44d13d6STom Herbert if (!flags) 189c44d13d6STom Herbert return; 190c44d13d6STom Herbert } 191c44d13d6STom Herbert 192c44d13d6STom Herbert if (flags & IP_CMSG_TOS) { 1931da177e4SLinus Torvalds ip_cmsg_recv_tos(msg, skb); 1941da177e4SLinus Torvalds 195c44d13d6STom Herbert flags &= ~IP_CMSG_TOS; 196c44d13d6STom Herbert if (!flags) 197c44d13d6STom Herbert return; 198c44d13d6STom Herbert } 199c44d13d6STom Herbert 200c44d13d6STom Herbert if (flags & IP_CMSG_RECVOPTS) { 2011da177e4SLinus Torvalds ip_cmsg_recv_opts(msg, skb); 2021da177e4SLinus Torvalds 203c44d13d6STom Herbert flags &= ~IP_CMSG_RECVOPTS; 204c44d13d6STom Herbert if (!flags) 205c44d13d6STom Herbert return; 206c44d13d6STom Herbert } 207c44d13d6STom Herbert 208c44d13d6STom Herbert if (flags & IP_CMSG_RETOPTS) { 20991ed1e66SPaolo Abeni ip_cmsg_recv_retopts(sock_net(sk), msg, skb); 2102c7946a7SCatherine Zhang 211c44d13d6STom Herbert flags &= ~IP_CMSG_RETOPTS; 212c44d13d6STom Herbert if (!flags) 213c44d13d6STom Herbert return; 214c44d13d6STom Herbert } 215c44d13d6STom Herbert 216c44d13d6STom Herbert if (flags & IP_CMSG_PASSSEC) { 2172c7946a7SCatherine Zhang ip_cmsg_recv_security(msg, skb); 218e8b2dfe9SBalazs Scheidler 219c44d13d6STom Herbert flags &= ~IP_CMSG_PASSSEC; 220c44d13d6STom Herbert if (!flags) 221e8b2dfe9SBalazs Scheidler return; 222c44d13d6STom Herbert } 223c44d13d6STom Herbert 224ad6f939aSTom Herbert if (flags & IP_CMSG_ORIGDSTADDR) { 225e8b2dfe9SBalazs Scheidler ip_cmsg_recv_dstaddr(msg, skb); 226e8b2dfe9SBalazs Scheidler 227ad6f939aSTom Herbert flags &= ~IP_CMSG_ORIGDSTADDR; 228ad6f939aSTom Herbert if (!flags) 229ad6f939aSTom Herbert return; 230ad6f939aSTom Herbert } 231ad6f939aSTom Herbert 232ad6f939aSTom Herbert if (flags & IP_CMSG_CHECKSUM) 23310df8e61SEric Dumazet ip_cmsg_recv_checksum(msg, skb, tlen, offset); 23470ecc248SWillem de Bruijn 23570ecc248SWillem de Bruijn if (flags & IP_CMSG_RECVFRAGSIZE) 23670ecc248SWillem de Bruijn ip_cmsg_recv_fragsize(msg, skb); 2371da177e4SLinus Torvalds } 2385961de9fSTom Herbert EXPORT_SYMBOL(ip_cmsg_recv_offset); 2391da177e4SLinus Torvalds 24024025c46SSoheil Hassas Yeganeh int ip_cmsg_send(struct sock *sk, struct msghdr *msg, struct ipcm_cookie *ipc, 241c8e6ad08SHannes Frederic Sowa bool allow_ipv6) 2421da177e4SLinus Torvalds { 243f02db315SFrancesco Fusco int err, val; 2441da177e4SLinus Torvalds struct cmsghdr *cmsg; 24524025c46SSoheil Hassas Yeganeh struct net *net = sock_net(sk); 2461da177e4SLinus Torvalds 247f95b414eSGu Zheng for_each_cmsghdr(cmsg, msg) { 2481da177e4SLinus Torvalds if (!CMSG_OK(msg, cmsg)) 2491da177e4SLinus Torvalds return -EINVAL; 2505337b5b7SEric Dumazet #if IS_ENABLED(CONFIG_IPV6) 251c8e6ad08SHannes Frederic Sowa if (allow_ipv6 && 252c8e6ad08SHannes Frederic Sowa cmsg->cmsg_level == SOL_IPV6 && 253c8e6ad08SHannes Frederic Sowa cmsg->cmsg_type == IPV6_PKTINFO) { 254c8e6ad08SHannes Frederic Sowa struct in6_pktinfo *src_info; 255c8e6ad08SHannes Frederic Sowa 256c8e6ad08SHannes Frederic Sowa if (cmsg->cmsg_len < CMSG_LEN(sizeof(*src_info))) 257c8e6ad08SHannes Frederic Sowa return -EINVAL; 258c8e6ad08SHannes Frederic Sowa src_info = (struct in6_pktinfo *)CMSG_DATA(cmsg); 259c8e6ad08SHannes Frederic Sowa if (!ipv6_addr_v4mapped(&src_info->ipi6_addr)) 260c8e6ad08SHannes Frederic Sowa return -EINVAL; 261*1cbec076SDavid Ahern if (src_info->ipi6_ifindex) 262c8e6ad08SHannes Frederic Sowa ipc->oif = src_info->ipi6_ifindex; 263c8e6ad08SHannes Frederic Sowa ipc->addr = src_info->ipi6_addr.s6_addr32[3]; 264c8e6ad08SHannes Frederic Sowa continue; 265c8e6ad08SHannes Frederic Sowa } 266c8e6ad08SHannes Frederic Sowa #endif 26724025c46SSoheil Hassas Yeganeh if (cmsg->cmsg_level == SOL_SOCKET) { 2682632616bSEric Dumazet err = __sock_cmsg_send(sk, msg, cmsg, &ipc->sockc); 2692632616bSEric Dumazet if (err) 2702632616bSEric Dumazet return err; 27124025c46SSoheil Hassas Yeganeh continue; 27224025c46SSoheil Hassas Yeganeh } 27324025c46SSoheil Hassas Yeganeh 2741da177e4SLinus Torvalds if (cmsg->cmsg_level != SOL_IP) 2751da177e4SLinus Torvalds continue; 2761da177e4SLinus Torvalds switch (cmsg->cmsg_type) { 2771da177e4SLinus Torvalds case IP_RETOPTS: 2781ff8cebfSyuan linyu err = cmsg->cmsg_len - sizeof(struct cmsghdr); 27991948309SEric Dumazet 28091948309SEric Dumazet /* Our caller is responsible for freeing ipc->opt */ 2814d52cfbeSEric Dumazet err = ip_options_get(net, &ipc->opt, CMSG_DATA(cmsg), 2824d52cfbeSEric Dumazet err < 40 ? err : 40); 2831da177e4SLinus Torvalds if (err) 2841da177e4SLinus Torvalds return err; 2851da177e4SLinus Torvalds break; 2861da177e4SLinus Torvalds case IP_PKTINFO: 2871da177e4SLinus Torvalds { 2881da177e4SLinus Torvalds struct in_pktinfo *info; 2891da177e4SLinus Torvalds if (cmsg->cmsg_len != CMSG_LEN(sizeof(struct in_pktinfo))) 2901da177e4SLinus Torvalds return -EINVAL; 2911da177e4SLinus Torvalds info = (struct in_pktinfo *)CMSG_DATA(cmsg); 292*1cbec076SDavid Ahern if (info->ipi_ifindex) 2931da177e4SLinus Torvalds ipc->oif = info->ipi_ifindex; 2941da177e4SLinus Torvalds ipc->addr = info->ipi_spec_dst.s_addr; 2951da177e4SLinus Torvalds break; 2961da177e4SLinus Torvalds } 297f02db315SFrancesco Fusco case IP_TTL: 298f02db315SFrancesco Fusco if (cmsg->cmsg_len != CMSG_LEN(sizeof(int))) 299f02db315SFrancesco Fusco return -EINVAL; 300f02db315SFrancesco Fusco val = *(int *)CMSG_DATA(cmsg); 301f02db315SFrancesco Fusco if (val < 1 || val > 255) 302f02db315SFrancesco Fusco return -EINVAL; 303f02db315SFrancesco Fusco ipc->ttl = val; 304f02db315SFrancesco Fusco break; 305f02db315SFrancesco Fusco case IP_TOS: 306e895cdceSEric Dumazet if (cmsg->cmsg_len == CMSG_LEN(sizeof(int))) 307f02db315SFrancesco Fusco val = *(int *)CMSG_DATA(cmsg); 308e895cdceSEric Dumazet else if (cmsg->cmsg_len == CMSG_LEN(sizeof(u8))) 309e895cdceSEric Dumazet val = *(u8 *)CMSG_DATA(cmsg); 310e895cdceSEric Dumazet else 311e895cdceSEric Dumazet return -EINVAL; 312f02db315SFrancesco Fusco if (val < 0 || val > 255) 313f02db315SFrancesco Fusco return -EINVAL; 314f02db315SFrancesco Fusco ipc->tos = val; 315f02db315SFrancesco Fusco ipc->priority = rt_tos2priority(ipc->tos); 316f02db315SFrancesco Fusco break; 317f02db315SFrancesco Fusco 3181da177e4SLinus Torvalds default: 3191da177e4SLinus Torvalds return -EINVAL; 3201da177e4SLinus Torvalds } 3211da177e4SLinus Torvalds } 3221da177e4SLinus Torvalds return 0; 3231da177e4SLinus Torvalds } 3241da177e4SLinus Torvalds 3251da177e4SLinus Torvalds 3261da177e4SLinus Torvalds /* Special input handler for packets caught by router alert option. 3271da177e4SLinus Torvalds They are selected only by protocol field, and then processed likely 3281da177e4SLinus Torvalds local ones; but only if someone wants them! Otherwise, router 3291da177e4SLinus Torvalds not running rsvpd will kill RSVP. 3301da177e4SLinus Torvalds 3311da177e4SLinus Torvalds It is user level problem, what it will make with them. 3321da177e4SLinus Torvalds I have no idea, how it will masquearde or NAT them (it is joke, joke :-)), 3331da177e4SLinus Torvalds but receiver should be enough clever f.e. to forward mtrace requests, 3341da177e4SLinus Torvalds sent to multicast group to reach destination designated router. 3351da177e4SLinus Torvalds */ 33643a951e9SEric Dumazet struct ip_ra_chain __rcu *ip_ra_chain; 33766018506SEric Dumazet 338592fcb9dSEric Dumazet 339592fcb9dSEric Dumazet static void ip_ra_destroy_rcu(struct rcu_head *head) 34066018506SEric Dumazet { 341592fcb9dSEric Dumazet struct ip_ra_chain *ra = container_of(head, struct ip_ra_chain, rcu); 342592fcb9dSEric Dumazet 343592fcb9dSEric Dumazet sock_put(ra->saved_sk); 344592fcb9dSEric Dumazet kfree(ra); 34566018506SEric Dumazet } 3461da177e4SLinus Torvalds 3474d52cfbeSEric Dumazet int ip_ra_control(struct sock *sk, unsigned char on, 3484d52cfbeSEric Dumazet void (*destructor)(struct sock *)) 3491da177e4SLinus Torvalds { 35043a951e9SEric Dumazet struct ip_ra_chain *ra, *new_ra; 35143a951e9SEric Dumazet struct ip_ra_chain __rcu **rap; 3521da177e4SLinus Torvalds 353c720c7e8SEric Dumazet if (sk->sk_type != SOCK_RAW || inet_sk(sk)->inet_num == IPPROTO_RAW) 3541da177e4SLinus Torvalds return -EINVAL; 3551da177e4SLinus Torvalds 3561da177e4SLinus Torvalds new_ra = on ? kmalloc(sizeof(*new_ra), GFP_KERNEL) : NULL; 3571da177e4SLinus Torvalds 35843a951e9SEric Dumazet for (rap = &ip_ra_chain; 359ba3f571dSWANG Cong (ra = rtnl_dereference(*rap)) != NULL; 36043a951e9SEric Dumazet rap = &ra->next) { 3611da177e4SLinus Torvalds if (ra->sk == sk) { 3621da177e4SLinus Torvalds if (on) { 3631da177e4SLinus Torvalds kfree(new_ra); 3641da177e4SLinus Torvalds return -EADDRINUSE; 3651da177e4SLinus Torvalds } 366592fcb9dSEric Dumazet /* dont let ip_call_ra_chain() use sk again */ 367592fcb9dSEric Dumazet ra->sk = NULL; 3688e380f00SEric Dumazet RCU_INIT_POINTER(*rap, ra->next); 3691da177e4SLinus Torvalds 3701da177e4SLinus Torvalds if (ra->destructor) 3711da177e4SLinus Torvalds ra->destructor(sk); 372592fcb9dSEric Dumazet /* 373592fcb9dSEric Dumazet * Delay sock_put(sk) and kfree(ra) after one rcu grace 374592fcb9dSEric Dumazet * period. This guarantee ip_call_ra_chain() dont need 375592fcb9dSEric Dumazet * to mess with socket refcounts. 376592fcb9dSEric Dumazet */ 377592fcb9dSEric Dumazet ra->saved_sk = sk; 378592fcb9dSEric Dumazet call_rcu(&ra->rcu, ip_ra_destroy_rcu); 3791da177e4SLinus Torvalds return 0; 3801da177e4SLinus Torvalds } 3811da177e4SLinus Torvalds } 382ba3f571dSWANG Cong if (!new_ra) 3831da177e4SLinus Torvalds return -ENOBUFS; 3841da177e4SLinus Torvalds new_ra->sk = sk; 3851da177e4SLinus Torvalds new_ra->destructor = destructor; 3861da177e4SLinus Torvalds 3878e380f00SEric Dumazet RCU_INIT_POINTER(new_ra->next, ra); 38866018506SEric Dumazet rcu_assign_pointer(*rap, new_ra); 3891da177e4SLinus Torvalds sock_hold(sk); 3901da177e4SLinus Torvalds 3911da177e4SLinus Torvalds return 0; 3921da177e4SLinus Torvalds } 3931da177e4SLinus Torvalds 3941da177e4SLinus Torvalds void ip_icmp_error(struct sock *sk, struct sk_buff *skb, int err, 39535986b32SAl Viro __be16 port, u32 info, u8 *payload) 3961da177e4SLinus Torvalds { 3971da177e4SLinus Torvalds struct sock_exterr_skb *serr; 3981da177e4SLinus Torvalds 3991da177e4SLinus Torvalds skb = skb_clone(skb, GFP_ATOMIC); 4001da177e4SLinus Torvalds if (!skb) 4011da177e4SLinus Torvalds return; 4021da177e4SLinus Torvalds 4031da177e4SLinus Torvalds serr = SKB_EXT_ERR(skb); 4041da177e4SLinus Torvalds serr->ee.ee_errno = err; 4051da177e4SLinus Torvalds serr->ee.ee_origin = SO_EE_ORIGIN_ICMP; 40688c7664fSArnaldo Carvalho de Melo serr->ee.ee_type = icmp_hdr(skb)->type; 40788c7664fSArnaldo Carvalho de Melo serr->ee.ee_code = icmp_hdr(skb)->code; 4081da177e4SLinus Torvalds serr->ee.ee_pad = 0; 4091da177e4SLinus Torvalds serr->ee.ee_info = info; 4101da177e4SLinus Torvalds serr->ee.ee_data = 0; 41188c7664fSArnaldo Carvalho de Melo serr->addr_offset = (u8 *)&(((struct iphdr *)(icmp_hdr(skb) + 1))->daddr) - 412d56f90a7SArnaldo Carvalho de Melo skb_network_header(skb); 4131da177e4SLinus Torvalds serr->port = port; 4141da177e4SLinus Torvalds 41500db4124SIan Morris if (skb_pull(skb, payload - skb->data)) { 416bd82393cSArnaldo Carvalho de Melo skb_reset_transport_header(skb); 417bd82393cSArnaldo Carvalho de Melo if (sock_queue_err_skb(sk, skb) == 0) 418bd82393cSArnaldo Carvalho de Melo return; 419bd82393cSArnaldo Carvalho de Melo } 4201da177e4SLinus Torvalds kfree_skb(skb); 4211da177e4SLinus Torvalds } 4221da177e4SLinus Torvalds 4230579016eSAl Viro void ip_local_error(struct sock *sk, int err, __be32 daddr, __be16 port, u32 info) 4241da177e4SLinus Torvalds { 4251da177e4SLinus Torvalds struct inet_sock *inet = inet_sk(sk); 4261da177e4SLinus Torvalds struct sock_exterr_skb *serr; 4271da177e4SLinus Torvalds struct iphdr *iph; 4281da177e4SLinus Torvalds struct sk_buff *skb; 4291da177e4SLinus Torvalds 4301da177e4SLinus Torvalds if (!inet->recverr) 4311da177e4SLinus Torvalds return; 4321da177e4SLinus Torvalds 4331da177e4SLinus Torvalds skb = alloc_skb(sizeof(struct iphdr), GFP_ATOMIC); 4341da177e4SLinus Torvalds if (!skb) 4351da177e4SLinus Torvalds return; 4361da177e4SLinus Torvalds 4372ca9e6f2SArnaldo Carvalho de Melo skb_put(skb, sizeof(struct iphdr)); 4382ca9e6f2SArnaldo Carvalho de Melo skb_reset_network_header(skb); 439eddc9ec5SArnaldo Carvalho de Melo iph = ip_hdr(skb); 4401da177e4SLinus Torvalds iph->daddr = daddr; 4411da177e4SLinus Torvalds 4421da177e4SLinus Torvalds serr = SKB_EXT_ERR(skb); 4431da177e4SLinus Torvalds serr->ee.ee_errno = err; 4441da177e4SLinus Torvalds serr->ee.ee_origin = SO_EE_ORIGIN_LOCAL; 4451da177e4SLinus Torvalds serr->ee.ee_type = 0; 4461da177e4SLinus Torvalds serr->ee.ee_code = 0; 4471da177e4SLinus Torvalds serr->ee.ee_pad = 0; 4481da177e4SLinus Torvalds serr->ee.ee_info = info; 4491da177e4SLinus Torvalds serr->ee.ee_data = 0; 450d56f90a7SArnaldo Carvalho de Melo serr->addr_offset = (u8 *)&iph->daddr - skb_network_header(skb); 4511da177e4SLinus Torvalds serr->port = port; 4521da177e4SLinus Torvalds 45327a884dcSArnaldo Carvalho de Melo __skb_pull(skb, skb_tail_pointer(skb) - skb->data); 454bd82393cSArnaldo Carvalho de Melo skb_reset_transport_header(skb); 4551da177e4SLinus Torvalds 4561da177e4SLinus Torvalds if (sock_queue_err_skb(sk, skb)) 4571da177e4SLinus Torvalds kfree_skb(skb); 4581da177e4SLinus Torvalds } 4591da177e4SLinus Torvalds 46034b99df4SJulian Anastasov /* For some errors we have valid addr_offset even with zero payload and 46134b99df4SJulian Anastasov * zero port. Also, addr_offset should be supported if port is set. 46234b99df4SJulian Anastasov */ 46334b99df4SJulian Anastasov static inline bool ipv4_datagram_support_addr(struct sock_exterr_skb *serr) 46434b99df4SJulian Anastasov { 46534b99df4SJulian Anastasov return serr->ee.ee_origin == SO_EE_ORIGIN_ICMP || 46634b99df4SJulian Anastasov serr->ee.ee_origin == SO_EE_ORIGIN_LOCAL || serr->port; 46734b99df4SJulian Anastasov } 46834b99df4SJulian Anastasov 469c247f053SWillem de Bruijn /* IPv4 supports cmsg on all imcp errors and some timestamps 470c247f053SWillem de Bruijn * 471c247f053SWillem de Bruijn * Timestamp code paths do not initialize the fields expected by cmsg: 472c247f053SWillem de Bruijn * the PKTINFO fields in skb->cb[]. Fill those in here. 473c247f053SWillem de Bruijn */ 474c247f053SWillem de Bruijn static bool ipv4_datagram_support_cmsg(const struct sock *sk, 475c247f053SWillem de Bruijn struct sk_buff *skb, 476829ae9d6SWillem de Bruijn int ee_origin) 477829ae9d6SWillem de Bruijn { 478c247f053SWillem de Bruijn struct in_pktinfo *info; 479829ae9d6SWillem de Bruijn 480c247f053SWillem de Bruijn if (ee_origin == SO_EE_ORIGIN_ICMP) 481c247f053SWillem de Bruijn return true; 482c247f053SWillem de Bruijn 483c247f053SWillem de Bruijn if (ee_origin == SO_EE_ORIGIN_LOCAL) 484c247f053SWillem de Bruijn return false; 485c247f053SWillem de Bruijn 486c247f053SWillem de Bruijn /* Support IP_PKTINFO on tstamp packets if requested, to correlate 4871862d620SWillem de Bruijn * timestamp with egress dev. Not possible for packets without iif 488c247f053SWillem de Bruijn * or without payload (SOF_TIMESTAMPING_OPT_TSONLY). 489c247f053SWillem de Bruijn */ 4901862d620SWillem de Bruijn info = PKTINFO_SKB_CB(skb); 4911862d620SWillem de Bruijn if (!(sk->sk_tsflags & SOF_TIMESTAMPING_OPT_CMSG) || 4921862d620SWillem de Bruijn !info->ipi_ifindex) 493829ae9d6SWillem de Bruijn return false; 494829ae9d6SWillem de Bruijn 495829ae9d6SWillem de Bruijn info->ipi_spec_dst.s_addr = ip_hdr(skb)->saddr; 496829ae9d6SWillem de Bruijn return true; 497829ae9d6SWillem de Bruijn } 498829ae9d6SWillem de Bruijn 4991da177e4SLinus Torvalds /* 5001da177e4SLinus Torvalds * Handle MSG_ERRQUEUE 5011da177e4SLinus Torvalds */ 50285fbaa75SHannes Frederic Sowa int ip_recv_error(struct sock *sk, struct msghdr *msg, int len, int *addr_len) 5031da177e4SLinus Torvalds { 5041da177e4SLinus Torvalds struct sock_exterr_skb *serr; 505364a9e93SWillem de Bruijn struct sk_buff *skb; 506342dfc30SSteffen Hurrle DECLARE_SOCKADDR(struct sockaddr_in *, sin, msg->msg_name); 5071da177e4SLinus Torvalds struct { 5081da177e4SLinus Torvalds struct sock_extended_err ee; 5091da177e4SLinus Torvalds struct sockaddr_in offender; 5101da177e4SLinus Torvalds } errhdr; 5111da177e4SLinus Torvalds int err; 5121da177e4SLinus Torvalds int copied; 5131da177e4SLinus Torvalds 5147ce875e5SWillem de Bruijn WARN_ON_ONCE(sk->sk_family == AF_INET6); 5157ce875e5SWillem de Bruijn 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 5671da177e4SLinus Torvalds 5681da177e4SLinus Torvalds /* 5694d52cfbeSEric Dumazet * Socket option code for IP. This is the end of the line after any 5704d52cfbeSEric Dumazet * TCP,UDP etc options on an IP socket. 5711da177e4SLinus Torvalds */ 572baf606d9SMarcelo Ricardo Leitner static bool setsockopt_needs_rtnl(int optname) 573baf606d9SMarcelo Ricardo Leitner { 574baf606d9SMarcelo Ricardo Leitner switch (optname) { 575baf606d9SMarcelo Ricardo Leitner case IP_ADD_MEMBERSHIP: 576baf606d9SMarcelo Ricardo Leitner case IP_ADD_SOURCE_MEMBERSHIP: 57754ff9ef3SMarcelo Ricardo Leitner case IP_BLOCK_SOURCE: 578baf606d9SMarcelo Ricardo Leitner case IP_DROP_MEMBERSHIP: 57954ff9ef3SMarcelo Ricardo Leitner case IP_DROP_SOURCE_MEMBERSHIP: 58054ff9ef3SMarcelo Ricardo Leitner case IP_MSFILTER: 58154ff9ef3SMarcelo Ricardo Leitner case IP_UNBLOCK_SOURCE: 58254ff9ef3SMarcelo Ricardo Leitner case MCAST_BLOCK_SOURCE: 58354ff9ef3SMarcelo Ricardo Leitner case MCAST_MSFILTER: 584baf606d9SMarcelo Ricardo Leitner case MCAST_JOIN_GROUP: 58554ff9ef3SMarcelo Ricardo Leitner case MCAST_JOIN_SOURCE_GROUP: 586baf606d9SMarcelo Ricardo Leitner case MCAST_LEAVE_GROUP: 58754ff9ef3SMarcelo Ricardo Leitner case MCAST_LEAVE_SOURCE_GROUP: 58854ff9ef3SMarcelo Ricardo Leitner case MCAST_UNBLOCK_SOURCE: 5891215e51eSWANG Cong case IP_ROUTER_ALERT: 590baf606d9SMarcelo Ricardo Leitner return true; 591baf606d9SMarcelo Ricardo Leitner } 592baf606d9SMarcelo Ricardo Leitner return false; 593baf606d9SMarcelo Ricardo Leitner } 5941da177e4SLinus Torvalds 5953fdadf7dSDmitry Mishin static int do_ip_setsockopt(struct sock *sk, int level, 596b7058842SDavid S. Miller int optname, char __user *optval, unsigned int optlen) 5971da177e4SLinus Torvalds { 5981da177e4SLinus Torvalds struct inet_sock *inet = inet_sk(sk); 599166b6b2dSNikolay Borisov struct net *net = sock_net(sk); 6001da177e4SLinus Torvalds int val = 0, err; 601baf606d9SMarcelo Ricardo Leitner bool needs_rtnl = setsockopt_needs_rtnl(optname); 6021da177e4SLinus Torvalds 6030c9f79beSXi Wang switch (optname) { 6040c9f79beSXi Wang case IP_PKTINFO: 6050c9f79beSXi Wang case IP_RECVTTL: 6060c9f79beSXi Wang case IP_RECVOPTS: 6070c9f79beSXi Wang case IP_RECVTOS: 6080c9f79beSXi Wang case IP_RETOPTS: 6090c9f79beSXi Wang case IP_TOS: 6100c9f79beSXi Wang case IP_TTL: 6110c9f79beSXi Wang case IP_HDRINCL: 6120c9f79beSXi Wang case IP_MTU_DISCOVER: 6130c9f79beSXi Wang case IP_RECVERR: 6140c9f79beSXi Wang case IP_ROUTER_ALERT: 6150c9f79beSXi Wang case IP_FREEBIND: 6160c9f79beSXi Wang case IP_PASSSEC: 6170c9f79beSXi Wang case IP_TRANSPARENT: 6180c9f79beSXi Wang case IP_MINTTL: 6190c9f79beSXi Wang case IP_NODEFRAG: 62090c337daSEric Dumazet case IP_BIND_ADDRESS_NO_PORT: 6210c9f79beSXi Wang case IP_UNICAST_IF: 6220c9f79beSXi Wang case IP_MULTICAST_TTL: 6230c9f79beSXi Wang case IP_MULTICAST_ALL: 6240c9f79beSXi Wang case IP_MULTICAST_LOOP: 6250c9f79beSXi Wang case IP_RECVORIGDSTADDR: 626ad6f939aSTom Herbert case IP_CHECKSUM: 62770ecc248SWillem de Bruijn case IP_RECVFRAGSIZE: 6281da177e4SLinus Torvalds if (optlen >= sizeof(int)) { 6291da177e4SLinus Torvalds if (get_user(val, (int __user *) optval)) 6301da177e4SLinus Torvalds return -EFAULT; 6311da177e4SLinus Torvalds } else if (optlen >= sizeof(char)) { 6321da177e4SLinus Torvalds unsigned char ucval; 6331da177e4SLinus Torvalds 6341da177e4SLinus Torvalds if (get_user(ucval, (unsigned char __user *) optval)) 6351da177e4SLinus Torvalds return -EFAULT; 6361da177e4SLinus Torvalds val = (int) ucval; 6371da177e4SLinus Torvalds } 6381da177e4SLinus Torvalds } 6391da177e4SLinus Torvalds 6401da177e4SLinus Torvalds /* If optlen==0, it is equivalent to val == 0 */ 6411da177e4SLinus Torvalds 6426a9fb947SPavel Emelyanov if (ip_mroute_opt(optname)) 6431da177e4SLinus Torvalds return ip_mroute_setsockopt(sk, optname, optval, optlen); 6441da177e4SLinus Torvalds 6451da177e4SLinus Torvalds err = 0; 646baf606d9SMarcelo Ricardo Leitner if (needs_rtnl) 647baf606d9SMarcelo Ricardo Leitner rtnl_lock(); 6481da177e4SLinus Torvalds lock_sock(sk); 6491da177e4SLinus Torvalds 6501da177e4SLinus Torvalds switch (optname) { 6511da177e4SLinus Torvalds case IP_OPTIONS: 6521da177e4SLinus Torvalds { 653f6d8bd05SEric Dumazet struct ip_options_rcu *old, *opt = NULL; 654f6d8bd05SEric Dumazet 65565a1c4ffSroel kluin if (optlen > 40) 6561da177e4SLinus Torvalds goto e_inval; 6573b1e0a65SYOSHIFUJI Hideaki err = ip_options_get_from_user(sock_net(sk), &opt, 658cb84663eSDenis V. Lunev optval, optlen); 6591da177e4SLinus Torvalds if (err) 6601da177e4SLinus Torvalds break; 661f6d8bd05SEric Dumazet old = rcu_dereference_protected(inet->inet_opt, 6621e1d04e6SHannes Frederic Sowa lockdep_sock_is_held(sk)); 663d83d8461SArnaldo Carvalho de Melo if (inet->is_icsk) { 664d83d8461SArnaldo Carvalho de Melo struct inet_connection_sock *icsk = inet_csk(sk); 665dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6) 6661da177e4SLinus Torvalds if (sk->sk_family == PF_INET || 6671da177e4SLinus Torvalds (!((1 << sk->sk_state) & 6681da177e4SLinus Torvalds (TCPF_LISTEN | TCPF_CLOSE)) && 669c720c7e8SEric Dumazet inet->inet_daddr != LOOPBACK4_IPV6)) { 6701da177e4SLinus Torvalds #endif 671f6d8bd05SEric Dumazet if (old) 672f6d8bd05SEric Dumazet icsk->icsk_ext_hdr_len -= old->opt.optlen; 6731da177e4SLinus Torvalds if (opt) 674f6d8bd05SEric Dumazet icsk->icsk_ext_hdr_len += opt->opt.optlen; 675d83d8461SArnaldo Carvalho de Melo icsk->icsk_sync_mss(sk, icsk->icsk_pmtu_cookie); 676dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6) 6771da177e4SLinus Torvalds } 6781da177e4SLinus Torvalds #endif 6791da177e4SLinus Torvalds } 680f6d8bd05SEric Dumazet rcu_assign_pointer(inet->inet_opt, opt); 681f6d8bd05SEric Dumazet if (old) 682605b4afeSPaul E. McKenney kfree_rcu(old, rcu); 6831da177e4SLinus Torvalds break; 6841da177e4SLinus Torvalds } 6851da177e4SLinus Torvalds case IP_PKTINFO: 6861da177e4SLinus Torvalds if (val) 6871da177e4SLinus Torvalds inet->cmsg_flags |= IP_CMSG_PKTINFO; 6881da177e4SLinus Torvalds else 6891da177e4SLinus Torvalds inet->cmsg_flags &= ~IP_CMSG_PKTINFO; 6901da177e4SLinus Torvalds break; 6911da177e4SLinus Torvalds case IP_RECVTTL: 6921da177e4SLinus Torvalds if (val) 6931da177e4SLinus Torvalds inet->cmsg_flags |= IP_CMSG_TTL; 6941da177e4SLinus Torvalds else 6951da177e4SLinus Torvalds inet->cmsg_flags &= ~IP_CMSG_TTL; 6961da177e4SLinus Torvalds break; 6971da177e4SLinus Torvalds case IP_RECVTOS: 6981da177e4SLinus Torvalds if (val) 6991da177e4SLinus Torvalds inet->cmsg_flags |= IP_CMSG_TOS; 7001da177e4SLinus Torvalds else 7011da177e4SLinus Torvalds inet->cmsg_flags &= ~IP_CMSG_TOS; 7021da177e4SLinus Torvalds break; 7031da177e4SLinus Torvalds case IP_RECVOPTS: 7041da177e4SLinus Torvalds if (val) 7051da177e4SLinus Torvalds inet->cmsg_flags |= IP_CMSG_RECVOPTS; 7061da177e4SLinus Torvalds else 7071da177e4SLinus Torvalds inet->cmsg_flags &= ~IP_CMSG_RECVOPTS; 7081da177e4SLinus Torvalds break; 7091da177e4SLinus Torvalds case IP_RETOPTS: 7101da177e4SLinus Torvalds if (val) 7111da177e4SLinus Torvalds inet->cmsg_flags |= IP_CMSG_RETOPTS; 7121da177e4SLinus Torvalds else 7131da177e4SLinus Torvalds inet->cmsg_flags &= ~IP_CMSG_RETOPTS; 7141da177e4SLinus Torvalds break; 7152c7946a7SCatherine Zhang case IP_PASSSEC: 7162c7946a7SCatherine Zhang if (val) 7172c7946a7SCatherine Zhang inet->cmsg_flags |= IP_CMSG_PASSSEC; 7182c7946a7SCatherine Zhang else 7192c7946a7SCatherine Zhang inet->cmsg_flags &= ~IP_CMSG_PASSSEC; 7202c7946a7SCatherine Zhang break; 721e8b2dfe9SBalazs Scheidler case IP_RECVORIGDSTADDR: 722e8b2dfe9SBalazs Scheidler if (val) 723e8b2dfe9SBalazs Scheidler inet->cmsg_flags |= IP_CMSG_ORIGDSTADDR; 724e8b2dfe9SBalazs Scheidler else 725e8b2dfe9SBalazs Scheidler inet->cmsg_flags &= ~IP_CMSG_ORIGDSTADDR; 726e8b2dfe9SBalazs Scheidler break; 727ad6f939aSTom Herbert case IP_CHECKSUM: 728ad6f939aSTom Herbert if (val) { 729ad6f939aSTom Herbert if (!(inet->cmsg_flags & IP_CMSG_CHECKSUM)) { 730ad6f939aSTom Herbert inet_inc_convert_csum(sk); 731ad6f939aSTom Herbert inet->cmsg_flags |= IP_CMSG_CHECKSUM; 732ad6f939aSTom Herbert } 733ad6f939aSTom Herbert } else { 734ad6f939aSTom Herbert if (inet->cmsg_flags & IP_CMSG_CHECKSUM) { 735ad6f939aSTom Herbert inet_dec_convert_csum(sk); 736ad6f939aSTom Herbert inet->cmsg_flags &= ~IP_CMSG_CHECKSUM; 737ad6f939aSTom Herbert } 738ad6f939aSTom Herbert } 739ad6f939aSTom Herbert break; 74070ecc248SWillem de Bruijn case IP_RECVFRAGSIZE: 74170ecc248SWillem de Bruijn if (sk->sk_type != SOCK_RAW && sk->sk_type != SOCK_DGRAM) 74270ecc248SWillem de Bruijn goto e_inval; 74370ecc248SWillem de Bruijn if (val) 74470ecc248SWillem de Bruijn inet->cmsg_flags |= IP_CMSG_RECVFRAGSIZE; 74570ecc248SWillem de Bruijn else 74670ecc248SWillem de Bruijn inet->cmsg_flags &= ~IP_CMSG_RECVFRAGSIZE; 74770ecc248SWillem de Bruijn break; 7481da177e4SLinus Torvalds case IP_TOS: /* This sets both TOS and Precedence */ 7491da177e4SLinus Torvalds if (sk->sk_type == SOCK_STREAM) { 7502c67e9acSMaciej Żenczykowski val &= ~INET_ECN_MASK; 7512c67e9acSMaciej Żenczykowski val |= inet->tos & INET_ECN_MASK; 7521da177e4SLinus Torvalds } 7531da177e4SLinus Torvalds if (inet->tos != val) { 7541da177e4SLinus Torvalds inet->tos = val; 7551da177e4SLinus Torvalds sk->sk_priority = rt_tos2priority(val); 7561da177e4SLinus Torvalds sk_dst_reset(sk); 7571da177e4SLinus Torvalds } 7581da177e4SLinus Torvalds break; 7591da177e4SLinus Torvalds case IP_TTL: 7601da177e4SLinus Torvalds if (optlen < 1) 7611da177e4SLinus Torvalds goto e_inval; 762c9be4a5cSCong Wang if (val != -1 && (val < 1 || val > 255)) 7631da177e4SLinus Torvalds goto e_inval; 7641da177e4SLinus Torvalds inet->uc_ttl = val; 7651da177e4SLinus Torvalds break; 7661da177e4SLinus Torvalds case IP_HDRINCL: 7671da177e4SLinus Torvalds if (sk->sk_type != SOCK_RAW) { 7681da177e4SLinus Torvalds err = -ENOPROTOOPT; 7691da177e4SLinus Torvalds break; 7701da177e4SLinus Torvalds } 7711da177e4SLinus Torvalds inet->hdrincl = val ? 1 : 0; 7721da177e4SLinus Torvalds break; 7737b2ff18eSJiri Olsa case IP_NODEFRAG: 7747b2ff18eSJiri Olsa if (sk->sk_type != SOCK_RAW) { 7757b2ff18eSJiri Olsa err = -ENOPROTOOPT; 7767b2ff18eSJiri Olsa break; 7777b2ff18eSJiri Olsa } 7787b2ff18eSJiri Olsa inet->nodefrag = val ? 1 : 0; 7797b2ff18eSJiri Olsa break; 78090c337daSEric Dumazet case IP_BIND_ADDRESS_NO_PORT: 78190c337daSEric Dumazet inet->bind_address_no_port = val ? 1 : 0; 78290c337daSEric Dumazet break; 7831da177e4SLinus Torvalds case IP_MTU_DISCOVER: 7841b346576SHannes Frederic Sowa if (val < IP_PMTUDISC_DONT || val > IP_PMTUDISC_OMIT) 7851da177e4SLinus Torvalds goto e_inval; 7861da177e4SLinus Torvalds inet->pmtudisc = val; 7871da177e4SLinus Torvalds break; 7881da177e4SLinus Torvalds case IP_RECVERR: 7891da177e4SLinus Torvalds inet->recverr = !!val; 7901da177e4SLinus Torvalds if (!val) 7911da177e4SLinus Torvalds skb_queue_purge(&sk->sk_error_queue); 7921da177e4SLinus Torvalds break; 7931da177e4SLinus Torvalds case IP_MULTICAST_TTL: 7941da177e4SLinus Torvalds if (sk->sk_type == SOCK_STREAM) 7951da177e4SLinus Torvalds goto e_inval; 7961da177e4SLinus Torvalds if (optlen < 1) 7971da177e4SLinus Torvalds goto e_inval; 7981da177e4SLinus Torvalds if (val == -1) 7991da177e4SLinus Torvalds val = 1; 8001da177e4SLinus Torvalds if (val < 0 || val > 255) 8011da177e4SLinus Torvalds goto e_inval; 8021da177e4SLinus Torvalds inet->mc_ttl = val; 8031da177e4SLinus Torvalds break; 8041da177e4SLinus Torvalds case IP_MULTICAST_LOOP: 8051da177e4SLinus Torvalds if (optlen < 1) 8061da177e4SLinus Torvalds goto e_inval; 8071da177e4SLinus Torvalds inet->mc_loop = !!val; 8081da177e4SLinus Torvalds break; 80976e21053SErich E. Hoover case IP_UNICAST_IF: 81076e21053SErich E. Hoover { 81176e21053SErich E. Hoover struct net_device *dev = NULL; 81276e21053SErich E. Hoover int ifindex; 8139515a2e0SDavid Ahern int midx; 81476e21053SErich E. Hoover 81576e21053SErich E. Hoover if (optlen != sizeof(int)) 81676e21053SErich E. Hoover goto e_inval; 81776e21053SErich E. Hoover 81876e21053SErich E. Hoover ifindex = (__force int)ntohl((__force __be32)val); 81976e21053SErich E. Hoover if (ifindex == 0) { 82076e21053SErich E. Hoover inet->uc_index = 0; 82176e21053SErich E. Hoover err = 0; 82276e21053SErich E. Hoover break; 82376e21053SErich E. Hoover } 82476e21053SErich E. Hoover 82576e21053SErich E. Hoover dev = dev_get_by_index(sock_net(sk), ifindex); 82676e21053SErich E. Hoover err = -EADDRNOTAVAIL; 82776e21053SErich E. Hoover if (!dev) 82876e21053SErich E. Hoover break; 8299515a2e0SDavid Ahern 8309515a2e0SDavid Ahern midx = l3mdev_master_ifindex(dev); 83176e21053SErich E. Hoover dev_put(dev); 83276e21053SErich E. Hoover 83376e21053SErich E. Hoover err = -EINVAL; 8349515a2e0SDavid Ahern if (sk->sk_bound_dev_if && 8359515a2e0SDavid Ahern (!midx || midx != sk->sk_bound_dev_if)) 83676e21053SErich E. Hoover break; 83776e21053SErich E. Hoover 83876e21053SErich E. Hoover inet->uc_index = ifindex; 83976e21053SErich E. Hoover err = 0; 84076e21053SErich E. Hoover break; 84176e21053SErich E. Hoover } 8421da177e4SLinus Torvalds case IP_MULTICAST_IF: 8431da177e4SLinus Torvalds { 8441da177e4SLinus Torvalds struct ip_mreqn mreq; 8451da177e4SLinus Torvalds struct net_device *dev = NULL; 8467bb387c5SDavid Ahern int midx; 8471da177e4SLinus Torvalds 8481da177e4SLinus Torvalds if (sk->sk_type == SOCK_STREAM) 8491da177e4SLinus Torvalds goto e_inval; 8501da177e4SLinus Torvalds /* 8511da177e4SLinus Torvalds * Check the arguments are allowable 8521da177e4SLinus Torvalds */ 8531da177e4SLinus Torvalds 8540915921bSShan Wei if (optlen < sizeof(struct in_addr)) 8550915921bSShan Wei goto e_inval; 8560915921bSShan Wei 8571da177e4SLinus Torvalds err = -EFAULT; 8581da177e4SLinus Torvalds if (optlen >= sizeof(struct ip_mreqn)) { 8591da177e4SLinus Torvalds if (copy_from_user(&mreq, optval, sizeof(mreq))) 8601da177e4SLinus Torvalds break; 8611da177e4SLinus Torvalds } else { 8621da177e4SLinus Torvalds memset(&mreq, 0, sizeof(mreq)); 8633a084ddbSJiri Pirko if (optlen >= sizeof(struct ip_mreq)) { 8643a084ddbSJiri Pirko if (copy_from_user(&mreq, optval, 8653a084ddbSJiri Pirko sizeof(struct ip_mreq))) 8663a084ddbSJiri Pirko break; 8673a084ddbSJiri Pirko } else if (optlen >= sizeof(struct in_addr)) { 8683a084ddbSJiri Pirko if (copy_from_user(&mreq.imr_address, optval, 8694d52cfbeSEric Dumazet sizeof(struct in_addr))) 8701da177e4SLinus Torvalds break; 8711da177e4SLinus Torvalds } 8723a084ddbSJiri Pirko } 8731da177e4SLinus Torvalds 8741da177e4SLinus Torvalds if (!mreq.imr_ifindex) { 875e6f1cebfSAl Viro if (mreq.imr_address.s_addr == htonl(INADDR_ANY)) { 8761da177e4SLinus Torvalds inet->mc_index = 0; 8771da177e4SLinus Torvalds inet->mc_addr = 0; 8781da177e4SLinus Torvalds err = 0; 8791da177e4SLinus Torvalds break; 8801da177e4SLinus Torvalds } 8813b1e0a65SYOSHIFUJI Hideaki dev = ip_dev_find(sock_net(sk), mreq.imr_address.s_addr); 88255b80503SEric Dumazet if (dev) 8831da177e4SLinus Torvalds mreq.imr_ifindex = dev->ifindex; 8841da177e4SLinus Torvalds } else 88555b80503SEric Dumazet dev = dev_get_by_index(sock_net(sk), mreq.imr_ifindex); 8861da177e4SLinus Torvalds 8871da177e4SLinus Torvalds 8881da177e4SLinus Torvalds err = -EADDRNOTAVAIL; 8891da177e4SLinus Torvalds if (!dev) 8901da177e4SLinus Torvalds break; 8917bb387c5SDavid Ahern 8927bb387c5SDavid Ahern midx = l3mdev_master_ifindex(dev); 8937bb387c5SDavid Ahern 89455b80503SEric Dumazet dev_put(dev); 8951da177e4SLinus Torvalds 8961da177e4SLinus Torvalds err = -EINVAL; 8971da177e4SLinus Torvalds if (sk->sk_bound_dev_if && 8987bb387c5SDavid Ahern mreq.imr_ifindex != sk->sk_bound_dev_if && 8997bb387c5SDavid Ahern (!midx || midx != sk->sk_bound_dev_if)) 9001da177e4SLinus Torvalds break; 9011da177e4SLinus Torvalds 9021da177e4SLinus Torvalds inet->mc_index = mreq.imr_ifindex; 9031da177e4SLinus Torvalds inet->mc_addr = mreq.imr_address.s_addr; 9041da177e4SLinus Torvalds err = 0; 9051da177e4SLinus Torvalds break; 9061da177e4SLinus Torvalds } 9071da177e4SLinus Torvalds 9081da177e4SLinus Torvalds case IP_ADD_MEMBERSHIP: 9091da177e4SLinus Torvalds case IP_DROP_MEMBERSHIP: 9101da177e4SLinus Torvalds { 9111da177e4SLinus Torvalds struct ip_mreqn mreq; 9121da177e4SLinus Torvalds 913a96fb49bSFlavio Leitner err = -EPROTO; 914a96fb49bSFlavio Leitner if (inet_sk(sk)->is_icsk) 915a96fb49bSFlavio Leitner break; 916a96fb49bSFlavio Leitner 9171da177e4SLinus Torvalds if (optlen < sizeof(struct ip_mreq)) 9181da177e4SLinus Torvalds goto e_inval; 9191da177e4SLinus Torvalds err = -EFAULT; 9201da177e4SLinus Torvalds if (optlen >= sizeof(struct ip_mreqn)) { 9211da177e4SLinus Torvalds if (copy_from_user(&mreq, optval, sizeof(mreq))) 9221da177e4SLinus Torvalds break; 9231da177e4SLinus Torvalds } else { 9241da177e4SLinus Torvalds memset(&mreq, 0, sizeof(mreq)); 9251da177e4SLinus Torvalds if (copy_from_user(&mreq, optval, sizeof(struct ip_mreq))) 9261da177e4SLinus Torvalds break; 9271da177e4SLinus Torvalds } 9281da177e4SLinus Torvalds 9291da177e4SLinus Torvalds if (optname == IP_ADD_MEMBERSHIP) 93054ff9ef3SMarcelo Ricardo Leitner err = ip_mc_join_group(sk, &mreq); 9311da177e4SLinus Torvalds else 93254ff9ef3SMarcelo Ricardo Leitner err = ip_mc_leave_group(sk, &mreq); 9331da177e4SLinus Torvalds break; 9341da177e4SLinus Torvalds } 9351da177e4SLinus Torvalds case IP_MSFILTER: 9361da177e4SLinus Torvalds { 9371da177e4SLinus Torvalds struct ip_msfilter *msf; 9381da177e4SLinus Torvalds 9391da177e4SLinus Torvalds if (optlen < IP_MSFILTER_SIZE(0)) 9401da177e4SLinus Torvalds goto e_inval; 9411da177e4SLinus Torvalds if (optlen > sysctl_optmem_max) { 9421da177e4SLinus Torvalds err = -ENOBUFS; 9431da177e4SLinus Torvalds break; 9441da177e4SLinus Torvalds } 945a2c841d9SAl Viro msf = memdup_user(optval, optlen); 946a2c841d9SAl Viro if (IS_ERR(msf)) { 947a2c841d9SAl Viro err = PTR_ERR(msf); 9481da177e4SLinus Torvalds break; 9491da177e4SLinus Torvalds } 9501da177e4SLinus Torvalds /* numsrc >= (1G-4) overflow in 32 bits */ 9511da177e4SLinus Torvalds if (msf->imsf_numsrc >= 0x3ffffffcU || 952166b6b2dSNikolay Borisov msf->imsf_numsrc > net->ipv4.sysctl_igmp_max_msf) { 9531da177e4SLinus Torvalds kfree(msf); 9541da177e4SLinus Torvalds err = -ENOBUFS; 9551da177e4SLinus Torvalds break; 9561da177e4SLinus Torvalds } 9571da177e4SLinus Torvalds if (IP_MSFILTER_SIZE(msf->imsf_numsrc) > optlen) { 9581da177e4SLinus Torvalds kfree(msf); 9591da177e4SLinus Torvalds err = -EINVAL; 9601da177e4SLinus Torvalds break; 9611da177e4SLinus Torvalds } 9621da177e4SLinus Torvalds err = ip_mc_msfilter(sk, msf, 0); 9631da177e4SLinus Torvalds kfree(msf); 9641da177e4SLinus Torvalds break; 9651da177e4SLinus Torvalds } 9661da177e4SLinus Torvalds case IP_BLOCK_SOURCE: 9671da177e4SLinus Torvalds case IP_UNBLOCK_SOURCE: 9681da177e4SLinus Torvalds case IP_ADD_SOURCE_MEMBERSHIP: 9691da177e4SLinus Torvalds case IP_DROP_SOURCE_MEMBERSHIP: 9701da177e4SLinus Torvalds { 9711da177e4SLinus Torvalds struct ip_mreq_source mreqs; 9721da177e4SLinus Torvalds int omode, add; 9731da177e4SLinus Torvalds 9741da177e4SLinus Torvalds if (optlen != sizeof(struct ip_mreq_source)) 9751da177e4SLinus Torvalds goto e_inval; 9761da177e4SLinus Torvalds if (copy_from_user(&mreqs, optval, sizeof(mreqs))) { 9771da177e4SLinus Torvalds err = -EFAULT; 9781da177e4SLinus Torvalds break; 9791da177e4SLinus Torvalds } 9801da177e4SLinus Torvalds if (optname == IP_BLOCK_SOURCE) { 9811da177e4SLinus Torvalds omode = MCAST_EXCLUDE; 9821da177e4SLinus Torvalds add = 1; 9831da177e4SLinus Torvalds } else if (optname == IP_UNBLOCK_SOURCE) { 9841da177e4SLinus Torvalds omode = MCAST_EXCLUDE; 9851da177e4SLinus Torvalds add = 0; 9861da177e4SLinus Torvalds } else if (optname == IP_ADD_SOURCE_MEMBERSHIP) { 9871da177e4SLinus Torvalds struct ip_mreqn mreq; 9881da177e4SLinus Torvalds 9891da177e4SLinus Torvalds mreq.imr_multiaddr.s_addr = mreqs.imr_multiaddr; 9901da177e4SLinus Torvalds mreq.imr_address.s_addr = mreqs.imr_interface; 9911da177e4SLinus Torvalds mreq.imr_ifindex = 0; 99254ff9ef3SMarcelo Ricardo Leitner err = ip_mc_join_group(sk, &mreq); 9938cdaaa15SDavid L Stevens if (err && err != -EADDRINUSE) 9941da177e4SLinus Torvalds break; 9951da177e4SLinus Torvalds omode = MCAST_INCLUDE; 9961da177e4SLinus Torvalds add = 1; 9971da177e4SLinus Torvalds } else /* IP_DROP_SOURCE_MEMBERSHIP */ { 9981da177e4SLinus Torvalds omode = MCAST_INCLUDE; 9991da177e4SLinus Torvalds add = 0; 10001da177e4SLinus Torvalds } 10011da177e4SLinus Torvalds err = ip_mc_source(add, omode, sk, &mreqs, 0); 10021da177e4SLinus Torvalds break; 10031da177e4SLinus Torvalds } 10041da177e4SLinus Torvalds case MCAST_JOIN_GROUP: 10051da177e4SLinus Torvalds case MCAST_LEAVE_GROUP: 10061da177e4SLinus Torvalds { 10071da177e4SLinus Torvalds struct group_req greq; 10081da177e4SLinus Torvalds struct sockaddr_in *psin; 10091da177e4SLinus Torvalds struct ip_mreqn mreq; 10101da177e4SLinus Torvalds 10111da177e4SLinus Torvalds if (optlen < sizeof(struct group_req)) 10121da177e4SLinus Torvalds goto e_inval; 10131da177e4SLinus Torvalds err = -EFAULT; 10141da177e4SLinus Torvalds if (copy_from_user(&greq, optval, sizeof(greq))) 10151da177e4SLinus Torvalds break; 10161da177e4SLinus Torvalds psin = (struct sockaddr_in *)&greq.gr_group; 10171da177e4SLinus Torvalds if (psin->sin_family != AF_INET) 10181da177e4SLinus Torvalds goto e_inval; 10191da177e4SLinus Torvalds memset(&mreq, 0, sizeof(mreq)); 10201da177e4SLinus Torvalds mreq.imr_multiaddr = psin->sin_addr; 10211da177e4SLinus Torvalds mreq.imr_ifindex = greq.gr_interface; 10221da177e4SLinus Torvalds 10231da177e4SLinus Torvalds if (optname == MCAST_JOIN_GROUP) 102454ff9ef3SMarcelo Ricardo Leitner err = ip_mc_join_group(sk, &mreq); 10251da177e4SLinus Torvalds else 102654ff9ef3SMarcelo Ricardo Leitner err = ip_mc_leave_group(sk, &mreq); 10271da177e4SLinus Torvalds break; 10281da177e4SLinus Torvalds } 10291da177e4SLinus Torvalds case MCAST_JOIN_SOURCE_GROUP: 10301da177e4SLinus Torvalds case MCAST_LEAVE_SOURCE_GROUP: 10311da177e4SLinus Torvalds case MCAST_BLOCK_SOURCE: 10321da177e4SLinus Torvalds case MCAST_UNBLOCK_SOURCE: 10331da177e4SLinus Torvalds { 10341da177e4SLinus Torvalds struct group_source_req greqs; 10351da177e4SLinus Torvalds struct ip_mreq_source mreqs; 10361da177e4SLinus Torvalds struct sockaddr_in *psin; 10371da177e4SLinus Torvalds int omode, add; 10381da177e4SLinus Torvalds 10391da177e4SLinus Torvalds if (optlen != sizeof(struct group_source_req)) 10401da177e4SLinus Torvalds goto e_inval; 10411da177e4SLinus Torvalds if (copy_from_user(&greqs, optval, sizeof(greqs))) { 10421da177e4SLinus Torvalds err = -EFAULT; 10431da177e4SLinus Torvalds break; 10441da177e4SLinus Torvalds } 10451da177e4SLinus Torvalds if (greqs.gsr_group.ss_family != AF_INET || 10461da177e4SLinus Torvalds greqs.gsr_source.ss_family != AF_INET) { 10471da177e4SLinus Torvalds err = -EADDRNOTAVAIL; 10481da177e4SLinus Torvalds break; 10491da177e4SLinus Torvalds } 10501da177e4SLinus Torvalds psin = (struct sockaddr_in *)&greqs.gsr_group; 10511da177e4SLinus Torvalds mreqs.imr_multiaddr = psin->sin_addr.s_addr; 10521da177e4SLinus Torvalds psin = (struct sockaddr_in *)&greqs.gsr_source; 10531da177e4SLinus Torvalds mreqs.imr_sourceaddr = psin->sin_addr.s_addr; 10541da177e4SLinus Torvalds mreqs.imr_interface = 0; /* use index for mc_source */ 10551da177e4SLinus Torvalds 10561da177e4SLinus Torvalds if (optname == MCAST_BLOCK_SOURCE) { 10571da177e4SLinus Torvalds omode = MCAST_EXCLUDE; 10581da177e4SLinus Torvalds add = 1; 10591da177e4SLinus Torvalds } else if (optname == MCAST_UNBLOCK_SOURCE) { 10601da177e4SLinus Torvalds omode = MCAST_EXCLUDE; 10611da177e4SLinus Torvalds add = 0; 10621da177e4SLinus Torvalds } else if (optname == MCAST_JOIN_SOURCE_GROUP) { 10631da177e4SLinus Torvalds struct ip_mreqn mreq; 10641da177e4SLinus Torvalds 10651da177e4SLinus Torvalds psin = (struct sockaddr_in *)&greqs.gsr_group; 10661da177e4SLinus Torvalds mreq.imr_multiaddr = psin->sin_addr; 10671da177e4SLinus Torvalds mreq.imr_address.s_addr = 0; 10681da177e4SLinus Torvalds mreq.imr_ifindex = greqs.gsr_interface; 106954ff9ef3SMarcelo Ricardo Leitner err = ip_mc_join_group(sk, &mreq); 10708cdaaa15SDavid L Stevens if (err && err != -EADDRINUSE) 10711da177e4SLinus Torvalds break; 10721da177e4SLinus Torvalds greqs.gsr_interface = mreq.imr_ifindex; 10731da177e4SLinus Torvalds omode = MCAST_INCLUDE; 10741da177e4SLinus Torvalds add = 1; 10751da177e4SLinus Torvalds } else /* MCAST_LEAVE_SOURCE_GROUP */ { 10761da177e4SLinus Torvalds omode = MCAST_INCLUDE; 10771da177e4SLinus Torvalds add = 0; 10781da177e4SLinus Torvalds } 10791da177e4SLinus Torvalds err = ip_mc_source(add, omode, sk, &mreqs, 10801da177e4SLinus Torvalds greqs.gsr_interface); 10811da177e4SLinus Torvalds break; 10821da177e4SLinus Torvalds } 10831da177e4SLinus Torvalds case MCAST_MSFILTER: 10841da177e4SLinus Torvalds { 10851da177e4SLinus Torvalds struct sockaddr_in *psin; 10861da177e4SLinus Torvalds struct ip_msfilter *msf = NULL; 10871da177e4SLinus Torvalds struct group_filter *gsf = NULL; 10881da177e4SLinus Torvalds int msize, i, ifindex; 10891da177e4SLinus Torvalds 10901da177e4SLinus Torvalds if (optlen < GROUP_FILTER_SIZE(0)) 10911da177e4SLinus Torvalds goto e_inval; 10921da177e4SLinus Torvalds if (optlen > sysctl_optmem_max) { 10931da177e4SLinus Torvalds err = -ENOBUFS; 10941da177e4SLinus Torvalds break; 10951da177e4SLinus Torvalds } 1096a2c841d9SAl Viro gsf = memdup_user(optval, optlen); 1097a2c841d9SAl Viro if (IS_ERR(gsf)) { 1098a2c841d9SAl Viro err = PTR_ERR(gsf); 10991da177e4SLinus Torvalds break; 11001da177e4SLinus Torvalds } 11014d52cfbeSEric Dumazet 11021da177e4SLinus Torvalds /* numsrc >= (4G-140)/128 overflow in 32 bits */ 11031da177e4SLinus Torvalds if (gsf->gf_numsrc >= 0x1ffffff || 1104166b6b2dSNikolay Borisov gsf->gf_numsrc > net->ipv4.sysctl_igmp_max_msf) { 11051da177e4SLinus Torvalds err = -ENOBUFS; 11061da177e4SLinus Torvalds goto mc_msf_out; 11071da177e4SLinus Torvalds } 11081da177e4SLinus Torvalds if (GROUP_FILTER_SIZE(gsf->gf_numsrc) > optlen) { 11091da177e4SLinus Torvalds err = -EINVAL; 11101da177e4SLinus Torvalds goto mc_msf_out; 11111da177e4SLinus Torvalds } 11121da177e4SLinus Torvalds msize = IP_MSFILTER_SIZE(gsf->gf_numsrc); 11138b3a7005SKris Katterjohn msf = kmalloc(msize, GFP_KERNEL); 1114cfcabdccSStephen Hemminger if (!msf) { 11151da177e4SLinus Torvalds err = -ENOBUFS; 11161da177e4SLinus Torvalds goto mc_msf_out; 11171da177e4SLinus Torvalds } 11181da177e4SLinus Torvalds ifindex = gsf->gf_interface; 11191da177e4SLinus Torvalds psin = (struct sockaddr_in *)&gsf->gf_group; 11201da177e4SLinus Torvalds if (psin->sin_family != AF_INET) { 11211da177e4SLinus Torvalds err = -EADDRNOTAVAIL; 11221da177e4SLinus Torvalds goto mc_msf_out; 11231da177e4SLinus Torvalds } 11241da177e4SLinus Torvalds msf->imsf_multiaddr = psin->sin_addr.s_addr; 11251da177e4SLinus Torvalds msf->imsf_interface = 0; 11261da177e4SLinus Torvalds msf->imsf_fmode = gsf->gf_fmode; 11271da177e4SLinus Torvalds msf->imsf_numsrc = gsf->gf_numsrc; 11281da177e4SLinus Torvalds err = -EADDRNOTAVAIL; 11291da177e4SLinus Torvalds for (i = 0; i < gsf->gf_numsrc; ++i) { 11301da177e4SLinus Torvalds psin = (struct sockaddr_in *)&gsf->gf_slist[i]; 11311da177e4SLinus Torvalds 11321da177e4SLinus Torvalds if (psin->sin_family != AF_INET) 11331da177e4SLinus Torvalds goto mc_msf_out; 11341da177e4SLinus Torvalds msf->imsf_slist[i] = psin->sin_addr.s_addr; 11351da177e4SLinus Torvalds } 11361da177e4SLinus Torvalds kfree(gsf); 11371da177e4SLinus Torvalds gsf = NULL; 11381da177e4SLinus Torvalds 11391da177e4SLinus Torvalds err = ip_mc_msfilter(sk, msf, ifindex); 11401da177e4SLinus Torvalds mc_msf_out: 11411da177e4SLinus Torvalds kfree(msf); 11421da177e4SLinus Torvalds kfree(gsf); 11431da177e4SLinus Torvalds break; 11441da177e4SLinus Torvalds } 1145f771bef9SNivedita Singhvi case IP_MULTICAST_ALL: 1146f771bef9SNivedita Singhvi if (optlen < 1) 1147f771bef9SNivedita Singhvi goto e_inval; 1148f771bef9SNivedita Singhvi if (val != 0 && val != 1) 1149f771bef9SNivedita Singhvi goto e_inval; 1150f771bef9SNivedita Singhvi inet->mc_all = val; 1151f771bef9SNivedita Singhvi break; 11521da177e4SLinus Torvalds case IP_ROUTER_ALERT: 11531da177e4SLinus Torvalds err = ip_ra_control(sk, val ? 1 : 0, NULL); 11541da177e4SLinus Torvalds break; 11551da177e4SLinus Torvalds 11561da177e4SLinus Torvalds case IP_FREEBIND: 11571da177e4SLinus Torvalds if (optlen < 1) 11581da177e4SLinus Torvalds goto e_inval; 11591da177e4SLinus Torvalds inet->freebind = !!val; 11601da177e4SLinus Torvalds break; 11611da177e4SLinus Torvalds 11621da177e4SLinus Torvalds case IP_IPSEC_POLICY: 11631da177e4SLinus Torvalds case IP_XFRM_POLICY: 11646fc0b4a7SHerbert Xu err = -EPERM; 116552e804c6SEric W. Biederman if (!ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN)) 11666fc0b4a7SHerbert Xu break; 11671da177e4SLinus Torvalds err = xfrm_user_policy(sk, optname, optval, optlen); 11681da177e4SLinus Torvalds break; 11691da177e4SLinus Torvalds 1170f5715aeaSKOVACS Krisztian case IP_TRANSPARENT: 117152e804c6SEric W. Biederman if (!!val && !ns_capable(sock_net(sk)->user_ns, CAP_NET_RAW) && 117252e804c6SEric W. Biederman !ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN)) { 1173f5715aeaSKOVACS Krisztian err = -EPERM; 1174f5715aeaSKOVACS Krisztian break; 1175f5715aeaSKOVACS Krisztian } 1176f5715aeaSKOVACS Krisztian if (optlen < 1) 1177f5715aeaSKOVACS Krisztian goto e_inval; 1178f5715aeaSKOVACS Krisztian inet->transparent = !!val; 1179f5715aeaSKOVACS Krisztian break; 1180f5715aeaSKOVACS Krisztian 1181d218d111SStephen Hemminger case IP_MINTTL: 1182d218d111SStephen Hemminger if (optlen < 1) 1183d218d111SStephen Hemminger goto e_inval; 1184d218d111SStephen Hemminger if (val < 0 || val > 255) 1185d218d111SStephen Hemminger goto e_inval; 1186d218d111SStephen Hemminger inet->min_ttl = val; 1187d218d111SStephen Hemminger break; 1188d218d111SStephen Hemminger 11891da177e4SLinus Torvalds default: 11901da177e4SLinus Torvalds err = -ENOPROTOOPT; 11911da177e4SLinus Torvalds break; 11921da177e4SLinus Torvalds } 11931da177e4SLinus Torvalds release_sock(sk); 1194baf606d9SMarcelo Ricardo Leitner if (needs_rtnl) 1195baf606d9SMarcelo Ricardo Leitner rtnl_unlock(); 11961da177e4SLinus Torvalds return err; 11971da177e4SLinus Torvalds 11981da177e4SLinus Torvalds e_inval: 11991da177e4SLinus Torvalds release_sock(sk); 1200baf606d9SMarcelo Ricardo Leitner if (needs_rtnl) 1201baf606d9SMarcelo Ricardo Leitner rtnl_unlock(); 12021da177e4SLinus Torvalds return -EINVAL; 12031da177e4SLinus Torvalds } 12041da177e4SLinus Torvalds 1205f84af32cSEric Dumazet /** 1206829ae9d6SWillem de Bruijn * ipv4_pktinfo_prepare - transfer some info from rtable to skb 1207f84af32cSEric Dumazet * @sk: socket 1208f84af32cSEric Dumazet * @skb: buffer 1209f84af32cSEric Dumazet * 121035ebf65eSDavid S. Miller * To support IP_CMSG_PKTINFO option, we store rt_iif and specific 121135ebf65eSDavid S. Miller * destination in skb->cb[] before dst drop. 12128e3bff96Sstephen hemminger * This way, receiver doesn't make cache line misses to read rtable. 1213f84af32cSEric Dumazet */ 1214fbf8866dSShawn Bohrer void ipv4_pktinfo_prepare(const struct sock *sk, struct sk_buff *skb) 1215f84af32cSEric Dumazet { 1216d826eb14SEric Dumazet struct in_pktinfo *pktinfo = PKTINFO_SKB_CB(skb); 12174b261c75SHannes Frederic Sowa bool prepare = (inet_sk(sk)->cmsg_flags & IP_CMSG_PKTINFO) || 12184b261c75SHannes Frederic Sowa ipv6_sk_rxinfo(sk); 1219d826eb14SEric Dumazet 12204b261c75SHannes Frederic Sowa if (prepare && skb_rtable(skb)) { 12210b922b7aSDavid Ahern /* skb->cb is overloaded: prior to this point it is IP{6}CB 12220b922b7aSDavid Ahern * which has interface index (iif) as the first member of the 12230b922b7aSDavid Ahern * underlying inet{6}_skb_parm struct. This code then overlays 12240b922b7aSDavid Ahern * PKTINFO_SKB_CB and in_pktinfo also has iif as the first 1225f0c16ba8SWei Zhang * element so the iif is picked up from the prior IPCB. If iif 1226f0c16ba8SWei Zhang * is the loopback interface, then return the sending interface 1227f0c16ba8SWei Zhang * (e.g., process binds socket to eth0 for Tx which is 1228f0c16ba8SWei Zhang * redirected to loopback in the rtable/dst). 12290b922b7aSDavid Ahern */ 1230cbea8f02SDavid Ahern struct rtable *rt = skb_rtable(skb); 1231cbea8f02SDavid Ahern bool l3slave = ipv4_l3mdev_skb(IPCB(skb)->flags); 1232cbea8f02SDavid Ahern 1233cbea8f02SDavid Ahern if (pktinfo->ipi_ifindex == LOOPBACK_IFINDEX) 1234f0c16ba8SWei Zhang pktinfo->ipi_ifindex = inet_iif(skb); 1235cbea8f02SDavid Ahern else if (l3slave && rt && rt->rt_iif) 1236cbea8f02SDavid Ahern pktinfo->ipi_ifindex = rt->rt_iif; 1237f0c16ba8SWei Zhang 123835ebf65eSDavid S. Miller pktinfo->ipi_spec_dst.s_addr = fib_compute_spec_dst(skb); 1239d826eb14SEric Dumazet } else { 1240d826eb14SEric Dumazet pktinfo->ipi_ifindex = 0; 1241d826eb14SEric Dumazet pktinfo->ipi_spec_dst.s_addr = 0; 1242f84af32cSEric Dumazet } 1243d826eb14SEric Dumazet skb_dst_drop(skb); 1244d826eb14SEric Dumazet } 1245f84af32cSEric Dumazet 12463fdadf7dSDmitry Mishin int ip_setsockopt(struct sock *sk, int level, 1247b7058842SDavid S. Miller int optname, char __user *optval, unsigned int optlen) 12483fdadf7dSDmitry Mishin { 12493fdadf7dSDmitry Mishin int err; 12503fdadf7dSDmitry Mishin 12513fdadf7dSDmitry Mishin if (level != SOL_IP) 12523fdadf7dSDmitry Mishin return -ENOPROTOOPT; 12533fdadf7dSDmitry Mishin 12543fdadf7dSDmitry Mishin err = do_ip_setsockopt(sk, level, optname, optval, optlen); 12553fdadf7dSDmitry Mishin #ifdef CONFIG_NETFILTER 12563fdadf7dSDmitry Mishin /* we need to exclude all possible ENOPROTOOPTs except default case */ 12573fdadf7dSDmitry Mishin if (err == -ENOPROTOOPT && optname != IP_HDRINCL && 12586a9fb947SPavel Emelyanov optname != IP_IPSEC_POLICY && 12596a9fb947SPavel Emelyanov optname != IP_XFRM_POLICY && 12603f34cfaeSPaolo Abeni !ip_mroute_opt(optname)) 12613fdadf7dSDmitry Mishin err = nf_setsockopt(sk, PF_INET, optname, optval, optlen); 12623fdadf7dSDmitry Mishin #endif 12633fdadf7dSDmitry Mishin return err; 12643fdadf7dSDmitry Mishin } 12654d52cfbeSEric Dumazet EXPORT_SYMBOL(ip_setsockopt); 12663fdadf7dSDmitry Mishin 12673fdadf7dSDmitry Mishin #ifdef CONFIG_COMPAT 1268543d9cfeSArnaldo Carvalho de Melo int compat_ip_setsockopt(struct sock *sk, int level, int optname, 1269b7058842SDavid S. Miller char __user *optval, unsigned int optlen) 12703fdadf7dSDmitry Mishin { 12713fdadf7dSDmitry Mishin int err; 12723fdadf7dSDmitry Mishin 12733fdadf7dSDmitry Mishin if (level != SOL_IP) 12743fdadf7dSDmitry Mishin return -ENOPROTOOPT; 12753fdadf7dSDmitry Mishin 1276dae50295SDavid L Stevens if (optname >= MCAST_JOIN_GROUP && optname <= MCAST_MSFILTER) 1277dae50295SDavid L Stevens return compat_mc_setsockopt(sk, level, optname, optval, optlen, 1278dae50295SDavid L Stevens ip_setsockopt); 1279dae50295SDavid L Stevens 12803fdadf7dSDmitry Mishin err = do_ip_setsockopt(sk, level, optname, optval, optlen); 12813fdadf7dSDmitry Mishin #ifdef CONFIG_NETFILTER 12823fdadf7dSDmitry Mishin /* we need to exclude all possible ENOPROTOOPTs except default case */ 12833fdadf7dSDmitry Mishin if (err == -ENOPROTOOPT && optname != IP_HDRINCL && 12846a9fb947SPavel Emelyanov optname != IP_IPSEC_POLICY && 12856a9fb947SPavel Emelyanov optname != IP_XFRM_POLICY && 12863f34cfaeSPaolo Abeni !ip_mroute_opt(optname)) 12873f34cfaeSPaolo Abeni err = compat_nf_setsockopt(sk, PF_INET, optname, optval, 12883f34cfaeSPaolo Abeni optlen); 12893fdadf7dSDmitry Mishin #endif 12903fdadf7dSDmitry Mishin return err; 12913fdadf7dSDmitry Mishin } 1292543d9cfeSArnaldo Carvalho de Melo EXPORT_SYMBOL(compat_ip_setsockopt); 12933fdadf7dSDmitry Mishin #endif 12943fdadf7dSDmitry Mishin 12951da177e4SLinus Torvalds /* 12964d52cfbeSEric Dumazet * Get the options. Note for future reference. The GET of IP options gets 12974d52cfbeSEric Dumazet * the _received_ ones. The set sets the _sent_ ones. 12981da177e4SLinus Torvalds */ 12991da177e4SLinus Torvalds 130087e9f031SWANG Cong static bool getsockopt_needs_rtnl(int optname) 130187e9f031SWANG Cong { 130287e9f031SWANG Cong switch (optname) { 130387e9f031SWANG Cong case IP_MSFILTER: 130487e9f031SWANG Cong case MCAST_MSFILTER: 130587e9f031SWANG Cong return true; 130687e9f031SWANG Cong } 130787e9f031SWANG Cong return false; 130887e9f031SWANG Cong } 130987e9f031SWANG Cong 13103fdadf7dSDmitry Mishin static int do_ip_getsockopt(struct sock *sk, int level, int optname, 131195c96174SEric Dumazet char __user *optval, int __user *optlen, unsigned int flags) 13121da177e4SLinus Torvalds { 13131da177e4SLinus Torvalds struct inet_sock *inet = inet_sk(sk); 131487e9f031SWANG Cong bool needs_rtnl = getsockopt_needs_rtnl(optname); 131587e9f031SWANG Cong int val, err = 0; 13161da177e4SLinus Torvalds int len; 13171da177e4SLinus Torvalds 13181da177e4SLinus Torvalds if (level != SOL_IP) 13191da177e4SLinus Torvalds return -EOPNOTSUPP; 13201da177e4SLinus Torvalds 13216a9fb947SPavel Emelyanov if (ip_mroute_opt(optname)) 13221da177e4SLinus Torvalds return ip_mroute_getsockopt(sk, optname, optval, optlen); 13231da177e4SLinus Torvalds 13241da177e4SLinus Torvalds if (get_user(len, optlen)) 13251da177e4SLinus Torvalds return -EFAULT; 13261da177e4SLinus Torvalds if (len < 0) 13271da177e4SLinus Torvalds return -EINVAL; 13281da177e4SLinus Torvalds 132987e9f031SWANG Cong if (needs_rtnl) 133087e9f031SWANG Cong rtnl_lock(); 13311da177e4SLinus Torvalds lock_sock(sk); 13321da177e4SLinus Torvalds 13331da177e4SLinus Torvalds switch (optname) { 13341da177e4SLinus Torvalds case IP_OPTIONS: 13351da177e4SLinus Torvalds { 13361da177e4SLinus Torvalds unsigned char optbuf[sizeof(struct ip_options)+40]; 13371da177e4SLinus Torvalds struct ip_options *opt = (struct ip_options *)optbuf; 1338f6d8bd05SEric Dumazet struct ip_options_rcu *inet_opt; 1339f6d8bd05SEric Dumazet 1340f6d8bd05SEric Dumazet inet_opt = rcu_dereference_protected(inet->inet_opt, 13411e1d04e6SHannes Frederic Sowa lockdep_sock_is_held(sk)); 13421da177e4SLinus Torvalds opt->optlen = 0; 1343f6d8bd05SEric Dumazet if (inet_opt) 1344f6d8bd05SEric Dumazet memcpy(optbuf, &inet_opt->opt, 13451da177e4SLinus Torvalds sizeof(struct ip_options) + 1346f6d8bd05SEric Dumazet inet_opt->opt.optlen); 13471da177e4SLinus Torvalds release_sock(sk); 13481da177e4SLinus Torvalds 13491da177e4SLinus Torvalds if (opt->optlen == 0) 13501da177e4SLinus Torvalds return put_user(0, optlen); 13511da177e4SLinus Torvalds 13521da177e4SLinus Torvalds ip_options_undo(opt); 13531da177e4SLinus Torvalds 13541da177e4SLinus Torvalds len = min_t(unsigned int, len, opt->optlen); 13551da177e4SLinus Torvalds if (put_user(len, optlen)) 13561da177e4SLinus Torvalds return -EFAULT; 13571da177e4SLinus Torvalds if (copy_to_user(optval, opt->__data, len)) 13581da177e4SLinus Torvalds return -EFAULT; 13591da177e4SLinus Torvalds return 0; 13601da177e4SLinus Torvalds } 13611da177e4SLinus Torvalds case IP_PKTINFO: 13621da177e4SLinus Torvalds val = (inet->cmsg_flags & IP_CMSG_PKTINFO) != 0; 13631da177e4SLinus Torvalds break; 13641da177e4SLinus Torvalds case IP_RECVTTL: 13651da177e4SLinus Torvalds val = (inet->cmsg_flags & IP_CMSG_TTL) != 0; 13661da177e4SLinus Torvalds break; 13671da177e4SLinus Torvalds case IP_RECVTOS: 13681da177e4SLinus Torvalds val = (inet->cmsg_flags & IP_CMSG_TOS) != 0; 13691da177e4SLinus Torvalds break; 13701da177e4SLinus Torvalds case IP_RECVOPTS: 13711da177e4SLinus Torvalds val = (inet->cmsg_flags & IP_CMSG_RECVOPTS) != 0; 13721da177e4SLinus Torvalds break; 13731da177e4SLinus Torvalds case IP_RETOPTS: 13741da177e4SLinus Torvalds val = (inet->cmsg_flags & IP_CMSG_RETOPTS) != 0; 13751da177e4SLinus Torvalds break; 13762c7946a7SCatherine Zhang case IP_PASSSEC: 13772c7946a7SCatherine Zhang val = (inet->cmsg_flags & IP_CMSG_PASSSEC) != 0; 13782c7946a7SCatherine Zhang break; 1379e8b2dfe9SBalazs Scheidler case IP_RECVORIGDSTADDR: 1380e8b2dfe9SBalazs Scheidler val = (inet->cmsg_flags & IP_CMSG_ORIGDSTADDR) != 0; 1381e8b2dfe9SBalazs Scheidler break; 1382ad6f939aSTom Herbert case IP_CHECKSUM: 1383ad6f939aSTom Herbert val = (inet->cmsg_flags & IP_CMSG_CHECKSUM) != 0; 1384ad6f939aSTom Herbert break; 138570ecc248SWillem de Bruijn case IP_RECVFRAGSIZE: 138670ecc248SWillem de Bruijn val = (inet->cmsg_flags & IP_CMSG_RECVFRAGSIZE) != 0; 138770ecc248SWillem de Bruijn break; 13881da177e4SLinus Torvalds case IP_TOS: 13891da177e4SLinus Torvalds val = inet->tos; 13901da177e4SLinus Torvalds break; 13911da177e4SLinus Torvalds case IP_TTL: 1392fa50d974SNikolay Borisov { 1393fa50d974SNikolay Borisov struct net *net = sock_net(sk); 13941da177e4SLinus Torvalds val = (inet->uc_ttl == -1 ? 1395fa50d974SNikolay Borisov net->ipv4.sysctl_ip_default_ttl : 13961da177e4SLinus Torvalds inet->uc_ttl); 13971da177e4SLinus Torvalds break; 1398fa50d974SNikolay Borisov } 13991da177e4SLinus Torvalds case IP_HDRINCL: 14001da177e4SLinus Torvalds val = inet->hdrincl; 14011da177e4SLinus Torvalds break; 1402a89b4763SMichael Kerrisk case IP_NODEFRAG: 1403a89b4763SMichael Kerrisk val = inet->nodefrag; 1404a89b4763SMichael Kerrisk break; 140590c337daSEric Dumazet case IP_BIND_ADDRESS_NO_PORT: 140690c337daSEric Dumazet val = inet->bind_address_no_port; 140790c337daSEric Dumazet break; 14081da177e4SLinus Torvalds case IP_MTU_DISCOVER: 14091da177e4SLinus Torvalds val = inet->pmtudisc; 14101da177e4SLinus Torvalds break; 14111da177e4SLinus Torvalds case IP_MTU: 14121da177e4SLinus Torvalds { 14131da177e4SLinus Torvalds struct dst_entry *dst; 14141da177e4SLinus Torvalds val = 0; 14151da177e4SLinus Torvalds dst = sk_dst_get(sk); 14161da177e4SLinus Torvalds if (dst) { 14171da177e4SLinus Torvalds val = dst_mtu(dst); 14181da177e4SLinus Torvalds dst_release(dst); 14191da177e4SLinus Torvalds } 14201da177e4SLinus Torvalds if (!val) { 14211da177e4SLinus Torvalds release_sock(sk); 14221da177e4SLinus Torvalds return -ENOTCONN; 14231da177e4SLinus Torvalds } 14241da177e4SLinus Torvalds break; 14251da177e4SLinus Torvalds } 14261da177e4SLinus Torvalds case IP_RECVERR: 14271da177e4SLinus Torvalds val = inet->recverr; 14281da177e4SLinus Torvalds break; 14291da177e4SLinus Torvalds case IP_MULTICAST_TTL: 14301da177e4SLinus Torvalds val = inet->mc_ttl; 14311da177e4SLinus Torvalds break; 14321da177e4SLinus Torvalds case IP_MULTICAST_LOOP: 14331da177e4SLinus Torvalds val = inet->mc_loop; 14341da177e4SLinus Torvalds break; 143576e21053SErich E. Hoover case IP_UNICAST_IF: 143676e21053SErich E. Hoover val = (__force int)htonl((__u32) inet->uc_index); 143776e21053SErich E. Hoover break; 14381da177e4SLinus Torvalds case IP_MULTICAST_IF: 14391da177e4SLinus Torvalds { 14401da177e4SLinus Torvalds struct in_addr addr; 14411da177e4SLinus Torvalds len = min_t(unsigned int, len, sizeof(struct in_addr)); 14421da177e4SLinus Torvalds addr.s_addr = inet->mc_addr; 14431da177e4SLinus Torvalds release_sock(sk); 14441da177e4SLinus Torvalds 14451da177e4SLinus Torvalds if (put_user(len, optlen)) 14461da177e4SLinus Torvalds return -EFAULT; 14471da177e4SLinus Torvalds if (copy_to_user(optval, &addr, len)) 14481da177e4SLinus Torvalds return -EFAULT; 14491da177e4SLinus Torvalds return 0; 14501da177e4SLinus Torvalds } 14511da177e4SLinus Torvalds case IP_MSFILTER: 14521da177e4SLinus Torvalds { 14531da177e4SLinus Torvalds struct ip_msfilter msf; 14541da177e4SLinus Torvalds 14551da177e4SLinus Torvalds if (len < IP_MSFILTER_SIZE(0)) { 145687e9f031SWANG Cong err = -EINVAL; 145787e9f031SWANG Cong goto out; 14581da177e4SLinus Torvalds } 14591da177e4SLinus Torvalds if (copy_from_user(&msf, optval, IP_MSFILTER_SIZE(0))) { 146087e9f031SWANG Cong err = -EFAULT; 146187e9f031SWANG Cong goto out; 14621da177e4SLinus Torvalds } 14631da177e4SLinus Torvalds err = ip_mc_msfget(sk, &msf, 14641da177e4SLinus Torvalds (struct ip_msfilter __user *)optval, optlen); 146587e9f031SWANG Cong goto out; 14661da177e4SLinus Torvalds } 14671da177e4SLinus Torvalds case MCAST_MSFILTER: 14681da177e4SLinus Torvalds { 14691da177e4SLinus Torvalds struct group_filter gsf; 14701da177e4SLinus Torvalds 14711da177e4SLinus Torvalds if (len < GROUP_FILTER_SIZE(0)) { 147287e9f031SWANG Cong err = -EINVAL; 147387e9f031SWANG Cong goto out; 14741da177e4SLinus Torvalds } 14751da177e4SLinus Torvalds if (copy_from_user(&gsf, optval, GROUP_FILTER_SIZE(0))) { 147687e9f031SWANG Cong err = -EFAULT; 147787e9f031SWANG Cong goto out; 14781da177e4SLinus Torvalds } 14791da177e4SLinus Torvalds err = ip_mc_gsfget(sk, &gsf, 14804d52cfbeSEric Dumazet (struct group_filter __user *)optval, 14814d52cfbeSEric Dumazet optlen); 148287e9f031SWANG Cong goto out; 14831da177e4SLinus Torvalds } 1484f771bef9SNivedita Singhvi case IP_MULTICAST_ALL: 1485f771bef9SNivedita Singhvi val = inet->mc_all; 1486f771bef9SNivedita Singhvi break; 14871da177e4SLinus Torvalds case IP_PKTOPTIONS: 14881da177e4SLinus Torvalds { 14891da177e4SLinus Torvalds struct msghdr msg; 14901da177e4SLinus Torvalds 14911da177e4SLinus Torvalds release_sock(sk); 14921da177e4SLinus Torvalds 14931da177e4SLinus Torvalds if (sk->sk_type != SOCK_STREAM) 14941da177e4SLinus Torvalds return -ENOPROTOOPT; 14951da177e4SLinus Torvalds 1496c54a5e02SKaroly Kemeny msg.msg_control = (__force void *) optval; 14971da177e4SLinus Torvalds msg.msg_controllen = len; 1498dd23198eSDaniel Baluta msg.msg_flags = flags; 14991da177e4SLinus Torvalds 15001da177e4SLinus Torvalds if (inet->cmsg_flags & IP_CMSG_PKTINFO) { 15011da177e4SLinus Torvalds struct in_pktinfo info; 15021da177e4SLinus Torvalds 1503c720c7e8SEric Dumazet info.ipi_addr.s_addr = inet->inet_rcv_saddr; 1504c720c7e8SEric Dumazet info.ipi_spec_dst.s_addr = inet->inet_rcv_saddr; 15051da177e4SLinus Torvalds info.ipi_ifindex = inet->mc_index; 15061da177e4SLinus Torvalds put_cmsg(&msg, SOL_IP, IP_PKTINFO, sizeof(info), &info); 15071da177e4SLinus Torvalds } 15081da177e4SLinus Torvalds if (inet->cmsg_flags & IP_CMSG_TTL) { 15091da177e4SLinus Torvalds int hlim = inet->mc_ttl; 15101da177e4SLinus Torvalds put_cmsg(&msg, SOL_IP, IP_TTL, sizeof(hlim), &hlim); 15111da177e4SLinus Torvalds } 15124c507d28SJiri Benc if (inet->cmsg_flags & IP_CMSG_TOS) { 15134c507d28SJiri Benc int tos = inet->rcv_tos; 15144c507d28SJiri Benc put_cmsg(&msg, SOL_IP, IP_TOS, sizeof(tos), &tos); 15154c507d28SJiri Benc } 15161da177e4SLinus Torvalds len -= msg.msg_controllen; 15171da177e4SLinus Torvalds return put_user(len, optlen); 15181da177e4SLinus Torvalds } 15191da177e4SLinus Torvalds case IP_FREEBIND: 15201da177e4SLinus Torvalds val = inet->freebind; 15211da177e4SLinus Torvalds break; 1522f5715aeaSKOVACS Krisztian case IP_TRANSPARENT: 1523f5715aeaSKOVACS Krisztian val = inet->transparent; 1524f5715aeaSKOVACS Krisztian break; 1525d218d111SStephen Hemminger case IP_MINTTL: 1526d218d111SStephen Hemminger val = inet->min_ttl; 1527d218d111SStephen Hemminger break; 15281da177e4SLinus Torvalds default: 15291da177e4SLinus Torvalds release_sock(sk); 15301da177e4SLinus Torvalds return -ENOPROTOOPT; 15311da177e4SLinus Torvalds } 15321da177e4SLinus Torvalds release_sock(sk); 15331da177e4SLinus Torvalds 1534951e07c9SDavid S. Miller if (len < sizeof(int) && len > 0 && val >= 0 && val <= 255) { 15351da177e4SLinus Torvalds unsigned char ucval = (unsigned char)val; 15361da177e4SLinus Torvalds len = 1; 15371da177e4SLinus Torvalds if (put_user(len, optlen)) 15381da177e4SLinus Torvalds return -EFAULT; 15391da177e4SLinus Torvalds if (copy_to_user(optval, &ucval, 1)) 15401da177e4SLinus Torvalds return -EFAULT; 15411da177e4SLinus Torvalds } else { 15421da177e4SLinus Torvalds len = min_t(unsigned int, sizeof(int), len); 15431da177e4SLinus Torvalds if (put_user(len, optlen)) 15441da177e4SLinus Torvalds return -EFAULT; 15451da177e4SLinus Torvalds if (copy_to_user(optval, &val, len)) 15461da177e4SLinus Torvalds return -EFAULT; 15471da177e4SLinus Torvalds } 15481da177e4SLinus Torvalds return 0; 154987e9f031SWANG Cong 155087e9f031SWANG Cong out: 155187e9f031SWANG Cong release_sock(sk); 155287e9f031SWANG Cong if (needs_rtnl) 155387e9f031SWANG Cong rtnl_unlock(); 155487e9f031SWANG Cong return err; 15551da177e4SLinus Torvalds } 15561da177e4SLinus Torvalds 15573fdadf7dSDmitry Mishin int ip_getsockopt(struct sock *sk, int level, 15583fdadf7dSDmitry Mishin int optname, char __user *optval, int __user *optlen) 15593fdadf7dSDmitry Mishin { 15603fdadf7dSDmitry Mishin int err; 15613fdadf7dSDmitry Mishin 1562dd23198eSDaniel Baluta err = do_ip_getsockopt(sk, level, optname, optval, optlen, 0); 15633fdadf7dSDmitry Mishin #ifdef CONFIG_NETFILTER 15643fdadf7dSDmitry Mishin /* we need to exclude all possible ENOPROTOOPTs except default case */ 15656a9fb947SPavel Emelyanov if (err == -ENOPROTOOPT && optname != IP_PKTOPTIONS && 15666a9fb947SPavel Emelyanov !ip_mroute_opt(optname)) { 15673fdadf7dSDmitry Mishin int len; 15683fdadf7dSDmitry Mishin 15693fdadf7dSDmitry Mishin if (get_user(len, optlen)) 15703fdadf7dSDmitry Mishin return -EFAULT; 15713fdadf7dSDmitry Mishin 15723fdadf7dSDmitry Mishin lock_sock(sk); 15733fdadf7dSDmitry Mishin err = nf_getsockopt(sk, PF_INET, optname, optval, 15743fdadf7dSDmitry Mishin &len); 15753fdadf7dSDmitry Mishin release_sock(sk); 15763fdadf7dSDmitry Mishin if (err >= 0) 15773fdadf7dSDmitry Mishin err = put_user(len, optlen); 15783fdadf7dSDmitry Mishin return err; 15793fdadf7dSDmitry Mishin } 15803fdadf7dSDmitry Mishin #endif 15813fdadf7dSDmitry Mishin return err; 15823fdadf7dSDmitry Mishin } 15834d52cfbeSEric Dumazet EXPORT_SYMBOL(ip_getsockopt); 15843fdadf7dSDmitry Mishin 15853fdadf7dSDmitry Mishin #ifdef CONFIG_COMPAT 1586543d9cfeSArnaldo Carvalho de Melo int compat_ip_getsockopt(struct sock *sk, int level, int optname, 1587543d9cfeSArnaldo Carvalho de Melo char __user *optval, int __user *optlen) 15883fdadf7dSDmitry Mishin { 158942908c69SDavid L Stevens int err; 159042908c69SDavid L Stevens 159142908c69SDavid L Stevens if (optname == MCAST_MSFILTER) 159242908c69SDavid L Stevens return compat_mc_getsockopt(sk, level, optname, optval, optlen, 159342908c69SDavid L Stevens ip_getsockopt); 159442908c69SDavid L Stevens 1595dd23198eSDaniel Baluta err = do_ip_getsockopt(sk, level, optname, optval, optlen, 1596dd23198eSDaniel Baluta MSG_CMSG_COMPAT); 159742908c69SDavid L Stevens 15983fdadf7dSDmitry Mishin #ifdef CONFIG_NETFILTER 15993fdadf7dSDmitry Mishin /* we need to exclude all possible ENOPROTOOPTs except default case */ 16006a9fb947SPavel Emelyanov if (err == -ENOPROTOOPT && optname != IP_PKTOPTIONS && 16016a9fb947SPavel Emelyanov !ip_mroute_opt(optname)) { 16023fdadf7dSDmitry Mishin int len; 16033fdadf7dSDmitry Mishin 16043fdadf7dSDmitry Mishin if (get_user(len, optlen)) 16053fdadf7dSDmitry Mishin return -EFAULT; 16063fdadf7dSDmitry Mishin 16073fdadf7dSDmitry Mishin lock_sock(sk); 1608543d9cfeSArnaldo Carvalho de Melo err = compat_nf_getsockopt(sk, PF_INET, optname, optval, &len); 16093fdadf7dSDmitry Mishin release_sock(sk); 16103fdadf7dSDmitry Mishin if (err >= 0) 16113fdadf7dSDmitry Mishin err = put_user(len, optlen); 16123fdadf7dSDmitry Mishin return err; 16133fdadf7dSDmitry Mishin } 16143fdadf7dSDmitry Mishin #endif 16153fdadf7dSDmitry Mishin return err; 16163fdadf7dSDmitry Mishin } 1617543d9cfeSArnaldo Carvalho de Melo EXPORT_SYMBOL(compat_ip_getsockopt); 16183fdadf7dSDmitry Mishin #endif 1619