linux-bk/net/ipv4/xfrm4_policy.c
<<
>>
Prefs
   1/* 
   2 * xfrm4_policy.c
   3 *
   4 * Changes:
   5 *      Kazunori MIYAZAWA @USAGI
   6 *      YOSHIFUJI Hideaki @USAGI
   7 *              Split up af-specific portion
   8 *      
   9 */
  10
  11#include <linux/config.h>
  12#include <net/xfrm.h>
  13#include <net/ip.h>
  14
  15extern struct dst_ops xfrm4_dst_ops;
  16extern struct xfrm_policy_afinfo xfrm4_policy_afinfo;
  17
  18static struct xfrm_type_map xfrm4_type_map = { .lock = RW_LOCK_UNLOCKED };
  19
  20static int xfrm4_dst_lookup(struct xfrm_dst **dst, struct flowi *fl)
  21{
  22        return __ip_route_output_key((struct rtable**)dst, fl);
  23}
  24
  25/* Check that the bundle accepts the flow and its components are
  26 * still valid.
  27 */
  28
  29static int __xfrm4_bundle_ok(struct xfrm_dst *xdst, struct flowi *fl)
  30{
  31        do {
  32                if (xdst->u.dst.ops != &xfrm4_dst_ops)
  33                        return 1;
  34
  35                if (!xfrm_selector_match(&xdst->u.dst.xfrm->sel, fl, AF_INET))
  36                        return 0;
  37                if (xdst->u.dst.xfrm->km.state != XFRM_STATE_VALID ||
  38                    xdst->u.dst.path->obsolete > 0)
  39                        return 0;
  40                xdst = (struct xfrm_dst*)xdst->u.dst.child;
  41        } while (xdst);
  42        return 0;
  43}
  44
  45static struct dst_entry *
  46__xfrm4_find_bundle(struct flowi *fl, struct rtable *rt, struct xfrm_policy *policy)
  47{
  48        struct dst_entry *dst;
  49
  50        if (!fl->fl4_src)
  51                fl->fl4_src = rt->rt_src;
  52        if (!fl->fl4_dst)
  53                fl->fl4_dst = rt->rt_dst;
  54        read_lock_bh(&policy->lock);
  55        for (dst = policy->bundles; dst; dst = dst->next) {
  56                struct xfrm_dst *xdst = (struct xfrm_dst*)dst;
  57                if (xdst->u.rt.fl.oif == fl->oif &&     /*XXX*/
  58                    xdst->u.rt.fl.fl4_dst == fl->fl4_dst &&
  59                    xdst->u.rt.fl.fl4_src == fl->fl4_src &&
  60                    __xfrm4_bundle_ok(xdst, fl)) {
  61                        dst_clone(dst);
  62                        break;
  63                }
  64        }
  65        read_unlock_bh(&policy->lock);
  66        return dst;
  67}
  68
  69/* Allocate chain of dst_entry's, attach known xfrm's, calculate
  70 * all the metrics... Shortly, bundle a bundle.
  71 */
  72
  73static int
  74__xfrm4_bundle_create(struct xfrm_policy *policy, struct xfrm_state **xfrm, int nx,
  75                      struct flowi *fl, struct dst_entry **dst_p)
  76{
  77        struct dst_entry *dst, *dst_prev;
  78        struct rtable *rt0 = (struct rtable*)(*dst_p);
  79        struct rtable *rt = rt0;
  80        u32 remote = fl->fl4_dst;
  81        u32 local  = fl->fl4_src;
  82        int i;
  83        int err;
  84        int header_len = 0;
  85        int trailer_len = 0;
  86
  87        dst = dst_prev = NULL;
  88
  89        for (i = 0; i < nx; i++) {
  90                struct dst_entry *dst1 = dst_alloc(&xfrm4_dst_ops);
  91
  92                if (unlikely(dst1 == NULL)) {
  93                        err = -ENOBUFS;
  94                        goto error;
  95                }
  96
  97                dst1->xfrm = xfrm[i];
  98                if (!dst)
  99                        dst = dst1;
 100                else {
 101                        dst_prev->child = dst1;
 102                        dst1->flags |= DST_NOHASH;
 103                        dst_clone(dst1);
 104                }
 105                dst_prev = dst1;
 106                if (xfrm[i]->props.mode) {
 107                        remote = xfrm[i]->id.daddr.a4;
 108                        local  = xfrm[i]->props.saddr.a4;
 109                }
 110                header_len += xfrm[i]->props.header_len;
 111                trailer_len += xfrm[i]->props.trailer_len;
 112        }
 113
 114        if (remote != fl->fl4_dst) {
 115                struct flowi fl_tunnel = { .nl_u = { .ip4_u =
 116                                                     { .daddr = remote,
 117                                                       .saddr = local }
 118                                                   }
 119                                         };
 120                err = xfrm_dst_lookup((struct xfrm_dst**)&rt, &fl_tunnel, AF_INET);
 121                if (err)
 122                        goto error;
 123        } else {
 124                dst_hold(&rt->u.dst);
 125        }
 126        dst_prev->child = &rt->u.dst;
 127        for (dst_prev = dst; dst_prev != &rt->u.dst; dst_prev = dst_prev->child) {
 128                struct xfrm_dst *x = (struct xfrm_dst*)dst_prev;
 129                x->u.rt.fl = *fl;
 130
 131                dst_prev->dev = rt->u.dst.dev;
 132                if (rt->u.dst.dev)
 133                        dev_hold(rt->u.dst.dev);
 134                dst_prev->obsolete      = -1;
 135                dst_prev->flags        |= DST_HOST;
 136                dst_prev->lastuse       = jiffies;
 137                dst_prev->header_len    = header_len;
 138                dst_prev->trailer_len   = trailer_len;
 139                memcpy(&dst_prev->metrics, &rt->u.dst.metrics, sizeof(dst_prev->metrics));
 140                dst_prev->path          = &rt->u.dst;
 141
 142                /* Copy neighbout for reachability confirmation */
 143                dst_prev->neighbour     = neigh_clone(rt->u.dst.neighbour);
 144                dst_prev->input         = rt->u.dst.input;
 145                dst_prev->output        = dst_prev->xfrm->type->output;
 146                if (rt->peer)
 147                        atomic_inc(&rt->peer->refcnt);
 148                x->u.rt.peer = rt->peer;
 149                /* Sheit... I remember I did this right. Apparently,
 150                 * it was magically lost, so this code needs audit */
 151                x->u.rt.rt_flags = rt0->rt_flags&(RTCF_BROADCAST|RTCF_MULTICAST|RTCF_LOCAL);
 152                x->u.rt.rt_type = rt->rt_type;
 153                x->u.rt.rt_src = rt0->rt_src;
 154                x->u.rt.rt_dst = rt0->rt_dst;
 155                x->u.rt.rt_gateway = rt->rt_gateway;
 156                x->u.rt.rt_spec_dst = rt0->rt_spec_dst;
 157                header_len -= x->u.dst.xfrm->props.header_len;
 158                trailer_len -= x->u.dst.xfrm->props.trailer_len;
 159        }
 160        *dst_p = dst;
 161        return 0;
 162
 163error:
 164        if (dst)
 165                dst_free(dst);
 166        return err;
 167}
 168
 169static void
 170_decode_session4(struct sk_buff *skb, struct flowi *fl)
 171{
 172        struct iphdr *iph = skb->nh.iph;
 173        u8 *xprth = skb->nh.raw + iph->ihl*4;
 174
 175        memset(fl, 0, sizeof(struct flowi));
 176        if (!(iph->frag_off & htons(IP_MF | IP_OFFSET))) {
 177                switch (iph->protocol) {
 178                case IPPROTO_UDP:
 179                case IPPROTO_TCP:
 180                case IPPROTO_SCTP:
 181                        if (pskb_may_pull(skb, xprth + 4 - skb->data)) {
 182                                u16 *ports = (u16 *)xprth;
 183
 184                                fl->fl_ip_sport = ports[0];
 185                                fl->fl_ip_dport = ports[1];
 186                        }
 187                        break;
 188
 189                case IPPROTO_ESP:
 190                        if (pskb_may_pull(skb, xprth + 4 - skb->data)) {
 191                                u32 *ehdr = (u32 *)xprth;
 192
 193                                fl->fl_ipsec_spi = ehdr[0];
 194                        }
 195                        break;
 196
 197                case IPPROTO_AH:
 198                        if (pskb_may_pull(skb, xprth + 8 - skb->data)) {
 199                                u32 *ah_hdr = (u32*)xprth;
 200
 201                                fl->fl_ipsec_spi = ah_hdr[1];
 202                        }
 203                        break;
 204
 205                case IPPROTO_COMP:
 206                        if (pskb_may_pull(skb, xprth + 4 - skb->data)) {
 207                                u16 *ipcomp_hdr = (u16 *)xprth;
 208
 209                                fl->fl_ipsec_spi = ntohl(ntohs(ipcomp_hdr[1]));
 210                        }
 211                        break;
 212                default:
 213                        fl->fl_ipsec_spi = 0;
 214                        break;
 215                };
 216        }
 217        fl->proto = iph->protocol;
 218        fl->fl4_dst = iph->daddr;
 219        fl->fl4_src = iph->saddr;
 220}
 221
 222static inline int xfrm4_garbage_collect(void)
 223{
 224        read_lock(&xfrm4_policy_afinfo.lock);
 225        xfrm4_policy_afinfo.garbage_collect();
 226        read_unlock(&xfrm4_policy_afinfo.lock);
 227        return (atomic_read(&xfrm4_dst_ops.entries) > xfrm4_dst_ops.gc_thresh*2);
 228}
 229
 230static void xfrm4_update_pmtu(struct dst_entry *dst, u32 mtu)
 231{
 232        struct dst_entry *path = dst->path;
 233
 234        if (mtu < 68 + dst->header_len)
 235                return;
 236
 237        path->ops->update_pmtu(path, mtu);
 238}
 239
 240struct dst_ops xfrm4_dst_ops = {
 241        .family =               AF_INET,
 242        .protocol =             __constant_htons(ETH_P_IP),
 243        .gc =                   xfrm4_garbage_collect,
 244        .update_pmtu =          xfrm4_update_pmtu,
 245        .gc_thresh =            1024,
 246        .entry_size =           sizeof(struct xfrm_dst),
 247};
 248
 249struct xfrm_policy_afinfo xfrm4_policy_afinfo = {
 250        .family =               AF_INET,
 251        .lock =                 RW_LOCK_UNLOCKED,
 252        .type_map =             &xfrm4_type_map,
 253        .dst_ops =              &xfrm4_dst_ops,
 254        .dst_lookup =           xfrm4_dst_lookup,
 255        .find_bundle =          __xfrm4_find_bundle,
 256        .bundle_create =        __xfrm4_bundle_create,
 257        .decode_session =       _decode_session4,
 258};
 259
 260void __init xfrm4_policy_init(void)
 261{
 262        xfrm_policy_register_afinfo(&xfrm4_policy_afinfo);
 263}
 264
 265void __exit xfrm4_policy_fini(void)
 266{
 267        xfrm_policy_unregister_afinfo(&xfrm4_policy_afinfo);
 268}
 269
 270void __init xfrm4_init(void)
 271{
 272        xfrm4_state_init();
 273        xfrm4_policy_init();
 274}
 275
 276void __exit xfrm4_fini(void)
 277{
 278        //xfrm4_input_fini();
 279        xfrm4_policy_fini();
 280        xfrm4_state_fini();
 281}
 282
 283
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.