lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <58712444-1de7-4076-b850-4c6035792931@openvpn.net>
Date: Wed, 26 Mar 2025 10:41:44 +0100
From: Antonio Quartulli <antonio@...nvpn.net>
To: Qingfang Deng <dqfext@...il.com>
Cc: Simon Horman <horms@...nel.org>, linux-kernel@...r.kernel.org,
 linux-kselftest@...r.kernel.org, netdev@...r.kernel.org,
 Eric Dumazet <edumazet@...gle.com>, Jakub Kicinski <kuba@...nel.org>,
 Paolo Abeni <pabeni@...hat.com>, Donald Hunter <donald.hunter@...il.com>,
 Shuah Khan <shuah@...nel.org>, sd@...asysnail.net, ryazanov.s.a@...il.com,
 Andrew Lunn <andrew+netdev@...n.ch>, Xiao Liang <shaw.leon@...il.com>
Subject: Re: [PATCH net-next v24 09/23] ovpn: implement packet processing

On 25/03/2025 03:07, Qingfang Deng wrote:
[...]
>> -static void ovpn_decrypt_post(struct sk_buff *skb, int ret)
>> +void ovpn_decrypt_post(void *data, int ret)
>>   {
>> -	struct ovpn_peer *peer = ovpn_skb_cb(skb)->peer;
>> +	struct ovpn_crypto_key_slot *ks;
>> +	unsigned int payload_offset = 0;
>> +	struct sk_buff *skb = data;
>> +	struct ovpn_peer *peer;
>> +	__be16 proto;
>> +	__be32 *pid;
>> +
>> +	/* crypto is happening asynchronously. this function will be called
>> +	 * again later by the crypto callback with a proper return code
>> +	 */
>> +	if (unlikely(ret == -EINPROGRESS))
>> +		return;
>> +
>> +	payload_offset = ovpn_skb_cb(skb)->payload_offset;
>> +	ks = ovpn_skb_cb(skb)->ks;
>> +	peer = ovpn_skb_cb(skb)->peer;
>> +
>> +	/* crypto is done, cleanup skb CB and its members */
>> +
>> +	if (likely(ovpn_skb_cb(skb)->iv))
>> +		kfree(ovpn_skb_cb(skb)->iv);
> 
> NULL check before kfree is unnecessary, as kfree(NULL) is a noop.

Right, will drop the check.

> 
>> +
>> +	if (likely(ovpn_skb_cb(skb)->sg))
>> +		kfree(ovpn_skb_cb(skb)->sg);
>> +
>> +	if (likely(ovpn_skb_cb(skb)->req))
>> +		aead_request_free(ovpn_skb_cb(skb)->req);
> 
> Likewise.

ACK

> 
>>   
>>   	if (unlikely(ret < 0))
>>   		goto drop;
>>   
>> +	/* PID sits after the op */
>> +	pid = (__force __be32 *)(skb->data + OVPN_OPCODE_SIZE);
>> +	ret = ovpn_pktid_recv(&ks->pid_recv, ntohl(*pid), 0);
>> +	if (unlikely(ret < 0)) {
>> +		net_err_ratelimited("%s: PKT ID RX error for peer %u: %d\n",
>> +				    netdev_name(peer->ovpn->dev), peer->id,
>> +				    ret);
>> +		goto drop;
>> +	}
>> +
>> +	/* point to encapsulated IP packet */
>> +	__skb_pull(skb, payload_offset);
>> +
>> +	/* check if this is a valid datapacket that has to be delivered to the
>> +	 * ovpn interface
>> +	 */
>> +	skb_reset_network_header(skb);
>> +	proto = ovpn_ip_check_protocol(skb);
>> +	if (unlikely(!proto)) {
>> +		/* check if null packet */
>> +		if (unlikely(!pskb_may_pull(skb, 1))) {
>> +			net_info_ratelimited("%s: NULL packet received from peer %u\n",
>> +					     netdev_name(peer->ovpn->dev),
>> +					     peer->id);
>> +			goto drop;
>> +		}
>> +
>> +		net_info_ratelimited("%s: unsupported protocol received from peer %u\n",
>> +				     netdev_name(peer->ovpn->dev), peer->id);
>> +		goto drop;
>> +	}
>> +	skb->protocol = proto;
>> +
>> +	/* perform Reverse Path Filtering (RPF) */
>> +	if (unlikely(!ovpn_peer_check_by_src(peer->ovpn, skb, peer))) {
>> +		if (skb->protocol == htons(ETH_P_IPV6))
>> +			net_dbg_ratelimited("%s: RPF dropped packet from peer %u, src: %pI6c\n",
>> +					    netdev_name(peer->ovpn->dev),
>> +					    peer->id, &ipv6_hdr(skb)->saddr);
>> +		else
>> +			net_dbg_ratelimited("%s: RPF dropped packet from peer %u, src: %pI4\n",
>> +					    netdev_name(peer->ovpn->dev),
>> +					    peer->id, &ip_hdr(skb)->saddr);
>> +		goto drop;
>> +	}
>> +
>>   	ovpn_netdev_write(peer, skb);
>>   	/* skb is passed to upper layer - don't free it */
>>   	skb = NULL;
>>   drop:
>>   	if (unlikely(skb))
>>   		dev_core_stats_rx_dropped_inc(peer->ovpn->dev);
>> -	ovpn_peer_put(peer);
>> +	if (likely(peer))
>> +		ovpn_peer_put(peer);
>> +	if (likely(ks))
>> +		ovpn_crypto_key_slot_put(ks);
>>   	kfree_skb(skb);
>>   }
>>   

[...]

>> --- /dev/null
>> +++ b/drivers/net/ovpn/pktid.h
>> @@ -0,0 +1,87 @@
>> +/* SPDX-License-Identifier: GPL-2.0-only */
>> +/*  OpenVPN data channel offload
>> + *
>> + *  Copyright (C) 2020-2025 OpenVPN, Inc.
>> + *
>> + *  Author:	Antonio Quartulli <antonio@...nvpn.net>
>> + *		James Yonan <james@...nvpn.net>
>> + */
>> +
>> +#ifndef _NET_OVPN_OVPNPKTID_H_
>> +#define _NET_OVPN_OVPNPKTID_H_
>> +
>> +#include "proto.h"
>> +
>> +/* If no packets received for this length of time, set a backtrack floor
>> + * at highest received packet ID thus far.
>> + */
>> +#define PKTID_RECV_EXPIRE (30 * HZ)
>> +
>> +/* Packet-ID state for transmitter */
>> +struct ovpn_pktid_xmit {
>> +	atomic64_t seq_num;
> 
> pktid is 32-bit so atomic_t should be enough (see below).
> 
>> +};
>> +
>> +/* replay window sizing in bytes = 2^REPLAY_WINDOW_ORDER */
>> +#define REPLAY_WINDOW_ORDER 8
>> +
>> +#define REPLAY_WINDOW_BYTES BIT(REPLAY_WINDOW_ORDER)
>> +#define REPLAY_WINDOW_SIZE  (REPLAY_WINDOW_BYTES * 8)
>> +#define REPLAY_INDEX(base, i) (((base) + (i)) & (REPLAY_WINDOW_SIZE - 1))
>> +
>> +/* Packet-ID state for receiver.
>> + * Other than lock member, can be zeroed to initialize.
>> + */
>> +struct ovpn_pktid_recv {
>> +	/* "sliding window" bitmask of recent packet IDs received */
>> +	u8 history[REPLAY_WINDOW_BYTES];
>> +	/* bit position of deque base in history */
>> +	unsigned int base;
>> +	/* extent (in bits) of deque in history */
>> +	unsigned int extent;
>> +	/* expiration of history in jiffies */
>> +	unsigned long expire;
>> +	/* highest sequence number received */
>> +	u32 id;
>> +	/* highest time stamp received */
>> +	u32 time;
>> +	/* we will only accept backtrack IDs > id_floor */
>> +	u32 id_floor;
>> +	unsigned int max_backtrack;
>> +	/* protects entire pktd ID state */
>> +	spinlock_t lock;
>> +};
>> +
>> +/* Get the next packet ID for xmit */
>> +static inline int ovpn_pktid_xmit_next(struct ovpn_pktid_xmit *pid, u32 *pktid)
>> +{
>> +	const s64 seq_num = atomic64_fetch_add_unless(&pid->seq_num, 1,
>> +						      0x100000000LL);
>> +	/* when the 32bit space is over, we return an error because the packet
>> +	 * ID is used to create the cipher IV and we do not want to reuse the
>> +	 * same value more than once
>> +	 */
>> +	if (unlikely(seq_num == 0x100000000LL))
>> +		return -ERANGE;
> 
> You may use a 32-bit atomic_t, instead of checking if it equals
> 0x1_00000000, check if it wraparounds to zero.
> Additionally, you don't need full memory ordering as you just want an
> incrementing value:
> 
> int seq_num = atomic_fetch_inc_relaxed(&pid->seq_num);
> 
> if (unlikely(!seq_num))
> 	...

But then if we have concurrent invocations of ovpn_pktid_xmit_next() 
only the first one will error out on wrap-around, while the others will 
return no error (seq_num becomes > 0) and will allow the packets to go 
through.

This is not what we want.

> 
>> +
>> +	*pktid = (u32)seq_num;
>> +
>> +	return 0;
>> +}
>> +
>> +/* Write 12-byte AEAD IV to dest */
>> +static inline void ovpn_pktid_aead_write(const u32 pktid,
>> +					 const u8 nt[],
>> +					 unsigned char *dest)
>> +{
>> +	*(__force __be32 *)(dest) = htonl(pktid);
>> +	BUILD_BUG_ON(4 + OVPN_NONCE_TAIL_SIZE != OVPN_NONCE_SIZE);
>> +	memcpy(dest + 4, nt, OVPN_NONCE_TAIL_SIZE);
>> +}
>> +

Qingfang, may I ask you to remove from your reply non-relevant code 
(like I did above), especially in such long patches, as it becomes a bit 
tedious to spot your comments and I may miss something.

Thanks a lot!

Regards,

-- 
Antonio Quartulli
OpenVPN Inc.


Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