123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688 |
- /*
- PIM for Quagga
- Copyright (C) 2008 Everton da Silva Marques
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
- This program is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; see the file COPYING; if not, write to the
- Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
- MA 02110-1301 USA
-
- $QuaggaId: $Format:%an, %ai, %h$ $
- */
- #include <zebra.h>
- #include "zebra/rib.h"
- #include "log.h"
- #include "zclient.h"
- #include "memory.h"
- #include "thread.h"
- #include "linklist.h"
- #include "pimd.h"
- #include "pim_pim.h"
- #include "pim_str.h"
- #include "pim_time.h"
- #include "pim_iface.h"
- #include "pim_join.h"
- #include "pim_zlookup.h"
- #include "pim_upstream.h"
- #include "pim_ifchannel.h"
- #include "pim_neighbor.h"
- #include "pim_rpf.h"
- #include "pim_zebra.h"
- #include "pim_oil.h"
- #include "pim_macro.h"
- static void join_timer_start(struct pim_upstream *up);
- static void pim_upstream_update_assert_tracking_desired(struct pim_upstream *up);
- void pim_upstream_free(struct pim_upstream *up)
- {
- XFREE(MTYPE_PIM_UPSTREAM, up);
- }
- static void upstream_channel_oil_detach(struct pim_upstream *up)
- {
- if (up->channel_oil) {
- pim_channel_oil_del(up->channel_oil);
- up->channel_oil = 0;
- }
- }
- void pim_upstream_delete(struct pim_upstream *up)
- {
- THREAD_OFF(up->t_join_timer);
- upstream_channel_oil_detach(up);
- /*
- notice that listnode_delete() can't be moved
- into pim_upstream_free() because the later is
- called by list_delete_all_node()
- */
- listnode_delete(qpim_upstream_list, up);
- pim_upstream_free(up);
- }
- static void send_join(struct pim_upstream *up)
- {
- zassert(up->join_state == PIM_UPSTREAM_JOINED);
-
- if (PIM_DEBUG_PIM_TRACE) {
- if (PIM_INADDR_IS_ANY(up->rpf.rpf_addr)) {
- char src_str[100];
- char grp_str[100];
- char rpf_str[100];
- pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
- pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
- pim_inet4_dump("<rpf?>", up->rpf.rpf_addr, rpf_str, sizeof(rpf_str));
- zlog_warn("%s: can't send join upstream: RPF'(%s,%s)=%s",
- __PRETTY_FUNCTION__,
- src_str, grp_str, rpf_str);
- /* warning only */
- }
- }
-
- /* send Join(S,G) to the current upstream neighbor */
- pim_joinprune_send(up->rpf.source_nexthop.interface,
- up->rpf.rpf_addr,
- up->source_addr,
- up->group_addr,
- 1 /* join */);
- }
- static int on_join_timer(struct thread *t)
- {
- struct pim_upstream *up;
- zassert(t);
- up = THREAD_ARG(t);
- zassert(up);
- send_join(up);
- up->t_join_timer = 0;
- join_timer_start(up);
- return 0;
- }
- static void join_timer_start(struct pim_upstream *up)
- {
- if (PIM_DEBUG_PIM_EVENTS) {
- char src_str[100];
- char grp_str[100];
- pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
- pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
- zlog_debug("%s: starting %d sec timer for upstream (S,G)=(%s,%s)",
- __PRETTY_FUNCTION__,
- qpim_t_periodic,
- src_str, grp_str);
- }
- zassert(!up->t_join_timer);
- THREAD_TIMER_ON(master, up->t_join_timer,
- on_join_timer,
- up, qpim_t_periodic);
- }
- void pim_upstream_join_timer_restart(struct pim_upstream *up)
- {
- THREAD_OFF(up->t_join_timer);
- join_timer_start(up);
- }
- static void pim_upstream_join_timer_restart_msec(struct pim_upstream *up,
- int interval_msec)
- {
- if (PIM_DEBUG_PIM_EVENTS) {
- char src_str[100];
- char grp_str[100];
- pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
- pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
- zlog_debug("%s: restarting %d msec timer for upstream (S,G)=(%s,%s)",
- __PRETTY_FUNCTION__,
- interval_msec,
- src_str, grp_str);
- }
- THREAD_OFF(up->t_join_timer);
- THREAD_TIMER_MSEC_ON(master, up->t_join_timer,
- on_join_timer,
- up, interval_msec);
- }
- void pim_upstream_join_suppress(struct pim_upstream *up,
- struct in_addr rpf_addr,
- int holdtime)
- {
- long t_joinsuppress_msec;
- long join_timer_remain_msec;
- t_joinsuppress_msec = MIN(pim_if_t_suppressed_msec(up->rpf.source_nexthop.interface),
- 1000 * holdtime);
- join_timer_remain_msec = pim_time_timer_remain_msec(up->t_join_timer);
- if (PIM_DEBUG_PIM_TRACE) {
- char src_str[100];
- char grp_str[100];
- char rpf_str[100];
- pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
- pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
- pim_inet4_dump("<rpf?>", rpf_addr, rpf_str, sizeof(rpf_str));
- zlog_debug("%s %s: detected Join(%s,%s) to RPF'(S,G)=%s: join_timer=%ld msec t_joinsuppress=%ld msec",
- __FILE__, __PRETTY_FUNCTION__,
- src_str, grp_str,
- rpf_str,
- join_timer_remain_msec, t_joinsuppress_msec);
- }
- if (join_timer_remain_msec < t_joinsuppress_msec) {
- if (PIM_DEBUG_PIM_TRACE) {
- char src_str[100];
- char grp_str[100];
- pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
- pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
- zlog_debug("%s %s: suppressing Join(S,G)=(%s,%s) for %ld msec",
- __FILE__, __PRETTY_FUNCTION__,
- src_str, grp_str, t_joinsuppress_msec);
- }
- pim_upstream_join_timer_restart_msec(up, t_joinsuppress_msec);
- }
- }
- void pim_upstream_join_timer_decrease_to_t_override(const char *debug_label,
- struct pim_upstream *up,
- struct in_addr rpf_addr)
- {
- long join_timer_remain_msec;
- int t_override_msec;
- join_timer_remain_msec = pim_time_timer_remain_msec(up->t_join_timer);
- t_override_msec = pim_if_t_override_msec(up->rpf.source_nexthop.interface);
- if (PIM_DEBUG_PIM_TRACE) {
- char src_str[100];
- char grp_str[100];
- char rpf_str[100];
- pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
- pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
- pim_inet4_dump("<rpf?>", rpf_addr, rpf_str, sizeof(rpf_str));
- zlog_debug("%s: to RPF'(%s,%s)=%s: join_timer=%ld msec t_override=%d msec",
- debug_label,
- src_str, grp_str, rpf_str,
- join_timer_remain_msec, t_override_msec);
- }
-
- if (join_timer_remain_msec > t_override_msec) {
- if (PIM_DEBUG_PIM_TRACE) {
- char src_str[100];
- char grp_str[100];
- pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
- pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
- zlog_debug("%s: decreasing (S,G)=(%s,%s) join timer to t_override=%d msec",
- debug_label,
- src_str, grp_str,
- t_override_msec);
- }
- pim_upstream_join_timer_restart_msec(up, t_override_msec);
- }
- }
- static void forward_on(struct pim_upstream *up)
- {
- struct listnode *ifnode;
- struct listnode *ifnextnode;
- struct listnode *chnode;
- struct listnode *chnextnode;
- struct interface *ifp;
- struct pim_interface *pim_ifp;
- struct pim_ifchannel *ch;
- /* scan all interfaces */
- for (ALL_LIST_ELEMENTS(iflist, ifnode, ifnextnode, ifp)) {
- pim_ifp = ifp->info;
- if (!pim_ifp)
- continue;
- /* scan per-interface (S,G) state */
- for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) {
- if (ch->upstream != up)
- continue;
- if (pim_macro_chisin_oiflist(ch))
- pim_forward_start(ch);
- } /* scan iface channel list */
- } /* scan iflist */
- }
- static void forward_off(struct pim_upstream *up)
- {
- struct listnode *ifnode;
- struct listnode *ifnextnode;
- struct listnode *chnode;
- struct listnode *chnextnode;
- struct interface *ifp;
- struct pim_interface *pim_ifp;
- struct pim_ifchannel *ch;
- /* scan all interfaces */
- for (ALL_LIST_ELEMENTS(iflist, ifnode, ifnextnode, ifp)) {
- pim_ifp = ifp->info;
- if (!pim_ifp)
- continue;
- /* scan per-interface (S,G) state */
- for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) {
- if (ch->upstream != up)
- continue;
- pim_forward_stop(ch);
- } /* scan iface channel list */
- } /* scan iflist */
- }
- static void pim_upstream_switch(struct pim_upstream *up,
- enum pim_upstream_state new_state)
- {
- enum pim_upstream_state old_state = up->join_state;
- zassert(old_state != new_state);
-
- up->join_state = new_state;
- up->state_transition = pim_time_monotonic_sec();
- if (PIM_DEBUG_PIM_EVENTS) {
- char src_str[100];
- char grp_str[100];
- pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
- pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
- zlog_debug("%s: PIM_UPSTREAM_%s: (S,G)=(%s,%s)",
- __PRETTY_FUNCTION__,
- ((new_state == PIM_UPSTREAM_JOINED) ? "JOINED" : "NOTJOINED"),
- src_str, grp_str);
- }
- pim_upstream_update_assert_tracking_desired(up);
- if (new_state == PIM_UPSTREAM_JOINED) {
- forward_on(up);
- send_join(up);
- join_timer_start(up);
- }
- else {
- forward_off(up);
- pim_joinprune_send(up->rpf.source_nexthop.interface,
- up->rpf.rpf_addr,
- up->source_addr,
- up->group_addr,
- 0 /* prune */);
- zassert(up->t_join_timer);
- THREAD_OFF(up->t_join_timer);
- }
- }
- static struct pim_upstream *pim_upstream_new(struct in_addr source_addr,
- struct in_addr group_addr)
- {
- struct pim_upstream *up;
- enum pim_rpf_result rpf_result;
- up = XMALLOC(MTYPE_PIM_UPSTREAM, sizeof(*up));
- if (!up) {
- zlog_err("%s: PIM XMALLOC(%zu) failure",
- __PRETTY_FUNCTION__, sizeof(*up));
- return 0;
- }
-
- up->source_addr = source_addr;
- up->group_addr = group_addr;
- up->flags = 0;
- up->ref_count = 1;
- up->t_join_timer = 0;
- up->join_state = 0;
- up->state_transition = pim_time_monotonic_sec();
- up->channel_oil = 0;
- up->rpf.source_nexthop.interface = 0;
- up->rpf.source_nexthop.mrib_nexthop_addr.s_addr = PIM_NET_INADDR_ANY;
- up->rpf.source_nexthop.mrib_metric_preference = qpim_infinite_assert_metric.metric_preference;
- up->rpf.source_nexthop.mrib_route_metric = qpim_infinite_assert_metric.route_metric;
- up->rpf.rpf_addr.s_addr = PIM_NET_INADDR_ANY;
- rpf_result = pim_rpf_update(up, 0);
- if (rpf_result == PIM_RPF_FAILURE) {
- XFREE(MTYPE_PIM_UPSTREAM, up);
- return NULL;
- }
- listnode_add(qpim_upstream_list, up);
- return up;
- }
- struct pim_upstream *pim_upstream_find(struct in_addr source_addr,
- struct in_addr group_addr)
- {
- struct listnode *up_node;
- struct pim_upstream *up;
- for (ALL_LIST_ELEMENTS_RO(qpim_upstream_list, up_node, up)) {
- if (
- (source_addr.s_addr == up->source_addr.s_addr) &&
- (group_addr.s_addr == up->group_addr.s_addr)
- ) {
- return up;
- }
- }
- return 0;
- }
- struct pim_upstream *pim_upstream_add(struct in_addr source_addr,
- struct in_addr group_addr)
- {
- struct pim_upstream *up;
- up = pim_upstream_find(source_addr, group_addr);
- if (up) {
- ++up->ref_count;
- }
- else {
- up = pim_upstream_new(source_addr, group_addr);
- }
- return up;
- }
- void pim_upstream_del(struct pim_upstream *up)
- {
- --up->ref_count;
- if (up->ref_count < 1) {
- pim_upstream_delete(up);
- }
- }
- /*
- Evaluate JoinDesired(S,G):
- JoinDesired(S,G) is true if there is a downstream (S,G) interface I
- in the set:
- inherited_olist(S,G) =
- joins(S,G) (+) pim_include(S,G) (-) lost_assert(S,G)
- JoinDesired(S,G) may be affected by changes in the following:
- pim_ifp->primary_address
- pim_ifp->pim_dr_addr
- ch->ifassert_winner_metric
- ch->ifassert_winner
- ch->local_ifmembership
- ch->ifjoin_state
- ch->upstream->rpf.source_nexthop.mrib_metric_preference
- ch->upstream->rpf.source_nexthop.mrib_route_metric
- ch->upstream->rpf.source_nexthop.interface
- See also pim_upstream_update_join_desired() below.
- */
- int pim_upstream_evaluate_join_desired(struct pim_upstream *up)
- {
- struct listnode *ifnode;
- struct listnode *ifnextnode;
- struct listnode *chnode;
- struct listnode *chnextnode;
- struct interface *ifp;
- struct pim_interface *pim_ifp;
- struct pim_ifchannel *ch;
- /* scan all interfaces */
- for (ALL_LIST_ELEMENTS(iflist, ifnode, ifnextnode, ifp)) {
- pim_ifp = ifp->info;
- if (!pim_ifp)
- continue;
- /* scan per-interface (S,G) state */
- for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) {
- if (ch->upstream != up)
- continue;
- if (pim_macro_ch_lost_assert(ch))
- continue; /* keep searching */
- if (pim_macro_chisin_joins_or_include(ch))
- return 1; /* true */
- } /* scan iface channel list */
- } /* scan iflist */
- return 0; /* false */
- }
- /*
- See also pim_upstream_evaluate_join_desired() above.
- */
- void pim_upstream_update_join_desired(struct pim_upstream *up)
- {
- int was_join_desired; /* boolean */
- int is_join_desired; /* boolean */
- was_join_desired = PIM_UPSTREAM_FLAG_TEST_DR_JOIN_DESIRED(up->flags);
- is_join_desired = pim_upstream_evaluate_join_desired(up);
- if (is_join_desired)
- PIM_UPSTREAM_FLAG_SET_DR_JOIN_DESIRED(up->flags);
- else
- PIM_UPSTREAM_FLAG_UNSET_DR_JOIN_DESIRED(up->flags);
- /* switched from false to true */
- if (is_join_desired && !was_join_desired) {
- zassert(up->join_state == PIM_UPSTREAM_NOTJOINED);
- pim_upstream_switch(up, PIM_UPSTREAM_JOINED);
- return;
- }
-
- /* switched from true to false */
- if (!is_join_desired && was_join_desired) {
- zassert(up->join_state == PIM_UPSTREAM_JOINED);
- pim_upstream_switch(up, PIM_UPSTREAM_NOTJOINED);
- return;
- }
- }
- /*
- RFC 4601 4.5.7. Sending (S,G) Join/Prune Messages
- Transitions from Joined State
- RPF'(S,G) GenID changes
- The upstream (S,G) state machine remains in Joined state. If the
- Join Timer is set to expire in more than t_override seconds, reset
- it so that it expires after t_override seconds.
- */
- void pim_upstream_rpf_genid_changed(struct in_addr neigh_addr)
- {
- struct listnode *up_node;
- struct listnode *up_nextnode;
- struct pim_upstream *up;
- /*
- Scan all (S,G) upstreams searching for RPF'(S,G)=neigh_addr
- */
- for (ALL_LIST_ELEMENTS(qpim_upstream_list, up_node, up_nextnode, up)) {
- if (PIM_DEBUG_PIM_TRACE) {
- char neigh_str[100];
- char src_str[100];
- char grp_str[100];
- char rpf_addr_str[100];
- pim_inet4_dump("<neigh?>", neigh_addr, neigh_str, sizeof(neigh_str));
- pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
- pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
- pim_inet4_dump("<rpf?>", up->rpf.rpf_addr, rpf_addr_str, sizeof(rpf_addr_str));
- zlog_debug("%s: matching neigh=%s against upstream (S,G)=(%s,%s) joined=%d rpf_addr=%s",
- __PRETTY_FUNCTION__,
- neigh_str, src_str, grp_str,
- up->join_state == PIM_UPSTREAM_JOINED,
- rpf_addr_str);
- }
- /* consider only (S,G) upstream in Joined state */
- if (up->join_state != PIM_UPSTREAM_JOINED)
- continue;
- /* match RPF'(S,G)=neigh_addr */
- if (up->rpf.rpf_addr.s_addr != neigh_addr.s_addr)
- continue;
- pim_upstream_join_timer_decrease_to_t_override("RPF'(S,G) GenID change",
- up, neigh_addr);
- }
- }
- void pim_upstream_rpf_interface_changed(struct pim_upstream *up,
- struct interface *old_rpf_ifp)
- {
- struct listnode *ifnode;
- struct listnode *ifnextnode;
- struct interface *ifp;
- /* scan all interfaces */
- for (ALL_LIST_ELEMENTS(iflist, ifnode, ifnextnode, ifp)) {
- struct listnode *chnode;
- struct listnode *chnextnode;
- struct pim_ifchannel *ch;
- struct pim_interface *pim_ifp;
- pim_ifp = ifp->info;
- if (!pim_ifp)
- continue;
- /* search all ifchannels */
- for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) {
- if (ch->upstream != up)
- continue;
- if (ch->ifassert_state == PIM_IFASSERT_I_AM_LOSER) {
- if (
- /* RPF_interface(S) was NOT I */
- (old_rpf_ifp == ch->interface)
- &&
- /* RPF_interface(S) stopped being I */
- (ch->upstream->rpf.source_nexthop.interface != ch->interface)
- ) {
- assert_action_a5(ch);
- }
- } /* PIM_IFASSERT_I_AM_LOSER */
- pim_ifchannel_update_assert_tracking_desired(ch);
- }
- }
- }
- void pim_upstream_update_could_assert(struct pim_upstream *up)
- {
- struct listnode *ifnode;
- struct listnode *ifnextnode;
- struct listnode *chnode;
- struct listnode *chnextnode;
- struct interface *ifp;
- struct pim_interface *pim_ifp;
- struct pim_ifchannel *ch;
- /* scan all interfaces */
- for (ALL_LIST_ELEMENTS(iflist, ifnode, ifnextnode, ifp)) {
- pim_ifp = ifp->info;
- if (!pim_ifp)
- continue;
- /* scan per-interface (S,G) state */
- for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) {
- if (ch->upstream != up)
- continue;
- pim_ifchannel_update_could_assert(ch);
- } /* scan iface channel list */
- } /* scan iflist */
- }
- void pim_upstream_update_my_assert_metric(struct pim_upstream *up)
- {
- struct listnode *ifnode;
- struct listnode *ifnextnode;
- struct listnode *chnode;
- struct listnode *chnextnode;
- struct interface *ifp;
- struct pim_interface *pim_ifp;
- struct pim_ifchannel *ch;
- /* scan all interfaces */
- for (ALL_LIST_ELEMENTS(iflist, ifnode, ifnextnode, ifp)) {
- pim_ifp = ifp->info;
- if (!pim_ifp)
- continue;
- /* scan per-interface (S,G) state */
- for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) {
- if (ch->upstream != up)
- continue;
- pim_ifchannel_update_my_assert_metric(ch);
- } /* scan iface channel list */
- } /* scan iflist */
- }
- static void pim_upstream_update_assert_tracking_desired(struct pim_upstream *up)
- {
- struct listnode *ifnode;
- struct listnode *ifnextnode;
- struct listnode *chnode;
- struct listnode *chnextnode;
- struct interface *ifp;
- struct pim_interface *pim_ifp;
- struct pim_ifchannel *ch;
- /* scan all interfaces */
- for (ALL_LIST_ELEMENTS(iflist, ifnode, ifnextnode, ifp)) {
- pim_ifp = ifp->info;
- if (!pim_ifp)
- continue;
- /* scan per-interface (S,G) state */
- for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) {
- if (ch->upstream != up)
- continue;
- pim_ifchannel_update_assert_tracking_desired(ch);
- } /* scan iface channel list */
- } /* scan iflist */
- }
|