Major cleanup of the parser and printing routines in an attempt to

render the syntax less ambiguous.

Now rules can be in one of these two forms

	<action> <protocol> from <src> to <dst> [options]
	<action> MAC dst-mac src-mac mac-type [options]

however you can now specify MAC and IP header fields as options e.g.

	ipfw add allow all from any to any mac-type arp
	ipfw add allow all from any to any { dst-ip me or src-ip me }

which makes complex expressions a lot easier to write and parse.
The "all from any to any" part is there just for backward compatibility.

Manpage updated accordingly.
This commit is contained in:
Luigi Rizzo 2002-08-19 04:52:15 +00:00
parent 306fe283a1
commit e706181ba6
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=102087
2 changed files with 378 additions and 212 deletions

View File

@ -671,27 +671,29 @@ Only one level of parentheses is allowed.
Beware that most shells have special meanings for parentheses Beware that most shells have special meanings for parentheses
or braces, so it is advisable to put a \\ in front of them. or braces, so it is advisable to put a \\ in front of them.
.Pp .Pp
The body of a rule must in general comprise a source and destination The body of a rule must in general include a source and destination
addres specifier. addres specifier.
The keyword The keyword
.Ar any .Ar any
can be used in various places to specify that the content of can be used in various places to specify that the content of
a required field is irrelevant. a required field is irrelevant.
.Pp .Pp
The general rule body format is one of the following: The rule body format is one of the following:
.Bd -ragged -offset indent .Bd -ragged -offset indent
.Ar proto .Ar proto
.Cm from Ar src .Cm from Ar src
.Cm to Ar dst .Cm to Ar dst
.Op Ar options .Op Ar options
.br .Pp
.Cm MAC Ar dst-mac src-mac mac-type .Cm MAC Ar dst-mac src-mac Op Cm not
.Op Cm from Ar src Cm to Ar dst .Ar mac-type Op Ar options
.Op Ar options
.Ed .Ed
.Pp .Pp
where the second format allows you to specify MAC header fields where the second format allows you to specify MAC header fields
instead (or in addition) of the IPv4 header fields. instead of IPv4 header fields.
Note that in practice both formats are equivalent, because the
.Ar options
let you specify match patterns for all IP and MAC header fields.
.Pp .Pp
Rule fields have the following meaning: Rule fields have the following meaning:
.Bl -tag -width indent .Bl -tag -width indent
@ -704,15 +706,16 @@ The
or or
.Cm all .Cm all
keywords mean any protocol will match. keywords mean any protocol will match.
.It Ar src No and Ar dst : .It Ar src No and Ar dst : ip-address | { ip-address Cm or ... } Op Ar ports
A single A single
.Ar ip address .Ar ip-address
, or an , or an
.Em or-block .Em or-block
containing one or more of them, containing one or more of them,
optionally followed by optionally followed by
.Em port numbers. .Ar ports
.It Ar ip address : specifiers.
.It Ar ip-address :
An address (or set of addresses) specified in one of the following An address (or set of addresses) specified in one of the following
ways, optionally preceded by a ways, optionally preceded by a
.Cm not .Cm not
@ -758,19 +761,13 @@ within a single rule. Because the matching occurs using a
bitmask, it takes constant time and dramatically reduces bitmask, it takes constant time and dramatically reduces
the complexity of rulesets. the complexity of rulesets.
.El .El
.It port numbers .It Ar ports : Oo Cm not Oc Bro Ar port | port Ns \&- Ns Ar port Ns Brc Op , Ns Ar ...
With protocols which support port numbers (such as TCP and UDP), optional For protocols which support port numbers (such as TCP and UDP), optional
.Cm ports .Cm ports
may be specified as one or more ports or port ranges, separated may be specified as one or more ports or port ranges, separated
by commas but no spaces, and an optional by commas but no spaces, and an optional
.Cm not .Cm not
operator: operator.
.Bd -ragged -offset indent
.Op Cm not
.Brq Ar port | port Ns \&- Ns Ar port Ns
.Op , Ns Ar ...
.Ed
.Pp
The The
.Ql \&- .Ql \&-
notation specifies a range of ports (including boundaries). notation specifies a range of ports (including boundaries).
@ -778,13 +775,12 @@ notation specifies a range of ports (including boundaries).
Service names (from Service names (from
.Pa /etc/services ) .Pa /etc/services )
may be used instead of numeric port values. may be used instead of numeric port values.
The length of the port list is limited to 14 ports or ranges, The length of the port list is limited to 30 ports or ranges,
though you can also use port ranges within an though one can specify larger ranges by using an
.Em or-block .Em or-block
to build essentially unlimited lists: in the
.Pp .Cm options
.Dl "ipfw add allow tcp from any { 1-20,30-50 or 500-600 } to any" section of the rule.
.Pp
.Pp .Pp
A backslash A backslash
.Pq Ql \e .Pq Ql \e
@ -800,7 +796,7 @@ specifications.
See the See the
.Cm frag .Cm frag
option for details on matching fragmented packets. option for details on matching fragmented packets.
.It dst-mac, src-mac .It Ar dst-mac, src-mac
Destination and source MAC addresses, specified as Destination and source MAC addresses, specified as
groups of hex digits separated by commas, and optionally groups of hex digits separated by commas, and optionally
followed by a mask indicating how many bits are significant: followed by a mask indicating how many bits are significant:
@ -811,7 +807,7 @@ Note that the order of MAC addresses (destination first,
source second) is source second) is
the same as on the wire, but the opposite of the one used for the same as on the wire, but the opposite of the one used for
IP addresses. IP addresses.
.It mac-type .It Ar mac-type
The value of the Ethernet Type field, specified in the same way as The value of the Ethernet Type field, specified in the same way as
.Cm port numbers .Cm port numbers
(i.e. one or more comma-separated single values or ranges). (i.e. one or more comma-separated single values or ranges).
@ -823,7 +819,7 @@ are always printed as hexadecimal (unless the
option is used, in which case symbolic resolution will be option is used, in which case symbolic resolution will be
attempted). attempted).
.El .El
.Ss RULE OPTIONS .Ss RULE OPTIONS (MATCH PATTERNS)
Additional match patterns can be used within Additional match patterns can be used within
rules. Zero or more of these so-called rules. Zero or more of these so-called
.Em options .Em options
@ -832,44 +828,31 @@ can be present in a rule, optionally prefixed by the
operand, and possibly grouped into operand, and possibly grouped into
.Em or-blocks . .Em or-blocks .
.Pp .Pp
Note that there is an ambiguity in the syntax: in a rule of The following match patterns can be used (listed in alphabetical order):
the form
.Pp
.Dl "ipfw add allow ip from any to any { in or layer2 }"
.Pp
the or-block could contain either port lists or options.
To remove the ambiguity, one should specify a destination
port, which can be done by either using the keyword
.Cm any
or an empty or-block
.Cm { }
e.g.:
.Pp
.Dl "ipfw add allow ip from any to any any { in or layer2 }"
.Pp
The following options are available:
.Bl -tag -width indent .Bl -tag -width indent
.It Cm bridged .It Cm bridged
Matches only bridged packets. Matches only bridged packets.
.It Cm dst-ip Ar ip address
Matches IP packets whose destination IP is one of the address(es)
specified as argument.
.It Cm dst-port Ar source ports
Matches IP packets whose destination port is one of the port(s)
specified as argument.
.It Cm established .It Cm established
TCP packets only. Matches TCP packets that have the RST or ACK bits set.
Match packets that have the RST or ACK bits set.
.It Cm frag .It Cm frag
Match if the packet is a fragment and this is not the first Matches packets that are fragments and not the first
fragment of the datagram. fragment of an IP datagram. Note that these packets will not have
.Cm frag the next protocol header (e.g. TCP, UDP) so options that look into
may not be used in conjunction with either these headers cannot match.
.Cm tcpflags
or TCP/UDP port specifications.
.It Cm gid Ar group .It Cm gid Ar group
Match all TCP or UDP packets sent by or received for a Matches all TCP or UDP packets sent by or received for a
.Ar group . .Ar group .
A A
.Ar group .Ar group
may be matched by name or identification number. may be specified by name or number.
.It Cm icmptypes Ar types .It Cm icmptypes Ar types
ICMP packets only. Matches ICMP packets whose ICMP type is in the list
Match if the ICMP type is in the list
.Ar types . .Ar types .
The list may be specified as any combination of ranges or The list may be specified as any combination of ranges or
individual types separated by commas. individual types separated by commas.
@ -906,24 +889,25 @@ address mask request
and address mask reply and address mask reply
.Pq Cm 18 . .Pq Cm 18 .
.It Cm in | out .It Cm in | out
Only match incoming or outgoing packets, respectively. Matches incoming or outgoing packets, respectively.
.Cm in .Cm in
and and
.Cm out .Cm out
are mutually exclusive (in fact, are mutually exclusive (in fact,
.Cm out .Cm out
is implemented as is implemented as
.Cm not in .Cm not in Ns No ).
).
.It Cm ipid Ar id .It Cm ipid Ar id
Match if the identification of IP datagram is Matches IP packets whose
.Cm ip_id
field has value
.Ar id . .Ar id .
.It Cm iplen Ar len .It Cm iplen Ar len
Match if the total length of a packet, including header and data, is Matches IP packets whose total length, including header and data, is
.Ar len .Ar len
bytes. bytes.
.It Cm ipoptions Ar spec .It Cm ipoptions Ar spec
Match if the IP header contains the comma separated list of Matches packets whose IP header contains the comma separated list of
options specified in options specified in
.Ar spec . .Ar spec .
The supported IP options are: The supported IP options are:
@ -940,10 +924,12 @@ The absence of a particular option may be denoted
with a with a
.Ql \&! . .Ql \&! .
.It Cm ipprecedence Ar precedence .It Cm ipprecedence Ar precedence
Match if the numeric value of IP datagram's precedence is equal to Matches IP packets whose precedence field is equal to
.Ar precedence . .Ar precedence .
.It Cm iptos Ar spec .It Cm iptos Ar spec
Match if the IP header contains the comma separated list of Matches IP packets whose
.Cm tos
field contains the comma separated list of
service types specified in service types specified in
.Ar spec . .Ar spec .
The supported IP types of service are: The supported IP types of service are:
@ -962,10 +948,10 @@ The absence of a particular type may be denoted
with a with a
.Ql \&! . .Ql \&! .
.It Cm ipttl Ar ttl .It Cm ipttl Ar ttl
Match if the time to live of IP datagram is Matches IP packets whose time to live is
.Ar ttl . .Ar ttl .
.It Cm ipversion Ar ver .It Cm ipversion Ar ver
Match if the IP header version is Matches IP packets whose IP version field is
.Ar ver . .Ar ver .
.It Cm keep-state .It Cm keep-state
Upon a match, the firewall will create a dynamic rule, whose Upon a match, the firewall will create a dynamic rule, whose
@ -987,13 +973,22 @@ set of parameters as specified in the rule.
One or more One or more
of source and destination addresses and ports can be of source and destination addresses and ports can be
specified. specified.
.It Cm { MAC | mac } Ar dst-mac src-mac
Match packets with a given dst-mac and src-mac addresses, specified
in one of the forms described earlier.
.It Cm mac-type Ar mac-type
Matches packets whose
.Ar mac-type
corresponds to one of those specified as argument.
.It Cm proto Ar protocol
Matches packets with the corresponding IPv4 protocol.
.It Cm recv | xmit | via Brq Ar ifX | Ar if Ns Cm * | Ar ipno | Ar any .It Cm recv | xmit | via Brq Ar ifX | Ar if Ns Cm * | Ar ipno | Ar any
Packet must be received, transmitted or be going through, Matches packets received, transmitted or be going through,
respectively, the interface specified by exact name ( respectively, the interface specified by exact name
.Ar ifX .Ns No ( Ar ifX Ns No ),
), by device name ( by device name
.Ar if Ns Cm * .Ns No ( Ar if Ns Ar * Ns No ),
), by IP address, or through some interface. by IP address, or through some interface.
.Pp .Pp
The The
.Cm via .Cm via
@ -1030,10 +1025,15 @@ originating from the local host have no receive interface,
while packets destined for the local host have no transmit while packets destined for the local host have no transmit
interface. interface.
.It Cm setup .It Cm setup
TCP packets only. Matches TCP packets that have the SYN bit set but no ACK bit.
Match packets that have the SYN bit set but no ACK bit.
This is the short form of This is the short form of
.Dq Li tcpflags\ syn,!ack . .Dq Li tcpflags\ syn,!ack .
.It Cm src-ip Ar ip-address
Matches IP packets whose source IP is one of the address(es)
specified as argument.
.It Cm src-port Ar ports
Matches IP packets whose source port is one of the port(s)
specified as argument.
.It Cm tcpack Ar ack .It Cm tcpack Ar ack
TCP packets only. TCP packets only.
Match if the TCP header acknowledgment number field is set to Match if the TCP header acknowledgment number field is set to
@ -1601,6 +1601,21 @@ have in writing your rulesets.
You might want to consider using these features in order to You might want to consider using these features in order to
write your rulesets in a more efficient way. write your rulesets in a more efficient way.
.Bl -tag -width indent .Bl -tag -width indent
.It Handling of non-IPv4 packets
.Nm ipfw1
will silently accept all non-IPv4 packets (which
.Nm ipfw1
will only see when
.Em net.link.ether.bridge_ipfw=1 Ns
).
.Nm ipfw2
will filter all packets (including non-IPv4 ones) according to the ruleset.
To achieve the same behaviour as
.Nm ipfw1
you can use the following as the very first rule in your ruleset:
.Pp
.Dl "ipfw add 1 allow MAC any any not ip"
.Pp
.It Address sets .It Address sets
.Nm ipfw1 .Nm ipfw1
does not supports address sets (those in the form does not supports address sets (those in the form

View File

@ -221,6 +221,8 @@ enum tokens {
TOK_TCPACK, TOK_TCPACK,
TOK_TCPWIN, TOK_TCPWIN,
TOK_ICMPTYPES, TOK_ICMPTYPES,
TOK_MAC,
TOK_MACTYPE,
TOK_PLR, TOK_PLR,
TOK_NOERROR, TOK_NOERROR,
@ -322,6 +324,14 @@ struct _s_x rule_options[] = {
{ "tcpwin", TOK_TCPWIN }, { "tcpwin", TOK_TCPWIN },
{ "icmptype", TOK_ICMPTYPES }, { "icmptype", TOK_ICMPTYPES },
{ "icmptypes", TOK_ICMPTYPES }, { "icmptypes", TOK_ICMPTYPES },
{ "dst-ip", TOK_DSTIP },
{ "src-ip", TOK_SRCIP },
{ "dst-port", TOK_DSTPORT },
{ "src-port", TOK_SRCPORT },
{ "proto", TOK_PROTO },
{ "MAC", TOK_MAC },
{ "mac", TOK_MAC },
{ "mac-type", TOK_MACTYPE },
{ "not", TOK_NOT }, /* pseudo option */ { "not", TOK_NOT }, /* pseudo option */
{ "!", /* escape ? */ TOK_NOT }, /* pseudo option */ { "!", /* escape ? */ TOK_NOT }, /* pseudo option */
@ -393,7 +403,7 @@ print_port(int proto, u_int16_t port)
* XXX todo: add support for mask. * XXX todo: add support for mask.
*/ */
static void static void
print_newports(ipfw_insn_u16 *cmd, int proto) print_newports(ipfw_insn_u16 *cmd, int proto, int opcode)
{ {
u_int16_t *p = cmd->ports; u_int16_t *p = cmd->ports;
int i; int i;
@ -401,6 +411,9 @@ print_newports(ipfw_insn_u16 *cmd, int proto)
if (cmd->o.len & F_NOT) if (cmd->o.len & F_NOT)
printf(" not"); printf(" not");
if (opcode != 0)
printf ("%s", opcode == O_MAC_TYPE ? " mac-type" :
(opcode == O_IP_DSTPORT ? " dst-port" : " src-port"));
for (i = F_LEN((ipfw_insn *)cmd) - 1; i > 0; i--, p += 2) { for (i = F_LEN((ipfw_insn *)cmd) - 1; i > 0; i--, p += 2) {
printf(sep); printf(sep);
print_port(proto, p[0]); print_port(proto, p[0]);
@ -487,10 +500,10 @@ fill_newports(ipfw_insn_u16 *cmd, char *av, int proto)
{ {
u_int16_t *p = cmd->ports; u_int16_t *p = cmd->ports;
int i = 0; int i = 0;
char *s = av;
for (; *av ; i++, p +=2 ) { while (*s) {
u_int16_t a, b; u_int16_t a, b;
char *s;
a = strtoport(av, &s, 0, proto); a = strtoport(av, &s, 0, proto);
if (s == av) /* no parameter */ if (s == av) /* no parameter */
@ -508,11 +521,13 @@ fill_newports(ipfw_insn_u16 *cmd, char *av, int proto)
errx(EX_DATAERR, "invalid separator <%c> in <%s>\n", errx(EX_DATAERR, "invalid separator <%c> in <%s>\n",
*s, av); *s, av);
} }
i++;
p += 2;
av = s+1; av = s+1;
} }
if (i > 0) { if (i > 0) {
if (i+1 > F_LEN_MASK) if (i+1 > F_LEN_MASK)
errx(EX_DATAERR, "too many port range\n"); errx(EX_DATAERR, "too many ports/ranges\n");
cmd->o.len |= i+1; /* leave F_NOT and F_OR untouched */ cmd->o.len |= i+1; /* leave F_NOT and F_OR untouched */
} }
return i; return i;
@ -547,7 +562,7 @@ fill_reject_code(u_short *codep, char *str)
val = strtoul(str, &s, 0); val = strtoul(str, &s, 0);
if (s == str || *s != '\0' || val >= 0x100) if (s == str || *s != '\0' || val >= 0x100)
val = match_token(icmpcodes, str); val = match_token(icmpcodes, str);
if (val <= 0) if (val < 0)
errx(EX_DATAERR, "unknown ICMP unreachable code ``%s''", str); errx(EX_DATAERR, "unknown ICMP unreachable code ``%s''", str);
*codep = val; *codep = val;
return; return;
@ -624,12 +639,12 @@ print_flags(char *name, ipfw_insn *cmd, struct _s_x *list)
* Print the ip address contained in a command. * Print the ip address contained in a command.
*/ */
static void static void
print_ip(ipfw_insn_ip *cmd) print_ip(ipfw_insn_ip *cmd, char *s)
{ {
struct hostent *he = NULL; struct hostent *he = NULL;
int mb; int mb;
printf("%s ", cmd->o.len & F_NOT ? " not": ""); printf("%s%s ", cmd->o.len & F_NOT ? " not": "", s);
if (cmd->o.opcode == O_IP_SRC_ME || cmd->o.opcode == O_IP_DST_ME) { if (cmd->o.opcode == O_IP_SRC_ME || cmd->o.opcode == O_IP_DST_ME) {
printf("me"); printf("me");
@ -741,33 +756,47 @@ print_icmptypes(ipfw_insn_u32 *cmd)
* show_ipfw() prints the body of an ipfw rule. * show_ipfw() prints the body of an ipfw rule.
* Because the standard rule has at least proto src_ip dst_ip, we use * Because the standard rule has at least proto src_ip dst_ip, we use
* a helper function to produce these entries if not provided explicitly. * a helper function to produce these entries if not provided explicitly.
* The first argument is the list of fields we have, the second is
* the list of fields we want to be printed.
* *
* Special case: if we have provided a MAC header, and no IP specs, * Special cases if we have provided a MAC header:
* just leave it alone. * + if the rule does not contain IP addresses/ports, do not print them;
* Also, if we have providea a MAC header and no IP protocol, print it * + if the rule does not contain an IP proto, print "all" instead of "ip";
* as "all" instead of "ip". *
* Once we have 'have_options', IP header fields are printed as options.
*/ */
#define HAVE_PROTO 0x0001 #define HAVE_PROTO 0x0001
#define HAVE_SRCIP 0x0002 #define HAVE_SRCIP 0x0002
#define HAVE_DSTIP 0x0004 #define HAVE_DSTIP 0x0004
#define HAVE_MAC 0x0008 #define HAVE_MAC 0x0008
#define HAVE_MACTYPE 0x0010 #define HAVE_MACTYPE 0x0010
#define HAVE_OPTIONS 0x8000
#define HAVE_IP (HAVE_PROTO | HAVE_SRCIP | HAVE_DSTIP) #define HAVE_IP (HAVE_PROTO | HAVE_SRCIP | HAVE_DSTIP)
static void static void
show_prerequisites(int *flags, int want) show_prerequisites(int *flags, int want, int cmd)
{ {
if ( (*flags & (HAVE_MAC | HAVE_MACTYPE)) == HAVE_MAC) { if ( (*flags & HAVE_IP) == HAVE_IP)
printf(" any"); /* MAC type */ *flags |= HAVE_OPTIONS;
*flags |= HAVE_MACTYPE;
if ( (*flags & (HAVE_MAC|HAVE_MACTYPE|HAVE_OPTIONS)) == HAVE_MAC &&
cmd != O_MAC_TYPE) {
/*
* mac-type was optimized out by the compiler,
* restore it
*/
printf(" any");
*flags |= HAVE_MACTYPE | HAVE_OPTIONS;
return;
}
if ( !(*flags & HAVE_OPTIONS)) {
if ( !(*flags & HAVE_PROTO) && (want & HAVE_PROTO))
printf(" ip");
if ( !(*flags & HAVE_SRCIP) && (want & HAVE_SRCIP))
printf(" from any");
if ( !(*flags & HAVE_DSTIP) && (want & HAVE_DSTIP))
printf(" to any");
} }
if ( !(*flags & HAVE_PROTO) && (want & HAVE_PROTO))
printf( (*flags & HAVE_MAC) ? " all" : " ip");
if ( !(*flags & HAVE_SRCIP) && (want & HAVE_SRCIP))
printf(" from any");
if ( !(*flags & HAVE_DSTIP) && (want & HAVE_DSTIP))
printf(" to any");
*flags |= want; *flags |= want;
} }
@ -818,8 +847,7 @@ show_ipfw(struct ip_fw *rule)
switch(cmd->opcode) { switch(cmd->opcode) {
case O_CHECK_STATE: case O_CHECK_STATE:
printf("check-state"); printf("check-state");
/* avoid printing anything else */ flags = HAVE_IP; /* avoid printing anything else */
flags = HAVE_PROTO|HAVE_SRCIP|HAVE_DSTIP;
break; break;
case O_PROB: case O_PROB:
@ -898,47 +926,59 @@ show_ipfw(struct ip_fw *rule)
else else
printf(" log"); printf(" log");
} }
/* /*
* then print the body * then print the body.
*/ */
if (rule->_pad & 1) { /* empty rules before options */
printf (" all from any to any");
flags |= HAVE_IP | HAVE_OPTIONS;
}
for (l = rule->act_ofs, cmd = rule->cmd ; for (l = rule->act_ofs, cmd = rule->cmd ;
l > 0 ; l -= F_LEN(cmd) , cmd += F_LEN(cmd)) { l > 0 ; l -= F_LEN(cmd) , cmd += F_LEN(cmd)) {
/* useful alias */ /* useful alias */
ipfw_insn_u32 *cmd32 = (ipfw_insn_u32 *)cmd; ipfw_insn_u32 *cmd32 = (ipfw_insn_u32 *)cmd;
show_prerequisites(&flags, 0, cmd->opcode);
switch(cmd->opcode) { switch(cmd->opcode) {
case O_PROBE_STATE: case O_PROBE_STATE:
break; /* no need to print anything here */ break; /* no need to print anything here */
case O_MACADDR2: { case O_MACADDR2: {
ipfw_insn_mac *m = (ipfw_insn_mac *)cmd; ipfw_insn_mac *m = (ipfw_insn_mac *)cmd;
if ( (flags & HAVE_MAC) == 0)
printf(" MAC"); if ((cmd->len & F_OR) && !or_block)
flags |= HAVE_MAC; printf(" {");
if (cmd->len & F_NOT) if (cmd->len & F_NOT)
printf(" not"); printf(" not");
printf(" MAC");
flags |= HAVE_MAC;
print_mac( m->addr, m->mask); print_mac( m->addr, m->mask);
print_mac( m->addr + 6, m->mask + 6); print_mac( m->addr + 6, m->mask + 6);
} }
break; break;
case O_MAC_TYPE: case O_MAC_TYPE:
if ( (flags & HAVE_MAC) == 0) if ((cmd->len & F_OR) && !or_block)
printf(" MAC"); printf(" {");
flags |= (HAVE_MAC | HAVE_MACTYPE); print_newports((ipfw_insn_u16 *)cmd, IPPROTO_ETHERTYPE,
print_newports((ipfw_insn_u16 *)cmd, IPPROTO_ETHERTYPE); (flags & HAVE_OPTIONS) ? cmd->opcode : 0);
flags |= HAVE_MAC | HAVE_MACTYPE | HAVE_OPTIONS;
break; break;
case O_IP_SRC: case O_IP_SRC:
case O_IP_SRC_MASK: case O_IP_SRC_MASK:
case O_IP_SRC_ME: case O_IP_SRC_ME:
case O_IP_SRC_SET: case O_IP_SRC_SET:
show_prerequisites(&flags, HAVE_PROTO); show_prerequisites(&flags, HAVE_PROTO, 0);
if (!(flags & HAVE_SRCIP)) if (!(flags & HAVE_SRCIP))
printf(" from"); printf(" from");
if ((cmd->len & F_OR) && !or_block) if ((cmd->len & F_OR) && !or_block)
printf(" {"); printf(" {");
print_ip((ipfw_insn_ip *)cmd); print_ip((ipfw_insn_ip *)cmd,
(flags & HAVE_OPTIONS) ? " src-ip" : "");
flags |= HAVE_SRCIP; flags |= HAVE_SRCIP;
break; break;
@ -946,23 +986,24 @@ show_ipfw(struct ip_fw *rule)
case O_IP_DST_MASK: case O_IP_DST_MASK:
case O_IP_DST_ME: case O_IP_DST_ME:
case O_IP_DST_SET: case O_IP_DST_SET:
show_prerequisites(&flags, HAVE_PROTO|HAVE_SRCIP); show_prerequisites(&flags, HAVE_PROTO|HAVE_SRCIP, 0);
if (!(flags & HAVE_DSTIP)) if (!(flags & HAVE_DSTIP))
printf(" to"); printf(" to");
if ((cmd->len & F_OR) && !or_block) if ((cmd->len & F_OR) && !or_block)
printf(" {"); printf(" {");
print_ip((ipfw_insn_ip *)cmd); print_ip((ipfw_insn_ip *)cmd,
(flags & HAVE_OPTIONS) ? " dst-ip" : "");
flags |= HAVE_DSTIP; flags |= HAVE_DSTIP;
break; break;
case O_IP_DSTPORT: case O_IP_DSTPORT:
show_prerequisites(&flags, show_prerequisites(&flags, HAVE_IP, 0);
HAVE_PROTO|HAVE_SRCIP|HAVE_DSTIP);
case O_IP_SRCPORT: case O_IP_SRCPORT:
show_prerequisites(&flags, HAVE_PROTO|HAVE_SRCIP); show_prerequisites(&flags, HAVE_PROTO|HAVE_SRCIP, 0);
if ((cmd->len & F_OR) && !or_block) if ((cmd->len & F_OR) && !or_block)
printf(" {"); printf(" {");
print_newports((ipfw_insn_u16 *)cmd, proto); print_newports((ipfw_insn_u16 *)cmd, proto,
(flags & HAVE_OPTIONS) ? cmd->opcode : 0);
break; break;
case O_PROTO: { case O_PROTO: {
@ -974,6 +1015,8 @@ show_ipfw(struct ip_fw *rule)
printf(" not"); printf(" not");
proto = cmd->arg1; proto = cmd->arg1;
pe = getprotobynumber(cmd->arg1); pe = getprotobynumber(cmd->arg1);
if (flags & HAVE_OPTIONS)
printf(" proto");
if (pe) if (pe)
printf(" %s", pe->p_name); printf(" %s", pe->p_name);
else else
@ -983,8 +1026,7 @@ show_ipfw(struct ip_fw *rule)
break; break;
default: /*options ... */ default: /*options ... */
show_prerequisites(&flags, show_prerequisites(&flags, HAVE_IP | HAVE_OPTIONS, 0);
HAVE_PROTO|HAVE_SRCIP|HAVE_DSTIP);
if ((cmd->len & F_OR) && !or_block) if ((cmd->len & F_OR) && !or_block)
printf(" {"); printf(" {");
if (cmd->len & F_NOT && cmd->opcode != O_IN) if (cmd->len & F_NOT && cmd->opcode != O_IN)
@ -1137,7 +1179,7 @@ show_ipfw(struct ip_fw *rule)
or_block = 0; or_block = 0;
} }
} }
show_prerequisites(&flags, HAVE_PROTO|HAVE_SRCIP|HAVE_DSTIP); show_prerequisites(&flags, HAVE_IP, 0);
printf("\n"); printf("\n");
} }
@ -2280,10 +2322,10 @@ fill_cmd(ipfw_insn *cmd, enum ipfw_opcodes opcode, int flags, u_int16_t arg)
static ipfw_insn * static ipfw_insn *
add_mac(ipfw_insn *cmd, int ac, char *av[]) add_mac(ipfw_insn *cmd, int ac, char *av[])
{ {
ipfw_insn_mac *mac; /* also *src */ ipfw_insn_mac *mac;
if (ac <2) if (ac < 2)
errx(EX_DATAERR, "MAC dst src [type]"); errx(EX_DATAERR, "MAC dst src [not] type");
cmd->opcode = O_MACADDR2; cmd->opcode = O_MACADDR2;
cmd->len = (cmd->len & (F_NOT | F_OR)) | F_INSN_SIZE(ipfw_insn_mac); cmd->len = (cmd->len & (F_NOT | F_OR)) | F_INSN_SIZE(ipfw_insn_mac);
@ -2291,17 +2333,84 @@ add_mac(ipfw_insn *cmd, int ac, char *av[])
mac = (ipfw_insn_mac *)cmd; mac = (ipfw_insn_mac *)cmd;
get_mac_addr_mask(av[0], mac->addr, mac->mask); /* dst */ get_mac_addr_mask(av[0], mac->addr, mac->mask); /* dst */
get_mac_addr_mask(av[1], &(mac->addr[6]), &(mac->mask[6])); /* src */ get_mac_addr_mask(av[1], &(mac->addr[6]), &(mac->mask[6])); /* src */
if (ac>2 && strcmp(av[2], "any") != 0) { /* we have a non-null type */
cmd += F_LEN(cmd);
fill_newports((ipfw_insn_u16 *)cmd, av[2], IPPROTO_ETHERTYPE);
cmd->opcode = O_MAC_TYPE;
}
return cmd; return cmd;
} }
static ipfw_insn *
add_mactype(ipfw_insn *cmd, int ac, char *av)
{
if (ac < 1)
errx(EX_DATAERR, "missing MAC type");
if (strcmp(av, "any") != 0) { /* we have a non-null type */
fill_newports((ipfw_insn_u16 *)cmd, av, IPPROTO_ETHERTYPE);
cmd->opcode = O_MAC_TYPE;
return cmd;
} else
return NULL;
}
static ipfw_insn *
add_proto(ipfw_insn *cmd, char *av)
{
struct protoent *pe;
u_char proto = 0;
if (!strncmp(av, "all", strlen(av)))
; /* same as "ip" */
else if ((proto = atoi(av)) > 0)
; /* all done! */
else if ((pe = getprotobyname(av)) != NULL)
proto = pe->p_proto;
else
errx(EX_DATAERR, "invalid protocol ``%s''", av);
if (proto != IPPROTO_IP)
fill_cmd(cmd, O_PROTO, 0, proto);
return cmd;
}
static ipfw_insn *
add_srcip(ipfw_insn *cmd, char *av)
{
fill_ip((ipfw_insn_ip *)cmd, av);
if (cmd->opcode == O_IP_DST_SET) /* set */
cmd->opcode = O_IP_SRC_SET;
else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn)) /* me */
cmd->opcode = O_IP_SRC_ME;
else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn_u32)) /* one IP */
cmd->opcode = O_IP_SRC;
else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn_ip)) /* addr/mask */
cmd->opcode = O_IP_SRC_MASK;
return cmd;
}
static ipfw_insn *
add_dstip(ipfw_insn *cmd, char *av)
{
fill_ip((ipfw_insn_ip *)cmd, av);
if (cmd->opcode == O_IP_DST_SET) /* set */
;
else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn)) /* me */
cmd->opcode = O_IP_DST_ME;
else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn_u32)) /* one IP */
cmd->opcode = O_IP_DST;
else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn_ip)) /* addr/mask */
cmd->opcode = O_IP_DST_MASK;
return cmd;
}
static ipfw_insn *
add_ports(ipfw_insn *cmd, char *av, u_char proto, int opcode)
{
if (!strncmp(av, "any", strlen(av))) {
return NULL;
} else if (fill_newports((ipfw_insn_u16 *)cmd, av, proto)) {
/* XXX todo: check that we have a protocol with ports */
cmd->opcode = opcode;
return cmd;
}
return NULL;
}
/* /*
* Parse arguments and assemble the microinstructions which make up a rule. * Parse arguments and assemble the microinstructions which make up a rule.
* Rules are added into the 'rulebuf' and then copied in the correct order * Rules are added into the 'rulebuf' and then copied in the correct order
@ -2326,13 +2435,13 @@ add(int ac, char *av[])
static u_int32_t rulebuf[255], actbuf[255], cmdbuf[255]; static u_int32_t rulebuf[255], actbuf[255], cmdbuf[255];
ipfw_insn *src, *dst, *cmd, *action, *prev; ipfw_insn *src, *dst, *cmd, *action, *prev;
ipfw_insn *first_cmd; /* first match pattern */
struct ip_fw *rule; struct ip_fw *rule;
/* /*
* various flags used to record that we entered some fields. * various flags used to record that we entered some fields.
*/ */
int have_mac = 0; /* set if we have a MAC address */
ipfw_insn *have_state = NULL; /* check-state or keep-state */ ipfw_insn *have_state = NULL; /* check-state or keep-state */
int i; int i;
@ -2489,7 +2598,7 @@ add(int ac, char *av[])
break; break;
default: default:
errx(EX_DATAERR, "invalid action %s\n", *av); errx(EX_DATAERR, "invalid action %s\n", av[-1]);
} }
action = next_cmd(action); action = next_cmd(action);
@ -2563,43 +2672,51 @@ add(int ac, char *av[])
} \ } \
CLOSE_PAR; CLOSE_PAR;
first_cmd = cmd;
/*
* MAC addresses, optional.
* If we have this, we skip the part "proto from src to dst"
* and jump straight to the option parsing.
*/
NOT_BLOCK;
NEED1("missing protocol");
if (!strncmp(*av, "MAC", strlen(*av)) ||
!strncmp(*av, "mac", strlen(*av))) {
ac--; av++; /* the "MAC" keyword */
add_mac(cmd, ac, av); /* exits in case of errors */
cmd = next_cmd(cmd);
ac -= 2; av += 2; /* dst-mac and src-mac */
NOT_BLOCK;
NEED1("missing mac type");
if (add_mactype(cmd, ac, av[0]))
cmd = next_cmd(cmd);
ac--; av++; /* any or mac-type */
goto read_options;
}
/* /*
* protocol, mandatory * protocol, mandatory
*/ */
OR_START(get_proto); OR_START(get_proto);
NOT_BLOCK; NOT_BLOCK;
NEED1("missing protocol"); NEED1("missing protocol");
{ if (add_proto(cmd, *av)) {
struct protoent *pe; av++; ac--;
if (F_LEN(cmd) == 0) /* plain IP */
if (!strncmp(*av, "all", strlen(*av))) proto = 0;
; /* same as "ip" */ else {
else if (!strncmp(*av, "MAC", strlen(*av))) { proto = cmd->arg1;
cmd = add_mac(cmd, ac-1, av+1); /* exits in case of errors */ prev = cmd;
av += 3; cmd = next_cmd(cmd);
ac -= 3; }
have_mac = 1;
} else if ((proto = atoi(*av)) > 0)
; /* all done! */
else if ((pe = getprotobyname(*av)) != NULL)
proto = pe->p_proto;
else
errx(EX_DATAERR, "invalid protocol ``%s''", *av);
av++; ac--;
if (proto != IPPROTO_IP)
fill_cmd(cmd, O_PROTO, 0, proto);
} }
cmd = next_cmd(cmd);
OR_BLOCK(get_proto); OR_BLOCK(get_proto);
/* /*
* "from", mandatory (unless we have a MAC address) * "from", mandatory
*/ */
if (!ac || strncmp(*av, "from", strlen(*av))) { if (!ac || strncmp(*av, "from", strlen(*av)))
if (have_mac) /* we do not need a "to" address */
goto read_to;
errx(EX_USAGE, "missing ``from''"); errx(EX_USAGE, "missing ``from''");
}
ac--; av++; ac--; av++;
/* /*
@ -2608,51 +2725,33 @@ add(int ac, char *av[])
OR_START(source_ip); OR_START(source_ip);
NOT_BLOCK; /* optional "not" */ NOT_BLOCK; /* optional "not" */
NEED1("missing source address"); NEED1("missing source address");
if (add_srcip(cmd, *av)) {
/* source -- mandatory */ ac--; av++;
fill_ip((ipfw_insn_ip *)cmd, *av); if (F_LEN(cmd) != 0) { /* ! any */
if (cmd->opcode == O_IP_DST_SET) /* set */ prev = cmd;
cmd->opcode = O_IP_SRC_SET; cmd = next_cmd(cmd);
else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn)) /* me */ }
cmd->opcode = O_IP_SRC_ME; }
else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn_u32)) /* one IP */
cmd->opcode = O_IP_SRC;
else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn_ip)) /* addr/mask */
cmd->opcode = O_IP_SRC_MASK;
/* otherwise len will be zero and the command skipped */
ac--; av++;
prev = cmd; /* in case we need to backtrack */
cmd = next_cmd(cmd);
OR_BLOCK(source_ip); OR_BLOCK(source_ip);
OR_START(source_port);
/* /*
* source ports, optional * source ports, optional
*/ */
NOT_BLOCK; /* optional "not" */ NOT_BLOCK; /* optional "not" */
if (ac) { if (ac) {
if (!strncmp(*av, "any", strlen(*av))) { if (!strncmp(*av, "any", strlen(*av)) ||
ac--; add_ports(cmd, *av, proto, O_IP_SRCPORT)) {
av++; ac--; av++;
} else if (fill_newports((ipfw_insn_u16 *)cmd, *av, proto)) { if (F_LEN(cmd) != 0)
/* XXX todo: check that we have a protocol with ports */ cmd = next_cmd(cmd);
cmd->opcode = O_IP_SRCPORT;
ac--;
av++;
cmd = next_cmd(cmd);
} }
} }
OR_BLOCK(source_port);
read_to:
/* /*
* "to", mandatory (unless we have a MAC address * "to", mandatory
*/ */
if (!ac || strncmp(*av, "to", strlen(*av))) { if (!ac || strncmp(*av, "to", strlen(*av)))
if (have_mac)
goto read_options;
errx(EX_USAGE, "missing ``to''"); errx(EX_USAGE, "missing ``to''");
}
av++; ac--; av++; ac--;
/* /*
@ -2661,41 +2760,36 @@ add(int ac, char *av[])
OR_START(dest_ip); OR_START(dest_ip);
NOT_BLOCK; /* optional "not" */ NOT_BLOCK; /* optional "not" */
NEED1("missing dst address"); NEED1("missing dst address");
fill_ip((ipfw_insn_ip *)cmd, *av); if (add_dstip(cmd, *av)) {
if (cmd->opcode == O_IP_DST_SET) /* set */ ac--; av++;
; if (F_LEN(cmd) != 0) { /* ! any */
else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn)) /* me */ prev = cmd;
cmd->opcode = O_IP_DST_ME; cmd = next_cmd(cmd);
else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn_u32)) /* one IP */ }
cmd->opcode = O_IP_DST; }
else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn_ip)) /* addr/mask */
cmd->opcode = O_IP_DST_MASK;
ac--;
av++;
prev = cmd;
cmd = next_cmd(cmd);
OR_BLOCK(dest_ip); OR_BLOCK(dest_ip);
OR_START(dest_port);
/* /*
* dest. ports, optional * dest. ports, optional
*/ */
NOT_BLOCK; /* optional "not" */ NOT_BLOCK; /* optional "not" */
if (ac) { if (ac) {
if (!strncmp(*av, "any", strlen(*av))) { if (!strncmp(*av, "any", strlen(*av)) ||
ac--; add_ports(cmd, *av, proto, O_IP_DSTPORT)) {
av++; ac--; av++;
} else if (fill_newports((ipfw_insn_u16 *)cmd, *av, proto)) { if (F_LEN(cmd) != 0)
/* XXX todo: check that we have a protocol with ports */ cmd = next_cmd(cmd);
cmd->opcode = O_IP_DSTPORT;
ac--;
av++;
cmd += F_LEN(cmd);
} }
} }
OR_BLOCK(dest_port);
read_options: read_options:
if (ac && first_cmd == cmd) {
/*
* nothing specified so far, store in the rule to ease
* printout later.
*/
rule->_pad = 1;
}
prev = NULL; prev = NULL;
while (ac) { while (ac) {
char *s; char *s;
@ -2735,6 +2829,7 @@ add(int ac, char *av[])
if (!open_par) if (!open_par)
errx(EX_USAGE, "+missing \")\"\n"); errx(EX_USAGE, "+missing \")\"\n");
open_par = 0; open_par = 0;
prev = NULL;
break; break;
case TOK_IN: case TOK_IN:
@ -2940,6 +3035,62 @@ add(int ac, char *av[])
} }
break; break;
case TOK_PROTO:
NEED1("missing protocol");
if (add_proto(cmd, *av)) {
proto = cmd->arg1;
ac--; av++;
}
break;
case TOK_SRCIP:
NEED1("missing source IP");
if (add_srcip(cmd, *av)) {
ac--; av++;
}
break;
case TOK_DSTIP:
NEED1("missing destination IP");
if (add_dstip(cmd, *av)) {
ac--; av++;
}
break;
case TOK_SRCPORT:
NEED1("missing source port");
if (!strncmp(*av, "any", strlen(*av)) ||
add_ports(cmd, *av, proto, O_IP_SRCPORT)) {
ac--; av++;
} else
errx(EX_DATAERR, "invalid source port %s", *av);
break;
case TOK_DSTPORT:
NEED1("missing destination port");
if (!strncmp(*av, "any", strlen(*av)) ||
add_ports(cmd, *av, proto, O_IP_DSTPORT)) {
ac--; av++;
} else
errx(EX_DATAERR, "invalid destination port %s",
*av);
break;
case TOK_MAC:
if (ac < 2)
errx(EX_USAGE, "MAC dst-mac src-mac");
if (add_mac(cmd, ac, av)) {
ac -= 2; av += 2;
}
break;
case TOK_MACTYPE:
NEED1("missing mac type");
if (!add_mactype(cmd, ac, *av))
errx(EX_DATAERR, "invalid mac type %s", av);
ac--; av++;
break;
default: default:
errx(EX_USAGE, "unrecognised option [%d] %s\n", i, s); errx(EX_USAGE, "unrecognised option [%d] %s\n", i, s);
} }