* Re: [PATCH net-next v3 0/5] veth: add Byte Queue Limits (BQL) support
From: Jesper Dangaard Brouer @ 2026-05-01 7:28 UTC (permalink / raw)
To: Jakub Kicinski
Cc: netdev, kernel-team, David S. Miller, Eric Dumazet, Paolo Abeni,
Simon Horman, Shuah Khan, linux-kselftest, Chris Arges,
Mike Freemon, Toke Høiland-Jørgensen,
Jonas Köppeler, Breno Leitao, Alexei Starovoitov,
Daniel Borkmann, John Fastabend, Stanislav Fomichev, bpf
In-Reply-To: <20260430185825.43b2d0aa@kernel.org>
On 01/05/2026 03.58, Jakub Kicinski wrote:
> On Wed, 29 Apr 2026 19:20:27 +0200 hawk@kernel.org wrote:
>> This series adds BQL (Byte Queue Limits) to the veth driver, reducing
>> latency by dynamically limiting in-flight packets in the ptr_ring and
>> moving buffering into the qdisc where AQM algorithms can act on it.
>
> Does not apply (am I remembering right that it's not the first time
> this series doesn't apply?)
Rebased posted
V4: https://lore.kernel.org/all/20260501071633.644353-1-hawk@kernel.org/
The conflict was on tools/testing/selftests/net/config for a change
applied 16 Apr, and I send patchset 29 Apr. So, yes it seems I forgot
to rebase for that period, sorry.
--Jesper
^ permalink raw reply
* Re: [PATCH] kcov: refactor common handle ID into kcov_common_handle_id
From: Dmitry Vyukov @ 2026-05-01 7:39 UTC (permalink / raw)
To: Jann Horn
Cc: Andrey Konovalov, kasan-dev, Andrew Morton, Alexander Potapenko,
Valentina Manea, Shuah Khan, Shuah Khan, Hongren Zheng, linux-usb,
Michael S. Tsirkin, Jason Wang, Eugenio Pérez, kvm,
virtualization, netdev, linux-kernel
In-Reply-To: <20260430-kcov-refactor-common-handle-v1-1-23a0c7a0ba38@google.com>
On Thu, 30 Apr 2026 at 16:15, Jann Horn <jannh@google.com> wrote:
>
> Store common handle IDs in "struct kcov_common_handle_id", which consumes
> no space in non-KCOV builds.
> This cleanup removes #ifdef boilerplate code from subsystems that
> integrate with KCOV (in particular in usbip_common.h and skbuff.h, see the
> diffstat).
> This should also make it easier to add KCOV remote coverage to more
> subsystems in the future.
>
> Signed-off-by: Jann Horn <jannh@google.com>
Reviewed-by: Dmitry Vyukov <dvyukov@google.com>
Thanks!
> ---
> drivers/usb/usbip/usbip_common.h | 29 +----------------------------
> drivers/usb/usbip/vhci_rx.c | 4 ++--
> drivers/usb/usbip/vhci_sysfs.c | 2 +-
> drivers/vhost/vhost.h | 2 +-
> include/linux/kcov.h | 12 ++++++------
> include/linux/skbuff.h | 14 +++-----------
> include/linux/types.h | 6 ++++++
> kernel/kcov.c | 6 +++---
> 8 files changed, 23 insertions(+), 52 deletions(-)
>
> diff --git a/drivers/usb/usbip/usbip_common.h b/drivers/usb/usbip/usbip_common.h
> index 282efca64a01..be4c5e65a7f8 100644
> --- a/drivers/usb/usbip/usbip_common.h
> +++ b/drivers/usb/usbip/usbip_common.h
> @@ -282,9 +282,7 @@ struct usbip_device {
> void (*unusable)(struct usbip_device *);
> } eh_ops;
>
> -#ifdef CONFIG_KCOV
> - u64 kcov_handle;
> -#endif
> + struct kcov_common_handle_id kcov_handle;
> };
>
> #define kthread_get_run(threadfn, data, namefmt, ...) \
> @@ -339,29 +337,4 @@ static inline int interface_to_devnum(struct usb_interface *interface)
> return udev->devnum;
> }
>
> -#ifdef CONFIG_KCOV
> -
> -static inline void usbip_kcov_handle_init(struct usbip_device *ud)
> -{
> - ud->kcov_handle = kcov_common_handle();
> -}
> -
> -static inline void usbip_kcov_remote_start(struct usbip_device *ud)
> -{
> - kcov_remote_start_common(ud->kcov_handle);
> -}
> -
> -static inline void usbip_kcov_remote_stop(void)
> -{
> - kcov_remote_stop();
> -}
> -
> -#else /* CONFIG_KCOV */
> -
> -static inline void usbip_kcov_handle_init(struct usbip_device *ud) { }
> -static inline void usbip_kcov_remote_start(struct usbip_device *ud) { }
> -static inline void usbip_kcov_remote_stop(void) { }
> -
> -#endif /* CONFIG_KCOV */
> -
> #endif /* __USBIP_COMMON_H */
> diff --git a/drivers/usb/usbip/vhci_rx.c b/drivers/usb/usbip/vhci_rx.c
> index a75f4a898a41..a678e7c89837 100644
> --- a/drivers/usb/usbip/vhci_rx.c
> +++ b/drivers/usb/usbip/vhci_rx.c
> @@ -261,9 +261,9 @@ int vhci_rx_loop(void *data)
> if (usbip_event_happened(ud))
> break;
>
> - usbip_kcov_remote_start(ud);
> + kcov_remote_start_common(ud->kcov_handle);
> vhci_rx_pdu(ud);
> - usbip_kcov_remote_stop();
> + kcov_remote_stop();
> }
>
> return 0;
> diff --git a/drivers/usb/usbip/vhci_sysfs.c b/drivers/usb/usbip/vhci_sysfs.c
> index 5bc8c47788d4..b98d14c43d13 100644
> --- a/drivers/usb/usbip/vhci_sysfs.c
> +++ b/drivers/usb/usbip/vhci_sysfs.c
> @@ -425,7 +425,7 @@ static ssize_t attach_store(struct device *dev, struct device_attribute *attr,
> vdev->ud.tcp_rx = tcp_rx;
> vdev->ud.tcp_tx = tcp_tx;
> vdev->ud.status = VDEV_ST_NOTASSIGNED;
> - usbip_kcov_handle_init(&vdev->ud);
> + vdev->ud.kcov_handle = kcov_common_handle();
>
> spin_unlock(&vdev->ud.lock);
> spin_unlock_irqrestore(&vhci->lock, flags);
> diff --git a/drivers/vhost/vhost.h b/drivers/vhost/vhost.h
> index 4fe99765c5c7..0192ade6e749 100644
> --- a/drivers/vhost/vhost.h
> +++ b/drivers/vhost/vhost.h
> @@ -44,7 +44,7 @@ struct vhost_worker {
> /* Used to serialize device wide flushing with worker swapping. */
> struct mutex mutex;
> struct llist_head work_list;
> - u64 kcov_handle;
> + struct kcov_common_handle_id kcov_handle;
> u32 id;
> int attachment_cnt;
> bool killed;
> diff --git a/include/linux/kcov.h b/include/linux/kcov.h
> index 0143358874b0..cdb72b3859d8 100644
> --- a/include/linux/kcov.h
> +++ b/include/linux/kcov.h
> @@ -43,11 +43,11 @@ do { \
> /* See Documentation/dev-tools/kcov.rst for usage details. */
> void kcov_remote_start(u64 handle);
> void kcov_remote_stop(void);
> -u64 kcov_common_handle(void);
> +struct kcov_common_handle_id kcov_common_handle(void);
>
> -static inline void kcov_remote_start_common(u64 id)
> +static inline void kcov_remote_start_common(struct kcov_common_handle_id id)
> {
> - kcov_remote_start(kcov_remote_handle(KCOV_SUBSYSTEM_COMMON, id));
> + kcov_remote_start(kcov_remote_handle(KCOV_SUBSYSTEM_COMMON, id.val));
> }
>
> static inline void kcov_remote_start_usb(u64 id)
> @@ -99,11 +99,11 @@ static inline void kcov_prepare_switch(struct task_struct *t) {}
> static inline void kcov_finish_switch(struct task_struct *t) {}
> static inline void kcov_remote_start(u64 handle) {}
> static inline void kcov_remote_stop(void) {}
> -static inline u64 kcov_common_handle(void)
> +static inline struct kcov_common_handle_id kcov_common_handle(void)
> {
> - return 0;
> + return (struct kcov_common_handle_id){};
> }
> -static inline void kcov_remote_start_common(u64 id) {}
> +static inline void kcov_remote_start_common(struct kcov_common_handle_id id) {}
> static inline void kcov_remote_start_usb(u64 id) {}
> static inline void kcov_remote_start_usb_softirq(u64 id) {}
> static inline void kcov_remote_stop_softirq(void) {}
> diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h
> index 2bcf78a4de7b..a3fe418f7ced 100644
> --- a/include/linux/skbuff.h
> +++ b/include/linux/skbuff.h
> @@ -1082,9 +1082,7 @@ struct sk_buff {
> __u16 network_header;
> __u16 mac_header;
>
> -#ifdef CONFIG_KCOV
> - u64 kcov_handle;
> -#endif
> + struct kcov_common_handle_id kcov_handle;
>
> ); /* end headers group */
>
> @@ -5437,20 +5435,14 @@ static inline void skb_reset_csum_not_inet(struct sk_buff *skb)
> }
>
> static inline void skb_set_kcov_handle(struct sk_buff *skb,
> - const u64 kcov_handle)
> + struct kcov_common_handle_id kcov_handle)
> {
> -#ifdef CONFIG_KCOV
> skb->kcov_handle = kcov_handle;
> -#endif
> }
>
> -static inline u64 skb_get_kcov_handle(struct sk_buff *skb)
> +static inline struct kcov_common_handle_id skb_get_kcov_handle(struct sk_buff *skb)
> {
> -#ifdef CONFIG_KCOV
> return skb->kcov_handle;
> -#else
> - return 0;
> -#endif
> }
>
> static inline void skb_mark_for_recycle(struct sk_buff *skb)
> diff --git a/include/linux/types.h b/include/linux/types.h
> index 608050dbca6a..93166b0b0617 100644
> --- a/include/linux/types.h
> +++ b/include/linux/types.h
> @@ -224,6 +224,12 @@ struct ustat {
> char f_fpack[6];
> };
>
> +struct kcov_common_handle_id {
> +#ifdef CONFIG_KCOV
> + u64 val;
> +#endif
> +};
> +
> /**
> * struct callback_head - callback structure for use with RCU and task_work
> * @next: next update requests in a list
> diff --git a/kernel/kcov.c b/kernel/kcov.c
> index 0b369e88c7c9..a43e33a28adb 100644
> --- a/kernel/kcov.c
> +++ b/kernel/kcov.c
> @@ -1083,11 +1083,11 @@ void kcov_remote_stop(void)
> EXPORT_SYMBOL(kcov_remote_stop);
>
> /* See the comment before kcov_remote_start() for usage details. */
> -u64 kcov_common_handle(void)
> +struct kcov_common_handle_id kcov_common_handle(void)
> {
> if (!in_task())
> - return 0;
> - return current->kcov_handle;
> + return (struct kcov_common_handle_id){ .val = 0 };
> + return (struct kcov_common_handle_id){ .val = current->kcov_handle };
> }
> EXPORT_SYMBOL(kcov_common_handle);
>
>
> ---
> base-commit: 57b8e2d666a31fa201432d58f5fe3469a0dd83ba
> change-id: 20260430-kcov-refactor-common-handle-25178495b2eb
>
> --
> Jann Horn <jannh@google.com>
>
^ permalink raw reply
* [PATCH v1 net] af_unix: Set gc_in_progress to true in unix_gc().
From: Kuniyuki Iwashima @ 2026-05-01 7:39 UTC (permalink / raw)
To: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni
Cc: Simon Horman, Kuniyuki Iwashima, Kuniyuki Iwashima, netdev,
Igor Ushakov
Igor Ushakov reported that unix_gc() could run with gc_in_progress
being false if the work is scheduled while running:
Thread 1 Thread 2 Thread 3
-------- -------- --------
unix_schedule_gc() unix_schedule_gc()
`- if (!gc_in_progress) `- if (!gc_in_progress)
|- gc_in_progress = true |
`- queue_work() |
unix_gc() <----------------/ |
| |- gc_in_progress = true
... `- queue_work()
| |
`- gc_in_progress = false |
|
unix_gc() <---------------------------------------------'
|
... /* gc_in_progress == false */
|
`- gc_in_progress = false
unix_peek_fpl() relies on gc_in_progress not to confuse GC
by MSG_PEEK.
Let's set gc_in_progress to true in unix_gc().
Fixes: 8b90a9f819dc ("af_unix: Run GC on only one CPU.")
Reported-by: Igor Ushakov <sysroot314@gmail.com>
Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com>
---
net/unix/garbage.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/net/unix/garbage.c b/net/unix/garbage.c
index a7967a345827..0783555e2526 100644
--- a/net/unix/garbage.c
+++ b/net/unix/garbage.c
@@ -607,6 +607,8 @@ static void unix_gc(struct work_struct *work)
struct sk_buff_head hitlist;
struct sk_buff *skb;
+ WRITE_ONCE(gc_in_progress, true);
+
spin_lock(&unix_gc_lock);
if (unix_graph_state == UNIX_GRAPH_NOT_CYCLIC) {
@@ -649,10 +651,8 @@ void unix_schedule_gc(struct user_struct *user)
READ_ONCE(user->unix_inflight) < UNIX_INFLIGHT_SANE_USER)
return;
- if (!READ_ONCE(gc_in_progress)) {
- WRITE_ONCE(gc_in_progress, true);
+ if (!READ_ONCE(gc_in_progress))
queue_work(system_dfl_wq, &unix_gc_work);
- }
if (user && READ_ONCE(unix_graph_cyclic_sccs))
flush_work(&unix_gc_work);
--
2.54.0.545.g6539524ca2-goog
^ permalink raw reply related
* [PATCH net v4] ipv6: flowlabel: enforce per-netns limit for unprivileged callers
From: Maoyi Xie @ 2026-05-01 7:41 UTC (permalink / raw)
To: netdev
Cc: willemdebruijn.kernel, willemb, edumazet, pabeni, kuba, davem,
dsahern, kuznet, linux-kernel, stable
fl_size, fl_ht and ip6_fl_lock in net/ipv6/ip6_flowlabel.c are file
scope and shared across netns. mem_check() reads fl_size to decide
whether to deny non-CAP_NET_ADMIN callers; capable() runs against
init_user_ns, so an unprivileged user in any non-init userns can
push fl_size past FL_MAX_SIZE - FL_MAX_SIZE/4 and starve every
other unprivileged userns on the host.
Add struct netns_ipv6::flowlabel_count, bumped and decremented next
to fl_size in fl_intern, ip6_fl_gc and ip6_fl_purge. The new field
is placed in the existing 4-byte hole after ipmr_seq, so struct
netns_ipv6 stays the same size on 64-bit builds.
mem_check() folds an extra FL_MAX_SIZE/8 ceiling into the existing
non-CAP_NET_ADMIN conditional.
Bump FL_MAX_SIZE from 4096 to 8192. It has been 4096 since the file
was added; machines and connection counts have grown. The new
per-netns ceiling is then 1024 flowlabels, half of FL_MAX_SIZE/4.
CAP_NET_ADMIN against init_user_ns still bypasses both caps.
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Suggested-by: Willem de Bruijn <willemb@google.com>
Cc: stable@vger.kernel.org # v5.15+
Signed-off-by: Maoyi Xie <maoyi.xie@ntu.edu.sg>
---
v4 (this submission, addressing v3 review by Willem):
- rephrased the flowlabel_count placement note: dropped the
flowlabel_has_excl cacheline argument; replaced with the
simpler "fills the existing 4-byte hole after ipmr_seq" fact.
- reordered atomic_dec(&...flowlabel_count) to sit immediately
after atomic_dec(&fl_size) in ip6_fl_gc and ip6_fl_purge so
the pairing is visually obvious. Both decs now happen before
fl_free(fl) since fl_free invalidates fl->fl_net. fl_intern
was already in this order.
v3: addressed Willem's review on the private security@ thread;
merged FL_MAX_SIZE doubling, dropped test data, moved
flowlabel_count near ipmr_seq, inlined fl->fl_net in ip6_fl_gc.
v2: per-netns counter + cap, sent to security@ as a 2-patch series.
v1: fix-shape sketch in original disclosure.
include/net/netns/ipv6.h | 1 +
net/ipv6/ip6_flowlabel.c | 14 ++++++++++----
2 files changed, 11 insertions(+), 4 deletions(-)
diff --git a/include/net/netns/ipv6.h b/include/net/netns/ipv6.h
index 34bdb1308..329482373 100644
--- a/include/net/netns/ipv6.h
+++ b/include/net/netns/ipv6.h
@@ -119,6 +119,7 @@ struct netns_ipv6 {
struct fib_notifier_ops *notifier_ops;
struct fib_notifier_ops *ip6mr_notifier_ops;
unsigned int ipmr_seq; /* protected by rtnl_mutex */
+ atomic_t flowlabel_count;
struct {
struct hlist_head head;
spinlock_t lock;
diff --git a/net/ipv6/ip6_flowlabel.c b/net/ipv6/ip6_flowlabel.c
index c92f98c6f..360109cad 100644
--- a/net/ipv6/ip6_flowlabel.c
+++ b/net/ipv6/ip6_flowlabel.c
@@ -36,7 +36,7 @@
/* FL hash table */
#define FL_MAX_PER_SOCK 32
-#define FL_MAX_SIZE 4096
+#define FL_MAX_SIZE 8192
#define FL_HASH_MASK 255
#define FL_HASH(l) (ntohl(l)&FL_HASH_MASK)
@@ -162,8 +162,9 @@ static void ip6_fl_gc(struct timer_list *unused)
ttd = fl->expires;
if (time_after_eq(now, ttd)) {
*flp = fl->next;
- fl_free(fl);
atomic_dec(&fl_size);
+ atomic_dec(&fl->fl_net->ipv6.flowlabel_count);
+ fl_free(fl);
continue;
}
if (!sched || time_before(ttd, sched))
@@ -195,8 +196,9 @@ static void __net_exit ip6_fl_purge(struct net *net)
if (net_eq(fl->fl_net, net) &&
atomic_read(&fl->users) == 0) {
*flp = fl->next;
- fl_free(fl);
atomic_dec(&fl_size);
+ atomic_dec(&net->ipv6.flowlabel_count);
+ fl_free(fl);
continue;
}
flp = &fl->next;
@@ -245,6 +247,7 @@ static struct ip6_flowlabel *fl_intern(struct net *net,
fl->next = fl_ht[FL_HASH(fl->label)];
rcu_assign_pointer(fl_ht[FL_HASH(fl->label)], fl);
atomic_inc(&fl_size);
+ atomic_inc(&net->ipv6.flowlabel_count);
spin_unlock_bh(&ip6_fl_lock);
rcu_read_unlock();
return NULL;
@@ -464,6 +467,7 @@ fl_create(struct net *net, struct sock *sk, struct in6_flowlabel_req *freq,
static int mem_check(struct sock *sk)
{
+ struct net *net = sock_net(sk);
int room = FL_MAX_SIZE - atomic_read(&fl_size);
struct ipv6_fl_socklist *sfl;
int count = 0;
@@ -478,7 +482,9 @@ static int mem_check(struct sock *sk)
if (room <= 0 ||
((count >= FL_MAX_PER_SOCK ||
- (count > 0 && room < FL_MAX_SIZE/2) || room < FL_MAX_SIZE/4) &&
+ (count > 0 && room < FL_MAX_SIZE/2) ||
+ room < FL_MAX_SIZE/4 ||
+ atomic_read(&net->ipv6.flowlabel_count) >= FL_MAX_SIZE/8) &&
!capable(CAP_NET_ADMIN)))
return -ENOBUFS;
--
2.34.1
^ permalink raw reply related
* [PATCH net-next v3] net: airoha: Introduce airoha_fe_get()/airoha_qdma_get() register read helpers
From: Lorenzo Bianconi @ 2026-05-01 7:49 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Lorenzo Bianconi
Cc: Simon Horman, linux-arm-kernel, linux-mediatek, netdev
Add airoha_fe_get() and airoha_qdma_get() as utility routines for reading
a masked field from a specified register.
This is a non-functional refactor, no logical changes are introduced to
the existing codebase.
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
---
Changes in v3:
- Rebase on top of net-next main branch.
- Link to v2: https://lore.kernel.org/r/20260429-airoha_fe_get-airoha_qdma_get-v2-1-909e791f1dc0@kernel.org
Changes in v2:
- Fix typo in airoha_qdma_get() definition.
- Link to v1: https://lore.kernel.org/r/20260428-airoha_fe_get-airoha_qdma_get-v1-1-6cfbdeb42743@kernel.org
---
drivers/net/ethernet/airoha/airoha_eth.c | 13 ++++---------
drivers/net/ethernet/airoha/airoha_eth.h | 4 ++++
drivers/net/ethernet/airoha/airoha_ppe.c | 5 ++---
3 files changed, 10 insertions(+), 12 deletions(-)
diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c
index e9e1645e8f9b..4222de6878d2 100644
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -201,15 +201,13 @@ static void airoha_fe_vip_setup(struct airoha_eth *eth)
static u32 airoha_fe_get_pse_queue_rsv_pages(struct airoha_eth *eth,
u32 port, u32 queue)
{
- u32 val;
-
airoha_fe_rmw(eth, REG_FE_PSE_QUEUE_CFG_WR,
PSE_CFG_PORT_ID_MASK | PSE_CFG_QUEUE_ID_MASK,
FIELD_PREP(PSE_CFG_PORT_ID_MASK, port) |
FIELD_PREP(PSE_CFG_QUEUE_ID_MASK, queue));
- val = airoha_fe_rr(eth, REG_FE_PSE_QUEUE_CFG_VAL);
- return FIELD_GET(PSE_CFG_OQ_RSV_MASK, val);
+ return airoha_fe_get(eth, REG_FE_PSE_QUEUE_CFG_VAL,
+ PSE_CFG_OQ_RSV_MASK);
}
static void airoha_fe_set_pse_queue_rsv_pages(struct airoha_eth *eth,
@@ -227,9 +225,7 @@ static void airoha_fe_set_pse_queue_rsv_pages(struct airoha_eth *eth,
static u32 airoha_fe_get_pse_all_rsv(struct airoha_eth *eth)
{
- u32 val = airoha_fe_rr(eth, REG_FE_PSE_BUF_SET);
-
- return FIELD_GET(PSE_ALLRSV_MASK, val);
+ return airoha_fe_get(eth, REG_FE_PSE_BUF_SET, PSE_ALLRSV_MASK);
}
static int airoha_fe_set_pse_oq_rsv(struct airoha_eth *eth,
@@ -247,8 +243,7 @@ static int airoha_fe_set_pse_oq_rsv(struct airoha_eth *eth,
FIELD_PREP(PSE_ALLRSV_MASK, all_rsv));
/* modify hthd */
- tmp = airoha_fe_rr(eth, PSE_FQ_CFG);
- fq_limit = FIELD_GET(PSE_FQ_LIMIT_MASK, tmp);
+ fq_limit = airoha_fe_get(eth, PSE_FQ_CFG, PSE_FQ_LIMIT_MASK);
tmp = fq_limit - all_rsv - 0x20;
airoha_fe_rmw(eth, REG_PSE_SHARE_USED_THD,
PSE_SHARE_USED_HTHD_MASK,
diff --git a/drivers/net/ethernet/airoha/airoha_eth.h b/drivers/net/ethernet/airoha/airoha_eth.h
index 717e4a7c59dd..58530d096de7 100644
--- a/drivers/net/ethernet/airoha/airoha_eth.h
+++ b/drivers/net/ethernet/airoha/airoha_eth.h
@@ -619,6 +619,8 @@ u32 airoha_rmw(void __iomem *base, u32 offset, u32 mask, u32 val);
airoha_rmw((eth)->fe_regs, (offset), 0, (val))
#define airoha_fe_clear(eth, offset, val) \
airoha_rmw((eth)->fe_regs, (offset), (val), 0)
+#define airoha_fe_get(eth, offset, mask) \
+ FIELD_GET((mask), airoha_fe_rr((eth), (offset)))
#define airoha_qdma_rr(qdma, offset) \
airoha_rr((qdma)->regs, (offset))
@@ -630,6 +632,8 @@ u32 airoha_rmw(void __iomem *base, u32 offset, u32 mask, u32 val);
airoha_rmw((qdma)->regs, (offset), 0, (val))
#define airoha_qdma_clear(qdma, offset, val) \
airoha_rmw((qdma)->regs, (offset), (val), 0)
+#define airoha_qdma_get(qdma, offset, mask) \
+ FIELD_GET((mask), airoha_qdma_rr((qdma), (offset)))
static inline u16 airoha_qdma_get_txq(struct airoha_qdma *qdma, u16 qid)
{
diff --git a/drivers/net/ethernet/airoha/airoha_ppe.c b/drivers/net/ethernet/airoha/airoha_ppe.c
index 5c9dff6bccd1..697af6fdd4c3 100644
--- a/drivers/net/ethernet/airoha/airoha_ppe.c
+++ b/drivers/net/ethernet/airoha/airoha_ppe.c
@@ -80,9 +80,8 @@ bool airoha_ppe_is_enabled(struct airoha_eth *eth, int index)
static u32 airoha_ppe_get_timestamp(struct airoha_ppe *ppe)
{
- u16 timestamp = airoha_fe_rr(ppe->eth, REG_FE_FOE_TS);
-
- return FIELD_GET(AIROHA_FOE_IB1_BIND_TIMESTAMP, timestamp);
+ return airoha_fe_get(ppe->eth, REG_FE_FOE_TS,
+ AIROHA_FOE_IB1_BIND_TIMESTAMP);
}
void airoha_ppe_set_cpu_port(struct airoha_gdm_port *port, u8 ppe_id, u8 fport)
---
base-commit: edf4bee4215a173c0534d1851d7523d827149f9e
change-id: 20260428-airoha_fe_get-airoha_qdma_get-7a087a23aef4
Best regards,
--
Lorenzo Bianconi <lorenzo@kernel.org>
^ permalink raw reply related
* Re: [PATCH net-next v2] net: airoha: Introduce airoha_fe_get()/airoha_qdma_get() register read helpers
From: Lorenzo Bianconi @ 2026-05-01 7:50 UTC (permalink / raw)
To: Jakub Kicinski
Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Paolo Abeni,
Simon Horman, linux-arm-kernel, linux-mediatek, netdev
In-Reply-To: <20260430181109.68c65940@kernel.org>
[-- Attachment #1: Type: text/plain, Size: 529 bytes --]
> On Wed, 29 Apr 2026 12:54:32 +0200 Lorenzo Bianconi wrote:
> > Add airoha_fe_get() and airoha_qdma_get() as utility routines for reading
> > a masked field from a specified register.
> > This is a non-functional refactor, no logical changes are introduced to
> > the existing codebase.
>
> Doesn't apply AFAICT.
> If this is because of the net -> net-next merge - please avoid posting
> patches which will cause a conflict on merge.
ack, sorry, I did not notice it. I will rebase and post v3.
Regards,
Lorenzo
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply
* Re: [PATCH net-next v3 5/5] selftests: net: add veth BQL stress test
From: Breno Leitao @ 2026-05-01 8:42 UTC (permalink / raw)
To: hawk
Cc: netdev, kernel-team, Jonas Köppeler, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
Shuah Khan, linux-kernel, linux-kselftest
In-Reply-To: <20260429172036.1028526-6-hawk@kernel.org>
On Wed, Apr 29, 2026 at 07:20:32PM +0200, hawk@kernel.org wrote:
> +compile_tools() {
> + echo "--- Compiling UDP flood tool ---"
> +cat > "$TMPDIR"/udp_flood.c << 'CEOF'
The biggest structural issue: the test ships ~250 lines of C source as
heredocs and shells out to gcc at runtime to build them. This is not
how selftests normally work.
^ permalink raw reply
* Re: [PATCH net] openvswitch: vport: fix race between tunnel creation and linking
From: Eelco Chaudron @ 2026-05-01 8:53 UTC (permalink / raw)
To: Ilya Maximets
Cc: netdev, Aaron Conole, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Simon Horman, dev, linux-kernel,
Yuan Tan, Yifan Wu, Juefei Pu, Xin Liu, Yang Yang
In-Reply-To: <20260430213349.407991-1-i.maximets@ovn.org>
On 30 Apr 2026, at 23:32, Ilya Maximets wrote:
> When a tunnel vport is created it first creates the tunnel device, e.g.,
> with geneve_dev_create_fb(), then it calls ovs_netdev_link() to take a
> reference and link it to the device that represents openvswitch datapath.
>
> The creation of the device is happening under RTNL, but then RTNL is
> released and re-acquired to find the device by name. It is technically
> possible for the tunnel device to be re-named or deleted within that
> window while RTNL is not held, and some other device created in its
> place. This will cause a non-tunnel device to be referenced in the
> vport and tunnel-specific functions used on it, e.g. vxlan_get_options()
> that directly casts the private netdev data into a struct vxlan_dev
> causing an invalid memory access:
>
> BUG: KASAN: slab-use-after-free in vxlan_get_options+0x323/0x3a0
> vxlan_get_options+0x323/0x3a0
> ovs_vport_cmd_new+0x6e3/0xd30
>
> Fix that by taking a reference to the just created device before
> releasing RTNL. This ensures that the device in the vport is always
> the one that was just created. The search by name is only needed
> for a standard vport-netdev that links pre-existing devices, so that
> functionality and device type checks are moved to netdev_create().
>
> It is also awkward that ovs_netdev_link() takes ownership of the vport
> and destroys it on failure. It doesn't know the type of the port it is
> dealing with, so we need to pass down the indicator that it's a tunnel,
> so the link can be properly deleted on failure.
>
> It's possible to refactor the logic to make the ovs_netdev_link() do
> only the linking part and let the callers perform a proper destruction,
> but it will be much more code for each legacy tunnel port type, so it
> is not worth it for the bug fix.
>
> Fixes: 614732eaa12d ("openvswitch: Use regular VXLAN net_device device")
> Reported-by: Yuan Tan <tanyuan98@outlook.com>
> Reported-by: Yifan Wu <yifanwucs@gmail.com>
> Reported-by: Juefei Pu <tomapufckgml@gmail.com>
> Reported-by: Xin Liu <bird@lzu.edu.cn>
> Reported-by: Yang Yang <n05ec@lzu.edu.cn>
> Signed-off-by: Ilya Maximets <i.maximets@ovn.org>
Thanks for working on this Ilya! The changes look good to me.
Acked-by: Eelco Chaudron <echaudro@redhat.com>
^ permalink raw reply
* Re: [PATCH net v2 2/2] selftests: openvswitch: add tests for tunnel vport refcounting
From: Eelco Chaudron @ 2026-05-01 8:56 UTC (permalink / raw)
To: Ilya Maximets
Cc: netdev, Aaron Conole, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Simon Horman, Shuah Khan, Yuan Tan,
Yang Yang, dev, linux-kernel, linux-kselftest
In-Reply-To: <20260430233848.440994-3-i.maximets@ovn.org>
On 1 May 2026, at 1:38, Ilya Maximets wrote:
> There were a few issues found with the tunnel vport types around the
> vport destruction code. Add some basic tests, so at least we know that
> they can be properly added and removed without obvious issues.
>
> The test creates OVS datapath, adds a non-LWT tunnel port, makes sure
> they are created, and then removes the datapath and waits for all the
> ports to be gone.
>
> The dpctl script had a few bugs in the none-lwt tunnel creation code,
> so fixing them as well to make the testing possible:
> - The type of the --lwt option changed in order to properly disable it.
> - Removed byte order conversion for the port numbers, as the value
> supposed to be in the host order.
> - Added missing 'gre' choice for the tunnel type.
>
> Signed-off-by: Ilya Maximets <i.maximets@ovn.org>
Changes on the v2 look good to me!
Acked-by: Eelco Chaudron <echaudro@redhat.com>
^ permalink raw reply
* Re: [PATCH net v2] net: mana: Optimize irq affinity for low vcpu configs
From: Simon Horman @ 2026-05-01 9:12 UTC (permalink / raw)
To: Shradha Gupta
Cc: Dexuan Cui, Wei Liu, Haiyang Zhang, K. Y. Srinivasan, Andrew Lunn,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Konstantin Taranov, Erni Sri Satya Vennela, Dipayaan Roy,
Shiraz Saleem, Michael Kelley, Long Li, Yury Norov, linux-hyperv,
linux-kernel, netdev, Paul Rosswurm, Shradha Gupta,
Saurabh Singh Sengar, stable
In-Reply-To: <20260429090640.1790104-1-shradhagupta@linux.microsoft.com>
On Wed, Apr 29, 2026 at 02:06:37AM -0700, Shradha Gupta wrote:
> In mana driver, the number of IRQs allocated is capped by the
> min(num_cpu + 1, queue count). In cases, where the IRQ count is greater
> than the vcpu count, we want to utilize all the vCPUs, irrespective of
> their NUMA/core bindings.
>
> This is important, especially in the envs where number of vCPUs are so
> few that the softIRQ handling overhead on two IRQs on the same vCPU is
> much more than their overheads if they were spread across sibling vCPUs.
>
> This behaviour is more evident with dynamic IRQ allocation. Since MANA
> IRQs are assigned at a later stage compared to static allocation, other
> device IRQs may already be affinitized to the vCPUs. As a result, IRQ
> weights become imbalanced, causing multiple MANA IRQs to land on the
> same vCPU, while some vCPUs have none.
>
> In such cases when many parallel TCP connections are tested, the
> throughput drops significantly.
...
> Fixes: 755391121038 ("net: mana: Allocate MSI-X vectors dynamically")
> Cc: stable@vger.kernel.org
> Co-developed-by: Erni Sri Satya Vennela <ernis@linux.microsoft.com>
> Signed-off-by: Erni Sri Satya Vennela <ernis@linux.microsoft.com>
> Signed-off-by: Shradha Gupta <shradhagupta@linux.microsoft.com>
> Reviewed-by: Haiyang Zhang <haiyangz@microsoft.com>
> ---
> Changes in v2
> * Removed the unused skip_first_cpu variable
> * fixed exit condition in irq_setup_linear() with len == 0
> * changed return type of irq_setup_linear() as it will always be 0
> * removed the unnecessary rcu_read_lock() in irq_setup_linear()
> * added appropriate comments to indicate expected behaviour when
> IRQs are more than or equal to num_online_cpus()
Reviewed-by: Simon Horman <horms@kernel.org>
^ permalink raw reply
* [PATCH net v2] ice: Fix missing 1's complement negation in GCS raw checksum
From: Matt Fleming @ 2026-05-01 9:57 UTC (permalink / raw)
To: Tony Nguyen
Cc: Aleksandr Loktionov, kernel-team, Matt Fleming, stable,
Simon Horman, Przemek Kitszel, Andrew Lunn, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Eric Joyner,
Paul Greenwalt, Alice Michael, intel-wired-lan, netdev,
linux-kernel
From: Matt Fleming <mfleming@cloudflare.com>
Commit 905d1a220e8d ("ice: Add E830 checksum offload support") added
Generic Checksum (GCS) support for E830 NICs but omitted the 1's
complement negation (~) when converting the hardware raw_csum to
skb->csum for CHECKSUM_COMPLETE.
Without the negation, every CHECKSUM_COMPLETE packet fails the
fast-path validation in nf_ip_checksum() and falls through to software
checksumming via __skb_checksum_complete(), which triggers the
rate-limited "hw csum failure" warning. Packets are still accepted
(the software recheck passes) but hardware checksum offload is
effectively disabled and the warning floods dmesg on systems running
nf_conntrack on VLAN sub-interfaces.
Multiple other drivers (idpf, ehea, iwlwifi, cassini, sunhme, enetc)
also apply ~ for CHECKSUM_COMPLETE. The ice driver was the only in-tree
user of csum_unfold() for CHECKSUM_COMPLETE that omitted it.
Fixes: 905d1a220e8d ("ice: Add E830 checksum offload support")
Cc: stable@vger.kernel.org
Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
Reviewed-by: Simon Horman <horms@kernel.org>
Signed-off-by: Matt Fleming <mfleming@cloudflare.com>
---
v2:
- Add Cc: stable@vger.kernel.org (Aleksandr)
- Pick up Reviewed-by tags from Aleksandr and Simon
- No code changes
v1: https://lore.kernel.org/netdev/20260408190214.1287708-1-matt@readmodwrite.com/
drivers/net/ethernet/intel/ice/ice_txrx_lib.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/net/ethernet/intel/ice/ice_txrx_lib.c b/drivers/net/ethernet/intel/ice/ice_txrx_lib.c
index e695a664e53d..c177579e0114 100644
--- a/drivers/net/ethernet/intel/ice/ice_txrx_lib.c
+++ b/drivers/net/ethernet/intel/ice/ice_txrx_lib.c
@@ -92,7 +92,7 @@ static void ice_rx_gcs(struct sk_buff *skb,
desc = (struct ice_32b_rx_flex_desc_nic *)rx_desc;
skb->ip_summed = CHECKSUM_COMPLETE;
csum = (__force u16)desc->raw_csum;
- skb->csum = csum_unfold((__force __sum16)swab16(csum));
+ skb->csum = csum_unfold((__force __sum16)~swab16(csum));
}
/**
--
2.43.0
^ permalink raw reply related
* [PATCH net] netpoll: pass buffer size to egress_dev() to avoid MAC truncation
From: Breno Leitao @ 2026-05-01 9:58 UTC (permalink / raw)
To: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Uday Shankar
Cc: netdev, linux-kernel, clm, kernel-team, stable, Breno Leitao
egress_dev() formats np->dev_mac via snprintf() but receives buf as
a bare char *, so it cannot derive the buffer size from the pointer. The
size argument was hardcoded to MAC_ADDR_STR_LEN (3 * ETH_ALEN - 1 = 17),
which is silly wrong in two ways:
1) misleading kernel log output on the MAC-selected target path
(np->dev_name[0] == '\0'); for example "aa:bb:cc:dd:ee:ff doesn't
exist, aborting" was logged as "aa:bb:cc:dd:ee:f doesn't exist,
aborting".
2) the second argument of snprintf is the size of the buffer, not the
size of what you want to write.
Add a bufsz parameter to egress_dev() and pass sizeof(buf) from each
caller, matching the standard snprintf() idiom and removing the
hardcoded size from the helper.
Every caller already declares "char buf[MAC_ADDR_STR_LEN + 1]" so the
formatted MAC continues to fit.
Tested by booting with
netconsole=6665@/aa:bb:cc:dd:ee:ff,6666@10.0.0.1/00:11:22:33:44:55
on a kernel without a matching device. Pre-fix dmesg shows
"aa:bb:cc:dd:ee:f doesn't exist, aborting"; post-fix shows the full
"aa:bb:cc:dd:ee:ff doesn't exist, aborting".
Fixes: f8a10bed32f5 ("netconsole: allow selection of egress interface via MAC address")
Cc: stable@vger.kernel.org
Signed-off-by: Breno Leitao <leitao@debian.org>
---
net/core/netpoll.c | 23 +++++++++++++----------
1 file changed, 13 insertions(+), 10 deletions(-)
diff --git a/net/core/netpoll.c b/net/core/netpoll.c
index 4381e0fc25bf4..84faace50ac28 100644
--- a/net/core/netpoll.c
+++ b/net/core/netpoll.c
@@ -608,14 +608,16 @@ EXPORT_SYMBOL_GPL(__netpoll_setup);
/*
* Returns a pointer to a string representation of the identifier used
* to select the egress interface for the given netpoll instance. buf
- * must be a buffer of length at least MAC_ADDR_STR_LEN + 1.
+ * is used to format np->dev_mac when np->dev_name is empty; bufsz must
+ * be at least MAC_ADDR_STR_LEN + 1 to fit the formatted MAC address
+ * and its NUL terminator.
*/
-static char *egress_dev(struct netpoll *np, char *buf)
+static char *egress_dev(struct netpoll *np, char *buf, size_t bufsz)
{
if (np->dev_name[0])
return np->dev_name;
- snprintf(buf, MAC_ADDR_STR_LEN, "%pM", np->dev_mac);
+ snprintf(buf, bufsz, "%pM", np->dev_mac);
return buf;
}
@@ -645,7 +647,7 @@ static int netpoll_take_ipv6(struct netpoll *np, struct net_device *ndev)
if (!IS_ENABLED(CONFIG_IPV6)) {
np_err(np, "IPv6 is not supported %s, aborting\n",
- egress_dev(np, buf));
+ egress_dev(np, buf, sizeof(buf)));
return -EINVAL;
}
@@ -667,7 +669,7 @@ static int netpoll_take_ipv6(struct netpoll *np, struct net_device *ndev)
}
if (err) {
np_err(np, "no IPv6 address for %s, aborting\n",
- egress_dev(np, buf));
+ egress_dev(np, buf, sizeof(buf)));
return err;
}
@@ -687,14 +689,14 @@ static int netpoll_take_ipv4(struct netpoll *np, struct net_device *ndev)
in_dev = __in_dev_get_rtnl(ndev);
if (!in_dev) {
np_err(np, "no IP address for %s, aborting\n",
- egress_dev(np, buf));
+ egress_dev(np, buf, sizeof(buf)));
return -EDESTADDRREQ;
}
ifa = rtnl_dereference(in_dev->ifa_list);
if (!ifa) {
np_err(np, "no IP address for %s, aborting\n",
- egress_dev(np, buf));
+ egress_dev(np, buf, sizeof(buf)));
return -EDESTADDRREQ;
}
@@ -736,7 +738,8 @@ int netpoll_setup(struct netpoll *np)
ndev = dev_getbyhwaddr(net, ARPHRD_ETHER, np->dev_mac);
if (!ndev) {
- np_err(np, "%s doesn't exist, aborting\n", egress_dev(np, buf));
+ np_err(np, "%s doesn't exist, aborting\n",
+ egress_dev(np, buf, sizeof(buf)));
err = -ENODEV;
goto unlock;
}
@@ -744,14 +747,14 @@ int netpoll_setup(struct netpoll *np)
if (netdev_master_upper_dev_get(ndev)) {
np_err(np, "%s is a slave device, aborting\n",
- egress_dev(np, buf));
+ egress_dev(np, buf, sizeof(buf)));
err = -EBUSY;
goto put;
}
if (!netif_running(ndev)) {
np_info(np, "device %s not up yet, forcing it\n",
- egress_dev(np, buf));
+ egress_dev(np, buf, sizeof(buf)));
err = dev_open(ndev, NULL);
if (err) {
---
base-commit: edf4bee4215a173c0534d1851d7523d827149f9e
change-id: 20260501-netpoll_snprintf_fix-95b40d048f18
Best regards,
--
Breno Leitao <leitao@debian.org>
^ permalink raw reply related
* Re: [PATCH net] net/mlx5e: Fix use-after-free in mlx5e_tx_reporter_timeout_recover
From: Matt Fleming @ 2026-05-01 10:03 UTC (permalink / raw)
To: Saeed Mahameed, Leon Romanovsky, Tariq Toukan, Mark Bloch
Cc: Andrew Lunn, David S . Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, netdev, linux-rdma, linux-kernel, kernel-team
In-Reply-To: <20260408184458.1274662-1-matt@readmodwrite.com>
On Wed, Apr 08, 2026 at 07:44:58PM +0100, Matt Fleming wrote:
> From: Matt Fleming <mfleming@cloudflare.com>
>
> mlx5e_tx_reporter_timeout_recover() accesses sq->netdev after
> mlx5e_safe_reopen_channels() has torn down and freed the channel (and
> its embedded SQs). Replace the three sq->netdev references with
> priv->netdev which is safe because priv outlives channel teardown.
>
> The netdev_err() call already used priv->netdev for this reason; make
> the trylock/unlock and health_channel_eq_recover calls consistent.
>
> This fixes the following KASAN splat:
>
> BUG: KASAN: use-after-free in mlx5e_tx_reporter_timeout_recover+0x1dd/0x360 [mlx5_core]
> Read of size 8 at addr ffff889860ed0b28 by task kworker/u113:2/5277
>
> Call Trace:
> mlx5e_tx_reporter_timeout_recover+0x1dd/0x360 [mlx5_core]
> devlink_health_reporter_recover+0xa2/0x150
> devlink_health_report+0x254/0x7c0
> mlx5e_reporter_tx_timeout+0x297/0x380 [mlx5_core]
> mlx5e_tx_timeout_work+0x109/0x170 [mlx5_core]
> process_one_work+0x677/0xf20
> worker_thread+0x51f/0xd90
> kthread+0x3a5/0x810
> ret_from_fork+0x208/0x400
> ret_from_fork_asm+0x1a/0x30
>
> Fixes: 83ac0304a2d7 ("net/mlx5e: Fix deadlocks between devlink and netdev instance locks")
> Signed-off-by: Matt Fleming <mfleming@cloudflare.com>
> ---
> drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c | 6 +++---
> 1 file changed, 3 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c b/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c
> index afdeb1b3d425..8409ae73768f 100644
> --- a/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c
> +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c
> @@ -160,13 +160,13 @@ static int mlx5e_tx_reporter_timeout_recover(void *ctx)
> * channels are being closed for other reason and this work is not
> * relevant anymore.
> */
> - while (!netdev_trylock(sq->netdev)) {
> + while (!netdev_trylock(priv->netdev)) {
> if (!test_bit(MLX5E_STATE_CHANNELS_ACTIVE, &priv->state))
> return 0;
> msleep(20);
> }
>
> - err = mlx5e_health_channel_eq_recover(sq->netdev, eq, sq->cq.ch_stats);
> + err = mlx5e_health_channel_eq_recover(priv->netdev, eq, sq->cq.ch_stats);
> if (!err) {
> to_ctx->status = 0; /* this sq recovered */
> goto out;
> @@ -186,7 +186,7 @@ static int mlx5e_tx_reporter_timeout_recover(void *ctx)
> "mlx5e_safe_reopen_channels failed recovering from a tx_timeout, err(%d).\n",
> err);
> out:
> - netdev_unlock(sq->netdev);
> + netdev_unlock(priv->netdev);
> return err;
> }
>
> --
> 2.43.0
>
Hey there, any thoughts on this?
Thanks,
Matt
^ permalink raw reply
* [PATCH v3 1/1] net: phy: realtek: Add support for PHY LEDs on RTL8221B
From: Chukun Pan @ 2026-05-01 10:00 UTC (permalink / raw)
To: David S . Miller
Cc: Andrew Lunn, Paolo Abeni, Jakub Kicinski, Eric Dumazet,
Russell King, Daniel Golle, Heiner Kallweit, linux-kernel, netdev,
Chukun Pan
Realtek RTL8221B Ethernet PHY supports three LED pins which are used to
indicate link status and activity. Add netdev trigger support for them.
Signed-off-by: Chukun Pan <amadeus@jmu.edu.cn>
---
Changes in v3:
- In led_hw_control_set, reset the LED polarity to default.
- Link to v2: https://lore.kernel.org/all/20260420100800.2435204-1-amadeus@jmu.edu.cn/
Changes in v2:
- Invert the LED polarity in led_brightness_set to achieve LED_ON.
- Link to v1: https://lore.kernel.org/all/20260401100010.3079700-1-amadeus@jmu.edu.cn/
---
drivers/net/phy/realtek/realtek_main.c | 165 +++++++++++++++++++++++++
1 file changed, 165 insertions(+)
diff --git a/drivers/net/phy/realtek/realtek_main.c b/drivers/net/phy/realtek/realtek_main.c
index 79c867ef64da..e0b37e8d3410 100644
--- a/drivers/net/phy/realtek/realtek_main.c
+++ b/drivers/net/phy/realtek/realtek_main.c
@@ -165,6 +165,18 @@
#define RTL8221B_VND2_INSR 0xa4d4
+#define RTL822X_VND2_LED(x) (0xd032 + ((x) * 2))
+#define RTL822X_VND2_LCR_LINK_10 BIT(0)
+#define RTL822X_VND2_LCR_LINK_100 BIT(1)
+#define RTL822X_VND2_LCR_LINK_1000 BIT(2)
+#define RTL822X_VND2_LCR_LINK_2500 BIT(5)
+
+#define RTL822X_VND2_LCR6 0xd040
+#define RTL822X_VND2_LED_ACT(x) BIT(x)
+
+#define RTL822X_VND2_LCR7 0xd044
+#define RTL822X_VND2_LED_POLAR(x) BIT(x)
+
#define RTL8224_MII_RTCT 0x11
#define RTL8224_MII_RTCT_ENABLE BIT(0)
#define RTL8224_MII_RTCT_PAIR_A BIT(4)
@@ -1797,6 +1809,151 @@ static int rtl822xb_c45_read_status(struct phy_device *phydev)
return 0;
}
+static int rtl822xb_led_brightness_set(struct phy_device *phydev, u8 index,
+ enum led_brightness value)
+{
+ int ret;
+
+ if (index >= RTL8211x_LED_COUNT)
+ return -EINVAL;
+
+ /* clear HW LED setup */
+ ret = phy_write_mmd(phydev, MDIO_MMD_VEND2,
+ RTL822X_VND2_LED(index), 0);
+ if (ret < 0)
+ return ret;
+
+ /* clear HW LED blink */
+ ret = phy_clear_bits_mmd(phydev, MDIO_MMD_VEND2, RTL822X_VND2_LCR6,
+ RTL822X_VND2_LED_ACT(index));
+ if (ret < 0)
+ return ret;
+
+ if (value != LED_OFF)
+ return phy_set_bits_mmd(phydev, MDIO_MMD_VEND2,
+ RTL822X_VND2_LCR7,
+ RTL822X_VND2_LED_POLAR(index));
+ else
+ return phy_clear_bits_mmd(phydev, MDIO_MMD_VEND2,
+ RTL822X_VND2_LCR7,
+ RTL822X_VND2_LED_POLAR(index));
+}
+
+static int rtl822xb_led_hw_is_supported(struct phy_device *phydev, u8 index,
+ unsigned long rules)
+{
+ const unsigned long act_mask = BIT(TRIGGER_NETDEV_RX) |
+ BIT(TRIGGER_NETDEV_TX);
+
+ const unsigned long link_mask = BIT(TRIGGER_NETDEV_LINK) |
+ BIT(TRIGGER_NETDEV_LINK_10) |
+ BIT(TRIGGER_NETDEV_LINK_100) |
+ BIT(TRIGGER_NETDEV_LINK_1000) |
+ BIT(TRIGGER_NETDEV_LINK_2500);
+
+ if (index >= RTL8211x_LED_COUNT)
+ return -EINVAL;
+
+ /* Filter out any other unsupported triggers. */
+ if (rules & ~(link_mask | act_mask))
+ return -EOPNOTSUPP;
+
+ /* RX and TX are not differentiated, they are not possible
+ * without combination with a link trigger.
+ */
+ if ((rules & act_mask) && !(rules & link_mask))
+ return -EOPNOTSUPP;
+
+ return 0;
+}
+
+static int rtl822xb_led_hw_control_get(struct phy_device *phydev, u8 index,
+ unsigned long *rules)
+{
+ int val;
+
+ if (index >= RTL8211x_LED_COUNT)
+ return -EINVAL;
+
+ val = phy_read_mmd(phydev, MDIO_MMD_VEND2, RTL822X_VND2_LED(index));
+ if (val < 0)
+ return val;
+
+ if (val & RTL822X_VND2_LCR_LINK_10)
+ __set_bit(TRIGGER_NETDEV_LINK_10, rules);
+
+ if (val & RTL822X_VND2_LCR_LINK_100)
+ __set_bit(TRIGGER_NETDEV_LINK_100, rules);
+
+ if (val & RTL822X_VND2_LCR_LINK_1000)
+ __set_bit(TRIGGER_NETDEV_LINK_1000, rules);
+
+ if (val & RTL822X_VND2_LCR_LINK_2500)
+ __set_bit(TRIGGER_NETDEV_LINK_2500, rules);
+
+ if ((val & RTL822X_VND2_LCR_LINK_10) &&
+ (val & RTL822X_VND2_LCR_LINK_100) &&
+ (val & RTL822X_VND2_LCR_LINK_1000) &&
+ (val & RTL822X_VND2_LCR_LINK_2500))
+ __set_bit(TRIGGER_NETDEV_LINK, rules);
+
+ val = phy_read_mmd(phydev, MDIO_MMD_VEND2, RTL822X_VND2_LCR6);
+ if (val < 0)
+ return val;
+
+ if (val & RTL822X_VND2_LED_ACT(index)) {
+ __set_bit(TRIGGER_NETDEV_RX, rules);
+ __set_bit(TRIGGER_NETDEV_TX, rules);
+ }
+
+ return 0;
+}
+
+static int rtl822xb_led_hw_control_set(struct phy_device *phydev, u8 index,
+ unsigned long rules)
+{
+ u16 val = 0;
+ bool act;
+ int ret;
+
+ if (index >= RTL8211x_LED_COUNT)
+ return -EINVAL;
+
+ if (test_bit(TRIGGER_NETDEV_LINK, &rules) ||
+ test_bit(TRIGGER_NETDEV_LINK_10, &rules))
+ val |= RTL822X_VND2_LCR_LINK_10;
+
+ if (test_bit(TRIGGER_NETDEV_LINK, &rules) ||
+ test_bit(TRIGGER_NETDEV_LINK_100, &rules))
+ val |= RTL822X_VND2_LCR_LINK_100;
+
+ if (test_bit(TRIGGER_NETDEV_LINK, &rules) ||
+ test_bit(TRIGGER_NETDEV_LINK_1000, &rules))
+ val |= RTL822X_VND2_LCR_LINK_1000;
+
+ if (test_bit(TRIGGER_NETDEV_LINK, &rules) ||
+ test_bit(TRIGGER_NETDEV_LINK_2500, &rules))
+ val |= RTL822X_VND2_LCR_LINK_2500;
+
+ ret = phy_write_mmd(phydev, MDIO_MMD_VEND2,
+ RTL822X_VND2_LED(index), val);
+ if (ret < 0)
+ return ret;
+
+ act = test_bit(TRIGGER_NETDEV_RX, &rules) ||
+ test_bit(TRIGGER_NETDEV_TX, &rules);
+
+ ret = phy_modify_mmd(phydev, MDIO_MMD_VEND2, RTL822X_VND2_LCR6,
+ RTL822X_VND2_LED_ACT(index), act ?
+ RTL822X_VND2_LED_ACT(index) : 0);
+ if (ret < 0)
+ return ret;
+
+ /* Reset polarity to default */
+ return phy_clear_bits_mmd(phydev, MDIO_MMD_VEND2, RTL822X_VND2_LCR7,
+ RTL822X_VND2_LED_POLAR(index));
+}
+
static int rtl8224_cable_test_start(struct phy_device *phydev)
{
u32 val;
@@ -2565,6 +2722,10 @@ static struct phy_driver realtek_drvs[] = {
.write_page = rtl821x_write_page,
.read_mmd = rtl822xb_read_mmd,
.write_mmd = rtl822xb_write_mmd,
+ .led_brightness_set = rtl822xb_led_brightness_set,
+ .led_hw_is_supported = rtl822xb_led_hw_is_supported,
+ .led_hw_control_get = rtl822xb_led_hw_control_get,
+ .led_hw_control_set = rtl822xb_led_hw_control_set,
}, {
.match_phy_device = rtl8221b_vm_cg_match_phy_device,
.name = "RTL8221B-VM-CG 2.5Gbps PHY",
@@ -2584,6 +2745,10 @@ static struct phy_driver realtek_drvs[] = {
.write_page = rtl821x_write_page,
.read_mmd = rtl822xb_read_mmd,
.write_mmd = rtl822xb_write_mmd,
+ .led_brightness_set = rtl822xb_led_brightness_set,
+ .led_hw_is_supported = rtl822xb_led_hw_is_supported,
+ .led_hw_control_get = rtl822xb_led_hw_control_get,
+ .led_hw_control_set = rtl822xb_led_hw_control_set,
}, {
.match_phy_device = rtl8251b_c45_match_phy_device,
.name = "RTL8251B 5Gbps PHY",
--
2.34.1
^ permalink raw reply related
* Re: [PATCH iproute2-next 1/2] dpll: add ps unit to phase-related pin attributes
From: Petr Oros @ 2026-05-01 10:20 UTC (permalink / raw)
To: Ivan Vecera, netdev; +Cc: David Ahern, Stephen Hemminger
In-Reply-To: <20260428152115.2815860-2-ivecera@redhat.com>
On 4/28/26 17:21, Ivan Vecera wrote:
> Display phase-adjust-min, phase-adjust-max and phase-adjust values
> with ps unit. Add DPLL_PR_PHASE_OFFSET macro that properly formats
> phase-offset as fractional picoseconds by dividing the raw kernel
> value by DPLL_PHASE_OFFSET_DIVIDER.
>
> Signed-off-by: Ivan Vecera <ivecera@redhat.com>
> ---
> dpll/dpll.c | 27 ++++++++++++++++++++++-----
> 1 file changed, 22 insertions(+), 5 deletions(-)
>
> diff --git a/dpll/dpll.c b/dpll/dpll.c
> index b6ba3283e0ba..e8056ff6a24b 100644
> --- a/dpll/dpll.c
> +++ b/dpll/dpll.c
> @@ -410,6 +410,21 @@ static __s64 mnl_attr_get_sint(const struct nlattr *attr)
> } \
> } while (0)
>
> +/* Phase offset - JSON prints raw sub-ps value, FP prints fractional ps */
> +#define DPLL_PR_PHASE_OFFSET(tb, attr_id) \
> + do { \
> + if (tb[attr_id]) { \
> + __s64 val = mnl_attr_get_sint(tb[attr_id]); \
> + lldiv_t d = lldiv(llabs(val), \
> + DPLL_PHASE_OFFSET_DIVIDER); \
> + print_s64(PRINT_JSON, "phase-offset", NULL, val); \
> + print_string(PRINT_FP, NULL, " phase-offset %s", \
> + val < 0 ? "-" : ""); \
> + print_s64(PRINT_FP, NULL, "%lld.", d.quot); \
> + print_s64(PRINT_FP, NULL, "%03lld ps", d.rem); \
> + } \
> + } while (0)
> +
> /* Generic version with custom format */
> #define DPLL_PR_ENUM_STR_FMT(tb, attr_id, name, format_str, name_func) \
> do { \
> @@ -1507,8 +1522,7 @@ static void dpll_pin_print_parent_devices(struct nlattr *attr)
> " prio %u");
> DPLL_PR_ENUM_STR_FMT(tb_parent, DPLL_A_PIN_STATE, "state",
> " state %s", dpll_pin_state_name);
> - DPLL_PR_SINT_FMT(tb_parent, DPLL_A_PIN_PHASE_OFFSET,
> - "phase-offset", " phase-offset %" PRId64);
> + DPLL_PR_PHASE_OFFSET(tb_parent, DPLL_A_PIN_PHASE_OFFSET);
>
> print_nl();
> close_json_object();
> @@ -1592,10 +1606,13 @@ static void dpll_pin_print_attrs(struct nlattr **tb)
>
> dpll_pin_print_capabilities(tb[DPLL_A_PIN_CAPABILITIES]);
>
> - DPLL_PR_INT(tb, DPLL_A_PIN_PHASE_ADJUST_MIN, "phase-adjust-min");
> - DPLL_PR_INT(tb, DPLL_A_PIN_PHASE_ADJUST_MAX, "phase-adjust-max");
> + DPLL_PR_INT_FMT(tb, DPLL_A_PIN_PHASE_ADJUST_MIN, "phase-adjust-min",
> + " phase-adjust-min: %d ps\n");
> + DPLL_PR_INT_FMT(tb, DPLL_A_PIN_PHASE_ADJUST_MAX, "phase-adjust-max",
> + " phase-adjust-max: %d ps\n");
> DPLL_PR_UINT(tb, DPLL_A_PIN_PHASE_ADJUST_GRAN, "phase-adjust-gran");
> - DPLL_PR_INT(tb, DPLL_A_PIN_PHASE_ADJUST, "phase-adjust");
> + DPLL_PR_INT_FMT(tb, DPLL_A_PIN_PHASE_ADJUST, "phase-adjust",
> + " phase-adjust: %d ps\n");
>
> if (json || !tb[DPLL_A_PIN_FRACTIONAL_FREQUENCY_OFFSET_PPT])
> DPLL_PR_SINT(tb, DPLL_A_PIN_FRACTIONAL_FREQUENCY_OFFSET,
Reviewed-by: Petr Oros <poros@redhat.com>
^ permalink raw reply
* Re: [PATCH iproute2-next 2/2] dpll: add frequency monitoring support
From: Petr Oros @ 2026-05-01 10:20 UTC (permalink / raw)
To: Ivan Vecera, netdev; +Cc: David Ahern, Stephen Hemminger
In-Reply-To: <20260428152115.2815860-3-ivecera@redhat.com>
On 4/28/26 17:21, Ivan Vecera wrote:
> Add support for the new frequency monitoring feature from the kernel
> patch series "dpll: add actual frequency monitoring feature". This
> includes:
>
> - DPLL_A_FREQUENCY_MONITOR device attribute (enable/disable)
> - DPLL_A_PIN_MEASURED_FREQUENCY pin attribute displayed as fractional Hz
> using DPLL_PR_MEASURED_FREQUENCY macro (kernel reports in mHz)
> - device set: frequency-monitor { enable | disable }
> - Refactor phase-offset-monitor to use new dpll_parse_attr_feature_state
> helper shared with frequency-monitor
> - Update man page and bash-completion
>
> Signed-off-by: Ivan Vecera <ivecera@redhat.com>
> ---
> bash-completion/dpll | 4 +--
> dpll/dpll.c | 62 ++++++++++++++++++++++++++++++++++----------
> man/man8/dpll.8 | 19 ++++++++++++--
> 3 files changed, 68 insertions(+), 17 deletions(-)
>
> diff --git a/bash-completion/dpll b/bash-completion/dpll
> index 542b99c2fce2..7ddcf529d429 100644
> --- a/bash-completion/dpll
> +++ b/bash-completion/dpll
> @@ -100,7 +100,7 @@ _dpll_device()
> COMPREPLY=( $( compgen -W "automatic manual" -- "$cur" ) )
> return 0
> ;;
> - phase-offset-monitor)
> + phase-offset-monitor|frequency-monitor)
> COMPREPLY=( $( compgen -W "enable disable true false 0 1" -- "$cur" ) )
> return 0
> ;;
> @@ -110,7 +110,7 @@ _dpll_device()
> ;;
> *)
> COMPREPLY=( $( compgen -W "id mode phase-offset-monitor \
> - phase-offset-avg-factor" -- "$cur" ) )
> + phase-offset-avg-factor frequency-monitor" -- "$cur" ) )
> return 0
> ;;
> esac
> diff --git a/dpll/dpll.c b/dpll/dpll.c
> index e8056ff6a24b..6d8c0cbb8a34 100644
> --- a/dpll/dpll.c
> +++ b/dpll/dpll.c
> @@ -313,6 +313,26 @@ static int dpll_parse_attr_str(struct dpll *dpll, struct nlmsghdr *nlh,
> return 0;
> }
>
> +static int dpll_parse_attr_feature_state(struct dpll *dpll,
> + struct nlmsghdr *nlh,
> + const char *arg_name, int attr_id)
> +{
> + const char *str = dpll_argv_next(dpll);
> + bool val;
> +
> + if (!str) {
> + pr_err("%s requires an argument\n", arg_name);
> + return -EINVAL;
> + }
> + if (str_to_bool(str, &val)) {
> + pr_err("invalid %s value: %s (use enable/disable)\n",
> + arg_name, str);
> + return -EINVAL;
> + }
> + mnl_attr_put_u32(nlh, attr_id, val ? 1 : 0);
> + return 0;
> +}
> +
> static int dpll_parse_attr_enum(struct dpll *dpll, struct nlmsghdr *nlh,
> const char *arg_name, int attr_id,
> int (*parse_func)(struct dpll *, __u32 *))
> @@ -425,6 +445,21 @@ static __s64 mnl_attr_get_sint(const struct nlattr *attr)
> } \
> } while (0)
>
> +/* Measured frequency - JSON prints raw mHz value, FP prints fractional Hz */
> +#define DPLL_PR_MEASURED_FREQUENCY(tb, attr_id) \
> + do { \
> + if (tb[attr_id]) { \
> + __u64 val = mnl_attr_get_u64(tb[attr_id]); \
> + lldiv_t d = lldiv(val, \
> + DPLL_PIN_MEASURED_FREQUENCY_DIVIDER); \
> + print_lluint(PRINT_JSON, "measured-frequency", \
> + NULL, val); \
> + print_s64(PRINT_FP, NULL, \
> + " measured-frequency: %lld.", d.quot); \
> + print_s64(PRINT_FP, NULL, "%03lld Hz\n", d.rem); \
> + } \
> + } while (0)
> +
> /* Generic version with custom format */
> #define DPLL_PR_ENUM_STR_FMT(tb, attr_id, name, format_str, name_func) \
> do { \
> @@ -657,6 +692,7 @@ static void cmd_device_help(void)
> pr_err(" dpll device set id DEVICE_ID [ mode { automatic | manual } ]\n");
> pr_err(" [ phase-offset-monitor { enable | disable } ]\n");
> pr_err(" [ phase-offset-avg-factor NUM ]\n");
> + pr_err(" [ frequency-monitor { enable | disable } ]\n");
> pr_err(" dpll device id-get [ module-name NAME ] [ clock-id ID ] [ type TYPE ]\n");
> }
>
> @@ -1058,6 +1094,10 @@ static void dpll_device_print_attrs(const struct nlmsghdr *nlh,
> str_enable_disable);
> DPLL_PR_UINT(tb, DPLL_A_PHASE_OFFSET_AVG_FACTOR,
> "phase-offset-avg-factor");
> + DPLL_PR_ENUM_STR_FMT(tb, DPLL_A_FREQUENCY_MONITOR,
> + "frequency-monitor",
> + " frequency-monitor: %s\n",
> + str_enable_disable);
> }
>
> /* Netlink callback - device get (single device) */
> @@ -1219,25 +1259,20 @@ static int cmd_device_set(struct dpll *dpll)
> dpll_parse_mode))
> return -EINVAL;
> } else if (dpll_argv_match(dpll, "phase-offset-monitor")) {
> - const char *str = dpll_argv_next(dpll);
> - bool val;
> -
> - if (!str) {
> - pr_err("phase-offset-monitor requires an argument\n");
> - return -EINVAL;
> - }
> - if (str_to_bool(str, &val)) {
> - pr_err("invalid phase-offset-monitor value: %s (use enable/disable)\n",
> - str);
> + if (dpll_parse_attr_feature_state(dpll, nlh,
> + "phase-offset-monitor",
> + DPLL_A_PHASE_OFFSET_MONITOR))
> return -EINVAL;
> - }
> - mnl_attr_put_u32(nlh, DPLL_A_PHASE_OFFSET_MONITOR,
> - val ? 1 : 0);
> } else if (dpll_argv_match(dpll, "phase-offset-avg-factor")) {
> if (dpll_parse_attr_u32(dpll, nlh,
> "phase-offset-avg-factor",
> DPLL_A_PHASE_OFFSET_AVG_FACTOR))
> return -EINVAL;
> + } else if (dpll_argv_match(dpll, "frequency-monitor")) {
> + if (dpll_parse_attr_feature_state(dpll, nlh,
> + "frequency-monitor",
> + DPLL_A_FREQUENCY_MONITOR))
> + return -EINVAL;
> } else {
> pr_err("unknown option: %s\n", dpll_argv(dpll));
> return -EINVAL;
> @@ -1601,6 +1636,7 @@ static void dpll_pin_print_attrs(struct nlattr **tb)
> DPLL_PR_ENUM_STR(tb, DPLL_A_PIN_TYPE, "type", dpll_pin_type_name);
> DPLL_PR_U64_FMT(tb, DPLL_A_PIN_FREQUENCY, "frequency",
> " frequency: %" PRIu64 " Hz\n");
> + DPLL_PR_MEASURED_FREQUENCY(tb, DPLL_A_PIN_MEASURED_FREQUENCY);
>
> dpll_pin_print_freq_supported(tb[DPLL_A_PIN_FREQUENCY_SUPPORTED]);
>
> diff --git a/man/man8/dpll.8 b/man/man8/dpll.8
> index 89f17af74923..59ec4208f251 100644
> --- a/man/man8/dpll.8
> +++ b/man/man8/dpll.8
> @@ -111,7 +111,7 @@ Temperature (if supported)
> Type (PPS or EEC)
> .RE
>
> -.SS dpll device set id ID [ mode { automatic | manual } ] [ phase-offset-monitor { enable | disable } ] [ phase-offset-avg-factor FACTOR ]
> +.SS dpll device set id ID [ mode { automatic | manual } ] [ phase-offset-monitor { enable | disable } ] [ phase-offset-avg-factor FACTOR ] [ frequency-monitor { enable | disable } ]
>
> Configure DPLL device parameters.
>
> @@ -140,6 +140,14 @@ When enabled, the kernel continuously measures and reports phase differences.
> Set the averaging factor (1-255) applied to phase offset calculations.
> Higher values provide smoother but slower-responding measurements.
>
> +.TP
> +.BI frequency-monitor " { enable | disable | true | false | 0 | 1 }"
> +Enable or disable frequency monitoring on the device. When enabled, the
> +kernel continuously measures and reports actual pin frequencies, which can
> +be read via the
> +.B measured-frequency
> +field in pin show output.
> +
> .SS dpll device id-get [ module-name NAME ] [ clock-id ID ] [ type TYPE ]
>
> Retrieve the device ID based on identifying attributes. Useful for scripting
> @@ -233,7 +241,9 @@ Board label (hardware label from device tree or ACPI)
> .IP \[bu]
> Pin type (mux, ext, synce-eth-port, int-oscillator, gnss)
> .IP \[bu]
> -Frequency and supported frequency ranges
> +Configured frequency and supported frequency ranges
> +.IP \[bu]
> +Measured frequency in Hz (when frequency monitoring is enabled)
> .IP \[bu]
> Capabilities (state-can-change, priority-can-change, direction-can-change)
> .IP \[bu]
> @@ -372,6 +382,11 @@ Press Ctrl+C to stop monitoring.
> .B dpll device set id 0 phase-offset-monitor enable
> .fi
>
> +.SS Enable frequency monitoring on device 0
> +.nf
> +.B dpll device set id 0 frequency-monitor enable
> +.fi
> +
> .SS Show all EEC devices
> .nf
> .B dpll device show type eec
Reviewed-by: Petr Oros <poros@redhat.com>
^ permalink raw reply
* Re: [PATCH net-next v2 1/3] netfilter: conntrack: add shared port parser for helpers
From: Phil Sutter @ 2026-05-01 10:25 UTC (permalink / raw)
To: HACKE-RC
Cc: Pablo Neira Ayuso, Florian Westphal, David S . Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
netfilter-devel, coreteam, netdev, linux-kernel
In-Reply-To: <20260501063156.2520780-2-rc@rexion.ai>
Hi,
On Fri, May 01, 2026 at 12:01:54PM +0530, HACKE-RC wrote:
> Add nf_ct_helper_parse_port() to the conntrack helper core. This
> provides a port parser that does not rely on nul-terminated strings,
> taking an explicit length parameter and validating the result fits
> in the 1-65535 range.
>
> Modeled after the approach in 8cf6809cddcb ("netfilter:
> nf_conntrack_sip: don't use simple_strtoul") but as a shared
> function so IRC, Amanda, and other helpers can use it instead of
> open-coding simple_strtoul calls with ad-hoc range checks.
>
> Signed-off-by: HACKE-RC <rc@rexion.ai>
> ---
> include/net/netfilter/nf_conntrack_helper.h | 3 +++
> net/netfilter/nf_conntrack_helper.c | 28 +++++++++++++++++++++
> 2 files changed, 31 insertions(+)
>
> diff --git a/include/net/netfilter/nf_conntrack_helper.h b/include/net/netfilter/nf_conntrack_helper.h
> index de2f956ab..db19fe25f 100644
> --- a/include/net/netfilter/nf_conntrack_helper.h
> +++ b/include/net/netfilter/nf_conntrack_helper.h
> @@ -160,6 +160,9 @@ nf_ct_helper_expectfn_find_by_name(const char *name);
> struct nf_ct_helper_expectfn *
> nf_ct_helper_expectfn_find_by_symbol(const void *symbol);
>
> +int nf_ct_helper_parse_port(const char *cp, unsigned int len,
> + u16 *port, char **endp);
> +
> extern struct hlist_head *nf_ct_helper_hash;
> extern unsigned int nf_ct_helper_hsize;
>
> diff --git a/net/netfilter/nf_conntrack_helper.c b/net/netfilter/nf_conntrack_helper.c
> index a715304a5..12f51670d 100644
> --- a/net/netfilter/nf_conntrack_helper.c
> +++ b/net/netfilter/nf_conntrack_helper.c
> @@ -499,6 +499,34 @@ void nf_nat_helper_unregister(struct nf_conntrack_nat_helper *nat)
> }
> EXPORT_SYMBOL_GPL(nf_nat_helper_unregister);
>
> +int nf_ct_helper_parse_port(const char *cp, unsigned int len,
> + u16 *port, char **endp)
> +{
> + unsigned long result = 0;
> + const char *start = cp;
> +
> + while (len > 0 && *cp >= '0' && *cp <= '9') {
> + result = result * 10 + (*cp - '0');
> + if (result > 65535)
> + return -1;
> + cp++;
> + len--;
> + }
> +
> + if (cp == start)
> + return -1;
This check is redundant wrt. the following one: If the loop didn't
increment 'cp', result must be zero. So you may just drop it entirely.
Cheers, Phil
> +
> + if (result == 0)
> + return -1;
> +
> + *port = result;
> + if (endp)
> + *endp = (char *)cp;
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(nf_ct_helper_parse_port);
> +
> int nf_conntrack_helper_init(void)
> {
> nf_ct_helper_hsize = 1; /* gets rounded up to use one page */
> --
> 2.54.0
>
>
^ permalink raw reply
* Re: [PATCH net-next v2 0/3] netfilter: conntrack: add shared port parser and use it in IRC and Amanda helpers
From: Phil Sutter @ 2026-05-01 10:34 UTC (permalink / raw)
To: HACKE-RC
Cc: Pablo Neira Ayuso, Florian Westphal, David S . Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
netfilter-devel, coreteam, netdev, linux-kernel
In-Reply-To: <20260501063156.2520780-1-rc@rexion.ai>
On Fri, May 01, 2026 at 12:01:53PM +0530, HACKE-RC wrote:
> Both nf_conntrack_irc and nf_conntrack_amanda parse port numbers
> from application-layer protocol data using simple_strtoul(), which
> relies on nul-terminated strings and returns unsigned long without
> range checking. Port values above 65535 silently truncate when
> stored in u16.
>
> This v2 adds a shared nf_ct_helper_parse_port() function to the
> conntrack helper core, modeled after the approach in 8cf6809cddcb
> ("netfilter: nf_conntrack_sip: don't use simple_strtoul"), then
> converts both helpers to use it.
Looking at Florian's patch, how about going the extra mile of
implementing a shared nf_ct_helper_parse_uint() which is called by the
new nf_ct_helper_parse_port(), then drop sip_strtouint() for the former
and have sip_parse_port() call the latter (wrapped by the colon and min
port value checks) in a fourth patch?
Cheers, Phil
>
> Changes since v1:
> - Added shared nf_ct_helper_parse_port() in the helper core
> instead of open-coding range checks in each helper (Pablo)
> - Parser does not rely on nul-terminated strings
> - Dropped simple_strtoul usage entirely for port parsing
>
> HACKE-RC (3):
> netfilter: conntrack: add shared port parser for helpers
> netfilter: nf_conntrack_irc: use nf_ct_helper_parse_port()
> netfilter: nf_conntrack_amanda: use nf_ct_helper_parse_port()
>
> include/net/netfilter/nf_conntrack_helper.h | 3 +++
> net/netfilter/nf_conntrack_amanda.c | 11 ++++----
> net/netfilter/nf_conntrack_helper.c | 28 +++++++++++++++++++++
> net/netfilter/nf_conntrack_irc.c | 4 ++-
> 4 files changed, 40 insertions(+), 6 deletions(-)
>
> --
> 2.54.0
>
>
^ permalink raw reply
* Re: [PATCH net 06/12] netfilter: nf_conntrack_expect: honor expectation helper field
From: Pablo Neira Ayuso @ 2026-05-01 10:37 UTC (permalink / raw)
To: Ilya Maximets
Cc: netfilter-devel, fw, davem, netdev, kuba, pabeni, edumazet, horms,
Eelco Chaudron, Aaron Conole
In-Reply-To: <8fd5d3a3-d1d7-4542-a0db-1678989940d4@ovn.org>
Hi Ilya,
On Thu, Apr 30, 2026 at 10:58:38PM +0200, Ilya Maximets wrote:
> On 3/26/26 1:51 PM, Pablo Neira Ayuso wrote:
> > The expectation helper field is mostly unused. As a result, the
> > netfilter codebase relies on accessing the helper through exp->master.
> >
> > Always set on the expectation helper field so it can be used to reach
> > the helper.
> >
> > nf_ct_expect_init() is called from packet path where the skb owns
> > the ct object, therefore accessing exp->master for the newly created
> > expectation is safe. This saves a lot of updates in all callsites
> > to pass the ct object as parameter to nf_ct_expect_init().
> >
> > This is a preparation patches for follow up fixes.
> >
> > Signed-off-by: Florian Westphal <fw@strlen.de>
> > Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
> > ---
>
> Hi, Pablo and Florian.
>
> I was investigating FTP test failures in OVS with 7.0 kernel and bisected
> the issue down to this commit. AFAIU, with this change all the related
> connections over time gain their parents' helpers,. This is causing a change
> visible to the userspace, because FTP data connections are now reported to
> have helpers in the conntrack dump:
>
> # conntrack -L
> tcp 6 119 TIME_WAIT src=10.1.1.1 dst=10.1.1.2 sport=59534 dport=21 \
> src=10.1.1.2 dst=10.1.1.1 sport=21 dport=59534 \
> [ASSURED] mark=0 helper=ftp use=2
> tcp 6 119 TIME_WAIT src=10.1.1.2 dst=10.1.1.1 sport=52709 dport=52381 \
> src=10.1.1.1 dst=10.1.1.2 sport=52381 dport=52709 \
> [ASSURED] mark=0 helper=ftp use=1
>
> Before this commit only the control connection had helper=ftp reported in
> the dump. The traffic seems to work fine, but our tests fail because we
> do not expect the helper attached.
>
> AFAIU, it's generally not something that should be happening, as helpers
> on data connections do not really make much sense. But I'm just trying to
> figure out if you would consider this as a regression and fix in the kernel
> or if we should adjust our userspace components for this new dump content,
> which would not be very straightforward to do if we want to be able to run
> tests on both old and the new versions.
>
> What do you think?
It seems previous behaviour to 9c42bc9db90a was inconsistent, ie. only
the h323 helper sets on exp->helper, then it shows helper= in expected
connections via ctnetlink. I guess this is for debugging given that
h323 is actually a family of helpers.
To consistently skip dumping this for expected connections, probably
this is the way to do:
diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conn
index eda5fe4a75c8..9491ae9e080e 100644
--- a/net/netfilter/nf_conntrack_netlink.c
+++ b/net/netfilter/nf_conntrack_netlink.c
@@ -226,7 +226,7 @@ static int ctnetlink_dump_helpinfo(struct sk_buff *sk
const struct nf_conn_help *help = nfct_help(ct);
struct nf_conntrack_helper *helper;
- if (!help)
+ if (!help || ct->status & IPS_EXPECTED)
return 0;
rcu_read_lock();
^ permalink raw reply related
* [PATCH v6] net: wwan: t7xx: validate port_count against message length in t7xx_port_enum_msg_handler
From: Pavitra Jha @ 2026-05-01 11:07 UTC (permalink / raw)
To: w
Cc: pabeni, horms, chandrashekar.devegowda, linux-wwan, netdev,
stable, Pavitra Jha
In-Reply-To: <20260427190407.1248872-1-horms@kernel.org>
t7xx_port_enum_msg_handler() uses the modem-supplied port_count field as
a loop bound over port_msg->data[] without checking that the message buffer
contains sufficient data. A modem sending port_count=65535 in a 12-byte
buffer triggers a slab-out-of-bounds read of up to 262140 bytes.
Add a sizeof(*port_msg) check before accessing the port message header
fields to guard against undersized messages.
Add a struct_size() check after extracting port_count and before the loop.
In t7xx_parse_host_rt_data(), guard the rt_feature header read with a
remaining-buffer check before accessing data_len, validate feat_data_len
against the actual remaining buffer to prevent OOB reads and signed
integer overflow on offset.
Pass msg_len from both call sites: skb->len at the DPMAIF path after
skb_pull(), and the validated feat_data_len at the handshake path.
Fixes: da45d2566a1d ("net: wwan: t7xx: Add control port")
Cc: stable@vger.kernel.org
Signed-off-by: Pavitra Jha <jhapavitra98@gmail.com>
---
drivers/net/wwan/t7xx/t7xx_modem_ops.c | 20 +++++++++++++++++---
drivers/net/wwan/t7xx/t7xx_port_ctrl_msg.c | 18 ++++++++++++++++--
drivers/net/wwan/t7xx/t7xx_port_proxy.h | 2 +-
3 files changed, 34 insertions(+), 6 deletions(-)
diff --git a/drivers/net/wwan/t7xx/t7xx_modem_ops.c b/drivers/net/wwan/t7xx/t7xx_modem_ops.c
index 7968e208d..adb29d30c 100644
--- a/drivers/net/wwan/t7xx/t7xx_modem_ops.c
+++ b/drivers/net/wwan/t7xx/t7xx_modem_ops.c
@@ -457,8 +457,20 @@ static int t7xx_parse_host_rt_data(struct t7xx_fsm_ctl *ctl, struct t7xx_sys_inf
offset = sizeof(struct feature_query);
for (i = 0; i < FEATURE_COUNT && offset < data_length; i++) {
+ size_t remaining = data_length - offset;
+ size_t feat_data_len, feat_total;
+
+ if (remaining < sizeof(*rt_feature))
+ break;
+
rt_feature = data + offset;
- offset += sizeof(*rt_feature) + le32_to_cpu(rt_feature->data_len);
+ feat_data_len = le32_to_cpu(rt_feature->data_len);
+
+ if (feat_data_len > remaining - sizeof(*rt_feature))
+ break;
+
+ feat_total = sizeof(*rt_feature) + feat_data_len;
+ offset += feat_total;
ft_spt_cfg = FIELD_GET(FEATURE_MSK, core->feature_set[i]);
if (ft_spt_cfg != MTK_FEATURE_MUST_BE_SUPPORTED)
@@ -468,8 +480,10 @@ static int t7xx_parse_host_rt_data(struct t7xx_fsm_ctl *ctl, struct t7xx_sys_inf
if (ft_spt_st != MTK_FEATURE_MUST_BE_SUPPORTED)
return -EINVAL;
- if (i == RT_ID_MD_PORT_ENUM || i == RT_ID_AP_PORT_ENUM)
- t7xx_port_enum_msg_handler(ctl->md, rt_feature->data);
+ if (i == RT_ID_MD_PORT_ENUM || i == RT_ID_AP_PORT_ENUM) {
+ t7xx_port_enum_msg_handler(ctl->md, rt_feature->data,
+ feat_data_len);
+ }
}
return 0;
diff --git a/drivers/net/wwan/t7xx/t7xx_port_ctrl_msg.c b/drivers/net/wwan/t7xx/t7xx_port_ctrl_msg.c
index ae632ef96..f869e4ed9 100644
--- a/drivers/net/wwan/t7xx/t7xx_port_ctrl_msg.c
+++ b/drivers/net/wwan/t7xx/t7xx_port_ctrl_msg.c
@@ -117,6 +117,7 @@ static int fsm_ee_message_handler(struct t7xx_port *port, struct t7xx_fsm_ctl *c
* t7xx_port_enum_msg_handler() - Parse the port enumeration message to create/remove nodes.
* @md: Modem context.
* @msg: Message.
+ * @msg_len: Length of @msg in bytes.
*
* Used to control create/remove device node.
*
@@ -124,12 +125,18 @@ static int fsm_ee_message_handler(struct t7xx_port *port, struct t7xx_fsm_ctl *c
* * 0 - Success.
* * -EFAULT - Message check failure.
*/
-int t7xx_port_enum_msg_handler(struct t7xx_modem *md, void *msg)
+int t7xx_port_enum_msg_handler(struct t7xx_modem *md, void *msg, size_t msg_len)
{
struct device *dev = &md->t7xx_dev->pdev->dev;
unsigned int version, port_count, i;
struct port_msg *port_msg = msg;
+ if (msg_len < sizeof(*port_msg)) {
+ dev_err(dev, "Port enum msg too short for header: need %zu, have %zu\n",
+ sizeof(*port_msg), msg_len);
+ return -EINVAL;
+ }
+
version = FIELD_GET(PORT_MSG_VERSION, le32_to_cpu(port_msg->info));
if (version != PORT_ENUM_VER ||
le32_to_cpu(port_msg->head_pattern) != PORT_ENUM_HEAD_PATTERN ||
@@ -141,6 +148,13 @@ int t7xx_port_enum_msg_handler(struct t7xx_modem *md, void *msg)
}
port_count = FIELD_GET(PORT_MSG_PRT_CNT, le32_to_cpu(port_msg->info));
+
+ if (msg_len < struct_size(port_msg, data, port_count)) {
+ dev_err(dev, "Port enum msg too short: need %zu, have %zu\n",
+ struct_size(port_msg, data, port_count), msg_len);
+ return -EINVAL;
+ }
+
for (i = 0; i < port_count; i++) {
u32 port_info = le32_to_cpu(port_msg->data[i]);
unsigned int ch_id;
@@ -191,7 +205,7 @@ static int control_msg_handler(struct t7xx_port *port, struct sk_buff *skb)
case CTL_ID_PORT_ENUM:
skb_pull(skb, sizeof(*ctrl_msg_h));
- ret = t7xx_port_enum_msg_handler(ctl->md, (struct port_msg *)skb->data);
+ ret = t7xx_port_enum_msg_handler(ctl->md, (struct port_msg *)skb->data, skb->len);
if (!ret)
ret = port_ctl_send_msg_to_md(port, CTL_ID_PORT_ENUM, 0);
else
diff --git a/drivers/net/wwan/t7xx/t7xx_port_proxy.h b/drivers/net/wwan/t7xx/t7xx_port_proxy.h
index f0918b36e..7c3190bf0 100644
--- a/drivers/net/wwan/t7xx/t7xx_port_proxy.h
+++ b/drivers/net/wwan/t7xx/t7xx_port_proxy.h
@@ -103,7 +103,7 @@ void t7xx_port_proxy_reset(struct port_proxy *port_prox);
void t7xx_port_proxy_uninit(struct port_proxy *port_prox);
int t7xx_port_proxy_init(struct t7xx_modem *md);
void t7xx_port_proxy_md_status_notify(struct port_proxy *port_prox, unsigned int state);
-int t7xx_port_enum_msg_handler(struct t7xx_modem *md, void *msg);
+int t7xx_port_enum_msg_handler(struct t7xx_modem *md, void *msg, size_t msg_len);
int t7xx_port_proxy_chl_enable_disable(struct port_proxy *port_prox, unsigned int ch_id,
bool en_flag);
void t7xx_port_proxy_set_cfg(struct t7xx_modem *md, enum port_cfg_id cfg_id);
--
2.53.0
^ permalink raw reply related
* Re: [PATCH net-next 3/4] r8152: Add irq mitigation for RTL8157/9
From: Andrew Lunn @ 2026-05-01 12:21 UTC (permalink / raw)
To: Birger Koblitz
Cc: Michal Pecio, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, linux-usb, netdev, linux-kernel,
Chih Kai Hsu
In-Reply-To: <62fb1f24-b0cb-4310-a155-1f5cce14d83a@birger-koblitz.de>
> However, there is a big difference in CPU-load when running iperf3,
> especially with multiple threads:
>
> WITH the CODE saying it disables IRQ mitigation:
> $ iperf3 -c 192.168.3.1
> [ 5] 0.00-10.00 sec 10.3 GBytes 8.83 Gbits/sec
> CPU: iperf3 22.5% ksoftirqd 16.6%
>
> $ iperf3 -c 192.168.3.1 -P4
> [SUM] 0.00-10.00 sec 9.36 GBytes 8.04 Gbits/sec
> perf3: 19.3% ksoftirqd 96.7%
>
>
> WITHOUT the CODE saying it disabled IRQ mitigation:
> [ 5] 0.00-10.00 sec 10.3 GBytes 8.87 Gbits/sec
> CPU: perf3 19.7% ksoftirqd 12.5%
>
> $ iperf3 -c 192.168.3.1 -P4
> [SUM] 0.00-10.00 sec 10.3 GBytes 8.81 Gbits/sec
> CPU: iperf3 22.0% ksoftirqd 46.5%
Thanks for doing this performance testing.
> My undestanding from the first email by Michal is that the behavior with the
> ESHUTOWN is actually harmless and has been seen in the driver already for
> the RTL8153.
>
> So, my suggestion is I drop this part of the patch-series and remove the
> IRQ-mitigation code that says it disables the IRQ mitigation as performance
> is higher without that code.
Agreed.
Andrew
^ permalink raw reply
* [PATCH net 00/14] Netfilter fixes for net
From: Pablo Neira Ayuso @ 2026-05-01 12:22 UTC (permalink / raw)
To: netfilter-devel; +Cc: davem, netdev, kuba, pabeni, edumazet, fw, horms
Hi,
The following batch contains Netfilter fixes for net:
1) Replace skb_try_make_writable() by skb_ensure_writable() in
nft_fwd_netdev and the flowtable to deal with uncloned packets
having their network header in paged fragments.
2) Drop packet if output device does not exist and ensure sufficient
headroom in nft_fwd_netdev before transmitting the skb.
3) Use the existing dup recursion counter in nft_fwd_netdev for the
neigh_xmit variant, from Weiming Shi.
4) Add .check_hooks interface to x_tables to detach the control plane
hook check based on the match/target configuration. Then, update
nft_compat to use .check_hooks from .validate path, this fixes a
lack of hook validation for several match/targets.
5) Fix incorrect .usersize in xt_CT, from Florian Westphal.
6) Fix a memleak with netdev tables in dormant state,
from Florian Westphal.
7) Several patches to check if the packet is a fragment, then skip
layer 4 inspection, for x_tables and nf_tables; as well as common
nf_socket infrastructure. The xt_hashlimit match drops fragments
to stay consistent with the existing approach when failing to parse
the layer 4 protocol header.
8) Ensure sufficient headroom in the flowtable before transmitting
the skb.
9) Fix the flowtable inline vlan approach for double-tagged vlan:
Reverse the iteration over .encap[] since it represents the
encapsulation as seen from the ingress path. Postpone pushing
layer 2 header so output device is available to calculate needed
headroom. Finally, add and use nf_flow_vlan_push() to fix it.
10) Fix flowtable inline pppoe with GSO packets. Moreover, use
FLOW_OFFLOAD_XMIT_DIRECT to fill up destination hardware
address since neighbour cache does not exist in pppoe.
11) Use skb_pull_rcsum() to decapsulate vlan and pppoe headers, for
double-tagged vlan in particular this should provide some benefits
in certain scenarios.
More notes regarding 9-11):
- sashiko is also signalling to use it for IPIP headers, but that needs
more adjustments such setting skb->protocol after removing the IPIP
header, will follow up in a separated patch.
- I plan to submit selftests to cover double-tagged-vlan. As for pppoe,
it should be possible but that would mandate a few userspace dependencies.
This has been semi-automatically tested by me and reporters describing
broken double-vlan-tagged and pppoe currently in the flowtable.
Please, pull these changes from:
git://git.kernel.org/pub/scm/linux/kernel/git/netfilter/nf.git nf-26-05-01
Thanks.
----------------------------------------------------------------
The following changes since commit 0c7a5ba011d336df4fcd1f667fcc16ea5549be12:
Merge branch 'mptcp-misc-fixes-for-v7-1-rc2' (2026-04-28 18:36:29 -0700)
are available in the Git repository at:
git://git.kernel.org/pub/scm/linux/kernel/git/netfilter/nf.git nf-26-05-01
for you to fetch changes up to baa3c65435fb3f450b262672bc06db887a92d397:
netfilter: flowtable: use skb_pull_rcsum() to pop vlan/pppoe header (2026-05-01 12:39:23 +0200)
----------------------------------------------------------------
netfilter pull request 26-05-01
----------------------------------------------------------------
Fernando Fernandez Mancera (3):
netfilter: nf_socket: skip socket lookup for non-first fragments
netfilter: nf_tables: skip L4 header parsing for non-first fragments
netfilter: xtables: fix L4 header parsing for non-first fragments
Florian Westphal (2):
netfilter: xt_CT: fix usersize for v1 and v2 revision
netfilter: nf_tables: fix netdev hook allocation memleak with dormant tables
Pablo Neira Ayuso (8):
netfilter: replace skb_try_make_writable() by skb_ensure_writable()
netfilter: nft_fwd_netdev: add device and headroom validate with neigh forwarding
netfilter: x_tables: add .check_hooks to matches and targets
netfilter: nft_compat: run xt_check_hooks_{match,target}() from .validate
netfilter: flowtable: ensure sufficient headroom in xmit path
netfilter: flowtable: fix inline vlan encapsulation in xmit path
netfilter: flowtable: fix inline pppoe encapsulation in xmit path
netfilter: flowtable: use skb_pull_rcsum() to pop vlan/pppoe header
Weiming Shi (1):
netfilter: nft_fwd_netdev: use recursion counter in neigh egress path
include/linux/netfilter/x_tables.h | 8 ++
include/net/netfilter/nf_dup_netdev.h | 13 +++
include/net/netfilter/nf_flow_table.h | 4 +-
net/ipv4/netfilter/nf_socket_ipv4.c | 3 +
net/ipv6/netfilter/nf_socket_ipv6.c | 5 +-
net/netfilter/nf_dup_netdev.c | 16 ----
net/netfilter/nf_flow_table_core.c | 1 +
net/netfilter/nf_flow_table_ip.c | 151 ++++++++++++++++++++++++++--------
net/netfilter/nf_flow_table_path.c | 7 +-
net/netfilter/nf_tables_api.c | 35 ++++----
net/netfilter/nf_tables_core.c | 2 +-
net/netfilter/nft_compat.c | 45 +++++++---
net/netfilter/nft_exthdr.c | 2 +-
net/netfilter/nft_fwd_netdev.c | 29 ++++++-
net/netfilter/nft_osf.c | 2 +-
net/netfilter/nft_tproxy.c | 8 +-
net/netfilter/x_tables.c | 79 ++++++++++++++++--
net/netfilter/xt_CT.c | 8 +-
net/netfilter/xt_TCPMSS.c | 33 ++++----
net/netfilter/xt_TPROXY.c | 11 ++-
net/netfilter/xt_addrtype.c | 25 ++++--
net/netfilter/xt_devgroup.c | 18 ++--
net/netfilter/xt_ecn.c | 4 +
net/netfilter/xt_hashlimit.c | 4 +-
net/netfilter/xt_osf.c | 3 +
net/netfilter/xt_physdev.c | 20 +++--
net/netfilter/xt_policy.c | 24 ++++--
net/netfilter/xt_set.c | 39 +++++----
net/netfilter/xt_tcpmss.c | 4 +
29 files changed, 447 insertions(+), 156 deletions(-)
^ permalink raw reply
* [PATCH net 01/14] netfilter: replace skb_try_make_writable() by skb_ensure_writable()
From: Pablo Neira Ayuso @ 2026-05-01 12:22 UTC (permalink / raw)
To: netfilter-devel; +Cc: davem, netdev, kuba, pabeni, edumazet, fw, horms
In-Reply-To: <20260501122237.296262-1-pablo@netfilter.org>
skb_try_make_writable() only works on clones and uncloned packets might
have their network header in paged fragments.
nft_fwd needs to work for the ingress and egress hooks, but the egress
hook where skb->data points to the mac header, use skb_network_offset()
to include the mac header. The flowtable is fine since it already uses
the transport offset.
Fixes: d32de98ea70f ("netfilter: nft_fwd_netdev: allow to forward packets via neighbour layer")
Fixes: 7d2086871762 ("netfilter: nf_flow_table: move ipv4 offload hook code to nf_flow_table")
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
net/netfilter/nf_flow_table_ip.c | 4 ++--
net/netfilter/nft_fwd_netdev.c | 5 +++--
2 files changed, 5 insertions(+), 4 deletions(-)
diff --git a/net/netfilter/nf_flow_table_ip.c b/net/netfilter/nf_flow_table_ip.c
index fd56d663cb5b..dbd7644fdbeb 100644
--- a/net/netfilter/nf_flow_table_ip.c
+++ b/net/netfilter/nf_flow_table_ip.c
@@ -524,7 +524,7 @@ static int nf_flow_offload_forward(struct nf_flowtable_ctx *ctx,
return 0;
}
- if (skb_try_make_writable(skb, thoff + ctx->hdrsize))
+ if (skb_ensure_writable(skb, thoff + ctx->hdrsize))
return -1;
flow_offload_refresh(flow_table, flow, false);
@@ -1037,7 +1037,7 @@ static int nf_flow_offload_ipv6_forward(struct nf_flowtable_ctx *ctx,
return 0;
}
- if (skb_try_make_writable(skb, thoff + ctx->hdrsize))
+ if (skb_ensure_writable(skb, thoff + ctx->hdrsize))
return -1;
flow_offload_refresh(flow_table, flow, false);
diff --git a/net/netfilter/nft_fwd_netdev.c b/net/netfilter/nft_fwd_netdev.c
index 4bce36c3a6a0..2cc809303ce8 100644
--- a/net/netfilter/nft_fwd_netdev.c
+++ b/net/netfilter/nft_fwd_netdev.c
@@ -100,6 +100,7 @@ static void nft_fwd_neigh_eval(const struct nft_expr *expr,
int oif = regs->data[priv->sreg_dev];
unsigned int verdict = NF_STOLEN;
struct sk_buff *skb = pkt->skb;
+ int nhoff = skb_network_offset(skb);
struct net_device *dev;
int neigh_table;
@@ -111,7 +112,7 @@ static void nft_fwd_neigh_eval(const struct nft_expr *expr,
verdict = NFT_BREAK;
goto out;
}
- if (skb_try_make_writable(skb, sizeof(*iph))) {
+ if (skb_ensure_writable(skb, nhoff + sizeof(*iph))) {
verdict = NF_DROP;
goto out;
}
@@ -132,7 +133,7 @@ static void nft_fwd_neigh_eval(const struct nft_expr *expr,
verdict = NFT_BREAK;
goto out;
}
- if (skb_try_make_writable(skb, sizeof(*ip6h))) {
+ if (skb_ensure_writable(skb, nhoff + sizeof(*ip6h))) {
verdict = NF_DROP;
goto out;
}
--
2.47.3
^ permalink raw reply related
* [PATCH net 02/14] netfilter: nft_fwd_netdev: add device and headroom validate with neigh forwarding
From: Pablo Neira Ayuso @ 2026-05-01 12:22 UTC (permalink / raw)
To: netfilter-devel; +Cc: davem, netdev, kuba, pabeni, edumazet, fw, horms
In-Reply-To: <20260501122237.296262-1-pablo@netfilter.org>
The ttl field has been decremented already and evaluation of this rule
would proceed, just drop this packet instead if there is no destination
device to forwards this packet. This is exactly what nf_dup already does
in this case.
Moreover, check for headroom and call skb_expand_head() like in the IP
output path to ensure there is sufficient headroom when forwarding this
via neigh_xmit().
Fixes: d32de98ea70f ("netfilter: nft_fwd_netdev: allow to forward packets via neighbour layer")
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
net/netfilter/nft_fwd_netdev.c | 16 ++++++++++++++--
1 file changed, 14 insertions(+), 2 deletions(-)
diff --git a/net/netfilter/nft_fwd_netdev.c b/net/netfilter/nft_fwd_netdev.c
index 2cc809303ce8..605b1d42abce 100644
--- a/net/netfilter/nft_fwd_netdev.c
+++ b/net/netfilter/nft_fwd_netdev.c
@@ -102,6 +102,7 @@ static void nft_fwd_neigh_eval(const struct nft_expr *expr,
struct sk_buff *skb = pkt->skb;
int nhoff = skb_network_offset(skb);
struct net_device *dev;
+ unsigned int hh_len;
int neigh_table;
switch (priv->nfproto) {
@@ -153,8 +154,19 @@ static void nft_fwd_neigh_eval(const struct nft_expr *expr,
}
dev = dev_get_by_index_rcu(nft_net(pkt), oif);
- if (dev == NULL)
- return;
+ if (dev == NULL) {
+ verdict = NF_DROP;
+ goto out;
+ }
+
+ hh_len = LL_RESERVED_SPACE(dev);
+ if (unlikely(skb_headroom(skb) < hh_len && dev->header_ops)) {
+ skb = skb_expand_head(skb, hh_len);
+ if (!skb) {
+ verdict = NF_STOLEN;
+ goto out;
+ }
+ }
skb->dev = dev;
skb_clear_tstamp(skb);
--
2.47.3
^ permalink raw reply related
* [PATCH net 03/14] netfilter: nft_fwd_netdev: use recursion counter in neigh egress path
From: Pablo Neira Ayuso @ 2026-05-01 12:22 UTC (permalink / raw)
To: netfilter-devel; +Cc: davem, netdev, kuba, pabeni, edumazet, fw, horms
In-Reply-To: <20260501122237.296262-1-pablo@netfilter.org>
From: Weiming Shi <bestswngs@gmail.com>
nft_fwd_neigh can be used in egress chains (NF_NETDEV_EGRESS). When the
forwarding rule targets the same device or two devices forward to each
other, neigh_xmit() triggers dev_queue_xmit() which re-enters
nf_hook_egress(), causing infinite recursion and stack overflow.
Move the nf_get_nf_dup_skb_recursion() accessor and NF_RECURSION_LIMIT
to the shared header nf_dup_netdev.h as a static inline, so that
nft_fwd_netdev can use the recursion counter directly without exported
function call overhead. Guard neigh_xmit() with the same recursion
limit already used in nf_do_netdev_egress().
[ Updated to cache the nf_get_nf_dup_skb_recursion pointer. --pablo ]
Fixes: f87b9464d152 ("netfilter: nft_fwd_netdev: Support egress hook")
Reported-by: Xiang Mei <xmei5@asu.edu>
Signed-off-by: Weiming Shi <bestswngs@gmail.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
include/net/netfilter/nf_dup_netdev.h | 13 +++++++++++++
net/netfilter/nf_dup_netdev.c | 16 ----------------
net/netfilter/nft_fwd_netdev.c | 8 ++++++++
3 files changed, 21 insertions(+), 16 deletions(-)
diff --git a/include/net/netfilter/nf_dup_netdev.h b/include/net/netfilter/nf_dup_netdev.h
index b175d271aec9..609bcf422a9b 100644
--- a/include/net/netfilter/nf_dup_netdev.h
+++ b/include/net/netfilter/nf_dup_netdev.h
@@ -3,10 +3,23 @@
#define _NF_DUP_NETDEV_H_
#include <net/netfilter/nf_tables.h>
+#include <linux/netdevice.h>
+#include <linux/sched.h>
void nf_dup_netdev_egress(const struct nft_pktinfo *pkt, int oif);
void nf_fwd_netdev_egress(const struct nft_pktinfo *pkt, int oif);
+#define NF_RECURSION_LIMIT 2
+
+static inline u8 *nf_get_nf_dup_skb_recursion(void)
+{
+#ifndef CONFIG_PREEMPT_RT
+ return this_cpu_ptr(&softnet_data.xmit.nf_dup_skb_recursion);
+#else
+ return ¤t->net_xmit.nf_dup_skb_recursion;
+#endif
+}
+
struct nft_offload_ctx;
struct nft_flow_rule;
diff --git a/net/netfilter/nf_dup_netdev.c b/net/netfilter/nf_dup_netdev.c
index e348fb90b8dc..3b0a70e154cd 100644
--- a/net/netfilter/nf_dup_netdev.c
+++ b/net/netfilter/nf_dup_netdev.c
@@ -13,22 +13,6 @@
#include <net/netfilter/nf_tables_offload.h>
#include <net/netfilter/nf_dup_netdev.h>
-#define NF_RECURSION_LIMIT 2
-
-#ifndef CONFIG_PREEMPT_RT
-static u8 *nf_get_nf_dup_skb_recursion(void)
-{
- return this_cpu_ptr(&softnet_data.xmit.nf_dup_skb_recursion);
-}
-#else
-
-static u8 *nf_get_nf_dup_skb_recursion(void)
-{
- return ¤t->net_xmit.nf_dup_skb_recursion;
-}
-
-#endif
-
static void nf_do_netdev_egress(struct sk_buff *skb, struct net_device *dev,
enum nf_dev_hooks hook)
{
diff --git a/net/netfilter/nft_fwd_netdev.c b/net/netfilter/nft_fwd_netdev.c
index 605b1d42abce..b9e88d7cf308 100644
--- a/net/netfilter/nft_fwd_netdev.c
+++ b/net/netfilter/nft_fwd_netdev.c
@@ -95,6 +95,7 @@ static void nft_fwd_neigh_eval(const struct nft_expr *expr,
struct nft_regs *regs,
const struct nft_pktinfo *pkt)
{
+ u8 *nf_dup_skb_recursion = nf_get_nf_dup_skb_recursion();
struct nft_fwd_neigh *priv = nft_expr_priv(expr);
void *addr = ®s->data[priv->sreg_addr];
int oif = regs->data[priv->sreg_dev];
@@ -153,6 +154,11 @@ static void nft_fwd_neigh_eval(const struct nft_expr *expr,
goto out;
}
+ if (*nf_dup_skb_recursion > NF_RECURSION_LIMIT) {
+ verdict = NF_DROP;
+ goto out;
+ }
+
dev = dev_get_by_index_rcu(nft_net(pkt), oif);
if (dev == NULL) {
verdict = NF_DROP;
@@ -170,7 +176,9 @@ static void nft_fwd_neigh_eval(const struct nft_expr *expr,
skb->dev = dev;
skb_clear_tstamp(skb);
+ (*nf_dup_skb_recursion)++;
neigh_xmit(neigh_table, dev, addr, skb);
+ (*nf_dup_skb_recursion)--;
out:
regs->verdict.code = verdict;
}
--
2.47.3
^ permalink raw reply related
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