/* * NPF tableset tests. * * Public Domain. */ #ifdef _KERNEL #include #include #endif #ifdef __linux__ #include #else #include #endif #include "npf_impl.h" #include "npf_test.h" static const char *ip_list[] = { "192.168.1.1", "10.0.0.1", "192.168.2.1", "10.1.0.1", "192.168.100.253", "10.0.5.1", "192.168.128.127", "10.0.0.2", }; static const uint8_t ip6_list[][16] = { { 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xa0, 0xc0, 0xff, 0xfe, 0x10, 0x12, 0x34 }, { 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xa0, 0xc0, 0xff, 0x00, 0x00, 0x00, 0x00, }, { 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, { 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xa0, 0xc0, 0xff, 0xfe, 0x10, 0x12, 0x30 } }; #define IPSET_TID 0 #define IPSET_NAME "ipset-table" #define LPM_TID 1 #define LPM_NAME "lpm-table" #define CDB_TID 2 #define CDB_NAME "cdb-table" #define IFADDR_TID 3 #define IFADDR_NAME ".ifaddr-eth0" /////////////////////////////////////////////////////////////////////////// static bool check_ip4(const npf_addr_t *addr, const char *ipstr) { npf_addr_t addr_storage, *test_addr = &addr_storage; const int alen = sizeof(struct in_addr); test_addr->word32[0] = inet_addr(ipstr); return memcmp(addr, test_addr, alen) == 0; } static bool ip4list_insert_lookup(npf_table_t *t, unsigned i) { npf_addr_t addr_storage, *addr = &addr_storage; const int alen = sizeof(struct in_addr); int error; addr->word32[0] = inet_addr(ip_list[i]); error = npf_table_insert(t, alen, addr, NPF_NO_NETMASK); CHECK_TRUE(error == 0); error = npf_table_lookup(t, alen, addr); CHECK_TRUE(error == 0); return true; } static bool fill_with_ip4(npf_tableset_t *tblset) { npf_addr_t addr_storage, *addr = &addr_storage; const int alen = sizeof(struct in_addr); const int nm = NPF_NO_NETMASK; for (unsigned i = 0; i < __arraycount(ip_list); i++) { npf_table_t *t; int error; addr->word32[0] = inet_addr(ip_list[i]); t = npf_tableset_getbyname(tblset, IPSET_NAME); error = npf_table_insert(t, alen, addr, nm); CHECK_TRUE(error == 0); error = npf_table_insert(t, alen, addr, nm); CHECK_TRUE(error != 0); // duplicate t = npf_tableset_getbyname(tblset, LPM_NAME); error = npf_table_insert(t, alen, addr, nm); CHECK_TRUE(error == 0); error = npf_table_insert(t, alen, addr, nm); CHECK_TRUE(error != 0); // duplicate } return true; } static bool verify_ip4(npf_tableset_t *tblset) { npf_addr_t addr_storage, *addr = &addr_storage; const size_t alen = sizeof(struct in_addr); const int nm = NPF_NO_NETMASK; npf_table_t *t; int error; /* Attempt to add duplicates - should fail. */ addr->word32[0] = inet_addr(ip_list[0]); t = npf_tableset_getbyname(tblset, IPSET_NAME); error = npf_table_insert(t, alen, addr, nm); CHECK_TRUE(error != 0); t = npf_tableset_getbyname(tblset, LPM_NAME); error = npf_table_insert(t, alen, addr, nm); CHECK_TRUE(error != 0); /* Match (validate) each IP entry. */ for (unsigned i = 0; i < __arraycount(ip_list); i++) { addr->word32[0] = inet_addr(ip_list[i]); t = npf_tableset_getbyname(tblset, IPSET_NAME); error = npf_table_lookup(t, alen, addr); CHECK_TRUE(error == 0); t = npf_tableset_getbyname(tblset, LPM_NAME); error = npf_table_lookup(t, alen, addr); CHECK_TRUE(error == 0); } return true; } static bool clear_ip4(npf_tableset_t *tblset) { npf_addr_t addr_storage, *addr = &addr_storage; const int alen = sizeof(struct in_addr); const int nm = NPF_NO_NETMASK; for (unsigned i = 0; i < __arraycount(ip_list); i++) { npf_table_t *t; int error; addr->word32[0] = inet_addr(ip_list[i]); t = npf_tableset_getbyname(tblset, IPSET_NAME); error = npf_table_remove(t, alen, addr, nm); CHECK_TRUE(error == 0); error = npf_table_remove(t, alen, addr, nm); CHECK_TRUE(error != 0); t = npf_tableset_getbyname(tblset, LPM_NAME); error = npf_table_remove(t, alen, addr, nm); CHECK_TRUE(error == 0); error = npf_table_remove(t, alen, addr, nm); CHECK_TRUE(error != 0); } return true; } /////////////////////////////////////////////////////////////////////////// static bool test_basic(npf_tableset_t *tblset) { npf_addr_t addr_storage, *addr = &addr_storage; const int alen = sizeof(struct in_addr); npf_table_t *t; int error; /* Basic IP set. */ t = npf_table_create(IPSET_NAME, IPSET_TID, NPF_TABLE_IPSET, NULL, 0); CHECK_TRUE(t != NULL); error = npf_tableset_insert(tblset, t); CHECK_TRUE(error == 0); /* Check for double-insert. */ error = npf_tableset_insert(tblset, t); CHECK_TRUE(error != 0); /* Longest-prefix match (LPM). */ t = npf_table_create(LPM_NAME, LPM_TID, NPF_TABLE_LPM, NULL, 0); CHECK_TRUE(t != NULL); error = npf_tableset_insert(tblset, t); CHECK_TRUE(error == 0); /* Table for interface addresses. */ t = npf_table_create(IFADDR_NAME, IFADDR_TID, NPF_TABLE_IFADDR, NULL, 0); CHECK_TRUE(t != NULL); error = npf_tableset_insert(tblset, t); CHECK_TRUE(error == 0); /* * Attempt to match some non-existing entries - should fail. */ addr->word32[0] = inet_addr(ip_list[0]); t = npf_tableset_getbyname(tblset, IPSET_NAME); error = npf_table_lookup(t, alen, addr); CHECK_TRUE(error != 0); t = npf_tableset_getbyname(tblset, LPM_NAME); error = npf_table_lookup(t, alen, addr); CHECK_TRUE(error != 0); return true; } static bool test_nocopy(npf_tableset_t *tblset) { const int alen = sizeof(struct in_addr); const char *tables[] = { IPSET_NAME, LPM_NAME, IFADDR_NAME }; npf_addr_t *addr, lookup_addr; for (unsigned i = 0; i < __arraycount(tables); i++) { npf_table_t *t; int error; addr = kmem_zalloc(sizeof(npf_addr_t), KM_SLEEP); assert(addr != NULL); addr->word32[0] = inet_addr("172.16.90.10"); t = npf_tableset_getbyname(tblset, tables[i]); (void)npf_table_flush(t); error = npf_table_insert(t, alen, addr, NPF_NO_NETMASK); CHECK_TRUE(error == 0); memcpy(&lookup_addr, addr, alen); memset(addr, 0xa5, alen); // explicit memset error = npf_table_lookup(t, alen, &lookup_addr); CHECK_TRUE(error == 0); CHECK_TRUE(*(volatile unsigned char *)addr == 0xa5); kmem_free(addr, sizeof(npf_addr_t)); } return true; } static bool test_ip6(npf_tableset_t *tblset) { npf_addr_t addr_storage, *addr = &addr_storage; const size_t alen = sizeof(struct in6_addr); const int nm = NPF_NO_NETMASK; npf_table_t *t; int error; /* IPv6 addresses. */ memcpy(addr, ip6_list[0], sizeof(ip6_list[0])); t = npf_tableset_getbyname(tblset, IPSET_NAME); error = npf_table_insert(t, alen, addr, nm); CHECK_TRUE(error == 0); error = npf_table_lookup(t, alen, addr); CHECK_TRUE(error == 0); error = npf_table_remove(t, alen, addr, nm); CHECK_TRUE(error == 0); t = npf_tableset_getbyname(tblset, LPM_NAME); error = npf_table_insert(t, alen, addr, nm); CHECK_TRUE(error == 0); error = npf_table_lookup(t, alen, addr); CHECK_TRUE(error == 0); error = npf_table_remove(t, alen, addr, nm); CHECK_TRUE(error == 0); return true; } static bool test_lpm_masks4(npf_tableset_t *tblset) { npf_table_t *t = npf_tableset_getbyname(tblset, LPM_NAME); npf_addr_t addr_storage, *addr = &addr_storage; const size_t alen = sizeof(struct in_addr); int error; addr->word32[0] = inet_addr("172.16.90.0"); error = npf_table_insert(t, alen, addr, 25); CHECK_TRUE(error == 0); addr->word32[0] = inet_addr("172.16.90.126"); error = npf_table_lookup(t, alen, addr); CHECK_TRUE(error == 0); addr->word32[0] = inet_addr("172.16.90.128"); error = npf_table_lookup(t, alen, addr); CHECK_TRUE(error != 0); return true; } static bool test_lpm_masks6(npf_tableset_t *tblset) { npf_table_t *t = npf_tableset_getbyname(tblset, LPM_NAME); npf_addr_t addr_storage, *addr = &addr_storage; const size_t alen = sizeof(struct in6_addr); int error; /* * 96 */ memcpy(addr, ip6_list[1], sizeof(ip6_list[1])); error = npf_table_insert(t, alen, addr, 96); CHECK_TRUE(error == 0); memcpy(addr, ip6_list[0], sizeof(ip6_list[0])); error = npf_table_lookup(t, alen, addr); CHECK_TRUE(error == 0); memcpy(addr, ip6_list[1], sizeof(ip6_list[1])); error = npf_table_remove(t, alen, addr, 96); CHECK_TRUE(error == 0); /* * 32 */ memcpy(addr, ip6_list[2], sizeof(ip6_list[2])); error = npf_table_insert(t, alen, addr, 32); CHECK_TRUE(error == 0); memcpy(addr, ip6_list[0], sizeof(ip6_list[0])); error = npf_table_lookup(t, alen, addr); CHECK_TRUE(error == 0); memcpy(addr, ip6_list[2], sizeof(ip6_list[2])); error = npf_table_remove(t, alen, addr, 32); CHECK_TRUE(error == 0); /* * 126 */ memcpy(addr, ip6_list[3], sizeof(ip6_list[3])); error = npf_table_insert(t, alen, addr, 126); CHECK_TRUE(error == 0); memcpy(addr, ip6_list[0], sizeof(ip6_list[0])); error = npf_table_lookup(t, alen, addr); CHECK_TRUE(error != 0); memcpy(addr, ip6_list[3], sizeof(ip6_list[3])); error = npf_table_remove(t, alen, addr, 126); CHECK_TRUE(error == 0); return true; } static bool test_const_table(npf_tableset_t *tblset, void *blob, size_t size) { npf_addr_t addr_storage, *addr = &addr_storage; const int alen = sizeof(struct in_addr); npf_table_t *t; int error; t = npf_table_create(CDB_NAME, CDB_TID, NPF_TABLE_CONST, blob, size); CHECK_TRUE(t != NULL); error = npf_tableset_insert(tblset, t); CHECK_TRUE(error == 0); addr->word32[0] = inet_addr(ip_list[0]); error = npf_table_lookup(t, alen, addr); CHECK_TRUE(error == 0); for (unsigned i = 1; i < __arraycount(ip_list) - 1; i++) { addr->word32[0] = inet_addr(ip_list[i]); error = npf_table_lookup(t, alen, addr); CHECK_TRUE(error != 0); } return true; } static bool test_ifaddr_table(npf_tableset_t *tblset) { npf_addr_t addr_storage, *addr = &addr_storage; npf_table_t *t = npf_tableset_getbyname(tblset, IFADDR_NAME); int error; bool ok; /* Two IPv4 addresses. */ ok = ip4list_insert_lookup(t, 0); CHECK_TRUE(ok); ok = ip4list_insert_lookup(t, 1); CHECK_TRUE(ok); /* And one IPv6 address. */ memcpy(addr, ip6_list[0], sizeof(ip6_list[0])); error = npf_table_insert(t, sizeof(struct in6_addr), addr, NPF_NO_NETMASK); CHECK_TRUE(error == 0); /* * Get IPv4 addresses. */ addr = npf_table_getsome(t, sizeof(struct in_addr), 0); ok = check_ip4(addr, "192.168.1.1"); CHECK_TRUE(ok); addr = npf_table_getsome(t, sizeof(struct in_addr), 1); ok = check_ip4(addr, "10.0.0.1"); CHECK_TRUE(ok); addr = npf_table_getsome(t, sizeof(struct in_addr), 2); ok = check_ip4(addr, "192.168.1.1"); CHECK_TRUE(ok); return true; } static void test_ipset_gc(npf_tableset_t *tblset) { npf_table_t *t = npf_tableset_getbyname(tblset, IPSET_NAME); npf_t *npf = npf_getkernctx(); npf_config_enter(npf); npf_table_gc(npf, t); npf_table_flush(t); npf_config_exit(npf); } bool npf_table_test(bool verbose, void *blob, size_t size) { npf_tableset_t *tblset; bool ok; (void)verbose; tblset = npf_tableset_create(4); CHECK_TRUE(tblset != NULL); ok = test_basic(tblset); CHECK_TRUE(ok); /* * Fill IPSET and LPM tables with IPv4 addresses. * Keep them in the table during the other tests. */ ok = fill_with_ip4(tblset); CHECK_TRUE(ok); ok = verify_ip4(tblset); CHECK_TRUE(ok); ok = test_ip6(tblset); CHECK_TRUE(ok); ok = test_lpm_masks4(tblset); CHECK_TRUE(ok); ok = test_lpm_masks6(tblset); CHECK_TRUE(ok); ok = test_const_table(tblset, blob, size); CHECK_TRUE(ok); ok = test_ifaddr_table(tblset); CHECK_TRUE(ok); /* * Remove the above IPv4 addresses -- they must have been untouched. */ ok = clear_ip4(tblset); CHECK_TRUE(ok); ok = test_nocopy(tblset); CHECK_TRUE(ok); test_ipset_gc(tblset); npf_tableset_destroy(tblset); return true; }