Netdev List
 help / color / mirror / Atom feed
* [PATCH iproute2 11/11] ss: report a failed SCTP or MPTCP dump
  2026-11-09 21:25 [PATCH iproute2 00/11] ss fixes from an OSS scan Stephen Hemminger
@ 2026-11-09 21:25 ` Stephen Hemminger
  0 siblings, 0 replies; 13+ messages in thread
From: Stephen Hemminger @ 2026-11-09 21:25 UTC (permalink / raw)
  To: netdev; +Cc: dsahern, Stephen Hemminger

sctp_show() and mptcp_show() discarded the return of
inet_show_netlink(), unlike every other family, so ss -S and ss -M
printed only a header and exited 0 when the kernel could not dump.

Neither protocol is dumped unless asked for, and sock_diag_error()
already reports a missing module as lack of support, so this does
not add noise to a default run.

Assisted-by: Claude:claude-opus-5
Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
---
 misc/ss.c | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/misc/ss.c b/misc/ss.c
index 55d9ae00..26cb0942 100644
--- a/misc/ss.c
+++ b/misc/ss.c
@@ -4095,7 +4095,8 @@ static int mptcp_show(struct filter *f)
 	if (!filter_af_get(f, AF_INET) && !filter_af_get(f, AF_INET6))
 		return 0;
 
-	inet_show_netlink(f, NULL, IPPROTO_MPTCP);
+	if (inet_show_netlink(f, NULL, IPPROTO_MPTCP))
+		return sock_diag_error("MPTCP");
 	return 0;
 }
 
@@ -4104,7 +4105,8 @@ static int sctp_show(struct filter *f)
 	if (!filter_af_get(f, AF_INET) && !filter_af_get(f, AF_INET6))
 		return 0;
 
-	inet_show_netlink(f, NULL, IPPROTO_SCTP);
+	if (inet_show_netlink(f, NULL, IPPROTO_SCTP))
+		return sock_diag_error("SCTP");
 	return 0;
 }
 
-- 
2.53.0


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

* [PATCH iproute2 00/11] ss fixes from an OSS scan
@ 2026-11-12 19:59 Stephen Hemminger
  2026-11-12 19:59 ` [PATCH iproute2 01/11] ss: fix double close on sock_diag send failure Stephen Hemminger
                   ` (10 more replies)
  0 siblings, 11 replies; 13+ messages in thread
From: Stephen Hemminger @ 2026-11-12 19:59 UTC (permalink / raw)
  To: netdev; +Cc: dsahern, Stephen Hemminger

Fixes for the ss findings of scan iproute2-20261015-004, plus bugs
found while confirming them.

The biggest change is removing the fallback /proc parsing.
It was buggy and fixing it is not worth bothering.
Every family ss lists that way has had sock_diag support since well before
the oldest supported LTS kernel, and the parsers were untested and
trusted file contents that an unprivileged user can partly control.
A kernel built without netlink socket diagnostics now gets an error
instead of a silent fallback.

The rest escape names chosen by whoever owns the socket, bound state
and type values from the kernel before indexing name tables, stop
treating a netlink port id as the owner's pid, and size the
TCPDIAG_FILE read buffer to the record while rejecting lengths the
parser cannot use.

Stephen Hemminger (11):
  ss: fix double close on sock_diag send failure
  ss: drop /proc fallback for socket listing
  ss: escape names chosen by the socket owner
  ss: size the TCPDIAG_FILE buffer to the record
  ss: replay all dumps in TCPDIAG_FILE
  ss: bound state and type name lookups
  ss: fix rpcbind service cache
  ss: read socket SELinux context only for -z
  ss: check owner before naming a netlink socket
  ss: keep following events after ENOBUFS
  ss: report a failed SCTP or MPTCP dump

 lib/escape.c                  |   7 +-
 man/man8/ss.8                 |   3 +-
 misc/ss.c                     | 884 +++++++++-------------------------
 testsuite/tests/ss/ssfilter.t |  26 +-
 4 files changed, 249 insertions(+), 671 deletions(-)

-- 
2.53.0


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

* [PATCH iproute2 01/11] ss: fix double close on sock_diag send failure
  2026-11-12 19:59 [PATCH iproute2 00/11] ss fixes from an OSS scan Stephen Hemminger
@ 2026-11-12 19:59 ` Stephen Hemminger
  2026-11-12 19:59 ` [PATCH iproute2 02/11] ss: drop /proc fallback for socket listing Stephen Hemminger
                   ` (9 subsequent siblings)
  10 siblings, 0 replies; 13+ messages in thread
From: Stephen Hemminger @ 2026-11-12 19:59 UTC (permalink / raw)
  To: netdev; +Cc: dsahern, Stephen Hemminger

If sendmsg() fails, tcpdiag_send() and sockdiag_send() close the
netlink socket, then inet_show_netlink() closes it again via
rtnl_close(). The second close clobbers errno with EBADF.

Leave closing the socket to the caller.

Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
---
 misc/ss.c | 5 +----
 1 file changed, 1 insertion(+), 4 deletions(-)

diff --git a/misc/ss.c b/misc/ss.c
index cffee883..674ed8e8 100644
--- a/misc/ss.c
+++ b/misc/ss.c
@@ -3931,10 +3931,8 @@ static int tcpdiag_send(int fd, int protocol, struct filter *f)
 		.msg_iovlen = iovlen,
 	};
 
-	if (sendmsg(fd, &msg, 0) < 0) {
-		close(fd);
+	if (sendmsg(fd, &msg, 0) < 0)
 		return -1;
-	}
 
 	return 0;
 }
@@ -4026,7 +4024,6 @@ static int sockdiag_send(int family, int fd, int protocol, struct filter *f)
 
 	if (sendmsg(fd, &msg, 0) < 0) {
 		free(bpf_rta);
-		close(fd);
 		return -1;
 	}
 
-- 
2.53.0


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

* [PATCH iproute2 02/11] ss: drop /proc fallback for socket listing
  2026-11-12 19:59 [PATCH iproute2 00/11] ss fixes from an OSS scan Stephen Hemminger
  2026-11-12 19:59 ` [PATCH iproute2 01/11] ss: fix double close on sock_diag send failure Stephen Hemminger
@ 2026-11-12 19:59 ` Stephen Hemminger
  2026-11-12 19:59 ` [PATCH iproute2 03/11] ss: escape names chosen by the socket owner Stephen Hemminger
                   ` (8 subsequent siblings)
  10 siblings, 0 replies; 13+ messages in thread
From: Stephen Hemminger @ 2026-11-12 19:59 UTC (permalink / raw)
  To: netdev; +Cc: dsahern, Stephen Hemminger

All socket families ss lists from /proc (tcp, udp, raw, unix, packet,
netlink) have had sock_diag support since well before the oldest
supported LTS kernel. The /proc parsers are rarely used, untested,
and trust file contents that unprivileged users can partly control;
a unix socket name containing a newline can forge a /proc/net/unix
line with an out of range state and crash ss.

Remove the fallback and print an error if the kernel can not dump a
socket family. The TCPDIAG_GETSOCK retry goes with it; that request
is only needed for kernels older than 3.3. /proc is still used for
-s and process info.

Reported-by: Anthropic OSS scanner (ANT-2026-TNFMK5ZB)
Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
---
 misc/ss.c | 626 +++---------------------------------------------------
 1 file changed, 28 insertions(+), 598 deletions(-)

diff --git a/misc/ss.c b/misc/ss.c
index 674ed8e8..b93df60f 100644
--- a/misc/ss.c
+++ b/misc/ss.c
@@ -473,17 +473,6 @@ static void filter_merge_defaults(struct filter *f)
 	}
 }
 
-#define net_tcp_open()		generic_proc_open("PROC_NET_TCP", "net/tcp")
-#define net_tcp6_open()		generic_proc_open("PROC_NET_TCP6", "net/tcp6")
-#define net_udp_open()		generic_proc_open("PROC_NET_UDP", "net/udp")
-#define net_udp6_open()		generic_proc_open("PROC_NET_UDP6", "net/udp6")
-#define net_raw_open()		generic_proc_open("PROC_NET_RAW", "net/raw")
-#define net_raw6_open()		generic_proc_open("PROC_NET_RAW6", "net/raw6")
-#define net_unix_open()		generic_proc_open("PROC_NET_UNIX", "net/unix")
-#define net_packet_open()	generic_proc_open("PROC_NET_PACKET", \
-							"net/packet")
-#define net_netlink_open()	generic_proc_open("PROC_NET_NETLINK", \
-							"net/netlink")
 #define net_sockstat_open()	generic_proc_open("PROC_NET_SOCKSTAT", \
 							"net/sockstat")
 #define net_sockstat6_open()	generic_proc_open("PROC_NET_SOCKSTAT6", \
@@ -2477,55 +2466,6 @@ static void inet_stats_print(struct sockstat *s, bool v6only)
 	proc_ctx_print(s);
 }
 
