Changes in uspace/app/ping/ping.c [9d58539:9749e47] in mainline
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
uspace/app/ping/ping.c
r9d58539 r9749e47 1 1 /* 2 * Copyright (c) 20 09 Lukas Mejdrech2 * Copyright (c) 2013 Jiri Svoboda 3 3 * All rights reserved. 4 4 * … … 30 30 * @{ 31 31 */ 32 33 /** @file 34 * Packet Internet Network Grouper. 32 /** @file ICMP echo utility. 35 33 */ 36 34 37 35 #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> 38 44 #include <stdio.h> 45 #include <stdlib.h> 39 46 #include <str.h> 40 #include <task.h>41 #include <time.h>42 #include <ipc/services.h>43 47 #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 58 typedef enum { 59 RECEIVED_NONE, 60 RECEIVED_SUCCESS, 61 RECEIVED_INTERRUPT 62 } received_t; 63 64 static received_t received; 65 static FIBRIL_CONDVAR_INITIALIZE(received_cv); 66 static FIBRIL_MUTEX_INITIALIZE(received_lock); 67 68 static bool quit = false; 69 static FIBRIL_CONDVAR_INITIALIZE(quit_cv); 70 static FIBRIL_MUTEX_INITIALIZE(quit_lock); 71 72 static int ping_ev_recv(inetping_sdu_t *); 73 74 static inetping_ev_ops_t ev_ops = { 75 .recv = ping_ev_recv 76 }; 77 78 static inet_addr_t src_addr; 79 static inet_addr_t dest_addr; 80 81 static bool repeat_forever = false; 82 static size_t repeat_count = 1; 83 84 static const char *short_options = "46rn:"; 85 86 static 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 94 static 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 102 static 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 110 static 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 134 static 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 152 static 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); 176 181 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 191 static 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; 294 212 } 295 213 296 214 int main(int argc, char *argv[]) 297 215 { 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; 386 248 break; 387 249 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); 394 333 return 0; 334 335 error: 336 free(asrc); 337 free(adest); 338 free(sdest); 339 dnsr_hostinfo_destroy(hinfo); 340 return 1; 395 341 } 396 342
Note:
See TracChangeset
for help on using the changeset viewer.