1 /* 2 * xfrm6_input.c: based on net/ipv4/xfrm4_input.c 3 * 4 * Authors: 5 * Mitsuru KANDA @USAGI 6 * Kazunori MIYAZAWA @USAGI 7 * Kunihiro Ishiguro <kunihiro@ipinfusion.com> 8 * YOSHIFUJI Hideaki @USAGI 9 * IPv6 support 10 */ 11 12 #include <linux/module.h> 13 #include <linux/string.h> 14 #include <linux/netfilter.h> 15 #include <linux/netfilter_ipv6.h> 16 #include <net/ipv6.h> 17 #include <net/xfrm.h> 18 19 int xfrm6_rcv_spi(struct sk_buff *skb, u32 spi) 20 { 21 int err; 22 u32 seq; 23 struct xfrm_state *xfrm_vec[XFRM_MAX_DEPTH]; 24 struct xfrm_state *x; 25 int xfrm_nr = 0; 26 int decaps = 0; 27 int nexthdr; 28 unsigned int nhoff; 29 30 nhoff = IP6CB(skb)->nhoff; 31 nexthdr = skb->nh.raw[nhoff]; 32 33 seq = 0; 34 if (!spi && (err = xfrm_parse_spi(skb, nexthdr, &spi, &seq)) != 0) 35 goto drop; 36 37 do { 38 struct ipv6hdr *iph = skb->nh.ipv6h; 39 40 if (xfrm_nr == XFRM_MAX_DEPTH) 41 goto drop; 42 43 x = xfrm_state_lookup((xfrm_address_t *)&iph->daddr, spi, nexthdr, AF_INET6); 44 if (x == NULL) 45 goto drop; 46 spin_lock(&x->lock); 47 if (unlikely(x->km.state != XFRM_STATE_VALID)) 48 goto drop_unlock; 49 50 if (x->props.replay_window && xfrm_replay_check(x, seq)) 51 goto drop_unlock; 52 53 if (xfrm_state_check_expire(x)) 54 goto drop_unlock; 55 56 nexthdr = x->type->input(x, skb); 57 if (nexthdr <= 0) 58 goto drop_unlock; 59 60 skb->nh.raw[nhoff] = nexthdr; 61 62 if (x->props.replay_window) 63 xfrm_replay_advance(x, seq); 64 65 x->curlft.bytes += skb->len; 66 x->curlft.packets++; 67 68 spin_unlock(&x->lock); 69 70 xfrm_vec[xfrm_nr++] = x; 71 72 if (x->mode->input(x, skb)) 73 goto drop; 74 75 if (x->props.mode) { /* XXX */ 76 decaps = 1; 77 break; 78 } 79 80 if ((err = xfrm_parse_spi(skb, nexthdr, &spi, &seq)) < 0) 81 goto drop; 82 } while (!err); 83 84 /* Allocate new secpath or COW existing one. */ 85 if (!skb->sp || atomic_read(&skb->sp->refcnt) != 1) { 86 struct sec_path *sp; 87 sp = secpath_dup(skb->sp); 88 if (!sp) 89 goto drop; 90 if (skb->sp) 91 secpath_put(skb->sp); 92 skb->sp = sp; 93 } 94 95 if (xfrm_nr + skb->sp->len > XFRM_MAX_DEPTH) 96 goto drop; 97 98 memcpy(skb->sp->xvec + skb->sp->len, xfrm_vec, 99 xfrm_nr * sizeof(xfrm_vec[0])); 100 skb->sp->len += xfrm_nr; 101 skb->ip_summed = CHECKSUM_NONE; 102 103 nf_reset(skb); 104 105 if (decaps) { 106 if (!(skb->dev->flags&IFF_LOOPBACK)) { 107 dst_release(skb->dst); 108 skb->dst = NULL; 109 } 110 netif_rx(skb); 111 return -1; 112 } else { 113 #ifdef CONFIG_NETFILTER 114 skb->nh.ipv6h->payload_len = htons(skb->len); 115 __skb_push(skb, skb->data - skb->nh.raw); 116 117 NF_HOOK(PF_INET6, NF_IP6_PRE_ROUTING, skb, skb->dev, NULL, 118 ip6_rcv_finish); 119 return -1; 120 #else 121 return 1; 122 #endif 123 } 124 125 drop_unlock: 126 spin_unlock(&x->lock); 127 xfrm_state_put(x); 128 drop: 129 while (--xfrm_nr >= 0) 130 xfrm_state_put(xfrm_vec[xfrm_nr]); 131 kfree_skb(skb); 132 return -1; 133 } 134 135 EXPORT_SYMBOL(xfrm6_rcv_spi); 136 137 int xfrm6_rcv(struct sk_buff **pskb) 138 { 139 return xfrm6_rcv_spi(*pskb, 0); 140 } 141