/* * Copyright (c) 2009 Lukas Mejdrech * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** @addtogroup nettest * @{ */ /** @file * Networking test 2 application - transfer. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "nettest.h" #include "print_error.h" /** Echo module name. */ #define NAME "Nettest2" /** Packet data pattern. */ #define NETTEST2_TEXT "Networking test 2 - transfer" /** Module entry point. * Starts testing. * @param[in] argc The number of command line parameters. * @param[in] argv The command line parameters. * @returns EOK on success. */ int main(int argc, char * argv[]); /** Prints the application help. */ void nettest2_print_help(void); /** Refreshes the data. * Fills the data block with the NETTEST1_TEXT pattern. * @param[out] data The data block. * @param[in] size The data block size in bytes. */ void nettest2_refresh_data(char * data, size_t size); int main(int argc, char * argv[]){ ERROR_DECLARE; size_t size = 28; int verbose = 0; sock_type_t type = SOCK_DGRAM; int sockets = 10; int messages = 10; int family = PF_INET; uint16_t port = 7; socklen_t max_length = sizeof(struct sockaddr_in6); uint8_t address_data[max_length]; struct sockaddr * address = (struct sockaddr *) address_data; struct sockaddr_in * address_in = (struct sockaddr_in *) address; struct sockaddr_in6 * address_in6 = (struct sockaddr_in6 *) address; socklen_t addrlen; // char address_string[INET6_ADDRSTRLEN]; uint8_t * address_start; int * socket_ids; char * data; int value; int index; struct timeval time_before; struct timeval time_after; // parse the command line arguments // stop before the last argument if it does not start with the minus sign ('-') for(index = 1; (index < argc - 1) || ((index == argc - 1) && (argv[index][0] == '-')); ++ index){ // options should start with the minus sign ('-') if(argv[index][0] == '-'){ switch(argv[index][1]){ // short options with only one letter case 'f': ERROR_PROPAGATE(arg_parse_name_int(argc, argv, &index, &family, 0, socket_parse_protocol_family)); break; case 'h': nettest2_print_help(); return EOK; break; case 'm': ERROR_PROPAGATE(arg_parse_int(argc, argv, &index, &messages, 0)); break; case 'n': ERROR_PROPAGATE(arg_parse_int(argc, argv, &index, &sockets, 0)); break; case 'p': ERROR_PROPAGATE(arg_parse_int(argc, argv, &index, &value, 0)); port = (uint16_t) value; break; case 's': ERROR_PROPAGATE(arg_parse_int(argc, argv, &index, &value, 0)); size = (value >= 0) ? (size_t) value : 0; break; case 't': ERROR_PROPAGATE(arg_parse_name_int(argc, argv, &index, &value, 0, socket_parse_socket_type)); type = (sock_type_t) value; break; case 'v': verbose = 1; break; // long options with the double minus sign ('-') case '-': if(str_lcmp(argv[index] + 2, "family=", 7) == 0){ ERROR_PROPAGATE(arg_parse_name_int(argc, argv, &index, &family, 9, socket_parse_protocol_family)); }else if(str_lcmp(argv[index] + 2, "help", 5) == 0){ nettest2_print_help(); return EOK; }else if(str_lcmp(argv[index] + 2, "messages=", 6) == 0){ ERROR_PROPAGATE(arg_parse_int(argc, argv, &index, &messages, 8)); }else if(str_lcmp(argv[index] + 2, "sockets=", 6) == 0){ ERROR_PROPAGATE(arg_parse_int(argc, argv, &index, &sockets, 8)); }else if(str_lcmp(argv[index] + 2, "port=", 5) == 0){ ERROR_PROPAGATE(arg_parse_int(argc, argv, &index, &value, 7)); port = (uint16_t) value; }else if(str_lcmp(argv[index] + 2, "type=", 5) == 0){ ERROR_PROPAGATE(arg_parse_name_int(argc, argv, &index, &value, 7, socket_parse_socket_type)); type = (sock_type_t) value; }else if(str_lcmp(argv[index] + 2, "verbose", 8) == 0){ verbose = 1; }else{ nettest2_print_help(); return EINVAL; } break; default: nettest2_print_help(); return EINVAL; } }else{ nettest2_print_help(); return EINVAL; } } // if not before the last argument containing the address if(index >= argc){ printf("Command line error: missing address\n"); nettest2_print_help(); return EINVAL; } // prepare the address buffer bzero(address_data, max_length); switch(family){ case PF_INET: address_in->sin_family = AF_INET; address_in->sin_port = htons(port); address_start = (uint8_t *) &address_in->sin_addr.s_addr; addrlen = sizeof(struct sockaddr_in); break; case PF_INET6: address_in6->sin6_family = AF_INET6; address_in6->sin6_port = htons(port); address_start = (uint8_t *) &address_in6->sin6_addr.s6_addr; addrlen = sizeof(struct sockaddr_in6); break; default: fprintf(stderr, "Address family is not supported\n"); return EAFNOSUPPORT; } // parse the last argument which should contain the address if(ERROR_OCCURRED(inet_pton(family, argv[argc - 1], address_start))){ fprintf(stderr, "Address parse error %d\n", ERROR_CODE); return ERROR_CODE; } // check the buffer size if(size <= 0){ fprintf(stderr, "Data buffer size too small (%d). Using 1024 bytes instead.\n", size); size = 1024; } // prepare the buffer // size plus terminating null (\0) data = (char *) malloc(size + 1); if(! data){ fprintf(stderr, "Failed to allocate data buffer.\n"); return ENOMEM; } nettest2_refresh_data(data, size); // check the socket count if(sockets <= 0){ fprintf(stderr, "Socket count too small (%d). Using 2 instead.\n", sockets); sockets = 2; } // prepare the socket buffer // count plus the terminating null (\0) socket_ids = (int *) malloc(sizeof(int) * (sockets + 1)); if(! socket_ids){ fprintf(stderr, "Failed to allocate receive buffer.\n"); return ENOMEM; } socket_ids[sockets] = NULL; if(verbose){ printf("Starting tests\n"); } ERROR_PROPAGATE(sockets_create(verbose, socket_ids, sockets, family, type)); if(type == SOCK_STREAM){ ERROR_PROPAGATE(sockets_connect(verbose, socket_ids, sockets, address, addrlen)); } if(verbose){ printf("\n"); } if(ERROR_OCCURRED(gettimeofday(&time_before, NULL))){ fprintf(stderr, "Get time of day error %d\n", ERROR_CODE); return ERROR_CODE; } ERROR_PROPAGATE(sockets_sendto_recvfrom(verbose, socket_ids, sockets, address, &addrlen, data, size, messages)); if(ERROR_OCCURRED(gettimeofday(&time_after, NULL))){ fprintf(stderr, "Get time of day error %d\n", ERROR_CODE); return ERROR_CODE; } if(verbose){ printf("\tOK\n"); } printf("sendto + recvfrom tested in %d microseconds\n", tv_sub(&time_after, &time_before)); if(ERROR_OCCURRED(gettimeofday(&time_before, NULL))){ fprintf(stderr, "Get time of day error %d\n", ERROR_CODE); return ERROR_CODE; } ERROR_PROPAGATE(sockets_sendto(verbose, socket_ids, sockets, address, addrlen, data, size, messages)); ERROR_PROPAGATE(sockets_recvfrom(verbose, socket_ids, sockets, address, &addrlen, data, size, messages)); if(ERROR_OCCURRED(gettimeofday(&time_after, NULL))){ fprintf(stderr, "Get time of day error %d\n", ERROR_CODE); return ERROR_CODE; } if(verbose){ printf("\tOK\n"); } printf("sendto, recvfrom tested in %d microseconds\n", tv_sub(&time_after, &time_before)); ERROR_PROPAGATE(sockets_close(verbose, socket_ids, sockets)); if(verbose){ printf("\nExiting\n"); } return EOK; } void nettest2_print_help(void){ printf( "Network Networking test 2 aplication - UDP transfer\n" \ "Usage: echo [options] numeric_address\n" \ "Where options are:\n" \ "-f protocol_family | --family=protocol_family\n" \ "\tThe listenning socket protocol family. Only the PF_INET and PF_INET6 are supported.\n" "\n" \ "-h | --help\n" \ "\tShow this application help.\n" "\n" \ "-m count | --messages=count\n" \ "\tThe number of messages to send and receive per socket. The default is 10.\n" \ "\n" \ "-n sockets | --sockets=count\n" \ "\tThe number of sockets to use. The default is 10.\n" \ "\n" \ "-p port_number | --port=port_number\n" \ "\tThe port number the application should send messages to. The default is 7.\n" \ "\n" \ "-s packet_size | --size=packet_size\n" \ "\tThe packet data size the application sends. The default is 29 bytes.\n" \ "\n" \ "-v | --verbose\n" \ "\tShow all output messages.\n" ); } void nettest2_refresh_data(char * data, size_t size){ size_t length; // fill the data length = 0; while(size > length + sizeof(NETTEST2_TEXT) - 1){ memcpy(data + length, NETTEST2_TEXT, sizeof(NETTEST2_TEXT) - 1); length += sizeof(NETTEST2_TEXT) - 1; } memcpy(data + length, NETTEST2_TEXT, size - length); data[size] = '\0'; } /** @} */