Annotation of embedaddon/pimd/igmp.c, revision 1.1
1.1 ! misho 1: /*
! 2: * Copyright (c) 1998-2001
! 3: * University of Southern California/Information Sciences Institute.
! 4: * All rights reserved.
! 5: *
! 6: * Redistribution and use in source and binary forms, with or without
! 7: * modification, are permitted provided that the following conditions
! 8: * are met:
! 9: * 1. Redistributions of source code must retain the above copyright
! 10: * notice, this list of conditions and the following disclaimer.
! 11: * 2. Redistributions in binary form must reproduce the above copyright
! 12: * notice, this list of conditions and the following disclaimer in the
! 13: * documentation and/or other materials provided with the distribution.
! 14: * 3. Neither the name of the project nor the names of its contributors
! 15: * may be used to endorse or promote products derived from this software
! 16: * without specific prior written permission.
! 17: *
! 18: * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
! 19: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
! 20: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
! 21: * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
! 22: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
! 23: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
! 24: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
! 25: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
! 26: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
! 27: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
! 28: * SUCH DAMAGE.
! 29: */
! 30: /*
! 31: * $Id: igmp.c,v 1.18 2002/09/26 00:59:29 pavlin Exp $
! 32: */
! 33: /*
! 34: * Part of this program has been derived from mrouted.
! 35: * The mrouted program is covered by the license in the accompanying file
! 36: * named "LICENSE.mrouted".
! 37: *
! 38: * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
! 39: * Leland Stanford Junior University.
! 40: *
! 41: */
! 42:
! 43: #include "defs.h"
! 44:
! 45: /*
! 46: * Exported variables.
! 47: */
! 48: char *igmp_recv_buf; /* input packet buffer */
! 49: char *igmp_send_buf; /* output packet buffer */
! 50: int igmp_socket; /* socket for all network I/O */
! 51: uint32_t allhosts_group; /* allhosts addr in net order */
! 52: uint32_t allrouters_group; /* All-Routers addr in net order */
! 53: uint32_t allreports_group; /* All IGMP routers in net order */
! 54:
! 55: #ifdef RAW_OUTPUT_IS_RAW
! 56: extern int curttl;
! 57: #endif /* RAW_OUTPUT_IS_RAW */
! 58:
! 59: /*
! 60: * Local functions definitions.
! 61: */
! 62: static void igmp_read (int i, fd_set *rfd);
! 63: static void accept_igmp (ssize_t recvlen);
! 64:
! 65:
! 66: /*
! 67: * Open and initialize the igmp socket, and fill in the non-changing
! 68: * IP header fields in the output packet buffer.
! 69: */
! 70: void init_igmp(void)
! 71: {
! 72: struct ip *ip;
! 73: char *router_alert;
! 74:
! 75: igmp_recv_buf = calloc(1, RECV_BUF_SIZE);
! 76: igmp_send_buf = calloc(1, SEND_BUF_SIZE);
! 77: if (!igmp_recv_buf || !igmp_send_buf)
! 78: logit(LOG_ERR, 0, "Ran out of memory in init_igmp()");
! 79:
! 80: if ((igmp_socket = socket(AF_INET, SOCK_RAW, IPPROTO_IGMP)) < 0)
! 81: logit(LOG_ERR, errno, "Failed creating IGMP socket in init_igmp()");
! 82:
! 83: k_hdr_include(igmp_socket, TRUE); /* include IP header when sending */
! 84: k_set_sndbuf(igmp_socket, SO_SEND_BUF_SIZE_MAX,
! 85: SO_SEND_BUF_SIZE_MIN); /* lots of output buffering */
! 86: k_set_rcvbuf(igmp_socket, SO_RECV_BUF_SIZE_MAX,
! 87: SO_RECV_BUF_SIZE_MIN); /* lots of input buffering */
! 88: k_set_ttl(igmp_socket, MINTTL); /* restrict multicasts to one hop */
! 89: k_set_loop(igmp_socket, FALSE); /* disable multicast loopback */
! 90:
! 91: ip = (struct ip *)igmp_send_buf;
! 92: memset(ip, 0, IP_IGMP_HEADER_LEN);
! 93: ip->ip_v = IPVERSION;
! 94: ip->ip_hl = IP_IGMP_HEADER_LEN >> 2;
! 95: ip->ip_tos = 0xc0; /* Internet Control */
! 96: ip->ip_id = 0; /* let kernel fill in */
! 97: ip->ip_off = 0;
! 98: ip->ip_ttl = MAXTTL; /* applies to unicasts only */
! 99: ip->ip_p = IPPROTO_IGMP;
! 100: ip->ip_sum = 0; /* let kernel fill in */
! 101:
! 102: /* Enable RFC2113 IP Router Alert. Per spec this is required to
! 103: * force certain routers/switches to inspect this frame. */
! 104: router_alert = igmp_send_buf + sizeof(struct ip);
! 105: router_alert[0] = IPOPT_RA;
! 106: router_alert[1] = 4;
! 107: router_alert[2] = 0;
! 108: router_alert[3] = 0;
! 109:
! 110: /* Everywhere in the daemon we use network-byte-order */
! 111: allhosts_group = htonl(INADDR_ALLHOSTS_GROUP);
! 112: allrouters_group = htonl(INADDR_ALLRTRS_GROUP);
! 113: allreports_group = htonl(INADDR_ALLRPTS_GROUP);
! 114:
! 115: if (register_input_handler(igmp_socket, igmp_read) < 0)
! 116: logit(LOG_ERR, 0, "Failed registering igmp_read() as an input handler in init_igmp()");
! 117: }
! 118:
! 119:
! 120: /* Read an IGMP message */
! 121: static void igmp_read(int i __attribute__((unused)), fd_set *rfd __attribute__((unused)))
! 122: {
! 123: ssize_t len;
! 124: socklen_t dummy = 0;
! 125:
! 126: while ((len = recvfrom(igmp_socket, igmp_recv_buf, RECV_BUF_SIZE, 0, NULL, &dummy)) < 0) {
! 127: if (errno == EINTR)
! 128: continue; /* Received signal, retry syscall. */
! 129:
! 130: logit(LOG_ERR, errno, "Failed recvfrom() in igmp_read()");
! 131: return;
! 132: }
! 133:
! 134: accept_igmp(len);
! 135: }
! 136:
! 137: /*
! 138: * Process a newly received IGMP packet that is sitting in the input
! 139: * packet buffer.
! 140: */
! 141: static void accept_igmp(ssize_t recvlen)
! 142: {
! 143: int ipdatalen, iphdrlen, igmpdatalen;
! 144: uint32_t src, dst, group;
! 145: struct ip *ip;
! 146: struct igmp *igmp;
! 147: int igmp_version = 3;
! 148:
! 149: if (recvlen < (ssize_t)sizeof(struct ip)) {
! 150: logit(LOG_WARNING, 0, "Received IGMP packet too short (%u bytes) for IP header", recvlen);
! 151: return;
! 152: }
! 153:
! 154: ip = (struct ip *)igmp_recv_buf;
! 155: src = ip->ip_src.s_addr;
! 156: dst = ip->ip_dst.s_addr;
! 157:
! 158: /* packets sent up from kernel to daemon have ip->ip_p = 0 */
! 159: if (ip->ip_p == 0) {
! 160: #if 0 /* XXX */
! 161: if (src == 0 || dst == 0)
! 162: logit(LOG_WARNING, 0, "Kernel request not accurate, src %s dst %s",
! 163: inet_fmt(src, s1, sizeof(s1)), inet_fmt(dst, s2, sizeof(s2)));
! 164: else
! 165: #endif
! 166: process_kernel_call();
! 167: return;
! 168: }
! 169:
! 170: iphdrlen = ip->ip_hl << 2;
! 171: #if 0
! 172: #ifdef HAVE_IP_HDRINCL_BSD_ORDER
! 173: #ifdef __NetBSD__
! 174: ipdatalen = ip->ip_len; /* The NetBSD kernel subtracts hlen for us, unfortunately. */
! 175: #else
! 176: ipdatalen = ip->ip_len - iphdrlen;
! 177: #endif
! 178: #else
! 179: ipdatalen = ntohs(ip->ip_len) - iphdrlen;
! 180: #endif
! 181: #else /* !0 */
! 182: ipdatalen = recvlen - iphdrlen;
! 183: #endif /* O */
! 184:
! 185: if (iphdrlen + ipdatalen != recvlen) {
! 186: logit(LOG_WARNING, 0, "Received packet from %s shorter (%u bytes) than hdr+data length (%u+%u)",
! 187: inet_fmt(src, s1, sizeof(s1)), recvlen, iphdrlen, ipdatalen);
! 188: return;
! 189: }
! 190:
! 191: igmp = (struct igmp *)(igmp_recv_buf + iphdrlen);
! 192: group = igmp->igmp_group.s_addr;
! 193: igmpdatalen = ipdatalen - IGMP_MINLEN;
! 194:
! 195: if (igmpdatalen < 0) {
! 196: logit(LOG_WARNING, 0, "Received IP data field too short (%u bytes) for IGMP, from %s",
! 197: ipdatalen, inet_fmt(src, s1, sizeof(s1)));
! 198: return;
! 199: }
! 200:
! 201: IF_DEBUG(DEBUG_IGMP)
! 202: logit(LOG_DEBUG, 0, "Received %s from %s to %s",
! 203: packet_kind(IPPROTO_IGMP, igmp->igmp_type, igmp->igmp_code),
! 204: inet_fmt(src, s1, sizeof(s1)), inet_fmt(dst, s2, sizeof(s2)));
! 205:
! 206: switch (igmp->igmp_type) {
! 207: case IGMP_MEMBERSHIP_QUERY:
! 208: /* RFC 3376:7.1 */
! 209: if (ipdatalen == 8) {
! 210: if (igmp->igmp_code == 0)
! 211: igmp_version = 1;
! 212: else
! 213: igmp_version = 2;
! 214: } else if (ipdatalen >= 12) {
! 215: igmp_version = 3;
! 216: } else {
! 217: logit(LOG_DEBUG, 0, "Received invalid IGMP Membership query: Max Resp Code = %d, length = %d",
! 218: igmp->igmp_code, ipdatalen);
! 219: }
! 220: accept_membership_query(src, dst, group, igmp->igmp_code, igmp_version);
! 221: return;
! 222:
! 223: case IGMP_V1_MEMBERSHIP_REPORT:
! 224: case IGMP_V2_MEMBERSHIP_REPORT:
! 225: accept_group_report(src, dst, group, igmp->igmp_type);
! 226: return;
! 227:
! 228: case IGMP_V2_LEAVE_GROUP:
! 229: accept_leave_message(src, dst, group);
! 230: return;
! 231:
! 232: case IGMP_V3_MEMBERSHIP_REPORT:
! 233: if (igmpdatalen < IGMP_V3_GROUP_RECORD_MIN_SIZE) {
! 234: logit(LOG_DEBUG, 0, "Too short IGMP v3 Membership report: igmpdatalen(%d) < MIN(%d)", igmpdatalen, IGMP_V3_GROUP_RECORD_MIN_SIZE);
! 235: return;
! 236: }
! 237: accept_membership_report(src, dst, (struct igmpv3_report *)(igmp_recv_buf + iphdrlen), recvlen - iphdrlen);
! 238: return;
! 239:
! 240: case IGMP_DVMRP:
! 241: /* XXX: TODO: most of the stuff below is not implemented. We are still
! 242: * only PIM router.
! 243: */
! 244: group = ntohl(group);
! 245:
! 246: switch (igmp->igmp_code) {
! 247: case DVMRP_PROBE:
! 248: dvmrp_accept_probe(src, dst, (uint8_t *)(igmp+1), igmpdatalen, group);
! 249: return;
! 250:
! 251: case DVMRP_REPORT:
! 252: dvmrp_accept_report(src, dst, (uint8_t *)(igmp+1), igmpdatalen, group);
! 253: return;
! 254:
! 255: case DVMRP_ASK_NEIGHBORS:
! 256: accept_neighbor_request(src, dst);
! 257: return;
! 258:
! 259: case DVMRP_ASK_NEIGHBORS2:
! 260: accept_neighbor_request2(src, dst);
! 261: return;
! 262:
! 263: case DVMRP_NEIGHBORS:
! 264: dvmrp_accept_neighbors(src, dst, (uint8_t *)(igmp+1), igmpdatalen, group);
! 265: return;
! 266:
! 267: case DVMRP_NEIGHBORS2:
! 268: dvmrp_accept_neighbors2(src, dst, (uint8_t *)(igmp+1), igmpdatalen, group);
! 269: return;
! 270:
! 271: case DVMRP_PRUNE:
! 272: dvmrp_accept_prune(src, dst, (uint8_t *)(igmp+1), igmpdatalen);
! 273: return;
! 274:
! 275: case DVMRP_GRAFT:
! 276: dvmrp_accept_graft(src, dst, (uint8_t *)(igmp+1), igmpdatalen);
! 277: return;
! 278:
! 279: case DVMRP_GRAFT_ACK:
! 280: dvmrp_accept_g_ack(src, dst, (uint8_t *)(igmp+1), igmpdatalen);
! 281: return;
! 282:
! 283: case DVMRP_INFO_REQUEST:
! 284: dvmrp_accept_info_request(src, dst, (uint8_t *)(igmp+1), igmpdatalen);
! 285: return;
! 286:
! 287: case DVMRP_INFO_REPLY:
! 288: dvmrp_accept_info_reply(src, dst, (uint8_t *)(igmp+1), igmpdatalen);
! 289: return;
! 290:
! 291: default:
! 292: logit(LOG_INFO, 0, "Ignoring unknown DVMRP message code %u from %s to %s",
! 293: igmp->igmp_code, inet_fmt(src, s1, sizeof(s1)), inet_fmt(dst, s2, sizeof(s2)));
! 294: return;
! 295: }
! 296:
! 297: case IGMP_PIM:
! 298: return; /* TODO: this is PIM v1 message. Handle it?. */
! 299:
! 300: case IGMP_MTRACE_RESP:
! 301: return; /* TODO: implement it */
! 302:
! 303: case IGMP_MTRACE:
! 304: accept_mtrace(src, dst, group, (char *)(igmp+1), igmp->igmp_code, igmpdatalen);
! 305: return;
! 306:
! 307: default:
! 308: logit(LOG_INFO, 0, "Ignoring unknown IGMP message type %x from %s to %s",
! 309: igmp->igmp_type, inet_fmt(src, s1, sizeof(s1)), inet_fmt(dst, s2, sizeof(s2)));
! 310: return;
! 311: }
! 312: }
! 313:
! 314: static void send_ip_frame(uint32_t src, uint32_t dst, int type, int code, char *buf, size_t len)
! 315: {
! 316: int setloop = 0;
! 317: struct ip *ip;
! 318: struct sockaddr_in sin;
! 319: char source[20], dest[20];
! 320:
! 321: /* Prepare the IP header */
! 322: len += IP_IGMP_HEADER_LEN;
! 323: ip = (struct ip *)buf;
! 324: ip->ip_id = 0; /* let kernel fill in */
! 325: ip->ip_off = 0;
! 326: ip->ip_src.s_addr = src;
! 327: ip->ip_dst.s_addr = dst;
! 328: #ifdef HAVE_IP_HDRINCL_BSD_ORDER
! 329: ip->ip_len = len;
! 330: #else
! 331: ip->ip_len = htons(len);
! 332: #endif
! 333:
! 334: if (IN_MULTICAST(ntohl(dst))) {
! 335: k_set_if(igmp_socket, src);
! 336: if (type != IGMP_DVMRP || dst == allhosts_group) {
! 337: setloop = 1;
! 338: k_set_loop(igmp_socket, TRUE);
! 339: }
! 340: #ifdef RAW_OUTPUT_IS_RAW
! 341: ip->ip_ttl = curttl;
! 342: } else {
! 343: ip->ip_ttl = MAXTTL;
! 344: #endif
! 345: }
! 346:
! 347: memset(&sin, 0, sizeof(sin));
! 348: sin.sin_family = AF_INET;
! 349: sin.sin_addr.s_addr = dst;
! 350: #ifdef HAVE_SA_LEN
! 351: sin.sin_len = sizeof(sin);
! 352: #endif
! 353:
! 354: IF_DEBUG(DEBUG_IGMP)
! 355: logit(LOG_DEBUG, 0, "Send %s from %s to %s",
! 356: packet_kind(IPPROTO_IGMP, type, code),
! 357: src == INADDR_ANY_N ? "INADDR_ANY" :
! 358: inet_fmt(src, s1, sizeof(s1)), inet_fmt(dst, s2, sizeof(s2)));
! 359:
! 360: while (sendto(igmp_socket, buf, len, 0, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
! 361: if (errno == EINTR)
! 362: continue; /* Received signal, retry syscall. */
! 363: if (errno == ENETDOWN || errno == ENODEV)
! 364: check_vif_state();
! 365: else if (errno == EPERM || errno == EHOSTUNREACH)
! 366: logit(LOG_WARNING, 0, "Not allowed to send IGMP message from %s to %s, possibly firewall"
! 367: #ifdef __linux__
! 368: ", or SELinux policy violation,"
! 369: #endif
! 370: " related problem."
! 371: ,
! 372: inet_fmt(src, source, sizeof(source)), inet_fmt(dst, dest, sizeof(dest)));
! 373: else
! 374: logit(log_level(IPPROTO_IGMP, type, code), errno, "Sendto to %s on %s",
! 375: inet_fmt(dst, s1, sizeof(s1)), inet_fmt(src, s2, sizeof(s2)));
! 376:
! 377: if (setloop)
! 378: k_set_loop(igmp_socket, FALSE);
! 379:
! 380: return;
! 381: }
! 382:
! 383: if (setloop)
! 384: k_set_loop(igmp_socket, FALSE);
! 385:
! 386: IF_DEBUG(DEBUG_PKT | debug_kind(IPPROTO_IGMP, type, code)) {
! 387: logit(LOG_DEBUG, 0, "SENT %5d bytes %s from %-15s to %s", len,
! 388: packet_kind(IPPROTO_IGMP, type, code),
! 389: src == INADDR_ANY_N
! 390: ? "INADDR_ANY"
! 391: : inet_fmt(src, s1, sizeof(s1)),
! 392: inet_fmt(dst, s2, sizeof(s2)));
! 393: }
! 394: }
! 395:
! 396: /*
! 397: * RFC-3376 states that Max Resp Code (MRC) and Querier's Query Interval Code
! 398: * (QQIC) should be presented in floating point value if their value exceeds
! 399: * 128. The following formula is used by IGMPv3 clients to calculate the
! 400: * actual value of the floating point:
! 401: *
! 402: * 0 1 2 3 4 5 6 7
! 403: * +-+-+-+-+-+-+-+-+
! 404: * |1| exp | mant |
! 405: * +-+-+-+-+-+-+-+-+
! 406: *
! 407: * QQI / MRT = (mant | 0x10) << (exp + 3)
! 408: *
! 409: * This requires us to find the largest set (fls) bit in the 15-bit number
! 410: * and set the exponent based on its index in the bits 15-8. ie.
! 411: *
! 412: * exponent 0: igmp_fls(0000 0000 1000 0010)
! 413: * exponent 5: igmp_fls(0001 0000 0000 0000)
! 414: * exponent 7: igmp_fls(0111 0101 0000 0000)
! 415: *
! 416: * and set that as the exponent. The mantissa is set to the last 4 bits
! 417: * remaining after the (3 + exponent) shifts to the right.
! 418: *
! 419: * Note!
! 420: * The numbers 31744-32767 are the maximum we can present with floating
! 421: * point that has an exponent of 3 and a mantissa of 4. After this the
! 422: * implementation just wraps around back to zero.
! 423: */
! 424: static inline uint8_t igmp_floating_point(unsigned int mantissa)
! 425: {
! 426: unsigned int exponent;
! 427:
! 428: /* Wrap around numbers larger than 2^15, since those can not be
! 429: * presented with 7-bit floating point. */
! 430: mantissa &= 0x00007FFF;
! 431:
! 432: /* If top 8 bits are zero. */
! 433: if (!(mantissa & 0x00007F80))
! 434: return mantissa;
! 435:
! 436: /* Shift the mantissa and mark this code floating point. */
! 437: mantissa >>= 3;
! 438: /* At this point the actual exponent (bits 7-5) are still 0, but the
! 439: * exponent might be incremented below. */
! 440: exponent = 0x00000080;
! 441:
! 442: /* If bits 7-4 are not zero. */
! 443: if (mantissa & 0x00000F00) {
! 444: mantissa >>= 4;
! 445: /* The index of largest set bit is at least 4. */
! 446: exponent |= 0x00000040;
! 447: }
! 448:
! 449: /* If bits 7-6 OR bits 3-2 are not zero. */
! 450: if (mantissa & 0x000000C0) {
! 451: mantissa >>= 2;
! 452: /* The index of largest set bit is atleast 6 if we shifted the
! 453: * mantissa earlier or atleast 2 if we did not shift it. */
! 454: exponent |= 0x00000020;
! 455: }
! 456:
! 457: /* If bit 7 OR bit 3 OR bit 1 is not zero. */
! 458: if (mantissa & 0x00000020) {
! 459: mantissa >>= 1;
! 460: /* The index of largest set bit is atleast 7 if we shifted the
! 461: * mantissa two times earlier or atleast 3 if we shifted the
! 462: * mantissa last time or atleast 1 if we did not shift it. */
! 463: exponent |= 0x00000010;
! 464: }
! 465:
! 466: return exponent | (mantissa & 0x0000000F);
! 467: }
! 468:
! 469: void send_igmp(char *buf, uint32_t src, uint32_t dst, int type, int code, uint32_t group, int datalen)
! 470: {
! 471: size_t len = IGMP_MINLEN + datalen;
! 472: struct igmpv3_query *igmp;
! 473:
! 474: igmp = (struct igmpv3_query *)(buf + IP_IGMP_HEADER_LEN);
! 475: igmp->type = type;
! 476: if (datalen >= 4)
! 477: igmp->code = igmp_floating_point(code);
! 478: else
! 479: igmp->code = code;
! 480: igmp->group = group;
! 481: igmp->csum = 0;
! 482: igmp->csum = inet_cksum((uint16_t *)igmp, len);
! 483:
! 484: if (datalen >= 4) {
! 485: igmp->qrv = 2;
! 486: igmp->qqic = igmp_floating_point(igmp_query_interval);
! 487: }
! 488:
! 489: send_ip_frame(src, dst, type, code, buf, len);
! 490: }
! 491:
! 492: /**
! 493: * Local Variables:
! 494: * version-control: t
! 495: * indent-tabs-mode: t
! 496: * c-file-style: "ellemtel"
! 497: * c-basic-offset: 4
! 498: * End:
! 499: */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>