From: Lorenzo Colitti Date: Wed, 21 Sep 2016 16:02:50 +0000 (+0900) Subject: ss: Support displaying and filtering on socket marks. X-Git-Tag: v4.9.0~77 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=ec75249b141e99b978e6494345d468595f5b829f;p=thirdparty%2Fiproute2.git ss: Support displaying and filtering on socket marks. 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 --- diff --git a/misc/ss.c b/misc/ss.c index a36ad5af8..7f8453418 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 { @@ -808,6 +809,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, @@ -1047,6 +1051,8 @@ struct aafilter { inet_prefix addr; int port; unsigned int iface; + __u32 mark; + __u32 mask; struct aafilter *next; }; @@ -1166,6 +1172,12 @@ static int run_ssfilter(struct ssfilter *f, struct sockstat *s) struct aafilter *a = (void *)f->pred; 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: @@ -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(); @@ -1621,6 +1650,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 = 0xffffffff; + 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) { @@ -2138,6 +2186,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 c7db8eee9..dfc5b938c 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 ssfilter **f, int argc, char **argv, FILE *fp); void *parse_hostcond(char *addr, bool is_port); void *parse_devcond(char *name); +void *parse_markmask(const char *markmask); diff --git a/misc/ssfilter.y b/misc/ssfilter.y index 14bf9817f..ba82b65f7 100644 --- a/misc/ssfilter.y +++ b/misc/ssfilter.y @@ -36,7 +36,7 @@ static void yyerror(char *s) %} -%token HOSTCOND DCOND SCOND DPORT SPORT LEQ GEQ NEQ AUTOBOUND DEVCOND DEVNAME +%token HOSTCOND DCOND SCOND DPORT SPORT LEQ GEQ NEQ AUTOBOUND DEVCOND DEVNAME MARKMASK FWMARK %left '|' %left '&' %nonassoc '!' @@ -116,7 +116,14 @@ expr: DCOND HOSTCOND { $$ = alloc_node(SSF_NOT, alloc_node(SSF_DEVCOND, $3)); } - + | FWMARK '=' MARKMASK + { + $$ = alloc_node(SSF_MARKMASK, $3); + } + | FWMARK NEQ MARKMASK + { + $$ = alloc_node(SSF_NOT, alloc_node(SSF_MARKMASK, $3)); + } | AUTOBOUND { $$ = alloc_node(SSF_S_AUTO, NULL); @@ -249,6 +256,10 @@ int yylex(void) tok_type = DEVNAME; return DEVNAME; } + if (strcmp(curtok, "fwmark") == 0) { + tok_type = FWMARK; + return FWMARK; + } if (strcmp(curtok, ">=") == 0 || strcmp(curtok, "ge") == 0 || strcmp(curtok, "geq") == 0) @@ -283,6 +294,14 @@ int yylex(void) } return DEVCOND; } + if (tok_type == FWMARK) { + yylval = (void*)parse_markmask(curtok); + if (yylval == NULL) { + fprintf(stderr, "Cannot parse mark %s.\n", curtok); + exit(1); + } + return MARKMASK; + } yylval = (void*)parse_hostcond(curtok, tok_type == SPORT || tok_type == DPORT); if (yylval == NULL) { fprintf(stderr, "Cannot parse dst/src address.\n");