Skip to content

Commit a3b5cd0

Browse files
authored
Replace 'stateful-ends' with more permissive 'stateful-all'. (#30)
1 parent e071e6b commit a3b5cd0

File tree

9 files changed

+48
-43
lines changed

9 files changed

+48
-43
lines changed

docs/stateful.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ http://www.usenix.org/events/sec01/invitedtalks/rooij.pdf) paper.
2525

2626
NPF is a stateful packet filter capable of tracking TCP connections,
2727
as well as performing limited UDP and ICMP tracking. Stateful filtering is
28-
enabled using the `stateful` or `stateful-ends` keywords. The former creates
28+
enabled using the `stateful` or `stateful-all` keywords. The former creates
2929
a state which is uniquely identified by a 5-tuple (source and destination IP
3030
addresses, port numbers and an interface identifier). The latter excludes
3131
the interface identifier and must be used with precaution. Once the state is
@@ -59,17 +59,17 @@ IMPORTANT: Stateful rules imply `flags S/SAFR` for TCP packets.
5959

6060
---
6161

62-
It is important to understand the implications of `stateful-ends`. Bypassing
62+
It is important to understand the implications of `stateful-all`. Bypassing
6363
the ruleset on other interfaces can have undesirable effects, e.g. a packet
6464
with a spoofed IP address might bypass ingress filtering. Associating a state
6565
with two interfaces (forwarding case) may also cause problems if the routes
6666
change. On the other hand, picking up the state on any interface may lead
6767
to higher performance in certain configurations and may also handle some
6868
asymmetric routing cases. The administrator is free to choose whether
69-
`stateful` or `stateful-ends` is more suitable.
69+
`stateful` or `stateful-all` is more suitable.
7070

7171
---
72-
WARNING: The `stateful-ends` keyword must be used with precaution.
72+
WARNING: The `stateful-all` keyword must be used with precaution.
7373

7474
---
7575

src/kern/npf.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ bool npf_autounload_p(void);
203203
#define NPF_RULE_RETRST 0x00000010
204204
#define NPF_RULE_RETICMP 0x00000020
205205
#define NPF_RULE_DYNAMIC 0x00000040
206-
#define NPF_RULE_MULTIENDS 0x00000080
206+
#define NPF_RULE_GSTATEFUL 0x00000080
207207

208208
#define NPF_DYNAMIC_GROUP (NPF_RULE_GROUP | NPF_RULE_DYNAMIC)
209209

src/kern/npf_conn.c

Lines changed: 28 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -264,23 +264,36 @@ conn_update_atime(npf_conn_t *con)
264264
}
265265

