Annotation of embedaddon/pimdd/igmp_proto.c, revision 1.1
1.1 ! misho 1: /*
! 2: * Copyright (c) 1998 by the University of Oregon.
! 3: * All rights reserved.
! 4: *
! 5: * Permission to use, copy, modify, and distribute this software and
! 6: * its documentation in source and binary forms for lawful
! 7: * purposes and without fee is hereby granted, provided
! 8: * that the above copyright notice appear in all copies and that both
! 9: * the copyright notice and this permission notice appear in supporting
! 10: * documentation, and that any documentation, advertising materials,
! 11: * and other materials related to such distribution and use acknowledge
! 12: * that the software was developed by the University of Oregon.
! 13: * The name of the University of Oregon may not be used to endorse or
! 14: * promote products derived from this software without specific prior
! 15: * written permission.
! 16: *
! 17: * THE UNIVERSITY OF OREGON DOES NOT MAKE ANY REPRESENTATIONS
! 18: * ABOUT THE SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE. THIS SOFTWARE IS
! 19: * PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
! 20: * INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
! 21: * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND
! 22: * NON-INFRINGEMENT.
! 23: *
! 24: * IN NO EVENT SHALL UO, OR ANY OTHER CONTRIBUTOR BE LIABLE FOR ANY
! 25: * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES, WHETHER IN CONTRACT,
! 26: * TORT, OR OTHER FORM OF ACTION, ARISING OUT OF OR IN CONNECTION WITH,
! 27: * THE USE OR PERFORMANCE OF THIS SOFTWARE.
! 28: *
! 29: * Other copyrights might apply to parts of this software and are so
! 30: * noted when applicable.
! 31: */
! 32: /*
! 33: * Questions concerning this software should be directed to
! 34: * Kurt Windisch (kurtw@antc.uoregon.edu)
! 35: *
! 36: * $Id: igmp_proto.c,v 1.4 1998/05/29 21:58:22 kurtw Exp $
! 37: */
! 38: /*
! 39: * Part of this program has been derived from PIM sparse-mode pimd.
! 40: * The pimd program is covered by the license in the accompanying file
! 41: * named "LICENSE.pimd".
! 42: *
! 43: * The pimd program is COPYRIGHT 1998 by University of Southern California.
! 44: *
! 45: * Part of this program has been derived from mrouted.
! 46: * The mrouted program is covered by the license in the accompanying file
! 47: * named "LICENSE.mrouted".
! 48: *
! 49: * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
! 50: * Leland Stanford Junior University.
! 51: *
! 52: */
! 53:
! 54: #include "defs.h"
! 55:
! 56:
! 57: typedef struct {
! 58: vifi_t vifi;
! 59: struct listaddr *g;
! 60: int q_time;
! 61: } cbk_t;
! 62:
! 63:
! 64: /*
! 65: * Forward declarations.
! 66: */
! 67: static void DelVif __P((void *arg));
! 68: static int SetTimer __P((int vifi, struct listaddr *g));
! 69: static int DeleteTimer __P((int id));
! 70: static void SendQuery __P((void *arg));
! 71: static int SetQueryTimer __P((struct listaddr *g, vifi_t vifi, int to_expire,
! 72: int q_time));
! 73:
! 74:
! 75: /*
! 76: * Send group membership queries on that interface if I am querier.
! 77: */
! 78: void
! 79: query_groups(v)
! 80: register struct uvif *v;
! 81: {
! 82: register struct listaddr *g;
! 83:
! 84: v->uv_gq_timer = IGMP_QUERY_INTERVAL;
! 85: if (v->uv_flags & VIFF_QUERIER)
! 86: send_igmp(igmp_send_buf, v->uv_lcl_addr, allhosts_group,
! 87: IGMP_MEMBERSHIP_QUERY,
! 88: (v->uv_flags & VIFF_IGMPV1) ? 0 :
! 89: IGMP_MAX_HOST_REPORT_DELAY * IGMP_TIMER_SCALE, 0, 0);
! 90: /*
! 91: * Decrement the old-hosts-present timer for each
! 92: * active group on that vif.
! 93: */
! 94: for (g = v->uv_groups; g != NULL; g = g->al_next)
! 95: if (g->al_old > TIMER_INTERVAL)
! 96: g->al_old -= TIMER_INTERVAL;
! 97: else
! 98: g->al_old = 0;
! 99: }
! 100:
! 101:
! 102: /*
! 103: * Process an incoming host membership query
! 104: */
! 105: void
! 106: accept_membership_query(src, dst, group, tmo)
! 107: u_int32 src, dst, group;
! 108: int tmo;
! 109: {
! 110: register vifi_t vifi;
! 111: register struct uvif *v;
! 112:
! 113: /* Ignore my own membership query */
! 114: if (local_address(src) != NO_VIF)
! 115: return;
! 116:
! 117: /* TODO: modify for DVMRP?? */
! 118: if ((vifi = find_vif_direct(src)) == NO_VIF) {
! 119: IF_DEBUG(DEBUG_IGMP)
! 120: log(LOG_INFO, 0,
! 121: "ignoring group membership query from non-adjacent host %s",
! 122: inet_fmt(src, s1));
! 123: return;
! 124: }
! 125:
! 126: v = &uvifs[vifi];
! 127:
! 128: if ((tmo == 0 && !(v->uv_flags & VIFF_IGMPV1)) ||
! 129: (tmo != 0 && (v->uv_flags & VIFF_IGMPV1))) {
! 130: int i;
! 131:
! 132: /*
! 133: * Exponentially back-off warning rate
! 134: */
! 135: i = ++v->uv_igmpv1_warn;
! 136: while (i && !(i & 1))
! 137: i >>= 1;
! 138: if (i == 1)
! 139: log(LOG_WARNING, 0, "%s %s on vif %d, %s",
! 140: tmo == 0 ? "Received IGMPv1 report from"
! 141: : "Received IGMPv2 report from",
! 142: inet_fmt(src, s1),
! 143: vifi,
! 144: tmo == 0 ? "please configure vif for IGMPv1"
! 145: : "but I am configured for IGMPv1");
! 146: }
! 147:
! 148: if (v->uv_querier == NULL || v->uv_querier->al_addr != src) {
! 149: /*
! 150: * This might be:
! 151: * - A query from a new querier, with a lower source address
! 152: * than the current querier (who might be me)
! 153: * - A query from a new router that just started up and doesn't
! 154: * know who the querier is.
! 155: * - A query from the current querier
! 156: */
! 157: if (ntohl(src) < (v->uv_querier ? ntohl(v->uv_querier->al_addr)
! 158: : ntohl(v->uv_lcl_addr))) {
! 159: IF_DEBUG(DEBUG_IGMP)
! 160: log(LOG_DEBUG, 0, "new querier %s (was %s) on vif %d",
! 161: inet_fmt(src, s1),
! 162: v->uv_querier ?
! 163: inet_fmt(v->uv_querier->al_addr, s2) :
! 164: "me", vifi);
! 165: if (!v->uv_querier) {
! 166: v->uv_querier = (struct listaddr *)
! 167: malloc(sizeof(struct listaddr));
! 168: v->uv_querier->al_next = (struct listaddr *)NULL;
! 169: v->uv_querier->al_timer = 0;
! 170: v->uv_querier->al_genid = 0;
! 171: /* TODO: write the protocol version */
! 172: v->uv_querier->al_pv = 0;
! 173: v->uv_querier->al_mv = 0;
! 174: v->uv_querier->al_old = 0;
! 175: v->uv_querier->al_index = 0;
! 176: v->uv_querier->al_timerid = 0;
! 177: v->uv_querier->al_query = 0;
! 178: v->uv_querier->al_flags = 0;
! 179:
! 180: v->uv_flags &= ~VIFF_QUERIER;
! 181: }
! 182: v->uv_querier->al_addr = src;
! 183: time(&v->uv_querier->al_ctime);
! 184: }
! 185: }
! 186:
! 187: /*
! 188: * Reset the timer since we've received a query.
! 189: */
! 190: if (v->uv_querier && src == v->uv_querier->al_addr)
! 191: v->uv_querier->al_timer = 0;
! 192:
! 193: /*
! 194: * If this is a Group-Specific query which we did not source,
! 195: * we must set our membership timer to [Last Member Query Count] *
! 196: * the [Max Response Time] in the packet.
! 197: */
! 198: if (!(v->uv_flags & VIFF_IGMPV1) && group != 0 &&
! 199: src != v->uv_lcl_addr) {
! 200: register struct listaddr *g;
! 201:
! 202: IF_DEBUG(DEBUG_IGMP)
! 203: log(LOG_DEBUG, 0,
! 204: "%s for %s from %s on vif %d, timer %d",
! 205: "Group-specific membership query",
! 206: inet_fmt(group, s2), inet_fmt(src, s1), vifi, tmo);
! 207:
! 208: for (g = v->uv_groups; g != NULL; g = g->al_next) {
! 209: if (group == g->al_addr && g->al_query == 0) {
! 210: /* setup a timeout to remove the group membership */
! 211: if (g->al_timerid)
! 212: g->al_timerid = DeleteTimer(g->al_timerid);
! 213: g->al_timer = IGMP_LAST_MEMBER_QUERY_COUNT *
! 214: tmo / IGMP_TIMER_SCALE;
! 215: /* use al_query to record our presence in last-member state */
! 216: g->al_query = -1;
! 217: g->al_timerid = SetTimer(vifi, g);
! 218: IF_DEBUG(DEBUG_IGMP)
! 219: log(LOG_DEBUG, 0,
! 220: "timer for grp %s on vif %d set to %d",
! 221: inet_fmt(group, s2), vifi, g->al_timer);
! 222: break;
! 223: }
! 224: }
! 225: }
! 226: }
! 227:
! 228:
! 229: /*
! 230: * Process an incoming group membership report.
! 231: */
! 232: void
! 233: accept_group_report(src, dst, group, igmp_report_type)
! 234: u_int32 src, dst, group;
! 235: int igmp_report_type;
! 236: {
! 237: register vifi_t vifi;
! 238: register struct uvif *v;
! 239: register struct listaddr *g;
! 240:
! 241: if ((vifi = find_vif_direct_local(src)) == NO_VIF) {
! 242: IF_DEBUG(DEBUG_IGMP)
! 243: log(LOG_INFO, 0,
! 244: "ignoring group membership report from non-adjacent host %s",
! 245: inet_fmt(src, s1));
! 246: return;
! 247: }
! 248:
! 249: IF_DEBUG(DEBUG_IGMP)
! 250: log(LOG_INFO, 0,
! 251: "accepting IGMP group membership report: src %s, dst% s, grp %s",
! 252: inet_fmt(src, s1), inet_fmt(dst, s2), inet_fmt(group, s3));
! 253:
! 254: v = &uvifs[vifi];
! 255:
! 256: /*
! 257: * Look for the group in our group list; if found, reset its timer.
! 258: */
! 259: for (g = v->uv_groups; g != NULL; g = g->al_next) {
! 260: if (group == g->al_addr) {
! 261: if (igmp_report_type == IGMP_V1_MEMBERSHIP_REPORT)
! 262: g->al_old = DVMRP_OLD_AGE_THRESHOLD;
! 263:
! 264: g->al_reporter = src;
! 265:
! 266: /** delete old timers, set a timer for expiration **/
! 267: g->al_timer = IGMP_GROUP_MEMBERSHIP_INTERVAL;
! 268: if (g->al_query)
! 269: g->al_query = DeleteTimer(g->al_query);
! 270: if (g->al_timerid)
! 271: g->al_timerid = DeleteTimer(g->al_timerid);
! 272: g->al_timerid = SetTimer(vifi, g);
! 273: add_leaf(vifi, INADDR_ANY_N, group);
! 274: break;
! 275: }
! 276: }
! 277:
! 278: /*
! 279: * If not found, add it to the list and update kernel cache.
! 280: */
! 281: if (g == NULL) {
! 282: g = (struct listaddr *)malloc(sizeof(struct listaddr));
! 283: if (g == NULL)
! 284: log(LOG_ERR, 0, "ran out of memory"); /* fatal */
! 285:
! 286: g->al_addr = group;
! 287: if (igmp_report_type == IGMP_V1_MEMBERSHIP_REPORT)
! 288: g->al_old = DVMRP_OLD_AGE_THRESHOLD;
! 289: else
! 290: g->al_old = 0;
! 291:
! 292: /** set a timer for expiration **/
! 293: g->al_query = 0;
! 294: g->al_timer = IGMP_GROUP_MEMBERSHIP_INTERVAL;
! 295: g->al_reporter = src;
! 296: g->al_timerid = SetTimer(vifi, g);
! 297: g->al_next = v->uv_groups;
! 298: v->uv_groups = g;
! 299: time(&g->al_ctime);
! 300:
! 301: add_leaf(vifi, INADDR_ANY_N, group);
! 302: }
! 303: }
! 304:
! 305:
! 306: /* TODO: send PIM prune message if the last member? */
! 307: void
! 308: accept_leave_message(src, dst, group)
! 309: u_int32 src, dst, group;
! 310: {
! 311: register vifi_t vifi;
! 312: register struct uvif *v;
! 313: register struct listaddr *g;
! 314:
! 315: /* TODO: modify for DVMRP ??? */
! 316: if ((vifi = find_vif_direct_local(src)) == NO_VIF) {
! 317: IF_DEBUG(DEBUG_IGMP)
! 318: log(LOG_INFO, 0,
! 319: "ignoring group leave report from non-adjacent host %s",
! 320: inet_fmt(src, s1));
! 321: return;
! 322: }
! 323:
! 324: IF_DEBUG(DEBUG_IGMP)
! 325: log(LOG_INFO, 0,
! 326: "accepting IGMP leave message: src %s, dst% s, grp %s",
! 327: inet_fmt(src, s1), inet_fmt(dst, s2), inet_fmt(group, s3));
! 328:
! 329: v = &uvifs[vifi];
! 330:
! 331: if (!(v->uv_flags & (VIFF_QUERIER | VIFF_DR))
! 332: || (v->uv_flags & VIFF_IGMPV1))
! 333: return;
! 334:
! 335: /*
! 336: * Look for the group in our group list in order to set up a short-timeout
! 337: * query.
! 338: */
! 339: for (g = v->uv_groups; g != NULL; g = g->al_next) {
! 340: if (group == g->al_addr) {
! 341: IF_DEBUG(DEBUG_IGMP)
! 342: log(LOG_DEBUG, 0,
! 343: "[vif.c, _accept_leave_message] %d %d \n",
! 344: g->al_old, g->al_query);
! 345:
! 346: /* Ignore the leave message if there are old hosts present */
! 347: if (g->al_old)
! 348: return;
! 349:
! 350: /* still waiting for a reply to a query, ignore the leave */
! 351: if (g->al_query)
! 352: return;
! 353:
! 354: /** delete old timer set a timer for expiration **/
! 355: if (g->al_timerid)
! 356: g->al_timerid = DeleteTimer(g->al_timerid);
! 357:
! 358: #if IGMP_LAST_MEMBER_QUERY_COUNT != 2
! 359: /*
! 360: This code needs to be updated to keep a counter of the number
! 361: of queries remaining.
! 362: */
! 363: #endif
! 364: /** send a group specific querry **/
! 365: g->al_timer = IGMP_LAST_MEMBER_QUERY_INTERVAL *
! 366: (IGMP_LAST_MEMBER_QUERY_COUNT + 1);
! 367: if (v->uv_flags & VIFF_QUERIER)
! 368: send_igmp(igmp_send_buf, v->uv_lcl_addr, g->al_addr,
! 369: IGMP_MEMBERSHIP_QUERY,
! 370: IGMP_LAST_MEMBER_QUERY_INTERVAL * IGMP_TIMER_SCALE,
! 371: g->al_addr, 0);
! 372: g->al_query = SetQueryTimer(g, vifi,
! 373: IGMP_LAST_MEMBER_QUERY_INTERVAL,
! 374: IGMP_LAST_MEMBER_QUERY_INTERVAL * IGMP_TIMER_SCALE);
! 375: g->al_timerid = SetTimer(vifi, g);
! 376: break;
! 377: }
! 378: }
! 379: }
! 380:
! 381:
! 382: /*
! 383: * Time out record of a group membership on a vif
! 384: */
! 385: static void
! 386: DelVif(arg)
! 387: void *arg;
! 388: {
! 389: cbk_t *cbk = (cbk_t *)arg;
! 390: vifi_t vifi = cbk->vifi;
! 391: struct uvif *v = &uvifs[vifi];
! 392: struct listaddr *a, **anp, *g = cbk->g;
! 393:
! 394: /*
! 395: * Group has expired
! 396: * delete all kernel cache entries with this group
! 397: */
! 398: if (g->al_query)
! 399: DeleteTimer(g->al_query);
! 400:
! 401: delete_leaf(vifi, INADDR_ANY_N, g->al_addr);
! 402:
! 403: anp = &(v->uv_groups);
! 404: while ((a = *anp) != NULL) {
! 405: if (a == g) {
! 406: *anp = a->al_next;
! 407: free((char *)a);
! 408: } else {
! 409: anp = &a->al_next;
! 410: }
! 411: }
! 412:
! 413: free(cbk);
! 414: }
! 415:
! 416:
! 417: /*
! 418: * Set a timer to delete the record of a group membership on a vif.
! 419: */
! 420: static int
! 421: SetTimer(vifi, g)
! 422: vifi_t vifi;
! 423: struct listaddr *g;
! 424: {
! 425: cbk_t *cbk;
! 426:
! 427: cbk = (cbk_t *) malloc(sizeof(cbk_t));
! 428: cbk->vifi = vifi;
! 429: cbk->g = g;
! 430: return timer_setTimer(g->al_timer, DelVif, cbk);
! 431: }
! 432:
! 433:
! 434: /*
! 435: * Delete a timer that was set above.
! 436: */
! 437: static int
! 438: DeleteTimer(id)
! 439: int id;
! 440: {
! 441: timer_clearTimer(id);
! 442: return 0;
! 443: }
! 444:
! 445:
! 446: /*
! 447: * Send a group-specific query.
! 448: */
! 449: static void
! 450: SendQuery(arg)
! 451: void *arg;
! 452: {
! 453: cbk_t *cbk = (cbk_t *)arg;
! 454: register struct uvif *v = &uvifs[cbk->vifi];
! 455:
! 456: if (v->uv_flags & VIFF_QUERIER)
! 457: send_igmp(igmp_send_buf, v->uv_lcl_addr, cbk->g->al_addr,
! 458: IGMP_MEMBERSHIP_QUERY,
! 459: cbk->q_time, cbk->g->al_addr, 0);
! 460: cbk->g->al_query = 0;
! 461: free(cbk);
! 462: }
! 463:
! 464:
! 465: /*
! 466: * Set a timer to send a group-specific query.
! 467: */
! 468: static int
! 469: SetQueryTimer(g, vifi, to_expire, q_time)
! 470: struct listaddr *g;
! 471: vifi_t vifi;
! 472: int to_expire;
! 473: int q_time;
! 474: {
! 475: cbk_t *cbk;
! 476:
! 477: cbk = (cbk_t *) malloc(sizeof(cbk_t));
! 478: cbk->g = g;
! 479: cbk->q_time = q_time;
! 480: cbk->vifi = vifi;
! 481: return timer_setTimer(to_expire, SendQuery, cbk);
! 482: }
! 483:
! 484: /* Checks for IGMP group membership: returns TRUE if there is a receiver for the
! 485: * group on the given vif, or returns FALSE otherwise.
! 486: */
! 487: int check_grp_membership(v, group)
! 488: struct uvif *v;
! 489: u_int32 group;
! 490: {
! 491: register struct listaddr *g;
! 492:
! 493: /*
! 494: * Look for the group in our group list;
! 495: */
! 496: for (g = v->uv_groups; g != NULL; g = g->al_next) {
! 497: if (group == g->al_addr)
! 498: return TRUE;
! 499: }
! 500: return FALSE;
! 501: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>