11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * INET An implementation of the TCP/IP protocol suite for the LINUX 31da177e4SLinus Torvalds * operating system. INET is implemented using the BSD Socket 41da177e4SLinus Torvalds * interface as the means of communication with the user level. 51da177e4SLinus Torvalds * 61da177e4SLinus Torvalds * The IP to API glue. 71da177e4SLinus Torvalds * 81da177e4SLinus Torvalds * Authors: see ip.c 91da177e4SLinus Torvalds * 101da177e4SLinus Torvalds * Fixes: 111da177e4SLinus Torvalds * Many : Split from ip.c , see ip.c for history. 121da177e4SLinus Torvalds * Martin Mares : TOS setting fixed. 131da177e4SLinus Torvalds * Alan Cox : Fixed a couple of oopses in Martin's 141da177e4SLinus Torvalds * TOS tweaks. 151da177e4SLinus Torvalds * Mike McLagan : Routing by source 161da177e4SLinus Torvalds */ 171da177e4SLinus Torvalds 181da177e4SLinus Torvalds #include <linux/module.h> 191da177e4SLinus Torvalds #include <linux/types.h> 201da177e4SLinus Torvalds #include <linux/mm.h> 211da177e4SLinus Torvalds #include <linux/skbuff.h> 221da177e4SLinus Torvalds #include <linux/ip.h> 231da177e4SLinus Torvalds #include <linux/icmp.h> 2414c85021SArnaldo Carvalho de Melo #include <linux/inetdevice.h> 251da177e4SLinus Torvalds #include <linux/netdevice.h> 265a0e3ad6STejun Heo #include <linux/slab.h> 271da177e4SLinus Torvalds #include <net/sock.h> 281da177e4SLinus Torvalds #include <net/ip.h> 291da177e4SLinus Torvalds #include <net/icmp.h> 30d83d8461SArnaldo Carvalho de Melo #include <net/tcp_states.h> 311da177e4SLinus Torvalds #include <linux/udp.h> 321da177e4SLinus Torvalds #include <linux/igmp.h> 331da177e4SLinus Torvalds #include <linux/netfilter.h> 341da177e4SLinus Torvalds #include <linux/route.h> 351da177e4SLinus Torvalds #include <linux/mroute.h> 362c67e9acSMaciej Żenczykowski #include <net/inet_ecn.h> 371da177e4SLinus Torvalds #include <net/route.h> 381da177e4SLinus Torvalds #include <net/xfrm.h> 39dae50295SDavid L Stevens #include <net/compat.h> 40ad6f939aSTom Herbert #include <net/checksum.h> 41dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6) 421da177e4SLinus Torvalds #include <net/transp_v6.h> 431da177e4SLinus Torvalds #endif 4435ebf65eSDavid S. Miller #include <net/ip_fib.h> 451da177e4SLinus Torvalds 461da177e4SLinus Torvalds #include <linux/errqueue.h> 471da177e4SLinus Torvalds #include <asm/uaccess.h> 481da177e4SLinus Torvalds 491da177e4SLinus Torvalds /* 501da177e4SLinus Torvalds * SOL_IP control messages. 511da177e4SLinus Torvalds */ 521da177e4SLinus Torvalds 531da177e4SLinus Torvalds static void ip_cmsg_recv_pktinfo(struct msghdr *msg, struct sk_buff *skb) 541da177e4SLinus Torvalds { 55d826eb14SEric Dumazet struct in_pktinfo info = *PKTINFO_SKB_CB(skb); 561da177e4SLinus Torvalds 57eddc9ec5SArnaldo Carvalho de Melo info.ipi_addr.s_addr = ip_hdr(skb)->daddr; 581da177e4SLinus Torvalds 591da177e4SLinus Torvalds put_cmsg(msg, SOL_IP, IP_PKTINFO, sizeof(info), &info); 601da177e4SLinus Torvalds } 611da177e4SLinus Torvalds 621da177e4SLinus Torvalds static void ip_cmsg_recv_ttl(struct msghdr *msg, struct sk_buff *skb) 631da177e4SLinus Torvalds { 64eddc9ec5SArnaldo Carvalho de Melo int ttl = ip_hdr(skb)->ttl; 651da177e4SLinus Torvalds put_cmsg(msg, SOL_IP, IP_TTL, sizeof(int), &ttl); 661da177e4SLinus Torvalds } 671da177e4SLinus Torvalds 681da177e4SLinus Torvalds static void ip_cmsg_recv_tos(struct msghdr *msg, struct sk_buff *skb) 691da177e4SLinus Torvalds { 70eddc9ec5SArnaldo Carvalho de Melo put_cmsg(msg, SOL_IP, IP_TOS, 1, &ip_hdr(skb)->tos); 711da177e4SLinus Torvalds } 721da177e4SLinus Torvalds 731da177e4SLinus Torvalds static void ip_cmsg_recv_opts(struct msghdr *msg, struct sk_buff *skb) 741da177e4SLinus Torvalds { 751da177e4SLinus Torvalds if (IPCB(skb)->opt.optlen == 0) 761da177e4SLinus Torvalds return; 771da177e4SLinus Torvalds 78eddc9ec5SArnaldo Carvalho de Melo put_cmsg(msg, SOL_IP, IP_RECVOPTS, IPCB(skb)->opt.optlen, 79eddc9ec5SArnaldo Carvalho de Melo ip_hdr(skb) + 1); 801da177e4SLinus Torvalds } 811da177e4SLinus Torvalds 821da177e4SLinus Torvalds 831da177e4SLinus Torvalds static void ip_cmsg_recv_retopts(struct msghdr *msg, struct sk_buff *skb) 841da177e4SLinus Torvalds { 851da177e4SLinus Torvalds unsigned char optbuf[sizeof(struct ip_options) + 40]; 861da177e4SLinus Torvalds struct ip_options *opt = (struct ip_options *)optbuf; 871da177e4SLinus Torvalds 881da177e4SLinus Torvalds if (IPCB(skb)->opt.optlen == 0) 891da177e4SLinus Torvalds return; 901da177e4SLinus Torvalds 911da177e4SLinus Torvalds if (ip_options_echo(opt, skb)) { 921da177e4SLinus Torvalds msg->msg_flags |= MSG_CTRUNC; 931da177e4SLinus Torvalds return; 941da177e4SLinus Torvalds } 951da177e4SLinus Torvalds ip_options_undo(opt); 961da177e4SLinus Torvalds 971da177e4SLinus Torvalds put_cmsg(msg, SOL_IP, IP_RETOPTS, opt->optlen, opt->__data); 981da177e4SLinus Torvalds } 991da177e4SLinus Torvalds 100ad6f939aSTom Herbert static void ip_cmsg_recv_checksum(struct msghdr *msg, struct sk_buff *skb, 101ad6f939aSTom Herbert int offset) 102ad6f939aSTom Herbert { 103ad6f939aSTom Herbert __wsum csum = skb->csum; 104ad6f939aSTom Herbert 105ad6f939aSTom Herbert if (skb->ip_summed != CHECKSUM_COMPLETE) 106ad6f939aSTom Herbert return; 107ad6f939aSTom Herbert 108ad6f939aSTom Herbert if (offset != 0) 109ad6f939aSTom Herbert csum = csum_sub(csum, csum_partial(skb->data, offset, 0)); 110ad6f939aSTom Herbert 111ad6f939aSTom Herbert put_cmsg(msg, SOL_IP, IP_CHECKSUM, sizeof(__wsum), &csum); 112ad6f939aSTom Herbert } 113ad6f939aSTom Herbert 1142c7946a7SCatherine Zhang static void ip_cmsg_recv_security(struct msghdr *msg, struct sk_buff *skb) 1152c7946a7SCatherine Zhang { 1162c7946a7SCatherine Zhang char *secdata; 117dc49c1f9SCatherine Zhang u32 seclen, secid; 1182c7946a7SCatherine Zhang int err; 1192c7946a7SCatherine Zhang 120dc49c1f9SCatherine Zhang err = security_socket_getpeersec_dgram(NULL, skb, &secid); 121dc49c1f9SCatherine Zhang if (err) 122dc49c1f9SCatherine Zhang return; 123dc49c1f9SCatherine Zhang 124dc49c1f9SCatherine Zhang err = security_secid_to_secctx(secid, &secdata, &seclen); 1252c7946a7SCatherine Zhang if (err) 1262c7946a7SCatherine Zhang return; 1272c7946a7SCatherine Zhang 1282c7946a7SCatherine Zhang put_cmsg(msg, SOL_IP, SCM_SECURITY, seclen, secdata); 129dc49c1f9SCatherine Zhang security_release_secctx(secdata, seclen); 1302c7946a7SCatherine Zhang } 1312c7946a7SCatherine Zhang 13221d1a161SHarvey Harrison static void ip_cmsg_recv_dstaddr(struct msghdr *msg, struct sk_buff *skb) 133e8b2dfe9SBalazs Scheidler { 134e8b2dfe9SBalazs Scheidler struct sockaddr_in sin; 135b71d1d42SEric Dumazet const struct iphdr *iph = ip_hdr(skb); 13621d1a161SHarvey Harrison __be16 *ports = (__be16 *)skb_transport_header(skb); 137e8b2dfe9SBalazs Scheidler 138e8b2dfe9SBalazs Scheidler if (skb_transport_offset(skb) + 4 > skb->len) 139e8b2dfe9SBalazs Scheidler return; 140e8b2dfe9SBalazs Scheidler 141e8b2dfe9SBalazs Scheidler /* All current transport protocols have the port numbers in the 142e8b2dfe9SBalazs Scheidler * first four bytes of the transport header and this function is 143e8b2dfe9SBalazs Scheidler * written with this assumption in mind. 144e8b2dfe9SBalazs Scheidler */ 145e8b2dfe9SBalazs Scheidler 146e8b2dfe9SBalazs Scheidler sin.sin_family = AF_INET; 147e8b2dfe9SBalazs Scheidler sin.sin_addr.s_addr = iph->daddr; 148e8b2dfe9SBalazs Scheidler sin.sin_port = ports[1]; 149e8b2dfe9SBalazs Scheidler memset(sin.sin_zero, 0, sizeof(sin.sin_zero)); 150e8b2dfe9SBalazs Scheidler 151e8b2dfe9SBalazs Scheidler put_cmsg(msg, SOL_IP, IP_ORIGDSTADDR, sizeof(sin), &sin); 152e8b2dfe9SBalazs Scheidler } 1531da177e4SLinus Torvalds 1545961de9fSTom Herbert void ip_cmsg_recv_offset(struct msghdr *msg, struct sk_buff *skb, 1555961de9fSTom Herbert int offset) 1561da177e4SLinus Torvalds { 1571da177e4SLinus Torvalds struct inet_sock *inet = inet_sk(skb->sk); 15895c96174SEric Dumazet unsigned int flags = inet->cmsg_flags; 1591da177e4SLinus Torvalds 1601da177e4SLinus Torvalds /* Ordered by supposed usage frequency */ 161c44d13d6STom Herbert if (flags & IP_CMSG_PKTINFO) { 1621da177e4SLinus Torvalds ip_cmsg_recv_pktinfo(msg, skb); 1631da177e4SLinus Torvalds 164c44d13d6STom Herbert flags &= ~IP_CMSG_PKTINFO; 165c44d13d6STom Herbert if (!flags) 166c44d13d6STom Herbert return; 167c44d13d6STom Herbert } 168c44d13d6STom Herbert 169c44d13d6STom Herbert if (flags & IP_CMSG_TTL) { 1701da177e4SLinus Torvalds ip_cmsg_recv_ttl(msg, skb); 1711da177e4SLinus Torvalds 172c44d13d6STom Herbert flags &= ~IP_CMSG_TTL; 173c44d13d6STom Herbert if (!flags) 174c44d13d6STom Herbert return; 175c44d13d6STom Herbert } 176c44d13d6STom Herbert 177c44d13d6STom Herbert if (flags & IP_CMSG_TOS) { 1781da177e4SLinus Torvalds ip_cmsg_recv_tos(msg, skb); 1791da177e4SLinus Torvalds 180c44d13d6STom Herbert flags &= ~IP_CMSG_TOS; 181c44d13d6STom Herbert if (!flags) 182c44d13d6STom Herbert return; 183c44d13d6STom Herbert } 184c44d13d6STom Herbert 185c44d13d6STom Herbert if (flags & IP_CMSG_RECVOPTS) { 1861da177e4SLinus Torvalds ip_cmsg_recv_opts(msg, skb); 1871da177e4SLinus Torvalds 188c44d13d6STom Herbert flags &= ~IP_CMSG_RECVOPTS; 189c44d13d6STom Herbert if (!flags) 190c44d13d6STom Herbert return; 191c44d13d6STom Herbert } 192c44d13d6STom Herbert 193c44d13d6STom Herbert if (flags & IP_CMSG_RETOPTS) { 1941da177e4SLinus Torvalds ip_cmsg_recv_retopts(msg, skb); 1952c7946a7SCatherine Zhang 196c44d13d6STom Herbert flags &= ~IP_CMSG_RETOPTS; 197c44d13d6STom Herbert if (!flags) 198c44d13d6STom Herbert return; 199c44d13d6STom Herbert } 200c44d13d6STom Herbert 201c44d13d6STom Herbert if (flags & IP_CMSG_PASSSEC) { 2022c7946a7SCatherine Zhang ip_cmsg_recv_security(msg, skb); 203e8b2dfe9SBalazs Scheidler 204c44d13d6STom Herbert flags &= ~IP_CMSG_PASSSEC; 205c44d13d6STom Herbert if (!flags) 206e8b2dfe9SBalazs Scheidler return; 207c44d13d6STom Herbert } 208c44d13d6STom Herbert 209ad6f939aSTom Herbert if (flags & IP_CMSG_ORIGDSTADDR) { 210e8b2dfe9SBalazs Scheidler ip_cmsg_recv_dstaddr(msg, skb); 211e8b2dfe9SBalazs Scheidler 212ad6f939aSTom Herbert flags &= ~IP_CMSG_ORIGDSTADDR; 213ad6f939aSTom Herbert if (!flags) 214ad6f939aSTom Herbert return; 215ad6f939aSTom Herbert } 216ad6f939aSTom Herbert 217ad6f939aSTom Herbert if (flags & IP_CMSG_CHECKSUM) 218ad6f939aSTom Herbert ip_cmsg_recv_checksum(msg, skb, offset); 2191da177e4SLinus Torvalds } 2205961de9fSTom Herbert EXPORT_SYMBOL(ip_cmsg_recv_offset); 2211da177e4SLinus Torvalds 222c8e6ad08SHannes Frederic Sowa int ip_cmsg_send(struct net *net, struct msghdr *msg, struct ipcm_cookie *ipc, 223c8e6ad08SHannes Frederic Sowa bool allow_ipv6) 2241da177e4SLinus Torvalds { 225f02db315SFrancesco Fusco int err, val; 2261da177e4SLinus Torvalds struct cmsghdr *cmsg; 2271da177e4SLinus Torvalds 228f95b414eSGu Zheng for_each_cmsghdr(cmsg, msg) { 2291da177e4SLinus Torvalds if (!CMSG_OK(msg, cmsg)) 2301da177e4SLinus Torvalds return -EINVAL; 2315337b5b7SEric Dumazet #if IS_ENABLED(CONFIG_IPV6) 232c8e6ad08SHannes Frederic Sowa if (allow_ipv6 && 233c8e6ad08SHannes Frederic Sowa cmsg->cmsg_level == SOL_IPV6 && 234c8e6ad08SHannes Frederic Sowa cmsg->cmsg_type == IPV6_PKTINFO) { 235c8e6ad08SHannes Frederic Sowa struct in6_pktinfo *src_info; 236c8e6ad08SHannes Frederic Sowa 237c8e6ad08SHannes Frederic Sowa if (cmsg->cmsg_len < CMSG_LEN(sizeof(*src_info))) 238c8e6ad08SHannes Frederic Sowa return -EINVAL; 239c8e6ad08SHannes Frederic Sowa src_info = (struct in6_pktinfo *)CMSG_DATA(cmsg); 240c8e6ad08SHannes Frederic Sowa if (!ipv6_addr_v4mapped(&src_info->ipi6_addr)) 241c8e6ad08SHannes Frederic Sowa return -EINVAL; 242c8e6ad08SHannes Frederic Sowa ipc->oif = src_info->ipi6_ifindex; 243c8e6ad08SHannes Frederic Sowa ipc->addr = src_info->ipi6_addr.s6_addr32[3]; 244c8e6ad08SHannes Frederic Sowa continue; 245c8e6ad08SHannes Frederic Sowa } 246c8e6ad08SHannes Frederic Sowa #endif 2471da177e4SLinus Torvalds if (cmsg->cmsg_level != SOL_IP) 2481da177e4SLinus Torvalds continue; 2491da177e4SLinus Torvalds switch (cmsg->cmsg_type) { 2501da177e4SLinus Torvalds case IP_RETOPTS: 2511da177e4SLinus Torvalds err = cmsg->cmsg_len - CMSG_ALIGN(sizeof(struct cmsghdr)); 2524d52cfbeSEric Dumazet err = ip_options_get(net, &ipc->opt, CMSG_DATA(cmsg), 2534d52cfbeSEric Dumazet err < 40 ? err : 40); 2541da177e4SLinus Torvalds if (err) 2551da177e4SLinus Torvalds return err; 2561da177e4SLinus Torvalds break; 2571da177e4SLinus Torvalds case IP_PKTINFO: 2581da177e4SLinus Torvalds { 2591da177e4SLinus Torvalds struct in_pktinfo *info; 2601da177e4SLinus Torvalds if (cmsg->cmsg_len != CMSG_LEN(sizeof(struct in_pktinfo))) 2611da177e4SLinus Torvalds return -EINVAL; 2621da177e4SLinus Torvalds info = (struct in_pktinfo *)CMSG_DATA(cmsg); 2631da177e4SLinus Torvalds ipc->oif = info->ipi_ifindex; 2641da177e4SLinus Torvalds ipc->addr = info->ipi_spec_dst.s_addr; 2651da177e4SLinus Torvalds break; 2661da177e4SLinus Torvalds } 267f02db315SFrancesco Fusco case IP_TTL: 268f02db315SFrancesco Fusco if (cmsg->cmsg_len != CMSG_LEN(sizeof(int))) 269f02db315SFrancesco Fusco return -EINVAL; 270f02db315SFrancesco Fusco val = *(int *)CMSG_DATA(cmsg); 271f02db315SFrancesco Fusco if (val < 1 || val > 255) 272f02db315SFrancesco Fusco return -EINVAL; 273f02db315SFrancesco Fusco ipc->ttl = val; 274f02db315SFrancesco Fusco break; 275f02db315SFrancesco Fusco case IP_TOS: 276f02db315SFrancesco Fusco if (cmsg->cmsg_len != CMSG_LEN(sizeof(int))) 277f02db315SFrancesco Fusco return -EINVAL; 278f02db315SFrancesco Fusco val = *(int *)CMSG_DATA(cmsg); 279f02db315SFrancesco Fusco if (val < 0 || val > 255) 280f02db315SFrancesco Fusco return -EINVAL; 281f02db315SFrancesco Fusco ipc->tos = val; 282f02db315SFrancesco Fusco ipc->priority = rt_tos2priority(ipc->tos); 283f02db315SFrancesco Fusco break; 284f02db315SFrancesco Fusco 2851da177e4SLinus Torvalds default: 2861da177e4SLinus Torvalds return -EINVAL; 2871da177e4SLinus Torvalds } 2881da177e4SLinus Torvalds } 2891da177e4SLinus Torvalds return 0; 2901da177e4SLinus Torvalds } 2911da177e4SLinus Torvalds 2921da177e4SLinus Torvalds 2931da177e4SLinus Torvalds /* Special input handler for packets caught by router alert option. 2941da177e4SLinus Torvalds They are selected only by protocol field, and then processed likely 2951da177e4SLinus Torvalds local ones; but only if someone wants them! Otherwise, router 2961da177e4SLinus Torvalds not running rsvpd will kill RSVP. 2971da177e4SLinus Torvalds 2981da177e4SLinus Torvalds It is user level problem, what it will make with them. 2991da177e4SLinus Torvalds I have no idea, how it will masquearde or NAT them (it is joke, joke :-)), 3001da177e4SLinus Torvalds but receiver should be enough clever f.e. to forward mtrace requests, 3011da177e4SLinus Torvalds sent to multicast group to reach destination designated router. 3021da177e4SLinus Torvalds */ 30343a951e9SEric Dumazet struct ip_ra_chain __rcu *ip_ra_chain; 30466018506SEric Dumazet static DEFINE_SPINLOCK(ip_ra_lock); 30566018506SEric Dumazet 306592fcb9dSEric Dumazet 307592fcb9dSEric Dumazet static void ip_ra_destroy_rcu(struct rcu_head *head) 30866018506SEric Dumazet { 309592fcb9dSEric Dumazet struct ip_ra_chain *ra = container_of(head, struct ip_ra_chain, rcu); 310592fcb9dSEric Dumazet 311592fcb9dSEric Dumazet sock_put(ra->saved_sk); 312592fcb9dSEric Dumazet kfree(ra); 31366018506SEric Dumazet } 3141da177e4SLinus Torvalds 3154d52cfbeSEric Dumazet int ip_ra_control(struct sock *sk, unsigned char on, 3164d52cfbeSEric Dumazet void (*destructor)(struct sock *)) 3171da177e4SLinus Torvalds { 31843a951e9SEric Dumazet struct ip_ra_chain *ra, *new_ra; 31943a951e9SEric Dumazet struct ip_ra_chain __rcu **rap; 3201da177e4SLinus Torvalds 321c720c7e8SEric Dumazet if (sk->sk_type != SOCK_RAW || inet_sk(sk)->inet_num == IPPROTO_RAW) 3221da177e4SLinus Torvalds return -EINVAL; 3231da177e4SLinus Torvalds 3241da177e4SLinus Torvalds new_ra = on ? kmalloc(sizeof(*new_ra), GFP_KERNEL) : NULL; 3251da177e4SLinus Torvalds 32666018506SEric Dumazet spin_lock_bh(&ip_ra_lock); 32743a951e9SEric Dumazet for (rap = &ip_ra_chain; 32843a951e9SEric Dumazet (ra = rcu_dereference_protected(*rap, 32943a951e9SEric Dumazet lockdep_is_held(&ip_ra_lock))) != NULL; 33043a951e9SEric Dumazet rap = &ra->next) { 3311da177e4SLinus Torvalds if (ra->sk == sk) { 3321da177e4SLinus Torvalds if (on) { 33366018506SEric Dumazet spin_unlock_bh(&ip_ra_lock); 3341da177e4SLinus Torvalds kfree(new_ra); 3351da177e4SLinus Torvalds return -EADDRINUSE; 3361da177e4SLinus Torvalds } 337592fcb9dSEric Dumazet /* dont let ip_call_ra_chain() use sk again */ 338592fcb9dSEric Dumazet ra->sk = NULL; 3398e380f00SEric Dumazet RCU_INIT_POINTER(*rap, ra->next); 34066018506SEric Dumazet spin_unlock_bh(&ip_ra_lock); 3411da177e4SLinus Torvalds 3421da177e4SLinus Torvalds if (ra->destructor) 3431da177e4SLinus Torvalds ra->destructor(sk); 344592fcb9dSEric Dumazet /* 345592fcb9dSEric Dumazet * Delay sock_put(sk) and kfree(ra) after one rcu grace 346592fcb9dSEric Dumazet * period. This guarantee ip_call_ra_chain() dont need 347592fcb9dSEric Dumazet * to mess with socket refcounts. 348592fcb9dSEric Dumazet */ 349592fcb9dSEric Dumazet ra->saved_sk = sk; 350592fcb9dSEric Dumazet call_rcu(&ra->rcu, ip_ra_destroy_rcu); 3511da177e4SLinus Torvalds return 0; 3521da177e4SLinus Torvalds } 3531da177e4SLinus Torvalds } 3541da177e4SLinus Torvalds if (new_ra == NULL) { 35566018506SEric Dumazet spin_unlock_bh(&ip_ra_lock); 3561da177e4SLinus Torvalds return -ENOBUFS; 3571da177e4SLinus Torvalds } 3581da177e4SLinus Torvalds new_ra->sk = sk; 3591da177e4SLinus Torvalds new_ra->destructor = destructor; 3601da177e4SLinus Torvalds 3618e380f00SEric Dumazet RCU_INIT_POINTER(new_ra->next, ra); 36266018506SEric Dumazet rcu_assign_pointer(*rap, new_ra); 3631da177e4SLinus Torvalds sock_hold(sk); 36466018506SEric Dumazet spin_unlock_bh(&ip_ra_lock); 3651da177e4SLinus Torvalds 3661da177e4SLinus Torvalds return 0; 3671da177e4SLinus Torvalds } 3681da177e4SLinus Torvalds 3691da177e4SLinus Torvalds void ip_icmp_error(struct sock *sk, struct sk_buff *skb, int err, 37035986b32SAl Viro __be16 port, u32 info, u8 *payload) 3711da177e4SLinus Torvalds { 3721da177e4SLinus Torvalds struct sock_exterr_skb *serr; 3731da177e4SLinus Torvalds 3741da177e4SLinus Torvalds skb = skb_clone(skb, GFP_ATOMIC); 3751da177e4SLinus Torvalds if (!skb) 3761da177e4SLinus Torvalds return; 3771da177e4SLinus Torvalds 3781da177e4SLinus Torvalds serr = SKB_EXT_ERR(skb); 3791da177e4SLinus Torvalds serr->ee.ee_errno = err; 3801da177e4SLinus Torvalds serr->ee.ee_origin = SO_EE_ORIGIN_ICMP; 38188c7664fSArnaldo Carvalho de Melo serr->ee.ee_type = icmp_hdr(skb)->type; 38288c7664fSArnaldo Carvalho de Melo serr->ee.ee_code = icmp_hdr(skb)->code; 3831da177e4SLinus Torvalds serr->ee.ee_pad = 0; 3841da177e4SLinus Torvalds serr->ee.ee_info = info; 3851da177e4SLinus Torvalds serr->ee.ee_data = 0; 38688c7664fSArnaldo Carvalho de Melo serr->addr_offset = (u8 *)&(((struct iphdr *)(icmp_hdr(skb) + 1))->daddr) - 387d56f90a7SArnaldo Carvalho de Melo skb_network_header(skb); 3881da177e4SLinus Torvalds serr->port = port; 3891da177e4SLinus Torvalds 390bd82393cSArnaldo Carvalho de Melo if (skb_pull(skb, payload - skb->data) != NULL) { 391bd82393cSArnaldo Carvalho de Melo skb_reset_transport_header(skb); 392bd82393cSArnaldo Carvalho de Melo if (sock_queue_err_skb(sk, skb) == 0) 393bd82393cSArnaldo Carvalho de Melo return; 394bd82393cSArnaldo Carvalho de Melo } 3951da177e4SLinus Torvalds kfree_skb(skb); 3961da177e4SLinus Torvalds } 3971da177e4SLinus Torvalds 3980579016eSAl Viro void ip_local_error(struct sock *sk, int err, __be32 daddr, __be16 port, u32 info) 3991da177e4SLinus Torvalds { 4001da177e4SLinus Torvalds struct inet_sock *inet = inet_sk(sk); 4011da177e4SLinus Torvalds struct sock_exterr_skb *serr; 4021da177e4SLinus Torvalds struct iphdr *iph; 4031da177e4SLinus Torvalds struct sk_buff *skb; 4041da177e4SLinus Torvalds 4051da177e4SLinus Torvalds if (!inet->recverr) 4061da177e4SLinus Torvalds return; 4071da177e4SLinus Torvalds 4081da177e4SLinus Torvalds skb = alloc_skb(sizeof(struct iphdr), GFP_ATOMIC); 4091da177e4SLinus Torvalds if (!skb) 4101da177e4SLinus Torvalds return; 4111da177e4SLinus Torvalds 4122ca9e6f2SArnaldo Carvalho de Melo skb_put(skb, sizeof(struct iphdr)); 4132ca9e6f2SArnaldo Carvalho de Melo skb_reset_network_header(skb); 414eddc9ec5SArnaldo Carvalho de Melo iph = ip_hdr(skb); 4151da177e4SLinus Torvalds iph->daddr = daddr; 4161da177e4SLinus Torvalds 4171da177e4SLinus Torvalds serr = SKB_EXT_ERR(skb); 4181da177e4SLinus Torvalds serr->ee.ee_errno = err; 4191da177e4SLinus Torvalds serr->ee.ee_origin = SO_EE_ORIGIN_LOCAL; 4201da177e4SLinus Torvalds serr->ee.ee_type = 0; 4211da177e4SLinus Torvalds serr->ee.ee_code = 0; 4221da177e4SLinus Torvalds serr->ee.ee_pad = 0; 4231da177e4SLinus Torvalds serr->ee.ee_info = info; 4241da177e4SLinus Torvalds serr->ee.ee_data = 0; 425d56f90a7SArnaldo Carvalho de Melo serr->addr_offset = (u8 *)&iph->daddr - skb_network_header(skb); 4261da177e4SLinus Torvalds serr->port = port; 4271da177e4SLinus Torvalds 42827a884dcSArnaldo Carvalho de Melo __skb_pull(skb, skb_tail_pointer(skb) - skb->data); 429bd82393cSArnaldo Carvalho de Melo skb_reset_transport_header(skb); 4301da177e4SLinus Torvalds 4311da177e4SLinus Torvalds if (sock_queue_err_skb(sk, skb)) 4321da177e4SLinus Torvalds kfree_skb(skb); 4331da177e4SLinus Torvalds } 4341da177e4SLinus Torvalds 435*c247f053SWillem de Bruijn /* IPv4 supports cmsg on all imcp errors and some timestamps 436*c247f053SWillem de Bruijn * 437*c247f053SWillem de Bruijn * Timestamp code paths do not initialize the fields expected by cmsg: 438*c247f053SWillem de Bruijn * the PKTINFO fields in skb->cb[]. Fill those in here. 439*c247f053SWillem de Bruijn */ 440*c247f053SWillem de Bruijn static bool ipv4_datagram_support_cmsg(const struct sock *sk, 441*c247f053SWillem de Bruijn struct sk_buff *skb, 442829ae9d6SWillem de Bruijn int ee_origin) 443829ae9d6SWillem de Bruijn { 444*c247f053SWillem de Bruijn struct in_pktinfo *info; 445829ae9d6SWillem de Bruijn 446*c247f053SWillem de Bruijn if (ee_origin == SO_EE_ORIGIN_ICMP) 447*c247f053SWillem de Bruijn return true; 448*c247f053SWillem de Bruijn 449*c247f053SWillem de Bruijn if (ee_origin == SO_EE_ORIGIN_LOCAL) 450*c247f053SWillem de Bruijn return false; 451*c247f053SWillem de Bruijn 452*c247f053SWillem de Bruijn /* Support IP_PKTINFO on tstamp packets if requested, to correlate 453*c247f053SWillem de Bruijn * timestamp with egress dev. Not possible for packets without dev 454*c247f053SWillem de Bruijn * or without payload (SOF_TIMESTAMPING_OPT_TSONLY). 455*c247f053SWillem de Bruijn */ 456*c247f053SWillem de Bruijn if ((!(sk->sk_tsflags & SOF_TIMESTAMPING_OPT_CMSG)) || 457829ae9d6SWillem de Bruijn (!skb->dev)) 458829ae9d6SWillem de Bruijn return false; 459829ae9d6SWillem de Bruijn 460*c247f053SWillem de Bruijn info = PKTINFO_SKB_CB(skb); 461829ae9d6SWillem de Bruijn info->ipi_spec_dst.s_addr = ip_hdr(skb)->saddr; 462829ae9d6SWillem de Bruijn info->ipi_ifindex = skb->dev->ifindex; 463829ae9d6SWillem de Bruijn return true; 464829ae9d6SWillem de Bruijn } 465829ae9d6SWillem de Bruijn 4661da177e4SLinus Torvalds /* 4671da177e4SLinus Torvalds * Handle MSG_ERRQUEUE 4681da177e4SLinus Torvalds */ 46985fbaa75SHannes Frederic Sowa int ip_recv_error(struct sock *sk, struct msghdr *msg, int len, int *addr_len) 4701da177e4SLinus Torvalds { 4711da177e4SLinus Torvalds struct sock_exterr_skb *serr; 472364a9e93SWillem de Bruijn struct sk_buff *skb; 473342dfc30SSteffen Hurrle DECLARE_SOCKADDR(struct sockaddr_in *, sin, msg->msg_name); 4741da177e4SLinus Torvalds struct { 4751da177e4SLinus Torvalds struct sock_extended_err ee; 4761da177e4SLinus Torvalds struct sockaddr_in offender; 4771da177e4SLinus Torvalds } errhdr; 4781da177e4SLinus Torvalds int err; 4791da177e4SLinus Torvalds int copied; 4801da177e4SLinus Torvalds 4817ce875e5SWillem de Bruijn WARN_ON_ONCE(sk->sk_family == AF_INET6); 4827ce875e5SWillem de Bruijn 4831da177e4SLinus Torvalds err = -EAGAIN; 484364a9e93SWillem de Bruijn skb = sock_dequeue_err_skb(sk); 4851da177e4SLinus Torvalds if (skb == NULL) 4861da177e4SLinus Torvalds goto out; 4871da177e4SLinus Torvalds 4881da177e4SLinus Torvalds copied = skb->len; 4891da177e4SLinus Torvalds if (copied > len) { 4901da177e4SLinus Torvalds msg->msg_flags |= MSG_TRUNC; 4911da177e4SLinus Torvalds copied = len; 4921da177e4SLinus Torvalds } 49351f3d02bSDavid S. Miller err = skb_copy_datagram_msg(skb, 0, msg, copied); 4941da177e4SLinus Torvalds if (err) 4951da177e4SLinus Torvalds goto out_free_skb; 4961da177e4SLinus Torvalds 4971da177e4SLinus Torvalds sock_recv_timestamp(msg, sk, skb); 4981da177e4SLinus Torvalds 4991da177e4SLinus Torvalds serr = SKB_EXT_ERR(skb); 5001da177e4SLinus Torvalds 501*c247f053SWillem de Bruijn if (sin && serr->port) { 5021da177e4SLinus Torvalds sin->sin_family = AF_INET; 503d56f90a7SArnaldo Carvalho de Melo sin->sin_addr.s_addr = *(__be32 *)(skb_network_header(skb) + 504d56f90a7SArnaldo Carvalho de Melo serr->addr_offset); 5051da177e4SLinus Torvalds sin->sin_port = serr->port; 5061da177e4SLinus Torvalds memset(&sin->sin_zero, 0, sizeof(sin->sin_zero)); 50785fbaa75SHannes Frederic Sowa *addr_len = sizeof(*sin); 5081da177e4SLinus Torvalds } 5091da177e4SLinus Torvalds 5101da177e4SLinus Torvalds memcpy(&errhdr.ee, &serr->ee, sizeof(struct sock_extended_err)); 5111da177e4SLinus Torvalds sin = &errhdr.offender; 512f812116bSWillem de Bruijn memset(sin, 0, sizeof(*sin)); 513829ae9d6SWillem de Bruijn 514*c247f053SWillem de Bruijn if (ipv4_datagram_support_cmsg(sk, skb, serr->ee.ee_origin)) { 5151da177e4SLinus Torvalds sin->sin_family = AF_INET; 516eddc9ec5SArnaldo Carvalho de Melo sin->sin_addr.s_addr = ip_hdr(skb)->saddr; 517f812116bSWillem de Bruijn if (inet_sk(sk)->cmsg_flags) 5181da177e4SLinus Torvalds ip_cmsg_recv(msg, skb); 5191da177e4SLinus Torvalds } 5201da177e4SLinus Torvalds 5211da177e4SLinus Torvalds put_cmsg(msg, SOL_IP, IP_RECVERR, sizeof(errhdr), &errhdr); 5221da177e4SLinus Torvalds 5231da177e4SLinus Torvalds /* Now we could try to dump offended packet options */ 5241da177e4SLinus Torvalds 5251da177e4SLinus Torvalds msg->msg_flags |= MSG_ERRQUEUE; 5261da177e4SLinus Torvalds err = copied; 5271da177e4SLinus Torvalds 5281da177e4SLinus Torvalds out_free_skb: 5291da177e4SLinus Torvalds kfree_skb(skb); 5301da177e4SLinus Torvalds out: 5311da177e4SLinus Torvalds return err; 5321da177e4SLinus Torvalds } 5331da177e4SLinus Torvalds 5341da177e4SLinus Torvalds 5351da177e4SLinus Torvalds /* 5364d52cfbeSEric Dumazet * Socket option code for IP. This is the end of the line after any 5374d52cfbeSEric Dumazet * TCP,UDP etc options on an IP socket. 5381da177e4SLinus Torvalds */ 5391da177e4SLinus Torvalds 5403fdadf7dSDmitry Mishin static int do_ip_setsockopt(struct sock *sk, int level, 541b7058842SDavid S. Miller int optname, char __user *optval, unsigned int optlen) 5421da177e4SLinus Torvalds { 5431da177e4SLinus Torvalds struct inet_sock *inet = inet_sk(sk); 5441da177e4SLinus Torvalds int val = 0, err; 5451da177e4SLinus Torvalds 5460c9f79beSXi Wang switch (optname) { 5470c9f79beSXi Wang case IP_PKTINFO: 5480c9f79beSXi Wang case IP_RECVTTL: 5490c9f79beSXi Wang case IP_RECVOPTS: 5500c9f79beSXi Wang case IP_RECVTOS: 5510c9f79beSXi Wang case IP_RETOPTS: 5520c9f79beSXi Wang case IP_TOS: 5530c9f79beSXi Wang case IP_TTL: 5540c9f79beSXi Wang case IP_HDRINCL: 5550c9f79beSXi Wang case IP_MTU_DISCOVER: 5560c9f79beSXi Wang case IP_RECVERR: 5570c9f79beSXi Wang case IP_ROUTER_ALERT: 5580c9f79beSXi Wang case IP_FREEBIND: 5590c9f79beSXi Wang case IP_PASSSEC: 5600c9f79beSXi Wang case IP_TRANSPARENT: 5610c9f79beSXi Wang case IP_MINTTL: 5620c9f79beSXi Wang case IP_NODEFRAG: 5630c9f79beSXi Wang case IP_UNICAST_IF: 5640c9f79beSXi Wang case IP_MULTICAST_TTL: 5650c9f79beSXi Wang case IP_MULTICAST_ALL: 5660c9f79beSXi Wang case IP_MULTICAST_LOOP: 5670c9f79beSXi Wang case IP_RECVORIGDSTADDR: 568ad6f939aSTom Herbert case IP_CHECKSUM: 5691da177e4SLinus Torvalds if (optlen >= sizeof(int)) { 5701da177e4SLinus Torvalds if (get_user(val, (int __user *) optval)) 5711da177e4SLinus Torvalds return -EFAULT; 5721da177e4SLinus Torvalds } else if (optlen >= sizeof(char)) { 5731da177e4SLinus Torvalds unsigned char ucval; 5741da177e4SLinus Torvalds 5751da177e4SLinus Torvalds if (get_user(ucval, (unsigned char __user *) optval)) 5761da177e4SLinus Torvalds return -EFAULT; 5771da177e4SLinus Torvalds val = (int) ucval; 5781da177e4SLinus Torvalds } 5791da177e4SLinus Torvalds } 5801da177e4SLinus Torvalds 5811da177e4SLinus Torvalds /* If optlen==0, it is equivalent to val == 0 */ 5821da177e4SLinus Torvalds 5836a9fb947SPavel Emelyanov if (ip_mroute_opt(optname)) 5841da177e4SLinus Torvalds return ip_mroute_setsockopt(sk, optname, optval, optlen); 5851da177e4SLinus Torvalds 5861da177e4SLinus Torvalds err = 0; 5871da177e4SLinus Torvalds lock_sock(sk); 5881da177e4SLinus Torvalds 5891da177e4SLinus Torvalds switch (optname) { 5901da177e4SLinus Torvalds case IP_OPTIONS: 5911da177e4SLinus Torvalds { 592f6d8bd05SEric Dumazet struct ip_options_rcu *old, *opt = NULL; 593f6d8bd05SEric Dumazet 59465a1c4ffSroel kluin if (optlen > 40) 5951da177e4SLinus Torvalds goto e_inval; 5963b1e0a65SYOSHIFUJI Hideaki err = ip_options_get_from_user(sock_net(sk), &opt, 597cb84663eSDenis V. Lunev optval, optlen); 5981da177e4SLinus Torvalds if (err) 5991da177e4SLinus Torvalds break; 600f6d8bd05SEric Dumazet old = rcu_dereference_protected(inet->inet_opt, 601f6d8bd05SEric Dumazet sock_owned_by_user(sk)); 602d83d8461SArnaldo Carvalho de Melo if (inet->is_icsk) { 603d83d8461SArnaldo Carvalho de Melo struct inet_connection_sock *icsk = inet_csk(sk); 604dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6) 6051da177e4SLinus Torvalds if (sk->sk_family == PF_INET || 6061da177e4SLinus Torvalds (!((1 << sk->sk_state) & 6071da177e4SLinus Torvalds (TCPF_LISTEN | TCPF_CLOSE)) && 608c720c7e8SEric Dumazet inet->inet_daddr != LOOPBACK4_IPV6)) { 6091da177e4SLinus Torvalds #endif 610f6d8bd05SEric Dumazet if (old) 611f6d8bd05SEric Dumazet icsk->icsk_ext_hdr_len -= old->opt.optlen; 6121da177e4SLinus Torvalds if (opt) 613f6d8bd05SEric Dumazet icsk->icsk_ext_hdr_len += opt->opt.optlen; 614d83d8461SArnaldo Carvalho de Melo icsk->icsk_sync_mss(sk, icsk->icsk_pmtu_cookie); 615dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6) 6161da177e4SLinus Torvalds } 6171da177e4SLinus Torvalds #endif 6181da177e4SLinus Torvalds } 619f6d8bd05SEric Dumazet rcu_assign_pointer(inet->inet_opt, opt); 620f6d8bd05SEric Dumazet if (old) 621605b4afeSPaul E. McKenney kfree_rcu(old, rcu); 6221da177e4SLinus Torvalds break; 6231da177e4SLinus Torvalds } 6241da177e4SLinus Torvalds case IP_PKTINFO: 6251da177e4SLinus Torvalds if (val) 6261da177e4SLinus Torvalds inet->cmsg_flags |= IP_CMSG_PKTINFO; 6271da177e4SLinus Torvalds else 6281da177e4SLinus Torvalds inet->cmsg_flags &= ~IP_CMSG_PKTINFO; 6291da177e4SLinus Torvalds break; 6301da177e4SLinus Torvalds case IP_RECVTTL: 6311da177e4SLinus Torvalds if (val) 6321da177e4SLinus Torvalds inet->cmsg_flags |= IP_CMSG_TTL; 6331da177e4SLinus Torvalds else 6341da177e4SLinus Torvalds inet->cmsg_flags &= ~IP_CMSG_TTL; 6351da177e4SLinus Torvalds break; 6361da177e4SLinus Torvalds case IP_RECVTOS: 6371da177e4SLinus Torvalds if (val) 6381da177e4SLinus Torvalds inet->cmsg_flags |= IP_CMSG_TOS; 6391da177e4SLinus Torvalds else 6401da177e4SLinus Torvalds inet->cmsg_flags &= ~IP_CMSG_TOS; 6411da177e4SLinus Torvalds break; 6421da177e4SLinus Torvalds case IP_RECVOPTS: 6431da177e4SLinus Torvalds if (val) 6441da177e4SLinus Torvalds inet->cmsg_flags |= IP_CMSG_RECVOPTS; 6451da177e4SLinus Torvalds else 6461da177e4SLinus Torvalds inet->cmsg_flags &= ~IP_CMSG_RECVOPTS; 6471da177e4SLinus Torvalds break; 6481da177e4SLinus Torvalds case IP_RETOPTS: 6491da177e4SLinus Torvalds if (val) 6501da177e4SLinus Torvalds inet->cmsg_flags |= IP_CMSG_RETOPTS; 6511da177e4SLinus Torvalds else 6521da177e4SLinus Torvalds inet->cmsg_flags &= ~IP_CMSG_RETOPTS; 6531da177e4SLinus Torvalds break; 6542c7946a7SCatherine Zhang case IP_PASSSEC: 6552c7946a7SCatherine Zhang if (val) 6562c7946a7SCatherine Zhang inet->cmsg_flags |= IP_CMSG_PASSSEC; 6572c7946a7SCatherine Zhang else 6582c7946a7SCatherine Zhang inet->cmsg_flags &= ~IP_CMSG_PASSSEC; 6592c7946a7SCatherine Zhang break; 660e8b2dfe9SBalazs Scheidler case IP_RECVORIGDSTADDR: 661e8b2dfe9SBalazs Scheidler if (val) 662e8b2dfe9SBalazs Scheidler inet->cmsg_flags |= IP_CMSG_ORIGDSTADDR; 663e8b2dfe9SBalazs Scheidler else 664e8b2dfe9SBalazs Scheidler inet->cmsg_flags &= ~IP_CMSG_ORIGDSTADDR; 665e8b2dfe9SBalazs Scheidler break; 666ad6f939aSTom Herbert case IP_CHECKSUM: 667ad6f939aSTom Herbert if (val) { 668ad6f939aSTom Herbert if (!(inet->cmsg_flags & IP_CMSG_CHECKSUM)) { 669ad6f939aSTom Herbert inet_inc_convert_csum(sk); 670ad6f939aSTom Herbert inet->cmsg_flags |= IP_CMSG_CHECKSUM; 671ad6f939aSTom Herbert } 672ad6f939aSTom Herbert } else { 673ad6f939aSTom Herbert if (inet->cmsg_flags & IP_CMSG_CHECKSUM) { 674ad6f939aSTom Herbert inet_dec_convert_csum(sk); 675ad6f939aSTom Herbert inet->cmsg_flags &= ~IP_CMSG_CHECKSUM; 676ad6f939aSTom Herbert } 677ad6f939aSTom Herbert } 678ad6f939aSTom Herbert break; 6791da177e4SLinus Torvalds case IP_TOS: /* This sets both TOS and Precedence */ 6801da177e4SLinus Torvalds if (sk->sk_type == SOCK_STREAM) { 6812c67e9acSMaciej Żenczykowski val &= ~INET_ECN_MASK; 6822c67e9acSMaciej Żenczykowski val |= inet->tos & INET_ECN_MASK; 6831da177e4SLinus Torvalds } 6841da177e4SLinus Torvalds if (inet->tos != val) { 6851da177e4SLinus Torvalds inet->tos = val; 6861da177e4SLinus Torvalds sk->sk_priority = rt_tos2priority(val); 6871da177e4SLinus Torvalds sk_dst_reset(sk); 6881da177e4SLinus Torvalds } 6891da177e4SLinus Torvalds break; 6901da177e4SLinus Torvalds case IP_TTL: 6911da177e4SLinus Torvalds if (optlen < 1) 6921da177e4SLinus Torvalds goto e_inval; 693c9be4a5cSCong Wang if (val != -1 && (val < 1 || val > 255)) 6941da177e4SLinus Torvalds goto e_inval; 6951da177e4SLinus Torvalds inet->uc_ttl = val; 6961da177e4SLinus Torvalds break; 6971da177e4SLinus Torvalds case IP_HDRINCL: 6981da177e4SLinus Torvalds if (sk->sk_type != SOCK_RAW) { 6991da177e4SLinus Torvalds err = -ENOPROTOOPT; 7001da177e4SLinus Torvalds break; 7011da177e4SLinus Torvalds } 7021da177e4SLinus Torvalds inet->hdrincl = val ? 1 : 0; 7031da177e4SLinus Torvalds break; 7047b2ff18eSJiri Olsa case IP_NODEFRAG: 7057b2ff18eSJiri Olsa if (sk->sk_type != SOCK_RAW) { 7067b2ff18eSJiri Olsa err = -ENOPROTOOPT; 7077b2ff18eSJiri Olsa break; 7087b2ff18eSJiri Olsa } 7097b2ff18eSJiri Olsa inet->nodefrag = val ? 1 : 0; 7107b2ff18eSJiri Olsa break; 7111da177e4SLinus Torvalds case IP_MTU_DISCOVER: 7121b346576SHannes Frederic Sowa if (val < IP_PMTUDISC_DONT || val > IP_PMTUDISC_OMIT) 7131da177e4SLinus Torvalds goto e_inval; 7141da177e4SLinus Torvalds inet->pmtudisc = val; 7151da177e4SLinus Torvalds break; 7161da177e4SLinus Torvalds case IP_RECVERR: 7171da177e4SLinus Torvalds inet->recverr = !!val; 7181da177e4SLinus Torvalds if (!val) 7191da177e4SLinus Torvalds skb_queue_purge(&sk->sk_error_queue); 7201da177e4SLinus Torvalds break; 7211da177e4SLinus Torvalds case IP_MULTICAST_TTL: 7221da177e4SLinus Torvalds if (sk->sk_type == SOCK_STREAM) 7231da177e4SLinus Torvalds goto e_inval; 7241da177e4SLinus Torvalds if (optlen < 1) 7251da177e4SLinus Torvalds goto e_inval; 7261da177e4SLinus Torvalds if (val == -1) 7271da177e4SLinus Torvalds val = 1; 7281da177e4SLinus Torvalds if (val < 0 || val > 255) 7291da177e4SLinus Torvalds goto e_inval; 7301da177e4SLinus Torvalds inet->mc_ttl = val; 7311da177e4SLinus Torvalds break; 7321da177e4SLinus Torvalds case IP_MULTICAST_LOOP: 7331da177e4SLinus Torvalds if (optlen < 1) 7341da177e4SLinus Torvalds goto e_inval; 7351da177e4SLinus Torvalds inet->mc_loop = !!val; 7361da177e4SLinus Torvalds break; 73776e21053SErich E. Hoover case IP_UNICAST_IF: 73876e21053SErich E. Hoover { 73976e21053SErich E. Hoover struct net_device *dev = NULL; 74076e21053SErich E. Hoover int ifindex; 74176e21053SErich E. Hoover 74276e21053SErich E. Hoover if (optlen != sizeof(int)) 74376e21053SErich E. Hoover goto e_inval; 74476e21053SErich E. Hoover 74576e21053SErich E. Hoover ifindex = (__force int)ntohl((__force __be32)val); 74676e21053SErich E. Hoover if (ifindex == 0) { 74776e21053SErich E. Hoover inet->uc_index = 0; 74876e21053SErich E. Hoover err = 0; 74976e21053SErich E. Hoover break; 75076e21053SErich E. Hoover } 75176e21053SErich E. Hoover 75276e21053SErich E. Hoover dev = dev_get_by_index(sock_net(sk), ifindex); 75376e21053SErich E. Hoover err = -EADDRNOTAVAIL; 75476e21053SErich E. Hoover if (!dev) 75576e21053SErich E. Hoover break; 75676e21053SErich E. Hoover dev_put(dev); 75776e21053SErich E. Hoover 75876e21053SErich E. Hoover err = -EINVAL; 75976e21053SErich E. Hoover if (sk->sk_bound_dev_if) 76076e21053SErich E. Hoover break; 76176e21053SErich E. Hoover 76276e21053SErich E. Hoover inet->uc_index = ifindex; 76376e21053SErich E. Hoover err = 0; 76476e21053SErich E. Hoover break; 76576e21053SErich E. Hoover } 7661da177e4SLinus Torvalds case IP_MULTICAST_IF: 7671da177e4SLinus Torvalds { 7681da177e4SLinus Torvalds struct ip_mreqn mreq; 7691da177e4SLinus Torvalds struct net_device *dev = NULL; 7701da177e4SLinus Torvalds 7711da177e4SLinus Torvalds if (sk->sk_type == SOCK_STREAM) 7721da177e4SLinus Torvalds goto e_inval; 7731da177e4SLinus Torvalds /* 7741da177e4SLinus Torvalds * Check the arguments are allowable 7751da177e4SLinus Torvalds */ 7761da177e4SLinus Torvalds 7770915921bSShan Wei if (optlen < sizeof(struct in_addr)) 7780915921bSShan Wei goto e_inval; 7790915921bSShan Wei 7801da177e4SLinus Torvalds err = -EFAULT; 7811da177e4SLinus Torvalds if (optlen >= sizeof(struct ip_mreqn)) { 7821da177e4SLinus Torvalds if (copy_from_user(&mreq, optval, sizeof(mreq))) 7831da177e4SLinus Torvalds break; 7841da177e4SLinus Torvalds } else { 7851da177e4SLinus Torvalds memset(&mreq, 0, sizeof(mreq)); 7863a084ddbSJiri Pirko if (optlen >= sizeof(struct ip_mreq)) { 7873a084ddbSJiri Pirko if (copy_from_user(&mreq, optval, 7883a084ddbSJiri Pirko sizeof(struct ip_mreq))) 7893a084ddbSJiri Pirko break; 7903a084ddbSJiri Pirko } else if (optlen >= sizeof(struct in_addr)) { 7913a084ddbSJiri Pirko if (copy_from_user(&mreq.imr_address, optval, 7924d52cfbeSEric Dumazet sizeof(struct in_addr))) 7931da177e4SLinus Torvalds break; 7941da177e4SLinus Torvalds } 7953a084ddbSJiri Pirko } 7961da177e4SLinus Torvalds 7971da177e4SLinus Torvalds if (!mreq.imr_ifindex) { 798e6f1cebfSAl Viro if (mreq.imr_address.s_addr == htonl(INADDR_ANY)) { 7991da177e4SLinus Torvalds inet->mc_index = 0; 8001da177e4SLinus Torvalds inet->mc_addr = 0; 8011da177e4SLinus Torvalds err = 0; 8021da177e4SLinus Torvalds break; 8031da177e4SLinus Torvalds } 8043b1e0a65SYOSHIFUJI Hideaki dev = ip_dev_find(sock_net(sk), mreq.imr_address.s_addr); 80555b80503SEric Dumazet if (dev) 8061da177e4SLinus Torvalds mreq.imr_ifindex = dev->ifindex; 8071da177e4SLinus Torvalds } else 80855b80503SEric Dumazet dev = dev_get_by_index(sock_net(sk), mreq.imr_ifindex); 8091da177e4SLinus Torvalds 8101da177e4SLinus Torvalds 8111da177e4SLinus Torvalds err = -EADDRNOTAVAIL; 8121da177e4SLinus Torvalds if (!dev) 8131da177e4SLinus Torvalds break; 81455b80503SEric Dumazet dev_put(dev); 8151da177e4SLinus Torvalds 8161da177e4SLinus Torvalds err = -EINVAL; 8171da177e4SLinus Torvalds if (sk->sk_bound_dev_if && 8181da177e4SLinus Torvalds mreq.imr_ifindex != sk->sk_bound_dev_if) 8191da177e4SLinus Torvalds break; 8201da177e4SLinus Torvalds 8211da177e4SLinus Torvalds inet->mc_index = mreq.imr_ifindex; 8221da177e4SLinus Torvalds inet->mc_addr = mreq.imr_address.s_addr; 8231da177e4SLinus Torvalds err = 0; 8241da177e4SLinus Torvalds break; 8251da177e4SLinus Torvalds } 8261da177e4SLinus Torvalds 8271da177e4SLinus Torvalds case IP_ADD_MEMBERSHIP: 8281da177e4SLinus Torvalds case IP_DROP_MEMBERSHIP: 8291da177e4SLinus Torvalds { 8301da177e4SLinus Torvalds struct ip_mreqn mreq; 8311da177e4SLinus Torvalds 832a96fb49bSFlavio Leitner err = -EPROTO; 833a96fb49bSFlavio Leitner if (inet_sk(sk)->is_icsk) 834a96fb49bSFlavio Leitner break; 835a96fb49bSFlavio Leitner 8361da177e4SLinus Torvalds if (optlen < sizeof(struct ip_mreq)) 8371da177e4SLinus Torvalds goto e_inval; 8381da177e4SLinus Torvalds err = -EFAULT; 8391da177e4SLinus Torvalds if (optlen >= sizeof(struct ip_mreqn)) { 8401da177e4SLinus Torvalds if (copy_from_user(&mreq, optval, sizeof(mreq))) 8411da177e4SLinus Torvalds break; 8421da177e4SLinus Torvalds } else { 8431da177e4SLinus Torvalds memset(&mreq, 0, sizeof(mreq)); 8441da177e4SLinus Torvalds if (copy_from_user(&mreq, optval, sizeof(struct ip_mreq))) 8451da177e4SLinus Torvalds break; 8461da177e4SLinus Torvalds } 8471da177e4SLinus Torvalds 8481da177e4SLinus Torvalds if (optname == IP_ADD_MEMBERSHIP) 8491da177e4SLinus Torvalds err = ip_mc_join_group(sk, &mreq); 8501da177e4SLinus Torvalds else 8511da177e4SLinus Torvalds err = ip_mc_leave_group(sk, &mreq); 8521da177e4SLinus Torvalds break; 8531da177e4SLinus Torvalds } 8541da177e4SLinus Torvalds case IP_MSFILTER: 8551da177e4SLinus Torvalds { 8561da177e4SLinus Torvalds struct ip_msfilter *msf; 8571da177e4SLinus Torvalds 8581da177e4SLinus Torvalds if (optlen < IP_MSFILTER_SIZE(0)) 8591da177e4SLinus Torvalds goto e_inval; 8601da177e4SLinus Torvalds if (optlen > sysctl_optmem_max) { 8611da177e4SLinus Torvalds err = -ENOBUFS; 8621da177e4SLinus Torvalds break; 8631da177e4SLinus Torvalds } 8648b3a7005SKris Katterjohn msf = kmalloc(optlen, GFP_KERNEL); 865cfcabdccSStephen Hemminger if (!msf) { 8661da177e4SLinus Torvalds err = -ENOBUFS; 8671da177e4SLinus Torvalds break; 8681da177e4SLinus Torvalds } 8691da177e4SLinus Torvalds err = -EFAULT; 8701da177e4SLinus Torvalds if (copy_from_user(msf, optval, optlen)) { 8711da177e4SLinus Torvalds kfree(msf); 8721da177e4SLinus Torvalds break; 8731da177e4SLinus Torvalds } 8741da177e4SLinus Torvalds /* numsrc >= (1G-4) overflow in 32 bits */ 8751da177e4SLinus Torvalds if (msf->imsf_numsrc >= 0x3ffffffcU || 8761da177e4SLinus Torvalds msf->imsf_numsrc > sysctl_igmp_max_msf) { 8771da177e4SLinus Torvalds kfree(msf); 8781da177e4SLinus Torvalds err = -ENOBUFS; 8791da177e4SLinus Torvalds break; 8801da177e4SLinus Torvalds } 8811da177e4SLinus Torvalds if (IP_MSFILTER_SIZE(msf->imsf_numsrc) > optlen) { 8821da177e4SLinus Torvalds kfree(msf); 8831da177e4SLinus Torvalds err = -EINVAL; 8841da177e4SLinus Torvalds break; 8851da177e4SLinus Torvalds } 8861da177e4SLinus Torvalds err = ip_mc_msfilter(sk, msf, 0); 8871da177e4SLinus Torvalds kfree(msf); 8881da177e4SLinus Torvalds break; 8891da177e4SLinus Torvalds } 8901da177e4SLinus Torvalds case IP_BLOCK_SOURCE: 8911da177e4SLinus Torvalds case IP_UNBLOCK_SOURCE: 8921da177e4SLinus Torvalds case IP_ADD_SOURCE_MEMBERSHIP: 8931da177e4SLinus Torvalds case IP_DROP_SOURCE_MEMBERSHIP: 8941da177e4SLinus Torvalds { 8951da177e4SLinus Torvalds struct ip_mreq_source mreqs; 8961da177e4SLinus Torvalds int omode, add; 8971da177e4SLinus Torvalds 8981da177e4SLinus Torvalds if (optlen != sizeof(struct ip_mreq_source)) 8991da177e4SLinus Torvalds goto e_inval; 9001da177e4SLinus Torvalds if (copy_from_user(&mreqs, optval, sizeof(mreqs))) { 9011da177e4SLinus Torvalds err = -EFAULT; 9021da177e4SLinus Torvalds break; 9031da177e4SLinus Torvalds } 9041da177e4SLinus Torvalds if (optname == IP_BLOCK_SOURCE) { 9051da177e4SLinus Torvalds omode = MCAST_EXCLUDE; 9061da177e4SLinus Torvalds add = 1; 9071da177e4SLinus Torvalds } else if (optname == IP_UNBLOCK_SOURCE) { 9081da177e4SLinus Torvalds omode = MCAST_EXCLUDE; 9091da177e4SLinus Torvalds add = 0; 9101da177e4SLinus Torvalds } else if (optname == IP_ADD_SOURCE_MEMBERSHIP) { 9111da177e4SLinus Torvalds struct ip_mreqn mreq; 9121da177e4SLinus Torvalds 9131da177e4SLinus Torvalds mreq.imr_multiaddr.s_addr = mreqs.imr_multiaddr; 9141da177e4SLinus Torvalds mreq.imr_address.s_addr = mreqs.imr_interface; 9151da177e4SLinus Torvalds mreq.imr_ifindex = 0; 9161da177e4SLinus Torvalds err = ip_mc_join_group(sk, &mreq); 9178cdaaa15SDavid L Stevens if (err && err != -EADDRINUSE) 9181da177e4SLinus Torvalds break; 9191da177e4SLinus Torvalds omode = MCAST_INCLUDE; 9201da177e4SLinus Torvalds add = 1; 9211da177e4SLinus Torvalds } else /* IP_DROP_SOURCE_MEMBERSHIP */ { 9221da177e4SLinus Torvalds omode = MCAST_INCLUDE; 9231da177e4SLinus Torvalds add = 0; 9241da177e4SLinus Torvalds } 9251da177e4SLinus Torvalds err = ip_mc_source(add, omode, sk, &mreqs, 0); 9261da177e4SLinus Torvalds break; 9271da177e4SLinus Torvalds } 9281da177e4SLinus Torvalds case MCAST_JOIN_GROUP: 9291da177e4SLinus Torvalds case MCAST_LEAVE_GROUP: 9301da177e4SLinus Torvalds { 9311da177e4SLinus Torvalds struct group_req greq; 9321da177e4SLinus Torvalds struct sockaddr_in *psin; 9331da177e4SLinus Torvalds struct ip_mreqn mreq; 9341da177e4SLinus Torvalds 9351da177e4SLinus Torvalds if (optlen < sizeof(struct group_req)) 9361da177e4SLinus Torvalds goto e_inval; 9371da177e4SLinus Torvalds err = -EFAULT; 9381da177e4SLinus Torvalds if (copy_from_user(&greq, optval, sizeof(greq))) 9391da177e4SLinus Torvalds break; 9401da177e4SLinus Torvalds psin = (struct sockaddr_in *)&greq.gr_group; 9411da177e4SLinus Torvalds if (psin->sin_family != AF_INET) 9421da177e4SLinus Torvalds goto e_inval; 9431da177e4SLinus Torvalds memset(&mreq, 0, sizeof(mreq)); 9441da177e4SLinus Torvalds mreq.imr_multiaddr = psin->sin_addr; 9451da177e4SLinus Torvalds mreq.imr_ifindex = greq.gr_interface; 9461da177e4SLinus Torvalds 9471da177e4SLinus Torvalds if (optname == MCAST_JOIN_GROUP) 9481da177e4SLinus Torvalds err = ip_mc_join_group(sk, &mreq); 9491da177e4SLinus Torvalds else 9501da177e4SLinus Torvalds err = ip_mc_leave_group(sk, &mreq); 9511da177e4SLinus Torvalds break; 9521da177e4SLinus Torvalds } 9531da177e4SLinus Torvalds case MCAST_JOIN_SOURCE_GROUP: 9541da177e4SLinus Torvalds case MCAST_LEAVE_SOURCE_GROUP: 9551da177e4SLinus Torvalds case MCAST_BLOCK_SOURCE: 9561da177e4SLinus Torvalds case MCAST_UNBLOCK_SOURCE: 9571da177e4SLinus Torvalds { 9581da177e4SLinus Torvalds struct group_source_req greqs; 9591da177e4SLinus Torvalds struct ip_mreq_source mreqs; 9601da177e4SLinus Torvalds struct sockaddr_in *psin; 9611da177e4SLinus Torvalds int omode, add; 9621da177e4SLinus Torvalds 9631da177e4SLinus Torvalds if (optlen != sizeof(struct group_source_req)) 9641da177e4SLinus Torvalds goto e_inval; 9651da177e4SLinus Torvalds if (copy_from_user(&greqs, optval, sizeof(greqs))) { 9661da177e4SLinus Torvalds err = -EFAULT; 9671da177e4SLinus Torvalds break; 9681da177e4SLinus Torvalds } 9691da177e4SLinus Torvalds if (greqs.gsr_group.ss_family != AF_INET || 9701da177e4SLinus Torvalds greqs.gsr_source.ss_family != AF_INET) { 9711da177e4SLinus Torvalds err = -EADDRNOTAVAIL; 9721da177e4SLinus Torvalds break; 9731da177e4SLinus Torvalds } 9741da177e4SLinus Torvalds psin = (struct sockaddr_in *)&greqs.gsr_group; 9751da177e4SLinus Torvalds mreqs.imr_multiaddr = psin->sin_addr.s_addr; 9761da177e4SLinus Torvalds psin = (struct sockaddr_in *)&greqs.gsr_source; 9771da177e4SLinus Torvalds mreqs.imr_sourceaddr = psin->sin_addr.s_addr; 9781da177e4SLinus Torvalds mreqs.imr_interface = 0; /* use index for mc_source */ 9791da177e4SLinus Torvalds 9801da177e4SLinus Torvalds if (optname == MCAST_BLOCK_SOURCE) { 9811da177e4SLinus Torvalds omode = MCAST_EXCLUDE; 9821da177e4SLinus Torvalds add = 1; 9831da177e4SLinus Torvalds } else if (optname == MCAST_UNBLOCK_SOURCE) { 9841da177e4SLinus Torvalds omode = MCAST_EXCLUDE; 9851da177e4SLinus Torvalds add = 0; 9861da177e4SLinus Torvalds } else if (optname == MCAST_JOIN_SOURCE_GROUP) { 9871da177e4SLinus Torvalds struct ip_mreqn mreq; 9881da177e4SLinus Torvalds 9891da177e4SLinus Torvalds psin = (struct sockaddr_in *)&greqs.gsr_group; 9901da177e4SLinus Torvalds mreq.imr_multiaddr = psin->sin_addr; 9911da177e4SLinus Torvalds mreq.imr_address.s_addr = 0; 9921da177e4SLinus Torvalds mreq.imr_ifindex = greqs.gsr_interface; 9931da177e4SLinus Torvalds err = ip_mc_join_group(sk, &mreq); 9948cdaaa15SDavid L Stevens if (err && err != -EADDRINUSE) 9951da177e4SLinus Torvalds break; 9961da177e4SLinus Torvalds greqs.gsr_interface = mreq.imr_ifindex; 9971da177e4SLinus Torvalds omode = MCAST_INCLUDE; 9981da177e4SLinus Torvalds add = 1; 9991da177e4SLinus Torvalds } else /* MCAST_LEAVE_SOURCE_GROUP */ { 10001da177e4SLinus Torvalds omode = MCAST_INCLUDE; 10011da177e4SLinus Torvalds add = 0; 10021da177e4SLinus Torvalds } 10031da177e4SLinus Torvalds err = ip_mc_source(add, omode, sk, &mreqs, 10041da177e4SLinus Torvalds greqs.gsr_interface); 10051da177e4SLinus Torvalds break; 10061da177e4SLinus Torvalds } 10071da177e4SLinus Torvalds case MCAST_MSFILTER: 10081da177e4SLinus Torvalds { 10091da177e4SLinus Torvalds struct sockaddr_in *psin; 10101da177e4SLinus Torvalds struct ip_msfilter *msf = NULL; 10111da177e4SLinus Torvalds struct group_filter *gsf = NULL; 10121da177e4SLinus Torvalds int msize, i, ifindex; 10131da177e4SLinus Torvalds 10141da177e4SLinus Torvalds if (optlen < GROUP_FILTER_SIZE(0)) 10151da177e4SLinus Torvalds goto e_inval; 10161da177e4SLinus Torvalds if (optlen > sysctl_optmem_max) { 10171da177e4SLinus Torvalds err = -ENOBUFS; 10181da177e4SLinus Torvalds break; 10191da177e4SLinus Torvalds } 10208b3a7005SKris Katterjohn gsf = kmalloc(optlen, GFP_KERNEL); 1021cfcabdccSStephen Hemminger if (!gsf) { 10221da177e4SLinus Torvalds err = -ENOBUFS; 10231da177e4SLinus Torvalds break; 10241da177e4SLinus Torvalds } 10251da177e4SLinus Torvalds err = -EFAULT; 10264d52cfbeSEric Dumazet if (copy_from_user(gsf, optval, optlen)) 10271da177e4SLinus Torvalds goto mc_msf_out; 10284d52cfbeSEric Dumazet 10291da177e4SLinus Torvalds /* numsrc >= (4G-140)/128 overflow in 32 bits */ 10301da177e4SLinus Torvalds if (gsf->gf_numsrc >= 0x1ffffff || 10311da177e4SLinus Torvalds gsf->gf_numsrc > sysctl_igmp_max_msf) { 10321da177e4SLinus Torvalds err = -ENOBUFS; 10331da177e4SLinus Torvalds goto mc_msf_out; 10341da177e4SLinus Torvalds } 10351da177e4SLinus Torvalds if (GROUP_FILTER_SIZE(gsf->gf_numsrc) > optlen) { 10361da177e4SLinus Torvalds err = -EINVAL; 10371da177e4SLinus Torvalds goto mc_msf_out; 10381da177e4SLinus Torvalds } 10391da177e4SLinus Torvalds msize = IP_MSFILTER_SIZE(gsf->gf_numsrc); 10408b3a7005SKris Katterjohn msf = kmalloc(msize, GFP_KERNEL); 1041cfcabdccSStephen Hemminger if (!msf) { 10421da177e4SLinus Torvalds err = -ENOBUFS; 10431da177e4SLinus Torvalds goto mc_msf_out; 10441da177e4SLinus Torvalds } 10451da177e4SLinus Torvalds ifindex = gsf->gf_interface; 10461da177e4SLinus Torvalds psin = (struct sockaddr_in *)&gsf->gf_group; 10471da177e4SLinus Torvalds if (psin->sin_family != AF_INET) { 10481da177e4SLinus Torvalds err = -EADDRNOTAVAIL; 10491da177e4SLinus Torvalds goto mc_msf_out; 10501da177e4SLinus Torvalds } 10511da177e4SLinus Torvalds msf->imsf_multiaddr = psin->sin_addr.s_addr; 10521da177e4SLinus Torvalds msf->imsf_interface = 0; 10531da177e4SLinus Torvalds msf->imsf_fmode = gsf->gf_fmode; 10541da177e4SLinus Torvalds msf->imsf_numsrc = gsf->gf_numsrc; 10551da177e4SLinus Torvalds err = -EADDRNOTAVAIL; 10561da177e4SLinus Torvalds for (i = 0; i < gsf->gf_numsrc; ++i) { 10571da177e4SLinus Torvalds psin = (struct sockaddr_in *)&gsf->gf_slist[i]; 10581da177e4SLinus Torvalds 10591da177e4SLinus Torvalds if (psin->sin_family != AF_INET) 10601da177e4SLinus Torvalds goto mc_msf_out; 10611da177e4SLinus Torvalds msf->imsf_slist[i] = psin->sin_addr.s_addr; 10621da177e4SLinus Torvalds } 10631da177e4SLinus Torvalds kfree(gsf); 10641da177e4SLinus Torvalds gsf = NULL; 10651da177e4SLinus Torvalds 10661da177e4SLinus Torvalds err = ip_mc_msfilter(sk, msf, ifindex); 10671da177e4SLinus Torvalds mc_msf_out: 10681da177e4SLinus Torvalds kfree(msf); 10691da177e4SLinus Torvalds kfree(gsf); 10701da177e4SLinus Torvalds break; 10711da177e4SLinus Torvalds } 1072f771bef9SNivedita Singhvi case IP_MULTICAST_ALL: 1073f771bef9SNivedita Singhvi if (optlen < 1) 1074f771bef9SNivedita Singhvi goto e_inval; 1075f771bef9SNivedita Singhvi if (val != 0 && val != 1) 1076f771bef9SNivedita Singhvi goto e_inval; 1077f771bef9SNivedita Singhvi inet->mc_all = val; 1078f771bef9SNivedita Singhvi break; 10791da177e4SLinus Torvalds case IP_ROUTER_ALERT: 10801da177e4SLinus Torvalds err = ip_ra_control(sk, val ? 1 : 0, NULL); 10811da177e4SLinus Torvalds break; 10821da177e4SLinus Torvalds 10831da177e4SLinus Torvalds case IP_FREEBIND: 10841da177e4SLinus Torvalds if (optlen < 1) 10851da177e4SLinus Torvalds goto e_inval; 10861da177e4SLinus Torvalds inet->freebind = !!val; 10871da177e4SLinus Torvalds break; 10881da177e4SLinus Torvalds 10891da177e4SLinus Torvalds case IP_IPSEC_POLICY: 10901da177e4SLinus Torvalds case IP_XFRM_POLICY: 10916fc0b4a7SHerbert Xu err = -EPERM; 109252e804c6SEric W. Biederman if (!ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN)) 10936fc0b4a7SHerbert Xu break; 10941da177e4SLinus Torvalds err = xfrm_user_policy(sk, optname, optval, optlen); 10951da177e4SLinus Torvalds break; 10961da177e4SLinus Torvalds 1097f5715aeaSKOVACS Krisztian case IP_TRANSPARENT: 109852e804c6SEric W. Biederman if (!!val && !ns_capable(sock_net(sk)->user_ns, CAP_NET_RAW) && 109952e804c6SEric W. Biederman !ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN)) { 1100f5715aeaSKOVACS Krisztian err = -EPERM; 1101f5715aeaSKOVACS Krisztian break; 1102f5715aeaSKOVACS Krisztian } 1103f5715aeaSKOVACS Krisztian if (optlen < 1) 1104f5715aeaSKOVACS Krisztian goto e_inval; 1105f5715aeaSKOVACS Krisztian inet->transparent = !!val; 1106f5715aeaSKOVACS Krisztian break; 1107f5715aeaSKOVACS Krisztian 1108d218d111SStephen Hemminger case IP_MINTTL: 1109d218d111SStephen Hemminger if (optlen < 1) 1110d218d111SStephen Hemminger goto e_inval; 1111d218d111SStephen Hemminger if (val < 0 || val > 255) 1112d218d111SStephen Hemminger goto e_inval; 1113d218d111SStephen Hemminger inet->min_ttl = val; 1114d218d111SStephen Hemminger break; 1115d218d111SStephen Hemminger 11161da177e4SLinus Torvalds default: 11171da177e4SLinus Torvalds err = -ENOPROTOOPT; 11181da177e4SLinus Torvalds break; 11191da177e4SLinus Torvalds } 11201da177e4SLinus Torvalds release_sock(sk); 11211da177e4SLinus Torvalds return err; 11221da177e4SLinus Torvalds 11231da177e4SLinus Torvalds e_inval: 11241da177e4SLinus Torvalds release_sock(sk); 11251da177e4SLinus Torvalds return -EINVAL; 11261da177e4SLinus Torvalds } 11271da177e4SLinus Torvalds 1128f84af32cSEric Dumazet /** 1129829ae9d6SWillem de Bruijn * ipv4_pktinfo_prepare - transfer some info from rtable to skb 1130f84af32cSEric Dumazet * @sk: socket 1131f84af32cSEric Dumazet * @skb: buffer 1132f84af32cSEric Dumazet * 113335ebf65eSDavid S. Miller * To support IP_CMSG_PKTINFO option, we store rt_iif and specific 113435ebf65eSDavid S. Miller * destination in skb->cb[] before dst drop. 11358e3bff96Sstephen hemminger * This way, receiver doesn't make cache line misses to read rtable. 1136f84af32cSEric Dumazet */ 1137fbf8866dSShawn Bohrer void ipv4_pktinfo_prepare(const struct sock *sk, struct sk_buff *skb) 1138f84af32cSEric Dumazet { 1139d826eb14SEric Dumazet struct in_pktinfo *pktinfo = PKTINFO_SKB_CB(skb); 11404b261c75SHannes Frederic Sowa bool prepare = (inet_sk(sk)->cmsg_flags & IP_CMSG_PKTINFO) || 11414b261c75SHannes Frederic Sowa ipv6_sk_rxinfo(sk); 1142d826eb14SEric Dumazet 11434b261c75SHannes Frederic Sowa if (prepare && skb_rtable(skb)) { 114492101b3bSDavid S. Miller pktinfo->ipi_ifindex = inet_iif(skb); 114535ebf65eSDavid S. Miller pktinfo->ipi_spec_dst.s_addr = fib_compute_spec_dst(skb); 1146d826eb14SEric Dumazet } else { 1147d826eb14SEric Dumazet pktinfo->ipi_ifindex = 0; 1148d826eb14SEric Dumazet pktinfo->ipi_spec_dst.s_addr = 0; 1149f84af32cSEric Dumazet } 1150d826eb14SEric Dumazet skb_dst_drop(skb); 1151d826eb14SEric Dumazet } 1152f84af32cSEric Dumazet 11533fdadf7dSDmitry Mishin int ip_setsockopt(struct sock *sk, int level, 1154b7058842SDavid S. Miller int optname, char __user *optval, unsigned int optlen) 11553fdadf7dSDmitry Mishin { 11563fdadf7dSDmitry Mishin int err; 11573fdadf7dSDmitry Mishin 11583fdadf7dSDmitry Mishin if (level != SOL_IP) 11593fdadf7dSDmitry Mishin return -ENOPROTOOPT; 11603fdadf7dSDmitry Mishin 11613fdadf7dSDmitry Mishin err = do_ip_setsockopt(sk, level, optname, optval, optlen); 11623fdadf7dSDmitry Mishin #ifdef CONFIG_NETFILTER 11633fdadf7dSDmitry Mishin /* we need to exclude all possible ENOPROTOOPTs except default case */ 11643fdadf7dSDmitry Mishin if (err == -ENOPROTOOPT && optname != IP_HDRINCL && 11656a9fb947SPavel Emelyanov optname != IP_IPSEC_POLICY && 11666a9fb947SPavel Emelyanov optname != IP_XFRM_POLICY && 11676a9fb947SPavel Emelyanov !ip_mroute_opt(optname)) { 11683fdadf7dSDmitry Mishin lock_sock(sk); 11693fdadf7dSDmitry Mishin err = nf_setsockopt(sk, PF_INET, optname, optval, optlen); 11703fdadf7dSDmitry Mishin release_sock(sk); 11713fdadf7dSDmitry Mishin } 11723fdadf7dSDmitry Mishin #endif 11733fdadf7dSDmitry Mishin return err; 11743fdadf7dSDmitry Mishin } 11754d52cfbeSEric Dumazet EXPORT_SYMBOL(ip_setsockopt); 11763fdadf7dSDmitry Mishin 11773fdadf7dSDmitry Mishin #ifdef CONFIG_COMPAT 1178543d9cfeSArnaldo Carvalho de Melo int compat_ip_setsockopt(struct sock *sk, int level, int optname, 1179b7058842SDavid S. Miller char __user *optval, unsigned int optlen) 11803fdadf7dSDmitry Mishin { 11813fdadf7dSDmitry Mishin int err; 11823fdadf7dSDmitry Mishin 11833fdadf7dSDmitry Mishin if (level != SOL_IP) 11843fdadf7dSDmitry Mishin return -ENOPROTOOPT; 11853fdadf7dSDmitry Mishin 1186dae50295SDavid L Stevens if (optname >= MCAST_JOIN_GROUP && optname <= MCAST_MSFILTER) 1187dae50295SDavid L Stevens return compat_mc_setsockopt(sk, level, optname, optval, optlen, 1188dae50295SDavid L Stevens ip_setsockopt); 1189dae50295SDavid L Stevens 11903fdadf7dSDmitry Mishin err = do_ip_setsockopt(sk, level, optname, optval, optlen); 11913fdadf7dSDmitry Mishin #ifdef CONFIG_NETFILTER 11923fdadf7dSDmitry Mishin /* we need to exclude all possible ENOPROTOOPTs except default case */ 11933fdadf7dSDmitry Mishin if (err == -ENOPROTOOPT && optname != IP_HDRINCL && 11946a9fb947SPavel Emelyanov optname != IP_IPSEC_POLICY && 11956a9fb947SPavel Emelyanov optname != IP_XFRM_POLICY && 11966a9fb947SPavel Emelyanov !ip_mroute_opt(optname)) { 11973fdadf7dSDmitry Mishin lock_sock(sk); 1198543d9cfeSArnaldo Carvalho de Melo err = compat_nf_setsockopt(sk, PF_INET, optname, 1199543d9cfeSArnaldo Carvalho de Melo optval, optlen); 12003fdadf7dSDmitry Mishin release_sock(sk); 12013fdadf7dSDmitry Mishin } 12023fdadf7dSDmitry Mishin #endif 12033fdadf7dSDmitry Mishin return err; 12043fdadf7dSDmitry Mishin } 1205543d9cfeSArnaldo Carvalho de Melo EXPORT_SYMBOL(compat_ip_setsockopt); 12063fdadf7dSDmitry Mishin #endif 12073fdadf7dSDmitry Mishin 12081da177e4SLinus Torvalds /* 12094d52cfbeSEric Dumazet * Get the options. Note for future reference. The GET of IP options gets 12104d52cfbeSEric Dumazet * the _received_ ones. The set sets the _sent_ ones. 12111da177e4SLinus Torvalds */ 12121da177e4SLinus Torvalds 12133fdadf7dSDmitry Mishin static int do_ip_getsockopt(struct sock *sk, int level, int optname, 121495c96174SEric Dumazet char __user *optval, int __user *optlen, unsigned int flags) 12151da177e4SLinus Torvalds { 12161da177e4SLinus Torvalds struct inet_sock *inet = inet_sk(sk); 12171da177e4SLinus Torvalds int val; 12181da177e4SLinus Torvalds int len; 12191da177e4SLinus Torvalds 12201da177e4SLinus Torvalds if (level != SOL_IP) 12211da177e4SLinus Torvalds return -EOPNOTSUPP; 12221da177e4SLinus Torvalds 12236a9fb947SPavel Emelyanov if (ip_mroute_opt(optname)) 12241da177e4SLinus Torvalds return ip_mroute_getsockopt(sk, optname, optval, optlen); 12251da177e4SLinus Torvalds 12261da177e4SLinus Torvalds if (get_user(len, optlen)) 12271da177e4SLinus Torvalds return -EFAULT; 12281da177e4SLinus Torvalds if (len < 0) 12291da177e4SLinus Torvalds return -EINVAL; 12301da177e4SLinus Torvalds 12311da177e4SLinus Torvalds lock_sock(sk); 12321da177e4SLinus Torvalds 12331da177e4SLinus Torvalds switch (optname) { 12341da177e4SLinus Torvalds case IP_OPTIONS: 12351da177e4SLinus Torvalds { 12361da177e4SLinus Torvalds unsigned char optbuf[sizeof(struct ip_options)+40]; 12371da177e4SLinus Torvalds struct ip_options *opt = (struct ip_options *)optbuf; 1238f6d8bd05SEric Dumazet struct ip_options_rcu *inet_opt; 1239f6d8bd05SEric Dumazet 1240f6d8bd05SEric Dumazet inet_opt = rcu_dereference_protected(inet->inet_opt, 1241f6d8bd05SEric Dumazet sock_owned_by_user(sk)); 12421da177e4SLinus Torvalds opt->optlen = 0; 1243f6d8bd05SEric Dumazet if (inet_opt) 1244f6d8bd05SEric Dumazet memcpy(optbuf, &inet_opt->opt, 12451da177e4SLinus Torvalds sizeof(struct ip_options) + 1246f6d8bd05SEric Dumazet inet_opt->opt.optlen); 12471da177e4SLinus Torvalds release_sock(sk); 12481da177e4SLinus Torvalds 12491da177e4SLinus Torvalds if (opt->optlen == 0) 12501da177e4SLinus Torvalds return put_user(0, optlen); 12511da177e4SLinus Torvalds 12521da177e4SLinus Torvalds ip_options_undo(opt); 12531da177e4SLinus Torvalds 12541da177e4SLinus Torvalds len = min_t(unsigned int, len, opt->optlen); 12551da177e4SLinus Torvalds if (put_user(len, optlen)) 12561da177e4SLinus Torvalds return -EFAULT; 12571da177e4SLinus Torvalds if (copy_to_user(optval, opt->__data, len)) 12581da177e4SLinus Torvalds return -EFAULT; 12591da177e4SLinus Torvalds return 0; 12601da177e4SLinus Torvalds } 12611da177e4SLinus Torvalds case IP_PKTINFO: 12621da177e4SLinus Torvalds val = (inet->cmsg_flags & IP_CMSG_PKTINFO) != 0; 12631da177e4SLinus Torvalds break; 12641da177e4SLinus Torvalds case IP_RECVTTL: 12651da177e4SLinus Torvalds val = (inet->cmsg_flags & IP_CMSG_TTL) != 0; 12661da177e4SLinus Torvalds break; 12671da177e4SLinus Torvalds case IP_RECVTOS: 12681da177e4SLinus Torvalds val = (inet->cmsg_flags & IP_CMSG_TOS) != 0; 12691da177e4SLinus Torvalds break; 12701da177e4SLinus Torvalds case IP_RECVOPTS: 12711da177e4SLinus Torvalds val = (inet->cmsg_flags & IP_CMSG_RECVOPTS) != 0; 12721da177e4SLinus Torvalds break; 12731da177e4SLinus Torvalds case IP_RETOPTS: 12741da177e4SLinus Torvalds val = (inet->cmsg_flags & IP_CMSG_RETOPTS) != 0; 12751da177e4SLinus Torvalds break; 12762c7946a7SCatherine Zhang case IP_PASSSEC: 12772c7946a7SCatherine Zhang val = (inet->cmsg_flags & IP_CMSG_PASSSEC) != 0; 12782c7946a7SCatherine Zhang break; 1279e8b2dfe9SBalazs Scheidler case IP_RECVORIGDSTADDR: 1280e8b2dfe9SBalazs Scheidler val = (inet->cmsg_flags & IP_CMSG_ORIGDSTADDR) != 0; 1281e8b2dfe9SBalazs Scheidler break; 1282ad6f939aSTom Herbert case IP_CHECKSUM: 1283ad6f939aSTom Herbert val = (inet->cmsg_flags & IP_CMSG_CHECKSUM) != 0; 1284ad6f939aSTom Herbert break; 12851da177e4SLinus Torvalds case IP_TOS: 12861da177e4SLinus Torvalds val = inet->tos; 12871da177e4SLinus Torvalds break; 12881da177e4SLinus Torvalds case IP_TTL: 12891da177e4SLinus Torvalds val = (inet->uc_ttl == -1 ? 12901da177e4SLinus Torvalds sysctl_ip_default_ttl : 12911da177e4SLinus Torvalds inet->uc_ttl); 12921da177e4SLinus Torvalds break; 12931da177e4SLinus Torvalds case IP_HDRINCL: 12941da177e4SLinus Torvalds val = inet->hdrincl; 12951da177e4SLinus Torvalds break; 1296a89b4763SMichael Kerrisk case IP_NODEFRAG: 1297a89b4763SMichael Kerrisk val = inet->nodefrag; 1298a89b4763SMichael Kerrisk break; 12991da177e4SLinus Torvalds case IP_MTU_DISCOVER: 13001da177e4SLinus Torvalds val = inet->pmtudisc; 13011da177e4SLinus Torvalds break; 13021da177e4SLinus Torvalds case IP_MTU: 13031da177e4SLinus Torvalds { 13041da177e4SLinus Torvalds struct dst_entry *dst; 13051da177e4SLinus Torvalds val = 0; 13061da177e4SLinus Torvalds dst = sk_dst_get(sk); 13071da177e4SLinus Torvalds if (dst) { 13081da177e4SLinus Torvalds val = dst_mtu(dst); 13091da177e4SLinus Torvalds dst_release(dst); 13101da177e4SLinus Torvalds } 13111da177e4SLinus Torvalds if (!val) { 13121da177e4SLinus Torvalds release_sock(sk); 13131da177e4SLinus Torvalds return -ENOTCONN; 13141da177e4SLinus Torvalds } 13151da177e4SLinus Torvalds break; 13161da177e4SLinus Torvalds } 13171da177e4SLinus Torvalds case IP_RECVERR: 13181da177e4SLinus Torvalds val = inet->recverr; 13191da177e4SLinus Torvalds break; 13201da177e4SLinus Torvalds case IP_MULTICAST_TTL: 13211da177e4SLinus Torvalds val = inet->mc_ttl; 13221da177e4SLinus Torvalds break; 13231da177e4SLinus Torvalds case IP_MULTICAST_LOOP: 13241da177e4SLinus Torvalds val = inet->mc_loop; 13251da177e4SLinus Torvalds break; 132676e21053SErich E. Hoover case IP_UNICAST_IF: 132776e21053SErich E. Hoover val = (__force int)htonl((__u32) inet->uc_index); 132876e21053SErich E. Hoover break; 13291da177e4SLinus Torvalds case IP_MULTICAST_IF: 13301da177e4SLinus Torvalds { 13311da177e4SLinus Torvalds struct in_addr addr; 13321da177e4SLinus Torvalds len = min_t(unsigned int, len, sizeof(struct in_addr)); 13331da177e4SLinus Torvalds addr.s_addr = inet->mc_addr; 13341da177e4SLinus Torvalds release_sock(sk); 13351da177e4SLinus Torvalds 13361da177e4SLinus Torvalds if (put_user(len, optlen)) 13371da177e4SLinus Torvalds return -EFAULT; 13381da177e4SLinus Torvalds if (copy_to_user(optval, &addr, len)) 13391da177e4SLinus Torvalds return -EFAULT; 13401da177e4SLinus Torvalds return 0; 13411da177e4SLinus Torvalds } 13421da177e4SLinus Torvalds case IP_MSFILTER: 13431da177e4SLinus Torvalds { 13441da177e4SLinus Torvalds struct ip_msfilter msf; 13451da177e4SLinus Torvalds int err; 13461da177e4SLinus Torvalds 13471da177e4SLinus Torvalds if (len < IP_MSFILTER_SIZE(0)) { 13481da177e4SLinus Torvalds release_sock(sk); 13491da177e4SLinus Torvalds return -EINVAL; 13501da177e4SLinus Torvalds } 13511da177e4SLinus Torvalds if (copy_from_user(&msf, optval, IP_MSFILTER_SIZE(0))) { 13521da177e4SLinus Torvalds release_sock(sk); 13531da177e4SLinus Torvalds return -EFAULT; 13541da177e4SLinus Torvalds } 13551da177e4SLinus Torvalds err = ip_mc_msfget(sk, &msf, 13561da177e4SLinus Torvalds (struct ip_msfilter __user *)optval, optlen); 13571da177e4SLinus Torvalds release_sock(sk); 13581da177e4SLinus Torvalds return err; 13591da177e4SLinus Torvalds } 13601da177e4SLinus Torvalds case MCAST_MSFILTER: 13611da177e4SLinus Torvalds { 13621da177e4SLinus Torvalds struct group_filter gsf; 13631da177e4SLinus Torvalds int err; 13641da177e4SLinus Torvalds 13651da177e4SLinus Torvalds if (len < GROUP_FILTER_SIZE(0)) { 13661da177e4SLinus Torvalds release_sock(sk); 13671da177e4SLinus Torvalds return -EINVAL; 13681da177e4SLinus Torvalds } 13691da177e4SLinus Torvalds if (copy_from_user(&gsf, optval, GROUP_FILTER_SIZE(0))) { 13701da177e4SLinus Torvalds release_sock(sk); 13711da177e4SLinus Torvalds return -EFAULT; 13721da177e4SLinus Torvalds } 13731da177e4SLinus Torvalds err = ip_mc_gsfget(sk, &gsf, 13744d52cfbeSEric Dumazet (struct group_filter __user *)optval, 13754d52cfbeSEric Dumazet optlen); 13761da177e4SLinus Torvalds release_sock(sk); 13771da177e4SLinus Torvalds return err; 13781da177e4SLinus Torvalds } 1379f771bef9SNivedita Singhvi case IP_MULTICAST_ALL: 1380f771bef9SNivedita Singhvi val = inet->mc_all; 1381f771bef9SNivedita Singhvi break; 13821da177e4SLinus Torvalds case IP_PKTOPTIONS: 13831da177e4SLinus Torvalds { 13841da177e4SLinus Torvalds struct msghdr msg; 13851da177e4SLinus Torvalds 13861da177e4SLinus Torvalds release_sock(sk); 13871da177e4SLinus Torvalds 13881da177e4SLinus Torvalds if (sk->sk_type != SOCK_STREAM) 13891da177e4SLinus Torvalds return -ENOPROTOOPT; 13901da177e4SLinus Torvalds 1391c54a5e02SKaroly Kemeny msg.msg_control = (__force void *) optval; 13921da177e4SLinus Torvalds msg.msg_controllen = len; 1393dd23198eSDaniel Baluta msg.msg_flags = flags; 13941da177e4SLinus Torvalds 13951da177e4SLinus Torvalds if (inet->cmsg_flags & IP_CMSG_PKTINFO) { 13961da177e4SLinus Torvalds struct in_pktinfo info; 13971da177e4SLinus Torvalds 1398c720c7e8SEric Dumazet info.ipi_addr.s_addr = inet->inet_rcv_saddr; 1399c720c7e8SEric Dumazet info.ipi_spec_dst.s_addr = inet->inet_rcv_saddr; 14001da177e4SLinus Torvalds info.ipi_ifindex = inet->mc_index; 14011da177e4SLinus Torvalds put_cmsg(&msg, SOL_IP, IP_PKTINFO, sizeof(info), &info); 14021da177e4SLinus Torvalds } 14031da177e4SLinus Torvalds if (inet->cmsg_flags & IP_CMSG_TTL) { 14041da177e4SLinus Torvalds int hlim = inet->mc_ttl; 14051da177e4SLinus Torvalds put_cmsg(&msg, SOL_IP, IP_TTL, sizeof(hlim), &hlim); 14061da177e4SLinus Torvalds } 14074c507d28SJiri Benc if (inet->cmsg_flags & IP_CMSG_TOS) { 14084c507d28SJiri Benc int tos = inet->rcv_tos; 14094c507d28SJiri Benc put_cmsg(&msg, SOL_IP, IP_TOS, sizeof(tos), &tos); 14104c507d28SJiri Benc } 14111da177e4SLinus Torvalds len -= msg.msg_controllen; 14121da177e4SLinus Torvalds return put_user(len, optlen); 14131da177e4SLinus Torvalds } 14141da177e4SLinus Torvalds case IP_FREEBIND: 14151da177e4SLinus Torvalds val = inet->freebind; 14161da177e4SLinus Torvalds break; 1417f5715aeaSKOVACS Krisztian case IP_TRANSPARENT: 1418f5715aeaSKOVACS Krisztian val = inet->transparent; 1419f5715aeaSKOVACS Krisztian break; 1420d218d111SStephen Hemminger case IP_MINTTL: 1421d218d111SStephen Hemminger val = inet->min_ttl; 1422d218d111SStephen Hemminger break; 14231da177e4SLinus Torvalds default: 14241da177e4SLinus Torvalds release_sock(sk); 14251da177e4SLinus Torvalds return -ENOPROTOOPT; 14261da177e4SLinus Torvalds } 14271da177e4SLinus Torvalds release_sock(sk); 14281da177e4SLinus Torvalds 1429951e07c9SDavid S. Miller if (len < sizeof(int) && len > 0 && val >= 0 && val <= 255) { 14301da177e4SLinus Torvalds unsigned char ucval = (unsigned char)val; 14311da177e4SLinus Torvalds len = 1; 14321da177e4SLinus Torvalds if (put_user(len, optlen)) 14331da177e4SLinus Torvalds return -EFAULT; 14341da177e4SLinus Torvalds if (copy_to_user(optval, &ucval, 1)) 14351da177e4SLinus Torvalds return -EFAULT; 14361da177e4SLinus Torvalds } else { 14371da177e4SLinus Torvalds len = min_t(unsigned int, sizeof(int), len); 14381da177e4SLinus Torvalds if (put_user(len, optlen)) 14391da177e4SLinus Torvalds return -EFAULT; 14401da177e4SLinus Torvalds if (copy_to_user(optval, &val, len)) 14411da177e4SLinus Torvalds return -EFAULT; 14421da177e4SLinus Torvalds } 14431da177e4SLinus Torvalds return 0; 14441da177e4SLinus Torvalds } 14451da177e4SLinus Torvalds 14463fdadf7dSDmitry Mishin int ip_getsockopt(struct sock *sk, int level, 14473fdadf7dSDmitry Mishin int optname, char __user *optval, int __user *optlen) 14483fdadf7dSDmitry Mishin { 14493fdadf7dSDmitry Mishin int err; 14503fdadf7dSDmitry Mishin 1451dd23198eSDaniel Baluta err = do_ip_getsockopt(sk, level, optname, optval, optlen, 0); 14523fdadf7dSDmitry Mishin #ifdef CONFIG_NETFILTER 14533fdadf7dSDmitry Mishin /* we need to exclude all possible ENOPROTOOPTs except default case */ 14546a9fb947SPavel Emelyanov if (err == -ENOPROTOOPT && optname != IP_PKTOPTIONS && 14556a9fb947SPavel Emelyanov !ip_mroute_opt(optname)) { 14563fdadf7dSDmitry Mishin int len; 14573fdadf7dSDmitry Mishin 14583fdadf7dSDmitry Mishin if (get_user(len, optlen)) 14593fdadf7dSDmitry Mishin return -EFAULT; 14603fdadf7dSDmitry Mishin 14613fdadf7dSDmitry Mishin lock_sock(sk); 14623fdadf7dSDmitry Mishin err = nf_getsockopt(sk, PF_INET, optname, optval, 14633fdadf7dSDmitry Mishin &len); 14643fdadf7dSDmitry Mishin release_sock(sk); 14653fdadf7dSDmitry Mishin if (err >= 0) 14663fdadf7dSDmitry Mishin err = put_user(len, optlen); 14673fdadf7dSDmitry Mishin return err; 14683fdadf7dSDmitry Mishin } 14693fdadf7dSDmitry Mishin #endif 14703fdadf7dSDmitry Mishin return err; 14713fdadf7dSDmitry Mishin } 14724d52cfbeSEric Dumazet EXPORT_SYMBOL(ip_getsockopt); 14733fdadf7dSDmitry Mishin 14743fdadf7dSDmitry Mishin #ifdef CONFIG_COMPAT 1475543d9cfeSArnaldo Carvalho de Melo int compat_ip_getsockopt(struct sock *sk, int level, int optname, 1476543d9cfeSArnaldo Carvalho de Melo char __user *optval, int __user *optlen) 14773fdadf7dSDmitry Mishin { 147842908c69SDavid L Stevens int err; 147942908c69SDavid L Stevens 148042908c69SDavid L Stevens if (optname == MCAST_MSFILTER) 148142908c69SDavid L Stevens return compat_mc_getsockopt(sk, level, optname, optval, optlen, 148242908c69SDavid L Stevens ip_getsockopt); 148342908c69SDavid L Stevens 1484dd23198eSDaniel Baluta err = do_ip_getsockopt(sk, level, optname, optval, optlen, 1485dd23198eSDaniel Baluta MSG_CMSG_COMPAT); 148642908c69SDavid L Stevens 14873fdadf7dSDmitry Mishin #ifdef CONFIG_NETFILTER 14883fdadf7dSDmitry Mishin /* we need to exclude all possible ENOPROTOOPTs except default case */ 14896a9fb947SPavel Emelyanov if (err == -ENOPROTOOPT && optname != IP_PKTOPTIONS && 14906a9fb947SPavel Emelyanov !ip_mroute_opt(optname)) { 14913fdadf7dSDmitry Mishin int len; 14923fdadf7dSDmitry Mishin 14933fdadf7dSDmitry Mishin if (get_user(len, optlen)) 14943fdadf7dSDmitry Mishin return -EFAULT; 14953fdadf7dSDmitry Mishin 14963fdadf7dSDmitry Mishin lock_sock(sk); 1497543d9cfeSArnaldo Carvalho de Melo err = compat_nf_getsockopt(sk, PF_INET, optname, optval, &len); 14983fdadf7dSDmitry Mishin release_sock(sk); 14993fdadf7dSDmitry Mishin if (err >= 0) 15003fdadf7dSDmitry Mishin err = put_user(len, optlen); 15013fdadf7dSDmitry Mishin return err; 15023fdadf7dSDmitry Mishin } 15033fdadf7dSDmitry Mishin #endif 15043fdadf7dSDmitry Mishin return err; 15053fdadf7dSDmitry Mishin } 1506543d9cfeSArnaldo Carvalho de Melo EXPORT_SYMBOL(compat_ip_getsockopt); 15073fdadf7dSDmitry Mishin #endif 1508