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