linux/net/ipv4/xfrm4_mode_tunnel.c
<<
>>
Prefs
   1/*
   2 * xfrm4_mode_tunnel.c - Tunnel mode encapsulation for IPv4.
   3 *
   4 * Copyright (c) 2004-2006 Herbert Xu <herbert@gondor.apana.org.au>
   5 */
   6
   7#include <linux/gfp.h>
   8#include <linux/init.h>
   9#include <linux/kernel.h>
  10#include <linux/module.h>
  11#include <linux/skbuff.h>
  12#include <linux/stringify.h>
  13#include <net/dst.h>
  14#include <net/inet_ecn.h>
  15#include <net/ip.h>
  16#include <net/xfrm.h>
  17
  18/* Informational hook. The decap is still done here. */
  19static struct xfrm_tunnel __rcu *rcv_notify_handlers __read_mostly;
  20static DEFINE_MUTEX(xfrm4_mode_tunnel_input_mutex);
  21
  22int xfrm4_mode_tunnel_input_register(struct xfrm_tunnel *handler)
  23{
  24        struct xfrm_tunnel __rcu **pprev;
  25        struct xfrm_tunnel *t;
  26        int ret = -EEXIST;
  27        int priority = handler->priority;
  28
  29        mutex_lock(&xfrm4_mode_tunnel_input_mutex);
  30
  31        for (pprev = &rcv_notify_handlers;
  32             (t = rcu_dereference_protected(*pprev,
  33             lockdep_is_held(&xfrm4_mode_tunnel_input_mutex))) != NULL;
  34             pprev = &t->next) {
  35                if (t->priority > priority)
  36                        break;
  37                if (t->priority == priority)
  38                        goto err;
  39
  40        }
  41
  42        handler->next = *pprev;
  43        rcu_assign_pointer(*pprev, handler);
  44
  45        ret = 0;
  46
  47err:
  48        mutex_unlock(&xfrm4_mode_tunnel_input_mutex);
  49        return ret;
  50}
  51EXPORT_SYMBOL_GPL(xfrm4_mode_tunnel_input_register);
  52
  53int xfrm4_mode_tunnel_input_deregister(struct xfrm_tunnel *handler)
  54{
  55        struct xfrm_tunnel __rcu **pprev;
  56        struct xfrm_tunnel *t;
  57        int ret = -ENOENT;
  58
  59        mutex_lock(&xfrm4_mode_tunnel_input_mutex);
  60        for (pprev = &rcv_notify_handlers;
  61             (t = rcu_dereference_protected(*pprev,
  62             lockdep_is_held(&xfrm4_mode_tunnel_input_mutex))) != NULL;
  63             pprev = &t->next) {
  64                if (t == handler) {
  65                        *pprev = handler->next;
  66                        ret = 0;
  67                        break;
  68                }
  69        }
  70        mutex_unlock(&xfrm4_mode_tunnel_input_mutex);
  71        synchronize_net();
  72
  73        return ret;
  74}
  75EXPORT_SYMBOL_GPL(xfrm4_mode_tunnel_input_deregister);
  76
  77static inline void ipip_ecn_decapsulate(struct sk_buff *skb)
  78{
  79        struct iphdr *inner_iph = ipip_hdr(skb);
  80
  81        if (INET_ECN_is_ce(XFRM_MODE_SKB_CB(skb)->tos))
  82                IP_ECN_set_ce(inner_iph);
  83}
  84
  85/* Add encapsulation header.
  86 *
  87 * The top IP header will be constructed per RFC 2401.
  88 */
  89static int xfrm4_mode_tunnel_output(struct xfrm_state *x, struct sk_buff *skb)
  90{
  91        struct dst_entry *dst = skb_dst(skb);
  92        struct iphdr *top_iph;
  93        int flags;
  94
  95        skb_set_network_header(skb, -x->props.header_len);
  96        skb->mac_header = skb->network_header +
  97                          offsetof(struct iphdr, protocol);
  98        skb->transport_header = skb->network_header + sizeof(*top_iph);
  99        top_iph = ip_hdr(skb);
 100
 101        top_iph->ihl = 5;
 102        top_iph->version = 4;
 103
 104        top_iph->protocol = xfrm_af2proto(skb_dst(skb)->ops->family);
 105
 106        /* DS disclosed */
 107        top_iph->tos = INET_ECN_encapsulate(XFRM_MODE_SKB_CB(skb)->tos,
 108                                            XFRM_MODE_SKB_CB(skb)->tos);
 109
 110        flags = x->props.flags;
 111        if (flags & XFRM_STATE_NOECN)
 112                IP_ECN_clear(top_iph);
 113
 114        top_iph->frag_off = (flags & XFRM_STATE_NOPMTUDISC) ?
 115                0 : (XFRM_MODE_SKB_CB(skb)->frag_off & htons(IP_DF));
 116        ip_select_ident(top_iph, dst->child, NULL);
 117
 118        top_iph->ttl = ip4_dst_hoplimit(dst->child);
 119
 120        top_iph->saddr = x->props.saddr.a4;
 121        top_iph->daddr = x->id.daddr.a4;
 122
 123        return 0;
 124}
 125
 126#define for_each_input_rcu(head, handler)       \
 127        for (handler = rcu_dereference(head);   \
 128             handler != NULL;                   \
 129             handler = rcu_dereference(handler->next))
 130
 131static int xfrm4_mode_tunnel_input(struct xfrm_state *x, struct sk_buff *skb)
 132{
 133        struct xfrm_tunnel *handler;
 134        int err = -EINVAL;
 135
 136        if (XFRM_MODE_SKB_CB(skb)->protocol != IPPROTO_IPIP)
 137                goto out;
 138
 139        if (!pskb_may_pull(skb, sizeof(struct iphdr)))
 140                goto out;
 141
 142        for_each_input_rcu(rcv_notify_handlers, handler)
 143                handler->handler(skb);
 144
 145        if (skb_cloned(skb) &&
 146            (err = pskb_expand_head(skb, 0, 0, GFP_ATOMIC)))
 147                goto out;
 148
 149        if (x->props.flags & XFRM_STATE_DECAP_DSCP)
 150                ipv4_copy_dscp(XFRM_MODE_SKB_CB(skb)->tos, ipip_hdr(skb));
 151        if (!(x->props.flags & XFRM_STATE_NOECN))
 152                ipip_ecn_decapsulate(skb);
 153
 154        skb_reset_network_header(skb);
 155        skb_mac_header_rebuild(skb);
 156
 157        err = 0;
 158
 159out:
 160        return err;
 161}
 162
 163static struct xfrm_mode xfrm4_tunnel_mode = {
 164        .input2 = xfrm4_mode_tunnel_input,
 165        .input = xfrm_prepare_input,
 166        .output2 = xfrm4_mode_tunnel_output,
 167        .output = xfrm4_prepare_output,
 168        .owner = THIS_MODULE,
 169        .encap = XFRM_MODE_TUNNEL,
 170        .flags = XFRM_MODE_FLAG_TUNNEL,
 171};
 172
 173static int __init xfrm4_mode_tunnel_init(void)
 174{
 175        return xfrm_register_mode(&xfrm4_tunnel_mode, AF_INET);
 176}
 177
 178static void __exit xfrm4_mode_tunnel_exit(void)
 179{
 180        int err;
 181
 182        err = xfrm_unregister_mode(&xfrm4_tunnel_mode, AF_INET);
 183        BUG_ON(err);
 184}
 185
 186module_init(xfrm4_mode_tunnel_init);
 187module_exit(xfrm4_mode_tunnel_exit);
 188MODULE_LICENSE("GPL");
 189MODULE_ALIAS_XFRM_MODE(AF_INET, XFRM_MODE_TUNNEL);
 190
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.