]> git.proxmox.com Git - mirror_frr.git/blob - pimd/pim_nht.c
pimd: add back blocking RPF for BSM
[mirror_frr.git] / pimd / pim_nht.c
1 /*
2 * PIM for Quagga
3 * Copyright (C) 2017 Cumulus Networks, Inc.
4 * Chirag Shah
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program; see the file COPYING; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20 #include <zebra.h>
21 #include "network.h"
22 #include "zclient.h"
23 #include "stream.h"
24 #include "nexthop.h"
25 #include "if.h"
26 #include "hash.h"
27 #include "jhash.h"
28
29 #include "lib/printfrr.h"
30
31 #include "pimd.h"
32 #include "pimd/pim_nht.h"
33 #include "log.h"
34 #include "pim_time.h"
35 #include "pim_oil.h"
36 #include "pim_ifchannel.h"
37 #include "pim_mroute.h"
38 #include "pim_zebra.h"
39 #include "pim_upstream.h"
40 #include "pim_join.h"
41 #include "pim_jp_agg.h"
42 #include "pim_zebra.h"
43 #include "pim_zlookup.h"
44 #include "pim_rp.h"
45
46 /**
47 * pim_sendmsg_zebra_rnh -- Format and send a nexthop register/Unregister
48 * command to Zebra.
49 */
50 void pim_sendmsg_zebra_rnh(struct pim_instance *pim, struct zclient *zclient,
51 struct pim_nexthop_cache *pnc, int command)
52 {
53 struct prefix *p;
54 int ret;
55
56 p = &(pnc->rpf.rpf_addr);
57 ret = zclient_send_rnh(zclient, command, p, false, false,
58 pim->vrf->vrf_id);
59 if (ret == ZCLIENT_SEND_FAILURE)
60 zlog_warn("sendmsg_nexthop: zclient_send_message() failed");
61
62 if (PIM_DEBUG_PIM_NHT)
63 zlog_debug(
64 "%s: NHT %sregistered addr %pFX(%s) with Zebra ret:%d ",
65 __func__,
66 (command == ZEBRA_NEXTHOP_REGISTER) ? " " : "de", p,
67 pim->vrf->name, ret);
68
69 return;
70 }
71
72 struct pim_nexthop_cache *pim_nexthop_cache_find(struct pim_instance *pim,
73 struct pim_rpf *rpf)
74 {
75 struct pim_nexthop_cache *pnc = NULL;
76 struct pim_nexthop_cache lookup;
77
78 lookup.rpf.rpf_addr.family = rpf->rpf_addr.family;
79 lookup.rpf.rpf_addr.prefixlen = rpf->rpf_addr.prefixlen;
80 lookup.rpf.rpf_addr.u.prefix4.s_addr = rpf->rpf_addr.u.prefix4.s_addr;
81
82 pnc = hash_lookup(pim->rpf_hash, &lookup);
83
84 return pnc;
85 }
86
87 static struct pim_nexthop_cache *pim_nexthop_cache_add(struct pim_instance *pim,
88 struct pim_rpf *rpf_addr)
89 {
90 struct pim_nexthop_cache *pnc;
91 char hash_name[64];
92
93 pnc = XCALLOC(MTYPE_PIM_NEXTHOP_CACHE,
94 sizeof(struct pim_nexthop_cache));
95 pnc->rpf.rpf_addr.family = rpf_addr->rpf_addr.family;
96 pnc->rpf.rpf_addr.prefixlen = rpf_addr->rpf_addr.prefixlen;
97 pnc->rpf.rpf_addr.u.prefix4.s_addr =
98 rpf_addr->rpf_addr.u.prefix4.s_addr;
99
100 pnc = hash_get(pim->rpf_hash, pnc, hash_alloc_intern);
101
102 pnc->rp_list = list_new();
103 pnc->rp_list->cmp = pim_rp_list_cmp;
104
105 snprintfrr(hash_name, sizeof(hash_name), "PNC %pFX(%s) Upstream Hash",
106 &pnc->rpf.rpf_addr, pim->vrf->name);
107 pnc->upstream_hash = hash_create_size(8192, pim_upstream_hash_key,
108 pim_upstream_equal, hash_name);
109
110 return pnc;
111 }
112
113 static struct pim_nexthop_cache *pim_nht_get(struct pim_instance *pim,
114 struct prefix *addr)
115 {
116 struct pim_nexthop_cache *pnc = NULL;
117 struct pim_rpf rpf;
118 struct zclient *zclient = NULL;
119
120 zclient = pim_zebra_zclient_get();
121 memset(&rpf, 0, sizeof(struct pim_rpf));
122 rpf.rpf_addr.family = addr->family;
123 rpf.rpf_addr.prefixlen = addr->prefixlen;
124 rpf.rpf_addr.u.prefix4 = addr->u.prefix4;
125
126 pnc = pim_nexthop_cache_find(pim, &rpf);
127 if (!pnc) {
128 pnc = pim_nexthop_cache_add(pim, &rpf);
129 pim_sendmsg_zebra_rnh(pim, zclient, pnc,
130 ZEBRA_NEXTHOP_REGISTER);
131 if (PIM_DEBUG_PIM_NHT)
132 zlog_debug(
133 "%s: NHT cache and zebra notification added for %pFX(%s)",
134 __func__, addr, pim->vrf->name);
135 }
136
137 return pnc;
138 }
139
140 /* TBD: this does several distinct things and should probably be split up.
141 * (checking state vs. returning pnc vs. adding upstream vs. adding rp)
142 */
143 int pim_find_or_track_nexthop(struct pim_instance *pim, struct prefix *addr,
144 struct pim_upstream *up, struct rp_info *rp,
145 struct pim_nexthop_cache *out_pnc)
146 {
147 struct pim_nexthop_cache *pnc;
148 struct listnode *ch_node = NULL;
149
150 pnc = pim_nht_get(pim, addr);
151
152 assertf(up || rp, "addr=%pFX", addr);
153
154 if (rp != NULL) {
155 ch_node = listnode_lookup(pnc->rp_list, rp);
156 if (ch_node == NULL)
157 listnode_add_sort(pnc->rp_list, rp);
158 }
159
160 if (up != NULL)
161 hash_get(pnc->upstream_hash, up, hash_alloc_intern);
162
163 if (CHECK_FLAG(pnc->flags, PIM_NEXTHOP_VALID)) {
164 if (out_pnc)
165 memcpy(out_pnc, pnc, sizeof(struct pim_nexthop_cache));
166 return 1;
167 }
168
169 return 0;
170 }
171
172 void pim_nht_bsr_add(struct pim_instance *pim, struct in_addr addr)
173 {
174 struct pim_nexthop_cache *pnc;
175 struct prefix pfx;
176
177 pfx.family = AF_INET;
178 pfx.prefixlen = IPV4_MAX_BITLEN;
179 pfx.u.prefix4 = addr;
180
181 pnc = pim_nht_get(pim, &pfx);
182
183 pnc->bsr_count++;
184 }
185
186 static void pim_nht_drop_maybe(struct pim_instance *pim,
187 struct pim_nexthop_cache *pnc)
188 {
189 if (PIM_DEBUG_PIM_NHT)
190 zlog_debug(
191 "%s: NHT %pFX(%s) rp_list count:%d upstream count:%ld BSR count:%u",
192 __func__, &pnc->rpf.rpf_addr, pim->vrf->name,
193 pnc->rp_list->count, pnc->upstream_hash->count,
194 pnc->bsr_count);
195
196 if (pnc->rp_list->count == 0 && pnc->upstream_hash->count == 0
197 && pnc->bsr_count == 0) {
198 struct zclient *zclient = pim_zebra_zclient_get();
199
200 pim_sendmsg_zebra_rnh(pim, zclient, pnc,
201 ZEBRA_NEXTHOP_UNREGISTER);
202
203 list_delete(&pnc->rp_list);
204 hash_free(pnc->upstream_hash);
205
206 hash_release(pim->rpf_hash, pnc);
207 if (pnc->nexthop)
208 nexthops_free(pnc->nexthop);
209 XFREE(MTYPE_PIM_NEXTHOP_CACHE, pnc);
210 }
211 }
212
213 void pim_delete_tracked_nexthop(struct pim_instance *pim, struct prefix *addr,
214 struct pim_upstream *up, struct rp_info *rp)
215 {
216 struct pim_nexthop_cache *pnc = NULL;
217 struct pim_nexthop_cache lookup;
218 struct pim_upstream *upstream = NULL;
219
220 /* Remove from RPF hash if it is the last entry */
221 lookup.rpf.rpf_addr = *addr;
222 pnc = hash_lookup(pim->rpf_hash, &lookup);
223 if (!pnc) {
224 zlog_warn("attempting to delete nonexistent NHT entry %pFX",
225 addr);
226 return;
227 }
228
229 if (rp) {
230 /* Release the (*, G)upstream from pnc->upstream_hash,
231 * whose Group belongs to the RP getting deleted
232 */
233 frr_each (rb_pim_upstream, &pim->upstream_head, upstream) {
234 struct prefix grp;
235 struct rp_info *trp_info;
236
237 if (upstream->sg.src.s_addr != INADDR_ANY)
238 continue;
239
240 grp.family = AF_INET;
241 grp.prefixlen = IPV4_MAX_BITLEN;
242 grp.u.prefix4 = upstream->sg.grp;
243
244 trp_info = pim_rp_find_match_group(pim, &grp);
245 if (trp_info == rp)
246 hash_release(pnc->upstream_hash, upstream);
247 }
248 listnode_delete(pnc->rp_list, rp);
249 }
250
251 if (up)
252 hash_release(pnc->upstream_hash, up);
253
254 pim_nht_drop_maybe(pim, pnc);
255 }
256
257 void pim_nht_bsr_del(struct pim_instance *pim, struct in_addr addr)
258 {
259 struct pim_nexthop_cache *pnc = NULL;
260 struct pim_nexthop_cache lookup;
261
262 lookup.rpf.rpf_addr.family = AF_INET;
263 lookup.rpf.rpf_addr.prefixlen = IPV4_MAX_BITLEN;
264 lookup.rpf.rpf_addr.u.prefix4 = addr;
265
266 pnc = hash_lookup(pim->rpf_hash, &lookup);
267
268 if (!pnc) {
269 zlog_warn("attempting to delete nonexistent NHT BSR entry %pI4",
270 &addr);
271 return;
272 }
273
274 assertf(pnc->bsr_count > 0, "addr=%pI4", &addr);
275 pnc->bsr_count--;
276
277 pim_nht_drop_maybe(pim, pnc);
278 }
279
280 bool pim_nht_bsr_rpf_check(struct pim_instance *pim, struct in_addr bsr_addr,
281 struct interface *src_ifp, struct in_addr src_ip)
282 {
283 struct pim_nexthop_cache *pnc = NULL;
284 struct pim_nexthop_cache lookup;
285 struct pim_neighbor *nbr = NULL;
286 struct nexthop *nh;
287 struct interface *ifp;
288
289 lookup.rpf.rpf_addr.family = AF_INET;
290 lookup.rpf.rpf_addr.prefixlen = IPV4_MAX_BITLEN;
291 lookup.rpf.rpf_addr.u.prefix4 = bsr_addr;
292
293 pnc = hash_lookup(pim->rpf_hash, &lookup);
294 if (!pnc || !CHECK_FLAG(pnc->flags, PIM_NEXTHOP_ANSWER_RECEIVED)) {
295 /* BSM from a new freshly registered BSR - do a synchronous
296 * zebra query since otherwise we'd drop the first packet,
297 * leading to additional delay in picking up BSM data
298 */
299
300 /* FIXME: this should really be moved into a generic NHT
301 * function that does "add and get immediate result" or maybe
302 * "check cache or get immediate result." But until that can
303 * be worked in, here's a copy of the code below :(
304 */
305 struct pim_zlookup_nexthop nexthop_tab[MULTIPATH_NUM];
306 ifindex_t i;
307 struct interface *ifp = NULL;
308 int num_ifindex;
309
310 memset(nexthop_tab, 0, sizeof(nexthop_tab));
311 num_ifindex = zclient_lookup_nexthop(pim, nexthop_tab,
312 MULTIPATH_NUM, bsr_addr,
313 PIM_NEXTHOP_LOOKUP_MAX);
314
315 if (num_ifindex <= 0)
316 return false;
317
318 for (i = 0; i < num_ifindex; i++) {
319 struct pim_zlookup_nexthop *znh = &nexthop_tab[i];
320
321 /* pim_zlookup_nexthop has no ->type */
322
323 /* 1:1 match code below with znh instead of nh */
324 ifp = if_lookup_by_index(znh->ifindex,
325 pim->vrf->vrf_id);
326
327 if (!ifp || !ifp->info)
328 continue;
329
330 if (if_is_loopback(ifp) && if_is_loopback(src_ifp))
331 return true;
332
333 nbr = pim_neighbor_find(ifp,
334 znh->nexthop_addr.u.prefix4);
335 if (!nbr)
336 continue;
337
338 return znh->ifindex == src_ifp->ifindex
339 && znh->nexthop_addr.u.prefix4.s_addr
340 == src_ip.s_addr;
341 }
342 return false;
343 }
344
345 if (!CHECK_FLAG(pnc->flags, PIM_NEXTHOP_VALID))
346 return false;
347
348 /* if we accept BSMs from more than one ECMP nexthop, this will cause
349 * BSM message "multiplication" for each ECMP hop. i.e. if you have
350 * 4-way ECMP and 4 hops you end up with 256 copies of each BSM
351 * message.
352 *
353 * so... only accept the first (IPv4) valid nexthop as source.
354 */
355
356 for (nh = pnc->nexthop; nh; nh = nh->next) {
357 struct in_addr nhaddr;
358
359 switch (nh->type) {
360 case NEXTHOP_TYPE_IPV4:
361 if (nh->ifindex == IFINDEX_INTERNAL)
362 continue;
363
364 /* fallthru */
365 case NEXTHOP_TYPE_IPV4_IFINDEX:
366 nhaddr = nh->gate.ipv4;
367 break;
368
369 case NEXTHOP_TYPE_IFINDEX:
370 nhaddr = bsr_addr;
371 break;
372
373 default:
374 continue;
375 }
376
377 ifp = if_lookup_by_index(nh->ifindex, pim->vrf->vrf_id);
378 if (!ifp || !ifp->info)
379 continue;
380
381 if (if_is_loopback(ifp) && if_is_loopback(src_ifp))
382 return true;
383
384 /* MRIB (IGP) may be pointing at a router where PIM is down */
385 nbr = pim_neighbor_find(ifp, nhaddr);
386 if (!nbr)
387 continue;
388
389 return nh->ifindex == src_ifp->ifindex
390 && nhaddr.s_addr == src_ip.s_addr;
391 }
392 return false;
393 }
394
395 void pim_rp_nexthop_del(struct rp_info *rp_info)
396 {
397 rp_info->rp.source_nexthop.interface = NULL;
398 rp_info->rp.source_nexthop.mrib_nexthop_addr.u.prefix4.s_addr =
399 PIM_NET_INADDR_ANY;
400 rp_info->rp.source_nexthop.mrib_metric_preference =
401 router->infinite_assert_metric.metric_preference;
402 rp_info->rp.source_nexthop.mrib_route_metric =
403 router->infinite_assert_metric.route_metric;
404 }
405
406 /* Update RP nexthop info based on Nexthop update received from Zebra.*/
407 static void pim_update_rp_nh(struct pim_instance *pim,
408 struct pim_nexthop_cache *pnc)
409 {
410 struct listnode *node = NULL;
411 struct rp_info *rp_info = NULL;
412
413 /*Traverse RP list and update each RP Nexthop info */
414 for (ALL_LIST_ELEMENTS_RO(pnc->rp_list, node, rp_info)) {
415 if (rp_info->rp.rpf_addr.u.prefix4.s_addr == INADDR_NONE)
416 continue;
417
418 // Compute PIM RPF using cached nexthop
419 if (!pim_ecmp_nexthop_lookup(pim, &rp_info->rp.source_nexthop,
420 &rp_info->rp.rpf_addr,
421 &rp_info->group, 1))
422 pim_rp_nexthop_del(rp_info);
423 }
424 }
425
426 /* Update Upstream nexthop info based on Nexthop update received from Zebra.*/
427 static int pim_update_upstream_nh_helper(struct hash_bucket *bucket, void *arg)
428 {
429 struct pim_instance *pim = (struct pim_instance *)arg;
430 struct pim_upstream *up = (struct pim_upstream *)bucket->data;
431
432 enum pim_rpf_result rpf_result;
433 struct pim_rpf old;
434
435 old.source_nexthop.interface = up->rpf.source_nexthop.interface;
436 rpf_result = pim_rpf_update(pim, up, &old, __func__);
437
438 /* update kernel multicast forwarding cache (MFC); if the
439 * RPF nbr is now unreachable the MFC has already been updated
440 * by pim_rpf_clear
441 */
442 if (rpf_result != PIM_RPF_FAILURE)
443 pim_upstream_mroute_iif_update(up->channel_oil, __func__);
444
445 if (rpf_result == PIM_RPF_CHANGED ||
446 (rpf_result == PIM_RPF_FAILURE && old.source_nexthop.interface))
447 pim_zebra_upstream_rpf_changed(pim, up, &old);
448
449
450 if (PIM_DEBUG_PIM_NHT) {
451 zlog_debug(
452 "%s: NHT upstream %s(%s) old ifp %s new ifp %s",
453 __func__, up->sg_str, pim->vrf->name,
454 old.source_nexthop.interface ? old.source_nexthop
455 .interface->name
456 : "Unknown",
457 up->rpf.source_nexthop.interface ? up->rpf.source_nexthop
458 .interface->name
459 : "Unknown");
460 }
461
462 return HASHWALK_CONTINUE;
463 }
464
465 static int pim_update_upstream_nh(struct pim_instance *pim,
466 struct pim_nexthop_cache *pnc)
467 {
468 hash_walk(pnc->upstream_hash, pim_update_upstream_nh_helper, pim);
469
470 pim_zebra_update_all_interfaces(pim);
471
472 return 0;
473 }
474
475 uint32_t pim_compute_ecmp_hash(struct prefix *src, struct prefix *grp)
476 {
477 uint32_t hash_val;
478 uint32_t s = 0, g = 0;
479
480 if ((!src))
481 return 0;
482
483 switch (src->family) {
484 case AF_INET: {
485 s = src->u.prefix4.s_addr;
486 s = s == 0 ? 1 : s;
487 if (grp)
488 g = grp->u.prefix4.s_addr;
489 } break;
490 default:
491 break;
492 }
493
494 hash_val = jhash_2words(g, s, 101);
495 return hash_val;
496 }
497
498 static int pim_ecmp_nexthop_search(struct pim_instance *pim,
499 struct pim_nexthop_cache *pnc,
500 struct pim_nexthop *nexthop,
501 struct prefix *src, struct prefix *grp,
502 int neighbor_needed)
503 {
504 struct pim_neighbor *nbrs[MULTIPATH_NUM], *nbr = NULL;
505 struct interface *ifps[MULTIPATH_NUM];
506 struct nexthop *nh_node = NULL;
507 ifindex_t first_ifindex;
508 struct interface *ifp = NULL;
509 uint32_t hash_val = 0, mod_val = 0;
510 uint8_t nh_iter = 0, found = 0;
511 uint32_t i, num_nbrs = 0;
512
513 if (!pnc || !pnc->nexthop_num || !nexthop)
514 return 0;
515
516 memset(&nbrs, 0, sizeof(nbrs));
517 memset(&ifps, 0, sizeof(ifps));
518
519 // Current Nexthop is VALID, check to stay on the current path.
520 if (nexthop->interface && nexthop->interface->info
521 && nexthop->mrib_nexthop_addr.u.prefix4.s_addr
522 != PIM_NET_INADDR_ANY) {
523 /* User configured knob to explicitly switch
524 to new path is disabled or current path
525 metric is less than nexthop update.
526 */
527
528 if (pim->ecmp_rebalance_enable == 0) {
529 uint8_t curr_route_valid = 0;
530 // Check if current nexthop is present in new updated
531 // Nexthop list.
532 // If the current nexthop is not valid, candidate to
533 // choose new Nexthop.
534 for (nh_node = pnc->nexthop; nh_node;
535 nh_node = nh_node->next) {
536 curr_route_valid = (nexthop->interface->ifindex
537 == nh_node->ifindex);
538 if (curr_route_valid)
539 break;
540 }
541
542 if (curr_route_valid
543 && !pim_if_connected_to_source(nexthop->interface,
544 src->u.prefix4)) {
545 nbr = pim_neighbor_find(
546 nexthop->interface,
547 nexthop->mrib_nexthop_addr.u.prefix4);
548 if (!nbr
549 && !if_is_loopback(nexthop->interface)) {
550 if (PIM_DEBUG_PIM_NHT)
551 zlog_debug(
552 "%s: current nexthop does not have nbr ",
553 __func__);
554 } else {
555 /* update metric even if the upstream
556 * neighbor stays unchanged
557 */
558 nexthop->mrib_metric_preference =
559 pnc->distance;
560 nexthop->mrib_route_metric =
561 pnc->metric;
562 if (PIM_DEBUG_PIM_NHT) {
563 char src_str[INET_ADDRSTRLEN];
564 pim_inet4_dump("<addr?>",
565 src->u.prefix4,
566 src_str,
567 sizeof(src_str));
568 char grp_str[INET_ADDRSTRLEN];
569 pim_inet4_dump("<addr?>",
570 grp->u.prefix4,
571 grp_str,
572 sizeof(grp_str));
573 zlog_debug(
574 "%s: (%s,%s)(%s) current nexthop %s is valid, skipping new path selection",
575 __func__, src_str,
576 grp_str, pim->vrf->name,
577 nexthop->interface->name);
578 }
579 return 1;
580 }
581 }
582 }
583 }
584
585 /*
586 * Look up all interfaces and neighbors,
587 * store for later usage
588 */
589 for (nh_node = pnc->nexthop, i = 0; nh_node;
590 nh_node = nh_node->next, i++) {
591 ifps[i] =
592 if_lookup_by_index(nh_node->ifindex, pim->vrf->vrf_id);
593 if (ifps[i]) {
594 nbrs[i] = pim_neighbor_find(ifps[i],
595 nh_node->gate.ipv4);
596 if (nbrs[i] || pim_if_connected_to_source(ifps[i],
597
598 src->u.prefix4))
599 num_nbrs++;
600 }
601 }
602 if (pim->ecmp_enable) {
603 uint32_t consider = pnc->nexthop_num;
604
605 if (neighbor_needed && num_nbrs < consider)
606 consider = num_nbrs;
607
608 if (consider == 0)
609 return 0;
610
611 // PIM ECMP flag is enable then choose ECMP path.
612 hash_val = pim_compute_ecmp_hash(src, grp);
613 mod_val = hash_val % consider;
614 }
615
616 for (nh_node = pnc->nexthop; nh_node && (found == 0);
617 nh_node = nh_node->next) {
618 first_ifindex = nh_node->ifindex;
619 ifp = ifps[nh_iter];
620 if (!ifp) {
621 if (PIM_DEBUG_PIM_NHT) {
622 char addr_str[INET_ADDRSTRLEN];
623 pim_inet4_dump("<addr?>", src->u.prefix4,
624 addr_str, sizeof(addr_str));
625 zlog_debug(
626 "%s %s: could not find interface for ifindex %d (address %s(%s))",
627 __FILE__, __func__, first_ifindex,
628 addr_str, pim->vrf->name);
629 }
630 if (nh_iter == mod_val)
631 mod_val++; // Select nexthpath
632 nh_iter++;
633 continue;
634 }
635 if (!ifp->info) {
636 if (PIM_DEBUG_PIM_NHT) {
637 char addr_str[INET_ADDRSTRLEN];
638 pim_inet4_dump("<addr?>", src->u.prefix4,
639 addr_str, sizeof(addr_str));
640 zlog_debug(
641 "%s: multicast not enabled on input interface %s(%s) (ifindex=%d, RPF for source %s)",
642 __func__, ifp->name, pim->vrf->name,
643 first_ifindex, addr_str);
644 }
645 if (nh_iter == mod_val)
646 mod_val++; // Select nexthpath
647 nh_iter++;
648 continue;
649 }
650
651 if (neighbor_needed
652 && !pim_if_connected_to_source(ifp, src->u.prefix4)) {
653 nbr = nbrs[nh_iter];
654 if (!nbr && !if_is_loopback(ifp)) {
655 if (PIM_DEBUG_PIM_NHT)
656 zlog_debug(
657 "%s: pim nbr not found on input interface %s(%s)",
658 __func__, ifp->name,
659 pim->vrf->name);
660 if (nh_iter == mod_val)
661 mod_val++; // Select nexthpath
662 nh_iter++;
663 continue;
664 }
665 }
666
667 if (nh_iter == mod_val) {
668 nexthop->interface = ifp;
669 nexthop->mrib_nexthop_addr.family = AF_INET;
670 nexthop->mrib_nexthop_addr.prefixlen = IPV4_MAX_BITLEN;
671 nexthop->mrib_nexthop_addr.u.prefix4 =
672 nh_node->gate.ipv4;
673 nexthop->mrib_metric_preference = pnc->distance;
674 nexthop->mrib_route_metric = pnc->metric;
675 nexthop->last_lookup = src->u.prefix4;
676 nexthop->last_lookup_time = pim_time_monotonic_usec();
677 nexthop->nbr = nbr;
678 found = 1;
679 if (PIM_DEBUG_PIM_NHT) {
680 char buf[INET_ADDRSTRLEN];
681 char buf2[INET_ADDRSTRLEN];
682 char buf3[INET_ADDRSTRLEN];
683 pim_inet4_dump("<src?>", src->u.prefix4, buf2,
684 sizeof(buf2));
685 pim_inet4_dump("<grp?>", grp->u.prefix4, buf3,
686 sizeof(buf3));
687 pim_inet4_dump(
688 "<rpf?>",
689 nexthop->mrib_nexthop_addr.u.prefix4,
690 buf, sizeof(buf));
691 zlog_debug(
692 "%s: (%s,%s)(%s) selected nhop interface %s addr %s mod_val %u iter %d ecmp %d",
693 __func__, buf2, buf3, pim->vrf->name,
694 ifp->name, buf, mod_val, nh_iter,
695 pim->ecmp_enable);
696 }
697 }
698 nh_iter++;
699 }
700
701 if (found)
702 return 1;
703 else
704 return 0;
705 }
706
707 /* This API is used to parse Registered address nexthop update coming from Zebra
708 */
709 int pim_parse_nexthop_update(ZAPI_CALLBACK_ARGS)
710 {
711 struct nexthop *nexthop;
712 struct nexthop *nhlist_head = NULL;
713 struct nexthop *nhlist_tail = NULL;
714 int i;
715 struct pim_rpf rpf;
716 struct pim_nexthop_cache *pnc = NULL;
717 struct pim_neighbor *nbr = NULL;
718 struct interface *ifp = NULL;
719 struct interface *ifp1 = NULL;
720 struct vrf *vrf = vrf_lookup_by_id(vrf_id);
721 struct pim_instance *pim;
722 struct zapi_route nhr;
723
724 if (!vrf)
725 return 0;
726 pim = vrf->info;
727
728 if (!zapi_nexthop_update_decode(zclient->ibuf, &nhr)) {
729 zlog_err("%s: Decode of nexthop update from zebra failed",
730 __func__);
731 return 0;
732 }
733
734 if (cmd == ZEBRA_NEXTHOP_UPDATE) {
735 prefix_copy(&rpf.rpf_addr, &nhr.prefix);
736 pnc = pim_nexthop_cache_find(pim, &rpf);
737 if (!pnc) {
738 if (PIM_DEBUG_PIM_NHT)
739 zlog_debug(
740 "%s: Skipping NHT update, addr %pFX is not in local cached DB.",
741 __func__, &rpf.rpf_addr);
742 return 0;
743 }
744 } else {
745 /*
746 * We do not currently handle ZEBRA_IMPORT_CHECK_UPDATE
747 */
748 return 0;
749 }
750
751 pnc->last_update = pim_time_monotonic_usec();
752
753 if (nhr.nexthop_num) {
754 pnc->nexthop_num = 0; // Only increment for pim enabled rpf.
755
756 for (i = 0; i < nhr.nexthop_num; i++) {
757 nexthop = nexthop_from_zapi_nexthop(&nhr.nexthops[i]);
758 switch (nexthop->type) {
759 case NEXTHOP_TYPE_IPV4:
760 case NEXTHOP_TYPE_IPV4_IFINDEX:
761 case NEXTHOP_TYPE_IPV6:
762 case NEXTHOP_TYPE_BLACKHOLE:
763 break;
764 case NEXTHOP_TYPE_IFINDEX:
765 /*
766 * Connected route (i.e. no nexthop), use
767 * RPF address from nexthop cache (i.e.
768 * destination) as PIM nexthop.
769 */
770 nexthop->type = NEXTHOP_TYPE_IPV4_IFINDEX;
771 nexthop->gate.ipv4 =
772 pnc->rpf.rpf_addr.u.prefix4;
773 break;
774 case NEXTHOP_TYPE_IPV6_IFINDEX:
775 ifp1 = if_lookup_by_index(nexthop->ifindex,
776 pim->vrf->vrf_id);
777
778 if (!ifp1)
779 nbr = NULL;
780 else
781 nbr = pim_neighbor_find_if(ifp1);
782 /* Overwrite with Nbr address as NH addr */
783 if (nbr)
784 nexthop->gate.ipv4 = nbr->source_addr;
785 else {
786 // Mark nexthop address to 0 until PIM
787 // Nbr is resolved.
788 nexthop->gate.ipv4.s_addr =
789 PIM_NET_INADDR_ANY;
790 }
791
792 break;
793 }
794
795 ifp = if_lookup_by_index(nexthop->ifindex,
796 pim->vrf->vrf_id);
797 if (!ifp) {
798 if (PIM_DEBUG_PIM_NHT) {
799 char buf[NEXTHOP_STRLEN];
800 zlog_debug(
801 "%s: could not find interface for ifindex %d(%s) (addr %s)",
802 __func__, nexthop->ifindex,
803 pim->vrf->name,
804 nexthop2str(nexthop, buf,
805 sizeof(buf)));
806 }
807 nexthop_free(nexthop);
808 continue;
809 }
810
811 if (PIM_DEBUG_PIM_NHT)
812 zlog_debug(
813 "%s: NHT addr %pFX(%s) %d-nhop via %pI4(%s) type %d distance:%u metric:%u ",
814 __func__, &nhr.prefix, pim->vrf->name,
815 i + 1, &nexthop->gate.ipv4,
816 ifp->name, nexthop->type, nhr.distance,
817 nhr.metric);
818
819 if (!ifp->info) {
820 /*
821 * Though Multicast is not enabled on this
822 * Interface store it in database otheriwse we
823 * may miss this update and this will not cause
824 * any issue, because while choosing the path we
825 * are ommitting the Interfaces which are not
826 * multicast enabled
827 */
828 if (PIM_DEBUG_PIM_NHT) {
829 char buf[NEXTHOP_STRLEN];
830
831 zlog_debug(
832 "%s: multicast not enabled on input interface %s(%s) (ifindex=%d, addr %s)",
833 __func__, ifp->name,
834 pim->vrf->name,
835 nexthop->ifindex,
836 nexthop2str(nexthop, buf,
837 sizeof(buf)));
838 }
839 }
840
841 if (nhlist_tail) {
842 nhlist_tail->next = nexthop;
843 nhlist_tail = nexthop;
844 } else {
845 nhlist_tail = nexthop;
846 nhlist_head = nexthop;
847 }
848 // Only keep track of nexthops which are PIM enabled.
849 pnc->nexthop_num++;
850 }
851 /* Reset existing pnc->nexthop before assigning new list */
852 nexthops_free(pnc->nexthop);
853 pnc->nexthop = nhlist_head;
854 if (pnc->nexthop_num) {
855 pnc->flags |= PIM_NEXTHOP_VALID;
856 pnc->distance = nhr.distance;
857 pnc->metric = nhr.metric;
858 }
859 } else {
860 pnc->flags &= ~PIM_NEXTHOP_VALID;
861 pnc->nexthop_num = nhr.nexthop_num;
862 nexthops_free(pnc->nexthop);
863 pnc->nexthop = NULL;
864 }
865 SET_FLAG(pnc->flags, PIM_NEXTHOP_ANSWER_RECEIVED);
866
867 if (PIM_DEBUG_PIM_NHT)
868 zlog_debug(
869 "%s: NHT Update for %pFX(%s) num_nh %d num_pim_nh %d vrf:%u up %ld rp %d",
870 __func__, &nhr.prefix, pim->vrf->name, nhr.nexthop_num,
871 pnc->nexthop_num, vrf_id, pnc->upstream_hash->count,
872 listcount(pnc->rp_list));
873
874 pim_rpf_set_refresh_time(pim);
875
876 if (listcount(pnc->rp_list))
877 pim_update_rp_nh(pim, pnc);
878 if (pnc->upstream_hash->count)
879 pim_update_upstream_nh(pim, pnc);
880
881 return 0;
882 }
883
884 int pim_ecmp_nexthop_lookup(struct pim_instance *pim,
885 struct pim_nexthop *nexthop, struct prefix *src,
886 struct prefix *grp, int neighbor_needed)
887 {
888 struct pim_nexthop_cache *pnc;
889 struct pim_zlookup_nexthop nexthop_tab[MULTIPATH_NUM];
890 struct pim_neighbor *nbrs[MULTIPATH_NUM], *nbr = NULL;
891 struct pim_rpf rpf;
892 int num_ifindex;
893 struct interface *ifps[MULTIPATH_NUM], *ifp;
894 int first_ifindex;
895 int found = 0;
896 uint8_t i = 0;
897 uint32_t hash_val = 0, mod_val = 0;
898 uint32_t num_nbrs = 0;
899 char addr_str[PREFIX_STRLEN];
900
901 if (PIM_DEBUG_PIM_NHT) {
902 pim_inet4_dump("<addr?>", src->u.prefix4, addr_str,
903 sizeof(addr_str));
904 zlog_debug("%s: Looking up: %s(%s), last lookup time: %lld",
905 __func__, addr_str, pim->vrf->name,
906 nexthop->last_lookup_time);
907 }
908
909 memset(&rpf, 0, sizeof(struct pim_rpf));
910 rpf.rpf_addr.family = AF_INET;
911 rpf.rpf_addr.prefixlen = IPV4_MAX_BITLEN;
912 rpf.rpf_addr.u.prefix4 = src->u.prefix4;
913
914 pnc = pim_nexthop_cache_find(pim, &rpf);
915 if (pnc) {
916 if (CHECK_FLAG(pnc->flags, PIM_NEXTHOP_ANSWER_RECEIVED))
917 return pim_ecmp_nexthop_search(pim, pnc, nexthop, src, grp,
918 neighbor_needed);
919 }
920
921 memset(nexthop_tab, 0,
922 sizeof(struct pim_zlookup_nexthop) * MULTIPATH_NUM);
923 num_ifindex =
924 zclient_lookup_nexthop(pim, nexthop_tab, MULTIPATH_NUM,
925 src->u.prefix4, PIM_NEXTHOP_LOOKUP_MAX);
926 if (num_ifindex < 1) {
927 if (PIM_DEBUG_PIM_NHT)
928 zlog_warn(
929 "%s: could not find nexthop ifindex for address %s(%s)",
930 __func__, addr_str, pim->vrf->name);
931 return 0;
932 }
933
934 memset(&nbrs, 0, sizeof(nbrs));
935 memset(&ifps, 0, sizeof(ifps));
936
937 /*
938 * Look up all interfaces and neighbors,
939 * store for later usage
940 */
941 for (i = 0; i < num_ifindex; i++) {
942 ifps[i] = if_lookup_by_index(nexthop_tab[i].ifindex,
943 pim->vrf->vrf_id);
944 if (ifps[i]) {
945 nbrs[i] = pim_neighbor_find(
946 ifps[i], nexthop_tab[i].nexthop_addr.u.prefix4);
947 if (nbrs[i]
948 || pim_if_connected_to_source(ifps[i],
949 src->u.prefix4))
950 num_nbrs++;
951 }
952 }
953
954 // If PIM ECMP enable then choose ECMP path.
955 if (pim->ecmp_enable) {
956 uint32_t consider = num_ifindex;
957
958 if (neighbor_needed && num_nbrs < consider)
959 consider = num_nbrs;
960
961 if (consider == 0)
962 return 0;
963
964 hash_val = pim_compute_ecmp_hash(src, grp);
965 mod_val = hash_val % consider;
966 if (PIM_DEBUG_PIM_NHT_DETAIL)
967 zlog_debug("%s: hash_val %u mod_val %u", __func__,
968 hash_val, mod_val);
969 }
970
971 i = 0;
972 while (!found && (i < num_ifindex)) {
973 first_ifindex = nexthop_tab[i].ifindex;
974
975 ifp = ifps[i];
976 if (!ifp) {
977 if (PIM_DEBUG_PIM_NHT)
978 zlog_debug(
979 "%s %s: could not find interface for ifindex %d (address %s(%s))",
980 __FILE__, __func__, first_ifindex,
981 addr_str, pim->vrf->name);
982 if (i == mod_val)
983 mod_val++;
984 i++;
985 continue;
986 }
987
988 if (!ifp->info) {
989 if (PIM_DEBUG_PIM_NHT)
990 zlog_debug(
991 "%s: multicast not enabled on input interface %s(%s) (ifindex=%d, RPF for source %s)",
992 __func__, ifp->name, pim->vrf->name,
993 first_ifindex, addr_str);
994 if (i == mod_val)
995 mod_val++;
996 i++;
997 continue;
998 }
999 if (neighbor_needed
1000 && !pim_if_connected_to_source(ifp, src->u.prefix4)) {
1001 nbr = nbrs[i];
1002 if (PIM_DEBUG_PIM_NHT_DETAIL)
1003 zlog_debug("ifp name: %s(%s), pim nbr: %p",
1004 ifp->name, pim->vrf->name, nbr);
1005 if (!nbr && !if_is_loopback(ifp)) {
1006 if (i == mod_val)
1007 mod_val++;
1008 i++;
1009 if (PIM_DEBUG_PIM_NHT)
1010 zlog_debug(
1011 "%s: NBR not found on input interface %s(%s) (RPF for source %s)",
1012 __func__, ifp->name,
1013 pim->vrf->name, addr_str);
1014 continue;
1015 }
1016 }
1017
1018 if (i == mod_val) {
1019 if (PIM_DEBUG_PIM_NHT) {
1020 char nexthop_str[PREFIX_STRLEN];
1021
1022 pim_addr_dump("<nexthop?>",
1023 &nexthop_tab[i].nexthop_addr,
1024 nexthop_str, sizeof(nexthop_str));
1025 zlog_debug(
1026 "%s: found nhop %s for addr %s interface %s(%s) metric %d dist %d",
1027 __func__, nexthop_str, addr_str,
1028 ifp->name, pim->vrf->name,
1029 nexthop_tab[i].route_metric,
1030 nexthop_tab[i].protocol_distance);
1031 }
1032 /* update nexthop data */
1033 nexthop->interface = ifp;
1034 nexthop->mrib_nexthop_addr =
1035 nexthop_tab[i].nexthop_addr;
1036 nexthop->mrib_metric_preference =
1037 nexthop_tab[i].protocol_distance;
1038 nexthop->mrib_route_metric =
1039 nexthop_tab[i].route_metric;
1040 nexthop->last_lookup = src->u.prefix4;
1041 nexthop->last_lookup_time = pim_time_monotonic_usec();
1042 nexthop->nbr = nbr;
1043 found = 1;
1044 }
1045 i++;
1046 }
1047
1048 if (found)
1049 return 1;
1050 else
1051 return 0;
1052 }
1053
1054 int pim_ecmp_fib_lookup_if_vif_index(struct pim_instance *pim,
1055 struct prefix *src, struct prefix *grp)
1056 {
1057 struct pim_nexthop nhop;
1058 int vif_index;
1059 ifindex_t ifindex;
1060 char addr_str[PREFIX_STRLEN];
1061
1062 if (PIM_DEBUG_PIM_NHT)
1063 pim_inet4_dump("<addr?>", src->u.prefix4, addr_str,
1064 sizeof(addr_str));
1065
1066 memset(&nhop, 0, sizeof(nhop));
1067 if (!pim_ecmp_nexthop_lookup(pim, &nhop, src, grp, 1)) {
1068 if (PIM_DEBUG_PIM_NHT)
1069 zlog_debug(
1070 "%s: could not find nexthop ifindex for address %s(%s)",
1071 __func__, addr_str, pim->vrf->name);
1072 return -1;
1073 }
1074
1075 ifindex = nhop.interface->ifindex;
1076 if (PIM_DEBUG_PIM_NHT)
1077 zlog_debug(
1078 "%s: found nexthop ifindex=%d (interface %s(%s)) for address %s",
1079 __func__, ifindex,
1080 ifindex2ifname(ifindex, pim->vrf->vrf_id),
1081 pim->vrf->name, addr_str);
1082
1083 vif_index = pim_if_find_vifindex_by_ifindex(pim, ifindex);
1084
1085 if (vif_index < 0) {
1086 if (PIM_DEBUG_PIM_NHT) {
1087 zlog_debug(
1088 "%s: low vif_index=%d(%s) < 1 nexthop for address %s",
1089 __func__, vif_index, pim->vrf->name, addr_str);
1090 }
1091 return -2;
1092 }
1093
1094 return vif_index;
1095 }