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.
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
_______________________________________________
freebsd-ipfw@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/freebsd-ipfw
To unsubscribe, send any mail to "freebsd-ipfw-unsubscr...@freebsd.org"

Reply via email to