ss: Support displaying and filtering on socket marks.
am: d4db01bf3f

Change-Id: Ic786233b6864b4510778813d76155de258a46406
diff --git a/include/linux/inet_diag.h b/include/linux/inet_diag.h
index 1db4116..016de88 100644
--- a/include/linux/inet_diag.h
+++ b/include/linux/inet_diag.h
@@ -72,6 +72,8 @@
 	INET_DIAG_BC_AUTO,
 	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 {
@@ -81,6 +83,11 @@
 	__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 {
@@ -113,9 +120,14 @@
 	INET_DIAG_DCTCPINFO,
 	INET_DIAG_PROTOCOL,  /* response attribute only */
 	INET_DIAG_SKV6ONLY,
+	INET_DIAG_LOCALS,
+	INET_DIAG_PEERS,
+	INET_DIAG_PAD,
+	INET_DIAG_MARK,
+	__INET_DIAG_MAX,
 };
 
-#define INET_DIAG_MAX INET_DIAG_SKV6ONLY
+#define INET_DIAG_MAX (__INET_DIAG_MAX - 1)
 
 /* INET_DIAG_MEM */
 
diff --git a/misc/ss.c b/misc/ss.c
index 13fcc8f..12a343c 100644
--- a/misc/ss.c
+++ b/misc/ss.c
@@ -738,6 +738,7 @@
 	unsigned long long  sk;
 	char *name;
 	char *peer_name;
+	__u32		    mark;
 };
 
 struct dctcpstat
@@ -806,6 +807,9 @@
 
 	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,
@@ -1043,6 +1047,8 @@
 {
 	inet_prefix	addr;
 	int		port;
+	__u32		mark;
+	__u32		mask;
 	struct aafilter *next;
 };
 
@@ -1148,7 +1154,12 @@
 		struct aafilter *a = (void*)f->pred;
 		return s->lport <= a->port;
 	}
+		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);
@@ -1297,6 +1308,23 @@
 		*bytecode = a;
 		return l1+4;
 	}
+		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();
 	}
@@ -1546,6 +1574,25 @@
 	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) {
@@ -2015,6 +2062,10 @@
 	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 53922a8..c7293cc 100644
--- a/misc/ssfilter.h
+++ b/misc/ssfilter.h
@@ -8,6 +8,7 @@
 #define SSF_S_GE  7
 #define SSF_S_LE  8
 #define SSF_S_AUTO  9
+#define SSF_MARKMASK 11
 
 #include <stdbool.h>
 
@@ -20,3 +21,4 @@
 
 int ssfilter_parse(struct ssfilter **f, int argc, char **argv, FILE *fp);
 void *parse_hostcond(char *addr, bool is_port);
+void *parse_markmask(const char *markmask);
diff --git a/misc/ssfilter.y b/misc/ssfilter.y
index 9906ad8..ba6b06f 100644
--- a/misc/ssfilter.y
+++ b/misc/ssfilter.y
@@ -39,7 +39,7 @@
 
 %}
 
-%token HOSTCOND DCOND SCOND DPORT SPORT LEQ GEQ NEQ AUTOBOUND
+%token HOSTCOND DCOND SCOND DPORT SPORT LEQ GEQ NEQ AUTOBOUND MARKMASK FWMARK
 %left '|'
 %left '&'
 %nonassoc '!'
@@ -111,7 +111,14 @@
         {
 		$$ = alloc_node(SSF_NOT, alloc_node(SSF_SCOND, $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);
@@ -240,6 +247,10 @@
 		tok_type = SPORT;
 		return SPORT;
 	}
+	if (strcmp(curtok, "fwmark") == 0) {
+		tok_type = FWMARK;
+		return FWMARK;
+	}
 	if (strcmp(curtok, ">=") == 0 ||
 	    strcmp(curtok, "ge") == 0 ||
 	    strcmp(curtok, "geq") == 0)
@@ -266,6 +277,14 @@
 		tok_type = AUTOBOUND;
 		return AUTOBOUND;
 	}
+	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");