Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 14 Mar 2013 17:47:54 +0400
From:      "Alexander V. Chernikov" <melifaro@yandex-team.ru>
To:        ipfw@freebsd.org
Cc:        Luigi Rizzo <rizzo@iet.unipi.it>
Subject:   [patch] setting/matching DSCP with ipfw
Message-ID:  <5141D50A.4070702@yandex-team.ru>

Next in thread | Raw E-Mail | Index | Archive | Help
This is a multi-part message in MIME format.
--------------040807080603000200070701
Content-Type: text/plain; charset=ISO-8859-1
Content-Transfer-Encoding: 7bit

Hello list!

This is the obvious thing which should be done at least 5 years ago.
There are several PRs like kern/102471 and kern/121122 with similar
functionality.

Given patch adds setting DSCP support (O_SETDSCP) which works for both
IPv4 and IPv6 packets. Fast checksum recalculation (RFC 1624) is done in
former case. Dscp can be specified by name (AFXY, CSX, BE, EF), by value
(0..63) or via tablearg.

Matching DSCP is done via another opcode (O_DSCP) which accepts several
classes at once (af11,af22,be). Classes are stored in bitmask (2 u32 words).

(Btw, current O_TOS can be modified to transparently match single DSCP
point, probably later on..)

Example:
00050  675  37800 setdscp ef ip from any to 2a02:978:11::/64 dscp be
ipfw add 100 count ip from any to any dscp af11,cs3
00100 count ip from any to any dscp af11,cs3

I'm planning to commit updated patch (docs, some style(9)) on Mon 18 if
there are no objections.

--------------040807080603000200070701
Content-Type: text/plain; charset=UTF-8;
 name="ipfw_dscp.diff"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename="ipfw_dscp.diff"

Index: sys/netpfil/ipfw/ip_fw2.c
===================================================================
--- sys/netpfil/ipfw/ip_fw2.c	(revision 248114)
+++ sys/netpfil/ipfw/ip_fw2.c	(working copy)
@@ -1624,6 +1624,32 @@ do {								\
 				    flags_match(cmd, ip->ip_tos));
 				break;
 
