11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * xfrm6_input.c: based on net/ipv4/xfrm4_input.c 31da177e4SLinus Torvalds * 41da177e4SLinus Torvalds * Authors: 51da177e4SLinus Torvalds * Mitsuru KANDA @USAGI 61da177e4SLinus Torvalds * Kazunori MIYAZAWA @USAGI 71da177e4SLinus Torvalds * Kunihiro Ishiguro <kunihiro@ipinfusion.com> 81da177e4SLinus Torvalds * YOSHIFUJI Hideaki @USAGI 91da177e4SLinus Torvalds * IPv6 support 101da177e4SLinus Torvalds */ 111da177e4SLinus Torvalds 121da177e4SLinus Torvalds #include <linux/module.h> 131da177e4SLinus Torvalds #include <linux/string.h> 14b05e1066SPatrick McHardy #include <linux/netfilter.h> 15b05e1066SPatrick McHardy #include <linux/netfilter_ipv6.h> 161da177e4SLinus Torvalds #include <net/dsfield.h> 171da177e4SLinus Torvalds #include <net/inet_ecn.h> 181da177e4SLinus Torvalds #include <net/ip.h> 191da177e4SLinus Torvalds #include <net/ipv6.h> 201da177e4SLinus Torvalds #include <net/xfrm.h> 211da177e4SLinus Torvalds 221da177e4SLinus Torvalds static inline void ipip6_ecn_decapsulate(struct sk_buff *skb) 231da177e4SLinus Torvalds { 241da177e4SLinus Torvalds struct ipv6hdr *outer_iph = skb->nh.ipv6h; 251da177e4SLinus Torvalds struct ipv6hdr *inner_iph = skb->h.ipv6h; 261da177e4SLinus Torvalds 271da177e4SLinus Torvalds if (INET_ECN_is_ce(ipv6_get_dsfield(outer_iph))) 281da177e4SLinus Torvalds IP6_ECN_set_ce(inner_iph); 291da177e4SLinus Torvalds } 301da177e4SLinus Torvalds 31951dbc8aSPatrick McHardy int xfrm6_rcv_spi(struct sk_buff **pskb, u32 spi) 321da177e4SLinus Torvalds { 331da177e4SLinus Torvalds struct sk_buff *skb = *pskb; 341da177e4SLinus Torvalds int err; 351da177e4SLinus Torvalds u32 seq; 361da177e4SLinus Torvalds struct sec_decap_state xfrm_vec[XFRM_MAX_DEPTH]; 371da177e4SLinus Torvalds struct xfrm_state *x; 381da177e4SLinus Torvalds int xfrm_nr = 0; 391da177e4SLinus Torvalds int decaps = 0; 401da177e4SLinus Torvalds int nexthdr; 411da177e4SLinus Torvalds unsigned int nhoff; 421da177e4SLinus Torvalds 43951dbc8aSPatrick McHardy nhoff = IP6CB(skb)->nhoff; 441da177e4SLinus Torvalds nexthdr = skb->nh.raw[nhoff]; 451da177e4SLinus Torvalds 461da177e4SLinus Torvalds seq = 0; 471da177e4SLinus Torvalds if (!spi && (err = xfrm_parse_spi(skb, nexthdr, &spi, &seq)) != 0) 481da177e4SLinus Torvalds goto drop; 491da177e4SLinus Torvalds 501da177e4SLinus Torvalds do { 511da177e4SLinus Torvalds struct ipv6hdr *iph = skb->nh.ipv6h; 521da177e4SLinus Torvalds 531da177e4SLinus Torvalds if (xfrm_nr == XFRM_MAX_DEPTH) 541da177e4SLinus Torvalds goto drop; 551da177e4SLinus Torvalds 561da177e4SLinus Torvalds x = xfrm_state_lookup((xfrm_address_t *)&iph->daddr, spi, nexthdr, AF_INET6); 571da177e4SLinus Torvalds if (x == NULL) 581da177e4SLinus Torvalds goto drop; 591da177e4SLinus Torvalds spin_lock(&x->lock); 601da177e4SLinus Torvalds if (unlikely(x->km.state != XFRM_STATE_VALID)) 611da177e4SLinus Torvalds goto drop_unlock; 621da177e4SLinus Torvalds 631da177e4SLinus Torvalds if (x->props.replay_window && xfrm_replay_check(x, seq)) 641da177e4SLinus Torvalds goto drop_unlock; 651da177e4SLinus Torvalds 661da177e4SLinus Torvalds if (xfrm_state_check_expire(x)) 671da177e4SLinus Torvalds goto drop_unlock; 681da177e4SLinus Torvalds 691da177e4SLinus Torvalds nexthdr = x->type->input(x, &(xfrm_vec[xfrm_nr].decap), skb); 701da177e4SLinus Torvalds if (nexthdr <= 0) 711da177e4SLinus Torvalds goto drop_unlock; 721da177e4SLinus Torvalds 731da177e4SLinus Torvalds skb->nh.raw[nhoff] = nexthdr; 741da177e4SLinus Torvalds 751da177e4SLinus Torvalds if (x->props.replay_window) 761da177e4SLinus Torvalds xfrm_replay_advance(x, seq); 771da177e4SLinus Torvalds 781da177e4SLinus Torvalds x->curlft.bytes += skb->len; 791da177e4SLinus Torvalds x->curlft.packets++; 801da177e4SLinus Torvalds 811da177e4SLinus Torvalds spin_unlock(&x->lock); 821da177e4SLinus Torvalds 831da177e4SLinus Torvalds xfrm_vec[xfrm_nr++].xvec = x; 841da177e4SLinus Torvalds 851da177e4SLinus Torvalds if (x->props.mode) { /* XXX */ 861da177e4SLinus Torvalds if (nexthdr != IPPROTO_IPV6) 871da177e4SLinus Torvalds goto drop; 881da177e4SLinus Torvalds if (!pskb_may_pull(skb, sizeof(struct ipv6hdr))) 891da177e4SLinus Torvalds goto drop; 901da177e4SLinus Torvalds if (skb_cloned(skb) && 911da177e4SLinus Torvalds pskb_expand_head(skb, 0, 0, GFP_ATOMIC)) 921da177e4SLinus Torvalds goto drop; 931da177e4SLinus Torvalds if (x->props.flags & XFRM_STATE_DECAP_DSCP) 941da177e4SLinus Torvalds ipv6_copy_dscp(skb->nh.ipv6h, skb->h.ipv6h); 951da177e4SLinus Torvalds if (!(x->props.flags & XFRM_STATE_NOECN)) 961da177e4SLinus Torvalds ipip6_ecn_decapsulate(skb); 971da177e4SLinus Torvalds skb->mac.raw = memmove(skb->data - skb->mac_len, 981da177e4SLinus Torvalds skb->mac.raw, skb->mac_len); 991da177e4SLinus Torvalds skb->nh.raw = skb->data; 1001da177e4SLinus Torvalds decaps = 1; 1011da177e4SLinus Torvalds break; 1021da177e4SLinus Torvalds } 1031da177e4SLinus Torvalds 1041da177e4SLinus Torvalds if ((err = xfrm_parse_spi(skb, nexthdr, &spi, &seq)) < 0) 1051da177e4SLinus Torvalds goto drop; 1061da177e4SLinus Torvalds } while (!err); 1071da177e4SLinus Torvalds 1081da177e4SLinus Torvalds /* Allocate new secpath or COW existing one. */ 1091da177e4SLinus Torvalds if (!skb->sp || atomic_read(&skb->sp->refcnt) != 1) { 1101da177e4SLinus Torvalds struct sec_path *sp; 1111da177e4SLinus Torvalds sp = secpath_dup(skb->sp); 1121da177e4SLinus Torvalds if (!sp) 1131da177e4SLinus Torvalds goto drop; 1141da177e4SLinus Torvalds if (skb->sp) 1151da177e4SLinus Torvalds secpath_put(skb->sp); 1161da177e4SLinus Torvalds skb->sp = sp; 1171da177e4SLinus Torvalds } 1181da177e4SLinus Torvalds 1191da177e4SLinus Torvalds if (xfrm_nr + skb->sp->len > XFRM_MAX_DEPTH) 1201da177e4SLinus Torvalds goto drop; 1211da177e4SLinus Torvalds 1221da177e4SLinus Torvalds memcpy(skb->sp->x+skb->sp->len, xfrm_vec, xfrm_nr*sizeof(struct sec_decap_state)); 1231da177e4SLinus Torvalds skb->sp->len += xfrm_nr; 1241da177e4SLinus Torvalds skb->ip_summed = CHECKSUM_NONE; 1251da177e4SLinus Torvalds 126b05e1066SPatrick McHardy nf_reset(skb); 127b05e1066SPatrick McHardy 1281da177e4SLinus Torvalds if (decaps) { 1291da177e4SLinus Torvalds if (!(skb->dev->flags&IFF_LOOPBACK)) { 1301da177e4SLinus Torvalds dst_release(skb->dst); 1311da177e4SLinus Torvalds skb->dst = NULL; 1321da177e4SLinus Torvalds } 1331da177e4SLinus Torvalds netif_rx(skb); 1341da177e4SLinus Torvalds return -1; 1351da177e4SLinus Torvalds } else { 136b05e1066SPatrick McHardy #ifdef CONFIG_NETFILTER 137b05e1066SPatrick McHardy skb->nh.ipv6h->payload_len = htons(skb->len); 138b05e1066SPatrick McHardy __skb_push(skb, skb->data - skb->nh.raw); 139b05e1066SPatrick McHardy 140b05e1066SPatrick McHardy NF_HOOK(PF_INET6, NF_IP6_PRE_ROUTING, skb, skb->dev, NULL, 141b05e1066SPatrick McHardy ip6_rcv_finish); 142b05e1066SPatrick McHardy return -1; 143b05e1066SPatrick McHardy #else 1441da177e4SLinus Torvalds return 1; 145b05e1066SPatrick McHardy #endif 1461da177e4SLinus Torvalds } 1471da177e4SLinus Torvalds 1481da177e4SLinus Torvalds drop_unlock: 1491da177e4SLinus Torvalds spin_unlock(&x->lock); 1501da177e4SLinus Torvalds xfrm_state_put(x); 1511da177e4SLinus Torvalds drop: 1521da177e4SLinus Torvalds while (--xfrm_nr >= 0) 1531da177e4SLinus Torvalds xfrm_state_put(xfrm_vec[xfrm_nr].xvec); 1541da177e4SLinus Torvalds kfree_skb(skb); 1551da177e4SLinus Torvalds return -1; 1561da177e4SLinus Torvalds } 1571da177e4SLinus Torvalds 1581da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm6_rcv_spi); 1591da177e4SLinus Torvalds 160951dbc8aSPatrick McHardy int xfrm6_rcv(struct sk_buff **pskb) 1611da177e4SLinus Torvalds { 162951dbc8aSPatrick McHardy return xfrm6_rcv_spi(pskb, 0); 1631da177e4SLinus Torvalds } 164