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