Subject: [RFC 2/3] [RFC iproute2-next] tc: f_u32: Fix the ipv6 pretty printing.

From: Anton Danilov <[email-masked]>
Date: Tue, 8 Jun 2021 18:33:08 +0300

Unexpectedly the print_ipv6 function is the copy-paste version of the print_ipv4 function. Fixed.

Before patch:

$ sudo tc -p -s f ls dev dummy0
filter parent 1: protocol ipv6 pref 30 u32 chain 0
filter parent 1: protocol ipv6 pref 30 u32 chain 0 fh 800: ht divisor 1
filter parent 1: protocol ipv6 pref 30 u32 chain 0 fh 800::800 order 2048 key ht 800 bkt 0 flowid 1:1 not_in_hw  (rule hit 0 success 0) (success 0 )
  match IP src 64.0.0.0/32 (success 0 )
  match IP dst 0.0.0.0/32 (success 0 )
  match sport 0, match dport 0 (success 0 )
filter parent 1: protocol ipv6 pref 30 u32 chain 0 fh 800::801 order 2049 key ht 800 bkt 0 flowid 1:1 not_in_hw  (rule hit 0 success 0) (success 0 )  (success 0 )  (success 0 )  (success 0 )
filter parent 1: protocol ipv6 pref 30 u32 chain 0 fh 800::802 order 2050 key ht 800 bkt 0 flowid 1:1 not_in_hw  (rule hit 0 success 0) (success 0 )
  match IP src 0.204.0.221/32 (success 0 )
  match IP dst 0.0.0.0/1 (success 0 )  (success 0 )  (success 0 )  (success 0 )
filter parent 1: protocol ipv6 pref 30 u32 chain 0 fh 800::803 order 2051 key ht 800 bkt 0 flowid 1:1 not_in_hw  (rule hit 0 success 0) (success 0 )
filter parent 1: protocol ipv6 pref 30 u32 chain 0 fh 800::804 order 2052 key ht 800 bkt 0 flowid 1:1 not_in_hw  (rule hit 0 success 0) (success 0 )
filter parent 1: protocol ipv6 pref 30 u32 chain 0 fh 800::805 order 2053 key ht 800 bkt 0 flowid 1:1 not_in_hw  (rule hit 0 success 0)
  match IP ihl 1 (success 0 )
filter parent 1: protocol ipv6 pref 30 u32 chain 0 fh 800::806 order 2054 key ht 800 bkt 0 flowid 1:1 not_in_hw  (rule hit 0 success 0) (success 0 )
filter parent 1: protocol ipv6 pref 30 u32 chain 0 fh 800::807 order 2055 key ht 800 bkt 0 flowid 1:1 not_in_hw  (rule hit 0 success 0) (success 0 )

After patch:

$ sudo tc -p -s f ls dev dummy0
filter parent 1: protocol ipv6 pref 30 u32 chain 0
filter parent 1: protocol ipv6 pref 30 u32 chain 0 fh 800: ht divisor 1
filter parent 1: protocol ipv6 pref 30 u32 chain 0 fh 800::800 order 2048 key ht 800 bkt 0 flowid 1:1 not_in_hw  (rule hit 0 success 0)
 match ipv6 src 2a02:26e0:4000::/128
filter parent 1: protocol ipv6 pref 30 u32 chain 0 fh 800::801 order 2049 key ht 800 bkt 0 flowid 1:1 not_in_hw  (rule hit 0 success 0)
 match ipv6 dst 2a02:26e0:4000::/128
filter parent 1: protocol ipv6 pref 30 u32 chain 0 fh 800::802 order 2050 key ht 800 bkt 0 flowid 1:1 not_in_hw  (rule hit 0 success 0)
 match ipv6 src aa:bb:cc:dd::/65
 match ipv6 dst aa:bb:cc:dd::/66
filter parent 1: protocol ipv6 pref 30 u32 chain 0 fh 800::803 order 2051 key ht 800 bkt 0 flowid 1:1 not_in_hw  (rule hit 0 success 0)
 match ipv6 class 0xff 0xff (success 0 )
filter parent 1: protocol ipv6 pref 30 u32 chain 0 fh 800::804 order 2052 key ht 800 bkt 0 flowid 1:1 not_in_hw  (rule hit 0 success 0)
 match ipv6 class 0x10 0xff (success 0 )
filter parent 1: protocol ipv6 pref 30 u32 chain 0 fh 800::805 order 2053 key ht 800 bkt 0 flowid 1:1 not_in_hw  (rule hit 0 success 0)
 match ipv6 class 0x10 0xf0 (success 0 )
filter parent 1: protocol ipv6 pref 30 u32 chain 0 fh 800::806 order 2054 key ht 800 bkt 0 flowid 1:1 not_in_hw  (rule hit 0 success 0)
 match ipv6 flowlabel 0xbcde 0xffff (success 0 )
