ipfw: make the "frag" keyword accept additional options "mf",

"df", "rf" and "offset".  This allows to match on specific
bits of ip_off field.

For compatibility reasons lack of keyword means "offset".

Reviewed by:	ae
Differential Revision:	https://reviews.freebsd.org/D26021
This commit is contained in:
Gleb Smirnoff 2020-08-11 15:46:22 +00:00
parent 3b44443626
commit 825398f946
3 changed files with 60 additions and 11 deletions

View File

@ -1,7 +1,7 @@
.\"
.\" $FreeBSD$
.\"
.Dd June 21, 2019
.Dd August 10, 2020
.Dt IPFW 8
.Os
.Sh NAME
@ -600,7 +600,7 @@ See Section
By name or address
.It Misc. IP header fields
Version, type of service, datagram length, identification,
fragment flag (non-zero IP offset),
fragmentation flags,
Time To Live
.It IP options
.It IPv6 Extension headers
@ -1602,12 +1602,29 @@ Matches IPv6 packets containing any of the flow labels given in
.Ar labels .
.Ar labels
is a comma separated list of numeric flow labels.
.It Cm frag
Matches packets that are fragments and not the first
fragment of an IP datagram.
Note that these packets will not have
the next protocol header (e.g.\& TCP, UDP) so options that look into
these headers cannot match.
.It Cm frag Ar spec
Matches IPv4 packets whose
.Cm ip_off
field contains the comma separated list of IPv4 fragmentation
options specified in
.Ar spec .
The recognized options are:
.Cm df
.Pq Dv don't fragment ,
.Cm mf
.Pq Dv more fragments ,
.Cm rf
.Pq Dv reserved fragment bit
.Cm offset
.Pq Dv non-zero fragment offset .
The absence of a particular options may be denoted
with a
.Ql \&! .
.Pp
Empty list of options defaults to matching on non-zero fragment offset.
Such rule would match all not the first fragment datagrams,
both IPv4 and IPv6.
This is a backward compatibility with older rulesets.
.It Cm gid Ar group
Matches all TCP or UDP packets sent by or received for a
.Ar group .

View File

@ -168,6 +168,14 @@ static struct _s_x f_iptos[] = {
{ NULL, 0 }
};
static struct _s_x f_ipoff[] = {
{ "rf", IP_RF >> 8 },
{ "df", IP_DF >> 8 },
{ "mf", IP_MF >> 8 },
{ "offset", 0x1 },
{ NULL, 0}
};
struct _s_x f_ipdscp[] = {
{ "af11", IPTOS_DSCP_AF11 >> 2 }, /* 001010 */
{ "af12", IPTOS_DSCP_AF12 >> 2 }, /* 001100 */
@ -1531,7 +1539,7 @@ print_instruction(struct buf_pr *bp, const struct format_opts *fo,
IPPROTO_ETHERTYPE, cmd->opcode);
break;
case O_FRAG:
bprintf(bp, " frag");
print_flags(bp, "frag", cmd, f_ipoff);
break;
case O_FIB:
bprintf(bp, " fib %u", cmd->arg1);
@ -4553,7 +4561,15 @@ compile_rule(char *av[], uint32_t *rbuf, int *rbufsize, struct tidx *tstate)
break;
case TOK_FRAG:
fill_cmd(cmd, O_FRAG, 0, 0);
fill_flags_cmd(cmd, O_FRAG, f_ipoff, *av);
/*
* Compatibility: no argument after "frag"
* keyword equals to "frag offset".
*/
if (cmd->arg1 == 0)
cmd->arg1 = 0x1;
else
av++;
break;
case TOK_LAYER2:

View File

@ -1944,7 +1944,23 @@ do { \
break;
case O_FRAG:
match = (offset != 0);
if (is_ipv4) {
/*
* Since flags_match() works with
* uint8_t we pack ip_off into 8 bits.
* For this match offset is a boolean.
*/
match = flags_match(cmd,
((ntohs(ip->ip_off) & ~IP_OFFMASK)
>> 8) | (offset != 0));
} else {
/*
* Compatiblity: historically bare
* "frag" would match IPv6 fragments.
*/
match = (cmd->arg1 == 0x1 &&
(offset != 0));
}
break;
case O_IN: /* "out" is "not in" */