-static int proc_parse_inet_addr(char *loc, char *rem, int family, struct
-		sockstat * s)
-{
-	s->local.family = s->remote.family = family;
-	if (family == AF_INET) {
-		sscanf(loc, "%x:%lx", s->local.data, (unsigned long *)&s->lport);
-		sscanf(rem, "%x:%lx", s->remote.data, (unsigned long *)&s->rport);
-		s->local.bytelen = s->remote.bytelen = 4;
-		return 0;
-	} else {
-		sscanf(loc, "%08x%08x%08x%08x:%lx",
-		       s->local.data,
-		       s->local.data + 1,
-		       s->local.data + 2,
-		       s->local.data + 3,
-		       (unsigned long *)&s->lport);
-		sscanf(rem, "%08x%08x%08x%08x:%lx",
-		       s->remote.data,
-		       s->remote.data + 1,
-		       s->remote.data + 2,
-		       s->remote.data + 3,
-		       (unsigned long *)&s->rport);
-		s->local.bytelen = s->remote.bytelen = 16;
-		return 0;
-	}
-	return -1;
-}
-
-static int proc_inet_split_line(char *line, char **loc, char **rem, char **data)
-{
-	char *p;
-
-	if ((p = strchr(line, ':')) == NULL)
-		return -1;
-
-	*loc = p+2;
-	if ((p = strchr(*loc, ':')) == NULL)
-		return -1;
-
-	p[5] = 0;
-	*rem = p+6;
-	if ((p = strchr(*rem, ':')) == NULL)
-		return -1;
-
-	p[5] = 0;
-	*data = p+6;
-	return 0;
-}
-
 /*
  * Display bandwidth in standard units
  * See: https://en.wikipedia.org/wiki/Data-rate_units
@@ -2794,98 +2734,6 @@ static void sctp_timer_print(struct tcpstat *s)
 		    print_ms_timer(s->timeout), s->retrans);
 }
 
-static int tcp_show_line(char *line, const struct filter *f, int family)
-{
-	int rto = 0, ato = 0;
-	struct tcpstat s = {};
-	char *loc, *rem, *data;
-	char opt[256];
-	int n;
-	int hz = get_user_hz();
-
-	if (proc_inet_split_line(line, &loc, &rem, &data))
-		return -1;
-
-	int state = (data[1] >= 'A') ? (data[1] - 'A' + 10) : (data[1] - '0');
-
-	if (!(f->states & (1 << state)))
-		return 0;
-
-	proc_parse_inet_addr(loc, rem, family, &s.ss);
-
-	if (f->f && run_ssfilter(f->f, &s.ss) == 0)
-		return 0;
-
-	opt[0] = 0;
-	n = sscanf(data, "%x %x:%x %x:%x %x %d %d %u %d %llx %d %d %d %u %d %[^\n]\n",
-		   &s.ss.state, &s.ss.wq, &s.ss.rq,
-		   &s.timer, &s.timeout, &s.retrans, &s.ss.uid, &s.probes,
-		   &s.ss.ino, &s.ss.refcnt, &s.ss.sk, &rto, &ato, &s.qack, &s.cwnd,
-		   &s.ssthresh, opt);
-
-	if (n < 17)
-		opt[0] = 0;
-
-	if (n < 12) {
-		rto = 0;
-		s.cwnd = 2;
-		s.ssthresh = -1;
-		ato = s.qack = 0;
-	}
-
-	s.retrans   = s.timer != 1 ? s.probes : s.retrans;
-	s.timeout   = (s.timeout * 1000 + hz - 1) / hz;
-	s.ato	    = (double)ato / hz;
-	s.qack	   /= 2;
-	s.rto	    = (double)rto;
-	s.ssthresh  = s.ssthresh == -1 ? 0 : s.ssthresh;
-	s.rto	    = s.rto != 3 * hz  ? s.rto / hz : 0;
-	s.ss.type   = IPPROTO_TCP;
-
-	inet_stats_print(&s.ss, false);
-
-	if (show_options)
-		tcp_timer_print(&s);
-
-	if (show_details) {
-		sock_details_print(&s.ss);
-		if (opt[0])
-			out(" opt:\"%s\"", opt);
-	}
-
-	if (show_tcpinfo)
-		tcp_stats_print(&s);
-
-	return 0;
-}
-
-static int generic_record_read(FILE *fp,
-			       int (*worker)(char*, const struct filter *, int),
-			       const struct filter *f, int fam)
-{
-	char line[256];
-
-	/* skip header */
-	if (fgets(line, sizeof(line), fp) == NULL)
-		goto outerr;
-
-	while (fgets(line, sizeof(line), fp) != NULL) {
-		int n = strlen(line);
-
-		if (n == 0 || line[n-1] != '\n') {
-			errno = -EINVAL;
-			return -1;
-		}
-		line[n-1] = 0;
-
-		if (worker(line, f, fam) < 0)
-			return 0;
-	}
-outerr:
-
-	return ferror(fp) ? -1 : 0;
-}
-
 static void print_skmeminfo(struct rtattr *tb[], int attrtype)
 {
 	const __u32 *skmeminfo;
@@ -3867,76 +3715,6 @@ static int inet_show_sock(struct nlmsghdr *nlh,
 	return 0;
 }
 
-static int tcpdiag_send(int fd, int protocol, struct filter *f)
-{
-	struct sockaddr_nl nladdr = { .nl_family = AF_NETLINK };
-	struct {
-		struct nlmsghdr nlh;
-		struct inet_diag_req r;
-	} req = {
-		.nlh.nlmsg_len = sizeof(req),
-		.nlh.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST,
-		.nlh.nlmsg_seq = MAGIC_SEQ,
-		.r.idiag_family = AF_INET,
-		.r.idiag_states = f->states,
-	};
-	char    *bc = NULL;
-	int	bclen;
-	struct msghdr msg;
-	struct rtattr rta;
-	struct iovec iov[3];
-	int iovlen = 1;
-
-	if (protocol == IPPROTO_TCP)
-		req.nlh.nlmsg_type = TCPDIAG_GETSOCK;
-	else
-		return -1;
-
-	if (show_mem) {
-		req.r.idiag_ext |= (1<<(INET_DIAG_MEMINFO-1));
-		req.r.idiag_ext |= (1<<(INET_DIAG_SKMEMINFO-1));
-	}
-
-	if (show_tcpinfo) {
-		req.r.idiag_ext |= (1<<(INET_DIAG_INFO-1));
-		req.r.idiag_ext |= (1<<(INET_DIAG_VEGASINFO-1));
-		req.r.idiag_ext |= (1<<(INET_DIAG_CONG-1));
-	}
-
-	if (show_tos) {
-		req.r.idiag_ext |= (1<<(INET_DIAG_TOS-1));
-		req.r.idiag_ext |= (1<<(INET_DIAG_TCLASS-1));
-	}
-
-	iov[0] = (struct iovec){
-		.iov_base = &req,
-		.iov_len = sizeof(req)
-	};
-	if (f->f) {
-		bclen = ssfilter_bytecompile(f->f, &bc);
-		if (bclen) {
-			rta.rta_type = INET_DIAG_REQ_BYTECODE;
-			rta.rta_len = RTA_LENGTH(bclen);
-			iov[1] = (struct iovec){ &rta, sizeof(rta) };
-			iov[2] = (struct iovec){ bc, bclen };
-			req.nlh.nlmsg_len += RTA_LENGTH(bclen);
-			iovlen = 3;
-		}
-	}
-
-	msg = (struct msghdr) {
-		.msg_name = (void *)&nladdr,
-		.msg_namelen = sizeof(nladdr),
-		.msg_iov = iov,
-		.msg_iovlen = iovlen,
-	};
-
-	if (sendmsg(fd, &msg, 0) < 0)
-		return -1;
-
-	return 0;
-}
-
 static int sockdiag_send(int family, int fd, int protocol, struct filter *f)
 {
 	struct sockaddr_nl nladdr = { .nl_family = AF_NETLINK };
@@ -3951,9 +3729,6 @@ static int sockdiag_send(int family, int fd, int protocol, struct filter *f)
 	struct iovec iov[6];
 	int iovlen = 1;
 
-	if (family == PF_UNSPEC)
-		return tcpdiag_send(fd, protocol, f);
-
 	memset(&req.r, 0, sizeof(req.r));
 	req.r.sdiag_family = family;
 	req.r.sdiag_protocol = protocol;
@@ -4131,13 +3906,8 @@ again:
 	if ((err = sockdiag_send(family, rth.fd, protocol, f)))
 		goto Exit;
 
-	if ((err = rtnl_dump_filter(&rth, show_one_inet_sock, &arg))) {
-		if (family != PF_UNSPEC) {
-			family = PF_UNSPEC;
-			goto again;
-		}
+	if ((err = rtnl_dump_filter(&rth, show_one_inet_sock, &arg)))
 		goto Exit;
-	}
 	if (family == PF_INET && preferred_family != PF_INET) {
 		family = PF_INET6;
 		goto again;
@@ -4222,12 +3992,20 @@ static int tcp_show_netlink_file(struct filter *f)
 	return err;
 }
 
-static int tcp_show(struct filter *f)
+static int sock_diag_error(const char *what)
 {
-	FILE *fp = NULL;
-	char *buf = NULL;
-	int bufsize = 1024*1024;
+	if (errno == ENOENT || errno == EOPNOTSUPP)
+		fprintf(stderr,
+			"Cannot dump %s sockets: kernel lacks sock_diag support\n",
+			what);
+	else
+		fprintf(stderr, "Cannot dump %s sockets: %s\n",
+			what, strerror(errno));
+	return -1;
+}
 
+static int tcp_show(struct filter *f)
+{
 	if (!filter_af_get(f, AF_INET) && !filter_af_get(f, AF_INET6))
 		return 0;
 
@@ -4236,52 +4014,9 @@ static int tcp_show(struct filter *f)
 	if (getenv("TCPDIAG_FILE"))
 		return tcp_show_netlink_file(f);
 
-	if (!getenv("PROC_NET_TCP") && !getenv("PROC_ROOT")
-	    && inet_show_netlink(f, NULL, IPPROTO_TCP) == 0)
-		return 0;
-
-	/* Sigh... We have to parse /proc/net/tcp... */
-	while (bufsize >= 64*1024) {
-		if ((buf = malloc(bufsize)) != NULL)
-			break;
-		bufsize /= 2;
-	}
-	if (buf == NULL) {
-		errno = ENOMEM;
-		return -1;
-	}
-
-	if (f->families & FAMILY_MASK(AF_INET)) {
-		if ((fp = net_tcp_open()) == NULL)
-			goto outerr;
-
-		setbuffer(fp, buf, bufsize);
-		if (generic_record_read(fp, tcp_show_line, f, AF_INET))
-			goto outerr;
-		fclose(fp);
-	}
-
-	if ((f->families & FAMILY_MASK(AF_INET6)) &&
-	    (fp = net_tcp6_open()) != NULL) {
-		setbuffer(fp, buf, bufsize);
-		if (generic_record_read(fp, tcp_show_line, f, AF_INET6))
-			goto outerr;
-		fclose(fp);
-	}
-
-	free(buf);
+	if (inet_show_netlink(f, NULL, IPPROTO_TCP))
+		return sock_diag_error("TCP");
 	return 0;
-
-outerr:
-	do {
-		int saved_errno = errno;
-
-		free(buf);
-		if (fp)
-			fclose(fp);
-		errno = saved_errno;
-		return -1;
-	} while (0);
 }
 
 static int mptcp_show(struct filter *f)
@@ -4289,10 +4024,7 @@ static int mptcp_show(struct filter *f)
 	if (!filter_af_get(f, AF_INET) && !filter_af_get(f, AF_INET6))
 		return 0;
 
-	if (!getenv("PROC_NET_MPTCP") && !getenv("PROC_ROOT")
-	    && inet_show_netlink(f, NULL, IPPROTO_MPTCP) == 0)
-		return 0;
-
+	inet_show_netlink(f, NULL, IPPROTO_MPTCP);
 	return 0;
 }
 
@@ -4301,140 +4033,32 @@ static int sctp_show(struct filter *f)
 	if (!filter_af_get(f, AF_INET) && !filter_af_get(f, AF_INET6))
 		return 0;
 
-	if (!getenv("PROC_NET_SCTP") && !getenv("PROC_ROOT")
-	    && inet_show_netlink(f, NULL, IPPROTO_SCTP) == 0)
-		return 0;
-
-	return 0;
-}
-
-static int dgram_show_line(char *line, const struct filter *f, int family)
-{
-	struct sockstat s = {};
-	char *loc, *rem, *data;
-	char opt[256];
-	int n;
-
-	if (proc_inet_split_line(line, &loc, &rem, &data))
-		return -1;
-
-	int state = (data[1] >= 'A') ? (data[1] - 'A' + 10) : (data[1] - '0');
-
-	if (!(f->states & (1 << state)))
-		return 0;
-
-	proc_parse_inet_addr(loc, rem, family, &s);
-
-	if (f->f && run_ssfilter(f->f, &s) == 0)
-		return 0;
-
-	opt[0] = 0;
-	n = sscanf(data, "%x %x:%x %*x:%*x %*x %d %*d %u %d %llx %[^\n]\n",
-	       &s.state, &s.wq, &s.rq,
-	       &s.uid, &s.ino,
-	       &s.refcnt, &s.sk, opt);
-
-	if (n < 9)
-		opt[0] = 0;
-
-	s.type = dg_proto == UDP_PROTO ? IPPROTO_UDP : 0;
-	inet_stats_print(&s, false);
-
-	if (show_details && opt[0])
-		out(" opt:\"%s\"", opt);
-
+	inet_show_netlink(f, NULL, IPPROTO_SCTP);
 	return 0;
 }
 
 static int udp_show(struct filter *f)
 {
-	FILE *fp = NULL;
-
 	if (!filter_af_get(f, AF_INET) && !filter_af_get(f, AF_INET6))
 		return 0;
 
 	dg_proto = UDP_PROTO;
 
-	if (!getenv("PROC_NET_UDP") && !getenv("PROC_ROOT")
-	    && inet_show_netlink(f, NULL, IPPROTO_UDP) == 0)
-		return 0;
-
-	if (f->families&FAMILY_MASK(AF_INET)) {
-		if ((fp = net_udp_open()) == NULL)
-			goto outerr;
-		if (generic_record_read(fp, dgram_show_line, f, AF_INET))
-			goto outerr;
-		fclose(fp);
-	}
-
-	if ((f->families&FAMILY_MASK(AF_INET6)) &&
-	    (fp = net_udp6_open()) != NULL) {
-		if (generic_record_read(fp, dgram_show_line, f, AF_INET6))
-			goto outerr;
-		fclose(fp);
-	}
+	if (inet_show_netlink(f, NULL, IPPROTO_UDP))
+		return sock_diag_error("UDP");
 	return 0;
-
-outerr:
-	do {
-		int saved_errno = errno;
-
-		if (fp)
-			fclose(fp);
-		errno = saved_errno;
-		return -1;
-	} while (0);
 }
 
 static int raw_show(struct filter *f)
 {
-	FILE *fp = NULL;
-
 	if (!filter_af_get(f, AF_INET) && !filter_af_get(f, AF_INET6))
 		return 0;
 
 	dg_proto = RAW_PROTO;
 
-	if (!getenv("PROC_NET_RAW") && !getenv("PROC_ROOT") &&
-	    inet_show_netlink(f, NULL, IPPROTO_RAW) == 0)
-		return 0;
-
-	if (f->families&FAMILY_MASK(AF_INET)) {
-		if ((fp = net_raw_open()) == NULL)
-			goto outerr;
-		if (generic_record_read(fp, dgram_show_line, f, AF_INET))
-			goto outerr;
-		fclose(fp);
-	}
-
-	if ((f->families&FAMILY_MASK(AF_INET6)) &&
-	    (fp = net_raw6_open()) != NULL) {
-		if (generic_record_read(fp, dgram_show_line, f, AF_INET6))
-			goto outerr;
-		fclose(fp);
-	}
+	if (inet_show_netlink(f, NULL, IPPROTO_RAW))
+		return sock_diag_error("RAW");
 	return 0;
-
-outerr:
-	do {
-		int saved_errno = errno;
-
-		if (fp)
-			fclose(fp);
-		errno = saved_errno;
-		return -1;
-	} while (0);
-}
-
-#define MAX_UNIX_REMEMBER (1024*1024/sizeof(struct sockstat))
-
-static void unix_list_drop_first(struct sockstat **list)
-{
-	struct sockstat *s = *list;
-
-	(*list) = (*list)->next;
-	free(s->name);
-	free(s);
 }
 
 static bool unix_type_skip(struct sockstat *s, struct filter *f)
