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_receiver.c
24 *
25 * Description:
26 * This is a multicast UDP datagram receiver
27 *
28 * Author:
29 * Mitsuru Chinen <mitch@jp.ibm.com>
30 *
31 * History:
32 * Apr 19 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 /*
56 * Structure Definitions
57 */
58 struct mcast_rcv_info {
59 struct addrinfo *mainfo;
60 struct group_req *greq;
61 struct group_filter *gsf;
62 double timeout;
63 };
64
65 /*
66 * Gloval variables
67 */
68 char *program_name; /* program name */
69 struct sigaction handler; /* Behavior for a signal */
70 int catch_sighup; /* When catch the SIGHUP, set to non-zero */
71
72 /*
73 * Function: usage()
74 *
75 * Descripton:
76 * Print the usage of this program. Then, terminate this program with
77 * the specified exit value.
78 *
79 * Argument:
80 * exit_value: exit value
81 *
82 * Return value:
83 * This function does not return.
84 */
usage(char * program_name,int exit_value)85 void usage(char *program_name, int exit_value)
86 {
87 FILE *stream = stdout; /* stream where the usage is output */
88
89 if (exit_value == EXIT_FAILURE)
90 stream = stderr;
91
92 fprintf(stream, "%s [OPTION]\n"
93 "\t-f num\ttprotocol family\n"
94 "\t\t 4 : IPv4\n"
95 "\t\t 6 : IPv6\n"
96 "\t-I ifname\tname of listening interface\n"
97 "\t-m addr\tmulticast address\n"
98 "\t-F mode\tfilter mode\n"
99 "\t\t include : include mode\n"
100 "\t\t exclude : exclude mode\n"
101 "\t-s addrs\tcomma separated array of Source Addresses\n"
102 "\t-p num\tport number\n"
103 "\t-t value\ttimeout [sec]\n"
104 "\t-b\t\twork in the background\n"
105 "\t-d\t\tdisplay debug informations\n"
106 "\t-h\t\tdisplay this usage\n", program_name);
107 exit(exit_value);
108 }
109
110 /*
111 * Function: set_signal_flag()
112 *
113 * Description:
114 * This function sets global variables accordig to signal
115 *
116 * Argument:
117 * type: type of signal
118 *
119 * Return value:
120 * None
121 */
set_signal_flag(int type)122 void set_signal_flag(int type)
123 {
124 if (debug)
125 fprintf(stderr, "Catch signal. type is %d\n", type);
126
127 switch (type) {
128 case SIGHUP:
129 catch_sighup = 1;
130 handler.sa_handler = SIG_IGN;
131 if (sigaction(type, &handler, NULL) < 0)
132 fatal_error("sigaction()");
133 break;
134
135 default:
136 fprintf(stderr, "Unexpected signal (%d) is caught\n", type);
137 exit(EXIT_FAILURE);
138 }
139 }
140
141 /*
142 * Function: parse_options()
143 *
144 * Description:
145 * This function parse the options
146 *
147 * Argument:
148 * argc: the number of argument
149 * argv: arguments
150 * info_p: pointer to data of multicast receiver information
151 * bg_p: pointer to the flag of working in backgrond
152 *
153 * Return value:
154 * None
155 */
156 void
parse_options(int argc,char * argv[],struct mcast_rcv_info * info_p,int * bg_p)157 parse_options(int argc, char *argv[], struct mcast_rcv_info *info_p, int *bg_p)
158 {
159 int optc; /* option */
160 unsigned long opt_ul; /* option value in unsigned long */
161 double opt_d; /* option value in double */
162 uint32_t ifindex = 0; /* interface index where listening multicast */
163 sa_family_t family = AF_UNSPEC; /* protocol family */
164 char *maddr; /* multicast address */
165 uint32_t fmode = 0; /* filter mode */
166 char *saddrs; /* comma separated array of source addresses */
167 char *portnum; /* listen port number in character string */
168
169 maddr = NULL;
170 saddrs = NULL;
171 portnum = NULL;
172
173 while ((optc = getopt(argc, argv, "f:I:m:F:s:p:t:bdh")) != EOF) {
174 switch (optc) {
175 case 'f':
176 if (optarg[0] == '4')
177 family = PF_INET; /* IPv4 */
178 else if (optarg[0] == '6')
179 family = PF_INET6; /* IPv6 */
180 else {
181 fprintf(stderr,
182 "protocol family should be 4 or 6.\n");
183 usage(program_name, EXIT_FAILURE);
184 }
185 break;
186
187 case 'I':
188 ifindex = if_nametoindex(optarg);
189 if (ifindex == 0) {
190 fprintf(stderr,
191 "specified interface is incorrect\n");
192 usage(program_name, EXIT_FAILURE);
193 }
194 break;
195
196 case 'm':
197 maddr = strdup(optarg);
198 if (maddr == NULL)
199 fatal_error("strdup()");
200 break;
201
202 case 'F':
203 if (strncmp(optarg, "exclude", 8) == 0)
204 fmode = MCAST_EXCLUDE;
205 else if (strncmp(optarg, "include", 8) == 0)
206 fmode = MCAST_INCLUDE;
207 else {
208 fprintf(stderr,
209 "specified filter mode is incorrect\n");
210 usage(program_name, EXIT_FAILURE);
211 }
212 break;
213
214 case 's':
215 saddrs = strdup(optarg);
216 if (saddrs == NULL)
217 fatal_error("strdup()");
218 break;
219
220 case 'p':
221 opt_ul = strtoul(optarg, NULL, 0);
222 if (opt_ul < PORTNUMMIN || PORTNUMMAX < opt_ul) {
223 fprintf(stderr,
224 "The range of port is from %u to %u\n",
225 PORTNUMMIN, PORTNUMMAX);
226 usage(program_name, EXIT_FAILURE);
227 }
228 portnum = strdup(optarg);
229 break;
230
231 case 't':
232 opt_d = strtod(optarg, NULL);
233 if (opt_d < 0.0) {
234 fprintf(stderr,
235 "Timeout should be positive value\n");
236 usage(program_name, EXIT_FAILURE);
237 }
238 info_p->timeout = opt_d;
239 break;
240
241 case 'b':
242 *bg_p = 1;
243 break;
244
245 case 'd':
246 debug = 1;
247 break;
248
249 case 'h':
250 usage(program_name, EXIT_SUCCESS);
251 break;
252
253 default:
254 usage(program_name, EXIT_FAILURE);
255 }
256 }
257
258 if (ifindex == 0) {
259 fprintf(stderr, "specified interface seems incorrect\n");
260 usage(program_name, EXIT_FAILURE);
261 }
262
263 if (maddr == NULL) {
264 fprintf(stderr, "multicast address is not specified\n");
265 usage(program_name, EXIT_FAILURE);
266 }
267
268 if (portnum == NULL) {
269 fprintf(stderr, "listening port number is not specified\n");
270 usage(program_name, EXIT_FAILURE);
271 }
272
273 info_p->mainfo = get_maddrinfo(family, maddr, portnum);
274 info_p->greq = create_group_info(ifindex, info_p->mainfo);
275 if (saddrs) {
276 if (fmode != MCAST_EXCLUDE && fmode != MCAST_INCLUDE) {
277 fprintf(stderr, "filter mode is wrong\n");
278 usage(program_name, EXIT_FAILURE);
279 }
280 info_p->gsf =
281 create_source_filter(ifindex, info_p->mainfo, fmode,
282 saddrs);
283 }
284
285 free(maddr);
286 free(saddrs);
287 free(portnum);
288 }
289
290 /*
291 * Function: create_mcast_socket()
292 *
293 * Description:
294 * This function creates a socket to receive multicast datagrams
295 *
296 * Argument:
297 * info_p: pointer to data of multicast receiver information
298 *
299 * Return value:
300 * file descriptor referencing the socket
301 */
create_mcast_socket(struct mcast_rcv_info * info_p)302 int create_mcast_socket(struct mcast_rcv_info *info_p)
303 {
304 int sd; /* socket file descriptor */
305 int level; /* protocol levels */
306 int on; /* flag for setsockopt */
307
308 switch (info_p->mainfo->ai_family) {
309 case PF_INET:
310 level = IPPROTO_IP;
311 break;
312 case PF_INET6:
313 level = IPPROTO_IPV6;
314 break;
315 default:
316 level = 0;
317 fprintf(stderr, "Unknown protocol level %d\n", level);
318 exit(EXIT_FAILURE);
319 break;
320 }
321
322 /* Create a socket */
323 sd = socket(info_p->mainfo->ai_family, info_p->mainfo->ai_socktype,
324 info_p->mainfo->ai_protocol);
325 if (sd < 0)
326 fatal_error("socket()");
327
328 /* Bind to the multicast address */
329 if (bind(sd, info_p->mainfo->ai_addr, info_p->mainfo->ai_addrlen) < 0)
330 fatal_error("bind()");
331
332 /* Enable to reuse the socket */
333 on = 1;
334 if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(int)))
335 fatal_error("setsockopt(): failed to set reuse the socket");
336
337 /* Join the multicast group */
338 if (setsockopt
339 (sd, level, MCAST_JOIN_GROUP, info_p->greq,
340 sizeof(struct group_req)))
341 fatal_error("setsockopt(): failed to join the multicast group");
342
343 /* Apply the source filter */
344 if (info_p->gsf) {
345 if (setsockopt
346 (sd, level, MCAST_MSFILTER, info_p->gsf,
347 GROUP_FILTER_SIZE(info_p->gsf->gf_numsrc)))
348 fatal_error
349 ("setsockopt(): failed to apply the source filter");
350 }
351
352 return sd;
353 }
354
355 /*
356 * Function: receive_mcast()
357 *
358 * Description:
359 * This function receives multicast datagarms
360 *
361 * Argument:
362 * info_p: pointer to data of multicast receiver information
363 *
364 * Return value:
365 * None
366 */
receive_mcast(struct mcast_rcv_info * info_p)367 void receive_mcast(struct mcast_rcv_info *info_p)
368 {
369 int sd;
370 char *msgbuf; /* Pointer to the message */
371 size_t msgbuf_size; /* size of msgbuf */
372 ssize_t msglen; /* the length of message */
373 socklen_t optlen; /* size of the result parameter */
374 double start_time; /* start time when receiving datagrams */
375
376 /* Create a socket */
377 sd = create_mcast_socket(info_p);
378
379 /* Allocate a buffer to store the message */
380 optlen = sizeof(msgbuf_size);
381 if (getsockopt(sd, SOL_SOCKET, SO_RCVBUF, &msgbuf_size, &optlen) < 0) {
382 perror("getsockopt()");
383 close(sd);
384 exit(EXIT_FAILURE);
385 }
386 msgbuf = malloc(msgbuf_size + 1);
387 if (msgbuf == NULL) {
388 fprintf(stderr, "malloc() is failed.\n");
389 close(sd);
390 exit(EXIT_FAILURE);
391 }
392
393 /* Set singal hander for SIGHUP */
394 handler.sa_handler = set_signal_flag;
395 handler.sa_flags = 0;
396 if (sigfillset(&handler.sa_mask) < 0)
397 fatal_error("sigfillset()");
398 if (sigaction(SIGHUP, &handler, NULL) < 0)
399 fatal_error("sigfillset()");
400
401 /* Receive the message */
402 start_time = time(NULL);
403 for (;;) {
404 struct sockaddr_storage addr;
405 socklen_t addrlen;
406
407 addrlen = sizeof(addr);
408 msglen = recvfrom(sd, msgbuf, msgbuf_size, MSG_DONTWAIT,
409 (struct sockaddr *)&addr, &addrlen);
410 if (msglen < 0) {
411 if (errno != EAGAIN)
412 fatal_error("recvfrom()");
413 } else if (debug)
414 fprintf(stderr, "received %zd byte message\n", msglen);
415
416 if (info_p->timeout)
417 if (info_p->timeout < difftime(time(NULL), start_time))
418 break;
419
420 if (catch_sighup) /* catch SIGHUP */
421 break;
422 }
423
424 close(sd);
425 }
426
427 /*
428 *
429 * Function: main()
430 *
431 */
main(int argc,char * argv[])432 int main(int argc, char *argv[])
433 {
434 struct mcast_rcv_info mcast_rcv;
435 int background = 0;
436
437 debug = 0;
438 program_name = strdup(argv[0]);
439
440 memset(&mcast_rcv, '\0', sizeof(struct mcast_rcv_info));
441 parse_options(argc, argv, &mcast_rcv, &background);
442
443 if (background) /* Work in the background */
444 if (daemon(0, 0) < 0)
445 fatal_error("daemon()");
446
447 receive_mcast(&mcast_rcv);
448
449 exit(EXIT_SUCCESS);
450 }
451