2015-02-04 07:01:14 +01:00
|
|
|
/*
|
|
|
|
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
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <zebra.h>
|
|
|
|
|
|
|
|
#include "zebra/rib.h"
|
|
|
|
|
|
|
|
#include "log.h"
|
|
|
|
#include "zclient.h"
|
|
|
|
#include "memory.h"
|
|
|
|
#include "thread.h"
|
|
|
|
#include "linklist.h"
|
2016-09-13 21:41:33 +02:00
|
|
|
#include "vty.h"
|
|
|
|
#include "plist.h"
|
2016-10-07 16:25:08 +02:00
|
|
|
#include "hash.h"
|
|
|
|
#include "jhash.h"
|
2016-11-03 00:19:40 +01:00
|
|
|
#include "wheel.h"
|
2015-02-04 07:01:14 +01:00
|
|
|
|
|
|
|
#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"
|
2015-09-30 14:41:18 +02:00
|
|
|
#include "pim_rp.h"
|
2015-10-16 16:50:16 +02:00
|
|
|
#include "pim_br.h"
|
2016-07-18 15:47:19 +02:00
|
|
|
#include "pim_register.h"
|
2016-10-31 20:29:17 +01:00
|
|
|
#include "pim_msdp.h"
|
2017-02-15 03:32:16 +01:00
|
|
|
#include "pim_jp_agg.h"
|
2017-02-22 16:28:36 +01:00
|
|
|
#include "pim_nht.h"
|
2017-03-17 19:51:13 +01:00
|
|
|
#include "pim_ssm.h"
|
2015-02-04 07:01:14 +01:00
|
|
|
|
2016-10-07 16:25:08 +02:00
|
|
|
struct hash *pim_upstream_hash = NULL;
|
|
|
|
struct list *pim_upstream_list = NULL;
|
2016-11-03 00:19:40 +01:00
|
|
|
struct timer_wheel *pim_upstream_sg_wheel = NULL;
|
2016-10-07 16:25:08 +02:00
|
|
|
|
2017-02-15 03:32:16 +01:00
|
|
|
static void join_timer_stop(struct pim_upstream *up);
|
2015-02-04 07:01:14 +01:00
|
|
|
static void pim_upstream_update_assert_tracking_desired(struct pim_upstream *up);
|
|
|
|
|
2016-08-02 17:21:49 +02:00
|
|
|
/*
|
|
|
|
* A (*,G) or a (*,*) is going away
|
|
|
|
* remove the parent pointer from
|
|
|
|
* those pointing at us
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
pim_upstream_remove_children (struct pim_upstream *up)
|
|
|
|
{
|
|
|
|
struct pim_upstream *child;
|
|
|
|
|
2016-10-24 04:47:25 +02:00
|
|
|
if (!up->sources)
|
2016-08-02 17:21:49 +02:00
|
|
|
return;
|
|
|
|
|
2016-10-24 04:47:25 +02:00
|
|
|
while (!list_isempty (up->sources))
|
2016-08-02 17:21:49 +02:00
|
|
|
{
|
2016-10-24 04:47:25 +02:00
|
|
|
child = listnode_head (up->sources);
|
|
|
|
listnode_delete (up->sources, child);
|
2017-05-09 20:30:43 +02:00
|
|
|
if (PIM_UPSTREAM_FLAG_TEST_SRC_LHR(child->flags))
|
|
|
|
{
|
|
|
|
PIM_UPSTREAM_FLAG_UNSET_SRC_LHR(child->flags);
|
|
|
|
child = pim_upstream_del(child, __PRETTY_FUNCTION__);
|
|
|
|
}
|
|
|
|
if (child)
|
|
|
|
child->parent = NULL;
|
2016-08-02 17:21:49 +02:00
|
|
|
}
|
2017-05-09 20:30:43 +02:00
|
|
|
list_delete(up->sources);
|
|
|
|
up->sources = NULL;
|
2016-08-02 17:21:49 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* A (*,G) or a (*,*) is being created
|
|
|
|
* Find the children that would point
|
|
|
|
* at us.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
pim_upstream_find_new_children (struct pim_upstream *up)
|
|
|
|
{
|
|
|
|
struct pim_upstream *child;
|
|
|
|
struct listnode *ch_node;
|
|
|
|
|
2016-08-02 10:38:11 +02:00
|
|
|
if ((up->sg.src.s_addr != INADDR_ANY) &&
|
|
|
|
(up->sg.grp.s_addr != INADDR_ANY))
|
2016-08-02 17:21:49 +02:00
|
|
|
return;
|
|
|
|
|
2016-08-02 10:38:11 +02:00
|
|
|
if ((up->sg.src.s_addr == INADDR_ANY) &&
|
|
|
|
(up->sg.grp.s_addr == INADDR_ANY))
|
2016-08-02 17:21:49 +02:00
|
|
|
return;
|
|
|
|
|
2016-10-07 16:25:08 +02:00
|
|
|
for (ALL_LIST_ELEMENTS_RO (pim_upstream_list, ch_node, child))
|
2016-08-02 17:21:49 +02:00
|
|
|
{
|
2016-08-02 10:38:11 +02:00
|
|
|
if ((up->sg.grp.s_addr != INADDR_ANY) &&
|
2016-10-07 16:25:08 +02:00
|
|
|
(child->sg.grp.s_addr == up->sg.grp.s_addr) &&
|
2016-08-02 17:21:49 +02:00
|
|
|
(child != up))
|
2016-10-24 04:47:25 +02:00
|
|
|
{
|
|
|
|
child->parent = up;
|
|
|
|
listnode_add_sort (up->sources, child);
|
|
|
|
}
|
2016-08-02 17:21:49 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-07-24 07:15:09 +02:00
|
|
|
/*
|
|
|
|
* If we have a (*,*) || (S,*) there is no parent
|
|
|
|
* If we have a (S,G), find the (*,G)
|
|
|
|
* If we have a (*,G), find the (*,*)
|
|
|
|
*/
|
|
|
|
static struct pim_upstream *
|
2016-10-24 04:47:25 +02:00
|
|
|
pim_upstream_find_parent (struct pim_upstream *child)
|
2016-07-24 07:15:09 +02:00
|
|
|
{
|
2016-10-24 04:47:25 +02:00
|
|
|
struct prefix_sg any = child->sg;
|
|
|
|
struct pim_upstream *up = NULL;
|
2016-07-24 07:15:09 +02:00
|
|
|
|
|
|
|
// (S,G)
|
2016-10-24 04:47:25 +02:00
|
|
|
if ((child->sg.src.s_addr != INADDR_ANY) &&
|
|
|
|
(child->sg.grp.s_addr != INADDR_ANY))
|
2016-07-24 07:15:09 +02:00
|
|
|
{
|
2016-08-02 10:38:11 +02:00
|
|
|
any.src.s_addr = INADDR_ANY;
|
2016-10-24 04:47:25 +02:00
|
|
|
up = pim_upstream_find (&any);
|
|
|
|
|
|
|
|
if (up)
|
|
|
|
listnode_add (up->sources, child);
|
|
|
|
|
|
|
|
return up;
|
2016-07-24 07:15:09 +02:00
|
|
|
}
|
|
|
|
|
2016-10-24 04:47:25 +02:00
|
|
|
return NULL;
|
2016-07-24 07:15:09 +02:00
|
|
|
}
|
|
|
|
|
2015-02-04 07:01:14 +01:00
|
|
|
void pim_upstream_free(struct pim_upstream *up)
|
|
|
|
{
|
|
|
|
XFREE(MTYPE_PIM_UPSTREAM, up);
|
2017-02-22 16:28:36 +01:00
|
|
|
up = NULL;
|
2015-02-04 07:01:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static void upstream_channel_oil_detach(struct pim_upstream *up)
|
|
|
|
{
|
2017-05-09 20:30:43 +02:00
|
|
|
if (up->channel_oil)
|
|
|
|
{
|
|
|
|
/* Detaching from channel_oil, channel_oil may exist post del,
|
|
|
|
but upstream would not keep reference of it
|
|
|
|
*/
|
|
|
|
pim_channel_oil_del(up->channel_oil);
|
|
|
|
up->channel_oil = NULL;
|
|
|
|
}
|
2015-02-04 07:01:14 +01:00
|
|
|
}
|
|
|
|
|
2017-03-10 21:01:11 +01:00
|
|
|
struct pim_upstream *
|
2016-10-27 14:05:57 +02:00
|
|
|
pim_upstream_del(struct pim_upstream *up, const char *name)
|
2015-02-04 07:01:14 +01:00
|
|
|
{
|
2016-11-08 19:34:31 +01:00
|
|
|
bool notify_msdp = false;
|
2017-02-22 16:28:36 +01:00
|
|
|
struct prefix nht_p;
|
2016-11-08 19:34:31 +01:00
|
|
|
|
2016-12-06 05:27:37 +01:00
|
|
|
if (PIM_DEBUG_TRACE)
|
2017-04-27 20:01:32 +02:00
|
|
|
zlog_debug ("%s(%s): Delete %s ref count: %d , flags: %d c_oil ref count %d (Pre decrement)",
|
2017-05-09 20:30:43 +02:00
|
|
|
__PRETTY_FUNCTION__, name, up->sg_str, up->ref_count, up->flags,
|
2017-04-27 20:01:32 +02:00
|
|
|
up->channel_oil->oil_ref_count);
|
2016-12-06 05:27:37 +01:00
|
|
|
|
2016-10-27 14:05:57 +02:00
|
|
|
--up->ref_count;
|
|
|
|
|
|
|
|
if (up->ref_count >= 1)
|
2017-03-10 21:01:11 +01:00
|
|
|
return up;
|
2016-10-27 14:05:57 +02:00
|
|
|
|
2015-10-16 16:50:16 +02:00
|
|
|
THREAD_OFF(up->t_ka_timer);
|
2016-07-18 15:52:45 +02:00
|
|
|
THREAD_OFF(up->t_rs_timer);
|
2016-11-16 00:39:11 +01:00
|
|
|
THREAD_OFF(up->t_msdp_reg_timer);
|
2015-02-04 07:01:14 +01:00
|
|
|
|
2016-11-08 19:34:31 +01:00
|
|
|
if (up->join_state == PIM_UPSTREAM_JOINED) {
|
2017-02-15 03:32:16 +01:00
|
|
|
pim_jp_agg_single_upstream_send (&up->rpf, up, 0);
|
|
|
|
|
2016-11-08 19:34:31 +01:00
|
|
|
if (up->sg.src.s_addr == INADDR_ANY) {
|
|
|
|
/* if a (*, G) entry in the joined state is being deleted we
|
|
|
|
* need to notify MSDP */
|
|
|
|
notify_msdp = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-06 18:56:32 +01:00
|
|
|
join_timer_stop(up);
|
2017-03-16 17:56:59 +01:00
|
|
|
pim_jp_agg_upstream_verification (up, false);
|
2017-03-06 18:56:32 +01:00
|
|
|
up->rpf.source_nexthop.interface = NULL;
|
|
|
|
|
2016-12-02 06:01:34 +01:00
|
|
|
if (up->sg.src.s_addr != INADDR_ANY) {
|
2016-11-03 00:19:40 +01:00
|
|
|
wheel_remove_item (pim_upstream_sg_wheel, up);
|
2016-12-02 06:01:34 +01:00
|
|
|
notify_msdp = true;
|
|
|
|
}
|
2016-11-03 00:19:40 +01:00
|
|
|
|
2016-08-02 17:21:49 +02:00
|
|
|
pim_upstream_remove_children (up);
|
2016-10-24 04:47:25 +02:00
|
|
|
if (up->sources)
|
2017-05-09 20:30:43 +02:00
|
|
|
list_delete (up->sources);
|
2016-10-24 04:47:25 +02:00
|
|
|
up->sources = NULL;
|
2017-05-09 20:30:43 +02:00
|
|
|
pim_mroute_del (up->channel_oil, __PRETTY_FUNCTION__);
|
|
|
|
upstream_channel_oil_detach(up);
|
2016-10-24 04:47:25 +02:00
|
|
|
|
2017-04-20 13:48:27 +02:00
|
|
|
list_delete (up->ifchannels);
|
pimd: Fix WG/SGRpt & WG J/P processing
During processing of Join/Prune,
for a S,G entry, current state is SGRpt, when only *,G is
received, need to clear SGRpt and add/inherit the *,G OIF to S,G so
it can forward traffic to downstream where *,G is received.
Upon receiving SGRpt prune remove the inherited *,G OIF.
From, downstream router received *,G Prune along with SGRpt
prune. Avoid sending *,G and SGRpt Prune together.
Reset upstream_del reset ifchannel to NULL.
Testing Done:
Run failed smoke test of sending data packets, trigger SPT switchover,
*,G path received SGRpt later data traffic stopped S,G ages out from LHR, sends only
*,G join to upstream, verified S,G entry inherit the OIF.
Upon receiving SGRpt deletes inherited oif and retains in SGRpt state.
Signed-off-by: Chirag Shah <chirag@cumulusnetworks.com>
2017-04-22 00:08:03 +02:00
|
|
|
up->ifchannels = NULL;
|
2017-04-20 13:48:27 +02:00
|
|
|
|
2015-02-04 07:01:14 +01:00
|
|
|
/*
|
|
|
|
notice that listnode_delete() can't be moved
|
|
|
|
into pim_upstream_free() because the later is
|
|
|
|
called by list_delete_all_node()
|
|
|
|
*/
|
2017-05-09 20:30:43 +02:00
|
|
|
if (up->parent && up->parent->sources)
|
|
|
|
listnode_delete (up->parent->sources, up);
|
|
|
|
up->parent = NULL;
|
|
|
|
|
2016-10-07 16:25:08 +02:00
|
|
|
listnode_delete (pim_upstream_list, up);
|
|
|
|
hash_release (pim_upstream_hash, up);
|
2015-02-04 07:01:14 +01:00
|
|
|
|
2017-02-22 16:28:36 +01:00
|
|
|
if (notify_msdp)
|
|
|
|
{
|
|
|
|
pim_msdp_up_del (&up->sg);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Deregister addr with Zebra NHT */
|
|
|
|
nht_p.family = AF_INET;
|
|
|
|
nht_p.prefixlen = IPV4_MAX_BITLEN;
|
|
|
|
nht_p.u.prefix4 = up->upstream_addr;
|
|
|
|
if (PIM_DEBUG_TRACE)
|
|
|
|
{
|
|
|
|
char buf[PREFIX2STR_BUFFER];
|
|
|
|
prefix2str (&nht_p, buf, sizeof (buf));
|
pimd: Fix WG/SGRpt & WG J/P processing
During processing of Join/Prune,
for a S,G entry, current state is SGRpt, when only *,G is
received, need to clear SGRpt and add/inherit the *,G OIF to S,G so
it can forward traffic to downstream where *,G is received.
Upon receiving SGRpt prune remove the inherited *,G OIF.
From, downstream router received *,G Prune along with SGRpt
prune. Avoid sending *,G and SGRpt Prune together.
Reset upstream_del reset ifchannel to NULL.
Testing Done:
Run failed smoke test of sending data packets, trigger SPT switchover,
*,G path received SGRpt later data traffic stopped S,G ages out from LHR, sends only
*,G join to upstream, verified S,G entry inherit the OIF.
Upon receiving SGRpt deletes inherited oif and retains in SGRpt state.
Signed-off-by: Chirag Shah <chirag@cumulusnetworks.com>
2017-04-22 00:08:03 +02:00
|
|
|
zlog_debug ("%s: Deregister upstream %s addr %s with Zebra NHT",
|
pimd: Pim Nexthop Tracking support with ECMP
In this patch, PIM nexthop tracking uses locally populated nexthop cached list
to determine ECMP based nexthop (w/ ECMP knob enabled), otherwise picks
the first nexthop as RPF.
Introduced '[no] ip pim ecmp' command to enable/disable PIM ECMP knob.
By default, PIM ECMP is disabled.
Intorudced '[no] ip pim ecmp rebalance' command to provide existing mcache
entry to switch new path based on hash chosen path.
Introduced, show command to display pim registered addresses and respective nexthops.
Introuduce, show command to find nexthop and out interface for (S,G) or (RP,G).
Re-Register an address with nexthop when Interface UP event received,
to ensure the PIM nexthop cache is updated (being PIM enabled).
During PIM neighbor UP, traverse all RPs and Upstreams nexthop and determine, if
any of nexthop's IPv4 address changes/resolves due to neigbor UP event.
Testing Done: Run various LHR, RP and FHR related cases to resolve RPF using
nexthop cache with ECMP knob disabled, performed interface/PIM neighbor flap events.
Executed pim-smoke with knob disabled.
Signed-off-by: Chirag Shah <chirag@cumulusnetworks.com>
(cherry picked from commit cba444817883b8b3b22a7ed9958dc9ed77f76230)
2017-04-05 22:14:12 +02:00
|
|
|
__PRETTY_FUNCTION__, up->sg_str, buf);
|
2017-02-22 16:28:36 +01:00
|
|
|
}
|
|
|
|
pim_delete_tracked_nexthop (&nht_p, up, NULL);
|
|
|
|
|
|
|
|
pim_upstream_free (up);
|
2017-03-10 21:01:11 +01:00
|
|
|
|
|
|
|
return NULL;
|
2015-02-04 07:01:14 +01:00
|
|
|
}
|
|
|
|
|
2016-07-15 21:42:09 +02:00
|
|
|
void
|
|
|
|
pim_upstream_send_join (struct pim_upstream *up)
|
2015-02-04 07:01:14 +01:00
|
|
|
{
|
2016-12-06 05:27:37 +01:00
|
|
|
if (PIM_DEBUG_TRACE) {
|
2016-10-20 16:09:30 +02:00
|
|
|
char rpf_str[PREFIX_STRLEN];
|
2016-09-02 18:17:10 +02:00
|
|
|
pim_addr_dump("<rpf?>", &up->rpf.rpf_addr, rpf_str, sizeof(rpf_str));
|
2016-07-22 14:57:20 +02:00
|
|
|
zlog_debug ("%s: RPF'%s=%s(%s) for Interface %s", __PRETTY_FUNCTION__,
|
2016-11-17 14:17:25 +01:00
|
|
|
up->sg_str, rpf_str, pim_upstream_state2str (up->join_state),
|
2016-07-15 21:42:09 +02:00
|
|
|
up->rpf.source_nexthop.interface->name);
|
2016-09-02 18:17:10 +02:00
|
|
|
if (pim_rpf_addr_is_inaddr_any(&up->rpf)) {
|
2016-07-22 14:57:20 +02:00
|
|
|
zlog_debug("%s: can't send join upstream: RPF'%s=%s",
|
2016-07-15 21:42:09 +02:00
|
|
|
__PRETTY_FUNCTION__,
|
2016-11-17 14:17:25 +01:00
|
|
|
up->sg_str, rpf_str);
|
2015-02-04 07:01:14 +01:00
|
|
|
/* warning only */
|
|
|
|
}
|
|
|
|
}
|
2016-07-15 21:42:09 +02:00
|
|
|
|
2015-02-04 07:01:14 +01:00
|
|
|
/* send Join(S,G) to the current upstream neighbor */
|
2017-02-15 03:32:16 +01:00
|
|
|
pim_jp_agg_single_upstream_send(&up->rpf, up, 1 /* join */);
|
2015-02-04 07:01:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static int on_join_timer(struct thread *t)
|
|
|
|
{
|
|
|
|
struct pim_upstream *up;
|
|
|
|
|
|
|
|
up = THREAD_ARG(t);
|
|
|
|
|
2016-08-23 19:39:48 +02:00
|
|
|
up->t_join_timer = NULL;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* In the case of a HFR we will not ahve anyone to send this to.
|
|
|
|
*/
|
2016-09-12 19:02:53 +02:00
|
|
|
if (PIM_UPSTREAM_FLAG_TEST_FHR(up->flags))
|
2016-08-23 19:39:48 +02:00
|
|
|
return 0;
|
|
|
|
|
2016-08-24 15:26:54 +02:00
|
|
|
/*
|
|
|
|
* Don't send the join if the outgoing interface is a loopback
|
|
|
|
* But since this might change leave the join timer running
|
|
|
|
*/
|
2017-03-06 18:56:32 +01:00
|
|
|
if (up->rpf.source_nexthop.interface &&
|
|
|
|
!if_is_loopback (up->rpf.source_nexthop.interface))
|
2016-08-24 15:26:54 +02:00
|
|
|
pim_upstream_send_join (up);
|
2015-02-04 07:01:14 +01:00
|
|
|
|
|
|
|
join_timer_start(up);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-02-15 03:32:16 +01:00
|
|
|
static void join_timer_stop(struct pim_upstream *up)
|
2015-02-04 07:01:14 +01:00
|
|
|
{
|
2017-02-15 03:32:16 +01:00
|
|
|
struct pim_neighbor *nbr;
|
|
|
|
|
2017-03-16 20:56:54 +01:00
|
|
|
THREAD_OFF (up->t_join_timer);
|
|
|
|
|
2017-02-15 03:32:16 +01:00
|
|
|
nbr = pim_neighbor_find (up->rpf.source_nexthop.interface,
|
|
|
|
up->rpf.rpf_addr.u.prefix4);
|
|
|
|
|
|
|
|
if (nbr)
|
|
|
|
pim_jp_agg_remove_group (nbr->upstream_jp_agg, up);
|
|
|
|
|
2017-03-16 17:56:59 +01:00
|
|
|
pim_jp_agg_upstream_verification (up, false);
|
2017-02-15 03:32:16 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
join_timer_start(struct pim_upstream *up)
|
|
|
|
{
|
2017-03-06 18:56:32 +01:00
|
|
|
struct pim_neighbor *nbr = NULL;
|
2017-02-15 03:32:16 +01:00
|
|
|
|
2017-03-06 18:56:32 +01:00
|
|
|
if (up->rpf.source_nexthop.interface)
|
|
|
|
{
|
|
|
|
nbr = pim_neighbor_find (up->rpf.source_nexthop.interface,
|
|
|
|
up->rpf.rpf_addr.u.prefix4);
|
|
|
|
|
|
|
|
if (PIM_DEBUG_PIM_EVENTS) {
|
|
|
|
zlog_debug("%s: starting %d sec timer for upstream (S,G)=%s",
|
|
|
|
__PRETTY_FUNCTION__,
|
|
|
|
qpim_t_periodic,
|
|
|
|
up->sg_str);
|
|
|
|
}
|
|
|
|
}
|
2015-02-04 07:01:14 +01:00
|
|
|
|
2017-02-15 03:32:16 +01:00
|
|
|
if (nbr)
|
|
|
|
pim_jp_agg_add_group (nbr->upstream_jp_agg, up, 1);
|
|
|
|
else
|
|
|
|
{
|
|
|
|
THREAD_OFF (up->t_join_timer);
|
|
|
|
THREAD_TIMER_ON(master, up->t_join_timer,
|
|
|
|
on_join_timer,
|
|
|
|
up, qpim_t_periodic);
|
|
|
|
}
|
2017-03-16 17:56:59 +01:00
|
|
|
pim_jp_agg_upstream_verification (up, true);
|
2015-02-04 07:01:14 +01:00
|
|
|
}
|
|
|
|
|
2017-02-15 03:32:16 +01:00
|
|
|
/*
|
|
|
|
* This is only called when we are switching the upstream
|
|
|
|
* J/P from one neighbor to another
|
|
|
|
*
|
|
|
|
* As such we need to remove from the old list and
|
|
|
|
* add to the new list.
|
|
|
|
*/
|
|
|
|
void pim_upstream_join_timer_restart(struct pim_upstream *up, struct pim_rpf *old)
|
2015-02-04 07:01:14 +01:00
|
|
|
{
|
2017-02-15 03:32:16 +01:00
|
|
|
//THREAD_OFF(up->t_join_timer);
|
2015-02-04 07:01:14 +01:00
|
|
|
join_timer_start(up);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void pim_upstream_join_timer_restart_msec(struct pim_upstream *up,
|
|
|
|
int interval_msec)
|
|
|
|
{
|
|
|
|
if (PIM_DEBUG_PIM_EVENTS) {
|
2016-07-22 14:57:20 +02:00
|
|
|
zlog_debug("%s: restarting %d msec timer for upstream (S,G)=%s",
|
2015-02-04 07:01:14 +01:00
|
|
|
__PRETTY_FUNCTION__,
|
|
|
|
interval_msec,
|
2016-11-17 14:17:25 +01:00
|
|
|
up->sg_str);
|
2015-02-04 07:01:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
2016-12-06 05:27:37 +01:00
|
|
|
if (PIM_DEBUG_TRACE) {
|
2016-10-20 16:09:30 +02:00
|
|
|
char rpf_str[INET_ADDRSTRLEN];
|
2015-02-04 07:01:14 +01:00
|
|
|
pim_inet4_dump("<rpf?>", rpf_addr, rpf_str, sizeof(rpf_str));
|
2016-07-22 14:57:20 +02:00
|
|
|
zlog_debug("%s %s: detected Join%s to RPF'(S,G)=%s: join_timer=%ld msec t_joinsuppress=%ld msec",
|
2015-02-04 07:01:14 +01:00
|
|
|
__FILE__, __PRETTY_FUNCTION__,
|
2016-11-17 14:17:25 +01:00
|
|
|
up->sg_str,
|
2015-02-04 07:01:14 +01:00
|
|
|
rpf_str,
|
|
|
|
join_timer_remain_msec, t_joinsuppress_msec);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (join_timer_remain_msec < t_joinsuppress_msec) {
|
2016-12-06 05:27:37 +01:00
|
|
|
if (PIM_DEBUG_TRACE) {
|
2016-07-22 14:57:20 +02:00
|
|
|
zlog_debug("%s %s: suppressing Join(S,G)=%s for %ld msec",
|
2015-02-04 07:01:14 +01:00
|
|
|
__FILE__, __PRETTY_FUNCTION__,
|
2016-11-17 14:17:25 +01:00
|
|
|
up->sg_str, t_joinsuppress_msec);
|
2015-02-04 07:01:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
pim_upstream_join_timer_restart_msec(up, t_joinsuppress_msec);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void pim_upstream_join_timer_decrease_to_t_override(const char *debug_label,
|
2017-02-10 21:04:17 +01:00
|
|
|
struct pim_upstream *up)
|
2015-02-04 07:01:14 +01:00
|
|
|
{
|
|
|
|
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);
|
|
|
|
|
2016-12-06 05:27:37 +01:00
|
|
|
if (PIM_DEBUG_TRACE) {
|
2016-10-20 16:09:30 +02:00
|
|
|
char rpf_str[INET_ADDRSTRLEN];
|
2017-02-10 21:04:17 +01:00
|
|
|
pim_inet4_dump("<rpf?>", up->rpf.rpf_addr.u.prefix4, rpf_str, sizeof(rpf_str));
|
2016-07-22 14:57:20 +02:00
|
|
|
zlog_debug("%s: to RPF'%s=%s: join_timer=%ld msec t_override=%d msec",
|
2015-02-04 07:01:14 +01:00
|
|
|
debug_label,
|
2016-11-17 14:17:25 +01:00
|
|
|
up->sg_str, rpf_str,
|
2015-02-04 07:01:14 +01:00
|
|
|
join_timer_remain_msec, t_override_msec);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (join_timer_remain_msec > t_override_msec) {
|
2016-12-06 05:27:37 +01:00
|
|
|
if (PIM_DEBUG_TRACE) {
|
2016-07-22 14:57:20 +02:00
|
|
|
zlog_debug("%s: decreasing (S,G)=%s join timer to t_override=%d msec",
|
2015-02-04 07:01:14 +01:00
|
|
|
debug_label,
|
2016-11-17 14:17:25 +01:00
|
|
|
up->sg_str,
|
2015-02-04 07:01:14 +01:00
|
|
|
t_override_msec);
|
|
|
|
}
|
|
|
|
|
|
|
|
pim_upstream_join_timer_restart_msec(up, t_override_msec);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void forward_on(struct pim_upstream *up)
|
|
|
|
{
|
|
|
|
struct listnode *chnode;
|
|
|
|
struct listnode *chnextnode;
|
2017-04-20 16:12:41 +02:00
|
|
|
struct pim_ifchannel *ch = NULL;
|
2015-02-04 07:01:14 +01:00
|
|
|
|
2016-11-07 18:34:44 +01:00
|
|
|
/* scan (S,G) state */
|
2017-04-20 16:12:41 +02:00
|
|
|
for (ALL_LIST_ELEMENTS(up->ifchannels, chnode, chnextnode, ch)) {
|
2016-11-07 18:34:44 +01:00
|
|
|
if (pim_macro_chisin_oiflist(ch))
|
|
|
|
pim_forward_start(ch);
|
2015-02-04 07:01:14 +01:00
|
|
|
|
2016-11-07 18:34:44 +01:00
|
|
|
} /* scan iface channel list */
|
2015-02-04 07:01:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static void forward_off(struct pim_upstream *up)
|
|
|
|
{
|
|
|
|
struct listnode *chnode;
|
|
|
|
struct listnode *chnextnode;
|
|
|
|
struct pim_ifchannel *ch;
|
|
|
|
|
2016-11-07 18:34:44 +01:00
|
|
|
/* scan per-interface (S,G) state */
|
2017-04-20 16:12:41 +02:00
|
|
|
for (ALL_LIST_ELEMENTS(up->ifchannels, chnode, chnextnode, ch)) {
|
2015-02-04 07:01:14 +01:00
|
|
|
|
2016-11-07 18:34:44 +01:00
|
|
|
pim_forward_stop(ch);
|
2015-02-04 07:01:14 +01:00
|
|
|
|
2016-11-07 18:34:44 +01:00
|
|
|
} /* scan iface channel list */
|
2015-02-04 07:01:14 +01:00
|
|
|
}
|
|
|
|
|
2016-08-23 19:39:48 +02:00
|
|
|
static int
|
|
|
|
pim_upstream_could_register (struct pim_upstream *up)
|
|
|
|
{
|
2017-04-25 07:32:23 +02:00
|
|
|
struct pim_interface *pim_ifp = NULL;
|
|
|
|
|
|
|
|
if (up->rpf.source_nexthop.interface)
|
|
|
|
pim_ifp = up->rpf.source_nexthop.interface->info;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (PIM_DEBUG_TRACE)
|
|
|
|
zlog_debug ("%s: up %s RPF is not present", __PRETTY_FUNCTION__, up->sg_str);
|
|
|
|
}
|
2016-08-23 19:39:48 +02:00
|
|
|
|
2016-08-24 14:29:24 +02:00
|
|
|
if (pim_ifp && PIM_I_am_DR (pim_ifp) &&
|
2016-08-23 19:39:48 +02:00
|
|
|
pim_if_connected_to_source (up->rpf.source_nexthop.interface, up->sg.src))
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-03-17 19:51:13 +01:00
|
|
|
/* Source registration is supressed for SSM groups. When the SSM range changes
|
|
|
|
* we re-revaluate register setup for existing upstream entries */
|
|
|
|
void
|
|
|
|
pim_upstream_register_reevaluate (void)
|
|
|
|
{
|
|
|
|
struct listnode *upnode;
|
|
|
|
struct pim_upstream *up;
|
|
|
|
|
|
|
|
for (ALL_LIST_ELEMENTS_RO (pim_upstream_list, upnode, up))
|
|
|
|
{
|
|
|
|
/* If FHR is set CouldRegister is True. Also check if the flow
|
|
|
|
* is actually active; if it is not kat setup will trigger source
|
|
|
|
* registration whenever the flow becomes active. */
|
|
|
|
if (!PIM_UPSTREAM_FLAG_TEST_FHR (up->flags) || !up->t_ka_timer)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (pim_is_grp_ssm (up->sg.grp))
|
|
|
|
{
|
|
|
|
/* clear the register state for SSM groups */
|
|
|
|
if (up->reg_state != PIM_REG_NOINFO)
|
|
|
|
{
|
|
|
|
if (PIM_DEBUG_PIM_EVENTS)
|
|
|
|
zlog_debug ("Clear register for %s as G is now SSM",
|
|
|
|
up->sg_str);
|
|
|
|
/* remove regiface from the OIL if it is there*/
|
|
|
|
pim_channel_del_oif (up->channel_oil, pim_regiface,
|
|
|
|
PIM_OIF_FLAG_PROTO_PIM);
|
|
|
|
up->reg_state = PIM_REG_NOINFO;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* register ASM sources with the RP */
|
|
|
|
if (up->reg_state == PIM_REG_NOINFO)
|
|
|
|
{
|
|
|
|
if (PIM_DEBUG_PIM_EVENTS)
|
|
|
|
zlog_debug ("Register %s as G is now ASM", up->sg_str);
|
|
|
|
pim_channel_add_oif (up->channel_oil, pim_regiface,
|
|
|
|
PIM_OIF_FLAG_PROTO_PIM);
|
|
|
|
up->reg_state = PIM_REG_JOIN;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-07-16 14:27:24 +02:00
|
|
|
void
|
|
|
|
pim_upstream_switch(struct pim_upstream *up,
|
|
|
|
enum pim_upstream_state new_state)
|
2015-02-04 07:01:14 +01:00
|
|
|
{
|
|
|
|
enum pim_upstream_state old_state = up->join_state;
|
|
|
|
|
2017-05-09 20:30:43 +02:00
|
|
|
if (PIM_DEBUG_PIM_EVENTS)
|
|
|
|
{
|
|
|
|
zlog_debug ("%s: PIM_UPSTREAM_%s: (S,G) old: %s new: %s",
|
2015-02-04 07:01:14 +01:00
|
|
|
__PRETTY_FUNCTION__,
|
2016-11-17 14:17:25 +01:00
|
|
|
up->sg_str,
|
2016-08-19 13:56:59 +02:00
|
|
|
pim_upstream_state2str (up->join_state),
|
|
|
|
pim_upstream_state2str (new_state));
|
2017-05-09 20:30:43 +02:00
|
|
|
}
|
2015-02-04 07:01:14 +01:00
|
|
|
|
2017-03-09 19:47:50 +01:00
|
|
|
up->join_state = new_state;
|
|
|
|
if (old_state != new_state)
|
|
|
|
up->state_transition = pim_time_monotonic_sec();
|
2016-08-17 21:27:03 +02:00
|
|
|
|
2015-02-04 07:01:14 +01:00
|
|
|
pim_upstream_update_assert_tracking_desired(up);
|
|
|
|
|
|
|
|
if (new_state == PIM_UPSTREAM_JOINED) {
|
2016-08-03 17:34:38 +02:00
|
|
|
if (old_state != PIM_UPSTREAM_JOINED)
|
|
|
|
{
|
2016-09-12 19:02:53 +02:00
|
|
|
int old_fhr = PIM_UPSTREAM_FLAG_TEST_FHR(up->flags);
|
2016-08-03 17:34:38 +02:00
|
|
|
forward_on(up);
|
2016-11-08 19:34:31 +01:00
|
|
|
pim_msdp_up_join_state_changed(up);
|
2016-09-12 19:02:53 +02:00
|
|
|
if (pim_upstream_could_register (up))
|
2016-08-23 19:39:48 +02:00
|
|
|
{
|
2016-09-12 19:02:53 +02:00
|
|
|
PIM_UPSTREAM_FLAG_SET_FHR(up->flags);
|
2016-09-13 14:20:39 +02:00
|
|
|
if (!old_fhr && PIM_UPSTREAM_FLAG_TEST_SRC_STREAM(up->flags))
|
2016-08-23 19:39:48 +02:00
|
|
|
{
|
|
|
|
pim_upstream_keep_alive_timer_start (up, qpim_keep_alive_time);
|
2017-03-17 19:51:13 +01:00
|
|
|
pim_register_join (up);
|
2016-08-23 19:39:48 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
pim_upstream_send_join (up);
|
|
|
|
join_timer_start (up);
|
|
|
|
}
|
2016-08-03 17:34:38 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
forward_on (up);
|
|
|
|
}
|
2015-02-04 07:01:14 +01:00
|
|
|
}
|
|
|
|
else {
|
2017-02-15 03:32:16 +01:00
|
|
|
|
2015-02-04 07:01:14 +01:00
|
|
|
forward_off(up);
|
2016-11-08 19:34:31 +01:00
|
|
|
if (old_state == PIM_UPSTREAM_JOINED)
|
|
|
|
pim_msdp_up_join_state_changed(up);
|
2017-02-15 03:32:16 +01:00
|
|
|
|
2017-05-09 20:30:43 +02:00
|
|
|
/* IHR, Trigger SGRpt on *,G IIF to prune S,G from RPT */
|
|
|
|
if (pim_upstream_is_sg_rpt(up) && up->parent)
|
|
|
|
{
|
|
|
|
if (PIM_DEBUG_PIM_TRACE_DETAIL)
|
|
|
|
zlog_debug ("%s: *,G IIF %s S,G IIF %s ", __PRETTY_FUNCTION__,
|
|
|
|
up->parent->rpf.source_nexthop.interface->name,
|
|
|
|
up->rpf.source_nexthop.interface->name);
|
|
|
|
pim_jp_agg_single_upstream_send(&up->parent->rpf, up->parent, 1 /* (W,G) Join */);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
pim_jp_agg_single_upstream_send(&up->rpf, up, 0 /* prune */);
|
2017-02-15 03:32:16 +01:00
|
|
|
join_timer_stop(up);
|
2015-02-04 07:01:14 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-22 16:28:36 +01:00
|
|
|
int
|
2016-10-24 04:47:25 +02:00
|
|
|
pim_upstream_compare (void *arg1, void *arg2)
|
|
|
|
{
|
|
|
|
const struct pim_upstream *up1 = (const struct pim_upstream *)arg1;
|
|
|
|
const struct pim_upstream *up2 = (const struct pim_upstream *)arg2;
|
|
|
|
|
|
|
|
if (ntohl(up1->sg.grp.s_addr) < ntohl(up2->sg.grp.s_addr))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (ntohl(up1->sg.grp.s_addr) > ntohl(up2->sg.grp.s_addr))
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
if (ntohl(up1->sg.src.s_addr) < ntohl(up2->sg.src.s_addr))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (ntohl(up1->sg.src.s_addr) > ntohl(up2->sg.src.s_addr))
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-12-07 14:15:16 +01:00
|
|
|
static struct pim_upstream *
|
|
|
|
pim_upstream_new (struct prefix_sg *sg,
|
|
|
|
struct interface *incoming,
|
|
|
|
int flags)
|
2015-02-04 07:01:14 +01:00
|
|
|
{
|
2015-06-13 02:47:26 +02:00
|
|
|
enum pim_rpf_result rpf_result;
|
2016-12-07 14:15:16 +01:00
|
|
|
struct pim_interface *pim_ifp;
|
|
|
|
struct pim_upstream *up;
|
2015-02-04 07:01:14 +01:00
|
|
|
|
2016-07-16 08:45:33 +02:00
|
|
|
up = XCALLOC(MTYPE_PIM_UPSTREAM, sizeof(*up));
|
2017-02-22 16:28:36 +01:00
|
|
|
if (!up)
|
|
|
|
{
|
|
|
|
zlog_err("%s: PIM XCALLOC(%zu) failure",
|
2015-02-04 07:01:14 +01:00
|
|
|
__PRETTY_FUNCTION__, sizeof(*up));
|
2017-02-22 16:28:36 +01:00
|
|
|
return NULL;
|
|
|
|
}
|
2015-02-04 07:01:14 +01:00
|
|
|
|
2016-07-23 05:12:06 +02:00
|
|
|
up->sg = *sg;
|
2016-11-17 14:17:25 +01:00
|
|
|
pim_str_sg_set (sg, up->sg_str);
|
2016-10-07 16:25:08 +02:00
|
|
|
up = hash_get (pim_upstream_hash, up, hash_alloc_intern);
|
2016-08-05 19:08:06 +02:00
|
|
|
if (!pim_rp_set_upstream_addr (&up->upstream_addr, sg->src, sg->grp))
|
2015-09-30 14:41:18 +02:00
|
|
|
{
|
2016-12-06 05:27:37 +01:00
|
|
|
if (PIM_DEBUG_TRACE)
|
2015-09-30 14:41:18 +02:00
|
|
|
zlog_debug("%s: Received a (*,G) with no RP configured", __PRETTY_FUNCTION__);
|
|
|
|
|
2016-10-24 04:47:25 +02:00
|
|
|
hash_release (pim_upstream_hash, up);
|
2015-09-30 14:41:18 +02:00
|
|
|
XFREE (MTYPE_PIM_UPSTREAM, up);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2016-10-24 04:47:25 +02:00
|
|
|
up->parent = pim_upstream_find_parent (up);
|
|
|
|
if (up->sg.src.s_addr == INADDR_ANY)
|
|
|
|
{
|
|
|
|
up->sources = list_new ();
|
|
|
|
up->sources->cmp = pim_upstream_compare;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
up->sources = NULL;
|
|
|
|
|
2016-08-02 17:21:49 +02:00
|
|
|
pim_upstream_find_new_children (up);
|
2016-09-15 22:50:08 +02:00
|
|
|
up->flags = flags;
|
2015-02-04 07:01:14 +01:00
|
|
|
up->ref_count = 1;
|
2015-10-16 16:01:47 +02:00
|
|
|
up->t_join_timer = NULL;
|
|
|
|
up->t_ka_timer = NULL;
|
2016-07-18 15:52:45 +02:00
|
|
|
up->t_rs_timer = NULL;
|
2016-11-16 00:39:11 +01:00
|
|
|
up->t_msdp_reg_timer = NULL;
|
2017-03-09 19:47:50 +01:00
|
|
|
up->join_state = PIM_UPSTREAM_NOTJOINED;
|
2017-03-09 18:52:59 +01:00
|
|
|
up->reg_state = PIM_REG_NOINFO;
|
2015-02-04 07:01:14 +01:00
|
|
|
up->state_transition = pim_time_monotonic_sec();
|
2015-10-16 16:01:47 +02:00
|
|
|
up->channel_oil = NULL;
|
2015-11-12 16:54:04 +01:00
|
|
|
up->sptbit = PIM_UPSTREAM_SPTBIT_FALSE;
|
2015-02-04 07:01:14 +01:00
|
|
|
|
2016-08-19 19:25:20 +02:00
|
|
|
up->rpf.source_nexthop.interface = NULL;
|
2016-09-02 18:17:10 +02:00
|
|
|
up->rpf.source_nexthop.mrib_nexthop_addr.family = AF_INET;
|
|
|
|
up->rpf.source_nexthop.mrib_nexthop_addr.u.prefix4.s_addr = PIM_NET_INADDR_ANY;
|
2015-02-04 07:01:14 +01:00
|
|
|
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;
|
2016-09-02 18:17:10 +02:00
|
|
|
up->rpf.rpf_addr.family = AF_INET;
|
|
|
|
up->rpf.rpf_addr.u.prefix4.s_addr = PIM_NET_INADDR_ANY;
|
2015-02-04 07:01:14 +01:00
|
|
|
|
2017-04-20 13:48:27 +02:00
|
|
|
up->ifchannels = list_new();
|
|
|
|
up->ifchannels->cmp = (int (*)(void *, void *))pim_ifchannel_compare;
|
|
|
|
|
2016-11-03 00:19:40 +01:00
|
|
|
if (up->sg.src.s_addr != INADDR_ANY)
|
|
|
|
wheel_add_item (pim_upstream_sg_wheel, up);
|
|
|
|
|
2017-02-22 16:28:36 +01:00
|
|
|
rpf_result = pim_rpf_update(up, NULL, 1);
|
2015-06-13 02:47:26 +02:00
|
|
|
if (rpf_result == PIM_RPF_FAILURE) {
|
2017-03-14 12:58:17 +01:00
|
|
|
struct prefix nht_p;
|
|
|
|
|
2016-12-06 05:27:37 +01:00
|
|
|
if (PIM_DEBUG_TRACE)
|
2016-09-15 22:50:08 +02:00
|
|
|
zlog_debug ("%s: Attempting to create upstream(%s), Unable to RPF for source", __PRETTY_FUNCTION__,
|
2016-11-17 14:17:25 +01:00
|
|
|
up->sg_str);
|
2016-10-24 04:47:25 +02:00
|
|
|
|
2017-03-14 12:58:17 +01:00
|
|
|
nht_p.family = AF_INET;
|
|
|
|
nht_p.prefixlen = IPV4_MAX_BITLEN;
|
|
|
|
nht_p.u.prefix4 = up->upstream_addr;
|
|
|
|
pim_delete_tracked_nexthop (&nht_p, up, NULL);
|
|
|
|
|
2016-10-24 04:47:25 +02:00
|
|
|
if (up->parent)
|
|
|
|
{
|
|
|
|
listnode_delete (up->parent->sources, up);
|
|
|
|
up->parent = NULL;
|
|
|
|
}
|
2016-11-03 00:19:40 +01:00
|
|
|
|
|
|
|
if (up->sg.src.s_addr != INADDR_ANY)
|
|
|
|
wheel_remove_item (pim_upstream_sg_wheel, up);
|
|
|
|
|
2016-10-24 04:47:25 +02:00
|
|
|
pim_upstream_remove_children (up);
|
|
|
|
if (up->sources)
|
|
|
|
list_delete (up->sources);
|
|
|
|
|
|
|
|
hash_release (pim_upstream_hash, up);
|
2015-06-13 02:47:26 +02:00
|
|
|
XFREE(MTYPE_PIM_UPSTREAM, up);
|
|
|
|
return NULL;
|
|
|
|
}
|
2015-02-04 07:01:14 +01:00
|
|
|
|
pimd: Pim Nexthop Tracking support with ECMP
In this patch, PIM nexthop tracking uses locally populated nexthop cached list
to determine ECMP based nexthop (w/ ECMP knob enabled), otherwise picks
the first nexthop as RPF.
Introduced '[no] ip pim ecmp' command to enable/disable PIM ECMP knob.
By default, PIM ECMP is disabled.
Intorudced '[no] ip pim ecmp rebalance' command to provide existing mcache
entry to switch new path based on hash chosen path.
Introduced, show command to display pim registered addresses and respective nexthops.
Introuduce, show command to find nexthop and out interface for (S,G) or (RP,G).
Re-Register an address with nexthop when Interface UP event received,
to ensure the PIM nexthop cache is updated (being PIM enabled).
During PIM neighbor UP, traverse all RPs and Upstreams nexthop and determine, if
any of nexthop's IPv4 address changes/resolves due to neigbor UP event.
Testing Done: Run various LHR, RP and FHR related cases to resolve RPF using
nexthop cache with ECMP knob disabled, performed interface/PIM neighbor flap events.
Executed pim-smoke with knob disabled.
Signed-off-by: Chirag Shah <chirag@cumulusnetworks.com>
(cherry picked from commit cba444817883b8b3b22a7ed9958dc9ed77f76230)
2017-04-05 22:14:12 +02:00
|
|
|
if (up->rpf.source_nexthop.interface)
|
|
|
|
{
|
|
|
|
pim_ifp = up->rpf.source_nexthop.interface->info;
|
|
|
|
if (pim_ifp)
|
|
|
|
up->channel_oil = pim_channel_oil_add(&up->sg, pim_ifp->mroute_vif_index);
|
|
|
|
}
|
2016-10-07 16:25:08 +02:00
|
|
|
listnode_add_sort(pim_upstream_list, up);
|
2015-02-04 07:01:14 +01:00
|
|
|
|
2016-12-06 05:27:37 +01:00
|
|
|
if (PIM_DEBUG_TRACE)
|
2017-02-22 16:28:36 +01:00
|
|
|
{
|
2017-05-09 20:30:43 +02:00
|
|
|
zlog_debug ("%s: Created Upstream %s upstream_addr %s ref count %d increment",
|
2017-02-22 16:28:36 +01:00
|
|
|
__PRETTY_FUNCTION__, up->sg_str,
|
2017-05-09 20:30:43 +02:00
|
|
|
inet_ntoa (up->upstream_addr), up->ref_count);
|
2017-02-22 16:28:36 +01:00
|
|
|
}
|
2016-10-24 04:47:25 +02:00
|
|
|
|
2015-02-04 07:01:14 +01:00
|
|
|
return up;
|
|
|
|
}
|
|
|
|
|
2016-08-02 10:38:11 +02:00
|
|
|
struct pim_upstream *pim_upstream_find(struct prefix_sg *sg)
|
2015-02-04 07:01:14 +01:00
|
|
|
{
|
2016-10-07 16:25:08 +02:00
|
|
|
struct pim_upstream lookup;
|
|
|
|
struct pim_upstream *up = NULL;
|
2015-02-04 07:01:14 +01:00
|
|
|
|
2016-10-07 16:25:08 +02:00
|
|
|
lookup.sg = *sg;
|
|
|
|
up = hash_lookup (pim_upstream_hash, &lookup);
|
|
|
|
return up;
|
2015-02-04 07:01:14 +01:00
|
|
|
}
|
|
|
|
|
2017-03-10 16:26:00 +01:00
|
|
|
struct pim_upstream *
|
|
|
|
pim_upstream_find_or_add(struct prefix_sg *sg,
|
|
|
|
struct interface *incoming,
|
|
|
|
int flags, const char *name)
|
|
|
|
{
|
|
|
|
struct pim_upstream *up;
|
|
|
|
|
|
|
|
up = pim_upstream_find(sg);
|
|
|
|
|
|
|
|
if (up)
|
|
|
|
{
|
|
|
|
if (!(up->flags & flags))
|
|
|
|
{
|
|
|
|
up->flags |= flags;
|
|
|
|
up->ref_count++;
|
2017-05-09 20:30:43 +02:00
|
|
|
if (PIM_DEBUG_TRACE)
|
|
|
|
zlog_debug ("%s(%s): upstream %s ref count %d increment",
|
|
|
|
__PRETTY_FUNCTION__, name, up->sg_str, up->ref_count);
|
2017-03-10 16:26:00 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
up = pim_upstream_add (sg, incoming, flags, name);
|
|
|
|
|
|
|
|
return up;
|
|
|
|
}
|
|
|
|
|
2017-03-10 21:01:11 +01:00
|
|
|
void
|
2017-05-09 20:30:43 +02:00
|
|
|
pim_upstream_ref(struct pim_upstream *up, int flags, const char *name)
|
2016-11-16 00:39:11 +01:00
|
|
|
{
|
|
|
|
up->flags |= flags;
|
|
|
|
++up->ref_count;
|
2017-05-09 20:30:43 +02:00
|
|
|
if (PIM_DEBUG_TRACE)
|
|
|
|
zlog_debug ("%s(%s): upstream %s ref count %d increment",
|
|
|
|
__PRETTY_FUNCTION__, name, up->sg_str, up->ref_count);
|
2016-11-16 00:39:11 +01:00
|
|
|
}
|
|
|
|
|
2016-08-02 10:38:11 +02:00
|
|
|
struct pim_upstream *pim_upstream_add(struct prefix_sg *sg,
|
2016-09-15 22:50:08 +02:00
|
|
|
struct interface *incoming,
|
2016-10-27 14:05:57 +02:00
|
|
|
int flags, const char *name)
|
2015-02-04 07:01:14 +01:00
|
|
|
{
|
2016-10-17 14:33:13 +02:00
|
|
|
struct pim_upstream *up = NULL;
|
2016-10-27 14:05:57 +02:00
|
|
|
int found = 0;
|
2016-07-23 05:12:06 +02:00
|
|
|
up = pim_upstream_find(sg);
|
2015-02-04 07:01:14 +01:00
|
|
|
if (up) {
|
2017-05-09 20:30:43 +02:00
|
|
|
pim_upstream_ref(up, flags, name);
|
2016-10-27 14:05:57 +02:00
|
|
|
found = 1;
|
2015-02-04 07:01:14 +01:00
|
|
|
}
|
|
|
|
else {
|
2016-09-15 22:50:08 +02:00
|
|
|
up = pim_upstream_new(sg, incoming, flags);
|
2015-02-04 07:01:14 +01:00
|
|
|
}
|
|
|
|
|
2016-10-27 14:05:57 +02:00
|
|
|
if (PIM_DEBUG_TRACE)
|
2016-10-28 00:27:28 +02:00
|
|
|
{
|
|
|
|
if (up)
|
pimd: Pim Nexthop Tracking support with ECMP
In this patch, PIM nexthop tracking uses locally populated nexthop cached list
to determine ECMP based nexthop (w/ ECMP knob enabled), otherwise picks
the first nexthop as RPF.
Introduced '[no] ip pim ecmp' command to enable/disable PIM ECMP knob.
By default, PIM ECMP is disabled.
Intorudced '[no] ip pim ecmp rebalance' command to provide existing mcache
entry to switch new path based on hash chosen path.
Introduced, show command to display pim registered addresses and respective nexthops.
Introuduce, show command to find nexthop and out interface for (S,G) or (RP,G).
Re-Register an address with nexthop when Interface UP event received,
to ensure the PIM nexthop cache is updated (being PIM enabled).
During PIM neighbor UP, traverse all RPs and Upstreams nexthop and determine, if
any of nexthop's IPv4 address changes/resolves due to neigbor UP event.
Testing Done: Run various LHR, RP and FHR related cases to resolve RPF using
nexthop cache with ECMP knob disabled, performed interface/PIM neighbor flap events.
Executed pim-smoke with knob disabled.
Signed-off-by: Chirag Shah <chirag@cumulusnetworks.com>
(cherry picked from commit cba444817883b8b3b22a7ed9958dc9ed77f76230)
2017-04-05 22:14:12 +02:00
|
|
|
{
|
|
|
|
char buf[PREFIX2STR_BUFFER];
|
|
|
|
prefix2str (&up->rpf.rpf_addr, buf, sizeof (buf));
|
2017-05-09 20:30:43 +02:00
|
|
|
zlog_debug("%s(%s): %s, iif %s (%s) found: %d: ref_count: %d",
|
2016-10-28 00:27:28 +02:00
|
|
|
__PRETTY_FUNCTION__, name,
|
2017-05-09 20:30:43 +02:00
|
|
|
up->sg_str, buf, up->rpf.source_nexthop.interface ?
|
|
|
|
up->rpf.source_nexthop.interface->name : "NIL" ,
|
|
|
|
found, up->ref_count);
|
pimd: Pim Nexthop Tracking support with ECMP
In this patch, PIM nexthop tracking uses locally populated nexthop cached list
to determine ECMP based nexthop (w/ ECMP knob enabled), otherwise picks
the first nexthop as RPF.
Introduced '[no] ip pim ecmp' command to enable/disable PIM ECMP knob.
By default, PIM ECMP is disabled.
Intorudced '[no] ip pim ecmp rebalance' command to provide existing mcache
entry to switch new path based on hash chosen path.
Introduced, show command to display pim registered addresses and respective nexthops.
Introuduce, show command to find nexthop and out interface for (S,G) or (RP,G).
Re-Register an address with nexthop when Interface UP event received,
to ensure the PIM nexthop cache is updated (being PIM enabled).
During PIM neighbor UP, traverse all RPs and Upstreams nexthop and determine, if
any of nexthop's IPv4 address changes/resolves due to neigbor UP event.
Testing Done: Run various LHR, RP and FHR related cases to resolve RPF using
nexthop cache with ECMP knob disabled, performed interface/PIM neighbor flap events.
Executed pim-smoke with knob disabled.
Signed-off-by: Chirag Shah <chirag@cumulusnetworks.com>
(cherry picked from commit cba444817883b8b3b22a7ed9958dc9ed77f76230)
2017-04-05 22:14:12 +02:00
|
|
|
}
|
2016-10-28 00:27:28 +02:00
|
|
|
else
|
|
|
|
zlog_debug("%s(%s): (%s) failure to create",
|
|
|
|
__PRETTY_FUNCTION__, name,
|
|
|
|
pim_str_sg_dump (sg));
|
|
|
|
}
|
2015-02-04 07:01:14 +01:00
|
|
|
|
2016-10-27 14:05:57 +02:00
|
|
|
return up;
|
2015-02-04 07:01:14 +01:00
|
|
|
}
|
|
|
|
|
2017-04-20 15:03:47 +02:00
|
|
|
/*
|
|
|
|
* Passed in up must be the upstream for ch. starch is NULL if no
|
|
|
|
* information
|
|
|
|
*/
|
2017-02-11 12:53:00 +01:00
|
|
|
int
|
2016-08-04 18:06:58 +02:00
|
|
|
pim_upstream_evaluate_join_desired_interface (struct pim_upstream *up,
|
2017-04-20 15:03:47 +02:00
|
|
|
struct pim_ifchannel *ch,
|
|
|
|
struct pim_ifchannel *starch)
|
2016-08-04 18:06:58 +02:00
|
|
|
{
|
2017-04-20 15:03:47 +02:00
|
|
|
if (ch)
|
2016-08-04 18:06:58 +02:00
|
|
|
{
|
2016-11-11 03:08:48 +01:00
|
|
|
if (PIM_IF_FLAG_TEST_S_G_RPT(ch->flags))
|
2017-04-20 15:03:47 +02:00
|
|
|
return 0;
|
2017-03-17 18:46:47 +01:00
|
|
|
|
|
|
|
if (!pim_macro_ch_lost_assert(ch) && pim_macro_chisin_joins_or_include(ch))
|
2017-04-20 15:03:47 +02:00
|
|
|
return 1;
|
2016-08-04 18:06:58 +02:00
|
|
|
}
|
2016-11-11 03:08:48 +01:00
|
|
|
|
2016-08-04 18:06:58 +02:00
|
|
|
/*
|
|
|
|
* joins (*,G)
|
|
|
|
*/
|
2017-04-20 15:03:47 +02:00
|
|
|
if (starch)
|
2016-08-04 18:06:58 +02:00
|
|
|
{
|
2017-04-20 15:03:47 +02:00
|
|
|
if (PIM_IF_FLAG_TEST_S_G_RPT (starch->upstream->flags))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (!pim_macro_ch_lost_assert (starch) && pim_macro_chisin_joins_or_include (starch))
|
|
|
|
return 1;
|
2016-08-04 18:06:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-02-04 07:01:14 +01:00
|
|
|
/*
|
|
|
|
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)
|
|
|
|
{
|
2017-04-20 15:03:47 +02:00
|
|
|
struct interface *ifp;
|
|
|
|
struct listnode *node;
|
|
|
|
struct pim_ifchannel *ch, *starch;
|
|
|
|
struct pim_upstream *starup = up->parent;
|
2016-08-04 18:06:58 +02:00
|
|
|
int ret = 0;
|
2015-02-04 07:01:14 +01:00
|
|
|
|
2017-04-20 15:03:47 +02:00
|
|
|
for (ALL_LIST_ELEMENTS_RO (vrf_iflist (VRF_DEFAULT), node, ifp))
|
2016-11-07 18:34:44 +01:00
|
|
|
{
|
2017-04-20 15:03:47 +02:00
|
|
|
if (!ifp->info)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
ch = pim_ifchannel_find (ifp, &up->sg);
|
|
|
|
|
|
|
|
if (starup)
|
|
|
|
starch = pim_ifchannel_find (ifp, &starup->sg);
|
|
|
|
else
|
|
|
|
starch = NULL;
|
|
|
|
|
|
|
|
if (!ch && !starch)
|
|
|
|
continue;
|
2015-02-04 07:01:14 +01:00
|
|
|
|
2017-04-20 15:03:47 +02:00
|
|
|
ret += pim_upstream_evaluate_join_desired_interface (up, ch, starch);
|
2016-11-07 18:34:44 +01:00
|
|
|
} /* scan iface channel list */
|
2015-02-04 07:01:14 +01:00
|
|
|
|
2016-08-04 18:06:58 +02:00
|
|
|
return ret; /* false */
|
2015-02-04 07:01:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
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) {
|
|
|
|
pim_upstream_switch(up, PIM_UPSTREAM_JOINED);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* switched from true to false */
|
|
|
|
if (!is_join_desired && was_join_desired) {
|
|
|
|
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;
|
|
|
|
|
|
|
|
/*
|
2016-10-07 16:25:08 +02:00
|
|
|
* Scan all (S,G) upstreams searching for RPF'(S,G)=neigh_addr
|
|
|
|
*/
|
|
|
|
for (ALL_LIST_ELEMENTS(pim_upstream_list, up_node, up_nextnode, up)) {
|
2015-02-04 07:01:14 +01:00
|
|
|
|
2016-12-06 05:27:37 +01:00
|
|
|
if (PIM_DEBUG_TRACE) {
|
2016-10-20 16:09:30 +02:00
|
|
|
char neigh_str[INET_ADDRSTRLEN];
|
|
|
|
char rpf_addr_str[PREFIX_STRLEN];
|
2015-02-04 07:01:14 +01:00
|
|
|
pim_inet4_dump("<neigh?>", neigh_addr, neigh_str, sizeof(neigh_str));
|
2016-09-02 18:17:10 +02:00
|
|
|
pim_addr_dump("<rpf?>", &up->rpf.rpf_addr, rpf_addr_str, sizeof(rpf_addr_str));
|
2016-07-22 14:57:20 +02:00
|
|
|
zlog_debug("%s: matching neigh=%s against upstream (S,G)=%s joined=%d rpf_addr=%s",
|
2015-02-04 07:01:14 +01:00
|
|
|
__PRETTY_FUNCTION__,
|
2016-11-17 14:17:25 +01:00
|
|
|
neigh_str, up->sg_str,
|
2015-02-04 07:01:14 +01:00
|
|
|
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 */
|
2016-09-02 18:17:10 +02:00
|
|
|
if (up->rpf.rpf_addr.u.prefix4.s_addr != neigh_addr.s_addr)
|
2015-02-04 07:01:14 +01:00
|
|
|
continue;
|
|
|
|
|
|
|
|
pim_upstream_join_timer_decrease_to_t_override("RPF'(S,G) GenID change",
|
2017-02-10 21:04:17 +01:00
|
|
|
up);
|
2015-02-04 07:01:14 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void pim_upstream_rpf_interface_changed(struct pim_upstream *up,
|
|
|
|
struct interface *old_rpf_ifp)
|
|
|
|
{
|
2016-11-07 18:34:44 +01:00
|
|
|
struct listnode *chnode;
|
|
|
|
struct listnode *chnextnode;
|
|
|
|
struct pim_ifchannel *ch;
|
|
|
|
|
|
|
|
/* search all ifchannels */
|
2017-04-20 16:12:41 +02:00
|
|
|
for (ALL_LIST_ELEMENTS(up->ifchannels, chnode, chnextnode, ch)) {
|
2016-11-07 18:34:44 +01:00
|
|
|
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 */
|
2015-02-04 07:01:14 +01:00
|
|
|
|
2016-11-07 18:34:44 +01:00
|
|
|
pim_ifchannel_update_assert_tracking_desired(ch);
|
2015-02-04 07:01:14 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void pim_upstream_update_could_assert(struct pim_upstream *up)
|
|
|
|
{
|
|
|
|
struct listnode *chnode;
|
|
|
|
struct listnode *chnextnode;
|
|
|
|
struct pim_ifchannel *ch;
|
|
|
|
|
2016-11-07 18:34:44 +01:00
|
|
|
/* scan per-interface (S,G) state */
|
2017-04-20 16:12:41 +02:00
|
|
|
for (ALL_LIST_ELEMENTS(up->ifchannels, chnode, chnextnode, ch)) {
|
2016-11-07 18:34:44 +01:00
|
|
|
pim_ifchannel_update_could_assert(ch);
|
|
|
|
} /* scan iface channel list */
|
2015-02-04 07:01:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void pim_upstream_update_my_assert_metric(struct pim_upstream *up)
|
|
|
|
{
|
|
|
|
struct listnode *chnode;
|
|
|
|
struct listnode *chnextnode;
|
|
|
|
struct pim_ifchannel *ch;
|
|
|
|
|
2016-11-07 18:34:44 +01:00
|
|
|
/* scan per-interface (S,G) state */
|
2017-04-20 16:12:41 +02:00
|
|
|
for (ALL_LIST_ELEMENTS(up->ifchannels, chnode, chnextnode, ch)) {
|
2016-11-07 18:34:44 +01:00
|
|
|
pim_ifchannel_update_my_assert_metric(ch);
|
2015-02-04 07:01:14 +01:00
|
|
|
|
2016-11-07 18:34:44 +01:00
|
|
|
} /* scan iface channel list */
|
2015-02-04 07:01:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static void pim_upstream_update_assert_tracking_desired(struct pim_upstream *up)
|
|
|
|
{
|
|
|
|
struct listnode *chnode;
|
|
|
|
struct listnode *chnextnode;
|
|
|
|
struct pim_interface *pim_ifp;
|
|
|
|
struct pim_ifchannel *ch;
|
|
|
|
|
2016-11-07 18:34:44 +01:00
|
|
|
/* scan per-interface (S,G) state */
|
pimd: Fix WG/SGRpt & WG J/P processing
During processing of Join/Prune,
for a S,G entry, current state is SGRpt, when only *,G is
received, need to clear SGRpt and add/inherit the *,G OIF to S,G so
it can forward traffic to downstream where *,G is received.
Upon receiving SGRpt prune remove the inherited *,G OIF.
From, downstream router received *,G Prune along with SGRpt
prune. Avoid sending *,G and SGRpt Prune together.
Reset upstream_del reset ifchannel to NULL.
Testing Done:
Run failed smoke test of sending data packets, trigger SPT switchover,
*,G path received SGRpt later data traffic stopped S,G ages out from LHR, sends only
*,G join to upstream, verified S,G entry inherit the OIF.
Upon receiving SGRpt deletes inherited oif and retains in SGRpt state.
Signed-off-by: Chirag Shah <chirag@cumulusnetworks.com>
2017-04-22 00:08:03 +02:00
|
|
|
for (ALL_LIST_ELEMENTS(up->ifchannels, chnode, chnextnode, ch))
|
|
|
|
{
|
|
|
|
if (!ch->interface)
|
|
|
|
continue;
|
|
|
|
pim_ifp = ch->interface->info;
|
|
|
|
if (!pim_ifp)
|
|
|
|
continue;
|
2015-02-04 07:01:14 +01:00
|
|
|
|
pimd: Fix WG/SGRpt & WG J/P processing
During processing of Join/Prune,
for a S,G entry, current state is SGRpt, when only *,G is
received, need to clear SGRpt and add/inherit the *,G OIF to S,G so
it can forward traffic to downstream where *,G is received.
Upon receiving SGRpt prune remove the inherited *,G OIF.
From, downstream router received *,G Prune along with SGRpt
prune. Avoid sending *,G and SGRpt Prune together.
Reset upstream_del reset ifchannel to NULL.
Testing Done:
Run failed smoke test of sending data packets, trigger SPT switchover,
*,G path received SGRpt later data traffic stopped S,G ages out from LHR, sends only
*,G join to upstream, verified S,G entry inherit the OIF.
Upon receiving SGRpt deletes inherited oif and retains in SGRpt state.
Signed-off-by: Chirag Shah <chirag@cumulusnetworks.com>
2017-04-22 00:08:03 +02:00
|
|
|
pim_ifchannel_update_assert_tracking_desired(ch);
|
2015-02-04 07:01:14 +01:00
|
|
|
|
pimd: Fix WG/SGRpt & WG J/P processing
During processing of Join/Prune,
for a S,G entry, current state is SGRpt, when only *,G is
received, need to clear SGRpt and add/inherit the *,G OIF to S,G so
it can forward traffic to downstream where *,G is received.
Upon receiving SGRpt prune remove the inherited *,G OIF.
From, downstream router received *,G Prune along with SGRpt
prune. Avoid sending *,G and SGRpt Prune together.
Reset upstream_del reset ifchannel to NULL.
Testing Done:
Run failed smoke test of sending data packets, trigger SPT switchover,
*,G path received SGRpt later data traffic stopped S,G ages out from LHR, sends only
*,G join to upstream, verified S,G entry inherit the OIF.
Upon receiving SGRpt deletes inherited oif and retains in SGRpt state.
Signed-off-by: Chirag Shah <chirag@cumulusnetworks.com>
2017-04-22 00:08:03 +02:00
|
|
|
} /* scan iface channel list */
|
2015-02-04 07:01:14 +01:00
|
|
|
}
|
2015-10-16 16:50:16 +02:00
|
|
|
|
2016-11-16 00:39:11 +01:00
|
|
|
/* When kat is stopped CouldRegister goes to false so we need to
|
|
|
|
* transition the (S, G) on FHR to NI state and remove reg tunnel
|
|
|
|
* from the OIL */
|
|
|
|
static void pim_upstream_fhr_kat_expiry(struct pim_upstream *up)
|
|
|
|
{
|
|
|
|
if (!PIM_UPSTREAM_FLAG_TEST_FHR(up->flags))
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (PIM_DEBUG_TRACE)
|
2016-11-17 14:17:25 +01:00
|
|
|
zlog_debug ("kat expired on %s; clear fhr reg state", up->sg_str);
|
|
|
|
|
2016-11-16 00:39:11 +01:00
|
|
|
/* stop reg-stop timer */
|
|
|
|
THREAD_OFF(up->t_rs_timer);
|
|
|
|
/* remove regiface from the OIL if it is there*/
|
|
|
|
pim_channel_del_oif (up->channel_oil, pim_regiface, PIM_OIF_FLAG_PROTO_PIM);
|
2017-03-09 18:52:59 +01:00
|
|
|
/* clear the register state */
|
|
|
|
up->reg_state = PIM_REG_NOINFO;
|
2016-11-16 00:39:11 +01:00
|
|
|
PIM_UPSTREAM_FLAG_UNSET_FHR(up->flags);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* When kat is started CouldRegister can go to true. And if it does we
|
|
|
|
* need to transition the (S, G) on FHR to JOINED state and add reg tunnel
|
|
|
|
* to the OIL */
|
|
|
|
static void pim_upstream_fhr_kat_start(struct pim_upstream *up)
|
|
|
|
{
|
|
|
|
if (pim_upstream_could_register(up)) {
|
|
|
|
if (PIM_DEBUG_TRACE)
|
2016-11-17 14:17:25 +01:00
|
|
|
zlog_debug ("kat started on %s; set fhr reg state to joined", up->sg_str);
|
|
|
|
|
2016-11-16 00:39:11 +01:00
|
|
|
PIM_UPSTREAM_FLAG_SET_FHR(up->flags);
|
2017-03-17 19:51:13 +01:00
|
|
|
if (up->reg_state == PIM_REG_NOINFO)
|
|
|
|
pim_register_join (up);
|
2016-11-16 00:39:11 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-10-16 16:50:16 +02:00
|
|
|
/*
|
|
|
|
* On an RP, the PMBR value must be cleared when the
|
|
|
|
* Keepalive Timer expires
|
2016-11-16 00:39:11 +01:00
|
|
|
* KAT expiry indicates that flow is inactive. If the flow was created or
|
|
|
|
* maintained by activity now is the time to deref it.
|
2015-10-16 16:50:16 +02:00
|
|
|
*/
|
|
|
|
static int
|
|
|
|
pim_upstream_keep_alive_timer (struct thread *t)
|
|
|
|
{
|
|
|
|
struct pim_upstream *up;
|
|
|
|
|
|
|
|
up = THREAD_ARG(t);
|
2016-11-16 00:39:11 +01:00
|
|
|
up->t_ka_timer = NULL;
|
2015-10-16 16:50:16 +02:00
|
|
|
|
2016-08-02 10:38:11 +02:00
|
|
|
if (I_am_RP (up->sg.grp))
|
pimd: Allow SPT switchover
This allows SPT switchover for S,G upon receipt of packets
on the LHR.
1) When we create a *,G from a IGMP Group Report, install
the *,G route with the pimreg device on the OIL.
2) When a packet hits the LHR that matches the *,G, we will
get a WHOLEPKT callback from the kernel and if we cannot
find the S,G, that means we have matched it on the LHR via
the *,G mroute. Create the S,G start the KAT and run
inherited_olist.
3) When the S,G times out, safely remove the S,G via
the KAT expiry
4) When the *,G is removed, remove any S,G associated
with it via the LHR flag.
Signed-off-by: Donald Sharp <sharpd@cumulusnetworks.com>
2017-03-23 02:07:57 +01:00
|
|
|
{
|
|
|
|
pim_br_clear_pmbr (&up->sg);
|
|
|
|
/*
|
|
|
|
* We need to do more here :)
|
|
|
|
* But this is the start.
|
|
|
|
*/
|
|
|
|
}
|
2016-08-09 00:56:05 +02:00
|
|
|
|
2016-11-16 00:39:11 +01:00
|
|
|
/* source is no longer active - pull the SA from MSDP's cache */
|
|
|
|
pim_msdp_sa_local_del(&up->sg);
|
2016-08-09 00:56:05 +02:00
|
|
|
|
2016-11-16 00:39:11 +01:00
|
|
|
/* if entry was created because of activity we need to deref it */
|
|
|
|
if (PIM_UPSTREAM_FLAG_TEST_SRC_STREAM(up->flags))
|
pimd: Allow SPT switchover
This allows SPT switchover for S,G upon receipt of packets
on the LHR.
1) When we create a *,G from a IGMP Group Report, install
the *,G route with the pimreg device on the OIL.
2) When a packet hits the LHR that matches the *,G, we will
get a WHOLEPKT callback from the kernel and if we cannot
find the S,G, that means we have matched it on the LHR via
the *,G mroute. Create the S,G start the KAT and run
inherited_olist.
3) When the S,G times out, safely remove the S,G via
the KAT expiry
4) When the *,G is removed, remove any S,G associated
with it via the LHR flag.
Signed-off-by: Donald Sharp <sharpd@cumulusnetworks.com>
2017-03-23 02:07:57 +01:00
|
|
|
{
|
|
|
|
pim_upstream_fhr_kat_expiry(up);
|
|
|
|
if (PIM_DEBUG_TRACE)
|
|
|
|
zlog_debug ("kat expired on %s; remove stream reference", up->sg_str);
|
|
|
|
PIM_UPSTREAM_FLAG_UNSET_SRC_STREAM(up->flags);
|
|
|
|
pim_upstream_del(up, __PRETTY_FUNCTION__);
|
|
|
|
}
|
|
|
|
else if (PIM_UPSTREAM_FLAG_TEST_SRC_LHR(up->flags))
|
|
|
|
{
|
|
|
|
PIM_UPSTREAM_FLAG_UNSET_SRC_LHR(up->flags);
|
|
|
|
pim_upstream_del(up, __PRETTY_FUNCTION__);
|
|
|
|
}
|
2016-08-09 00:56:05 +02:00
|
|
|
|
2016-11-16 00:39:11 +01:00
|
|
|
return 0;
|
2015-10-16 16:50:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
pim_upstream_keep_alive_timer_start (struct pim_upstream *up,
|
|
|
|
uint32_t time)
|
|
|
|
{
|
2016-11-16 00:39:11 +01:00
|
|
|
if (!PIM_UPSTREAM_FLAG_TEST_SRC_STREAM(up->flags)) {
|
|
|
|
if (PIM_DEBUG_TRACE)
|
2016-11-17 14:17:25 +01:00
|
|
|
zlog_debug ("kat start on %s with no stream reference", up->sg_str);
|
2016-11-16 00:39:11 +01:00
|
|
|
}
|
2016-08-18 02:44:51 +02:00
|
|
|
THREAD_OFF (up->t_ka_timer);
|
2015-10-16 16:50:16 +02:00
|
|
|
THREAD_TIMER_ON (master,
|
|
|
|
up->t_ka_timer,
|
|
|
|
pim_upstream_keep_alive_timer,
|
|
|
|
up, time);
|
2016-11-16 00:39:11 +01:00
|
|
|
|
|
|
|
/* any time keepalive is started against a SG we will have to
|
|
|
|
* re-evaluate our active source database */
|
|
|
|
pim_msdp_sa_local_update(up);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* MSDP on RP needs to know if a source is registerable to this RP */
|
|
|
|
static int
|
|
|
|
pim_upstream_msdp_reg_timer(struct thread *t)
|
|
|
|
{
|
|
|
|
struct pim_upstream *up;
|
|
|
|
|
|
|
|
up = THREAD_ARG(t);
|
|
|
|
up->t_msdp_reg_timer = NULL;
|
|
|
|
|
|
|
|
/* source is no longer active - pull the SA from MSDP's cache */
|
|
|
|
pim_msdp_sa_local_del(&up->sg);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
void
|
|
|
|
pim_upstream_msdp_reg_timer_start(struct pim_upstream *up)
|
|
|
|
{
|
|
|
|
THREAD_OFF(up->t_msdp_reg_timer);
|
|
|
|
THREAD_TIMER_ON(master, up->t_msdp_reg_timer,
|
|
|
|
pim_upstream_msdp_reg_timer, up, PIM_MSDP_REG_RXED_PERIOD);
|
|
|
|
|
|
|
|
pim_msdp_sa_local_update(up);
|
2015-10-16 16:50:16 +02:00
|
|
|
}
|
2015-10-16 18:11:51 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 4.2.1 Last-Hop Switchover to the SPT
|
|
|
|
*
|
|
|
|
* In Sparse-Mode PIM, last-hop routers join the shared tree towards the
|
|
|
|
* RP. Once traffic from sources to joined groups arrives at a last-hop
|
|
|
|
* router, it has the option of switching to receive the traffic on a
|
|
|
|
* shortest path tree (SPT).
|
|
|
|
*
|
|
|
|
* The decision for a router to switch to the SPT is controlled as
|
|
|
|
* follows:
|
|
|
|
*
|
|
|
|
* void
|
|
|
|
* CheckSwitchToSpt(S,G) {
|
|
|
|
* if ( ( pim_include(*,G) (-) pim_exclude(S,G)
|
|
|
|
* (+) pim_include(S,G) != NULL )
|
|
|
|
* AND SwitchToSptDesired(S,G) ) {
|
|
|
|
* # Note: Restarting the KAT will result in the SPT switch
|
|
|
|
* set KeepaliveTimer(S,G) to Keepalive_Period
|
|
|
|
* }
|
|
|
|
* }
|
|
|
|
*
|
|
|
|
* SwitchToSptDesired(S,G) is a policy function that is implementation
|
|
|
|
* defined. An "infinite threshold" policy can be implemented by making
|
|
|
|
* SwitchToSptDesired(S,G) return false all the time. A "switch on
|
|
|
|
* first packet" policy can be implemented by making
|
|
|
|
* SwitchToSptDesired(S,G) return true once a single packet has been
|
|
|
|
* received for the source and group.
|
|
|
|
*/
|
|
|
|
int
|
2016-08-02 10:38:11 +02:00
|
|
|
pim_upstream_switch_to_spt_desired (struct prefix_sg *sg)
|
2015-10-16 18:11:51 +02:00
|
|
|
{
|
2016-08-02 10:38:11 +02:00
|
|
|
if (I_am_RP (sg->grp))
|
2016-07-23 09:33:53 +02:00
|
|
|
return 1;
|
|
|
|
|
2015-10-16 18:11:51 +02:00
|
|
|
return 0;
|
|
|
|
}
|
2016-07-18 01:45:16 +02:00
|
|
|
|
2016-11-02 16:20:27 +01:00
|
|
|
int
|
|
|
|
pim_upstream_is_sg_rpt (struct pim_upstream *up)
|
|
|
|
{
|
2016-11-11 02:09:01 +01:00
|
|
|
struct listnode *chnode;
|
|
|
|
struct pim_ifchannel *ch;
|
|
|
|
|
2017-04-20 16:12:41 +02:00
|
|
|
for (ALL_LIST_ELEMENTS_RO(up->ifchannels, chnode, ch))
|
2016-11-11 02:09:01 +01:00
|
|
|
{
|
2017-04-20 16:12:41 +02:00
|
|
|
if (PIM_IF_FLAG_TEST_S_G_RPT(ch->flags))
|
|
|
|
return 1;
|
2016-11-11 02:09:01 +01:00
|
|
|
}
|
2016-11-03 15:02:59 +01:00
|
|
|
|
2016-11-02 16:20:27 +01:00
|
|
|
return 0;
|
|
|
|
}
|
2016-11-03 15:56:39 +01:00
|
|
|
/*
|
|
|
|
* After receiving a packet set SPTbit:
|
|
|
|
* void
|
|
|
|
* Update_SPTbit(S,G,iif) {
|
|
|
|
* if ( iif == RPF_interface(S)
|
|
|
|
* AND JoinDesired(S,G) == TRUE
|
|
|
|
* AND ( DirectlyConnected(S) == TRUE
|
|
|
|
* OR RPF_interface(S) != RPF_interface(RP(G))
|
|
|
|
* OR inherited_olist(S,G,rpt) == NULL
|
|
|
|
* OR ( ( RPF'(S,G) == RPF'(*,G) ) AND
|
|
|
|
* ( RPF'(S,G) != NULL ) )
|
|
|
|
* OR ( I_Am_Assert_Loser(S,G,iif) ) {
|
|
|
|
* Set SPTbit(S,G) to TRUE
|
|
|
|
* }
|
|
|
|
* }
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
pim_upstream_set_sptbit (struct pim_upstream *up, struct interface *incoming)
|
|
|
|
{
|
2017-04-20 22:11:58 +02:00
|
|
|
struct pim_upstream *starup = up->parent;
|
2016-11-03 15:56:39 +01:00
|
|
|
|
|
|
|
// iif == RPF_interfvace(S)
|
|
|
|
if (up->rpf.source_nexthop.interface != incoming)
|
|
|
|
{
|
|
|
|
if (PIM_DEBUG_TRACE)
|
|
|
|
zlog_debug ("%s: Incoming Interface: %s is different than RPF_interface(S) %s",
|
|
|
|
__PRETTY_FUNCTION__, incoming->name, up->rpf.source_nexthop.interface->name);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// AND JoinDesired(S,G) == TRUE
|
|
|
|
// FIXME
|
|
|
|
|
|
|
|
// DirectlyConnected(S) == TRUE
|
|
|
|
if (pim_if_connected_to_source (up->rpf.source_nexthop.interface, up->sg.src))
|
|
|
|
{
|
|
|
|
if (PIM_DEBUG_TRACE)
|
|
|
|
zlog_debug ("%s: %s is directly connected to the source", __PRETTY_FUNCTION__,
|
2016-11-17 14:17:25 +01:00
|
|
|
up->sg_str);
|
2016-11-03 15:56:39 +01:00
|
|
|
up->sptbit = PIM_UPSTREAM_SPTBIT_TRUE;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// OR RPF_interface(S) != RPF_interface(RP(G))
|
2017-04-20 22:11:58 +02:00
|
|
|
if (!starup || up->rpf.source_nexthop.interface != starup->rpf.source_nexthop.interface)
|
2016-11-03 15:56:39 +01:00
|
|
|
{
|
|
|
|
if (PIM_DEBUG_TRACE)
|
|
|
|
zlog_debug ("%s: %s RPF_interface(S) != RPF_interface(RP(G))",
|
2016-11-17 14:17:25 +01:00
|
|
|
__PRETTY_FUNCTION__, up->sg_str);
|
2016-11-03 15:56:39 +01:00
|
|
|
up->sptbit = PIM_UPSTREAM_SPTBIT_TRUE;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// OR inherited_olist(S,G,rpt) == NULL
|
|
|
|
if (pim_upstream_is_sg_rpt(up) && pim_upstream_empty_inherited_olist(up))
|
|
|
|
{
|
|
|
|
if (PIM_DEBUG_TRACE)
|
|
|
|
zlog_debug ("%s: %s OR inherited_olist(S,G,rpt) == NULL", __PRETTY_FUNCTION__,
|
2016-11-17 14:17:25 +01:00
|
|
|
up->sg_str);
|
2016-11-03 15:56:39 +01:00
|
|
|
up->sptbit = PIM_UPSTREAM_SPTBIT_TRUE;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// OR ( ( RPF'(S,G) == RPF'(*,G) ) AND
|
|
|
|
// ( RPF'(S,G) != NULL ) )
|
|
|
|
if (up->parent && pim_rpf_is_same (&up->rpf, &up->parent->rpf))
|
|
|
|
{
|
|
|
|
if (PIM_DEBUG_TRACE)
|
|
|
|
zlog_debug ("%s: %s RPF'(S,G) is the same as RPF'(*,G)", __PRETTY_FUNCTION__,
|
2016-11-17 14:17:25 +01:00
|
|
|
up->sg_str);
|
2016-11-03 15:56:39 +01:00
|
|
|
up->sptbit = PIM_UPSTREAM_SPTBIT_TRUE;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
2016-11-02 16:20:27 +01:00
|
|
|
|
2016-07-18 01:45:16 +02:00
|
|
|
const char *
|
2016-08-19 13:56:59 +02:00
|
|
|
pim_upstream_state2str (enum pim_upstream_state join_state)
|
2016-07-18 01:45:16 +02:00
|
|
|
{
|
2016-08-19 13:56:59 +02:00
|
|
|
switch (join_state)
|
2016-07-18 01:45:16 +02:00
|
|
|
{
|
|
|
|
case PIM_UPSTREAM_NOTJOINED:
|
2016-09-17 04:22:02 +02:00
|
|
|
return "NotJoined";
|
2016-07-18 01:45:16 +02:00
|
|
|
break;
|
|
|
|
case PIM_UPSTREAM_JOINED:
|
2016-09-17 04:22:02 +02:00
|
|
|
return "Joined";
|
2016-07-18 01:45:16 +02:00
|
|
|
break;
|
|
|
|
}
|
2016-09-17 04:22:02 +02:00
|
|
|
return "Unknown";
|
2016-07-18 01:45:16 +02:00
|
|
|
}
|
2016-07-18 15:47:19 +02:00
|
|
|
|
2017-03-08 18:29:40 +01:00
|
|
|
const char *
|
2017-03-09 18:52:59 +01:00
|
|
|
pim_reg_state2str (enum pim_reg_state reg_state, char *state_str)
|
2017-03-08 18:29:40 +01:00
|
|
|
{
|
2017-03-09 18:52:59 +01:00
|
|
|
switch (reg_state)
|
2017-03-08 18:29:40 +01:00
|
|
|
{
|
2017-03-09 18:52:59 +01:00
|
|
|
case PIM_REG_NOINFO:
|
|
|
|
strcpy (state_str, "RegNoInfo");
|
2017-03-08 18:29:40 +01:00
|
|
|
break;
|
2017-03-09 18:52:59 +01:00
|
|
|
case PIM_REG_JOIN:
|
|
|
|
strcpy (state_str, "RegJoined");
|
2017-03-08 18:29:40 +01:00
|
|
|
break;
|
2017-03-09 18:52:59 +01:00
|
|
|
case PIM_REG_JOIN_PENDING:
|
|
|
|
strcpy (state_str, "RegJoinPend");
|
2017-03-08 18:29:40 +01:00
|
|
|
break;
|
2017-03-09 18:52:59 +01:00
|
|
|
case PIM_REG_PRUNE:
|
|
|
|
strcpy (state_str, "RegPrune");
|
2017-03-08 18:29:40 +01:00
|
|
|
break;
|
|
|
|
default:
|
2017-03-09 18:52:59 +01:00
|
|
|
strcpy (state_str, "RegUnknown");
|
2017-03-08 18:29:40 +01:00
|
|
|
}
|
|
|
|
return state_str;
|
|
|
|
}
|
|
|
|
|
2016-07-18 15:47:19 +02:00
|
|
|
static int
|
|
|
|
pim_upstream_register_stop_timer (struct thread *t)
|
|
|
|
{
|
2016-08-22 15:10:05 +02:00
|
|
|
struct pim_interface *pim_ifp;
|
2016-07-18 15:47:19 +02:00
|
|
|
struct pim_upstream *up;
|
|
|
|
struct pim_rpf *rpg;
|
|
|
|
struct ip ip_hdr;
|
|
|
|
up = THREAD_ARG (t);
|
|
|
|
|
|
|
|
up->t_rs_timer = NULL;
|
|
|
|
|
|
|
|
if (PIM_DEBUG_TRACE)
|
|
|
|
{
|
2017-03-09 18:52:59 +01:00
|
|
|
char state_str[PIM_REG_STATE_STR_LEN];
|
2016-08-18 20:15:33 +02:00
|
|
|
zlog_debug ("%s: (S,G)=%s upstream register stop timer %s",
|
2016-11-17 14:17:25 +01:00
|
|
|
__PRETTY_FUNCTION__, up->sg_str,
|
2017-03-09 18:52:59 +01:00
|
|
|
pim_reg_state2str(up->reg_state, state_str));
|
2016-07-18 15:47:19 +02:00
|
|
|
}
|
|
|
|
|
2017-03-08 18:29:40 +01:00
|
|
|
switch (up->reg_state)
|
2016-07-18 15:47:19 +02:00
|
|
|
{
|
2017-03-09 18:52:59 +01:00
|
|
|
case PIM_REG_JOIN_PENDING:
|
|
|
|
up->reg_state = PIM_REG_JOIN;
|
2016-08-17 21:27:03 +02:00
|
|
|
pim_channel_add_oif (up->channel_oil, pim_regiface, PIM_OIF_FLAG_PROTO_PIM);
|
|
|
|
break;
|
2017-03-09 18:52:59 +01:00
|
|
|
case PIM_REG_JOIN:
|
2016-07-18 15:47:19 +02:00
|
|
|
break;
|
2017-03-09 18:52:59 +01:00
|
|
|
case PIM_REG_PRUNE:
|
2016-08-22 15:10:05 +02:00
|
|
|
pim_ifp = up->rpf.source_nexthop.interface->info;
|
2016-09-21 22:16:43 +02:00
|
|
|
if (!pim_ifp)
|
|
|
|
{
|
|
|
|
if (PIM_DEBUG_TRACE)
|
|
|
|
zlog_debug ("%s: Interface: %s is not configured for pim",
|
|
|
|
__PRETTY_FUNCTION__, up->rpf.source_nexthop.interface->name);
|
|
|
|
return 0;
|
|
|
|
}
|
2017-03-09 18:52:59 +01:00
|
|
|
up->reg_state = PIM_REG_JOIN_PENDING;
|
2016-07-18 15:47:19 +02:00
|
|
|
pim_upstream_start_register_stop_timer (up, 1);
|
|
|
|
|
2016-12-05 13:39:59 +01:00
|
|
|
if (((up->channel_oil->cc.lastused/100) > PIM_KEEPALIVE_PERIOD) &&
|
|
|
|
(I_am_RP (up->sg.grp)))
|
|
|
|
{
|
|
|
|
if (PIM_DEBUG_TRACE)
|
|
|
|
zlog_debug ("%s: Stop sending the register, because I am the RP and we haven't seen a packet in a while", __PRETTY_FUNCTION__);
|
|
|
|
return 0;
|
|
|
|
}
|
2016-08-02 10:38:11 +02:00
|
|
|
rpg = RP (up->sg.grp);
|
2016-07-18 15:47:19 +02:00
|
|
|
memset (&ip_hdr, 0, sizeof (struct ip));
|
|
|
|
ip_hdr.ip_p = PIM_IP_PROTO_PIM;
|
|
|
|
ip_hdr.ip_hl = 5;
|
|
|
|
ip_hdr.ip_v = 4;
|
2016-08-02 10:38:11 +02:00
|
|
|
ip_hdr.ip_src = up->sg.src;
|
|
|
|
ip_hdr.ip_dst = up->sg.grp;
|
2016-08-04 18:58:47 +02:00
|
|
|
ip_hdr.ip_len = htons (20);
|
2016-07-18 15:47:19 +02:00
|
|
|
// checksum is broken
|
2016-08-22 15:10:05 +02:00
|
|
|
pim_register_send ((uint8_t *)&ip_hdr, sizeof (struct ip),
|
2016-11-17 01:20:33 +01:00
|
|
|
pim_ifp->primary_address, rpg, 1, up);
|
2016-07-18 15:47:19 +02:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
pim_upstream_start_register_stop_timer (struct pim_upstream *up, int null_register)
|
|
|
|
{
|
|
|
|
uint32_t time;
|
|
|
|
|
|
|
|
if (up->t_rs_timer)
|
|
|
|
{
|
|
|
|
THREAD_TIMER_OFF (up->t_rs_timer);
|
|
|
|
up->t_rs_timer = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!null_register)
|
|
|
|
{
|
|
|
|
uint32_t lower = (0.5 * PIM_REGISTER_SUPPRESSION_PERIOD);
|
|
|
|
uint32_t upper = (1.5 * PIM_REGISTER_SUPPRESSION_PERIOD);
|
|
|
|
time = lower + (random () % (upper - lower + 1)) - PIM_REGISTER_PROBE_PERIOD;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
time = PIM_REGISTER_PROBE_PERIOD;
|
|
|
|
|
|
|
|
if (PIM_DEBUG_TRACE)
|
|
|
|
{
|
2016-07-22 14:57:20 +02:00
|
|
|
zlog_debug ("%s: (S,G)=%s Starting upstream register stop timer %d",
|
2016-11-17 14:17:25 +01:00
|
|
|
__PRETTY_FUNCTION__, up->sg_str, time);
|
2016-07-18 15:47:19 +02:00
|
|
|
}
|
|
|
|
THREAD_TIMER_ON (master, up->t_rs_timer,
|
|
|
|
pim_upstream_register_stop_timer,
|
|
|
|
up, time);
|
|
|
|
}
|
2016-07-23 09:47:15 +02:00
|
|
|
|
|
|
|
int
|
2016-12-07 21:07:55 +01:00
|
|
|
pim_upstream_inherited_olist_decide (struct pim_upstream *up)
|
2016-07-23 09:47:15 +02:00
|
|
|
{
|
2017-04-20 15:03:47 +02:00
|
|
|
struct interface *ifp;
|
2017-04-25 07:32:23 +02:00
|
|
|
struct pim_interface *pim_ifp = NULL;
|
2017-04-20 15:03:47 +02:00
|
|
|
struct pim_ifchannel *ch, *starch;
|
|
|
|
struct listnode *node;
|
|
|
|
struct pim_upstream *starup = up->parent;
|
2016-07-23 11:36:44 +02:00
|
|
|
int output_intf = 0;
|
|
|
|
|
2017-04-25 07:32:23 +02:00
|
|
|
if (up->rpf.source_nexthop.interface)
|
|
|
|
pim_ifp = up->rpf.source_nexthop.interface->info;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (PIM_DEBUG_TRACE)
|
|
|
|
zlog_debug ("%s: up %s RPF is not present", __PRETTY_FUNCTION__, up->sg_str);
|
|
|
|
}
|
2016-08-24 22:09:28 +02:00
|
|
|
if (pim_ifp && !up->channel_oil)
|
2016-08-01 18:29:58 +02:00
|
|
|
up->channel_oil = pim_channel_oil_add (&up->sg, pim_ifp->mroute_vif_index);
|
2016-07-23 11:36:44 +02:00
|
|
|
|
2017-04-20 15:03:47 +02:00
|
|
|
for (ALL_LIST_ELEMENTS_RO (vrf_iflist (VRF_DEFAULT), node, ifp))
|
2016-07-23 11:36:44 +02:00
|
|
|
{
|
2017-04-20 15:03:47 +02:00
|
|
|
if (!ifp->info)
|
|
|
|
continue;
|
2016-07-23 11:36:44 +02:00
|
|
|
|
2017-04-20 15:03:47 +02:00
|
|
|
ch = pim_ifchannel_find (ifp, &up->sg);
|
|
|
|
|
|
|
|
if (starup)
|
|
|
|
starch = pim_ifchannel_find (ifp, &starup->sg);
|
|
|
|
else
|
|
|
|
starch = NULL;
|
|
|
|
|
|
|
|
if (!ch && !starch)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (pim_upstream_evaluate_join_desired_interface (up, ch, starch))
|
|
|
|
{
|
2017-01-19 18:09:26 +01:00
|
|
|
int flag = PIM_OIF_FLAG_PROTO_PIM;
|
|
|
|
|
2017-04-20 15:03:47 +02:00
|
|
|
if (!ch)
|
2017-01-19 18:09:26 +01:00
|
|
|
flag = PIM_OIF_FLAG_PROTO_STAR;
|
2017-04-20 15:03:47 +02:00
|
|
|
|
|
|
|
pim_channel_add_oif (up->channel_oil, ifp, flag);
|
|
|
|
output_intf++;
|
|
|
|
}
|
2016-07-23 11:36:44 +02:00
|
|
|
}
|
|
|
|
|
2016-12-07 21:07:55 +01:00
|
|
|
return output_intf;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* For a given upstream, determine the inherited_olist
|
|
|
|
* and apply it.
|
|
|
|
*
|
|
|
|
* inherited_olist(S,G,rpt) =
|
|
|
|
* ( joins(*,*,RP(G)) (+) joins(*,G) (-) prunes(S,G,rpt) )
|
|
|
|
* (+) ( pim_include(*,G) (-) pim_exclude(S,G))
|
|
|
|
* (-) ( lost_assert(*,G) (+) lost_assert(S,G,rpt) )
|
|
|
|
*
|
|
|
|
* inherited_olist(S,G) =
|
|
|
|
* inherited_olist(S,G,rpt) (+)
|
|
|
|
* joins(S,G) (+) pim_include(S,G) (-) lost_assert(S,G)
|
|
|
|
*
|
|
|
|
* return 1 if there are any output interfaces
|
|
|
|
* return 0 if there are not any output interfaces
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
pim_upstream_inherited_olist (struct pim_upstream *up)
|
|
|
|
{
|
|
|
|
int output_intf = pim_upstream_inherited_olist_decide (up);
|
|
|
|
|
2016-08-26 15:40:08 +02:00
|
|
|
/*
|
|
|
|
* If we have output_intf switch state to Join and work like normal
|
|
|
|
* If we don't have an output_intf that means we are probably a
|
|
|
|
* switch on a stick so turn on forwarding to just accept the
|
|
|
|
* incoming packets so we don't bother the other stuff!
|
|
|
|
*/
|
|
|
|
if (output_intf)
|
|
|
|
pim_upstream_switch (up, PIM_UPSTREAM_JOINED);
|
|
|
|
else
|
|
|
|
forward_on (up);
|
2016-07-23 11:36:44 +02:00
|
|
|
|
|
|
|
return output_intf;
|
2016-07-23 09:47:15 +02:00
|
|
|
}
|
2016-08-29 16:49:47 +02:00
|
|
|
|
2016-11-02 16:20:27 +01:00
|
|
|
int
|
|
|
|
pim_upstream_empty_inherited_olist (struct pim_upstream *up)
|
|
|
|
{
|
2016-11-10 15:41:28 +01:00
|
|
|
return pim_channel_oil_empty (up->channel_oil);
|
2016-11-02 16:20:27 +01:00
|
|
|
}
|
|
|
|
|
2016-08-29 16:49:47 +02:00
|
|
|
/*
|
|
|
|
* When we have a new neighbor,
|
|
|
|
* find upstreams that don't have their rpf_addr
|
|
|
|
* set and see if the new neighbor allows
|
|
|
|
* the join to be sent
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
pim_upstream_find_new_rpf (void)
|
|
|
|
{
|
|
|
|
struct listnode *up_node;
|
|
|
|
struct listnode *up_nextnode;
|
|
|
|
struct pim_upstream *up;
|
|
|
|
|
|
|
|
/*
|
2016-10-07 16:25:08 +02:00
|
|
|
* Scan all (S,G) upstreams searching for RPF'(S,G)=neigh_addr
|
|
|
|
*/
|
|
|
|
for (ALL_LIST_ELEMENTS(pim_upstream_list, up_node, up_nextnode, up))
|
2016-08-29 16:49:47 +02:00
|
|
|
{
|
2016-09-02 18:17:10 +02:00
|
|
|
if (pim_rpf_addr_is_inaddr_any(&up->rpf))
|
2016-08-29 16:49:47 +02:00
|
|
|
{
|
2016-12-06 05:27:37 +01:00
|
|
|
if (PIM_DEBUG_TRACE)
|
2016-08-29 16:49:47 +02:00
|
|
|
zlog_debug ("Upstream %s without a path to send join, checking",
|
2016-11-17 14:17:25 +01:00
|
|
|
up->sg_str);
|
2017-02-22 16:28:36 +01:00
|
|
|
pim_rpf_update (up, NULL, 1);
|
2016-08-29 16:49:47 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-10-07 16:25:08 +02:00
|
|
|
|
|
|
|
static unsigned int
|
|
|
|
pim_upstream_hash_key (void *arg)
|
|
|
|
{
|
|
|
|
struct pim_upstream *up = (struct pim_upstream *)arg;
|
|
|
|
|
|
|
|
return jhash_2words (up->sg.src.s_addr, up->sg.grp.s_addr, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
void pim_upstream_terminate (void)
|
|
|
|
{
|
|
|
|
if (pim_upstream_list)
|
2016-12-11 21:24:13 +01:00
|
|
|
list_delete (pim_upstream_list);
|
2016-10-07 16:25:08 +02:00
|
|
|
pim_upstream_list = NULL;
|
|
|
|
|
|
|
|
if (pim_upstream_hash)
|
|
|
|
hash_free (pim_upstream_hash);
|
2016-12-11 21:24:13 +01:00
|
|
|
pim_upstream_hash = NULL;
|
2016-10-07 16:25:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
pim_upstream_equal (const void *arg1, const void *arg2)
|
|
|
|
{
|
|
|
|
const struct pim_upstream *up1 = (const struct pim_upstream *)arg1;
|
|
|
|
const struct pim_upstream *up2 = (const struct pim_upstream *)arg2;
|
|
|
|
|
|
|
|
if ((up1->sg.grp.s_addr == up2->sg.grp.s_addr) &&
|
|
|
|
(up1->sg.src.s_addr == up2->sg.src.s_addr))
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-11-16 00:39:11 +01:00
|
|
|
/* rfc4601:section-4.2:"Data Packet Forwarding Rules" defines
|
|
|
|
* the cases where kat has to be restarted on rxing traffic -
|
|
|
|
*
|
|
|
|
* if( DirectlyConnected(S) == TRUE AND iif == RPF_interface(S) ) {
|
|
|
|
* set KeepaliveTimer(S,G) to Keepalive_Period
|
|
|
|
* # Note: a register state transition or UpstreamJPState(S,G)
|
|
|
|
* # transition may happen as a result of restarting
|
|
|
|
* # KeepaliveTimer, and must be dealt with here.
|
|
|
|
* }
|
|
|
|
* if( iif == RPF_interface(S) AND UpstreamJPState(S,G) == Joined AND
|
|
|
|
* inherited_olist(S,G) != NULL ) {
|
|
|
|
* set KeepaliveTimer(S,G) to Keepalive_Period
|
|
|
|
* }
|
|
|
|
*/
|
|
|
|
static bool pim_upstream_kat_start_ok(struct pim_upstream *up)
|
|
|
|
{
|
|
|
|
/* "iif == RPF_interface(S)" check has to be done by the kernel or hw
|
|
|
|
* so we will skip that here */
|
|
|
|
if (pim_if_connected_to_source(up->rpf.source_nexthop.interface,
|
|
|
|
up->sg.src)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((up->join_state == PIM_UPSTREAM_JOINED) &&
|
|
|
|
!pim_upstream_empty_inherited_olist(up)) {
|
|
|
|
/* XXX: I have added this RP check just for 3.2 and it's a digression from
|
|
|
|
* what rfc-4601 says. Till now we were only running KAT on FHR and RP and
|
|
|
|
* there is some angst around making the change to run it all routers that
|
|
|
|
* maintain the (S, G) state. This is tracked via CM-13601 and MUST be
|
|
|
|
* removed to handle spt turn-arounds correctly in a 3-tier clos */
|
|
|
|
if (I_am_RP (up->sg.grp))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2016-11-03 00:19:40 +01:00
|
|
|
/*
|
|
|
|
* Code to check and see if we've received packets on a S,G mroute
|
|
|
|
* and if so to set the SPT bit appropriately
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
pim_upstream_sg_running (void *arg)
|
|
|
|
{
|
|
|
|
struct pim_upstream *up = (struct pim_upstream *)arg;
|
2016-11-03 15:02:59 +01:00
|
|
|
|
|
|
|
// No packet can have arrived here if this is the case
|
|
|
|
if (!up->channel_oil || !up->channel_oil->installed)
|
|
|
|
{
|
|
|
|
if (PIM_DEBUG_TRACE)
|
|
|
|
zlog_debug ("%s: %s is not installed in mroute",
|
2016-11-17 14:17:25 +01:00
|
|
|
__PRETTY_FUNCTION__, up->sg_str);
|
2016-11-03 15:02:59 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-12-09 01:26:41 +01:00
|
|
|
/*
|
|
|
|
* This is a bit of a hack
|
|
|
|
* We've noted that we should rescan but
|
|
|
|
* we've missed the window for doing so in
|
|
|
|
* pim_zebra.c for some reason. I am
|
|
|
|
* only doing this at this point in time
|
|
|
|
* to get us up and working for the moment
|
|
|
|
*/
|
|
|
|
if (up->channel_oil->oil_inherited_rescan)
|
|
|
|
{
|
|
|
|
if (PIM_DEBUG_TRACE)
|
|
|
|
zlog_debug ("%s: Handling unscanned inherited_olist for %s", __PRETTY_FUNCTION__, up->sg_str);
|
|
|
|
pim_upstream_inherited_olist_decide (up);
|
|
|
|
up->channel_oil->oil_inherited_rescan = 0;
|
|
|
|
}
|
2016-11-03 15:02:59 +01:00
|
|
|
pim_mroute_update_counters (up->channel_oil);
|
|
|
|
|
|
|
|
// Have we seen packets?
|
2016-11-16 00:39:11 +01:00
|
|
|
if ((up->channel_oil->cc.oldpktcnt >= up->channel_oil->cc.pktcnt) &&
|
2016-11-03 15:02:59 +01:00
|
|
|
(up->channel_oil->cc.lastused/100 > 30))
|
|
|
|
{
|
|
|
|
if (PIM_DEBUG_TRACE)
|
|
|
|
{
|
2016-12-05 23:53:49 +01:00
|
|
|
zlog_debug ("%s: %s old packet count is equal or lastused is greater than 30, (%ld,%ld,%lld)",
|
|
|
|
__PRETTY_FUNCTION__, up->sg_str,
|
|
|
|
up->channel_oil->cc.oldpktcnt,
|
|
|
|
up->channel_oil->cc.pktcnt,
|
|
|
|
up->channel_oil->cc.lastused/100);
|
2016-11-03 15:02:59 +01:00
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
pimd: Allow SPT switchover
This allows SPT switchover for S,G upon receipt of packets
on the LHR.
1) When we create a *,G from a IGMP Group Report, install
the *,G route with the pimreg device on the OIL.
2) When a packet hits the LHR that matches the *,G, we will
get a WHOLEPKT callback from the kernel and if we cannot
find the S,G, that means we have matched it on the LHR via
the *,G mroute. Create the S,G start the KAT and run
inherited_olist.
3) When the S,G times out, safely remove the S,G via
the KAT expiry
4) When the *,G is removed, remove any S,G associated
with it via the LHR flag.
Signed-off-by: Donald Sharp <sharpd@cumulusnetworks.com>
2017-03-23 02:07:57 +01:00
|
|
|
if (pim_upstream_kat_start_ok(up))
|
2016-11-16 00:39:11 +01:00
|
|
|
{
|
pimd: Allow SPT switchover
This allows SPT switchover for S,G upon receipt of packets
on the LHR.
1) When we create a *,G from a IGMP Group Report, install
the *,G route with the pimreg device on the OIL.
2) When a packet hits the LHR that matches the *,G, we will
get a WHOLEPKT callback from the kernel and if we cannot
find the S,G, that means we have matched it on the LHR via
the *,G mroute. Create the S,G start the KAT and run
inherited_olist.
3) When the S,G times out, safely remove the S,G via
the KAT expiry
4) When the *,G is removed, remove any S,G associated
with it via the LHR flag.
Signed-off-by: Donald Sharp <sharpd@cumulusnetworks.com>
2017-03-23 02:07:57 +01:00
|
|
|
/* Add a source reference to the stream if
|
|
|
|
* one doesn't already exist */
|
|
|
|
if (!PIM_UPSTREAM_FLAG_TEST_SRC_STREAM(up->flags))
|
|
|
|
{
|
|
|
|
if (PIM_DEBUG_TRACE)
|
|
|
|
zlog_debug ("source reference created on kat restart %s", up->sg_str);
|
2016-11-17 14:17:25 +01:00
|
|
|
|
2017-05-09 20:30:43 +02:00
|
|
|
pim_upstream_ref(up, PIM_UPSTREAM_FLAG_MASK_SRC_STREAM, __PRETTY_FUNCTION__);
|
pimd: Allow SPT switchover
This allows SPT switchover for S,G upon receipt of packets
on the LHR.
1) When we create a *,G from a IGMP Group Report, install
the *,G route with the pimreg device on the OIL.
2) When a packet hits the LHR that matches the *,G, we will
get a WHOLEPKT callback from the kernel and if we cannot
find the S,G, that means we have matched it on the LHR via
the *,G mroute. Create the S,G start the KAT and run
inherited_olist.
3) When the S,G times out, safely remove the S,G via
the KAT expiry
4) When the *,G is removed, remove any S,G associated
with it via the LHR flag.
Signed-off-by: Donald Sharp <sharpd@cumulusnetworks.com>
2017-03-23 02:07:57 +01:00
|
|
|
PIM_UPSTREAM_FLAG_SET_SRC_STREAM(up->flags);
|
|
|
|
pim_upstream_fhr_kat_start(up);
|
|
|
|
}
|
|
|
|
pim_upstream_keep_alive_timer_start(up, qpim_keep_alive_time);
|
2016-11-16 00:39:11 +01:00
|
|
|
}
|
pimd: Allow SPT switchover
This allows SPT switchover for S,G upon receipt of packets
on the LHR.
1) When we create a *,G from a IGMP Group Report, install
the *,G route with the pimreg device on the OIL.
2) When a packet hits the LHR that matches the *,G, we will
get a WHOLEPKT callback from the kernel and if we cannot
find the S,G, that means we have matched it on the LHR via
the *,G mroute. Create the S,G start the KAT and run
inherited_olist.
3) When the S,G times out, safely remove the S,G via
the KAT expiry
4) When the *,G is removed, remove any S,G associated
with it via the LHR flag.
Signed-off-by: Donald Sharp <sharpd@cumulusnetworks.com>
2017-03-23 02:07:57 +01:00
|
|
|
else if (PIM_UPSTREAM_FLAG_TEST_SRC_LHR(up->flags))
|
2016-11-16 00:39:11 +01:00
|
|
|
pim_upstream_keep_alive_timer_start(up, qpim_keep_alive_time);
|
|
|
|
|
|
|
|
if (up->sptbit != PIM_UPSTREAM_SPTBIT_TRUE)
|
pimd: Allow SPT switchover
This allows SPT switchover for S,G upon receipt of packets
on the LHR.
1) When we create a *,G from a IGMP Group Report, install
the *,G route with the pimreg device on the OIL.
2) When a packet hits the LHR that matches the *,G, we will
get a WHOLEPKT callback from the kernel and if we cannot
find the S,G, that means we have matched it on the LHR via
the *,G mroute. Create the S,G start the KAT and run
inherited_olist.
3) When the S,G times out, safely remove the S,G via
the KAT expiry
4) When the *,G is removed, remove any S,G associated
with it via the LHR flag.
Signed-off-by: Donald Sharp <sharpd@cumulusnetworks.com>
2017-03-23 02:07:57 +01:00
|
|
|
{
|
|
|
|
pim_upstream_set_sptbit(up, up->rpf.source_nexthop.interface);
|
|
|
|
}
|
2016-11-03 15:02:59 +01:00
|
|
|
return;
|
2016-11-03 00:19:40 +01:00
|
|
|
}
|
|
|
|
|
2017-04-05 18:08:53 +02:00
|
|
|
void
|
|
|
|
pim_upstream_add_lhr_star_pimreg (void)
|
|
|
|
{
|
|
|
|
struct pim_upstream *up;
|
|
|
|
struct listnode *node;
|
|
|
|
|
|
|
|
for (ALL_LIST_ELEMENTS_RO (pim_upstream_list, node, up))
|
|
|
|
{
|
|
|
|
if (up->sg.src.s_addr != INADDR_ANY)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (!PIM_UPSTREAM_FLAG_TEST_SRC_IGMP (up->flags))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
pim_channel_add_oif (up->channel_oil, pim_regiface, PIM_OIF_FLAG_PROTO_IGMP);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2017-04-07 16:16:23 +02:00
|
|
|
pim_upstream_spt_prefix_list_update (struct prefix_list *pl)
|
|
|
|
{
|
|
|
|
const char *pname = prefix_list_name (pl);
|
|
|
|
|
|
|
|
if (pimg->spt.plist && strcmp (pimg->spt.plist, pname) == 0)
|
|
|
|
{
|
|
|
|
pim_upstream_remove_lhr_star_pimreg (pname);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* nlist -> The new prefix list
|
|
|
|
*
|
|
|
|
* Per Group Application of pimreg to the OIL
|
|
|
|
* If the prefix list tells us DENY then
|
|
|
|
* we need to Switchover to SPT immediate
|
|
|
|
* so add the pimreg.
|
|
|
|
* If the prefix list tells us to ACCEPT than
|
|
|
|
* we need to Never do the SPT so remove
|
|
|
|
* the interface
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
pim_upstream_remove_lhr_star_pimreg (const char *nlist)
|
2017-04-05 18:08:53 +02:00
|
|
|
{
|
|
|
|
struct pim_upstream *up;
|
|
|
|
struct listnode *node;
|
2017-04-07 16:16:23 +02:00
|
|
|
struct prefix_list *np;
|
|
|
|
struct prefix g;
|
|
|
|
enum prefix_list_type apply_new;
|
|
|
|
|
|
|
|
np = prefix_list_lookup (AFI_IP, nlist);
|
|
|
|
|
|
|
|
g.family = AF_INET;
|
|
|
|
g.prefixlen = IPV4_MAX_PREFIXLEN;
|
2017-04-05 18:08:53 +02:00
|
|
|
|
|
|
|
for (ALL_LIST_ELEMENTS_RO (pim_upstream_list, node, up))
|
|
|
|
{
|
|
|
|
if (up->sg.src.s_addr != INADDR_ANY)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (!PIM_UPSTREAM_FLAG_TEST_SRC_IGMP (up->flags))
|
|
|
|
continue;
|
|
|
|
|
2017-04-07 16:16:23 +02:00
|
|
|
if (!nlist)
|
|
|
|
{
|
|
|
|
pim_channel_del_oif (up->channel_oil, pim_regiface, PIM_OIF_FLAG_PROTO_IGMP);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
g.u.prefix4 = up->sg.grp;
|
|
|
|
apply_new = prefix_list_apply (np, &g);
|
|
|
|
if (apply_new == PREFIX_DENY)
|
|
|
|
pim_channel_add_oif (up->channel_oil, pim_regiface, PIM_OIF_FLAG_PROTO_IGMP);
|
|
|
|
else
|
|
|
|
pim_channel_del_oif (up->channel_oil, pim_regiface, PIM_OIF_FLAG_PROTO_IGMP);
|
2017-04-05 18:08:53 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-10-13 14:34:48 +02:00
|
|
|
void
|
|
|
|
pim_upstream_init (void)
|
2016-10-07 16:25:08 +02:00
|
|
|
{
|
2016-11-03 00:19:40 +01:00
|
|
|
pim_upstream_sg_wheel = wheel_init (master, 31000, 100,
|
|
|
|
pim_upstream_hash_key,
|
|
|
|
pim_upstream_sg_running);
|
2016-10-07 16:25:08 +02:00
|
|
|
pim_upstream_hash = hash_create_size (8192, pim_upstream_hash_key,
|
|
|
|
pim_upstream_equal);
|
|
|
|
|
|
|
|
pim_upstream_list = list_new ();
|
|
|
|
pim_upstream_list->del = (void (*)(void *)) pim_upstream_free;
|
2016-10-24 04:47:25 +02:00
|
|
|
pim_upstream_list->cmp = pim_upstream_compare;
|
2016-10-07 16:25:08 +02:00
|
|
|
|
|
|
|
}
|