1 /*
2 * $Id$
3 *
4 * Copyright (c) 2008, 2009
5 * Sten Spans <sten@blinkenlights.nl>
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
20 #include "config.h"
21 #include <check.h>
22
23 #include "common.h"
24 #include "util.h"
25 #include "proto/protos.h"
26 #include "main.h"
27 #include "parent.h"
28 #include "check_wrap.h"
29
30 #ifdef USE_CAPABILITIES
31 #include <sys/prctl.h>
32 #include <sys/capability.h>
33 #endif
34
35 #if HAVE_LINUX_ETHTOOL_H
36 #include <linux/ethtool.h>
37 #endif /* HAVE_LINUX_ETHTOOL_H */
38 #ifdef HAVE_NET_BPF_H
39 #include <net/bpf.h>
40 #endif /* HAVE_NET_BPF_H */
41 #ifdef HAVE_PCI_PCI_H
42 #include <pci/pci.h>
43 #endif /* HAVE_PCI_PCI_H */
44
45 const char *ifname = NULL;
46 unsigned int ifindex = 0;
47
48 uint32_t options = OPT_DAEMON | OPT_CHECK;
49 extern int dfd;
50 extern int mfd;
51 extern struct rfdhead rawfds;
52
START_TEST(test_parent_init)53 START_TEST(test_parent_init) {
54 const char *errstr = NULL;
55 int spair[2], fd = -1;
56
57 options |= OPT_DEBUG;
58
59 // make sure stdout is not a tty
60 fd = dup(STDOUT_FILENO);
61 close(STDOUT_FILENO);
62 my_socketpair(spair);
63
64 errstr = "test";
65 my_log(CRIT, errstr);
66 WRAP_FATAL_START();
67 parent_init(0, 0, 0);
68 WRAP_FATAL_END();
69 fail_unless (strcmp(check_wrap_errstr, errstr) == 0,
70 "incorrect message logged: %s", check_wrap_errstr);
71
72 close(spair[0]);
73 close(spair[1]);
74 fd = dup(fd);
75 options &= ~OPT_DEBUG;
76 }
77 END_TEST
78
START_TEST(test_parent_signal)79 START_TEST(test_parent_signal) {
80 int sig = 0;
81 short event = 0;
82 pid_t pid = 1;
83 const char *errstr = NULL;
84
85 loglevel = INFO;
86
87 mark_point();
88 sig = SIGCHLD;
89 errstr = "quitting";
90 WRAP_FATAL_START();
91 parent_signal(sig, event, &pid);
92 WRAP_FATAL_END();
93 fail_unless (strcmp(check_wrap_errstr, errstr) == 0,
94 "incorrect message logged: %s", check_wrap_errstr);
95
96 mark_point();
97 check_wrap_fake |= FAKE_KILL;
98 sig = SIGINT;
99 errstr = "quitting";
100 WRAP_FATAL_START();
101 parent_signal(sig, event, &pid);
102 WRAP_FATAL_END();
103 fail_unless (strcmp(check_wrap_errstr, errstr) == 0,
104 "incorrect message logged: %s", check_wrap_errstr);
105
106 mark_point();
107 sig = SIGTERM;
108 errstr = "quitting";
109 WRAP_FATAL_START();
110 parent_signal(sig, event, &pid);
111 WRAP_FATAL_END();
112 fail_unless (strcmp(check_wrap_errstr, errstr) == 0,
113 "incorrect message logged: %s", check_wrap_errstr);
114 check_wrap_fake &= ~FAKE_KILL;
115
116 mark_point();
117 sig = SIGHUP;
118 errstr = "check";
119 my_log(CRIT, errstr);
120 parent_signal(sig, event, NULL);
121 fail_unless (strcmp(check_wrap_errstr, errstr) == 0,
122 "incorrect message logged: %s", check_wrap_errstr);
123
124 mark_point();
125 sig = 0;
126 errstr = "unexpected signal";
127 WRAP_FATAL_START();
128 parent_signal(sig, event, NULL);
129 WRAP_FATAL_END();
130 fail_unless (strcmp(check_wrap_errstr, errstr) == 0,
131 "incorrect message logged: %s", check_wrap_errstr);
132
133 // reset
134 check_wrap_fake = 0;
135 }
136 END_TEST
137
START_TEST(test_parent_req)138 START_TEST(test_parent_req) {
139 struct parent_req mreq = {};
140 struct ether_hdr ether = {};
141 static uint8_t lldp_dst[] = LLDP_MULTICAST_ADDR;
142 struct rawfd *rfd;
143 const char *errstr = NULL;
144 int spair[2], fd = -1;
145 short event = 0;
146
147 loglevel = INFO;
148 my_socketpair(spair);
149
150 // supply an invalid fd, resulting in a read error
151 mark_point();
152 errstr = "invalid request received";
153 my_log(CRIT, errstr);
154 WRAP_FATAL_START();
155 parent_req(fd, event);
156 WRAP_FATAL_END();
157 fail_unless (strcmp(check_wrap_errstr, errstr) == 0,
158 "incorrect message logged: %s", check_wrap_errstr);
159
160 // test a message with an incorrect size
161 mark_point();
162 errstr = "invalid request received";
163 WRAP_WRITE(spair[0], &mreq, 1);
164 WRAP_FATAL_START();
165 parent_req(spair[1], event);
166 WRAP_FATAL_END();
167 fail_unless (strcmp(check_wrap_errstr, errstr) == 0,
168 "incorrect message logged: %s", check_wrap_errstr);
169
170 // test a message with incorrect ifindex
171 mark_point();
172 mreq.op = PARENT_MAX - 1;
173 mreq.len = ETHER_MIN_LEN;
174
175 errstr = "check";
176 my_log(CRIT, errstr);
177 WRAP_WRITE(spair[0], &mreq, PARENT_REQ_LEN(mreq.len));
178 WRAP_FATAL_START();
179 parent_req(spair[1], event);
180 WRAP_FATAL_END();
181 fail_unless (strcmp(check_wrap_errstr, errstr) == 0,
182 "incorrect message logged: %s", check_wrap_errstr);
183
184 // test a correct CLOSE
185 mark_point();
186 dfd = spair[1];
187 options |= OPT_DEBUG;
188 mreq.op = PARENT_CLOSE;
189 mreq.index = ifindex;
190 mreq.len = 0;
191 memcpy(ether.dst, lldp_dst, ETHER_ADDR_LEN);
192 ether.type = htons(ETHERTYPE_LLDP);
193 memcpy(mreq.buf, ðer, sizeof(struct ether_hdr));
194 WRAP_WRITE(spair[0], &mreq, PARENT_REQ_LEN(mreq.len));
195
196 errstr = "check";
197 my_log(CRIT, errstr);
198 WRAP_FATAL_START();
199 parent_req(spair[1], event);
200 WRAP_FATAL_END();
201 fail_unless (strcmp(check_wrap_errstr, errstr) == 0,
202 "incorrect message logged: %s", check_wrap_errstr);
203
204 // test a correct CLOSE
205 mark_point();
206 fail_unless (rfd_byindex(&rawfds, ifindex) == NULL,
207 "the queue should be empty");
208
209 options |= OPT_DEBUG;
210 parent_open(ifindex, ifname);
211 fail_unless (rfd_byindex(&rawfds, ifindex) != NULL,
212 "rfd should be added to the queue");
213
214 errstr = "check";
215 my_log(CRIT, errstr);
216 mreq.op = PARENT_CLOSE;
217 mreq.index = ifindex;
218 strlcpy(mreq.name, ifname, IFNAMSIZ);
219 WRAP_WRITE(spair[0], &mreq, PARENT_REQ_LEN(mreq.len));
220 parent_req(spair[1], event);
221 fail_unless (strcmp(check_wrap_errstr, errstr) == 0,
222 "incorrect message logged: %s", check_wrap_errstr);
223 fail_unless (rfd_byindex(&rawfds, ifindex) == NULL,
224 "rfd should be removed from the queue");
225
226 #if defined(SIOCSIFDESCR) || defined(HAVE_SYSFS)
227 // test a correct DESCR
228 mark_point();
229 mreq.op = PARENT_DESCR;
230 mreq.len = 1;
231
232 WRAP_WRITE(spair[0], &mreq, PARENT_REQ_LEN(mreq.len));
233
234 errstr = "check";
235 my_log(CRIT, errstr);
236 parent_req(spair[1], event);
237 fail_unless (strcmp(check_wrap_errstr, errstr) == 0,
238 "incorrect message logged: %s", check_wrap_errstr);
239 #endif
240
241 #ifdef HAVE_SYSFS
242 mark_point();
243 mreq.op = PARENT_ALIAS;
244 mreq.len = 1;
245
246 WRAP_WRITE(spair[0], &mreq, PARENT_REQ_LEN(mreq.len));
247
248 errstr = "check";
249 my_log(CRIT, errstr);
250 parent_req(spair[1], event);
251 fail_unless (strcmp(check_wrap_errstr, errstr) == 0,
252 "incorrect message logged: %s", check_wrap_errstr);
253
254 // test a correct DEVICE
255 mreq.op = PARENT_DEVICE;
256 mreq.len = 0;
257 WRAP_WRITE(spair[0], &mreq, PARENT_REQ_LEN(mreq.len));
258 parent_req(spair[1], event);
259 fail_unless (strcmp(check_wrap_errstr, errstr) == 0,
260 "incorrect message logged: %s", check_wrap_errstr);
261 #endif /* HAVE_SYSFS */
262
263 // test a failing return message
264 mark_point();
265 parent_open(ifindex, ifname);
266 rfd = rfd_byindex(&rawfds, ifindex);
267 fail_unless (rfd != NULL, "rfd should be added to the queue");
268 mreq.op = PARENT_CLOSE;
269 close(rfd->fd);
270 rfd->fd = dup(spair[1]);
271 WRAP_WRITE(spair[0], &mreq, PARENT_REQ_LEN(mreq.len));
272 close(spair[0]);
273
274 errstr = "failed to return request to child";
275 WRAP_FATAL_START();
276 parent_req(spair[1], event);
277 WRAP_FATAL_END();
278 fail_unless (strcmp(check_wrap_errstr, errstr) == 0,
279 "incorrect message logged: %s", check_wrap_errstr);
280 fail_unless (close(fd) == -1, "rfd->fd should be closed");
281 fail_unless (rfd_byindex(&rawfds, ifindex) == NULL,
282 "rfd should be removed from the queue");
283
284 close(spair[1]);
285 }
286 END_TEST
287
START_TEST(test_parent_check)288 START_TEST(test_parent_check) {
289 struct parent_req mreq = {};
290
291 mark_point();
292 mreq.op = PARENT_OPEN;
293 fail_unless(parent_check(&mreq) == EXIT_SUCCESS,
294 "PARENT_OPEN check failed");
295
296 mark_point();
297 mreq.op = PARENT_CLOSE;
298 fail_unless(parent_check(&mreq) == EXIT_SUCCESS,
299 "PARENT_CLOSE check failed");
300
301 #ifdef HAVE_LINUX_ETHTOOL_H
302 mark_point();
303 mreq.op = PARENT_ETHTOOL_GSET;
304 mreq.index = ifindex;
305 mreq.len = sizeof(struct ethtool_cmd);
306 fail_unless(parent_check(&mreq) == EXIT_SUCCESS,
307 "PARENT_ETHTOOL_GSET check failed");
308
309 mark_point();
310 mreq.op = PARENT_ETHTOOL_GDRV;
311 mreq.index = ifindex;
312 mreq.len = sizeof(struct ethtool_drvinfo);
313 fail_unless(parent_check(&mreq) == EXIT_SUCCESS,
314 "PARENT_ETHTOOL_GDRV check failed");
315 #endif
316
317 #ifdef SIOCSIFDESCR
318 mark_point();
319 mreq.op = PARENT_DESCR;
320 mreq.index = ifindex;
321 mreq.len = 0;
322 fail_unless(parent_check(&mreq) == EXIT_SUCCESS,
323 "PARENT_DESCR check failed");
324 #endif
325
326 #ifndef HAVE_LINUX_ETHTOOL_H
327 mark_point();
328 mreq.op = PARENT_ETHTOOL_GSET;
329 fail_unless(parent_check(&mreq) == EXIT_FAILURE,
330 "parent_check should fail");
331 #endif
332 }
333 END_TEST
334
START_TEST(test_parent_send)335 START_TEST(test_parent_send) {
336 struct rawfd *rfd;
337 struct parent_msg msg = {};
338 struct ether_hdr ether = {};
339 static uint8_t lldp_dst[] = LLDP_MULTICAST_ADDR;
340 int spair[2];
341 const char *errstr;
342 short event = 0;
343
344 loglevel = INFO;
345 options |= OPT_DEBUG;
346 my_socketpair(spair);
347 msg.index = ifindex;
348 msg.len = ETHER_MIN_LEN;
349 strlcpy(msg.name, ifname, IFNAMSIZ);
350
351 dfd = spair[1];
352 parent_open(ifindex, ifname);
353 rfd = rfd_byindex(&rawfds, ifindex);
354 fail_unless (rfd != NULL, "rfd should be added to the queue");
355
356 // incorrect msend msg.len should be skipped
357 mark_point();
358 errstr = "check";
359 my_log(CRIT, errstr);
360 WRAP_WRITE(spair[0], &msg, PARENT_MSG_LEN(msg.len) - 1);
361 WRAP_FATAL_START();
362 parent_send(spair[1], event);
363 WRAP_FATAL_END();
364 fail_unless (strncmp(check_wrap_errstr, errstr, strlen(errstr)) == 0,
365 "incorrect message logged: %s", check_wrap_errstr);
366
367 // incorrect msend msg.index should fail
368 mark_point();
369 errstr = "invalid ifindex supplied";
370 msg.index = UINT32_MAX;
371 WRAP_WRITE(spair[0], &msg, PARENT_MSG_LEN(msg.len));
372 WRAP_FATAL_START();
373 parent_send(spair[1], event);
374 WRAP_FATAL_END();
375 fail_unless (strncmp(check_wrap_errstr, errstr, strlen(errstr)) == 0,
376 "incorrect message logged: %s", check_wrap_errstr);
377
378 mark_point();
379 msg.index = ifindex;
380 msg.proto = PROTO_LLDP;
381 memcpy(ether.dst, lldp_dst, ETHER_ADDR_LEN);
382 ether.type = htons(ETHERTYPE_LLDP);
383 memcpy(msg.msg, ðer, sizeof(ether));
384 errstr = "only -1 bytes written";
385 close(rfd->fd);
386 rfd->fd = -1;
387 options &= ~OPT_DEBUG;
388 WRAP_WRITE(spair[0], &msg, PARENT_MSG_LEN(msg.len));
389 parent_send(spair[1], event);
390 fail_unless (strncmp(check_wrap_errstr, errstr, strlen(errstr)) == 0,
391 "incorrect message logged: %s", check_wrap_errstr);
392
393 options |= OPT_DEBUG;
394
395 close(spair[0]);
396 close(spair[1]);
397 rfd_closeall(&rawfds);
398 }
399 END_TEST
400
START_TEST(test_parent_open_close)401 START_TEST(test_parent_open_close) {
402 struct rawfd *rfd;
403
404 options |= OPT_DEBUG;
405 dfd = STDOUT_FILENO;
406
407 mark_point();
408 parent_open(ifindex, ifname);
409 rfd = rfd_byindex(&rawfds, ifindex);
410 fail_unless (rfd != NULL,
411 "rfd should be added to the queue");
412 parent_close(rfd);
413 fail_unless (rfd_byindex(&rawfds, ifindex) == NULL,
414 "rfd should be removed from the queue");
415
416 mark_point();
417 parent_open(ifindex, ifname);
418 rfd = rfd_byindex(&rawfds, ifindex);
419 fail_unless (rfd != NULL,
420 "rfd should be added to the queue");
421
422 parent_open(2, "lo1");
423 rfd = rfd_byindex(&rawfds, 2);
424 fail_unless (rfd != NULL,
425 "rfd should be added to the queue");
426
427 rfd_closeall(&rawfds);
428 fail_unless (TAILQ_EMPTY(&rawfds),
429 "the queue should be empty");
430 }
431 END_TEST
432
START_TEST(test_parent_socket)433 START_TEST(test_parent_socket) {
434 struct rawfd *rfd;
435 const char *errstr;
436
437 options |= OPT_DEBUG;
438 dfd = STDOUT_FILENO;
439
440 mark_point();
441 parent_open(ifindex, ifname);
442 rfd = rfd_byindex(&rawfds, ifindex);
443 fail_unless (rfd != NULL, "rfd should be added to the queue");
444
445 mark_point();
446 // only run this as a regular user
447 if (!geteuid())
448 return;
449
450 options &= ~OPT_DEBUG;
451 errstr = "pcap_";
452 WRAP_FATAL_START();
453 parent_socket(rfd);
454 WRAP_FATAL_END();
455 fail_unless (strncmp(check_wrap_errstr, errstr, strlen(errstr)) == 0,
456 "incorrect message logged: %s", check_wrap_errstr);
457
458 rfd = rfd_byindex(&rawfds, ifindex);
459 fail_unless (rfd != NULL,
460 "rfd not found");
461 parent_close(rfd);
462 fail_unless (TAILQ_EMPTY(&rawfds),
463 "the queue should be empty");
464 }
465 END_TEST
466
START_TEST(test_parent_multi)467 START_TEST(test_parent_multi) {
468 struct rawfd rfd;
469 int spair[2];
470 const char *errstr;
471
472 my_socketpair(spair);
473 rfd.fd = spair[1];
474 rfd.index = ifindex;
475 strlcpy(rfd.name, ifname, IFNAMSIZ);
476
477 check_wrap_fake = 0;
478 protos[PROTO_LLDP].enabled = 1;
479
480 mark_point();
481 options |= OPT_DEBUG;
482 errstr = "check";
483 my_log(CRIT, errstr);
484 parent_multi(&rfd, protos, 0);
485 fail_unless (strncmp(check_wrap_errstr, errstr, strlen(errstr)) == 0,
486 "incorrect message logged: %s", check_wrap_errstr);
487
488 mark_point();
489 options &= ~OPT_DEBUG;
490 errstr = "unable to change LLDP multicast on";
491 WRAP_FATAL_START();
492 parent_multi(&rfd, protos, 1);
493 WRAP_FATAL_END();
494 fail_unless (strncmp(check_wrap_errstr, errstr, strlen(errstr)) == 0,
495 "incorrect message logged: %s", check_wrap_errstr);
496
497 mark_point();
498 check_wrap_fake |= FAKE_IOCTL|FAKE_SETSOCKOPT;
499 errstr = "check";
500 my_log(CRIT, errstr);
501 parent_multi(&rfd, protos, 1);
502 check_wrap_fake = 0;
503 fail_unless (strncmp(check_wrap_errstr, errstr, strlen(errstr)) == 0,
504 "incorrect message logged: %s", check_wrap_errstr);
505
506 // reset
507 check_wrap_fake = 0;
508 close(spair[0]);
509 close(spair[1]);
510 }
511 END_TEST
512
START_TEST(test_parent_recv)513 START_TEST(test_parent_recv) {
514 struct rawfd *rfd;
515 short event = 0;
516 const char *errstr = NULL;
517 char *prefix, *suffix, *path = NULL;
518 char errbuf[PCAP_ERRBUF_SIZE];
519 int spair[2];
520
521 my_socketpair(spair);
522
523 options |= OPT_DEBUG;
524 loglevel = INFO;
525 dfd = STDOUT_FILENO;
526
527 mark_point();
528 parent_open(ifindex, ifname);
529 rfd = rfd_byindex(&rawfds, ifindex);
530 fail_unless (rfd != NULL, "rfd should be added to the queue");
531
532 if ((prefix = getenv("srcdir")) == NULL)
533 prefix = ".";
534
535 mark_point();
536 suffix = "proto/broken/00.unknown";
537 fail_if(asprintf(&path, "%s/%s.pcap", prefix, suffix) == -1,
538 "asprintf failed");
539 fail_if((rfd->p_handle = pcap_open_offline(path, errbuf)) == NULL,
540 "failed to open %s: %s", path, errbuf);
541
542 errstr = "unknown message type received";
543 my_log(CRIT, "test");
544 WRAP_FATAL_START();
545 parent_recv(rfd->fd, event, rfd);
546 WRAP_FATAL_END();
547 fail_unless (strncmp(check_wrap_errstr, errstr, strlen(errstr)) == 0,
548 "incorrect message logged: %s", check_wrap_errstr);
549
550 pcap_close(rfd->p_handle);
551 rfd->p_handle = NULL;
552 free(path);
553
554 mark_point();
555 suffix = "proto/broken/01.empty";
556 fail_if(asprintf(&path, "%s/%s.pcap", prefix, suffix) == -1,
557 "asprintf failed");
558 fail_if((rfd->p_handle = pcap_open_offline(path, errbuf)) == NULL,
559 "failed to open %s: %s", path, errbuf);
560
561 errstr = "test";
562 my_log(CRIT, errstr);
563 WRAP_FATAL_START();
564 parent_recv(rfd->fd, event, rfd);
565 WRAP_FATAL_END();
566 fail_unless (strncmp(check_wrap_errstr, errstr, strlen(errstr)) == 0,
567 "incorrect message logged: %s", check_wrap_errstr);
568
569 pcap_close(rfd->p_handle);
570 rfd->p_handle = NULL;
571 free(path);
572
573 mark_point();
574 suffix = "proto/cdp/43.good.big";
575 fail_if(asprintf(&path, "%s/%s.pcap", prefix, suffix) == -1,
576 "asprintf failed");
577 fail_if((rfd->p_handle = pcap_open_offline(path, errbuf)) == NULL,
578 "failed to open %s: %s", path, errbuf);
579
580 // closed child socket
581 mark_point();
582 errstr = "failed to send message to child";
583 my_log(CRIT, "test");
584 WRAP_FATAL_START();
585 parent_recv(rfd->fd, event, rfd);
586 WRAP_FATAL_END();
587 fail_unless (strncmp(check_wrap_errstr, errstr, strlen(errstr)) == 0,
588 "incorrect message logged: %s", check_wrap_errstr);
589 pcap_close(rfd->p_handle);
590 rfd->p_handle = NULL;
591
592 // working
593 mark_point();
594 mfd = spair[0];
595
596 fail_if((rfd->p_handle = pcap_open_offline(path, errbuf)) == NULL,
597 "failed to open %s: %s", path, errbuf);
598
599 errstr = "received CDP message (422 bytes)";
600 my_log(CRIT, "test");
601 WRAP_FATAL_START();
602 parent_recv(rfd->fd, event, rfd);
603 WRAP_FATAL_END();
604 fail_unless (strncmp(check_wrap_errstr, errstr, strlen(errstr)) == 0,
605 "incorrect message logged: %s", check_wrap_errstr);
606
607 pcap_close(rfd->p_handle);
608 rfd->p_handle = NULL;
609 free(path);
610
611 mark_point();
612 rfd_closeall(&rawfds);
613 close(spair[0]);
614 close(spair[1]);
615 }
616 END_TEST
617
parent_suite(void)618 Suite * parent_suite (void) {
619 Suite *s = suite_create("parent.c");
620
621 TAILQ_INIT(&rawfds);
622
623 // parent test case
624 TCase *tc_parent = tcase_create("parent");
625 tcase_add_test(tc_parent, test_parent_init);
626 tcase_add_test(tc_parent, test_parent_signal);
627 tcase_add_test(tc_parent, test_parent_req);
628 tcase_add_test(tc_parent, test_parent_check);
629 tcase_add_test(tc_parent, test_parent_send);
630 tcase_add_test(tc_parent, test_parent_open_close);
631 tcase_add_test(tc_parent, test_parent_socket);
632 tcase_add_test(tc_parent, test_parent_multi);
633 tcase_add_test(tc_parent, test_parent_recv);
634 suite_add_tcase(s, tc_parent);
635
636 ifname = "lo";
637 ifindex = if_nametoindex(ifname);
638 if (!ifindex) {
639 ifname = "lo0";
640 ifindex = if_nametoindex(ifname);
641 }
642
643 return s;
644 }
645
main(void)646 int main (void) {
647 int number_failed;
648 Suite *s = parent_suite ();
649 SRunner *sr = srunner_create (s);
650 srunner_run_all (sr, CK_NORMAL);
651 number_failed = srunner_ntests_failed (sr);
652 srunner_free (sr);
653 return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
654 }
655
656