1

ipsec-next-2024-07-13

-----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEEH7ZpcWbFyOOp6OJbrB3Eaf9PW7cFAmaSU/QACgkQrB3Eaf9P
 W7etjA/+I8bWTjMCCGFT7AXIisXWQhHbrRuaU6hpROxWUTAyjUuM4qhdXHYUyG6i
 2mcg7Ppqn0etEnrvCDJqgWGPonSJuxKRMpRNiB2uRYZAKDK2X7d5gCVVK+xGyuYn
 rXjAw3yQ9W6oV8lQvm7GqLYOFL5vj9UA5q8QEhyTxH11HDDRBjlHSgzgWovzGsjO
 2qLHSh3wuBuuoWS6jhN5n0pA1mFiKxhzPRRvTV2Q8CEBt+JML0gGd08g0s6tSGMJ
 qlEGdTHIkIGi/QsbOoRm14X5gYYrDz1EEATISZTA9/Pbb03MsQfxUp6EUZNZIM4O
 /K9XO7LLXOYWXBcI3BDCHCOT1cJPw1WVvYwlwWzu4DpxelPAc+pk2/QZk9wV2cWd
 MzScbhHKmZ5GnYnlfQAyOnC5tvQXUBG2OntyXMBGh9seh+H5Lcl1RJAflIwRvBx5
 7cnR6HiTmLUlbBxKjSJF+xFPnTucp0J637DkY/ONtAA7qNHnOKh3LWqkIH80q/FI
 7Ua0EpgTtzAzN6iR2ujMHusfAjJs4yhMGY5KFGcEHwqS2axYq+mpnaShYzNebzl6
 9kOmj6UAVP0tivH2Ahmsz2HaNhZaJ3hXftZeF3zwcoN6XTc3jrQ4JuNyiDcsUdnf
 ggyLMZ7VI6Jf38ep8LEnfpqQm5qFTVfto62goWWLlGgr4wsy66c=
 =KyYL
 -----END PGP SIGNATURE-----

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.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Jakub Kicinski 2024-07-14 07:56:32 -07:00
commit 62fdd1708f
16 changed files with 393 additions and 10 deletions

View File

@ -9,6 +9,7 @@
#include <net/flow.h>
#include <net/neighbour.h>
#include <net/sock.h>
#include <net/ipv6.h>
/* structs from net/ip6_fib.h */
struct fib6_info;
@ -72,6 +73,8 @@ struct ipv6_stub {
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 *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;

View File

@ -83,6 +83,7 @@ struct netns_xfrm {
spinlock_t xfrm_policy_lock;
struct mutex xfrm_cfg_mutex;
struct delayed_work nat_keepalive_work;
};
#endif

View File

@ -229,6 +229,10 @@ struct xfrm_state {
struct xfrm_encap_tmpl *encap;
struct sock __rcu *encap_sk;
/* NAT keepalive */
u32 nat_keepalive_interval; /* seconds */
time64_t nat_keepalive_expiration;
/* Data for care-of address */
xfrm_address_t *coaddr;
@ -2203,4 +2207,10 @@ static inline int register_xfrm_state_bpf(void)
}
#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 */

View File

@ -321,6 +321,7 @@ enum xfrm_attr_type_t {
XFRMA_IF_ID, /* __u32 */
XFRMA_MTIMER_THRESH, /* __u32 in seconds for input SA */
XFRMA_SA_DIR, /* __u8 */
XFRMA_NAT_KEEPALIVE_INTERVAL, /* __u32 in seconds for NAT keepalive */
__XFRMA_MAX
#define XFRMA_OUTPUT_MARK XFRMA_SET_MARK /* Compatibility */

View File

@ -349,6 +349,7 @@ static struct ip_esp_hdr *esp_output_udp_encap(struct sk_buff *skb,
{
struct udphdr *uh;
unsigned int len;
struct xfrm_offload *xo = xfrm_offload(skb);
len = skb->len + esp->tailen - skb_transport_offset(skb);
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,
uh->len = htons(len);
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);
}

View File

@ -264,6 +264,7 @@ static int esp_xmit(struct xfrm_state *x, struct sk_buff *skb, netdev_features_
struct esp_info esp;
bool hw_offload = true;
__u32 seq;
int encap_type = 0;
esp.inplace = true;
@ -296,8 +297,10 @@ static int esp_xmit(struct xfrm_state *x, struct sk_buff *skb, netdev_features_
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);
if (esp.nfrags < 0)
return esp.nfrags;
@ -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));
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_send_check(ip_hdr(skb));

View File

@ -1060,6 +1060,7 @@ static const struct ipv6_stub ipv6_stub_impl = {
.nd_tbl = &nd_tbl,
.ipv6_fragment = ip6_fragment,
.ipv6_dev_find = ipv6_dev_find,
.ip6_xmit = ip6_xmit,
};
static const struct ipv6_bpf_stub ipv6_bpf_stub_impl = {

View File

@ -290,8 +290,14 @@ int __init xfrm6_init(void)
ret = register_pernet_subsys(&xfrm6_net_ops);
if (ret)
goto out_protocol;
ret = xfrm_nat_keepalive_init(AF_INET6);
if (ret)
goto out_nat_keepalive;
out:
return ret;
out_nat_keepalive:
unregister_pernet_subsys(&xfrm6_net_ops);
out_protocol:
xfrm6_protocol_fini();
out_state:
@ -303,6 +309,7 @@ out_policy:
void xfrm6_fini(void)
{
xfrm_nat_keepalive_fini(AF_INET6);
unregister_pernet_subsys(&xfrm6_net_ops);
xfrm6_protocol_fini();
xfrm6_policy_fini();

View File

@ -13,7 +13,8 @@ endif
obj-$(CONFIG_XFRM) := xfrm_policy.o xfrm_state.o xfrm_hash.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_ALGO) += xfrm_algo.o
obj-$(CONFIG_XFRM_USER) += xfrm_user.o

View File

@ -131,6 +131,7 @@ static const struct nla_policy compat_policy[XFRMA_MAX+1] = {
[XFRMA_IF_ID] = { .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_NAT_KEEPALIVE_INTERVAL] = { .type = NLA_U32 },
};
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)
case XFRMA_IF_ID:
case XFRMA_MTIMER_THRESH:
case XFRMA_SA_DIR:
case XFRMA_NAT_KEEPALIVE_INTERVAL:
return xfrm_nla_cpy(dst, src, nla_len(src));
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);
return -EOPNOTSUPP;
}
@ -437,7 +439,7 @@ static int xfrm_xlate32_attr(void *dst, const struct nlattr *nla,
int err;
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");
return -EOPNOTSUPP;
}

