aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrey V. Elsukov <ae@FreeBSD.org>2019-06-21 10:54:51 +0000
committerAndrey V. Elsukov <ae@FreeBSD.org>2019-06-21 10:54:51 +0000
commit978f2d17285bf3a0017510c61eb71a2dcae38dbe (patch)
tree3b4f2ef29d6880a58db7336fbeb58e824605f079
parent05fc9d78d783848f63ccf1d1dc54775aab284bc6 (diff)
downloadsrc-978f2d17285bf3a0017510c61eb71a2dcae38dbe.tar.gz
src-978f2d17285bf3a0017510c61eb71a2dcae38dbe.zip
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
Notes
Notes: svn path=/head/; revision=349267
-rw-r--r--sbin/ipfw/ipfw.88
-rw-r--r--sbin/ipfw/ipfw2.c20
-rw-r--r--sbin/ipfw/ipfw2.h1
-rw-r--r--sys/netinet/ip_fw.h1
-rw-r--r--sys/netpfil/ipfw/ip_fw2.c42
-rw-r--r--sys/netpfil/ipfw/ip_fw_sockopt.c5
6 files changed, 65 insertions, 12 deletions
diff --git a/sbin/ipfw/ipfw.8 b/sbin/ipfw/ipfw.8
index fcba4a4b03f2..bcc8017a571b 100644
--- a/sbin/ipfw/ipfw.8
+++ b/sbin/ipfw/ipfw.8
@@ -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
diff --git a/sbin/ipfw/ipfw2.c b/sbin/ipfw/ipfw2.c
index 3e73fa3b8736..4fd977edb91f 100644
--- a/sbin/ipfw/ipfw2.c
+++ b/sbin/ipfw/ipfw2.c
@@ -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 @@ read_options:
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;
diff --git a/sbin/ipfw/ipfw2.h b/sbin/ipfw/ipfw2.h
index e6c209d65bab..215416eecc8a 100644
--- a/sbin/ipfw/ipfw2.h
+++ b/sbin/ipfw/ipfw2.h
@@ -151,6 +151,7 @@ enum tokens {
TOK_TCPOPTS,
TOK_TCPSEQ,
TOK_TCPACK,
+ TOK_TCPMSS,
TOK_TCPWIN,
TOK_ICMPTYPES,
TOK_MAC,
diff --git a/sys/netinet/ip_fw.h b/sys/netinet/ip_fw.h
index de0cc29db1d2..7a01c82ba58b 100644
--- a/sys/netinet/ip_fw.h
+++ b/sys/netinet/ip_fw.h
@@ -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! */
};
diff --git a/sys/netpfil/ipfw/ip_fw2.c b/sys/netpfil/ipfw/ip_fw2.c
index a4a3830132eb..6796ad81611d 100644
--- a/sys/netpfil/ipfw/ip_fw2.c
+++ b/sys/netpfil/ipfw/ip_fw2.c
@@ -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;
diff --git a/sys/netpfil/ipfw/ip_fw_sockopt.c b/sys/netpfil/ipfw/ip_fw_sockopt.c
index a83e75447633..297b01ca7d3d 100644
--- a/sys/netpfil/ipfw/ip_fw_sockopt.c
+++ b/sys/netpfil/ipfw/ip_fw_sockopt.c
@@ -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)