kern/102471: [ipfw] [patch] add tos and dscp support

Roman Bogorodskiy novel at FreeBSD.org
Thu Aug 24 07:30:25 UTC 2006


>Number:         102471
>Category:       kern
>Synopsis:       [ipfw] [patch] add tos and dscp support
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          change-request
>Submitter-Id:   current-users
>Arrival-Date:   Thu Aug 24 07:30:24 GMT 2006
>Closed-Date:
>Last-Modified:
>Originator:     Roman Bogorodskiy
>Release:        FreeBSD 6.0-STABLE i386
>Organization:
>Environment:
System: FreeBSD freefall.freebsd.org 6.0-STABLE FreeBSD 6.0-STABLE #0: Sat Dec 10 03:18:20 UTC 2005 kensmith at freefall.freebsd.org:/usr/obj/usr/src/sys/FREEFALL i386


	
>Description:
	This patch adds two features:
	
	1. Allow to change tos and dscp field of IPv4 packets. It can be
	used in a such way:

	ipfw add 100 iptos lowdelay all from <the rest of the rule>
	ipfw add 110 dscp AF33 alll from -//-

	That will change tos/dscp field.

	2. Allow to match IPv4 packets by dscp field:

	ipfw add 80 count tcp from any to any dscp AF33

	The patch was originally written by Sergey Ryabin for FreeBSD 5.x
	and I have rewrote it in various places. I tested it of 6-STABLE.

	References:
		http://www.cisco.com/warp/public/105/dscpvalues.html
		http://www.iana.org/assignments/ipv4-tos-byte
		http://www.iana.org/assignments/dscp-registry
		
>How-To-Repeat:
>Fix:

--- ipfw_tos_dscp_20060824_1.diff begins here ---
diff -ru src/sbin/ipfw/ipfw.8 /usr/src/sbin/ipfw/ipfw.8
--- src/sbin/ipfw/ipfw.8	Sat Jul 29 12:24:12 2006
+++ /usr/src/sbin/ipfw/ipfw.8	Thu Aug 24 10:42:19 2006
@@ -823,6 +823,14 @@
 and
 .Cm ngtee
 actions.
+.It Cm iptos Ar code
+Changes
+.Cm tos
+field of an IPv4 packet.
+.It Cm dscp Ar code
+Changes
+.Cm dscp
+field of an IPv4 packet.
 .El
 .Ss RULE BODY
 The body of a rule contains zero or more patterns (such as
@@ -1273,6 +1281,55 @@
 The absence of a particular type may be denoted
 with a
 .Ql \&! .
+.It Cm dscp Ar code
+Matches IPv4 packets whose
+.Cm dscp
+field contains
+.Ar code .
+The 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 .
 .It Cm ipttl Ar ttl-list
 Matches IPv4 packets whose time to live is included in
 .Ar ttl-list ,
diff -ru src/sbin/ipfw/ipfw2.c /usr/src/sbin/ipfw/ipfw2.c
--- src/sbin/ipfw/ipfw2.c	Mon Aug  7 23:32:57 2006
+++ /usr/src/sbin/ipfw/ipfw2.c	Thu Aug 24 10:14:10 2006
@@ -133,6 +133,33 @@
 	int x;
 };
 
+static struct _s_x f_ipdscp[] = {
+	/* DSCP */
+	{ "AF11", 40 },
+	{ "AF12", 48 },
+	{ "AF13", 56 },
+        { "AF21", 72 },
+        { "AF22", 80 },
+	{ "AF23", 88 },
+	{ "AF31", 104 },
+	{ "AF32", 112 },
+	{ "AF33", 120 },
+	{ "AF41", 136 },
+	{ "AF42", 144 }, 
+	{ "AF43", 152 },
+	{ "EF",   184 },
+	/* COS flags */
+        { "CS0", 0 },
+	{ "CS1", 32 },  
+	{ "CS2", 64 },
+	{ "CS3", 96 },
+	{ "CS4", 128 },
+	{ "CS5", 160 },
+	{ "CS6", 192 },
+	{ "CS7", 224 },
+	{ NULL, 0 }
+};
+
 static struct _s_x f_tcpflags[] = {
 	{ "syn", TH_SYN },
 	{ "fin", TH_FIN },
@@ -274,6 +301,7 @@
 	TOK_IPID,
 	TOK_IPPRECEDENCE,
 	TOK_IPTOS,
+	TOK_IPDSCP,
 	TOK_IPTTL,
 	TOK_IPVER,
 	TOK_ESTAB,
@@ -309,6 +337,8 @@
 	TOK_DROPTAIL,
 	TOK_PROTO,
 	TOK_WEIGHT,
+	TOK_SETIPTOS,
+	TOK_SETDSCP,
 
 	TOK_IPV6,
 	TOK_FLOWID,
@@ -374,6 +404,8 @@
 	{ "unreach6",		TOK_UNREACH6 },
 	{ "unreach",		TOK_UNREACH },
 	{ "check-state",	TOK_CHECKSTATE },
+	{ "iptos",		TOK_SETIPTOS },
+	{ "dscp",               TOK_SETDSCP },
 	{ "//",			TOK_COMMENT },
 	{ NULL, 0 }	/* terminator */
 };
