From b9f3a51c07819edcbd1622b68be9c52b88bf1cc5 Mon Sep 17 00:00:00 2001 From: Anuradha Karuppiah Date: Fri, 22 Mar 2019 13:38:50 -0700 Subject: [PATCH] pimd: register local VTEP-IP for each BUM MDT via NULL registers For multicast vxlan tunnels we register the local VTEP-IP independent of the prescence of BUM traffic i.e. we prime the pump. This is acheived via NULL registers. VxLAN orig entries with upstream in a PIM_REG_JOIN state are linked to a work list for periodic NULL register transmission. Once the SPT setup is complete the upstream-entry moves to a PIM_REG_PRUNE state and is remved from the VxLAN work list. Signed-off-by: Anuradha Karuppiah --- pimd/pim_register.c | 4 ++ pimd/pim_upstream.c | 2 + pimd/pim_vxlan.c | 149 ++++++++++++++++++++++++++++++++++++++++++++ pimd/pim_vxlan.h | 2 + 4 files changed, 157 insertions(+) diff --git a/pimd/pim_register.c b/pimd/pim_register.c index e2f38aab8b..372b7ba995 100644 --- a/pimd/pim_register.c +++ b/pimd/pim_register.c @@ -43,6 +43,7 @@ #include "pim_join.h" #include "pim_util.h" #include "pim_ssm.h" +#include "pim_vxlan.h" struct thread *send_test_packet_timer = NULL; @@ -60,6 +61,7 @@ void pim_register_join(struct pim_upstream *up) pim_channel_add_oif(up->channel_oil, pim->regiface, PIM_OIF_FLAG_PROTO_PIM); up->reg_state = PIM_REG_JOIN; + pim_vxlan_update_sg_reg_state(pim, up, TRUE /*reg_join*/); } void pim_register_stop_send(struct interface *ifp, struct prefix_sg *sg, @@ -145,6 +147,8 @@ int pim_register_stop_recv(struct interface *ifp, uint8_t *buf, int buf_size) pim_channel_del_oif(upstream->channel_oil, pim->regiface, PIM_OIF_FLAG_PROTO_PIM); pim_upstream_start_register_stop_timer(upstream, 0); + pim_vxlan_update_sg_reg_state(pim, upstream, + FALSE /*reg_join*/); break; case PIM_REG_JOIN_PENDING: upstream->reg_state = PIM_REG_PRUNE; diff --git a/pimd/pim_upstream.c b/pimd/pim_upstream.c index dc37815bba..7de2092a53 100644 --- a/pimd/pim_upstream.c +++ b/pimd/pim_upstream.c @@ -51,6 +51,7 @@ #include "pim_jp_agg.h" #include "pim_nht.h" #include "pim_ssm.h" +#include "pim_vxlan.h" static void join_timer_stop(struct pim_upstream *up); static void @@ -1432,6 +1433,7 @@ static int pim_upstream_register_stop_timer(struct thread *t) up->reg_state = PIM_REG_JOIN; pim_channel_add_oif(up->channel_oil, pim->regiface, PIM_OIF_FLAG_PROTO_PIM); + pim_vxlan_update_sg_reg_state(pim, up, TRUE /*reg_join*/); break; case PIM_REG_JOIN: break; diff --git a/pimd/pim_vxlan.c b/pimd/pim_vxlan.c index 24b71bf3d8..d6d6eaec1b 100644 --- a/pimd/pim_vxlan.c +++ b/pimd/pim_vxlan.c @@ -42,6 +42,153 @@ /* pim-vxlan global info */ struct pim_vxlan vxlan_info, *pim_vxlan_p = &vxlan_info; +static void pim_vxlan_work_timer_setup(bool start); + +/*************************** vxlan work list ********************************** + * A work list is maintained for staggered generation of pim null register + * messages for vxlan SG entries that are in a reg_join state. + * + * A max of 500 NULL registers are generated at one shot. If paused reg + * generation continues on the next second and so on till all register + * messages have been sent out. And the process is restarted every 60s. + * + * purpose of this null register generation is to setup the SPT and maintain + * independent of the presence of overlay BUM traffic. + ****************************************************************************/ +static void pim_vxlan_do_reg_work(void) +{ + struct listnode *listnode; + int work_cnt = 0; + struct pim_vxlan_sg *vxlan_sg; + static int sec_count; + + ++sec_count; + + if (sec_count > PIM_VXLAN_NULL_REG_INTERVAL) { + sec_count = 0; + listnode = vxlan_info.next_work ? + vxlan_info.next_work : + vxlan_info.work_list->head; + if (PIM_DEBUG_VXLAN && listnode) + zlog_debug("vxlan SG work %s", + vxlan_info.next_work ? "continues" : "starts"); + } else { + listnode = vxlan_info.next_work; + } + + for (; listnode; listnode = listnode->next) { + vxlan_sg = (struct pim_vxlan_sg *)listnode->data; + if (vxlan_sg->up && (vxlan_sg->up->reg_state == PIM_REG_JOIN)) { + if (PIM_DEBUG_VXLAN) + zlog_debug("vxlan SG %s periodic NULL register", + vxlan_sg->sg_str); + pim_null_register_send(vxlan_sg->up); + ++work_cnt; + } + + if (work_cnt > vxlan_info.max_work_cnt) { + vxlan_info.next_work = listnode->next; + if (PIM_DEBUG_VXLAN) + zlog_debug("vxlan SG %d work items proc and pause", + work_cnt); + return; + } + } + + if (work_cnt) { + if (PIM_DEBUG_VXLAN) + zlog_debug("vxlan SG %d work items proc", work_cnt); + } + vxlan_info.next_work = NULL; +} + +/* Staggered work related info is initialized when the first work comes + * along + */ +static void pim_vxlan_init_work(void) +{ + if (vxlan_info.flags & PIM_VXLANF_WORK_INITED) + return; + + vxlan_info.max_work_cnt = PIM_VXLAN_WORK_MAX; + vxlan_info.flags |= PIM_VXLANF_WORK_INITED; + vxlan_info.work_list = list_new(); + pim_vxlan_work_timer_setup(TRUE /* start */); +} + +static void pim_vxlan_add_work(struct pim_vxlan_sg *vxlan_sg) +{ + if (vxlan_sg->flags & PIM_VXLAN_SGF_DEL_IN_PROG) { + if (PIM_DEBUG_VXLAN) + zlog_debug("vxlan SG %s skip work list; del-in-prog", + vxlan_sg->sg_str); + return; + } + + pim_vxlan_init_work(); + + /* already a part of the work list */ + if (vxlan_sg->work_node) + return; + + if (PIM_DEBUG_VXLAN) + zlog_debug("vxlan SG %s work list add", + vxlan_sg->sg_str); + vxlan_sg->work_node = listnode_add(vxlan_info.work_list, vxlan_sg); + /* XXX: adjust max_work_cnt if needed */ +} + +static void pim_vxlan_del_work(struct pim_vxlan_sg *vxlan_sg) +{ + if (!vxlan_sg->work_node) + return; + + if (PIM_DEBUG_VXLAN) + zlog_debug("vxlan SG %s work list del", + vxlan_sg->sg_str); + + if (vxlan_sg->work_node == vxlan_info.next_work) + vxlan_info.next_work = vxlan_sg->work_node->next; + + list_delete_node(vxlan_info.work_list, vxlan_sg->work_node); + vxlan_sg->work_node = NULL; +} + +void pim_vxlan_update_sg_reg_state(struct pim_instance *pim, + struct pim_upstream *up, bool reg_join) +{ + struct pim_vxlan_sg *vxlan_sg; + + vxlan_sg = pim_vxlan_sg_find(pim, &up->sg); + if (!vxlan_sg) + return; + + /* add the vxlan sg entry to a work list for periodic reg joins. + * the entry will stay in the list as long as the register state is + * PIM_REG_JOIN + */ + if (reg_join) + pim_vxlan_add_work(vxlan_sg); + else + pim_vxlan_del_work(vxlan_sg); +} + +static int pim_vxlan_work_timer_cb(struct thread *t) +{ + pim_vxlan_do_reg_work(); + pim_vxlan_work_timer_setup(true /* start */); + return 0; +} + +/* global 1second timer used for periodic processing */ +static void pim_vxlan_work_timer_setup(bool start) +{ + THREAD_OFF(vxlan_info.work_timer); + if (start) + thread_add_timer(router->master, pim_vxlan_work_timer_cb, NULL, + PIM_VXLAN_WORK_TIME, &vxlan_info.work_timer); +} + /**************************** vxlan origination mroutes *********************** * For every (local-vtep-ip, bum-mcast-grp) registered by evpn an origination * mroute is setup by pimd. The purpose of this mroute is to forward vxlan @@ -394,6 +541,8 @@ void pim_vxlan_sg_del(struct pim_instance *pim, struct prefix_sg *sg) vxlan_sg->flags |= PIM_VXLAN_SGF_DEL_IN_PROG; + pim_vxlan_del_work(vxlan_sg); + if (pim_vxlan_is_orig_mroute(vxlan_sg)) pim_vxlan_orig_mr_del(vxlan_sg); diff --git a/pimd/pim_vxlan.h b/pimd/pim_vxlan.h index 0f729b4fff..5b810c3071 100644 --- a/pimd/pim_vxlan.h +++ b/pimd/pim_vxlan.h @@ -120,5 +120,7 @@ extern struct pim_vxlan_sg *pim_vxlan_sg_find(struct pim_instance *pim, extern struct pim_vxlan_sg *pim_vxlan_sg_add(struct pim_instance *pim, struct prefix_sg *sg); extern void pim_vxlan_sg_del(struct pim_instance *pim, struct prefix_sg *sg); +extern void pim_vxlan_update_sg_reg_state(struct pim_instance *pim, + struct pim_upstream *up, bool reg_join); #endif /* PIM_VXLAN_H */ -- 2.39.5