Annotation of embedaddon/pimd/igmp_proto.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_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>