@@ -411,6 +443,7 @@
 	{ "ipid",		TOK_IPID },
 	{ "ipprecedence",	TOK_IPPRECEDENCE },
 	{ "iptos",		TOK_IPTOS },
+	{ "dscp",		TOK_IPDSCP },
 	{ "ipttl",		TOK_IPTTL },
 	{ "ipversion",		TOK_IPVER },
 	{ "ipver",		TOK_IPVER },
@@ -1550,6 +1583,12 @@
 				printf(",%d", s->sa.sin_port);
 		    }
 			break;
+		case O_SET_IPTOS:
+			printf("iptos %s", match_value(f_iptos, cmd->arg1));
+			break;
+	        case O_SET_DSCP:
+			printf("dscp %s", match_value(f_ipdscp, cmd->arg1));
+			break;
 
 		case O_LOG: /* O_LOG is printed last */
 			logptr = (ipfw_insn_log *)cmd;
@@ -1855,6 +1894,10 @@
 				print_flags("iptos", cmd, f_iptos);
 				break;
 
+			case O_IPDSCP:
+				printf(" dscp %s", match_value(f_ipdscp, cmd->arg1));
+				break;
+
 			case O_ICMPTYPE:
 				print_icmptypes((ipfw_insn_u32 *)cmd);
 				break;
@@ -2631,7 +2674,7 @@
 "RULE-BODY:	check-state [PARAMS] | ACTION [PARAMS] ADDR [OPTION_LIST]\n"
 "ACTION:	check-state | allow | count | deny | unreach{,6} CODE |\n"
 "               skipto N | {divert|tee} PORT | forward ADDR |\n"
-"               pipe N | queue N\n"
+"               pipe N | queue N | iptos CODE | dscp CODE\n"
 "PARAMS: 	[log [logamount LOGLIMIT]] [altq QUEUE_NAME]\n"
 "ADDR:		[ MAC dst src ether_type ] \n"
 "		[ ip from IPADDR [ PORT ] to IPADDR [ PORTLIST ] ]\n"
@@ -2642,8 +2685,8 @@
 "IPLIST:	{ ip | ip/bits | ip:mask }[,IPLIST]\n"
 "OPTION_LIST:	OPTION [OPTION_LIST]\n"
 "OPTION:	bridged | diverted | diverted-loopback | diverted-output |\n"
-"	{dst-ip|src-ip} IPADDR | {dst-ip6|src-ip6|dst-ipv6|src-ipv6} IP6ADDR |\n"
-"	{dst-port|src-port} LIST |\n"
+"	dscp CODE | {dst-ip|src-ip} IPADDR |\n"
+"	{dst-ip6|src-ip6|dst-ipv6|src-ipv6} IP6ADDR | {dst-port|src-port} LIST |\n"
 "	estab | frag | {gid|uid} N | icmptypes LIST | in | out | ipid LIST |\n"
 "	iplen LIST | ipoptions SPEC | ipprecedence | ipsec | iptos SPEC |\n"
 "	ipttl LIST | ipversion VER | keep-state | layer2 | limit ... |\n"
@@ -3960,6 +4003,18 @@
 	case TOK_COUNT:
 		action->opcode = O_COUNT;
 		break;
+		
+	case TOK_SETIPTOS:
+		NEED1("need iptos arg\n");
+		fill_flags(action, O_SET_IPTOS, f_iptos, *av);
+		ac--; av++;
+		break;
+	
+	case TOK_SETDSCP:
+		NEED1("need dscp arg\n");
+		fill_flags(action, O_SET_DSCP, f_ipdscp, *av);
+		ac--; av++;
+		break;
 
 	case TOK_QUEUE:
 		action->len = F_INSN_SIZE(ipfw_insn_pipe);
@@ -4446,6 +4501,12 @@
 		case TOK_IPTOS:
 			NEED1("missing argument for iptos");
 			fill_flags(cmd, O_IPTOS, f_iptos, *av);
+			ac--; av++;
+			break;
+
+		case TOK_IPDSCP:
+			NEED1("missing argument for dscp");
+			fill_flags(cmd, O_IPDSCP, f_ipdscp, *av);
 			ac--; av++;
 			break;
 
diff -ru src/sys/netinet/ip_fw.h /usr/src/sys/netinet/ip_fw.h
--- src/sys/netinet/ip_fw.h	Sat Jul 29 12:24:12 2006
+++ /usr/src/sys/netinet/ip_fw.h	Wed Aug 23 17:22:43 2006
@@ -160,6 +160,10 @@
 	O_TAG,   		/* arg1=tag number */
 	O_TAGGED,		/* arg1=tag number */
 
