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 */ 58struct 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 */ 68char *program_name; /* program name */ 69struct sigaction handler; /* Behavior for a signal */ 70int 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 */ 85void 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 */ 122void 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 */ 156void 157parse_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 */ 302int 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 */ 367void 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 */ 432int 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