diff options
-rw-r--r-- | include/net/xfrm.h | 1 | ||||
-rw-r--r-- | net/ipv4/xfrm4_output.c | 12 | ||||
-rw-r--r-- | net/ipv6/xfrm6_output.c | 21 | ||||
-rw-r--r-- | net/xfrm/xfrm_input.c | 34 | ||||
-rw-r--r-- | net/xfrm/xfrm_output.c | 12 | ||||
-rw-r--r-- | net/xfrm/xfrm_policy.c | 10 | ||||
-rw-r--r-- | net/xfrm/xfrm_state.c | 4 |
7 files changed, 80 insertions, 14 deletions
diff --git a/include/net/xfrm.h b/include/net/xfrm.h index 4351444c10fc..8d1c9506bcf6 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h | |||
@@ -423,7 +423,6 @@ int xfrm_register_type_offload(const struct xfrm_type_offload *type, unsigned sh | |||
423 | int xfrm_unregister_type_offload(const struct xfrm_type_offload *type, unsigned short family); | 423 | int xfrm_unregister_type_offload(const struct xfrm_type_offload *type, unsigned short family); |
424 | 424 | ||
425 | struct xfrm_mode { | 425 | struct xfrm_mode { |
426 | struct xfrm_state_afinfo *afinfo; | ||
427 | struct module *owner; | 426 | struct module *owner; |
428 | u8 encap; | 427 | u8 encap; |
429 | u8 family; | 428 | u8 family; |
diff --git a/net/ipv4/xfrm4_output.c b/net/ipv4/xfrm4_output.c index 6802d1aee424..7c3df14daef3 100644 --- a/net/ipv4/xfrm4_output.c +++ b/net/ipv4/xfrm4_output.c | |||
@@ -72,6 +72,8 @@ int xfrm4_output_finish(struct sock *sk, struct sk_buff *skb) | |||
72 | static int __xfrm4_output(struct net *net, struct sock *sk, struct sk_buff *skb) | 72 | static int __xfrm4_output(struct net *net, struct sock *sk, struct sk_buff *skb) |
73 | { | 73 | { |
74 | struct xfrm_state *x = skb_dst(skb)->xfrm; | 74 | struct xfrm_state *x = skb_dst(skb)->xfrm; |
75 | const struct xfrm_state_afinfo *afinfo; | ||
76 | int ret = -EAFNOSUPPORT; | ||
75 | 77 | ||
76 | #ifdef CONFIG_NETFILTER | 78 | #ifdef CONFIG_NETFILTER |
77 | if (!x) { | 79 | if (!x) { |
@@ -80,7 +82,15 @@ static int __xfrm4_output(struct net *net, struct sock *sk, struct sk_buff *skb) | |||
80 | } | 82 | } |
81 | #endif | 83 | #endif |
82 | 84 | ||
83 | return x->outer_mode->afinfo->output_finish(sk, skb); | 85 | rcu_read_lock(); |
86 | afinfo = xfrm_state_afinfo_get_rcu(x->outer_mode->family); | ||
87 | if (likely(afinfo)) | ||
88 | ret = afinfo->output_finish(sk, skb); | ||
89 | else | ||
90 | kfree_skb(skb); | ||
91 | rcu_read_unlock(); | ||
92 | |||
93 | return ret; | ||
84 | } | 94 | } |
85 | 95 | ||
86 | int xfrm4_output(struct net *net, struct sock *sk, struct sk_buff *skb) | 96 | int xfrm4_output(struct net *net, struct sock *sk, struct sk_buff *skb) |
diff --git a/net/ipv6/xfrm6_output.c b/net/ipv6/xfrm6_output.c index 2b663d2ffdcd..455fbf3b91cf 100644 --- a/net/ipv6/xfrm6_output.c +++ b/net/ipv6/xfrm6_output.c | |||
@@ -122,11 +122,28 @@ int xfrm6_output_finish(struct sock *sk, struct sk_buff *skb) | |||
122 | return xfrm_output(sk, skb); | 122 | return xfrm_output(sk, skb); |
123 | } | 123 | } |
124 | 124 | ||
125 | static int __xfrm6_output_state_finish(struct xfrm_state *x, struct sock *sk, | ||
126 | struct sk_buff *skb) | ||
127 | { | ||
128 | const struct xfrm_state_afinfo *afinfo; | ||
129 | int ret = -EAFNOSUPPORT; | ||
130 | |||
131 | rcu_read_lock(); | ||
132 | afinfo = xfrm_state_afinfo_get_rcu(x->outer_mode->family); | ||
133 | if (likely(afinfo)) | ||
134 | ret = afinfo->output_finish(sk, skb); | ||
135 | else | ||
136 | kfree_skb(skb); | ||
137 | rcu_read_unlock(); | ||
138 | |||
139 | return ret; | ||
140 | } | ||
141 | |||
125 | static int __xfrm6_output_finish(struct net *net, struct sock *sk, struct sk_buff *skb) | 142 | static int __xfrm6_output_finish(struct net *net, struct sock *sk, struct sk_buff *skb) |
126 | { | 143 | { |
127 | struct xfrm_state *x = skb_dst(skb)->xfrm; | 144 | struct xfrm_state *x = skb_dst(skb)->xfrm; |
128 | 145 | ||
129 | return x->outer_mode->afinfo->output_finish(sk, skb); | 146 | return __xfrm6_output_state_finish(x, sk, skb); |
130 | } | 147 | } |
131 | 148 | ||
132 | static int __xfrm6_output(struct net *net, struct sock *sk, struct sk_buff *skb) | 149 | static int __xfrm6_output(struct net *net, struct sock *sk, struct sk_buff *skb) |
@@ -168,7 +185,7 @@ static int __xfrm6_output(struct net *net, struct sock *sk, struct sk_buff *skb) | |||
168 | __xfrm6_output_finish); | 185 | __xfrm6_output_finish); |
169 | 186 | ||
170 | skip_frag: | 187 | skip_frag: |
171 | return x->outer_mode->afinfo->output_finish(sk, skb); | 188 | return __xfrm6_output_state_finish(x, sk, skb); |
172 | } | 189 | } |
173 | 190 | ||
174 | int xfrm6_output(struct net *net, struct sock *sk, struct sk_buff *skb) | 191 | int xfrm6_output(struct net *net, struct sock *sk, struct sk_buff *skb) |
diff --git a/net/xfrm/xfrm_input.c b/net/xfrm/xfrm_input.c index e0fd9561ffe5..74b53c13279b 100644 --- a/net/xfrm/xfrm_input.c +++ b/net/xfrm/xfrm_input.c | |||
@@ -352,19 +352,35 @@ xfrm_inner_mode_encap_remove(struct xfrm_state *x, | |||
352 | static int xfrm_prepare_input(struct xfrm_state *x, struct sk_buff *skb) | 352 | static int xfrm_prepare_input(struct xfrm_state *x, struct sk_buff *skb) |
353 | { | 353 | { |
354 | struct xfrm_mode *inner_mode = x->inner_mode; | 354 | struct xfrm_mode *inner_mode = x->inner_mode; |
355 | int err; | 355 | const struct xfrm_state_afinfo *afinfo; |
356 | int err = -EAFNOSUPPORT; | ||
356 | 357 | ||
357 | err = x->outer_mode->afinfo->extract_input(x, skb); | 358 | rcu_read_lock(); |
358 | if (err) | 359 | afinfo = xfrm_state_afinfo_get_rcu(x->outer_mode->family); |
360 | if (likely(afinfo)) | ||
361 | err = afinfo->extract_input(x, skb); | ||
362 | |||
363 | if (err) { | ||
364 | rcu_read_unlock(); | ||
359 | return err; | 365 | return err; |
366 | } | ||
360 | 367 | ||
361 | if (x->sel.family == AF_UNSPEC) { | 368 | if (x->sel.family == AF_UNSPEC) { |
362 | inner_mode = xfrm_ip2inner_mode(x, XFRM_MODE_SKB_CB(skb)->protocol); | 369 | inner_mode = xfrm_ip2inner_mode(x, XFRM_MODE_SKB_CB(skb)->protocol); |
363 | if (inner_mode == NULL) | 370 | if (!inner_mode) { |
371 | rcu_read_unlock(); | ||
364 | return -EAFNOSUPPORT; | 372 | return -EAFNOSUPPORT; |
373 | } | ||
365 | } | 374 | } |
366 | 375 | ||
367 | skb->protocol = inner_mode->afinfo->eth_proto; | 376 | afinfo = xfrm_state_afinfo_get_rcu(inner_mode->family); |
377 | if (unlikely(!afinfo)) { | ||
378 | rcu_read_unlock(); | ||
379 | return -EAFNOSUPPORT; | ||
380 | } | ||
381 | |||
382 | skb->protocol = afinfo->eth_proto; | ||
383 | rcu_read_unlock(); | ||
368 | return xfrm_inner_mode_encap_remove(x, inner_mode, skb); | 384 | return xfrm_inner_mode_encap_remove(x, inner_mode, skb); |
369 | } | 385 | } |
370 | 386 | ||
@@ -440,6 +456,7 @@ static int xfrm_inner_mode_input(struct xfrm_state *x, | |||
440 | 456 | ||
441 | int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type) | 457 | int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type) |
442 | { | 458 | { |
459 | const struct xfrm_state_afinfo *afinfo; | ||
443 | struct net *net = dev_net(skb->dev); | 460 | struct net *net = dev_net(skb->dev); |
444 | int err; | 461 | int err; |
445 | __be32 seq; | 462 | __be32 seq; |
@@ -705,7 +722,12 @@ resume: | |||
705 | if (xo) | 722 | if (xo) |
706 | xfrm_gro = xo->flags & XFRM_GRO; | 723 | xfrm_gro = xo->flags & XFRM_GRO; |
707 | 724 | ||
708 | err = x->inner_mode->afinfo->transport_finish(skb, xfrm_gro || async); | 725 | err = -EAFNOSUPPORT; |
726 | rcu_read_lock(); | ||
727 | afinfo = xfrm_state_afinfo_get_rcu(x->inner_mode->family); | ||
728 | if (likely(afinfo)) | ||
729 | err = afinfo->transport_finish(skb, xfrm_gro || async); | ||
730 | rcu_read_unlock(); | ||
709 | if (xfrm_gro) { | 731 | if (xfrm_gro) { |
710 | sp = skb_sec_path(skb); | 732 | sp = skb_sec_path(skb); |
711 | if (sp) | 733 | if (sp) |
diff --git a/net/xfrm/xfrm_output.c b/net/xfrm/xfrm_output.c index 9bdf16f13606..17c4f58d28ea 100644 --- a/net/xfrm/xfrm_output.c +++ b/net/xfrm/xfrm_output.c | |||
@@ -623,7 +623,10 @@ EXPORT_SYMBOL_GPL(xfrm_output); | |||
623 | 623 | ||
624 | static int xfrm_inner_extract_output(struct xfrm_state *x, struct sk_buff *skb) | 624 | static int xfrm_inner_extract_output(struct xfrm_state *x, struct sk_buff *skb) |
625 | { | 625 | { |
626 | const struct xfrm_state_afinfo *afinfo; | ||
626 | struct xfrm_mode *inner_mode; | 627 | struct xfrm_mode *inner_mode; |
628 | int err = -EAFNOSUPPORT; | ||
629 | |||
627 | if (x->sel.family == AF_UNSPEC) | 630 | if (x->sel.family == AF_UNSPEC) |
628 | inner_mode = xfrm_ip2inner_mode(x, | 631 | inner_mode = xfrm_ip2inner_mode(x, |
629 | xfrm_af2proto(skb_dst(skb)->ops->family)); | 632 | xfrm_af2proto(skb_dst(skb)->ops->family)); |
@@ -632,7 +635,14 @@ static int xfrm_inner_extract_output(struct xfrm_state *x, struct sk_buff *skb) | |||
632 | 635 | ||
633 | if (inner_mode == NULL) | 636 | if (inner_mode == NULL) |
634 | return -EAFNOSUPPORT; | 637 | return -EAFNOSUPPORT; |
635 | return inner_mode->afinfo->extract_output(x, skb); | 638 | |
639 | rcu_read_lock(); | ||
640 | afinfo = xfrm_state_afinfo_get_rcu(inner_mode->family); | ||
641 | if (likely(afinfo)) | ||
642 | err = afinfo->extract_output(x, skb); | ||
643 | rcu_read_unlock(); | ||
644 | |||
645 | return err; | ||
636 | } | 646 | } |
637 | 647 | ||
638 | void xfrm_local_error(struct sk_buff *skb, int mtu) | 648 | void xfrm_local_error(struct sk_buff *skb, int mtu) |
diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index 8d1a898d0ba5..67122beb116c 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c | |||
@@ -2545,6 +2545,7 @@ static struct dst_entry *xfrm_bundle_create(struct xfrm_policy *policy, | |||
2545 | const struct flowi *fl, | 2545 | const struct flowi *fl, |
2546 | struct dst_entry *dst) | 2546 | struct dst_entry *dst) |
2547 | { | 2547 | { |
2548 | const struct xfrm_state_afinfo *afinfo; | ||
2548 | struct net *net = xp_net(policy); | 2549 | struct net *net = xp_net(policy); |
2549 | unsigned long now = jiffies; | 2550 | unsigned long now = jiffies; |
2550 | struct net_device *dev; | 2551 | struct net_device *dev; |
@@ -2622,7 +2623,14 @@ static struct dst_entry *xfrm_bundle_create(struct xfrm_policy *policy, | |||
2622 | dst1->lastuse = now; | 2623 | dst1->lastuse = now; |
2623 | 2624 | ||
2624 | dst1->input = dst_discard; | 2625 | dst1->input = dst_discard; |
2625 | dst1->output = inner_mode->afinfo->output; | 2626 | |
2627 | rcu_read_lock(); | ||
2628 | afinfo = xfrm_state_afinfo_get_rcu(inner_mode->family); | ||
2629 | if (likely(afinfo)) | ||
2630 | dst1->output = afinfo->output; | ||
2631 | else | ||
2632 | dst1->output = dst_discard_out; | ||
2633 | rcu_read_unlock(); | ||
2626 | 2634 | ||
2627 | xdst_prev = xdst; | 2635 | xdst_prev = xdst; |
2628 | 2636 | ||
diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c index c32394b59776..358b09f0d018 100644 --- a/net/xfrm/xfrm_state.c +++ b/net/xfrm/xfrm_state.c | |||
@@ -354,7 +354,6 @@ int xfrm_register_mode(struct xfrm_mode *mode) | |||
354 | if (!try_module_get(afinfo->owner)) | 354 | if (!try_module_get(afinfo->owner)) |
355 | goto out; | 355 | goto out; |
356 | 356 | ||
357 | mode->afinfo = afinfo; | ||
358 | modemap[mode->encap] = mode; | 357 | modemap[mode->encap] = mode; |
359 | err = 0; | 358 | err = 0; |
360 | 359 | ||
@@ -378,7 +377,7 @@ void xfrm_unregister_mode(struct xfrm_mode *mode) | |||
378 | spin_lock_bh(&xfrm_mode_lock); | 377 | spin_lock_bh(&xfrm_mode_lock); |
379 | if (likely(modemap[mode->encap] == mode)) { | 378 | if (likely(modemap[mode->encap] == mode)) { |
380 | modemap[mode->encap] = NULL; | 379 | modemap[mode->encap] = NULL; |
381 | module_put(mode->afinfo->owner); | 380 | module_put(afinfo->owner); |
382 | } | 381 | } |
383 | 382 | ||
384 | spin_unlock_bh(&xfrm_mode_lock); | 383 | spin_unlock_bh(&xfrm_mode_lock); |
@@ -2188,6 +2187,7 @@ struct xfrm_state_afinfo *xfrm_state_afinfo_get_rcu(unsigned int family) | |||
2188 | 2187 | ||
2189 | return rcu_dereference(xfrm_state_afinfo[family]); | 2188 | return rcu_dereference(xfrm_state_afinfo[family]); |
2190 | } | 2189 | } |
2190 | EXPORT_SYMBOL_GPL(xfrm_state_afinfo_get_rcu); | ||
2191 | 2191 | ||
2192 | struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned int family) | 2192 | struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned int family) |
2193 | { | 2193 | { |