+        O_SET_IPTOS,            /* arg1 = iptos               	*/
+        O_SET_DSCP,             /* arg1 = dscp            	*/
+        O_IPDSCP,               /* arg1 = DSCP                  */
+
 	O_LAST_OPCODE		/* not an opcode!		*/
 };
 
@@ -563,6 +567,21 @@
 typedef	int ip_fw_chk_t(struct ip_fw_args *args);
 extern	ip_fw_chk_t	*ip_fw_chk_ptr;
 #define	IPFW_LOADED	(ip_fw_chk_ptr != NULL)
+
+#define ADJUST_CHECKSUM(acc, cksum) \
+        do { \
+                acc += cksum; \
+                if (acc < 0) { \
+                        acc = -acc; \
+                        acc = (acc >> 16) + (acc & 0xffff); \
+                        acc += acc >> 16; \
+                        cksum = (u_short) ~acc; \
+                } else { \
+                        acc = (acc >> 16) + (acc & 0xffff); \
+                        acc += acc >> 16; \
+                        cksum = (u_short) acc; \
+                } \
+        } while (0)
 
 #endif /* _KERNEL */
 #endif /* _IPFW2_H */
diff -ru src/sys/netinet/ip_fw2.c /usr/src/sys/netinet/ip_fw2.c
--- src/sys/netinet/ip_fw2.c	Sat Jul 29 12:24:12 2006
+++ /usr/src/sys/netinet/ip_fw2.c	Thu Aug 24 10:43:17 2006
@@ -145,6 +145,21 @@
 	NET_ASSERT_GIANT();						\
 } while (0)
 
+static __inline int
+twowords(void *p)
+{
+        uint8_t *c = p;
+
+#if BYTE_ORDER == LITTLE_ENDIAN
+        uint16_t s1 = ((uint16_t)c[1] << 8) + (uint16_t)c[0];
+        uint16_t s2 = ((uint16_t)c[3] << 8) + (uint16_t)c[2];
+#else   
+        uint16_t s1 = ((uint16_t)c[0] << 8) + (uint16_t)c[1];
+        uint16_t s2 = ((uint16_t)c[2] << 8) + (uint16_t)c[3];
+#endif  
+        return (s1 + s2);
+}
+
 static __inline void
 IPFW_RLOCK(struct ip_fw_chain *chain)
 {
@@ -2427,6 +2442,7 @@
 	for (; f; f = f->next) {
 		ipfw_insn *cmd;
 		uint32_t tablearg = 0;
+		int accumulate;
 		int l, cmdlen, skip_or; /* skip rest of OR block */
 
 again:
@@ -2734,6 +2750,11 @@
 				    flags_match(cmd, mtod(m, struct ip *)->ip_tos));
 				break;
 
+			case O_IPDSCP:
+				match = (is_ipv4 &&
+				    (cmd->arg1 == (mtod(m, struct ip *)->ip_tos & 0xfc)) );
+				break;
+			
 			case O_TCPDATALEN:
 				if (proto == IPPROTO_TCP && offset == 0) {
 				    struct tcphdr *tcp;
@@ -3052,6 +3073,28 @@
 				}
 				match = 1;
 				break;
+			
+			case O_SET_IPTOS:
+                                accumulate = twowords(&ip->ip_tos);
+				/* for tos, we have to change whole byte,
+				   and for dscp bits 0-5 only */
+                                ip->ip_tos = cmd->arg1;
+                                accumulate -= twowords(&ip->ip_tos);
+                                ADJUST_CHECKSUM(accumulate, ip->ip_sum);
+                                f->pcnt++;      /* update stats */
+                                f->bcnt += pktlen;
+                                f->timestamp = time_second;
+                                goto next_rule;
+
+			case O_SET_DSCP:
+				accumulate = twowords(&ip->ip_tos);
+				ip->ip_tos = ip->ip_tos | cmd->arg1;
+				accumulate -= twowords(&ip->ip_tos);
+				ADJUST_CHECKSUM(accumulate, ip->ip_sum);
+				f->pcnt++;      /* update stats */
+				f->bcnt += pktlen;
+				f->timestamp = time_second;
+				goto next_rule;
 
 			case O_PROBE_STATE:
 			case O_CHECK_STATE:
@@ -3655,6 +3698,7 @@
 		case O_DIVERTED:
 		case O_IPOPT:
 		case O_IPTOS:
+		case O_IPDSCP:
 		case O_IPPRECEDENCE:
 		case O_IPVER:
 		case O_TCPWIN:
@@ -3675,6 +3719,11 @@
 		case O_TAG:
 			if (cmdlen != F_INSN_SIZE(ipfw_insn))
 				goto bad_size;
+			break;
+
+		case O_SET_DSCP:
+		case O_SET_IPTOS:
+			have_action = 1;
 			break;
 
 		case O_UID:
--- ipfw_tos_dscp_20060824_1.diff ends here ---


>Release-Note:
>Audit-Trail:
>Unformatted:


More information about the freebsd-bugs mailing list