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 */
71void 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 */
89void 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 */
146u_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 */
180void 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 */
201int 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 */
228u_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 */
258int 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 */
304void 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 */
327int 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 */
368int 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 */
437struct 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 */
477struct 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 */
509struct 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