--- embedaddon/mtr/ui/net.c 2019/10/21 14:25:31 1.1.1.1 +++ embedaddon/mtr/ui/net.c 2021/03/17 00:07:30 1.1.1.2 @@ -11,14 +11,16 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "config.h" #include +#include +#include #include #include #include @@ -37,20 +39,17 @@ #include "display.h" #include "dns.h" #include "utils.h" +#include "packet/sockaddr.h" #define MinSequence 33000 #define MaxSequence 65536 static int packetsize; /* packet size used by ping */ -static void sockaddrtop( - struct sockaddr *saddr, - char *strptr, - size_t len); - struct nethost { - ip_t addr; - ip_t addrs[MAXPATH]; /* for multi paths byMin */ + ip_t addr; /* Latest host to respond */ + ip_t addrs[MAX_PATH]; /* For Multi paths/Path Changes: List of all hosts that have responded */ + int err; int xmit; int returned; int sent; @@ -69,7 +68,7 @@ struct nethost { int saved[SAVED_PINGS]; int saved_seq_offset; struct mplslen mpls; - struct mplslen mplss[MAXPATH]; + struct mplslen mplss[MAX_PATH]; }; @@ -85,26 +84,13 @@ static struct nethost host[MaxHost]; static struct sequence sequence[MaxSequence]; static struct packet_command_pipe_t packet_command_pipe; -#ifdef ENABLE_IPV6 static struct sockaddr_storage sourcesockaddr_struct; static struct sockaddr_storage remotesockaddr_struct; -static struct sockaddr_in6 *ssa6 = - (struct sockaddr_in6 *) &sourcesockaddr_struct; -static struct sockaddr_in6 *rsa6 = - (struct sockaddr_in6 *) &remotesockaddr_struct; -#else -static struct sockaddr_in sourcesockaddr_struct; -static struct sockaddr_in remotesockaddr_struct; -#endif static struct sockaddr *sourcesockaddr = (struct sockaddr *) &sourcesockaddr_struct; static struct sockaddr *remotesockaddr = (struct sockaddr *) &remotesockaddr_struct; -static struct sockaddr_in *ssa4 = - (struct sockaddr_in *) &sourcesockaddr_struct; -static struct sockaddr_in *rsa4 = - (struct sockaddr_in *) &remotesockaddr_struct; static ip_t *sourceaddress; static ip_t *remoteaddress; @@ -121,6 +107,14 @@ static char localaddr[INET_ADDRSTRLEN]; static int batch_at = 0; static int numhosts = 10; + +#define host_addr_cmp(index, other, af) \ + addrcmp((void *) &(host[(index)].addr), (void *) (other), (af)) + +#define host_addrs_cmp(index, path, other, af) \ + addrcmp((void *) &(host[(index)].addrs[path]), (void *) (other), (af)) + + /* return the number of microseconds to wait before sending the next ping */ int calc_deltatime( @@ -181,15 +175,42 @@ static void net_send_query( int time_to_live = index + 1; send_probe_command(ctl, &packet_command_pipe, remoteaddress, - sourceaddress, packetsize, seq, time_to_live); + sourceaddress, packet_size, seq, time_to_live); } -/* We got a return on something we sent out. Record the address and - time. */ +/* + Mark a sequence entry as completed and return the host index + being probed. + + Returns -1 in the case of an invalid sequence number. +*/ +static int mark_sequence_complete( + int seq) +{ + if ((seq < 0) || (seq >= MaxSequence)) { + return -1; + } + + if (!sequence[seq].transit) { + return -1; + } + sequence[seq].transit = 0; + + return sequence[seq].index; +} + + +/* + A probe has successfully completed. + + Record the round trip time and address of the responding host. +*/ + static void net_process_ping( struct mtr_ctl *ctl, int seq, + int err, struct mplslen *mpls, ip_t * addr, int totusec) @@ -198,103 +219,100 @@ static void net_process_ping( int oldavg; /* usedByMin */ int oldjavg; /* usedByMin */ int i; /* usedByMin */ + int found = 0; #ifdef ENABLE_IPV6 char addrcopy[sizeof(struct in6_addr)]; #else char addrcopy[sizeof(struct in_addr)]; #endif + struct nethost *nh = NULL; - addrcpy((void *) &addrcopy, (char *) addr, ctl->af); + memcpy(&addrcopy, addr, sockaddr_addr_size(sourcesockaddr)); - if ((seq < 0) || (seq >= MaxSequence)) { + index = mark_sequence_complete(seq); + if (index < 0) { return; } + nh = &host[index]; + nh->err = err; - if (!sequence[seq].transit) { - return; - } - sequence[seq].transit = 0; - index = sequence[seq].index; - if (addrcmp((void *) &(host[index].addr), - (void *) &ctl->unspec_addr, ctl->af) == 0) { - /* should be out of if as addr can change */ - addrcpy((void *) &(host[index].addr), addrcopy, ctl->af); - host[index].mpls = *mpls; - display_rawhost(ctl, index, (void *) &(host[index].addr)); - - /* multi paths */ - addrcpy((void *) &(host[index].addrs[0]), addrcopy, ctl->af); - host[index].mplss[0] = *mpls; - } else { - for (i = 0; i < MAXPATH;) { - if (addrcmp - ((void *) &(host[index].addrs[i]), (void *) &addrcopy, - ctl->af) == 0 - || addrcmp((void *) &(host[index].addrs[i]), - (void *) &ctl->unspec_addr, ctl->af) == 0) { + if (addrcmp(&nh->addr, &addrcopy, ctl->af) != 0) { + for (i = 0; i < MAX_PATH;) { + if (addrcmp(&nh->addrs[i], &nh->addr, ctl->af) == 0) { + found = 1; /* This host is already in the list */ break; } + if (addrcmp(&nh->addrs[i], &ctl->unspec_addr, ctl->af) == 0) { + break; /* Found first vacant position */ + } i++; } - if (addrcmp((void *) &(host[index].addrs[i]), addrcopy, ctl->af) != - 0 && i < MAXPATH) { - addrcpy((void *) &(host[index].addrs[i]), addrcopy, ctl->af); - host[index].mplss[i] = *mpls; - display_rawhost(ctl, index, (void *) &(host[index].addrs[i])); + if (found == 0 && i < MAX_PATH) { + memcpy(&nh->addrs[i], &nh->addr, sockaddr_addr_size(sourcesockaddr)); + + nh->mplss[i] = nh->mpls; + display_rawhost(ctl, index, (void *)&(nh->addrs[i]), (void *)&(nh->addrs[i])); } + + /* Always save the latest host in nh->addr. This + * allows maxTTL to change whenever path changes. + */ + memcpy(&nh->addr, addrcopy, sockaddr_addr_size(sourcesockaddr)); + nh->mpls = *mpls; + display_rawhost(ctl, index, (void *)&(nh->addr), (void *)&(nh->mpls)); } - host[index].jitter = totusec - host[index].last; - if (host[index].jitter < 0) { - host[index].jitter = -host[index].jitter; + nh->jitter = totusec - nh->last; + if (nh->jitter < 0) { + nh->jitter = -nh->jitter; } - host[index].last = totusec; + nh->last = totusec; - if (host[index].returned < 1) { - host[index].best = host[index].worst = host[index].gmean = totusec; - host[index].avg = host[index].ssd = 0; + if (nh->returned < 1) { + nh->best = nh->worst = nh->gmean = totusec; + nh->avg = nh->ssd = 0; - host[index].jitter = host[index].jworst = host[index].jinta = 0; + nh->jitter = nh->jworst = nh->jinta = 0; } - if (totusec < host[index].best) { - host[index].best = totusec; + if (totusec < nh->best) { + nh->best = totusec; } - if (totusec > host[index].worst) { - host[index].worst = totusec; + if (totusec > nh->worst) { + nh->worst = totusec; } - if (host[index].jitter > host[index].jworst) { - host[index].jworst = host[index].jitter; + if (nh->jitter > nh->jworst) { + nh->jworst = nh->jitter; } - host[index].returned++; - oldavg = host[index].avg; - host[index].avg += (totusec - oldavg + .0) / host[index].returned; - host[index].ssd += - (totusec - oldavg + .0) * (totusec - host[index].avg); + nh->returned++; + oldavg = nh->avg; + nh->avg += (totusec - oldavg + .0) / nh->returned; + nh->ssd += + (totusec - oldavg + .0) * (totusec - nh->avg); - oldjavg = host[index].javg; - host[index].javg += - (host[index].jitter - oldjavg) / host[index].returned; + oldjavg = nh->javg; + nh->javg += + (nh->jitter - oldjavg) / nh->returned; /* below algorithm is from rfc1889, A.8 */ - host[index].jinta += - host[index].jitter - ((host[index].jinta + 8) >> 4); + nh->jinta += + nh->jitter - ((nh->jinta + 8) >> 4); - if (host[index].returned > 1) { - host[index].gmean = - pow((double) host[index].gmean, - (host[index].returned - 1.0) / host[index].returned) - * pow((double) totusec, 1.0 / host[index].returned); + if (nh->returned > 1) { + nh->gmean = + pow((double) nh->gmean, + (nh->returned - 1.0) / nh->returned) + * pow((double) totusec, 1.0 / nh->returned); } - host[index].sent = 0; - host[index].up = 1; - host[index].transit = 0; + nh->sent = 0; + nh->up = 1; + nh->transit = 0; net_save_return(index, sequence[seq].saved_seq, totusec); display_rawping(ctl, index, totusec, seq); @@ -325,6 +343,15 @@ ip_t *net_addrs( return (ip_t *) & (host[at].addrs[i]); } +/* + Get the error code corresponding to a host entry. +*/ +int net_err( + int at) +{ + return host[at].err; +} + void *net_mpls( int at) { @@ -440,16 +467,23 @@ int net_max( int max; max = 0; - for (at = 0; at < ctl->maxTTL - 1; at++) { - if (addrcmp((void *) &(host[at].addr), - (void *) remoteaddress, ctl->af) == 0) { + for (at = 0; at < ctl->maxTTL; at++) { + if (host_addr_cmp(at , remoteaddress, ctl->af) == 0) { return at + 1; - } else if (addrcmp((void *) &(host[at].addr), - (void *) &ctl->unspec_addr, ctl->af) != 0) { + } else if (host[at].err != 0) { + /* + If a hop has returned an ICMP error + (such as "no route to host") then we'll consider that the + final hop. + */ + return at + 1; + } else if (host_addr_cmp(at, &ctl->unspec_addr, ctl->af) != 0) { max = at + 2; } } + if (max > ctl->maxTTL) + max = ctl->maxTTL; return max; } @@ -503,6 +537,7 @@ int net_send_batch( struct mtr_ctl *ctl) { int n_unknown = 0, i; + int restart = 0; /* randomized packet size and/or bit pattern if packetsize<0 and/or bitpattern<0. abs(packetsize) and/or abs(bitpattern) will be used @@ -514,7 +549,7 @@ int net_send_batch( have a range for "rand()" that runs to 32768, and the destination range is 10000, you end up with 4 out of 32768 0-2768's and only 3 out of 32768 for results 2769 .. 9999. - As our detination range (in the example 10000) is much + As our destination range (in the example 10000) is much smaller (reasonable packet sizes), and our rand() range much larger, this effect is insignificant. Oh! That other formula didn't work. */ @@ -532,9 +567,7 @@ int net_send_batch( net_send_query(ctl, batch_at, abs(packetsize)); for (i = ctl->fstTTL - 1; i < batch_at; i++) { - if (addrcmp - ((void *) &(host[i].addr), (void *) &ctl->unspec_addr, - ctl->af) == 0) + if (host_addr_cmp(i, &ctl->unspec_addr, ctl->af) == 0) n_unknown++; /* The second condition in the next "if" statement was added in mtr-0.56, @@ -542,19 +575,24 @@ int net_send_batch( hosts. Removed in 0.65. If the line proves necessary, it should at least NOT trigger that line when host[i].addr == 0 */ - if ((addrcmp((void *) &(host[i].addr), - (void *) remoteaddress, ctl->af) == 0)) - n_unknown = MaxHost; /* Make sure we drop into "we should restart" */ + if (host_addr_cmp(i, remoteaddress, ctl->af) == 0) { + restart = 1; + numhosts = i + 1; /* Saves batch_at - index number of probes in the next round!*/ + break; + } } if ( /* success in reaching target */ - (addrcmp((void *) &(host[batch_at].addr), - (void *) remoteaddress, ctl->af) == 0) || + (host_addr_cmp(batch_at, remoteaddress, ctl->af) == 0) || /* fail in consecutive maxUnknown (firewall?) */ (n_unknown > ctl->maxUnknown) || /* or reach limit */ (batch_at >= ctl->maxTTL - 1)) { + restart = 1; numhosts = batch_at + 1; + } + + if(restart) { batch_at = ctl->fstTTL - 1; return 1; } @@ -582,6 +620,61 @@ static void net_validate_interface_address( /* + Given the name of a network interface and a preferred address + family (IPv4 or IPv6), find the source IP address for sending + probes from that interface. +*/ +static void net_find_interface_address_from_name( + struct sockaddr_storage *addr, + int address_family, + const char *interface_name) +{ + struct ifaddrs *ifaddrs; + struct ifaddrs *interface; + int found_interface_name = 0; + + if (getifaddrs(&ifaddrs) != 0) { + error(EXIT_FAILURE, errno, "getifaddrs failure"); + } + + interface = ifaddrs; + while (interface != NULL) { + if (interface->ifa_addr != NULL && !strcmp(interface->ifa_name, interface_name)) { + found_interface_name = 1; + + if (interface->ifa_addr->sa_family == address_family) { + if (address_family == AF_INET) { + memcpy(addr, + interface->ifa_addr, sizeof(struct sockaddr_in)); + freeifaddrs(ifaddrs); + + return; + } else if (address_family == AF_INET6) { + memcpy(addr, + interface->ifa_addr, sizeof(struct sockaddr_in6)); + freeifaddrs(ifaddrs); + + return; + } + } + } + + interface = interface->ifa_next; + } + + if (!found_interface_name) { + error(EXIT_FAILURE, 0, "no such interface"); + } else if (address_family == AF_INET) { + error(EXIT_FAILURE, 0, "interface missing IPv4 address"); + } else if (address_family == AF_INET6) { + error(EXIT_FAILURE, 0, "interface missing IPv6 address"); + } else { + error(EXIT_FAILURE, 0, "interface missing address"); + } +} + + +/* Find the local address we will use to sent to the remote host by connecting a UDP socket and checking the address the socket is bound to. @@ -592,8 +685,6 @@ static void net_find_local_address( int udp_socket; int addr_length; struct sockaddr_storage remote_sockaddr; - struct sockaddr_in *remote4; - struct sockaddr_in6 *remote6; udp_socket = socket(remotesockaddr->sa_family, SOCK_DGRAM, IPPROTO_UDP); @@ -605,24 +696,22 @@ static void net_find_local_address( We need to set the port to a non-zero value for the connect to succeed. */ - if (remotesockaddr->sa_family == AF_INET6) { -#ifdef ENABLE_IPV6 - addr_length = sizeof(struct sockaddr_in6); + addr_length = sockaddr_size(&remotesockaddr_struct); + memcpy(&remote_sockaddr, &remotesockaddr_struct, addr_length); + *sockaddr_port_offset(&remote_sockaddr) = htons(1); - memcpy(&remote_sockaddr, rsa6, addr_length); - remote6 = (struct sockaddr_in6 *) &remote_sockaddr; - remote6->sin6_port = htons(1); -#endif - } else { - addr_length = sizeof(struct sockaddr_in); - - memcpy(&remote_sockaddr, rsa4, addr_length); - remote4 = (struct sockaddr_in *) &remote_sockaddr; - remote4->sin_port = htons(1); - } - if (connect - (udp_socket, (struct sockaddr *) &remote_sockaddr, addr_length)) { + (udp_socket, (struct sockaddr *) &remote_sockaddr, sockaddr_size(&remote_sockaddr))) { +#ifdef __linux__ + /* Linux doesn't require source address, so we can support + * a case when mtr is run against unreachable host (that can become + * reachable) */ + if (errno == EHOSTUNREACH) { + close(udp_socket); + localaddr[0] = '\0'; + return; + } +#endif error(EXIT_FAILURE, errno, "udp socket connect failed"); } @@ -631,7 +720,7 @@ static void net_find_local_address( error(EXIT_FAILURE, errno, "local address determination failed"); } - sockaddrtop(sourcesockaddr, localaddr, sizeof(localaddr)); + inet_ntop(sourcesockaddr->sa_family, sockaddr_addr_offset(sourcesockaddr), localaddr, sizeof(localaddr)); close(udp_socket); } @@ -651,27 +740,18 @@ int net_open( net_reset(ctl); - remotesockaddr->sa_family = hostent->h_addrtype; + remotesockaddr->sa_family = sourcesockaddr->sa_family = hostent->h_addrtype; + memcpy(sockaddr_addr_offset(remotesockaddr), hostent->h_addr, sockaddr_addr_size(remotesockaddr)); - switch (hostent->h_addrtype) { - case AF_INET: - addrcpy((void *) &(rsa4->sin_addr), hostent->h_addr, AF_INET); - sourceaddress = (ip_t *) & (ssa4->sin_addr); - remoteaddress = (ip_t *) & (rsa4->sin_addr); - break; -#ifdef ENABLE_IPV6 - case AF_INET6: - addrcpy((void *) &(rsa6->sin6_addr), hostent->h_addr, AF_INET6); - sourceaddress = (ip_t *) & (ssa6->sin6_addr); - remoteaddress = (ip_t *) & (rsa6->sin6_addr); - break; -#endif - default: - error(EXIT_FAILURE, 0, "net_open bad address type"); - } + sourceaddress = sockaddr_addr_offset(sourcesockaddr); + remoteaddress = sockaddr_addr_offset(remotesockaddr); if (ctl->InterfaceAddress) { net_validate_interface_address(ctl->af, ctl->InterfaceAddress); + } else if (ctl->InterfaceName) { + net_find_interface_address_from_name( + &sourcesockaddr_struct, ctl->af, ctl->InterfaceName); + inet_ntop(sourcesockaddr->sa_family, sockaddr_addr_offset(sourcesockaddr), localaddr, sizeof(localaddr)); } else { net_find_local_address(); } @@ -691,21 +771,8 @@ void net_reopen( } remotesockaddr->sa_family = addr->h_addrtype; - addrcpy((void *) remoteaddress, addr->h_addr, addr->h_addrtype); - - switch (addr->h_addrtype) { - case AF_INET: - addrcpy((void *) &(rsa4->sin_addr), addr->h_addr, AF_INET); - break; -#ifdef ENABLE_IPV6 - case AF_INET6: - addrcpy((void *) &(rsa6->sin6_addr), addr->h_addr, AF_INET6); - break; -#endif - default: - error(EXIT_FAILURE, 0, "net_reopen bad address type"); - } - + memcpy(remoteaddress, addr->h_addr, sockaddr_addr_size(remotesockaddr)); + memcpy(sockaddr_addr_offset(remotesockaddr), addr->h_addr, sockaddr_addr_size(remotesockaddr)); net_reset(ctl); net_send_batch(ctl); } @@ -794,41 +861,10 @@ void net_save_return( host[at].saved[idx] = ms; } -/* Similar to inet_ntop but uses a sockaddr as it's argument. */ -static void sockaddrtop( - struct sockaddr *saddr, - char *strptr, - size_t len) -{ - struct sockaddr_in *sa4; -#ifdef ENABLE_IPV6 - struct sockaddr_in6 *sa6; -#endif - - switch (saddr->sa_family) { - case AF_INET: - sa4 = (struct sockaddr_in *) saddr; - xstrncpy(strptr, inet_ntoa(sa4->sin_addr), len - 1); - strptr[len - 1] = '\0'; - return; -#ifdef ENABLE_IPV6 - case AF_INET6: - sa6 = (struct sockaddr_in6 *) saddr; - inet_ntop(sa6->sin6_family, &(sa6->sin6_addr), strptr, len); - return; -#endif - default: - error(0, 0, "sockaddrtop unknown address type"); - strptr[0] = '\0'; - return; - } -} - - /* Address comparison. */ int addrcmp( - char *a, - char *b, + void *a, + void *b, int family) { int rc = -1; @@ -845,25 +881,6 @@ int addrcmp( } return rc; -} - -/* Address copy. */ -void addrcpy( - char *a, - char *b, - int family) -{ - - switch (family) { - case AF_INET: - memcpy(a, b, sizeof(struct in_addr)); - break; -#ifdef ENABLE_IPV6 - case AF_INET6: - memcpy(a, b, sizeof(struct in6_addr)); - break; -#endif - } } /* for GTK frontend */