filter parent 1: protocol ipv6 pref 30 u32 chain 0 fh 800::807 order 2055 key ht 800 bkt 0 flowid 1:1 not_in_hw  (rule hit 0 success 0)
 match ipv6 class 0x12 0xff
 match ipv6 flowlabel 0xbcde 0xffff (success 0 )

Signed-off-by: Anton Danilov <[email-masked]>
---
 tc/f_u32.c | 152 +++++++++++++++++++++++++++++------------------------
 1 file changed, 82 insertions(+), 70 deletions(-)

diff --git a/tc/f_u32.c b/tc/f_u32.c
index 3fd3eb17..d7beb586 100644
--- a/tc/f_u32.c
+++ b/tc/f_u32.c
@@ -820,28 +820,28 @@ static int parse_hashkey(int *argc_p, char ***argv_p, struct tc_u32_sel *sel)
 	return 0;
 }
 
-static void print_ipv4(FILE *f, const struct tc_u32_key *key)
+static int print_ipv4(FILE *f, const struct tc_u32_key *key)
 {
 	char abuf[256];
 
+	if (key == NULL)
+		return 0;
+
 	switch (key->off) {
 	case 0:
 		switch (ntohl(key->mask)) {
 		case 0x0f000000:
-			fprintf(f, "\n  match IP ihl %u",
+			return fprintf(f, "\n  match IP ihl %u",
 				ntohl(key->val) >> 24);
-			return;
 		case 0x00ff0000:
-			fprintf(f, "\n  match IP dsfield %#x",
+			return fprintf(f, "\n  match IP dsfield %#x",
 				ntohl(key->val) >> 16);
-			return;
 		}
 		break;
 	case 8:
 		if (ntohl(key->mask) == 0x00ff0000) {
-			fprintf(f, "\n  match IP protocol %d",
+			return fprintf(f, "\n  match IP protocol %d",
 				ntohl(key->val) >> 16);
-			return;
 		}
 		break;
 	case 12:
@@ -849,12 +849,11 @@ static void print_ipv4(FILE *f, const struct tc_u32_key *key)
 			int bits = mask2bits(key->mask);
 
 			if (bits >= 0) {
-				fprintf(f, "\n  %s %s/%d",
+				return fprintf(f, "\n  %s %s/%d",
 					key->off == 12 ? "match IP src" : "match IP dst",
 					inet_ntop(AF_INET, &key->val,
 						  abuf, sizeof(abuf)),
 					bits);
-				return;
 			}
 		}
 		break;
@@ -862,87 +861,100 @@ static void print_ipv4(FILE *f, const struct tc_u32_key *key)
 	case 20:
 		switch (ntohl(key->mask)) {
 		case 0x0000ffff:
-			fprintf(f, "\n  match dport %u",
+			return fprintf(f, "\n  match dport %u",
 				ntohl(key->val) & 0xffff);
-			return;
 		case 0xffff0000:
-			fprintf(f, "\n  match sport %u",
+			return fprintf(f, "\n  match sport %u",
 				ntohl(key->val) >> 16);
-			return;
 		case 0xffffffff:
-			fprintf(f, "\n  match dport %u, match sport %u",
+			return fprintf(f, "\n  match dport %u, match sport %u",
 				ntohl(key->val) & 0xffff,
 				ntohl(key->val) >> 16);
 
-			return;
 		}
 		/* XXX: Default print_raw */
 	}
+
+	return 0;
 }
 
-static void print_ipv6(FILE *f, const struct tc_u32_key *key)
+static int print_ipv6(FILE *f, const struct tc_u32_key *key)
 {
 	char abuf[256];
 
+	static __u32 ipv6_src_addr[4];
+	static int ipv6_src_plen;
+	static __u32 ipv6_dst_addr[4];
+	static int ipv6_dst_plen;
+
+	int ret = 0;
+
+	if ((key == NULL || key->off >= 24) && ipv6_src_plen > 0) {
+		ret = fprintf(f, "\n match ipv6 src %s/%d",
+			inet_ntop(AF_INET6, ipv6_src_addr, abuf, sizeof(abuf)), ipv6_src_plen);
+		memset(ipv6_src_addr, 0, 16);
+		ipv6_src_plen = 0;
+	}
+
+	if (key == NULL && ipv6_dst_plen > 0) {
+		ret += fprintf(f, "\n match ipv6 dst %s/%d",
+			inet_ntop(AF_INET6, ipv6_dst_addr, abuf, sizeof(abuf)), ipv6_dst_plen);
+		memset(ipv6_dst_addr, 0, 16);
+		ipv6_dst_plen = 0;
+	}
+
+	if (key == NULL)
+		return ret;
+
 	switch (key->off) {
 	case 0:
-		switch (ntohl(key->mask)) {
-		case 0x0f000000:
-			fprintf(f, "\n  match IP ihl %u",
-				ntohl(key->val) >> 24);
-			return;
-		case 0x00ff0000:
-			fprintf(f, "\n  match IP dsfield %#x",
-				ntohl(key->val) >> 16);
-			return;
+		if (ntohl(key->mask) & 0x0ff00000) {
+			ret = fprintf(f, "\n match ipv6 class %#x %#x",
+				(ntohl(key->val) & 0x0ff00000) >> 20,
+				(ntohl(key->mask) & 0x0ff00000) >> 20);
+		}
+		if (ntohl(key->mask) & 0x000fffff) {
+			ret += fprintf(f, "\n match ipv6 flowlabel %#x %#x",
+				ntohl(key->val) & 0x000fffff,
+				ntohl(key->mask) & 0x000fffff);
 		}
+		return ret;
 		break;
-	case 8:
-		if (ntohl(key->mask) == 0x00ff0000) {
-			fprintf(f, "\n  match IP protocol %d",
-				ntohl(key->val) >> 16);
-			return;
+	case 4:
+		if (ntohl(key->mask) == 0x0000ff00) {
+			return fprintf(f, "\n  match ipv6 protocol %d",
+				ntohl(key->val) >> 8);
 		}
 		break;
+	case 8:
 	case 12:
-	case 16: {
-			int bits = mask2bits(key->mask);
-
-			if (bits >= 0) {
-				fprintf(f, "\n  %s %s/%d",
-					key->off == 12 ? "match IP src" : "match IP dst",
-					inet_ntop(AF_INET, &key->val,
-						  abuf, sizeof(abuf)),
-					bits);
-				return;
-			}
+	case 16:
+	case 20: {
+			ipv6_src_plen += mask2bits(key->mask);
+			ipv6_src_addr[(key->off - 8) / 4] = key->val;
 		}
 		break;
-
-	case 20:
-		switch (ntohl(key->mask)) {
-		case 0x0000ffff:
-			fprintf(f, "\n  match sport %u",
-				ntohl(key->val) & 0xffff);
-			return;
-		case 0xffff0000:
-			fprintf(f, "\n  match dport %u",
-				ntohl(key->val) >> 16);
-			return;
-		case 0xffffffff:
-			fprintf(f, "\n  match sport %u, match dport %u",
-				ntohl(key->val) & 0xffff,
-				ntohl(key->val) >> 16);
-
-			return;
+	case 24:
+	case 28:
+	case 32:
+	case 36: {
+			ipv6_dst_plen += mask2bits(key->mask);
+			ipv6_dst_addr[(key->off - 24) / 4] = key->val;
 		}
+		break;
+
 		/* XXX: Default print_raw */
 	}
+
+	return 0;
 }
 
-static void print_raw(FILE *f, const struct tc_u32_key *key)
+static int print_raw(FILE *f, const struct tc_u32_key *key)
 {
-	fprintf(f, "\n  match %08x/%08x at %s%d",
+	if (key == NULL)
+		return 0;
+
+	return fprintf(f, "\n  match %08x/%08x at %s%d",
 		(unsigned int)ntohl(key->val),
 		(unsigned int)ntohl(key->mask),
 		key->offmask ? "nexthdr+" : "",
@@ -952,14 +964,14 @@ static void print_raw(FILE *f, const struct tc_u32_key *key)
 static const struct {
 	__u16 proto;
 	__u16 pad;
-	void (*pprinter)(FILE *f, const struct tc_u32_key *key);
+	int (*pprinter)(FILE *f, const struct tc_u32_key *key);
 } u32_pprinters[] = {
 	{0,	   0, print_raw},
 	{ETH_P_IP, 0, print_ipv4},
 	{ETH_P_IPV6, 0, print_ipv6},
 };
 
-static void show_keys(FILE *f, const struct tc_u32_key *key)
+static int show_keys(FILE *f, const struct tc_u32_key *key)
 {
 	int i = 0;
 
@@ -969,8 +981,7 @@ static void show_keys(FILE *f, const struct tc_u32_key *key)
 	for (i = 0; i < ARRAY_SIZE(u32_pprinters); i++) {
 		if (u32_pprinters[i].proto == ntohs(f_proto)) {
 show_k:
-			u32_pprinters[i].pprinter(f, key);
-			return;
+			return u32_pprinters[i].pprinter(f, key);
 		}
 	}
 
@@ -1289,12 +1300,13 @@ static int u32_print_opt(struct filter_util *qu, FILE *f, struct rtattr *opt,
 	if (sel) {
 		if (sel->nkeys) {
 			int i;
-
-			for (i = 0; i < sel->nkeys; i++) {
-				show_keys(f, sel->keys + i);
-				if (show_stats && NULL != pf)
-					fprintf(f, " (success %llu ) ",
-						(unsigned long long) pf->kcnts[i]);
+			// The ipv6 pretty printing requires an additional call at the end.
+			// Call the show_keys with the NULL value after call with last key.
+			for (i = 0; i <= sel->nkeys; i++) {
+				if (show_keys(f, i < sel->nkeys ? sel->keys + i : NULL))
+					if (i < sel->nkeys && show_stats && NULL != pf)
+						fprintf(f, " (success %llu ) ",
+							(unsigned long long) pf->kcnts[i]);
 			}
 		}
 
-- 
2.20.1