]> git.proxmox.com Git - mirror_frr.git/blob - pimd/pim_nht.c
Merge pull request #2422 from pacovn/Coverity_23201_Dereference_before_null_check
[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
43 /**
44 * pim_sendmsg_zebra_rnh -- Format and send a nexthop register/Unregister
45 * command to Zebra.
46 */
47 void pim_sendmsg_zebra_rnh(struct pim_instance *pim, struct zclient *zclient,
48 struct pim_nexthop_cache *pnc, int command)
49 {
50 struct prefix *p;
51 int ret;
52
53 p = &(pnc->rpf.rpf_addr);
54 ret = zclient_send_rnh(zclient, command, p, false, pim->vrf_id);
55 if (ret < 0)
56 zlog_warn("sendmsg_nexthop: zclient_send_message() failed");
57
58 if (PIM_DEBUG_PIM_NHT) {
59 char buf[PREFIX2STR_BUFFER];
60 prefix2str(p, buf, sizeof(buf));
61 zlog_debug(
62 "%s: NHT %sregistered addr %s(%s) with Zebra ret:%d ",
63 __PRETTY_FUNCTION__,
64 (command == ZEBRA_NEXTHOP_REGISTER) ? " " : "de", buf,
65 pim->vrf->name, ret);
66 }
67
68 return;
69 }
70
71 struct pim_nexthop_cache *pim_nexthop_cache_find(struct pim_instance *pim,
72 struct pim_rpf *rpf)
73 {
74 struct pim_nexthop_cache *pnc = NULL;
75 struct pim_nexthop_cache lookup;
76
77 lookup.rpf.rpf_addr.family = rpf->rpf_addr.family;
78 lookup.rpf.rpf_addr.prefixlen = rpf->rpf_addr.prefixlen;
79 lookup.rpf.rpf_addr.u.prefix4.s_addr = rpf->rpf_addr.u.prefix4.s_addr;
80
81 pnc = hash_lookup(pim->rpf_hash, &lookup);
82
83 return pnc;
84 }
85
86 static struct pim_nexthop_cache *pim_nexthop_cache_add(struct pim_instance *pim,
87 struct pim_rpf *rpf_addr)
88 {
89 struct pim_nexthop_cache *pnc;
90 char hash_name[64];
91 char buf1[64];
92
93 pnc = XCALLOC(MTYPE_PIM_NEXTHOP_CACHE,
94 sizeof(struct pim_nexthop_cache));
95 if (!pnc) {
96 zlog_err("%s: NHT PIM XCALLOC failure ", __PRETTY_FUNCTION__);
97 return NULL;
98 }
99 pnc->rpf.rpf_addr.family = rpf_addr->rpf_addr.family;
100 pnc->rpf.rpf_addr.prefixlen = rpf_addr->rpf_addr.prefixlen;
101 pnc->rpf.rpf_addr.u.prefix4.s_addr =
102 rpf_addr->rpf_addr.u.prefix4.s_addr;
103
104 pnc = hash_get(pim->rpf_hash, pnc, hash_alloc_intern);
105
106 pnc->rp_list = list_new();
107 pnc->rp_list->cmp = pim_rp_list_cmp;
108
109 snprintf(hash_name, 64, "PNC %s(%s) Upstream Hash",
110 prefix2str(&pnc->rpf.rpf_addr, buf1, 64), pim->vrf->name);
111 pnc->upstream_hash = hash_create_size(8192, pim_upstream_hash_key,
112 pim_upstream_equal, hash_name);
113
114 return pnc;
115 }
116
117 /*
118 * pim_find_or_track_nexthop
119 *
120 * This API is used to Register an address with Zebra
121 *
122 * 1 -> Success
123 * 0 -> Failure
124 */
125 int pim_find_or_track_nexthop(struct pim_instance *pim, struct prefix *addr,
126 struct pim_upstream *up, struct rp_info *rp,
127 struct pim_nexthop_cache *out_pnc)
128 {
129 struct pim_nexthop_cache *pnc = NULL;
130 struct pim_rpf rpf;
131 struct listnode *ch_node = NULL;
132 struct zclient *zclient = NULL;
133
134 zclient = pim_zebra_zclient_get();
135 memset(&rpf, 0, sizeof(struct pim_rpf));
136 rpf.rpf_addr.family = addr->family;
137 rpf.rpf_addr.prefixlen = addr->prefixlen;
138 rpf.rpf_addr.u.prefix4 = addr->u.prefix4;
139
140 pnc = pim_nexthop_cache_find(pim, &rpf);
141 if (!pnc) {
142 pnc = pim_nexthop_cache_add(pim, &rpf);
143 if (!pnc) {
144 char rpf_str[PREFIX_STRLEN];
145 pim_addr_dump("<nht-pnc?>", addr, rpf_str,
146 sizeof(rpf_str));
147 zlog_warn("%s: pnc node allocation failed. addr %s ",
148 __PRETTY_FUNCTION__, rpf_str);
149 return 0;
150 }
151 pim_sendmsg_zebra_rnh(pim, zclient, pnc,
152 ZEBRA_NEXTHOP_REGISTER);
153 if (PIM_DEBUG_PIM_NHT) {
154 char buf[PREFIX2STR_BUFFER];
155 prefix2str(addr, buf, sizeof(buf));
156 zlog_debug(
157 "%s: NHT cache and zebra notification added for %s(%s)",
158 __PRETTY_FUNCTION__, buf, pim->vrf->name);
159 }
160 }
161
162 if (rp != NULL) {
163 ch_node = listnode_lookup(pnc->rp_list, rp);
164 if (ch_node == NULL)
165 listnode_add_sort(pnc->rp_list, rp);
166 }
167
168 if (up != NULL)
169 hash_get(pnc->upstream_hash, up, hash_alloc_intern);
170
171 if (pnc && CHECK_FLAG(pnc->flags, PIM_NEXTHOP_VALID)) {
172 memcpy(out_pnc, pnc, sizeof(struct pim_nexthop_cache));
173 return 1;
174 }
175
176 return 0;
177 }
178
179 void pim_delete_tracked_nexthop(struct pim_instance *pim, struct prefix *addr,
180 struct pim_upstream *up, struct rp_info *rp)
181 {
182 struct pim_nexthop_cache *pnc = NULL;
183 struct pim_nexthop_cache lookup;
184 struct zclient *zclient = NULL;
185
186 zclient = pim_zebra_zclient_get();
187
188 /* Remove from RPF hash if it is the last entry */
189 lookup.rpf.rpf_addr = *addr;
190 pnc = hash_lookup(pim->rpf_hash, &lookup);
191 if (pnc) {
192 if (rp)
193 listnode_delete(pnc->rp_list, rp);
194 if (up)
195 hash_release(pnc->upstream_hash, up);
196
197 if (PIM_DEBUG_PIM_NHT) {
198 char buf[PREFIX_STRLEN];
199 prefix2str(addr, buf, sizeof buf);
200 zlog_debug(
201 "%s: NHT %s(%s) rp_list count:%d upstream count:%ld",
202 __PRETTY_FUNCTION__, buf, pim->vrf->name,
203 pnc->rp_list->count, pnc->upstream_hash->count);
204 }
205
206 if (pnc->rp_list->count == 0
207 && pnc->upstream_hash->count == 0) {
208 pim_sendmsg_zebra_rnh(pim, zclient, pnc,
209 ZEBRA_NEXTHOP_UNREGISTER);
210
211 list_delete_and_null(&pnc->rp_list);
212 hash_free(pnc->upstream_hash);
213
214 hash_release(pim->rpf_hash, pnc);
215 if (pnc->nexthop)
216 nexthops_free(pnc->nexthop);
217 XFREE(MTYPE_PIM_NEXTHOP_CACHE, pnc);
218 }
219 }
220 }
221
222 /* Update RP nexthop info based on Nexthop update received from Zebra.*/
223 static void pim_update_rp_nh(struct pim_instance *pim,
224 struct pim_nexthop_cache *pnc)
225 {
226 struct listnode *node = NULL;
227 struct rp_info *rp_info = NULL;
228
229 /*Traverse RP list and update each RP Nexthop info */
230 for (ALL_LIST_ELEMENTS_RO(pnc->rp_list, node, rp_info)) {
231 if (rp_info->rp.rpf_addr.u.prefix4.s_addr == INADDR_NONE)
232 continue;
233
234 // Compute PIM RPF using cached nexthop
235 pim_ecmp_nexthop_search(pim, pnc, &rp_info->rp.source_nexthop,
236 &rp_info->rp.rpf_addr, &rp_info->group,
237 1);
238 }
239 }
240
241 /* This API is used to traverse nexthop cache of RPF addr
242 of upstream entry whose IPv4 nexthop address is in
243 unresolved state and due to event like pim neighbor
244 UP event if it can be resolved.
245 */
246 void pim_resolve_upstream_nh(struct pim_instance *pim, struct prefix *nht_p)
247 {
248 struct nexthop *nh_node = NULL;
249 struct pim_nexthop_cache pnc;
250 struct pim_neighbor *nbr = NULL;
251
252 memset(&pnc, 0, sizeof(struct pim_nexthop_cache));
253 if (!pim_find_or_track_nexthop(pim, nht_p, NULL, NULL, &pnc))
254 return;
255
256 for (nh_node = pnc.nexthop; nh_node; nh_node = nh_node->next) {
257 if (nh_node->gate.ipv4.s_addr != 0)
258 continue;
259
260 struct interface *ifp1 =
261 if_lookup_by_index(nh_node->ifindex, pim->vrf_id);
262 nbr = pim_neighbor_find_if(ifp1);
263 if (!nbr)
264 continue;
265
266 nh_node->gate.ipv4 = nbr->source_addr;
267 if (PIM_DEBUG_PIM_NHT) {
268 char str[PREFIX_STRLEN];
269 char str1[INET_ADDRSTRLEN];
270 pim_inet4_dump("<nht_nbr?>", nbr->source_addr, str1,
271 sizeof(str1));
272 pim_addr_dump("<nht_addr?>", nht_p, str, sizeof(str));
273 zlog_debug(
274 "%s: addr %s new nexthop addr %s interface %s",
275 __PRETTY_FUNCTION__, str, str1, ifp1->name);
276 }
277 }
278 }
279
280 /* Update Upstream nexthop info based on Nexthop update received from Zebra.*/
281 static int pim_update_upstream_nh_helper(struct hash_backet *backet, void *arg)
282 {
283 struct pim_instance *pim = (struct pim_instance *)arg;
284 struct pim_upstream *up = (struct pim_upstream *)backet->data;
285 int vif_index = 0;
286
287 enum pim_rpf_result rpf_result;
288 struct pim_rpf old;
289
290 old.source_nexthop.interface = up->rpf.source_nexthop.interface;
291 rpf_result = pim_rpf_update(pim, up, &old, 0);
292 if (rpf_result == PIM_RPF_FAILURE) {
293 pim_mroute_del(up->channel_oil, __PRETTY_FUNCTION__);
294 return HASHWALK_CONTINUE;
295 }
296
297 /* update kernel multicast forwarding cache (MFC) */
298 if (up->channel_oil) {
299 ifindex_t ifindex = up->rpf.source_nexthop.interface->ifindex;
300
301 vif_index = pim_if_find_vifindex_by_ifindex(pim, ifindex);
302 /* Pass Current selected NH vif index to mroute download
303 */
304 if (vif_index)
305 pim_scan_individual_oil(up->channel_oil, vif_index);
306 else {
307 if (PIM_DEBUG_PIM_NHT)
308 zlog_debug(
309 "%s: NHT upstream %s channel_oil IIF %s vif_index is not valid",
310 __PRETTY_FUNCTION__, up->sg_str,
311 up->rpf.source_nexthop.interface->name);
312 }
313 }
314
315 if (rpf_result == PIM_RPF_CHANGED) {
316 struct pim_neighbor *nbr;
317
318 nbr = pim_neighbor_find(old.source_nexthop.interface,
319 old.rpf_addr.u.prefix4);
320 if (nbr)
321 pim_jp_agg_remove_group(nbr->upstream_jp_agg, up);
322
323 /*
324 * We have detected a case where we might need to rescan
325 * the inherited o_list so do it.
326 */
327 if (up->channel_oil && up->channel_oil->oil_inherited_rescan) {
328 pim_upstream_inherited_olist_decide(pim, up);
329 up->channel_oil->oil_inherited_rescan = 0;
330 }
331
332 if (up->join_state == PIM_UPSTREAM_JOINED) {
333 /*
334 * If we come up real fast we can be here
335 * where the mroute has not been installed
336 * so install it.
337 */
338 if (up->channel_oil && !up->channel_oil->installed)
339 pim_mroute_add(up->channel_oil,
340 __PRETTY_FUNCTION__);
341
342 /*
343 * RFC 4601: 4.5.7. Sending (S,G) Join/Prune Messages
344 *
345 * Transitions from Joined State
346 *
347 * RPF'(S,G) changes not due to an Assert
348 *
349 * The upstream (S,G) state machine remains in Joined
350 * state. Send Join(S,G) to the new upstream
351 * neighbor, which is the new value of RPF'(S,G).
352 * Send Prune(S,G) to the old upstream neighbor, which
353 * is the old value of RPF'(S,G). Set the Join
354 * Timer (JT) to expire after t_periodic seconds.
355 */
356 pim_jp_agg_switch_interface(&old, &up->rpf, up);
357
358 pim_upstream_join_timer_restart(up, &old);
359 } /* up->join_state == PIM_UPSTREAM_JOINED */
360
361 /*
362 * FIXME can join_desired actually be changed by
363 * pim_rpf_update() returning PIM_RPF_CHANGED ?
364 */
365 pim_upstream_update_join_desired(pim, up);
366
367 } /* PIM_RPF_CHANGED */
368
369 if (PIM_DEBUG_PIM_NHT) {
370 zlog_debug("%s: NHT upstream %s(%s) old ifp %s new ifp %s",
371 __PRETTY_FUNCTION__, up->sg_str, pim->vrf->name,
372 old.source_nexthop.interface->name,
373 up->rpf.source_nexthop.interface->name);
374 }
375
376 return HASHWALK_CONTINUE;
377 }
378
379 static int pim_update_upstream_nh(struct pim_instance *pim,
380 struct pim_nexthop_cache *pnc)
381 {
382 struct listnode *node;
383 struct interface *ifp;
384
385 hash_walk(pnc->upstream_hash, pim_update_upstream_nh_helper, pim);
386
387 FOR_ALL_INTERFACES (pim->vrf, ifp)
388 if (ifp->info) {
389 struct pim_interface *pim_ifp = ifp->info;
390 struct pim_iface_upstream_switch *us;
391
392 for (ALL_LIST_ELEMENTS_RO(pim_ifp->upstream_switch_list,
393 node, us)) {
394 struct pim_rpf rpf;
395 rpf.source_nexthop.interface = ifp;
396 rpf.rpf_addr.u.prefix4 = us->address;
397 pim_joinprune_send(&rpf, us->us);
398 pim_jp_agg_clear_group(us->us);
399 }
400 }
401
402 return 0;
403 }
404
405 uint32_t pim_compute_ecmp_hash(struct prefix *src, struct prefix *grp)
406 {
407 uint32_t hash_val;
408 uint32_t s = 0, g = 0;
409
410 if ((!src))
411 return 0;
412
413 switch (src->family) {
414 case AF_INET: {
415 s = src->u.prefix4.s_addr;
416 s = s == 0 ? 1 : s;
417 if (grp)
418 g = grp->u.prefix4.s_addr;
419 } break;
420 default:
421 break;
422 }
423
424 hash_val = jhash_2words(g, s, 101);
425 return hash_val;
426 }
427
428 int pim_ecmp_nexthop_search(struct pim_instance *pim,
429 struct pim_nexthop_cache *pnc,
430 struct pim_nexthop *nexthop, struct prefix *src,
431 struct prefix *grp, int neighbor_needed)
432 {
433 struct pim_neighbor *nbr = NULL;
434 struct nexthop *nh_node = NULL;
435 ifindex_t first_ifindex;
436 struct interface *ifp = NULL;
437 uint32_t hash_val = 0, mod_val = 0;
438 uint8_t nh_iter = 0, found = 0;
439
440 if (!pnc || !pnc->nexthop_num || !nexthop)
441 return 0;
442
443 // Current Nexthop is VALID, check to stay on the current path.
444 if (nexthop->interface && nexthop->interface->info
445 && nexthop->mrib_nexthop_addr.u.prefix4.s_addr
446 != PIM_NET_INADDR_ANY) {
447 /* User configured knob to explicitly switch
448 to new path is disabled or current path
449 metric is less than nexthop update.
450 */
451
452 if (pim->ecmp_rebalance_enable == 0) {
453 uint8_t curr_route_valid = 0;
454 // Check if current nexthop is present in new updated
455 // Nexthop list.
456 // If the current nexthop is not valid, candidate to
457 // choose new Nexthop.
458 for (nh_node = pnc->nexthop; nh_node;
459 nh_node = nh_node->next) {
460 curr_route_valid = (nexthop->interface->ifindex
461 == nh_node->ifindex);
462 if (curr_route_valid)
463 break;
464 }
465
466 if (curr_route_valid
467 && !pim_if_connected_to_source(nexthop->interface,
468 src->u.prefix4)) {
469 nbr = pim_neighbor_find(
470 nexthop->interface,
471 nexthop->mrib_nexthop_addr.u.prefix4);
472 if (!nbr
473 && !if_is_loopback(nexthop->interface)) {
474 if (PIM_DEBUG_PIM_NHT)
475 zlog_debug(
476 "%s: current nexthop does not have nbr ",
477 __PRETTY_FUNCTION__);
478 } else {
479 if (PIM_DEBUG_PIM_NHT) {
480 char src_str[INET_ADDRSTRLEN];
481 pim_inet4_dump("<addr?>",
482 src->u.prefix4,
483 src_str,
484 sizeof(src_str));
485 char grp_str[INET_ADDRSTRLEN];
486 pim_inet4_dump("<addr?>",
487 grp->u.prefix4,
488 grp_str,
489 sizeof(grp_str));
490 zlog_debug(
491 "%s: (%s,%s)(%s) current nexthop %s is valid, skipping new path selection",
492 __PRETTY_FUNCTION__,
493 src_str, grp_str,
494 pim->vrf->name,
495 nexthop->interface->name);
496 }
497 return 1;
498 }
499 }
500 }
501 }
502 if (pim->ecmp_enable) {
503 // PIM ECMP flag is enable then choose ECMP path.
504 hash_val = pim_compute_ecmp_hash(src, grp);
505 mod_val = hash_val % pnc->nexthop_num;
506 }
507
508 for (nh_node = pnc->nexthop; nh_node && (found == 0);
509 nh_node = nh_node->next) {
510 first_ifindex = nh_node->ifindex;
511 ifp = if_lookup_by_index(first_ifindex, pim->vrf_id);
512 if (!ifp) {
513 if (PIM_DEBUG_PIM_NHT) {
514 char addr_str[INET_ADDRSTRLEN];
515 pim_inet4_dump("<addr?>", src->u.prefix4,
516 addr_str, sizeof(addr_str));
517 zlog_debug(
518 "%s %s: could not find interface for ifindex %d (address %s(%s))",
519 __FILE__, __PRETTY_FUNCTION__,
520 first_ifindex, addr_str,
521 pim->vrf->name);
522 }
523 if (nh_iter == mod_val)
524 mod_val++; // Select nexthpath
525 nh_iter++;
526 continue;
527 }
528 if (!ifp->info) {
529 if (PIM_DEBUG_PIM_NHT) {
530 char addr_str[INET_ADDRSTRLEN];
531 pim_inet4_dump("<addr?>", src->u.prefix4,
532 addr_str, sizeof(addr_str));
533 zlog_debug(
534 "%s: multicast not enabled on input interface %s(%s) (ifindex=%d, RPF for source %s)",
535 __PRETTY_FUNCTION__, ifp->name,
536 pim->vrf->name, first_ifindex,
537 addr_str);
538 }
539 if (nh_iter == mod_val)
540 mod_val++; // Select nexthpath
541 nh_iter++;
542 continue;
543 }
544
545 if (neighbor_needed
546 && !pim_if_connected_to_source(ifp, src->u.prefix4)) {
547 nbr = pim_neighbor_find(ifp, nh_node->gate.ipv4);
548 if (!nbr && !if_is_loopback(ifp)) {
549 if (PIM_DEBUG_PIM_NHT)
550 zlog_debug(
551 "%s: pim nbr not found on input interface %s(%s)",
552 __PRETTY_FUNCTION__, ifp->name,
553 pim->vrf->name);
554 if (nh_iter == mod_val)
555 mod_val++; // Select nexthpath
556 nh_iter++;
557 continue;
558 }
559 }
560
561 if (nh_iter == mod_val) {
562 nexthop->interface = ifp;
563 nexthop->mrib_nexthop_addr.family = AF_INET;
564 nexthop->mrib_nexthop_addr.prefixlen = IPV4_MAX_BITLEN;
565 nexthop->mrib_nexthop_addr.u.prefix4 =
566 nh_node->gate.ipv4;
567 nexthop->mrib_metric_preference = pnc->distance;
568 nexthop->mrib_route_metric = pnc->metric;
569 nexthop->last_lookup = src->u.prefix4;
570 nexthop->last_lookup_time = pim_time_monotonic_usec();
571 nexthop->nbr = nbr;
572 found = 1;
573 if (PIM_DEBUG_PIM_NHT) {
574 char buf[INET_ADDRSTRLEN];
575 char buf2[INET_ADDRSTRLEN];
576 char buf3[INET_ADDRSTRLEN];
577 pim_inet4_dump("<src?>", src->u.prefix4, buf2,
578 sizeof(buf2));
579 pim_inet4_dump("<grp?>", grp->u.prefix4, buf3,
580 sizeof(buf3));
581 pim_inet4_dump(
582 "<rpf?>",
583 nexthop->mrib_nexthop_addr.u.prefix4,
584 buf, sizeof(buf));
585 zlog_debug(
586 "%s: (%s,%s)(%s) selected nhop interface %s addr %s mod_val %u iter %d ecmp %d",
587 __PRETTY_FUNCTION__, buf2, buf3,
588 pim->vrf->name, ifp->name, buf, mod_val,
589 nh_iter, pim->ecmp_enable);
590 }
591 }
592 nh_iter++;
593 }
594
595 if (found)
596 return 1;
597 else
598 return 0;
599 }
600
601 /* This API is used to parse Registered address nexthop update coming from Zebra
602 */
603 int pim_parse_nexthop_update(int command, struct zclient *zclient,
604 zebra_size_t length, vrf_id_t vrf_id)
605 {
606 struct nexthop *nexthop;
607 struct nexthop *nhlist_head = NULL;
608 struct nexthop *nhlist_tail = NULL;
609 int i;
610 struct pim_rpf rpf;
611 struct pim_nexthop_cache *pnc = NULL;
612 struct pim_neighbor *nbr = NULL;
613 struct interface *ifp = NULL;
614 struct interface *ifp1 = NULL;
615 struct vrf *vrf = vrf_lookup_by_id(vrf_id);
616 struct pim_instance *pim;
617 struct zapi_route nhr;
618
619 if (!vrf)
620 return 0;
621 pim = vrf->info;
622
623 if (!zapi_nexthop_update_decode(zclient->ibuf, &nhr)) {
624 if (PIM_DEBUG_PIM_NHT)
625 zlog_debug(
626 "%s: Decode of nexthop update from zebra failed",
627 __PRETTY_FUNCTION__);
628 return 0;
629 }
630
631 if (command == ZEBRA_NEXTHOP_UPDATE) {
632 prefix_copy(&rpf.rpf_addr, &nhr.prefix);
633 pnc = pim_nexthop_cache_find(pim, &rpf);
634 if (!pnc) {
635 if (PIM_DEBUG_PIM_NHT) {
636 char buf[PREFIX2STR_BUFFER];
637 prefix2str(&rpf.rpf_addr, buf, sizeof(buf));
638 zlog_debug(
639 "%s: Skipping NHT update, addr %s is not in local cached DB.",
640 __PRETTY_FUNCTION__, buf);
641 }
642 return 0;
643 }
644 } else {
645 /*
646 * We do not currently handle ZEBRA_IMPORT_CHECK_UPDATE
647 */
648 return 0;
649 }
650
651 pnc->last_update = pim_time_monotonic_usec();
652
653 if (nhr.nexthop_num) {
654 pnc->nexthop_num = 0; // Only increment for pim enabled rpf.
655
656 for (i = 0; i < nhr.nexthop_num; i++) {
657 nexthop = nexthop_from_zapi_nexthop(&nhr.nexthops[i]);
658 switch (nexthop->type) {
659 case NEXTHOP_TYPE_IPV4:
660 case NEXTHOP_TYPE_IPV4_IFINDEX:
661 case NEXTHOP_TYPE_IPV6:
662 case NEXTHOP_TYPE_BLACKHOLE:
663 break;
664 case NEXTHOP_TYPE_IFINDEX:
665 /*
666 * Connected route (i.e. no nexthop), use
667 * RPF address from nexthop cache (i.e.
668 * destination) as PIM nexthop.
669 */
670 nexthop->type = NEXTHOP_TYPE_IPV4_IFINDEX;
671 nexthop->gate.ipv4 =
672 pnc->rpf.rpf_addr.u.prefix4;
673 break;
674 case NEXTHOP_TYPE_IPV6_IFINDEX:
675 ifp1 = if_lookup_by_index(nexthop->ifindex,
676 pim->vrf_id);
677 nbr = pim_neighbor_find_if(ifp1);
678 /* Overwrite with Nbr address as NH addr */
679 if (nbr)
680 nexthop->gate.ipv4 = nbr->source_addr;
681 else {
682 // Mark nexthop address to 0 until PIM
683 // Nbr is resolved.
684 nexthop->gate.ipv4.s_addr =
685 PIM_NET_INADDR_ANY;
686 }
687
688 break;
689 }
690
691 ifp = if_lookup_by_index(nexthop->ifindex, pim->vrf_id);
692 if (!ifp) {
693 if (PIM_DEBUG_PIM_NHT) {
694 char buf[NEXTHOP_STRLEN];
695 zlog_debug(
696 "%s: could not find interface for ifindex %d(%s) (addr %s)",
697 __PRETTY_FUNCTION__,
698 nexthop->ifindex,
699 pim->vrf->name,
700 nexthop2str(nexthop, buf,
701 sizeof(buf)));
702 }
703 nexthop_free(nexthop);
704 continue;
705 }
706
707 if (PIM_DEBUG_PIM_NHT) {
708 char p_str[PREFIX2STR_BUFFER];
709
710 prefix2str(&nhr.prefix, p_str, sizeof(p_str));
711 zlog_debug(
712 "%s: NHT addr %s(%s) %d-nhop via %s(%s) type %d distance:%u metric:%u ",
713 __PRETTY_FUNCTION__, p_str,
714 pim->vrf->name, i + 1,
715 inet_ntoa(nexthop->gate.ipv4),
716 ifp->name, nexthop->type, nhr.distance,
717 nhr.metric);
718 }
719
720 if (!ifp->info) {
721 if (PIM_DEBUG_PIM_NHT) {
722 char buf[NEXTHOP_STRLEN];
723
724 zlog_debug(
725 "%s: multicast not enabled on input interface %s(%s) (ifindex=%d, addr %s)",
726 __PRETTY_FUNCTION__, ifp->name,
727 pim->vrf->name,
728 nexthop->ifindex,
729 nexthop2str(nexthop, buf,
730 sizeof(buf)));
731 }
732 nexthop_free(nexthop);
733 continue;
734 }
735
736 if (nhlist_tail) {
737 nhlist_tail->next = nexthop;
738 nhlist_tail = nexthop;
739 } else {
740 nhlist_tail = nexthop;
741 nhlist_head = nexthop;
742 }
743 // Only keep track of nexthops which are PIM enabled.
744 pnc->nexthop_num++;
745 }
746 /* Reset existing pnc->nexthop before assigning new list */
747 nexthops_free(pnc->nexthop);
748 pnc->nexthop = nhlist_head;
749 if (pnc->nexthop_num) {
750 pnc->flags |= PIM_NEXTHOP_VALID;
751 pnc->distance = nhr.distance;
752 pnc->metric = nhr.metric;
753 }
754 } else {
755 pnc->flags &= ~PIM_NEXTHOP_VALID;
756 pnc->nexthop_num = nhr.nexthop_num;
757 nexthops_free(pnc->nexthop);
758 pnc->nexthop = NULL;
759 }
760
761 if (PIM_DEBUG_PIM_NHT) {
762 char buf[PREFIX2STR_BUFFER];
763 prefix2str(&nhr.prefix, buf, sizeof(buf));
764 zlog_debug(
765 "%s: NHT Update for %s(%s) num_nh %d num_pim_nh %d vrf:%u up %ld rp %d",
766 __PRETTY_FUNCTION__, buf, pim->vrf->name,
767 nhr.nexthop_num, pnc->nexthop_num, vrf_id,
768 pnc->upstream_hash->count, listcount(pnc->rp_list));
769 }
770
771 pim_rpf_set_refresh_time(pim);
772
773 if (listcount(pnc->rp_list))
774 pim_update_rp_nh(pim, pnc);
775 if (pnc->upstream_hash->count)
776 pim_update_upstream_nh(pim, pnc);
777
778 return 0;
779 }
780
781 int pim_ecmp_nexthop_lookup(struct pim_instance *pim,
782 struct pim_nexthop *nexthop, struct in_addr addr,
783 struct prefix *src, struct prefix *grp,
784 int neighbor_needed)
785 {
786 struct pim_zlookup_nexthop nexthop_tab[MULTIPATH_NUM];
787 struct pim_neighbor *nbr = NULL;
788 int num_ifindex;
789 struct interface *ifp;
790 int first_ifindex;
791 int found = 0;
792 uint8_t i = 0;
793 uint32_t hash_val = 0, mod_val = 0;
794
795 if (PIM_DEBUG_PIM_NHT) {
796 char addr_str[INET_ADDRSTRLEN];
797 pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
798 zlog_debug("%s: Looking up: %s(%s), last lookup time: %lld",
799 __PRETTY_FUNCTION__, addr_str, pim->vrf->name,
800 nexthop->last_lookup_time);
801 }
802
803 memset(nexthop_tab, 0,
804 sizeof(struct pim_zlookup_nexthop) * MULTIPATH_NUM);
805 num_ifindex = zclient_lookup_nexthop(pim, nexthop_tab, MULTIPATH_NUM,
806 addr, PIM_NEXTHOP_LOOKUP_MAX);
807 if (num_ifindex < 1) {
808 if (PIM_DEBUG_PIM_NHT) {
809 char addr_str[INET_ADDRSTRLEN];
810 pim_inet4_dump("<addr?>", addr, addr_str,
811 sizeof(addr_str));
812 zlog_warn(
813 "%s: could not find nexthop ifindex for address %s(%s)",
814 __PRETTY_FUNCTION__, addr_str, pim->vrf->name);
815 }
816 return 0;
817 }
818
819 // If PIM ECMP enable then choose ECMP path.
820 if (pim->ecmp_enable) {
821 hash_val = pim_compute_ecmp_hash(src, grp);
822 mod_val = hash_val % num_ifindex;
823 if (PIM_DEBUG_PIM_NHT_DETAIL)
824 zlog_debug("%s: hash_val %u mod_val %u",
825 __PRETTY_FUNCTION__, hash_val, mod_val);
826 }
827
828 while (!found && (i < num_ifindex)) {
829 first_ifindex = nexthop_tab[i].ifindex;
830
831 ifp = if_lookup_by_index(first_ifindex, pim->vrf_id);
832 if (!ifp) {
833 if (PIM_DEBUG_PIM_NHT) {
834 char addr_str[INET_ADDRSTRLEN];
835 pim_inet4_dump("<addr?>", addr, addr_str,
836 sizeof(addr_str));
837 zlog_debug(
838 "%s %s: could not find interface for ifindex %d (address %s(%s))",
839 __FILE__, __PRETTY_FUNCTION__,
840 first_ifindex, addr_str,
841 pim->vrf->name);
842 }
843 if (i == mod_val)
844 mod_val++;
845 i++;
846 continue;
847 }
848
849 if (!ifp->info) {
850 if (PIM_DEBUG_PIM_NHT) {
851 char addr_str[INET_ADDRSTRLEN];
852 pim_inet4_dump("<addr?>", addr, addr_str,
853 sizeof(addr_str));
854 zlog_debug(
855 "%s: multicast not enabled on input interface %s(%s) (ifindex=%d, RPF for source %s)",
856 __PRETTY_FUNCTION__, ifp->name,
857 pim->vrf->name, first_ifindex,
858 addr_str);
859 }
860 if (i == mod_val)
861 mod_val++;
862 i++;
863 continue;
864 }
865 if (neighbor_needed && !pim_if_connected_to_source(ifp, addr)) {
866 nbr = pim_neighbor_find(
867 ifp, nexthop_tab[i].nexthop_addr.u.prefix4);
868 if (PIM_DEBUG_PIM_NHT_DETAIL)
869 zlog_debug("ifp name: %s(%s), pim nbr: %p",
870 ifp->name, pim->vrf->name, nbr);
871 if (!nbr && !if_is_loopback(ifp)) {
872 if (i == mod_val)
873 mod_val++;
874 i++;
875 if (PIM_DEBUG_PIM_NHT) {
876 char addr_str[INET_ADDRSTRLEN];
877 pim_inet4_dump("<addr?>", addr,
878 addr_str,
879 sizeof(addr_str));
880 zlog_debug(
881 "%s: NBR not found on input interface %s(%s) (RPF for source %s)",
882 __PRETTY_FUNCTION__, ifp->name,
883 pim->vrf->name, addr_str);
884 }
885 continue;
886 }
887 }
888
889 if (i == mod_val) {
890 if (PIM_DEBUG_PIM_NHT) {
891 char nexthop_str[PREFIX_STRLEN];
892 char addr_str[INET_ADDRSTRLEN];
893 pim_addr_dump("<nexthop?>",
894 &nexthop_tab[i].nexthop_addr,
895 nexthop_str, sizeof(nexthop_str));
896 pim_inet4_dump("<addr?>", addr, addr_str,
897 sizeof(addr_str));
898 zlog_debug(
899 "%s: found nhop %s for addr %s interface %s(%s) metric %d dist %d",
900 __PRETTY_FUNCTION__, nexthop_str,
901 addr_str, ifp->name, pim->vrf->name,
902 nexthop_tab[i].route_metric,
903 nexthop_tab[i].protocol_distance);
904 }
905 /* update nexthop data */
906 nexthop->interface = ifp;
907 nexthop->mrib_nexthop_addr =
908 nexthop_tab[i].nexthop_addr;
909 nexthop->mrib_metric_preference =
910 nexthop_tab[i].protocol_distance;
911 nexthop->mrib_route_metric =
912 nexthop_tab[i].route_metric;
913 nexthop->last_lookup = addr;
914 nexthop->last_lookup_time = pim_time_monotonic_usec();
915 nexthop->nbr = nbr;
916 found = 1;
917 }
918 i++;
919 }
920
921 if (found)
922 return 1;
923 else
924 return 0;
925 }
926
927 int pim_ecmp_fib_lookup_if_vif_index(struct pim_instance *pim,
928 struct in_addr addr, struct prefix *src,
929 struct prefix *grp)
930 {
931 struct pim_zlookup_nexthop nexthop_tab[MULTIPATH_NUM];
932 int num_ifindex;
933 int vif_index;
934 ifindex_t first_ifindex;
935 uint32_t hash_val = 0, mod_val = 0;
936
937 memset(nexthop_tab, 0,
938 sizeof(struct pim_zlookup_nexthop) * MULTIPATH_NUM);
939 num_ifindex = zclient_lookup_nexthop(pim, nexthop_tab, MULTIPATH_NUM,
940 addr, PIM_NEXTHOP_LOOKUP_MAX);
941 if (num_ifindex < 1) {
942 if (PIM_DEBUG_PIM_NHT) {
943 char addr_str[INET_ADDRSTRLEN];
944 pim_inet4_dump("<addr?>", addr, addr_str,
945 sizeof(addr_str));
946 zlog_debug(
947 "%s: could not find nexthop ifindex for address %s(%s)",
948 __PRETTY_FUNCTION__, addr_str, pim->vrf->name);
949 }
950 return -1;
951 }
952
953 // If PIM ECMP enable then choose ECMP path.
954 if (pim->ecmp_enable) {
955 hash_val = pim_compute_ecmp_hash(src, grp);
956 mod_val = hash_val % num_ifindex;
957 if (PIM_DEBUG_PIM_NHT_DETAIL)
958 zlog_debug("%s: hash_val %u mod_val %u",
959 __PRETTY_FUNCTION__, hash_val, mod_val);
960 }
961
962 first_ifindex = nexthop_tab[mod_val].ifindex;
963
964 if (PIM_DEBUG_PIM_NHT) {
965 char addr_str[INET_ADDRSTRLEN];
966 pim_inet4_dump("<ifaddr?>", addr, addr_str, sizeof(addr_str));
967 zlog_debug(
968 "%s: found nexthop ifindex=%d (interface %s(%s)) for address %s",
969 __PRETTY_FUNCTION__, first_ifindex,
970 ifindex2ifname(first_ifindex, pim->vrf_id),
971 pim->vrf->name, addr_str);
972 }
973
974 vif_index = pim_if_find_vifindex_by_ifindex(pim, first_ifindex);
975
976 if (vif_index < 0) {
977 if (PIM_DEBUG_PIM_NHT) {
978 char addr_str[INET_ADDRSTRLEN];
979 pim_inet4_dump("<addr?>", addr, addr_str,
980 sizeof(addr_str));
981 zlog_debug(
982 "%s: low vif_index=%d(%s) < 1 nexthop for address %s",
983 __PRETTY_FUNCTION__, vif_index, pim->vrf->name,
984 addr_str);
985 }
986 return -2;
987 }
988
989 return vif_index;
990 }