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