@@ -4579,134 +4203,11 @@ static int unix_show_netlink(struct filter *f)
 
 static int unix_show(struct filter *f)
 {
-	FILE *fp;
-	char buf[256];
-	char name[128];
-	int  newformat = 0;
-	int  cnt;
-	struct sockstat *list = NULL;
-	const int unix_state_map[] = { SS_CLOSE, SS_SYN_SENT,
-				       SS_ESTABLISHED, SS_CLOSING };
-
 	if (!filter_af_get(f, AF_UNIX))
 		return 0;
 
-	if (!getenv("PROC_NET_UNIX") && !getenv("PROC_ROOT")
-	    && unix_show_netlink(f) == 0)
-		return 0;
-
-	if ((fp = net_unix_open()) == NULL)
-		return -1;
-	if (!fgets(buf, sizeof(buf), fp)) {
-		fclose(fp);
-		return -1;
-	}
-
-	if (memcmp(buf, "Peer", 4) == 0)
-		newformat = 1;
-	cnt = 0;
-
-	while (fgets(buf, sizeof(buf), fp)) {
-		struct sockstat *u, **insp;
-		int flags;
-
-		if (!(u = calloc(1, sizeof(*u))))
-			break;
-
-		if (sscanf(buf, "%lx: %x %x %x %x %x %d %s",
-			   (unsigned long *)&u->rport, &u->rq, &u->wq, &flags, &u->type,
-			   &u->state, &u->ino, name) < 8)
-			name[0] = 0;
-
-		u->lport = u->ino;
-		u->local.family = u->remote.family = AF_UNIX;
-
-		if (flags & (1 << 16)) {
-			u->state = SS_LISTEN;
-		} else if (u->state > 0 &&
-			   u->state <= ARRAY_SIZE(unix_state_map)) {
-			u->state = unix_state_map[u->state-1];
-			if (u->type == SOCK_DGRAM && u->state == SS_CLOSE && u->rport)
-				u->state = SS_ESTABLISHED;
-		}
-		if (unix_type_skip(u, f) ||
-		    !(f->states & (1 << u->state))) {
-			free(u);
-			continue;
-		}
-
-		if (!newformat) {
-			u->rport = 0;
-			u->rq = 0;
-			u->wq = 0;
-		}
-
-		if (name[0]) {
-			u->name = strdup(name);
-			if (!u->name) {
-				free(u);
-				break;
-			}
-		}
-
-		if (u->rport) {
-			struct sockstat *p;
-
-			for (p = list; p; p = p->next) {
-				if (u->rport == p->lport)
-					break;
-			}
-			if (!p)
-				u->peer_name = "?";
-			else
-				u->peer_name = p->name ? : "*";
-		}
-
-		if (f->f) {
-			struct sockstat st = {
-				.local.family = AF_UNIX,
-				.remote.family = AF_UNIX,
-			};
-
-			memcpy(st.local.data, &u->name, sizeof(u->name));
-			/* when parsing the old format rport is set to 0 and
-			 * therefore peer_name remains NULL
-			 */
-			if (u->peer_name && strcmp(u->peer_name, "*"))
-				memcpy(st.remote.data, &u->peer_name,
-				       sizeof(u->peer_name));
-			if (run_ssfilter(f->f, &st) == 0) {
-				free(u->name);
-				free(u);
-				continue;
-			}
-		}
-
-		insp = &list;
-		while (*insp) {
-			if (u->type < (*insp)->type ||
-			    (u->type == (*insp)->type &&
-			     u->ino < (*insp)->ino))
-				break;
-			insp = &(*insp)->next;
-		}
-		u->next = *insp;
-		*insp = u;
-
-		if (++cnt > MAX_UNIX_REMEMBER) {
-			while (list) {
-				unix_stats_print(list, f);
-				unix_list_drop_first(&list);
-			}
-			cnt = 0;
-		}
-	}
-	fclose(fp);
-	while (list) {
-		unix_stats_print(list, f);
-		unix_list_drop_first(&list);
-	}
-
+	if (unix_show_netlink(f))
+		return sock_diag_error("UNIX");
 	return 0;
 }
 
