Annotation of embedaddon/arping/src/arping_test.c, revision 1.1
1.1 ! misho 1: /* arping/src/arping_test.c
! 2: *
! 3: * Copyright (C) 2015-2019 Thomas Habets <thomas@habets.se>
! 4: *
! 5: * This program is free software; you can redistribute it and/or modify
! 6: * it under the terms of the GNU General Public License as published by
! 7: * the Free Software Foundation; either version 2 of the License, or
! 8: * (at your option) any later version.
! 9: *
! 10: * This program is distributed in the hope that it will be useful,
! 11: * but WITHOUT ANY WARRANTY; without even the implied warranty of
! 12: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
! 13: * GNU General Public License for more details.
! 14: *
! 15: * You should have received a copy of the GNU General Public License along
! 16: * with this program; if not, write to the Free Software Foundation, Inc.,
! 17: * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
! 18: */
! 19: #include"config.h"
! 20: #define _GNU_SOURCE
! 21: #include<assert.h>
! 22: #include<errno.h>
! 23: #include<fcntl.h>
! 24: #include<inttypes.h>
! 25: #include<pthread.h>
! 26: #include<stdio.h>
! 27: #include<stdlib.h>
! 28:
! 29: #include<check.h>
! 30: #include<libnet.h>
! 31: #include<pcap.h>
! 32:
! 33: #include"arping.h"
! 34:
! 35: #ifndef ETH_ALEN
! 36: #define ETH_ALEN 6
! 37: #endif
! 38:
! 39: extern libnet_t* libnet;
! 40: extern int mock_libnet_lo_ok;
! 41: extern int mock_libnet_null_ok;
! 42:
! 43: struct registered_test {
! 44: void* fn;
! 45: const char* name;
! 46: };
! 47:
! 48: static int numtests = 0;
! 49: static struct registered_test test_registry[1024];
! 50:
! 51: static int num_exit_tests = 0;
! 52: static struct registered_test test_exit_registry[1024];
! 53:
! 54: int get_mac_addr(const char *in, char *out);
! 55: void strip_newline(char* s);
! 56:
! 57:
! 58: #define MYTEST(a) static void a(int);__attribute__((constructor)) \
! 59: static void cons_##a() { \
! 60: test_registry[numtests].fn = a; \
! 61: test_registry[numtests].name = #a; \
! 62: numtests++; \
! 63: } START_TEST(a)
! 64:
! 65: #define MY_EXIT_TEST(a) static void a(int);__attribute__((constructor)) \
! 66: static void cons_##a() { \
! 67: test_exit_registry[num_exit_tests].fn = a; \
! 68: test_exit_registry[num_exit_tests].name = #a; \
! 69: num_exit_tests++; \
! 70: } START_TEST(a)
! 71:
! 72: /**
! 73: *
! 74: */
! 75: static void
! 76: xclose(int* fd)
! 77: {
! 78: if (0 > close(*fd)) {
! 79: fprintf(stderr, "close(%d): %s", *fd, strerror(errno));
! 80: *fd = -1;
! 81: }
! 82: }
! 83:
! 84: struct captured_output {
! 85: int saved_fd; // Old fd, will be dup2()ed back in place when done.
! 86: int fno; // Overridden fd (e.g. stdout or stderr).
! 87: int reader_fd; // Reader end of the pipe.
! 88: char* buffer; // Output buffer.
! 89: size_t bufsize; // Buffer size.
! 90: pthread_t thread; // Reader thread.
! 91: };
! 92:
! 93: /**
! 94: * Helper function for stdout/stderr catching.
! 95: *
! 96: * This is the main() for the thread that reads from the fake stdout pipe
! 97: * and writes into the buffer.
! 98: *
! 99: */
! 100: static void*
! 101: read_main(void* p)
! 102: {
! 103: struct captured_output* out = p;
! 104: char *cur = out->buffer;
! 105:
! 106: for (;;) {
! 107: ssize_t n;
! 108: n = out->bufsize - (cur - out->buffer);
! 109: assert(n > 0);
! 110: n = read(out->reader_fd, cur, n);
! 111: if (n > 0) {
! 112: cur += n;
! 113: }
! 114: if (n == 0) {
! 115: return NULL;
! 116: }
! 117: }
! 118: }
! 119:
! 120: /**
! 121: * Helper function to capture stdout/stderr output.
! 122: *
! 123: * Args:
! 124: * fd: The fd to capture.
! 125: * Returns:
! 126: * A structure to be used as a handle. Only thing caller should do with
! 127: * this structure is call stop_capture(), read its .buffer member, and
! 128: * uncapture().
! 129: */
! 130: static struct captured_output*
! 131: capture(int fd)
! 132: {
! 133: struct captured_output* out;
! 134:
! 135: out = calloc(1, sizeof(struct captured_output));
! 136: fail_if(out == NULL);
! 137:
! 138: out->fno = fd;
! 139: out->saved_fd = dup(fd);
! 140: out->bufsize = 1024*100;
! 141: out->buffer = calloc(1, out->bufsize);
! 142:
! 143: fail_if(0 > out->saved_fd);
! 144: fail_if(out->buffer == NULL);
! 145:
! 146: // set up pipe
! 147: int fds[2];
! 148: fail_if(0 > pipe(fds));
! 149: fail_if(0 > dup2(fds[1], fd));
! 150: out->reader_fd = fds[0];
! 151: xclose(&fds[1]);
! 152:
! 153: fail_if(pthread_create(&out->thread, NULL, read_main, (void*)out));
! 154: return out;
! 155: }
! 156:
! 157: /**
! 158: * Helper function to capture stdout/stderr output.
! 159: *
! 160: * Stop capture, so that .buffer becomes readable.
! 161: */
! 162: static void
! 163: stop_capture(struct captured_output* out)
! 164: {
! 165: fail_if(0 > dup2(out->saved_fd, out->fno));
! 166: xclose(&out->saved_fd);
! 167: fail_if(pthread_join(out->thread, NULL));
! 168: xclose(&out->reader_fd);
! 169: }
! 170:
! 171: /**
! 172: * Helper function to capture stdout/stderr output.
! 173: *
! 174: * Deallocate buffer. stop_capture() must be called before uncapture().
! 175: */
! 176: static void
! 177: uncapture(struct captured_output* out)
! 178: {
! 179: free(out->buffer);
! 180: out->buffer = NULL;
! 181: free(out);
! 182: }
! 183:
! 184: static uint8_t*
! 185: mkpacket(struct pcap_pkthdr* pkthdr)
! 186: {
! 187: uint8_t* packet = calloc(1, 1500);
! 188: fail_if(packet == NULL);
! 189:
! 190: struct libnet_802_3_hdr* heth;
! 191: struct libnet_arp_hdr* harp;
! 192:
! 193: // Set up ethernet header
! 194: heth = (void*)packet;
! 195: memcpy(heth->_802_3_dhost, "\x11\x22\x33\x44\x55\x66", 6);
! 196: memcpy(heth->_802_3_shost, "\x77\x88\x99\xaa\xbb\xcc", 6);
! 197: heth->_802_3_len = 0; // FIXME: is this correct?
! 198:
! 199: // Set up ARP header.
! 200: harp = (void*)((char*)heth + LIBNET_ETH_H);
! 201: harp->ar_hln = 6;
! 202: harp->ar_pln = 4;
! 203: harp->ar_hrd = htons(ARPHRD_ETHER);
! 204: harp->ar_op = htons(ARPOP_REPLY);
! 205: harp->ar_pro = htons(ETHERTYPE_IP);
! 206:
! 207: memcpy((char*)harp + LIBNET_ARP_H, heth->_802_3_shost, 6);
! 208: memcpy((char*)harp + LIBNET_ARP_H + harp->ar_hln, &dstip, 4);
! 209:
! 210: memcpy((char*)harp + LIBNET_ARP_H
! 211: + harp->ar_hln
! 212: + harp->ar_pln, heth->_802_3_dhost, 6);
! 213: memcpy((char*)harp + LIBNET_ARP_H
! 214: + harp->ar_hln
! 215: + harp->ar_pln
! 216: + harp->ar_hln, &srcip, 4);
! 217:
! 218: pkthdr->ts.tv_sec = time(NULL);
! 219: pkthdr->ts.tv_usec = 0;
! 220: pkthdr->len = 60;
! 221: pkthdr->caplen = 60;
! 222:
! 223: return packet;
! 224: }
! 225:
! 226: static void
! 227: dump_packet(uint8_t* packet, int len)
! 228: {
! 229: int c;
! 230: for (c = 0; c < len; c++) {
! 231: fprintf(stderr, "0x%.2x, ", (int)packet[c]);
! 232: if (!((c+1) % 10)) {
! 233: fprintf(stderr, "\n");
! 234: }
! 235: }
! 236: fprintf(stderr, "\n");
! 237: }
! 238:
! 239: MYTEST(test_mkpacket)
! 240: {
! 241: uint8_t correct_packet[] = {
! 242: 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa,
! 243: 0xbb, 0xcc, 0x00, 0x00, 0x00, 0x01, 0x08, 0x00, 0x06, 0x04,
! 244: 0x00, 0x02, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0x12, 0x34,
! 245: 0x56, 0x78, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x87, 0x65,
! 246: 0x43, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
! 247: 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
! 248: };
! 249: struct pcap_pkthdr pkthdr;
! 250: dstip = htonl(0x12345678);
! 251: srcip = htonl(0x87654321);
! 252:
! 253: uint8_t* packet = mkpacket(&pkthdr);
! 254: fail_if(packet == NULL);
! 255: fail_unless(pkthdr.caplen == 60);
! 256: if (memcmp(packet, correct_packet, pkthdr.caplen)) {
! 257: dump_packet(packet, pkthdr.caplen);
! 258: }
! 259: fail_unless(!memcmp(packet, correct_packet, pkthdr.caplen));
! 260: } END_TEST
! 261:
! 262:
! 263: // Received uninteresting packet, should not record anything.
! 264: MYTEST(pingip_uninteresting_packet)
! 265: {
! 266: struct pcap_pkthdr pkthdr;
! 267: uint8_t* packet;
! 268: struct libnet_802_3_hdr* heth;
! 269: struct libnet_arp_hdr* harp;
! 270:
! 271: int prev_numrecvd = numrecvd;
! 272: struct captured_output* sout;
! 273:
! 274: // Completely broken packet.
! 275: packet = calloc(1, 1500);
! 276: sout = capture(1);
! 277: pingip_recv(NULL, &pkthdr, packet);
! 278: stop_capture(sout);
! 279: fail_unless(strlen(sout->buffer) == 0);
! 280: fail_unless(prev_numrecvd == numrecvd);
! 281: uncapture(sout);
! 282: free(packet);
! 283:
! 284: // Not ETHERTYPE_IP.
! 285: packet = mkpacket(&pkthdr);
! 286: harp = (void*)((char*)packet + LIBNET_ETH_H);
! 287: harp->ar_pro = 0;
! 288: sout = capture(1);
! 289: pingip_recv(NULL, &pkthdr, packet);
! 290: stop_capture(sout);
! 291: fail_unless(prev_numrecvd == numrecvd);
! 292: fail_unless(strlen(sout->buffer) == 0);
! 293: uncapture(sout);
! 294: free(packet);
! 295:
! 296: // Not ARPHRD_ETHER
! 297: packet = mkpacket(&pkthdr);
! 298: harp = (void*)((char*)packet + LIBNET_ETH_H);
! 299: harp->ar_hrd = 0;
! 300: sout = capture(1);
! 301: pingip_recv(NULL, &pkthdr, packet);
! 302: stop_capture(sout);
! 303: fail_unless(prev_numrecvd == numrecvd);
! 304: fail_unless(strlen(sout->buffer) == 0);
! 305: uncapture(sout);
! 306: free(packet);
! 307:
! 308: // Wrong dstip
! 309: if (0) {
! 310: uint32_t wrongip = 123;
! 311: packet = mkpacket(&pkthdr);
! 312: harp = (void*)((char*)packet + LIBNET_ETH_H);
! 313: memcpy((char*)harp + harp->ar_hln + LIBNET_ARP_H, &wrongip, 4);
! 314: sout = capture(1);
! 315: pingip_recv(NULL, &pkthdr, packet);
! 316: stop_capture(sout);
! 317: fail_unless(prev_numrecvd == numrecvd);
! 318: fail_unless(strlen(sout->buffer) == 0);
! 319: uncapture(sout);
! 320: free(packet);
! 321: }
! 322:
! 323: // Short packet.
! 324: packet = mkpacket(&pkthdr);
! 325: pkthdr.caplen = pkthdr.len = 41;
! 326: sout = capture(1);
! 327: pingip_recv(NULL, &pkthdr, packet);
! 328: stop_capture(sout);
! 329: fail_unless(prev_numrecvd == numrecvd);
! 330: fail_unless(strlen(sout->buffer) == 0);
! 331: uncapture(sout);
! 332: free(packet);
! 333:
! 334: // Short captured packet.
! 335: packet = mkpacket(&pkthdr);
! 336: pkthdr.caplen = 41;
! 337: sout = capture(1);
! 338: pingip_recv(NULL, &pkthdr, packet);
! 339: stop_capture(sout);
! 340: fail_unless(prev_numrecvd == numrecvd);
! 341: fail_unless(strlen(sout->buffer) == 0);
! 342: uncapture(sout);
! 343: free(packet);
! 344:
! 345: // Wrong length of hardware address.
! 346: {
! 347: uint8_t packet[] = {
! 348: 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, // dst
! 349: 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, // src
! 350: 0x00, 0x00, // type
! 351: 0x00, 0x01, // hardware
! 352: 0x08, 0x00, // protocol
! 353: 0x04, 0x04, // lengths (for this test length is wrong)
! 354: 0x00, 0x02, // operator
! 355:
! 356: 0x77, 0x88, 0x99, 0xaa, // sender (wrong length for test)
! 357: 0x12, 0x34, 0x56, 0x78, // sender protocol address
! 358:
! 359: 0x11, 0x22, 0x33, 0x44, // receiver (wrong length for test)
! 360: 0x87, 0x65, 0x43, 0x21, // receiver protocol address
! 361:
! 362: 0x00, 0x00, 0x00, 0x00,
! 363: 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
! 364: 0x00, 0x00, 0x00, 0x00,
! 365: 0x00, 0x00, 0x00, 0x00,
! 366: 0x00, 0x00, 0x00, 0x00,
! 367: 0x6f, 0xa8, 0x58, 0x63,
! 368: };
! 369: pkthdr.len = 60;
! 370: pkthdr.caplen = 60;
! 371: sout = capture(1);
! 372: pingip_recv(NULL, &pkthdr, packet);
! 373: stop_capture(sout);
! 374: fail_unless(strlen(sout->buffer) == 0, sout->buffer);
! 375: fail_unless(prev_numrecvd == numrecvd);
! 376: uncapture(sout);
! 377: }
! 378:
! 379: // Wrong length of protocol address.
! 380: packet = mkpacket(&pkthdr);
! 381: ((struct libnet_arp_hdr*)((char*)packet + LIBNET_ETH_H))->ar_pln = 6;
! 382: sout = capture(1);
! 383: pingip_recv(NULL, &pkthdr, packet);
! 384: stop_capture(sout);
! 385: fail_unless(prev_numrecvd == numrecvd);
! 386: fail_unless(strlen(sout->buffer) == 0);
! 387: uncapture(sout);
! 388: free(packet);
! 389: } END_TEST
! 390:
! 391: // Received reply that actually matches. Things should happen.
! 392: MYTEST(pingip_interesting_packet)
! 393: {
! 394: struct pcap_pkthdr pkthdr;
! 395: extern uint8_t srcmac[ETH_ALEN];
! 396: memcpy(srcmac, "\x11\x22\x33\x44\x55\x66", ETH_ALEN);
! 397: uint8_t packet[] = {
! 398: 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, // dst
! 399: 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, // src
! 400: 0x00, 0x00, // type
! 401: 0x00, 0x01, // hardware
! 402: 0x08, 0x00, // protocol
! 403: 0x06, 0x04, // lengths
! 404: 0x00, 0x02, // operator
! 405:
! 406: 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, // sender
! 407: 0x12, 0x34, 0x56, 0x78, // sender protocol address
! 408:
! 409: 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, // receiver
! 410: 0x87, 0x65, 0x43, 0x21, // receiver protocol address
! 411:
! 412: 0x00, 0x00, 0x00, 0x00,
! 413: 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
! 414: 0x6f, 0xa8, 0x58, 0x63,
! 415: };
! 416: numrecvd = 0;
! 417: int prev_numrecvd = numrecvd;
! 418:
! 419: dstip = htonl(0x12345678);
! 420:
! 421: pkthdr.ts.tv_sec = time(NULL);
! 422: pkthdr.ts.tv_usec = 0;
! 423: pkthdr.len = 60;
! 424: pkthdr.caplen = 60;
! 425:
! 426: struct captured_output *sout;
! 427:
! 428: // First ping.
! 429: const char* correct0 =
! 430: "60 bytes from 77:88:99:aa:bb:cc (18.52.86.120): "
! 431: "index=0 time=";
! 432: sout = capture(1);
! 433: pingip_recv(NULL, &pkthdr, packet);
! 434: stop_capture(sout);
! 435:
! 436: char* emsg = NULL;
! 437: fail_unless(0 < asprintf(&emsg, "Captured: <%s> (%zd), want <%s> %zd\n",
! 438: sout->buffer, strlen(sout->buffer),
! 439: correct0, strlen(correct0)));
! 440: fail_unless(!strncmp(sout->buffer, correct0, strlen(correct0)), emsg);
! 441: uncapture(sout);
! 442: free(emsg);
! 443:
! 444: fail_unless(numrecvd == prev_numrecvd + 1,
! 445: "numrecvd not incremented");
! 446:
! 447: pingip_recv(NULL, &pkthdr, packet);
! 448: fail_unless(numrecvd == prev_numrecvd + 2,
! 449: "numrecvd not incremented second time");
! 450: } END_TEST
! 451:
! 452: MYTEST(strip_newline_test)
! 453: {
! 454: const char *tests[][2] = {
! 455: {"", ""},
! 456: {"\n", ""},
! 457: {"\n\n\n", ""},
! 458: {"foo", "foo"},
! 459: {"foo\n", "foo"},
! 460: {"foo\n\n\n", "foo"},
! 461: {NULL, NULL},
! 462: };
! 463: int c;
! 464: for (c = 0; tests[c][0]; c++){
! 465: char buf[128];
! 466: strcpy(buf, tests[c][0]);
! 467: strip_newline(buf);
! 468: fail_unless(!strcmp(buf, tests[c][1]));
! 469: }
! 470: } END_TEST
! 471:
! 472: MYTEST(get_mac_addr_success)
! 473: {
! 474: const char *tests[][2] = {
! 475: // Null.
! 476: {"0000.0000.0000", "\x00\x00\x00\x00\x00\x00"},
! 477: {"00:00:00:00:00:00", "\x00\x00\x00\x00\x00\x00"},
! 478: {"00-00-00-00-00-00", "\x00\x00\x00\x00\x00\x00"},
! 479:
! 480: // Broadcast.
! 481: {"FFFF.FFFF.FFFF", "\xFF\xFF\xFF\xFF\xFF\xFF"},
! 482: {"FF:FF:FF:FF:FF:FF", "\xFF\xFF\xFF\xFF\xFF\xFF"},
! 483: {"FF-FF-FF-FF-FF-FF", "\xFF\xFF\xFF\xFF\xFF\xFF"},
! 484:
! 485: // Normal looking.
! 486: {"1122.3344.5566", "\x11\x22\x33\x44\x55\x66"},
! 487: {"11:22:33:44:55:66", "\x11\x22\x33\x44\x55\x66"},
! 488: {"11-22-33-44-55-66", "\x11\x22\x33\x44\x55\x66"},
! 489:
! 490: // Has some zeroes.
! 491: {"1100.0000.5566", "\x11\x00\x00\x00\x55\x66"},
! 492: {"11:00:00:00:55:66", "\x11\x00\x00\x00\x55\x66"},
! 493: {"11-00-00-00-55-66", "\x11\x00\x00\x00\x55\x66"},
! 494: {NULL, NULL},
! 495: };
! 496: int c;
! 497: for (c = 0; tests[c][0]; c++){
! 498: char buf[6];
! 499: fail_unless(get_mac_addr(tests[c][0], buf));
! 500: fail_unless(!memcmp(buf, tests[c][1], 6));
! 501: }
! 502: } END_TEST
! 503:
! 504: MYTEST(get_mac_addr_fail)
! 505: {
! 506: const char *tests[] = {
! 507: "",
! 508: "blaha",
! 509: "11:22:33:44:55",
! 510: "11:22:33:44:55:zz",
! 511: NULL,
! 512: };
! 513: int c;
! 514: for (c = 0; tests[c]; c++){
! 515: char buf[6];
! 516: fail_if(get_mac_addr(tests[c], buf));
! 517: }
! 518: } END_TEST
! 519:
! 520: MY_EXIT_TEST(libnet_init_bad_nolo)
! 521: {
! 522: // It'll only try lo if named interface fails.
! 523: // So by accepting lo, we make sure it doesn't try lo.
! 524: mock_libnet_lo_ok = 1;
! 525: do_libnet_init("bad", 0);
! 526: } END_TEST
! 527:
! 528: MY_EXIT_TEST(libnet_init_null_nolo_nonull)
! 529: {
! 530: mock_libnet_lo_ok = 0;
! 531: mock_libnet_null_ok = 0;
! 532: do_libnet_init(NULL, 0);
! 533: } END_TEST
! 534:
! 535: MYTEST(libnet_init_good)
! 536: {
! 537: mock_libnet_lo_ok = 0; // Don't even try falling back to lo.
! 538: do_libnet_init("good", 0);
! 539: fail_if(libnet == NULL);
! 540: } END_TEST
! 541:
! 542: MYTEST(libnet_init_null_nolo)
! 543: {
! 544: mock_libnet_lo_ok = 0;
! 545: mock_libnet_null_ok = 1;
! 546: do_libnet_init(NULL, 0);
! 547: fail_if(libnet == NULL);
! 548: } END_TEST
! 549:
! 550: static Suite*
! 551: arping_suite(void)
! 552: {
! 553: int c;
! 554: Suite* s = suite_create("Arping");
! 555:
! 556: //tcase_add_checked_fixture (tc_core, setup, teardown);
! 557: for (c = 0; c < numtests; c++) {
! 558: TCase *tc_core = tcase_create(test_registry[c].name);
! 559: tcase_add_test(tc_core, test_registry[c].fn);
! 560: suite_add_tcase(s, tc_core);
! 561: }
! 562: for (c = 0; c < num_exit_tests; c++) {
! 563: TCase *tc_core = tcase_create(test_exit_registry[c].name);
! 564: tcase_add_exit_test(tc_core, test_exit_registry[c].fn, 1);
! 565: suite_add_tcase(s, tc_core);
! 566: }
! 567: return s;
! 568: }
! 569:
! 570: int
! 571: main()
! 572: {
! 573: int number_failed;
! 574: Suite *s = arping_suite();
! 575: SRunner *sr = srunner_create (s);
! 576: srunner_run_all (sr, CK_NORMAL);
! 577: number_failed = srunner_ntests_failed (sr);
! 578: srunner_free (sr);
! 579: return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
! 580: }
! 581: /* ---- Emacs Variables ----
! 582: * Local Variables:
! 583: * c-basic-offset: 8
! 584: * indent-tabs-mode: nil
! 585: * End:
! 586: */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>