]>
Commit | Line | Data |
---|---|---|
acddc0ed | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
12e41d03 | 2 | /* |
896014f4 DL |
3 | * PIM for Quagga |
4 | * Copyright (C) 2008 Everton da Silva Marques | |
896014f4 | 5 | */ |
12e41d03 DL |
6 | |
7 | #include <zebra.h> | |
8 | ||
744d91b3 DS |
9 | #include "if.h" |
10 | ||
12e41d03 DL |
11 | #include "log.h" |
12 | #include "prefix.h" | |
13 | #include "memory.h" | |
c2cf4b02 | 14 | #include "jhash.h" |
12e41d03 DL |
15 | |
16 | #include "pimd.h" | |
993e3d8e | 17 | #include "pim_instance.h" |
12e41d03 DL |
18 | #include "pim_rpf.h" |
19 | #include "pim_pim.h" | |
20 | #include "pim_str.h" | |
21 | #include "pim_iface.h" | |
e34e07e6 | 22 | #include "pim_neighbor.h" |
12e41d03 DL |
23 | #include "pim_zlookup.h" |
24 | #include "pim_ifchannel.h" | |
e71bf8f7 | 25 | #include "pim_time.h" |
1bc98276 | 26 | #include "pim_nht.h" |
cc0cecae | 27 | #include "pim_oil.h" |
f03999ca | 28 | #include "pim_mlag.h" |
e71bf8f7 | 29 | |
00b1f412 | 30 | static pim_addr pim_rpf_find_rpf_addr(struct pim_upstream *up); |
12e41d03 | 31 | |
bfc92019 | 32 | void pim_rpf_set_refresh_time(struct pim_instance *pim) |
e71bf8f7 | 33 | { |
bfc92019 | 34 | pim->last_route_change_time = pim_time_monotonic_usec(); |
23fc858a | 35 | if (PIM_DEBUG_PIM_TRACE) |
bfc92019 | 36 | zlog_debug("%s: vrf(%s) New last route change time: %" PRId64, |
15569c58 | 37 | __func__, pim->vrf->name, |
bfc92019 | 38 | pim->last_route_change_time); |
e71bf8f7 DS |
39 | } |
40 | ||
ade155e1 | 41 | bool pim_nexthop_lookup(struct pim_instance *pim, struct pim_nexthop *nexthop, |
00b1f412 | 42 | pim_addr addr, int neighbor_needed) |
12e41d03 | 43 | { |
75700af6 | 44 | struct pim_zlookup_nexthop nexthop_tab[router->multipath]; |
d62a17ae | 45 | struct pim_neighbor *nbr = NULL; |
46 | int num_ifindex; | |
47 | struct interface *ifp = NULL; | |
48 | ifindex_t first_ifindex = 0; | |
49 | int found = 0; | |
50 | int i = 0; | |
31b66ed6 | 51 | struct pim_interface *pim_ifp; |
d62a17ae | 52 | |
00b1f412 | 53 | #if PIM_IPV == 4 |
ea89ab14 DS |
54 | /* |
55 | * We should not attempt to lookup a | |
56 | * 255.255.255.255 address, since | |
57 | * it will never work | |
58 | */ | |
cc144e8b | 59 | if (pim_addr_is_any(addr)) |
ade155e1 | 60 | return false; |
00b1f412 | 61 | #endif |
ea89ab14 | 62 | |
ded5024a MR |
63 | if ((!pim_addr_cmp(nexthop->last_lookup, addr)) && |
64 | (nexthop->last_lookup_time > pim->last_route_change_time)) { | |
65 | if (PIM_DEBUG_PIM_NHT) | |
d62a17ae | 66 | zlog_debug( |
ded5024a | 67 | "%s: Using last lookup for %pPAs at %lld, %" PRId64 |
34a466b7 | 68 | " addr %pPAs", |
00b1f412 | 69 | __func__, &addr, nexthop->last_lookup_time, |
ded5024a MR |
70 | pim->last_route_change_time, |
71 | &nexthop->mrib_nexthop_addr); | |
bfc92019 | 72 | pim->nexthop_lookups_avoided++; |
ade155e1 | 73 | return true; |
d62a17ae | 74 | } else { |
00b1f412 | 75 | if (PIM_DEBUG_PIM_NHT) |
d62a17ae | 76 | zlog_debug( |
00b1f412 DL |
77 | "%s: Looking up: %pPAs, last lookup time: %lld, %" PRId64, |
78 | __func__, &addr, nexthop->last_lookup_time, | |
bfc92019 | 79 | pim->last_route_change_time); |
e71bf8f7 | 80 | } |
d62a17ae | 81 | |
82 | memset(nexthop_tab, 0, | |
75700af6 DS |
83 | sizeof(struct pim_zlookup_nexthop) * router->multipath); |
84 | num_ifindex = | |
85 | zclient_lookup_nexthop(pim, nexthop_tab, router->multipath, | |
86 | addr, PIM_NEXTHOP_LOOKUP_MAX); | |
d62a17ae | 87 | if (num_ifindex < 1) { |
7d7206a5 DS |
88 | if (PIM_DEBUG_PIM_NHT) |
89 | zlog_debug( | |
90 | "%s %s: could not find nexthop ifindex for address %pPAs", | |
91 | __FILE__, __func__, &addr); | |
ade155e1 | 92 | return false; |
d62a17ae | 93 | } |
94 | ||
95 | while (!found && (i < num_ifindex)) { | |
96 | first_ifindex = nexthop_tab[i].ifindex; | |
97 | ||
d3cc1e45 | 98 | ifp = if_lookup_by_index(first_ifindex, pim->vrf->vrf_id); |
d62a17ae | 99 | if (!ifp) { |
00b1f412 | 100 | if (PIM_DEBUG_ZEBRA) |
d62a17ae | 101 | zlog_debug( |
00b1f412 | 102 | "%s %s: could not find interface for ifindex %d (address %pPAs)", |
15569c58 | 103 | __FILE__, __func__, first_ifindex, |
00b1f412 | 104 | &addr); |
d62a17ae | 105 | i++; |
106 | continue; | |
107 | } | |
108 | ||
31b66ed6 SP |
109 | pim_ifp = ifp->info; |
110 | if (!pim_ifp || !pim_ifp->pim_enable) { | |
00b1f412 | 111 | if (PIM_DEBUG_ZEBRA) |
d62a17ae | 112 | zlog_debug( |
31b66ed6 | 113 | "%s: pim not enabled on input interface %s (ifindex=%d, RPF for source %pPAs)", |
15569c58 | 114 | __func__, ifp->name, first_ifindex, |
00b1f412 | 115 | &addr); |
d62a17ae | 116 | i++; |
31b66ed6 SP |
117 | } else if (neighbor_needed && |
118 | !pim_if_connected_to_source(ifp, addr)) { | |
e14ba248 SP |
119 | nbr = pim_neighbor_find( |
120 | ifp, nexthop_tab[i].nexthop_addr, true); | |
d62a17ae | 121 | if (PIM_DEBUG_PIM_TRACE_DETAIL) |
122 | zlog_debug("ifp name: %s, pim nbr: %p", | |
123 | ifp->name, nbr); | |
124 | if (!nbr && !if_is_loopback(ifp)) | |
125 | i++; | |
126 | else | |
127 | found = 1; | |
128 | } else | |
129 | found = 1; | |
e71bf8f7 | 130 | } |
d62a17ae | 131 | |
132 | if (found) { | |
ded5024a | 133 | if (PIM_DEBUG_ZEBRA) |
d62a17ae | 134 | zlog_debug( |
34a466b7 | 135 | "%s %s: found nexthop %pPAs for address %pPAs: interface %s ifindex=%d metric=%d pref=%d", |
ded5024a MR |
136 | __FILE__, __func__, |
137 | &nexthop_tab[i].nexthop_addr, &addr, ifp->name, | |
138 | first_ifindex, nexthop_tab[i].route_metric, | |
d62a17ae | 139 | nexthop_tab[i].protocol_distance); |
34a466b7 | 140 | |
1a81b790 | 141 | /* update nexthop data */ |
d62a17ae | 142 | nexthop->interface = ifp; |
143 | nexthop->mrib_nexthop_addr = nexthop_tab[i].nexthop_addr; | |
144 | nexthop->mrib_metric_preference = | |
145 | nexthop_tab[i].protocol_distance; | |
146 | nexthop->mrib_route_metric = nexthop_tab[i].route_metric; | |
147 | nexthop->last_lookup = addr; | |
148 | nexthop->last_lookup_time = pim_time_monotonic_usec(); | |
149 | nexthop->nbr = nbr; | |
ade155e1 | 150 | return true; |
d62a17ae | 151 | } else |
ade155e1 | 152 | return false; |
12e41d03 DL |
153 | } |
154 | ||
155 | static int nexthop_mismatch(const struct pim_nexthop *nh1, | |
156 | const struct pim_nexthop *nh2) | |
157 | { | |
3e394a77 | 158 | return (nh1->interface != nh2->interface) || |
34a466b7 | 159 | (pim_addr_cmp(nh1->mrib_nexthop_addr, nh2->mrib_nexthop_addr)) || |
3e394a77 | 160 | (nh1->mrib_metric_preference != nh2->mrib_metric_preference) || |
161 | (nh1->mrib_route_metric != nh2->mrib_route_metric); | |
12e41d03 DL |
162 | } |
163 | ||
05ca004b AK |
164 | static void pim_rpf_cost_change(struct pim_instance *pim, |
165 | struct pim_upstream *up, uint32_t old_cost) | |
166 | { | |
167 | struct pim_rpf *rpf = &up->rpf; | |
168 | uint32_t new_cost; | |
169 | ||
170 | new_cost = pim_up_mlag_local_cost(up); | |
171 | if (PIM_DEBUG_MLAG) | |
172 | zlog_debug( | |
173 | "%s: Cost_to_rp of upstream-%s changed to:%u, from:%u", | |
174 | __func__, up->sg_str, new_cost, old_cost); | |
175 | ||
176 | if (old_cost == new_cost) | |
177 | return; | |
178 | ||
179 | /* Cost changed, it might Impact MLAG DF election, update */ | |
180 | if (PIM_DEBUG_MLAG) | |
181 | zlog_debug( | |
182 | "%s: Cost_to_rp of upstream-%s changed to:%u", | |
183 | __func__, up->sg_str, | |
184 | rpf->source_nexthop.mrib_route_metric); | |
185 | ||
186 | if (pim_up_mlag_is_local(up)) | |
187 | pim_mlag_up_local_add(pim, up); | |
188 | } | |
189 | ||
2002dcdb | 190 | enum pim_rpf_result pim_rpf_update(struct pim_instance *pim, |
8c55c132 AK |
191 | struct pim_upstream *up, struct pim_rpf *old, |
192 | const char *caller) | |
12e41d03 | 193 | { |
d62a17ae | 194 | struct pim_rpf *rpf = &up->rpf; |
195 | struct pim_rpf saved; | |
66344d3d | 196 | pim_addr src; |
197 | struct prefix grp; | |
57695eb6 | 198 | bool neigh_needed = true; |
05ca004b | 199 | uint32_t saved_mrib_route_metric; |
d62a17ae | 200 | |
6a5de0ad AK |
201 | if (PIM_UPSTREAM_FLAG_TEST_STATIC_IIF(up->flags)) |
202 | return PIM_RPF_OK; | |
203 | ||
01adb431 | 204 | if (pim_addr_is_any(up->upstream_addr)) { |
8c55c132 AK |
205 | zlog_debug("%s(%s): RP is not configured yet for %s", |
206 | __func__, caller, up->sg_str); | |
957d93ea SP |
207 | return PIM_RPF_OK; |
208 | } | |
209 | ||
d62a17ae | 210 | saved.source_nexthop = rpf->source_nexthop; |
211 | saved.rpf_addr = rpf->rpf_addr; | |
05ca004b | 212 | saved_mrib_route_metric = pim_up_mlag_local_cost(up); |
b36576e4 AK |
213 | if (old) { |
214 | old->source_nexthop = saved.source_nexthop; | |
215 | old->rpf_addr = saved.rpf_addr; | |
216 | } | |
d62a17ae | 217 | |
66344d3d | 218 | src = up->upstream_addr; // RP or Src address |
c631920c | 219 | pim_addr_to_prefix(&grp, up->sg.grp); |
57695eb6 | 220 | |
2a27f13b | 221 | if ((pim_addr_is_any(up->sg.src) && I_am_RP(pim, up->sg.grp)) || |
57695eb6 | 222 | PIM_UPSTREAM_FLAG_TEST_FHR(up->flags)) |
2951a7a4 | 223 | neigh_needed = false; |
e6e53006 | 224 | pim_find_or_track_nexthop(pim, up->upstream_addr, up, NULL, NULL); |
225 | if (!pim_ecmp_nexthop_lookup(pim, &rpf->source_nexthop, src, &grp, | |
226 | neigh_needed)) { | |
05ca004b AK |
227 | /* Route is Deleted in Zebra, reset the stored NH data */ |
228 | pim_upstream_rpf_clear(pim, up); | |
229 | pim_rpf_cost_change(pim, up, saved_mrib_route_metric); | |
43763b11 | 230 | return PIM_RPF_FAILURE; |
05ca004b | 231 | } |
d62a17ae | 232 | |
66344d3d | 233 | rpf->rpf_addr = pim_rpf_find_rpf_addr(up); |
00b1f412 | 234 | |
d62a17ae | 235 | if (pim_rpf_addr_is_inaddr_any(rpf) && PIM_DEBUG_ZEBRA) { |
236 | /* RPF'(S,G) not found */ | |
8c55c132 AK |
237 | zlog_debug("%s(%s): RPF'%s not found: won't send join upstream", |
238 | __func__, caller, up->sg_str); | |
d62a17ae | 239 | /* warning only */ |
240 | } | |
241 | ||
242 | /* detect change in pim_nexthop */ | |
243 | if (nexthop_mismatch(&rpf->source_nexthop, &saved.source_nexthop)) { | |
244 | ||
ded5024a | 245 | if (PIM_DEBUG_ZEBRA) |
34a466b7 | 246 | zlog_debug("%s(%s): (S,G)=%s source nexthop now is: interface=%s address=%pPAs pref=%d metric=%d", |
8c55c132 | 247 | __func__, caller, |
8bfb8b67 | 248 | up->sg_str, |
12e41d03 | 249 | rpf->source_nexthop.interface ? rpf->source_nexthop.interface->name : "<ifname?>", |
ded5024a | 250 | &rpf->source_nexthop.mrib_nexthop_addr, |
12e41d03 DL |
251 | rpf->source_nexthop.mrib_metric_preference, |
252 | rpf->source_nexthop.mrib_route_metric); | |
12e41d03 | 253 | |
cc0cecae | 254 | pim_upstream_update_join_desired(pim, up); |
d62a17ae | 255 | pim_upstream_update_could_assert(up); |
256 | pim_upstream_update_my_assert_metric(up); | |
257 | } | |
12e41d03 | 258 | |
d62a17ae | 259 | /* detect change in RPF_interface(S) */ |
260 | if (saved.source_nexthop.interface != rpf->source_nexthop.interface) { | |
12e41d03 | 261 | |
d62a17ae | 262 | if (PIM_DEBUG_ZEBRA) { |
8c55c132 AK |
263 | zlog_debug("%s(%s): (S,G)=%s RPF_interface(S) changed from %s to %s", |
264 | __func__, caller, | |
8bfb8b67 | 265 | up->sg_str, |
42a0111b | 266 | saved.source_nexthop.interface ? saved.source_nexthop.interface->name : "<oldif?>", |
12e41d03 | 267 | rpf->source_nexthop.interface ? rpf->source_nexthop.interface->name : "<newif?>"); |
d62a17ae | 268 | /* warning only */ |
269 | } | |
270 | ||
271 | pim_upstream_rpf_interface_changed( | |
272 | up, saved.source_nexthop.interface); | |
273 | } | |
274 | ||
275 | /* detect change in RPF'(S,G) */ | |
66344d3d | 276 | if (pim_addr_cmp(saved.rpf_addr, rpf->rpf_addr) || |
3e394a77 | 277 | saved.source_nexthop.interface != rpf->source_nexthop.interface) { |
05ca004b | 278 | pim_rpf_cost_change(pim, up, saved_mrib_route_metric); |
d62a17ae | 279 | return PIM_RPF_CHANGED; |
280 | } | |
281 | ||
05ca004b AK |
282 | if (PIM_DEBUG_MLAG) |
283 | zlog_debug( | |
284 | "%s(%s): Cost_to_rp of upstream-%s changed to:%u", | |
285 | __func__, caller, up->sg_str, | |
286 | rpf->source_nexthop.mrib_route_metric); | |
287 | ||
288 | pim_rpf_cost_change(pim, up, saved_mrib_route_metric); | |
289 | ||
d62a17ae | 290 | return PIM_RPF_OK; |
12e41d03 DL |
291 | } |
292 | ||
1250cb5d SP |
293 | /* |
294 | * In the case of RP deletion and RP unreachablity, | |
295 | * uninstall the mroute in the kernel and clear the | |
296 | * rpf information in the pim upstream and pim channel | |
297 | * oil data structure. | |
298 | */ | |
299 | void pim_upstream_rpf_clear(struct pim_instance *pim, | |
300 | struct pim_upstream *up) | |
301 | { | |
302 | if (up->rpf.source_nexthop.interface) { | |
4b67fe2d | 303 | pim_upstream_switch(pim, up, PIM_UPSTREAM_NOTJOINED); |
1250cb5d | 304 | up->rpf.source_nexthop.interface = NULL; |
34a466b7 | 305 | up->rpf.source_nexthop.mrib_nexthop_addr = PIMADDR_ANY; |
1250cb5d SP |
306 | up->rpf.source_nexthop.mrib_metric_preference = |
307 | router->infinite_assert_metric.metric_preference; | |
308 | up->rpf.source_nexthop.mrib_route_metric = | |
309 | router->infinite_assert_metric.route_metric; | |
66344d3d | 310 | up->rpf.rpf_addr = PIMADDR_ANY; |
7984af18 | 311 | pim_upstream_mroute_iif_update(up->channel_oil, __func__); |
1250cb5d SP |
312 | } |
313 | } | |
314 | ||
12e41d03 DL |
315 | /* |
316 | RFC 4601: 4.1.6. State Summarization Macros | |
317 | ||
318 | neighbor RPF'(S,G) { | |
d62a17ae | 319 | if ( I_Am_Assert_Loser(S, G, RPF_interface(S) )) { |
320 | return AssertWinner(S, G, RPF_interface(S) ) | |
321 | } else { | |
322 | return NBR( RPF_interface(S), MRIB.next_hop( S ) ) | |
323 | } | |
12e41d03 DL |
324 | } |
325 | ||
326 | RPF'(*,G) and RPF'(S,G) indicate the neighbor from which data | |
327 | packets should be coming and to which joins should be sent on the RP | |
328 | tree and SPT, respectively. | |
329 | */ | |
00b1f412 | 330 | static pim_addr pim_rpf_find_rpf_addr(struct pim_upstream *up) |
12e41d03 | 331 | { |
d62a17ae | 332 | struct pim_ifchannel *rpf_ch; |
333 | struct pim_neighbor *neigh; | |
00b1f412 | 334 | pim_addr rpf_addr; |
d62a17ae | 335 | |
336 | if (!up->rpf.source_nexthop.interface) { | |
337 | zlog_warn("%s: missing RPF interface for upstream (S,G)=%s", | |
15569c58 | 338 | __func__, up->sg_str); |
d62a17ae | 339 | |
00b1f412 | 340 | return PIMADDR_ANY; |
d62a17ae | 341 | } |
342 | ||
343 | rpf_ch = pim_ifchannel_find(up->rpf.source_nexthop.interface, &up->sg); | |
344 | if (rpf_ch) { | |
345 | if (rpf_ch->ifassert_state == PIM_IFASSERT_I_AM_LOSER) { | |
346 | return rpf_ch->ifassert_winner; | |
347 | } | |
348 | } | |
349 | ||
350 | /* return NBR( RPF_interface(S), MRIB.next_hop( S ) ) */ | |
351 | ||
34a466b7 | 352 | neigh = pim_if_find_neighbor(up->rpf.source_nexthop.interface, |
353 | up->rpf.source_nexthop.mrib_nexthop_addr); | |
d62a17ae | 354 | if (neigh) |
355 | rpf_addr = neigh->source_addr; | |
356 | else | |
00b1f412 | 357 | rpf_addr = PIMADDR_ANY; |
d62a17ae | 358 | |
359 | return rpf_addr; | |
12e41d03 | 360 | } |
63c59d0c | 361 | |
d62a17ae | 362 | int pim_rpf_addr_is_inaddr_any(struct pim_rpf *rpf) |
63c59d0c | 363 | { |
66344d3d | 364 | return pim_addr_is_any(rpf->rpf_addr); |
63c59d0c | 365 | } |
66cc32fa | 366 | |
d62a17ae | 367 | int pim_rpf_is_same(struct pim_rpf *rpf1, struct pim_rpf *rpf2) |
66cc32fa | 368 | { |
d62a17ae | 369 | if (rpf1->source_nexthop.interface == rpf2->source_nexthop.interface) |
370 | return 1; | |
66cc32fa | 371 | |
d62a17ae | 372 | return 0; |
66cc32fa | 373 | } |
c2cf4b02 | 374 | |
d8b87afe | 375 | unsigned int pim_rpf_hash_key(const void *arg) |
c2cf4b02 | 376 | { |
d8b87afe | 377 | const struct pim_nexthop_cache *r = arg; |
c2cf4b02 | 378 | |
ded5024a | 379 | #if PIM_IPV == 4 |
66344d3d | 380 | return jhash_1word(r->rpf.rpf_addr.s_addr, 0); |
3e394a77 | 381 | #else |
66344d3d | 382 | return jhash2(r->rpf.rpf_addr.s6_addr32, |
383 | array_size(r->rpf.rpf_addr.s6_addr32), 0); | |
3e394a77 | 384 | #endif |
c2cf4b02 DS |
385 | } |
386 | ||
74df8d6d | 387 | bool pim_rpf_equal(const void *arg1, const void *arg2) |
c2cf4b02 DS |
388 | { |
389 | const struct pim_nexthop_cache *r1 = | |
390 | (const struct pim_nexthop_cache *)arg1; | |
391 | const struct pim_nexthop_cache *r2 = | |
392 | (const struct pim_nexthop_cache *)arg2; | |
393 | ||
66344d3d | 394 | return (!pim_addr_cmp(r1->rpf.rpf_addr, r2->rpf.rpf_addr)); |
c2cf4b02 | 395 | } |