linux/net/ipv6/xfrm6_input.c
<<
>>
Prefs
   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
  19int xfrm6_rcv_spi(struct sk_buff *skb, int nexthdr, __be32 spi)
  20{
  21        int err;
  22        __be32 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        unsigned int nhoff;
  28
  29        nhoff = IP6CB(skb)->nhoff;
  30
  31        seq = 0;
  32        if (!spi && (err = xfrm_parse_spi(skb, nexthdr, &spi, &seq)) != 0)
  33                goto drop;
  34
  35        do {
  36                struct ipv6hdr *iph = ipv6_hdr(skb);
  37
  38                if (xfrm_nr == XFRM_MAX_DEPTH)
  39                        goto drop;
  40
  41                x = xfrm_state_lookup((xfrm_address_t *)&iph->daddr, spi,
  42                                      nexthdr, AF_INET6);
  43                if (x == NULL)
  44                        goto drop;
  45                spin_lock(&x->lock);
  46                if (unlikely(x->km.state != XFRM_STATE_VALID))
  47                        goto drop_unlock;
  48
  49                if (x->props.replay_window && xfrm_replay_check(x, seq))
  50                        goto drop_unlock;
  51
  52                if (xfrm_state_check_expire(x))
  53                        goto drop_unlock;
  54
  55                nexthdr = x->type->input(x, skb);
  56                if (nexthdr <= 0)
  57                        goto drop_unlock;
  58
  59                skb_network_header(skb)[nhoff] = nexthdr;
  60
  61                if (x->props.replay_window)
  62                        xfrm_replay_advance(x, seq);
  63
  64                x->curlft.bytes += skb->len;
  65                x->curlft.packets++;
  66
  67                spin_unlock(&x->lock);
  68
  69                xfrm_vec[xfrm_nr++] = x;
  70
  71                if (x->outer_mode->input(x, skb))
  72                        goto drop;
  73
  74                if (x->outer_mode->flags & XFRM_MODE_FLAG_TUNNEL) {
  75                        decaps = 1;
  76                        break;
  77                }
  78
  79                if ((err = xfrm_parse_spi(skb, nexthdr, &spi, &seq)) < 0)
  80                        goto drop;
  81        } while (!err);
  82
  83        /* Allocate new secpath or COW existing one. */
  84        if (!skb->sp || atomic_read(&skb->sp->refcnt) != 1) {
  85                struct sec_path *sp;
  86                sp = secpath_dup(skb->sp);
  87                if (!sp)
  88                        goto drop;
  89                if (skb->sp)
  90                        secpath_put(skb->sp);
  91                skb->sp = sp;
  92        }
  93
  94        if (xfrm_nr + skb->sp->len > XFRM_MAX_DEPTH)
  95                goto drop;
  96
  97        memcpy(skb->sp->xvec + skb->sp->len, xfrm_vec,
  98               xfrm_nr * sizeof(xfrm_vec[0]));
  99        skb->sp->len += xfrm_nr;
 100
 101        nf_reset(skb);
 102
 103        if (decaps) {
 104                dst_release(skb->dst);
 105                skb->dst = NULL;
 106                netif_rx(skb);
 107                return -1;
 108        } else {
 109#ifdef CONFIG_NETFILTER
 110                ipv6_hdr(skb)->payload_len = htons(skb->len);
 111                __skb_push(skb, skb->data - skb_network_header(skb));
 112
 113                NF_HOOK(PF_INET6, NF_IP6_PRE_ROUTING, skb, skb->dev, NULL,
 114                        ip6_rcv_finish);
 115                return -1;
 116#else
 117                return 1;
 118#endif
 119        }
 120
 121drop_unlock:
 122        spin_unlock(&x->lock);
 123        xfrm_state_put(x);
 124drop:
 125        while (--xfrm_nr >= 0)
 126                xfrm_state_put(xfrm_vec[xfrm_nr]);
 127        kfree_skb(skb);
 128        return -1;
 129}
 130
 131EXPORT_SYMBOL(xfrm6_rcv_spi);
 132
 133int xfrm6_rcv(struct sk_buff *skb)
 134{
 135        return xfrm6_rcv_spi(skb, skb_network_header(skb)[IP6CB(skb)->nhoff],
 136                             0);
 137}
 138
 139EXPORT_SYMBOL(xfrm6_rcv);
 140
 141int xfrm6_input_addr(struct sk_buff *skb, xfrm_address_t *daddr,
 142                     xfrm_address_t *saddr, u8 proto)
 143{
 144        struct xfrm_state *x = NULL;
 145        int wildcard = 0;
 146        xfrm_address_t *xany;
 147        struct xfrm_state *xfrm_vec_one = NULL;
 148        int nh = 0;
 149        int i = 0;
 150
 151        xany = (xfrm_address_t *)&in6addr_any;
 152
 153        for (i = 0; i < 3; i++) {
 154                xfrm_address_t *dst, *src;
 155                switch (i) {
 156                case 0:
 157                        dst = daddr;
 158                        src = saddr;
 159                        break;
 160                case 1:
 161                        /* lookup state with wild-card source address */
 162                        wildcard = 1;
 163                        dst = daddr;
 164                        src = xany;
 165                        break;
 166                case 2:
 167                default:
 168                        /* lookup state with wild-card addresses */
 169                        wildcard = 1; /* XXX */
 170                        dst = xany;
 171                        src = xany;
 172                        break;
 173                }
 174
 175                x = xfrm_state_lookup_byaddr(dst, src, proto, AF_INET6);
 176                if (!x)
 177                        continue;
 178
 179                spin_lock(&x->lock);
 180
 181                if (wildcard) {
 182                        if ((x->props.flags & XFRM_STATE_WILDRECV) == 0) {
 183                                spin_unlock(&x->lock);
 184                                xfrm_state_put(x);
 185                                x = NULL;
 186                                continue;
 187                        }
 188                }
 189
 190                if (unlikely(x->km.state != XFRM_STATE_VALID)) {
 191                        spin_unlock(&x->lock);
 192                        xfrm_state_put(x);
 193                        x = NULL;
 194                        continue;
 195                }
 196                if (xfrm_state_check_expire(x)) {
 197                        spin_unlock(&x->lock);
 198                        xfrm_state_put(x);
 199                        x = NULL;
 200                        continue;
 201                }
 202
 203                nh = x->type->input(x, skb);
 204                if (nh <= 0) {
 205                        spin_unlock(&x->lock);
 206                        xfrm_state_put(x);
 207                        x = NULL;
 208                        continue;
 209                }
 210
 211                x->curlft.bytes += skb->len;
 212                x->curlft.packets++;
 213
 214                spin_unlock(&x->lock);
 215
 216                xfrm_vec_one = x;
 217                break;
 218        }
 219
 220        if (!xfrm_vec_one)
 221                goto drop;
 222
 223        /* Allocate new secpath or COW existing one. */
 224        if (!skb->sp || atomic_read(&skb->sp->refcnt) != 1) {
 225                struct sec_path *sp;
 226                sp = secpath_dup(skb->sp);
 227                if (!sp)
 228                        goto drop;
 229                if (skb->sp)
 230                        secpath_put(skb->sp);
 231                skb->sp = sp;
 232        }
 233
 234        if (1 + skb->sp->len > XFRM_MAX_DEPTH)
 235                goto drop;
 236
 237        skb->sp->xvec[skb->sp->len] = xfrm_vec_one;
 238        skb->sp->len ++;
 239
 240        return 1;
 241drop:
 242        if (xfrm_vec_one)
 243                xfrm_state_put(xfrm_vec_one);
 244        return -1;
 245}
 246
 247EXPORT_SYMBOL(xfrm6_input_addr);
 248