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-igmp_querier.c 24 * 25 * Description: 26 * This utiltity sends IGMP queries. 27 * (General Query, Multicast Address Specific Query 28 * or Multicast Address and Source Specific Query) 29 * 30 * Author: 31 * Mitsuru Chinen <mitch (at) jp.ibm.com> 32 * 33 * History: 34 * Apr 24 2006 - Created (Mitsuru Chinen) 35 *---------------------------------------------------------------------------*/ 36 37 /* 38 * Header Files 39 */ 40 #include <stdio.h> 41 #include <stdlib.h> 42 #include <string.h> 43 #include <errno.h> 44 #include <netdb.h> 45 #include <signal.h> 46 #include <time.h> 47 #include <unistd.h> 48 #include <arpa/inet.h> 49 #include <net/if.h> 50 #include <netinet/in.h> 51 #include <netinet/igmp.h> 52 #include <sys/ioctl.h> 53 #include <sys/socket.h> 54 #include <sys/types.h> 55 56 #include "ns-mcast.h" 57 #include "ns-traffic.h" 58 59 /* 60 * Structure Definitions 61 */ 62 struct igmp_info { 63 uint32_t ifindex; 64 struct igmpv3_query *query; 65 double timeout; 66 struct timespec interval; 67 }; 68 69 /* 70 * Gloval variables 71 */ 72 char *program_name; /* program name */ 73 struct sigaction handler; /* Behavior for a signal */ 74 int catch_sighup; /* When catch the SIGHUP, set to non-zero */ 75 76 /* 77 * Function: usage() 78 * 79 * Descripton: 80 * Print the usage of this program. Then, terminate this program with 81 * the specified exit value. 82 * 83 * Argument: 84 * exit_value: exit value 85 * 86 * Return value: 87 * This function does not return. 88 */ 89 void usage(char *program_name, int exit_value) 90 { 91 FILE *stream = stdout; /* stream where the usage is output */ 92 93 if (exit_value == EXIT_FAILURE) 94 stream = stderr; 95 96 fprintf(stream, "%s [OPTION]\n" 97 "\t-I ifname\tname of listening interface\n" 98 "\t-m addr\tmulticast address\n" 99 "\t-s addrs\tcomma separated array of Source Addresses\n" 100 "\t-r value\tMax Resp Code\n" 101 "\t-i value\tinterval [nanosec]\n" 102 "\t-t value\ttimeout [sec]\n" 103 "\t-o\t\tsend only one query\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 */ 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: create_query() 143 * 144 * Description: 145 * This function create a igmpv3 query information. 146 * This function allocates memory to store the information. 147 * 148 * Argument: 149 * code: Max Resp Code 150 * maddr: multicast address 151 * saddrs: comma separated array of the source addresses 152 * 153 * Return value: 154 * pointer to allocated igmpv3_query structure 155 */ 156 struct igmpv3_query *create_query(uint8_t code, char *maddr, char *saddrs) 157 { 158 struct igmpv3_query *query; /* pointer to igmpv3_query structure */ 159 uint16_t numsrc; /* number of source address */ 160 size_t query_size; /* size of igmpv3_query */ 161 struct in_addr ip; 162 uint32_t idx; 163 char *sp, *ep; 164 165 /* calculate the number of source address */ 166 if (saddrs == NULL) { 167 numsrc = 0; 168 } else { 169 numsrc = 1; 170 for (sp = saddrs; *sp != '\0'; sp++) 171 if (*sp == ',') 172 numsrc++; 173 } 174 if (debug) 175 fprintf(stderr, "number of source address is %u\n", numsrc); 176 177 /* allocate memory for igmpv3_query structure */ 178 query_size = MY_IGMPV3_QUERY_SIZE(numsrc); 179 query = (struct igmpv3_query *)calloc(1, query_size); 180 if (query == NULL) 181 fatal_error("calloc()"); 182 183 /* substitute paramaters */ 184 query->type = IGMP_HOST_MEMBERSHIP_QUERY; 185 query->code = code; 186 query->csum = 0; /* Calculate later */ 187 query->resv = 0; 188 query->suppress = 0; 189 query->qrv = 0; 190 query->qqic = 0; 191 query->nsrcs = htons(numsrc); 192 193 /* substitute multicast address */ 194 if (maddr == NULL) { 195 query->group = htonl(INADDR_ANY); 196 } else { 197 if (inet_pton(AF_INET, maddr, &ip) <= 0) { 198 fprintf(stderr, 199 "multicast address is something wrong\n"); 200 return NULL; 201 } 202 query->group = ip.s_addr; 203 } 204 205 /* substitute source addresses */ 206 sp = saddrs; 207 for (idx = 0; idx < numsrc; idx++) { 208 ep = strchr(sp, ','); 209 if (ep != NULL) 210 *ep = '\0'; 211 if (debug) 212 fprintf(stderr, "source address[%u]: %s\n", idx, sp); 213 214 if (inet_pton(AF_INET, sp, &ip) <= 0) { 215 fprintf(stderr, 216 "source address list is something wrong\n"); 217 return NULL; 218 } 219 query->srcs[idx] = ip.s_addr; 220 sp = ep + 1; 221 } 222 223 /* Calculate checksum */ 224 query->csum = calc_checksum((u_int16_t *) query, query_size); 225 226 return query; 227 } 228 229 /* 230 * Function: parse_options() 231 * 232 * Description: 233 * This function parse the options 234 * 235 * Argument: 236 * argc: the number of argument 237 * argv: arguments 238 * info_p: pointer to data of querier information 239 * bg_p: pointer to the flag of working in backgrond 240 * 241 * Return value: 242 * None 243 */ 244 void parse_options(int argc, char *argv[], struct igmp_info *info_p, int *bg_p) 245 { 246 int optc; /* option */ 247 unsigned long opt_ul; /* option value in unsigned long */ 248 double opt_d; /* option value in double */ 249 uint8_t max_resp; /* Max Resp Code */ 250 char *maddr; /* multicast address */ 251 char *saddrs; /* comma separated array of source addresses */ 252 253 max_resp = IGMP_MAX_HOST_REPORT_DELAY; 254 maddr = NULL; 255 saddrs = NULL; 256 257 while ((optc = getopt(argc, argv, "I:m:s:r:t:i:obdh")) != EOF) { 258 switch (optc) { 259 case 'I': 260 info_p->ifindex = if_nametoindex(optarg); 261 if (info_p->ifindex == 0) { 262 fprintf(stderr, 263 "specified interface is incorrect\n"); 264 usage(program_name, EXIT_FAILURE); 265 } 266 break; 267 268 case 'm': 269 maddr = strdup(optarg); 270 if (maddr == NULL) 271 fatal_error("strdup()"); 272 break; 273 274 case 's': 275 saddrs = strdup(optarg); 276 if (saddrs == NULL) 277 fatal_error("strdup()"); 278 break; 279 280 case 'r': 281 opt_ul = strtoul(optarg, NULL, 0); 282 if (opt_ul > 255) { 283 fprintf(stderr, 284 "Max Resp Code should be less then 256\n"); 285 usage(program_name, EXIT_FAILURE); 286 } 287 max_resp = opt_ul; 288 break; 289 290 case 't': 291 opt_d = strtod(optarg, NULL); 292 if (opt_d < 0.0) { 293 fprintf(stderr, 294 "Timeout should be positive value\n"); 295 usage(program_name, EXIT_FAILURE); 296 } 297 info_p->timeout = opt_d; 298 break; 299 300 case 'i': 301 if (strtotimespec(optarg, &info_p->interval)) { 302 fprintf(stderr, 303 "Interval is something wrong\n"); 304 usage(program_name, EXIT_FAILURE); 305 } 306 break; 307 308 case 'o': 309 info_p->timeout = -1.0; 310 break; 311 312 case 'b': 313 *bg_p = 1; 314 break; 315 316 case 'd': 317 debug = 1; 318 break; 319 320 case 'h': 321 usage(program_name, EXIT_SUCCESS); 322 break; 323 324 default: 325 usage(program_name, EXIT_FAILURE); 326 } 327 } 328 329 if (info_p->ifindex == 0) { 330 fprintf(stderr, "specified interface seems incorrect\n"); 331 usage(program_name, EXIT_FAILURE); 332 } 333 334 if ((info_p->query = create_query(max_resp, maddr, saddrs)) == NULL) 335 usage(program_name, EXIT_FAILURE); 336 337 free(maddr); 338 free(saddrs); 339 } 340 341 /* 342 * Function: create_socket() 343 * 344 * Description: 345 * This function creates a socket to send 346 * 347 * Argument: 348 * info_p: pointer to data of igmp query information 349 * 350 * Return value: 351 * file descriptor referencing the socket 352 */ 353 int create_socket(struct igmp_info *info_p) 354 { 355 int sd; /* socket file descriptor */ 356 int on; 357 unsigned char opt[4] = { 0x94, 0x04, 0x00, 0x00 }; /* Router Alert */ 358 struct ip_mreqn mcast_req, *req_p = &mcast_req; 359 360 /* Create a socket */ 361 sd = socket(AF_INET, SOCK_RAW, IPPROTO_IGMP); 362 if (sd < 0) 363 fatal_error("socket()"); 364 365 /* Enable to reuse the socket */ 366 on = 1; 367 if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(int))) 368 fatal_error("setsockopt(): enable to reuse the socket"); 369 370 /* Add router alert option */ 371 if (setsockopt(sd, IPPROTO_IP, IP_OPTIONS, opt, sizeof(opt))) 372 fatal_error("setsockopt(): socket options"); 373 374 /* Specify the interface for outgoing datagrams */ 375 req_p->imr_multiaddr.s_addr = info_p->query->group; 376 req_p->imr_address.s_addr = htonl(INADDR_ANY); 377 req_p->imr_ifindex = info_p->ifindex; 378 if (setsockopt(sd, IPPROTO_IP, IP_MULTICAST_IF, 379 req_p, sizeof(struct ip_mreqn))) { 380 fatal_error("setsockopt(): specify the interface"); 381 } 382 383 return sd; 384 } 385 386 /* 387 * Function: send_query() 388 * 389 * Description: 390 * This function sends IGMP query 391 * 392 * Argument: 393 * info_p: pointer to data of igmp query information 394 * 395 * Return value: 396 * None 397 */ 398 void send_query(struct igmp_info *info_p) 399 { 400 int sd; 401 int retval; 402 double start_time; 403 struct sockaddr_in to; 404 size_t query_size; 405 406 /* Set singal hander for SIGHUP */ 407 handler.sa_handler = set_signal_flag; 408 handler.sa_flags = 0; 409 if (sigfillset(&handler.sa_mask) < 0) 410 fatal_error("sigfillset()"); 411 if (sigaction(SIGHUP, &handler, NULL) < 0) 412 fatal_error("sigaction()"); 413 414 /* Specify multicast address to send */ 415 to.sin_family = AF_INET; 416 to.sin_port = IPPROTO_IGMP; 417 if (info_p->query->group == htonl(INADDR_ANY)) 418 to.sin_addr.s_addr = IGMP_ALL_HOSTS; 419 else 420 to.sin_addr.s_addr = info_p->query->group; 421 422 /* Create a socket */ 423 sd = create_socket(info_p); 424 425 /* loop for sending queries */ 426 start_time = time(NULL); 427 query_size = MY_IGMPV3_QUERY_SIZE(ntohs(info_p->query->nsrcs)); 428 if (debug) 429 fprintf(stderr, "query size is %zu\n", query_size); 430 431 for (;;) { 432 retval = sendto(sd, info_p->query, query_size, 0, 433 (struct sockaddr *)&to, 434 sizeof(struct sockaddr_in)); 435 if (retval != query_size) { 436 if (catch_sighup) 437 break; 438 else 439 fatal_error("sendto()"); 440 } 441 442 /* Check timeout: 443 If timeout value is negative only send one datagram */ 444 if (info_p->timeout) 445 if (info_p->timeout < difftime(time(NULL), start_time)) 446 break; 447 448 /* Wait in specified interval */ 449 nanosleep(&info_p->interval, NULL); 450 451 /* catch SIGHUP */ 452 if (catch_sighup) 453 break; 454 455 } 456 457 close(sd); 458 } 459 460 /* 461 * 462 * Function: main() 463 * 464 */ 465 int main(int argc, char *argv[]) 466 { 467 struct igmp_info mcast_rcv; 468 int background = 0; 469 470 debug = 0; 471 program_name = strdup(argv[0]); 472 473 memset(&mcast_rcv, '\0', sizeof(struct igmp_info)); 474 parse_options(argc, argv, &mcast_rcv, &background); 475 476 if (background) /* Work in the background */ 477 if (daemon(0, 0) < 0) 478 fatal_error("daemon()"); 479 480 send_query(&mcast_rcv); 481 482 exit(EXIT_SUCCESS); 483 } 484