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@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  */
fatal_error(char * errmsg)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  */
maximize_sockbuf(int sd)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  */
calc_checksum(u_int16_t * data,size_t size)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  */
fill_payload(unsigned char * payload_p,size_t size)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  */
rand_within(int first,int last)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  */
bit_change_seed(size_t bitsize,size_t oversize)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  */
eth_pton(int af,const char * str,struct sockaddr_ll * ll)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  */
get_ifinfo(struct ifreq * ans,int sock_fd,const char * ifname,int query)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  */
strtotimespec(const char * str,struct timespec * ts_p)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  */
get_a_lla_byifindex(struct sockaddr_in6 * lla_p,int ifindex)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  */
get_maddrinfo(sa_family_t family,const char * maddr,const char * portnum)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  */
create_group_info(uint32_t ifindex,struct addrinfo * mainfo_p)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  */
create_source_filter(uint32_t ifindex,struct addrinfo * mainfo_p,uint32_t fmode,char * saddrs)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