1/* $NetBSD: npftest.c,v 1.3.2.4 2012/11/18 21:45:08 riz Exp $ */ 2 3/* 4 * NPF testing framework. 5 * 6 * Public Domain. 7 */ 8 9#include <stdio.h> 10#include <stdlib.h> 11#include <stdbool.h> 12#include <string.h> 13#include <unistd.h> 14#include <assert.h> 15#include <fcntl.h> 16#include <err.h> 17 18#include <sys/ioctl.h> 19#include <net/if.h> 20#include <arpa/inet.h> 21 22#include <rump/rump.h> 23#include <rump/rump_syscalls.h> 24 25#include "npftest.h" 26 27static bool verbose, quiet; 28 29__dead static void 30usage(void) 31{ 32 printf("usage:\n" 33 " %s [ -q | -v ] [ -c <config> ] " 34 "[ -i <interface> ] < -b | -t | -s file >\n" 35 " %s -T <testname> -c <config>\n" 36 " %s -L\n" 37 "where:\n" 38 "\t-b: benchmark\n" 39 "\t-t: regression test\n" 40 "\t-T <testname>: specific test\n" 41 "\t-s <file>: pcap stream\n" 42 "\t-c <config>: NPF configuration file\n" 43 "\t-i <interface>: primary interface\n" 44 "\t-L: list testnames and description for -T\n" 45 "\t-q: quiet mode\n" 46 "\t-v: verbose mode\n", 47 getprogname(), getprogname(), getprogname()); 48 exit(EXIT_FAILURE); 49} 50 51__dead static void 52describe_tests(void) 53{ 54 printf( "nbuf\tbasic npf mbuf handling\n" 55 "processor\tncode processing\n" 56 "table\ttable handling\n" 57 "state\tstate handling and processing\n" 58 "rule\trule processing\n" 59 "nat\tNAT rule processing\n"); 60 exit(EXIT_SUCCESS); 61} 62 63static bool 64result(const char *testcase, bool ok) 65{ 66 if (!quiet) { 67 printf("NPF %-10s\t%s\n", testcase, ok ? "OK" : "fail"); 68 } 69 if (verbose) { 70 puts("-----"); 71 } 72 return !ok; 73} 74 75static void 76load_npf_config_ifs(prop_dictionary_t dbg_dict) 77{ 78 prop_dictionary_t ifdict; 79 prop_object_iterator_t it; 80 prop_array_t iflist; 81 82 iflist = prop_dictionary_get(dbg_dict, "interfaces"); 83 it = prop_array_iterator(iflist); 84 while ((ifdict = prop_object_iterator_next(it)) != NULL) { 85 const char *ifname; 86 unsigned if_idx; 87 88 prop_dictionary_get_cstring_nocopy(ifdict, "name", &ifname); 89 prop_dictionary_get_uint32(ifdict, "idx", &if_idx); 90 (void)rumpns_npf_test_addif(ifname, if_idx, verbose); 91 } 92 prop_object_iterator_release(it); 93} 94 95static void 96load_npf_config(const char *config) 97{ 98 prop_dictionary_t npf_dict, dbg_dict; 99 void *xml; 100 int error; 101 102 /* Read the configuration from the specified file. */ 103 npf_dict = prop_dictionary_internalize_from_file(config); 104 if (!npf_dict) { 105 err(EXIT_FAILURE, "prop_dictionary_internalize_from_file"); 106 } 107 xml = prop_dictionary_externalize(npf_dict); 108 109 /* Inspect the debug data. Create the interfaces, if any. */ 110 dbg_dict = prop_dictionary_get(npf_dict, "debug"); 111 if (dbg_dict) { 112 load_npf_config_ifs(dbg_dict); 113 } 114 prop_object_release(npf_dict); 115 116 /* Pass the XML configuration for NPF kernel component to load. */ 117 error = rumpns_npf_test_load(xml); 118 if (error) { 119 errx(EXIT_FAILURE, "npf_test_load: %s\n", strerror(error)); 120 } 121 free(xml); 122 123 if (verbose) { 124 printf("Loaded NPF config at '%s'\n", config); 125 } 126} 127 128/* 129 * Need to override for cprng_fast32(), since RUMP uses arc4random() for it. 130 */ 131uint32_t 132arc4random(void) 133{ 134 return random(); 135} 136 137int 138main(int argc, char **argv) 139{ 140 bool benchmark, test, ok, fail, tname_matched; 141 char *config, *interface, *stream, *testname; 142 int idx = -1, ch; 143 144 benchmark = false; 145 test = false; 146 147 tname_matched = false; 148 testname = NULL; 149 config = NULL; 150 interface = NULL; 151 stream = NULL; 152 153 verbose = false; 154 quiet = false; 155 156 while ((ch = getopt(argc, argv, "bqvc:i:s:tT:L")) != -1) { 157 switch (ch) { 158 case 'b': 159 benchmark = true; 160 break; 161 case 'q': 162 quiet = true; 163 break; 164 case 'v': 165 verbose = true; 166 break; 167 case 'c': 168 config = optarg; 169 break; 170 case 'i': 171 interface = optarg; 172 break; 173 case 's': 174 stream = optarg; 175 break; 176 case 't': 177 test = true; 178 break; 179 case 'T': 180 test = true; 181 testname = optarg; 182 break; 183 case 'L': 184 describe_tests(); 185 default: 186 usage(); 187 } 188 } 189 190 /* 191 * Either benchmark or test. If stream analysis, then the interface 192 * is needed as well. 193 */ 194 if (benchmark == test && (stream && !interface)) { 195 usage(); 196 } 197 198 /* XXX rn_init */ 199 extern int rumpns_max_keylen; 200 rumpns_max_keylen = 1; 201 202 rump_init(); 203 rump_schedule(); 204 205 rumpns_npf_test_init(); 206 207 if (config) { 208 load_npf_config(config); 209 } 210 if (interface && (idx = rumpns_npf_test_getif(interface)) == 0) { 211 errx(EXIT_FAILURE, "failed to find the interface"); 212 } 213 214 srandom(1); 215 fail = false; 216 217 if (test) { 218 if (!testname || strcmp("nbuf", testname) == 0) { 219 ok = rumpns_npf_nbuf_test(verbose); 220 fail |= result("nbuf", ok); 221 tname_matched = true; 222 } 223 224 if (!testname || strcmp("processor", testname) == 0) { 225 ok = rumpns_npf_processor_test(verbose); 226 fail |= result("processor", ok); 227 tname_matched = true; 228 } 229 230 if (!testname || strcmp("table", testname) == 0) { 231 ok = rumpns_npf_table_test(verbose); 232 fail |= result("table", ok); 233 tname_matched = true; 234 } 235 236 if (!testname || strcmp("state", testname) == 0) { 237 ok = rumpns_npf_state_test(verbose); 238 fail |= result("state", ok); 239 tname_matched = true; 240 } 241 } 242 243 if (test && config) { 244 if (!testname || strcmp("rule", testname) == 0) { 245 ok = rumpns_npf_rule_test(verbose); 246 fail |= result("rule", ok); 247 tname_matched = true; 248 } 249 250 if (!testname || strcmp("nat", testname) == 0) { 251 ok = rumpns_npf_nat_test(verbose); 252 fail |= result("nat", ok); 253 tname_matched = true; 254 } 255 } 256 257 if (stream) { 258 process_stream(stream, NULL, idx); 259 } 260 261 rump_unschedule(); 262 263 if (testname && !tname_matched) 264 errx(EXIT_FAILURE, "test \"%s\" unknown", testname); 265 266 return fail ? EXIT_FAILURE : EXIT_SUCCESS; 267} 268