Commit 62fdd170 authored by Jakub Kicinski's avatar Jakub Kicinski

Merge tag 'ipsec-next-2024-07-13' of...

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

Steffen Klassert says:

====================
pull request (net-next): ipsec-next 2024-07-13

1) Support sending NAT keepalives in ESP in UDP states.
   Userspace IKE daemon had to do this before, but the
   kernel can better keep track of it.
   From Eyal Birger.

2) Support IPsec crypto offload for IPv6 ESP and IPv4 UDP-encapsulated
   ESP data paths. Currently, IPsec crypto offload is enabled for GRO
   code path only. This patchset support UDP encapsulation for the non
   GRO path. From Mike Yu.

* tag 'ipsec-next-2024-07-13' of git://git.kernel.org/pub/scm/linux/kernel/git/klassert/ipsec-next:
  xfrm: Support crypto offload for outbound IPv4 UDP-encapsulated ESP packet
  xfrm: Support crypto offload for inbound IPv4 UDP-encapsulated ESP packet
  xfrm: Allow UDP encapsulation in crypto offload control path
  xfrm: Support crypto offload for inbound IPv6 ESP packets not in GRO path
  xfrm: support sending NAT keepalives in ESP in UDP states
====================

Link: https://patch.msgid.link/20240713102416.3272997-1-steffen.klassert@secunet.comSigned-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents ecb1e1dc d5b60c65
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include <net/flow.h> #include <net/flow.h>
#include <net/neighbour.h> #include <net/neighbour.h>
#include <net/sock.h> #include <net/sock.h>
#include <net/ipv6.h>
/* structs from net/ip6_fib.h */ /* structs from net/ip6_fib.h */
struct fib6_info; struct fib6_info;
...@@ -72,6 +73,8 @@ struct ipv6_stub { ...@@ -72,6 +73,8 @@ struct ipv6_stub {
int (*output)(struct net *, struct sock *, struct sk_buff *)); int (*output)(struct net *, struct sock *, struct sk_buff *));
struct net_device *(*ipv6_dev_find)(struct net *net, const struct in6_addr *addr, struct net_device *(*ipv6_dev_find)(struct net *net, const struct in6_addr *addr,
struct net_device *dev); struct net_device *dev);
int (*ip6_xmit)(const struct sock *sk, struct sk_buff *skb, struct flowi6 *fl6,
__u32 mark, struct ipv6_txoptions *opt, int tclass, u32 priority);
}; };
extern const struct ipv6_stub *ipv6_stub __read_mostly; extern const struct ipv6_stub *ipv6_stub __read_mostly;
......
...@@ -83,6 +83,7 @@ struct netns_xfrm { ...@@ -83,6 +83,7 @@ struct netns_xfrm {
spinlock_t xfrm_policy_lock; spinlock_t xfrm_policy_lock;
struct mutex xfrm_cfg_mutex; struct mutex xfrm_cfg_mutex;
struct delayed_work nat_keepalive_work;
}; };
#endif #endif
...@@ -229,6 +229,10 @@ struct xfrm_state { ...@@ -229,6 +229,10 @@ struct xfrm_state {
struct xfrm_encap_tmpl *encap; struct xfrm_encap_tmpl *encap;
struct sock __rcu *encap_sk; struct sock __rcu *encap_sk;
/* NAT keepalive */
u32 nat_keepalive_interval; /* seconds */
time64_t nat_keepalive_expiration;
/* Data for care-of address */ /* Data for care-of address */
xfrm_address_t *coaddr; xfrm_address_t *coaddr;
...@@ -2203,4 +2207,10 @@ static inline int register_xfrm_state_bpf(void) ...@@ -2203,4 +2207,10 @@ static inline int register_xfrm_state_bpf(void)
} }
#endif #endif
int xfrm_nat_keepalive_init(unsigned short family);
void xfrm_nat_keepalive_fini(unsigned short family);
int xfrm_nat_keepalive_net_init(struct net *net);
int xfrm_nat_keepalive_net_fini(struct net *net);
void xfrm_nat_keepalive_state_updated(struct xfrm_state *x);
#endif /* _NET_XFRM_H */ #endif /* _NET_XFRM_H */
...@@ -321,6 +321,7 @@ enum xfrm_attr_type_t { ...@@ -321,6 +321,7 @@ enum xfrm_attr_type_t {
XFRMA_IF_ID, /* __u32 */ XFRMA_IF_ID, /* __u32 */
XFRMA_MTIMER_THRESH, /* __u32 in seconds for input SA */ XFRMA_MTIMER_THRESH, /* __u32 in seconds for input SA */
XFRMA_SA_DIR, /* __u8 */ XFRMA_SA_DIR, /* __u8 */
XFRMA_NAT_KEEPALIVE_INTERVAL, /* __u32 in seconds for NAT keepalive */
__XFRMA_MAX __XFRMA_MAX
#define XFRMA_OUTPUT_MARK XFRMA_SET_MARK /* Compatibility */ #define XFRMA_OUTPUT_MARK XFRMA_SET_MARK /* Compatibility */
......
...@@ -349,6 +349,7 @@ static struct ip_esp_hdr *esp_output_udp_encap(struct sk_buff *skb, ...@@ -349,6 +349,7 @@ static struct ip_esp_hdr *esp_output_udp_encap(struct sk_buff *skb,
{ {
struct udphdr *uh; struct udphdr *uh;
unsigned int len; unsigned int len;
struct xfrm_offload *xo = xfrm_offload(skb);
len = skb->len + esp->tailen - skb_transport_offset(skb); len = skb->len + esp->tailen - skb_transport_offset(skb);
if (len + sizeof(struct iphdr) > IP_MAX_MTU) if (len + sizeof(struct iphdr) > IP_MAX_MTU)
...@@ -360,7 +361,12 @@ static struct ip_esp_hdr *esp_output_udp_encap(struct sk_buff *skb, ...@@ -360,7 +361,12 @@ static struct ip_esp_hdr *esp_output_udp_encap(struct sk_buff *skb,
uh->len = htons(len); uh->len = htons(len);
uh->check = 0; uh->check = 0;
*skb_mac_header(skb) = IPPROTO_UDP; /* For IPv4 ESP with UDP encapsulation, if xo is not null, the skb is in the crypto offload
* data path, which means that esp_output_udp_encap is called outside of the XFRM stack.
* In this case, the mac header doesn't point to the IPv4 protocol field, so don't set it.
*/
if (!xo || encap_type != UDP_ENCAP_ESPINUDP)
*skb_mac_header(skb) = IPPROTO_UDP;
return (struct ip_esp_hdr *)(uh + 1); return (struct ip_esp_hdr *)(uh + 1);
} }
......
...@@ -264,6 +264,7 @@ static int esp_xmit(struct xfrm_state *x, struct sk_buff *skb, netdev_features_ ...@@ -264,6 +264,7 @@ static int esp_xmit(struct xfrm_state *x, struct sk_buff *skb, netdev_features_
struct esp_info esp; struct esp_info esp;
bool hw_offload = true; bool hw_offload = true;
__u32 seq; __u32 seq;
int encap_type = 0;
esp.inplace = true; esp.inplace = true;
...@@ -296,8 +297,10 @@ static int esp_xmit(struct xfrm_state *x, struct sk_buff *skb, netdev_features_ ...@@ -296,8 +297,10 @@ static int esp_xmit(struct xfrm_state *x, struct sk_buff *skb, netdev_features_
esp.esph = ip_esp_hdr(skb); esp.esph = ip_esp_hdr(skb);
if (x->encap)
encap_type = x->encap->encap_type;
if (!hw_offload || !skb_is_gso(skb)) { if (!hw_offload || !skb_is_gso(skb) || (hw_offload && encap_type == UDP_ENCAP_ESPINUDP)) {
esp.nfrags = esp_output_head(x, skb, &esp); esp.nfrags = esp_output_head(x, skb, &esp);
if (esp.nfrags < 0) if (esp.nfrags < 0)
return esp.nfrags; return esp.nfrags;
...@@ -324,6 +327,18 @@ static int esp_xmit(struct xfrm_state *x, struct sk_buff *skb, netdev_features_ ...@@ -324,6 +327,18 @@ static int esp_xmit(struct xfrm_state *x, struct sk_buff *skb, netdev_features_
esp.seqno = cpu_to_be64(seq + ((u64)xo->seq.hi << 32)); esp.seqno = cpu_to_be64(seq + ((u64)xo->seq.hi << 32));
if (hw_offload && encap_type == UDP_ENCAP_ESPINUDP) {
/* In the XFRM stack, the encapsulation protocol is set to iphdr->protocol by
* setting *skb_mac_header(skb) (see esp_output_udp_encap()) where skb->mac_header
* points to iphdr->protocol (see xfrm4_tunnel_encap_add()).
* However, in esp_xmit(), skb->mac_header doesn't point to iphdr->protocol.
* Therefore, the protocol field needs to be corrected.
*/
ip_hdr(skb)->protocol = IPPROTO_UDP;
esph->seq_no = htonl(seq);
}
ip_hdr(skb)->tot_len = htons(skb->len); ip_hdr(skb)->tot_len = htons(skb->len);
ip_send_check(ip_hdr(skb)); ip_send_check(ip_hdr(skb));
......
...@@ -1060,6 +1060,7 @@ static const struct ipv6_stub ipv6_stub_impl = { ...@@ -1060,6 +1060,7 @@ static const struct ipv6_stub ipv6_stub_impl = {
.nd_tbl = &nd_tbl, .nd_tbl = &nd_tbl,
.ipv6_fragment = ip6_fragment, .ipv6_fragment = ip6_fragment,
.ipv6_dev_find = ipv6_dev_find, .ipv6_dev_find = ipv6_dev_find,
.ip6_xmit = ip6_xmit,
}; };
static const struct ipv6_bpf_stub ipv6_bpf_stub_impl = { static const struct ipv6_bpf_stub ipv6_bpf_stub_impl = {
......
...@@ -290,8 +290,14 @@ int __init xfrm6_init(void) ...@@ -290,8 +290,14 @@ int __init xfrm6_init(void)
ret = register_pernet_subsys(&xfrm6_net_ops); ret = register_pernet_subsys(&xfrm6_net_ops);
if (ret) if (ret)
goto out_protocol; goto out_protocol;
ret = xfrm_nat_keepalive_init(AF_INET6);
if (ret)
goto out_nat_keepalive;
out: out:
return ret; return ret;
out_nat_keepalive:
unregister_pernet_subsys(&xfrm6_net_ops);
out_protocol: out_protocol:
xfrm6_protocol_fini(); xfrm6_protocol_fini();
out_state: out_state:
...@@ -303,6 +309,7 @@ int __init xfrm6_init(void) ...@@ -303,6 +309,7 @@ int __init xfrm6_init(void)
void xfrm6_fini(void) void xfrm6_fini(void)
{ {
xfrm_nat_keepalive_fini(AF_INET6);
unregister_pernet_subsys(&xfrm6_net_ops); unregister_pernet_subsys(&xfrm6_net_ops);
xfrm6_protocol_fini(); xfrm6_protocol_fini();
xfrm6_policy_fini(); xfrm6_policy_fini();
......
...@@ -13,7 +13,8 @@ endif ...@@ -13,7 +13,8 @@ endif
obj-$(CONFIG_XFRM) := xfrm_policy.o xfrm_state.o xfrm_hash.o \ obj-$(CONFIG_XFRM) := xfrm_policy.o xfrm_state.o xfrm_hash.o \
xfrm_input.o xfrm_output.o \ xfrm_input.o xfrm_output.o \
xfrm_sysctl.o xfrm_replay.o xfrm_device.o xfrm_sysctl.o xfrm_replay.o xfrm_device.o \
xfrm_nat_keepalive.o
obj-$(CONFIG_XFRM_STATISTICS) += xfrm_proc.o obj-$(CONFIG_XFRM_STATISTICS) += xfrm_proc.o
obj-$(CONFIG_XFRM_ALGO) += xfrm_algo.o obj-$(CONFIG_XFRM_ALGO) += xfrm_algo.o
obj-$(CONFIG_XFRM_USER) += xfrm_user.o obj-$(CONFIG_XFRM_USER) += xfrm_user.o
......
...@@ -131,6 +131,7 @@ static const struct nla_policy compat_policy[XFRMA_MAX+1] = { ...@@ -131,6 +131,7 @@ static const struct nla_policy compat_policy[XFRMA_MAX+1] = {
[XFRMA_IF_ID] = { .type = NLA_U32 }, [XFRMA_IF_ID] = { .type = NLA_U32 },
[XFRMA_MTIMER_THRESH] = { .type = NLA_U32 }, [XFRMA_MTIMER_THRESH] = { .type = NLA_U32 },
[XFRMA_SA_DIR] = NLA_POLICY_RANGE(NLA_U8, XFRM_SA_DIR_IN, XFRM_SA_DIR_OUT), [XFRMA_SA_DIR] = NLA_POLICY_RANGE(NLA_U8, XFRM_SA_DIR_IN, XFRM_SA_DIR_OUT),
[XFRMA_NAT_KEEPALIVE_INTERVAL] = { .type = NLA_U32 },
}; };
static struct nlmsghdr *xfrm_nlmsg_put_compat(struct sk_buff *skb, static struct nlmsghdr *xfrm_nlmsg_put_compat(struct sk_buff *skb,
...@@ -280,9 +281,10 @@ static int xfrm_xlate64_attr(struct sk_buff *dst, const struct nlattr *src) ...@@ -280,9 +281,10 @@ static int xfrm_xlate64_attr(struct sk_buff *dst, const struct nlattr *src)
case XFRMA_IF_ID: case XFRMA_IF_ID:
case XFRMA_MTIMER_THRESH: case XFRMA_MTIMER_THRESH:
case XFRMA_SA_DIR: case XFRMA_SA_DIR:
case XFRMA_NAT_KEEPALIVE_INTERVAL:
return xfrm_nla_cpy(dst, src, nla_len(src)); return xfrm_nla_cpy(dst, src, nla_len(src));
default: default:
BUILD_BUG_ON(XFRMA_MAX != XFRMA_SA_DIR); BUILD_BUG_ON(XFRMA_MAX != XFRMA_NAT_KEEPALIVE_INTERVAL);
pr_warn_once("unsupported nla_type %d\n", src->nla_type); pr_warn_once("unsupported nla_type %d\n", src->nla_type);
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
...@@ -437,7 +439,7 @@ static int xfrm_xlate32_attr(void *dst, const struct nlattr *nla, ...@@ -437,7 +439,7 @@ static int xfrm_xlate32_attr(void *dst, const struct nlattr *nla,
int err; int err;
if (type > XFRMA_MAX) { if (type > XFRMA_MAX) {
BUILD_BUG_ON(XFRMA_MAX != XFRMA_SA_DIR); BUILD_BUG_ON(XFRMA_MAX != XFRMA_NAT_KEEPALIVE_INTERVAL);
NL_SET_ERR_MSG(extack, "Bad attribute"); NL_SET_ERR_MSG(extack, "Bad attribute");
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
......
...@@ -261,9 +261,9 @@ int xfrm_dev_state_add(struct net *net, struct xfrm_state *x, ...@@ -261,9 +261,9 @@ int xfrm_dev_state_add(struct net *net, struct xfrm_state *x,
is_packet_offload = xuo->flags & XFRM_OFFLOAD_PACKET; is_packet_offload = xuo->flags & XFRM_OFFLOAD_PACKET;
/* We don't yet support UDP encapsulation and TFC padding. */ /* We don't yet support TFC padding. */
if ((!is_packet_offload && x->encap) || x->tfcpad) { if (x->tfcpad) {
NL_SET_ERR_MSG(extack, "Encapsulation and TFC padding can't be offloaded"); NL_SET_ERR_MSG(extack, "TFC padding can't be offloaded");
return -EINVAL; return -EINVAL;
} }
......
...@@ -471,7 +471,8 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type) ...@@ -471,7 +471,8 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)
struct xfrm_offload *xo = xfrm_offload(skb); struct xfrm_offload *xo = xfrm_offload(skb);
struct sec_path *sp; struct sec_path *sp;
if (encap_type < 0 || (xo && xo->flags & XFRM_GRO)) { if (encap_type < 0 || (xo && (xo->flags & XFRM_GRO || encap_type == 0 ||
encap_type == UDP_ENCAP_ESPINUDP))) {
x = xfrm_input_state(skb); x = xfrm_input_state(skb);
if (unlikely(x->dir && x->dir != XFRM_SA_DIR_IN)) { if (unlikely(x->dir && x->dir != XFRM_SA_DIR_IN)) {
......
// SPDX-License-Identifier: GPL-2.0-only
/*
* xfrm_nat_keepalive.c
*
* (c) 2024 Eyal Birger <eyal.birger@gmail.com>
*/
#include <net/inet_common.h>
#include <net/ip6_checksum.h>
#include <net/xfrm.h>
static DEFINE_PER_CPU(struct sock *, nat_keepalive_sk_ipv4);
#if IS_ENABLED(CONFIG_IPV6)
static DEFINE_PER_CPU(struct sock *, nat_keepalive_sk_ipv6);
#endif
struct nat_keepalive {
struct net *net;
u16 family;
xfrm_address_t saddr;
xfrm_address_t daddr;
__be16 encap_sport;
__be16 encap_dport;
__u32 smark;
};
static void nat_keepalive_init(struct nat_keepalive *ka, struct xfrm_state *x)
{
ka->net = xs_net(x);
ka->family = x->props.family;
ka->saddr = x->props.saddr;
ka->daddr = x->id.daddr;
ka->encap_sport = x->encap->encap_sport;
ka->encap_dport = x->encap->encap_dport;
ka->smark = xfrm_smark_get(0, x);
}
static int nat_keepalive_send_ipv4(struct sk_buff *skb,
struct nat_keepalive *ka)
{
struct net *net = ka->net;
struct flowi4 fl4;
struct rtable *rt;
struct sock *sk;
__u8 tos = 0;
int err;
flowi4_init_output(&fl4, 0 /* oif */, skb->mark, tos,
RT_SCOPE_UNIVERSE, IPPROTO_UDP, 0,
ka->daddr.a4, ka->saddr.a4, ka->encap_dport,
ka->encap_sport, sock_net_uid(net, NULL));
rt = ip_route_output_key(net, &fl4);
if (IS_ERR(rt))
return PTR_ERR(rt);
skb_dst_set(skb, &rt->dst);
sk = *this_cpu_ptr(&nat_keepalive_sk_ipv4);
sock_net_set(sk, net);
err = ip_build_and_send_pkt(skb, sk, fl4.saddr, fl4.daddr, NULL, tos);
sock_net_set(sk, &init_net);
return err;
}
#if IS_ENABLED(CONFIG_IPV6)
static int nat_keepalive_send_ipv6(struct sk_buff *skb,
struct nat_keepalive *ka,
struct udphdr *uh)
{
struct net *net = ka->net;
struct dst_entry *dst;
struct flowi6 fl6;
struct sock *sk;
__wsum csum;
int err;
csum = skb_checksum(skb, 0, skb->len, 0);
uh->check = csum_ipv6_magic(&ka->saddr.in6, &ka->daddr.in6,
skb->len, IPPROTO_UDP, csum);
if (uh->check == 0)
uh->check = CSUM_MANGLED_0;
memset(&fl6, 0, sizeof(fl6));
fl6.flowi6_mark = skb->mark;
fl6.saddr = ka->saddr.in6;
fl6.daddr = ka->daddr.in6;
fl6.flowi6_proto = IPPROTO_UDP;
fl6.fl6_sport = ka->encap_sport;
fl6.fl6_dport = ka->encap_dport;
sk = *this_cpu_ptr(&nat_keepalive_sk_ipv6);
sock_net_set(sk, net);
dst = ipv6_stub->ipv6_dst_lookup_flow(net, sk, &fl6, NULL);
if (IS_ERR(dst))
return PTR_ERR(dst);
skb_dst_set(skb, dst);
err = ipv6_stub->ip6_xmit(sk, skb, &fl6, skb->mark, NULL, 0, 0);
sock_net_set(sk, &init_net);
return err;
}
#endif
static void nat_keepalive_send(struct nat_keepalive *ka)
{
const int nat_ka_hdrs_len = max(sizeof(struct iphdr),
sizeof(struct ipv6hdr)) +
sizeof(struct udphdr);
const u8 nat_ka_payload = 0xFF;
int err = -EAFNOSUPPORT;
struct sk_buff *skb;
struct udphdr *uh;
skb = alloc_skb(nat_ka_hdrs_len + sizeof(nat_ka_payload), GFP_ATOMIC);
if (unlikely(!skb))
return;
skb_reserve(skb, nat_ka_hdrs_len);
skb_put_u8(skb, nat_ka_payload);
uh = skb_push(skb, sizeof(*uh));
uh->source = ka->encap_sport;
uh->dest = ka->encap_dport;
uh->len = htons(skb->len);
uh->check = 0;
skb->mark = ka->smark;
switch (ka->family) {
case AF_INET:
err = nat_keepalive_send_ipv4(skb, ka);
break;
#if IS_ENABLED(CONFIG_IPV6)
case AF_INET6:
err = nat_keepalive_send_ipv6(skb, ka, uh);
break;
#endif
}
if (err)
kfree_skb(skb);
}
struct nat_keepalive_work_ctx {
time64_t next_run;
time64_t now;
};
static int nat_keepalive_work_single(struct xfrm_state *x, int count, void *ptr)
{
struct nat_keepalive_work_ctx *ctx = ptr;
bool send_keepalive = false;
struct nat_keepalive ka;
time64_t next_run;
u32 interval;
int delta;
interval = x->nat_keepalive_interval;
if (!interval)
return 0;
spin_lock(&x->lock);
delta = (int)(ctx->now - x->lastused);
if (delta < interval) {
x->nat_keepalive_expiration = ctx->now + interval - delta;
next_run = x->nat_keepalive_expiration;
} else if (x->nat_keepalive_expiration > ctx->now) {
next_run = x->nat_keepalive_expiration;
} else {
next_run = ctx->now + interval;
nat_keepalive_init(&ka, x);
send_keepalive = true;
}
spin_unlock(&x->lock);
if (send_keepalive)
nat_keepalive_send(&ka);
if (!ctx->next_run || next_run < ctx->next_run)
ctx->next_run = next_run;
return 0;
}
static void nat_keepalive_work(struct work_struct *work)
{
struct nat_keepalive_work_ctx ctx;
struct xfrm_state_walk walk;
struct net *net;
ctx.next_run = 0;
ctx.now = ktime_get_real_seconds();
net = container_of(work, struct net, xfrm.nat_keepalive_work.work);
xfrm_state_walk_init(&walk, IPPROTO_ESP, NULL);
xfrm_state_walk(net, &walk, nat_keepalive_work_single, &ctx);
xfrm_state_walk_done(&walk, net);
if (ctx.next_run)
schedule_delayed_work(&net->xfrm.nat_keepalive_work,
(ctx.next_run - ctx.now) * HZ);
}
static int nat_keepalive_sk_init(struct sock * __percpu *socks,
unsigned short family)
{
struct sock *sk;
int err, i;
for_each_possible_cpu(i) {
err = inet_ctl_sock_create(&sk, family, SOCK_RAW, IPPROTO_UDP,
&init_net);
if (err < 0)
goto err;
*per_cpu_ptr(socks, i) = sk;
}
return 0;
err:
for_each_possible_cpu(i)
inet_ctl_sock_destroy(*per_cpu_ptr(socks, i));
return err;
}
static void nat_keepalive_sk_fini(struct sock * __percpu *socks)
{
int i;
for_each_possible_cpu(i)
inet_ctl_sock_destroy(*per_cpu_ptr(socks, i));
}
void xfrm_nat_keepalive_state_updated(struct xfrm_state *x)
{
struct net *net;
if (!x->nat_keepalive_interval)
return;
net = xs_net(x);
schedule_delayed_work(&net->xfrm.nat_keepalive_work, 0);
}
int __net_init xfrm_nat_keepalive_net_init(struct net *net)
{
INIT_DELAYED_WORK(&net->xfrm.nat_keepalive_work, nat_keepalive_work);
return 0;
}
int xfrm_nat_keepalive_net_fini(struct net *net)
{
cancel_delayed_work_sync(&net->xfrm.nat_keepalive_work);
return 0;
}
int xfrm_nat_keepalive_init(unsigned short family)
{
int err = -EAFNOSUPPORT;
switch (family) {
case AF_INET:
err = nat_keepalive_sk_init(&nat_keepalive_sk_ipv4, PF_INET);
break;
#if IS_ENABLED(CONFIG_IPV6)
case AF_INET6:
err = nat_keepalive_sk_init(&nat_keepalive_sk_ipv6, PF_INET6);
break;
#endif
}
if (err)
pr_err("xfrm nat keepalive init: failed to init err:%d\n", err);
return err;
}
EXPORT_SYMBOL_GPL(xfrm_nat_keepalive_init);
void xfrm_nat_keepalive_fini(unsigned short family)
{
switch (family) {
case AF_INET:
nat_keepalive_sk_fini(&nat_keepalive_sk_ipv4);
break;
#if IS_ENABLED(CONFIG_IPV6)
case AF_INET6:
nat_keepalive_sk_fini(&nat_keepalive_sk_ipv6);
break;
#endif
}
}
EXPORT_SYMBOL_GPL(xfrm_nat_keepalive_fini);
...@@ -3718,12 +3718,15 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb, ...@@ -3718,12 +3718,15 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb,
pol = xfrm_in_fwd_icmp(skb, &fl, family, if_id); pol = xfrm_in_fwd_icmp(skb, &fl, family, if_id);
if (!pol) { if (!pol) {
const bool is_crypto_offload = sp &&
(xfrm_input_state(skb)->xso.type == XFRM_DEV_OFFLOAD_CRYPTO);
if (net->xfrm.policy_default[dir] == XFRM_USERPOLICY_BLOCK) { if (net->xfrm.policy_default[dir] == XFRM_USERPOLICY_BLOCK) {
XFRM_INC_STATS(net, LINUX_MIB_XFRMINNOPOLS); XFRM_INC_STATS(net, LINUX_MIB_XFRMINNOPOLS);
return 0; return 0;
} }
if (sp && secpath_has_nontransport(sp, 0, &xerr_idx)) { if (sp && secpath_has_nontransport(sp, 0, &xerr_idx) && !is_crypto_offload) {
xfrm_secpath_reject(xerr_idx, skb, &fl); xfrm_secpath_reject(xerr_idx, skb, &fl);
XFRM_INC_STATS(net, LINUX_MIB_XFRMINNOPOLS); XFRM_INC_STATS(net, LINUX_MIB_XFRMINNOPOLS);
return 0; return 0;
...@@ -4284,8 +4287,14 @@ static int __net_init xfrm_net_init(struct net *net) ...@@ -4284,8 +4287,14 @@ static int __net_init xfrm_net_init(struct net *net)
if (rv < 0) if (rv < 0)
goto out_sysctl; goto out_sysctl;
rv = xfrm_nat_keepalive_net_init(net);
if (rv < 0)
goto out_nat_keepalive;
return 0; return 0;
out_nat_keepalive:
xfrm_sysctl_fini(net);
out_sysctl: out_sysctl:
xfrm_policy_fini(net); xfrm_policy_fini(net);
out_policy: out_policy:
...@@ -4298,6 +4307,7 @@ static int __net_init xfrm_net_init(struct net *net) ...@@ -4298,6 +4307,7 @@ static int __net_init xfrm_net_init(struct net *net)
static void __net_exit xfrm_net_exit(struct net *net) static void __net_exit xfrm_net_exit(struct net *net)
{ {
xfrm_nat_keepalive_net_fini(net);
xfrm_sysctl_fini(net); xfrm_sysctl_fini(net);
xfrm_policy_fini(net); xfrm_policy_fini(net);
xfrm_state_fini(net); xfrm_state_fini(net);
...@@ -4359,6 +4369,7 @@ void __init xfrm_init(void) ...@@ -4359,6 +4369,7 @@ void __init xfrm_init(void)
#endif #endif
register_xfrm_state_bpf(); register_xfrm_state_bpf();
xfrm_nat_keepalive_init(AF_INET);
} }
#ifdef CONFIG_AUDITSYSCALL #ifdef CONFIG_AUDITSYSCALL
......
...@@ -715,6 +715,7 @@ int __xfrm_state_delete(struct xfrm_state *x) ...@@ -715,6 +715,7 @@ int __xfrm_state_delete(struct xfrm_state *x)
if (x->id.spi) if (x->id.spi)
hlist_del_rcu(&x->byspi); hlist_del_rcu(&x->byspi);
net->xfrm.state_num--; net->xfrm.state_num--;
xfrm_nat_keepalive_state_updated(x);
spin_unlock(&net->xfrm.xfrm_state_lock); spin_unlock(&net->xfrm.xfrm_state_lock);
if (x->encap_sk) if (x->encap_sk)
...@@ -1453,6 +1454,7 @@ static void __xfrm_state_insert(struct xfrm_state *x) ...@@ -1453,6 +1454,7 @@ static void __xfrm_state_insert(struct xfrm_state *x)
net->xfrm.state_num++; net->xfrm.state_num++;
xfrm_hash_grow_check(net, x->bydst.next != NULL); xfrm_hash_grow_check(net, x->bydst.next != NULL);
xfrm_nat_keepalive_state_updated(x);
} }
/* net->xfrm.xfrm_state_lock is held */ /* net->xfrm.xfrm_state_lock is held */
...@@ -2871,6 +2873,21 @@ int __xfrm_init_state(struct xfrm_state *x, bool init_replay, bool offload, ...@@ -2871,6 +2873,21 @@ int __xfrm_init_state(struct xfrm_state *x, bool init_replay, bool offload,
goto error; goto error;
} }
if (x->nat_keepalive_interval) {
if (x->dir != XFRM_SA_DIR_OUT) {
NL_SET_ERR_MSG(extack, "NAT keepalive is only supported for outbound SAs");
err = -EINVAL;
goto error;
}
if (!x->encap || x->encap->encap_type != UDP_ENCAP_ESPINUDP) {
NL_SET_ERR_MSG(extack,
"NAT keepalive is only supported for UDP encapsulation");
err = -EINVAL;
goto error;
}
}
error: error:
return err; return err;
} }
......
...@@ -833,6 +833,10 @@ static struct xfrm_state *xfrm_state_construct(struct net *net, ...@@ -833,6 +833,10 @@ static struct xfrm_state *xfrm_state_construct(struct net *net,
if (attrs[XFRMA_SA_DIR]) if (attrs[XFRMA_SA_DIR])
x->dir = nla_get_u8(attrs[XFRMA_SA_DIR]); x->dir = nla_get_u8(attrs[XFRMA_SA_DIR]);
if (attrs[XFRMA_NAT_KEEPALIVE_INTERVAL])
x->nat_keepalive_interval =
nla_get_u32(attrs[XFRMA_NAT_KEEPALIVE_INTERVAL]);
err = __xfrm_init_state(x, false, attrs[XFRMA_OFFLOAD_DEV], extack); err = __xfrm_init_state(x, false, attrs[XFRMA_OFFLOAD_DEV], extack);
if (err) if (err)
goto error; goto error;
...@@ -1288,6 +1292,13 @@ static int copy_to_user_state_extra(struct xfrm_state *x, ...@@ -1288,6 +1292,13 @@ static int copy_to_user_state_extra(struct xfrm_state *x,
} }
if (x->dir) if (x->dir)
ret = nla_put_u8(skb, XFRMA_SA_DIR, x->dir); ret = nla_put_u8(skb, XFRMA_SA_DIR, x->dir);
if (x->nat_keepalive_interval) {
ret = nla_put_u32(skb, XFRMA_NAT_KEEPALIVE_INTERVAL,
x->nat_keepalive_interval);
if (ret)
goto out;
}
out: out:
return ret; return ret;
} }
...@@ -3165,6 +3176,7 @@ const struct nla_policy xfrma_policy[XFRMA_MAX+1] = { ...@@ -3165,6 +3176,7 @@ const struct nla_policy xfrma_policy[XFRMA_MAX+1] = {
[XFRMA_IF_ID] = { .type = NLA_U32 }, [XFRMA_IF_ID] = { .type = NLA_U32 },
[XFRMA_MTIMER_THRESH] = { .type = NLA_U32 }, [XFRMA_MTIMER_THRESH] = { .type = NLA_U32 },
[XFRMA_SA_DIR] = NLA_POLICY_RANGE(NLA_U8, XFRM_SA_DIR_IN, XFRM_SA_DIR_OUT), [XFRMA_SA_DIR] = NLA_POLICY_RANGE(NLA_U8, XFRM_SA_DIR_IN, XFRM_SA_DIR_OUT),
[XFRMA_NAT_KEEPALIVE_INTERVAL] = { .type = NLA_U32 },
}; };
EXPORT_SYMBOL_GPL(xfrma_policy); EXPORT_SYMBOL_GPL(xfrma_policy);
...@@ -3474,6 +3486,9 @@ static inline unsigned int xfrm_sa_len(struct xfrm_state *x) ...@@ -3474,6 +3486,9 @@ static inline unsigned int xfrm_sa_len(struct xfrm_state *x)
if (x->dir) if (x->dir)
l += nla_total_size(sizeof(x->dir)); l += nla_total_size(sizeof(x->dir));
if (x->nat_keepalive_interval)
l += nla_total_size(sizeof(x->nat_keepalive_interval));
return l; return l;
} }
......
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