xfrm4_mode_tunnel.c 3.81 KB
Newer Older
1 2 3 4 5 6
/*
 * xfrm4_mode_tunnel.c - Tunnel mode encapsulation for IPv4.
 *
 * Copyright (c) 2004-2006 Herbert Xu <herbert@gondor.apana.org.au>
 */

7
#include <linux/gfp.h>
8 9 10 11 12 13 14 15 16 17 18 19
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/skbuff.h>
#include <linux/stringify.h>
#include <net/dst.h>
#include <net/inet_ecn.h>
#include <net/ip.h>
#include <net/xfrm.h>

static inline void ipip_ecn_decapsulate(struct sk_buff *skb)
{
20
	struct iphdr *inner_iph = ipip_hdr(skb);
21

22
	if (INET_ECN_is_ce(XFRM_MODE_SKB_CB(skb)->tos))
23 24 25 26 27
		IP_ECN_set_ce(inner_iph);
}

/* Add encapsulation header.
 *
28
 * The top IP header will be constructed per RFC 2401.
29
 */
30
static int xfrm4_mode_tunnel_output(struct xfrm_state *x, struct sk_buff *skb)
31
{
Eric Dumazet's avatar
Eric Dumazet committed
32
	struct dst_entry *dst = skb_dst(skb);
33
	struct iphdr *top_iph;
34 35
	int flags;

36 37 38
	skb_set_inner_network_header(skb, skb_network_offset(skb));
	skb_set_inner_transport_header(skb, skb_transport_offset(skb));

39
	skb_set_network_header(skb, -x->props.header_len);
40 41
	skb->mac_header = skb->network_header +
			  offsetof(struct iphdr, protocol);
42
	skb->transport_header = skb->network_header + sizeof(*top_iph);
43
	top_iph = ip_hdr(skb);
44 45 46 47

	top_iph->ihl = 5;
	top_iph->version = 4;

Eric Dumazet's avatar
Eric Dumazet committed
48
	top_iph->protocol = xfrm_af2proto(skb_dst(skb)->ops->family);
49

50 51 52 53 54 55
	/* DS disclosing depends on XFRM_SA_XFLAG_DONT_ENCAP_DSCP */
	if (x->props.extra_flags & XFRM_SA_XFLAG_DONT_ENCAP_DSCP)
		top_iph->tos = 0;
	else
		top_iph->tos = XFRM_MODE_SKB_CB(skb)->tos;
	top_iph->tos = INET_ECN_encapsulate(top_iph->tos,
56
					    XFRM_MODE_SKB_CB(skb)->tos);
57

58
	flags = x->props.flags;
59 60 61
	if (flags & XFRM_STATE_NOECN)
		IP_ECN_clear(top_iph);

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(dst->child);
66 67 68

	top_iph->saddr = x->props.saddr.a4;
	top_iph->daddr = x->id.daddr.a4;
69
	ip_select_ident(dev_net(dst->dev), skb, NULL);
70 71 72 73

	return 0;
}

74
static int xfrm4_mode_tunnel_input(struct xfrm_state *x, struct sk_buff *skb)
75 76 77
{
	int err = -EINVAL;

78 79
	if (XFRM_MODE_SKB_CB(skb)->protocol != IPPROTO_IPIP)
		goto out;
80

81 82 83
	if (!pskb_may_pull(skb, sizeof(struct iphdr)))
		goto out;

84 85
	err = skb_unclone(skb, GFP_ATOMIC);
	if (err)
86 87
		goto out;

88 89 90 91 92
	if (x->props.flags & XFRM_STATE_DECAP_DSCP)
		ipv4_copy_dscp(XFRM_MODE_SKB_CB(skb)->tos, ipip_hdr(skb));
	if (!(x->props.flags & XFRM_STATE_NOECN))
		ipip_ecn_decapsulate(skb);

93
	skb_reset_network_header(skb);
94
	skb_mac_header_rebuild(skb);
95
	eth_hdr(skb)->h_proto = skb->protocol;
96

97 98 99 100 101 102
	err = 0;

out:
	return err;
}

103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125
static struct sk_buff *xfrm4_mode_tunnel_gso_segment(struct xfrm_state *x,
						     struct sk_buff *skb,
						     netdev_features_t features)
{
	__skb_push(skb, skb->mac_len);
	return skb_mac_gso_segment(skb, features);

}

static void xfrm4_mode_tunnel_xmit(struct xfrm_state *x, struct sk_buff *skb)
{
	struct xfrm_offload *xo = xfrm_offload(skb);

	if (xo->flags & XFRM_GSO_SEGMENT) {
		skb->network_header = skb->network_header - x->props.header_len;
		skb->transport_header = skb->network_header +
					sizeof(struct iphdr);
	}

	skb_reset_mac_len(skb);
	pskb_pull(skb, skb->mac_len + x->props.header_len);
}

126
static struct xfrm_mode xfrm4_tunnel_mode = {
127
	.input2 = xfrm4_mode_tunnel_input,
128
	.input = xfrm_prepare_input,
129
	.output2 = xfrm4_mode_tunnel_output,
130
	.output = xfrm4_prepare_output,
131 132
	.gso_segment = xfrm4_mode_tunnel_gso_segment,
	.xmit = xfrm4_mode_tunnel_xmit,
133 134
	.owner = THIS_MODULE,
	.encap = XFRM_MODE_TUNNEL,
Herbert Xu's avatar
Herbert Xu committed
135
	.flags = XFRM_MODE_FLAG_TUNNEL,
136 137
};

138
static int __init xfrm4_mode_tunnel_init(void)
139 140 141 142
{
	return xfrm_register_mode(&xfrm4_tunnel_mode, AF_INET);
}

143
static void __exit xfrm4_mode_tunnel_exit(void)
144 145 146 147 148 149 150
{
	int err;

	err = xfrm_unregister_mode(&xfrm4_tunnel_mode, AF_INET);
	BUG_ON(err);
}

151 152
module_init(xfrm4_mode_tunnel_init);
module_exit(xfrm4_mode_tunnel_exit);
153 154
MODULE_LICENSE("GPL");
MODULE_ALIAS_XFRM_MODE(AF_INET, XFRM_MODE_TUNNEL);