npf_ext_test.c revision 1.2 1
2 /*-
3 * Copyright (c) 2026 The NetBSD Foundation, Inc.
4 * All rights reserved.
5 *
6 * This code is derived from software contributed to The NetBSD Foundation
7 * by Emmanuel Nyarko.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
19 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
20 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
22 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 * POSSIBILITY OF SUCH DAMAGE.
29 */
30
31 /*
32 * NPF tests of extension.
33 */
34
35 #ifdef _KERNEL
36 #include <sys/types.h>
37 #endif
38
39 #include "npf_impl.h"
40 #include "npf_test.h"
41
42 static const struct test_case {
43 const char * src;
44 in_port_t sport;
45 const char * dst;
46 in_port_t dport;
47 const char * ifname;
48 int di;
49 int af;
50 } test_cases[] = {
51 {
52 LOCAL_IP60, 15000, LOCAL_IP70, 7000,
53 IFNAME_EXT, PFIL_OUT, AF_INET
54 },
55 {
56 LOCAL_IP60, 15040, LOCAL_IP70, 6000,
57 IFNAME_EXT, PFIL_OUT, AF_INET
58 },
59
60 };
61
62 #define TCP_OPT_LEN 4
63 static struct mbuf *
64 mbuf_get_pkt_with_option(int af, int proto, const char *src, const char *dst,
65 int sport, int dport)
66 {
67 struct mbuf *m;
68 struct ip *ip;
69 struct tcphdr *th;
70 void *ipsrc, *ipdst;
71 int size = 0;
72 uint8_t *opt;
73 uint16_t mss = 1400;
74
75 m = m_gethdr(M_WAITOK, MT_HEADER);
76 ip = mtod(m, struct ip *);
77
78 ip->ip_v = IPVERSION;
79 ip->ip_hl = sizeof(struct ip) >> 2;
80 ip->ip_off = 0;
81 ip->ip_ttl = 64;
82 ip->ip_p = proto;
83
84 ipsrc = &ip->ip_src.s_addr;
85 ipdst = &ip->ip_dst.s_addr;
86
87 npf_inet_pton(af, src, ipsrc);
88 npf_inet_pton(af, dst, ipdst);
89
90 th = mbuf_return_hdrs(m, false, &ip);
91 size = sizeof(struct ip) + sizeof(struct tcphdr) + TCP_OPT_LEN;
92 th->th_flags |= TH_SYN;
93 th->th_off = (sizeof(struct tcphdr) + TCP_OPT_LEN) >> 2;
94 th->th_sport = htons(sport);
95 th->th_dport = htons(dport);
96
97 opt = (uint8_t *)th + sizeof(struct tcphdr);
98 opt[0] = TCPOPT_MAXSEG; /* Kind = 2 */
99 opt[1] = TCPOLEN_MAXSEG; /* Length = 4 */
100 opt[2] = (mss >> 8) & 0xff; /* 0x05 */
101 opt[3] = mss & 0xff; /* 0x29 */
102
103 ip->ip_len = htons(size);
104
105 m->m_pkthdr.len = size;
106 m->m_len = size;
107 m->m_next = NULL;
108 return m;
109 }
110
111 static int
112 run_raw_testcase(npf_cache_t *npc, unsigned i, bool verbose)
113 {
114 const struct test_case *t = &test_cases[i];
115 npf_t *npf = npf_getkernctx();
116 int slock, error;
117 npf_rproc_t *rp;
118 npf_rule_t *rl;
119 npf_match_info_t mi;
120 int decision = 1;
121
122 slock = npf_config_read_enter(npf);
123 rl = npf_ruleset_inspect(npc, npf_config_ruleset(npf), t->di, NPF_RULE_LAYER_3);
124 if (rl) {
125 error = npf_rule_conclude(rl, &mi);
126 } else {
127 error = ENOENT;
128 }
129 npf_config_read_exit(npf, slock);
130
131 if (error)
132 return error;
133
134 rp = npf_rule_getrproc(rl);
135 assert(rp != NULL);
136
137 error = npf_rproc_run(npc, rp, &mi, &decision);
138
139 if (error == 1) {
140 npf_rproc_release(rp);
141 }
142
143 return error;
144 }
145
146 static void
147 npf_get_normalize_params(npf_cache_t *npc, uint16_t *ip_ttl, uint8_t *ip_id, int *df, uint16_t *maxmss)
148 {
149 int wscale;
150 struct ip *ip = npc->npc_ip.v4;
151 *ip_id = ip->ip_id;
152 *ip_ttl = ip->ip_ttl;
153 *df = ip->ip_off & IP_DF;
154 npf_fetch_tcpopts(npc, maxmss, &wscale);
155 }
156
157 bool
158 npf_procedure_test(bool verbose)
159 {
160 int ret;
161 for (unsigned i = 0; i < __arraycount(test_cases); i++) {
162 const struct test_case *t = &test_cases[i];
163 npf_cache_t *npc;
164 struct mbuf *m;
165 uint16_t ip_ttl;
166 uint8_t ip_id;
167 int df;
168 uint16_t maxmss;
169
170 if (npf_test_getif(t->ifname) == NULL) {
171 printf("Interface %s is not configured.\n", t->ifname);
172 return false;
173 }
174
175 m = mbuf_get_pkt_with_option(t->af, IPPROTO_TCP, t->src, t->dst, 9000, 9000);
176 npc = get_cached_pkt(m, t->ifname, NPF_RULE_LAYER_3);
177
178 ret = run_raw_testcase(npc, i, verbose);
179 if (verbose) {
180 printf("%s: expected return in raw testcase 1, returned %d\n", __func__, ret);
181 }
182 CHECK_TRUE(ret == 1);
183
184 npf_get_normalize_params(npc, &ip_ttl, &ip_id, &df, &maxmss);
185 maxmss = ntohs(maxmss);
186
187 if (verbose) {
188 printf("Normalization:\texpected 164 ttl but got %u\n", ip_ttl);
189 printf("Normalization:\texpected 1321 maxmss but got %u\n", maxmss);
190 }
191 CHECK_TRUE((df & IP_DF) == 0);
192 CHECK_TRUE(maxmss == 1321);
193 CHECK_TRUE(ip_ttl == 164);
194 put_cached_pkt(npc);
195 }
196
197 return true;
198 }
199