linux/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/compiler.h>
  12#include <linux/inetdevice.h>
  13#include <net/xfrm.h>
  14#include <net/ip.h>
  15
  16static struct dst_ops xfrm4_dst_ops;
  17static struct xfrm_policy_afinfo xfrm4_policy_afinfo;
  18
  19static int xfrm4_dst_lookup(struct xfrm_dst **dst, struct flowi *fl)
  20{
  21        return __ip_route_output_key((struct rtable**)dst, fl);
  22}
  23
  24static int xfrm4_get_saddr(xfrm_address_t *saddr, xfrm_address_t *daddr)
  25{
  26        struct rtable *rt;
  27        struct flowi fl_tunnel = {
  28                .nl_u = {
  29                        .ip4_u = {
  30                                .daddr = daddr->a4,
  31                        },
  32                },
  33        };
  34
  35        if (!xfrm4_dst_lookup((struct xfrm_dst **)&rt, &fl_tunnel)) {
  36                saddr->a4 = rt->rt_src;
  37                dst_release(&rt->u.dst);
  38                return 0;
  39        }
  40        return -EHOSTUNREACH;
  41}
  42
  43static struct dst_entry *
  44__xfrm4_find_bundle(struct flowi *fl, struct xfrm_policy *policy)
  45{
  46        struct dst_entry *dst;
  47
  48        read_lock_bh(&policy->lock);
  49        for (dst = policy->bundles; dst; dst = dst->next) {
  50                struct xfrm_dst *xdst = (struct xfrm_dst*)dst;
  51                if (xdst->u.rt.fl.oif == fl->oif &&     /*XXX*/
  52                    xdst->u.rt.fl.fl4_dst == fl->fl4_dst &&
  53                    xdst->u.rt.fl.fl4_src == fl->fl4_src &&
  54                    xdst->u.rt.fl.fl4_tos == fl->fl4_tos &&
  55                    xfrm_bundle_ok(policy, xdst, fl, AF_INET, 0)) {
  56                        dst_clone(dst);
  57                        break;
  58                }
  59        }
  60        read_unlock_bh(&policy->lock);
  61        return dst;
  62}
  63
  64/* Allocate chain of dst_entry's, attach known xfrm's, calculate
  65 * all the metrics... Shortly, bundle a bundle.
  66 */
  67
  68static int
  69__xfrm4_bundle_create(struct xfrm_policy *policy, struct xfrm_state **xfrm, int nx,
  70                      struct flowi *fl, struct dst_entry **dst_p)
  71{
  72        struct dst_entry *dst, *dst_prev;
  73        struct rtable *rt0 = (struct rtable*)(*dst_p);
  74        struct rtable *rt = rt0;
  75        struct flowi fl_tunnel = {
  76                .nl_u = {
  77                        .ip4_u = {
  78                                .saddr = fl->fl4_src,
  79                                .daddr = fl->fl4_dst,
  80                                .tos = fl->fl4_tos
  81                        }
  82                }
  83        };
  84        int i;
  85        int err;
  86        int header_len = 0;
  87        int trailer_len = 0;
  88
  89        dst = dst_prev = NULL;
  90        dst_hold(&rt->u.dst);
  91
  92        for (i = 0; i < nx; i++) {
  93                struct dst_entry *dst1 = dst_alloc(&xfrm4_dst_ops);
  94                struct xfrm_dst *xdst;
  95
  96                if (unlikely(dst1 == NULL)) {
  97                        err = -ENOBUFS;
  98                        dst_release(&rt->u.dst);
  99                        goto error;
 100                }
 101
 102                if (!dst)
 103                        dst = dst1;
 104                else {
 105                        dst_prev->child = dst1;
 106                        dst1->flags |= DST_NOHASH;
 107                        dst_clone(dst1);
 108                }
 109
 110                xdst = (struct xfrm_dst *)dst1;
 111                xdst->route = &rt->u.dst;
 112                xdst->genid = xfrm[i]->genid;
 113
 114                dst1->next = dst_prev;
 115                dst_prev = dst1;
 116
 117                header_len += xfrm[i]->props.header_len;
 118                trailer_len += xfrm[i]->props.trailer_len;
 119
 120                if (xfrm[i]->props.mode != XFRM_MODE_TRANSPORT) {
 121                        unsigned short encap_family = xfrm[i]->props.family;
 122                        switch (encap_family) {
 123                        case AF_INET:
 124                                fl_tunnel.fl4_dst = xfrm[i]->id.daddr.a4;
 125                                fl_tunnel.fl4_src = xfrm[i]->props.saddr.a4;
 126                                break;
 127#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
 128                        case AF_INET6:
 129                                ipv6_addr_copy(&fl_tunnel.fl6_dst, (struct in6_addr*)&xfrm[i]->id.daddr.a6);
 130                                ipv6_addr_copy(&fl_tunnel.fl6_src, (struct in6_addr*)&xfrm[i]->props.saddr.a6);
 131                                break;
 132#endif
 133                        default:
 134                                BUG_ON(1);
 135                        }
 136                        err = xfrm_dst_lookup((struct xfrm_dst **)&rt,
 137                                              &fl_tunnel, encap_family);
 138                        if (err)
 139                                goto error;
 140                } else
 141                        dst_hold(&rt->u.dst);
 142        }
 143
 144        dst_prev->child = &rt->u.dst;
 145        dst->path = &rt->u.dst;
 146
 147        *dst_p = dst;
 148        dst = dst_prev;
 149
 150        dst_prev = *dst_p;
 151        i = 0;
 152        for (; dst_prev != &rt->u.dst; dst_prev = dst_prev->child) {
 153                struct xfrm_dst *x = (struct xfrm_dst*)dst_prev;
 154                x->u.rt.fl = *fl;
 155
 156                dst_prev->xfrm = xfrm[i++];
 157                dst_prev->dev = rt->u.dst.dev;
 158                if (rt->u.dst.dev)
 159                        dev_hold(rt->u.dst.dev);
 160                dst_prev->obsolete      = -1;
 161                dst_prev->flags        |= DST_HOST;
 162                dst_prev->lastuse       = jiffies;
 163                dst_prev->header_len    = header_len;
 164                dst_prev->nfheader_len  = 0;
 165                dst_prev->trailer_len   = trailer_len;
 166                memcpy(&dst_prev->metrics, &x->route->metrics, sizeof(dst_prev->metrics));
 167
 168                /* Copy neighbout for reachability confirmation */
 169                dst_prev->neighbour     = neigh_clone(rt->u.dst.neighbour);
 170                dst_prev->input         = rt->u.dst.input;
 171                dst_prev->output = dst_prev->xfrm->outer_mode->afinfo->output;
 172                if (rt0->peer)
 173                        atomic_inc(&rt0->peer->refcnt);
 174                x->u.rt.peer = rt0->peer;
 175                /* Sheit... I remember I did this right. Apparently,
 176                 * it was magically lost, so this code needs audit */
 177                x->u.rt.rt_flags = rt0->rt_flags&(RTCF_BROADCAST|RTCF_MULTICAST|RTCF_LOCAL);
 178                x->u.rt.rt_type = rt0->rt_type;
 179                x->u.rt.rt_src = rt0->rt_src;
 180                x->u.rt.rt_dst = rt0->rt_dst;
 181                x->u.rt.rt_gateway = rt0->rt_gateway;
 182                x->u.rt.rt_spec_dst = rt0->rt_spec_dst;
 183                x->u.rt.idev = rt0->idev;
 184                in_dev_hold(rt0->idev);
 185                header_len -= x->u.dst.xfrm->props.header_len;
 186                trailer_len -= x->u.dst.xfrm->props.trailer_len;
 187        }
 188
 189        xfrm_init_pmtu(dst);
 190        return 0;
 191
 192error:
 193        if (dst)
 194                dst_free(dst);
 195        return err;
 196}
 197
 198static void
 199_decode_session4(struct sk_buff *skb, struct flowi *fl)
 200{
 201        struct iphdr *iph = ip_hdr(skb);
 202        u8 *xprth = skb_network_header(skb) + iph->ihl * 4;
 203
 204        memset(fl, 0, sizeof(struct flowi));
 205        if (!(iph->frag_off & htons(IP_MF | IP_OFFSET))) {
 206                switch (iph->protocol) {
 207                case IPPROTO_UDP:
 208                case IPPROTO_UDPLITE:
 209                case IPPROTO_TCP:
 210                case IPPROTO_SCTP:
 211                case IPPROTO_DCCP:
 212                        if (pskb_may_pull(skb, xprth + 4 - skb->data)) {
 213                                __be16 *ports = (__be16 *)xprth;
 214
 215                                fl->fl_ip_sport = ports[0];
 216                                fl->fl_ip_dport = ports[1];
 217                        }
 218                        break;
 219
 220                case IPPROTO_ICMP:
 221                        if (pskb_may_pull(skb, xprth + 2 - skb->data)) {
 222                                u8 *icmp = xprth;
 223
 224                                fl->fl_icmp_type = icmp[0];
 225                                fl->fl_icmp_code = icmp[1];
 226                        }
 227                        break;
 228
 229                case IPPROTO_ESP:
 230                        if (pskb_may_pull(skb, xprth + 4 - skb->data)) {
 231                                __be32 *ehdr = (__be32 *)xprth;
 232
 233                                fl->fl_ipsec_spi = ehdr[0];
 234                        }
 235                        break;
 236
 237                case IPPROTO_AH:
 238                        if (pskb_may_pull(skb, xprth + 8 - skb->data)) {
 239                                __be32 *ah_hdr = (__be32*)xprth;
 240
 241                                fl->fl_ipsec_spi = ah_hdr[1];
 242                        }
 243                        break;
 244
 245                case IPPROTO_COMP:
 246                        if (pskb_may_pull(skb, xprth + 4 - skb->data)) {
 247                                __be16 *ipcomp_hdr = (__be16 *)xprth;
 248
 249                                fl->fl_ipsec_spi = htonl(ntohs(ipcomp_hdr[1]));
 250                        }
 251                        break;
 252                default:
 253                        fl->fl_ipsec_spi = 0;
 254                        break;
 255                }
 256        }
 257        fl->proto = iph->protocol;
 258        fl->fl4_dst = iph->daddr;
 259        fl->fl4_src = iph->saddr;
 260        fl->fl4_tos = iph->tos;
 261}
 262
 263static inline int xfrm4_garbage_collect(void)
 264{
 265        xfrm4_policy_afinfo.garbage_collect();
 266        return (atomic_read(&xfrm4_dst_ops.entries) > xfrm4_dst_ops.gc_thresh*2);
 267}
 268
 269static void xfrm4_update_pmtu(struct dst_entry *dst, u32 mtu)
 270{
 271        struct xfrm_dst *xdst = (struct xfrm_dst *)dst;
 272        struct dst_entry *path = xdst->route;
 273
 274        path->ops->update_pmtu(path, mtu);
 275}
 276
 277static void xfrm4_dst_destroy(struct dst_entry *dst)
 278{
 279        struct xfrm_dst *xdst = (struct xfrm_dst *)dst;
 280
 281        if (likely(xdst->u.rt.idev))
 282                in_dev_put(xdst->u.rt.idev);
 283        if (likely(xdst->u.rt.peer))
 284                inet_putpeer(xdst->u.rt.peer);
 285        xfrm_dst_destroy(xdst);
 286}
 287
 288static void xfrm4_dst_ifdown(struct dst_entry *dst, struct net_device *dev,
 289                             int unregister)
 290{
 291        struct xfrm_dst *xdst;
 292
 293        if (!unregister)
 294                return;
 295
 296        xdst = (struct xfrm_dst *)dst;
 297        if (xdst->u.rt.idev->dev == dev) {
 298                struct in_device *loopback_idev = in_dev_get(init_net.loopback_dev);
 299                BUG_ON(!loopback_idev);
 300
 301                do {
 302                        in_dev_put(xdst->u.rt.idev);
 303                        xdst->u.rt.idev = loopback_idev;
 304                        in_dev_hold(loopback_idev);
 305                        xdst = (struct xfrm_dst *)xdst->u.dst.child;
 306                } while (xdst->u.dst.xfrm);
 307
 308                __in_dev_put(loopback_idev);
 309        }
 310
 311        xfrm_dst_ifdown(dst, dev);
 312}
 313
 314static struct dst_ops xfrm4_dst_ops = {
 315        .family =               AF_INET,
 316        .protocol =             __constant_htons(ETH_P_IP),
 317        .gc =                   xfrm4_garbage_collect,
 318        .update_pmtu =          xfrm4_update_pmtu,
 319        .destroy =              xfrm4_dst_destroy,
 320        .ifdown =               xfrm4_dst_ifdown,
 321        .gc_thresh =            1024,
 322        .entry_size =           sizeof(struct xfrm_dst),
 323};
 324
 325static struct xfrm_policy_afinfo xfrm4_policy_afinfo = {
 326        .family =               AF_INET,
 327        .dst_ops =              &xfrm4_dst_ops,
 328        .dst_lookup =           xfrm4_dst_lookup,
 329        .get_saddr =            xfrm4_get_saddr,
 330        .find_bundle =          __xfrm4_find_bundle,
 331        .bundle_create =        __xfrm4_bundle_create,
 332        .decode_session =       _decode_session4,
 333};
 334
 335static void __init xfrm4_policy_init(void)
 336{
 337        xfrm_policy_register_afinfo(&xfrm4_policy_afinfo);
 338}
 339
 340static void __exit xfrm4_policy_fini(void)
 341{
 342        xfrm_policy_unregister_afinfo(&xfrm4_policy_afinfo);
 343}
 344
 345void __init xfrm4_init(void)
 346{
 347        xfrm4_state_init();
 348        xfrm4_policy_init();
 349}
 350
 351