summaryrefslogtreecommitdiffstats
path: root/pimd/pim_zebra.c
diff options
context:
space:
mode:
Diffstat (limited to 'pimd/pim_zebra.c')
-rw-r--r--pimd/pim_zebra.c525
1 files changed, 525 insertions, 0 deletions
diff --git a/pimd/pim_zebra.c b/pimd/pim_zebra.c
new file mode 100644
index 0000000..62f00db
--- /dev/null
+++ b/pimd/pim_zebra.c
@@ -0,0 +1,525 @@
+// 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;
+}
+
+#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,
+
+#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;
+ 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);
+
+ if (PIM_IF_FLAG_TEST_PROTO_IGMP(ch->flags))
+ mask = PIM_OIF_FLAG_PROTO_GM;
+
+ 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);
+}