[Midnightbsd-cvs] src [9405] trunk: add ipfw support for setting/matching diffserv codepoints (DSCP).

laffer1 at midnightbsd.org laffer1 at midnightbsd.org
Sat Mar 4 16:36:45 EST 2017


Revision: 9405
          http://svnweb.midnightbsd.org/src/?rev=9405
Author:   laffer1
Date:     2017-03-04 16:36:44 -0500 (Sat, 04 Mar 2017)
Log Message:
-----------
add ipfw support for setting/matching diffserv codepoints (DSCP).

Setting DSCP support is done via O_SETDSCP which works for both IPv4 and IPv6 packets.

Modified Paths:
--------------
    trunk/sbin/ipfw/ipfw.8
    trunk/sbin/ipfw/ipfw2.c
    trunk/sbin/ipfw/ipfw2.h
    trunk/sys/netinet/ip_fw.h
    trunk/sys/netpfil/ipfw/ip_fw2.c
    trunk/sys/netpfil/ipfw/ip_fw_log.c
    trunk/sys/netpfil/ipfw/ip_fw_sockopt.c

Modified: trunk/sbin/ipfw/ipfw.8
===================================================================
--- trunk/sbin/ipfw/ipfw.8	2017-03-04 21:35:16 UTC (rev 9404)
+++ trunk/sbin/ipfw/ipfw.8	2017-03-04 21:36:44 UTC (rev 9405)
@@ -948,6 +948,61 @@
 It is possible to use the
 .Cm tablearg
 keyword with a setfib. If tablearg value is not within compiled FIB range packet fib is set to 0.
+.It Cm setdscp Ar DSCP | number | tablearg
+Set specified DiffServ codepoint for an IPv4/IPv6 packet.
+Processing continues at the next rule.
+Supported values are:
+.Pp
+.Cm CS0
+.Pq Dv 000000 ,
+.Cm CS1
+.Pq Dv 001000 ,
+.Cm CS2
+.Pq Dv 010000 ,
+.Cm CS3
+.Pq Dv 011000 ,
+.Cm CS4
+.Pq Dv 100000 ,
+.Cm CS5
+.Pq Dv 101000 ,
+.Cm CS6
+.Pq Dv 110000 ,
+.Cm CS7
+.Pq Dv 111000 ,
+.Cm AF11
+.Pq Dv 001010 ,
+.Cm AF12
+.Pq Dv 001100 ,
+.Cm AF13
+.Pq Dv 001110 ,
+.Cm AF21
+.Pq Dv 010010 ,
+.Cm AF22
+.Pq Dv 010100 ,
+.Cm AF23
+.Pq Dv 010110 ,
+.Cm AF31
+.Pq Dv 011010 ,
+.Cm AF32
+.Pq Dv 011100 ,
+.Cm AF33
+.Pq Dv 011110 ,
+.Cm AF41
+.Pq Dv 100010 ,
+.Cm AF42
+.Pq Dv 100100 ,
+.Cm AF43
+.Pq Dv 100110 ,
+.Cm EF
+.Pq Dv 101110 ,
+.Cm BE
+.Pq Dv 000000 .
+Additionally, DSCP value can be specified by number (0..64).
+It is also possible to use the
+.Cm tablearg
+keyword with setdscp.
+If the tablearg value is not within the 0..64 range, lower 6 bits of supplied
+value are used.
 .It Cm reass
 Queue and reassemble ip fragments.
 If the packet is not fragmented, counters are updated and processing continues with the next rule.
@@ -1436,6 +1491,17 @@
 The absence of a particular type may be denoted
 with a
 .Ql \&! .
+.It Cm dscp spec Ns Op , Ns Ar spec
+Matches IPv4/IPv6 packets whose
+.Cm DS
+field value is contained in
+.Ar spec
+mask.
+Multiple values can be specified via 
+the comma separated list.
+Value can be one of keywords used in
+.Cm setdscp
+action or exact number.
 .It Cm ipttl Ar ttl-list
 Matches IPv4 packets whose time to live is included in
 .Ar ttl-list ,
@@ -2944,6 +3010,23 @@
 but coming in on
 .Li fxp1
 would be dropped.
+.Pp
+The
+.Cm setdscp
+option could be used to (re)mark user traffic,
+by adding the following to the appropriate place in ruleset:
+.Pp
+.Dl "ipfw add setdscp be ip from any to any dscp af11,af21"
+.Pp
+This rule drops all incoming packets that appear to be coming from another
+directly connected system but on the wrong interface.
+For example, a packet with a source address of
+.Li 192.168.0.0/24 ,
+configured on
+.Li fxp0 ,
+but coming in on
+.Li fxp1
+would be dropped.
 .Ss DYNAMIC RULES
 In order to protect a site from flood attacks involving fake
 TCP packets, it is safer to use dynamic rules:

Modified: trunk/sbin/ipfw/ipfw2.c
===================================================================
--- trunk/sbin/ipfw/ipfw2.c	2017-03-04 21:35:16 UTC (rev 9404)
+++ trunk/sbin/ipfw/ipfw2.c	2017-03-04 21:36:44 UTC (rev 9405)
@@ -167,6 +167,32 @@
 	{ 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 @@
 	{ "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 @@
 	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 @@
 	}
 }
 
+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
@@ -1204,6 +1302,17 @@
 			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;
@@ -1499,6 +1608,10 @@
 				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 );
@@ -3035,6 +3148,24 @@
 		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;
@@ -3447,6 +3578,12 @@
 			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);

Modified: trunk/sbin/ipfw/ipfw2.h
===================================================================
--- trunk/sbin/ipfw/ipfw2.h	2017-03-04 21:35:16 UTC (rev 9404)
+++ trunk/sbin/ipfw/ipfw2.h	2017-03-04 21:36:44 UTC (rev 9405)
@@ -203,6 +203,7 @@
 	TOK_SETFIB,
 	TOK_LOOKUP,
 	TOK_SOCKARG,
+	TOK_SETDSCP,
 };
 /*
  * the following macro returns an error message if we run out of

Modified: trunk/sys/netinet/ip_fw.h
===================================================================
--- trunk/sys/netinet/ip_fw.h	2017-03-04 21:35:16 UTC (rev 9404)
+++ trunk/sys/netinet/ip_fw.h	2017-03-04 21:36:44 UTC (rev 9405)
@@ -219,6 +219,9 @@
 
 	O_FORWARD_IP6,		/* fwd sockaddr_in6             */
 
+	O_DSCP,			/* 2 u32 = DSCP mask */
+	O_SETDSCP,		/* arg1=DSCP value */
+
 	O_LAST_OPCODE		/* not an opcode!		*/
 };
 

Modified: trunk/sys/netpfil/ipfw/ip_fw2.c
===================================================================
--- trunk/sys/netpfil/ipfw/ip_fw2.c	2017-03-04 21:35:16 UTC (rev 9404)
+++ trunk/sys/netpfil/ipfw/ip_fw2.c	2017-03-04 21:36:44 UTC (rev 9405)
@@ -1658,6 +1658,32 @@
 				    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;
@@ -2340,6 +2366,32 @@
 				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;

Modified: trunk/sys/netpfil/ipfw/ip_fw_log.c
===================================================================
--- trunk/sys/netpfil/ipfw/ip_fw_log.c	2017-03-04 21:35:16 UTC (rev 9404)
+++ trunk/sys/netpfil/ipfw/ip_fw_log.c	2017-03-04 21:36:44 UTC (rev 9405)
@@ -209,12 +209,10 @@
 				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:

Modified: trunk/sys/netpfil/ipfw/ip_fw_sockopt.c
===================================================================
--- trunk/sys/netpfil/ipfw/ip_fw_sockopt.c	2017-03-04 21:35:16 UTC (rev 9404)
+++ trunk/sys/netpfil/ipfw/ip_fw_sockopt.c	2017-03-04 21:36:44 UTC (rev 9405)
@@ -681,6 +681,11 @@
 				goto bad_size;
 			break;
 
+		case O_DSCP:
+			if (cmdlen != F_INSN_SIZE(ipfw_insn_u32) + 1)
+				goto bad_size;
+			break;
+
 		case O_MAC_TYPE:
 		case O_IP_SRCPORT:
 		case O_IP_DSTPORT: /* XXX artificial limit, 30 port pairs */
@@ -741,6 +746,7 @@
 		case O_ACCEPT:
 		case O_DENY:
 		case O_REJECT:
+		case O_SETDSCP:
 #ifdef INET6
 		case O_UNREACH6:
 #endif



More information about the Midnightbsd-cvs mailing list