Home | History | Annotate | Download | only in ns-tools
      1 /******************************************************************************/
      2 /*                                                                            */
      3 /*   Copyright (c) International Business Machines  Corp., 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-mcast_join.c
     24  *
     25  * Description:
     26  *	This is an assistant tool to join multicast groups
     27  *
     28  * Author:
     29  *	Mitsuru Chinen <mitch (at) jp.ibm.com>
     30  *
     31  * History:
     32  *	May 1 2006 - Created (Mitsuru Chinen)
     33  *---------------------------------------------------------------------------*/
     34 
     35 /*
     36  * Header Files
     37  */
     38 #include <netinet/in.h>
     39 #include <stdio.h>
     40 #include <stdlib.h>
     41 #include <string.h>
     42 #include <errno.h>
     43 #include <netdb.h>
     44 #include <signal.h>
     45 #include <time.h>
     46 #include <unistd.h>
     47 #include <net/if.h>
     48 #include <sys/ioctl.h>
     49 #include <sys/socket.h>
     50 #include <sys/types.h>
     51 
     52 #include "ns-mcast.h"
     53 #include "ns-traffic.h"
     54 
     55 #define ADDR_STR_MAXSIZE    80
     56 #define OPEN_SOCK_MIN	    6
     57 
     58 /*
     59  * Gloval variables
     60  */
     61 char *program_name;		/* program name */
     62 
     63 struct sigaction handler;	/* Behavior for a signal */
     64 volatile int catch_sighup;	/* When catch the SIGHUP, set to non-zero */
     65 
     66 sa_family_t family;		/* protocol family */
     67 int level;			/* protocol levels */
     68 uint32_t ifindex;		/* interface index where listening multicast */
     69 
     70 size_t num_group;		/* Number of the groups */
     71 char *mcast_prefix;		/* Prefix of the multicast address */
     72 uint32_t fmode;			/* filter mode */
     73 char *saddrs;			/* comma separated array of source addresses */
     74 
     75 int is_multi_socket;		/* If non-zero, multi-socket mode */
     76 
     77 size_t join_leave_times;	/* If non-zero, join-leave mode */
     78 				/* the value is times of join/leave */
     79 char *mcast_addr;		/* multicast address to join/leave */
     80 struct timespec interval;	/* interval for join-leave mode */
     81 
     82 /*
     83  * Function: usage()
     84  *
     85  * Descripton:
     86  *  Print the usage of this program. Then, terminate this program with
     87  *  the specified exit value.
     88  *
     89  * Argument:
     90  *  exit_value:	exit value
     91  *
     92  * Return value:
     93  *  This function does not return.
     94  */
     95 void usage(char *program_name, int exit_value)
     96 {
     97 	FILE *stream = stdout;	/* stream where the usage is output */
     98 
     99 	if (exit_value == EXIT_FAILURE)
    100 		stream = stderr;
    101 
    102 	fprintf(stream, "%s [OPTION]\n"
    103 		"\t-f num\tprotocol family\n"
    104 		"\t\t  4 : IPv4\n"
    105 		"\t\t  6 : IPv6\n"
    106 		"\t-I ifname\tname of listening interface\n"
    107 		"\t-a addr\tmulticast address for join-leave mode\n"
    108 		"\t-F mode\tfilter mode\n"
    109 		"\t\t  include : include mode\n"
    110 		"\t\t  exclude : exclude mode\n"
    111 		"\t-s addrs\tcomma separated array of Source Addresses\n"
    112 		"\t-d\t\tdisplay debug informations\n"
    113 		"\t-h\t\tdisplay this usage\n"
    114 		"\n"
    115 		"\t[multiple join mode]\n"
    116 		"\t  -n num\tnumber of multicast address\n"
    117 		"\t  -p prefix\tprefix of the multicast address\n"
    118 		"\t  -m\t\tmultiple socket mode\n"
    119 		"\t\t  4 : a.b(.x.y) - x y is defined automatically\n"
    120 		"\t\t  6 : {prefix}::z - z is defined automatically\n"
    121 		"\n"
    122 		"\t[join-leave mode]\n"
    123 		"\t  -l times of join/leave\n"
    124 		"\t  -i nsec\tinterval for join-leave mode\n", program_name);
    125 	exit(exit_value);
    126 }
    127 
    128 /*
    129  * Function: set_signal_flag()
    130  *
    131  * Description:
    132  *  This function sets global variables accordig to signal
    133  *
    134  * Argument:
    135  *  type: type of signal
    136  *
    137  * Return value:
    138  *  None
    139  */
    140 void set_signal_flag(int type)
    141 {
    142 	if (debug)
    143 		fprintf(stderr, "Catch signal. type is %d\n", type);
    144 
    145 	switch (type) {
    146 	case SIGHUP:
    147 		catch_sighup = 1;
    148 		handler.sa_handler = SIG_IGN;
    149 		if (sigaction(type, &handler, NULL) < 0)
    150 			fatal_error("sigaction()");
    151 		break;
    152 
    153 	default:
    154 		fprintf(stderr, "Unexpected signal (%d) is caught\n", type);
    155 		exit(EXIT_FAILURE);
    156 	}
    157 }
    158 
    159 /*
    160  * Function: parse_options()
    161  *
    162  * Description:
    163  *  This function parse the options
    164  *
    165  * Argument:
    166  *   argc:  the number of argument
    167  *   argv:  arguments
    168  *
    169  * Return value:
    170  *  None
    171  */
    172 void parse_options(int argc, char *argv[])
    173 {
    174 	int optc;		/* option */
    175 	unsigned long opt_ul;	/* option value in unsigned long */
    176 
    177 	while ((optc = getopt(argc, argv, "f:I:p:F:s:n:ml:i:a:dh")) != EOF) {
    178 		switch (optc) {
    179 		case 'f':
    180 			if (optarg[0] == '4') {
    181 				family = PF_INET;	/* IPv4 */
    182 				level = IPPROTO_IP;
    183 			} else if (optarg[0] == '6') {
    184 				family = PF_INET6;	/* IPv6 */
    185 				level = IPPROTO_IPV6;
    186 			} else {
    187 				fprintf(stderr,
    188 					"protocol family should be 4 or 6.\n");
    189 				usage(program_name, EXIT_FAILURE);
    190 			}
    191 			break;
    192 
    193 		case 'I':
    194 			ifindex = if_nametoindex(optarg);
    195 			if (ifindex == 0) {
    196 				fprintf(stderr,
    197 					"specified interface is incorrect\n");
    198 				usage(program_name, EXIT_FAILURE);
    199 			}
    200 			break;
    201 
    202 		case 'p':
    203 			mcast_prefix = strdup(optarg);
    204 			break;
    205 
    206 		case 'F':
    207 			if (strncmp(optarg, "exclude", 8) == 0)
    208 				fmode = MCAST_EXCLUDE;
    209 			else if (strncmp(optarg, "include", 8) == 0)
    210 				fmode = MCAST_INCLUDE;
    211 			else {
    212 				fprintf(stderr,
    213 					"specified filter mode is incorrect\n");
    214 				usage(program_name, EXIT_FAILURE);
    215 			}
    216 			break;
    217 
    218 		case 'l':
    219 			join_leave_times = strtoul(optarg, NULL, 0);
    220 			break;
    221 
    222 		case 'i':
    223 			if (strtotimespec(optarg, &interval)) {
    224 				fprintf(stderr,
    225 					"Interval is something wrong\n");
    226 				usage(program_name, EXIT_FAILURE);
    227 			}
    228 			break;
    229 
    230 		case 'a':
    231 			mcast_addr = strdup(optarg);
    232 			break;
    233 
    234 		case 's':
    235 			saddrs = strdup(optarg);
    236 			if (saddrs == NULL)
    237 				fatal_error("strdup()");
    238 			break;
    239 
    240 		case 'n':
    241 			opt_ul = strtoul(optarg, NULL, 0);
    242 			if (opt_ul > 255 * 254) {
    243 				fprintf(stderr,
    244 					"The number of group shoud be less than %u\n",
    245 					255 * 254);
    246 				usage(program_name, EXIT_FAILURE);
    247 			}
    248 			num_group = opt_ul;
    249 			break;
    250 
    251 		case 'm':
    252 			is_multi_socket = 1;
    253 			break;
    254 
    255 		case 'd':
    256 			debug = 1;
    257 			break;
    258 
    259 		case 'h':
    260 			usage(program_name, EXIT_SUCCESS);
    261 			break;
    262 
    263 		default:
    264 			usage(program_name, EXIT_FAILURE);
    265 		}
    266 	}
    267 
    268 	if (ifindex == 0) {
    269 		fprintf(stderr, "specified interface seems incorrect\n");
    270 		usage(program_name, EXIT_FAILURE);
    271 	}
    272 
    273 	if (saddrs) {
    274 		if (fmode != MCAST_EXCLUDE && fmode != MCAST_INCLUDE) {
    275 			fprintf(stderr, "filter mode is wrong\n");
    276 			usage(program_name, EXIT_FAILURE);
    277 		}
    278 	}
    279 }
    280 
    281 /*
    282  * Function: join_group()
    283  *
    284  * Description:
    285  *  This function make sockets to join the groups
    286  *
    287  * Return value:
    288  *  None
    289  */
    290 void join_group(void)
    291 {
    292 	int sd;			/* socket file descriptor */
    293 	int *sock_array;	/* socket descriptor array */
    294 	size_t num_sock;	/* number of the socket */
    295 	char maddr[ADDR_STR_MAXSIZE];	/* multicast address in string */
    296 	int idx;
    297 	struct addrinfo *maddr_info;
    298 	struct group_req *grp_info;
    299 	struct group_filter *gsf;
    300 
    301 	if (!is_multi_socket)
    302 		num_sock = 1;
    303 	else
    304 		num_sock = num_group;
    305 
    306 	/* Allocate socket array */
    307 	sock_array = calloc(num_sock, sizeof(int));
    308 	if (sock_array == NULL)
    309 		fatal_error("calloc()");
    310 
    311 	for (idx = 0; idx < num_sock; idx++) {
    312 		sock_array[idx] = socket(family, SOCK_DGRAM, IPPROTO_UDP);
    313 
    314 		if (sock_array[idx] < 0) {
    315 			if (idx < OPEN_SOCK_MIN)
    316 				fatal_error("socket()");
    317 			else {
    318 				int j;	/* Closed some sockets for daemon() */
    319 				for (j = 0; j < OPEN_SOCK_MIN; j++)
    320 					close(sock_array[idx - 1 - j]);
    321 				num_group = idx - j - 1;
    322 				break;
    323 			}
    324 		}
    325 
    326 		if (!is_multi_socket)
    327 			maximize_sockbuf(sock_array[idx]);
    328 	}
    329 
    330 	sd = sock_array[0];
    331 	if (mcast_addr) {
    332 		strncpy(maddr, mcast_addr, ADDR_STR_MAXSIZE);
    333 		if (debug)
    334 			fprintf(stderr, "multicast address is %s\n", maddr);
    335 	}
    336 
    337 	for (idx = 0; idx < num_group; idx++) {
    338 		if (is_multi_socket)
    339 			sd = sock_array[idx];
    340 
    341 		if (debug)
    342 			fprintf(stderr, "socket: %d\n", sd);
    343 
    344 		if (mcast_prefix) {
    345 			switch (family) {
    346 			case PF_INET:
    347 				{
    348 					unsigned int x, y;
    349 					x = idx / 254;
    350 					y = idx % 254 + 1;
    351 					sprintf(maddr, "%s.%d.%d", mcast_prefix,
    352 						x, y);
    353 				}
    354 				break;
    355 
    356 			case PF_INET6:
    357 				sprintf(maddr, "%s:%x", mcast_prefix, idx + 1);
    358 				break;
    359 			}
    360 
    361 			if (debug)
    362 				fprintf(stderr, "multicast address is %s\n",
    363 					maddr);
    364 		}
    365 
    366 		maddr_info = get_maddrinfo(family, maddr, NULL);
    367 
    368 		grp_info = create_group_info(ifindex, maddr_info);
    369 		if (setsockopt(sd, level, MCAST_JOIN_GROUP, grp_info,
    370 			       sizeof(struct group_req)) == -1) {
    371 			if (idx == 0)
    372 				fatal_error("setsockopt(): Join no group");
    373 			else {
    374 				num_group--;
    375 				free(grp_info);
    376 				freeaddrinfo(maddr_info);
    377 				break;
    378 			}
    379 			free(grp_info);
    380 		}
    381 
    382 		if (saddrs) {
    383 			gsf =
    384 			    create_source_filter(ifindex, maddr_info, fmode,
    385 						 saddrs);
    386 			if (setsockopt
    387 			    (sd, level, MCAST_MSFILTER, gsf,
    388 			     GROUP_FILTER_SIZE(gsf->gf_numsrc)) == -1) {
    389 				if (idx == 0)
    390 					fatal_error
    391 					    ("setsockopt(): Add no group filter");
    392 				else {
    393 					num_group--;
    394 					free(gsf);
    395 					freeaddrinfo(maddr_info);
    396 					break;
    397 				}
    398 				free(gsf);
    399 			}
    400 		}
    401 
    402 		freeaddrinfo(maddr_info);
    403 	}
    404 
    405 	fprintf(stdout, "%zu groups\n", num_group);
    406 	fflush(stdout);
    407 
    408 	/* Become a daemon for the next step in shell script */
    409 	if (daemon(0, 0) < 0)
    410 		fatal_error("daemon()");
    411 
    412 	/* Waiting for SIGHUP */
    413 	handler.sa_handler = set_signal_flag;
    414 	handler.sa_flags = 0;
    415 	if (sigfillset(&handler.sa_mask) < 0)
    416 		fatal_error("sigfillset()");
    417 	if (sigaction(SIGHUP, &handler, NULL) < 0)
    418 		fatal_error("sigfillset()");
    419 
    420 	for (;;)
    421 		if (catch_sighup)
    422 			break;
    423 }
    424 
    425 /*
    426  * Function: join_leave_group()
    427  *
    428  * Description:
    429  *  This function make sockets to join the groups then leave it
    430  *
    431  * Return value:
    432  *  None
    433  */
    434 void join_leave_group(void)
    435 {
    436 	int sd;			/* socket file descriptor */
    437 	struct addrinfo *maddr_info;
    438 	struct group_req *grp_info;
    439 	struct group_filter *gsf;
    440 	size_t cnt;
    441 
    442 	sd = socket(family, SOCK_DGRAM, IPPROTO_UDP);
    443 	if (sd < 0)
    444 		fatal_error("socket()");
    445 
    446 	maddr_info = get_maddrinfo(family, mcast_addr, NULL);
    447 	grp_info = create_group_info(ifindex, maddr_info);
    448 	if (saddrs)
    449 		gsf = create_source_filter(ifindex, maddr_info, fmode, saddrs);
    450 	else
    451 		gsf = NULL;
    452 
    453 	/* Waiting for SIGHUP */
    454 	handler.sa_handler = set_signal_flag;
    455 	handler.sa_flags = 0;
    456 	if (sigfillset(&handler.sa_mask) < 0)
    457 		fatal_error("sigfillset()");
    458 	if (sigaction(SIGHUP, &handler, NULL) < 0)
    459 		fatal_error("sigfillset()");
    460 
    461 	for (cnt = 0; cnt < join_leave_times; cnt++) {
    462 		/* Join */
    463 		if (setsockopt(sd, level, MCAST_JOIN_GROUP, grp_info,
    464 			       sizeof(struct group_req)) == -1)
    465 			fatal_error("setsockopt(): Failed to join a group");
    466 
    467 		if (gsf)
    468 			if (setsockopt(sd, level, MCAST_MSFILTER, gsf,
    469 				       GROUP_FILTER_SIZE(gsf->gf_numsrc)) == -1)
    470 				fatal_error
    471 				    ("setsockopt(): Failed to add a group filter");
    472 
    473 		nanosleep(&interval, NULL);
    474 
    475 		/* Leave */
    476 		if (setsockopt(sd, level, MCAST_LEAVE_GROUP, grp_info,
    477 			       sizeof(struct group_req)) == -1)
    478 			fatal_error("setsockopt(): Failed to leave a group");
    479 
    480 		nanosleep(&interval, NULL);
    481 
    482 		if (catch_sighup)
    483 			break;
    484 	}
    485 
    486 	free(grp_info);
    487 	if (gsf)
    488 		free(gsf);
    489 	freeaddrinfo(maddr_info);
    490 }
    491 
    492 /*
    493  *
    494  *  Function: main()
    495  *
    496  */
    497 int main(int argc, char *argv[])
    498 {
    499 	debug = 0;
    500 	program_name = strdup(argv[0]);
    501 
    502 	parse_options(argc, argv);
    503 
    504 	if (!join_leave_times) {
    505 		if (mcast_prefix == NULL && mcast_addr == NULL) {
    506 			fprintf(stderr, "multicast address is not specified\n");
    507 			usage(program_name, EXIT_FAILURE);
    508 		}
    509 		join_group();
    510 	} else {
    511 		if (mcast_addr == NULL) {
    512 			fprintf(stderr, "multicast address is not specified\n");
    513 			usage(program_name, EXIT_FAILURE);
    514 		}
    515 		join_leave_group();
    516 	}
    517 
    518 	exit(EXIT_SUCCESS);
    519 }
    520