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) 10931c2e492SWillem de Bruijn csum = csum_sub(csum, csum_partial(skb_transport_header(skb), 11031c2e492SWillem de Bruijn offset, 0)); 111ad6f939aSTom Herbert 112ad6f939aSTom Herbert put_cmsg(msg, SOL_IP, IP_CHECKSUM, sizeof(__wsum), &csum); 113ad6f939aSTom Herbert } 114ad6f939aSTom Herbert 1152c7946a7SCatherine Zhang static void ip_cmsg_recv_security(struct msghdr *msg, struct sk_buff *skb) 1162c7946a7SCatherine Zhang { 1172c7946a7SCatherine Zhang char *secdata; 118dc49c1f9SCatherine Zhang u32 seclen, secid; 1192c7946a7SCatherine Zhang int err; 1202c7946a7SCatherine Zhang 121dc49c1f9SCatherine Zhang err = security_socket_getpeersec_dgram(NULL, skb, &secid); 122dc49c1f9SCatherine Zhang if (err) 123dc49c1f9SCatherine Zhang return; 124dc49c1f9SCatherine Zhang 125dc49c1f9SCatherine Zhang err = security_secid_to_secctx(secid, &secdata, &seclen); 1262c7946a7SCatherine Zhang if (err) 1272c7946a7SCatherine Zhang return; 1282c7946a7SCatherine Zhang 1292c7946a7SCatherine Zhang put_cmsg(msg, SOL_IP, SCM_SECURITY, seclen, secdata); 130dc49c1f9SCatherine Zhang security_release_secctx(secdata, seclen); 1312c7946a7SCatherine Zhang } 1322c7946a7SCatherine Zhang 13321d1a161SHarvey Harrison static void ip_cmsg_recv_dstaddr(struct msghdr *msg, struct sk_buff *skb) 134e8b2dfe9SBalazs Scheidler { 135e8b2dfe9SBalazs Scheidler struct sockaddr_in sin; 136b71d1d42SEric Dumazet const struct iphdr *iph = ip_hdr(skb); 13721d1a161SHarvey Harrison __be16 *ports = (__be16 *)skb_transport_header(skb); 138e8b2dfe9SBalazs Scheidler 139e8b2dfe9SBalazs Scheidler if (skb_transport_offset(skb) + 4 > skb->len) 140e8b2dfe9SBalazs Scheidler return; 141e8b2dfe9SBalazs Scheidler 142e8b2dfe9SBalazs Scheidler /* All current transport protocols have the port numbers in the 143e8b2dfe9SBalazs Scheidler * first four bytes of the transport header and this function is 144e8b2dfe9SBalazs Scheidler * written with this assumption in mind. 145e8b2dfe9SBalazs Scheidler */ 146e8b2dfe9SBalazs Scheidler 147e8b2dfe9SBalazs Scheidler sin.sin_family = AF_INET; 148e8b2dfe9SBalazs Scheidler sin.sin_addr.s_addr = iph->daddr; 149e8b2dfe9SBalazs Scheidler sin.sin_port = ports[1]; 150e8b2dfe9SBalazs Scheidler memset(sin.sin_zero, 0, sizeof(sin.sin_zero)); 151e8b2dfe9SBalazs Scheidler 152e8b2dfe9SBalazs Scheidler put_cmsg(msg, SOL_IP, IP_ORIGDSTADDR, sizeof(sin), &sin); 153e8b2dfe9SBalazs Scheidler } 1541da177e4SLinus Torvalds 1555961de9fSTom Herbert void ip_cmsg_recv_offset(struct msghdr *msg, struct sk_buff *skb, 1565961de9fSTom Herbert int offset) 1571da177e4SLinus Torvalds { 1581da177e4SLinus Torvalds struct inet_sock *inet = inet_sk(skb->sk); 15995c96174SEric Dumazet unsigned int flags = inet->cmsg_flags; 1601da177e4SLinus Torvalds 1611da177e4SLinus Torvalds /* Ordered by supposed usage frequency */ 162c44d13d6STom Herbert if (flags & IP_CMSG_PKTINFO) { 1631da177e4SLinus Torvalds ip_cmsg_recv_pktinfo(msg, skb); 1641da177e4SLinus Torvalds 165c44d13d6STom Herbert flags &= ~IP_CMSG_PKTINFO; 166c44d13d6STom Herbert if (!flags) 167c44d13d6STom Herbert return; 168c44d13d6STom Herbert } 169c44d13d6STom Herbert 170c44d13d6STom Herbert if (flags & IP_CMSG_TTL) { 1711da177e4SLinus Torvalds ip_cmsg_recv_ttl(msg, skb); 1721da177e4SLinus Torvalds 173c44d13d6STom Herbert flags &= ~IP_CMSG_TTL; 174c44d13d6STom Herbert if (!flags) 175c44d13d6STom Herbert return; 176c44d13d6STom Herbert } 177c44d13d6STom Herbert 178c44d13d6STom Herbert if (flags & IP_CMSG_TOS) { 1791da177e4SLinus Torvalds ip_cmsg_recv_tos(msg, skb); 1801da177e4SLinus Torvalds 181c44d13d6STom Herbert flags &= ~IP_CMSG_TOS; 182c44d13d6STom Herbert if (!flags) 183c44d13d6STom Herbert return; 184c44d13d6STom Herbert } 185c44d13d6STom Herbert 186c44d13d6STom Herbert if (flags & IP_CMSG_RECVOPTS) { 1871da177e4SLinus Torvalds ip_cmsg_recv_opts(msg, skb); 1881da177e4SLinus Torvalds 189c44d13d6STom Herbert flags &= ~IP_CMSG_RECVOPTS; 190c44d13d6STom Herbert if (!flags) 191c44d13d6STom Herbert return; 192c44d13d6STom Herbert } 193c44d13d6STom Herbert 194c44d13d6STom Herbert if (flags & IP_CMSG_RETOPTS) { 1951da177e4SLinus Torvalds ip_cmsg_recv_retopts(msg, skb); 1962c7946a7SCatherine Zhang 197c44d13d6STom Herbert flags &= ~IP_CMSG_RETOPTS; 198c44d13d6STom Herbert if (!flags) 199c44d13d6STom Herbert return; 200c44d13d6STom Herbert } 201c44d13d6STom Herbert 202c44d13d6STom Herbert if (flags & IP_CMSG_PASSSEC) { 2032c7946a7SCatherine Zhang ip_cmsg_recv_security(msg, skb); 204e8b2dfe9SBalazs Scheidler 205c44d13d6STom Herbert flags &= ~IP_CMSG_PASSSEC; 206c44d13d6STom Herbert if (!flags) 207e8b2dfe9SBalazs Scheidler return; 208c44d13d6STom Herbert } 209c44d13d6STom Herbert 210ad6f939aSTom Herbert if (flags & IP_CMSG_ORIGDSTADDR) { 211e8b2dfe9SBalazs Scheidler ip_cmsg_recv_dstaddr(msg, skb); 212e8b2dfe9SBalazs Scheidler 213ad6f939aSTom Herbert flags &= ~IP_CMSG_ORIGDSTADDR; 214ad6f939aSTom Herbert if (!flags) 215ad6f939aSTom Herbert return; 216ad6f939aSTom Herbert } 217ad6f939aSTom Herbert 218ad6f939aSTom Herbert if (flags & IP_CMSG_CHECKSUM) 219ad6f939aSTom Herbert ip_cmsg_recv_checksum(msg, skb, offset); 2201da177e4SLinus Torvalds } 2215961de9fSTom Herbert EXPORT_SYMBOL(ip_cmsg_recv_offset); 2221da177e4SLinus Torvalds 22324025c46SSoheil Hassas Yeganeh int ip_cmsg_send(struct sock *sk, struct msghdr *msg, struct ipcm_cookie *ipc, 224c8e6ad08SHannes Frederic Sowa bool allow_ipv6) 2251da177e4SLinus Torvalds { 226f02db315SFrancesco Fusco int err, val; 2271da177e4SLinus Torvalds struct cmsghdr *cmsg; 22824025c46SSoheil Hassas Yeganeh struct net *net = sock_net(sk); 2291da177e4SLinus Torvalds 230f95b414eSGu Zheng for_each_cmsghdr(cmsg, msg) { 2311da177e4SLinus Torvalds if (!CMSG_OK(msg, cmsg)) 2321da177e4SLinus Torvalds return -EINVAL; 2335337b5b7SEric Dumazet #if IS_ENABLED(CONFIG_IPV6) 234c8e6ad08SHannes Frederic Sowa if (allow_ipv6 && 235c8e6ad08SHannes Frederic Sowa cmsg->cmsg_level == SOL_IPV6 && 236c8e6ad08SHannes Frederic Sowa cmsg->cmsg_type == IPV6_PKTINFO) { 237c8e6ad08SHannes Frederic Sowa struct in6_pktinfo *src_info; 238c8e6ad08SHannes Frederic Sowa 239c8e6ad08SHannes Frederic Sowa if (cmsg->cmsg_len < CMSG_LEN(sizeof(*src_info))) 240c8e6ad08SHannes Frederic Sowa return -EINVAL; 241c8e6ad08SHannes Frederic Sowa src_info = (struct in6_pktinfo *)CMSG_DATA(cmsg); 242c8e6ad08SHannes Frederic Sowa if (!ipv6_addr_v4mapped(&src_info->ipi6_addr)) 243c8e6ad08SHannes Frederic Sowa return -EINVAL; 244c8e6ad08SHannes Frederic Sowa ipc->oif = src_info->ipi6_ifindex; 245c8e6ad08SHannes Frederic Sowa ipc->addr = src_info->ipi6_addr.s6_addr32[3]; 246c8e6ad08SHannes Frederic Sowa continue; 247c8e6ad08SHannes Frederic Sowa } 248c8e6ad08SHannes Frederic Sowa #endif 24924025c46SSoheil Hassas Yeganeh if (cmsg->cmsg_level == SOL_SOCKET) { 25024025c46SSoheil Hassas Yeganeh if (__sock_cmsg_send(sk, msg, cmsg, &ipc->sockc)) 25124025c46SSoheil Hassas Yeganeh return -EINVAL; 25224025c46SSoheil Hassas Yeganeh continue; 25324025c46SSoheil Hassas Yeganeh } 25424025c46SSoheil Hassas Yeganeh 2551da177e4SLinus Torvalds if (cmsg->cmsg_level != SOL_IP) 2561da177e4SLinus Torvalds continue; 2571da177e4SLinus Torvalds switch (cmsg->cmsg_type) { 2581da177e4SLinus Torvalds case IP_RETOPTS: 2591da177e4SLinus Torvalds err = cmsg->cmsg_len - CMSG_ALIGN(sizeof(struct cmsghdr)); 26091948309SEric Dumazet 26191948309SEric Dumazet /* Our caller is responsible for freeing ipc->opt */ 2624d52cfbeSEric Dumazet err = ip_options_get(net, &ipc->opt, CMSG_DATA(cmsg), 2634d52cfbeSEric Dumazet err < 40 ? err : 40); 2641da177e4SLinus Torvalds if (err) 2651da177e4SLinus Torvalds return err; 2661da177e4SLinus Torvalds break; 2671da177e4SLinus Torvalds case IP_PKTINFO: 2681da177e4SLinus Torvalds { 2691da177e4SLinus Torvalds struct in_pktinfo *info; 2701da177e4SLinus Torvalds if (cmsg->cmsg_len != CMSG_LEN(sizeof(struct in_pktinfo))) 2711da177e4SLinus Torvalds return -EINVAL; 2721da177e4SLinus Torvalds info = (struct in_pktinfo *)CMSG_DATA(cmsg); 2731da177e4SLinus Torvalds ipc->oif = info->ipi_ifindex; 2741da177e4SLinus Torvalds ipc->addr = info->ipi_spec_dst.s_addr; 2751da177e4SLinus Torvalds break; 2761da177e4SLinus Torvalds } 277f02db315SFrancesco Fusco case IP_TTL: 278f02db315SFrancesco Fusco if (cmsg->cmsg_len != CMSG_LEN(sizeof(int))) 279f02db315SFrancesco Fusco return -EINVAL; 280f02db315SFrancesco Fusco val = *(int *)CMSG_DATA(cmsg); 281f02db315SFrancesco Fusco if (val < 1 || val > 255) 282f02db315SFrancesco Fusco return -EINVAL; 283f02db315SFrancesco Fusco ipc->ttl = val; 284f02db315SFrancesco Fusco break; 285f02db315SFrancesco Fusco case IP_TOS: 286f02db315SFrancesco Fusco if (cmsg->cmsg_len != CMSG_LEN(sizeof(int))) 287f02db315SFrancesco Fusco return -EINVAL; 288f02db315SFrancesco Fusco val = *(int *)CMSG_DATA(cmsg); 289f02db315SFrancesco Fusco if (val < 0 || val > 255) 290f02db315SFrancesco Fusco return -EINVAL; 291f02db315SFrancesco Fusco ipc->tos = val; 292f02db315SFrancesco Fusco ipc->priority = rt_tos2priority(ipc->tos); 293f02db315SFrancesco Fusco break; 294f02db315SFrancesco Fusco 2951da177e4SLinus Torvalds default: 2961da177e4SLinus Torvalds return -EINVAL; 2971da177e4SLinus Torvalds } 2981da177e4SLinus Torvalds } 2991da177e4SLinus Torvalds return 0; 3001da177e4SLinus Torvalds } 3011da177e4SLinus Torvalds 3021da177e4SLinus Torvalds 3031da177e4SLinus Torvalds /* Special input handler for packets caught by router alert option. 3041da177e4SLinus Torvalds They are selected only by protocol field, and then processed likely 3051da177e4SLinus Torvalds local ones; but only if someone wants them! Otherwise, router 3061da177e4SLinus Torvalds not running rsvpd will kill RSVP. 3071da177e4SLinus Torvalds 3081da177e4SLinus Torvalds It is user level problem, what it will make with them. 3091da177e4SLinus Torvalds I have no idea, how it will masquearde or NAT them (it is joke, joke :-)), 3101da177e4SLinus Torvalds but receiver should be enough clever f.e. to forward mtrace requests, 3111da177e4SLinus Torvalds sent to multicast group to reach destination designated router. 3121da177e4SLinus Torvalds */ 31343a951e9SEric Dumazet struct ip_ra_chain __rcu *ip_ra_chain; 31466018506SEric Dumazet static DEFINE_SPINLOCK(ip_ra_lock); 31566018506SEric Dumazet 316592fcb9dSEric Dumazet 317592fcb9dSEric Dumazet static void ip_ra_destroy_rcu(struct rcu_head *head) 31866018506SEric Dumazet { 319592fcb9dSEric Dumazet struct ip_ra_chain *ra = container_of(head, struct ip_ra_chain, rcu); 320592fcb9dSEric Dumazet 321592fcb9dSEric Dumazet sock_put(ra->saved_sk); 322592fcb9dSEric Dumazet kfree(ra); 32366018506SEric Dumazet } 3241da177e4SLinus Torvalds 3254d52cfbeSEric Dumazet int ip_ra_control(struct sock *sk, unsigned char on, 3264d52cfbeSEric Dumazet void (*destructor)(struct sock *)) 3271da177e4SLinus Torvalds { 32843a951e9SEric Dumazet struct ip_ra_chain *ra, *new_ra; 32943a951e9SEric Dumazet struct ip_ra_chain __rcu **rap; 3301da177e4SLinus Torvalds 331c720c7e8SEric Dumazet if (sk->sk_type != SOCK_RAW || inet_sk(sk)->inet_num == IPPROTO_RAW) 3321da177e4SLinus Torvalds return -EINVAL; 3331da177e4SLinus Torvalds 3341da177e4SLinus Torvalds new_ra = on ? kmalloc(sizeof(*new_ra), GFP_KERNEL) : NULL; 3351da177e4SLinus Torvalds 33666018506SEric Dumazet spin_lock_bh(&ip_ra_lock); 33743a951e9SEric Dumazet for (rap = &ip_ra_chain; 33843a951e9SEric Dumazet (ra = rcu_dereference_protected(*rap, 33943a951e9SEric Dumazet lockdep_is_held(&ip_ra_lock))) != NULL; 34043a951e9SEric Dumazet rap = &ra->next) { 3411da177e4SLinus Torvalds if (ra->sk == sk) { 3421da177e4SLinus Torvalds if (on) { 34366018506SEric Dumazet spin_unlock_bh(&ip_ra_lock); 3441da177e4SLinus Torvalds kfree(new_ra); 3451da177e4SLinus Torvalds return -EADDRINUSE; 3461da177e4SLinus Torvalds } 347592fcb9dSEric Dumazet /* dont let ip_call_ra_chain() use sk again */ 348592fcb9dSEric Dumazet ra->sk = NULL; 3498e380f00SEric Dumazet RCU_INIT_POINTER(*rap, ra->next); 35066018506SEric Dumazet spin_unlock_bh(&ip_ra_lock); 3511da177e4SLinus Torvalds 3521da177e4SLinus Torvalds if (ra->destructor) 3531da177e4SLinus Torvalds ra->destructor(sk); 354592fcb9dSEric Dumazet /* 355592fcb9dSEric Dumazet * Delay sock_put(sk) and kfree(ra) after one rcu grace 356592fcb9dSEric Dumazet * period. This guarantee ip_call_ra_chain() dont need 357592fcb9dSEric Dumazet * to mess with socket refcounts. 358592fcb9dSEric Dumazet */ 359592fcb9dSEric Dumazet ra->saved_sk = sk; 360592fcb9dSEric Dumazet call_rcu(&ra->rcu, ip_ra_destroy_rcu); 3611da177e4SLinus Torvalds return 0; 3621da177e4SLinus Torvalds } 3631da177e4SLinus Torvalds } 36451456b29SIan Morris if (!new_ra) { 36566018506SEric Dumazet spin_unlock_bh(&ip_ra_lock); 3661da177e4SLinus Torvalds return -ENOBUFS; 3671da177e4SLinus Torvalds } 3681da177e4SLinus Torvalds new_ra->sk = sk; 3691da177e4SLinus Torvalds new_ra->destructor = destructor; 3701da177e4SLinus Torvalds 3718e380f00SEric Dumazet RCU_INIT_POINTER(new_ra->next, ra); 37266018506SEric Dumazet rcu_assign_pointer(*rap, new_ra); 3731da177e4SLinus Torvalds sock_hold(sk); 37466018506SEric Dumazet spin_unlock_bh(&ip_ra_lock); 3751da177e4SLinus Torvalds 3761da177e4SLinus Torvalds return 0; 3771da177e4SLinus Torvalds } 3781da177e4SLinus Torvalds 3791da177e4SLinus Torvalds void ip_icmp_error(struct sock *sk, struct sk_buff *skb, int err, 38035986b32SAl Viro __be16 port, u32 info, u8 *payload) 3811da177e4SLinus Torvalds { 3821da177e4SLinus Torvalds struct sock_exterr_skb *serr; 3831da177e4SLinus Torvalds 3841da177e4SLinus Torvalds skb = skb_clone(skb, GFP_ATOMIC); 3851da177e4SLinus Torvalds if (!skb) 3861da177e4SLinus Torvalds return; 3871da177e4SLinus Torvalds 3881da177e4SLinus Torvalds serr = SKB_EXT_ERR(skb); 3891da177e4SLinus Torvalds serr->ee.ee_errno = err; 3901da177e4SLinus Torvalds serr->ee.ee_origin = SO_EE_ORIGIN_ICMP; 39188c7664fSArnaldo Carvalho de Melo serr->ee.ee_type = icmp_hdr(skb)->type; 39288c7664fSArnaldo Carvalho de Melo serr->ee.ee_code = icmp_hdr(skb)->code; 3931da177e4SLinus Torvalds serr->ee.ee_pad = 0; 3941da177e4SLinus Torvalds serr->ee.ee_info = info; 3951da177e4SLinus Torvalds serr->ee.ee_data = 0; 39688c7664fSArnaldo Carvalho de Melo serr->addr_offset = (u8 *)&(((struct iphdr *)(icmp_hdr(skb) + 1))->daddr) - 397d56f90a7SArnaldo Carvalho de Melo skb_network_header(skb); 3981da177e4SLinus Torvalds serr->port = port; 3991da177e4SLinus Torvalds 40000db4124SIan Morris if (skb_pull(skb, payload - skb->data)) { 401bd82393cSArnaldo Carvalho de Melo skb_reset_transport_header(skb); 402bd82393cSArnaldo Carvalho de Melo if (sock_queue_err_skb(sk, skb) == 0) 403bd82393cSArnaldo Carvalho de Melo return; 404bd82393cSArnaldo Carvalho de Melo } 4051da177e4SLinus Torvalds kfree_skb(skb); 4061da177e4SLinus Torvalds } 4071da177e4SLinus Torvalds 4080579016eSAl Viro void ip_local_error(struct sock *sk, int err, __be32 daddr, __be16 port, u32 info) 4091da177e4SLinus Torvalds { 4101da177e4SLinus Torvalds struct inet_sock *inet = inet_sk(sk); 4111da177e4SLinus Torvalds struct sock_exterr_skb *serr; 4121da177e4SLinus Torvalds struct iphdr *iph; 4131da177e4SLinus Torvalds struct sk_buff *skb; 4141da177e4SLinus Torvalds 4151da177e4SLinus Torvalds if (!inet->recverr) 4161da177e4SLinus Torvalds return; 4171da177e4SLinus Torvalds 4181da177e4SLinus Torvalds skb = alloc_skb(sizeof(struct iphdr), GFP_ATOMIC); 4191da177e4SLinus Torvalds if (!skb) 4201da177e4SLinus Torvalds return; 4211da177e4SLinus Torvalds 4222ca9e6f2SArnaldo Carvalho de Melo skb_put(skb, sizeof(struct iphdr)); 4232ca9e6f2SArnaldo Carvalho de Melo skb_reset_network_header(skb); 424eddc9ec5SArnaldo Carvalho de Melo iph = ip_hdr(skb); 4251da177e4SLinus Torvalds iph->daddr = daddr; 4261da177e4SLinus Torvalds 4271da177e4SLinus Torvalds serr = SKB_EXT_ERR(skb); 4281da177e4SLinus Torvalds serr->ee.ee_errno = err; 4291da177e4SLinus Torvalds serr->ee.ee_origin = SO_EE_ORIGIN_LOCAL; 4301da177e4SLinus Torvalds serr->ee.ee_type = 0; 4311da177e4SLinus Torvalds serr->ee.ee_code = 0; 4321da177e4SLinus Torvalds serr->ee.ee_pad = 0; 4331da177e4SLinus Torvalds serr->ee.ee_info = info; 4341da177e4SLinus Torvalds serr->ee.ee_data = 0; 435d56f90a7SArnaldo Carvalho de Melo serr->addr_offset = (u8 *)&iph->daddr - skb_network_header(skb); 4361da177e4SLinus Torvalds serr->port = port; 4371da177e4SLinus Torvalds 43827a884dcSArnaldo Carvalho de Melo __skb_pull(skb, skb_tail_pointer(skb) - skb->data); 439bd82393cSArnaldo Carvalho de Melo skb_reset_transport_header(skb); 4401da177e4SLinus Torvalds 4411da177e4SLinus Torvalds if (sock_queue_err_skb(sk, skb)) 4421da177e4SLinus Torvalds kfree_skb(skb); 4431da177e4SLinus Torvalds } 4441da177e4SLinus Torvalds 44534b99df4SJulian Anastasov /* For some errors we have valid addr_offset even with zero payload and 44634b99df4SJulian Anastasov * zero port. Also, addr_offset should be supported if port is set. 44734b99df4SJulian Anastasov */ 44834b99df4SJulian Anastasov static inline bool ipv4_datagram_support_addr(struct sock_exterr_skb *serr) 44934b99df4SJulian Anastasov { 45034b99df4SJulian Anastasov return serr->ee.ee_origin == SO_EE_ORIGIN_ICMP || 45134b99df4SJulian Anastasov serr->ee.ee_origin == SO_EE_ORIGIN_LOCAL || serr->port; 45234b99df4SJulian Anastasov } 45334b99df4SJulian Anastasov 454c247f053SWillem de Bruijn /* IPv4 supports cmsg on all imcp errors and some timestamps 455c247f053SWillem de Bruijn * 456c247f053SWillem de Bruijn * Timestamp code paths do not initialize the fields expected by cmsg: 457c247f053SWillem de Bruijn * the PKTINFO fields in skb->cb[]. Fill those in here. 458c247f053SWillem de Bruijn */ 459c247f053SWillem de Bruijn static bool ipv4_datagram_support_cmsg(const struct sock *sk, 460c247f053SWillem de Bruijn struct sk_buff *skb, 461829ae9d6SWillem de Bruijn int ee_origin) 462829ae9d6SWillem de Bruijn { 463c247f053SWillem de Bruijn struct in_pktinfo *info; 464829ae9d6SWillem de Bruijn 465c247f053SWillem de Bruijn if (ee_origin == SO_EE_ORIGIN_ICMP) 466c247f053SWillem de Bruijn return true; 467c247f053SWillem de Bruijn 468c247f053SWillem de Bruijn if (ee_origin == SO_EE_ORIGIN_LOCAL) 469c247f053SWillem de Bruijn return false; 470c247f053SWillem de Bruijn 471c247f053SWillem de Bruijn /* Support IP_PKTINFO on tstamp packets if requested, to correlate 472c247f053SWillem de Bruijn * timestamp with egress dev. Not possible for packets without dev 473c247f053SWillem de Bruijn * or without payload (SOF_TIMESTAMPING_OPT_TSONLY). 474c247f053SWillem de Bruijn */ 475c247f053SWillem de Bruijn if ((!(sk->sk_tsflags & SOF_TIMESTAMPING_OPT_CMSG)) || 476829ae9d6SWillem de Bruijn (!skb->dev)) 477829ae9d6SWillem de Bruijn return false; 478829ae9d6SWillem de Bruijn 479c247f053SWillem de Bruijn info = PKTINFO_SKB_CB(skb); 480829ae9d6SWillem de Bruijn info->ipi_spec_dst.s_addr = ip_hdr(skb)->saddr; 481829ae9d6SWillem de Bruijn info->ipi_ifindex = skb->dev->ifindex; 482829ae9d6SWillem de Bruijn return true; 483829ae9d6SWillem de Bruijn } 484829ae9d6SWillem de Bruijn 4851da177e4SLinus Torvalds /* 4861da177e4SLinus Torvalds * Handle MSG_ERRQUEUE 4871da177e4SLinus Torvalds */ 48885fbaa75SHannes Frederic Sowa int ip_recv_error(struct sock *sk, struct msghdr *msg, int len, int *addr_len) 4891da177e4SLinus Torvalds { 4901da177e4SLinus Torvalds struct sock_exterr_skb *serr; 491364a9e93SWillem de Bruijn struct sk_buff *skb; 492342dfc30SSteffen Hurrle DECLARE_SOCKADDR(struct sockaddr_in *, sin, msg->msg_name); 4931da177e4SLinus Torvalds struct { 4941da177e4SLinus Torvalds struct sock_extended_err ee; 4951da177e4SLinus Torvalds struct sockaddr_in offender; 4961da177e4SLinus Torvalds } errhdr; 4971da177e4SLinus Torvalds int err; 4981da177e4SLinus Torvalds int copied; 4991da177e4SLinus Torvalds 5007ce875e5SWillem de Bruijn WARN_ON_ONCE(sk->sk_family == AF_INET6); 5017ce875e5SWillem de Bruijn 5021da177e4SLinus Torvalds err = -EAGAIN; 503364a9e93SWillem de Bruijn skb = sock_dequeue_err_skb(sk); 50451456b29SIan Morris if (!skb) 5051da177e4SLinus Torvalds goto out; 5061da177e4SLinus Torvalds 5071da177e4SLinus Torvalds copied = skb->len; 5081da177e4SLinus Torvalds if (copied > len) { 5091da177e4SLinus Torvalds msg->msg_flags |= MSG_TRUNC; 5101da177e4SLinus Torvalds copied = len; 5111da177e4SLinus Torvalds } 51251f3d02bSDavid S. Miller err = skb_copy_datagram_msg(skb, 0, msg, copied); 513*960a2628SEric Dumazet if (unlikely(err)) { 514*960a2628SEric Dumazet kfree_skb(skb); 515*960a2628SEric Dumazet return err; 516*960a2628SEric Dumazet } 5171da177e4SLinus Torvalds sock_recv_timestamp(msg, sk, skb); 5181da177e4SLinus Torvalds 5191da177e4SLinus Torvalds serr = SKB_EXT_ERR(skb); 5201da177e4SLinus Torvalds 52134b99df4SJulian Anastasov if (sin && ipv4_datagram_support_addr(serr)) { 5221da177e4SLinus Torvalds sin->sin_family = AF_INET; 523d56f90a7SArnaldo Carvalho de Melo sin->sin_addr.s_addr = *(__be32 *)(skb_network_header(skb) + 524d56f90a7SArnaldo Carvalho de Melo serr->addr_offset); 5251da177e4SLinus Torvalds sin->sin_port = serr->port; 5261da177e4SLinus Torvalds memset(&sin->sin_zero, 0, sizeof(sin->sin_zero)); 52785fbaa75SHannes Frederic Sowa *addr_len = sizeof(*sin); 5281da177e4SLinus Torvalds } 5291da177e4SLinus Torvalds 5301da177e4SLinus Torvalds memcpy(&errhdr.ee, &serr->ee, sizeof(struct sock_extended_err)); 5311da177e4SLinus Torvalds sin = &errhdr.offender; 532f812116bSWillem de Bruijn memset(sin, 0, sizeof(*sin)); 533829ae9d6SWillem de Bruijn 534c247f053SWillem de Bruijn if (ipv4_datagram_support_cmsg(sk, skb, serr->ee.ee_origin)) { 5351da177e4SLinus Torvalds sin->sin_family = AF_INET; 536eddc9ec5SArnaldo Carvalho de Melo sin->sin_addr.s_addr = ip_hdr(skb)->saddr; 537f812116bSWillem de Bruijn if (inet_sk(sk)->cmsg_flags) 5381da177e4SLinus Torvalds ip_cmsg_recv(msg, skb); 5391da177e4SLinus Torvalds } 5401da177e4SLinus Torvalds 5411da177e4SLinus Torvalds put_cmsg(msg, SOL_IP, IP_RECVERR, sizeof(errhdr), &errhdr); 5421da177e4SLinus Torvalds 5431da177e4SLinus Torvalds /* Now we could try to dump offended packet options */ 5441da177e4SLinus Torvalds 5451da177e4SLinus Torvalds msg->msg_flags |= MSG_ERRQUEUE; 5461da177e4SLinus Torvalds err = copied; 5471da177e4SLinus Torvalds 548*960a2628SEric Dumazet consume_skb(skb); 5491da177e4SLinus Torvalds out: 5501da177e4SLinus Torvalds return err; 5511da177e4SLinus Torvalds } 5521da177e4SLinus Torvalds 5531da177e4SLinus Torvalds 5541da177e4SLinus Torvalds /* 5554d52cfbeSEric Dumazet * Socket option code for IP. This is the end of the line after any 5564d52cfbeSEric Dumazet * TCP,UDP etc options on an IP socket. 5571da177e4SLinus Torvalds */ 558baf606d9SMarcelo Ricardo Leitner static bool setsockopt_needs_rtnl(int optname) 559baf606d9SMarcelo Ricardo Leitner { 560baf606d9SMarcelo Ricardo Leitner switch (optname) { 561baf606d9SMarcelo Ricardo Leitner case IP_ADD_MEMBERSHIP: 562baf606d9SMarcelo Ricardo Leitner case IP_ADD_SOURCE_MEMBERSHIP: 56354ff9ef3SMarcelo Ricardo Leitner case IP_BLOCK_SOURCE: 564baf606d9SMarcelo Ricardo Leitner case IP_DROP_MEMBERSHIP: 56554ff9ef3SMarcelo Ricardo Leitner case IP_DROP_SOURCE_MEMBERSHIP: 56654ff9ef3SMarcelo Ricardo Leitner case IP_MSFILTER: 56754ff9ef3SMarcelo Ricardo Leitner case IP_UNBLOCK_SOURCE: 56854ff9ef3SMarcelo Ricardo Leitner case MCAST_BLOCK_SOURCE: 56954ff9ef3SMarcelo Ricardo Leitner case MCAST_MSFILTER: 570baf606d9SMarcelo Ricardo Leitner case MCAST_JOIN_GROUP: 57154ff9ef3SMarcelo Ricardo Leitner case MCAST_JOIN_SOURCE_GROUP: 572baf606d9SMarcelo Ricardo Leitner case MCAST_LEAVE_GROUP: 57354ff9ef3SMarcelo Ricardo Leitner case MCAST_LEAVE_SOURCE_GROUP: 57454ff9ef3SMarcelo Ricardo Leitner case MCAST_UNBLOCK_SOURCE: 575baf606d9SMarcelo Ricardo Leitner return true; 576baf606d9SMarcelo Ricardo Leitner } 577baf606d9SMarcelo Ricardo Leitner return false; 578baf606d9SMarcelo Ricardo Leitner } 5791da177e4SLinus Torvalds 5803fdadf7dSDmitry Mishin static int do_ip_setsockopt(struct sock *sk, int level, 581b7058842SDavid S. Miller int optname, char __user *optval, unsigned int optlen) 5821da177e4SLinus Torvalds { 5831da177e4SLinus Torvalds struct inet_sock *inet = inet_sk(sk); 584166b6b2dSNikolay Borisov struct net *net = sock_net(sk); 5851da177e4SLinus Torvalds int val = 0, err; 586baf606d9SMarcelo Ricardo Leitner bool needs_rtnl = setsockopt_needs_rtnl(optname); 5871da177e4SLinus Torvalds 5880c9f79beSXi Wang switch (optname) { 5890c9f79beSXi Wang case IP_PKTINFO: 5900c9f79beSXi Wang case IP_RECVTTL: 5910c9f79beSXi Wang case IP_RECVOPTS: 5920c9f79beSXi Wang case IP_RECVTOS: 5930c9f79beSXi Wang case IP_RETOPTS: 5940c9f79beSXi Wang case IP_TOS: 5950c9f79beSXi Wang case IP_TTL: 5960c9f79beSXi Wang case IP_HDRINCL: 5970c9f79beSXi Wang case IP_MTU_DISCOVER: 5980c9f79beSXi Wang case IP_RECVERR: 5990c9f79beSXi Wang case IP_ROUTER_ALERT: 6000c9f79beSXi Wang case IP_FREEBIND: 6010c9f79beSXi Wang case IP_PASSSEC: 6020c9f79beSXi Wang case IP_TRANSPARENT: 6030c9f79beSXi Wang case IP_MINTTL: 6040c9f79beSXi Wang case IP_NODEFRAG: 60590c337daSEric Dumazet case IP_BIND_ADDRESS_NO_PORT: 6060c9f79beSXi Wang case IP_UNICAST_IF: 6070c9f79beSXi Wang case IP_MULTICAST_TTL: 6080c9f79beSXi Wang case IP_MULTICAST_ALL: 6090c9f79beSXi Wang case IP_MULTICAST_LOOP: 6100c9f79beSXi Wang case IP_RECVORIGDSTADDR: 611ad6f939aSTom Herbert case IP_CHECKSUM: 6121da177e4SLinus Torvalds if (optlen >= sizeof(int)) { 6131da177e4SLinus Torvalds if (get_user(val, (int __user *) optval)) 6141da177e4SLinus Torvalds return -EFAULT; 6151da177e4SLinus Torvalds } else if (optlen >= sizeof(char)) { 6161da177e4SLinus Torvalds unsigned char ucval; 6171da177e4SLinus Torvalds 6181da177e4SLinus Torvalds if (get_user(ucval, (unsigned char __user *) optval)) 6191da177e4SLinus Torvalds return -EFAULT; 6201da177e4SLinus Torvalds val = (int) ucval; 6211da177e4SLinus Torvalds } 6221da177e4SLinus Torvalds } 6231da177e4SLinus Torvalds 6241da177e4SLinus Torvalds /* If optlen==0, it is equivalent to val == 0 */ 6251da177e4SLinus Torvalds 6266a9fb947SPavel Emelyanov if (ip_mroute_opt(optname)) 6271da177e4SLinus Torvalds return ip_mroute_setsockopt(sk, optname, optval, optlen); 6281da177e4SLinus Torvalds 6291da177e4SLinus Torvalds err = 0; 630baf606d9SMarcelo Ricardo Leitner if (needs_rtnl) 631baf606d9SMarcelo Ricardo Leitner rtnl_lock(); 6321da177e4SLinus Torvalds lock_sock(sk); 6331da177e4SLinus Torvalds 6341da177e4SLinus Torvalds switch (optname) { 6351da177e4SLinus Torvalds case IP_OPTIONS: 6361da177e4SLinus Torvalds { 637f6d8bd05SEric Dumazet struct ip_options_rcu *old, *opt = NULL; 638f6d8bd05SEric Dumazet 63965a1c4ffSroel kluin if (optlen > 40) 6401da177e4SLinus Torvalds goto e_inval; 6413b1e0a65SYOSHIFUJI Hideaki err = ip_options_get_from_user(sock_net(sk), &opt, 642cb84663eSDenis V. Lunev optval, optlen); 6431da177e4SLinus Torvalds if (err) 6441da177e4SLinus Torvalds break; 645f6d8bd05SEric Dumazet old = rcu_dereference_protected(inet->inet_opt, 6461e1d04e6SHannes Frederic Sowa lockdep_sock_is_held(sk)); 647d83d8461SArnaldo Carvalho de Melo if (inet->is_icsk) { 648d83d8461SArnaldo Carvalho de Melo struct inet_connection_sock *icsk = inet_csk(sk); 649dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6) 6501da177e4SLinus Torvalds if (sk->sk_family == PF_INET || 6511da177e4SLinus Torvalds (!((1 << sk->sk_state) & 6521da177e4SLinus Torvalds (TCPF_LISTEN | TCPF_CLOSE)) && 653c720c7e8SEric Dumazet inet->inet_daddr != LOOPBACK4_IPV6)) { 6541da177e4SLinus Torvalds #endif 655f6d8bd05SEric Dumazet if (old) 656f6d8bd05SEric Dumazet icsk->icsk_ext_hdr_len -= old->opt.optlen; 6571da177e4SLinus Torvalds if (opt) 658f6d8bd05SEric Dumazet icsk->icsk_ext_hdr_len += opt->opt.optlen; 659d83d8461SArnaldo Carvalho de Melo icsk->icsk_sync_mss(sk, icsk->icsk_pmtu_cookie); 660dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6) 6611da177e4SLinus Torvalds } 6621da177e4SLinus Torvalds #endif 6631da177e4SLinus Torvalds } 664f6d8bd05SEric Dumazet rcu_assign_pointer(inet->inet_opt, opt); 665f6d8bd05SEric Dumazet if (old) 666605b4afeSPaul E. McKenney kfree_rcu(old, rcu); 6671da177e4SLinus Torvalds break; 6681da177e4SLinus Torvalds } 6691da177e4SLinus Torvalds case IP_PKTINFO: 6701da177e4SLinus Torvalds if (val) 6711da177e4SLinus Torvalds inet->cmsg_flags |= IP_CMSG_PKTINFO; 6721da177e4SLinus Torvalds else 6731da177e4SLinus Torvalds inet->cmsg_flags &= ~IP_CMSG_PKTINFO; 6741da177e4SLinus Torvalds break; 6751da177e4SLinus Torvalds case IP_RECVTTL: 6761da177e4SLinus Torvalds if (val) 6771da177e4SLinus Torvalds inet->cmsg_flags |= IP_CMSG_TTL; 6781da177e4SLinus Torvalds else 6791da177e4SLinus Torvalds inet->cmsg_flags &= ~IP_CMSG_TTL; 6801da177e4SLinus Torvalds break; 6811da177e4SLinus Torvalds case IP_RECVTOS: 6821da177e4SLinus Torvalds if (val) 6831da177e4SLinus Torvalds inet->cmsg_flags |= IP_CMSG_TOS; 6841da177e4SLinus Torvalds else 6851da177e4SLinus Torvalds inet->cmsg_flags &= ~IP_CMSG_TOS; 6861da177e4SLinus Torvalds break; 6871da177e4SLinus Torvalds case IP_RECVOPTS: 6881da177e4SLinus Torvalds if (val) 6891da177e4SLinus Torvalds inet->cmsg_flags |= IP_CMSG_RECVOPTS; 6901da177e4SLinus Torvalds else 6911da177e4SLinus Torvalds inet->cmsg_flags &= ~IP_CMSG_RECVOPTS; 6921da177e4SLinus Torvalds break; 6931da177e4SLinus Torvalds case IP_RETOPTS: 6941da177e4SLinus Torvalds if (val) 6951da177e4SLinus Torvalds inet->cmsg_flags |= IP_CMSG_RETOPTS; 6961da177e4SLinus Torvalds else 6971da177e4SLinus Torvalds inet->cmsg_flags &= ~IP_CMSG_RETOPTS; 6981da177e4SLinus Torvalds break; 6992c7946a7SCatherine Zhang case IP_PASSSEC: 7002c7946a7SCatherine Zhang if (val) 7012c7946a7SCatherine Zhang inet->cmsg_flags |= IP_CMSG_PASSSEC; 7022c7946a7SCatherine Zhang else 7032c7946a7SCatherine Zhang inet->cmsg_flags &= ~IP_CMSG_PASSSEC; 7042c7946a7SCatherine Zhang break; 705e8b2dfe9SBalazs Scheidler case IP_RECVORIGDSTADDR: 706e8b2dfe9SBalazs Scheidler if (val) 707e8b2dfe9SBalazs Scheidler inet->cmsg_flags |= IP_CMSG_ORIGDSTADDR; 708e8b2dfe9SBalazs Scheidler else 709e8b2dfe9SBalazs Scheidler inet->cmsg_flags &= ~IP_CMSG_ORIGDSTADDR; 710e8b2dfe9SBalazs Scheidler break; 711ad6f939aSTom Herbert case IP_CHECKSUM: 712ad6f939aSTom Herbert if (val) { 713ad6f939aSTom Herbert if (!(inet->cmsg_flags & IP_CMSG_CHECKSUM)) { 714ad6f939aSTom Herbert inet_inc_convert_csum(sk); 715ad6f939aSTom Herbert inet->cmsg_flags |= IP_CMSG_CHECKSUM; 716ad6f939aSTom Herbert } 717ad6f939aSTom Herbert } else { 718ad6f939aSTom Herbert if (inet->cmsg_flags & IP_CMSG_CHECKSUM) { 719ad6f939aSTom Herbert inet_dec_convert_csum(sk); 720ad6f939aSTom Herbert inet->cmsg_flags &= ~IP_CMSG_CHECKSUM; 721ad6f939aSTom Herbert } 722ad6f939aSTom Herbert } 723ad6f939aSTom Herbert break; 7241da177e4SLinus Torvalds case IP_TOS: /* This sets both TOS and Precedence */ 7251da177e4SLinus Torvalds if (sk->sk_type == SOCK_STREAM) { 7262c67e9acSMaciej Żenczykowski val &= ~INET_ECN_MASK; 7272c67e9acSMaciej Żenczykowski val |= inet->tos & INET_ECN_MASK; 7281da177e4SLinus Torvalds } 7291da177e4SLinus Torvalds if (inet->tos != val) { 7301da177e4SLinus Torvalds inet->tos = val; 7311da177e4SLinus Torvalds sk->sk_priority = rt_tos2priority(val); 7321da177e4SLinus Torvalds sk_dst_reset(sk); 7331da177e4SLinus Torvalds } 7341da177e4SLinus Torvalds break; 7351da177e4SLinus Torvalds case IP_TTL: 7361da177e4SLinus Torvalds if (optlen < 1) 7371da177e4SLinus Torvalds goto e_inval; 738c9be4a5cSCong Wang if (val != -1 && (val < 1 || val > 255)) 7391da177e4SLinus Torvalds goto e_inval; 7401da177e4SLinus Torvalds inet->uc_ttl = val; 7411da177e4SLinus Torvalds break; 7421da177e4SLinus Torvalds case IP_HDRINCL: 7431da177e4SLinus Torvalds if (sk->sk_type != SOCK_RAW) { 7441da177e4SLinus Torvalds err = -ENOPROTOOPT; 7451da177e4SLinus Torvalds break; 7461da177e4SLinus Torvalds } 7471da177e4SLinus Torvalds inet->hdrincl = val ? 1 : 0; 7481da177e4SLinus Torvalds break; 7497b2ff18eSJiri Olsa case IP_NODEFRAG: 7507b2ff18eSJiri Olsa if (sk->sk_type != SOCK_RAW) { 7517b2ff18eSJiri Olsa err = -ENOPROTOOPT; 7527b2ff18eSJiri Olsa break; 7537b2ff18eSJiri Olsa } 7547b2ff18eSJiri Olsa inet->nodefrag = val ? 1 : 0; 7557b2ff18eSJiri Olsa break; 75690c337daSEric Dumazet case IP_BIND_ADDRESS_NO_PORT: 75790c337daSEric Dumazet inet->bind_address_no_port = val ? 1 : 0; 75890c337daSEric Dumazet break; 7591da177e4SLinus Torvalds case IP_MTU_DISCOVER: 7601b346576SHannes Frederic Sowa if (val < IP_PMTUDISC_DONT || val > IP_PMTUDISC_OMIT) 7611da177e4SLinus Torvalds goto e_inval; 7621da177e4SLinus Torvalds inet->pmtudisc = val; 7631da177e4SLinus Torvalds break; 7641da177e4SLinus Torvalds case IP_RECVERR: 7651da177e4SLinus Torvalds inet->recverr = !!val; 7661da177e4SLinus Torvalds if (!val) 7671da177e4SLinus Torvalds skb_queue_purge(&sk->sk_error_queue); 7681da177e4SLinus Torvalds break; 7691da177e4SLinus Torvalds case IP_MULTICAST_TTL: 7701da177e4SLinus Torvalds if (sk->sk_type == SOCK_STREAM) 7711da177e4SLinus Torvalds goto e_inval; 7721da177e4SLinus Torvalds if (optlen < 1) 7731da177e4SLinus Torvalds goto e_inval; 7741da177e4SLinus Torvalds if (val == -1) 7751da177e4SLinus Torvalds val = 1; 7761da177e4SLinus Torvalds if (val < 0 || val > 255) 7771da177e4SLinus Torvalds goto e_inval; 7781da177e4SLinus Torvalds inet->mc_ttl = val; 7791da177e4SLinus Torvalds break; 7801da177e4SLinus Torvalds case IP_MULTICAST_LOOP: 7811da177e4SLinus Torvalds if (optlen < 1) 7821da177e4SLinus Torvalds goto e_inval; 7831da177e4SLinus Torvalds inet->mc_loop = !!val; 7841da177e4SLinus Torvalds break; 78576e21053SErich E. Hoover case IP_UNICAST_IF: 78676e21053SErich E. Hoover { 78776e21053SErich E. Hoover struct net_device *dev = NULL; 78876e21053SErich E. Hoover int ifindex; 78976e21053SErich E. Hoover 79076e21053SErich E. Hoover if (optlen != sizeof(int)) 79176e21053SErich E. Hoover goto e_inval; 79276e21053SErich E. Hoover 79376e21053SErich E. Hoover ifindex = (__force int)ntohl((__force __be32)val); 79476e21053SErich E. Hoover if (ifindex == 0) { 79576e21053SErich E. Hoover inet->uc_index = 0; 79676e21053SErich E. Hoover err = 0; 79776e21053SErich E. Hoover break; 79876e21053SErich E. Hoover } 79976e21053SErich E. Hoover 80076e21053SErich E. Hoover dev = dev_get_by_index(sock_net(sk), ifindex); 80176e21053SErich E. Hoover err = -EADDRNOTAVAIL; 80276e21053SErich E. Hoover if (!dev) 80376e21053SErich E. Hoover break; 80476e21053SErich E. Hoover dev_put(dev); 80576e21053SErich E. Hoover 80676e21053SErich E. Hoover err = -EINVAL; 80776e21053SErich E. Hoover if (sk->sk_bound_dev_if) 80876e21053SErich E. Hoover break; 80976e21053SErich E. Hoover 81076e21053SErich E. Hoover inet->uc_index = ifindex; 81176e21053SErich E. Hoover err = 0; 81276e21053SErich E. Hoover break; 81376e21053SErich E. Hoover } 8141da177e4SLinus Torvalds case IP_MULTICAST_IF: 8151da177e4SLinus Torvalds { 8161da177e4SLinus Torvalds struct ip_mreqn mreq; 8171da177e4SLinus Torvalds struct net_device *dev = NULL; 8181da177e4SLinus Torvalds 8191da177e4SLinus Torvalds if (sk->sk_type == SOCK_STREAM) 8201da177e4SLinus Torvalds goto e_inval; 8211da177e4SLinus Torvalds /* 8221da177e4SLinus Torvalds * Check the arguments are allowable 8231da177e4SLinus Torvalds */ 8241da177e4SLinus Torvalds 8250915921bSShan Wei if (optlen < sizeof(struct in_addr)) 8260915921bSShan Wei goto e_inval; 8270915921bSShan Wei 8281da177e4SLinus Torvalds err = -EFAULT; 8291da177e4SLinus Torvalds if (optlen >= sizeof(struct ip_mreqn)) { 8301da177e4SLinus Torvalds if (copy_from_user(&mreq, optval, sizeof(mreq))) 8311da177e4SLinus Torvalds break; 8321da177e4SLinus Torvalds } else { 8331da177e4SLinus Torvalds memset(&mreq, 0, sizeof(mreq)); 8343a084ddbSJiri Pirko if (optlen >= sizeof(struct ip_mreq)) { 8353a084ddbSJiri Pirko if (copy_from_user(&mreq, optval, 8363a084ddbSJiri Pirko sizeof(struct ip_mreq))) 8373a084ddbSJiri Pirko break; 8383a084ddbSJiri Pirko } else if (optlen >= sizeof(struct in_addr)) { 8393a084ddbSJiri Pirko if (copy_from_user(&mreq.imr_address, optval, 8404d52cfbeSEric Dumazet sizeof(struct in_addr))) 8411da177e4SLinus Torvalds break; 8421da177e4SLinus Torvalds } 8433a084ddbSJiri Pirko } 8441da177e4SLinus Torvalds 8451da177e4SLinus Torvalds if (!mreq.imr_ifindex) { 846e6f1cebfSAl Viro if (mreq.imr_address.s_addr == htonl(INADDR_ANY)) { 8471da177e4SLinus Torvalds inet->mc_index = 0; 8481da177e4SLinus Torvalds inet->mc_addr = 0; 8491da177e4SLinus Torvalds err = 0; 8501da177e4SLinus Torvalds break; 8511da177e4SLinus Torvalds } 8523b1e0a65SYOSHIFUJI Hideaki dev = ip_dev_find(sock_net(sk), mreq.imr_address.s_addr); 85355b80503SEric Dumazet if (dev) 8541da177e4SLinus Torvalds mreq.imr_ifindex = dev->ifindex; 8551da177e4SLinus Torvalds } else 85655b80503SEric Dumazet dev = dev_get_by_index(sock_net(sk), mreq.imr_ifindex); 8571da177e4SLinus Torvalds 8581da177e4SLinus Torvalds 8591da177e4SLinus Torvalds err = -EADDRNOTAVAIL; 8601da177e4SLinus Torvalds if (!dev) 8611da177e4SLinus Torvalds break; 86255b80503SEric Dumazet dev_put(dev); 8631da177e4SLinus Torvalds 8641da177e4SLinus Torvalds err = -EINVAL; 8651da177e4SLinus Torvalds if (sk->sk_bound_dev_if && 8661da177e4SLinus Torvalds mreq.imr_ifindex != sk->sk_bound_dev_if) 8671da177e4SLinus Torvalds break; 8681da177e4SLinus Torvalds 8691da177e4SLinus Torvalds inet->mc_index = mreq.imr_ifindex; 8701da177e4SLinus Torvalds inet->mc_addr = mreq.imr_address.s_addr; 8711da177e4SLinus Torvalds err = 0; 8721da177e4SLinus Torvalds break; 8731da177e4SLinus Torvalds } 8741da177e4SLinus Torvalds 8751da177e4SLinus Torvalds case IP_ADD_MEMBERSHIP: 8761da177e4SLinus Torvalds case IP_DROP_MEMBERSHIP: 8771da177e4SLinus Torvalds { 8781da177e4SLinus Torvalds struct ip_mreqn mreq; 8791da177e4SLinus Torvalds 880a96fb49bSFlavio Leitner err = -EPROTO; 881a96fb49bSFlavio Leitner if (inet_sk(sk)->is_icsk) 882a96fb49bSFlavio Leitner break; 883a96fb49bSFlavio Leitner 8841da177e4SLinus Torvalds if (optlen < sizeof(struct ip_mreq)) 8851da177e4SLinus Torvalds goto e_inval; 8861da177e4SLinus Torvalds err = -EFAULT; 8871da177e4SLinus Torvalds if (optlen >= sizeof(struct ip_mreqn)) { 8881da177e4SLinus Torvalds if (copy_from_user(&mreq, optval, sizeof(mreq))) 8891da177e4SLinus Torvalds break; 8901da177e4SLinus Torvalds } else { 8911da177e4SLinus Torvalds memset(&mreq, 0, sizeof(mreq)); 8921da177e4SLinus Torvalds if (copy_from_user(&mreq, optval, sizeof(struct ip_mreq))) 8931da177e4SLinus Torvalds break; 8941da177e4SLinus Torvalds } 8951da177e4SLinus Torvalds 8961da177e4SLinus Torvalds if (optname == IP_ADD_MEMBERSHIP) 89754ff9ef3SMarcelo Ricardo Leitner err = ip_mc_join_group(sk, &mreq); 8981da177e4SLinus Torvalds else 89954ff9ef3SMarcelo Ricardo Leitner err = ip_mc_leave_group(sk, &mreq); 9001da177e4SLinus Torvalds break; 9011da177e4SLinus Torvalds } 9021da177e4SLinus Torvalds case IP_MSFILTER: 9031da177e4SLinus Torvalds { 9041da177e4SLinus Torvalds struct ip_msfilter *msf; 9051da177e4SLinus Torvalds 9061da177e4SLinus Torvalds if (optlen < IP_MSFILTER_SIZE(0)) 9071da177e4SLinus Torvalds goto e_inval; 9081da177e4SLinus Torvalds if (optlen > sysctl_optmem_max) { 9091da177e4SLinus Torvalds err = -ENOBUFS; 9101da177e4SLinus Torvalds break; 9111da177e4SLinus Torvalds } 9128b3a7005SKris Katterjohn msf = kmalloc(optlen, GFP_KERNEL); 913cfcabdccSStephen Hemminger if (!msf) { 9141da177e4SLinus Torvalds err = -ENOBUFS; 9151da177e4SLinus Torvalds break; 9161da177e4SLinus Torvalds } 9171da177e4SLinus Torvalds err = -EFAULT; 9181da177e4SLinus Torvalds if (copy_from_user(msf, optval, optlen)) { 9191da177e4SLinus Torvalds kfree(msf); 9201da177e4SLinus Torvalds break; 9211da177e4SLinus Torvalds } 9221da177e4SLinus Torvalds /* numsrc >= (1G-4) overflow in 32 bits */ 9231da177e4SLinus Torvalds if (msf->imsf_numsrc >= 0x3ffffffcU || 924166b6b2dSNikolay Borisov msf->imsf_numsrc > net->ipv4.sysctl_igmp_max_msf) { 9251da177e4SLinus Torvalds kfree(msf); 9261da177e4SLinus Torvalds err = -ENOBUFS; 9271da177e4SLinus Torvalds break; 9281da177e4SLinus Torvalds } 9291da177e4SLinus Torvalds if (IP_MSFILTER_SIZE(msf->imsf_numsrc) > optlen) { 9301da177e4SLinus Torvalds kfree(msf); 9311da177e4SLinus Torvalds err = -EINVAL; 9321da177e4SLinus Torvalds break; 9331da177e4SLinus Torvalds } 9341da177e4SLinus Torvalds err = ip_mc_msfilter(sk, msf, 0); 9351da177e4SLinus Torvalds kfree(msf); 9361da177e4SLinus Torvalds break; 9371da177e4SLinus Torvalds } 9381da177e4SLinus Torvalds case IP_BLOCK_SOURCE: 9391da177e4SLinus Torvalds case IP_UNBLOCK_SOURCE: 9401da177e4SLinus Torvalds case IP_ADD_SOURCE_MEMBERSHIP: 9411da177e4SLinus Torvalds case IP_DROP_SOURCE_MEMBERSHIP: 9421da177e4SLinus Torvalds { 9431da177e4SLinus Torvalds struct ip_mreq_source mreqs; 9441da177e4SLinus Torvalds int omode, add; 9451da177e4SLinus Torvalds 9461da177e4SLinus Torvalds if (optlen != sizeof(struct ip_mreq_source)) 9471da177e4SLinus Torvalds goto e_inval; 9481da177e4SLinus Torvalds if (copy_from_user(&mreqs, optval, sizeof(mreqs))) { 9491da177e4SLinus Torvalds err = -EFAULT; 9501da177e4SLinus Torvalds break; 9511da177e4SLinus Torvalds } 9521da177e4SLinus Torvalds if (optname == IP_BLOCK_SOURCE) { 9531da177e4SLinus Torvalds omode = MCAST_EXCLUDE; 9541da177e4SLinus Torvalds add = 1; 9551da177e4SLinus Torvalds } else if (optname == IP_UNBLOCK_SOURCE) { 9561da177e4SLinus Torvalds omode = MCAST_EXCLUDE; 9571da177e4SLinus Torvalds add = 0; 9581da177e4SLinus Torvalds } else if (optname == IP_ADD_SOURCE_MEMBERSHIP) { 9591da177e4SLinus Torvalds struct ip_mreqn mreq; 9601da177e4SLinus Torvalds 9611da177e4SLinus Torvalds mreq.imr_multiaddr.s_addr = mreqs.imr_multiaddr; 9621da177e4SLinus Torvalds mreq.imr_address.s_addr = mreqs.imr_interface; 9631da177e4SLinus Torvalds mreq.imr_ifindex = 0; 96454ff9ef3SMarcelo Ricardo Leitner err = ip_mc_join_group(sk, &mreq); 9658cdaaa15SDavid L Stevens if (err && err != -EADDRINUSE) 9661da177e4SLinus Torvalds break; 9671da177e4SLinus Torvalds omode = MCAST_INCLUDE; 9681da177e4SLinus Torvalds add = 1; 9691da177e4SLinus Torvalds } else /* IP_DROP_SOURCE_MEMBERSHIP */ { 9701da177e4SLinus Torvalds omode = MCAST_INCLUDE; 9711da177e4SLinus Torvalds add = 0; 9721da177e4SLinus Torvalds } 9731da177e4SLinus Torvalds err = ip_mc_source(add, omode, sk, &mreqs, 0); 9741da177e4SLinus Torvalds break; 9751da177e4SLinus Torvalds } 9761da177e4SLinus Torvalds case MCAST_JOIN_GROUP: 9771da177e4SLinus Torvalds case MCAST_LEAVE_GROUP: 9781da177e4SLinus Torvalds { 9791da177e4SLinus Torvalds struct group_req greq; 9801da177e4SLinus Torvalds struct sockaddr_in *psin; 9811da177e4SLinus Torvalds struct ip_mreqn mreq; 9821da177e4SLinus Torvalds 9831da177e4SLinus Torvalds if (optlen < sizeof(struct group_req)) 9841da177e4SLinus Torvalds goto e_inval; 9851da177e4SLinus Torvalds err = -EFAULT; 9861da177e4SLinus Torvalds if (copy_from_user(&greq, optval, sizeof(greq))) 9871da177e4SLinus Torvalds break; 9881da177e4SLinus Torvalds psin = (struct sockaddr_in *)&greq.gr_group; 9891da177e4SLinus Torvalds if (psin->sin_family != AF_INET) 9901da177e4SLinus Torvalds goto e_inval; 9911da177e4SLinus Torvalds memset(&mreq, 0, sizeof(mreq)); 9921da177e4SLinus Torvalds mreq.imr_multiaddr = psin->sin_addr; 9931da177e4SLinus Torvalds mreq.imr_ifindex = greq.gr_interface; 9941da177e4SLinus Torvalds 9951da177e4SLinus Torvalds if (optname == MCAST_JOIN_GROUP) 99654ff9ef3SMarcelo Ricardo Leitner err = ip_mc_join_group(sk, &mreq); 9971da177e4SLinus Torvalds else 99854ff9ef3SMarcelo Ricardo Leitner err = ip_mc_leave_group(sk, &mreq); 9991da177e4SLinus Torvalds break; 10001da177e4SLinus Torvalds } 10011da177e4SLinus Torvalds case MCAST_JOIN_SOURCE_GROUP: 10021da177e4SLinus Torvalds case MCAST_LEAVE_SOURCE_GROUP: 10031da177e4SLinus Torvalds case MCAST_BLOCK_SOURCE: 10041da177e4SLinus Torvalds case MCAST_UNBLOCK_SOURCE: 10051da177e4SLinus Torvalds { 10061da177e4SLinus Torvalds struct group_source_req greqs; 10071da177e4SLinus Torvalds struct ip_mreq_source mreqs; 10081da177e4SLinus Torvalds struct sockaddr_in *psin; 10091da177e4SLinus Torvalds int omode, add; 10101da177e4SLinus Torvalds 10111da177e4SLinus Torvalds if (optlen != sizeof(struct group_source_req)) 10121da177e4SLinus Torvalds goto e_inval; 10131da177e4SLinus Torvalds if (copy_from_user(&greqs, optval, sizeof(greqs))) { 10141da177e4SLinus Torvalds err = -EFAULT; 10151da177e4SLinus Torvalds break; 10161da177e4SLinus Torvalds } 10171da177e4SLinus Torvalds if (greqs.gsr_group.ss_family != AF_INET || 10181da177e4SLinus Torvalds greqs.gsr_source.ss_family != AF_INET) { 10191da177e4SLinus Torvalds err = -EADDRNOTAVAIL; 10201da177e4SLinus Torvalds break; 10211da177e4SLinus Torvalds } 10221da177e4SLinus Torvalds psin = (struct sockaddr_in *)&greqs.gsr_group; 10231da177e4SLinus Torvalds mreqs.imr_multiaddr = psin->sin_addr.s_addr; 10241da177e4SLinus Torvalds psin = (struct sockaddr_in *)&greqs.gsr_source; 10251da177e4SLinus Torvalds mreqs.imr_sourceaddr = psin->sin_addr.s_addr; 10261da177e4SLinus Torvalds mreqs.imr_interface = 0; /* use index for mc_source */ 10271da177e4SLinus Torvalds 10281da177e4SLinus Torvalds if (optname == MCAST_BLOCK_SOURCE) { 10291da177e4SLinus Torvalds omode = MCAST_EXCLUDE; 10301da177e4SLinus Torvalds add = 1; 10311da177e4SLinus Torvalds } else if (optname == MCAST_UNBLOCK_SOURCE) { 10321da177e4SLinus Torvalds omode = MCAST_EXCLUDE; 10331da177e4SLinus Torvalds add = 0; 10341da177e4SLinus Torvalds } else if (optname == MCAST_JOIN_SOURCE_GROUP) { 10351da177e4SLinus Torvalds struct ip_mreqn mreq; 10361da177e4SLinus Torvalds 10371da177e4SLinus Torvalds psin = (struct sockaddr_in *)&greqs.gsr_group; 10381da177e4SLinus Torvalds mreq.imr_multiaddr = psin->sin_addr; 10391da177e4SLinus Torvalds mreq.imr_address.s_addr = 0; 10401da177e4SLinus Torvalds mreq.imr_ifindex = greqs.gsr_interface; 104154ff9ef3SMarcelo Ricardo Leitner err = ip_mc_join_group(sk, &mreq); 10428cdaaa15SDavid L Stevens if (err && err != -EADDRINUSE) 10431da177e4SLinus Torvalds break; 10441da177e4SLinus Torvalds greqs.gsr_interface = mreq.imr_ifindex; 10451da177e4SLinus Torvalds omode = MCAST_INCLUDE; 10461da177e4SLinus Torvalds add = 1; 10471da177e4SLinus Torvalds } else /* MCAST_LEAVE_SOURCE_GROUP */ { 10481da177e4SLinus Torvalds omode = MCAST_INCLUDE; 10491da177e4SLinus Torvalds add = 0; 10501da177e4SLinus Torvalds } 10511da177e4SLinus Torvalds err = ip_mc_source(add, omode, sk, &mreqs, 10521da177e4SLinus Torvalds greqs.gsr_interface); 10531da177e4SLinus Torvalds break; 10541da177e4SLinus Torvalds } 10551da177e4SLinus Torvalds case MCAST_MSFILTER: 10561da177e4SLinus Torvalds { 10571da177e4SLinus Torvalds struct sockaddr_in *psin; 10581da177e4SLinus Torvalds struct ip_msfilter *msf = NULL; 10591da177e4SLinus Torvalds struct group_filter *gsf = NULL; 10601da177e4SLinus Torvalds int msize, i, ifindex; 10611da177e4SLinus Torvalds 10621da177e4SLinus Torvalds if (optlen < GROUP_FILTER_SIZE(0)) 10631da177e4SLinus Torvalds goto e_inval; 10641da177e4SLinus Torvalds if (optlen > sysctl_optmem_max) { 10651da177e4SLinus Torvalds err = -ENOBUFS; 10661da177e4SLinus Torvalds break; 10671da177e4SLinus Torvalds } 10688b3a7005SKris Katterjohn gsf = kmalloc(optlen, GFP_KERNEL); 1069cfcabdccSStephen Hemminger if (!gsf) { 10701da177e4SLinus Torvalds err = -ENOBUFS; 10711da177e4SLinus Torvalds break; 10721da177e4SLinus Torvalds } 10731da177e4SLinus Torvalds err = -EFAULT; 10744d52cfbeSEric Dumazet if (copy_from_user(gsf, optval, optlen)) 10751da177e4SLinus Torvalds goto mc_msf_out; 10764d52cfbeSEric Dumazet 10771da177e4SLinus Torvalds /* numsrc >= (4G-140)/128 overflow in 32 bits */ 10781da177e4SLinus Torvalds if (gsf->gf_numsrc >= 0x1ffffff || 1079166b6b2dSNikolay Borisov gsf->gf_numsrc > net->ipv4.sysctl_igmp_max_msf) { 10801da177e4SLinus Torvalds err = -ENOBUFS; 10811da177e4SLinus Torvalds goto mc_msf_out; 10821da177e4SLinus Torvalds } 10831da177e4SLinus Torvalds if (GROUP_FILTER_SIZE(gsf->gf_numsrc) > optlen) { 10841da177e4SLinus Torvalds err = -EINVAL; 10851da177e4SLinus Torvalds goto mc_msf_out; 10861da177e4SLinus Torvalds } 10871da177e4SLinus Torvalds msize = IP_MSFILTER_SIZE(gsf->gf_numsrc); 10888b3a7005SKris Katterjohn msf = kmalloc(msize, GFP_KERNEL); 1089cfcabdccSStephen Hemminger if (!msf) { 10901da177e4SLinus Torvalds err = -ENOBUFS; 10911da177e4SLinus Torvalds goto mc_msf_out; 10921da177e4SLinus Torvalds } 10931da177e4SLinus Torvalds ifindex = gsf->gf_interface; 10941da177e4SLinus Torvalds psin = (struct sockaddr_in *)&gsf->gf_group; 10951da177e4SLinus Torvalds if (psin->sin_family != AF_INET) { 10961da177e4SLinus Torvalds err = -EADDRNOTAVAIL; 10971da177e4SLinus Torvalds goto mc_msf_out; 10981da177e4SLinus Torvalds } 10991da177e4SLinus Torvalds msf->imsf_multiaddr = psin->sin_addr.s_addr; 11001da177e4SLinus Torvalds msf->imsf_interface = 0; 11011da177e4SLinus Torvalds msf->imsf_fmode = gsf->gf_fmode; 11021da177e4SLinus Torvalds msf->imsf_numsrc = gsf->gf_numsrc; 11031da177e4SLinus Torvalds err = -EADDRNOTAVAIL; 11041da177e4SLinus Torvalds for (i = 0; i < gsf->gf_numsrc; ++i) { 11051da177e4SLinus Torvalds psin = (struct sockaddr_in *)&gsf->gf_slist[i]; 11061da177e4SLinus Torvalds 11071da177e4SLinus Torvalds if (psin->sin_family != AF_INET) 11081da177e4SLinus Torvalds goto mc_msf_out; 11091da177e4SLinus Torvalds msf->imsf_slist[i] = psin->sin_addr.s_addr; 11101da177e4SLinus Torvalds } 11111da177e4SLinus Torvalds kfree(gsf); 11121da177e4SLinus Torvalds gsf = NULL; 11131da177e4SLinus Torvalds 11141da177e4SLinus Torvalds err = ip_mc_msfilter(sk, msf, ifindex); 11151da177e4SLinus Torvalds mc_msf_out: 11161da177e4SLinus Torvalds kfree(msf); 11171da177e4SLinus Torvalds kfree(gsf); 11181da177e4SLinus Torvalds break; 11191da177e4SLinus Torvalds } 1120f771bef9SNivedita Singhvi case IP_MULTICAST_ALL: 1121f771bef9SNivedita Singhvi if (optlen < 1) 1122f771bef9SNivedita Singhvi goto e_inval; 1123f771bef9SNivedita Singhvi if (val != 0 && val != 1) 1124f771bef9SNivedita Singhvi goto e_inval; 1125f771bef9SNivedita Singhvi inet->mc_all = val; 1126f771bef9SNivedita Singhvi break; 11271da177e4SLinus Torvalds case IP_ROUTER_ALERT: 11281da177e4SLinus Torvalds err = ip_ra_control(sk, val ? 1 : 0, NULL); 11291da177e4SLinus Torvalds break; 11301da177e4SLinus Torvalds 11311da177e4SLinus Torvalds case IP_FREEBIND: 11321da177e4SLinus Torvalds if (optlen < 1) 11331da177e4SLinus Torvalds goto e_inval; 11341da177e4SLinus Torvalds inet->freebind = !!val; 11351da177e4SLinus Torvalds break; 11361da177e4SLinus Torvalds 11371da177e4SLinus Torvalds case IP_IPSEC_POLICY: 11381da177e4SLinus Torvalds case IP_XFRM_POLICY: 11396fc0b4a7SHerbert Xu err = -EPERM; 114052e804c6SEric W. Biederman if (!ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN)) 11416fc0b4a7SHerbert Xu break; 11421da177e4SLinus Torvalds err = xfrm_user_policy(sk, optname, optval, optlen); 11431da177e4SLinus Torvalds break; 11441da177e4SLinus Torvalds 1145f5715aeaSKOVACS Krisztian case IP_TRANSPARENT: 114652e804c6SEric W. Biederman if (!!val && !ns_capable(sock_net(sk)->user_ns, CAP_NET_RAW) && 114752e804c6SEric W. Biederman !ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN)) { 1148f5715aeaSKOVACS Krisztian err = -EPERM; 1149f5715aeaSKOVACS Krisztian break; 1150f5715aeaSKOVACS Krisztian } 1151f5715aeaSKOVACS Krisztian if (optlen < 1) 1152f5715aeaSKOVACS Krisztian goto e_inval; 1153f5715aeaSKOVACS Krisztian inet->transparent = !!val; 1154f5715aeaSKOVACS Krisztian break; 1155f5715aeaSKOVACS Krisztian 1156d218d111SStephen Hemminger case IP_MINTTL: 1157d218d111SStephen Hemminger if (optlen < 1) 1158d218d111SStephen Hemminger goto e_inval; 1159d218d111SStephen Hemminger if (val < 0 || val > 255) 1160d218d111SStephen Hemminger goto e_inval; 1161d218d111SStephen Hemminger inet->min_ttl = val; 1162d218d111SStephen Hemminger break; 1163d218d111SStephen Hemminger 11641da177e4SLinus Torvalds default: 11651da177e4SLinus Torvalds err = -ENOPROTOOPT; 11661da177e4SLinus Torvalds break; 11671da177e4SLinus Torvalds } 11681da177e4SLinus Torvalds release_sock(sk); 1169baf606d9SMarcelo Ricardo Leitner if (needs_rtnl) 1170baf606d9SMarcelo Ricardo Leitner rtnl_unlock(); 11711da177e4SLinus Torvalds return err; 11721da177e4SLinus Torvalds 11731da177e4SLinus Torvalds e_inval: 11741da177e4SLinus Torvalds release_sock(sk); 1175baf606d9SMarcelo Ricardo Leitner if (needs_rtnl) 1176baf606d9SMarcelo Ricardo Leitner rtnl_unlock(); 11771da177e4SLinus Torvalds return -EINVAL; 11781da177e4SLinus Torvalds } 11791da177e4SLinus Torvalds 1180f84af32cSEric Dumazet /** 1181829ae9d6SWillem de Bruijn * ipv4_pktinfo_prepare - transfer some info from rtable to skb 1182f84af32cSEric Dumazet * @sk: socket 1183f84af32cSEric Dumazet * @skb: buffer 1184f84af32cSEric Dumazet * 118535ebf65eSDavid S. Miller * To support IP_CMSG_PKTINFO option, we store rt_iif and specific 118635ebf65eSDavid S. Miller * destination in skb->cb[] before dst drop. 11878e3bff96Sstephen hemminger * This way, receiver doesn't make cache line misses to read rtable. 1188f84af32cSEric Dumazet */ 1189fbf8866dSShawn Bohrer void ipv4_pktinfo_prepare(const struct sock *sk, struct sk_buff *skb) 1190f84af32cSEric Dumazet { 1191d826eb14SEric Dumazet struct in_pktinfo *pktinfo = PKTINFO_SKB_CB(skb); 11924b261c75SHannes Frederic Sowa bool prepare = (inet_sk(sk)->cmsg_flags & IP_CMSG_PKTINFO) || 11934b261c75SHannes Frederic Sowa ipv6_sk_rxinfo(sk); 1194d826eb14SEric Dumazet 11954b261c75SHannes Frederic Sowa if (prepare && skb_rtable(skb)) { 119692101b3bSDavid S. Miller pktinfo->ipi_ifindex = inet_iif(skb); 119735ebf65eSDavid S. Miller pktinfo->ipi_spec_dst.s_addr = fib_compute_spec_dst(skb); 1198d826eb14SEric Dumazet } else { 1199d826eb14SEric Dumazet pktinfo->ipi_ifindex = 0; 1200d826eb14SEric Dumazet pktinfo->ipi_spec_dst.s_addr = 0; 1201f84af32cSEric Dumazet } 1202d826eb14SEric Dumazet skb_dst_drop(skb); 1203d826eb14SEric Dumazet } 1204f84af32cSEric Dumazet 12053fdadf7dSDmitry Mishin int ip_setsockopt(struct sock *sk, int level, 1206b7058842SDavid S. Miller int optname, char __user *optval, unsigned int optlen) 12073fdadf7dSDmitry Mishin { 12083fdadf7dSDmitry Mishin int err; 12093fdadf7dSDmitry Mishin 12103fdadf7dSDmitry Mishin if (level != SOL_IP) 12113fdadf7dSDmitry Mishin return -ENOPROTOOPT; 12123fdadf7dSDmitry Mishin 12133fdadf7dSDmitry Mishin err = do_ip_setsockopt(sk, level, optname, optval, optlen); 12143fdadf7dSDmitry Mishin #ifdef CONFIG_NETFILTER 12153fdadf7dSDmitry Mishin /* we need to exclude all possible ENOPROTOOPTs except default case */ 12163fdadf7dSDmitry Mishin if (err == -ENOPROTOOPT && optname != IP_HDRINCL && 12176a9fb947SPavel Emelyanov optname != IP_IPSEC_POLICY && 12186a9fb947SPavel Emelyanov optname != IP_XFRM_POLICY && 12196a9fb947SPavel Emelyanov !ip_mroute_opt(optname)) { 12203fdadf7dSDmitry Mishin lock_sock(sk); 12213fdadf7dSDmitry Mishin err = nf_setsockopt(sk, PF_INET, optname, optval, optlen); 12223fdadf7dSDmitry Mishin release_sock(sk); 12233fdadf7dSDmitry Mishin } 12243fdadf7dSDmitry Mishin #endif 12253fdadf7dSDmitry Mishin return err; 12263fdadf7dSDmitry Mishin } 12274d52cfbeSEric Dumazet EXPORT_SYMBOL(ip_setsockopt); 12283fdadf7dSDmitry Mishin 12293fdadf7dSDmitry Mishin #ifdef CONFIG_COMPAT 1230543d9cfeSArnaldo Carvalho de Melo int compat_ip_setsockopt(struct sock *sk, int level, int optname, 1231b7058842SDavid S. Miller char __user *optval, unsigned int optlen) 12323fdadf7dSDmitry Mishin { 12333fdadf7dSDmitry Mishin int err; 12343fdadf7dSDmitry Mishin 12353fdadf7dSDmitry Mishin if (level != SOL_IP) 12363fdadf7dSDmitry Mishin return -ENOPROTOOPT; 12373fdadf7dSDmitry Mishin 1238dae50295SDavid L Stevens if (optname >= MCAST_JOIN_GROUP && optname <= MCAST_MSFILTER) 1239dae50295SDavid L Stevens return compat_mc_setsockopt(sk, level, optname, optval, optlen, 1240dae50295SDavid L Stevens ip_setsockopt); 1241dae50295SDavid L Stevens 12423fdadf7dSDmitry Mishin err = do_ip_setsockopt(sk, level, optname, optval, optlen); 12433fdadf7dSDmitry Mishin #ifdef CONFIG_NETFILTER 12443fdadf7dSDmitry Mishin /* we need to exclude all possible ENOPROTOOPTs except default case */ 12453fdadf7dSDmitry Mishin if (err == -ENOPROTOOPT && optname != IP_HDRINCL && 12466a9fb947SPavel Emelyanov optname != IP_IPSEC_POLICY && 12476a9fb947SPavel Emelyanov optname != IP_XFRM_POLICY && 12486a9fb947SPavel Emelyanov !ip_mroute_opt(optname)) { 12493fdadf7dSDmitry Mishin lock_sock(sk); 1250543d9cfeSArnaldo Carvalho de Melo err = compat_nf_setsockopt(sk, PF_INET, optname, 1251543d9cfeSArnaldo Carvalho de Melo optval, optlen); 12523fdadf7dSDmitry Mishin release_sock(sk); 12533fdadf7dSDmitry Mishin } 12543fdadf7dSDmitry Mishin #endif 12553fdadf7dSDmitry Mishin return err; 12563fdadf7dSDmitry Mishin } 1257543d9cfeSArnaldo Carvalho de Melo EXPORT_SYMBOL(compat_ip_setsockopt); 12583fdadf7dSDmitry Mishin #endif 12593fdadf7dSDmitry Mishin 12601da177e4SLinus Torvalds /* 12614d52cfbeSEric Dumazet * Get the options. Note for future reference. The GET of IP options gets 12624d52cfbeSEric Dumazet * the _received_ ones. The set sets the _sent_ ones. 12631da177e4SLinus Torvalds */ 12641da177e4SLinus Torvalds 126587e9f031SWANG Cong static bool getsockopt_needs_rtnl(int optname) 126687e9f031SWANG Cong { 126787e9f031SWANG Cong switch (optname) { 126887e9f031SWANG Cong case IP_MSFILTER: 126987e9f031SWANG Cong case MCAST_MSFILTER: 127087e9f031SWANG Cong return true; 127187e9f031SWANG Cong } 127287e9f031SWANG Cong return false; 127387e9f031SWANG Cong } 127487e9f031SWANG Cong 12753fdadf7dSDmitry Mishin static int do_ip_getsockopt(struct sock *sk, int level, int optname, 127695c96174SEric Dumazet char __user *optval, int __user *optlen, unsigned int flags) 12771da177e4SLinus Torvalds { 12781da177e4SLinus Torvalds struct inet_sock *inet = inet_sk(sk); 127987e9f031SWANG Cong bool needs_rtnl = getsockopt_needs_rtnl(optname); 128087e9f031SWANG Cong int val, err = 0; 12811da177e4SLinus Torvalds int len; 12821da177e4SLinus Torvalds 12831da177e4SLinus Torvalds if (level != SOL_IP) 12841da177e4SLinus Torvalds return -EOPNOTSUPP; 12851da177e4SLinus Torvalds 12866a9fb947SPavel Emelyanov if (ip_mroute_opt(optname)) 12871da177e4SLinus Torvalds return ip_mroute_getsockopt(sk, optname, optval, optlen); 12881da177e4SLinus Torvalds 12891da177e4SLinus Torvalds if (get_user(len, optlen)) 12901da177e4SLinus Torvalds return -EFAULT; 12911da177e4SLinus Torvalds if (len < 0) 12921da177e4SLinus Torvalds return -EINVAL; 12931da177e4SLinus Torvalds 129487e9f031SWANG Cong if (needs_rtnl) 129587e9f031SWANG Cong rtnl_lock(); 12961da177e4SLinus Torvalds lock_sock(sk); 12971da177e4SLinus Torvalds 12981da177e4SLinus Torvalds switch (optname) { 12991da177e4SLinus Torvalds case IP_OPTIONS: 13001da177e4SLinus Torvalds { 13011da177e4SLinus Torvalds unsigned char optbuf[sizeof(struct ip_options)+40]; 13021da177e4SLinus Torvalds struct ip_options *opt = (struct ip_options *)optbuf; 1303f6d8bd05SEric Dumazet struct ip_options_rcu *inet_opt; 1304f6d8bd05SEric Dumazet 1305f6d8bd05SEric Dumazet inet_opt = rcu_dereference_protected(inet->inet_opt, 13061e1d04e6SHannes Frederic Sowa lockdep_sock_is_held(sk)); 13071da177e4SLinus Torvalds opt->optlen = 0; 1308f6d8bd05SEric Dumazet if (inet_opt) 1309f6d8bd05SEric Dumazet memcpy(optbuf, &inet_opt->opt, 13101da177e4SLinus Torvalds sizeof(struct ip_options) + 1311f6d8bd05SEric Dumazet inet_opt->opt.optlen); 13121da177e4SLinus Torvalds release_sock(sk); 13131da177e4SLinus Torvalds 13141da177e4SLinus Torvalds if (opt->optlen == 0) 13151da177e4SLinus Torvalds return put_user(0, optlen); 13161da177e4SLinus Torvalds 13171da177e4SLinus Torvalds ip_options_undo(opt); 13181da177e4SLinus Torvalds 13191da177e4SLinus Torvalds len = min_t(unsigned int, len, opt->optlen); 13201da177e4SLinus Torvalds if (put_user(len, optlen)) 13211da177e4SLinus Torvalds return -EFAULT; 13221da177e4SLinus Torvalds if (copy_to_user(optval, opt->__data, len)) 13231da177e4SLinus Torvalds return -EFAULT; 13241da177e4SLinus Torvalds return 0; 13251da177e4SLinus Torvalds } 13261da177e4SLinus Torvalds case IP_PKTINFO: 13271da177e4SLinus Torvalds val = (inet->cmsg_flags & IP_CMSG_PKTINFO) != 0; 13281da177e4SLinus Torvalds break; 13291da177e4SLinus Torvalds case IP_RECVTTL: 13301da177e4SLinus Torvalds val = (inet->cmsg_flags & IP_CMSG_TTL) != 0; 13311da177e4SLinus Torvalds break; 13321da177e4SLinus Torvalds case IP_RECVTOS: 13331da177e4SLinus Torvalds val = (inet->cmsg_flags & IP_CMSG_TOS) != 0; 13341da177e4SLinus Torvalds break; 13351da177e4SLinus Torvalds case IP_RECVOPTS: 13361da177e4SLinus Torvalds val = (inet->cmsg_flags & IP_CMSG_RECVOPTS) != 0; 13371da177e4SLinus Torvalds break; 13381da177e4SLinus Torvalds case IP_RETOPTS: 13391da177e4SLinus Torvalds val = (inet->cmsg_flags & IP_CMSG_RETOPTS) != 0; 13401da177e4SLinus Torvalds break; 13412c7946a7SCatherine Zhang case IP_PASSSEC: 13422c7946a7SCatherine Zhang val = (inet->cmsg_flags & IP_CMSG_PASSSEC) != 0; 13432c7946a7SCatherine Zhang break; 1344e8b2dfe9SBalazs Scheidler case IP_RECVORIGDSTADDR: 1345e8b2dfe9SBalazs Scheidler val = (inet->cmsg_flags & IP_CMSG_ORIGDSTADDR) != 0; 1346e8b2dfe9SBalazs Scheidler break; 1347ad6f939aSTom Herbert case IP_CHECKSUM: 1348ad6f939aSTom Herbert val = (inet->cmsg_flags & IP_CMSG_CHECKSUM) != 0; 1349ad6f939aSTom Herbert break; 13501da177e4SLinus Torvalds case IP_TOS: 13511da177e4SLinus Torvalds val = inet->tos; 13521da177e4SLinus Torvalds break; 13531da177e4SLinus Torvalds case IP_TTL: 1354fa50d974SNikolay Borisov { 1355fa50d974SNikolay Borisov struct net *net = sock_net(sk); 13561da177e4SLinus Torvalds val = (inet->uc_ttl == -1 ? 1357fa50d974SNikolay Borisov net->ipv4.sysctl_ip_default_ttl : 13581da177e4SLinus Torvalds inet->uc_ttl); 13591da177e4SLinus Torvalds break; 1360fa50d974SNikolay Borisov } 13611da177e4SLinus Torvalds case IP_HDRINCL: 13621da177e4SLinus Torvalds val = inet->hdrincl; 13631da177e4SLinus Torvalds break; 1364a89b4763SMichael Kerrisk case IP_NODEFRAG: 1365a89b4763SMichael Kerrisk val = inet->nodefrag; 1366a89b4763SMichael Kerrisk break; 136790c337daSEric Dumazet case IP_BIND_ADDRESS_NO_PORT: 136890c337daSEric Dumazet val = inet->bind_address_no_port; 136990c337daSEric Dumazet break; 13701da177e4SLinus Torvalds case IP_MTU_DISCOVER: 13711da177e4SLinus Torvalds val = inet->pmtudisc; 13721da177e4SLinus Torvalds break; 13731da177e4SLinus Torvalds case IP_MTU: 13741da177e4SLinus Torvalds { 13751da177e4SLinus Torvalds struct dst_entry *dst; 13761da177e4SLinus Torvalds val = 0; 13771da177e4SLinus Torvalds dst = sk_dst_get(sk); 13781da177e4SLinus Torvalds if (dst) { 13791da177e4SLinus Torvalds val = dst_mtu(dst); 13801da177e4SLinus Torvalds dst_release(dst); 13811da177e4SLinus Torvalds } 13821da177e4SLinus Torvalds if (!val) { 13831da177e4SLinus Torvalds release_sock(sk); 13841da177e4SLinus Torvalds return -ENOTCONN; 13851da177e4SLinus Torvalds } 13861da177e4SLinus Torvalds break; 13871da177e4SLinus Torvalds } 13881da177e4SLinus Torvalds case IP_RECVERR: 13891da177e4SLinus Torvalds val = inet->recverr; 13901da177e4SLinus Torvalds break; 13911da177e4SLinus Torvalds case IP_MULTICAST_TTL: 13921da177e4SLinus Torvalds val = inet->mc_ttl; 13931da177e4SLinus Torvalds break; 13941da177e4SLinus Torvalds case IP_MULTICAST_LOOP: 13951da177e4SLinus Torvalds val = inet->mc_loop; 13961da177e4SLinus Torvalds break; 139776e21053SErich E. Hoover case IP_UNICAST_IF: 139876e21053SErich E. Hoover val = (__force int)htonl((__u32) inet->uc_index); 139976e21053SErich E. Hoover break; 14001da177e4SLinus Torvalds case IP_MULTICAST_IF: 14011da177e4SLinus Torvalds { 14021da177e4SLinus Torvalds struct in_addr addr; 14031da177e4SLinus Torvalds len = min_t(unsigned int, len, sizeof(struct in_addr)); 14041da177e4SLinus Torvalds addr.s_addr = inet->mc_addr; 14051da177e4SLinus Torvalds release_sock(sk); 14061da177e4SLinus Torvalds 14071da177e4SLinus Torvalds if (put_user(len, optlen)) 14081da177e4SLinus Torvalds return -EFAULT; 14091da177e4SLinus Torvalds if (copy_to_user(optval, &addr, len)) 14101da177e4SLinus Torvalds return -EFAULT; 14111da177e4SLinus Torvalds return 0; 14121da177e4SLinus Torvalds } 14131da177e4SLinus Torvalds case IP_MSFILTER: 14141da177e4SLinus Torvalds { 14151da177e4SLinus Torvalds struct ip_msfilter msf; 14161da177e4SLinus Torvalds 14171da177e4SLinus Torvalds if (len < IP_MSFILTER_SIZE(0)) { 141887e9f031SWANG Cong err = -EINVAL; 141987e9f031SWANG Cong goto out; 14201da177e4SLinus Torvalds } 14211da177e4SLinus Torvalds if (copy_from_user(&msf, optval, IP_MSFILTER_SIZE(0))) { 142287e9f031SWANG Cong err = -EFAULT; 142387e9f031SWANG Cong goto out; 14241da177e4SLinus Torvalds } 14251da177e4SLinus Torvalds err = ip_mc_msfget(sk, &msf, 14261da177e4SLinus Torvalds (struct ip_msfilter __user *)optval, optlen); 142787e9f031SWANG Cong goto out; 14281da177e4SLinus Torvalds } 14291da177e4SLinus Torvalds case MCAST_MSFILTER: 14301da177e4SLinus Torvalds { 14311da177e4SLinus Torvalds struct group_filter gsf; 14321da177e4SLinus Torvalds 14331da177e4SLinus Torvalds if (len < GROUP_FILTER_SIZE(0)) { 143487e9f031SWANG Cong err = -EINVAL; 143587e9f031SWANG Cong goto out; 14361da177e4SLinus Torvalds } 14371da177e4SLinus Torvalds if (copy_from_user(&gsf, optval, GROUP_FILTER_SIZE(0))) { 143887e9f031SWANG Cong err = -EFAULT; 143987e9f031SWANG Cong goto out; 14401da177e4SLinus Torvalds } 14411da177e4SLinus Torvalds err = ip_mc_gsfget(sk, &gsf, 14424d52cfbeSEric Dumazet (struct group_filter __user *)optval, 14434d52cfbeSEric Dumazet optlen); 144487e9f031SWANG Cong goto out; 14451da177e4SLinus Torvalds } 1446f771bef9SNivedita Singhvi case IP_MULTICAST_ALL: 1447f771bef9SNivedita Singhvi val = inet->mc_all; 1448f771bef9SNivedita Singhvi break; 14491da177e4SLinus Torvalds case IP_PKTOPTIONS: 14501da177e4SLinus Torvalds { 14511da177e4SLinus Torvalds struct msghdr msg; 14521da177e4SLinus Torvalds 14531da177e4SLinus Torvalds release_sock(sk); 14541da177e4SLinus Torvalds 14551da177e4SLinus Torvalds if (sk->sk_type != SOCK_STREAM) 14561da177e4SLinus Torvalds return -ENOPROTOOPT; 14571da177e4SLinus Torvalds 1458c54a5e02SKaroly Kemeny msg.msg_control = (__force void *) optval; 14591da177e4SLinus Torvalds msg.msg_controllen = len; 1460dd23198eSDaniel Baluta msg.msg_flags = flags; 14611da177e4SLinus Torvalds 14621da177e4SLinus Torvalds if (inet->cmsg_flags & IP_CMSG_PKTINFO) { 14631da177e4SLinus Torvalds struct in_pktinfo info; 14641da177e4SLinus Torvalds 1465c720c7e8SEric Dumazet info.ipi_addr.s_addr = inet->inet_rcv_saddr; 1466c720c7e8SEric Dumazet info.ipi_spec_dst.s_addr = inet->inet_rcv_saddr; 14671da177e4SLinus Torvalds info.ipi_ifindex = inet->mc_index; 14681da177e4SLinus Torvalds put_cmsg(&msg, SOL_IP, IP_PKTINFO, sizeof(info), &info); 14691da177e4SLinus Torvalds } 14701da177e4SLinus Torvalds if (inet->cmsg_flags & IP_CMSG_TTL) { 14711da177e4SLinus Torvalds int hlim = inet->mc_ttl; 14721da177e4SLinus Torvalds put_cmsg(&msg, SOL_IP, IP_TTL, sizeof(hlim), &hlim); 14731da177e4SLinus Torvalds } 14744c507d28SJiri Benc if (inet->cmsg_flags & IP_CMSG_TOS) { 14754c507d28SJiri Benc int tos = inet->rcv_tos; 14764c507d28SJiri Benc put_cmsg(&msg, SOL_IP, IP_TOS, sizeof(tos), &tos); 14774c507d28SJiri Benc } 14781da177e4SLinus Torvalds len -= msg.msg_controllen; 14791da177e4SLinus Torvalds return put_user(len, optlen); 14801da177e4SLinus Torvalds } 14811da177e4SLinus Torvalds case IP_FREEBIND: 14821da177e4SLinus Torvalds val = inet->freebind; 14831da177e4SLinus Torvalds break; 1484f5715aeaSKOVACS Krisztian case IP_TRANSPARENT: 1485f5715aeaSKOVACS Krisztian val = inet->transparent; 1486f5715aeaSKOVACS Krisztian break; 1487d218d111SStephen Hemminger case IP_MINTTL: 1488d218d111SStephen Hemminger val = inet->min_ttl; 1489d218d111SStephen Hemminger break; 14901da177e4SLinus Torvalds default: 14911da177e4SLinus Torvalds release_sock(sk); 14921da177e4SLinus Torvalds return -ENOPROTOOPT; 14931da177e4SLinus Torvalds } 14941da177e4SLinus Torvalds release_sock(sk); 14951da177e4SLinus Torvalds 1496951e07c9SDavid S. Miller if (len < sizeof(int) && len > 0 && val >= 0 && val <= 255) { 14971da177e4SLinus Torvalds unsigned char ucval = (unsigned char)val; 14981da177e4SLinus Torvalds len = 1; 14991da177e4SLinus Torvalds if (put_user(len, optlen)) 15001da177e4SLinus Torvalds return -EFAULT; 15011da177e4SLinus Torvalds if (copy_to_user(optval, &ucval, 1)) 15021da177e4SLinus Torvalds return -EFAULT; 15031da177e4SLinus Torvalds } else { 15041da177e4SLinus Torvalds len = min_t(unsigned int, sizeof(int), len); 15051da177e4SLinus Torvalds if (put_user(len, optlen)) 15061da177e4SLinus Torvalds return -EFAULT; 15071da177e4SLinus Torvalds if (copy_to_user(optval, &val, len)) 15081da177e4SLinus Torvalds return -EFAULT; 15091da177e4SLinus Torvalds } 15101da177e4SLinus Torvalds return 0; 151187e9f031SWANG Cong 151287e9f031SWANG Cong out: 151387e9f031SWANG Cong release_sock(sk); 151487e9f031SWANG Cong if (needs_rtnl) 151587e9f031SWANG Cong rtnl_unlock(); 151687e9f031SWANG Cong return err; 15171da177e4SLinus Torvalds } 15181da177e4SLinus Torvalds 15193fdadf7dSDmitry Mishin int ip_getsockopt(struct sock *sk, int level, 15203fdadf7dSDmitry Mishin int optname, char __user *optval, int __user *optlen) 15213fdadf7dSDmitry Mishin { 15223fdadf7dSDmitry Mishin int err; 15233fdadf7dSDmitry Mishin 1524dd23198eSDaniel Baluta err = do_ip_getsockopt(sk, level, optname, optval, optlen, 0); 15253fdadf7dSDmitry Mishin #ifdef CONFIG_NETFILTER 15263fdadf7dSDmitry Mishin /* we need to exclude all possible ENOPROTOOPTs except default case */ 15276a9fb947SPavel Emelyanov if (err == -ENOPROTOOPT && optname != IP_PKTOPTIONS && 15286a9fb947SPavel Emelyanov !ip_mroute_opt(optname)) { 15293fdadf7dSDmitry Mishin int len; 15303fdadf7dSDmitry Mishin 15313fdadf7dSDmitry Mishin if (get_user(len, optlen)) 15323fdadf7dSDmitry Mishin return -EFAULT; 15333fdadf7dSDmitry Mishin 15343fdadf7dSDmitry Mishin lock_sock(sk); 15353fdadf7dSDmitry Mishin err = nf_getsockopt(sk, PF_INET, optname, optval, 15363fdadf7dSDmitry Mishin &len); 15373fdadf7dSDmitry Mishin release_sock(sk); 15383fdadf7dSDmitry Mishin if (err >= 0) 15393fdadf7dSDmitry Mishin err = put_user(len, optlen); 15403fdadf7dSDmitry Mishin return err; 15413fdadf7dSDmitry Mishin } 15423fdadf7dSDmitry Mishin #endif 15433fdadf7dSDmitry Mishin return err; 15443fdadf7dSDmitry Mishin } 15454d52cfbeSEric Dumazet EXPORT_SYMBOL(ip_getsockopt); 15463fdadf7dSDmitry Mishin 15473fdadf7dSDmitry Mishin #ifdef CONFIG_COMPAT 1548543d9cfeSArnaldo Carvalho de Melo int compat_ip_getsockopt(struct sock *sk, int level, int optname, 1549543d9cfeSArnaldo Carvalho de Melo char __user *optval, int __user *optlen) 15503fdadf7dSDmitry Mishin { 155142908c69SDavid L Stevens int err; 155242908c69SDavid L Stevens 155342908c69SDavid L Stevens if (optname == MCAST_MSFILTER) 155442908c69SDavid L Stevens return compat_mc_getsockopt(sk, level, optname, optval, optlen, 155542908c69SDavid L Stevens ip_getsockopt); 155642908c69SDavid L Stevens 1557dd23198eSDaniel Baluta err = do_ip_getsockopt(sk, level, optname, optval, optlen, 1558dd23198eSDaniel Baluta MSG_CMSG_COMPAT); 155942908c69SDavid L Stevens 15603fdadf7dSDmitry Mishin #ifdef CONFIG_NETFILTER 15613fdadf7dSDmitry Mishin /* we need to exclude all possible ENOPROTOOPTs except default case */ 15626a9fb947SPavel Emelyanov if (err == -ENOPROTOOPT && optname != IP_PKTOPTIONS && 15636a9fb947SPavel Emelyanov !ip_mroute_opt(optname)) { 15643fdadf7dSDmitry Mishin int len; 15653fdadf7dSDmitry Mishin 15663fdadf7dSDmitry Mishin if (get_user(len, optlen)) 15673fdadf7dSDmitry Mishin return -EFAULT; 15683fdadf7dSDmitry Mishin 15693fdadf7dSDmitry Mishin lock_sock(sk); 1570543d9cfeSArnaldo Carvalho de Melo err = compat_nf_getsockopt(sk, PF_INET, optname, optval, &len); 15713fdadf7dSDmitry Mishin release_sock(sk); 15723fdadf7dSDmitry Mishin if (err >= 0) 15733fdadf7dSDmitry Mishin err = put_user(len, optlen); 15743fdadf7dSDmitry Mishin return err; 15753fdadf7dSDmitry Mishin } 15763fdadf7dSDmitry Mishin #endif 15773fdadf7dSDmitry Mishin return err; 15783fdadf7dSDmitry Mishin } 1579543d9cfeSArnaldo Carvalho de Melo EXPORT_SYMBOL(compat_ip_getsockopt); 15803fdadf7dSDmitry Mishin #endif 1581