Merge projects/ipfw to HEAD.

Main user-visible changes are related to tables:

* Tables are now identified by names, not numbers.
 There can be up to 65k tables with up to 63-byte long names.
* Tables are now set-aware (default off), so you can switch/move
 them atomically with rules.
* More functionality is supported (swap, lock, limits, user-level lookup,
 batched add/del) by generic table code.
* New table types are added (flow) so you can match multiple packet fields at once.
* Ability to add different type of lookup algorithms for particular
 table type has been added.
* New table algorithms are added (cidr:hash, iface:array, number:array and
 flow:hash) to make certain types of lookup more effective.
* Table value are now capable of holding multiple data fields for
  different tablearg users

Performance changes:
* Main ipfw lock was converted to rmlock
* Rule counters were separated from rule itself and made per-cpu.
* Radix table entries fits into 128 bytes
* struct ip_fw is now more compact so more rules will fit into 64 bytes
* interface tables uses array of existing ifindexes for faster match

ABI changes:
All functionality supported by old ipfw(8) remains functional.
 Old & new binaries can work together with the following restrictions:
* Tables named other than ^\d+$ are shown as table(65535) in
 ruleset in old binaries

Internal changes:.
Changing table ids to numbers resulted in format modification for
 most sockopt codes. Old sopt format was compact, but very hard to
 extend (no versioning, inability to add more opcodes), so
* All relevant opcodes were converted to TLV-based versioned IP_FW3-based codes.
* The remaining opcodes were also converted to be able to eliminate
 all older opcodes at once
* All IP_FW3 handlers uses special API instead of calling sooptcopy*
 directly to ease adding another communication methods
* struct ip_fw is now different for kernel and userland
* tablearg value has been changed to 0 to ease future extensions
* table "values" are now indexes in special value array which
 holds extended data for given index
* Batched add/delete has been added to tables code
* Most changes has been done to permit batched rule addition.
* interface tracking API has been added (started on demand)
 to permit effective interface tables operations
* O(1) skipto cache, currently turned off by default at
 compile-time (eats 512K).

* Several steps has been made towards making libipfw:
  * most of new functions were separated into "parse/prepare/show
    and actuall-do-stuff" pieces (already merged).
  * there are separate functions for parsing text string into "struct ip_fw"
    and printing "struct ip_fw" to supplied buffer (already merged).
* Probably some more less significant/forgotten features

MFC after:	1 month
Sponsored by:	Yandex LLC
This commit is contained in:
Alexander V. Chernikov 2014-10-09 19:32:35 +00:00
commit a13a821641
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=272840
23 changed files with 16506 additions and 1983 deletions

View File

@ -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"

View File

@ -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
.Cm table
.Brq Ar number | all
.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
.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.
.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
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 .
Tables require explicit creation via
.Cm create
before use.
.Pp
Internally, each table is stored in a Radix tree, the same way as
the routing table (see
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
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:arrray | 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 ) .
.Pp
Lookup tables currently support only ports, jail IDs, IPv4/IPv6 addresses
and interface names.
Wildcards is not supported for interface names.
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:arrray
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 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 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 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

File diff suppressed because it is too large Load Diff

View File

@ -207,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.
@ -236,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);
@ -282,6 +311,7 @@ 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 */
@ -311,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[]);

View File

@ -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);
}

View File

