1 |
|
|
/* $OpenBSD: mtrace.c,v 1.39 2017/08/31 12:03:02 otto Exp $ */ |
2 |
|
|
/* $NetBSD: mtrace.c,v 1.5 1995/12/10 10:57:15 mycroft Exp $ */ |
3 |
|
|
|
4 |
|
|
/* |
5 |
|
|
* mtrace.c |
6 |
|
|
* |
7 |
|
|
* This tool traces the branch of a multicast tree from a source to a |
8 |
|
|
* receiver for a particular multicast group and gives statistics |
9 |
|
|
* about packet rate and loss for each hop along the path. It can |
10 |
|
|
* usually be invoked just as |
11 |
|
|
* |
12 |
|
|
* mtrace source |
13 |
|
|
* |
14 |
|
|
* to trace the route from that source to the local host for a default |
15 |
|
|
* group when only the route is desired and not group-specific packet |
16 |
|
|
* counts. See the usage line for more complex forms. |
17 |
|
|
* |
18 |
|
|
* |
19 |
|
|
* Released 4 Apr 1995. This program was adapted by Steve Casner |
20 |
|
|
* (USC/ISI) from a prototype written by Ajit Thyagarajan (UDel and |
21 |
|
|
* Xerox PARC). It attempts to parallel in command syntax and output |
22 |
|
|
* format the unicast traceroute program written by Van Jacobson (LBL) |
23 |
|
|
* for the parts where that makes sense. |
24 |
|
|
* |
25 |
|
|
* Copyright (c) 1998-2001. |
26 |
|
|
* The University of Southern California/Information Sciences Institute. |
27 |
|
|
* All rights reserved. |
28 |
|
|
* |
29 |
|
|
* Redistribution and use in source and binary forms, with or without |
30 |
|
|
* modification, are permitted provided that the following conditions |
31 |
|
|
* are met: |
32 |
|
|
* 1. Redistributions of source code must retain the above copyright |
33 |
|
|
* notice, this list of conditions and the following disclaimer. |
34 |
|
|
* 2. Redistributions in binary form must reproduce the above copyright |
35 |
|
|
* notice, this list of conditions and the following disclaimer in the |
36 |
|
|
* documentation and/or other materials provided with the distribution. |
37 |
|
|
* 3. Neither the name of the project nor the names of its contributors |
38 |
|
|
* may be used to endorse or promote products derived from this software |
39 |
|
|
* without specific prior written permission. |
40 |
|
|
* |
41 |
|
|
* THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND |
42 |
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
43 |
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
44 |
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE |
45 |
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
46 |
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
47 |
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
48 |
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
49 |
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
50 |
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
51 |
|
|
* SUCH DAMAGE. |
52 |
|
|
*/ |
53 |
|
|
|
54 |
|
|
#include <netdb.h> |
55 |
|
|
#include <sys/time.h> |
56 |
|
|
#include <string.h> |
57 |
|
|
#include <poll.h> |
58 |
|
|
#include <ctype.h> |
59 |
|
|
#include <sys/ioctl.h> |
60 |
|
|
#include "defs.h" |
61 |
|
|
#include <arpa/inet.h> |
62 |
|
|
#include <stdarg.h> |
63 |
|
|
#ifdef SUNOS5 |
64 |
|
|
#include <sys/systeminfo.h> |
65 |
|
|
#endif |
66 |
|
|
#include <ifaddrs.h> |
67 |
|
|
#include <err.h> |
68 |
|
|
|
69 |
|
|
#define DEFAULT_TIMEOUT 3 /* How long to wait before retrying requests */ |
70 |
|
|
#define DEFAULT_RETRIES 3 /* How many times to try */ |
71 |
|
|
#define MAXHOPS UNREACHABLE /* Don't need more hops than max metric */ |
72 |
|
|
#define UNICAST_TTL 255 /* TTL for unicast response */ |
73 |
|
|
#define MULTICAST_TTL1 64 /* Default TTL for multicast query/response */ |
74 |
|
|
#define MULTICAST_TTL_INC 32 /* TTL increment for increase after timeout */ |
75 |
|
|
#define MULTICAST_TTL_MAX 192 /* Maximum TTL allowed (protect low-BW links */ |
76 |
|
|
|
77 |
|
|
struct resp_buf { |
78 |
|
|
u_long qtime; /* Time query was issued */ |
79 |
|
|
u_long rtime; /* Time response was received */ |
80 |
|
|
int len; /* Number of reports or length of data */ |
81 |
|
|
struct igmp igmp; /* IGMP header */ |
82 |
|
|
union { |
83 |
|
|
struct { |
84 |
|
|
struct tr_query q; /* Query/response header */ |
85 |
|
|
struct tr_resp r[MAXHOPS]; /* Per-hop reports */ |
86 |
|
|
} t; |
87 |
|
|
char d[MAX_DVMRP_DATA_LEN]; /* Neighbor data */ |
88 |
|
|
} u; |
89 |
|
|
} base, incr[2]; |
90 |
|
|
|
91 |
|
|
#define qhdr u.t.q |
92 |
|
|
#define resps u.t.r |
93 |
|
|
#define ndata u.d |
94 |
|
|
|
95 |
|
|
char names[MAXHOPS][40]; |
96 |
|
|
int reset[MAXHOPS]; /* To get around 3.4 bug, ... */ |
97 |
|
|
int swaps[MAXHOPS]; /* To get around 3.6 bug, ... */ |
98 |
|
|
|
99 |
|
|
int timeout = DEFAULT_TIMEOUT; |
100 |
|
|
int nqueries = DEFAULT_RETRIES; |
101 |
|
|
int numeric = FALSE; |
102 |
|
|
int debug = 0; |
103 |
|
|
int passive = FALSE; |
104 |
|
|
int multicast = FALSE; |
105 |
|
|
int statint = 10; |
106 |
|
|
int verbose = 0; |
107 |
|
|
|
108 |
|
|
u_int32_t defgrp; /* Default group if not specified */ |
109 |
|
|
u_int32_t query_cast; /* All routers multicast addr */ |
110 |
|
|
u_int32_t resp_cast; /* Mtrace response multicast addr */ |
111 |
|
|
|
112 |
|
|
u_int32_t lcl_addr = 0; /* This host address, in NET order */ |
113 |
|
|
u_int32_t dst_netmask; /* netmask to go with qdst */ |
114 |
|
|
|
115 |
|
|
/* |
116 |
|
|
* Query/response parameters, all initialized to zero and set later |
117 |
|
|
* to default values or from options. |
118 |
|
|
*/ |
119 |
|
|
u_int32_t qsrc = 0; /* Source address in the query */ |
120 |
|
|
u_int32_t qgrp = 0; /* Group address in the query */ |
121 |
|
|
u_int32_t qdst = 0; /* Destination (receiver) address in query */ |
122 |
|
|
u_char qno = 0; /* Max number of hops to query */ |
123 |
|
|
u_int32_t raddr = 0; /* Address where response should be sent */ |
124 |
|
|
int qttl = 0; /* TTL for the query packet */ |
125 |
|
|
u_char rttl = 0; /* TTL for the response packet */ |
126 |
|
|
u_int32_t gwy = 0; /* User-supplied last-hop router address */ |
127 |
|
|
u_int32_t tdst = 0; /* Address where trace is sent (last-hop) */ |
128 |
|
|
|
129 |
|
|
vifi_t numvifs; /* to keep loader happy */ |
130 |
|
|
/* (see kern.c) */ |
131 |
|
|
|
132 |
|
|
char * inet_name(u_int32_t addr); |
133 |
|
|
u_int32_t host_addr(char *name); |
134 |
|
|
/* u_int is promoted u_char */ |
135 |
|
|
char * proto_type(u_int type); |
136 |
|
|
char * flag_type(u_int type); |
137 |
|
|
|
138 |
|
|
u_int32_t get_netmask(int s, u_int32_t dst); |
139 |
|
|
int get_ttl(struct resp_buf *buf); |
140 |
|
|
int t_diff(u_long a, u_long b); |
141 |
|
|
u_long fixtime(u_long time); |
142 |
|
|
int send_recv(u_int32_t dst, int type, int code, |
143 |
|
|
int tries, struct resp_buf *save); |
144 |
|
|
char * print_host(u_int32_t addr); |
145 |
|
|
char * print_host2(u_int32_t addr1, u_int32_t addr2); |
146 |
|
|
void print_trace(int index, struct resp_buf *buf); |
147 |
|
|
int what_kind(struct resp_buf *buf, char *why); |
148 |
|
|
char * scale(int *hop); |
149 |
|
|
void stat_line(struct tr_resp *r, struct tr_resp *s, |
150 |
|
|
int have_next, int *res); |
151 |
|
|
void fixup_stats(struct resp_buf *base, |
152 |
|
|
struct resp_buf *prev, struct resp_buf *new); |
153 |
|
|
int print_stats(struct resp_buf *base, |
154 |
|
|
struct resp_buf *prev, struct resp_buf *new); |
155 |
|
|
void check_vif_state(void); |
156 |
|
|
u_long byteswap(u_long v); |
157 |
|
|
|
158 |
|
|
int main(int argc, char *argv[]); |
159 |
|
|
|
160 |
|
|
|
161 |
|
|
|
162 |
|
|
char * |
163 |
|
|
inet_name(u_int32_t addr) |
164 |
|
|
{ |
165 |
|
|
struct hostent *e; |
166 |
|
|
|
167 |
|
|
e = gethostbyaddr((char *)&addr, sizeof(addr), AF_INET); |
168 |
|
|
|
169 |
|
|
return e ? e->h_name : "?"; |
170 |
|
|
} |
171 |
|
|
|
172 |
|
|
|
173 |
|
|
u_int32_t |
174 |
|
|
host_addr(char *name) |
175 |
|
|
{ |
176 |
|
|
struct hostent *e = NULL; |
177 |
|
|
u_int32_t addr; |
178 |
|
|
int i, dots = 3; |
179 |
|
|
char buf[40]; |
180 |
|
|
char *ip = name; |
181 |
|
|
char *op = buf; |
182 |
|
|
|
183 |
|
|
/* |
184 |
|
|
* Undo BSD's favor -- take fewer than 4 octets as net/subnet address |
185 |
|
|
* if the name is all numeric. |
186 |
|
|
*/ |
187 |
|
|
for (i = sizeof(buf) - 7; i > 0; --i) { |
188 |
|
|
if (*ip == '.') |
189 |
|
|
--dots; |
190 |
|
|
else if (*ip == '\0') |
191 |
|
|
break; |
192 |
|
|
else if (!isdigit((unsigned char)*ip)) |
193 |
|
|
dots = 0; /* Not numeric, don't add zeroes */ |
194 |
|
|
*op++ = *ip++; |
195 |
|
|
} |
196 |
|
|
for (i = 0; i < dots; ++i) { |
197 |
|
|
*op++ = '.'; |
198 |
|
|
*op++ = '0'; |
199 |
|
|
} |
200 |
|
|
*op = '\0'; |
201 |
|
|
|
202 |
|
|
if (dots <= 0) e = gethostbyname(name); |
203 |
|
|
if (e) memcpy((char *)&addr, e->h_addr_list[0], e->h_length); |
204 |
|
|
else { |
205 |
|
|
addr = inet_addr(buf); |
206 |
|
|
if (addr == -1) { |
207 |
|
|
addr = 0; |
208 |
|
|
printf("Could not parse %s as host name or address\n", name); |
209 |
|
|
} |
210 |
|
|
} |
211 |
|
|
return addr; |
212 |
|
|
} |
213 |
|
|
|
214 |
|
|
|
215 |
|
|
char * |
216 |
|
|
proto_type(u_int type) |
217 |
|
|
{ |
218 |
|
|
static char buf[80]; |
219 |
|
|
|
220 |
|
|
switch (type) { |
221 |
|
|
case PROTO_DVMRP: |
222 |
|
|
return ("DVMRP"); |
223 |
|
|
case PROTO_MOSPF: |
224 |
|
|
return ("MOSPF"); |
225 |
|
|
case PROTO_PIM: |
226 |
|
|
return ("PIM"); |
227 |
|
|
case PROTO_CBT: |
228 |
|
|
return ("CBT"); |
229 |
|
|
default: |
230 |
|
|
(void) snprintf(buf, sizeof buf, "Unknown protocol code %d", type); |
231 |
|
|
return (buf); |
232 |
|
|
} |
233 |
|
|
} |
234 |
|
|
|
235 |
|
|
|
236 |
|
|
char * |
237 |
|
|
flag_type(u_int type) |
238 |
|
|
{ |
239 |
|
|
static char buf[80]; |
240 |
|
|
|
241 |
|
|
switch (type) { |
242 |
|
|
case TR_NO_ERR: |
243 |
|
|
return (""); |
244 |
|
|
case TR_WRONG_IF: |
245 |
|
|
return ("Wrong interface"); |
246 |
|
|
case TR_PRUNED: |
247 |
|
|
return ("Prune sent upstream"); |
248 |
|
|
case TR_OPRUNED: |
249 |
|
|
return ("Output pruned"); |
250 |
|
|
case TR_SCOPED: |
251 |
|
|
return ("Hit scope boundary"); |
252 |
|
|
case TR_NO_RTE: |
253 |
|
|
return ("No route"); |
254 |
|
|
case TR_OLD_ROUTER: |
255 |
|
|
return ("Next router no mtrace"); |
256 |
|
|
case TR_NO_FWD: |
257 |
|
|
return ("Not forwarding"); |
258 |
|
|
case TR_NO_SPACE: |
259 |
|
|
return ("No space in packet"); |
260 |
|
|
default: |
261 |
|
|
(void) snprintf(buf, sizeof buf, "Unknown error code %d", type); |
262 |
|
|
return (buf); |
263 |
|
|
} |
264 |
|
|
} |
265 |
|
|
|
266 |
|
|
/* |
267 |
|
|
* If destination is on a local net, get the netmask, else set the |
268 |
|
|
* netmask to all ones. There are two side effects: if the local |
269 |
|
|
* address was not explicitly set, and if the destination is on a |
270 |
|
|
* local net, use that one; in either case, verify that the local |
271 |
|
|
* address is valid. |
272 |
|
|
*/ |
273 |
|
|
|
274 |
|
|
u_int32_t |
275 |
|
|
get_netmask(int s, u_int32_t dst) |
276 |
|
|
{ |
277 |
|
|
u_int32_t if_addr, if_mask; |
278 |
|
|
u_int32_t retval = 0xFFFFFFFF; |
279 |
|
|
int found = FALSE; |
280 |
|
|
struct ifaddrs *ifap, *ifa; |
281 |
|
|
|
282 |
|
|
if (getifaddrs(&ifap) != 0) { |
283 |
|
|
perror("getifaddrs"); |
284 |
|
|
return (retval); |
285 |
|
|
} |
286 |
|
|
for (ifa = ifap; ifa; ifa = ifa->ifa_next) { |
287 |
|
|
if (ifa->ifa_addr->sa_family != AF_INET) |
288 |
|
|
continue; |
289 |
|
|
if_addr = ((struct sockaddr_in *)ifa->ifa_addr)->sin_addr.s_addr; |
290 |
|
|
if_mask = ((struct sockaddr_in *)ifa->ifa_netmask)->sin_addr.s_addr; |
291 |
|
|
if ((dst & if_mask) == (if_addr & if_mask)) { |
292 |
|
|
retval = if_mask; |
293 |
|
|
if (lcl_addr == 0) |
294 |
|
|
lcl_addr = if_addr; |
295 |
|
|
} |
296 |
|
|
if (lcl_addr == if_addr) |
297 |
|
|
found = TRUE; |
298 |
|
|
} |
299 |
|
|
if (!found && lcl_addr != 0) { |
300 |
|
|
printf("Interface address is not valid\n"); |
301 |
|
|
exit(1); |
302 |
|
|
} |
303 |
|
|
freeifaddrs(ifap); |
304 |
|
|
return (retval); |
305 |
|
|
} |
306 |
|
|
|
307 |
|
|
|
308 |
|
|
int |
309 |
|
|
get_ttl(struct resp_buf *buf) |
310 |
|
|
{ |
311 |
|
|
int rno; |
312 |
|
|
struct tr_resp *b; |
313 |
|
|
u_int ttl; |
314 |
|
|
|
315 |
|
|
if (buf && (rno = buf->len) > 0) { |
316 |
|
|
b = buf->resps + rno - 1; |
317 |
|
|
ttl = b->tr_fttl; |
318 |
|
|
|
319 |
|
|
while (--rno > 0) { |
320 |
|
|
--b; |
321 |
|
|
if (ttl < b->tr_fttl) ttl = b->tr_fttl; |
322 |
|
|
else ++ttl; |
323 |
|
|
} |
324 |
|
|
ttl += MULTICAST_TTL_INC; |
325 |
|
|
if (ttl < MULTICAST_TTL1) ttl = MULTICAST_TTL1; |
326 |
|
|
if (ttl > MULTICAST_TTL_MAX) ttl = MULTICAST_TTL_MAX; |
327 |
|
|
return (ttl); |
328 |
|
|
} else return(MULTICAST_TTL1); |
329 |
|
|
} |
330 |
|
|
|
331 |
|
|
/* |
332 |
|
|
* Calculate the difference between two 32-bit NTP timestamps and return |
333 |
|
|
* the result in milliseconds. |
334 |
|
|
*/ |
335 |
|
|
int |
336 |
|
|
t_diff(u_long a, u_long b) |
337 |
|
|
{ |
338 |
|
|
int d = a - b; |
339 |
|
|
|
340 |
|
|
return ((d * 125) >> 13); |
341 |
|
|
} |
342 |
|
|
|
343 |
|
|
/* |
344 |
|
|
* Fixup for incorrect time format in 3.3 mrouted. |
345 |
|
|
* This is possible because (JAN_1970 mod 64K) is quite close to 32K, |
346 |
|
|
* so correct and incorrect times will be far apart. |
347 |
|
|
*/ |
348 |
|
|
u_long |
349 |
|
|
fixtime(u_long time) |
350 |
|
|
{ |
351 |
|
|
if (abs((int)(time-base.qtime)) > 0x3FFFFFFF) |
352 |
|
|
time = ((time & 0xFFFF0000) + (JAN_1970 << 16)) + |
353 |
|
|
((time & 0xFFFF) << 14) / 15625; |
354 |
|
|
return (time); |
355 |
|
|
} |
356 |
|
|
|
357 |
|
|
/* |
358 |
|
|
* Swap bytes for poor little-endian machines that don't byte-swap |
359 |
|
|
*/ |
360 |
|
|
u_long |
361 |
|
|
byteswap(u_long v) |
362 |
|
|
{ |
363 |
|
|
return ((v << 24) | ((v & 0xff00) << 8) | |
364 |
|
|
((v >> 8) & 0xff00) | (v >> 24)); |
365 |
|
|
} |
366 |
|
|
|
367 |
|
|
int |
368 |
|
|
send_recv(u_int32_t dst, int type, int code, int tries, struct resp_buf *save) |
369 |
|
|
{ |
370 |
|
|
struct timeval tq, tr, tv; |
371 |
|
|
struct ip *ip; |
372 |
|
|
struct igmp *igmp; |
373 |
|
|
struct tr_query *query, *rquery; |
374 |
|
|
int ipdatalen, iphdrlen, igmpdatalen; |
375 |
|
|
u_int32_t local, group; |
376 |
|
|
int datalen; |
377 |
|
|
struct pollfd pfd[1]; |
378 |
|
|
int count, recvlen, dummy = 0; |
379 |
|
|
int len; |
380 |
|
|
int i; |
381 |
|
|
|
382 |
|
|
if (type == IGMP_MTRACE_QUERY) { |
383 |
|
|
group = qgrp; |
384 |
|
|
datalen = sizeof(struct tr_query); |
385 |
|
|
} else { |
386 |
|
|
group = htonl(MROUTED_LEVEL); |
387 |
|
|
datalen = 0; |
388 |
|
|
} |
389 |
|
|
if (IN_MULTICAST(ntohl(dst))) local = lcl_addr; |
390 |
|
|
else local = INADDR_ANY; |
391 |
|
|
|
392 |
|
|
/* |
393 |
|
|
* If the reply address was not explicitly specified, start off |
394 |
|
|
* with the unicast address of this host. Then, if there is no |
395 |
|
|
* response after trying half the tries with unicast, switch to |
396 |
|
|
* the standard multicast reply address. If the TTL was also not |
397 |
|
|
* specified, set a multicast TTL and if needed increase it for the |
398 |
|
|
* last quarter of the tries. |
399 |
|
|
*/ |
400 |
|
|
query = (struct tr_query *)(send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN); |
401 |
|
|
query->tr_raddr = raddr ? raddr : multicast ? resp_cast : lcl_addr; |
402 |
|
|
query->tr_rttl = rttl ? rttl : |
403 |
|
|
IN_MULTICAST(ntohl(query->tr_raddr)) ? get_ttl(save) : UNICAST_TTL; |
404 |
|
|
query->tr_src = qsrc; |
405 |
|
|
query->tr_dst = qdst; |
406 |
|
|
|
407 |
|
|
for (i = tries ; i > 0; --i) { |
408 |
|
|
if (tries == nqueries && raddr == 0) { |
409 |
|
|
if (i == ((nqueries + 1) >> 1)) { |
410 |
|
|
query->tr_raddr = resp_cast; |
411 |
|
|
if (rttl == 0) query->tr_rttl = get_ttl(save); |
412 |
|
|
} |
413 |
|
|
if (i <= ((nqueries + 3) >> 2) && rttl == 0) { |
414 |
|
|
query->tr_rttl += MULTICAST_TTL_INC; |
415 |
|
|
if (query->tr_rttl > MULTICAST_TTL_MAX) |
416 |
|
|
query->tr_rttl = MULTICAST_TTL_MAX; |
417 |
|
|
} |
418 |
|
|
} |
419 |
|
|
|
420 |
|
|
/* |
421 |
|
|
* Change the qid for each request sent to avoid being confused |
422 |
|
|
* by duplicate responses |
423 |
|
|
*/ |
424 |
|
|
query->tr_qid = arc4random(); |
425 |
|
|
|
426 |
|
|
/* |
427 |
|
|
* Set timer to calculate delays, then send query |
428 |
|
|
*/ |
429 |
|
|
gettimeofday(&tq, 0); |
430 |
|
|
send_igmp(local, dst, type, code, group, datalen); |
431 |
|
|
|
432 |
|
|
/* |
433 |
|
|
* Wait for response, discarding false alarms |
434 |
|
|
*/ |
435 |
|
|
pfd[0].fd = igmp_socket; |
436 |
|
|
pfd[0].events = POLLIN; |
437 |
|
|
while (TRUE) { |
438 |
|
|
gettimeofday(&tv, 0); |
439 |
|
|
tv.tv_sec = tq.tv_sec + timeout - tv.tv_sec; |
440 |
|
|
tv.tv_usec = tq.tv_usec - tv.tv_usec; |
441 |
|
|
if (tv.tv_usec < 0) tv.tv_usec += 1000000L, --tv.tv_sec; |
442 |
|
|
if (tv.tv_sec < 0) tv.tv_sec = tv.tv_usec = 0; |
443 |
|
|
|
444 |
|
|
count = poll(pfd, 1, tv.tv_sec * 1000); |
445 |
|
|
|
446 |
|
|
if (count < 0) { |
447 |
|
|
if (errno != EINTR) perror("poll"); |
448 |
|
|
continue; |
449 |
|
|
} else if (count == 0) { |
450 |
|
|
printf("* "); |
451 |
|
|
fflush(stdout); |
452 |
|
|
break; |
453 |
|
|
} |
454 |
|
|
|
455 |
|
|
gettimeofday(&tr, 0); |
456 |
|
|
recvlen = recvfrom(igmp_socket, recv_buf, RECV_BUF_SIZE, |
457 |
|
|
0, NULL, &dummy); |
458 |
|
|
|
459 |
|
|
if (recvlen <= 0) { |
460 |
|
|
if (recvlen && errno != EINTR) perror("recvfrom"); |
461 |
|
|
continue; |
462 |
|
|
} |
463 |
|
|
|
464 |
|
|
if (recvlen < sizeof(struct ip)) { |
465 |
|
|
fprintf(stderr, |
466 |
|
|
"packet too short (%u bytes) for IP header", recvlen); |
467 |
|
|
continue; |
468 |
|
|
} |
469 |
|
|
ip = (struct ip *) recv_buf; |
470 |
|
|
if (ip->ip_p == 0) /* ignore cache creation requests */ |
471 |
|
|
continue; |
472 |
|
|
|
473 |
|
|
iphdrlen = ip->ip_hl << 2; |
474 |
|
|
ipdatalen = ntohs(ip->ip_len) - iphdrlen; |
475 |
|
|
if (iphdrlen + ipdatalen != recvlen) { |
476 |
|
|
fprintf(stderr, |
477 |
|
|
"packet shorter (%u bytes) than hdr+data len (%u+%u)\n", |
478 |
|
|
recvlen, iphdrlen, ipdatalen); |
479 |
|
|
continue; |
480 |
|
|
} |
481 |
|
|
|
482 |
|
|
igmp = (struct igmp *) (recv_buf + iphdrlen); |
483 |
|
|
igmpdatalen = ipdatalen - IGMP_MINLEN; |
484 |
|
|
if (igmpdatalen < 0) { |
485 |
|
|
fprintf(stderr, |
486 |
|
|
"IP data field too short (%u bytes) for IGMP from %s\n", |
487 |
|
|
ipdatalen, inet_fmt(ip->ip_src.s_addr, s1)); |
488 |
|
|
continue; |
489 |
|
|
} |
490 |
|
|
|
491 |
|
|
switch (igmp->igmp_type) { |
492 |
|
|
|
493 |
|
|
case IGMP_DVMRP: |
494 |
|
|
if (igmp->igmp_code != DVMRP_NEIGHBORS2) continue; |
495 |
|
|
len = igmpdatalen; |
496 |
|
|
/* |
497 |
|
|
* Accept DVMRP_NEIGHBORS2 response if it comes from the |
498 |
|
|
* address queried or if that address is one of the local |
499 |
|
|
* addresses in the response. |
500 |
|
|
*/ |
501 |
|
|
if (ip->ip_src.s_addr != dst) { |
502 |
|
|
u_int32_t *p = (u_int32_t *)(igmp + 1); |
503 |
|
|
u_int32_t *ep = p + (len >> 2); |
504 |
|
|
while (p < ep) { |
505 |
|
|
u_int32_t laddr = *p++; |
506 |
|
|
int n = ntohl(*p++) & 0xFF; |
507 |
|
|
if (laddr == dst) { |
508 |
|
|
ep = p + 1; /* ensure p < ep after loop */ |
509 |
|
|
break; |
510 |
|
|
} |
511 |
|
|
p += n; |
512 |
|
|
} |
513 |
|
|
if (p >= ep) continue; |
514 |
|
|
} |
515 |
|
|
break; |
516 |
|
|
|
517 |
|
|
case IGMP_MTRACE_QUERY: /* For backward compatibility with 3.3 */ |
518 |
|
|
case IGMP_MTRACE_REPLY: |
519 |
|
|
if (igmpdatalen <= QLEN) continue; |
520 |
|
|
if ((igmpdatalen - QLEN)%RLEN) { |
521 |
|
|
printf("packet with incorrect datalen\n"); |
522 |
|
|
continue; |
523 |
|
|
} |
524 |
|
|
|
525 |
|
|
/* |
526 |
|
|
* Ignore responses that don't match query. |
527 |
|
|
*/ |
528 |
|
|
rquery = (struct tr_query *)(igmp + 1); |
529 |
|
|
if (rquery->tr_qid != query->tr_qid) continue; |
530 |
|
|
if (rquery->tr_src != qsrc) continue; |
531 |
|
|
if (rquery->tr_dst != qdst) continue; |
532 |
|
|
len = (igmpdatalen - QLEN)/RLEN; |
533 |
|
|
|
534 |
|
|
/* |
535 |
|
|
* Ignore trace queries passing through this node when |
536 |
|
|
* mtrace is run on an mrouter that is in the path |
537 |
|
|
* (needed only because IGMP_MTRACE_QUERY is accepted above |
538 |
|
|
* for backward compatibility with multicast release 3.3). |
539 |
|
|
*/ |
540 |
|
|
if (igmp->igmp_type == IGMP_MTRACE_QUERY) { |
541 |
|
|
struct tr_resp *r = (struct tr_resp *)(rquery+1) + len - 1; |
542 |
|
|
u_int32_t smask; |
543 |
|
|
|
544 |
|
|
VAL_TO_MASK(smask, r->tr_smask); |
545 |
|
|
if (len < code && (r->tr_inaddr & smask) != (qsrc & smask) |
546 |
|
|
&& r->tr_rmtaddr != 0 && !(r->tr_rflags & 0x80)) |
547 |
|
|
continue; |
548 |
|
|
} |
549 |
|
|
|
550 |
|
|
/* |
551 |
|
|
* A match, we'll keep this one. |
552 |
|
|
*/ |
553 |
|
|
if (len > code) { |
554 |
|
|
fprintf(stderr, |
555 |
|
|
"Num hops received (%d) exceeds request (%d)\n", |
556 |
|
|
len, code); |
557 |
|
|
} |
558 |
|
|
rquery->tr_raddr = query->tr_raddr; /* Insure these are */ |
559 |
|
|
rquery->tr_rttl = query->tr_rttl; /* as we sent them */ |
560 |
|
|
break; |
561 |
|
|
|
562 |
|
|
default: |
563 |
|
|
continue; |
564 |
|
|
} |
565 |
|
|
|
566 |
|
|
/* |
567 |
|
|
* Most of the sanity checking done at this point. |
568 |
|
|
* Return this packet we have been waiting for. |
569 |
|
|
*/ |
570 |
|
|
if (save) { |
571 |
|
|
save->qtime = ((tq.tv_sec + JAN_1970) << 16) + |
572 |
|
|
(tq.tv_usec << 10) / 15625; |
573 |
|
|
save->rtime = ((tr.tv_sec + JAN_1970) << 16) + |
574 |
|
|
(tr.tv_usec << 10) / 15625; |
575 |
|
|
save->len = len; |
576 |
|
|
bcopy((char *)igmp, (char *)&save->igmp, ipdatalen); |
577 |
|
|
} |
578 |
|
|
return (recvlen); |
579 |
|
|
} |
580 |
|
|
} |
581 |
|
|
return (0); |
582 |
|
|
} |
583 |
|
|
|
584 |
|
|
/* |
585 |
|
|
* Most of this code is duplicated elsewhere. I'm not sure if |
586 |
|
|
* the duplication is absolutely required or not. |
587 |
|
|
* |
588 |
|
|
* Ideally, this would keep track of ongoing statistics |
589 |
|
|
* collection and print out statistics. (& keep track |
590 |
|
|
* of h-b-h traces and only print the longest) For now, |
591 |
|
|
* it just snoops on what traces it can. |
592 |
|
|
*/ |
593 |
|
|
void |
594 |
|
|
passive_mode(void) |
595 |
|
|
{ |
596 |
|
|
struct timeval tr; |
597 |
|
|
struct ip *ip; |
598 |
|
|
struct igmp *igmp; |
599 |
|
|
struct tr_resp *r; |
600 |
|
|
int ipdatalen, iphdrlen, igmpdatalen; |
601 |
|
|
int len, recvlen, dummy = 0; |
602 |
|
|
u_int32_t smask; |
603 |
|
|
|
604 |
|
|
if (raddr) { |
605 |
|
|
if (IN_MULTICAST(ntohl(raddr))) k_join(raddr, INADDR_ANY); |
606 |
|
|
} else k_join(htonl(0xE0000120), INADDR_ANY); |
607 |
|
|
|
608 |
|
|
while (1) { |
609 |
|
|
recvlen = recvfrom(igmp_socket, recv_buf, RECV_BUF_SIZE, |
610 |
|
|
0, NULL, &dummy); |
611 |
|
|
gettimeofday(&tr,0); |
612 |
|
|
|
613 |
|
|
if (recvlen <= 0) { |
614 |
|
|
if (recvlen && errno != EINTR) perror("recvfrom"); |
615 |
|
|
continue; |
616 |
|
|
} |
617 |
|
|
|
618 |
|
|
if (recvlen < sizeof(struct ip)) { |
619 |
|
|
fprintf(stderr, |
620 |
|
|
"packet too short (%u bytes) for IP header", recvlen); |
621 |
|
|
continue; |
622 |
|
|
} |
623 |
|
|
ip = (struct ip *) recv_buf; |
624 |
|
|
if (ip->ip_p == 0) /* ignore cache creation requests */ |
625 |
|
|
continue; |
626 |
|
|
|
627 |
|
|
iphdrlen = ip->ip_hl << 2; |
628 |
|
|
ipdatalen = ntohs(ip->ip_len) - iphdrlen; |
629 |
|
|
if (iphdrlen + ipdatalen != recvlen) { |
630 |
|
|
fprintf(stderr, |
631 |
|
|
"packet shorter (%u bytes) than hdr+data len (%u+%u)\n", |
632 |
|
|
recvlen, iphdrlen, ipdatalen); |
633 |
|
|
continue; |
634 |
|
|
} |
635 |
|
|
|
636 |
|
|
igmp = (struct igmp *) (recv_buf + iphdrlen); |
637 |
|
|
igmpdatalen = ipdatalen - IGMP_MINLEN; |
638 |
|
|
if (igmpdatalen < 0) { |
639 |
|
|
fprintf(stderr, |
640 |
|
|
"IP data field too short (%u bytes) for IGMP from %s\n", |
641 |
|
|
ipdatalen, inet_fmt(ip->ip_src.s_addr, s1)); |
642 |
|
|
continue; |
643 |
|
|
} |
644 |
|
|
|
645 |
|
|
switch (igmp->igmp_type) { |
646 |
|
|
|
647 |
|
|
case IGMP_MTRACE_QUERY: /* For backward compatibility with 3.3 */ |
648 |
|
|
case IGMP_MTRACE_REPLY: |
649 |
|
|
if (igmpdatalen < QLEN) continue; |
650 |
|
|
if ((igmpdatalen - QLEN)%RLEN) { |
651 |
|
|
printf("packet with incorrect datalen\n"); |
652 |
|
|
continue; |
653 |
|
|
} |
654 |
|
|
|
655 |
|
|
len = (igmpdatalen - QLEN)/RLEN; |
656 |
|
|
|
657 |
|
|
break; |
658 |
|
|
|
659 |
|
|
default: |
660 |
|
|
continue; |
661 |
|
|
} |
662 |
|
|
|
663 |
|
|
base.qtime = ((tr.tv_sec + JAN_1970) << 16) + |
664 |
|
|
(tr.tv_usec << 10) / 15625; |
665 |
|
|
base.rtime = ((tr.tv_sec + JAN_1970) << 16) + |
666 |
|
|
(tr.tv_usec << 10) / 15625; |
667 |
|
|
base.len = len; |
668 |
|
|
bcopy((char *)igmp, (char *)&base.igmp, ipdatalen); |
669 |
|
|
/* |
670 |
|
|
* If the user specified which traces to monitor, |
671 |
|
|
* only accept traces that correspond to the |
672 |
|
|
* request |
673 |
|
|
*/ |
674 |
|
|
if ((qsrc != 0 && qsrc != base.qhdr.tr_src) || |
675 |
|
|
(qdst != 0 && qdst != base.qhdr.tr_dst) || |
676 |
|
|
(qgrp != 0 && qgrp != igmp->igmp_group.s_addr)) |
677 |
|
|
continue; |
678 |
|
|
|
679 |
|
|
printf("Mtrace from %s to %s via group %s (mxhop=%d)\n", |
680 |
|
|
inet_fmt(base.qhdr.tr_dst, s1), inet_fmt(base.qhdr.tr_src, s2), |
681 |
|
|
inet_fmt(igmp->igmp_group.s_addr, s3), igmp->igmp_code); |
682 |
|
|
if (len == 0) |
683 |
|
|
continue; |
684 |
|
|
printf(" 0 "); |
685 |
|
|
print_host(base.qhdr.tr_dst); |
686 |
|
|
printf("\n"); |
687 |
|
|
print_trace(1, &base); |
688 |
|
|
r = base.resps + base.len - 1; |
689 |
|
|
VAL_TO_MASK(smask, r->tr_smask); |
690 |
|
|
if ((r->tr_inaddr & smask) == (base.qhdr.tr_src & smask)) { |
691 |
|
|
printf("%3d ", -(base.len+1)); |
692 |
|
|
print_host(base.qhdr.tr_src); |
693 |
|
|
printf("\n"); |
694 |
|
|
} else if (r->tr_rmtaddr != 0) { |
695 |
|
|
printf("%3d ", -(base.len+1)); |
696 |
|
|
what_kind(&base, r->tr_rflags == TR_OLD_ROUTER ? |
697 |
|
|
"doesn't support mtrace" |
698 |
|
|
: "is the next hop"); |
699 |
|
|
} |
700 |
|
|
printf("\n"); |
701 |
|
|
} |
702 |
|
|
} |
703 |
|
|
|
704 |
|
|
char * |
705 |
|
|
print_host(u_int32_t addr) |
706 |
|
|
{ |
707 |
|
|
return print_host2(addr, 0); |
708 |
|
|
} |
709 |
|
|
|
710 |
|
|
/* |
711 |
|
|
* On some routers, one interface has a name and the other doesn't. |
712 |
|
|
* We always print the address of the outgoing interface, but can |
713 |
|
|
* sometimes get the name from the incoming interface. This might be |
714 |
|
|
* confusing but should be slightly more helpful than just a "?". |
715 |
|
|
*/ |
716 |
|
|
char * |
717 |
|
|
print_host2(u_int32_t addr1, u_int32_t addr2) |
718 |
|
|
{ |
719 |
|
|
char *name; |
720 |
|
|
|
721 |
|
|
if (numeric) { |
722 |
|
|
printf("%s", inet_fmt(addr1, s1)); |
723 |
|
|
return (""); |
724 |
|
|
} |
725 |
|
|
name = inet_name(addr1); |
726 |
|
|
if (*name == '?' && *(name + 1) == '\0' && addr2 != 0) |
727 |
|
|
name = inet_name(addr2); |
728 |
|
|
printf("%s (%s)", name, inet_fmt(addr1, s1)); |
729 |
|
|
return (name); |
730 |
|
|
} |
731 |
|
|
|
732 |
|
|
/* |
733 |
|
|
* Print responses as received (reverse path from dst to src) |
734 |
|
|
*/ |
735 |
|
|
void |
736 |
|
|
print_trace(int index, struct resp_buf *buf) |
737 |
|
|
{ |
738 |
|
|
struct tr_resp *r; |
739 |
|
|
char *name; |
740 |
|
|
int i; |
741 |
|
|
int hop; |
742 |
|
|
char *ms; |
743 |
|
|
|
744 |
|
|
i = abs(index); |
745 |
|
|
r = buf->resps + i - 1; |
746 |
|
|
|
747 |
|
|
for (; i <= buf->len; ++i, ++r) { |
748 |
|
|
if (index > 0) printf("%3d ", -i); |
749 |
|
|
name = print_host2(r->tr_outaddr, r->tr_inaddr); |
750 |
|
|
printf(" %s thresh^ %d", proto_type(r->tr_rproto), r->tr_fttl); |
751 |
|
|
if (verbose) { |
752 |
|
|
hop = t_diff(fixtime(ntohl(r->tr_qarr)), buf->qtime); |
753 |
|
|
ms = scale(&hop); |
754 |
|
|
printf(" %d%s", hop, ms); |
755 |
|
|
} |
756 |
|
|
printf(" %s\n", flag_type(r->tr_rflags)); |
757 |
|
|
memcpy(names[i-1], name, sizeof(names[0]) - 1); |
758 |
|
|
names[i-1][sizeof(names[0])-1] = '\0'; |
759 |
|
|
} |
760 |
|
|
} |
761 |
|
|
|
762 |
|
|
/* |
763 |
|
|
* See what kind of router is the next hop |
764 |
|
|
*/ |
765 |
|
|
int |
766 |
|
|
what_kind(struct resp_buf *buf, char *why) |
767 |
|
|
{ |
768 |
|
|
u_int32_t smask; |
769 |
|
|
int retval; |
770 |
|
|
int hops = buf->len; |
771 |
|
|
struct tr_resp *r = buf->resps + hops - 1; |
772 |
|
|
u_int32_t next = r->tr_rmtaddr; |
773 |
|
|
|
774 |
|
|
retval = send_recv(next, IGMP_DVMRP, DVMRP_ASK_NEIGHBORS2, 1, &incr[0]); |
775 |
|
|
print_host(next); |
776 |
|
|
if (retval) { |
777 |
|
|
u_int32_t version = ntohl(incr[0].igmp.igmp_group.s_addr); |
778 |
|
|
u_int32_t *p = (u_int32_t *)incr[0].ndata; |
779 |
|
|
u_int32_t *ep = p + (incr[0].len >> 2); |
780 |
|
|
char *type = ""; |
781 |
|
|
retval = 0; |
782 |
|
|
switch (version & 0xFF) { |
783 |
|
|
case 1: |
784 |
|
|
type = "proteon/mrouted "; |
785 |
|
|
retval = 1; |
786 |
|
|
break; |
787 |
|
|
|
788 |
|
|
case 2: |
789 |
|
|
case 3: |
790 |
|
|
if (((version >> 8) & 0xFF) < 3) retval = 1; |
791 |
|
|
/* Fall through */ |
792 |
|
|
case 4: |
793 |
|
|
type = "mrouted "; |
794 |
|
|
break; |
795 |
|
|
|
796 |
|
|
case 10: |
797 |
|
|
type = "cisco "; |
798 |
|
|
} |
799 |
|
|
printf(" [%s%d.%d] %s\n", |
800 |
|
|
type, version & 0xFF, (version >> 8) & 0xFF, |
801 |
|
|
why); |
802 |
|
|
VAL_TO_MASK(smask, r->tr_smask); |
803 |
|
|
while (p < ep) { |
804 |
|
|
u_int32_t laddr = *p++; |
805 |
|
|
int flags = (ntohl(*p) & 0xFF00) >> 8; |
806 |
|
|
int n = ntohl(*p++) & 0xFF; |
807 |
|
|
if (!(flags & (DVMRP_NF_DOWN | DVMRP_NF_DISABLED)) && |
808 |
|
|
(laddr & smask) == (qsrc & smask)) { |
809 |
|
|
printf("%3d ", -(hops+2)); |
810 |
|
|
print_host(qsrc); |
811 |
|
|
printf("\n"); |
812 |
|
|
return 1; |
813 |
|
|
} |
814 |
|
|
p += n; |
815 |
|
|
} |
816 |
|
|
return retval; |
817 |
|
|
} |
818 |
|
|
printf(" %s\n", why); |
819 |
|
|
return 0; |
820 |
|
|
} |
821 |
|
|
|
822 |
|
|
|
823 |
|
|
char * |
824 |
|
|
scale(int *hop) |
825 |
|
|
{ |
826 |
|
|
if (*hop > -1000 && *hop < 10000) return (" ms"); |
827 |
|
|
*hop /= 1000; |
828 |
|
|
if (*hop > -1000 && *hop < 10000) return (" s "); |
829 |
|
|
return ("s "); |
830 |
|
|
} |
831 |
|
|
|
832 |
|
|
/* |
833 |
|
|
* Calculate and print one line of packet loss and packet rate statistics. |
834 |
|
|
* Checks for count of all ones from mrouted 2.3 that doesn't have counters. |
835 |
|
|
*/ |
836 |
|
|
#define NEITHER 0 |
837 |
|
|
#define INS 1 |
838 |
|
|
#define OUTS 2 |
839 |
|
|
#define BOTH 3 |
840 |
|
|
void |
841 |
|
|
stat_line(struct tr_resp *r, struct tr_resp *s, int have_next, int *rst) |
842 |
|
|
{ |
843 |
|
|
int timediff = (fixtime(ntohl(s->tr_qarr)) - |
844 |
|
|
fixtime(ntohl(r->tr_qarr))) >> 16; |
845 |
|
|
int v_lost, v_pct; |
846 |
|
|
int g_lost, g_pct; |
847 |
|
|
int v_out = ntohl(s->tr_vifout) - ntohl(r->tr_vifout); |
848 |
|
|
int g_out = ntohl(s->tr_pktcnt) - ntohl(r->tr_pktcnt); |
849 |
|
|
int v_pps, g_pps; |
850 |
|
|
char v_str[8], g_str[8]; |
851 |
|
|
int have = NEITHER; |
852 |
|
|
int res = *rst; |
853 |
|
|
|
854 |
|
|
if (timediff == 0) timediff = 1; |
855 |
|
|
v_pps = v_out / timediff; |
856 |
|
|
g_pps = g_out / timediff; |
857 |
|
|
|
858 |
|
|
if ((v_out && (s->tr_vifout != 0xFFFFFFFF && s->tr_vifout != 0)) || |
859 |
|
|
(r->tr_vifout != 0xFFFFFFFF && r->tr_vifout != 0)) |
860 |
|
|
have |= OUTS; |
861 |
|
|
|
862 |
|
|
if (have_next) { |
863 |
|
|
--r, --s, --rst; |
864 |
|
|
if ((s->tr_vifin != 0xFFFFFFFF && s->tr_vifin != 0) || |
865 |
|
|
(r->tr_vifin != 0xFFFFFFFF && r->tr_vifin != 0)) |
866 |
|
|
have |= INS; |
867 |
|
|
if (*rst) |
868 |
|
|
res = 1; |
869 |
|
|
} |
870 |
|
|
|
871 |
|
|
switch (have) { |
872 |
|
|
case BOTH: |
873 |
|
|
v_lost = v_out - (ntohl(s->tr_vifin) - ntohl(r->tr_vifin)); |
874 |
|
|
if (v_out) v_pct = (v_lost * 100 + (v_out >> 1)) / v_out; |
875 |
|
|
else v_pct = 0; |
876 |
|
|
if (-100 < v_pct && v_pct < 101 && v_out > 10) |
877 |
|
|
snprintf(v_str, sizeof v_str, "%3d", v_pct); |
878 |
|
|
else memcpy(v_str, " --", 4); |
879 |
|
|
|
880 |
|
|
g_lost = g_out - (ntohl(s->tr_pktcnt) - ntohl(r->tr_pktcnt)); |
881 |
|
|
if (g_out) g_pct = (g_lost * 100 + (g_out >> 1))/ g_out; |
882 |
|
|
else g_pct = 0; |
883 |
|
|
if (-100 < g_pct && g_pct < 101 && g_out > 10) |
884 |
|
|
snprintf(g_str, sizeof g_str, "%3d", g_pct); |
885 |
|
|
else memcpy(g_str, " --", 4); |
886 |
|
|
|
887 |
|
|
printf("%6d/%-5d=%s%%%4d pps", |
888 |
|
|
v_lost, v_out, v_str, v_pps); |
889 |
|
|
if (res) |
890 |
|
|
printf("\n"); |
891 |
|
|
else |
892 |
|
|
printf("%6d/%-5d=%s%%%4d pps\n", |
893 |
|
|
g_lost, g_out, g_str, g_pps); |
894 |
|
|
break; |
895 |
|
|
|
896 |
|
|
case INS: |
897 |
|
|
v_out = ntohl(s->tr_vifin) - ntohl(r->tr_vifin); |
898 |
|
|
v_pps = v_out / timediff; |
899 |
|
|
/* Fall through */ |
900 |
|
|
|
901 |
|
|
case OUTS: |
902 |
|
|
printf(" %-5d %4d pps", |
903 |
|
|
v_out, v_pps); |
904 |
|
|
if (res) |
905 |
|
|
printf("\n"); |
906 |
|
|
else |
907 |
|
|
printf(" %-5d %4d pps\n", |
908 |
|
|
g_out, g_pps); |
909 |
|
|
break; |
910 |
|
|
|
911 |
|
|
case NEITHER: |
912 |
|
|
printf("\n"); |
913 |
|
|
break; |
914 |
|
|
} |
915 |
|
|
|
916 |
|
|
if (debug > 2) { |
917 |
|
|
printf("\t\t\t\tv_in: %u ", ntohl(s->tr_vifin)); |
918 |
|
|
printf("v_out: %u ", ntohl(s->tr_vifout)); |
919 |
|
|
printf("pkts: %u\n", ntohl(s->tr_pktcnt)); |
920 |
|
|
printf("\t\t\t\tv_in: %u ", ntohl(r->tr_vifin)); |
921 |
|
|
printf("v_out: %u ", ntohl(r->tr_vifout)); |
922 |
|
|
printf("pkts: %u\n", ntohl(r->tr_pktcnt)); |
923 |
|
|
printf("\t\t\t\tv_in: %u ", ntohl(s->tr_vifin)-ntohl(r->tr_vifin)); |
924 |
|
|
printf("v_out: %u ", ntohl(s->tr_vifout) - ntohl(r->tr_vifout)); |
925 |
|
|
printf("pkts: %u ", ntohl(s->tr_pktcnt) - ntohl(r->tr_pktcnt)); |
926 |
|
|
printf("time: %d\n", timediff); |
927 |
|
|
printf("\t\t\t\tres: %d\n", res); |
928 |
|
|
} |
929 |
|
|
} |
930 |
|
|
|
931 |
|
|
/* |
932 |
|
|
* A fixup to check if any pktcnt has been reset, and to fix the |
933 |
|
|
* byteorder bugs in mrouted 3.6 on little-endian machines. |
934 |
|
|
*/ |
935 |
|
|
void |
936 |
|
|
fixup_stats(struct resp_buf *base, struct resp_buf *prev, struct resp_buf *new) |
937 |
|
|
{ |
938 |
|
|
int rno = base->len; |
939 |
|
|
struct tr_resp *b = base->resps + rno; |
940 |
|
|
struct tr_resp *p = prev->resps + rno; |
941 |
|
|
struct tr_resp *n = new->resps + rno; |
942 |
|
|
int *r = reset + rno; |
943 |
|
|
int *s = swaps + rno; |
944 |
|
|
int res; |
945 |
|
|
|
946 |
|
|
/* Check for byte-swappers */ |
947 |
|
|
while (--rno >= 0) { |
948 |
|
|
--n; --p; --b; --s; |
949 |
|
|
if (*s || ntohl(n->tr_vifout) - ntohl(p->tr_vifout) > 100000) { |
950 |
|
|
/* This host sends byteswapped reports; swap 'em */ |
951 |
|
|
if (!*s) { |
952 |
|
|
*s = 1; |
953 |
|
|
b->tr_qarr = byteswap(b->tr_qarr); |
954 |
|
|
b->tr_vifin = byteswap(b->tr_vifin); |
955 |
|
|
b->tr_vifout = byteswap(b->tr_vifout); |
956 |
|
|
b->tr_pktcnt = byteswap(b->tr_pktcnt); |
957 |
|
|
} |
958 |
|
|
|
959 |
|
|
n->tr_qarr = byteswap(n->tr_qarr); |
960 |
|
|
n->tr_vifin = byteswap(n->tr_vifin); |
961 |
|
|
n->tr_vifout = byteswap(n->tr_vifout); |
962 |
|
|
n->tr_pktcnt = byteswap(n->tr_pktcnt); |
963 |
|
|
} |
964 |
|
|
} |
965 |
|
|
|
966 |
|
|
rno = base->len; |
967 |
|
|
b = base->resps + rno; |
968 |
|
|
p = prev->resps + rno; |
969 |
|
|
n = new->resps + rno; |
970 |
|
|
|
971 |
|
|
while (--rno >= 0) { |
972 |
|
|
--n; --p; --b; --r; |
973 |
|
|
res = ((ntohl(n->tr_pktcnt) < ntohl(b->tr_pktcnt)) || |
974 |
|
|
(ntohl(n->tr_pktcnt) < ntohl(p->tr_pktcnt))); |
975 |
|
|
if (debug > 2) |
976 |
|
|
printf("\t\tr=%d, res=%d\n", *r, res); |
977 |
|
|
if (*r) { |
978 |
|
|
if (res || *r > 1) { |
979 |
|
|
/* |
980 |
|
|
* This router appears to be a 3.4 with that nasty ol' |
981 |
|
|
* neighbor version bug, which causes it to constantly |
982 |
|
|
* reset. Just nuke the statistics for this node, and |
983 |
|
|
* don't even bother giving it the benefit of the |
984 |
|
|
* doubt from now on. |
985 |
|
|
*/ |
986 |
|
|
p->tr_pktcnt = b->tr_pktcnt = n->tr_pktcnt; |
987 |
|
|
r++; |
988 |
|
|
} else { |
989 |
|
|
/* |
990 |
|
|
* This is simply the situation that the original |
991 |
|
|
* fixup_stats was meant to deal with -- that a |
992 |
|
|
* 3.3 or 3.4 router deleted a cache entry while |
993 |
|
|
* traffic was still active. |
994 |
|
|
*/ |
995 |
|
|
*r = 0; |
996 |
|
|
break; |
997 |
|
|
} |
998 |
|
|
} else |
999 |
|
|
*r = res; |
1000 |
|
|
} |
1001 |
|
|
|
1002 |
|
|
if (rno < 0) return; |
1003 |
|
|
|
1004 |
|
|
rno = base->len; |
1005 |
|
|
b = base->resps + rno; |
1006 |
|
|
p = prev->resps + rno; |
1007 |
|
|
|
1008 |
|
|
while (--rno >= 0) (--b)->tr_pktcnt = (--p)->tr_pktcnt; |
1009 |
|
|
} |
1010 |
|
|
|
1011 |
|
|
/* |
1012 |
|
|
* Print responses with statistics for forward path (from src to dst) |
1013 |
|
|
*/ |
1014 |
|
|
int |
1015 |
|
|
print_stats(struct resp_buf *base, struct resp_buf *prev, struct resp_buf *new) |
1016 |
|
|
{ |
1017 |
|
|
int rtt, hop; |
1018 |
|
|
char *ms; |
1019 |
|
|
u_int32_t smask; |
1020 |
|
|
int rno = base->len - 1; |
1021 |
|
|
struct tr_resp *b = base->resps + rno; |
1022 |
|
|
struct tr_resp *p = prev->resps + rno; |
1023 |
|
|
struct tr_resp *n = new->resps + rno; |
1024 |
|
|
int *r = reset + rno; |
1025 |
|
|
u_long resptime = new->rtime; |
1026 |
|
|
u_long qarrtime = fixtime(ntohl(n->tr_qarr)); |
1027 |
|
|
u_int ttl = n->tr_fttl; |
1028 |
|
|
int first = (base == prev); |
1029 |
|
|
|
1030 |
|
|
VAL_TO_MASK(smask, b->tr_smask); |
1031 |
|
|
printf(" Source Response Dest"); |
1032 |
|
|
printf(" Packet Statistics For Only For Traffic\n"); |
1033 |
|
|
printf("%-15s %-15s All Multicast Traffic From %s\n", |
1034 |
|
|
((b->tr_inaddr & smask) == (qsrc & smask)) ? s1 : " * * * ", |
1035 |
|
|
inet_fmt(base->qhdr.tr_raddr, s2), inet_fmt(qsrc, s1)); |
1036 |
|
|
rtt = t_diff(resptime, new->qtime); |
1037 |
|
|
ms = scale(&rtt); |
1038 |
|
|
printf(" %c __/ rtt%5d%s Lost/Sent = Pct Rate To %s\n", |
1039 |
|
|
first ? 'v' : '|', rtt, ms, inet_fmt(qgrp, s2)); |
1040 |
|
|
if (!first) { |
1041 |
|
|
hop = t_diff(resptime, qarrtime); |
1042 |
|
|
ms = scale(&hop); |
1043 |
|
|
printf(" v / hop%5d%s", hop, ms); |
1044 |
|
|
printf(" --------------------- --------------------\n"); |
1045 |
|
|
} |
1046 |
|
|
if (debug > 2) { |
1047 |
|
|
printf("\t\t\t\tv_in: %u ", ntohl(n->tr_vifin)); |
1048 |
|
|
printf("v_out: %u ", ntohl(n->tr_vifout)); |
1049 |
|
|
printf("pkts: %u\n", ntohl(n->tr_pktcnt)); |
1050 |
|
|
printf("\t\t\t\tv_in: %u ", ntohl(b->tr_vifin)); |
1051 |
|
|
printf("v_out: %u ", ntohl(b->tr_vifout)); |
1052 |
|
|
printf("pkts: %u\n", ntohl(b->tr_pktcnt)); |
1053 |
|
|
printf("\t\t\t\tv_in: %u ", ntohl(n->tr_vifin) - ntohl(b->tr_vifin)); |
1054 |
|
|
printf("v_out: %u ", ntohl(n->tr_vifout) - ntohl(b->tr_vifout)); |
1055 |
|
|
printf("pkts: %u\n", ntohl(n->tr_pktcnt) - ntohl(b->tr_pktcnt)); |
1056 |
|
|
printf("\t\t\t\treset: %d\n", *r); |
1057 |
|
|
} |
1058 |
|
|
|
1059 |
|
|
while (TRUE) { |
1060 |
|
|
if ((n->tr_inaddr != b->tr_inaddr) || (p->tr_inaddr != b->tr_inaddr)) |
1061 |
|
|
return 1; /* Route changed */ |
1062 |
|
|
|
1063 |
|
|
if ((n->tr_inaddr != n->tr_outaddr)) |
1064 |
|
|
printf("%-15s\n", inet_fmt(n->tr_inaddr, s1)); |
1065 |
|
|
printf("%-15s %-14s %s\n", inet_fmt(n->tr_outaddr, s1), names[rno], |
1066 |
|
|
flag_type(n->tr_rflags)); |
1067 |
|
|
|
1068 |
|
|
if (rno-- < 1) break; |
1069 |
|
|
|
1070 |
|
|
printf(" %c ^ ttl%5d ", first ? 'v' : '|', ttl); |
1071 |
|
|
stat_line(p, n, TRUE, r); |
1072 |
|
|
if (!first) { |
1073 |
|
|
resptime = qarrtime; |
1074 |
|
|
qarrtime = fixtime(ntohl((n-1)->tr_qarr)); |
1075 |
|
|
hop = t_diff(resptime, qarrtime); |
1076 |
|
|
ms = scale(&hop); |
1077 |
|
|
printf(" v | hop%5d%s", hop, ms); |
1078 |
|
|
stat_line(b, n, TRUE, r); |
1079 |
|
|
} |
1080 |
|
|
|
1081 |
|
|
--b, --p, --n, --r; |
1082 |
|
|
if (ttl < n->tr_fttl) ttl = n->tr_fttl; |
1083 |
|
|
else ++ttl; |
1084 |
|
|
} |
1085 |
|
|
|
1086 |
|
|
printf(" %c \\__ ttl%5d ", first ? 'v' : '|', ttl); |
1087 |
|
|
stat_line(p, n, FALSE, r); |
1088 |
|
|
if (!first) { |
1089 |
|
|
hop = t_diff(qarrtime, new->qtime); |
1090 |
|
|
ms = scale(&hop); |
1091 |
|
|
printf(" v \\ hop%5d%s", hop, ms); |
1092 |
|
|
stat_line(b, n, FALSE, r); |
1093 |
|
|
} |
1094 |
|
|
printf("%-15s %s\n", inet_fmt(qdst, s1), inet_fmt(lcl_addr, s2)); |
1095 |
|
|
printf(" Receiver Query Source\n\n"); |
1096 |
|
|
return 0; |
1097 |
|
|
} |
1098 |
|
|
|
1099 |
|
|
|
1100 |
|
|
/*************************************************************************** |
1101 |
|
|
* main |
1102 |
|
|
***************************************************************************/ |
1103 |
|
|
|
1104 |
|
|
int |
1105 |
|
|
main(int argc, char *argv[]) |
1106 |
|
|
{ |
1107 |
|
|
int udp; |
1108 |
|
|
struct sockaddr_in addr; |
1109 |
|
|
int addrlen = sizeof(addr); |
1110 |
|
|
int recvlen; |
1111 |
|
|
struct timeval tv; |
1112 |
|
|
struct resp_buf *prev, *new; |
1113 |
|
|
struct tr_resp *r; |
1114 |
|
|
u_int32_t smask; |
1115 |
|
|
int rno; |
1116 |
|
|
int hops, nexthop, tries; |
1117 |
|
|
u_int32_t lastout = 0; |
1118 |
|
|
int numstats = 1; |
1119 |
|
|
int waittime; |
1120 |
|
|
uid_t uid; |
1121 |
|
|
|
1122 |
|
|
init_igmp(); |
1123 |
|
|
|
1124 |
|
|
uid = getuid(); |
1125 |
|
|
if (setresuid(uid, uid, uid) == -1) |
1126 |
|
|
err(1, "setresuid"); |
1127 |
|
|
|
1128 |
|
|
argv++, argc--; |
1129 |
|
|
if (argc == 0) goto usage; |
1130 |
|
|
|
1131 |
|
|
while (argc > 0 && *argv[0] == '-') { |
1132 |
|
|
char *p = *argv++; argc--; |
1133 |
|
|
p++; |
1134 |
|
|
do { |
1135 |
|
|
char c = *p++; |
1136 |
|
|
char *arg = NULL; |
1137 |
|
|
if (isdigit((unsigned char)*p)) { |
1138 |
|
|
arg = p; |
1139 |
|
|
p = ""; |
1140 |
|
|
} else if (argc > 0) arg = argv[0]; |
1141 |
|
|
switch (c) { |
1142 |
|
|
case 'd': /* Unlisted debug print option */ |
1143 |
|
|
if (arg && isdigit((unsigned char)*arg)) { |
1144 |
|
|
debug = atoi(arg); |
1145 |
|
|
if (debug < 0) debug = 0; |
1146 |
|
|
if (debug > 3) debug = 3; |
1147 |
|
|
if (arg == argv[0]) argv++, argc--; |
1148 |
|
|
break; |
1149 |
|
|
} else |
1150 |
|
|
goto usage; |
1151 |
|
|
case 'M': /* Use multicast for response */ |
1152 |
|
|
multicast = TRUE; |
1153 |
|
|
break; |
1154 |
|
|
case 'l': /* Loop updating stats indefinitely */ |
1155 |
|
|
numstats = 3153600; |
1156 |
|
|
break; |
1157 |
|
|
case 'n': /* Don't reverse map host addresses */ |
1158 |
|
|
numeric = TRUE; |
1159 |
|
|
break; |
1160 |
|
|
case 'p': /* Passive listen for traces */ |
1161 |
|
|
passive = TRUE; |
1162 |
|
|
break; |
1163 |
|
|
case 'v': /* Verbosity */ |
1164 |
|
|
verbose = TRUE; |
1165 |
|
|
break; |
1166 |
|
|
case 's': /* Short form, don't wait for stats */ |
1167 |
|
|
numstats = 0; |
1168 |
|
|
break; |
1169 |
|
|
case 'w': /* Time to wait for packet arrival */ |
1170 |
|
|
if (arg && isdigit((unsigned char)*arg)) { |
1171 |
|
|
timeout = atoi(arg); |
1172 |
|
|
if (timeout < 1) timeout = 1; |
1173 |
|
|
if (arg == argv[0]) argv++, argc--; |
1174 |
|
|
break; |
1175 |
|
|
} else |
1176 |
|
|
goto usage; |
1177 |
|
|
case 'm': /* Max number of hops to trace */ |
1178 |
|
|
if (arg && isdigit((unsigned char)*arg)) { |
1179 |
|
|
qno = atoi(arg); |
1180 |
|
|
if (qno > MAXHOPS) qno = MAXHOPS; |
1181 |
|
|
else if (qno < 1) qno = 0; |
1182 |
|
|
if (arg == argv[0]) argv++, argc--; |
1183 |
|
|
break; |
1184 |
|
|
} else |
1185 |
|
|
goto usage; |
1186 |
|
|
case 'q': /* Number of query retries */ |
1187 |
|
|
if (arg && isdigit((unsigned char)*arg)) { |
1188 |
|
|
nqueries = atoi(arg); |
1189 |
|
|
if (nqueries < 1) nqueries = 1; |
1190 |
|
|
if (arg == argv[0]) argv++, argc--; |
1191 |
|
|
break; |
1192 |
|
|
} else |
1193 |
|
|
goto usage; |
1194 |
|
|
case 'g': /* Last-hop gateway (dest of query) */ |
1195 |
|
|
if (arg && (gwy = host_addr(arg))) { |
1196 |
|
|
if (arg == argv[0]) argv++, argc--; |
1197 |
|
|
break; |
1198 |
|
|
} else |
1199 |
|
|
goto usage; |
1200 |
|
|
case 't': /* TTL for query packet */ |
1201 |
|
|
if (arg && isdigit((unsigned char)*arg)) { |
1202 |
|
|
qttl = atoi(arg); |
1203 |
|
|
if (qttl < 1) qttl = 1; |
1204 |
|
|
rttl = qttl; |
1205 |
|
|
if (arg == argv[0]) argv++, argc--; |
1206 |
|
|
break; |
1207 |
|
|
} else |
1208 |
|
|
goto usage; |
1209 |
|
|
case 'r': /* Dest for response packet */ |
1210 |
|
|
if (arg && (raddr = host_addr(arg))) { |
1211 |
|
|
if (arg == argv[0]) argv++, argc--; |
1212 |
|
|
break; |
1213 |
|
|
} else |
1214 |
|
|
goto usage; |
1215 |
|
|
case 'i': /* Local interface address */ |
1216 |
|
|
if (arg && (lcl_addr = host_addr(arg))) { |
1217 |
|
|
if (arg == argv[0]) argv++, argc--; |
1218 |
|
|
break; |
1219 |
|
|
} else |
1220 |
|
|
goto usage; |
1221 |
|
|
case 'S': /* Stat accumulation interval */ |
1222 |
|
|
if (arg && isdigit((unsigned char)*arg)) { |
1223 |
|
|
statint = atoi(arg); |
1224 |
|
|
if (statint < 1) statint = 1; |
1225 |
|
|
if (arg == argv[0]) argv++, argc--; |
1226 |
|
|
break; |
1227 |
|
|
} else |
1228 |
|
|
goto usage; |
1229 |
|
|
default: |
1230 |
|
|
goto usage; |
1231 |
|
|
} |
1232 |
|
|
} while (*p); |
1233 |
|
|
} |
1234 |
|
|
|
1235 |
|
|
if (argc > 0 && (qsrc = host_addr(argv[0]))) { /* Source of path */ |
1236 |
|
|
if (IN_MULTICAST(ntohl(qsrc))) goto usage; |
1237 |
|
|
argv++, argc--; |
1238 |
|
|
if (argc > 0 && (qdst = host_addr(argv[0]))) { /* Dest of path */ |
1239 |
|
|
argv++, argc--; |
1240 |
|
|
if (argc > 0 && (qgrp = host_addr(argv[0]))) { /* Path via group */ |
1241 |
|
|
argv++, argc--; |
1242 |
|
|
} |
1243 |
|
|
if (IN_MULTICAST(ntohl(qdst))) { |
1244 |
|
|
u_int32_t temp = qdst; |
1245 |
|
|
qdst = qgrp; |
1246 |
|
|
qgrp = temp; |
1247 |
|
|
if (IN_MULTICAST(ntohl(qdst))) goto usage; |
1248 |
|
|
} else if (qgrp && !IN_MULTICAST(ntohl(qgrp))) goto usage; |
1249 |
|
|
} |
1250 |
|
|
} |
1251 |
|
|
|
1252 |
|
|
if (passive) { |
1253 |
|
|
passive_mode(); |
1254 |
|
|
return(0); |
1255 |
|
|
} |
1256 |
|
|
|
1257 |
|
|
if (argc > 0 || qsrc == 0) { |
1258 |
|
|
usage: printf("\ |
1259 |
|
|
usage: mtrace [-lMnpsv] [-g gateway] [-i if_addr] [-m max_hops] [-q nqueries]\n\ |
1260 |
|
|
[-r host] [-S stat_int] [-t ttl] [-w waittime] source [receiver]\n\ |
1261 |
|
|
[group]\n"); |
1262 |
|
|
exit(1); |
1263 |
|
|
} |
1264 |
|
|
|
1265 |
|
|
/* |
1266 |
|
|
* Set useful defaults for as many parameters as possible. |
1267 |
|
|
*/ |
1268 |
|
|
|
1269 |
|
|
defgrp = htonl(0xE0020001); /* MBone Audio (224.2.0.1) */ |
1270 |
|
|
query_cast = htonl(0xE0000002); /* All routers multicast addr */ |
1271 |
|
|
resp_cast = htonl(0xE0000120); /* Mtrace response multicast addr */ |
1272 |
|
|
if (qgrp == 0) qgrp = defgrp; |
1273 |
|
|
|
1274 |
|
|
/* |
1275 |
|
|
* Get default local address for multicasts to use in setting defaults. |
1276 |
|
|
*/ |
1277 |
|
|
memset(&addr, 0, sizeof addr); |
1278 |
|
|
addr.sin_family = AF_INET; |
1279 |
|
|
addr.sin_len = sizeof(addr); |
1280 |
|
|
addr.sin_addr.s_addr = qgrp; |
1281 |
|
|
addr.sin_port = htons(2000); /* Any port above 1024 will do */ |
1282 |
|
|
|
1283 |
|
|
if (((udp = socket(AF_INET, SOCK_DGRAM, 0)) < 0) || |
1284 |
|
|
(connect(udp, (struct sockaddr *) &addr, sizeof(addr)) < 0) || |
1285 |
|
|
getsockname(udp, (struct sockaddr *) &addr, &addrlen) < 0) { |
1286 |
|
|
perror("Determining local address"); |
1287 |
|
|
exit(1); |
1288 |
|
|
} |
1289 |
|
|
|
1290 |
|
|
#ifdef SUNOS5 |
1291 |
|
|
/* |
1292 |
|
|
* SunOS 5.X prior to SunOS 2.6, getsockname returns 0 for udp socket. |
1293 |
|
|
* This call to sysinfo will return the hostname. |
1294 |
|
|
* If the default multicast interface (set with the route |
1295 |
|
|
* for 224.0.0.0) is not the same as the hostname, |
1296 |
|
|
* mtrace -i [if_addr] will have to be used. |
1297 |
|
|
*/ |
1298 |
|
|
if (addr.sin_addr.s_addr == 0) { |
1299 |
|
|
char myhostname[HOST_NAME_MAX+1]; |
1300 |
|
|
struct hostent *hp; |
1301 |
|
|
int error; |
1302 |
|
|
|
1303 |
|
|
error = sysinfo(SI_HOSTNAME, myhostname, sizeof(myhostname)); |
1304 |
|
|
if (error == -1) { |
1305 |
|
|
perror("Getting my hostname"); |
1306 |
|
|
exit(1); |
1307 |
|
|
} |
1308 |
|
|
|
1309 |
|
|
hp = gethostbyname(myhostname); |
1310 |
|
|
if (hp == NULL || hp->h_addrtype != AF_INET || |
1311 |
|
|
hp->h_length != sizeof(addr.sin_addr)) { |
1312 |
|
|
perror("Finding IP address for my hostname"); |
1313 |
|
|
exit(1); |
1314 |
|
|
} |
1315 |
|
|
|
1316 |
|
|
memcpy((char *)&addr.sin_addr.s_addr, hp->h_addr, hp->h_length); |
1317 |
|
|
} |
1318 |
|
|
#endif |
1319 |
|
|
|
1320 |
|
|
/* |
1321 |
|
|
* Default destination for path to be queried is the local host. |
1322 |
|
|
*/ |
1323 |
|
|
if (qdst == 0) qdst = lcl_addr ? lcl_addr : addr.sin_addr.s_addr; |
1324 |
|
|
dst_netmask = get_netmask(udp, qdst); |
1325 |
|
|
close(udp); |
1326 |
|
|
if (lcl_addr == 0) lcl_addr = addr.sin_addr.s_addr; |
1327 |
|
|
|
1328 |
|
|
/* |
1329 |
|
|
* Protect against unicast queries to mrouted versions that might crash. |
1330 |
|
|
*/ |
1331 |
|
|
if (gwy && !IN_MULTICAST(ntohl(gwy))) |
1332 |
|
|
if (send_recv(gwy, IGMP_DVMRP, DVMRP_ASK_NEIGHBORS2, 1, &incr[0])) { |
1333 |
|
|
int version = ntohl(incr[0].igmp.igmp_group.s_addr) & 0xFFFF; |
1334 |
|
|
if (version == 0x0303 || version == 0x0503) { |
1335 |
|
|
printf("Don't use -g to address an mrouted 3.%d, it might crash\n", |
1336 |
|
|
(version >> 8) & 0xFF); |
1337 |
|
|
exit(0); |
1338 |
|
|
} |
1339 |
|
|
} |
1340 |
|
|
|
1341 |
|
|
printf("Mtrace from %s to %s via group %s\n", |
1342 |
|
|
inet_fmt(qsrc, s1), inet_fmt(qdst, s2), inet_fmt(qgrp, s3)); |
1343 |
|
|
|
1344 |
|
|
if ((qdst & dst_netmask) == (qsrc & dst_netmask)) { |
1345 |
|
|
printf("Source & receiver are directly connected, no path to trace\n"); |
1346 |
|
|
exit(0); |
1347 |
|
|
} |
1348 |
|
|
|
1349 |
|
|
/* |
1350 |
|
|
* If the response is to be a multicast address, make sure we |
1351 |
|
|
* are listening on that multicast address. |
1352 |
|
|
*/ |
1353 |
|
|
if (raddr) { |
1354 |
|
|
if (IN_MULTICAST(ntohl(raddr))) k_join(raddr, lcl_addr); |
1355 |
|
|
} else k_join(resp_cast, lcl_addr); |
1356 |
|
|
|
1357 |
|
|
/* |
1358 |
|
|
* If the destination is on the local net, the last-hop router can |
1359 |
|
|
* be found by multicast to the all-routers multicast group. |
1360 |
|
|
* Otherwise, use the group address that is the subject of the |
1361 |
|
|
* query since by definition the last-hop router will be a member. |
1362 |
|
|
* Set default TTLs for local remote multicasts. |
1363 |
|
|
*/ |
1364 |
|
|
restart: |
1365 |
|
|
|
1366 |
|
|
if (gwy == 0) |
1367 |
|
|
if ((qdst & dst_netmask) == (lcl_addr & dst_netmask)) tdst = query_cast; |
1368 |
|
|
else tdst = qgrp; |
1369 |
|
|
else tdst = gwy; |
1370 |
|
|
|
1371 |
|
|
if (IN_MULTICAST(ntohl(tdst))) { |
1372 |
|
|
k_set_loop(1); /* If I am running on a router, I need to hear this */ |
1373 |
|
|
if (tdst == query_cast) k_set_ttl(qttl ? qttl : 1); |
1374 |
|
|
else k_set_ttl(qttl ? qttl : MULTICAST_TTL1); |
1375 |
|
|
} |
1376 |
|
|
|
1377 |
|
|
/* |
1378 |
|
|
* Try a query at the requested number of hops or MAXHOPS if unspecified. |
1379 |
|
|
*/ |
1380 |
|
|
if (qno == 0) { |
1381 |
|
|
hops = MAXHOPS; |
1382 |
|
|
tries = 1; |
1383 |
|
|
printf("Querying full reverse path... "); |
1384 |
|
|
fflush(stdout); |
1385 |
|
|
} else { |
1386 |
|
|
hops = qno; |
1387 |
|
|
tries = nqueries; |
1388 |
|
|
printf("Querying reverse path, maximum %d hops... ", qno); |
1389 |
|
|
fflush(stdout); |
1390 |
|
|
} |
1391 |
|
|
base.rtime = 0; |
1392 |
|
|
base.len = 0; |
1393 |
|
|
|
1394 |
|
|
recvlen = send_recv(tdst, IGMP_MTRACE_QUERY, hops, tries, &base); |
1395 |
|
|
|
1396 |
|
|
/* |
1397 |
|
|
* If the initial query was successful, print it. Otherwise, if |
1398 |
|
|
* the query max hop count is the default of zero, loop starting |
1399 |
|
|
* from one until there is no response for four hops. The extra |
1400 |
|
|
* hops allow getting past an mtrace-capable mrouter that can't |
1401 |
|
|
* send multicast packets because all phyints are disabled. |
1402 |
|
|
*/ |
1403 |
|
|
if (recvlen) { |
1404 |
|
|
printf("\n 0 "); |
1405 |
|
|
print_host(qdst); |
1406 |
|
|
printf("\n"); |
1407 |
|
|
print_trace(1, &base); |
1408 |
|
|
r = base.resps + base.len - 1; |
1409 |
|
|
if (r->tr_rflags == TR_OLD_ROUTER || r->tr_rflags == TR_NO_SPACE || |
1410 |
|
|
qno != 0) { |
1411 |
|
|
printf("%3d ", -(base.len+1)); |
1412 |
|
|
what_kind(&base, r->tr_rflags == TR_OLD_ROUTER ? |
1413 |
|
|
"doesn't support mtrace" |
1414 |
|
|
: "is the next hop"); |
1415 |
|
|
} else { |
1416 |
|
|
VAL_TO_MASK(smask, r->tr_smask); |
1417 |
|
|
if ((r->tr_inaddr & smask) == (qsrc & smask)) { |
1418 |
|
|
printf("%3d ", -(base.len+1)); |
1419 |
|
|
print_host(qsrc); |
1420 |
|
|
printf("\n"); |
1421 |
|
|
} |
1422 |
|
|
} |
1423 |
|
|
} else if (qno == 0) { |
1424 |
|
|
printf("switching to hop-by-hop:\n 0 "); |
1425 |
|
|
print_host(qdst); |
1426 |
|
|
printf("\n"); |
1427 |
|
|
|
1428 |
|
|
for (hops = 1, nexthop = 1; hops <= MAXHOPS; ++hops) { |
1429 |
|
|
printf("%3d ", -hops); |
1430 |
|
|
fflush(stdout); |
1431 |
|
|
|
1432 |
|
|
/* |
1433 |
|
|
* After a successful first hop, try switching to the unicast |
1434 |
|
|
* address of the last-hop router instead of multicasting the |
1435 |
|
|
* trace query. This should be safe for mrouted versions 3.3 |
1436 |
|
|
* and 3.5 because there is a long route timeout with metric |
1437 |
|
|
* infinity before a route disappears. Switching to unicast |
1438 |
|
|
* reduces the amount of multicast traffic and avoids a bug |
1439 |
|
|
* with duplicate suppression in mrouted 3.5. |
1440 |
|
|
*/ |
1441 |
|
|
if (hops == 2 && gwy == 0 && |
1442 |
|
|
(recvlen = send_recv(lastout, IGMP_MTRACE_QUERY, hops, 1, &base))) |
1443 |
|
|
tdst = lastout; |
1444 |
|
|
else recvlen = send_recv(tdst, IGMP_MTRACE_QUERY, hops, nqueries, &base); |
1445 |
|
|
|
1446 |
|
|
if (recvlen == 0) { |
1447 |
|
|
if (hops == 1) break; |
1448 |
|
|
if (hops == nexthop) { |
1449 |
|
|
if (what_kind(&base, "didn't respond")) { |
1450 |
|
|
/* the ask_neighbors determined that the |
1451 |
|
|
* not-responding router is the first-hop. */ |
1452 |
|
|
break; |
1453 |
|
|
} |
1454 |
|
|
} else if (hops < nexthop + 3) { |
1455 |
|
|
printf("\n"); |
1456 |
|
|
} else { |
1457 |
|
|
printf("...giving up\n"); |
1458 |
|
|
break; |
1459 |
|
|
} |
1460 |
|
|
continue; |
1461 |
|
|
} |
1462 |
|
|
r = base.resps + base.len - 1; |
1463 |
|
|
if (base.len == hops && |
1464 |
|
|
(hops == 1 || (base.resps+nexthop-2)->tr_outaddr == lastout)) { |
1465 |
|
|
if (hops == nexthop) { |
1466 |
|
|
print_trace(-hops, &base); |
1467 |
|
|
} else { |
1468 |
|
|
printf("\nResuming...\n"); |
1469 |
|
|
print_trace(nexthop, &base); |
1470 |
|
|
} |
1471 |
|
|
} else { |
1472 |
|
|
if (base.len < hops) { |
1473 |
|
|
/* |
1474 |
|
|
* A shorter trace than requested means a fatal error |
1475 |
|
|
* occurred along the path, or that the route changed |
1476 |
|
|
* to a shorter one. |
1477 |
|
|
* |
1478 |
|
|
* If the trace is longer than the last one we received, |
1479 |
|
|
* then we are resuming from a skipped router (but there |
1480 |
|
|
* is still probably a problem). |
1481 |
|
|
* |
1482 |
|
|
* If the trace is shorter than the last one we |
1483 |
|
|
* received, then the route must have changed (and |
1484 |
|
|
* there is still probably a problem). |
1485 |
|
|
*/ |
1486 |
|
|
if (nexthop <= base.len) { |
1487 |
|
|
printf("\nResuming...\n"); |
1488 |
|
|
print_trace(nexthop, &base); |
1489 |
|
|
} else if (nexthop > base.len + 1) { |
1490 |
|
|
hops = base.len; |
1491 |
|
|
printf("\nRoute must have changed...\n"); |
1492 |
|
|
print_trace(1, &base); |
1493 |
|
|
} |
1494 |
|
|
} else { |
1495 |
|
|
/* |
1496 |
|
|
* The last hop address is not the same as it was; |
1497 |
|
|
* the route probably changed underneath us. |
1498 |
|
|
*/ |
1499 |
|
|
hops = base.len; |
1500 |
|
|
printf("\nRoute must have changed...\n"); |
1501 |
|
|
print_trace(1, &base); |
1502 |
|
|
} |
1503 |
|
|
} |
1504 |
|
|
lastout = r->tr_outaddr; |
1505 |
|
|
|
1506 |
|
|
if (base.len < hops || |
1507 |
|
|
r->tr_rmtaddr == 0 || |
1508 |
|
|
(r->tr_rflags & 0x80)) { |
1509 |
|
|
VAL_TO_MASK(smask, r->tr_smask); |
1510 |
|
|
if (r->tr_rmtaddr) { |
1511 |
|
|
if (hops != nexthop) { |
1512 |
|
|
printf("\n%3d ", -(base.len+1)); |
1513 |
|
|
} |
1514 |
|
|
what_kind(&base, r->tr_rflags == TR_OLD_ROUTER ? |
1515 |
|
|
"doesn't support mtrace" : |
1516 |
|
|
"would be the next hop"); |
1517 |
|
|
/* XXX could do segmented trace if TR_NO_SPACE */ |
1518 |
|
|
} else if (r->tr_rflags == TR_NO_ERR && |
1519 |
|
|
(r->tr_inaddr & smask) == (qsrc & smask)) { |
1520 |
|
|
printf("%3d ", -(hops + 1)); |
1521 |
|
|
print_host(qsrc); |
1522 |
|
|
printf("\n"); |
1523 |
|
|
} |
1524 |
|
|
break; |
1525 |
|
|
} |
1526 |
|
|
|
1527 |
|
|
nexthop = hops + 1; |
1528 |
|
|
} |
1529 |
|
|
} |
1530 |
|
|
|
1531 |
|
|
if (base.rtime == 0) { |
1532 |
|
|
printf("Timed out receiving responses\n"); |
1533 |
|
|
if (IN_MULTICAST(ntohl(tdst))) { |
1534 |
|
|
if (tdst == query_cast) |
1535 |
|
|
printf("Perhaps no local router has a route for source %s\n", |
1536 |
|
|
inet_fmt(qsrc, s1)); |
1537 |
|
|
else |
1538 |
|
|
printf("Perhaps receiver %s is not a member of group %s,\n\ |
1539 |
|
|
or no router local to it has a route for source %s,\n\ |
1540 |
|
|
or multicast at ttl %d doesn't reach its last-hop router for that source\n", |
1541 |
|
|
inet_fmt(qdst, s2), inet_fmt(qgrp, s3), inet_fmt(qsrc, s1), |
1542 |
|
|
qttl ? qttl : MULTICAST_TTL1); |
1543 |
|
|
} |
1544 |
|
|
exit(1); |
1545 |
|
|
} |
1546 |
|
|
|
1547 |
|
|
printf("Round trip time %d ms\n\n", t_diff(base.rtime, base.qtime)); |
1548 |
|
|
|
1549 |
|
|
/* |
1550 |
|
|
* Use the saved response which was the longest one received, |
1551 |
|
|
* and make additional probes after delay to measure loss. |
1552 |
|
|
*/ |
1553 |
|
|
raddr = base.qhdr.tr_raddr; |
1554 |
|
|
rttl = base.qhdr.tr_rttl; |
1555 |
|
|
gettimeofday(&tv, 0); |
1556 |
|
|
waittime = statint - (((tv.tv_sec + JAN_1970) & 0xFFFF) - (base.qtime >> 16)); |
1557 |
|
|
prev = &base; |
1558 |
|
|
new = &incr[numstats&1]; |
1559 |
|
|
|
1560 |
|
|
while (numstats--) { |
1561 |
|
|
if (waittime < 1) |
1562 |
|
|
printf("\n"); |
1563 |
|
|
else { |
1564 |
|
|
printf("Waiting to accumulate statistics... "); |
1565 |
|
|
fflush(stdout); |
1566 |
|
|
sleep((unsigned int)waittime); |
1567 |
|
|
} |
1568 |
|
|
rno = base.len; |
1569 |
|
|
recvlen = send_recv(tdst, IGMP_MTRACE_QUERY, rno, nqueries, new); |
1570 |
|
|
|
1571 |
|
|
if (recvlen == 0) { |
1572 |
|
|
printf("Timed out.\n"); |
1573 |
|
|
exit(1); |
1574 |
|
|
} |
1575 |
|
|
|
1576 |
|
|
if (rno != new->len) { |
1577 |
|
|
printf("Trace length doesn't match:\n"); |
1578 |
|
|
/* |
1579 |
|
|
* XXX Should this trace result be printed, or is that |
1580 |
|
|
* too verbose? Perhaps it should just say restarting. |
1581 |
|
|
* But if the path is changing quickly, this may be the |
1582 |
|
|
* only snapshot of the current path. But, if the path |
1583 |
|
|
* is changing that quickly, does the current path really |
1584 |
|
|
* matter? |
1585 |
|
|
*/ |
1586 |
|
|
print_trace(1, new); |
1587 |
|
|
printf("Restarting.\n\n"); |
1588 |
|
|
numstats++; |
1589 |
|
|
goto restart; |
1590 |
|
|
} |
1591 |
|
|
|
1592 |
|
|
printf("Results after %d seconds:\n\n", |
1593 |
|
|
(int)((new->qtime - base.qtime) >> 16)); |
1594 |
|
|
fixup_stats(&base, prev, new); |
1595 |
|
|
if (print_stats(&base, prev, new)) { |
1596 |
|
|
printf("Route changed:\n"); |
1597 |
|
|
print_trace(1, new); |
1598 |
|
|
printf("Restarting.\n\n"); |
1599 |
|
|
goto restart; |
1600 |
|
|
} |
1601 |
|
|
prev = new; |
1602 |
|
|
new = &incr[numstats&1]; |
1603 |
|
|
waittime = statint; |
1604 |
|
|
} |
1605 |
|
|
|
1606 |
|
|
/* |
1607 |
|
|
* If the response was multicast back, leave the group |
1608 |
|
|
*/ |
1609 |
|
|
if (raddr) { |
1610 |
|
|
if (IN_MULTICAST(ntohl(raddr))) k_leave(raddr, lcl_addr); |
1611 |
|
|
} else k_leave(resp_cast, lcl_addr); |
1612 |
|
|
|
1613 |
|
|
return (0); |
1614 |
|
|
} |
1615 |
|
|
|
1616 |
|
|
void |
1617 |
|
|
check_vif_state(void) |
1618 |
|
|
{ |
1619 |
|
|
logit(LOG_WARNING, errno, "sendto"); |
1620 |
|
|
} |
1621 |
|
|
|
1622 |
|
|
/* |
1623 |
|
|
* Log errors and other messages to stderr, according to the severity |
1624 |
|
|
* of the message and the current debug level. For errors of severity |
1625 |
|
|
* LOG_ERR or worse, terminate the program. |
1626 |
|
|
*/ |
1627 |
|
|
void |
1628 |
|
|
logit(int severity, int syserr, char *format, ...) |
1629 |
|
|
{ |
1630 |
|
|
va_list ap; |
1631 |
|
|
|
1632 |
|
|
switch (debug) { |
1633 |
|
|
case 0: if (severity > LOG_WARNING) return; |
1634 |
|
|
case 1: if (severity > LOG_NOTICE) return; |
1635 |
|
|
case 2: if (severity > LOG_INFO ) return; |
1636 |
|
|
default: |
1637 |
|
|
if (severity == LOG_WARNING) |
1638 |
|
|
fprintf(stderr, "warning - "); |
1639 |
|
|
va_start(ap, format); |
1640 |
|
|
vfprintf(stderr, format, ap); |
1641 |
|
|
va_end(ap); |
1642 |
|
|
if (syserr == 0) |
1643 |
|
|
fprintf(stderr, "\n"); |
1644 |
|
|
else if(syserr < sys_nerr) |
1645 |
|
|
fprintf(stderr, ": %s\n", sys_errlist[syserr]); |
1646 |
|
|
else |
1647 |
|
|
fprintf(stderr, ": errno %d\n", syserr); |
1648 |
|
|
} |
1649 |
|
|
if (severity <= LOG_ERR) exit(1); |
1650 |
|
|
} |
1651 |
|
|
|
1652 |
|
|
/* dummies */ |
1653 |
|
|
void accept_probe(u_int32_t src, u_int32_t dst, char *p, int datalen, |
1654 |
|
|
u_int32_t level) |
1655 |
|
|
{ |
1656 |
|
|
} |
1657 |
|
|
|
1658 |
|
|
void accept_group_report(u_int32_t src, u_int32_t dst, u_int32_t group, |
1659 |
|
|
int r_type) |
1660 |
|
|
{ |
1661 |
|
|
} |
1662 |
|
|
|
1663 |
|
|
void accept_neighbor_request2(u_int32_t src, u_int32_t dst) |
1664 |
|
|
{ |
1665 |
|
|
} |
1666 |
|
|
|
1667 |
|
|
void accept_report(u_int32_t src, u_int32_t dst, char *p, int datalen, |
1668 |
|
|
u_int32_t level) |
1669 |
|
|
{ |
1670 |
|
|
} |
1671 |
|
|
|
1672 |
|
|
void accept_neighbor_request(u_int32_t src, u_int32_t dst) |
1673 |
|
|
{ |
1674 |
|
|
} |
1675 |
|
|
|
1676 |
|
|
void accept_prune(u_int32_t src, u_int32_t dst, char *p, int datalen) |
1677 |
|
|
{ |
1678 |
|
|
} |
1679 |
|
|
|
1680 |
|
|
void accept_graft(u_int32_t src, u_int32_t dst, char *p, int datalen) |
1681 |
|
|
{ |
1682 |
|
|
} |
1683 |
|
|
|
1684 |
|
|
void accept_g_ack(u_int32_t src, u_int32_t dst, char *p, int datalen) |
1685 |
|
|
{ |
1686 |
|
|
} |
1687 |
|
|
|
1688 |
|
|
void add_table_entry(u_int32_t origin, u_int32_t mcastgrp) |
1689 |
|
|
{ |
1690 |
|
|
} |
1691 |
|
|
|
1692 |
|
|
void accept_leave_message(u_int32_t src, u_int32_t dst, u_int32_t group) |
1693 |
|
|
{ |
1694 |
|
|
} |
1695 |
|
|
|
1696 |
|
|
void accept_mtrace(u_int32_t src, u_int32_t dst, u_int32_t group, char *data, |
1697 |
|
|
u_int no, int datalen) |
1698 |
|
|
{ |
1699 |
|
|
} |
1700 |
|
|
|
1701 |
|
|
void accept_membership_query(u_int32_t src, u_int32_t dst, u_int32_t group, |
1702 |
|
|
int tmo) |
1703 |
|
|
{ |
1704 |
|
|
} |
1705 |
|
|
|
1706 |
|
|
void accept_neighbors(u_int32_t src, u_int32_t dst, u_char *p, int datalen, |
1707 |
|
|
u_int32_t level) |
1708 |
|
|
{ |
1709 |
|
|
} |
1710 |
|
|
|
1711 |
|
|
void accept_neighbors2(u_int32_t src, u_int32_t dst, u_char *p, int datalen, |
1712 |
|
|
u_int32_t level) |
1713 |
|
|
{ |
1714 |
|
|
} |
1715 |
|
|
|
1716 |
|
|
void accept_info_request(u_int32_t src, u_int32_t dst, u_char *p, int datalen) |
1717 |
|
|
{ |
1718 |
|
|
} |
1719 |
|
|
|
1720 |
|
|
void accept_info_reply(u_int32_t src, u_int32_t dst, u_char *p, int datalen) |
1721 |
|
|
{ |
1722 |
|
|
} |