Changes in uspace/app/ping/ping.c [9d58539:9749e47] in mainline


Ignore:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • uspace/app/ping/ping.c

    r9d58539 r9749e47  
    11/*
    2  * Copyright (c) 2009 Lukas Mejdrech
     2 * Copyright (c) 2013 Jiri Svoboda
    33 * All rights reserved.
    44 *
     
    3030 * @{
    3131 */
    32 
    33 /** @file
    34  * Packet Internet Network Grouper.
     32/** @file ICMP echo utility.
    3533 */
    3634
    3735#include <async.h>
     36#include <stdbool.h>
     37#include <errno.h>
     38#include <fibril_synch.h>
     39#include <inet/dnsr.h>
     40#include <inet/addr.h>
     41#include <inet/inetping.h>
     42#include <io/console.h>
     43#include <getopt.h>
    3844#include <stdio.h>
     45#include <stdlib.h>
    3946#include <str.h>
    40 #include <task.h>
    41 #include <time.h>
    42 #include <ipc/services.h>
    4347#include <str_error.h>
    44 #include <errno.h>
    45 #include <arg_parse.h>
    46 
    47 #include <net/icmp_api.h>
    48 #include <net/in.h>
    49 #include <net/in6.h>
    50 #include <net/inet.h>
    51 #include <net/socket_parse.h>
    52 #include <net/ip_codes.h>
    53 
    54 #include "print_error.h"
    55 
    56 #define NAME  "ping"
    57 
    58 #define CL_OK           0
    59 #define CL_USAGE        -1
    60 #define CL_MISSING      -2
    61 #define CL_INVALID      -3
    62 #define CL_UNSUPPORTED  -4
    63 #define CL_ERROR        -5
    64 
    65 /** Ping configuration
    66  *
    67  */
    68 typedef struct {
    69         bool verbose;               /**< Verbose printouts. */
    70         size_t size;                /**< Outgoing packet size. */
    71         unsigned int count;         /**< Number of packets to send. */
    72         suseconds_t timeout;        /**< Reply wait timeout. */
    73         int af;                     /**< Address family. */
    74         ip_tos_t tos;               /**< Type of service. */
    75         ip_ttl_t ttl;               /**< Time-to-live. */
    76         bool fragments;             /**< Fragmentation. */
    77        
    78         char *dest_addr;            /**< Destination address. */
    79         struct sockaddr_in dest;    /**< IPv4 destionation. */
    80         struct sockaddr_in6 dest6;  /**< IPv6 destionation. */
    81        
    82         struct sockaddr *dest_raw;  /**< Raw destination address. */
    83         socklen_t dest_len;         /**< Raw destination address length. */
    84        
    85         /** Converted address string. */
    86         char dest_str[INET6_ADDRSTRLEN];
    87 } ping_config_t;
    88 
    89 
    90 static void usage(void)
    91 {
    92         printf(
    93             "Usage: ping [-c count] [-s size] [-W timeout] [-f family] [-t ttl]\n" \
    94             "            [-Q tos] [--dont_fragment] destination\n" \
    95             "\n" \
    96             "Options:\n" \
    97             "\t-c count\n" \
    98             "\t--count=count\n" \
    99             "\t\tNumber of outgoing packets (default: 4)\n" \
    100             "\n" \
    101             "\t-s size\n" \
    102             "\t--size=bytes\n" \
    103             "\t\tOutgoing packet size (default: 56 bytes)\n" \
    104             "\n" \
    105             "\t-W timeout\n" \
    106             "\t--timeout=ms\n" \
    107             "\t\tReply wait timeout (default: 3000 ms)\n" \
    108             "\n" \
    109             "\t-f family\n" \
    110             "\t--family=family\n" \
    111             "\t\tDestination address family, AF_INET or AF_INET6 (default: AF_INET)\n" \
    112             "\n" \
    113             "\t-t ttl\n" \
    114             "\t--ttl=ttl\n" \
    115             "\t\tOutgoing packet time-to-live (default: 0)\n" \
    116             "\n" \
    117             "\t-Q tos\n" \
    118             "\t--tos=tos\n" \
    119             "\t\tOutgoing packet type of service (default: 0)\n" \
    120             "\n" \
    121             "\t--dont_fragment\n" \
    122             "\t\tDisable packet fragmentation (default: enabled)\n" \
    123             "\n" \
    124             "\t-v\n" \
    125             "\t--verbose\n" \
    126             "\t\tVerbose operation\n" \
    127             "\n" \
    128             "\t-h\n" \
    129             "\t--help\n" \
    130             "\t\tPrint this usage information\n"
    131         );
    132 }
    133 
    134 static int args_parse(int argc, char *argv[], ping_config_t *config)
    135 {
    136         if (argc < 2)
    137                 return CL_USAGE;
    138        
    139         int i;
    140         int ret;
    141        
    142         for (i = 1; i < argc; i++) {
    143                
    144                 /* Not an option */
    145                 if (argv[i][0] != '-')
    146                         break;
    147                
    148                 /* Options terminator */
    149                 if (str_cmp(argv[i], "--") == 0) {
    150                         i++;
    151                         break;
    152                 }
    153                
    154                 int off;
    155                
    156                 /* Usage */
    157                 if ((off = arg_parse_short_long(argv[i], "-h", "--help")) != -1)
    158                         return CL_USAGE;
    159                
    160                 /* Verbose */
    161                 if ((off = arg_parse_short_long(argv[i], "-v", "--verbose")) != -1) {
    162                         config->verbose = true;
    163                         continue;
    164                 }
    165                
    166                 /* Don't fragment */
    167                 if (str_cmp(argv[i], "--dont_fragment") == 0) {
    168                         config->fragments = false;
    169                         continue;
    170                 }
    171                
    172                 /* Count */
    173                 if ((off = arg_parse_short_long(argv[i], "-c", "--count=")) != -1) {
    174                         int tmp;
    175                         ret = arg_parse_int(argc, argv, &i, &tmp, off);
     48#include <sys/types.h>
     49
     50#define NAME "ping"
     51
     52/** Delay between subsequent ping requests in microseconds */
     53#define PING_DELAY (1000 * 1000)
     54
     55/** Ping request timeout in microseconds */
     56#define PING_TIMEOUT (1000 * 1000)
     57
     58typedef enum {
     59        RECEIVED_NONE,
     60        RECEIVED_SUCCESS,
     61        RECEIVED_INTERRUPT
     62} received_t;
     63
     64static received_t received;
     65static FIBRIL_CONDVAR_INITIALIZE(received_cv);
     66static FIBRIL_MUTEX_INITIALIZE(received_lock);
     67
     68static bool quit = false;
     69static FIBRIL_CONDVAR_INITIALIZE(quit_cv);
     70static FIBRIL_MUTEX_INITIALIZE(quit_lock);
     71
     72static int ping_ev_recv(inetping_sdu_t *);
     73
     74static inetping_ev_ops_t ev_ops = {
     75        .recv = ping_ev_recv
     76};
     77
     78static inet_addr_t src_addr;
     79static inet_addr_t dest_addr;
     80
     81static bool repeat_forever = false;
     82static size_t repeat_count = 1;
     83
     84static const char *short_options = "46rn:";
     85
     86static void print_syntax(void)
     87{
     88        printf("Syntax: %s [<options>] <host>\n", NAME);
     89        printf("\t-n <count> Repeat the specified number of times\n");
     90        printf("\t-r         Repeat forever\n");
     91        printf("\t-4|-6      Use IPv4 or IPv6 destination host address\n");
     92}
     93
     94static void ping_signal_received(received_t value)
     95{
     96        fibril_mutex_lock(&received_lock);
     97        received = value;
     98        fibril_mutex_unlock(&received_lock);
     99        fibril_condvar_broadcast(&received_cv);
     100}
     101
     102static void ping_signal_quit(void)
     103{
     104        fibril_mutex_lock(&quit_lock);
     105        quit = true;
     106        fibril_mutex_unlock(&quit_lock);
     107        fibril_condvar_broadcast(&quit_cv);
     108}
     109
     110static int ping_ev_recv(inetping_sdu_t *sdu)
     111{
     112        char *asrc;
     113        int rc = inet_addr_format(&src_addr, &asrc);
     114        if (rc != EOK)
     115                return ENOMEM;
     116       
     117        char *adest;
     118        rc = inet_addr_format(&dest_addr, &adest);
     119        if (rc != EOK) {
     120                free(asrc);
     121                return ENOMEM;
     122        }
     123       
     124        printf("Received ICMP echo reply: from %s to %s, seq. no %u, "
     125            "payload size %zu\n", asrc, adest, sdu->seq_no, sdu->size);
     126       
     127        ping_signal_received(RECEIVED_SUCCESS);
     128       
     129        free(asrc);
     130        free(adest);
     131        return EOK;
     132}
     133
     134static int ping_send(uint16_t seq_no)
     135{
     136        inetping_sdu_t sdu;
     137       
     138        sdu.src = src_addr;
     139        sdu.dest = dest_addr;
     140        sdu.seq_no = seq_no;
     141        sdu.data = (void *) "foo";
     142        sdu.size = 3;
     143       
     144        int rc = inetping_send(&sdu);
     145        if (rc != EOK)
     146                printf("Failed sending echo request: %s (%d).\n",
     147                    str_error(rc), rc);
     148       
     149        return rc;
     150}
     151
     152static int transmit_fibril(void *arg)
     153{
     154        uint16_t seq_no = 0;
     155       
     156        while ((repeat_count--) || (repeat_forever)) {
     157                fibril_mutex_lock(&received_lock);
     158                received = RECEIVED_NONE;
     159                fibril_mutex_unlock(&received_lock);
     160               
     161                (void) ping_send(++seq_no);
     162               
     163                fibril_mutex_lock(&received_lock);
     164                int rc = fibril_condvar_wait_timeout(&received_cv, &received_lock,
     165                    PING_TIMEOUT);
     166                received_t recv = received;
     167                fibril_mutex_unlock(&received_lock);
     168               
     169                if ((rc == ETIMEOUT) || (recv == RECEIVED_NONE))
     170                        printf("Echo request timed out (seq. no %u)\n", seq_no);
     171               
     172                if (recv == RECEIVED_INTERRUPT)
     173                        break;
     174               
     175                if ((repeat_count > 0) || (repeat_forever)) {
     176                        fibril_mutex_lock(&received_lock);
     177                        rc = fibril_condvar_wait_timeout(&received_cv, &received_lock,
     178                            PING_DELAY);
     179                        recv = received;
     180                        fibril_mutex_unlock(&received_lock);
    176181                       
    177                         if ((ret != EOK) || (tmp < 0))
    178                                 return i;
    179                        
    180                         config->count = (unsigned int) tmp;
    181                         continue;
    182                 }
    183                
    184                 /* Outgoing packet size */
    185                 if ((off = arg_parse_short_long(argv[i], "-s", "--size=")) != -1) {
    186                         int tmp;
    187                         ret = arg_parse_int(argc, argv, &i, &tmp, off);
    188                        
    189                         if ((ret != EOK) || (tmp < 0))
    190                                 return i;
    191                        
    192                         config->size = (size_t) tmp;
    193                         continue;
    194                 }
    195                
    196                 /* Reply wait timeout */
    197                 if ((off = arg_parse_short_long(argv[i], "-W", "--timeout=")) != -1) {
    198                         int tmp;
    199                         ret = arg_parse_int(argc, argv, &i, &tmp, off);
    200                        
    201                         if ((ret != EOK) || (tmp < 0))
    202                                 return i;
    203                        
    204                         config->timeout = (suseconds_t) tmp;
    205                         continue;
    206                 }
    207                
    208                 /* Address family */
    209                 if ((off = arg_parse_short_long(argv[i], "-f", "--family=")) != -1) {
    210                         ret = arg_parse_name_int(argc, argv, &i, &config->af, off,
    211                             socket_parse_address_family);
    212                        
    213                         if (ret != EOK)
    214                                 return i;
    215                        
    216                         continue;
    217                 }
    218                
    219                 /* Type of service */
    220                 if ((off = arg_parse_short_long(argv[i], "-Q", "--tos=")) != -1) {
    221                         int tmp;
    222                         ret = arg_parse_name_int(argc, argv, &i, &tmp, off,
    223                             socket_parse_address_family);
    224                        
    225                         if ((ret != EOK) || (tmp < 0))
    226                                 return i;
    227                        
    228                         config->tos = (ip_tos_t) tmp;
    229                         continue;
    230                 }
    231                
    232                 /* Time to live */
    233                 if ((off = arg_parse_short_long(argv[i], "-t", "--ttl=")) != -1) {
    234                         int tmp;
    235                         ret = arg_parse_name_int(argc, argv, &i, &tmp, off,
    236                             socket_parse_address_family);
    237                        
    238                         if ((ret != EOK) || (tmp < 0))
    239                                 return i;
    240                        
    241                         config->ttl = (ip_ttl_t) tmp;
    242                         continue;
    243                 }
    244         }
    245        
    246         if (i >= argc)
    247                 return CL_MISSING;
    248        
    249         config->dest_addr = argv[i];
    250        
    251         /* Resolve destionation address */
    252         switch (config->af) {
    253         case AF_INET:
    254                 config->dest_raw = (struct sockaddr *) &config->dest;
    255                 config->dest_len = sizeof(config->dest);
    256                 config->dest.sin_family = config->af;
    257                 ret = inet_pton(config->af, config->dest_addr,
    258                     (uint8_t *) &config->dest.sin_addr.s_addr);
    259                 break;
    260         case AF_INET6:
    261                 config->dest_raw = (struct sockaddr *) &config->dest6;
    262                 config->dest_len = sizeof(config->dest6);
    263                 config->dest6.sin6_family = config->af;
    264                 ret = inet_pton(config->af, config->dest_addr,
    265                     (uint8_t *) &config->dest6.sin6_addr.s6_addr);
    266                 break;
    267         default:
    268                 return CL_UNSUPPORTED;
    269         }
    270        
    271         if (ret != EOK)
    272                 return CL_INVALID;
    273        
    274         /* Convert destination address back to string */
    275         switch (config->af) {
    276         case AF_INET:
    277                 ret = inet_ntop(config->af,
    278                     (uint8_t *) &config->dest.sin_addr.s_addr,
    279                     config->dest_str, sizeof(config->dest_str));
    280                 break;
    281         case AF_INET6:
    282                 ret = inet_ntop(config->af,
    283                     (uint8_t *) &config->dest6.sin6_addr.s6_addr,
    284                     config->dest_str, sizeof(config->dest_str));
    285                 break;
    286         default:
    287                 return CL_UNSUPPORTED;
    288         }
    289        
    290         if (ret != EOK)
    291                 return CL_ERROR;
    292        
    293         return CL_OK;
     182                        if (recv == RECEIVED_INTERRUPT)
     183                                break;
     184                }
     185        }
     186       
     187        ping_signal_quit();
     188        return 0;
     189}
     190
     191static int input_fibril(void *arg)
     192{
     193        console_ctrl_t *con = console_init(stdin, stdout);
     194       
     195        while (true) {
     196                cons_event_t ev;
     197                if (!console_get_event(con, &ev))
     198                        break;
     199               
     200                if ((ev.type == CEV_KEY) && (ev.ev.key.type == KEY_PRESS) &&
     201                    ((ev.ev.key.mods & (KM_ALT | KM_SHIFT)) == 0) &&
     202                    ((ev.ev.key.mods & KM_CTRL) != 0)) {
     203                        /* Ctrl+key */
     204                        if (ev.ev.key.key == KC_Q) {
     205                                ping_signal_received(RECEIVED_INTERRUPT);
     206                                break;
     207                        }
     208                }
     209        }
     210       
     211        return 0;
    294212}
    295213
    296214int main(int argc, char *argv[])
    297215{
    298         ping_config_t config;
    299        
    300         /* Default configuration */
    301         config.verbose = false;
    302         config.size = 56;
    303         config.count = 4;
    304         config.timeout = 3000;
    305         config.af = AF_INET;
    306         config.tos = 0;
    307         config.ttl = 0;
    308         config.fragments = true;
    309        
    310         int ret = args_parse(argc, argv, &config);
    311        
    312         switch (ret) {
    313         case CL_OK:
    314                 break;
    315         case CL_USAGE:
    316                 usage();
    317                 return 0;
    318         case CL_MISSING:
    319                 fprintf(stderr, "%s: Destination address missing\n", NAME);
    320                 return 1;
    321         case CL_INVALID:
    322                 fprintf(stderr, "%s: Destination address '%s' invalid or malformed\n",
    323                     NAME, config.dest_addr);
    324                 return 2;
    325         case CL_UNSUPPORTED:
    326                 fprintf(stderr, "%s: Destination address '%s' unsupported\n",
    327                     NAME, config.dest_addr);
    328                 return 3;
    329         case CL_ERROR:
    330                 fprintf(stderr, "%s: Destination address '%s' error\n",
    331                     NAME, config.dest_addr);
    332                 return 4;
    333         default:
    334                 fprintf(stderr, "%s: Unknown or invalid option '%s'\n", NAME,
    335                     argv[ret]);
    336                 return 5;
    337         }
    338        
    339         printf("PING %s (%s) %zu(%zu) bytes of data\n", config.dest_addr,
    340             config.dest_str, config.size, config.size);
    341        
    342         async_sess_t *sess = icmp_connect_module();
    343         if (!sess) {
    344                 fprintf(stderr, "%s: Unable to connect to ICMP service (%s)\n", NAME,
    345                     str_error(errno));
    346                 return errno;
    347         }
    348        
    349         unsigned int seq;
    350         for (seq = 0; seq < config.count; seq++) {
    351                 struct timeval t0;
    352                 ret = gettimeofday(&t0, NULL);
    353                 if (ret != EOK) {
    354                         fprintf(stderr, "%s: gettimeofday failed (%s)\n", NAME,
    355                             str_error(ret));
    356                        
    357                         async_hangup(sess);
    358                         return ret;
    359                 }
    360                
    361                 /* Ping! */
    362                 int result = icmp_echo_msg(sess, config.size, config.timeout,
    363                     config.ttl, config.tos, !config.fragments, config.dest_raw,
    364                     config.dest_len);
    365                
    366                 struct timeval t1;
    367                 ret = gettimeofday(&t1, NULL);
    368                 if (ret != EOK) {
    369                         fprintf(stderr, "%s: gettimeofday failed (%s)\n", NAME,
    370                             str_error(ret));
    371                        
    372                         async_hangup(sess);
    373                         return ret;
    374                 }
    375                
    376                 suseconds_t elapsed = tv_sub(&t1, &t0);
    377                
    378                 switch (result) {
    379                 case ICMP_ECHO:
    380                         printf("%zu bytes from ? (?): icmp_seq=%u ttl=? time=%ld.%04ld\n",
    381                                 config.size, seq, elapsed / 1000, elapsed % 1000);
    382                         break;
    383                 case ETIMEOUT:
    384                         printf("%zu bytes from ? (?): icmp_seq=%u Timed out\n",
    385                                 config.size, seq);
     216        dnsr_hostinfo_t *hinfo = NULL;
     217        char *asrc = NULL;
     218        char *adest = NULL;
     219        char *sdest = NULL;
     220        ip_ver_t ip_ver = ip_any;
     221       
     222        int rc = inetping_init(&ev_ops);
     223        if (rc != EOK) {
     224                printf("Failed connecting to internet ping service: "
     225                    "%s (%d).\n", str_error(rc), rc);
     226                goto error;
     227        }
     228       
     229        int c;
     230        while ((c = getopt(argc, argv, short_options)) != -1) {
     231                switch (c) {
     232                case 'r':
     233                        repeat_forever = true;
     234                        break;
     235                case 'n':
     236                        rc = str_size_t(optarg, NULL, 10, true, &repeat_count);
     237                        if (rc != EOK) {
     238                                printf("Invalid repeat count.\n");
     239                                print_syntax();
     240                                goto error;
     241                        }
     242                        break;
     243                case '4':
     244                        ip_ver = ip_v4;
     245                        break;
     246                case '6':
     247                        ip_ver = ip_v6;
    386248                        break;
    387249                default:
    388                         print_error(stdout, result, NULL, "\n");
    389                 }
    390         }
    391        
    392         async_hangup(sess);
    393        
     250                        printf("Unknown option passed.\n");
     251                        print_syntax();
     252                        goto error;
     253                }
     254        }
     255       
     256        if (optind >= argc) {
     257                printf("IP address or host name not supplied.\n");
     258                print_syntax();
     259                goto error;
     260        }
     261       
     262        /* Parse destination address */
     263        rc = inet_addr_parse(argv[optind], &dest_addr);
     264        if (rc != EOK) {
     265                /* Try interpreting as a host name */
     266                rc = dnsr_name2host(argv[optind], &hinfo, ip_ver);
     267                if (rc != EOK) {
     268                        printf("Error resolving host '%s'.\n", argv[optind]);
     269                        goto error;
     270                }
     271               
     272                dest_addr = hinfo->addr;
     273        }
     274       
     275        /* Determine source address */
     276        rc = inetping_get_srcaddr(&dest_addr, &src_addr);
     277        if (rc != EOK) {
     278                printf("Failed determining source address.\n");
     279                goto error;
     280        }
     281       
     282        rc = inet_addr_format(&src_addr, &asrc);
     283        if (rc != EOK) {
     284                printf("Out of memory.\n");
     285                goto error;
     286        }
     287       
     288        rc = inet_addr_format(&dest_addr, &adest);
     289        if (rc != EOK) {
     290                printf("Out of memory.\n");
     291                goto error;
     292        }
     293       
     294        if (hinfo != NULL) {
     295                rc = asprintf(&sdest, "%s (%s)", hinfo->cname, adest);
     296                if (rc < 0) {
     297                        printf("Out of memory.\n");
     298                        goto error;
     299                }
     300        } else {
     301                sdest = adest;
     302                adest = NULL;
     303        }
     304       
     305        printf("Sending ICMP echo request from %s to %s (Ctrl+Q to quit)\n",
     306            asrc, sdest);
     307       
     308        fid_t fid = fibril_create(transmit_fibril, NULL);
     309        if (fid == 0) {
     310                printf("Failed creating transmit fibril.\n");
     311                goto error;
     312        }
     313       
     314        fibril_add_ready(fid);
     315       
     316        fid = fibril_create(input_fibril, NULL);
     317        if (fid == 0) {
     318                printf("Failed creating input fibril.\n");
     319                goto error;
     320        }
     321       
     322        fibril_add_ready(fid);
     323       
     324        fibril_mutex_lock(&quit_lock);
     325        while (!quit)
     326                fibril_condvar_wait(&quit_cv, &quit_lock);
     327        fibril_mutex_unlock(&quit_lock);
     328       
     329        free(asrc);
     330        free(adest);
     331        free(sdest);
     332        dnsr_hostinfo_destroy(hinfo);
    394333        return 0;
     334       
     335error:
     336        free(asrc);
     337        free(adest);
     338        free(sdest);
     339        dnsr_hostinfo_destroy(hinfo);
     340        return 1;
    395341}
    396342
Note: See TracChangeset for help on using the changeset viewer.