@@ -4770,10 +4271,6 @@ static int packet_show_sock(struct nlmsghdr *nlh, void *arg)
 	parse_rtattr(tb, PACKET_DIAG_MAX, (struct rtattr *)(r+1),
 		     nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*r)));
 
-	/* use /proc/net/packet if all info are not available */
-	if (!tb[PACKET_DIAG_MEMINFO])
-		return -1;
-
 	stat.type   = r->pdiag_type;
 	stat.prot   = r->pdiag_num;
 	stat.ino    = r->pdiag_ino;
@@ -4909,56 +4406,14 @@ static int packet_show_netlink(struct filter *f)
 	return handle_netlink_request(f, &req.nlh, sizeof(req), packet_show_sock);
 }
 
-static int packet_show_line(char *buf, const struct filter *f, int fam)
-{
-	unsigned long long sk;
-	struct sockstat stat = {};
-	int type, prot, iface, state, rq, uid, ino;
-
-	sscanf(buf, "%llx %*d %d %x %d %d %u %u %u",
-			&sk,
-			&type, &prot, &iface, &state,
-			&rq, &uid, &ino);
-
-	if (type == SOCK_RAW && !(f->dbs & (1<<PACKET_R_DB)))
-		return 0;
-	if (type == SOCK_DGRAM && !(f->dbs & (1<<PACKET_DG_DB)))
-		return 0;
-
-	stat.type  = type;
-	stat.prot  = prot;
-	stat.lport = stat.iface = iface;
-	stat.state = state;
-	stat.rq    = rq;
-	stat.uid   = uid;
-	stat.ino   = ino;
-	stat.state = SS_CLOSE;
-
-	if (packet_stats_print(&stat, f))
-		return 0;
-
-	return 0;
-}
-
 static int packet_show(struct filter *f)
 {
-	FILE *fp;
-	int rc = 0;
-
 	if (!filter_af_get(f, AF_PACKET) || !(f->states & (1 << SS_CLOSE)))
 		return 0;
 
-	if (!getenv("PROC_NET_PACKET") && !getenv("PROC_ROOT") &&
-			packet_show_netlink(f) == 0)
-		return 0;
-
-	if ((fp = net_packet_open()) == NULL)
-		return -1;
-	if (generic_record_read(fp, packet_show_line, f, AF_PACKET))
-		rc = -1;
-
-	fclose(fp);
-	return rc;
+	if (packet_show_netlink(f))
+		return sock_diag_error("PACKET");
+	return 0;
 }
 
 static int xdp_stats_print(struct sockstat *s, const struct filter *f)
@@ -5263,36 +4718,11 @@ static int netlink_show_netlink(struct filter *f)
 
 static int netlink_show(struct filter *f)
 {
-	FILE *fp;
-	char buf[256];
-	int prot, pid;
-	unsigned int groups;
-	int rq, wq, rc;
-	unsigned long long sk, cb;
-
 	if (!filter_af_get(f, AF_NETLINK) || !(f->states & (1 << SS_CLOSE)))
 		return 0;
 
-	if (!getenv("PROC_NET_NETLINK") && !getenv("PROC_ROOT") &&
-		netlink_show_netlink(f) == 0)
-		return 0;
-
-	if ((fp = net_netlink_open()) == NULL)
-		return -1;
-	if (!fgets(buf, sizeof(buf), fp)) {
-		fclose(fp);
-		return -1;
-	}
-
-	while (fgets(buf, sizeof(buf), fp)) {
-		sscanf(buf, "%llx %d %d %x %d %d %llx %d",
-		       &sk,
-		       &prot, &pid, &groups, &rq, &wq, &cb, &rc);
-
-		netlink_show_one(f, prot, pid, groups, 0, 0, 0, rq, wq, sk, cb);
-	}
-
-	fclose(fp);
+	if (netlink_show_netlink(f))
+		return sock_diag_error("NETLINK");
 	return 0;
 }
 
-- 
2.53.0


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

* [PATCH iproute2 03/11] ss: escape names chosen by the socket owner
  2026-11-12 19:59 [PATCH iproute2 00/11] ss fixes from an OSS scan Stephen Hemminger
  2026-11-12 19:59 ` [PATCH iproute2 01/11] ss: fix double close on sock_diag send failure Stephen Hemminger
  2026-11-12 19:59 ` [PATCH iproute2 02/11] ss: drop /proc fallback for socket listing Stephen Hemminger
@ 2026-11-12 19:59 ` Stephen Hemminger
  2026-11-12 19:59 ` [PATCH iproute2 04/11] ss: size the TCPDIAG_FILE buffer to the record Stephen Hemminger
                   ` (7 subsequent siblings)
  10 siblings, 0 replies; 13+ messages in thread
From: Stephen Hemminger @ 2026-11-12 19:59 UTC (permalink / raw)
  To: netdev; +Cc: dsahern, Stephen Hemminger

Unix socket names and process names are set by whoever owns the
socket and can contain newlines, control characters or a double
quote. They reached the output unchanged, so a local user could
split a row, insert a fake one, or, since -p prints the process name
inside double quotes, make a bogus pid appear:

  users:(("x",pid=1,fd=3",pid=268,fd=3))

Escape them with escape_str(), and replace the double quote
there like the other non-printing characters. Set LC_CTYPE from the
environment so UTF-8 names stay intact instead of having every
non-ASCII byte replaced.

Reported-by: Anthropic OSS scanner (ANT-2026-4X7MF603)
Reported-by: Anthropic OSS scanner (ANT-2026-CAAB6GZ9)
Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
---
 lib/escape.c |  7 ++++---
 misc/ss.c    | 13 +++++++++++--
 2 files changed, 15 insertions(+), 5 deletions(-)

diff --git a/lib/escape.c b/lib/escape.c
index b110f61b..e4646bc5 100644
--- a/lib/escape.c
+++ b/lib/escape.c
@@ -50,7 +50,7 @@ static const char UTF_tab[] = {
 
 static const unsigned char ESC_tab[] = {
 	"@..............................." // 0x00 - 0x1F
-	"||||||||||||||||||||||||||||||||" // 0x20 - 0x3F
+	"||?|||||||||||||||||||||||||||||" // 0x20 - 0x3F
 	"||||||||||||||||||||||||||||||||" // 0x40 - 0x5f
 	"|||||||||||||||||||||||||||||||." // 0x60 - 0x7F
 	"????????????????????????????????" // 0x80 - 0x9F
@@ -83,8 +83,9 @@ static void esc_ctl(unsigned char *str, int len)
 			esc_all(str);
 			return;
 		}
-		// and eliminate those non-printing control characters
-		if (*str < 0x20 || *str == 0x7f)
+		// and eliminate those non-printing control characters,
+		// plus double quote which callers use as a delimiter
+		if (*str < 0x20 || *str == 0x7f || *str == '"')
 			*str = '?';
 		str += n;
 		i += n;
diff --git a/misc/ss.c b/misc/ss.c
index b93df60f..4462a479 100644
--- a/misc/ss.c
+++ b/misc/ss.c
@@ -25,6 +25,7 @@
 #include <limits.h>
 #include <stdarg.h>
 #include <ctype.h>
+#include <locale.h>
 
 #include "ss_util.h"
 #include "utils.h"
@@ -4075,10 +4076,13 @@ static bool unix_type_skip(struct sockstat *s, struct filter *f)
 static void unix_stats_print(struct sockstat *s, struct filter *f)
 {
 	char port_name[30] = {};
+	char name[128];
 
 	sock_state_print(s);
 
-	sock_addr_print(s->name ?: "*", " ",
+	/* name is chosen by whoever bound the socket, may contain newlines */
+	escape_str(name, s->name ?: "*", sizeof(name));
+	sock_addr_print(name, " ",
 			uint_to_str(s->lport, port_name), NULL);
 	sock_addr_print(s->peer_name ?: "*", " ",
 			uint_to_str(s->rport, port_name), NULL);
@@ -4611,6 +4615,7 @@ static int netlink_show_one(struct filter *f,
 	if (pid == -1) {
 		procname[0] = '*';
 	} else if (!numeric) {
+		char task[16];
 		int done = 0;
 
 		if (!pid) {
@@ -4618,7 +4623,8 @@ static int netlink_show_one(struct filter *f,
 			strncpy(procname, "kernel", 7);
 		} else if (pid > 0) {
 			if (get_task_name(getenv("PROC_ROOT"), pid,
-					  procname, sizeof(procname)) == 0) {
+					  task, sizeof(task)) == 0) {
+				escape_str(procname, task, sizeof(procname));
 				snprintf(procname + strlen(procname),
 					 sizeof(procname) - strlen(procname),
 					 "/%d", pid);
@@ -5357,6 +5363,9 @@ int main(int argc, char *argv[])
 	int ch;
 	int state_filter = 0;
 
+	/* needed by escape_str() to pass through UTF-8 names */
+	setlocale(LC_CTYPE, "");
+
 	while ((ch = getopt_long(argc, argv,
 				 "halBetuwxnro460spTbEf:mMiA:D:F:vVzZN:KHQSO",
 				 long_opts, NULL)) != EOF) {
-- 
2.53.0


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

* [PATCH iproute2 04/11] ss: size the TCPDIAG_FILE buffer to the record
  2026-11-12 19:59 [PATCH iproute2 00/11] ss fixes from an OSS scan Stephen Hemminger
                   ` (2 preceding siblings ...)
  2026-11-12 19:59 ` [PATCH iproute2 03/11] ss: escape names chosen by the socket owner Stephen Hemminger
@ 2026-11-12 19:59 ` Stephen Hemminger
  2026-11-12 19:59 ` [PATCH iproute2 05/11] ss: replay all dumps in TCPDIAG_FILE Stephen Hemminger
                   ` (6 subsequent siblings)
  10 siblings, 0 replies; 13+ messages in thread
