* [PATCH] rose: fix OOB read on short CLEAR REQUEST frames.
From: Ashutosh Desai @ 2026-04-09 1:32 UTC (permalink / raw)
To: netdev
Cc: linux-hams, davem, edumazet, kuba, pabeni, horms, linux-kernel,
Ashutosh Desai
rose_process_rx_frame() dispatches to state machines after calling
rose_decode(), but does not verify the frame is long enough before
doing so. All five state machine handlers read skb->data[3] and
skb->data[4] (cause and diagnostic bytes) when handling a
ROSE_CLEAR_REQUEST frame, yet the only upstream length check is
ROSE_MIN_LEN (3 bytes) in rose_route_frame().
A crafted 3-byte ROSE CLEAR REQUEST frame (bytes: GFI/LCI-high,
LCI-low, 0x13) passes the minimum length gate and reaches the state
machines, where skb->data[3] and skb->data[4] are read one and two
bytes past the valid buffer respectively.
Add a check in rose_process_rx_frame() that drops any CLEAR REQUEST
frame shorter than 5 bytes (3-byte header + cause + diagnostic),
covering all five state machines with a single guard.
Signed-off-by: Ashutosh Desai <ashutoshdesai993@gmail.com>
---
net/rose/rose_in.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/net/rose/rose_in.c b/net/rose/rose_in.c
index 0276b393f..1ac9a6aee 100644
--- a/net/rose/rose_in.c
+++ b/net/rose/rose_in.c
@@ -271,6 +271,11 @@ int rose_process_rx_frame(struct sock *sk, struct sk_buff *skb)
frametype = rose_decode(skb, &ns, &nr, &q, &d, &m);
+ if (frametype == ROSE_CLEAR_REQUEST && skb->len < 5) {
+ kfree_skb(skb);
+ return 0;
+ }
+
switch (rose->state) {
case ROSE_STATE_1:
queued = rose_state1_machine(sk, skb, frametype);
--
2.34.1
^ permalink raw reply related
* [PATCH] ax25: fix OOB read after address header strip in ax25_rcv().
From: Ashutosh Desai @ 2026-04-09 1:22 UTC (permalink / raw)
To: netdev
Cc: linux-hams, jreuter, davem, edumazet, kuba, pabeni, horms,
linux-kernel, Ashutosh Desai
ax25_rcv() calls skb_pull(skb, ax25_addr_size(&dp)) to strip the
address header, then immediately reads skb->data[0] and skb->data[1]
without verifying the buffer still contains at least 2 bytes.
A crafted 15-byte KISS frame (1 KISS byte + 14 address bytes with
EBIT set in the source address, no control/PID bytes) passes
ax25_addr_parse() which only requires len >= 14, and passes the KISS
byte check (low nibble == 0). After skb_pull(1) in ax25_kiss_rcv()
and skb_pull(14) in ax25_rcv(), skb->len is 0 and the subsequent
reads of skb->data[0] (control byte) and skb->data[1] (PID byte)
are out of bounds.
Add a check that at least 2 bytes remain after stripping the address
header, freeing the skb and returning on malformed input.
Signed-off-by: Ashutosh Desai <ashutoshdesai993@gmail.com>
---
net/ax25/ax25_in.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/net/ax25/ax25_in.c b/net/ax25/ax25_in.c
index d75b3e9ed..92baac77f 100644
--- a/net/ax25/ax25_in.c
+++ b/net/ax25/ax25_in.c
@@ -217,6 +217,11 @@ static int ax25_rcv(struct sk_buff *skb, struct net_device *dev,
*/
skb_pull(skb, ax25_addr_size(&dp));
+ if (skb->len < 2) {
+ kfree_skb(skb);
+ return 0;
+ }
+
/* For our port addresses ? */
if (ax25cmp(&dest, dev_addr) == 0 && dp.lastrepeat + 1 == dp.ndigi)
mine = 1;
--
2.34.1
^ permalink raw reply related
* Re: [PATCH net-next v3 3/3] gve: implement PTP gettimex64
From: Jordan Rhee @ 2026-04-09 1:08 UTC (permalink / raw)
To: Jacob Keller
Cc: Jakub Kicinski, Harshitha Ramamurthy, netdev, joshwash,
andrew+netdev, davem, edumazet, pabeni, richardcochran, willemb,
nktgrg, jfraker, ziweixiao, maolson, thostet, jefrogers,
alok.a.tiwari, yyd, linux-kernel, Naman Gulati
In-Reply-To: <093a5c92-f94c-49d6-96ea-0c76ff18f9e1@intel.com>
On Wed, Apr 8, 2026 at 3:43 PM Jacob Keller <jacob.e.keller@intel.com> wrote:
>
> On 4/6/2026 1:41 PM, Jordan Rhee wrote:
> > On Fri, Apr 3, 2026 at 2:18 PM Jacob Keller <jacob.e.keller@intel.com> wrote:
> >>
> >> On 4/3/2026 12:44 PM, Harshitha Ramamurthy wrote:
> >>> From: Jordan Rhee <jordanrhee@google.com>
> >>>
> >>> Enable chrony and phc2sys to synchronize system clock to NIC clock.
> >>>
> >>> The system cycle counters are sampled by the device to minimize the
> >>> uncertainty window. If the system times are sampled in the host, the
> >>> delta between pre and post readings is 100us or more due to AQ command
> >>> latency. The system times returned by the device have a delta of ~1us,
> >>> which enables significantly more accurate clock synchronization.
> >>>
> >>> Reviewed-by: Willem de Bruijn <willemb@google.com>
> >>> Reviewed-by: Kevin Yang <yyd@google.com>
> >>> Reviewed-by: Naman Gulati <namangulati@google.com>
> >>> Signed-off-by: Jordan Rhee <jordanrhee@google.com>
> >>> Signed-off-by: Harshitha Ramamurthy <hramamurthy@google.com>
> >>> ---
> >>
> >>> +/*
> >>> + * Convert a raw cycle count (e.g. from get_cycles()) to the system clock
> >>> + * type specified by clockid. The system_time_snapshot must be taken before
> >>> + * the cycle counter is sampled.
> >>> + */
> >>> +static int gve_cycles_to_timespec64(struct gve_priv *priv, clockid_t clockid,
> >>> + struct system_time_snapshot *snap,
> >>> + u64 cycles, struct timespec64 *ts)
> >>> +{
> >>> + struct gve_cycles_to_clock_callback_ctx ctx = {0};
> >>> + struct system_device_crosststamp xtstamp;
> >>> + int err;
> >>> +
> >>> + ctx.cycles = cycles;
> >>> + err = get_device_system_crosststamp(gve_cycles_to_clock_fn, &ctx, snap,
> >>> + &xtstamp);
> >>> + if (err) {
> >>> + dev_err_ratelimited(&priv->pdev->dev,
> >>> + "get_device_system_crosststamp() failed to convert %lld cycles to system time: %d\n",
> >>> + cycles,
> >>> + err);
> >>> + return err;
> >>> + }
> >>> +
> >>
> >> This looks a lot like a cross timestamp (i.e. something like PCIe PTM)
> >> Why not just implement the .crosstimestamp and PTP_SYS_OFF_PRECISE? Does
> >> that not work properly? Or is this not really a cross timestamp despite
> >> use of the get_device_system_crosststamp handler? :D
> >
> > .crosstimestamp is for devices that support simultaneous NIC and
> > system timestamps. Devices that don't support simultaneous timestamps
> > have to take a system time sandwich by calling
> > ptp_read_system_prets()/ptp_read_system_postts() on either side of the
> > NIC timestamp. Upper layers (e.g. chrony) use the sandwich delta in
> > nontrivial ways when estimating the system clock / NIC clock offset.
> > This is information that must be preserved, and it would be incorrect
> > to implement .crosstimestamp by returning the midpoint of the
> > sandwich, as tempting as that implementation might be.
> >
>
> True.
>
> > Gvnic does not support simultaneous NIC and system timestamps, so it
> > must use the sandwich technique. Since the NIC timestamp is obtained
> > using a firmware (hypervisor) call, the uncertainty window would be
> > too large if it were taken inside the VM. Gvnic takes the sandwich in
> > the hypervisor and returns the raw TSC values to the VM.
> > get_device_system_crosststamp() is used to convert the TSCs to system
> > times, which I believe is the only correct way to do this conversion.
> > Jordan
> >
>
> Hmm. The function says:
>
> "Synchronously capture system/device timestamp". That is what confuses
> me. Your implementation uses gve_cycles_to_clock_fn() which just sets
> some values in the system_counterval struct and exits. It doesn't
> "capture a system/device timestamp" tuple.
>
> This does feel a bit weird. No other caller appears to exist outside of
> the cross timestamp implementations.
>
> It sounds like what you want is a function that takes a cycles count
> value and does the conversion from TSC to the appropriate clock, along
> with all of the interopolation etc. What you've done is sort of a cludge
> around get_device_system_crosststamp() to force it to do that for you
> without actually using it as intended.
>
> I'd argue it would be better to have a cycles_to_ktime() or something
> which takes the TSC cycles value and the appropriate clock and does the
> exact same flow as get_device_system_crosststamp() for converting the
> cycles into proper ktime values without the mess of the callback
> function etc.
Yes, that's exactly how I'm using get_device_system_crosststamp().
There is no cycles_to_ktime() function, and adding one would make this
patch more difficult to backport to older kernels.
There is precedent for using get_device_system_crosststamp() this way
in the virtio_rtc driver. In viortc_ptp_getcrosststamp(), the
timestamp is sampled before calling get_device_system_crosststamp(),
and the callback simply populates the return values from the context.
I believe the snapshot parameter was added to support this use case.
>
> I guess in principle what you've implemented is "correct" and
> functional, but it definitely feels a bit weird to use the API in this
> way. It smells like a neat hack instead of a proper interface for this
> purpose.
>
> That said, I won't object strongly if the maintainers are fine with
> using it for this purpose.
>
> Thanks,
> Jake
^ permalink raw reply
* Re: linux-next: manual merge of the net-next tree with the netfilter tree
From: Andrea Mayer @ 2026-04-09 1:04 UTC (permalink / raw)
To: Mark Brown, Matthieu Baerts
Cc: David Miller, Jakub Kicinski, Paolo Abeni, Networking,
Justin Iurman, Linux Kernel Mailing List, Linux Next Mailing List,
Andrea Mayer
In-Reply-To: <d9d4e328-6668-4c24-bace-77a0f93bbed6@sirena.org.uk>
On Wed, 8 Apr 2026 18:00:50 +0100
Mark Brown <broonie@kernel.org> wrote:
> On Wed, Apr 08, 2026 at 06:43:36PM +0200, Matthieu Baerts wrote:
> > On 08/04/2026 17:08, Mark Brown wrote:
>
> > > This also needs a fixup for a new jump to the error handling paths that
> > > was added in seg6_build_state().
>
> > I also had this other conflict there, and I did this when resolving it
> > in MPTCP tree:
>
> > + if (tb[SEG6_IPTUNNEL_SRC]) {
> > + slwt->tunsrc = nla_get_in6_addr(tb[SEG6_IPTUNNEL_SRC]);
> > +
> > + if (ipv6_addr_any(&slwt->tunsrc) ||
> > + ipv6_addr_is_multicast(&slwt->tunsrc) ||
> > + ipv6_addr_loopback(&slwt->tunsrc)) {
> > + NL_SET_ERR_MSG(extack, "invalid tunsrc address");
> > + err = -EINVAL;
> > - goto free_dst_cache;
> > ++ goto err_destroy_output;
> > + }
> > + }
> > +
>
> Yes, that's the additional fixup I mentioned above - it didn't conflict
> for me (well, the exit path did).
Thanks Mark and Matthieu for taking care of this.
I went through both commits and the rerere.
The resolution looks correct from the seg6 side. Build-tested.
Happy to help if anything else comes up.
Cheers,
Andrea
^ permalink raw reply
* Re: [PATCH v2] net: dsa: microchip: implement KSZ87xx Module 3 low-loss cable errata
From: Marek Vasut @ 2026-04-09 1:04 UTC (permalink / raw)
To: Fidelio Lawson, Woojung Huh, UNGLinuxDriver, Andrew Lunn,
Vladimir Oltean, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Marek Vasut, Maxime Chevallier
Cc: netdev, devicetree, linux-kernel, Fidelio Lawson
In-Reply-To: <20260408-ksz87xx_errata_low_loss_connections-v2-1-9cfe38691713@exotec.com>
On 4/8/26 1:57 PM, Fidelio Lawson wrote:
> Implement the "Module 3: Equalizer fix for short cables" erratum from
> Microchip document DS80000687C for KSZ87xx switches.
>
> The issue affects short or low-loss cable links (e.g. CAT5e/CAT6),
> where the PHY receiver equalizer may amplify high-amplitude signals
> excessively, resulting in internal distortion and link establishment
> failures.
>
> KSZ87xx devices require a workaround for the Module 3 low-loss cable
> condition, controlled through the switch TABLE_LINK_MD_V indirect
> registers.
>
> The affected registers are part of the switch address space and are not
> directly accessible from the PHY driver. To keep the PHY-facing API
> clean and avoid leaking switch-specific details, model this errata
> control as vendor-specific Clause 22 PHY registers.
>
> Two vendor-defined bits are introduced in PHY_REG_LOW_LOSS_CTRL,
> and ksz8_r_phy() / ksz8_w_phy() translate accesses to these bits
> into the appropriate indirect TABLE_LINK_MD_V accesses.
>
> The control register defines the following modes:
> bits [1:0]:
> 00 = workaround disabled
> 01 = workaround 1 (DSP EQ training adjustment, LinkMD reg 0x3c)
> 10 = workaround 2 (receiver LPF bandwidth, LinkMD reg 0x4c)
>
> Workaround 1: Adjusts the DSP EQ training behavior via LinkMD register
> 0x3C. Widens and optimizes the DSP EQ compensation range,
> and is expected to solve most short/low-loss cable issues.
>
> Workaround 2: for the cases where Workaround 1 is not sufficient.
> This one adjusts the receiver low-pass filter bandwidth, effectively
> reducing the high-frequency component of the received signal
>
> The register is accessible through standard PHY read/write operations
> (e.g. phytool), without requiring any switch-specific userspace
> interface. This allows robust link establishment on short or
> low-loss cabling without requiring DTS properties and without
> constraining hardware design choices.
>
> The erratum affects the shared PHY analog front-end and therefore
> applies globally to the switch.
>
> Signed-off-by: Fidelio Lawson <fidelio.lawson@exotec.com>
> ---
> Hello,
>
> This patch implements the “Module 3: Equalizer fix for short cables” erratum
> described in Microchip document DS80000687C for KSZ87xx switches.
>
> According to the erratum, the embedded PHY receiver in KSZ87xx switches is
> tuned by default for long, high-loss Ethernet cables. When operating with
> short or low-loss cables (for example CAT5e or CAT6), the PHY equalizer may
> over-amplify the incoming signal, leading to internal distortion and link
> establishment failures.
>
> Microchip provides two workarounds, each requiring a write to a different
> indirect PHY register access mechanism.
>
> The workaround requires programming internal PHY/DSP registers located in the
> LinkMD table, accessed through the KSZ8 indirect register mechanism. Since these
> registers belong to the switch address space and are not directly accessible
> from a standalone PHY driver, the erratum control is modeled as a vendor-specific
> Clause 22 PHY register, virtualized by the KSZ8 DSA driver.
>
> Reads and writes to this register are intercepted by ksz8_r_phy() /
> ksz8_w_phy() and translated into the required TABLE_LINK_MD_V indirect accesses.
> The erratum affects the shared PHY analog front-end and therefore applies
> globally to the switch.
>
> The register defines three modes:
> - 0x0: workaround disabled
> - 0x1: workaround 1 (DSP EQ training adjustment)
> - 0x2: workaround 2 (receiver low-pass filter bandwidth reduction)
>
> The register can be read and written from userspace via standard Clause 22 PHY
> accesses (for example using phytool) on DSA user ports.
>
> This series is based on Linux v7.0-rc1.
> ---
> Changes in v2:
> - Dropped the device tree approache based on review feedback
> - Modeled the errata control as a vendor-specific Clause 22 PHY register
> - Added KSZ87xx-specific guards and replaced magic values with named macros
> - Rebased on Linux v7.0-rc1
> - Link to v1: https://patch.msgid.link/20260326-ksz87xx_errata_low_loss_connections-v1-0-79a698f43626@exotec.com
> ---
> drivers/net/dsa/microchip/ksz8.c | 33 +++++++++++++++++++++++++++++++++
> drivers/net/dsa/microchip/ksz8_reg.h | 20 +++++++++++++++++++-
> drivers/net/dsa/microchip/ksz_common.h | 3 +++
> 3 files changed, 55 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/net/dsa/microchip/ksz8.c b/drivers/net/dsa/microchip/ksz8.c
> index c354abdafc1b..d11da6e9ff54 100644
> --- a/drivers/net/dsa/microchip/ksz8.c
> +++ b/drivers/net/dsa/microchip/ksz8.c
> @@ -1058,6 +1058,11 @@ int ksz8_r_phy(struct ksz_device *dev, u16 phy, u16 reg, u16 *val)
> if (ret)
> return ret;
>
> + break;
> + case PHY_REG_KSZ87XX_LOW_LOSS:
> + if (!ksz_is_ksz87xx(dev))
> + return -EOPNOTSUPP;
> + data = dev->low_loss_wa_mode;
> break;
> default:
> processed = false;
> @@ -1271,6 +1276,34 @@ int ksz8_w_phy(struct ksz_device *dev, u16 phy, u16 reg, u16 val)
> if (ret)
> return ret;
> break;
> + case PHY_REG_KSZ87XX_LOW_LOSS:
> + if (!ksz_is_ksz87xx(dev))
> + return -EOPNOTSUPP;
> +
> + switch (val & PHY_KSZ87XX_LOW_LOSS_MASK) {
> + case PHY_LOW_LOSS_ERRATA_DISABLED:
> + ret = ksz8_ind_write8(dev, TABLE_LINK_MD, KSZ87XX_REG_EQ_TRAIN,
> + KSZ87XX_EQ_TRAIN_DEFAULT);
> + if (!ret)
> + ret = ksz8_ind_write8(dev, TABLE_LINK_MD,
> + KSZ87XX_REG_PHY_LPF,
> + KSZ87XX_PHY_LPF_DEFAULT);
> + break;
> + case KSZ87XX_LOW_LOSS_WA_EQ:
> + ret = ksz8_ind_write8(dev, TABLE_LINK_MD, KSZ87XX_REG_EQ_TRAIN,
> + KSZ87XX_EQ_TRAIN_LOW_LOSS);
> + break;
> + case KSZ87XX_LOW_LOSS_WA_LPF:
> + ret = ksz8_ind_write8(dev, TABLE_LINK_MD, KSZ87XX_REG_PHY_LPF,
> + KSZ87XX_PHY_LPF_62MHZ);
Please adjust this and make the low pass filter bandwidth actually
configurable according to the values supported by the hardware, see this
article:
https://microchip.my.site.com/s/article/Solution-for-Using-CAT-5E-or-CAT-6-Short-Cable-with-a-Link-Issue-for-the-KSZ8795-Family
The indirect register (0x4C) is an 8-bit register. The bits [7:6] are
described in the table below.
Low pass filter bandwidth
00 = 90MHz
01 = 62MHz
10 = 55MHz
11 = 44MHz
...
^ permalink raw reply
* Re: [PATCH net-next v3 00/10] enic: SR-IOV V2 admin channel and MBOX protocol
From: Jakub Kicinski @ 2026-04-09 1:02 UTC (permalink / raw)
To: Satish Kharat via B4 Relay
Cc: satishkh, Andrew Lunn, David S. Miller, Eric Dumazet, Paolo Abeni,
netdev, linux-kernel,
20260401-enic-sriov-v2-prep-v4-0-d5834b2ef1b9, Breno Leitao
In-Reply-To: <20260408-enic-sriov-v2-admin-channel-v2-v3-0-1d4999a03cec@cisco.com>
On Wed, 08 Apr 2026 09:36:21 -0700 Satish Kharat via B4 Relay wrote:
> This series adds the admin channel infrastructure and mailbox (MBOX)
> protocol needed for V2 SR-IOV support in the enic driver.
>
> The V2 SR-IOV design uses a direct PF-VF communication channel built on
> dedicated WQ/RQ/CQ hardware resources and an MSI-X interrupt.
read this please:
https://www.kernel.org/doc/html/next/process/maintainer-netdev.html
--
pv-bot: 24h
^ permalink raw reply
* Re: [PATCH iwl-next 1/2] i40e: implement basic per-queue stats
From: Jakub Kicinski @ 2026-04-09 0:45 UTC (permalink / raw)
To: Paolo Abeni
Cc: intel-wired-lan, Tony Nguyen, Przemek Kitszel, Andrew Lunn,
David S. Miller, Eric Dumazet, Alexei Starovoitov,
Daniel Borkmann, Jesper Dangaard Brouer, John Fastabend,
Stanislav Fomichev, netdev
In-Reply-To: <0815f1eb4b60faa653ea703e420395b724d05216.1775648513.git.pabeni@redhat.com>
On Wed, 8 Apr 2026 13:43:45 +0200 Paolo Abeni wrote:
> +static void i40e_get_queue_stats_tx(struct net_device *dev, int idx,
> + struct netdev_queue_stats_tx *tx)
> +{
> + struct i40e_netdev_priv *np = netdev_priv(dev);
> + struct i40e_vsi *vsi = np->vsi;
> + struct i40e_ring *tx_ring;
> +
> + rcu_read_lock();
> + tx_ring = READ_ONCE(vsi->tx_rings[idx]);
> + if (!tx_ring)
> + goto out;
> +
> + i40e_zero_tx_ring_stats(tx);
> + i40e_add_tx_ring_stats(tx_ring, tx);
> +
> + if (i40e_enabled_xdp_vsi(vsi)) {
> + tx_ring = READ_ONCE(vsi->xdp_rings[idx]);
> + if (tx_ring)
> + i40e_add_tx_ring_stats(tx_ring, tx);
If XDP Tx happens on dedicated queues it should be added to base,
not to the stats of the "stack" queue. This is in anticipation of
XDP being its own queue type one day, we'll then isolate those
out of base. Ripping the out of TX could cause regressions.
> + }
> +
> +out:
> + rcu_read_unlock();
> +}
> +
> +static void i40e_get_base_stats(struct net_device *dev,
> + struct netdev_queue_stats_rx *rx,
> + struct netdev_queue_stats_tx *tx)
> +{
> + struct i40e_netdev_priv *np = netdev_priv(dev);
> + struct i40e_vsi *vsi = np->vsi;
> +
> + tx->bytes = vsi->tx_bytes;
> + tx->packets = vsi->tx_packets;
> + tx->wake = vsi->tx_restart_base;
> + tx->stop = vsi->tx_stopped_base;
> + tx->hw_drops = vsi->tx_busy_base;
> +
> + rx->bytes = vsi->rx_bytes;
> + rx->packets = vsi->rx_packets;
> +}
^ permalink raw reply
* Re: [PATCH net-next v2 5/5] ethtool: strset: check nla_len overflow
From: Jakub Kicinski @ 2026-04-09 0:39 UTC (permalink / raw)
To: Stanislav Fomichev
Cc: Hangbin Liu, Donald Hunter, David S. Miller, Eric Dumazet,
Paolo Abeni, Simon Horman, Andrew Lunn, netdev, linux-kernel
In-Reply-To: <adaFjwkOrPoBgzoc@devvm17672.vll0.facebook.com>
On Wed, 8 Apr 2026 09:43:35 -0700 Stanislav Fomichev wrote:
> On 04/08, Hangbin Liu wrote:
> > The netlink attribute length field nla_len is a __u16, which can only
> > represent values up to 65535 bytes. NICs with a large number of
> > statistics strings (e.g. mlx5_core with thousands of ETH_SS_STATS
> > entries) can produce a ETHTOOL_A_STRINGSET_STRINGS nest that exceeds
> > this limit.
> >
> > When nla_nest_end() writes the actual nest size back to nla_len, the
> > value is silently truncated. This results in a corrupted netlink message
> > being sent to userspace: the parser reads a wrong (truncated) attribute
> > length and misaligns all subsequent attribute boundaries, causing decode
> > errors.
> >
> > Fix this by using the new helper nla_nest_end_safe and error out if
> > the size exceeds U16_MAX.
>
> Not sure that's the user supposed to do? Does it mean there is no way
> to retrieve ETHTOOL_A_STRINGSET_STRINGS for those devices with too
> many strings?
Not via Netlink, they can still read them via the ioctl?
Since the legacy stats themselves can't be fetched over Netlink
I'm not sure we should lose sleep over reading the stats strings
via Netlink.
^ permalink raw reply
* Re: [PATCH net-next v2 1/5] tools: ynl: move ethtool.py to selftest
From: Jakub Kicinski @ 2026-04-09 0:37 UTC (permalink / raw)
To: Stanislav Fomichev
Cc: Hangbin Liu, Donald Hunter, David S. Miller, Eric Dumazet,
Paolo Abeni, Simon Horman, Andrew Lunn, netdev, linux-kernel
In-Reply-To: <adaFLI2b5eb2mZHx@devvm17672.vll0.facebook.com>
On Wed, 8 Apr 2026 09:42:33 -0700 Stanislav Fomichev wrote:
> > @@ -8,7 +8,7 @@ KSELFTEST_KTAP_HELPERS="$(dirname "$(realpath "$0")")/../../../testing/selftests
> > source "$KSELFTEST_KTAP_HELPERS"
> >
> > # Default ynl-ethtool path for direct execution, can be overridden by make install
> > -ynl_ethtool="../pyynl/ethtool.py"
> > +ynl_ethtool="./ethtool.py"
> >
> > readonly NSIM_ID="1337"
> > readonly NSIM_DEV_NAME="nsim${NSIM_ID}"
>
> Do we need to add some expects/asserts to the script to really make it into
> a test? Right now it just prints things, so it's not really a test.
This file is full of asserts? It's a bash script that runs ethtool.py
and checks the output. Which one of us is missing the point ? :)
^ permalink raw reply
* Re: [PATCH net-next v6 00/10] Decouple receive and transmit enablement in team driver
From: Jakub Kicinski @ 2026-04-09 0:31 UTC (permalink / raw)
To: Marc Harvey
Cc: Jiri Pirko, Andrew Lunn, David S. Miller, Eric Dumazet,
Paolo Abeni, Shuah Khan, Simon Horman, netdev, linux-kernel,
linux-kselftest
In-Reply-To: <CANkEMgk16j2xzQ85JGQ4OWqeiwiVO5Gy-UfkN3omKkKizpLxiQ@mail.gmail.com>
On Wed, 8 Apr 2026 17:10:05 -0700 Marc Harvey wrote:
> On Wed, Apr 8, 2026 at 9:40 AM Jakub Kicinski <kuba@kernel.org> wrote:
> > It pains me to report on non-debug kernels:
>
> I'm sorry to have pained you.
To be clear it's a compassionate pain on your behalf, I don' care :)
> Despite my best efforts to run with the exact same environment and
> conditions as your CI, my teamd can be killed with "teamd -k" but
> yours hangs (both are version 1.32 on Fedora with the same kernel
> config). For v7, I’ll invoke "teamd -k" using the timeout utility, or
> just increase the test timeout.
^ permalink raw reply
* Re: [RFC net-next 2/4] selftests: drv-net: tso: add helpers for double tunneling GSO
From: Jakub Kicinski @ 2026-04-09 0:27 UTC (permalink / raw)
To: Xu Du
Cc: davem, edumazet, pabeni, horms, shuah, netdev, linux-kselftest,
linux-kernel
In-Reply-To: <CAA92KxmXOpH=CUqEdVV2948XhU1oFh8PBYLUt=ktSsK3sOh40g@mail.gmail.com>
On Wed, 8 Apr 2026 10:04:09 +0800 Xu Du wrote:
> On Tue, Apr 7, 2026 at 11:08 PM Jakub Kicinski <kuba@kernel.org> wrote:
> >
> > On Tue, 7 Apr 2026 10:45:09 +0800 Xu Du wrote:
> > > As the YNL Python module cannot be invoked across different devices or
> > > environments directly in its current form, the helper abstracts the
> > > YNL CLI calls to ensure proper configuration of the tunneling device
> > > features.
> >
> > Can you explain more? Why can't you use class RtnlFamily?
>
> I want to test the gro-hint parameter functionality of the GENEVE tunnel,
> so I intend to use YNL for the testing. I am conducting the test between
> two machines using SSH type. I want to add the gro-hint parameter on
> both the local and remote nodes; however, I am unable to invoke class
> RtnlFamily on the remote node via SSH.
Oh. But that's not really what you're doing:
+def ynlcli(family, args, json=None, ns=None, host=None):
+ if (KSFT_DIR / "kselftest-list.txt").exists():
+ cli = KSFT_DIR / "net/lib/ynl/pyynl/cli.py"
+ spec = KSFT_DIR / f"net/lib/specs/{family}.yaml"
+ else:
+ cli = KSRC / "tools/net/ynl/pyynl/cli.py"
+ spec = KSRC / f"Documentation/netlink/specs/{family}.yaml"
+ if not cli.exists():
+ raise FileNotFoundError(f"cli not found at {cli}")
+ args = f"--spec {spec} --no-schema {args}"
+ return tool(cli.as_posix(), args, json=json, ns=ns, host=host, shell=True)
You're not deploying anything to the remote system.
Are you assuming that the remote system magically has the same
filesystem layout?
You can use the ynl CLI but it has to be whatever version is on
the remote system. Just call ynl --family rt-link, don't dig
around for the spec paths etc.
^ permalink raw reply
* Re: [PATCH net-next v6 00/10] Decouple receive and transmit enablement in team driver
From: Marc Harvey @ 2026-04-09 0:10 UTC (permalink / raw)
To: Jakub Kicinski
Cc: Jiri Pirko, Andrew Lunn, David S. Miller, Eric Dumazet,
Paolo Abeni, Shuah Khan, Simon Horman, netdev, linux-kernel,
linux-kselftest
In-Reply-To: <20260408094015.3b8359c1@kernel.org>
On Wed, Apr 8, 2026 at 9:40 AM Jakub Kicinski <kuba@kernel.org> wrote:
>
> It pains me to report on non-debug kernels:
I'm sorry to have pained you. Despite my best efforts to run with the
exact same environment and conditions as your CI, my teamd can be
killed with "teamd -k" but yours hangs (both are version 1.32 on
Fedora with the same kernel config). For v7, I’ll invoke "teamd -k"
using the timeout utility, or just increase the test timeout.
^ permalink raw reply
* [PATCH] MAINTAINERS: Remove Salil Mehta as HiSilicon HNS3/HNS Ethernet maintainer
From: Salil Mehta @ 2026-04-09 0:04 UTC (permalink / raw)
To: davem, netdev, kuba; +Cc: salil.mehta, shenjian15, shaojijie, Salil Mehta
From: Salil Mehta <salil.mehta@opnsrc.net>
Closing this chapter and a long wonderful journey with my team, I sign off one
last time with my Huawei email address. Remove my maintainer entry for the
HiSilicon HNS and HNS3 10G/100G Ethernet drivers, and add a CREDITS entry for
my co-authorship and maintenance contributions to these drivers.
Link: https://lore.kernel.org/netdev/259cd032-2ccb-452b-8524-75bc7162e138@huawei.com/
Cc: Jian Shen <shenjian15@huawei.com>
Cc: Jijie Shao <shaojijie@huawei.com>
Signed-off-by: Salil Mehta <salil.mehta@huawei.com>
---
CREDITS | 10 ++++++++++
MAINTAINERS | 2 --
2 files changed, 10 insertions(+), 2 deletions(-)
diff --git a/CREDITS b/CREDITS
index 9091bac3d2da..a03b00452a1e 100644
--- a/CREDITS
+++ b/CREDITS
@@ -3592,6 +3592,16 @@ E: wsalamon@tislabs.com
E: wsalamon@nai.com
D: portions of the Linux Security Module (LSM) framework and security modules
+N: Salil Mehta
+E: salil.mehta@opnsrc.net
+D: Co-authored Huawei/HiSilicon Kunpeng 920 SoC HNS3 PF and VF 100G
+D: Ethernet driver
+D: Co-authored Huawei/HiSilicon Kunpeng 916 SoC HNS 10G Ethernet
+D: driver enhancements
+D: Maintained Huawei/HiSilicon HNS and HNS3 10G/100G Ethernet drivers
+D: for Kunpeng 916 family, 920 family of SoCs
+S: Cambridge, Cambridgeshire, United Kingdom
+
N: Robert Sanders
E: gt8134b@prism.gatech.edu
D: Dosemu
diff --git a/MAINTAINERS b/MAINTAINERS
index 9d1e6d3acbac..97d0bc3108de 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -11530,7 +11530,6 @@ F: drivers/bus/hisi_lpc.c
HISILICON NETWORK SUBSYSTEM 3 DRIVER (HNS3)
M: Jian Shen <shenjian15@huawei.com>
-M: Salil Mehta <salil.mehta@huawei.com>
M: Jijie Shao <shaojijie@huawei.com>
L: netdev@vger.kernel.org
S: Maintained
@@ -11545,7 +11544,6 @@ F: drivers/net/ethernet/hisilicon/hibmcge/
HISILICON NETWORK SUBSYSTEM DRIVER
M: Jian Shen <shenjian15@huawei.com>
-M: Salil Mehta <salil.mehta@huawei.com>
L: netdev@vger.kernel.org
S: Maintained
W: http://www.hisilicon.com
--
2.34.1
^ permalink raw reply related
* Re: [PATCH net v3 1/2] seg6: separate dst_cache for input and output paths in seg6 lwtunnel
From: Jakub Kicinski @ 2026-04-09 0:08 UTC (permalink / raw)
To: Andrea Mayer
Cc: netdev, davem, edumazet, pabeni, horms, dsahern, david.lebrun,
stefano.salsano, paolo.lungaroni, nicolas.dichtel, justin.iurman,
linux-kernel, shuah, linux-kselftest
In-Reply-To: <20260409012708.78040268c05e3285742157ae@uniroma2.it>
On Thu, 9 Apr 2026 01:27:08 +0200 Andrea Mayer wrote:
> It does seem orthogonal to the dst_cache split and worth investigating.
> I'll take a look.
I fingered the review send command, to be clear the series we're
commenting on has been applied already.
^ permalink raw reply
* Re: [PATCH net-next v2 3/4] bpf-timestamp: keep track of the skb when wait_for_space occurs
From: Jason Xing @ 2026-04-08 23:52 UTC (permalink / raw)
To: Martin KaFai Lau
Cc: Willem de Bruijn, Jakub Sitnicki, davem, edumazet, kuba, pabeni,
horms, willemb, martin.lau, netdev, bpf, Jason Xing, Yushan Zhou
In-Reply-To: <20264818422.Ya8u.martin.lau@linux.dev>
On Thu, Apr 9, 2026 at 2:13 AM Martin KaFai Lau <martin.lau@linux.dev> wrote:
>
> On Wed, Apr 08, 2026 at 11:15:09AM -0400, Willem de Bruijn wrote:
> > > Avoiding adding a new one makes the whole work extremely hard. I'm
> > > wondering since we have hwtstamp in shared info, why not add a
> > > software one for timestamping use? Then, we would support more
> > > different protocols in more different stages in a finer grain, which
> > > is a big coarse picture in my mind.
> >
> > I don't understand the need to store more data in the skb for BPF.
My take is that it's not for BPF only. BPF is an approach that achieves
network observability safely. The ultimate goal is to serve the
network area.
>
> Adding a field specific to bpf timestamping is not scalable.
> There will always be other bpf use cases that need to store
> something in a skb.
>
> There have been discussions about storing metadata for a skb which should
> solve the general bpf use cases.
>
> https://msgid.link/20260226-skb-local-storage-v1-0-4ca44f0dd9d1@cloudflare.com/
> https://msgid.link/20260110-skb-meta-fixup-skb_metadata_set-calls-v1-0-1047878ed1b0@cloudflare.com/
I've been tracing this feature continuously since Jakub gave a talk in
netdev 0x19. We've discussed a bit there with the overall plan. Yes, in
theory, it works. But if we put it into practice, I totally have no idea.
In terms of the latest patchset, it doesn't meet my requirement:
1. in the stages below TCP stack, we're unable to read the timestamp
recorded in the orig skb.
2. it only serves the low rate flow, which is definitely not what I
expect. Please see the original verification from the cover letter:
"Rounding up to ~500 nsec per packet:
- at 100k pps, that's 5% of the 10 usec per-packet budget, but
- at 1 Mpps, that's already 50% of the budget, which is not acceptable.
While definitely not suitable for high-pps flows, the naive skb local
storage implementation is arguably acceptable at low rates, for example
when you need to attach metadata only to the first packet of a TCP/QUIC
connection or sample packets at very low rates for tracing."
As I replied to Willem, the look-up process is also very
time-consuming. It can be a temporary tool but not a always-on plan in
production.
Thanks,
Jason
^ permalink raw reply
* RE: [Intel-wired-lan] [PATCH iwl-net] ice: update PCS latency settings for E825 10G/25Gb modes
From: Mekala, SunithaX D @ 2026-04-08 23:45 UTC (permalink / raw)
To: Nitka, Grzegorz, intel-wired-lan@lists.osuosl.org
Cc: netdev@vger.kernel.org, Loktionov, Aleksandr, Nguyen, Anthony L,
Fodor, Zoltan, Keller, Jacob E
In-Reply-To: <20260217225956.1593920-1-grzegorz.nitka@intel.com>
> -----Original Message-----
> From: Intel-wired-lan <intel-wired-lan-bounces@osuosl.org> On Behalf Of Grzegorz Nitka
> Sent: Tuesday, February 17, 2026 3:00 PM
> To: intel-wired-lan@lists.osuosl.org
> Cc: netdev@vger.kernel.org; Loktionov, Aleksandr <aleksandr.loktionov@intel.com>; Nguyen, Anthony L <anthony.l.nguyen@intel.com>; Fodor, Zoltan <zoltan.fodor@intel.com>; Keller, Jacob E > <jacob.e.keller@intel.com>
> Subject: [Intel-wired-lan] [PATCH iwl-net] ice: update PCS latency settings for E825 10G/25Gb modes
>
> Update MAC Rx/Tx offset registers settings (PHY_MAC_[RX|TX]_OFFSET
> registers) with the data obtained with the latest research. It applies
> to PCS latency settings for the following speeds/modes:
> * 10Gb NO-FEC
> - TX latency changed from 71.25 ns to 73 ns
> - RX latency changed from -25.6 ns to -28 ns
> * 25Gb NO-FEC
> - TX latency changed from 28.17 ns to 33 ns
> - RX latency changed from -12.45 ns to -12 ns
> * 25Gb RS-FEC
> - TX latency changed from 64.5 ns to 69 ns
> - RX latency changed from -3.6 ns to -3 ns
>
> The original data came from simulation and pre-production hardware.
> The new data measures the actual delays and as such is more accurate.
>
> Fixes: 7cab44f1c35f ("ice: Introduce ETH56G PHY model for E825C products")
> Co-developed-by: Zoltan Fodor <zoltan.fodor@intel.com>
> Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
> Reviewed-by: Jacob Keller <jacob.e.keller@intel.com>
> Signed-off-by: Zoltan Fodor <zoltan.fodor@intel.com>
> Signed-off-by: Grzegorz Nitka <grzegorz.nitka@intel.com>
> ---
> drivers/net/ethernet/intel/ice/ice_ptp_consts.h | 12 ++++++------
> 1 file changed, 6 insertions(+), 6 deletions(-)
Tested-by: Sunitha Mekala <sunithax.d.mekala@intel.com> (A Contingent worker at Intel)
^ permalink raw reply
* [PATCH net] net: ethernet: mtk_eth_soc: initialize PPE per-tag-layer MTU registers
From: Daniel Golle @ 2026-04-08 23:33 UTC (permalink / raw)
To: Felix Fietkau, Lorenzo Bianconi, Andrew Lunn, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Matthias Brugger,
AngeloGioacchino Del Regno, Pablo Neira Ayuso, netdev,
linux-kernel, linux-arm-kernel, linux-mediatek
Cc: Chad Monroe, Elad Yifee, João Duarte, John Crispin
The PPE enforces output frame size limits via per-tag-layer VLAN_MTU
registers that the driver never initializes. The hardware defaults do
not account for PPPoE overhead, causing the PPE to punt encapsulated
frames back to the CPU instead of forwarding them.
Initialize the registers at PPE start and on MTU changes using the
maximum GMAC MTU. This is a conservative approximation -- the actual
per-PPE requirement depends on egress path, but using the global
maximum ensures the limits are never too small.
Fixes: ba37b7caf1ed2 ("net: ethernet: mtk_eth_soc: add support for initializing the PPE")
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
drivers/net/ethernet/mediatek/mtk_eth_soc.c | 22 ++++++++++++++-
drivers/net/ethernet/mediatek/mtk_ppe.c | 30 +++++++++++++++++++++
drivers/net/ethernet/mediatek/mtk_ppe.h | 1 +
3 files changed, 52 insertions(+), 1 deletion(-)
diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
index 1767463475de4..06880fa86f0ea 100644
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
@@ -3922,12 +3922,23 @@ static int mtk_device_event(struct notifier_block *n, unsigned long event, void
return NOTIFY_DONE;
}
+static int mtk_max_gmac_mtu(struct mtk_eth *eth)
+{
+ int i, max_mtu = ETH_DATA_LEN;
+
+ for (i = 0; i < ARRAY_SIZE(eth->netdev); i++)
+ if (eth->netdev[i] && eth->netdev[i]->mtu > max_mtu)
+ max_mtu = eth->netdev[i]->mtu;
+
+ return max_mtu;
+}
+
static int mtk_open(struct net_device *dev)
{
struct mtk_mac *mac = netdev_priv(dev);
struct mtk_eth *eth = mac->hw;
struct mtk_mac *target_mac;
- int i, err, ppe_num;
+ int i, err, ppe_num, mtu;
ppe_num = eth->soc->ppe_num;
@@ -3974,6 +3985,10 @@ static int mtk_open(struct net_device *dev)
mtk_gdm_config(eth, target_mac->id, gdm_config);
}
+ mtu = mtk_max_gmac_mtu(eth);
+ for (i = 0; i < ARRAY_SIZE(eth->ppe); i++)
+ mtk_ppe_update_mtu(eth->ppe[i], mtu);
+
napi_enable(ð->tx_napi);
napi_enable(ð->rx_napi[0].napi);
mtk_tx_irq_enable(eth, MTK_TX_DONE_INT);
@@ -4788,6 +4803,7 @@ static int mtk_change_mtu(struct net_device *dev, int new_mtu)
int length = new_mtu + MTK_RX_ETH_HLEN;
struct mtk_mac *mac = netdev_priv(dev);
struct mtk_eth *eth = mac->hw;
+ int max_mtu, i;
if (rcu_access_pointer(eth->prog) &&
length > MTK_PP_MAX_BUF_SIZE) {
@@ -4798,6 +4814,10 @@ static int mtk_change_mtu(struct net_device *dev, int new_mtu)
mtk_set_mcr_max_rx(mac, length);
WRITE_ONCE(dev->mtu, new_mtu);
+ max_mtu = mtk_max_gmac_mtu(eth);
+ for (i = 0; i < ARRAY_SIZE(eth->ppe); i++)
+ mtk_ppe_update_mtu(eth->ppe[i], max_mtu);
+
return 0;
}
diff --git a/drivers/net/ethernet/mediatek/mtk_ppe.c b/drivers/net/ethernet/mediatek/mtk_ppe.c
index 79c11e179d894..9e829a2e4354e 100644
--- a/drivers/net/ethernet/mediatek/mtk_ppe.c
+++ b/drivers/net/ethernet/mediatek/mtk_ppe.c
@@ -973,6 +973,36 @@ static void mtk_ppe_init_foe_table(struct mtk_ppe *ppe)
}
}
+void mtk_ppe_update_mtu(struct mtk_ppe *ppe, int mtu)
+{
+ int base;
+ u32 val;
+
+ if (!ppe)
+ return;
+
+ /* The PPE checks output frame size against per-tag-layer MTU limits,
+ * treating PPPoE and DSA tags just like 802.1Q VLAN tags. The Linux
+ * device MTU already accounts for PPPoE (PPPOE_SES_HLEN) and DSA tag
+ * overhead, but 802.1Q VLAN tags are handled transparently without
+ * being reflected by the lower device MTU being increased by 4.
+ * Use the maximum MTU across all GMAC interfaces so that PPE output
+ * frame limits are sufficiently high regardless of which port a flow
+ * egresses through.
+ */
+ base = ETH_HLEN + mtu;
+
+ val = FIELD_PREP(MTK_PPE_VLAN_MTU0_NONE, base) |
+ FIELD_PREP(MTK_PPE_VLAN_MTU0_1TAG, base + VLAN_HLEN);
+ ppe_w32(ppe, MTK_PPE_VLAN_MTU0, val);
+
+ val = FIELD_PREP(MTK_PPE_VLAN_MTU1_2TAG,
+ base + 2 * VLAN_HLEN) |
+ FIELD_PREP(MTK_PPE_VLAN_MTU1_3TAG,
+ base + 3 * VLAN_HLEN);
+ ppe_w32(ppe, MTK_PPE_VLAN_MTU1, val);
+}
+
void mtk_ppe_start(struct mtk_ppe *ppe)
{
u32 val;
diff --git a/drivers/net/ethernet/mediatek/mtk_ppe.h b/drivers/net/ethernet/mediatek/mtk_ppe.h
index 223f709e2704f..ba85e39a155bf 100644
--- a/drivers/net/ethernet/mediatek/mtk_ppe.h
+++ b/drivers/net/ethernet/mediatek/mtk_ppe.h
@@ -346,6 +346,7 @@ struct mtk_ppe {
struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base, int index);
void mtk_ppe_deinit(struct mtk_eth *eth);
+void mtk_ppe_update_mtu(struct mtk_ppe *ppe, int mtu);
void mtk_ppe_start(struct mtk_ppe *ppe);
int mtk_ppe_stop(struct mtk_ppe *ppe);
int mtk_ppe_prepare_reset(struct mtk_ppe *ppe);
--
2.53.0
^ permalink raw reply related
* Re: [PATCH net v3 1/2] seg6: separate dst_cache for input and output paths in seg6 lwtunnel
From: Andrea Mayer @ 2026-04-08 23:27 UTC (permalink / raw)
To: Jakub Kicinski
Cc: netdev, davem, edumazet, pabeni, horms, dsahern, david.lebrun,
stefano.salsano, paolo.lungaroni, nicolas.dichtel, justin.iurman,
linux-kernel, shuah, linux-kselftest, Andrea Mayer
In-Reply-To: <20260408032039.314762-1-kuba@kernel.org>
On Tue, 7 Apr 2026 20:20:38 -0700
Jakub Kicinski <kuba@kernel.org> wrote:
> This is an AI-generated review of your patch. The human sending this
> email says "Seems orthogonal but also legit"
> ---
> seg6: separate dst_cache for input and output paths in seg6 lwtunnel
>
> This commit splits the shared seg6 lwtunnel dst_cache into separate input
> and output caches to prevent blind reuse across different routing contexts.
>
> > diff --git a/net/ipv6/seg6_iptunnel.c b/net/ipv6/seg6_iptunnel.c
> > index 3e1b9991131a2..d6a0f7df90807 100644
> > --- a/net/ipv6/seg6_iptunnel.c
> > +++ b/net/ipv6/seg6_iptunnel.c
>
> [ ... ]
>
> > @@ -504,7 +505,7 @@ static int seg6_input_core(struct net *net, struct sock *sk,
> > /* cache only if we don't create a dst reference loop */
> > if (!dst->error && lwtst != dst->lwtstate) {
> > local_bh_disable();
> > - dst_cache_set_ip6(&slwt->cache, dst,
> > + dst_cache_set_ip6(&slwt->cache_input, dst,
> > &ipv6_hdr(skb)->saddr);
> > local_bh_enable();
> > }
>
> This code appears to cache the dst entry resolved just prior by calling
> ip6_route_input(skb).
>
> Does ip6_route_input() assign a NOREF dst to the skb?
>
> If so, this dst is protected only by RCU. If the underlying route is
> concurrently deleted, its refcount can drop to zero. When
> dst_cache_set_ip6() is called, it attempts to use dst_hold(dst).
>
> If the refcount is already zero, dst_hold() will warn and skip incrementing
> the refcount, but dst_cache_set_ip6() will still store the pointer.
>
> Could this cause the cache to hold a dangling pointer, leading to a
> use-after-free once the RCU grace period expires?
>
> Should this path use skb_dst_force(skb) or dst_hold_safe(dst) to safely
> upgrade the dst to a refcounted reference before it is cached?
> --
> pw-bot: cr
Thanks Jakub.
It does seem orthogonal to the dst_cache split and worth investigating.
I'll take a look.
Andrea
^ permalink raw reply
* Re: [PATCH net-next v11 14/14] selftests/net: Add queue leasing tests with netkit
From: Jakub Kicinski @ 2026-04-08 23:22 UTC (permalink / raw)
To: Daniel Borkmann
Cc: netdev, bpf, davem, razor, pabeni, willemb, sdf, john.fastabend,
martin.lau, jordan, maciej.fijalkowski, magnus.karlsson, dw, toke,
yangzhenze, wangdongdong.6
In-Reply-To: <20260402231031.447597-15-daniel@iogearbox.net>
On Fri, 3 Apr 2026 01:10:31 +0200 Daniel Borkmann wrote:
> + ksft_run(
> + [
> + test_remove_phys,
> + test_double_lease,
> + test_virtual_lessor,
> + test_phys_lessee,
> + test_different_lessors,
> + test_queue_out_of_range,
> + test_resize_leased,
> + # test_destroy must be last because it destroys the netkit devices
> + ksft_run(
> + [test_iou_zcrx, test_attrs, test_attach_xdp_with_mp, test_destroy],
> + args=(cfg,),
> + )
> + ksft_exit()
ksft_run() can't be called multiple times.
The first run looks like it's purely testing netdevsim. So that should
move to selftests/net. The rest which tests HW should stay here.
Please also move all the setup inside the test cases.
^ permalink raw reply
* [PATCH v11 net-next 5/5] selftest/net: psp: Add test for dev-assoc/disassoc
From: Wei Wang @ 2026-04-08 23:14 UTC (permalink / raw)
To: netdev, Jakub Kicinski, Daniel Zahka, Willem de Bruijn, David Wei,
Andrew Lunn, David S . Miller, Eric Dumazet, Simon Horman
Cc: Wei Wang
In-Reply-To: <20260408231415.522691-1-weibunny.kernel@gmail.com>
From: Wei Wang <weibunny@fb.com>
Add a new param to NetDrvContEnv to add an additional bpf redirect
program on nk_host to redirect traffic to the psp_dev_local.
The topology looks like this:
Host NS: psp_dev_local <---> nk_host
| |
| | (netkit pair)
| |
Remote NS: psp_dev_peer Guest NS: nk_guest
(responder) (PSP tests)
Add following tests for dev-assoc/dev-disassoc functionality:
1. Test the output of `./tools/net/ynl/pyynl/cli.py --spec
Documentation/netlink/specs/psp.yaml --dump dev-get` in both default and
the guest netns.
2. Test the case where we associate netkit with psp_dev_local, and
send PSP traffic from nk_guest to psp_dev_peer in 2 different netns.
3. Test to make sure the key rotation notification is sent to the netns
for associated dev as well
4. Test to make sure the dev change notification is sent to the netns
for associated dev as well
5. Test for dev-assoc/dev-disassoc without nsid parameter.
6. Test the deletion of nk_guest in client netns, and proper cleanup in
the assoc-list for psp dev.
Signed-off-by: Wei Wang <weibunny@fb.com>
---
tools/testing/selftests/drivers/net/config | 1 +
.../selftests/drivers/net/lib/py/env.py | 54 ++-
tools/testing/selftests/drivers/net/psp.py | 457 ++++++++++++++++--
3 files changed, 478 insertions(+), 34 deletions(-)
diff --git a/tools/testing/selftests/drivers/net/config b/tools/testing/selftests/drivers/net/config
index 77ccf83d87e0..cdde8234dc07 100644
--- a/tools/testing/selftests/drivers/net/config
+++ b/tools/testing/selftests/drivers/net/config
@@ -7,4 +7,5 @@ CONFIG_NETCONSOLE=m
CONFIG_NETCONSOLE_DYNAMIC=y
CONFIG_NETCONSOLE_EXTENDED_LOG=y
CONFIG_NETDEVSIM=m
+CONFIG_NETKIT=y
CONFIG_XDP_SOCKETS=y
diff --git a/tools/testing/selftests/drivers/net/lib/py/env.py b/tools/testing/selftests/drivers/net/lib/py/env.py
index 6a71c7e7f136..a70d776cf6b8 100644
--- a/tools/testing/selftests/drivers/net/lib/py/env.py
+++ b/tools/testing/selftests/drivers/net/lib/py/env.py
@@ -2,6 +2,7 @@
import ipaddress
import os
+import re
import time
import json
from pathlib import Path
@@ -327,7 +328,7 @@ class NetDrvContEnv(NetDrvEpEnv):
+---------------+
"""
- def __init__(self, src_path, rxqueues=1, **kwargs):
+ def __init__(self, src_path, rxqueues=1, install_tx_redirect_bpf=False, **kwargs):
self.netns = None
self._nk_host_ifname = None
self._nk_guest_ifname = None
@@ -338,6 +339,8 @@ class NetDrvContEnv(NetDrvEpEnv):
self._init_ns_attached = False
self._old_fwd = None
self._old_accept_ra = None
+ self._nk_host_tc_attached = False
+ self._nk_host_bpf_prog_pref = None
super().__init__(src_path, **kwargs)
@@ -388,7 +391,13 @@ class NetDrvContEnv(NetDrvEpEnv):
self._setup_ns()
self._attach_bpf()
+ if install_tx_redirect_bpf:
+ self._attach_tx_redirect_bpf()
+
def __del__(self):
+ if self._nk_host_tc_attached:
+ cmd(f"tc filter del dev {self._nk_host_ifname} ingress pref {self._nk_host_bpf_prog_pref}", fail=False)
+ self._nk_host_tc_attached = False
if self._tc_attached:
cmd(f"tc filter del dev {self.ifname} ingress pref {self._bpf_prog_pref}")
self._tc_attached = False
@@ -496,3 +505,46 @@ class NetDrvContEnv(NetDrvEpEnv):
value = ipv6_bytes + ifindex_bytes
value_hex = ' '.join(f'{b:02x}' for b in value)
bpftool(f"map update id {bss_map_id} key hex 00 00 00 00 value hex {value_hex}")
+
+ def _attach_tx_redirect_bpf(self):
+ """
+ Attach BPF program on nk_host ingress to redirect TX traffic.
+
+ Packets from nk_guest destined for the nsim network arrive at nk_host
+ via the netkit pair. This BPF program redirects them to the physical
+ interface so they can reach the remote peer.
+ """
+ bpf_obj = self.test_dir / "nk_redirect.bpf.o"
+ if not bpf_obj.exists():
+ raise KsftSkipEx("BPF prog nk_redirect.bpf.o not found")
+
+ cmd(f"tc qdisc add dev {self._nk_host_ifname} clsact")
+
+ cmd(f"tc filter add dev {self._nk_host_ifname} ingress bpf obj {bpf_obj} sec tc/ingress direct-action")
+ self._nk_host_tc_attached = True
+
+ tc_info = cmd(f"tc filter show dev {self._nk_host_ifname} ingress").stdout
+ match = re.search(r'pref (\d+).*nk_redirect\.bpf.*id (\d+)', tc_info)
+ if not match:
+ raise Exception("Failed to get TX redirect BPF prog ID")
+ self._nk_host_bpf_prog_pref = int(match.group(1))
+ nk_host_bpf_prog_id = int(match.group(2))
+
+ prog_info = bpftool(f"prog show id {nk_host_bpf_prog_id}", json=True)
+ map_ids = prog_info.get("map_ids", [])
+
+ bss_map_id = None
+ for map_id in map_ids:
+ map_info = bpftool(f"map show id {map_id}", json=True)
+ if map_info.get("name").endswith("bss"):
+ bss_map_id = map_id
+
+ if bss_map_id is None:
+ raise Exception("Failed to find TX redirect BPF .bss map")
+
+ ipv6_addr = ipaddress.IPv6Address(self.nsim_v6_pfx)
+ ipv6_bytes = ipv6_addr.packed
+ ifindex_bytes = self.ifindex.to_bytes(4, byteorder='little')
+ value = ipv6_bytes + ifindex_bytes
+ value_hex = ' '.join(f'{b:02x}' for b in value)
+ bpftool(f"map update id {bss_map_id} key hex 00 00 00 00 value hex {value_hex}")
diff --git a/tools/testing/selftests/drivers/net/psp.py b/tools/testing/selftests/drivers/net/psp.py
index 864d9fce1094..79da4d425c50 100755
--- a/tools/testing/selftests/drivers/net/psp.py
+++ b/tools/testing/selftests/drivers/net/psp.py
@@ -5,6 +5,7 @@
import errno
import fcntl
+import os
import socket
import struct
import termios
@@ -14,9 +15,12 @@ from lib.py import defer
from lib.py import ksft_run, ksft_exit, ksft_pr
from lib.py import ksft_true, ksft_eq, ksft_ne, ksft_gt, ksft_raises
from lib.py import ksft_not_none
-from lib.py import KsftSkipEx
-from lib.py import NetDrvEpEnv, PSPFamily, NlError
+from lib.py import ksft_variants, KsftNamedVariant
+from lib.py import KsftSkipEx, KsftFailEx
+from lib.py import NetDrvEpEnv, NetDrvContEnv, PSPFamily, NlError
+from lib.py import NetNSEnter
from lib.py import bkg, rand_port, wait_port_listen
+from lib.py import ip
def _get_outq(s):
@@ -117,11 +121,13 @@ def _get_stat(cfg, key):
# Test case boiler plate
#
-def _init_psp_dev(cfg):
+def _init_psp_dev(cfg, use_psp_ifindex=False):
if not hasattr(cfg, 'psp_dev_id'):
# Figure out which local device we are testing against
+ # For NetDrvContEnv: use psp_ifindex instead of ifindex
+ target_ifindex = cfg.psp_ifindex if use_psp_ifindex else cfg.ifindex
for dev in cfg.pspnl.dev_get({}, dump=True):
- if dev['ifindex'] == cfg.ifindex:
+ if dev['ifindex'] == target_ifindex:
cfg.psp_info = dev
cfg.psp_dev_id = cfg.psp_info['id']
break
@@ -394,6 +400,297 @@ def _data_basic_send(cfg, version, ipver):
_close_psp_conn(cfg, s)
+def _data_basic_send_netkit_psp_assoc(cfg, version, ipver):
+ """
+ Test basic data send with netkit interface associated with PSP dev.
+ """
+
+ _init_psp_dev(cfg, True)
+ psp_dev_id_for_assoc = cfg.psp_dev_id
+
+ # Associate PSP device with nk_guest interface (in guest namespace)
+ nk_guest_dev = ip(f"link show dev {cfg._nk_guest_ifname}", json=True, ns=cfg.netns)[0]
+ nk_guest_ifindex = nk_guest_dev['ifindex']
+
+ cfg.pspnl.dev_assoc({'id': psp_dev_id_for_assoc, 'ifindex': nk_guest_ifindex, 'nsid': cfg.psp_dev_peer_nsid})
+
+ # Check if assoc-list contains nk_guest
+ dev_info = cfg.pspnl.dev_get({'id': psp_dev_id_for_assoc})
+
+ if 'assoc-list' in dev_info:
+ found = False
+ for assoc in dev_info['assoc-list']:
+ if assoc['ifindex'] == nk_guest_ifindex and assoc['nsid'] == cfg.psp_dev_peer_nsid:
+ found = True
+ break
+ ksft_true(found, "Associated device not found in dev_get() response")
+ else:
+ raise RuntimeError("No assoc-list in dev_get() response after association")
+
+ # Enter guest namespace (netns) to run PSP test
+ with NetNSEnter(cfg.netns.name):
+ cfg.pspnl = PSPFamily()
+
+ s = _make_psp_conn(cfg, version, ipver)
+
+ rx_assoc = cfg.pspnl.rx_assoc({"version": version,
+ "dev-id": cfg.psp_dev_id,
+ "sock-fd": s.fileno()})
+ rx = rx_assoc['rx-key']
+ tx = _spi_xchg(s, rx)
+
+ cfg.pspnl.tx_assoc({"dev-id": cfg.psp_dev_id,
+ "version": version,
+ "tx-key": tx,
+ "sock-fd": s.fileno()})
+
+ data_len = _send_careful(cfg, s, 100)
+ _check_data_rx(cfg, data_len)
+ _close_psp_conn(cfg, s)
+
+ # Clean up - back in host namespace
+ cfg.pspnl = PSPFamily()
+ cfg.pspnl.dev_disassoc({'id': psp_dev_id_for_assoc, 'ifindex': nk_guest_ifindex, 'nsid': cfg.psp_dev_peer_nsid})
+
+ del cfg.psp_dev_id
+ del cfg.psp_info
+
+
+def _key_rotation_notify_multi_ns_netkit(cfg):
+ """ Test key rotation notifications across multiple namespaces using netkit """
+ _init_psp_dev(cfg, True)
+ psp_dev_id_for_assoc = cfg.psp_dev_id
+
+ # Associate PSP device with nk_guest interface (in guest namespace)
+ nk_guest_dev = ip(f"link show dev {cfg._nk_guest_ifname}", json=True, ns=cfg.netns)[0]
+ nk_guest_ifindex = nk_guest_dev['ifindex']
+
+ cfg.pspnl.dev_assoc({'id': psp_dev_id_for_assoc, 'ifindex': nk_guest_ifindex, 'nsid': cfg.psp_dev_peer_nsid})
+
+ # Create listener in guest namespace; socket stays bound to that ns
+ with NetNSEnter(cfg.netns.name):
+ peer_pspnl = PSPFamily()
+ peer_pspnl.ntf_subscribe('use')
+
+ # Create listener in main namespace
+ main_pspnl = PSPFamily()
+ main_pspnl.ntf_subscribe('use')
+
+ # Trigger key rotation on the PSP device
+ cfg.pspnl.key_rotate({"id": psp_dev_id_for_assoc})
+
+ # Poll both sockets from main thread
+ for pspnl, label in [(main_pspnl, "main"), (peer_pspnl, "guest")]:
+ for i in range(100):
+ pspnl.check_ntf()
+
+ try:
+ msg = pspnl.async_msg_queue.get_nowait()
+ break
+ except Exception:
+ pass
+
+ time.sleep(0.1)
+ else:
+ raise KsftFailEx(f"No key rotation notification received in {label} namespace")
+
+ ksft_true(msg['msg'].get('id') == psp_dev_id_for_assoc,
+ f"Key rotation notification for correct device not found in {label} namespace")
+
+ # Clean up
+ cfg.pspnl.dev_disassoc({'id': psp_dev_id_for_assoc, 'ifindex': nk_guest_ifindex, 'nsid': cfg.psp_dev_peer_nsid})
+ del cfg.psp_dev_id
+ del cfg.psp_info
+
+
+def _dev_change_notify_multi_ns_netkit(cfg):
+ """ Test dev_change notifications across multiple namespaces using netkit """
+ _init_psp_dev(cfg, True)
+ psp_dev_id_for_assoc = cfg.psp_dev_id
+
+ # Associate PSP device with nk_guest interface (in guest namespace)
+ nk_guest_dev = ip(f"link show dev {cfg._nk_guest_ifname}", json=True, ns=cfg.netns)[0]
+ nk_guest_ifindex = nk_guest_dev['ifindex']
+
+ cfg.pspnl.dev_assoc({'id': psp_dev_id_for_assoc, 'ifindex': nk_guest_ifindex, 'nsid': cfg.psp_dev_peer_nsid})
+
+ # Create listener in guest namespace; socket stays bound to that ns
+ with NetNSEnter(cfg.netns.name):
+ peer_pspnl = PSPFamily()
+ peer_pspnl.ntf_subscribe('mgmt')
+
+ # Create listener in main namespace
+ main_pspnl = PSPFamily()
+ main_pspnl.ntf_subscribe('mgmt')
+
+ # Trigger dev_change by calling dev_set (notification is always sent)
+ cfg.pspnl.dev_set({'id': psp_dev_id_for_assoc, 'psp-versions-ena': cfg.psp_info['psp-versions-cap']})
+
+ # Poll both sockets from main thread
+ for pspnl, label in [(main_pspnl, "main"), (peer_pspnl, "guest")]:
+ for i in range(100):
+ pspnl.check_ntf()
+
+ try:
+ msg = pspnl.async_msg_queue.get_nowait()
+ break
+ except Exception:
+ pass
+
+ time.sleep(0.1)
+ else:
+ raise KsftFailEx(f"No dev_change notification received in {label} namespace")
+
+ ksft_true(msg['msg'].get('id') == psp_dev_id_for_assoc,
+ f"Dev_change notification for correct device not found in {label} namespace")
+
+ # Clean up
+ cfg.pspnl.dev_disassoc({'id': psp_dev_id_for_assoc, 'ifindex': nk_guest_ifindex, 'nsid': cfg.psp_dev_peer_nsid})
+ del cfg.psp_dev_id
+ del cfg.psp_info
+
+
+def _psp_dev_get_check_netkit_psp_assoc(cfg):
+ """ Check psp dev-get output with netkit interface associated with PSP dev """
+
+ _init_psp_dev(cfg, True)
+ psp_dev_id_for_assoc = cfg.psp_dev_id
+
+ # Associate PSP device with nk_guest interface (in guest namespace)
+ nk_guest_dev = ip(f"link show dev {cfg._nk_guest_ifname}", json=True, ns=cfg.netns)[0]
+ nk_guest_ifindex = nk_guest_dev['ifindex']
+
+ cfg.pspnl.dev_assoc({'id': psp_dev_id_for_assoc, 'ifindex': nk_guest_ifindex, 'nsid': cfg.psp_dev_peer_nsid})
+
+ # Check 1: In default netns, verify dev-get has correct ifindex and assoc-list
+ dev_info = cfg.pspnl.dev_get({'id': psp_dev_id_for_assoc})
+
+ # Verify the PSP device has the correct ifindex
+ ksft_eq(dev_info['ifindex'], cfg.psp_ifindex)
+
+ # Verify assoc-list exists and contains the associated nk_guest with correct ifindex and nsid
+ ksft_true('assoc-list' in dev_info, "No assoc-list in dev_get() response after association")
+ found = False
+ for assoc in dev_info['assoc-list']:
+ if assoc['ifindex'] == nk_guest_ifindex and assoc['nsid'] == cfg.psp_dev_peer_nsid:
+ found = True
+ break
+ ksft_true(found, "Associated device not found in assoc-list with correct ifindex and nsid")
+
+ # Check 2: In guest netns, verify dev-get has assoc-list with nk_guest device
+ with NetNSEnter(cfg.netns.name):
+ peer_pspnl = PSPFamily()
+
+ # Dump all devices in the guest namespace
+ peer_devices = peer_pspnl.dev_get({}, dump=True)
+
+ # Find the device with by-association flag
+ peer_dev = None
+ for dev in peer_devices:
+ if dev.get('by-association'):
+ peer_dev = dev
+ break
+
+ ksft_not_none(peer_dev, "No PSP device found with by-association flag in guest netns")
+
+ # Verify assoc-list contains the nk_guest device
+ ksft_true('assoc-list' in peer_dev and len(peer_dev['assoc-list']) > 0,
+ "Guest device should have assoc-list with local devices")
+
+ # Verify the assoc-list contains nk_guest ifindex with nsid=-1 (same namespace)
+ found = False
+ for assoc in peer_dev['assoc-list']:
+ if assoc['ifindex'] == nk_guest_ifindex:
+ ksft_eq(assoc['nsid'], -1,
+ "nsid should be -1 (NETNSA_NSID_NOT_ASSIGNED) for same-namespace device")
+ found = True
+ break
+ ksft_true(found, "nk_guest ifindex not found in assoc-list")
+
+ # Clean up
+ cfg.pspnl.dev_disassoc({'id': psp_dev_id_for_assoc, 'ifindex': nk_guest_ifindex, 'nsid': cfg.psp_dev_peer_nsid})
+
+ del cfg.psp_dev_id
+ del cfg.psp_info
+
+
+def _dev_assoc_no_nsid(cfg):
+ """ Test dev-assoc and dev-disassoc without nsid attribute """
+ _init_psp_dev(cfg, True)
+ psp_dev_id = cfg.psp_dev_id
+
+ # Get nk_host's ifindex (in host namespace, same as caller)
+ nk_host_dev = ip(f"link show dev {cfg._nk_host_ifname}", json=True)[0]
+ nk_host_ifindex = nk_host_dev['ifindex']
+
+ # Associate without nsid - should look up ifindex in caller's netns
+ cfg.pspnl.dev_assoc({'id': psp_dev_id, 'ifindex': nk_host_ifindex})
+
+ # Verify assoc-list contains the device
+ dev_info = cfg.pspnl.dev_get({'id': psp_dev_id})
+ ksft_true('assoc-list' in dev_info, "No assoc-list after association")
+ found = False
+ for assoc in dev_info['assoc-list']:
+ if assoc['ifindex'] == nk_host_ifindex:
+ found = True
+ break
+ ksft_true(found, "Associated device not found in assoc-list")
+
+ # Disassociate without nsid - should also use caller's netns
+ cfg.pspnl.dev_disassoc({'id': psp_dev_id, 'ifindex': nk_host_ifindex})
+
+ # Verify assoc-list no longer contains the device
+ dev_info = cfg.pspnl.dev_get({'id': psp_dev_id})
+ found = False
+ if 'assoc-list' in dev_info:
+ for assoc in dev_info['assoc-list']:
+ if assoc['ifindex'] == nk_host_ifindex:
+ found = True
+ break
+ ksft_true(not found, "Device should not be in assoc-list after disassociation")
+
+ del cfg.psp_dev_id
+ del cfg.psp_info
+
+
+def _psp_dev_assoc_cleanup_on_netkit_del(cfg):
+ """ Test that assoc-list is cleared when associated netkit interface is deleted """
+ _init_psp_dev(cfg, True)
+ psp_dev_id_for_assoc = cfg.psp_dev_id
+
+ # Associate PSP device with nk_guest interface (in guest namespace)
+ nk_guest_dev = ip(f"link show dev {cfg._nk_guest_ifname}", json=True, ns=cfg.netns)[0]
+ nk_guest_ifindex = nk_guest_dev['ifindex']
+
+ cfg.pspnl.dev_assoc({'id': psp_dev_id_for_assoc, 'ifindex': nk_guest_ifindex, 'nsid': cfg.psp_dev_peer_nsid})
+
+ # Verify assoc-list exists in default netns
+ dev_info = cfg.pspnl.dev_get({'id': psp_dev_id_for_assoc})
+ ksft_true('assoc-list' in dev_info, "No assoc-list after association")
+ found = False
+ for assoc in dev_info['assoc-list']:
+ if assoc['ifindex'] == nk_guest_ifindex and assoc['nsid'] == cfg.psp_dev_peer_nsid:
+ found = True
+ break
+ ksft_true(found, "Associated device not found in assoc-list")
+
+ # Delete the netkit interface in the guest namespace
+ ip(f"link del {cfg._nk_guest_ifname}", ns=cfg.netns)
+
+ # Mark netkit as already deleted so cleanup won't try to delete it again
+ # (deleting nk_guest also removes nk_host since they're a pair)
+ cfg._nk_host_ifname = None
+ cfg._nk_guest_ifname = None
+
+ # Verify assoc-list is gone in default netns after netkit deletion
+ dev_info = cfg.pspnl.dev_get({'id': psp_dev_id_for_assoc})
+ ksft_true('assoc-list' not in dev_info or len(dev_info['assoc-list']) == 0,
+ "assoc-list should be empty after netkit deletion")
+
+ del cfg.psp_dev_id
+ del cfg.psp_info
+
+
def __bad_xfer_do(cfg, s, tx, version='hdr0-aes-gcm-128'):
# Make sure we accept the ACK for the SPI before we seal with the bad assoc
_check_data_outq(s, 0)
@@ -571,33 +868,127 @@ def removal_device_bi(cfg):
_close_conn(cfg, s)
-def psp_ip_ver_test_builder(name, test_func, psp_ver, ipver):
- """Build test cases for each combo of PSP version and IP version"""
- def test_case(cfg):
- cfg.require_ipver(ipver)
- test_func(cfg, psp_ver, ipver)
-
- test_case.__name__ = f"{name}_v{psp_ver}_ip{ipver}"
- return test_case
+@ksft_variants([
+ KsftNamedVariant(f"v{v}_ip{ip}", v, ip)
+ for v in range(4) for ip in ("4", "6")
+])
+def data_basic_send(cfg, version, ipver):
+ cfg.require_ipver(ipver)
+ _data_basic_send(cfg, version, ipver)
+
+
+@ksft_variants([
+ KsftNamedVariant(f"ip{ip}", ip)
+ for ip in ("4", "6")
+])
+def data_mss_adjust(cfg, ipver):
+ cfg.require_ipver(ipver)
+ _data_mss_adjust(cfg, ipver)
+
+
+@ksft_variants([
+ KsftNamedVariant(f"v{v}_ip6", v, "6")
+ for v in range(4)
+])
+def data_basic_send_netkit_psp_assoc(cfg, version, ipver):
+ cfg.require_ipver(ipver)
+ _data_basic_send_netkit_psp_assoc(cfg, version, ipver)
+
+
+
+def _get_nsid(ns_name):
+ """Get the nsid for a namespace."""
+ for entry in ip("netns list-id", json=True):
+ if entry.get("name") == str(ns_name):
+ return entry["nsid"]
+ raise KsftSkipEx(f"nsid not found for namespace {ns_name}")
+
+
+def _setup_psp_attributes(cfg):
+ """
+ Set up PSP-specific attributes on the environment.
+
+ This sets attributes needed for PSP tests based on whether we're using
+ netdevsim or a real NIC.
+ """
+ if cfg._ns is not None:
+ # netdevsim case: PSP device is the local dev (in host namespace)
+ cfg.psp_dev = cfg._ns.nsims[0].dev
+ cfg.psp_ifname = cfg.psp_dev['ifname']
+ cfg.psp_ifindex = cfg.psp_dev['ifindex']
+
+ # PSP peer device is the remote dev (in _netns, where psp_responder runs)
+ cfg.psp_dev_peer = cfg._ns_peer.nsims[0].dev
+ cfg.psp_dev_peer_ifname = cfg.psp_dev_peer['ifname']
+ cfg.psp_dev_peer_ifindex = cfg.psp_dev_peer['ifindex']
+ else:
+ # Real NIC case: PSP device is the local interface
+ cfg.psp_dev = cfg.dev
+ cfg.psp_ifname = cfg.ifname
+ cfg.psp_ifindex = cfg.ifindex
+
+ # PSP peer device is the remote interface
+ cfg.psp_dev_peer = cfg.remote_dev
+ cfg.psp_dev_peer_ifname = cfg.remote_ifname
+ cfg.psp_dev_peer_ifindex = cfg.remote_ifindex
+
+ # Get nsid for the guest namespace (netns) where nk_guest is
+ cfg.psp_dev_peer_nsid = _get_nsid(cfg.netns.name)
+
+
+def _setup_psp_routes(cfg):
+ """
+ Set up routes for cross-namespace connectivity.
+
+ Traffic flows:
+ 1. remote (_netns) -> nk_guest (netns):
+ psp_dev_peer -> psp_dev_local -> BPF redirect -> nk_host -> nk_guest
+ Needs: route in _netns to nk_v6_pfx/64 via psp_dev_local
+
+ 2. nk_guest (netns) -> remote (_netns):
+ nk_guest -> nk_host -> psp_dev_local -> psp_dev_peer
+ Needs: route in netns to dev_v6_pfx/64 via nk_host
+ """
+ # In _netns (remote namespace): add route to nk_guest prefix via psp_dev_local
+ # psp_dev_peer can reach psp_dev_local via the link, then traffic goes through BPF
+ ip(f"-6 route add {cfg.nk_v6_pfx}/64 via {cfg.nsim_v6_pfx}1 dev {cfg.psp_dev_peer_ifname}",
+ ns=cfg._netns)
+
+ # In netns (guest namespace): add route to remote peer prefix
+ # nk_guest default route goes to nk_host, but we need explicit route to dev_v6_pfx/64
+ ip(f"-6 route add {cfg.nsim_v6_pfx}/64 via fe80::1 dev {cfg._nk_guest_ifname}",
+ ns=cfg.netns)
-def ipver_test_builder(name, test_func, ipver):
- """Build test cases for each IP version"""
- def test_case(cfg):
- cfg.require_ipver(ipver)
- test_func(cfg, ipver)
+def main() -> None:
+ """ Ksft boiler plate main """
- test_case.__name__ = f"{name}_ip{ipver}"
- return test_case
+ # Use a different prefix for netkit guest to avoid conflict with dev prefix
+ nk_v6_pfx = "2001:db9::"
+ # Set LOCAL_PREFIX_V6 to a DIFFERENT prefix than the dev prefix to avoid BPF
+ # redirecting psp_responder traffic. The BPF only redirects traffic
+ # matching LOCAL_PREFIX_V6, so dev traffic (2001:db8::) won't be affected.
+ if "LOCAL_PREFIX_V6" not in os.environ:
+ os.environ["LOCAL_PREFIX_V6"] = nk_v6_pfx
-def main() -> None:
- """ Ksft boiler plate main """
+ try:
+ env = NetDrvContEnv(__file__, install_tx_redirect_bpf=True)
+ has_cont = True
+ except KsftSkipEx:
+ env = NetDrvEpEnv(__file__)
+ has_cont = False
- with NetDrvEpEnv(__file__) as cfg:
+ with env as cfg:
cfg.pspnl = PSPFamily()
+ if has_cont:
+ cfg.nk_v6_pfx = nk_v6_pfx
+ _setup_psp_attributes(cfg)
+ _setup_psp_routes(cfg)
+
# Set up responder and communication sock
+ # psp_responder runs in _netns (remote namespace with psp_dev_peer)
responder = cfg.remote.deploy("psp_responder")
cfg.comm_port = rand_port()
@@ -611,17 +1002,17 @@ def main() -> None:
cfg.comm_port),
timeout=1)
- cases = [
- psp_ip_ver_test_builder(
- "data_basic_send", _data_basic_send, version, ipver
- )
- for version in range(0, 4)
- for ipver in ("4", "6")
- ]
- cases += [
- ipver_test_builder("data_mss_adjust", _data_mss_adjust, ipver)
- for ipver in ("4", "6")
- ]
+ cases = [data_basic_send, data_mss_adjust]
+
+ if has_cont:
+ cases += [
+ data_basic_send_netkit_psp_assoc,
+ _key_rotation_notify_multi_ns_netkit,
+ _dev_change_notify_multi_ns_netkit,
+ _psp_dev_get_check_netkit_psp_assoc,
+ _dev_assoc_no_nsid,
+ _psp_dev_assoc_cleanup_on_netkit_del,
+ ]
ksft_run(cases=cases, globs=globals(),
case_pfx={"dev_", "data_", "assoc_", "removal_"},
--
2.52.0
^ permalink raw reply related
* [PATCH v11 net-next 4/5] selftests/net: Add bpf skb forwarding program
From: Wei Wang @ 2026-04-08 23:14 UTC (permalink / raw)
To: netdev, Jakub Kicinski, Daniel Zahka, Willem de Bruijn, David Wei,
Andrew Lunn, David S . Miller, Eric Dumazet, Simon Horman
Cc: Wei Wang, Bobby Eshleman
In-Reply-To: <20260408231415.522691-1-weibunny.kernel@gmail.com>
From: Wei Wang <weibunny@fb.com>
Add nk_redirect.bpf.c, a BPF program that forwards skbs matching some IPv6
prefix received on eth0 ifindex to a specified dev ifindex.
bpf_redirect_neigh() is used to make sure neighbor lookup is performed
and proper MAC addr is being used.
Signed-off-by: Wei Wang <weibunny@fb.com>
Reviewed-by: Bobby Eshleman <bobbyeshleman@meta.com>
Tested-by: Bobby Eshleman <bobbyeshleman@meta.com>
---
.../drivers/net/hw/nk_redirect.bpf.c | 60 +++++++++++++++++++
1 file changed, 60 insertions(+)
create mode 100644 tools/testing/selftests/drivers/net/hw/nk_redirect.bpf.c
diff --git a/tools/testing/selftests/drivers/net/hw/nk_redirect.bpf.c b/tools/testing/selftests/drivers/net/hw/nk_redirect.bpf.c
new file mode 100644
index 000000000000..7ac9ffd50f15
--- /dev/null
+++ b/tools/testing/selftests/drivers/net/hw/nk_redirect.bpf.c
@@ -0,0 +1,60 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * BPF program for redirecting traffic using bpf_redirect_neigh().
+ * Unlike bpf_redirect() which preserves L2 headers, bpf_redirect_neigh()
+ * performs neighbor lookup and fills in the correct L2 addresses for the
+ * target interface. This is necessary when redirecting across different
+ * device types (e.g., from netdevsim to netkit).
+ */
+#include <linux/bpf.h>
+#include <linux/pkt_cls.h>
+#include <linux/if_ether.h>
+#include <linux/ipv6.h>
+#include <linux/in6.h>
+#include <bpf/bpf_endian.h>
+#include <bpf/bpf_helpers.h>
+
+#define TC_ACT_OK 0
+#define ETH_P_IPV6 0x86DD
+
+#define ctx_ptr(field) ((void *)(long)(field))
+
+#define v6_p64_equal(a, b) (a.s6_addr32[0] == b.s6_addr32[0] && \
+ a.s6_addr32[1] == b.s6_addr32[1])
+
+volatile __u32 redirect_ifindex;
+volatile __u8 ipv6_prefix[16];
+
+SEC("tc/ingress")
+int tc_redirect(struct __sk_buff *skb)
+{
+ void *data_end = ctx_ptr(skb->data_end);
+ void *data = ctx_ptr(skb->data);
+ struct in6_addr *match_prefix;
+ struct ipv6hdr *ip6h;
+ struct ethhdr *eth;
+
+ match_prefix = (struct in6_addr *)ipv6_prefix;
+
+ if (skb->protocol != bpf_htons(ETH_P_IPV6))
+ return TC_ACT_OK;
+
+ eth = data;
+ if ((void *)(eth + 1) > data_end)
+ return TC_ACT_OK;
+
+ ip6h = data + sizeof(struct ethhdr);
+ if ((void *)(ip6h + 1) > data_end)
+ return TC_ACT_OK;
+
+ if (!v6_p64_equal(ip6h->daddr, (*match_prefix)))
+ return TC_ACT_OK;
+
+ /*
+ * Use bpf_redirect_neigh() to perform neighbor lookup and fill in
+ * correct L2 addresses for the target interface.
+ */
+ return bpf_redirect_neigh(redirect_ifindex, NULL, 0, 0);
+}
+
+char __license[] SEC("license") = "GPL";
--
2.52.0
^ permalink raw reply related
* [PATCH v11 net-next 3/5] psp: add a new netdev event for dev unregister
From: Wei Wang @ 2026-04-08 23:14 UTC (permalink / raw)
To: netdev, Jakub Kicinski, Daniel Zahka, Willem de Bruijn, David Wei,
Andrew Lunn, David S . Miller, Eric Dumazet, Simon Horman
Cc: Wei Wang
In-Reply-To: <20260408231415.522691-1-weibunny.kernel@gmail.com>
From: Wei Wang <weibunny@fb.com>
Add a new netdev event for dev unregister and handle the removal of this
dev from psp->assoc_dev_list, upon the first dev-assoc operation.
Signed-off-by: Wei Wang <weibunny@fb.com>
Reviewed-by: Daniel Zahka <daniel.zahka@gmail.com>
---
Documentation/netlink/specs/psp.yaml | 2 +-
net/psp/psp-nl-gen.c | 2 +-
net/psp/psp-nl-gen.h | 3 ++
net/psp/psp.h | 1 +
net/psp/psp_main.c | 75 ++++++++++++++++++++++++++++
net/psp/psp_nl.c | 16 ++++++
6 files changed, 97 insertions(+), 2 deletions(-)
diff --git a/Documentation/netlink/specs/psp.yaml b/Documentation/netlink/specs/psp.yaml
index 3d1b7223e084..538ed9184965 100644
--- a/Documentation/netlink/specs/psp.yaml
+++ b/Documentation/netlink/specs/psp.yaml
@@ -328,7 +328,7 @@ operations:
- nsid
reply:
attributes: []
- pre: psp-device-get-locked
+ pre: psp-device-get-locked-dev-assoc
post: psp-device-unlock
-
name: dev-disassoc
diff --git a/net/psp/psp-nl-gen.c b/net/psp/psp-nl-gen.c
index 114299c64423..389a8480cc3d 100644
--- a/net/psp/psp-nl-gen.c
+++ b/net/psp/psp-nl-gen.c
@@ -135,7 +135,7 @@ static const struct genl_split_ops psp_nl_ops[] = {
},
{
.cmd = PSP_CMD_DEV_ASSOC,
- .pre_doit = psp_device_get_locked,
+ .pre_doit = psp_device_get_locked_dev_assoc,
.doit = psp_nl_dev_assoc_doit,
.post_doit = psp_device_unlock,
.policy = psp_dev_assoc_nl_policy,
diff --git a/net/psp/psp-nl-gen.h b/net/psp/psp-nl-gen.h
index 4dd0f0f23053..24d51bff997f 100644
--- a/net/psp/psp-nl-gen.h
+++ b/net/psp/psp-nl-gen.h
@@ -21,6 +21,9 @@ int psp_device_get_locked_admin(const struct genl_split_ops *ops,
struct sk_buff *skb, struct genl_info *info);
int psp_assoc_device_get_locked(const struct genl_split_ops *ops,
struct sk_buff *skb, struct genl_info *info);
+int psp_device_get_locked_dev_assoc(const struct genl_split_ops *ops,
+ struct sk_buff *skb,
+ struct genl_info *info);
void
psp_device_unlock(const struct genl_split_ops *ops, struct sk_buff *skb,
struct genl_info *info);
diff --git a/net/psp/psp.h b/net/psp/psp.h
index 0f9c4e4e52cb..c82b21bae240 100644
--- a/net/psp/psp.h
+++ b/net/psp/psp.h
@@ -15,6 +15,7 @@ extern struct mutex psp_devs_lock;
void psp_dev_free(struct psp_dev *psd);
int psp_dev_check_access(struct psp_dev *psd, struct net *net, bool admin);
+int psp_attach_netdev_notifier(void);
void psp_nl_notify_dev(struct psp_dev *psd, u32 cmd);
diff --git a/net/psp/psp_main.c b/net/psp/psp_main.c
index 97b04958c413..90836997aa97 100644
--- a/net/psp/psp_main.c
+++ b/net/psp/psp_main.c
@@ -375,6 +375,81 @@ int psp_dev_rcv(struct sk_buff *skb, u16 dev_id, u8 generation, bool strip_icv)
}
EXPORT_SYMBOL(psp_dev_rcv);
+static void psp_dev_disassoc_one(struct psp_dev *psd, struct net_device *dev)
+{
+ struct psp_assoc_dev *entry, *tmp;
+
+ list_for_each_entry_safe(entry, tmp, &psd->assoc_dev_list, dev_list) {
+ if (entry->assoc_dev == dev) {
+ list_del(&entry->dev_list);
+ rcu_assign_pointer(entry->assoc_dev->psp_dev, NULL);
+ netdev_put(entry->assoc_dev, &entry->dev_tracker);
+ kfree(entry);
+ return;
+ }
+ }
+}
+
+static int psp_netdev_event(struct notifier_block *nb, unsigned long event,
+ void *ptr)
+{
+ struct net_device *dev = netdev_notifier_info_to_dev(ptr);
+ struct psp_dev *psd;
+
+ if (event != NETDEV_UNREGISTER)
+ return NOTIFY_DONE;
+
+ rcu_read_lock();
+ psd = rcu_dereference(dev->psp_dev);
+ if (psd && psp_dev_tryget(psd)) {
+ rcu_read_unlock();
+ mutex_lock(&psd->lock);
+ psp_dev_disassoc_one(psd, dev);
+ mutex_unlock(&psd->lock);
+ psp_dev_put(psd);
+ } else {
+ rcu_read_unlock();
+ }
+
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block psp_netdev_notifier = {
+ .notifier_call = psp_netdev_event,
+};
+
+static DEFINE_MUTEX(psp_notifier_lock);
+static bool psp_notifier_registered;
+
+/*
+ * psp_attach_netdev_notifier() - register netdev notifier on first use
+ *
+ * Register the netdevice notifier when the first device association
+ * is created. In many installations no associations will be created and
+ * the notifier won't be needed.
+ *
+ * Must be called without psd->lock held, due to lock ordering:
+ * rtnl_lock -> psd->lock (the notifier callback runs under rtnl_lock
+ * and takes psd->lock).
+ */
+int psp_attach_netdev_notifier(void)
+{
+ int err = 0;
+
+ if (READ_ONCE(psp_notifier_registered))
+ return 0;
+
+ mutex_lock(&psp_notifier_lock);
+ if (!psp_notifier_registered) {
+ err = register_netdevice_notifier(&psp_netdev_notifier);
+ if (!err)
+ WRITE_ONCE(psp_notifier_registered, true);
+ }
+ mutex_unlock(&psp_notifier_lock);
+
+ return err;
+}
+
static int __init psp_init(void)
{
mutex_init(&psp_devs_lock);
diff --git a/net/psp/psp_nl.c b/net/psp/psp_nl.c
index 8f2b925eda00..8f87018f1a8e 100644
--- a/net/psp/psp_nl.c
+++ b/net/psp/psp_nl.c
@@ -167,6 +167,22 @@ int psp_device_get_locked(const struct genl_split_ops *ops,
return __psp_device_get_locked(ops, skb, info, false);
}
+/*
+ * Non-admin version of psp_device_get_locked() + psp_attach_netdev_notifier()
+ * only used for dev-assoc.
+ */
+int psp_device_get_locked_dev_assoc(const struct genl_split_ops *ops,
+ struct sk_buff *skb, struct genl_info *info)
+{
+ int err;
+
+ err = psp_attach_netdev_notifier();
+ if (err)
+ return err;
+
+ return __psp_device_get_locked(ops, skb, info, false);
+}
+
void
psp_device_unlock(const struct genl_split_ops *ops, struct sk_buff *skb,
struct genl_info *info)
--
2.52.0
^ permalink raw reply related
* [PATCH v11 net-next 2/5] psp: add new netlink cmd for dev-assoc and dev-disassoc
From: Wei Wang @ 2026-04-08 23:14 UTC (permalink / raw)
To: netdev, Jakub Kicinski, Daniel Zahka, Willem de Bruijn, David Wei,
Andrew Lunn, David S . Miller, Eric Dumazet, Simon Horman
Cc: Wei Wang
In-Reply-To: <20260408231415.522691-1-weibunny.kernel@gmail.com>
From: Wei Wang <weibunny@fb.com>
The main purpose of this cmd is to be able to associate a
non-psp-capable device (e.g. veth or netkit) with a psp device.
One use case is if we create a pair of veth/netkit, and assign 1 end
inside a netns, while leaving the other end within the default netns,
with a real PSP device, e.g. netdevsim or a physical PSP-capable NIC.
With this command, we could associate the veth/netkit inside the netns
with PSP device, so the virtual device could act as PSP-capable device
to initiate PSP connections, and performs PSP encryption/decryption on
the real PSP device.
Signed-off-by: Wei Wang <weibunny@fb.com>
Reviewed-by: Daniel Zahka <daniel.zahka@gmail.com>
---
Documentation/netlink/specs/psp.yaml | 67 +++++-
include/net/psp/types.h | 15 ++
include/uapi/linux/psp.h | 13 ++
net/psp/psp-nl-gen.c | 32 +++
net/psp/psp-nl-gen.h | 2 +
net/psp/psp_main.c | 20 ++
net/psp/psp_nl.c | 325 ++++++++++++++++++++++++++-
7 files changed, 462 insertions(+), 12 deletions(-)
diff --git a/Documentation/netlink/specs/psp.yaml b/Documentation/netlink/specs/psp.yaml
index c54e1202cbe0..3d1b7223e084 100644
--- a/Documentation/netlink/specs/psp.yaml
+++ b/Documentation/netlink/specs/psp.yaml
@@ -13,6 +13,17 @@ definitions:
hdr0-aes-gmac-128, hdr0-aes-gmac-256]
attribute-sets:
+ -
+ name: assoc-dev-info
+ attributes:
+ -
+ name: ifindex
+ doc: ifindex of an associated network device.
+ type: u32
+ -
+ name: nsid
+ doc: Network namespace ID of the associated device.
+ type: s32
-
name: dev
attributes:
@@ -24,7 +35,9 @@ attribute-sets:
min: 1
-
name: ifindex
- doc: ifindex of the main netdevice linked to the PSP device.
+ doc: |
+ ifindex of the main netdevice linked to the PSP device,
+ or the ifindex to associate with the PSP device.
type: u32
-
name: psp-versions-cap
@@ -38,6 +51,28 @@ attribute-sets:
type: u32
enum: version
enum-as-flags: true
+ -
+ name: assoc-list
+ doc: List of associated virtual devices.
+ type: nest
+ nested-attributes: assoc-dev-info
+ multi-attr: true
+ -
+ name: nsid
+ doc: |
+ Network namespace ID for the device to associate/disassociate.
+ Optional for dev-assoc and dev-disassoc; if not present, the
+ device is looked up in the caller's network namespace.
+ type: s32
+ -
+ name: by-association
+ doc: |
+ Flag indicating the PSP device is an associated device from a
+ different network namespace.
+ Present when in associated namespace, absent when in primary/host
+ namespace.
+ type: flag
+
-
name: assoc
attributes:
@@ -170,6 +205,8 @@ operations:
- ifindex
- psp-versions-cap
- psp-versions-ena
+ - assoc-list
+ - by-association
pre: psp-device-get-locked
post: psp-device-unlock
dump:
@@ -279,6 +316,34 @@ operations:
post: psp-device-unlock
dump:
reply: *stats-all
+ -
+ name: dev-assoc
+ doc: Associate a network device with a PSP device.
+ attribute-set: dev
+ do:
+ request:
+ attributes:
+ - id
+ - ifindex
+ - nsid
+ reply:
+ attributes: []
+ pre: psp-device-get-locked
+ post: psp-device-unlock
+ -
+ name: dev-disassoc
+ doc: Disassociate a network device from a PSP device.
+ attribute-set: dev
+ do:
+ request:
+ attributes:
+ - id
+ - ifindex
+ - nsid
+ reply:
+ attributes: []
+ pre: psp-device-get-locked
+ post: psp-device-unlock
mcast-groups:
list:
diff --git a/include/net/psp/types.h b/include/net/psp/types.h
index 25a9096d4e7d..4bd432ed107a 100644
--- a/include/net/psp/types.h
+++ b/include/net/psp/types.h
@@ -5,6 +5,7 @@
#include <linux/mutex.h>
#include <linux/refcount.h>
+#include <net/net_trackers.h>
struct netlink_ext_ack;
@@ -43,9 +44,22 @@ struct psp_dev_config {
u32 versions;
};
+/**
+ * struct psp_assoc_dev - wrapper for associated net_device
+ * @dev_list: list node for psp_dev::assoc_dev_list
+ * @assoc_dev: the associated net_device
+ * @dev_tracker: tracker for the net_device reference
+ */
+struct psp_assoc_dev {
+ struct list_head dev_list;
+ struct net_device *assoc_dev;
+ netdevice_tracker dev_tracker;
+};
+
/**
* struct psp_dev - PSP device struct
* @main_netdev: original netdevice of this PSP device
+ * @assoc_dev_list: list of psp_assoc_dev entries associated with this PSP device
* @ops: driver callbacks
* @caps: device capabilities
* @drv_priv: driver priv pointer
@@ -67,6 +81,7 @@ struct psp_dev_config {
*/
struct psp_dev {
struct net_device *main_netdev;
+ struct list_head assoc_dev_list;
struct psp_dev_ops *ops;
struct psp_dev_caps *caps;
diff --git a/include/uapi/linux/psp.h b/include/uapi/linux/psp.h
index a3a336488dc3..1c8899cd4da5 100644
--- a/include/uapi/linux/psp.h
+++ b/include/uapi/linux/psp.h
@@ -17,11 +17,22 @@ enum psp_version {
PSP_VERSION_HDR0_AES_GMAC_256,
};
+enum {
+ PSP_A_ASSOC_DEV_INFO_IFINDEX = 1,
+ PSP_A_ASSOC_DEV_INFO_NSID,
+
+ __PSP_A_ASSOC_DEV_INFO_MAX,
+ PSP_A_ASSOC_DEV_INFO_MAX = (__PSP_A_ASSOC_DEV_INFO_MAX - 1)
+};
+
enum {
PSP_A_DEV_ID = 1,
PSP_A_DEV_IFINDEX,
PSP_A_DEV_PSP_VERSIONS_CAP,
PSP_A_DEV_PSP_VERSIONS_ENA,
+ PSP_A_DEV_ASSOC_LIST,
+ PSP_A_DEV_NSID,
+ PSP_A_DEV_BY_ASSOCIATION,
__PSP_A_DEV_MAX,
PSP_A_DEV_MAX = (__PSP_A_DEV_MAX - 1)
@@ -74,6 +85,8 @@ enum {
PSP_CMD_RX_ASSOC,
PSP_CMD_TX_ASSOC,
PSP_CMD_GET_STATS,
+ PSP_CMD_DEV_ASSOC,
+ PSP_CMD_DEV_DISASSOC,
__PSP_CMD_MAX,
PSP_CMD_MAX = (__PSP_CMD_MAX - 1)
diff --git a/net/psp/psp-nl-gen.c b/net/psp/psp-nl-gen.c
index 1f5e73e7ccc1..114299c64423 100644
--- a/net/psp/psp-nl-gen.c
+++ b/net/psp/psp-nl-gen.c
@@ -53,6 +53,20 @@ static const struct nla_policy psp_get_stats_nl_policy[PSP_A_STATS_DEV_ID + 1] =
[PSP_A_STATS_DEV_ID] = NLA_POLICY_MIN(NLA_U32, 1),
};
+/* PSP_CMD_DEV_ASSOC - do */
+static const struct nla_policy psp_dev_assoc_nl_policy[PSP_A_DEV_NSID + 1] = {
+ [PSP_A_DEV_ID] = NLA_POLICY_MIN(NLA_U32, 1),
+ [PSP_A_DEV_IFINDEX] = { .type = NLA_U32, },
+ [PSP_A_DEV_NSID] = { .type = NLA_S32, },
+};
+
+/* PSP_CMD_DEV_DISASSOC - do */
+static const struct nla_policy psp_dev_disassoc_nl_policy[PSP_A_DEV_NSID + 1] = {
+ [PSP_A_DEV_ID] = NLA_POLICY_MIN(NLA_U32, 1),
+ [PSP_A_DEV_IFINDEX] = { .type = NLA_U32, },
+ [PSP_A_DEV_NSID] = { .type = NLA_S32, },
+};
+
/* Ops table for psp */
static const struct genl_split_ops psp_nl_ops[] = {
{
@@ -119,6 +133,24 @@ static const struct genl_split_ops psp_nl_ops[] = {
.dumpit = psp_nl_get_stats_dumpit,
.flags = GENL_CMD_CAP_DUMP,
},
+ {
+ .cmd = PSP_CMD_DEV_ASSOC,
+ .pre_doit = psp_device_get_locked,
+ .doit = psp_nl_dev_assoc_doit,
+ .post_doit = psp_device_unlock,
+ .policy = psp_dev_assoc_nl_policy,
+ .maxattr = PSP_A_DEV_NSID,
+ .flags = GENL_CMD_CAP_DO,
+ },
+ {
+ .cmd = PSP_CMD_DEV_DISASSOC,
+ .pre_doit = psp_device_get_locked,
+ .doit = psp_nl_dev_disassoc_doit,
+ .post_doit = psp_device_unlock,
+ .policy = psp_dev_disassoc_nl_policy,
+ .maxattr = PSP_A_DEV_NSID,
+ .flags = GENL_CMD_CAP_DO,
+ },
};
static const struct genl_multicast_group psp_nl_mcgrps[] = {
diff --git a/net/psp/psp-nl-gen.h b/net/psp/psp-nl-gen.h
index 977355455395..4dd0f0f23053 100644
--- a/net/psp/psp-nl-gen.h
+++ b/net/psp/psp-nl-gen.h
@@ -33,6 +33,8 @@ int psp_nl_rx_assoc_doit(struct sk_buff *skb, struct genl_info *info);
int psp_nl_tx_assoc_doit(struct sk_buff *skb, struct genl_info *info);
int psp_nl_get_stats_doit(struct sk_buff *skb, struct genl_info *info);
int psp_nl_get_stats_dumpit(struct sk_buff *skb, struct netlink_callback *cb);
+int psp_nl_dev_assoc_doit(struct sk_buff *skb, struct genl_info *info);
+int psp_nl_dev_disassoc_doit(struct sk_buff *skb, struct genl_info *info);
enum {
PSP_NLGRP_MGMT,
diff --git a/net/psp/psp_main.c b/net/psp/psp_main.c
index 82de78a1d6bd..97b04958c413 100644
--- a/net/psp/psp_main.c
+++ b/net/psp/psp_main.c
@@ -37,8 +37,18 @@ struct mutex psp_devs_lock;
*/
int psp_dev_check_access(struct psp_dev *psd, struct net *net, bool admin)
{
+ struct psp_assoc_dev *entry;
+
if (dev_net(psd->main_netdev) == net)
return 0;
+
+ if (!admin) {
+ list_for_each_entry(entry, &psd->assoc_dev_list, dev_list) {
+ if (dev_net(entry->assoc_dev) == net)
+ return 0;
+ }
+ }
+
return -ENOENT;
}
@@ -74,6 +84,7 @@ psp_dev_create(struct net_device *netdev,
return ERR_PTR(-ENOMEM);
psd->main_netdev = netdev;
+ INIT_LIST_HEAD(&psd->assoc_dev_list);
psd->ops = psd_ops;
psd->caps = psd_caps;
psd->drv_priv = priv_ptr;
@@ -121,6 +132,7 @@ void psp_dev_free(struct psp_dev *psd)
*/
void psp_dev_unregister(struct psp_dev *psd)
{
+ struct psp_assoc_dev *entry, *entry_tmp;
struct psp_assoc *pas, *next;
mutex_lock(&psp_devs_lock);
@@ -140,6 +152,14 @@ void psp_dev_unregister(struct psp_dev *psd)
list_for_each_entry_safe(pas, next, &psd->stale_assocs, assocs_list)
psp_dev_tx_key_del(psd, pas);
+ list_for_each_entry_safe(entry, entry_tmp, &psd->assoc_dev_list,
+ dev_list) {
+ list_del(&entry->dev_list);
+ rcu_assign_pointer(entry->assoc_dev->psp_dev, NULL);
+ netdev_put(entry->assoc_dev, &entry->dev_tracker);
+ kfree(entry);
+ }
+
rcu_assign_pointer(psd->main_netdev->psp_dev, NULL);
psd->ops = NULL;
diff --git a/net/psp/psp_nl.c b/net/psp/psp_nl.c
index eb47a9ee4438..8f2b925eda00 100644
--- a/net/psp/psp_nl.c
+++ b/net/psp/psp_nl.c
@@ -2,6 +2,7 @@
#include <linux/ethtool.h>
#include <linux/skbuff.h>
+#include <linux/net_namespace.h>
#include <linux/xarray.h>
#include <net/genetlink.h>
#include <net/psp.h>
@@ -38,6 +39,73 @@ static int psp_nl_reply_send(struct sk_buff *rsp, struct genl_info *info)
return genlmsg_reply(rsp, info);
}
+/**
+ * psp_nl_multicast_per_ns() - multicast a notification to each unique netns
+ * @psd: PSP device (must be locked)
+ * @group: multicast group
+ * @build_ntf: callback to build an skb for a given netns, or NULL on failure
+ * @ctx: opaque context passed to @build_ntf
+ *
+ * Iterates all unique network namespaces from the associated device list
+ * plus the main device's netns. For each unique netns, calls @build_ntf
+ * to construct a notification skb and multicasts it.
+ */
+static void psp_nl_multicast_per_ns(struct psp_dev *psd, unsigned int group,
+ struct sk_buff *(*build_ntf)(struct psp_dev *,
+ struct net *,
+ void *),
+ void *ctx)
+{
+ struct psp_assoc_dev *entry;
+ struct xarray sent_nets;
+ struct net *main_net;
+ struct sk_buff *ntf;
+
+ main_net = dev_net(psd->main_netdev);
+ xa_init(&sent_nets);
+
+ list_for_each_entry(entry, &psd->assoc_dev_list, dev_list) {
+ struct net *assoc_net = dev_net(entry->assoc_dev);
+ int ret;
+
+ if (net_eq(assoc_net, main_net))
+ continue;
+
+ ret = xa_insert(&sent_nets, (unsigned long)assoc_net, assoc_net,
+ GFP_KERNEL);
+ if (ret == -EBUSY)
+ continue;
+
+ ntf = build_ntf(psd, assoc_net, ctx);
+ if (!ntf)
+ continue;
+
+ genlmsg_multicast_netns(&psp_nl_family, assoc_net, ntf, 0,
+ group, GFP_KERNEL);
+ }
+ xa_destroy(&sent_nets);
+
+ /* Send to main device netns */
+ ntf = build_ntf(psd, main_net, ctx);
+ if (!ntf)
+ return;
+ genlmsg_multicast_netns(&psp_nl_family, main_net, ntf, 0, group,
+ GFP_KERNEL);
+}
+
+static struct sk_buff *psp_nl_clone_ntf(struct psp_dev *psd, struct net *net,
+ void *ctx)
+{
+ return skb_clone(ctx, GFP_KERNEL);
+}
+
+static void psp_nl_multicast_all_ns(struct psp_dev *psd, struct sk_buff *ntf,
+ unsigned int group)
+{
+ psp_nl_multicast_per_ns(psd, group, psp_nl_clone_ntf, ntf);
+ nlmsg_free(ntf);
+}
+
/* Device stuff */
static struct psp_dev *
@@ -79,12 +147,20 @@ static int __psp_device_get_locked(const struct genl_split_ops *ops,
return PTR_ERR_OR_ZERO(info->user_ptr[0]);
}
+/*
+ * Admin version of psp_device_get_locked() where it returns psd only if
+ * current netns is the same as psd->main_netdev's netns.
+ */
int psp_device_get_locked_admin(const struct genl_split_ops *ops,
struct sk_buff *skb, struct genl_info *info)
{
return __psp_device_get_locked(ops, skb, info, true);
}
+/*
+ * Non-admin version of psp_device_get_locked() where it returns psd in netns
+ * for not only psd->main_netdev but all netdevs in psd->assoc_dev_list.
+ */
int psp_device_get_locked(const struct genl_split_ops *ops,
struct sk_buff *skb, struct genl_info *info)
{
@@ -103,11 +179,74 @@ psp_device_unlock(const struct genl_split_ops *ops, struct sk_buff *skb,
sockfd_put(socket);
}
+static bool psp_has_assoc_dev_in_ns(struct psp_dev *psd, struct net *net)
+{
+ struct psp_assoc_dev *entry;
+
+ list_for_each_entry(entry, &psd->assoc_dev_list, dev_list) {
+ if (dev_net(entry->assoc_dev) == net)
+ return true;
+ }
+
+ return false;
+}
+
+static int psp_nl_fill_assoc_dev_list(struct psp_dev *psd, struct sk_buff *rsp,
+ struct net *cur_net,
+ struct net *filter_net)
+{
+ struct psp_assoc_dev *entry;
+ struct net *dev_net_ns;
+ struct nlattr *nest;
+ int nsid;
+
+ list_for_each_entry(entry, &psd->assoc_dev_list, dev_list) {
+ dev_net_ns = dev_net(entry->assoc_dev);
+
+ if (filter_net && dev_net_ns != filter_net)
+ continue;
+
+ /* When filtering by namespace, all devices are in the caller's
+ * namespace so nsid is always NETNSA_NSID_NOT_ASSIGNED (-1).
+ * Otherwise, calculate the nsid relative to cur_net.
+ */
+ nsid = filter_net ? NETNSA_NSID_NOT_ASSIGNED :
+ peernet2id_alloc(cur_net, dev_net_ns,
+ GFP_KERNEL);
+
+ nest = nla_nest_start(rsp, PSP_A_DEV_ASSOC_LIST);
+ if (!nest)
+ return -1;
+
+ if (nla_put_u32(rsp, PSP_A_ASSOC_DEV_INFO_IFINDEX,
+ entry->assoc_dev->ifindex) ||
+ nla_put_s32(rsp, PSP_A_ASSOC_DEV_INFO_NSID, nsid)) {
+ nla_nest_cancel(rsp, nest);
+ return -1;
+ }
+
+ nla_nest_end(rsp, nest);
+ }
+
+ return 0;
+}
+
static int
psp_nl_dev_fill(struct psp_dev *psd, struct sk_buff *rsp,
const struct genl_info *info)
{
+ struct net *cur_net;
void *hdr;
+ int err;
+
+ cur_net = genl_info_net(info);
+
+ /* Skip this device if we're in an associated netns but have no
+ * associated devices in cur_net
+ */
+ if (cur_net != dev_net(psd->main_netdev) &&
+ !psp_has_assoc_dev_in_ns(psd, cur_net))
+ return 0;
hdr = genlmsg_iput(rsp, info);
if (!hdr)
@@ -119,6 +258,22 @@ psp_nl_dev_fill(struct psp_dev *psd, struct sk_buff *rsp,
nla_put_u32(rsp, PSP_A_DEV_PSP_VERSIONS_ENA, psd->config.versions))
goto err_cancel_msg;
+ if (cur_net == dev_net(psd->main_netdev)) {
+ /* Primary device - dump assoc list */
+ err = psp_nl_fill_assoc_dev_list(psd, rsp, cur_net, NULL);
+ if (err)
+ goto err_cancel_msg;
+ } else {
+ /* In netns: set by-association flag and dump filtered
+ * assoc list containing only devices in cur_net
+ */
+ if (nla_put_flag(rsp, PSP_A_DEV_BY_ASSOCIATION))
+ goto err_cancel_msg;
+ err = psp_nl_fill_assoc_dev_list(psd, rsp, cur_net, cur_net);
+ if (err)
+ goto err_cancel_msg;
+ }
+
genlmsg_end(rsp, hdr);
return 0;
@@ -127,27 +282,34 @@ psp_nl_dev_fill(struct psp_dev *psd, struct sk_buff *rsp,
return -EMSGSIZE;
}
-void psp_nl_notify_dev(struct psp_dev *psd, u32 cmd)
+static struct sk_buff *psp_nl_build_dev_ntf(struct psp_dev *psd,
+ struct net *net, void *ctx)
{
+ u32 cmd = *(u32 *)ctx;
struct genl_info info;
struct sk_buff *ntf;
- if (!genl_has_listeners(&psp_nl_family, dev_net(psd->main_netdev),
- PSP_NLGRP_MGMT))
- return;
+ if (!genl_has_listeners(&psp_nl_family, net, PSP_NLGRP_MGMT))
+ return NULL;
ntf = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL);
if (!ntf)
- return;
+ return NULL;
genl_info_init_ntf(&info, &psp_nl_family, cmd);
+ genl_info_net_set(&info, net);
if (psp_nl_dev_fill(psd, ntf, &info)) {
nlmsg_free(ntf);
- return;
+ return NULL;
}
- genlmsg_multicast_netns(&psp_nl_family, dev_net(psd->main_netdev), ntf,
- 0, PSP_NLGRP_MGMT, GFP_KERNEL);
+ return ntf;
+}
+
+void psp_nl_notify_dev(struct psp_dev *psd, u32 cmd)
+{
+ psp_nl_multicast_per_ns(psd, PSP_NLGRP_MGMT,
+ psp_nl_build_dev_ntf, &cmd);
}
int psp_nl_dev_get_doit(struct sk_buff *req, struct genl_info *info)
@@ -281,8 +443,9 @@ int psp_nl_key_rotate_doit(struct sk_buff *skb, struct genl_info *info)
psd->stats.rotations++;
nlmsg_end(ntf, (struct nlmsghdr *)ntf->data);
- genlmsg_multicast_netns(&psp_nl_family, dev_net(psd->main_netdev), ntf,
- 0, PSP_NLGRP_USE, GFP_KERNEL);
+
+ psp_nl_multicast_all_ns(psd, ntf, PSP_NLGRP_USE);
+
return psp_nl_reply_send(rsp, info);
err_free_ntf:
@@ -292,6 +455,145 @@ int psp_nl_key_rotate_doit(struct sk_buff *skb, struct genl_info *info)
return err;
}
+int psp_nl_dev_assoc_doit(struct sk_buff *skb, struct genl_info *info)
+{
+ struct psp_dev *psd = info->user_ptr[0];
+ struct psp_assoc_dev *psp_assoc_dev;
+ struct net_device *assoc_dev;
+ struct sk_buff *rsp;
+ u32 assoc_ifindex;
+ struct net *net;
+ int nsid, err;
+
+ if (GENL_REQ_ATTR_CHECK(info, PSP_A_DEV_IFINDEX))
+ return -EINVAL;
+
+ if (info->attrs[PSP_A_DEV_NSID]) {
+ nsid = nla_get_s32(info->attrs[PSP_A_DEV_NSID]);
+
+ net = get_net_ns_by_id(genl_info_net(info), nsid);
+ if (!net) {
+ NL_SET_BAD_ATTR(info->extack,
+ info->attrs[PSP_A_DEV_NSID]);
+ return -EINVAL;
+ }
+ } else {
+ net = get_net(genl_info_net(info));
+ }
+
+ psp_assoc_dev = kzalloc(sizeof(*psp_assoc_dev), GFP_KERNEL);
+ if (!psp_assoc_dev) {
+ err = -ENOMEM;
+ goto alloc_err;
+ }
+
+ assoc_ifindex = nla_get_u32(info->attrs[PSP_A_DEV_IFINDEX]);
+ assoc_dev = netdev_get_by_index(net, assoc_ifindex,
+ &psp_assoc_dev->dev_tracker,
+ GFP_KERNEL);
+ if (!assoc_dev) {
+ NL_SET_BAD_ATTR(info->extack, info->attrs[PSP_A_DEV_IFINDEX]);
+ err = -ENODEV;
+ goto assoc_dev_err;
+ }
+
+ /* Check if device is already associated with a PSP device */
+ if (cmpxchg(&assoc_dev->psp_dev, NULL, RCU_INITIALIZER(psd))) {
+ NL_SET_ERR_MSG(info->extack,
+ "Device already associated with a PSP device");
+ err = -EBUSY;
+ goto cmpxchg_err;
+ }
+
+ psp_assoc_dev->assoc_dev = assoc_dev;
+ rsp = psp_nl_reply_new(info);
+ if (!rsp) {
+ err = -ENOMEM;
+ goto rsp_err;
+ }
+
+ list_add_tail(&psp_assoc_dev->dev_list, &psd->assoc_dev_list);
+
+ put_net(net);
+
+ psp_nl_notify_dev(psd, PSP_CMD_DEV_CHANGE_NTF);
+
+ return psp_nl_reply_send(rsp, info);
+
+rsp_err:
+ rcu_assign_pointer(assoc_dev->psp_dev, NULL);
+cmpxchg_err:
+ netdev_put(assoc_dev, &psp_assoc_dev->dev_tracker);
+assoc_dev_err:
+ kfree(psp_assoc_dev);
+alloc_err:
+ put_net(net);
+
+ return err;
+}
+
+int psp_nl_dev_disassoc_doit(struct sk_buff *skb, struct genl_info *info)
+{
+ struct psp_assoc_dev *entry, *found = NULL;
+ struct psp_dev *psd = info->user_ptr[0];
+ struct sk_buff *rsp;
+ u32 assoc_ifindex;
+ struct net *net;
+ int nsid;
+
+ if (GENL_REQ_ATTR_CHECK(info, PSP_A_DEV_IFINDEX))
+ return -EINVAL;
+
+ if (info->attrs[PSP_A_DEV_NSID]) {
+ nsid = nla_get_s32(info->attrs[PSP_A_DEV_NSID]);
+
+ net = get_net_ns_by_id(genl_info_net(info), nsid);
+ if (!net) {
+ NL_SET_BAD_ATTR(info->extack,
+ info->attrs[PSP_A_DEV_NSID]);
+ return -EINVAL;
+ }
+ } else {
+ net = get_net(genl_info_net(info));
+ }
+
+ assoc_ifindex = nla_get_u32(info->attrs[PSP_A_DEV_IFINDEX]);
+
+ /* Search the association list by ifindex and netns */
+ list_for_each_entry(entry, &psd->assoc_dev_list, dev_list) {
+ if (entry->assoc_dev->ifindex == assoc_ifindex &&
+ dev_net(entry->assoc_dev) == net) {
+ found = entry;
+ break;
+ }
+ }
+
+ if (!found) {
+ put_net(net);
+ NL_SET_BAD_ATTR(info->extack, info->attrs[PSP_A_DEV_IFINDEX]);
+ return -ENODEV;
+ }
+
+ rsp = psp_nl_reply_new(info);
+ if (!rsp) {
+ put_net(net);
+ return -ENOMEM;
+ }
+
+ /* Notify before removal */
+ psp_nl_notify_dev(psd, PSP_CMD_DEV_CHANGE_NTF);
+
+ /* Remove from the association list */
+ list_del(&found->dev_list);
+ rcu_assign_pointer(found->assoc_dev->psp_dev, NULL);
+ netdev_put(found->assoc_dev, &found->dev_tracker);
+ kfree(found);
+
+ put_net(net);
+
+ return psp_nl_reply_send(rsp, info);
+}
+
/* Key etc. */
int psp_assoc_device_get_locked(const struct genl_split_ops *ops,
@@ -320,8 +622,10 @@ int psp_assoc_device_get_locked(const struct genl_split_ops *ops,
psd = psp_dev_get_for_sock(socket->sk);
if (psd) {
+ mutex_lock(&psd->lock);
err = psp_dev_check_access(psd, genl_info_net(info), false);
if (err) {
+ mutex_unlock(&psd->lock);
psp_dev_put(psd);
psd = NULL;
}
@@ -334,7 +638,6 @@ int psp_assoc_device_get_locked(const struct genl_split_ops *ops,
id = info->attrs[PSP_A_ASSOC_DEV_ID];
if (psd) {
- mutex_lock(&psd->lock);
if (id && psd->id != nla_get_u32(id)) {
mutex_unlock(&psd->lock);
NL_SET_ERR_MSG_ATTR(info->extack, id,
--
2.52.0
^ permalink raw reply related
* [PATCH v11 net-next 1/5] psp: add admin/non-admin version of psp_device_get_locked
From: Wei Wang @ 2026-04-08 23:14 UTC (permalink / raw)
To: netdev, Jakub Kicinski, Daniel Zahka, Willem de Bruijn, David Wei,
Andrew Lunn, David S . Miller, Eric Dumazet, Simon Horman
Cc: Wei Wang
In-Reply-To: <20260408231415.522691-1-weibunny.kernel@gmail.com>
From: Wei Wang <weibunny@fb.com>
Introduce 2 versions of psp_device_get_locked:
1. psp_device_get_locked_admin(): This version is used for operations
that would change the status of the psd, and are currently used for
dev-set and key-rotation.
2. psp_device_get_locked(): This is the non-admin version, which are
used for broader user issued operations including: dev-get, rx-assoc,
tx-assoc, get-stats.
Following commit will be implementing both of the checks.
Signed-off-by: Wei Wang <weibunny@fb.com>
Reviewed-by: Daniel Zahka <daniel.zahka@gmail.com>
---
Documentation/netlink/specs/psp.yaml | 4 ++--
net/psp/psp-nl-gen.c | 4 ++--
net/psp/psp-nl-gen.h | 2 ++
net/psp/psp.h | 2 +-
net/psp/psp_main.c | 7 +++++-
net/psp/psp_nl.c | 33 ++++++++++++++++++++--------
6 files changed, 37 insertions(+), 15 deletions(-)
diff --git a/Documentation/netlink/specs/psp.yaml b/Documentation/netlink/specs/psp.yaml
index 100c36cda8e5..c54e1202cbe0 100644
--- a/Documentation/netlink/specs/psp.yaml
+++ b/Documentation/netlink/specs/psp.yaml
@@ -195,7 +195,7 @@ operations:
- psp-versions-ena
reply:
attributes: []
- pre: psp-device-get-locked
+ pre: psp-device-get-locked-admin
post: psp-device-unlock
-
name: dev-change-ntf
@@ -214,7 +214,7 @@ operations:
reply:
attributes:
- id
- pre: psp-device-get-locked
+ pre: psp-device-get-locked-admin
post: psp-device-unlock
-
name: key-rotate-ntf
diff --git a/net/psp/psp-nl-gen.c b/net/psp/psp-nl-gen.c
index 22a48d0fa378..1f5e73e7ccc1 100644
--- a/net/psp/psp-nl-gen.c
+++ b/net/psp/psp-nl-gen.c
@@ -71,7 +71,7 @@ static const struct genl_split_ops psp_nl_ops[] = {
},
{
.cmd = PSP_CMD_DEV_SET,
- .pre_doit = psp_device_get_locked,
+ .pre_doit = psp_device_get_locked_admin,
.doit = psp_nl_dev_set_doit,
.post_doit = psp_device_unlock,
.policy = psp_dev_set_nl_policy,
@@ -80,7 +80,7 @@ static const struct genl_split_ops psp_nl_ops[] = {
},
{
.cmd = PSP_CMD_KEY_ROTATE,
- .pre_doit = psp_device_get_locked,
+ .pre_doit = psp_device_get_locked_admin,
.doit = psp_nl_key_rotate_doit,
.post_doit = psp_device_unlock,
.policy = psp_key_rotate_nl_policy,
diff --git a/net/psp/psp-nl-gen.h b/net/psp/psp-nl-gen.h
index 599c5f1c82f2..977355455395 100644
--- a/net/psp/psp-nl-gen.h
+++ b/net/psp/psp-nl-gen.h
@@ -17,6 +17,8 @@ extern const struct nla_policy psp_keys_nl_policy[PSP_A_KEYS_SPI + 1];
int psp_device_get_locked(const struct genl_split_ops *ops,
struct sk_buff *skb, struct genl_info *info);
+int psp_device_get_locked_admin(const struct genl_split_ops *ops,
+ struct sk_buff *skb, struct genl_info *info);
int psp_assoc_device_get_locked(const struct genl_split_ops *ops,
struct sk_buff *skb, struct genl_info *info);
void
diff --git a/net/psp/psp.h b/net/psp/psp.h
index 9f19137593a0..0f9c4e4e52cb 100644
--- a/net/psp/psp.h
+++ b/net/psp/psp.h
@@ -14,7 +14,7 @@ extern struct xarray psp_devs;
extern struct mutex psp_devs_lock;
void psp_dev_free(struct psp_dev *psd);
-int psp_dev_check_access(struct psp_dev *psd, struct net *net);
+int psp_dev_check_access(struct psp_dev *psd, struct net *net, bool admin);
void psp_nl_notify_dev(struct psp_dev *psd, u32 cmd);
diff --git a/net/psp/psp_main.c b/net/psp/psp_main.c
index 9508b6c38003..82de78a1d6bd 100644
--- a/net/psp/psp_main.c
+++ b/net/psp/psp_main.c
@@ -27,10 +27,15 @@ struct mutex psp_devs_lock;
* psp_dev_check_access() - check if user in a given net ns can access PSP dev
* @psd: PSP device structure user is trying to access
* @net: net namespace user is in
+ * @admin: If true, only allow access from @psd's main device's netns,
+ * for admin operations like config changes and key rotation.
+ * If false, also allow access from network namespaces that have
+ * an associated device with @psd, for read-only and association
+ * management operations.
*
* Return: 0 if PSP device should be visible in @net, errno otherwise.
*/
-int psp_dev_check_access(struct psp_dev *psd, struct net *net)
+int psp_dev_check_access(struct psp_dev *psd, struct net *net, bool admin)
{
if (dev_net(psd->main_netdev) == net)
return 0;
diff --git a/net/psp/psp_nl.c b/net/psp/psp_nl.c
index 6afd7707ec12..eb47a9ee4438 100644
--- a/net/psp/psp_nl.c
+++ b/net/psp/psp_nl.c
@@ -41,7 +41,8 @@ static int psp_nl_reply_send(struct sk_buff *rsp, struct genl_info *info)
/* Device stuff */
static struct psp_dev *
-psp_device_get_and_lock(struct net *net, struct nlattr *dev_id)
+psp_device_get_and_lock(struct net *net, struct nlattr *dev_id,
+ bool admin)
{
struct psp_dev *psd;
int err;
@@ -56,7 +57,7 @@ psp_device_get_and_lock(struct net *net, struct nlattr *dev_id)
mutex_lock(&psd->lock);
mutex_unlock(&psp_devs_lock);
- err = psp_dev_check_access(psd, net);
+ err = psp_dev_check_access(psd, net, admin);
if (err) {
mutex_unlock(&psd->lock);
return ERR_PTR(err);
@@ -65,17 +66,31 @@ psp_device_get_and_lock(struct net *net, struct nlattr *dev_id)
return psd;
}
-int psp_device_get_locked(const struct genl_split_ops *ops,
- struct sk_buff *skb, struct genl_info *info)
+static int __psp_device_get_locked(const struct genl_split_ops *ops,
+ struct sk_buff *skb, struct genl_info *info,
+ bool admin)
{
if (GENL_REQ_ATTR_CHECK(info, PSP_A_DEV_ID))
return -EINVAL;
info->user_ptr[0] = psp_device_get_and_lock(genl_info_net(info),
- info->attrs[PSP_A_DEV_ID]);
+ info->attrs[PSP_A_DEV_ID],
+ admin);
return PTR_ERR_OR_ZERO(info->user_ptr[0]);
}
+int psp_device_get_locked_admin(const struct genl_split_ops *ops,
+ struct sk_buff *skb, struct genl_info *info)
+{
+ return __psp_device_get_locked(ops, skb, info, true);
+}
+
+int psp_device_get_locked(const struct genl_split_ops *ops,
+ struct sk_buff *skb, struct genl_info *info)
+{
+ return __psp_device_get_locked(ops, skb, info, false);
+}
+
void
psp_device_unlock(const struct genl_split_ops *ops, struct sk_buff *skb,
struct genl_info *info)
@@ -160,7 +175,7 @@ static int
psp_nl_dev_get_dumpit_one(struct sk_buff *rsp, struct netlink_callback *cb,
struct psp_dev *psd)
{
- if (psp_dev_check_access(psd, sock_net(rsp->sk)))
+ if (psp_dev_check_access(psd, sock_net(rsp->sk), false))
return 0;
return psp_nl_dev_fill(psd, rsp, genl_info_dump(cb));
@@ -305,7 +320,7 @@ int psp_assoc_device_get_locked(const struct genl_split_ops *ops,
psd = psp_dev_get_for_sock(socket->sk);
if (psd) {
- err = psp_dev_check_access(psd, genl_info_net(info));
+ err = psp_dev_check_access(psd, genl_info_net(info), false);
if (err) {
psp_dev_put(psd);
psd = NULL;
@@ -330,7 +345,7 @@ int psp_assoc_device_get_locked(const struct genl_split_ops *ops,
psp_dev_put(psd);
} else {
- psd = psp_device_get_and_lock(genl_info_net(info), id);
+ psd = psp_device_get_and_lock(genl_info_net(info), id, false);
if (IS_ERR(psd)) {
err = PTR_ERR(psd);
goto err_sock_put;
@@ -573,7 +588,7 @@ static int
psp_nl_stats_get_dumpit_one(struct sk_buff *rsp, struct netlink_callback *cb,
struct psp_dev *psd)
{
- if (psp_dev_check_access(psd, sock_net(rsp->sk)))
+ if (psp_dev_check_access(psd, sock_net(rsp->sk), false))
return 0;
return psp_nl_stats_fill(psd, rsp, genl_info_dump(cb));
--
2.52.0
^ permalink raw reply related
* [PATCH v11 net-next 0/5] psp: Add support for dev-assoc/disassoc
From: Wei Wang @ 2026-04-08 23:14 UTC (permalink / raw)
To: netdev, Jakub Kicinski, Daniel Zahka, Willem de Bruijn, David Wei,
Andrew Lunn, David S . Miller, Eric Dumazet, Simon Horman
Cc: Wei Wang
From: Wei Wang <weibunny@fb.com>
The main purpose of this feature is to associate virtual devices like
veth or netkit with a real PSP device, so we could provide PSP
functionality to the application running with virtual devices.
A typical deployment that works with this feature is as follows:
Host Namespace:
psp_dev_local ←──physically linked──→ psp_dev_peer
(PSP device)
│
│ BPF on psp_dev_local ingress: bpf_redirect_peer() to nk_guest
│
nk_host / veth_host
│
│ BPF on nk_host ingress: bpf_redirect_neigh() to psp_dev_local
│
Guest Namespace (netns):
│
nk_guest / veth_guest
★ PSP application run here
Remote Namespace (_netns):
psp_dev_peer
★ PSP server application runs here
Note:
The general requirement for this feature to work:
For PSP to work correctly, the egress device at validate_xmit_skb()
time must have psp_dev matching the association's psd. Any device
stacking or traffic redirection that changes the egress device will
cause either:
1. TX validation failure (SKB_DROP_REASON_PSP_OUTPUT) - fail-safe
2. RX policy failure after tx-assoc - packets without PSP extension
are rejected by receiver expecting encrypted traffic
Here are a few examples that this feature would not work:
- Bonding with load balancing in round-robin, XOR, 802.3ad mode across
multiple PSP devices, or mixed PSP and non-PSP devices
- Bonding with active-backup mode might work without PSP migration for
failover case.
- ipvlan/macvlan in bridge mode would not work given packets are
loopbacked locally without going through the PSP device.
Changes since v10:
- Corrected typo on patch 1
- Removed the kdoc style comments, Use goto style in
psp_nl_dev_assoc_doit() clean up code, Resolved "TOCTOU" issue in
psp_assoc_device_get_locked() in patch 2
- Replaced psp_devs_lock with a new mutex in
psp_attach_netdev_notifier(), Fixed kdoc style comments in patch 3
Changes since v9:
- Added comments for psp_device_get_locked(), fixed lint issue, fixed
rcu warning in patch 2
- Return error if register_netdevice_notifier() fails in
psp_device_get_locked_dev_assoc() in patch 3
- Removed psp version and ip version for unnecessary tests cases in
patch 5
Changes since v8:
- Rebase
Changes since v7:
- Refactor in patch 1 to have a common helper for
psp_device_get_locked_admin() and psp_device_get_locked()
- Take psd->lock in psp_assoc_device_get_locked() before
psp_dev_check_access() in patch 2
- Use cmpxchg() for assoc_dev->psp_dev assignment when doing dev-assoc
in patch 2
- Check for err for register_netdevice_notifier() in patch 3
- Call psp_attach_netdev_notifier() in pre_doit handler for dev-assoc to
avoid releasing of psd->lock in patch 3
Changes since v6:
- Remove the unused remote_addr, nk_guest_addr and import cmd in patch 5
Changes since v5:
- Remove module_exit() in patch 3
Changes since v4:
- Address compilation warning in patch 3
- Removed the call to psp_nl_has_listeners_any_ns() and check listeners
when looping through netns in psp_nl_notify_dev() in patch 2. This
makes sure we only send notification to netns that has listeners.
Changes since v3:
- Make nsid optional for dev-assoc/dev-disassoc operation, and use
the ns user is in when it's not specified. Also added a test for this.
- Fix psp_nl_notify_dev() to compute the correct nsid relative to the
listener's netns.
- Only register the new netdev event for psp dev cleanup upon the first
successful dev-assoc operation.
- Change the following in selftest:
- Add CONFIG_NETKIT to driver/net's config
- Fall back to NetDrvEpEnv and run basic test cases if NetDrvContEnv
does not load
- Use ksft_variants instead of psp_ip_ver_test_builder
Changes since v2:
- Change the newly added parameter to psp_device_get_and_lock() to
admin in patch 1. Introduce 2 device check functions:
- psp_device_get_locked_admin() for dev-set and key-rotate
- psp_device_get_locked() for all other operations
Flip the logic for checking the dev_assoc_list accordingly in patch 2.
- Move psp_nl_notify_dev() before removing the dev from assoc_dev_list
in psp_nl_dev_disassoc_doit() and correct the typo in commit msg in
patch 2.
- Remove the threading and subprocess and some comment updates in patch 5.
Changes since v1:
- Update the first 4 patches to reflect the latest changes in
https://lore.kernel.org/netdev/20260302053315.1919859-1-dw@davidwei.uk/
- Update patch 9 to add a param to NetDrvContEnv to control the loading
of the tx forwarding bpf program
Wei Wang (5):
psp: add admin/non-admin version of psp_device_get_locked
psp: add new netlink cmd for dev-assoc and dev-disassoc
psp: add a new netdev event for dev unregister
selftests/net: Add bpf skb forwarding program
selftest/net: psp: Add test for dev-assoc/disassoc
Documentation/netlink/specs/psp.yaml | 71 ++-
include/net/psp/types.h | 15 +
include/uapi/linux/psp.h | 13 +
net/psp/psp-nl-gen.c | 36 +-
net/psp/psp-nl-gen.h | 7 +
net/psp/psp.h | 3 +-
net/psp/psp_main.c | 102 +++-
net/psp/psp_nl.c | 374 +++++++++++++-
tools/testing/selftests/drivers/net/config | 1 +
.../drivers/net/hw/nk_redirect.bpf.c | 60 +++
.../selftests/drivers/net/lib/py/env.py | 54 ++-
tools/testing/selftests/drivers/net/psp.py | 457 ++++++++++++++++--
12 files changed, 1132 insertions(+), 61 deletions(-)
create mode 100644 tools/testing/selftests/drivers/net/hw/nk_redirect.bpf.c
--
2.52.0
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox