aboutsummaryrefslogtreecommitdiff
path: root/sbin/ipfw
diff options
context:
space:
mode:
Diffstat (limited to 'sbin/ipfw')
-rw-r--r--sbin/ipfw/Makefile2
-rw-r--r--sbin/ipfw/altq.c6
-rw-r--r--sbin/ipfw/dummynet.c41
-rw-r--r--sbin/ipfw/ipfw.8388
-rw-r--r--sbin/ipfw/ipfw2.c2232
-rw-r--r--sbin/ipfw/ipfw2.h69
-rw-r--r--sbin/ipfw/ipv6.c54
-rw-r--r--sbin/ipfw/main.c4
-rw-r--r--sbin/ipfw/nat.c357
-rw-r--r--sbin/ipfw/tables.c2012
10 files changed, 4078 insertions, 1087 deletions
diff --git a/sbin/ipfw/Makefile b/sbin/ipfw/Makefile
index de27e3ebdedc..9eb4511fb3c9 100644
--- a/sbin/ipfw/Makefile
+++ b/sbin/ipfw/Makefile
@@ -3,7 +3,7 @@
.include <src.opts.mk>
PROG= ipfw
-SRCS= ipfw2.c dummynet.c ipv6.c main.c nat.c
+SRCS= ipfw2.c dummynet.c ipv6.c main.c nat.c tables.c
WARNS?= 2
.if ${MK_PF} != "no"
diff --git a/sbin/ipfw/altq.c b/sbin/ipfw/altq.c
index 8dced11b72b3..8398ab611f23 100644
--- a/sbin/ipfw/altq.c
+++ b/sbin/ipfw/altq.c
@@ -137,15 +137,15 @@ altq_qid_to_name(u_int32_t qid)
}
void
-print_altq_cmd(ipfw_insn_altq *altqptr)
+print_altq_cmd(struct buf_pr *bp, ipfw_insn_altq *altqptr)
{
if (altqptr) {
const char *qname;
qname = altq_qid_to_name(altqptr->qid);
if (qname == NULL)
- printf(" altq ?<%u>", altqptr->qid);
+ bprintf(bp, " altq ?<%u>", altqptr->qid);
else
- printf(" altq %s", qname);
+ bprintf(bp, " altq %s", qname);
}
}
diff --git a/sbin/ipfw/dummynet.c b/sbin/ipfw/dummynet.c
index cb6285323eed..dc95a1988ffb 100644
--- a/sbin/ipfw/dummynet.c
+++ b/sbin/ipfw/dummynet.c
@@ -174,48 +174,44 @@ print_header(struct ipfw_flow_id *id)
}
static void
-list_flow(struct dn_flow *ni, int *print)
+list_flow(struct buf_pr *bp, struct dn_flow *ni)
{
char buff[255];
struct protoent *pe = NULL;
struct in_addr ina;
struct ipfw_flow_id *id = &ni->fid;
- if (*print) {
- print_header(&ni->fid);
- *print = 0;
- }
pe = getprotobynumber(id->proto);
/* XXX: Should check for IPv4 flows */
- printf("%3u%c", (ni->oid.id) & 0xff,
+ bprintf(bp, "%3u%c", (ni->oid.id) & 0xff,
id->extra ? '*' : ' ');
if (!IS_IP6_FLOW_ID(id)) {
if (pe)
- printf("%-4s ", pe->p_name);
+ bprintf(bp, "%-4s ", pe->p_name);
else
- printf("%4u ", id->proto);
+ bprintf(bp, "%4u ", id->proto);
ina.s_addr = htonl(id->src_ip);
- printf("%15s/%-5d ",
+ bprintf(bp, "%15s/%-5d ",
inet_ntoa(ina), id->src_port);
ina.s_addr = htonl(id->dst_ip);
- printf("%15s/%-5d ",
+ bprintf(bp, "%15s/%-5d ",
inet_ntoa(ina), id->dst_port);
} else {
/* Print IPv6 flows */
if (pe != NULL)
- printf("%9s ", pe->p_name);
+ bprintf(bp, "%9s ", pe->p_name);
else
- printf("%9u ", id->proto);
- printf("%7d %39s/%-5d ", id->flow_id6,
+ bprintf(bp, "%9u ", id->proto);
+ bprintf(bp, "%7d %39s/%-5d ", id->flow_id6,
inet_ntop(AF_INET6, &(id->src_ip6), buff, sizeof(buff)),
id->src_port);
- printf(" %39s/%-5d ",
+ bprintf(bp, " %39s/%-5d ",
inet_ntop(AF_INET6, &(id->dst_ip6), buff, sizeof(buff)),
id->dst_port);
}
- pr_u64(&ni->tot_pkts, 4);
- pr_u64(&ni->tot_bytes, 8);
- printf("%2u %4u %3u\n",
+ pr_u64(bp, &ni->tot_pkts, 4);
+ pr_u64(bp, &ni->tot_bytes, 8);
+ bprintf(bp, "%2u %4u %3u",
ni->length, ni->len_bytes, ni->drops);
}
@@ -303,8 +299,10 @@ list_pipes(struct dn_id *oid, struct dn_id *end)
{
char buf[160]; /* pending buffer */
int toPrint = 1; /* print header */
+ struct buf_pr bp;
buf[0] = '\0';
+ bp_alloc(&bp, 4096);
for (; oid != end; oid = O_NEXT(oid, oid->len)) {
if (oid->len < sizeof(*oid))
errx(1, "invalid oid len %d\n", oid->len);
@@ -346,7 +344,12 @@ list_pipes(struct dn_id *oid, struct dn_id *end)
break;
case DN_FLOW:
- list_flow((struct dn_flow *)oid, &toPrint);
+ if (toPrint != 0) {
+ print_header(&((struct dn_flow *)oid)->fid);
+ toPrint = 0;
+ }
+ list_flow(&bp, (struct dn_flow *)oid);
+ printf("%s\n", bp.buf);
break;
case DN_LINK: {
@@ -384,6 +387,8 @@ list_pipes(struct dn_id *oid, struct dn_id *end)
}
flush_buf(buf); // XXX does it really go here ?
}
+
+ bp_free(&bp);
}
/*
diff --git a/sbin/ipfw/ipfw.8 b/sbin/ipfw/ipfw.8
index 5b56b39c84e6..0c6edb2be3c2 100644
--- a/sbin/ipfw/ipfw.8
+++ b/sbin/ipfw/ipfw.8
@@ -1,7 +1,7 @@
.\"
.\" $FreeBSD$
.\"
-.Dd May 31, 2014
+.Dd Aug 13, 2014
.Dt IPFW 8
.Os
.Sh NAME
@@ -48,17 +48,43 @@ in-kernel NAT.
.Brq Cm firewall | altq | one_pass | debug | verbose | dyn_keepalive
.Ss LOOKUP TABLES
.Nm
-.Cm table Ar number Cm add Ar addr Ns Oo / Ns Ar masklen Oc Op Ar value
+.Oo Cm set Ar N Oc Cm table Ar name Cm create Ar create-options
.Nm
-.Cm table Ar number Cm delete Ar addr Ns Op / Ns Ar masklen
+.Oo Cm set Ar N Oc Cm table Ar name Cm destroy
.Nm
-.Cm table
-.Brq Ar number | all
-.Cm flush
+.Oo Cm set Ar N Oc Cm table Ar name Cm modify Ar modify-options
+.Nm
+.Oo Cm set Ar N Oc Cm table Ar name Cm swap Ar name
+.Nm
+.Oo Cm set Ar N Oc Cm table Ar name Cm add Ar table-key Op Ar value
+.Nm
+.Oo Cm set Ar N Oc Cm table Ar name Cm add Op Ar table-key Ar value ...
+.Nm
+.Oo Cm set Ar N Oc Cm table Ar name Cm atomic add Op Ar table-key Ar value ...
+.Nm
+.Oo Cm set Ar N Oc Cm table Ar name Cm delete Op Ar table-key ...
.Nm
-.Cm table
-.Brq Ar number | all
+.Oo Cm set Ar N Oc Cm table Ar name Cm lookup Ar addr
+.Nm
+.Oo Cm set Ar N Oc Cm table Ar name Cm lock
+.Nm
+.Oo Cm set Ar N Oc Cm table Ar name Cm unlock
+.Nm
+.Oo Cm set Ar N Oc Cm table
+.Brq Ar name | all
.Cm list
+.Nm
+.Oo Cm set Ar N Oc Cm table
+.Brq Ar name | all
+.Cm info
+.Nm
+.Oo Cm set Ar N Oc Cm table
+.Brq Ar name | all
+.Cm detail
+.Nm
+.Oo Cm set Ar N Oc Cm table
+.Brq Ar name | all
+.Cm flush
.Ss DUMMYNET CONFIGURATION (TRAFFIC SHAPER AND PACKET SCHEDULER)
.Nm
.Brq Cm pipe | queue | sched
@@ -87,6 +113,13 @@ in-kernel NAT.
.Oc
.Oc
.Ar pathname
+.Ss INTERNAL DIAGNOSTICS
+.Nm
+.Cm internal iflist
+.Nm
+.Cm internal talist
+.Nm
+.Cm internal vlist
.Sh DESCRIPTION
The
.Nm
@@ -822,10 +855,11 @@ It is possible to use the
.Cm tablearg
keyword with a skipto for a
.Em computed
-skipto, but care should be used, as no destination caching
-is possible in this case so the rules are always walked to find it,
-starting from the
-.Cm skipto .
+skipto. Skipto may work either in O(log(N)) or in O(1) depending
+on amount of memory and/or sysctl variables.
+See the
+.Sx SYSCTL VARIABLES
+section for more details.
.It Cm call Ar number | tablearg
The current rule number is saved in the internal stack and
ruleset processing continues with the first rule numbered
@@ -1152,7 +1186,7 @@ with multiple addresses) is provided for convenience only and
its use is discouraged.
.It Ar addr : Oo Cm not Oc Bro
.Cm any | me | me6 |
-.Cm table Ns Pq Ar number Ns Op , Ns Ar value
+.Cm table Ns Pq Ar name Ns Op , Ns Ar value
.Ar | addr-list | addr-set
.Brc
.Bl -tag -width indent
@@ -1164,8 +1198,8 @@ matches any IP address configured on an interface in the system.
matches any IPv6 address configured on an interface in the system.
The address list is evaluated at the time the packet is
analysed.
-.It Cm table Ns Pq Ar number Ns Op , Ns Ar value
-Matches any IPv4 address for which an entry exists in the lookup table
+.It Cm table Ns Pq Ar name Ns Op , Ns Ar value
+Matches any IPv4 or IPv6 address for which an entry exists in the lookup table
.Ar number .
If an optional 32-bit unsigned
.Ar value
@@ -1359,6 +1393,19 @@ and IPsec encapsulated security payload headers
.It Cm fib Ar fibnum
Matches a packet that has been tagged to use
the given FIB (routing table) number.
+.It Cm flow Ar table Ns Pq Ar name Ns Op , Ns Ar value
+Search for the flow entry in lookup table
+.Ar name .
+If not found, the match fails.
+Otherwise, the match succeeds and
+.Cm tablearg
+is set to the value extracted from the table.
+.Pp
+This option can be useful to quickly dispatch traffic based on
+certain packet fields.
+See the
+.Sx LOOKUP TABLES
+section below for more information on lookup tables.
.It Cm flow-id Ar labels
Matches IPv6 packets containing any of the flow labels given in
.Ar labels .
@@ -1550,9 +1597,9 @@ of source and destination addresses and ports can be
specified.
Currently,
only IPv4 flows are supported.
-.It Cm lookup Bro Cm dst-ip | dst-port | src-ip | src-port | uid | jail Brc Ar N
+.It Cm lookup Bro Cm dst-ip | dst-port | src-ip | src-port | uid | jail Brc Ar name
Search an entry in lookup table
-.Ar N
+.Ar name
that matches the field specified as argument.
If not found, the match fails.
Otherwise, the match succeeds and
@@ -1616,13 +1663,19 @@ and they are always printed as hexadecimal (unless the
option is used, in which case symbolic resolution will be attempted).
.It Cm proto Ar protocol
Matches packets with the corresponding IP protocol.
-.It Cm recv | xmit | via Brq Ar ifX | Ar if Ns Cm * | Ar table Ns Pq Ar number Ns Op , Ns Ar value | Ar ipno | Ar any
+.It Cm recv | xmit | via Brq Ar ifX | Ar if Ns Cm * | Ar table Ns Po Ar name Ns Oo , Ns Ar value Oc Pc | Ar ipno | Ar any
Matches packets received, transmitted or going through,
respectively, the interface specified by exact name
.Po Ar ifX Pc ,
by device name
.Po Ar if* Pc ,
by IP address, or through some interface.
+Table
+.Ar name
+may be used to match interface by its kernel ifindex.
+See the
+.Sx LOOKUP TABLES
+section below for more information on lookup tables.
.Pp
The
.Cm via
@@ -1817,15 +1870,35 @@ connected networks instead of all source addresses.
.Sh LOOKUP TABLES
Lookup tables are useful to handle large sparse sets of
addresses or other search keys (e.g., ports, jail IDs, interface names).
-In the rest of this section we will use the term ``address''.
-There may be up to 65535 different lookup tables, numbered 0 to 65534.
+In the rest of this section we will use the term ``key''.
+Table name needs to match the following spec:
+.Ar table-name .
+Tables with the same name can be created in different
+.Ar sets .
+However, rule links to the tables in
+.Ar set 0
+by default.
+This behavior can be controlled by
+.Va net.inet.ip.fw.tables_sets
+variable.
+See the
+.Sx SETS OF RULES
+section for more information.
+There may be up to 65535 different lookup tables.
.Pp
+The following table types are supported:
+.Bl -tag -width indent
+.It Ar table-type : Ar addr | iface | number | flow
+.It Ar table-key : Ar addr Ns Oo / Ns Ar masklen Oc | iface-name | number | flow-spec
+.It Ar flow-spec : Ar flow-field Ns Op , Ns Ar flow-spec
+.It Ar flow-field : src-ip | proto | src-port | dst-ip | dst-port
+.It Cm addr
+matches IPv4 or IPv6 address.
Each entry is represented by an
.Ar addr Ns Op / Ns Ar masklen
and will match all addresses with base
.Ar addr
-(specified as an IPv4/IPv6 address, a hostname or an unsigned integer)
-and mask width of
+(specified as an IPv4/IPv6 address, or a hostname) and mask width of
.Ar masklen
bits.
If
@@ -1833,29 +1906,140 @@ If
is not specified, it defaults to 32 for IPv4 and 128 for IPv6.
When looking up an IP address in a table, the most specific
entry will match.
-Associated with each entry is a 32-bit unsigned
-.Ar value ,
-which can optionally be checked by a rule matching code.
-When adding an entry, if
-.Ar value
-is not specified, it defaults to 0.
-.Pp
-An entry can be added to a table
-.Pq Cm add ,
-or removed from a table
-.Pq Cm delete .
-A table can be examined
-.Pq Cm list
-or flushed
-.Pq Cm flush .
-.Pp
-Internally, each table is stored in a Radix tree, the same way as
-the routing table (see
-.Xr route 4 ) .
+.It Cm iface
+matches interface names.
+Each entry is represented by string treated as interface name.
+Wildcards are not supported.
+.It Cm number
+maches protocol ports, uids/gids or jail IDs.
+Each entry is represented by 32-bit unsigned integer.
+Ranges are not supported.
+.It Cm flow
+Matches packet fields specified by
+.Ar flow
+type suboptions with table entries.
+.El
+.Pp
+Tables require explicit creation via
+.Cm create
+before use.
+.Pp
+The following creation options are supported:
+.Bl -tag -width indent
+.It Ar create-options : Ar create-option | create-options
+.It Ar create-option : Cm type Ar table-type | Cm valtype Ar value-mask | Cm algo Ar algo-desc |
+.Cm limit Ar number | Cm locked
+.It Cm type
+Table key type.
+.It Cm valtype
+Table value mask.
+.It Cm algo
+Table algorithm to use (see below).
+.It Cm limit
+Maximum number of items that may be inserted into table.
+.It Cm locked
+Restrict any table modifications.
+.El
+.Pp
+Some of these options may be modified later via
+.Cm modify
+keyword.
+The following options can be changed:
+.Bl -tag -width indent
+.It Ar modify-options : Ar modify-option | modify-options
+.It Ar modify-option : Cm limit Ar number
+.It Cm limit
+Alter maximum number of items that may be inserted into table.
+.El
+.Pp
+Additionally, table can be locked or unlocked using
+.Cm lock
+or
+.Cm unlock
+commands.
+.Pp
+Tables of the same
+.Ar type
+can be swapped with each other using
+.Cm swap Ar name
+command.
+Swap may fail if tables limits are set and data exchange
+would result in limits hit.
+Operation is performed atomically.
+.Pp
+One or more entries can be added to a table at once using
+.Cm add
+command.
+Addition of all items are performed atomically.
+By default, error in addition of one entry does not influence
+addition of other entries. However, non-zero error code is returned
+in that case.
+Special
+.Cm atomic
+keyword may be specified before
+.Cm add
+to indicate all-or-none add request.
.Pp
-Lookup tables currently support only ports, jail IDs, IPv4/IPv6 addresses
-and interface names.
-Wildcards is not supported for interface names.
+One or more entries can be removed from a table at once using
+.Cm delete
+command.
+By default, error in removal of one entry does not influence
+removing of other entries. However, non-zero error code is returned
+in that case.
+.Pp
+It may be possible to check what entry will be found on particular
+.Ar table-key
+using
+.Cm lookup
+.Ae table-key
+command.
+This functionality is optional and may be unsupported in some algorithms.
+.Pp
+The following operations can be performed on
+.Ar one
+or
+.Cm all
+tables:
+.Bl -tag -width indent
+.It Cm list
+List all entries.
+.It Cm flush
+Removes all entries.
+.It Cm info
+Shows generic table information.
+.It Cm detail
+Shows generic table information and algo-specific data.
+.El
+.Pp
+The following lookup algorithms are supported:
+.Bl -tag -width indent
+.It Ar algo-desc : algo-name | "algo-name algo-data"
+.It Ar algo-name: Ar addr:radix | addr:hash | iface:array | number:array | flow:hash
+.It Cm addr:radix
+Separate Radix trees for IPv4 and IPv6, the same way as the routing table (see
+.Xr route 4 ) .
+Default choice for
+.Ar addr
+type.
+.It Cm addr:hash
+Separate auto-growing hashes for IPv4 and IPv6.
+Accepts entries with the same mask length specified initially via
+.Cm "addr:hash masks=/v4,/v6"
+algorithm creation options.
+Assume /32 and /128 masks by default.
+Search removes host bits (according to mask) from supplied address and checks
+resulting key in appropriate hash.
+Mostly optimized for /64 and byte-ranged IPv6 masks.
+.It Cm iface:array
+Array storing sorted indexes for entries which are presented in the system.
+Optimized for very fast lookup.
+.It Cm number:array
+Array storing sorted u32 numbers.
+.It Cm flow:hash
+Auto-growing hash storing flow entries.
+Search calculates hash on required packet fields and searches for matching
+entries in selected bucket.
+.El
.Pp
The
.Cm tablearg
@@ -1864,6 +2048,39 @@ the argument for a rule action, action parameter or rule option.
This can significantly reduce number of rules in some configurations.
If two tables are used in a rule, the result of the second (destination)
is used.
+.Pp
+Each record may hold one or more values according to
+.Ar value-mask .
+This mask is set on table creation via
+.Cm valtype
+option.
+The following value types are supported:
+.Bl -tag -width indent
+.It Ar value-mask : Ar value-type Ns Op , Ns Ar value-mask
+.It Ar value-type : Ar skipto | pipe | fib | nat | dscp | tag | divert |
+.Ar netgraph | limit | ipv4
+.It Cm skipto
+rule number to jump to.
+.It Cm pipe
+Pipe number to use.
+.It Cm fib
+fib number to match/set.
+.It Cm nat
+nat number to jump to.
+.It Cm dscp
+dscp value to match/set.
+.It Cm tag
+tag number to match/set.
+.It Cm divert
+port number to divert traffic to.
+.It Cm netgraph
+hook number to move packet to.
+.It Cm limit
+maximum number of connections.
+.It Cm ipv4
+IPv4 nexthop to fwd packets to.
+.El
+.Pp
The
.Cm tablearg
argument can be used with the following actions:
@@ -1873,32 +2090,34 @@ action parameters:
rule options:
.Cm limit, tagged.
.Pp
-When used with
-.Cm fwd
-it is possible to supply table entries with values
-that are in the form of IP addresses or hostnames.
-See the
-.Sx EXAMPLES
-Section for example usage of tables and the tablearg keyword.
-.Pp
When used with the
.Cm skipto
action, the user should be aware that the code will walk the ruleset
-up to a rule equal to, or past, the given number,
-and should therefore try keep the
-ruleset compact between the skipto and the target rules.
+up to a rule equal to, or past, the given number.
+.Pp
+See the
+.Sx EXAMPLES
+Section for example usage of tables and the tablearg keyword.
.Sh SETS OF RULES
-Each rule belongs to one of 32 different
+Each rule or table belongs to one of 32 different
.Em sets
, numbered 0 to 31.
Set 31 is reserved for the default rule.
.Pp
-By default, rules are put in set 0, unless you use the
+By default, rules or tables are put in set 0, unless you use the
.Cm set N
-attribute when entering a new rule.
+attribute when adding a new rule or table.
Sets can be individually and atomically enabled or disabled,
so this mechanism permits an easy way to store multiple configurations
of the firewall and quickly (and atomically) switch between them.
+.Pp
+By default, tables from set 0 are referenced when adding rule with
+table opcodes regardless of rule set.
+This behavior can be changed by setting
+.Va net.inet.ip.fw.tables_set
+variable to 1.
+Rule's set will then be used for table references.
+.Pp
The command to enable/disable sets is
.Bd -ragged -offset indent
.Nm
@@ -2968,6 +3187,22 @@ Controls whether bridged packets are passed to
.Nm .
Default is no.
.El
+.Sh INTERNAL DIAGNOSTICS
+There are some commands that may be useful to understand current state
+of certain subsystems inside kernel module.
+These commands provide debugging output which may change without notice.
+.Pp
+Currently the following commands are available as
+.Cm internal
+sub-options:
+.Bl -tag -width indent
+.It Cm iflist
+Lists all interface which are currently tracked by
+.Nm
+with their in-kernel status.
+.It Cm talist
+List all table lookup algorithms currently available.
+.El
.Sh EXAMPLES
There are far too many possible uses of
.Nm
@@ -3220,30 +3455,43 @@ Then we classify traffic using a single rule:
.Dl "ipfw pipe 1 config bw 1000Kbyte/s"
.Dl "ipfw pipe 4 config bw 4000Kbyte/s"
.Dl "..."
-.Dl "ipfw table 1 add 192.168.2.0/24 1"
-.Dl "ipfw table 1 add 192.168.0.0/27 4"
-.Dl "ipfw table 1 add 192.168.0.2 1"
+.Dl "ipfw table T1 create type addr"
+.Dl "ipfw table T1 add 192.168.2.0/24 1"
+.Dl "ipfw table T1 add 192.168.0.0/27 4"
+.Dl "ipfw table T1 add 192.168.0.2 1"
.Dl "..."
-.Dl "ipfw add pipe tablearg ip from table(1) to any"
+.Dl "ipfw add pipe tablearg ip from 'table(T1)' to any"
.Pp
Using the
.Cm fwd
action, the table entries may include hostnames and IP addresses.
.Pp
-.Dl "ipfw table 1 add 192.168.2.0/24 10.23.2.1"
-.Dl "ipfw table 1 add 192.168.0.0/27 router1.dmz"
+.Dl "ipfw table T2 create type addr ftype ip"
+.Dl "ipfw table T2 add 192.168.2.0/24 10.23.2.1"
+.Dl "ipfw table T21 add 192.168.0.0/27 router1.dmz"
.Dl "..."
.Dl "ipfw add 100 fwd tablearg ip from any to table(1)"
.Pp
In the following example per-interface firewall is created:
.Pp
-.Dl "ipfw table 10 add vlan20 12000"
-.Dl "ipfw table 10 add vlan30 13000"
-.Dl "ipfw table 20 add vlan20 22000"
-.Dl "ipfw table 20 add vlan30 23000"
+.Dl "ipfw table IN create type iface valtype skipto,fib"
+.Dl "ipfw table IN add vlan20 12000,12"
+.Dl "ipfw table IN add vlan30 13000,13"
+.Dl "ipfw table OUT create type iface valtype skipto"
+.Dl "ipfw table OUT add vlan20 22000"
+.Dl "ipfw table OUT add vlan30 23000"
+.Dl ".."
+.Dl "ipfw add 100 ipfw setfib tablearg ip from any to any recv 'table(IN)' in"
+.Dl "ipfw add 200 ipfw skipto tablearg ip from any to any recv 'table(IN)' in"
+.Dl "ipfw add 300 ipfw skipto tablearg ip from any to any xmit 'table(OUT)' out"
+.Pp
+The following example illustrate usage of flow tables:
+.Pp
+.Dl "ipfw table fl create type flow:flow:src-ip,proto,dst-ip,dst-port"
+.Dl "ipfw table fl add 2a02:6b8:77::88,tcp,2a02:6b8:77::99,80 11"
+.Dl "ipfw table fl add 10.0.0.1,udp,10.0.0.2,53 12"
.Dl ".."
-.Dl "ipfw add 100 ipfw skipto tablearg ip from any to any recv 'table(10)' in"
-.Dl "ipfw add 200 ipfw skipto tablearg ip from any to any xmit 'table(10)' out"
+.Dl "ipfw add 100 allow ip from any to any flow 'table(fl,11)' recv ix0"
.Ss SETS OF RULES
To add a set of rules atomically, e.g.\& set 18:
.Pp
diff --git a/sbin/ipfw/ipfw2.c b/sbin/ipfw/ipfw2.c
index 25d6afd5febe..2c9846628977 100644
--- a/sbin/ipfw/ipfw2.c
+++ b/sbin/ipfw/ipfw2.c
@@ -35,6 +35,7 @@
#include <netdb.h>
#include <pwd.h>
#include <stdio.h>
+#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <sysexits.h>
@@ -56,16 +57,22 @@
struct cmdline_opts co; /* global options */
+struct format_opts {
+ int bcwidth;
+ int pcwidth;
+ int show_counters;
+ uint32_t set_mask; /* enabled sets mask */
+ uint32_t flags; /* request flags */
+ uint32_t first; /* first rule to request */
+ uint32_t last; /* last rule to request */
+ uint32_t dcnt; /* number of dynamic states */
+ ipfw_obj_ctlv *tstate; /* table state data */
+};
+
int resvd_set_number = RESVD_SET;
int ipfw_socket = -1;
-uint32_t ipfw_tables_max = 0; /* Number of tables supported by kernel */
-
-#ifndef s6_addr32
-#define s6_addr32 __u6_addr.__u6_addr32
-#endif
-
#define CHECK_LENGTH(v, len) do { \
if ((v) < (len)) \
errx(EX_DATAERR, "Rule too long"); \
@@ -86,7 +93,7 @@ uint32_t ipfw_tables_max = 0; /* Number of tables supported by kernel */
if (!av[0]) \
errx(EX_USAGE, "%s: missing argument", match_value(s_x, tok)); \
if (_substrcmp(*av, "tablearg") == 0) { \
- arg = IP_FW_TABLEARG; \
+ arg = IP_FW_TARG; \
break; \
} \
\
@@ -104,24 +111,13 @@ uint32_t ipfw_tables_max = 0; /* Number of tables supported by kernel */
errx(EX_DATAERR, "%s: argument is out of range (%u..%u): %s", \
match_value(s_x, tok), min, max, *av); \
\
- if (_xval == IP_FW_TABLEARG) \
+ if (_xval == IP_FW_TARG) \
errx(EX_DATAERR, "%s: illegal argument value: %s", \
match_value(s_x, tok), *av); \
arg = _xval; \
} \
} while (0)
-static void
-PRINT_UINT_ARG(const char *str, uint32_t arg)
-{
- if (str != NULL)
- printf("%s",str);
- if (arg == IP_FW_TABLEARG)
- printf("tablearg");
- else
- printf("%u", arg);
-}
-
static struct _s_x f_tcpflags[] = {
{ "syn", TH_SYN },
{ "fin", TH_FIN },
@@ -169,7 +165,7 @@ static struct _s_x f_iptos[] = {
{ NULL, 0 }
};
-static struct _s_x f_ipdscp[] = {
+struct _s_x f_ipdscp[] = {
{ "af11", IPTOS_DSCP_AF11 >> 2 }, /* 001010 */
{ "af12", IPTOS_DSCP_AF12 >> 2 }, /* 001100 */
{ "af13", IPTOS_DSCP_AF13 >> 2 }, /* 001110 */
@@ -357,6 +353,7 @@ static struct _s_x rule_options[] = {
{ "src-ipv6", TOK_SRCIP6},
{ "src-ip6", TOK_SRCIP6},
{ "lookup", TOK_LOOKUP},
+ { "flow", TOK_FLOW},
{ "//", TOK_COMMENT },
{ "not", TOK_NOT }, /* pseudo option */
@@ -370,6 +367,103 @@ static struct _s_x rule_options[] = {
{ NULL, 0 } /* terminator */
};
+void bprint_uint_arg(struct buf_pr *bp, const char *str, uint32_t arg);
+static int ipfw_get_config(struct cmdline_opts *co, struct format_opts *fo,
+ ipfw_cfg_lheader **pcfg, size_t *psize);
+static int ipfw_show_config(struct cmdline_opts *co, struct format_opts *fo,
+ ipfw_cfg_lheader *cfg, size_t sz, int ac, char **av);
+static void ipfw_list_tifaces(void);
+
+/*
+ * Simple string buffer API.
+ * Used to simplify buffer passing between function and for
+ * transparent overrun handling.
+ */
+
+/*
+ * Allocates new buffer of given size @sz.
+ *
+ * Returns 0 on success.
+ */
+int
+bp_alloc(struct buf_pr *b, size_t size)
+{
+ memset(b, 0, sizeof(struct buf_pr));
+
+ if ((b->buf = calloc(1, size)) == NULL)
+ return (ENOMEM);
+
+ b->ptr = b->buf;
+ b->size = size;
+ b->avail = b->size;
+
+ return (0);
+}
+
+void
+bp_free(struct buf_pr *b)
+{
+
+ free(b->buf);
+}
+
+/*
+ * Flushes buffer so new writer start from beginning.
+ */
+void
+bp_flush(struct buf_pr *b)
+{
+
+ b->ptr = b->buf;
+ b->avail = b->size;
+}
+
+/*
+ * Print message specified by @format and args.
+ * Automatically manage buffer space and transparently handle
+ * buffer overruns.
+ *
+ * Returns number of bytes that should have been printed.
+ */
+int
+bprintf(struct buf_pr *b, char *format, ...)
+{
+ va_list args;
+ int i;
+
+ va_start(args, format);
+
+ i = vsnprintf(b->ptr, b->avail, format, args);
+ va_end(args);
+
+ if (i > b->avail || i < 0) {
+ /* Overflow or print error */
+ b->avail = 0;
+ } else {
+ b->ptr += i;
+ b->avail -= i;
+ }
+
+ b->needed += i;
+
+ return (i);
+}
+
+/*
+ * Special values printer for tablearg-aware opcodes.
+ */
+void
+bprint_uint_arg(struct buf_pr *bp, const char *str, uint32_t arg)
+{
+
+ if (str != NULL)
+ bprintf(bp, "%s", str);
+ if (arg == IP_FW_TARG)
+ bprintf(bp, "tablearg");
+ else
+ bprintf(bp, "%u", arg);
+}
+
/*
* Helper routine to print a possibly unaligned uint64_t on
* various platform. If width > 0, print the value with
@@ -377,7 +471,7 @@ static struct _s_x rule_options[] = {
* otherwise, return the required width.
*/
int
-pr_u64(uint64_t *pd, int width)
+pr_u64(struct buf_pr *b, uint64_t *pd, int width)
{
#ifdef TCC
#define U64_FMT "I64"
@@ -390,11 +484,12 @@ pr_u64(uint64_t *pd, int width)
bcopy (pd, &u, sizeof(u));
d = u;
return (width > 0) ?
- printf("%*" U64_FMT " ", width, d) :
+ bprintf(b, "%*" U64_FMT " ", width, d) :
snprintf(NULL, 0, "%" U64_FMT, d) ;
#undef U64_FMT
}
+
void *
safe_calloc(size_t number, size_t size)
{
@@ -416,6 +511,26 @@ safe_realloc(void *ptr, size_t size)
}
/*
+ * Compare things like interface or table names.
+ */
+int
+stringnum_cmp(const char *a, const char *b)
+{
+ int la, lb;
+
+ la = strlen(a);
+ lb = strlen(b);
+
+ if (la > lb)
+ return (1);
+ else if (la < lb)
+ return (-01);
+
+ return (strcmp(a, b));
+}
+
+
+/*
* conditionally runs the command.
* Selected options or negative -> getsockopt
*/
@@ -448,20 +563,18 @@ do_cmd(int optname, void *optval, uintptr_t optlen)
}
/*
- * do_setcmd3 - pass ipfw control cmd to kernel
+ * do_set3 - pass ipfw control cmd to kernel
* @optname: option name
* @optval: pointer to option data
* @optlen: option length
*
- * Function encapsulates option value in IP_FW3 socket option
- * and calls setsockopt().
- * Function returns 0 on success or -1 otherwise.
+ * Assumes op3 header is already embedded.
+ * Calls setsockopt() with IP_FW3 as kernel-visible opcode.
+ * Returns 0 on success or errno otherwise.
*/
-static int
-do_setcmd3(int optname, void *optval, socklen_t optlen)
+int
+do_set3(int optname, ip_fw3_opheader *op3, uintptr_t optlen)
{
- socklen_t len;
- ip_fw3_opheader *op3;
if (co.test_only)
return (0);
@@ -471,14 +584,40 @@ do_setcmd3(int optname, void *optval, socklen_t optlen)
if (ipfw_socket < 0)
err(EX_UNAVAILABLE, "socket");
- len = sizeof(ip_fw3_opheader) + optlen;
- op3 = alloca(len);
- /* Zero reserved fields */
- memset(op3, 0, sizeof(ip_fw3_opheader));
- memcpy(op3 + 1, optval, optlen);
op3->opcode = optname;
- return setsockopt(ipfw_socket, IPPROTO_IP, IP_FW3, op3, len);
+ return (setsockopt(ipfw_socket, IPPROTO_IP, IP_FW3, op3, optlen));
+}
+
+/*
+ * do_get3 - pass ipfw control cmd to kernel
+ * @optname: option name
+ * @optval: pointer to option data
+ * @optlen: pointer to option length
+ *
+ * Assumes op3 header is already embedded.
+ * Calls getsockopt() with IP_FW3 as kernel-visible opcode.
+ * Returns 0 on success or errno otherwise.
+ */
+int
+do_get3(int optname, ip_fw3_opheader *op3, size_t *optlen)
+{
+ int error;
+
+ if (co.test_only)
+ return (0);
+
+ if (ipfw_socket == -1)
+ ipfw_socket = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
+ if (ipfw_socket < 0)
+ err(EX_UNAVAILABLE, "socket");
+
+ op3->opcode = optname;
+
+ error = getsockopt(ipfw_socket, IPPROTO_IP, IP_FW3, op3,
+ (socklen_t *)optlen);
+
+ return (error);
}
/**
@@ -494,7 +633,38 @@ match_token(struct _s_x *table, char *string)
for (pt = table ; i && pt->s != NULL ; pt++)
if (strlen(pt->s) == i && !bcmp(string, pt->s, i))
return pt->x;
- return -1;
+ return (-1);
+}
+
+/**
+ * match_token takes a table and a string, returns the value associated
+ * with the string for the best match.
+ *
+ * Returns:
+ * value from @table for matched records
+ * -1 for non-matched records
+ * -2 if more than one records match @string.
+ */
+int
+match_token_relaxed(struct _s_x *table, char *string)
+{
+ struct _s_x *pt, *m;
+ int i, c;
+
+ i = strlen(string);
+ c = 0;
+
+ for (pt = table ; i != 0 && pt->s != NULL ; pt++) {
+ if (strncmp(pt->s, string, i) != 0)
+ continue;
+ m = pt;
+ c++;
+ }
+
+ if (c == 1)
+ return (m->x);
+
+ return (c > 0 ? -2: -1);
}
/**
@@ -510,6 +680,78 @@ match_value(struct _s_x *p, int value)
return NULL;
}
+size_t
+concat_tokens(char *buf, size_t bufsize, struct _s_x *table, char *delimiter)
+{
+ struct _s_x *pt;
+ int l;
+ size_t sz;
+
+ for (sz = 0, pt = table ; pt->s != NULL; pt++) {
+ l = snprintf(buf + sz, bufsize - sz, "%s%s",
+ (sz == 0) ? "" : delimiter, pt->s);
+ sz += l;
+ bufsize += l;
+ if (sz > bufsize)
+ return (bufsize);
+ }
+
+ return (sz);
+}
+
+/*
+ * helper function to process a set of flags and set bits in the
+ * appropriate masks.
+ */
+int
+fill_flags(struct _s_x *flags, char *p, char **e, uint32_t *set,
+ uint32_t *clear)
+{
+ char *q; /* points to the separator */
+ int val;
+ uint32_t *which; /* mask we are working on */
+
+ while (p && *p) {
+ if (*p == '!') {
+ p++;
+ which = clear;
+ } else
+ which = set;
+ q = strchr(p, ',');
+ if (q)
+ *q++ = '\0';
+ val = match_token(flags, p);
+ if (val <= 0) {
+ if (e != NULL)
+ *e = p;
+ return (-1);
+ }
+ *which |= (uint32_t)val;
+ p = q;
+ }
+ return (0);
+}
+
+void
+print_flags_buffer(char *buf, size_t sz, struct _s_x *list, uint32_t set)
+{
+ char const *comma = "";
+ int i, l;
+
+ for (i = 0; list[i].x != 0; i++) {
+ if ((set & list[i].x) == 0)
+ continue;
+
+ set &= ~list[i].x;
+ l = snprintf(buf, sz, "%s%s", comma, list[i].s);
+ if (l >= sz)
+ return;
+ comma = ",";
+ buf += l;
+ sz -=l;
+ }
+}
+
/*
* _substrcmp takes two strings and returns 1 if they do not match,
* and 0 if they match exactly or the first string is a sub-string
@@ -564,16 +806,16 @@ _substrcmp2(const char *str1, const char* str2, const char* str3)
* prints one port, symbolic or numeric
*/
static void
-print_port(int proto, uint16_t port)
+print_port(struct buf_pr *bp, int proto, uint16_t port)
{
if (proto == IPPROTO_ETHERTYPE) {
char const *s;
if (co.do_resolv && (s = match_value(ether_types, port)) )
- printf("%s", s);
+ bprintf(bp, "%s", s);
else
- printf("0x%04x", port);
+ bprintf(bp, "0x%04x", port);
} else {
struct servent *se = NULL;
if (co.do_resolv) {
@@ -582,9 +824,9 @@ print_port(int proto, uint16_t port)
se = getservbyport(htons(port), pe ? pe->p_name : NULL);
}
if (se)
- printf("%s", se->s_name);
+ bprintf(bp, "%s", se->s_name);
else
- printf("%d", port);
+ bprintf(bp, "%d", port);
}
}
@@ -606,7 +848,7 @@ static struct _s_x _port_name[] = {
* XXX todo: add support for mask.
*/
static void
-print_newports(ipfw_insn_u16 *cmd, int proto, int opcode)
+print_newports(struct buf_pr *bp, ipfw_insn_u16 *cmd, int proto, int opcode)
{
uint16_t *p = cmd->ports;
int i;
@@ -616,15 +858,15 @@ print_newports(ipfw_insn_u16 *cmd, int proto, int opcode)
sep = match_value(_port_name, opcode);
if (sep == NULL)
sep = "???";
- printf (" %s", sep);
+ bprintf(bp, " %s", sep);
}
sep = " ";
for (i = F_LEN((ipfw_insn *)cmd) - 1; i > 0; i--, p += 2) {
- printf("%s", sep);
- print_port(proto, p[0]);
+ bprintf(bp, "%s", sep);
+ print_port(bp, proto, p[0]);
if (p[0] != p[1]) {
- printf("-");
- print_port(proto, p[1]);
+ bprintf(bp, "-");
+ print_port(bp, proto, p[1]);
}
sep = ",";
}
@@ -824,14 +1066,14 @@ fill_reject_code(u_short *codep, char *str)
}
static void
-print_reject_code(uint16_t code)
+print_reject_code(struct buf_pr *bp, uint16_t code)
{
- char const *s = match_value(icmpcodes, code);
+ char const *s;
- if (s != NULL)
- printf("unreach %s", s);
+ if ((s = match_value(icmpcodes, code)) != NULL)
+ bprintf(bp, "unreach %s", s);
else
- printf("unreach %u", code);
+ bprintf(bp, "unreach %u", code);
}
/*
@@ -864,7 +1106,8 @@ contigmask(uint8_t *p, int len)
* There is a specialized check for f_tcpflags.
*/
static void
-print_flags(char const *name, ipfw_insn *cmd, struct _s_x *list)
+print_flags(struct buf_pr *bp, char const *name, ipfw_insn *cmd,
+ struct _s_x *list)
{
char const *comma = "";
int i;
@@ -872,34 +1115,38 @@ print_flags(char const *name, ipfw_insn *cmd, struct _s_x *list)
uint8_t clear = (cmd->arg1 >> 8) & 0xff;
if (list == f_tcpflags && set == TH_SYN && clear == TH_ACK) {
- printf(" setup");
+ bprintf(bp, " setup");
return;
}
- printf(" %s ", name);
+ bprintf(bp, " %s ", name);
for (i=0; list[i].x != 0; i++) {
if (set & list[i].x) {
set &= ~list[i].x;
- printf("%s%s", comma, list[i].s);
+ bprintf(bp, "%s%s", comma, list[i].s);
comma = ",";
}
if (clear & list[i].x) {
clear &= ~list[i].x;
- printf("%s!%s", comma, list[i].s);
+ bprintf(bp, "%s!%s", comma, list[i].s);
comma = ",";
}
}
}
+
/*
* Print the ip address contained in a command.
*/
static void
-print_ip(ipfw_insn_ip *cmd, char const *s)
+print_ip(struct buf_pr *bp, struct format_opts *fo, ipfw_insn_ip *cmd,
+ char const *s)
{
struct hostent *he = NULL;
+ struct in_addr *ia;
uint32_t len = F_LEN((ipfw_insn *)cmd);
uint32_t *a = ((ipfw_insn_u32 *)cmd)->d;
+ char *t;
if (cmd->o.opcode == O_IP_DST_LOOKUP && len > F_INSN_SIZE(ipfw_insn_u32)) {
uint32_t d = a[1];
@@ -907,22 +1154,24 @@ print_ip(ipfw_insn_ip *cmd, char const *s)
if (d < sizeof(lookup_key)/sizeof(lookup_key[0]))
arg = match_value(rule_options, lookup_key[d]);
- printf("%s lookup %s %d", cmd->o.len & F_NOT ? " not": "",
- arg, cmd->o.arg1);
+ t = table_search_ctlv(fo->tstate, ((ipfw_insn *)cmd)->arg1);
+ bprintf(bp, "%s lookup %s %s", cmd->o.len & F_NOT ? " not": "",
+ arg, t);
return;
}
- printf("%s%s ", cmd->o.len & F_NOT ? " not": "", s);
+ bprintf(bp, "%s%s ", cmd->o.len & F_NOT ? " not": "", s);
if (cmd->o.opcode == O_IP_SRC_ME || cmd->o.opcode == O_IP_DST_ME) {
- printf("me");
+ bprintf(bp, "me");
return;
}
if (cmd->o.opcode == O_IP_SRC_LOOKUP ||
cmd->o.opcode == O_IP_DST_LOOKUP) {
- printf("table(%u", ((ipfw_insn *)cmd)->arg1);
+ t = table_search_ctlv(fo->tstate, ((ipfw_insn *)cmd)->arg1);
+ bprintf(bp, "table(%s", t);
if (len == F_INSN_SIZE(ipfw_insn_u32))
- printf(",%u", *a);
- printf(")");
+ bprintf(bp, ",%u", *a);
+ bprintf(bp, ")");
return;
}
if (cmd->o.opcode == O_IP_SRC_SET || cmd->o.opcode == O_IP_DST_SET) {
@@ -933,7 +1182,7 @@ print_ip(ipfw_insn_ip *cmd, char const *s)
x = cmd->o.arg1 - 1;
x = htonl( ~x );
cmd->addr.s_addr = htonl(cmd->addr.s_addr);
- printf("%s/%d", inet_ntoa(cmd->addr),
+ bprintf(bp, "%s/%d", inet_ntoa(cmd->addr),
contigmask((uint8_t *)&x, 32));
x = cmd->addr.s_addr = htonl(cmd->addr.s_addr);
x &= 0xff; /* base */
@@ -948,14 +1197,14 @@ print_ip(ipfw_insn_ip *cmd, char const *s)
for (j=i+1; j < cmd->o.arg1; j++)
if (!(map[ j/32] & (1<<(j & 31))))
break;
- printf("%c%d", comma, i+x);
+ bprintf(bp, "%c%d", comma, i+x);
if (j>i+2) { /* range has at least 3 elements */
- printf("-%d", j-1+x);
+ bprintf(bp, "-%d", j-1+x);
i = j-1;
}
comma = ',';
}
- printf("}");
+ bprintf(bp, "}");
return;
}
/*
@@ -970,18 +1219,20 @@ print_ip(ipfw_insn_ip *cmd, char const *s)
if (mb == 32 && co.do_resolv)
he = gethostbyaddr((char *)&(a[0]), sizeof(u_long), AF_INET);
if (he != NULL) /* resolved to name */
- printf("%s", he->h_name);
+ bprintf(bp, "%s", he->h_name);
else if (mb == 0) /* any */
- printf("any");
+ bprintf(bp, "any");
else { /* numeric IP followed by some kind of mask */
- printf("%s", inet_ntoa( *((struct in_addr *)&a[0]) ) );
- if (mb < 0)
- printf(":%s", inet_ntoa( *((struct in_addr *)&a[1]) ) );
- else if (mb < 32)
- printf("/%d", mb);
+ ia = (struct in_addr *)&a[0];
+ bprintf(bp, "%s", inet_ntoa(*ia));
+ if (mb < 0) {
+ ia = (struct in_addr *)&a[1];
+ bprintf(bp, ":%s", inet_ntoa(*ia));
+ } else if (mb < 32)
+ bprintf(bp, "/%d", mb);
}
if (len > 1)
- printf(",");
+ bprintf(bp, ",");
}
}
@@ -989,21 +1240,21 @@ print_ip(ipfw_insn_ip *cmd, char const *s)
* prints a MAC address/mask pair
*/
static void
-print_mac(uint8_t *addr, uint8_t *mask)
+print_mac(struct buf_pr *bp, uint8_t *addr, uint8_t *mask)
{
int l = contigmask(mask, 48);
if (l == 0)
- printf(" any");
+ bprintf(bp, " any");
else {
- printf(" %02x:%02x:%02x:%02x:%02x:%02x",
+ bprintf(bp, " %02x:%02x:%02x:%02x:%02x:%02x",
addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
if (l == -1)
- printf("&%02x:%02x:%02x:%02x:%02x:%02x",
+ bprintf(bp, "&%02x:%02x:%02x:%02x:%02x:%02x",
mask[0], mask[1], mask[2],
mask[3], mask[4], mask[5]);
else if (l < 48)
- printf("/%d", l);
+ bprintf(bp, "/%d", l);
}
}
@@ -1032,38 +1283,38 @@ fill_icmptypes(ipfw_insn_u32 *cmd, char *av)
}
static void
-print_icmptypes(ipfw_insn_u32 *cmd)
+print_icmptypes(struct buf_pr *bp, ipfw_insn_u32 *cmd)
{
int i;
char sep= ' ';
- printf(" icmptypes");
+ bprintf(bp, " icmptypes");
for (i = 0; i < 32; i++) {
if ( (cmd->d[0] & (1 << (i))) == 0)
continue;
- printf("%c%d", sep, i);
+ bprintf(bp, "%c%d", sep, i);
sep = ',';
}
}
static void
-print_dscp(ipfw_insn_u32 *cmd)
+print_dscp(struct buf_pr *bp, ipfw_insn_u32 *cmd)
{
int i, c;
uint32_t *v;
char sep= ' ';
const char *code;
- printf(" dscp");
+ bprintf(bp, " dscp");
i = 0;
c = 0;
v = cmd->d;
while (i < 64) {
if (*v & (1 << i)) {
if ((code = match_value(f_ipdscp, i)) != NULL)
- printf("%c%s", sep, code);
+ bprintf(bp, "%c%s", sep, code);
else
- printf("%c%d", sep, i);
+ bprintf(bp, "%c%d", sep, i);
sep = ',';
}
@@ -1094,7 +1345,7 @@ print_dscp(ipfw_insn_u32 *cmd)
#define HAVE_OPTIONS 0x8000
static void
-show_prerequisites(int *flags, int want, int cmd)
+show_prerequisites(struct buf_pr *bp, int *flags, int want, int cmd)
{
(void)cmd; /* UNUSED */
if (co.comment_only)
@@ -1105,22 +1356,23 @@ show_prerequisites(int *flags, int want, int cmd)
if ( !(*flags & HAVE_OPTIONS)) {
if ( !(*flags & HAVE_PROTO) && (want & HAVE_PROTO)) {
if ( (*flags & HAVE_PROTO4))
- printf(" ip4");
+ bprintf(bp, " ip4");
else if ( (*flags & HAVE_PROTO6))
- printf(" ip6");
+ bprintf(bp, " ip6");
else
- printf(" ip");
+ bprintf(bp, " ip");
}
if ( !(*flags & HAVE_SRCIP) && (want & HAVE_SRCIP))
- printf(" from any");
+ bprintf(bp, " from any");
if ( !(*flags & HAVE_DSTIP) && (want & HAVE_DSTIP))
- printf(" to any");
+ bprintf(bp, " to any");
}
*flags |= want;
}
static void
-show_ipfw(struct ip_fw *rule, int pcwidth, int bcwidth)
+show_static_rule(struct cmdline_opts *co, struct format_opts *fo,
+ struct buf_pr *bp, struct ip_fw_rule *rule, struct ip_fw_bcounter *cntr)
{
static int twidth = 0;
int l;
@@ -1131,26 +1383,26 @@ show_ipfw(struct ip_fw *rule, int pcwidth, int bcwidth)
ipfw_insn_log *logptr = NULL; /* set if we find an O_LOG */
ipfw_insn_altq *altqptr = NULL; /* set if we find an O_ALTQ */
int or_block = 0; /* we are in an or block */
- uint32_t set_disable;
+ uint32_t uval;
- bcopy(&rule->next_rule, &set_disable, sizeof(set_disable));
-
- if (set_disable & (1 << rule->set)) { /* disabled */
- if (!co.show_sets)
+ if ((fo->set_mask & (1 << rule->set)) == 0) {
+ /* disabled mask */
+ if (!co->show_sets)
return;
else
- printf("# DISABLED ");
+ bprintf(bp, "# DISABLED ");
}
- printf("%05u ", rule->rulenum);
+ bprintf(bp, "%05u ", rule->rulenum);
- if (pcwidth > 0 || bcwidth > 0) {
- pr_u64(&rule->pcnt, pcwidth);
- pr_u64(&rule->bcnt, bcwidth);
+ /* Print counters if enabled */
+ if (fo->pcwidth > 0 || fo->bcwidth > 0) {
+ pr_u64(bp, &cntr->pcnt, fo->pcwidth);
+ pr_u64(bp, &cntr->bcnt, fo->bcwidth);
}
- if (co.do_time == 2)
- printf("%10u ", rule->timestamp);
- else if (co.do_time == 1) {
+ if (co->do_time == 2)
+ bprintf(bp, "%10u ", cntr->timestamp);
+ else if (co->do_time == 1) {
char timestr[30];
time_t t = (time_t)0;
@@ -1159,19 +1411,19 @@ show_ipfw(struct ip_fw *rule, int pcwidth, int bcwidth)
*strchr(timestr, '\n') = '\0';
twidth = strlen(timestr);
}
- if (rule->timestamp) {
- t = _long_to_time(rule->timestamp);
+ if (cntr->timestamp > 0) {
+ t = _long_to_time(cntr->timestamp);
strcpy(timestr, ctime(&t));
*strchr(timestr, '\n') = '\0';
- printf("%s ", timestr);
+ bprintf(bp, "%s ", timestr);
} else {
- printf("%*s", twidth, " ");
+ bprintf(bp, "%*s", twidth, " ");
}
}
- if (co.show_sets)
- printf("set %d ", rule->set);
+ if (co->show_sets)
+ bprintf(bp, "set %d ", rule->set);
/*
* print the optional "match probability"
@@ -1183,7 +1435,7 @@ show_ipfw(struct ip_fw *rule, int pcwidth, int bcwidth)
double d = 1.0 * p->d[0];
d = (d / 0x7fffffff);
- printf("prob %f ", d);
+ bprintf(bp, "prob %f ", d);
}
}
@@ -1194,66 +1446,66 @@ show_ipfw(struct ip_fw *rule, int pcwidth, int bcwidth)
l > 0 ; l -= F_LEN(cmd), cmd += F_LEN(cmd)) {
switch(cmd->opcode) {
case O_CHECK_STATE:
- printf("check-state");
+ bprintf(bp, "check-state");
/* avoid printing anything else */
flags = HAVE_PROTO | HAVE_SRCIP |
HAVE_DSTIP | HAVE_IP;
break;
case O_ACCEPT:
- printf("allow");
+ bprintf(bp, "allow");
break;
case O_COUNT:
- printf("count");
+ bprintf(bp, "count");
break;
case O_DENY:
- printf("deny");
+ bprintf(bp, "deny");
break;
case O_REJECT:
if (cmd->arg1 == ICMP_REJECT_RST)
- printf("reset");
+ bprintf(bp, "reset");
else if (cmd->arg1 == ICMP_UNREACH_HOST)
- printf("reject");
+ bprintf(bp, "reject");
else
- print_reject_code(cmd->arg1);
+ print_reject_code(bp, cmd->arg1);
break;
case O_UNREACH6:
if (cmd->arg1 == ICMP6_UNREACH_RST)
- printf("reset6");
+ bprintf(bp, "reset6");
else
print_unreach6_code(cmd->arg1);
break;
case O_SKIPTO:
- PRINT_UINT_ARG("skipto ", cmd->arg1);
+ bprint_uint_arg(bp, "skipto ", cmd->arg1);
break;
case O_PIPE:
- PRINT_UINT_ARG("pipe ", cmd->arg1);
+ bprint_uint_arg(bp, "pipe ", cmd->arg1);
break;
case O_QUEUE:
- PRINT_UINT_ARG("queue ", cmd->arg1);
+ bprint_uint_arg(bp, "queue ", cmd->arg1);
break;
case O_DIVERT:
- PRINT_UINT_ARG("divert ", cmd->arg1);
+ bprint_uint_arg(bp, "divert ", cmd->arg1);
break;
case O_TEE:
- PRINT_UINT_ARG("tee ", cmd->arg1);
+ bprint_uint_arg(bp, "tee ", cmd->arg1);
break;
case O_NETGRAPH:
- PRINT_UINT_ARG("netgraph ", cmd->arg1);
+ bprint_uint_arg(bp, "netgraph ", cmd->arg1);
break;
case O_NGTEE:
- PRINT_UINT_ARG("ngtee ", cmd->arg1);
+ bprint_uint_arg(bp, "ngtee ", cmd->arg1);
break;
case O_FORWARD_IP:
@@ -1261,12 +1513,12 @@ show_ipfw(struct ip_fw *rule, int pcwidth, int bcwidth)
ipfw_insn_sa *s = (ipfw_insn_sa *)cmd;
if (s->sa.sin_addr.s_addr == INADDR_ANY) {
- printf("fwd tablearg");
+ bprintf(bp, "fwd tablearg");
} else {
- printf("fwd %s", inet_ntoa(s->sa.sin_addr));
+ bprintf(bp, "fwd %s",inet_ntoa(s->sa.sin_addr));
}
if (s->sa.sin_port)
- printf(",%d", s->sa.sin_port);
+ bprintf(bp, ",%d", s->sa.sin_port);
}
break;
@@ -1275,10 +1527,10 @@ show_ipfw(struct ip_fw *rule, int pcwidth, int bcwidth)
char buf[4 + INET6_ADDRSTRLEN + 1];
ipfw_insn_sa6 *s = (ipfw_insn_sa6 *)cmd;
- printf("fwd %s", inet_ntop(AF_INET6, &s->sa.sin6_addr,
- buf, sizeof(buf)));
+ bprintf(bp, "fwd %s", inet_ntop(AF_INET6,
+ &s->sa.sin6_addr, buf, sizeof(buf)));
if (s->sa.sin6_port)
- printf(",%d", s->sa.sin6_port);
+ bprintf(bp, ",%d", s->sa.sin6_port);
}
break;
@@ -1296,64 +1548,69 @@ show_ipfw(struct ip_fw *rule, int pcwidth, int bcwidth)
case O_NAT:
if (cmd->arg1 != 0)
- PRINT_UINT_ARG("nat ", cmd->arg1);
+ bprint_uint_arg(bp, "nat ", cmd->arg1);
else
- printf("nat global");
+ bprintf(bp, "nat global");
break;
case O_SETFIB:
- PRINT_UINT_ARG("setfib ", cmd->arg1);
+ bprint_uint_arg(bp, "setfib ", cmd->arg1 & 0x7FFF);
break;
case O_SETDSCP:
{
const char *code;
- if ((code = match_value(f_ipdscp, cmd->arg1)) != NULL)
- printf("setdscp %s", code);
+ if (cmd->arg1 == IP_FW_TARG) {
+ bprint_uint_arg(bp, "setdscp ", cmd->arg1);
+ break;
+ }
+ uval = cmd->arg1 & 0x3F;
+ if ((code = match_value(f_ipdscp, uval)) != NULL)
+ bprintf(bp, "setdscp %s", code);
else
- PRINT_UINT_ARG("setdscp ", cmd->arg1);
+ bprint_uint_arg(bp, "setdscp ", uval);
}
break;
case O_REASS:
- printf("reass");
+ bprintf(bp, "reass");
break;
case O_CALLRETURN:
if (cmd->len & F_NOT)
- printf("return");
+ bprintf(bp, "return");
else
- PRINT_UINT_ARG("call ", cmd->arg1);
+ bprint_uint_arg(bp, "call ", cmd->arg1);
break;
default:
- printf("** unrecognized action %d len %d ",
+ bprintf(bp, "** unrecognized action %d len %d ",
cmd->opcode, cmd->len);
}
}
if (logptr) {
if (logptr->max_log > 0)
- printf(" log logamount %d", logptr->max_log);
+ bprintf(bp, " log logamount %d", logptr->max_log);
else
- printf(" log");
+ bprintf(bp, " log");
}
#ifndef NO_ALTQ
if (altqptr) {
- print_altq_cmd(altqptr);
+ print_altq_cmd(bp, altqptr);
}
#endif
if (tagptr) {
if (tagptr->len & F_NOT)
- PRINT_UINT_ARG(" untag ", tagptr->arg1);
+ bprint_uint_arg(bp, " untag ", tagptr->arg1);
else
- PRINT_UINT_ARG(" tag ", tagptr->arg1);
+ bprint_uint_arg(bp, " tag ", tagptr->arg1);
}
/*
* then print the body.
*/
- 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)) {
if ((cmd->len & F_OR) || (cmd->len & F_NOT))
continue;
@@ -1365,31 +1622,31 @@ show_ipfw(struct ip_fw *rule, int pcwidth, int bcwidth)
break;
}
}
- if (rule->_pad & 1) { /* empty rules before options */
- if (!co.do_compact) {
- show_prerequisites(&flags, HAVE_PROTO, 0);
- printf(" from any to any");
+ if (rule->flags & IPFW_RULE_NOOPT) { /* empty rules before options */
+ if (!co->do_compact) {
+ show_prerequisites(bp, &flags, HAVE_PROTO, 0);
+ bprintf(bp, " from any to any");
}
flags |= HAVE_IP | HAVE_OPTIONS | HAVE_PROTO |
HAVE_SRCIP | HAVE_DSTIP;
}
- if (co.comment_only)
+ if (co->comment_only)
comment = "...";
- 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)) {
/* useful alias */
ipfw_insn_u32 *cmd32 = (ipfw_insn_u32 *)cmd;
- if (co.comment_only) {
+ if (co->comment_only) {
if (cmd->opcode != O_NOP)
continue;
- printf(" // %s\n", (char *)(cmd + 1));
+ bprintf(bp, " // %s\n", (char *)(cmd + 1));
return;
}
- show_prerequisites(&flags, 0, cmd->opcode);
+ show_prerequisites(bp, &flags, 0, cmd->opcode);
switch(cmd->opcode) {
case O_PROB:
@@ -1403,12 +1660,12 @@ show_ipfw(struct ip_fw *rule, int pcwidth, int bcwidth)
case O_IP_SRC_MASK:
case O_IP_SRC_ME:
case O_IP_SRC_SET:
- show_prerequisites(&flags, HAVE_PROTO, 0);
+ show_prerequisites(bp, &flags, HAVE_PROTO, 0);
if (!(flags & HAVE_SRCIP))
- printf(" from");
+ bprintf(bp, " from");
if ((cmd->len & F_OR) && !or_block)
- printf(" {");
- print_ip((ipfw_insn_ip *)cmd,
+ bprintf(bp, " {");
+ print_ip(bp, fo, (ipfw_insn_ip *)cmd,
(flags & HAVE_OPTIONS) ? " src-ip" : "");
flags |= HAVE_SRCIP;
break;
@@ -1418,12 +1675,12 @@ show_ipfw(struct ip_fw *rule, int pcwidth, int bcwidth)
case O_IP_DST_MASK:
case O_IP_DST_ME:
case O_IP_DST_SET:
- show_prerequisites(&flags, HAVE_PROTO|HAVE_SRCIP, 0);
+ show_prerequisites(bp, &flags, HAVE_PROTO|HAVE_SRCIP, 0);
if (!(flags & HAVE_DSTIP))
- printf(" to");
+ bprintf(bp, " to");
if ((cmd->len & F_OR) && !or_block)
- printf(" {");
- print_ip((ipfw_insn_ip *)cmd,
+ bprintf(bp, " {");
+ print_ip(bp, fo, (ipfw_insn_ip *)cmd,
(flags & HAVE_OPTIONS) ? " dst-ip" : "");
flags |= HAVE_DSTIP;
break;
@@ -1431,12 +1688,12 @@ show_ipfw(struct ip_fw *rule, int pcwidth, int bcwidth)
case O_IP6_SRC:
case O_IP6_SRC_MASK:
case O_IP6_SRC_ME:
- show_prerequisites(&flags, HAVE_PROTO, 0);
+ show_prerequisites(bp, &flags, HAVE_PROTO, 0);
if (!(flags & HAVE_SRCIP))
- printf(" from");
+ bprintf(bp, " from");
if ((cmd->len & F_OR) && !or_block)
- printf(" {");
- print_ip6((ipfw_insn_ip6 *)cmd,
+ bprintf(bp, " {");
+ print_ip6(bp, (ipfw_insn_ip6 *)cmd,
(flags & HAVE_OPTIONS) ? " src-ip6" : "");
flags |= HAVE_SRCIP | HAVE_PROTO;
break;
@@ -1444,35 +1701,35 @@ show_ipfw(struct ip_fw *rule, int pcwidth, int bcwidth)
case O_IP6_DST:
case O_IP6_DST_MASK:
case O_IP6_DST_ME:
- show_prerequisites(&flags, HAVE_PROTO|HAVE_SRCIP, 0);
+ show_prerequisites(bp, &flags, HAVE_PROTO|HAVE_SRCIP, 0);
if (!(flags & HAVE_DSTIP))
- printf(" to");
+ bprintf(bp, " to");
if ((cmd->len & F_OR) && !or_block)
- printf(" {");
- print_ip6((ipfw_insn_ip6 *)cmd,
+ bprintf(bp, " {");
+ print_ip6(bp, (ipfw_insn_ip6 *)cmd,
(flags & HAVE_OPTIONS) ? " dst-ip6" : "");
flags |= HAVE_DSTIP;
break;
case O_FLOW6ID:
- print_flow6id( (ipfw_insn_u32 *) cmd );
- flags |= HAVE_OPTIONS;
- break;
+ print_flow6id(bp, (ipfw_insn_u32 *) cmd );
+ flags |= HAVE_OPTIONS;
+ break;
case O_IP_DSTPORT:
- show_prerequisites(&flags,
+ show_prerequisites(bp, &flags,
HAVE_PROTO | HAVE_SRCIP |
HAVE_DSTIP | HAVE_IP, 0);
case O_IP_SRCPORT:
if (flags & HAVE_DSTIP)
flags |= HAVE_IP;
- show_prerequisites(&flags,
+ show_prerequisites(bp, &flags,
HAVE_PROTO | HAVE_SRCIP, 0);
if ((cmd->len & F_OR) && !or_block)
- printf(" {");
+ bprintf(bp, " {");
if (cmd->len & F_NOT)
- printf(" not");
- print_newports((ipfw_insn_u16 *)cmd, proto,
+ bprintf(bp, " not");
+ print_newports(bp, (ipfw_insn_u16 *)cmd, proto,
(flags & HAVE_OPTIONS) ? cmd->opcode : 0);
break;
@@ -1480,22 +1737,22 @@ show_ipfw(struct ip_fw *rule, int pcwidth, int bcwidth)
struct protoent *pe = NULL;
if ((cmd->len & F_OR) && !or_block)
- printf(" {");
+ bprintf(bp, " {");
if (cmd->len & F_NOT)
- printf(" not");
+ bprintf(bp, " not");
proto = cmd->arg1;
pe = getprotobynumber(cmd->arg1);
if ((flags & (HAVE_PROTO4 | HAVE_PROTO6)) &&
!(flags & HAVE_PROTO))
- show_prerequisites(&flags,
+ show_prerequisites(bp, &flags,
HAVE_PROTO | HAVE_IP | HAVE_SRCIP |
HAVE_DSTIP | HAVE_OPTIONS, 0);
if (flags & HAVE_OPTIONS)
- printf(" proto");
+ bprintf(bp, " proto");
if (pe)
- printf(" %s", pe->p_name);
+ bprintf(bp, " %s", pe->p_name);
else
- printf(" %u", cmd->arg1);
+ bprintf(bp, " %u", cmd->arg1);
}
flags |= HAVE_PROTO;
break;
@@ -1507,68 +1764,68 @@ show_ipfw(struct ip_fw *rule, int pcwidth, int bcwidth)
((cmd->opcode == O_IP4) &&
(flags & HAVE_PROTO4)))
break;
- show_prerequisites(&flags, HAVE_PROTO | HAVE_SRCIP |
+ show_prerequisites(bp, &flags, HAVE_PROTO | HAVE_SRCIP |
HAVE_DSTIP | HAVE_IP | HAVE_OPTIONS, 0);
if ((cmd->len & F_OR) && !or_block)
- printf(" {");
+ bprintf(bp, " {");
if (cmd->len & F_NOT && cmd->opcode != O_IN)
- printf(" not");
+ bprintf(bp, " not");
switch(cmd->opcode) {
case O_MACADDR2: {
ipfw_insn_mac *m = (ipfw_insn_mac *)cmd;
- printf(" MAC");
- print_mac(m->addr, m->mask);
- print_mac(m->addr + 6, m->mask + 6);
+ bprintf(bp, " MAC");
+ print_mac(bp, m->addr, m->mask);
+ print_mac(bp, m->addr + 6, m->mask + 6);
}
break;
case O_MAC_TYPE:
- print_newports((ipfw_insn_u16 *)cmd,
+ print_newports(bp, (ipfw_insn_u16 *)cmd,
IPPROTO_ETHERTYPE, cmd->opcode);
break;
case O_FRAG:
- printf(" frag");
+ bprintf(bp, " frag");
break;
case O_FIB:
- printf(" fib %u", cmd->arg1 );
+ bprintf(bp, " fib %u", cmd->arg1 );
break;
case O_SOCKARG:
- printf(" sockarg");
+ bprintf(bp, " sockarg");
break;
case O_IN:
- printf(cmd->len & F_NOT ? " out" : " in");
+ bprintf(bp, cmd->len & F_NOT ? " out" : " in");
break;
case O_DIVERTED:
switch (cmd->arg1) {
case 3:
- printf(" diverted");
+ bprintf(bp, " diverted");
break;
case 1:
- printf(" diverted-loopback");
+ bprintf(bp, " diverted-loopback");
break;
case 2:
- printf(" diverted-output");
+ bprintf(bp, " diverted-output");
break;
default:
- printf(" diverted-?<%u>", cmd->arg1);
+ bprintf(bp, " diverted-?<%u>", cmd->arg1);
break;
}
break;
case O_LAYER2:
- printf(" layer2");
+ bprintf(bp, " layer2");
break;
case O_XMIT:
case O_RECV:
case O_VIA:
{
- char const *s;
+ char const *s, *t;
ipfw_insn_if *cmdif = (ipfw_insn_if *)cmd;
if (cmd->opcode == O_XMIT)
@@ -1578,97 +1835,112 @@ show_ipfw(struct ip_fw *rule, int pcwidth, int bcwidth)
else /* if (cmd->opcode == O_VIA) */
s = "via";
if (cmdif->name[0] == '\0')
- printf(" %s %s", s,
+ bprintf(bp, " %s %s", s,
inet_ntoa(cmdif->p.ip));
- else if (cmdif->name[0] == '\1') /* interface table */
- printf(" %s table(%d)", s, cmdif->p.glob);
- else
- printf(" %s %s", s, cmdif->name);
+ else if (cmdif->name[0] == '\1') {
+ /* interface table */
+ t = table_search_ctlv(fo->tstate,
+ cmdif->p.kidx);
+ bprintf(bp, " %s table(%s)", s, t);
+ } else
+ bprintf(bp, " %s %s", s, cmdif->name);
break;
}
+ case O_IP_FLOW_LOOKUP:
+ {
+ char *t;
+
+ t = table_search_ctlv(fo->tstate, cmd->arg1);
+ bprintf(bp, " flow table(%s", t);
+ if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn_u32))
+ bprintf(bp, ",%u",
+ ((ipfw_insn_u32 *)cmd)->d[0]);
+ bprintf(bp, ")");
+ break;
+ }
case O_IPID:
if (F_LEN(cmd) == 1)
- printf(" ipid %u", cmd->arg1 );
+ bprintf(bp, " ipid %u", cmd->arg1 );
else
- print_newports((ipfw_insn_u16 *)cmd, 0,
+ print_newports(bp, (ipfw_insn_u16 *)cmd, 0,
O_IPID);
break;
case O_IPTTL:
if (F_LEN(cmd) == 1)
- printf(" ipttl %u", cmd->arg1 );
+ bprintf(bp, " ipttl %u", cmd->arg1 );
else
- print_newports((ipfw_insn_u16 *)cmd, 0,
+ print_newports(bp, (ipfw_insn_u16 *)cmd, 0,
O_IPTTL);
break;
case O_IPVER:
- printf(" ipver %u", cmd->arg1 );
+ bprintf(bp, " ipver %u", cmd->arg1 );
break;
case O_IPPRECEDENCE:
- printf(" ipprecedence %u", (cmd->arg1) >> 5 );
+ bprintf(bp, " ipprecedence %u", cmd->arg1 >> 5);
break;
case O_DSCP:
- print_dscp((ipfw_insn_u32 *)cmd);
+ print_dscp(bp, (ipfw_insn_u32 *)cmd);
break;
case O_IPLEN:
if (F_LEN(cmd) == 1)
- printf(" iplen %u", cmd->arg1 );
+ bprintf(bp, " iplen %u", cmd->arg1 );
else
- print_newports((ipfw_insn_u16 *)cmd, 0,
+ print_newports(bp, (ipfw_insn_u16 *)cmd, 0,
O_IPLEN);
break;
case O_IPOPT:
- print_flags("ipoptions", cmd, f_ipopts);
+ print_flags(bp, "ipoptions", cmd, f_ipopts);
break;
case O_IPTOS:
- print_flags("iptos", cmd, f_iptos);
+ print_flags(bp, "iptos", cmd, f_iptos);
break;
case O_ICMPTYPE:
- print_icmptypes((ipfw_insn_u32 *)cmd);
+ print_icmptypes(bp, (ipfw_insn_u32 *)cmd);
break;
case O_ESTAB:
- printf(" established");
+ bprintf(bp, " established");
break;
case O_TCPDATALEN:
if (F_LEN(cmd) == 1)
- printf(" tcpdatalen %u", cmd->arg1 );
+ bprintf(bp, " tcpdatalen %u", cmd->arg1 );
else
- print_newports((ipfw_insn_u16 *)cmd, 0,
+ print_newports(bp, (ipfw_insn_u16 *)cmd, 0,
O_TCPDATALEN);
break;
case O_TCPFLAGS:
- print_flags("tcpflags", cmd, f_tcpflags);
+ print_flags(bp, "tcpflags", cmd, f_tcpflags);
break;
case O_TCPOPTS:
- print_flags("tcpoptions", cmd, f_tcpopts);
+ print_flags(bp, "tcpoptions", cmd, f_tcpopts);
break;
case O_TCPWIN:
if (F_LEN(cmd) == 1)
- printf(" tcpwin %u", cmd->arg1);
+ bprintf(bp, " tcpwin %u", cmd->arg1);
else
- print_newports((ipfw_insn_u16 *)cmd, 0,
+ print_newports(bp, (ipfw_insn_u16 *)cmd, 0,
O_TCPWIN);
break;
case O_TCPACK:
- printf(" tcpack %d", ntohl(cmd32->d[0]));
+ bprintf(bp, " tcpack %d", ntohl(cmd32->d[0]));
break;
case O_TCPSEQ:
- printf(" tcpseq %d", ntohl(cmd32->d[0]));
+ bprintf(bp, " tcpseq %d", ntohl(cmd32->d[0]));
break;
case O_UID:
@@ -1676,9 +1948,9 @@ show_ipfw(struct ip_fw *rule, int pcwidth, int bcwidth)
struct passwd *pwd = getpwuid(cmd32->d[0]);
if (pwd)
- printf(" uid %s", pwd->pw_name);
+ bprintf(bp, " uid %s", pwd->pw_name);
else
- printf(" uid %u", cmd32->d[0]);
+ bprintf(bp, " uid %u", cmd32->d[0]);
}
break;
@@ -1687,30 +1959,30 @@ show_ipfw(struct ip_fw *rule, int pcwidth, int bcwidth)
struct group *grp = getgrgid(cmd32->d[0]);
if (grp)
- printf(" gid %s", grp->gr_name);
+ bprintf(bp, " gid %s", grp->gr_name);
else
- printf(" gid %u", cmd32->d[0]);
+ bprintf(bp, " gid %u", cmd32->d[0]);
}
break;
case O_JAIL:
- printf(" jail %d", cmd32->d[0]);
+ bprintf(bp, " jail %d", cmd32->d[0]);
break;
case O_VERREVPATH:
- printf(" verrevpath");
+ bprintf(bp, " verrevpath");
break;
case O_VERSRCREACH:
- printf(" versrcreach");
+ bprintf(bp, " versrcreach");
break;
case O_ANTISPOOF:
- printf(" antispoof");
+ bprintf(bp, " antispoof");
break;
case O_IPSEC:
- printf(" ipsec");
+ bprintf(bp, " ipsec");
break;
case O_NOP:
@@ -1718,7 +1990,7 @@ show_ipfw(struct ip_fw *rule, int pcwidth, int bcwidth)
break;
case O_KEEP_STATE:
- printf(" keep-state");
+ bprintf(bp, " keep-state");
break;
case O_LIMIT: {
@@ -1727,113 +1999,132 @@ show_ipfw(struct ip_fw *rule, int pcwidth, int bcwidth)
uint8_t x = c->limit_mask;
char const *comma = " ";
- printf(" limit");
+ bprintf(bp, " limit");
for (; p->x != 0 ; p++)
if ((x & p->x) == p->x) {
x &= ~p->x;
- printf("%s%s", comma, p->s);
+ bprintf(bp, "%s%s", comma,p->s);
comma = ",";
}
- PRINT_UINT_ARG(" ", c->conn_limit);
+ bprint_uint_arg(bp, " ", c->conn_limit);
break;
}
case O_IP6:
- printf(" ip6");
+ bprintf(bp, " ip6");
break;
case O_IP4:
- printf(" ip4");
+ bprintf(bp, " ip4");
break;
case O_ICMP6TYPE:
- print_icmp6types((ipfw_insn_u32 *)cmd);
+ print_icmp6types(bp, (ipfw_insn_u32 *)cmd);
break;
case O_EXT_HDR:
- print_ext6hdr( (ipfw_insn *) cmd );
+ print_ext6hdr(bp, (ipfw_insn *)cmd);
break;
case O_TAGGED:
if (F_LEN(cmd) == 1)
- PRINT_UINT_ARG(" tagged ", cmd->arg1);
+ bprint_uint_arg(bp, " tagged ",
+ cmd->arg1);
else
- print_newports((ipfw_insn_u16 *)cmd, 0,
- O_TAGGED);
+ print_newports(bp, (ipfw_insn_u16 *)cmd,
+ 0, O_TAGGED);
break;
default:
- printf(" [opcode %d len %d]",
+ bprintf(bp, " [opcode %d len %d]",
cmd->opcode, cmd->len);
}
}
if (cmd->len & F_OR) {
- printf(" or");
+ bprintf(bp, " or");
or_block = 1;
} else if (or_block) {
- printf(" }");
+ bprintf(bp, " }");
or_block = 0;
}
}
- show_prerequisites(&flags, HAVE_PROTO | HAVE_SRCIP | HAVE_DSTIP
+ show_prerequisites(bp, &flags, HAVE_PROTO | HAVE_SRCIP | HAVE_DSTIP
| HAVE_IP, 0);
if (comment)
- printf(" // %s", comment);
- printf("\n");
+ bprintf(bp, " // %s", comment);
+ bprintf(bp, "\n");
}
static void
-show_dyn_ipfw(ipfw_dyn_rule *d, int pcwidth, int bcwidth)
+show_dyn_state(struct cmdline_opts *co, struct format_opts *fo,
+ struct buf_pr *bp, ipfw_dyn_rule *d)
{
struct protoent *pe;
struct in_addr a;
uint16_t rulenum;
char buf[INET6_ADDRSTRLEN];
- if (!co.do_expired) {
+ if (!co->do_expired) {
if (!d->expire && !(d->dyn_type == O_LIMIT_PARENT))
return;
}
bcopy(&d->rule, &rulenum, sizeof(rulenum));
- printf("%05d", rulenum);
- if (pcwidth > 0 || bcwidth > 0) {
- printf(" ");
- pr_u64(&d->pcnt, pcwidth);
- pr_u64(&d->bcnt, bcwidth);
- printf("(%ds)", d->expire);
+ bprintf(bp, "%05d", rulenum);
+ if (fo->pcwidth > 0 || fo->bcwidth > 0) {
+ bprintf(bp, " ");
+ pr_u64(bp, &d->pcnt, fo->pcwidth);
+ pr_u64(bp, &d->bcnt, fo->bcwidth);
+ bprintf(bp, "(%ds)", d->expire);
}
switch (d->dyn_type) {
case O_LIMIT_PARENT:
- printf(" PARENT %d", d->count);
+ bprintf(bp, " PARENT %d", d->count);
break;
case O_LIMIT:
- printf(" LIMIT");
+ bprintf(bp, " LIMIT");
break;
case O_KEEP_STATE: /* bidir, no mask */
- printf(" STATE");
+ bprintf(bp, " STATE");
break;
}
if ((pe = getprotobynumber(d->id.proto)) != NULL)
- printf(" %s", pe->p_name);
+ bprintf(bp, " %s", pe->p_name);
else
- printf(" proto %u", d->id.proto);
+ bprintf(bp, " proto %u", d->id.proto);
if (d->id.addr_type == 4) {
a.s_addr = htonl(d->id.src_ip);
- printf(" %s %d", inet_ntoa(a), d->id.src_port);
+ bprintf(bp, " %s %d", inet_ntoa(a), d->id.src_port);
a.s_addr = htonl(d->id.dst_ip);
- printf(" <-> %s %d", inet_ntoa(a), d->id.dst_port);
+ bprintf(bp, " <-> %s %d", inet_ntoa(a), d->id.dst_port);
} else if (d->id.addr_type == 6) {
- printf(" %s %d", inet_ntop(AF_INET6, &d->id.src_ip6, buf,
+ bprintf(bp, " %s %d", inet_ntop(AF_INET6, &d->id.src_ip6, buf,
sizeof(buf)), d->id.src_port);
- printf(" <-> %s %d", inet_ntop(AF_INET6, &d->id.dst_ip6, buf,
- sizeof(buf)), d->id.dst_port);
+ bprintf(bp, " <-> %s %d", inet_ntop(AF_INET6, &d->id.dst_ip6,
+ buf, sizeof(buf)), d->id.dst_port);
} else
- printf(" UNKNOWN <-> UNKNOWN\n");
+ bprintf(bp, " UNKNOWN <-> UNKNOWN\n");
+}
- printf("\n");
+static int
+do_range_cmd(int cmd, ipfw_range_tlv *rt)
+{
+ ipfw_range_header rh;
+ size_t sz;
+
+ memset(&rh, 0, sizeof(rh));
+ memcpy(&rh.range, rt, sizeof(*rt));
+ rh.range.head.length = sizeof(*rt);
+ rh.range.head.type = IPFW_TLV_RANGE;
+ sz = sizeof(rh);
+
+ if (do_get3(cmd, &rh.opheader, &sz) != 0)
+ return (-1);
+ /* Save number of matched objects */
+ rt->new_set = rh.range.new_set;
+ return (0);
}
/*
@@ -1846,77 +2137,75 @@ show_dyn_ipfw(ipfw_dyn_rule *d, int pcwidth, int bcwidth)
void
ipfw_sets_handler(char *av[])
{
- uint32_t set_disable, masks[2];
- int i, nbytes;
- uint16_t rulenum;
- uint8_t cmd, new_set;
+ uint32_t masks[2];
+ int i;
+ uint8_t cmd, rulenum;
+ ipfw_range_tlv rt;
+ char *msg;
+ size_t size;
av++;
+ memset(&rt, 0, sizeof(rt));
if (av[0] == NULL)
errx(EX_USAGE, "set needs command");
if (_substrcmp(*av, "show") == 0) {
- void *data = NULL;
- char const *msg;
- int nalloc;
-
- nalloc = nbytes = sizeof(struct ip_fw);
- while (nbytes >= nalloc) {
- if (data)
- free(data);
- nalloc = nalloc * 2 + 200;
- nbytes = nalloc;
- data = safe_calloc(1, nbytes);
- if (do_cmd(IP_FW_GET, data, (uintptr_t)&nbytes) < 0)
- err(EX_OSERR, "getsockopt(IP_FW_GET)");
- }
+ struct format_opts fo;
+ ipfw_cfg_lheader *cfg;
- bcopy(&((struct ip_fw *)data)->next_rule,
- &set_disable, sizeof(set_disable));
+ memset(&fo, 0, sizeof(fo));
+ if (ipfw_get_config(&co, &fo, &cfg, &size) != 0)
+ err(EX_OSERR, "requesting config failed");
- for (i = 0, msg = "disable" ; i < RESVD_SET; i++)
- if ((set_disable & (1<<i))) {
+ for (i = 0, msg = "disable"; i < RESVD_SET; i++)
+ if ((cfg->set_mask & (1<<i)) == 0) {
printf("%s %d", msg, i);
msg = "";
}
- msg = (set_disable) ? " enable" : "enable";
+ msg = (cfg->set_mask != (uint32_t)-1) ? " enable" : "enable";
for (i = 0; i < RESVD_SET; i++)
- if (!(set_disable & (1<<i))) {
+ if ((cfg->set_mask & (1<<i)) != 0) {
printf("%s %d", msg, i);
msg = "";
}
printf("\n");
+ free(cfg);
} else if (_substrcmp(*av, "swap") == 0) {
av++;
if ( av[0] == NULL || av[1] == NULL )
errx(EX_USAGE, "set swap needs 2 set numbers\n");
- rulenum = atoi(av[0]);
- new_set = atoi(av[1]);
- if (!isdigit(*(av[0])) || rulenum > RESVD_SET)
+ rt.set = atoi(av[0]);
+ rt.new_set = atoi(av[1]);
+ if (!isdigit(*(av[0])) || rt.set > RESVD_SET)
errx(EX_DATAERR, "invalid set number %s\n", av[0]);
- if (!isdigit(*(av[1])) || new_set > RESVD_SET)
+ if (!isdigit(*(av[1])) || rt.new_set > RESVD_SET)
errx(EX_DATAERR, "invalid set number %s\n", av[1]);
- masks[0] = (4 << 24) | (new_set << 16) | (rulenum);
- i = do_cmd(IP_FW_DEL, masks, sizeof(uint32_t));
+ i = do_range_cmd(IP_FW_SET_SWAP, &rt);
} else if (_substrcmp(*av, "move") == 0) {
av++;
if (av[0] && _substrcmp(*av, "rule") == 0) {
- cmd = 2;
+ rt.flags = IPFW_RCFLAG_RANGE; /* move rules to new set */
+ cmd = IP_FW_XMOVE;
av++;
} else
- cmd = 3;
+ cmd = IP_FW_SET_MOVE; /* Move set to new one */
if (av[0] == NULL || av[1] == NULL || av[2] == NULL ||
av[3] != NULL || _substrcmp(av[1], "to") != 0)
errx(EX_USAGE, "syntax: set move [rule] X to Y\n");
rulenum = atoi(av[0]);
- new_set = atoi(av[2]);
- if (!isdigit(*(av[0])) || (cmd == 3 && rulenum > RESVD_SET) ||
- (cmd == 2 && rulenum == IPFW_DEFAULT_RULE) )
+ rt.new_set = atoi(av[2]);
+ if (cmd == IP_FW_XMOVE) {
+ rt.start_rule = rulenum;
+ rt.end_rule = rulenum;
+ } else
+ rt.set = rulenum;
+ rt.new_set = atoi(av[2]);
+ if (!isdigit(*(av[0])) || (cmd == 3 && rt.set > RESVD_SET) ||
+ (cmd == 2 && rt.start_rule == IPFW_DEFAULT_RULE) )
errx(EX_DATAERR, "invalid source number %s\n", av[0]);
- if (!isdigit(*(av[2])) || new_set > RESVD_SET)
+ if (!isdigit(*(av[2])) || rt.new_set > RESVD_SET)
errx(EX_DATAERR, "invalid dest. set %s\n", av[1]);
- masks[0] = (cmd << 24) | (new_set << 16) | (rulenum);
- i = do_cmd(IP_FW_DEL, masks, sizeof(uint32_t));
+ i = do_range_cmd(cmd, &rt);
} else if (_substrcmp(*av, "disable") == 0 ||
_substrcmp(*av, "enable") == 0 ) {
int which = _substrcmp(*av, "enable") == 0 ? 1 : 0;
@@ -1944,9 +2233,11 @@ ipfw_sets_handler(char *av[])
errx(EX_DATAERR,
"cannot enable and disable the same set\n");
- i = do_cmd(IP_FW_DEL, masks, sizeof(masks));
+ rt.set = masks[0];
+ rt.new_set = masks[1];
+ i = do_range_cmd(IP_FW_SET_ENABLE, &rt);
if (i)
- warn("set enable/disable: setsockopt(IP_FW_DEL)");
+ warn("set enable/disable: setsockopt(IP_FW_SET_ENABLE)");
} else
errx(EX_USAGE, "invalid set command %s\n", *av);
}
@@ -1984,28 +2275,204 @@ ipfw_sysctl_handler(char *av[], int which)
}
}
+typedef void state_cb(struct cmdline_opts *co, struct format_opts *fo,
+ void *arg, void *state);
+
+static void
+prepare_format_dyn(struct cmdline_opts *co, struct format_opts *fo,
+ void *arg, void *_state)
+{
+ ipfw_dyn_rule *d;
+ int width;
+ uint8_t set;
+
+ d = (ipfw_dyn_rule *)_state;
+ /* Count _ALL_ states */
+ fo->dcnt++;
+
+ if (fo->show_counters == 0)
+ return;
+
+ if (co->use_set) {
+ /* skip states from another set */
+ bcopy((char *)&d->rule + sizeof(uint16_t), &set,
+ sizeof(uint8_t));
+ if (set != co->use_set - 1)
+ return;
+ }
+
+ width = pr_u64(NULL, &d->pcnt, 0);
+ if (width > fo->pcwidth)
+ fo->pcwidth = width;
+
+ width = pr_u64(NULL, &d->bcnt, 0);
+ if (width > fo->bcwidth)
+ fo->bcwidth = width;
+}
+
+static int
+foreach_state(struct cmdline_opts *co, struct format_opts *fo,
+ caddr_t base, size_t sz, state_cb dyn_bc, void *dyn_arg)
+{
+ int ttype;
+ state_cb *fptr;
+ void *farg;
+ ipfw_obj_tlv *tlv;
+ ipfw_obj_ctlv *ctlv;
+
+ fptr = NULL;
+ ttype = 0;
+
+ while (sz > 0) {
+ ctlv = (ipfw_obj_ctlv *)base;
+ switch (ctlv->head.type) {
+ case IPFW_TLV_DYNSTATE_LIST:
+ base += sizeof(*ctlv);
+ sz -= sizeof(*ctlv);
+ ttype = IPFW_TLV_DYN_ENT;
+ fptr = dyn_bc;
+ farg = dyn_arg;
+ break;
+ default:
+ return (sz);
+ }
+
+ while (sz > 0) {
+ tlv = (ipfw_obj_tlv *)base;
+ if (tlv->type != ttype)
+ break;
+
+ fptr(co, fo, farg, tlv + 1);
+ sz -= tlv->length;
+ base += tlv->length;
+ }
+ }
+
+ return (sz);
+}
+
+static void
+prepare_format_opts(struct cmdline_opts *co, struct format_opts *fo,
+ ipfw_obj_tlv *rtlv, int rcnt, caddr_t dynbase, size_t dynsz)
+{
+ int bcwidth, pcwidth, width;
+ int n;
+ struct ip_fw_bcounter *cntr;
+ struct ip_fw_rule *r;
+
+ bcwidth = 0;
+ pcwidth = 0;
+ if (fo->show_counters != 0) {
+ for (n = 0; n < rcnt; n++,
+ rtlv = (ipfw_obj_tlv *)((caddr_t)rtlv + rtlv->length)) {
+ cntr = (struct ip_fw_bcounter *)(rtlv + 1);
+ r = (struct ip_fw_rule *)((caddr_t)cntr + cntr->size);
+ /* skip rules from another set */
+ if (co->use_set && r->set != co->use_set - 1)
+ continue;
+
+ /* packet counter */
+ width = pr_u64(NULL, &cntr->pcnt, 0);
+ if (width > pcwidth)
+ pcwidth = width;
+
+ /* byte counter */
+ width = pr_u64(NULL, &cntr->bcnt, 0);
+ if (width > bcwidth)
+ bcwidth = width;
+ }
+ }
+ fo->bcwidth = bcwidth;
+ fo->pcwidth = pcwidth;
+
+ fo->dcnt = 0;
+ if (co->do_dynamic && dynsz > 0)
+ foreach_state(co, fo, dynbase, dynsz, prepare_format_dyn, NULL);
+}
+
+static int
+list_static_range(struct cmdline_opts *co, struct format_opts *fo,
+ struct buf_pr *bp, ipfw_obj_tlv *rtlv, int rcnt)
+{
+ int n, seen;
+ struct ip_fw_rule *r;
+ struct ip_fw_bcounter *cntr;
+ int c = 0;
+
+ for (n = seen = 0; n < rcnt; n++,
+ rtlv = (ipfw_obj_tlv *)((caddr_t)rtlv + rtlv->length)) {
+
+ if (fo->show_counters != 0) {
+ cntr = (struct ip_fw_bcounter *)(rtlv + 1);
+ r = (struct ip_fw_rule *)((caddr_t)cntr + cntr->size);
+ } else {
+ cntr = NULL;
+ r = (struct ip_fw_rule *)(rtlv + 1);
+ }
+ if (r->rulenum > fo->last)
+ break;
+ if (co->use_set && r->set != co->use_set - 1)
+ continue;
+ if (r->rulenum >= fo->first && r->rulenum <= fo->last) {
+ show_static_rule(co, fo, bp, r, cntr);
+ printf("%s", bp->buf);
+ c += rtlv->length;
+ bp_flush(bp);
+ seen++;
+ }
+ }
+
+ return (seen);
+}
+
+static void
+list_dyn_state(struct cmdline_opts *co, struct format_opts *fo,
+ void *_arg, void *_state)
+{
+ uint16_t rulenum;
+ uint8_t set;
+ ipfw_dyn_rule *d;
+ struct buf_pr *bp;
+
+ d = (ipfw_dyn_rule *)_state;
+ bp = (struct buf_pr *)_arg;
+
+ bcopy(&d->rule, &rulenum, sizeof(rulenum));
+ if (rulenum > fo->last)
+ return;
+ if (co->use_set) {
+ bcopy((char *)&d->rule + sizeof(uint16_t),
+ &set, sizeof(uint8_t));
+ if (set != co->use_set - 1)
+ return;
+ }
+ if (rulenum >= fo->first) {
+ show_dyn_state(co, fo, bp, d);
+ printf("%s\n", bp->buf);
+ bp_flush(bp);
+ }
+}
+
+static int
+list_dyn_range(struct cmdline_opts *co, struct format_opts *fo,
+ struct buf_pr *bp, caddr_t base, size_t sz)
+{
+
+ sz = foreach_state(co, fo, base, sz, list_dyn_state, bp);
+ return (sz);
+}
+
void
ipfw_list(int ac, char *av[], int show_counters)
{
- struct ip_fw *r;
- ipfw_dyn_rule *dynrules, *d;
-
-#define NEXT(r) ((struct ip_fw *)((char *)r + RULESIZE(r)))
- char *lim;
- void *data = NULL;
- int bcwidth, n, nbytes, nstat, ndyn, pcwidth, width;
- int exitval = EX_OK;
+ ipfw_cfg_lheader *cfg;
+ struct format_opts sfo;
+ size_t sz;
+ int error;
int lac;
char **lav;
- u_long rnum, last;
+ uint32_t rnum;
char *endptr;
- int seen = 0;
- uint8_t set;
-
- const int ocmd = co.do_pipe ? IP_DUMMYNET_GET : IP_FW_GET;
- int nalloc = 1024; /* start somewhere... */
-
- last = 0;
if (co.test_only) {
fprintf(stderr, "Testing only, list disabled\n");
@@ -2018,162 +2485,216 @@ ipfw_list(int ac, char *av[], int show_counters)
ac--;
av++;
+ memset(&sfo, 0, sizeof(sfo));
- /* get rules or pipes from kernel, resizing array as necessary */
- nbytes = nalloc;
+ /* Determine rule range to request */
+ if (ac > 0) {
+ for (lac = ac, lav = av; lac != 0; lac--) {
+ rnum = strtoul(*lav++, &endptr, 10);
+ if (sfo.first == 0 || rnum < sfo.first)
+ sfo.first = rnum;
- while (nbytes >= nalloc) {
- nalloc = nalloc * 2 + 200;
- nbytes = nalloc;
- data = safe_realloc(data, nbytes);
- if (do_cmd(ocmd, data, (uintptr_t)&nbytes) < 0)
- err(EX_OSERR, "getsockopt(IP_%s_GET)",
- co.do_pipe ? "DUMMYNET" : "FW");
+ if (*endptr == '-')
+ rnum = strtoul(endptr + 1, &endptr, 10);
+ if (sfo.last == 0 || rnum > sfo.last)
+ sfo.last = rnum;
+ }
}
- /*
- * Count static rules. They have variable size so we
- * need to scan the list to count them.
- */
- for (nstat = 1, r = data, lim = (char *)data + nbytes;
- r->rulenum < IPFW_DEFAULT_RULE && (char *)r < lim;
- ++nstat, r = NEXT(r) )
- ; /* nothing */
+ /* get configuraion from kernel */
+ cfg = NULL;
+ sfo.show_counters = show_counters;
+ sfo.flags = IPFW_CFG_GET_STATIC;
+ if (co.do_dynamic != 0)
+ sfo.flags |= IPFW_CFG_GET_STATES;
+ if (sfo.show_counters != 0)
+ sfo.flags |= IPFW_CFG_GET_COUNTERS;
+ if (ipfw_get_config(&co, &sfo, &cfg, &sz) != 0)
+ err(EX_OSERR, "retrieving config failed");
+
+ error = ipfw_show_config(&co, &sfo, cfg, sz, ac, av);
+
+ free(cfg);
+
+ if (error != EX_OK)
+ exit(error);
+}
+
+static int
+ipfw_show_config(struct cmdline_opts *co, struct format_opts *fo,
+ ipfw_cfg_lheader *cfg, size_t sz, int ac, char *av[])
+{
+ caddr_t dynbase;
+ size_t dynsz;
+ int rcnt;
+ int exitval = EX_OK;
+ int lac;
+ char **lav;
+ char *endptr;
+ size_t readsz;
+ struct buf_pr bp;
+ ipfw_obj_ctlv *ctlv, *tstate;
+ ipfw_obj_tlv *rbase;
/*
- * Count dynamic rules. This is easier as they have
- * fixed size.
+ * Handle tablenames TLV first, if any
*/
- r = NEXT(r);
- dynrules = (ipfw_dyn_rule *)r ;
- n = (char *)r - (char *)data;
- ndyn = (nbytes - n) / sizeof *dynrules;
-
- /* if showing stats, figure out column widths ahead of time */
- bcwidth = pcwidth = 0;
- if (show_counters) {
- for (n = 0, r = data; n < nstat; n++, r = NEXT(r)) {
- /* skip rules from another set */
- if (co.use_set && r->set != co.use_set - 1)
- continue;
-
- /* packet counter */
- width = pr_u64(&r->pcnt, 0);
- if (width > pcwidth)
- pcwidth = width;
+ tstate = NULL;
+ rbase = NULL;
+ dynbase = NULL;
+ dynsz = 0;
+ readsz = sizeof(*cfg);
+ rcnt = 0;
+
+ fo->set_mask = cfg->set_mask;
+
+ ctlv = (ipfw_obj_ctlv *)(cfg + 1);
+
+ if (cfg->flags & IPFW_CFG_GET_STATIC) {
+ /* We've requested static rules */
+ if (ctlv->head.type == IPFW_TLV_TBLNAME_LIST) {
+ fo->tstate = ctlv;
+ readsz += ctlv->head.length;
+ ctlv = (ipfw_obj_ctlv *)((caddr_t)ctlv +
+ ctlv->head.length);
+ }
- /* byte counter */
- width = pr_u64(&r->bcnt, 0);
- if (width > bcwidth)
- bcwidth = width;
+ if (ctlv->head.type == IPFW_TLV_RULE_LIST) {
+ rbase = (ipfw_obj_tlv *)(ctlv + 1);
+ rcnt = ctlv->count;
+ readsz += ctlv->head.length;
+ ctlv = (ipfw_obj_ctlv *)((caddr_t)ctlv +
+ ctlv->head.length);
}
}
- if (co.do_dynamic && ndyn) {
- for (n = 0, d = dynrules; n < ndyn; n++, d++) {
- if (co.use_set) {
- /* skip rules from another set */
- bcopy((char *)&d->rule + sizeof(uint16_t),
- &set, sizeof(uint8_t));
- if (set != co.use_set - 1)
- continue;
- }
- width = pr_u64(&d->pcnt, 0);
- if (width > pcwidth)
- pcwidth = width;
- width = pr_u64(&d->bcnt, 0);
- if (width > bcwidth)
- bcwidth = width;
- }
+ if ((cfg->flags & IPFW_CFG_GET_STATES) && (readsz != sz)) {
+ /* We may have some dynamic states */
+ dynsz = sz - readsz;
+ /* Skip empty header */
+ if (dynsz != sizeof(ipfw_obj_ctlv))
+ dynbase = (caddr_t)ctlv;
+ else
+ dynsz = 0;
}
+
+ prepare_format_opts(co, fo, rbase, rcnt, dynbase, dynsz);
+ bp_alloc(&bp, 4096);
+
/* if no rule numbers were specified, list all rules */
if (ac == 0) {
- for (n = 0, r = data; n < nstat; n++, r = NEXT(r)) {
- if (co.use_set && r->set != co.use_set - 1)
- continue;
- show_ipfw(r, pcwidth, bcwidth);
- }
+ fo->first = 0;
+ fo->last = IPFW_DEFAULT_RULE;
+ list_static_range(co, fo, &bp, rbase, rcnt);
- if (co.do_dynamic && ndyn) {
- printf("## Dynamic rules (%d):\n", ndyn);
- for (n = 0, d = dynrules; n < ndyn; n++, d++) {
- if (co.use_set) {
- bcopy((char *)&d->rule + sizeof(uint16_t),
- &set, sizeof(uint8_t));
- if (set != co.use_set - 1)
- continue;
- }
- show_dyn_ipfw(d, pcwidth, bcwidth);
- }
+ if (co->do_dynamic && dynsz > 0) {
+ printf("## Dynamic rules (%d %zu):\n", fo->dcnt, dynsz);
+ list_dyn_range(co, fo, &bp, dynbase, dynsz);
}
- goto done;
+
+ bp_free(&bp);
+ return (EX_OK);
}
/* display specific rules requested on command line */
-
for (lac = ac, lav = av; lac != 0; lac--) {
/* convert command line rule # */
- last = rnum = strtoul(*lav++, &endptr, 10);
+ fo->last = fo->first = strtoul(*lav++, &endptr, 10);
if (*endptr == '-')
- last = strtoul(endptr+1, &endptr, 10);
+ fo->last = strtoul(endptr + 1, &endptr, 10);
if (*endptr) {
exitval = EX_USAGE;
warnx("invalid rule number: %s", *(lav - 1));
continue;
}
- for (n = seen = 0, r = data; n < nstat; n++, r = NEXT(r) ) {
- if (r->rulenum > last)
- break;
- if (co.use_set && r->set != co.use_set - 1)
- continue;
- if (r->rulenum >= rnum && r->rulenum <= last) {
- show_ipfw(r, pcwidth, bcwidth);
- seen = 1;
- }
- }
- if (!seen) {
+
+ if (list_static_range(co, fo, &bp, rbase, rcnt) == 0) {
/* give precedence to other error(s) */
if (exitval == EX_OK)
exitval = EX_UNAVAILABLE;
- warnx("rule %lu does not exist", rnum);
+ if (fo->first == fo->last)
+ warnx("rule %u does not exist", fo->first);
+ else
+ warnx("no rules in range %u-%u",
+ fo->first, fo->last);
}
}
- if (co.do_dynamic && ndyn) {
+ if (co->do_dynamic && dynsz > 0) {
printf("## Dynamic rules:\n");
for (lac = ac, lav = av; lac != 0; lac--) {
- last = rnum = strtoul(*lav++, &endptr, 10);
+ fo->last = fo->first = strtoul(*lav++, &endptr, 10);
if (*endptr == '-')
- last = strtoul(endptr+1, &endptr, 10);
+ fo->last = strtoul(endptr+1, &endptr, 10);
if (*endptr)
/* already warned */
continue;
- for (n = 0, d = dynrules; n < ndyn; n++, d++) {
- uint16_t rulenum;
-
- bcopy(&d->rule, &rulenum, sizeof(rulenum));
- if (rulenum > rnum)
- break;
- if (co.use_set) {
- bcopy((char *)&d->rule + sizeof(uint16_t),
- &set, sizeof(uint8_t));
- if (set != co.use_set - 1)
- continue;
- }
- if (r->rulenum >= rnum && r->rulenum <= last)
- show_dyn_ipfw(d, pcwidth, bcwidth);
- }
+ list_dyn_range(co, fo, &bp, dynbase, dynsz);
}
}
- ac = 0;
+ bp_free(&bp);
+ return (exitval);
+}
-done:
- free(data);
- if (exitval != EX_OK)
- exit(exitval);
-#undef NEXT
+/*
+ * Retrieves current ipfw configuration of given type
+ * and stores its pointer to @pcfg.
+ *
+ * Caller is responsible for freeing @pcfg.
+ *
+ * Returns 0 on success.
+ */
+
+static int
+ipfw_get_config(struct cmdline_opts *co, struct format_opts *fo,
+ ipfw_cfg_lheader **pcfg, size_t *psize)
+{
+ ipfw_cfg_lheader *cfg;
+ size_t sz;
+ int i;
+
+
+ if (co->test_only != 0) {
+ fprintf(stderr, "Testing only, list disabled\n");
+ return (0);
+ }
+
+ /* Start with some data size */
+ sz = 4096;
+ cfg = NULL;
+
+ for (i = 0; i < 16; i++) {
+ if (cfg != NULL)
+ free(cfg);
+ if ((cfg = calloc(1, sz)) == NULL)
+ return (ENOMEM);
+
+ cfg->flags = fo->flags;
+ cfg->start_rule = fo->first;
+ cfg->end_rule = fo->last;
+
+ if (do_get3(IP_FW_XGET, &cfg->opheader, &sz) != 0) {
+ if (errno != ENOMEM) {
+ free(cfg);
+ return (errno);
+ }
+
+ /* Buffer size is not enough. Try to increase */
+ sz = sz * 2;
+ if (sz < cfg->size)
+ sz = cfg->size;
+ continue;
+ }
+
+ *pcfg = cfg;
+ *psize = sz;
+ return (0);
+ }
+
+ free(cfg);
+ return (ENOMEM);
}
static int
@@ -2189,6 +2710,79 @@ lookup_host (char *host, struct in_addr *ipaddr)
return(0);
}
+struct tidx {
+ ipfw_obj_ntlv *idx;
+ uint32_t count;
+ uint32_t size;
+ uint16_t counter;
+ uint8_t set;
+};
+
+static uint16_t
+pack_table(struct tidx *tstate, char *name)
+{
+ int i;
+ ipfw_obj_ntlv *ntlv;
+
+ if (table_check_name(name) != 0)
+ return (0);
+
+ for (i = 0; i < tstate->count; i++) {
+ if (strcmp(tstate->idx[i].name, name) != 0)
+ continue;
+ if (tstate->idx[i].set != tstate->set)
+ continue;
+
+ return (tstate->idx[i].idx);
+ }
+
+ if (tstate->count + 1 > tstate->size) {
+ tstate->size += 4;
+ tstate->idx = realloc(tstate->idx, tstate->size *
+ sizeof(ipfw_obj_ntlv));
+ if (tstate->idx == NULL)
+ return (0);
+ }
+
+ ntlv = &tstate->idx[i];
+ memset(ntlv, 0, sizeof(ipfw_obj_ntlv));
+ strlcpy(ntlv->name, name, sizeof(ntlv->name));
+ ntlv->head.type = IPFW_TLV_TBL_NAME;
+ ntlv->head.length = sizeof(ipfw_obj_ntlv);
+ ntlv->set = tstate->set;
+ ntlv->idx = ++tstate->counter;
+ tstate->count++;
+
+ return (ntlv->idx);
+}
+
+static void
+fill_table(ipfw_insn *cmd, char *av, uint8_t opcode, struct tidx *tstate)
+{
+ uint32_t *d = ((ipfw_insn_u32 *)cmd)->d;
+ uint16_t uidx;
+ char *p;
+
+ if ((p = strchr(av + 6, ')')) == NULL)
+ errx(EX_DATAERR, "forgotten parenthesis: '%s'", av);
+ *p = '\0';
+ p = strchr(av + 6, ',');
+ if (p)
+ *p++ = '\0';
+
+ if ((uidx = pack_table(tstate, av + 6)) == 0)
+ errx(EX_DATAERR, "Invalid table name: %s", av + 6);
+
+ cmd->opcode = opcode;
+ cmd->arg1 = uidx;
+ if (p) {
+ cmd->len |= F_INSN_SIZE(ipfw_insn_u32);
+ d[0] = strtoul(p, NULL, 0);
+ } else
+ cmd->len |= F_INSN_SIZE(ipfw_insn);
+}
+
+
/*
* fills the addr and mask fields in the instruction as appropriate from av.
* Update length as appropriate.
@@ -2201,11 +2795,10 @@ lookup_host (char *host, struct in_addr *ipaddr)
* We can have multiple comma-separated address/mask entries.
*/
static void
-fill_ip(ipfw_insn_ip *cmd, char *av, int cblen)
+fill_ip(ipfw_insn_ip *cmd, char *av, int cblen, struct tidx *tstate)
{
int len = 0;
uint32_t *d = ((ipfw_insn_u32 *)cmd)->d;
- uint32_t tables_max;
cmd->o.len &= ~F_LEN_MASK; /* zero len */
@@ -2218,21 +2811,7 @@ fill_ip(ipfw_insn_ip *cmd, char *av, int cblen)
}
if (strncmp(av, "table(", 6) == 0) {
- char *p = strchr(av + 6, ',');
-
- if (p)
- *p++ = '\0';
- cmd->o.opcode = O_IP_DST_LOOKUP;
- cmd->o.arg1 = strtoul(av + 6, NULL, 0);
- tables_max = ipfw_get_tables_max();
- if (cmd->o.arg1 > tables_max)
- errx(EX_USAGE, "The table number exceeds the maximum "
- "allowed value (%u)", tables_max - 1);
- if (p) {
- cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32);
- d[0] = strtoul(p, NULL, 0);
- } else
- cmd->o.len |= F_INSN_SIZE(ipfw_insn);
+ fill_table(&cmd->o, av, O_IP_DST_LOOKUP, tstate);
return;
}
@@ -2413,35 +2992,16 @@ n2mask(struct in6_addr *mask, int n)
return;
}
-/*
- * helper function to process a set of flags and set bits in the
- * appropriate masks.
- */
static void
-fill_flags(ipfw_insn *cmd, enum ipfw_opcodes opcode,
+fill_flags_cmd(ipfw_insn *cmd, enum ipfw_opcodes opcode,
struct _s_x *flags, char *p)
{
- uint8_t set=0, clear=0;
+ char *e;
+ uint32_t set = 0, clear = 0;
- while (p && *p) {
- char *q; /* points to the separator */
- int val;
- uint8_t *which; /* mask we are working on */
+ if (fill_flags(flags, p, &e, &set, &clear) != 0)
+ errx(EX_DATAERR, "invalid flag %s", e);
- if (*p == '!') {
- p++;
- which = &clear;
- } else
- which = &set;
- q = strchr(p, ',');
- if (q)
- *q++ = '\0';
- val = match_token(flags, p);
- if (val <= 0)
- errx(EX_DATAERR, "invalid flag %s", p);
- *which |= (uint8_t)val;
- p = q;
- }
cmd->opcode = opcode;
cmd->len = (cmd->len & (F_NOT | F_OR)) | 1;
cmd->arg1 = (set & 0xff) | ( (clear & 0xff) << 8);
@@ -2451,13 +3011,14 @@ fill_flags(ipfw_insn *cmd, enum ipfw_opcodes opcode,
void
ipfw_delete(char *av[])
{
- uint32_t rulenum;
int i;
int exitval = EX_OK;
int do_set = 0;
+ ipfw_range_tlv rt;
av++;
NEED1("missing rule specification");
+ memset(&rt, 0, sizeof(rt));
if ( *av && _substrcmp(*av, "set") == 0) {
/* Do not allow using the following syntax:
* ipfw set N delete set M
@@ -2480,16 +3041,34 @@ ipfw_delete(char *av[])
} else if (co.do_pipe) {
exitval = ipfw_delete_pipe(co.do_pipe, i);
} else {
- if (co.use_set)
- rulenum = (i & 0xffff) | (5 << 24) |
- ((co.use_set - 1) << 16);
- else
- rulenum = (i & 0xffff) | (do_set << 24);
- i = do_cmd(IP_FW_DEL, &rulenum, sizeof rulenum);
- if (i) {
+ if (do_set != 0) {
+ rt.set = i & 31;
+ rt.flags = IPFW_RCFLAG_SET;
+ } else {
+ rt.start_rule = i & 0xffff;
+ rt.end_rule = i & 0xffff;
+ if (rt.start_rule == 0 && rt.end_rule == 0)
+ rt.flags |= IPFW_RCFLAG_ALL;
+ else
+ rt.flags |= IPFW_RCFLAG_RANGE;
+ if (co.use_set != 0) {
+ rt.set = co.use_set - 1;
+ rt.flags |= IPFW_RCFLAG_SET;
+ }
+ }
+ i = do_range_cmd(IP_FW_XDEL, &rt);
+ if (i != 0) {
exitval = EX_UNAVAILABLE;
- warn("rule %u: setsockopt(IP_FW_DEL)",
- rulenum);
+ warn("rule %u: setsockopt(IP_FW_XDEL)",
+ rt.start_rule);
+ } else if (rt.new_set == 0) {
+ exitval = EX_UNAVAILABLE;
+ if (rt.start_rule != rt.end_rule)
+ warnx("no rules rules in %u-%u range",
+ rt.start_rule, rt.end_rule);
+ else
+ warnx("rule %u not found",
+ rt.start_rule);
}
}
}
@@ -2506,8 +3085,11 @@ ipfw_delete(char *av[])
* patterns which match interfaces.
*/
static void
-fill_iface(ipfw_insn_if *cmd, char *arg, int cblen)
+fill_iface(ipfw_insn_if *cmd, char *arg, int cblen, struct tidx *tstate)
{
+ char *p;
+ uint16_t uidx;
+
cmd->name[0] = '\0';
cmd->o.len |= F_INSN_SIZE(ipfw_insn_if);
@@ -2517,11 +3099,17 @@ fill_iface(ipfw_insn_if *cmd, char *arg, int cblen)
if (strcmp(arg, "any") == 0)
cmd->o.len = 0; /* effectively ignore this command */
else if (strncmp(arg, "table(", 6) == 0) {
- char *p = strchr(arg + 6, ',');
+ if ((p = strchr(arg + 6, ')')) == NULL)
+ errx(EX_DATAERR, "forgotten parenthesis: '%s'", arg);
+ *p = '\0';
+ p = strchr(arg + 6, ',');
if (p)
*p++ = '\0';
+ if ((uidx = pack_table(tstate, arg + 6)) == 0)
+ errx(EX_DATAERR, "Invalid table name: %s", arg + 6);
+
cmd->name[0] = '\1'; /* Special value indicating table */
- cmd->p.glob = strtoul(arg + 6, NULL, 0);
+ cmd->p.kidx = uidx;
} else if (!isdigit(*arg)) {
strlcpy(cmd->name, arg, sizeof(cmd->name));
cmd->p.glob = strpbrk(arg, "*?[") != NULL ? 1 : 0;
@@ -2735,9 +3323,9 @@ add_proto_compat(ipfw_insn *cmd, char *av, u_char *protop)
}
static ipfw_insn *
-add_srcip(ipfw_insn *cmd, char *av, int cblen)
+add_srcip(ipfw_insn *cmd, char *av, int cblen, struct tidx *tstate)
{
- fill_ip((ipfw_insn_ip *)cmd, av, cblen);
+ fill_ip((ipfw_insn_ip *)cmd, av, cblen, tstate);
if (cmd->opcode == O_IP_DST_SET) /* set */
cmd->opcode = O_IP_SRC_SET;
else if (cmd->opcode == O_IP_DST_LOOKUP) /* table */
@@ -2752,9 +3340,9 @@ add_srcip(ipfw_insn *cmd, char *av, int cblen)
}
static ipfw_insn *
-add_dstip(ipfw_insn *cmd, char *av, int cblen)
+add_dstip(ipfw_insn *cmd, char *av, int cblen, struct tidx *tstate)
{
- fill_ip((ipfw_insn_ip *)cmd, av, cblen);
+ fill_ip((ipfw_insn_ip *)cmd, av, cblen, tstate);
if (cmd->opcode == O_IP_DST_SET) /* set */
;
else if (cmd->opcode == O_IP_DST_LOOKUP) /* table */
@@ -2768,13 +3356,34 @@ add_dstip(ipfw_insn *cmd, char *av, int cblen)
return cmd;
}
+static struct _s_x f_reserved_keywords[] = {
+ { "altq", TOK_OR },
+ { "//", TOK_OR },
+ { "diverted", TOK_OR },
+ { "dst-port", TOK_OR },
+ { "src-port", TOK_OR },
+ { "established", TOK_OR },
+ { "keep-state", TOK_OR },
+ { "frag", TOK_OR },
+ { "icmptypes", TOK_OR },
+ { "in", TOK_OR },
+ { "out", TOK_OR },
+ { "ip6", TOK_OR },
+ { "any", TOK_OR },
+ { "to", TOK_OR },
+ { "via", TOK_OR },
+ { "{", TOK_OR },
+ { NULL, 0 } /* terminator */
+};
+
static ipfw_insn *
add_ports(ipfw_insn *cmd, char *av, u_char proto, int opcode, int cblen)
{
- /* XXX "any" is trapped before. Perhaps "to" */
- if (_substrcmp(av, "any") == 0) {
- return NULL;
- } else if (fill_newports((ipfw_insn_u16 *)cmd, av, proto, cblen)) {
+
+ if (match_token(f_reserved_keywords, av) != -1)
+ return (NULL);
+
+ if (fill_newports((ipfw_insn_u16 *)cmd, av, proto, cblen)) {
/* XXX todo: check that we have a protocol with ports */
cmd->opcode = opcode;
return cmd;
@@ -2783,7 +3392,7 @@ add_ports(ipfw_insn *cmd, char *av, u_char proto, int opcode, int cblen)
}
static ipfw_insn *
-add_src(ipfw_insn *cmd, char *av, u_char proto, int cblen)
+add_src(ipfw_insn *cmd, char *av, u_char proto, int cblen, struct tidx *tstate)
{
struct in6_addr a;
char *host, *ch, buf[INET6_ADDRSTRLEN];
@@ -2806,7 +3415,7 @@ add_src(ipfw_insn *cmd, char *av, u_char proto, int cblen)
/* XXX: should check for IPv4, not !IPv6 */
if (ret == NULL && (proto == IPPROTO_IP || strcmp(av, "me") == 0 ||
inet_pton(AF_INET6, host, &a) != 1))
- ret = add_srcip(cmd, av, cblen);
+ ret = add_srcip(cmd, av, cblen, tstate);
if (ret == NULL && strcmp(av, "any") != 0)
ret = cmd;
@@ -2814,7 +3423,7 @@ add_src(ipfw_insn *cmd, char *av, u_char proto, int cblen)
}
static ipfw_insn *
-add_dst(ipfw_insn *cmd, char *av, u_char proto, int cblen)
+add_dst(ipfw_insn *cmd, char *av, u_char proto, int cblen, struct tidx *tstate)
{
struct in6_addr a;
char *host, *ch, buf[INET6_ADDRSTRLEN];
@@ -2837,7 +3446,7 @@ add_dst(ipfw_insn *cmd, char *av, u_char proto, int cblen)
/* XXX: should check for IPv4, not !IPv6 */
if (ret == NULL && (proto == IPPROTO_IP || strcmp(av, "me") == 0 ||
inet_pton(AF_INET6, host, &a) != 1))
- ret = add_dstip(cmd, av, cblen);
+ ret = add_dstip(cmd, av, cblen, tstate);
if (ret == NULL && strcmp(av, "any") != 0)
ret = cmd;
@@ -2857,7 +3466,7 @@ add_dst(ipfw_insn *cmd, char *av, u_char proto, int cblen)
*
*/
void
-ipfw_add(char *av[])
+compile_rule(char *av[], uint32_t *rbuf, int *rbufsize, struct tidx *tstate)
{
/*
* rules are added into the 'rulebuf' and then copied in
@@ -2865,13 +3474,13 @@ ipfw_add(char *av[])
* Some things that need to go out of order (prob, action etc.)
* go into actbuf[].
*/
- static uint32_t rulebuf[255], actbuf[255], cmdbuf[255];
+ static uint32_t actbuf[255], cmdbuf[255];
int rblen, ablen, cblen;
ipfw_insn *src, *dst, *cmd, *action, *prev=NULL;
ipfw_insn *first_cmd; /* first match pattern */
- struct ip_fw *rule;
+ struct ip_fw_rule *rule;
/*
* various flags used to record that we entered some fields.
@@ -2891,14 +3500,14 @@ ipfw_add(char *av[])
bzero(actbuf, sizeof(actbuf)); /* actions go here */
bzero(cmdbuf, sizeof(cmdbuf));
- bzero(rulebuf, sizeof(rulebuf));
+ bzero(rbuf, *rbufsize);
- rule = (struct ip_fw *)rulebuf;
+ rule = (struct ip_fw_rule *)rbuf;
cmd = (ipfw_insn *)cmdbuf;
action = (ipfw_insn *)actbuf;
- rblen = sizeof(rulebuf) / sizeof(rulebuf[0]);
- rblen -= offsetof(struct ip_fw, cmd) / sizeof(rulebuf[0]);
+ rblen = *rbufsize / sizeof(uint32_t);
+ rblen -= sizeof(struct ip_fw_rule) / sizeof(uint32_t);
ablen = sizeof(actbuf) / sizeof(actbuf[0]);
cblen = sizeof(cmdbuf) / sizeof(cmdbuf[0]);
cblen -= F_INSN_SIZE(ipfw_insn_u32) + 1;
@@ -2920,6 +3529,7 @@ ipfw_add(char *av[])
if (set < 0 || set > RESVD_SET)
errx(EX_DATAERR, "illegal set %s", av[1]);
rule->set = set;
+ tstate->set = set;
av += 2;
}
@@ -3029,7 +3639,7 @@ chkarg:
errx(EX_DATAERR, "illegal argument for %s",
*(av - 1));
} else if (_substrcmp(*av, "tablearg") == 0) {
- action->arg1 = IP_FW_TABLEARG;
+ action->arg1 = IP_FW_TARG;
} else if (i == TOK_DIVERT || i == TOK_TEE) {
struct servent *s;
setservent(1);
@@ -3153,7 +3763,7 @@ chkarg:
action->opcode = O_SETFIB;
NEED1("missing fib number");
if (_substrcmp(*av, "tablearg") == 0) {
- action->arg1 = IP_FW_TABLEARG;
+ action->arg1 = IP_FW_TARG;
} else {
action->arg1 = strtoul(*av, NULL, 10);
if (sysctlbyname("net.fibs", &numfibs, &intsize,
@@ -3161,6 +3771,8 @@ chkarg:
errx(EX_DATAERR, "fibs not suported.\n");
if (action->arg1 >= numfibs) /* Temporary */
errx(EX_DATAERR, "fib too large.\n");
+ /* Add high-order bit to fib to make room for tablearg*/
+ action->arg1 |= 0x8000;
}
av++;
break;
@@ -3173,13 +3785,16 @@ chkarg:
action->opcode = O_SETDSCP;
NEED1("missing DSCP code");
if (_substrcmp(*av, "tablearg") == 0) {
- action->arg1 = IP_FW_TABLEARG;
+ action->arg1 = IP_FW_TARG;
} else if (isalpha(*av[0])) {
if ((code = match_token(f_ipdscp, *av)) == -1)
errx(EX_DATAERR, "Unknown DSCP code");
action->arg1 = code;
} else
action->arg1 = strtoul(*av, NULL, 10);
+ /* Add high-order bit to DSCP to make room for tablearg */
+ if (action->arg1 != IP_FW_TARG)
+ action->arg1 |= 0x8000;
av++;
break;
}
@@ -3386,7 +4001,7 @@ chkarg:
OR_START(source_ip);
NOT_BLOCK; /* optional "not" */
NEED1("missing source address");
- if (add_src(cmd, *av, proto, cblen)) {
+ if (add_src(cmd, *av, proto, cblen, tstate)) {
av++;
if (F_LEN(cmd) != 0) { /* ! any */
prev = cmd;
@@ -3422,7 +4037,7 @@ chkarg:
OR_START(dest_ip);
NOT_BLOCK; /* optional "not" */
NEED1("missing dst address");
- if (add_dst(cmd, *av, proto, cblen)) {
+ if (add_dst(cmd, *av, proto, cblen, tstate)) {
av++;
if (F_LEN(cmd) != 0) { /* ! any */
prev = cmd;
@@ -3451,7 +4066,7 @@ read_options:
* nothing specified so far, store in the rule to ease
* printout later.
*/
- rule->_pad = 1;
+ rule->flags |= IPFW_RULE_NOOPT;
}
prev = NULL;
while ( av[0] != NULL ) {
@@ -3529,7 +4144,7 @@ read_options:
case TOK_VIA:
NEED1("recv, xmit, via require interface name"
" or address");
- fill_iface((ipfw_insn_if *)cmd, av[0], cblen);
+ fill_iface((ipfw_insn_if *)cmd, av[0], cblen, tstate);
av++;
if (F_LEN(cmd) == 0) /* not a valid address */
break;
@@ -3604,13 +4219,13 @@ read_options:
case TOK_IPOPTS:
NEED1("missing argument for ipoptions");
- fill_flags(cmd, O_IPOPT, f_ipopts, *av);
+ fill_flags_cmd(cmd, O_IPOPT, f_ipopts, *av);
av++;
break;
case TOK_IPTOS:
NEED1("missing argument for iptos");
- fill_flags(cmd, O_IPTOS, f_iptos, *av);
+ fill_flags_cmd(cmd, O_IPTOS, f_iptos, *av);
av++;
break;
@@ -3688,7 +4303,7 @@ read_options:
case TOK_TCPOPTS:
NEED1("missing argument for tcpoptions");
- fill_flags(cmd, O_TCPOPTS, f_tcpopts, *av);
+ fill_flags_cmd(cmd, O_TCPOPTS, f_tcpopts, *av);
av++;
break;
@@ -3715,7 +4330,7 @@ read_options:
case TOK_TCPFLAGS:
NEED1("missing argument for tcpflags");
cmd->opcode = O_TCPFLAGS;
- fill_flags(cmd, O_TCPFLAGS, f_tcpflags, *av);
+ fill_flags_cmd(cmd, O_TCPFLAGS, f_tcpflags, *av);
av++;
break;
@@ -3775,14 +4390,14 @@ read_options:
case TOK_SRCIP:
NEED1("missing source IP");
- if (add_srcip(cmd, *av, cblen)) {
+ if (add_srcip(cmd, *av, cblen, tstate)) {
av++;
}
break;
case TOK_DSTIP:
NEED1("missing destination IP");
- if (add_dstip(cmd, *av, cblen)) {
+ if (add_dstip(cmd, *av, cblen, tstate)) {
av++;
}
break;
@@ -3901,7 +4516,6 @@ read_options:
case TOK_LOOKUP: {
ipfw_insn_u32 *c = (ipfw_insn_u32 *)cmd;
- char *p;
int j;
if (!av[0] || !av[1])
@@ -3917,12 +4531,22 @@ read_options:
errx(EX_USAGE, "format: cannot lookup on %s", *av);
__PAST_END(c->d, 1) = j; // i converted to option
av++;
- cmd->arg1 = strtoul(*av, &p, 0);
- if (p && *p)
- errx(EX_USAGE, "format: lookup argument tablenum");
+
+ if ((j = pack_table(tstate, *av)) == 0)
+ errx(EX_DATAERR, "Invalid table name: %s", *av);
+
+ cmd->arg1 = j;
av++;
}
break;
+ case TOK_FLOW:
+ NEED1("missing table name");
+ if (strncmp(*av, "table(", 6) != 0)
+ errx(EX_DATAERR,
+ "enclose table name into \"table()\"");
+ fill_table(cmd, *av, O_IP_FLOW_LOOKUP, tstate);
+ av++;
+ break;
default:
errx(EX_USAGE, "unrecognised option [%d] %s\n", i, s);
@@ -4024,34 +4648,143 @@ done:
}
rule->cmd_len = (uint32_t *)dst - (uint32_t *)(rule->cmd);
- i = (char *)dst - (char *)rule;
- if (do_cmd(IP_FW_ADD, rule, (uintptr_t)&i) == -1)
- err(EX_UNAVAILABLE, "getsockopt(%s)", "IP_FW_ADD");
- if (!co.do_quiet)
- show_ipfw(rule, 0, 0);
+ *rbufsize = (char *)dst - (char *)rule;
+}
+
+/*
+ * Adds one or more rules to ipfw chain.
+ * Data layout:
+ * Request:
+ * [
+ * ip_fw3_opheader
+ * [ ipfw_obj_ctlv(IPFW_TLV_TBL_LIST) ipfw_obj_ntlv x N ] (optional *1)
+ * [ ipfw_obj_ctlv(IPFW_TLV_RULE_LIST) [ ip_fw_rule ip_fw_insn ] x N ] (*2) (*3)
+ * ]
+ * Reply:
+ * [
+ * ip_fw3_opheader
+ * [ ipfw_obj_ctlv(IPFW_TLV_TBL_LIST) ipfw_obj_ntlv x N ] (optional)
+ * [ ipfw_obj_ctlv(IPFW_TLV_RULE_LIST) [ ip_fw_rule ip_fw_insn ] x N ]
+ * ]
+ *
+ * Rules in reply are modified to store their actual ruleset number.
+ *
+ * (*1) TLVs inside IPFW_TLV_TBL_LIST needs to be sorted ascending
+ * accoring to their idx field and there has to be no duplicates.
+ * (*2) Numbered rules inside IPFW_TLV_RULE_LIST needs to be sorted ascending.
+ * (*3) Each ip_fw structure needs to be aligned to u64 boundary.
+ */
+void
+ipfw_add(char *av[])
+{
+ uint32_t rulebuf[1024];
+ int rbufsize, default_off, tlen, rlen;
+ size_t sz;
+ struct tidx ts;
+ struct ip_fw_rule *rule;
+ caddr_t tbuf;
+ ip_fw3_opheader *op3;
+ ipfw_obj_ctlv *ctlv, *tstate;
+
+ rbufsize = sizeof(rulebuf);
+ memset(rulebuf, 0, rbufsize);
+ memset(&ts, 0, sizeof(ts));
+
+ /* Optimize case with no tables */
+ default_off = sizeof(ipfw_obj_ctlv) + sizeof(ip_fw3_opheader);
+ op3 = (ip_fw3_opheader *)rulebuf;
+ ctlv = (ipfw_obj_ctlv *)(op3 + 1);
+ rule = (struct ip_fw_rule *)(ctlv + 1);
+ rbufsize -= default_off;
+
+ compile_rule(av, (uint32_t *)rule, &rbufsize, &ts);
+ /* Align rule size to u64 boundary */
+ rlen = roundup2(rbufsize, sizeof(uint64_t));
+
+ tbuf = NULL;
+ sz = 0;
+ tstate = NULL;
+ if (ts.count != 0) {
+ /* Some tables. We have to alloc more data */
+ tlen = ts.count * sizeof(ipfw_obj_ntlv);
+ sz = default_off + sizeof(ipfw_obj_ctlv) + tlen + rlen;
+
+ if ((tbuf = calloc(1, sz)) == NULL)
+ err(EX_UNAVAILABLE, "malloc() failed for IP_FW_ADD");
+ op3 = (ip_fw3_opheader *)tbuf;
+ /* Tables first */
+ ctlv = (ipfw_obj_ctlv *)(op3 + 1);
+ ctlv->head.type = IPFW_TLV_TBLNAME_LIST;
+ ctlv->head.length = sizeof(ipfw_obj_ctlv) + tlen;
+ ctlv->count = ts.count;
+ ctlv->objsize = sizeof(ipfw_obj_ntlv);
+ memcpy(ctlv + 1, ts.idx, tlen);
+ table_sort_ctlv(ctlv);
+ tstate = ctlv;
+ /* Rule next */
+ ctlv = (ipfw_obj_ctlv *)((caddr_t)ctlv + ctlv->head.length);
+ ctlv->head.type = IPFW_TLV_RULE_LIST;
+ ctlv->head.length = sizeof(ipfw_obj_ctlv) + rlen;
+ ctlv->count = 1;
+ memcpy(ctlv + 1, rule, rbufsize);
+ } else {
+ /* Simply add header */
+ sz = rlen + default_off;
+ memset(ctlv, 0, sizeof(*ctlv));
+ ctlv->head.type = IPFW_TLV_RULE_LIST;
+ ctlv->head.length = sizeof(ipfw_obj_ctlv) + rlen;
+ ctlv->count = 1;
+ }
+
+ if (do_get3(IP_FW_XADD, op3, &sz) != 0)
+ err(EX_UNAVAILABLE, "getsockopt(%s)", "IP_FW_XADD");
+
+ if (!co.do_quiet) {
+ struct format_opts sfo;
+ struct buf_pr bp;
+ memset(&sfo, 0, sizeof(sfo));
+ sfo.tstate = tstate;
+ sfo.set_mask = (uint32_t)(-1);
+ bp_alloc(&bp, 4096);
+ show_static_rule(&co, &sfo, &bp, rule, NULL);
+ printf("%s", bp.buf);
+ bp_free(&bp);
+ }
+
+ if (tbuf != NULL)
+ free(tbuf);
+
+ if (ts.idx != NULL)
+ free(ts.idx);
}
/*
* clear the counters or the log counters.
+ * optname has the following values:
+ * 0 (zero both counters and logging)
+ * 1 (zero logging only)
*/
void
-ipfw_zero(int ac, char *av[], int optname /* 0 = IP_FW_ZERO, 1 = IP_FW_RESETLOG */)
+ipfw_zero(int ac, char *av[], int optname)
{
- uint32_t arg, saved_arg;
+ ipfw_range_tlv rt;
+ uint32_t arg;
int failed = EX_OK;
char const *errstr;
char const *name = optname ? "RESETLOG" : "ZERO";
- optname = optname ? IP_FW_RESETLOG : IP_FW_ZERO;
+ optname = optname ? IP_FW_XRESETLOG : IP_FW_XZERO;
+ memset(&rt, 0, sizeof(rt));
av++; ac--;
- if (!ac) {
+ if (ac == 0) {
/* clear all entries */
- if (do_cmd(optname, NULL, 0) < 0)
- err(EX_UNAVAILABLE, "setsockopt(IP_FW_%s)", name);
+ rt.flags = IPFW_RCFLAG_ALL;
+ if (do_range_cmd(optname, &rt) < 0)
+ err(EX_UNAVAILABLE, "setsockopt(IP_FW_X%s)", name);
if (!co.do_quiet)
- printf("%s.\n", optname == IP_FW_ZERO ?
+ printf("%s.\n", optname == IP_FW_XZERO ?
"Accounting cleared":"Logging counts reset");
return;
@@ -4064,22 +4797,28 @@ ipfw_zero(int ac, char *av[], int optname /* 0 = IP_FW_ZERO, 1 = IP_FW_RESETLOG
if (errstr)
errx(EX_DATAERR,
"invalid rule number %s\n", *av);
- saved_arg = arg;
- if (co.use_set)
- arg |= (1 << 24) | ((co.use_set - 1) << 16);
- av++;
- ac--;
- if (do_cmd(optname, &arg, sizeof(arg))) {
- warn("rule %u: setsockopt(IP_FW_%s)",
- saved_arg, name);
+ rt.start_rule = arg;
+ rt.end_rule = arg;
+ rt.flags |= IPFW_RCFLAG_RANGE;
+ if (co.use_set != 0) {
+ rt.set = co.use_set - 1;
+ rt.flags |= IPFW_RCFLAG_SET;
+ }
+ if (do_range_cmd(optname, &rt) != 0) {
+ warn("rule %u: setsockopt(IP_FW_X%s)",
+ arg, name);
+ failed = EX_UNAVAILABLE;
+ } else if (rt.new_set == 0) {
+ printf("Entry %d not found\n", arg);
failed = EX_UNAVAILABLE;
} else if (!co.do_quiet)
- printf("Entry %d %s.\n", saved_arg,
- optname == IP_FW_ZERO ?
+ printf("Entry %d %s.\n", arg,
+ optname == IP_FW_XZERO ?
"cleared" : "logging count reset");
} else {
errx(EX_USAGE, "invalid rule number ``%s''", *av);
}
+ av++; ac--;
}
if (failed != EX_OK)
exit(failed);
@@ -4088,7 +4827,7 @@ ipfw_zero(int ac, char *av[], int optname /* 0 = IP_FW_ZERO, 1 = IP_FW_RESETLOG
void
ipfw_flush(int force)
{
- int cmd = co.do_pipe ? IP_DUMMYNET_FLUSH : IP_FW_FLUSH;
+ ipfw_range_tlv rt;
if (!force && !co.do_quiet) { /* need to ask user */
int c;
@@ -4110,316 +4849,121 @@ ipfw_flush(int force)
return;
}
/* `ipfw set N flush` - is the same that `ipfw delete set N` */
- if (co.use_set) {
- uint32_t arg = ((co.use_set - 1) & 0xffff) | (1 << 24);
- if (do_cmd(IP_FW_DEL, &arg, sizeof(arg)) < 0)
- err(EX_UNAVAILABLE, "setsockopt(IP_FW_DEL)");
- } else if (do_cmd(cmd, NULL, 0) < 0)
- err(EX_UNAVAILABLE, "setsockopt(IP_%s_FLUSH)",
- co.do_pipe ? "DUMMYNET" : "FW");
+ memset(&rt, 0, sizeof(rt));
+ if (co.use_set != 0) {
+ rt.set = co.use_set - 1;
+ rt.flags = IPFW_RCFLAG_SET;
+ } else
+ rt.flags = IPFW_RCFLAG_ALL;
+ if (do_range_cmd(IP_FW_XDEL, &rt) != 0)
+ err(EX_UNAVAILABLE, "setsockopt(IP_FW_XDEL)");
if (!co.do_quiet)
printf("Flushed all %s.\n", co.do_pipe ? "pipes" : "rules");
}
+static struct _s_x intcmds[] = {
+ { "talist", TOK_TALIST },
+ { "iflist", TOK_IFLIST },
+ { "vlist", TOK_VLIST },
+ { NULL, 0 }
+};
-static void table_list(uint16_t num, int need_header);
-static void table_fill_xentry(char *arg, ipfw_table_xentry *xent);
-
-/*
- * Retrieve maximum number of tables supported by ipfw(4) module.
- */
-uint32_t
-ipfw_get_tables_max()
-{
- size_t len;
- uint32_t tables_max;
-
- if (ipfw_tables_max != 0)
- return (ipfw_tables_max);
-
- len = sizeof(tables_max);
- if (sysctlbyname("net.inet.ip.fw.tables_max", &tables_max, &len,
- NULL, 0) == -1) {
- if (co.test_only)
- tables_max = 128; /* Old conservative default */
- else
- errx(1, "Can't determine maximum number of ipfw tables."
- " Perhaps you forgot to load ipfw module?");
- }
-
- ipfw_tables_max = tables_max;
-
- return (ipfw_tables_max);
-}
-
-/*
- * This one handles all table-related commands
- * ipfw table N add addr[/masklen] [value]
- * ipfw table N delete addr[/masklen]
- * ipfw table {N | all} flush
- * ipfw table {N | all} list
- */
void
-ipfw_table_handler(int ac, char *av[])
+ipfw_internal_handler(int ac, char *av[])
{
- ipfw_table_xentry xent;
- int do_add;
- int is_all;
- uint32_t a;
- uint32_t tables_max;
+ int tcmd;
- tables_max = ipfw_get_tables_max();
+ ac--; av++;
+ NEED1("internal cmd required");
- memset(&xent, 0, sizeof(xent));
+ if ((tcmd = match_token(intcmds, *av)) == -1)
+ errx(EX_USAGE, "invalid internal sub-cmd: %s", *av);
- ac--; av++;
- if (ac && isdigit(**av)) {
- xent.tbl = atoi(*av);
- is_all = 0;
- ac--; av++;
- } else if (ac && _substrcmp(*av, "all") == 0) {
- xent.tbl = 0;
- is_all = 1;
- ac--; av++;
- } else
- errx(EX_USAGE, "table number or 'all' keyword required");
- if (xent.tbl >= tables_max)
- errx(EX_USAGE, "The table number exceeds the maximum allowed "
- "value (%d)", tables_max - 1);
- NEED1("table needs command");
- if (is_all && _substrcmp(*av, "list") != 0
- && _substrcmp(*av, "flush") != 0)
- errx(EX_USAGE, "table number required");
-
- if (_substrcmp(*av, "add") == 0 ||
- _substrcmp(*av, "delete") == 0) {
- do_add = **av == 'a';
- ac--; av++;
- if (!ac)
- errx(EX_USAGE, "address required");
-
- table_fill_xentry(*av, &xent);
-
- ac--; av++;
- if (do_add && ac) {
- unsigned int tval;
- /* isdigit is a bit of a hack here.. */
- if (strchr(*av, (int)'.') == NULL && isdigit(**av)) {
- xent.value = strtoul(*av, NULL, 0);
- } else {
- if (lookup_host(*av, (struct in_addr *)&tval) == 0) {
- /* The value must be stored in host order *
- * so that the values < 65k can be distinguished */
- xent.value = ntohl(tval);
- } else {
- errx(EX_NOHOST, "hostname ``%s'' unknown", *av);
- }
- }
- } else
- xent.value = 0;
- if (do_setcmd3(do_add ? IP_FW_TABLE_XADD : IP_FW_TABLE_XDEL,
- &xent, xent.len) < 0) {
- /* If running silent, don't bomb out on these errors. */
- if (!(co.do_quiet && (errno == (do_add ? EEXIST : ESRCH))))
- err(EX_OSERR, "setsockopt(IP_FW_TABLE_%s)",
- do_add ? "XADD" : "XDEL");
- /* In silent mode, react to a failed add by deleting */
- if (do_add) {
- do_setcmd3(IP_FW_TABLE_XDEL, &xent, xent.len);
- if (do_setcmd3(IP_FW_TABLE_XADD, &xent, xent.len) < 0)
- err(EX_OSERR,
- "setsockopt(IP_FW_TABLE_XADD)");
- }
- }
- } else if (_substrcmp(*av, "flush") == 0) {
- a = is_all ? tables_max : (uint32_t)(xent.tbl + 1);
- do {
- if (do_cmd(IP_FW_TABLE_FLUSH, &xent.tbl,
- sizeof(xent.tbl)) < 0)
- err(EX_OSERR, "setsockopt(IP_FW_TABLE_FLUSH)");
- } while (++xent.tbl < a);
- } else if (_substrcmp(*av, "list") == 0) {
- a = is_all ? tables_max : (uint32_t)(xent.tbl + 1);
- do {
- table_list(xent.tbl, is_all);
- } while (++xent.tbl < a);
- } else
- errx(EX_USAGE, "invalid table command %s", *av);
+ switch (tcmd) {
+ case TOK_IFLIST:
+ ipfw_list_tifaces();
+ break;
+ case TOK_TALIST:
+ ipfw_list_ta(ac, av);
+ break;
+ case TOK_VLIST:
+ ipfw_list_values(ac, av);
+ break;
+ }
}
-static void
-table_fill_xentry(char *arg, ipfw_table_xentry *xent)
+static int
+ipfw_get_tracked_ifaces(ipfw_obj_lheader **polh)
{
- int addrlen, mask, masklen, type;
- struct in6_addr *paddr;
- uint32_t *pkey;
- char *p;
- uint32_t key;
-
- mask = 0;
- type = 0;
- addrlen = 0;
- masklen = 0;
-
- /*
- * Let's try to guess type by agrument.
- * Possible types:
- * 1) IPv4[/mask]
- * 2) IPv6[/mask]
- * 3) interface name
- * 4) port, uid/gid or other u32 key (base 10 format)
- * 5) hostname
- */
- paddr = &xent->k.addr6;
- if (ishexnumber(*arg) != 0 || *arg == ':') {
- /* Remove / if exists */
- if ((p = strchr(arg, '/')) != NULL) {
- *p = '\0';
- mask = atoi(p + 1);
- }
-
- if (inet_pton(AF_INET, arg, paddr) == 1) {
- if (p != NULL && mask > 32)
- errx(EX_DATAERR, "bad IPv4 mask width: %s",
- p + 1);
+ ipfw_obj_lheader req, *olh;
+ size_t sz;
- type = IPFW_TABLE_CIDR;
- masklen = p ? mask : 32;
- addrlen = sizeof(struct in_addr);
- } else if (inet_pton(AF_INET6, arg, paddr) == 1) {
- if (IN6_IS_ADDR_V4COMPAT(paddr))
- errx(EX_DATAERR,
- "Use IPv4 instead of v4-compatible");
- if (p != NULL && mask > 128)
- errx(EX_DATAERR, "bad IPv6 mask width: %s",
- p + 1);
-
- type = IPFW_TABLE_CIDR;
- masklen = p ? mask : 128;
- addrlen = sizeof(struct in6_addr);
- } else {
- /* Port or any other key */
- /* Skip non-base 10 entries like 'fa1' */
- key = strtol(arg, &p, 10);
- if (*p == '\0') {
- pkey = (uint32_t *)paddr;
- *pkey = htonl(key);
- type = IPFW_TABLE_CIDR;
- masklen = 32;
- addrlen = sizeof(uint32_t);
- } else if ((p != arg) && (*p == '.')) {
- /*
- * Warn on IPv4 address strings
- * which are "valid" for inet_aton() but not
- * in inet_pton().
- *
- * Typical examples: '10.5' or '10.0.0.05'
- */
- errx(EX_DATAERR,
- "Invalid IPv4 address: %s", arg);
- }
- }
- }
+ memset(&req, 0, sizeof(req));
+ sz = sizeof(req);
- if (type == 0 && strchr(arg, '.') == NULL) {
- /* Assume interface name. Copy significant data only */
- mask = MIN(strlen(arg), IF_NAMESIZE - 1);
- memcpy(xent->k.iface, arg, mask);
- /* Set mask to exact match */
- masklen = 8 * IF_NAMESIZE;
- type = IPFW_TABLE_INTERFACE;
- addrlen = IF_NAMESIZE;
+ if (do_get3(IP_FW_XIFLIST, &req.opheader, &sz) != 0) {
+ if (errno != ENOMEM)
+ return (errno);
}
- if (type == 0) {
- if (lookup_host(arg, (struct in_addr *)paddr) != 0)
- errx(EX_NOHOST, "hostname ``%s'' unknown", arg);
+ sz = req.size;
+ if ((olh = calloc(1, sz)) == NULL)
+ return (ENOMEM);
- masklen = 32;
- type = IPFW_TABLE_CIDR;
- addrlen = sizeof(struct in_addr);
+ olh->size = sz;
+ if (do_get3(IP_FW_XIFLIST, &olh->opheader, &sz) != 0) {
+ free(olh);
+ return (errno);
}
- xent->type = type;
- xent->masklen = masklen;
- xent->len = offsetof(ipfw_table_xentry, k) + addrlen;
+ *polh = olh;
+ return (0);
}
-static void
-table_list(uint16_t num, int need_header)
+static int
+ifinfo_cmp(const void *a, const void *b)
{
- ipfw_xtable *tbl;
- ipfw_table_xentry *xent;
- socklen_t l;
- uint32_t *a, sz, tval;
- char tbuf[128];
- struct in6_addr *addr6;
- ip_fw3_opheader *op3;
+ ipfw_iface_info *ia, *ib;
- /* Prepend value with IP_FW3 header */
- l = sizeof(ip_fw3_opheader) + sizeof(uint32_t);
- op3 = alloca(l);
- /* Zero reserved fields */
- memset(op3, 0, sizeof(ip_fw3_opheader));
- a = (uint32_t *)(op3 + 1);
- *a = num;
- op3->opcode = IP_FW_TABLE_XGETSIZE;
- if (do_cmd(IP_FW3, op3, (uintptr_t)&l) < 0)
- err(EX_OSERR, "getsockopt(IP_FW_TABLE_XGETSIZE)");
-
- /* If a is zero we have nothing to do, the table is empty. */
- if (*a == 0)
- return;
+ ia = (ipfw_iface_info *)a;
+ ib = (ipfw_iface_info *)b;
- l = *a;
- tbl = safe_calloc(1, l);
- tbl->opheader.opcode = IP_FW_TABLE_XLIST;
- tbl->tbl = num;
- if (do_cmd(IP_FW3, tbl, (uintptr_t)&l) < 0)
- err(EX_OSERR, "getsockopt(IP_FW_TABLE_XLIST)");
- if (tbl->cnt && need_header)
- printf("---table(%d)---\n", tbl->tbl);
- sz = tbl->size - sizeof(ipfw_xtable);
- xent = &tbl->xent[0];
- while (sz > 0) {
- switch (tbl->type) {
- case IPFW_TABLE_CIDR:
- /* IPv4 or IPv6 prefixes */
- tval = xent->value;
- addr6 = &xent->k.addr6;
+ return (stringnum_cmp(ia->ifname, ib->ifname));
+}
+/*
+ * Retrieves table list from kernel,
+ * optionally sorts it and calls requested function for each table.
+ * Returns 0 on success.
+ */
+static void
+ipfw_list_tifaces()
+{
+ ipfw_obj_lheader *olh;
+ ipfw_iface_info *info;
+ int i, error;
- if ((xent->flags & IPFW_TCF_INET) != 0) {
- /* IPv4 address */
- inet_ntop(AF_INET, &addr6->s6_addr32[3], tbuf, sizeof(tbuf));
- } else {
- /* IPv6 address */
- inet_ntop(AF_INET6, addr6, tbuf, sizeof(tbuf));
- }
+ if ((error = ipfw_get_tracked_ifaces(&olh)) != 0)
+ err(EX_OSERR, "Unable to request ipfw tracked interface list");
- if (co.do_value_as_ip) {
- tval = htonl(tval);
- printf("%s/%u %s\n", tbuf, xent->masklen,
- inet_ntoa(*(struct in_addr *)&tval));
- } else
- printf("%s/%u %u\n", tbuf, xent->masklen, tval);
- break;
- case IPFW_TABLE_INTERFACE:
- /* Interface names */
- tval = xent->value;
- if (co.do_value_as_ip) {
- tval = htonl(tval);
- printf("%s %s\n", xent->k.iface,
- inet_ntoa(*(struct in_addr *)&tval));
- } else
- printf("%s %u\n", xent->k.iface, tval);
- }
- if (sz < xent->len)
- break;
- sz -= xent->len;
- xent = (ipfw_table_xentry *)((char *)xent + xent->len);
+ qsort(olh + 1, olh->count, olh->objsize, ifinfo_cmp);
+
+ info = (ipfw_iface_info *)(olh + 1);
+ for (i = 0; i < olh->count; i++) {
+ if (info->flags & IPFW_IFFLAG_RESOLVED)
+ printf("%s ifindex: %d refcount: %u changes: %u\n",
+ info->ifname, info->ifindex, info->refcnt,
+ info->gencnt);
+ else
+ printf("%s ifindex: unresolved refcount: %u changes: %u\n",
+ info->ifname, info->refcnt, info->gencnt);
+ info = (ipfw_iface_info *)((caddr_t)info + olh->objsize);
}
- free(tbl);
+ free(olh);
}
+
+
+
+
diff --git a/sbin/ipfw/ipfw2.h b/sbin/ipfw/ipfw2.h
index 2301c40f2b76..80970ef0deee 100644
--- a/sbin/ipfw/ipfw2.h
+++ b/sbin/ipfw/ipfw2.h
@@ -71,6 +71,8 @@ struct _s_x {
int x;
};
+extern struct _s_x f_ipdscp[];
+
enum tokens {
TOK_NULL=0,
@@ -205,7 +207,28 @@ enum tokens {
TOK_LOOKUP,
TOK_SOCKARG,
TOK_SETDSCP,
+ TOK_FLOW,
+ TOK_IFLIST,
+ /* Table tokens */
+ TOK_CREATE,
+ TOK_DESTROY,
+ TOK_LIST,
+ TOK_INFO,
+ TOK_DETAIL,
+ TOK_MODIFY,
+ TOK_FLUSH,
+ TOK_SWAP,
+ TOK_ADD,
+ TOK_DEL,
+ TOK_VALTYPE,
+ TOK_ALGO,
+ TOK_TALIST,
+ TOK_ATOMIC,
+ TOK_LOCK,
+ TOK_UNLOCK,
+ TOK_VLIST,
};
+
/*
* the following macro returns an error message if we run out of
* arguments.
@@ -213,7 +236,19 @@ enum tokens {
#define NEED(_p, msg) {if (!_p) errx(EX_USAGE, msg);}
#define NEED1(msg) {if (!(*av)) errx(EX_USAGE, msg);}
-int pr_u64(uint64_t *pd, int width);
+struct buf_pr {
+ char *buf; /* allocated buffer */
+ char *ptr; /* current pointer */
+ size_t size; /* total buffer size */
+ size_t avail; /* available storage */
+ size_t needed; /* length needed */
+};
+
+int pr_u64(struct buf_pr *bp, uint64_t *pd, int width);
+int bp_alloc(struct buf_pr *b, size_t size);
+void bp_free(struct buf_pr *b);
+int bprintf(struct buf_pr *b, char *format, ...);
+
/* memory allocation support */
void *safe_calloc(size_t number, size_t size);
@@ -222,14 +257,22 @@ void *safe_realloc(void *ptr, size_t size);
/* string comparison functions used for historical compatibility */
int _substrcmp(const char *str1, const char* str2);
int _substrcmp2(const char *str1, const char* str2, const char* str3);
+int stringnum_cmp(const char *a, const char *b);
/* utility functions */
int match_token(struct _s_x *table, char *string);
+int match_token_relaxed(struct _s_x *table, char *string);
char const *match_value(struct _s_x *p, int value);
+size_t concat_tokens(char *buf, size_t bufsize, struct _s_x *table,
+ char *delimiter);
+int fill_flags(struct _s_x *flags, char *p, char **e, uint32_t *set,
+ uint32_t *clear);
+void print_flags_buffer(char *buf, size_t sz, struct _s_x *list, uint32_t set);
+struct _ip_fw3_opheader;
int do_cmd(int optname, void *optval, uintptr_t optlen);
-
-uint32_t ipfw_get_tables_max(void);
+int do_set3(int optname, struct _ip_fw3_opheader *op3, uintptr_t optlen);
+int do_get3(int optname, struct _ip_fw3_opheader *op3, size_t *optlen);
struct in6_addr;
void n2mask(struct in6_addr *mask, int n);
@@ -268,12 +311,13 @@ void ipfw_delete(char *av[]);
void ipfw_flush(int force);
void ipfw_zero(int ac, char *av[], int optname);
void ipfw_list(int ac, char *av[], int show_counters);
+void ipfw_internal_handler(int ac, char *av[]);
#ifdef PF
/* altq.c */
void altq_set_enabled(int enabled);
u_int32_t altq_name_to_qid(const char *name);
-void print_altq_cmd(struct _ipfw_insn_altq *altqptr);
+void print_altq_cmd(struct buf_pr *bp, struct _ipfw_insn_altq *altqptr);
#else
#define NO_ALTQ
#endif
@@ -285,10 +329,10 @@ int ipfw_delete_pipe(int pipe_or_queue, int n);
/* ipv6.c */
void print_unreach6_code(uint16_t code);
-void print_ip6(struct _ipfw_insn_ip6 *cmd, char const *s);
-void print_flow6id(struct _ipfw_insn_u32 *cmd);
-void print_icmp6types(struct _ipfw_insn_u32 *cmd);
-void print_ext6hdr(struct _ipfw_insn *cmd );
+void print_ip6(struct buf_pr *bp, struct _ipfw_insn_ip6 *cmd, char const *s);
+void print_flow6id(struct buf_pr *bp, struct _ipfw_insn_u32 *cmd);
+void print_icmp6types(struct buf_pr *bp, struct _ipfw_insn_u32 *cmd);
+void print_ext6hdr(struct buf_pr *bp, struct _ipfw_insn *cmd );
struct _ipfw_insn *add_srcip6(struct _ipfw_insn *cmd, char *av, int cblen);
struct _ipfw_insn *add_dstip6(struct _ipfw_insn *cmd, char *av, int cblen);
@@ -297,3 +341,12 @@ void fill_flow6(struct _ipfw_insn_u32 *cmd, char *av, int cblen);
void fill_unreach6_code(u_short *codep, char *str);
void fill_icmp6types(struct _ipfw_insn_icmp6 *cmd, char *av, int cblen);
int fill_ext6hdr(struct _ipfw_insn *cmd, char *av);
+
+/* tables.c */
+struct _ipfw_obj_ctlv;
+char *table_search_ctlv(struct _ipfw_obj_ctlv *ctlv, uint16_t idx);
+void table_sort_ctlv(struct _ipfw_obj_ctlv *ctlv);
+int table_check_name(char *tablename);
+void ipfw_list_ta(int ac, char *av[]);
+void ipfw_list_values(int ac, char *av[]);
+
diff --git a/sbin/ipfw/ipv6.c b/sbin/ipfw/ipv6.c
index ee9bb623f3ae..36ee675bdf9a 100644
--- a/sbin/ipfw/ipv6.c
+++ b/sbin/ipfw/ipv6.c
@@ -85,21 +85,21 @@ print_unreach6_code(uint16_t code)
* Print the ip address contained in a command.
*/
void
-print_ip6(ipfw_insn_ip6 *cmd, char const *s)
+print_ip6(struct buf_pr *bp, ipfw_insn_ip6 *cmd, char const *s)
{
struct hostent *he = NULL;
int len = F_LEN((ipfw_insn *) cmd) - 1;
struct in6_addr *a = &(cmd->addr6);
char trad[255];
- printf("%s%s ", cmd->o.len & F_NOT ? " not": "", s);
+ bprintf(bp, "%s%s ", cmd->o.len & F_NOT ? " not": "", s);
if (cmd->o.opcode == O_IP6_SRC_ME || cmd->o.opcode == O_IP6_DST_ME) {
- printf("me6");
+ bprintf(bp, "me6");
return;
}
if (cmd->o.opcode == O_IP6) {
- printf(" ip6");
+ bprintf(bp, " ip6");
return;
}
@@ -117,21 +117,21 @@ print_ip6(ipfw_insn_ip6 *cmd, char const *s)
if (mb == 128 && co.do_resolv)
he = gethostbyaddr((char *)a, sizeof(*a), AF_INET6);
if (he != NULL) /* resolved to name */
- printf("%s", he->h_name);
+ bprintf(bp, "%s", he->h_name);
else if (mb == 0) /* any */
- printf("any");
+ bprintf(bp, "any");
else { /* numeric IP followed by some kind of mask */
if (inet_ntop(AF_INET6, a, trad, sizeof( trad ) ) == NULL)
- printf("Error ntop in print_ip6\n");
- printf("%s", trad );
+ bprintf(bp, "Error ntop in print_ip6\n");
+ bprintf(bp, "%s", trad );
if (mb < 0) /* XXX not really legal... */
- printf(":%s",
+ bprintf(bp, ":%s",
inet_ntop(AF_INET6, &a[1], trad, sizeof(trad)));
else if (mb < 128)
- printf("/%d", mb);
+ bprintf(bp, "/%d", mb);
}
if (len > 2)
- printf(",");
+ bprintf(bp, ",");
}
}
@@ -165,32 +165,32 @@ fill_icmp6types(ipfw_insn_icmp6 *cmd, char *av, int cblen)
void
-print_icmp6types(ipfw_insn_u32 *cmd)
+print_icmp6types(struct buf_pr *bp, ipfw_insn_u32 *cmd)
{
int i, j;
char sep= ' ';
- printf(" ip6 icmp6types");
+ bprintf(bp, " ip6 icmp6types");
for (i = 0; i < 7; i++)
for (j=0; j < 32; ++j) {
if ( (cmd->d[i] & (1 << (j))) == 0)
continue;
- printf("%c%d", sep, (i*32 + j));
+ bprintf(bp, "%c%d", sep, (i*32 + j));
sep = ',';
}
}
void
-print_flow6id( ipfw_insn_u32 *cmd)
+print_flow6id(struct buf_pr *bp, ipfw_insn_u32 *cmd)
{
uint16_t i, limit = cmd->o.arg1;
char sep = ',';
- printf(" flow-id ");
+ bprintf(bp, " flow-id ");
for( i=0; i < limit; ++i) {
if (i == limit - 1)
sep = ' ';
- printf("%d%c", cmd->d[i], sep);
+ bprintf(bp, "%d%c", cmd->d[i], sep);
}
}
@@ -265,41 +265,41 @@ fill_ext6hdr( ipfw_insn *cmd, char *av)
}
void
-print_ext6hdr( ipfw_insn *cmd )
+print_ext6hdr(struct buf_pr *bp, ipfw_insn *cmd )
{
char sep = ' ';
- printf(" extension header:");
+ bprintf(bp, " extension header:");
if (cmd->arg1 & EXT_FRAGMENT ) {
- printf("%cfragmentation", sep);
+ bprintf(bp, "%cfragmentation", sep);
sep = ',';
}
if (cmd->arg1 & EXT_HOPOPTS ) {
- printf("%chop options", sep);
+ bprintf(bp, "%chop options", sep);
sep = ',';
}
if (cmd->arg1 & EXT_ROUTING ) {
- printf("%crouting options", sep);
+ bprintf(bp, "%crouting options", sep);
sep = ',';
}
if (cmd->arg1 & EXT_RTHDR0 ) {
- printf("%crthdr0", sep);
+ bprintf(bp, "%crthdr0", sep);
sep = ',';
}
if (cmd->arg1 & EXT_RTHDR2 ) {
- printf("%crthdr2", sep);
+ bprintf(bp, "%crthdr2", sep);
sep = ',';
}
if (cmd->arg1 & EXT_DSTOPTS ) {
- printf("%cdestination options", sep);
+ bprintf(bp, "%cdestination options", sep);
sep = ',';
}
if (cmd->arg1 & EXT_AH ) {
- printf("%cauthentication header", sep);
+ bprintf(bp, "%cauthentication header", sep);
sep = ',';
}
if (cmd->arg1 & EXT_ESP ) {
- printf("%cencapsulated security payload", sep);
+ bprintf(bp, "%cencapsulated security payload", sep);
}
}
diff --git a/sbin/ipfw/main.c b/sbin/ipfw/main.c
index 82a299bbd58d..f25578f55332 100644
--- a/sbin/ipfw/main.c
+++ b/sbin/ipfw/main.c
@@ -436,6 +436,10 @@ ipfw_main(int oldac, char **oldav)
ipfw_list(ac, av, do_acct);
else if (_substrcmp(*av, "show") == 0)
ipfw_list(ac, av, 1 /* show counters */);
+ else if (_substrcmp(*av, "table") == 0)
+ ipfw_table_handler(ac, av);
+ else if (_substrcmp(*av, "internal") == 0)
+ ipfw_internal_handler(ac, av);
else
errx(EX_USAGE, "bad command `%s'", *av);
}
diff --git a/sbin/ipfw/nat.c b/sbin/ipfw/nat.c
index bff28e156c77..3bd0259ad5ad 100644
--- a/sbin/ipfw/nat.c
+++ b/sbin/ipfw/nat.c
@@ -30,14 +30,13 @@
#include <ctype.h>
#include <err.h>
+#include <errno.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sysexits.h>
-#define IPFW_INTERNAL /* Access to protected structures in ip_fw.h. */
-
#include <net/if.h>
#include <net/if_dl.h>
#include <net/route.h> /* def. of struct route */
@@ -46,6 +45,14 @@
#include <arpa/inet.h>
#include <alias.h>
+typedef int (nat_cb_t)(struct nat44_cfg_nat *cfg, void *arg);
+static void nat_show_cfg(struct nat44_cfg_nat *n, void *arg);
+static void nat_show_log(struct nat44_cfg_nat *n, void *arg);
+static int nat_show_data(struct nat44_cfg_nat *cfg, void *arg);
+static int natname_cmp(const void *a, const void *b);
+static int nat_foreach(nat_cb_t *f, void *arg, int sort);
+static int nat_get_cmd(char *name, uint16_t cmd, ipfw_obj_header **ooh);
+
static struct _s_x nat_params[] = {
{ "ip", TOK_IP },
{ "if", TOK_IF },
@@ -71,7 +78,7 @@ static struct _s_x nat_params[] = {
* n->if_name copy of interface name "ifn"
*/
static void
-set_addr_dynamic(const char *ifn, struct cfg_nat *n)
+set_addr_dynamic(const char *ifn, struct nat44_cfg_nat *n)
{
size_t needed;
int mib[6];
@@ -288,15 +295,15 @@ StrToAddrAndPortRange (const char* str, struct in_addr* addr, char* proto,
* and SetupProtoRedirect() from natd.c.
*
* Every setup_* function fills at least one redirect entry
- * (struct cfg_redir) and zero or more server pool entry (struct cfg_spool)
- * in buf.
+ * (struct nat44_cfg_redir) and zero or more server pool entry
+ * (struct nat44_cfg_spool) in buf.
*
* The format of data in buf is:
*
- * cfg_nat cfg_redir cfg_spool ...... cfg_spool
+ * nat44_cfg_nat nat44_cfg_redir nat44_cfg_spool ...... nat44_cfg_spool
*
* ------------------------------------- ------------
- * | | .....X ... | | | | .....
+ * | | .....X ..... | | | | .....
* ------------------------------------- ...... ------------
* ^
* spool_cnt n=0 ...... n=(X-1)
@@ -314,7 +321,7 @@ StrToAddrAndPortRange (const char* str, struct in_addr* addr, char* proto,
static int
estimate_redir_addr(int *ac, char ***av)
{
- size_t space = sizeof(struct cfg_redir);
+ size_t space = sizeof(struct nat44_cfg_redir);
char *sep = **av;
u_int c = 0;
@@ -327,7 +334,7 @@ estimate_redir_addr(int *ac, char ***av)
if (c > 0)
c++;
- space += c * sizeof(struct cfg_spool);
+ space += c * sizeof(struct nat44_cfg_spool);
return (space);
}
@@ -335,31 +342,31 @@ estimate_redir_addr(int *ac, char ***av)
static int
setup_redir_addr(char *buf, int *ac, char ***av)
{
- struct cfg_redir *r;
+ struct nat44_cfg_redir *r;
char *sep;
size_t space;
- r = (struct cfg_redir *)buf;
+ r = (struct nat44_cfg_redir *)buf;
r->mode = REDIR_ADDR;
- /* Skip cfg_redir at beginning of buf. */
- buf = &buf[sizeof(struct cfg_redir)];
- space = sizeof(struct cfg_redir);
+ /* Skip nat44_cfg_redir at beginning of buf. */
+ buf = &buf[sizeof(struct nat44_cfg_redir)];
+ space = sizeof(struct nat44_cfg_redir);
/* Extract local address. */
if (strchr(**av, ',') != NULL) {
- struct cfg_spool *spool;
+ struct nat44_cfg_spool *spool;
/* Setup LSNAT server pool. */
r->laddr.s_addr = INADDR_NONE;
sep = strtok(**av, ",");
while (sep != NULL) {
- spool = (struct cfg_spool *)buf;
- space += sizeof(struct cfg_spool);
+ spool = (struct nat44_cfg_spool *)buf;
+ space += sizeof(struct nat44_cfg_spool);
StrToAddr(sep, &spool->addr);
spool->port = ~0;
r->spool_cnt++;
- /* Point to the next possible cfg_spool. */
- buf = &buf[sizeof(struct cfg_spool)];
+ /* Point to the next possible nat44_cfg_spool. */
+ buf = &buf[sizeof(struct nat44_cfg_spool)];
sep = strtok(NULL, ",");
}
} else
@@ -376,7 +383,7 @@ setup_redir_addr(char *buf, int *ac, char ***av)
static int
estimate_redir_port(int *ac, char ***av)
{
- size_t space = sizeof(struct cfg_redir);
+ size_t space = sizeof(struct nat44_cfg_redir);
char *sep = **av;
u_int c = 0;
@@ -389,7 +396,7 @@ estimate_redir_port(int *ac, char ***av)
if (c > 0)
c++;
- space += c * sizeof(struct cfg_spool);
+ space += c * sizeof(struct nat44_cfg_spool);
return (space);
}
@@ -397,7 +404,7 @@ estimate_redir_port(int *ac, char ***av)
static int
setup_redir_port(char *buf, int *ac, char ***av)
{
- struct cfg_redir *r;
+ struct nat44_cfg_redir *r;
char *sep, *protoName, *lsnat = NULL;
size_t space;
u_short numLocalPorts;
@@ -405,11 +412,11 @@ setup_redir_port(char *buf, int *ac, char ***av)
numLocalPorts = 0;
- r = (struct cfg_redir *)buf;
+ r = (struct nat44_cfg_redir *)buf;
r->mode = REDIR_PORT;
- /* Skip cfg_redir at beginning of buf. */
- buf = &buf[sizeof(struct cfg_redir)];
- space = sizeof(struct cfg_redir);
+ /* Skip nat44_cfg_redir at beginning of buf. */
+ buf = &buf[sizeof(struct nat44_cfg_redir)];
+ space = sizeof(struct nat44_cfg_redir);
/*
* Extract protocol.
@@ -516,12 +523,12 @@ setup_redir_port(char *buf, int *ac, char ***av)
/* Setup LSNAT server pool. */
if (lsnat != NULL) {
- struct cfg_spool *spool;
+ struct nat44_cfg_spool *spool;
sep = strtok(lsnat, ",");
while (sep != NULL) {
- spool = (struct cfg_spool *)buf;
- space += sizeof(struct cfg_spool);
+ spool = (struct nat44_cfg_spool *)buf;
+ space += sizeof(struct nat44_cfg_spool);
/*
* The sctp nat does not allow the port numbers to
* be mapped to new port numbers. Therefore, no ports
@@ -549,8 +556,8 @@ setup_redir_port(char *buf, int *ac, char ***av)
spool->port = GETLOPORT(portRange);
}
r->spool_cnt++;
- /* Point to the next possible cfg_spool. */
- buf = &buf[sizeof(struct cfg_spool)];
+ /* Point to the next possible nat44_cfg_spool. */
+ buf = &buf[sizeof(struct nat44_cfg_spool)];
sep = strtok(NULL, ",");
}
}
@@ -561,15 +568,15 @@ setup_redir_port(char *buf, int *ac, char ***av)
static int
setup_redir_proto(char *buf, int *ac, char ***av)
{
- struct cfg_redir *r;
+ struct nat44_cfg_redir *r;
struct protoent *protoent;
size_t space;
- r = (struct cfg_redir *)buf;
+ r = (struct nat44_cfg_redir *)buf;
r->mode = REDIR_PROTO;
- /* Skip cfg_redir at beginning of buf. */
- buf = &buf[sizeof(struct cfg_redir)];
- space = sizeof(struct cfg_redir);
+ /* Skip nat44_cfg_redir at beginning of buf. */
+ buf = &buf[sizeof(struct nat44_cfg_redir)];
+ space = sizeof(struct nat44_cfg_redir);
/*
* Extract protocol.
@@ -616,18 +623,28 @@ setup_redir_proto(char *buf, int *ac, char ***av)
}
static void
-print_nat_config(unsigned char *buf)
+nat_show_log(struct nat44_cfg_nat *n, void *arg)
+{
+ char *buf;
+
+ buf = (char *)(n + 1);
+ if (buf[0] != '\0')
+ printf("nat %s: %s\n", n->name, buf);
+}
+
+static void
+nat_show_cfg(struct nat44_cfg_nat *n, void *arg)
{
- struct cfg_nat *n;
int i, cnt, flag, off;
- struct cfg_redir *t;
- struct cfg_spool *s;
+ struct nat44_cfg_redir *t;
+ struct nat44_cfg_spool *s;
+ caddr_t buf;
struct protoent *p;
- n = (struct cfg_nat *)buf;
+ buf = (caddr_t)n;
flag = 1;
- off = sizeof(*n);
- printf("ipfw nat %u config", n->id);
+ off = sizeof(*n);
+ printf("ipfw nat %s config", n->name);
if (strlen(n->if_name) != 0)
printf(" if %s", n->if_name);
else if (n->ip.s_addr != 0)
@@ -661,8 +678,8 @@ print_nat_config(unsigned char *buf)
}
/* Print all the redirect's data configuration. */
for (cnt = 0; cnt < n->redir_cnt; cnt++) {
- t = (struct cfg_redir *)&buf[off];
- off += SOF_REDIR;
+ t = (struct nat44_cfg_redir *)&buf[off];
+ off += sizeof(struct nat44_cfg_redir);
switch (t->mode) {
case REDIR_ADDR:
printf(" redirect_addr");
@@ -670,13 +687,13 @@ print_nat_config(unsigned char *buf)
printf(" %s", inet_ntoa(t->laddr));
else
for (i = 0; i < t->spool_cnt; i++) {
- s = (struct cfg_spool *)&buf[off];
+ s = (struct nat44_cfg_spool *)&buf[off];
if (i)
printf(",");
else
printf(" ");
printf("%s", inet_ntoa(s->addr));
- off += SOF_SPOOL;
+ off += sizeof(struct nat44_cfg_spool);
}
printf(" %s", inet_ntoa(t->paddr));
break;
@@ -690,12 +707,12 @@ print_nat_config(unsigned char *buf)
t->pport_cnt - 1);
} else
for (i=0; i < t->spool_cnt; i++) {
- s = (struct cfg_spool *)&buf[off];
+ s = (struct nat44_cfg_spool *)&buf[off];
if (i)
printf(",");
printf("%s:%u", inet_ntoa(s->addr),
s->port);
- off += SOF_SPOOL;
+ off += sizeof(struct nat44_cfg_spool);
}
printf(" ");
@@ -736,7 +753,8 @@ print_nat_config(unsigned char *buf)
void
ipfw_config_nat(int ac, char **av)
{
- struct cfg_nat *n; /* Nat instance configuration. */
+ ipfw_obj_header *oh;
+ struct nat44_cfg_nat *n; /* Nat instance configuration. */
int i, off, tok, ac1;
char *id, *buf, **av1, *end;
size_t len;
@@ -755,7 +773,7 @@ ipfw_config_nat(int ac, char **av)
if (ac == 0)
errx(EX_DATAERR, "missing option");
- len = sizeof(struct cfg_nat);
+ len = sizeof(*oh) + sizeof(*n);
ac1 = ac;
av1 = av;
while (ac1 > 0) {
@@ -804,7 +822,7 @@ ipfw_config_nat(int ac, char **av)
if (ac1 < 2)
errx(EX_DATAERR, "redirect_proto: "
"not enough arguments");
- len += sizeof(struct cfg_redir);
+ len += sizeof(struct nat44_cfg_redir);
av1 += 2;
ac1 -= 2;
/* Skip optional remoteIP/port */
@@ -825,11 +843,14 @@ ipfw_config_nat(int ac, char **av)
if ((buf = malloc(len)) == NULL)
errx(EX_OSERR, "malloc failed");
- /* Offset in buf: save space for n at the beginning. */
- off = sizeof(*n);
+ /* Offset in buf: save space for header at the beginning. */
+ off = sizeof(*oh) + sizeof(*n);
memset(buf, 0, len);
- n = (struct cfg_nat *)buf;
- n->id = i;
+ oh = (ipfw_obj_header *)buf;
+ n = (struct nat44_cfg_nat *)(oh + 1);
+ oh->ntlv.head.length = sizeof(oh->ntlv);
+ snprintf(oh->ntlv.name, sizeof(oh->ntlv.name), "%d", i);
+ snprintf(n->name, sizeof(n->name), "%d", i);
while (ac > 0) {
tok = match_token(nat_params, *av);
@@ -900,9 +921,9 @@ ipfw_config_nat(int ac, char **av)
}
}
- i = do_cmd(IP_FW_NAT_CFG, buf, off);
- if (i)
- err(1, "setsockopt(%s)", "IP_FW_NAT_CFG");
+ i = do_set3(IP_FW_NAT44_XCONFIG, &oh->opheader, len);
+ if (i != 0)
+ err(1, "setsockopt(%s)", "IP_FW_NAT44_XCONFIG");
if (!co.do_quiet) {
/* After every modification, we show the resultant rule. */
@@ -912,23 +933,147 @@ ipfw_config_nat(int ac, char **av)
}
}
+struct nat_list_arg {
+ uint16_t cmd;
+ int is_all;
+};
+
+static int
+nat_show_data(struct nat44_cfg_nat *cfg, void *arg)
+{
+ struct nat_list_arg *nla;
+ ipfw_obj_header *oh;
+
+ nla = (struct nat_list_arg *)arg;
+
+ switch (nla->cmd) {
+ case IP_FW_NAT44_XGETCONFIG:
+ if (nat_get_cmd(cfg->name, nla->cmd, &oh) != 0) {
+ warnx("Error getting nat instance %s info", cfg->name);
+ break;
+ }
+ nat_show_cfg((struct nat44_cfg_nat *)(oh + 1), NULL);
+ free(oh);
+ break;
+ case IP_FW_NAT44_XGETLOG:
+ if (nat_get_cmd(cfg->name, nla->cmd, &oh) == 0) {
+ nat_show_log((struct nat44_cfg_nat *)(oh + 1), NULL);
+ free(oh);
+ break;
+ }
+ /* Handle error */
+ if (nla->is_all != 0 && errno == ENOENT)
+ break;
+ warn("Error getting nat instance %s info", cfg->name);
+ break;
+ }
+
+ return (0);
+}
+
+/*
+ * Compare nat names.
+ * Honor number comparison.
+ */
+static int
+natname_cmp(const void *a, const void *b)
+{
+ struct nat44_cfg_nat *ia, *ib;
+
+ ia = (struct nat44_cfg_nat *)a;
+ ib = (struct nat44_cfg_nat *)b;
+
+ return (stringnum_cmp(ia->name, ib->name));
+}
+
+/*
+ * Retrieves nat list from kernel,
+ * optionally sorts it and calls requested function for each table.
+ * Returns 0 on success.
+ */
+static int
+nat_foreach(nat_cb_t *f, void *arg, int sort)
+{
+ ipfw_obj_lheader *olh;
+ struct nat44_cfg_nat *cfg;
+ size_t sz;
+ int i, error;
+
+ /* Start with reasonable default */
+ sz = sizeof(*olh) + 16 * sizeof(struct nat44_cfg_nat);
+
+ for (;;) {
+ if ((olh = calloc(1, sz)) == NULL)
+ return (ENOMEM);
+
+ olh->size = sz;
+ if (do_get3(IP_FW_NAT44_LIST_NAT, &olh->opheader, &sz) != 0) {
+ free(olh);
+ if (errno == ENOMEM) {
+ sz = olh->size;
+ continue;
+ }
+ return (errno);
+ }
+
+ if (sort != 0)
+ qsort(olh + 1, olh->count, olh->objsize, natname_cmp);
+
+ cfg = (struct nat44_cfg_nat*)(olh + 1);
+ for (i = 0; i < olh->count; i++) {
+ error = f(cfg, arg); /* Ignore errors for now */
+ cfg = (struct nat44_cfg_nat *)((caddr_t)cfg +
+ olh->objsize);
+ }
+
+ free(olh);
+ break;
+ }
+
+ return (0);
+}
+
+static int
+nat_get_cmd(char *name, uint16_t cmd, ipfw_obj_header **ooh)
+{
+ ipfw_obj_header *oh;
+ struct nat44_cfg_nat *cfg;
+ size_t sz;
+
+ /* Start with reasonable default */
+ sz = sizeof(*oh) + sizeof(*cfg) + 128;
+
+ for (;;) {
+ if ((oh = calloc(1, sz)) == NULL)
+ return (ENOMEM);
+ cfg = (struct nat44_cfg_nat *)(oh + 1);
+ oh->ntlv.head.length = sizeof(oh->ntlv);
+ strlcpy(oh->ntlv.name, name, sizeof(oh->ntlv.name));
+ strlcpy(cfg->name, name, sizeof(cfg->name));
+
+ if (do_get3(cmd, &oh->opheader, &sz) != 0) {
+ sz = cfg->size;
+ free(oh);
+ if (errno == ENOMEM)
+ continue;
+ return (errno);
+ }
+
+ *ooh = oh;
+ break;
+ }
+
+ return (0);
+}
void
ipfw_show_nat(int ac, char **av)
{
- struct cfg_nat *n;
- struct cfg_redir *e;
- int cmd, i, nbytes, do_cfg, do_rule, frule, lrule, nalloc, size;
- int nat_cnt, redir_cnt, r;
- uint8_t *data, *p;
- char *endptr;
-
- do_rule = 0;
- nalloc = 1024;
- size = 0;
- data = NULL;
- frule = 0;
- lrule = IPFW_DEFAULT_RULE; /* max ipfw rule number */
+ ipfw_obj_header *oh;
+ char *name;
+ int cmd;
+ struct nat_list_arg nla;
+
ac--;
av++;
@@ -936,55 +1081,35 @@ ipfw_show_nat(int ac, char **av)
return;
/* Parse parameters. */
- for (cmd = IP_FW_NAT_GET_LOG, do_cfg = 0; ac != 0; ac--, av++) {
+ cmd = 0; /* XXX: Change to IP_FW_NAT44_XGETLOG @ MFC */
+ name = NULL;
+ for ( ; ac != 0; ac--, av++) {
if (!strncmp(av[0], "config", strlen(av[0]))) {
- cmd = IP_FW_NAT_GET_CONFIG, do_cfg = 1;
+ cmd = IP_FW_NAT44_XGETCONFIG;
+ continue;
+ }
+ if (strcmp(av[0], "log") == 0) {
+ cmd = IP_FW_NAT44_XGETLOG;
continue;
}
- /* Convert command line rule #. */
- frule = lrule = strtoul(av[0], &endptr, 10);
- if (*endptr == '-')
- lrule = strtoul(endptr+1, &endptr, 10);
- if (lrule == 0)
- err(EX_USAGE, "invalid rule number: %s", av[0]);
- do_rule = 1;
+ if (name != NULL)
+ err(EX_USAGE,"only one instance name may be specified");
+ name = av[0];
}
- nbytes = nalloc;
- while (nbytes >= nalloc) {
- nalloc = nalloc * 2;
- nbytes = nalloc;
- data = safe_realloc(data, nbytes);
- if (do_cmd(cmd, data, (uintptr_t)&nbytes) < 0)
- err(EX_OSERR, "getsockopt(IP_FW_GET_%s)",
- (cmd == IP_FW_NAT_GET_LOG) ? "LOG" : "CONFIG");
- }
- if (nbytes == 0)
- exit(0);
- if (do_cfg) {
- nat_cnt = *((int *)data);
- for (i = sizeof(nat_cnt); nat_cnt; nat_cnt--) {
- n = (struct cfg_nat *)&data[i];
- if (frule <= n->id && lrule >= n->id)
- print_nat_config(&data[i]);
- i += sizeof(struct cfg_nat);
- for (redir_cnt = 0; redir_cnt < n->redir_cnt; redir_cnt++) {
- e = (struct cfg_redir *)&data[i];
- i += sizeof(struct cfg_redir) + e->spool_cnt *
- sizeof(struct cfg_spool);
- }
- }
+ if (cmd == 0)
+ errx(EX_USAGE, "Please specify action. Available: config,log");
+
+ if (name == NULL) {
+ memset(&nla, 0, sizeof(nla));
+ nla.cmd = cmd;
+ nla.is_all = 1;
+ nat_foreach(nat_show_data, &nla, 1);
} else {
- for (i = 0; 1; i += LIBALIAS_BUF_SIZE + sizeof(int)) {
- p = &data[i];
- if (p == data + nbytes)
- break;
- bcopy(p, &r, sizeof(int));
- if (do_rule) {
- if (!(frule <= r && lrule >= r))
- continue;
- }
- printf("nat %u: %s\n", r, p+sizeof(int));
- }
+ if (nat_get_cmd(name, cmd, &oh) != 0)
+ err(EX_OSERR, "Error getting nat %s instance info", name);
+ nat_show_cfg((struct nat44_cfg_nat *)(oh + 1), NULL);
+ free(oh);
}
}
+
diff --git a/sbin/ipfw/tables.c b/sbin/ipfw/tables.c
new file mode 100644
index 000000000000..4829fecdc417
--- /dev/null
+++ b/sbin/ipfw/tables.c
@@ -0,0 +1,2012 @@
+/*
+ * Copyright (c) 2014 Yandex LLC
+ * Copyright (c) 2014 Alexander V. Chernikov
+ *
+ * Redistribution and use in source forms, with and without modification,
+ * are permitted provided that this entire comment appears intact.
+ *
+ * Redistribution in binary form may occur without any restrictions.
+ * Obviously, it would be nice if you gave credit where credit is due
+ * but requiring it would be too onerous.
+ *
+ * This software is provided ``AS IS'' without any warranties of any kind.
+ *
+ * in-kernel ipfw tables support.
+ *
+ * $FreeBSD$
+ */
+
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netinet/ip_fw.h>
+#include <arpa/inet.h>
+
+#include "ipfw2.h"
+
+static void table_modify_record(ipfw_obj_header *oh, int ac, char *av[],
+ int add, int quiet, int update, int atomic);
+static int table_flush(ipfw_obj_header *oh);
+static int table_destroy(ipfw_obj_header *oh);
+static int table_do_create(ipfw_obj_header *oh, ipfw_xtable_info *i);
+static int table_do_modify(ipfw_obj_header *oh, ipfw_xtable_info *i);
+static int table_do_swap(ipfw_obj_header *oh, char *second);
+static void table_create(ipfw_obj_header *oh, int ac, char *av[]);
+static void table_modify(ipfw_obj_header *oh, int ac, char *av[]);
+static void table_lookup(ipfw_obj_header *oh, int ac, char *av[]);
+static void table_lock(ipfw_obj_header *oh, int lock);
+static int table_swap(ipfw_obj_header *oh, char *second);
+static int table_get_info(ipfw_obj_header *oh, ipfw_xtable_info *i);
+static int table_show_info(ipfw_xtable_info *i, void *arg);
+static void table_fill_ntlv(ipfw_obj_ntlv *ntlv, char *name, uint32_t set,
+ uint16_t uidx);
+
+static int table_flush_one(ipfw_xtable_info *i, void *arg);
+static int table_show_one(ipfw_xtable_info *i, void *arg);
+static int table_do_get_list(ipfw_xtable_info *i, ipfw_obj_header **poh);
+static void table_show_list(ipfw_obj_header *oh, int need_header);
+static void table_show_entry(ipfw_xtable_info *i, ipfw_obj_tentry *tent);
+
+static void tentry_fill_key(ipfw_obj_header *oh, ipfw_obj_tentry *tent,
+ char *key, int add, uint8_t *ptype, uint32_t *pvmask, ipfw_xtable_info *xi);
+static void tentry_fill_value(ipfw_obj_header *oh, ipfw_obj_tentry *tent,
+ char *arg, uint8_t type, uint32_t vmask);
+static void table_show_value(char *buf, size_t bufsize, ipfw_table_value *v,
+ uint32_t vmask, int print_ip);
+
+typedef int (table_cb_t)(ipfw_xtable_info *i, void *arg);
+static int tables_foreach(table_cb_t *f, void *arg, int sort);
+
+#ifndef s6_addr32
+#define s6_addr32 __u6_addr.__u6_addr32
+#endif
+
+static struct _s_x tabletypes[] = {
+ { "addr", IPFW_TABLE_ADDR },
+ { "iface", IPFW_TABLE_INTERFACE },
+ { "number", IPFW_TABLE_NUMBER },
+ { "flow", IPFW_TABLE_FLOW },
+ { NULL, 0 }
+};
+
+static struct _s_x tablevaltypes[] = {
+ { "skipto", IPFW_VTYPE_SKIPTO },
+ { "pipe", IPFW_VTYPE_PIPE },
+ { "fib", IPFW_VTYPE_FIB },
+ { "nat", IPFW_VTYPE_NAT },
+ { "dscp", IPFW_VTYPE_DSCP },
+ { "tag", IPFW_VTYPE_TAG },
+ { "divert", IPFW_VTYPE_DIVERT },
+ { "netgraph", IPFW_VTYPE_NETGRAPH },
+ { "limit", IPFW_VTYPE_LIMIT },
+ { "ipv4", IPFW_VTYPE_NH4 },
+ { "ipv6", IPFW_VTYPE_NH6 },
+ { NULL, 0 }
+};
+
+static struct _s_x tablecmds[] = {
+ { "add", TOK_ADD },
+ { "delete", TOK_DEL },
+ { "create", TOK_CREATE },
+ { "destroy", TOK_DESTROY },
+ { "flush", TOK_FLUSH },
+ { "modify", TOK_MODIFY },
+ { "swap", TOK_SWAP },
+ { "info", TOK_INFO },
+ { "detail", TOK_DETAIL },
+ { "list", TOK_LIST },
+ { "lookup", TOK_LOOKUP },
+ { "atomic", TOK_ATOMIC },
+ { "lock", TOK_LOCK },
+ { "unlock", TOK_UNLOCK },
+ { NULL, 0 }
+};
+
+static int
+lookup_host (char *host, struct in_addr *ipaddr)
+{
+ struct hostent *he;
+
+ if (!inet_aton(host, ipaddr)) {
+ if ((he = gethostbyname(host)) == NULL)
+ return(-1);
+ *ipaddr = *(struct in_addr *)he->h_addr_list[0];
+ }
+ return(0);
+}
+
+static int
+get_token(struct _s_x *table, char *string, char *errbase)
+{
+ int tcmd;
+
+ if ((tcmd = match_token_relaxed(table, string)) < 0)
+ errx(EX_USAGE, "%s %s %s",
+ (tcmd == 0) ? "invalid" : "ambiguous", errbase, string);
+
+ return (tcmd);
+}
+
+/*
+ * This one handles all table-related commands
+ * ipfw table NAME create ...
+ * ipfw table NAME modify ...
+ * ipfw table NAME destroy
+ * ipfw table NAME swap NAME
+ * ipfw table NAME lock
+ * ipfw table NAME unlock
+ * ipfw table NAME add addr[/masklen] [value]
+ * ipfw table NAME add [addr[/masklen] value] [addr[/masklen] value] ..
+ * ipfw table NAME delete addr[/masklen] [addr[/masklen]] ..
+ * ipfw table NAME lookup addr
+ * ipfw table {NAME | all} flush
+ * ipfw table {NAME | all} list
+ * ipfw table {NAME | all} info
+ * ipfw table {NAME | all} detail
+ */
+void
+ipfw_table_handler(int ac, char *av[])
+{
+ int do_add, is_all;
+ int atomic, error, tcmd;
+ ipfw_xtable_info i;
+ ipfw_obj_header oh;
+ char *tablename;
+ uint32_t set;
+ void *arg;
+
+ memset(&oh, 0, sizeof(oh));
+ is_all = 0;
+ if (co.use_set != 0)
+ set = co.use_set - 1;
+ else
+ set = 0;
+
+ ac--; av++;
+ NEED1("table needs name");
+ tablename = *av;
+
+ if (table_check_name(tablename) == 0) {
+ table_fill_ntlv(&oh.ntlv, *av, set, 1);
+ oh.idx = 1;
+ } else {
+ if (strcmp(tablename, "all") == 0)
+ is_all = 1;
+ else
+ errx(EX_USAGE, "table name %s is invalid", tablename);
+ }
+ ac--; av++;
+ NEED1("table needs command");
+
+ tcmd = get_token(tablecmds, *av, "table command");
+ /* Check if atomic operation was requested */
+ atomic = 0;
+ if (tcmd == TOK_ATOMIC) {
+ ac--; av++;
+ NEED1("atomic needs command");
+ tcmd = get_token(tablecmds, *av, "table command");
+ switch (tcmd) {
+ case TOK_ADD:
+ break;
+ default:
+ errx(EX_USAGE, "atomic is not compatible with %s", *av);
+ }
+ atomic = 1;
+ }
+
+ switch (tcmd) {
+ case TOK_LIST:
+ case TOK_INFO:
+ case TOK_DETAIL:
+ case TOK_FLUSH:
+ break;
+ default:
+ if (is_all != 0)
+ errx(EX_USAGE, "table name required");
+ }
+
+ switch (tcmd) {
+ case TOK_ADD:
+ case TOK_DEL:
+ do_add = **av == 'a';
+ ac--; av++;
+ table_modify_record(&oh, ac, av, do_add, co.do_quiet,
+ co.do_quiet, atomic);
+ break;
+ case TOK_CREATE:
+ ac--; av++;
+ table_create(&oh, ac, av);
+ break;
+ case TOK_MODIFY:
+ ac--; av++;
+ table_modify(&oh, ac, av);
+ break;
+ case TOK_DESTROY:
+ if (table_destroy(&oh) != 0)
+ err(EX_OSERR, "failed to destroy table %s", tablename);
+ break;
+ case TOK_FLUSH:
+ if (is_all == 0) {
+ if ((error = table_flush(&oh)) != 0)
+ err(EX_OSERR, "failed to flush table %s info",
+ tablename);
+ } else {
+ error = tables_foreach(table_flush_one, &oh, 1);
+ if (error != 0)
+ err(EX_OSERR, "failed to flush tables list");
+ }
+ break;
+ case TOK_SWAP:
+ ac--; av++;
+ NEED1("second table name required");
+ table_swap(&oh, *av);
+ break;
+ case TOK_LOCK:
+ case TOK_UNLOCK:
+ table_lock(&oh, (tcmd == TOK_LOCK));
+ break;
+ case TOK_DETAIL:
+ case TOK_INFO:
+ arg = (tcmd == TOK_DETAIL) ? (void *)1 : NULL;
+ if (is_all == 0) {
+ if ((error = table_get_info(&oh, &i)) != 0)
+ err(EX_OSERR, "failed to request table info");
+ table_show_info(&i, arg);
+ } else {
+ error = tables_foreach(table_show_info, arg, 1);
+ if (error != 0)
+ err(EX_OSERR, "failed to request tables list");
+ }
+ break;
+ case TOK_LIST:
+ if (is_all == 0) {
+ ipfw_xtable_info i;
+ if ((error = table_get_info(&oh, &i)) != 0)
+ err(EX_OSERR, "failed to request table info");
+ table_show_one(&i, NULL);
+ } else {
+ error = tables_foreach(table_show_one, NULL, 1);
+ if (error != 0)
+ err(EX_OSERR, "failed to request tables list");
+ }
+ break;
+ case TOK_LOOKUP:
+ ac--; av++;
+ table_lookup(&oh, ac, av);
+ break;
+ }
+}
+
+static void
+table_fill_ntlv(ipfw_obj_ntlv *ntlv, char *name, uint32_t set, uint16_t uidx)
+{
+
+ ntlv->head.type = IPFW_TLV_TBL_NAME;
+ ntlv->head.length = sizeof(ipfw_obj_ntlv);
+ ntlv->idx = uidx;
+ ntlv->set = set;
+ strlcpy(ntlv->name, name, sizeof(ntlv->name));
+}
+
+static void
+table_fill_objheader(ipfw_obj_header *oh, ipfw_xtable_info *i)
+{
+
+ oh->idx = 1;
+ table_fill_ntlv(&oh->ntlv, i->tablename, i->set, 1);
+}
+
+static struct _s_x tablenewcmds[] = {
+ { "type", TOK_TYPE },
+ { "valtype", TOK_VALTYPE },
+ { "algo", TOK_ALGO },
+ { "limit", TOK_LIMIT },
+ { "locked", TOK_LOCK },
+ { NULL, 0 }
+};
+
+static struct _s_x flowtypecmds[] = {
+ { "src-ip", IPFW_TFFLAG_SRCIP },
+ { "proto", IPFW_TFFLAG_PROTO },
+ { "src-port", IPFW_TFFLAG_SRCPORT },
+ { "dst-ip", IPFW_TFFLAG_DSTIP },
+ { "dst-port", IPFW_TFFLAG_DSTPORT },
+ { NULL, 0 }
+};
+
+int
+table_parse_type(uint8_t ttype, char *p, uint8_t *tflags)
+{
+ uint32_t fset, fclear;
+ char *e;
+
+ /* Parse type options */
+ switch(ttype) {
+ case IPFW_TABLE_FLOW:
+ fset = fclear = 0;
+ if (fill_flags(flowtypecmds, p, &e, &fset, &fclear) != 0)
+ errx(EX_USAGE,
+ "unable to parse flow option %s", e);
+ *tflags = fset;
+ break;
+ default:
+ return (EX_USAGE);
+ }
+
+ return (0);
+}
+
+void
+table_print_type(char *tbuf, size_t size, uint8_t type, uint8_t tflags)
+{
+ const char *tname;
+ int l;
+
+ if ((tname = match_value(tabletypes, type)) == NULL)
+ tname = "unknown";
+
+ l = snprintf(tbuf, size, "%s", tname);
+ tbuf += l;
+ size -= l;
+
+ switch(type) {
+ case IPFW_TABLE_FLOW:
+ if (tflags != 0) {
+ *tbuf++ = ':';
+ l--;
+ print_flags_buffer(tbuf, size, flowtypecmds, tflags);
+ }
+ break;
+ }
+}
+
+/*
+ * Creates new table
+ *
+ * ipfw table NAME create [ type { addr | iface | number | flow } ]
+ * [ algo algoname ]
+ */
+static void
+table_create(ipfw_obj_header *oh, int ac, char *av[])
+{
+ ipfw_xtable_info xi;
+ int error, tcmd, val;
+ uint32_t fset, fclear;
+ size_t sz;
+ char *e, *p;
+ char tbuf[128];
+
+ sz = sizeof(tbuf);
+ memset(&xi, 0, sizeof(xi));
+
+ while (ac > 0) {
+ tcmd = get_token(tablenewcmds, *av, "option");
+ ac--; av++;
+
+ switch (tcmd) {
+ case TOK_LIMIT:
+ NEED1("limit value required");
+ xi.limit = strtol(*av, NULL, 10);
+ ac--; av++;
+ break;
+ case TOK_TYPE:
+ NEED1("table type required");
+ /* Type may have suboptions after ':' */
+ if ((p = strchr(*av, ':')) != NULL)
+ *p++ = '\0';
+ val = match_token(tabletypes, *av);
+ if (val == -1) {
+ concat_tokens(tbuf, sizeof(tbuf), tabletypes,
+ ", ");
+ errx(EX_USAGE,
+ "Unknown tabletype: %s. Supported: %s",
+ *av, tbuf);
+ }
+ xi.type = val;
+ if (p != NULL) {
+ error = table_parse_type(val, p, &xi.tflags);
+ if (error != 0)
+ errx(EX_USAGE,
+ "Unsupported suboptions: %s", p);
+ }
+ ac--; av++;
+ break;
+ case TOK_VALTYPE:
+ NEED1("table value type required");
+ fset = fclear = 0;
+ val = fill_flags(tablevaltypes, *av, &e, &fset, &fclear);
+ if (val != -1) {
+ xi.vmask = fset;
+ ac--; av++;
+ break;
+ }
+ concat_tokens(tbuf, sizeof(tbuf), tablevaltypes, ", ");
+ errx(EX_USAGE, "Unknown value type: %s. Supported: %s",
+ e, tbuf);
+ break;
+ case TOK_ALGO:
+ NEED1("table algorithm name required");
+ if (strlen(*av) > sizeof(xi.algoname))
+ errx(EX_USAGE, "algorithm name too long");
+ strlcpy(xi.algoname, *av, sizeof(xi.algoname));
+ ac--; av++;
+ break;
+ case TOK_LOCK:
+ xi.flags |= IPFW_TGFLAGS_LOCKED;
+ break;
+ }
+ }
+
+ /* Set some defaults to preserve compability */
+ if (xi.algoname[0] == '\0' && xi.type == 0)
+ xi.type = IPFW_TABLE_ADDR;
+ if (xi.vmask == 0)
+ xi.vmask = IPFW_VTYPE_LEGACY;
+
+ if ((error = table_do_create(oh, &xi)) != 0)
+ err(EX_OSERR, "Table creation failed");
+}
+
+/*
+ * Creates new table
+ *
+ * Request: [ ipfw_obj_header ipfw_xtable_info ]
+ *
+ * Returns 0 on success.
+ */
+static int
+table_do_create(ipfw_obj_header *oh, ipfw_xtable_info *i)
+{
+ char tbuf[sizeof(ipfw_obj_header) + sizeof(ipfw_xtable_info)];
+ int error;
+
+ memcpy(tbuf, oh, sizeof(*oh));
+ memcpy(tbuf + sizeof(*oh), i, sizeof(*i));
+ oh = (ipfw_obj_header *)tbuf;
+
+ error = do_set3(IP_FW_TABLE_XCREATE, &oh->opheader, sizeof(tbuf));
+
+ return (error);
+}
+
+/*
+ * Modifies existing table
+ *
+ * ipfw table NAME modify [ limit number ]
+ */
+static void
+table_modify(ipfw_obj_header *oh, int ac, char *av[])
+{
+ ipfw_xtable_info xi;
+ int tcmd;
+ size_t sz;
+ char tbuf[128];
+
+ sz = sizeof(tbuf);
+ memset(&xi, 0, sizeof(xi));
+
+ while (ac > 0) {
+ tcmd = get_token(tablenewcmds, *av, "option");
+ ac--; av++;
+
+ switch (tcmd) {
+ case TOK_LIMIT:
+ NEED1("limit value required");
+ xi.limit = strtol(*av, NULL, 10);
+ xi.mflags |= IPFW_TMFLAGS_LIMIT;
+ ac--; av++;
+ break;
+ default:
+ errx(EX_USAGE, "cmd is not supported for modificatiob");
+ }
+ }
+
+ if (table_do_modify(oh, &xi) != 0)
+ err(EX_OSERR, "Table modification failed");
+}
+
+/*
+ * Modifies existing table.
+ *
+ * Request: [ ipfw_obj_header ipfw_xtable_info ]
+ *
+ * Returns 0 on success.
+ */
+static int
+table_do_modify(ipfw_obj_header *oh, ipfw_xtable_info *i)
+{
+ char tbuf[sizeof(ipfw_obj_header) + sizeof(ipfw_xtable_info)];
+ int error;
+
+ memcpy(tbuf, oh, sizeof(*oh));
+ memcpy(tbuf + sizeof(*oh), i, sizeof(*i));
+ oh = (ipfw_obj_header *)tbuf;
+
+ error = do_set3(IP_FW_TABLE_XMODIFY, &oh->opheader, sizeof(tbuf));
+
+ return (error);
+}
+
+/*
+ * Locks or unlocks given table
+ */
+static void
+table_lock(ipfw_obj_header *oh, int lock)
+{
+ ipfw_xtable_info xi;
+
+ memset(&xi, 0, sizeof(xi));
+
+ xi.mflags |= IPFW_TMFLAGS_LOCK;
+ xi.flags |= (lock != 0) ? IPFW_TGFLAGS_LOCKED : 0;
+
+ if (table_do_modify(oh, &xi) != 0)
+ err(EX_OSERR, "Table %s failed", lock != 0 ? "lock" : "unlock");
+}
+
+/*
+ * Destroys given table specified by @oh->ntlv.
+ * Returns 0 on success.
+ */
+static int
+table_destroy(ipfw_obj_header *oh)
+{
+
+ if (do_set3(IP_FW_TABLE_XDESTROY, &oh->opheader, sizeof(*oh)) != 0)
+ return (-1);
+
+ return (0);
+}
+
+/*
+ * Flushes given table specified by @oh->ntlv.
+ * Returns 0 on success.
+ */
+static int
+table_flush(ipfw_obj_header *oh)
+{
+
+ if (do_set3(IP_FW_TABLE_XFLUSH, &oh->opheader, sizeof(*oh)) != 0)
+ return (-1);
+
+ return (0);
+}
+
+static int
+table_do_swap(ipfw_obj_header *oh, char *second)
+{
+ char tbuf[sizeof(ipfw_obj_header) + sizeof(ipfw_obj_ntlv)];
+ int error;
+
+ memset(tbuf, 0, sizeof(tbuf));
+ memcpy(tbuf, oh, sizeof(*oh));
+ oh = (ipfw_obj_header *)tbuf;
+ table_fill_ntlv((ipfw_obj_ntlv *)(oh + 1), second, oh->ntlv.set, 1);
+
+ error = do_set3(IP_FW_TABLE_XSWAP, &oh->opheader, sizeof(tbuf));
+
+ return (error);
+}
+
+/*
+ * Swaps given table with @second one.
+ */
+static int
+table_swap(ipfw_obj_header *oh, char *second)
+{
+ int error;
+
+ if (table_check_name(second) != 0)
+ errx(EX_USAGE, "table name %s is invalid", second);
+
+ error = table_do_swap(oh, second);
+
+ switch (error) {
+ case EINVAL:
+ errx(EX_USAGE, "Unable to swap table: check types");
+ case EFBIG:
+ errx(EX_USAGE, "Unable to swap table: check limits");
+ }
+
+ return (0);
+}
+
+
+/*
+ * Retrieves table in given table specified by @oh->ntlv.
+ * it inside @i.
+ * Returns 0 on success.
+ */
+static int
+table_get_info(ipfw_obj_header *oh, ipfw_xtable_info *i)
+{
+ char tbuf[sizeof(ipfw_obj_header) + sizeof(ipfw_xtable_info)];
+ size_t sz;
+
+ sz = sizeof(tbuf);
+ memset(tbuf, 0, sizeof(tbuf));
+ memcpy(tbuf, oh, sizeof(*oh));
+ oh = (ipfw_obj_header *)tbuf;
+
+ if (do_get3(IP_FW_TABLE_XINFO, &oh->opheader, &sz) != 0)
+ return (errno);
+
+ if (sz < sizeof(tbuf))
+ return (EINVAL);
+
+ *i = *(ipfw_xtable_info *)(oh + 1);
+
+ return (0);
+}
+
+static struct _s_x tablealgoclass[] = {
+ { "hash", IPFW_TACLASS_HASH },
+ { "array", IPFW_TACLASS_ARRAY },
+ { "radix", IPFW_TACLASS_RADIX },
+ { NULL, 0 }
+};
+
+struct ta_cldata {
+ uint8_t taclass;
+ uint8_t spare4;
+ uint16_t itemsize;
+ uint16_t itemsize6;
+ uint32_t size;
+ uint32_t count;
+};
+
+/*
+ * Print global/per-AF table @i algorithm info.
+ */
+static void
+table_show_tainfo(ipfw_xtable_info *i, struct ta_cldata *d,
+ const char *af, const char *taclass)
+{
+
+ switch (d->taclass) {
+ case IPFW_TACLASS_HASH:
+ case IPFW_TACLASS_ARRAY:
+ printf(" %salgorithm %s info\n", af, taclass);
+ if (d->itemsize == d->itemsize6)
+ printf(" size: %u items: %u itemsize: %u\n",
+ d->size, d->count, d->itemsize);
+ else
+ printf(" size: %u items: %u "
+ "itemsize4: %u itemsize6: %u\n",
+ d->size, d->count,
+ d->itemsize, d->itemsize6);
+ break;
+ case IPFW_TACLASS_RADIX:
+ printf(" %salgorithm %s info\n", af, taclass);
+ if (d->itemsize == d->itemsize6)
+ printf(" items: %u itemsize: %u\n",
+ d->count, d->itemsize);
+ else
+ printf(" items: %u "
+ "itemsize4: %u itemsize6: %u\n",
+ d->count, d->itemsize, d->itemsize6);
+ break;
+ default:
+ printf(" algo class: %s\n", taclass);
+ }
+}
+
+static void
+table_print_valheader(char *buf, size_t bufsize, uint32_t vmask)
+{
+
+ if (vmask == IPFW_VTYPE_LEGACY) {
+ snprintf(buf, bufsize, "legacy");
+ return;
+ }
+
+ print_flags_buffer(buf, bufsize, tablevaltypes, vmask);
+}
+
+/*
+ * Prints table info struct @i in human-readable form.
+ */
+static int
+table_show_info(ipfw_xtable_info *i, void *arg)
+{
+ const char *vtype;
+ ipfw_ta_tinfo *tainfo;
+ int afdata, afitem;
+ struct ta_cldata d;
+ char ttype[64], tvtype[64];
+
+ table_print_type(ttype, sizeof(ttype), i->type, i->tflags);
+ table_print_valheader(tvtype, sizeof(tvtype), i->vmask);
+
+ printf("--- table(%s), set(%u) ---\n", i->tablename, i->set);
+ if ((i->flags & IPFW_TGFLAGS_LOCKED) != 0)
+ printf(" kindex: %d, type: %s, locked\n", i->kidx, ttype);
+ else
+ printf(" kindex: %d, type: %s\n", i->kidx, ttype);
+ printf(" references: %u, valtype: %s\n", i->refcnt, tvtype);
+ printf(" algorithm: %s\n", i->algoname);
+ printf(" items: %u, size: %u\n", i->count, i->size);
+ if (i->limit > 0)
+ printf(" limit: %u\n", i->limit);
+
+ /* Print algo-specific info if requested & set */
+ if (arg == NULL)
+ return (0);
+
+ if ((i->ta_info.flags & IPFW_TATFLAGS_DATA) == 0)
+ return (0);
+ tainfo = &i->ta_info;
+
+ afdata = 0;
+ afitem = 0;
+ if (tainfo->flags & IPFW_TATFLAGS_AFDATA)
+ afdata = 1;
+ if (tainfo->flags & IPFW_TATFLAGS_AFITEM)
+ afitem = 1;
+
+ memset(&d, 0, sizeof(d));
+ d.taclass = tainfo->taclass4;
+ d.size = tainfo->size4;
+ d.count = tainfo->count4;
+ d.itemsize = tainfo->itemsize4;
+ if (afdata == 0 && afitem != 0)
+ d.itemsize6 = tainfo->itemsize6;
+ else
+ d.itemsize6 = d.itemsize;
+ if ((vtype = match_value(tablealgoclass, d.taclass)) == NULL)
+ vtype = "unknown";
+
+ if (afdata == 0) {
+ table_show_tainfo(i, &d, "", vtype);
+ } else {
+ table_show_tainfo(i, &d, "IPv4 ", vtype);
+ memset(&d, 0, sizeof(d));
+ d.taclass = tainfo->taclass6;
+ if ((vtype = match_value(tablealgoclass, d.taclass)) == NULL)
+ vtype = "unknown";
+ d.size = tainfo->size6;
+ d.count = tainfo->count6;
+ d.itemsize = tainfo->itemsize6;
+ d.itemsize6 = d.itemsize;
+ table_show_tainfo(i, &d, "IPv6 ", vtype);
+ }
+
+ return (0);
+}
+
+
+/*
+ * Function wrappers which can be used either
+ * as is or as foreach function parameter.
+ */
+
+static int
+table_show_one(ipfw_xtable_info *i, void *arg)
+{
+ ipfw_obj_header *oh;
+ int error;
+
+ if ((error = table_do_get_list(i, &oh)) != 0) {
+ err(EX_OSERR, "Error requesting table %s list", i->tablename);
+ return (error);
+ }
+
+ table_show_list(oh, 1);
+
+ free(oh);
+ return (0);
+}
+
+static int
+table_flush_one(ipfw_xtable_info *i, void *arg)
+{
+ ipfw_obj_header *oh;
+
+ oh = (ipfw_obj_header *)arg;
+
+ table_fill_ntlv(&oh->ntlv, i->tablename, i->set, 1);
+
+ return (table_flush(oh));
+}
+
+static int
+table_do_modify_record(int cmd, ipfw_obj_header *oh,
+ ipfw_obj_tentry *tent, int count, int atomic)
+{
+ ipfw_obj_ctlv *ctlv;
+ ipfw_obj_tentry *tent_base;
+ caddr_t pbuf;
+ char xbuf[sizeof(*oh) + sizeof(ipfw_obj_ctlv) + sizeof(*tent)];
+ int error, i;
+ size_t sz;
+
+ sz = sizeof(*ctlv) + sizeof(*tent) * count;
+ if (count == 1) {
+ memset(xbuf, 0, sizeof(xbuf));
+ pbuf = xbuf;
+ } else {
+ if ((pbuf = calloc(1, sizeof(*oh) + sz)) == NULL)
+ return (ENOMEM);
+ }
+
+ memcpy(pbuf, oh, sizeof(*oh));
+ oh = (ipfw_obj_header *)pbuf;
+ oh->opheader.version = 1;
+
+ ctlv = (ipfw_obj_ctlv *)(oh + 1);
+ ctlv->count = count;
+ ctlv->head.length = sz;
+ if (atomic != 0)
+ ctlv->flags |= IPFW_CTF_ATOMIC;
+
+ tent_base = tent;
+ memcpy(ctlv + 1, tent, sizeof(*tent) * count);
+ tent = (ipfw_obj_tentry *)(ctlv + 1);
+ for (i = 0; i < count; i++, tent++) {
+ tent->head.length = sizeof(ipfw_obj_tentry);
+ tent->idx = oh->idx;
+ }
+
+ sz += sizeof(*oh);
+ error = do_get3(cmd, &oh->opheader, &sz);
+ tent = (ipfw_obj_tentry *)(ctlv + 1);
+ /* Copy result back to provided buffer */
+ memcpy(tent_base, ctlv + 1, sizeof(*tent) * count);
+
+ if (pbuf != xbuf)
+ free(pbuf);
+
+ return (error);
+}
+
+static void
+table_modify_record(ipfw_obj_header *oh, int ac, char *av[], int add,
+ int quiet, int update, int atomic)
+{
+ ipfw_obj_tentry *ptent, tent, *tent_buf;
+ ipfw_xtable_info xi;
+ uint8_t type;
+ uint32_t vmask;
+ int cmd, count, error, i, ignored;
+ char *texterr, *etxt, *px;
+
+ if (ac == 0)
+ errx(EX_USAGE, "address required");
+
+ if (add != 0) {
+ cmd = IP_FW_TABLE_XADD;
+ texterr = "Adding record failed";
+ } else {
+ cmd = IP_FW_TABLE_XDEL;
+ texterr = "Deleting record failed";
+ }
+
+ /*
+ * Calculate number of entries:
+ * Assume [key val] x N for add
+ * and
+ * key x N for delete
+ */
+ count = (add != 0) ? ac / 2 + 1 : ac;
+
+ if (count <= 1) {
+ /* Adding single entry with/without value */
+ memset(&tent, 0, sizeof(tent));
+ tent_buf = &tent;
+ } else {
+
+ if ((tent_buf = calloc(count, sizeof(tent))) == NULL)
+ errx(EX_OSERR,
+ "Unable to allocate memory for all entries");
+ }
+ ptent = tent_buf;
+
+ memset(&xi, 0, sizeof(xi));
+ count = 0;
+ while (ac > 0) {
+ tentry_fill_key(oh, ptent, *av, add, &type, &vmask, &xi);
+
+ /*
+ * compability layer: auto-create table if not exists
+ */
+ if (xi.tablename[0] == '\0') {
+ xi.type = type;
+ xi.vmask = vmask;
+ strlcpy(xi.tablename, oh->ntlv.name,
+ sizeof(xi.tablename));
+ fprintf(stderr, "DEPRECATED: inserting data info "
+ "non-existent table %s. (auto-created)\n",
+ xi.tablename);
+ table_do_create(oh, &xi);
+ }
+
+ oh->ntlv.type = type;
+ ac--; av++;
+
+ if (add != 0 && ac > 0) {
+ tentry_fill_value(oh, ptent, *av, type, vmask);
+ ac--; av++;
+ }
+
+ if (update != 0)
+ ptent->head.flags |= IPFW_TF_UPDATE;
+
+ count++;
+ ptent++;
+ }
+
+ error = table_do_modify_record(cmd, oh, tent_buf, count, atomic);
+
+ quiet = 0;
+
+ /*
+ * Compatibility stuff: do not yell on duplicate keys or
+ * failed deletions.
+ */
+ if (error == 0 || (error == EEXIST && add != 0) ||
+ (error == ENOENT && add == 0)) {
+ if (quiet != 0) {
+ if (tent_buf != &tent)
+ free(tent_buf);
+ return;
+ }
+ }
+
+ /* Report results back */
+ ptent = tent_buf;
+ for (i = 0; i < count; ptent++, i++) {
+ ignored = 0;
+ switch (ptent->result) {
+ case IPFW_TR_ADDED:
+ px = "added";
+ break;
+ case IPFW_TR_DELETED:
+ px = "deleted";
+ break;
+ case IPFW_TR_UPDATED:
+ px = "updated";
+ break;
+ case IPFW_TR_LIMIT:
+ px = "limit";
+ ignored = 1;
+ break;
+ case IPFW_TR_ERROR:
+ px = "error";
+ ignored = 1;
+ break;
+ case IPFW_TR_NOTFOUND:
+ px = "notfound";
+ ignored = 1;
+ break;
+ case IPFW_TR_EXISTS:
+ px = "exists";
+ ignored = 1;
+ break;
+ case IPFW_TR_IGNORED:
+ px = "ignored";
+ ignored = 1;
+ break;
+ default:
+ px = "unknown";
+ ignored = 1;
+ }
+
+ if (error != 0 && atomic != 0 && ignored == 0)
+ printf("%s(reverted): ", px);
+ else
+ printf("%s: ", px);
+
+ table_show_entry(&xi, ptent);
+ }
+
+ if (tent_buf != &tent)
+ free(tent_buf);
+
+ if (error == 0)
+ return;
+ /* Get real OS error */
+ error = errno;
+
+ /* Try to provide more human-readable error */
+ switch (error) {
+ case EEXIST:
+ etxt = "record already exists";
+ break;
+ case EFBIG:
+ etxt = "limit hit";
+ break;
+ case ESRCH:
+ etxt = "table not found";
+ break;
+ case ENOENT:
+ etxt = "record not found";
+ break;
+ case EACCES:
+ etxt = "table is locked";
+ break;
+ default:
+ etxt = strerror(error);
+ }
+
+ errx(EX_OSERR, "%s: %s", texterr, etxt);
+}
+
+static int
+table_do_lookup(ipfw_obj_header *oh, char *key, ipfw_xtable_info *xi,
+ ipfw_obj_tentry *xtent)
+{
+ char xbuf[sizeof(ipfw_obj_header) + sizeof(ipfw_obj_tentry)];
+ ipfw_obj_tentry *tent;
+ uint8_t type;
+ uint32_t vmask;
+ size_t sz;
+
+ memcpy(xbuf, oh, sizeof(*oh));
+ oh = (ipfw_obj_header *)xbuf;
+ tent = (ipfw_obj_tentry *)(oh + 1);
+
+ memset(tent, 0, sizeof(*tent));
+ tent->head.length = sizeof(*tent);
+ tent->idx = 1;
+
+ tentry_fill_key(oh, tent, key, 0, &type, &vmask, xi);
+ oh->ntlv.type = type;
+
+ sz = sizeof(xbuf);
+ if (do_get3(IP_FW_TABLE_XFIND, &oh->opheader, &sz) != 0)
+ return (errno);
+
+ if (sz < sizeof(xbuf))
+ return (EINVAL);
+
+ *xtent = *tent;
+
+ return (0);
+}
+
+static void
+table_lookup(ipfw_obj_header *oh, int ac, char *av[])
+{
+ ipfw_obj_tentry xtent;
+ ipfw_xtable_info xi;
+ char key[64];
+ int error;
+
+ if (ac == 0)
+ errx(EX_USAGE, "address required");
+
+ strlcpy(key, *av, sizeof(key));
+
+ memset(&xi, 0, sizeof(xi));
+ error = table_do_lookup(oh, key, &xi, &xtent);
+
+ switch (error) {
+ case 0:
+ break;
+ case ESRCH:
+ errx(EX_UNAVAILABLE, "Table %s not found", oh->ntlv.name);
+ case ENOENT:
+ errx(EX_UNAVAILABLE, "Entry %s not found", *av);
+ case ENOTSUP:
+ errx(EX_UNAVAILABLE, "Table %s algo does not support "
+ "\"lookup\" method", oh->ntlv.name);
+ default:
+ err(EX_OSERR, "getsockopt(IP_FW_TABLE_XFIND)");
+ }
+
+ table_show_entry(&xi, &xtent);
+}
+
+static void
+tentry_fill_key_type(char *arg, ipfw_obj_tentry *tentry, uint8_t type,
+ uint8_t tflags)
+{
+ char *p, *pp;
+ int mask, af;
+ struct in6_addr *paddr, tmp;
+ struct tflow_entry *tfe;
+ uint32_t key, *pkey;
+ uint16_t port;
+ struct protoent *pent;
+ struct servent *sent;
+ int masklen;
+
+ masklen = 0;
+ af = 0;
+ paddr = (struct in6_addr *)&tentry->k;
+
+ switch (type) {
+ case IPFW_TABLE_ADDR:
+ /* Remove / if exists */
+ if ((p = strchr(arg, '/')) != NULL) {
+ *p = '\0';
+ mask = atoi(p + 1);
+ }
+
+ if (inet_pton(AF_INET, arg, paddr) == 1) {
+ if (p != NULL && mask > 32)
+ errx(EX_DATAERR, "bad IPv4 mask width: %s",
+ p + 1);
+
+ masklen = p ? mask : 32;
+ af = AF_INET;
+ } else if (inet_pton(AF_INET6, arg, paddr) == 1) {
+ if (IN6_IS_ADDR_V4COMPAT(paddr))
+ errx(EX_DATAERR,
+ "Use IPv4 instead of v4-compatible");
+ if (p != NULL && mask > 128)
+ errx(EX_DATAERR, "bad IPv6 mask width: %s",
+ p + 1);
+
+ masklen = p ? mask : 128;
+ af = AF_INET6;
+ } else {
+ /* Assume FQDN */
+ if (lookup_host(arg, (struct in_addr *)paddr) != 0)
+ errx(EX_NOHOST, "hostname ``%s'' unknown", arg);
+
+ masklen = 32;
+ type = IPFW_TABLE_ADDR;
+ af = AF_INET;
+ }
+ break;
+ case IPFW_TABLE_INTERFACE:
+ /* Assume interface name. Copy significant data only */
+ mask = MIN(strlen(arg), IF_NAMESIZE - 1);
+ memcpy(paddr, arg, mask);
+ /* Set mask to exact match */
+ masklen = 8 * IF_NAMESIZE;
+ break;
+ case IPFW_TABLE_NUMBER:
+ /* Port or any other key */
+ key = strtol(arg, &p, 10);
+ if (*p != '\0')
+ errx(EX_DATAERR, "Invalid number: %s", arg);
+
+ pkey = (uint32_t *)paddr;
+ *pkey = key;
+ masklen = 32;
+ break;
+ case IPFW_TABLE_FLOW:
+ /* Assume [src-ip][,proto][,src-port][,dst-ip][,dst-port] */
+ tfe = &tentry->k.flow;
+ af = 0;
+
+ /* Handle <ipv4|ipv6> */
+ if ((tflags & IPFW_TFFLAG_SRCIP) != 0) {
+ if ((p = strchr(arg, ',')) != NULL)
+ *p++ = '\0';
+ /* Determine family using temporary storage */
+ if (inet_pton(AF_INET, arg, &tmp) == 1) {
+ if (af != 0 && af != AF_INET)
+ errx(EX_DATAERR,
+ "Inconsistent address family\n");
+ af = AF_INET;
+ memcpy(&tfe->a.a4.sip, &tmp, 4);
+ } else if (inet_pton(AF_INET6, arg, &tmp) == 1) {
+ if (af != 0 && af != AF_INET6)
+ errx(EX_DATAERR,
+ "Inconsistent address family\n");
+ af = AF_INET6;
+ memcpy(&tfe->a.a6.sip6, &tmp, 16);
+ }
+
+ arg = p;
+ }
+
+ /* Handle <proto-num|proto-name> */
+ if ((tflags & IPFW_TFFLAG_PROTO) != 0) {
+ if (arg == NULL)
+ errx(EX_DATAERR, "invalid key: proto missing");
+ if ((p = strchr(arg, ',')) != NULL)
+ *p++ = '\0';
+
+ key = strtol(arg, &pp, 10);
+ if (*pp != '\0') {
+ if ((pent = getprotobyname(arg)) == NULL)
+ errx(EX_DATAERR, "Unknown proto: %s",
+ arg);
+ else
+ key = pent->p_proto;
+ }
+
+ if (key > 255)
+ errx(EX_DATAERR, "Bad protocol number: %u",key);
+
+ tfe->proto = key;
+
+ arg = p;
+ }
+
+ /* Handle <port-num|service-name> */
+ if ((tflags & IPFW_TFFLAG_SRCPORT) != 0) {
+ if (arg == NULL)
+ errx(EX_DATAERR, "invalid key: src port missing");
+ if ((p = strchr(arg, ',')) != NULL)
+ *p++ = '\0';
+
+ if ((port = htons(strtol(arg, NULL, 10))) == 0) {
+ if ((sent = getservbyname(arg, NULL)) == NULL)
+ errx(EX_DATAERR, "Unknown service: %s",
+ arg);
+ else
+ key = sent->s_port;
+ }
+
+ tfe->sport = port;
+
+ arg = p;
+ }
+
+ /* Handle <ipv4|ipv6>*/
+ if ((tflags & IPFW_TFFLAG_DSTIP) != 0) {
+ if (arg == NULL)
+ errx(EX_DATAERR, "invalid key: dst ip missing");
+ if ((p = strchr(arg, ',')) != NULL)
+ *p++ = '\0';
+ /* Determine family using temporary storage */
+ if (inet_pton(AF_INET, arg, &tmp) == 1) {
+ if (af != 0 && af != AF_INET)
+ errx(EX_DATAERR,
+ "Inconsistent address family");
+ af = AF_INET;
+ memcpy(&tfe->a.a4.dip, &tmp, 4);
+ } else if (inet_pton(AF_INET6, arg, &tmp) == 1) {
+ if (af != 0 && af != AF_INET6)
+ errx(EX_DATAERR,
+ "Inconsistent address family");
+ af = AF_INET6;
+ memcpy(&tfe->a.a6.dip6, &tmp, 16);
+ }
+
+ arg = p;
+ }
+
+ /* Handle <port-num|service-name> */
+ if ((tflags & IPFW_TFFLAG_DSTPORT) != 0) {
+ if (arg == NULL)
+ errx(EX_DATAERR, "invalid key: dst port missing");
+ if ((p = strchr(arg, ',')) != NULL)
+ *p++ = '\0';
+
+ if ((port = htons(strtol(arg, NULL, 10))) == 0) {
+ if ((sent = getservbyname(arg, NULL)) == NULL)
+ errx(EX_DATAERR, "Unknown service: %s",
+ arg);
+ else
+ key = sent->s_port;
+ }
+
+ tfe->dport = port;
+
+ arg = p;
+ }
+
+ tfe->af = af;
+
+ break;
+
+ default:
+ errx(EX_DATAERR, "Unsupported table type: %d", type);
+ }
+
+ tentry->subtype = af;
+ tentry->masklen = masklen;
+}
+
+static void
+tentry_fill_key(ipfw_obj_header *oh, ipfw_obj_tentry *tent, char *key,
+ int add, uint8_t *ptype, uint32_t *pvmask, ipfw_xtable_info *xi)
+{
+ uint8_t type, tflags;
+ uint32_t vmask;
+ int error;
+ char *del;
+
+ type = 0;
+ tflags = 0;
+ vmask = 0;
+
+ if (xi->tablename[0] == '\0')
+ error = table_get_info(oh, xi);
+ else
+ error = 0;
+
+ if (error == 0) {
+ /* Table found. */
+ type = xi->type;
+ tflags = xi->tflags;
+ vmask = xi->vmask;
+ } else {
+ if (error != ESRCH)
+ errx(EX_OSERR, "Error requesting table %s info",
+ oh->ntlv.name);
+ if (add == 0)
+ errx(EX_DATAERR, "Table %s does not exist",
+ oh->ntlv.name);
+ /*
+ * Table does not exist.
+ * Compability layer: try to interpret data as ADDR
+ * before failing.
+ */
+ if ((del = strchr(key, '/')) != NULL)
+ *del = '\0';
+ if (inet_pton(AF_INET, key, &tent->k.addr6) == 1 ||
+ inet_pton(AF_INET6, key, &tent->k.addr6) == 1) {
+ /* OK Prepare and send */
+ type = IPFW_TABLE_ADDR;
+ vmask = IPFW_VTYPE_LEGACY;
+ } else {
+ /* Inknown key */
+ errx(EX_USAGE, "Table %s does not exist, cannot guess "
+ "key '%s' type", oh->ntlv.name, key);
+ }
+ if (del != NULL)
+ *del = '/';
+ }
+
+ tentry_fill_key_type(key, tent, type, tflags);
+
+ *ptype = type;
+ *pvmask = vmask;
+}
+
+static void
+set_legacy_value(uint32_t val, ipfw_table_value *v)
+{
+ v->tag = val;
+ v->pipe = val;
+ v->divert = val;
+ v->skipto = val;
+ v->netgraph = val;
+ v->fib = val;
+ v->nat = val;
+ v->nh4 = val;
+ v->dscp = (uint8_t)val;
+ v->limit = val;
+}
+
+static void
+tentry_fill_value(ipfw_obj_header *oh, ipfw_obj_tentry *tent, char *arg,
+ uint8_t type, uint32_t vmask)
+{
+ uint32_t a4, flag, val, vm;
+ ipfw_table_value *v;
+ uint32_t i;
+ int dval;
+ char *comma, *e, *etype, *n, *p;
+
+ v = &tent->v.value;
+ vm = vmask;
+
+ /* Compat layer: keep old behavior for legacy value types */
+ if (vmask == IPFW_VTYPE_LEGACY) {
+ /* Try to interpret as number first */
+ val = strtoul(arg, &p, 0);
+ if (*p == '\0') {
+ set_legacy_value(val, v);
+ return;
+ }
+ if (inet_pton(AF_INET, arg, &val) == 1) {
+ set_legacy_value(ntohl(val), v);
+ return;
+ }
+ /* Try hostname */
+ if (lookup_host(arg, (struct in_addr *)&val) == 0) {
+ set_legacy_value(val, v);
+ return;
+ }
+ errx(EX_OSERR, "Unable to parse value %s", arg);
+ }
+
+ /*
+ * Shorthands: handle single value if vmask consists
+ * of numbers only. e.g.:
+ * vmask = "fib,skipto" -> treat input "1" as "1,1"
+ */
+
+ n = arg;
+ etype = NULL;
+ for (i = 1; i < (1 << 31); i *= 2) {
+ if ((flag = (vmask & i)) == 0)
+ continue;
+ vmask &= ~flag;
+
+ if ((comma = strchr(n, ',')) != NULL)
+ *comma = '\0';
+
+ switch (flag) {
+ case IPFW_VTYPE_TAG:
+ v->tag = strtol(n, &e, 10);
+ if (*e != '\0')
+ etype = "tag";
+ break;
+ case IPFW_VTYPE_PIPE:
+ v->pipe = strtol(n, &e, 10);
+ if (*e != '\0')
+ etype = "pipe";
+ break;
+ case IPFW_VTYPE_DIVERT:
+ v->divert = strtol(n, &e, 10);
+ if (*e != '\0')
+ etype = "divert";
+ break;
+ case IPFW_VTYPE_SKIPTO:
+ v->skipto = strtol(n, &e, 10);
+ if (*e != '\0')
+ etype = "skipto";
+ break;
+ case IPFW_VTYPE_NETGRAPH:
+ v->netgraph = strtol(n, &e, 10);
+ if (*e != '\0')
+ etype = "netgraph";
+ break;
+ case IPFW_VTYPE_FIB:
+ v->fib = strtol(n, &e, 10);
+ if (*e != '\0')
+ etype = "fib";
+ break;
+ case IPFW_VTYPE_NAT:
+ v->nat = strtol(n, &e, 10);
+ if (*e != '\0')
+ etype = "nat";
+ break;
+ case IPFW_VTYPE_LIMIT:
+ v->limit = strtol(n, &e, 10);
+ if (*e != '\0')
+ etype = "limit";
+ break;
+ case IPFW_VTYPE_NH4:
+ if (strchr(n, '.') != NULL &&
+ inet_pton(AF_INET, n, &a4) == 1) {
+ v->nh4 = ntohl(a4);
+ break;
+ }
+ if (lookup_host(n, (struct in_addr *)&v->nh4) == 0)
+ break;
+ etype = "ipv4";
+ break;
+ case IPFW_VTYPE_DSCP:
+ if (isalpha(*n)) {
+ if ((dval = match_token(f_ipdscp, n)) != -1) {
+ v->dscp = dval;
+ break;
+ } else
+ etype = "DSCP code";
+ } else {
+ v->dscp = strtol(n, &e, 10);
+ if (v->dscp > 63 || *e != '\0')
+ etype = "DSCP value";
+ }
+ break;
+ case IPFW_VTYPE_NH6:
+ if (strchr(n, ':') != NULL &&
+ inet_pton(AF_INET6, n, &v->nh6) == 1)
+ break;
+ etype = "ipv6";
+ break;
+ }
+
+ if (etype != NULL)
+ errx(EX_USAGE, "Unable to parse %s as %s", n, etype);
+
+ if (comma != NULL)
+ *comma++ = ',';
+
+ if ((n = comma) != NULL)
+ continue;
+
+ /* End of input. */
+ if (vmask != 0)
+ errx(EX_USAGE, "Not enough fields inside value");
+ }
+}
+
+/*
+ * Compare table names.
+ * Honor number comparison.
+ */
+static int
+tablename_cmp(const void *a, const void *b)
+{
+ ipfw_xtable_info *ia, *ib;
+
+ ia = (ipfw_xtable_info *)a;
+ ib = (ipfw_xtable_info *)b;
+
+ return (stringnum_cmp(ia->tablename, ib->tablename));
+}
+
+/*
+ * Retrieves table list from kernel,
+ * optionally sorts it and calls requested function for each table.
+ * Returns 0 on success.
+ */
+static int
+tables_foreach(table_cb_t *f, void *arg, int sort)
+{
+ ipfw_obj_lheader *olh;
+ ipfw_xtable_info *info;
+ size_t sz;
+ int i, error;
+
+ /* Start with reasonable default */
+ sz = sizeof(*olh) + 16 * sizeof(ipfw_xtable_info);
+
+ for (;;) {
+ if ((olh = calloc(1, sz)) == NULL)
+ return (ENOMEM);
+
+ olh->size = sz;
+ if (do_get3(IP_FW_TABLES_XLIST, &olh->opheader, &sz) != 0) {
+ sz = olh->size;
+ free(olh);
+ if (errno != ENOMEM)
+ return (errno);
+ continue;
+ }
+
+ if (sort != 0)
+ qsort(olh + 1, olh->count, olh->objsize, tablename_cmp);
+
+ info = (ipfw_xtable_info *)(olh + 1);
+ for (i = 0; i < olh->count; i++) {
+ error = f(info, arg); /* Ignore errors for now */
+ info = (ipfw_xtable_info *)((caddr_t)info + olh->objsize);
+ }
+
+ free(olh);
+ break;
+ }
+
+ return (0);
+}
+
+
+/*
+ * Retrieves all entries for given table @i in
+ * eXtended format. Allocate buffer large enough
+ * to store result. Called needs to free it later.
+ *
+ * Returns 0 on success.
+ */
+static int
+table_do_get_list(ipfw_xtable_info *i, ipfw_obj_header **poh)
+{
+ ipfw_obj_header *oh;
+ size_t sz;
+ int c;
+
+ sz = 0;
+ oh = NULL;
+ for (c = 0; c < 8; c++) {
+ if (sz < i->size)
+ sz = i->size + 44;
+ if (oh != NULL)
+ free(oh);
+ if ((oh = calloc(1, sz)) == NULL)
+ continue;
+ table_fill_objheader(oh, i);
+ oh->opheader.version = 1; /* Current version */
+ if (do_get3(IP_FW_TABLE_XLIST, &oh->opheader, &sz) == 0) {
+ *poh = oh;
+ return (0);
+ }
+
+ if (errno != ENOMEM)
+ break;
+ }
+ free(oh);
+
+ return (errno);
+}
+
+/*
+ * Shows all entries from @oh in human-readable format
+ */
+static void
+table_show_list(ipfw_obj_header *oh, int need_header)
+{
+ ipfw_obj_tentry *tent;
+ uint32_t count;
+ ipfw_xtable_info *i;
+
+ i = (ipfw_xtable_info *)(oh + 1);
+ tent = (ipfw_obj_tentry *)(i + 1);
+
+ if (need_header)
+ printf("--- table(%s), set(%u) ---\n", i->tablename, i->set);
+
+ count = i->count;
+ while (count > 0) {
+ table_show_entry(i, tent);
+ tent = (ipfw_obj_tentry *)((caddr_t)tent + tent->head.length);
+ count--;
+ }
+}
+
+static void
+table_show_value(char *buf, size_t bufsize, ipfw_table_value *v,
+ uint32_t vmask, int print_ip)
+{
+ uint32_t flag, i, l;
+ size_t sz;
+ struct in_addr a4;
+ char abuf[INET6_ADDRSTRLEN];
+
+ sz = bufsize;
+
+ /*
+ * Some shorthands for printing values:
+ * legacy assumes all values are equal, so keep the first one.
+ */
+ if (vmask == IPFW_VTYPE_LEGACY) {
+ if (print_ip != 0) {
+ flag = htonl(v->tag);
+ inet_ntop(AF_INET, &flag, buf, sz);
+ } else
+ snprintf(buf, sz, "%u", v->tag);
+ return;
+ }
+
+ for (i = 1; i < (1 << 31); i *= 2) {
+ if ((flag = (vmask & i)) == 0)
+ continue;
+ l = 0;
+
+ switch (flag) {
+ case IPFW_VTYPE_TAG:
+ l = snprintf(buf, sz, "%u,", v->tag);
+ break;
+ case IPFW_VTYPE_PIPE:
+ l = snprintf(buf, sz, "%u,", v->pipe);
+ break;
+ case IPFW_VTYPE_DIVERT:
+ l = snprintf(buf, sz, "%d,", v->divert);
+ break;
+ case IPFW_VTYPE_SKIPTO:
+ l = snprintf(buf, sz, "%d,", v->skipto);
+ break;
+ case IPFW_VTYPE_NETGRAPH:
+ l = snprintf(buf, sz, "%u,", v->netgraph);
+ break;
+ case IPFW_VTYPE_FIB:
+ l = snprintf(buf, sz, "%u,", v->fib);
+ break;
+ case IPFW_VTYPE_NAT:
+ l = snprintf(buf, sz, "%u,", v->nat);
+ break;
+ case IPFW_VTYPE_LIMIT:
+ l = snprintf(buf, sz, "%u,", v->limit);
+ break;
+ case IPFW_VTYPE_NH4:
+ a4.s_addr = htonl(v->nh4);
+ inet_ntop(AF_INET, &a4, abuf, sizeof(abuf));
+ l = snprintf(buf, sz, "%s,", abuf);
+ break;
+ case IPFW_VTYPE_DSCP:
+ l = snprintf(buf, sz, "%d,", v->dscp);
+ break;
+ case IPFW_VTYPE_NH6:
+ inet_ntop(AF_INET6, &v->nh6, abuf, sizeof(abuf));
+ l = snprintf(buf, sz, "%s,", abuf);
+ break;
+ }
+
+ buf += l;
+ sz -= l;
+ }
+
+ if (sz != bufsize)
+ *(buf - 1) = '\0';
+}
+
+static void
+table_show_entry(ipfw_xtable_info *i, ipfw_obj_tentry *tent)
+{
+ char *comma, tbuf[128], pval[128];
+ void *paddr;
+ struct tflow_entry *tfe;
+
+ table_show_value(pval, sizeof(pval), &tent->v.value, i->vmask,
+ co.do_value_as_ip);
+
+ switch (i->type) {
+ case IPFW_TABLE_ADDR:
+ /* IPv4 or IPv6 prefixes */
+ inet_ntop(tent->subtype, &tent->k, tbuf, sizeof(tbuf));
+ printf("%s/%u %s\n", tbuf, tent->masklen, pval);
+ break;
+ case IPFW_TABLE_INTERFACE:
+ /* Interface names */
+ printf("%s %s\n", tent->k.iface, pval);
+ break;
+ case IPFW_TABLE_NUMBER:
+ /* numbers */
+ printf("%u %s\n", tent->k.key, pval);
+ break;
+ case IPFW_TABLE_FLOW:
+ /* flows */
+ tfe = &tent->k.flow;
+ comma = "";
+
+ if ((i->tflags & IPFW_TFFLAG_SRCIP) != 0) {
+ if (tfe->af == AF_INET)
+ paddr = &tfe->a.a4.sip;
+ else
+ paddr = &tfe->a.a6.sip6;
+
+ inet_ntop(tfe->af, paddr, tbuf, sizeof(tbuf));
+ printf("%s%s", comma, tbuf);
+ comma = ",";
+ }
+
+ if ((i->tflags & IPFW_TFFLAG_PROTO) != 0) {
+ printf("%s%d", comma, tfe->proto);
+ comma = ",";
+ }
+
+ if ((i->tflags & IPFW_TFFLAG_SRCPORT) != 0) {
+ printf("%s%d", comma, ntohs(tfe->sport));
+ comma = ",";
+ }
+ if ((i->tflags & IPFW_TFFLAG_DSTIP) != 0) {
+ if (tfe->af == AF_INET)
+ paddr = &tfe->a.a4.dip;
+ else
+ paddr = &tfe->a.a6.dip6;
+
+ inet_ntop(tfe->af, paddr, tbuf, sizeof(tbuf));
+ printf("%s%s", comma, tbuf);
+ comma = ",";
+ }
+
+ if ((i->tflags & IPFW_TFFLAG_DSTPORT) != 0) {
+ printf("%s%d", comma, ntohs(tfe->dport));
+ comma = ",";
+ }
+
+ printf(" %s\n", pval);
+ }
+}
+
+static int
+table_do_get_stdlist(uint16_t opcode, ipfw_obj_lheader **polh)
+{
+ ipfw_obj_lheader req, *olh;
+ size_t sz;
+
+ memset(&req, 0, sizeof(req));
+ sz = sizeof(req);
+
+ if (do_get3(opcode, &req.opheader, &sz) != 0)
+ if (errno != ENOMEM)
+ return (errno);
+
+ sz = req.size;
+ if ((olh = calloc(1, sz)) == NULL)
+ return (ENOMEM);
+
+ olh->size = sz;
+ if (do_get3(opcode, &olh->opheader, &sz) != 0) {
+ free(olh);
+ return (errno);
+ }
+
+ *polh = olh;
+ return (0);
+}
+
+static int
+table_do_get_algolist(ipfw_obj_lheader **polh)
+{
+
+ return (table_do_get_stdlist(IP_FW_TABLES_ALIST, polh));
+}
+
+static int
+table_do_get_vlist(ipfw_obj_lheader **polh)
+{
+
+ return (table_do_get_stdlist(IP_FW_TABLE_VLIST, polh));
+}
+
+void
+ipfw_list_ta(int ac, char *av[])
+{
+ ipfw_obj_lheader *olh;
+ ipfw_ta_info *info;
+ int error, i;
+ const char *atype;
+
+ error = table_do_get_algolist(&olh);
+ if (error != 0)
+ err(EX_OSERR, "Unable to request algorithm list");
+
+ info = (ipfw_ta_info *)(olh + 1);
+ for (i = 0; i < olh->count; i++) {
+ if ((atype = match_value(tabletypes, info->type)) == NULL)
+ atype = "unknown";
+ printf("--- %s ---\n", info->algoname);
+ printf(" type: %s\n refcount: %u\n", atype, info->refcnt);
+
+ info = (ipfw_ta_info *)((caddr_t)info + olh->objsize);
+ }
+
+ free(olh);
+}
+
+
+/* Copy of current kernel table_value structure */
+struct _table_value {
+ uint32_t tag; /* O_TAG/O_TAGGED */
+ uint32_t pipe; /* O_PIPE/O_QUEUE */
+ uint16_t divert; /* O_DIVERT/O_TEE */
+ uint16_t skipto; /* skipto, CALLRET */
+ uint32_t netgraph; /* O_NETGRAPH/O_NGTEE */
+ uint32_t fib; /* O_SETFIB */
+ uint32_t nat; /* O_NAT */
+ uint32_t nh4;
+ uint8_t dscp;
+ uint8_t spare0[3];
+ /* -- 32 bytes -- */
+ struct in6_addr nh6;
+ uint32_t limit; /* O_LIMIT */
+ uint32_t spare1;
+ uint64_t refcnt; /* Number of references */
+};
+
+int
+compare_values(const void *_a, const void *_b)
+{
+ struct _table_value *a, *b;
+
+ a = (struct _table_value *)_a;
+ b = (struct _table_value *)_b;
+
+ if (a->spare1 < b->spare1)
+ return (-1);
+ else if (a->spare1 > b->spare1)
+ return (1);
+
+ return (0);
+}
+
+void
+ipfw_list_values(int ac, char *av[])
+{
+ ipfw_obj_lheader *olh;
+ struct _table_value *v;
+ int error, i;
+ uint32_t vmask;
+ char buf[128];
+
+ error = table_do_get_vlist(&olh);
+ if (error != 0)
+ err(EX_OSERR, "Unable to request value list");
+
+ vmask = 0x7FFFFFFF; /* Similar to IPFW_VTYPE_LEGACY */
+
+ table_print_valheader(buf, sizeof(buf), vmask);
+ printf("HEADER: %s\n", buf);
+ v = (struct _table_value *)(olh + 1);
+ qsort(v, olh->count, olh->objsize, compare_values);
+ for (i = 0; i < olh->count; i++) {
+ table_show_value(buf, sizeof(buf), (ipfw_table_value *)v,
+ vmask, 0);
+ printf("[%u] refs=%lu %s\n", v->spare1, (u_long)v->refcnt, buf);
+ v = (struct _table_value *)((caddr_t)v + olh->objsize);
+ }
+
+ free(olh);
+}
+
+int
+compare_ntlv(const void *_a, const void *_b)
+{
+ ipfw_obj_ntlv *a, *b;
+
+ a = (ipfw_obj_ntlv *)_a;
+ b = (ipfw_obj_ntlv *)_b;
+
+ if (a->set < b->set)
+ return (-1);
+ else if (a->set > b->set)
+ return (1);
+
+ if (a->idx < b->idx)
+ return (-1);
+ else if (a->idx > b->idx)
+ return (1);
+
+ return (0);
+}
+
+int
+compare_kntlv(const void *k, const void *v)
+{
+ ipfw_obj_ntlv *ntlv;
+ uint16_t key;
+
+ key = *((uint16_t *)k);
+ ntlv = (ipfw_obj_ntlv *)v;
+
+ if (key < ntlv->idx)
+ return (-1);
+ else if (key > ntlv->idx)
+ return (1);
+
+ return (0);
+}
+
+/*
+ * Finds table name in @ctlv by @idx.
+ * Uses the following facts:
+ * 1) All TLVs are the same size
+ * 2) Kernel implementation provides already sorted list.
+ *
+ * Returns table name or NULL.
+ */
+char *
+table_search_ctlv(ipfw_obj_ctlv *ctlv, uint16_t idx)
+{
+ ipfw_obj_ntlv *ntlv;
+
+ ntlv = bsearch(&idx, (ctlv + 1), ctlv->count, ctlv->objsize,
+ compare_kntlv);
+
+ if (ntlv != 0)
+ return (ntlv->name);
+
+ return (NULL);
+}
+
+void
+table_sort_ctlv(ipfw_obj_ctlv *ctlv)
+{
+
+ qsort(ctlv + 1, ctlv->count, ctlv->objsize, compare_ntlv);
+}
+
+int
+table_check_name(char *tablename)
+{
+ int c, i, l;
+
+ /*
+ * Check if tablename is null-terminated and contains
+ * valid symbols only. Valid mask is:
+ * [a-zA-Z0-9\-_\.]{1,63}
+ */
+ l = strlen(tablename);
+ if (l == 0 || l >= 64)
+ return (EINVAL);
+ for (i = 0; i < l; i++) {
+ c = tablename[i];
+ if (isalpha(c) || isdigit(c) || c == '_' ||
+ c == '-' || c == '.')
+ continue;
+ return (EINVAL);
+ }
+
+ /* Restrict some 'special' names */
+ if (strcmp(tablename, "all") == 0)
+ return (EINVAL);
+
+ return (0);
+}
+