1 /* $NetBSD: npf_rule_test.c,v 1.12 2014/08/10 19:09:43 rmind Exp $ */ 2 3 /* 4 * NPF ruleset test. 5 * 6 * Public Domain. 7 */ 8 9 #include <sys/types.h> 10 11 #include "npf_impl.h" 12 #include "npf_test.h" 13 14 #define RESULT_PASS 0 15 #define RESULT_BLOCK ENETUNREACH 16 17 static const struct test_case { 18 const char * src; 19 const char * dst; 20 const char * ifname; 21 int di; 22 int stateful_ret; 23 int ret; 24 } test_cases[] = { 25 26 /* Stateful pass. */ 27 { 28 .src = "10.1.1.1", .dst = "10.1.1.2", 29 .ifname = IFNAME_INT, .di = PFIL_OUT, 30 .stateful_ret = RESULT_PASS, .ret = RESULT_PASS 31 }, 32 { 33 .src = "10.1.1.2", .dst = "10.1.1.1", 34 .ifname = IFNAME_INT, .di = PFIL_IN, 35 .stateful_ret = RESULT_PASS, .ret = RESULT_BLOCK 36 }, 37 38 /* Pass forwards stream only. */ 39 { 40 .src = "10.1.1.1", .dst = "10.1.1.3", 41 .ifname = IFNAME_INT, .di = PFIL_OUT, 42 .stateful_ret = RESULT_PASS, .ret = RESULT_PASS 43 }, 44 { 45 .src = "10.1.1.3", .dst = "10.1.1.1", 46 .ifname = IFNAME_INT, .di = PFIL_IN, 47 .stateful_ret = RESULT_BLOCK, .ret = RESULT_BLOCK 48 }, 49 50 /* Block. */ 51 { .src = "10.1.1.1", .dst = "10.1.1.4", 52 .ifname = IFNAME_INT, .di = PFIL_OUT, 53 .stateful_ret = RESULT_BLOCK, .ret = RESULT_BLOCK 54 }, 55 56 }; 57 58 static struct mbuf * 59 fill_packet(const struct test_case *t) 60 { 61 struct mbuf *m; 62 struct ip *ip; 63 struct udphdr *uh; 64 65 m = mbuf_construct(IPPROTO_UDP); 66 uh = mbuf_return_hdrs(m, false, &ip); 67 ip->ip_src.s_addr = inet_addr(t->src); 68 ip->ip_dst.s_addr = inet_addr(t->dst); 69 uh->uh_sport = htons(9000); 70 uh->uh_dport = htons(9000); 71 return m; 72 } 73 74 static int 75 npf_rule_raw_test(bool verbose, struct mbuf *m, ifnet_t *ifp, int di) 76 { 77 npf_cache_t npc = { .npc_info = 0 }; 78 nbuf_t nbuf; 79 npf_rule_t *rl; 80 int retfl, error; 81 82 nbuf_init(&nbuf, m, ifp); 83 npc.npc_nbuf = &nbuf; 84 npf_cache_all(&npc); 85 86 int slock = npf_config_read_enter(); 87 rl = npf_ruleset_inspect(&npc, npf_config_ruleset(), 88 di, NPF_LAYER_3); 89 if (rl) { 90 error = npf_rule_conclude(rl, &retfl); 91 } else { 92 error = ENOENT; 93 } 94 npf_config_read_exit(slock); 95 return error; 96 } 97 98 static int 99 npf_test_case(u_int i, bool verbose) 100 { 101 const struct test_case *t = &test_cases[i]; 102 ifnet_t *ifp = ifunit(t->ifname); 103 int error; 104 105 struct mbuf *m = fill_packet(t); 106 error = npf_rule_raw_test(verbose, m, ifp, t->di); 107 m_freem(m); 108 return error; 109 } 110 111 static npf_rule_t * 112 npf_blockall_rule(void) 113 { 114 prop_dictionary_t rldict; 115 116 rldict = prop_dictionary_create(); 117 prop_dictionary_set_uint32(rldict, "attr", 118 NPF_RULE_IN | NPF_RULE_OUT | NPF_RULE_DYNAMIC); 119 return npf_rule_alloc(rldict); 120 } 121 122 bool 123 npf_rule_test(bool verbose) 124 { 125 npf_ruleset_t *rlset; 126 npf_rule_t *rl; 127 bool fail = false; 128 uint64_t id; 129 int error; 130 131 for (unsigned i = 0; i < __arraycount(test_cases); i++) { 132 const struct test_case *t = &test_cases[i]; 133 ifnet_t *ifp = ifunit(t->ifname); 134 int serror; 135 136 if (ifp == NULL) { 137 printf("Interface %s is not configured.\n", t->ifname); 138 return false; 139 } 140 141 struct mbuf *m = fill_packet(t); 142 error = npf_rule_raw_test(verbose, m, ifp, t->di); 143 serror = npf_packet_handler(NULL, &m, ifp, t->di); 144 145 if (m) { 146 m_freem(m); 147 } 148 149 if (verbose) { 150 printf("Rule test %d, expected %d (stateful) and %d \n" 151 "-> returned %d and %d.\n", 152 i + 1, t->stateful_ret, t->ret, serror, error); 153 } 154 fail |= (serror != t->stateful_ret || error != t->ret); 155 } 156 157 /* 158 * Test dynamic NPF rules. 159 */ 160 161 error = npf_test_case(0, verbose); 162 assert(error == RESULT_PASS); 163 164 npf_config_enter(); 165 rlset = npf_config_ruleset(); 166 167 rl = npf_blockall_rule(); 168 error = npf_ruleset_add(rlset, "test-rules", rl); 169 fail |= error != 0; 170 171 error = npf_test_case(0, verbose); 172 fail |= (error != RESULT_BLOCK); 173 174 id = npf_rule_getid(rl); 175 error = npf_ruleset_remove(rlset, "test-rules", id); 176 fail |= error != 0; 177 178 npf_config_exit(); 179 180 error = npf_test_case(0, verbose); 181 fail |= (error != RESULT_PASS); 182 183 return !fail; 184 } 185