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


Ignore:
File:
1 edited

Legend:

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

    r2721a75 r849ed54  
    2828
    2929/** @addtogroup ping
    30  * @{
     30 *  @{
    3131 */
    3232
    3333/** @file
    34  * Packet Internet Network Grouper.
     34 *  Ping application.
    3535 */
    3636
     
    4141#include <ipc/ipc.h>
    4242#include <ipc/services.h>
    43 #include <str_error.h>
    44 #include <arg_parse.h>
    4543
    4644#include <icmp_api.h>
     
    5048#include <ip_codes.h>
    5149#include <socket_errno.h>
    52 #include <socket_parse.h>
    53 
     50#include <net_err.h>
     51
     52#include "parse.h"
    5453#include "print_error.h"
    5554
    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 {
     55/** Echo module name.
     56 */
     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 */
     65int main(int argc, char * argv[]);
     66
     67/** Prints the application help.
     68 */
     69void ping_print_help(void);
     70
     71int 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);
     187                        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);
     192                        break;
     193                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
     260        ipc_hangup(icmp_phone);
     261
     262        return EOK;
     263}
     264
     265void ping_print_help(void){
    92266        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"
     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"
    131297        );
    132298}
    133299
    134 static 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 
    146 static 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] != '-')
    158                         break;
    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 
    304 int 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);
    394                         break;
    395                 default:
    396                         print_error(stdout, result, NULL, "\n");
    397                 }
    398         }
    399        
    400         ipc_hangup(icmp_phone);
    401        
    402         return 0;
    403 }
    404 
    405300/** @}
    406301 */
Note: See TracChangeset for help on using the changeset viewer.