diff options
-rw-r--r-- | include/linux/xfrm.h | 4 | ||||
-rw-r--r-- | include/net/xfrm.h | 17 | ||||
-rw-r--r-- | net/ipv4/Kconfig | 18 | ||||
-rw-r--r-- | net/ipv4/Makefile | 2 | ||||
-rw-r--r-- | net/ipv4/xfrm4_input.c | 28 | ||||
-rw-r--r-- | net/ipv4/xfrm4_mode_transport.c | 69 | ||||
-rw-r--r-- | net/ipv4/xfrm4_mode_tunnel.c | 125 | ||||
-rw-r--r-- | net/ipv4/xfrm4_output.c | 61 | ||||
-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 | ||||
-rw-r--r-- | net/xfrm/xfrm_policy.c | 83 | ||||
-rw-r--r-- | net/xfrm/xfrm_state.c | 6 |
17 files changed, 553 insertions, 170 deletions
diff --git a/include/linux/xfrm.h b/include/linux/xfrm.h index 6b42cc474c01..46a15c7a1a13 100644 --- a/include/linux/xfrm.h +++ b/include/linux/xfrm.h | |||
@@ -118,6 +118,10 @@ enum | |||
118 | XFRM_SHARE_UNIQUE /* Use once */ | 118 | XFRM_SHARE_UNIQUE /* Use once */ |
119 | }; | 119 | }; |
120 | 120 | ||
121 | #define XFRM_MODE_TRANSPORT 0 | ||
122 | #define XFRM_MODE_TUNNEL 1 | ||
123 | #define XFRM_MODE_MAX 2 | ||
124 | |||
121 | /* Netlink configuration messages. */ | 125 | /* Netlink configuration messages. */ |
122 | enum { | 126 | enum { |
123 | XFRM_MSG_BASE = 0x10, | 127 | XFRM_MSG_BASE = 0x10, |
diff --git a/include/net/xfrm.h b/include/net/xfrm.h index ed7c9747059d..ed5bb34f817f 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h | |||
@@ -20,6 +20,8 @@ | |||
20 | #include <net/ip6_fib.h> | 20 | #include <net/ip6_fib.h> |
21 | 21 | ||
22 | #define XFRM_ALIGN8(len) (((len) + 7) & ~7) | 22 | #define XFRM_ALIGN8(len) (((len) + 7) & ~7) |
23 | #define MODULE_ALIAS_XFRM_MODE(family, encap) \ | ||
24 | MODULE_ALIAS("xfrm-mode-" __stringify(family) "-" __stringify(encap)) | ||
23 | 25 | ||
24 | extern struct sock *xfrm_nl; | 26 | extern struct sock *xfrm_nl; |
25 | extern u32 sysctl_xfrm_aevent_etime; | 27 | extern u32 sysctl_xfrm_aevent_etime; |
@@ -164,6 +166,7 @@ struct xfrm_state | |||
164 | /* Reference to data common to all the instances of this | 166 | /* Reference to data common to all the instances of this |
165 | * transformer. */ | 167 | * transformer. */ |
166 | struct xfrm_type *type; | 168 | struct xfrm_type *type; |
169 | struct xfrm_mode *mode; | ||
167 | 170 | ||
168 | /* Security context */ | 171 | /* Security context */ |
169 | struct xfrm_sec_ctx *security; | 172 | struct xfrm_sec_ctx *security; |
@@ -205,6 +208,7 @@ struct xfrm_dst; | |||
205 | struct xfrm_policy_afinfo { | 208 | struct xfrm_policy_afinfo { |
206 | unsigned short family; | 209 | unsigned short family; |
207 | struct xfrm_type *type_map[256]; | 210 | struct xfrm_type *type_map[256]; |
211 | struct xfrm_mode *mode_map[XFRM_MODE_MAX]; | ||
208 | struct dst_ops *dst_ops; | 212 | struct dst_ops *dst_ops; |
209 | void (*garbage_collect)(void); | 213 | void (*garbage_collect)(void); |
210 | int (*dst_lookup)(struct xfrm_dst **dst, struct flowi *fl); | 214 | int (*dst_lookup)(struct xfrm_dst **dst, struct flowi *fl); |
@@ -267,6 +271,19 @@ extern int xfrm_unregister_type(struct xfrm_type *type, unsigned short family); | |||
267 | extern struct xfrm_type *xfrm_get_type(u8 proto, unsigned short family); | 271 | extern struct xfrm_type *xfrm_get_type(u8 proto, unsigned short family); |
268 | extern void xfrm_put_type(struct xfrm_type *type); | 272 | extern void xfrm_put_type(struct xfrm_type *type); |
269 | 273 | ||
274 | struct xfrm_mode { | ||
275 | int (*input)(struct xfrm_state *x, struct sk_buff *skb); | ||
276 | int (*output)(struct sk_buff *skb); | ||
277 | |||
278 | struct module *owner; | ||
279 | unsigned int encap; | ||
280 | }; | ||
281 | |||
282 | extern int xfrm_register_mode(struct xfrm_mode *mode, int family); | ||
283 | extern int xfrm_unregister_mode(struct xfrm_mode *mode, int family); | ||
284 | extern struct xfrm_mode *xfrm_get_mode(unsigned int encap, int family); | ||
285 | extern void xfrm_put_mode(struct xfrm_mode *mode); | ||
286 | |||
270 | struct xfrm_tmpl | 287 | struct xfrm_tmpl |
271 | { | 288 | { |
272 | /* id in template is interpreted as: | 289 | /* id in template is interpreted as: |
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 | ||
417 | config 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 | |||
426 | config 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 | |||
417 | config INET_DIAG | 435 | config 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 | |||
24 | obj-$(CONFIG_INET_IPCOMP) += ipcomp.o | 24 | obj-$(CONFIG_INET_IPCOMP) += ipcomp.o |
25 | obj-$(CONFIG_INET_XFRM_TUNNEL) += xfrm4_tunnel.o | 25 | obj-$(CONFIG_INET_XFRM_TUNNEL) += xfrm4_tunnel.o |
26 | obj-$(CONFIG_INET_TUNNEL) += tunnel4.o | 26 | obj-$(CONFIG_INET_TUNNEL) += tunnel4.o |
27 | obj-$(CONFIG_INET_XFRM_MODE_TRANSPORT) += xfrm4_mode_transport.o | ||
28 | obj-$(CONFIG_INET_XFRM_MODE_TUNNEL) += xfrm4_mode_tunnel.o | ||
27 | obj-$(CONFIG_IP_PNP) += ipconfig.o | 29 | obj-$(CONFIG_IP_PNP) += ipconfig.o |
28 | obj-$(CONFIG_IP_ROUTE_MULTIPATH_RR) += multipath_rr.o | 30 | obj-$(CONFIG_IP_ROUTE_MULTIPATH_RR) += multipath_rr.o |
29 | obj-$(CONFIG_IP_ROUTE_MULTIPATH_RANDOM) += multipath_random.o | 31 | obj-$(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 | ||
25 | EXPORT_SYMBOL(xfrm4_rcv); | 24 | EXPORT_SYMBOL(xfrm4_rcv); |
26 | 25 | ||
27 | static 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 | |||
36 | static int xfrm4_parse_spi(struct sk_buff *skb, u8 nexthdr, u32 *spi, u32 *seq) | 26 | static 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 | */ | ||
24 | static 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 | |||
41 | static int xfrm4_transport_input(struct xfrm_state *x, struct sk_buff *skb) | ||
42 | { | ||
43 | return 0; | ||
44 | } | ||
45 | |||
46 | static 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 | |||
53 | static int __init xfrm4_transport_init(void) | ||
54 | { | ||
55 | return xfrm_register_mode(&xfrm4_transport_mode, AF_INET); | ||
56 | } | ||
57 | |||
58 | static 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 | |||
66 | module_init(xfrm4_transport_init); | ||
67 | module_exit(xfrm4_transport_exit); | ||
68 | MODULE_LICENSE("GPL"); | ||
69 | MODULE_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 | |||
17 | static 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 | */ | ||
36 | static 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 | |||
74 | static 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 | |||
98 | out: | ||
99 | return err; | ||
100 | } | ||
101 | |||
102 | static 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 | |||
109 | static int __init xfrm4_tunnel_init(void) | ||
110 | { | ||
111 | return xfrm_register_mode(&xfrm4_tunnel_mode, AF_INET); | ||
112 | } | ||
113 | |||
114 | static 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 | |||
122 | module_init(xfrm4_tunnel_init); | ||
123 | module_exit(xfrm4_tunnel_exit); | ||
124 | MODULE_LICENSE("GPL"); | ||
125 | MODULE_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 | */ | ||
33 | static 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 | |||
76 | static int xfrm4_tunnel_check_size(struct sk_buff *skb) | 19 | static 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 | ||
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) |
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 | ||
141 | int 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 | } | ||
164 | EXPORT_SYMBOL(xfrm_register_mode); | ||
165 | |||
166 | int 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 | } | ||
189 | EXPORT_SYMBOL(xfrm_unregister_mode); | ||
190 | |||
191 | struct 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 | |||
200 | retry: | ||
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 | |||
219 | void xfrm_put_mode(struct xfrm_mode *mode) | ||
220 | { | ||
221 | module_put(mode->owner); | ||
222 | } | ||
223 | |||
141 | static inline unsigned long make_jiffies(long secs) | 224 | static 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 | ||
1198 | error: | 1204 | error: |