Add "tcpmss" opcode to match the TCP MSS value.

With this opcode it is possible to match TCP packets with specified
MSS option, whose value corresponds to configured in opcode value.
It is allowed to specify single value, range of values, or array of
specific values or ranges. E.g.

 # ipfw add deny log tcp from any to any tcpmss 0-500

Reviewed by:	melifaro,bcr
Obtained from:	Yandex LLC
MFC after:	1 week
Sponsored by:	Yandex LLC
This commit is contained in:
Andrey V. Elsukov 2019-06-21 10:54:51 +00:00
parent 05fc9d78d7
commit 978f2d1728
6 changed files with 65 additions and 12 deletions

View File

@ -1,7 +1,7 @@
.\"
.\" $FreeBSD$
.\"
.Dd May 24, 2019
.Dd June 21, 2019
.Dt IPFW 8
.Os
.Sh NAME
@ -1989,6 +1989,12 @@ a non-zero offset.
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

View File

@ -338,6 +338,7 @@ static struct _s_x rule_options[] = {
{ "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 @@ static struct _s_x _port_name[] = {
{"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 @@ print_instruction(struct buf_pr *bp, const struct format_opts *fo,
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 @@ print_instruction(struct buf_pr *bp, const struct format_opts *fo,
case O_TCPDATALEN:
s = "tcpdatalen";
break;
case O_TCPMSS:
s = "tcpmss";
break;
case O_TCPWIN:
s = "tcpwin";
break;
@ -4709,14 +4715,18 @@ compile_rule(char *av[], uint32_t *rbuf, int *rbufsize, struct tidx *tstate)
av++;
break;
case TOK_TCPMSS:
case TOK_TCPWIN:
NEED1("tcpwin requires length");
NEED1("tcpmss/tcpwin requires size");
if (strpbrk(*av, "-,")) {
if (!add_ports(cmd, *av, 0, O_TCPWIN, cblen))
errx(EX_DATAERR, "invalid tcpwin len %s", *av);
if (add_ports(cmd, *av, 0,
i == TOK_TCPWIN ? O_TCPWIN : O_TCPMSS,
cblen) == NULL)
errx(EX_DATAERR, "invalid %s size %s",
s, *av);
} else
fill_cmd(cmd, O_TCPWIN, 0,
strtoul(*av, NULL, 0));
fill_cmd(cmd, i == TOK_TCPWIN ? O_TCPWIN :
O_TCPMSS, 0, strtoul(*av, NULL, 0));
av++;
break;

View File

@ -151,6 +151,7 @@ enum tokens {
TOK_TCPOPTS,
TOK_TCPSEQ,
TOK_TCPACK,
TOK_TCPMSS,
TOK_TCPWIN,
TOK_ICMPTYPES,
TOK_MAC,

View File

@ -293,6 +293,7 @@ enum ipfw_opcodes { /* arguments (4 byte each) */
O_EXTERNAL_DATA, /* variable length data */
O_SKIP_ACTION, /* none */
O_TCPMSS, /* arg1=MSS value */
O_LAST_OPCODE /* not an opcode! */
};

View File

@ -331,10 +331,10 @@ ipopts_match(struct ip *ip, ipfw_insn *cmd)
}
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 @@ tcpopts_match(struct tcphdr *tcp, ipfw_insn *cmd)
}
switch (opt) {
default:
break;
case TCPOPT_MAXSEG:
bits |= IP_FW_TCPOPT_MSS;
if (mss != NULL)
*mss = be16dec(cp + 2);
break;
case TCPOPT_WINDOW:
@ -370,10 +371,16 @@ tcpopts_match(struct tcphdr *tcp, ipfw_insn *cmd)
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
@ -2316,6 +2323,31 @@ do { \
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:
if (proto == IPPROTO_TCP && offset == 0) {
uint16_t x;

View File

@ -1176,7 +1176,9 @@ move_objects(struct ip_fw_chain *ch, ipfw_range_tlv *rt)
}
}
return (c);
}/*
}
/*
* Changes set of given rule rannge @rt
* with each other.
*
@ -1907,6 +1909,7 @@ check_ipfw_rule_body(ipfw_insn *cmd, int cmd_len, struct rule_check_info *ci)
case O_IPTTL:
case O_IPLEN:
case O_TCPDATALEN:
case O_TCPMSS:
case O_TCPWIN:
case O_TAGGED:
if (cmdlen < 1 || cmdlen > 31)