1218887Sdim// Copyright 2017 The Fuchsia Authors. All rights reserved. 2218887Sdim// Use of this source code is governed by a BSD-style license that can be 3353358Sdim// found in the LICENSE file. 4353358Sdim 5353358Sdim#include <arpa/inet.h> 6218887Sdim#include <errno.h> 7218887Sdim#include <fcntl.h> 8218887Sdim#include <limits.h> 9218887Sdim#include <netdb.h> 10218887Sdim#include <netinet/in.h> 11218887Sdim#include <netinet/ip_icmp.h> 12276479Sdim#include <poll.h> 13249423Sdim#include <stdlib.h> 14249423Sdim#include <stdio.h> 15249423Sdim#include <string.h> 16218887Sdim#include <unistd.h> 17218887Sdim 18239462Sdim#include <zircon/syscalls.h> 19218887Sdim#include <zircon/compiler.h> 20218887Sdim 21218887Sdimtypedef struct { 22218887Sdim icmphdr hdr; 23218887Sdim uint8_t payload[32]; 24218887Sdim} __PACKED packet_t; 25218887Sdim 26218887Sdimint usage() { 27218887Sdim fprintf(stderr, "usage: ping [ <option>* ] destination\n"); 28218887Sdim fprintf(stderr, "Send ICMP ECHO_REQUEST to a destination. This destination\n"); 29341825Sdim fprintf(stderr, "may be a hostname (google.com) or an IP address (8.8.8.8).\n"); 30218887Sdim fprintf(stderr, "-c count: Only receive count packets (default = 3)\n"); 31341825Sdim fprintf(stderr, "--help: View this help message\n"); 32218887Sdim return -1; 33218887Sdim} 34218887Sdim 35218887Sdimint main(int argc, const char** argv) { 36226633Sdim size_t count = 3; 37218887Sdim argv++; 38327952Sdim argc--; 39327952Sdim while (argc > 1) { 40239462Sdim if (!strncmp(argv[0], "-c", strlen("-c"))) { 41327952Sdim argv++; 42353358Sdim argc--; 43327952Sdim if (argc < 1) { 44327952Sdim return usage(); 45327952Sdim } 46353358Sdim char* endptr; 47327952Sdim count = strtol(argv[0], &endptr, 10); 48327952Sdim if (*endptr != '\0') { 49327952Sdim return usage(); 50327952Sdim } 51327952Sdim argv++; 52327952Sdim argc--; 53327952Sdim } else { 54327952Sdim return usage(); 55327952Sdim } 56327952Sdim } 57327952Sdim 58327952Sdim if (argc == 0 || !strcmp(argv[0], "--help")) { 59327952Sdim return usage(); 60341825Sdim } 61218887Sdim 62218887Sdim const char* host = argv[0]; 63327952Sdim 64327952Sdim int s = socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP); 65218887Sdim if (s < 0) { 66218887Sdim fprintf(stderr, "Could not acquire ICMP socket: %d\n", errno); 67327952Sdim return -1; 68218887Sdim } 69218887Sdim 70218887Sdim struct addrinfo hints; 71218887Sdim memset(&hints, 0, sizeof(hints)); 72341825Sdim hints.ai_family = AF_UNSPEC; 73218887Sdim hints.ai_socktype = SOCK_DGRAM; 74218887Sdim hints.ai_flags = 0; 75327952Sdim hints.ai_protocol = IPPROTO_ICMP; 76218887Sdim struct addrinfo* info; 77218887Sdim if (getaddrinfo(host, NULL, &hints, &info)) { 78218887Sdim fprintf(stderr, "ping: unknown host %s\n", host); 79341825Sdim return -1; 80218887Sdim } 81218887Sdim 82218887Sdim struct sockaddr* saddr = info->ai_addr; 83218887Sdim char buf[256]; 84218887Sdim if (saddr->sa_family == AF_INET) { 85218887Sdim struct sockaddr_in* iaddr = reinterpret_cast<struct sockaddr_in*>(saddr); 86327952Sdim inet_ntop(saddr->sa_family, &iaddr->sin_addr, buf, sizeof(buf)); 87218887Sdim } else { 88218887Sdim struct sockaddr_in6* iaddr = reinterpret_cast<struct sockaddr_in6*>(saddr); 89218887Sdim inet_ntop(saddr->sa_family, &iaddr->sin6_addr, buf, sizeof(buf)); 90218887Sdim } 91341825Sdim 92218887Sdim printf("PING %s (%s)\n", host, buf); 93218887Sdim 94218887Sdim uint16_t sequence = 1; 95327952Sdim packet_t packet; 96341825Sdim ssize_t r; 97218887Sdim const zx_ticks_t ticks_per_usec = zx_ticks_per_second() / 1000000; 98218887Sdim 99341825Sdim while (count-- > 0) { 100341825Sdim memset(&packet, 0, sizeof(packet)); 101218887Sdim packet.hdr.type = ICMP_ECHO; 102327952Sdim packet.hdr.code = 0; 103327952Sdim packet.hdr.un.echo.id = 0; 104218887Sdim packet.hdr.un.echo.sequence = htons(sequence++); 105327952Sdim constexpr char kMessage[] = "This is an echo message!"; 106327952Sdim strcpy(reinterpret_cast<char *>(packet.payload), kMessage); 107341825Sdim // Netstack will overwrite the checksum 108218887Sdim zx_ticks_t before = zx_ticks_get(); 109218887Sdim r = sendto(s, &packet, sizeof(packet.hdr) + strlen(kMessage) + 1, 0, 110218887Sdim saddr, sizeof(*saddr)); 111341825Sdim if (r < 0) { 112234353Sdim fprintf(stderr, "ping: Could not send packet\n"); 113234353Sdim return -1; 114234353Sdim } 115234353Sdim 116234353Sdim struct pollfd fd; 117234353Sdim fd.fd = s; 118234353Sdim fd.events = POLLIN; 119234353Sdim switch (poll(&fd, 1, 1000)) { 120234353Sdim case 1: 121234353Sdim if (fd.revents & POLLIN) { 122234353Sdim r = recvfrom(s, &packet, sizeof(packet), 0, NULL, NULL); 123234353Sdim break; 124234353Sdim } else { 125234353Sdim fprintf(stderr, "ping: Spurious wakeup from poll\n"); 126234353Sdim r = -1; 127218887Sdim break; 128218887Sdim } 129218887Sdim case 0: 130218887Sdim fprintf(stderr, "ping: Timed out after one second\n"); 131341825Sdim __FALLTHROUGH; 132218887Sdim default: 133341825Sdim r = -1; 134239462Sdim } 135239462Sdim 136239462Sdim if (r < 0) { 137218887Sdim fprintf(stderr, "ping: Could not read result of ping\n"); 138239462Sdim return -1; 139218887Sdim } 140218887Sdim zx_ticks_t after = zx_ticks_get(); 141341825Sdim int seq = ntohs(packet.hdr.un.echo.sequence); 142218887Sdim uint64_t usec = (after - before) / ticks_per_usec; 143218887Sdim printf("%" PRIu64 " bytes: icmp_seq=%d time=%" PRIu64 " us\n", r, seq, usec); 144239462Sdim if (count > 0) { 145218887Sdim sleep(1); 146218887Sdim } 147218887Sdim } 148218887Sdim freeaddrinfo(info); 149218887Sdim close(s); 150341825Sdim return 0; 151218887Sdim} 152218887Sdim