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