From: Stephen Hemminger @ 2026-11-12 19:59 UTC (permalink / raw)
  To: netdev; +Cc: dsahern, Stephen Hemminger

tcp_show_netlink_file() read each record into a 16K stack buffer
using the length from the file, so a record over 16K, which the
kernel can produce (e.g. many TCP-MD5 keys), overflowed the stack.

Read into a heap buffer grown to the record instead. A length below
a netlink header is still rejected, since the size arithmetic would
underflow.

Reported-by: Anthropic OSS scanner (ANT-2026-RV0SH3JH)
Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
---
 misc/ss.c | 40 +++++++++++++++++++++++++++++++++++-----
 1 file changed, 35 insertions(+), 5 deletions(-)

diff --git a/misc/ss.c b/misc/ss.c
index 4462a479..3c538a4e 100644
--- a/misc/ss.c
+++ b/misc/ss.c
@@ -3923,8 +3923,9 @@ Exit:
 
 static int tcp_show_netlink_file(struct filter *f)
 {
+	struct nlmsghdr *h = NULL;
+	size_t buflen = 0;
 	FILE	*fp;
-	char	buf[16384];
 	int	err = -1;
 
 	if ((fp = fopen(getenv("TCPDIAG_FILE"), "r")) == NULL) {
@@ -3935,11 +3936,11 @@ static int tcp_show_netlink_file(struct filter *f)
 	while (1) {
 		int err2;
 		size_t status, nitems;
-		struct nlmsghdr *h = (struct nlmsghdr *)buf;
+		struct nlmsghdr hdr;
 		struct sockstat s = {};
 
-		status = fread(buf, 1, sizeof(*h), fp);
-		if (status != sizeof(*h)) {
+		status = fread(&hdr, 1, sizeof(hdr), fp);
+		if (status != sizeof(hdr)) {
 			if (ferror(fp))
 				perror("Reading header from $TCPDIAG_FILE");
 			if (feof(fp))
@@ -3947,7 +3948,29 @@ static int tcp_show_netlink_file(struct filter *f)
 			break;
 		}
 
-		nitems = NLMSG_ALIGN(h->nlmsg_len - sizeof(*h));
+		/* NLMSG_ALIGN() is u32 arithmetic and wraps near UINT_MAX,
+		 * which would size the buffer below the record.
+		 */
+		if (hdr.nlmsg_len < sizeof(hdr) ||
+		    hdr.nlmsg_len > UINT32_MAX - NLMSG_ALIGNTO + 1) {
+			fprintf(stderr, "Malformed message in $TCPDIAG_FILE: len=%u\n",
+				hdr.nlmsg_len);
+			break;
+		}
+
+		if (NLMSG_ALIGN(hdr.nlmsg_len) > buflen) {
+			void *nbuf = realloc(h, NLMSG_ALIGN(hdr.nlmsg_len));
+
+			if (!nbuf) {
+				perror("Reading $TCPDIAG_FILE");
+				break;
+			}
+			h = nbuf;
+			buflen = NLMSG_ALIGN(hdr.nlmsg_len);
+		}
+		*h = hdr;
+
+		nitems = NLMSG_ALIGN(h->nlmsg_len) - sizeof(*h);
 		status = fread(h+1, 1, nitems, fp);
 
 		if (status != nitems) {
@@ -3976,6 +3999,12 @@ static int tcp_show_netlink_file(struct filter *f)
 			break;
 		}
 
+		if (h->nlmsg_len < NLMSG_LENGTH(sizeof(struct inet_diag_msg))) {
+			fprintf(stderr, "Malformed message in $TCPDIAG_FILE: len=%u\n",
+				h->nlmsg_len);
+			break;
+		}
+
 		parse_diag_msg(h, &s);
 		s.type = IPPROTO_TCP;
 
@@ -3989,6 +4018,7 @@ static int tcp_show_netlink_file(struct filter *f)
 		}
 	}
 
+	free(h);
 	fclose(fp);
 	return err;
 }
-- 
2.53.0


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

* [PATCH iproute2 05/11] ss: replay all dumps in TCPDIAG_FILE
  2026-11-12 19:59 [PATCH iproute2 00/11] ss fixes from an OSS scan Stephen Hemminger
                   ` (3 preceding siblings ...)
  2026-11-12 19:59 ` [PATCH iproute2 04/11] ss: size the TCPDIAG_FILE buffer to the record Stephen Hemminger
@ 2026-11-12 19:59 ` Stephen Hemminger
  2026-11-12 19:59 ` [PATCH iproute2 06/11] ss: bound state and type name lookups Stephen Hemminger
                   ` (5 subsequent siblings)
  10 siblings, 0 replies; 13+ messages in thread
From: Stephen Hemminger @ 2026-11-12 19:59 UTC (permalink / raw)
  To: netdev; +Cc: dsahern, Stephen Hemminger

ss -D records an IPv4 dump followed by an IPv6 dump, but the
replay stopped at the first NLMSG_DONE, so IPv6 sockets were never
shown. Keep reading until end of file, and apply the -4/-6 family
filter, which a live dump applies in the request. The family comes
from the file, so check its range.

The ssfilter test expected the IPv4-only output; add -4 where both
families match and add IPv6 cases.

Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
---
 misc/ss.c                     | 18 +++++++++++++-----
 testsuite/tests/ss/ssfilter.t | 26 +++++++++++++++++++-------
 2 files changed, 32 insertions(+), 12 deletions(-)

diff --git a/misc/ss.c b/misc/ss.c
index 3c538a4e..72de0742 100644
--- a/misc/ss.c
+++ b/misc/ss.c
@@ -444,6 +444,9 @@ static void filter_af_set(struct filter *f, int af)
 
 static int filter_af_get(struct filter *f, int af)
 {
+	if (af < 0 || af >= 64)
+		return 0;
+
 	return !!(f->families & FAMILY_MASK(af));
 }
 
@@ -3943,8 +3946,8 @@ static int tcp_show_netlink_file(struct filter *f)
 		if (status != sizeof(hdr)) {
 			if (ferror(fp))
 				perror("Reading header from $TCPDIAG_FILE");
-			if (feof(fp))
-				fprintf(stderr, "Unexpected EOF reading $TCPDIAG_FILE");
+			else if (status || err)
+				fprintf(stderr, "Unexpected EOF reading $TCPDIAG_FILE\n");
 			break;
 		}
 
@@ -3977,15 +3980,17 @@ static int tcp_show_netlink_file(struct filter *f)
 			if (ferror(fp))
 				perror("Reading $TCPDIAG_FILE");
 			if (feof(fp))
-				fprintf(stderr, "Unexpected EOF reading $TCPDIAG_FILE");
+				fprintf(stderr, "Unexpected EOF reading $TCPDIAG_FILE\n");
+			err = -1;
 			break;
 		}
 
-		/* The only legal exit point */
+		/* One dump per address family; the file ends after a DONE */
 		if (h->nlmsg_type == NLMSG_DONE) {
 			err = 0;
-			break;
+			continue;
 		}
+		err = -1;
 
 		if (h->nlmsg_type == NLMSG_ERROR) {
 			struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(h);
@@ -4008,6 +4013,9 @@ static int tcp_show_netlink_file(struct filter *f)
 		parse_diag_msg(h, &s);
 		s.type = IPPROTO_TCP;
 
+		if (f && !filter_af_get(f, s.local.family))
+			continue;
+
 		if (f && f->f && run_ssfilter(f->f, &s) == 0)
 			continue;
 
diff --git a/testsuite/tests/ss/ssfilter.t b/testsuite/tests/ss/ssfilter.t
index 4c2315ca..cc0f01a5 100755
--- a/testsuite/tests/ss/ssfilter.t
+++ b/testsuite/tests/ss/ssfilter.t
@@ -3,21 +3,27 @@
 . lib/generic.sh
 
 # % ./misc/ss -Htna
-# LISTEN  0    128    0.0.0.0:22       0.0.0.0:*
-# ESTAB   0    0     10.0.0.1:22      10.0.0.1:36266
-# ESTAB   0    0     10.0.0.1:36266   10.0.0.1:22
-# ESTAB   0    0     10.0.0.1:22      10.0.0.2:50312
+# LISTEN 0      128          0.0.0.0:22         0.0.0.0:*
+# ESTAB  0      0           10.0.0.1:22        10.0.0.1:36266
+# ESTAB  0      0           10.0.0.1:36266     10.0.0.1:22
+# ESTAB  0      0           10.0.0.1:22        10.0.0.2:50312
+# LISTEN 0      128             [::]:22            [::]:*
+# ESTAB  0      0      [fec0:42::24]:40520 [fec0:42::1]:22
+# ESTAB  0      0      [fec0:42::24]:22    [fec0:42::1]:44248
+#
+# Each test filters to one address family, so the address columns are
+# not padded for the wider IPv6 rows.
 export TCPDIAG_FILE="$(dirname $0)/ss1.dump"
 
 ts_log "[Testing ssfilter]"
 
-ts_ss "$0" "Match dport = 22" -Htna dport = 22
+ts_ss "$0" "Match dport = 22" -Htna4 dport = 22
 test_on "ESTAB 0      0      10.0.0.1:36266 10.0.0.1:22"
 
-ts_ss "$0" "Match dport 22" -Htna dport 22
+ts_ss "$0" "Match dport 22" -Htna4 dport 22
 test_on "ESTAB 0      0      10.0.0.1:36266 10.0.0.1:22"
 
-ts_ss "$0" "Match (dport)" -Htna '( dport = 22 )'
+ts_ss "$0" "Match (dport)" -Htna4 '( dport = 22 )'
 test_on "ESTAB 0      0      10.0.0.1:36266 10.0.0.1:22"
 
 ts_ss "$0" "Match src = 0.0.0.0" -Htna src = 0.0.0.0
@@ -46,3 +52,9 @@ test_on "ESTAB 0      0      10.0.0.1:22 10.0.0.2:50312"
 
 ts_ss "$0" "Match (src or src) and dst" -Htna '( src 0.0.0.0 or src 10.0.0.1 ) and dst 10.0.0.2'
 test_on "ESTAB 0      0      10.0.0.1:22 10.0.0.2:50312"
+
+ts_ss "$0" "Match IPv6 dport = 22" -Htna6 dport = 22
+test_on "ESTAB 0      0      \[fec0:42::24\]:40520 \[fec0:42::1\]:22"
+
+ts_ss "$0" "Match IPv6 src:port" -Htna 'src [fec0:42::24]:22'
+test_on "ESTAB 0      0      \[fec0:42::24\]:22 \[fec0:42::1\]:44248"
-- 
2.53.0


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

* [PATCH iproute2 06/11] ss: bound state and type name lookups
  2026-11-12 19:59 [PATCH iproute2 00/11] ss fixes from an OSS scan Stephen Hemminger
                   ` (4 preceding siblings ...)
  2026-11-12 19:59 ` [PATCH iproute2 05/11] ss: replay all dumps in TCPDIAG_FILE Stephen Hemminger
@ 2026-11-12 19:59 ` Stephen Hemminger
  2026-11-12 19:59 ` [PATCH iproute2 07/11] ss: fix rpcbind service cache Stephen Hemminger
                   ` (4 subsequent siblings)
  10 siblings, 0 replies; 13+ messages in thread
From: Stephen Hemminger @ 2026-11-12 19:59 UTC (permalink / raw)
  To: netdev; +Cc: dsahern, Stephen Hemminger

The socket state, SCTP state and TIPC socket type come from the
kernel (or a TCPDIAG_FILE) and were used to index name tables
without a range check.

SCTP associations were detected by the inode matching the previous
row, so an endpoint dumped twice was treated as an association and
its TCP-style state indexed the SCTP table out of range. Use the
INET_DIAG_PEERS attribute, which the kernel only sends for
associations, instead.

Reported-by: Anthropic OSS scanner (ANT-2026-GDQ1INKZ)
Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
---
 misc/ss.c | 43 ++++++++++++++++++++++---------------------
 1 file changed, 22 insertions(+), 21 deletions(-)

diff --git a/misc/ss.c b/misc/ss.c
index 72de0742..2becb852 100644
--- a/misc/ss.c
+++ b/misc/ss.c
@@ -107,7 +107,6 @@ static int show_proc_ctx;
 static int show_sock_ctx;
 static int show_header = 1;
 static int follow_events;
-static int sctp_ino;
 static int show_tipcinfo;
 static int show_tos;
 static int show_cgroup;
@@ -766,7 +765,15 @@ static unsigned long long cookie_sk_get(const uint32_t *cookie)
 	return (((unsigned long long)cookie[1] << 31) << 1) | cookie[0];
 }
 
