linux/net/ipv6/xfrm6_mode_tunnel.c
<<
>>
Prefs
   1/*
   2 * xfrm6_mode_tunnel.c - Tunnel mode encapsulation for IPv6.
   3 *
   4 * Copyright (C) 2002 USAGI/WIDE Project
   5 * Copyright (c) 2004-2006 Herbert Xu <herbert@gondor.apana.org.au>
   6 */
   7
   8#include <linux/gfp.h>
   9#include <linux/init.h>
  10#include <linux/kernel.h>
  11#include <linux/module.h>
  12#include <linux/skbuff.h>
  13#include <linux/stringify.h>
  14#include <net/dsfield.h>
  15#include <net/dst.h>
  16#include <net/inet_ecn.h>
  17#include <net/ip6_route.h>
  18#include <net/ipv6.h>
  19#include <net/xfrm.h>
  20
  21static inline void ipip6_ecn_decapsulate(struct sk_buff *skb)
  22{
  23        const struct ipv6hdr *outer_iph = ipv6_hdr(skb);
  24        struct ipv6hdr *inner_iph = ipipv6_hdr(skb);
  25
  26        if (INET_ECN_is_ce(ipv6_get_dsfield(outer_iph)))
  27                IP6_ECN_set_ce(inner_iph);
  28}
  29
  30/* Add encapsulation header.
  31 *
  32 * The top IP header will be constructed per RFC 2401.
  33 */
  34static int xfrm6_mode_tunnel_output(struct xfrm_state *x, struct sk_buff *skb)
  35{
  36        struct dst_entry *dst = skb_dst(skb);
  37        struct ipv6hdr *top_iph;
  38        int dsfield;
  39
  40        skb_set_network_header(skb, -x->props.header_len);
  41        skb->mac_header = skb->network_header +
  42                          offsetof(struct ipv6hdr, nexthdr);
  43        skb->transport_header = skb->network_header + sizeof(*top_iph);
  44        top_iph = ipv6_hdr(skb);
  45
  46        top_iph->version = 6;
  47
  48        memcpy(top_iph->flow_lbl, XFRM_MODE_SKB_CB(skb)->flow_lbl,
  49               sizeof(top_iph->flow_lbl));
  50        top_iph->nexthdr = xfrm_af2proto(skb_dst(skb)->ops->family);
  51
  52        if (x->props.extra_flags & XFRM_SA_XFLAG_DONT_ENCAP_DSCP)
  53                dsfield = 0;
  54        else
  55                dsfield = XFRM_MODE_SKB_CB(skb)->tos;
  56        dsfield = INET_ECN_encapsulate(dsfield, XFRM_MODE_SKB_CB(skb)->tos);
  57        if (x->props.flags & XFRM_STATE_NOECN)
  58                dsfield &= ~INET_ECN_MASK;
  59        ipv6_change_dsfield(top_iph, 0, dsfield);
  60        top_iph->hop_limit = ip6_dst_hoplimit(dst->child);
  61        top_iph->saddr = *(struct in6_addr *)&x->props.saddr;
  62        top_iph->daddr = *(struct in6_addr *)&x->id.daddr;
  63        return 0;
  64}
  65
  66static int xfrm6_mode_tunnel_input(struct xfrm_state *x, struct sk_buff *skb)
  67{
  68        int err = -EINVAL;
  69
  70        if (XFRM_MODE_SKB_CB(skb)->protocol != IPPROTO_IPV6)
  71                goto out;
  72        if (!pskb_may_pull(skb, sizeof(struct ipv6hdr)))
  73                goto out;
  74
  75        err = skb_unclone(skb, GFP_ATOMIC);
  76        if (err)
  77                goto out;
  78
  79        if (x->props.flags & XFRM_STATE_DECAP_DSCP)
  80                ipv6_copy_dscp(ipv6_get_dsfield(ipv6_hdr(skb)),
  81                               ipipv6_hdr(skb));
  82        if (!(x->props.flags & XFRM_STATE_NOECN))
  83                ipip6_ecn_decapsulate(skb);
  84
  85        skb_reset_network_header(skb);
  86        skb_mac_header_rebuild(skb);
  87
  88        err = 0;
  89
  90out:
  91        return err;
  92}
  93
  94static struct xfrm_mode xfrm6_tunnel_mode = {
  95        .input2 = xfrm6_mode_tunnel_input,
  96        .input = xfrm_prepare_input,
  97        .output2 = xfrm6_mode_tunnel_output,
  98        .output = xfrm6_prepare_output,
  99        .owner = THIS_MODULE,
 100        .encap = XFRM_MODE_TUNNEL,
 101        .flags = XFRM_MODE_FLAG_TUNNEL,
 102};
 103
 104static int __init xfrm6_mode_tunnel_init(void)
 105{
 106        return xfrm_register_mode(&xfrm6_tunnel_mode, AF_INET6);
 107}
 108
 109static void __exit xfrm6_mode_tunnel_exit(void)
 110{
 111        int err;
 112
 113        err = xfrm_unregister_mode(&xfrm6_tunnel_mode, AF_INET6);
 114        BUG_ON(err);
 115}
 116
 117module_init(xfrm6_mode_tunnel_init);
 118module_exit(xfrm6_mode_tunnel_exit);
 119MODULE_LICENSE("GPL");
 120MODULE_ALIAS_XFRM_MODE(AF_INET6, XFRM_MODE_TUNNEL);
 121