NAME
npf.conf —
NPF packet filter
configuration file
DESCRIPTION
npf.conf is the default configuration file for the NPF packet
filter.
This manual page serves as a reference for editing
npf.conf.
Please refer to the official NPF documentation website for comprehensive and
in-depth information.
There are multiple structural elements that
npf.conf may
contain, such as:
- variables
- table definitions (with or
without content)
- abstraction groups
- packet filtering rules
- map rules for address
translation
- application level
gateways
- procedure definitions to
call on filtered packets.
SYNTAX
Variables
Variables are specified using the dollar (
$
) sign, which
is used for both definition and referencing of a variable. Variables are
defined by assigning a value to them as follows:
$var1 = 10.0.0.1
A variable may also be defined as a set:
$var2 = { 10.0.0.1, 10.0.0.2 }
Common variable definitions are for IP addresses, networks, ports, and
interfaces.
Tables
Tables are specified using a name between angle brackets
‘
<
’ and
‘
>
’. The following is an example of
table definition:
table <black> type hash
dynamic
Currently, tables support three data storage types:
hash,
tree, or
cdb. Tables can also be set as
containing
dynamic data or static
file
filename data loaded from a specified file. Tables of
type “hash” and “cdb” can only contain IP addresses.
Only static data can be used with a storage type of “cdb”.
The specified file should contain a list of IP addresses and/or networks in the
form of
10.1.1.1
or
10.0.0.0/24
Interfaces
In NPF, an interface can be referenced directly by using its name, or can be
passed to an extraction function which will return a list of IP addresses
configured on the actual associated interface.
It is legal to pass an extracted list from an interface in keywords where NPF
would expect instead a direct reference to said interface. In this case, NPF
infers a direct reference to the interface, and does not consider the list.
There are two types of IP address lists. With a static list, NPF will capture
the interface addresses on configuration load, whereas with a dynamic list NPF
will capture the runtime list of addresses, reflecting any changes to the
interface, including the attach and detach. Note that with a dynamic list,
bringing the interface down has no effect, all addresses will remain present.
Three functions exist, to extract addresses from an interface with a chosen list
type and IP address type:
-
-
- inet4(interface)
- Static list. IPv4 addresses.
-
-
- inet6(interface)
- Static list. IPv6 addresses.
-
-
- ifaddrs(interface)
- Dynamic list. Both IPv4 and IPv6. The
family keyword of a filtering rule can be used in
combination to explicitly select an IP address type.
Example of configuration:
$var1 = inet4(wm0)
$var2 = ifaddrs(wm0)
group default {
block in on wm0 all # rule 1
block in on $var1 all # rule 2
block in on inet4(wm0) all # rule 3
pass in on inet6(wm0) from $var2 # rule 4
pass in on wm0 from ifaddrs(wm0) # rule 5
}
In the above example,
$var1
is the static list of IPv4
addresses configured on wm0, and
$var2
is the dynamic
list of all the IPv4 and IPv6 addresses configured on wm0. The first three
rules are equivalent, because with the
block
... on
<
interface>
syntax, NPF expects a direct reference to an interface, and therefore does not
consider the extraction functions. The fourth and fifth rules are equivalent,
for the same reason.
Groups
NPF requires that all rules be defined within groups. Groups can be thought of
as higher level rules which can contain subrules. Groups may have the
following options: name, interface, and direction. Packets matching group
criteria are passed to the ruleset of that group. If a packet does not match
any group, it is passed to the
default
group. The
default
group must always be defined.
Example of configuration:
group "my-name" in on wm0 {
# List of rules, for packets received on wm0
}
group default {
# List of rules, for the other packets
}
Rules
With a rule statement NPF is instructed to
pass or
block a packet depending on packet header information,
transit direction and the interface it arrived on, either immediately upon
match or using the last match.
If a packet matches a rule which has the
final option set,
this rule is considered the last matching rule, and evaluation of subsequent
rules is skipped. Otherwise, the last matching rule is used.
The
proto keyword can be used to filter packets by layer 4
protocol (TCP, UDP, ICMP or other). Its parameter should be a protocol number
or its symbolic name, as specified in the
/etc/protocols
file. This keyword can additionally have protocol-specific options, such as
flags.
The
flags keyword can be used to match the packets against
specific TCP flags, according to the following syntax:
proto tcp
flags
match[/
mask
]
Where
match is the set of TCP flags to be matched, out of
the
mask set, both sets being represented as a string
combination of: ‘
S’ (SYN),
‘
A’ (ACK), ‘
F’
(FIN), and ‘
R’ (RST). The flags that are not
present in
mask are ignored.
To notify the sender of a blocking decision, three
return
options can be used in conjunction with a
block rule:
-
-
- return
- Behaves as return-rst or
return-icmp, depending on whether the packet being
blocked is TCP or UDP.
-
-
- return-rst
- Return a TCP RST message, when the packet being blocked is
a TCP packet. Applies to IPv4 and IPv6.
-
-
- return-icmp
- Return an ICMP UNREACHABLE message, when the packet being
blocked is a UDP packet. Applies to IPv4 and IPv6.
Further packet specification at present is limited to TCP and UDP understanding
source and destination ports, and ICMP and IPv6-ICMP understanding icmp-type.
A rule can also instruct NPF to create an entry in the state table when passing
the packet or to apply a procedure to the packet (e.g. "log").
A “fully-featured” rule would for example be:
pass stateful in final family inet4 proto tcp flags S/SA \
from $source port $sport to $dest port $dport \
apply "someproc"
Alternatively, NPF supports
pcap-filter(7) syntax, for
example:
block out final pcap-filter "tcp and dst
10.1.1.252"
Fragments are not selectable since NPF always reassembles packets before further
processing.
Stateful
Stateful packet inspection is enabled using the
stateful or
stateful-ends keywords. The former creates a state which is
uniquely identified by a 5-tuple (source and destination IP addresses, port
numbers and an interface identifier). The latter excludes the interface
identifier and must be used with precaution. In both cases, a full TCP state
tracking is performed for TCP connections and a limited tracking for
message-based protocols (UDP and ICMP).
By default, a stateful rule implies SYN-only flag check
(“
flags S/SAFR
”) for the TCP packets. It
is not advisable to change this behavior; however, it can be overridden with
the aforementioned
flags keyword.
Map
Network Address Translation (NAT) is expressed in a form of segment mapping. The
translation may be
dynamic (stateful) or
static (stateless). The following mapping types are
available:
- ->
- outbound NAT (translation of the source)
- <-
- inbound NAT (translation of the destination)
- <->
- bi-directional NAT (combination of inbound and outbound
NAT)
The following would translate the source (10.1.1.0/24) to the IP address
specified by
$pub_ip
for the packets on the interface
$ext_if
.
map $ext_if dynamic 10.1.1.0/24 ->
$pub_ip
Translations are implicitly filtered by limiting the operation to the network
segments specified, that is, translation would be performed only on packets
originating from the 10.1.1.0/24 network. Explicit filter criteria can be
specified using
pass criteria ... as
an additional option of the mapping.
The dynamic NAT implies network address and port translation (NAPT). The port
translation can be controlled explicitly. For example, the following provides
“port forwarding”, redirecting the public port 9022 to the port 22
of an internal host:
map $ext_if dynamic proto tcp 10.1.1.2 port 22
<- $ext_if port 9022
The static NAT can have different address translation algorithms, which can be
chosen using the
algo keyword. The currently available
algorithms are:
-
-
- npt66
- IPv6-to-IPv6 network prefix translation (NPTv6).
Currently, the static NAT algorithms do not perform port translation.
Application Level Gateways
Certain application layer protocols are not compatible with NAT and require
translation outside layers 3 and 4. Such translation is performed by packet
filter extensions called Application Level Gateways (ALGs).
NPF supports the following ALGs:
-
-
- icmp
- ICMP ALG. Applies to IPv4 and IPv6. Allows to find an
active connection by looking at the ICMP payload, and to perform NAT
translation of the ICMP payload. Generally, this ALG is necessary to
support traceroute(8)
behind the NAT, when using the UDP or TCP probes.
The ALGs are built-in. If NPF is used as kernel module, then they come as kernel
modules too. In such case, the ALG kernel modules can be autoloaded through
the configuration, using the
alg keyword.
For example:
alg "icmp"
Alternatively, the ALG kernel modules can be loaded manually, using
modload(8).
Procedures
A rule procedure is defined as a collection of extension calls (it may have
none). Every extension call has a name and a list of options in the form of
key-value pairs. Depending on the call, the key might represent the argument
and the value might be optional. Available options:
-
-
- log:
interface
- Log events. This requires the npf_ext_log
kernel module, which would normally get auto-loaded by NPF. The specified
npflog interface would also be auto-created once the configuration is
loaded. The log packets can be written to a file using the
npfd(8) daemon.
-
-
- normalize:
option1[
,
option2 ...]
- Modify packets according to the specified normalization
options. This requires the npf_ext_normalize kernel
module, which would normally get auto-loaded by NPF.
The available normalization options are:
-
-
- "max-mss"
value
- Enforce a maximum value for the Maximum Segment Size (MSS)
TCP option. Typically, for “MSS clamping”.
-
-
- "min-ttl"
value
- Enforce a minimum value for the IPv4 Time To Live (TTL)
parameter.
-
-
- "no-df"
- Remove the Don't Fragment (DF) flag from IPv4 packets.
-
-
- "random-id"
- Randomize the IPv4 ID parameter.
For example:
procedure "someproc" {
log: npflog0
normalize: "random-id", "min-ttl" 64, "max-mss" 1432
}
In this case, the procedure calls the logging and normalization modules.
Misc
Text after a hash (‘#’) character is considered a comment. The
backslash (‘\’) character at the end of a line marks a
continuation line, i.e., the next line is considered an extension of the
present line.
GRAMMAR
The following is a non-formal BNF-like definition of the grammar. The definition
is simplified and is intended to be human readable, therefore it does not
strictly represent the formal grammar.
# Syntax of a single line. Lines can be separated by LF (\n) or
# a semicolon. Comments start with a hash (#) character.
syntax = var-def | set-param | alg | table-def |
map | group | proc | comment
# Variable definition. Names can be alpha-numeric, including "_"
# character.
var-name = "$" . string
interface = interface-name | var-name
var-def = var "=" ( var-value | "{" value *[ "," value ] "}" )
# Parameter setting.
set-param = "set" param-value
# Application level gateway. The name should be in double quotes.
alg = "alg" alg-name
alg-name = "icmp"
# Table definition. Table ID shall be numeric. Path is in the
# double quotes.
table-id = <table-name>
table-def = "table" table-id "type" ( "hash" | "tree" | "cdb" )
( "dynamic" | "file" path )
# Mapping for address translation.
map = "map" interface
( "static" [ "algo" map-algo ] | "dynamic" )
[ proto ]
map-seg ( "->" | "<-" | "<->" ) map-seg
[ "pass" [ proto ] filt-opts ]
map-algo = "npt66"
map-seg = ( addr-mask | interface ) [ port-opts ]
# Rule procedure definition. The name should be in the double quotes.
#
# Each call can have its own options in a form of key-value pairs.
# Both key and values may be strings (either in double quotes or not)
# and numbers, depending on the extension.
proc = "procedure" proc-name "{" *( proc-call [ new-line ] ) "}"
proc-opts = key [ " " val ] [ "," proc-opts ]
proc-call = call-name ":" proc-opts new-line
# Group definition and the rule list.
group = "group" ( "default" | group-opts ) "{" rule-list "}"
group-opts = name-string [ "in" | "out" ] [ "on" interface ]
rule-list = [ rule new-line ] rule-list
npf-filter = [ "family" family-opt ] [ proto ] ( "all" | filt-opts )
static-rule = ( "block" [ block-opts ] | "pass" )
[ "stateful" | "stateful-ends" ]
[ "in" | "out" ] [ "final" ] [ "on" interface ]
( npf-filter | "pcap-filter" pcap-filter-expr )
[ "apply" proc-name ]
dynamic-ruleset = "ruleset" group-opts
rule = static-rule | dynamic-ruleset
tcp-flag-mask = tcp-flags
tcp-flags = [ "S" ] [ "A" ] [ "F" ] [ "R" ]
block-opts = "return-rst" | "return-icmp" | "return"
family-opt = "inet4" | "inet6"
proto-opts = "flags" tcp-flags [ "/" tcp-flag-mask ] |
"icmp-type" type [ "code" icmp-code ]
proto = "proto" protocol [ proto-opts ]
filt-opts = "from" filt-addr [ port-opts ] "to" filt-addr
[ port-opts ]
filt-addr = [ "!" ] [ interface | addr-mask | table-id | "any" ]
port-opts = "port" ( port-num | port-from "-" port-to | var-name )
addr-mask = addr [ "/" mask ]
FILES
- /dev/npf
- control device
- /etc/npf.conf
- default configuration file
- /usr/share/examples/npf
- directory containing further examples
EXAMPLES
$ext_if = { inet4(wm0) }
$int_if = { inet4(wm1) }
table <blacklist> type hash file "/etc/npf_blacklist"
table <limited> type tree dynamic
$services_tcp = { http, https, smtp, domain, 6000, 9022 }
$services_udp = { domain, ntp, 6000 }
$localnet = { 10.1.1.0/24 }
alg "icmp"
# Note: if $ext_if has multiple IP address (e.g. IPv6 as well),
# then the translation address has to be specified explicitly.
map $ext_if dynamic 10.1.1.0/24 -> $ext_if
map $ext_if dynamic proto tcp 10.1.1.2 port 22 <- $ext_if port 9022
procedure "log" {
# The logging facility can be used together with npfd(8).
log: npflog0
}
group "external" on $ext_if {
pass stateful out final all
block in final from <blacklist>
pass stateful in final family inet4 proto tcp to $ext_if \
port ssh apply "log"
pass stateful in final proto tcp to $ext_if \
port $services_tcp
pass stateful in final proto udp to $ext_if \
port $services_udp
pass stateful in final proto tcp to $ext_if \
port 49151-65535 # passive FTP
pass stateful in final proto udp to $ext_if \
port 33434-33600 # traceroute
}
group "internal" on $int_if {
block in all
block in final from <limited>
# Ingress filtering as per BCP 38 / RFC 2827.
pass in final from $localnet
pass out final all
}
group default {
pass final on lo0 all
block all
}
SEE ALSO
bpf(4),
npf(7),
pcap-filter(7),
npfctl(8),
npfd(8)
NPF documentation
website
HISTORY
NPF first appeared in
NetBSD 6.0.
AUTHORS
NPF was designed and implemented by
Mindaugas
Rasiukevicius.