diff --git a/sbin/ipfw/ipfw.8 b/sbin/ipfw/ipfw.8 index ef05581b19f8..7ba90e46a94c 100644 --- a/sbin/ipfw/ipfw.8 +++ b/sbin/ipfw/ipfw.8 @@ -1236,6 +1236,12 @@ specified as argument. TCP packets only. Match if the TCP header acknowledgment number field is set to .Ar ack . +.It Cm tcpdatalen Ar tcpdatalen-list +Matches TCP packets whose length of TCP data is +.Ar tcpdatalen-list , +which is either a single value or a list of values or ranges +specified in the same way as +.Ar ports . .It Cm tcpflags Ar spec TCP packets only. Match if the TCP header contains the comma separated list of diff --git a/sbin/ipfw/ipfw2.c b/sbin/ipfw/ipfw2.c index bfc0fc43f42b..fe3a8fe21d01 100644 --- a/sbin/ipfw/ipfw2.c +++ b/sbin/ipfw/ipfw2.c @@ -233,6 +233,7 @@ enum tokens { TOK_IPVER, TOK_ESTAB, TOK_SETUP, + TOK_TCPDATALEN, TOK_TCPFLAGS, TOK_TCPOPTS, TOK_TCPSEQ, @@ -348,6 +349,7 @@ struct _s_x rule_options[] = { { "estab", TOK_ESTAB }, { "established", TOK_ESTAB }, { "setup", TOK_SETUP }, + { "tcpdatalen", TOK_TCPDATALEN }, { "tcpflags", TOK_TCPFLAGS }, { "tcpflgs", TOK_TCPFLAGS }, { "tcpoptions", TOK_TCPOPTS }, @@ -481,6 +483,7 @@ struct _s_x _port_name[] = { {"iplen", O_IPLEN}, {"ipttl", O_IPTTL}, {"mac-type", O_MAC_TYPE}, + {"tcpdatalen", O_TCPDATALEN}, {NULL, 0} }; @@ -1395,6 +1398,14 @@ show_ipfw(struct ip_fw *rule, int pcwidth, int bcwidth) printf(" established"); break; + case O_TCPDATALEN: + if (F_LEN(cmd) == 1) + printf(" tcpdatalen %u", cmd->arg1 ); + else + print_newports((ipfw_insn_u16 *)cmd, 0, + O_TCPDATALEN); + break; + case O_TCPFLAGS: print_flags("tcpflags", cmd, f_tcpflags); break; @@ -2072,6 +2083,7 @@ help(void) " mac ... | mac-type LIST | proto LIST | {recv|xmit|via} {IF|IPADDR} |\n" " setup | {tcpack|tcpseq|tcpwin} NN | tcpflags SPEC | tcpoptions SPEC |\n" " verrevpath | versrcreach | antispoof\n" +" tcpdatalen LIST | verrevpath | versrcreach | antispoof\n" ); exit(0); } @@ -3542,6 +3554,17 @@ add(int ac, char *av[]) (TH_SYN) | ( (TH_ACK) & 0xff) <<8 ); break; + case TOK_TCPDATALEN: + NEED1("tcpdatalen requires length"); + if (strpbrk(*av, "-,")) { + if (!add_ports(cmd, *av, 0, O_TCPDATALEN)) + errx(EX_DATAERR, "invalid tcpdata len %s", *av); + } else + fill_cmd(cmd, O_TCPDATALEN, 0, + strtoul(*av, NULL, 0)); + ac--; av++; + break; + case TOK_TCPOPTS: NEED1("missing argument for tcpoptions"); fill_flags(cmd, O_TCPOPTS, f_tcpopts, *av); diff --git a/sys/netinet/ip_fw.h b/sys/netinet/ip_fw.h index 15ce84b288b5..97711899d37b 100644 --- a/sys/netinet/ip_fw.h +++ b/sys/netinet/ip_fw.h @@ -136,6 +136,7 @@ enum ipfw_opcodes { /* arguments (4 byte each) */ O_JAIL, /* u32 = id */ O_ALTQ, /* u32 = altq classif. qid */ O_DIVERTED, /* arg1=bitmap (1:loop, 2:out) */ + O_TCPDATALEN, /* arg1 = tcp data len */ O_LAST_OPCODE /* not an opcode! */ }; diff --git a/sys/netinet/ip_fw2.c b/sys/netinet/ip_fw2.c index 854e2d927e43..c45a04f5f932 100644 --- a/sys/netinet/ip_fw2.c +++ b/sys/netinet/ip_fw2.c @@ -2203,6 +2203,28 @@ ipfw_chk(struct ip_fw_args *args) flags_match(cmd, ip->ip_tos)); break; + case O_TCPDATALEN: + if (proto == IPPROTO_TCP && offset == 0) { + struct tcphdr *tcp; + uint16_t x; + uint16_t *p; + int i; + + tcp = L3HDR(struct tcphdr,ip); + x = ip_len - + ((ip->ip_hl + tcp->th_off) << 2); + if (cmdlen == 1) { + match = (cmd->arg1 == x); + break; + } + /* otherwise we have ranges */ + p = ((ipfw_insn_u16 *)cmd)->ports; + i = cmdlen - 1; + for (; !match && i>0; i--, p += 2) + match = (x >= p[0] && x <= p[1]); + } + break; + case O_TCPFLAGS: match = (proto == IPPROTO_TCP && offset == 0 && flags_match(cmd, @@ -3014,6 +3036,7 @@ check_ipfw_struct(struct ip_fw *rule, int size) case O_IPID: case O_IPTTL: case O_IPLEN: + case O_TCPDATALEN: if (cmdlen < 1 || cmdlen > 31) goto bad_size; break;