]> git.proxmox.com Git - mirror_frr.git/commitdiff
pimd: PIM Bootstrap packet processing
authorsaravanank <saravanank@vmware.com>
Sat, 4 May 2019 13:04:17 +0000 (06:04 -0700)
committersaravanank <saravanank@vmware.com>
Wed, 15 May 2019 04:40:50 +0000 (21:40 -0700)
1. Packet validation as per RFC 5059 Sec 3.1.3
We won't supporting scope zone BSM as of now, they are dropped now.
Order of the check slightly be changed in code for optimization.

   if ((DirectlyConnected(BSM.src_ip_address) == FALSE) OR
        (we have no Hello state for BSM.src_ip_address)) {
     drop the Bootstrap message silently
   }

   if (BSM.dst_ip_address == ALL-PIM-ROUTERS) {
     if (BSM.no_forward_bit == 0) {
       if (BSM.src_ip_address != RPF_neighbor(BSM.BSR_ip_address)) {
         drop the Bootstrap message silently
       }
     } else if ((any previous BSM for this scope has been accepted) OR
                (more than BS_Period has elapsed since startup)) {
       #only accept no-forward BSM if quick refresh on startup
       drop the Bootstrap message silently
     }
   } else if ((Unicast BSM support enabled) AND
              (BSM.dst_ip_address is one of my addresses)) {
     if ((any previous BSM for this scope has been accepted) OR
         (more than BS_Period has elapsed since startup)) {
       #the packet was unicast, but this wasn't
       #a quick refresh on startup
       drop the Bootstrap message silently
     }
   } else {
     drop the Bootstrap message silently
   }

2. Nexthop tracking registration for BSR
3. RPF check for BSR Message.
   Zebra Lookup based rpf check for new BSR
   NHT cache(pnc) based lookup for old BSR

Signed-off-by: Saravanan K <saravanank@vmware.com>
pimd/pim_bsm.c
pimd/pim_bsm.h
pimd/pim_pim.c

index cd8e9ede27cedf50bcc94aa9f79c19a523c24ced..aab742f5c148e49ab47d7ff9fc3117640971ad4e 100644 (file)
@@ -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, 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("<bsr?>", 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;
+}
index 65df83d8e83cc1978295c7802ee4985dab22d7f8..79aa805767edcb3b3f25f99c3cbadca75b5b3e01 100644 (file)
@@ -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
index e9d44b9c3c09e0fcfd7ffd66189f0f69c58e7a2a..12b28ed9af68015f44d2b858929b4842967e7648 100644 (file)
@@ -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) {