/******************************************************************************/
/* */
/* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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;
}