]> git.proxmox.com Git - mirror_frr.git/blob - pimd/pim_nht.c
Merge pull request #4214 from donaldsharp/s_g_channel_deletion
[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 "pimd.h"
30 #include "pimd/pim_nht.h"
31 #include "log.h"
32 #include "pim_time.h"
33 #include "pim_oil.h"
34 #include "pim_ifchannel.h"
35 #include "pim_mroute.h"
36 #include "pim_zebra.h"
37 #include "pim_upstream.h"
38 #include "pim_join.h"
39 #include "pim_jp_agg.h"
40 #include "pim_zebra.h"
41 #include "pim_zlookup.h"
42 #include "pim_rp.h"
43
44 /**
45 * pim_sendmsg_zebra_rnh -- Format and send a nexthop register/Unregister
46 * command to Zebra.
47 */
48 void pim_sendmsg_zebra_rnh(struct pim_instance *pim, struct zclient *zclient,
49 struct pim_nexthop_cache *pnc, int command)
50 {
51 struct prefix *p;
52 int ret;
53
54 p = &(pnc->rpf.rpf_addr);
55 ret = zclient_send_rnh(zclient, command, p, false, pim->vrf_id);
56 if (ret < 0)
57 zlog_warn("sendmsg_nexthop: zclient_send_message() failed");
58
59 if (PIM_DEBUG_PIM_NHT) {
60 char buf[PREFIX2STR_BUFFER];
61 prefix2str(p, buf, sizeof(buf));
62 zlog_debug(
63 "%s: NHT %sregistered addr %s(%s) with Zebra ret:%d ",
64 __PRETTY_FUNCTION__,
65 (command == ZEBRA_NEXTHOP_REGISTER) ? " " : "de", buf,
66 pim->vrf->name, ret);
67 }
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 char buf1[64];
93
94 pnc = XCALLOC(MTYPE_PIM_NEXTHOP_CACHE,
95 sizeof(struct pim_nexthop_cache));
96 pnc->rpf.rpf_addr.family = rpf_addr->rpf_addr.family;
97 pnc->rpf.rpf_addr.prefixlen = rpf_addr->rpf_addr.prefixlen;
98 pnc->rpf.rpf_addr.u.prefix4.s_addr =
99 rpf_addr->rpf_addr.u.prefix4.s_addr;
100
101 pnc = hash_get(pim->rpf_hash, pnc, hash_alloc_intern);
102
103 pnc->rp_list = list_new();
104 pnc->rp_list->cmp = pim_rp_list_cmp;
105
106 snprintf(hash_name, 64, "PNC %s(%s) Upstream Hash",
107 prefix2str(&pnc->rpf.rpf_addr, buf1, 64), pim->vrf->name);
108 pnc->upstream_hash = hash_create_size(8192, pim_upstream_hash_key,
109 pim_upstream_equal, hash_name);
110
111 return pnc;
112 }
113
114 /*
115 * pim_find_or_track_nexthop
116 *
117 * This API is used to Register an address with Zebra
118 *
119 * 1 -> Success
120 * 0 -> Failure
121 */
122 int pim_find_or_track_nexthop(struct pim_instance *pim, struct prefix *addr,
123 struct pim_upstream *up, struct rp_info *rp,
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 char buf[PREFIX2STR_BUFFER];
144 prefix2str(addr, buf, sizeof(buf));
145 zlog_debug(
146 "%s: NHT cache and zebra notification added for %s(%s)",
147 __PRETTY_FUNCTION__, buf, pim->vrf->name);
148 }
149 }
150
151 if (rp != NULL) {
152 ch_node = listnode_lookup(pnc->rp_list, rp);
153 if (ch_node == NULL)
154 listnode_add_sort(pnc->rp_list, rp);
155 }
156
157 if (up != NULL)
158 hash_get(pnc->upstream_hash, up, hash_alloc_intern);
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 {
172 struct pim_nexthop_cache *pnc = NULL;
173 struct pim_nexthop_cache lookup;
174 struct zclient *zclient = NULL;
175 struct listnode *upnode = 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 for (ALL_LIST_ELEMENTS_RO(pim->upstream_list, upnode,
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 (PIM_DEBUG_PIM_NHT) {
212 char buf[PREFIX_STRLEN];
213 prefix2str(addr, buf, sizeof buf);
214 zlog_debug(
215 "%s: NHT %s(%s) rp_list count:%d upstream count:%ld",
216 __PRETTY_FUNCTION__, buf, pim->vrf->name,
217 pnc->rp_list->count, pnc->upstream_hash->count);
218 }
219
220 if (pnc->rp_list->count == 0
221 && pnc->upstream_hash->count == 0) {
222 pim_sendmsg_zebra_rnh(pim, zclient, pnc,
223 ZEBRA_NEXTHOP_UNREGISTER);
224
225 list_delete(&pnc->rp_list);
226 hash_free(pnc->upstream_hash);
227
228 hash_release(pim->rpf_hash, pnc);
229 if (pnc->nexthop)
230 nexthops_free(pnc->nexthop);
231 XFREE(MTYPE_PIM_NEXTHOP_CACHE, pnc);
232 }
233 }
234 }
235
236 void pim_rp_nexthop_del(struct rp_info *rp_info)
237 {
238 rp_info->rp.source_nexthop.interface = NULL;
239 rp_info->rp.source_nexthop.mrib_nexthop_addr.u.prefix4.s_addr =
240 PIM_NET_INADDR_ANY;
241 rp_info->rp.source_nexthop.mrib_metric_preference =
242 router->infinite_assert_metric.metric_preference;
243 rp_info->rp.source_nexthop.mrib_route_metric =
244 router->infinite_assert_metric.route_metric;
245 }
246
247 /* Update RP nexthop info based on Nexthop update received from Zebra.*/
248 static void pim_update_rp_nh(struct pim_instance *pim,
249 struct pim_nexthop_cache *pnc)
250 {
251 struct listnode *node = NULL;
252 struct rp_info *rp_info = NULL;
253
254 /*Traverse RP list and update each RP Nexthop info */
255 for (ALL_LIST_ELEMENTS_RO(pnc->rp_list, node, rp_info)) {
256 if (rp_info->rp.rpf_addr.u.prefix4.s_addr == INADDR_NONE)
257 continue;
258
259 // Compute PIM RPF using cached nexthop
260 if (!pim_ecmp_nexthop_lookup(pim, &rp_info->rp.source_nexthop,
261 &rp_info->rp.rpf_addr,
262 &rp_info->group, 1))
263 pim_rp_nexthop_del(rp_info);
264 }
265 }
266
267 /* Update Upstream nexthop info based on Nexthop update received from Zebra.*/
268 static int pim_update_upstream_nh_helper(struct hash_bucket *bucket, void *arg)
269 {
270 struct pim_instance *pim = (struct pim_instance *)arg;
271 struct pim_upstream *up = (struct pim_upstream *)bucket->data;
272 int vif_index = 0;
273
274 enum pim_rpf_result rpf_result;
275 struct pim_rpf old;
276
277 old.source_nexthop.interface = up->rpf.source_nexthop.interface;
278 rpf_result = pim_rpf_update(pim, up, &old, 0);
279 if (rpf_result == PIM_RPF_FAILURE) {
280 pim_upstream_rpf_clear(pim, up);
281 return HASHWALK_CONTINUE;
282 }
283
284 /* update kernel multicast forwarding cache (MFC) */
285 if (up->rpf.source_nexthop.interface) {
286 ifindex_t ifindex = up->rpf.source_nexthop.interface->ifindex;
287
288 vif_index = pim_if_find_vifindex_by_ifindex(pim, ifindex);
289 /* Pass Current selected NH vif index to mroute download
290 */
291 if (vif_index)
292 pim_scan_individual_oil(up->channel_oil, vif_index);
293 else {
294 if (PIM_DEBUG_PIM_NHT)
295 zlog_debug(
296 "%s: NHT upstream %s channel_oil IIF %s vif_index is not valid",
297 __PRETTY_FUNCTION__, up->sg_str,
298 up->rpf.source_nexthop.interface->name);
299 }
300 }
301
302 if (rpf_result == PIM_RPF_CHANGED)
303 pim_zebra_upstream_rpf_changed(pim, up, &old);
304
305
306 if (PIM_DEBUG_PIM_NHT) {
307 zlog_debug("%s: NHT upstream %s(%s) old ifp %s new ifp %s",
308 __PRETTY_FUNCTION__, up->sg_str, pim->vrf->name,
309 old.source_nexthop.interface
310 ? old.source_nexthop.interface->name : "Unknwon",
311 up->rpf.source_nexthop.interface->name);
312 }
313
314 return HASHWALK_CONTINUE;
315 }
316
317 static int pim_update_upstream_nh(struct pim_instance *pim,
318 struct pim_nexthop_cache *pnc)
319 {
320 hash_walk(pnc->upstream_hash, pim_update_upstream_nh_helper, pim);
321
322 pim_zebra_update_all_interfaces(pim);
323
324 return 0;
325 }
326
327 uint32_t pim_compute_ecmp_hash(struct prefix *src, struct prefix *grp)
328 {
329 uint32_t hash_val;
330 uint32_t s = 0, g = 0;
331
332 if ((!src))
333 return 0;
334
335 switch (src->family) {
336 case AF_INET: {
337 s = src->u.prefix4.s_addr;
338 s = s == 0 ? 1 : s;
339 if (grp)
340 g = grp->u.prefix4.s_addr;
341 } break;
342 default:
343 break;
344 }
345
346 hash_val = jhash_2words(g, s, 101);
347 return hash_val;
348 }
349
350 static int pim_ecmp_nexthop_search(struct pim_instance *pim,
351 struct pim_nexthop_cache *pnc,
352 struct pim_nexthop *nexthop,
353 struct prefix *src, struct prefix *grp,
354 int neighbor_needed)
355 {
356 struct pim_neighbor *nbrs[MULTIPATH_NUM], *nbr = NULL;
357 struct interface *ifps[MULTIPATH_NUM];
358 struct nexthop *nh_node = NULL;
359 ifindex_t first_ifindex;
360 struct interface *ifp = NULL;
361 uint32_t hash_val = 0, mod_val = 0;
362 uint8_t nh_iter = 0, found = 0;
363 uint32_t i, num_nbrs = 0;
364
365 if (!pnc || !pnc->nexthop_num || !nexthop)
366 return 0;
367
368 memset(&nbrs, 0, sizeof(nbrs));
369 memset(&ifps, 0, sizeof(ifps));
370
371 // Current Nexthop is VALID, check to stay on the current path.
372 if (nexthop->interface && nexthop->interface->info
373 && nexthop->mrib_nexthop_addr.u.prefix4.s_addr
374 != PIM_NET_INADDR_ANY) {
375 /* User configured knob to explicitly switch
376 to new path is disabled or current path
377 metric is less than nexthop update.
378 */
379
380 if (pim->ecmp_rebalance_enable == 0) {
381 uint8_t curr_route_valid = 0;
382 // Check if current nexthop is present in new updated
383 // Nexthop list.
384 // If the current nexthop is not valid, candidate to
385 // choose new Nexthop.
386 for (nh_node = pnc->nexthop; nh_node;
387 nh_node = nh_node->next) {
388 curr_route_valid = (nexthop->interface->ifindex
389 == nh_node->ifindex);
390 if (curr_route_valid)
391 break;
392 }
393
394 if (curr_route_valid
395 && !pim_if_connected_to_source(nexthop->interface,
396 src->u.prefix4)) {
397 nbr = pim_neighbor_find(
398 nexthop->interface,
399 nexthop->mrib_nexthop_addr.u.prefix4);
400 if (!nbr
401 && !if_is_loopback(nexthop->interface)) {
402 if (PIM_DEBUG_PIM_NHT)
403 zlog_debug(
404 "%s: current nexthop does not have nbr ",
405 __PRETTY_FUNCTION__);
406 } else {
407 if (PIM_DEBUG_PIM_NHT) {
408 char src_str[INET_ADDRSTRLEN];
409 pim_inet4_dump("<addr?>",
410 src->u.prefix4,
411 src_str,
412 sizeof(src_str));
413 char grp_str[INET_ADDRSTRLEN];
414 pim_inet4_dump("<addr?>",
415 grp->u.prefix4,
416 grp_str,
417 sizeof(grp_str));
418 zlog_debug(
419 "%s: (%s,%s)(%s) current nexthop %s is valid, skipping new path selection",
420 __PRETTY_FUNCTION__,
421 src_str, grp_str,
422 pim->vrf->name,
423 nexthop->interface->name);
424 }
425 return 1;
426 }
427 }
428 }
429 }
430
431 /*
432 * Look up all interfaces and neighbors,
433 * store for later usage
434 */
435 for (nh_node = pnc->nexthop, i = 0; nh_node;
436 nh_node = nh_node->next, i++) {
437 ifps[i] = if_lookup_by_index(nh_node->ifindex, pim->vrf_id);
438 if (ifps[i]) {
439 nbrs[i] = pim_neighbor_find(ifps[i],
440 nh_node->gate.ipv4);
441 if (nbrs[i] || pim_if_connected_to_source(ifps[i],
442
443 src->u.prefix4))
444 num_nbrs++;
445 }
446 }
447 if (pim->ecmp_enable) {
448 uint32_t consider = pnc->nexthop_num;
449
450 if (neighbor_needed && num_nbrs < consider)
451 consider = num_nbrs;
452
453 if (consider == 0)
454 return 0;
455
456 // PIM ECMP flag is enable then choose ECMP path.
457 hash_val = pim_compute_ecmp_hash(src, grp);
458 mod_val = hash_val % consider;
459 }
460
461 for (nh_node = pnc->nexthop; nh_node && (found == 0);
462 nh_node = nh_node->next) {
463 first_ifindex = nh_node->ifindex;
464 ifp = ifps[nh_iter];
465 if (!ifp) {
466 if (PIM_DEBUG_PIM_NHT) {
467 char addr_str[INET_ADDRSTRLEN];
468 pim_inet4_dump("<addr?>", src->u.prefix4,
469 addr_str, sizeof(addr_str));
470 zlog_debug(
471 "%s %s: could not find interface for ifindex %d (address %s(%s))",
472 __FILE__, __PRETTY_FUNCTION__,
473 first_ifindex, addr_str,
474 pim->vrf->name);
475 }
476 if (nh_iter == mod_val)
477 mod_val++; // Select nexthpath
478 nh_iter++;
479 continue;
480 }
481 if (!ifp->info) {
482 if (PIM_DEBUG_PIM_NHT) {
483 char addr_str[INET_ADDRSTRLEN];
484 pim_inet4_dump("<addr?>", src->u.prefix4,
485 addr_str, sizeof(addr_str));
486 zlog_debug(
487 "%s: multicast not enabled on input interface %s(%s) (ifindex=%d, RPF for source %s)",
488 __PRETTY_FUNCTION__, ifp->name,
489 pim->vrf->name, first_ifindex,
490 addr_str);
491 }
492 if (nh_iter == mod_val)
493 mod_val++; // Select nexthpath
494 nh_iter++;
495 continue;
496 }
497
498 if (neighbor_needed
499 && !pim_if_connected_to_source(ifp, src->u.prefix4)) {
500 nbr = nbrs[nh_iter];
501 if (!nbr && !if_is_loopback(ifp)) {
502 if (PIM_DEBUG_PIM_NHT)
503 zlog_debug(
504 "%s: pim nbr not found on input interface %s(%s)",
505 __PRETTY_FUNCTION__, ifp->name,
506 pim->vrf->name);
507 if (nh_iter == mod_val)
508 mod_val++; // Select nexthpath
509 nh_iter++;
510 continue;
511 }
512 }
513
514 if (nh_iter == mod_val) {
515 nexthop->interface = ifp;
516 nexthop->mrib_nexthop_addr.family = AF_INET;
517 nexthop->mrib_nexthop_addr.prefixlen = IPV4_MAX_BITLEN;
518 nexthop->mrib_nexthop_addr.u.prefix4 =
519 nh_node->gate.ipv4;
520 nexthop->mrib_metric_preference = pnc->distance;
521 nexthop->mrib_route_metric = pnc->metric;
522 nexthop->last_lookup = src->u.prefix4;
523 nexthop->last_lookup_time = pim_time_monotonic_usec();
524 nexthop->nbr = nbr;
525 found = 1;
526 if (PIM_DEBUG_PIM_NHT) {
527 char buf[INET_ADDRSTRLEN];
528 char buf2[INET_ADDRSTRLEN];
529 char buf3[INET_ADDRSTRLEN];
530 pim_inet4_dump("<src?>", src->u.prefix4, buf2,
531 sizeof(buf2));
532 pim_inet4_dump("<grp?>", grp->u.prefix4, buf3,
533 sizeof(buf3));
534 pim_inet4_dump(
535 "<rpf?>",
536 nexthop->mrib_nexthop_addr.u.prefix4,
537 buf, sizeof(buf));
538 zlog_debug(
539 "%s: (%s,%s)(%s) selected nhop interface %s addr %s mod_val %u iter %d ecmp %d",
540 __PRETTY_FUNCTION__, buf2, buf3,
541 pim->vrf->name, ifp->name, buf, mod_val,
542 nh_iter, pim->ecmp_enable);
543 }
544 }
545 nh_iter++;
546 }
547
548 if (found)
549 return 1;
550 else
551 return 0;
552 }
553
554 /* This API is used to parse Registered address nexthop update coming from Zebra
555 */
556 int pim_parse_nexthop_update(ZAPI_CALLBACK_ARGS)
557 {
558 struct nexthop *nexthop;
559 struct nexthop *nhlist_head = NULL;
560 struct nexthop *nhlist_tail = NULL;
561 int i;
562 struct pim_rpf rpf;
563 struct pim_nexthop_cache *pnc = NULL;
564 struct pim_neighbor *nbr = NULL;
565 struct interface *ifp = NULL;
566 struct interface *ifp1 = NULL;
567 struct vrf *vrf = vrf_lookup_by_id(vrf_id);
568 struct pim_instance *pim;
569 struct zapi_route nhr;
570
571 if (!vrf)
572 return 0;
573 pim = vrf->info;
574
575 if (!zapi_nexthop_update_decode(zclient->ibuf, &nhr)) {
576 if (PIM_DEBUG_PIM_NHT)
577 zlog_debug(
578 "%s: Decode of nexthop update from zebra failed",
579 __PRETTY_FUNCTION__);
580 return 0;
581 }
582
583 if (cmd == ZEBRA_NEXTHOP_UPDATE) {
584 prefix_copy(&rpf.rpf_addr, &nhr.prefix);
585 pnc = pim_nexthop_cache_find(pim, &rpf);
586 if (!pnc) {
587 if (PIM_DEBUG_PIM_NHT) {
588 char buf[PREFIX2STR_BUFFER];
589 prefix2str(&rpf.rpf_addr, buf, sizeof(buf));
590 zlog_debug(
591 "%s: Skipping NHT update, addr %s is not in local cached DB.",
592 __PRETTY_FUNCTION__, buf);
593 }
594 return 0;
595 }
596 } else {
597 /*
598 * We do not currently handle ZEBRA_IMPORT_CHECK_UPDATE
599 */
600 return 0;
601 }
602
603 pnc->last_update = pim_time_monotonic_usec();
604
605 if (nhr.nexthop_num) {
606 pnc->nexthop_num = 0; // Only increment for pim enabled rpf.
607
608 for (i = 0; i < nhr.nexthop_num; i++) {
609 nexthop = nexthop_from_zapi_nexthop(&nhr.nexthops[i]);
610 switch (nexthop->type) {
611 case NEXTHOP_TYPE_IPV4:
612 case NEXTHOP_TYPE_IPV4_IFINDEX:
613 case NEXTHOP_TYPE_IPV6:
614 case NEXTHOP_TYPE_BLACKHOLE:
615 break;
616 case NEXTHOP_TYPE_IFINDEX:
617 /*
618 * Connected route (i.e. no nexthop), use
619 * RPF address from nexthop cache (i.e.
620 * destination) as PIM nexthop.
621 */
622 nexthop->type = NEXTHOP_TYPE_IPV4_IFINDEX;
623 nexthop->gate.ipv4 =
624 pnc->rpf.rpf_addr.u.prefix4;
625 break;
626 case NEXTHOP_TYPE_IPV6_IFINDEX:
627 ifp1 = if_lookup_by_index(nexthop->ifindex,
628 pim->vrf_id);
629 nbr = pim_neighbor_find_if(ifp1);
630 /* Overwrite with Nbr address as NH addr */
631 if (nbr)
632 nexthop->gate.ipv4 = nbr->source_addr;
633 else {
634 // Mark nexthop address to 0 until PIM
635 // Nbr is resolved.
636 nexthop->gate.ipv4.s_addr =
637 PIM_NET_INADDR_ANY;
638 }
639
640 break;
641 }
642
643 ifp = if_lookup_by_index(nexthop->ifindex, pim->vrf_id);
644 if (!ifp) {
645 if (PIM_DEBUG_PIM_NHT) {
646 char buf[NEXTHOP_STRLEN];
647 zlog_debug(
648 "%s: could not find interface for ifindex %d(%s) (addr %s)",
649 __PRETTY_FUNCTION__,
650 nexthop->ifindex,
651 pim->vrf->name,
652 nexthop2str(nexthop, buf,
653 sizeof(buf)));
654 }
655 nexthop_free(nexthop);
656 continue;
657 }
658
659 if (PIM_DEBUG_PIM_NHT) {
660 char p_str[PREFIX2STR_BUFFER];
661
662 prefix2str(&nhr.prefix, p_str, sizeof(p_str));
663 zlog_debug(
664 "%s: NHT addr %s(%s) %d-nhop via %s(%s) type %d distance:%u metric:%u ",
665 __PRETTY_FUNCTION__, p_str,
666 pim->vrf->name, i + 1,
667 inet_ntoa(nexthop->gate.ipv4),
668 ifp->name, nexthop->type, nhr.distance,
669 nhr.metric);
670 }
671
672 if (!ifp->info) {
673 if (PIM_DEBUG_PIM_NHT) {
674 char buf[NEXTHOP_STRLEN];
675
676 zlog_debug(
677 "%s: multicast not enabled on input interface %s(%s) (ifindex=%d, addr %s)",
678 __PRETTY_FUNCTION__, ifp->name,
679 pim->vrf->name,
680 nexthop->ifindex,
681 nexthop2str(nexthop, buf,
682 sizeof(buf)));
683 }
684 nexthop_free(nexthop);
685 continue;
686 }
687
688 if (nhlist_tail) {
689 nhlist_tail->next = nexthop;
690 nhlist_tail = nexthop;
691 } else {
692 nhlist_tail = nexthop;
693 nhlist_head = nexthop;
694 }
695 // Only keep track of nexthops which are PIM enabled.
696 pnc->nexthop_num++;
697 }
698 /* Reset existing pnc->nexthop before assigning new list */
699 nexthops_free(pnc->nexthop);
700 pnc->nexthop = nhlist_head;
701 if (pnc->nexthop_num) {
702 pnc->flags |= PIM_NEXTHOP_VALID;
703 pnc->distance = nhr.distance;
704 pnc->metric = nhr.metric;
705 }
706 } else {
707 pnc->flags &= ~PIM_NEXTHOP_VALID;
708 pnc->nexthop_num = nhr.nexthop_num;
709 nexthops_free(pnc->nexthop);
710 pnc->nexthop = NULL;
711 }
712 SET_FLAG(pnc->flags, PIM_NEXTHOP_ANSWER_RECEIVED);
713
714 if (PIM_DEBUG_PIM_NHT) {
715 char buf[PREFIX2STR_BUFFER];
716 prefix2str(&nhr.prefix, buf, sizeof(buf));
717 zlog_debug(
718 "%s: NHT Update for %s(%s) num_nh %d num_pim_nh %d vrf:%u up %ld rp %d",
719 __PRETTY_FUNCTION__, buf, pim->vrf->name,
720 nhr.nexthop_num, pnc->nexthop_num, vrf_id,
721 pnc->upstream_hash->count, listcount(pnc->rp_list));
722 }
723
724 pim_rpf_set_refresh_time(pim);
725
726 if (listcount(pnc->rp_list))
727 pim_update_rp_nh(pim, pnc);
728 if (pnc->upstream_hash->count)
729 pim_update_upstream_nh(pim, pnc);
730
731 return 0;
732 }
733
734 int pim_ecmp_nexthop_lookup(struct pim_instance *pim,
735 struct pim_nexthop *nexthop, struct prefix *src,
736 struct prefix *grp, int neighbor_needed)
737 {
738 struct pim_nexthop_cache *pnc;
739 struct pim_zlookup_nexthop nexthop_tab[MULTIPATH_NUM];
740 struct pim_neighbor *nbrs[MULTIPATH_NUM], *nbr = NULL;
741 struct pim_rpf rpf;
742 int num_ifindex;
743 struct interface *ifps[MULTIPATH_NUM], *ifp;
744 int first_ifindex;
745 int found = 0;
746 uint8_t i = 0;
747 uint32_t hash_val = 0, mod_val = 0;
748 uint32_t num_nbrs = 0;
749 char addr_str[PREFIX_STRLEN];
750
751 if (PIM_DEBUG_PIM_NHT) {
752 pim_inet4_dump("<addr?>", src->u.prefix4, addr_str,
753 sizeof(addr_str));
754 zlog_debug("%s: Looking up: %s(%s), last lookup time: %lld",
755 __PRETTY_FUNCTION__, addr_str, pim->vrf->name,
756 nexthop->last_lookup_time);
757 }
758
759 memset(&rpf, 0, sizeof(struct pim_rpf));
760 rpf.rpf_addr.family = AF_INET;
761 rpf.rpf_addr.prefixlen = IPV4_MAX_BITLEN;
762 rpf.rpf_addr.u.prefix4 = src->u.prefix4;
763
764 pnc = pim_nexthop_cache_find(pim, &rpf);
765 if (pnc) {
766 if (CHECK_FLAG(pnc->flags, PIM_NEXTHOP_ANSWER_RECEIVED))
767 return pim_ecmp_nexthop_search(pim, pnc, nexthop, src, grp,
768 neighbor_needed);
769 }
770
771 memset(nexthop_tab, 0,
772 sizeof(struct pim_zlookup_nexthop) * MULTIPATH_NUM);
773 num_ifindex =
774 zclient_lookup_nexthop(pim, nexthop_tab, MULTIPATH_NUM,
775 src->u.prefix4, PIM_NEXTHOP_LOOKUP_MAX);
776 if (num_ifindex < 1) {
777 if (PIM_DEBUG_PIM_NHT)
778 zlog_warn(
779 "%s: could not find nexthop ifindex for address %s(%s)",
780 __PRETTY_FUNCTION__, addr_str, pim->vrf->name);
781 return 0;
782 }
783
784 memset(&nbrs, 0, sizeof(nbrs));
785 memset(&ifps, 0, sizeof(ifps));
786
787 /*
788 * Look up all interfaces and neighbors,
789 * store for later usage
790 */
791 for (i = 0; i < num_ifindex; i++) {
792 ifps[i] = if_lookup_by_index(nexthop_tab[i].ifindex,
793 pim->vrf_id);
794 if (ifps[i]) {
795 nbrs[i] = pim_neighbor_find(
796 ifps[i], nexthop_tab[i].nexthop_addr.u.prefix4);
797 if (nbrs[i]
798 || pim_if_connected_to_source(ifps[i],
799 src->u.prefix4))
800 num_nbrs++;
801 }
802 }
803
804 // If PIM ECMP enable then choose ECMP path.
805 if (pim->ecmp_enable) {
806 uint32_t consider = num_ifindex;
807
808 if (neighbor_needed && num_nbrs < consider)
809 consider = num_nbrs;
810
811 if (consider == 0)
812 return 0;
813
814 hash_val = pim_compute_ecmp_hash(src, grp);
815 mod_val = hash_val % consider;
816 if (PIM_DEBUG_PIM_NHT_DETAIL)
817 zlog_debug("%s: hash_val %u mod_val %u",
818 __PRETTY_FUNCTION__, hash_val, mod_val);
819 }
820
821 i = 0;
822 while (!found && (i < num_ifindex)) {
823 first_ifindex = nexthop_tab[i].ifindex;
824
825 ifp = ifps[i];
826 if (!ifp) {
827 if (PIM_DEBUG_PIM_NHT)
828 zlog_debug(
829 "%s %s: could not find interface for ifindex %d (address %s(%s))",
830 __FILE__, __PRETTY_FUNCTION__,
831 first_ifindex, addr_str,
832 pim->vrf->name);
833 if (i == mod_val)
834 mod_val++;
835 i++;
836 continue;
837 }
838
839 if (!ifp->info) {
840 if (PIM_DEBUG_PIM_NHT)
841 zlog_debug(
842 "%s: multicast not enabled on input interface %s(%s) (ifindex=%d, RPF for source %s)",
843 __PRETTY_FUNCTION__, ifp->name,
844 pim->vrf->name, first_ifindex,
845 addr_str);
846 if (i == mod_val)
847 mod_val++;
848 i++;
849 continue;
850 }
851 if (neighbor_needed
852 && !pim_if_connected_to_source(ifp, src->u.prefix4)) {
853 nbr = nbrs[i];
854 if (PIM_DEBUG_PIM_NHT_DETAIL)
855 zlog_debug("ifp name: %s(%s), pim nbr: %p",
856 ifp->name, pim->vrf->name, nbr);
857 if (!nbr && !if_is_loopback(ifp)) {
858 if (i == mod_val)
859 mod_val++;
860 i++;
861 if (PIM_DEBUG_PIM_NHT)
862 zlog_debug(
863 "%s: NBR not found on input interface %s(%s) (RPF for source %s)",
864 __PRETTY_FUNCTION__, ifp->name,
865 pim->vrf->name, addr_str);
866 continue;
867 }
868 }
869
870 if (i == mod_val) {
871 if (PIM_DEBUG_PIM_NHT) {
872 char nexthop_str[PREFIX_STRLEN];
873
874 pim_addr_dump("<nexthop?>",
875 &nexthop_tab[i].nexthop_addr,
876 nexthop_str, sizeof(nexthop_str));
877 zlog_debug(
878 "%s: found nhop %s for addr %s interface %s(%s) metric %d dist %d",
879 __PRETTY_FUNCTION__, nexthop_str,
880 addr_str, ifp->name, pim->vrf->name,
881 nexthop_tab[i].route_metric,
882 nexthop_tab[i].protocol_distance);
883 }
884 /* update nexthop data */
885 nexthop->interface = ifp;
886 nexthop->mrib_nexthop_addr =
887 nexthop_tab[i].nexthop_addr;
888 nexthop->mrib_metric_preference =
889 nexthop_tab[i].protocol_distance;
890 nexthop->mrib_route_metric =
891 nexthop_tab[i].route_metric;
892 nexthop->last_lookup = src->u.prefix4;
893 nexthop->last_lookup_time = pim_time_monotonic_usec();
894 nexthop->nbr = nbr;
895 found = 1;
896 }
897 i++;
898 }
899
900 if (found)
901 return 1;
902 else
903 return 0;
904 }
905
906 int pim_ecmp_fib_lookup_if_vif_index(struct pim_instance *pim,
907 struct prefix *src, struct prefix *grp)
908 {
909 struct pim_nexthop nhop;
910 int vif_index;
911 ifindex_t ifindex;
912 char addr_str[PREFIX_STRLEN];
913
914 if (PIM_DEBUG_PIM_NHT)
915 pim_inet4_dump("<addr?>", src->u.prefix4, addr_str,
916 sizeof(addr_str));
917
918 memset(&nhop, 0, sizeof(nhop));
919 if (!pim_ecmp_nexthop_lookup(pim, &nhop, src, grp, 0)) {
920 if (PIM_DEBUG_PIM_NHT)
921 zlog_debug(
922 "%s: could not find nexthop ifindex for address %s(%s)",
923 __PRETTY_FUNCTION__, addr_str, pim->vrf->name);
924 return -1;
925 }
926
927 ifindex = nhop.interface->ifindex;
928 if (PIM_DEBUG_PIM_NHT)
929 zlog_debug(
930 "%s: found nexthop ifindex=%d (interface %s(%s)) for address %s",
931 __PRETTY_FUNCTION__, ifindex,
932 ifindex2ifname(ifindex, pim->vrf_id),
933 pim->vrf->name, addr_str);
934
935 vif_index = pim_if_find_vifindex_by_ifindex(pim, ifindex);
936
937 if (vif_index < 0) {
938 if (PIM_DEBUG_PIM_NHT) {
939 zlog_debug(
940 "%s: low vif_index=%d(%s) < 1 nexthop for address %s",
941 __PRETTY_FUNCTION__, vif_index, pim->vrf->name,
942 addr_str);
943 }
944 return -2;
945 }
946
947 return vif_index;
948 }