@ -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;
ipfw_obj_header *oh;
char *name;
int cmd;
struct nat_list_arg nla;
do_rule = 0;
nalloc = 1024;
size = 0;
data = NULL;
frule = 0;
lrule = IPFW_DEFAULT_RULE; /* max ipfw rule number */
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;
}
/* 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 (strcmp(av[0], "log") == 0) {
cmd = IP_FW_NAT44_XGETLOG;
continue;
}
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);
}
}

2012
sbin/ipfw/tables.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -3514,6 +3514,9 @@ netpfil/ipfw/ip_fw_log.c optional inet ipfirewall
netpfil/ipfw/ip_fw_pfil.c optional inet ipfirewall
netpfil/ipfw/ip_fw_sockopt.c optional inet ipfirewall
netpfil/ipfw/ip_fw_table.c optional inet ipfirewall
netpfil/ipfw/ip_fw_table_algo.c optional inet ipfirewall
netpfil/ipfw/ip_fw_table_value.c optional inet ipfirewall
netpfil/ipfw/ip_fw_iface.c optional inet ipfirewall
netpfil/ipfw/ip_fw_nat.c optional inet ipfirewall_nat
netpfil/pf/if_pflog.c optional pflog pf inet
netpfil/pf/if_pfsync.c optional pfsync pf inet

View File

@ -5,7 +5,8 @@
KMOD= ipfw
SRCS= ip_fw2.c ip_fw_pfil.c
SRCS+= ip_fw_dynamic.c ip_fw_log.c
SRCS+= ip_fw_sockopt.c ip_fw_table.c
SRCS+= ip_fw_sockopt.c ip_fw_table.c ip_fw_table_algo.c ip_fw_iface.c
SRCS+= ip_fw_table_value.c
SRCS+= opt_inet.h opt_inet6.h opt_ipdivert.h opt_ipfw.h opt_ipsec.h
CFLAGS+= -DIPFIREWALL

View File

@ -90,7 +90,7 @@ static struct ng_type ng_ipfw_typestruct = {
.disconnect = ng_ipfw_disconnect,
};
NETGRAPH_INIT(ipfw, &ng_ipfw_typestruct);
MODULE_DEPEND(ng_ipfw, ipfw, 2, 2, 2);
MODULE_DEPEND(ng_ipfw, ipfw, 3, 3, 3);
/* Information we store for each hook */
struct ng_ipfw_hook_priv {

View File

@ -36,6 +36,9 @@
*/
#define IPFW_DEFAULT_RULE 65535
#define RESVD_SET 31 /*set for default and persistent rules*/
#define IPFW_MAX_SETS 32 /* Number of sets supported by ipfw*/
/*
* Default number of ipfw tables.
*/
@ -44,17 +47,17 @@
/*
* Most commands (queue, pipe, tag, untag, limit...) can have a 16-bit
* argument between 1 and 65534. The value 0 is unused, the value
* 65535 (IP_FW_TABLEARG) is used to represent 'tablearg', i.e. the
* can be 1..65534, or 65535 to indicate the use of a 'tablearg'
* argument between 1 and 65534. The value 0 (IP_FW_TARG) is used
* to represent 'tablearg' value, e.g. indicate the use of a 'tablearg'
* result of the most recent table() lookup.
* Note that 16bit is only a historical limit, resulting from
* the use of a 16-bit fields for that value. In reality, we can have
* 2^32 pipes, queues, tag values and so on, and use 0 as a tablearg.
* 2^32 pipes, queues, tag values and so on.
*/
#define IPFW_ARG_MIN 1
#define IPFW_ARG_MAX 65534
#define IP_FW_TABLEARG 65535 /* XXX should use 0 */
#define IP_FW_TABLEARG 65535 /* Compat value for old clients */
#define IP_FW_TARG 0 /* Current tablearg value */
/*
* Number of entries in the call stack of the call/return commands.
@ -65,15 +68,43 @@
/* IP_FW3 header/opcodes */
typedef struct _ip_fw3_opheader {
uint16_t opcode; /* Operation opcode */
uint16_t reserved[3]; /* Align to 64-bit boundary */
uint16_t version; /* Opcode version */
uint16_t reserved[2]; /* Align to 64-bit boundary */
} ip_fw3_opheader;
/* IPFW extented tables support */
/* IP_FW3 opcodes */
#define IP_FW_TABLE_XADD 86 /* add entry */
#define IP_FW_TABLE_XDEL 87 /* delete entry */
#define IP_FW_TABLE_XGETSIZE 88 /* get table size */
#define IP_FW_TABLE_XGETSIZE 88 /* get table size (deprecated) */
#define IP_FW_TABLE_XLIST 89 /* list table contents */
#define IP_FW_TABLE_XDESTROY 90 /* destroy table */
#define IP_FW_TABLES_XLIST 92 /* list all tables */
#define IP_FW_TABLE_XINFO 93 /* request info for one table */
#define IP_FW_TABLE_XFLUSH 94 /* flush table data */
#define IP_FW_TABLE_XCREATE 95 /* create new table */
#define IP_FW_TABLE_XMODIFY 96 /* modify existing table */
#define IP_FW_XGET 97 /* Retrieve configuration */
#define IP_FW_XADD 98 /* add rule */
#define IP_FW_XDEL 99 /* del rule */
#define IP_FW_XMOVE 100 /* move rules to different set */
#define IP_FW_XZERO 101 /* clear accounting */
#define IP_FW_XRESETLOG 102 /* zero rules logs */
#define IP_FW_SET_SWAP 103 /* Swap between 2 sets */
#define IP_FW_SET_MOVE 104 /* Move one set to another one */
#define IP_FW_SET_ENABLE 105 /* Enable/disable sets */
#define IP_FW_TABLE_XFIND 106 /* finds an entry */
#define IP_FW_XIFLIST 107 /* list tracked interfaces */
#define IP_FW_TABLES_ALIST 108 /* list table algorithms */
#define IP_FW_TABLE_XSWAP 109 /* swap two tables */
#define IP_FW_TABLE_VLIST 110 /* dump table value hash */
#define IP_FW_NAT44_XCONFIG 111 /* Create/modify NAT44 instance */
#define IP_FW_NAT44_DESTROY 112 /* Destroys NAT44 instance */
#define IP_FW_NAT44_XGETCONFIG 113 /* Get NAT44 instance config */
#define IP_FW_NAT44_LIST_NAT 114 /* List all NAT44 instances */
#define IP_FW_NAT44_XGETLOG 115 /* Get log from NAT44 instance */
#define IP_FW_DUMP_SOPTCODES 116 /* Dump available sopts/versions */
/*
* The kernel representation of ipfw rules is made of a list of
@ -220,6 +251,7 @@ enum ipfw_opcodes { /* arguments (4 byte each) */
O_DSCP, /* 2 u32 = DSCP mask */
O_SETDSCP, /* arg1=DSCP value */
O_IP_FLOW_LOOKUP, /* arg1=table number, u32=value */
O_LAST_OPCODE /* not an opcode! */
};
@ -341,6 +373,7 @@ typedef struct _ipfw_insn_if {
union {
struct in_addr ip;
int glob;
uint16_t kidx;
} p;
char name[IFNAMSIZ];
} ipfw_insn_if;
@ -377,6 +410,8 @@ typedef struct _ipfw_insn_log {
u_int32_t log_left; /* how many left to log */
} ipfw_insn_log;
/* Legacy NAT structures, compat only */
#ifndef _KERNEL
/*
* Data structures required by both ipfw(8) and ipfw(4) but not part of the
* management API are protected by IPFW_INTERNAL.
@ -438,6 +473,44 @@ struct cfg_nat {
#define SOF_REDIR sizeof(struct cfg_redir)
#define SOF_SPOOL sizeof(struct cfg_spool)
#endif /* ifndef _KERNEL */
struct nat44_cfg_spool {
struct in_addr addr;
uint16_t port;
uint16_t spare;
};
#define NAT44_REDIR_ADDR 0x01
#define NAT44_REDIR_PORT 0x02
#define NAT44_REDIR_PROTO 0x04
/* Nat redirect configuration. */
struct nat44_cfg_redir {
struct in_addr laddr; /* local ip address */
struct in_addr paddr; /* public ip address */
struct in_addr raddr; /* remote ip address */
uint16_t lport; /* local port */
uint16_t pport; /* public port */
uint16_t rport; /* remote port */
uint16_t pport_cnt; /* number of public ports */
uint16_t rport_cnt; /* number of remote ports */
uint16_t mode; /* type of redirect mode */
uint16_t spool_cnt; /* num of entry in spool chain */
uint16_t spare;
uint32_t proto; /* protocol: tcp/udp */
};
/* Nat configuration data struct. */
struct nat44_cfg_nat {
char name[64]; /* nat name */
char if_name[64]; /* interface name */
uint32_t size; /* structure size incl. redirs */
struct in_addr ip; /* nat IPv4 address */
uint32_t mode; /* aliasing mode */
uint32_t redir_cnt; /* number of entry in spool chain */
};
/* Nat command. */
typedef struct _ipfw_insn_nat {
ipfw_insn o;
@ -471,15 +544,17 @@ typedef struct _ipfw_insn_icmp6 {
/*
* Here we have the structure representing an ipfw rule.
*
* It starts with a general area (with link fields and counters)
* Layout:
* struct ip_fw_rule
* [ counter block, size = rule->cntr_len ]
* [ one or more instructions, size = rule->cmd_len * 4 ]
*
* It starts with a general area (with link fields).
* Counter block may be next (if rule->cntr_len > 0),
* followed by an array of one or more instructions, which the code
* accesses as an array of 32-bit values.
*
* Given a rule pointer r:
*
* r->cmd is the start of the first instruction.
* ACTION_PTR(r) is the start of the first action (things to do
* once a rule matched).
* accesses as an array of 32-bit values. rule->cmd_len represents
* the total instructions legth in u32 worrd, while act_ofs represents
* rule action offset in u32 words.
*
* When assembling instruction, remember the following:
*
@ -490,11 +565,41 @@ typedef struct _ipfw_insn_icmp6 {
* + if a rule has an "altq" option, it comes after "log"
* + if a rule has an O_TAG option, it comes after "log" and "altq"
*
* NOTE: we use a simple linked list of rules because we never need
* to delete a rule without scanning the list. We do not use
* queue(3) macros for portability and readability.
*
* All structures (excluding instructions) are u64-aligned.
* Please keep this.
*/
struct ip_fw_rule {
uint16_t act_ofs; /* offset of action in 32-bit units */
uint16_t cmd_len; /* # of 32-bit words in cmd */
uint16_t spare;
uint8_t set; /* rule set (0..31) */
uint8_t flags; /* rule flags */
uint32_t rulenum; /* rule number */
uint32_t id; /* rule id */
ipfw_insn cmd[1]; /* storage for commands */
};
#define IPFW_RULE_NOOPT 0x01 /* Has no options in body */
/* Unaligned version */
/* Base ipfw rule counter block. */
struct ip_fw_bcounter {
uint16_t size; /* Size of counter block, bytes */
uint8_t flags; /* flags for given block */
uint8_t spare;
uint32_t timestamp; /* tv_sec of last match */
uint64_t pcnt; /* Packet counter */
uint64_t bcnt; /* Byte counter */
};
#ifndef _KERNEL
/*
* Legacy rule format
*/
struct ip_fw {
struct ip_fw *x_next; /* linked list of rules */
struct ip_fw *next_rule; /* ptr to next [skipto] rule */
@ -503,8 +608,7 @@ struct ip_fw {
uint16_t act_ofs; /* offset of action in 32-bit units */
uint16_t cmd_len; /* # of 32-bit words in cmd */
uint16_t rulenum; /* rule number */
uint8_t set; /* rule set (0..31) */
#define RESVD_SET 31 /* set for default and persistent rules */
uint8_t set; /* rule set (0..31) */
uint8_t _pad; /* padding */
uint32_t id; /* rule id */
@ -515,12 +619,13 @@ struct ip_fw {
ipfw_insn cmd[1]; /* storage for commands */
};
#endif
#define ACTION_PTR(rule) \
(ipfw_insn *)( (u_int32_t *)((rule)->cmd) + ((rule)->act_ofs) )
#define RULESIZE(rule) (sizeof(struct ip_fw) + \
((struct ip_fw *)(rule))->cmd_len * 4 - 4)
#define RULESIZE(rule) (sizeof(*(rule)) + (rule)->cmd_len * 4 - 4)
#if 1 // should be moved to in.h
/*
@ -598,9 +703,27 @@ struct _ipfw_dyn_rule {
* These are used for lookup tables.
*/
#define IPFW_TABLE_CIDR 1 /* Table for holding IPv4/IPv6 prefixes */
#define IPFW_TABLE_ADDR 1 /* Table for holding IPv4/IPv6 prefixes */
#define IPFW_TABLE_INTERFACE 2 /* Table for holding interface names */
#define IPFW_TABLE_MAXTYPE 2 /* Maximum valid number */
#define IPFW_TABLE_NUMBER 3 /* Table for holding ports/uid/gid/etc */
#define IPFW_TABLE_FLOW 4 /* Table for holding flow data */
#define IPFW_TABLE_MAXTYPE 4 /* Maximum valid number */
#define IPFW_TABLE_CIDR IPFW_TABLE_ADDR /* compat */
/* Value types */
#define IPFW_VTYPE_LEGACY 0xFFFFFFFF /* All data is filled in */
#define IPFW_VTYPE_SKIPTO 0x00000001 /* skipto/call/callreturn */
#define IPFW_VTYPE_PIPE 0x00000002 /* pipe/queue */
#define IPFW_VTYPE_FIB 0x00000004 /* setfib */
#define IPFW_VTYPE_NAT 0x00000008 /* nat */
#define IPFW_VTYPE_DSCP 0x00000010 /* dscp */
#define IPFW_VTYPE_TAG 0x00000020 /* tag/untag */
#define IPFW_VTYPE_DIVERT 0x00000040 /* divert/tee */
#define IPFW_VTYPE_NETGRAPH 0x00000080 /* netgraph/ngtee */
#define IPFW_VTYPE_LIMIT 0x00000100 /* IPv6 nexthop */
#define IPFW_VTYPE_NH4 0x00000200 /* IPv4 nexthop */
#define IPFW_VTYPE_NH6 0x00000400 /* IPv6 nexthop */
typedef struct _ipfw_table_entry {
in_addr_t addr; /* network address */
@ -632,7 +755,7 @@ typedef struct _ipfw_table {
} ipfw_table;
typedef struct _ipfw_xtable {
ip_fw3_opheader opheader; /* eXtended tables are controlled via IP_FW3 */
ip_fw3_opheader opheader; /* IP_FW3 opcode */
uint32_t size; /* size of entries in bytes */
uint32_t cnt; /* # of entries */
uint16_t tbl; /* table number */
@ -640,4 +763,242 @@ typedef struct _ipfw_xtable {
ipfw_table_xentry xent[0]; /* entries */
} ipfw_xtable;
typedef struct _ipfw_obj_tlv {
uint16_t type; /* TLV type */
uint16_t flags; /* TLV-specific flags */
uint32_t length; /* Total length, aligned to u64 */
} ipfw_obj_tlv;
#define IPFW_TLV_TBL_NAME 1
#define IPFW_TLV_TBLNAME_LIST 2
#define IPFW_TLV_RULE_LIST 3
#define IPFW_TLV_DYNSTATE_LIST 4
#define IPFW_TLV_TBL_ENT 5
#define IPFW_TLV_DYN_ENT 6
#define IPFW_TLV_RULE_ENT 7
#define IPFW_TLV_TBLENT_LIST 8
#define IPFW_TLV_RANGE 9
/* Object name TLV */
typedef struct _ipfw_obj_ntlv {
ipfw_obj_tlv head; /* TLV header */
uint16_t idx; /* Name index */
uint8_t spare; /* unused */
uint8_t type; /* object type, if applicable */
uint32_t set; /* set, if applicable */
char name[64]; /* Null-terminated name */
} ipfw_obj_ntlv;
/* IPv4/IPv6 L4 flow description */
struct tflow_entry {
uint8_t af;
uint8_t proto;
uint16_t spare;
uint16_t sport;
uint16_t dport;
union {
struct {
struct in_addr sip;
struct in_addr dip;
} a4;
struct {
struct in6_addr sip6;
struct in6_addr dip6;
} a6;
} a;
};
typedef struct _ipfw_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];
struct in6_addr nh6;
uint32_t limit; /* O_LIMIT */
uint32_t spare1;
uint64_t reserved;
} ipfw_table_value;
/* Table entry TLV */
typedef struct _ipfw_obj_tentry {
ipfw_obj_tlv head; /* TLV header */
uint8_t subtype; /* subtype (IPv4,IPv6) */
uint8_t masklen; /* mask length */
uint8_t result; /* request result */
uint8_t spare0;
uint16_t idx; /* Table name index */
uint16_t spare1;
union {
/* Longest field needs to be aligned by 8-byte boundary */
struct in_addr addr; /* IPv4 address */
uint32_t key; /* uid/gid/port */
struct in6_addr addr6; /* IPv6 address */
char iface[IF_NAMESIZE]; /* interface name */
struct tflow_entry flow;
} k;
union {
ipfw_table_value value; /* value data */
uint32_t kidx; /* value kernel index */
} v;
} ipfw_obj_tentry;
#define IPFW_TF_UPDATE 0x01 /* Update record if exists */
/* Container TLV */
#define IPFW_CTF_ATOMIC 0x01 /* Perform atomic operation */
/* Operation results */
#define IPFW_TR_IGNORED 0 /* Entry was ignored (rollback) */
#define IPFW_TR_ADDED 1 /* Entry was succesfully added */
#define IPFW_TR_UPDATED 2 /* Entry was succesfully updated*/
#define IPFW_TR_DELETED 3 /* Entry was succesfully deleted*/
#define IPFW_TR_LIMIT 4 /* Entry was ignored (limit) */
#define IPFW_TR_NOTFOUND 5 /* Entry was not found */
#define IPFW_TR_EXISTS 6 /* Entry already exists */
#define IPFW_TR_ERROR 7 /* Request has failed (unknown) */
typedef struct _ipfw_obj_dyntlv {
ipfw_obj_tlv head;
ipfw_dyn_rule state;
} ipfw_obj_dyntlv;
#define IPFW_DF_LAST 0x01 /* Last state in chain */
/* Containter TLVs */
typedef struct _ipfw_obj_ctlv {
ipfw_obj_tlv head; /* TLV header */
uint32_t count; /* Number of sub-TLVs */
uint16_t objsize; /* Single object size */
uint8_t version; /* TLV version */
uint8_t flags; /* TLV-specific flags */
} ipfw_obj_ctlv;
/* Range TLV */
typedef struct _ipfw_range_tlv {
ipfw_obj_tlv head; /* TLV header */
uint32_t flags; /* Range flags */
uint16_t start_rule; /* Range start */
uint16_t end_rule; /* Range end */
uint32_t set; /* Range set to match */
uint32_t new_set; /* New set to move/swap to */
} ipfw_range_tlv;
#define IPFW_RCFLAG_RANGE 0x01 /* rule range is set */
#define IPFW_RCFLAG_ALL 0x02 /* match ALL rules */
#define IPFW_RCFLAG_SET 0x04 /* match rules in given set */
typedef struct _ipfw_ta_tinfo {
uint32_t flags; /* Format flags */
uint32_t spare;
uint8_t taclass4; /* algorithm class */
uint8_t spare4;
uint16_t itemsize4; /* item size in runtime */
uint32_t size4; /* runtime structure size */
uint32_t count4; /* number of items in runtime */
uint8_t taclass6; /* algorithm class */
uint8_t spare6;
uint16_t itemsize6; /* item size in runtime */
uint32_t size6; /* runtime structure size */
uint32_t count6; /* number of items in runtime */
} ipfw_ta_tinfo;
#define IPFW_TACLASS_HASH 1 /* algo is based on hash */
#define IPFW_TACLASS_ARRAY 2 /* algo is based on array */
#define IPFW_TACLASS_RADIX 3 /* algo is based on radix tree */
#define IPFW_TATFLAGS_DATA 0x0001 /* Has data filled in */
#define IPFW_TATFLAGS_AFDATA 0x0002 /* Separate data per AF */
#define IPFW_TATFLAGS_AFITEM 0x0004 /* diff. items per AF */
typedef struct _ipfw_xtable_info {
uint8_t type; /* table type (addr,iface,..) */
uint8_t tflags; /* type flags */
uint16_t mflags; /* modification flags */
uint16_t flags; /* generic table flags */
uint16_t spare[3];
uint32_t vmask; /* bitmask with value types */
uint32_t set; /* set table is in */
uint32_t kidx; /* kernel index */
uint32_t refcnt; /* number of references */
uint32_t count; /* Number of records */
uint32_t size; /* Total size of records(export)*/
uint32_t limit; /* Max number of records */
char tablename[64]; /* table name */
char algoname[64]; /* algorithm name */
ipfw_ta_tinfo ta_info; /* additional algo stats */
} ipfw_xtable_info;
/* Generic table flags */
#define IPFW_TGFLAGS_LOCKED 0x01 /* Tables is locked from changes*/
/* Table type-specific flags */
#define IPFW_TFFLAG_SRCIP 0x01
#define IPFW_TFFLAG_DSTIP 0x02
#define IPFW_TFFLAG_SRCPORT 0x04
#define IPFW_TFFLAG_DSTPORT 0x08
#define IPFW_TFFLAG_PROTO 0x10
/* Table modification flags */
#define IPFW_TMFLAGS_LIMIT 0x0002 /* Change limit value */
#define IPFW_TMFLAGS_LOCK 0x0004 /* Change table lock state */
typedef struct _ipfw_iface_info {
char ifname[64]; /* interface name */
uint32_t ifindex; /* interface index */
uint32_t flags; /* flags */
uint32_t refcnt; /* number of references */
uint32_t gencnt; /* number of changes */
uint64_t spare;
} ipfw_iface_info;
#define IPFW_IFFLAG_RESOLVED 0x01 /* Interface exists */
typedef struct _ipfw_ta_info {
char algoname[64]; /* algorithm name */
uint32_t type; /* lookup type */
uint32_t flags;
uint32_t refcnt;
uint32_t spare0;
uint64_t spare1;
} ipfw_ta_info;
#define IPFW_OBJTYPE_TABLE 1
typedef struct _ipfw_obj_header {
ip_fw3_opheader opheader; /* IP_FW3 opcode */
uint32_t spare;
uint16_t idx; /* object name index */
uint8_t objtype; /* object type */
uint8_t objsubtype; /* object subtype */
ipfw_obj_ntlv ntlv; /* object name tlv */
} ipfw_obj_header;
typedef struct _ipfw_obj_lheader {
ip_fw3_opheader opheader; /* IP_FW3 opcode */
uint32_t set_mask; /* disabled set mask */
uint32_t count; /* Total objects count */
uint32_t size; /* Total size (incl. header) */
uint32_t objsize; /* Size of one object */
} ipfw_obj_lheader;
#define IPFW_CFG_GET_STATIC 0x01
#define IPFW_CFG_GET_STATES 0x02
#define IPFW_CFG_GET_COUNTERS 0x04
typedef struct _ipfw_cfg_lheader {
ip_fw3_opheader opheader; /* IP_FW3 opcode */
uint32_t set_mask; /* enabled set mask */
uint32_t spare;
uint32_t flags; /* Request flags */
uint32_t size; /* neded buffer size */
uint32_t start_rule;
uint32_t end_rule;
} ipfw_cfg_lheader;
typedef struct _ipfw_range_header {
ip_fw3_opheader opheader; /* IP_FW3 opcode */
ipfw_range_tlv range;
} ipfw_range_header;
typedef struct _ipfw_sopt_info {
uint16_t opcode;
uint8_t version;
uint8_t dir;
uint8_t spare;
uint64_t refcnt;
} ipfw_sopt_info;
#endif /* _IPFW2_H */

View File

@ -2294,7 +2294,7 @@ static moduledata_t dummynet_mod = {
#define DN_SI_SUB SI_SUB_PROTO_IFATTACHDOMAIN
#define DN_MODEV_ORD (SI_ORDER_ANY - 128) /* after ipfw */
DECLARE_MODULE(dummynet, dummynet_mod, DN_SI_SUB, DN_MODEV_ORD);
MODULE_DEPEND(dummynet, ipfw, 2, 2, 2);
MODULE_DEPEND(dummynet, ipfw, 3, 3, 3);
MODULE_VERSION(dummynet, 3);
/*

View File

@ -42,6 +42,7 @@ __FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/condvar.h>
#include <sys/counter.h>
#include <sys/eventhandler.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
@ -52,6 +53,7 @@ __FBSDID("$FreeBSD$");
#include <sys/priv.h>
#include <sys/proc.h>
#include <sys/rwlock.h>
#include <sys/rmlock.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/sysctl.h>
@ -101,10 +103,6 @@ __FBSDID("$FreeBSD$");
* All ipfw global variables are here.
*/
/* ipfw_vnet_ready controls when we are open for business */
static VNET_DEFINE(int, ipfw_vnet_ready) = 0;
#define V_ipfw_vnet_ready VNET(ipfw_vnet_ready)
static VNET_DEFINE(int, fw_deny_unknown_exthdrs);
#define V_fw_deny_unknown_exthdrs VNET(fw_deny_unknown_exthdrs)
@ -121,9 +119,20 @@ VNET_DEFINE(int, autoinc_step);
VNET_DEFINE(int, fw_one_pass) = 1;
VNET_DEFINE(unsigned int, fw_tables_max);
VNET_DEFINE(unsigned int, fw_tables_sets) = 0; /* Don't use set-aware tables */
/* Use 128 tables by default */
static unsigned int default_fw_tables = IPFW_TABLES_DEFAULT;
#ifndef LINEAR_SKIPTO
static int jump_fast(struct ip_fw_chain *chain, struct ip_fw *f, int num,
int tablearg, int jump_backwards);
#define JUMP(ch, f, num, targ, back) jump_fast(ch, f, num, targ, back)
#else
static int jump_linear(struct ip_fw_chain *chain, struct ip_fw *f, int num,
int tablearg, int jump_backwards);
#define JUMP(ch, f, num, targ, back) jump_linear(ch, f, num, targ, back)
#endif
/*
* Each rule belongs to one of 32 different sets (0..31).
* The variable set_disable contains one bit per set.
@ -144,6 +153,9 @@ VNET_DEFINE(int, verbose_limit);
/* layer3_chain contains the list of rules for layer 3 */
VNET_DEFINE(struct ip_fw_chain, layer3_chain);
/* ipfw_vnet_ready controls when we are open for business */
VNET_DEFINE(int, ipfw_vnet_ready) = 0;
VNET_DEFINE(int, ipfw_nat_ready) = 0;
ipfw_nat_t *ipfw_nat_ptr = NULL;
@ -156,6 +168,7 @@ ipfw_nat_cfg_t *ipfw_nat_get_log_ptr;
#ifdef SYSCTL_NODE
uint32_t dummy_def = IPFW_DEFAULT_RULE;
static int sysctl_ipfw_table_num(SYSCTL_HANDLER_ARGS);
static int sysctl_ipfw_tables_sets(SYSCTL_HANDLER_ARGS);
SYSBEGIN(f3)
@ -177,7 +190,10 @@ SYSCTL_UINT(_net_inet_ip_fw, OID_AUTO, default_rule, CTLFLAG_RD,
"The default/max possible rule number.");
SYSCTL_VNET_PROC(_net_inet_ip_fw, OID_AUTO, tables_max,
CTLTYPE_UINT|CTLFLAG_RW, 0, 0, sysctl_ipfw_table_num, "IU",
"Maximum number of tables");
"Maximum number of concurrently used tables");
SYSCTL_VNET_PROC(_net_inet_ip_fw, OID_AUTO, tables_sets,
CTLTYPE_UINT|CTLFLAG_RW, 0, 0, sysctl_ipfw_tables_sets, "IU",
"Use per-set namespace for tables");
SYSCTL_INT(_net_inet_ip_fw, OID_AUTO, default_to_accept, CTLFLAG_RDTUN,
&default_to_accept, 0,
"Make the default rule accept all packets.");
@ -361,8 +377,8 @@ iface_match(struct ifnet *ifp, ipfw_insn_if *cmd, struct ip_fw_chain *chain,
/* Check by name or by IP address */
if (cmd->name[0] != '\0') { /* match by name */
if (cmd->name[0] == '\1') /* use tablearg to match */
return ipfw_lookup_table_extended(chain, cmd->p.glob,
ifp->if_xname, tablearg, IPFW_TABLE_INTERFACE);
return ipfw_lookup_table_extended(chain, cmd->p.kidx, 0,
&ifp->if_index, tablearg);
/* Check name */
if (cmd->p.glob) {
if (fnmatch(cmd->name, ifp->if_xname, 0) == 0)
@ -789,9 +805,10 @@ set_match(struct ip_fw_args *args, int slot,
args->rule.rulenum = chain->map[slot]->rulenum;
}
#ifndef LINEAR_SKIPTO
/*
* Helper function to enable cached rule lookups using
* x_next and next_rule fields in ipfw rule.
* cached_id and cached_pos fields in ipfw rule.
*/
static int
jump_fast(struct ip_fw_chain *chain, struct ip_fw *f, int num,
@ -799,28 +816,51 @@ jump_fast(struct ip_fw_chain *chain, struct ip_fw *f, int num,
{
int f_pos;
/* If possible use cached f_pos (in f->next_rule),
* whose version is written in f->next_rule
/* If possible use cached f_pos (in f->cached_pos),
* whose version is written in f->cached_id
* (horrible hacks to avoid changing the ABI).
*/
if (num != IP_FW_TABLEARG && (uintptr_t)f->x_next == chain->id)
f_pos = (uintptr_t)f->next_rule;
if (num != IP_FW_TARG && f->cached_id == chain->id)
f_pos = f->cached_pos;
else {
int i = IP_FW_ARG_TABLEARG(num);
int i = IP_FW_ARG_TABLEARG(chain, num, skipto);
/* make sure we do not jump backward */
if (jump_backwards == 0 && i <= f->rulenum)
i = f->rulenum + 1;
f_pos = ipfw_find_rule(chain, i, 0);
if (chain->idxmap != NULL)
f_pos = chain->idxmap[i];
else
f_pos = ipfw_find_rule(chain, i, 0);
/* update the cache */
if (num != IP_FW_TABLEARG) {
f->next_rule = (void *)(uintptr_t)f_pos;
f->x_next = (void *)(uintptr_t)chain->id;
if (num != IP_FW_TARG) {
f->cached_id = chain->id;
f->cached_pos = f_pos;
}
}
return (f_pos);
}
#else
/*
* Helper function to enable real fast rule lookups.
*/
static int
jump_linear(struct ip_fw_chain *chain, struct ip_fw *f, int num,
int tablearg, int jump_backwards)
{
int f_pos;
num = IP_FW_ARG_TABLEARG(chain, num, skipto);
/* make sure we do not jump backward */
if (jump_backwards == 0 && num <= f->rulenum)
num = f->rulenum + 1;
f_pos = chain->idxmap[num];
return (f_pos);
}
#endif
#define TARG(k, f) IP_FW_ARG_TABLEARG(chain, k, f)
/*
* The main check routine for the firewall.
*
@ -980,6 +1020,7 @@ ipfw_chk(struct ip_fw_args *args)
int is_ipv4 = 0;
int done = 0; /* flag to exit the outer loop */
IPFW_RLOCK_TRACKER;
if (m->m_flags & M_SKIP_FIREWALL || (! V_ipfw_vnet_ready))
return (IP_FW_PASS); /* accept */
@ -1467,9 +1508,9 @@ do { \
proto != IPPROTO_UDP)
break;
else if (v == 2)
key = htonl(dst_port);
key = dst_port;
else if (v == 3)
key = htonl(src_port);
key = src_port;
#ifndef USERSPACE
else if (v == 4 || v == 5) {
check_uidgid(
@ -1488,7 +1529,6 @@ do { \
else if (v == 5 /* O_JAIL */)
key = ucred_cache.xid;
#endif /* !__FreeBSD__ */
key = htonl(key);
} else
#endif /* !USERSPACE */
break;
@ -1507,8 +1547,9 @@ do { \
void *pkey = (cmd->opcode == O_IP_DST_LOOKUP) ?
&args->f_id.dst_ip6: &args->f_id.src_ip6;
match = ipfw_lookup_table_extended(chain,
cmd->arg1, pkey, &v,
IPFW_TABLE_CIDR);
cmd->arg1,
sizeof(struct in6_addr),
pkey, &v);
if (cmdlen == F_INSN_SIZE(ipfw_insn_u32))
match = ((ipfw_insn_u32 *)cmd)->d[0] == v;
if (match)
@ -1516,6 +1557,17 @@ do { \
}
break;
case O_IP_FLOW_LOOKUP:
{
uint32_t v = 0;
match = ipfw_lookup_table_extended(chain,
cmd->arg1, 0, &args->f_id, &v);
if (cmdlen == F_INSN_SIZE(ipfw_insn_u32))
match = ((ipfw_insn_u32 *)cmd)->d[0] == v;
if (match)
tablearg = v;
}
break;
case O_IP_SRC_MASK:
case O_IP_DST_MASK:
if (is_ipv4) {
@ -1800,7 +1852,7 @@ do { \
}
case O_LOG:
ipfw_log(f, hlen, args, m,
ipfw_log(chain, f, hlen, args, m,
oif, offset | ip6f_mf, tablearg, ip);
match = 1;
break;
@ -1922,7 +1974,7 @@ do { \
case O_TAG: {
struct m_tag *mtag;
uint32_t tag = IP_FW_ARG_TABLEARG(cmd->arg1);
uint32_t tag = TARG(cmd->arg1, tag);
/* Packet is already tagged with this tag? */
mtag = m_tag_locate(m, MTAG_IPFW, tag, NULL);
@ -2003,7 +2055,7 @@ do { \
case O_TAGGED: {
struct m_tag *mtag;
uint32_t tag = IP_FW_ARG_TABLEARG(cmd->arg1);
uint32_t tag = TARG(cmd->arg1, tag);
if (cmdlen == 1) {
match = m_tag_locate(m, MTAG_IPFW,
@ -2074,7 +2126,7 @@ do { \
*/
case O_LIMIT:
case O_KEEP_STATE:
if (ipfw_install_state(f,
if (ipfw_install_state(chain, f,
(ipfw_insn_limit *)cmd, args, tablearg)) {
/* error or limit violation */
retval = IP_FW_DENY;
@ -2141,7 +2193,7 @@ do { \
case O_PIPE:
case O_QUEUE:
set_match(args, f_pos, chain);
args->rule.info = IP_FW_ARG_TABLEARG(cmd->arg1);
args->rule.info = TARG(cmd->arg1, pipe);
if (cmd->opcode == O_PIPE)
args->rule.info |= IPFW_IS_PIPE;
if (V_fw_one_pass)
@ -2161,7 +2213,7 @@ do { \
retval = (cmd->opcode == O_DIVERT) ?
IP_FW_DIVERT : IP_FW_TEE;
set_match(args, f_pos, chain);
args->rule.info = IP_FW_ARG_TABLEARG(cmd->arg1);
args->rule.info = TARG(cmd->arg1, divert);
break;
case O_COUNT:
@ -2171,7 +2223,7 @@ do { \
case O_SKIPTO:
IPFW_INC_RULE_COUNTER(f, pktlen);
f_pos = jump_fast(chain, f, cmd->arg1, tablearg, 0);
f_pos = JUMP(chain, f, cmd->arg1, tablearg, 0);
/*
* Skip disabled rules, and re-enter
* the inner loop with the correct
@ -2260,7 +2312,7 @@ do { \
if (IS_CALL) {
stack[mtag->m_tag_id] = f->rulenum;
mtag->m_tag_id++;
f_pos = jump_fast(chain, f, cmd->arg1,
f_pos = JUMP(chain, f, cmd->arg1,
tablearg, 1);
} else { /* `return' action */
mtag->m_tag_id--;
@ -2368,7 +2420,7 @@ do { \
case O_NETGRAPH:
case O_NGTEE:
set_match(args, f_pos, chain);
args->rule.info = IP_FW_ARG_TABLEARG(cmd->arg1);
args->rule.info = TARG(cmd->arg1, netgraph);
if (V_fw_one_pass)
args->rule.info |= IPFW_ONEPASS;
retval = (cmd->opcode == O_NETGRAPH) ?
@ -2381,7 +2433,7 @@ do { \
uint32_t fib;
IPFW_INC_RULE_COUNTER(f, pktlen);
fib = IP_FW_ARG_TABLEARG(cmd->arg1);
fib = TARG(cmd->arg1, fib) & 0x7FFFF;
if (fib >= rt_numfibs)
fib = 0;
M_SETFIB(m, fib);
@ -2393,7 +2445,7 @@ do { \
case O_SETDSCP: {
uint16_t code;
code = IP_FW_ARG_TABLEARG(cmd->arg1) & 0x3F;
code = TARG(cmd->arg1, dscp) & 0x3F;
l = 0; /* exit inner loop */
if (is_ipv4) {
uint16_t a;
@ -2435,14 +2487,14 @@ do { \
}
t = ((ipfw_insn_nat *)cmd)->nat;
if (t == NULL) {
nat_id = IP_FW_ARG_TABLEARG(cmd->arg1);
nat_id = TARG(cmd->arg1, nat);
t = (*lookup_nat_ptr)(&chain->nat, nat_id);
if (t == NULL) {
retval = IP_FW_DENY;
break;
}
if (cmd->arg1 != IP_FW_TABLEARG)
if (cmd->arg1 != IP_FW_TARG)
((ipfw_insn_nat *)cmd)->nat = t;
}
retval = ipfw_nat_ptr(args, t, m);
@ -2551,6 +2603,25 @@ sysctl_ipfw_table_num(SYSCTL_HANDLER_ARGS)
return (ipfw_resize_tables(&V_layer3_chain, ntables));
}
/*
* Switches table namespace between global and per-set.
*/
static int
sysctl_ipfw_tables_sets(SYSCTL_HANDLER_ARGS)
{
int error;
unsigned int sets;
sets = V_fw_tables_sets;
error = sysctl_handle_int(oidp, &sets, 0, req);
/* Read operation or some error */
if ((error != 0) || (req->newptr == NULL))
return (error);
return (ipfw_switch_tables_namespace(&V_layer3_chain, sets));
}
#endif
/*
@ -2606,7 +2677,9 @@ ipfw_init(void)
if (default_fw_tables > IPFW_TABLES_MAX)
default_fw_tables = IPFW_TABLES_MAX;
ipfw_init_sopt_handler();
ipfw_log_bpf(1); /* init */
ipfw_iface_init();
return (error);
}
@ -2617,7 +2690,9 @@ static void
ipfw_destroy(void)
{
ipfw_iface_destroy();
ipfw_log_bpf(0); /* uninit */
ipfw_destroy_sopt_handler();
printf("IP firewall unloaded\n");
}
@ -2628,12 +2703,14 @@ ipfw_destroy(void)
static int
vnet_ipfw_init(const void *unused)
{
int error;
int error, first;
struct ip_fw *rule = NULL;
struct ip_fw_chain *chain;
chain = &V_layer3_chain;
first = IS_DEFAULT_VNET(curvnet) ? 1 : 0;
/* First set up some values that are compile time options */
V_autoinc_step = 100; /* bounded to 1..1000 in add_rule() */
V_fw_deny_unknown_exthdrs = 1;
@ -2647,16 +2724,15 @@ vnet_ipfw_init(const void *unused)
LIST_INIT(&chain->nat);
#endif
ipfw_init_counters();
/* insert the default rule and create the initial map */
chain->n_rules = 1;
chain->static_len = sizeof(struct ip_fw);
chain->map = malloc(sizeof(struct ip_fw *), M_IPFW, M_WAITOK | M_ZERO);
if (chain->map)
rule = malloc(chain->static_len, M_IPFW, M_WAITOK | M_ZERO);
rule = ipfw_alloc_rule(chain, sizeof(struct ip_fw));
/* Set initial number of tables */
V_fw_tables_max = default_fw_tables;
error = ipfw_init_tables(chain);
error = ipfw_init_tables(chain, first);
if (error) {
printf("ipfw2: setting up tables failed\n");
free(chain->map, M_IPFW);
@ -2673,9 +2749,14 @@ vnet_ipfw_init(const void *unused)
rule->cmd[0].opcode = default_to_accept ? O_ACCEPT : O_DENY;
chain->default_rule = chain->map[0] = rule;
chain->id = rule->id = 1;
/* Pre-calculate rules length for legacy dump format */
chain->static_len = sizeof(struct ip_fw_rule0);
IPFW_LOCK_INIT(chain);
ipfw_dyn_init(chain);
#ifdef LINEAR_SKIPTO
ipfw_init_skipto_cache(chain);
#endif
/* First set up some values that are compile time options */
V_ipfw_vnet_ready = 1; /* Open for business */
@ -2693,7 +2774,7 @@ vnet_ipfw_init(const void *unused)
* In layer2 we have the same behaviour, except that V_ether_ipfw
* is checked on each packet because there are no pfil hooks.
*/
V_ip_fw_ctl_ptr = ipfw_ctl;
V_ip_fw_ctl_ptr = ipfw_ctl3;
error = ipfw_attach_hooks(1);
return (error);
}
@ -2704,9 +2785,9 @@ vnet_ipfw_init(const void *unused)
static int
vnet_ipfw_uninit(const void *unused)
{
struct ip_fw *reap, *rule;
struct ip_fw *reap;
struct ip_fw_chain *chain = &V_layer3_chain;
int i;
int i, last;
V_ipfw_vnet_ready = 0; /* tell new callers to go away */
/*
@ -2716,6 +2797,9 @@ vnet_ipfw_uninit(const void *unused)
*/
(void)ipfw_attach_hooks(0 /* detach */);
V_ip_fw_ctl_ptr = NULL;
last = IS_DEFAULT_VNET(curvnet) ? 1 : 0;
IPFW_UH_WLOCK(chain);
IPFW_UH_WUNLOCK(chain);
IPFW_UH_WLOCK(chain);
@ -2724,22 +2808,23 @@ vnet_ipfw_uninit(const void *unused)
ipfw_dyn_uninit(0); /* run the callout_drain */
IPFW_WUNLOCK(chain);
ipfw_destroy_tables(chain);
reap = NULL;
IPFW_WLOCK(chain);
for (i = 0; i < chain->n_rules; i++) {
rule = chain->map[i];
rule->x_next = reap;
reap = rule;
}
if (chain->map)
free(chain->map, M_IPFW);
for (i = 0; i < chain->n_rules; i++)
ipfw_reap_add(chain, &reap, chain->map[i]);
free(chain->map, M_IPFW);
#ifdef LINEAR_SKIPTO
ipfw_destroy_skipto_cache(chain);
#endif
IPFW_WUNLOCK(chain);
IPFW_UH_WUNLOCK(chain);
ipfw_destroy_tables(chain, last);
if (reap != NULL)
ipfw_reap_rules(reap);
vnet_ipfw_iface_destroy(chain);
IPFW_LOCK_DESTROY(chain);
ipfw_dyn_uninit(1); /* free the remaining parts */
ipfw_destroy_counters();
return (0);
}
@ -2791,7 +2876,8 @@ static moduledata_t ipfwmod = {
#define IPFW_VNET_ORDER (IPFW_MODEVENT_ORDER + 2) /* Later still. */
DECLARE_MODULE(ipfw, ipfwmod, IPFW_SI_SUB_FIREWALL, IPFW_MODEVENT_ORDER);
MODULE_VERSION(ipfw, 2);
FEATURE(ipfw_ctl3, "ipfw new sockopt calls");
MODULE_VERSION(ipfw, 3);
/* should declare some dependencies here */
/*

View File

@ -47,6 +47,7 @@ __FBSDID("$FreeBSD$");
#include <sys/kernel.h>
#include <sys/ktr.h>
#include <sys/lock.h>
#include <sys/rmlock.h>
#include <sys/socket.h>
#include <sys/sysctl.h>
#include <sys/syslog.h>
@ -196,8 +197,7 @@ static int ipfw_dyn_count; /* number of objects */
static int last_log; /* Log ratelimiting */
static void ipfw_dyn_tick(void *vnetx);
static void check_dyn_rules(struct ip_fw_chain *, struct ip_fw *,
int, int, int);
static void check_dyn_rules(struct ip_fw_chain *, ipfw_range_tlv *, int, int);
#ifdef SYSCTL_NODE
static int sysctl_ipfw_dyn_count(SYSCTL_HANDLER_ARGS);
@ -673,8 +673,8 @@ lookup_dyn_parent(struct ipfw_flow_id *pkt, int *pindex, struct ip_fw *rule)
* session limitations are enforced.
*/
int
ipfw_install_state(struct ip_fw *rule, ipfw_insn_limit *cmd,
struct ip_fw_args *args, uint32_t tablearg)
ipfw_install_state(struct ip_fw_chain *chain, struct ip_fw *rule,
ipfw_insn_limit *cmd, struct ip_fw_args *args, uint32_t tablearg)
{
ipfw_dyn_rule *q;
int i;
@ -717,10 +717,10 @@ ipfw_install_state(struct ip_fw *rule, ipfw_insn_limit *cmd,
uint16_t limit_mask = cmd->limit_mask;
int pindex;
conn_limit = IP_FW_ARG_TABLEARG(cmd->conn_limit);
conn_limit = IP_FW_ARG_TABLEARG(chain, cmd->conn_limit, limit);
DEB(
if (cmd->conn_limit == IP_FW_TABLEARG)
if (cmd->conn_limit == IP_FW_TARG)
printf("ipfw: %s: O_LIMIT rule, conn_limit: %u "
"(tablearg)\n", __func__, conn_limit);
else
@ -1008,7 +1008,7 @@ ipfw_dyn_tick(void * vnetx)
check_ka = 1;
}
check_dyn_rules(chain, NULL, RESVD_SET, check_ka, 1);
check_dyn_rules(chain, NULL, check_ka, 1);
callout_reset_on(&V_ipfw_timeout, hz, ipfw_dyn_tick, vnetx, 0);
@ -1040,8 +1040,8 @@ ipfw_dyn_tick(void * vnetx)
* are not freed by other instance (see stage 2, 3)
*/
static void
check_dyn_rules(struct ip_fw_chain *chain, struct ip_fw *rule,
int set, int check_ka, int timer)
check_dyn_rules(struct ip_fw_chain *chain, ipfw_range_tlv *rt,
int check_ka, int timer)
{
struct mbuf *m0, *m, *mnext, **mtailp;
struct ip *h;
@ -1105,12 +1105,10 @@ check_dyn_rules(struct ip_fw_chain *chain, struct ip_fw *rule,
/*
* Remove rules which are:
* 1) expired
* 2) created by given rule
* 3) created by any rule in given set
* 2) matches deletion range
*/
if ((TIME_LEQ(q->expire, time_uptime)) ||
((rule != NULL) && (q->rule == rule)) ||
((set != RESVD_SET) && (q->rule->set == set))) {
(rt != NULL && ipfw_match_range(q->rule, rt))) {
if (TIME_LE(time_uptime, q->expire) &&
q->dyn_type == O_KEEP_STATE &&
V_dyn_keep_states != 0) {
@ -1324,8 +1322,7 @@ check_dyn_rules(struct ip_fw_chain *chain, struct ip_fw *rule,
* Deletes all dynamic rules originated by given rule or all rules in
* given set. Specify RESVD_SET to indicate set should not be used.
* @chain - pointer to current ipfw rules chain
* @rule - delete all states originated by given rule if != NULL
* @set - delete all states originated by any rule in set @set if != RESVD_SET
* @rr - delete all states originated by rules in matched range.
*
* Function has to be called with IPFW_UH_WLOCK held.
* Additionally, function assume that dynamic rule/set is
@ -1333,10 +1330,39 @@ check_dyn_rules(struct ip_fw_chain *chain, struct ip_fw *rule,
* 'deleted' rules.
*/
void
ipfw_expire_dyn_rules(struct ip_fw_chain *chain, struct ip_fw *rule, int set)
ipfw_expire_dyn_rules(struct ip_fw_chain *chain, ipfw_range_tlv *rt)
{
check_dyn_rules(chain, rule, set, 0, 0);
check_dyn_rules(chain, rt, 0, 0);
}
/*
* Check if rule contains at least one dynamic opcode.
*
* Returns 1 if such opcode is found, 0 otherwise.
*/
int
ipfw_is_dyn_rule(struct ip_fw *rule)
{
int cmdlen, l;
ipfw_insn *cmd;
l = rule->cmd_len;
cmd = rule->cmd;
cmdlen = 0;
for ( ; l > 0 ; l -= cmdlen, cmd += cmdlen) {
cmdlen = F_LEN(cmd);
switch (cmd->opcode) {
case O_LIMIT:
case O_KEEP_STATE:
case O_PROBE_STATE:
case O_CHECK_STATE:
return (1);
}
}
return (0);
}
void
@ -1444,7 +1470,7 @@ sysctl_ipfw_dyn_count(SYSCTL_HANDLER_ARGS)
#endif
/*
* Returns number of dynamic rules.
* Returns size of dynamic states in legacy format
*/
int
ipfw_dyn_len(void)
@ -1455,7 +1481,92 @@ ipfw_dyn_len(void)
}
/*
* Fill given buffer with dynamic states.
* Returns number of dynamic states.
* Used by dump format v1 (current).
*/
int
ipfw_dyn_get_count(void)
{
return (V_ipfw_dyn_v == NULL) ? 0 : DYN_COUNT;
}
static void
export_dyn_rule(ipfw_dyn_rule *src, ipfw_dyn_rule *dst)
{
memcpy(dst, src, sizeof(*src));
memcpy(&(dst->rule), &(src->rule->rulenum), sizeof(src->rule->rulenum));
/*
* store set number into high word of
* dst->rule pointer.
*/
memcpy((char *)&dst->rule + sizeof(src->rule->rulenum),
&(src->rule->set), sizeof(src->rule->set));
/*
* store a non-null value in "next".
* The userland code will interpret a
* NULL here as a marker
* for the last dynamic rule.
*/
memcpy(&dst->next, &dst, sizeof(dst));
dst->expire =
TIME_LEQ(dst->expire, time_uptime) ? 0 : dst->expire - time_uptime;
}
/*
* Fills int buffer given by @sd with dynamic states.
* Used by dump format v1 (current).
*
* Returns 0 on success.
*/
int
ipfw_dump_states(struct ip_fw_chain *chain, struct sockopt_data *sd)
{
ipfw_dyn_rule *p;
ipfw_obj_dyntlv *dst, *last;
ipfw_obj_ctlv *ctlv;
int i;
size_t sz;
if (V_ipfw_dyn_v == NULL)
return (0);
IPFW_UH_RLOCK_ASSERT(chain);
ctlv = (ipfw_obj_ctlv *)ipfw_get_sopt_space(sd, sizeof(*ctlv));
if (ctlv == NULL)
return (ENOMEM);
sz = sizeof(ipfw_obj_dyntlv);
ctlv->head.type = IPFW_TLV_DYNSTATE_LIST;
ctlv->objsize = sz;
last = NULL;
for (i = 0 ; i < V_curr_dyn_buckets; i++) {
IPFW_BUCK_LOCK(i);
for (p = V_ipfw_dyn_v[i].head ; p != NULL; p = p->next) {
dst = (ipfw_obj_dyntlv *)ipfw_get_sopt_space(sd, sz);
if (dst == NULL) {
IPFW_BUCK_UNLOCK(i);
return (ENOMEM);
}
export_dyn_rule(p, &dst->state);
dst->head.length = sz;
dst->head.type = IPFW_TLV_DYN_ENT;
last = dst;
}
IPFW_BUCK_UNLOCK(i);
}
if (last != NULL) /* mark last dynamic rule */
last->head.flags = IPFW_DF_LAST;
return (0);
}
/*
* Fill given buffer with dynamic states (legacy format).
* IPFW_UH_RLOCK has to be held while calling.
*/
void
@ -1477,28 +1588,9 @@ ipfw_get_dynamic(struct ip_fw_chain *chain, char **pbp, const char *ep)
if (bp + sizeof *p <= ep) {
ipfw_dyn_rule *dst =
(ipfw_dyn_rule *)bp;
bcopy(p, dst, sizeof *p);
bcopy(&(p->rule->rulenum), &(dst->rule),
sizeof(p->rule->rulenum));
/*
* store set number into high word of
* dst->rule pointer.
*/
bcopy(&(p->rule->set),
(char *)&dst->rule +
sizeof(p->rule->rulenum),
sizeof(p->rule->set));
/*
* store a non-null value in "next".
* The userland code will interpret a
* NULL here as a marker
* for the last dynamic rule.
*/
bcopy(&dst, &dst->next, sizeof(dst));
export_dyn_rule(p, dst);
last = dst;
dst->expire =
TIME_LEQ(dst->expire, time_uptime) ?
0 : dst->expire - time_uptime ;
bp += sizeof(ipfw_dyn_rule);
}
}

View File

@ -0,0 +1,537 @@
/*-
* Copyright (c) 2014 Yandex LLC.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD: projects/ipfw/sys/netpfil/ipfw/ip_fw_iface.c 267384 2014-06-12 09:59:11Z melifaro $");
/*
* Kernel interface tracking API.
*
*/
#include "opt_ipfw.h"
#include "opt_inet.h"
#ifndef INET
#error IPFIREWALL requires INET.
#endif /* INET */
#include "opt_inet6.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/malloc.h>
#include <sys/kernel.h>
#include <sys/lock.h>
#include <sys/rwlock.h>
#include <sys/rmlock.h>
#include <sys/socket.h>
#include <sys/queue.h>
#include <sys/eventhandler.h>
#include <net/if.h>
#include <net/if_var.h>
#include <net/vnet.h>
#include <netinet/in.h>
#include <netinet/ip_var.h> /* struct ipfw_rule_ref */
#include <netinet/ip_fw.h>
#include <netpfil/ipfw/ip_fw_private.h>
#define CHAIN_TO_II(ch) ((struct namedobj_instance *)ch->ifcfg)
#define DEFAULT_IFACES 128
static void handle_ifdetach(struct ip_fw_chain *ch, struct ipfw_iface *iif,
uint16_t ifindex);
static void handle_ifattach(struct ip_fw_chain *ch, struct ipfw_iface *iif,
uint16_t ifindex);
static int list_ifaces(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
struct sockopt_data *sd);
static struct ipfw_sopt_handler scodes[] = {
{ IP_FW_XIFLIST, 0, HDIR_GET, list_ifaces },
};
/*
* FreeBSD Kernel interface.
*/
static void ipfw_kifhandler(void *arg, struct ifnet *ifp);
static int ipfw_kiflookup(char *name);
static void iface_khandler_register(void);
static void iface_khandler_deregister(void);
static eventhandler_tag ipfw_ifdetach_event, ipfw_ifattach_event;
static int num_vnets = 0;
static struct mtx vnet_mtx;
/*
* Checks if kernel interface is contained in our tracked
* interface list and calls attach/detach handler.
*/
static void
ipfw_kifhandler(void *arg, struct ifnet *ifp)
{
struct ip_fw_chain *ch;
struct ipfw_iface *iif;
struct namedobj_instance *ii;
uintptr_t htype;
if (V_ipfw_vnet_ready == 0)
return;
ch = &V_layer3_chain;
htype = (uintptr_t)arg;
IPFW_UH_WLOCK(ch);
ii = CHAIN_TO_II(ch);
if (ii == NULL) {
IPFW_UH_WUNLOCK(ch);
return;
}
iif = (struct ipfw_iface*)ipfw_objhash_lookup_name(ii, 0,
if_name(ifp));
if (iif != NULL) {
if (htype == 1)
handle_ifattach(ch, iif, ifp->if_index);
else
handle_ifdetach(ch, iif, ifp->if_index);
}
IPFW_UH_WUNLOCK(ch);
}
/*
* Reference current VNET as iface tracking API user.
* Registers interface tracking handlers for first VNET.
*/
static void
iface_khandler_register()
{
int create;
create = 0;
mtx_lock(&vnet_mtx);
if (num_vnets == 0)
create = 1;
num_vnets++;
mtx_unlock(&vnet_mtx);
if (create == 0)
return;
printf("IPFW: starting up interface tracker\n");
ipfw_ifdetach_event = EVENTHANDLER_REGISTER(
ifnet_departure_event, ipfw_kifhandler, NULL,
EVENTHANDLER_PRI_ANY);
ipfw_ifattach_event = EVENTHANDLER_REGISTER(
ifnet_arrival_event, ipfw_kifhandler, (void*)((uintptr_t)1),
EVENTHANDLER_PRI_ANY);
}
/*
*
* Detach interface event handlers on last VNET instance
* detach.
*/
static void
iface_khandler_deregister()
{
int destroy;
destroy = 0;
mtx_lock(&vnet_mtx);
if (num_vnets == 1)
destroy = 1;
num_vnets--;
mtx_unlock(&vnet_mtx);
if (destroy == 0)
return;
EVENTHANDLER_DEREGISTER(ifnet_arrival_event,
ipfw_ifattach_event);
EVENTHANDLER_DEREGISTER(ifnet_departure_event,
ipfw_ifdetach_event);
}
/*
* Retrieves ifindex for given @name.
*
* Returns ifindex or 0.
*/
static int
ipfw_kiflookup(char *name)
{
struct ifnet *ifp;
int ifindex;
ifindex = 0;
if ((ifp = ifunit_ref(name)) != NULL) {
ifindex = ifp->if_index;
if_rele(ifp);
}
return (ifindex);
}
/*
* Global ipfw startup hook.
* Since we perform lazy initialization, do nothing except
* mutex init.
*/
int
ipfw_iface_init()
{
mtx_init(&vnet_mtx, "IPFW ifhandler mtx", NULL, MTX_DEF);
IPFW_ADD_SOPT_HANDLER(1, scodes);
return (0);
}
/*
* Global ipfw destroy hook.
* Unregister khandlers iff init has been done.
*/
void
ipfw_iface_destroy()
{
IPFW_DEL_SOPT_HANDLER(1, scodes);
mtx_destroy(&vnet_mtx);
}
/*
* Perform actual init on internal request.
* Inits both namehash and global khandler.
*/
static void
vnet_ipfw_iface_init(struct ip_fw_chain *ch)
{
struct namedobj_instance *ii;
ii = ipfw_objhash_create(DEFAULT_IFACES);
IPFW_UH_WLOCK(ch);
if (ch->ifcfg == NULL) {
ch->ifcfg = ii;
ii = NULL;
}
IPFW_UH_WUNLOCK(ch);
if (ii != NULL) {
/* Already initialized. Free namehash. */
ipfw_objhash_destroy(ii);
} else {
/* We're the first ones. Init kernel hooks. */
iface_khandler_register();
}
}
static void
destroy_iface(struct namedobj_instance *ii, struct named_object *no,
void *arg)
{
/* Assume all consumers have been already detached */
free(no, M_IPFW);
}
/*
* Per-VNET ipfw detach hook.
*
*/
void
vnet_ipfw_iface_destroy(struct ip_fw_chain *ch)
{
struct namedobj_instance *ii;
IPFW_UH_WLOCK(ch);
ii = CHAIN_TO_II(ch);
ch->ifcfg = NULL;
IPFW_UH_WUNLOCK(ch);
if (ii != NULL) {
ipfw_objhash_foreach(ii, destroy_iface, ch);
ipfw_objhash_destroy(ii);
iface_khandler_deregister();
}
}
/*
* Notify the subsystem that we are interested in tracking
* interface @name. This function has to be called without
* holding any locks to permit allocating the necessary states
* for proper interface tracking.
*
* Returns 0 on success.
*/
int
ipfw_iface_ref(struct ip_fw_chain *ch, char *name,
struct ipfw_ifc *ic)
{
struct namedobj_instance *ii;
struct ipfw_iface *iif, *tmp;
if (strlen(name) >= sizeof(iif->ifname))
return (EINVAL);
IPFW_UH_WLOCK(ch);
ii = CHAIN_TO_II(ch);
if (ii == NULL) {
/*
* First request to subsystem.
* Let's perform init.
*/
IPFW_UH_WUNLOCK(ch);
vnet_ipfw_iface_init(ch);
IPFW_UH_WLOCK(ch);
ii = CHAIN_TO_II(ch);
}
iif = (struct ipfw_iface *)ipfw_objhash_lookup_name(ii, 0, name);
if (iif != NULL) {
iif->no.refcnt++;
ic->iface = iif;
IPFW_UH_WUNLOCK(ch);
return (0);
}
IPFW_UH_WUNLOCK(ch);
/* Not found. Let's create one */
iif = malloc(sizeof(struct ipfw_iface), M_IPFW, M_WAITOK | M_ZERO);
TAILQ_INIT(&iif->consumers);
iif->no.name = iif->ifname;
strlcpy(iif->ifname, name, sizeof(iif->ifname));
/*
* Ref & link to the list.
*
* We assume ifnet_arrival_event / ifnet_departure_event
* are not holding any locks.
*/
iif->no.refcnt = 1;
IPFW_UH_WLOCK(ch);
tmp = (struct ipfw_iface *)ipfw_objhash_lookup_name(ii, 0, name);
if (tmp != NULL) {
/* Interface has been created since unlock. Ref and return */
tmp->no.refcnt++;
ic->iface = tmp;
IPFW_UH_WUNLOCK(ch);
free(iif, M_IPFW);
return (0);
}
iif->ifindex = ipfw_kiflookup(name);
if (iif->ifindex != 0)
iif->resolved = 1;
ipfw_objhash_add(ii, &iif->no);
ic->iface = iif;
IPFW_UH_WUNLOCK(ch);
return (0);
}
/*
* Adds @ic to the list of iif interface consumers.
* Must be called with holding both UH+WLOCK.
* Callback may be immediately called (if interface exists).
*/
void
ipfw_iface_add_notify(struct ip_fw_chain *ch, struct ipfw_ifc *ic)
{
struct ipfw_iface *iif;
IPFW_UH_WLOCK_ASSERT(ch);
IPFW_WLOCK_ASSERT(ch);
iif = ic->iface;
TAILQ_INSERT_TAIL(&iif->consumers, ic, next);
if (iif->resolved != 0)
ic->cb(ch, ic->cbdata, iif->ifindex);
}
/*
* Unlinks interface tracker object @ic from interface.
* Must be called while holding UH lock.
*/
void
ipfw_iface_del_notify(struct ip_fw_chain *ch, struct ipfw_ifc *ic)
{
struct ipfw_iface *iif;
IPFW_UH_WLOCK_ASSERT(ch);
iif = ic->iface;
TAILQ_REMOVE(&iif->consumers, ic, next);
}
/*
* Unreference interface specified by @ic.
* Must be called without holding any locks.
*/
void
ipfw_iface_unref(struct ip_fw_chain *ch, struct ipfw_ifc *ic)
{
struct ipfw_iface *iif;
iif = ic->iface;
ic->iface = NULL;
IPFW_UH_WLOCK(ch);
iif->no.refcnt--;
/* TODO: check for references & delete */
IPFW_UH_WUNLOCK(ch);
}
/*
* Interface arrival handler.
*/
static void
handle_ifattach(struct ip_fw_chain *ch, struct ipfw_iface *iif,
uint16_t ifindex)
{
struct ipfw_ifc *ic;
IPFW_UH_WLOCK_ASSERT(ch);
iif->gencnt++;
iif->resolved = 1;
iif->ifindex = ifindex;
IPFW_WLOCK(ch);
TAILQ_FOREACH(ic, &iif->consumers, next)
ic->cb(ch, ic->cbdata, iif->ifindex);
IPFW_WUNLOCK(ch);
}
/*
* Interface departure handler.
*/
static void
handle_ifdetach(struct ip_fw_chain *ch, struct ipfw_iface *iif,
uint16_t ifindex)
{
struct ipfw_ifc *ic;
IPFW_UH_WLOCK_ASSERT(ch);
IPFW_WLOCK(ch);
TAILQ_FOREACH(ic, &iif->consumers, next)
ic->cb(ch, ic->cbdata, 0);
IPFW_WUNLOCK(ch);
iif->gencnt++;
iif->resolved = 0;
iif->ifindex = 0;
}
struct dump_iface_args {
struct ip_fw_chain *ch;
struct sockopt_data *sd;
};
static void
export_iface_internal(struct namedobj_instance *ii, struct named_object *no,
void *arg)
{
ipfw_iface_info *i;
struct dump_iface_args *da;
struct ipfw_iface *iif;
da = (struct dump_iface_args *)arg;
i = (ipfw_iface_info *)ipfw_get_sopt_space(da->sd, sizeof(*i));
KASSERT(i != 0, ("previously checked buffer is not enough"));
iif = (struct ipfw_iface *)no;
strlcpy(i->ifname, iif->ifname, sizeof(i->ifname));
if (iif->resolved)
i->flags |= IPFW_IFFLAG_RESOLVED;
i->ifindex = iif->ifindex;
i->refcnt = iif->no.refcnt;
i->gencnt = iif->gencnt;
}
/*
* Lists all interface currently tracked by ipfw.
* Data layout (v0)(current):
* Request: [ ipfw_obj_lheader ], size = ipfw_obj_lheader.size
* Reply: [ ipfw_obj_lheader ipfw_iface_info x N ]
*
* Returns 0 on success
*/
static int
list_ifaces(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
struct sockopt_data *sd)
{
struct namedobj_instance *ii;
struct _ipfw_obj_lheader *olh;
struct dump_iface_args da;
uint32_t count, size;
olh = (struct _ipfw_obj_lheader *)ipfw_get_sopt_header(sd,sizeof(*olh));
if (olh == NULL)
return (EINVAL);
if (sd->valsize < olh->size)
return (EINVAL);
IPFW_UH_RLOCK(ch);
ii = CHAIN_TO_II(ch);
if (ii != NULL)
count = ipfw_objhash_count(ii);
else
count = 0;
size = count * sizeof(ipfw_iface_info) + sizeof(ipfw_obj_lheader);
/* Fill in header regadless of buffer size */
olh->count = count;
olh->objsize = sizeof(ipfw_iface_info);
if (size > olh->size) {
olh->size = size;
IPFW_UH_RUNLOCK(ch);
return (ENOMEM);
}
olh->size = size;
da.ch = ch;
da.sd = sd;
if (ii != NULL)
ipfw_objhash_foreach(ii, export_iface_internal, &da);
IPFW_UH_RUNLOCK(ch);
return (0);
}

View File

@ -240,14 +240,15 @@ ipfw_log_bpf(int onoff)
}
#endif /* !WITHOUT_BPF */
#define TARG(k, f) IP_FW_ARG_TABLEARG(chain, k, f)
/*
* We enter here when we have a rule with O_LOG.
* XXX this function alone takes about 2Kbytes of code!
*/
void
ipfw_log(struct ip_fw *f, u_int hlen, struct ip_fw_args *args,
struct mbuf *m, struct ifnet *oif, u_short offset, uint32_t tablearg,
struct ip *ip)
ipfw_log(struct ip_fw_chain *chain, struct ip_fw *f, u_int hlen,
struct ip_fw_args *args, struct mbuf *m, struct ifnet *oif,
u_short offset, uint32_t tablearg, struct ip *ip)
{
char *action;
int limit_reached = 0;
@ -343,27 +344,27 @@ ipfw_log(struct ip_fw *f, u_int hlen, struct ip_fw_args *args,
break;
case O_DIVERT:
snprintf(SNPARGS(action2, 0), "Divert %d",
cmd->arg1);
TARG(cmd->arg1, divert));
break;
case O_TEE:
snprintf(SNPARGS(action2, 0), "Tee %d",
cmd->arg1);
TARG(cmd->arg1, divert));
break;
case O_SETFIB:
snprintf(SNPARGS(action2, 0), "SetFib %d",
IP_FW_ARG_TABLEARG(cmd->arg1));
TARG(cmd->arg1, fib));
break;
case O_SKIPTO:
snprintf(SNPARGS(action2, 0), "SkipTo %d",
IP_FW_ARG_TABLEARG(cmd->arg1));
TARG(cmd->arg1, skipto));
break;
case O_PIPE:
snprintf(SNPARGS(action2, 0), "Pipe %d",
IP_FW_ARG_TABLEARG(cmd->arg1));
TARG(cmd->arg1, pipe));
break;
case O_QUEUE:
snprintf(SNPARGS(action2, 0), "Queue %d",
IP_FW_ARG_TABLEARG(cmd->arg1));
TARG(cmd->arg1, pipe));
break;
case O_FORWARD_IP: {
ipfw_insn_sa *sa = (ipfw_insn_sa *)cmd;

View File

@ -36,8 +36,7 @@ __FBSDID("$FreeBSD$");
#include <sys/lock.h>
#include <sys/module.h>
#include <sys/rwlock.h>
#define IPFW_INTERNAL /* Access to protected data structures in ip_fw.h. */
#include <sys/rmlock.h>
#include <netinet/libalias/alias.h>
#include <netinet/libalias/alias_local.h>
@ -55,6 +54,45 @@ __FBSDID("$FreeBSD$");
#include <machine/in_cksum.h> /* XXX for in_cksum */
struct cfg_spool {
LIST_ENTRY(cfg_spool) _next; /* chain of spool instances */
struct in_addr addr;
uint16_t port;
};
/* Nat redirect configuration. */
struct cfg_redir {
LIST_ENTRY(cfg_redir) _next; /* chain of redir instances */
uint16_t mode; /* type of redirect mode */
uint16_t proto; /* protocol: tcp/udp */
struct in_addr laddr; /* local ip address */
struct in_addr paddr; /* public ip address */
struct in_addr raddr; /* remote ip address */
uint16_t lport; /* local port */
uint16_t pport; /* public port */
uint16_t rport; /* remote port */
uint16_t pport_cnt; /* number of public ports */
uint16_t rport_cnt; /* number of remote ports */
struct alias_link **alink;
u_int16_t spool_cnt; /* num of entry in spool chain */
/* chain of spool instances */
LIST_HEAD(spool_chain, cfg_spool) spool_chain;
};
/* Nat configuration data struct. */
struct cfg_nat {
/* chain of nat instances */
LIST_ENTRY(cfg_nat) _next;
int id; /* nat id */
struct in_addr ip; /* nat ip address */
struct libalias *lib; /* libalias instance */
int mode; /* aliasing mode */
int redir_cnt; /* number of entry in spool chain */
/* chain of redir instances */
LIST_HEAD(redir_chain, cfg_redir) redir_chain;
char if_name[IF_NAMESIZE]; /* interface name */
};
static eventhandler_tag ifaddr_event_tag;
static void
@ -117,11 +155,11 @@ del_redir_spool_cfg(struct cfg_nat *n, struct redir_chain *head)
LIST_FOREACH_SAFE(r, head, _next, tmp_r) {
num = 1; /* Number of alias_link to delete. */
switch (r->mode) {
case REDIR_PORT:
case NAT44_REDIR_PORT:
num = r->pport_cnt;
/* FALLTHROUGH */
case REDIR_ADDR:
case REDIR_PROTO:
case NAT44_REDIR_ADDR:
case NAT44_REDIR_PROTO:
/* Delete all libalias redirect entry. */
for (i = 0; i < num; i++)
LibAliasRedirectDelete(n->lib, r->alink[i]);
@ -142,27 +180,41 @@ del_redir_spool_cfg(struct cfg_nat *n, struct redir_chain *head)
}
}
static void
static int
add_redir_spool_cfg(char *buf, struct cfg_nat *ptr)
{
struct cfg_redir *r, *ser_r;
struct cfg_spool *s, *ser_s;
struct cfg_redir *r;
struct cfg_spool *s;
struct nat44_cfg_redir *ser_r;
struct nat44_cfg_spool *ser_s;
int cnt, off, i;
for (cnt = 0, off = 0; cnt < ptr->redir_cnt; cnt++) {
ser_r = (struct cfg_redir *)&buf[off];
r = malloc(SOF_REDIR, M_IPFW, M_WAITOK | M_ZERO);
memcpy(r, ser_r, SOF_REDIR);
ser_r = (struct nat44_cfg_redir *)&buf[off];
r = malloc(sizeof(*r), M_IPFW, M_WAITOK | M_ZERO);
r->mode = ser_r->mode;
r->laddr = ser_r->laddr;
r->paddr = ser_r->paddr;
r->raddr = ser_r->raddr;
r->lport = ser_r->lport;
r->pport = ser_r->pport;
r->rport = ser_r->rport;
r->pport_cnt = ser_r->pport_cnt;
r->rport_cnt = ser_r->rport_cnt;
r->proto = ser_r->proto;
r->spool_cnt = ser_r->spool_cnt;
//memcpy(r, ser_r, SOF_REDIR);
LIST_INIT(&r->spool_chain);
off += SOF_REDIR;
off += sizeof(struct nat44_cfg_redir);
r->alink = malloc(sizeof(struct alias_link *) * r->pport_cnt,
M_IPFW, M_WAITOK | M_ZERO);
switch (r->mode) {
case REDIR_ADDR:
case NAT44_REDIR_ADDR:
r->alink[0] = LibAliasRedirectAddr(ptr->lib, r->laddr,
r->paddr);
break;
case REDIR_PORT:
case NAT44_REDIR_PORT:
for (i = 0 ; i < r->pport_cnt; i++) {
/* If remotePort is all ports, set it to 0. */
u_short remotePortCopy = r->rport + i;
@ -178,7 +230,7 @@ add_redir_spool_cfg(char *buf, struct cfg_nat *ptr)
}
}
break;
case REDIR_PROTO:
case NAT44_REDIR_PROTO:
r->alink[0] = LibAliasRedirectProto(ptr->lib ,r->laddr,
r->raddr, r->paddr, r->proto);
break;
@ -186,23 +238,27 @@ add_redir_spool_cfg(char *buf, struct cfg_nat *ptr)
printf("unknown redirect mode: %u\n", r->mode);
break;
}
/* XXX perhaps return an error instead of panic ? */
if (r->alink[0] == NULL)
panic("LibAliasRedirect* returned NULL");
if (r->alink[0] == NULL) {
printf("LibAliasRedirect* returned NULL\n");
return (EINVAL);
}
/* LSNAT handling. */
for (i = 0; i < r->spool_cnt; i++) {
ser_s = (struct cfg_spool *)&buf[off];
s = malloc(SOF_REDIR, M_IPFW, M_WAITOK | M_ZERO);
memcpy(s, ser_s, SOF_SPOOL);
ser_s = (struct nat44_cfg_spool *)&buf[off];
s = malloc(sizeof(*s), M_IPFW, M_WAITOK | M_ZERO);
s->addr = ser_s->addr;
s->port = ser_s->port;
LibAliasAddServer(ptr->lib, r->alink[0],
s->addr, htons(s->port));
off += SOF_SPOOL;
off += sizeof(struct nat44_cfg_spool);
/* Hook spool entry. */
LIST_INSERT_HEAD(&r->spool_chain, s, _next);
}
/* And finally hook this redir entry. */
LIST_INSERT_HEAD(&ptr->redir_chain, r, _next);
}
return (0);
}
/*
@ -392,60 +448,68 @@ lookup_nat(struct nat_list *l, int nat_id)
return res;
}
static int
ipfw_nat_cfg(struct sockopt *sopt)
static struct cfg_nat *
lookup_nat_name(struct nat_list *l, char *name)
{
struct cfg_nat *cfg, *ptr;
char *buf;
struct ip_fw_chain *chain = &V_layer3_chain;
size_t len;
int gencnt, error = 0;
struct cfg_nat *res;
int id;
char *errptr;
len = sopt->sopt_valsize;
buf = malloc(len, M_TEMP, M_WAITOK | M_ZERO);
if ((error = sooptcopyin(sopt, buf, len, sizeof(struct cfg_nat))) != 0)
goto out;
id = strtol(name, &errptr, 10);
if (id == 0 || *errptr != '\0')
return (NULL);
cfg = (struct cfg_nat *)buf;
if (cfg->id < 0) {
error = EINVAL;
goto out;
LIST_FOREACH(res, l, _next) {
if (res->id == id)
break;
}
return (res);
}
/* IP_FW3 configuration routines */
static void
nat44_config(struct ip_fw_chain *chain, struct nat44_cfg_nat *ucfg)
{
struct cfg_nat *ptr, *tcfg;
int gencnt;
/*
* Find/create nat rule.
*/
IPFW_WLOCK(chain);
IPFW_UH_WLOCK(chain);
gencnt = chain->gencnt;
ptr = lookup_nat(&chain->nat, cfg->id);
ptr = lookup_nat_name(&chain->nat, ucfg->name);
if (ptr == NULL) {
IPFW_WUNLOCK(chain);
IPFW_UH_WUNLOCK(chain);
/* New rule: allocate and init new instance. */
ptr = malloc(sizeof(struct cfg_nat), M_IPFW, M_WAITOK | M_ZERO);
ptr->lib = LibAliasInit(NULL);
LIST_INIT(&ptr->redir_chain);
} else {
/* Entry already present: temporarily unhook it. */
IPFW_WLOCK(chain);
LIST_REMOVE(ptr, _next);
flush_nat_ptrs(chain, cfg->id);
flush_nat_ptrs(chain, ptr->id);
IPFW_WUNLOCK(chain);
IPFW_UH_WUNLOCK(chain);
}
/*
* Basic nat configuration.
* Basic nat (re)configuration.
*/
ptr->id = cfg->id;
ptr->id = strtol(ucfg->name, NULL, 10);
/*
* XXX - what if this rule doesn't nat any ip and just
* redirect?
* do we set aliasaddress to 0.0.0.0?
*/
ptr->ip = cfg->ip;
ptr->redir_cnt = cfg->redir_cnt;
ptr->mode = cfg->mode;
LibAliasSetMode(ptr->lib, cfg->mode, ~0);
ptr->ip = ucfg->ip;
ptr->redir_cnt = ucfg->redir_cnt;
ptr->mode = ucfg->mode;
strlcpy(ptr->if_name, ucfg->if_name, sizeof(ptr->if_name));
LibAliasSetMode(ptr->lib, ptr->mode, ~0);
LibAliasSetAddress(ptr->lib, ptr->ip);
memcpy(ptr->if_name, cfg->if_name, IF_NAMESIZE);
/*
* Redir and LSNAT configuration.
@ -453,16 +517,455 @@ ipfw_nat_cfg(struct sockopt *sopt)
/* Delete old cfgs. */
del_redir_spool_cfg(ptr, &ptr->redir_chain);
/* Add new entries. */
add_redir_spool_cfg(&buf[(sizeof(struct cfg_nat))], ptr);
add_redir_spool_cfg((char *)(ucfg + 1), ptr);
IPFW_UH_WLOCK(chain);
IPFW_WLOCK(chain);
/* Extra check to avoid race with another ipfw_nat_cfg() */
if (gencnt != chain->gencnt &&
((cfg = lookup_nat(&chain->nat, ptr->id)) != NULL))
LIST_REMOVE(cfg, _next);
tcfg = NULL;
if (gencnt != chain->gencnt)
tcfg = lookup_nat_name(&chain->nat, ucfg->name);
IPFW_WLOCK(chain);
if (tcfg != NULL)
LIST_REMOVE(tcfg, _next);
LIST_INSERT_HEAD(&chain->nat, ptr, _next);
chain->gencnt++;
IPFW_WUNLOCK(chain);
chain->gencnt++;
IPFW_UH_WUNLOCK(chain);
if (tcfg != NULL)
free(tcfg, M_IPFW);
}
/*
* Creates/configure nat44 instance
* Data layout (v0)(current):
* Request: [ ipfw_obj_header nat44_cfg_nat .. ]
*
* Returns 0 on success
*/
static int
nat44_cfg(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
struct sockopt_data *sd)
{
ipfw_obj_header *oh;
struct nat44_cfg_nat *ucfg;
int id;
size_t read;
char *errptr;
/* Check minimum header size */
if (sd->valsize < (sizeof(*oh) + sizeof(*ucfg)))
return (EINVAL);
oh = (ipfw_obj_header *)sd->kbuf;
/* Basic length checks for TLVs */
if (oh->ntlv.head.length != sizeof(oh->ntlv))
return (EINVAL);
ucfg = (struct nat44_cfg_nat *)(oh + 1);
/* Check if name is properly terminated and looks like number */
if (strnlen(ucfg->name, sizeof(ucfg->name)) == sizeof(ucfg->name))
return (EINVAL);
id = strtol(ucfg->name, &errptr, 10);
if (id == 0 || *errptr != '\0')
return (EINVAL);
read = sizeof(*oh) + sizeof(*ucfg);
/* Check number of redirs */
if (sd->valsize < read + ucfg->redir_cnt*sizeof(struct nat44_cfg_redir))
return (EINVAL);
nat44_config(chain, ucfg);
return (0);
}
/*
* Destroys given nat instances.
* Data layout (v0)(current):
* Request: [ ipfw_obj_header ]
*
* Returns 0 on success
*/
static int
nat44_destroy(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
struct sockopt_data *sd)
{
ipfw_obj_header *oh;
struct cfg_nat *ptr;
ipfw_obj_ntlv *ntlv;
/* Check minimum header size */
if (sd->valsize < sizeof(*oh))
return (EINVAL);
oh = (ipfw_obj_header *)sd->kbuf;
/* Basic length checks for TLVs */
if (oh->ntlv.head.length != sizeof(oh->ntlv))
return (EINVAL);
ntlv = &oh->ntlv;
/* Check if name is properly terminated */
if (strnlen(ntlv->name, sizeof(ntlv->name)) == sizeof(ntlv->name))
return (EINVAL);
IPFW_UH_WLOCK(chain);
ptr = lookup_nat_name(&chain->nat, ntlv->name);
if (ptr == NULL) {
IPFW_UH_WUNLOCK(chain);
return (ESRCH);
}
IPFW_WLOCK(chain);
LIST_REMOVE(ptr, _next);
flush_nat_ptrs(chain, ptr->id);
IPFW_WUNLOCK(chain);
IPFW_UH_WUNLOCK(chain);
del_redir_spool_cfg(ptr, &ptr->redir_chain);
LibAliasUninit(ptr->lib);
free(ptr, M_IPFW);
return (0);
}
static void
export_nat_cfg(struct cfg_nat *ptr, struct nat44_cfg_nat *ucfg)
{
snprintf(ucfg->name, sizeof(ucfg->name), "%d", ptr->id);
ucfg->ip = ptr->ip;
ucfg->redir_cnt = ptr->redir_cnt;
ucfg->mode = ptr->mode;
strlcpy(ucfg->if_name, ptr->if_name, sizeof(ucfg->if_name));
}
/*
* Gets config for given nat instance
* Data layout (v0)(current):
* Request: [ ipfw_obj_header nat44_cfg_nat .. ]
*
* Returns 0 on success
*/
static int
nat44_get_cfg(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
struct sockopt_data *sd)
{
ipfw_obj_header *oh;
struct nat44_cfg_nat *ucfg;
struct cfg_nat *ptr;
struct cfg_redir *r;
struct cfg_spool *s;
struct nat44_cfg_redir *ser_r;
struct nat44_cfg_spool *ser_s;
size_t sz;
sz = sizeof(*oh) + sizeof(*ucfg);
/* Check minimum header size */
if (sd->valsize < sz)
return (EINVAL);
oh = (struct _ipfw_obj_header *)ipfw_get_sopt_header(sd, sz);
/* Basic length checks for TLVs */
if (oh->ntlv.head.length != sizeof(oh->ntlv))
return (EINVAL);
ucfg = (struct nat44_cfg_nat *)(oh + 1);
/* Check if name is properly terminated */
if (strnlen(ucfg->name, sizeof(ucfg->name)) == sizeof(ucfg->name))
return (EINVAL);
IPFW_UH_RLOCK(chain);
ptr = lookup_nat_name(&chain->nat, ucfg->name);
if (ptr == NULL) {
IPFW_UH_RUNLOCK(chain);
return (ESRCH);
}
export_nat_cfg(ptr, ucfg);
/* Estimate memory amount */
sz = sizeof(struct nat44_cfg_nat);
LIST_FOREACH(r, &ptr->redir_chain, _next) {
sz += sizeof(struct nat44_cfg_redir);
LIST_FOREACH(s, &r->spool_chain, _next)
sz += sizeof(struct nat44_cfg_spool);
}
ucfg->size = sz;
if (sd->valsize < sz + sizeof(*oh)) {
/*
* Submitted buffer size is not enough.
* WE've already filled in @ucfg structure with
* relevant info including size, so we
* can return. Buffer will be flushed automatically.
*/
IPFW_UH_RUNLOCK(chain);
return (ENOMEM);
}
/* Size OK, let's copy data */
LIST_FOREACH(r, &ptr->redir_chain, _next) {
ser_r = (struct nat44_cfg_redir *)ipfw_get_sopt_space(sd,
sizeof(*ser_r));
ser_r->mode = r->mode;
ser_r->laddr = r->laddr;
ser_r->paddr = r->paddr;
ser_r->raddr = r->raddr;
ser_r->lport = r->lport;
ser_r->pport = r->pport;
ser_r->rport = r->rport;
ser_r->pport_cnt = r->pport_cnt;
ser_r->rport_cnt = r->rport_cnt;
ser_r->proto = r->proto;
ser_r->spool_cnt = r->spool_cnt;
LIST_FOREACH(s, &r->spool_chain, _next) {
ser_s = (struct nat44_cfg_spool *)ipfw_get_sopt_space(
sd, sizeof(*ser_s));
ser_s->addr = s->addr;
ser_s->port = s->port;
}
}
IPFW_UH_RUNLOCK(chain);
return (0);
}
/*
* Lists all nat44 instances currently available in kernel.
* Data layout (v0)(current):
* Request: [ ipfw_obj_lheader ]
* Reply: [ ipfw_obj_lheader nat44_cfg_nat x N ]
*
* Returns 0 on success
*/
static int
nat44_list_nat(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
struct sockopt_data *sd)
{
ipfw_obj_lheader *olh;
struct nat44_cfg_nat *ucfg;
struct cfg_nat *ptr;
int nat_count;
/* Check minimum header size */
if (sd->valsize < sizeof(ipfw_obj_lheader))
return (EINVAL);
olh = (ipfw_obj_lheader *)ipfw_get_sopt_header(sd, sizeof(*olh));
IPFW_UH_RLOCK(chain);
nat_count = 0;
LIST_FOREACH(ptr, &chain->nat, _next)
nat_count++;
olh->count = nat_count;
olh->objsize = sizeof(struct nat44_cfg_nat);
olh->size = sizeof(*olh) + olh->count * olh->objsize;
if (sd->valsize < olh->size) {
IPFW_UH_RUNLOCK(chain);
return (ENOMEM);
}
LIST_FOREACH(ptr, &chain->nat, _next) {
ucfg = (struct nat44_cfg_nat *)ipfw_get_sopt_space(sd,
sizeof(*ucfg));
export_nat_cfg(ptr, ucfg);
}
IPFW_UH_RUNLOCK(chain);
return (0);
}
/*
* Gets log for given nat instance
* Data layout (v0)(current):
* Request: [ ipfw_obj_header nat44_cfg_nat ]
* Reply: [ ipfw_obj_header nat44_cfg_nat LOGBUFFER ]
*
* Returns 0 on success
*/
static int
nat44_get_log(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
struct sockopt_data *sd)
{
ipfw_obj_header *oh;
struct nat44_cfg_nat *ucfg;
struct cfg_nat *ptr;
void *pbuf;
size_t sz;
sz = sizeof(*oh) + sizeof(*ucfg);
/* Check minimum header size */
if (sd->valsize < sz)
return (EINVAL);
oh = (struct _ipfw_obj_header *)ipfw_get_sopt_header(sd, sz);
/* Basic length checks for TLVs */
if (oh->ntlv.head.length != sizeof(oh->ntlv))
return (EINVAL);
ucfg = (struct nat44_cfg_nat *)(oh + 1);
/* Check if name is properly terminated */
if (strnlen(ucfg->name, sizeof(ucfg->name)) == sizeof(ucfg->name))
return (EINVAL);
IPFW_UH_RLOCK(chain);
ptr = lookup_nat_name(&chain->nat, ucfg->name);
if (ptr == NULL) {
IPFW_UH_RUNLOCK(chain);
return (ESRCH);
}
if (ptr->lib->logDesc == NULL) {
IPFW_UH_RUNLOCK(chain);
return (ENOENT);
}
export_nat_cfg(ptr, ucfg);
/* Estimate memory amount */
ucfg->size = sizeof(struct nat44_cfg_nat) + LIBALIAS_BUF_SIZE;
if (sd->valsize < sz + sizeof(*oh)) {
/*
* Submitted buffer size is not enough.
* WE've already filled in @ucfg structure with
* relevant info including size, so we
* can return. Buffer will be flushed automatically.
*/
IPFW_UH_RUNLOCK(chain);
return (ENOMEM);
}
pbuf = (void *)ipfw_get_sopt_space(sd, LIBALIAS_BUF_SIZE);
memcpy(pbuf, ptr->lib->logDesc, LIBALIAS_BUF_SIZE);
IPFW_UH_RUNLOCK(chain);
return (0);
}
static struct ipfw_sopt_handler scodes[] = {
{ IP_FW_NAT44_XCONFIG, 0, HDIR_SET, nat44_cfg },
{ IP_FW_NAT44_DESTROY, 0, HDIR_SET, nat44_destroy },
{ IP_FW_NAT44_XGETCONFIG, 0, HDIR_GET, nat44_get_cfg },
{ IP_FW_NAT44_LIST_NAT, 0, HDIR_GET, nat44_list_nat },
{ IP_FW_NAT44_XGETLOG, 0, HDIR_GET, nat44_get_log },
};
/*
* Legacy configuration routines
*/
struct cfg_spool_legacy {
LIST_ENTRY(cfg_spool_legacy) _next;
struct in_addr addr;
u_short port;
};
struct cfg_redir_legacy {
LIST_ENTRY(cfg_redir) _next;
u_int16_t mode;
struct in_addr laddr;
struct in_addr paddr;
struct in_addr raddr;
u_short lport;
u_short pport;
u_short rport;
u_short pport_cnt;
u_short rport_cnt;
int proto;
struct alias_link **alink;
u_int16_t spool_cnt;
LIST_HEAD(, cfg_spool_legacy) spool_chain;
};
struct cfg_nat_legacy {
LIST_ENTRY(cfg_nat_legacy) _next;
int id;
struct in_addr ip;
char if_name[IF_NAMESIZE];
int mode;
struct libalias *lib;
int redir_cnt;
LIST_HEAD(, cfg_redir_legacy) redir_chain;
};
static int
ipfw_nat_cfg(struct sockopt *sopt)
{
struct cfg_nat_legacy *cfg;
struct nat44_cfg_nat *ucfg;
struct cfg_redir_legacy *rdir;
struct nat44_cfg_redir *urdir;
char *buf;
size_t len, len2;
int error, i;
len = sopt->sopt_valsize;
len2 = len + 128;
/*
* Allocate 2x buffer to store converted structures.
* new redir_cfg has shrinked, so we're sure that
* new buffer size is enough.
*/
buf = malloc(roundup2(len, 8) + len2, M_TEMP, M_WAITOK | M_ZERO);
error = sooptcopyin(sopt, buf, len, sizeof(struct cfg_nat_legacy));
if (error != 0)
goto out;
cfg = (struct cfg_nat_legacy *)buf;
if (cfg->id < 0) {
error = EINVAL;
goto out;
}
ucfg = (struct nat44_cfg_nat *)&buf[roundup2(len, 8)];
snprintf(ucfg->name, sizeof(ucfg->name), "%d", cfg->id);
strlcpy(ucfg->if_name, cfg->if_name, sizeof(ucfg->if_name));
ucfg->ip = cfg->ip;
ucfg->mode = cfg->mode;
ucfg->redir_cnt = cfg->redir_cnt;
if (len < sizeof(*cfg) + cfg->redir_cnt * sizeof(*rdir)) {
error = EINVAL;
goto out;
}
urdir = (struct nat44_cfg_redir *)(ucfg + 1);
rdir = (struct cfg_redir_legacy *)(cfg + 1);
for (i = 0; i < cfg->redir_cnt; i++) {
urdir->mode = rdir->mode;
urdir->laddr = rdir->laddr;
urdir->paddr = rdir->paddr;
urdir->raddr = rdir->raddr;
urdir->lport = rdir->lport;
urdir->pport = rdir->pport;
urdir->rport = rdir->rport;
urdir->pport_cnt = rdir->pport_cnt;
urdir->rport_cnt = rdir->rport_cnt;
urdir->proto = rdir->proto;
urdir->spool_cnt = rdir->spool_cnt;
urdir++;
rdir++;
}
nat44_config(&V_layer3_chain, ucfg);
out:
free(buf, M_TEMP);
@ -478,15 +981,17 @@ ipfw_nat_del(struct sockopt *sopt)
sooptcopyin(sopt, &i, sizeof i, sizeof i);
/* XXX validate i */
IPFW_WLOCK(chain);
IPFW_UH_WLOCK(chain);
ptr = lookup_nat(&chain->nat, i);
if (ptr == NULL) {
IPFW_WUNLOCK(chain);
IPFW_UH_WUNLOCK(chain);
return (EINVAL);
}
IPFW_WLOCK(chain);
LIST_REMOVE(ptr, _next);
flush_nat_ptrs(chain, i);
IPFW_WUNLOCK(chain);
IPFW_UH_WUNLOCK(chain);
del_redir_spool_cfg(ptr, &ptr->redir_chain);
LibAliasUninit(ptr->lib);
free(ptr, M_IPFW);
@ -498,28 +1003,31 @@ ipfw_nat_get_cfg(struct sockopt *sopt)
{
struct ip_fw_chain *chain = &V_layer3_chain;
struct cfg_nat *n;
struct cfg_nat_legacy *ucfg;
struct cfg_redir *r;
struct cfg_spool *s;
struct cfg_redir_legacy *ser_r;
struct cfg_spool_legacy *ser_s;
char *data;
int gencnt, nat_cnt, len, error;
nat_cnt = 0;
len = sizeof(nat_cnt);
IPFW_RLOCK(chain);
IPFW_UH_RLOCK(chain);
retry:
gencnt = chain->gencnt;
/* Estimate memory amount */
LIST_FOREACH(n, &chain->nat, _next) {
nat_cnt++;
len += sizeof(struct cfg_nat);
len += sizeof(struct cfg_nat_legacy);
LIST_FOREACH(r, &n->redir_chain, _next) {
len += sizeof(struct cfg_redir);
len += sizeof(struct cfg_redir_legacy);
LIST_FOREACH(s, &r->spool_chain, _next)
len += sizeof(struct cfg_spool);
len += sizeof(struct cfg_spool_legacy);
}
}
IPFW_RUNLOCK(chain);
IPFW_UH_RUNLOCK(chain);
data = malloc(len, M_TEMP, M_WAITOK | M_ZERO);
bcopy(&nat_cnt, data, sizeof(nat_cnt));
@ -527,25 +1035,43 @@ ipfw_nat_get_cfg(struct sockopt *sopt)
nat_cnt = 0;
len = sizeof(nat_cnt);
IPFW_RLOCK(chain);
IPFW_UH_RLOCK(chain);
if (gencnt != chain->gencnt) {
free(data, M_TEMP);
goto retry;
}
/* Serialize all the data. */
LIST_FOREACH(n, &chain->nat, _next) {
bcopy(n, &data[len], sizeof(struct cfg_nat));
len += sizeof(struct cfg_nat);
ucfg = (struct cfg_nat_legacy *)&data[len];
ucfg->id = n->id;
ucfg->ip = n->ip;
ucfg->redir_cnt = n->redir_cnt;
ucfg->mode = n->mode;
strlcpy(ucfg->if_name, n->if_name, sizeof(ucfg->if_name));
len += sizeof(struct cfg_nat_legacy);
LIST_FOREACH(r, &n->redir_chain, _next) {
bcopy(r, &data[len], sizeof(struct cfg_redir));
len += sizeof(struct cfg_redir);
ser_r = (struct cfg_redir_legacy *)&data[len];
ser_r->mode = r->mode;
ser_r->laddr = r->laddr;
ser_r->paddr = r->paddr;
ser_r->raddr = r->raddr;
ser_r->lport = r->lport;
ser_r->pport = r->pport;
ser_r->rport = r->rport;
ser_r->pport_cnt = r->pport_cnt;
ser_r->rport_cnt = r->rport_cnt;
ser_r->proto = r->proto;
ser_r->spool_cnt = r->spool_cnt;
len += sizeof(struct cfg_redir_legacy);
LIST_FOREACH(s, &r->spool_chain, _next) {
bcopy(s, &data[len], sizeof(struct cfg_spool));
len += sizeof(struct cfg_spool);
ser_s = (struct cfg_spool_legacy *)&data[len];
ser_s->addr = s->addr;
ser_s->port = s->port;
len += sizeof(struct cfg_spool_legacy);
}
}
}
IPFW_RUNLOCK(chain);
IPFW_UH_RUNLOCK(chain);
error = sooptcopyout(sopt, data, len);
free(data, M_TEMP);
@ -560,6 +1086,7 @@ ipfw_nat_get_log(struct sockopt *sopt)
struct cfg_nat *ptr;
int i, size;
struct ip_fw_chain *chain;
IPFW_RLOCK_TRACKER;
chain = &V_layer3_chain;
@ -631,6 +1158,7 @@ ipfw_nat_init(void)
ipfw_nat_del_ptr = ipfw_nat_del;
ipfw_nat_get_cfg_ptr = ipfw_nat_get_cfg;
ipfw_nat_get_log_ptr = ipfw_nat_get_log;
IPFW_ADD_SOPT_HANDLER(1, scodes);
ifaddr_event_tag = EVENTHANDLER_REGISTER(ifaddr_event, ifaddr_change,
NULL, EVENTHANDLER_PRI_ANY);
@ -642,6 +1170,7 @@ ipfw_nat_destroy(void)
EVENTHANDLER_DEREGISTER(ifaddr_event, ifaddr_event_tag);
/* deregister ipfw_nat */
IPFW_DEL_SOPT_HANDLER(1, scodes);
ipfw_nat_ptr = NULL;
lookup_nat_ptr = NULL;
ipfw_nat_cfg_ptr = NULL;
@ -683,7 +1212,7 @@ static moduledata_t ipfw_nat_mod = {
DECLARE_MODULE(ipfw_nat, ipfw_nat_mod, IPFW_NAT_SI_SUB_FIREWALL, SI_ORDER_ANY);
MODULE_DEPEND(ipfw_nat, libalias, 1, 1, 1);
MODULE_DEPEND(ipfw_nat, ipfw, 2, 2, 2);
MODULE_DEPEND(ipfw_nat, ipfw, 3, 3, 3);
MODULE_VERSION(ipfw_nat, 1);
SYSINIT(ipfw_nat_init, IPFW_NAT_SI_SUB_FIREWALL, IPFW_NAT_MODULE_ORDER,

View File

@ -152,10 +152,11 @@ void ipfw_nat_destroy(void);
/* In ip_fw_log.c */
struct ip;
struct ip_fw_chain;
void ipfw_log_bpf(int);
void ipfw_log(struct ip_fw *f, u_int hlen, struct ip_fw_args *args,
struct mbuf *m, struct ifnet *oif, u_short offset, uint32_t tablearg,
struct ip *ip);
void ipfw_log(struct ip_fw_chain *chain, struct ip_fw *f, u_int hlen,
struct ip_fw_args *args, struct mbuf *m, struct ifnet *oif,
u_short offset, uint32_t tablearg, struct ip *ip);
VNET_DECLARE(u_int64_t, norule_counter);
#define V_norule_counter VNET(norule_counter)
VNET_DECLARE(int, verbose_limit);
@ -176,22 +177,26 @@ enum { /* result for matching dynamic rules */
* Eventually we may implement it with a callback on the function.
*/
struct ip_fw_chain;
void ipfw_expire_dyn_rules(struct ip_fw_chain *, struct ip_fw *, int);
struct sockopt_data;
int ipfw_is_dyn_rule(struct ip_fw *rule);
void ipfw_expire_dyn_rules(struct ip_fw_chain *, ipfw_range_tlv *);
void ipfw_dyn_unlock(ipfw_dyn_rule *q);
struct tcphdr;
struct mbuf *ipfw_send_pkt(struct mbuf *, struct ipfw_flow_id *,
u_int32_t, u_int32_t, int);
int ipfw_install_state(struct ip_fw *rule, ipfw_insn_limit *cmd,
struct ip_fw_args *args, uint32_t tablearg);
int ipfw_install_state(struct ip_fw_chain *chain, struct ip_fw *rule,
ipfw_insn_limit *cmd, struct ip_fw_args *args, uint32_t tablearg);
ipfw_dyn_rule *ipfw_lookup_dyn_rule(struct ipfw_flow_id *pkt,
int *match_direction, struct tcphdr *tcp);
void ipfw_remove_dyn_children(struct ip_fw *rule);
void ipfw_get_dynamic(struct ip_fw_chain *chain, char **bp, const char *ep);
int ipfw_dump_states(struct ip_fw_chain *chain, struct sockopt_data *sd);
void ipfw_dyn_init(struct ip_fw_chain *); /* per-vnet initialization */
void ipfw_dyn_uninit(int); /* per-vnet deinitialization */
int ipfw_dyn_len(void);
int ipfw_dyn_get_count(void);
/* common variables */
VNET_DECLARE(int, fw_one_pass);
@ -203,6 +208,9 @@ VNET_DECLARE(int, fw_verbose);
VNET_DECLARE(struct ip_fw_chain, layer3_chain);
#define V_layer3_chain VNET(layer3_chain)
VNET_DECLARE(int, ipfw_vnet_ready);
#define V_ipfw_vnet_ready VNET(ipfw_vnet_ready)
VNET_DECLARE(u_int32_t, set_disable);
#define V_set_disable VNET(set_disable)
@ -212,23 +220,68 @@ VNET_DECLARE(int, autoinc_step);
VNET_DECLARE(unsigned int, fw_tables_max);
#define V_fw_tables_max VNET(fw_tables_max)
VNET_DECLARE(unsigned int, fw_tables_sets);
#define V_fw_tables_sets VNET(fw_tables_sets)
struct tables_config;
#ifdef _KERNEL
typedef struct ip_fw_cntr {
uint64_t pcnt; /* Packet counter */
uint64_t bcnt; /* Byte counter */
uint64_t timestamp; /* tv_sec of last match */
} ip_fw_cntr;
/*
* Here we have the structure representing an ipfw rule.
*
* It starts with a general area
* followed by an array of one or more instructions, which the code
* accesses as an array of 32-bit values.
*
* Given a rule pointer r:
*
* r->cmd is the start of the first instruction.
* ACTION_PTR(r) is the start of the first action (things to do
* once a rule matched).
*/
struct ip_fw {
uint16_t act_ofs; /* offset of action in 32-bit units */
uint16_t cmd_len; /* # of 32-bit words in cmd */
uint16_t rulenum; /* rule number */
uint8_t set; /* rule set (0..31) */
uint8_t flags; /* currently unused */
counter_u64_t cntr; /* Pointer to rule counters */
uint32_t timestamp; /* tv_sec of last match */
uint32_t id; /* rule id */
uint32_t cached_id; /* used by jump_fast */
uint32_t cached_pos; /* used by jump_fast */
ipfw_insn cmd[1]; /* storage for commands */
};
#endif
struct ip_fw_chain {
struct ip_fw **map; /* array of rule ptrs to ease lookup */
uint32_t id; /* ruleset id */
int n_rules; /* number of static rules */
LIST_HEAD(nat_list, cfg_nat) nat; /* list of nat entries */
struct radix_node_head **tables; /* IPv4 tables */
struct radix_node_head **xtables; /* extended tables */
uint8_t *tabletype; /* Array of table types */
void *tablestate; /* runtime table info */
void *valuestate; /* runtime table value info */
int *idxmap; /* skipto array of rules */
#if defined( __linux__ ) || defined( _WIN32 )
spinlock_t rwmtx;
#else
struct rwlock rwmtx;
struct rmlock rwmtx;
#endif
int static_len; /* total len of static rules */
int static_len; /* total len of static rules (v0) */
uint32_t gencnt; /* NAT generation count */
struct ip_fw *reap; /* list of rules to reap */
struct ip_fw *default_rule;
struct tables_config *tblcfg; /* tables module data */
void *ifcfg; /* interface module data */
int *idxmap_back; /* standby skipto array of rules */
#if defined( __linux__ ) || defined( _WIN32 )
spinlock_t uh_lock;
#else
@ -236,9 +289,76 @@ struct ip_fw_chain {
#endif
};
/* 64-byte structure representing multi-field table value */
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 */
};
struct namedobj_instance;
struct named_object {
TAILQ_ENTRY(named_object) nn_next; /* namehash */
TAILQ_ENTRY(named_object) nv_next; /* valuehash */
char *name; /* object name */
uint8_t type; /* object type */
uint8_t compat; /* Object name is number */
uint16_t kidx; /* object kernel index */
uint16_t uidx; /* userland idx for compat records */
uint32_t set; /* set object belongs to */
uint32_t refcnt; /* number of references */
};
TAILQ_HEAD(namedobjects_head, named_object);
struct sockopt; /* used by tcp_var.h */
struct sockopt_data {
caddr_t kbuf; /* allocated buffer */
size_t ksize; /* given buffer size */
size_t koff; /* data already used */
size_t kavail; /* number of bytes available */
size_t ktotal; /* total bytes pushed */
struct sockopt *sopt; /* socket data */
caddr_t sopt_val; /* sopt user buffer */
size_t valsize; /* original data size */
};
struct ipfw_ifc;
typedef void (ipfw_ifc_cb)(struct ip_fw_chain *ch, void *cbdata,
uint16_t ifindex);
struct ipfw_iface {
struct named_object no;
char ifname[64];
int resolved;
uint16_t ifindex;
uint16_t spare;
uint64_t gencnt;
TAILQ_HEAD(, ipfw_ifc) consumers;
};
struct ipfw_ifc {
TAILQ_ENTRY(ipfw_ifc) next;
struct ipfw_iface *iface;
ipfw_ifc_cb *cb;
void *cbdata;
};
/* Macro for working with various counters */
#ifdef USERSPACE
#define IPFW_INC_RULE_COUNTER(_cntr, _bytes) do { \
(_cntr)->pcnt++; \
(_cntr)->bcnt += _bytes; \
@ -260,13 +380,40 @@ struct sockopt; /* used by tcp_var.h */
(_cntr)->pcnt = 0; \
(_cntr)->bcnt = 0; \
} while (0)
#else
#define IPFW_INC_RULE_COUNTER(_cntr, _bytes) do { \
counter_u64_add((_cntr)->cntr, 1); \
counter_u64_add((_cntr)->cntr + 1, _bytes); \
if ((_cntr)->timestamp != time_uptime) \
(_cntr)->timestamp = time_uptime; \
} while (0)
#define IP_FW_ARG_TABLEARG(a) (((a) == IP_FW_TABLEARG) ? tablearg : (a))
#define IPFW_INC_DYN_COUNTER(_cntr, _bytes) do { \
(_cntr)->pcnt++; \
(_cntr)->bcnt += _bytes; \
} while (0)
#define IPFW_ZERO_RULE_COUNTER(_cntr) do { \
counter_u64_zero((_cntr)->cntr); \
counter_u64_zero((_cntr)->cntr + 1); \
(_cntr)->timestamp = 0; \
} while (0)
#define IPFW_ZERO_DYN_COUNTER(_cntr) do { \
(_cntr)->pcnt = 0; \
(_cntr)->bcnt = 0; \
} while (0)
#endif
#define TARG_VAL(ch, k, f) ((struct table_value *)((ch)->valuestate))[k].f
#define IP_FW_ARG_TABLEARG(ch, a, f) \
(((a) == IP_FW_TARG) ? TARG_VAL(ch, tablearg, f) : (a))
/*
* The lock is heavily used by ip_fw2.c (the main file) and ip_fw_nat.c
* so the variable and the macros must be here.
*/
#if defined( __linux__ ) || defined( _WIN32 )
#define IPFW_LOCK_INIT(_chain) do { \
rw_init(&(_chain)->rwmtx, "IPFW static rules"); \
rw_init(&(_chain)->uh_lock, "IPFW UH lock"); \
@ -280,12 +427,35 @@ struct sockopt; /* used by tcp_var.h */
#define IPFW_RLOCK_ASSERT(_chain) rw_assert(&(_chain)->rwmtx, RA_RLOCKED)
#define IPFW_WLOCK_ASSERT(_chain) rw_assert(&(_chain)->rwmtx, RA_WLOCKED)
#define IPFW_RLOCK_TRACKER
#define IPFW_RLOCK(p) rw_rlock(&(p)->rwmtx)
#define IPFW_RUNLOCK(p) rw_runlock(&(p)->rwmtx)
#define IPFW_WLOCK(p) rw_wlock(&(p)->rwmtx)
#define IPFW_WUNLOCK(p) rw_wunlock(&(p)->rwmtx)
#define IPFW_PF_RLOCK(p) IPFW_RLOCK(p)
#define IPFW_PF_RUNLOCK(p) IPFW_RUNLOCK(p)
#else /* FreeBSD */
#define IPFW_LOCK_INIT(_chain) do { \
rm_init(&(_chain)->rwmtx, "IPFW static rules"); \
rw_init(&(_chain)->uh_lock, "IPFW UH lock"); \
} while (0)
#define IPFW_LOCK_DESTROY(_chain) do { \
rm_destroy(&(_chain)->rwmtx); \
rw_destroy(&(_chain)->uh_lock); \
} while (0)
#define IPFW_RLOCK_ASSERT(_chain) rm_assert(&(_chain)->rwmtx, RA_RLOCKED)
#define IPFW_WLOCK_ASSERT(_chain) rm_assert(&(_chain)->rwmtx, RA_WLOCKED)
#define IPFW_RLOCK_TRACKER struct rm_priotracker _tracker
#define IPFW_RLOCK(p) rm_rlock(&(p)->rwmtx, &_tracker)
#define IPFW_RUNLOCK(p) rm_runlock(&(p)->rwmtx, &_tracker)
#define IPFW_WLOCK(p) rm_wlock(&(p)->rwmtx)
#define IPFW_WUNLOCK(p) rm_wunlock(&(p)->rwmtx)
#define IPFW_PF_RLOCK(p) IPFW_RLOCK(p)
#define IPFW_PF_RUNLOCK(p) IPFW_RUNLOCK(p)
#endif
#define IPFW_UH_RLOCK_ASSERT(_chain) rw_assert(&(_chain)->uh_lock, RA_RLOCKED)
#define IPFW_UH_WLOCK_ASSERT(_chain) rw_assert(&(_chain)->uh_lock, RA_WLOCKED)
@ -295,32 +465,172 @@ struct sockopt; /* used by tcp_var.h */
#define IPFW_UH_WLOCK(p) rw_wlock(&(p)->uh_lock)
#define IPFW_UH_WUNLOCK(p) rw_wunlock(&(p)->uh_lock)
struct obj_idx {
uint16_t uidx; /* internal index supplied by userland */
uint16_t kidx; /* kernel object index */
uint16_t off; /* tlv offset from rule end in 4-byte words */
uint8_t spare;
uint8_t type; /* object type within its category */
};
struct rule_check_info {
uint16_t flags; /* rule-specific check flags */
uint16_t table_opcodes; /* count of opcodes referencing table */
uint16_t urule_numoff; /* offset of rulenum in bytes */
uint8_t version; /* rule version */
uint8_t spare;
ipfw_obj_ctlv *ctlv; /* name TLV containter */
struct ip_fw *krule; /* resulting rule pointer */
caddr_t urule; /* original rule pointer */
struct obj_idx obuf[8]; /* table references storage */
};
/* Legacy interface support */
/*
* FreeBSD 8 export rule format
*/
struct ip_fw_rule0 {
struct ip_fw *x_next; /* linked list of rules */
struct ip_fw *next_rule; /* ptr to next [skipto] rule */
/* 'next_rule' is used to pass up 'set_disable' status */
uint16_t act_ofs; /* offset of action in 32-bit units */
uint16_t cmd_len; /* # of 32-bit words in cmd */
uint16_t rulenum; /* rule number */
uint8_t set; /* rule set (0..31) */
uint8_t _pad; /* padding */
uint32_t id; /* rule id */
/* These fields are present in all rules. */
uint64_t pcnt; /* Packet counter */
uint64_t bcnt; /* Byte counter */
uint32_t timestamp; /* tv_sec of last match */
ipfw_insn cmd[1]; /* storage for commands */
};
struct ip_fw_bcounter0 {
uint64_t pcnt; /* Packet counter */
uint64_t bcnt; /* Byte counter */
uint32_t timestamp; /* tv_sec of last match */
};
/* Kernel rule length */
/*
* RULE _K_ SIZE _V_ ->
* get kernel size from userland rool version _V_.
* RULE _U_ SIZE _V_ ->
* get user size version _V_ from kernel rule
* RULESIZE _V_ ->
* get user size rule length
*/
/* FreeBSD8 <> current kernel format */
#define RULEUSIZE0(r) (sizeof(struct ip_fw_rule0) + (r)->cmd_len * 4 - 4)
#define RULEKSIZE0(r) roundup2((sizeof(struct ip_fw) + (r)->cmd_len*4 - 4), 8)
/* FreeBSD11 <> current kernel format */
#define RULEUSIZE1(r) (roundup2(sizeof(struct ip_fw_rule) + \
(r)->cmd_len * 4 - 4, 8))
#define RULEKSIZE1(r) roundup2((sizeof(struct ip_fw) + (r)->cmd_len*4 - 4), 8)
/* In ip_fw_iface.c */
int ipfw_iface_init(void);
void ipfw_iface_destroy(void);
void vnet_ipfw_iface_destroy(struct ip_fw_chain *ch);
int ipfw_iface_ref(struct ip_fw_chain *ch, char *name,
struct ipfw_ifc *ic);
void ipfw_iface_unref(struct ip_fw_chain *ch, struct ipfw_ifc *ic);
void ipfw_iface_add_notify(struct ip_fw_chain *ch, struct ipfw_ifc *ic);
void ipfw_iface_del_notify(struct ip_fw_chain *ch, struct ipfw_ifc *ic);
/* In ip_fw_sockopt.c */
void ipfw_init_skipto_cache(struct ip_fw_chain *chain);
void ipfw_destroy_skipto_cache(struct ip_fw_chain *chain);
int ipfw_find_rule(struct ip_fw_chain *chain, uint32_t key, uint32_t id);
int ipfw_add_rule(struct ip_fw_chain *chain, struct ip_fw *input_rule);
int ipfw_ctl(struct sockopt *sopt);
int ipfw_ctl3(struct sockopt *sopt);
int ipfw_chk(struct ip_fw_args *args);
void ipfw_reap_add(struct ip_fw_chain *chain, struct ip_fw **head,
struct ip_fw *rule);
void ipfw_reap_rules(struct ip_fw *head);
void ipfw_init_counters(void);
void ipfw_destroy_counters(void);
struct ip_fw *ipfw_alloc_rule(struct ip_fw_chain *chain, size_t rulesize);
int ipfw_match_range(struct ip_fw *rule, ipfw_range_tlv *rt);
typedef int (sopt_handler_f)(struct ip_fw_chain *ch,
ip_fw3_opheader *op3, struct sockopt_data *sd);
struct ipfw_sopt_handler {
uint16_t opcode;
uint8_t version;
uint8_t dir;
sopt_handler_f *handler;
uint64_t refcnt;
};
#define HDIR_SET 0x01 /* Handler is used to set some data */
#define HDIR_GET 0x02 /* Handler is used to retrieve data */
#define HDIR_BOTH HDIR_GET|HDIR_SET
void ipfw_init_sopt_handler(void);
void ipfw_destroy_sopt_handler(void);
void ipfw_add_sopt_handler(struct ipfw_sopt_handler *sh, size_t count);
int ipfw_del_sopt_handler(struct ipfw_sopt_handler *sh, size_t count);
caddr_t ipfw_get_sopt_space(struct sockopt_data *sd, size_t needed);
caddr_t ipfw_get_sopt_header(struct sockopt_data *sd, size_t needed);
#define IPFW_ADD_SOPT_HANDLER(f, c) do { \
if ((f) != 0) \
ipfw_add_sopt_handler(c, \
sizeof(c) / sizeof(c[0])); \
} while(0)
#define IPFW_DEL_SOPT_HANDLER(l, c) do { \
if ((l) != 0) \
ipfw_del_sopt_handler(c, \
sizeof(c) / sizeof(c[0])); \
} while(0)
typedef void (objhash_cb_t)(struct namedobj_instance *ni, struct named_object *,
void *arg);
typedef uint32_t (objhash_hash_f)(struct namedobj_instance *ni, void *key,
uint32_t kopt);
typedef int (objhash_cmp_f)(struct named_object *no, void *key, uint32_t kopt);
struct namedobj_instance *ipfw_objhash_create(uint32_t items);
void ipfw_objhash_destroy(struct namedobj_instance *);
void ipfw_objhash_bitmap_alloc(uint32_t items, void **idx, int *pblocks);
void ipfw_objhash_bitmap_merge(struct namedobj_instance *ni,
void **idx, int *blocks);
void ipfw_objhash_bitmap_swap(struct namedobj_instance *ni,
void **idx, int *blocks);
void ipfw_objhash_bitmap_free(void *idx, int blocks);
void ipfw_objhash_set_hashf(struct namedobj_instance *ni, objhash_hash_f *f);
struct named_object *ipfw_objhash_lookup_name(struct namedobj_instance *ni,
uint32_t set, char *name);
struct named_object *ipfw_objhash_lookup_kidx(struct namedobj_instance *ni,
uint16_t idx);
int ipfw_objhash_same_name(struct namedobj_instance *ni, struct named_object *a,
struct named_object *b);
void ipfw_objhash_add(struct namedobj_instance *ni, struct named_object *no);
void ipfw_objhash_del(struct namedobj_instance *ni, struct named_object *no);
uint32_t ipfw_objhash_count(struct namedobj_instance *ni);
void ipfw_objhash_foreach(struct namedobj_instance *ni, objhash_cb_t *f,
void *arg);
int ipfw_objhash_free_idx(struct namedobj_instance *ni, uint16_t idx);
int ipfw_objhash_alloc_idx(void *n, uint16_t *pidx);
void ipfw_objhash_set_funcs(struct namedobj_instance *ni,
objhash_hash_f *hash_f, objhash_cmp_f *cmp_f);
/* In ip_fw_table.c */
struct radix_node;
struct table_info;
typedef int (table_lookup_t)(struct table_info *ti, void *key, uint32_t keylen,
uint32_t *val);
int ipfw_lookup_table(struct ip_fw_chain *ch, uint16_t tbl, in_addr_t addr,
uint32_t *val);
int ipfw_lookup_table_extended(struct ip_fw_chain *ch, uint16_t tbl, void *paddr,
uint32_t *val, int type);
int ipfw_init_tables(struct ip_fw_chain *ch);
void ipfw_destroy_tables(struct ip_fw_chain *ch);
int ipfw_flush_table(struct ip_fw_chain *ch, uint16_t tbl);
int ipfw_add_table_entry(struct ip_fw_chain *ch, uint16_t tbl, void *paddr,
uint8_t plen, uint8_t mlen, uint8_t type, uint32_t value);
int ipfw_del_table_entry(struct ip_fw_chain *ch, uint16_t tbl, void *paddr,
uint8_t plen, uint8_t mlen, uint8_t type);
int ipfw_count_table(struct ip_fw_chain *ch, uint32_t tbl, uint32_t *cnt);
int ipfw_dump_table_entry(struct radix_node *rn, void *arg);
int ipfw_dump_table(struct ip_fw_chain *ch, ipfw_table *tbl);
int ipfw_count_xtable(struct ip_fw_chain *ch, uint32_t tbl, uint32_t *cnt);
int ipfw_dump_xtable(struct ip_fw_chain *ch, ipfw_xtable *tbl);
int ipfw_lookup_table_extended(struct ip_fw_chain *ch, uint16_t tbl, uint16_t plen,
void *paddr, uint32_t *val);
int ipfw_init_tables(struct ip_fw_chain *ch, int first);
int ipfw_resize_tables(struct ip_fw_chain *ch, unsigned int ntables);
int ipfw_switch_tables_namespace(struct ip_fw_chain *ch, unsigned int nsets);
void ipfw_destroy_tables(struct ip_fw_chain *ch, int last);
/* In ip_fw_nat.c -- XXX to be moved to ip_var.h */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,246 @@
/*-
* Copyright (c) 2002-2009 Luigi Rizzo, Universita` di Pisa
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
#ifndef _IPFW2_TABLE_H
#define _IPFW2_TABLE_H
/*
* Internal constants and data structures used by ipfw tables
* not meant to be exported outside the kernel.
*/
#ifdef _KERNEL
struct table_algo;
struct tables_config {
struct namedobj_instance *namehash;
struct namedobj_instance *valhash;
uint32_t val_size;
uint32_t algo_count;
struct table_algo *algo[256];
struct table_algo *def_algo[IPFW_TABLE_MAXTYPE + 1];
TAILQ_HEAD(op_state_l,op_state) state_list;
};
#define CHAIN_TO_TCFG(chain) ((struct tables_config *)(chain)->tblcfg)
struct table_info {
table_lookup_t *lookup; /* Lookup function */
void *state; /* Lookup radix/other structure */
void *xstate; /* eXtended state */
u_long data; /* Hints for given func */
};
/* Internal structures for handling sockopt data */
struct tid_info {
uint32_t set; /* table set */
uint16_t uidx; /* table index */
uint8_t type; /* table type */
uint8_t atype;
void *tlvs; /* Pointer to first TLV */
int tlen; /* Total TLV size block */
};
struct table_value;
struct tentry_info {
void *paddr;
struct table_value *pvalue;
void *ptv; /* Temporary field to hold obj */
uint8_t masklen; /* mask length */
uint8_t subtype;
uint16_t flags; /* record flags */
uint32_t value; /* value index */
};
#define TEI_FLAGS_UPDATE 0x0001 /* Add or update rec if exists */
#define TEI_FLAGS_UPDATED 0x0002 /* Entry has been updated */
#define TEI_FLAGS_COMPAT 0x0004 /* Called from old ABI */
#define TEI_FLAGS_DONTADD 0x0008 /* Do not create new rec */
#define TEI_FLAGS_ADDED 0x0010 /* Entry was added */
#define TEI_FLAGS_DELETED 0x0020 /* Entry was deleted */
#define TEI_FLAGS_LIMIT 0x0040 /* Limit was hit */
#define TEI_FLAGS_ERROR 0x0080 /* Unknown request error */
#define TEI_FLAGS_NOTFOUND 0x0100 /* Entry was not found */
#define TEI_FLAGS_EXISTS 0x0200 /* Entry already exists */
typedef int (ta_init)(struct ip_fw_chain *ch, void **ta_state,
struct table_info *ti, char *data, uint8_t tflags);
typedef void (ta_destroy)(void *ta_state, struct table_info *ti);
typedef int (ta_prepare_add)(struct ip_fw_chain *ch, struct tentry_info *tei,
void *ta_buf);
typedef int (ta_prepare_del)(struct ip_fw_chain *ch, struct tentry_info *tei,
void *ta_buf);
typedef int (ta_add)(void *ta_state, struct table_info *ti,
struct tentry_info *tei, void *ta_buf, uint32_t *pnum);
typedef int (ta_del)(void *ta_state, struct table_info *ti,
struct tentry_info *tei, void *ta_buf, uint32_t *pnum);
typedef void (ta_flush_entry)(struct ip_fw_chain *ch, struct tentry_info *tei,
void *ta_buf);
typedef int (ta_need_modify)(void *ta_state, struct table_info *ti,
uint32_t count, uint64_t *pflags);
typedef int (ta_prepare_mod)(void *ta_buf, uint64_t *pflags);
typedef int (ta_fill_mod)(void *ta_state, struct table_info *ti,
void *ta_buf, uint64_t *pflags);
typedef void (ta_modify)(void *ta_state, struct table_info *ti,
void *ta_buf, uint64_t pflags);
typedef void (ta_flush_mod)(void *ta_buf);
typedef void (ta_change_ti)(void *ta_state, struct table_info *ti);
typedef void (ta_print_config)(void *ta_state, struct table_info *ti, char *buf,
size_t bufsize);
typedef int ta_foreach_f(void *node, void *arg);
typedef void ta_foreach(void *ta_state, struct table_info *ti, ta_foreach_f *f,
void *arg);
typedef int ta_dump_tentry(void *ta_state, struct table_info *ti, void *e,
ipfw_obj_tentry *tent);
typedef int ta_find_tentry(void *ta_state, struct table_info *ti,
ipfw_obj_tentry *tent);
typedef void ta_dump_tinfo(void *ta_state, struct table_info *ti,
ipfw_ta_tinfo *tinfo);
typedef uint32_t ta_get_count(void *ta_state, struct table_info *ti);
struct table_algo {
char name[16];
uint32_t idx;
uint32_t type;
uint32_t refcnt;
uint32_t flags;
uint32_t vlimit;
size_t ta_buf_size;
ta_init *init;
ta_destroy *destroy;
ta_prepare_add *prepare_add;
ta_prepare_del *prepare_del;
ta_add *add;
ta_del *del;
ta_flush_entry *flush_entry;
ta_find_tentry *find_tentry;
ta_need_modify *need_modify;
ta_prepare_mod *prepare_mod;
ta_fill_mod *fill_mod;
ta_modify *modify;
ta_flush_mod *flush_mod;
ta_change_ti *change_ti;
ta_foreach *foreach;
ta_dump_tentry *dump_tentry;
ta_print_config *print_config;
ta_dump_tinfo *dump_tinfo;
ta_get_count *get_count;
};
#define TA_FLAG_DEFAULT 0x01 /* Algo is default for given type */
#define TA_FLAG_READONLY 0x02 /* Algo does not support modifications*/
#define TA_FLAG_EXTCOUNTER 0x04 /* Algo has external counter available*/
int ipfw_add_table_algo(struct ip_fw_chain *ch, struct table_algo *ta,
size_t size, int *idx);
void ipfw_del_table_algo(struct ip_fw_chain *ch, int idx);
void ipfw_table_algo_init(struct ip_fw_chain *chain);
void ipfw_table_algo_destroy(struct ip_fw_chain *chain);
MALLOC_DECLARE(M_IPFW_TBL);
/* Exported to support legacy opcodes */
int add_table_entry(struct ip_fw_chain *ch, struct tid_info *ti,
struct tentry_info *tei, uint8_t flags, uint32_t count);
int del_table_entry(struct ip_fw_chain *ch, struct tid_info *ti,
struct tentry_info *tei, uint8_t flags, uint32_t count);
int flush_table(struct ip_fw_chain *ch, struct tid_info *ti);
void ipfw_import_table_value_legacy(uint32_t value, struct table_value *v);
uint32_t ipfw_export_table_value_legacy(struct table_value *v);
int ipfw_get_table_size(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
struct sockopt_data *sd);
/* ipfw_table_value.c functions */
struct table_config;
struct tableop_state;
void ipfw_table_value_init(struct ip_fw_chain *ch, int first);
void ipfw_table_value_destroy(struct ip_fw_chain *ch, int last);
int ipfw_link_table_values(struct ip_fw_chain *ch, struct tableop_state *ts);
void ipfw_garbage_table_values(struct ip_fw_chain *ch, struct table_config *tc,
struct tentry_info *tei, uint32_t count, int rollback);
void ipfw_import_table_value_v1(ipfw_table_value *iv);
void ipfw_export_table_value_v1(struct table_value *v, ipfw_table_value *iv);
void ipfw_unref_table_values(struct ip_fw_chain *ch, struct table_config *tc,
struct table_algo *ta, void *astate, struct table_info *ti);
void rollback_table_values(struct tableop_state *ts);
int ipfw_rewrite_table_uidx(struct ip_fw_chain *chain,
struct rule_check_info *ci);
int ipfw_rewrite_table_kidx(struct ip_fw_chain *chain,
struct ip_fw_rule0 *rule);
int ipfw_mark_table_kidx(struct ip_fw_chain *chain, struct ip_fw *rule,
uint32_t *bmask);
int ipfw_export_table_ntlv(struct ip_fw_chain *ch, uint16_t kidx,
struct sockopt_data *sd);
void ipfw_unref_rule_tables(struct ip_fw_chain *chain, struct ip_fw *rule);
/* utility functions */
int ipfw_check_table_name(char *name);
int ipfw_move_tables_sets(struct ip_fw_chain *ch, ipfw_range_tlv *rt,
uint32_t new_set);
void ipfw_swap_tables_sets(struct ip_fw_chain *ch, uint32_t old_set,
uint32_t new_set, int mv);
int ipfw_foreach_table_tentry(struct ip_fw_chain *ch, uint16_t kidx,
ta_foreach_f f, void *arg);
/* internal functions */
void tc_ref(struct table_config *tc);
void tc_unref(struct table_config *tc);
struct op_state;
typedef void (op_rollback_f)(void *object, struct op_state *state);
struct op_state {
TAILQ_ENTRY(op_state) next; /* chain link */
op_rollback_f *func;
};
struct tableop_state {
struct op_state opstate;
struct ip_fw_chain *ch;
struct table_config *tc;
struct table_algo *ta;
struct tentry_info *tei;
uint32_t count;
uint32_t vmask;
int vshared;
int modified;
};
void add_toperation_state(struct ip_fw_chain *ch, struct tableop_state *ts);
void del_toperation_state(struct ip_fw_chain *ch, struct tableop_state *ts);
void rollback_toperation_state(struct ip_fw_chain *ch, void *object);
/* Legacy interfaces */
int ipfw_count_table(struct ip_fw_chain *ch, struct tid_info *ti,
uint32_t *cnt);
int ipfw_count_xtable(struct ip_fw_chain *ch, struct tid_info *ti,
uint32_t *cnt);
int ipfw_dump_table_legacy(struct ip_fw_chain *ch, struct tid_info *ti,
ipfw_table *tbl);
#endif /* _KERNEL */
#endif /* _IPFW2_TABLE_H */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,812 @@
/*-
* Copyright (c) 2014 Yandex LLC
* Copyright (c) 2014 Alexander V. Chernikov
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD: projects/ipfw/sys/netpfil/ipfw/ip_fw_table.c 270407 2014-08-23 12:41:39Z melifaro $");
/*
* Multi-field value support for ipfw tables.
*
* This file contains necessary functions to convert
* large multi-field values into u32 indices suitable to be fed
* to various table algorithms. Other machinery like proper refcounting,
* internal structures resizing are also kept here.
*/
#include "opt_ipfw.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/malloc.h>
#include <sys/kernel.h>
#include <sys/hash.h>
#include <sys/lock.h>
#include <sys/rwlock.h>
#include <sys/rmlock.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/queue.h>
#include <net/if.h> /* ip_fw.h requires IFNAMSIZ */
#include <netinet/in.h>
#include <netinet/ip_var.h> /* struct ipfw_rule_ref */
#include <netinet/ip_fw.h>
#include <netpfil/ipfw/ip_fw_private.h>
#include <netpfil/ipfw/ip_fw_table.h>
static uint32_t hash_table_value(struct namedobj_instance *ni, void *key,
uint32_t kopt);
static int cmp_table_value(struct named_object *no, void *key, uint32_t kopt);
static int list_table_values(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
struct sockopt_data *sd);
static struct ipfw_sopt_handler scodes[] = {
{ IP_FW_TABLE_VLIST, 0, HDIR_GET, list_table_values },
};
#define CHAIN_TO_VI(chain) (CHAIN_TO_TCFG(chain)->valhash)
struct table_val_link
{
struct named_object no;
struct table_value *pval; /* Pointer to real table value */
};
#define VALDATA_START_SIZE 64 /* Allocate 64-items array by default */
struct vdump_args {
struct ip_fw_chain *ch;
struct sockopt_data *sd;
struct table_value *pval;
int error;
};
static uint32_t
hash_table_value(struct namedobj_instance *ni, void *key, uint32_t kopt)
{
return (hash32_buf(key, 56, 0));
}
static int
cmp_table_value(struct named_object *no, void *key, uint32_t kopt)
{
return (memcmp(((struct table_val_link *)no)->pval, key, 56));
}
static void
mask_table_value(struct table_value *src, struct table_value *dst,
uint32_t mask)
{
#define _MCPY(f, b) if ((mask & (b)) != 0) { dst->f = src->f; }
memset(dst, 0, sizeof(*dst));
_MCPY(tag, IPFW_VTYPE_TAG);
_MCPY(pipe, IPFW_VTYPE_PIPE);
_MCPY(divert, IPFW_VTYPE_DIVERT);
_MCPY(skipto, IPFW_VTYPE_SKIPTO);
_MCPY(netgraph, IPFW_VTYPE_NETGRAPH);
_MCPY(fib, IPFW_VTYPE_FIB);
_MCPY(nat, IPFW_VTYPE_NAT);
_MCPY(dscp, IPFW_VTYPE_DSCP);
_MCPY(nh4, IPFW_VTYPE_NH4);
_MCPY(nh6, IPFW_VTYPE_NH6);
#undef _MCPY
}
static void
get_value_ptrs(struct ip_fw_chain *ch, struct table_config *tc, int vshared,
struct table_value **ptv, struct namedobj_instance **pvi)
{
struct table_value *pval;
struct namedobj_instance *vi;
if (vshared != 0) {
pval = (struct table_value *)ch->valuestate;
vi = CHAIN_TO_VI(ch);
} else {
pval = NULL;
vi = NULL;
//pval = (struct table_value *)&tc->ti.data;
}
if (ptv != NULL)
*ptv = pval;
if (pvi != NULL)
*pvi = vi;
}
/*
* Update pointers to real vaues after @pval change.
*/
static void
update_tvalue(struct namedobj_instance *ni, struct named_object *no, void *arg)
{
struct vdump_args *da;
struct table_val_link *ptv;
struct table_value *pval;
da = (struct vdump_args *)arg;
ptv = (struct table_val_link *)no;
pval = da->pval;
ptv->pval = &pval[ptv->no.kidx];
}
/*
* Grows value storage shared among all tables.
* Drops/reacquires UH locks.
* Notifies other running adds on @ch shared storage resize.
* Note function does not guarantee that free space
* will be available after invocation, so one caller needs
* to roll cycle himself.
*
* Returns 0 if case of no errors.
*/
static int
resize_shared_value_storage(struct ip_fw_chain *ch)
{
struct tables_config *tcfg;
struct namedobj_instance *vi;
struct table_value *pval, *valuestate, *old_valuestate;
void *new_idx;
struct vdump_args da;
int new_blocks;
int val_size, val_size_old;
IPFW_UH_WLOCK_ASSERT(ch);
valuestate = NULL;
new_idx = NULL;
pval = (struct table_value *)ch->valuestate;
vi = CHAIN_TO_VI(ch);
tcfg = CHAIN_TO_TCFG(ch);
val_size = tcfg->val_size * 2;
if (val_size == (1 << 30))
return (ENOSPC);
IPFW_UH_WUNLOCK(ch);
valuestate = malloc(sizeof(struct table_value) * val_size, M_IPFW,
M_WAITOK | M_ZERO);
ipfw_objhash_bitmap_alloc(val_size, (void *)&new_idx,
&new_blocks);
IPFW_UH_WLOCK(ch);
/*
* Check if we still need to resize
*/
if (tcfg->val_size >= val_size)
goto done;
/* Update pointers and notify everyone we're changing @ch */
pval = (struct table_value *)ch->valuestate;
rollback_toperation_state(ch, ch);
/* Good. Let's merge */
memcpy(valuestate, pval, sizeof(struct table_value) * tcfg->val_size);
ipfw_objhash_bitmap_merge(CHAIN_TO_VI(ch), &new_idx, &new_blocks);
IPFW_WLOCK(ch);
/* Change pointers */
old_valuestate = ch->valuestate;
ch->valuestate = valuestate;
valuestate = old_valuestate;
ipfw_objhash_bitmap_swap(CHAIN_TO_VI(ch), &new_idx, &new_blocks);
val_size_old = tcfg->val_size;
tcfg->val_size = val_size;
val_size = val_size_old;
IPFW_WUNLOCK(ch);
/* Update pointers to reflect resize */
memset(&da, 0, sizeof(da));
da.pval = (struct table_value *)ch->valuestate;
ipfw_objhash_foreach(vi, update_tvalue, &da);
done:
free(valuestate, M_IPFW);
ipfw_objhash_bitmap_free(new_idx, new_blocks);
return (0);
}
/*
* Drops reference for table value with index @kidx, stored in @pval and
* @vi. Frees value if it has no references.
*/
static void
unref_table_value(struct namedobj_instance *vi, struct table_value *pval,
uint32_t kidx)
{
struct table_val_link *ptvl;
KASSERT(pval[kidx].refcnt > 0, ("Refcount is 0 on kidx %d", kidx));
if (--pval[kidx].refcnt > 0)
return;
/* Last reference, delete item */
ptvl = (struct table_val_link *)ipfw_objhash_lookup_kidx(vi, kidx);
KASSERT(ptvl != NULL, ("lookup on value kidx %d failed", kidx));
ipfw_objhash_del(vi, &ptvl->no);
ipfw_objhash_free_idx(vi, kidx);
free(ptvl, M_IPFW);
}
struct flush_args {
struct ip_fw_chain *ch;
struct table_algo *ta;
struct table_info *ti;
void *astate;
ipfw_obj_tentry tent;
};
static int
unref_table_value_cb(void *e, void *arg)
{
struct flush_args *fa;
struct ip_fw_chain *ch;
struct table_algo *ta;
ipfw_obj_tentry *tent;
int error;
fa = (struct flush_args *)arg;
ta = fa->ta;
memset(&fa->tent, 0, sizeof(fa->tent));
tent = &fa->tent;
error = ta->dump_tentry(fa->astate, fa->ti, e, tent);
if (error != 0)
return (error);
ch = fa->ch;
unref_table_value(CHAIN_TO_VI(ch),
(struct table_value *)ch->valuestate, tent->v.kidx);
return (0);
}
/*
* Drop references for each value used in @tc.
*/
void
ipfw_unref_table_values(struct ip_fw_chain *ch, struct table_config *tc,
struct table_algo *ta, void *astate, struct table_info *ti)
{
struct flush_args fa;
IPFW_UH_WLOCK_ASSERT(ch);
memset(&fa, 0, sizeof(fa));
fa.ch = ch;
fa.ta = ta;
fa.astate = astate;
fa.ti = ti;
ta->foreach(astate, ti, unref_table_value_cb, &fa);
}
/*
* Table operation state handler.
* Called when we are going to change something in @tc which
* may lead to inconsistencies in on-going table data addition.
*
* Here we rollback all already committed state (table values, currently)
* and set "modified" field to non-zero value to indicate
* that we need to restart original operation.
*/
void
rollback_table_values(struct tableop_state *ts)
{
struct ip_fw_chain *ch;
struct table_value *pval;
struct tentry_info *ptei;
struct namedobj_instance *vi;
int i;
ch = ts->ch;
IPFW_UH_WLOCK_ASSERT(ch);
/* Get current table value pointer */
get_value_ptrs(ch, ts->tc, ts->vshared, &pval, &vi);
for (i = 0; i < ts->count; i++) {
ptei = &ts->tei[i];
if (ptei->value == 0)
continue;
unref_table_value(vi, pval, ptei->value);
}
}
/*
* Allocate new value index in either shared or per-table array.
* Function may drop/reacquire UH lock.
*
* Returns 0 on success.
*/
static int
alloc_table_vidx(struct ip_fw_chain *ch, struct tableop_state *ts,
struct namedobj_instance *vi, uint16_t *pvidx)
{
int error, vlimit;
uint16_t vidx;
IPFW_UH_WLOCK_ASSERT(ch);
error = ipfw_objhash_alloc_idx(vi, &vidx);
if (error != 0) {
/*
* We need to resize array. This involves
* lock/unlock, so we need to check "modified"
* state.
*/
ts->opstate.func(ts->tc, &ts->opstate);
error = resize_shared_value_storage(ch);
return (error); /* ts->modified should be set, we will restart */
}
vlimit = ts->ta->vlimit;
if (vlimit != 0 && vidx >= vlimit) {
/*
* Algorithm is not able to store given index.
* We have to rollback state, start using
* per-table value array or return error
* if we're already using it.
*
* TODO: do not rollback state if
* atomicity is not required.
*/
if (ts->vshared != 0) {
/* shared -> per-table */
return (ENOSPC); /* TODO: proper error */
}
/* per-table. Fail for now. */
return (ENOSPC); /* TODO: proper error */
}
*pvidx = vidx;
return (0);
}
/*
* Drops value reference for unused values (updates, deletes, partially
* successful adds or rollbacks).
*/
void
ipfw_garbage_table_values(struct ip_fw_chain *ch, struct table_config *tc,
struct tentry_info *tei, uint32_t count, int rollback)
{
int i;
struct tentry_info *ptei;
struct table_value *pval;
struct namedobj_instance *vi;
/*
* We have two slightly different ADD cases here:
* either (1) we are successful / partially successful,
* in that case we need
* * to ignore ADDED entries values
* * rollback every other values (either UPDATED since
* old value has been stored there, or some failure like
* EXISTS or LIMIT or simply "ignored" case.
*
* (2): atomic rollback of partially successful operation
* in that case we simply need to unref all entries.
*
* DELETE case is simpler: no atomic support there, so
* we simply unref all non-zero values.
*/
/*
* Get current table value pointers.
* XXX: Properly read vshared
*/
get_value_ptrs(ch, tc, 1, &pval, &vi);
for (i = 0; i < count; i++) {
ptei = &tei[i];
if (ptei->value == 0) {
/*
* We may be deleting non-existing record.
* Skip.
*/
continue;
}
if ((ptei->flags & TEI_FLAGS_ADDED) != 0 && rollback == 0) {
ptei->value = 0;
continue;
}
unref_table_value(vi, pval, ptei->value);
ptei->value = 0;
}
}
/*
* Main function used to link values of entries going to be added,
* to the index. Since we may perform many UH locks drops/acquires,
* handle changes by checking tablestate "modified" field.
*
* Success: return 0.
*/
int
ipfw_link_table_values(struct ip_fw_chain *ch, struct tableop_state *ts)
{
int error, i, found;
struct namedobj_instance *vi;
struct table_config *tc;
struct tentry_info *tei, *ptei;
uint32_t count, vlimit;
uint16_t vidx;
struct table_val_link *ptv;
struct table_value tval, *pval;
/*
* Stage 1: reference all existing values and
* save their indices.
*/
IPFW_UH_WLOCK_ASSERT(ch);
get_value_ptrs(ch, ts->tc, ts->vshared, &pval, &vi);
error = 0;
found = 0;
vlimit = ts->ta->vlimit;
vidx = 0;
tc = ts->tc;
tei = ts->tei;
count = ts->count;
for (i = 0; i < count; i++) {
ptei = &tei[i];
ptei->value = 0; /* Ensure value is always 0 in the beginnig */
mask_table_value(ptei->pvalue, &tval, ts->vmask);
ptv = (struct table_val_link *)ipfw_objhash_lookup_name(vi, 0,
(char *)&tval);
if (ptv == NULL)
continue;
/* Deal with vlimit later */
if (vlimit > 0 && vlimit <= ptv->no.kidx)
continue;
/* Value found. Bump refcount */
ptv->pval->refcnt++;
ptei->value = ptv->no.kidx;
found++;
}
if (ts->count == found) {
/* We've found all values , no need ts create new ones */
return (0);
}
/*
* we have added some state here, let's attach operation
* state ts the list ts be able ts rollback if necessary.
*/
add_toperation_state(ch, ts);
/* Ensure table won't disappear */
tc_ref(tc);
IPFW_UH_WUNLOCK(ch);
/*
* Stage 2: allocate objects for non-existing values.
*/
for (i = 0; i < count; i++) {
ptei = &tei[i];
if (ptei->value != 0)
continue;
if (ptei->ptv != NULL)
continue;
ptei->ptv = malloc(sizeof(struct table_val_link), M_IPFW,
M_WAITOK | M_ZERO);
}
/*
* Stage 3: allocate index numbers for new values
* and link them to index.
*/
IPFW_UH_WLOCK(ch);
tc_unref(tc);
del_toperation_state(ch, ts);
if (ts->modified != 0) {
/*
* In general, we should free all state/indexes here
* and return. However, we keep allocated state instead
* to ensure we achieve some progress on each restart.
*/
return (0);
}
KASSERT(pval == ch->tablestate, ("resize_storage() notify failure"));
/* Let's try to link values */
for (i = 0; i < count; i++) {
ptei = &tei[i];
if (ptei->value != 0) {
/*
* We may be here after several process restarts,
* so we need to update all fields that might
* have changed.
*/
ptv = (struct table_val_link *)ptei->ptv;
ptv->pval = &pval[i];
continue;
}
/* Check if record has appeared */
mask_table_value(ptei->pvalue, &tval, ts->vmask);
ptv = (struct table_val_link *)ipfw_objhash_lookup_name(vi, 0,
(char *)&tval);
if (ptv != NULL) {
ptv->pval->refcnt++;
ptei->value = ptv->no.kidx;
continue;
}
/* May perform UH unlock/lock */
error = alloc_table_vidx(ch, ts, vi, &vidx);
if (error != 0) {
ts->opstate.func(ts->tc, &ts->opstate);
return (error);
}
/* value storage resize has happened, return */
if (ts->modified != 0)
return (0);
/* Finally, we have allocated valid index, let's add entry */
ptei->value = vidx;
ptv = (struct table_val_link *)ptei->ptv;
ptei->ptv = NULL;
ptv->no.kidx = vidx;
ptv->no.name = (char *)&pval[vidx];
ptv->pval = &pval[vidx];
memcpy(ptv->pval, &tval, sizeof(struct table_value));
pval[vidx].refcnt = 1;
ipfw_objhash_add(vi, &ptv->no);
}
return (0);
}
/*
* Compability function used to import data from old
* IP_FW_TABLE_ADD / IP_FW_TABLE_XADD opcodes.
*/
void
ipfw_import_table_value_legacy(uint32_t value, struct table_value *v)
{
memset(v, 0, sizeof(*v));
v->tag = value;
v->pipe = value;
v->divert = value;
v->skipto = value;
v->netgraph = value;
v->fib = value;
v->nat = value;
v->nh4 = value; /* host format */
v->dscp = value;
v->limit = value;
}
/*
* Export data to legacy table dumps opcodes.
*/
uint32_t
ipfw_export_table_value_legacy(struct table_value *v)
{
/*
* TODO: provide more compatibility depending on
* vmask value.
*/
return (v->tag);
}
/*
* Imports table value from current userland format.
* Saves value in kernel format to the same place.
*/
void
ipfw_import_table_value_v1(ipfw_table_value *iv)
{
struct table_value v;
memset(&v, 0, sizeof(v));
v.tag = iv->tag;
v.pipe = iv->pipe;
v.divert = iv->divert;
v.skipto = iv->skipto;
v.netgraph = iv->netgraph;
v.fib = iv->fib;
v.nat = iv->nat;
v.dscp = iv->dscp;
v.nh4 = iv->nh4;
v.nh6 = iv->nh6;
v.limit = iv->limit;
memcpy(iv, &v, sizeof(ipfw_table_value));
}
/*
* Export real table value @v to current userland format.
* Note that @v and @piv may point to the same memory.
*/
void
ipfw_export_table_value_v1(struct table_value *v, ipfw_table_value *piv)
{
ipfw_table_value iv;
memset(&iv, 0, sizeof(iv));
iv.tag = v->tag;
iv.pipe = v->pipe;
iv.divert = v->divert;
iv.skipto = v->skipto;
iv.netgraph = v->netgraph;
iv.fib = v->fib;
iv.nat = v->nat;
iv.dscp = v->dscp;
iv.limit = v->limit;
iv.nh4 = v->nh4;
iv.nh6 = v->nh6;
memcpy(piv, &iv, sizeof(iv));
}
/*
* Exports real value data into ipfw_table_value structure.
* Utilizes "spare1" field to store kernel index.
*/
static void
dump_tvalue(struct namedobj_instance *ni, struct named_object *no, void *arg)
{
struct vdump_args *da;
struct table_val_link *ptv;
struct table_value *v;
da = (struct vdump_args *)arg;
ptv = (struct table_val_link *)no;
v = (struct table_value *)ipfw_get_sopt_space(da->sd, sizeof(*v));
/* Out of memory, returning */
if (v == NULL) {
da->error = ENOMEM;
return;
}
memcpy(v, ptv->pval, sizeof(*v));
v->spare1 = ptv->no.kidx;
}
/*
* Dumps all shared/table value data
* Data layout (v1)(current):
* Request: [ ipfw_obj_lheader ], size = ipfw_obj_lheader.size
* Reply: [ ipfw_obj_lheader ipfw_table_value x N ]
*
* Returns 0 on success
*/
static int
list_table_values(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
struct sockopt_data *sd)
{
struct _ipfw_obj_lheader *olh;
struct namedobj_instance *vi;
struct vdump_args da;
uint32_t count, size;
olh = (struct _ipfw_obj_lheader *)ipfw_get_sopt_header(sd,sizeof(*olh));
if (olh == NULL)
return (EINVAL);
if (sd->valsize < olh->size)
return (EINVAL);
IPFW_UH_RLOCK(ch);
vi = CHAIN_TO_VI(ch);
count = ipfw_objhash_count(vi);
size = count * sizeof(ipfw_table_value) + sizeof(ipfw_obj_lheader);
/* Fill in header regadless of buffer size */
olh->count = count;
olh->objsize = sizeof(ipfw_table_value);
if (size > olh->size) {
olh->size = size;
IPFW_UH_RUNLOCK(ch);
return (ENOMEM);
}
olh->size = size;
/*
* Do the actual value dump
*/
memset(&da, 0, sizeof(da));
da.ch = ch;
da.sd = sd;
ipfw_objhash_foreach(vi, dump_tvalue, &da);
IPFW_UH_RUNLOCK(ch);
return (0);
}
void
ipfw_table_value_init(struct ip_fw_chain *ch, int first)
{
struct tables_config *tcfg;
ch->valuestate = malloc(VALDATA_START_SIZE * sizeof(struct table_value),
M_IPFW, M_WAITOK | M_ZERO);
tcfg = ch->tblcfg;
tcfg->val_size = VALDATA_START_SIZE;
tcfg->valhash = ipfw_objhash_create(tcfg->val_size);
ipfw_objhash_set_funcs(tcfg->valhash, hash_table_value,
cmp_table_value);
IPFW_ADD_SOPT_HANDLER(first, scodes);
}
static void
destroy_value(struct namedobj_instance *ni, struct named_object *no,
void *arg)
{
free(no, M_IPFW);
}
void
ipfw_table_value_destroy(struct ip_fw_chain *ch, int last)
{
IPFW_DEL_SOPT_HANDLER(last, scodes);
free(ch->valuestate, M_IPFW);
ipfw_objhash_foreach(CHAIN_TO_VI(ch), destroy_value, ch);
ipfw_objhash_destroy(CHAIN_TO_VI(ch));
}