| /******************************************************************************/ |
| /* */ |
| /* Copyright (c) International Business Machines Corp., 2005, 2006 */ |
| /* */ |
| /* This program is free software; you can redistribute it and/or modify */ |
| /* it under the terms of the GNU General Public License as published by */ |
| /* the Free Software Foundation; either version 2 of the License, or */ |
| /* (at your option) any later version. */ |
| /* */ |
| /* This program is distributed in the hope that it will be useful, */ |
| /* but WITHOUT ANY WARRANTY; without even the implied warranty of */ |
| /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See */ |
| /* the GNU General Public License for more details. */ |
| /* */ |
| /* You should have received a copy of the GNU General Public License */ |
| /* along with this program; if not, write to the Free Software */ |
| /* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ |
| /* */ |
| /******************************************************************************/ |
| |
| /* |
| * File: |
| * ns-common.c |
| * |
| * Description: |
| * Common functions and variables in the ns-tools |
| * |
| * Author: |
| * Mitsuru Chinen <mitch@jp.ibm.com> |
| * |
| * History: |
| * Oct 19 2005 - Created (Mitsuru Chinen) |
| * May 1 2006 - Added functions for broken_ip, route, multicast tests |
| *---------------------------------------------------------------------------*/ |
| |
| /* |
| * Fixed values |
| */ |
| #define PROC_RMEM_MAX "/proc/sys/net/core/rmem_max" |
| #define PROC_WMEM_MAX "/proc/sys/net/core/wmem_max" |
| |
| /* |
| * Standard Header Files |
| */ |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <sys/ioctl.h> |
| #include <sys/types.h> |
| #include <sys/socket.h> |
| #include <net/ethernet.h> |
| #include <net/if.h> |
| #include <net/if_arp.h> |
| |
| #include "ns-mcast.h" |
| #define NS_COMMON 1 |
| #include "ns-traffic.h" |
| |
| /* |
| * Function: fatal_error() |
| * |
| * Description: |
| * Output an error message then exit the program with EXIT_FAILURE |
| * |
| * Argument: |
| * errmsg: message printed by perror() |
| * |
| * Return value: |
| * This function does not return. |
| */ |
| void |
| fatal_error(char *errmsg) |
| { |
| perror(errmsg); |
| exit(EXIT_FAILURE); |
| } |
| |
| /* |
| * Function: maximize_sockbuf() |
| * |
| * Descripton: |
| * This function maximize the send and receive buffer size of a socket |
| * |
| * Argument: |
| * sd: target socket descriptor |
| * |
| * Return value: |
| * None |
| */ |
| void |
| maximize_sockbuf(int sd) |
| { |
| size_t idx; |
| int level[] = { SO_RCVBUF, SO_SNDBUF }; |
| char *procfile[] = { PROC_RMEM_MAX, PROC_WMEM_MAX }; |
| char *bufname[] = {"rcvbuf", "sndbuf"}; |
| |
| for (idx = 0; idx < (sizeof(level) / sizeof(int)); idx++) { |
| FILE *fp; /* File pointer to a proc file */ |
| int bufsiz; /* buffer size of socket */ |
| unsigned int optlen; /* size of sd option parameter */ |
| |
| if ((fp = fopen(procfile[idx], "r")) == NULL) { |
| fprintf(stderr, "Failed to open %s\n", procfile[idx]); |
| fatal_error("fopen()"); |
| } |
| if ((fscanf(fp, "%d", &bufsiz)) != 1) { |
| fprintf(stderr, "Failed to read from %s\n", procfile[idx]); |
| fatal_error("fscanf()"); |
| } |
| if (setsockopt(sd, SOL_SOCKET, level[idx], &bufsiz, sizeof(int))) { |
| fatal_error("setsockopt()"); |
| } |
| if (fclose(fp)) { |
| fprintf(stderr, "Failed to close to %s\n", procfile[idx]); |
| fatal_error("fopen()"); |
| } |
| |
| if (debug) { |
| optlen = sizeof(bufsiz); |
| if (getsockopt(sd, SOL_SOCKET, level[idx], &bufsiz, &optlen) < 0) { |
| fatal_error("getsockopt()"); |
| } |
| fprintf(stderr, "socket %s size is %d\n", bufname[idx], bufsiz); |
| } |
| } |
| } |
| |
| /* |
| * Function: calc_checksum() |
| * |
| * Description: |
| * This function calculate the checksum of IPv4 or ICMP |
| * |
| * Argument: |
| * data: pointer to target data for checksum |
| * size: target data size |
| * |
| * Return value: |
| * None |
| */ |
| u_int16_t |
| calc_checksum(u_int16_t *data, size_t size) |
| { |
| u_int32_t sum; |
| u_int16_t *pos; |
| size_t rest; |
| |
| sum = 0; |
| pos = data; |
| for (rest = size; rest > 1; rest -= 2) |
| sum += *(pos++); |
| |
| if (rest > 0) |
| sum += (*pos) & 0xff00; |
| |
| sum = (sum & 0xffff) + (sum >> 16); |
| sum = (sum & 0xffff) + (sum >> 16); |
| sum = ~sum; |
| |
| return sum; |
| } |
| |
| /* |
| * Function: fill_payload() |
| * |
| * Description: |
| * This function fills the payload |
| * |
| * Argument: |
| * payload_p: pointer to data of payload |
| * size: payload size |
| * |
| * Return value: |
| * None |
| */ |
| void |
| fill_payload(unsigned char *payload_p, size_t size) |
| { |
| size_t idx; |
| |
| for (idx = 0; idx < size; idx++) |
| *(payload_p + idx) = idx % 0x100; |
| } |
| |
| /* |
| * Function: rand_within() |
| * |
| * Description: |
| * This function returns a presudo-random integer within specified range |
| * |
| * Argument: |
| * first: Fisrt value of the range. If negative, assumed 0 |
| * last : Last value of the range. If bigger than RAND_MAX, assumed RAND_MAX |
| * |
| * Return value: |
| * integer value between first to last |
| */ |
| int |
| rand_within(int first, int last) |
| { |
| unsigned int num; |
| int rand_val; |
| |
| first = first < 0 ? 0 : first; |
| last = RAND_MAX < (unsigned int)last ? RAND_MAX : last; |
| |
| num = last - first + 1U; |
| rand_val = rand() / ((RAND_MAX + 1U) / num) + first; |
| |
| return rand_val; |
| } |
| |
| /* |
| * Function: bit_change_seed |
| * |
| * Description: |
| * This function creates a seed to change 1 bit at random position |
| * |
| * Argument: |
| * bitsize : bit size of data whose bit would be changed |
| * oversize: This value controls whether a bit is changed or not |
| * |
| * Return value: |
| * seed of the bit for change. |
| */ |
| u_int32_t |
| bit_change_seed(size_t bitsize, size_t oversize) |
| { |
| int rand_val; |
| u_int32_t seed; |
| rand_val = rand() / ((RAND_MAX + 1U) / (bitsize + oversize)); |
| |
| seed = (rand_val < bitsize) ? (0x00000001 << rand_val) : 0; |
| |
| if (debug) |
| fprintf(stderr, "Bit seed is %08x\n", seed); |
| |
| return seed; |
| } |
| |
| /* |
| * Function: eth_pton() |
| * |
| * Description: |
| * This function convert a string to struct sockaddr_ll (Ethernet) |
| * Note) The ifindex is set to `any'. |
| * |
| * Argument: |
| * af : AF_INET or AF_INET6 |
| * str: Pointer to a string which represents MAC address |
| * ll : pointer to struct sockaddr_ll |
| * |
| * Return value: |
| * 0 : Success |
| * 1 : Fail |
| */ |
| int |
| eth_pton(int af, const char *str, struct sockaddr_ll *ll) |
| { |
| size_t idx; |
| unsigned char *addr_p; |
| unsigned int val[ETH_ALEN]; |
| |
| ll->sll_family = AF_PACKET; /* Always AF_PACKET */ |
| if (af == AF_INET) |
| ll->sll_protocol = htons(ETH_P_IP); /* IPv4 */ |
| else |
| ll->sll_protocol = htons(ETH_P_IPV6); /* IPv6 */ |
| ll->sll_ifindex = 0; /* any interface */ |
| ll->sll_hatype = ARPHRD_ETHER; /* Header type */ |
| ll->sll_pkttype = PACKET_OTHERHOST; /* Packet type */ |
| ll->sll_halen = ETH_ALEN; /* Length of address */ |
| |
| /* Physical layer address */ |
| if (sscanf(str, "%2x:%2x:%2x:%2x:%2x:%2x", &val[0], &val[1], |
| &val[2], &val[3], &val[4], &val[5]) != ETH_ALEN) { |
| fprintf(stderr, "%s is not a valid MAC address", str); |
| return 1; |
| } |
| |
| addr_p = (unsigned char *)ll->sll_addr; |
| for (idx = 0; idx < ETH_ALEN; idx++) |
| addr_p[idx] = val[idx]; |
| |
| return 0; |
| } |
| |
| /* |
| * Function: get_ifinfo() |
| * |
| * Description: |
| * This function gets the interface information with ioctl() |
| * |
| * Argument: |
| * ans : ifreq structure to store the information |
| * sock_fd : socket file descriptor |
| * ifname : interface name |
| * query : ioctl request value |
| * |
| * Return value: |
| * None |
| * |
| */ |
| void |
| get_ifinfo(struct ifreq *ans, int sock_fd, const char *ifname, int query) |
| { |
| memset(ans, '\0', sizeof(struct ifreq)); |
| strncpy(ans->ifr_name, ifname, (IFNAMSIZ - 1)); |
| |
| if (ioctl(sock_fd, query, ans) < 0) |
| fatal_error("ioctl()"); |
| } |
| |
| /* |
| * Function: strtotimespec() |
| * |
| * Description: |
| * This function converts a string to timespec structure |
| * |
| * Argument: |
| * str : nano second value in character representation |
| * ts_p : pointer to a timespec structure |
| * |
| * Return value: |
| * 0: Success |
| * 1: Fail |
| */ |
| int |
| strtotimespec(const char *str, struct timespec *ts_p) |
| { |
| size_t len; |
| char *sec_str; |
| unsigned long sec = 0; |
| unsigned long nsec = 0; |
| |
| len = strlen(str); |
| if (len > 9) { /* Check the specified value is bigger than 999999999 */ |
| sec_str = calloc((len - 9 + 1), sizeof(char)); |
| strncpy(sec_str, str, len - 9); |
| sec = strtoul(sec_str, NULL, 0); |
| if (sec > 0x7fffffff) |
| return 1; |
| free(sec_str); |
| nsec = strtoul(str + len - 9, NULL, 0); |
| } else { |
| nsec = strtoul(str, NULL, 0); |
| } |
| |
| ts_p->tv_sec = sec; |
| ts_p->tv_nsec = nsec; |
| |
| return 0; |
| } |
| |
| /* |
| * Function: get_a_lla_byifindex() |
| * |
| * Description: |
| * This function gets one of the link-local addresses which is specified |
| * by interface index |
| * |
| * Argument: |
| * lla_p : pointer to a sockaddr_in6 sturcture which stores the lla |
| * ifindex : index of the interface |
| * |
| * Return value: |
| * 0: Success |
| * 1: Fail |
| */ |
| int |
| get_a_lla_byifindex(struct sockaddr_in6 *lla_p, int ifindex) |
| { |
| FILE *fp; |
| int ret; |
| unsigned int oct[16]; |
| int ifidx, prefixlen, scope; |
| char line[PROC_IFINET6_FILE_LINELENGTH]; |
| int pos; |
| |
| if ((fp = fopen(PROC_IFINET6_FILE, "r")) == NULL) { |
| fprintf(stderr, "Faile to open %s\n", PROC_IFINET6_FILE); |
| return 1; |
| } |
| |
| while (fgets(line, PROC_IFINET6_FILE_LINELENGTH, fp) != NULL) { |
| ret = sscanf(line, |
| "%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x %x %x %x", |
| &oct[0], &oct[1], &oct[2], &oct[3], |
| &oct[4], &oct[5], &oct[6], &oct[7], |
| &oct[8], &oct[9], &oct[10], &oct[11], |
| &oct[12], &oct[13], &oct[14], &oct[15], |
| &ifidx, &prefixlen, &scope); |
| |
| if (ret == EOF) |
| fatal_error("scanf()"); |
| else if (ret != 19) |
| fatal_error("The number of input item is less than the expected"); |
| |
| if (ifidx != ifindex) |
| continue; |
| |
| if (prefixlen != 64) |
| continue; |
| |
| if (scope != PROC_IFINET6_LINKLOCAL) |
| continue; |
| |
| /* Find a link-local address */ |
| lla_p->sin6_family = AF_INET6; |
| lla_p->sin6_port = 0; |
| lla_p->sin6_flowinfo = 0; |
| lla_p->sin6_scope_id = ifindex; |
| |
| for (pos = 0; pos < 16; pos++) |
| lla_p->sin6_addr.s6_addr[pos] = oct[pos]; |
| |
| return 0; |
| } |
| |
| fprintf(stderr, "No link-local address is found.\n"); |
| return 1; |
| } |
| |
| /* |
| * Function: get_maddrinfo() |
| * |
| * Description: |
| * This function translates multicast address informantion into the addrinfo |
| * structure |
| * |
| * Argument: |
| * family: protocol family |
| * maddr: multicast address in character string |
| * portnum: port number in character string |
| * |
| * Return value: |
| * pointer to the addrinfo which stores the multicast address information |
| */ |
| struct addrinfo * |
| get_maddrinfo(sa_family_t family, const char *maddr, const char *portnum) |
| { |
| struct addrinfo hints; /* hints for getaddrinfo() */ |
| struct addrinfo *res; /* pointer to addrinfo structure */ |
| int err; /* return value of getaddrinfo */ |
| |
| memset(&hints, '\0', sizeof(struct addrinfo)); |
| hints.ai_family = family; |
| hints.ai_socktype = SOCK_DGRAM; |
| hints.ai_protocol = IPPROTO_UDP; |
| hints.ai_flags |= AI_NUMERICHOST; |
| |
| err = getaddrinfo(maddr, portnum, &hints, &res); |
| if (err) { |
| fprintf(stderr, "getaddrinfo(): %s\n", gai_strerror(err)); |
| exit(EXIT_FAILURE); |
| } |
| if (res->ai_next) { |
| fprintf(stderr, "getaddrinfo(): multiple address is found."); |
| exit(EXIT_FAILURE); |
| } |
| |
| return res; |
| } |
| |
| /* |
| * Function: create_group_info() |
| * |
| * Description: |
| * This function create a group information to join the group |
| * This function calls malloc to store the information |
| * |
| * Argument: |
| * ifindex: interface index |
| * mainfo_p: pointer to addrinfo structure for multicast address |
| * |
| * Return value: |
| * pointer to allocated group_filter structure |
| */ |
| struct group_req * |
| create_group_info(uint32_t ifindex, struct addrinfo *mainfo_p) |
| { |
| struct group_req *greq; |
| |
| /* allocate memory for group_filter */ |
| greq = (struct group_req *)calloc(1, sizeof(struct group_req)); |
| if (greq == NULL) |
| fatal_error("calloc()"); |
| |
| /* substitute informations */ |
| greq->gr_interface = ifindex; |
| memcpy(&greq->gr_group, mainfo_p->ai_addr, mainfo_p->ai_addrlen); |
| |
| return greq; |
| } |
| |
| /* |
| * Function: create_source_filter() |
| * |
| * Description: |
| * This function create a source filter. |
| * This function calls malloc to store the source filter. |
| * |
| * Argument: |
| * ifindex: interface index |
| * mainfo_p: pointer to addrinfo structure for multicast address |
| * fmode: filter mode |
| * saddrs: comma separated array of the source addresses |
| * |
| * Return value: |
| * pointer to allocated group_filter structure |
| */ |
| struct group_filter * |
| create_source_filter(uint32_t ifindex, struct addrinfo *mainfo_p, uint32_t fmode, char *saddrs) |
| { |
| struct group_filter *gsf; /* pointer to group_filter structure */ |
| uint32_t numsrc; /* number of source address */ |
| struct addrinfo hints; /* hints for getaddrinfo() */ |
| struct addrinfo *res; /* pointer to addrinfo structure */ |
| int err; /* return value of getaddrinfo */ |
| uint32_t idx; |
| char *sp, *ep; |
| |
| /* calculate the number of source address */ |
| numsrc = 1; |
| for (sp = saddrs; *sp != '\0'; sp++) |
| if (*sp == ',') |
| numsrc++; |
| |
| if (debug) |
| fprintf(stderr, "number of source address is %u\n", numsrc); |
| |
| /* allocate memory for group_filter */ |
| gsf = (struct group_filter *)calloc(1, GROUP_FILTER_SIZE(numsrc)); |
| if (gsf == NULL) |
| fatal_error("calloc()"); |
| |
| /* substitute interface index, multicast address, filter mode */ |
| gsf->gf_interface = ifindex; |
| memcpy(&gsf->gf_group, mainfo_p->ai_addr, mainfo_p->ai_addrlen); |
| gsf->gf_fmode = fmode; |
| gsf->gf_numsrc = numsrc; |
| |
| /* extract source address aray and substitute the addersses */ |
| memset(&hints, '\0', sizeof(struct addrinfo)); |
| hints.ai_family = mainfo_p->ai_family; |
| hints.ai_socktype = SOCK_DGRAM; |
| hints.ai_protocol = IPPROTO_UDP; |
| hints.ai_flags |= AI_NUMERICHOST; |
| |
| /* extract source address aray and substitute the addersses */ |
| memset(&hints, '\0', sizeof(struct addrinfo)); |
| hints.ai_family = mainfo_p->ai_family; |
| hints.ai_socktype = SOCK_DGRAM; |
| hints.ai_protocol = IPPROTO_UDP; |
| hints.ai_flags |= AI_NUMERICHOST; |
| |
| sp = saddrs; |
| for (idx = 0; idx < numsrc; idx++) { |
| ep = strchr(sp, ','); |
| if (ep != NULL) |
| *ep = '\0'; |
| if (debug) |
| fprintf(stderr, "source address[%u]: %s\n", idx, sp); |
| |
| err = getaddrinfo(sp, NULL, &hints, &res); |
| if (err) { |
| fprintf(stderr, "getaddrinfo(): %s\n", gai_strerror(err)); |
| exit(EXIT_FAILURE); |
| } |
| |
| memcpy(&gsf->gf_slist[idx], res->ai_addr, res->ai_addrlen); |
| freeaddrinfo(res); |
| sp = ep + 1; |
| } |
| |
| return gsf; |
| } |