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@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 */
usage(char * program_name,int exit_value)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 */
set_signal_flag(int type)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 */
parse_options(int argc,char * argv[])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 */
join_group(void)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 */
join_leave_group(void)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 */
main(int argc,char * argv[])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