diff --git a/sbin/ipfw/ipfw.8 b/sbin/ipfw/ipfw.8 index db0dfc0a505e..a68d490400df 100644 --- a/sbin/ipfw/ipfw.8 +++ b/sbin/ipfw/ipfw.8 @@ -960,6 +960,61 @@ It is possible to use the keyword with setfib. If the tablearg value is not within the compiled range of fibs, the packet's 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 @@ -1454,6 +1509,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 , @@ -2976,6 +3042,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: diff --git a/sbin/ipfw/ipfw2.c b/sbin/ipfw/ipfw2.c index ae5d2aaab457..39934ab32a3f 100644 --- a/sbin/ipfw/ipfw2.c +++ b/sbin/ipfw/ipfw2.c @@ -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 proto, int cblen) 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 bcwidth) 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 bcwidth) 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); diff --git a/sbin/ipfw/ipfw2.h b/sbin/ipfw/ipfw2.h index 81e0ba8aa29c..d592930338f2 100644 --- a/sbin/ipfw/ipfw2.h +++ b/sbin/ipfw/ipfw2.h @@ -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 diff --git a/sys/netinet/ip_fw.h b/sys/netinet/ip_fw.h index 9693523c6db4..14b08f5e34d1 100644 --- a/sys/netinet/ip_fw.h +++ b/sys/netinet/ip_fw.h @@ -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! */ }; diff --git a/sys/netpfil/ipfw/ip_fw2.c b/sys/netpfil/ipfw/ip_fw2.c index 1c096d928a07..5e0675b7af91 100644 --- a/sys/netpfil/ipfw/ip_fw2.c +++ b/sys/netpfil/ipfw/ip_fw2.c @@ -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; diff --git a/sys/netpfil/ipfw/ip_fw_log.c b/sys/netpfil/ipfw/ip_fw_log.c index 48e67911356f..0f3ab9da9e05 100644 --- a/sys/netpfil/ipfw/ip_fw_log.c +++ b/sys/netpfil/ipfw/ip_fw_log.c @@ -292,10 +292,8 @@ ipfw_log(struct ip_fw *f, u_int hlen, struct ip_fw_args *args, 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; diff --git a/sys/netpfil/ipfw/ip_fw_sockopt.c b/sys/netpfil/ipfw/ip_fw_sockopt.c index 3363cf9654bb..45da394ad812 100644 --- a/sys/netpfil/ipfw/ip_fw_sockopt.c +++ b/sys/netpfil/ipfw/ip_fw_sockopt.c @@ -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