All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH conntrack-tools] conntrack: apply the receive buffer size to the event socket
@ 2026-10-18  4:03 Yuen Su
  2026-10-20 23:17 ` Pablo Neira Ayuso
  0 siblings, 1 reply; 4+ messages in thread
From: Yuen Su @ 2026-10-18  4:03 UTC (permalink / raw)
  To: netfilter-devel; +Cc: Pablo Neira Ayuso

CT_EVENT receives notifications on event_sock after socket
consolidation, but --buffer-size still configures and queries the
separate control socket. A larger value therefore leaves the active
event receive buffer unchanged while reporting success. Use event_sock
for the forced setting, fallback setting and effective-size readback.

Add an isolated real-kernel regression that traces all event masks and
two requested sizes. The original fails because the option targets fd 3
while the multicast listener is fd 4; the fix passes all eight cases.
The existing conntrack CLI suite also passes 286 cases. Prepared with AI
assistance and verified against actual netlink syscalls.

Fixes: 5ec684be0854 ("conntrack: consolidate socket open call")
Signed-off-by: Yuen Su <zypulsecore@gmail.com>
---

Validation (outside the commit message):
- Public CI: https://github.com/ZyPulse-zy/packages/actions/runs/39094897174
- Real netlink syscalls: eight event cases / 72 assertions per binary;
  original mainline and 1.4.8 fail the exact wrong-socket assertion.
- Actual Autotools builds and existing upstream CLI suites pass.
  Local current-mainline CLI suite: 286 cases.
- ASan/UBSan tracing passes; LSan is disabled only because ptrace is
  unsupported by LSan. The default-LSan failure log is retained.
- The OpenWrt 1.4.8 backport was refreshed by the actual SDK quilt target;
  official 25.12.0 GCC14.3 AArch64/musl APKs and dependencies build.
  SDK kernel configuration and Module.symvers hashes remain unchanged.
- QEMU could not open NETLINK_NETFILTER (Protocol not supported), so
  target-userland runtime and hardware behavior are not claimed.

 src/conntrack.c                      |   6 +-
 tests/conntrack/test-event-buffer.py | 130 +++++++++++++++++++++++++++
 2 files changed, 133 insertions(+), 3 deletions(-)
 create mode 100644 tests/conntrack/test-event-buffer.py

diff --git a/src/conntrack.c b/src/conntrack.c
index 940469d..977f130 100644
--- a/src/conntrack.c
+++ b/src/conntrack.c
@@ -3555,17 +3555,17 @@ static int do_command_ct(const char *progname,
struct ct_cmd *cmd,

 			socklen_t socklen = sizeof(socketbuffersize);

-			res = setsockopt(mnl_socket_get_fd(sock->mnl),
+			res = setsockopt(mnl_socket_get_fd(event_sock->mnl),
 					 SOL_SOCKET, SO_RCVBUFFORCE,
 					 &socketbuffersize,
 					 sizeof(socketbuffersize));
 			if (res < 0) {
-				setsockopt(mnl_socket_get_fd(sock->mnl),
+				setsockopt(mnl_socket_get_fd(event_sock->mnl),
 					   SOL_SOCKET, SO_RCVBUF,
 					   &socketbuffersize,
 					   sizeof(socketbuffersize));
 			}
-			getsockopt(mnl_socket_get_fd(sock->mnl), SOL_SOCKET,
+			getsockopt(mnl_socket_get_fd(event_sock->mnl), SOL_SOCKET,
 				   SO_RCVBUF, &socketbuffersize, &socklen);
 			fprintf(stderr, "NOTICE: Netlink socket buffer size "
 					"has been set to %zu bytes.\n",
diff --git a/tests/conntrack/test-event-buffer.py
b/tests/conntrack/test-event-buffer.py
new file mode 100644
index 0000000..4a5adff
--- /dev/null
+++ b/tests/conntrack/test-event-buffer.py
@@ -0,0 +1,130 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0-or-later
+"""Check -E --buffer-size on real netlink sockets in an isolated netns.
+
+Requires Python 3, strace and GNU timeout. Run as follows:
+  CONNTRACK_TEST_PARENT_NETNS=$(readlink /proc/self/ns/net) \
+    unshare --user --map-root-user --net python3 test-event-buffer.py
/path/to/conntrack
+No connections are created, removed or modified.
+"""
+
+import argparse
+import json
+import os
+from pathlib import Path
+import re
+import subprocess
+import tempfile
+
+
+def main():
+    parser = argparse.ArgumentParser(description=__doc__)
+    parser.add_argument("conntrack", type=Path)
+    parser.add_argument("--report", type=Path)
+    args = parser.parse_args()
+    binary = args.conntrack.resolve()
+    parent = os.environ.get("CONNTRACK_TEST_PARENT_NETNS")
+    if not parent or parent == os.readlink("/proc/self/ns/net") or
os.geteuid() != 0:
+        raise SystemExit("Run in a new user/network namespace as documented")
+    report = {"passed": False, "checks": 0, "cases": []}
+
+    def check(condition, description):
+        if not condition:
+            raise AssertionError(description)
+        report["checks"] += 1
+
+    try:
+        with tempfile.TemporaryDirectory(prefix="conntrack-event-buffer-")
as temporary:
+            for mask, groups in [(None, 7), ("NEW", 1), ("UPDATE",
2), ("DESTROY", 4)]:
+                for requested in (32768, 1048576):
+                    trace = Path(temporary) / "syscalls.log"
+                    command = [
+                        "strace",
+                        "-f",
+                        "-o",
+                        str(trace),
+                        "-e",
+                        "trace=socket,bind,setsockopt,getsockopt",
+                        "timeout",
+                        "--preserve-status",
+                        "--signal=TERM",
+                        "1",
+                        str(binary),
+                        "-E",
+                        "--buffer-size",
+                        str(requested),
+                    ]
+                    if mask:
+                        command += ["--event-mask", mask]
+                    result = subprocess.run(command,
capture_output=True, text=True, timeout=5)
+                    text = trace.read_text()
+                    case = {
+                        "mask": mask or "ALL",
+                        "requested": requested,
+                        "exit": result.returncode,
+                        "stderr": result.stderr,
+                        "trace": text,
+                    }
+                    report["cases"].append(case)
+                    check(result.returncode == 0, "Event command did
not shut down cleanly")
+                    binds = re.findall(
+                        r"bind\((\d+),
.*nl_groups=(0x[0-9a-f]+|0+)\}, \d+\) = 0", text
+                    )
+                    control = [int(fd) for fd, group in binds if
int(group, 0) == 0]
+                    events = [int(fd) for fd, group in binds if
int(group, 0) == groups]
+                    check(
+                        len(control) == len(events) == 1,
+                        "Expected control and event netlink sockets",
+                    )
+                    event_fd = events[0]
+                    check(event_fd != control[0], "Event listener
must be a separate socket")
+                    writes = re.findall(
+                        r"setsockopt\((\d+), SOL_SOCKET,
SO_RCVBUF(?:FORCE)?, \[(\d+)\].*", text
+                    )
+                    check(
+                        bool(writes) and all(int(fd) == event_fd for
fd, _ in writes),
+                        "--buffer-size changed the query socket
instead of the event listener",
+                    )
+                    check(
+                        all(int(size) == requested for _, size in
writes), "Requested size changed"
+                    )
+                    check(
+                        bool(
+                            re.search(
+                                r"setsockopt\("
+                                + str(event_fd)
+                                + r", SOL_SOCKET, SO_RCVBUF(?:FORCE)?, .* = 0",
+                                text,
+                            )
+                        ),
+                        "No receive-buffer option succeeded on the
event listener",
+                    )
+                    reads = re.findall(r"getsockopt\((\d+),
SOL_SOCKET, SO_RCVBUF, \[(\d+)\]", text)
+                    check(
+                        len(reads) == 1 and int(reads[0][0]) == event_fd,
+                        "Reported buffer size was read from the query socket",
+                    )
+                    actual = int(reads[0][1])
+                    check(actual > 0, "Kernel returned an invalid buffer size")
+                    check(
+                        f"set to {actual} bytes" in result.stderr,
+                        "Notice does not describe the kernel event
socket readback",
+                    )
+                    case.update(
+                        event_fd=event_fd,
+                        control_fd=control[0],
+                        actual=actual,
+                        force_fallback="EPERM" in text,
+                    )
+        report["passed"] = True
+    except BaseException as exc:
+        report["failure"] = str(exc)
+        raise
+    finally:
+        if args.report:
+            args.report.write_text(json.dumps(report, indent=2) + "\n")
+    print(f"PASS: {report['checks']} real-kernel assertions,
{len(report['cases'])} event cases")
+
+
+if __name__ == "__main__":
+    main()
-- 
2.53.0

^ permalink raw reply related	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2026-10-20 23:55 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-18  4:03 [PATCH conntrack-tools] conntrack: apply the receive buffer size to the event socket Yuen Su
2026-10-20 23:17 ` Pablo Neira Ayuso
2026-10-20 23:19   ` Pablo Neira Ayuso
2026-10-20 23:55     ` Pulse Zy

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.