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 <lorenzo@google.com>
This commit is contained in:
Lorenzo Colitti 2016-09-22 01:02:50 +09:00 committed by Stephen Hemminger
parent 4bfe682536
commit ec75249b14
3 changed files with 75 additions and 2 deletions

View File

@ -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

View File

@ -9,6 +9,7 @@
#define SSF_S_LE 8
#define SSF_S_AUTO 9
#define SSF_DEVCOND 10
#define SSF_MARKMASK 11
#include <stdbool.h>
@ -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);

View File

@ -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");