diff --git a/pimd/pim_bsm.c b/pimd/pim_bsm.c index cd8e9ede27..aab742f5c1 100644 --- a/pimd/pim_bsm.c +++ b/pimd/pim_bsm.c @@ -170,6 +170,11 @@ static void pim_bs_timer_start(struct bsm_scope *scope, int bs_timeout) &scope->bs_timer); } +static inline void pim_bs_timer_restart(struct bsm_scope *scope, int bs_timeout) +{ + pim_bs_timer_start(scope, bs_timeout); +} + void pim_bsm_proc_init(struct pim_instance *pim) { memset(&pim->global_scope, 0, sizeof(struct bsm_scope)); @@ -316,6 +321,99 @@ static inline void pim_g2rp_timer_restart(struct bsm_rpinfo *bsrp, pim_g2rp_timer_start(bsrp, hold_time); } +static bool pim_bsr_rpf_check(struct pim_instance *pim, struct in_addr bsr, + struct in_addr ip_src_addr) +{ + struct pim_nexthop nexthop; + int result; + + memset(&nexthop, 0, sizeof(nexthop)); + + /* New BSR recived */ + if (bsr.s_addr != pim->global_scope.current_bsr.s_addr) { + result = pim_nexthop_match(pim, bsr, ip_src_addr); + + /* Nexthop lookup pass for the new BSR address */ + if (result) + return true; + + if (PIM_DEBUG_BSM) { + char bsr_str[INET_ADDRSTRLEN]; + + pim_inet4_dump("", bsr, bsr_str, sizeof(bsr_str)); + zlog_debug("%s : No route to BSR address %s", + __PRETTY_FUNCTION__, bsr_str); + } + return false; + } + + return pim_nexthop_match_nht_cache(pim, bsr, ip_src_addr); +} + +static bool is_preferred_bsr(struct pim_instance *pim, struct in_addr bsr, + uint32_t bsr_prio) +{ + if (bsr.s_addr == pim->global_scope.current_bsr.s_addr) + return true; + + if (bsr_prio > pim->global_scope.current_bsr_prio) + return true; + + else if (bsr_prio == pim->global_scope.current_bsr_prio) { + if (bsr.s_addr >= pim->global_scope.current_bsr.s_addr) + return true; + else + return false; + } else + return false; +} + +static void pim_bsm_update(struct pim_instance *pim, struct in_addr bsr, + uint32_t bsr_prio) +{ + struct pim_nexthop_cache pnc; + + if (bsr.s_addr != pim->global_scope.current_bsr.s_addr) { + struct prefix nht_p; + char buf[PREFIX2STR_BUFFER]; + bool is_bsr_tracking = true; + + /* De-register old BSR and register new BSR with Zebra NHT */ + nht_p.family = AF_INET; + nht_p.prefixlen = IPV4_MAX_BITLEN; + + if (pim->global_scope.current_bsr.s_addr != INADDR_ANY) { + nht_p.u.prefix4 = pim->global_scope.current_bsr; + if (PIM_DEBUG_BSM) { + prefix2str(&nht_p, buf, sizeof(buf)); + zlog_debug( + "%s: Deregister BSR addr %s with Zebra NHT", + __PRETTY_FUNCTION__, buf); + } + pim_delete_tracked_nexthop(pim, &nht_p, NULL, NULL, + is_bsr_tracking); + } + + nht_p.u.prefix4 = bsr; + if (PIM_DEBUG_BSM) { + prefix2str(&nht_p, buf, sizeof(buf)); + zlog_debug( + "%s: NHT Register BSR addr %s with Zebra NHT", + __PRETTY_FUNCTION__, buf); + } + + memset(&pnc, 0, sizeof(struct pim_nexthop_cache)); + pim_find_or_track_nexthop(pim, &nht_p, NULL, NULL, + is_bsr_tracking, &pnc); + pim->global_scope.current_bsr = bsr; + pim->global_scope.current_bsr_first_ts = + pim_time_monotonic_sec(); + pim->global_scope.state = ACCEPT_PREFERRED; + } + pim->global_scope.current_bsr_prio = bsr_prio; + pim->global_scope.current_bsr_last_ts = pim_time_monotonic_sec(); +} + struct bsgrp_node *pim_bsm_get_bsgrp_node(struct bsm_scope *scope, struct prefix *grp) { @@ -334,3 +432,148 @@ struct bsgrp_node *pim_bsm_get_bsgrp_node(struct bsm_scope *scope, return bsgrp; } + +int pim_bsm_process(struct interface *ifp, struct ip *ip_hdr, uint8_t *buf, + uint32_t buf_size, bool no_fwd) +{ + struct bsm_hdr *bshdr; + struct bsmmsg_grpinfo *msg_grp; + struct pim_interface *pim_ifp = NULL; + struct pim_instance *pim; + char bsr_str[INET_ADDRSTRLEN]; + uint16_t frag_tag; + bool empty_bsm = FALSE; + + /* BSM Packet acceptance validation */ + pim_ifp = ifp->info; + if (!pim_ifp) { + if (PIM_DEBUG_BSM) + zlog_debug("%s: multicast not enabled on interface %s", + __PRETTY_FUNCTION__, ifp->name); + return -1; + } + + pim_ifp->pim_ifstat_bsm_rx++; + pim = pim_ifp->pim; + pim->bsm_rcvd++; + + /* Drop if bsm processing is disabled on interface */ + if (!pim_ifp->bsm_enable) { + zlog_warn("%s: BSM not enabled on interface %s", + __PRETTY_FUNCTION__, ifp->name); + pim_ifp->pim_ifstat_bsm_cfg_miss++; + pim->bsm_dropped++; + return -1; + } + + bshdr = (struct bsm_hdr *)(buf + PIM_MSG_HEADER_LEN); + pim_inet4_dump("", bshdr->bsr_addr.addr, bsr_str, + sizeof(bsr_str)); + pim->global_scope.hashMasklen = bshdr->hm_len; + frag_tag = ntohs(bshdr->frag_tag); + + /* Identify empty BSM */ + if ((buf_size - PIM_BSM_HDR_LEN - PIM_MSG_HEADER_LEN) < PIM_BSM_GRP_LEN) + empty_bsm = true; + + if (!empty_bsm) { + msg_grp = (struct bsmmsg_grpinfo *)(buf + PIM_MSG_HEADER_LEN + + PIM_BSM_HDR_LEN); + /* Currently we don't support scope zoned BSM */ + if (msg_grp->group.sz) { + if (PIM_DEBUG_BSM) + zlog_debug( + "%s : Administratively scoped range BSM received", + __PRETTY_FUNCTION__); + pim_ifp->pim_ifstat_bsm_invalid_sz++; + pim->bsm_dropped++; + return -1; + } + } + + /* Drop if bsr is not preferred bsr */ + if (!is_preferred_bsr(pim, bshdr->bsr_addr.addr, bshdr->bsr_prio)) { + if (PIM_DEBUG_BSM) + zlog_debug("%s : Received a non-preferred BSM", + __PRETTY_FUNCTION__); + pim->bsm_dropped++; + return -1; + } + + if (no_fwd) { + /* only accept no-forward BSM if quick refresh on startup */ + if ((pim->global_scope.accept_nofwd_bsm) + || (frag_tag == pim->global_scope.bsm_frag_tag)) { + pim->global_scope.accept_nofwd_bsm = false; + } else { + if (PIM_DEBUG_BSM) + zlog_debug( + "%s : nofwd_bsm received on %s when accpt_nofwd_bsm false", + __PRETTY_FUNCTION__, bsr_str); + pim->bsm_dropped++; + pim_ifp->pim_ifstat_ucast_bsm_cfg_miss++; + return -1; + } + } + + /* Mulicast BSM received */ + if (ip_hdr->ip_dst.s_addr == qpim_all_pim_routers_addr.s_addr) { + if (!no_fwd) { + if (!pim_bsr_rpf_check(pim, bshdr->bsr_addr.addr, + ip_hdr->ip_src)) { + if (PIM_DEBUG_BSM) + zlog_debug( + "%s : RPF check fail for BSR address %s", + __PRETTY_FUNCTION__, bsr_str); + pim->bsm_dropped++; + return -1; + } + } + } else if (if_lookup_exact_address(&ip_hdr->ip_dst, AF_INET, + pim->vrf_id)) { + /* Unicast BSM received - if ucast bsm not enabled on + * the interface, drop it + */ + if (!pim_ifp->ucast_bsm_accept) { + if (PIM_DEBUG_BSM) + zlog_debug( + "%s : Unicast BSM not enabled on interface %s", + __PRETTY_FUNCTION__, ifp->name); + pim_ifp->pim_ifstat_ucast_bsm_cfg_miss++; + pim->bsm_dropped++; + return -1; + } + + } else { + if (PIM_DEBUG_BSM) + zlog_debug("%s : Invalid destination address", + __PRETTY_FUNCTION__); + pim->bsm_dropped++; + return -1; + } + + if (empty_bsm) { + if (PIM_DEBUG_BSM) + zlog_debug("%s : Empty Pref BSM received", + __PRETTY_FUNCTION__); + } + /* Restart the bootstrap timer */ + pim_bs_timer_restart(&pim_ifp->pim->global_scope, + PIM_BSR_DEFAULT_TIMEOUT); + + /* If new BSM received, clear the old bsm database */ + if (pim_ifp->pim->global_scope.bsm_frag_tag != frag_tag) { + if (PIM_DEBUG_BSM) { + zlog_debug("%s: Current frag tag: %d Frag teg rcvd: %d", + __PRETTY_FUNCTION__, + pim_ifp->pim->global_scope.bsm_frag_tag, + frag_tag); + } + list_delete_all_node(pim_ifp->pim->global_scope.bsm_list); + pim_ifp->pim->global_scope.bsm_frag_tag = frag_tag; + } + + /* update the scope information from bsm */ + pim_bsm_update(pim, bshdr->bsr_addr.addr, bshdr->bsr_prio); + return 0; +} diff --git a/pimd/pim_bsm.h b/pimd/pim_bsm.h index 65df83d8e8..79aa805767 100644 --- a/pimd/pim_bsm.h +++ b/pimd/pim_bsm.h @@ -187,6 +187,11 @@ struct bsmmsg_rpinfo { void pim_bsm_proc_init(struct pim_instance *pim); void pim_bsm_proc_free(struct pim_instance *pim); void pim_bsm_write_config(struct vty *vty, struct interface *ifp); +int pim_bsm_process(struct interface *ifp, + struct ip *ip_hdr, + uint8_t *buf, + uint32_t buf_size, + bool no_fwd); struct bsgrp_node *pim_bsm_get_bsgrp_node(struct bsm_scope *scope, struct prefix *grp); #endif diff --git a/pimd/pim_pim.c b/pimd/pim_pim.c index e9d44b9c3c..12b28ed9af 100644 --- a/pimd/pim_pim.c +++ b/pimd/pim_pim.c @@ -149,6 +149,7 @@ int pim_pim_packet(struct interface *ifp, uint8_t *buf, size_t len) uint16_t checksum; /* computed checksum */ struct pim_neighbor *neigh; struct pim_msg_header *header; + bool no_fwd; if (len < sizeof(*ip_hdr)) { if (PIM_DEBUG_PIM_PACKETS) @@ -186,6 +187,7 @@ int pim_pim_packet(struct interface *ifp, uint8_t *buf, size_t len) /* for computing checksum */ header->checksum = 0; + no_fwd = header->Nbit; if (header->type == PIM_MSG_TYPE_REGISTER) { /* First 8 byte header checksum */ @@ -274,6 +276,10 @@ int pim_pim_packet(struct interface *ifp, uint8_t *buf, size_t len) pim_msg + PIM_MSG_HEADER_LEN, pim_msg_len - PIM_MSG_HEADER_LEN); break; + case PIM_MSG_TYPE_BOOTSTRAP: + return pim_bsm_process(ifp, ip_hdr, pim_msg, pim_msg_len, + no_fwd); + break; default: if (PIM_DEBUG_PIM_PACKETS) {