Annotation of embedaddon/pimd/igmp_proto.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_proto.c,v 1.15 2001/09/10 20:31:36 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: typedef struct {
        !            46:     vifi_t  vifi;
        !            47:     struct listaddr *g;
        !            48:     uint32_t source; /* Source for SSM */
        !            49:     int q_time; /* IGMP Code */
        !            50:     int q_len; /* Data length */
        !            51: } cbk_t;
        !            52: 
        !            53: 
        !            54: /*
        !            55:  * Forward declarations.
        !            56:  */
        !            57: static void DelVif       (void *arg);
        !            58: static int SetTimer      (vifi_t vifi, struct listaddr *g, uint32_t source);
        !            59: static int SetVersionTimer      (vifi_t vifi, struct listaddr *g);
        !            60: static int DeleteTimer   (int id);
        !            61: static void send_query   (struct uvif *v, uint32_t group, int interval);
        !            62: static void SendQuery    (void *arg);
        !            63: static int SetQueryTimer (struct listaddr *g, vifi_t vifi, int to_expire, int q_time, int q_len);
        !            64: static uint32_t igmp_group_membership_timeout(void);
        !            65: 
        !            66: /* The querier timeout depends on the configured query interval */
        !            67: uint32_t igmp_query_interval  = IGMP_QUERY_INTERVAL;
        !            68: uint32_t igmp_querier_timeout = IGMP_OTHER_QUERIER_PRESENT_INTERVAL;
        !            69: 
        !            70: 
        !            71: /*
        !            72:  * Send group membership queries on that interface if I am querier.
        !            73:  */
        !            74: void query_groups(struct uvif *v)
        !            75: {
        !            76:     int datalen = 4;
        !            77:     int code = IGMP_MAX_HOST_REPORT_DELAY * IGMP_TIMER_SCALE;
        !            78:     struct listaddr *g;
        !            79: 
        !            80:     v->uv_gq_timer = igmp_query_interval;
        !            81: 
        !            82:     if (v->uv_flags & VIFF_QUERIER) {
        !            83:        /* IGMP version to use depends on the compatibility mode of the interface */
        !            84:        if (v->uv_flags & VIFF_IGMPV2) {
        !            85:            /* RFC 3376: When in IGMPv2 mode, routers MUST send Periodic
        !            86:            Queries truncated at the Group Address field (i.e., 8 bytes long) */
        !            87:            datalen = 0;
        !            88:        } else if (v->uv_flags & VIFF_IGMPV1) {
        !            89:            /* RFC 3376: When in IGMPv1 mode, routers MUST send Periodic Queries with a Max Response Time of 0 */
        !            90:            datalen = 0;
        !            91:            code = 0;
        !            92:        }
        !            93: 
        !            94:        IF_DEBUG(DEBUG_IGMP)
        !            95:            logit(LOG_DEBUG, 0, "%s(): Sending IGMP v%s query on %s",
        !            96:                  __func__, datalen == 4 ? "3" : "2", v->uv_name);
        !            97:        send_igmp(igmp_send_buf, v->uv_lcl_addr, allhosts_group,
        !            98:                  IGMP_MEMBERSHIP_QUERY,
        !            99:                  code, 0, datalen);
        !           100:     }
        !           101: 
        !           102:     /*
        !           103:      * Decrement the old-hosts-present timer for each
        !           104:      * active group on that vif.
        !           105:      */
        !           106:     for (g = v->uv_groups; g != NULL; g = g->al_next) {
        !           107:        if (g->al_old > TIMER_INTERVAL)
        !           108:            g->al_old -= TIMER_INTERVAL;
        !           109:        else
        !           110:            g->al_old = 0;
        !           111:     }
        !           112: }
        !           113: 
        !           114: 
        !           115: /*
        !           116:  * Process an incoming host membership query
        !           117:  */
        !           118: void accept_membership_query(uint32_t src, uint32_t dst __attribute__((unused)), uint32_t group, int tmo, int igmp_version)
        !           119: {
        !           120:     vifi_t vifi;
        !           121:     struct uvif *v;
        !           122: 
        !           123:     /* Ignore my own membership query */
        !           124:     if (local_address(src) != NO_VIF)
        !           125:        return;
        !           126: 
        !           127:     /* Only v3 is allowed for SSM
        !           128:      * TODO: Rate-limit messages?
        !           129:      */
        !           130:     if (igmp_version != 3 && IN_PIM_SSM_RANGE(group)) {
        !           131:        logit(LOG_WARNING, 0, "SSM addresses are not allowed in v%d query.", igmp_version);
        !           132:        return;
        !           133:     }
        !           134: 
        !           135:     /* TODO: modify for DVMRP?? */
        !           136:     if ((vifi = find_vif_direct(src)) == NO_VIF) {
        !           137:        IF_DEBUG(DEBUG_IGMP)
        !           138:            logit(LOG_INFO, 0, "Ignoring group membership query from non-adjacent host %s",
        !           139:                  inet_fmt(src, s1, sizeof(s1)));
        !           140:        return;
        !           141:     }
        !           142: 
        !           143:     v = &uvifs[vifi];
        !           144: 
        !           145:     /* Do not accept messages of higher version than current
        !           146:      * compatibility mode as specified in RFC 3376 - 7.3.1
        !           147:      */
        !           148:     if (v->uv_querier) {
        !           149:        if ((igmp_version == 3 && (v->uv_flags & VIFF_IGMPV2)) ||
        !           150:            (igmp_version == 2 && (v->uv_flags & VIFF_IGMPV1))) {
        !           151:            int i;
        !           152: 
        !           153:            /*
        !           154:             * Exponentially back-off warning rate
        !           155:             */
        !           156:            i = ++v->uv_igmpv1_warn;
        !           157:            while (i && !(i & 1)) {
        !           158:                i >>= 1;
        !           159:                if (i == 1) {
        !           160:                    logit(LOG_WARNING, 0, "Received IGMP v%d query from %s on vif %d,"
        !           161:                          " but I am configured for IGMP v%d network compatibility mode",
        !           162:                          igmp_version,
        !           163:                          inet_fmt(src, s1, sizeof(s1)),
        !           164:                          vifi,
        !           165:                          v->uv_flags & VIFF_IGMPV1 ? 1 : 2);
        !           166:                }
        !           167:                return;
        !           168:            }
        !           169:        }
        !           170:     }
        !           171: 
        !           172:     if (!v->uv_querier || v->uv_querier->al_addr != src) {
        !           173:        /*
        !           174:         * This might be:
        !           175:         * - A query from a new querier, with a lower source address
        !           176:         *   than the current querier (who might be me)
        !           177:         * - A query from a new router that just started up and doesn't
        !           178:         *   know who the querier is.
        !           179:         * - A query from the current querier
        !           180:         */
        !           181:        if (ntohl(src) < (v->uv_querier
        !           182:                          ? ntohl(v->uv_querier->al_addr)
        !           183:                          : ntohl(v->uv_lcl_addr))) {
        !           184:            IF_DEBUG(DEBUG_IGMP) {
        !           185:                logit(LOG_DEBUG, 0, "new querier %s (was %s) on vif %d",
        !           186:                      inet_fmt(src, s1, sizeof(s1)),
        !           187:                      v->uv_querier
        !           188:                      ? inet_fmt(v->uv_querier->al_addr, s2, sizeof(s2))
        !           189:                      : "me", vifi);
        !           190:            }
        !           191: 
        !           192:            if (!v->uv_querier) {
        !           193:                v->uv_querier = (struct listaddr *) calloc(1, sizeof(struct listaddr));
        !           194:                if (!v->uv_querier) {
        !           195:                    logit(LOG_ERR, 0, "Failed calloc() in accept_membership_query()");
        !           196:                    return;
        !           197:                }
        !           198: 
        !           199:                v->uv_querier->al_next = (struct listaddr *)NULL;
        !           200:                v->uv_querier->al_timer = 0;
        !           201:                v->uv_querier->al_genid = 0;
        !           202:                v->uv_querier->al_mv = 0;
        !           203:                v->uv_querier->al_old = 0;
        !           204:                v->uv_querier->al_index = 0;
        !           205:                v->uv_querier->al_timerid = 0;
        !           206:                v->uv_querier->al_query = 0;
        !           207:                v->uv_querier->al_flags = 0;
        !           208: 
        !           209:                v->uv_flags &= ~VIFF_QUERIER;
        !           210:            }
        !           211:            v->uv_querier->al_addr = src;
        !           212:            time(&v->uv_querier->al_ctime);
        !           213:        }
        !           214:     }
        !           215: 
        !           216:     /*
        !           217:      * Reset the timer since we've received a query.
        !           218:      */
        !           219:     if (v->uv_querier && src == v->uv_querier->al_addr)
        !           220:        v->uv_querier->al_timer = 0;
        !           221: 
        !           222:     /*
        !           223:      * If this is a Group-Specific query which we did not source,
        !           224:      * we must set our membership timer to [Last Member Query Count] *
        !           225:      * the [Max Response Time] in the packet.
        !           226:      */
        !           227:     if (!(v->uv_flags & VIFF_IGMPV1) && group != 0 && src != v->uv_lcl_addr) {
        !           228:        struct listaddr *g;
        !           229: 
        !           230:        IF_DEBUG(DEBUG_IGMP) {
        !           231:            logit(LOG_DEBUG, 0, "Group-specific membership query for %s from %s on vif %d, timer %d",
        !           232:                  inet_fmt(group, s2, sizeof(s2)), inet_fmt(src, s1, sizeof(s1)), vifi, tmo);
        !           233:        }
        !           234: 
        !           235:        for (g = v->uv_groups; g != NULL; g = g->al_next) {
        !           236:            if (group == g->al_addr && g->al_query == 0) {
        !           237:                /* setup a timeout to remove the group membership */
        !           238:                if (g->al_timerid)
        !           239:                    g->al_timerid = DeleteTimer(g->al_timerid);
        !           240: 
        !           241:                g->al_timer = IGMP_LAST_MEMBER_QUERY_COUNT * tmo / IGMP_TIMER_SCALE;
        !           242:                /* use al_query to record our presence in last-member state */
        !           243:                g->al_query = -1;
        !           244:                g->al_timerid = SetTimer(vifi, g, 0);
        !           245:                IF_DEBUG(DEBUG_IGMP) {
        !           246:                    logit(LOG_DEBUG, 0, "Timer for grp %s on vif %d set to %ld",
        !           247:                          inet_fmt(group, s2, sizeof(s2)), vifi, g->al_timer);
        !           248:                }
        !           249:                break;
        !           250:            }
        !           251:        }
        !           252:     }
        !           253: }
        !           254: 
        !           255: 
        !           256: /*
        !           257:  * Process an incoming group membership report.
        !           258:  */
        !           259: void accept_group_report(uint32_t igmp_src, uint32_t ssm_src, uint32_t group, int igmp_report_type)
        !           260: {
        !           261:     vifi_t vifi;
        !           262:     struct uvif *v;
        !           263:     struct listaddr *g;
        !           264:     struct listaddr *s = NULL;
        !           265: 
        !           266:     if ((vifi = find_vif_direct_local(igmp_src)) == NO_VIF) {
        !           267:        IF_DEBUG(DEBUG_IGMP) {
        !           268:            logit(LOG_INFO, 0, "Ignoring group membership report from non-adjacent host %s",
        !           269:                  inet_fmt(igmp_src, s1, sizeof(s1)));
        !           270:        }
        !           271:        return;
        !           272:     }
        !           273: 
        !           274:     inet_fmt(igmp_src, s1, sizeof(s1));
        !           275:     inet_fmt(ssm_src, s2, sizeof(s2));
        !           276:     inet_fmt(group, s3, sizeof(s3));
        !           277:     IF_DEBUG(DEBUG_IGMP)
        !           278:        logit(LOG_DEBUG, 0, "%s(): igmp_src %s ssm_src %s group %s report_type %i",
        !           279:              __func__, s1, s2, s3, igmp_report_type);
        !           280: 
        !           281:     v = &uvifs[vifi];
        !           282: 
        !           283:     /*
        !           284:      * Look for the group in our group list; if found, reset its timer.
        !           285:      */
        !           286:     for (g = v->uv_groups; g != NULL; g = g->al_next) {
        !           287:        if (group == g->al_addr) {
        !           288:            if (igmp_report_type == IGMP_V1_MEMBERSHIP_REPORT) {
        !           289:                g->al_old = DVMRP_OLD_AGE_THRESHOLD;
        !           290:                if (!IN_PIM_SSM_RANGE(group) && g->al_pv>1) {
        !           291:                    IF_DEBUG(DEBUG_IGMP)
        !           292:                        logit(LOG_DEBUG, 0, "Change IGMP compatibility mode to v1 for group %s", s3);
        !           293:                    g->al_pv = 1;
        !           294:                }
        !           295:            } else if (!IN_PIM_SSM_RANGE(group) && igmp_report_type == IGMP_V2_MEMBERSHIP_REPORT) {
        !           296:                IF_DEBUG(DEBUG_IGMP)
        !           297:                    logit(LOG_DEBUG,0, "%s(): al_pv=%d", __func__, g->al_pv);
        !           298:                if (g->al_pv > 2) {
        !           299:                    IF_DEBUG(DEBUG_IGMP)
        !           300:                        logit(LOG_DEBUG, 0, "Change IGMP compatibility mode to v2 for group %s", s3);
        !           301:                    g->al_pv = 2;
        !           302:                }
        !           303:            }
        !           304: 
        !           305:            g->al_reporter = igmp_src;
        !           306: 
        !           307:            /** delete old timers, set a timer for expiration **/
        !           308:            g->al_timer = igmp_group_membership_timeout();
        !           309:            if (g->al_query)
        !           310:                g->al_query = DeleteTimer(g->al_query);
        !           311: 
        !           312:            if (g->al_timerid)
        !           313:                g->al_timerid = DeleteTimer(g->al_timerid);
        !           314: 
        !           315:            g->al_timerid = SetTimer(vifi, g, ssm_src);
        !           316: 
        !           317:            /* Reset timer for switching version back every time an older version report is received */
        !           318:            if (!IN_PIM_SSM_RANGE(group) && g->al_pv<3 && (igmp_report_type == IGMP_V1_MEMBERSHIP_REPORT ||
        !           319:                igmp_report_type == IGMP_V2_MEMBERSHIP_REPORT)) {
        !           320:                if (g->al_versiontimer)
        !           321:                        g->al_versiontimer = DeleteTimer(g->al_versiontimer);
        !           322: 
        !           323:                g->al_versiontimer = SetVersionTimer(vifi, g);
        !           324:            }
        !           325: 
        !           326:            /* Find source */
        !           327:            if (IN_PIM_SSM_RANGE(group)) {
        !           328:                for (s = g->al_sources; s; s = s->al_next) {
        !           329:                    IF_DEBUG(DEBUG_IGMP)
        !           330:                        logit(LOG_DEBUG, 0, "%s(): Seek source %s, curr=%s", __func__,
        !           331:                              inet_fmt(ssm_src, s1, sizeof(s1)),
        !           332:                              inet_fmt(s->al_addr, s2, sizeof(s2)));
        !           333:                    if (ssm_src == s->al_addr) {
        !           334:                        IF_DEBUG(DEBUG_IGMP)
        !           335:                            logit(LOG_DEBUG, 0, "%s(): Source found", __func__);
        !           336:                        break;
        !           337:                    }
        !           338:                }
        !           339:                if (!s) {
        !           340:                    /* Add new source */
        !           341:                    s = (struct listaddr *)calloc(1, sizeof(struct listaddr));
        !           342:                    if (!s) {
        !           343:                        logit(LOG_ERR, errno, "%s(): Ran out of memory", __func__);
        !           344:                        return;
        !           345:                    }
        !           346:                    s->al_addr = ssm_src;
        !           347:                    s->al_next = g->al_sources;
        !           348:                    g->al_sources = s;
        !           349: 
        !           350:                    IF_DEBUG(DEBUG_IGMP)
        !           351:                        logit(LOG_DEBUG, 0, "%s(): Source %s added to g:%p", __func__, s2, g);
        !           352:                }
        !           353:            }
        !           354: 
        !           355:            /* TODO: might need to add a check if I am the forwarder??? */
        !           356:            /* if (v->uv_flags & VIFF_DR) */
        !           357:            if (IN_PIM_SSM_RANGE(group)) {
        !           358:                IF_DEBUG(DEBUG_IGMP)
        !           359:                    logit(LOG_INFO, 0, "Add leaf (%s,%s)", s1, s3);
        !           360:                add_leaf(vifi, ssm_src, group);
        !           361:            } else {
        !           362:                add_leaf(vifi, INADDR_ANY_N, group);
        !           363:            }
        !           364:            break;
        !           365:        }
        !           366:     }
        !           367: 
        !           368:     /*
        !           369:      * If not found, add it to the list and update kernel cache.
        !           370:      */
        !           371:     if (!g) {
        !           372:        g = (struct listaddr *)calloc(1, sizeof(struct listaddr));
        !           373:        if (!g) {
        !           374:            logit(LOG_ERR, errno, "%s(): Ran out of memory", __func__);
        !           375:            return;
        !           376:        }
        !           377: 
        !           378:        g->al_addr = group;
        !           379:        if (!IN_PIM_SSM_RANGE(group) && igmp_report_type == IGMP_V1_MEMBERSHIP_REPORT) {
        !           380:            g->al_old = DVMRP_OLD_AGE_THRESHOLD;
        !           381:            IF_DEBUG(DEBUG_IGMP)
        !           382:                logit(LOG_DEBUG, 0, "Change IGMP compatibility mode to v1 for group %s", s3);
        !           383:            g->al_pv = 1;
        !           384:        } else if (!IN_PIM_SSM_RANGE(group) && igmp_report_type == IGMP_V2_MEMBERSHIP_REPORT) {
        !           385:            IF_DEBUG(DEBUG_IGMP)
        !           386:                logit(LOG_DEBUG, 0, "Change IGMP compatibility mode to v2 for group %s", s3);
        !           387:            g->al_pv = 2;
        !           388:        } else {
        !           389:            g->al_pv = 3;
        !           390:        }
        !           391: 
        !           392:        /* Add new source */
        !           393:        if (IN_PIM_SSM_RANGE(group)) {
        !           394:            s = (struct listaddr *)calloc(1, sizeof(struct listaddr));
        !           395:            if (!s) {
        !           396:                logit(LOG_ERR, errno, "%s(): Ran out of memory", __func__);
        !           397:                return;
        !           398:            }
        !           399:            s->al_addr = ssm_src;
        !           400:            s->al_next = g->al_sources;
        !           401:            g->al_sources = s;
        !           402:            IF_DEBUG(DEBUG_IGMP)
        !           403:                logit(LOG_DEBUG, 0, "%s(): Source %s added to new g:%p", __func__, s2, g);
        !           404:        }
        !           405: 
        !           406:        /** set a timer for expiration **/
        !           407:        g->al_query     = 0;
        !           408:        g->al_timer     = IGMP_GROUP_MEMBERSHIP_INTERVAL;
        !           409:        g->al_reporter  = igmp_src;
        !           410:        g->al_timerid   = SetTimer(vifi, g, ssm_src);
        !           411: 
        !           412:        /* Set timer for swithing version back if an older version report is received */
        !           413:        if (!IN_PIM_SSM_RANGE(group) && g->al_pv<3) {
        !           414:            g->al_versiontimer = SetVersionTimer(vifi, g);
        !           415:        }
        !           416: 
        !           417:        g->al_next      = v->uv_groups;
        !           418:        v->uv_groups    = g;
        !           419:        time(&g->al_ctime);
        !           420: 
        !           421:        /* TODO: might need to add a check if I am the forwarder??? */
        !           422:        /* if (v->uv_flags & VIFF_DR) */
        !           423:        if (IN_PIM_SSM_RANGE(group)) {
        !           424:            IF_DEBUG(DEBUG_IGMP)
        !           425:                logit(LOG_INFO, 0, "SSM group order from  %s (%s,%s)", s1, s2, s3);
        !           426:            add_leaf(vifi, ssm_src, group);
        !           427:        } else {
        !           428:            IF_DEBUG(DEBUG_IGMP)
        !           429:                logit(LOG_INFO, 0, "SM group order from  %s (*,%s)", s1, s3);
        !           430:            add_leaf(vifi, INADDR_ANY_N, group);
        !           431:        }
        !           432:     }
        !           433: }
        !           434: 
        !           435: 
        !           436: /* TODO: send PIM prune message if the last member? */
        !           437: void accept_leave_message(uint32_t src, uint32_t dst __attribute__((unused)), uint32_t group)
        !           438: {
        !           439:     vifi_t vifi;
        !           440:     struct uvif *v;
        !           441:     struct listaddr *g;
        !           442: 
        !           443:     int datalen = 4;
        !           444:     int code = IGMP_LAST_MEMBER_QUERY_INTERVAL * IGMP_TIMER_SCALE;
        !           445: 
        !           446:     /* TODO: modify for DVMRP ??? */
        !           447:     if ((vifi = find_vif_direct_local(src)) == NO_VIF) {
        !           448:        IF_DEBUG(DEBUG_IGMP)
        !           449:            logit(LOG_INFO, 0, "ignoring group leave report from non-adjacent host %s",
        !           450:                  inet_fmt(src, s1, sizeof(s1)));
        !           451:        return;
        !           452:     }
        !           453: 
        !           454:     inet_fmt(src, s1, sizeof(s1));
        !           455:     inet_fmt(dst, s2, sizeof(s2));
        !           456:     inet_fmt(group, s3, sizeof(s3));
        !           457:     IF_DEBUG(DEBUG_IGMP)
        !           458:        logit(LOG_DEBUG, 0, "%s(): src %s dst %s group %s", __func__, s1, s2, s3);
        !           459:     v = &uvifs[vifi];
        !           460: 
        !           461: #if 0
        !           462:     /* XXX: a PIM-SM last-hop router needs to know when a local member
        !           463:      * has left.
        !           464:      */
        !           465:     if (!(v->uv_flags & (VIFF_QUERIER | VIFF_DR))
        !           466:        || (v->uv_flags & VIFF_IGMPV1))
        !           467:        return;
        !           468: #endif
        !           469: 
        !           470:     /*
        !           471:      * Look for the group in our group list in order to set up a short-timeout
        !           472:      * query.
        !           473:      */
        !           474:     for (g = v->uv_groups; g; g = g->al_next) {
        !           475:        if (group == g->al_addr) {
        !           476:            IF_DEBUG(DEBUG_IGMP)
        !           477:                logit(LOG_DEBUG, 0, "accept_leave_message(): old=%d query=%d", g->al_old, g->al_query);
        !           478: 
        !           479:            /* Ignore the leave message if there are old hosts present */
        !           480:            if (g->al_old)
        !           481:                return;
        !           482: 
        !           483:            /* still waiting for a reply to a query, ignore the leave */
        !           484:            if (g->al_query)
        !           485:                return;
        !           486: 
        !           487:            /* TODO: Remove the source. Ignore the leave if there
        !           488:               are still sources left
        !           489:            if (IN_PIM_SSM_RANGE(g->al_addr)) {
        !           490:                for (s = g->al_sources; s != NULL; s = s->al_next) {
        !           491:                    if (dst == s->al_addr) {
        !           492:                    }
        !           493:                }
        !           494:            }
        !           495:            */
        !           496: 
        !           497:            /** delete old timer set a timer for expiration **/
        !           498:            if (g->al_timerid)
        !           499:                g->al_timerid = DeleteTimer(g->al_timerid);
        !           500: 
        !           501: #if IGMP_LAST_MEMBER_QUERY_COUNT != 2
        !           502: /*
        !           503:   This code needs to be updated to keep a counter of the number
        !           504:   of queries remaining.
        !           505: */
        !           506: #endif
        !           507: 
        !           508:            if (v->uv_flags & VIFF_QUERIER) {
        !           509:                /* Use lowest IGMP version */
        !           510:                if (v->uv_flags & VIFF_IGMPV2 || g->al_pv <= 2) {
        !           511:                    datalen = 0;
        !           512:                } else if (v->uv_flags & VIFF_IGMPV1 || g->al_pv == 1) {
        !           513:                    datalen = 0;
        !           514:                    code = 0;
        !           515:                }
        !           516: 
        !           517:                IF_DEBUG(DEBUG_IGMP)
        !           518:                    logit(LOG_DEBUG, 0, "%s(): Sending IGMP v%s query (al_pv=%d)",
        !           519:                          __func__, datalen == 4 ? "3" : "2", g->al_pv);
        !           520:                send_igmp(igmp_send_buf, v->uv_lcl_addr, g->al_addr,
        !           521:                          IGMP_MEMBERSHIP_QUERY,
        !           522:                          code,
        !           523:                          g->al_addr, datalen);
        !           524:            }
        !           525: 
        !           526:            g->al_timer = IGMP_LAST_MEMBER_QUERY_INTERVAL * (IGMP_LAST_MEMBER_QUERY_COUNT + 1);
        !           527:            g->al_query = SetQueryTimer(g, vifi,
        !           528:                                        IGMP_LAST_MEMBER_QUERY_INTERVAL,
        !           529:                                        code, datalen);
        !           530:            g->al_timerid = SetTimer(vifi, g, dst);
        !           531:            break;
        !           532:        }
        !           533:     }
        !           534: }
        !           535: 
        !           536: /*
        !           537:  * Time out old version compatibility mode
        !           538:  */
        !           539: static void SwitchVersion(void *arg)
        !           540: {
        !           541:     cbk_t *cbk = (cbk_t *)arg;
        !           542: 
        !           543:     if (cbk->g->al_pv < 3)
        !           544:        cbk->g->al_pv += 1;
        !           545: 
        !           546:     logit(LOG_INFO, 0, "Switch IGMP compatibility mode back to v%d for group %s",
        !           547:          cbk->g->al_pv, inet_fmt(cbk->g->al_addr, s1, sizeof(s1)));
        !           548: }
        !           549: 
        !           550: /*
        !           551:  * Loop through and process all sources in a v3 record.
        !           552:  *
        !           553:  * Parameters:
        !           554:  *     igmp_report_type   Report type of IGMP message
        !           555:  *     igmp_src           Src address of IGMP message
        !           556:  *     group              Multicast group
        !           557:  *     sources            Pointer to the beginning of sources list in the IGMP message
        !           558:  *     report_pastend     Pointer to the end of IGMP message
        !           559:  *
        !           560:  * Returns:
        !           561:  *     1 if succeeded, 0 if failed
        !           562:  */
        !           563: int accept_sources(int igmp_report_type, uint32_t igmp_src, uint32_t group, uint8_t *sources,
        !           564:     uint8_t *report_pastend, int rec_num_sources) {
        !           565:     int j;
        !           566:     uint8_t *src;
        !           567:     char src_str[200];
        !           568: 
        !           569:     for (j = 0, src = sources; j < rec_num_sources; ++j, src += 4) {
        !           570:         if ((src + 4) > report_pastend) {
        !           571:            IF_DEBUG(DEBUG_IGMP)
        !           572:                logit(LOG_DEBUG, 0, "src +4 > report_pastend");
        !           573:             return 0;
        !           574:         }
        !           575: 
        !           576:         inet_ntop(AF_INET, src, src_str , sizeof(src_str));
        !           577:        IF_DEBUG(DEBUG_IGMP)
        !           578:            logit(LOG_DEBUG, 0, "Add source (%s,%s)", src_str, inet_fmt(group, s1, sizeof(s1)));
        !           579: 
        !           580:         accept_group_report(igmp_src, ((struct in_addr*)src)->s_addr, group, igmp_report_type);
        !           581: 
        !           582:        IF_DEBUG(DEBUG_IGMP)
        !           583:            logit(LOG_DEBUG, 0, "Accepted, switch SPT (%s,%s)", src_str, inet_fmt(group, s1, sizeof(s1)));
        !           584:         switch_shortest_path(((struct in_addr*)src)->s_addr, group);
        !           585:     }
        !           586: 
        !           587:     return 1;
        !           588: }
        !           589: 
        !           590: /*
        !           591:  * Handle IGMP v3 membership reports (join/leave)
        !           592:  */
        !           593: void accept_membership_report(uint32_t src, uint32_t dst, struct igmpv3_report *report, ssize_t reportlen)
        !           594: {
        !           595:     struct igmpv3_grec *record;
        !           596:     int num_groups, i;
        !           597:     uint8_t *report_pastend = (uint8_t *)report + reportlen;
        !           598: 
        !           599:     num_groups = ntohs(report->ngrec);
        !           600:     if (num_groups < 0) {
        !           601:        logit(LOG_INFO, 0, "Invalid Membership Report from %s: num_groups = %d",
        !           602:              inet_fmt(src, s1, sizeof(s1)), num_groups);
        !           603:        return;
        !           604:     }
        !           605: 
        !           606:     IF_DEBUG(DEBUG_IGMP)
        !           607:        logit(LOG_DEBUG, 0, "%s(): IGMP v3 report, %d bytes, from %s to %s with %d group records.",
        !           608:              __func__, reportlen, inet_fmt(src, s1, sizeof(s1)), inet_fmt(dst, s2, sizeof(s2)), num_groups);
        !           609: 
        !           610:     record = &report->grec[0];
        !           611: 
        !           612:     for (i = 0; i < num_groups; i++) {
        !           613:        struct in_addr  rec_group;
        !           614:        uint8_t        *sources;
        !           615:        int             rec_type;
        !           616:        int             rec_auxdatalen;
        !           617:        int             rec_num_sources;
        !           618:        int             j;
        !           619:        char src_str[200];
        !           620:        int record_size = 0;
        !           621: 
        !           622:        rec_num_sources = ntohs(record->grec_nsrcs);
        !           623:        rec_auxdatalen = record->grec_auxwords;
        !           624:        record_size = sizeof(struct igmpv3_grec) + sizeof(uint32_t) * rec_num_sources + rec_auxdatalen;
        !           625:        if ((uint8_t *)record + record_size > report_pastend) {
        !           626:            logit(LOG_INFO, 0, "Invalid group report %p > %p",
        !           627:                  (uint8_t *)record + record_size, report_pastend);
        !           628:            return;
        !           629:        }
        !           630: 
        !           631:        rec_type = record->grec_type;
        !           632:        rec_group.s_addr = (in_addr_t)record->grec_mca;
        !           633:        sources = (u_int8_t *)record->grec_src;
        !           634: 
        !           635:        switch (rec_type) {
        !           636:            case IGMP_MODE_IS_EXCLUDE:
        !           637:                /* RFC 4604: A router SHOULD ignore a group record of
        !           638:                   type MODE_IS_EXCLUDE if it refers to an SSM destination address */
        !           639:                if (!IN_PIM_SSM_RANGE(rec_group.s_addr)) {
        !           640:                    if (rec_num_sources==0) {
        !           641:                        /* RFC 5790: EXCLUDE (*,G) join can be interpreted by the router
        !           642:                           as a request to include all sources. */
        !           643:                        accept_group_report(src, 0 /*dst*/, rec_group.s_addr, report->type);
        !           644:                    } else {
        !           645:                        /* RFC 5790: LW-IGMPv3 does not use EXCLUDE filter-mode with a non-null source address list.*/
        !           646:                        logit(LOG_INFO, 0, "Record type MODE_IS_EXCLUDE with non-null source list is currently unsupported.");
        !           647:                    }
        !           648:                }
        !           649:                break;
        !           650: 
        !           651:            case IGMP_CHANGE_TO_EXCLUDE_MODE:
        !           652:                /* RFC 4604: A router SHOULD ignore a group record of
        !           653:                   type CHANGE_TO_EXCLUDE_MODE if it refers to an SSM destination address */
        !           654:                if (!IN_PIM_SSM_RANGE(rec_group.s_addr)) {
        !           655:                    if (rec_num_sources==0) {
        !           656:                        /* RFC 5790: EXCLUDE (*,G) join can be interpreted by the router
        !           657:                           as a request to include all sources. */
        !           658:                        accept_group_report(src, 0 /*dst*/, rec_group.s_addr, report->type);
        !           659:                    } else {
        !           660:                        /* RFC 5790: LW-IGMPv3 does not use EXCLUDE filter-mode with a non-null source address list.*/
        !           661:                        logit(LOG_DEBUG, 0, "Record type MODE_TO_EXCLUDE with non-null source list is currently unsupported.");
        !           662:                    }
        !           663:                }
        !           664:                break;
        !           665: 
        !           666:            case IGMP_MODE_IS_INCLUDE:
        !           667:                if (!accept_sources(report->type, src, rec_group.s_addr, sources, report_pastend, rec_num_sources)) {
        !           668:                    IF_DEBUG(DEBUG_IGMP)
        !           669:                        logit(LOG_DEBUG, 0, "Accept sources failed.");
        !           670:                    return;
        !           671:                }
        !           672:                break;
        !           673: 
        !           674:            case IGMP_CHANGE_TO_INCLUDE_MODE:
        !           675:                if (!accept_sources(report->type, src, rec_group.s_addr, sources, report_pastend, rec_num_sources)) {
        !           676:                    IF_DEBUG(DEBUG_IGMP)
        !           677:                        logit(LOG_DEBUG, 0, "Accept sources failed.");
        !           678:                    return;
        !           679:                }
        !           680:                break;
        !           681: 
        !           682:            case IGMP_ALLOW_NEW_SOURCES:
        !           683:                if (!accept_sources(report->type, src, rec_group.s_addr, sources, report_pastend, rec_num_sources)) {
        !           684:                    logit(LOG_DEBUG, 0, "Accept sources failed.");
        !           685:                    return;
        !           686:                }
        !           687:                break;
        !           688: 
        !           689:            case IGMP_BLOCK_OLD_SOURCES:
        !           690:                for (j = 0; j < rec_num_sources; j++) {
        !           691:                    uint32_t *gsrc = (uint32_t *)&record->grec_src[j];
        !           692: 
        !           693:                    if ((uint8_t *)gsrc > report_pastend) {
        !           694:                        logit(LOG_INFO, 0, "Invalid group record");
        !           695:                        return;
        !           696:                    }
        !           697: 
        !           698:                    inet_ntop(AF_INET, gsrc, src_str , sizeof(src_str));
        !           699:                    IF_DEBUG(DEBUG_IGMP)
        !           700:                        logit(LOG_DEBUG, 0, "Remove source[%d] (%s,%s)", j, src_str, inet_ntoa(rec_group));
        !           701:                    accept_leave_message(src, *gsrc, rec_group.s_addr);
        !           702:                    IF_DEBUG(DEBUG_IGMP)
        !           703:                        logit(LOG_DEBUG, 0, "Accepted");
        !           704:                }
        !           705:                break;
        !           706: 
        !           707:            default:
        !           708:                //  RFC3376: Unrecognized Record Type values MUST be silently ignored.
        !           709:                break;
        !           710:        }
        !           711: 
        !           712:        record = (struct igmpv3_grec *)((uint8_t *)record + record_size);
        !           713:     }
        !           714: }
        !           715: 
        !           716: /*
        !           717:  * Calculate group membership timeout
        !           718:  */
        !           719: static uint32_t igmp_group_membership_timeout(void)
        !           720: {
        !           721:     return IGMP_ROBUSTNESS_VARIABLE * igmp_query_interval + IGMP_QUERY_RESPONSE_INTERVAL;
        !           722: }
        !           723: 
        !           724: /*
        !           725:  * Time out record of a group membership on a vif
        !           726:  */
        !           727: static void DelVif(void *arg)
        !           728: {
        !           729:     cbk_t *cbk = (cbk_t *)arg;
        !           730:     vifi_t vifi = cbk->vifi;
        !           731:     struct uvif *v = &uvifs[vifi];
        !           732:     struct listaddr *a, **anp, *g = cbk->g;
        !           733:     struct listaddr *curr, *prev = NULL;
        !           734: 
        !           735:     if (IN_PIM_SSM_RANGE(g->al_addr)) {
        !           736:        for (curr = g->al_sources; curr; prev = curr, curr = curr->al_next) {
        !           737:            inet_fmt(cbk->source, s1, sizeof(s1));
        !           738:            inet_fmt(curr->al_addr, s2, sizeof(s2));
        !           739:            IF_DEBUG(DEBUG_IGMP)
        !           740:                logit(LOG_DEBUG, 0, "DelVif: Seek source %s, curr=%s (%p)", s1, s2, curr);
        !           741: 
        !           742:            if (curr->al_addr == cbk->source) {
        !           743:                if (!prev)
        !           744:                    g->al_sources = curr->al_next; /* Remove from beginning */
        !           745:                else
        !           746:                    prev->al_next = curr->al_next;
        !           747: 
        !           748:                free(curr);
        !           749:                break;
        !           750:            }
        !           751:        }
        !           752: 
        !           753:        IF_DEBUG(DEBUG_IGMP)
        !           754:            logit(LOG_DEBUG, 0, "DelVif: %s sources left", g->al_sources ? "Still" : "No");
        !           755:        if (g->al_sources) {
        !           756:            IF_DEBUG(DEBUG_IGMP)
        !           757:                logit(LOG_DEBUG, 0, "DelVif: Not last source, g->al_sources --> %s",
        !           758:                      inet_fmt(g->al_sources->al_addr, s1, sizeof(s1)));
        !           759:            delete_leaf(vifi, cbk->source, g->al_addr);
        !           760:            free(cbk);
        !           761: 
        !           762:            return;    /* This was not last source for this interface */
        !           763:        }
        !           764:     }
        !           765: 
        !           766:     /*
        !           767:      * Group has expired
        !           768:      * delete all kernel cache entries with this group
        !           769:      */
        !           770:     if (g->al_query)
        !           771:        DeleteTimer(g->al_query);
        !           772: 
        !           773:     if (g->al_versiontimer)
        !           774:        DeleteTimer(g->al_versiontimer);
        !           775: 
        !           776:     if (IN_PIM_SSM_RANGE(g->al_addr)) {
        !           777:        inet_fmt(g->al_addr, s1, sizeof(s1));
        !           778:        inet_fmt(cbk->source, s2, sizeof(s2));
        !           779:        IF_DEBUG(DEBUG_IGMP)
        !           780:            logit(LOG_DEBUG, 0, "SSM range, source specific delete");
        !           781: 
        !           782:        /* delete (S,G) entry */
        !           783:        IF_DEBUG(DEBUG_IGMP)
        !           784:            logit(LOG_DEBUG, 0, "DelVif: vif:%d(%s), (S=%s,G=%s)", vifi, v->uv_name, s2, s1);
        !           785:        delete_leaf(vifi, cbk->source, g->al_addr);
        !           786:     } else {
        !           787:        delete_leaf(vifi, INADDR_ANY_N, g->al_addr);
        !           788:     }
        !           789: 
        !           790:     anp = &(v->uv_groups);
        !           791:     while ((a = *anp)) {
        !           792:        if (a == g) {
        !           793:            *anp = a->al_next;
        !           794:            free(a->al_sources);
        !           795:            free(a);
        !           796:        } else {
        !           797:            anp = &a->al_next;
        !           798:        }
        !           799:     }
        !           800: 
        !           801:     free(cbk);
        !           802: }
        !           803: 
        !           804: /*
        !           805:  * Set a timer to switch version back on a vif.
        !           806:  */
        !           807: static int SetVersionTimer(vifi_t vifi, struct listaddr *g)
        !           808: {
        !           809:     cbk_t *cbk;
        !           810: 
        !           811:     cbk = (cbk_t *)calloc(1, sizeof(cbk_t));
        !           812:     if (!cbk) {
        !           813:        logit(LOG_ERR, 0, "Failed calloc() in SetVersionTimer()\n");
        !           814:        return -1;
        !           815:     }
        !           816: 
        !           817:     cbk->vifi = vifi;
        !           818:     cbk->g = g;
        !           819: 
        !           820:     return timer_setTimer(IGMP_ROBUSTNESS_VARIABLE * igmp_query_interval + IGMP_QUERY_RESPONSE_INTERVAL,
        !           821:                          SwitchVersion, cbk);
        !           822: }
        !           823: 
        !           824: /*
        !           825:  * Set a timer to delete the record of a group membership on a vif.
        !           826:  */
        !           827: static int SetTimer(vifi_t vifi, struct listaddr *g, uint32_t source)
        !           828: {
        !           829:     cbk_t *cbk;
        !           830: 
        !           831:     cbk = (cbk_t *) calloc(1, sizeof(cbk_t));
        !           832:     if (!cbk) {
        !           833:        logit(LOG_ERR, 0, "Failed calloc() in SetTimer()");
        !           834:        return -1;
        !           835:     }
        !           836: 
        !           837:     cbk->vifi = vifi;
        !           838:     cbk->g = g;
        !           839:     cbk->source = source;
        !           840: 
        !           841:     IF_DEBUG(DEBUG_IGMP)
        !           842:        logit(LOG_DEBUG, 0, "Set delete timer for group: %s", inet_ntoa(*((struct in_addr *)&g->al_addr)));
        !           843: 
        !           844:     return timer_setTimer(g->al_timer, DelVif, cbk);
        !           845: }
        !           846: 
        !           847: 
        !           848: /*
        !           849:  * Delete a timer that was set above.
        !           850:  */
        !           851: static int DeleteTimer(int id)
        !           852: {
        !           853:     timer_clearTimer(id);
        !           854: 
        !           855:     return 0;
        !           856: }
        !           857: 
        !           858: 
        !           859: /*
        !           860:  * Send IGMP Query
        !           861:  */
        !           862: static void send_query(struct uvif *v, uint32_t group, int interval)
        !           863: {
        !           864:     if (v->uv_flags & VIFF_QUERIER) {
        !           865:        send_igmp(igmp_send_buf, v->uv_lcl_addr, group,
        !           866:                  IGMP_MEMBERSHIP_QUERY, interval, group != allhosts_group ? group : 0, 0);
        !           867:     }
        !           868: }
        !           869: 
        !           870: /*
        !           871:  * Send a group-specific query.
        !           872:  */
        !           873: static void SendQuery(void *arg)
        !           874: {
        !           875:     cbk_t *cbk = (cbk_t *)arg;
        !           876: 
        !           877:     IF_DEBUG(DEBUG_IGMP)
        !           878:        logit(LOG_DEBUG, 0, "SendQuery: Send IGMP v%s query", cbk->q_len == 4 ? "3" : "2");
        !           879:     send_query(&uvifs[cbk->vifi], cbk->g->al_addr, cbk->q_time);
        !           880:     cbk->g->al_query = 0;
        !           881:     free(cbk);
        !           882: }
        !           883: 
        !           884: 
        !           885: /*
        !           886:  * Set a timer to send a group-specific query.
        !           887:  */
        !           888: static int SetQueryTimer(struct listaddr *g, vifi_t vifi, int to_expire, int q_time, int q_len)
        !           889: {
        !           890:     cbk_t *cbk;
        !           891: 
        !           892:     cbk = (cbk_t *)calloc(1, sizeof(cbk_t));
        !           893:     if (!cbk) {
        !           894:        logit(LOG_ERR, 0, "Failed calloc() in SetQueryTimer()");
        !           895:        return -1;
        !           896:     }
        !           897: 
        !           898:     cbk->g = g;
        !           899:     cbk->q_time = q_time;
        !           900:     cbk->q_len = q_len;
        !           901:     cbk->vifi = vifi;
        !           902: 
        !           903:     return timer_setTimer(to_expire, SendQuery, cbk);
        !           904: }
        !           905: 
        !           906: /**
        !           907:  * Local Variables:
        !           908:  *  version-control: t
        !           909:  *  indent-tabs-mode: t
        !           910:  *  c-file-style: "ellemtel"
        !           911:  *  c-basic-offset: 4
        !           912:  * End:
        !           913:  */

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>