11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * Extension Header handling for IPv6 31da177e4SLinus Torvalds * Linux INET6 implementation 41da177e4SLinus Torvalds * 51da177e4SLinus Torvalds * Authors: 61da177e4SLinus Torvalds * Pedro Roque <roque@di.fc.ul.pt> 71da177e4SLinus Torvalds * Andi Kleen <ak@muc.de> 81da177e4SLinus Torvalds * Alexey Kuznetsov <kuznet@ms2.inr.ac.ru> 91da177e4SLinus Torvalds * 101da177e4SLinus Torvalds * This program is free software; you can redistribute it and/or 111da177e4SLinus Torvalds * modify it under the terms of the GNU General Public License 121da177e4SLinus Torvalds * as published by the Free Software Foundation; either version 131da177e4SLinus Torvalds * 2 of the License, or (at your option) any later version. 141da177e4SLinus Torvalds */ 151da177e4SLinus Torvalds 161da177e4SLinus Torvalds /* Changes: 171da177e4SLinus Torvalds * yoshfuji : ensure not to overrun while parsing 181da177e4SLinus Torvalds * tlv options. 191da177e4SLinus Torvalds * Mitsuru KANDA @USAGI and: Remove ipv6_parse_exthdrs(). 201da177e4SLinus Torvalds * YOSHIFUJI Hideaki @USAGI Register inbound extension header 211da177e4SLinus Torvalds * handlers as inet6_protocol{}. 221da177e4SLinus Torvalds */ 231da177e4SLinus Torvalds 241da177e4SLinus Torvalds #include <linux/errno.h> 251da177e4SLinus Torvalds #include <linux/types.h> 261da177e4SLinus Torvalds #include <linux/socket.h> 271da177e4SLinus Torvalds #include <linux/sockios.h> 281da177e4SLinus Torvalds #include <linux/net.h> 291da177e4SLinus Torvalds #include <linux/netdevice.h> 301da177e4SLinus Torvalds #include <linux/in6.h> 311da177e4SLinus Torvalds #include <linux/icmpv6.h> 325a0e3ad6STejun Heo #include <linux/slab.h> 33bc3b2d7fSPaul Gortmaker #include <linux/export.h> 341da177e4SLinus Torvalds 35352e512cSHerbert Xu #include <net/dst.h> 361da177e4SLinus Torvalds #include <net/sock.h> 371da177e4SLinus Torvalds #include <net/snmp.h> 381da177e4SLinus Torvalds 391da177e4SLinus Torvalds #include <net/ipv6.h> 401da177e4SLinus Torvalds #include <net/protocol.h> 411da177e4SLinus Torvalds #include <net/transp_v6.h> 421da177e4SLinus Torvalds #include <net/rawv6.h> 431da177e4SLinus Torvalds #include <net/ndisc.h> 441da177e4SLinus Torvalds #include <net/ip6_route.h> 451da177e4SLinus Torvalds #include <net/addrconf.h> 4659fbb3a6SMasahide NAKAMURA #if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE) 4765d4ed92SMasahide NAKAMURA #include <net/xfrm.h> 4865d4ed92SMasahide NAKAMURA #endif 491da177e4SLinus Torvalds 501da177e4SLinus Torvalds #include <asm/uaccess.h> 511da177e4SLinus Torvalds 52c61a4043SMasahide NAKAMURA int ipv6_find_tlv(struct sk_buff *skb, int offset, int type) 53c61a4043SMasahide NAKAMURA { 54d56f90a7SArnaldo Carvalho de Melo const unsigned char *nh = skb_network_header(skb); 5527a884dcSArnaldo Carvalho de Melo int packet_len = skb->tail - skb->network_header; 56c61a4043SMasahide NAKAMURA struct ipv6_opt_hdr *hdr; 57c61a4043SMasahide NAKAMURA int len; 58c61a4043SMasahide NAKAMURA 59c61a4043SMasahide NAKAMURA if (offset + 2 > packet_len) 60c61a4043SMasahide NAKAMURA goto bad; 61d56f90a7SArnaldo Carvalho de Melo hdr = (struct ipv6_opt_hdr *)(nh + offset); 62c61a4043SMasahide NAKAMURA len = ((hdr->hdrlen + 1) << 3); 63c61a4043SMasahide NAKAMURA 64c61a4043SMasahide NAKAMURA if (offset + len > packet_len) 65c61a4043SMasahide NAKAMURA goto bad; 66c61a4043SMasahide NAKAMURA 67c61a4043SMasahide NAKAMURA offset += 2; 68c61a4043SMasahide NAKAMURA len -= 2; 69c61a4043SMasahide NAKAMURA 70c61a4043SMasahide NAKAMURA while (len > 0) { 71d56f90a7SArnaldo Carvalho de Melo int opttype = nh[offset]; 72c61a4043SMasahide NAKAMURA int optlen; 73c61a4043SMasahide NAKAMURA 74c61a4043SMasahide NAKAMURA if (opttype == type) 75c61a4043SMasahide NAKAMURA return offset; 76c61a4043SMasahide NAKAMURA 77c61a4043SMasahide NAKAMURA switch (opttype) { 78c61a4043SMasahide NAKAMURA case IPV6_TLV_PAD0: 79c61a4043SMasahide NAKAMURA optlen = 1; 80c61a4043SMasahide NAKAMURA break; 81c61a4043SMasahide NAKAMURA default: 82d56f90a7SArnaldo Carvalho de Melo optlen = nh[offset + 1] + 2; 83c61a4043SMasahide NAKAMURA if (optlen > len) 84c61a4043SMasahide NAKAMURA goto bad; 85c61a4043SMasahide NAKAMURA break; 86c61a4043SMasahide NAKAMURA } 87c61a4043SMasahide NAKAMURA offset += optlen; 88c61a4043SMasahide NAKAMURA len -= optlen; 89c61a4043SMasahide NAKAMURA } 90c61a4043SMasahide NAKAMURA /* not_found */ 91c61a4043SMasahide NAKAMURA bad: 92c61a4043SMasahide NAKAMURA return -1; 93c61a4043SMasahide NAKAMURA } 9459fbb3a6SMasahide NAKAMURA EXPORT_SYMBOL_GPL(ipv6_find_tlv); 95c61a4043SMasahide NAKAMURA 961da177e4SLinus Torvalds /* 971da177e4SLinus Torvalds * Parsing tlv encoded headers. 981da177e4SLinus Torvalds * 991da177e4SLinus Torvalds * Parsing function "func" returns 1, if parsing succeed 1001da177e4SLinus Torvalds * and 0, if it failed. 1011da177e4SLinus Torvalds * It MUST NOT touch skb->h. 1021da177e4SLinus Torvalds */ 1031da177e4SLinus Torvalds 1041da177e4SLinus Torvalds struct tlvtype_proc { 1051da177e4SLinus Torvalds int type; 106e5bbef20SHerbert Xu int (*func)(struct sk_buff *skb, int offset); 1071da177e4SLinus Torvalds }; 1081da177e4SLinus Torvalds 1091da177e4SLinus Torvalds /********************* 1101da177e4SLinus Torvalds Generic functions 1111da177e4SLinus Torvalds *********************/ 1121da177e4SLinus Torvalds 1131da177e4SLinus Torvalds /* An unknown option is detected, decide what to do */ 1141da177e4SLinus Torvalds 115e5bbef20SHerbert Xu static int ip6_tlvopt_unknown(struct sk_buff *skb, int optoff) 1161da177e4SLinus Torvalds { 117d56f90a7SArnaldo Carvalho de Melo switch ((skb_network_header(skb)[optoff] & 0xC0) >> 6) { 1181da177e4SLinus Torvalds case 0: /* ignore */ 1191da177e4SLinus Torvalds return 1; 1201da177e4SLinus Torvalds 1211da177e4SLinus Torvalds case 1: /* drop packet */ 1221da177e4SLinus Torvalds break; 1231da177e4SLinus Torvalds 1241da177e4SLinus Torvalds case 3: /* Send ICMP if not a multicast address and drop packet */ 1251da177e4SLinus Torvalds /* Actually, it is redundant check. icmp_send 1261da177e4SLinus Torvalds will recheck in any case. 1271da177e4SLinus Torvalds */ 1280660e03fSArnaldo Carvalho de Melo if (ipv6_addr_is_multicast(&ipv6_hdr(skb)->daddr)) 1291da177e4SLinus Torvalds break; 1301da177e4SLinus Torvalds case 2: /* send ICMP PARM PROB regardless and drop packet */ 1311da177e4SLinus Torvalds icmpv6_param_prob(skb, ICMPV6_UNK_OPTION, optoff); 1321da177e4SLinus Torvalds return 0; 1333ff50b79SStephen Hemminger } 1341da177e4SLinus Torvalds 1351da177e4SLinus Torvalds kfree_skb(skb); 1361da177e4SLinus Torvalds return 0; 1371da177e4SLinus Torvalds } 1381da177e4SLinus Torvalds 1391da177e4SLinus Torvalds /* Parse tlv encoded option header (hop-by-hop or destination) */ 1401da177e4SLinus Torvalds 141e5bbef20SHerbert Xu static int ip6_parse_tlv(struct tlvtype_proc *procs, struct sk_buff *skb) 1421da177e4SLinus Torvalds { 1431da177e4SLinus Torvalds struct tlvtype_proc *curr; 144d56f90a7SArnaldo Carvalho de Melo const unsigned char *nh = skb_network_header(skb); 145cfe1fc77SArnaldo Carvalho de Melo int off = skb_network_header_len(skb); 1469c70220bSArnaldo Carvalho de Melo int len = (skb_transport_header(skb)[1] + 1) << 3; 1471da177e4SLinus Torvalds 148ea2ae17dSArnaldo Carvalho de Melo if (skb_transport_offset(skb) + len > skb_headlen(skb)) 1491da177e4SLinus Torvalds goto bad; 1501da177e4SLinus Torvalds 1511da177e4SLinus Torvalds off += 2; 1521da177e4SLinus Torvalds len -= 2; 1531da177e4SLinus Torvalds 1541da177e4SLinus Torvalds while (len > 0) { 155d56f90a7SArnaldo Carvalho de Melo int optlen = nh[off + 1] + 2; 1561da177e4SLinus Torvalds 157d56f90a7SArnaldo Carvalho de Melo switch (nh[off]) { 1581da177e4SLinus Torvalds case IPV6_TLV_PAD0: 1591da177e4SLinus Torvalds optlen = 1; 1601da177e4SLinus Torvalds break; 1611da177e4SLinus Torvalds 1621da177e4SLinus Torvalds case IPV6_TLV_PADN: 1631da177e4SLinus Torvalds break; 1641da177e4SLinus Torvalds 1651da177e4SLinus Torvalds default: /* Other TLV code so scan list */ 1661da177e4SLinus Torvalds if (optlen > len) 1671da177e4SLinus Torvalds goto bad; 1681da177e4SLinus Torvalds for (curr=procs; curr->type >= 0; curr++) { 169d56f90a7SArnaldo Carvalho de Melo if (curr->type == nh[off]) { 1701da177e4SLinus Torvalds /* type specific length/alignment 1711da177e4SLinus Torvalds checks will be performed in the 1721da177e4SLinus Torvalds func(). */ 173e5bbef20SHerbert Xu if (curr->func(skb, off) == 0) 1741da177e4SLinus Torvalds return 0; 1751da177e4SLinus Torvalds break; 1761da177e4SLinus Torvalds } 1771da177e4SLinus Torvalds } 1781da177e4SLinus Torvalds if (curr->type < 0) { 179e5bbef20SHerbert Xu if (ip6_tlvopt_unknown(skb, off) == 0) 1801da177e4SLinus Torvalds return 0; 1811da177e4SLinus Torvalds } 1821da177e4SLinus Torvalds break; 1831da177e4SLinus Torvalds } 1841da177e4SLinus Torvalds off += optlen; 1851da177e4SLinus Torvalds len -= optlen; 1861da177e4SLinus Torvalds } 1871da177e4SLinus Torvalds if (len == 0) 1881da177e4SLinus Torvalds return 1; 1891da177e4SLinus Torvalds bad: 1901da177e4SLinus Torvalds kfree_skb(skb); 1911da177e4SLinus Torvalds return 0; 1921da177e4SLinus Torvalds } 1931da177e4SLinus Torvalds 1941da177e4SLinus Torvalds /***************************** 1951da177e4SLinus Torvalds Destination options header. 1961da177e4SLinus Torvalds *****************************/ 1971da177e4SLinus Torvalds 19859fbb3a6SMasahide NAKAMURA #if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE) 199e5bbef20SHerbert Xu static int ipv6_dest_hao(struct sk_buff *skb, int optoff) 200a831f5bbSMasahide NAKAMURA { 201a831f5bbSMasahide NAKAMURA struct ipv6_destopt_hao *hao; 202a831f5bbSMasahide NAKAMURA struct inet6_skb_parm *opt = IP6CB(skb); 2030660e03fSArnaldo Carvalho de Melo struct ipv6hdr *ipv6h = ipv6_hdr(skb); 204a831f5bbSMasahide NAKAMURA struct in6_addr tmp_addr; 205a831f5bbSMasahide NAKAMURA int ret; 206a831f5bbSMasahide NAKAMURA 207a831f5bbSMasahide NAKAMURA if (opt->dsthao) { 208a831f5bbSMasahide NAKAMURA LIMIT_NETDEBUG(KERN_DEBUG "hao duplicated\n"); 209a831f5bbSMasahide NAKAMURA goto discard; 210a831f5bbSMasahide NAKAMURA } 211a831f5bbSMasahide NAKAMURA opt->dsthao = opt->dst1; 212a831f5bbSMasahide NAKAMURA opt->dst1 = 0; 213a831f5bbSMasahide NAKAMURA 214d56f90a7SArnaldo Carvalho de Melo hao = (struct ipv6_destopt_hao *)(skb_network_header(skb) + optoff); 215a831f5bbSMasahide NAKAMURA 216a831f5bbSMasahide NAKAMURA if (hao->length != 16) { 217a831f5bbSMasahide NAKAMURA LIMIT_NETDEBUG( 218a831f5bbSMasahide NAKAMURA KERN_DEBUG "hao invalid option length = %d\n", hao->length); 219a831f5bbSMasahide NAKAMURA goto discard; 220a831f5bbSMasahide NAKAMURA } 221a831f5bbSMasahide NAKAMURA 222a831f5bbSMasahide NAKAMURA if (!(ipv6_addr_type(&hao->addr) & IPV6_ADDR_UNICAST)) { 223a831f5bbSMasahide NAKAMURA LIMIT_NETDEBUG( 2245b095d98SHarvey Harrison KERN_DEBUG "hao is not an unicast addr: %pI6\n", &hao->addr); 225a831f5bbSMasahide NAKAMURA goto discard; 226a831f5bbSMasahide NAKAMURA } 227a831f5bbSMasahide NAKAMURA 228a831f5bbSMasahide NAKAMURA ret = xfrm6_input_addr(skb, (xfrm_address_t *)&ipv6h->daddr, 229a831f5bbSMasahide NAKAMURA (xfrm_address_t *)&hao->addr, IPPROTO_DSTOPTS); 230a831f5bbSMasahide NAKAMURA if (unlikely(ret < 0)) 231a831f5bbSMasahide NAKAMURA goto discard; 232a831f5bbSMasahide NAKAMURA 233a831f5bbSMasahide NAKAMURA if (skb_cloned(skb)) { 23465c88466SHerbert Xu if (pskb_expand_head(skb, 0, 0, GFP_ATOMIC)) 235a831f5bbSMasahide NAKAMURA goto discard; 236a831f5bbSMasahide NAKAMURA 237a831f5bbSMasahide NAKAMURA /* update all variable using below by copied skbuff */ 23865c88466SHerbert Xu hao = (struct ipv6_destopt_hao *)(skb_network_header(skb) + 239d56f90a7SArnaldo Carvalho de Melo optoff); 24065c88466SHerbert Xu ipv6h = ipv6_hdr(skb); 241a831f5bbSMasahide NAKAMURA } 242a831f5bbSMasahide NAKAMURA 243a831f5bbSMasahide NAKAMURA if (skb->ip_summed == CHECKSUM_COMPLETE) 244a831f5bbSMasahide NAKAMURA skb->ip_summed = CHECKSUM_NONE; 245a831f5bbSMasahide NAKAMURA 246*4e3fd7a0SAlexey Dobriyan tmp_addr = ipv6h->saddr; 247*4e3fd7a0SAlexey Dobriyan ipv6h->saddr = hao->addr; 248*4e3fd7a0SAlexey Dobriyan hao->addr = tmp_addr; 249a831f5bbSMasahide NAKAMURA 250b7aa0bf7SEric Dumazet if (skb->tstamp.tv64 == 0) 251a831f5bbSMasahide NAKAMURA __net_timestamp(skb); 252a831f5bbSMasahide NAKAMURA 253a831f5bbSMasahide NAKAMURA return 1; 254a831f5bbSMasahide NAKAMURA 255a831f5bbSMasahide NAKAMURA discard: 256a831f5bbSMasahide NAKAMURA kfree_skb(skb); 257a831f5bbSMasahide NAKAMURA return 0; 258a831f5bbSMasahide NAKAMURA } 259a831f5bbSMasahide NAKAMURA #endif 260a831f5bbSMasahide NAKAMURA 2611da177e4SLinus Torvalds static struct tlvtype_proc tlvprocdestopt_lst[] = { 26259fbb3a6SMasahide NAKAMURA #if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE) 263a831f5bbSMasahide NAKAMURA { 264a831f5bbSMasahide NAKAMURA .type = IPV6_TLV_HAO, 265a831f5bbSMasahide NAKAMURA .func = ipv6_dest_hao, 266a831f5bbSMasahide NAKAMURA }, 267a831f5bbSMasahide NAKAMURA #endif 2681da177e4SLinus Torvalds {-1, NULL} 2691da177e4SLinus Torvalds }; 2701da177e4SLinus Torvalds 271e5bbef20SHerbert Xu static int ipv6_destopt_rcv(struct sk_buff *skb) 2721da177e4SLinus Torvalds { 2731da177e4SLinus Torvalds struct inet6_skb_parm *opt = IP6CB(skb); 27459fbb3a6SMasahide NAKAMURA #if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE) 275a831f5bbSMasahide NAKAMURA __u16 dstbuf; 276a831f5bbSMasahide NAKAMURA #endif 277897dc80bSEric Dumazet struct dst_entry *dst = skb_dst(skb); 2781da177e4SLinus Torvalds 279ea2ae17dSArnaldo Carvalho de Melo if (!pskb_may_pull(skb, skb_transport_offset(skb) + 8) || 280ea2ae17dSArnaldo Carvalho de Melo !pskb_may_pull(skb, (skb_transport_offset(skb) + 2819c70220bSArnaldo Carvalho de Melo ((skb_transport_header(skb)[1] + 1) << 3)))) { 282897dc80bSEric Dumazet IP6_INC_STATS_BH(dev_net(dst->dev), ip6_dst_idev(dst), 283a11d206dSYOSHIFUJI Hideaki IPSTATS_MIB_INHDRERRORS); 2841da177e4SLinus Torvalds kfree_skb(skb); 2851da177e4SLinus Torvalds return -1; 2861da177e4SLinus Torvalds } 2871da177e4SLinus Torvalds 288cfe1fc77SArnaldo Carvalho de Melo opt->lastopt = opt->dst1 = skb_network_header_len(skb); 28959fbb3a6SMasahide NAKAMURA #if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE) 290a831f5bbSMasahide NAKAMURA dstbuf = opt->dst1; 291a831f5bbSMasahide NAKAMURA #endif 2921da177e4SLinus Torvalds 293e5bbef20SHerbert Xu if (ip6_parse_tlv(tlvprocdestopt_lst, skb)) { 294b0e380b1SArnaldo Carvalho de Melo skb->transport_header += (skb_transport_header(skb)[1] + 1) << 3; 295dc435e6dSMasahide NAKAMURA opt = IP6CB(skb); 29659fbb3a6SMasahide NAKAMURA #if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE) 297a831f5bbSMasahide NAKAMURA opt->nhoff = dstbuf; 298a831f5bbSMasahide NAKAMURA #else 299951dbc8aSPatrick McHardy opt->nhoff = opt->dst1; 300a831f5bbSMasahide NAKAMURA #endif 3011da177e4SLinus Torvalds return 1; 3021da177e4SLinus Torvalds } 3031da177e4SLinus Torvalds 304483a47d2SDenis V. Lunev IP6_INC_STATS_BH(dev_net(dst->dev), 305483a47d2SDenis V. Lunev ip6_dst_idev(dst), IPSTATS_MIB_INHDRERRORS); 3061da177e4SLinus Torvalds return -1; 3071da177e4SLinus Torvalds } 3081da177e4SLinus Torvalds 3091da177e4SLinus Torvalds /******************************** 3101da177e4SLinus Torvalds Routing header. 3111da177e4SLinus Torvalds ********************************/ 3121da177e4SLinus Torvalds 313f6bc7d9eSEric Dumazet /* called with rcu_read_lock() */ 314e5bbef20SHerbert Xu static int ipv6_rthdr_rcv(struct sk_buff *skb) 3151da177e4SLinus Torvalds { 3161da177e4SLinus Torvalds struct inet6_skb_parm *opt = IP6CB(skb); 31765d4ed92SMasahide NAKAMURA struct in6_addr *addr = NULL; 3181da177e4SLinus Torvalds struct in6_addr daddr; 3190bcbc926SYOSHIFUJI Hideaki struct inet6_dev *idev; 3201da177e4SLinus Torvalds int n, i; 3211da177e4SLinus Torvalds struct ipv6_rt_hdr *hdr; 3221da177e4SLinus Torvalds struct rt0_hdr *rthdr; 323483a47d2SDenis V. Lunev struct net *net = dev_net(skb->dev); 324483a47d2SDenis V. Lunev int accept_source_route = net->ipv6.devconf_all->accept_source_route; 3250bcbc926SYOSHIFUJI Hideaki 326f6bc7d9eSEric Dumazet idev = __in6_dev_get(skb->dev); 327f6bc7d9eSEric Dumazet if (idev && accept_source_route > idev->cnf.accept_source_route) 3280bcbc926SYOSHIFUJI Hideaki accept_source_route = idev->cnf.accept_source_route; 3291da177e4SLinus Torvalds 330ea2ae17dSArnaldo Carvalho de Melo if (!pskb_may_pull(skb, skb_transport_offset(skb) + 8) || 331ea2ae17dSArnaldo Carvalho de Melo !pskb_may_pull(skb, (skb_transport_offset(skb) + 3329c70220bSArnaldo Carvalho de Melo ((skb_transport_header(skb)[1] + 1) << 3)))) { 333adf30907SEric Dumazet IP6_INC_STATS_BH(net, ip6_dst_idev(skb_dst(skb)), 334a11d206dSYOSHIFUJI Hideaki IPSTATS_MIB_INHDRERRORS); 3351da177e4SLinus Torvalds kfree_skb(skb); 3361da177e4SLinus Torvalds return -1; 3371da177e4SLinus Torvalds } 3381da177e4SLinus Torvalds 3399c70220bSArnaldo Carvalho de Melo hdr = (struct ipv6_rt_hdr *)skb_transport_header(skb); 3401da177e4SLinus Torvalds 3410660e03fSArnaldo Carvalho de Melo if (ipv6_addr_is_multicast(&ipv6_hdr(skb)->daddr) || 3421da177e4SLinus Torvalds skb->pkt_type != PACKET_HOST) { 343adf30907SEric Dumazet IP6_INC_STATS_BH(net, ip6_dst_idev(skb_dst(skb)), 344a11d206dSYOSHIFUJI Hideaki IPSTATS_MIB_INADDRERRORS); 3451da177e4SLinus Torvalds kfree_skb(skb); 3461da177e4SLinus Torvalds return -1; 3471da177e4SLinus Torvalds } 3481da177e4SLinus Torvalds 3491da177e4SLinus Torvalds looped_back: 3501da177e4SLinus Torvalds if (hdr->segments_left == 0) { 35165d4ed92SMasahide NAKAMURA switch (hdr->type) { 35259fbb3a6SMasahide NAKAMURA #if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE) 35365d4ed92SMasahide NAKAMURA case IPV6_SRCRT_TYPE_2: 35465d4ed92SMasahide NAKAMURA /* Silently discard type 2 header unless it was 35565d4ed92SMasahide NAKAMURA * processed by own 35665d4ed92SMasahide NAKAMURA */ 35765d4ed92SMasahide NAKAMURA if (!addr) { 358adf30907SEric Dumazet IP6_INC_STATS_BH(net, ip6_dst_idev(skb_dst(skb)), 359a11d206dSYOSHIFUJI Hideaki IPSTATS_MIB_INADDRERRORS); 36065d4ed92SMasahide NAKAMURA kfree_skb(skb); 36165d4ed92SMasahide NAKAMURA return -1; 36265d4ed92SMasahide NAKAMURA } 36365d4ed92SMasahide NAKAMURA break; 36465d4ed92SMasahide NAKAMURA #endif 36565d4ed92SMasahide NAKAMURA default: 36665d4ed92SMasahide NAKAMURA break; 36765d4ed92SMasahide NAKAMURA } 36865d4ed92SMasahide NAKAMURA 369cfe1fc77SArnaldo Carvalho de Melo opt->lastopt = opt->srcrt = skb_network_header_len(skb); 370b0e380b1SArnaldo Carvalho de Melo skb->transport_header += (hdr->hdrlen + 1) << 3; 3711da177e4SLinus Torvalds opt->dst0 = opt->dst1; 3721da177e4SLinus Torvalds opt->dst1 = 0; 373d56f90a7SArnaldo Carvalho de Melo opt->nhoff = (&hdr->nexthdr) - skb_network_header(skb); 3741da177e4SLinus Torvalds return 1; 3751da177e4SLinus Torvalds } 3761da177e4SLinus Torvalds 37765d4ed92SMasahide NAKAMURA switch (hdr->type) { 37859fbb3a6SMasahide NAKAMURA #if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE) 37965d4ed92SMasahide NAKAMURA case IPV6_SRCRT_TYPE_2: 380c382bb9dSYOSHIFUJI Hideaki if (accept_source_route < 0) 381c382bb9dSYOSHIFUJI Hideaki goto unknown_rh; 38265d4ed92SMasahide NAKAMURA /* Silently discard invalid RTH type 2 */ 38365d4ed92SMasahide NAKAMURA if (hdr->hdrlen != 2 || hdr->segments_left != 1) { 384adf30907SEric Dumazet IP6_INC_STATS_BH(net, ip6_dst_idev(skb_dst(skb)), 385a11d206dSYOSHIFUJI Hideaki IPSTATS_MIB_INHDRERRORS); 38665d4ed92SMasahide NAKAMURA kfree_skb(skb); 38765d4ed92SMasahide NAKAMURA return -1; 38865d4ed92SMasahide NAKAMURA } 38965d4ed92SMasahide NAKAMURA break; 39065d4ed92SMasahide NAKAMURA #endif 391c382bb9dSYOSHIFUJI Hideaki default: 392c382bb9dSYOSHIFUJI Hideaki goto unknown_rh; 39365d4ed92SMasahide NAKAMURA } 3941da177e4SLinus Torvalds 3951da177e4SLinus Torvalds /* 3961da177e4SLinus Torvalds * This is the routing header forwarding algorithm from 3971da177e4SLinus Torvalds * RFC 2460, page 16. 3981da177e4SLinus Torvalds */ 3991da177e4SLinus Torvalds 4001da177e4SLinus Torvalds n = hdr->hdrlen >> 1; 4011da177e4SLinus Torvalds 4021da177e4SLinus Torvalds if (hdr->segments_left > n) { 403adf30907SEric Dumazet IP6_INC_STATS_BH(net, ip6_dst_idev(skb_dst(skb)), 404a11d206dSYOSHIFUJI Hideaki IPSTATS_MIB_INHDRERRORS); 405d56f90a7SArnaldo Carvalho de Melo icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, 406d56f90a7SArnaldo Carvalho de Melo ((&hdr->segments_left) - 407d56f90a7SArnaldo Carvalho de Melo skb_network_header(skb))); 4081da177e4SLinus Torvalds return -1; 4091da177e4SLinus Torvalds } 4101da177e4SLinus Torvalds 4111da177e4SLinus Torvalds /* We are about to mangle packet header. Be careful! 4121da177e4SLinus Torvalds Do not damage packets queued somewhere. 4131da177e4SLinus Torvalds */ 4141da177e4SLinus Torvalds if (skb_cloned(skb)) { 4151da177e4SLinus Torvalds /* the copy is a forwarded packet */ 41665c88466SHerbert Xu if (pskb_expand_head(skb, 0, 0, GFP_ATOMIC)) { 417adf30907SEric Dumazet IP6_INC_STATS_BH(net, ip6_dst_idev(skb_dst(skb)), 418a11d206dSYOSHIFUJI Hideaki IPSTATS_MIB_OUTDISCARDS); 419a11d206dSYOSHIFUJI Hideaki kfree_skb(skb); 4201da177e4SLinus Torvalds return -1; 4211da177e4SLinus Torvalds } 42265c88466SHerbert Xu hdr = (struct ipv6_rt_hdr *)skb_transport_header(skb); 4231da177e4SLinus Torvalds } 4241da177e4SLinus Torvalds 42584fa7933SPatrick McHardy if (skb->ip_summed == CHECKSUM_COMPLETE) 4261da177e4SLinus Torvalds skb->ip_summed = CHECKSUM_NONE; 4271da177e4SLinus Torvalds 4281da177e4SLinus Torvalds i = n - --hdr->segments_left; 4291da177e4SLinus Torvalds 4301da177e4SLinus Torvalds rthdr = (struct rt0_hdr *) hdr; 4311da177e4SLinus Torvalds addr = rthdr->addr; 4321da177e4SLinus Torvalds addr += i - 1; 4331da177e4SLinus Torvalds 43465d4ed92SMasahide NAKAMURA switch (hdr->type) { 43559fbb3a6SMasahide NAKAMURA #if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE) 43665d4ed92SMasahide NAKAMURA case IPV6_SRCRT_TYPE_2: 43765d4ed92SMasahide NAKAMURA if (xfrm6_input_addr(skb, (xfrm_address_t *)addr, 4380660e03fSArnaldo Carvalho de Melo (xfrm_address_t *)&ipv6_hdr(skb)->saddr, 43965d4ed92SMasahide NAKAMURA IPPROTO_ROUTING) < 0) { 440adf30907SEric Dumazet IP6_INC_STATS_BH(net, ip6_dst_idev(skb_dst(skb)), 441a11d206dSYOSHIFUJI Hideaki IPSTATS_MIB_INADDRERRORS); 44265d4ed92SMasahide NAKAMURA kfree_skb(skb); 44365d4ed92SMasahide NAKAMURA return -1; 44465d4ed92SMasahide NAKAMURA } 445adf30907SEric Dumazet if (!ipv6_chk_home_addr(dev_net(skb_dst(skb)->dev), addr)) { 446adf30907SEric Dumazet IP6_INC_STATS_BH(net, ip6_dst_idev(skb_dst(skb)), 447a11d206dSYOSHIFUJI Hideaki IPSTATS_MIB_INADDRERRORS); 44865d4ed92SMasahide NAKAMURA kfree_skb(skb); 44965d4ed92SMasahide NAKAMURA return -1; 45065d4ed92SMasahide NAKAMURA } 45165d4ed92SMasahide NAKAMURA break; 45265d4ed92SMasahide NAKAMURA #endif 45365d4ed92SMasahide NAKAMURA default: 45465d4ed92SMasahide NAKAMURA break; 45565d4ed92SMasahide NAKAMURA } 45665d4ed92SMasahide NAKAMURA 4571da177e4SLinus Torvalds if (ipv6_addr_is_multicast(addr)) { 458adf30907SEric Dumazet IP6_INC_STATS_BH(net, ip6_dst_idev(skb_dst(skb)), 459a11d206dSYOSHIFUJI Hideaki IPSTATS_MIB_INADDRERRORS); 4601da177e4SLinus Torvalds kfree_skb(skb); 4611da177e4SLinus Torvalds return -1; 4621da177e4SLinus Torvalds } 4631da177e4SLinus Torvalds 464*4e3fd7a0SAlexey Dobriyan daddr = *addr; 465*4e3fd7a0SAlexey Dobriyan *addr = ipv6_hdr(skb)->daddr; 466*4e3fd7a0SAlexey Dobriyan ipv6_hdr(skb)->daddr = daddr; 4671da177e4SLinus Torvalds 468adf30907SEric Dumazet skb_dst_drop(skb); 4691da177e4SLinus Torvalds ip6_route_input(skb); 470adf30907SEric Dumazet if (skb_dst(skb)->error) { 471d56f90a7SArnaldo Carvalho de Melo skb_push(skb, skb->data - skb_network_header(skb)); 4721da177e4SLinus Torvalds dst_input(skb); 4731da177e4SLinus Torvalds return -1; 4741da177e4SLinus Torvalds } 4751da177e4SLinus Torvalds 476adf30907SEric Dumazet if (skb_dst(skb)->dev->flags&IFF_LOOPBACK) { 4770660e03fSArnaldo Carvalho de Melo if (ipv6_hdr(skb)->hop_limit <= 1) { 478adf30907SEric Dumazet IP6_INC_STATS_BH(net, ip6_dst_idev(skb_dst(skb)), 479a11d206dSYOSHIFUJI Hideaki IPSTATS_MIB_INHDRERRORS); 4801da177e4SLinus Torvalds icmpv6_send(skb, ICMPV6_TIME_EXCEED, ICMPV6_EXC_HOPLIMIT, 4813ffe533cSAlexey Dobriyan 0); 4821da177e4SLinus Torvalds kfree_skb(skb); 4831da177e4SLinus Torvalds return -1; 4841da177e4SLinus Torvalds } 4850660e03fSArnaldo Carvalho de Melo ipv6_hdr(skb)->hop_limit--; 4861da177e4SLinus Torvalds goto looped_back; 4871da177e4SLinus Torvalds } 4881da177e4SLinus Torvalds 489d56f90a7SArnaldo Carvalho de Melo skb_push(skb, skb->data - skb_network_header(skb)); 4901da177e4SLinus Torvalds dst_input(skb); 4911da177e4SLinus Torvalds return -1; 492c382bb9dSYOSHIFUJI Hideaki 493c382bb9dSYOSHIFUJI Hideaki unknown_rh: 494adf30907SEric Dumazet IP6_INC_STATS_BH(net, ip6_dst_idev(skb_dst(skb)), IPSTATS_MIB_INHDRERRORS); 495c382bb9dSYOSHIFUJI Hideaki icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, 496c382bb9dSYOSHIFUJI Hideaki (&hdr->type) - skb_network_header(skb)); 497c382bb9dSYOSHIFUJI Hideaki return -1; 4981da177e4SLinus Torvalds } 4991da177e4SLinus Torvalds 50041135cc8SAlexey Dobriyan static const struct inet6_protocol rthdr_protocol = { 5011da177e4SLinus Torvalds .handler = ipv6_rthdr_rcv, 502adcfc7d0SHerbert Xu .flags = INET6_PROTO_NOPOLICY | INET6_PROTO_GSO_EXTHDR, 5031da177e4SLinus Torvalds }; 5041da177e4SLinus Torvalds 50541135cc8SAlexey Dobriyan static const struct inet6_protocol destopt_protocol = { 506248b238dSDaniel Lezcano .handler = ipv6_destopt_rcv, 507248b238dSDaniel Lezcano .flags = INET6_PROTO_NOPOLICY | INET6_PROTO_GSO_EXTHDR, 5081da177e4SLinus Torvalds }; 5091da177e4SLinus Torvalds 51041135cc8SAlexey Dobriyan static const struct inet6_protocol nodata_protocol = { 511248b238dSDaniel Lezcano .handler = dst_discard, 512248b238dSDaniel Lezcano .flags = INET6_PROTO_NOPOLICY, 513248b238dSDaniel Lezcano }; 514248b238dSDaniel Lezcano 515248b238dSDaniel Lezcano int __init ipv6_exthdrs_init(void) 516248b238dSDaniel Lezcano { 517248b238dSDaniel Lezcano int ret; 518248b238dSDaniel Lezcano 519248b238dSDaniel Lezcano ret = inet6_add_protocol(&rthdr_protocol, IPPROTO_ROUTING); 520248b238dSDaniel Lezcano if (ret) 521248b238dSDaniel Lezcano goto out; 522248b238dSDaniel Lezcano 523248b238dSDaniel Lezcano ret = inet6_add_protocol(&destopt_protocol, IPPROTO_DSTOPTS); 524248b238dSDaniel Lezcano if (ret) 525248b238dSDaniel Lezcano goto out_rthdr; 526248b238dSDaniel Lezcano 527248b238dSDaniel Lezcano ret = inet6_add_protocol(&nodata_protocol, IPPROTO_NONE); 528248b238dSDaniel Lezcano if (ret) 529248b238dSDaniel Lezcano goto out_destopt; 530248b238dSDaniel Lezcano 531248b238dSDaniel Lezcano out: 532248b238dSDaniel Lezcano return ret; 533248b238dSDaniel Lezcano out_rthdr: 534248b238dSDaniel Lezcano inet6_del_protocol(&rthdr_protocol, IPPROTO_ROUTING); 535248b238dSDaniel Lezcano out_destopt: 536248b238dSDaniel Lezcano inet6_del_protocol(&destopt_protocol, IPPROTO_DSTOPTS); 537248b238dSDaniel Lezcano goto out; 538248b238dSDaniel Lezcano }; 539248b238dSDaniel Lezcano 540248b238dSDaniel Lezcano void ipv6_exthdrs_exit(void) 541248b238dSDaniel Lezcano { 542248b238dSDaniel Lezcano inet6_del_protocol(&nodata_protocol, IPPROTO_NONE); 543248b238dSDaniel Lezcano inet6_del_protocol(&destopt_protocol, IPPROTO_DSTOPTS); 544248b238dSDaniel Lezcano inet6_del_protocol(&rthdr_protocol, IPPROTO_ROUTING); 545248b238dSDaniel Lezcano } 546248b238dSDaniel Lezcano 5471da177e4SLinus Torvalds /********************************** 5481da177e4SLinus Torvalds Hop-by-hop options. 5491da177e4SLinus Torvalds **********************************/ 5501da177e4SLinus Torvalds 551e76b2b25SYOSHIFUJI Hideaki /* 552adf30907SEric Dumazet * Note: we cannot rely on skb_dst(skb) before we assign it in ip6_route_input(). 553e76b2b25SYOSHIFUJI Hideaki */ 554e76b2b25SYOSHIFUJI Hideaki static inline struct inet6_dev *ipv6_skb_idev(struct sk_buff *skb) 555e76b2b25SYOSHIFUJI Hideaki { 556adf30907SEric Dumazet return skb_dst(skb) ? ip6_dst_idev(skb_dst(skb)) : __in6_dev_get(skb->dev); 557e76b2b25SYOSHIFUJI Hideaki } 558e76b2b25SYOSHIFUJI Hideaki 5592570a4f5SDavid S. Miller static inline struct net *ipv6_skb_net(struct sk_buff *skb) 5602570a4f5SDavid S. Miller { 5612570a4f5SDavid S. Miller return skb_dst(skb) ? dev_net(skb_dst(skb)->dev) : dev_net(skb->dev); 5622570a4f5SDavid S. Miller } 5632570a4f5SDavid S. Miller 5641da177e4SLinus Torvalds /* Router Alert as of RFC 2711 */ 5651da177e4SLinus Torvalds 566e5bbef20SHerbert Xu static int ipv6_hop_ra(struct sk_buff *skb, int optoff) 5671da177e4SLinus Torvalds { 568d56f90a7SArnaldo Carvalho de Melo const unsigned char *nh = skb_network_header(skb); 569a80ff03eSMasahide NAKAMURA 570d56f90a7SArnaldo Carvalho de Melo if (nh[optoff + 1] == 2) { 5711da177e4SLinus Torvalds IP6CB(skb)->ra = optoff; 5721da177e4SLinus Torvalds return 1; 5731da177e4SLinus Torvalds } 57464ce2073SPatrick McHardy LIMIT_NETDEBUG(KERN_DEBUG "ipv6_hop_ra: wrong RA length %d\n", 575d56f90a7SArnaldo Carvalho de Melo nh[optoff + 1]); 5761da177e4SLinus Torvalds kfree_skb(skb); 5771da177e4SLinus Torvalds return 0; 5781da177e4SLinus Torvalds } 5791da177e4SLinus Torvalds 5801da177e4SLinus Torvalds /* Jumbo payload */ 5811da177e4SLinus Torvalds 582e5bbef20SHerbert Xu static int ipv6_hop_jumbo(struct sk_buff *skb, int optoff) 5831da177e4SLinus Torvalds { 584d56f90a7SArnaldo Carvalho de Melo const unsigned char *nh = skb_network_header(skb); 5852570a4f5SDavid S. Miller struct net *net = ipv6_skb_net(skb); 5861da177e4SLinus Torvalds u32 pkt_len; 5871da177e4SLinus Torvalds 588d56f90a7SArnaldo Carvalho de Melo if (nh[optoff + 1] != 4 || (optoff & 3) != 2) { 58964ce2073SPatrick McHardy LIMIT_NETDEBUG(KERN_DEBUG "ipv6_hop_jumbo: wrong jumbo opt length/alignment %d\n", 590d56f90a7SArnaldo Carvalho de Melo nh[optoff+1]); 591483a47d2SDenis V. Lunev IP6_INC_STATS_BH(net, ipv6_skb_idev(skb), 592a11d206dSYOSHIFUJI Hideaki IPSTATS_MIB_INHDRERRORS); 5931da177e4SLinus Torvalds goto drop; 5941da177e4SLinus Torvalds } 5951da177e4SLinus Torvalds 596d56f90a7SArnaldo Carvalho de Melo pkt_len = ntohl(*(__be32 *)(nh + optoff + 2)); 5971da177e4SLinus Torvalds if (pkt_len <= IPV6_MAXPLEN) { 598483a47d2SDenis V. Lunev IP6_INC_STATS_BH(net, ipv6_skb_idev(skb), 599483a47d2SDenis V. Lunev IPSTATS_MIB_INHDRERRORS); 6001da177e4SLinus Torvalds icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, optoff+2); 6011da177e4SLinus Torvalds return 0; 6021da177e4SLinus Torvalds } 6030660e03fSArnaldo Carvalho de Melo if (ipv6_hdr(skb)->payload_len) { 604483a47d2SDenis V. Lunev IP6_INC_STATS_BH(net, ipv6_skb_idev(skb), 605483a47d2SDenis V. Lunev IPSTATS_MIB_INHDRERRORS); 6061da177e4SLinus Torvalds icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, optoff); 6071da177e4SLinus Torvalds return 0; 6081da177e4SLinus Torvalds } 6091da177e4SLinus Torvalds 6101da177e4SLinus Torvalds if (pkt_len > skb->len - sizeof(struct ipv6hdr)) { 611483a47d2SDenis V. Lunev IP6_INC_STATS_BH(net, ipv6_skb_idev(skb), 612483a47d2SDenis V. Lunev IPSTATS_MIB_INTRUNCATEDPKTS); 6131da177e4SLinus Torvalds goto drop; 6141da177e4SLinus Torvalds } 61542ca89c1SStephen Hemminger 61642ca89c1SStephen Hemminger if (pskb_trim_rcsum(skb, pkt_len + sizeof(struct ipv6hdr))) 61742ca89c1SStephen Hemminger goto drop; 61842ca89c1SStephen Hemminger 6191da177e4SLinus Torvalds return 1; 6201da177e4SLinus Torvalds 6211da177e4SLinus Torvalds drop: 6221da177e4SLinus Torvalds kfree_skb(skb); 6231da177e4SLinus Torvalds return 0; 6241da177e4SLinus Torvalds } 6251da177e4SLinus Torvalds 6261da177e4SLinus Torvalds static struct tlvtype_proc tlvprochopopt_lst[] = { 6271da177e4SLinus Torvalds { 6281da177e4SLinus Torvalds .type = IPV6_TLV_ROUTERALERT, 6291da177e4SLinus Torvalds .func = ipv6_hop_ra, 6301da177e4SLinus Torvalds }, 6311da177e4SLinus Torvalds { 6321da177e4SLinus Torvalds .type = IPV6_TLV_JUMBO, 6331da177e4SLinus Torvalds .func = ipv6_hop_jumbo, 6341da177e4SLinus Torvalds }, 6351da177e4SLinus Torvalds { -1, } 6361da177e4SLinus Torvalds }; 6371da177e4SLinus Torvalds 638e5bbef20SHerbert Xu int ipv6_parse_hopopts(struct sk_buff *skb) 6391da177e4SLinus Torvalds { 640951dbc8aSPatrick McHardy struct inet6_skb_parm *opt = IP6CB(skb); 641951dbc8aSPatrick McHardy 642ec670095SYOSHIFUJI Hideaki /* 643d56f90a7SArnaldo Carvalho de Melo * skb_network_header(skb) is equal to skb->data, and 644cfe1fc77SArnaldo Carvalho de Melo * skb_network_header_len(skb) is always equal to 645ec670095SYOSHIFUJI Hideaki * sizeof(struct ipv6hdr) by definition of 646ec670095SYOSHIFUJI Hideaki * hop-by-hop options. 647ec670095SYOSHIFUJI Hideaki */ 648ec670095SYOSHIFUJI Hideaki if (!pskb_may_pull(skb, sizeof(struct ipv6hdr) + 8) || 6499c70220bSArnaldo Carvalho de Melo !pskb_may_pull(skb, (sizeof(struct ipv6hdr) + 6509c70220bSArnaldo Carvalho de Melo ((skb_transport_header(skb)[1] + 1) << 3)))) { 651ec670095SYOSHIFUJI Hideaki kfree_skb(skb); 652ec670095SYOSHIFUJI Hideaki return -1; 653ec670095SYOSHIFUJI Hideaki } 654ec670095SYOSHIFUJI Hideaki 655951dbc8aSPatrick McHardy opt->hop = sizeof(struct ipv6hdr); 656e5bbef20SHerbert Xu if (ip6_parse_tlv(tlvprochopopt_lst, skb)) { 657b0e380b1SArnaldo Carvalho de Melo skb->transport_header += (skb_transport_header(skb)[1] + 1) << 3; 658dc435e6dSMasahide NAKAMURA opt = IP6CB(skb); 659951dbc8aSPatrick McHardy opt->nhoff = sizeof(struct ipv6hdr); 660b809739aSYOSHIFUJI Hideaki return 1; 661951dbc8aSPatrick McHardy } 6621da177e4SLinus Torvalds return -1; 6631da177e4SLinus Torvalds } 6641da177e4SLinus Torvalds 6651da177e4SLinus Torvalds /* 6661da177e4SLinus Torvalds * Creating outbound headers. 6671da177e4SLinus Torvalds * 6681da177e4SLinus Torvalds * "build" functions work when skb is filled from head to tail (datagram) 6691da177e4SLinus Torvalds * "push" functions work when headers are added from tail to head (tcp) 6701da177e4SLinus Torvalds * 6711da177e4SLinus Torvalds * In both cases we assume, that caller reserved enough room 6721da177e4SLinus Torvalds * for headers. 6731da177e4SLinus Torvalds */ 6741da177e4SLinus Torvalds 6751da177e4SLinus Torvalds static void ipv6_push_rthdr(struct sk_buff *skb, u8 *proto, 6761da177e4SLinus Torvalds struct ipv6_rt_hdr *opt, 6771da177e4SLinus Torvalds struct in6_addr **addr_p) 6781da177e4SLinus Torvalds { 6791da177e4SLinus Torvalds struct rt0_hdr *phdr, *ihdr; 6801da177e4SLinus Torvalds int hops; 6811da177e4SLinus Torvalds 6821da177e4SLinus Torvalds ihdr = (struct rt0_hdr *) opt; 6831da177e4SLinus Torvalds 6841da177e4SLinus Torvalds phdr = (struct rt0_hdr *) skb_push(skb, (ihdr->rt_hdr.hdrlen + 1) << 3); 6851da177e4SLinus Torvalds memcpy(phdr, ihdr, sizeof(struct rt0_hdr)); 6861da177e4SLinus Torvalds 6871da177e4SLinus Torvalds hops = ihdr->rt_hdr.hdrlen >> 1; 6881da177e4SLinus Torvalds 6891da177e4SLinus Torvalds if (hops > 1) 6901da177e4SLinus Torvalds memcpy(phdr->addr, ihdr->addr + 1, 6911da177e4SLinus Torvalds (hops - 1) * sizeof(struct in6_addr)); 6921da177e4SLinus Torvalds 693*4e3fd7a0SAlexey Dobriyan phdr->addr[hops - 1] = **addr_p; 6941da177e4SLinus Torvalds *addr_p = ihdr->addr; 6951da177e4SLinus Torvalds 6961da177e4SLinus Torvalds phdr->rt_hdr.nexthdr = *proto; 6971da177e4SLinus Torvalds *proto = NEXTHDR_ROUTING; 6981da177e4SLinus Torvalds } 6991da177e4SLinus Torvalds 7001da177e4SLinus Torvalds static void ipv6_push_exthdr(struct sk_buff *skb, u8 *proto, u8 type, struct ipv6_opt_hdr *opt) 7011da177e4SLinus Torvalds { 7021da177e4SLinus Torvalds struct ipv6_opt_hdr *h = (struct ipv6_opt_hdr *)skb_push(skb, ipv6_optlen(opt)); 7031da177e4SLinus Torvalds 7041da177e4SLinus Torvalds memcpy(h, opt, ipv6_optlen(opt)); 7051da177e4SLinus Torvalds h->nexthdr = *proto; 7061da177e4SLinus Torvalds *proto = type; 7071da177e4SLinus Torvalds } 7081da177e4SLinus Torvalds 7091da177e4SLinus Torvalds void ipv6_push_nfrag_opts(struct sk_buff *skb, struct ipv6_txoptions *opt, 7101da177e4SLinus Torvalds u8 *proto, 7111da177e4SLinus Torvalds struct in6_addr **daddr) 7121da177e4SLinus Torvalds { 713333fad53SYOSHIFUJI Hideaki if (opt->srcrt) { 7141da177e4SLinus Torvalds ipv6_push_rthdr(skb, proto, opt->srcrt, daddr); 715333fad53SYOSHIFUJI Hideaki /* 716333fad53SYOSHIFUJI Hideaki * IPV6_RTHDRDSTOPTS is ignored 717333fad53SYOSHIFUJI Hideaki * unless IPV6_RTHDR is set (RFC3542). 718333fad53SYOSHIFUJI Hideaki */ 7191da177e4SLinus Torvalds if (opt->dst0opt) 7201da177e4SLinus Torvalds ipv6_push_exthdr(skb, proto, NEXTHDR_DEST, opt->dst0opt); 721333fad53SYOSHIFUJI Hideaki } 7221da177e4SLinus Torvalds if (opt->hopopt) 7231da177e4SLinus Torvalds ipv6_push_exthdr(skb, proto, NEXTHDR_HOP, opt->hopopt); 7241da177e4SLinus Torvalds } 7251da177e4SLinus Torvalds 7267159039aSYOSHIFUJI Hideaki EXPORT_SYMBOL(ipv6_push_nfrag_opts); 7277159039aSYOSHIFUJI Hideaki 7281da177e4SLinus Torvalds void ipv6_push_frag_opts(struct sk_buff *skb, struct ipv6_txoptions *opt, u8 *proto) 7291da177e4SLinus Torvalds { 7301da177e4SLinus Torvalds if (opt->dst1opt) 7311da177e4SLinus Torvalds ipv6_push_exthdr(skb, proto, NEXTHDR_DEST, opt->dst1opt); 7321da177e4SLinus Torvalds } 7331da177e4SLinus Torvalds 7341da177e4SLinus Torvalds struct ipv6_txoptions * 7351da177e4SLinus Torvalds ipv6_dup_options(struct sock *sk, struct ipv6_txoptions *opt) 7361da177e4SLinus Torvalds { 7371da177e4SLinus Torvalds struct ipv6_txoptions *opt2; 7381da177e4SLinus Torvalds 7391da177e4SLinus Torvalds opt2 = sock_kmalloc(sk, opt->tot_len, GFP_ATOMIC); 7401da177e4SLinus Torvalds if (opt2) { 7411da177e4SLinus Torvalds long dif = (char*)opt2 - (char*)opt; 7421da177e4SLinus Torvalds memcpy(opt2, opt, opt->tot_len); 7431da177e4SLinus Torvalds if (opt2->hopopt) 7441da177e4SLinus Torvalds *((char**)&opt2->hopopt) += dif; 7451da177e4SLinus Torvalds if (opt2->dst0opt) 7461da177e4SLinus Torvalds *((char**)&opt2->dst0opt) += dif; 7471da177e4SLinus Torvalds if (opt2->dst1opt) 7481da177e4SLinus Torvalds *((char**)&opt2->dst1opt) += dif; 7491da177e4SLinus Torvalds if (opt2->srcrt) 7501da177e4SLinus Torvalds *((char**)&opt2->srcrt) += dif; 7511da177e4SLinus Torvalds } 7521da177e4SLinus Torvalds return opt2; 7531da177e4SLinus Torvalds } 754333fad53SYOSHIFUJI Hideaki 7553cf3dc6cSArnaldo Carvalho de Melo EXPORT_SYMBOL_GPL(ipv6_dup_options); 7563cf3dc6cSArnaldo Carvalho de Melo 757333fad53SYOSHIFUJI Hideaki static int ipv6_renew_option(void *ohdr, 758333fad53SYOSHIFUJI Hideaki struct ipv6_opt_hdr __user *newopt, int newoptlen, 759333fad53SYOSHIFUJI Hideaki int inherit, 760333fad53SYOSHIFUJI Hideaki struct ipv6_opt_hdr **hdr, 761333fad53SYOSHIFUJI Hideaki char **p) 762333fad53SYOSHIFUJI Hideaki { 763333fad53SYOSHIFUJI Hideaki if (inherit) { 764333fad53SYOSHIFUJI Hideaki if (ohdr) { 765333fad53SYOSHIFUJI Hideaki memcpy(*p, ohdr, ipv6_optlen((struct ipv6_opt_hdr *)ohdr)); 766333fad53SYOSHIFUJI Hideaki *hdr = (struct ipv6_opt_hdr *)*p; 767333fad53SYOSHIFUJI Hideaki *p += CMSG_ALIGN(ipv6_optlen(*(struct ipv6_opt_hdr **)hdr)); 768333fad53SYOSHIFUJI Hideaki } 769333fad53SYOSHIFUJI Hideaki } else { 770333fad53SYOSHIFUJI Hideaki if (newopt) { 771333fad53SYOSHIFUJI Hideaki if (copy_from_user(*p, newopt, newoptlen)) 772333fad53SYOSHIFUJI Hideaki return -EFAULT; 773333fad53SYOSHIFUJI Hideaki *hdr = (struct ipv6_opt_hdr *)*p; 774333fad53SYOSHIFUJI Hideaki if (ipv6_optlen(*(struct ipv6_opt_hdr **)hdr) > newoptlen) 775333fad53SYOSHIFUJI Hideaki return -EINVAL; 776333fad53SYOSHIFUJI Hideaki *p += CMSG_ALIGN(newoptlen); 777333fad53SYOSHIFUJI Hideaki } 778333fad53SYOSHIFUJI Hideaki } 779333fad53SYOSHIFUJI Hideaki return 0; 780333fad53SYOSHIFUJI Hideaki } 781333fad53SYOSHIFUJI Hideaki 782333fad53SYOSHIFUJI Hideaki struct ipv6_txoptions * 783333fad53SYOSHIFUJI Hideaki ipv6_renew_options(struct sock *sk, struct ipv6_txoptions *opt, 784333fad53SYOSHIFUJI Hideaki int newtype, 785333fad53SYOSHIFUJI Hideaki struct ipv6_opt_hdr __user *newopt, int newoptlen) 786333fad53SYOSHIFUJI Hideaki { 787333fad53SYOSHIFUJI Hideaki int tot_len = 0; 788333fad53SYOSHIFUJI Hideaki char *p; 789333fad53SYOSHIFUJI Hideaki struct ipv6_txoptions *opt2; 790333fad53SYOSHIFUJI Hideaki int err; 791333fad53SYOSHIFUJI Hideaki 79299c7bc01SYOSHIFUJI Hideaki if (opt) { 793333fad53SYOSHIFUJI Hideaki if (newtype != IPV6_HOPOPTS && opt->hopopt) 794333fad53SYOSHIFUJI Hideaki tot_len += CMSG_ALIGN(ipv6_optlen(opt->hopopt)); 795333fad53SYOSHIFUJI Hideaki if (newtype != IPV6_RTHDRDSTOPTS && opt->dst0opt) 796333fad53SYOSHIFUJI Hideaki tot_len += CMSG_ALIGN(ipv6_optlen(opt->dst0opt)); 797333fad53SYOSHIFUJI Hideaki if (newtype != IPV6_RTHDR && opt->srcrt) 798333fad53SYOSHIFUJI Hideaki tot_len += CMSG_ALIGN(ipv6_optlen(opt->srcrt)); 799333fad53SYOSHIFUJI Hideaki if (newtype != IPV6_DSTOPTS && opt->dst1opt) 800333fad53SYOSHIFUJI Hideaki tot_len += CMSG_ALIGN(ipv6_optlen(opt->dst1opt)); 80199c7bc01SYOSHIFUJI Hideaki } 80299c7bc01SYOSHIFUJI Hideaki 803333fad53SYOSHIFUJI Hideaki if (newopt && newoptlen) 804333fad53SYOSHIFUJI Hideaki tot_len += CMSG_ALIGN(newoptlen); 805333fad53SYOSHIFUJI Hideaki 806333fad53SYOSHIFUJI Hideaki if (!tot_len) 807333fad53SYOSHIFUJI Hideaki return NULL; 808333fad53SYOSHIFUJI Hideaki 8098b8aa4b5SYOSHIFUJI Hideaki tot_len += sizeof(*opt2); 810333fad53SYOSHIFUJI Hideaki opt2 = sock_kmalloc(sk, tot_len, GFP_ATOMIC); 811333fad53SYOSHIFUJI Hideaki if (!opt2) 812333fad53SYOSHIFUJI Hideaki return ERR_PTR(-ENOBUFS); 813333fad53SYOSHIFUJI Hideaki 814333fad53SYOSHIFUJI Hideaki memset(opt2, 0, tot_len); 815333fad53SYOSHIFUJI Hideaki 816333fad53SYOSHIFUJI Hideaki opt2->tot_len = tot_len; 817333fad53SYOSHIFUJI Hideaki p = (char *)(opt2 + 1); 818333fad53SYOSHIFUJI Hideaki 81999c7bc01SYOSHIFUJI Hideaki err = ipv6_renew_option(opt ? opt->hopopt : NULL, newopt, newoptlen, 820333fad53SYOSHIFUJI Hideaki newtype != IPV6_HOPOPTS, 821333fad53SYOSHIFUJI Hideaki &opt2->hopopt, &p); 822333fad53SYOSHIFUJI Hideaki if (err) 823333fad53SYOSHIFUJI Hideaki goto out; 824333fad53SYOSHIFUJI Hideaki 82599c7bc01SYOSHIFUJI Hideaki err = ipv6_renew_option(opt ? opt->dst0opt : NULL, newopt, newoptlen, 826333fad53SYOSHIFUJI Hideaki newtype != IPV6_RTHDRDSTOPTS, 827333fad53SYOSHIFUJI Hideaki &opt2->dst0opt, &p); 828333fad53SYOSHIFUJI Hideaki if (err) 829333fad53SYOSHIFUJI Hideaki goto out; 830333fad53SYOSHIFUJI Hideaki 83199c7bc01SYOSHIFUJI Hideaki err = ipv6_renew_option(opt ? opt->srcrt : NULL, newopt, newoptlen, 832333fad53SYOSHIFUJI Hideaki newtype != IPV6_RTHDR, 83399c7bc01SYOSHIFUJI Hideaki (struct ipv6_opt_hdr **)&opt2->srcrt, &p); 834333fad53SYOSHIFUJI Hideaki if (err) 835333fad53SYOSHIFUJI Hideaki goto out; 836333fad53SYOSHIFUJI Hideaki 83799c7bc01SYOSHIFUJI Hideaki err = ipv6_renew_option(opt ? opt->dst1opt : NULL, newopt, newoptlen, 838333fad53SYOSHIFUJI Hideaki newtype != IPV6_DSTOPTS, 839333fad53SYOSHIFUJI Hideaki &opt2->dst1opt, &p); 840333fad53SYOSHIFUJI Hideaki if (err) 841333fad53SYOSHIFUJI Hideaki goto out; 842333fad53SYOSHIFUJI Hideaki 843333fad53SYOSHIFUJI Hideaki opt2->opt_nflen = (opt2->hopopt ? ipv6_optlen(opt2->hopopt) : 0) + 844333fad53SYOSHIFUJI Hideaki (opt2->dst0opt ? ipv6_optlen(opt2->dst0opt) : 0) + 845333fad53SYOSHIFUJI Hideaki (opt2->srcrt ? ipv6_optlen(opt2->srcrt) : 0); 846333fad53SYOSHIFUJI Hideaki opt2->opt_flen = (opt2->dst1opt ? ipv6_optlen(opt2->dst1opt) : 0); 847333fad53SYOSHIFUJI Hideaki 848333fad53SYOSHIFUJI Hideaki return opt2; 849333fad53SYOSHIFUJI Hideaki out: 8508b8aa4b5SYOSHIFUJI Hideaki sock_kfree_s(sk, opt2, opt2->tot_len); 851333fad53SYOSHIFUJI Hideaki return ERR_PTR(err); 852333fad53SYOSHIFUJI Hideaki } 853333fad53SYOSHIFUJI Hideaki 854df9890c3SYOSHIFUJI Hideaki struct ipv6_txoptions *ipv6_fixup_options(struct ipv6_txoptions *opt_space, 855df9890c3SYOSHIFUJI Hideaki struct ipv6_txoptions *opt) 856df9890c3SYOSHIFUJI Hideaki { 857df9890c3SYOSHIFUJI Hideaki /* 858df9890c3SYOSHIFUJI Hideaki * ignore the dest before srcrt unless srcrt is being included. 859df9890c3SYOSHIFUJI Hideaki * --yoshfuji 860df9890c3SYOSHIFUJI Hideaki */ 861df9890c3SYOSHIFUJI Hideaki if (opt && opt->dst0opt && !opt->srcrt) { 862df9890c3SYOSHIFUJI Hideaki if (opt_space != opt) { 863df9890c3SYOSHIFUJI Hideaki memcpy(opt_space, opt, sizeof(*opt_space)); 864df9890c3SYOSHIFUJI Hideaki opt = opt_space; 865df9890c3SYOSHIFUJI Hideaki } 866df9890c3SYOSHIFUJI Hideaki opt->opt_nflen -= ipv6_optlen(opt->dst0opt); 867df9890c3SYOSHIFUJI Hideaki opt->dst0opt = NULL; 868df9890c3SYOSHIFUJI Hideaki } 869df9890c3SYOSHIFUJI Hideaki 870df9890c3SYOSHIFUJI Hideaki return opt; 871df9890c3SYOSHIFUJI Hideaki } 872df9890c3SYOSHIFUJI Hideaki 87320c59de2SArnaud Ebalard /** 87420c59de2SArnaud Ebalard * fl6_update_dst - update flowi destination address with info given 87520c59de2SArnaud Ebalard * by srcrt option, if any. 87620c59de2SArnaud Ebalard * 8774c9483b2SDavid S. Miller * @fl6: flowi6 for which daddr is to be updated 87820c59de2SArnaud Ebalard * @opt: struct ipv6_txoptions in which to look for srcrt opt 8794c9483b2SDavid S. Miller * @orig: copy of original daddr address if modified 88020c59de2SArnaud Ebalard * 88120c59de2SArnaud Ebalard * Returns NULL if no txoptions or no srcrt, otherwise returns orig 8824c9483b2SDavid S. Miller * and initial value of fl6->daddr set in orig 88320c59de2SArnaud Ebalard */ 8844c9483b2SDavid S. Miller struct in6_addr *fl6_update_dst(struct flowi6 *fl6, 88520c59de2SArnaud Ebalard const struct ipv6_txoptions *opt, 88620c59de2SArnaud Ebalard struct in6_addr *orig) 88720c59de2SArnaud Ebalard { 88820c59de2SArnaud Ebalard if (!opt || !opt->srcrt) 88920c59de2SArnaud Ebalard return NULL; 89020c59de2SArnaud Ebalard 891*4e3fd7a0SAlexey Dobriyan *orig = fl6->daddr; 892*4e3fd7a0SAlexey Dobriyan fl6->daddr = *((struct rt0_hdr *)opt->srcrt)->addr; 89320c59de2SArnaud Ebalard return orig; 89420c59de2SArnaud Ebalard } 89520c59de2SArnaud Ebalard 89620c59de2SArnaud Ebalard EXPORT_SYMBOL_GPL(fl6_update_dst); 897