traceroute: fix traceroute6 -I (icmp mode)
function old new delta common_traceroute_main 3530 3544 +14 Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
This commit is contained in:
parent
ef2366cdca
commit
3978adc445
@ -414,9 +414,32 @@ struct globals {
|
||||
waittime = 5; \
|
||||
} while (0)
|
||||
|
||||
#define outicmp ((struct icmp *)(outip + 1))
|
||||
#define outudp ((struct udphdr *)(outip + 1))
|
||||
|
||||
#define outudp ((struct udphdr *)(outip + 1))
|
||||
#define outudp6 ((struct udphdr *)(((struct ip6_hdr*)outip) + 1))
|
||||
#define outicmp ((struct icmp *)(outip + 1))
|
||||
#define outicmp6 ((struct icmp *)(((struct ip6_hdr*)outip) + 1))
|
||||
/* NB: for icmp echo, IPv4 and IPv6 fields are the same size and offset:
|
||||
* struct icmp:
|
||||
* uint8_t icmp_type;
|
||||
* uint8_t icmp_code;
|
||||
* uint16_t icmp_cksum;
|
||||
* uint16_t icmp_id;
|
||||
* uint16_t icmp_seq;
|
||||
* struct icmp6_hdr:
|
||||
* uint8_t icmp6_type;
|
||||
* uint8_t icmp6_code;
|
||||
* uint16_t icmp6_cksum;
|
||||
* uint16_t icmp6_id;
|
||||
* uint16_t icmp6_seq;
|
||||
* therefore both outicmp and outicmp6 are pointers to *IPv4* icmp struct.
|
||||
* SIZEOF_ICMP_HDR == 8 is the same for both, as well.
|
||||
* However, values of these pointers are not the same (since IPv6 IP header is larger),
|
||||
* and icmp_type constants are not the same:
|
||||
* #define ICMP_ECHO 8
|
||||
* #define ICMP_ECHOREPLY 0
|
||||
* #define ICMP6_ECHO_REQUEST 128
|
||||
* #define ICMP6_ECHO_REPLY 129
|
||||
*/
|
||||
|
||||
static int
|
||||
wait_for_reply(len_and_sockaddr *from_lsa, struct sockaddr *to, unsigned *timestamp_us, int *left_ms)
|
||||
@ -446,14 +469,16 @@ send_probe(int seq, int ttl)
|
||||
{
|
||||
int len, res;
|
||||
void *out;
|
||||
struct icmp *icp;
|
||||
|
||||
/* Payload */
|
||||
#if ENABLE_TRACEROUTE6
|
||||
if (dest_lsa->u.sa.sa_family == AF_INET6) {
|
||||
struct outdata6_t *pkt = (struct outdata6_t *) outdata;
|
||||
struct outdata6_t *pkt = (void *) outdata;
|
||||
pkt->ident6 = htonl(ident);
|
||||
pkt->seq6 = htonl(seq);
|
||||
/*gettimeofday(&pkt->tv, &tz);*/
|
||||
icp = outicmp6;
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
@ -461,19 +486,23 @@ send_probe(int seq, int ttl)
|
||||
outdata->ttl = ttl;
|
||||
// UNUSED: was storing gettimeofday's result there, but never ever checked it
|
||||
/*memcpy(&outdata->tv, tp, sizeof(outdata->tv));*/
|
||||
|
||||
if (option_mask32 & OPT_USE_ICMP) {
|
||||
outicmp->icmp_seq = htons(seq);
|
||||
|
||||
/* Always calculate checksum for icmp packets */
|
||||
outicmp->icmp_cksum = 0;
|
||||
outicmp->icmp_cksum = inet_cksum(
|
||||
outicmp,
|
||||
((char*)outip + packlen) - (char*)outicmp
|
||||
);
|
||||
if (outicmp->icmp_cksum == 0)
|
||||
outicmp->icmp_cksum = 0xffff;
|
||||
}
|
||||
icp = outicmp;
|
||||
}
|
||||
out = outdata;
|
||||
if (option_mask32 & OPT_USE_ICMP) {
|
||||
out = icp;
|
||||
/*icp->icmp_type = ICMP[6]_ECHO; - already set */
|
||||
/*icp->icmp_code = 0; - already set */
|
||||
/*icp->icmp_id = ident; - already set */
|
||||
icp->icmp_seq = htons(seq);
|
||||
/* Always calculate checksum for icmp packets */
|
||||
icp->icmp_cksum = 0;
|
||||
icp->icmp_cksum = inet_cksum(
|
||||
icp,
|
||||
((char*)outip + packlen) - (char*)icp
|
||||
);
|
||||
if (icp->icmp_cksum == 0)
|
||||
icp->icmp_cksum = 0xffff;
|
||||
}
|
||||
|
||||
//BUG! verbose is (x & OPT_VERBOSE), not a counter!
|
||||
@ -502,7 +531,6 @@ send_probe(int seq, int ttl)
|
||||
}
|
||||
#endif
|
||||
|
||||
out = outdata;
|
||||
#if ENABLE_TRACEROUTE6
|
||||
if (dest_lsa->u.sa.sa_family == AF_INET6) {
|
||||
res = setsockopt_int(sndsock, SOL_IPV6, IPV6_UNICAST_HOPS, ttl);
|
||||
@ -516,8 +544,6 @@ send_probe(int seq, int ttl)
|
||||
if (res != 0)
|
||||
bb_perror_msg_and_die("setsockopt(%s) %d", "TTL", ttl);
|
||||
#endif
|
||||
if (option_mask32 & OPT_USE_ICMP)
|
||||
out = outicmp;
|
||||
}
|
||||
|
||||
if (!(option_mask32 & OPT_USE_ICMP)) {
|
||||
@ -579,7 +605,12 @@ packet4_ok(int read_len, const struct sockaddr_in *from, int seq)
|
||||
int hlen;
|
||||
const struct ip *ip;
|
||||
|
||||
/* NB: reads from (AF_INET, SOCK_RAW, IPPROTO_ICMP) socket
|
||||
* return the entire IP packet (IOW: they do not strip IP header).
|
||||
* This differs from (AF_INET6, SOCK_RAW, IPPROTO_ICMPV6) sockets!?
|
||||
*/
|
||||
ip = (struct ip *) recv_pkt;
|
||||
|
||||
hlen = ip->ip_hl << 2;
|
||||
if (read_len < hlen + ICMP_MINLEN) {
|
||||
#if ENABLE_FEATURE_TRACEROUTE_VERBOSE
|
||||
@ -598,9 +629,20 @@ packet4_ok(int read_len, const struct sockaddr_in *from, int seq)
|
||||
if (code == ICMP_UNREACH_NEEDFRAG)
|
||||
pmtu = ntohs(icp->icmp_nextmtu);
|
||||
|
||||
if ((option_mask32 & OPT_USE_ICMP)
|
||||
&& type == ICMP_ECHOREPLY
|
||||
&& icp->icmp_id == htons(ident)
|
||||
&& icp->icmp_seq == htons(seq)
|
||||
) {
|
||||
/* In UDP mode, when we reach the machine, we (usually)
|
||||
* would get "port unreachable" - in ICMP we got "echo reply".
|
||||
* Simulate "port unreachable" for caller:
|
||||
*/
|
||||
return ICMP_UNREACH_PORT+1;
|
||||
}
|
||||
|
||||
if ((type == ICMP_TIMXCEED && code == ICMP_TIMXCEED_INTRANS)
|
||||
|| type == ICMP_UNREACH
|
||||
|| type == ICMP_ECHOREPLY
|
||||
) {
|
||||
const struct ip *hip;
|
||||
const struct udphdr *up;
|
||||
@ -610,14 +652,6 @@ packet4_ok(int read_len, const struct sockaddr_in *from, int seq)
|
||||
if (option_mask32 & OPT_USE_ICMP) {
|
||||
struct icmp *hicmp;
|
||||
|
||||
/* XXX */
|
||||
if (type == ICMP_ECHOREPLY
|
||||
&& icp->icmp_id == htons(ident)
|
||||
&& icp->icmp_seq == htons(seq)
|
||||
) {
|
||||
return ICMP_UNREACH_PORT+1;
|
||||
}
|
||||
|
||||
hicmp = (struct icmp *)((unsigned char *)hip + hlen);
|
||||
if (hlen + SIZEOF_ICMP_HDR <= read_len
|
||||
&& hip->ip_p == IPPROTO_ICMP
|
||||
@ -648,6 +682,7 @@ packet4_ok(int read_len, const struct sockaddr_in *from, int seq)
|
||||
printf("\n%d bytes from %s to "
|
||||
"%s: icmp type %d (%s) code %d\n",
|
||||
read_len, inet_ntoa(from->sin_addr),
|
||||
//BUG: inet_ntoa() returns static buf! x2 is NONO!
|
||||
inet_ntoa(ip->ip_dst),
|
||||
type, pr_type(type), icp->icmp_code);
|
||||
for (i = 4; i < read_len; i += sizeof(*lp))
|
||||
@ -673,11 +708,27 @@ packet_ok(int read_len, len_and_sockaddr *from_lsa,
|
||||
if (from_lsa->u.sa.sa_family == AF_INET)
|
||||
return packet4_ok(read_len, &from_lsa->u.sin, seq);
|
||||
|
||||
/* NB: reads from (AF_INET6, SOCK_RAW, IPPROTO_ICMPV6) socket
|
||||
* return only ICMP packet (IOW: they strip IPv6 header).
|
||||
* This differs from (AF_INET, SOCK_RAW, IPPROTO_ICMP) sockets!?
|
||||
*/
|
||||
icp = (struct icmp6_hdr *) recv_pkt;
|
||||
|
||||
type = icp->icmp6_type;
|
||||
code = icp->icmp6_code;
|
||||
|
||||
if ((option_mask32 & OPT_USE_ICMP)
|
||||
&& type == ICMP6_ECHO_REPLY
|
||||
&& icp->icmp6_id == htons(ident)
|
||||
&& icp->icmp6_seq == htons(seq)
|
||||
) {
|
||||
/* In UDP mode, when we reach the machine, we (usually)
|
||||
* would get "port unreachable" - in ICMP we got "echo reply".
|
||||
* Simulate "port unreachable" for caller:
|
||||
*/
|
||||
return (ICMP6_DST_UNREACH_NOPORT << 8) + 1;
|
||||
}
|
||||
|
||||
if ((type == ICMP6_TIME_EXCEEDED && code == ICMP6_TIME_EXCEED_TRANSIT)
|
||||
|| type == ICMP6_DST_UNREACH
|
||||
) {
|
||||
@ -787,8 +838,13 @@ print(int read_len, const struct sockaddr *from, const struct sockaddr *to)
|
||||
if (verbose) {
|
||||
char *ina = xmalloc_sockaddr2dotted_noport(to);
|
||||
#if ENABLE_TRACEROUTE6
|
||||
/* NB: reads from (AF_INET, SOCK_RAW, IPPROTO_ICMP) socket
|
||||
* return the entire IP packet (IOW: they do not strip IP header).
|
||||
* Reads from (AF_INET6, SOCK_RAW, IPPROTO_ICMPV6) do strip IPv6
|
||||
* header and return only ICMP6 packet. Weird.
|
||||
*/
|
||||
if (to->sa_family == AF_INET6) {
|
||||
read_len -= sizeof(struct ip6_hdr);
|
||||
/* read_len -= sizeof(struct ip6_hdr); - WRONG! */
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
@ -844,6 +900,9 @@ common_traceroute_main(int op, char **argv)
|
||||
struct sockaddr *lastaddr;
|
||||
struct sockaddr *to;
|
||||
|
||||
/* Ensure the socket fds won't be 0, 1 or 2 */
|
||||
bb_sanitize_stdio();
|
||||
|
||||
INIT_G();
|
||||
|
||||
op |= getopt32(argv, "^"
|
||||
@ -885,12 +944,14 @@ common_traceroute_main(int op, char **argv)
|
||||
|
||||
/* Process destination and optional packet size */
|
||||
minpacket = sizeof(struct ip)
|
||||
+ SIZEOF_ICMP_HDR
|
||||
+ sizeof(struct outdata_t);
|
||||
if (!(op & OPT_USE_ICMP))
|
||||
minpacket = sizeof(struct ip)
|
||||
+ sizeof(struct udphdr)
|
||||
+ sizeof(struct outdata_t);
|
||||
if (op & OPT_USE_ICMP) {
|
||||
minpacket = sizeof(struct ip)
|
||||
+ SIZEOF_ICMP_HDR
|
||||
+ sizeof(struct outdata_t);
|
||||
port = 0; /* on ICMP6 sockets, sendto(ipv6.nonzero_port) throws EINVAL */
|
||||
}
|
||||
#if ENABLE_TRACEROUTE6
|
||||
af = AF_UNSPEC;
|
||||
if (op & OPT_IPV4)
|
||||
@ -899,10 +960,15 @@ common_traceroute_main(int op, char **argv)
|
||||
af = AF_INET6;
|
||||
dest_lsa = xhost_and_af2sockaddr(argv[0], port, af);
|
||||
af = dest_lsa->u.sa.sa_family;
|
||||
if (af == AF_INET6)
|
||||
if (af == AF_INET6) {
|
||||
minpacket = sizeof(struct ip6_hdr)
|
||||
+ sizeof(struct udphdr)
|
||||
+ sizeof(struct outdata6_t);
|
||||
+ sizeof(struct udphdr)
|
||||
+ sizeof(struct outdata6_t);
|
||||
if (op & OPT_USE_ICMP)
|
||||
minpacket = sizeof(struct ip6_hdr)
|
||||
+ SIZEOF_ICMP_HDR
|
||||
+ sizeof(struct outdata6_t);
|
||||
}
|
||||
#else
|
||||
dest_lsa = xhost2sockaddr(argv[0], port);
|
||||
#endif
|
||||
@ -910,13 +976,10 @@ common_traceroute_main(int op, char **argv)
|
||||
if (argv[1])
|
||||
packlen = xatoul_range(argv[1], minpacket, 32 * 1024);
|
||||
|
||||
/* Ensure the socket fds won't be 0, 1 or 2 */
|
||||
bb_sanitize_stdio();
|
||||
|
||||
#if ENABLE_TRACEROUTE6
|
||||
if (af == AF_INET6) {
|
||||
xmove_fd(xsocket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6), rcvsock);
|
||||
setsockopt_1(rcvsock, SOL_IPV6, IPV6_RECVPKTINFO);
|
||||
setsockopt_1(rcvsock, SOL_IPV6, IPV6_RECVPKTINFO); //why?
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
@ -934,7 +997,10 @@ common_traceroute_main(int op, char **argv)
|
||||
if (af == AF_INET6) {
|
||||
if (setsockopt_int(rcvsock, SOL_RAW, IPV6_CHECKSUM, 2) != 0)
|
||||
bb_perror_msg_and_die("setsockopt(%s)", "IPV6_CHECKSUM");
|
||||
xmove_fd(xsocket(af, SOCK_DGRAM, 0), sndsock);
|
||||
if (op & OPT_USE_ICMP)
|
||||
xmove_fd(xsocket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6), sndsock);
|
||||
else
|
||||
xmove_fd(xsocket(AF_INET6, SOCK_DGRAM, 0), sndsock);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
@ -943,6 +1009,7 @@ common_traceroute_main(int op, char **argv)
|
||||
else
|
||||
xmove_fd(xsocket(AF_INET, SOCK_DGRAM, 0), sndsock);
|
||||
}
|
||||
//TODO xmove_fd to here!
|
||||
|
||||
#ifdef SO_SNDBUF
|
||||
if (setsockopt_SOL_SOCKET_int(sndsock, SO_SNDBUF, packlen) != 0) {
|
||||
@ -970,21 +1037,25 @@ common_traceroute_main(int op, char **argv)
|
||||
ident = getpid();
|
||||
|
||||
if (!ENABLE_TRACEROUTE6 || af == AF_INET) {
|
||||
outdata = (void*)(outudp + 1);
|
||||
if (op & OPT_USE_ICMP) {
|
||||
ident |= 0x8000;
|
||||
outicmp->icmp_type = ICMP_ECHO;
|
||||
/*outicmp->icmp_code = 0; - set by xzalloc */
|
||||
outicmp->icmp_id = htons(ident);
|
||||
outdata = (struct outdata_t *)((char *)outicmp + SIZEOF_ICMP_HDR);
|
||||
} else {
|
||||
outdata = (struct outdata_t *)(outudp + 1);
|
||||
outdata = (void*)((char *)outicmp + SIZEOF_ICMP_HDR);
|
||||
}
|
||||
}
|
||||
#if ENABLE_TRACEROUTE6
|
||||
if (af == AF_INET6) {
|
||||
outdata = (void*)((char*)outip
|
||||
+ sizeof(struct ip6_hdr)
|
||||
+ sizeof(struct udphdr)
|
||||
);
|
||||
outdata = (void*)(outudp6 + 1);
|
||||
if (op & OPT_USE_ICMP) {
|
||||
ident |= 0x8000;
|
||||
outicmp6->icmp_type = ICMP6_ECHO_REQUEST;
|
||||
/*outicmp->icmp_code = 0; - set by xzalloc */
|
||||
outicmp6->icmp_id = htons(ident);
|
||||
outdata = (void*)((char *)outicmp6 + SIZEOF_ICMP_HDR);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -1013,26 +1084,16 @@ common_traceroute_main(int op, char **argv)
|
||||
//TODO: why we don't do it for IPv4?
|
||||
len_and_sockaddr *source_lsa;
|
||||
|
||||
int probe_fd = xsocket(af, SOCK_DGRAM, 0);
|
||||
if (op & OPT_DEVICE)
|
||||
setsockopt_bindtodevice(probe_fd, device);
|
||||
set_nport(&dest_lsa->u.sa, htons(1025));
|
||||
/* dummy connect. makes kernel pick source IP (and port) */
|
||||
xconnect(probe_fd, &dest_lsa->u.sa, dest_lsa->len);
|
||||
set_nport(&dest_lsa->u.sa, htons(port));
|
||||
|
||||
/* read IP and port */
|
||||
source_lsa = get_sock_lsa(probe_fd);
|
||||
/* Connect makes kernel pick source IP and port */
|
||||
xconnect(sndsock, &dest_lsa->u.sa, dest_lsa->len);
|
||||
/* Read IP and port */
|
||||
source_lsa = get_sock_lsa(sndsock);
|
||||
if (source_lsa == NULL)
|
||||
bb_simple_error_msg_and_die("can't get probe addr");
|
||||
|
||||
close(probe_fd);
|
||||
|
||||
/* bind our sockets to this IP (but not port) */
|
||||
/* bind our recv socket to this IP (but not port) */
|
||||
set_nport(&source_lsa->u.sa, 0);
|
||||
xbind(sndsock, &source_lsa->u.sa, source_lsa->len);
|
||||
xbind(rcvsock, &source_lsa->u.sa, source_lsa->len);
|
||||
|
||||
free(source_lsa);
|
||||
}
|
||||
#endif
|
||||
@ -1067,10 +1128,9 @@ common_traceroute_main(int op, char **argv)
|
||||
unsigned t1;
|
||||
unsigned t2;
|
||||
int left_ms;
|
||||
struct ip *ip;
|
||||
|
||||
fflush_all();
|
||||
if (probe != 0 && pausemsecs > 0)
|
||||
if (probe != 0)
|
||||
msleep(pausemsecs);
|
||||
|
||||
send_probe(++seq, ttl);
|
||||
@ -1099,15 +1159,18 @@ common_traceroute_main(int op, char **argv)
|
||||
}
|
||||
|
||||
print_delta_ms(t1, t2);
|
||||
ip = (struct ip *)recv_pkt;
|
||||
|
||||
if (from_lsa->u.sa.sa_family == AF_INET)
|
||||
if (op & OPT_TTL_FLAG)
|
||||
if (from_lsa->u.sa.sa_family == AF_INET) {
|
||||
if (op & OPT_TTL_FLAG) {
|
||||
struct ip *ip = (struct ip *)recv_pkt;
|
||||
printf(" (%d)", ip->ip_ttl);
|
||||
}
|
||||
}
|
||||
|
||||
/* time exceeded in transit */
|
||||
/* Got a "time exceeded in transit" icmp message? */
|
||||
if (icmp_code == -1)
|
||||
break;
|
||||
|
||||
icmp_code--;
|
||||
switch (icmp_code) {
|
||||
#if ENABLE_TRACEROUTE6
|
||||
@ -1115,12 +1178,13 @@ common_traceroute_main(int op, char **argv)
|
||||
got_there = 1;
|
||||
break;
|
||||
#endif
|
||||
case ICMP_UNREACH_PORT:
|
||||
case ICMP_UNREACH_PORT: {
|
||||
struct ip *ip = (struct ip *)recv_pkt;
|
||||
if (ip->ip_ttl <= 1)
|
||||
printf(" !");
|
||||
got_there = 1;
|
||||
break;
|
||||
|
||||
}
|
||||
case ICMP_UNREACH_NET:
|
||||
#if ENABLE_TRACEROUTE6 && (ICMP6_DST_UNREACH_NOROUTE != ICMP_UNREACH_NET)
|
||||
case ICMP6_DST_UNREACH_NOROUTE << 8:
|
||||
|
Loading…
Reference in New Issue
Block a user