Adds an EXPORT_SYMBOL for afinfo_get_rcu, as it will now be called from ipv6 in case of CONFIG_IPV6=m.
This change has virtually no effect on vmlinux size, but it reduces afinfo size and allows followup patch to make xfrm modes const. Signed-off-by: Florian Westphal <f...@strlen.de> --- include/net/xfrm.h | 1 - net/ipv4/xfrm4_output.c | 11 ++++++++++- net/ipv6/xfrm6_output.c | 21 +++++++++++++++++++-- net/xfrm/xfrm_input.c | 28 ++++++++++++++++++++++------ net/xfrm/xfrm_output.c | 12 +++++++++++- net/xfrm/xfrm_policy.c | 7 ++++++- net/xfrm/xfrm_state.c | 4 ++-- 7 files changed, 70 insertions(+), 14 deletions(-) diff --git a/include/net/xfrm.h b/include/net/xfrm.h index 6249d44997e0..bbce45f5ca6d 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 int xfrm_unregister_type_offload(const struct xfrm_type_offload *type, unsigned short family); struct xfrm_mode { - struct xfrm_state_afinfo *afinfo; struct module *owner; u8 encap; u8 family; diff --git a/net/ipv4/xfrm4_output.c b/net/ipv4/xfrm4_output.c index 6802d1aee424..cff048ad8562 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) static int __xfrm4_output(struct net *net, struct sock *sk, struct sk_buff *skb) { struct xfrm_state *x = skb_dst(skb)->xfrm; + const struct xfrm_state_afinfo *afinfo; + int ret = -EAFNOSUPPORT; #ifdef CONFIG_NETFILTER if (!x) { @@ -80,7 +82,14 @@ static int __xfrm4_output(struct net *net, struct sock *sk, struct sk_buff *skb) } #endif - return x->outer_mode->afinfo->output_finish(sk, skb); + rcu_read_lock(); + afinfo = xfrm_state_afinfo_get_rcu(x->outer_mode->family); + if (afinfo) + ret = afinfo->output_finish(sk, skb); + else + kfree_skb(skb); + rcu_read_unlock(); + return ret; } 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..82168de60e6b 100644 --- a/net/ipv6/xfrm6_output.c +++ b/net/ipv6/xfrm6_output.c @@ -122,11 +122,27 @@ int xfrm6_output_finish(struct sock *sk, struct sk_buff *skb) return xfrm_output(sk, skb); } +static int __xfrm6_output_state_finish(struct xfrm_state *x, struct sock *sk, struct sk_buff *skb) +{ + const struct xfrm_state_afinfo *afinfo; + int ret = -EAFNOSUPPORT; + + rcu_read_lock(); + afinfo = xfrm_state_afinfo_get_rcu(x->outer_mode->family); + if (afinfo) + ret = afinfo->output_finish(sk, skb); + else + kfree_skb(skb); + rcu_read_unlock(); + + return ret; +} + static int __xfrm6_output_finish(struct net *net, struct sock *sk, struct sk_buff *skb) { struct xfrm_state *x = skb_dst(skb)->xfrm; - return x->outer_mode->afinfo->output_finish(sk, skb); + return __xfrm6_output_state_finish(x, sk, skb); } static int __xfrm6_output(struct net *net, struct sock *sk, struct sk_buff *skb) @@ -168,7 +184,8 @@ static int __xfrm6_output(struct net *net, struct sock *sk, struct sk_buff *skb) __xfrm6_output_finish); skip_frag: - return x->outer_mode->afinfo->output_finish(sk, skb); + + return __xfrm6_output_state_finish(x, sk, skb); } 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 065ace68b26a..a6db51cee1cf 100644 --- a/net/xfrm/xfrm_input.c +++ b/net/xfrm/xfrm_input.c @@ -350,19 +350,29 @@ xfrm_inner_mode_encap_remove(struct xfrm_state *x, struct sk_buff *skb) int xfrm_prepare_input(struct xfrm_state *x, struct sk_buff *skb) { struct xfrm_mode *inner_mode = x->inner_mode; - int err; + const struct xfrm_state_afinfo *afinfo; + int err = -EAFNOSUPPORT; - err = x->outer_mode->afinfo->extract_input(x, skb); - if (err) + rcu_read_lock(); + afinfo = xfrm_state_afinfo_get_rcu(x->outer_mode->family); + if (afinfo) + err = afinfo->extract_input(x, skb); + + if (err) { + rcu_read_unlock(); return err; + } if (x->sel.family == AF_UNSPEC) { inner_mode = xfrm_ip2inner_mode(x, XFRM_MODE_SKB_CB(skb)->protocol); - if (inner_mode == NULL) + if (!inner_mode) { + rcu_read_unlock(); return -EAFNOSUPPORT; + } } - skb->protocol = inner_mode->afinfo->eth_proto; + skb->protocol = afinfo->eth_proto; + rcu_read_unlock(); return xfrm_inner_mode_encap_remove(x, skb); } EXPORT_SYMBOL(xfrm_prepare_input); @@ -437,6 +447,7 @@ static int xfrm_inner_mode_input(struct xfrm_state *x, struct sk_buff *skb) int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type) { + const struct xfrm_state_afinfo *afinfo; struct net *net = dev_net(skb->dev); int err; __be32 seq; @@ -702,7 +713,12 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type) if (xo) xfrm_gro = xo->flags & XFRM_GRO; - err = x->inner_mode->afinfo->transport_finish(skb, xfrm_gro || async); + err = -EAFNOSUPPORT; + rcu_read_lock(); + afinfo = xfrm_state_afinfo_get_rcu(x->inner_mode->family); + if (afinfo) + err = afinfo->transport_finish(skb, xfrm_gro || async); + rcu_read_unlock(); if (xfrm_gro) { sp = skb_sec_path(skb); if (sp) diff --git a/net/xfrm/xfrm_output.c b/net/xfrm/xfrm_output.c index 1174c7babcce..3e4b3fdf34a4 100644 --- a/net/xfrm/xfrm_output.c +++ b/net/xfrm/xfrm_output.c @@ -611,7 +611,10 @@ EXPORT_SYMBOL_GPL(xfrm_output); int xfrm_inner_extract_output(struct xfrm_state *x, struct sk_buff *skb) { + const struct xfrm_state_afinfo *afinfo; struct xfrm_mode *inner_mode; + int err = -EAFNOSUPPORT; + if (x->sel.family == AF_UNSPEC) inner_mode = xfrm_ip2inner_mode(x, xfrm_af2proto(skb_dst(skb)->ops->family)); @@ -620,7 +623,14 @@ int xfrm_inner_extract_output(struct xfrm_state *x, struct sk_buff *skb) if (inner_mode == NULL) return -EAFNOSUPPORT; - return inner_mode->afinfo->extract_output(x, skb); + + rcu_read_lock(); + afinfo = xfrm_state_afinfo_get_rcu(inner_mode->family); + if (afinfo) + err = afinfo->extract_output(x, skb); + rcu_read_unlock(); + + return err; } EXPORT_SYMBOL_GPL(xfrm_inner_extract_output); diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index 8d1a898d0ba5..e82e022182ae 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, const struct flowi *fl, struct dst_entry *dst) { + const struct xfrm_state_afinfo *afinfo; struct net *net = xp_net(policy); unsigned long now = jiffies; struct net_device *dev; @@ -2622,7 +2623,11 @@ static struct dst_entry *xfrm_bundle_create(struct xfrm_policy *policy, dst1->lastuse = now; dst1->input = dst_discard; - dst1->output = inner_mode->afinfo->output; + + rcu_read_lock(); + afinfo = xfrm_state_afinfo_get_rcu(inner_mode->family); + dst1->output = afinfo ? afinfo->output : dst_discard_out; + rcu_read_unlock(); xdst_prev = xdst; 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) if (!try_module_get(afinfo->owner)) goto out; - mode->afinfo = afinfo; modemap[mode->encap] = mode; err = 0; @@ -378,7 +377,7 @@ void xfrm_unregister_mode(struct xfrm_mode *mode) spin_lock_bh(&xfrm_mode_lock); if (likely(modemap[mode->encap] == mode)) { modemap[mode->encap] = NULL; - module_put(mode->afinfo->owner); + module_put(afinfo->owner); } spin_unlock_bh(&xfrm_mode_lock); @@ -2188,6 +2187,7 @@ struct xfrm_state_afinfo *xfrm_state_afinfo_get_rcu(unsigned int family) return rcu_dereference(xfrm_state_afinfo[family]); } +EXPORT_SYMBOL_GPL(xfrm_state_afinfo_get_rcu); struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned int family) { -- 2.19.2