This allows the user to dump sockets with a given mark (via
"fwmark = 0x1234/0x1234" or "fwmark = 12345", etc.) , and to
display the socket marks of dumped sockets.
The relevant kernel commits are: d545caca827b ("net: inet: diag:
expose the socket mark to privileged processes.") and
- a52e95abf772 ("net: diag: allow socket bytecode filters to
match socket marks")
Signed-off-by: Lorenzo Colitti
---
include/linux/inet_diag.h | 7 +++
misc/ss.c | 52 +++
misc/ssfilter.h | 2 ++
misc/ssfilter.y | 23 +++--
4 files changed, 82 insertions(+), 2 deletions(-)
diff --git a/include/linux/inet_diag.h b/include/linux/inet_diag.h
index beb74ee..016de88 100644
--- a/include/linux/inet_diag.h
+++ b/include/linux/inet_diag.h
@@ -73,6 +73,7 @@ enum {
INET_DIAG_BC_S_COND,
INET_DIAG_BC_D_COND,
INET_DIAG_BC_DEV_COND, /* u32 ifindex */
+ INET_DIAG_BC_MARK_COND,
};
struct inet_diag_hostcond {
@@ -82,6 +83,11 @@ struct inet_diag_hostcond {
__be32 addr[0];
};
+struct inet_diag_markcond {
+ __u32 mark;
+ __u32 mask;
+};
+
/* Base info structure. It contains socket identity (addrs/ports/cookie)
* and, alas, the information shown by netstat. */
struct inet_diag_msg {
@@ -117,6 +123,7 @@ enum {
INET_DIAG_LOCALS,
INET_DIAG_PEERS,
INET_DIAG_PAD,
+ INET_DIAG_MARK,
__INET_DIAG_MAX,
};
diff --git a/misc/ss.c b/misc/ss.c
index 3b268d9..83fb01f 100644
--- a/misc/ss.c
+++ b/misc/ss.c
@@ -737,6 +737,7 @@ struct sockstat {
unsigned long long sk;
char *name;
char *peer_name;
+ __u32 mark;
};
struct dctcpstat {
@@ -807,6 +808,9 @@ static void sock_details_print(struct sockstat *s)
printf(" ino:%u", s->ino);
printf(" sk:%llx", s->sk);
+
+ if (s->mark)
+ printf(" fwmark:0x%x", s->mark);
}
static void sock_addr_print_width(int addr_len, const char *addr, char *delim,
@@ -1046,6 +1050,8 @@ struct aafilter {
inet_prefix addr;
int port;
unsigned intiface;
+ __u32 mark;
+ __u32 mask;
struct aafilter *next;
};
@@ -1166,6 +1172,12 @@ static int run_ssfilter(struct ssfilter *f, struct
sockstat *s)
return s->iface == a->iface;
}
+ case SSF_MARKMASK:
+ {
+ struct aafilter *a = (void *)f->pred;
+
+ return (s->mark & a->mask) == a->mark;
+ }
/* Yup. It is recursion. Sorry. */
case SSF_AND:
return run_ssfilter(f->pred, s) && run_ssfilter(f->post, s);
@@ -1341,6 +1353,23 @@ static int ssfilter_bytecompile(struct ssfilter *f, char
**bytecode)
/* bytecompile for SSF_DEVCOND not supported yet */
return 0;
}
+ case SSF_MARKMASK:
+ {
+ struct aafilter *a = (void *)f->pred;
+ struct instr {
+ struct inet_diag_bc_op op;
+ struct inet_diag_markcond cond;
+ };
+ int inslen = sizeof(struct instr);
+
+ if (!(*bytecode = malloc(inslen))) abort();
+ ((struct instr *)*bytecode)[0] = (struct instr) {
+ { INET_DIAG_BC_MARK_COND, inslen, inslen + 4 },
+ { a->mark, a->mask},
+ };
+
+ return inslen;
+ }
default:
abort();
}
@@ -1620,6 +1649,25 @@ out:
return res;
}
+void *parse_markmask(const char *markmask)
+{
+ struct aafilter a, *res;
+
+ if (strchr(markmask, '/')) {
+ if (sscanf(markmask, "%i/%i", &a.mark, &a.mask) != 2)
+ return NULL;
+ } else {
+ a.mask = 0x;
+ if (sscanf(markmask, "%i", &a.mark) != 1)
+ return NULL;
+ }
+
+ res = malloc(sizeof(*res));
+ if (res)
+ memcpy(res, &a, sizeof(a));
+ return res;
+}
+
static char *proto_name(int protocol)
{
switch (protocol) {
@@ -2107,6 +2155,10 @@ static void parse_diag_msg(struct nlmsghdr *nlh, struct
sockstat *s)
s->iface= r->id.idiag_if;
s->sk = cookie_sk_get(&r->id.idiag_cookie[0]);
+ s->mark = 0;
+ if (tb[INET_DIAG_MARK])
+ s->mark = *(__u32 *) RTA_DATA(tb[INET_DIAG_MARK]);
+
if (s->local.family == AF_INET)
s->local.bytelen = s->remote.bytelen = 4;
else
diff --git a/misc/ssfilter.h b/misc/ssfilter.h
index c7db8ee..dfc5b93 100644
--- a/misc/ssfilter.h
+++ b/misc/ssfilter.h
@@ -9,6 +9,7 @@
#define SSF_S_LE 8
#define SSF_S_AUTO 9
#define SSF_DEVCOND 10
+#define SSF_MARKMASK 11
#include
@@ -22,3 +23,4 @@ struct ssfilter
int ssfilter_parse(struct