Commit 995dca4c authored by David S. Miller's avatar David S. Miller

Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/klassert/ipsec-next

Steffen Klassert says:

====================
One patch to rename a newly introduced struct. The rest is
the rework of the IPsec virtual tunnel interface for ipv6 to
support inter address family tunneling and namespace crossing.

1) Rename the newly introduced struct xfrm_filter to avoid a
   conflict with iproute2. From Nicolas Dichtel.

2) Introduce xfrm_input_afinfo to access the address family
   dependent tunnel callback functions properly.

3) Add and use a IPsec protocol multiplexer for ipv6.

4) Remove dst_entry caching. vti can lookup multiple different
   dst entries, dependent of the configured xfrm states. Therefore
   it does not make to cache a dst_entry.

5) Remove caching of flow informations. vti6 does not use the the
   tunnel endpoint addresses to do route and xfrm lookups.

6) Update the vti6 to use its own receive hook.

7) Remove the now unused xfrm_tunnel_notifier. This was used from vti
   and is replaced by the IPsec protocol multiplexer hooks.

8) Support inter address family tunneling for vti6.

9) Check if the tunnel endpoints of the xfrm state and the vti interface
   are matching and return an error otherwise.

10) Enable namespace crossing for vti devices.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents d70e941b 61220ab3
...@@ -121,7 +121,7 @@ struct xfrm_state_walk { ...@@ -121,7 +121,7 @@ struct xfrm_state_walk {
u8 dying; u8 dying;
u8 proto; u8 proto;
u32 seq; u32 seq;
struct xfrm_filter *filter; struct xfrm_address_filter *filter;
}; };
/* Full description of state of transformer. */ /* Full description of state of transformer. */
...@@ -349,6 +349,16 @@ int xfrm_state_unregister_afinfo(struct xfrm_state_afinfo *afinfo); ...@@ -349,6 +349,16 @@ int xfrm_state_unregister_afinfo(struct xfrm_state_afinfo *afinfo);
struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned int family); struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned int family);
void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo); void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo);
struct xfrm_input_afinfo {
unsigned int family;
struct module *owner;
int (*callback)(struct sk_buff *skb, u8 protocol,
int err);
};
int xfrm_input_register_afinfo(struct xfrm_input_afinfo *afinfo);
int xfrm_input_unregister_afinfo(struct xfrm_input_afinfo *afinfo);
void xfrm_state_delete_tunnel(struct xfrm_state *x); void xfrm_state_delete_tunnel(struct xfrm_state *x);
struct xfrm_type { struct xfrm_type {
...@@ -1364,18 +1374,22 @@ struct xfrm4_protocol { ...@@ -1364,18 +1374,22 @@ struct xfrm4_protocol {
int priority; int priority;
}; };
/* XFRM tunnel handlers. */ struct xfrm6_protocol {
struct xfrm_tunnel {
int (*handler)(struct sk_buff *skb); int (*handler)(struct sk_buff *skb);
int (*err_handler)(struct sk_buff *skb, u32 info); int (*cb_handler)(struct sk_buff *skb, int err);
int (*err_handler)(struct sk_buff *skb, struct inet6_skb_parm *opt,
u8 type, u8 code, int offset, __be32 info);
struct xfrm_tunnel __rcu *next; struct xfrm6_protocol __rcu *next;
int priority; int priority;
}; };
struct xfrm_tunnel_notifier { /* XFRM tunnel handlers. */
struct xfrm_tunnel {
int (*handler)(struct sk_buff *skb); int (*handler)(struct sk_buff *skb);
struct xfrm_tunnel_notifier __rcu *next; int (*err_handler)(struct sk_buff *skb, u32 info);
struct xfrm_tunnel __rcu *next;
int priority; int priority;
}; };
...@@ -1392,11 +1406,14 @@ void xfrm4_init(void); ...@@ -1392,11 +1406,14 @@ void xfrm4_init(void);
int xfrm_state_init(struct net *net); int xfrm_state_init(struct net *net);
void xfrm_state_fini(struct net *net); void xfrm_state_fini(struct net *net);
void xfrm4_state_init(void); void xfrm4_state_init(void);
void xfrm4_protocol_init(void);
#ifdef CONFIG_XFRM #ifdef CONFIG_XFRM
int xfrm6_init(void); int xfrm6_init(void);
void xfrm6_fini(void); void xfrm6_fini(void);
int xfrm6_state_init(void); int xfrm6_state_init(void);
void xfrm6_state_fini(void); void xfrm6_state_fini(void);
int xfrm6_protocol_init(void);
void xfrm6_protocol_fini(void);
#else #else
static inline int xfrm6_init(void) static inline int xfrm6_init(void)
{ {
...@@ -1423,7 +1440,7 @@ static inline void xfrm_sysctl_fini(struct net *net) ...@@ -1423,7 +1440,7 @@ static inline void xfrm_sysctl_fini(struct net *net)
#endif #endif
void xfrm_state_walk_init(struct xfrm_state_walk *walk, u8 proto, void xfrm_state_walk_init(struct xfrm_state_walk *walk, u8 proto,
struct xfrm_filter *filter); struct xfrm_address_filter *filter);
int xfrm_state_walk(struct net *net, struct xfrm_state_walk *walk, int xfrm_state_walk(struct net *net, struct xfrm_state_walk *walk,
int (*func)(struct xfrm_state *, int, void*), void *); int (*func)(struct xfrm_state *, int, void*), void *);
void xfrm_state_walk_done(struct xfrm_state_walk *walk, struct net *net); void xfrm_state_walk_done(struct xfrm_state_walk *walk, struct net *net);
...@@ -1531,8 +1548,6 @@ int xfrm4_protocol_deregister(struct xfrm4_protocol *handler, unsigned char prot ...@@ -1531,8 +1548,6 @@ int xfrm4_protocol_deregister(struct xfrm4_protocol *handler, unsigned char prot
int xfrm4_tunnel_register(struct xfrm_tunnel *handler, unsigned short family); int xfrm4_tunnel_register(struct xfrm_tunnel *handler, unsigned short family);
int xfrm4_tunnel_deregister(struct xfrm_tunnel *handler, unsigned short family); int xfrm4_tunnel_deregister(struct xfrm_tunnel *handler, unsigned short family);
void xfrm4_local_error(struct sk_buff *skb, u32 mtu); void xfrm4_local_error(struct sk_buff *skb, u32 mtu);
int xfrm6_mode_tunnel_input_register(struct xfrm_tunnel_notifier *handler);
int xfrm6_mode_tunnel_input_deregister(struct xfrm_tunnel_notifier *handler);
int xfrm6_extract_header(struct sk_buff *skb); int xfrm6_extract_header(struct sk_buff *skb);
int xfrm6_extract_input(struct xfrm_state *x, struct sk_buff *skb); int xfrm6_extract_input(struct xfrm_state *x, struct sk_buff *skb);
int xfrm6_rcv_spi(struct sk_buff *skb, int nexthdr, __be32 spi); int xfrm6_rcv_spi(struct sk_buff *skb, int nexthdr, __be32 spi);
...@@ -1541,6 +1556,9 @@ int xfrm6_rcv(struct sk_buff *skb); ...@@ -1541,6 +1556,9 @@ int xfrm6_rcv(struct sk_buff *skb);
int xfrm6_input_addr(struct sk_buff *skb, xfrm_address_t *daddr, int xfrm6_input_addr(struct sk_buff *skb, xfrm_address_t *daddr,
xfrm_address_t *saddr, u8 proto); xfrm_address_t *saddr, u8 proto);
void xfrm6_local_error(struct sk_buff *skb, u32 mtu); void xfrm6_local_error(struct sk_buff *skb, u32 mtu);
int xfrm6_rcv_cb(struct sk_buff *skb, u8 protocol, int err);
int xfrm6_protocol_register(struct xfrm6_protocol *handler, unsigned char protocol);
int xfrm6_protocol_deregister(struct xfrm6_protocol *handler, unsigned char protocol);
int xfrm6_tunnel_register(struct xfrm6_tunnel *handler, unsigned short family); int xfrm6_tunnel_register(struct xfrm6_tunnel *handler, unsigned short family);
int xfrm6_tunnel_deregister(struct xfrm6_tunnel *handler, unsigned short family); int xfrm6_tunnel_deregister(struct xfrm6_tunnel *handler, unsigned short family);
__be32 xfrm6_tunnel_alloc_spi(struct net *net, xfrm_address_t *saddr); __be32 xfrm6_tunnel_alloc_spi(struct net *net, xfrm_address_t *saddr);
...@@ -1784,18 +1802,6 @@ static inline int xfrm_mark_put(struct sk_buff *skb, const struct xfrm_mark *m) ...@@ -1784,18 +1802,6 @@ static inline int xfrm_mark_put(struct sk_buff *skb, const struct xfrm_mark *m)
return ret; return ret;
} }
static inline int xfrm_rcv_cb(struct sk_buff *skb, unsigned int family,
u8 protocol, int err)
{
switch(family) {
#ifdef CONFIG_INET
case AF_INET:
return xfrm4_rcv_cb(skb, protocol, err);
#endif
}
return 0;
}
static inline int xfrm_tunnel_check(struct sk_buff *skb, struct xfrm_state *x, static inline int xfrm_tunnel_check(struct sk_buff *skb, struct xfrm_state *x,
unsigned int family) unsigned int family)
{ {
......
...@@ -299,7 +299,7 @@ enum xfrm_attr_type_t { ...@@ -299,7 +299,7 @@ enum xfrm_attr_type_t {
XFRMA_REPLAY_ESN_VAL, /* struct xfrm_replay_esn */ XFRMA_REPLAY_ESN_VAL, /* struct xfrm_replay_esn */
XFRMA_SA_EXTRA_FLAGS, /* __u32 */ XFRMA_SA_EXTRA_FLAGS, /* __u32 */
XFRMA_PROTO, /* __u8 */ XFRMA_PROTO, /* __u8 */
XFRMA_FILTER, /* struct xfrm_filter */ XFRMA_ADDRESS_FILTER, /* struct xfrm_address_filter */
__XFRMA_MAX __XFRMA_MAX
#define XFRMA_MAX (__XFRMA_MAX - 1) #define XFRMA_MAX (__XFRMA_MAX - 1)
...@@ -476,7 +476,7 @@ struct xfrm_user_mapping { ...@@ -476,7 +476,7 @@ struct xfrm_user_mapping {
__be16 new_sport; __be16 new_sport;
}; };
struct xfrm_filter { struct xfrm_address_filter {
xfrm_address_t saddr; xfrm_address_t saddr;
xfrm_address_t daddr; xfrm_address_t daddr;
__u16 family; __u16 family;
......
...@@ -325,6 +325,7 @@ void __init xfrm4_init(void) ...@@ -325,6 +325,7 @@ void __init xfrm4_init(void)
xfrm4_state_init(); xfrm4_state_init();
xfrm4_policy_init(); xfrm4_policy_init();
xfrm4_protocol_init();
#ifdef CONFIG_SYSCTL #ifdef CONFIG_SYSCTL
register_pernet_subsys(&xfrm4_net_ops); register_pernet_subsys(&xfrm4_net_ops);
#endif #endif
......
...@@ -179,6 +179,12 @@ static const struct net_protocol ipcomp4_protocol = { ...@@ -179,6 +179,12 @@ static const struct net_protocol ipcomp4_protocol = {
.netns_ok = 1, .netns_ok = 1,
}; };
static struct xfrm_input_afinfo xfrm4_input_afinfo = {
.family = AF_INET,
.owner = THIS_MODULE,
.callback = xfrm4_rcv_cb,
};
static inline const struct net_protocol *netproto(unsigned char protocol) static inline const struct net_protocol *netproto(unsigned char protocol)
{ {
switch (protocol) { switch (protocol) {
...@@ -199,7 +205,6 @@ int xfrm4_protocol_register(struct xfrm4_protocol *handler, ...@@ -199,7 +205,6 @@ int xfrm4_protocol_register(struct xfrm4_protocol *handler,
struct xfrm4_protocol __rcu **pprev; struct xfrm4_protocol __rcu **pprev;
struct xfrm4_protocol *t; struct xfrm4_protocol *t;
bool add_netproto = false; bool add_netproto = false;
int ret = -EEXIST; int ret = -EEXIST;
int priority = handler->priority; int priority = handler->priority;
...@@ -273,3 +278,9 @@ int xfrm4_protocol_deregister(struct xfrm4_protocol *handler, ...@@ -273,3 +278,9 @@ int xfrm4_protocol_deregister(struct xfrm4_protocol *handler,
return ret; return ret;
} }
EXPORT_SYMBOL(xfrm4_protocol_deregister); EXPORT_SYMBOL(xfrm4_protocol_deregister);
void __init xfrm4_protocol_init(void)
{
xfrm_input_register_afinfo(&xfrm4_input_afinfo);
}
EXPORT_SYMBOL(xfrm4_protocol_init);
...@@ -16,7 +16,7 @@ ipv6-$(CONFIG_SYSCTL) = sysctl_net_ipv6.o ...@@ -16,7 +16,7 @@ ipv6-$(CONFIG_SYSCTL) = sysctl_net_ipv6.o
ipv6-$(CONFIG_IPV6_MROUTE) += ip6mr.o ipv6-$(CONFIG_IPV6_MROUTE) += ip6mr.o
ipv6-$(CONFIG_XFRM) += xfrm6_policy.o xfrm6_state.o xfrm6_input.o \ ipv6-$(CONFIG_XFRM) += xfrm6_policy.o xfrm6_state.o xfrm6_input.o \
xfrm6_output.o xfrm6_output.o xfrm6_protocol.o
ipv6-$(CONFIG_NETFILTER) += netfilter.o ipv6-$(CONFIG_NETFILTER) += netfilter.o
ipv6-$(CONFIG_IPV6_MULTIPLE_TABLES) += fib6_rules.o ipv6-$(CONFIG_IPV6_MULTIPLE_TABLES) += fib6_rules.o
ipv6-$(CONFIG_PROC_FS) += proc.o ipv6-$(CONFIG_PROC_FS) += proc.o
......
...@@ -643,8 +643,8 @@ static int ah6_input(struct xfrm_state *x, struct sk_buff *skb) ...@@ -643,8 +643,8 @@ static int ah6_input(struct xfrm_state *x, struct sk_buff *skb)
return err; return err;
} }
static void ah6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, static int ah6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
u8 type, u8 code, int offset, __be32 info) u8 type, u8 code, int offset, __be32 info)
{ {
struct net *net = dev_net(skb->dev); struct net *net = dev_net(skb->dev);
struct ipv6hdr *iph = (struct ipv6hdr*)skb->data; struct ipv6hdr *iph = (struct ipv6hdr*)skb->data;
...@@ -653,17 +653,19 @@ static void ah6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, ...@@ -653,17 +653,19 @@ static void ah6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
if (type != ICMPV6_PKT_TOOBIG && if (type != ICMPV6_PKT_TOOBIG &&
type != NDISC_REDIRECT) type != NDISC_REDIRECT)
return; return 0;
x = xfrm_state_lookup(net, skb->mark, (xfrm_address_t *)&iph->daddr, ah->spi, IPPROTO_AH, AF_INET6); x = xfrm_state_lookup(net, skb->mark, (xfrm_address_t *)&iph->daddr, ah->spi, IPPROTO_AH, AF_INET6);
if (!x) if (!x)
return; return 0;
if (type == NDISC_REDIRECT) if (type == NDISC_REDIRECT)
ip6_redirect(skb, net, skb->dev->ifindex, 0); ip6_redirect(skb, net, skb->dev->ifindex, 0);
else else
ip6_update_pmtu(skb, net, info, 0, 0); ip6_update_pmtu(skb, net, info, 0, 0);
xfrm_state_put(x); xfrm_state_put(x);
return 0;
} }
static int ah6_init_state(struct xfrm_state *x) static int ah6_init_state(struct xfrm_state *x)
...@@ -748,6 +750,11 @@ static void ah6_destroy(struct xfrm_state *x) ...@@ -748,6 +750,11 @@ static void ah6_destroy(struct xfrm_state *x)
kfree(ahp); kfree(ahp);
} }
static int ah6_rcv_cb(struct sk_buff *skb, int err)
{
return 0;
}
static const struct xfrm_type ah6_type = static const struct xfrm_type ah6_type =
{ {
.description = "AH6", .description = "AH6",
...@@ -761,10 +768,11 @@ static const struct xfrm_type ah6_type = ...@@ -761,10 +768,11 @@ static const struct xfrm_type ah6_type =
.hdr_offset = xfrm6_find_1stfragopt, .hdr_offset = xfrm6_find_1stfragopt,
}; };
static const struct inet6_protocol ah6_protocol = { static struct xfrm6_protocol ah6_protocol = {
.handler = xfrm6_rcv, .handler = xfrm6_rcv,
.cb_handler = ah6_rcv_cb,
.err_handler = ah6_err, .err_handler = ah6_err,
.flags = INET6_PROTO_NOPOLICY, .priority = 0,
}; };
static int __init ah6_init(void) static int __init ah6_init(void)
...@@ -774,7 +782,7 @@ static int __init ah6_init(void) ...@@ -774,7 +782,7 @@ static int __init ah6_init(void)
return -EAGAIN; return -EAGAIN;
} }
if (inet6_add_protocol(&ah6_protocol, IPPROTO_AH) < 0) { if (xfrm6_protocol_register(&ah6_protocol, IPPROTO_AH) < 0) {
pr_info("%s: can't add protocol\n", __func__); pr_info("%s: can't add protocol\n", __func__);
xfrm_unregister_type(&ah6_type, AF_INET6); xfrm_unregister_type(&ah6_type, AF_INET6);
return -EAGAIN; return -EAGAIN;
...@@ -785,7 +793,7 @@ static int __init ah6_init(void) ...@@ -785,7 +793,7 @@ static int __init ah6_init(void)
static void __exit ah6_fini(void) static void __exit ah6_fini(void)
{ {
if (inet6_del_protocol(&ah6_protocol, IPPROTO_AH) < 0) if (xfrm6_protocol_deregister(&ah6_protocol, IPPROTO_AH) < 0)
pr_info("%s: can't remove protocol\n", __func__); pr_info("%s: can't remove protocol\n", __func__);
if (xfrm_unregister_type(&ah6_type, AF_INET6) < 0) if (xfrm_unregister_type(&ah6_type, AF_INET6) < 0)
......
...@@ -421,8 +421,8 @@ static u32 esp6_get_mtu(struct xfrm_state *x, int mtu) ...@@ -421,8 +421,8 @@ static u32 esp6_get_mtu(struct xfrm_state *x, int mtu)
net_adj) & ~(blksize - 1)) + net_adj - 2; net_adj) & ~(blksize - 1)) + net_adj - 2;
} }
static void esp6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, static int esp6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
u8 type, u8 code, int offset, __be32 info) u8 type, u8 code, int offset, __be32 info)
{ {
struct net *net = dev_net(skb->dev); struct net *net = dev_net(skb->dev);
const struct ipv6hdr *iph = (const struct ipv6hdr *)skb->data; const struct ipv6hdr *iph = (const struct ipv6hdr *)skb->data;
...@@ -431,18 +431,20 @@ static void esp6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, ...@@ -431,18 +431,20 @@ static void esp6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
if (type != ICMPV6_PKT_TOOBIG && if (type != ICMPV6_PKT_TOOBIG &&
type != NDISC_REDIRECT) type != NDISC_REDIRECT)
return; return 0;
x = xfrm_state_lookup(net, skb->mark, (const xfrm_address_t *)&iph->daddr, x = xfrm_state_lookup(net, skb->mark, (const xfrm_address_t *)&iph->daddr,
esph->spi, IPPROTO_ESP, AF_INET6); esph->spi, IPPROTO_ESP, AF_INET6);
if (!x) if (!x)
return; return 0;
if (type == NDISC_REDIRECT) if (type == NDISC_REDIRECT)
ip6_redirect(skb, net, skb->dev->ifindex, 0); ip6_redirect(skb, net, skb->dev->ifindex, 0);
else else
ip6_update_pmtu(skb, net, info, 0, 0); ip6_update_pmtu(skb, net, info, 0, 0);
xfrm_state_put(x); xfrm_state_put(x);
return 0;
} }
static void esp6_destroy(struct xfrm_state *x) static void esp6_destroy(struct xfrm_state *x)
...@@ -614,6 +616,11 @@ static int esp6_init_state(struct xfrm_state *x) ...@@ -614,6 +616,11 @@ static int esp6_init_state(struct xfrm_state *x)
return err; return err;
} }
static int esp6_rcv_cb(struct sk_buff *skb, int err)
{
return 0;
}
static const struct xfrm_type esp6_type = static const struct xfrm_type esp6_type =
{ {
.description = "ESP6", .description = "ESP6",
...@@ -628,10 +635,11 @@ static const struct xfrm_type esp6_type = ...@@ -628,10 +635,11 @@ static const struct xfrm_type esp6_type =
.hdr_offset = xfrm6_find_1stfragopt, .hdr_offset = xfrm6_find_1stfragopt,
}; };
static const struct inet6_protocol esp6_protocol = { static struct xfrm6_protocol esp6_protocol = {
.handler = xfrm6_rcv, .handler = xfrm6_rcv,
.cb_handler = esp6_rcv_cb,
.err_handler = esp6_err, .err_handler = esp6_err,
.flags = INET6_PROTO_NOPOLICY, .priority = 0,
}; };
static int __init esp6_init(void) static int __init esp6_init(void)
...@@ -640,7 +648,7 @@ static int __init esp6_init(void) ...@@ -640,7 +648,7 @@ static int __init esp6_init(void)
pr_info("%s: can't add xfrm type\n", __func__); pr_info("%s: can't add xfrm type\n", __func__);
return -EAGAIN; return -EAGAIN;
} }
if (inet6_add_protocol(&esp6_protocol, IPPROTO_ESP) < 0) { if (xfrm6_protocol_register(&esp6_protocol, IPPROTO_ESP) < 0) {
pr_info("%s: can't add protocol\n", __func__); pr_info("%s: can't add protocol\n", __func__);
xfrm_unregister_type(&esp6_type, AF_INET6); xfrm_unregister_type(&esp6_type, AF_INET6);
return -EAGAIN; return -EAGAIN;
...@@ -651,7 +659,7 @@ static int __init esp6_init(void) ...@@ -651,7 +659,7 @@ static int __init esp6_init(void)
static void __exit esp6_fini(void) static void __exit esp6_fini(void)
{ {
if (inet6_del_protocol(&esp6_protocol, IPPROTO_ESP) < 0) if (xfrm6_protocol_deregister(&esp6_protocol, IPPROTO_ESP) < 0)
pr_info("%s: can't remove protocol\n", __func__); pr_info("%s: can't remove protocol\n", __func__);
if (xfrm_unregister_type(&esp6_type, AF_INET6) < 0) if (xfrm_unregister_type(&esp6_type, AF_INET6) < 0)
pr_info("%s: can't remove xfrm type\n", __func__); pr_info("%s: can't remove xfrm type\n", __func__);
......
This diff is collapsed.
...@@ -53,7 +53,7 @@ ...@@ -53,7 +53,7 @@
#include <linux/icmpv6.h> #include <linux/icmpv6.h>
#include <linux/mutex.h> #include <linux/mutex.h>
static void ipcomp6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, static int ipcomp6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
u8 type, u8 code, int offset, __be32 info) u8 type, u8 code, int offset, __be32 info)
{ {
struct net *net = dev_net(skb->dev); struct net *net = dev_net(skb->dev);
...@@ -65,19 +65,21 @@ static void ipcomp6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, ...@@ -65,19 +65,21 @@ static void ipcomp6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
if (type != ICMPV6_PKT_TOOBIG && if (type != ICMPV6_PKT_TOOBIG &&
type != NDISC_REDIRECT) type != NDISC_REDIRECT)
return; return 0;
spi = htonl(ntohs(ipcomph->cpi)); spi = htonl(ntohs(ipcomph->cpi));
x = xfrm_state_lookup(net, skb->mark, (const xfrm_address_t *)&iph->daddr, x = xfrm_state_lookup(net, skb->mark, (const xfrm_address_t *)&iph->daddr,
spi, IPPROTO_COMP, AF_INET6); spi, IPPROTO_COMP, AF_INET6);
if (!x) if (!x)
return; return 0;
if (type == NDISC_REDIRECT) if (type == NDISC_REDIRECT)
ip6_redirect(skb, net, skb->dev->ifindex, 0); ip6_redirect(skb, net, skb->dev->ifindex, 0);
else else
ip6_update_pmtu(skb, net, info, 0, 0); ip6_update_pmtu(skb, net, info, 0, 0);
xfrm_state_put(x); xfrm_state_put(x);
return 0;
} }
static struct xfrm_state *ipcomp6_tunnel_create(struct xfrm_state *x) static struct xfrm_state *ipcomp6_tunnel_create(struct xfrm_state *x)
...@@ -174,6 +176,11 @@ static int ipcomp6_init_state(struct xfrm_state *x) ...@@ -174,6 +176,11 @@ static int ipcomp6_init_state(struct xfrm_state *x)
return err; return err;
} }
static int ipcomp6_rcv_cb(struct sk_buff *skb, int err)
{
return 0;
}
static const struct xfrm_type ipcomp6_type = static const struct xfrm_type ipcomp6_type =
{ {
.description = "IPCOMP6", .description = "IPCOMP6",
...@@ -186,11 +193,12 @@ static const struct xfrm_type ipcomp6_type = ...@@ -186,11 +193,12 @@ static const struct xfrm_type ipcomp6_type =
.hdr_offset = xfrm6_find_1stfragopt, .hdr_offset = xfrm6_find_1stfragopt,
}; };
static const struct inet6_protocol ipcomp6_protocol = static struct xfrm6_protocol ipcomp6_protocol =
{ {
.handler = xfrm6_rcv, .handler = xfrm6_rcv,
.cb_handler = ipcomp6_rcv_cb,
.err_handler = ipcomp6_err, .err_handler = ipcomp6_err,
.flags = INET6_PROTO_NOPOLICY, .priority = 0,
}; };
static int __init ipcomp6_init(void) static int __init ipcomp6_init(void)
...@@ -199,7 +207,7 @@ static int __init ipcomp6_init(void) ...@@ -199,7 +207,7 @@ static int __init ipcomp6_init(void)
pr_info("%s: can't add xfrm type\n", __func__); pr_info("%s: can't add xfrm type\n", __func__);
return -EAGAIN; return -EAGAIN;
} }
if (inet6_add_protocol(&ipcomp6_protocol, IPPROTO_COMP) < 0) { if (xfrm6_protocol_register(&ipcomp6_protocol, IPPROTO_COMP) < 0) {
pr_info("%s: can't add protocol\n", __func__); pr_info("%s: can't add protocol\n", __func__);
xfrm_unregister_type(&ipcomp6_type, AF_INET6); xfrm_unregister_type(&ipcomp6_type, AF_INET6);
return -EAGAIN; return -EAGAIN;
...@@ -209,7 +217,7 @@ static int __init ipcomp6_init(void) ...@@ -209,7 +217,7 @@ static int __init ipcomp6_init(void)
static void __exit ipcomp6_fini(void) static void __exit ipcomp6_fini(void)
{ {
if (inet6_del_protocol(&ipcomp6_protocol, IPPROTO_COMP) < 0) if (xfrm6_protocol_deregister(&ipcomp6_protocol, IPPROTO_COMP) < 0)
pr_info("%s: can't remove protocol\n", __func__); pr_info("%s: can't remove protocol\n", __func__);
if (xfrm_unregister_type(&ipcomp6_type, AF_INET6) < 0) if (xfrm_unregister_type(&ipcomp6_type, AF_INET6) < 0)
pr_info("%s: can't remove xfrm type\n", __func__); pr_info("%s: can't remove xfrm type\n", __func__);
......
...@@ -18,65 +18,6 @@ ...@@ -18,65 +18,6 @@
#include <net/ipv6.h> #include <net/ipv6.h>
#include <net/xfrm.h> #include <net/xfrm.h>
/* Informational hook. The decap is still done here. */
static struct xfrm_tunnel_notifier __rcu *rcv_notify_handlers __read_mostly;
static DEFINE_MUTEX(xfrm6_mode_tunnel_input_mutex);
int xfrm6_mode_tunnel_input_register(struct xfrm_tunnel_notifier *handler)
{
struct xfrm_tunnel_notifier __rcu **pprev;
struct xfrm_tunnel_notifier *t;
int ret = -EEXIST;
int priority = handler->priority;
mutex_lock(&xfrm6_mode_tunnel_input_mutex);
for (pprev = &rcv_notify_handlers;
(t = rcu_dereference_protected(*pprev,
lockdep_is_held(&xfrm6_mode_tunnel_input_mutex))) != NULL;
pprev = &t->next) {
if (t->priority > priority)
break;
if (t->priority == priority)
goto err;
}
handler->next = *pprev;
rcu_assign_pointer(*pprev, handler);
ret = 0;
err:
mutex_unlock(&xfrm6_mode_tunnel_input_mutex);
return ret;
}
EXPORT_SYMBOL_GPL(xfrm6_mode_tunnel_input_register);
int xfrm6_mode_tunnel_input_deregister(struct xfrm_tunnel_notifier *handler)
{
struct xfrm_tunnel_notifier __rcu **pprev;
struct xfrm_tunnel_notifier *t;
int ret = -ENOENT;
mutex_lock(&xfrm6_mode_tunnel_input_mutex);
for (pprev = &rcv_notify_handlers;
(t = rcu_dereference_protected(*pprev,
lockdep_is_held(&xfrm6_mode_tunnel_input_mutex))) != NULL;
pprev = &t->next) {
if (t == handler) {
*pprev = handler->next;
ret = 0;
break;
}
}
mutex_unlock(&xfrm6_mode_tunnel_input_mutex);
synchronize_net();
return ret;
}
EXPORT_SYMBOL_GPL(xfrm6_mode_tunnel_input_deregister);
static inline void ipip6_ecn_decapsulate(struct sk_buff *skb) static inline void ipip6_ecn_decapsulate(struct sk_buff *skb)
{ {
const struct ipv6hdr *outer_iph = ipv6_hdr(skb); const struct ipv6hdr *outer_iph = ipv6_hdr(skb);
...@@ -130,7 +71,6 @@ static int xfrm6_mode_tunnel_output(struct xfrm_state *x, struct sk_buff *skb) ...@@ -130,7 +71,6 @@ static int xfrm6_mode_tunnel_output(struct xfrm_state *x, struct sk_buff *skb)
static int xfrm6_mode_tunnel_input(struct xfrm_state *x, struct sk_buff *skb) static int xfrm6_mode_tunnel_input(struct xfrm_state *x, struct sk_buff *skb)
{ {
struct xfrm_tunnel_notifier *handler;
int err = -EINVAL; int err = -EINVAL;
if (XFRM_MODE_SKB_CB(skb)->protocol != IPPROTO_IPV6) if (XFRM_MODE_SKB_CB(skb)->protocol != IPPROTO_IPV6)
...@@ -138,9 +78,6 @@ static int xfrm6_mode_tunnel_input(struct xfrm_state *x, struct sk_buff *skb) ...@@ -138,9 +78,6 @@ static int xfrm6_mode_tunnel_input(struct xfrm_state *x, struct sk_buff *skb)
if (!pskb_may_pull(skb, sizeof(struct ipv6hdr))) if (!pskb_may_pull(skb, sizeof(struct ipv6hdr)))
goto out; goto out;
for_each_input_rcu(rcv_notify_handlers, handler)
handler->handler(skb);
err = skb_unclone(skb, GFP_ATOMIC); err = skb_unclone(skb, GFP_ATOMIC);
if (err) if (err)
goto out; goto out;
......
...@@ -389,11 +389,17 @@ int __init xfrm6_init(void) ...@@ -389,11 +389,17 @@ int __init xfrm6_init(void)
if (ret) if (ret)
goto out_policy; goto out_policy;
ret = xfrm6_protocol_init();
if (ret)
goto out_state;
#ifdef CONFIG_SYSCTL #ifdef CONFIG_SYSCTL
register_pernet_subsys(&xfrm6_net_ops); register_pernet_subsys(&xfrm6_net_ops);
#endif #endif
out: out:
return ret; return ret;
out_state:
xfrm6_state_fini();
out_policy: out_policy:
xfrm6_policy_fini(); xfrm6_policy_fini();
goto out; goto out;
...@@ -404,6 +410,7 @@ void xfrm6_fini(void) ...@@ -404,6 +410,7 @@ void xfrm6_fini(void)
#ifdef CONFIG_SYSCTL #ifdef CONFIG_SYSCTL
unregister_pernet_subsys(&xfrm6_net_ops); unregister_pernet_subsys(&xfrm6_net_ops);
#endif #endif
xfrm6_protocol_fini();
xfrm6_policy_fini(); xfrm6_policy_fini();
xfrm6_state_fini(); xfrm6_state_fini();
dst_entries_destroy(&xfrm6_dst_ops); dst_entries_destroy(&xfrm6_dst_ops);
......
/* xfrm6_protocol.c - Generic xfrm protocol multiplexer for ipv6.
*
* Copyright (C) 2013 secunet Security Networks AG
*
* Author:
* Steffen Klassert <steffen.klassert@secunet.com>
*
* Based on:
* net/ipv4/xfrm4_protocol.c
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*/
#include <linux/init.h>
#include <linux/mutex.h>
#include <linux/skbuff.h>
#include <linux/icmpv6.h>
#include <net/ipv6.h>
#include <net/protocol.h>
#include <net/xfrm.h>
static struct xfrm6_protocol __rcu *esp6_handlers __read_mostly;
static struct xfrm6_protocol __rcu *ah6_handlers __read_mostly;
static struct xfrm6_protocol __rcu *ipcomp6_handlers __read_mostly;
static DEFINE_MUTEX(xfrm6_protocol_mutex);
static inline struct xfrm6_protocol __rcu **proto_handlers(u8 protocol)
{
switch (protocol) {
case IPPROTO_ESP:
return &esp6_handlers;
case IPPROTO_AH:
return &ah6_handlers;
case IPPROTO_COMP:
return &ipcomp6_handlers;
}
return NULL;
}
#define for_each_protocol_rcu(head, handler) \
for (handler = rcu_dereference(head); \
handler != NULL; \
handler = rcu_dereference(handler->next)) \
int xfrm6_rcv_cb(struct sk_buff *skb, u8 protocol, int err)
{
int ret;
struct xfrm6_protocol *handler;
for_each_protocol_rcu(*proto_handlers(protocol), handler)
if ((ret = handler->cb_handler(skb, err)) <= 0)
return ret;
return 0;
}
EXPORT_SYMBOL(xfrm6_rcv_cb);
static int xfrm6_esp_rcv(struct sk_buff *skb)
{
int ret;
struct xfrm6_protocol *handler;
XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip6 = NULL;
for_each_protocol_rcu(esp6_handlers, handler)
if ((ret = handler->handler(skb)) != -EINVAL)
return ret;
icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH, 0);
kfree_skb(skb);
return 0;
}
static void xfrm6_esp_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
u8 type, u8 code, int offset, __be32 info)
{
struct xfrm6_protocol *handler;
for_each_protocol_rcu(esp6_handlers, handler)
if (!handler->err_handler(skb, opt, type, code, offset, info))
break;
}
static int xfrm6_ah_rcv(struct sk_buff *skb)
{
int ret;
struct xfrm6_protocol *handler;
XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip6 = NULL;
for_each_protocol_rcu(ah6_handlers, handler)
if ((ret = handler->handler(skb)) != -EINVAL)
return ret;
icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH, 0);
kfree_skb(skb);
return 0;
}
static void xfrm6_ah_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
u8 type, u8 code, int offset, __be32 info)
{
struct xfrm6_protocol *handler;
for_each_protocol_rcu(ah6_handlers, handler)
if (!handler->err_handler(skb, opt, type, code, offset, info))
break;
}
static int xfrm6_ipcomp_rcv(struct sk_buff *skb)
{
int ret;
struct xfrm6_protocol *handler;
XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip6 = NULL;
for_each_protocol_rcu(ipcomp6_handlers, handler)
if ((ret = handler->handler(skb)) != -EINVAL)
return ret;
icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH, 0);
kfree_skb(skb);
return 0;
}
static void xfrm6_ipcomp_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
u8 type, u8 code, int offset, __be32 info)
{
struct xfrm6_protocol *handler;
for_each_protocol_rcu(ipcomp6_handlers, handler)
if (!handler->err_handler(skb, opt, type, code, offset, info))
break;
}
static const struct inet6_protocol esp6_protocol = {
.handler = xfrm6_esp_rcv,
.err_handler = xfrm6_esp_err,
.flags = INET6_PROTO_NOPOLICY,
};
static const struct inet6_protocol ah6_protocol = {
.handler = xfrm6_ah_rcv,
.err_handler = xfrm6_ah_err,
.flags = INET6_PROTO_NOPOLICY,
};
static const struct inet6_protocol ipcomp6_protocol = {
.handler = xfrm6_ipcomp_rcv,
.err_handler = xfrm6_ipcomp_err,
.flags = INET6_PROTO_NOPOLICY,
};
static struct xfrm_input_afinfo xfrm6_input_afinfo = {
.family = AF_INET6,
.owner = THIS_MODULE,
.callback = xfrm6_rcv_cb,
};
static inline const struct inet6_protocol *netproto(unsigned char protocol)
{
switch (protocol) {
case IPPROTO_ESP:
return &esp6_protocol;
case IPPROTO_AH:
return &ah6_protocol;
case IPPROTO_COMP:
return &ipcomp6_protocol;
}
return NULL;
}
int xfrm6_protocol_register(struct xfrm6_protocol *handler,
unsigned char protocol)
{
struct xfrm6_protocol __rcu **pprev;
struct xfrm6_protocol *t;
bool add_netproto = false;
int ret = -EEXIST;
int priority = handler->priority;
mutex_lock(&xfrm6_protocol_mutex);
if (!rcu_dereference_protected(*proto_handlers(protocol),
lockdep_is_held(&xfrm6_protocol_mutex)))
add_netproto = true;
for (pprev = proto_handlers(protocol);
(t = rcu_dereference_protected(*pprev,
lockdep_is_held(&xfrm6_protocol_mutex))) != NULL;
pprev = &t->next) {
if (t->priority < priority)
break;
if (t->priority == priority)
goto err;
}
handler->next = *pprev;
rcu_assign_pointer(*pprev, handler);
ret = 0;
err:
mutex_unlock(&xfrm6_protocol_mutex);
if (add_netproto) {
if (inet6_add_protocol(netproto(protocol), protocol)) {
pr_err("%s: can't add protocol\n", __func__);
ret = -EAGAIN;
}
}
return ret;
}
EXPORT_SYMBOL(xfrm6_protocol_register);
int xfrm6_protocol_deregister(struct xfrm6_protocol *handler,
unsigned char protocol)
{
struct xfrm6_protocol __rcu **pprev;
struct xfrm6_protocol *t;
int ret = -ENOENT;
mutex_lock(&xfrm6_protocol_mutex);
for (pprev = proto_handlers(protocol);
(t = rcu_dereference_protected(*pprev,
lockdep_is_held(&xfrm6_protocol_mutex))) != NULL;
pprev = &t->next) {
if (t == handler) {
*pprev = handler->next;
ret = 0;
break;
}
}
if (!rcu_dereference_protected(*proto_handlers(protocol),
lockdep_is_held(&xfrm6_protocol_mutex))) {
if (inet6_del_protocol(netproto(protocol), protocol) < 0) {
pr_err("%s: can't remove protocol\n", __func__);
ret = -EAGAIN;
}
}
mutex_unlock(&xfrm6_protocol_mutex);
synchronize_net();
return ret;
}
EXPORT_SYMBOL(xfrm6_protocol_deregister);
int __init xfrm6_protocol_init(void)
{
return xfrm_input_register_afinfo(&xfrm6_input_afinfo);
}
void xfrm6_protocol_fini(void)
{
xfrm_input_unregister_afinfo(&xfrm6_input_afinfo);
}
...@@ -1799,7 +1799,7 @@ static void pfkey_dump_sa_done(struct pfkey_sock *pfk) ...@@ -1799,7 +1799,7 @@ static void pfkey_dump_sa_done(struct pfkey_sock *pfk)
static int pfkey_dump(struct sock *sk, struct sk_buff *skb, const struct sadb_msg *hdr, void * const *ext_hdrs) static int pfkey_dump(struct sock *sk, struct sk_buff *skb, const struct sadb_msg *hdr, void * const *ext_hdrs)
{ {
u8 proto; u8 proto;
struct xfrm_filter *filter = NULL; struct xfrm_address_filter *filter = NULL;
struct pfkey_sock *pfk = pfkey_sk(sk); struct pfkey_sock *pfk = pfkey_sk(sk);
if (pfk->dump.dump != NULL) if (pfk->dump.dump != NULL)
......
...@@ -16,6 +16,81 @@ ...@@ -16,6 +16,81 @@
static struct kmem_cache *secpath_cachep __read_mostly; static struct kmem_cache *secpath_cachep __read_mostly;
static DEFINE_SPINLOCK(xfrm_input_afinfo_lock);
static struct xfrm_input_afinfo __rcu *xfrm_input_afinfo[NPROTO];
int xfrm_input_register_afinfo(struct xfrm_input_afinfo *afinfo)
{
int err = 0;
if (unlikely(afinfo == NULL))
return -EINVAL;
if (unlikely(afinfo->family >= NPROTO))
return -EAFNOSUPPORT;
spin_lock_bh(&xfrm_input_afinfo_lock);
if (unlikely(xfrm_input_afinfo[afinfo->family] != NULL))
err = -ENOBUFS;
else
rcu_assign_pointer(xfrm_input_afinfo[afinfo->family], afinfo);
spin_unlock_bh(&xfrm_input_afinfo_lock);
return err;
}
EXPORT_SYMBOL(xfrm_input_register_afinfo);
int xfrm_input_unregister_afinfo(struct xfrm_input_afinfo *afinfo)
{
int err = 0;
if (unlikely(afinfo == NULL))
return -EINVAL;
if (unlikely(afinfo->family >= NPROTO))
return -EAFNOSUPPORT;
spin_lock_bh(&xfrm_input_afinfo_lock);
if (likely(xfrm_input_afinfo[afinfo->family] != NULL)) {
if (unlikely(xfrm_input_afinfo[afinfo->family] != afinfo))
err = -EINVAL;
else
RCU_INIT_POINTER(xfrm_input_afinfo[afinfo->family], NULL);
}
spin_unlock_bh(&xfrm_input_afinfo_lock);
synchronize_rcu();
return err;
}
EXPORT_SYMBOL(xfrm_input_unregister_afinfo);
static struct xfrm_input_afinfo *xfrm_input_get_afinfo(unsigned int family)
{
struct xfrm_input_afinfo *afinfo;
if (unlikely(family >= NPROTO))
return NULL;
rcu_read_lock();
afinfo = rcu_dereference(xfrm_input_afinfo[family]);
if (unlikely(!afinfo))
rcu_read_unlock();
return afinfo;
}
static void xfrm_input_put_afinfo(struct xfrm_input_afinfo *afinfo)
{
rcu_read_unlock();
}
static int xfrm_rcv_cb(struct sk_buff *skb, unsigned int family, u8 protocol,
int err)
{
int ret;
struct xfrm_input_afinfo *afinfo = xfrm_input_get_afinfo(family);
if (!afinfo)
return -EAFNOSUPPORT;
ret = afinfo->callback(skb, protocol, err);
xfrm_input_put_afinfo(afinfo);
return ret;
}
void __secpath_destroy(struct sec_path *sp) void __secpath_destroy(struct sec_path *sp)
{ {
int i; int i;
......
...@@ -1609,7 +1609,7 @@ int xfrm_alloc_spi(struct xfrm_state *x, u32 low, u32 high) ...@@ -1609,7 +1609,7 @@ int xfrm_alloc_spi(struct xfrm_state *x, u32 low, u32 high)
EXPORT_SYMBOL(xfrm_alloc_spi); EXPORT_SYMBOL(xfrm_alloc_spi);
static bool __xfrm_state_filter_match(struct xfrm_state *x, static bool __xfrm_state_filter_match(struct xfrm_state *x,
struct xfrm_filter *filter) struct xfrm_address_filter *filter)
{ {
if (filter) { if (filter) {
if ((filter->family == AF_INET || if ((filter->family == AF_INET ||
...@@ -1668,7 +1668,7 @@ int xfrm_state_walk(struct net *net, struct xfrm_state_walk *walk, ...@@ -1668,7 +1668,7 @@ int xfrm_state_walk(struct net *net, struct xfrm_state_walk *walk,
EXPORT_SYMBOL(xfrm_state_walk); EXPORT_SYMBOL(xfrm_state_walk);
void xfrm_state_walk_init(struct xfrm_state_walk *walk, u8 proto, void xfrm_state_walk_init(struct xfrm_state_walk *walk, u8 proto,
struct xfrm_filter *filter) struct xfrm_address_filter *filter)
{ {
INIT_LIST_HEAD(&walk->all); INIT_LIST_HEAD(&walk->all);
walk->proto = proto; walk->proto = proto;
......
...@@ -899,7 +899,7 @@ static int xfrm_dump_sa(struct sk_buff *skb, struct netlink_callback *cb) ...@@ -899,7 +899,7 @@ static int xfrm_dump_sa(struct sk_buff *skb, struct netlink_callback *cb)
if (!cb->args[0]) { if (!cb->args[0]) {
struct nlattr *attrs[XFRMA_MAX+1]; struct nlattr *attrs[XFRMA_MAX+1];
struct xfrm_filter *filter = NULL; struct xfrm_address_filter *filter = NULL;
u8 proto = 0; u8 proto = 0;
int err; int err;
...@@ -910,12 +910,12 @@ static int xfrm_dump_sa(struct sk_buff *skb, struct netlink_callback *cb) ...@@ -910,12 +910,12 @@ static int xfrm_dump_sa(struct sk_buff *skb, struct netlink_callback *cb)
if (err < 0) if (err < 0)
return err; return err;
if (attrs[XFRMA_FILTER]) { if (attrs[XFRMA_ADDRESS_FILTER]) {
filter = kmalloc(sizeof(*filter), GFP_KERNEL); filter = kmalloc(sizeof(*filter), GFP_KERNEL);
if (filter == NULL) if (filter == NULL)
return -ENOMEM; return -ENOMEM;
memcpy(filter, nla_data(attrs[XFRMA_FILTER]), memcpy(filter, nla_data(attrs[XFRMA_ADDRESS_FILTER]),
sizeof(*filter)); sizeof(*filter));
} }
...@@ -2329,7 +2329,7 @@ static const struct nla_policy xfrma_policy[XFRMA_MAX+1] = { ...@@ -2329,7 +2329,7 @@ static const struct nla_policy xfrma_policy[XFRMA_MAX+1] = {
[XFRMA_REPLAY_ESN_VAL] = { .len = sizeof(struct xfrm_replay_state_esn) }, [XFRMA_REPLAY_ESN_VAL] = { .len = sizeof(struct xfrm_replay_state_esn) },
[XFRMA_SA_EXTRA_FLAGS] = { .type = NLA_U32 }, [XFRMA_SA_EXTRA_FLAGS] = { .type = NLA_U32 },
[XFRMA_PROTO] = { .type = NLA_U8 }, [XFRMA_PROTO] = { .type = NLA_U8 },
[XFRMA_FILTER] = { .len = sizeof(struct xfrm_filter) }, [XFRMA_ADDRESS_FILTER] = { .len = sizeof(struct xfrm_address_filter) },
}; };
static const struct xfrm_link { static const struct xfrm_link {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment