Skip site navigation (1)Skip section navigation (2)
Date:      Sat, 18 May 2013 05:48:46 +0000 (UTC)
From:      "Alexander V. Chernikov" <melifaro@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-9@freebsd.org
Subject:   svn commit: r250762 - in stable/9: sbin/ipfw sys/netinet sys/netpfil/ipfw
Message-ID:  <201305180548.r4I5mkqN005104@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: melifaro
Date: Sat May 18 05:48:46 2013
New Revision: 250762
URL: http://svnweb.freebsd.org/changeset/base/250762

Log:
  MFC r248552, r248971
  
  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. Fast checksum recalculation (RFC 1624) is done for IPv4.
  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).
  
  Many people made their variants of this patch, the ones I'm aware of are
  (in alphabetic order):
  
  Dmitrii Tejblum
  Marcelo Araujo
  Roman Bogorodskiy (novel)
  Sergey Matveichuk (sem)
  Sergey Ryabin
  
  PR:		kern/102471, kern/121122
  
  Fix ipfw rule validation partially broken by r248552.

Modified:
  stable/9/sbin/ipfw/ipfw.8
  stable/9/sbin/ipfw/ipfw2.c
  stable/9/sbin/ipfw/ipfw2.h
  stable/9/sys/netinet/ip_fw.h
  stable/9/sys/netpfil/ipfw/ip_fw2.c
  stable/9/sys/netpfil/ipfw/ip_fw_log.c
  stable/9/sys/netpfil/ipfw/ip_fw_sockopt.c
Directory Properties:
  stable/9/sbin/   (props changed)
  stable/9/sbin/ipfw/   (props changed)
  stable/9/sys/   (props changed)

Modified: stable/9/sbin/ipfw/ipfw.8
==============================================================================
--- stable/9/sbin/ipfw/ipfw.8	Sat May 18 05:40:59 2013	(r250761)
+++ stable/9/sbin/ipfw/ipfw.8	Sat May 18 05:48:46 2013	(r250762)
@@ -948,6 +948,61 @@ Processing continues at the next rule.
 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 supported IP types of service are:
 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 @@ configured on
 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: stable/9/sbin/ipfw/ipfw2.c
==============================================================================
--- stable/9/sbin/ipfw/ipfw2.c	Sat May 18 05:40:59 2013	(r250761)
+++ stable/9/sbin/ipfw/ipfw2.c	Sat May 18 05:48:46 2013	(r250762)
@@ -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 *
 	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
@@ -1204,6 +1302,17 @@ show_ipfw(struct ip_fw *rule, int pcwidt
 			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 @@ show_ipfw(struct ip_fw *rule, int pcwidt
 				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 @@ 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;
@@ -3447,6 +3578,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);

Modified: stable/9/sbin/ipfw/ipfw2.h
==============================================================================
--- stable/9/sbin/ipfw/ipfw2.h	Sat May 18 05:40:59 2013	(r250761)
+++ stable/9/sbin/ipfw/ipfw2.h	Sat May 18 05:48:46 2013	(r250762)
@@ -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

Modified: stable/9/sys/netinet/ip_fw.h
==============================================================================
--- stable/9/sys/netinet/ip_fw.h	Sat May 18 05:40:59 2013	(r250761)
+++ stable/9/sys/netinet/ip_fw.h	Sat May 18 05:48:46 2013	(r250762)
@@ -218,6 +218,9 @@ enum ipfw_opcodes {		/* arguments (4 byt
 
 	O_FORWARD_IP6,		/* fwd sockaddr_in6             */
 
+	O_DSCP,			/* 2 u32 = DSCP mask */
+	O_SETDSCP,		/* arg1=DSCP value */
+
 	O_LAST_OPCODE		/* not an opcode!		*/
 };
 

Modified: stable/9/sys/netpfil/ipfw/ip_fw2.c
==============================================================================
--- stable/9/sys/netpfil/ipfw/ip_fw2.c	Sat May 18 05:40:59 2013	(r250761)
+++ stable/9/sys/netpfil/ipfw/ip_fw2.c	Sat May 18 05:48:46 2013	(r250762)
@@ -1658,6 +1658,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;
@@ -2340,6 +2366,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;

Modified: stable/9/sys/netpfil/ipfw/ip_fw_log.c
==============================================================================
--- stable/9/sys/netpfil/ipfw/ip_fw_log.c	Sat May 18 05:40:59 2013	(r250761)
+++ stable/9/sys/netpfil/ipfw/ip_fw_log.c	Sat May 18 05:48:46 2013	(r250762)
@@ -209,10 +209,8 @@ ipfw_log(struct ip_fw *f, u_int hlen, st
 				altq->qid);
 			cmd += F_LEN(cmd);
 		}
-		if (cmd->opcode == O_PROB)
-			cmd += F_LEN(cmd);
-
-		if (cmd->opcode == O_TAG)
+		if (cmd->opcode == O_PROB || cmd->opcode == O_TAG ||
+		    cmd->opcode == O_SETDSCP)
 			cmd += F_LEN(cmd);
 
 		action = action2;

Modified: stable/9/sys/netpfil/ipfw/ip_fw_sockopt.c
==============================================================================
--- stable/9/sys/netpfil/ipfw/ip_fw_sockopt.c	Sat May 18 05:40:59 2013	(r250761)
+++ stable/9/sys/netpfil/ipfw/ip_fw_sockopt.c	Sat May 18 05:48:46 2013	(r250762)
@@ -681,6 +681,11 @@ check_ipfw_struct(struct ip_fw *rule, in
 				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 @@ check_ipfw_struct(struct ip_fw *rule, in
 		case O_ACCEPT:
 		case O_DENY:
 		case O_REJECT:
+		case O_SETDSCP:
 #ifdef INET6
 		case O_UNREACH6:
 #endif



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201305180548.r4I5mkqN005104>