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