View File

@ -261,9 +261,9 @@ int xfrm_dev_state_add(struct net *net, struct xfrm_state *x,
is_packet_offload = xuo->flags & XFRM_OFFLOAD_PACKET;
/* We don't yet support UDP encapsulation and TFC padding. */
if ((!is_packet_offload && x->encap) || x->tfcpad) {
NL_SET_ERR_MSG(extack, "Encapsulation and TFC padding can't be offloaded");
/* We don't yet support TFC padding. */
if (x->tfcpad) {
NL_SET_ERR_MSG(extack, "TFC padding can't be offloaded");
return -EINVAL;
}

View File

@ -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 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);
if (unlikely(x->dir && x->dir != XFRM_SA_DIR_IN)) {

View File

@ -0,0 +1,292 @@
// 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);

View File

@ -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);
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) {
XFRM_INC_STATS(net, LINUX_MIB_XFRMINNOPOLS);
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_INC_STATS(net, LINUX_MIB_XFRMINNOPOLS);
return 0;
@ -4284,8 +4287,14 @@ static int __net_init xfrm_net_init(struct net *net)
if (rv < 0)
goto out_sysctl;
rv = xfrm_nat_keepalive_net_init(net);
if (rv < 0)
goto out_nat_keepalive;
return 0;
out_nat_keepalive:
xfrm_sysctl_fini(net);
out_sysctl:
xfrm_policy_fini(net);
out_policy:
@ -4298,6 +4307,7 @@ out_statistics:
static void __net_exit xfrm_net_exit(struct net *net)
{
xfrm_nat_keepalive_net_fini(net);
xfrm_sysctl_fini(net);
xfrm_policy_fini(net);
xfrm_state_fini(net);
@ -4359,6 +4369,7 @@ void __init xfrm_init(void)
#endif
register_xfrm_state_bpf();
xfrm_nat_keepalive_init(AF_INET);
}
#ifdef CONFIG_AUDITSYSCALL

View File

@ -715,6 +715,7 @@ int __xfrm_state_delete(struct xfrm_state *x)
if (x->id.spi)
hlist_del_rcu(&x->byspi);
net->xfrm.state_num--;
xfrm_nat_keepalive_state_updated(x);
spin_unlock(&net->xfrm.xfrm_state_lock);
if (x->encap_sk)
@ -1453,6 +1454,7 @@ static void __xfrm_state_insert(struct xfrm_state *x)
net->xfrm.state_num++;
xfrm_hash_grow_check(net, x->bydst.next != NULL);
xfrm_nat_keepalive_state_updated(x);
}
/* net->xfrm.xfrm_state_lock is held */
@ -2871,6 +2873,21 @@ int __xfrm_init_state(struct xfrm_state *x, bool init_replay, bool offload,
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:
return err;
}

View File

@ -833,6 +833,10 @@ static struct xfrm_state *xfrm_state_construct(struct net *net,
if (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);
if (err)
goto error;
@ -1288,6 +1292,13 @@ static int copy_to_user_state_extra(struct xfrm_state *x,
}
if (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:
return ret;
}
@ -3165,6 +3176,7 @@ const struct nla_policy xfrma_policy[XFRMA_MAX+1] = {
[XFRMA_IF_ID] = { .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_NAT_KEEPALIVE_INTERVAL] = { .type = NLA_U32 },
};
EXPORT_SYMBOL_GPL(xfrma_policy);
@ -3474,6 +3486,9 @@ static inline unsigned int xfrm_sa_len(struct xfrm_state *x)
if (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;
}