Index: sbin/ipfw/ipfw.8 =================================================================== --- sbin/ipfw/ipfw.8 +++ sbin/ipfw/ipfw.8 @@ -1989,6 +1989,12 @@ See the .Cm frag option for details on matching fragmented packets. +.It Cm tcpmss Ar tcpmss-list +Matches TCP packets whose MSS (maximum segment size) value is set to +.Ar tcpmss-list , +which is either a single value or a list of values or ranges +specified in the same way as +.Ar ports . .It Cm tcpseq Ar seq TCP packets only. Match if the TCP header sequence number field is set to Index: sbin/ipfw/ipfw2.h =================================================================== --- sbin/ipfw/ipfw2.h +++ sbin/ipfw/ipfw2.h @@ -151,6 +151,7 @@ TOK_TCPOPTS, TOK_TCPSEQ, TOK_TCPACK, + TOK_TCPMSS, TOK_TCPWIN, TOK_ICMPTYPES, TOK_MAC, Index: sbin/ipfw/ipfw2.c =================================================================== --- sbin/ipfw/ipfw2.c +++ sbin/ipfw/ipfw2.c @@ -338,6 +338,7 @@ { "tcpdatalen", TOK_TCPDATALEN }, { "tcpflags", TOK_TCPFLAGS }, { "tcpflgs", TOK_TCPFLAGS }, + { "tcpmss", TOK_TCPMSS }, { "tcpoptions", TOK_TCPOPTS }, { "tcpopts", TOK_TCPOPTS }, { "tcpseq", TOK_TCPSEQ }, @@ -881,6 +882,7 @@ {"ipttl", O_IPTTL}, {"mac-type", O_MAC_TYPE}, {"tcpdatalen", O_TCPDATALEN}, + {"tcpmss", O_TCPMSS}, {"tcpwin", O_TCPWIN}, {"tagged", O_TAGGED}, {NULL, 0} @@ -1588,6 +1590,7 @@ case O_IPTTL: case O_IPLEN: case O_TCPDATALEN: + case O_TCPMSS: case O_TCPWIN: if (F_LEN(cmd) == 1) { switch (cmd->opcode) { @@ -1603,6 +1606,9 @@ case O_TCPDATALEN: s = "tcpdatalen"; break; + case O_TCPMSS: + s = "tcpmss"; + break; case O_TCPWIN: s = "tcpwin"; break; @@ -4709,14 +4715,17 @@ av++; break; + case TOK_TCPMSS: case TOK_TCPWIN: - NEED1("tcpwin requires length"); - if (strpbrk(*av, "-,")) { - if (!add_ports(cmd, *av, 0, O_TCPWIN, cblen)) - errx(EX_DATAERR, "invalid tcpwin len %s", *av); - } else - fill_cmd(cmd, O_TCPWIN, 0, - strtoul(*av, NULL, 0)); + NEED1("%s requires length", s); + if (strpbrk(*av, "-,") && + add_ports(cmd, *av, 0, + i == TOK_TCPWIN ? O_TCPWIN : O_TCPMSS, + cblen) == 0) + errx(EX_DATAERR, "invalid %s len %s", s, *av); + else + fill_cmd(cmd, i == TOK_TCPWIN ? O_TCPWIN : + O_TCPMSS, 0, strtoul(*av, NULL, 0)); av++; break; Index: sys/netinet/ip_fw.h =================================================================== --- sys/netinet/ip_fw.h +++ sys/netinet/ip_fw.h @@ -293,6 +293,7 @@ O_EXTERNAL_DATA, /* variable length data */ O_SKIP_ACTION, /* none */ + O_TCPMSS, /* arg1=MSS value */ O_LAST_OPCODE /* not an opcode! */ }; Index: sys/netpfil/ipfw/ip_fw2.c =================================================================== --- sys/netpfil/ipfw/ip_fw2.c +++ sys/netpfil/ipfw/ip_fw2.c @@ -331,10 +331,10 @@ } static int -tcpopts_match(struct tcphdr *tcp, ipfw_insn *cmd) +tcpopts_parse(struct tcphdr *tcp, uint16_t *mss) { - int optlen, bits = 0; u_char *cp = (u_char *)(tcp + 1); + int optlen, bits = 0; int x = (tcp->th_off << 2) - sizeof(struct tcphdr); for (; x > 0; x -= optlen, cp += optlen) { @@ -350,12 +350,13 @@ } switch (opt) { - default: break; case TCPOPT_MAXSEG: bits |= IP_FW_TCPOPT_MSS; + if (mss != NULL) + *mss = be16dec(cp + 2); break; case TCPOPT_WINDOW: @@ -370,13 +371,19 @@ case TCPOPT_TIMESTAMP: bits |= IP_FW_TCPOPT_TS; break; - } } - return (flags_match(cmd, bits)); + return (bits); } static int +tcpopts_match(struct tcphdr *tcp, ipfw_insn *cmd) +{ + + return (flags_match(cmd, tcpopts_parse(tcp, NULL))); +} + +static int iface_match(struct ifnet *ifp, ipfw_insn_if *cmd, struct ip_fw_chain *chain, uint32_t *tablearg) { @@ -2314,6 +2321,31 @@ match = (proto == IPPROTO_TCP && offset == 0 && ((ipfw_insn_u32 *)cmd)->d[0] == TCP(ulp)->th_ack); + break; + + case O_TCPMSS: + if (proto == IPPROTO_TCP && + (args->f_id._flags & TH_SYN) != 0 && + ulp != NULL) { + uint16_t mss, *p; + int i; + + PULLUP_LEN(hlen, ulp, + (TCP(ulp)->th_off << 2)); + if ((tcpopts_parse(TCP(ulp), &mss) & + IP_FW_TCPOPT_MSS) == 0) + break; + if (cmdlen == 1) { + match = (cmd->arg1 == mss); + break; + } + /* Otherwise we have ranges. */ + p = ((ipfw_insn_u16 *)cmd)->ports; + i = cmdlen - 1; + for (; !match && i > 0; i--, p += 2) + match = (mss >= p[0] && + mss <= p[1]); + } break; case O_TCPWIN: Index: sys/netpfil/ipfw/ip_fw_sockopt.c =================================================================== --- sys/netpfil/ipfw/ip_fw_sockopt.c +++ sys/netpfil/ipfw/ip_fw_sockopt.c @@ -1176,7 +1176,9 @@ } } return (c); -}/* +} + +/* * Changes set of given rule rannge @rt * with each other. * @@ -1907,6 +1909,7 @@ case O_IPTTL: case O_IPLEN: case O_TCPDATALEN: + case O_TCPMSS: case O_TCPWIN: case O_TAGGED: if (cmdlen < 1 || cmdlen > 31)