Add unit tests suite using cmocka library
[tinc] / test / unit / test_subnet.c
1 #include "unittest.h"
2 #include "../../src/subnet.h"
3
4 typedef struct net_str_testcase {
5         const char *text;
6         subnet_t data;
7 } net_str_testcase;
8
9 static void test_subnet_compare_different_types(void **state) {
10         (void)state;
11
12         const subnet_t ipv4 = {.type = SUBNET_IPV4};
13         const subnet_t ipv6 = {.type = SUBNET_IPV6};
14         const subnet_t mac = {.type = SUBNET_MAC};
15
16         assert_int_not_equal(0, subnet_compare(&ipv4, &ipv6));
17         assert_int_not_equal(0, subnet_compare(&ipv4, &mac));
18         assert_int_not_equal(0, subnet_compare(&ipv6, &mac));
19 }
20
21 static const mac_t mac1 = {{0x00, 0x01, 0x02, 0x03, 0x04, 0x05}};
22 static const mac_t mac2 = {{0x42, 0x01, 0x02, 0x03, 0x04, 0x05}};
23
24 static const subnet_ipv4_t ipv4_1 = {.address = {{0x01, 0x02, 0x03, 0x04}}, .prefixlength = 24};
25 static const subnet_ipv4_t ipv4_1_pref = {.address = {{0x01, 0x02, 0x03, 0x04}}, .prefixlength = 16};
26 static const subnet_ipv4_t ipv4_2 = {.address = {{0x11, 0x22, 0x33, 0x44}}, .prefixlength = 16};
27
28 static const subnet_ipv6_t ipv6_1 = {
29         .address = {{0x01, 0x02, 0x03, 0x04, 0x01, 0x02, 0x03, 0x04}},
30         .prefixlength = 24
31 };
32
33 static const subnet_ipv6_t ipv6_1_pref = {
34         .address = {{0x01, 0x02, 0x03, 0x04, 0x01, 0x02, 0x03, 0x04}},
35         .prefixlength = 16
36 };
37
38 static const subnet_ipv6_t ipv6_2 = {
39         .address = {{0x11, 0x22, 0x03, 0x04, 0x01, 0x02, 0x03, 0x04}},
40         .prefixlength = 24
41 };
42
43 static void test_maskcmp(void **state) {
44         (void)state;
45
46         const ipv4_t a = {{1, 2, 3, 4}};
47         const ipv4_t b = {{1, 2, 3, 0xff}};
48
49         for(int mask = 0; mask <= 24; ++mask) {
50                 assert_int_equal(0, maskcmp(&a, &b, mask));
51         }
52
53         for(int mask = 25; mask <= 32; ++mask) {
54                 assert_true(maskcmp(&a, &b, mask) != 0);
55         }
56 }
57
58 static void test_mask(void **state) {
59         (void)state;
60
61         ipv4_t dst = {{0xff, 0xff, 0xff, 0xff}};
62         mask(&dst, 23, sizeof(dst));
63
64         const ipv4_t ref = {{0xff, 0xff, 0xfe, 0x00}};
65         assert_memory_equal(&ref, &dst, sizeof(dst));
66 }
67
68 static void test_maskcpy(void **state) {
69         (void)state;
70
71         const ipv4_t src = {{0xff, 0xff, 0xff, 0xff}};
72         const ipv4_t ref = {{0xff, 0xff, 0xfe, 0x00}};
73         ipv4_t dst;
74
75         maskcpy(&dst, &src, 23, sizeof(src));
76
77         assert_memory_equal(&ref, &dst, sizeof(dst));
78 }
79
80 static void test_subnet_compare_mac_eq(void **state) {
81         (void)state;
82
83         node_t owner = {.name = strdup("foobar")};
84         const subnet_t a = {.type = SUBNET_MAC, .net.mac.address = mac1, .weight = 42, .owner = &owner};
85         const subnet_t b = {.type = SUBNET_MAC, .net.mac.address = mac1, .weight = 42, .owner = &owner};
86
87         assert_int_equal(0, subnet_compare(&a, &a));
88         assert_int_equal(0, subnet_compare(&a, &b));
89         assert_int_equal(0, subnet_compare(&b, &a));
90
91         free(owner.name);
92 }
93
94 static void test_subnet_compare_mac_neq_address(void **state) {
95         (void)state;
96
97         node_t owner = {.name = strdup("foobar")};
98         const subnet_t a = {.type = SUBNET_MAC, .net.mac.address = mac1, .weight = 10, .owner = &owner};
99         const subnet_t b = {.type = SUBNET_MAC, .net.mac.address = mac2, .weight = 10, .owner = &owner};
100
101         assert_true(subnet_compare(&a, &b) < 0);
102         assert_true(subnet_compare(&b, &a) > 0);
103
104         free(owner.name);
105 }
106
107 static void test_subnet_compare_mac_weight(void **state) {
108         (void)state;
109
110         node_t owner = {.name = strdup("foobar")};
111         const subnet_t a = {.type = SUBNET_MAC, .net.mac.address = mac1, .weight = 42, .owner = &owner};
112         const subnet_t b = {.type = SUBNET_MAC, .net.mac.address = mac1, .weight = 42, .owner = &owner};
113         const subnet_t c = {.type = SUBNET_MAC, .net.mac.address = mac1, .weight = 10, .owner = &owner};
114
115         assert_int_equal(0, subnet_compare(&a, &a));
116         assert_int_equal(0, subnet_compare(&a, &b));
117         assert_int_equal(0, subnet_compare(&b, &a));
118
119         assert_true(subnet_compare(&a, &c) > 0);
120         assert_true(subnet_compare(&c, &a) < 0);
121
122         free(owner.name);
123 }
124
125 static void test_subnet_compare_mac_owners(void **state) {
126         (void)state;
127
128         node_t foo = {.name = strdup("foo")};
129         node_t bar = {.name = strdup("bar")};
130
131         const subnet_t a = {.type = SUBNET_MAC, .net.mac.address = mac1, .weight = 42, .owner = &foo};
132         const subnet_t b = {.type = SUBNET_MAC, .net.mac.address = mac1, .weight = 42, .owner = &bar};
133
134         assert_int_equal(0, subnet_compare(&a, &a));
135         assert_int_equal(0, subnet_compare(&b, &b));
136
137         assert_true(subnet_compare(&a, &b) > 0);
138         assert_true(subnet_compare(&b, &a) < 0);
139
140         free(foo.name);
141         free(bar.name);
142 }
143
144
145 static void test_subnet_compare_ipv4_eq(void **state) {
146         (void)state;
147
148         const subnet_t a = {.type = SUBNET_IPV4, .net.ipv4 = ipv4_1};
149         const subnet_t b = {.type = SUBNET_IPV4, .net.ipv4 = ipv4_1};
150
151         assert_int_equal(0, subnet_compare(&a, &b));
152         assert_int_equal(0, subnet_compare(&b, &a));
153 }
154
155 static void test_subnet_compare_ipv4_neq(void **state) {
156         (void)state;
157
158         const subnet_t a = {.type = SUBNET_IPV4, .net.ipv4 = ipv4_1};
159         const subnet_t b = {.type = SUBNET_IPV4, .net.ipv4 = ipv4_1_pref};
160         const subnet_t c = {.type = SUBNET_IPV4, .net.ipv4 = ipv4_2};
161
162         assert_true(subnet_compare(&a, &b) < 0);
163         assert_true(subnet_compare(&b, &a) > 0);
164
165         assert_true(subnet_compare(&a, &c) < 0);
166         assert_true(subnet_compare(&b, &c) < 0);
167 }
168
169 static void test_subnet_compare_ipv4_weight(void **state) {
170         (void)state;
171
172         const subnet_t a = {.type = SUBNET_IPV4, .net.ipv4 = ipv4_1, .weight = 1};
173         const subnet_t b = {.type = SUBNET_IPV4, .net.ipv4 = ipv4_1, .weight = 2};
174
175         assert_true(subnet_compare(&a, &b) < 0);
176 }
177
178 static void test_subnet_compare_ipv4_owners(void **state) {
179         (void)state;
180
181         node_t foo = {.name = strdup("foo")};
182         node_t bar = {.name = strdup("bar")};
183
184         const subnet_t a = {.type = SUBNET_IPV4, .net.ipv4 = ipv4_1, .owner = &foo};
185         const subnet_t b = {.type = SUBNET_IPV4, .net.ipv4 = ipv4_1, .owner = &foo};
186         const subnet_t c = {.type = SUBNET_IPV4, .net.ipv4 = ipv4_1, .owner = &bar};
187
188         assert_int_equal(0, subnet_compare(&a, &b));
189         assert_true(subnet_compare(&a, &c) > 0);
190
191         free(foo.name);
192         free(bar.name);
193 }
194
195 static void test_subnet_compare_ipv6_eq(void **state) {
196         (void)state;
197
198         const subnet_t a = {.type = SUBNET_IPV6, .net.ipv6 = ipv6_1};
199         const subnet_t b = {.type = SUBNET_IPV6, .net.ipv6 = ipv6_1};
200
201         assert_int_equal(0, subnet_compare(&a, &b));
202         assert_int_equal(0, subnet_compare(&b, &a));
203 }
204
205 static void test_subnet_compare_ipv6_neq(void **state) {
206         (void)state;
207
208         const subnet_t a = {.type = SUBNET_IPV6, .net.ipv6 = ipv6_1};
209         const subnet_t b = {.type = SUBNET_IPV6, .net.ipv6 = ipv6_1_pref};
210         const subnet_t c = {.type = SUBNET_IPV6, .net.ipv6 = ipv6_2};
211
212         assert_true(subnet_compare(&a, &b) < 0);
213         assert_true(subnet_compare(&b, &a) > 0);
214
215         assert_true(subnet_compare(&a, &c) < 0);
216         assert_true(subnet_compare(&b, &c) > 0);
217 }
218
219 static void test_subnet_compare_ipv6_weight(void **state) {
220         (void)state;
221
222         const subnet_t a = {.type = SUBNET_IPV6, .net.ipv6 = ipv6_1, .weight = 1};
223         const subnet_t b = {.type = SUBNET_IPV6, .net.ipv6 = ipv6_1, .weight = 2};
224
225         assert_true(subnet_compare(&a, &b) < 0);
226 }
227
228 static void test_subnet_compare_ipv6_owners(void **state) {
229         (void)state;
230
231         node_t foo = {.name = strdup("foo")};
232         node_t bar = {.name = strdup("bar")};
233
234         const subnet_t a = {.type = SUBNET_IPV6, .net.ipv6 = ipv6_1, .owner = &foo};
235         const subnet_t b = {.type = SUBNET_IPV6, .net.ipv6 = ipv6_1, .owner = &foo};
236         const subnet_t c = {.type = SUBNET_IPV6, .net.ipv6 = ipv6_1, .owner = &bar};
237
238         assert_int_equal(0, subnet_compare(&a, &b));
239         assert_true(subnet_compare(&a, &c) > 0);
240
241         free(foo.name);
242         free(bar.name);
243 }
244
245 static void test_str2net_valid(void **state) {
246         (void)state;
247
248         const net_str_testcase testcases[] = {
249                 {
250                         .text = "1.2.3.0/24#42",
251                         .data = {
252                                 .type = SUBNET_IPV4,
253                                 .weight = 42,
254                                 .net = {
255                                         .ipv4 = {
256                                                 .address = {
257                                                         .x = {1, 2, 3, 0}
258                                                 },
259                                                 .prefixlength = 24,
260                                         },
261                                 },
262                         },
263                 },
264                 {
265                         .text = "04fb:7deb:78db:1950:2d21:258d:40b6:f0d7/128#999",
266                         .data = {
267                                 .type = SUBNET_IPV6,
268                                 .weight = 999,
269                                 .net = {
270                                         .ipv6 = {
271                                                 .address = {
272                                                         .x = {
273                                                                 htons(0x04fb), htons(0x7deb), htons(0x78db), htons(0x1950),
274                                                                 htons(0x2d21), htons(0x258d), htons(0x40b6), htons(0xf0d7),
275                                                         }
276                                                 },
277                                                 .prefixlength = 128,
278                                         },
279                                 },
280                         },
281                 },
282                 {
283                         .text = "fe80::16dd:a9ff:fe7e:b4c2/64",
284                         .data = {
285                                 .type = SUBNET_IPV6,
286                                 .weight = 10,
287                                 .net = {
288                                         .ipv6 = {
289                                                 .address = {
290                                                         .x = {
291                                                                 htons(0xfe80), htons(0x0000), htons(0x0000), htons(0x0000),
292                                                                 htons(0x16dd), htons(0xa9ff), htons(0xfe7e), htons(0xb4c2),
293                                                         }
294                                                 },
295                                                 .prefixlength = 64,
296                                         },
297                                 },
298                         },
299                 },
300                 {
301                         .text = "57:04:13:01:f9:26#60",
302                         .data = {
303                                 .type = SUBNET_MAC,
304                                 .weight = 60,
305                                 .net = {
306                                         .mac = {
307                                                 .address = {
308                                                         .x = {0x57, 0x04, 0x13, 0x01, 0xf9, 0x26},
309                                                 },
310                                         },
311                                 },
312                         },
313                 },
314         };
315
316         for(size_t i = 0; i < sizeof(testcases) / sizeof(*testcases); ++i) {
317                 const char *text = testcases[i].text;
318                 const subnet_t *ref = &testcases[i].data;
319
320                 subnet_t sub = {0};
321                 bool ok = str2net(&sub, text);
322
323                 // Split into separate assertions for more clear failures
324                 assert_true(ok);
325                 assert_int_equal(ref->type, sub.type);
326                 assert_int_equal(ref->weight, sub.weight);
327
328                 switch(ref->type) {
329                 case SUBNET_MAC:
330                         assert_memory_equal(&ref->net.mac.address, &sub.net.mac.address, sizeof(mac_t));
331                         break;
332
333                 case SUBNET_IPV4:
334                         assert_int_equal(ref->net.ipv4.prefixlength, sub.net.ipv4.prefixlength);
335                         assert_memory_equal(&ref->net.ipv4.address, &sub.net.ipv4.address, sizeof(ipv4_t));
336                         break;
337
338                 case SUBNET_IPV6:
339                         assert_int_equal(ref->net.ipv6.prefixlength, sub.net.ipv6.prefixlength);
340                         assert_memory_equal(&ref->net.ipv6.address, &sub.net.ipv6.address, sizeof(ipv6_t));
341                         break;
342
343                 default:
344                         fail_msg("unknown subnet type %d", ref->type);
345                 }
346         }
347 }
348
349 static void test_str2net_invalid(void **state) {
350         (void)state;
351
352         subnet_t sub = {0};
353
354         const char *test_cases[] = {
355                 // Overflow
356                 "1.2.256.0",
357
358                 // Invalid mask
359                 "1.2.3.0/",
360                 "1.2.3.0/42",
361                 "1.2.3.0/MASK",
362                 "fe80::/129",
363                 "fe80::/MASK",
364                 "cb:0c:1b:60:ed:7a/1",
365
366                 // Invalid weight
367                 "1.2.3.4#WEIGHT",
368                 "1.2.0.0/16#WEIGHT",
369                 "1.2.0.0/16#",
370                 "feff::/16#",
371                 "feff::/16#w",
372
373                 NULL,
374         };
375
376         for(const char **str = test_cases; *str; ++str) {
377                 bool ok = str2net(&sub, *str);
378                 assert_false(ok);
379         }
380 }
381
382 static void test_net2str_valid(void **state) {
383         (void)state;
384
385         const net_str_testcase testcases[] = {
386                 {
387                         .text = "12:fe:ff:3a:28:90#42",
388                         .data = {
389                                 .type = SUBNET_MAC,
390                                 .weight = 42,
391                                 .net = {
392                                         .mac = {
393                                                 .address = {
394                                                         .x = {0x12, 0xfe, 0xff, 0x3a, 0x28, 0x90}
395                                                 },
396                                         },
397                                 },
398                         },
399                 },
400                 {
401                         .text = "1.2.3.4",
402                         .data = {
403                                 .type = SUBNET_IPV4,
404                                 .weight = 10,
405                                 .net = {
406                                         .ipv4 = {
407                                                 .address = {
408                                                         .x = {1, 2, 3, 4}
409                                                 },
410                                                 .prefixlength = 32,
411                                         },
412                                 },
413                         },
414                 },
415                 {
416                         .text = "181.35.16.0/27#1",
417                         .data = {
418                                 .type = SUBNET_IPV4,
419                                 .weight = 1,
420                                 .net = {
421                                         .ipv4 = {
422                                                 .address = {
423                                                         .x = {181, 35, 16, 0}
424                                                 },
425                                                 .prefixlength = 27,
426                                         },
427                                 },
428                         },
429                 },
430                 {
431                         .text = "5fbf:5cfe:0:fdd2:fd76::/96#900",
432                         .data = {
433                                 .type = SUBNET_IPV6,
434                                 .weight = 900,
435                                 .net = {
436                                         .ipv6 = {
437                                                 .address = {
438                                                         .x = {
439                                                                 htons(0x5fbf), htons(0x5cfe), htons(0x0000), htons(0xfdd2),
440                                                                 htons(0xfd76), htons(0x0000), htons(0x0000), htons(0x0000),
441                                                         },
442                                                 },
443                                                 .prefixlength = 96,
444                                         },
445                                 },
446                         },
447                 },
448         };
449
450         for(size_t i = 0; i < sizeof(testcases) / sizeof(*testcases); ++i) {
451                 const char *text = testcases[i].text;
452                 const subnet_t *ref = &testcases[i].data;
453
454                 char buf[256];
455                 bool ok = net2str(buf, sizeof(buf), ref);
456
457                 assert_true(ok);
458                 assert_string_equal(text, buf);
459         }
460 }
461
462 static void test_net2str_invalid(void **state) {
463         (void)state;
464
465         const subnet_t sub = {0};
466         char buf[256];
467         assert_false(net2str(NULL, sizeof(buf), &sub));
468         assert_false(net2str(buf, sizeof(buf), NULL));
469 }
470
471 static void test_maskcheck_valid_ipv4(void **state) {
472         (void)state;
473
474         const ipv4_t a = {{10, 0, 0, 0}};
475         const ipv4_t b = {{192, 168, 0, 0}};
476         const ipv4_t c = {{192, 168, 24, 0}};
477
478         assert_true(maskcheck(&a, 8, sizeof(a)));
479         assert_true(maskcheck(&b, 16, sizeof(b)));
480         assert_true(maskcheck(&c, 24, sizeof(c)));
481 }
482
483 static void test_maskcheck_valid_ipv6(void **state) {
484         (void)state;
485
486         const ipv6_t a = {{10, 0, 0, 0, 0, 0, 0, 0}};
487         assert_true(maskcheck(&a, 8, sizeof(a)));
488
489         const ipv6_t b = {{10, 20, 0, 0, 0, 0, 0, 0}};
490         assert_true(maskcheck(&b, 32, sizeof(b)));
491
492         const ipv6_t c = {{192, 168, 24, 0, 0, 0, 0, 0}};
493         assert_true(maskcheck(&c, 48, sizeof(c)));
494 }
495
496 static void test_maskcheck_invalid_ipv4(void **state) {
497         (void)state;
498
499         const ipv4_t a = {{10, 20, 0, 0}};
500         const ipv4_t b = {{10, 20, 30, 0}};
501
502         assert_false(maskcheck(&a, 8, sizeof(a)));
503         assert_false(maskcheck(&b, 16, sizeof(b)));
504 }
505
506 static void test_maskcheck_invalid_ipv6(void **state) {
507         (void)state;
508
509         const ipv6_t a = {{1, 2, 3, 4, 5, 6, 7, 0xAABB}};
510
511         for(int mask = 0; mask < 128; mask += 8) {
512                 assert_false(maskcheck(&a, mask, sizeof(a)));
513         }
514 }
515
516 int main(void) {
517         const struct CMUnitTest tests[] = {
518                 cmocka_unit_test(test_maskcmp),
519                 cmocka_unit_test(test_mask),
520                 cmocka_unit_test(test_maskcpy),
521
522                 cmocka_unit_test(test_subnet_compare_different_types),
523
524                 cmocka_unit_test(test_subnet_compare_mac_eq),
525                 cmocka_unit_test(test_subnet_compare_mac_neq_address),
526                 cmocka_unit_test(test_subnet_compare_mac_weight),
527                 cmocka_unit_test(test_subnet_compare_mac_owners),
528
529                 cmocka_unit_test(test_subnet_compare_ipv4_eq),
530                 cmocka_unit_test(test_subnet_compare_ipv4_neq),
531                 cmocka_unit_test(test_subnet_compare_ipv4_weight),
532                 cmocka_unit_test(test_subnet_compare_ipv4_owners),
533
534                 cmocka_unit_test(test_subnet_compare_ipv6_eq),
535                 cmocka_unit_test(test_subnet_compare_ipv6_neq),
536                 cmocka_unit_test(test_subnet_compare_ipv6_weight),
537                 cmocka_unit_test(test_subnet_compare_ipv6_owners),
538
539                 cmocka_unit_test(test_str2net_valid),
540                 cmocka_unit_test(test_str2net_invalid),
541
542                 cmocka_unit_test(test_net2str_valid),
543                 cmocka_unit_test(test_net2str_invalid),
544
545                 cmocka_unit_test(test_maskcheck_valid_ipv4),
546                 cmocka_unit_test(test_maskcheck_valid_ipv6),
547                 cmocka_unit_test(test_maskcheck_invalid_ipv4),
548                 cmocka_unit_test(test_maskcheck_invalid_ipv6),
549         };
550         return cmocka_run_group_tests(tests, NULL, NULL);
551 }