Changes in uspace/app/ping/ping.c [07b7c48:9d58539] in mainline


Ignore:
File:
1 edited

Legend:

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

    r07b7c48 r9d58539  
    11/*
    2  * Copyright (c) 2012 Jiri Svoboda
     2 * Copyright (c) 2009 Lukas Mejdrech
    33 * All rights reserved.
    44 *
     
    3030 * @{
    3131 */
    32 /** @file ICMP echo utility.
     32
     33/** @file
     34 * Packet Internet Network Grouper.
    3335 */
    3436
    3537#include <async.h>
    36 #include <stdbool.h>
     38#include <stdio.h>
     39#include <str.h>
     40#include <task.h>
     41#include <time.h>
     42#include <ipc/services.h>
     43#include <str_error.h>
    3744#include <errno.h>
    38 #include <fibril_synch.h>
    39 #include <inet/inetping.h>
    40 #include <io/console.h>
    41 #include <stdio.h>
    42 #include <stdlib.h>
    43 #include <sys/types.h>
    44 
    45 #define NAME "ping"
    46 
    47 /** Delay between subsequent ping requests in microseconds */
    48 #define PING_DELAY (1000 * 1000)
    49 
    50 /** Ping request timeout in microseconds */
    51 #define PING_TIMEOUT (1000 * 1000)
    52 
    53 static int ping_ev_recv(inetping_sdu_t *);
    54 
    55 static bool done = false;
    56 static FIBRIL_CONDVAR_INITIALIZE(done_cv);
    57 static FIBRIL_MUTEX_INITIALIZE(done_lock);
    58 
    59 static inetping_ev_ops_t ev_ops = {
    60         .recv = ping_ev_recv
    61 };
    62 
    63 static inet_addr_t src_addr;
    64 static inet_addr_t dest_addr;
    65 
    66 static bool ping_repeat = false;
    67 
    68 static void print_syntax(void)
     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 */
     68typedef 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
     90static void usage(void)
    6991{
    70         printf("syntax: " NAME " [-r] <addr>\n");
     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        );
    71132}
    72133
    73 static int addr_parse(const char *text, inet_addr_t *addr)
     134static int args_parse(int argc, char *argv[], ping_config_t *config)
    74135{
    75         unsigned long a[4];
    76         char *cp = (char *)text;
     136        if (argc < 2)
     137                return CL_USAGE;
     138       
    77139        int i;
    78 
    79         for (i = 0; i < 3; i++) {
    80                 a[i] = strtoul(cp, &cp, 10);
    81                 if (*cp != '.')
    82                         return EINVAL;
    83                 ++cp;
    84         }
    85 
    86         a[3] = strtoul(cp, &cp, 10);
    87         if (*cp != '\0')
    88                 return EINVAL;
    89 
    90         addr->ipv4 = 0;
    91         for (i = 0; i < 4; i++) {
    92                 if (a[i] > 255)
    93                         return EINVAL;
    94                 addr->ipv4 = (addr->ipv4 << 8) | a[i];
    95         }
    96 
    97         return EOK;
     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);
     176                       
     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;
    98294}
    99295
    100 static int addr_format(inet_addr_t *addr, char **bufp)
     296int main(int argc, char *argv[])
    101297{
    102         int rc;
    103 
    104         rc = asprintf(bufp, "%d.%d.%d.%d", addr->ipv4 >> 24,
    105             (addr->ipv4 >> 16) & 0xff, (addr->ipv4 >> 8) & 0xff,
    106             addr->ipv4 & 0xff);
    107 
    108         if (rc < 0)
    109                 return ENOMEM;
    110 
    111         return EOK;
    112 }
    113 
    114 static void ping_signal_done(void)
    115 {
    116         fibril_mutex_lock(&done_lock);
    117         done = true;
    118         fibril_mutex_unlock(&done_lock);
    119         fibril_condvar_broadcast(&done_cv);
    120 }
    121 
    122 static int ping_ev_recv(inetping_sdu_t *sdu)
    123 {
    124         char *asrc, *adest;
    125         int rc;
    126 
    127         rc = addr_format(&sdu->src, &asrc);
    128         if (rc != EOK)
    129                 return ENOMEM;
    130 
    131         rc = addr_format(&sdu->dest, &adest);
    132         if (rc != EOK) {
    133                 free(asrc);
    134                 return ENOMEM;
    135         }
    136         printf("Received ICMP echo reply: from %s to %s, seq. no %u, "
    137             "payload size %zu\n", asrc, adest, sdu->seq_no, sdu->size);
    138 
    139         if (!ping_repeat) {
    140                 ping_signal_done();
    141         }
    142 
    143         free(asrc);
    144         free(adest);
    145         return EOK;
    146 }
    147 
    148 static int ping_send(uint16_t seq_no)
    149 {
    150         inetping_sdu_t sdu;
    151         int rc;
    152 
    153         sdu.src = src_addr;
    154         sdu.dest = dest_addr;
    155         sdu.seq_no = seq_no;
    156         sdu.data = (void *) "foo";
    157         sdu.size = 3;
    158 
    159         rc = inetping_send(&sdu);
    160         if (rc != EOK) {
    161                 printf(NAME ": Failed sending echo request (%d).\n", rc);
    162                 return rc;
    163         }
    164 
    165         return EOK;
    166 }
    167 
    168 static int transmit_fibril(void *arg)
    169 {
    170         uint16_t seq_no = 0;
    171 
    172         while (true) {
    173                 fibril_mutex_lock(&done_lock);
    174                 if (done) {
    175                         fibril_mutex_unlock(&done_lock);
    176                         return 0;
    177                 }
    178                 fibril_mutex_unlock(&done_lock);
    179 
    180                 (void) ping_send(++seq_no);
    181                 async_usleep(PING_DELAY);
    182         }
    183 
     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);
     386                        break;
     387                default:
     388                        print_error(stdout, result, NULL, "\n");
     389                }
     390        }
     391       
     392        async_hangup(sess);
     393       
    184394        return 0;
    185395}
    186396
    187 static int input_fibril(void *arg)
    188 {
    189         console_ctrl_t *con;
    190         cons_event_t ev;
    191 
    192         con = console_init(stdin, stdout);
    193         printf("[Press Ctrl-Q to quit]\n");
    194 
    195         while (true) {
    196                 if (!console_get_event(con, &ev))
    197                         break;
    198 
    199                 if (ev.type == CEV_KEY && ev.ev.key.type == KEY_PRESS &&
    200                     (ev.ev.key.mods & (KM_ALT | KM_SHIFT)) ==
    201                     0 && (ev.ev.key.mods & KM_CTRL) != 0) {
    202                         /* Ctrl+key */
    203                         if (ev.ev.key.key == KC_Q) {
    204                                 ping_signal_done();
    205                                 return 0;
    206                         }
    207                 }
    208         }
    209 
    210         return 0;
    211 }
    212 
    213 int main(int argc, char *argv[])
    214 {
    215         int rc;
    216         int argi;
    217 
    218         rc = inetping_init(&ev_ops);
    219         if (rc != EOK) {
    220                 printf(NAME ": Failed connecting to internet ping service "
    221                     "(%d).\n", rc);
    222                 return 1;
    223         }
    224 
    225         argi = 1;
    226         if (argi < argc && str_cmp(argv[argi], "-r") == 0) {
    227                 ping_repeat = true;
    228                 ++argi;
    229         } else {
    230                 ping_repeat = false;
    231         }
    232 
    233         if (argc - argi != 1) {
    234                 print_syntax();
    235                 return 1;
    236         }
    237 
    238         /* Parse destination address */
    239         rc = addr_parse(argv[argi], &dest_addr);
    240         if (rc != EOK) {
    241                 printf(NAME ": Invalid address format.\n");
    242                 print_syntax();
    243                 return 1;
    244         }
    245 
    246         /* Determine source address */
    247         rc = inetping_get_srcaddr(&dest_addr, &src_addr);
    248         if (rc != EOK) {
    249                 printf(NAME ": Failed determining source address.\n");
    250                 return 1;
    251         }
    252 
    253         fid_t fid;
    254 
    255         if (ping_repeat) {
    256                 fid = fibril_create(transmit_fibril, NULL);
    257                 if (fid == 0) {
    258                         printf(NAME ": Failed creating transmit fibril.\n");
    259                         return 1;
    260                 }
    261 
    262                 fibril_add_ready(fid);
    263 
    264                 fid = fibril_create(input_fibril, NULL);
    265                 if (fid == 0) {
    266                         printf(NAME ": Failed creating input fibril.\n");
    267                         return 1;
    268                 }
    269 
    270                 fibril_add_ready(fid);
    271         } else {
    272                 ping_send(1);
    273         }
    274 
    275         fibril_mutex_lock(&done_lock);
    276         rc = EOK;
    277         while (!done && rc != ETIMEOUT) {
    278                 rc = fibril_condvar_wait_timeout(&done_cv, &done_lock,
    279                         ping_repeat ? 0 : PING_TIMEOUT);
    280         }
    281         fibril_mutex_unlock(&done_lock);
    282 
    283         if (rc == ETIMEOUT) {
    284                 printf(NAME ": Echo request timed out.\n");
    285                 return 1;
    286         }
    287 
    288         return 0;
    289 }
    290 
    291397/** @}
    292398 */
Note: See TracChangeset for help on using the changeset viewer.