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

Steffen Klassert says:

====================
This is the rework of the IPsec virtual tunnel interface
for ipv4 to support inter address family tunneling and
namespace crossing. The only change to the last RFC version
is a compile fix for an odd configuration where CONFIG_XFRM
is set but CONFIG_INET is not set.

1) Add and use a IPsec protocol multiplexer.

2) Add xfrm_tunnel_skb_cb to the skb common buffer
   to store a receive callback there.

3) Make vti work with i_key set by not including the i_key
   when comupting the hash for the tunnel lookup in case of
   vti tunnels.

4) Update ip_vti to use it's own receive hook.

5) Remove xfrm_tunnel_notifier, this is replaced by the IPsec
   protocol multiplexer.

6) We need to be protocol family indepenent, so use the on xfrm_lookup
   returned dst_entry instead of the ipv4 rtable in vti_tunnel_xmit().

7) Add support for inter address family tunneling.

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

8) Enable namespace crossing tor vti devices.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
David S. Miller 2014-02-27 16:31:54 -05:00
commit 8e1f40ec77
11 changed files with 660 additions and 194 deletions

View File

@ -599,16 +599,27 @@ struct xfrm_mgr {
int xfrm_register_km(struct xfrm_mgr *km);
int xfrm_unregister_km(struct xfrm_mgr *km);
struct xfrm_tunnel_skb_cb {
union {
struct inet_skb_parm h4;
struct inet6_skb_parm h6;
} header;
union {
struct ip_tunnel *ip4;
struct ip6_tnl *ip6;
} tunnel;
};
#define XFRM_TUNNEL_SKB_CB(__skb) ((struct xfrm_tunnel_skb_cb *)&((__skb)->cb[0]))
/*
* This structure is used for the duration where packets are being
* transformed by IPsec. As soon as the packet leaves IPsec the
* area beyond the generic IP part may be overwritten.
*/
struct xfrm_skb_cb {
union {
struct inet_skb_parm h4;
struct inet6_skb_parm h6;
} header;
struct xfrm_tunnel_skb_cb header;
/* Sequence number for replay protection. */
union {
@ -630,10 +641,7 @@ struct xfrm_skb_cb {
* to transmit header information to the mode input/output functions.
*/
struct xfrm_mode_skb_cb {
union {
struct inet_skb_parm h4;
struct inet6_skb_parm h6;
} header;
struct xfrm_tunnel_skb_cb header;
/* Copied from header for IPv4, always set to zero and DF for IPv6. */
__be16 id;
@ -665,10 +673,7 @@ struct xfrm_mode_skb_cb {
* related information.
*/
struct xfrm_spi_skb_cb {
union {
struct inet_skb_parm h4;
struct inet6_skb_parm h6;
} header;
struct xfrm_tunnel_skb_cb header;
unsigned int daddroff;
unsigned int family;
@ -1347,6 +1352,18 @@ struct xfrm_algo_desc {
struct sadb_alg desc;
};
/* XFRM protocol handlers. */
struct xfrm4_protocol {
int (*handler)(struct sk_buff *skb);
int (*input_handler)(struct sk_buff *skb, int nexthdr, __be32 spi,
int encap_type);
int (*cb_handler)(struct sk_buff *skb, int err);
int (*err_handler)(struct sk_buff *skb, u32 info);
struct xfrm4_protocol __rcu *next;
int priority;
};
/* XFRM tunnel handlers. */
struct xfrm_tunnel {
int (*handler)(struct sk_buff *skb);
@ -1498,18 +1515,22 @@ int xfrm4_rcv(struct sk_buff *skb);
static inline int xfrm4_rcv_spi(struct sk_buff *skb, int nexthdr, __be32 spi)
{
return xfrm4_rcv_encap(skb, nexthdr, spi, 0);
XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip4 = NULL;
XFRM_SPI_SKB_CB(skb)->family = AF_INET;
XFRM_SPI_SKB_CB(skb)->daddroff = offsetof(struct iphdr, daddr);
return xfrm_input(skb, nexthdr, spi, 0);
}
int xfrm4_extract_output(struct xfrm_state *x, struct sk_buff *skb);
int xfrm4_prepare_output(struct xfrm_state *x, struct sk_buff *skb);
int xfrm4_output(struct sk_buff *skb);
int xfrm4_output_finish(struct sk_buff *skb);
int xfrm4_rcv_cb(struct sk_buff *skb, u8 protocol, int err);
int xfrm4_protocol_register(struct xfrm4_protocol *handler, unsigned char protocol);
int xfrm4_protocol_deregister(struct xfrm4_protocol *handler, unsigned char protocol);
int xfrm4_tunnel_register(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);
int xfrm4_mode_tunnel_input_register(struct xfrm_tunnel_notifier *handler);
int xfrm4_mode_tunnel_input_deregister(struct xfrm_tunnel_notifier *handler);
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);
@ -1752,4 +1773,36 @@ static inline int xfrm_mark_put(struct sk_buff *skb, const struct xfrm_mark *m)
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,
unsigned int family)
{
bool tunnel = false;
switch(family) {
case AF_INET:
if (XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip4)
tunnel = true;
break;
case AF_INET6:
if (XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip6)
tunnel = true;
break;
}
if (tunnel && !(x->outer_mode->flags & XFRM_MODE_FLAG_TUNNEL))
return -EINVAL;
return 0;
}
#endif /* _NET_XFRM_H */

View File

@ -55,4 +55,4 @@ obj-$(CONFIG_MEMCG_KMEM) += tcp_memcontrol.o
obj-$(CONFIG_NETLABEL) += cipso_ipv4.o
obj-$(CONFIG_XFRM) += xfrm4_policy.o xfrm4_state.o xfrm4_input.o \
xfrm4_output.o
xfrm4_output.o xfrm4_protocol.o

View File

@ -428,7 +428,7 @@ out:
return err;
}
static void ah4_err(struct sk_buff *skb, u32 info)
static int ah4_err(struct sk_buff *skb, u32 info)
{
struct net *net = dev_net(skb->dev);
const struct iphdr *iph = (const struct iphdr *)skb->data;
@ -438,23 +438,25 @@ static void ah4_err(struct sk_buff *skb, u32 info)
switch (icmp_hdr(skb)->type) {
case ICMP_DEST_UNREACH:
if (icmp_hdr(skb)->code != ICMP_FRAG_NEEDED)
return;
return 0;
case ICMP_REDIRECT:
break;
default:
return;
return 0;
}
x = xfrm_state_lookup(net, skb->mark, (const xfrm_address_t *)&iph->daddr,
ah->spi, IPPROTO_AH, AF_INET);
if (!x)
return;
return 0;
if (icmp_hdr(skb)->type == ICMP_DEST_UNREACH)
ipv4_update_pmtu(skb, net, info, 0, 0, IPPROTO_AH, 0);
else
ipv4_redirect(skb, net, 0, 0, IPPROTO_AH, 0);
xfrm_state_put(x);
return 0;
}
static int ah_init_state(struct xfrm_state *x)
@ -536,6 +538,10 @@ static void ah_destroy(struct xfrm_state *x)
kfree(ahp);
}
static int ah4_rcv_cb(struct sk_buff *skb, int err)
{
return 0;
}
static const struct xfrm_type ah_type =
{
@ -549,11 +555,12 @@ static const struct xfrm_type ah_type =
.output = ah_output
};
static const struct net_protocol ah4_protocol = {
static struct xfrm4_protocol ah4_protocol = {
.handler = xfrm4_rcv,
.input_handler = xfrm_input,
.cb_handler = ah4_rcv_cb,
.err_handler = ah4_err,
.no_policy = 1,
.netns_ok = 1,
.priority = 0,
};
static int __init ah4_init(void)
@ -562,7 +569,7 @@ static int __init ah4_init(void)
pr_info("%s: can't add xfrm type\n", __func__);
return -EAGAIN;
}
if (inet_add_protocol(&ah4_protocol, IPPROTO_AH) < 0) {
if (xfrm4_protocol_register(&ah4_protocol, IPPROTO_AH) < 0) {
pr_info("%s: can't add protocol\n", __func__);
xfrm_unregister_type(&ah_type, AF_INET);
return -EAGAIN;
@ -572,7 +579,7 @@ static int __init ah4_init(void)
static void __exit ah4_fini(void)
{
if (inet_del_protocol(&ah4_protocol, IPPROTO_AH) < 0)
if (xfrm4_protocol_deregister(&ah4_protocol, IPPROTO_AH) < 0)
pr_info("%s: can't remove protocol\n", __func__);
if (xfrm_unregister_type(&ah_type, AF_INET) < 0)
pr_info("%s: can't remove xfrm type\n", __func__);

View File

@ -473,7 +473,7 @@ static u32 esp4_get_mtu(struct xfrm_state *x, int mtu)
net_adj) & ~(blksize - 1)) + net_adj - 2;
}
static void esp4_err(struct sk_buff *skb, u32 info)
static int esp4_err(struct sk_buff *skb, u32 info)
{
struct net *net = dev_net(skb->dev);
const struct iphdr *iph = (const struct iphdr *)skb->data;
@ -483,23 +483,25 @@ static void esp4_err(struct sk_buff *skb, u32 info)
switch (icmp_hdr(skb)->type) {
case ICMP_DEST_UNREACH:
if (icmp_hdr(skb)->code != ICMP_FRAG_NEEDED)
return;
return 0;
case ICMP_REDIRECT:
break;
default:
return;
return 0;
}
x = xfrm_state_lookup(net, skb->mark, (const xfrm_address_t *)&iph->daddr,
esph->spi, IPPROTO_ESP, AF_INET);
if (!x)
return;
return 0;
if (icmp_hdr(skb)->type == ICMP_DEST_UNREACH)
ipv4_update_pmtu(skb, net, info, 0, 0, IPPROTO_ESP, 0);
else
ipv4_redirect(skb, net, 0, 0, IPPROTO_ESP, 0);
xfrm_state_put(x);
return 0;
}
static void esp_destroy(struct xfrm_state *x)
@ -672,6 +674,11 @@ error:
return err;
}
static int esp4_rcv_cb(struct sk_buff *skb, int err)
{
return 0;
}
static const struct xfrm_type esp_type =
{
.description = "ESP4",
@ -685,11 +692,12 @@ static const struct xfrm_type esp_type =
.output = esp_output
};
static const struct net_protocol esp4_protocol = {
static struct xfrm4_protocol esp4_protocol = {
.handler = xfrm4_rcv,
.input_handler = xfrm_input,
.cb_handler = esp4_rcv_cb,
.err_handler = esp4_err,
.no_policy = 1,
.netns_ok = 1,
.priority = 0,
};
static int __init esp4_init(void)
@ -698,7 +706,7 @@ static int __init esp4_init(void)
pr_info("%s: can't add xfrm type\n", __func__);
return -EAGAIN;
}
if (inet_add_protocol(&esp4_protocol, IPPROTO_ESP) < 0) {
if (xfrm4_protocol_register(&esp4_protocol, IPPROTO_ESP) < 0) {
pr_info("%s: can't add protocol\n", __func__);
xfrm_unregister_type(&esp_type, AF_INET);
return -EAGAIN;
@ -708,7 +716,7 @@ static int __init esp4_init(void)
static void __exit esp4_fini(void)
{
if (inet_del_protocol(&esp4_protocol, IPPROTO_ESP) < 0)
if (xfrm4_protocol_deregister(&esp4_protocol, IPPROTO_ESP) < 0)
pr_info("%s: can't remove protocol\n", __func__);
if (xfrm_unregister_type(&esp_type, AF_INET) < 0)
pr_info("%s: can't remove xfrm type\n", __func__);

View File

@ -280,13 +280,17 @@ static struct hlist_head *ip_bucket(struct ip_tunnel_net *itn,
{
unsigned int h;
__be32 remote;
__be32 i_key = parms->i_key;
if (parms->iph.daddr && !ipv4_is_multicast(parms->iph.daddr))
remote = parms->iph.daddr;
else
remote = 0;
h = ip_tunnel_hash(parms->i_key, remote);
if (!(parms->i_flags & TUNNEL_KEY) && (parms->i_flags & VTI_ISVTI))
i_key = 0;
h = ip_tunnel_hash(i_key, remote);
return &itn->tunnels[h];
}

View File

@ -34,6 +34,7 @@
#include <linux/init.h>
#include <linux/netfilter_ipv4.h>
#include <linux/if_ether.h>
#include <linux/icmpv6.h>
#include <net/sock.h>
#include <net/ip.h>
@ -49,8 +50,8 @@ static struct rtnl_link_ops vti_link_ops __read_mostly;
static int vti_net_id __read_mostly;
static int vti_tunnel_init(struct net_device *dev);
/* We dont digest the packet therefore let the packet pass */
static int vti_rcv(struct sk_buff *skb)
static int vti_input(struct sk_buff *skb, int nexthdr, __be32 spi,
int encap_type)
{
struct ip_tunnel *tunnel;
const struct iphdr *iph = ip_hdr(skb);
@ -60,79 +61,120 @@ static int vti_rcv(struct sk_buff *skb)
tunnel = ip_tunnel_lookup(itn, skb->dev->ifindex, TUNNEL_NO_KEY,
iph->saddr, iph->daddr, 0);
if (tunnel != NULL) {
struct pcpu_sw_netstats *tstats;
u32 oldmark = skb->mark;
int ret;
if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb))
goto drop;
XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip4 = tunnel;
skb->mark = be32_to_cpu(tunnel->parms.i_key);
/* temporarily mark the skb with the tunnel o_key, to
* only match policies with this mark.
*/
skb->mark = be32_to_cpu(tunnel->parms.o_key);
ret = xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb);
skb->mark = oldmark;
if (!ret)
return -1;
tstats = this_cpu_ptr(tunnel->dev->tstats);
u64_stats_update_begin(&tstats->syncp);
tstats->rx_packets++;
tstats->rx_bytes += skb->len;
u64_stats_update_end(&tstats->syncp);
secpath_reset(skb);
skb->dev = tunnel->dev;
return 1;
return xfrm_input(skb, nexthdr, spi, encap_type);
}
return -1;
return -EINVAL;
drop:
kfree_skb(skb);
return 0;
}
/* This function assumes it is being called from dev_queue_xmit()
* and that skb is filled properly by that function.
*/
static netdev_tx_t vti_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
static int vti_rcv(struct sk_buff *skb)
{
struct ip_tunnel *tunnel = netdev_priv(dev);
struct iphdr *tiph = &tunnel->parms.iph;
u8 tos;
struct rtable *rt; /* Route to the other host */
struct net_device *tdev; /* Device to other host */
struct iphdr *old_iph = ip_hdr(skb);
__be32 dst = tiph->daddr;
struct flowi4 fl4;
int err;
XFRM_SPI_SKB_CB(skb)->family = AF_INET;
XFRM_SPI_SKB_CB(skb)->daddroff = offsetof(struct iphdr, daddr);
if (skb->protocol != htons(ETH_P_IP))
goto tx_error;
return vti_input(skb, ip_hdr(skb)->protocol, 0, 0);
}
tos = old_iph->tos;
static int vti_rcv_cb(struct sk_buff *skb, int err)
{
unsigned short family;
struct net_device *dev;
struct pcpu_sw_netstats *tstats;
struct xfrm_state *x;
struct ip_tunnel *tunnel = XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip4;
memset(&fl4, 0, sizeof(fl4));
flowi4_init_output(&fl4, tunnel->parms.link,
be32_to_cpu(tunnel->parms.o_key), RT_TOS(tos),
RT_SCOPE_UNIVERSE,
IPPROTO_IPIP, 0,
dst, tiph->saddr, 0, 0);
rt = ip_route_output_key(dev_net(dev), &fl4);
if (IS_ERR(rt)) {
dev->stats.tx_carrier_errors++;
goto tx_error_icmp;
if (!tunnel)
return 1;
dev = tunnel->dev;
if (err) {
dev->stats.rx_errors++;
dev->stats.rx_dropped++;
return 0;
}
x = xfrm_input_state(skb);
family = x->inner_mode->afinfo->family;
if (!xfrm_policy_check(NULL, XFRM_POLICY_IN, skb, family))
return -EPERM;
skb_scrub_packet(skb, !net_eq(tunnel->net, dev_net(skb->dev)));
skb->dev = dev;
tstats = this_cpu_ptr(dev->tstats);
u64_stats_update_begin(&tstats->syncp);
tstats->rx_packets++;
tstats->rx_bytes += skb->len;
u64_stats_update_end(&tstats->syncp);
return 0;
}
static bool vti_state_check(const struct xfrm_state *x, __be32 dst, __be32 src)
{
xfrm_address_t *daddr = (xfrm_address_t *)&dst;
xfrm_address_t *saddr = (xfrm_address_t *)&src;
/* if there is no transform then this tunnel is not functional.
* Or if the xfrm is not mode tunnel.
*/
if (!rt->dst.xfrm ||
rt->dst.xfrm->props.mode != XFRM_MODE_TUNNEL) {
if (!x || x->props.mode != XFRM_MODE_TUNNEL ||
x->props.family != AF_INET)
return false;
if (!dst)
return xfrm_addr_equal(saddr, &x->props.saddr, AF_INET);
if (!xfrm_state_addr_check(x, daddr, saddr, AF_INET))
return false;
return true;
}
static netdev_tx_t vti_xmit(struct sk_buff *skb, struct net_device *dev,
struct flowi *fl)
{
struct ip_tunnel *tunnel = netdev_priv(dev);
struct ip_tunnel_parm *parms = &tunnel->parms;
struct dst_entry *dst = skb_dst(skb);
struct net_device *tdev; /* Device to other host */
int err;
if (!dst) {
dev->stats.tx_carrier_errors++;
ip_rt_put(rt);
goto tx_error_icmp;
}
tdev = rt->dst.dev;
dst_hold(dst);
dst = xfrm_lookup(tunnel->net, dst, fl, NULL, 0);
if (IS_ERR(dst)) {
dev->stats.tx_carrier_errors++;
goto tx_error_icmp;
}
if (!vti_state_check(dst->xfrm, parms->iph.daddr, parms->iph.saddr)) {
dev->stats.tx_carrier_errors++;
dst_release(dst);
goto tx_error_icmp;
}
tdev = dst->dev;
if (tdev == dev) {
ip_rt_put(rt);
dst_release(dst);
dev->stats.collisions++;
goto tx_error;
}
@ -146,10 +188,8 @@ static netdev_tx_t vti_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
tunnel->err_count = 0;
}
memset(IPCB(skb), 0, sizeof(*IPCB(skb)));
skb_dst_drop(skb);
skb_dst_set(skb, &rt->dst);
nf_reset(skb);
skb_scrub_packet(skb, !net_eq(tunnel->net, dev_net(dev)));
skb_dst_set(skb, dst);
skb->dev = skb_dst(skb)->dev;
err = dst_output(skb);
@ -166,6 +206,95 @@ tx_error:
return NETDEV_TX_OK;
}
/* This function assumes it is being called from dev_queue_xmit()
* and that skb is filled properly by that function.
*/
static netdev_tx_t vti_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct ip_tunnel *tunnel = netdev_priv(dev);
struct flowi fl;
memset(&fl, 0, sizeof(fl));
skb->mark = be32_to_cpu(tunnel->parms.o_key);
switch (skb->protocol) {
case htons(ETH_P_IP):
xfrm_decode_session(skb, &fl, AF_INET);
memset(IPCB(skb), 0, sizeof(*IPCB(skb)));
break;
case htons(ETH_P_IPV6):
xfrm_decode_session(skb, &fl, AF_INET6);
memset(IP6CB(skb), 0, sizeof(*IP6CB(skb)));
break;
default:
dev->stats.tx_errors++;
dev_kfree_skb(skb);
return NETDEV_TX_OK;
}
return vti_xmit(skb, dev, &fl);
}
static int vti4_err(struct sk_buff *skb, u32 info)
{
__be32 spi;
struct xfrm_state *x;
struct ip_tunnel *tunnel;
struct ip_esp_hdr *esph;
struct ip_auth_hdr *ah ;
struct ip_comp_hdr *ipch;
struct net *net = dev_net(skb->dev);
const struct iphdr *iph = (const struct iphdr *)skb->data;
int protocol = iph->protocol;
struct ip_tunnel_net *itn = net_generic(net, vti_net_id);
tunnel = ip_tunnel_lookup(itn, skb->dev->ifindex, TUNNEL_NO_KEY,
iph->daddr, iph->saddr, 0);
if (!tunnel)
return -1;
switch (protocol) {
case IPPROTO_ESP:
esph = (struct ip_esp_hdr *)(skb->data+(iph->ihl<<2));
spi = esph->spi;
break;
case IPPROTO_AH:
ah = (struct ip_auth_hdr *)(skb->data+(iph->ihl<<2));
spi = ah->spi;
break;
case IPPROTO_COMP:
ipch = (struct ip_comp_hdr *)(skb->data+(iph->ihl<<2));
spi = htonl(ntohs(ipch->cpi));
break;
default:
return 0;
}
switch (icmp_hdr(skb)->type) {
case ICMP_DEST_UNREACH:
if (icmp_hdr(skb)->code != ICMP_FRAG_NEEDED)
return 0;
case ICMP_REDIRECT:
break;
default:
return 0;
}
x = xfrm_state_lookup(net, skb->mark, (const xfrm_address_t *)&iph->daddr,
spi, protocol, AF_INET);
if (!x)
return 0;
if (icmp_hdr(skb)->type == ICMP_DEST_UNREACH)
ipv4_update_pmtu(skb, net, info, 0, 0, protocol, 0);
else
ipv4_redirect(skb, net, 0, 0, protocol, 0);
xfrm_state_put(x);
return 0;
}
static int
vti_tunnel_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
{
@ -181,12 +310,13 @@ vti_tunnel_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
return -EINVAL;
}
p.i_flags |= VTI_ISVTI;
err = ip_tunnel_ioctl(dev, &p, cmd);
if (err)
return err;
if (cmd != SIOCDELTUNNEL) {
p.i_flags |= GRE_KEY | VTI_ISVTI;
p.i_flags |= GRE_KEY;
p.o_flags |= GRE_KEY;
}
@ -224,7 +354,6 @@ static int vti_tunnel_init(struct net_device *dev)
dev->flags = IFF_NOARP;
dev->iflink = 0;
dev->addr_len = 4;
dev->features |= NETIF_F_NETNS_LOCAL;
dev->features |= NETIF_F_LLTX;
dev->priv_flags &= ~IFF_XMIT_DST_RELEASE;
@ -241,9 +370,28 @@ static void __net_init vti_fb_tunnel_init(struct net_device *dev)
iph->ihl = 5;
}
static struct xfrm_tunnel_notifier vti_handler __read_mostly = {
static struct xfrm4_protocol vti_esp4_protocol __read_mostly = {
.handler = vti_rcv,
.priority = 1,
.input_handler = vti_input,
.cb_handler = vti_rcv_cb,
.err_handler = vti4_err,
.priority = 100,
};
static struct xfrm4_protocol vti_ah4_protocol __read_mostly = {
.handler = vti_rcv,
.input_handler = vti_input,
.cb_handler = vti_rcv_cb,
.err_handler = vti4_err,
.priority = 100,
};
static struct xfrm4_protocol vti_ipcomp4_protocol __read_mostly = {
.handler = vti_rcv,
.input_handler = vti_input,
.cb_handler = vti_rcv_cb,
.err_handler = vti4_err,
.priority = 100,
};
static int __net_init vti_init_net(struct net *net)
@ -287,6 +435,8 @@ static void vti_netlink_parms(struct nlattr *data[],
if (!data)
return;
parms->i_flags = VTI_ISVTI;
if (data[IFLA_VTI_LINK])
parms->link = nla_get_u32(data[IFLA_VTI_LINK]);
@ -382,10 +532,31 @@ static int __init vti_init(void)
err = register_pernet_device(&vti_net_ops);
if (err < 0)
return err;
err = xfrm4_mode_tunnel_input_register(&vti_handler);
err = xfrm4_protocol_register(&vti_esp4_protocol, IPPROTO_ESP);
if (err < 0) {
unregister_pernet_device(&vti_net_ops);
pr_info("vti init: can't register tunnel\n");
return err;
}
err = xfrm4_protocol_register(&vti_ah4_protocol, IPPROTO_AH);
if (err < 0) {
xfrm4_protocol_deregister(&vti_esp4_protocol, IPPROTO_ESP);
unregister_pernet_device(&vti_net_ops);
pr_info("vti init: can't register tunnel\n");
return err;
}
err = xfrm4_protocol_register(&vti_ipcomp4_protocol, IPPROTO_COMP);
if (err < 0) {
xfrm4_protocol_deregister(&vti_ah4_protocol, IPPROTO_AH);
xfrm4_protocol_deregister(&vti_esp4_protocol, IPPROTO_ESP);
unregister_pernet_device(&vti_net_ops);
pr_info("vti init: can't register tunnel\n");
return err;
}
err = rtnl_link_register(&vti_link_ops);
@ -395,7 +566,9 @@ static int __init vti_init(void)
return err;
rtnl_link_failed:
xfrm4_mode_tunnel_input_deregister(&vti_handler);
xfrm4_protocol_deregister(&vti_ipcomp4_protocol, IPPROTO_COMP);
xfrm4_protocol_deregister(&vti_ah4_protocol, IPPROTO_AH);
xfrm4_protocol_deregister(&vti_esp4_protocol, IPPROTO_ESP);
unregister_pernet_device(&vti_net_ops);
return err;
}
@ -403,8 +576,13 @@ rtnl_link_failed:
static void __exit vti_fini(void)
{
rtnl_link_unregister(&vti_link_ops);
if (xfrm4_mode_tunnel_input_deregister(&vti_handler))
if (xfrm4_protocol_deregister(&vti_ipcomp4_protocol, IPPROTO_COMP))
pr_info("vti close: can't deregister tunnel\n");
if (xfrm4_protocol_deregister(&vti_ah4_protocol, IPPROTO_AH))
pr_info("vti close: can't deregister tunnel\n");
if (xfrm4_protocol_deregister(&vti_esp4_protocol, IPPROTO_ESP))
pr_info("vti close: can't deregister tunnel\n");
unregister_pernet_device(&vti_net_ops);
}

View File

@ -23,7 +23,7 @@
#include <net/protocol.h>
#include <net/sock.h>
static void ipcomp4_err(struct sk_buff *skb, u32 info)
static int ipcomp4_err(struct sk_buff *skb, u32 info)
{
struct net *net = dev_net(skb->dev);
__be32 spi;
@ -34,24 +34,26 @@ static void ipcomp4_err(struct sk_buff *skb, u32 info)
switch (icmp_hdr(skb)->type) {
case ICMP_DEST_UNREACH:
if (icmp_hdr(skb)->code != ICMP_FRAG_NEEDED)
return;
return 0;
case ICMP_REDIRECT:
break;
default:
return;
return 0;
}
spi = htonl(ntohs(ipch->cpi));
x = xfrm_state_lookup(net, skb->mark, (const xfrm_address_t *)&iph->daddr,
spi, IPPROTO_COMP, AF_INET);
if (!x)
return;
return 0;
if (icmp_hdr(skb)->type == ICMP_DEST_UNREACH)
ipv4_update_pmtu(skb, net, info, 0, 0, IPPROTO_COMP, 0);
else
ipv4_redirect(skb, net, 0, 0, IPPROTO_COMP, 0);
xfrm_state_put(x);
return 0;
}
/* We always hold one tunnel user reference to indicate a tunnel */
@ -147,6 +149,11 @@ out:
return err;
}
static int ipcomp4_rcv_cb(struct sk_buff *skb, int err)
{
return 0;
}
static const struct xfrm_type ipcomp_type = {
.description = "IPCOMP4",
.owner = THIS_MODULE,
@ -157,11 +164,12 @@ static const struct xfrm_type ipcomp_type = {
.output = ipcomp_output
};
static const struct net_protocol ipcomp4_protocol = {
static struct xfrm4_protocol ipcomp4_protocol = {
.handler = xfrm4_rcv,
.input_handler = xfrm_input,
.cb_handler = ipcomp4_rcv_cb,
.err_handler = ipcomp4_err,
.no_policy = 1,
.netns_ok = 1,
.priority = 0,
};
static int __init ipcomp4_init(void)
@ -170,7 +178,7 @@ static int __init ipcomp4_init(void)
pr_info("%s: can't add xfrm type\n", __func__);
return -EAGAIN;
}
if (inet_add_protocol(&ipcomp4_protocol, IPPROTO_COMP) < 0) {
if (xfrm4_protocol_register(&ipcomp4_protocol, IPPROTO_COMP) < 0) {
pr_info("%s: can't add protocol\n", __func__);
xfrm_unregister_type(&ipcomp_type, AF_INET);
return -EAGAIN;
@ -180,7 +188,7 @@ static int __init ipcomp4_init(void)
static void __exit ipcomp4_fini(void)
{
if (inet_del_protocol(&ipcomp4_protocol, IPPROTO_COMP) < 0)
if (xfrm4_protocol_deregister(&ipcomp4_protocol, IPPROTO_COMP) < 0)
pr_info("%s: can't remove protocol\n", __func__);
if (xfrm_unregister_type(&ipcomp_type, AF_INET) < 0)
pr_info("%s: can't remove xfrm type\n", __func__);

View File

@ -37,15 +37,6 @@ drop:
return NET_RX_DROP;
}
int xfrm4_rcv_encap(struct sk_buff *skb, int nexthdr, __be32 spi,
int encap_type)
{
XFRM_SPI_SKB_CB(skb)->family = AF_INET;
XFRM_SPI_SKB_CB(skb)->daddroff = offsetof(struct iphdr, daddr);
return xfrm_input(skb, nexthdr, spi, encap_type);
}
EXPORT_SYMBOL(xfrm4_rcv_encap);
int xfrm4_transport_finish(struct sk_buff *skb, int async)
{
struct iphdr *iph = ip_hdr(skb);

View File

@ -15,65 +15,6 @@
#include <net/ip.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(xfrm4_mode_tunnel_input_mutex);
int xfrm4_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(&xfrm4_mode_tunnel_input_mutex);
for (pprev = &rcv_notify_handlers;
(t = rcu_dereference_protected(*pprev,
lockdep_is_held(&xfrm4_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(&xfrm4_mode_tunnel_input_mutex);
return ret;
}
EXPORT_SYMBOL_GPL(xfrm4_mode_tunnel_input_register);
int xfrm4_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(&xfrm4_mode_tunnel_input_mutex);
for (pprev = &rcv_notify_handlers;
(t = rcu_dereference_protected(*pprev,
lockdep_is_held(&xfrm4_mode_tunnel_input_mutex))) != NULL;
pprev = &t->next) {
if (t == handler) {
*pprev = handler->next;
ret = 0;
break;
}
}
mutex_unlock(&xfrm4_mode_tunnel_input_mutex);
synchronize_net();
return ret;
}
EXPORT_SYMBOL_GPL(xfrm4_mode_tunnel_input_deregister);
static inline void ipip_ecn_decapsulate(struct sk_buff *skb)
{
struct iphdr *inner_iph = ipip_hdr(skb);
@ -127,14 +68,8 @@ static int xfrm4_mode_tunnel_output(struct xfrm_state *x, struct sk_buff *skb)
return 0;
}
#define for_each_input_rcu(head, handler) \
for (handler = rcu_dereference(head); \
handler != NULL; \
handler = rcu_dereference(handler->next))
static int xfrm4_mode_tunnel_input(struct xfrm_state *x, struct sk_buff *skb)
{
struct xfrm_tunnel_notifier *handler;
int err = -EINVAL;
if (XFRM_MODE_SKB_CB(skb)->protocol != IPPROTO_IPIP)
@ -143,9 +78,6 @@ static int xfrm4_mode_tunnel_input(struct xfrm_state *x, struct sk_buff *skb)
if (!pskb_may_pull(skb, sizeof(struct iphdr)))
goto out;
for_each_input_rcu(rcv_notify_handlers, handler)
handler->handler(skb);
err = skb_unclone(skb, GFP_ATOMIC);
if (err)
goto out;

275
net/ipv4/xfrm4_protocol.c Normal file
View File

@ -0,0 +1,275 @@
/* xfrm4_protocol.c - Generic xfrm protocol multiplexer.
*
* Copyright (C) 2013 secunet Security Networks AG
*
* Author:
* Steffen Klassert <steffen.klassert@secunet.com>
*
* Based on:
* net/ipv4/tunnel4.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 <net/icmp.h>
#include <net/ip.h>
#include <net/protocol.h>
#include <net/xfrm.h>
static struct xfrm4_protocol __rcu *esp4_handlers __read_mostly;
static struct xfrm4_protocol __rcu *ah4_handlers __read_mostly;
static struct xfrm4_protocol __rcu *ipcomp4_handlers __read_mostly;
static DEFINE_MUTEX(xfrm4_protocol_mutex);
static inline struct xfrm4_protocol __rcu **proto_handlers(u8 protocol)
{
switch (protocol) {
case IPPROTO_ESP:
return &esp4_handlers;
case IPPROTO_AH:
return &ah4_handlers;
case IPPROTO_COMP:
return &ipcomp4_handlers;
}
return NULL;
}
#define for_each_protocol_rcu(head, handler) \
for (handler = rcu_dereference(head); \
handler != NULL; \
handler = rcu_dereference(handler->next)) \
int xfrm4_rcv_cb(struct sk_buff *skb, u8 protocol, int err)
{
int ret;
struct xfrm4_protocol *handler;
for_each_protocol_rcu(*proto_handlers(protocol), handler)
if ((ret = handler->cb_handler(skb, err)) <= 0)
return ret;
return 0;
}
EXPORT_SYMBOL(xfrm4_rcv_cb);
int xfrm4_rcv_encap(struct sk_buff *skb, int nexthdr, __be32 spi,
int encap_type)
{
int ret;
struct xfrm4_protocol *handler;
XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip4 = NULL;
XFRM_SPI_SKB_CB(skb)->family = AF_INET;
XFRM_SPI_SKB_CB(skb)->daddroff = offsetof(struct iphdr, daddr);
for_each_protocol_rcu(*proto_handlers(nexthdr), handler)
if ((ret = handler->input_handler(skb, nexthdr, spi, encap_type)) != -EINVAL)
return ret;
icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0);
kfree_skb(skb);
return 0;
}
EXPORT_SYMBOL(xfrm4_rcv_encap);
static int xfrm4_esp_rcv(struct sk_buff *skb)
{
int ret;
struct xfrm4_protocol *handler;
XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip4 = NULL;
for_each_protocol_rcu(esp4_handlers, handler)
if ((ret = handler->handler(skb)) != -EINVAL)
return ret;
icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0);
kfree_skb(skb);
return 0;
}
static void xfrm4_esp_err(struct sk_buff *skb, u32 info)
{
struct xfrm4_protocol *handler;
for_each_protocol_rcu(esp4_handlers, handler)
if (!handler->err_handler(skb, info))
break;
}
static int xfrm4_ah_rcv(struct sk_buff *skb)
{
int ret;
struct xfrm4_protocol *handler;
XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip4 = NULL;
for_each_protocol_rcu(ah4_handlers, handler)
if ((ret = handler->handler(skb)) != -EINVAL)
return ret;;
icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0);
kfree_skb(skb);
return 0;
}
static void xfrm4_ah_err(struct sk_buff *skb, u32 info)
{
struct xfrm4_protocol *handler;
for_each_protocol_rcu(ah4_handlers, handler)
if (!handler->err_handler(skb, info))
break;
}
static int xfrm4_ipcomp_rcv(struct sk_buff *skb)
{
int ret;
struct xfrm4_protocol *handler;
XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip4 = NULL;
for_each_protocol_rcu(ipcomp4_handlers, handler)
if ((ret = handler->handler(skb)) != -EINVAL)
return ret;
icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0);
kfree_skb(skb);
return 0;
}
static void xfrm4_ipcomp_err(struct sk_buff *skb, u32 info)
{
struct xfrm4_protocol *handler;
for_each_protocol_rcu(ipcomp4_handlers, handler)
if (!handler->err_handler(skb, info))
break;
}
static const struct net_protocol esp4_protocol = {
.handler = xfrm4_esp_rcv,
.err_handler = xfrm4_esp_err,
.no_policy = 1,
.netns_ok = 1,
};
static const struct net_protocol ah4_protocol = {
.handler = xfrm4_ah_rcv,
.err_handler = xfrm4_ah_err,
.no_policy = 1,
.netns_ok = 1,
};
static const struct net_protocol ipcomp4_protocol = {
.handler = xfrm4_ipcomp_rcv,
.err_handler = xfrm4_ipcomp_err,
.no_policy = 1,
.netns_ok = 1,
};
static inline const struct net_protocol *netproto(unsigned char protocol)
{
switch (protocol) {
case IPPROTO_ESP:
return &esp4_protocol;
case IPPROTO_AH:
return &ah4_protocol;
case IPPROTO_COMP:
return &ipcomp4_protocol;
}
return NULL;
}
int xfrm4_protocol_register(struct xfrm4_protocol *handler,
unsigned char protocol)
{
struct xfrm4_protocol __rcu **pprev;
struct xfrm4_protocol *t;
bool add_netproto = false;
int ret = -EEXIST;
int priority = handler->priority;
mutex_lock(&xfrm4_protocol_mutex);
if (!rcu_dereference_protected(*proto_handlers(protocol),
lockdep_is_held(&xfrm4_protocol_mutex)))
add_netproto = true;
for (pprev = proto_handlers(protocol);
(t = rcu_dereference_protected(*pprev,
lockdep_is_held(&xfrm4_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(&xfrm4_protocol_mutex);
if (add_netproto) {
if (inet_add_protocol(netproto(protocol), protocol)) {
pr_err("%s: can't add protocol\n", __func__);
ret = -EAGAIN;
}
}
return ret;
}
EXPORT_SYMBOL(xfrm4_protocol_register);
int xfrm4_protocol_deregister(struct xfrm4_protocol *handler,
unsigned char protocol)
{
struct xfrm4_protocol __rcu **pprev;
struct xfrm4_protocol *t;
int ret = -ENOENT;
mutex_lock(&xfrm4_protocol_mutex);
for (pprev = proto_handlers(protocol);
(t = rcu_dereference_protected(*pprev,
lockdep_is_held(&xfrm4_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(&xfrm4_protocol_mutex))) {
if (inet_del_protocol(netproto(protocol), protocol) < 0) {
pr_err("%s: can't remove protocol\n", __func__);
ret = -EAGAIN;
}
}
mutex_unlock(&xfrm4_protocol_mutex);
synchronize_net();
return ret;
}
EXPORT_SYMBOL(xfrm4_protocol_deregister);

View File

@ -108,7 +108,7 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)
int err;
__be32 seq;
__be32 seq_hi;
struct xfrm_state *x;
struct xfrm_state *x = NULL;
xfrm_address_t *daddr;
struct xfrm_mode *inner_mode;
unsigned int family;
@ -120,9 +120,14 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)
async = 1;
x = xfrm_input_state(skb);
seq = XFRM_SKB_CB(skb)->seq.input.low;
family = x->outer_mode->afinfo->family;
goto resume;
}
daddr = (xfrm_address_t *)(skb_network_header(skb) +
XFRM_SPI_SKB_CB(skb)->daddroff);
family = XFRM_SPI_SKB_CB(skb)->family;
/* Allocate new secpath or COW existing one. */
if (!skb->sp || atomic_read(&skb->sp->refcnt) != 1) {
struct sec_path *sp;
@ -137,10 +142,6 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)
skb->sp = sp;
}
daddr = (xfrm_address_t *)(skb_network_header(skb) +
XFRM_SPI_SKB_CB(skb)->daddroff);
family = XFRM_SPI_SKB_CB(skb)->family;
seq = 0;
if (!spi && (err = xfrm_parse_spi(skb, nexthdr, &spi, &seq)) != 0) {
XFRM_INC_STATS(net, LINUX_MIB_XFRMINHDRERROR);
@ -162,6 +163,11 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)
skb->sp->xvec[skb->sp->len++] = x;
if (xfrm_tunnel_check(skb, x, family)) {
XFRM_INC_STATS(net, LINUX_MIB_XFRMINSTATEMODEERROR);
goto drop;
}
spin_lock(&x->lock);
if (unlikely(x->km.state == XFRM_STATE_ACQ)) {
XFRM_INC_STATS(net, LINUX_MIB_XFRMACQUIREERROR);
@ -201,7 +207,6 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)
if (nexthdr == -EINPROGRESS)
return 0;
resume:
spin_lock(&x->lock);
if (nexthdr <= 0) {
@ -263,6 +268,10 @@ resume:
}
} while (!err);
err = xfrm_rcv_cb(skb, family, x->type->proto, 0);
if (err)
goto drop;
nf_reset(skb);
if (decaps) {
@ -276,6 +285,7 @@ resume:
drop_unlock:
spin_unlock(&x->lock);
drop:
xfrm_rcv_cb(skb, family, x && x->type ? x->type->proto : nexthdr, -1);
kfree_skb(skb);
return 0;
}