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