266266
/*
267-
* npf_conn_ok: check if the connection is active and has the right direction.
267+
* npf_conn_check: check that:
268+
*
269+
* - the connection is active;
270+
*
271+
* - the packet is travelling in the right direction with the respect
272+
* to the connection direction (if interface-id is not zero);
273+
*
274+
* - the packet is travelling on the same interface as the
275+
* connection interface (if interface-id is not zero).
268276
*/
269277
static bool
270-
npf_conn_ok(const npf_conn_t *con, const int di, bool forw)
278+
npf_conn_check(const npf_conn_t *con, const nbuf_t *nbuf,
279+
const unsigned di, const bool forw)
271280
{
272281
const uint32_t flags = con->c_flags;
282+
const unsigned ifid = con->c_ifid;
283+
bool active, pforw;
273284

274-
/* Check if connection is active and not expired. */
275-
bool ok = (flags & (CONN_ACTIVE | CONN_EXPIRE)) == CONN_ACTIVE;
276-
if (__predict_false(!ok)) {
285+
active = (flags & (CONN_ACTIVE | CONN_EXPIRE)) == CONN_ACTIVE;
286+
if (__predict_false(!active)) {
277287
return false;
278288
}
279-
280-
/* Check if the direction is consistent */
281-
bool pforw = (flags & PFIL_ALL) == (unsigned)di;
282-
if (__predict_false(forw != pforw)) {
283-
return false;
289+
if (ifid && nbuf) {
290+
pforw = (flags & PFIL_ALL) == (unsigned)di;
291+
if (__predict_false(forw != pforw)) {
292+
return false;
293+
}
294+
if (__predict_false(ifid != nbuf->nb_ifid)) {
295+
return false;
296+
}
284297
}
285298
return true;
286299
}
@@ -297,7 +310,6 @@ npf_conn_lookup(const npf_cache_t *npc, const int di, bool *forw)
297310
const nbuf_t *nbuf = npc->npc_nbuf;
298311
npf_conn_t *con;
299312
npf_connkey_t key;
300-
u_int cifid;
301313

302314
/* Construct a key and lookup for a connection in the store. */
303315
if (!npf_conn_conkey(npc, &key, true)) {
@@ -309,18 +321,8 @@ npf_conn_lookup(const npf_cache_t *npc, const int di, bool *forw)
309321
}
310322
KASSERT(npc->npc_proto == con->c_proto);
311323

312-
/* Check if connection is active and not expired. */
313-
if (!npf_conn_ok(con, di, *forw)) {
314-
atomic_dec_uint(&con->c_refcnt);
315-
return NULL;
316-
}
317-
318-
/*
319-
* Match the interface and the direction of the connection entry
320-
* and the packet.
321-
*/
322-
cifid = con->c_ifid;
323-
if (__predict_false(cifid && cifid != nbuf->nb_ifid)) {
324+
/* Extra checks for the connection and packet. */
325+
if (!npf_conn_check(con, nbuf, di, *forw)) {
324326
atomic_dec_uint(&con->c_refcnt);
325327
return NULL;
326328
}
@@ -394,7 +396,7 @@ npf_conn_inspect(npf_cache_t *npc, const int di, int *error)
394396
* => Connection will be activated on the first reference release.
395397
*/
396398
npf_conn_t *
397-
npf_conn_establish(npf_cache_t *npc, int di, bool per_if)
399+
npf_conn_establish(npf_cache_t *npc, int di, bool global)
398400
{
399401
npf_t *npf = npc->npc_ctx;
400402
const unsigned alen = npc->npc_alen;
@@ -447,7 +449,7 @@ npf_conn_establish(npf_cache_t *npc, int di, bool per_if)
447449
npf_conn_destroy(npf, con);
448450
return NULL;
449451
}
450-
con->c_ifid = per_if ? nbuf->nb_ifid : 0;
452+
con->c_ifid = global ? nbuf->nb_ifid : 0;
451453

452454
/*
453455
* Set last activity time for a new connection and acquire
@@ -914,7 +916,7 @@ npf_conn_find(npf_t *npf, const nvlist_t *idict, nvlist_t **odict)
914916
return ESRCH;
915917
}
916918
dir = dnvlist_get_number(idict, "direction", 0);
917-
if (!npf_conn_ok(con, dir, true)) {
919+
if (!npf_conn_check(con, NULL, dir, true)) {
918920
atomic_dec_uint(&con->c_refcnt);
919921
return ESRCH;
920922
}

src/kern/npf_handler.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,7 @@ npf_packet_handler(npf_t *npf, struct mbuf **mp, ifnet_t *ifp, int di)
232232
*/
233233
if ((mi.mi_retfl & NPF_RULE_STATEFUL) != 0 && !con) {
234234
con = npf_conn_establish(&npc, di,
235-
(mi.mi_retfl & NPF_RULE_MULTIENDS) == 0);
235+
(mi.mi_retfl & NPF_RULE_GSTATEFUL) == 0);
236236
if (con) {
237237
/*
238238
* Note: the reference on the rule procedure is

src/libnpf/libnpf.3

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,8 +178,11 @@ Create a state (session) on match, track the connection and pass the
178178
backwards stream (the returning packets) without the ruleset inspection.
179179
The state is uniquely identified by a 5-tuple (source and destination
180180
IP addresses, port numbers and an interface identifier).
181-
.It Dv NPF_RULE_MULTIENDS
181+
.It Dv NPF_RULE_GSTATEFUL
182182
Exclude the interface identifier from the state key i.e. use a 4-tuple.
183+
This makes the state global with the respect network interfaces.
184+
The state is also picked on packet travelling different direction that
185+
originally.
183186
.It Dv NPF_RULE_RETRST
184187
Return TCP RST packet in a case of packet block.
185188
.It Dv NPF_RULE_RETICMP

src/npfctl/npf.conf.5

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -299,12 +299,12 @@ before further processing.
299299
Stateful packet inspection is enabled using the
300300
.Cm stateful
301301
or
302-
.Cm stateful-ends
302+
.Cm stateful-all
303303
keywords.
304304
The former creates a state which is uniquely identified by a 5-tuple (source
305305
and destination IP addresses, port numbers and an interface identifier).
306-
The latter excludes the interface identifier and must be used with
307-
precaution.
306+
The latter excludes the interface identifier, i.e. making the state global,
307+
and must be used with precaution.
308308
In both cases, a full TCP state tracking is performed for TCP connections
309309
and a limited tracking for message-based protocols (UDP and ICMP).
310310
.Pp
@@ -550,7 +550,7 @@ rule-list = [ rule new-line ] rule-list
550550

551551
npf-filter = [ "family" family-opt ] [ proto ] ( "all" | filt-opts )
552552
static-rule = ( "block" [ block-opts ] | "pass" )
553-
[ "stateful" | "stateful-ends" ]
553+
[ "stateful" | "stateful-all" ]
554554
[ "in" | "out" ] [ "final" ] [ "on" interface ]
555555
( npf-filter | "pcap-filter" pcap-filter-expr )
556556
[ "apply" proc-name ]

src/npfctl/npf_parse.y

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ yyerror(const char *fmt, ...)
158158
%token SET
159159
%token SLASH
160160
%token STATEFUL
161-
%token STATEFUL_ENDS
161+
%token STATEFUL_ALL
162162
%token TABLE
163163
%token TCP
164164
%token TO
@@ -643,7 +643,7 @@ all_or_filt_opts
643643

644644
opt_stateful
645645
: STATEFUL { $$ = NPF_RULE_STATEFUL; }
646-
| STATEFUL_ENDS { $$ = NPF_RULE_STATEFUL | NPF_RULE_MULTIENDS; }
646+
| STATEFUL_ALL { $$ = NPF_RULE_STATEFUL | NPF_RULE_GSTATEFUL; }
647647
| { $$ = 0; }
648648
;
649649

src/npfctl/npf_scan.l

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ block return BLOCK;
125125
pass return PASS;
126126
pcap-filter return PCAP_FILTER;
127127
stateful return STATEFUL;
128-
stateful-ends return STATEFUL_ENDS;
128+
stateful-all return STATEFUL_ALL;
129129
apply return APPLY;
130130
final return FINAL;
131131
quick return FINAL;

src/npfctl/npf_show.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,7 @@ print_portrange(npf_conf_info_t *ctx, const uint32_t *words)
246246
*/
247247

248248
#define F(name) __CONCAT(NPF_RULE_, name)
249-
#define STATEFUL_ENDS (NPF_RULE_STATEFUL | NPF_RULE_MULTIENDS)
249+
#define STATEFUL_ALL (NPF_RULE_STATEFUL | NPF_RULE_GSTATEFUL)
250250
#define NAME_AT 2
251251

252252
static const struct attr_keyword_mapent {
@@ -261,8 +261,8 @@ static const struct attr_keyword_mapent {
261261
{ F(RETRST)|F(RETICMP), F(RETRST)|F(RETICMP), "return" },
262262
{ F(RETRST)|F(RETICMP), F(RETRST), "return-rst" },
263263
{ F(RETRST)|F(RETICMP), F(RETICMP), "return-icmp" },
264-
{ STATEFUL_ENDS, F(STATEFUL), "stateful" },
265-
{ STATEFUL_ENDS, STATEFUL_ENDS, "stateful-ends" },
264+
{ STATEFUL_ALL, F(STATEFUL), "stateful" },
265+
{ STATEFUL_ALL, STATEFUL_ALL, "stateful-all" },
266266
{ F(DIMASK), F(IN), "in" },
267267
{ F(DIMASK), F(OUT), "out" },
268268
{ F(FINAL), F(FINAL), "final" },

0 commit comments

Comments
 (0)