Annotation of embedaddon/quagga/pimd/pim_ifchannel.c, revision 1.1
1.1 ! misho 1: /*
! 2: PIM for Quagga
! 3: Copyright (C) 2008 Everton da Silva Marques
! 4:
! 5: This program is free software; you can redistribute it and/or modify
! 6: it under the terms of the GNU General Public License as published by
! 7: the Free Software Foundation; either version 2 of the License, or
! 8: (at your option) any later version.
! 9:
! 10: This program is distributed in the hope that it will be useful, but
! 11: WITHOUT ANY WARRANTY; without even the implied warranty of
! 12: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
! 13: General Public License for more details.
! 14:
! 15: You should have received a copy of the GNU General Public License
! 16: along with this program; see the file COPYING; if not, write to the
! 17: Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
! 18: MA 02110-1301 USA
! 19:
! 20: $QuaggaId: $Format:%an, %ai, %h$ $
! 21: */
! 22:
! 23: #include <zebra.h>
! 24:
! 25: #include "linklist.h"
! 26: #include "thread.h"
! 27: #include "memory.h"
! 28:
! 29: #include "pimd.h"
! 30: #include "pim_str.h"
! 31: #include "pim_iface.h"
! 32: #include "pim_ifchannel.h"
! 33: #include "pim_zebra.h"
! 34: #include "pim_time.h"
! 35: #include "pim_msg.h"
! 36: #include "pim_pim.h"
! 37: #include "pim_join.h"
! 38: #include "pim_rpf.h"
! 39: #include "pim_macro.h"
! 40:
! 41: void pim_ifchannel_free(struct pim_ifchannel *ch)
! 42: {
! 43: zassert(!ch->t_ifjoin_expiry_timer);
! 44: zassert(!ch->t_ifjoin_prune_pending_timer);
! 45: zassert(!ch->t_ifassert_timer);
! 46:
! 47: XFREE(MTYPE_PIM_IFCHANNEL, ch);
! 48: }
! 49:
! 50: void pim_ifchannel_delete(struct pim_ifchannel *ch)
! 51: {
! 52: struct pim_interface *pim_ifp;
! 53:
! 54: pim_ifp = ch->interface->info;
! 55: zassert(pim_ifp);
! 56:
! 57: if (ch->ifjoin_state != PIM_IFJOIN_NOINFO) {
! 58: pim_upstream_update_join_desired(ch->upstream);
! 59: }
! 60:
! 61: pim_upstream_del(ch->upstream);
! 62:
! 63: THREAD_OFF(ch->t_ifjoin_expiry_timer);
! 64: THREAD_OFF(ch->t_ifjoin_prune_pending_timer);
! 65: THREAD_OFF(ch->t_ifassert_timer);
! 66:
! 67: /*
! 68: notice that listnode_delete() can't be moved
! 69: into pim_ifchannel_free() because the later is
! 70: called by list_delete_all_node()
! 71: */
! 72: listnode_delete(pim_ifp->pim_ifchannel_list, ch);
! 73:
! 74: pim_ifchannel_free(ch);
! 75: }
! 76:
! 77: #define IFCHANNEL_NOINFO(ch) \
! 78: ( \
! 79: ((ch)->local_ifmembership == PIM_IFMEMBERSHIP_NOINFO) \
! 80: && \
! 81: ((ch)->ifjoin_state == PIM_IFJOIN_NOINFO) \
! 82: && \
! 83: ((ch)->ifassert_state == PIM_IFASSERT_NOINFO) \
! 84: )
! 85:
! 86: static void delete_on_noinfo(struct pim_ifchannel *ch)
! 87: {
! 88: if (IFCHANNEL_NOINFO(ch)) {
! 89:
! 90: /* In NOINFO state, timers should have been cleared */
! 91: zassert(!ch->t_ifjoin_expiry_timer);
! 92: zassert(!ch->t_ifjoin_prune_pending_timer);
! 93: zassert(!ch->t_ifassert_timer);
! 94:
! 95: pim_ifchannel_delete(ch);
! 96: }
! 97: }
! 98:
! 99: void pim_ifchannel_ifjoin_switch(const char *caller,
! 100: struct pim_ifchannel *ch,
! 101: enum pim_ifjoin_state new_state)
! 102: {
! 103: enum pim_ifjoin_state old_state = ch->ifjoin_state;
! 104:
! 105: if (old_state == new_state) {
! 106: if (PIM_DEBUG_PIM_EVENTS) {
! 107: zlog_debug("%s calledby %s: non-transition on state %d (%s)",
! 108: __PRETTY_FUNCTION__, caller, new_state,
! 109: pim_ifchannel_ifjoin_name(new_state));
! 110: }
! 111: return;
! 112: }
! 113:
! 114: zassert(old_state != new_state);
! 115:
! 116: ch->ifjoin_state = new_state;
! 117:
! 118: /* Transition to/from NOINFO ? */
! 119: if (
! 120: (old_state == PIM_IFJOIN_NOINFO)
! 121: ||
! 122: (new_state == PIM_IFJOIN_NOINFO)
! 123: ) {
! 124:
! 125: if (PIM_DEBUG_PIM_EVENTS) {
! 126: char src_str[100];
! 127: char grp_str[100];
! 128: pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
! 129: pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
! 130: zlog_debug("PIM_IFCHANNEL_%s: (S,G)=(%s,%s) on interface %s",
! 131: ((new_state == PIM_IFJOIN_NOINFO) ? "DOWN" : "UP"),
! 132: src_str, grp_str, ch->interface->name);
! 133: }
! 134:
! 135: /*
! 136: Record uptime of state transition to/from NOINFO
! 137: */
! 138: ch->ifjoin_creation = pim_time_monotonic_sec();
! 139:
! 140: pim_upstream_update_join_desired(ch->upstream);
! 141: pim_ifchannel_update_could_assert(ch);
! 142: pim_ifchannel_update_assert_tracking_desired(ch);
! 143: }
! 144: }
! 145:
! 146: const char *pim_ifchannel_ifjoin_name(enum pim_ifjoin_state ifjoin_state)
! 147: {
! 148: switch (ifjoin_state) {
! 149: case PIM_IFJOIN_NOINFO: return "NOINFO";
! 150: case PIM_IFJOIN_JOIN: return "JOIN";
! 151: case PIM_IFJOIN_PRUNE_PENDING: return "PRUNEP";
! 152: }
! 153:
! 154: return "ifjoin_bad_state";
! 155: }
! 156:
! 157: const char *pim_ifchannel_ifassert_name(enum pim_ifassert_state ifassert_state)
! 158: {
! 159: switch (ifassert_state) {
! 160: case PIM_IFASSERT_NOINFO: return "NOINFO";
! 161: case PIM_IFASSERT_I_AM_WINNER: return "WINNER";
! 162: case PIM_IFASSERT_I_AM_LOSER: return "LOSER";
! 163: }
! 164:
! 165: return "ifassert_bad_state";
! 166: }
! 167:
! 168: /*
! 169: RFC 4601: 4.6.5. Assert State Macros
! 170:
! 171: AssertWinner(S,G,I) defaults to NULL and AssertWinnerMetric(S,G,I)
! 172: defaults to Infinity when in the NoInfo state.
! 173: */
! 174: void reset_ifassert_state(struct pim_ifchannel *ch)
! 175: {
! 176: THREAD_OFF(ch->t_ifassert_timer);
! 177:
! 178: pim_ifassert_winner_set(ch,
! 179: PIM_IFASSERT_NOINFO,
! 180: qpim_inaddr_any,
! 181: qpim_infinite_assert_metric);
! 182: }
! 183:
! 184: static struct pim_ifchannel *pim_ifchannel_new(struct interface *ifp,
! 185: struct in_addr source_addr,
! 186: struct in_addr group_addr)
! 187: {
! 188: struct pim_ifchannel *ch;
! 189: struct pim_interface *pim_ifp;
! 190: struct pim_upstream *up;
! 191:
! 192: pim_ifp = ifp->info;
! 193: zassert(pim_ifp);
! 194:
! 195: up = pim_upstream_add(source_addr, group_addr);
! 196: if (!up) {
! 197: char src_str[100];
! 198: char grp_str[100];
! 199: pim_inet4_dump("<src?>", source_addr, src_str, sizeof(src_str));
! 200: pim_inet4_dump("<grp?>", group_addr, grp_str, sizeof(grp_str));
! 201: zlog_err("%s: could not attach upstream (S,G)=(%s,%s) on interface %s",
! 202: __PRETTY_FUNCTION__,
! 203: src_str, grp_str, ifp->name);
! 204: return 0;
! 205: }
! 206:
! 207: ch = XMALLOC(MTYPE_PIM_IFCHANNEL, sizeof(*ch));
! 208: if (!ch) {
! 209: zlog_err("%s: PIM XMALLOC(%zu) failure",
! 210: __PRETTY_FUNCTION__, sizeof(*ch));
! 211: return 0;
! 212: }
! 213:
! 214: ch->flags = 0;
! 215: ch->upstream = up;
! 216: ch->interface = ifp;
! 217: ch->source_addr = source_addr;
! 218: ch->group_addr = group_addr;
! 219: ch->local_ifmembership = PIM_IFMEMBERSHIP_NOINFO;
! 220:
! 221: ch->ifjoin_state = PIM_IFJOIN_NOINFO;
! 222: ch->t_ifjoin_expiry_timer = 0;
! 223: ch->t_ifjoin_prune_pending_timer = 0;
! 224: ch->ifjoin_creation = 0;
! 225:
! 226: /* Assert state */
! 227: ch->t_ifassert_timer = 0;
! 228: reset_ifassert_state(ch);
! 229: if (pim_macro_ch_could_assert_eval(ch))
! 230: PIM_IF_FLAG_SET_COULD_ASSERT(ch->flags);
! 231: else
! 232: PIM_IF_FLAG_UNSET_COULD_ASSERT(ch->flags);
! 233:
! 234: if (pim_macro_assert_tracking_desired_eval(ch))
! 235: PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(ch->flags);
! 236: else
! 237: PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(ch->flags);
! 238:
! 239: ch->ifassert_my_metric = pim_macro_ch_my_assert_metric_eval(ch);
! 240:
! 241: /* Attach to list */
! 242: listnode_add(pim_ifp->pim_ifchannel_list, ch);
! 243:
! 244: zassert(IFCHANNEL_NOINFO(ch));
! 245:
! 246: return ch;
! 247: }
! 248:
! 249: struct pim_ifchannel *pim_ifchannel_find(struct interface *ifp,
! 250: struct in_addr source_addr,
! 251: struct in_addr group_addr)
! 252: {
! 253: struct pim_interface *pim_ifp;
! 254: struct listnode *ch_node;
! 255: struct pim_ifchannel *ch;
! 256:
! 257: zassert(ifp);
! 258:
! 259: pim_ifp = ifp->info;
! 260:
! 261: if (!pim_ifp) {
! 262: char src_str[100];
! 263: char grp_str[100];
! 264: pim_inet4_dump("<src?>", source_addr, src_str, sizeof(src_str));
! 265: pim_inet4_dump("<grp?>", group_addr, grp_str, sizeof(grp_str));
! 266: zlog_warn("%s: (S,G)=(%s,%s): multicast not enabled on interface %s",
! 267: __PRETTY_FUNCTION__,
! 268: src_str, grp_str,
! 269: ifp->name);
! 270: return 0;
! 271: }
! 272:
! 273: for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_ifchannel_list, ch_node, ch)) {
! 274: if (
! 275: (source_addr.s_addr == ch->source_addr.s_addr) &&
! 276: (group_addr.s_addr == ch->group_addr.s_addr)
! 277: ) {
! 278: return ch;
! 279: }
! 280: }
! 281:
! 282: return 0;
! 283: }
! 284:
! 285: static void ifmembership_set(struct pim_ifchannel *ch,
! 286: enum pim_ifmembership membership)
! 287: {
! 288: if (ch->local_ifmembership == membership)
! 289: return;
! 290:
! 291: if (PIM_DEBUG_PIM_EVENTS) {
! 292: char src_str[100];
! 293: char grp_str[100];
! 294: pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
! 295: pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
! 296: zlog_debug("%s: (S,G)=(%s,%s) membership now is %s on interface %s",
! 297: __PRETTY_FUNCTION__,
! 298: src_str, grp_str,
! 299: membership == PIM_IFMEMBERSHIP_INCLUDE ? "INCLUDE" : "NOINFO",
! 300: ch->interface->name);
! 301: }
! 302:
! 303: ch->local_ifmembership = membership;
! 304:
! 305: pim_upstream_update_join_desired(ch->upstream);
! 306: pim_ifchannel_update_could_assert(ch);
! 307: pim_ifchannel_update_assert_tracking_desired(ch);
! 308: }
! 309:
! 310:
! 311: void pim_ifchannel_membership_clear(struct interface *ifp)
! 312: {
! 313: struct pim_interface *pim_ifp;
! 314: struct listnode *ch_node;
! 315: struct pim_ifchannel *ch;
! 316:
! 317: pim_ifp = ifp->info;
! 318: zassert(pim_ifp);
! 319:
! 320: for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_ifchannel_list, ch_node, ch)) {
! 321: ifmembership_set(ch, PIM_IFMEMBERSHIP_NOINFO);
! 322: }
! 323: }
! 324:
! 325: void pim_ifchannel_delete_on_noinfo(struct interface *ifp)
! 326: {
! 327: struct pim_interface *pim_ifp;
! 328: struct listnode *node;
! 329: struct listnode *next_node;
! 330: struct pim_ifchannel *ch;
! 331:
! 332: pim_ifp = ifp->info;
! 333: zassert(pim_ifp);
! 334:
! 335: for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, node, next_node, ch)) {
! 336: delete_on_noinfo(ch);
! 337: }
! 338: }
! 339:
! 340: struct pim_ifchannel *pim_ifchannel_add(struct interface *ifp,
! 341: struct in_addr source_addr,
! 342: struct in_addr group_addr)
! 343: {
! 344: struct pim_ifchannel *ch;
! 345: char src_str[100];
! 346: char grp_str[100];
! 347:
! 348: ch = pim_ifchannel_find(ifp, source_addr, group_addr);
! 349: if (ch)
! 350: return ch;
! 351:
! 352: ch = pim_ifchannel_new(ifp, source_addr, group_addr);
! 353: if (ch)
! 354: return ch;
! 355:
! 356: pim_inet4_dump("<src?>", source_addr, src_str, sizeof(src_str));
! 357: pim_inet4_dump("<grp?>", group_addr, grp_str, sizeof(grp_str));
! 358: zlog_warn("%s: pim_ifchannel_new() failure for (S,G)=(%s,%s) on interface %s",
! 359: __PRETTY_FUNCTION__,
! 360: src_str, grp_str, ifp->name);
! 361:
! 362: return 0;
! 363: }
! 364:
! 365: static void ifjoin_to_noinfo(struct pim_ifchannel *ch)
! 366: {
! 367: pim_forward_stop(ch);
! 368: pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch, PIM_IFJOIN_NOINFO);
! 369: delete_on_noinfo(ch);
! 370: }
! 371:
! 372: static int on_ifjoin_expiry_timer(struct thread *t)
! 373: {
! 374: struct pim_ifchannel *ch;
! 375:
! 376: zassert(t);
! 377: ch = THREAD_ARG(t);
! 378: zassert(ch);
! 379:
! 380: ch->t_ifjoin_expiry_timer = 0;
! 381:
! 382: zassert(ch->ifjoin_state == PIM_IFJOIN_JOIN);
! 383:
! 384: ifjoin_to_noinfo(ch);
! 385: /* ch may have been deleted */
! 386:
! 387: return 0;
! 388: }
! 389:
! 390: static void prune_echo(struct interface *ifp,
! 391: struct in_addr source_addr,
! 392: struct in_addr group_addr)
! 393: {
! 394: struct pim_interface *pim_ifp;
! 395: struct in_addr neigh_dst_addr;
! 396:
! 397: pim_ifp = ifp->info;
! 398: zassert(pim_ifp);
! 399:
! 400: neigh_dst_addr = pim_ifp->primary_address;
! 401:
! 402: if (PIM_DEBUG_PIM_EVENTS) {
! 403: char source_str[100];
! 404: char group_str[100];
! 405: char neigh_dst_str[100];
! 406: pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
! 407: pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str));
! 408: pim_inet4_dump("<neigh?>", neigh_dst_addr, neigh_dst_str, sizeof(neigh_dst_str));
! 409: zlog_debug("%s: sending PruneEcho(S,G)=(%s,%s) to upstream=%s on interface %s",
! 410: __PRETTY_FUNCTION__, source_str, group_str, neigh_dst_str, ifp->name);
! 411: }
! 412:
! 413: pim_joinprune_send(ifp, neigh_dst_addr, source_addr, group_addr,
! 414: 0 /* boolean: send_join=false (prune) */);
! 415: }
! 416:
! 417: static int on_ifjoin_prune_pending_timer(struct thread *t)
! 418: {
! 419: struct pim_ifchannel *ch;
! 420: int send_prune_echo; /* boolean */
! 421: struct interface *ifp;
! 422: struct pim_interface *pim_ifp;
! 423: struct in_addr ch_source;
! 424: struct in_addr ch_group;
! 425:
! 426: zassert(t);
! 427: ch = THREAD_ARG(t);
! 428: zassert(ch);
! 429:
! 430: ch->t_ifjoin_prune_pending_timer = 0;
! 431:
! 432: zassert(ch->ifjoin_state == PIM_IFJOIN_PRUNE_PENDING);
! 433:
! 434: /* Send PruneEcho(S,G) ? */
! 435: ifp = ch->interface;
! 436: pim_ifp = ifp->info;
! 437: send_prune_echo = (listcount(pim_ifp->pim_neighbor_list) > 1);
! 438:
! 439: /* Save (S,G) */
! 440: ch_source = ch->source_addr;
! 441: ch_group = ch->group_addr;
! 442:
! 443: ifjoin_to_noinfo(ch);
! 444: /* from here ch may have been deleted */
! 445:
! 446: if (send_prune_echo)
! 447: prune_echo(ifp, ch_source, ch_group);
! 448:
! 449: return 0;
! 450: }
! 451:
! 452: static void check_recv_upstream(int is_join,
! 453: struct interface *recv_ifp,
! 454: struct in_addr upstream,
! 455: struct in_addr source_addr,
! 456: struct in_addr group_addr,
! 457: uint8_t source_flags,
! 458: int holdtime)
! 459: {
! 460: struct pim_upstream *up;
! 461:
! 462: /* Upstream (S,G) in Joined state ? */
! 463: up = pim_upstream_find(source_addr, group_addr);
! 464: if (!up)
! 465: return;
! 466: if (up->join_state != PIM_UPSTREAM_JOINED)
! 467: return;
! 468:
! 469: /* Upstream (S,G) in Joined state */
! 470:
! 471: if (PIM_INADDR_IS_ANY(up->rpf.rpf_addr)) {
! 472: /* RPF'(S,G) not found */
! 473: char src_str[100];
! 474: char grp_str[100];
! 475: pim_inet4_dump("<src?>", source_addr, src_str, sizeof(src_str));
! 476: pim_inet4_dump("<grp?>", group_addr, grp_str, sizeof(grp_str));
! 477: zlog_warn("%s %s: RPF'(%s,%s) not found",
! 478: __FILE__, __PRETTY_FUNCTION__,
! 479: src_str, grp_str);
! 480: return;
! 481: }
! 482:
! 483: /* upstream directed to RPF'(S,G) ? */
! 484: if (upstream.s_addr != up->rpf.rpf_addr.s_addr) {
! 485: char src_str[100];
! 486: char grp_str[100];
! 487: char up_str[100];
! 488: char rpf_str[100];
! 489: pim_inet4_dump("<src?>", source_addr, src_str, sizeof(src_str));
! 490: pim_inet4_dump("<grp?>", group_addr, grp_str, sizeof(grp_str));
! 491: pim_inet4_dump("<up?>", upstream, up_str, sizeof(up_str));
! 492: pim_inet4_dump("<rpf?>", up->rpf.rpf_addr, rpf_str, sizeof(rpf_str));
! 493: zlog_warn("%s %s: (S,G)=(%s,%s) upstream=%s not directed to RPF'(S,G)=%s on interface %s",
! 494: __FILE__, __PRETTY_FUNCTION__,
! 495: src_str, grp_str,
! 496: up_str, rpf_str, recv_ifp->name);
! 497: return;
! 498: }
! 499: /* upstream directed to RPF'(S,G) */
! 500:
! 501: if (is_join) {
! 502: /* Join(S,G) to RPF'(S,G) */
! 503: pim_upstream_join_suppress(up, up->rpf.rpf_addr, holdtime);
! 504: return;
! 505: }
! 506:
! 507: /* Prune to RPF'(S,G) */
! 508:
! 509: if (source_flags & PIM_RPT_BIT_MASK) {
! 510: if (source_flags & PIM_WILDCARD_BIT_MASK) {
! 511: /* Prune(*,G) to RPF'(S,G) */
! 512: pim_upstream_join_timer_decrease_to_t_override("Prune(*,G)",
! 513: up, up->rpf.rpf_addr);
! 514: return;
! 515: }
! 516:
! 517: /* Prune(S,G,rpt) to RPF'(S,G) */
! 518: pim_upstream_join_timer_decrease_to_t_override("Prune(S,G,rpt)",
! 519: up, up->rpf.rpf_addr);
! 520: return;
! 521: }
! 522:
! 523: /* Prune(S,G) to RPF'(S,G) */
! 524: pim_upstream_join_timer_decrease_to_t_override("Prune(S,G)", up,
! 525: up->rpf.rpf_addr);
! 526: }
! 527:
! 528: static int nonlocal_upstream(int is_join,
! 529: struct interface *recv_ifp,
! 530: struct in_addr upstream,
! 531: struct in_addr source_addr,
! 532: struct in_addr group_addr,
! 533: uint8_t source_flags,
! 534: uint16_t holdtime)
! 535: {
! 536: struct pim_interface *recv_pim_ifp;
! 537: int is_local; /* boolean */
! 538:
! 539: recv_pim_ifp = recv_ifp->info;
! 540: zassert(recv_pim_ifp);
! 541:
! 542: is_local = (upstream.s_addr == recv_pim_ifp->primary_address.s_addr);
! 543:
! 544: if (PIM_DEBUG_PIM_TRACE) {
! 545: char up_str[100];
! 546: char src_str[100];
! 547: char grp_str[100];
! 548: pim_inet4_dump("<upstream?>", upstream, up_str, sizeof(up_str));
! 549: pim_inet4_dump("<src?>", source_addr, src_str, sizeof(src_str));
! 550: pim_inet4_dump("<grp?>", group_addr, grp_str, sizeof(grp_str));
! 551: zlog_warn("%s: recv %s (S,G)=(%s,%s) to %s upstream=%s on %s",
! 552: __PRETTY_FUNCTION__,
! 553: is_join ? "join" : "prune",
! 554: src_str, grp_str,
! 555: is_local ? "local" : "non-local",
! 556: up_str, recv_ifp->name);
! 557: }
! 558:
! 559: if (is_local)
! 560: return 0;
! 561:
! 562: /*
! 563: Since recv upstream addr was not directed to our primary
! 564: address, check if we should react to it in any way.
! 565: */
! 566: check_recv_upstream(is_join, recv_ifp, upstream, source_addr, group_addr,
! 567: source_flags, holdtime);
! 568:
! 569: return 1; /* non-local */
! 570: }
! 571:
! 572: void pim_ifchannel_join_add(struct interface *ifp,
! 573: struct in_addr neigh_addr,
! 574: struct in_addr upstream,
! 575: struct in_addr source_addr,
! 576: struct in_addr group_addr,
! 577: uint8_t source_flags,
! 578: uint16_t holdtime)
! 579: {
! 580: struct pim_interface *pim_ifp;
! 581: struct pim_ifchannel *ch;
! 582:
! 583: if (nonlocal_upstream(1 /* join */, ifp, upstream,
! 584: source_addr, group_addr, source_flags, holdtime)) {
! 585: return;
! 586: }
! 587:
! 588: ch = pim_ifchannel_add(ifp, source_addr, group_addr);
! 589: if (!ch)
! 590: return;
! 591:
! 592: /*
! 593: RFC 4601: 4.6.1. (S,G) Assert Message State Machine
! 594:
! 595: Transitions from "I am Assert Loser" State
! 596:
! 597: Receive Join(S,G) on Interface I
! 598:
! 599: We receive a Join(S,G) that has the Upstream Neighbor Address
! 600: field set to my primary IP address on interface I. The action is
! 601: to transition to NoInfo state, delete this (S,G) assert state
! 602: (Actions A5 below), and allow the normal PIM Join/Prune mechanisms
! 603: to operate.
! 604:
! 605: Notice: The nonlocal_upstream() test above ensures the upstream
! 606: address of the join message is our primary address.
! 607: */
! 608: if (ch->ifassert_state == PIM_IFASSERT_I_AM_LOSER) {
! 609: char src_str[100];
! 610: char grp_str[100];
! 611: char neigh_str[100];
! 612: pim_inet4_dump("<src?>", source_addr, src_str, sizeof(src_str));
! 613: pim_inet4_dump("<grp?>", group_addr, grp_str, sizeof(grp_str));
! 614: pim_inet4_dump("<neigh?>", neigh_addr, neigh_str, sizeof(neigh_str));
! 615: zlog_warn("%s: Assert Loser recv Join(%s,%s) from %s on %s",
! 616: __PRETTY_FUNCTION__,
! 617: src_str, grp_str, neigh_str, ifp->name);
! 618:
! 619: assert_action_a5(ch);
! 620: }
! 621:
! 622: pim_ifp = ifp->info;
! 623: zassert(pim_ifp);
! 624:
! 625: switch (ch->ifjoin_state) {
! 626: case PIM_IFJOIN_NOINFO:
! 627: pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch, PIM_IFJOIN_JOIN);
! 628: if (pim_macro_chisin_oiflist(ch)) {
! 629: pim_forward_start(ch);
! 630: }
! 631: break;
! 632: case PIM_IFJOIN_JOIN:
! 633: zassert(!ch->t_ifjoin_prune_pending_timer);
! 634:
! 635: /*
! 636: In the JOIN state ch->t_ifjoin_expiry_timer may be NULL due to a
! 637: previously received join message with holdtime=0xFFFF.
! 638: */
! 639: if (ch->t_ifjoin_expiry_timer) {
! 640: unsigned long remain =
! 641: thread_timer_remain_second(ch->t_ifjoin_expiry_timer);
! 642: if (remain > holdtime) {
! 643: /*
! 644: RFC 4601: 4.5.3. Receiving (S,G) Join/Prune Messages
! 645:
! 646: Transitions from Join State
! 647:
! 648: The (S,G) downstream state machine on interface I remains in
! 649: Join state, and the Expiry Timer (ET) is restarted, set to
! 650: maximum of its current value and the HoldTime from the
! 651: triggering Join/Prune message.
! 652:
! 653: Conclusion: Do not change the ET if the current value is
! 654: higher than the received join holdtime.
! 655: */
! 656: return;
! 657: }
! 658: }
! 659: THREAD_OFF(ch->t_ifjoin_expiry_timer);
! 660: break;
! 661: case PIM_IFJOIN_PRUNE_PENDING:
! 662: zassert(!ch->t_ifjoin_expiry_timer);
! 663: zassert(ch->t_ifjoin_prune_pending_timer);
! 664: THREAD_OFF(ch->t_ifjoin_prune_pending_timer);
! 665: pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch, PIM_IFJOIN_JOIN);
! 666: break;
! 667: }
! 668:
! 669: zassert(!IFCHANNEL_NOINFO(ch));
! 670:
! 671: if (holdtime != 0xFFFF) {
! 672: THREAD_TIMER_ON(master, ch->t_ifjoin_expiry_timer,
! 673: on_ifjoin_expiry_timer,
! 674: ch, holdtime);
! 675: }
! 676: }
! 677:
! 678: void pim_ifchannel_prune(struct interface *ifp,
! 679: struct in_addr upstream,
! 680: struct in_addr source_addr,
! 681: struct in_addr group_addr,
! 682: uint8_t source_flags,
! 683: uint16_t holdtime)
! 684: {
! 685: struct pim_ifchannel *ch;
! 686: int jp_override_interval_msec;
! 687:
! 688: if (nonlocal_upstream(0 /* prune */, ifp, upstream,
! 689: source_addr, group_addr, source_flags, holdtime)) {
! 690: return;
! 691: }
! 692:
! 693: ch = pim_ifchannel_add(ifp, source_addr, group_addr);
! 694: if (!ch)
! 695: return;
! 696:
! 697: switch (ch->ifjoin_state) {
! 698: case PIM_IFJOIN_NOINFO:
! 699: case PIM_IFJOIN_PRUNE_PENDING:
! 700: /* nothing to do */
! 701: break;
! 702: case PIM_IFJOIN_JOIN:
! 703: {
! 704: struct pim_interface *pim_ifp;
! 705:
! 706: pim_ifp = ifp->info;
! 707:
! 708: zassert(ch->t_ifjoin_expiry_timer);
! 709: zassert(!ch->t_ifjoin_prune_pending_timer);
! 710:
! 711: THREAD_OFF(ch->t_ifjoin_expiry_timer);
! 712:
! 713: pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch, PIM_IFJOIN_PRUNE_PENDING);
! 714:
! 715: if (listcount(pim_ifp->pim_neighbor_list) > 1) {
! 716: jp_override_interval_msec = pim_if_jp_override_interval_msec(ifp);
! 717: }
! 718: else {
! 719: jp_override_interval_msec = 0; /* schedule to expire immediately */
! 720: /* If we called ifjoin_prune() directly instead, care should
! 721: be taken not to use "ch" afterwards since it would be
! 722: deleted. */
! 723: }
! 724:
! 725: THREAD_TIMER_MSEC_ON(master, ch->t_ifjoin_prune_pending_timer,
! 726: on_ifjoin_prune_pending_timer,
! 727: ch, jp_override_interval_msec);
! 728:
! 729: zassert(!ch->t_ifjoin_expiry_timer);
! 730: zassert(ch->t_ifjoin_prune_pending_timer);
! 731: }
! 732: break;
! 733: }
! 734:
! 735: }
! 736:
! 737: void pim_ifchannel_local_membership_add(struct interface *ifp,
! 738: struct in_addr source_addr,
! 739: struct in_addr group_addr)
! 740: {
! 741: struct pim_ifchannel *ch;
! 742: struct pim_interface *pim_ifp;
! 743:
! 744: /* PIM enabled on interface? */
! 745: pim_ifp = ifp->info;
! 746: if (!pim_ifp)
! 747: return;
! 748: if (!PIM_IF_TEST_PIM(pim_ifp->options))
! 749: return;
! 750:
! 751: ch = pim_ifchannel_add(ifp, source_addr, group_addr);
! 752: if (!ch) {
! 753: return;
! 754: }
! 755:
! 756: ifmembership_set(ch, PIM_IFMEMBERSHIP_INCLUDE);
! 757:
! 758: zassert(!IFCHANNEL_NOINFO(ch));
! 759: }
! 760:
! 761: void pim_ifchannel_local_membership_del(struct interface *ifp,
! 762: struct in_addr source_addr,
! 763: struct in_addr group_addr)
! 764: {
! 765: struct pim_ifchannel *ch;
! 766: struct pim_interface *pim_ifp;
! 767:
! 768: /* PIM enabled on interface? */
! 769: pim_ifp = ifp->info;
! 770: if (!pim_ifp)
! 771: return;
! 772: if (!PIM_IF_TEST_PIM(pim_ifp->options))
! 773: return;
! 774:
! 775: ch = pim_ifchannel_find(ifp, source_addr, group_addr);
! 776: if (!ch)
! 777: return;
! 778:
! 779: ifmembership_set(ch, PIM_IFMEMBERSHIP_NOINFO);
! 780:
! 781: delete_on_noinfo(ch);
! 782: }
! 783:
! 784: void pim_ifchannel_update_could_assert(struct pim_ifchannel *ch)
! 785: {
! 786: int old_couldassert = PIM_FORCE_BOOLEAN(PIM_IF_FLAG_TEST_COULD_ASSERT(ch->flags));
! 787: int new_couldassert = PIM_FORCE_BOOLEAN(pim_macro_ch_could_assert_eval(ch));
! 788:
! 789: if (new_couldassert == old_couldassert)
! 790: return;
! 791:
! 792: if (PIM_DEBUG_PIM_EVENTS) {
! 793: char src_str[100];
! 794: char grp_str[100];
! 795: pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
! 796: pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
! 797: zlog_debug("%s: CouldAssert(%s,%s,%s) changed from %d to %d",
! 798: __PRETTY_FUNCTION__,
! 799: src_str, grp_str, ch->interface->name,
! 800: old_couldassert, new_couldassert);
! 801: }
! 802:
! 803: if (new_couldassert) {
! 804: /* CouldAssert(S,G,I) switched from FALSE to TRUE */
! 805: PIM_IF_FLAG_SET_COULD_ASSERT(ch->flags);
! 806: }
! 807: else {
! 808: /* CouldAssert(S,G,I) switched from TRUE to FALSE */
! 809: PIM_IF_FLAG_UNSET_COULD_ASSERT(ch->flags);
! 810:
! 811: if (ch->ifassert_state == PIM_IFASSERT_I_AM_WINNER) {
! 812: assert_action_a4(ch);
! 813: }
! 814: }
! 815:
! 816: pim_ifchannel_update_my_assert_metric(ch);
! 817: }
! 818:
! 819: /*
! 820: my_assert_metric may be affected by:
! 821:
! 822: CouldAssert(S,G)
! 823: pim_ifp->primary_address
! 824: rpf->source_nexthop.mrib_metric_preference;
! 825: rpf->source_nexthop.mrib_route_metric;
! 826: */
! 827: void pim_ifchannel_update_my_assert_metric(struct pim_ifchannel *ch)
! 828: {
! 829: struct pim_assert_metric my_metric_new = pim_macro_ch_my_assert_metric_eval(ch);
! 830:
! 831: if (pim_assert_metric_match(&my_metric_new, &ch->ifassert_my_metric))
! 832: return;
! 833:
! 834: if (PIM_DEBUG_PIM_EVENTS) {
! 835: char src_str[100];
! 836: char grp_str[100];
! 837: char old_addr_str[100];
! 838: char new_addr_str[100];
! 839: pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
! 840: pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
! 841: pim_inet4_dump("<old_addr?>", ch->ifassert_my_metric.ip_address, old_addr_str, sizeof(old_addr_str));
! 842: pim_inet4_dump("<new_addr?>", my_metric_new.ip_address, new_addr_str, sizeof(new_addr_str));
! 843: zlog_debug("%s: my_assert_metric(%s,%s,%s) changed from %u,%u,%u,%s to %u,%u,%u,%s",
! 844: __PRETTY_FUNCTION__,
! 845: src_str, grp_str, ch->interface->name,
! 846: ch->ifassert_my_metric.rpt_bit_flag,
! 847: ch->ifassert_my_metric.metric_preference,
! 848: ch->ifassert_my_metric.route_metric,
! 849: old_addr_str,
! 850: my_metric_new.rpt_bit_flag,
! 851: my_metric_new.metric_preference,
! 852: my_metric_new.route_metric,
! 853: new_addr_str);
! 854: }
! 855:
! 856: ch->ifassert_my_metric = my_metric_new;
! 857:
! 858: if (pim_assert_metric_better(&ch->ifassert_my_metric,
! 859: &ch->ifassert_winner_metric)) {
! 860: assert_action_a5(ch);
! 861: }
! 862: }
! 863:
! 864: void pim_ifchannel_update_assert_tracking_desired(struct pim_ifchannel *ch)
! 865: {
! 866: int old_atd = PIM_FORCE_BOOLEAN(PIM_IF_FLAG_TEST_ASSERT_TRACKING_DESIRED(ch->flags));
! 867: int new_atd = PIM_FORCE_BOOLEAN(pim_macro_assert_tracking_desired_eval(ch));
! 868:
! 869: if (new_atd == old_atd)
! 870: return;
! 871:
! 872: if (PIM_DEBUG_PIM_EVENTS) {
! 873: char src_str[100];
! 874: char grp_str[100];
! 875: pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
! 876: pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
! 877: zlog_debug("%s: AssertTrackingDesired(%s,%s,%s) changed from %d to %d",
! 878: __PRETTY_FUNCTION__,
! 879: src_str, grp_str, ch->interface->name,
! 880: old_atd, new_atd);
! 881: }
! 882:
! 883: if (new_atd) {
! 884: /* AssertTrackingDesired(S,G,I) switched from FALSE to TRUE */
! 885: PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(ch->flags);
! 886: }
! 887: else {
! 888: /* AssertTrackingDesired(S,G,I) switched from TRUE to FALSE */
! 889: PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(ch->flags);
! 890:
! 891: if (ch->ifassert_state == PIM_IFASSERT_I_AM_LOSER) {
! 892: assert_action_a5(ch);
! 893: }
! 894: }
! 895: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>