Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F146584645
D52447.id161785.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
60 KB
Referenced Files
None
Subscribers
None
D52447.id161785.diff
View Options
diff --git a/sys/net/pfvar.h b/sys/net/pfvar.h
--- a/sys/net/pfvar.h
+++ b/sys/net/pfvar.h
@@ -1155,7 +1155,6 @@
int rewrite;
u_short reason;
struct pf_src_node *sns[PF_SN_MAX];
- struct pf_krule_slist rules;
struct pf_krule *nr;
struct pf_krule *tr;
struct pf_krule **rm;
@@ -2666,8 +2665,10 @@
#ifdef _KERNEL
void pf_print_host(struct pf_addr *, u_int16_t, sa_family_t);
-enum pf_test_status pf_step_into_anchor(struct pf_test_ctx *, struct pf_krule *);
-enum pf_test_status pf_match_rule(struct pf_test_ctx *, struct pf_kruleset *);
+enum pf_test_status pf_step_into_anchor(struct pf_test_ctx *, struct pf_krule *,
+ struct pf_krule_slist *match_rules);
+enum pf_test_status pf_match_rule(struct pf_test_ctx *, struct pf_kruleset *,
+ struct pf_krule_slist *);
void pf_step_into_keth_anchor(struct pf_keth_anchor_stackframe *,
int *, struct pf_keth_ruleset **,
struct pf_keth_rule **, struct pf_keth_rule **,
diff --git a/sys/netpfil/pf/pf.c b/sys/netpfil/pf/pf.c
--- a/sys/netpfil/pf/pf.c
+++ b/sys/netpfil/pf/pf.c
@@ -344,10 +344,12 @@
struct mbuf **);
static int pf_test_rule(struct pf_krule **, struct pf_kstate **,
struct pf_pdesc *, struct pf_krule **,
- struct pf_kruleset **, u_short *, struct inpcb *);
+ struct pf_kruleset **, u_short *, struct inpcb *,
+ struct pf_krule_slist *);
static int pf_create_state(struct pf_krule *,
struct pf_test_ctx *,
- struct pf_kstate **, u_int16_t, u_int16_t);
+ struct pf_kstate **, u_int16_t, u_int16_t,
+ struct pf_krule_slist *match_rules);
static int pf_state_key_addr_setup(struct pf_pdesc *,
struct pf_state_key_cmp *, int);
static int pf_tcp_track_full(struct pf_kstate *,
@@ -393,7 +395,7 @@
static int pf_match_rcvif(struct mbuf *, struct pf_krule *);
static void pf_counters_inc(int, struct pf_pdesc *,
struct pf_kstate *, struct pf_krule *,
- struct pf_krule *);
+ struct pf_krule *, struct pf_krule_slist *);
static void pf_log_matches(struct pf_pdesc *, struct pf_krule *,
struct pf_krule *, struct pf_kruleset *,
struct pf_krule_slist *);
@@ -489,26 +491,30 @@
counter_u64_add(s->anchor->states_cur, 1); \
counter_u64_add(s->anchor->states_tot, 1); \
} \
- if (s->nat_rule != NULL) { \
- counter_u64_add(s->nat_rule->states_cur, 1);\
- counter_u64_add(s->nat_rule->states_tot, 1);\
+ if (s->nat_rule != NULL && s->nat_rule != s->rule) { \
+ counter_u64_add(s->nat_rule->states_cur, 1); \
+ counter_u64_add(s->nat_rule->states_tot, 1); \
} \
SLIST_FOREACH(mrm, &s->match_rules, entry) { \
- counter_u64_add(mrm->r->states_cur, 1); \
- counter_u64_add(mrm->r->states_tot, 1); \
+ if (s->nat_rule != mrm->r) { \
+ counter_u64_add(mrm->r->states_cur, 1); \
+ counter_u64_add(mrm->r->states_tot, 1); \
+ } \
} \
} while (0)
#define STATE_DEC_COUNTERS(s) \
do { \
struct pf_krule_item *mrm; \
- if (s->nat_rule != NULL) \
- counter_u64_add(s->nat_rule->states_cur, -1);\
- if (s->anchor != NULL) \
- counter_u64_add(s->anchor->states_cur, -1); \
counter_u64_add(s->rule->states_cur, -1); \
+ if (s->anchor != NULL) \
+ counter_u64_add(s->anchor->states_cur, -1); \
+ if (s->nat_rule != NULL && s->nat_rule != s->rule) \
+ counter_u64_add(s->nat_rule->states_cur, -1); \
SLIST_FOREACH(mrm, &s->match_rules, entry) \
- counter_u64_add(mrm->r->states_cur, -1); \
+ if (s->nat_rule != mrm->r) { \
+ counter_u64_add(mrm->r->states_cur, -1);\
+ } \
} while (0)
MALLOC_DEFINE(M_PFHASH, "pf_hash", "pf(4) hash header structures");
@@ -2869,20 +2875,24 @@
return (uma_zalloc(V_pf_state_z, flags | M_ZERO));
}
+static __inline void
+pf_free_match_rules(struct pf_krule_slist *match_rules) {
+ struct pf_krule_item *ri;
+
+ while ((ri = SLIST_FIRST(match_rules))) {
+ SLIST_REMOVE_HEAD(match_rules, entry);
+ free(ri, M_PF_RULE_ITEM);
+ }
+}
+
void
pf_free_state(struct pf_kstate *cur)
{
- struct pf_krule_item *ri;
-
KASSERT(cur->refs == 0, ("%s: %p has refs", __func__, cur));
KASSERT(cur->timeout == PFTM_UNLINKED, ("%s: timeout %u", __func__,
cur->timeout));
- while ((ri = SLIST_FIRST(&cur->match_rules))) {
- SLIST_REMOVE_HEAD(&cur->match_rules, entry);
- free(ri, M_PF_RULE_ITEM);
- }
-
+ pf_free_match_rules(&(cur->match_rules));
pf_normalize_tcp_cleanup(cur);
uma_zfree(V_pf_state_z, cur);
pf_counter_u64_add(&V_pf_status.fcounters[FCNT_STATE_REMOVALS], 1);
@@ -4733,7 +4743,8 @@
} while (0)
enum pf_test_status
-pf_step_into_anchor(struct pf_test_ctx *ctx, struct pf_krule *r)
+pf_step_into_anchor(struct pf_test_ctx *ctx, struct pf_krule *r,
+ struct pf_krule_slist *match_rules)
{
enum pf_test_status rv;
@@ -4751,7 +4762,7 @@
struct pf_kanchor *child;
rv = PF_TEST_OK;
RB_FOREACH(child, pf_kanchor_node, &r->anchor->children) {
- rv = pf_match_rule(ctx, &child->ruleset);
+ rv = pf_match_rule(ctx, &child->ruleset, match_rules);
if ((rv == PF_TEST_QUICK) || (rv == PF_TEST_FAIL)) {
/*
* we either hit a rule with quick action
@@ -4762,7 +4773,7 @@
}
}
} else {
- rv = pf_match_rule(ctx, &r->anchor->ruleset);
+ rv = pf_match_rule(ctx, &r->anchor->ruleset, match_rules);
/*
* Unless errors occured, stop iff any rule matched
* within quick anchors.
@@ -5611,9 +5622,10 @@
}
enum pf_test_status
-pf_match_rule(struct pf_test_ctx *ctx, struct pf_kruleset *ruleset)
+pf_match_rule(struct pf_test_ctx *ctx, struct pf_kruleset *ruleset,
+ struct pf_krule_slist *match_rules)
{
- struct pf_krule_item *ri;
+ struct pf_krule_item *ri, *rt;
struct pf_krule *r;
struct pf_krule *save_a;
struct pf_kruleset *save_aruleset;
@@ -5752,11 +5764,15 @@
return (PF_TEST_FAIL);
}
ri->r = r;
- SLIST_INSERT_HEAD(&ctx->rules, ri, entry);
- pf_counter_u64_critical_enter();
- pf_counter_u64_add_protected(&r->packets[pd->dir == PF_OUT], 1);
- pf_counter_u64_add_protected(&r->bytes[pd->dir == PF_OUT], pd->tot_len);
- pf_counter_u64_critical_exit();
+
+ if (SLIST_EMPTY(match_rules)) {
+ SLIST_INSERT_HEAD(match_rules, ri, entry);
+ rt = ri;
+ } else {
+ SLIST_INSERT_AFTER(rt, ri, entry);
+ rt = ri;
+ }
+
pf_rule_to_actions(r, &pd->act);
if (r->log)
PFLOG_PACKET(r->action, PFRES_MATCH, r,
@@ -5780,7 +5796,7 @@
ctx->arsm = ctx->aruleset;
}
if (pd->act.log & PF_LOG_MATCHES)
- pf_log_matches(pd, r, ctx->a, ruleset, &ctx->rules);
+ pf_log_matches(pd, r, ctx->a, ruleset, match_rules);
if (r->quick) {
ctx->test_status = PF_TEST_QUICK;
break;
@@ -5797,7 +5813,7 @@
* Note: we don't need to restore if we are not going
* to continue with ruleset evaluation.
*/
- if (pf_step_into_anchor(ctx, r) != PF_TEST_OK) {
+ if (pf_step_into_anchor(ctx, r, match_rules) != PF_TEST_OK) {
break;
}
ctx->a = save_a;
@@ -5812,11 +5828,11 @@
static int
pf_test_rule(struct pf_krule **rm, struct pf_kstate **sm,
struct pf_pdesc *pd, struct pf_krule **am,
- struct pf_kruleset **rsm, u_short *reason, struct inpcb *inp)
+ struct pf_kruleset **rsm, u_short *reason, struct inpcb *inp,
+ struct pf_krule_slist *match_rules)
{
struct pf_krule *r = NULL;
struct pf_kruleset *ruleset = NULL;
- struct pf_krule_item *ri;
struct pf_test_ctx ctx;
u_short transerror;
int action = PF_PASS;
@@ -5833,7 +5849,6 @@
ctx.rsm = rsm;
ctx.th = &pd->hdr.tcp;
ctx.reason = *reason;
- SLIST_INIT(&ctx.rules);
pf_addrcpy(&pd->nsaddr, pd->src, pd->af);
pf_addrcpy(&pd->ndaddr, pd->dst, pd->af);
@@ -5926,7 +5941,7 @@
}
ruleset = &pf_main_ruleset;
- rv = pf_match_rule(&ctx, ruleset);
+ rv = pf_match_rule(&ctx, ruleset, match_rules);
if (rv == PF_TEST_FAIL) {
/*
* Reason has been set in pf_match_rule() already.
@@ -5962,7 +5977,7 @@
PFLOG_PACKET(r->action, ctx.reason, r, ctx.a, ruleset, pd, 1, NULL);
}
if (pd->act.log & PF_LOG_MATCHES)
- pf_log_matches(pd, r, ctx.a, ruleset, &ctx.rules);
+ pf_log_matches(pd, r, ctx.a, ruleset, match_rules);
if (pd->virtual_proto != PF_VPROTO_FRAGMENT &&
(r->action == PF_DROP) &&
((r->rule_flag & PFRULE_RETURNRST) ||
@@ -6007,7 +6022,8 @@
(pd->flags & PFDESC_TCP_NORM)))) {
bool nat64;
- action = pf_create_state(r, &ctx, sm, bproto_sum, bip_sum);
+ action = pf_create_state(r, &ctx, sm, bproto_sum, bip_sum,
+ match_rules);
ctx.sk = ctx.nk = NULL;
if (action != PF_PASS) {
pf_udp_mapping_release(ctx.udp_mapping);
@@ -6053,11 +6069,6 @@
action = PF_AFRT;
}
} else {
- while ((ri = SLIST_FIRST(&ctx.rules))) {
- SLIST_REMOVE_HEAD(&ctx.rules, entry);
- free(ri, M_PF_RULE_ITEM);
- }
-
uma_zfree(V_pf_state_key_z, ctx.sk);
uma_zfree(V_pf_state_key_z, ctx.nk);
ctx.sk = ctx.nk = NULL;
@@ -6085,11 +6096,6 @@
return (action);
cleanup:
- while ((ri = SLIST_FIRST(&ctx.rules))) {
- SLIST_REMOVE_HEAD(&ctx.rules, entry);
- free(ri, M_PF_RULE_ITEM);
- }
-
uma_zfree(V_pf_state_key_z, ctx.sk);
uma_zfree(V_pf_state_key_z, ctx.nk);
pf_udp_mapping_release(ctx.udp_mapping);
@@ -6100,7 +6106,8 @@
static int
pf_create_state(struct pf_krule *r, struct pf_test_ctx *ctx,
- struct pf_kstate **sm, u_int16_t bproto_sum, u_int16_t bip_sum)
+ struct pf_kstate **sm, u_int16_t bproto_sum, u_int16_t bip_sum,
+ struct pf_krule_slist *match_rules)
{
struct pf_pdesc *pd = ctx->pd;
struct pf_kstate *s = NULL;
@@ -6114,7 +6121,6 @@
struct tcphdr *th = &pd->hdr.tcp;
u_int16_t mss = V_tcp_mssdflt;
u_short sn_reason;
- struct pf_krule_item *ri;
/* check maximums */
if (r->max_states &&
@@ -6166,7 +6172,7 @@
s->rule = r;
s->nat_rule = ctx->nr;
s->anchor = ctx->a;
- memcpy(&s->match_rules, &ctx->rules, sizeof(s->match_rules));
+ s->match_rules = *match_rules;
memcpy(&s->act, &pd->act, sizeof(struct pf_rule_actions));
if (pd->act.allow_opts)
@@ -6330,11 +6336,6 @@
return (PF_PASS);
csfailed:
- while ((ri = SLIST_FIRST(&ctx->rules))) {
- SLIST_REMOVE_HEAD(&ctx->rules, entry);
- free(ri, M_PF_RULE_ITEM);
- }
-
uma_zfree(V_pf_state_key_z, ctx->sk);
uma_zfree(V_pf_state_key_z, ctx->nk);
@@ -7484,6 +7485,7 @@
pf_sctp_multihome_delayed(struct pf_pdesc *pd, struct pfi_kkif *kif,
struct pf_kstate *s, int action)
{
+ struct pf_krule_slist match_rules;
struct pf_sctp_multihome_job *j, *tmp;
struct pf_sctp_source *i;
int ret;
@@ -7531,8 +7533,14 @@
if (s->rule->rule_flag & PFRULE_ALLOW_RELATED) {
j->pd.related_rule = s->rule;
}
+ SLIST_INIT(&match_rules);
ret = pf_test_rule(&r, &sm,
- &j->pd, &ra, &rs, &reason, NULL);
+ &j->pd, &ra, &rs, &reason, NULL, &match_rules);
+ /*
+ * Nothing to do about match rules, the processed
+ * packet has already increased the counters.
+ */
+ pf_free_match_rules(&match_rules);
PF_RULES_RUNLOCK();
SDT_PROBE4(pf, sctp, multihome, test, kif, r, j->pd.m, ret);
if (ret != PF_DROP && sm != NULL) {
@@ -10614,108 +10622,149 @@
return (0);
}
-static void
-pf_counters_inc(int action, struct pf_pdesc *pd,
- struct pf_kstate *s, struct pf_krule *r, struct pf_krule *a)
+static __inline void
+pf_rule_counters_inc(struct pf_pdesc *pd, struct pf_krule *r, int dir_out,
+ int op_pass, sa_family_t af, struct pf_addr *src_host,
+ struct pf_addr *dst_host)
{
- struct pf_krule *tr;
- int dir = pd->dir;
- int dirndx;
+ pf_counter_u64_add_protected(&(r->packets[dir_out]), 1);
+ pf_counter_u64_add_protected(&(r->bytes[dir_out]), pd->tot_len);
+ pf_update_timestamp(r);
+
+ if (r->src.addr.type == PF_ADDR_TABLE)
+ pfr_update_stats(r->src.addr.p.tbl, src_host, af,
+ pd->tot_len, dir_out, op_pass, r->src.neg);
+ if (r->dst.addr.type == PF_ADDR_TABLE)
+ pfr_update_stats(r->dst.addr.p.tbl, dst_host, af,
+ pd->tot_len, dir_out, op_pass, r->dst.neg);
+}
+
+static void
+pf_counters_inc(int action, struct pf_pdesc *pd, struct pf_kstate *s,
+ struct pf_krule *r, struct pf_krule *a, struct pf_krule_slist *match_rules)
+{
+ struct pf_krule_slist *mr = match_rules;
+ struct pf_krule_item *ri;
+ struct pf_krule *nr = NULL;
+ struct pf_addr *src_host = pd->src;
+ struct pf_addr *dst_host = pd->dst;
+ struct pf_state_key *key;
+ int dir_out = (pd->dir == PF_OUT);
+ int op_pass = (r->action == PF_PASS);
+ sa_family_t af = pd->af;
+ int s_dir_in, s_dir_out, s_dir_rev;
pf_counter_u64_critical_enter();
+
pf_counter_u64_add_protected(
- &pd->kif->pfik_bytes[pd->af == AF_INET6][dir == PF_OUT][action != PF_PASS],
+ &pd->kif->pfik_bytes[pd->af == AF_INET6][dir_out][action != PF_PASS],
pd->tot_len);
pf_counter_u64_add_protected(
- &pd->kif->pfik_packets[pd->af == AF_INET6][dir == PF_OUT][action != PF_PASS],
+ &pd->kif->pfik_packets[pd->af == AF_INET6][dir_out][action != PF_PASS],
1);
- if (action == PF_PASS || action == PF_AFRT || r->action == PF_DROP) {
- dirndx = (dir == PF_OUT);
- pf_counter_u64_add_protected(&r->packets[dirndx], 1);
- pf_counter_u64_add_protected(&r->bytes[dirndx], pd->tot_len);
- pf_update_timestamp(r);
+ /* If the rule has failed to apply, don't increase its counters */
+ if (!(action == PF_PASS || action == PF_AFRT || r->action == PF_DROP)) {
+ pf_counter_u64_critical_exit();
+ return;
+ }
- if (a != NULL) {
- pf_counter_u64_add_protected(&a->packets[dirndx], 1);
- pf_counter_u64_add_protected(&a->bytes[dirndx], pd->tot_len);
+ if (s != NULL) {
+ PF_STATE_LOCK_ASSERT(s);
+ mr = &(s->match_rules);
+
+ /*
+ * For af-to on the inbound direction we can determine
+ * the direction only by checking direction of AF translation,
+ * since the state is always "in" and so is packet's direction.
+ */
+ if (pd->af != pd->naf && s->direction == PF_IN) {
+ dir_out = (pd->naf == s->rule->naf);
+ s_dir_in = 1;
+ s_dir_out = 0;
+ s_dir_rev = (pd->naf != s->rule->naf);
+ }
+ else {
+ dir_out = (pd->dir == PF_OUT);
+ s_dir_in = (s->direction == PF_IN);
+ s_dir_out = (s->direction == PF_OUT);
+ s_dir_rev = (pd->dir != s->direction);
}
- if (s != NULL) {
- struct pf_krule_item *ri;
- if (s->nat_rule != NULL) {
- pf_counter_u64_add_protected(&s->nat_rule->packets[dirndx],
+ s->packets[s_dir_rev]++;
+ s->bytes[s_dir_rev] += pd->tot_len;
+
+ /*
+ * Source nodes are accessed unlocked here. But since we are
+ * operating with stateful tracking and the state is locked,
+ * those SNs could not have been freed.
+ */
+ for (pf_sn_types_t sn_type=0; sn_type<PF_SN_MAX; sn_type++) {
+ if (s->sns[sn_type] != NULL) {
+ counter_u64_add(
+ s->sns[sn_type]->packets[dir_out],
1);
- pf_counter_u64_add_protected(&s->nat_rule->bytes[dirndx],
+ counter_u64_add(
+ s->sns[sn_type]->bytes[dir_out],
pd->tot_len);
}
- /*
- * Source nodes are accessed unlocked here.
- * But since we are operating with stateful tracking
- * and the state is locked, those SNs could not have
- * been freed.
- */
- for (pf_sn_types_t sn_type=0; sn_type<PF_SN_MAX; sn_type++) {
- if (s->sns[sn_type] != NULL) {
- counter_u64_add(
- s->sns[sn_type]->packets[dirndx],
- 1);
- counter_u64_add(
- s->sns[sn_type]->bytes[dirndx],
- pd->tot_len);
- }
- }
- dirndx = (dir == s->direction) ? 0 : 1;
- s->packets[dirndx]++;
- s->bytes[dirndx] += pd->tot_len;
-
- SLIST_FOREACH(ri, &s->match_rules, entry) {
- pf_counter_u64_add_protected(&ri->r->packets[dirndx], 1);
- pf_counter_u64_add_protected(&ri->r->bytes[dirndx], pd->tot_len);
-
- if (ri->r->src.addr.type == PF_ADDR_TABLE)
- pfr_update_stats(ri->r->src.addr.p.tbl,
- (s == NULL) ? pd->src :
- &s->key[(s->direction == PF_IN)]->
- addr[(s->direction == PF_OUT)],
- pd->af, pd->tot_len, dir == PF_OUT,
- r->action == PF_PASS, ri->r->src.neg);
- if (ri->r->dst.addr.type == PF_ADDR_TABLE)
- pfr_update_stats(ri->r->dst.addr.p.tbl,
- (s == NULL) ? pd->dst :
- &s->key[(s->direction == PF_IN)]->
- addr[(s->direction == PF_IN)],
- pd->af, pd->tot_len, dir == PF_OUT,
- r->action == PF_PASS, ri->r->dst.neg);
- }
}
- tr = r;
- if (s != NULL && s->nat_rule != NULL &&
- r == &V_pf_default_rule)
- tr = s->nat_rule;
-
- if (tr->src.addr.type == PF_ADDR_TABLE)
- pfr_update_stats(tr->src.addr.p.tbl,
- (s == NULL) ? pd->src :
- &s->key[(s->direction == PF_IN)]->
- addr[(s->direction == PF_OUT)],
- pd->af, pd->tot_len, dir == PF_OUT,
- r->action == PF_PASS, tr->src.neg);
- if (tr->dst.addr.type == PF_ADDR_TABLE)
- pfr_update_stats(tr->dst.addr.p.tbl,
- (s == NULL) ? pd->dst :
- &s->key[(s->direction == PF_IN)]->
- addr[(s->direction == PF_IN)],
- pd->af, pd->tot_len, dir == PF_OUT,
- r->action == PF_PASS, tr->dst.neg);
+ /* Start with pre-NAT addresses */
+ key = s->key[(s->direction == PF_OUT)];
+ src_host = &(key->addr[s_dir_out]);
+ dst_host = &(key->addr[s_dir_in]);
+ af = key->af;
+ if (s->nat_rule) {
+ /* Old-style NAT rules */
+ if (s->nat_rule->action == PF_NAT ||
+ s->nat_rule->action == PF_RDR ||
+ s->nat_rule->action == PF_BINAT) {
+ nr = s->nat_rule;
+ pf_rule_counters_inc(pd, s->nat_rule, dir_out,
+ op_pass, af, src_host, dst_host);
+ /* Use post-NAT addresses from now on */
+ key = s->key[s_dir_in];
+ src_host = &(key->addr[s_dir_out]);
+ dst_host = &(key->addr[s_dir_in]);
+ af = key->af;
+ }
+ }
}
+
+ SLIST_FOREACH(ri, mr, entry) {
+ pf_rule_counters_inc(pd, ri->r, dir_out, op_pass, af,
+ src_host, dst_host);
+ if (s && s->nat_rule == ri->r) {
+ /* Use post-NAT addresses after a match NAT rule */
+ key = s->key[s_dir_in];
+ src_host = &(key->addr[s_dir_out]);
+ dst_host = &(key->addr[s_dir_in]);
+ af = key->af;
+ }
+ }
+
+ if (s == NULL) {
+ pf_free_match_rules(mr);
+ }
+
+ if (a != NULL) {
+ pf_rule_counters_inc(pd, a, dir_out, op_pass, af,
+ src_host, dst_host);
+ }
+
+ if (r != nr) {
+ pf_rule_counters_inc(pd, r, dir_out, op_pass, af,
+ src_host, dst_host);
+ }
+
pf_counter_u64_critical_exit();
}
+
static void
pf_log_matches(struct pf_pdesc *pd, struct pf_krule *rm,
struct pf_krule *am, struct pf_kruleset *ruleset,
- struct pf_krule_slist *matchrules)
+ struct pf_krule_slist *match_rules)
{
struct pf_krule_item *ri;
@@ -10723,7 +10772,7 @@
if (rm->log & PF_LOG_MATCHES)
return;
- SLIST_FOREACH(ri, matchrules, entry)
+ SLIST_FOREACH(ri, match_rules, entry)
if (ri->r->log & PF_LOG_MATCHES)
PFLOG_PACKET(rm->action, PFRES_MATCH, rm, am,
ruleset, pd, 1, ri->r);
@@ -10740,6 +10789,8 @@
struct pf_krule *a = NULL, *r = &V_pf_default_rule;
struct pf_kstate *s = NULL;
struct pf_kruleset *ruleset = NULL;
+ struct pf_krule_item *ri;
+ struct pf_krule_slist match_rules;
struct pf_pdesc pd;
int use_2nd_queue = 0;
uint16_t tag;
@@ -10776,6 +10827,7 @@
}
pf_init_pdesc(&pd, *m0);
+ SLIST_INIT(&match_rules);
if (pd.pf_mtag != NULL && (pd.pf_mtag->flags & PF_MTAG_FLAG_ROUTE_TO)) {
pd.pf_mtag->flags &= ~PF_MTAG_FLAG_ROUTE_TO;
@@ -10872,7 +10924,7 @@
action = PF_DROP;
else
action = pf_test_rule(&r, &s, &pd, &a,
- &ruleset, &reason, inp);
+ &ruleset, &reason, inp, &match_rules);
if (action != PF_PASS)
REASON_SET(&reason, PFRES_FRAG);
break;
@@ -10930,7 +10982,7 @@
break;
} else {
action = pf_test_rule(&r, &s, &pd,
- &a, &ruleset, &reason, inp);
+ &a, &ruleset, &reason, inp, &match_rules);
}
}
break;
@@ -10951,7 +11003,7 @@
a = s->anchor;
} else if (s == NULL) {
action = pf_test_rule(&r, &s,
- &pd, &a, &ruleset, &reason, inp);
+ &pd, &a, &ruleset, &reason, inp, &match_rules);
}
break;
@@ -10979,7 +11031,7 @@
a = s->anchor;
} else if (s == NULL)
action = pf_test_rule(&r, &s, &pd,
- &a, &ruleset, &reason, inp);
+ &a, &ruleset, &reason, inp, &match_rules);
break;
}
@@ -10988,8 +11040,11 @@
done:
PF_RULES_RUNLOCK();
- if (pd.m == NULL)
+ /* if packet sits in reassembly queue, return without error */
+ if (pd.m == NULL) {
+ pf_free_match_rules(&match_rules);
goto eat_pkt;
+ }
if (s)
memcpy(&pd.act, &s->act, sizeof(s->act));
@@ -11086,6 +11141,8 @@
(dir == PF_IN) ? PF_DIVERT_MTAG_DIR_IN :
PF_DIVERT_MTAG_DIR_OUT;
+ pf_counters_inc(action, &pd, s, r, a, &match_rules);
+
if (s)
PF_STATE_UNLOCK(s);
@@ -11127,7 +11184,6 @@
if (pd.act.log) {
struct pf_krule *lr;
- struct pf_krule_item *ri;
if (s != NULL && s->nat_rule != NULL &&
s->nat_rule->log & PF_LOG_ALL)
@@ -11146,7 +11202,7 @@
}
}
- pf_counters_inc(action, &pd, s, r, a);
+ pf_counters_inc(action, &pd, s, r, a, &match_rules);
switch (action) {
case PF_SYNPROXY_DROP:
diff --git a/tests/sys/netpfil/pf/Makefile b/tests/sys/netpfil/pf/Makefile
--- a/tests/sys/netpfil/pf/Makefile
+++ b/tests/sys/netpfil/pf/Makefile
@@ -5,6 +5,7 @@
ATF_TESTS_SH+= altq \
anchor \
+ counters \
debug \
divert-to \
dup \
diff --git a/tests/sys/netpfil/pf/counters.sh b/tests/sys/netpfil/pf/counters.sh
new file mode 100644
--- /dev/null
+++ b/tests/sys/netpfil/pf/counters.sh
@@ -0,0 +1,817 @@
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2025 Kajetan Staszkiewicz
+#
+# 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.
+
+. $(atf_get_srcdir)/utils.subr
+
+get_counters()
+{
+ echo " === rules ==="
+ rules=$(mktemp) || exit
+ (jexec router pfctl -qvvsn ; jexec router pfctl -qvvsr) | normalize_pfctl_s > $rules
+ cat $rules
+
+ echo " === tables ==="
+ tables=$(mktemp) || exit 1
+ jexec router pfctl -qvvsT > $tables
+ cat $tables
+
+ echo " === states ==="
+ states=$(mktemp) || exit 1
+ jexec router pfctl -qvvss | normalize_pfctl_s > $states
+ cat $states
+
+ echo " === nodes ==="
+ nodes=$(mktemp) || exit 1
+ jexec router pfctl -qvvsS | normalize_pfctl_s > $nodes
+ cat $nodes
+}
+
+atf_test_case "match_pass_state" "cleanup"
+match_pass_state_head()
+{
+ atf_set descr 'Counters on match and pass rules'
+ atf_set require.user root
+}
+
+match_pass_state_body()
+{
+ setup_router_server_ipv6
+
+ # Thest counters for a statefull firewall. Expose the behaviour of
+ # increasing table counters if a table is used multiple times.
+ # The table "tbl_in" is used both in match and pass rule. It's counters
+ # are incremented twice. The tables "tbl_out_match" and "tbl_out_pass"
+ # are used only once and have their countes increased only once.
+ # Test source node counters for this simple scenario too.
+ pft_set_rules router \
+ "set state-policy if-bound" \
+ "table <tbl_in> { ${net_tester_host_tester} }" \
+ "table <tbl_out_pass> { ${net_server_host_server} }" \
+ "table <tbl_out_match> { ${net_server_host_server} }" \
+ "block" \
+ "pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv }" \
+ "match in on ${epair_tester}b inet6 proto tcp from <tbl_in> scrub (random-id)" \
+ "pass in on ${epair_tester}b inet6 proto tcp from <tbl_in> keep state (max-src-states 3 source-track rule)" \
+ "match out on ${epair_server}a inet6 proto tcp to <tbl_out_match> scrub (random-id)" \
+ "pass out on ${epair_server}a inet6 proto tcp to <tbl_out_pass> keep state"
+
+ # Use a real TCP connection so that it will be properly closed, guaranteeing the amount of packets.
+ atf_check -s exit:0 -o match:"This is a test" -x \
+ "echo 'This is a test' | nc -w3 ${net_server_host_server} echo"
+ # Let FINs pass through.
+ sleep 1
+ get_counters
+
+ for rule_regexp in \
+ "@3 match in on ${epair_tester}b .* Packets: 10 Bytes: 766 States: 1 " \
+ "@4 pass in on ${epair_tester}b .* Packets: 10 Bytes: 766 States: 1 " \
+ "@5 match out on ${epair_server}a .* Packets: 10 Bytes: 766 States: 1 " \
+ "@6 pass out on ${epair_server}a .* Packets: 10 Bytes: 766 States: 1 " \
+ ; do
+ grep -qE "${rule_regexp}" $rules || atf_fail "Rule regexp not found for '${rule_regexp}'"
+ done
+
+ table_counters_single="Evaluations: NoMatch: 0 Match: 1 In/Block: Packets: 0 Bytes: 0 In/Pass: Packets: 4 Bytes: 311 In/XPass: Packets: 0 Bytes: 0 Out/Block: Packets: 0 Bytes: 0 Out/Pass: Packets: 6 Bytes: 455 Out/XPass: Packets: 0 Bytes: 0"
+ table_counters_double="Evaluations: NoMatch: 0 Match: 2 In/Block: Packets: 0 Bytes: 0 In/Pass: Packets: 12 Bytes: 910 In/XPass: Packets: 0 Bytes: 0 Out/Block: Packets: 0 Bytes: 0 Out/Pass: Packets: 8 Bytes: 622 Out/XPass: Packets: 0 Bytes: 0"
+ for table_test in \
+ "tbl_in___${table_counters_double}" \
+ "tbl_out_match___${table_counters_single}" \
+ "tbl_out_pass___${table_counters_single}" \
+ ; do
+ table_name=${table_test%%___*}
+ table_regexp=${table_test##*___}
+ table=$(mktemp) || exit 1
+ cat $tables | grep -A10 $table_name | tr '\n' ' ' | awk '{gsub("[\\[\\]]", " ", $0); gsub("[[:blank:]]+"," ",$0); print $0}' > ${table}
+ grep -qE "${table_regexp}" ${table} || atf_fail "Bad counters for table ${table_name}"
+ done;
+
+ for state_regexp in \
+ "${epair_tester}b tcp ${net_server_host_server}.* <- ${net_tester_host_tester}.* 6:4 pkts, 455:311 bytes, rule 4," \
+ "${epair_server}a tcp ${net_server_host_tester}.* -> ${net_server_host_server}.* 6:4 pkts, 455:311 bytes, rule 6," \
+ ; do
+ grep -qE "${state_regexp}" $states || atf_fail "State not found for '${state_regexp}'"
+ done
+
+ for node_regexp in \
+ "${net_tester_host_tester} -> :: .* 10 pkts, 766 bytes, filter rule 4, limit source-track"\
+ ; do
+ grep -qE "${node_regexp}" $nodes || atf_fail "Source node not found for '${node_regexp}'"
+ done
+}
+
+match_pass_state_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "match_pass_no_state" "cleanup"
+match_pass_no_state_head()
+{
+ atf_set descr 'Counters on match and pass rules without keep state'
+ atf_set require.user root
+}
+
+match_pass_no_state_body()
+{
+ setup_router_server_ipv6
+
+ # Test counters for a stateless firewall.
+ # The table "tbl_in" is used both in match and pass rule in the inbound
+ # direction. The "In/Pass" counter is incremented twice. The table
+ # "tbl_inout" matches the same host on inbound and outbound direction.
+ # It will also be incremented twice. The tables "tbl_out_match" and
+ # "tbl_out_pass" will have their counters increased only once.
+ pft_set_rules router \
+ "table <tbl_in> { ${net_tester_host_tester} }" \
+ "table <tbl_inout> { ${net_tester_host_tester} }" \
+ "table <tbl_out_match> { ${net_server_host_server} }" \
+ "table <tbl_out_pass> { ${net_server_host_server} }" \
+ "block" \
+ "pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv }" \
+ "match in on ${epair_tester}b inet6 proto tcp from <tbl_inout>" \
+ "match in on ${epair_tester}b inet6 proto tcp from <tbl_in>" \
+ "pass in on ${epair_tester}b inet6 proto tcp from <tbl_in> no state" \
+ "pass out on ${epair_tester}b inet6 proto tcp to <tbl_in> no state" \
+ "match in on ${epair_server}a inet6 proto tcp from <tbl_out_match>" \
+ "pass in on ${epair_server}a inet6 proto tcp from <tbl_out_pass> no state" \
+ "match out on ${epair_server}a inet6 proto tcp from <tbl_inout> no state" \
+ "pass out on ${epair_server}a inet6 proto tcp to <tbl_out_pass> no state"
+
+ # Use a real TCP connection so that it will be properly closed, guaranteeing the amount of packets.
+ atf_check -s exit:0 -o match:"This is a test" -x \
+ "echo 'This is a test' | nc -w3 ${net_server_host_server} echo"
+ sleep 1
+ get_counters
+
+ for rule_regexp in \
+ "@3 match in on ${epair_tester}b .* Packets: 6 Bytes: 455 " \
+ "@4 match in on ${epair_tester}b .* Packets: 6 Bytes: 455 " \
+ "@5 pass in on ${epair_tester}b .* Packets: 6 Bytes: 455 " \
+ "@6 pass out on ${epair_tester}b .* Packets: 4 Bytes: 311 " \
+ "@7 match in on ${epair_server}a .* Packets: 4 Bytes: 311 " \
+ "@8 pass in on ${epair_server}a .* Packets: 4 Bytes: 311 " \
+ "@10 pass out on ${epair_server}a .* Packets: 6 Bytes: 455 " \
+ ; do
+ grep -qE "${rule_regexp}" $rules || atf_fail "Rule regexp not found for '${rule_regexp}'"
+ done
+
+ for table_test in \
+ "tbl_in___Evaluations: NoMatch: 0 Match: 16 In/Block: Packets: 0 Bytes: 0 In/Pass: Packets: 12 Bytes: 910 In/XPass: Packets: 0 Bytes: 0 Out/Block: Packets: 0 Bytes: 0 Out/Pass: Packets: 4 Bytes: 311 Out/XPass: Packets: 0 Bytes: 0" \
+ "tbl_out_match___Evaluations: NoMatch: 0 Match: 4 In/Block: Packets: 0 Bytes: 0 In/Pass: Packets: 4 Bytes: 311 In/XPass: Packets: 0 Bytes: 0 Out/Block: Packets: 0 Bytes: 0 Out/Pass: Packets: 0 Bytes: 0 Out/XPass: Packets: 0 Bytes: 0" \
+ "tbl_out_pass___Evaluations: NoMatch: 0 Match: 10 In/Block: Packets: 0 Bytes: 0 In/Pass: Packets: 4 Bytes: 311 In/XPass: Packets: 0 Bytes: 0 Out/Block: Packets: 0 Bytes: 0 Out/Pass: Packets: 6 Bytes: 455 Out/XPass: Packets: 0 Bytes: 0" \
+ "tbl_inout___Evaluations: NoMatch: 0 Match: 12 In/Block: Packets: 0 Bytes: 0 In/Pass: Packets: 6 Bytes: 455 In/XPass: Packets: 0 Bytes: 0 Out/Block: Packets: 0 Bytes: 0 Out/Pass: Packets: 6 Bytes: 455 Out/XPass: Packets: 0 Bytes: 0" \
+ ; do
+ table_name=${table_test%%___*}
+ table_regexp=${table_test##*___}
+ table=$(mktemp) || exit 1
+ cat $tables | grep -A10 $table_name | tr '\n' ' ' | awk '{gsub("[\\[\\]]", " ", $0); gsub("[[:blank:]]+"," ",$0); print $0}' > ${table}
+ grep -qE "${table_regexp}" ${table} || atf_fail "Bad counters for table ${table_name}"
+ done;
+}
+
+match_pass_no_state_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "match_block" "cleanup"
+match_block_head()
+{
+ atf_set descr 'Counters on match and block rules'
+ atf_set require.user root
+}
+
+match_block_body()
+{
+ setup_router_server_ipv6
+
+ # Stateful firewall with a blocking rule. The rule will have its
+ # counters increased because it matches and applies correctly.
+ # The "match" rule before the "pass" rule will have its counters
+ # increased for blocked traffic too.
+ pft_set_rules router \
+ "set state-policy if-bound" \
+ "table <tbl_in_match> { ${net_server_host_server} }" \
+ "table <tbl_in_block> { ${net_server_host_server} }" \
+ "block" \
+ "pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv }" \
+ "match in on ${epair_tester}b inet6 proto tcp to <tbl_in_match> scrub (random-id)" \
+ "block in on ${epair_tester}b inet6 proto tcp to <tbl_in_block>" \
+ "pass out on ${epair_server}a inet6 proto tcp keep state"
+
+ # Wait 3 seconds, that will cause 2 SYNs to be sent out.
+ echo 'This is a test' | nc -w3 ${net_server_host_server} echo
+ sleep 1
+ get_counters
+
+ for rule_regexp in \
+ "@3 match in on ${epair_tester}b .* Packets: 2 Bytes: 160 States: 0 " \
+ "@4 block drop in on ${epair_tester}b .* Packets: 2 Bytes: 160 States: 0 " \
+ ; do
+ grep -qE "${rule_regexp}" $rules || atf_fail "Rule regexp not found for '${rule_regexp}'"
+ done
+
+ # OpenBSD has (In|Out)/Match. We don't (yet) have it in FreeBSD
+ # so we follow the action of the "pass" rule ("block" for this test)
+ # in "match" rules.
+ for table_test in \
+ "tbl_in_match___Evaluations: NoMatch: 0 Match: 2 In/Block: Packets: 2 Bytes: 160 In/Pass: Packets: 0 Bytes: 0 In/XPass: Packets: 0 Bytes: 0 Out/Block: Packets: 0 Bytes: 0 Out/Pass: Packets: 0 Bytes: 0 Out/XPass: Packets: 0 Bytes: 0" \
+ "tbl_in_block___Evaluations: NoMatch: 0 Match: 2 In/Block: Packets: 2 Bytes: 160 In/Pass: Packets: 0 Bytes: 0 In/XPass: Packets: 0 Bytes: 0 Out/Block: Packets: 0 Bytes: 0 Out/Pass: Packets: 0 Bytes: 0 Out/XPass: Packets: 0 Bytes: 0" \
+ ; do
+ table_name=${table_test%%___*}
+ table_regexp=${table_test##*___}
+ table=$(mktemp) || exit 1
+ cat $tables | grep -A10 $table_name | tr '\n' ' ' | awk '{gsub("[\\[\\]]", " ", $0); gsub("[[:blank:]]+"," ",$0); print $0}' > ${table}
+ grep -qE "${table_regexp}" ${table} || atf_fail "Bad counters for table ${table_name}"
+ done;
+}
+
+match_block_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "match_fail" "cleanup"
+match_fail_head()
+{
+ atf_set descr 'Counters on match and failing pass rules'
+ atf_set require.user root
+}
+
+match_fail_body()
+{
+ setup_router_server_ipv6
+
+ # Statefull firewall with a failing "pass" rule.
+ # When the rule can't apply it will not have its counters increased.
+ pft_set_rules router \
+ "set state-policy if-bound" \
+ "table <tbl_in_match> { ${net_server_host_server} }" \
+ "table <tbl_in_fail> { ${net_server_host_server} }" \
+ "block" \
+ "pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv }" \
+ "match in on ${epair_tester}b inet6 proto tcp to <tbl_in_match> scrub (random-id)" \
+ "pass in on ${epair_tester}b inet6 proto tcp to <tbl_in_fail> keep state (max 1)" \
+ "pass out on ${epair_server}a inet6 proto tcp keep state"
+
+ # The first test will pass and increase the counters for all rules.
+ echo 'This is a test' | nc -w3 ${net_server_host_server} echo
+ # The second test will go through the "match" rules but fail
+ # on the "pass" rule due to 'keep state (max 1)'.
+ # Wait 3 seconds, that will cause 2 SYNs to be sent out.
+ echo 'This is a test' | nc -w3 ${net_server_host_server} echo
+ sleep 1
+ get_counters
+
+ for rule_regexp in \
+ "@3 match in on ${epair_tester}b .* Packets: 10 Bytes: 766 States: 1 " \
+ "@4 pass in on ${epair_tester}b .* Packets: 10 Bytes: 766 States: 1 " \
+ ; do
+ grep -qE "${rule_regexp}" $rules || atf_fail "Rule regexp not found for '${rule_regexp}'"
+ done
+
+ $table_counters_single="Evaluations: NoMatch: 0 Match: 3 In/Block: Packets: 0 Bytes: 0 In/Pass: Packets: 6 Bytes: 455 In/XPass: Packets: 0 Bytes: 0 Out/Block: Packets: 0 Bytes: 0 Out/Pass: Packets: 4 Bytes: 311 Out/XPass: Packets: 0 Bytes: 0"
+ for table_test in \
+ "tbl_in_match___${table_counters_single}" \
+ "tbl_in_fail___${table_counters_single}" \
+ ; do
+ table_name=${table_test%%___*}
+ table_regexp=${table_test##*___}
+ table=$(mktemp) || exit 1
+ cat $tables | grep -A10 $table_name | tr '\n' ' ' | awk '{gsub("[\\[\\]]", " ", $0); gsub("[[:blank:]]+"," ",$0); print $0}' > ${table}
+ grep -qE "${table_regexp}" ${table} || atf_fail "Bad counters for table ${table_name}"
+ done;
+}
+
+match_fail_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "nat_natonly" "cleanup"
+nat_natonly_head()
+{
+ atf_set descr 'Counters on only a NAT rule creating state'
+ atf_set require.user root
+}
+
+nat_natonly_body()
+{
+ setup_router_server_ipv6
+
+ # NAT is applied on the "nat" rule.
+ # The "nat" rule matches on pre-NAT addresses. There is no separate
+ # "pass" rule so the "nat" rule creates the state.
+ pft_set_rules router \
+ "set state-policy if-bound" \
+ "table <tbl_src_nat> { ${net_tester_host_tester} }" \
+ "table <tbl_dst_nat> { ${net_server_host_server} }" \
+ "nat on ${epair_server}a inet6 proto tcp from <tbl_src_nat> to <tbl_dst_nat> -> ${net_server_host_router}"
+
+ # Use a real TCP connection so that it will be properly closed, guaranteeing the amount of packets.
+ atf_check -s exit:0 -o match:"This is a test" -x \
+ "echo 'This is a test' | nc -w3 ${net_server_host_server} echo"
+ sleep 1
+ get_counters
+
+ for rule_regexp in \
+ "@0 nat on ${epair_server}a .* Packets: 10 Bytes: 766 States: 1 " \
+ ; do
+ grep -qE "${rule_regexp}" $rules || atf_fail "Rule regexp not found for '${rule_regexp}'"
+ done
+
+ # All tables have counters increased for In/Pass and Out/Pass, not XPass.
+ table_counters="Evaluations: NoMatch: 0 Match: 1 In/Block: Packets: 0 Bytes: 0 In/Pass: Packets: 4 Bytes: 311 In/XPass: Packets: 0 Bytes: 0 Out/Block: Packets: 0 Bytes: 0 Out/Pass: Packets: 6 Bytes: 455 Out/XPass: Packets: 0 Bytes: 0"
+ for table_test in \
+ "tbl_src_nat___${table_counters}" \
+ "tbl_dst_nat___${table_counters}" \
+ ; do
+ table_name=${table_test%%___*}
+ table_regexp=${table_test##*___}
+ table=$(mktemp) || exit 1
+ cat $tables | grep -A10 $table_name | tr '\n' ' ' | awk '{gsub("[\\[\\]]", " ", $0); gsub("[[:blank:]]+"," ",$0); print $0}' > ${table}
+ grep -qE "${table_regexp}" ${table} || atf_fail "Bad counters for table ${table_name}"
+ done;
+
+ for state_regexp in \
+ "all tcp ${net_server_host_router}.* -> ${net_server_host_server}.* 6:4 pkts, 455:311 bytes" \
+ ; do
+ grep -qE "${state_regexp}" $states || atf_fail "State not found for '${state_regexp}'"
+ done
+}
+
+nat_natonly_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "nat_nat" "cleanup"
+nat_nat_head()
+{
+ atf_set descr 'Counters on NAT, match and pass rules with keep state'
+ atf_set require.user root
+}
+
+nat_nat_body()
+{
+ setup_router_server_ipv6
+
+ # NAT is applied in the NAT ruleset.
+ # The "nat" rule matches on pre-NAT addresses.
+ # The "match" rule matches on post-NAT addresses.
+ # The "pass" rule matches on post-NAT addresses and creates the state.
+ pft_set_rules router \
+ "set state-policy if-bound" \
+ "table <tbl_src_nat> { ${net_tester_host_tester} }" \
+ "table <tbl_dst_nat> { ${net_server_host_server} }" \
+ "table <tbl_src_match> { ${net_server_host_router} }" \
+ "table <tbl_dst_match> { ${net_server_host_server} }" \
+ "table <tbl_src_pass> { ${net_server_host_router} }" \
+ "table <tbl_dst_pass> { ${net_server_host_server} }" \
+ "nat on ${epair_server}a inet6 proto tcp from <tbl_src_nat> to <tbl_dst_nat> -> ${net_server_host_router}" \
+ "block" \
+ "pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv }" \
+ "pass in on ${epair_tester}b inet6 proto tcp keep state" \
+ "match out on ${epair_server}a inet6 proto tcp from <tbl_src_match> to <tbl_dst_match> scrub (random-id)" \
+ "pass out on ${epair_server}a inet6 proto tcp from <tbl_src_pass> to <tbl_dst_pass> keep state"
+
+ # Use a real TCP connection so that it will be properly closed, guaranteeing the amount of packets.
+ atf_check -s exit:0 -o match:"This is a test" -x \
+ "echo 'This is a test' | nc -w3 ${net_server_host_server} echo"
+ sleep 1
+ get_counters
+
+ for rule_regexp in \
+ "@0 nat on ${epair_server}a .* Packets: 10 Bytes: 766 States: 1 " \
+ "@4 match out on ${epair_server}a .* Packets: 10 Bytes: 766 States: 1 " \
+ "@5 pass out on ${epair_server}a .* Packets: 10 Bytes: 766 States: 1 " \
+ ; do
+ grep -qE "${rule_regexp}" $rules || atf_fail "Rule regexp not found for '${rule_regexp}'"
+ done
+
+ # All tables have counters increased for In/Pass and Out/Pass, not XPass nor Block.
+ table_counters="Evaluations: NoMatch: 0 Match: 1 In/Block: Packets: 0 Bytes: 0 In/Pass: Packets: 4 Bytes: 311 In/XPass: Packets: 0 Bytes: 0 Out/Block: Packets: 0 Bytes: 0 Out/Pass: Packets: 6 Bytes: 455 Out/XPass: Packets: 0 Bytes: 0"
+ for table_test in \
+ "tbl_src_nat___${table_counters}" \
+ "tbl_dst_nat___${table_counters}" \
+ "tbl_src_match___${table_counters}" \
+ "tbl_dst_match___${table_counters}" \
+ "tbl_src_pass___${table_counters}" \
+ "tbl_dst_pass___${table_counters}" \
+ ; do
+ table_name=${table_test%%___*}
+ table_regexp=${table_test##*___}
+ table=$(mktemp) || exit 1
+ cat $tables | grep -A10 $table_name | tr '\n' ' ' | awk '{gsub("[\\[\\]]", " ", $0); gsub("[[:blank:]]+"," ",$0); print $0}' > ${table}
+ grep -qE "${table_regexp}" ${table} || atf_fail "Bad counters for table ${table_name}"
+ done;
+
+ for state_regexp in \
+ "${epair_server}a tcp ${net_server_host_router}.* -> ${net_server_host_server}.* 6:4 pkts, 455:311 bytes, rule 5," \
+ ; do
+ grep -qE "${state_regexp}" $states || atf_fail "State not found for '${state_regexp}'"
+ done
+}
+
+nat_nat_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "nat_match" "cleanup"
+nat_match_head()
+{
+ atf_set descr 'Counters on match with NAT and pass rules'
+ atf_set require.user root
+}
+
+nat_match_body()
+{
+ setup_router_server_ipv6
+
+ # NAT is applied on the "match" rule.
+ # The "match" rule up to and including the NAT rule match on pre-NAT addresses.
+ # The "match" rule after NAT matches on post-NAT addresses.
+ # The "pass" rule matches on post-NAT addresses and creates the state.
+ pft_set_rules router \
+ "set state-policy if-bound" \
+ "table <tbl_src_match1> { ${net_tester_host_tester} }" \
+ "table <tbl_dst_match1> { ${net_server_host_server} }" \
+ "table <tbl_src_match2> { ${net_tester_host_tester} }" \
+ "table <tbl_dst_match2> { ${net_server_host_server} }" \
+ "table <tbl_src_match3> { ${net_server_host_router} }" \
+ "table <tbl_dst_match3> { ${net_server_host_server} }" \
+ "table <tbl_src_pass> { ${net_server_host_router} }" \
+ "table <tbl_dst_pass> { ${net_server_host_server} }" \
+ "block" \
+ "pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv }" \
+ "pass in on ${epair_tester}b inet6 proto tcp keep state" \
+ "match out on ${epair_server}a inet6 proto tcp from <tbl_src_match1> to <tbl_dst_match1> scrub (random-id)" \
+ "match out on ${epair_server}a inet6 proto tcp from <tbl_src_match2> to <tbl_dst_match2> nat-to ${net_server_host_router}" \
+ "match out on ${epair_server}a inet6 proto tcp from <tbl_src_match3> to <tbl_dst_match3> scrub (random-id)" \
+ "pass out on ${epair_server}a inet6 proto tcp from <tbl_src_pass> to <tbl_dst_pass> keep state"
+
+ # Use a real TCP connection so that it will be properly closed, guaranteeing the amount of packets.
+ atf_check -s exit:0 -o match:"This is a test" -x \
+ "echo 'This is a test' | nc -w3 ${net_server_host_server} echo"
+ sleep 1
+ get_counters
+
+ for rule_regexp in \
+ "@4 match out on ${epair_server}a .* Packets: 10 Bytes: 766 States: 1 " \
+ "@5 match out on ${epair_server}a .* Packets: 10 Bytes: 766 States: 1 " \
+ "@6 match out on ${epair_server}a .* Packets: 10 Bytes: 766 States: 1 " \
+ "@7 pass out on ${epair_server}a .* Packets: 10 Bytes: 766 States: 1 " \
+ ; do
+ grep -qE "${rule_regexp}" $rules || atf_fail "Rule regexp not found for '${rule_regexp}'"
+ done
+
+ # All tables have counters increased for In/Pass and Out/Pass, not XPass nor Block.
+ table_counters="Evaluations: NoMatch: 0 Match: 1 In/Block: Packets: 0 Bytes: 0 In/Pass: Packets: 4 Bytes: 311 In/XPass: Packets: 0 Bytes: 0 Out/Block: Packets: 0 Bytes: 0 Out/Pass: Packets: 6 Bytes: 455 Out/XPass: Packets: 0 Bytes: 0"
+ for table_test in \
+ "tbl_src_match1___${table_counters}" \
+ "tbl_dst_match1___${table_counters}" \
+ "tbl_src_match2___${table_counters}" \
+ "tbl_dst_match2___${table_counters}" \
+ "tbl_src_match3___${table_counters}" \
+ "tbl_dst_match3___${table_counters}" \
+ "tbl_src_pass___${table_counters}" \
+ "tbl_dst_pass___${table_counters}" \
+ ; do
+ table_name=${table_test%%___*}
+ table_regexp=${table_test##*___}
+ table=$(mktemp) || exit 1
+ cat $tables | grep -A10 $table_name | tr '\n' ' ' | awk '{gsub("[\\[\\]]", " ", $0); gsub("[[:blank:]]+"," ",$0); print $0}' > ${table}
+ grep -qE "${table_regexp}" ${table} || atf_fail "Bad counters for table ${table_name}"
+ done;
+
+ for state_regexp in \
+ "${epair_server}a tcp ${net_server_host_tester}.* -> ${net_server_host_server}.* 6:4 pkts, 455:311 bytes, rule 7, " \
+ ; do
+ grep -qE "${state_regexp}" $states || atf_fail "State not found for '${state_regexp}'"
+ done
+}
+
+nat_match_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "nat_pass" "cleanup"
+nat_pass_head()
+{
+ atf_set descr 'Counters on match, and pass with NAT rules'
+ atf_set require.user root
+}
+
+nat_pass_body()
+{
+ setup_router_server_ipv6
+
+ # NAT is applied on the "pass" rule which also creates the state.
+ # All rules match on pre-NAT addresses.
+ pft_set_rules router \
+ "set state-policy if-bound" \
+ "table <tbl_src_match> { ${net_tester_host_tester} }" \
+ "table <tbl_dst_match> { ${net_server_host_server} }" \
+ "table <tbl_src_pass> { ${net_tester_host_tester} }" \
+ "table <tbl_dst_pass> { ${net_server_host_server} }" \
+ "block" \
+ "pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv }" \
+ "pass in on ${epair_tester}b inet6 proto tcp keep state" \
+ "match out on ${epair_server}a inet6 proto tcp from <tbl_src_match> to <tbl_dst_match> scrub (random-id)" \
+ "pass out on ${epair_server}a inet6 proto tcp from <tbl_src_pass> to <tbl_dst_pass> nat-to ${net_server_host_router} keep state"
+
+ # Use a real TCP connection so that it will be properly closed, guaranteeing the amount of packets.
+ atf_check -s exit:0 -o match:"This is a test" -x \
+ "echo 'This is a test' | nc -w3 ${net_server_host_server} echo"
+ sleep 1
+ get_counters
+
+ for rule_regexp in \
+ "@4 match out on ${epair_server}a .* Packets: 10 Bytes: 766 States: 1 " \
+ "@5 pass out on ${epair_server}a .* Packets: 10 Bytes: 766 States: 1 " \
+ ; do
+ grep -qE "${rule_regexp}" $rules || atf_fail "Rule regexp not found for '${rule_regexp}'"
+ done
+
+ table_counters="Evaluations: NoMatch: 0 Match: 1 In/Block: Packets: 0 Bytes: 0 In/Pass: Packets: 4 Bytes: 311 In/XPass: Packets: 0 Bytes: 0 Out/Block: Packets: 0 Bytes: 0 Out/Pass: Packets: 6 Bytes: 455 Out/XPass: Packets: 0 Bytes: 0"
+ for table_test in \
+ "tbl_src_match___${table_counters}" \
+ "tbl_dst_match___${table_counters}" \
+ "tbl_src_pass___${table_counters}" \
+ "tbl_dst_pass___${table_counters}" \
+ ; do
+ table_name=${table_test%%___*}
+ table_regexp=${table_test##*___}
+ table=$(mktemp) || exit 1
+ cat $tables | grep -A10 $table_name | tr '\n' ' ' | awk '{gsub("[\\[\\]]", " ", $0); gsub("[[:blank:]]+"," ",$0); print $0}' > ${table}
+ grep -qE "${table_regexp}" ${table} || atf_fail "Bad counters for table ${table_name}"
+ done;
+
+ for state_regexp in \
+ "${epair_server}a tcp ${net_server_host_router}.* -> ${net_server_host_server}.* 6:4 pkts, 455:311 bytes, rule 5," \
+ ; do
+ grep -qE "${state_regexp}" $states || atf_fail "State not found for '${state_regexp}'"
+ done
+}
+
+nat_pass_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "rdr_match" "cleanup"
+rdr_match_head()
+{
+ atf_set descr 'Counters on match with RDR and pass rules'
+ atf_set require.user root
+}
+
+rdr_match_body()
+{
+ setup_router_server_ipv6
+
+ # Similar to the nat_match test but for the RDR action.
+ # Hopefully we don't need all other tests duplicated for RDR.
+ # Send traffic to a non-existing host, RDR it to the server.
+ #
+ # The "match" rule up to and including the RDR rule match on pre-RDR dst address.
+ # The "match" rule after NAT matches on post-RDR dst address.
+ # The "pass" rule matches on post-RDR dst address.
+ net_server_host_notserver=${net_server_host_server%%::*}::3
+ pft_set_rules router \
+ "set state-policy if-bound" \
+ "table <tbl_src_match1> { ${net_tester_host_tester} }" \
+ "table <tbl_dst_match1> { ${net_server_host_notserver} }" \
+ "table <tbl_src_match2> { ${net_tester_host_tester} }" \
+ "table <tbl_dst_match2> { ${net_server_host_notserver} }" \
+ "table <tbl_src_match3> { ${net_tester_host_tester} }" \
+ "table <tbl_dst_match3> { ${net_server_host_server} }" \
+ "table <tbl_src_pass> { ${net_tester_host_tester} }" \
+ "table <tbl_dst_pass> { ${net_server_host_server} }" \
+ "block" \
+ "pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv }" \
+ "pass out on ${epair_server}a inet6 proto tcp keep state" \
+ "match in on ${epair_tester}b inet6 proto tcp from <tbl_src_match1> to <tbl_dst_match1> scrub (random-id)" \
+ "match in on ${epair_tester}b inet6 proto tcp from <tbl_src_match2> to <tbl_dst_match2> rdr-to ${net_server_host_server}" \
+ "match in on ${epair_tester}b inet6 proto tcp from <tbl_src_match3> to <tbl_dst_match3> scrub (random-id)" \
+ "pass in on ${epair_tester}b inet6 proto tcp from <tbl_src_pass> to <tbl_dst_pass> keep state"
+
+ # Use a real TCP connection so that it will be properly closed, guaranteeing the amount of packets.
+ atf_check -s exit:0 -o match:"This is a test" -x \
+ "echo 'This is a test' | nc -w3 ${net_server_host_notserver} echo"
+ sleep 1
+ get_counters
+
+ for rule_regexp in \
+ "@4 match in on ${epair_tester}b .* Packets: 10 Bytes: 766 States: 1 " \
+ "@5 match in on ${epair_tester}b .* Packets: 10 Bytes: 766 States: 1 " \
+ "@6 match in on ${epair_tester}b .* Packets: 10 Bytes: 766 States: 1 " \
+ "@7 pass in on ${epair_tester}b .* Packets: 10 Bytes: 766 States: 1 " \
+ ; do
+ grep -qE "${rule_regexp}" $rules || atf_fail "Rule regexp not found for '${rule_regexp}'"
+ done
+
+ # All tables have counters increased for In/Pass and Out/Pass, not XPass nor Block.
+ table_counters="Evaluations: NoMatch: 0 Match: 1 In/Block: Packets: 0 Bytes: 0 In/Pass: Packets: 6 Bytes: 455 In/XPass: Packets: 0 Bytes: 0 Out/Block: Packets: 0 Bytes: 0 Out/Pass: Packets: 4 Bytes: 311 Out/XPass: Packets: 0 Bytes: 0"
+ for table_test in \
+ "tbl_src_match1___${table_counters}" \
+ "tbl_dst_match1___${table_counters}" \
+ "tbl_src_match2___${table_counters}" \
+ "tbl_dst_match2___${table_counters}" \
+ "tbl_src_match3___${table_counters}" \
+ "tbl_dst_match3___${table_counters}" \
+ "tbl_src_pass___${table_counters}" \
+ "tbl_dst_pass___${table_counters}" \
+ ; do
+ table_name=${table_test%%___*}
+ table_regexp=${table_test##*___}
+ table=$(mktemp) || exit 1
+ cat $tables | grep -A10 $table_name | tr '\n' ' ' | awk '{gsub("[\\[\\]]", " ", $0); gsub("[[:blank:]]+"," ",$0); print $0}' > ${table}
+ grep -qE "${table_regexp}" ${table} || atf_fail "Bad counters for table ${table_name}"
+ done;
+
+ for state_regexp in \
+ "${epair_tester}b tcp ${net_server_host_server}.* 6:4 pkts, 455:311 bytes, rule 7, " \
+ ; do
+ grep -qE "${state_regexp}" $states || atf_fail "State not found for '${state_regexp}'"
+ done
+}
+
+rdr_match_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "nat64_in" "cleanup"
+nat64_in_head()
+{
+ atf_set descr 'Counters on match and inbound af-to rules'
+ atf_set require.user root
+}
+
+nat64_in_body()
+{
+ setup_router_server_nat64
+
+ pft_set_rules router \
+ "set state-policy if-bound" \
+ "table <tbl_src_match> { ${net_tester_6_host_tester} }" \
+ "table <tbl_dst_match> { 64:ff9b::${net_server1_4_host_server} }" \
+ "table <tbl_src_pass> { ${net_tester_6_host_tester} }" \
+ "table <tbl_dst_pass> { 64:ff9b::${net_server1_4_host_server} }" \
+ "block" \
+ "pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv }" \
+ "match in on ${epair_tester}b inet6 proto tcp from <tbl_src_match> to <tbl_dst_match> scrub (random-id)" \
+ "pass in on ${epair_tester}b inet6 proto tcp from <tbl_src_pass> to <tbl_dst_pass> \
+ af-to inet from (${epair_server1}a) \
+ keep state"
+
+ # Use a real TCP connection so that it will be properly closed, guaranteeing the amount of packets.
+ atf_check -s exit:0 -o match:"This is a test" -x \
+ "echo 'This is a test' | nc -w3 64:ff9b::${net_server1_4_host_server} echo"
+ sleep 1
+ get_counters
+
+ # The amount of packets is counted properly but sizes are not because
+ # pd->tot_len is always post-nat, even when updating pre-nat counters.
+ for rule_regexp in \
+ "@3 match in on ${epair_tester}b .* Packets: 10 Bytes: 686 States: 1 " \
+ "@4 pass in on ${epair_tester}b .* Packets: 10 Bytes: 686 States: 1 " \
+ ; do
+ grep -qE "${rule_regexp}" $rules || atf_fail "Rule regexp not found for '${rule_regexp}'"
+ done
+
+ # All tables have counters increased for In/Pass and Out/Pass, not XPass nor Block.
+ table_counters="Evaluations: NoMatch: 0 Match: 1 In/Block: Packets: 0 Bytes: 0 In/Pass: Packets: 4 Bytes: 231 In/XPass: Packets: 0 Bytes: 0 Out/Block: Packets: 0 Bytes: 0 Out/Pass: Packets: 6 Bytes: 455 Out/XPass: Packets: 0 Bytes: 0"
+ for table_test in \
+ "tbl_src_match___${table_counters}" \
+ "tbl_dst_match___${table_counters}" \
+ "tbl_src_pass___${table_counters}" \
+ "tbl_dst_pass___${table_counters}" \
+ ; do
+ table_name=${table_test%%___*}
+ table_regexp=${table_test##*___}
+ table=$(mktemp) || exit 1
+ cat $tables | grep -A10 $table_name | tr '\n' ' ' | awk '{gsub("[\\[\\]]", " ", $0); gsub("[[:blank:]]+"," ",$0); print $0}' > ${table}
+ grep -qE "${table_regexp}" ${table} || atf_fail "Bad counters for table ${table_name}"
+ done;
+
+ for state_regexp in \
+ "${epair_server1}a tcp ${net_server_host_tester}.* 6:4 pkts, 455:231 bytes, rule 4, " \
+ ; do
+ grep -qE "${state_regexp}" $states || atf_fail "State not found for '${state_regexp}'"
+ done
+}
+
+nat64_in_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "nat64_out" "cleanup"
+nat64_out_head()
+{
+ atf_set descr 'Counters on match and outbound af-to rules'
+ atf_set require.user root
+}
+
+nat64_out_body()
+{
+ setup_router_server_nat64
+
+ # af-to in outbound path requires routes for the pre-af-to traffic.
+ jexec router route add -inet6 64:ff9b::/96 -iface ${epair_server1}a
+
+ pft_set_rules router \
+ "set state-policy if-bound" \
+ "table <tbl_src_match> { ${net_tester_6_host_tester} }" \
+ "table <tbl_dst_match> { 64:ff9b::${net_server1_4_host_server} }" \
+ "table <tbl_src_pass> { ${net_tester_6_host_tester} }" \
+ "table <tbl_dst_pass> { 64:ff9b::${net_server1_4_host_server} }" \
+ "block" \
+ "pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv }" \
+ "pass in on ${epair_tester}b inet6 proto tcp keep state" \
+ "match out on ${epair_server1}a inet6 proto tcp from <tbl_src_match> to <tbl_dst_match> scrub (random-id)" \
+ "pass out on ${epair_server1}a inet6 proto tcp from <tbl_src_pass> to <tbl_dst_pass> \
+ af-to inet from (${epair_server1}a) \
+ keep state"
+
+ # Use a real TCP connection so that it will be properly closed, guaranteeing the amount of packets.
+ atf_check -s exit:0 -o match:"This is a test" -x \
+ "echo 'This is a test' | nc -w3 64:ff9b::${net_server1_4_host_server} echo"
+ sleep 1
+ get_counters
+
+ for rule_regexp in \
+ "@4 match out on ${epair_server1}a .* Packets: 10 Bytes: 686 States: 1 " \
+ "@5 pass out on ${epair_server1}a .* Packets: 10 Bytes: 686 States: 1 " \
+ ; do
+ grep -qE "${rule_regexp}" $rules || atf_fail "Rule regexp not found for '${rule_regexp}'"
+ done
+
+ # All tables have counters increased for In/Pass and Out/Pass, not XPass nor Block.
+ table_counters="Evaluations: NoMatch: 0 Match: 1 In/Block: Packets: 0 Bytes: 0 In/Pass: Packets: 4 Bytes: 231 In/XPass: Packets: 0 Bytes: 0 Out/Block: Packets: 0 Bytes: 0 Out/Pass: Packets: 6 Bytes: 455 Out/XPass: Packets: 0 Bytes: 0"
+ for table_test in \
+ "tbl_src_match___${table_counters}" \
+ "tbl_dst_match___${table_counters}" \
+ "tbl_src_pass___${table_counters}" \
+ "tbl_dst_pass___${table_counters}" \
+ ; do
+ table_name=${table_test%%___*}
+ table_regexp=${table_test##*___}
+ table=$(mktemp) || exit 1
+ cat $tables | grep -A10 $table_name | tr '\n' ' ' | awk '{gsub("[\\[\\]]", " ", $0); gsub("[[:blank:]]+"," ",$0); print $0}' > ${table}
+ grep -qE "${table_regexp}" ${table} || atf_fail "Bad counters for table ${table_name}"
+ done;
+
+ for state_regexp in \
+ "${epair_server1}a tcp 198.51.100.17:[0-9]+ \(64:ff9b::c633:6412\[7\]\) -> 198.51.100.18:7 \(2001:db8:4200::2\[[0-9]+\]\) .* 6:4 pkts, 455:231 bytes, rule 5," \
+ ; do
+ grep -qE "${state_regexp}" $states || atf_fail "State not found for '${state_regexp}'"
+ done
+}
+
+nat64_out_cleanup()
+{
+ pft_cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case "match_pass_state"
+ atf_add_test_case "match_pass_no_state"
+ atf_add_test_case "match_block"
+ atf_add_test_case "match_fail"
+ atf_add_test_case "nat_natonly"
+ atf_add_test_case "nat_nat"
+ atf_add_test_case "nat_match"
+ atf_add_test_case "nat_pass"
+ atf_add_test_case "rdr_match"
+ atf_add_test_case "nat64_in"
+ atf_add_test_case "nat64_out"
+}
diff --git a/tests/sys/netpfil/pf/nat64.sh b/tests/sys/netpfil/pf/nat64.sh
--- a/tests/sys/netpfil/pf/nat64.sh
+++ b/tests/sys/netpfil/pf/nat64.sh
@@ -213,12 +213,14 @@
atf_fail "Failed to connect to TCP server"
fi
+ sleep 1
+
# Interfaces of the state are reversed when doing inbound NAT64!
# FIXME: Packets from both directions are counted only on the inbound direction!
states=$(mktemp) || exit 1
jexec rtr pfctl -qvvss | normalize_pfctl_s > $states
for state_regexp in \
- "${epair_link}a tcp 192.0.2.1:[0-9]+ \(2001:db8::2\[[0-9]+\]\) -> 192.0.2.2:1234 \(64:ff9b::c000:202\[1234\]\) .* 9:0 pkts.* rule 3 .* origif: ${epair}b" \
+ "${epair_link}a tcp 192.0.2.1:[0-9]+ \(2001:db8::2\[[0-9]+\]\) -> 192.0.2.2:1234 \(64:ff9b::c000:202\[1234\]\) .* 5:4 pkts.* rule 3 .* origif: ${epair}b" \
; do
grep -qE "${state_regexp}" $states || atf_fail "State not found for '${state_regexp}'"
done
@@ -254,6 +256,8 @@
atf_fail "Failed to connect to TCP server"
fi
+ sleep 1
+
# Origif is not printed when identical as if.
states=$(mktemp) || exit 1
jexec rtr pfctl -qvvss | normalize_pfctl_s > $states
@@ -295,12 +299,14 @@
atf_fail "Failed to connect to TCP server"
fi
+ sleep 1
+
# Interfaces of the state are reversed when doing inbound NAT64!
# FIXME: Packets from both directions are counted only on the inbound direction!
states=$(mktemp) || exit 1
jexec rtr pfctl -qvvss | normalize_pfctl_s > $states
for state_regexp in \
- "all tcp 192.0.2.1:[0-9]+ \(2001:db8::2\[[0-9]+\]\) -> 192.0.2.2:1234 \(64:ff9b::c000:202\[1234\]\).* 9:0 pkts.* rule 3 .* origif: ${epair}b" \
+ "all tcp 192.0.2.1:[0-9]+ \(2001:db8::2\[[0-9]+\]\) -> 192.0.2.2:1234 \(64:ff9b::c000:202\[1234\]\).* 5:4 pkts.* rule 3 .* origif: ${epair}b" \
; do
grep -qE "${state_regexp}" $states || atf_fail "State not found for '${state_regexp}'"
done
@@ -336,6 +342,8 @@
atf_fail "Failed to connect to TCP server"
fi
+ sleep 1
+
# Origif is not printed when identical as if.
states=$(mktemp) || exit 1
jexec rtr pfctl -qvvss | normalize_pfctl_s > $states
@@ -1011,20 +1019,19 @@
pft_init
epair_link=$(vnet_mkepair)
- epair_null=$(vnet_mkepair)
epair=$(vnet_mkepair)
ifconfig ${epair}a inet6 2001:db8::2/64 up no_dad
route -6 add default 2001:db8::1
- vnet_mkjail rtr ${epair}b ${epair_link}a ${epair_null}a
+ vnet_mkjail rtr ${epair}b ${epair_link}a
jexec rtr ifconfig ${epair}b inet6 2001:db8::1/64 up no_dad
- jexec rtr ifconfig ${epair_null}a 192.0.2.3/24 up
jexec rtr ifconfig ${epair_link}a 192.0.2.1/24 up
vnet_mkjail dst ${epair_link}b
jexec dst ifconfig ${epair_link}b 192.0.2.2/24 up
- jexec dst route add default 192.0.2.1
+ jexec dst ifconfig lo0 203.0.113.1/32 alias
+ jexec dst route add default 192.0.2.2
# Sanity checks
atf_check -s exit:0 -o ignore \
@@ -1033,8 +1040,9 @@
jexec rtr pfctl -e
pft_set_rules rtr \
"set reassemble yes" \
+ "set debug loud" \
"set state-policy if-bound" \
- "block" \
+ "block log (all)" \
"pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv }" \
"pass in on ${epair}b route-to (${epair_link}a 192.0.2.2) inet6 from any to 64:ff9b::/96 af-to inet from (${epair_link}a)"
@@ -1044,11 +1052,11 @@
states=$(mktemp) || exit 1
jexec rtr pfctl -qvvss | normalize_pfctl_s > $states
+ cat $states
# Interfaces of the state are reversed when doing inbound NAT64!
- # FIXME: Packets from both directions are counted only on the inbound direction!
for state_regexp in \
- "${epair_link}a ipv6-icmp 192.0.2.1:.* \(2001:db8::2\[[0-9]+\]\) -> 192.0.2.2:8 \(64:ff9b::c000:202\[[0-9]+\]\).* 6:0 pkts.*route-to: 192.0.2.2@${epair_link}a origif: ${epair}b" \
+ "${epair_link}a ipv6-icmp 192.0.2.1:.* \(2001:db8::2\[[0-9]+\]\) -> 192.0.2.2:8 \(64:ff9b::c000:202\[[0-9]+\]\).* 3:3 pkts.*route-to: 192.0.2.2@${epair_link}a origif: ${epair}b" \
; do
grep -qE "${state_regexp}" $states || atf_fail "State not found for '${state_regexp}'"
done
diff --git a/tests/sys/netpfil/pf/utils.subr b/tests/sys/netpfil/pf/utils.subr
--- a/tests/sys/netpfil/pf/utils.subr
+++ b/tests/sys/netpfil/pf/utils.subr
@@ -217,6 +217,7 @@
jexec server route add -net ${net_tester} ${net_server_host_router}
inetd_conf=$(mktemp)
echo "discard stream tcp nowait root internal" > $inetd_conf
+ echo "echo stream tcp nowait root internal" >> $inetd_conf
jexec server inetd -p ${PWD}/inetd.pid $inetd_conf
}
@@ -271,6 +272,7 @@
jexec server route add -6 ${net_tester} ${net_server_host_router}
inetd_conf=$(mktemp)
echo "discard stream tcp6 nowait root internal" > $inetd_conf
+ echo "echo stream tcp6 nowait root internal" >> $inetd_conf
jexec server inetd -p ${PWD}/inetd.pid $inetd_conf
}
@@ -353,6 +355,7 @@
inetd_conf=$(mktemp)
echo "discard stream tcp46 nowait root internal" >> $inetd_conf
+ echo "echo stream tcp46 nowait root internal" >> $inetd_conf
vnet_mkjail server1 ${epair_server1}b
jexec server1 /etc/rc.d/netif start lo0
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Wed, Mar 4, 8:55 PM (6 m, 41 s)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
29256154
Default Alt Text
D52447.id161785.diff (60 KB)
Attached To
Mode
D52447: pf: Fix rule and state counters
Attached
Detach File
Event Timeline
Log In to Comment