Commit 3dbf6d67 authored by David S. Miller's avatar David S. Miller
Browse files

Merge tag 'ipsec-next-2024-03-06' of...

Merge tag 'ipsec-next-2024-03-06' of git://git.kernel.org/pub/scm/linux/kernel/git/klassert/ipsec-next



Steffen Klassert says:

====================
1) Introduce forwarding of ICMP Error messages. That is specified
   in RFC 4301 but was never implemented. From Antony Antony.

2) Use KMEM_CACHE instead of kmem_cache_create in xfrm6_tunnel_init()
   and xfrm_policy_init(). From Kunwu Chan.

3) Do not allocate stats in the xfrm interface driver, this can be done
   on net core now. From Breno Leitao.
====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 7cf497e5 aceb147b
Loading
Loading
Loading
Loading
+1 −4
Original line number Diff line number Diff line
@@ -355,10 +355,7 @@ static int __init xfrm6_tunnel_init(void)
{
	int rv;

	xfrm6_tunnel_spi_kmem = kmem_cache_create("xfrm6_tunnel_spi",
						  sizeof(struct xfrm6_tunnel_spi),
						  0, SLAB_HWCACHE_ALIGN,
						  NULL);
	xfrm6_tunnel_spi_kmem = KMEM_CACHE(xfrm6_tunnel_spi, SLAB_HWCACHE_ALIGN);
	if (!xfrm6_tunnel_spi_kmem)
		return -ENOMEM;
	rv = register_pernet_subsys(&xfrm6_tunnel_net_ops);
+2 −8
Original line number Diff line number Diff line
@@ -240,7 +240,6 @@ static void xfrmi_dev_free(struct net_device *dev)
	struct xfrm_if *xi = netdev_priv(dev);

	gro_cells_destroy(&xi->gro_cells);
	free_percpu(dev->tstats);
}

static int xfrmi_create(struct net_device *dev)
@@ -749,6 +748,7 @@ static void xfrmi_dev_setup(struct net_device *dev)
	dev->flags 		= IFF_NOARP;
	dev->needs_free_netdev	= true;
	dev->priv_destructor	= xfrmi_dev_free;
	dev->pcpu_stat_type	= NETDEV_PCPU_STAT_TSTATS;
	netif_keep_dst(dev);

	eth_broadcast_addr(dev->broadcast);
@@ -765,15 +765,9 @@ static int xfrmi_dev_init(struct net_device *dev)
	struct net_device *phydev = __dev_get_by_index(xi->net, xi->p.link);
	int err;

	dev->tstats = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats);
	if (!dev->tstats)
		return -ENOMEM;

	err = gro_cells_init(&xi->gro_cells, dev);
	if (err) {
		free_percpu(dev->tstats);
	if (err)
		return err;
	}

	dev->features |= NETIF_F_LLTX;
	dev->features |= XFRMI_FEATURES;
+141 −6
Original line number Diff line number Diff line
@@ -29,6 +29,7 @@
#include <linux/audit.h>
#include <linux/rhashtable.h>
#include <linux/if_tunnel.h>
#include <linux/icmp.h>
#include <net/dst.h>
#include <net/flow.h>
#include <net/inet_ecn.h>
@@ -3505,6 +3506,128 @@ static inline int secpath_has_nontransport(const struct sec_path *sp, int k, int
	return 0;
}

static bool icmp_err_packet(const struct flowi *fl, unsigned short family)
{
	const struct flowi4 *fl4 = &fl->u.ip4;

	if (family == AF_INET &&
	    fl4->flowi4_proto == IPPROTO_ICMP &&
	    (fl4->fl4_icmp_type == ICMP_DEST_UNREACH ||
	     fl4->fl4_icmp_type == ICMP_TIME_EXCEEDED))
		return true;

#if IS_ENABLED(CONFIG_IPV6)
	if (family == AF_INET6) {
		const struct flowi6 *fl6 = &fl->u.ip6;

		if (fl6->flowi6_proto == IPPROTO_ICMPV6 &&
		    (fl6->fl6_icmp_type == ICMPV6_DEST_UNREACH ||
		    fl6->fl6_icmp_type == ICMPV6_PKT_TOOBIG ||
		    fl6->fl6_icmp_type == ICMPV6_TIME_EXCEED))
			return true;
	}
#endif
	return false;
}

