aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorHerbert Xu <herbert@gondor.apana.org.au>2006-05-28 02:05:54 -0400
committerDavid S. Miller <davem@sunset.davemloft.net>2006-06-18 00:28:39 -0400
commitb59f45d0b2878ab76f8053b0973654e6621828ee (patch)
tree40dc5e2ede2620f7935fb3dae0d0eb199851f611 /net
parent546be2405be119ef55467aace45f337a16e5d424 (diff)
[IPSEC] xfrm: Abstract out encapsulation modes
This patch adds the structure xfrm_mode. It is meant to represent the operations carried out by transport/tunnel modes. By doing this we allow additional encapsulation modes to be added without clogging up the xfrm_input/xfrm_output paths. Candidate modes include 4-to-6 tunnel mode, 6-to-4 tunnel mode, and BEET modes. Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r--net/ipv4/Kconfig18
-rw-r--r--net/ipv4/Makefile2
-rw-r--r--net/ipv4/xfrm4_input.c28
-rw-r--r--net/ipv4/xfrm4_mode_transport.c69
-rw-r--r--net/ipv4/xfrm4_mode_tunnel.c125
-rw-r--r--net/ipv4/xfrm4_output.c61
-rw-r--r--net/ipv6/Kconfig20
-rw-r--r--net/ipv6/Makefile2
-rw-r--r--net/ipv6/ip6_output.c2
-rw-r--r--net/ipv6/xfrm6_input.c29
-rw-r--r--net/ipv6/xfrm6_mode_transport.c73
-rw-r--r--net/ipv6/xfrm6_mode_tunnel.c121
-rw-r--r--net/ipv6/xfrm6_output.c63
-rw-r--r--net/xfrm/xfrm_policy.c83
-rw-r--r--net/xfrm/xfrm_state.c6
15 files changed, 532 insertions, 170 deletions
diff --git a/net/ipv4/Kconfig b/net/ipv4/Kconfig
index e40f75322377..35eb70b1448d 100644
--- a/net/ipv4/Kconfig
+++ b/net/ipv4/Kconfig
@@ -414,6 +414,24 @@ config INET_TUNNEL
414 tristate 414 tristate
415 default n 415 default n
416 416
417config INET_XFRM_MODE_TRANSPORT
418 tristate "IP: IPsec transport mode"
419 default y
420 select XFRM
421 ---help---
422 Support for IPsec transport mode.
423
424 If unsure, say Y.
425
426config INET_XFRM_MODE_TUNNEL
427 tristate "IP: IPsec tunnel mode"
428 default y
429 select XFRM
430 ---help---
431 Support for IPsec tunnel mode.
432
433 If unsure, say Y.
434
417config INET_DIAG 435config INET_DIAG
418 tristate "INET: socket monitoring interface" 436 tristate "INET: socket monitoring interface"
419 default y 437 default y
diff --git a/net/ipv4/Makefile b/net/ipv4/Makefile
index 9ef50a0b9d2c..4cc94482eacc 100644
--- a/net/ipv4/Makefile
+++ b/net/ipv4/Makefile
@@ -24,6 +24,8 @@ obj-$(CONFIG_INET_ESP) += esp4.o
24obj-$(CONFIG_INET_IPCOMP) += ipcomp.o 24obj-$(CONFIG_INET_IPCOMP) += ipcomp.o
25obj-$(CONFIG_INET_XFRM_TUNNEL) += xfrm4_tunnel.o 25obj-$(CONFIG_INET_XFRM_TUNNEL) += xfrm4_tunnel.o
26obj-$(CONFIG_INET_TUNNEL) += tunnel4.o 26obj-$(CONFIG_INET_TUNNEL) += tunnel4.o
27obj-$(CONFIG_INET_XFRM_MODE_TRANSPORT) += xfrm4_mode_transport.o
28obj-$(CONFIG_INET_XFRM_MODE_TUNNEL) += xfrm4_mode_tunnel.o
27obj-$(CONFIG_IP_PNP) += ipconfig.o 29obj-$(CONFIG_IP_PNP) += ipconfig.o
28obj-$(CONFIG_IP_ROUTE_MULTIPATH_RR) += multipath_rr.o 30obj-$(CONFIG_IP_ROUTE_MULTIPATH_RR) += multipath_rr.o
29obj-$(CONFIG_IP_ROUTE_MULTIPATH_RANDOM) += multipath_random.o 31obj-$(CONFIG_IP_ROUTE_MULTIPATH_RANDOM) += multipath_random.o
diff --git a/net/ipv4/xfrm4_input.c b/net/ipv4/xfrm4_input.c
index 3e174c83bfe7..817ed84511a6 100644
--- a/net/ipv4/xfrm4_input.c
+++ b/net/ipv4/xfrm4_input.c
@@ -13,7 +13,6 @@
13#include <linux/string.h> 13#include <linux/string.h>
14#include <linux/netfilter.h> 14#include <linux/netfilter.h>
15#include <linux/netfilter_ipv4.h> 15#include <linux/netfilter_ipv4.h>
16#include <net/inet_ecn.h>
17#include <net/ip.h> 16#include <net/ip.h>
18#include <net/xfrm.h> 17#include <net/xfrm.h>
19 18
@@ -24,15 +23,6 @@ int xfrm4_rcv(struct sk_buff *skb)
24 23
25EXPORT_SYMBOL(xfrm4_rcv); 24EXPORT_SYMBOL(xfrm4_rcv);
26 25
27static inline void ipip_ecn_decapsulate(struct sk_buff *skb)
28{
29 struct iphdr *outer_iph = skb->nh.iph;
30 struct iphdr *inner_iph = skb->h.ipiph;
31
32 if (INET_ECN_is_ce(outer_iph->tos))
33 IP_ECN_set_ce(inner_iph);
34}
35
36static int xfrm4_parse_spi(struct sk_buff *skb, u8 nexthdr, u32 *spi, u32 *seq) 26static int xfrm4_parse_spi(struct sk_buff *skb, u8 nexthdr, u32 *spi, u32 *seq)
37{ 27{
38 switch (nexthdr) { 28 switch (nexthdr) {
@@ -113,24 +103,10 @@ int xfrm4_rcv_encap(struct sk_buff *skb, __u16 encap_type)
113 103
114 xfrm_vec[xfrm_nr++] = x; 104 xfrm_vec[xfrm_nr++] = x;
115 105
116 iph = skb->nh.iph; 106 if (x->mode->input(x, skb))
107 goto drop;
117 108
118 if (x->props.mode) { 109 if (x->props.mode) {
119 if (iph->protocol != IPPROTO_IPIP)
120 goto drop;
121 if (!pskb_may_pull(skb, sizeof(struct iphdr)))
122 goto drop;
123 if (skb_cloned(skb) &&
124 pskb_expand_head(skb, 0, 0, GFP_ATOMIC))
125 goto drop;
126 if (x->props.flags & XFRM_STATE_DECAP_DSCP)
127 ipv4_copy_dscp(iph, skb->h.ipiph);
128 if (!(x->props.flags & XFRM_STATE_NOECN))
129 ipip_ecn_decapsulate(skb);
130 skb->mac.raw = memmove(skb->data - skb->mac_len,
131 skb->mac.raw, skb->mac_len);
132 skb->nh.raw = skb->data;
133 memset(&(IPCB(skb)->opt), 0, sizeof(struct ip_options));
134 decaps = 1; 110 decaps = 1;
135 break; 111 break;
136 } 112 }
diff --git a/net/ipv4/xfrm4_mode_transport.c b/net/ipv4/xfrm4_mode_transport.c
new file mode 100644
index 000000000000..e46d9a4ccc55
--- /dev/null
+++ b/net/ipv4/xfrm4_mode_transport.c
@@ -0,0 +1,69 @@
1/*
2 * xfrm4_mode_transport.c - Transport mode encapsulation for IPv4.
3 *
4 * Copyright (c) 2004-2006 Herbert Xu <herbert@gondor.apana.org.au>
5 */
6
7#include <linux/init.h>
8#include <linux/kernel.h>
9#include <linux/module.h>
10#include <linux/skbuff.h>
11#include <linux/stringify.h>
12#include <net/dst.h>
13#include <net/ip.h>
14#include <net/xfrm.h>
15
16/* Add encapsulation header.
17 *
18 * The IP header will be moved forward to make space for the encapsulation
19 * header.
20 *
21 * On exit, skb->h will be set to the start of the payload to be processed
22 * by x->type->output and skb->nh will be set to the top IP header.
23 */
24static int xfrm4_transport_output(struct sk_buff *skb)
25{
26 struct xfrm_state *x;
27 struct iphdr *iph;
28 int ihl;
29
30 iph = skb->nh.iph;
31 skb->h.ipiph = iph;
32
33 ihl = iph->ihl * 4;
34 skb->h.raw += ihl;
35
36 x = skb->dst->xfrm;
37 skb->nh.raw = memmove(skb_push(skb, x->props.header_len), iph, ihl);
38 return 0;
39}
40
41static int xfrm4_transport_input(struct xfrm_state *x, struct sk_buff *skb)
42{
43 return 0;
44}
45
46static struct xfrm_mode xfrm4_transport_mode = {
47 .input = xfrm4_transport_input,
48 .output = xfrm4_transport_output,
49 .owner = THIS_MODULE,
50 .encap = XFRM_MODE_TRANSPORT,
51};
52
53static int __init xfrm4_transport_init(void)
54{
55 return xfrm_register_mode(&xfrm4_transport_mode, AF_INET);
56}
57
58static void __exit xfrm4_transport_exit(void)
59{
60 int err;
61
62 err = xfrm_unregister_mode(&xfrm4_transport_mode, AF_INET);
63 BUG_ON(err);
64}
65
66module_init(xfrm4_transport_init);
67module_exit(xfrm4_transport_exit);
68MODULE_LICENSE("GPL");
69MODULE_ALIAS_XFRM_MODE(AF_INET, XFRM_MODE_TRANSPORT);
diff --git a/net/ipv4/xfrm4_mode_tunnel.c b/net/ipv4/xfrm4_mode_tunnel.c
new file mode 100644
index 000000000000..f8d880beb12f
--- /dev/null
+++ b/net/ipv4/xfrm4_mode_tunnel.c
@@ -0,0 +1,125 @@
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/init.h>
8#include <linux/kernel.h>
9#include <linux/module.h>
10#include <linux/skbuff.h>
11#include <linux/stringify.h>
12#include <net/dst.h>
13#include <net/inet_ecn.h>
14#include <net/ip.h>
15#include <net/xfrm.h>
16
17static inline void ipip_ecn_decapsulate(struct sk_buff *skb)
18{
19 struct iphdr *outer_iph = skb->nh.iph;
20 struct iphdr *inner_iph = skb->h.ipiph;
21
22 if (INET_ECN_is_ce(outer_iph->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. The following fields
29 * in it shall be filled in by x->type->output:
30 * tot_len
31 * check
32 *
33 * On exit, skb->h will be set to the start of the payload to be processed
34 * by x->type->output and skb->nh will be set to the top IP header.
35 */
36static int xfrm4_tunnel_output(struct sk_buff *skb)
37{
38 struct dst_entry *dst = skb->dst;
39 struct xfrm_state *x = dst->xfrm;
40 struct iphdr *iph, *top_iph;
41 int flags;
42
43 iph = skb->nh.iph;
44 skb->h.ipiph = iph;
45
46 skb->nh.raw = skb_push(skb, x->props.header_len);
47 top_iph = skb->nh.iph;
48
49 top_iph->ihl = 5;
50 top_iph->version = 4;
51
52 /* DS disclosed */
53 top_iph->tos = INET_ECN_encapsulate(iph->tos, iph->tos);
54
55 flags = x->props.flags;
56 if (flags & XFRM_STATE_NOECN)
57 IP_ECN_clear(top_iph);
58
59 top_iph->frag_off = (flags & XFRM_STATE_NOPMTUDISC) ?
60 0 : (iph->frag_off & htons(IP_DF));
61 if (!top_iph->frag_off)
62 __ip_select_ident(top_iph, dst->child, 0);
63
64 top_iph->ttl = dst_metric(dst->child, RTAX_HOPLIMIT);
65
66 top_iph->saddr = x->props.saddr.a4;
67 top_iph->daddr = x->id.daddr.a4;
68 top_iph->protocol = IPPROTO_IPIP;
69
70 memset(&(IPCB(skb)->opt), 0, sizeof(struct ip_options));
71 return 0;
72}
73
74static int xfrm4_tunnel_input(struct xfrm_state *x, struct sk_buff *skb)
75{
76 struct iphdr *iph = skb->nh.iph;
77 int err = -EINVAL;
78
79 if (iph->protocol != IPPROTO_IPIP)
80 goto out;
81 if (!pskb_may_pull(skb, sizeof(struct iphdr)))
82 goto out;
83
84 if (skb_cloned(skb) &&
85 (err = pskb_expand_head(skb, 0, 0, GFP_ATOMIC)))
86 goto out;
87
88 if (x->props.flags & XFRM_STATE_DECAP_DSCP)
89 ipv4_copy_dscp(iph, skb->h.ipiph);
90 if (!(x->props.flags & XFRM_STATE_NOECN))
91 ipip_ecn_decapsulate(skb);
92 skb->mac.raw = memmove(skb->data - skb->mac_len,
93 skb->mac.raw, skb->mac_len);
94 skb->nh.raw = skb->data;
95 memset(&(IPCB(skb)->opt), 0, sizeof(struct ip_options));
96 err = 0;
97
98out:
99 return err;
100}
101
102static struct xfrm_mode xfrm4_tunnel_mode = {
103 .input = xfrm4_tunnel_input,
104 .output = xfrm4_tunnel_output,
105 .owner = THIS_MODULE,
106 .encap = XFRM_MODE_TUNNEL,
107};
108
109static int __init xfrm4_tunnel_init(void)
110{
111 return xfrm_register_mode(&xfrm4_tunnel_mode, AF_INET);
112}
113
114static void __exit xfrm4_tunnel_exit(void)
115{
116 int err;
117
118 err = xfrm_unregister_mode(&xfrm4_tunnel_mode, AF_INET);
119 BUG_ON(err);
120}
121
122module_init(xfrm4_tunnel_init);
123module_exit(xfrm4_tunnel_exit);
124MODULE_LICENSE("GPL");
125MODULE_ALIAS_XFRM_MODE(AF_INET, XFRM_MODE_TUNNEL);
diff --git a/net/ipv4/xfrm4_output.c b/net/ipv4/xfrm4_output.c
index 4ef8efaf6a67..ac9d91d4bb05 100644
--- a/net/ipv4/xfrm4_output.c
+++ b/net/ipv4/xfrm4_output.c
@@ -12,67 +12,10 @@
12#include <linux/skbuff.h> 12#include <linux/skbuff.h>
13#include <linux/spinlock.h> 13#include <linux/spinlock.h>
14#include <linux/netfilter_ipv4.h> 14#include <linux/netfilter_ipv4.h>
15#include <net/inet_ecn.h>
16#include <net/ip.h> 15#include <net/ip.h>
17#include <net/xfrm.h> 16#include <net/xfrm.h>
18#include <net/icmp.h> 17#include <net/icmp.h>
19 18
20/* Add encapsulation header.
21 *
22 * In transport mode, the IP header will be moved forward to make space
23 * for the encapsulation header.
24 *
25 * In tunnel mode, the top IP header will be constructed per RFC 2401.
26 * The following fields in it shall be filled in by x->type->output:
27 * tot_len
28 * check
29 *
30 * On exit, skb->h will be set to the start of the payload to be processed
31 * by x->type->output and skb->nh will be set to the top IP header.
32 */
33static void xfrm4_encap(struct sk_buff *skb)
34{
35 struct dst_entry *dst = skb->dst;
36 struct xfrm_state *x = dst->xfrm;
37 struct iphdr *iph, *top_iph;
38 int flags;
39
40 iph = skb->nh.iph;
41 skb->h.ipiph = iph;
42
43 skb->nh.raw = skb_push(skb, x->props.header_len);
44 top_iph = skb->nh.iph;
45
46 if (!x->props.mode) {
47 skb->h.raw += iph->ihl*4;
48 memmove(top_iph, iph, iph->ihl*4);
49 return;
50 }
51
52 top_iph->ihl = 5;
53 top_iph->version = 4;
54
55 /* DS disclosed */
56 top_iph->tos = INET_ECN_encapsulate(iph->tos, iph->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 : (iph->frag_off & htons(IP_DF));
64 if (!top_iph->frag_off)
65 __ip_select_ident(top_iph, dst->child, 0);
66
67 top_iph->ttl = dst_metric(dst->child, RTAX_HOPLIMIT);
68
69 top_iph->saddr = x->props.saddr.a4;
70 top_iph->daddr = x->id.daddr.a4;
71 top_iph->protocol = IPPROTO_IPIP;
72
73 memset(&(IPCB(skb)->opt), 0, sizeof(struct ip_options));
74}
75
76static int xfrm4_tunnel_check_size(struct sk_buff *skb) 19static int xfrm4_tunnel_check_size(struct sk_buff *skb)
77{ 20{
78 int mtu, ret = 0; 21 int mtu, ret = 0;
@@ -121,7 +64,9 @@ static int xfrm4_output_one(struct sk_buff *skb)
121 if (err) 64 if (err)
122 goto error; 65 goto error;
123 66
124 xfrm4_encap(skb); 67 err = x->mode->output(skb);
68 if (err)
69 goto error;
125 70
126 err = x->type->output(x, skb); 71 err = x->type->output(x, skb);
127 if (err) 72 if (err)
diff --git a/net/ipv6/Kconfig b/net/ipv6/Kconfig
index f8a107ab5592..e923d4dea418 100644
--- a/net/ipv6/Kconfig
+++ b/net/ipv6/Kconfig
@@ -106,6 +106,26 @@ config INET6_TUNNEL
106 tristate 106 tristate
107 default n 107 default n
108 108
109config INET6_XFRM_MODE_TRANSPORT
110 tristate "IPv6: IPsec transport mode"
111 depends on IPV6
112 default IPV6
113 select XFRM
114 ---help---
115 Support for IPsec transport mode.
116
117 If unsure, say Y.
118
119config INET6_XFRM_MODE_TUNNEL
120 tristate "IPv6: IPsec tunnel mode"
121 depends on IPV6
122 default IPV6
123 select XFRM
124 ---help---
125 Support for IPsec tunnel mode.
126
127 If unsure, say Y.
128
109config IPV6_TUNNEL 129config IPV6_TUNNEL
110 tristate "IPv6: IPv6-in-IPv6 tunnel" 130 tristate "IPv6: IPv6-in-IPv6 tunnel"
111 select INET6_TUNNEL 131 select INET6_TUNNEL
diff --git a/net/ipv6/Makefile b/net/ipv6/Makefile
index a760b0988fbb..386e0a626948 100644
--- a/net/ipv6/Makefile
+++ b/net/ipv6/Makefile
@@ -20,6 +20,8 @@ obj-$(CONFIG_INET6_ESP) += esp6.o
20obj-$(CONFIG_INET6_IPCOMP) += ipcomp6.o 20obj-$(CONFIG_INET6_IPCOMP) += ipcomp6.o
21obj-$(CONFIG_INET6_XFRM_TUNNEL) += xfrm6_tunnel.o 21obj-$(CONFIG_INET6_XFRM_TUNNEL) += xfrm6_tunnel.o
22obj-$(CONFIG_INET6_TUNNEL) += tunnel6.o 22obj-$(CONFIG_INET6_TUNNEL) += tunnel6.o
23obj-$(CONFIG_INET6_XFRM_MODE_TRANSPORT) += xfrm6_mode_transport.o
24obj-$(CONFIG_INET6_XFRM_MODE_TUNNEL) += xfrm6_mode_tunnel.o
23obj-$(CONFIG_NETFILTER) += netfilter/ 25obj-$(CONFIG_NETFILTER) += netfilter/
24 26
25obj-$(CONFIG_IPV6_TUNNEL) += ip6_tunnel.o 27obj-$(CONFIG_IPV6_TUNNEL) += ip6_tunnel.o
diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
index e46048974f37..416f6e428a0a 100644
--- a/net/ipv6/ip6_output.c
+++ b/net/ipv6/ip6_output.c
@@ -39,6 +39,7 @@
39#include <linux/in6.h> 39#include <linux/in6.h>
40#include <linux/tcp.h> 40#include <linux/tcp.h>
41#include <linux/route.h> 41#include <linux/route.h>
42#include <linux/module.h>
42 43
43#include <linux/netfilter.h> 44#include <linux/netfilter.h>
44#include <linux/netfilter_ipv6.h> 45#include <linux/netfilter_ipv6.h>
@@ -488,6 +489,7 @@ int ip6_find_1stfragopt(struct sk_buff *skb, u8 **nexthdr)
488 489
489 return offset; 490 return offset;
490} 491}
492EXPORT_SYMBOL_GPL(ip6_find_1stfragopt);
491 493
492static int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *)) 494static int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *))
493{ 495{
diff --git a/net/ipv6/xfrm6_input.c b/net/ipv6/xfrm6_input.c
index 00cfdee18dca..0405d74ff910 100644
--- a/net/ipv6/xfrm6_input.c
+++ b/net/ipv6/xfrm6_input.c
@@ -13,21 +13,9 @@
13#include <linux/string.h> 13#include <linux/string.h>
14#include <linux/netfilter.h> 14#include <linux/netfilter.h>
15#include <linux/netfilter_ipv6.h> 15#include <linux/netfilter_ipv6.h>
16#include <net/dsfield.h>
17#include <net/inet_ecn.h>
18#include <net/ip.h>
19#include <net/ipv6.h> 16#include <net/ipv6.h>
20#include <net/xfrm.h> 17#include <net/xfrm.h>
21 18
22static inline void ipip6_ecn_decapsulate(struct sk_buff *skb)
23{
24 struct ipv6hdr *outer_iph = skb->nh.ipv6h;
25 struct ipv6hdr *inner_iph = skb->h.ipv6h;
26
27 if (INET_ECN_is_ce(ipv6_get_dsfield(outer_iph)))
28 IP6_ECN_set_ce(inner_iph);
29}
30
31int xfrm6_rcv_spi(struct sk_buff *skb, u32 spi) 19int xfrm6_rcv_spi(struct sk_buff *skb, u32 spi)
32{ 20{
33 int err; 21 int err;
@@ -81,21 +69,10 @@ int xfrm6_rcv_spi(struct sk_buff *skb, u32 spi)
81 69
82 xfrm_vec[xfrm_nr++] = x; 70 xfrm_vec[xfrm_nr++] = x;
83 71
72 if (x->mode->input(x, skb))
73 goto drop;
74
84 if (x->props.mode) { /* XXX */ 75 if (x->props.mode) { /* XXX */
85 if (nexthdr != IPPROTO_IPV6)
86 goto drop;
87 if (!pskb_may_pull(skb, sizeof(struct ipv6hdr)))
88 goto drop;
89 if (skb_cloned(skb) &&
90 pskb_expand_head(skb, 0, 0, GFP_ATOMIC))
91 goto drop;
92 if (x->props.flags & XFRM_STATE_DECAP_DSCP)
93 ipv6_copy_dscp(skb->nh.ipv6h, skb->h.ipv6h);
94 if (!(x->props.flags & XFRM_STATE_NOECN))
95 ipip6_ecn_decapsulate(skb);
96 skb->mac.raw = memmove(skb->data - skb->mac_len,
97 skb->mac.raw, skb->mac_len);
98 skb->nh.raw = skb->data;
99 decaps = 1; 76 decaps = 1;
100 break; 77 break;
101 } 78 }
diff --git a/net/ipv6/xfrm6_mode_transport.c b/net/ipv6/xfrm6_mode_transport.c
new file mode 100644
index 000000000000..5efbbae08ef0
--- /dev/null
+++ b/net/ipv6/xfrm6_mode_transport.c
@@ -0,0 +1,73 @@
1/*
2 * xfrm6_mode_transport.c - Transport 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/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/ipv6.h>
15#include <net/xfrm.h>
16
17/* Add encapsulation header.
18 *
19 * The IP header and mutable extension headers will be moved forward to make
20 * space for the encapsulation header.
21 *
22 * On exit, skb->h will be set to the start of the encapsulation header to be
23 * filled in by x->type->output and skb->nh will be set to the nextheader field
24 * of the extension header directly preceding the encapsulation header, or in
25 * its absence, that of the top IP header. The value of skb->data will always
26 * point to the top IP header.
27 */
28static int xfrm6_transport_output(struct sk_buff *skb)
29{
30 struct xfrm_state *x = skb->dst->xfrm;
31 struct ipv6hdr *iph;
32 u8 *prevhdr;
33 int hdr_len;
34
35 skb_push(skb, x->props.header_len);
36 iph = skb->nh.ipv6h;
37
38 hdr_len = ip6_find_1stfragopt(skb, &prevhdr);
39 skb->nh.raw = prevhdr - x->props.header_len;
40 skb->h.raw = skb->data + hdr_len;
41 memmove(skb->data, iph, hdr_len);
42 return 0;
43}
44
45static int xfrm6_transport_input(struct xfrm_state *x, struct sk_buff *skb)
46{
47 return 0;
48}
49
50static struct xfrm_mode xfrm6_transport_mode = {
51 .input = xfrm6_transport_input,
52 .output = xfrm6_transport_output,
53 .owner = THIS_MODULE,
54 .encap = XFRM_MODE_TRANSPORT,
55};
56
57static int __init xfrm6_transport_init(void)
58{
59 return xfrm_register_mode(&xfrm6_transport_mode, AF_INET6);
60}
61
62static void __exit xfrm6_transport_exit(void)
63{
64 int err;
65
66 err = xfrm_unregister_mode(&xfrm6_transport_mode, AF_INET6);
67 BUG_ON(err);
68}
69
70module_init(xfrm6_transport_init);
71module_exit(xfrm6_transport_exit);
72MODULE_LICENSE("GPL");
73MODULE_ALIAS_XFRM_MODE(AF_INET6, XFRM_MODE_TRANSPORT);
diff --git a/net/ipv6/xfrm6_mode_tunnel.c b/net/ipv6/xfrm6_mode_tunnel.c
new file mode 100644
index 000000000000..8af79be2edca
--- /dev/null
+++ b/net/ipv6/xfrm6_mode_tunnel.c
@@ -0,0 +1,121 @@
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/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/dsfield.h>
14#include <net/dst.h>
15#include <net/inet_ecn.h>
16#include <net/ipv6.h>
17#include <net/xfrm.h>
18
19static inline void ipip6_ecn_decapsulate(struct sk_buff *skb)
20{
21 struct ipv6hdr *outer_iph = skb->nh.ipv6h;
22 struct ipv6hdr *inner_iph = skb->h.ipv6h;
23
24 if (INET_ECN_is_ce(ipv6_get_dsfield(outer_iph)))
25 IP6_ECN_set_ce(inner_iph);
26}
27
28/* Add encapsulation header.
29 *
30 * The top IP header will be constructed per RFC 2401. The following fields
31 * in it shall be filled in by x->type->output:
32 * payload_len
33 *
34 * On exit, skb->h will be set to the start of the encapsulation header to be
35 * filled in by x->type->output and skb->nh will be set to the nextheader field
36 * of the extension header directly preceding the encapsulation header, or in
37 * its absence, that of the top IP header. The value of skb->data will always
38 * point to the top IP header.
39 */
40static int xfrm6_tunnel_output(struct sk_buff *skb)
41{
42 struct dst_entry *dst = skb->dst;
43 struct xfrm_state *x = dst->xfrm;
44 struct ipv6hdr *iph, *top_iph;
45 int dsfield;
46
47 skb_push(skb, x->props.header_len);
48 iph = skb->nh.ipv6h;
49
50 skb->nh.raw = skb->data;
51 top_iph = skb->nh.ipv6h;
52 skb->nh.raw = &top_iph->nexthdr;
53 skb->h.ipv6h = top_iph + 1;
54
55 top_iph->version = 6;
56 top_iph->priority = iph->priority;
57 top_iph->flow_lbl[0] = iph->flow_lbl[0];
58 top_iph->flow_lbl[1] = iph->flow_lbl[1];
59 top_iph->flow_lbl[2] = iph->flow_lbl[2];
60 dsfield = ipv6_get_dsfield(top_iph);
61 dsfield = INET_ECN_encapsulate(dsfield, dsfield);
62 if (x->props.flags & XFRM_STATE_NOECN)
63 dsfield &= ~INET_ECN_MASK;
64 ipv6_change_dsfield(top_iph, 0, dsfield);
65 top_iph->nexthdr = IPPROTO_IPV6;
66 top_iph->hop_limit = dst_metric(dst->child, RTAX_HOPLIMIT);
67 ipv6_addr_copy(&top_iph->saddr, (struct in6_addr *)&x->props.saddr);
68 ipv6_addr_copy(&top_iph->daddr, (struct in6_addr *)&x->id.daddr);
69 return 0;
70}
71
72static int xfrm6_tunnel_input(struct xfrm_state *x, struct sk_buff *skb)
73{
74 int err = -EINVAL;
75
76 if (skb->nh.raw[IP6CB(skb)->nhoff] != IPPROTO_IPV6)
77 goto out;
78 if (!pskb_may_pull(skb, sizeof(struct ipv6hdr)))
79 goto out;
80
81 if (skb_cloned(skb) &&
82 (err = pskb_expand_head(skb, 0, 0, GFP_ATOMIC)))
83 goto out;
84
85 if (x->props.flags & XFRM_STATE_DECAP_DSCP)
86 ipv6_copy_dscp(skb->nh.ipv6h, skb->h.ipv6h);
87 if (!(x->props.flags & XFRM_STATE_NOECN))
88 ipip6_ecn_decapsulate(skb);
89 skb->mac.raw = memmove(skb->data - skb->mac_len,
90 skb->mac.raw, skb->mac_len);
91 skb->nh.raw = skb->data;
92 err = 0;
93
94out:
95 return err;
96}
97
98static struct xfrm_mode xfrm6_tunnel_mode = {
99 .input = xfrm6_tunnel_input,
100 .output = xfrm6_tunnel_output,
101 .owner = THIS_MODULE,
102 .encap = XFRM_MODE_TUNNEL,
103};
104
105static int __init xfrm6_tunnel_init(void)
106{
107 return xfrm_register_mode(&xfrm6_tunnel_mode, AF_INET6);
108}
109
110static void __exit xfrm6_tunnel_exit(void)
111{
112 int err;
113
114 err = xfrm_unregister_mode(&xfrm6_tunnel_mode, AF_INET6);
115 BUG_ON(err);
116}
117
118module_init(xfrm6_tunnel_init);
119module_exit(xfrm6_tunnel_exit);
120MODULE_LICENSE("GPL");
121MODULE_ALIAS_XFRM_MODE(AF_INET6, XFRM_MODE_TUNNEL);
diff --git a/net/ipv6/xfrm6_output.c b/net/ipv6/xfrm6_output.c
index 80242172a5df..16e84254a252 100644
--- a/net/ipv6/xfrm6_output.c
+++ b/net/ipv6/xfrm6_output.c
@@ -14,68 +14,9 @@
14#include <linux/spinlock.h> 14#include <linux/spinlock.h>
15#include <linux/icmpv6.h> 15#include <linux/icmpv6.h>
16#include <linux/netfilter_ipv6.h> 16#include <linux/netfilter_ipv6.h>
17#include <net/dsfield.h>
18#include <net/inet_ecn.h>
19#include <net/ipv6.h> 17#include <net/ipv6.h>
20#include <net/xfrm.h> 18#include <net/xfrm.h>
21 19
22/* Add encapsulation header.
23 *
24 * In transport mode, the IP header and mutable extension headers will be moved
25 * forward to make space for the encapsulation header.
26 *
27 * In tunnel mode, the top IP header will be constructed per RFC 2401.
28 * The following fields in it shall be filled in by x->type->output:
29 * payload_len
30 *
31 * On exit, skb->h will be set to the start of the encapsulation header to be
32 * filled in by x->type->output and skb->nh will be set to the nextheader field
33 * of the extension header directly preceding the encapsulation header, or in
34 * its absence, that of the top IP header. The value of skb->data will always
35 * point to the top IP header.
36 */
37static void xfrm6_encap(struct sk_buff *skb)
38{
39 struct dst_entry *dst = skb->dst;
40 struct xfrm_state *x = dst->xfrm;
41 struct ipv6hdr *iph, *top_iph;
42 int dsfield;
43
44 skb_push(skb, x->props.header_len);
45 iph = skb->nh.ipv6h;
46
47 if (!x->props.mode) {
48 u8 *prevhdr;
49 int hdr_len;
50
51 hdr_len = ip6_find_1stfragopt(skb, &prevhdr);
52 skb->nh.raw = prevhdr - x->props.header_len;
53 skb->h.raw = skb->data + hdr_len;
54 memmove(skb->data, iph, hdr_len);
55 return;
56 }
57
58 skb->nh.raw = skb->data;
59 top_iph = skb->nh.ipv6h;
60 skb->nh.raw = &top_iph->nexthdr;
61 skb->h.ipv6h = top_iph + 1;
62
63 top_iph->version = 6;
64 top_iph->priority = iph->priority;
65 top_iph->flow_lbl[0] = iph->flow_lbl[0];
66 top_iph->flow_lbl[1] = iph->flow_lbl[1];
67 top_iph->flow_lbl[2] = iph->flow_lbl[2];
68 dsfield = ipv6_get_dsfield(top_iph);
69 dsfield = INET_ECN_encapsulate(dsfield, dsfield);
70 if (x->props.flags & XFRM_STATE_NOECN)
71 dsfield &= ~INET_ECN_MASK;
72 ipv6_change_dsfield(top_iph, 0, dsfield);
73 top_iph->nexthdr = IPPROTO_IPV6;
74 top_iph->hop_limit = dst_metric(dst->child, RTAX_HOPLIMIT);
75 ipv6_addr_copy(&top_iph->saddr, (struct in6_addr *)&x->props.saddr);
76 ipv6_addr_copy(&top_iph->daddr, (struct in6_addr *)&x->id.daddr);
77}
78
79static int xfrm6_tunnel_check_size(struct sk_buff *skb) 20static int xfrm6_tunnel_check_size(struct sk_buff *skb)
80{ 21{
81 int mtu, ret = 0; 22 int mtu, ret = 0;
@@ -118,7 +59,9 @@ static int xfrm6_output_one(struct sk_buff *skb)
118 if (err) 59 if (err)
119 goto error; 60 goto error;
120 61
121 xfrm6_encap(skb); 62 err = x->mode->output(skb);
63 if (err)
64 goto error;
122 65
123 err = x->type->output(x, skb); 66 err = x->type->output(x, skb);
124 if (err) 67 if (err)
diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c
index 44b64a593c01..b8936926c24b 100644
--- a/net/xfrm/xfrm_policy.c
+++ b/net/xfrm/xfrm_policy.c
@@ -138,6 +138,89 @@ void xfrm_put_type(struct xfrm_type *type)
138 module_put(type->owner); 138 module_put(type->owner);
139} 139}
140 140
141int xfrm_register_mode(struct xfrm_mode *mode, int family)
142{
143 struct xfrm_policy_afinfo *afinfo;
144 struct xfrm_mode **modemap;
145 int err;
146
147 if (unlikely(mode->encap >= XFRM_MODE_MAX))
148 return -EINVAL;
149
150 afinfo = xfrm_policy_lock_afinfo(family);
151 if (unlikely(afinfo == NULL))
152 return -EAFNOSUPPORT;
153
154 err = -EEXIST;
155 modemap = afinfo->mode_map;
156 if (likely(modemap[mode->encap] == NULL)) {
157 modemap[mode->encap] = mode;
158 err = 0;
159 }
160
161 xfrm_policy_unlock_afinfo(afinfo);
162 return err;
163}
164EXPORT_SYMBOL(xfrm_register_mode);
165
166int xfrm_unregister_mode(struct xfrm_mode *mode, int family)
167{
168 struct xfrm_policy_afinfo *afinfo;
169 struct xfrm_mode **modemap;
170 int err;
171
172 if (unlikely(mode->encap >= XFRM_MODE_MAX))
173 return -EINVAL;
174
175 afinfo = xfrm_policy_lock_afinfo(family);
176 if (unlikely(afinfo == NULL))
177 return -EAFNOSUPPORT;
178
179 err = -ENOENT;
180 modemap = afinfo->mode_map;
181 if (likely(modemap[mode->encap] == mode)) {
182 modemap[mode->encap] = NULL;
183 err = 0;
184 }
185
186 xfrm_policy_unlock_afinfo(afinfo);
187 return err;
188}
189EXPORT_SYMBOL(xfrm_unregister_mode);
190
191struct xfrm_mode *xfrm_get_mode(unsigned int encap, int family)
192{
193 struct xfrm_policy_afinfo *afinfo;
194 struct xfrm_mode *mode;
195 int modload_attempted = 0;
196
197 if (unlikely(encap >= XFRM_MODE_MAX))
198 return NULL;
199
200retry:
201 afinfo = xfrm_policy_get_afinfo(family);
202 if (unlikely(afinfo == NULL))
203 return NULL;
204
205 mode = afinfo->mode_map[encap];
206 if (unlikely(mode && !try_module_get(mode->owner)))
207 mode = NULL;
208 if (!mode && !modload_attempted) {
209 xfrm_policy_put_afinfo(afinfo);
210 request_module("xfrm-mode-%d-%d", family, encap);
211 modload_attempted = 1;
212 goto retry;
213 }
214
215 xfrm_policy_put_afinfo(afinfo);
216 return mode;
217}
218
219void xfrm_put_mode(struct xfrm_mode *mode)
220{
221 module_put(mode->owner);
222}
223
141static inline unsigned long make_jiffies(long secs) 224static inline unsigned long make_jiffies(long secs)
142{ 225{
143 if (secs >= (MAX_SCHEDULE_TIMEOUT-1)/HZ) 226 if (secs >= (MAX_SCHEDULE_TIMEOUT-1)/HZ)
diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c
index ee62c239a7e3..17b29ec3c417 100644
--- a/net/xfrm/xfrm_state.c
+++ b/net/xfrm/xfrm_state.c
@@ -77,6 +77,8 @@ static void xfrm_state_gc_destroy(struct xfrm_state *x)
77 kfree(x->ealg); 77 kfree(x->ealg);
78 kfree(x->calg); 78 kfree(x->calg);
79 kfree(x->encap); 79 kfree(x->encap);
80 if (x->mode)
81 xfrm_put_mode(x->mode);
80 if (x->type) { 82 if (x->type) {
81 x->type->destructor(x); 83 x->type->destructor(x);
82 xfrm_put_type(x->type); 84 xfrm_put_type(x->type);
@@ -1193,6 +1195,10 @@ int xfrm_init_state(struct xfrm_state *x)
1193 if (err) 1195 if (err)
1194 goto error; 1196 goto error;
1195 1197
1198 x->mode = xfrm_get_mode(x->props.mode, family);
1199 if (x->mode == NULL)
1200 goto error;
1201
1196 x->km.state = XFRM_STATE_VALID; 1202 x->km.state = XFRM_STATE_VALID;
1197 1203
1198error: 1204error: