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