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
  18static inline void ipip_ecn_decapsulate(struct sk_buff *skb)
  19{
  20        struct iphdr *inner_iph = ipip_hdr(skb);
  21
  22        if (INET_ECN_is_ce(XFRM_MODE_SKB_CB(skb)->tos))
  23                IP_ECN_set_ce(inner_iph);
  24}
  25
  26/* Add encapsulation header.
  27 *
  28 * The top IP header will be constructed per RFC 2401.
  29 */
  30static int xfrm4_mode_tunnel_output(struct xfrm_state *x, struct sk_buff *skb)
  31{
  32        struct dst_entry *dst = skb_dst(skb);
  33        struct iphdr *top_iph;
  34        int flags;
  35
  36        skb_set_inner_network_header(skb, skb_network_offset(skb));
  37        skb_set_inner_transport_header(skb, skb_transport_offset(skb));
  38
  39        skb_set_network_header(skb, -x->props.header_len);
  40        skb->mac_header = skb->network_header +
  41                          offsetof(struct iphdr, protocol);
  42        skb->transport_header = skb->network_header + sizeof(*top_iph);
  43        top_iph = ip_hdr(skb);
  44
  45        top_iph->ihl = 5;
  46        top_iph->version = 4;
  47
  48        top_iph->protocol = xfrm_af2proto(skb_dst(skb)->ops->family);
  49
  50        /* DS disclosing depends on XFRM_SA_XFLAG_DONT_ENCAP_DSCP */
  51        if (x->props.extra_flags & XFRM_SA_XFLAG_DONT_ENCAP_DSCP)
  52                top_iph->tos = 0;
  53        else
  54                top_iph->tos = XFRM_MODE_SKB_CB(skb)->tos;
  55        top_iph->tos = INET_ECN_encapsulate(top_iph->tos,
  56                                            XFRM_MODE_SKB_CB(skb)->tos);
  57
  58        flags = x->props.flags;
  59        if (flags & XFRM_STATE_NOECN)
  60                IP_ECN_clear(top_iph);
  61
  62        top_iph->frag_off = (flags & XFRM_STATE_NOPMTUDISC) ?
  63                0 : (XFRM_MODE_SKB_CB(skb)->frag_off & htons(IP_DF));
  64
  65        top_iph->ttl = ip4_dst_hoplimit(xfrm_dst_child(dst));
  66
  67        top_iph->saddr = x->props.saddr.a4;
  68        top_iph->daddr = x->id.daddr.a4;
  69        ip_select_ident(dev_net(dst->dev), skb, NULL);
  70
  71        return 0;
  72}
  73
  74static int xfrm4_mode_tunnel_input(struct xfrm_state *x, struct sk_buff *skb)
  75{
  76        int err = -EINVAL;
  77
  78        if (XFRM_MODE_SKB_CB(skb)->protocol != IPPROTO_IPIP)
  79                goto out;
  80
  81        if (!pskb_may_pull(skb, sizeof(struct iphdr)))
  82                goto out;
  83
  84        err = skb_unclone(skb, GFP_ATOMIC);
  85        if (err)
  86                goto out;
  87
  88        if (x->props.flags & XFRM_STATE_DECAP_DSCP)
  89                ipv4_copy_dscp(XFRM_MODE_SKB_CB(skb)->tos, ipip_hdr(skb));
  90        if (!(x->props.flags & XFRM_STATE_NOECN))
  91                ipip_ecn_decapsulate(skb);
  92
  93        skb_reset_network_header(skb);
  94        skb_mac_header_rebuild(skb);
  95        if (skb->mac_len)
  96                eth_hdr(skb)->h_proto = skb->protocol;
  97
  98        err = 0;
  99
 100out:
 101        return err;
 102}
 103
 104static struct sk_buff *xfrm4_mode_tunnel_gso_segment(struct xfrm_state *x,
 105                                                     struct sk_buff *skb,
 106                                                     netdev_features_t features)
 107{
 108        __skb_push(skb, skb->mac_len);
 109        return skb_mac_gso_segment(skb, features);
 110}
 111
 112static void xfrm4_mode_tunnel_xmit(struct xfrm_state *x, struct sk_buff *skb)
 113{
 114        struct xfrm_offload *xo = xfrm_offload(skb);
 115
 116        if (xo->flags & XFRM_GSO_SEGMENT)
 117                skb->transport_header = skb->network_header +
 118                                        sizeof(struct iphdr);
 119
 120        skb_reset_mac_len(skb);
 121        pskb_pull(skb, skb->mac_len + x->props.header_len);
 122}
 123
 124static struct xfrm_mode xfrm4_tunnel_mode = {
 125        .input2 = xfrm4_mode_tunnel_input,
 126        .input = xfrm_prepare_input,
 127        .output2 = xfrm4_mode_tunnel_output,
 128        .output = xfrm4_prepare_output,
 129        .gso_segment = xfrm4_mode_tunnel_gso_segment,
 130        .xmit = xfrm4_mode_tunnel_xmit,
 131        .owner = THIS_MODULE,
 132        .encap = XFRM_MODE_TUNNEL,
 133        .flags = XFRM_MODE_FLAG_TUNNEL,
 134};
 135
 136static int __init xfrm4_mode_tunnel_init(void)
 137{
 138        return xfrm_register_mode(&xfrm4_tunnel_mode, AF_INET);
 139}
 140
 141static void __exit xfrm4_mode_tunnel_exit(void)
 142{
 143        int err;
 144
 145        err = xfrm_unregister_mode(&xfrm4_tunnel_mode, AF_INET);
 146        BUG_ON(err);
 147}
 148
 149module_init(xfrm4_mode_tunnel_init);
 150module_exit(xfrm4_mode_tunnel_exit);
 151MODULE_LICENSE("GPL");
 152MODULE_ALIAS_XFRM_MODE(AF_INET, XFRM_MODE_TUNNEL);
 153