163f44833Srmind /* 23d9a792dSrmind * NPF ruleset tests. 363f44833Srmind * 463f44833Srmind * Public Domain. 563f44833Srmind */ 663f44833Srmind 7f75d79ebSchristos #ifdef _KERNEL 863f44833Srmind #include <sys/types.h> 9f75d79ebSchristos #endif 1063f44833Srmind 1163f44833Srmind #include "npf_impl.h" 1263f44833Srmind #include "npf_test.h" 1363f44833Srmind 1463f44833Srmind #define RESULT_PASS 0 1563f44833Srmind #define RESULT_BLOCK ENETUNREACH 1663f44833Srmind 1763f44833Srmind static const struct test_case { 18ccafaf8eSriastradh int af; 1963f44833Srmind const char * src; 2063f44833Srmind const char * dst; 2163f44833Srmind const char * ifname; 2263f44833Srmind int di; 2363f44833Srmind int stateful_ret; 2463f44833Srmind int ret; 2563f44833Srmind } test_cases[] = { 2663f44833Srmind 2763f44833Srmind /* Stateful pass. */ 2863f44833Srmind { 29ccafaf8eSriastradh .af = AF_INET, 3063f44833Srmind .src = "10.1.1.1", .dst = "10.1.1.2", 3163f44833Srmind .ifname = IFNAME_INT, .di = PFIL_OUT, 3263f44833Srmind .stateful_ret = RESULT_PASS, .ret = RESULT_PASS 3363f44833Srmind }, 3463f44833Srmind { 35ccafaf8eSriastradh .af = AF_INET, 3663f44833Srmind .src = "10.1.1.2", .dst = "10.1.1.1", 3763f44833Srmind .ifname = IFNAME_INT, .di = PFIL_IN, 3863f44833Srmind .stateful_ret = RESULT_PASS, .ret = RESULT_BLOCK 3963f44833Srmind }, 4063f44833Srmind 4163f44833Srmind /* Pass forwards stream only. */ 4263f44833Srmind { 43ccafaf8eSriastradh .af = AF_INET, 4463f44833Srmind .src = "10.1.1.1", .dst = "10.1.1.3", 4563f44833Srmind .ifname = IFNAME_INT, .di = PFIL_OUT, 4663f44833Srmind .stateful_ret = RESULT_PASS, .ret = RESULT_PASS 4763f44833Srmind }, 4863f44833Srmind { 49ccafaf8eSriastradh .af = AF_INET, 5063f44833Srmind .src = "10.1.1.3", .dst = "10.1.1.1", 5163f44833Srmind .ifname = IFNAME_INT, .di = PFIL_IN, 5263f44833Srmind .stateful_ret = RESULT_BLOCK, .ret = RESULT_BLOCK 5363f44833Srmind }, 5463f44833Srmind 551452d7e1Sriastradh /* 56*4603749aSriastradh * Pass in from any of the { fe80::1, fe80:1000:0:0/95, 57*4603749aSriastradh * fe80::2, fe80::2000:0:0/96, fe80::3, fe80::3000:0:0/97 } 58*4603749aSriastradh * group. 591452d7e1Sriastradh */ 60*4603749aSriastradh { /* fe80::1 */ 611452d7e1Sriastradh .af = AF_INET6, 621452d7e1Sriastradh .src = "fe80::1", .dst = "fe80::adec:c91c:d116:7592", 63158a2254Sriastradh .ifname = IFNAME_INT, .di = PFIL_IN, 641452d7e1Sriastradh .stateful_ret = RESULT_PASS, .ret = RESULT_PASS 651452d7e1Sriastradh }, 66*4603749aSriastradh { /* fe80::1000:0:0/95 */ 67*4603749aSriastradh .af = AF_INET6, 68*4603749aSriastradh .src = "fe80::1001:0:0", .dst = "fe80::adec:c91c:d116:7592", 69*4603749aSriastradh .ifname = IFNAME_INT, .di = PFIL_IN, 70*4603749aSriastradh .stateful_ret = RESULT_PASS, .ret = RESULT_PASS 71*4603749aSriastradh }, 72*4603749aSriastradh { /* fe80::1000:0:0/95, one bit off */ 73*4603749aSriastradh .af = AF_INET6, 74*4603749aSriastradh .src = "fe80::1003:0:0", .dst = "fe80::adec:c91c:d116:7592", 75*4603749aSriastradh .ifname = IFNAME_INT, .di = PFIL_IN, 76*4603749aSriastradh .stateful_ret = RESULT_BLOCK, .ret = RESULT_BLOCK 77*4603749aSriastradh }, 78*4603749aSriastradh { /* fe80::2 */ 791452d7e1Sriastradh .af = AF_INET6, 801452d7e1Sriastradh .src = "fe80::2", .dst = "fe80::adec:c91c:d116:7592", 81158a2254Sriastradh .ifname = IFNAME_INT, .di = PFIL_IN, 821452d7e1Sriastradh .stateful_ret = RESULT_PASS, .ret = RESULT_PASS 831452d7e1Sriastradh }, 84*4603749aSriastradh { /* fe80::2000:0:0/96 */ 85*4603749aSriastradh .af = AF_INET6, 86*4603749aSriastradh .src = "fe80::2000:8000:0", .dst = "fe80::adec:c91c:d116:7592", 87*4603749aSriastradh .ifname = IFNAME_INT, .di = PFIL_IN, 88*4603749aSriastradh .stateful_ret = RESULT_PASS, .ret = RESULT_PASS 89*4603749aSriastradh }, 90*4603749aSriastradh { /* fe80::2000:0:0/96, one bit off */ 91*4603749aSriastradh .af = AF_INET6, 92*4603749aSriastradh .src = "fe80::2001:8000:0", .dst = "fe80::adec:c91c:d116:7592", 93*4603749aSriastradh .ifname = IFNAME_INT, .di = PFIL_IN, 94*4603749aSriastradh .stateful_ret = RESULT_BLOCK, .ret = RESULT_BLOCK 95*4603749aSriastradh }, 96*4603749aSriastradh { /* fe80::3 */ 971452d7e1Sriastradh .af = AF_INET6, 981452d7e1Sriastradh .src = "fe80::3", .dst = "fe80::adec:c91c:d116:7592", 99158a2254Sriastradh .ifname = IFNAME_INT, .di = PFIL_IN, 100*4603749aSriastradh .stateful_ret = RESULT_PASS, .ret = RESULT_PASS 101*4603749aSriastradh }, 102*4603749aSriastradh { /* fe80::3000:0:0/97 */ 103*4603749aSriastradh .af = AF_INET6, 104*4603749aSriastradh .src = "fe80::3000:7fff:0", .dst = "fe80::adec:c91c:d116:7592", 105*4603749aSriastradh .ifname = IFNAME_INT, .di = PFIL_IN, 106*4603749aSriastradh .stateful_ret = RESULT_PASS, .ret = RESULT_PASS 107*4603749aSriastradh }, 108*4603749aSriastradh { /* fe80::3000:0:0/97, one bit off */ 109*4603749aSriastradh .af = AF_INET6, 110*4603749aSriastradh .src = "fe80::3000:ffff:0", .dst = "fe80::adec:c91c:d116:7592", 111*4603749aSriastradh .ifname = IFNAME_INT, .di = PFIL_IN, 112*4603749aSriastradh .stateful_ret = RESULT_BLOCK, .ret = RESULT_BLOCK 113*4603749aSriastradh }, 114*4603749aSriastradh { 115*4603749aSriastradh .af = AF_INET6, 116*4603749aSriastradh .src = "fe80::4", .dst = "fe80::adec:c91c:d116:7592", 117*4603749aSriastradh .ifname = IFNAME_INT, .di = PFIL_IN, 118158a2254Sriastradh .stateful_ret = RESULT_BLOCK, .ret = RESULT_BLOCK 119158a2254Sriastradh }, 120158a2254Sriastradh 121158a2254Sriastradh /* 122*4603749aSriastradh * Pass in from anywhere _not_ in that group, as long as it is 123*4603749aSriastradh * to that group. 124158a2254Sriastradh */ 125*4603749aSriastradh { /* fe80::1 */ 126158a2254Sriastradh .af = AF_INET6, 127158a2254Sriastradh .src = "fe80::adec:c91c:d116:7592", .dst = "fe80::1", 128158a2254Sriastradh .ifname = IFNAME_INT, .di = PFIL_IN, 129158a2254Sriastradh .stateful_ret = RESULT_PASS, .ret = RESULT_PASS 130158a2254Sriastradh }, 131*4603749aSriastradh { /* fe80::1000:0:0/95 */ 132*4603749aSriastradh .af = AF_INET6, 133*4603749aSriastradh .src = "fe80::adec:c91c:d116:7592", .dst = "fe80::1001:0:0", 134*4603749aSriastradh .ifname = IFNAME_INT, .di = PFIL_IN, 135*4603749aSriastradh .stateful_ret = RESULT_PASS, .ret = RESULT_PASS 136*4603749aSriastradh }, 137*4603749aSriastradh { /* fe80::1000:0:0/95, one bit off */ 138*4603749aSriastradh .af = AF_INET6, 139*4603749aSriastradh .src = "fe80::adec:c91c:d116:7592", .dst = "fe80::1003:0:0", 140*4603749aSriastradh .ifname = IFNAME_INT, .di = PFIL_IN, 141*4603749aSriastradh .stateful_ret = RESULT_BLOCK, .ret = RESULT_BLOCK 142*4603749aSriastradh }, 143*4603749aSriastradh { /* fe80::2 */ 144158a2254Sriastradh .af = AF_INET6, 145158a2254Sriastradh .src = "fe80::adec:c91c:d116:7592", .dst = "fe80::2", 146158a2254Sriastradh .ifname = IFNAME_INT, .di = PFIL_IN, 147158a2254Sriastradh .stateful_ret = RESULT_PASS, .ret = RESULT_PASS 148158a2254Sriastradh }, 149*4603749aSriastradh { /* fe80::2000:0:0/96 */ 150*4603749aSriastradh .af = AF_INET6, 151*4603749aSriastradh .src = "fe80::adec:c91c:d116:7592", .dst = "fe80::2000:8000:0", 152*4603749aSriastradh .ifname = IFNAME_INT, .di = PFIL_IN, 153*4603749aSriastradh .stateful_ret = RESULT_PASS, .ret = RESULT_PASS 154*4603749aSriastradh }, 155*4603749aSriastradh { /* fe80::2000:0:0/96, one bit off */ 156*4603749aSriastradh .af = AF_INET6, 157*4603749aSriastradh .src = "fe80::adec:c91c:d116:7592", .dst = "fe80::2001:8000:0", 158*4603749aSriastradh .ifname = IFNAME_INT, .di = PFIL_IN, 159*4603749aSriastradh .stateful_ret = RESULT_BLOCK, .ret = RESULT_BLOCK 160*4603749aSriastradh }, 161*4603749aSriastradh { /* fe80::3 */ 162158a2254Sriastradh .af = AF_INET6, 163158a2254Sriastradh .src = "fe80::adec:c91c:d116:7592", .dst = "fe80::3", 164158a2254Sriastradh .ifname = IFNAME_INT, .di = PFIL_IN, 165*4603749aSriastradh .stateful_ret = RESULT_PASS, .ret = RESULT_PASS 166*4603749aSriastradh }, 167*4603749aSriastradh { /* fe80::3000:0:0/97 */ 168*4603749aSriastradh .af = AF_INET6, 169*4603749aSriastradh .src = "fe80::adec:c91c:d116:7592", .dst = "fe80::3000:7fff:0", 170*4603749aSriastradh .ifname = IFNAME_INT, .di = PFIL_IN, 171*4603749aSriastradh .stateful_ret = RESULT_PASS, .ret = RESULT_PASS 172*4603749aSriastradh }, 173*4603749aSriastradh { /* fe80::3000:0:0/97, one bit off */ 174*4603749aSriastradh .af = AF_INET6, 175*4603749aSriastradh .src = "fe80::adec:c91c:d116:7592", .dst = "fe80::3000:ffff:0", 176*4603749aSriastradh .ifname = IFNAME_INT, .di = PFIL_IN, 177*4603749aSriastradh .stateful_ret = RESULT_BLOCK, .ret = RESULT_BLOCK 178*4603749aSriastradh }, 179*4603749aSriastradh { 180*4603749aSriastradh .af = AF_INET6, 181*4603749aSriastradh .src = "fe80::adec:c91c:d116:7592", .dst = "fe80::4", 182*4603749aSriastradh .ifname = IFNAME_INT, .di = PFIL_IN, 1831452d7e1Sriastradh .stateful_ret = RESULT_BLOCK, .ret = RESULT_BLOCK 1841452d7e1Sriastradh }, 1851452d7e1Sriastradh 18663f44833Srmind /* Block. */ 187ccafaf8eSriastradh { 188ccafaf8eSriastradh .af = AF_INET, 189ccafaf8eSriastradh .src = "10.1.1.1", .dst = "10.1.1.4", 19063f44833Srmind .ifname = IFNAME_INT, .di = PFIL_OUT, 19163f44833Srmind .stateful_ret = RESULT_BLOCK, .ret = RESULT_BLOCK 19263f44833Srmind }, 19363f44833Srmind 19463f44833Srmind }; 19563f44833Srmind 19663f44833Srmind static int 197dadc88e3Srmind run_raw_testcase(unsigned i) 19863f44833Srmind { 199dadc88e3Srmind const struct test_case *t = &test_cases[i]; 200f75d79ebSchristos npf_t *npf = npf_getkernctx(); 201dadc88e3Srmind npf_cache_t *npc; 202dadc88e3Srmind struct mbuf *m; 20363f44833Srmind npf_rule_t *rl; 204dadc88e3Srmind int slock, error; 20563f44833Srmind 206ccafaf8eSriastradh m = mbuf_get_pkt(t->af, IPPROTO_UDP, t->src, t->dst, 9000, 9000); 207dadc88e3Srmind npc = get_cached_pkt(m, t->ifname); 208352f1606Srmind 209298883feSrmind slock = npf_config_read_enter(npf); 210dadc88e3Srmind rl = npf_ruleset_inspect(npc, npf_config_ruleset(npf), t->di, NPF_LAYER_3); 21163f44833Srmind if (rl) { 212dadc88e3Srmind npf_match_info_t mi; 2132e57ffe4Schristos error = npf_rule_conclude(rl, &mi); 21463f44833Srmind } else { 21563f44833Srmind error = ENOENT; 21663f44833Srmind } 217298883feSrmind npf_config_read_exit(npf, slock); 218dadc88e3Srmind 219dadc88e3Srmind put_cached_pkt(npc); 22063f44833Srmind return error; 22163f44833Srmind } 22263f44833Srmind 2230e218254Srmind static int 224dadc88e3Srmind run_handler_testcase(unsigned i) 2250e218254Srmind { 2264e592132Srmind const struct test_case *t = &test_cases[i]; 227f75d79ebSchristos ifnet_t *ifp = npf_test_getif(t->ifname); 228dadc88e3Srmind npf_t *npf = npf_getkernctx(); 229dadc88e3Srmind struct mbuf *m; 2300e218254Srmind int error; 2310e218254Srmind 232ccafaf8eSriastradh m = mbuf_get_pkt(t->af, IPPROTO_UDP, t->src, t->dst, 9000, 9000); 23304ad65d9Srmind error = npfk_packet_handler(npf, &m, ifp, t->di); 234dadc88e3Srmind if (m) { 2350e218254Srmind m_freem(m); 236dadc88e3Srmind } 2370e218254Srmind return error; 2380e218254Srmind } 2390e218254Srmind 2400e218254Srmind static npf_rule_t * 2410e218254Srmind npf_blockall_rule(void) 2420e218254Srmind { 243f75d79ebSchristos npf_t *npf = npf_getkernctx(); 24439013e66Srmind nvlist_t *rule = nvlist_create(0); 245dadc88e3Srmind npf_rule_t *rl; 2460e218254Srmind 24739013e66Srmind nvlist_add_number(rule, "attr", 248e9a253f3Srmind NPF_RULE_IN | NPF_RULE_OUT | NPF_RULE_DYNAMIC); 249dadc88e3Srmind rl = npf_rule_alloc(npf, rule); 250dadc88e3Srmind nvlist_destroy(rule); 251dadc88e3Srmind return rl; 2520e218254Srmind } 2530e218254Srmind 254dadc88e3Srmind static bool 255dadc88e3Srmind test_static(bool verbose) 256dadc88e3Srmind { 257dadc88e3Srmind for (unsigned i = 0; i < __arraycount(test_cases); i++) { 258dadc88e3Srmind const struct test_case *t = &test_cases[i]; 259dadc88e3Srmind int error, serror; 260dadc88e3Srmind 261dadc88e3Srmind if (npf_test_getif(t->ifname) == NULL) { 262dadc88e3Srmind printf("Interface %s is not configured.\n", t->ifname); 263dadc88e3Srmind return false; 264dadc88e3Srmind } 265dadc88e3Srmind 266dadc88e3Srmind error = run_raw_testcase(i); 267dadc88e3Srmind serror = run_handler_testcase(i); 268dadc88e3Srmind 269dadc88e3Srmind if (verbose) { 270dadc88e3Srmind printf("rule test %d:\texpected %d (stateful) and %d\n" 271dadc88e3Srmind "\t\t-> returned %d and %d\n", 272dadc88e3Srmind i + 1, t->stateful_ret, t->ret, serror, error); 273dadc88e3Srmind } 274dadc88e3Srmind CHECK_TRUE(error == t->ret); 275dadc88e3Srmind CHECK_TRUE(serror == t->stateful_ret) 276dadc88e3Srmind } 277dadc88e3Srmind return true; 278dadc88e3Srmind } 279dadc88e3Srmind 280dadc88e3Srmind static bool 281dadc88e3Srmind test_dynamic(void) 28263f44833Srmind { 283f75d79ebSchristos npf_t *npf = npf_getkernctx(); 2840e218254Srmind npf_ruleset_t *rlset; 2850e218254Srmind npf_rule_t *rl; 28656910be7Srmind uint64_t id; 2870e218254Srmind int error; 288b8c27e4aSrmind 2894e592132Srmind /* 2904e592132Srmind * Test dynamic NPF rules. 2914e592132Srmind */ 2924e592132Srmind 293dadc88e3Srmind error = run_raw_testcase(0); 2943d9a792dSrmind CHECK_TRUE(error == RESULT_PASS); 2950e218254Srmind 296f75d79ebSchristos npf_config_enter(npf); 297f75d79ebSchristos rlset = npf_config_ruleset(npf); 2980e218254Srmind 2990e218254Srmind rl = npf_blockall_rule(); 3000e218254Srmind error = npf_ruleset_add(rlset, "test-rules", rl); 3013d9a792dSrmind CHECK_TRUE(error == 0); 3020e218254Srmind 303dadc88e3Srmind error = run_raw_testcase(0); 3043d9a792dSrmind CHECK_TRUE(error == RESULT_BLOCK); 3050e218254Srmind 30656910be7Srmind id = npf_rule_getid(rl); 30756910be7Srmind error = npf_ruleset_remove(rlset, "test-rules", id); 3083d9a792dSrmind CHECK_TRUE(error == 0); 3090e218254Srmind 310f75d79ebSchristos npf_config_exit(npf); 3110e218254Srmind 312dadc88e3Srmind error = run_raw_testcase(0); 3133d9a792dSrmind CHECK_TRUE(error == RESULT_PASS); 3140e218254Srmind 3153d9a792dSrmind return true; 31663f44833Srmind } 317dadc88e3Srmind 318dadc88e3Srmind bool 319dadc88e3Srmind npf_rule_test(bool verbose) 320dadc88e3Srmind { 321dadc88e3Srmind bool ok; 322dadc88e3Srmind 323dadc88e3Srmind ok = test_static(verbose); 324dadc88e3Srmind CHECK_TRUE(ok); 325dadc88e3Srmind 326dadc88e3Srmind ok = test_dynamic(); 327dadc88e3Srmind CHECK_TRUE(ok); 328dadc88e3Srmind 329dadc88e3Srmind return true; 330dadc88e3Srmind } 331