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