diff options
author | Herbert Xu <herbert@gondor.apana.org.au> | 2006-05-28 02:05:54 -0400 |
---|---|---|
committer | David S. Miller <davem@sunset.davemloft.net> | 2006-06-18 00:28:39 -0400 |
commit | b59f45d0b2878ab76f8053b0973654e6621828ee (patch) | |
tree | 40dc5e2ede2620f7935fb3dae0d0eb199851f611 /net/ipv6 | |
parent | 546be2405be119ef55467aace45f337a16e5d424 (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/ipv6')
-rw-r--r-- | net/ipv6/Kconfig | 20 | ||||
-rw-r--r-- | net/ipv6/Makefile | 2 | ||||
-rw-r--r-- | net/ipv6/ip6_output.c | 2 | ||||
-rw-r--r-- | net/ipv6/xfrm6_input.c | 29 | ||||
-rw-r--r-- | net/ipv6/xfrm6_mode_transport.c | 73 | ||||
-rw-r--r-- | net/ipv6/xfrm6_mode_tunnel.c | 121 | ||||
-rw-r--r-- | net/ipv6/xfrm6_output.c | 63 |
7 files changed, 224 insertions, 86 deletions
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 | ||
109 | config 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 | |||
119 | config 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 | |||
109 | config IPV6_TUNNEL | 129 | config 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 | |||
20 | obj-$(CONFIG_INET6_IPCOMP) += ipcomp6.o | 20 | obj-$(CONFIG_INET6_IPCOMP) += ipcomp6.o |
21 | obj-$(CONFIG_INET6_XFRM_TUNNEL) += xfrm6_tunnel.o | 21 | obj-$(CONFIG_INET6_XFRM_TUNNEL) += xfrm6_tunnel.o |
22 | obj-$(CONFIG_INET6_TUNNEL) += tunnel6.o | 22 | obj-$(CONFIG_INET6_TUNNEL) += tunnel6.o |
23 | obj-$(CONFIG_INET6_XFRM_MODE_TRANSPORT) += xfrm6_mode_transport.o | ||
24 | obj-$(CONFIG_INET6_XFRM_MODE_TUNNEL) += xfrm6_mode_tunnel.o | ||
23 | obj-$(CONFIG_NETFILTER) += netfilter/ | 25 | obj-$(CONFIG_NETFILTER) += netfilter/ |
24 | 26 | ||
25 | obj-$(CONFIG_IPV6_TUNNEL) += ip6_tunnel.o | 27 | obj-$(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 | } |
492 | EXPORT_SYMBOL_GPL(ip6_find_1stfragopt); | ||
491 | 493 | ||
492 | static int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *)) | 494 | static 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 | ||
22 | static 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 | |||
31 | int xfrm6_rcv_spi(struct sk_buff *skb, u32 spi) | 19 | int 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 | */ | ||
28 | static 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 | |||
45 | static int xfrm6_transport_input(struct xfrm_state *x, struct sk_buff *skb) | ||
46 | { | ||
47 | return 0; | ||
48 | } | ||
49 | |||
50 | static 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 | |||
57 | static int __init xfrm6_transport_init(void) | ||
58 | { | ||
59 | return xfrm_register_mode(&xfrm6_transport_mode, AF_INET6); | ||
60 | } | ||
61 | |||
62 | static 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 | |||
70 | module_init(xfrm6_transport_init); | ||
71 | module_exit(xfrm6_transport_exit); | ||
72 | MODULE_LICENSE("GPL"); | ||
73 | MODULE_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 | |||
19 | static 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 | */ | ||
40 | static 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 | |||
72 | static 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 | |||
94 | out: | ||
95 | return err; | ||
96 | } | ||
97 | |||
98 | static 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 | |||
105 | static int __init xfrm6_tunnel_init(void) | ||
106 | { | ||
107 | return xfrm_register_mode(&xfrm6_tunnel_mode, AF_INET6); | ||
108 | } | ||
109 | |||
110 | static 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 | |||
118 | module_init(xfrm6_tunnel_init); | ||
119 | module_exit(xfrm6_tunnel_exit); | ||
120 | MODULE_LICENSE("GPL"); | ||
121 | MODULE_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 | */ | ||
37 | static 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 | |||
79 | static int xfrm6_tunnel_check_size(struct sk_buff *skb) | 20 | static 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) |