You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

rspamd_radix_test.c 9.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382
  1. /*-
  2. * Copyright 2016 Vsevolod Stakhov
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #include "config.h"
  17. #include "rspamd.h"
  18. #include "radix.h"
  19. #include "ottery.h"
  20. #include "btrie.h"
  21. const gsize max_elts = 500 * 1024;
  22. const gint lookup_cycles = 1 * 1024;
  23. const gint lookup_divisor = 10;
  24. const uint masks[] = {
  25. 8,
  26. 16,
  27. 24,
  28. 32,
  29. 27,
  30. 29,
  31. 19,
  32. 13,
  33. 22
  34. };
  35. struct _tv {
  36. const char *ip;
  37. const char *nip;
  38. const char *m;
  39. guint32 mask;
  40. guint8 *addr;
  41. guint8 *naddr;
  42. gsize len;
  43. } test_vec[] = {
  44. {"192.168.1.1", "192.168.1.2", "32", 0, 0, 0, 0},
  45. {"192.168.1.0", "192.168.2.1", "24", 0, 0, 0, 0},
  46. {"192.0.0.0", "193.167.2.1", "8", 0, 0, 0, 0},
  47. {"172.0.0.0", "171.16.1.0", "8", 0, 0, 0, 0},
  48. {"172.16.0.1", "127.0.0.1", "16", 0, 0, 0, 0},
  49. {"172.17.1.0", "10.0.0.1", "27", 0, 0, 0, 0},
  50. {"172.17.1.1", "0.0.0.1", "32", 0, 0, 0, 0},
  51. /* Some bad data known to cause problem in the past */
  52. {"191.245.170.246", NULL, "19", 0, 0, 0, 0},
  53. {"227.88.150.170", NULL, "23", 0, 0, 0, 0},
  54. {"105.225.182.92", NULL, "24", 0, 0, 0, 0},
  55. {"223.167.155.240", NULL, "29", 0, 0, 0, 0},
  56. {"125.241.220.172", NULL, "2", 0, 0, 0, 0},
  57. /* Mask = 0 */
  58. {"143.105.181.13", NULL, "8", 0, 0, 0, 0},
  59. {"113.241.233.86", NULL, "26", 0, 0, 0, 0},
  60. {"185.187.122.222", NULL, "8", 0, 0, 0, 0},
  61. {"109.206.26.202", NULL, "12", 0, 0, 0, 0},
  62. {"130.244.233.150", NULL, "0", 0, 0, 0, 0},
  63. /* Close ip addresses */
  64. {"1.2.3.1", NULL, "32", 0, 0, 0, 0},
  65. {"1.2.3.2", NULL, "32", 0, 0, 0, 0},
  66. {"1.2.3.3", NULL, "32", 0, 0, 0, 0},
  67. {"1.2.3.4", NULL, "32", 0, 0, 0, 0},
  68. {NULL, NULL, NULL, 0, 0, 0, 0}
  69. };
  70. static void
  71. rspamd_radix_test_vec (void)
  72. {
  73. radix_compressed_t *tree = radix_create_compressed (NULL);
  74. struct _tv *t = &test_vec[0];
  75. struct in_addr ina;
  76. struct in6_addr in6a;
  77. gulong i, val;
  78. while (t->ip != NULL) {
  79. t->addr = g_malloc (sizeof (in6a));
  80. t->naddr = g_malloc (sizeof (in6a));
  81. if (inet_pton (AF_INET, t->ip, &ina) == 1) {
  82. memcpy (t->addr, &ina, sizeof (ina));
  83. t->len = sizeof (ina);
  84. }
  85. else if (inet_pton (AF_INET6, t->ip, &in6a) == 1) {
  86. memcpy (t->addr, &in6a, sizeof (in6a));
  87. t->len = sizeof (in6a);
  88. }
  89. else {
  90. g_assert (0);
  91. }
  92. if (t->nip) {
  93. if (inet_pton (AF_INET, t->nip, &ina) == 1) {
  94. memcpy (t->naddr, &ina, sizeof (ina));
  95. }
  96. else if (inet_pton (AF_INET6, t->nip, &in6a) == 1) {
  97. memcpy (t->naddr, &in6a, sizeof (in6a));
  98. }
  99. else {
  100. g_assert (0);
  101. }
  102. }
  103. t->mask = t->len * NBBY - strtoul (t->m, NULL, 10);
  104. t ++;
  105. }
  106. t = &test_vec[0];
  107. i = 0;
  108. while (t->ip != NULL) {
  109. radix_insert_compressed (tree, t->addr, t->len, t->mask, ++i);
  110. t ++;
  111. }
  112. i = 0;
  113. t = &test_vec[0];
  114. while (t->ip != NULL) {
  115. val = radix_find_compressed (tree, t->addr, t->len);
  116. g_assert (val == ++i);
  117. /* g_assert (val != RADIX_NO_VALUE); */
  118. if (t->nip != NULL) {
  119. val = radix_find_compressed (tree, t->naddr, t->len);
  120. g_assert (val != i);
  121. }
  122. t ++;
  123. }
  124. radix_destroy_compressed (tree);
  125. }
  126. static void
  127. rspamd_btrie_test_vec (void)
  128. {
  129. rspamd_mempool_t *pool;
  130. struct btrie *tree;
  131. struct _tv *t = &test_vec[0];
  132. struct in_addr ina;
  133. struct in6_addr in6a;
  134. gsize i;
  135. gpointer val;
  136. pool = rspamd_mempool_new (rspamd_mempool_suggest_size (), "btrie", 0);
  137. tree = btrie_init (pool);
  138. while (t->ip != NULL) {
  139. t->addr = g_malloc (sizeof (in6a));
  140. t->naddr = g_malloc (sizeof (in6a));
  141. if (inet_pton (AF_INET, t->ip, &ina) == 1) {
  142. memcpy (t->addr, &ina, sizeof (ina));
  143. t->len = sizeof (ina);
  144. }
  145. else if (inet_pton (AF_INET6, t->ip, &in6a) == 1) {
  146. memcpy (t->addr, &in6a, sizeof (in6a));
  147. t->len = sizeof (in6a);
  148. }
  149. else {
  150. g_assert (0);
  151. }
  152. if (t->nip) {
  153. if (inet_pton (AF_INET, t->nip, &ina) == 1) {
  154. memcpy (t->naddr, &ina, sizeof (ina));
  155. }
  156. else if (inet_pton (AF_INET6, t->nip, &in6a) == 1) {
  157. memcpy (t->naddr, &in6a, sizeof (in6a));
  158. }
  159. else {
  160. g_assert (0);
  161. }
  162. }
  163. t->mask = strtoul (t->m, NULL, 10);
  164. t ++;
  165. }
  166. t = &test_vec[0];
  167. i = 0;
  168. while (t->ip != NULL) {
  169. g_assert (btrie_add_prefix (tree, t->addr, t->mask,
  170. GSIZE_TO_POINTER (++i)) == BTRIE_OKAY);
  171. t ++;
  172. }
  173. i = 0;
  174. t = &test_vec[0];
  175. while (t->ip != NULL) {
  176. val = btrie_lookup (tree, t->addr, t->len * NBBY);
  177. i ++;
  178. g_assert (GPOINTER_TO_SIZE (val) == i);
  179. if (t->nip != NULL) {
  180. val = btrie_lookup (tree, t->naddr, t->len * NBBY);
  181. g_assert (GPOINTER_TO_SIZE (val) != i);
  182. }
  183. t ++;
  184. }
  185. }
  186. void
  187. rspamd_radix_test_func (void)
  188. {
  189. struct btrie *btrie;
  190. rspamd_mempool_t *pool;
  191. radix_compressed_t *comp_tree = radix_create_compressed (NULL);
  192. struct {
  193. guint32 addr;
  194. guint32 mask;
  195. guint8 addr6[16];
  196. guint32 mask6;
  197. guint8 addr64[16];
  198. } *addrs;
  199. gsize nelts, i, check;
  200. gint lc;
  201. gboolean all_good = TRUE;
  202. gdouble ts1, ts2;
  203. double diff;
  204. /* Test suite for the compressed trie */
  205. rspamd_btrie_test_vec ();
  206. rspamd_radix_test_vec ();
  207. rspamd_random_seed_fast ();
  208. nelts = max_elts;
  209. /* First of all we generate many elements and push them to the array */
  210. addrs = g_malloc (nelts * sizeof (addrs[0]));
  211. for (i = 0; i < nelts; i ++) {
  212. addrs[i].addr = ottery_rand_uint32 ();
  213. memset (addrs[i].addr64, 0, 10);
  214. memcpy (addrs[i].addr64 + 12, &addrs[i].addr, 4);
  215. addrs[i].mask = masks[ottery_rand_range(G_N_ELEMENTS (masks) - 1)];
  216. ottery_rand_bytes (addrs[i].addr6, sizeof(addrs[i].addr6));
  217. addrs[i].mask6 = ottery_rand_range(128 - 16) + 16;
  218. }
  219. pool = rspamd_mempool_new (65536, "btrie6", 0);
  220. btrie = btrie_init (pool);
  221. msg_notice ("btrie performance ipv6 only (%z elts)", nelts);
  222. ts1 = rspamd_get_ticks (TRUE);
  223. for (i = 0; i < nelts; i ++) {
  224. btrie_add_prefix (btrie, addrs[i].addr6,
  225. addrs[i].mask6, GSIZE_TO_POINTER (i + 1));
  226. }
  227. ts2 = rspamd_get_ticks (TRUE);
  228. diff = (ts2 - ts1);
  229. msg_notice ("Added %hz elements in %.0f ticks (%.2f ticks per element)",
  230. nelts, diff, diff / (double)nelts);
  231. ts1 = rspamd_get_ticks (TRUE);
  232. for (lc = 0; lc < lookup_cycles && all_good; lc ++) {
  233. for (i = 0; i < nelts / lookup_divisor; i ++) {
  234. check = rspamd_random_uint64_fast () % nelts;
  235. if (btrie_lookup (btrie, addrs[check].addr6, sizeof (addrs[check].addr6) * 8)
  236. == NULL) {
  237. char ipbuf[INET6_ADDRSTRLEN + 1];
  238. all_good = FALSE;
  239. inet_ntop(AF_INET6, addrs[check].addr6, ipbuf, sizeof(ipbuf));
  240. msg_notice("BAD btrie: {\"%s\", NULL, \"%ud\", 0, 0, 0, 0},",
  241. ipbuf,
  242. addrs[check].mask6);
  243. all_good = FALSE;
  244. }
  245. }
  246. }
  247. g_assert (all_good);
  248. ts2 = rspamd_get_ticks (TRUE);
  249. diff = (ts2 - ts1);
  250. msg_notice ("Checked %hz elements in %.0f ticks (%.2f ticks per lookup)",
  251. nelts * lookup_cycles / lookup_divisor, diff,
  252. diff / ((gdouble)nelts * lookup_cycles / lookup_divisor));
  253. rspamd_mempool_delete (pool);
  254. /*
  255. * IPv4 part
  256. */
  257. pool = rspamd_mempool_new (65536, "btrie4", 0);
  258. btrie = btrie_init (pool);
  259. msg_notice ("btrie performance ipv4 only (%z elts)", nelts);
  260. ts1 = rspamd_get_ticks (TRUE);
  261. for (i = 0; i < nelts; i ++) {
  262. btrie_add_prefix (btrie, (guchar *)&addrs[i].addr,
  263. addrs[i].mask, GSIZE_TO_POINTER (i + 1));
  264. }
  265. ts2 = rspamd_get_ticks (TRUE);
  266. diff = (ts2 - ts1);
  267. msg_notice ("Added %hz elements in %.0f ticks (%.2f ticks per element)",
  268. nelts, diff, diff / (double)nelts);
  269. ts1 = rspamd_get_ticks (TRUE);
  270. for (lc = 0; lc < lookup_cycles && all_good; lc ++) {
  271. for (i = 0; i < nelts / lookup_divisor; i ++) {
  272. check = rspamd_random_uint64_fast () % nelts;
  273. if (btrie_lookup (btrie, (guchar *)&addrs[check].addr, sizeof (addrs[check].addr) * 8)
  274. == NULL) {
  275. char ipbuf[INET6_ADDRSTRLEN + 1];
  276. all_good = FALSE;
  277. inet_ntop(AF_INET, (guchar *)&addrs[check].addr, ipbuf, sizeof(ipbuf));
  278. msg_notice("BAD btrie: {\"%s\", NULL, \"%ud\", 0, 0, 0, 0},",
  279. ipbuf,
  280. addrs[check].mask);
  281. all_good = FALSE;
  282. }
  283. }
  284. }
  285. g_assert (all_good);
  286. ts2 = rspamd_get_ticks (TRUE);
  287. diff = (ts2 - ts1);
  288. msg_notice ("Checked %hz elements in %.0f ticks (%.2f ticks per lookup)",
  289. nelts * lookup_cycles / lookup_divisor, diff,
  290. diff / ((gdouble)nelts * lookup_cycles / lookup_divisor));
  291. rspamd_mempool_delete (pool);
  292. /*
  293. * IPv4 -> IPv6 mapped
  294. */
  295. pool = rspamd_mempool_new (65536, "btrie4map", 0);
  296. btrie = btrie_init (pool);
  297. msg_notice ("btrie performance ipv4 + ipv6map (%z elts)", nelts);
  298. ts1 = rspamd_get_ticks (TRUE);
  299. for (i = 0; i < nelts; i ++) {
  300. btrie_add_prefix (btrie, addrs[i].addr64,
  301. addrs[i].mask + 96, GSIZE_TO_POINTER (i + 1));
  302. }
  303. ts2 = rspamd_get_ticks (TRUE);
  304. diff = (ts2 - ts1);
  305. msg_notice ("Added %hz elements in %.0f ticks (%.2f ticks per element)",
  306. nelts, diff, diff / (double)nelts);
  307. ts1 = rspamd_get_ticks (TRUE);
  308. for (lc = 0; lc < lookup_cycles && all_good; lc ++) {
  309. for (i = 0; i < nelts / lookup_divisor; i ++) {
  310. check = rspamd_random_uint64_fast () % nelts;
  311. if (btrie_lookup (btrie, addrs[check].addr64,
  312. sizeof (addrs[check].addr64) * 8) == NULL) {
  313. char ipbuf[INET6_ADDRSTRLEN + 1];
  314. all_good = FALSE;
  315. inet_ntop(AF_INET, (guchar *)&addrs[check].addr, ipbuf, sizeof(ipbuf));
  316. msg_notice("BAD btrie: {\"%s\", NULL, \"%ud\", 0, 0, 0, 0},",
  317. ipbuf,
  318. addrs[check].mask);
  319. all_good = FALSE;
  320. }
  321. }
  322. }
  323. g_assert (all_good);
  324. ts2 = rspamd_get_ticks (TRUE);
  325. diff = (ts2 - ts1);
  326. msg_notice ("Checked %hz elements in %.0f ticks (%.2f ticks per lookup)",
  327. nelts * lookup_cycles / lookup_divisor, diff,
  328. diff / ((gdouble)nelts * lookup_cycles / lookup_divisor));
  329. rspamd_mempool_delete (pool);
  330. g_free (addrs);
  331. }