Home | History | Annotate | Download | only in ns-tools
      1 /******************************************************************************/
      2 /*                                                                            */
      3 /*   Copyright (c) International Business Machines  Corp., 2005, 2006         */
      4 /*                                                                            */
      5 /*   This program is free software;  you can redistribute it and/or modify    */
      6 /*   it under the terms of the GNU General Public License as published by     */
      7 /*   the Free Software Foundation; either version 2 of the License, or        */
      8 /*   (at your option) any later version.                                      */
      9 /*                                                                            */
     10 /*   This program is distributed in the hope that it will be useful,          */
     11 /*   but WITHOUT ANY WARRANTY;  without even the implied warranty of          */
     12 /*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See                */
     13 /*   the GNU General Public License for more details.                         */
     14 /*                                                                            */
     15 /*   You should have received a copy of the GNU General Public License        */
     16 /*   along with this program;  if not, write to the Free Software             */
     17 /*   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA  */
     18 /*                                                                            */
     19 /******************************************************************************/
     20 
     21 /*
     22  * File:
     23  *	ns-common.c
     24  *
     25  * Description:
     26  *	Common functions and variables in the ns-tools
     27  *
     28  * Author:
     29  *	Mitsuru Chinen <mitch (at) jp.ibm.com>
     30  *
     31  * History:
     32  *	Oct 19 2005 - Created (Mitsuru Chinen)
     33  *	May  1 2006 - Added functions for broken_ip, route, multicast tests
     34  *---------------------------------------------------------------------------*/
     35 
     36 /*
     37  * Fixed values
     38  */
     39 #define PROC_RMEM_MAX	"/proc/sys/net/core/rmem_max"
     40 #define PROC_WMEM_MAX	"/proc/sys/net/core/wmem_max"
     41 
     42 /*
     43  * Standard Header Files
     44  */
     45 #include <stdio.h>
     46 #include <stdlib.h>
     47 #include <string.h>
     48 #include <sys/ioctl.h>
     49 #include <sys/types.h>
     50 #include <sys/socket.h>
     51 #include <net/ethernet.h>
     52 #include <net/if.h>
     53 #include <net/if_arp.h>
     54 
     55 #include "ns-mcast.h"
     56 #define NS_COMMON 1
     57 #include "ns-traffic.h"
     58 
     59 /*
     60  * Function: fatal_error()
     61  *
     62  * Description:
     63  *  Output an error message then exit the program with EXIT_FAILURE
     64  *
     65  * Argument:
     66  *  errmsg: message printed by perror()
     67  *
     68  * Return value:
     69  *  This function does not return.
     70  */
     71 void fatal_error(char *errmsg)
     72 {
     73 	perror(errmsg);
     74 	exit(EXIT_FAILURE);
     75 }
     76 
     77 /*
     78  * Function: maximize_sockbuf()
     79  *
     80  * Descripton:
     81  *  This function maximize the send and receive buffer size of a socket
     82  *
     83  * Argument:
     84  *  sd:	target socket descriptor
     85  *
     86  * Return value:
     87  *  None
     88  */
     89 void maximize_sockbuf(int sd)
     90 {
     91 	size_t idx;
     92 	int level[] = { SO_RCVBUF, SO_SNDBUF };
     93 	char *procfile[] = { PROC_RMEM_MAX, PROC_WMEM_MAX };
     94 	char *bufname[] = { "rcvbuf", "sndbuf" };
     95 
     96 	for (idx = 0; idx < (sizeof(level) / sizeof(int)); idx++) {
     97 		FILE *fp;	/* File pointer to a proc file */
     98 		int bufsiz;	/* buffer size of socket */
     99 		unsigned int optlen;	/* size of sd option parameter */
    100 
    101 		if ((fp = fopen(procfile[idx], "r")) == NULL) {
    102 			fprintf(stderr, "Failed to open %s\n", procfile[idx]);
    103 			fatal_error("fopen()");
    104 		}
    105 		if ((fscanf(fp, "%d", &bufsiz)) != 1) {
    106 			fprintf(stderr, "Failed to read from %s\n",
    107 				procfile[idx]);
    108 			fatal_error("fscanf()");
    109 		}
    110 		if (setsockopt
    111 		    (sd, SOL_SOCKET, level[idx], &bufsiz, sizeof(int))) {
    112 			fatal_error("setsockopt()");
    113 		}
    114 		if (fclose(fp)) {
    115 			fprintf(stderr, "Failed to close to %s\n",
    116 				procfile[idx]);
    117 			fatal_error("fopen()");
    118 		}
    119 
    120 		if (debug) {
    121 			optlen = sizeof(bufsiz);
    122 			if (getsockopt
    123 			    (sd, SOL_SOCKET, level[idx], &bufsiz,
    124 			     &optlen) < 0) {
    125 				fatal_error("getsockopt()");
    126 			}
    127 			fprintf(stderr, "socket %s size is %d\n", bufname[idx],
    128 				bufsiz);
    129 		}
    130 	}
    131 }
    132 
    133 /*
    134  * Function: calc_checksum()
    135  *
    136  * Description:
    137  *  This function calculate the checksum of IPv4 or ICMP
    138  *
    139  * Argument:
    140  *  data: pointer to target data for checksum
    141  *  size: target data size
    142  *
    143  * Return value:
    144  *  None
    145  */
    146 u_int16_t calc_checksum(u_int16_t * data, size_t size)
    147 {
    148 	u_int32_t sum;
    149 	u_int16_t *pos;
    150 	size_t rest;
    151 
    152 	sum = 0;
    153 	pos = data;
    154 	for (rest = size; rest > 1; rest -= 2)
    155 		sum += *(pos++);
    156 
    157 	if (rest > 0)
    158 		sum += (*pos) & 0xff00;
    159 
    160 	sum = (sum & 0xffff) + (sum >> 16);
    161 	sum = (sum & 0xffff) + (sum >> 16);
    162 	sum = ~sum;
    163 
    164 	return sum;
    165 }
    166 
    167 /*
    168  * Function: fill_payload()
    169  *
    170  * Description:
    171  *  This function fills the payload
    172  *
    173  * Argument:
    174  *  payload_p: pointer to data of payload
    175  *    size:    payload size
    176  *
    177  * Return value:
    178  *  None
    179  */
    180 void fill_payload(unsigned char *payload_p, size_t size)
    181 {
    182 	size_t idx;
    183 
    184 	for (idx = 0; idx < size; idx++)
    185 		*(payload_p + idx) = idx % 0x100;
    186 }
    187 
    188 /*
    189  * Function: rand_within()
    190  *
    191  * Description:
    192  *  This function returns a presudo-random integer within specified range
    193  *
    194  * Argument:
    195  *  first: Fisrt value of the range. If negative, assumed 0
    196  *  last : Last value of the range. If bigger than RAND_MAX, assumed RAND_MAX
    197  *
    198  * Return value:
    199  *  integer value between first to last
    200  */
    201 int rand_within(int first, int last)
    202 {
    203 	unsigned int num;
    204 	int rand_val;
    205 
    206 	first = first < 0 ? 0 : first;
    207 	last = RAND_MAX < (unsigned int)last ? RAND_MAX : last;
    208 
    209 	num = last - first + 1U;
    210 	rand_val = rand() / ((RAND_MAX + 1U) / num) + first;
    211 
    212 	return rand_val;
    213 }
    214 
    215 /*
    216  * Function: bit_change_seed
    217  *
    218  * Description:
    219  *  This function creates a seed to change 1 bit at random position
    220  *
    221  * Argument:
    222  *  bitsize : bit size of data whose bit would be changed
    223  *  oversize: This value controls whether a bit is changed or not
    224  *
    225  * Return value:
    226  *  seed of the bit for change.
    227  */
    228 u_int32_t bit_change_seed(size_t bitsize, size_t oversize)
    229 {
    230 	int rand_val;
    231 	u_int32_t seed;
    232 	rand_val = rand() / ((RAND_MAX + 1U) / (bitsize + oversize));
    233 
    234 	seed = (rand_val < bitsize) ? (0x00000001 << rand_val) : 0;
    235 
    236 	if (debug)
    237 		fprintf(stderr, "Bit seed is %08x\n", seed);
    238 
    239 	return seed;
    240 }
    241 
    242 /*
    243  * Function: eth_pton()
    244  *
    245  * Description:
    246  *  This function convert a string to struct sockaddr_ll (Ethernet)
    247  *  Note) The ifindex is set to `any'.
    248  *
    249  * Argument:
    250  *   af : AF_INET or AF_INET6
    251  *   str: Pointer to a string which represents MAC address
    252  *   ll : pointer to struct sockaddr_ll
    253  *
    254  * Return value:
    255  *    0  : Success
    256  *    1  : Fail
    257  */
    258 int eth_pton(int af, const char *str, struct sockaddr_ll *ll)
    259 {
    260 	size_t idx;
    261 	unsigned char *addr_p;
    262 	unsigned int val[ETH_ALEN];
    263 
    264 	ll->sll_family = AF_PACKET;	/* Always AF_PACKET */
    265 	if (af == AF_INET)
    266 		ll->sll_protocol = htons(ETH_P_IP);	/* IPv4 */
    267 	else
    268 		ll->sll_protocol = htons(ETH_P_IPV6);	/* IPv6 */
    269 	ll->sll_ifindex = 0;	/* any interface */
    270 	ll->sll_hatype = ARPHRD_ETHER;	/* Header type */
    271 	ll->sll_pkttype = PACKET_OTHERHOST;	/* Packet type */
    272 	ll->sll_halen = ETH_ALEN;	/* Length of address */
    273 
    274 	/* Physical layer address */
    275 	if (sscanf(str, "%2x:%2x:%2x:%2x:%2x:%2x", &val[0], &val[1],
    276 		   &val[2], &val[3], &val[4], &val[5]) != ETH_ALEN) {
    277 		fprintf(stderr, "%s is not a valid MAC address", str);
    278 		return 1;
    279 	}
    280 
    281 	addr_p = (unsigned char *)ll->sll_addr;
    282 	for (idx = 0; idx < ETH_ALEN; idx++)
    283 		addr_p[idx] = val[idx];
    284 
    285 	return 0;
    286 }
    287 
    288 /*
    289  * Function: get_ifinfo()
    290  *
    291  * Description:
    292  *  This function gets the interface information with ioctl()
    293  *
    294  * Argument:
    295  *    ans   : ifreq structure to store the information
    296  *  sock_fd : socket file descriptor
    297  *  ifname  : interface name
    298  *   query  : ioctl request value
    299  *
    300  * Return value:
    301  *  None
    302  *
    303  */
    304 void get_ifinfo(struct ifreq *ans, int sock_fd, const char *ifname, int query)
    305 {
    306 	memset(ans, '\0', sizeof(struct ifreq));
    307 	strncpy(ans->ifr_name, ifname, (IFNAMSIZ - 1));
    308 
    309 	if (ioctl(sock_fd, query, ans) < 0)
    310 		fatal_error("ioctl()");
    311 }
    312 
    313 /*
    314  * Function: strtotimespec()
    315  *
    316  * Description:
    317  *  This function converts a string to timespec structure
    318  *
    319  * Argument:
    320  *    str   : nano second value in character representation
    321  *    ts_p  : pointer to a timespec structure
    322  *
    323  * Return value:
    324  *  0: Success
    325  *  1: Fail
    326  */
    327 int strtotimespec(const char *str, struct timespec *ts_p)
    328 {
    329 	size_t len;
    330 	char *sec_str;
    331 	unsigned long sec = 0;
    332 	unsigned long nsec = 0;
    333 
    334 	len = strlen(str);
    335 	if (len > 9) {		/* Check the specified value is bigger than 999999999 */
    336 		sec_str = calloc((len - 9 + 1), sizeof(char));
    337 		strncpy(sec_str, str, len - 9);
    338 		sec = strtoul(sec_str, NULL, 0);
    339 		if (sec > 0x7fffffff)
    340 			return 1;
    341 		free(sec_str);
    342 		nsec = strtoul(str + len - 9, NULL, 0);
    343 	} else {
    344 		nsec = strtoul(str, NULL, 0);
    345 	}
    346 
    347 	ts_p->tv_sec = sec;
    348 	ts_p->tv_nsec = nsec;
    349 
    350 	return 0;
    351 }
    352 
    353 /*
    354  * Function: get_a_lla_byifindex()
    355  *
    356  * Description:
    357  *  This function gets one of the link-local addresses which is specified
    358  *  by interface index
    359  *
    360  * Argument:
    361  *   lla_p  : pointer to a sockaddr_in6 sturcture which stores the lla
    362  *  ifindex : index of the interface
    363  *
    364  * Return value:
    365  *  0: Success
    366  *  1: Fail
    367  */
    368 int get_a_lla_byifindex(struct sockaddr_in6 *lla_p, int ifindex)
    369 {
    370 	FILE *fp;
    371 	int ret;
    372 	unsigned int oct[16];
    373 	int ifidx, prefixlen, scope;
    374 	char line[PROC_IFINET6_FILE_LINELENGTH];
    375 	int pos;
    376 
    377 	if ((fp = fopen(PROC_IFINET6_FILE, "r")) == NULL) {
    378 		fprintf(stderr, "Faile to open %s\n", PROC_IFINET6_FILE);
    379 		return 1;
    380 	}
    381 
    382 	while (fgets(line, PROC_IFINET6_FILE_LINELENGTH, fp) != NULL) {
    383 		ret = sscanf(line,
    384 			     "%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x %x %x %x",
    385 			     &oct[0], &oct[1], &oct[2], &oct[3],
    386 			     &oct[4], &oct[5], &oct[6], &oct[7],
    387 			     &oct[8], &oct[9], &oct[10], &oct[11],
    388 			     &oct[12], &oct[13], &oct[14], &oct[15],
    389 			     &ifidx, &prefixlen, &scope);
    390 
    391 		if (ret == EOF)
    392 			fatal_error("scanf()");
    393 		else if (ret != 19)
    394 			fatal_error
    395 			    ("The number of input item is less than the expected");
    396 
    397 		if (ifidx != ifindex)
    398 			continue;
    399 
    400 		if (prefixlen != 64)
    401 			continue;
    402 
    403 		if (scope != PROC_IFINET6_LINKLOCAL)
    404 			continue;
    405 
    406 		/* Find a link-local address */
    407 		lla_p->sin6_family = AF_INET6;
    408 		lla_p->sin6_port = 0;
    409 		lla_p->sin6_flowinfo = 0;
    410 		lla_p->sin6_scope_id = ifindex;
    411 
    412 		for (pos = 0; pos < 16; pos++)
    413 			lla_p->sin6_addr.s6_addr[pos] = oct[pos];
    414 
    415 		return 0;
    416 	}
    417 
    418 	fprintf(stderr, "No link-local address is found.\n");
    419 	return 1;
    420 }
    421 
    422 /*
    423  * Function: get_maddrinfo()
    424  *
    425  * Description:
    426  *  This function translates multicast address informantion into the addrinfo
    427  *  structure
    428  *
    429  * Argument:
    430  *   family:    protocol family
    431  *   maddr:     multicast address in character string
    432  *   portnum:   port number in character string
    433  *
    434  * Return value:
    435  *  pointer to the addrinfo which stores the multicast address information
    436  */
    437 struct addrinfo *get_maddrinfo(sa_family_t family, const char *maddr,
    438 			       const char *portnum)
    439 {
    440 	struct addrinfo hints;	/* hints for getaddrinfo() */
    441 	struct addrinfo *res;	/* pointer to addrinfo structure */
    442 	int err;		/* return value of getaddrinfo */
    443 
    444 	memset(&hints, '\0', sizeof(struct addrinfo));
    445 	hints.ai_family = family;
    446 	hints.ai_socktype = SOCK_DGRAM;
    447 	hints.ai_protocol = IPPROTO_UDP;
    448 	hints.ai_flags |= AI_NUMERICHOST;
    449 
    450 	err = getaddrinfo(maddr, portnum, &hints, &res);
    451 	if (err) {
    452 		fprintf(stderr, "getaddrinfo(): %s\n", gai_strerror(err));
    453 		exit(EXIT_FAILURE);
    454 	}
    455 	if (res->ai_next) {
    456 		fprintf(stderr, "getaddrinfo(): multiple address is found.");
    457 		exit(EXIT_FAILURE);
    458 	}
    459 
    460 	return res;
    461 }
    462 
    463 /*
    464  * Function: create_group_info()
    465  *
    466  * Description:
    467  *  This function create a group information to join the group
    468  *  This function calls malloc to store the information
    469  *
    470  * Argument:
    471  *   ifindex:   interface index
    472  *   mainfo_p:  pointer to addrinfo structure for multicast address
    473  *
    474  * Return value:
    475  *  pointer to allocated group_filter structure
    476  */
    477 struct group_req *create_group_info(uint32_t ifindex, struct addrinfo *mainfo_p)
    478 {
    479 	struct group_req *greq;
    480 
    481 	/* allocate memory for group_filter */
    482 	greq = (struct group_req *)calloc(1, sizeof(struct group_req));
    483 	if (greq == NULL)
    484 		fatal_error("calloc()");
    485 
    486 	/* substitute informations */
    487 	greq->gr_interface = ifindex;
    488 	memcpy(&greq->gr_group, mainfo_p->ai_addr, mainfo_p->ai_addrlen);
    489 
    490 	return greq;
    491 }
    492 
    493 /*
    494  * Function: create_source_filter()
    495  *
    496  * Description:
    497  *  This function create a source filter.
    498  *  This function calls malloc to store the source filter.
    499  *
    500  * Argument:
    501  *   ifindex:   interface index
    502  *   mainfo_p:  pointer to addrinfo structure for multicast address
    503  *   fmode:     filter mode
    504  *   saddrs:    comma separated array of the source addresses
    505  *
    506  * Return value:
    507  *  pointer to allocated group_filter structure
    508  */
    509 struct group_filter *create_source_filter(uint32_t ifindex,
    510 					  struct addrinfo *mainfo_p,
    511 					  uint32_t fmode, char *saddrs)
    512 {
    513 	struct group_filter *gsf;	/* pointer to group_filter structure */
    514 	uint32_t numsrc;	/* number of source address */
    515 	struct addrinfo hints;	/* hints for getaddrinfo() */
    516 	struct addrinfo *res;	/* pointer to addrinfo structure */
    517 	int err;		/* return value of getaddrinfo */
    518 	uint32_t idx;
    519 	char *sp, *ep;
    520 
    521 	/* calculate the number of source address */
    522 	numsrc = 1;
    523 	for (sp = saddrs; *sp != '\0'; sp++)
    524 		if (*sp == ',')
    525 			numsrc++;
    526 
    527 	if (debug)
    528 		fprintf(stderr, "number of source address is %u\n", numsrc);
    529 
    530 	/* allocate memory for group_filter */
    531 	gsf = (struct group_filter *)calloc(1, GROUP_FILTER_SIZE(numsrc));
    532 	if (gsf == NULL)
    533 		fatal_error("calloc()");
    534 
    535 	/* substitute interface index, multicast address, filter mode */
    536 	gsf->gf_interface = ifindex;
    537 	memcpy(&gsf->gf_group, mainfo_p->ai_addr, mainfo_p->ai_addrlen);
    538 	gsf->gf_fmode = fmode;
    539 	gsf->gf_numsrc = numsrc;
    540 
    541 	/* extract source address aray and substitute the addersses */
    542 	memset(&hints, '\0', sizeof(struct addrinfo));
    543 	hints.ai_family = mainfo_p->ai_family;
    544 	hints.ai_socktype = SOCK_DGRAM;
    545 	hints.ai_protocol = IPPROTO_UDP;
    546 	hints.ai_flags |= AI_NUMERICHOST;
    547 
    548 	/* extract source address aray and substitute the addersses */
    549 	memset(&hints, '\0', sizeof(struct addrinfo));
    550 	hints.ai_family = mainfo_p->ai_family;
    551 	hints.ai_socktype = SOCK_DGRAM;
    552 	hints.ai_protocol = IPPROTO_UDP;
    553 	hints.ai_flags |= AI_NUMERICHOST;
    554 
    555 	sp = saddrs;
    556 	for (idx = 0; idx < numsrc; idx++) {
    557 		ep = strchr(sp, ',');
    558 		if (ep != NULL)
    559 			*ep = '\0';
    560 		if (debug)
    561 			fprintf(stderr, "source address[%u]: %s\n", idx, sp);
    562 
    563 		err = getaddrinfo(sp, NULL, &hints, &res);
    564 		if (err) {
    565 			fprintf(stderr, "getaddrinfo(): %s\n",
    566 				gai_strerror(err));
    567 			exit(EXIT_FAILURE);
    568 		}
    569 
    570 		memcpy(&gsf->gf_slist[idx], res->ai_addr, res->ai_addrlen);
    571 		freeaddrinfo(res);
    572 		sp = ep + 1;
    573 	}
    574 
    575 	return gsf;
    576 }
    577