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