+			case O_DSCP:
+			    {
+				uint32_t *p;
+				uint16_t x;
+
+				p = ((ipfw_insn_u32 *)cmd)->d;
+
+				if (is_ipv4)
+					x = ip->ip_tos >> 2;
+				else if (is_ipv6) {
+					uint8_t *v;
+					v = &((struct ip6_hdr *)ip)->ip6_vfc;
+					x = (*v & 0x0F) << 2;
+					v++;
+					x |= *v >> 6;
+				} else
+					break;
+
+				/* DSCP bitmask is stored as low_u32 high_u32 */
+				if (x > 32)
+					match = *(p + 1) & (1 << (x - 32));
+				else
+					match = *p & (1 << x);
+			    }
+				break;
+
 			case O_TCPDATALEN:
 				if (proto == IPPROTO_TCP && offset == 0) {
 				    struct tcphdr *tcp;
@@ -2353,6 +2379,32 @@ do {								\
 				break;
 		        }
 
+			case O_SETDSCP: {
+				uint16_t code;
+
+				code = IP_FW_ARG_TABLEARG(cmd->arg1) & 0x3F;
+				l = 0;		/* exit inner loop */
+				if (is_ipv4) {
+					uint16_t a;
+
+					a = ip->ip_tos;
+					ip->ip_tos = (code << 2) | (ip->ip_tos & 0x03);
+					a += ntohs(ip->ip_sum) - ip->ip_tos;
+					ip->ip_sum = htons(a);
+				} else if (is_ipv6) {
+					uint8_t *v;
+
+					v = &((struct ip6_hdr *)ip)->ip6_vfc;
+					*v = (*v & 0xF0) | (code >> 2);
+					v++;
+					*v = (*v & 0x3F) | ((code & 0x03) << 6);
+				} else
+					break;
+
+				IPFW_INC_RULE_COUNTER(f, pktlen);
+				break;
+			}
+
 			case O_NAT:
  				if (!IPFW_NAT_LOADED) {
 				    retval = IP_FW_DENY;
Index: sys/netpfil/ipfw/ip_fw_log.c
===================================================================
--- sys/netpfil/ipfw/ip_fw_log.c	(revision 248114)
+++ sys/netpfil/ipfw/ip_fw_log.c	(working copy)
@@ -292,12 +292,10 @@ ipfw_log(struct ip_fw *f, u_int hlen, struct ip_fw
 				altq->qid);
 			cmd += F_LEN(cmd);
 		}
-		if (cmd->opcode == O_PROB)
+		if (cmd->opcode == O_PROB || cmd->opcode == O_TAG ||
+		    cmd->opcode == O_SETDSCP)
 			cmd += F_LEN(cmd);
 
-		if (cmd->opcode == O_TAG)
-			cmd += F_LEN(cmd);
-
 		action = action2;
 		switch (cmd->opcode) {
 		case O_DENY:
Index: sys/netpfil/ipfw/ip_fw_sockopt.c
===================================================================
--- sys/netpfil/ipfw/ip_fw_sockopt.c	(revision 248114)
+++ sys/netpfil/ipfw/ip_fw_sockopt.c	(working copy)
@@ -671,6 +671,10 @@ check_ipfw_struct(struct ip_fw *rule, int size)
 		case O_IPID:
 		case O_IPTTL:
 		case O_IPLEN:
+		case O_DSCP:
+			if (cmdlen != F_INSN_SIZE(ipfw_insn_u32) + 1)
+				goto bad_size;
+			break;
 		case O_TCPDATALEN:
 		case O_TCPWIN:
 		case O_TAGGED:
@@ -738,6 +742,7 @@ check_ipfw_struct(struct ip_fw *rule, int size)
 		case O_ACCEPT:
 		case O_DENY:
 		case O_REJECT:
+		case O_SETDSCP:
 #ifdef INET6
 		case O_UNREACH6:
 #endif
Index: sys/netinet/ip_fw.h
===================================================================
--- sys/netinet/ip_fw.h	(revision 248114)
+++ sys/netinet/ip_fw.h	(working copy)
@@ -218,6 +218,9 @@ enum ipfw_opcodes {		/* arguments (4 byte each)	*/
 
 	O_FORWARD_IP6,		/* fwd sockaddr_in6             */
 
+	O_DSCP,			/* 2 u32 = DSCP mask */
+	O_SETDSCP,		/* arg1=DSCP value */
+
 	O_LAST_OPCODE		/* not an opcode!		*/
 };
 
Index: sbin/ipfw/ipfw2.c
===================================================================
--- sbin/ipfw/ipfw2.c	(revision 248114)
+++ sbin/ipfw/ipfw2.c	(working copy)
@@ -167,6 +167,32 @@ static struct _s_x f_iptos[] = {
 	{ NULL,	0 }
 };
 
+static struct _s_x f_ipdscp[] = {
+	{ "af11", IPTOS_DSCP_AF11 >> 2 },	/* 001010 */
+	{ "af12", IPTOS_DSCP_AF12 >> 2 },	/* 001100 */
+	{ "af13", IPTOS_DSCP_AF13 >> 2 },	/* 001110 */
+	{ "af21", IPTOS_DSCP_AF21 >> 2 },	/* 010010 */
+	{ "af22", IPTOS_DSCP_AF22 >> 2 },	/* 010100 */
+	{ "af23", IPTOS_DSCP_AF23 >> 2 },	/* 010110 */
+	{ "af31", IPTOS_DSCP_AF31 >> 2 },	/* 011010 */
+	{ "af32", IPTOS_DSCP_AF32 >> 2 },	/* 011100 */
+	{ "af33", IPTOS_DSCP_AF33 >> 2 },	/* 011110 */
+	{ "af41", IPTOS_DSCP_AF41 >> 2 },	/* 100010 */
+	{ "af42", IPTOS_DSCP_AF42 >> 2 },	/* 100100 */
+	{ "af43", IPTOS_DSCP_AF43 >> 2 },	/* 100110 */
+	{ "be", IPTOS_DSCP_CS0 >> 2 }, 	/* 000000 */
+	{ "ef", IPTOS_DSCP_EF >> 2 },	/* 101110 */
+	{ "cs0", IPTOS_DSCP_CS0 >> 2 },	/* 000000 */
+	{ "cs1", IPTOS_DSCP_CS1 >> 2 },	/* 001000 */
+	{ "cs2", IPTOS_DSCP_CS2 >> 2 },	/* 010000 */
+	{ "cs3", IPTOS_DSCP_CS3 >> 2 },	/* 011000 */
+	{ "cs4", IPTOS_DSCP_CS4 >> 2 },	/* 100000 */
+	{ "cs5", IPTOS_DSCP_CS5 >> 2 },	/* 101000 */
+	{ "cs6", IPTOS_DSCP_CS6 >> 2 },	/* 110000 */
+	{ "cs7", IPTOS_DSCP_CS7 >> 2 },	/* 100000 */
+	{ NULL, 0 }
+};
+
 static struct _s_x limit_masks[] = {
 	{"all",		DYN_SRC_ADDR|DYN_SRC_PORT|DYN_DST_ADDR|DYN_DST_PORT},
 	{"src-addr",	DYN_SRC_ADDR},
@@ -237,6 +263,7 @@ static struct _s_x rule_actions[] = {
 	{ "nat",		TOK_NAT },
 	{ "reass",		TOK_REASS },
 	{ "setfib",		TOK_SETFIB },
+	{ "setdscp",		TOK_SETDSCP },
 	{ "call",		TOK_CALL },
 	{ "return",		TOK_RETURN },
 	{ NULL, 0 }	/* terminator */
@@ -714,6 +741,51 @@ fill_newports(ipfw_insn_u16 *cmd, char *av, int pr
 	return (i);
 }
 
+/*
+ * Fill the body of the command with the list of DiffServ codepoints.
+ */
+static void
+fill_dscp(ipfw_insn *cmd, char *av, int cblen)
+{
+	uint32_t *low, *high;
+	char *s = av, *a;
+	int code;
+
+	cmd->opcode = O_DSCP;
+	cmd->len |= F_INSN_SIZE(ipfw_insn_u32) + 1;
+
+	CHECK_CMDLEN;
+
+	low = (uint32_t *)(cmd + 1);
+	high = low + 1;
+
+	*low = 0;
+	*high = 0;
+
+	while (s != NULL) {
+		a = strchr(s, ',');
+
+		if (a != NULL)
+			*a++ = '\0';
+
+		if (isalpha(*s)) {
+			if ((code = match_token(f_ipdscp, s)) == -1)
+				errx(EX_DATAERR, "Unknown DSCP code");
+		} else {
+			code = strtoul(s, NULL, 10);
+			if (code < 0 || code > 63)
+				errx(EX_DATAERR, "Invalid DSCP value");
+		}
+
+		if (code > 32)
+			*high |= 1 << (code - 32);
+		else
+			*low |= 1 << code;
+
+		s = a;
+	}
+}
+
 static struct _s_x icmpcodes[] = {
       { "net",			ICMP_UNREACH_NET },
       { "host",			ICMP_UNREACH_HOST },
@@ -972,6 +1044,32 @@ print_icmptypes(ipfw_insn_u32 *cmd)
 	}
 }
 
+static void
+print_dscp(ipfw_insn_u32 *cmd)
+{
+	int i, c;
+	uint32_t *v;
+	char sep= ' ';
+	const char *code;
+
+	printf(" dscp");
+	i = 0;
+	c = 0;
+	v = cmd->d;
+	while (i < 64) {
+		if (*v & (1 << i)) {
+			if ((code = match_value(f_ipdscp, i)) != NULL)
+				printf("%c%s", sep, code);
+			else
+				printf("%c%d", sep, i);
+			sep = ',';
+		}
+
+		if ((++i % 32) == 0)
+			v++;
+	}
+}
+
 /*
  * show_ipfw() prints the body of an ipfw rule.
  * Because the standard rule has at least proto src_ip dst_ip, we use
@@ -1205,6 +1303,17 @@ show_ipfw(struct ip_fw *rule, int pcwidth, int bcw
 			PRINT_UINT_ARG("setfib ", cmd->arg1);
  			break;
 
+		case O_SETDSCP:
+		    {
+			const char *code;
+
+			if ((code = match_value(f_ipdscp, cmd->arg1)) != NULL)
+				printf("setdscp %s", code);
+			else
+				PRINT_UINT_ARG("setdscp ", cmd->arg1);
+		    }
+ 			break;
+
 		case O_REASS:
 			printf("reass");
 			break;
@@ -1500,6 +1609,10 @@ show_ipfw(struct ip_fw *rule, int pcwidth, int bcw
 				printf(" ipprecedence %u", (cmd->arg1) >> 5 );
 				break;
 
+			case O_DSCP:
+				print_dscp((ipfw_insn_u32 *)cmd);
+	 			break;
+
 			case O_IPLEN:
 				if (F_LEN(cmd) == 1)
 				    printf(" iplen %u", cmd->arg1 );
@@ -3036,6 +3149,24 @@ chkarg:
 		break;
 	    }
 
+	case TOK_SETDSCP:
+	    {
+		int code;
+
+		action->opcode = O_SETDSCP;
+		NEED1("missing DSCP code");
+		if (_substrcmp(*av, "tablearg") == 0) {
+			action->arg1 = IP_FW_TABLEARG;
+		} else if (isalpha(*av[0])) {
+			if ((code = match_token(f_ipdscp, *av)) == -1)
+				errx(EX_DATAERR, "Unknown DSCP code");
+			action->arg1 = code;
+		} else
+		        action->arg1 = strtoul(*av, NULL, 10);
+		av++;
+		break;
+	    }
+
 	case TOK_REASS:
 		action->opcode = O_REASS;
 		break;
@@ -3448,6 +3579,12 @@ read_options:
 			av++;
 			break;
 
+		case TOK_DSCP:
+			NEED1("missing DSCP code");
+			fill_dscp(cmd, *av, cblen);
+			av++;
+			break;
+
 		case TOK_IPOPTS:
 			NEED1("missing argument for ipoptions");
 			fill_flags(cmd, O_IPOPT, f_ipopts, *av);
Index: sbin/ipfw/ipfw2.h
===================================================================
--- sbin/ipfw/ipfw2.h	(revision 248114)
+++ sbin/ipfw/ipfw2.h	(working copy)
@@ -203,6 +203,7 @@ enum tokens {
 	TOK_SETFIB,
 	TOK_LOOKUP,
 	TOK_SOCKARG,
+	TOK_SETDSCP,
 };
 /*
  * the following macro returns an error message if we run out of

--------------040807080603000200070701--



Want to link to this message? Use this URL: <http://docs.FreeBSD.org/cgi/mid.cgi?5141D50A.4070702>