Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/tests/sys/netpfil/pf/counters.sh
39507 views
1
#
2
# SPDX-License-Identifier: BSD-2-Clause
3
#
4
# Copyright (c) 2025 Kajetan Staszkiewicz
5
#
6
# Redistribution and use in source and binary forms, with or without
7
# modification, are permitted provided that the following conditions
8
# are met:
9
# 1. Redistributions of source code must retain the above copyright
10
# notice, this list of conditions and the following disclaimer.
11
# 2. Redistributions in binary form must reproduce the above copyright
12
# notice, this list of conditions and the following disclaimer in the
13
# documentation and/or other materials provided with the distribution.
14
#
15
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25
# SUCH DAMAGE.
26
27
. $(atf_get_srcdir)/utils.subr
28
29
get_counters()
30
{
31
echo " === rules ==="
32
rules=$(mktemp) || exit
33
(jexec router pfctl -qvvsn ; jexec router pfctl -qvvsr) | normalize_pfctl_s > $rules
34
cat $rules
35
36
echo " === tables ==="
37
tables=$(mktemp) || exit 1
38
jexec router pfctl -qvvsT > $tables
39
cat $tables
40
41
echo " === states ==="
42
states=$(mktemp) || exit 1
43
jexec router pfctl -qvvss | normalize_pfctl_s > $states
44
cat $states
45
46
echo " === nodes ==="
47
nodes=$(mktemp) || exit 1
48
jexec router pfctl -qvvsS | normalize_pfctl_s > $nodes
49
cat $nodes
50
}
51
52
atf_test_case "match_pass_state" "cleanup"
53
match_pass_state_head()
54
{
55
atf_set descr 'Counters on match and pass rules'
56
atf_set require.user root
57
}
58
59
match_pass_state_body()
60
{
61
setup_router_server_ipv6
62
63
# Thest counters for a statefull firewall. Expose the behaviour of
64
# increasing table counters if a table is used multiple times.
65
# The table "tbl_in" is used both in match and pass rule. It's counters
66
# are incremented twice. The tables "tbl_out_match" and "tbl_out_pass"
67
# are used only once and have their countes increased only once.
68
# Test source node counters for this simple scenario too.
69
pft_set_rules router \
70
"set state-policy if-bound" \
71
"table <tbl_in> { ${net_tester_host_tester} }" \
72
"table <tbl_out_pass> { ${net_server_host_server} }" \
73
"table <tbl_out_match> { ${net_server_host_server} }" \
74
"block" \
75
"pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv }" \
76
"match in on ${epair_tester}b inet6 proto tcp from <tbl_in> scrub (random-id)" \
77
"pass in on ${epair_tester}b inet6 proto tcp from <tbl_in> keep state (max-src-states 3 source-track rule)" \
78
"match out on ${epair_server}a inet6 proto tcp to <tbl_out_match> scrub (random-id)" \
79
"pass out on ${epair_server}a inet6 proto tcp to <tbl_out_pass> keep state"
80
81
# Use a real TCP connection so that it will be properly closed, guaranteeing the amount of packets.
82
atf_check -s exit:0 -o match:"This is a test" -x \
83
"echo 'This is a test' | nc -w3 ${net_server_host_server} echo"
84
# Let FINs pass through.
85
sleep 1
86
get_counters
87
88
for rule_regexp in \
89
"@3 match in on ${epair_tester}b .* Packets: 10 Bytes: 766 States: 1 " \
90
"@4 pass in on ${epair_tester}b .* Packets: 10 Bytes: 766 States: 1 " \
91
"@5 match out on ${epair_server}a .* Packets: 10 Bytes: 766 States: 1 " \
92
"@6 pass out on ${epair_server}a .* Packets: 10 Bytes: 766 States: 1 " \
93
; do
94
grep -qE "${rule_regexp}" $rules || atf_fail "Rule regexp not found for '${rule_regexp}'"
95
done
96
97
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"
98
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"
99
for table_test in \
100
"tbl_in___${table_counters_double}" \
101
"tbl_out_match___${table_counters_single}" \
102
"tbl_out_pass___${table_counters_single}" \
103
; do
104
table_name=${table_test%%___*}
105
table_regexp=${table_test##*___}
106
table=$(mktemp) || exit 1
107
cat $tables | grep -A10 $table_name | tr '\n' ' ' | awk '{gsub("[\\[\\]]", " ", $0); gsub("[[:blank:]]+"," ",$0); print $0}' > ${table}
108
grep -qE "${table_regexp}" ${table} || atf_fail "Bad counters for table ${table_name}"
109
done;
110
111
for state_regexp in \
112
"${epair_tester}b tcp ${net_server_host_server}.* <- ${net_tester_host_tester}.* 6:4 pkts, 455:311 bytes, rule 4," \
113
"${epair_server}a tcp ${net_server_host_tester}.* -> ${net_server_host_server}.* 6:4 pkts, 455:311 bytes, rule 6," \
114
; do
115
grep -qE "${state_regexp}" $states || atf_fail "State not found for '${state_regexp}'"
116
done
117
118
for node_regexp in \
119
"${net_tester_host_tester} -> :: .* 10 pkts, 766 bytes, filter rule 4, limit source-track"\
120
; do
121
grep -qE "${node_regexp}" $nodes || atf_fail "Source node not found for '${node_regexp}'"
122
done
123
}
124
125
match_pass_state_cleanup()
126
{
127
pft_cleanup
128
}
129
130
atf_test_case "match_pass_no_state" "cleanup"
131
match_pass_no_state_head()
132
{
133
atf_set descr 'Counters on match and pass rules without keep state'
134
atf_set require.user root
135
}
136
137
match_pass_no_state_body()
138
{
139
setup_router_server_ipv6
140
141
# Test counters for a stateless firewall.
142
# The table "tbl_in" is used both in match and pass rule in the inbound
143
# direction. The "In/Pass" counter is incremented twice. The table
144
# "tbl_inout" matches the same host on inbound and outbound direction.
145
# It will also be incremented twice. The tables "tbl_out_match" and
146
# "tbl_out_pass" will have their counters increased only once.
147
pft_set_rules router \
148
"table <tbl_in> { ${net_tester_host_tester} }" \
149
"table <tbl_inout> { ${net_tester_host_tester} }" \
150
"table <tbl_out_match> { ${net_server_host_server} }" \
151
"table <tbl_out_pass> { ${net_server_host_server} }" \
152
"block" \
153
"pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv }" \
154
"match in on ${epair_tester}b inet6 proto tcp from <tbl_inout>" \
155
"match in on ${epair_tester}b inet6 proto tcp from <tbl_in>" \
156
"pass in on ${epair_tester}b inet6 proto tcp from <tbl_in> no state" \
157
"pass out on ${epair_tester}b inet6 proto tcp to <tbl_in> no state" \
158
"match in on ${epair_server}a inet6 proto tcp from <tbl_out_match>" \
159
"pass in on ${epair_server}a inet6 proto tcp from <tbl_out_pass> no state" \
160
"match out on ${epair_server}a inet6 proto tcp from <tbl_inout> no state" \
161
"pass out on ${epair_server}a inet6 proto tcp to <tbl_out_pass> no state"
162
163
# Use a real TCP connection so that it will be properly closed, guaranteeing the amount of packets.
164
atf_check -s exit:0 -o match:"This is a test" -x \
165
"echo 'This is a test' | nc -w3 ${net_server_host_server} echo"
166
sleep 1
167
get_counters
168
169
for rule_regexp in \
170
"@3 match in on ${epair_tester}b .* Packets: 6 Bytes: 455 " \
171
"@4 match in on ${epair_tester}b .* Packets: 6 Bytes: 455 " \
172
"@5 pass in on ${epair_tester}b .* Packets: 6 Bytes: 455 " \
173
"@6 pass out on ${epair_tester}b .* Packets: 4 Bytes: 311 " \
174
"@7 match in on ${epair_server}a .* Packets: 4 Bytes: 311 " \
175
"@8 pass in on ${epair_server}a .* Packets: 4 Bytes: 311 " \
176
"@10 pass out on ${epair_server}a .* Packets: 6 Bytes: 455 " \
177
; do
178
grep -qE "${rule_regexp}" $rules || atf_fail "Rule regexp not found for '${rule_regexp}'"
179
done
180
181
for table_test in \
182
"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" \
183
"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" \
184
"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" \
185
"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" \
186
; do
187
table_name=${table_test%%___*}
188
table_regexp=${table_test##*___}
189
table=$(mktemp) || exit 1
190
cat $tables | grep -A10 $table_name | tr '\n' ' ' | awk '{gsub("[\\[\\]]", " ", $0); gsub("[[:blank:]]+"," ",$0); print $0}' > ${table}
191
grep -qE "${table_regexp}" ${table} || atf_fail "Bad counters for table ${table_name}"
192
done;
193
}
194
195
match_pass_no_state_cleanup()
196
{
197
pft_cleanup
198
}
199
200
atf_test_case "match_block" "cleanup"
201
match_block_head()
202
{
203
atf_set descr 'Counters on match and block rules'
204
atf_set require.user root
205
}
206
207
match_block_body()
208
{
209
setup_router_server_ipv6
210
211
# Stateful firewall with a blocking rule. The rule will have its
212
# counters increased because it matches and applies correctly.
213
# The "match" rule before the "pass" rule will have its counters
214
# increased for blocked traffic too.
215
pft_set_rules router \
216
"set state-policy if-bound" \
217
"table <tbl_in_match> { ${net_server_host_server} }" \
218
"table <tbl_in_block> { ${net_server_host_server} }" \
219
"block" \
220
"pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv }" \
221
"match in on ${epair_tester}b inet6 proto tcp to <tbl_in_match> scrub (random-id)" \
222
"block in on ${epair_tester}b inet6 proto tcp to <tbl_in_block>" \
223
"pass out on ${epair_server}a inet6 proto tcp keep state"
224
225
# Wait 3 seconds, that will cause 2 SYNs to be sent out.
226
echo 'This is a test' | nc -w3 ${net_server_host_server} echo
227
sleep 1
228
get_counters
229
230
for rule_regexp in \
231
"@3 match in on ${epair_tester}b .* Packets: 2 Bytes: 160 States: 0 " \
232
"@4 block drop in on ${epair_tester}b .* Packets: 2 Bytes: 160 States: 0 " \
233
; do
234
grep -qE "${rule_regexp}" $rules || atf_fail "Rule regexp not found for '${rule_regexp}'"
235
done
236
237
# OpenBSD has (In|Out)/Match. We don't (yet) have it in FreeBSD
238
# so we follow the action of the "pass" rule ("block" for this test)
239
# in "match" rules.
240
for table_test in \
241
"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" \
242
"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" \
243
; do
244
table_name=${table_test%%___*}
245
table_regexp=${table_test##*___}
246
table=$(mktemp) || exit 1
247
cat $tables | grep -A10 $table_name | tr '\n' ' ' | awk '{gsub("[\\[\\]]", " ", $0); gsub("[[:blank:]]+"," ",$0); print $0}' > ${table}
248
grep -qE "${table_regexp}" ${table} || atf_fail "Bad counters for table ${table_name}"
249
done;
250
}
251
252
match_block_cleanup()
253
{
254
pft_cleanup
255
}
256
257
atf_test_case "match_fail" "cleanup"
258
match_fail_head()
259
{
260
atf_set descr 'Counters on match and failing pass rules'
261
atf_set require.user root
262
}
263
264
match_fail_body()
265
{
266
setup_router_server_ipv6
267
268
# Statefull firewall with a failing "pass" rule.
269
# When the rule can't apply it will not have its counters increased.
270
pft_set_rules router \
271
"set state-policy if-bound" \
272
"table <tbl_in_match> { ${net_server_host_server} }" \
273
"table <tbl_in_fail> { ${net_server_host_server} }" \
274
"block" \
275
"pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv }" \
276
"match in on ${epair_tester}b inet6 proto tcp to <tbl_in_match> scrub (random-id)" \
277
"pass in on ${epair_tester}b inet6 proto tcp to <tbl_in_fail> keep state (max 1)" \
278
"pass out on ${epair_server}a inet6 proto tcp keep state"
279
280
# The first test will pass and increase the counters for all rules.
281
echo 'This is a test' | nc -w3 ${net_server_host_server} echo
282
# The second test will go through the "match" rules but fail
283
# on the "pass" rule due to 'keep state (max 1)'.
284
# Wait 3 seconds, that will cause 2 SYNs to be sent out.
285
echo 'This is a test' | nc -w3 ${net_server_host_server} echo
286
sleep 1
287
get_counters
288
289
for rule_regexp in \
290
"@3 match in on ${epair_tester}b .* Packets: 10 Bytes: 766 States: 1 " \
291
"@4 pass in on ${epair_tester}b .* Packets: 10 Bytes: 766 States: 1 " \
292
; do
293
grep -qE "${rule_regexp}" $rules || atf_fail "Rule regexp not found for '${rule_regexp}'"
294
done
295
296
$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"
297
for table_test in \
298
"tbl_in_match___${table_counters_single}" \
299
"tbl_in_fail___${table_counters_single}" \
300
; do
301
table_name=${table_test%%___*}
302
table_regexp=${table_test##*___}
303
table=$(mktemp) || exit 1
304
cat $tables | grep -A10 $table_name | tr '\n' ' ' | awk '{gsub("[\\[\\]]", " ", $0); gsub("[[:blank:]]+"," ",$0); print $0}' > ${table}
305
grep -qE "${table_regexp}" ${table} || atf_fail "Bad counters for table ${table_name}"
306
done;
307
}
308
309
match_fail_cleanup()
310
{
311
pft_cleanup
312
}
313
314
atf_test_case "nat_natonly" "cleanup"
315
nat_natonly_head()
316
{
317
atf_set descr 'Counters on only a NAT rule creating state'
318
atf_set require.user root
319
}
320
321
nat_natonly_body()
322
{
323
setup_router_server_ipv6
324
325
# NAT is applied on the "nat" rule.
326
# The "nat" rule matches on pre-NAT addresses. There is no separate
327
# "pass" rule so the "nat" rule creates the state.
328
pft_set_rules router \
329
"set state-policy if-bound" \
330
"table <tbl_src_nat> { ${net_tester_host_tester} }" \
331
"table <tbl_dst_nat> { ${net_server_host_server} }" \
332
"nat on ${epair_server}a inet6 proto tcp from <tbl_src_nat> to <tbl_dst_nat> -> ${net_server_host_router}"
333
334
# Use a real TCP connection so that it will be properly closed, guaranteeing the amount of packets.
335
atf_check -s exit:0 -o match:"This is a test" -x \
336
"echo 'This is a test' | nc -w3 ${net_server_host_server} echo"
337
sleep 1
338
get_counters
339
340
for rule_regexp in \
341
"@0 nat on ${epair_server}a .* Packets: 10 Bytes: 766 States: 1 " \
342
; do
343
grep -qE "${rule_regexp}" $rules || atf_fail "Rule regexp not found for '${rule_regexp}'"
344
done
345
346
# All tables have counters increased for In/Pass and Out/Pass, not XPass.
347
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"
348
for table_test in \
349
"tbl_src_nat___${table_counters}" \
350
"tbl_dst_nat___${table_counters}" \
351
; do
352
table_name=${table_test%%___*}
353
table_regexp=${table_test##*___}
354
table=$(mktemp) || exit 1
355
cat $tables | grep -A10 $table_name | tr '\n' ' ' | awk '{gsub("[\\[\\]]", " ", $0); gsub("[[:blank:]]+"," ",$0); print $0}' > ${table}
356
grep -qE "${table_regexp}" ${table} || atf_fail "Bad counters for table ${table_name}"
357
done;
358
359
for state_regexp in \
360
"all tcp ${net_server_host_router}.* -> ${net_server_host_server}.* 6:4 pkts, 455:311 bytes" \
361
; do
362
grep -qE "${state_regexp}" $states || atf_fail "State not found for '${state_regexp}'"
363
done
364
}
365
366
nat_natonly_cleanup()
367
{
368
pft_cleanup
369
}
370
371
atf_test_case "nat_nat" "cleanup"
372
nat_nat_head()
373
{
374
atf_set descr 'Counters on NAT, match and pass rules with keep state'
375
atf_set require.user root
376
}
377
378
nat_nat_body()
379
{
380
setup_router_server_ipv6
381
382
# NAT is applied in the NAT ruleset.
383
# The "nat" rule matches on pre-NAT addresses.
384
# The "match" rule matches on post-NAT addresses.
385
# The "pass" rule matches on post-NAT addresses and creates the state.
386
pft_set_rules router \
387
"set state-policy if-bound" \
388
"table <tbl_src_nat> { ${net_tester_host_tester} }" \
389
"table <tbl_dst_nat> { ${net_server_host_server} }" \
390
"table <tbl_src_match> { ${net_server_host_router} }" \
391
"table <tbl_dst_match> { ${net_server_host_server} }" \
392
"table <tbl_src_pass> { ${net_server_host_router} }" \
393
"table <tbl_dst_pass> { ${net_server_host_server} }" \
394
"nat on ${epair_server}a inet6 proto tcp from <tbl_src_nat> to <tbl_dst_nat> -> ${net_server_host_router}" \
395
"block" \
396
"pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv }" \
397
"pass in on ${epair_tester}b inet6 proto tcp keep state" \
398
"match out on ${epair_server}a inet6 proto tcp from <tbl_src_match> to <tbl_dst_match> scrub (random-id)" \
399
"pass out on ${epair_server}a inet6 proto tcp from <tbl_src_pass> to <tbl_dst_pass> keep state"
400
401
# Use a real TCP connection so that it will be properly closed, guaranteeing the amount of packets.
402
atf_check -s exit:0 -o match:"This is a test" -x \
403
"echo 'This is a test' | nc -w3 ${net_server_host_server} echo"
404
sleep 1
405
get_counters
406
407
for rule_regexp in \
408
"@0 nat on ${epair_server}a .* Packets: 10 Bytes: 766 States: 1 " \
409
"@4 match out on ${epair_server}a .* Packets: 10 Bytes: 766 States: 1 " \
410
"@5 pass out on ${epair_server}a .* Packets: 10 Bytes: 766 States: 1 " \
411
; do
412
grep -qE "${rule_regexp}" $rules || atf_fail "Rule regexp not found for '${rule_regexp}'"
413
done
414
415
# All tables have counters increased for In/Pass and Out/Pass, not XPass nor Block.
416
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"
417
for table_test in \
418
"tbl_src_nat___${table_counters}" \
419
"tbl_dst_nat___${table_counters}" \
420
"tbl_src_match___${table_counters}" \
421
"tbl_dst_match___${table_counters}" \
422
"tbl_src_pass___${table_counters}" \
423
"tbl_dst_pass___${table_counters}" \
424
; do
425
table_name=${table_test%%___*}
426
table_regexp=${table_test##*___}
427
table=$(mktemp) || exit 1
428
cat $tables | grep -A10 $table_name | tr '\n' ' ' | awk '{gsub("[\\[\\]]", " ", $0); gsub("[[:blank:]]+"," ",$0); print $0}' > ${table}
429
grep -qE "${table_regexp}" ${table} || atf_fail "Bad counters for table ${table_name}"
430
done;
431
432
for state_regexp in \
433
"${epair_server}a tcp ${net_server_host_router}.* -> ${net_server_host_server}.* 6:4 pkts, 455:311 bytes, rule 5," \
434
; do
435
grep -qE "${state_regexp}" $states || atf_fail "State not found for '${state_regexp}'"
436
done
437
}
438
439
nat_nat_cleanup()
440
{
441
pft_cleanup
442
}
443
444
atf_test_case "nat_match" "cleanup"
445
nat_match_head()
446
{
447
atf_set descr 'Counters on match with NAT and pass rules'
448
atf_set require.user root
449
}
450
451
nat_match_body()
452
{
453
setup_router_server_ipv6
454
455
# NAT is applied on the "match" rule.
456
# The "match" rule up to and including the NAT rule match on pre-NAT addresses.
457
# The "match" rule after NAT matches on post-NAT addresses.
458
# The "pass" rule matches on post-NAT addresses and creates the state.
459
pft_set_rules router \
460
"set state-policy if-bound" \
461
"table <tbl_src_match1> { ${net_tester_host_tester} }" \
462
"table <tbl_dst_match1> { ${net_server_host_server} }" \
463
"table <tbl_src_match2> { ${net_tester_host_tester} }" \
464
"table <tbl_dst_match2> { ${net_server_host_server} }" \
465
"table <tbl_src_match3> { ${net_server_host_router} }" \
466
"table <tbl_dst_match3> { ${net_server_host_server} }" \
467
"table <tbl_src_pass> { ${net_server_host_router} }" \
468
"table <tbl_dst_pass> { ${net_server_host_server} }" \
469
"block" \
470
"pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv }" \
471
"pass in on ${epair_tester}b inet6 proto tcp keep state" \
472
"match out on ${epair_server}a inet6 proto tcp from <tbl_src_match1> to <tbl_dst_match1> scrub (random-id)" \
473
"match out on ${epair_server}a inet6 proto tcp from <tbl_src_match2> to <tbl_dst_match2> nat-to ${net_server_host_router}" \
474
"match out on ${epair_server}a inet6 proto tcp from <tbl_src_match3> to <tbl_dst_match3> scrub (random-id)" \
475
"pass out on ${epair_server}a inet6 proto tcp from <tbl_src_pass> to <tbl_dst_pass> keep state"
476
477
# Use a real TCP connection so that it will be properly closed, guaranteeing the amount of packets.
478
atf_check -s exit:0 -o match:"This is a test" -x \
479
"echo 'This is a test' | nc -w3 ${net_server_host_server} echo"
480
sleep 1
481
get_counters
482
483
for rule_regexp in \
484
"@4 match out on ${epair_server}a .* Packets: 10 Bytes: 766 States: 1 " \
485
"@5 match out on ${epair_server}a .* Packets: 10 Bytes: 766 States: 1 " \
486
"@6 match out on ${epair_server}a .* Packets: 10 Bytes: 766 States: 1 " \
487
"@7 pass out on ${epair_server}a .* Packets: 10 Bytes: 766 States: 1 " \
488
; do
489
grep -qE "${rule_regexp}" $rules || atf_fail "Rule regexp not found for '${rule_regexp}'"
490
done
491
492
# All tables have counters increased for In/Pass and Out/Pass, not XPass nor Block.
493
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"
494
for table_test in \
495
"tbl_src_match1___${table_counters}" \
496
"tbl_dst_match1___${table_counters}" \
497
"tbl_src_match2___${table_counters}" \
498
"tbl_dst_match2___${table_counters}" \
499
"tbl_src_match3___${table_counters}" \
500
"tbl_dst_match3___${table_counters}" \
501
"tbl_src_pass___${table_counters}" \
502
"tbl_dst_pass___${table_counters}" \
503
; do
504
table_name=${table_test%%___*}
505
table_regexp=${table_test##*___}
506
table=$(mktemp) || exit 1
507
cat $tables | grep -A10 $table_name | tr '\n' ' ' | awk '{gsub("[\\[\\]]", " ", $0); gsub("[[:blank:]]+"," ",$0); print $0}' > ${table}
508
grep -qE "${table_regexp}" ${table} || atf_fail "Bad counters for table ${table_name}"
509
done;
510
511
for state_regexp in \
512
"${epair_server}a tcp ${net_server_host_tester}.* -> ${net_server_host_server}.* 6:4 pkts, 455:311 bytes, rule 7, " \
513
; do
514
grep -qE "${state_regexp}" $states || atf_fail "State not found for '${state_regexp}'"
515
done
516
}
517
518
nat_match_cleanup()
519
{
520
pft_cleanup
521
}
522
523
atf_test_case "nat_pass" "cleanup"
524
nat_pass_head()
525
{
526
atf_set descr 'Counters on match, and pass with NAT rules'
527
atf_set require.user root
528
}
529
530
nat_pass_body()
531
{
532
setup_router_server_ipv6
533
534
# NAT is applied on the "pass" rule which also creates the state.
535
# All rules match on pre-NAT addresses.
536
pft_set_rules router \
537
"set state-policy if-bound" \
538
"table <tbl_src_match> { ${net_tester_host_tester} }" \
539
"table <tbl_dst_match> { ${net_server_host_server} }" \
540
"table <tbl_src_pass> { ${net_tester_host_tester} }" \
541
"table <tbl_dst_pass> { ${net_server_host_server} }" \
542
"block" \
543
"pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv }" \
544
"pass in on ${epair_tester}b inet6 proto tcp keep state" \
545
"match out on ${epair_server}a inet6 proto tcp from <tbl_src_match> to <tbl_dst_match> scrub (random-id)" \
546
"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"
547
548
# Use a real TCP connection so that it will be properly closed, guaranteeing the amount of packets.
549
atf_check -s exit:0 -o match:"This is a test" -x \
550
"echo 'This is a test' | nc -w3 ${net_server_host_server} echo"
551
sleep 1
552
get_counters
553
554
for rule_regexp in \
555
"@4 match out on ${epair_server}a .* Packets: 10 Bytes: 766 States: 1 " \
556
"@5 pass out on ${epair_server}a .* Packets: 10 Bytes: 766 States: 1 " \
557
; do
558
grep -qE "${rule_regexp}" $rules || atf_fail "Rule regexp not found for '${rule_regexp}'"
559
done
560
561
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"
562
for table_test in \
563
"tbl_src_match___${table_counters}" \
564
"tbl_dst_match___${table_counters}" \
565
"tbl_src_pass___${table_counters}" \
566
"tbl_dst_pass___${table_counters}" \
567
; do
568
table_name=${table_test%%___*}
569
table_regexp=${table_test##*___}
570
table=$(mktemp) || exit 1
571
cat $tables | grep -A10 $table_name | tr '\n' ' ' | awk '{gsub("[\\[\\]]", " ", $0); gsub("[[:blank:]]+"," ",$0); print $0}' > ${table}
572
grep -qE "${table_regexp}" ${table} || atf_fail "Bad counters for table ${table_name}"
573
done;
574
575
for state_regexp in \
576
"${epair_server}a tcp ${net_server_host_router}.* -> ${net_server_host_server}.* 6:4 pkts, 455:311 bytes, rule 5," \
577
; do
578
grep -qE "${state_regexp}" $states || atf_fail "State not found for '${state_regexp}'"
579
done
580
}
581
582
nat_pass_cleanup()
583
{
584
pft_cleanup
585
}
586
587
atf_test_case "rdr_match" "cleanup"
588
rdr_match_head()
589
{
590
atf_set descr 'Counters on match with RDR and pass rules'
591
atf_set require.user root
592
}
593
594
rdr_match_body()
595
{
596
setup_router_server_ipv6
597
598
# Similar to the nat_match test but for the RDR action.
599
# Hopefully we don't need all other tests duplicated for RDR.
600
# Send traffic to a non-existing host, RDR it to the server.
601
#
602
# The "match" rule up to and including the RDR rule match on pre-RDR dst address.
603
# The "match" rule after NAT matches on post-RDR dst address.
604
# The "pass" rule matches on post-RDR dst address.
605
net_server_host_notserver=${net_server_host_server%%::*}::3
606
pft_set_rules router \
607
"set state-policy if-bound" \
608
"table <tbl_src_match1> { ${net_tester_host_tester} }" \
609
"table <tbl_dst_match1> { ${net_server_host_notserver} }" \
610
"table <tbl_src_match2> { ${net_tester_host_tester} }" \
611
"table <tbl_dst_match2> { ${net_server_host_notserver} }" \
612
"table <tbl_src_match3> { ${net_tester_host_tester} }" \
613
"table <tbl_dst_match3> { ${net_server_host_server} }" \
614
"table <tbl_src_pass> { ${net_tester_host_tester} }" \
615
"table <tbl_dst_pass> { ${net_server_host_server} }" \
616
"block" \
617
"pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv }" \
618
"pass out on ${epair_server}a inet6 proto tcp keep state" \
619
"match in on ${epair_tester}b inet6 proto tcp from <tbl_src_match1> to <tbl_dst_match1> scrub (random-id)" \
620
"match in on ${epair_tester}b inet6 proto tcp from <tbl_src_match2> to <tbl_dst_match2> rdr-to ${net_server_host_server}" \
621
"match in on ${epair_tester}b inet6 proto tcp from <tbl_src_match3> to <tbl_dst_match3> scrub (random-id)" \
622
"pass in on ${epair_tester}b inet6 proto tcp from <tbl_src_pass> to <tbl_dst_pass> keep state"
623
624
# Use a real TCP connection so that it will be properly closed, guaranteeing the amount of packets.
625
atf_check -s exit:0 -o match:"This is a test" -x \
626
"echo 'This is a test' | nc -w3 ${net_server_host_notserver} echo"
627
sleep 1
628
get_counters
629
630
for rule_regexp in \
631
"@4 match in on ${epair_tester}b .* Packets: 10 Bytes: 766 States: 1 " \
632
"@5 match in on ${epair_tester}b .* Packets: 10 Bytes: 766 States: 1 " \
633
"@6 match in on ${epair_tester}b .* Packets: 10 Bytes: 766 States: 1 " \
634
"@7 pass in on ${epair_tester}b .* Packets: 10 Bytes: 766 States: 1 " \
635
; do
636
grep -qE "${rule_regexp}" $rules || atf_fail "Rule regexp not found for '${rule_regexp}'"
637
done
638
639
# All tables have counters increased for In/Pass and Out/Pass, not XPass nor Block.
640
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"
641
for table_test in \
642
"tbl_src_match1___${table_counters}" \
643
"tbl_dst_match1___${table_counters}" \
644
"tbl_src_match2___${table_counters}" \
645
"tbl_dst_match2___${table_counters}" \
646
"tbl_src_match3___${table_counters}" \
647
"tbl_dst_match3___${table_counters}" \
648
"tbl_src_pass___${table_counters}" \
649
"tbl_dst_pass___${table_counters}" \
650
; do
651
table_name=${table_test%%___*}
652
table_regexp=${table_test##*___}
653
table=$(mktemp) || exit 1
654
cat $tables | grep -A10 $table_name | tr '\n' ' ' | awk '{gsub("[\\[\\]]", " ", $0); gsub("[[:blank:]]+"," ",$0); print $0}' > ${table}
655
grep -qE "${table_regexp}" ${table} || atf_fail "Bad counters for table ${table_name}"
656
done;
657
658
for state_regexp in \
659
"${epair_tester}b tcp ${net_server_host_server}.* 6:4 pkts, 455:311 bytes, rule 7, " \
660
; do
661
grep -qE "${state_regexp}" $states || atf_fail "State not found for '${state_regexp}'"
662
done
663
}
664
665
rdr_match_cleanup()
666
{
667
pft_cleanup
668
}
669
670
atf_test_case "nat64_in" "cleanup"
671
nat64_in_head()
672
{
673
atf_set descr 'Counters on match and inbound af-to rules'
674
atf_set require.user root
675
}
676
677
nat64_in_body()
678
{
679
setup_router_server_nat64
680
681
pft_set_rules router \
682
"set state-policy if-bound" \
683
"table <tbl_src_match> { ${net_tester_6_host_tester} }" \
684
"table <tbl_dst_match> { 64:ff9b::${net_server1_4_host_server} }" \
685
"table <tbl_src_pass> { ${net_tester_6_host_tester} }" \
686
"table <tbl_dst_pass> { 64:ff9b::${net_server1_4_host_server} }" \
687
"block log" \
688
"pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv }" \
689
"match in on ${epair_tester}b inet6 proto tcp from <tbl_src_match> to <tbl_dst_match> scrub (random-id)" \
690
"pass in on ${epair_tester}b inet6 proto tcp from <tbl_src_pass> to <tbl_dst_pass> \
691
af-to inet from (${epair_server1}a) \
692
keep state"
693
694
# Use a real TCP connection so that it will be properly closed, guaranteeing the amount of packets.
695
atf_check -s exit:0 -o match:"This is a test" -x \
696
"echo 'This is a test' | nc -w3 64:ff9b::${net_server1_4_host_server} echo"
697
sleep 1
698
get_counters
699
700
# The amount of packets is counted properly but sizes are not because
701
# pd->tot_len is always post-nat, even when updating pre-nat counters.
702
for rule_regexp in \
703
"@3 match in on ${epair_tester}b .* Packets: 10 Bytes: 686 States: 1 " \
704
"@4 pass in on ${epair_tester}b .* Packets: 10 Bytes: 686 States: 1 " \
705
; do
706
grep -qE "${rule_regexp}" $rules || atf_fail "Rule regexp not found for '${rule_regexp}'"
707
done
708
709
# All tables have counters increased for In/Pass and Out/Pass, not XPass nor Block.
710
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"
711
for table_test in \
712
"tbl_src_match___${table_counters}" \
713
"tbl_dst_match___${table_counters}" \
714
"tbl_src_pass___${table_counters}" \
715
"tbl_dst_pass___${table_counters}" \
716
; do
717
table_name=${table_test%%___*}
718
table_regexp=${table_test##*___}
719
table=$(mktemp) || exit 1
720
cat $tables | grep -A10 $table_name | tr '\n' ' ' | awk '{gsub("[\\[\\]]", " ", $0); gsub("[[:blank:]]+"," ",$0); print $0}' > ${table}
721
grep -qE "${table_regexp}" ${table} || atf_fail "Bad counters for table ${table_name}"
722
done;
723
724
for state_regexp in \
725
"${epair_server1}a tcp ${net_server_host_tester}.* 6:4 pkts, 455:231 bytes, rule 4, " \
726
; do
727
grep -qE "${state_regexp}" $states || atf_fail "State not found for '${state_regexp}'"
728
done
729
730
echo " === interfaces === "
731
echo " === tester === "
732
jexec router pfctl -qvvsI -i ${epair_tester}b
733
echo " === server === "
734
jexec router pfctl -qvvsI -i ${epair_server1}a
735
echo " === "
736
}
737
738
nat64_in_cleanup()
739
{
740
pft_cleanup
741
}
742
743
atf_test_case "nat64_out" "cleanup"
744
nat64_out_head()
745
{
746
atf_set descr 'Counters on match and outbound af-to rules'
747
atf_set require.user root
748
}
749
750
nat64_out_body()
751
{
752
setup_router_server_nat64
753
754
# af-to in outbound path requires routes for the pre-af-to traffic.
755
jexec router route add -inet6 64:ff9b::/96 -iface ${epair_server1}a
756
757
pft_set_rules router \
758
"set state-policy if-bound" \
759
"table <tbl_src_match> { ${net_tester_6_host_tester} }" \
760
"table <tbl_dst_match> { 64:ff9b::${net_server1_4_host_server} }" \
761
"table <tbl_src_pass> { ${net_tester_6_host_tester} }" \
762
"table <tbl_dst_pass> { 64:ff9b::${net_server1_4_host_server} }" \
763
"block log " \
764
"pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv }" \
765
"pass in on ${epair_tester}b inet6 proto tcp keep state" \
766
"match out on ${epair_server1}a inet6 proto tcp from <tbl_src_match> to <tbl_dst_match> scrub (random-id)" \
767
"pass out on ${epair_server1}a inet6 proto tcp from <tbl_src_pass> to <tbl_dst_pass> \
768
af-to inet from (${epair_server1}a) \
769
keep state"
770
771
# Use a real TCP connection so that it will be properly closed, guaranteeing the amount of packets.
772
atf_check -s exit:0 -o match:"This is a test" -x \
773
"echo 'This is a test' | nc -w3 64:ff9b::${net_server1_4_host_server} echo"
774
sleep 1
775
get_counters
776
777
for rule_regexp in \
778
"@4 match out on ${epair_server1}a .* Packets: 10 Bytes: 686 States: 1 " \
779
"@5 pass out on ${epair_server1}a .* Packets: 10 Bytes: 686 States: 1 " \
780
; do
781
grep -qE "${rule_regexp}" $rules || atf_fail "Rule regexp not found for '${rule_regexp}'"
782
done
783
784
# All tables have counters increased for In/Pass and Out/Pass, not XPass nor Block.
785
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"
786
for table_test in \
787
"tbl_src_match___${table_counters}" \
788
"tbl_dst_match___${table_counters}" \
789
"tbl_src_pass___${table_counters}" \
790
"tbl_dst_pass___${table_counters}" \
791
; do
792
table_name=${table_test%%___*}
793
table_regexp=${table_test##*___}
794
table=$(mktemp) || exit 1
795
cat $tables | grep -A10 $table_name | tr '\n' ' ' | awk '{gsub("[\\[\\]]", " ", $0); gsub("[[:blank:]]+"," ",$0); print $0}' > ${table}
796
grep -qE "${table_regexp}" ${table} || atf_fail "Bad counters for table ${table_name}"
797
done;
798
799
for state_regexp in \
800
"${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," \
801
; do
802
grep -qE "${state_regexp}" $states || atf_fail "State not found for '${state_regexp}'"
803
done
804
805
echo " === interfaces === "
806
echo " === tester === "
807
jexec router pfctl -qvvsI -i ${epair_tester}b
808
echo " === server === "
809
jexec router pfctl -qvvsI -i ${epair_server1}a
810
echo " === "
811
}
812
813
nat64_out_cleanup()
814
{
815
pft_cleanup
816
}
817
818
atf_init_test_cases()
819
{
820
atf_add_test_case "match_pass_state"
821
atf_add_test_case "match_pass_no_state"
822
atf_add_test_case "match_block"
823
atf_add_test_case "match_fail"
824
atf_add_test_case "nat_natonly"
825
atf_add_test_case "nat_nat"
826
atf_add_test_case "nat_match"
827
atf_add_test_case "nat_pass"
828
atf_add_test_case "rdr_match"
829
atf_add_test_case "nat64_in"
830
atf_add_test_case "nat64_out"
831
}
832
833