static bool xfrm_icmp_flow_decode(struct sk_buff *skb, unsigned short family,
				  const struct flowi *fl, struct flowi *fl1)
{
	bool ret = true;
	struct sk_buff *newskb = skb_clone(skb, GFP_ATOMIC);
	int hl = family == AF_INET ? (sizeof(struct iphdr) +  sizeof(struct icmphdr)) :
		 (sizeof(struct ipv6hdr) + sizeof(struct icmp6hdr));

	if (!newskb)
		return true;

	if (!pskb_pull(newskb, hl))
		goto out;

	skb_reset_network_header(newskb);

	if (xfrm_decode_session_reverse(dev_net(skb->dev), newskb, fl1, family) < 0)
		goto out;

	fl1->flowi_oif = fl->flowi_oif;
	fl1->flowi_mark = fl->flowi_mark;
	fl1->flowi_tos = fl->flowi_tos;
	nf_nat_decode_session(newskb, fl1, family);
	ret = false;

out:
	consume_skb(newskb);
	return ret;
}

static bool xfrm_selector_inner_icmp_match(struct sk_buff *skb, unsigned short family,
					   const struct xfrm_selector *sel,
					   const struct flowi *fl)
{
	bool ret = false;

	if (icmp_err_packet(fl, family)) {
		struct flowi fl1;

		if (xfrm_icmp_flow_decode(skb, family, fl, &fl1))
			return ret;

		ret = xfrm_selector_match(sel, &fl1, family);
	}

	return ret;
}

static inline struct
xfrm_policy *xfrm_in_fwd_icmp(struct sk_buff *skb,
			      const struct flowi *fl, unsigned short family,
			      u32 if_id)
{
	struct xfrm_policy *pol = NULL;

	if (icmp_err_packet(fl, family)) {
		struct flowi fl1;
		struct net *net = dev_net(skb->dev);

		if (xfrm_icmp_flow_decode(skb, family, fl, &fl1))
			return pol;

		pol = xfrm_policy_lookup(net, &fl1, family, XFRM_POLICY_FWD, if_id);
	}

	return pol;
}

static inline struct
dst_entry *xfrm_out_fwd_icmp(struct sk_buff *skb, struct flowi *fl,
			     unsigned short family, struct dst_entry *dst)
{
	if (icmp_err_packet(fl, family)) {
		struct net *net = dev_net(skb->dev);
		struct dst_entry *dst2;
		struct flowi fl1;

		if (xfrm_icmp_flow_decode(skb, family, fl, &fl1))
			return dst;

		dst_hold(dst);

		dst2 = xfrm_lookup(net, dst, &fl1, NULL, (XFRM_LOOKUP_QUEUE | XFRM_LOOKUP_ICMP));

		if (IS_ERR(dst2))
			return dst;

		if (dst2->xfrm) {
			dst_release(dst);
			dst = dst2;
		} else {
			dst_release(dst2);
		}
	}

	return dst;
}

int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb,
			unsigned short family)
{
@@ -3551,12 +3674,20 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb,

		for (i = sp->len - 1; i >= 0; i--) {
			struct xfrm_state *x = sp->xvec[i];
			int ret = 0;

			if (!xfrm_selector_match(&x->sel, &fl, family)) {
				ret = 1;
				if (x->props.flags & XFRM_STATE_ICMP &&
				    xfrm_selector_inner_icmp_match(skb, family, &x->sel, &fl))
					ret = 0;
				if (ret) {
					XFRM_INC_STATS(net, LINUX_MIB_XFRMINSTATEMISMATCH);
					return 0;
				}
			}
		}
	}

	pol = NULL;
	sk = sk_to_full_sk(sk);
@@ -3576,6 +3707,9 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb,
		return 0;
	}

	if (!pol && dir == XFRM_POLICY_FWD)
		pol = xfrm_in_fwd_icmp(skb, &fl, family, if_id);

	if (!pol) {
		if (net->xfrm.policy_default[dir] == XFRM_USERPOLICY_BLOCK) {
			XFRM_INC_STATS(net, LINUX_MIB_XFRMINNOPOLS);
@@ -3709,6 +3843,10 @@ int __xfrm_route_forward(struct sk_buff *skb, unsigned short family)
		res = 0;
		dst = NULL;
	}

	if (dst && !dst->xfrm)
		dst = xfrm_out_fwd_icmp(skb, &fl, family, dst);

	skb_dst_set(skb, dst);
	return res;
}
@@ -4027,10 +4165,7 @@ static int __net_init xfrm_policy_init(struct net *net)
	int dir, err;

	if (net_eq(net, &init_net)) {
		xfrm_dst_cache = kmem_cache_create("xfrm_dst_cache",
					   sizeof(struct xfrm_dst),
					   0, SLAB_HWCACHE_ALIGN|SLAB_PANIC,
					   NULL);
		xfrm_dst_cache = KMEM_CACHE(xfrm_dst, SLAB_HWCACHE_ALIGN | SLAB_PANIC);
		err = rhashtable_init(&xfrm_policy_inexact_table,
				      &xfrm_pol_inexact_params);
		BUG_ON(err);