frr/pimd/pim_zebra.c

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

560 lines
13 KiB
C
Raw Normal View History

// SPDX-License-Identifier: GPL-2.0-or-later
/*
* PIM for Quagga
* Copyright (C) 2008 Everton da Silva Marques
*/
#include <zebra.h>
#include "if.h"
#include "log.h"
#include "prefix.h"
#include "zclient.h"
#include "stream.h"
#include "network.h"
#include "vty.h"
#include "plist.h"
#include "lib/bfd.h"
#include "pimd.h"
#include "pim_pim.h"
#include "pim_zebra.h"
#include "pim_iface.h"
#include "pim_str.h"
#include "pim_oil.h"
#include "pim_rpf.h"
#include "pim_time.h"
#include "pim_join.h"
#include "pim_zlookup.h"
#include "pim_ifchannel.h"
#include "pim_rp.h"
#include "pim_igmpv3.h"
#include "pim_jp_agg.h"
#include "pim_nht.h"
#include "pim_ssm.h"
#include "pim_vxlan.h"
#include "pim_mlag.h"
#undef PIM_DEBUG_IFADDR_DUMP
#define PIM_DEBUG_IFADDR_DUMP
struct zclient *zclient;
/* Router-id update message from zebra. */
static int pim_router_id_update_zebra(ZAPI_CALLBACK_ARGS)
{
struct prefix router_id;
zebra_router_id_update_read(zclient->ibuf, &router_id);
return 0;
}
static int pim_zebra_interface_vrf_update(ZAPI_CALLBACK_ARGS)
{
struct interface *ifp;
vrf_id_t new_vrf_id;
pimd: Fix use after free issue for ifp's moving vrfs We have this valgrind trace: ==1125== Invalid read of size 4 ==1125== at 0x170A7D: pim_if_delete (pim_iface.c:203) ==1125== by 0x170C01: pim_if_terminate (pim_iface.c:80) ==1125== by 0x174F34: pim_instance_terminate (pim_instance.c:68) ==1125== by 0x17535B: pim_vrf_terminate (pim_instance.c:260) ==1125== by 0x1941CF: pim_terminate (pimd.c:161) ==1125== by 0x1B476D: pim_sigint (pim_signals.c:44) ==1125== by 0x4910C22: frr_sigevent_process (sigevent.c:133) ==1125== by 0x49220A4: thread_fetch (thread.c:1777) ==1125== by 0x48DC8E2: frr_run (libfrr.c:1222) ==1125== by 0x15E12A: main (pim_main.c:176) ==1125== Address 0x6274d28 is 1,192 bytes inside a block of size 1,752 free'd ==1125== at 0x48369AB: free (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==1125== by 0x174FF1: pim_vrf_delete (pim_instance.c:181) ==1125== by 0x4925480: vrf_delete (vrf.c:264) ==1125== by 0x4925480: vrf_delete (vrf.c:238) ==1125== by 0x49332C7: zclient_vrf_delete (zclient.c:2187) ==1125== by 0x4934319: zclient_read (zclient.c:4003) ==1125== by 0x492249C: thread_call (thread.c:2008) ==1125== by 0x48DC8D7: frr_run (libfrr.c:1223) ==1125== by 0x15E12A: main (pim_main.c:176) ==1125== Block was alloc'd at ==1125== at 0x4837B65: calloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==1125== by 0x48E80AF: qcalloc (memory.c:116) ==1125== by 0x1750DA: pim_instance_init (pim_instance.c:90) ==1125== by 0x1750DA: pim_vrf_new (pim_instance.c:161) ==1125== by 0x4924FDC: vrf_get (vrf.c:183) ==1125== by 0x493334C: zclient_vrf_add (zclient.c:2157) ==1125== by 0x4934319: zclient_read (zclient.c:4003) ==1125== by 0x492249C: thread_call (thread.c:2008) ==1125== by 0x48DC8D7: frr_run (libfrr.c:1223) ==1125== by 0x15E12A: main (pim_main.c:176) and you do this series of events: a) Create a vrf, put an interface in it b) Turn on pim on that interface and turn on pim in that vrf c) Delete the vrf d) Do anything with the interface, in this case shutdown the system The move of the interface to a new vrf is leaving the pim_ifp->pim pointer pointing at the old pim instance, which was just deleted, so the instance pointer was freed. Let's clean up the pim pointer in the interface pointer as well. Signed-off-by: Donald Sharp <sharpd@nvidia.com>
2023-03-22 23:24:56 +01:00
struct pim_instance *pim;
struct pim_interface *pim_ifp;
ifp = zebra_interface_vrf_update_read(zclient->ibuf, vrf_id,
&new_vrf_id);
if (!ifp)
return 0;
if (PIM_DEBUG_ZEBRA)
zlog_debug("%s: %s updating from %u to %u", __func__, ifp->name,
vrf_id, new_vrf_id);
pimd: Fix use after free issue for ifp's moving vrfs We have this valgrind trace: ==1125== Invalid read of size 4 ==1125== at 0x170A7D: pim_if_delete (pim_iface.c:203) ==1125== by 0x170C01: pim_if_terminate (pim_iface.c:80) ==1125== by 0x174F34: pim_instance_terminate (pim_instance.c:68) ==1125== by 0x17535B: pim_vrf_terminate (pim_instance.c:260) ==1125== by 0x1941CF: pim_terminate (pimd.c:161) ==1125== by 0x1B476D: pim_sigint (pim_signals.c:44) ==1125== by 0x4910C22: frr_sigevent_process (sigevent.c:133) ==1125== by 0x49220A4: thread_fetch (thread.c:1777) ==1125== by 0x48DC8E2: frr_run (libfrr.c:1222) ==1125== by 0x15E12A: main (pim_main.c:176) ==1125== Address 0x6274d28 is 1,192 bytes inside a block of size 1,752 free'd ==1125== at 0x48369AB: free (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==1125== by 0x174FF1: pim_vrf_delete (pim_instance.c:181) ==1125== by 0x4925480: vrf_delete (vrf.c:264) ==1125== by 0x4925480: vrf_delete (vrf.c:238) ==1125== by 0x49332C7: zclient_vrf_delete (zclient.c:2187) ==1125== by 0x4934319: zclient_read (zclient.c:4003) ==1125== by 0x492249C: thread_call (thread.c:2008) ==1125== by 0x48DC8D7: frr_run (libfrr.c:1223) ==1125== by 0x15E12A: main (pim_main.c:176) ==1125== Block was alloc'd at ==1125== at 0x4837B65: calloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==1125== by 0x48E80AF: qcalloc (memory.c:116) ==1125== by 0x1750DA: pim_instance_init (pim_instance.c:90) ==1125== by 0x1750DA: pim_vrf_new (pim_instance.c:161) ==1125== by 0x4924FDC: vrf_get (vrf.c:183) ==1125== by 0x493334C: zclient_vrf_add (zclient.c:2157) ==1125== by 0x4934319: zclient_read (zclient.c:4003) ==1125== by 0x492249C: thread_call (thread.c:2008) ==1125== by 0x48DC8D7: frr_run (libfrr.c:1223) ==1125== by 0x15E12A: main (pim_main.c:176) and you do this series of events: a) Create a vrf, put an interface in it b) Turn on pim on that interface and turn on pim in that vrf c) Delete the vrf d) Do anything with the interface, in this case shutdown the system The move of the interface to a new vrf is leaving the pim_ifp->pim pointer pointing at the old pim instance, which was just deleted, so the instance pointer was freed. Let's clean up the pim pointer in the interface pointer as well. Signed-off-by: Donald Sharp <sharpd@nvidia.com>
2023-03-22 23:24:56 +01:00
pim = pim_get_pim_instance(new_vrf_id);
if (!pim)
return 0;
pimd: Fix use after free issue for ifp's moving vrfs We have this valgrind trace: ==1125== Invalid read of size 4 ==1125== at 0x170A7D: pim_if_delete (pim_iface.c:203) ==1125== by 0x170C01: pim_if_terminate (pim_iface.c:80) ==1125== by 0x174F34: pim_instance_terminate (pim_instance.c:68) ==1125== by 0x17535B: pim_vrf_terminate (pim_instance.c:260) ==1125== by 0x1941CF: pim_terminate (pimd.c:161) ==1125== by 0x1B476D: pim_sigint (pim_signals.c:44) ==1125== by 0x4910C22: frr_sigevent_process (sigevent.c:133) ==1125== by 0x49220A4: thread_fetch (thread.c:1777) ==1125== by 0x48DC8E2: frr_run (libfrr.c:1222) ==1125== by 0x15E12A: main (pim_main.c:176) ==1125== Address 0x6274d28 is 1,192 bytes inside a block of size 1,752 free'd ==1125== at 0x48369AB: free (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==1125== by 0x174FF1: pim_vrf_delete (pim_instance.c:181) ==1125== by 0x4925480: vrf_delete (vrf.c:264) ==1125== by 0x4925480: vrf_delete (vrf.c:238) ==1125== by 0x49332C7: zclient_vrf_delete (zclient.c:2187) ==1125== by 0x4934319: zclient_read (zclient.c:4003) ==1125== by 0x492249C: thread_call (thread.c:2008) ==1125== by 0x48DC8D7: frr_run (libfrr.c:1223) ==1125== by 0x15E12A: main (pim_main.c:176) ==1125== Block was alloc'd at ==1125== at 0x4837B65: calloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==1125== by 0x48E80AF: qcalloc (memory.c:116) ==1125== by 0x1750DA: pim_instance_init (pim_instance.c:90) ==1125== by 0x1750DA: pim_vrf_new (pim_instance.c:161) ==1125== by 0x4924FDC: vrf_get (vrf.c:183) ==1125== by 0x493334C: zclient_vrf_add (zclient.c:2157) ==1125== by 0x4934319: zclient_read (zclient.c:4003) ==1125== by 0x492249C: thread_call (thread.c:2008) ==1125== by 0x48DC8D7: frr_run (libfrr.c:1223) ==1125== by 0x15E12A: main (pim_main.c:176) and you do this series of events: a) Create a vrf, put an interface in it b) Turn on pim on that interface and turn on pim in that vrf c) Delete the vrf d) Do anything with the interface, in this case shutdown the system The move of the interface to a new vrf is leaving the pim_ifp->pim pointer pointing at the old pim instance, which was just deleted, so the instance pointer was freed. Let's clean up the pim pointer in the interface pointer as well. Signed-off-by: Donald Sharp <sharpd@nvidia.com>
2023-03-22 23:24:56 +01:00
if_update_to_new_vrf(ifp, new_vrf_id);
pimd: Fix use after free issue for ifp's moving vrfs We have this valgrind trace: ==1125== Invalid read of size 4 ==1125== at 0x170A7D: pim_if_delete (pim_iface.c:203) ==1125== by 0x170C01: pim_if_terminate (pim_iface.c:80) ==1125== by 0x174F34: pim_instance_terminate (pim_instance.c:68) ==1125== by 0x17535B: pim_vrf_terminate (pim_instance.c:260) ==1125== by 0x1941CF: pim_terminate (pimd.c:161) ==1125== by 0x1B476D: pim_sigint (pim_signals.c:44) ==1125== by 0x4910C22: frr_sigevent_process (sigevent.c:133) ==1125== by 0x49220A4: thread_fetch (thread.c:1777) ==1125== by 0x48DC8E2: frr_run (libfrr.c:1222) ==1125== by 0x15E12A: main (pim_main.c:176) ==1125== Address 0x6274d28 is 1,192 bytes inside a block of size 1,752 free'd ==1125== at 0x48369AB: free (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==1125== by 0x174FF1: pim_vrf_delete (pim_instance.c:181) ==1125== by 0x4925480: vrf_delete (vrf.c:264) ==1125== by 0x4925480: vrf_delete (vrf.c:238) ==1125== by 0x49332C7: zclient_vrf_delete (zclient.c:2187) ==1125== by 0x4934319: zclient_read (zclient.c:4003) ==1125== by 0x492249C: thread_call (thread.c:2008) ==1125== by 0x48DC8D7: frr_run (libfrr.c:1223) ==1125== by 0x15E12A: main (pim_main.c:176) ==1125== Block was alloc'd at ==1125== at 0x4837B65: calloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==1125== by 0x48E80AF: qcalloc (memory.c:116) ==1125== by 0x1750DA: pim_instance_init (pim_instance.c:90) ==1125== by 0x1750DA: pim_vrf_new (pim_instance.c:161) ==1125== by 0x4924FDC: vrf_get (vrf.c:183) ==1125== by 0x493334C: zclient_vrf_add (zclient.c:2157) ==1125== by 0x4934319: zclient_read (zclient.c:4003) ==1125== by 0x492249C: thread_call (thread.c:2008) ==1125== by 0x48DC8D7: frr_run (libfrr.c:1223) ==1125== by 0x15E12A: main (pim_main.c:176) and you do this series of events: a) Create a vrf, put an interface in it b) Turn on pim on that interface and turn on pim in that vrf c) Delete the vrf d) Do anything with the interface, in this case shutdown the system The move of the interface to a new vrf is leaving the pim_ifp->pim pointer pointing at the old pim instance, which was just deleted, so the instance pointer was freed. Let's clean up the pim pointer in the interface pointer as well. Signed-off-by: Donald Sharp <sharpd@nvidia.com>
2023-03-22 23:24:56 +01:00
pim_ifp = ifp->info;
if (!pim_ifp)
return 0;
pim_ifp->pim->mcast_if_count--;
pim_ifp->pim = pim;
pim_ifp->pim->mcast_if_count++;
return 0;
}
#ifdef PIM_DEBUG_IFADDR_DUMP
static void dump_if_address(struct interface *ifp)
{
struct connected *ifc;
struct listnode *node;
zlog_debug("%s %s: interface %s addresses:", __FILE__, __func__,
ifp->name);
for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, ifc)) {
struct prefix *p = ifc->address;
if (p->family != AF_INET)
continue;
zlog_debug("%s %s: interface %s address %pI4 %s", __FILE__,
__func__, ifp->name, &p->u.prefix4,
CHECK_FLAG(ifc->flags, ZEBRA_IFA_SECONDARY)
? "secondary"
: "primary");
}
}
#endif
static int pim_zebra_if_address_add(ZAPI_CALLBACK_ARGS)
{
struct connected *c;
struct prefix *p;
struct pim_interface *pim_ifp;
/*
zebra api notifies address adds/dels events by using the same call
interface_add_read below, see comments in lib/zclient.c
zebra_interface_address_read(ZEBRA_INTERFACE_ADDRESS_ADD, ...)
will add address to interface list by calling
connected_add_by_prefix()
*/
c = zebra_interface_address_read(cmd, zclient->ibuf, vrf_id);
if (!c)
return 0;
pim_ifp = c->ifp->info;
p = c->address;
if (PIM_DEBUG_ZEBRA) {
zlog_debug("%s: %s(%u) connected IP address %pFX flags %u %s",
__func__, c->ifp->name, vrf_id, p, c->flags,
CHECK_FLAG(c->flags, ZEBRA_IFA_SECONDARY)
? "secondary"
: "primary");
#ifdef PIM_DEBUG_IFADDR_DUMP
dump_if_address(c->ifp);
#endif
}
#if PIM_IPV == 4
if (p->family != PIM_AF)
SET_FLAG(c->flags, ZEBRA_IFA_SECONDARY);
else if (!CHECK_FLAG(c->flags, ZEBRA_IFA_SECONDARY)) {
/* trying to add primary address? */
pim_addr primary_addr = pim_find_primary_addr(c->ifp);
pim_addr addr = pim_addr_from_prefix(p);
if (pim_addr_cmp(primary_addr, addr)) {
if (PIM_DEBUG_ZEBRA)
zlog_warn(
"%s: %s : forcing secondary flag on %pFX",
__func__, c->ifp->name, p);
SET_FLAG(c->flags, ZEBRA_IFA_SECONDARY);
}
}
#else /* PIM_IPV != 4 */
if (p->family != PIM_AF)
return 0;
#endif
pim_if_addr_add(c);
if (pim_ifp) {
struct pim_instance *pim;
pim = pim_get_pim_instance(vrf_id);
if (!pim) {
if (PIM_DEBUG_ZEBRA)
zlog_debug("%s: Unable to find pim instance",
__func__);
return 0;
}
pim_ifp->pim = pim;
pim_rp_check_on_if_add(pim_ifp);
}
if (if_is_loopback(c->ifp)) {
struct vrf *vrf = vrf_lookup_by_id(vrf_id);
struct interface *ifp;
FOR_ALL_INTERFACES (vrf, ifp) {
if (!if_is_loopback(ifp) && if_is_operative(ifp))
pim_if_addr_add_all(ifp);
}
}
return 0;
}
static int pim_zebra_if_address_del(ZAPI_CALLBACK_ARGS)
{
struct connected *c;
struct prefix *p;
struct vrf *vrf = vrf_lookup_by_id(vrf_id);
if (!vrf)
return 0;
/*
zebra api notifies address adds/dels events by using the same call
interface_add_read below, see comments in lib/zclient.c
zebra_interface_address_read(ZEBRA_INTERFACE_ADDRESS_DELETE, ...)
will remove address from interface list by calling
connected_delete_by_prefix()
*/
c = zebra_interface_address_read(cmd, zclient->ibuf, vrf_id);
if (!c)
return 0;
p = c->address;
if (PIM_DEBUG_ZEBRA) {
zlog_debug(
"%s: %s(%u) disconnected IP address %pFX flags %u %s",
__func__, c->ifp->name, vrf_id, p, c->flags,
CHECK_FLAG(c->flags, ZEBRA_IFA_SECONDARY)
? "secondary"
: "primary");
#ifdef PIM_DEBUG_IFADDR_DUMP
dump_if_address(c->ifp);
#endif
}
if (p->family == PIM_AF) {
struct pim_instance *pim;
pim = vrf->info;
pim_if_addr_del(c, 0);
pim_rp_setup(pim);
pim_i_am_rp_re_evaluate(pim);
}
connected_free(&c);
return 0;
}
void pim_zebra_update_all_interfaces(struct pim_instance *pim)
{
struct interface *ifp;
FOR_ALL_INTERFACES (pim->vrf, ifp) {
struct pim_interface *pim_ifp = ifp->info;
struct pim_iface_upstream_switch *us;
struct listnode *node;
if (!pim_ifp)
continue;
for (ALL_LIST_ELEMENTS_RO(pim_ifp->upstream_switch_list, node,
us)) {
struct pim_rpf rpf;
rpf.source_nexthop.interface = ifp;
rpf.rpf_addr = us->address;
pim_joinprune_send(&rpf, us->us);
pim_jp_agg_clear_group(us->us);
}
}
}
void pim_zebra_upstream_rpf_changed(struct pim_instance *pim,
struct pim_upstream *up,
struct pim_rpf *old)
{
if (old->source_nexthop.interface) {
struct pim_neighbor *nbr;
nbr = pim_neighbor_find(old->source_nexthop.interface,
old->rpf_addr, true);
if (nbr)
pim_jp_agg_remove_group(nbr->upstream_jp_agg, up, nbr);
/*
* We have detected a case where we might need
* to rescan the inherited o_list so do it.
*/
if (up->channel_oil->oil_inherited_rescan) {
pim_upstream_inherited_olist_decide(pim, up);
up->channel_oil->oil_inherited_rescan = 0;
}
if (up->join_state == PIM_UPSTREAM_JOINED) {
/*
* If we come up real fast we can be here
* where the mroute has not been installed
* so install it.
*/
if (!up->channel_oil->installed)
pim_upstream_mroute_add(up->channel_oil,
__func__);
/*
* RFC 4601: 4.5.7. Sending (S,G)
* Join/Prune Messages
*
* Transitions from Joined State
*
* RPF'(S,G) changes not due to an Assert
*
* The upstream (S,G) state machine remains
* in Joined state. Send Join(S,G) to the new
* upstream neighbor, which is the new value
* of RPF'(S,G). Send Prune(S,G) to the old
* upstream neighbor, which is the old value
* of RPF'(S,G). Set the Join Timer (JT) to
* expire after t_periodic seconds.
*/
pim_jp_agg_switch_interface(old, &up->rpf, up);
pim_upstream_join_timer_restart(up, old);
} /* up->join_state == PIM_UPSTREAM_JOINED */
}
else {
/*
* We have detected a case where we might need
* to rescan the inherited o_list so do it.
*/
if (up->channel_oil->oil_inherited_rescan) {
pim_upstream_inherited_olist_decide(pim, up);
up->channel_oil->oil_inherited_rescan = 0;
}
if (up->join_state == PIM_UPSTREAM_JOINED)
pim_jp_agg_switch_interface(old, &up->rpf, up);
if (!up->channel_oil->installed)
pim_upstream_mroute_add(up->channel_oil, __func__);
}
/* FIXME can join_desired actually be changed by pim_rpf_update()
* returning PIM_RPF_CHANGED ?
*/
pim_upstream_update_join_desired(pim, up);
}
__attribute__((unused))
static int pim_zebra_vxlan_sg_proc(ZAPI_CALLBACK_ARGS)
{
struct stream *s;
struct pim_instance *pim;
pim_sgaddr sg;
size_t prefixlen;
pim = pim_get_pim_instance(vrf_id);
if (!pim)
return 0;
s = zclient->ibuf;
prefixlen = stream_getl(s);
stream_get(&sg.src, s, prefixlen);
stream_get(&sg.grp, s, prefixlen);
if (PIM_DEBUG_ZEBRA)
zlog_debug("%u:recv SG %s %pSG", vrf_id,
(cmd == ZEBRA_VXLAN_SG_ADD) ? "add" : "del", &sg);
if (cmd == ZEBRA_VXLAN_SG_ADD)
pim_vxlan_sg_add(pim, &sg);
else
pim_vxlan_sg_del(pim, &sg);
return 0;
}
__attribute__((unused))
static void pim_zebra_vxlan_replay(void)
{
struct stream *s = NULL;
/* Check socket. */
if (!zclient || zclient->sock < 0)
return;
s = zclient->obuf;
stream_reset(s);
zclient_create_header(s, ZEBRA_VXLAN_SG_REPLAY, VRF_DEFAULT);
stream_putw_at(s, 0, stream_get_endp(s));
zclient_send_message(zclient);
}
void pim_scan_oil(struct pim_instance *pim)
{
struct channel_oil *c_oil;
pim->scan_oil_last = pim_time_monotonic_sec();
++pim->scan_oil_events;
frr_each (rb_pim_oil, &pim->channel_oil_head, c_oil)
pim_upstream_mroute_iif_update(c_oil, __func__);
}
static void on_rpf_cache_refresh(struct event *t)
{
struct pim_instance *pim = EVENT_ARG(t);
/* update kernel multicast forwarding cache (MFC) */
pim_scan_oil(pim);
pim->rpf_cache_refresh_last = pim_time_monotonic_sec();
++pim->rpf_cache_refresh_events;
// It is called as part of pim_neighbor_add
// pim_rp_setup ();
}
void sched_rpf_cache_refresh(struct pim_instance *pim)
{
++pim->rpf_cache_refresh_requests;
pim_rpf_set_refresh_time(pim);
if (pim->rpf_cache_refresher) {
/* Refresh timer is already running */
return;
}
/* Start refresh timer */
if (PIM_DEBUG_ZEBRA) {
zlog_debug("%s: triggering %ld msec timer", __func__,
router->rpf_cache_refresh_delay_msec);
}
event_add_timer_msec(router->master, on_rpf_cache_refresh, pim,
router->rpf_cache_refresh_delay_msec,
&pim->rpf_cache_refresher);
}
static void pim_zebra_connected(struct zclient *zclient)
{
#if PIM_IPV == 4
/* Send the client registration */
bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER, router->vrf_id);
#endif
zclient_send_reg_requests(zclient, router->vrf_id);
#if PIM_IPV == 4
/* request for VxLAN BUM group addresses */
pim_zebra_vxlan_replay();
#endif
}
static void pim_zebra_capabilities(struct zclient_capabilities *cap)
{
router->mlag_role = cap->role;
router->multipath = cap->ecmp;
}
static zclient_handler *const pim_handlers[] = {
[ZEBRA_INTERFACE_ADDRESS_ADD] = pim_zebra_if_address_add,
[ZEBRA_INTERFACE_ADDRESS_DELETE] = pim_zebra_if_address_del,
[ZEBRA_NEXTHOP_UPDATE] = pim_parse_nexthop_update,
[ZEBRA_ROUTER_ID_UPDATE] = pim_router_id_update_zebra,
[ZEBRA_INTERFACE_VRF_UPDATE] = pim_zebra_interface_vrf_update,
#if PIM_IPV == 4
[ZEBRA_VXLAN_SG_ADD] = pim_zebra_vxlan_sg_proc,
[ZEBRA_VXLAN_SG_DEL] = pim_zebra_vxlan_sg_proc,
[ZEBRA_MLAG_PROCESS_UP] = pim_zebra_mlag_process_up,
[ZEBRA_MLAG_PROCESS_DOWN] = pim_zebra_mlag_process_down,
[ZEBRA_MLAG_FORWARD_MSG] = pim_zebra_mlag_handle_msg,
#endif
};
void pim_zebra_init(void)
{
/* Socket for receiving updates from Zebra daemon */
zclient = zclient_new(router->master, &zclient_options_default,
pim_handlers, array_size(pim_handlers));
zclient->zebra_capabilities = pim_zebra_capabilities;
zclient->zebra_connected = pim_zebra_connected;
zclient_init(zclient, ZEBRA_ROUTE_PIM, 0, &pimd_privs);
if (PIM_DEBUG_PIM_TRACE) {
zlog_notice("%s: zclient socket initialized", __func__);
}
zclient_lookup_new();
}
void pim_forward_start(struct pim_ifchannel *ch)
{
struct pim_upstream *up = ch->upstream;
pimd: fix OIL not removed after IGMP prune Issue: Client1------LHR-----(int-1)RP(int-2)------client2 Client2 send IGMP join for group G. Client1 send IGMP join for group G. verify show ip mroute in RP, will have 2 OIL. Client2 send IGMP leave. Verify show ip mroute in RP, will still have 2. Root cause: When RP receives IGMP join from client2, it creates a (s,g) channel oil and add the interface int-2 into oil list and set the flag PIM_OIF_FLAG_PROTO_IGMP to int-2 Client1 send IGMP join, LHR will send a (*,G) join to RP. RP will add the interface int-1 into the oil list of (s,g) channel_oil and will set the flag PIM_OIF_FLAG_PROTO_IGMP and PIM_OIF_FLAG_PROTO_PIM to the int-1 and set PIM_OIF_FLAG_PROTO_PIM to int-2 as well. It is happening because of the pim_upstream_inherited_olist_decide() and forward_on() get all the oil and update the flag wrongly. So now when client 2 sends IGMP prune, RP will not remove the int-2 from oil list since both PIM_OIF_FLAG_PROTO_PIM & PIM_OIF_FLAG_PROTO_IGMP are set, it just unset the flag PIM_OIF_FLAG_PROTO_IGMP. Fix: Introduced new flags in if_channel, PIM_IF_FLAG_MASK_PROTO_PIM & PIM_IF_FLAG_MASK_PROTO_IGMP. If a if_channel is created because of pim join or pim (s,g,rpt) prune received, then set the flag PIM_IF_FLAG_MASK_PROTO_PIM. If a if_channel is created becuase of IGMP join received, then set the flag PIM_IF_FLAG_MASK_PROTO_IGMP. When an interface needs to be added into the oil list check if PIM_IF_FLAG_MASK_PROTO_PIM or PIM_IF_FLAG_MASK_PROTO_IGMP is set, then update oil flag accordingly. Signed-off-by: Sarita Patra <saritap@vmware.com>
2020-03-02 09:55:22 +01:00
uint32_t mask = 0;
if (PIM_DEBUG_PIM_TRACE)
zlog_debug("%s: (S,G)=%pSG oif=%s (%pPA)", __func__, &ch->sg,
ch->interface->name, &up->upstream_addr);
pimd: fix OIL not removed after IGMP prune Issue: Client1------LHR-----(int-1)RP(int-2)------client2 Client2 send IGMP join for group G. Client1 send IGMP join for group G. verify show ip mroute in RP, will have 2 OIL. Client2 send IGMP leave. Verify show ip mroute in RP, will still have 2. Root cause: When RP receives IGMP join from client2, it creates a (s,g) channel oil and add the interface int-2 into oil list and set the flag PIM_OIF_FLAG_PROTO_IGMP to int-2 Client1 send IGMP join, LHR will send a (*,G) join to RP. RP will add the interface int-1 into the oil list of (s,g) channel_oil and will set the flag PIM_OIF_FLAG_PROTO_IGMP and PIM_OIF_FLAG_PROTO_PIM to the int-1 and set PIM_OIF_FLAG_PROTO_PIM to int-2 as well. It is happening because of the pim_upstream_inherited_olist_decide() and forward_on() get all the oil and update the flag wrongly. So now when client 2 sends IGMP prune, RP will not remove the int-2 from oil list since both PIM_OIF_FLAG_PROTO_PIM & PIM_OIF_FLAG_PROTO_IGMP are set, it just unset the flag PIM_OIF_FLAG_PROTO_IGMP. Fix: Introduced new flags in if_channel, PIM_IF_FLAG_MASK_PROTO_PIM & PIM_IF_FLAG_MASK_PROTO_IGMP. If a if_channel is created because of pim join or pim (s,g,rpt) prune received, then set the flag PIM_IF_FLAG_MASK_PROTO_PIM. If a if_channel is created becuase of IGMP join received, then set the flag PIM_IF_FLAG_MASK_PROTO_IGMP. When an interface needs to be added into the oil list check if PIM_IF_FLAG_MASK_PROTO_PIM or PIM_IF_FLAG_MASK_PROTO_IGMP is set, then update oil flag accordingly. Signed-off-by: Sarita Patra <saritap@vmware.com>
2020-03-02 09:55:22 +01:00
if (PIM_IF_FLAG_TEST_PROTO_IGMP(ch->flags))
mask = PIM_OIF_FLAG_PROTO_GM;
pimd: fix OIL not removed after IGMP prune Issue: Client1------LHR-----(int-1)RP(int-2)------client2 Client2 send IGMP join for group G. Client1 send IGMP join for group G. verify show ip mroute in RP, will have 2 OIL. Client2 send IGMP leave. Verify show ip mroute in RP, will still have 2. Root cause: When RP receives IGMP join from client2, it creates a (s,g) channel oil and add the interface int-2 into oil list and set the flag PIM_OIF_FLAG_PROTO_IGMP to int-2 Client1 send IGMP join, LHR will send a (*,G) join to RP. RP will add the interface int-1 into the oil list of (s,g) channel_oil and will set the flag PIM_OIF_FLAG_PROTO_IGMP and PIM_OIF_FLAG_PROTO_PIM to the int-1 and set PIM_OIF_FLAG_PROTO_PIM to int-2 as well. It is happening because of the pim_upstream_inherited_olist_decide() and forward_on() get all the oil and update the flag wrongly. So now when client 2 sends IGMP prune, RP will not remove the int-2 from oil list since both PIM_OIF_FLAG_PROTO_PIM & PIM_OIF_FLAG_PROTO_IGMP are set, it just unset the flag PIM_OIF_FLAG_PROTO_IGMP. Fix: Introduced new flags in if_channel, PIM_IF_FLAG_MASK_PROTO_PIM & PIM_IF_FLAG_MASK_PROTO_IGMP. If a if_channel is created because of pim join or pim (s,g,rpt) prune received, then set the flag PIM_IF_FLAG_MASK_PROTO_PIM. If a if_channel is created becuase of IGMP join received, then set the flag PIM_IF_FLAG_MASK_PROTO_IGMP. When an interface needs to be added into the oil list check if PIM_IF_FLAG_MASK_PROTO_PIM or PIM_IF_FLAG_MASK_PROTO_IGMP is set, then update oil flag accordingly. Signed-off-by: Sarita Patra <saritap@vmware.com>
2020-03-02 09:55:22 +01:00
if (PIM_IF_FLAG_TEST_PROTO_PIM(ch->flags))
mask |= PIM_OIF_FLAG_PROTO_PIM;
pim_channel_add_oif(up->channel_oil, ch->interface,
mask, __func__);
}
void pim_forward_stop(struct pim_ifchannel *ch)
{
struct pim_upstream *up = ch->upstream;
if (PIM_DEBUG_PIM_TRACE) {
zlog_debug("%s: (S,G)=%s oif=%s installed: %d",
__func__, ch->sg_str, ch->interface->name,
up->channel_oil->installed);
}
/*
* If a channel is being removed, check to see if we still need
* to inherit the interface. If so make sure it is added in
*/
if (pim_upstream_evaluate_join_desired_interface(up, ch, ch->parent))
pim_channel_add_oif(up->channel_oil, ch->interface,
PIM_OIF_FLAG_PROTO_PIM, __func__);
else
pim_channel_del_oif(up->channel_oil, ch->interface,
PIM_OIF_FLAG_PROTO_PIM, __func__);
}
void pim_zebra_zclient_update(struct vty *vty)
{
vty_out(vty, "Zclient update socket: ");
if (zclient) {
vty_out(vty, "%d failures=%d\n", zclient->sock, zclient->fail);
} else {
vty_out(vty, "<null zclient>\n");
}
}
struct zclient *pim_zebra_zclient_get(void)
{
if (zclient)
return zclient;
else
return NULL;
}
void pim_zebra_interface_set_master(struct interface *vrf,
struct interface *ifp)
{
zclient_interface_set_master(zclient, vrf, ifp);
}