Commit | Line | Data |
---|---|---|
0a4d79af JH |
1 | /* Mapper for connections between MRouteD multicast routers. |
2 | * Written by Pavel Curtis <Pavel@PARC.Xerox.Com> | |
3 | * | |
4 | * $Id: mapper.c,v 1.4 1993/06/24 05:11:16 deering Exp $ | |
5 | */ | |
6 | ||
7 | /* | |
8 | * Copyright (c) Xerox Corporation 1992. All rights reserved. | |
9 | * | |
10 | * License is granted to copy, to use, and to make and to use derivative | |
11 | * works for research and evaluation purposes, provided that Xerox is | |
12 | * acknowledged in all documentation pertaining to any such copy or derivative | |
13 | * work. Xerox grants no other licenses expressed or implied. The Xerox trade | |
14 | * name should not be used in any advertising without its written permission. | |
15 | * | |
16 | * XEROX CORPORATION MAKES NO REPRESENTATIONS CONCERNING EITHER THE | |
17 | * MERCHANTABILITY OF THIS SOFTWARE OR THE SUITABILITY OF THIS SOFTWARE | |
18 | * FOR ANY PARTICULAR PURPOSE. The software is provided "as is" without | |
19 | * express or implied warranty of any kind. | |
20 | * | |
21 | * These notices must be retained in any copies of any part of this software. | |
22 | */ | |
23 | ||
24 | #include <netdb.h> | |
25 | #include <sys/time.h> | |
26 | #include "defs.h" | |
27 | ||
28 | #define DEFAULT_TIMEOUT 2 /* How long to wait before retrying requests */ | |
29 | #define DEFAULT_RETRIES 1 /* How many times to ask each router */ | |
30 | ||
31 | ||
32 | /* All IP addresses are stored in the data structure in NET order. */ | |
33 | ||
34 | typedef struct neighbor { | |
35 | struct neighbor *next; | |
36 | u_long addr; /* IP address in NET order */ | |
37 | u_char metric; /* TTL cost of forwarding */ | |
38 | u_char threshold; /* TTL threshold to forward */ | |
39 | u_short flags; /* flags on connection */ | |
40 | #define NF_PRESENT 0x8000 /* True if flags are meaningful */ | |
41 | } Neighbor; | |
42 | ||
43 | typedef struct interface { | |
44 | struct interface *next; | |
45 | u_long addr; /* IP address of the interface in NET order */ | |
46 | Neighbor *neighbors; /* List of neighbors' IP addresses */ | |
47 | } Interface; | |
48 | ||
49 | typedef struct node { | |
50 | u_long addr; /* IP address of this entry in NET order */ | |
51 | u_long version; /* which mrouted version is running */ | |
52 | int tries; /* How many requests sent? -1 for aliases */ | |
53 | union { | |
54 | struct node *alias; /* If alias, to what? */ | |
55 | struct interface *interfaces; /* Else, neighbor data */ | |
56 | } u; | |
57 | struct node *left, *right; | |
58 | } Node; | |
59 | ||
60 | ||
61 | Node *routers = 0; | |
62 | u_long our_addr, target_addr = 0; /* in NET order */ | |
63 | int debug = 0; | |
64 | int retries = DEFAULT_RETRIES; | |
65 | int timeout = DEFAULT_TIMEOUT; | |
66 | int show_names = TRUE; | |
67 | ||
68 | ||
69 | Node *find_node(addr, ptr) | |
70 | u_long addr; | |
71 | Node **ptr; | |
72 | { | |
73 | Node *n = *ptr; | |
74 | ||
75 | if (!n) { | |
76 | *ptr = n = (Node *) malloc(sizeof(Node)); | |
77 | n->addr = addr; | |
78 | n->version = 0; | |
79 | n->tries = 0; | |
80 | n->u.interfaces = 0; | |
81 | n->left = n->right = 0; | |
82 | return n; | |
83 | } else if (addr == n->addr) | |
84 | return n; | |
85 | else if (addr < n->addr) | |
86 | return find_node(addr, &(n->left)); | |
87 | else | |
88 | return find_node(addr, &(n->right)); | |
89 | } | |
90 | ||
91 | ||
92 | Interface *find_interface(addr, node) | |
93 | u_long addr; | |
94 | Node *node; | |
95 | { | |
96 | Interface *ifc; | |
97 | ||
98 | for (ifc = node->u.interfaces; ifc; ifc = ifc->next) | |
99 | if (ifc->addr == addr) | |
100 | return ifc; | |
101 | ||
102 | ifc = (Interface *) malloc(sizeof(Interface)); | |
103 | ifc->addr = addr; | |
104 | ifc->next = node->u.interfaces; | |
105 | node->u.interfaces = ifc; | |
106 | ifc->neighbors = 0; | |
107 | ||
108 | return ifc; | |
109 | } | |
110 | ||
111 | ||
112 | Neighbor *find_neighbor(addr, node) | |
113 | u_long addr; | |
114 | Node *node; | |
115 | { | |
116 | Interface *ifc; | |
117 | ||
118 | for (ifc = node->u.interfaces; ifc; ifc = ifc->next) { | |
119 | Neighbor *nb; | |
120 | ||
121 | for (nb = ifc->neighbors; nb; nb = nb->next) | |
122 | if (nb->addr == addr) | |
123 | return nb; | |
124 | } | |
125 | ||
126 | return 0; | |
127 | } | |
128 | ||
129 | ||
130 | /* | |
131 | * Log errors and other messages to stderr, according to the severity of the | |
132 | * message and the current debug level. For errors of severity LOG_ERR or | |
133 | * worse, terminate the program. | |
134 | */ | |
135 | void log(severity, syserr, format, a, b, c, d, e) | |
136 | int severity, syserr; | |
137 | char *format; | |
138 | int a, b, c, d, e; | |
139 | { | |
140 | char fmt[100]; | |
141 | ||
142 | switch (debug) { | |
143 | case 0: if (severity > LOG_WARNING) return; | |
144 | case 1: if (severity > LOG_NOTICE ) return; | |
145 | case 2: if (severity > LOG_INFO ) return; | |
146 | default: | |
147 | fmt[0] = '\0'; | |
148 | if (severity == LOG_WARNING) | |
149 | strcat(fmt, "warning - "); | |
150 | strncat(fmt, format, 80); | |
151 | fprintf(stderr, fmt, a, b, c, d, e); | |
152 | if (syserr == 0) | |
153 | fprintf(stderr, "\n"); | |
154 | else if (syserr < sys_nerr) | |
155 | fprintf(stderr, ": %s\n", sys_errlist[syserr]); | |
156 | else | |
157 | fprintf(stderr, ": errno %d\n", syserr); | |
158 | } | |
159 | ||
160 | if (severity <= LOG_ERR) | |
161 | exit(-1); | |
162 | } | |
163 | ||
164 | ||
165 | /* | |
166 | * Send a neighbors-list request. | |
167 | */ | |
168 | void ask(dst) | |
169 | u_long dst; | |
170 | { | |
171 | send_igmp(our_addr, dst, IGMP_DVMRP, DVMRP_ASK_NEIGHBORS, | |
172 | htonl(MROUTED_LEVEL), 0); | |
173 | } | |
174 | ||
175 | void ask2(dst) | |
176 | u_long dst; | |
177 | { | |
178 | send_igmp(our_addr, dst, IGMP_DVMRP, DVMRP_ASK_NEIGHBORS2, | |
179 | htonl(MROUTED_LEVEL), 0); | |
180 | } | |
181 | ||
182 | ||
183 | /* | |
184 | * Process an incoming group membership report. | |
185 | */ | |
186 | void accept_group_report(src, dst, group) | |
187 | u_long src, dst, group; | |
188 | { | |
189 | log(LOG_INFO, 0, "ignoring IGMP group membership report from %s to %s", | |
190 | inet_fmt(src, s1), inet_fmt(dst, s2)); | |
191 | } | |
192 | ||
193 | ||
194 | /* | |
195 | * Process an incoming neighbor probe message. | |
196 | */ | |
197 | void accept_probe(src, dst) | |
198 | u_long src, dst; | |
199 | { | |
200 | log(LOG_INFO, 0, "ignoring DVMRP probe from %s to %s", | |
201 | inet_fmt(src, s1), inet_fmt(dst, s2)); | |
202 | } | |
203 | ||
204 | ||
205 | /* | |
206 | * Process an incoming route report message. | |
207 | */ | |
208 | void accept_report(src, dst, p, datalen) | |
209 | u_long src, dst; | |
210 | char *p; | |
211 | int datalen; | |
212 | { | |
213 | log(LOG_INFO, 0, "ignoring DVMRP routing report from %s to %s", | |
214 | inet_fmt(src, s1), inet_fmt(dst, s2)); | |
215 | } | |
216 | ||
217 | ||
218 | /* | |
219 | * Process an incoming neighbor-list request message. | |
220 | */ | |
221 | void accept_neighbor_request(src, dst) | |
222 | u_long src, dst; | |
223 | { | |
224 | if (src != our_addr) | |
225 | log(LOG_INFO, 0, | |
226 | "ignoring spurious DVMRP neighbor request from %s to %s", | |
227 | inet_fmt(src, s1), inet_fmt(dst, s2)); | |
228 | } | |
229 | ||
230 | void accept_neighbor_request2(src, dst) | |
231 | u_long src, dst; | |
232 | { | |
233 | if (src != our_addr) | |
234 | log(LOG_INFO, 0, | |
235 | "ignoring spurious DVMRP neighbor request2 from %s to %s", | |
236 | inet_fmt(src, s1), inet_fmt(dst, s2)); | |
237 | } | |
238 | ||
239 | ||
240 | /* | |
241 | * Process an incoming neighbor-list message. | |
242 | */ | |
243 | void accept_neighbors(src, dst, p, datalen, level) | |
244 | u_long src, dst, level; | |
245 | u_char *p; | |
246 | int datalen; | |
247 | { | |
248 | Node *node = find_node(src, &routers); | |
249 | ||
250 | if (node->tries == 0) /* Never heard of 'em; must have hit them at */ | |
251 | node->tries = 1; /* least once, though...*/ | |
252 | else if (node->tries == -1) /* follow alias link */ | |
253 | node = node->u.alias; | |
254 | ||
255 | #define GET_ADDR(a) (a = ((u_long)*p++ << 24), a += ((u_long)*p++ << 16),\ | |
256 | a += ((u_long)*p++ << 8), a += *p++) | |
257 | ||
258 | /* if node is running a recent mrouted, ask for additional info */ | |
259 | if (level != 0) { | |
260 | node->version = ntohl(level); | |
261 | node->tries = 0; | |
262 | ask2(src); | |
263 | return; | |
264 | } | |
265 | ||
266 | if (debug > 3) { | |
267 | int i; | |
268 | ||
269 | fprintf(stderr, " datalen = %d\n", datalen); | |
270 | for (i = 0; i < datalen; i++) { | |
271 | if ((i & 0xF) == 0) | |
272 | fprintf(stderr, " "); | |
273 | fprintf(stderr, " %02x", p[i]); | |
274 | if ((i & 0xF) == 0xF) | |
275 | fprintf(stderr, "\n"); | |
276 | } | |
277 | if ((datalen & 0xF) != 0xF) | |
278 | fprintf(stderr, "\n"); | |
279 | } | |
280 | ||
281 | while (datalen > 0) { /* loop through interfaces */ | |
282 | u_long ifc_addr; | |
283 | u_char metric, threshold, ncount; | |
284 | Node *ifc_node; | |
285 | Interface *ifc; | |
286 | Neighbor *old_neighbors; | |
287 | ||
288 | if (datalen < 4 + 3) { | |
289 | log(LOG_WARNING, 0, "received truncated interface record from %s", | |
290 | inet_fmt(src, s1)); | |
291 | return; | |
292 | } | |
293 | ||
294 | GET_ADDR(ifc_addr); | |
295 | ifc_addr = htonl(ifc_addr); | |
296 | metric = *p++; | |
297 | threshold = *p++; | |
298 | ncount = *p++; | |
299 | datalen -= 4 + 3; | |
300 | ||
301 | /* Fix up any alias information */ | |
302 | ifc_node = find_node(ifc_addr, &routers); | |
303 | if (ifc_node->tries == 0) { /* new node */ | |
304 | ifc_node->tries = -1; | |
305 | ifc_node->u.alias = node; | |
306 | } else if (ifc_node != node | |
307 | && (ifc_node->tries > 0 || ifc_node->u.alias != node)) { | |
308 | /* must merge two hosts' nodes */ | |
309 | Interface *ifc_i, *next_ifc_i; | |
310 | ||
311 | if (ifc_node->tries == -1) { | |
312 | Node *tmp = ifc_node->u.alias; | |
313 | ||
314 | ifc_node->u.alias = node; | |
315 | ifc_node = tmp; | |
316 | } | |
317 | ||
318 | /* Merge ifc_node (foo_i) into node (foo_n) */ | |
319 | ||
320 | if (ifc_node->tries > node->tries) | |
321 | node->tries = ifc_node->tries; | |
322 | ||
323 | for (ifc_i = ifc_node->u.interfaces; ifc_i; ifc_i = next_ifc_i) { | |
324 | Neighbor *nb_i, *next_nb_i, *nb_n; | |
325 | Interface *ifc_n = find_interface(ifc_i->addr, node); | |
326 | ||
327 | old_neighbors = ifc_n->neighbors; | |
328 | for (nb_i = ifc_i->neighbors; nb_i; nb_i = next_nb_i) { | |
329 | next_nb_i = nb_i->next; | |
330 | for (nb_n = old_neighbors; nb_n; nb_n = nb_n->next) | |
331 | if (nb_i->addr == nb_n->addr) { | |
332 | if (nb_i->metric != nb_n->metric | |
333 | || nb_i->threshold != nb_i->threshold) | |
334 | log(LOG_WARNING, 0, | |
335 | "inconsistent %s for neighbor %s of %s", | |
336 | "metric/threshold", | |
337 | inet_fmt(nb_i->addr, s1), | |
338 | inet_fmt(node->addr, s2)); | |
339 | free(nb_i); | |
340 | break; | |
341 | } | |
342 | if (!nb_n) { /* no match for this neighbor yet */ | |
343 | nb_i->next = ifc_n->neighbors; | |
344 | ifc_n->neighbors = nb_i; | |
345 | } | |
346 | } | |
347 | ||
348 | next_ifc_i = ifc_i->next; | |
349 | free(ifc_i); | |
350 | } | |
351 | ||
352 | ifc_node->tries = -1; | |
353 | ifc_node->u.alias = node; | |
354 | } | |
355 | ||
356 | ifc = find_interface(ifc_addr, node); | |
357 | old_neighbors = ifc->neighbors; | |
358 | ||
359 | /* Add the neighbors for this interface */ | |
360 | while (ncount--) { | |
361 | u_long neighbor; | |
362 | Neighbor *nb; | |
363 | Node *n_node; | |
364 | ||
365 | if (datalen < 4) { | |
366 | log(LOG_WARNING, 0, "received truncated neighbor list from %s", | |
367 | inet_fmt(src, s1)); | |
368 | return; | |
369 | } | |
370 | ||
371 | GET_ADDR(neighbor); | |
372 | neighbor = htonl(neighbor); | |
373 | datalen -= 4; | |
374 | ||
375 | for (nb = old_neighbors; nb; nb = nb->next) | |
376 | if (nb->addr == neighbor) { | |
377 | if (metric != nb->metric || threshold != nb->threshold) | |
378 | log(LOG_WARNING, 0, | |
379 | "inconsistent %s for neighbor %s of %s", | |
380 | "metric/threshold", | |
381 | inet_fmt(nb->addr, s1), inet_fmt(node->addr, s2)); | |
382 | goto next_neighbor; | |
383 | } | |
384 | ||
385 | nb = (Neighbor *) malloc(sizeof(Neighbor)); | |
386 | nb->next = ifc->neighbors; | |
387 | ifc->neighbors = nb; | |
388 | nb->addr = neighbor; | |
389 | nb->metric = metric; | |
390 | nb->threshold = threshold; | |
391 | nb->flags = 0; | |
392 | ||
393 | n_node = find_node(neighbor, &routers); | |
394 | if (n_node->tries == 0 && !target_addr) { /* it's a new router */ | |
395 | ask(neighbor); | |
396 | n_node->tries = 1; | |
397 | } | |
398 | ||
399 | next_neighbor: ; | |
400 | } | |
401 | } | |
402 | } | |
403 | ||
404 | void accept_neighbors2(src, dst, p, datalen) | |
405 | u_long src, dst; | |
406 | u_char *p; | |
407 | int datalen; | |
408 | { | |
409 | Node *node = find_node(src, &routers); | |
410 | ||
411 | if (node->tries == 0) /* Never heard of 'em; must have hit them at */ | |
412 | node->tries = 1; /* least once, though...*/ | |
413 | else if (node->tries == -1) /* follow alias link */ | |
414 | node = node->u.alias; | |
415 | ||
416 | while (datalen > 0) { /* loop through interfaces */ | |
417 | u_long ifc_addr; | |
418 | u_char metric, threshold, ncount, flags; | |
419 | Node *ifc_node; | |
420 | Interface *ifc; | |
421 | Neighbor *old_neighbors; | |
422 | ||
423 | if (datalen < 4 + 4) { | |
424 | log(LOG_WARNING, 0, "received truncated interface record from %s", | |
425 | inet_fmt(src, s1)); | |
426 | return; | |
427 | } | |
428 | ||
429 | ifc_addr = *(u_long*)p; | |
430 | p += 4; | |
431 | metric = *p++; | |
432 | threshold = *p++; | |
433 | flags = *p++; | |
434 | ncount = *p++; | |
435 | datalen -= 4 + 4; | |
436 | ||
437 | /* Fix up any alias information */ | |
438 | ifc_node = find_node(ifc_addr, &routers); | |
439 | if (ifc_node->tries == 0) { /* new node */ | |
440 | ifc_node->tries = -1; | |
441 | ifc_node->u.alias = node; | |
442 | } else if (ifc_node != node | |
443 | && (ifc_node->tries > 0 || ifc_node->u.alias != node)) { | |
444 | /* must merge two hosts' nodes */ | |
445 | Interface *ifc_i, *next_ifc_i; | |
446 | ||
447 | if (ifc_node->tries == -1) { | |
448 | Node *tmp = ifc_node->u.alias; | |
449 | ||
450 | ifc_node->u.alias = node; | |
451 | ifc_node = tmp; | |
452 | } | |
453 | ||
454 | /* Merge ifc_node (foo_i) into node (foo_n) */ | |
455 | ||
456 | if (ifc_node->tries > node->tries) | |
457 | node->tries = ifc_node->tries; | |
458 | ||
459 | for (ifc_i = ifc_node->u.interfaces; ifc_i; ifc_i = next_ifc_i) { | |
460 | Neighbor *nb_i, *next_nb_i, *nb_n; | |
461 | Interface *ifc_n = find_interface(ifc_i->addr, node); | |
462 | ||
463 | old_neighbors = ifc_n->neighbors; | |
464 | for (nb_i = ifc_i->neighbors; nb_i; nb_i = next_nb_i) { | |
465 | next_nb_i = nb_i->next; | |
466 | for (nb_n = old_neighbors; nb_n; nb_n = nb_n->next) | |
467 | if (nb_i->addr == nb_n->addr) { | |
468 | if (nb_i->metric != nb_n->metric | |
469 | || nb_i->threshold != nb_i->threshold) | |
470 | log(LOG_WARNING, 0, | |
471 | "inconsistent %s for neighbor %s of %s", | |
472 | "metric/threshold", | |
473 | inet_fmt(nb_i->addr, s1), | |
474 | inet_fmt(node->addr, s2)); | |
475 | free(nb_i); | |
476 | break; | |
477 | } | |
478 | if (!nb_n) { /* no match for this neighbor yet */ | |
479 | nb_i->next = ifc_n->neighbors; | |
480 | ifc_n->neighbors = nb_i; | |
481 | } | |
482 | } | |
483 | ||
484 | next_ifc_i = ifc_i->next; | |
485 | free(ifc_i); | |
486 | } | |
487 | ||
488 | ifc_node->tries = -1; | |
489 | ifc_node->u.alias = node; | |
490 | } | |
491 | ||
492 | ifc = find_interface(ifc_addr, node); | |
493 | old_neighbors = ifc->neighbors; | |
494 | ||
495 | /* Add the neighbors for this interface */ | |
496 | while (ncount--) { | |
497 | u_long neighbor; | |
498 | Neighbor *nb; | |
499 | Node *n_node; | |
500 | ||
501 | if (datalen < 4) { | |
502 | log(LOG_WARNING, 0, "received truncated neighbor list from %s", | |
503 | inet_fmt(src, s1)); | |
504 | return; | |
505 | } | |
506 | ||
507 | neighbor = *(u_long*)p; | |
508 | p += 4; | |
509 | datalen -= 4; | |
510 | if (neighbor == 0) | |
511 | /* make leaf nets point to themselves */ | |
512 | neighbor = ifc_addr; | |
513 | ||
514 | for (nb = old_neighbors; nb; nb = nb->next) | |
515 | if (nb->addr == neighbor) { | |
516 | if (metric != nb->metric || threshold != nb->threshold) | |
517 | log(LOG_WARNING, 0, | |
518 | "inconsistent %s for neighbor %s of %s", | |
519 | "metric/threshold", | |
520 | inet_fmt(nb->addr, s1), inet_fmt(node->addr, s2)); | |
521 | goto next_neighbor; | |
522 | } | |
523 | ||
524 | nb = (Neighbor *) malloc(sizeof(Neighbor)); | |
525 | nb->next = ifc->neighbors; | |
526 | ifc->neighbors = nb; | |
527 | nb->addr = neighbor; | |
528 | nb->metric = metric; | |
529 | nb->threshold = threshold; | |
530 | nb->flags = flags | NF_PRESENT; | |
531 | ||
532 | n_node = find_node(neighbor, &routers); | |
533 | if (n_node->tries == 0 && !target_addr) { /* it's a new router */ | |
534 | ask(neighbor); | |
535 | n_node->tries = 1; | |
536 | } | |
537 | ||
538 | next_neighbor: ; | |
539 | } | |
540 | } | |
541 | } | |
542 | ||
543 | ||
544 | void check_vif_state() | |
545 | { | |
546 | log(LOG_NOTICE, 0, "network marked down..."); | |
547 | } | |
548 | ||
549 | ||
550 | int retry_requests(node) | |
551 | Node *node; | |
552 | { | |
553 | int result; | |
554 | ||
555 | if (node) { | |
556 | result = retry_requests(node->left); | |
557 | if (node->tries > 0 && node->tries < retries) { | |
558 | if (node->version) | |
559 | ask2(node->addr); | |
560 | else | |
561 | ask(node->addr); | |
562 | node->tries++; | |
563 | result = 1; | |
564 | } | |
565 | return retry_requests(node->right) || result; | |
566 | } else | |
567 | return 0; | |
568 | } | |
569 | ||
570 | ||
571 | char *inet_name(addr) | |
572 | u_long addr; | |
573 | { | |
574 | struct hostent *e; | |
575 | ||
576 | e = gethostbyaddr((char *)&addr, sizeof(addr), AF_INET); | |
577 | ||
578 | return e ? e->h_name : 0; | |
579 | } | |
580 | ||
581 | ||
582 | void print_map(node) | |
583 | Node *node; | |
584 | { | |
585 | if (node) { | |
586 | char *name, *addr; | |
587 | ||
588 | print_map(node->left); | |
589 | ||
590 | addr = inet_fmt(node->addr, s1); | |
591 | if (!target_addr | |
592 | || (node->tries >= 0 && node->u.interfaces) | |
593 | || (node->tries == -1 | |
594 | && node->u.alias->tries >= 0 | |
595 | && node->u.alias->u.interfaces)) { | |
596 | if (show_names && (name = inet_name(node->addr))) | |
597 | printf("%s (%s):", addr, name); | |
598 | else | |
599 | printf("%s:", addr); | |
600 | if (node->tries < 0) | |
601 | printf(" alias for %s\n\n", inet_fmt(node->u.alias->addr, s1)); | |
602 | else if (!node->u.interfaces) | |
603 | printf(" no response to query\n\n"); | |
604 | else { | |
605 | Interface *ifc; | |
606 | ||
607 | if (node->version) | |
608 | printf(" <v%d.%d>", node->version & 0xff, | |
609 | (node->version >> 8) & 0xff); | |
610 | printf("\n"); | |
611 | for (ifc = node->u.interfaces; ifc; ifc = ifc->next) { | |
612 | Neighbor *nb; | |
613 | char *ifc_name = inet_fmt(ifc->addr, s1); | |
614 | int ifc_len = strlen(ifc_name); | |
615 | int count = 0; | |
616 | ||
617 | printf(" %s:", ifc_name); | |
618 | for (nb = ifc->neighbors; nb; nb = nb->next) { | |
619 | if (count > 0) | |
620 | printf("%*s", ifc_len + 5, ""); | |
621 | printf(" %s", inet_fmt(nb->addr, s1)); | |
622 | if (show_names && (name = inet_name(nb->addr))) | |
623 | printf(" (%s)", name); | |
624 | printf(" [%d/%d", nb->metric, nb->threshold); | |
625 | if (nb->flags) { | |
626 | u_short flags = nb->flags; | |
627 | if (flags & DVMRP_NF_TUNNEL) | |
628 | printf("/tunnel"); | |
629 | if (flags & DVMRP_NF_SRCRT) | |
630 | printf("/srcrt"); | |
631 | if (flags & DVMRP_NF_QUERIER) | |
632 | printf("/querier"); | |
633 | if (flags & DVMRP_NF_DISABLED) | |
634 | printf("/disabled"); | |
635 | if (flags & DVMRP_NF_DOWN) | |
636 | printf("/down"); | |
637 | } | |
638 | printf("]\n"); | |
639 | count++; | |
640 | } | |
641 | } | |
642 | printf("\n"); | |
643 | } | |
644 | } | |
645 | print_map(node->right); | |
646 | } | |
647 | } | |
648 | ||
649 | ||
650 | char *graph_name(addr, buf) | |
651 | u_long addr; | |
652 | char *buf; | |
653 | { | |
654 | char *name; | |
655 | ||
656 | if (show_names && (name = inet_name(addr))) | |
657 | strcpy(buf, name); | |
658 | else | |
659 | inet_fmt(addr, buf); | |
660 | ||
661 | return buf; | |
662 | } | |
663 | ||
664 | ||
665 | void graph_edges(node) | |
666 | Node *node; | |
667 | { | |
668 | Interface *ifc; | |
669 | Neighbor *nb; | |
670 | char name[100]; | |
671 | ||
672 | if (node) { | |
673 | graph_edges(node->left); | |
674 | if (node->tries >= 0) { | |
675 | printf(" %d {$ NP %d0 %d0 $} \"%s%s\" \n", | |
676 | (int) node->addr, | |
677 | node->addr & 0xFF, (node->addr >> 8) & 0xFF, | |
678 | graph_name(node->addr, name), | |
679 | node->u.interfaces ? "" : "*"); | |
680 | for (ifc = node->u.interfaces; ifc; ifc = ifc->next) | |
681 | for (nb = ifc->neighbors; nb; nb = nb->next) { | |
682 | Node *nb_node = find_node(nb->addr, &routers); | |
683 | Neighbor *nb2; | |
684 | ||
685 | if (nb_node->tries < 0) | |
686 | nb_node = nb_node->u.alias; | |
687 | ||
688 | if (node != nb_node && | |
689 | (!(nb2 = find_neighbor(node->addr, nb_node)) | |
690 | || node->addr < nb_node->addr)) { | |
691 | printf(" %d \"%d/%d", | |
692 | nb_node->addr, nb->metric, nb->threshold); | |
693 | if (nb2 && (nb2->metric != nb->metric | |
694 | || nb2->threshold != nb->threshold)) | |
695 | printf(",%d/%d", nb2->metric, nb2->threshold); | |
696 | if (nb->flags & NF_PRESENT) | |
697 | printf("%s%s", | |
698 | nb->flags & DVMRP_NF_SRCRT ? "" : | |
699 | nb->flags & DVMRP_NF_TUNNEL ? "E" : "P", | |
700 | nb->flags & DVMRP_NF_DOWN ? "D" : ""); | |
701 | printf("\"\n"); | |
702 | } | |
703 | } | |
704 | printf(" ;\n"); | |
705 | } | |
706 | graph_edges(node->right); | |
707 | } | |
708 | } | |
709 | ||
710 | void elide_aliases(node) | |
711 | Node *node; | |
712 | { | |
713 | if (node) { | |
714 | elide_aliases(node->left); | |
715 | if (node->tries >= 0) { | |
716 | Interface *ifc; | |
717 | ||
718 | for (ifc = node->u.interfaces; ifc; ifc = ifc->next) { | |
719 | Neighbor *nb; | |
720 | ||
721 | for (nb = ifc->neighbors; nb; nb = nb->next) { | |
722 | Node *nb_node = find_node(nb->addr, &routers); | |
723 | ||
724 | if (nb_node->tries < 0) | |
725 | nb->addr = nb_node->u.alias->addr; | |
726 | } | |
727 | } | |
728 | } | |
729 | elide_aliases(node->right); | |
730 | } | |
731 | } | |
732 | ||
733 | void graph_map() | |
734 | { | |
735 | time_t now = time(0); | |
736 | char *nowstr = ctime(&now); | |
737 | ||
738 | nowstr[24] = '\0'; /* Kill the newline at the end */ | |
739 | elide_aliases(routers); | |
740 | printf("GRAPH \"Multicast Router Connectivity: %s\" = UNDIRECTED\n", | |
741 | nowstr); | |
742 | graph_edges(routers); | |
743 | printf("END\n"); | |
744 | } | |
745 | ||
746 | ||
747 | int get_number(var, deflt, pargv, pargc) | |
748 | int *var, *pargc, deflt; | |
749 | char ***pargv; | |
750 | { | |
751 | if ((*pargv)[0][2] == '\0') { /* Get the value from the next argument */ | |
752 | if (*pargc > 1 && isdigit((*pargv)[1][0])) { | |
753 | (*pargv)++, (*pargc)--; | |
754 | *var = atoi((*pargv)[0]); | |
755 | return 1; | |
756 | } else if (deflt >= 0) { | |
757 | *var = deflt; | |
758 | return 1; | |
759 | } else | |
760 | return 0; | |
761 | } else { /* Get value from the rest of this argument */ | |
762 | if (isdigit((*pargv)[0][2])) { | |
763 | *var = atoi((*pargv)[0] + 2); | |
764 | return 1; | |
765 | } else { | |
766 | return 0; | |
767 | } | |
768 | } | |
769 | } | |
770 | ||
771 | ||
772 | u_long host_addr(name) | |
773 | char *name; | |
774 | { | |
775 | struct hostent *e = gethostbyname(name); | |
776 | int addr; | |
777 | ||
778 | if (e) | |
779 | memcpy(&addr, e->h_addr_list[0], e->h_length); | |
780 | else { | |
781 | addr = inet_addr(name); | |
782 | if (addr == -1) | |
783 | addr = 0; | |
784 | } | |
785 | ||
786 | return addr; | |
787 | } | |
788 | ||
789 | ||
790 | main(argc, argv) | |
791 | int argc; | |
792 | char *argv[]; | |
793 | { | |
794 | int flood = FALSE, graph = FALSE; | |
795 | ||
796 | #ifdef SYSV | |
797 | setvbuf(stderr, NULL, _IOLBF, 0); | |
798 | #else | |
799 | setlinebuf(stderr); | |
800 | #endif | |
801 | ||
802 | if (geteuid() != 0) { | |
803 | fprintf(stderr, "must be root\n"); | |
804 | exit(1); | |
805 | } | |
806 | ||
807 | argv++, argc--; | |
808 | while (argc > 0 && argv[0][0] == '-') { | |
809 | switch (argv[0][1]) { | |
810 | case 'd': | |
811 | if (!get_number(&debug, DEFAULT_DEBUG, &argv, &argc)) | |
812 | goto usage; | |
813 | break; | |
814 | case 'f': | |
815 | flood = TRUE; | |
816 | break; | |
817 | case 'g': | |
818 | graph = TRUE; | |
819 | break; | |
820 | case 'n': | |
821 | show_names = FALSE; | |
822 | break; | |
823 | case 'r': | |
824 | if (!get_number(&retries, -1, &argv, &argc)) | |
825 | goto usage; | |
826 | break; | |
827 | case 't': | |
828 | if (!get_number(&timeout, -1, &argv, &argc)) | |
829 | goto usage; | |
830 | break; | |
831 | default: | |
832 | goto usage; | |
833 | } | |
834 | argv++, argc--; | |
835 | } | |
836 | ||
837 | if (argc > 1) { | |
838 | usage: | |
839 | fprintf(stderr, | |
840 | "Usage: map-mbone [-f] [-g] [-n] [-t timeout] %s\n\n", | |
841 | "[-r retries] [-d [debug-level]] [router]"); | |
842 | fprintf(stderr, "\t-f Flood the routing graph with queries\n"); | |
843 | fprintf(stderr, "\t (True by default unless `router' is given)\n"); | |
844 | fprintf(stderr, "\t-g Generate output in GraphEd format\n"); | |
845 | fprintf(stderr, "\t-n Don't look up DNS names for routers\n"); | |
846 | exit(1); | |
847 | } else if (argc == 1 && !(target_addr = host_addr(argv[0]))) { | |
848 | fprintf(stderr, "Unknown host: %s\n", argv[0]); | |
849 | exit(2); | |
850 | } | |
851 | ||
852 | if (debug) | |
853 | fprintf(stderr, "Debug level %u\n", debug); | |
854 | ||
855 | init_igmp(); | |
856 | ||
857 | { /* Find a good local address for us. */ | |
858 | int udp; | |
859 | struct sockaddr_in addr; | |
860 | int addrlen = sizeof(addr); | |
861 | ||
862 | addr.sin_family = AF_INET; | |
863 | addr.sin_addr.s_addr = dvmrp_group; | |
864 | addr.sin_port = htons(2000); /* any port over 1024 will do... */ | |
865 | if ((udp = socket(AF_INET, SOCK_DGRAM, 0)) < 0 | |
866 | || connect(udp, (struct sockaddr *) &addr, sizeof(addr)) < 0 | |
867 | || getsockname(udp, (struct sockaddr *) &addr, &addrlen) < 0) { | |
868 | perror("Determining local address"); | |
869 | exit(-1); | |
870 | } | |
871 | close(udp); | |
872 | our_addr = addr.sin_addr.s_addr; | |
873 | } | |
874 | ||
875 | /* Send initial seed message to all local routers */ | |
876 | ask(target_addr ? target_addr : allhosts_group); | |
877 | ||
878 | if (target_addr) { | |
879 | Node *n = find_node(target_addr, &routers); | |
880 | ||
881 | n->tries = 1; | |
882 | ||
883 | if (flood) | |
884 | target_addr = 0; | |
885 | } | |
886 | ||
887 | /* Main receive loop */ | |
888 | for(;;) { | |
889 | fd_set fds; | |
890 | struct timeval tv; | |
891 | int count, recvlen, dummy = 0; | |
892 | ||
893 | FD_ZERO(&fds); | |
894 | FD_SET(igmp_socket, &fds); | |
895 | ||
896 | tv.tv_sec = timeout; | |
897 | tv.tv_usec = 0; | |
898 | ||
899 | count = select(igmp_socket + 1, &fds, 0, 0, &tv); | |
900 | ||
901 | if (count < 0) { | |
902 | if (errno != EINTR) | |
903 | perror("select"); | |
904 | continue; | |
905 | } else if (count == 0) { | |
906 | log(LOG_DEBUG, 0, "Timed out receiving neighbor lists"); | |
907 | if (retry_requests(routers)) | |
908 | continue; | |
909 | else | |
910 | break; | |
911 | } | |
912 | ||
913 | recvlen = recvfrom(igmp_socket, recv_buf, sizeof(recv_buf), | |
914 | 0, NULL, &dummy); | |
915 | if (recvlen >= 0) | |
916 | accept_igmp(recvlen); | |
917 | else if (errno != EINTR) | |
918 | perror("recvfrom"); | |
919 | } | |
920 | ||
921 | printf("\n"); | |
922 | ||
923 | if (graph) | |
924 | graph_map(); | |
925 | else { | |
926 | if (!target_addr) | |
927 | printf("Multicast Router Connectivity:\n\n"); | |
928 | print_map(routers); | |
929 | } | |
930 | ||
931 | exit(0); | |
932 | } |