This commit is contained in:
Mark Brown
2026-07-31 13:58:46 +01:00
8 changed files with 91 additions and 26 deletions
+7
View File
@@ -104,6 +104,13 @@ static void esp_ssg_unref(struct xfrm_state *x, void *tmp, struct sk_buff *skb,
struct aead_request *req;
struct scatterlist *sg;
/* Managed frags are owned by the zerocopy ubuf; the skb holds no
* per-frag page reference, so we must not drop one here. Mirrors
* the SKBFL_MANAGED_FRAG_REFS handling in skb_release_data().
*/
if (skb_zcopy_managed(skb))
return;
if (x->props.flags & XFRM_STATE_ESN)
extralen += sizeof(struct esp_output_extra);
+18 -11
View File
@@ -232,26 +232,28 @@ static void ipv6_rearrange_destopt(struct ipv6hdr *iph, struct ipv6_opt_hdr *des
* Rearrange the destination address in @iph and the addresses in @rthdr
* so that they appear in the order they will at the final destination.
* See Appendix A2 of RFC 2402 for details.
*
* Return: 0 on success, -EINVAL if segments_left exceeds the number of
* addresses described by hdrlen.
*/
static void ipv6_rearrange_rthdr(struct ipv6hdr *iph, struct ipv6_rt_hdr *rthdr)
static int ipv6_rearrange_rthdr(struct ipv6hdr *iph, struct ipv6_rt_hdr *rthdr)
{
int segments, segments_left;
unsigned int segments, segments_left;
struct in6_addr *addrs;
struct in6_addr final_addr;
segments_left = rthdr->segments_left;
if (segments_left == 0)
return;
rthdr->segments_left = 0;
return 0;
/* The value of rthdr->hdrlen has been verified either by the system
* call if it is locally generated, or by ipv6_rthdr_rcv() for incoming
* packets. So we can assume that it is even and that segments is
* greater than or equal to segments_left.
*
* For the same reason we can assume that this option is of type 0.
/* Raw locally generated packets can reach AH6 without the invariant
* required by the rt0-style address rearrangement below.
*/
segments = rthdr->hdrlen >> 1;
if (segments_left > segments)
return -EINVAL;
rthdr->segments_left = 0;
addrs = ((struct rt0_hdr *)rthdr)->addr;
final_addr = addrs[segments - 1];
@@ -261,6 +263,8 @@ static void ipv6_rearrange_rthdr(struct ipv6hdr *iph, struct ipv6_rt_hdr *rthdr)
addrs[0] = iph->daddr;
iph->daddr = final_addr;
return 0;
}
static int ipv6_clear_mutable_options(struct ipv6hdr *iph, int len, int dir)
@@ -273,6 +277,7 @@ static int ipv6_clear_mutable_options(struct ipv6hdr *iph, int len, int dir)
} exthdr = { .iph = iph };
char *end = exthdr.raw + len;
int nexthdr = iph->nexthdr;
int err;
exthdr.iph++;
@@ -292,7 +297,9 @@ static int ipv6_clear_mutable_options(struct ipv6hdr *iph, int len, int dir)
break;
case NEXTHDR_ROUTING:
ipv6_rearrange_rthdr(iph, exthdr.rth);
err = ipv6_rearrange_rthdr(iph, exthdr.rth);
if (err)
return err;
break;
default:
+7
View File
@@ -121,6 +121,13 @@ static void esp_ssg_unref(struct xfrm_state *x, void *tmp, struct sk_buff *skb,
struct aead_request *req;
struct scatterlist *sg;
/* Managed frags are owned by the zerocopy ubuf; the skb holds no
* per-frag page reference, so we must not drop one here. Mirrors
* the SKBFL_MANAGED_FRAG_REFS handling in skb_release_data().
*/
if (skb_zcopy_managed(skb))
return;
if (x->props.flags & XFRM_STATE_ESN)
extralen += sizeof(struct esp_output_extra);
+1 -1
View File
@@ -247,7 +247,7 @@ int xfrm6_input_addr(struct sk_buff *skb, xfrm_address_t *daddr,
goto drop;
}
if (1 + sp->len == XFRM_MAX_DEPTH) {
if (sp->len >= XFRM_MAX_DEPTH) {
XFRM_INC_STATS(net, LINUX_MIB_XFRMINBUFFERERROR);
goto drop;
}
+8 -1
View File
@@ -37,6 +37,11 @@ static void handle_esp(struct sk_buff *skb, struct sock *sk)
rcu_read_lock();
skb->dev = dev_get_by_index_rcu(sock_net(sk), skb->skb_iif);
if (!skb->dev) {
XFRM_INC_STATS(sock_net(sk), LINUX_MIB_XFRMINERROR);
kfree_skb(skb);
goto out;
}
local_bh_disable();
#if IS_ENABLED(CONFIG_IPV6)
if (sk->sk_family == AF_INET6)
@@ -45,6 +50,7 @@ static void handle_esp(struct sk_buff *skb, struct sock *sk)
#endif
xfrm4_rcv_encap(skb, IPPROTO_ESP, 0, TCP_ENCAP_ESPINTCP);
local_bh_enable();
out:
rcu_read_unlock();
}
@@ -515,7 +521,8 @@ static void espintcp_close(struct sock *sk, long timeout)
strp_stop(&ctx->strp);
sk->sk_prot = &tcp_prot;
barrier();
synchronize_rcu();
disable_work_sync(&ctx->work);
strp_done(&ctx->strp);
+48 -9
View File
@@ -156,24 +156,51 @@ static void nat_keepalive_send(struct nat_keepalive *ka)
}
struct nat_keepalive_work_ctx {
struct list_head states;
time64_t next_run;
time64_t now;
};
static int nat_keepalive_work_single(struct xfrm_state *x, int count, void *ptr)
struct nat_keepalive_state {
struct list_head list;
struct xfrm_state *x;
};
static int nat_keepalive_work_collect(struct xfrm_state *x, int count, void *ptr)
{
struct nat_keepalive_work_ctx *ctx = ptr;
struct nat_keepalive_state *state;
if (!READ_ONCE(x->nat_keepalive_interval))
return 0;
state = kmalloc_obj(*state, GFP_ATOMIC);
if (!state)
return -ENOMEM;
xfrm_state_hold(x);
state->x = x;
list_add_tail(&state->list, &ctx->states);
return 0;
}
static void nat_keepalive_work_single(struct xfrm_state *x,
struct nat_keepalive_work_ctx *ctx)
{
bool send_keepalive = false;
struct nat_keepalive ka;
time64_t next_run;
time64_t next_run = 0;
u32 interval;
int delta;
spin_lock_bh(&x->lock);
if (x->km.state == XFRM_STATE_DEAD)
goto out;
interval = x->nat_keepalive_interval;
if (!interval)
return 0;
spin_lock(&x->lock);
goto out;
delta = (int)(ctx->now - x->lastused);
if (delta < interval) {
@@ -187,29 +214,41 @@ static int nat_keepalive_work_single(struct xfrm_state *x, int count, void *ptr)
send_keepalive = true;
}
spin_unlock(&x->lock);
out:
spin_unlock_bh(&x->lock);
if (send_keepalive)
nat_keepalive_send(&ka);
if (!ctx->next_run || next_run < ctx->next_run)
if (next_run && (!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_state *state, *tmp;
struct nat_keepalive_work_ctx ctx;
struct xfrm_state_walk walk;
struct net *net;
int err;
INIT_LIST_HEAD(&ctx.states);
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);
err = xfrm_state_walk(net, &walk, nat_keepalive_work_collect, &ctx);
xfrm_state_walk_done(&walk, net);
list_for_each_entry_safe(state, tmp, &ctx.states, list) {
nat_keepalive_work_single(state->x, &ctx);
xfrm_state_put(state->x);
kfree(state);
}
if (err == -ENOMEM) {
schedule_delayed_work(&net->xfrm.nat_keepalive_work, 0);
return;
}
if (ctx.next_run)
schedule_delayed_work(&net->xfrm.nat_keepalive_work,
(ctx.next_run - ctx.now) * HZ);
+1 -3
View File
@@ -636,10 +636,8 @@ static int xfrm_dev_direct_output(struct sock *sk, struct xfrm_state *x,
nf_reset_ct(skb);
err = skb_dst(skb)->ops->local_out(net, sk, skb);
if (unlikely(err != 1)) {
kfree_skb(skb);
if (unlikely(err != 1))
return err;
}
/* In transport mode, network destination is
* directly reachable, while in tunnel mode,
+1 -1
View File
@@ -940,7 +940,7 @@ static struct xfrm_state *xfrm_state_construct(struct net *net,
if ((err = attach_auth_trunc(&x->aalg, &x->props.aalgo,
attrs[XFRMA_ALG_AUTH_TRUNC], extack)))
goto error;
if (!x->props.aalgo) {
if (!x->aalg) {
if ((err = attach_auth(&x->aalg, &x->props.aalgo,
attrs[XFRMA_ALG_AUTH], extack)))
goto error;