23 |
*/ |
*/ |
24 |
|
|
25 |
/* |
/* |
26 |
|
* traceroute6 |
27 |
|
* |
28 |
|
* Modified for NRL 4.4BSD IPv6 release. |
29 |
|
* 07/31/96 bgp |
30 |
|
* |
31 |
|
* Modified for Linux IPv6 by Pedro Roque <roque@di.fc.ul.pt> |
32 |
|
* 31/07/1996 |
33 |
|
* |
34 |
|
* As ICMP error messages for IPv6 now include more than 8 bytes |
35 |
|
* UDP datagrams are now sent via an UDP socket instead of magic |
36 |
|
* RAW socket tricks. |
37 |
|
* |
38 |
|
* Converted to busybox applet by Leonid Lisovskiy <lly@sf.net> |
39 |
|
* 2009-11-16 |
40 |
|
*/ |
41 |
|
|
42 |
|
/* |
43 |
* traceroute host - trace the route ip packets follow going to "host". |
* traceroute host - trace the route ip packets follow going to "host". |
44 |
* |
* |
45 |
* Attempt to trace the route an ip packet would follow to some |
* Attempt to trace the route an ip packet would follow to some |
228 |
#include <netinet/udp.h> |
#include <netinet/udp.h> |
229 |
#include <netinet/ip.h> |
#include <netinet/ip.h> |
230 |
#include <netinet/ip_icmp.h> |
#include <netinet/ip_icmp.h> |
231 |
|
#if ENABLE_FEATURE_IPV6 |
232 |
|
# include <netinet/ip6.h> |
233 |
|
# include <netinet/icmp6.h> |
234 |
|
# ifndef SOL_IPV6 |
235 |
|
# define SOL_IPV6 IPPROTO_IPV6 |
236 |
|
# endif |
237 |
|
#endif |
238 |
|
|
239 |
#include "libbb.h" |
#include "libbb.h" |
240 |
#include "inet_common.h" |
#include "inet_common.h" |
241 |
|
|
|
|
|
|
/* |
|
|
* Definitions for internet protocol version 4. |
|
|
* Per RFC 791, September 1981. |
|
|
*/ |
|
|
#define IPVERSION 4 |
|
|
|
|
242 |
#ifndef IPPROTO_ICMP |
#ifndef IPPROTO_ICMP |
243 |
/* Grrrr.... */ |
# define IPPROTO_ICMP 1 |
|
#define IPPROTO_ICMP 1 |
|
244 |
#endif |
#endif |
245 |
#ifndef IPPROTO_IP |
#ifndef IPPROTO_IP |
246 |
#define IPPROTO_IP 0 |
# define IPPROTO_IP 0 |
247 |
#endif |
#endif |
248 |
|
|
|
/* |
|
|
* Overlay for ip header used by other protocols (tcp, udp). |
|
|
*/ |
|
|
struct ipovly { |
|
|
unsigned char ih_x1[9]; /* (unused) */ |
|
|
unsigned char ih_pr; /* protocol */ |
|
|
short ih_len; /* protocol length */ |
|
|
struct in_addr ih_src; /* source internet address */ |
|
|
struct in_addr ih_dst; /* destination internet address */ |
|
|
}; |
|
249 |
|
|
250 |
/* |
#define OPT_STRING "FIlnrdvxt:i:m:p:q:s:w:z:f:" \ |
251 |
* UDP kernel structures and variables. |
IF_FEATURE_TRACEROUTE_SOURCE_ROUTE("g:") \ |
252 |
*/ |
"4" IF_TRACEROUTE6("6") |
253 |
struct udpiphdr { |
enum { |
254 |
struct ipovly ui_i; /* overlaid ip structure */ |
OPT_DONT_FRAGMNT = (1 << 0), /* F */ |
255 |
struct udphdr ui_u; /* udp header */ |
OPT_USE_ICMP = (1 << 1) * ENABLE_FEATURE_TRACEROUTE_USE_ICMP, /* I */ |
256 |
|
OPT_TTL_FLAG = (1 << 2), /* l */ |
257 |
|
OPT_ADDR_NUM = (1 << 3), /* n */ |
258 |
|
OPT_BYPASS_ROUTE = (1 << 4), /* r */ |
259 |
|
OPT_DEBUG = (1 << 5), /* d */ |
260 |
|
OPT_VERBOSE = (1 << 6) * ENABLE_FEATURE_TRACEROUTE_VERBOSE, /* v */ |
261 |
|
OPT_IP_CHKSUM = (1 << 7), /* x */ |
262 |
|
OPT_TOS = (1 << 8), /* t */ |
263 |
|
OPT_DEVICE = (1 << 9), /* i */ |
264 |
|
OPT_MAX_TTL = (1 << 10), /* m */ |
265 |
|
OPT_PORT = (1 << 11), /* p */ |
266 |
|
OPT_NPROBES = (1 << 12), /* q */ |
267 |
|
OPT_SOURCE = (1 << 13), /* s */ |
268 |
|
OPT_WAITTIME = (1 << 14), /* w */ |
269 |
|
OPT_PAUSE_MS = (1 << 15), /* z */ |
270 |
|
OPT_FIRST_TTL = (1 << 16), /* f */ |
271 |
|
OPT_SOURCE_ROUTE = (1 << 17) * ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE, /* g */ |
272 |
|
OPT_IPV4 = (1 << (17+ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE)), /* 4 */ |
273 |
|
OPT_IPV6 = (1 << (18+ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE)) * ENABLE_TRACEROUTE6, /* 6 */ |
274 |
}; |
}; |
275 |
#define ui_next ui_i.ih_next |
#define verbose (option_mask32 & OPT_VERBOSE) |
276 |
#define ui_prev ui_i.ih_prev |
|
277 |
#define ui_x1 ui_i.ih_x1 |
enum { |
278 |
#define ui_pr ui_i.ih_pr |
SIZEOF_ICMP_HDR = 8, |
279 |
#define ui_len ui_i.ih_len |
rcvsock = 3, /* receive (icmp) socket file descriptor */ |
280 |
#define ui_src ui_i.ih_src |
sndsock = 4, /* send (udp/icmp) socket file descriptor */ |
|
#define ui_dst ui_i.ih_dst |
|
|
#define ui_sport ui_u.uh_sport |
|
|
#define ui_dport ui_u.uh_dport |
|
|
#define ui_ulen ui_u.uh_ulen |
|
|
#define ui_sum ui_u.uh_sum |
|
|
|
|
|
|
|
|
/* Host name and address list */ |
|
|
struct hostinfo { |
|
|
char *name; |
|
|
int n; |
|
|
uint32_t *addrs; |
|
281 |
}; |
}; |
282 |
|
|
283 |
/* Data section of the probe packet */ |
/* Data section of the probe packet */ |
284 |
typedef struct outdata { |
struct outdata_t { |
285 |
unsigned char seq; /* sequence number of this packet */ |
unsigned char seq; /* sequence number of this packet */ |
286 |
unsigned char ttl; /* ttl packet left with */ |
unsigned char ttl; /* ttl packet left with */ |
287 |
// UNUSED. Retaining to have the same packet size. |
// UNUSED. Retaining to have the same packet size. |
288 |
struct timeval tv_UNUSED PACKED; /* time packet left */ |
struct timeval tv_UNUSED PACKED; /* time packet left */ |
|
} outdata_t; |
|
|
|
|
|
struct IFADDRLIST { |
|
|
uint32_t addr; |
|
|
char device[sizeof(struct ifreq)]; |
|
289 |
}; |
}; |
290 |
|
|
291 |
|
#if ENABLE_TRACEROUTE6 |
292 |
/* Keep in sync with getopt32 call! */ |
struct outdata6_t { |
293 |
#define OPT_DONT_FRAGMNT (1<<0) /* F */ |
uint32_t ident6; |
294 |
#define OPT_USE_ICMP (1<<1) /* I */ |
uint32_t seq6; |
295 |
#define OPT_TTL_FLAG (1<<2) /* l */ |
struct timeval tv_UNUSED PACKED; /* time packet left */ |
296 |
#define OPT_ADDR_NUM (1<<3) /* n */ |
}; |
|
#define OPT_BYPASS_ROUTE (1<<4) /* r */ |
|
|
#define OPT_DEBUG (1<<5) /* d */ |
|
|
#define OPT_VERBOSE (1<<6) /* v */ |
|
|
#define OPT_IP_CHKSUM (1<<7) /* x */ |
|
|
#define OPT_TOS (1<<8) /* t */ |
|
|
#define OPT_DEVICE (1<<9) /* i */ |
|
|
#define OPT_MAX_TTL (1<<10) /* m */ |
|
|
#define OPT_PORT (1<<11) /* p */ |
|
|
#define OPT_NPROBES (1<<12) /* q */ |
|
|
#define OPT_SOURCE (1<<13) /* s */ |
|
|
#define OPT_WAITTIME (1<<14) /* w */ |
|
|
#define OPT_PAUSE_MS (1<<15) /* z */ |
|
|
#define OPT_FIRST_TTL (1<<16) /* f */ |
|
|
|
|
|
#if ENABLE_FEATURE_TRACEROUTE_USE_ICMP |
|
|
/* use icmp echo instead of udp packets */ |
|
|
#define useicmp (option_mask32 & OPT_USE_ICMP) |
|
|
#endif |
|
|
#if ENABLE_FEATURE_TRACEROUTE_VERBOSE |
|
|
#define verbose (option_mask32 & OPT_VERBOSE) |
|
297 |
#endif |
#endif |
|
#define nflag (option_mask32 & OPT_ADDR_NUM) |
|
|
|
|
298 |
|
|
299 |
struct globals { |
struct globals { |
300 |
struct ip *outip; /* last output (udp) packet */ |
struct ip *outip; |
301 |
struct udphdr *outudp; /* last output (udp) packet */ |
struct outdata_t *outdata; |
302 |
struct outdata *outdata; /* last output (udp) packet */ |
len_and_sockaddr *dest_lsa; |
|
|
|
|
#if ENABLE_FEATURE_TRACEROUTE_USE_ICMP |
|
|
struct icmp *outicmp; /* last output (icmp) packet */ |
|
|
#endif |
|
|
|
|
|
int rcvsock; /* receive (icmp) socket file descriptor */ |
|
|
int sndsock; /* send (udp/icmp) socket file descriptor */ |
|
|
|
|
303 |
int packlen; /* total length of packet */ |
int packlen; /* total length of packet */ |
|
int minpacket; /* min ip packet size */ |
|
|
int maxpacket; // 32 * 1024; /* max ip packet size */ |
|
304 |
int pmtu; /* Path MTU Discovery (RFC1191) */ |
int pmtu; /* Path MTU Discovery (RFC1191) */ |
305 |
|
uint32_t ident; |
|
char *hostname; |
|
|
|
|
|
uint16_t ident; |
|
306 |
uint16_t port; // 32768 + 666; /* start udp dest port # for probe packets */ |
uint16_t port; // 32768 + 666; /* start udp dest port # for probe packets */ |
|
|
|
307 |
int waittime; // 5; /* time to wait for response (in seconds) */ |
int waittime; // 5; /* time to wait for response (in seconds) */ |
|
int doipcksum; // 1; /* calculate ip checksums by default */ |
|
|
|
|
308 |
#if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE |
#if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE |
309 |
int optlen; /* length of ip options */ |
int optlen; /* length of ip options */ |
310 |
#else |
#else |
311 |
#define optlen 0 |
#define optlen 0 |
312 |
#endif |
#endif |
313 |
|
unsigned char recv_pkt[512]; /* last inbound (icmp) packet */ |
|
struct sockaddr_storage whereto; /* Who to try to reach */ |
|
|
struct sockaddr_storage wherefrom; /* Who we are */ |
|
|
/* last inbound (icmp) packet */ |
|
|
unsigned char packet[512]; |
|
314 |
#if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE |
#if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE |
315 |
/* Maximum number of gateways (include room for one noop) */ |
/* Maximum number of gateways (include room for one noop) */ |
316 |
#define NGATEWAYS ((int)((MAX_IPOPTLEN - IPOPT_MINOFF - 1) / sizeof(uint32_t))) |
#define NGATEWAYS ((int)((MAX_IPOPTLEN - IPOPT_MINOFF - 1) / sizeof(uint32_t))) |
321 |
|
|
322 |
#define G (*ptr_to_globals) |
#define G (*ptr_to_globals) |
323 |
#define outip (G.outip ) |
#define outip (G.outip ) |
|
#define outudp (G.outudp ) |
|
324 |
#define outdata (G.outdata ) |
#define outdata (G.outdata ) |
325 |
#define outicmp (G.outicmp ) |
#define dest_lsa (G.dest_lsa ) |
|
#define rcvsock (G.rcvsock ) |
|
|
#define sndsock (G.sndsock ) |
|
326 |
#define packlen (G.packlen ) |
#define packlen (G.packlen ) |
|
#define minpacket (G.minpacket) |
|
|
#define maxpacket (G.maxpacket) |
|
327 |
#define pmtu (G.pmtu ) |
#define pmtu (G.pmtu ) |
|
#define hostname (G.hostname ) |
|
328 |
#define ident (G.ident ) |
#define ident (G.ident ) |
329 |
#define port (G.port ) |
#define port (G.port ) |
330 |
#define waittime (G.waittime ) |
#define waittime (G.waittime ) |
|
#define doipcksum (G.doipcksum) |
|
331 |
#if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE |
#if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE |
332 |
#define optlen (G.optlen ) |
# define optlen (G.optlen ) |
333 |
#endif |
#endif |
334 |
#define packet (G.packet ) |
#define recv_pkt (G.recv_pkt ) |
|
#define whereto (G.whereto ) |
|
|
#define wherefrom (G.wherefrom) |
|
335 |
#define gwlist (G.gwlist ) |
#define gwlist (G.gwlist ) |
336 |
#define INIT_G() do { \ |
#define INIT_G() do { \ |
337 |
SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \ |
SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \ |
|
maxpacket = 32 * 1024; \ |
|
338 |
port = 32768 + 666; \ |
port = 32768 + 666; \ |
339 |
waittime = 5; \ |
waittime = 5; \ |
|
doipcksum = 1; \ |
|
340 |
} while (0) |
} while (0) |
341 |
|
|
342 |
|
#define outicmp ((struct icmp *)(outip + 1)) |
343 |
|
#define outudp ((struct udphdr *)(outip + 1)) |
344 |
|
|
|
/* |
|
|
* Return the interface list |
|
|
*/ |
|
|
static int |
|
|
ifaddrlist(struct IFADDRLIST **ipaddrp) |
|
|
{ |
|
|
enum { IFREQ_BUFSIZE = (32 * 1024) / sizeof(struct ifreq) }; |
|
|
|
|
|
int fd, nipaddr; |
|
|
#ifdef HAVE_SOCKADDR_SA_LEN |
|
|
int n; |
|
|
#endif |
|
|
struct ifreq *ifrp, *ifend, *ifnext; |
|
|
struct sockaddr_in *addr_sin; |
|
|
struct IFADDRLIST *al; |
|
|
struct ifconf ifc; |
|
|
struct ifreq ifr; |
|
|
/* Was on stack, but 32k is a bit too much: */ |
|
|
struct ifreq *ibuf = xmalloc(IFREQ_BUFSIZE * sizeof(ibuf[0])); |
|
|
struct IFADDRLIST *st_ifaddrlist; |
|
345 |
|
|
346 |
fd = xsocket(AF_INET, SOCK_DGRAM, 0); |
/* libbb candidate? tftp uses this idiom too */ |
347 |
|
static len_and_sockaddr* dup_sockaddr(const len_and_sockaddr *lsa) |
|
ifc.ifc_len = IFREQ_BUFSIZE * sizeof(ibuf[0]); |
|
|
ifc.ifc_buf = (caddr_t)ibuf; |
|
|
|
|
|
if (ioctl(fd, SIOCGIFCONF, (char *)&ifc) < 0 |
|
|
|| ifc.ifc_len < (int)sizeof(struct ifreq) |
|
|
) { |
|
|
if (errno == EINVAL) |
|
|
bb_error_msg_and_die( |
|
|
"SIOCGIFCONF: ifreq struct too small (%u bytes)", |
|
|
(unsigned)(IFREQ_BUFSIZE * sizeof(ibuf[0]))); |
|
|
bb_perror_msg_and_die("SIOCGIFCONF"); |
|
|
} |
|
|
ifrp = ibuf; |
|
|
ifend = (struct ifreq *)((char *)ibuf + ifc.ifc_len); |
|
|
|
|
|
nipaddr = 1 + (ifc.ifc_len / sizeof(struct ifreq)); |
|
|
st_ifaddrlist = xzalloc(nipaddr * sizeof(struct IFADDRLIST)); |
|
|
al = st_ifaddrlist; |
|
|
nipaddr = 0; |
|
|
|
|
|
for (; ifrp < ifend; ifrp = ifnext) { |
|
|
#ifdef HAVE_SOCKADDR_SA_LEN |
|
|
n = ifrp->ifr_addr.sa_len + sizeof(ifrp->ifr_name); |
|
|
if (n < sizeof(*ifrp)) |
|
|
ifnext = ifrp + 1; |
|
|
else |
|
|
ifnext = (struct ifreq *)((char *)ifrp + n); |
|
|
if (ifrp->ifr_addr.sa_family != AF_INET) |
|
|
continue; |
|
|
#else |
|
|
ifnext = ifrp + 1; |
|
|
#endif |
|
|
/* |
|
|
* Need a template to preserve address info that is |
|
|
* used below to locate the next entry. (Otherwise, |
|
|
* SIOCGIFFLAGS stomps over it because the requests |
|
|
* are returned in a union.) |
|
|
*/ |
|
|
strncpy(ifr.ifr_name, ifrp->ifr_name, sizeof(ifr.ifr_name)); |
|
|
if (ioctl(fd, SIOCGIFFLAGS, (char *)&ifr) < 0) { |
|
|
if (errno == ENXIO) |
|
|
continue; |
|
|
bb_perror_msg_and_die("SIOCGIFFLAGS: %.*s", |
|
|
(int)sizeof(ifr.ifr_name), ifr.ifr_name); |
|
|
} |
|
|
|
|
|
/* Must be up */ |
|
|
if ((ifr.ifr_flags & IFF_UP) == 0) |
|
|
continue; |
|
|
|
|
|
safe_strncpy(al->device, ifr.ifr_name, sizeof(ifr.ifr_name) + 1); |
|
|
#ifdef sun |
|
|
/* Ignore sun virtual interfaces */ |
|
|
if (strchr(al->device, ':') != NULL) |
|
|
continue; |
|
|
#endif |
|
|
ioctl_or_perror_and_die(fd, SIOCGIFADDR, (char *)&ifr, |
|
|
"SIOCGIFADDR: %s", al->device); |
|
|
|
|
|
addr_sin = (struct sockaddr_in *)&ifr.ifr_addr; |
|
|
al->addr = addr_sin->sin_addr.s_addr; |
|
|
++al; |
|
|
++nipaddr; |
|
|
} |
|
|
if (nipaddr == 0) |
|
|
bb_error_msg_and_die("can't find any network interfaces"); |
|
|
|
|
|
free(ibuf); |
|
|
close(fd); |
|
|
*ipaddrp = st_ifaddrlist; |
|
|
return nipaddr; |
|
|
} |
|
|
|
|
|
|
|
|
static void |
|
|
setsin(struct sockaddr_in *addr_sin, uint32_t addr) |
|
|
{ |
|
|
memset(addr_sin, 0, sizeof(*addr_sin)); |
|
|
#ifdef HAVE_SOCKADDR_SA_LEN |
|
|
addr_sin->sin_len = sizeof(*addr_sin); |
|
|
#endif |
|
|
addr_sin->sin_family = AF_INET; |
|
|
addr_sin->sin_addr.s_addr = addr; |
|
|
} |
|
|
|
|
|
|
|
|
/* |
|
|
* Return the source address for the given destination address |
|
|
*/ |
|
|
static void |
|
|
findsaddr(const struct sockaddr_in *to, struct sockaddr_in *from) |
|
348 |
{ |
{ |
349 |
int i, n; |
len_and_sockaddr *new_lsa = xzalloc(LSA_LEN_SIZE + lsa->len); |
350 |
FILE *f; |
memcpy(new_lsa, lsa, LSA_LEN_SIZE + lsa->len); |
351 |
uint32_t mask; |
return new_lsa; |
|
uint32_t dest, tmask; |
|
|
struct IFADDRLIST *al; |
|
|
char buf[256], tdevice[256], device[256]; |
|
|
|
|
|
f = xfopen_for_read("/proc/net/route"); |
|
|
|
|
|
/* Find the appropriate interface */ |
|
|
n = 0; |
|
|
mask = 0; |
|
|
device[0] = '\0'; |
|
|
while (fgets(buf, sizeof(buf), f) != NULL) { |
|
|
++n; |
|
|
if (n == 1 && strncmp(buf, "Iface", 5) == 0) |
|
|
continue; |
|
|
i = sscanf(buf, "%255s %x %*s %*s %*s %*s %*s %x", |
|
|
tdevice, &dest, &tmask); |
|
|
if (i != 3) |
|
|
bb_error_msg_and_die("junk in buffer"); |
|
|
if ((to->sin_addr.s_addr & tmask) == dest |
|
|
&& (tmask > mask || mask == 0) |
|
|
) { |
|
|
mask = tmask; |
|
|
strcpy(device, tdevice); |
|
|
} |
|
|
} |
|
|
fclose(f); |
|
|
|
|
|
if (device[0] == '\0') |
|
|
bb_error_msg_and_die("can't find interface"); |
|
|
|
|
|
/* Get the interface address list */ |
|
|
n = ifaddrlist(&al); |
|
|
|
|
|
/* Find our appropriate source address */ |
|
|
for (i = n; i > 0; --i, ++al) |
|
|
if (strcmp(device, al->device) == 0) |
|
|
break; |
|
|
if (i <= 0) |
|
|
bb_error_msg_and_die("can't find interface %s", device); |
|
|
|
|
|
setsin(from, al->addr); |
|
352 |
} |
} |
353 |
|
|
|
/* |
|
|
"Usage: %s [-dFIlnrvx] [-g gateway] [-i iface] [-f first_ttl]\n" |
|
|
"\t[-m max_ttl] [ -p port] [-q nqueries] [-s src_addr] [-t tos]\n" |
|
|
"\t[-w waittime] [-z pausemsecs] host [packetlen]" |
|
|
|
|
|
*/ |
|
354 |
|
|
355 |
static int |
static int |
356 |
wait_for_reply(int sock, struct sockaddr_in *fromp) |
wait_for_reply(len_and_sockaddr *from_lsa, struct sockaddr *to) |
357 |
{ |
{ |
358 |
struct pollfd pfd[1]; |
struct pollfd pfd[1]; |
359 |
int cc = 0; |
int read_len = 0; |
|
socklen_t fromlen = sizeof(*fromp); |
|
360 |
|
|
361 |
pfd[0].fd = sock; |
pfd[0].fd = rcvsock; |
362 |
pfd[0].events = POLLIN; |
pfd[0].events = POLLIN; |
363 |
if (safe_poll(pfd, 1, waittime * 1000) > 0) |
if (safe_poll(pfd, 1, waittime * 1000) > 0) { |
364 |
cc = recvfrom(sock, packet, sizeof(packet), 0, |
read_len = recv_from_to(rcvsock, |
365 |
(struct sockaddr *)fromp, &fromlen); |
recv_pkt, sizeof(recv_pkt), |
366 |
return cc; |
/*flags:*/ 0, |
367 |
|
&from_lsa->u.sa, to, from_lsa->len); |
368 |
|
} |
369 |
|
|
370 |
|
return read_len; |
371 |
} |
} |
372 |
|
|
373 |
/* |
/* |
382 |
int sum = 0; |
int sum = 0; |
383 |
|
|
384 |
/* |
/* |
385 |
* Our algorithm is simple, using a 32 bit accumulator (sum), |
* Our algorithm is simple, using a 32 bit accumulator (sum), |
386 |
* we add sequential 16 bit words to it, and at the end, fold |
* we add sequential 16 bit words to it, and at the end, fold |
387 |
* back all the carry bits from the top 16 bits into the lower |
* back all the carry bits from the top 16 bits into the lower |
388 |
* 16 bits. |
* 16 bits. |
389 |
*/ |
*/ |
390 |
while (nleft > 1) { |
while (nleft > 1) { |
391 |
sum += *w++; |
sum += *w++; |
392 |
nleft -= 2; |
nleft -= 2; |
393 |
} |
} |
396 |
if (nleft == 1) |
if (nleft == 1) |
397 |
sum += *(unsigned char *)w; |
sum += *(unsigned char *)w; |
398 |
|
|
399 |
/* |
/* add back carry outs from top 16 bits to low 16 bits */ |
|
* add back carry outs from top 16 bits to low 16 bits |
|
|
*/ |
|
400 |
sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */ |
sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */ |
401 |
sum += (sum >> 16); /* add carry */ |
sum += (sum >> 16); /* add carry */ |
402 |
answer = ~sum; /* truncate to 16 bits */ |
answer = ~sum; /* truncate to 16 bits */ |
403 |
return answer; |
return answer; |
404 |
} |
} |
405 |
|
|
|
|
|
406 |
static void |
static void |
407 |
send_probe(int seq, int ttl) |
send_probe(int seq, int ttl) |
408 |
{ |
{ |
409 |
int cc; |
int len, res; |
410 |
struct udpiphdr *ui, *oui; |
void *out; |
|
struct ip tip; |
|
|
|
|
|
outip->ip_ttl = ttl; |
|
|
outip->ip_id = htons(ident + seq); |
|
|
|
|
|
/* |
|
|
* In most cases, the kernel will recalculate the ip checksum. |
|
|
* But we must do it anyway so that the udp checksum comes out |
|
|
* right. |
|
|
*/ |
|
|
if (doipcksum) { |
|
|
outip->ip_sum = |
|
|
in_cksum((uint16_t *)outip, sizeof(*outip) + optlen); |
|
|
if (outip->ip_sum == 0) |
|
|
outip->ip_sum = 0xffff; |
|
|
} |
|
411 |
|
|
412 |
/* Payload */ |
/* Payload */ |
413 |
outdata->seq = seq; |
#if ENABLE_TRACEROUTE6 |
414 |
outdata->ttl = ttl; |
if (dest_lsa->u.sa.sa_family == AF_INET6) { |
415 |
// UNUSED: was storing gettimeofday's result there, but never ever checked it |
struct outdata6_t *pkt = (struct outdata6_t *) outip; |
416 |
/*memcpy(&outdata->tv, tp, sizeof(outdata->tv));*/ |
pkt->ident6 = htonl(ident); |
417 |
|
pkt->seq6 = htonl(seq); |
418 |
#if ENABLE_FEATURE_TRACEROUTE_USE_ICMP |
/*gettimeofday(&pkt->tv, &tz);*/ |
|
if (useicmp) |
|
|
outicmp->icmp_seq = htons(seq); |
|
|
else |
|
|
#endif |
|
|
outudp->dest = htons(port + seq); |
|
|
|
|
|
#if ENABLE_FEATURE_TRACEROUTE_USE_ICMP |
|
|
if (useicmp) { |
|
|
/* Always calculate checksum for icmp packets */ |
|
|
outicmp->icmp_cksum = 0; |
|
|
outicmp->icmp_cksum = in_cksum((uint16_t *)outicmp, |
|
|
packlen - (sizeof(*outip) + optlen)); |
|
|
if (outicmp->icmp_cksum == 0) |
|
|
outicmp->icmp_cksum = 0xffff; |
|
419 |
} else |
} else |
420 |
#endif |
#endif |
421 |
if (doipcksum) { |
{ |
422 |
/* Checksum (we must save and restore ip header) */ |
outdata->seq = seq; |
423 |
tip = *outip; |
outdata->ttl = ttl; |
424 |
ui = (struct udpiphdr *)outip; |
// UNUSED: was storing gettimeofday's result there, but never ever checked it |
425 |
oui = (struct udpiphdr *)&tip; |
/*memcpy(&outdata->tv, tp, sizeof(outdata->tv));*/ |
426 |
/* Easier to zero and put back things that are ok */ |
|
427 |
memset((char *)ui, 0, sizeof(ui->ui_i)); |
if (option_mask32 & OPT_USE_ICMP) { |
428 |
ui->ui_src = oui->ui_src; |
outicmp->icmp_seq = htons(seq); |
429 |
ui->ui_dst = oui->ui_dst; |
|
430 |
ui->ui_pr = oui->ui_pr; |
/* Always calculate checksum for icmp packets */ |
431 |
ui->ui_len = outudp->len; |
outicmp->icmp_cksum = 0; |
432 |
outudp->check = 0; |
outicmp->icmp_cksum = in_cksum((uint16_t *)outicmp, |
433 |
outudp->check = in_cksum((uint16_t *)ui, packlen); |
packlen - (sizeof(*outip) + optlen)); |
434 |
if (outudp->check == 0) |
if (outicmp->icmp_cksum == 0) |
435 |
outudp->check = 0xffff; |
outicmp->icmp_cksum = 0xffff; |
436 |
*outip = tip; |
} |
437 |
} |
} |
438 |
|
|
439 |
#if ENABLE_FEATURE_TRACEROUTE_VERBOSE |
//BUG! verbose is (x & OPT_VERBOSE), not a counter! |
440 |
|
#if 0 //ENABLE_FEATURE_TRACEROUTE_VERBOSE |
441 |
/* XXX undocumented debugging hack */ |
/* XXX undocumented debugging hack */ |
442 |
if (verbose > 1) { |
if (verbose > 1) { |
443 |
const uint16_t *sp; |
const uint16_t *sp; |
462 |
} |
} |
463 |
#endif |
#endif |
464 |
|
|
465 |
#if !defined(IP_HDRINCL) && defined(IP_TTL) |
#if ENABLE_TRACEROUTE6 |
466 |
if (setsockopt(sndsock, IPPROTO_IP, IP_TTL, |
if (dest_lsa->u.sa.sa_family == AF_INET6) { |
467 |
(char *)&ttl, sizeof(ttl)) < 0) { |
res = setsockopt(sndsock, SOL_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl)); |
468 |
bb_perror_msg_and_die("setsockopt ttl %d", ttl); |
if (res < 0) |
469 |
} |
bb_perror_msg_and_die("setsockopt UNICAST_HOPS %d", ttl); |
470 |
|
out = outip; |
471 |
|
len = packlen; |
472 |
|
} else |
473 |
#endif |
#endif |
474 |
|
{ |
475 |
cc = xsendto(sndsock, (char *)outip, |
#if defined IP_TTL |
476 |
packlen, (struct sockaddr *)&whereto, sizeof(whereto)); |
res = setsockopt(sndsock, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl)); |
477 |
if (cc != packlen) { |
if (res < 0) |
478 |
bb_info_msg("wrote %s %d chars, ret=%d", hostname, packlen, cc); |
bb_perror_msg_and_die("setsockopt ttl %d", ttl); |
479 |
|
#endif |
480 |
|
out = outicmp; |
481 |
|
len = packlen - sizeof(*outip); |
482 |
|
if (!(option_mask32 & OPT_USE_ICMP)) { |
483 |
|
out = outdata; |
484 |
|
len -= sizeof(*outudp); |
485 |
|
set_nport(dest_lsa, htons(port + seq)); |
486 |
|
} |
487 |
} |
} |
488 |
|
|
489 |
|
res = xsendto(sndsock, out, len, &dest_lsa->u.sa, dest_lsa->len); |
490 |
|
if (res != len) |
491 |
|
bb_info_msg("sent %d octets, ret=%d", len, res); |
492 |
} |
} |
493 |
|
|
494 |
#if ENABLE_FEATURE_TRACEROUTE_VERBOSE |
#if ENABLE_FEATURE_TRACEROUTE_VERBOSE |
495 |
/* |
/* |
496 |
* Convert an ICMP "type" field to a printable string. |
* Convert an ICMP "type" field to a printable string. |
497 |
*/ |
*/ |
498 |
static inline const char * |
static const char * |
499 |
pr_type(unsigned char t) |
pr_type(unsigned char t) |
500 |
{ |
{ |
501 |
static const char *const ttab[] = { |
static const char *const ttab[] = { |
505 |
"Param Problem", "Timestamp", "Timestamp Reply", "Info Request", |
"Param Problem", "Timestamp", "Timestamp Reply", "Info Request", |
506 |
"Info Reply", "Mask Request", "Mask Reply" |
"Info Reply", "Mask Request", "Mask Reply" |
507 |
}; |
}; |
508 |
|
# if ENABLE_TRACEROUTE6 |
509 |
|
static const char *const ttab6[] = { |
510 |
|
[0] "Error", "Dest Unreachable", "Packet Too Big", "Time Exceeded", |
511 |
|
[4] "Param Problem", |
512 |
|
[8] "Echo Request", "Echo Reply", "Membership Query", "Membership Report", |
513 |
|
[12] "Membership Reduction", "Router Solicit", "Router Advert", "Neighbor Solicit", |
514 |
|
[16] "Neighbor Advert", "Redirect", |
515 |
|
}; |
516 |
|
|
517 |
if (t > 18) |
if (dest_lsa->u.sa.sa_family == AF_INET6) { |
518 |
|
if (t < 5) |
519 |
|
return ttab6[t]; |
520 |
|
if (t < 128 || t > ND_REDIRECT) |
521 |
|
return "OUT-OF-RANGE"; |
522 |
|
return ttab6[(t & 63) + 8]; |
523 |
|
} |
524 |
|
# endif |
525 |
|
if (t >= ARRAY_SIZE(ttab)) |
526 |
return "OUT-OF-RANGE"; |
return "OUT-OF-RANGE"; |
527 |
|
|
528 |
return ttab[t]; |
return ttab[t]; |
530 |
#endif |
#endif |
531 |
|
|
532 |
#if !ENABLE_FEATURE_TRACEROUTE_VERBOSE |
#if !ENABLE_FEATURE_TRACEROUTE_VERBOSE |
533 |
#define packet_ok(buf, cc, from, seq) \ |
#define packet4_ok(read_len, from, seq) \ |
534 |
packet_ok(buf, cc, seq) |
packet4_ok(read_len, seq) |
535 |
#endif |
#endif |
536 |
static int |
static int |
537 |
packet_ok(unsigned char *buf, int cc, struct sockaddr_in *from, int seq) |
packet4_ok(int read_len, const struct sockaddr_in *from, int seq) |
538 |
{ |
{ |
539 |
struct icmp *icp; |
const struct icmp *icp; |
540 |
unsigned char type, code; |
unsigned char type, code; |
541 |
int hlen; |
int hlen; |
542 |
struct ip *ip; |
const struct ip *ip; |
543 |
|
|
544 |
ip = (struct ip *) buf; |
ip = (struct ip *) recv_pkt; |
545 |
hlen = ip->ip_hl << 2; |
hlen = ip->ip_hl << 2; |
546 |
if (cc < hlen + ICMP_MINLEN) { |
if (read_len < hlen + ICMP_MINLEN) { |
547 |
#if ENABLE_FEATURE_TRACEROUTE_VERBOSE |
#if ENABLE_FEATURE_TRACEROUTE_VERBOSE |
548 |
if (verbose) |
if (verbose) |
549 |
printf("packet too short (%d bytes) from %s\n", cc, |
printf("packet too short (%d bytes) from %s\n", read_len, |
550 |
inet_ntoa(from->sin_addr)); |
inet_ntoa(from->sin_addr)); |
551 |
#endif |
#endif |
552 |
return 0; |
return 0; |
553 |
} |
} |
554 |
cc -= hlen; |
read_len -= hlen; |
555 |
icp = (struct icmp *)(buf + hlen); |
icp = (struct icmp *)(recv_pkt + hlen); |
556 |
type = icp->icmp_type; |
type = icp->icmp_type; |
557 |
code = icp->icmp_code; |
code = icp->icmp_code; |
558 |
/* Path MTU Discovery (RFC1191) */ |
/* Path MTU Discovery (RFC1191) */ |
559 |
if (code != ICMP_UNREACH_NEEDFRAG) |
pmtu = 0; |
560 |
pmtu = 0; |
if (code == ICMP_UNREACH_NEEDFRAG) |
|
else { |
|
561 |
pmtu = ntohs(icp->icmp_nextmtu); |
pmtu = ntohs(icp->icmp_nextmtu); |
562 |
} |
|
563 |
if ((type == ICMP_TIMXCEED && code == ICMP_TIMXCEED_INTRANS) || |
if ((type == ICMP_TIMXCEED && code == ICMP_TIMXCEED_INTRANS) |
564 |
type == ICMP_UNREACH || type == ICMP_ECHOREPLY) { |
|| type == ICMP_UNREACH |
565 |
struct ip *hip; |
|| type == ICMP_ECHOREPLY |
566 |
struct udphdr *up; |
) { |
567 |
|
const struct ip *hip; |
568 |
|
const struct udphdr *up; |
569 |
|
|
570 |
hip = &icp->icmp_ip; |
hip = &icp->icmp_ip; |
571 |
hlen = hip->ip_hl << 2; |
hlen = hip->ip_hl << 2; |
572 |
#if ENABLE_FEATURE_TRACEROUTE_USE_ICMP |
if (option_mask32 & OPT_USE_ICMP) { |
|
if (useicmp) { |
|
573 |
struct icmp *hicmp; |
struct icmp *hicmp; |
574 |
|
|
575 |
/* XXX */ |
/* XXX */ |
576 |
if (type == ICMP_ECHOREPLY && |
if (type == ICMP_ECHOREPLY |
577 |
icp->icmp_id == htons(ident) && |
&& icp->icmp_id == htons(ident) |
578 |
icp->icmp_seq == htons(seq)) |
&& icp->icmp_seq == htons(seq) |
579 |
return -2; |
) { |
580 |
|
return ICMP_UNREACH_PORT+1; |
581 |
|
} |
582 |
|
|
583 |
hicmp = (struct icmp *)((unsigned char *)hip + hlen); |
hicmp = (struct icmp *)((unsigned char *)hip + hlen); |
584 |
/* XXX 8 is a magic number */ |
if (hlen + SIZEOF_ICMP_HDR <= read_len |
585 |
if (hlen + 8 <= cc && |
&& hip->ip_p == IPPROTO_ICMP |
586 |
hip->ip_p == IPPROTO_ICMP && |
&& hicmp->icmp_id == htons(ident) |
587 |
hicmp->icmp_id == htons(ident) && |
&& hicmp->icmp_seq == htons(seq) |
588 |
hicmp->icmp_seq == htons(seq)) |
) { |
589 |
return (type == ICMP_TIMXCEED ? -1 : code + 1); |
return (type == ICMP_TIMXCEED ? -1 : code + 1); |
590 |
} else |
} |
591 |
#endif |
} else { |
592 |
{ |
up = (struct udphdr *)((char *)hip + hlen); |
593 |
up = (struct udphdr *)((unsigned char *)hip + hlen); |
if (hlen + 12 <= read_len |
594 |
/* XXX 8 is a magic number */ |
&& hip->ip_p == IPPROTO_UDP |
595 |
if (hlen + 12 <= cc && |
// Off: since we do not form the entire IP packet, |
596 |
hip->ip_p == IPPROTO_UDP && |
// but defer it to kernel, we can't set source port, |
597 |
up->source == htons(ident) && |
// and thus can't check it here in the reply |
598 |
up->dest == htons(port + seq)) |
/* && up->source == htons(ident) */ |
599 |
|
&& up->dest == htons(port + seq) |
600 |
|
) { |
601 |
return (type == ICMP_TIMXCEED ? -1 : code + 1); |
return (type == ICMP_TIMXCEED ? -1 : code + 1); |
602 |
|
} |
603 |
} |
} |
604 |
} |
} |
605 |
#if ENABLE_FEATURE_TRACEROUTE_VERBOSE |
#if ENABLE_FEATURE_TRACEROUTE_VERBOSE |
609 |
|
|
610 |
printf("\n%d bytes from %s to " |
printf("\n%d bytes from %s to " |
611 |
"%s: icmp type %d (%s) code %d\n", |
"%s: icmp type %d (%s) code %d\n", |
612 |
cc, inet_ntoa(from->sin_addr), |
read_len, inet_ntoa(from->sin_addr), |
613 |
inet_ntoa(ip->ip_dst), type, pr_type(type), icp->icmp_code); |
inet_ntoa(ip->ip_dst), |
614 |
for (i = 4; i < cc; i += sizeof(*lp)) |
type, pr_type(type), icp->icmp_code); |
615 |
|
for (i = 4; i < read_len; i += sizeof(*lp)) |
616 |
printf("%2d: x%8.8x\n", i, *lp++); |
printf("%2d: x%8.8x\n", i, *lp++); |
617 |
} |
} |
618 |
#endif |
#endif |
619 |
return 0; |
return 0; |
620 |
} |
} |
621 |
|
|
622 |
|
#if ENABLE_TRACEROUTE6 |
623 |
|
# if !ENABLE_FEATURE_TRACEROUTE_VERBOSE |
624 |
|
#define packet_ok(read_len, from_lsa, to, seq) \ |
625 |
|
packet_ok(read_len, from_lsa, seq) |
626 |
|
# endif |
627 |
|
static int |
628 |
|
packet_ok(int read_len, len_and_sockaddr *from_lsa, |
629 |
|
struct sockaddr *to, |
630 |
|
int seq) |
631 |
|
{ |
632 |
|
const struct icmp6_hdr *icp; |
633 |
|
unsigned char type, code; |
634 |
|
|
635 |
|
if (from_lsa->u.sa.sa_family == AF_INET) |
636 |
|
return packet4_ok(read_len, &from_lsa->u.sin, seq); |
637 |
|
|
638 |
|
icp = (struct icmp6_hdr *) recv_pkt; |
639 |
|
|
640 |
|
type = icp->icmp6_type; |
641 |
|
code = icp->icmp6_code; |
642 |
|
|
643 |
|
if ((type == ICMP6_TIME_EXCEEDED && code == ICMP6_TIME_EXCEED_TRANSIT) |
644 |
|
|| type == ICMP6_DST_UNREACH |
645 |
|
) { |
646 |
|
struct ip6_hdr *hip; |
647 |
|
struct udphdr *up; |
648 |
|
int nexthdr; |
649 |
|
|
650 |
|
hip = (struct ip6_hdr *)(icp + 1); |
651 |
|
up = (struct udphdr *) (hip + 1); |
652 |
|
nexthdr = hip->ip6_nxt; |
653 |
|
|
654 |
|
if (nexthdr == IPPROTO_FRAGMENT) { |
655 |
|
nexthdr = *(unsigned char*)up; |
656 |
|
up++; |
657 |
|
} |
658 |
|
if (nexthdr == IPPROTO_UDP) { |
659 |
|
struct outdata6_t *pkt; |
660 |
|
|
661 |
|
pkt = (struct outdata6_t *) (up + 1); |
662 |
|
|
663 |
|
if (ntohl(pkt->ident6) == ident |
664 |
|
&& ntohl(pkt->seq6) == seq |
665 |
|
) { |
666 |
|
return (type == ICMP6_TIME_EXCEEDED ? -1 : (code<<8)+1); |
667 |
|
} |
668 |
|
} |
669 |
|
|
670 |
|
} |
671 |
|
|
672 |
|
# if ENABLE_FEATURE_TRACEROUTE_VERBOSE |
673 |
|
if (verbose) { |
674 |
|
unsigned char *p; |
675 |
|
char pa1[MAXHOSTNAMELEN]; |
676 |
|
char pa2[MAXHOSTNAMELEN]; |
677 |
|
int i; |
678 |
|
|
679 |
|
p = (unsigned char *) (icp + 1); |
680 |
|
|
681 |
|
printf("\n%d bytes from %s to " |
682 |
|
"%s: icmp type %d (%s) code %d\n", |
683 |
|
read_len, |
684 |
|
inet_ntop(AF_INET6, &from_lsa->u.sin6.sin6_addr, pa1, sizeof(pa1)), |
685 |
|
inet_ntop(AF_INET6, &((struct sockaddr_in6*)to)->sin6_addr, pa2, sizeof(pa2)), |
686 |
|
type, pr_type(type), icp->icmp6_code); |
687 |
|
|
688 |
|
read_len -= sizeof(struct icmp6_hdr); |
689 |
|
for (i = 0; i < read_len ; i++) { |
690 |
|
if (i % 16 == 0) |
691 |
|
printf("%04x:", i); |
692 |
|
if (i % 4 == 0) |
693 |
|
bb_putchar(' '); |
694 |
|
printf("%02x", p[i]); |
695 |
|
if ((i % 16 == 15) && (i + 1 < read_len)) |
696 |
|
bb_putchar('\n'); |
697 |
|
} |
698 |
|
bb_putchar('\n'); |
699 |
|
} |
700 |
|
# endif |
701 |
|
|
702 |
|
return 0; |
703 |
|
} |
704 |
|
#else /* !ENABLE_TRACEROUTE6 */ |
705 |
|
static ALWAYS_INLINE int |
706 |
|
packet_ok(int read_len, |
707 |
|
len_and_sockaddr *from_lsa IF_NOT_FEATURE_TRACEROUTE_VERBOSE(UNUSED_PARAM), |
708 |
|
struct sockaddr *to UNUSED_PARAM, |
709 |
|
int seq) |
710 |
|
{ |
711 |
|
return packet4_ok(read_len, &from_lsa->u.sin, seq); |
712 |
|
} |
713 |
|
#endif |
714 |
|
|
715 |
/* |
/* |
716 |
* Construct an Internet address representation. |
* Construct an Internet address representation. |
717 |
* If the nflag has been supplied, give |
* If the -n flag has been supplied, give |
718 |
* numeric value, otherwise try for symbolic name. |
* numeric value, otherwise try for symbolic name. |
719 |
*/ |
*/ |
720 |
static void |
static void |
721 |
print_inetname(struct sockaddr_in *from) |
print_inetname(const struct sockaddr *from) |
722 |
{ |
{ |
723 |
const char *ina; |
char *ina = xmalloc_sockaddr2dotted_noport(from); |
724 |
|
|
725 |
ina = inet_ntoa(from->sin_addr); |
if (option_mask32 & OPT_ADDR_NUM) { |
726 |
if (nflag) |
printf(" %s", ina); |
727 |
printf(" %s", ina); |
} else { |
|
else { |
|
728 |
char *n = NULL; |
char *n = NULL; |
729 |
if (from->sin_addr.s_addr != INADDR_ANY) |
|
730 |
|
if (from->sa_family != AF_INET |
731 |
|
|| ((struct sockaddr_in*)from)->sin_addr.s_addr != INADDR_ANY |
732 |
|
) { |
733 |
|
/* Try to reverse resolve if it is not 0.0.0.0 */ |
734 |
n = xmalloc_sockaddr2host_noport((struct sockaddr*)from); |
n = xmalloc_sockaddr2host_noport((struct sockaddr*)from); |
735 |
printf(" %s (%s)", (n ? n : ina), ina); |
} |
736 |
|
printf(" %s (%s)", (n ? n : ina), ina); |
737 |
free(n); |
free(n); |
738 |
} |
} |
739 |
|
free(ina); |
740 |
} |
} |
741 |
|
|
742 |
static void |
static void |
743 |
print(unsigned char *buf, int cc, struct sockaddr_in *from) |
print(int read_len, const struct sockaddr *from, const struct sockaddr *to) |
744 |
{ |
{ |
|
struct ip *ip; |
|
|
int hlen; |
|
|
|
|
|
ip = (struct ip *) buf; |
|
|
hlen = ip->ip_hl << 2; |
|
|
cc -= hlen; |
|
|
|
|
745 |
print_inetname(from); |
print_inetname(from); |
|
#if ENABLE_FEATURE_TRACEROUTE_VERBOSE |
|
|
if (verbose) |
|
|
printf(" %d bytes to %s", cc, inet_ntoa(ip->ip_dst)); |
|
|
#endif |
|
|
} |
|
|
|
|
|
|
|
|
static struct hostinfo * |
|
|
gethostinfo(const char *host) |
|
|
{ |
|
|
int n; |
|
|
struct hostent *hp; |
|
|
struct hostinfo *hi; |
|
|
char **p; |
|
|
uint32_t addr, *ap; |
|
|
|
|
|
hi = xzalloc(sizeof(*hi)); |
|
|
addr = inet_addr(host); |
|
|
if (addr != 0xffffffff) { |
|
|
hi->name = xstrdup(host); |
|
|
hi->n = 1; |
|
|
hi->addrs = xzalloc(sizeof(hi->addrs[0])); |
|
|
hi->addrs[0] = addr; |
|
|
return hi; |
|
|
} |
|
|
|
|
|
hp = xgethostbyname(host); |
|
|
if (hp->h_addrtype != AF_INET || hp->h_length != 4) |
|
|
bb_perror_msg_and_die("bad host %s", host); |
|
|
hi->name = xstrdup(hp->h_name); |
|
|
for (n = 0, p = hp->h_addr_list; *p != NULL; ++n, ++p) |
|
|
continue; |
|
|
hi->n = n; |
|
|
hi->addrs = xzalloc(n * sizeof(hi->addrs[0])); |
|
|
for (ap = hi->addrs, p = hp->h_addr_list; *p != NULL; ++ap, ++p) |
|
|
memcpy(ap, *p, sizeof(*ap)); |
|
|
return hi; |
|
|
} |
|
746 |
|
|
747 |
static void |
if (verbose) { |
748 |
freehostinfo(struct hostinfo *hi) |
char *ina = xmalloc_sockaddr2dotted_noport(to); |
749 |
{ |
#if ENABLE_TRACEROUTE6 |
750 |
free(hi->name); |
if (to->sa_family == AF_INET6) { |
751 |
free(hi->addrs); |
read_len -= sizeof(struct ip6_hdr); |
752 |
free(hi); |
} else |
|
} |
|
|
|
|
|
#if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE |
|
|
static void |
|
|
getaddr(uint32_t *ap, const char *host) |
|
|
{ |
|
|
struct hostinfo *hi; |
|
|
|
|
|
hi = gethostinfo(host); |
|
|
*ap = hi->addrs[0]; |
|
|
freehostinfo(hi); |
|
|
} |
|
753 |
#endif |
#endif |
754 |
|
{ |
755 |
|
read_len -= ((struct ip*)recv_pkt)->ip_hl << 2; |
756 |
|
} |
757 |
|
printf(" %d bytes to %s", read_len, ina); |
758 |
|
free(ina); |
759 |
|
} |
760 |
|
} |
761 |
|
|
762 |
static void |
static void |
763 |
print_delta_ms(unsigned t1p, unsigned t2p) |
print_delta_ms(unsigned t1p, unsigned t2p) |
764 |
{ |
{ |
765 |
unsigned tt = t2p - t1p; |
unsigned tt = t2p - t1p; |
766 |
printf(" %u.%03u ms", tt/1000, tt%1000); |
printf(" %u.%03u ms", tt / 1000, tt % 1000); |
767 |
} |
} |
768 |
|
|
769 |
int traceroute_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
/* |
770 |
int traceroute_main(int argc, char **argv) |
* Usage: [-dFIlnrvx] [-g gateway] [-i iface] [-f first_ttl] |
771 |
|
* [-m max_ttl] [ -p port] [-q nqueries] [-s src_addr] [-t tos] |
772 |
|
* [-w waittime] [-z pausemsecs] host [packetlen]" |
773 |
|
*/ |
774 |
|
static int |
775 |
|
common_traceroute_main(int op, char **argv) |
776 |
{ |
{ |
777 |
int code, n; |
int i; |
778 |
unsigned char *outp; |
int minpacket; |
|
uint32_t *ap; |
|
|
struct sockaddr_in *from; |
|
|
struct sockaddr_in *to; |
|
|
struct hostinfo *hi; |
|
|
int ttl, probe, i; |
|
|
int seq = 0; |
|
779 |
int tos = 0; |
int tos = 0; |
780 |
char *tos_str; |
int max_ttl = 30; |
781 |
|
int nprobes = 3; |
782 |
|
int first_ttl = 1; |
783 |
|
unsigned pausemsecs = 0; |
784 |
char *source; |
char *source; |
|
unsigned op; |
|
|
#if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE |
|
|
int lsrr = 0; |
|
|
#endif |
|
|
uint16_t off = 0; |
|
|
struct IFADDRLIST *al; |
|
785 |
char *device; |
char *device; |
786 |
int max_ttl = 30; |
char *tos_str; |
787 |
char *max_ttl_str; |
char *max_ttl_str; |
788 |
char *port_str; |
char *port_str; |
|
int nprobes = 3; |
|
789 |
char *nprobes_str; |
char *nprobes_str; |
790 |
char *waittime_str; |
char *waittime_str; |
|
unsigned pausemsecs = 0; |
|
791 |
char *pausemsecs_str; |
char *pausemsecs_str; |
|
int first_ttl = 1; |
|
792 |
char *first_ttl_str; |
char *first_ttl_str; |
793 |
#if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE |
#if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE |
794 |
llist_t *source_route_list = NULL; |
llist_t *source_route_list = NULL; |
795 |
|
int lsrr = 0; |
796 |
#endif |
#endif |
797 |
|
#if ENABLE_TRACEROUTE6 |
798 |
INIT_G(); |
sa_family_t af; |
|
from = (struct sockaddr_in *)&wherefrom; |
|
|
to = (struct sockaddr_in *)&whereto; |
|
|
|
|
|
//opterr = 0; |
|
|
#if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE |
|
|
opt_complementary = "x-x:g::"; |
|
799 |
#else |
#else |
800 |
opt_complementary = "x-x"; |
enum { af = AF_INET }; |
801 |
#endif |
#endif |
802 |
|
int ttl; |
803 |
|
int seq; |
804 |
|
len_and_sockaddr *from_lsa; |
805 |
|
struct sockaddr *lastaddr; |
806 |
|
struct sockaddr *to; |
807 |
|
|
808 |
op = getopt32(argv, "FIlnrdvxt:i:m:p:q:s:w:z:f:" |
INIT_G(); |
809 |
#if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE |
|
810 |
"g:" |
/* minimum 1 arg */ |
811 |
#endif |
opt_complementary = "-1:x-x" IF_FEATURE_TRACEROUTE_SOURCE_ROUTE(":g::"); |
812 |
|
op |= getopt32(argv, OPT_STRING |
813 |
, &tos_str, &device, &max_ttl_str, &port_str, &nprobes_str |
, &tos_str, &device, &max_ttl_str, &port_str, &nprobes_str |
814 |
, &source, &waittime_str, &pausemsecs_str, &first_ttl_str |
, &source, &waittime_str, &pausemsecs_str, &first_ttl_str |
815 |
#if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE |
#if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE |
816 |
, &source_route_list |
, &source_route_list |
817 |
#endif |
#endif |
818 |
); |
); |
819 |
|
argv += optind; |
820 |
|
|
821 |
if (op & OPT_DONT_FRAGMNT) |
#if 0 /* IGNORED */ |
822 |
off = IP_DF; |
if (op & OPT_IP_CHKSUM) |
|
if (op & OPT_IP_CHKSUM) { |
|
|
doipcksum = 0; |
|
823 |
bb_error_msg("warning: ip checksums disabled"); |
bb_error_msg("warning: ip checksums disabled"); |
824 |
} |
#endif |
825 |
if (op & OPT_TOS) |
if (op & OPT_TOS) |
826 |
tos = xatou_range(tos_str, 0, 255); |
tos = xatou_range(tos_str, 0, 255); |
827 |
if (op & OPT_MAX_TTL) |
if (op & OPT_MAX_TTL) |
835 |
* set the ip source address of the outbound |
* set the ip source address of the outbound |
836 |
* probe (e.g., on a multi-homed host). |
* probe (e.g., on a multi-homed host). |
837 |
*/ |
*/ |
838 |
if (getuid()) |
if (getuid() != 0) |
839 |
bb_error_msg_and_die("-s %s: permission denied", source); |
bb_error_msg_and_die(bb_msg_you_must_be_root); |
840 |
} |
} |
841 |
if (op & OPT_WAITTIME) |
if (op & OPT_WAITTIME) |
842 |
waittime = xatou_range(waittime_str, 2, 24 * 60 * 60); |
waittime = xatou_range(waittime_str, 1, 24 * 60 * 60); |
843 |
if (op & OPT_PAUSE_MS) |
if (op & OPT_PAUSE_MS) |
844 |
pausemsecs = xatou_range(pausemsecs_str, 0, 60 * 60 * 1000); |
pausemsecs = xatou_range(pausemsecs_str, 0, 60 * 60 * 1000); |
845 |
if (op & OPT_FIRST_TTL) |
if (op & OPT_FIRST_TTL) |
846 |
first_ttl = xatou_range(first_ttl_str, 1, 255); |
first_ttl = xatou_range(first_ttl_str, 1, max_ttl); |
847 |
|
|
848 |
#if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE |
#if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE |
849 |
if (source_route_list) { |
if (source_route_list) { |
850 |
while (source_route_list) { |
while (source_route_list) { |
851 |
|
len_and_sockaddr *lsa; |
852 |
|
|
853 |
if (lsrr >= NGATEWAYS) |
if (lsrr >= NGATEWAYS) |
854 |
bb_error_msg_and_die("no more than %d gateways", NGATEWAYS); |
bb_error_msg_and_die("no more than %d gateways", NGATEWAYS); |
855 |
getaddr(gwlist + lsrr, llist_pop(&source_route_list)); |
lsa = xhost_and_af2sockaddr(llist_pop(&source_route_list), 0, AF_INET); |
856 |
|
gwlist[lsrr] = lsa->u.sin.sin_addr.s_addr; |
857 |
|
free(lsa); |
858 |
++lsrr; |
++lsrr; |
859 |
} |
} |
860 |
optlen = (lsrr + 1) * sizeof(gwlist[0]); |
optlen = (lsrr + 1) * sizeof(gwlist[0]); |
861 |
} |
} |
862 |
#endif |
#endif |
863 |
|
|
|
if (first_ttl > max_ttl) { |
|
|
bb_error_msg_and_die( |
|
|
"first ttl (%d) may not be greater than max ttl (%d)", |
|
|
first_ttl, max_ttl); |
|
|
} |
|
|
|
|
|
minpacket = sizeof(*outip) + sizeof(*outdata) + optlen; |
|
|
|
|
|
#if ENABLE_FEATURE_TRACEROUTE_USE_ICMP |
|
|
if (useicmp) |
|
|
minpacket += 8; /* XXX magic number */ |
|
|
else |
|
|
#endif |
|
|
minpacket += sizeof(*outudp); |
|
|
packlen = minpacket; /* minimum sized packet */ |
|
|
|
|
864 |
/* Process destination and optional packet size */ |
/* Process destination and optional packet size */ |
865 |
switch (argc - optind) { |
minpacket = sizeof(*outip) + SIZEOF_ICMP_HDR + sizeof(*outdata) + optlen; |
866 |
|
if (!(op & OPT_USE_ICMP)) |
867 |
case 2: |
minpacket += sizeof(*outudp) - SIZEOF_ICMP_HDR; |
868 |
packlen = xatoul_range(argv[optind + 1], minpacket, maxpacket); |
#if ENABLE_TRACEROUTE6 |
869 |
/* Fall through */ |
af = AF_UNSPEC; |
870 |
|
if (op & OPT_IPV4) |
871 |
case 1: |
af = AF_INET; |
872 |
hostname = argv[optind]; |
if (op & OPT_IPV6) |
873 |
hi = gethostinfo(hostname); |
af = AF_INET6; |
874 |
setsin(to, hi->addrs[0]); |
dest_lsa = xhost_and_af2sockaddr(argv[0], port, af); |
875 |
if (hi->n > 1) |
af = dest_lsa->u.sa.sa_family; |
876 |
bb_error_msg("warning: %s has multiple addresses; using %s", |
if (af == AF_INET6) |
877 |
hostname, inet_ntoa(to->sin_addr)); |
minpacket = sizeof(struct outdata6_t); |
878 |
hostname = hi->name; |
#else |
879 |
hi->name = NULL; |
dest_lsa = xhost2sockaddr(argv[0], port); |
880 |
freehostinfo(hi); |
#endif |
881 |
break; |
packlen = minpacket; |
882 |
|
if (argv[1]) |
883 |
default: |
packlen = xatoul_range(argv[1], minpacket, 32 * 1024); |
|
bb_show_usage(); |
|
|
} |
|
884 |
|
|
885 |
/* Ensure the socket fds won't be 0, 1 or 2 */ |
/* Ensure the socket fds won't be 0, 1 or 2 */ |
886 |
bb_sanitize_stdio(); |
bb_sanitize_stdio(); |
887 |
|
|
888 |
rcvsock = xsocket(AF_INET, SOCK_RAW, IPPROTO_ICMP); |
#if ENABLE_TRACEROUTE6 |
889 |
|
if (af == AF_INET6) { |
890 |
|
xmove_fd(xsocket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6), rcvsock); |
891 |
|
# ifdef IPV6_RECVPKTINFO |
892 |
|
setsockopt(rcvsock, SOL_IPV6, IPV6_RECVPKTINFO, |
893 |
|
&const_int_1, sizeof(const_int_1)); |
894 |
|
setsockopt(rcvsock, SOL_IPV6, IPV6_2292PKTINFO, |
895 |
|
&const_int_1, sizeof(const_int_1)); |
896 |
|
# else |
897 |
|
setsockopt(rcvsock, SOL_IPV6, IPV6_PKTINFO, |
898 |
|
&const_int_1, sizeof(const_int_1)); |
899 |
|
# endif |
900 |
|
} else |
901 |
|
#endif |
902 |
|
{ |
903 |
|
xmove_fd(xsocket(AF_INET, SOCK_RAW, IPPROTO_ICMP), rcvsock); |
904 |
|
} |
905 |
|
|
906 |
#if TRACEROUTE_SO_DEBUG |
#if TRACEROUTE_SO_DEBUG |
907 |
if (op & OPT_DEBUG) |
if (op & OPT_DEBUG) |
912 |
setsockopt(rcvsock, SOL_SOCKET, SO_DONTROUTE, |
setsockopt(rcvsock, SOL_SOCKET, SO_DONTROUTE, |
913 |
&const_int_1, sizeof(const_int_1)); |
&const_int_1, sizeof(const_int_1)); |
914 |
|
|
915 |
sndsock = xsocket(AF_INET, SOCK_RAW, IPPROTO_RAW); |
#if ENABLE_TRACEROUTE6 |
916 |
|
if (af == AF_INET6) { |
917 |
|
static const int two = 2; |
918 |
|
if (setsockopt(rcvsock, SOL_RAW, IPV6_CHECKSUM, &two, sizeof(two)) < 0) |
919 |
|
bb_perror_msg_and_die("setsockopt RAW_CHECKSUM"); |
920 |
|
xmove_fd(xsocket(af, SOCK_DGRAM, 0), sndsock); |
921 |
|
} else |
922 |
|
#endif |
923 |
|
{ |
924 |
|
if (op & OPT_USE_ICMP) |
925 |
|
xmove_fd(xsocket(AF_INET, SOCK_RAW, IPPROTO_ICMP), sndsock); |
926 |
|
else |
927 |
|
xmove_fd(xsocket(AF_INET, SOCK_DGRAM, 0), sndsock); |
928 |
|
#if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE && defined IP_OPTIONS |
929 |
|
if (lsrr > 0) { |
930 |
|
unsigned char optlist[MAX_IPOPTLEN]; |
931 |
|
|
932 |
|
/* final hop */ |
933 |
|
gwlist[lsrr] = dest_lsa->u.sin.sin_addr.s_addr; |
934 |
|
++lsrr; |
935 |
|
|
936 |
#if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE |
/* force 4 byte alignment */ |
937 |
#if defined(IP_OPTIONS) |
optlist[0] = IPOPT_NOP; |
938 |
if (lsrr > 0) { |
/* loose source route option */ |
939 |
unsigned char optlist[MAX_IPOPTLEN]; |
optlist[1] = IPOPT_LSRR; |
940 |
|
i = lsrr * sizeof(gwlist[0]); |
941 |
/* final hop */ |
optlist[2] = i + 3; |
942 |
gwlist[lsrr] = to->sin_addr.s_addr; |
/* pointer to LSRR addresses */ |
943 |
++lsrr; |
optlist[3] = IPOPT_MINOFF; |
944 |
|
memcpy(optlist + 4, gwlist, i); |
945 |
/* force 4 byte alignment */ |
|
946 |
optlist[0] = IPOPT_NOP; |
if (setsockopt(sndsock, IPPROTO_IP, IP_OPTIONS, |
947 |
/* loose source route option */ |
(char *)optlist, i + sizeof(gwlist[0])) < 0) { |
948 |
optlist[1] = IPOPT_LSRR; |
bb_perror_msg_and_die("IP_OPTIONS"); |
949 |
i = lsrr * sizeof(gwlist[0]); |
} |
|
optlist[2] = i + 3; |
|
|
/* Pointer to LSRR addresses */ |
|
|
optlist[3] = IPOPT_MINOFF; |
|
|
memcpy(optlist + 4, gwlist, i); |
|
|
|
|
|
if ((setsockopt(sndsock, IPPROTO_IP, IP_OPTIONS, |
|
|
(char *)optlist, i + sizeof(gwlist[0]))) < 0) { |
|
|
bb_perror_msg_and_die("IP_OPTIONS"); |
|
950 |
} |
} |
951 |
|
#endif |
952 |
} |
} |
|
#endif /* IP_OPTIONS */ |
|
|
#endif /* CONFIG_FEATURE_TRACEROUTE_SOURCE_ROUTE */ |
|
953 |
|
|
954 |
#ifdef SO_SNDBUF |
#ifdef SO_SNDBUF |
955 |
if (setsockopt(sndsock, SOL_SOCKET, SO_SNDBUF, &packlen, sizeof(packlen)) < 0) { |
if (setsockopt(sndsock, SOL_SOCKET, SO_SNDBUF, &packlen, sizeof(packlen)) < 0) { |
956 |
bb_perror_msg_and_die("SO_SNDBUF"); |
bb_perror_msg_and_die("SO_SNDBUF"); |
957 |
} |
} |
958 |
#endif |
#endif |
|
#ifdef IP_HDRINCL |
|
|
if (setsockopt(sndsock, IPPROTO_IP, IP_HDRINCL, &const_int_1, sizeof(const_int_1)) < 0 |
|
|
&& errno != ENOPROTOOPT |
|
|
) { |
|
|
bb_perror_msg_and_die("IP_HDRINCL"); |
|
|
} |
|
|
#else |
|
959 |
#ifdef IP_TOS |
#ifdef IP_TOS |
960 |
if (tos_str && setsockopt(sndsock, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)) < 0) { |
if ((op & OPT_TOS) && setsockopt(sndsock, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)) < 0) { |
961 |
bb_perror_msg_and_die("setsockopt tos %d", tos); |
bb_perror_msg_and_die("setsockopt tos %d", tos); |
962 |
} |
} |
963 |
#endif |
#endif |
964 |
|
#ifdef IP_DONTFRAG |
965 |
|
if (op & OPT_DONT_FRAGMNT) |
966 |
|
setsockopt(sndsock, IPPROTO_IP, IP_DONTFRAG, |
967 |
|
&const_int_1, sizeof(const_int_1)); |
968 |
#endif |
#endif |
969 |
#if TRACEROUTE_SO_DEBUG |
#if TRACEROUTE_SO_DEBUG |
970 |
if (op & OPT_DEBUG) |
if (op & OPT_DEBUG) |
975 |
setsockopt(sndsock, SOL_SOCKET, SO_DONTROUTE, |
setsockopt(sndsock, SOL_SOCKET, SO_DONTROUTE, |
976 |
&const_int_1, sizeof(const_int_1)); |
&const_int_1, sizeof(const_int_1)); |
977 |
|
|
|
/* Revert to non-privileged user after opening sockets */ |
|
|
xsetgid(getgid()); |
|
|
xsetuid(getuid()); |
|
|
|
|
978 |
outip = xzalloc(packlen); |
outip = xzalloc(packlen); |
979 |
|
|
980 |
outip->ip_v = IPVERSION; |
ident = getpid(); |
|
if (tos_str) |
|
|
outip->ip_tos = tos; |
|
|
outip->ip_len = htons(packlen); |
|
|
outip->ip_off = htons(off); |
|
|
outp = (unsigned char *)(outip + 1); |
|
|
outip->ip_dst = to->sin_addr; |
|
|
|
|
|
outip->ip_hl = (outp - (unsigned char *)outip) >> 2; |
|
|
ident = (getpid() & 0xffff) | 0x8000; |
|
|
#if ENABLE_FEATURE_TRACEROUTE_USE_ICMP |
|
|
if (useicmp) { |
|
|
outip->ip_p = IPPROTO_ICMP; |
|
|
outicmp = (struct icmp *)outp; |
|
|
outicmp->icmp_type = ICMP_ECHO; |
|
|
outicmp->icmp_id = htons(ident); |
|
|
outdata = (outdata_t *)(outp + 8); /* XXX magic number */ |
|
|
} else |
|
|
#endif |
|
|
{ |
|
|
outip->ip_p = IPPROTO_UDP; |
|
|
outudp = (struct udphdr *)outp; |
|
|
outudp->source = htons(ident); |
|
|
outudp->len = htons((uint16_t)(packlen - (sizeof(*outip) + optlen))); |
|
|
outdata = (outdata_t *)(outudp + 1); |
|
|
} |
|
|
|
|
|
/* Get the interface address list */ |
|
|
n = ifaddrlist(&al); |
|
|
|
|
|
/* Look for a specific device */ |
|
|
if (op & OPT_DEVICE) { |
|
|
for (i = n; i > 0; --i, ++al) |
|
|
if (strcmp(device, al->device) == 0) |
|
|
goto found_dev; |
|
|
bb_error_msg_and_die("can't find interface %s", device); |
|
|
} |
|
|
found_dev: |
|
981 |
|
|
982 |
/* Determine our source address */ |
if (af == AF_INET) { |
983 |
if (!(op & OPT_SOURCE)) { |
if (op & OPT_USE_ICMP) { |
984 |
/* |
ident |= 0x8000; |
985 |
* If a device was specified, use the interface address. |
outicmp->icmp_type = ICMP_ECHO; |
986 |
* Otherwise, try to determine our source address. |
outicmp->icmp_id = htons(ident); |
987 |
*/ |
outdata = (struct outdata_t *)((char *)outicmp + SIZEOF_ICMP_HDR); |
|
if (op & OPT_DEVICE) |
|
|
setsin(from, al->addr); |
|
|
findsaddr(to, from); |
|
|
} else { |
|
|
hi = gethostinfo(source); |
|
|
source = hi->name; |
|
|
hi->name = NULL; |
|
|
/* |
|
|
* If the device was specified make sure it |
|
|
* corresponds to the source address specified. |
|
|
* Otherwise, use the first address (and warn if |
|
|
* there are more than one). |
|
|
*/ |
|
|
if (op & OPT_DEVICE) { |
|
|
for (i = hi->n, ap = hi->addrs; i > 0; --i, ++ap) |
|
|
if (*ap == al->addr) |
|
|
goto found_dev2; |
|
|
bb_error_msg_and_die("%s is not on interface %s", |
|
|
source, device); |
|
|
found_dev2: |
|
|
setsin(from, *ap); |
|
988 |
} else { |
} else { |
989 |
setsin(from, hi->addrs[0]); |
outdata = (struct outdata_t *)(outudp + 1); |
|
if (hi->n > 1) |
|
|
bb_error_msg( |
|
|
"warning: %s has multiple addresses; using %s", |
|
|
source, inet_ntoa(from->sin_addr)); |
|
990 |
} |
} |
|
freehostinfo(hi); |
|
991 |
} |
} |
992 |
|
|
993 |
outip->ip_src = from->sin_addr; |
if (op & OPT_DEVICE) /* hmm, do we need error check? */ |
994 |
#ifndef IP_HDRINCL |
setsockopt_bindtodevice(sndsock, device); |
995 |
xbind(sndsock, (struct sockaddr *)from, sizeof(*from)); |
|
996 |
|
if (op & OPT_SOURCE) { |
997 |
|
#if ENABLE_TRACEROUTE6 |
998 |
|
// TODO: need xdotted_and_af2sockaddr? |
999 |
|
len_and_sockaddr *source_lsa = xhost_and_af2sockaddr(source, 0, af); |
1000 |
|
#else |
1001 |
|
len_and_sockaddr *source_lsa = xdotted2sockaddr(source, 0); |
1002 |
|
#endif |
1003 |
|
/* Ping4 does this (why?) */ |
1004 |
|
if (af == AF_INET) |
1005 |
|
if (setsockopt(sndsock, IPPROTO_IP, IP_MULTICAST_IF, |
1006 |
|
&source_lsa->u.sa, source_lsa->len)) |
1007 |
|
bb_error_msg_and_die("can't set multicast source interface"); |
1008 |
|
//TODO: we can query source port we bound to, |
1009 |
|
// and check it in replies... if we care enough |
1010 |
|
xbind(sndsock, &source_lsa->u.sa, source_lsa->len); |
1011 |
|
free(source_lsa); |
1012 |
|
} |
1013 |
|
#if ENABLE_TRACEROUTE6 |
1014 |
|
else if (af == AF_INET6) { |
1015 |
|
//TODO: why we don't do it for IPv4? |
1016 |
|
len_and_sockaddr *source_lsa; |
1017 |
|
|
1018 |
|
int probe_fd = xsocket(af, SOCK_DGRAM, 0); |
1019 |
|
if (op & OPT_DEVICE) |
1020 |
|
setsockopt_bindtodevice(probe_fd, device); |
1021 |
|
set_nport(dest_lsa, htons(1025)); |
1022 |
|
/* dummy connect. makes kernel pick source IP (and port) */ |
1023 |
|
xconnect(probe_fd, &dest_lsa->u.sa, dest_lsa->len); |
1024 |
|
set_nport(dest_lsa, htons(port)); |
1025 |
|
|
1026 |
|
/* read IP and port */ |
1027 |
|
source_lsa = get_sock_lsa(probe_fd); |
1028 |
|
if (source_lsa == NULL) |
1029 |
|
bb_error_msg_and_die("can't get probe addr"); |
1030 |
|
|
1031 |
|
close(probe_fd); |
1032 |
|
|
1033 |
|
/* bind our sockets to this IP (but not port) */ |
1034 |
|
set_nport(source_lsa, 0); |
1035 |
|
xbind(sndsock, &source_lsa->u.sa, source_lsa->len); |
1036 |
|
xbind(rcvsock, &source_lsa->u.sa, source_lsa->len); |
1037 |
|
|
1038 |
|
free(source_lsa); |
1039 |
|
} |
1040 |
#endif |
#endif |
1041 |
|
|
1042 |
printf("traceroute to %s (%s)", hostname, inet_ntoa(to->sin_addr)); |
/* Revert to non-privileged user after opening sockets */ |
1043 |
|
xsetgid(getgid()); |
1044 |
|
xsetuid(getuid()); |
1045 |
|
|
1046 |
|
printf("traceroute to %s (%s)", argv[0], |
1047 |
|
xmalloc_sockaddr2dotted_noport(&dest_lsa->u.sa)); |
1048 |
if (op & OPT_SOURCE) |
if (op & OPT_SOURCE) |
1049 |
printf(" from %s", source); |
printf(" from %s", source); |
1050 |
printf(", %d hops max, %d byte packets\n", max_ttl, packlen); |
printf(", %d hops max, %d byte packets\n", max_ttl, packlen); |
|
fflush(stdout); |
|
1051 |
|
|
1052 |
|
from_lsa = dup_sockaddr(dest_lsa); |
1053 |
|
lastaddr = xzalloc(dest_lsa->len); |
1054 |
|
to = xzalloc(dest_lsa->len); |
1055 |
|
seq = 0; |
1056 |
for (ttl = first_ttl; ttl <= max_ttl; ++ttl) { |
for (ttl = first_ttl; ttl <= max_ttl; ++ttl) { |
1057 |
uint32_t lastaddr = 0; |
int probe; |
1058 |
int gotlastaddr = 0; |
int unreachable = 0; /* counter */ |
1059 |
|
int gotlastaddr = 0; /* flags */ |
1060 |
int got_there = 0; |
int got_there = 0; |
1061 |
int unreachable = 0; |
int first = 1; |
|
int sentfirst = 0; |
|
1062 |
|
|
1063 |
printf("%2d ", ttl); |
printf("%2d", ttl); |
1064 |
for (probe = 0; probe < nprobes; ++probe) { |
for (probe = 0; probe < nprobes; ++probe) { |
1065 |
int cc; |
int read_len; |
1066 |
unsigned t1; |
unsigned t1; |
1067 |
unsigned t2; |
unsigned t2; |
1068 |
struct ip *ip; |
struct ip *ip; |
1069 |
|
|
1070 |
if (sentfirst && pausemsecs > 0) |
if (!first && pausemsecs > 0) |
1071 |
usleep(pausemsecs * 1000); |
usleep(pausemsecs * 1000); |
1072 |
|
fflush_all(); |
1073 |
|
|
1074 |
t1 = monotonic_us(); |
t1 = monotonic_us(); |
1075 |
send_probe(++seq, ttl); |
send_probe(++seq, ttl); |
1076 |
++sentfirst; |
|
1077 |
while ((cc = wait_for_reply(rcvsock, from)) != 0) { |
first = 0; |
1078 |
|
while ((read_len = wait_for_reply(from_lsa, to)) != 0) { |
1079 |
t2 = monotonic_us(); |
t2 = monotonic_us(); |
1080 |
i = packet_ok(packet, cc, from, seq); |
i = packet_ok(read_len, from_lsa, to, seq); |
1081 |
/* Skip short packet */ |
/* Skip short packet */ |
1082 |
if (i == 0) |
if (i == 0) |
1083 |
continue; |
continue; |
1084 |
if (!gotlastaddr || |
|
1085 |
from->sin_addr.s_addr != lastaddr) { |
if (!gotlastaddr |
1086 |
print(packet, cc, from); |
|| (memcmp(lastaddr, &from_lsa->u.sa, from_lsa->len) != 0) |
1087 |
lastaddr = from->sin_addr.s_addr; |
) { |
1088 |
++gotlastaddr; |
print(read_len, &from_lsa->u.sa, to); |
1089 |
|
memcpy(lastaddr, &from_lsa->u.sa, from_lsa->len); |
1090 |
|
gotlastaddr = 1; |
1091 |
} |
} |
1092 |
|
|
1093 |
print_delta_ms(t1, t2); |
print_delta_ms(t1, t2); |
1094 |
ip = (struct ip *)packet; |
ip = (struct ip *)recv_pkt; |
1095 |
if (op & OPT_TTL_FLAG) |
|
1096 |
printf(" (%d)", ip->ip_ttl); |
if (from_lsa->u.sa.sa_family == AF_INET) |
1097 |
if (i == -2) { |
if (op & OPT_TTL_FLAG) |
1098 |
if (ip->ip_ttl <= 1) |
printf(" (%d)", ip->ip_ttl); |
1099 |
printf(" !"); |
|
|
++got_there; |
|
|
break; |
|
|
} |
|
1100 |
/* time exceeded in transit */ |
/* time exceeded in transit */ |
1101 |
if (i == -1) |
if (i == -1) |
1102 |
break; |
break; |
1103 |
code = i - 1; |
i--; |
1104 |
switch (code) { |
switch (i) { |
1105 |
|
#if ENABLE_TRACEROUTE6 |
1106 |
|
case ICMP6_DST_UNREACH_NOPORT << 8: |
1107 |
|
got_there = 1; |
1108 |
|
break; |
1109 |
|
#endif |
1110 |
case ICMP_UNREACH_PORT: |
case ICMP_UNREACH_PORT: |
1111 |
if (ip->ip_ttl <= 1) |
if (ip->ip_ttl <= 1) |
1112 |
printf(" !"); |
printf(" !"); |
1113 |
++got_there; |
got_there = 1; |
1114 |
break; |
break; |
1115 |
|
|
1116 |
case ICMP_UNREACH_NET: |
case ICMP_UNREACH_NET: |
1117 |
++unreachable; |
#if ENABLE_TRACEROUTE6 && (ICMP6_DST_UNREACH_NOROUTE != ICMP_UNREACH_NET) |
1118 |
|
case ICMP6_DST_UNREACH_NOROUTE << 8: |
1119 |
|
#endif |
1120 |
printf(" !N"); |
printf(" !N"); |
1121 |
|
++unreachable; |
1122 |
break; |
break; |
|
|
|
1123 |
case ICMP_UNREACH_HOST: |
case ICMP_UNREACH_HOST: |
1124 |
++unreachable; |
#if ENABLE_TRACEROUTE6 |
1125 |
|
case ICMP6_DST_UNREACH_ADDR << 8: |
1126 |
|
#endif |
1127 |
printf(" !H"); |
printf(" !H"); |
1128 |
|
++unreachable; |
1129 |
break; |
break; |
|
|
|
1130 |
case ICMP_UNREACH_PROTOCOL: |
case ICMP_UNREACH_PROTOCOL: |
|
++got_there; |
|
1131 |
printf(" !P"); |
printf(" !P"); |
1132 |
|
got_there = 1; |
1133 |
break; |
break; |
|
|
|
1134 |
case ICMP_UNREACH_NEEDFRAG: |
case ICMP_UNREACH_NEEDFRAG: |
|
++unreachable; |
|
1135 |
printf(" !F-%d", pmtu); |
printf(" !F-%d", pmtu); |
1136 |
|
++unreachable; |
1137 |
break; |
break; |
|
|
|
1138 |
case ICMP_UNREACH_SRCFAIL: |
case ICMP_UNREACH_SRCFAIL: |
1139 |
++unreachable; |
#if ENABLE_TRACEROUTE6 |
1140 |
|
case ICMP6_DST_UNREACH_ADMIN << 8: |
1141 |
|
#endif |
1142 |
printf(" !S"); |
printf(" !S"); |
1143 |
|
++unreachable; |
1144 |
break; |
break; |
|
|
|
1145 |
case ICMP_UNREACH_FILTER_PROHIB: |
case ICMP_UNREACH_FILTER_PROHIB: |
1146 |
case ICMP_UNREACH_NET_PROHIB: /* misuse */ |
case ICMP_UNREACH_NET_PROHIB: /* misuse */ |
|
++unreachable; |
|
1147 |
printf(" !A"); |
printf(" !A"); |
1148 |
|
++unreachable; |
1149 |
break; |
break; |
|
|
|
1150 |
case ICMP_UNREACH_HOST_PROHIB: |
case ICMP_UNREACH_HOST_PROHIB: |
|
++unreachable; |
|
1151 |
printf(" !C"); |
printf(" !C"); |
1152 |
|
++unreachable; |
1153 |
break; |
break; |
|
|
|
1154 |
case ICMP_UNREACH_HOST_PRECEDENCE: |
case ICMP_UNREACH_HOST_PRECEDENCE: |
|
++unreachable; |
|
1155 |
printf(" !V"); |
printf(" !V"); |
1156 |
|
++unreachable; |
1157 |
break; |
break; |
|
|
|
1158 |
case ICMP_UNREACH_PRECEDENCE_CUTOFF: |
case ICMP_UNREACH_PRECEDENCE_CUTOFF: |
|
++unreachable; |
|
1159 |
printf(" !C"); |
printf(" !C"); |
1160 |
|
++unreachable; |
1161 |
break; |
break; |
|
|
|
1162 |
case ICMP_UNREACH_NET_UNKNOWN: |
case ICMP_UNREACH_NET_UNKNOWN: |
1163 |
case ICMP_UNREACH_HOST_UNKNOWN: |
case ICMP_UNREACH_HOST_UNKNOWN: |
|
++unreachable; |
|
1164 |
printf(" !U"); |
printf(" !U"); |
1165 |
|
++unreachable; |
1166 |
break; |
break; |
|
|
|
1167 |
case ICMP_UNREACH_ISOLATED: |
case ICMP_UNREACH_ISOLATED: |
|
++unreachable; |
|
1168 |
printf(" !I"); |
printf(" !I"); |
1169 |
|
++unreachable; |
1170 |
break; |
break; |
|
|
|
1171 |
case ICMP_UNREACH_TOSNET: |
case ICMP_UNREACH_TOSNET: |
1172 |
case ICMP_UNREACH_TOSHOST: |
case ICMP_UNREACH_TOSHOST: |
|
++unreachable; |
|
1173 |
printf(" !T"); |
printf(" !T"); |
1174 |
|
++unreachable; |
1175 |
break; |
break; |
|
|
|
1176 |
default: |
default: |
1177 |
|
printf(" !<%d>", i); |
1178 |
++unreachable; |
++unreachable; |
|
printf(" !<%d>", code); |
|
1179 |
break; |
break; |
1180 |
} |
} |
1181 |
break; |
break; |
1182 |
} |
} |
1183 |
if (cc == 0) |
/* there was no packet at all? */ |
1184 |
printf(" *"); |
if (read_len == 0) |
1185 |
(void)fflush(stdout); |
printf(" *"); |
1186 |
} |
} |
1187 |
bb_putchar('\n'); |
bb_putchar('\n'); |
1188 |
if (got_there || |
if (got_there |
1189 |
(unreachable > 0 && unreachable >= nprobes - 1)) |
|| (unreachable > 0 && unreachable >= nprobes - 1) |
1190 |
|
) { |
1191 |
break; |
break; |
1192 |
|
} |
1193 |
} |
} |
1194 |
|
|
1195 |
return 0; |
return 0; |
1196 |
} |
} |
1197 |
|
|
1198 |
|
int traceroute_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
1199 |
|
int traceroute_main(int argc UNUSED_PARAM, char **argv) |
1200 |
|
{ |
1201 |
|
return common_traceroute_main(0, argv); |
1202 |
|
} |
1203 |
|
|
1204 |
|
#if ENABLE_TRACEROUTE6 |
1205 |
|
int traceroute6_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
1206 |
|
int traceroute6_main(int argc UNUSED_PARAM, char **argv) |
1207 |
|
{ |
1208 |
|
return common_traceroute_main(OPT_IPV6, argv); |
1209 |
|
} |
1210 |
|
#endif |