-static const char *sctp_sstate_name[] = {
+/* Names indexed by a value from the kernel, which may be out of range */
+static const char *table_name(const char * const *names, size_t n,
+			      unsigned int i)
+{
+	return i < n && names[i] ? names[i] : "UNKNOWN";
+}
+#define TABLE_NAME(names, i)	table_name(names, ARRAY_SIZE(names), i)
+
+static const char * const sctp_sstate_name[] = {
 	[SCTP_STATE_CLOSED] = "CLOSED",
 	[SCTP_STATE_COOKIE_WAIT] = "COOKIE_WAIT",
 	[SCTP_STATE_COOKIE_ECHOED] = "COOKIE_ECHOED",
@@ -805,6 +812,7 @@ struct sockstat {
 	char *peer_name;
 	__u32		    mark;
 	__u64		    cgroup_id;
+	bool		    sctp_assoc;
 };
 
 struct dctcpstat {
@@ -880,18 +888,6 @@ struct tcpstat {
 	struct tcp_bbr_info *bbr_info;
 };
 
-/* SCTP assocs share the same inode number with their parent endpoint. So if we
- * have seen the inode number before, it must be an assoc instead of the next
- * endpoint. */
-static bool is_sctp_assoc(struct sockstat *s, const char *sock_name)
-{
-	if (strcmp(sock_name, "sctp"))
-		return false;
-	if (!sctp_ino || sctp_ino != s->ino)
-		return false;
-	return true;
-}
-
 static const char *unix_netid_name(int type)
 {
 	switch (type) {
@@ -1437,14 +1433,14 @@ static void sock_state_print(struct sockstat *s)
 		sock_name = "unknown";
 	}
 
-	if (is_sctp_assoc(s, sock_name)) {
+	if (s->sctp_assoc) {
 		field_set(COL_STATE);		/* Empty Netid field */
-		out("`- %s", sctp_sstate_name[s->state]);
+		out("`- %s", TABLE_NAME(sctp_sstate_name, s->state));
 	} else {
 		field_set(COL_NETID);
 		out("%s", sock_name);
 		field_set(COL_STATE);
-		out("%s", sstate_name[s->state]);
+		out("%s", TABLE_NAME(sstate_name, s->state));
 	}
 
 	if (show_queues) {
@@ -2498,7 +2494,7 @@ static void sctp_stats_print(struct sctp_info *s)
 	if (s->sctpi_tag)
 		out(" tag:%x", s->sctpi_tag);
 	if (s->sctpi_state)
-		out(" state:%s", sctp_sstate_name[s->sctpi_state]);
+		out(" state:%s", TABLE_NAME(sctp_sstate_name, s->sctpi_state));
 	if (s->sctpi_rwnd)
 		out(" rwnd:%d", s->sctpi_rwnd);
 	if (s->sctpi_unackdata)
@@ -3622,6 +3618,12 @@ static int inet_show_sock(struct nlmsghdr *nlh,
 	if (s->local.family == AF_INET6 && tb[INET_DIAG_SKV6ONLY])
 		v6only = rta_getattr_u8(tb[INET_DIAG_SKV6ONLY]);
 
+	/* SCTP assocs share the inode number of their endpoint; only
+	 * assocs carry INET_DIAG_PEERS, and only for those is the state
+	 * an SCTP_STATE_* value.
+	 */
+	s->sctp_assoc = s->type == IPPROTO_SCTP && tb[INET_DIAG_PEERS];
+
 	inet_stats_print(s, v6only);
 
 	if (show_options) {
@@ -3709,7 +3711,6 @@ static int inet_show_sock(struct nlmsghdr *nlh,
 		else
 			tcp_show_info(nlh, r, tb);
 	}
-	sctp_ino = s->ino;
 
 #ifdef ENABLE_BPF_SKSTORAGE_SUPPORT
 	if (tb[INET_DIAG_SK_BPF_STORAGES])
@@ -4918,9 +4919,9 @@ static int tipc_show_sock(struct nlmsghdr *nlh, void *arg)
 
 	if (show_tipcinfo) {
 		if (oneline)
-			out(" type:%s", stype_nameg[ss.type]);
+			out(" type:%s", TABLE_NAME(stype_nameg, ss.type));
 		else
-			out("\n type:%s", stype_nameg[ss.type]);
+			out("\n type:%s", TABLE_NAME(stype_nameg, ss.type));
 		out(" cong:%s ",
 		       stat[TIPC_NLA_SOCK_STAT_LINK_CONG] ? "link" :
 		       stat[TIPC_NLA_SOCK_STAT_CONN_CONG] ? "conn" : "none");
-- 
2.53.0


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

* [PATCH iproute2 07/11] ss: fix rpcbind service cache
  2026-11-12 19:59 [PATCH iproute2 00/11] ss fixes from an OSS scan Stephen Hemminger
                   ` (5 preceding siblings ...)
  2026-11-12 19:59 ` [PATCH iproute2 06/11] ss: bound state and type name lookups Stephen Hemminger
@ 2026-11-12 19:59 ` Stephen Hemminger
  2026-11-12 19:59 ` [PATCH iproute2 08/11] ss: read socket SELinux context only for -z Stephen Hemminger
                   ` (3 subsequent siblings)
  10 siblings, 0 replies; 13+ messages in thread
From: Stephen Hemminger @ 2026-11-12 19:59 UTC (permalink / raw)
  To: netdev; +Cc: dsahern, Stephen Hemminger

init_service_resolver() left the name of an entry uninitialized
when the program was not in the rpc database, so ss -r could crash
using it. It also leaked an entry on every skipped mapping and
accepted a partly parsed address.

Parse the mapping before allocating, and name unknown programs by
number.

Reported-by: Anthropic OSS scanner (ANT-2026-M2GWD3L5)
Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
---
 misc/ss.c | 44 ++++++++++++++++++++++++++------------------
 1 file changed, 26 insertions(+), 18 deletions(-)

diff --git a/misc/ss.c b/misc/ss.c
index 2becb852..5ecc5b5c 100644
--- a/misc/ss.c
+++ b/misc/ss.c
@@ -1559,37 +1559,45 @@ static void init_service_resolver(void)
 		return;
 
 	for (; rhead; rhead = rhead->rpcb_next) {
-		char prog[128] = "rpc.";
+		const char *proto;
+		char prog[128];
 		struct scache *c;
-		int hport, lport, ok;
+		int hport, lport;
 
-		c = malloc(sizeof(*c));
-		if (!c)
+		if (sscanf(rhead->rpcb_map.r_addr, "::.%d.%d",
+			   &hport, &lport) != 2 &&
+		    sscanf(rhead->rpcb_map.r_addr, "0.0.0.0.%d.%d",
+			   &hport, &lport) != 2)
 			continue;
 
-		ok = sscanf(rhead->rpcb_map.r_addr, "::.%d.%d", &hport, &lport);
-		if (!ok)
-			ok = sscanf(rhead->rpcb_map.r_addr, "0.0.0.0.%d.%d",
-				    &hport, &lport);
-		if (!ok)
-			continue;
-		c->port = hport << 8 | lport;
-
 		if (strcmp(rhead->rpcb_map.r_netid, TCP_PROTO) == 0 ||
 		    strcmp(rhead->rpcb_map.r_netid, TCP6_PROTO) == 0)
-			c->proto = TCP_PROTO;
+			proto = TCP_PROTO;
 		else if (strcmp(rhead->rpcb_map.r_netid, UDP_PROTO) == 0 ||
 			 strcmp(rhead->rpcb_map.r_netid, UDP6_PROTO) == 0)
-			c->proto = UDP_PROTO;
+			proto = UDP_PROTO;
 		else if (strcmp(rhead->rpcb_map.r_netid, SCTP_PROTO) == 0)
-			c->proto = SCTP_PROTO;
+			proto = SCTP_PROTO;
 		else
 			continue;
 
 		rpc = getrpcbynumber(rhead->rpcb_map.r_prog);
-		if (rpc) {
-			strncat(prog, rpc->r_name, 128 - strlen(prog));
-			c->name = strdup(prog);
+		if (rpc)
+			snprintf(prog, sizeof(prog), "rpc.%s", rpc->r_name);
+		else
+			snprintf(prog, sizeof(prog), "rpc.%lu",
+				 (unsigned long)rhead->rpcb_map.r_prog);
+
+		c = malloc(sizeof(*c));
+		if (!c)
+			continue;
+
+		c->port = hport << 8 | lport;
+		c->proto = proto;
+		c->name = strdup(prog);
+		if (!c->name) {
+			free(c);
+			continue;
 		}
 
 		c->next = rlist;
-- 
2.53.0


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

* [PATCH iproute2 08/11] ss: read socket SELinux context only for -z
  2026-11-12 19:59 [PATCH iproute2 00/11] ss fixes from an OSS scan Stephen Hemminger
                   ` (6 preceding siblings ...)
  2026-11-12 19:59 ` [PATCH iproute2 07/11] ss: fix rpcbind service cache Stephen Hemminger
@ 2026-11-12 19:59 ` Stephen Hemminger
  2026-11-12 19:59 ` [PATCH iproute2 09/11] ss: check owner before naming a netlink socket Stephen Hemminger
                   ` (2 subsequent siblings)
  10 siblings, 0 replies; 13+ messages in thread
From: Stephen Hemminger @ 2026-11-12 19:59 UTC (permalink / raw)
  To: netdev; +Cc: dsahern, Stephen Hemminger

user_ent_hash_build_task() called getfilecon() on /proc/PID/fd/N
for every socket, even without -z, and the path was resolved again
after readlink() had seen a socket. The owner of the descriptor
could swap in a file on a FUSE mount it serves and hang ss -p
in getxattr.

Only look up the context for -z, and read it through an O_PATH
descriptor after checking with readlink(), which does not call into
the file's filesystem, that it is still the same socket.

Reported-by: Anthropic OSS scanner (ANT-2026-EPMKLNM2)
Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
---
 misc/ss.c | 35 ++++++++++++++++++++++++++++++++++-
 1 file changed, 34 insertions(+), 1 deletion(-)

diff --git a/misc/ss.c b/misc/ss.c
index 5ecc5b5c..9d2f9891 100644
--- a/misc/ss.c
+++ b/misc/ss.c
@@ -533,6 +533,38 @@ static void user_ent_add(unsigned int ino, char *task,
 
 #define MAX_PATH_LEN	1024
 
+/* The owner of a descriptor can point $PROC_ROOT/$ID/fd/$FD elsewhere
+ * (e.g. at a file on a FUSE mount it serves) after readlink() saw a socket.
+ * Pin the object and check it is still that socket before reading its label.
+ * The check uses readlink(), which does not call into the filesystem of
+ * the pinned object.
+ */
+static int user_ent_sock_context(const char *path, unsigned int ino,
+				 char **context)
+{
+	unsigned int pinned;
+	char self[64];
+	char lnk[64];
+	ssize_t len;
+	int ret = -1;
+	int fd;
+
+	fd = open(path, O_PATH | O_CLOEXEC);
+	if (fd < 0)
+		return -1;
+
+	snprintf(self, sizeof(self), "/proc/self/fd/%d", fd);
+	len = readlink(self, lnk, sizeof(lnk) - 1);
+	if (len > 0) {
+		lnk[len] = '\0';
+		if (sscanf(lnk, "socket:[%u]", &pinned) == 1 && pinned == ino)
+			ret = getfilecon(self, context);
+	}
+
+	close(fd);
+	return ret;
+}
+
 static void user_ent_hash_build_task(char *path, int pid, int tid)
 {
 	const char *no_ctx = "unavailable";
@@ -580,7 +612,8 @@ static void user_ent_hash_build_task(char *path, int pid, int tid)
 		if (sscanf(lnk, "socket:[%u]", &ino) != 1)
 			continue;
 
-		if (getfilecon(path, &sock_context) <= 0)
+		if (!show_sock_ctx ||
+		    user_ent_sock_context(path, ino, &sock_context) <= 0)
 			sock_context = strdup(no_ctx);
 
 		if (task[0] == '\0') {
-- 
2.53.0


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

* [PATCH iproute2 09/11] ss: check owner before naming a netlink socket
  2026-11-12 19:59 [PATCH iproute2 00/11] ss fixes from an OSS scan Stephen Hemminger
                   ` (7 preceding siblings ...)
  2026-11-12 19:59 ` [PATCH iproute2 08/11] ss: read socket SELinux context only for -z Stephen Hemminger
@ 2026-11-12 19:59 ` Stephen Hemminger
  2026-11-12 19:59 ` [PATCH iproute2 10/11] ss: keep following events after ENOBUFS Stephen Hemminger
  2026-11-12 19:59 ` [PATCH iproute2 11/11] ss: report a failed SCTP or MPTCP dump Stephen Hemminger
  10 siblings, 0 replies; 13+ messages in thread
From: Stephen Hemminger @ 2026-11-12 19:59 UTC (permalink / raw)
  To: netdev; +Cc: dsahern, Stephen Hemminger

The netlink port id was taken as the pid of the owning process,
but any process can bind any free port id, so ss could show (and
report the SELinux context of) an unrelated process.

Only name the process if it holds the socket. Port ids that cannot
be checked are shown as numbers.

Reported-by: Anthropic OSS scanner (ANT-2026-1FCHVE1N)
Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
---
 man/man8/ss.8 |  3 ++-
 misc/ss.c     | 51 ++++++++++++++++++++++++++++++++++++++++++++++-----
 2 files changed, 48 insertions(+), 6 deletions(-)

diff --git a/man/man8/ss.8 b/man/man8/ss.8
index 54af744a..96d25ab0 100644
--- a/man/man8/ss.8
+++ b/man/man8/ss.8
@@ -337,7 +337,8 @@ sockets the initiating process context is displayed as follows:
 .RS
 .RS
 .IP "1." 4
-If valid pid show the process context.
+If the port id is the pid of a process holding the socket, show the process
+context.
 .IP "2." 4
 If destination is kernel (pid = 0) show kernel initial context.
 .IP "3." 4
diff --git a/misc/ss.c b/misc/ss.c
index 9d2f9891..6d37f676 100644
--- a/misc/ss.c
+++ b/misc/ss.c
@@ -4662,10 +4662,47 @@ static int xdp_show(struct filter *f)
 	return handle_netlink_request(f, &req.nlh, sizeof(req), xdp_show_sock);
 }
 
+/* A netlink port id is chosen by whoever binds the socket; it is the
+ * pid of the owner only by convention. Check that the process holds it.
+ */
+static bool netlink_pid_holds_sock(int pid, unsigned int ino)
+{
+	char path[MAX_PATH_LEN];
+	struct dirent *d;
+	bool ret = false;
+	DIR *dir;
+
+	if (pid <= 0 || !ino)
+		return false;
+
+	snprintf(path, sizeof(path), "%s/%d/fd",
+		 getenv("PROC_ROOT") ? : "/proc", pid);
+	dir = opendir(path);
+	if (!dir)
+		return false;
+
+	while (!ret && (d = readdir(dir)) != NULL) {
+		unsigned int fd_ino;
+		ssize_t link_len;
+		char lnk[64];
+
+		link_len = readlinkat(dirfd(dir), d->d_name, lnk, sizeof(lnk) - 1);
+		if (link_len == -1)
+			continue;
+		lnk[link_len] = '\0';
+
+		if (sscanf(lnk, "socket:[%u]", &fd_ino) == 1)
+			ret = fd_ino == ino;
+	}
+	closedir(dir);
+
+	return ret;
+}
+
 static int netlink_show_one(struct filter *f,
 				int prot, int pid, unsigned int groups,
 				int state, int dst_pid, unsigned int dst_group,
-				int rq, int wq,
+				int rq, int wq, unsigned int ino,
 				unsigned long long sk, unsigned long long cb)
 {
 	struct sockstat st = {
@@ -4679,6 +4716,7 @@ static int netlink_show_one(struct filter *f,
 	SPRINT_BUF(prot_buf) = {};
 	const char *prot_name;
 	char procname[64] = {};
+	bool is_owner = false;
 
 	if (f->f) {
 		st.rport = -1;
@@ -4692,6 +4730,9 @@ static int netlink_show_one(struct filter *f,
 
 	prot_name = nl_proto_n2a(prot, prot_buf, sizeof(prot_buf));
 
+	if (pid > 0 && (!numeric || show_proc_ctx))
+		is_owner = netlink_pid_holds_sock(pid, ino);
+
 	if (pid == -1) {
 		procname[0] = '*';
 	} else if (!numeric) {
@@ -4701,7 +4742,7 @@ static int netlink_show_one(struct filter *f,
 		if (!pid) {
 			done = 1;
 			strncpy(procname, "kernel", 7);
-		} else if (pid > 0) {
+		} else if (is_owner) {
 			if (get_task_name(getenv("PROC_ROOT"), pid,
 					  task, sizeof(task)) == 0) {
 				escape_str(procname, task, sizeof(procname));
@@ -4734,13 +4775,13 @@ static int netlink_show_one(struct filter *f,
 	if (show_proc_ctx) {
 		/* The pid value will either be:
 		 *   0 if destination kernel - show kernel initial context.
-		 *   A valid process pid - use getpidcon.
+		 *   The pid of a process holding the socket - use getpidcon.
 		 *   A unique value allocated by the kernel or netlink user
 		 *   to the process - show context as "not available".
 		 */
 		if (!pid)
 			security_get_initial_context("kernel", &pid_context);
-		else if (pid > 0)
+		else if (is_owner)
 			getpidcon(pid, &pid_context);
 
 		out(" proc_ctx=%s", pid_context ? : "unavailable");
@@ -4779,7 +4820,7 @@ static int netlink_show_sock(struct nlmsghdr *nlh, void *arg)
 
 	if (netlink_show_one(f, r->ndiag_protocol, r->ndiag_portid, groups,
 			 r->ndiag_state, r->ndiag_dst_portid, r->ndiag_dst_group,
-			 rq, wq, 0, 0)) {
+			 rq, wq, r->ndiag_ino, 0, 0)) {
 		return 0;
 	}
 
-- 
2.53.0


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

* [PATCH iproute2 10/11] ss: keep following events after ENOBUFS
  2026-11-12 19:59 [PATCH iproute2 00/11] ss fixes from an OSS scan Stephen Hemminger
                   ` (8 preceding siblings ...)
  2026-11-12 19:59 ` [PATCH iproute2 09/11] ss: check owner before naming a netlink socket Stephen Hemminger
@ 2026-11-12 19:59 ` Stephen Hemminger
  2026-11-12 19:59 ` [PATCH iproute2 11/11] ss: report a failed SCTP or MPTCP dump Stephen Hemminger
  10 siblings, 0 replies; 13+ messages in thread
From: Stephen Hemminger @ 2026-11-12 19:59 UTC (permalink / raw)
  To: netdev; +Cc: dsahern, Stephen Hemminger

ss -E exited when a burst of socket closes overflowed its receive
queue. Lost events are not fatal, so keep listening, as
rtnl_listen() does.

With -K the second socket, used only to send SOCK_DESTROY, also
joined the event groups and its queue filled up unread. Open it
without groups.

Reported-by: Anthropic OSS scanner (ANT-2026-39AYDLBX)
Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
---
 misc/ss.c | 9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)

diff --git a/misc/ss.c b/misc/ss.c
index 6d37f676..7c47d377 100644
--- a/misc/ss.c
+++ b/misc/ss.c
@@ -5099,14 +5099,19 @@ static int handle_follow_request(struct filter *f)
 	rth.local.nl_pid = 0;
 
 	if (f->kill) {
-		if (rtnl_open_byproto(&rth2, groups, NETLINK_SOCK_DIAG)) {
+		/* Only used to send SOCK_DESTROY, must not get the events */
+		if (rtnl_open_byproto(&rth2, 0, NETLINK_SOCK_DIAG)) {
 			rtnl_close(&rth);
 			return -1;
 		}
 		f->rth_for_killing = &rth2;
 	}
 
-	if (rtnl_dump_filter(&rth, generic_show_sock, f))
+	/* ENOBUFS means events were lost, keep listening */
+	do {
+		ret = rtnl_dump_filter(&rth, generic_show_sock, f);
+	} while (ret == -ENOBUFS);
+	if (ret)
 		ret = -1;
 
 	rtnl_close(&rth);
-- 
2.53.0


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

* [PATCH iproute2 11/11] ss: report a failed SCTP or MPTCP dump
  2026-11-12 19:59 [PATCH iproute2 00/11] ss fixes from an OSS scan Stephen Hemminger
                   ` (9 preceding siblings ...)
  2026-11-12 19:59 ` [PATCH iproute2 10/11] ss: keep following events after ENOBUFS Stephen Hemminger
@ 2026-11-12 19:59 ` Stephen Hemminger
  10 siblings, 0 replies; 13+ messages in thread
From: Stephen Hemminger @ 2026-11-12 19:59 UTC (permalink / raw)
  To: netdev; +Cc: dsahern, Stephen Hemminger

sctp_show() and mptcp_show() discarded the return of
inet_show_netlink(), unlike every other family, so ss -S and ss -M
printed only a header and exited 0 when the kernel could not dump.

Neither protocol is dumped unless asked for, and sock_diag_error()
already reports a missing module as lack of support, so this does
not add noise to a default run.

Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
---
 misc/ss.c | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/misc/ss.c b/misc/ss.c
index 7c47d377..f4160f59 100644
--- a/misc/ss.c
+++ b/misc/ss.c
@@ -4105,7 +4105,8 @@ static int mptcp_show(struct filter *f)
 	if (!filter_af_get(f, AF_INET) && !filter_af_get(f, AF_INET6))
 		return 0;
 
-	inet_show_netlink(f, NULL, IPPROTO_MPTCP);
+	if (inet_show_netlink(f, NULL, IPPROTO_MPTCP))
+		return sock_diag_error("MPTCP");
 	return 0;
 }
 
@@ -4114,7 +4115,8 @@ static int sctp_show(struct filter *f)
 	if (!filter_af_get(f, AF_INET) && !filter_af_get(f, AF_INET6))
 		return 0;
 
-	inet_show_netlink(f, NULL, IPPROTO_SCTP);
+	if (inet_show_netlink(f, NULL, IPPROTO_SCTP))
+		return sock_diag_error("SCTP");
 	return 0;
 }
 
-- 
2.53.0


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

end of thread, other threads:[~2026-11-12 20:00 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-11-12 19:59 [PATCH iproute2 00/11] ss fixes from an OSS scan Stephen Hemminger
2026-11-12 19:59 ` [PATCH iproute2 01/11] ss: fix double close on sock_diag send failure Stephen Hemminger
2026-11-12 19:59 ` [PATCH iproute2 02/11] ss: drop /proc fallback for socket listing Stephen Hemminger
2026-11-12 19:59 ` [PATCH iproute2 03/11] ss: escape names chosen by the socket owner Stephen Hemminger
2026-11-12 19:59 ` [PATCH iproute2 04/11] ss: size the TCPDIAG_FILE buffer to the record Stephen Hemminger
2026-11-12 19:59 ` [PATCH iproute2 05/11] ss: replay all dumps in TCPDIAG_FILE Stephen Hemminger
2026-11-12 19:59 ` [PATCH iproute2 06/11] ss: bound state and type name lookups Stephen Hemminger
2026-11-12 19:59 ` [PATCH iproute2 07/11] ss: fix rpcbind service cache Stephen Hemminger
2026-11-12 19:59 ` [PATCH iproute2 08/11] ss: read socket SELinux context only for -z Stephen Hemminger
2026-11-12 19:59 ` [PATCH iproute2 09/11] ss: check owner before naming a netlink socket Stephen Hemminger
2026-11-12 19:59 ` [PATCH iproute2 10/11] ss: keep following events after ENOBUFS Stephen Hemminger
2026-11-12 19:59 ` [PATCH iproute2 11/11] ss: report a failed SCTP or MPTCP dump Stephen Hemminger
  -- strict thread matches above, loose matches on Subject: below --
2026-11-09 21:25 [PATCH iproute2 00/11] ss fixes from an OSS scan Stephen Hemminger
2026-11-09 21:25 ` [PATCH iproute2 11/11] ss: report a failed SCTP or MPTCP dump Stephen Hemminger

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox