blob: 545ad79cdc101545b29f1c71bb371beb47d78754 [file] [log] [blame]
/* ping.c - check network connectivity
*
* Copyright 2014 Rob Landley <rob@landley.net>
*
* Not in SUSv4.
USE_PING(NEWTOY(ping, "<1>1t#<0>255c#<0s#<0>65535I:W#<0w#<0q46[-46]", TOYFLAG_ROOTONLY|TOYFLAG_USR|TOYFLAG_BIN))
config PING
bool "ping"
default n
help
usage: ping [OPTIONS] HOST
Check network connectivity by sending packets to a host and reporting
its response.
Send ICMP ECHO_REQUEST packets to ipv4 or ipv6 addresses and prints each
echo it receives back, with round trip time.
Options:
-4, -6 Force IPv4 or IPv6
-c CNT Send CNT many packets
-I IFACE/IP Source interface or address
-q Quiet, only displays output at start and when finished
-s SIZE Packet SIZE in bytes (default 56)
-t TTL Set Time (number of hops) To Live
-W SEC Seconds to wait for response after all packets sent (default 10)
-w SEC Exit after this many seconds
*/
#define FOR_ping
#include "toys.h"
#include <ifaddrs.h>
GLOBALS(
long wait_exit;
long wait_resp;
char *iface;
long size;
long count;
long ttl;
int sock;
)
void *sock2addr(struct sockaddr *sa)
{
if (sa->sa_family == AF_INET)
return &((struct sockaddr_in *)sa)->sin_addr;
return &((struct sockaddr_in6 *)sa)->sin6_addr;
}
void ping_main(void)
{
struct addrinfo *ai, *ai2;
struct ifaddrs *ifa, *ifa2 = 0;
union {
struct sockaddr_in in;
struct sockaddr_in6 in6;
} src_addr;
struct sockaddr *sa = (void *)&src_addr;
int family = 0;
// no 4/6 specified: -I has only one, arg must match
// no 4/6 specified: arg is one, -I must match
// 4/6 specified, both must match
if (!(toys.optflags&FLAG_s)) TT.size = 56; // 64-PHDR_LEN
if (toys.optflags&FLAG_6) family = AF_INET6;
else if (toys.optflags&FLAG_4) family = AF_INET;
else family = 0;
// If -I src_addr look it up. Allow numeric address of correct type.
memset(&src_addr, 0, sizeof(src_addr));
if (TT.iface) {
if (!(toys.optflags&FLAG_6) && inet_pton(AF_INET, TT.iface,
(void *)&src_addr.in.sin_addr))
family = sa->sa_family = AF_INET;
else if (!(toys.optflags&FLAG_4) && inet_pton(AF_INET6, TT.iface,
(void *)&src_addr.in6.sin6_addr))
family = sa->sa_family = AF_INET6;
else if (getifaddrs(&ifa2)) perror_exit("getifaddrs");
}
// Look up HOST address, filtering for correct type.
// If -I but no -46 then find compatible type between -I and HOST
ai2 = xgetaddrinfo(toys.optargs[0], 0, family, 0, 0, 0);
for (ai = ai2; ai; ai = ai->ai_next) {
if (family && family!=ai->ai_family) continue;
if (ai->ai_family!=AF_INET && ai->ai_family!=AF_INET6) continue;
if (!TT.iface || !ifa2) break;
for (ifa = ifa2; ifa; ifa = ifa->ifa_next) {
if (!ifa->ifa_addr || ifa->ifa_addr->sa_family!=ai->ai_family
|| strcmp(ifa->ifa_name, TT.iface)) continue;
sa = (void *)ifa->ifa_addr;
break;
}
if (ifa) break;
}
if (!ai)
error_exit("no v%d addr for -I %s", 4+2*(family==AF_INET6), TT.iface);
inet_ntop(family, sock2addr(sa), toybuf, sizeof(toybuf));
printf("host=%s\n", toybuf);
*toybuf = 0;
inet_ntop(ai->ai_family, sock2addr(ai->ai_addr), toybuf, sizeof(toybuf));
printf("targ=%s\n", toybuf);
// Open raw socket
TT.sock = xsocket(ai->ai_family, SOCK_DGRAM, (ifa->ifa_addr->sa_family == AF_INET) ?
IPPROTO_ICMP : IPPROTO_ICMPV6);
if (TT.iface && bind(TT.sock, ifa->ifa_addr, sizeof(src_addr)))
perror_exit("bind");
if (CFG_TOYBOX_FREE) {
freeaddrinfo(ai2);
if (ifa2) freeifaddrs(ifa2);
}
}