Annotation of embedaddon/arping/src/arping.c, revision 1.1.1.1.2.1
1.1 misho 1: /** arping/src/arping.c
2: *
3: * arping
4: *
5: * By Thomas Habets <thomas@habets.pp.se>
6: *
7: * ARP 'ping' utility
8: *
9: * Broadcasts a who-has ARP packet on the network and prints answers.
10: * *VERY* useful when you are trying to pick an unused IP for a net that
11: * you don't yet have routing to. Then again, if you have no idea what I'm
12: * talking about then you prolly don't need it.
13: *
14: * Also finds out IP of specified MAC
15: *
16: */
17: /*
18: * Copyright (C) 2000-2010 Thomas Habets <thomas@habets.pp.se>
19: *
20: * This library is free software; you can redistribute it and/or
21: * modify it under the terms of the GNU General Public
22: * License as published by the Free Software Foundation; either
23: * version 2 of the License, or (at your option) any later version.
24: *
25: * This library is distributed in the hope that it will be useful,
26: * but WITHOUT ANY WARRANTY; without even the implied warranty of
27: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
28: * General Public License for more details.
29: *
30: * You should have received a copy of the GNU General Public License along
31: * with this program; if not, write to the Free Software Foundation, Inc.,
32: * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
33: */
34: #if HAVE_CONFIG_H
35: #include "config.h"
36: #endif
37:
38: #include <stdio.h>
39: #include <stdlib.h>
40: #include <poll.h>
41:
42: #if HAVE_UNISTD_H
43: #include <unistd.h>
44: #endif
45:
46: #if HAVE_STDINT_H
47: #include <stdint.h>
48: #endif
49:
50: #if HAVE_INTTYPES_H
51: #include <inttypes.h>
52: #endif
53:
54: #if HAVE_SYS_TIME_H
55: #include <sys/time.h>
56: #endif
57:
58: #if HAVE_SYS_TYPES_H
59: #include <sys/types.h>
60: #endif
61:
62: #if HAVE_SYS_SOCKET_H
63: #include <sys/socket.h>
64: #endif
65:
66: #if HAVE_NETINET_IN_H
67: #include <netinet/in.h>
68: #endif
69:
70: #if HAVE_ARPA_INET_H
71: #include <arpa/inet.h>
72: #endif
73:
74: #if HAVE_LIBNET_H
75: #include <libnet.h>
76: #endif
77:
1.1.1.1.2.1! misho 78: #if HAVE_NET_BPF_H
! 79: #include <net/bpf.h>
! 80: #endif
! 81:
1.1 misho 82: #if HAVE_WIN32_LIBNET_H
83: #include <win32/libnet.h>
84: #endif
85: #include <pcap.h>
86:
87: #ifndef ETH_ALEN
88: #define ETH_ALEN 6
89: #endif
90:
91: #ifndef IP_ALEN
92: #define IP_ALEN 4
93: #endif
94:
95: #ifndef WIN32
96: #define WIN32 0
97: #endif
98:
99: /**
100: * OS-specific interface finding using routing table. See findif_*.c
101: */
102: const char *
103: arping_lookupdev(const char *ifname,
104: uint32_t srcip,
105: uint32_t dstip,
106: char *ebuf);
107:
108: static const char *version = VERSION; /* from autoconf */
109:
110: static libnet_t *libnet = 0;
111:
112: static struct timeval lastpacketsent;
113:
114: uint32_t srcip, dstip;
115:
116: static int beep = 0;
117: static int reverse_beep = 0;
118: static int verbose = 0;
119: static int alsototal = 0;
120: /*static int pingmac = 0; */
121: static int finddup = 0;
122: static int dupfound = 0;
123: static unsigned int numsent = 0;
124: static unsigned int numrecvd = 0;
125: static unsigned int numdots = 0;
126: static int addr_must_be_same = 0;
127: /* RAWRAW is RAW|RRAW */
128: static enum { NORMAL, QUIET, RAW, RRAW, RAWRAW, DOT } display = NORMAL;
129: static char *target = "huh? bug in arping?";
130: static uint8_t ethnull[ETH_ALEN];
131: static uint8_t ethxmas[ETH_ALEN];
132: static char srcmac[ETH_ALEN];
133: static char dstmac[ETH_ALEN];
134: static char lastreplymac[ETH_ALEN];
135:
136: /* doesn't need to be volatile */
137: volatile int time_to_die = 0;
138:
139: /**
140: *
141: */
142: static void
143: count_missing_dots()
144: {
145: while (numsent > numdots) {
146: putchar('.');
147: numdots++;
148: }
149: }
150:
151: /**
152: *
153: */
154: void
155: do_libnet_init(const char *ifname)
156: {
157: char ebuf[LIBNET_ERRBUF_SIZE];
158: if (verbose > 1) {
159: printf("libnet_init(%s)\n", ifname?ifname:"<null>");
160: }
161: if (libnet) {
162: /* prolly going to switch interface from temp to real */
163: libnet_destroy(libnet);
164: libnet = 0;
165: }
166: if (getuid() && geteuid()) {
167: fprintf(stderr, "arping: must run as root\n");
168: exit(1);
169: }
170:
171: if (!(libnet = libnet_init(LIBNET_LINK,
172: (char*)ifname,
173: ebuf))) {
174: fprintf(stderr, "arping: libnet_init(): %s\n", ebuf);
175: exit(1);
176: }
177: }
178:
179: /**
180: *
181: */
182: const char *
183: arping_lookupdev_default(const char *ifname,
184: uint32_t srcip, uint32_t dstip,
185: char *ebuf)
186: {
187: #if WIN32
188: WCHAR buf[LIBNET_ERRBUF_SIZE + PCAP_ERRBUF_SIZE];
189: WCHAR* ret = (WCHAR*)pcap_lookupdev((char*)buf);
190: if (ret != NULL) {
191: wcstombs(ebuf, ret, LIBNET_ERRBUF_SIZE + PCAP_ERRBUF_SIZE);
192: return ebuf;
193: }
194: return NULL;
195: #else
196: return pcap_lookupdev(ebuf);
197: #endif
198: }
199:
200: #if WIN32
201: static BOOL WINAPI arping_console_ctrl_handler(DWORD dwCtrlType)
202: {
203: if(verbose) {
204: printf("arping_console_ctrl_handler( %d )\n", (int)dwCtrlType);
205: }
206: time_to_die = 1;
207:
208: #if 0
209: /* if SetConsoleCtrlHandler() does what I think, this isn't needed */
210: if (display == NORMAL) {
211: printf("\n--- %s statistics ---\n"
212: "%d packets transmitted, %d packets received, %3.0f%% "
213: "unanswered\n",target,numsent,numrecvd,
214: 100.0 - 100.0 * (float)(numrecvd)/(float)numsent);
215: }
216: #endif
217: return TRUE;
218: }
219: #endif
220:
221:
222: /**
223: *
224: */
225: static void sigint(int i)
226: {
227: time_to_die = 1;
228: }
229:
230: /**
231: *
232: */
233: static void
234: extended_usage()
235: {
236: printf("\nOptions:\n");
237: printf("\n"
238: " -0 Use this option to ping with source IP address 0.0.0.0. Use this\n"
239: " when you haven't configured your interface yet. Note that this\n"
240: " may get the MAC-ping unanswered. This is an alias for -S\n"
241: " 0.0.0.0.\n"
242: " -a Audiable ping.\n"
243: " -A Only count addresses matching requested address (This *WILL*\n"
244: " break most things you do. Only useful if you are arpinging many\n"
245: " hosts at once. See arping-scan-net.sh for an example).\n"
246: " -b Like -0 but source broadcast source address (255.255.255.255).\n"
247: " Note that this may get the arping unanswered since it's not nor-\n"
248: " mal behavior for a host.\n"
249: " -B Use instead of host if you want to address 255.255.255.255.\n"
250: " -c count\n"
251: " Only send count requests.\n"
252: " -d Find duplicate replies. Exit with 1 if there are "
253: "answers from\n"
254: " two different MAC addresses.\n"
255: " -D Display answers as dots and missing packets as exclamation points.\n"
256: " -e Like -a but beep when there is no reply.\n"
257: " -F Don't try to be smart about the interface name. (even if this\n"
258: " switch is not given, -i overrides smartness)\n"
259: " -h Displays a help message and exits.\n"
260: " -i interface\n"
261: " Use the specified interface.\n"
262: " -q Does not display messages, except error messages.\n"
263: " -r Raw output: only the MAC/IP address is displayed for each reply.\n"
264: " -R Raw output: Like -r but shows \"the other one\", can be combined\n"
265: " with -r.\n"
266: " -s MAC Set source MAC address. You may need to use -p with this.\n"
267: " -S IP Like -b and -0 but with set source address. Note that this may\n"
268: " get the arping unanswered if the target does not have routing to\n"
269: " the IP. If you don't own the IP you are using, you may need to\n"
270: " turn on promiscious mode on the interface (with -p). With this\n"
271: " switch you can find out what IP-address a host has without tak-\n"
272: " ing an IP-address yourself.\n"
273: " -t MAC Set target MAC address to use when pinging IP address.\n"
274: " -T IP Use -T as target address when pinging MACs that won't respond to\n"
275: " a broadcast ping but perhaps to a directed broadcast.\n"
276: " Example:\n"
277: " To check the address of MAC-A, use knowledge of MAC-B and IP-B.\n"
278: " $ arping -S <IP-B> -s <MAC-B> -p <MAC-A>\n"
279: " -p Turn on promiscious mode on interface, use this if you don't\n"
280: " \"own\" the MAC address you are using.\n"
281: " -u Show index=received/sent instead of just index=received when\n"
282: " pinging MACs.\n"
283: " -v Verbose output. Use twice for more messages.\n"
284: " -w Time to wait between pings, in microseconds.\n");
285: printf("Report bugs to: thomas@habets.pp.se\n"
286: "Arping home page: <http://www.habets.pp.se/synscan/>\n"
287: "Development repo: http://github.com/ThomasHabets/arping\n");
288: }
289:
290: /**
291: *
292: */
293: static void
294: standard_usage()
295: {
296: printf("ARPing %s, by Thomas Habets <thomas@habets.pp.se>\n",
297: version);
298: printf("usage: arping [ -0aAbdDeFpqrRuv ] [ -w <us> ] "
299: "[ -S <host/ip> ]\n"
300: " "
301: "[ -T <host/ip ] "
302: "[ -s <MAC> ] [ -t <MAC> ] [ -c <count> ]\n"
303: " "
304: "[ -i <interface> ] "
305: "<host/ip/MAC | -B>\n");
306: }
307:
308: /**
309: *
310: */
311: static void
312: usage(int ret)
313: {
314: standard_usage();
315: if (WIN32) {
316: extended_usage();
317: } else {
318: printf("For complete usage info, use --help"
319: " or check the manpage.\n");
320: }
321: exit(ret);
322: }
323:
324: /**
325: * It was unclear from msdn.microsoft.com if their scanf() supported
326: * [0-9a-fA-F], so I'll stay away from it.
327: */
328: static int is_mac_addr(const char *p)
329: {
330: /* cisco-style */
331: if (3*5-1 == strlen(p)) {
332: unsigned int c;
333: for (c = 0; c < strlen(p); c++) {
334: if ((c % 5) == 4) {
335: if ('.' != p[c]) {
336: goto checkcolon;
337: }
338: } else {
339: if (!isxdigit(p[c])) {
340: goto checkcolon;
341: }
342: }
343: }
344: return 1;
345: }
346: /* windows-style */
347: if (6*3-1 == strlen(p)) {
348: unsigned int c;
349: for (c = 0; c < strlen(p); c++) {
350: if ((c % 3) == 2) {
351: if ('-' != p[c]) {
352: goto checkcolon;
353: }
354: } else {
355: if (!isxdigit(p[c])) {
356: goto checkcolon;
357: }
358: }
359: }
360: return 1;
361: }
362:
363: checkcolon:
364: /* unix */
365: return strchr(p, ':') ? 1 : 0;
366: }
367:
368: /**
369: * lots of parms since C arrays suck
370: */
371: static int get_mac_addr(const char *in,
372: unsigned int *n0,
373: unsigned int *n1,
374: unsigned int *n2,
375: unsigned int *n3,
376: unsigned int *n4,
377: unsigned int *n5)
378: {
379: if (6 == sscanf(in, "%x:%x:%x:%x:%x:%x",n0,n1,n2,n3,n4,n5)) {
380: return 1;
381: } else if(6 == sscanf(in, "%2x%x.%2x%x.%2x%x",n0,n1,n2,n3,n4,n5)) {
382: return 1;
383: } else if(6 == sscanf(in, "%x-%x-%x-%x-%x-%x",n0,n1,n2,n3,n4,n5)) {
384: return 1;
385: }
386: return 0;
387: }
388:
389: /**
390: * as always, the answer is 42
391: *
392: * in this case the question is how many bytes buf needs to be.
393: * Assuming a 33 byte max %d
394: *
395: * Still, I'm using at least 128bytes below
396: *
397: * (because snprintf() sadly isn't as portable, that's why)
398: */
399: static char *tv2str(const struct timeval *tv, const struct timeval *tv2,
400: char *buf)
401: {
402: double f,f2;
403: int exp = 0;
404:
405: f = tv->tv_sec + (double)tv->tv_usec / 1000000;
406: f2 = tv2->tv_sec + (double)tv2->tv_usec / 1000000;
407: f = (f2 - f) * 1000000;
408: while (f > 1000) {
409: exp+= 3;
410: f /= 1000;
411: }
412: switch (exp) {
413: case 0:
414: sprintf(buf, "%.3f usec", f);
415: break;
416: case 3:
417: sprintf(buf, "%.3f msec", f);
418: break;
419: case 6:
420: sprintf(buf, "%.3f sec", f);
421: break;
422: case 9:
423: sprintf(buf, "%.3f sec", f*1000);
424: break;
425: default:
426: /* huh, uh, huhuh */
427: sprintf(buf, "%.3fe%d sec", f, exp-6);
428: }
429: return buf;
430: }
431:
432:
433:
434: /** Send directed IPv4 ICMP echo request.
435: *
436: * \param srcmac Source MAC. From -s switch or autodetected
437: * \param dstmac Destination/target MAC. Target command line.
438: * \param srcip From -S switch or autodetected
439: * \param dstip From -D switch, or 255.255.255.255
440: * \param id IP id
441: * \param seq Ping seq
442: */
443: static void
444: pingmac_send(uint8_t *srcmac, uint8_t *dstmac,
445: uint32_t srcip, uint32_t dstip,
446: uint16_t id, uint16_t seq)
447: {
448: static libnet_ptag_t icmp = 0, ipv4 = 0,eth=0;
449: int c;
450:
451: if (-1 == (icmp = libnet_build_icmpv4_echo(ICMP_ECHO, /* type */
452: 0, /* code */
453: 0, /* checksum */
454: id, /* id */
455: seq, /* seq */
456: NULL, /* payload */
457: 0, /* payload len */
458: libnet,
459: icmp))) {
460: fprintf(stderr, "libnet_build_icmpv4_echo(): %s\n",
461: libnet_geterror(libnet));
462: sigint(0);
463: }
464:
465: if (-1==(ipv4 = libnet_build_ipv4(LIBNET_IPV4_H
466: + LIBNET_ICMPV4_ECHO_H + 0,
467: 0, /* ToS */
468: id, /* id */
469: 0, /* frag */
470: 64, /* ttl */
471: IPPROTO_ICMP,
472: 0, /* checksum */
473: srcip,
474: dstip,
475: NULL, /* payload */
476: 0,
477: libnet,
478: ipv4))) {
479: fprintf(stderr, "libnet_build_ipv4(): %s\n",
480: libnet_geterror(libnet));
481: sigint(0);
482: }
483: if (-1 == (eth = libnet_build_ethernet(dstmac,
484: srcmac,
485: ETHERTYPE_IP,
486: NULL,
487: 0,
488: libnet,
489: eth))) {
490: fprintf(stderr, "libnet_build_ethernet(): %s\n",
491: libnet_geterror(libnet));
492: sigint(0);
493: }
494: if(verbose>1) {
495: if (-1 == gettimeofday(&lastpacketsent, NULL)) {
496: fprintf(stderr, "arping: gettimeofday(): %s\n",
497: strerror(errno));
498: sigint(0);
499: }
500: printf("arping: sending packet at time %d %d\n",
501: lastpacketsent.tv_sec,
502: lastpacketsent.tv_usec);
503: }
504: if (-1 == (c = libnet_write(libnet))) {
505: fprintf(stderr, "arping: libnet_write(): %s\n",
506: libnet_geterror(libnet));
507: sigint(0);
508: }
509: if (-1 == gettimeofday(&lastpacketsent, NULL)) {
510: fprintf(stderr, "arping: gettimeofday(): %s\n",
511: strerror(errno));
512: sigint(0);
513: }
514: numsent++;
515: }
516:
517: /** Send ARP who-has.
518: *
519: * \param srcmac -s or autodetected
520: * \param dstmac -t or ff:ff:ff:ff:ff:ff
521: * \param srcip -S or autodetected
522: * \param dstip -T or or cmdline
523: *
524: */
525: static void
526: pingip_send(uint8_t *srcmac, uint8_t *dstmac,
527: uint32_t srcip, uint32_t dstip)
528: {
529: static libnet_ptag_t arp=0,eth=0;
530: if (-1 == (arp = libnet_build_arp(ARPHRD_ETHER,
531: ETHERTYPE_IP,
532: ETH_ALEN,
533: IP_ALEN,
534: ARPOP_REQUEST,
535: srcmac,
536: (uint8_t*)&srcip,
537: ethnull,
538: (uint8_t*)&dstip,
539: NULL,
540: 0,
541: libnet,
542: arp))) {
543: fprintf(stderr, "arping: libnet_build_arp(): %s\n",
544: libnet_geterror(libnet));
545: sigint(0);
546: }
547: if (-1 == (eth = libnet_build_ethernet(dstmac,
548: srcmac,
549: ETHERTYPE_ARP,
550: NULL,
551: 0,
552: libnet,
553: eth))) {
554: fprintf(stderr, "arping: libnet_build_ethernet(): %s\n",
555: libnet_geterror(libnet));
556: sigint(0);
557: }
558: if(verbose>1) {
559: if (-1 == gettimeofday(&lastpacketsent, NULL)) {
560: fprintf(stderr, "arping: gettimeofday(): %s\n",
561: strerror(errno));
562: sigint(0);
563: }
564: printf("arping: sending packet at time %d %d\n",
565: lastpacketsent.tv_sec,
566: lastpacketsent.tv_usec);
567: }
568: if (-1 == libnet_write(libnet)) {
569: fprintf(stderr, "arping: libnet_write(): %s\n",
570: libnet_geterror(libnet));
571: sigint(0);
572: }
573: if (-1 == gettimeofday(&lastpacketsent, NULL)) {
574: fprintf(stderr, "arping: gettimeofday(): %s\n",
575: strerror(errno));
576: sigint(0);
577: }
578: numsent++;
579: }
580:
581: /** handle incoming packet when pinging an IP address.
582: *
583: * \param h packet metadata
584: * \param packet packet data
585: */
586: static void
587: pingip_recv(const char *unused, struct pcap_pkthdr *h,
588: uint8_t *packet)
589: {
590: struct libnet_802_3_hdr *heth;
591: struct libnet_arp_hdr *harp;
592: struct timeval arrival;
593: int c;
594:
595: if(verbose>2) {
596: printf("arping: received response for ip ping\n");
597: }
598:
599: if (-1 == gettimeofday(&arrival, NULL)) {
600: fprintf(stderr, "arping: gettimeofday(): %s\n",
601: strerror(errno));
602: sigint(0);
603: }
604: heth = (void*)packet;
605: harp = (void*)((char*)heth + LIBNET_ETH_H);
606:
607: if ((htons(harp->ar_op) == ARPOP_REPLY)
608: && (htons(harp->ar_pro) == ETHERTYPE_IP)
609: && (htons(harp->ar_hrd) == ARPHRD_ETHER)) {
610: uint32_t ip;
611: memcpy(&ip, (char*)harp + harp->ar_hln
612: + LIBNET_ARP_H,4);
613: if (addr_must_be_same
614: && (memcmp((u_char*)harp+sizeof(struct libnet_arp_hdr),
615: dstmac, ETH_ALEN))) {
616: return;
617: }
618: if (dstip == ip) {
619: switch(display) {
620: case DOT:
621: numdots++;
622: count_missing_dots();
623: putchar('!');
624: break;
625: case NORMAL: {
626: char buf[128];
627: printf("%d bytes from ", h->len);
628: for (c = 0; c < 6; c++) {
629: printf("%.2x%c", heth->_802_3_shost[c],
630: (c<5)?':':' ');
631: }
632:
633: printf("(%s): index=%d",
634: libnet_addr2name4(ip,0),
635: numrecvd);
636: if (alsototal) {
637: printf("/%u", numsent-1);
638: }
639: printf(" time=%s",
640: tv2str(&lastpacketsent,
641: &arrival,buf));
642: break; }
643: case QUIET:
644: break;
645: case RAWRAW:
646: for (c = 0; c < 6; c++) {
647: printf("%.2x%c", heth->_802_3_shost[c],
648: (c<5)?':':' ');
649: }
650: printf("%s", libnet_addr2name4(ip,0));
651: break;
652: case RRAW:
653: printf("%s", libnet_addr2name4(ip,0));
654: break;
655: case RAW:
656: for (c = 0; c < 6; c++) {
657: printf("%.2x%s", heth->_802_3_shost[c],
658: (c<5)?":":"");
659: }
660: break;
661: default:
662: fprintf(stderr, "arping: can't happen!\n");
663: }
664:
665: switch (display) {
666: case QUIET:
667: case DOT:
668: break;
669: default:
670: if (beep) {
671: printf("\a");
672: }
673: printf("\n");
674: }
675: if (numrecvd) {
676: if (memcmp(lastreplymac,
677: heth->_802_3_shost, ETH_ALEN)) {
678: dupfound = 1;
679: }
680: }
681: memcpy(lastreplymac, heth->_802_3_shost, ETH_ALEN);
682:
683: numrecvd++;
684: }
685: }
686: }
687:
688: /** handle incoming packet when pinging an MAC address.
689: *
690: * \param h packet metadata
691: * \param packet packet data
692: */
693: static void
694: pingmac_recv(const char *unused, struct pcap_pkthdr *h,
695: uint8_t *packet)
696: {
697: struct libnet_802_3_hdr *heth;
698: struct libnet_ipv4_hdr *hip;
699: struct libnet_icmpv4_hdr *hicmp;
700: struct timeval arrival;
701: int c;
702:
703: if(verbose>2) {
704: printf("arping: received response for mac ping\n");
705: }
706:
707: if (-1 == gettimeofday(&arrival, NULL)) {
708: fprintf(stderr, "arping: gettimeofday(): %s\n",
709: strerror(errno));
710: sigint(0);
711: }
712:
713: heth = (void*)packet;
714: hip = (void*)((char*)heth + LIBNET_ETH_H);
715: hicmp = (void*)((char*)hip + LIBNET_IPV4_H);
716:
717: if ((htons(hicmp->icmp_type) == ICMP_ECHOREPLY)
718: && ((!memcmp(heth->_802_3_shost, dstmac,ETH_ALEN)
719: || !memcmp(dstmac, ethxmas, ETH_ALEN)))
720: && !memcmp(heth->_802_3_dhost, srcmac, ETH_ALEN)) {
721: if (addr_must_be_same) {
722: uint32_t tmp;
723: memcpy(&tmp, &hip->ip_src, 4);
724: if (dstip != tmp) {
725: return;
726: }
727: }
728: switch(display) {
729: case QUIET:
730: break;
731: case NORMAL: {
732: char buf[128];
733: printf("%d bytes from %s (",h->len,
734: libnet_addr2name4(*(int*)&hip->ip_src, 0));
735: for (c = 0; c < 6; c++) {
736: printf("%.2x%c", heth->_802_3_shost[c],
737: (c<5)?':':')');
738: }
739: printf(": icmp_seq=%d time=%s",
740: htons(hicmp->icmp_seq),tv2str(&lastpacketsent,
741: &arrival,buf));
742: break; }
743: case RAW:
744: printf("%s",
745: libnet_addr2name4(hip->ip_src.s_addr, 0));
746: break;
747: case RRAW:
748: for (c = 0; c < 6; c++) {
749: printf("%.2x%s", heth->_802_3_shost[c],
750: (c<5)?":":"");
751: }
752: break;
753: case RAWRAW:
754: for (c = 0; c < 6; c++) {
755: printf("%.2x%c", heth->_802_3_shost[c],
756: (c<5)?':':' ');
757: }
758: printf("%s",
759: libnet_addr2name4(hip->ip_src.s_addr, 0));
760: break;
761: default:
762: fprintf(stderr, "arping: can't-happen-bug\n");
763: sigint(0);
764: }
765: if (display != QUIET) {
766: printf(beep?"\a\n":"\n");
767: }
768: numrecvd++;
769: }
770: }
771:
772:
773: #if WIN32
774: /**
775: * untested for a long time. Maybe since arping 2.05 or so.
776: */
777: static void
778: ping_recv_win32(pcap_t *pcap,uint32_t packetwait, pcap_handler func)
779: {
780: struct timeval tv,tv2;
781: char done = 0;
782: /* windows won't let us do select() */
783: if (-1 == gettimeofday(&tv2,NULL)) {
784: fprintf(stderr, "arping: gettimeofday(): %s\n",
785: strerror(errno));
786: sigint(0);
787: }
788: while (!done && !time_to_die) {
789: struct pcap_pkthdr *pkt_header;
790: u_char *pkt_data;
791: if (pcap_next_ex(pcap, &pkt_header, &pkt_data) == 1) {
792: func(pcap, pkt_header, pkt_data);
793: }
794: if (-1 == gettimeofday(&tv,NULL)) {
795: fprintf(stderr, "arping: "
796: "gettimeofday(): %s\n",
797: strerror(errno));
798: sigint(0);
799: }
800: /*
801: * setup next timeval, not very exact
802: */
803: tv.tv_sec = (packetwait / 1000000)
804: - (tv.tv_sec - tv2.tv_sec);
805: tv.tv_usec = (packetwait % 1000000)
806: - (tv.tv_usec - tv2.tv_usec);
807: while (tv.tv_usec < 0) {
808: tv.tv_sec--;
809: tv.tv_usec += 1000000;
810: }
811: usleep(10);
812: if (tv.tv_sec < 0) {
813: done=1;
814: }
815: }
816: }
817: #endif
818:
819: /**
820: * while negative microseconds, take from whole seconds.
821: * help function for measuring deltas.
822: */
823: static void
824: fixup_timeval(struct timeval *tv)
825: {
826: while (tv->tv_usec < 0) {
827: tv->tv_sec--;
828: tv->tv_usec += 1000000;
829: }
830: }
831:
832: /**
833: * idiot-proof gettimeofday() wrapper
834: */
835: static void
836: gettv(struct timeval *tv)
837: {
838: if (-1 == gettimeofday(tv,NULL)) {
839: fprintf(stderr, "arping: "
840: "gettimeofday(): %s\n",
841: strerror(errno));
842: sigint(0);
843: }
844: }
845:
846:
847: /**
848: *
849: */
850: static void
851: ping_recv_unix(pcap_t *pcap,uint32_t packetwait, pcap_handler func)
852: {
853: struct timeval tv;
854: struct timeval endtime;
855: char done = 0;
856:
857: gettv(&tv);
858: endtime.tv_sec = tv.tv_sec + (packetwait / 1000000);
859: endtime.tv_usec = tv.tv_usec + (packetwait % 1000000);
860: fixup_timeval(&endtime);
861:
862: int fd;
863:
864: fd = pcap_get_selectable_fd(pcap);
865:
866: for (;!done;) {
867: int trydispatch = 0;
868:
869: gettv(&tv);
870: tv.tv_sec = endtime.tv_sec - tv.tv_sec;
871: tv.tv_usec = endtime.tv_usec - tv.tv_usec;
872: fixup_timeval(&tv);
873: if (tv.tv_sec < 0) {
874: tv.tv_sec = 0;
875: tv.tv_usec = 1;
876: done = 1;
877: }
878: if (time_to_die) {
879: return;
880: }
881:
882: /* try to wait for data */
883: {
884: struct pollfd p;
885: int r;
886: p.fd = fd;
887: p.events = POLLIN | POLLPRI;
888:
889: r = poll(&p, 1, tv.tv_sec * 1000 + tv.tv_usec / 1000);
890: switch (r) {
891: case 0: /* timeout */
892: done = 1;
893: break;
894: case -1: /* error */
895: if (errno != EINTR) {
896: done = 1;
897: sigint(0);
898: fprintf(stderr,
899: "arping: poll() failed: %s\n",
900: strerror(errno));
901: }
902: break;
903: default: /* data returned */
904: trydispatch = 1;
905: break;
906: }
907: }
908:
909: if (trydispatch) {
910: int ret;
911: if (1 != (ret = pcap_dispatch(pcap, 1,
912: func,
913: NULL))) {
914: /* rest, so we don't take 100% CPU... mostly
915: hmm... does usleep() exist everywhere? */
916: usleep(1);
917:
918: /* weird is normal on bsd :) */
919: if (verbose > 3) {
920: fprintf(stderr,
921: "arping: poll says ok, "
922: "pcap_dispatch=%d!\n",
923: ret);
924: }
925: }
926: }
927: }
928: }
929:
930: /**
931: *
932: */
933: static void
934: ping_recv(pcap_t *pcap,uint32_t packetwait, pcap_handler func)
935: {
936: if(verbose>3) {
937: printf("arping: receiving packets...\n");
938: }
939:
940: #if WIN32
941: ping_recv_win32(pcap,packetwait,func);
942: #else
943: ping_recv_unix(pcap,packetwait,func);
944: #endif
945: }
946:
947: /**
948: *
949: */
950: int main(int argc, char **argv)
951: {
952: char ebuf[LIBNET_ERRBUF_SIZE + PCAP_ERRBUF_SIZE];
953: char *cp;
954: int promisc = 0;
955: int srcip_given = 0;
956: int srcmac_given = 0;
957: int dstip_given = 0;
958: const char *ifname = NULL;
959: char *parm;
960: int c;
961: unsigned int maxcount = -1;
962: int dont_use_arping_lookupdev=0;
963: struct bpf_program bp;
964: pcap_t *pcap;
965: static enum { NONE, PINGMAC, PINGIP } mode = NONE;
966: unsigned int packetwait = 1000000;
967:
968: for (c = 1; c < argc; c++) {
969: if (!strcmp(argv[c], "--help")) {
970: standard_usage();
971: extended_usage();
972: exit(0);
973: }
974: }
975:
976: memset(ethnull, 0, ETH_ALEN);
977:
978: srcip = 0;
979: dstip = 0xffffffff;
980: memset(dstmac, 0xff, ETH_ALEN);
981: memset(ethxmas, 0xff, ETH_ALEN);
982:
983: while (EOF!=(c=getopt(argc,argv,"0aAbBc:dDeFhi:I:pqrRs:S:t:T:uvw:"))) {
984: switch(c) {
985: case '0':
986: srcip = 0;
987: srcip_given = 1;
988: break;
989: case 'a':
990: beep = 1;
991: break;
992: case 'A':
993: addr_must_be_same = 1;
994: break;
995: case 'b':
996: srcip = 0xffffffff;
997: srcip_given = 1;
998: break;
999: case 'B':
1000: dstip = 0xffffffff;
1001: dstip_given = 1;
1002: break;
1003: case 'c':
1004: maxcount = atoi(optarg);
1005: break;
1006: case 'd':
1007: finddup = 1;
1008: break;
1009: case 'D':
1010: display = DOT;
1011: break;
1012: case 'e':
1013: reverse_beep = 1;
1014: break;
1015: case 'F':
1016: dont_use_arping_lookupdev=1;
1017: break;
1018: case 'h':
1019: usage(0);
1020: case 'i':
1021: if (strchr(optarg, ':')) {
1022: fprintf(stderr, "arping: If you're trying to "
1023: "feed me an interface alias then you "
1024: "don't really\nknow what this programs"
1025: " does, do you?\nUse -I if you really"
1026: " mean it (undocumented on "
1027: "purpose)\n");
1028: exit(1);
1029: }
1030: case 'I': /* FALL THROUGH */
1031: ifname = optarg;
1032: break;
1033: case 'p':
1034: promisc = 1;
1035: break;
1036: case 'q':
1037: display = QUIET;
1038: break;
1039: case 'r':
1040: display = (display==RRAW)?RAWRAW:RAW;
1041: break;
1042: case 'R':
1043: display = (display==RAW)?RAWRAW:RRAW;
1044: break;
1045: case 's': { /* spoof source MAC */
1046: unsigned int n[6];
1047: if (!get_mac_addr(optarg,
1048: &n[0],&n[1],&n[2],
1049: &n[3],&n[4],&n[5])){
1050: fprintf(stderr, "arping: Weird MAC addr %s\n",
1051: optarg);
1052: exit(1);
1053: }
1054: for (c = 0; c < 6; c++) {
1055: srcmac[c] = n[c] & 0xff;
1056: }
1057: srcmac_given = 1;
1058: break;
1059: }
1060: case 'S': /* set source IP, may be null for don't-know */
1061: do_libnet_init(ifname);
1062: if (-1 == (srcip = libnet_name2addr4(libnet,
1063: optarg,
1064: LIBNET_RESOLVE))){
1065: fprintf(stderr, "arping: Can't resolve %s, or "
1066: "%s is broadcast. If it is, use -b"
1067: " instead of -S\n", optarg,optarg);
1068: exit(1);
1069: }
1070: srcip_given = 1;
1071: break;
1072: case 't': { /* set taget mac */
1073: unsigned int n[6];
1074: if (mode == PINGMAC) {
1075: fprintf(stderr, "arping: -t can only be used "
1076: "in IP ping mode\n");
1077: exit(1);
1078: }
1079: if (!get_mac_addr(optarg,
1080: &n[0],&n[1],&n[2],
1081: &n[3],&n[4],&n[5])){
1082: fprintf(stderr, "Illegal MAC addr %s\n",
1083: optarg);
1084: exit(1);
1085: }
1086: for (c = 0; c < 6; c++) {
1087: dstmac[c] = n[c] & 0xff;
1088: }
1089: mode = PINGIP;
1090: break;
1091: }
1092: case 'T': /* set destination IP */
1093: if (mode == PINGIP) {
1094: fprintf(stderr, "arping: -T can only be used "
1095: "in MAC ping mode\n");
1096: exit(1);
1097: }
1098: do_libnet_init(ifname);
1099: if (-1 == (dstip = libnet_name2addr4(libnet,
1100: optarg,
1101: LIBNET_RESOLVE))){
1102: fprintf(stderr,"arping: Can't resolve %s, or "
1103: "%s is broadcast. If it is, use -B "
1104: "instead of -T\n",optarg,optarg);
1105: exit(1);
1106: }
1107: mode = PINGMAC;
1108: break;
1109: case 'u':
1110: alsototal = 1;
1111: break;
1112: case 'v':
1113: verbose++;
1114: break;
1115: case 'w':
1116: packetwait = (unsigned)atoi(optarg);
1117: break;
1118: default:
1119: usage(1);
1120: }
1121: }
1122:
1123: if (display == DOT) {
1124: setvbuf(stdout, NULL, _IONBF, 0);
1125: }
1126:
1127: if (finddup && maxcount == -1) {
1128: maxcount = 3;
1129: }
1130:
1131: parm = (optind < argc) ? argv[optind] : NULL;
1132:
1133: /* default to own IP address when doing -d */
1134: if (finddup && !parm) {
1135: dstip_given = 1;
1136: do_libnet_init(ifname);
1137: dstip = libnet_get_ipaddr4(libnet);
1138: if (verbose) {
1139: printf("defaulting to checking dup for %s\n",
1140: libnet_addr2name4(dstip, 0));
1141: }
1142: }
1143:
1144: /*
1145: * Handle dstip_given instead of ip address after parms (-B really)
1146: */
1147: if (mode == NONE) {
1148: if (optind + 1 == argc) {
1149: mode = is_mac_addr(parm)?PINGMAC:PINGIP;
1150: } else if (dstip_given) {
1151: mode = PINGIP;
1152: do_libnet_init(ifname);
1153: parm = strdup(libnet_addr2name4(dstip,0));
1154: if (!parm) {
1155: fprintf(stderr, "arping: out of mem\n");
1156: exit(1);
1157: }
1158: }
1159: }
1160:
1161: if (!parm) {
1162: usage(1);
1163: }
1164:
1165: /*
1166: *
1167: */
1168: if (mode == NONE) {
1169: usage(1);
1170: }
1171:
1172: /*
1173: * libnet init (may be done already for resolving)
1174: */
1175: do_libnet_init(ifname);
1176:
1177: /*
1178: * Make sure dstip and parm like eachother
1179: */
1180: if (mode == PINGIP && (!dstip_given)) {
1181: if (is_mac_addr(parm)) {
1182: fprintf(stderr, "arping: Options given only apply to "
1183: "IP ping, but MAC address given as argument"
1184: "\n");
1185: exit(1);
1186: }
1187: if (-1 == (dstip = libnet_name2addr4(libnet,
1188: parm,
1189: LIBNET_RESOLVE))) {
1190: fprintf(stderr, "arping: Can't resolve %s\n", parm);
1191: exit(1);
1192: }
1193: parm = strdup(libnet_addr2name4(dstip,0));
1194: }
1195:
1196: /*
1197: * parse parm into dstmac
1198: */
1199: if (mode == PINGMAC) {
1200: unsigned int n[6];
1201: if (optind + 1 != argc) {
1202: usage(1);
1203: }
1204: if (!is_mac_addr(parm)) {
1205: fprintf(stderr, "arping: Options given only apply to "
1206: "MAC ping, but no MAC address given as "
1207: "argument\n");
1208: exit(1);
1209: }
1210: if (!get_mac_addr(argv[optind],
1211: &n[0],&n[1],&n[2],
1212: &n[3],&n[4],&n[5])) {
1213: fprintf(stderr, "arping: Illegal mac addr %s\n",
1214: argv[optind]);
1215: return 1;
1216: }
1217: for (c = 0; c < 6; c++) {
1218: dstmac[c] = n[c] & 0xff;
1219: }
1220: }
1221:
1222: target = parm;
1223: /*
1224: * Argument processing done, parameters considered sane below
1225: */
1226:
1227: /*
1228: * Get some good iface.
1229: */
1230: if (!ifname) {
1231: if (dont_use_arping_lookupdev) {
1232: ifname = arping_lookupdev_default(ifname,
1233: srcip,dstip,ebuf);
1234: } else {
1235: ifname = arping_lookupdev(ifname,srcip,dstip,ebuf);
1236: }
1237: if (!ifname) {
1238: fprintf(stderr, "arping: arping_lookupdev(): %s\n",
1239: ebuf);
1240: exit(1);
1241: }
1242: /* FIXME: check for other probably-not interfaces */
1243: if (!strcmp(ifname, "ipsec")
1244: || !strcmp(ifname,"lo")) {
1245: fprintf(stderr, "arping: Um.. %s looks like the wrong "
1246: "interface to use. Is it? "
1247: "(-i switch)\n", ifname);
1248: fprintf(stderr, "arping: using it anyway this time\n");
1249: }
1250: }
1251:
1252: /*
1253: * Init libnet again, because we now know the interface name.
1254: * We should know it by know at least
1255: */
1256: do_libnet_init(ifname);
1257:
1258: /*
1259: * pcap init
1260: */
1261: if (!(pcap = pcap_open_live((char*)ifname, 100, promisc, 10, ebuf))) {
1262: fprintf(stderr, "arping: pcap_open_live(): %s\n",ebuf);
1263: exit(1);
1264: }
1265: if (pcap_setnonblock(pcap, 1, ebuf)) {
1266: fprintf(stderr, "arping: pcap_set_nonblock(): %s\n", ebuf);
1267: exit(1);
1268: }
1269: if (verbose) {
1270: printf("pcap_get_selectable(): %d\n",
1271: pcap_get_selectable_fd(pcap));
1272: }
1273:
1274: #if HAVE_NET_BPF_H
1275: {
1276: uint32_t on = 1;
1277: if (0 < (ioctl(pcap_fileno(pcap), BIOCIMMEDIATE,
1278: &on))) {
1279: fprintf(stderr, "arping: ioctl(fd,BIOCIMMEDIATE, 1) "
1280: "failed, continuing anyway, YMMV: %s\n",
1281: strerror(errno));
1282: }
1283: }
1284: #endif
1285:
1286: if (mode == PINGIP) {
1287: /* FIXME: better filter with addresses? */
1288: if (-1 == pcap_compile(pcap, &bp, "arp", 0,-1)) {
1289: fprintf(stderr, "arping: pcap_compile(): error\n");
1290: exit(1);
1291: }
1292: } else { /* ping mac */
1293: /* FIXME: better filter with addresses? */
1294: if (-1 == pcap_compile(pcap, &bp, "icmp", 0,-1)) {
1295: fprintf(stderr, "arping: pcap_compile(): error\n");
1296: exit(1);
1297: }
1298: }
1299: if (-1 == pcap_setfilter(pcap, &bp)) {
1300: fprintf(stderr, "arping: pcap_setfilter(): error\n");
1301: exit(1);
1302: }
1303:
1304: /*
1305: * final init
1306: */
1307: if (!srcmac_given) {
1308: if (!(cp = (char*)libnet_get_hwaddr(libnet))) {
1309: fprintf(stderr, "arping: libnet_get_hwaddr(): %s\n",
1310: libnet_geterror(libnet));
1311: exit(1);
1312: }
1313: memcpy(srcmac, cp, ETH_ALEN);
1314: }
1315: if (!srcip_given) {
1316: if (-1 == (srcip = libnet_get_ipaddr4(libnet))) {
1317: fprintf(stderr, "arping: libnet_get_ipaddr4(libnet): "
1318: "%s\n", libnet_geterror(libnet));
1319: exit(1);
1320: }
1321: }
1322: #if WIN32
1323: /* SetConsoleCtrlHandler(NULL, TRUE); */
1324: SetConsoleCtrlHandler(arping_console_ctrl_handler, TRUE);
1325: #else
1326: signal(SIGINT, sigint);
1327: #endif
1328:
1329: if (verbose) {
1330: printf("This box: Interface: %s IP: %s MAC address: ",
1331: ifname, libnet_addr2name4(libnet_get_ipaddr4(libnet),
1332: 0));
1333: for (c = 0; c < ETH_ALEN - 1; c++) {
1334: printf("%.2x:", (uint8_t)srcmac[c]);
1335: }
1336: printf("%.2x\n", (uint8_t)srcmac[ETH_ALEN - 1]);
1337: }
1338:
1339:
1340: if (display == NORMAL) {
1341: printf("ARPING %s\n", parm);
1342: }
1343:
1344: /*
1345: * let's roll
1346: */
1347: if (mode == PINGIP) {
1348: unsigned int c;
1349: unsigned int r;
1350: for (c = 0; c < maxcount && !time_to_die; c++) {
1351: pingip_send(srcmac, dstmac, srcip, dstip);
1352: r = numrecvd;
1353: ping_recv(pcap,packetwait,
1354: (pcap_handler)pingip_recv);
1355: if (reverse_beep && !time_to_die && (r == numrecvd)) {
1356: printf("\a");
1357: fflush(stdout);
1358: }
1359: }
1360: } else { /* PINGMAC */
1361: unsigned int c;
1362: unsigned int r;
1363: for (c = 0; c < maxcount && !time_to_die; c++) {
1364: pingmac_send(srcmac, dstmac, srcip, dstip, rand(), c);
1365: r = numrecvd;
1366: ping_recv(pcap,packetwait,
1367: (pcap_handler)pingmac_recv);
1368: if (reverse_beep && !time_to_die && (r == numrecvd)) {
1369: printf("\a");
1370: fflush(stdout);
1371: }
1372: }
1373: }
1374: if (display == DOT) {
1375: count_missing_dots();
1376: printf("\t%3.0f%% packet loss\n",
1377: 100.0 - 100.0 * (float)(numrecvd)/(float)numsent);
1378: } else if (display == NORMAL) {
1379: float succ;
1380: succ = 100.0 - 100.0 * (float)(numrecvd)/(float)numsent;
1381: printf("\n--- %s statistics ---\n"
1382: "%d packets transmitted, "
1383: "%d packets received, "
1384: "%3.0f%% "
1385: "unanswered (%d extra)\n",
1386: target,numsent,numrecvd,
1387: (succ < 0.0) ? 0.0 : succ,
1388: (succ < 0.0) ? (numrecvd - numsent): 0);
1389: }
1390:
1391: if (finddup) {
1392: return dupfound;
1393: } else {
1394: return !numrecvd;
1395: }
1396: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>