Annotation of embedaddon/pimd/igmp.c, revision 1.1.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>