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 100*70ecc248SWillem de Bruijn static void ip_cmsg_recv_fragsize(struct msghdr *msg, struct sk_buff *skb) 101*70ecc248SWillem de Bruijn { 102*70ecc248SWillem de Bruijn int val; 103*70ecc248SWillem de Bruijn 104*70ecc248SWillem de Bruijn if (IPCB(skb)->frag_max_size == 0) 105*70ecc248SWillem de Bruijn return; 106*70ecc248SWillem de Bruijn 107*70ecc248SWillem de Bruijn val = IPCB(skb)->frag_max_size; 108*70ecc248SWillem de Bruijn put_cmsg(msg, SOL_IP, IP_RECVFRAGSIZE, sizeof(val), &val); 109*70ecc248SWillem de Bruijn } 110*70ecc248SWillem de Bruijn 111ad6f939aSTom Herbert static void ip_cmsg_recv_checksum(struct msghdr *msg, struct sk_buff *skb, 11210df8e61SEric Dumazet int tlen, int offset) 113ad6f939aSTom Herbert { 114ad6f939aSTom Herbert __wsum csum = skb->csum; 115ad6f939aSTom Herbert 116ad6f939aSTom Herbert if (skb->ip_summed != CHECKSUM_COMPLETE) 117ad6f939aSTom Herbert return; 118ad6f939aSTom Herbert 119ad6f939aSTom Herbert if (offset != 0) 12010df8e61SEric Dumazet csum = csum_sub(csum, 12110df8e61SEric Dumazet csum_partial(skb_transport_header(skb) + tlen, 12231c2e492SWillem de Bruijn offset, 0)); 123ad6f939aSTom Herbert 124ad6f939aSTom Herbert put_cmsg(msg, SOL_IP, IP_CHECKSUM, sizeof(__wsum), &csum); 125ad6f939aSTom Herbert } 126ad6f939aSTom Herbert 1272c7946a7SCatherine Zhang static void ip_cmsg_recv_security(struct msghdr *msg, struct sk_buff *skb) 1282c7946a7SCatherine Zhang { 1292c7946a7SCatherine Zhang char *secdata; 130dc49c1f9SCatherine Zhang u32 seclen, secid; 1312c7946a7SCatherine Zhang int err; 1322c7946a7SCatherine Zhang 133dc49c1f9SCatherine Zhang err = security_socket_getpeersec_dgram(NULL, skb, &secid); 134dc49c1f9SCatherine Zhang if (err) 135dc49c1f9SCatherine Zhang return; 136dc49c1f9SCatherine Zhang 137dc49c1f9SCatherine Zhang err = security_secid_to_secctx(secid, &secdata, &seclen); 1382c7946a7SCatherine Zhang if (err) 1392c7946a7SCatherine Zhang return; 1402c7946a7SCatherine Zhang 1412c7946a7SCatherine Zhang put_cmsg(msg, SOL_IP, SCM_SECURITY, seclen, secdata); 142dc49c1f9SCatherine Zhang security_release_secctx(secdata, seclen); 1432c7946a7SCatherine Zhang } 1442c7946a7SCatherine Zhang 14521d1a161SHarvey Harrison static void ip_cmsg_recv_dstaddr(struct msghdr *msg, struct sk_buff *skb) 146e8b2dfe9SBalazs Scheidler { 147e8b2dfe9SBalazs Scheidler struct sockaddr_in sin; 148b71d1d42SEric Dumazet const struct iphdr *iph = ip_hdr(skb); 14921d1a161SHarvey Harrison __be16 *ports = (__be16 *)skb_transport_header(skb); 150e8b2dfe9SBalazs Scheidler 151e8b2dfe9SBalazs Scheidler if (skb_transport_offset(skb) + 4 > skb->len) 152e8b2dfe9SBalazs Scheidler return; 153e8b2dfe9SBalazs Scheidler 154e8b2dfe9SBalazs Scheidler /* All current transport protocols have the port numbers in the 155e8b2dfe9SBalazs Scheidler * first four bytes of the transport header and this function is 156e8b2dfe9SBalazs Scheidler * written with this assumption in mind. 157e8b2dfe9SBalazs Scheidler */ 158e8b2dfe9SBalazs Scheidler 159e8b2dfe9SBalazs Scheidler sin.sin_family = AF_INET; 160e8b2dfe9SBalazs Scheidler sin.sin_addr.s_addr = iph->daddr; 161e8b2dfe9SBalazs Scheidler sin.sin_port = ports[1]; 162e8b2dfe9SBalazs Scheidler memset(sin.sin_zero, 0, sizeof(sin.sin_zero)); 163e8b2dfe9SBalazs Scheidler 164e8b2dfe9SBalazs Scheidler put_cmsg(msg, SOL_IP, IP_ORIGDSTADDR, sizeof(sin), &sin); 165e8b2dfe9SBalazs Scheidler } 1661da177e4SLinus Torvalds 1675961de9fSTom Herbert void ip_cmsg_recv_offset(struct msghdr *msg, struct sk_buff *skb, 16810df8e61SEric Dumazet int tlen, int offset) 1691da177e4SLinus Torvalds { 1701da177e4SLinus Torvalds struct inet_sock *inet = inet_sk(skb->sk); 17195c96174SEric Dumazet unsigned int flags = inet->cmsg_flags; 1721da177e4SLinus Torvalds 1731da177e4SLinus Torvalds /* Ordered by supposed usage frequency */ 174c44d13d6STom Herbert if (flags & IP_CMSG_PKTINFO) { 1751da177e4SLinus Torvalds ip_cmsg_recv_pktinfo(msg, skb); 1761da177e4SLinus Torvalds 177c44d13d6STom Herbert flags &= ~IP_CMSG_PKTINFO; 178c44d13d6STom Herbert if (!flags) 179c44d13d6STom Herbert return; 180c44d13d6STom Herbert } 181c44d13d6STom Herbert 182c44d13d6STom Herbert if (flags & IP_CMSG_TTL) { 1831da177e4SLinus Torvalds ip_cmsg_recv_ttl(msg, skb); 1841da177e4SLinus Torvalds 185c44d13d6STom Herbert flags &= ~IP_CMSG_TTL; 186c44d13d6STom Herbert if (!flags) 187c44d13d6STom Herbert return; 188c44d13d6STom Herbert } 189c44d13d6STom Herbert 190c44d13d6STom Herbert if (flags & IP_CMSG_TOS) { 1911da177e4SLinus Torvalds ip_cmsg_recv_tos(msg, skb); 1921da177e4SLinus Torvalds 193c44d13d6STom Herbert flags &= ~IP_CMSG_TOS; 194c44d13d6STom Herbert if (!flags) 195c44d13d6STom Herbert return; 196c44d13d6STom Herbert } 197c44d13d6STom Herbert 198c44d13d6STom Herbert if (flags & IP_CMSG_RECVOPTS) { 1991da177e4SLinus Torvalds ip_cmsg_recv_opts(msg, skb); 2001da177e4SLinus Torvalds 201c44d13d6STom Herbert flags &= ~IP_CMSG_RECVOPTS; 202c44d13d6STom Herbert if (!flags) 203c44d13d6STom Herbert return; 204c44d13d6STom Herbert } 205c44d13d6STom Herbert 206c44d13d6STom Herbert if (flags & IP_CMSG_RETOPTS) { 2071da177e4SLinus Torvalds ip_cmsg_recv_retopts(msg, skb); 2082c7946a7SCatherine Zhang 209c44d13d6STom Herbert flags &= ~IP_CMSG_RETOPTS; 210c44d13d6STom Herbert if (!flags) 211c44d13d6STom Herbert return; 212c44d13d6STom Herbert } 213c44d13d6STom Herbert 214c44d13d6STom Herbert if (flags & IP_CMSG_PASSSEC) { 2152c7946a7SCatherine Zhang ip_cmsg_recv_security(msg, skb); 216e8b2dfe9SBalazs Scheidler 217c44d13d6STom Herbert flags &= ~IP_CMSG_PASSSEC; 218c44d13d6STom Herbert if (!flags) 219e8b2dfe9SBalazs Scheidler return; 220c44d13d6STom Herbert } 221c44d13d6STom Herbert 222ad6f939aSTom Herbert if (flags & IP_CMSG_ORIGDSTADDR) { 223e8b2dfe9SBalazs Scheidler ip_cmsg_recv_dstaddr(msg, skb); 224e8b2dfe9SBalazs Scheidler 225ad6f939aSTom Herbert flags &= ~IP_CMSG_ORIGDSTADDR; 226ad6f939aSTom Herbert if (!flags) 227ad6f939aSTom Herbert return; 228ad6f939aSTom Herbert } 229ad6f939aSTom Herbert 230ad6f939aSTom Herbert if (flags & IP_CMSG_CHECKSUM) 23110df8e61SEric Dumazet ip_cmsg_recv_checksum(msg, skb, tlen, offset); 232*70ecc248SWillem de Bruijn 233*70ecc248SWillem de Bruijn if (flags & IP_CMSG_RECVFRAGSIZE) 234*70ecc248SWillem de Bruijn ip_cmsg_recv_fragsize(msg, skb); 2351da177e4SLinus Torvalds } 2365961de9fSTom Herbert EXPORT_SYMBOL(ip_cmsg_recv_offset); 2371da177e4SLinus Torvalds 23824025c46SSoheil Hassas Yeganeh int ip_cmsg_send(struct sock *sk, struct msghdr *msg, struct ipcm_cookie *ipc, 239c8e6ad08SHannes Frederic Sowa bool allow_ipv6) 2401da177e4SLinus Torvalds { 241f02db315SFrancesco Fusco int err, val; 2421da177e4SLinus Torvalds struct cmsghdr *cmsg; 24324025c46SSoheil Hassas Yeganeh struct net *net = sock_net(sk); 2441da177e4SLinus Torvalds 245f95b414eSGu Zheng for_each_cmsghdr(cmsg, msg) { 2461da177e4SLinus Torvalds if (!CMSG_OK(msg, cmsg)) 2471da177e4SLinus Torvalds return -EINVAL; 2485337b5b7SEric Dumazet #if IS_ENABLED(CONFIG_IPV6) 249c8e6ad08SHannes Frederic Sowa if (allow_ipv6 && 250c8e6ad08SHannes Frederic Sowa cmsg->cmsg_level == SOL_IPV6 && 251c8e6ad08SHannes Frederic Sowa cmsg->cmsg_type == IPV6_PKTINFO) { 252c8e6ad08SHannes Frederic Sowa struct in6_pktinfo *src_info; 253c8e6ad08SHannes Frederic Sowa 254c8e6ad08SHannes Frederic Sowa if (cmsg->cmsg_len < CMSG_LEN(sizeof(*src_info))) 255c8e6ad08SHannes Frederic Sowa return -EINVAL; 256c8e6ad08SHannes Frederic Sowa src_info = (struct in6_pktinfo *)CMSG_DATA(cmsg); 257c8e6ad08SHannes Frederic Sowa if (!ipv6_addr_v4mapped(&src_info->ipi6_addr)) 258c8e6ad08SHannes Frederic Sowa return -EINVAL; 259c8e6ad08SHannes Frederic Sowa ipc->oif = src_info->ipi6_ifindex; 260c8e6ad08SHannes Frederic Sowa ipc->addr = src_info->ipi6_addr.s6_addr32[3]; 261c8e6ad08SHannes Frederic Sowa continue; 262c8e6ad08SHannes Frederic Sowa } 263c8e6ad08SHannes Frederic Sowa #endif 26424025c46SSoheil Hassas Yeganeh if (cmsg->cmsg_level == SOL_SOCKET) { 2652632616bSEric Dumazet err = __sock_cmsg_send(sk, msg, cmsg, &ipc->sockc); 2662632616bSEric Dumazet if (err) 2672632616bSEric Dumazet return err; 26824025c46SSoheil Hassas Yeganeh continue; 26924025c46SSoheil Hassas Yeganeh } 27024025c46SSoheil Hassas Yeganeh 2711da177e4SLinus Torvalds if (cmsg->cmsg_level != SOL_IP) 2721da177e4SLinus Torvalds continue; 2731da177e4SLinus Torvalds switch (cmsg->cmsg_type) { 2741da177e4SLinus Torvalds case IP_RETOPTS: 2751da177e4SLinus Torvalds err = cmsg->cmsg_len - CMSG_ALIGN(sizeof(struct cmsghdr)); 27691948309SEric Dumazet 27791948309SEric Dumazet /* Our caller is responsible for freeing ipc->opt */ 2784d52cfbeSEric Dumazet err = ip_options_get(net, &ipc->opt, CMSG_DATA(cmsg), 2794d52cfbeSEric Dumazet err < 40 ? err : 40); 2801da177e4SLinus Torvalds if (err) 2811da177e4SLinus Torvalds return err; 2821da177e4SLinus Torvalds break; 2831da177e4SLinus Torvalds case IP_PKTINFO: 2841da177e4SLinus Torvalds { 2851da177e4SLinus Torvalds struct in_pktinfo *info; 2861da177e4SLinus Torvalds if (cmsg->cmsg_len != CMSG_LEN(sizeof(struct in_pktinfo))) 2871da177e4SLinus Torvalds return -EINVAL; 2881da177e4SLinus Torvalds info = (struct in_pktinfo *)CMSG_DATA(cmsg); 2891da177e4SLinus Torvalds ipc->oif = info->ipi_ifindex; 2901da177e4SLinus Torvalds ipc->addr = info->ipi_spec_dst.s_addr; 2911da177e4SLinus Torvalds break; 2921da177e4SLinus Torvalds } 293f02db315SFrancesco Fusco case IP_TTL: 294f02db315SFrancesco Fusco if (cmsg->cmsg_len != CMSG_LEN(sizeof(int))) 295f02db315SFrancesco Fusco return -EINVAL; 296f02db315SFrancesco Fusco val = *(int *)CMSG_DATA(cmsg); 297f02db315SFrancesco Fusco if (val < 1 || val > 255) 298f02db315SFrancesco Fusco return -EINVAL; 299f02db315SFrancesco Fusco ipc->ttl = val; 300f02db315SFrancesco Fusco break; 301f02db315SFrancesco Fusco case IP_TOS: 302e895cdceSEric Dumazet if (cmsg->cmsg_len == CMSG_LEN(sizeof(int))) 303f02db315SFrancesco Fusco val = *(int *)CMSG_DATA(cmsg); 304e895cdceSEric Dumazet else if (cmsg->cmsg_len == CMSG_LEN(sizeof(u8))) 305e895cdceSEric Dumazet val = *(u8 *)CMSG_DATA(cmsg); 306e895cdceSEric Dumazet else 307e895cdceSEric Dumazet return -EINVAL; 308f02db315SFrancesco Fusco if (val < 0 || val > 255) 309f02db315SFrancesco Fusco return -EINVAL; 310f02db315SFrancesco Fusco ipc->tos = val; 311f02db315SFrancesco Fusco ipc->priority = rt_tos2priority(ipc->tos); 312f02db315SFrancesco Fusco break; 313f02db315SFrancesco Fusco 3141da177e4SLinus Torvalds default: 3151da177e4SLinus Torvalds return -EINVAL; 3161da177e4SLinus Torvalds } 3171da177e4SLinus Torvalds } 3181da177e4SLinus Torvalds return 0; 3191da177e4SLinus Torvalds } 3201da177e4SLinus Torvalds 3211da177e4SLinus Torvalds 3221da177e4SLinus Torvalds /* Special input handler for packets caught by router alert option. 3231da177e4SLinus Torvalds They are selected only by protocol field, and then processed likely 3241da177e4SLinus Torvalds local ones; but only if someone wants them! Otherwise, router 3251da177e4SLinus Torvalds not running rsvpd will kill RSVP. 3261da177e4SLinus Torvalds 3271da177e4SLinus Torvalds It is user level problem, what it will make with them. 3281da177e4SLinus Torvalds I have no idea, how it will masquearde or NAT them (it is joke, joke :-)), 3291da177e4SLinus Torvalds but receiver should be enough clever f.e. to forward mtrace requests, 3301da177e4SLinus Torvalds sent to multicast group to reach destination designated router. 3311da177e4SLinus Torvalds */ 33243a951e9SEric Dumazet struct ip_ra_chain __rcu *ip_ra_chain; 33366018506SEric Dumazet static DEFINE_SPINLOCK(ip_ra_lock); 33466018506SEric Dumazet 335592fcb9dSEric Dumazet 336592fcb9dSEric Dumazet static void ip_ra_destroy_rcu(struct rcu_head *head) 33766018506SEric Dumazet { 338592fcb9dSEric Dumazet struct ip_ra_chain *ra = container_of(head, struct ip_ra_chain, rcu); 339592fcb9dSEric Dumazet 340592fcb9dSEric Dumazet sock_put(ra->saved_sk); 341592fcb9dSEric Dumazet kfree(ra); 34266018506SEric Dumazet } 3431da177e4SLinus Torvalds 3444d52cfbeSEric Dumazet int ip_ra_control(struct sock *sk, unsigned char on, 3454d52cfbeSEric Dumazet void (*destructor)(struct sock *)) 3461da177e4SLinus Torvalds { 34743a951e9SEric Dumazet struct ip_ra_chain *ra, *new_ra; 34843a951e9SEric Dumazet struct ip_ra_chain __rcu **rap; 3491da177e4SLinus Torvalds 350c720c7e8SEric Dumazet if (sk->sk_type != SOCK_RAW || inet_sk(sk)->inet_num == IPPROTO_RAW) 3511da177e4SLinus Torvalds return -EINVAL; 3521da177e4SLinus Torvalds 3531da177e4SLinus Torvalds new_ra = on ? kmalloc(sizeof(*new_ra), GFP_KERNEL) : NULL; 3541da177e4SLinus Torvalds 35566018506SEric Dumazet spin_lock_bh(&ip_ra_lock); 35643a951e9SEric Dumazet for (rap = &ip_ra_chain; 35743a951e9SEric Dumazet (ra = rcu_dereference_protected(*rap, 35843a951e9SEric Dumazet lockdep_is_held(&ip_ra_lock))) != NULL; 35943a951e9SEric Dumazet rap = &ra->next) { 3601da177e4SLinus Torvalds if (ra->sk == sk) { 3611da177e4SLinus Torvalds if (on) { 36266018506SEric Dumazet spin_unlock_bh(&ip_ra_lock); 3631da177e4SLinus Torvalds kfree(new_ra); 3641da177e4SLinus Torvalds return -EADDRINUSE; 3651da177e4SLinus Torvalds } 366592fcb9dSEric Dumazet /* dont let ip_call_ra_chain() use sk again */ 367592fcb9dSEric Dumazet ra->sk = NULL; 3688e380f00SEric Dumazet RCU_INIT_POINTER(*rap, ra->next); 36966018506SEric Dumazet spin_unlock_bh(&ip_ra_lock); 3701da177e4SLinus Torvalds 3711da177e4SLinus Torvalds if (ra->destructor) 3721da177e4SLinus Torvalds ra->destructor(sk); 373592fcb9dSEric Dumazet /* 374592fcb9dSEric Dumazet * Delay sock_put(sk) and kfree(ra) after one rcu grace 375592fcb9dSEric Dumazet * period. This guarantee ip_call_ra_chain() dont need 376592fcb9dSEric Dumazet * to mess with socket refcounts. 377592fcb9dSEric Dumazet */ 378592fcb9dSEric Dumazet ra->saved_sk = sk; 379592fcb9dSEric Dumazet call_rcu(&ra->rcu, ip_ra_destroy_rcu); 3801da177e4SLinus Torvalds return 0; 3811da177e4SLinus Torvalds } 3821da177e4SLinus Torvalds } 38351456b29SIan Morris if (!new_ra) { 38466018506SEric Dumazet spin_unlock_bh(&ip_ra_lock); 3851da177e4SLinus Torvalds return -ENOBUFS; 3861da177e4SLinus Torvalds } 3871da177e4SLinus Torvalds new_ra->sk = sk; 3881da177e4SLinus Torvalds new_ra->destructor = destructor; 3891da177e4SLinus Torvalds 3908e380f00SEric Dumazet RCU_INIT_POINTER(new_ra->next, ra); 39166018506SEric Dumazet rcu_assign_pointer(*rap, new_ra); 3921da177e4SLinus Torvalds sock_hold(sk); 39366018506SEric Dumazet spin_unlock_bh(&ip_ra_lock); 3941da177e4SLinus Torvalds 3951da177e4SLinus Torvalds return 0; 3961da177e4SLinus Torvalds } 3971da177e4SLinus Torvalds 3981da177e4SLinus Torvalds void ip_icmp_error(struct sock *sk, struct sk_buff *skb, int err, 39935986b32SAl Viro __be16 port, u32 info, u8 *payload) 4001da177e4SLinus Torvalds { 4011da177e4SLinus Torvalds struct sock_exterr_skb *serr; 4021da177e4SLinus Torvalds 4031da177e4SLinus Torvalds skb = skb_clone(skb, GFP_ATOMIC); 4041da177e4SLinus Torvalds if (!skb) 4051da177e4SLinus Torvalds return; 4061da177e4SLinus Torvalds 4071da177e4SLinus Torvalds serr = SKB_EXT_ERR(skb); 4081da177e4SLinus Torvalds serr->ee.ee_errno = err; 4091da177e4SLinus Torvalds serr->ee.ee_origin = SO_EE_ORIGIN_ICMP; 41088c7664fSArnaldo Carvalho de Melo serr->ee.ee_type = icmp_hdr(skb)->type; 41188c7664fSArnaldo Carvalho de Melo serr->ee.ee_code = icmp_hdr(skb)->code; 4121da177e4SLinus Torvalds serr->ee.ee_pad = 0; 4131da177e4SLinus Torvalds serr->ee.ee_info = info; 4141da177e4SLinus Torvalds serr->ee.ee_data = 0; 41588c7664fSArnaldo Carvalho de Melo serr->addr_offset = (u8 *)&(((struct iphdr *)(icmp_hdr(skb) + 1))->daddr) - 416d56f90a7SArnaldo Carvalho de Melo skb_network_header(skb); 4171da177e4SLinus Torvalds serr->port = port; 4181da177e4SLinus Torvalds 41900db4124SIan Morris if (skb_pull(skb, payload - skb->data)) { 420bd82393cSArnaldo Carvalho de Melo skb_reset_transport_header(skb); 421bd82393cSArnaldo Carvalho de Melo if (sock_queue_err_skb(sk, skb) == 0) 422bd82393cSArnaldo Carvalho de Melo return; 423bd82393cSArnaldo Carvalho de Melo } 4241da177e4SLinus Torvalds kfree_skb(skb); 4251da177e4SLinus Torvalds } 4261da177e4SLinus Torvalds 4270579016eSAl Viro void ip_local_error(struct sock *sk, int err, __be32 daddr, __be16 port, u32 info) 4281da177e4SLinus Torvalds { 4291da177e4SLinus Torvalds struct inet_sock *inet = inet_sk(sk); 4301da177e4SLinus Torvalds struct sock_exterr_skb *serr; 4311da177e4SLinus Torvalds struct iphdr *iph; 4321da177e4SLinus Torvalds struct sk_buff *skb; 4331da177e4SLinus Torvalds 4341da177e4SLinus Torvalds if (!inet->recverr) 4351da177e4SLinus Torvalds return; 4361da177e4SLinus Torvalds 4371da177e4SLinus Torvalds skb = alloc_skb(sizeof(struct iphdr), GFP_ATOMIC); 4381da177e4SLinus Torvalds if (!skb) 4391da177e4SLinus Torvalds return; 4401da177e4SLinus Torvalds 4412ca9e6f2SArnaldo Carvalho de Melo skb_put(skb, sizeof(struct iphdr)); 4422ca9e6f2SArnaldo Carvalho de Melo skb_reset_network_header(skb); 443eddc9ec5SArnaldo Carvalho de Melo iph = ip_hdr(skb); 4441da177e4SLinus Torvalds iph->daddr = daddr; 4451da177e4SLinus Torvalds 4461da177e4SLinus Torvalds serr = SKB_EXT_ERR(skb); 4471da177e4SLinus Torvalds serr->ee.ee_errno = err; 4481da177e4SLinus Torvalds serr->ee.ee_origin = SO_EE_ORIGIN_LOCAL; 4491da177e4SLinus Torvalds serr->ee.ee_type = 0; 4501da177e4SLinus Torvalds serr->ee.ee_code = 0; 4511da177e4SLinus Torvalds serr->ee.ee_pad = 0; 4521da177e4SLinus Torvalds serr->ee.ee_info = info; 4531da177e4SLinus Torvalds serr->ee.ee_data = 0; 454d56f90a7SArnaldo Carvalho de Melo serr->addr_offset = (u8 *)&iph->daddr - skb_network_header(skb); 4551da177e4SLinus Torvalds serr->port = port; 4561da177e4SLinus Torvalds 45727a884dcSArnaldo Carvalho de Melo __skb_pull(skb, skb_tail_pointer(skb) - skb->data); 458bd82393cSArnaldo Carvalho de Melo skb_reset_transport_header(skb); 4591da177e4SLinus Torvalds 4601da177e4SLinus Torvalds if (sock_queue_err_skb(sk, skb)) 4611da177e4SLinus Torvalds kfree_skb(skb); 4621da177e4SLinus Torvalds } 4631da177e4SLinus Torvalds 46434b99df4SJulian Anastasov /* For some errors we have valid addr_offset even with zero payload and 46534b99df4SJulian Anastasov * zero port. Also, addr_offset should be supported if port is set. 46634b99df4SJulian Anastasov */ 46734b99df4SJulian Anastasov static inline bool ipv4_datagram_support_addr(struct sock_exterr_skb *serr) 46834b99df4SJulian Anastasov { 46934b99df4SJulian Anastasov return serr->ee.ee_origin == SO_EE_ORIGIN_ICMP || 47034b99df4SJulian Anastasov serr->ee.ee_origin == SO_EE_ORIGIN_LOCAL || serr->port; 47134b99df4SJulian Anastasov } 47234b99df4SJulian Anastasov 473c247f053SWillem de Bruijn /* IPv4 supports cmsg on all imcp errors and some timestamps 474c247f053SWillem de Bruijn * 475c247f053SWillem de Bruijn * Timestamp code paths do not initialize the fields expected by cmsg: 476c247f053SWillem de Bruijn * the PKTINFO fields in skb->cb[]. Fill those in here. 477c247f053SWillem de Bruijn */ 478c247f053SWillem de Bruijn static bool ipv4_datagram_support_cmsg(const struct sock *sk, 479c247f053SWillem de Bruijn struct sk_buff *skb, 480829ae9d6SWillem de Bruijn int ee_origin) 481829ae9d6SWillem de Bruijn { 482c247f053SWillem de Bruijn struct in_pktinfo *info; 483829ae9d6SWillem de Bruijn 484c247f053SWillem de Bruijn if (ee_origin == SO_EE_ORIGIN_ICMP) 485c247f053SWillem de Bruijn return true; 486c247f053SWillem de Bruijn 487c247f053SWillem de Bruijn if (ee_origin == SO_EE_ORIGIN_LOCAL) 488c247f053SWillem de Bruijn return false; 489c247f053SWillem de Bruijn 490c247f053SWillem de Bruijn /* Support IP_PKTINFO on tstamp packets if requested, to correlate 491c247f053SWillem de Bruijn * timestamp with egress dev. Not possible for packets without dev 492c247f053SWillem de Bruijn * or without payload (SOF_TIMESTAMPING_OPT_TSONLY). 493c247f053SWillem de Bruijn */ 494c247f053SWillem de Bruijn if ((!(sk->sk_tsflags & SOF_TIMESTAMPING_OPT_CMSG)) || 495829ae9d6SWillem de Bruijn (!skb->dev)) 496829ae9d6SWillem de Bruijn return false; 497829ae9d6SWillem de Bruijn 498c247f053SWillem de Bruijn info = PKTINFO_SKB_CB(skb); 499829ae9d6SWillem de Bruijn info->ipi_spec_dst.s_addr = ip_hdr(skb)->saddr; 500829ae9d6SWillem de Bruijn info->ipi_ifindex = skb->dev->ifindex; 501829ae9d6SWillem de Bruijn return true; 502829ae9d6SWillem de Bruijn } 503829ae9d6SWillem de Bruijn 5041da177e4SLinus Torvalds /* 5051da177e4SLinus Torvalds * Handle MSG_ERRQUEUE 5061da177e4SLinus Torvalds */ 50785fbaa75SHannes Frederic Sowa int ip_recv_error(struct sock *sk, struct msghdr *msg, int len, int *addr_len) 5081da177e4SLinus Torvalds { 5091da177e4SLinus Torvalds struct sock_exterr_skb *serr; 510364a9e93SWillem de Bruijn struct sk_buff *skb; 511342dfc30SSteffen Hurrle DECLARE_SOCKADDR(struct sockaddr_in *, sin, msg->msg_name); 5121da177e4SLinus Torvalds struct { 5131da177e4SLinus Torvalds struct sock_extended_err ee; 5141da177e4SLinus Torvalds struct sockaddr_in offender; 5151da177e4SLinus Torvalds } errhdr; 5161da177e4SLinus Torvalds int err; 5171da177e4SLinus Torvalds int copied; 5181da177e4SLinus Torvalds 5197ce875e5SWillem de Bruijn WARN_ON_ONCE(sk->sk_family == AF_INET6); 5207ce875e5SWillem de Bruijn 5211da177e4SLinus Torvalds err = -EAGAIN; 522364a9e93SWillem de Bruijn skb = sock_dequeue_err_skb(sk); 52351456b29SIan Morris if (!skb) 5241da177e4SLinus Torvalds goto out; 5251da177e4SLinus Torvalds 5261da177e4SLinus Torvalds copied = skb->len; 5271da177e4SLinus Torvalds if (copied > len) { 5281da177e4SLinus Torvalds msg->msg_flags |= MSG_TRUNC; 5291da177e4SLinus Torvalds copied = len; 5301da177e4SLinus Torvalds } 53151f3d02bSDavid S. Miller err = skb_copy_datagram_msg(skb, 0, msg, copied); 532960a2628SEric Dumazet if (unlikely(err)) { 533960a2628SEric Dumazet kfree_skb(skb); 534960a2628SEric Dumazet return err; 535960a2628SEric Dumazet } 5361da177e4SLinus Torvalds sock_recv_timestamp(msg, sk, skb); 5371da177e4SLinus Torvalds 5381da177e4SLinus Torvalds serr = SKB_EXT_ERR(skb); 5391da177e4SLinus Torvalds 54034b99df4SJulian Anastasov if (sin && ipv4_datagram_support_addr(serr)) { 5411da177e4SLinus Torvalds sin->sin_family = AF_INET; 542d56f90a7SArnaldo Carvalho de Melo sin->sin_addr.s_addr = *(__be32 *)(skb_network_header(skb) + 543d56f90a7SArnaldo Carvalho de Melo serr->addr_offset); 5441da177e4SLinus Torvalds sin->sin_port = serr->port; 5451da177e4SLinus Torvalds memset(&sin->sin_zero, 0, sizeof(sin->sin_zero)); 54685fbaa75SHannes Frederic Sowa *addr_len = sizeof(*sin); 5471da177e4SLinus Torvalds } 5481da177e4SLinus Torvalds 5491da177e4SLinus Torvalds memcpy(&errhdr.ee, &serr->ee, sizeof(struct sock_extended_err)); 5501da177e4SLinus Torvalds sin = &errhdr.offender; 551f812116bSWillem de Bruijn memset(sin, 0, sizeof(*sin)); 552829ae9d6SWillem de Bruijn 553c247f053SWillem de Bruijn if (ipv4_datagram_support_cmsg(sk, skb, serr->ee.ee_origin)) { 5541da177e4SLinus Torvalds sin->sin_family = AF_INET; 555eddc9ec5SArnaldo Carvalho de Melo sin->sin_addr.s_addr = ip_hdr(skb)->saddr; 556f812116bSWillem de Bruijn if (inet_sk(sk)->cmsg_flags) 5571da177e4SLinus Torvalds ip_cmsg_recv(msg, skb); 5581da177e4SLinus Torvalds } 5591da177e4SLinus Torvalds 5601da177e4SLinus Torvalds put_cmsg(msg, SOL_IP, IP_RECVERR, sizeof(errhdr), &errhdr); 5611da177e4SLinus Torvalds 5621da177e4SLinus Torvalds /* Now we could try to dump offended packet options */ 5631da177e4SLinus Torvalds 5641da177e4SLinus Torvalds msg->msg_flags |= MSG_ERRQUEUE; 5651da177e4SLinus Torvalds err = copied; 5661da177e4SLinus Torvalds 567960a2628SEric Dumazet consume_skb(skb); 5681da177e4SLinus Torvalds out: 5691da177e4SLinus Torvalds return err; 5701da177e4SLinus Torvalds } 5711da177e4SLinus Torvalds 5721da177e4SLinus Torvalds 5731da177e4SLinus Torvalds /* 5744d52cfbeSEric Dumazet * Socket option code for IP. This is the end of the line after any 5754d52cfbeSEric Dumazet * TCP,UDP etc options on an IP socket. 5761da177e4SLinus Torvalds */ 577baf606d9SMarcelo Ricardo Leitner static bool setsockopt_needs_rtnl(int optname) 578baf606d9SMarcelo Ricardo Leitner { 579baf606d9SMarcelo Ricardo Leitner switch (optname) { 580baf606d9SMarcelo Ricardo Leitner case IP_ADD_MEMBERSHIP: 581baf606d9SMarcelo Ricardo Leitner case IP_ADD_SOURCE_MEMBERSHIP: 58254ff9ef3SMarcelo Ricardo Leitner case IP_BLOCK_SOURCE: 583baf606d9SMarcelo Ricardo Leitner case IP_DROP_MEMBERSHIP: 58454ff9ef3SMarcelo Ricardo Leitner case IP_DROP_SOURCE_MEMBERSHIP: 58554ff9ef3SMarcelo Ricardo Leitner case IP_MSFILTER: 58654ff9ef3SMarcelo Ricardo Leitner case IP_UNBLOCK_SOURCE: 58754ff9ef3SMarcelo Ricardo Leitner case MCAST_BLOCK_SOURCE: 58854ff9ef3SMarcelo Ricardo Leitner case MCAST_MSFILTER: 589baf606d9SMarcelo Ricardo Leitner case MCAST_JOIN_GROUP: 59054ff9ef3SMarcelo Ricardo Leitner case MCAST_JOIN_SOURCE_GROUP: 591baf606d9SMarcelo Ricardo Leitner case MCAST_LEAVE_GROUP: 59254ff9ef3SMarcelo Ricardo Leitner case MCAST_LEAVE_SOURCE_GROUP: 59354ff9ef3SMarcelo Ricardo Leitner case MCAST_UNBLOCK_SOURCE: 594baf606d9SMarcelo Ricardo Leitner return true; 595baf606d9SMarcelo Ricardo Leitner } 596baf606d9SMarcelo Ricardo Leitner return false; 597baf606d9SMarcelo Ricardo Leitner } 5981da177e4SLinus Torvalds 5993fdadf7dSDmitry Mishin static int do_ip_setsockopt(struct sock *sk, int level, 600b7058842SDavid S. Miller int optname, char __user *optval, unsigned int optlen) 6011da177e4SLinus Torvalds { 6021da177e4SLinus Torvalds struct inet_sock *inet = inet_sk(sk); 603166b6b2dSNikolay Borisov struct net *net = sock_net(sk); 6041da177e4SLinus Torvalds int val = 0, err; 605baf606d9SMarcelo Ricardo Leitner bool needs_rtnl = setsockopt_needs_rtnl(optname); 6061da177e4SLinus Torvalds 6070c9f79beSXi Wang switch (optname) { 6080c9f79beSXi Wang case IP_PKTINFO: 6090c9f79beSXi Wang case IP_RECVTTL: 6100c9f79beSXi Wang case IP_RECVOPTS: 6110c9f79beSXi Wang case IP_RECVTOS: 6120c9f79beSXi Wang case IP_RETOPTS: 6130c9f79beSXi Wang case IP_TOS: 6140c9f79beSXi Wang case IP_TTL: 6150c9f79beSXi Wang case IP_HDRINCL: 6160c9f79beSXi Wang case IP_MTU_DISCOVER: 6170c9f79beSXi Wang case IP_RECVERR: 6180c9f79beSXi Wang case IP_ROUTER_ALERT: 6190c9f79beSXi Wang case IP_FREEBIND: 6200c9f79beSXi Wang case IP_PASSSEC: 6210c9f79beSXi Wang case IP_TRANSPARENT: 6220c9f79beSXi Wang case IP_MINTTL: 6230c9f79beSXi Wang case IP_NODEFRAG: 62490c337daSEric Dumazet case IP_BIND_ADDRESS_NO_PORT: 6250c9f79beSXi Wang case IP_UNICAST_IF: 6260c9f79beSXi Wang case IP_MULTICAST_TTL: 6270c9f79beSXi Wang case IP_MULTICAST_ALL: 6280c9f79beSXi Wang case IP_MULTICAST_LOOP: 6290c9f79beSXi Wang case IP_RECVORIGDSTADDR: 630ad6f939aSTom Herbert case IP_CHECKSUM: 631*70ecc248SWillem de Bruijn case IP_RECVFRAGSIZE: 6321da177e4SLinus Torvalds if (optlen >= sizeof(int)) { 6331da177e4SLinus Torvalds if (get_user(val, (int __user *) optval)) 6341da177e4SLinus Torvalds return -EFAULT; 6351da177e4SLinus Torvalds } else if (optlen >= sizeof(char)) { 6361da177e4SLinus Torvalds unsigned char ucval; 6371da177e4SLinus Torvalds 6381da177e4SLinus Torvalds if (get_user(ucval, (unsigned char __user *) optval)) 6391da177e4SLinus Torvalds return -EFAULT; 6401da177e4SLinus Torvalds val = (int) ucval; 6411da177e4SLinus Torvalds } 6421da177e4SLinus Torvalds } 6431da177e4SLinus Torvalds 6441da177e4SLinus Torvalds /* If optlen==0, it is equivalent to val == 0 */ 6451da177e4SLinus Torvalds 6466a9fb947SPavel Emelyanov if (ip_mroute_opt(optname)) 6471da177e4SLinus Torvalds return ip_mroute_setsockopt(sk, optname, optval, optlen); 6481da177e4SLinus Torvalds 6491da177e4SLinus Torvalds err = 0; 650baf606d9SMarcelo Ricardo Leitner if (needs_rtnl) 651baf606d9SMarcelo Ricardo Leitner rtnl_lock(); 6521da177e4SLinus Torvalds lock_sock(sk); 6531da177e4SLinus Torvalds 6541da177e4SLinus Torvalds switch (optname) { 6551da177e4SLinus Torvalds case IP_OPTIONS: 6561da177e4SLinus Torvalds { 657f6d8bd05SEric Dumazet struct ip_options_rcu *old, *opt = NULL; 658f6d8bd05SEric Dumazet 65965a1c4ffSroel kluin if (optlen > 40) 6601da177e4SLinus Torvalds goto e_inval; 6613b1e0a65SYOSHIFUJI Hideaki err = ip_options_get_from_user(sock_net(sk), &opt, 662cb84663eSDenis V. Lunev optval, optlen); 6631da177e4SLinus Torvalds if (err) 6641da177e4SLinus Torvalds break; 665f6d8bd05SEric Dumazet old = rcu_dereference_protected(inet->inet_opt, 6661e1d04e6SHannes Frederic Sowa lockdep_sock_is_held(sk)); 667d83d8461SArnaldo Carvalho de Melo if (inet->is_icsk) { 668d83d8461SArnaldo Carvalho de Melo struct inet_connection_sock *icsk = inet_csk(sk); 669dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6) 6701da177e4SLinus Torvalds if (sk->sk_family == PF_INET || 6711da177e4SLinus Torvalds (!((1 << sk->sk_state) & 6721da177e4SLinus Torvalds (TCPF_LISTEN | TCPF_CLOSE)) && 673c720c7e8SEric Dumazet inet->inet_daddr != LOOPBACK4_IPV6)) { 6741da177e4SLinus Torvalds #endif 675f6d8bd05SEric Dumazet if (old) 676f6d8bd05SEric Dumazet icsk->icsk_ext_hdr_len -= old->opt.optlen; 6771da177e4SLinus Torvalds if (opt) 678f6d8bd05SEric Dumazet icsk->icsk_ext_hdr_len += opt->opt.optlen; 679d83d8461SArnaldo Carvalho de Melo icsk->icsk_sync_mss(sk, icsk->icsk_pmtu_cookie); 680dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6) 6811da177e4SLinus Torvalds } 6821da177e4SLinus Torvalds #endif 6831da177e4SLinus Torvalds } 684f6d8bd05SEric Dumazet rcu_assign_pointer(inet->inet_opt, opt); 685f6d8bd05SEric Dumazet if (old) 686605b4afeSPaul E. McKenney kfree_rcu(old, rcu); 6871da177e4SLinus Torvalds break; 6881da177e4SLinus Torvalds } 6891da177e4SLinus Torvalds case IP_PKTINFO: 6901da177e4SLinus Torvalds if (val) 6911da177e4SLinus Torvalds inet->cmsg_flags |= IP_CMSG_PKTINFO; 6921da177e4SLinus Torvalds else 6931da177e4SLinus Torvalds inet->cmsg_flags &= ~IP_CMSG_PKTINFO; 6941da177e4SLinus Torvalds break; 6951da177e4SLinus Torvalds case IP_RECVTTL: 6961da177e4SLinus Torvalds if (val) 6971da177e4SLinus Torvalds inet->cmsg_flags |= IP_CMSG_TTL; 6981da177e4SLinus Torvalds else 6991da177e4SLinus Torvalds inet->cmsg_flags &= ~IP_CMSG_TTL; 7001da177e4SLinus Torvalds break; 7011da177e4SLinus Torvalds case IP_RECVTOS: 7021da177e4SLinus Torvalds if (val) 7031da177e4SLinus Torvalds inet->cmsg_flags |= IP_CMSG_TOS; 7041da177e4SLinus Torvalds else 7051da177e4SLinus Torvalds inet->cmsg_flags &= ~IP_CMSG_TOS; 7061da177e4SLinus Torvalds break; 7071da177e4SLinus Torvalds case IP_RECVOPTS: 7081da177e4SLinus Torvalds if (val) 7091da177e4SLinus Torvalds inet->cmsg_flags |= IP_CMSG_RECVOPTS; 7101da177e4SLinus Torvalds else 7111da177e4SLinus Torvalds inet->cmsg_flags &= ~IP_CMSG_RECVOPTS; 7121da177e4SLinus Torvalds break; 7131da177e4SLinus Torvalds case IP_RETOPTS: 7141da177e4SLinus Torvalds if (val) 7151da177e4SLinus Torvalds inet->cmsg_flags |= IP_CMSG_RETOPTS; 7161da177e4SLinus Torvalds else 7171da177e4SLinus Torvalds inet->cmsg_flags &= ~IP_CMSG_RETOPTS; 7181da177e4SLinus Torvalds break; 7192c7946a7SCatherine Zhang case IP_PASSSEC: 7202c7946a7SCatherine Zhang if (val) 7212c7946a7SCatherine Zhang inet->cmsg_flags |= IP_CMSG_PASSSEC; 7222c7946a7SCatherine Zhang else 7232c7946a7SCatherine Zhang inet->cmsg_flags &= ~IP_CMSG_PASSSEC; 7242c7946a7SCatherine Zhang break; 725e8b2dfe9SBalazs Scheidler case IP_RECVORIGDSTADDR: 726e8b2dfe9SBalazs Scheidler if (val) 727e8b2dfe9SBalazs Scheidler inet->cmsg_flags |= IP_CMSG_ORIGDSTADDR; 728e8b2dfe9SBalazs Scheidler else 729e8b2dfe9SBalazs Scheidler inet->cmsg_flags &= ~IP_CMSG_ORIGDSTADDR; 730e8b2dfe9SBalazs Scheidler break; 731ad6f939aSTom Herbert case IP_CHECKSUM: 732ad6f939aSTom Herbert if (val) { 733ad6f939aSTom Herbert if (!(inet->cmsg_flags & IP_CMSG_CHECKSUM)) { 734ad6f939aSTom Herbert inet_inc_convert_csum(sk); 735ad6f939aSTom Herbert inet->cmsg_flags |= IP_CMSG_CHECKSUM; 736ad6f939aSTom Herbert } 737ad6f939aSTom Herbert } else { 738ad6f939aSTom Herbert if (inet->cmsg_flags & IP_CMSG_CHECKSUM) { 739ad6f939aSTom Herbert inet_dec_convert_csum(sk); 740ad6f939aSTom Herbert inet->cmsg_flags &= ~IP_CMSG_CHECKSUM; 741ad6f939aSTom Herbert } 742ad6f939aSTom Herbert } 743ad6f939aSTom Herbert break; 744*70ecc248SWillem de Bruijn case IP_RECVFRAGSIZE: 745*70ecc248SWillem de Bruijn if (sk->sk_type != SOCK_RAW && sk->sk_type != SOCK_DGRAM) 746*70ecc248SWillem de Bruijn goto e_inval; 747*70ecc248SWillem de Bruijn if (val) 748*70ecc248SWillem de Bruijn inet->cmsg_flags |= IP_CMSG_RECVFRAGSIZE; 749*70ecc248SWillem de Bruijn else 750*70ecc248SWillem de Bruijn inet->cmsg_flags &= ~IP_CMSG_RECVFRAGSIZE; 751*70ecc248SWillem de Bruijn break; 7521da177e4SLinus Torvalds case IP_TOS: /* This sets both TOS and Precedence */ 7531da177e4SLinus Torvalds if (sk->sk_type == SOCK_STREAM) { 7542c67e9acSMaciej Żenczykowski val &= ~INET_ECN_MASK; 7552c67e9acSMaciej Żenczykowski val |= inet->tos & INET_ECN_MASK; 7561da177e4SLinus Torvalds } 7571da177e4SLinus Torvalds if (inet->tos != val) { 7581da177e4SLinus Torvalds inet->tos = val; 7591da177e4SLinus Torvalds sk->sk_priority = rt_tos2priority(val); 7601da177e4SLinus Torvalds sk_dst_reset(sk); 7611da177e4SLinus Torvalds } 7621da177e4SLinus Torvalds break; 7631da177e4SLinus Torvalds case IP_TTL: 7641da177e4SLinus Torvalds if (optlen < 1) 7651da177e4SLinus Torvalds goto e_inval; 766c9be4a5cSCong Wang if (val != -1 && (val < 1 || val > 255)) 7671da177e4SLinus Torvalds goto e_inval; 7681da177e4SLinus Torvalds inet->uc_ttl = val; 7691da177e4SLinus Torvalds break; 7701da177e4SLinus Torvalds case IP_HDRINCL: 7711da177e4SLinus Torvalds if (sk->sk_type != SOCK_RAW) { 7721da177e4SLinus Torvalds err = -ENOPROTOOPT; 7731da177e4SLinus Torvalds break; 7741da177e4SLinus Torvalds } 7751da177e4SLinus Torvalds inet->hdrincl = val ? 1 : 0; 7761da177e4SLinus Torvalds break; 7777b2ff18eSJiri Olsa case IP_NODEFRAG: 7787b2ff18eSJiri Olsa if (sk->sk_type != SOCK_RAW) { 7797b2ff18eSJiri Olsa err = -ENOPROTOOPT; 7807b2ff18eSJiri Olsa break; 7817b2ff18eSJiri Olsa } 7827b2ff18eSJiri Olsa inet->nodefrag = val ? 1 : 0; 7837b2ff18eSJiri Olsa break; 78490c337daSEric Dumazet case IP_BIND_ADDRESS_NO_PORT: 78590c337daSEric Dumazet inet->bind_address_no_port = val ? 1 : 0; 78690c337daSEric Dumazet break; 7871da177e4SLinus Torvalds case IP_MTU_DISCOVER: 7881b346576SHannes Frederic Sowa if (val < IP_PMTUDISC_DONT || val > IP_PMTUDISC_OMIT) 7891da177e4SLinus Torvalds goto e_inval; 7901da177e4SLinus Torvalds inet->pmtudisc = val; 7911da177e4SLinus Torvalds break; 7921da177e4SLinus Torvalds case IP_RECVERR: 7931da177e4SLinus Torvalds inet->recverr = !!val; 7941da177e4SLinus Torvalds if (!val) 7951da177e4SLinus Torvalds skb_queue_purge(&sk->sk_error_queue); 7961da177e4SLinus Torvalds break; 7971da177e4SLinus Torvalds case IP_MULTICAST_TTL: 7981da177e4SLinus Torvalds if (sk->sk_type == SOCK_STREAM) 7991da177e4SLinus Torvalds goto e_inval; 8001da177e4SLinus Torvalds if (optlen < 1) 8011da177e4SLinus Torvalds goto e_inval; 8021da177e4SLinus Torvalds if (val == -1) 8031da177e4SLinus Torvalds val = 1; 8041da177e4SLinus Torvalds if (val < 0 || val > 255) 8051da177e4SLinus Torvalds goto e_inval; 8061da177e4SLinus Torvalds inet->mc_ttl = val; 8071da177e4SLinus Torvalds break; 8081da177e4SLinus Torvalds case IP_MULTICAST_LOOP: 8091da177e4SLinus Torvalds if (optlen < 1) 8101da177e4SLinus Torvalds goto e_inval; 8111da177e4SLinus Torvalds inet->mc_loop = !!val; 8121da177e4SLinus Torvalds break; 81376e21053SErich E. Hoover case IP_UNICAST_IF: 81476e21053SErich E. Hoover { 81576e21053SErich E. Hoover struct net_device *dev = NULL; 81676e21053SErich E. Hoover int ifindex; 81776e21053SErich E. Hoover 81876e21053SErich E. Hoover if (optlen != sizeof(int)) 81976e21053SErich E. Hoover goto e_inval; 82076e21053SErich E. Hoover 82176e21053SErich E. Hoover ifindex = (__force int)ntohl((__force __be32)val); 82276e21053SErich E. Hoover if (ifindex == 0) { 82376e21053SErich E. Hoover inet->uc_index = 0; 82476e21053SErich E. Hoover err = 0; 82576e21053SErich E. Hoover break; 82676e21053SErich E. Hoover } 82776e21053SErich E. Hoover 82876e21053SErich E. Hoover dev = dev_get_by_index(sock_net(sk), ifindex); 82976e21053SErich E. Hoover err = -EADDRNOTAVAIL; 83076e21053SErich E. Hoover if (!dev) 83176e21053SErich E. Hoover break; 83276e21053SErich E. Hoover dev_put(dev); 83376e21053SErich E. Hoover 83476e21053SErich E. Hoover err = -EINVAL; 83576e21053SErich E. Hoover if (sk->sk_bound_dev_if) 83676e21053SErich E. Hoover break; 83776e21053SErich E. Hoover 83876e21053SErich E. Hoover inet->uc_index = ifindex; 83976e21053SErich E. Hoover err = 0; 84076e21053SErich E. Hoover break; 84176e21053SErich E. Hoover } 8421da177e4SLinus Torvalds case IP_MULTICAST_IF: 8431da177e4SLinus Torvalds { 8441da177e4SLinus Torvalds struct ip_mreqn mreq; 8451da177e4SLinus Torvalds struct net_device *dev = NULL; 8461da177e4SLinus Torvalds 8471da177e4SLinus Torvalds if (sk->sk_type == SOCK_STREAM) 8481da177e4SLinus Torvalds goto e_inval; 8491da177e4SLinus Torvalds /* 8501da177e4SLinus Torvalds * Check the arguments are allowable 8511da177e4SLinus Torvalds */ 8521da177e4SLinus Torvalds 8530915921bSShan Wei if (optlen < sizeof(struct in_addr)) 8540915921bSShan Wei goto e_inval; 8550915921bSShan Wei 8561da177e4SLinus Torvalds err = -EFAULT; 8571da177e4SLinus Torvalds if (optlen >= sizeof(struct ip_mreqn)) { 8581da177e4SLinus Torvalds if (copy_from_user(&mreq, optval, sizeof(mreq))) 8591da177e4SLinus Torvalds break; 8601da177e4SLinus Torvalds } else { 8611da177e4SLinus Torvalds memset(&mreq, 0, sizeof(mreq)); 8623a084ddbSJiri Pirko if (optlen >= sizeof(struct ip_mreq)) { 8633a084ddbSJiri Pirko if (copy_from_user(&mreq, optval, 8643a084ddbSJiri Pirko sizeof(struct ip_mreq))) 8653a084ddbSJiri Pirko break; 8663a084ddbSJiri Pirko } else if (optlen >= sizeof(struct in_addr)) { 8673a084ddbSJiri Pirko if (copy_from_user(&mreq.imr_address, optval, 8684d52cfbeSEric Dumazet sizeof(struct in_addr))) 8691da177e4SLinus Torvalds break; 8701da177e4SLinus Torvalds } 8713a084ddbSJiri Pirko } 8721da177e4SLinus Torvalds 8731da177e4SLinus Torvalds if (!mreq.imr_ifindex) { 874e6f1cebfSAl Viro if (mreq.imr_address.s_addr == htonl(INADDR_ANY)) { 8751da177e4SLinus Torvalds inet->mc_index = 0; 8761da177e4SLinus Torvalds inet->mc_addr = 0; 8771da177e4SLinus Torvalds err = 0; 8781da177e4SLinus Torvalds break; 8791da177e4SLinus Torvalds } 8803b1e0a65SYOSHIFUJI Hideaki dev = ip_dev_find(sock_net(sk), mreq.imr_address.s_addr); 88155b80503SEric Dumazet if (dev) 8821da177e4SLinus Torvalds mreq.imr_ifindex = dev->ifindex; 8831da177e4SLinus Torvalds } else 88455b80503SEric Dumazet dev = dev_get_by_index(sock_net(sk), mreq.imr_ifindex); 8851da177e4SLinus Torvalds 8861da177e4SLinus Torvalds 8871da177e4SLinus Torvalds err = -EADDRNOTAVAIL; 8881da177e4SLinus Torvalds if (!dev) 8891da177e4SLinus Torvalds break; 89055b80503SEric Dumazet dev_put(dev); 8911da177e4SLinus Torvalds 8921da177e4SLinus Torvalds err = -EINVAL; 8931da177e4SLinus Torvalds if (sk->sk_bound_dev_if && 8941da177e4SLinus Torvalds mreq.imr_ifindex != sk->sk_bound_dev_if) 8951da177e4SLinus Torvalds break; 8961da177e4SLinus Torvalds 8971da177e4SLinus Torvalds inet->mc_index = mreq.imr_ifindex; 8981da177e4SLinus Torvalds inet->mc_addr = mreq.imr_address.s_addr; 8991da177e4SLinus Torvalds err = 0; 9001da177e4SLinus Torvalds break; 9011da177e4SLinus Torvalds } 9021da177e4SLinus Torvalds 9031da177e4SLinus Torvalds case IP_ADD_MEMBERSHIP: 9041da177e4SLinus Torvalds case IP_DROP_MEMBERSHIP: 9051da177e4SLinus Torvalds { 9061da177e4SLinus Torvalds struct ip_mreqn mreq; 9071da177e4SLinus Torvalds 908a96fb49bSFlavio Leitner err = -EPROTO; 909a96fb49bSFlavio Leitner if (inet_sk(sk)->is_icsk) 910a96fb49bSFlavio Leitner break; 911a96fb49bSFlavio Leitner 9121da177e4SLinus Torvalds if (optlen < sizeof(struct ip_mreq)) 9131da177e4SLinus Torvalds goto e_inval; 9141da177e4SLinus Torvalds err = -EFAULT; 9151da177e4SLinus Torvalds if (optlen >= sizeof(struct ip_mreqn)) { 9161da177e4SLinus Torvalds if (copy_from_user(&mreq, optval, sizeof(mreq))) 9171da177e4SLinus Torvalds break; 9181da177e4SLinus Torvalds } else { 9191da177e4SLinus Torvalds memset(&mreq, 0, sizeof(mreq)); 9201da177e4SLinus Torvalds if (copy_from_user(&mreq, optval, sizeof(struct ip_mreq))) 9211da177e4SLinus Torvalds break; 9221da177e4SLinus Torvalds } 9231da177e4SLinus Torvalds 9241da177e4SLinus Torvalds if (optname == IP_ADD_MEMBERSHIP) 92554ff9ef3SMarcelo Ricardo Leitner err = ip_mc_join_group(sk, &mreq); 9261da177e4SLinus Torvalds else 92754ff9ef3SMarcelo Ricardo Leitner err = ip_mc_leave_group(sk, &mreq); 9281da177e4SLinus Torvalds break; 9291da177e4SLinus Torvalds } 9301da177e4SLinus Torvalds case IP_MSFILTER: 9311da177e4SLinus Torvalds { 9321da177e4SLinus Torvalds struct ip_msfilter *msf; 9331da177e4SLinus Torvalds 9341da177e4SLinus Torvalds if (optlen < IP_MSFILTER_SIZE(0)) 9351da177e4SLinus Torvalds goto e_inval; 9361da177e4SLinus Torvalds if (optlen > sysctl_optmem_max) { 9371da177e4SLinus Torvalds err = -ENOBUFS; 9381da177e4SLinus Torvalds break; 9391da177e4SLinus Torvalds } 9408b3a7005SKris Katterjohn msf = kmalloc(optlen, GFP_KERNEL); 941cfcabdccSStephen Hemminger if (!msf) { 9421da177e4SLinus Torvalds err = -ENOBUFS; 9431da177e4SLinus Torvalds break; 9441da177e4SLinus Torvalds } 9451da177e4SLinus Torvalds err = -EFAULT; 9461da177e4SLinus Torvalds if (copy_from_user(msf, optval, optlen)) { 9471da177e4SLinus Torvalds kfree(msf); 9481da177e4SLinus Torvalds break; 9491da177e4SLinus Torvalds } 9501da177e4SLinus Torvalds /* numsrc >= (1G-4) overflow in 32 bits */ 9511da177e4SLinus Torvalds if (msf->imsf_numsrc >= 0x3ffffffcU || 952166b6b2dSNikolay Borisov msf->imsf_numsrc > net->ipv4.sysctl_igmp_max_msf) { 9531da177e4SLinus Torvalds kfree(msf); 9541da177e4SLinus Torvalds err = -ENOBUFS; 9551da177e4SLinus Torvalds break; 9561da177e4SLinus Torvalds } 9571da177e4SLinus Torvalds if (IP_MSFILTER_SIZE(msf->imsf_numsrc) > optlen) { 9581da177e4SLinus Torvalds kfree(msf); 9591da177e4SLinus Torvalds err = -EINVAL; 9601da177e4SLinus Torvalds break; 9611da177e4SLinus Torvalds } 9621da177e4SLinus Torvalds err = ip_mc_msfilter(sk, msf, 0); 9631da177e4SLinus Torvalds kfree(msf); 9641da177e4SLinus Torvalds break; 9651da177e4SLinus Torvalds } 9661da177e4SLinus Torvalds case IP_BLOCK_SOURCE: 9671da177e4SLinus Torvalds case IP_UNBLOCK_SOURCE: 9681da177e4SLinus Torvalds case IP_ADD_SOURCE_MEMBERSHIP: 9691da177e4SLinus Torvalds case IP_DROP_SOURCE_MEMBERSHIP: 9701da177e4SLinus Torvalds { 9711da177e4SLinus Torvalds struct ip_mreq_source mreqs; 9721da177e4SLinus Torvalds int omode, add; 9731da177e4SLinus Torvalds 9741da177e4SLinus Torvalds if (optlen != sizeof(struct ip_mreq_source)) 9751da177e4SLinus Torvalds goto e_inval; 9761da177e4SLinus Torvalds if (copy_from_user(&mreqs, optval, sizeof(mreqs))) { 9771da177e4SLinus Torvalds err = -EFAULT; 9781da177e4SLinus Torvalds break; 9791da177e4SLinus Torvalds } 9801da177e4SLinus Torvalds if (optname == IP_BLOCK_SOURCE) { 9811da177e4SLinus Torvalds omode = MCAST_EXCLUDE; 9821da177e4SLinus Torvalds add = 1; 9831da177e4SLinus Torvalds } else if (optname == IP_UNBLOCK_SOURCE) { 9841da177e4SLinus Torvalds omode = MCAST_EXCLUDE; 9851da177e4SLinus Torvalds add = 0; 9861da177e4SLinus Torvalds } else if (optname == IP_ADD_SOURCE_MEMBERSHIP) { 9871da177e4SLinus Torvalds struct ip_mreqn mreq; 9881da177e4SLinus Torvalds 9891da177e4SLinus Torvalds mreq.imr_multiaddr.s_addr = mreqs.imr_multiaddr; 9901da177e4SLinus Torvalds mreq.imr_address.s_addr = mreqs.imr_interface; 9911da177e4SLinus Torvalds mreq.imr_ifindex = 0; 99254ff9ef3SMarcelo Ricardo Leitner err = ip_mc_join_group(sk, &mreq); 9938cdaaa15SDavid L Stevens if (err && err != -EADDRINUSE) 9941da177e4SLinus Torvalds break; 9951da177e4SLinus Torvalds omode = MCAST_INCLUDE; 9961da177e4SLinus Torvalds add = 1; 9971da177e4SLinus Torvalds } else /* IP_DROP_SOURCE_MEMBERSHIP */ { 9981da177e4SLinus Torvalds omode = MCAST_INCLUDE; 9991da177e4SLinus Torvalds add = 0; 10001da177e4SLinus Torvalds } 10011da177e4SLinus Torvalds err = ip_mc_source(add, omode, sk, &mreqs, 0); 10021da177e4SLinus Torvalds break; 10031da177e4SLinus Torvalds } 10041da177e4SLinus Torvalds case MCAST_JOIN_GROUP: 10051da177e4SLinus Torvalds case MCAST_LEAVE_GROUP: 10061da177e4SLinus Torvalds { 10071da177e4SLinus Torvalds struct group_req greq; 10081da177e4SLinus Torvalds struct sockaddr_in *psin; 10091da177e4SLinus Torvalds struct ip_mreqn mreq; 10101da177e4SLinus Torvalds 10111da177e4SLinus Torvalds if (optlen < sizeof(struct group_req)) 10121da177e4SLinus Torvalds goto e_inval; 10131da177e4SLinus Torvalds err = -EFAULT; 10141da177e4SLinus Torvalds if (copy_from_user(&greq, optval, sizeof(greq))) 10151da177e4SLinus Torvalds break; 10161da177e4SLinus Torvalds psin = (struct sockaddr_in *)&greq.gr_group; 10171da177e4SLinus Torvalds if (psin->sin_family != AF_INET) 10181da177e4SLinus Torvalds goto e_inval; 10191da177e4SLinus Torvalds memset(&mreq, 0, sizeof(mreq)); 10201da177e4SLinus Torvalds mreq.imr_multiaddr = psin->sin_addr; 10211da177e4SLinus Torvalds mreq.imr_ifindex = greq.gr_interface; 10221da177e4SLinus Torvalds 10231da177e4SLinus Torvalds if (optname == MCAST_JOIN_GROUP) 102454ff9ef3SMarcelo Ricardo Leitner err = ip_mc_join_group(sk, &mreq); 10251da177e4SLinus Torvalds else 102654ff9ef3SMarcelo Ricardo Leitner err = ip_mc_leave_group(sk, &mreq); 10271da177e4SLinus Torvalds break; 10281da177e4SLinus Torvalds } 10291da177e4SLinus Torvalds case MCAST_JOIN_SOURCE_GROUP: 10301da177e4SLinus Torvalds case MCAST_LEAVE_SOURCE_GROUP: 10311da177e4SLinus Torvalds case MCAST_BLOCK_SOURCE: 10321da177e4SLinus Torvalds case MCAST_UNBLOCK_SOURCE: 10331da177e4SLinus Torvalds { 10341da177e4SLinus Torvalds struct group_source_req greqs; 10351da177e4SLinus Torvalds struct ip_mreq_source mreqs; 10361da177e4SLinus Torvalds struct sockaddr_in *psin; 10371da177e4SLinus Torvalds int omode, add; 10381da177e4SLinus Torvalds 10391da177e4SLinus Torvalds if (optlen != sizeof(struct group_source_req)) 10401da177e4SLinus Torvalds goto e_inval; 10411da177e4SLinus Torvalds if (copy_from_user(&greqs, optval, sizeof(greqs))) { 10421da177e4SLinus Torvalds err = -EFAULT; 10431da177e4SLinus Torvalds break; 10441da177e4SLinus Torvalds } 10451da177e4SLinus Torvalds if (greqs.gsr_group.ss_family != AF_INET || 10461da177e4SLinus Torvalds greqs.gsr_source.ss_family != AF_INET) { 10471da177e4SLinus Torvalds err = -EADDRNOTAVAIL; 10481da177e4SLinus Torvalds break; 10491da177e4SLinus Torvalds } 10501da177e4SLinus Torvalds psin = (struct sockaddr_in *)&greqs.gsr_group; 10511da177e4SLinus Torvalds mreqs.imr_multiaddr = psin->sin_addr.s_addr; 10521da177e4SLinus Torvalds psin = (struct sockaddr_in *)&greqs.gsr_source; 10531da177e4SLinus Torvalds mreqs.imr_sourceaddr = psin->sin_addr.s_addr; 10541da177e4SLinus Torvalds mreqs.imr_interface = 0; /* use index for mc_source */ 10551da177e4SLinus Torvalds 10561da177e4SLinus Torvalds if (optname == MCAST_BLOCK_SOURCE) { 10571da177e4SLinus Torvalds omode = MCAST_EXCLUDE; 10581da177e4SLinus Torvalds add = 1; 10591da177e4SLinus Torvalds } else if (optname == MCAST_UNBLOCK_SOURCE) { 10601da177e4SLinus Torvalds omode = MCAST_EXCLUDE; 10611da177e4SLinus Torvalds add = 0; 10621da177e4SLinus Torvalds } else if (optname == MCAST_JOIN_SOURCE_GROUP) { 10631da177e4SLinus Torvalds struct ip_mreqn mreq; 10641da177e4SLinus Torvalds 10651da177e4SLinus Torvalds psin = (struct sockaddr_in *)&greqs.gsr_group; 10661da177e4SLinus Torvalds mreq.imr_multiaddr = psin->sin_addr; 10671da177e4SLinus Torvalds mreq.imr_address.s_addr = 0; 10681da177e4SLinus Torvalds mreq.imr_ifindex = greqs.gsr_interface; 106954ff9ef3SMarcelo Ricardo Leitner err = ip_mc_join_group(sk, &mreq); 10708cdaaa15SDavid L Stevens if (err && err != -EADDRINUSE) 10711da177e4SLinus Torvalds break; 10721da177e4SLinus Torvalds greqs.gsr_interface = mreq.imr_ifindex; 10731da177e4SLinus Torvalds omode = MCAST_INCLUDE; 10741da177e4SLinus Torvalds add = 1; 10751da177e4SLinus Torvalds } else /* MCAST_LEAVE_SOURCE_GROUP */ { 10761da177e4SLinus Torvalds omode = MCAST_INCLUDE; 10771da177e4SLinus Torvalds add = 0; 10781da177e4SLinus Torvalds } 10791da177e4SLinus Torvalds err = ip_mc_source(add, omode, sk, &mreqs, 10801da177e4SLinus Torvalds greqs.gsr_interface); 10811da177e4SLinus Torvalds break; 10821da177e4SLinus Torvalds } 10831da177e4SLinus Torvalds case MCAST_MSFILTER: 10841da177e4SLinus Torvalds { 10851da177e4SLinus Torvalds struct sockaddr_in *psin; 10861da177e4SLinus Torvalds struct ip_msfilter *msf = NULL; 10871da177e4SLinus Torvalds struct group_filter *gsf = NULL; 10881da177e4SLinus Torvalds int msize, i, ifindex; 10891da177e4SLinus Torvalds 10901da177e4SLinus Torvalds if (optlen < GROUP_FILTER_SIZE(0)) 10911da177e4SLinus Torvalds goto e_inval; 10921da177e4SLinus Torvalds if (optlen > sysctl_optmem_max) { 10931da177e4SLinus Torvalds err = -ENOBUFS; 10941da177e4SLinus Torvalds break; 10951da177e4SLinus Torvalds } 10968b3a7005SKris Katterjohn gsf = kmalloc(optlen, GFP_KERNEL); 1097cfcabdccSStephen Hemminger if (!gsf) { 10981da177e4SLinus Torvalds err = -ENOBUFS; 10991da177e4SLinus Torvalds break; 11001da177e4SLinus Torvalds } 11011da177e4SLinus Torvalds err = -EFAULT; 11024d52cfbeSEric Dumazet if (copy_from_user(gsf, optval, optlen)) 11031da177e4SLinus Torvalds goto mc_msf_out; 11044d52cfbeSEric Dumazet 11051da177e4SLinus Torvalds /* numsrc >= (4G-140)/128 overflow in 32 bits */ 11061da177e4SLinus Torvalds if (gsf->gf_numsrc >= 0x1ffffff || 1107166b6b2dSNikolay Borisov gsf->gf_numsrc > net->ipv4.sysctl_igmp_max_msf) { 11081da177e4SLinus Torvalds err = -ENOBUFS; 11091da177e4SLinus Torvalds goto mc_msf_out; 11101da177e4SLinus Torvalds } 11111da177e4SLinus Torvalds if (GROUP_FILTER_SIZE(gsf->gf_numsrc) > optlen) { 11121da177e4SLinus Torvalds err = -EINVAL; 11131da177e4SLinus Torvalds goto mc_msf_out; 11141da177e4SLinus Torvalds } 11151da177e4SLinus Torvalds msize = IP_MSFILTER_SIZE(gsf->gf_numsrc); 11168b3a7005SKris Katterjohn msf = kmalloc(msize, GFP_KERNEL); 1117cfcabdccSStephen Hemminger if (!msf) { 11181da177e4SLinus Torvalds err = -ENOBUFS; 11191da177e4SLinus Torvalds goto mc_msf_out; 11201da177e4SLinus Torvalds } 11211da177e4SLinus Torvalds ifindex = gsf->gf_interface; 11221da177e4SLinus Torvalds psin = (struct sockaddr_in *)&gsf->gf_group; 11231da177e4SLinus Torvalds if (psin->sin_family != AF_INET) { 11241da177e4SLinus Torvalds err = -EADDRNOTAVAIL; 11251da177e4SLinus Torvalds goto mc_msf_out; 11261da177e4SLinus Torvalds } 11271da177e4SLinus Torvalds msf->imsf_multiaddr = psin->sin_addr.s_addr; 11281da177e4SLinus Torvalds msf->imsf_interface = 0; 11291da177e4SLinus Torvalds msf->imsf_fmode = gsf->gf_fmode; 11301da177e4SLinus Torvalds msf->imsf_numsrc = gsf->gf_numsrc; 11311da177e4SLinus Torvalds err = -EADDRNOTAVAIL; 11321da177e4SLinus Torvalds for (i = 0; i < gsf->gf_numsrc; ++i) { 11331da177e4SLinus Torvalds psin = (struct sockaddr_in *)&gsf->gf_slist[i]; 11341da177e4SLinus Torvalds 11351da177e4SLinus Torvalds if (psin->sin_family != AF_INET) 11361da177e4SLinus Torvalds goto mc_msf_out; 11371da177e4SLinus Torvalds msf->imsf_slist[i] = psin->sin_addr.s_addr; 11381da177e4SLinus Torvalds } 11391da177e4SLinus Torvalds kfree(gsf); 11401da177e4SLinus Torvalds gsf = NULL; 11411da177e4SLinus Torvalds 11421da177e4SLinus Torvalds err = ip_mc_msfilter(sk, msf, ifindex); 11431da177e4SLinus Torvalds mc_msf_out: 11441da177e4SLinus Torvalds kfree(msf); 11451da177e4SLinus Torvalds kfree(gsf); 11461da177e4SLinus Torvalds break; 11471da177e4SLinus Torvalds } 1148f771bef9SNivedita Singhvi case IP_MULTICAST_ALL: 1149f771bef9SNivedita Singhvi if (optlen < 1) 1150f771bef9SNivedita Singhvi goto e_inval; 1151f771bef9SNivedita Singhvi if (val != 0 && val != 1) 1152f771bef9SNivedita Singhvi goto e_inval; 1153f771bef9SNivedita Singhvi inet->mc_all = val; 1154f771bef9SNivedita Singhvi break; 11551da177e4SLinus Torvalds case IP_ROUTER_ALERT: 11561da177e4SLinus Torvalds err = ip_ra_control(sk, val ? 1 : 0, NULL); 11571da177e4SLinus Torvalds break; 11581da177e4SLinus Torvalds 11591da177e4SLinus Torvalds case IP_FREEBIND: 11601da177e4SLinus Torvalds if (optlen < 1) 11611da177e4SLinus Torvalds goto e_inval; 11621da177e4SLinus Torvalds inet->freebind = !!val; 11631da177e4SLinus Torvalds break; 11641da177e4SLinus Torvalds 11651da177e4SLinus Torvalds case IP_IPSEC_POLICY: 11661da177e4SLinus Torvalds case IP_XFRM_POLICY: 11676fc0b4a7SHerbert Xu err = -EPERM; 116852e804c6SEric W. Biederman if (!ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN)) 11696fc0b4a7SHerbert Xu break; 11701da177e4SLinus Torvalds err = xfrm_user_policy(sk, optname, optval, optlen); 11711da177e4SLinus Torvalds break; 11721da177e4SLinus Torvalds 1173f5715aeaSKOVACS Krisztian case IP_TRANSPARENT: 117452e804c6SEric W. Biederman if (!!val && !ns_capable(sock_net(sk)->user_ns, CAP_NET_RAW) && 117552e804c6SEric W. Biederman !ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN)) { 1176f5715aeaSKOVACS Krisztian err = -EPERM; 1177f5715aeaSKOVACS Krisztian break; 1178f5715aeaSKOVACS Krisztian } 1179f5715aeaSKOVACS Krisztian if (optlen < 1) 1180f5715aeaSKOVACS Krisztian goto e_inval; 1181f5715aeaSKOVACS Krisztian inet->transparent = !!val; 1182f5715aeaSKOVACS Krisztian break; 1183f5715aeaSKOVACS Krisztian 1184d218d111SStephen Hemminger case IP_MINTTL: 1185d218d111SStephen Hemminger if (optlen < 1) 1186d218d111SStephen Hemminger goto e_inval; 1187d218d111SStephen Hemminger if (val < 0 || val > 255) 1188d218d111SStephen Hemminger goto e_inval; 1189d218d111SStephen Hemminger inet->min_ttl = val; 1190d218d111SStephen Hemminger break; 1191d218d111SStephen Hemminger 11921da177e4SLinus Torvalds default: 11931da177e4SLinus Torvalds err = -ENOPROTOOPT; 11941da177e4SLinus Torvalds break; 11951da177e4SLinus Torvalds } 11961da177e4SLinus Torvalds release_sock(sk); 1197baf606d9SMarcelo Ricardo Leitner if (needs_rtnl) 1198baf606d9SMarcelo Ricardo Leitner rtnl_unlock(); 11991da177e4SLinus Torvalds return err; 12001da177e4SLinus Torvalds 12011da177e4SLinus Torvalds e_inval: 12021da177e4SLinus Torvalds release_sock(sk); 1203baf606d9SMarcelo Ricardo Leitner if (needs_rtnl) 1204baf606d9SMarcelo Ricardo Leitner rtnl_unlock(); 12051da177e4SLinus Torvalds return -EINVAL; 12061da177e4SLinus Torvalds } 12071da177e4SLinus Torvalds 1208f84af32cSEric Dumazet /** 1209829ae9d6SWillem de Bruijn * ipv4_pktinfo_prepare - transfer some info from rtable to skb 1210f84af32cSEric Dumazet * @sk: socket 1211f84af32cSEric Dumazet * @skb: buffer 1212f84af32cSEric Dumazet * 121335ebf65eSDavid S. Miller * To support IP_CMSG_PKTINFO option, we store rt_iif and specific 121435ebf65eSDavid S. Miller * destination in skb->cb[] before dst drop. 12158e3bff96Sstephen hemminger * This way, receiver doesn't make cache line misses to read rtable. 1216f84af32cSEric Dumazet */ 1217fbf8866dSShawn Bohrer void ipv4_pktinfo_prepare(const struct sock *sk, struct sk_buff *skb) 1218f84af32cSEric Dumazet { 1219d826eb14SEric Dumazet struct in_pktinfo *pktinfo = PKTINFO_SKB_CB(skb); 12204b261c75SHannes Frederic Sowa bool prepare = (inet_sk(sk)->cmsg_flags & IP_CMSG_PKTINFO) || 12214b261c75SHannes Frederic Sowa ipv6_sk_rxinfo(sk); 1222d826eb14SEric Dumazet 12234b261c75SHannes Frederic Sowa if (prepare && skb_rtable(skb)) { 12240b922b7aSDavid Ahern /* skb->cb is overloaded: prior to this point it is IP{6}CB 12250b922b7aSDavid Ahern * which has interface index (iif) as the first member of the 12260b922b7aSDavid Ahern * underlying inet{6}_skb_parm struct. This code then overlays 12270b922b7aSDavid Ahern * PKTINFO_SKB_CB and in_pktinfo also has iif as the first 12280b922b7aSDavid Ahern * element so the iif is picked up from the prior IPCB 12290b922b7aSDavid Ahern */ 123035ebf65eSDavid S. Miller pktinfo->ipi_spec_dst.s_addr = fib_compute_spec_dst(skb); 1231d826eb14SEric Dumazet } else { 1232d826eb14SEric Dumazet pktinfo->ipi_ifindex = 0; 1233d826eb14SEric Dumazet pktinfo->ipi_spec_dst.s_addr = 0; 1234f84af32cSEric Dumazet } 1235d826eb14SEric Dumazet skb_dst_drop(skb); 1236d826eb14SEric Dumazet } 1237f84af32cSEric Dumazet 12383fdadf7dSDmitry Mishin int ip_setsockopt(struct sock *sk, int level, 1239b7058842SDavid S. Miller int optname, char __user *optval, unsigned int optlen) 12403fdadf7dSDmitry Mishin { 12413fdadf7dSDmitry Mishin int err; 12423fdadf7dSDmitry Mishin 12433fdadf7dSDmitry Mishin if (level != SOL_IP) 12443fdadf7dSDmitry Mishin return -ENOPROTOOPT; 12453fdadf7dSDmitry Mishin 12463fdadf7dSDmitry Mishin err = do_ip_setsockopt(sk, level, optname, optval, optlen); 12473fdadf7dSDmitry Mishin #ifdef CONFIG_NETFILTER 12483fdadf7dSDmitry Mishin /* we need to exclude all possible ENOPROTOOPTs except default case */ 12493fdadf7dSDmitry Mishin if (err == -ENOPROTOOPT && optname != IP_HDRINCL && 12506a9fb947SPavel Emelyanov optname != IP_IPSEC_POLICY && 12516a9fb947SPavel Emelyanov optname != IP_XFRM_POLICY && 12526a9fb947SPavel Emelyanov !ip_mroute_opt(optname)) { 12533fdadf7dSDmitry Mishin lock_sock(sk); 12543fdadf7dSDmitry Mishin err = nf_setsockopt(sk, PF_INET, optname, optval, optlen); 12553fdadf7dSDmitry Mishin release_sock(sk); 12563fdadf7dSDmitry Mishin } 12573fdadf7dSDmitry Mishin #endif 12583fdadf7dSDmitry Mishin return err; 12593fdadf7dSDmitry Mishin } 12604d52cfbeSEric Dumazet EXPORT_SYMBOL(ip_setsockopt); 12613fdadf7dSDmitry Mishin 12623fdadf7dSDmitry Mishin #ifdef CONFIG_COMPAT 1263543d9cfeSArnaldo Carvalho de Melo int compat_ip_setsockopt(struct sock *sk, int level, int optname, 1264b7058842SDavid S. Miller char __user *optval, unsigned int optlen) 12653fdadf7dSDmitry Mishin { 12663fdadf7dSDmitry Mishin int err; 12673fdadf7dSDmitry Mishin 12683fdadf7dSDmitry Mishin if (level != SOL_IP) 12693fdadf7dSDmitry Mishin return -ENOPROTOOPT; 12703fdadf7dSDmitry Mishin 1271dae50295SDavid L Stevens if (optname >= MCAST_JOIN_GROUP && optname <= MCAST_MSFILTER) 1272dae50295SDavid L Stevens return compat_mc_setsockopt(sk, level, optname, optval, optlen, 1273dae50295SDavid L Stevens ip_setsockopt); 1274dae50295SDavid L Stevens 12753fdadf7dSDmitry Mishin err = do_ip_setsockopt(sk, level, optname, optval, optlen); 12763fdadf7dSDmitry Mishin #ifdef CONFIG_NETFILTER 12773fdadf7dSDmitry Mishin /* we need to exclude all possible ENOPROTOOPTs except default case */ 12783fdadf7dSDmitry Mishin if (err == -ENOPROTOOPT && optname != IP_HDRINCL && 12796a9fb947SPavel Emelyanov optname != IP_IPSEC_POLICY && 12806a9fb947SPavel Emelyanov optname != IP_XFRM_POLICY && 12816a9fb947SPavel Emelyanov !ip_mroute_opt(optname)) { 12823fdadf7dSDmitry Mishin lock_sock(sk); 1283543d9cfeSArnaldo Carvalho de Melo err = compat_nf_setsockopt(sk, PF_INET, optname, 1284543d9cfeSArnaldo Carvalho de Melo optval, optlen); 12853fdadf7dSDmitry Mishin release_sock(sk); 12863fdadf7dSDmitry Mishin } 12873fdadf7dSDmitry Mishin #endif 12883fdadf7dSDmitry Mishin return err; 12893fdadf7dSDmitry Mishin } 1290543d9cfeSArnaldo Carvalho de Melo EXPORT_SYMBOL(compat_ip_setsockopt); 12913fdadf7dSDmitry Mishin #endif 12923fdadf7dSDmitry Mishin 12931da177e4SLinus Torvalds /* 12944d52cfbeSEric Dumazet * Get the options. Note for future reference. The GET of IP options gets 12954d52cfbeSEric Dumazet * the _received_ ones. The set sets the _sent_ ones. 12961da177e4SLinus Torvalds */ 12971da177e4SLinus Torvalds 129887e9f031SWANG Cong static bool getsockopt_needs_rtnl(int optname) 129987e9f031SWANG Cong { 130087e9f031SWANG Cong switch (optname) { 130187e9f031SWANG Cong case IP_MSFILTER: 130287e9f031SWANG Cong case MCAST_MSFILTER: 130387e9f031SWANG Cong return true; 130487e9f031SWANG Cong } 130587e9f031SWANG Cong return false; 130687e9f031SWANG Cong } 130787e9f031SWANG Cong 13083fdadf7dSDmitry Mishin static int do_ip_getsockopt(struct sock *sk, int level, int optname, 130995c96174SEric Dumazet char __user *optval, int __user *optlen, unsigned int flags) 13101da177e4SLinus Torvalds { 13111da177e4SLinus Torvalds struct inet_sock *inet = inet_sk(sk); 131287e9f031SWANG Cong bool needs_rtnl = getsockopt_needs_rtnl(optname); 131387e9f031SWANG Cong int val, err = 0; 13141da177e4SLinus Torvalds int len; 13151da177e4SLinus Torvalds 13161da177e4SLinus Torvalds if (level != SOL_IP) 13171da177e4SLinus Torvalds return -EOPNOTSUPP; 13181da177e4SLinus Torvalds 13196a9fb947SPavel Emelyanov if (ip_mroute_opt(optname)) 13201da177e4SLinus Torvalds return ip_mroute_getsockopt(sk, optname, optval, optlen); 13211da177e4SLinus Torvalds 13221da177e4SLinus Torvalds if (get_user(len, optlen)) 13231da177e4SLinus Torvalds return -EFAULT; 13241da177e4SLinus Torvalds if (len < 0) 13251da177e4SLinus Torvalds return -EINVAL; 13261da177e4SLinus Torvalds 132787e9f031SWANG Cong if (needs_rtnl) 132887e9f031SWANG Cong rtnl_lock(); 13291da177e4SLinus Torvalds lock_sock(sk); 13301da177e4SLinus Torvalds 13311da177e4SLinus Torvalds switch (optname) { 13321da177e4SLinus Torvalds case IP_OPTIONS: 13331da177e4SLinus Torvalds { 13341da177e4SLinus Torvalds unsigned char optbuf[sizeof(struct ip_options)+40]; 13351da177e4SLinus Torvalds struct ip_options *opt = (struct ip_options *)optbuf; 1336f6d8bd05SEric Dumazet struct ip_options_rcu *inet_opt; 1337f6d8bd05SEric Dumazet 1338f6d8bd05SEric Dumazet inet_opt = rcu_dereference_protected(inet->inet_opt, 13391e1d04e6SHannes Frederic Sowa lockdep_sock_is_held(sk)); 13401da177e4SLinus Torvalds opt->optlen = 0; 1341f6d8bd05SEric Dumazet if (inet_opt) 1342f6d8bd05SEric Dumazet memcpy(optbuf, &inet_opt->opt, 13431da177e4SLinus Torvalds sizeof(struct ip_options) + 1344f6d8bd05SEric Dumazet inet_opt->opt.optlen); 13451da177e4SLinus Torvalds release_sock(sk); 13461da177e4SLinus Torvalds 13471da177e4SLinus Torvalds if (opt->optlen == 0) 13481da177e4SLinus Torvalds return put_user(0, optlen); 13491da177e4SLinus Torvalds 13501da177e4SLinus Torvalds ip_options_undo(opt); 13511da177e4SLinus Torvalds 13521da177e4SLinus Torvalds len = min_t(unsigned int, len, opt->optlen); 13531da177e4SLinus Torvalds if (put_user(len, optlen)) 13541da177e4SLinus Torvalds return -EFAULT; 13551da177e4SLinus Torvalds if (copy_to_user(optval, opt->__data, len)) 13561da177e4SLinus Torvalds return -EFAULT; 13571da177e4SLinus Torvalds return 0; 13581da177e4SLinus Torvalds } 13591da177e4SLinus Torvalds case IP_PKTINFO: 13601da177e4SLinus Torvalds val = (inet->cmsg_flags & IP_CMSG_PKTINFO) != 0; 13611da177e4SLinus Torvalds break; 13621da177e4SLinus Torvalds case IP_RECVTTL: 13631da177e4SLinus Torvalds val = (inet->cmsg_flags & IP_CMSG_TTL) != 0; 13641da177e4SLinus Torvalds break; 13651da177e4SLinus Torvalds case IP_RECVTOS: 13661da177e4SLinus Torvalds val = (inet->cmsg_flags & IP_CMSG_TOS) != 0; 13671da177e4SLinus Torvalds break; 13681da177e4SLinus Torvalds case IP_RECVOPTS: 13691da177e4SLinus Torvalds val = (inet->cmsg_flags & IP_CMSG_RECVOPTS) != 0; 13701da177e4SLinus Torvalds break; 13711da177e4SLinus Torvalds case IP_RETOPTS: 13721da177e4SLinus Torvalds val = (inet->cmsg_flags & IP_CMSG_RETOPTS) != 0; 13731da177e4SLinus Torvalds break; 13742c7946a7SCatherine Zhang case IP_PASSSEC: 13752c7946a7SCatherine Zhang val = (inet->cmsg_flags & IP_CMSG_PASSSEC) != 0; 13762c7946a7SCatherine Zhang break; 1377e8b2dfe9SBalazs Scheidler case IP_RECVORIGDSTADDR: 1378e8b2dfe9SBalazs Scheidler val = (inet->cmsg_flags & IP_CMSG_ORIGDSTADDR) != 0; 1379e8b2dfe9SBalazs Scheidler break; 1380ad6f939aSTom Herbert case IP_CHECKSUM: 1381ad6f939aSTom Herbert val = (inet->cmsg_flags & IP_CMSG_CHECKSUM) != 0; 1382ad6f939aSTom Herbert break; 1383*70ecc248SWillem de Bruijn case IP_RECVFRAGSIZE: 1384*70ecc248SWillem de Bruijn val = (inet->cmsg_flags & IP_CMSG_RECVFRAGSIZE) != 0; 1385*70ecc248SWillem de Bruijn break; 13861da177e4SLinus Torvalds case IP_TOS: 13871da177e4SLinus Torvalds val = inet->tos; 13881da177e4SLinus Torvalds break; 13891da177e4SLinus Torvalds case IP_TTL: 1390fa50d974SNikolay Borisov { 1391fa50d974SNikolay Borisov struct net *net = sock_net(sk); 13921da177e4SLinus Torvalds val = (inet->uc_ttl == -1 ? 1393fa50d974SNikolay Borisov net->ipv4.sysctl_ip_default_ttl : 13941da177e4SLinus Torvalds inet->uc_ttl); 13951da177e4SLinus Torvalds break; 1396fa50d974SNikolay Borisov } 13971da177e4SLinus Torvalds case IP_HDRINCL: 13981da177e4SLinus Torvalds val = inet->hdrincl; 13991da177e4SLinus Torvalds break; 1400a89b4763SMichael Kerrisk case IP_NODEFRAG: 1401a89b4763SMichael Kerrisk val = inet->nodefrag; 1402a89b4763SMichael Kerrisk break; 140390c337daSEric Dumazet case IP_BIND_ADDRESS_NO_PORT: 140490c337daSEric Dumazet val = inet->bind_address_no_port; 140590c337daSEric Dumazet break; 14061da177e4SLinus Torvalds case IP_MTU_DISCOVER: 14071da177e4SLinus Torvalds val = inet->pmtudisc; 14081da177e4SLinus Torvalds break; 14091da177e4SLinus Torvalds case IP_MTU: 14101da177e4SLinus Torvalds { 14111da177e4SLinus Torvalds struct dst_entry *dst; 14121da177e4SLinus Torvalds val = 0; 14131da177e4SLinus Torvalds dst = sk_dst_get(sk); 14141da177e4SLinus Torvalds if (dst) { 14151da177e4SLinus Torvalds val = dst_mtu(dst); 14161da177e4SLinus Torvalds dst_release(dst); 14171da177e4SLinus Torvalds } 14181da177e4SLinus Torvalds if (!val) { 14191da177e4SLinus Torvalds release_sock(sk); 14201da177e4SLinus Torvalds return -ENOTCONN; 14211da177e4SLinus Torvalds } 14221da177e4SLinus Torvalds break; 14231da177e4SLinus Torvalds } 14241da177e4SLinus Torvalds case IP_RECVERR: 14251da177e4SLinus Torvalds val = inet->recverr; 14261da177e4SLinus Torvalds break; 14271da177e4SLinus Torvalds case IP_MULTICAST_TTL: 14281da177e4SLinus Torvalds val = inet->mc_ttl; 14291da177e4SLinus Torvalds break; 14301da177e4SLinus Torvalds case IP_MULTICAST_LOOP: 14311da177e4SLinus Torvalds val = inet->mc_loop; 14321da177e4SLinus Torvalds break; 143376e21053SErich E. Hoover case IP_UNICAST_IF: 143476e21053SErich E. Hoover val = (__force int)htonl((__u32) inet->uc_index); 143576e21053SErich E. Hoover break; 14361da177e4SLinus Torvalds case IP_MULTICAST_IF: 14371da177e4SLinus Torvalds { 14381da177e4SLinus Torvalds struct in_addr addr; 14391da177e4SLinus Torvalds len = min_t(unsigned int, len, sizeof(struct in_addr)); 14401da177e4SLinus Torvalds addr.s_addr = inet->mc_addr; 14411da177e4SLinus Torvalds release_sock(sk); 14421da177e4SLinus Torvalds 14431da177e4SLinus Torvalds if (put_user(len, optlen)) 14441da177e4SLinus Torvalds return -EFAULT; 14451da177e4SLinus Torvalds if (copy_to_user(optval, &addr, len)) 14461da177e4SLinus Torvalds return -EFAULT; 14471da177e4SLinus Torvalds return 0; 14481da177e4SLinus Torvalds } 14491da177e4SLinus Torvalds case IP_MSFILTER: 14501da177e4SLinus Torvalds { 14511da177e4SLinus Torvalds struct ip_msfilter msf; 14521da177e4SLinus Torvalds 14531da177e4SLinus Torvalds if (len < IP_MSFILTER_SIZE(0)) { 145487e9f031SWANG Cong err = -EINVAL; 145587e9f031SWANG Cong goto out; 14561da177e4SLinus Torvalds } 14571da177e4SLinus Torvalds if (copy_from_user(&msf, optval, IP_MSFILTER_SIZE(0))) { 145887e9f031SWANG Cong err = -EFAULT; 145987e9f031SWANG Cong goto out; 14601da177e4SLinus Torvalds } 14611da177e4SLinus Torvalds err = ip_mc_msfget(sk, &msf, 14621da177e4SLinus Torvalds (struct ip_msfilter __user *)optval, optlen); 146387e9f031SWANG Cong goto out; 14641da177e4SLinus Torvalds } 14651da177e4SLinus Torvalds case MCAST_MSFILTER: 14661da177e4SLinus Torvalds { 14671da177e4SLinus Torvalds struct group_filter gsf; 14681da177e4SLinus Torvalds 14691da177e4SLinus Torvalds if (len < GROUP_FILTER_SIZE(0)) { 147087e9f031SWANG Cong err = -EINVAL; 147187e9f031SWANG Cong goto out; 14721da177e4SLinus Torvalds } 14731da177e4SLinus Torvalds if (copy_from_user(&gsf, optval, GROUP_FILTER_SIZE(0))) { 147487e9f031SWANG Cong err = -EFAULT; 147587e9f031SWANG Cong goto out; 14761da177e4SLinus Torvalds } 14771da177e4SLinus Torvalds err = ip_mc_gsfget(sk, &gsf, 14784d52cfbeSEric Dumazet (struct group_filter __user *)optval, 14794d52cfbeSEric Dumazet optlen); 148087e9f031SWANG Cong goto out; 14811da177e4SLinus Torvalds } 1482f771bef9SNivedita Singhvi case IP_MULTICAST_ALL: 1483f771bef9SNivedita Singhvi val = inet->mc_all; 1484f771bef9SNivedita Singhvi break; 14851da177e4SLinus Torvalds case IP_PKTOPTIONS: 14861da177e4SLinus Torvalds { 14871da177e4SLinus Torvalds struct msghdr msg; 14881da177e4SLinus Torvalds 14891da177e4SLinus Torvalds release_sock(sk); 14901da177e4SLinus Torvalds 14911da177e4SLinus Torvalds if (sk->sk_type != SOCK_STREAM) 14921da177e4SLinus Torvalds return -ENOPROTOOPT; 14931da177e4SLinus Torvalds 1494c54a5e02SKaroly Kemeny msg.msg_control = (__force void *) optval; 14951da177e4SLinus Torvalds msg.msg_controllen = len; 1496dd23198eSDaniel Baluta msg.msg_flags = flags; 14971da177e4SLinus Torvalds 14981da177e4SLinus Torvalds if (inet->cmsg_flags & IP_CMSG_PKTINFO) { 14991da177e4SLinus Torvalds struct in_pktinfo info; 15001da177e4SLinus Torvalds 1501c720c7e8SEric Dumazet info.ipi_addr.s_addr = inet->inet_rcv_saddr; 1502c720c7e8SEric Dumazet info.ipi_spec_dst.s_addr = inet->inet_rcv_saddr; 15031da177e4SLinus Torvalds info.ipi_ifindex = inet->mc_index; 15041da177e4SLinus Torvalds put_cmsg(&msg, SOL_IP, IP_PKTINFO, sizeof(info), &info); 15051da177e4SLinus Torvalds } 15061da177e4SLinus Torvalds if (inet->cmsg_flags & IP_CMSG_TTL) { 15071da177e4SLinus Torvalds int hlim = inet->mc_ttl; 15081da177e4SLinus Torvalds put_cmsg(&msg, SOL_IP, IP_TTL, sizeof(hlim), &hlim); 15091da177e4SLinus Torvalds } 15104c507d28SJiri Benc if (inet->cmsg_flags & IP_CMSG_TOS) { 15114c507d28SJiri Benc int tos = inet->rcv_tos; 15124c507d28SJiri Benc put_cmsg(&msg, SOL_IP, IP_TOS, sizeof(tos), &tos); 15134c507d28SJiri Benc } 15141da177e4SLinus Torvalds len -= msg.msg_controllen; 15151da177e4SLinus Torvalds return put_user(len, optlen); 15161da177e4SLinus Torvalds } 15171da177e4SLinus Torvalds case IP_FREEBIND: 15181da177e4SLinus Torvalds val = inet->freebind; 15191da177e4SLinus Torvalds break; 1520f5715aeaSKOVACS Krisztian case IP_TRANSPARENT: 1521f5715aeaSKOVACS Krisztian val = inet->transparent; 1522f5715aeaSKOVACS Krisztian break; 1523d218d111SStephen Hemminger case IP_MINTTL: 1524d218d111SStephen Hemminger val = inet->min_ttl; 1525d218d111SStephen Hemminger break; 15261da177e4SLinus Torvalds default: 15271da177e4SLinus Torvalds release_sock(sk); 15281da177e4SLinus Torvalds return -ENOPROTOOPT; 15291da177e4SLinus Torvalds } 15301da177e4SLinus Torvalds release_sock(sk); 15311da177e4SLinus Torvalds 1532951e07c9SDavid S. Miller if (len < sizeof(int) && len > 0 && val >= 0 && val <= 255) { 15331da177e4SLinus Torvalds unsigned char ucval = (unsigned char)val; 15341da177e4SLinus Torvalds len = 1; 15351da177e4SLinus Torvalds if (put_user(len, optlen)) 15361da177e4SLinus Torvalds return -EFAULT; 15371da177e4SLinus Torvalds if (copy_to_user(optval, &ucval, 1)) 15381da177e4SLinus Torvalds return -EFAULT; 15391da177e4SLinus Torvalds } else { 15401da177e4SLinus Torvalds len = min_t(unsigned int, sizeof(int), len); 15411da177e4SLinus Torvalds if (put_user(len, optlen)) 15421da177e4SLinus Torvalds return -EFAULT; 15431da177e4SLinus Torvalds if (copy_to_user(optval, &val, len)) 15441da177e4SLinus Torvalds return -EFAULT; 15451da177e4SLinus Torvalds } 15461da177e4SLinus Torvalds return 0; 154787e9f031SWANG Cong 154887e9f031SWANG Cong out: 154987e9f031SWANG Cong release_sock(sk); 155087e9f031SWANG Cong if (needs_rtnl) 155187e9f031SWANG Cong rtnl_unlock(); 155287e9f031SWANG Cong return err; 15531da177e4SLinus Torvalds } 15541da177e4SLinus Torvalds 15553fdadf7dSDmitry Mishin int ip_getsockopt(struct sock *sk, int level, 15563fdadf7dSDmitry Mishin int optname, char __user *optval, int __user *optlen) 15573fdadf7dSDmitry Mishin { 15583fdadf7dSDmitry Mishin int err; 15593fdadf7dSDmitry Mishin 1560dd23198eSDaniel Baluta err = do_ip_getsockopt(sk, level, optname, optval, optlen, 0); 15613fdadf7dSDmitry Mishin #ifdef CONFIG_NETFILTER 15623fdadf7dSDmitry Mishin /* we need to exclude all possible ENOPROTOOPTs except default case */ 15636a9fb947SPavel Emelyanov if (err == -ENOPROTOOPT && optname != IP_PKTOPTIONS && 15646a9fb947SPavel Emelyanov !ip_mroute_opt(optname)) { 15653fdadf7dSDmitry Mishin int len; 15663fdadf7dSDmitry Mishin 15673fdadf7dSDmitry Mishin if (get_user(len, optlen)) 15683fdadf7dSDmitry Mishin return -EFAULT; 15693fdadf7dSDmitry Mishin 15703fdadf7dSDmitry Mishin lock_sock(sk); 15713fdadf7dSDmitry Mishin err = nf_getsockopt(sk, PF_INET, optname, optval, 15723fdadf7dSDmitry Mishin &len); 15733fdadf7dSDmitry Mishin release_sock(sk); 15743fdadf7dSDmitry Mishin if (err >= 0) 15753fdadf7dSDmitry Mishin err = put_user(len, optlen); 15763fdadf7dSDmitry Mishin return err; 15773fdadf7dSDmitry Mishin } 15783fdadf7dSDmitry Mishin #endif 15793fdadf7dSDmitry Mishin return err; 15803fdadf7dSDmitry Mishin } 15814d52cfbeSEric Dumazet EXPORT_SYMBOL(ip_getsockopt); 15823fdadf7dSDmitry Mishin 15833fdadf7dSDmitry Mishin #ifdef CONFIG_COMPAT 1584543d9cfeSArnaldo Carvalho de Melo int compat_ip_getsockopt(struct sock *sk, int level, int optname, 1585543d9cfeSArnaldo Carvalho de Melo char __user *optval, int __user *optlen) 15863fdadf7dSDmitry Mishin { 158742908c69SDavid L Stevens int err; 158842908c69SDavid L Stevens 158942908c69SDavid L Stevens if (optname == MCAST_MSFILTER) 159042908c69SDavid L Stevens return compat_mc_getsockopt(sk, level, optname, optval, optlen, 159142908c69SDavid L Stevens ip_getsockopt); 159242908c69SDavid L Stevens 1593dd23198eSDaniel Baluta err = do_ip_getsockopt(sk, level, optname, optval, optlen, 1594dd23198eSDaniel Baluta MSG_CMSG_COMPAT); 159542908c69SDavid L Stevens 15963fdadf7dSDmitry Mishin #ifdef CONFIG_NETFILTER 15973fdadf7dSDmitry Mishin /* we need to exclude all possible ENOPROTOOPTs except default case */ 15986a9fb947SPavel Emelyanov if (err == -ENOPROTOOPT && optname != IP_PKTOPTIONS && 15996a9fb947SPavel Emelyanov !ip_mroute_opt(optname)) { 16003fdadf7dSDmitry Mishin int len; 16013fdadf7dSDmitry Mishin 16023fdadf7dSDmitry Mishin if (get_user(len, optlen)) 16033fdadf7dSDmitry Mishin return -EFAULT; 16043fdadf7dSDmitry Mishin 16053fdadf7dSDmitry Mishin lock_sock(sk); 1606543d9cfeSArnaldo Carvalho de Melo err = compat_nf_getsockopt(sk, PF_INET, optname, optval, &len); 16073fdadf7dSDmitry Mishin release_sock(sk); 16083fdadf7dSDmitry Mishin if (err >= 0) 16093fdadf7dSDmitry Mishin err = put_user(len, optlen); 16103fdadf7dSDmitry Mishin return err; 16113fdadf7dSDmitry Mishin } 16123fdadf7dSDmitry Mishin #endif 16133fdadf7dSDmitry Mishin return err; 16143fdadf7dSDmitry Mishin } 1615543d9cfeSArnaldo Carvalho de Melo EXPORT_SYMBOL(compat_ip_getsockopt); 16163fdadf7dSDmitry Mishin #endif 1617