Changes in uspace/app/ping/ping.c [849ed54:2721a75] in mainline


Ignore:
File:
1 edited

Legend:

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

    r849ed54 r2721a75  
    2828
    2929/** @addtogroup ping
    30  *  @{
     30 * @{
    3131 */
    3232
    3333/** @file
    34  *  Ping application.
     34 * Packet Internet Network Grouper.
    3535 */
    3636
     
    4141#include <ipc/ipc.h>
    4242#include <ipc/services.h>
     43#include <str_error.h>
     44#include <arg_parse.h>
    4345
    4446#include <icmp_api.h>
     
    4850#include <ip_codes.h>
    4951#include <socket_errno.h>
    50 #include <net_err.h>
    51 
    52 #include "parse.h"
     52#include <socket_parse.h>
     53
    5354#include "print_error.h"
    5455
    55 /** Echo module name.
     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 *
    5667 */
    57 #define NAME    "Ping"
    58 
    59 /** Module entry point.
    60  *  Reads command line parameters and pings.
    61  *  @param[in] argc The number of command line parameters.
    62  *  @param[in] argv The command line parameters.
    63  *  @returns EOK on success.
    64  */
    65 int main(int argc, char * argv[]);
    66 
    67 /** Prints the application help.
    68  */
    69 void ping_print_help(void);
    70 
    71 int main(int argc, char * argv[]){
    72         ERROR_DECLARE;
    73 
    74         size_t size                     = 38;
    75         int verbose                     = 0;
    76         int dont_fragment       = 0;
    77         ip_ttl_t ttl            = 0;
    78         ip_tos_t tos            = 0;
    79         int count                       = 3;
    80         suseconds_t timeout     = 3000;
    81         int family                      = AF_INET;
    82 
    83         socklen_t max_length                            = sizeof(struct sockaddr_in6);
    84         uint8_t address_data[max_length];
    85         struct sockaddr * address                       = (struct sockaddr *) address_data;
    86         struct sockaddr_in * address_in         = (struct sockaddr_in *) address;
    87         struct sockaddr_in6 * address_in6       = (struct sockaddr_in6 *) address;
    88         socklen_t addrlen;
    89         char address_string[INET6_ADDRSTRLEN];
    90         uint8_t * address_start;
    91         int icmp_phone;
    92         struct timeval time_before;
    93         struct timeval time_after;
    94         int result;
    95         int value;
    96         int index;
    97 
    98         // print the program label
    99         printf("Task %d - ", task_get_id());
    100         printf("%s\n", NAME);
    101 
    102         // parse the command line arguments
    103         // stop before the last argument if it does not start with the minus sign ('-')
    104         for(index = 1; (index < argc - 1) || ((index == argc - 1) && (argv[index][0] == '-')); ++ index){
    105                 // options should start with the minus sign ('-')
    106                 if(argv[index][0] == '-'){
    107                         switch(argv[index][1]){
    108                                 // short options with only one letter
    109                                 case 'c':
    110                                         ERROR_PROPAGATE(parse_parameter_int(argc, argv, &index, &count, "count", 0));
    111                                         break;
    112                                 case 'f':
    113                                         ERROR_PROPAGATE(parse_parameter_name_int(argc, argv, &index, &family, "address family", 0, parse_address_family));
    114                                         break;
    115                                 case 'h':
    116                                         ping_print_help();
    117                                         return EOK;
    118                                         break;
    119                                 case 's':
    120                                         ERROR_PROPAGATE(parse_parameter_int(argc, argv, &index, &value, "packet size", 0));
    121                                         size = (value >= 0) ? (size_t) value : 0;
    122                                         break;
    123                                 case 't':
    124                                         ERROR_PROPAGATE(parse_parameter_int(argc, argv, &index, &value, "timeout", 0));
    125                                         timeout = (value >= 0) ? (suseconds_t) value : 0;
    126                                         break;
    127                                 case 'v':
    128                                         verbose = 1;
    129                                         break;
    130                                 // long options with the double minus sign ('-')
    131                                 case '-':
    132                                         if(str_lcmp(argv[index] + 2, "count=", 6) == 0){
    133                                                 ERROR_PROPAGATE(parse_parameter_int(argc, argv, &index, &count, "received count", 8));
    134                                         }else if(str_lcmp(argv[index] + 2, "dont_fragment", 13) == 0){
    135                                                 dont_fragment = 1;
    136                                         }else if(str_lcmp(argv[index] + 2, "family=", 7) == 0){
    137                                                 ERROR_PROPAGATE(parse_parameter_name_int(argc, argv, &index, &family, "address family", 9, parse_address_family));
    138                                         }else if(str_lcmp(argv[index] + 2, "help", 5) == 0){
    139                                                 ping_print_help();
    140                                                 return EOK;
    141                                         }else if(str_lcmp(argv[index] + 2, "size=", 5) == 0){
    142                                                 ERROR_PROPAGATE(parse_parameter_int(argc, argv, &index, &value, "packet size", 7));
    143                                                 size = (value >= 0) ? (size_t) value : 0;
    144                                         }else if(str_lcmp(argv[index] + 2, "timeout=", 8) == 0){
    145                                                 ERROR_PROPAGATE(parse_parameter_int(argc, argv, &index, &value, "timeout", 7));
    146                                                 timeout = (value >= 0) ? (suseconds_t) value : 0;
    147                                         }else if(str_lcmp(argv[index] + 2, "tos=", 4) == 0){
    148                                                 ERROR_PROPAGATE(parse_parameter_int(argc, argv, &index, &value, "type of service", 7));
    149                                                 tos = (value >= 0) ? (ip_tos_t) value : 0;
    150                                         }else if(str_lcmp(argv[index] + 2, "ttl=", 4) == 0){
    151                                                 ERROR_PROPAGATE(parse_parameter_int(argc, argv, &index, &value, "time to live", 7));
    152                                                 ttl = (value >= 0) ? (ip_ttl_t) value : 0;
    153                                         }else if(str_lcmp(argv[index] + 2, "verbose", 8) == 0){
    154                                                 verbose = 1;
    155                                         }else{
    156                                                 print_unrecognized(index, argv[index] + 2);
    157                                                 ping_print_help();
    158                                                 return EINVAL;
    159                                         }
    160                                         break;
    161                                 default:
    162                                         print_unrecognized(index, argv[index] + 1);
    163                                         ping_print_help();
    164                                         return EINVAL;
    165                         }
    166                 }else{
    167                         print_unrecognized(index, argv[index]);
    168                         ping_print_help();
    169                         return EINVAL;
    170                 }
    171         }
    172 
    173         // if not before the last argument containing the address
    174         if(index >= argc){
    175                 printf("Command line error: missing address\n");
    176                 ping_print_help();
    177                 return EINVAL;
    178         }
    179 
    180         // prepare the address buffer
    181         bzero(address_data, max_length);
    182         switch(family){
    183                 case AF_INET:
    184                         address_in->sin_family = AF_INET;
    185                         address_start = (uint8_t *) &address_in->sin_addr.s_addr;
    186                         addrlen = sizeof(struct sockaddr_in);
     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)
     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
     134static int arg_short_long(const char *arg, const char *ashort,
     135    const char *along)
     136{
     137        if (str_cmp(arg, ashort) == 0)
     138                return 0;
     139       
     140        if (str_lcmp(arg, along, str_length(along)) == 0)
     141                return str_length(along);
     142       
     143        return -1;
     144}
     145
     146static int args_parse(int argc, char *argv[], ping_config_t *config)
     147{
     148        if (argc < 2)
     149                return CL_USAGE;
     150       
     151        int i;
     152        int ret;
     153       
     154        for (i = 1; i < argc; i++) {
     155               
     156                /* Not an option */
     157                if (argv[i][0] != '-')
    187158                        break;
    188                 case AF_INET6:
    189                         address_in6->sin6_family = AF_INET6;
    190                         address_start = (uint8_t *) &address_in6->sin6_addr.s6_addr;
    191                         addrlen = sizeof(struct sockaddr_in6);
     159               
     160                /* Options terminator */
     161                if (str_cmp(argv[i], "--") == 0) {
     162                        i++;
     163                        break;
     164                }
     165               
     166                int off;
     167                int tmp;
     168               
     169                /* Usage */
     170                if ((off = arg_short_long(argv[i], "-h", "--help")) != -1)
     171                        return CL_USAGE;
     172               
     173                /* Verbose */
     174                if ((off = arg_short_long(argv[i], "-v", "--verbose")) != -1) {
     175                        config->verbose = true;
     176                        continue;
     177                }
     178               
     179                /* Don't fragment */
     180                if (str_cmp(argv[i], "--dont_fragment") == 0) {
     181                        config->fragments = false;
     182                        continue;
     183                }
     184               
     185                /* Count */
     186                if ((off = arg_short_long(argv[i], "-c", "--count=")) != -1) {
     187                        ret = arg_parse_int(argc, argv, &i, &tmp, off);
     188                       
     189                        if ((ret != EOK) || (tmp < 0))
     190                                return i;
     191                       
     192                        config->count = (unsigned int) tmp;
     193                        continue;
     194                }
     195               
     196                /* Outgoing packet size */
     197                if ((off = arg_short_long(argv[i], "-s", "--size=")) != -1) {
     198                        ret = arg_parse_int(argc, argv, &i, &tmp, off);
     199                       
     200                        if ((ret != EOK) || (tmp < 0))
     201                                return i;
     202                       
     203                        config->size = (size_t) tmp;
     204                        continue;
     205                }
     206               
     207                /* Reply wait timeout */
     208                if ((off = arg_short_long(argv[i], "-W", "--timeout=")) != -1) {
     209                        ret = arg_parse_int(argc, argv, &i, &tmp, off);
     210                       
     211                        if ((ret != EOK) || (tmp < 0))
     212                                return i;
     213                       
     214                        config->timeout = (suseconds_t) tmp;
     215                        continue;
     216                }
     217               
     218                /* Address family */
     219                if ((off = arg_short_long(argv[i], "-f", "--family=")) != -1) {
     220                        ret = arg_parse_name_int(argc, argv, &i, &config->af, off,
     221                            socket_parse_address_family);
     222                       
     223                        if (ret != EOK)
     224                                return i;
     225                       
     226                        continue;
     227                }
     228               
     229                /* Type of service */
     230                if ((off = arg_short_long(argv[i], "-Q", "--tos=")) != -1) {
     231                        ret = arg_parse_name_int(argc, argv, &i, &tmp, off,
     232                            socket_parse_address_family);
     233                       
     234                        if ((ret != EOK) || (tmp < 0))
     235                                return i;
     236                       
     237                        config->tos = (ip_tos_t) tmp;
     238                        continue;
     239                }
     240               
     241                /* Time to live */
     242                if ((off = arg_short_long(argv[i], "-t", "--ttl=")) != -1) {
     243                        ret = arg_parse_name_int(argc, argv, &i, &tmp, off,
     244                            socket_parse_address_family);
     245                       
     246                        if ((ret != EOK) || (tmp < 0))
     247                                return i;
     248                       
     249                        config->ttl = (ip_ttl_t) tmp;
     250                        continue;
     251                }
     252        }
     253       
     254        if (i >= argc)
     255                return CL_MISSING;
     256       
     257        config->dest_addr = argv[i];
     258       
     259        /* Resolve destionation address */
     260        switch (config->af) {
     261        case AF_INET:
     262                config->dest_raw = (struct sockaddr *) &config->dest;
     263                config->dest_len = sizeof(config->dest);
     264                config->dest.sin_family = config->af;
     265                ret = inet_pton(config->af, config->dest_addr,
     266                    (uint8_t *) &config->dest.sin_addr.s_addr);
     267                break;
     268        case AF_INET6:
     269                config->dest_raw = (struct sockaddr *) &config->dest6;
     270                config->dest_len = sizeof(config->dest6);
     271                config->dest6.sin6_family = config->af;
     272                ret = inet_pton(config->af, config->dest_addr,
     273                    (uint8_t *) &config->dest6.sin6_addr.s6_addr);
     274                break;
     275        default:
     276                return CL_UNSUPPORTED;
     277        }
     278       
     279        if (ret != EOK)
     280                return CL_INVALID;
     281       
     282        /* Convert destination address back to string */
     283        switch (config->af) {
     284        case AF_INET:
     285                ret = inet_ntop(config->af,
     286                    (uint8_t *) &config->dest.sin_addr.s_addr,
     287                    config->dest_str, sizeof(config->dest_str));
     288                break;
     289        case AF_INET6:
     290                ret = inet_ntop(config->af,
     291                    (uint8_t *) &config->dest6.sin6_addr.s6_addr,
     292                    config->dest_str, sizeof(config->dest_str));
     293                break;
     294        default:
     295                return CL_UNSUPPORTED;
     296        }
     297       
     298        if (ret != EOK)
     299                return CL_ERROR;
     300       
     301        return CL_OK;
     302}
     303
     304int main(int argc, char *argv[])
     305{
     306        ping_config_t config;
     307       
     308        /* Default configuration */
     309        config.verbose = false;
     310        config.size = 56;
     311        config.count = 4;
     312        config.timeout = 3000;
     313        config.af = AF_INET;
     314        config.tos = 0;
     315        config.ttl = 0;
     316        config.fragments = true;
     317       
     318        int ret = args_parse(argc, argv, &config);
     319       
     320        switch (ret) {
     321        case CL_OK:
     322                break;
     323        case CL_USAGE:
     324                usage();
     325                return 0;
     326        case CL_MISSING:
     327                fprintf(stderr, "%s: Destination address missing\n", NAME);
     328                return 1;
     329        case CL_INVALID:
     330                fprintf(stderr, "%s: Destination address '%s' invalid or malformed\n",
     331                    NAME, config.dest_addr);
     332                return 2;
     333        case CL_UNSUPPORTED:
     334                fprintf(stderr, "%s: Destination address '%s' unsupported\n",
     335                    NAME, config.dest_addr);
     336                return 3;
     337        case CL_ERROR:
     338                fprintf(stderr, "%s: Destination address '%s' error\n",
     339                    NAME, config.dest_addr);
     340                return 4;
     341        default:
     342                fprintf(stderr, "%s: Unknown or invalid option '%s'\n", NAME,
     343                    argv[ret]);
     344                return 5;
     345        }
     346       
     347        printf("PING %s (%s) %u(%u) bytes of data\n", config.dest_addr,
     348            config.dest_str, config.size, config.size);
     349       
     350        int icmp_phone = icmp_connect_module(SERVICE_ICMP, ICMP_CONNECT_TIMEOUT);
     351        if (icmp_phone < 0) {
     352                fprintf(stderr, "%s: Unable to connect to ICMP service (%s)\n", NAME,
     353                    str_error(icmp_phone));
     354                return icmp_phone;
     355        }
     356       
     357        unsigned int seq;
     358        for (seq = 0; seq < config.count; seq++) {
     359                struct timeval t0;
     360                ret = gettimeofday(&t0, NULL);
     361                if (ret != EOK) {
     362                        fprintf(stderr, "%s: gettimeofday failed (%s)\n", NAME,
     363                            str_error(ret));
     364                       
     365                        ipc_hangup(icmp_phone);
     366                        return ret;
     367                }
     368               
     369                /* Ping! */
     370                int result = icmp_echo_msg(icmp_phone, config.size, config.timeout,
     371                    config.ttl, config.tos, !config.fragments, config.dest_raw,
     372                    config.dest_len);
     373               
     374                struct timeval t1;
     375                ret = gettimeofday(&t1, NULL);
     376                if (ret != EOK) {
     377                        fprintf(stderr, "%s: gettimeofday failed (%s)\n", NAME,
     378                            str_error(ret));
     379                       
     380                        ipc_hangup(icmp_phone);
     381                        return ret;
     382                }
     383               
     384                suseconds_t elapsed = tv_sub(&t1, &t0);
     385               
     386                switch (result) {
     387                case ICMP_ECHO:
     388                        printf("%u bytes from ? (?): icmp_seq=%u ttl=? time=%u.%04u\n",
     389                                config.size, seq, elapsed / 1000, elapsed % 1000);
     390                        break;
     391                case ETIMEOUT:
     392                        printf("%u bytes from ? (?): icmp_seq=%u Timed out\n",
     393                                config.size, seq);
    192394                        break;
    193395                default:
    194                         fprintf(stderr, "Address family is not supported\n");
    195                         return EAFNOSUPPORT;
    196         }
    197 
    198         // parse the last argument which should contain the address
    199         if(ERROR_OCCURRED(inet_pton(family, argv[argc - 1], address_start))){
    200                 fprintf(stderr, "Address parse error %d\n", ERROR_CODE);
    201                 return ERROR_CODE;
    202         }
    203 
    204         // connect to the ICMP module
    205         icmp_phone = icmp_connect_module(SERVICE_ICMP, ICMP_CONNECT_TIMEOUT);
    206         if(icmp_phone < 0){
    207                 fprintf(stderr, "ICMP connect error %d\n", icmp_phone);
    208                 return icmp_phone;
    209         }
    210 
    211         // print the ping header
    212         printf("PING %d bytes of data\n", size);
    213         if(ERROR_OCCURRED(inet_ntop(address->sa_family, address_start, address_string, sizeof(address_string)))){
    214                 fprintf(stderr, "Address error %d\n", ERROR_CODE);
    215         }else{
    216                 printf("Address %s:\n", address_string);
    217         }
    218 
    219         // do count times
    220         while(count > 0){
    221 
    222                 // get the starting time
    223                 if(ERROR_OCCURRED(gettimeofday(&time_before, NULL))){
    224                         fprintf(stderr, "Get time of day error %d\n", ERROR_CODE);
    225                         // release the ICMP phone
    226                         ipc_hangup(icmp_phone);
    227                         return ERROR_CODE;
    228                 }
    229 
    230                 // request the ping
    231                 result = icmp_echo_msg(icmp_phone, size, timeout, ttl, tos, dont_fragment, address, addrlen);
    232 
    233                 // get the ending time
    234                 if(ERROR_OCCURRED(gettimeofday(&time_after, NULL))){
    235                         fprintf(stderr, "Get time of day error %d\n", ERROR_CODE);
    236                         // release the ICMP phone
    237                         ipc_hangup(icmp_phone);
    238                         return ERROR_CODE;
    239                 }
    240 
    241                 // print the result
    242                 switch(result){
    243                         case ICMP_ECHO:
    244                                 printf("Ping round trip time %d miliseconds\n", tv_sub(&time_after, &time_before) / 1000);
    245                                 break;
    246                         case ETIMEOUT:
    247                                 printf("Timed out.\n");
    248                                 break;
    249                         default:
    250                                 print_error(stdout, result, NULL, "\n");
    251                 }
    252                 -- count;
    253         }
    254 
    255         if(verbose){
    256                 printf("Exiting\n");
    257         }
    258 
    259         // release the ICMP phone
     396                        print_error(stdout, result, NULL, "\n");
     397                }
     398        }
     399       
    260400        ipc_hangup(icmp_phone);
    261 
    262         return EOK;
    263 }
    264 
    265 void ping_print_help(void){
    266         printf(
    267                 "Network Ping aplication\n" \
    268                 "Usage: ping [options] numeric_address\n" \
    269                 "Where options are:\n" \
    270                 "\n" \
    271                 "-c request_count | --count=request_count\n" \
    272                 "\tThe number of packets the application sends. The default is three (3).\n" \
    273                 "\n" \
    274                 "--dont_fragment\n" \
    275                 "\tDisable packet fragmentation.\n"
    276                 "\n" \
    277                 "-f address_family | --family=address_family\n" \
    278                 "\tThe given address family. Only the AF_INET and AF_INET6 are supported.\n"
    279                 "\n" \
    280                 "-h | --help\n" \
    281                 "\tShow this application help.\n"
    282                 "\n" \
    283                 "-s packet_size | --size=packet_size\n" \
    284                 "\tThe packet data size the application sends. The default is 38 bytes.\n" \
    285                 "\n" \
    286                 "-t timeout | --timeout=timeout\n" \
    287                 "\tThe number of miliseconds the application waits for a reply. The default is three thousands (3 000).\n" \
    288                 "\n" \
    289                 "--tos=tos\n" \
    290                 "\tThe type of service to be used.\n" \
    291                 "\n" \
    292                 "--ttl=ttl\n" \
    293                 "\tThe time to live to be used.\n" \
    294                 "\n" \
    295                 "-v | --verbose\n" \
    296                 "\tShow all output messages.\n"
    297         );
     401       
     402        return 0;
    298403}
    299404
Note: See TracChangeset for help on using the changeset viewer.