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

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
......@@ -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);
......
......@@ -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;
......
......@@ -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,9 +3674,17 @@ 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)) {
XFRM_INC_STATS(net, LINUX_MIB_XFRMINSTATEMISMATCH);
return 0;
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;
}
}
}
}
......@@ -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);
......
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