Annotation of embedaddon/mtr/mtr.c, revision 1.1.1.1
1.1 misho 1: /*
2: mtr -- a network diagnostic tool
3: Copyright (C) 1997,1998 Matt Kimball
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 version 2 as
7: published by the Free Software Foundation.
8:
9: This program is distributed in the hope that it will be useful,
10: but WITHOUT ANY WARRANTY; without even the implied warranty of
11: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12: GNU General Public License for more details.
13:
14: You should have received a copy of the GNU General Public License
15: along with this program; if not, write to the Free Software
16: Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17: */
18:
19: #include <sys/types.h>
20: #include <config.h>
21: #include <netdb.h>
22: #include <stdio.h>
23: #include <stdlib.h>
24: #include <string.h>
25: #include <netinet/in.h>
26: #include <sys/socket.h>
27: #include <unistd.h>
28: #include <strings.h>
29: #include <time.h>
30: #include <errno.h>
31: #include <string.h>
32: #include <ctype.h>
33: #include <assert.h>
34: #include <fcntl.h>
35: #include <sys/stat.h>
36:
37: #include "mtr.h"
38: #include "mtr-curses.h"
39: #include "getopt.h"
40: #include "display.h"
41: #include "dns.h"
42: #include "report.h"
43: #include "net.h"
44: #ifndef NO_IPINFO
45: #include "asn.h"
46: #endif
47: #include "version.h"
48:
49:
50: #ifdef ENABLE_IPV6
51: #define DEFAULT_AF AF_UNSPEC
52: #else
53: #define DEFAULT_AF AF_INET
54: #endif
55:
56:
57: #ifdef NO_HERROR
58: #define herror(str) fprintf(stderr, str ": error looking up \"%s\"\n", Hostname);
59: #endif
60:
61:
62: int DisplayMode;
63: int display_mode;
64: int Interactive = 1;
65: int PrintVersion = 0;
66: int PrintHelp = 0;
67: int MaxPing = 10;
68: int ForceMaxPing = 0;
69: float WaitTime = 1.0;
70: char *Hostname = NULL;
71: char *InterfaceAddress = NULL;
72: char LocalHostname[128];
73: int dns = 1;
74: int show_ips = 0;
75: int enablempls = 0;
76: int cpacketsize = 64; /* default packet size */
77: int bitpattern = 0;
78: int tos = 0;
79: int reportwide = 0;
80: int af = DEFAULT_AF;
81: int mtrtype = IPPROTO_ICMP; /* Use ICMP as default packet type */
82:
83: /* begin ttl windows addByMin */
84: int fstTTL = 1; /* default start at first hop */
85: /*int maxTTL = MaxHost-1; */ /* max you can go is 255 hops */
86: int maxTTL = 30; /* inline with traceroute */
87: /* end ttl window stuff. */
88: int remoteport = 80; /* for TCP tracing */
89: int timeout = 10 * 1000000; /* for TCP tracing */
90:
91:
92: /* default display field(defined by key in net.h) and order */
93: unsigned char fld_active[2*MAXFLD] = "LS NABWV";
94: int fld_index[256];
95: char available_options[MAXFLD];
96:
97:
98: struct fields data_fields[MAXFLD] = {
99: /* key, Remark, Header, Format, Width, CallBackFunc */
100: {' ', "<sp>: Space between fields", " ", " ", 1, &net_drop },
101: {'L', "L: Loss Ratio", "Loss%", " %4.1f%%", 6, &net_loss },
102: {'D', "D: Dropped Packets", "Drop", " %4d", 5, &net_drop },
103: {'R', "R: Received Packets", "Rcv", " %5d", 6, &net_returned},
104: {'S', "S: Sent Packets", "Snt", " %5d", 6, &net_xmit },
105: {'N', "N: Newest RTT(ms)", "Last", " %5.1f", 6, &net_last },
106: {'B', "B: Min/Best RTT(ms)", "Best", " %5.1f", 6, &net_best },
107: {'A', "A: Average RTT(ms)", "Avg", " %5.1f", 6, &net_avg },
108: {'W', "W: Max/Worst RTT(ms)", "Wrst", " %5.1f", 6, &net_worst },
109: {'V', "V: Standard Deviation", "StDev", " %5.1f", 6, &net_stdev },
110: {'G', "G: Geometric Mean", "Gmean", " %5.1f", 6, &net_gmean },
111: {'J', "J: Current Jitter", "Jttr", " %4.1f", 5, &net_jitter},
112: {'M', "M: Jitter Mean/Avg.", "Javg", " %4.1f", 5, &net_javg },
113: {'X', "X: Worst Jitter", "Jmax", " %4.1f", 5, &net_jworst},
114: {'I', "I: Interarrival Jitter", "Jint", " %4.1f", 5, &net_jinta },
115: {'\0', NULL, NULL, NULL, 0, NULL}
116: };
117:
118: typedef struct names {
119: char* name;
120: struct names* next;
121: } names_t;
122: static names_t *names = NULL;
123:
124: char *
125: trim(char * s) {
126:
127: char * p = s;
128: int l = strlen(p);
129:
130: while(isspace(p[l-1]) && l) p[--l] = 0;
131: while(*p && isspace(*p) && l) ++p, --l;
132:
133: return p;
134: }
135:
136: static void
137: append_to_names(const char* progname, const char* item) {
138:
139: names_t* name = calloc(1, sizeof(names_t));
140: if (name == NULL) {
141: fprintf(stderr, "%s: memory allocation failure\n", progname);
142: exit(EXIT_FAILURE);
143: }
144: name->name = strdup(item);
145: name->next = names;
146: names = name;
147: }
148:
149: static void
150: read_from_file(const char* progname, const char *filename) {
151:
152: FILE *in;
153: char line[512];
154:
155: if (! filename || strcmp(filename, "-") == 0) {
156: clearerr(stdin);
157: in = stdin;
158: } else {
159: in = fopen(filename, "r");
160: if (! in) {
161: fprintf(stderr, "%s: fopen: %s\n", progname, strerror(errno));
162: exit(EXIT_FAILURE);
163: }
164: }
165:
166: while (fgets(line, sizeof(line), in)) {
167: char* name = trim(line);
168: append_to_names(progname, name);
169: }
170:
171: if (ferror(in)) {
172: fprintf(stderr, "%s: ferror: %s\n", progname, strerror(errno));
173: exit(EXIT_FAILURE);
174: }
175:
176: if (in != stdin) fclose(in);
177: }
178:
179: /*
180: * If the file stream is associated with a regular file, lock the file
181: * in order coordinate writes to a common file from multiple mtr
182: * instances. This is useful if, for example, multiple mtr instances
183: * try to append results to a common file.
184: */
185:
186: static void
187: lock(const char* progname, FILE *f) {
188: int fd;
189: struct stat buf;
190: static struct flock lock;
191:
192: assert(f);
193:
194: lock.l_type = F_WRLCK;
195: lock.l_start = 0;
196: lock.l_whence = SEEK_END;
197: lock.l_len = 0;
198: lock.l_pid = getpid();
199:
200: fd = fileno(f);
201: if ((fstat(fd, &buf) == 0) && S_ISREG(buf.st_mode)) {
202: if (fcntl(fd, F_SETLKW, &lock) == -1) {
203: fprintf(stderr, "%s: fcntl: %s (ignored)\n",
204: progname, strerror(errno));
205: }
206: }
207: }
208:
209: /*
210: * If the file stream is associated with a regular file, unlock the
211: * file (which presumably has previously been locked).
212: */
213:
214: static void
215: unlock(const char* progname, FILE *f) {
216: int fd;
217: struct stat buf;
218: static struct flock lock;
219:
220: assert(f);
221:
222: lock.l_type = F_UNLCK;
223: lock.l_start = 0;
224: lock.l_whence = SEEK_END;
225: lock.l_len = 0;
226: lock.l_pid = getpid();
227:
228: fd = fileno(f);
229: if ((fstat(fd, &buf) == 0) && S_ISREG(buf.st_mode)) {
230: if (fcntl(fd, F_SETLKW, &lock) == -1) {
231: fprintf(stderr, "%s: fcntl: %s (ignored)\n",
232: progname, strerror(errno));
233: }
234: }
235: }
236:
237:
238: void init_fld_options (void)
239: {
240: int i;
241:
242: for (i=0;i < 256;i++)
243: fld_index[i] = -1;
244:
245: for (i=0;data_fields[i].key != 0;i++) {
246: available_options[i] = data_fields[i].key;
247: fld_index[data_fields[i].key] = i;
248: }
249: available_options[i] = 0;
250: }
251:
252:
253: void parse_arg (int argc, char **argv)
254: {
255: int opt;
256: int i;
257: static struct option long_options[] = {
258: { "version", 0, 0, 'v' },
259: { "help", 0, 0, 'h' },
260:
261: { "report", 0, 0, 'r' },
262: { "report-wide", 0, 0, 'w' },
263: { "xml", 0, 0, 'x' },
264: { "curses", 0, 0, 't' },
265: { "gtk", 0, 0, 'g' },
266: { "raw", 0, 0, 'l' },
267: { "csv", 0, 0, 'C' },
268: { "split", 0, 0, 'p' }, /* BL */
269: /* maybe above should change to -d 'x' */
270:
271: { "order", 1, 0, 'o' }, /* fileds to display & their order */
272:
273: { "interval", 1, 0, 'i' },
274: { "report-cycles", 1, 0, 'c' },
275: { "psize", 1, 0, 's' }, /* changed 'p' to 's' to match ping option
276: overload psize<0, ->rand(min,max) */
277: { "bitpattern", 1, 0, 'B' },/* overload b>255, ->rand(0,255) */
278: { "tos", 1, 0, 'Q' }, /* typeof service (0,255) */
279: { "mpls", 0, 0, 'e' },
280: { "no-dns", 0, 0, 'n' },
281: { "show-ips", 0, 0, 'b' },
282: { "address", 1, 0, 'a' },
283: { "first-ttl", 1, 0, 'f' }, /* -f & -m are borrowed from traceroute */
284: { "filename", 1, 0, 'F' },
285: { "max-ttl", 1, 0, 'm' },
286: { "udp", 0, 0, 'u' }, /* UDP (default is ICMP) */
287: { "tcp", 0, 0, 'T' }, /* TCP (default is ICMP) */
288: { "port", 1, 0, 'P' }, /* target port number for TCP */
289: { "timeout", 1, 0, 'Z' }, /* timeout for TCP sockets */
290: { "inet", 0, 0, '4' }, /* IPv4 only */
291: { "inet6", 0, 0, '6' }, /* IPv6 only */
292: #ifndef NO_IPINFO
293: { "ipinfo", 1, 0, 'y' }, /* IP info lookup */
294: { "aslookup", 0, 0, 'z' }, /* Do AS lookup (--ipinfo 0) */
295: #endif
296: { 0, 0, 0, 0 }
297: };
298:
299: opt = 0;
300: while(1) {
301: /* added f:m:o: byMin */
302: opt = getopt_long(argc, argv,
303: "vhrwxtglCpo:B:i:c:s:Q:ena:f:m:uTP:Zby:z46", long_options, NULL);
304: if(opt == -1)
305: break;
306:
307: switch(opt) {
308: case 'v':
309: PrintVersion = 1;
310: break;
311: case 'h':
312: PrintHelp = 1;
313: break;
314:
315: case 'r':
316: DisplayMode = DisplayReport;
317: break;
318: case 'w':
319: reportwide = 1;
320: DisplayMode = DisplayReport;
321: break;
322: case 't':
323: DisplayMode = DisplayCurses;
324: break;
325: case 'g':
326: DisplayMode = DisplayGTK;
327: break;
328: case 'p': /* BL */
329: DisplayMode = DisplaySplit;
330: break;
331: case 'l':
332: DisplayMode = DisplayRaw;
333: break;
334: case 'C':
335: DisplayMode = DisplayCSV;
336: break;
337: case 'x':
338: DisplayMode = DisplayXML;
339: break;
340:
341: case 'c':
342: MaxPing = atoi (optarg);
343: ForceMaxPing = 1;
344: break;
345: case 's':
346: cpacketsize = atoi (optarg);
347: break;
348: case 'a':
349: InterfaceAddress = optarg;
350: break;
351: case 'e':
352: enablempls = 1;
353: break;
354: case 'n':
355: dns = 0;
356: break;
357: case 'i':
358: WaitTime = atof (optarg);
359: if (WaitTime <= 0.0) {
360: fprintf (stderr, "mtr: wait time must be positive\n");
361: exit (1);
362: }
363: if (getuid() != 0 && WaitTime < 1.0) {
364: fprintf (stderr, "non-root users cannot request an interval < 1.0 seconds\r\n");
365: exit (1);
366: }
367: break;
368: case 'f':
369: fstTTL = atoi (optarg);
370: if (fstTTL > maxTTL) {
371: fstTTL = maxTTL;
372: }
373: if (fstTTL < 1) { /* prevent 0 hop */
374: fstTTL = 1;
375: }
376: break;
377: case 'F':
378: read_from_file(argv[0], optarg);
379: break;
380: case 'm':
381: maxTTL = atoi (optarg);
382: if (maxTTL > (MaxHost - 1)) {
383: maxTTL = MaxHost-1;
384: }
385: if (maxTTL < 1) { /* prevent 0 hop */
386: maxTTL = 1;
387: }
388: if (fstTTL > maxTTL) { /* don't know the pos of -m or -f */
389: fstTTL = maxTTL;
390: }
391: break;
392: case 'o':
393: /* Check option before passing it on to fld_active. */
394: if (strlen (optarg) > MAXFLD) {
395: fprintf (stderr, "Too many fields: %s\n", optarg);
396: exit (1);
397: }
398: for (i=0; optarg[i]; i++) {
399: if(!strchr (available_options, optarg[i])) {
400: fprintf (stderr, "Unknown field identifier: %c\n", optarg[i]);
401: exit (1);
402: }
403: }
404: strcpy ((char*)fld_active, optarg);
405: break;
406: case 'B':
407: bitpattern = atoi (optarg);
408: if (bitpattern > 255)
409: bitpattern = -1;
410: break;
411: case 'Q':
412: tos = atoi (optarg);
413: if (tos > 255 || tos < 0) {
414: /* error message, should do more checking for valid values,
415: * details in rfc2474 */
416: tos = 0;
417: }
418: break;
419: case 'u':
420: if (mtrtype != IPPROTO_ICMP) {
421: fprintf(stderr, "-u and -T are mutually exclusive.\n");
422: exit(EXIT_FAILURE);
423: }
424: mtrtype = IPPROTO_UDP;
425: break;
426: case 'T':
427: if (mtrtype != IPPROTO_ICMP) {
428: fprintf(stderr, "-u and -T are mutually exclusive.\n");
429: exit(EXIT_FAILURE);
430: }
431: mtrtype = IPPROTO_TCP;
432: break;
433: case 'b':
434: show_ips = 1;
435: break;
436: case 'P':
437: remoteport = atoi(optarg);
438: if (remoteport > 65535 || remoteport < 1) {
439: fprintf(stderr, "Illegal port number.\n");
440: exit(EXIT_FAILURE);
441: }
442: break;
443: case 'Z':
444: timeout = atoi(optarg);
445: timeout *= 1000000;
446: break;
447: case '4':
448: af = AF_INET;
449: break;
450: case '6':
451: #ifdef ENABLE_IPV6
452: af = AF_INET6;
453: break;
454: #else
455: fprintf( stderr, "IPv6 not enabled.\n" );
456: break;
457: #endif
458: #ifndef NO_IPINFO
459: case 'y':
460: ipinfo_no = atoi (optarg);
461: if (ipinfo_no < 0)
462: ipinfo_no = 0;
463: break;
464: case 'z':
465: ipinfo_no = 0;
466: break;
467: #endif
468: }
469: }
470:
471: if (DisplayMode == DisplayReport ||
472: DisplayMode == DisplayTXT ||
473: DisplayMode == DisplayXML ||
474: DisplayMode == DisplayRaw ||
475: DisplayMode == DisplayCSV)
476: Interactive = 0;
477:
478: if (optind > argc - 1)
479: return;
480:
481: }
482:
483:
484: void parse_mtr_options (char *string)
485: {
486: int argc;
487: char *argv[128], *p;
488:
489: if (!string) return;
490:
491: argv[0] = "mtr";
492: argc = 1;
493: p = strtok (string, " \t");
494: while (p != NULL && ((size_t) argc < (sizeof(argv)/sizeof(argv[0])))) {
495: argv[argc++] = p;
496: p = strtok (NULL, " \t");
497: }
498: if (p != NULL) {
499: fprintf (stderr, "Warning: extra arguments ignored: %s", p);
500: }
501:
502: parse_arg (argc, argv);
503: optind = 0;
504: }
505:
506:
507: int main(int argc, char **argv)
508: {
509: struct hostent * host = NULL;
510: int net_preopen_result;
511: #ifdef ENABLE_IPV6
512: struct addrinfo hints, *res;
513: int error;
514: struct hostent trhost;
515: char * alptr[2];
516: struct sockaddr_in * sa4;
517: struct sockaddr_in6 * sa6;
518: #endif
519:
520: /* Get the raw sockets first thing, so we can drop to user euid immediately */
521:
522: if ( ( net_preopen_result = net_preopen () ) ) {
523: fprintf( stderr, "mtr: unable to get raw sockets.\n" );
524: exit( EXIT_FAILURE );
525: }
526:
527: /* Now drop to user permissions */
528: if (setgid(getgid()) || setuid(getuid())) {
529: fprintf (stderr, "mtr: Unable to drop permissions.\n");
530: exit(1);
531: }
532:
533: /* Double check, just in case */
534: if ((geteuid() != getuid()) || (getegid() != getgid())) {
535: fprintf (stderr, "mtr: Unable to drop permissions.\n");
536: exit(1);
537: }
538:
539: /* reset the random seed */
540: srand (getpid());
541:
542: display_detect(&argc, &argv);
543:
544: /* The field options are now in a static array all together,
545: but that requires a run-time initialization. */
546: init_fld_options ();
547:
548: parse_mtr_options (getenv ("MTR_OPTIONS"));
549:
550: parse_arg (argc, argv);
551:
552: while (optind < argc) {
553: char* name = argv[optind++];
554: append_to_names(argv[0], name);
555: }
556:
557: /* Now that we know mtrtype we can select which socket to use */
558: if (net_selectsocket() != 0) {
559: fprintf( stderr, "mtr: Couldn't determine raw socket type.\n" );
560: exit( EXIT_FAILURE );
561: }
562:
563: if (PrintVersion) {
564: printf ("mtr " MTR_VERSION "\n");
565: exit(0);
566: }
567:
568: if (PrintHelp) {
569: printf("usage: %s [-hvrwctglspniuT46] [--help] [--version] [--report]\n"
570: "\t\t[--report-wide] [--report-cycles=COUNT] [--curses] [--gtk]\n"
571: "\t\t[--csv|-C] [--raw] [--split] [--mpls] [--no-dns] [--show-ips]\n"
572: "\t\t[--address interface] [--filename=FILE|-F]\n" /* BL */
573: #ifndef NO_IPINFO
574: "\t\t[--ipinfo=item_no|-y item_no]\n"
575: "\t\t[--aslookup|-z]\n"
576: #endif
577: "\t\t[--psize=bytes/-s bytes]\n" /* ok */
578: "\t\t[--report-wide|-w] [-u|-T] [--port=PORT] [--timeout=SECONDS]\n" /* rew */
579: "\t\t[--interval=SECONDS] HOSTNAME\n", argv[0]);
580: exit(0);
581: }
582:
583: time_t now = time(NULL);
584: names_t* head = names;
585: while (names != NULL) {
586:
587: Hostname = names->name;
588: if (Hostname == NULL) Hostname = "localhost";
589: if (gethostname(LocalHostname, sizeof(LocalHostname))) {
590: strcpy(LocalHostname, "UNKNOWNHOST");
591: }
592:
593: if (net_preopen_result != 0) {
594: fprintf(stderr, "mtr: Unable to get raw socket. (Executable not suid?)\n");
595: if ( DisplayMode != DisplayCSV ) exit(EXIT_FAILURE);
596: else {
597: names = names->next;
598: continue;
599: }
600: }
601:
602: #ifdef ENABLE_IPV6
603: /* gethostbyname2() is deprecated so we'll use getaddrinfo() instead. */
604: bzero( &hints, sizeof hints );
605: hints.ai_family = af;
606: hints.ai_socktype = SOCK_DGRAM;
607: error = getaddrinfo( Hostname, NULL, &hints, &res );
608: if ( error ) {
609: if (error == EAI_SYSTEM)
610: perror ("Failed to resolve host");
611: else
612: fprintf (stderr, "Failed to resolve host: %s\n", gai_strerror(error));
613:
614: if ( DisplayMode != DisplayCSV ) exit(EXIT_FAILURE);
615: else {
616: names = names->next;
617: continue;
618: }
619: }
620: /* Convert the first addrinfo into a hostent. */
621: host = &trhost;
622: bzero( host, sizeof trhost );
623: host->h_name = res->ai_canonname;
624: host->h_aliases = NULL;
625: host->h_addrtype = res->ai_family;
626: af = res->ai_family;
627: host->h_length = res->ai_addrlen;
628: host->h_addr_list = alptr;
629: switch ( af ) {
630: case AF_INET:
631: sa4 = (struct sockaddr_in *) res->ai_addr;
632: alptr[0] = (void *) &(sa4->sin_addr);
633: break;
634: case AF_INET6:
635: sa6 = (struct sockaddr_in6 *) res->ai_addr;
636: alptr[0] = (void *) &(sa6->sin6_addr);
637: break;
638: default:
639: fprintf( stderr, "mtr unknown address type\n" );
640: if ( DisplayMode != DisplayCSV ) exit(EXIT_FAILURE);
641: else {
642: names = names->next;
643: continue;
644: }
645: }
646: alptr[1] = NULL;
647: #else
648: host = gethostbyname(Hostname);
649: if (host == NULL) {
650: herror("mtr gethostbyname");
651: if ( DisplayMode != DisplayCSV ) exit(EXIT_FAILURE);
652: else {
653: names = names->next;
654: continue;
655: }
656: }
657: af = host->h_addrtype;
658: #endif
659:
660: if (net_open(host) != 0) {
661: fprintf(stderr, "mtr: Unable to start net module.\n");
662: if ( DisplayMode != DisplayCSV ) exit(EXIT_FAILURE);
663: else {
664: names = names->next;
665: continue;
666: }
667: }
668:
669: if (net_set_interfaceaddress (InterfaceAddress) != 0) {
670: fprintf( stderr, "mtr: Couldn't set interface address.\n" );
671: if ( DisplayMode != DisplayCSV ) exit(EXIT_FAILURE);
672: else {
673: names = names->next;
674: continue;
675: }
676: }
677:
678: lock(argv[0], stdout);
679: display_open();
680: dns_open();
681:
682: display_mode = 0;
683: display_loop();
684:
685: net_end_transit();
686: display_close(now);
687: unlock(argv[0], stdout);
688:
689: if ( DisplayMode != DisplayCSV ) break;
690: else names = names->next;
691:
692: }
693:
694: net_close();
695:
696: while (head != NULL) {
697: names_t* item = head;
698: free(item->name); item->name = NULL;
699: head = head->next;
700: free(item); item = NULL;
701: }
702: head=NULL;
703:
704: return 0;
705: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>