]>
Commit | Line | Data |
---|---|---|
12e41d03 | 1 | /* |
896014f4 DL |
2 | * PIM for Quagga |
3 | * Copyright (C) 2008 Everton da Silva Marques | |
4 | * | |
5 | * This program is free software; you can redistribute it and/or modify | |
6 | * it under the terms of the GNU General Public License as published by | |
7 | * the Free Software Foundation; either version 2 of the License, or | |
8 | * (at your option) any later version. | |
9 | * | |
10 | * This program is distributed in the hope that it will be useful, but | |
11 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
13 | * General Public License for more details. | |
14 | * | |
15 | * You should have received a copy of the GNU General Public License along | |
16 | * with this program; see the file COPYING; if not, write to the Free Software | |
17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | |
18 | */ | |
12e41d03 DL |
19 | |
20 | #include <zebra.h> | |
21 | ||
12e41d03 DL |
22 | #include "log.h" |
23 | #include "zclient.h" | |
24 | #include "memory.h" | |
25 | #include "thread.h" | |
26 | #include "linklist.h" | |
dfe43e25 DW |
27 | #include "vty.h" |
28 | #include "plist.h" | |
0f588989 DS |
29 | #include "hash.h" |
30 | #include "jhash.h" | |
9c5e4d62 | 31 | #include "wheel.h" |
5920b3eb | 32 | #include "network.h" |
12e41d03 DL |
33 | |
34 | #include "pimd.h" | |
35 | #include "pim_pim.h" | |
36 | #include "pim_str.h" | |
37 | #include "pim_time.h" | |
38 | #include "pim_iface.h" | |
39 | #include "pim_join.h" | |
40 | #include "pim_zlookup.h" | |
41 | #include "pim_upstream.h" | |
42 | #include "pim_ifchannel.h" | |
43 | #include "pim_neighbor.h" | |
44 | #include "pim_rpf.h" | |
45 | #include "pim_zebra.h" | |
46 | #include "pim_oil.h" | |
47 | #include "pim_macro.h" | |
8f5f5e91 | 48 | #include "pim_rp.h" |
f14248dd | 49 | #include "pim_br.h" |
627ed2a3 | 50 | #include "pim_register.h" |
3c72d654 | 51 | #include "pim_msdp.h" |
982bff89 | 52 | #include "pim_jp_agg.h" |
1bc98276 | 53 | #include "pim_nht.h" |
15a5dafe | 54 | #include "pim_ssm.h" |
b9f3a51c | 55 | #include "pim_vxlan.h" |
05ca004b | 56 | #include "pim_mlag.h" |
12e41d03 | 57 | |
982bff89 | 58 | static void join_timer_stop(struct pim_upstream *up); |
d62a17ae | 59 | static void |
60 | pim_upstream_update_assert_tracking_desired(struct pim_upstream *up); | |
ea6d91c8 | 61 | static bool pim_upstream_sg_running_proc(struct pim_upstream *up); |
12e41d03 | 62 | |
cfa91a87 DS |
63 | /* |
64 | * A (*,G) or a (*,*) is going away | |
65 | * remove the parent pointer from | |
66 | * those pointing at us | |
67 | */ | |
9b29ea95 DS |
68 | static void pim_upstream_remove_children(struct pim_instance *pim, |
69 | struct pim_upstream *up) | |
cfa91a87 | 70 | { |
d62a17ae | 71 | struct pim_upstream *child; |
72 | ||
73 | if (!up->sources) | |
74 | return; | |
75 | ||
76 | while (!list_isempty(up->sources)) { | |
77 | child = listnode_head(up->sources); | |
78 | listnode_delete(up->sources, child); | |
79 | if (PIM_UPSTREAM_FLAG_TEST_SRC_LHR(child->flags)) { | |
80 | PIM_UPSTREAM_FLAG_UNSET_SRC_LHR(child->flags); | |
15569c58 | 81 | child = pim_upstream_del(pim, child, __func__); |
d62a17ae | 82 | } |
70c86421 | 83 | if (child) { |
d62a17ae | 84 | child->parent = NULL; |
70c86421 AK |
85 | if (PIM_UPSTREAM_FLAG_TEST_USE_RPT(child->flags)) |
86 | pim_upstream_mroute_iif_update( | |
87 | child->channel_oil, | |
88 | __func__); | |
89 | } | |
d62a17ae | 90 | } |
6a154c88 | 91 | list_delete(&up->sources); |
cfa91a87 DS |
92 | } |
93 | ||
94 | /* | |
95 | * A (*,G) or a (*,*) is being created | |
96 | * Find the children that would point | |
97 | * at us. | |
98 | */ | |
9b29ea95 DS |
99 | static void pim_upstream_find_new_children(struct pim_instance *pim, |
100 | struct pim_upstream *up) | |
cfa91a87 | 101 | { |
d62a17ae | 102 | struct pim_upstream *child; |
d62a17ae | 103 | |
2a27f13b | 104 | if (!pim_addr_is_any(up->sg.src) && !pim_addr_is_any(up->sg.grp)) |
d62a17ae | 105 | return; |
106 | ||
2a27f13b | 107 | if (pim_addr_is_any(up->sg.src) && pim_addr_is_any(up->sg.grp)) |
d62a17ae | 108 | return; |
109 | ||
dd3364cb | 110 | frr_each (rb_pim_upstream, &pim->upstream_head, child) { |
2a27f13b | 111 | if (!pim_addr_is_any(up->sg.grp) && |
032a7412 | 112 | !pim_addr_cmp(child->sg.grp, up->sg.grp) && (child != up)) { |
d62a17ae | 113 | child->parent = up; |
114 | listnode_add_sort(up->sources, child); | |
70c86421 AK |
115 | if (PIM_UPSTREAM_FLAG_TEST_USE_RPT(child->flags)) |
116 | pim_upstream_mroute_iif_update( | |
117 | child->channel_oil, | |
118 | __func__); | |
d62a17ae | 119 | } |
03417ccd | 120 | } |
cfa91a87 DS |
121 | } |
122 | ||
4d99418b DS |
123 | /* |
124 | * If we have a (*,*) || (S,*) there is no parent | |
125 | * If we have a (S,G), find the (*,G) | |
126 | * If we have a (*,G), find the (*,*) | |
127 | */ | |
9b29ea95 DS |
128 | static struct pim_upstream *pim_upstream_find_parent(struct pim_instance *pim, |
129 | struct pim_upstream *child) | |
4d99418b | 130 | { |
6fff2cc6 | 131 | pim_sgaddr any = child->sg; |
d62a17ae | 132 | struct pim_upstream *up = NULL; |
4d99418b | 133 | |
d62a17ae | 134 | // (S,G) |
2a27f13b DL |
135 | if (!pim_addr_is_any(child->sg.src) && |
136 | !pim_addr_is_any(child->sg.grp)) { | |
bca160c6 | 137 | any.src = PIMADDR_ANY; |
9b29ea95 | 138 | up = pim_upstream_find(pim, &any); |
03417ccd | 139 | |
d62a17ae | 140 | if (up) |
141 | listnode_add(up->sources, child); | |
03417ccd | 142 | |
22c35834 SK |
143 | /* |
144 | * In case parent is MLAG entry copy the data to child | |
145 | */ | |
146 | if (up && PIM_UPSTREAM_FLAG_TEST_MLAG_INTERFACE(up->flags)) { | |
147 | PIM_UPSTREAM_FLAG_SET_MLAG_INTERFACE(child->flags); | |
148 | if (PIM_UPSTREAM_FLAG_TEST_MLAG_NON_DF(up->flags)) | |
149 | PIM_UPSTREAM_FLAG_SET_MLAG_NON_DF(child->flags); | |
150 | else | |
151 | PIM_UPSTREAM_FLAG_UNSET_MLAG_NON_DF( | |
152 | child->flags); | |
153 | } | |
154 | ||
d62a17ae | 155 | return up; |
156 | } | |
4d99418b | 157 | |
d62a17ae | 158 | return NULL; |
4d99418b DS |
159 | } |
160 | ||
12e41d03 DL |
161 | static void upstream_channel_oil_detach(struct pim_upstream *up) |
162 | { | |
a155fed5 AK |
163 | struct channel_oil *channel_oil = up->channel_oil; |
164 | ||
165 | if (channel_oil) { | |
d62a17ae | 166 | /* Detaching from channel_oil, channel_oil may exist post del, |
167 | but upstream would not keep reference of it | |
168 | */ | |
a155fed5 | 169 | channel_oil->up = NULL; |
d62a17ae | 170 | up->channel_oil = NULL; |
a155fed5 AK |
171 | |
172 | /* attempt to delete channel_oil; if channel_oil is being held | |
173 | * because of other references cleanup info such as "Mute" | |
174 | * inferred from the parent upstream | |
175 | */ | |
176 | pim_channel_oil_upstream_deref(channel_oil); | |
d62a17ae | 177 | } |
a155fed5 | 178 | |
12e41d03 DL |
179 | } |
180 | ||
9b29ea95 DS |
181 | struct pim_upstream *pim_upstream_del(struct pim_instance *pim, |
182 | struct pim_upstream *up, const char *name) | |
12e41d03 | 183 | { |
7692c5ae DS |
184 | struct listnode *node, *nnode; |
185 | struct pim_ifchannel *ch; | |
d62a17ae | 186 | bool notify_msdp = false; |
187 | struct prefix nht_p; | |
188 | ||
23fc858a | 189 | if (PIM_DEBUG_PIM_TRACE) |
d62a17ae | 190 | zlog_debug( |
8dbdb215 | 191 | "%s(%s): Delete %s[%s] ref count: %d , flags: %d c_oil ref count %d (Pre decrement)", |
5e81f5dd | 192 | __func__, name, up->sg_str, pim->vrf->name, |
996c9314 LB |
193 | up->ref_count, up->flags, |
194 | up->channel_oil->oil_ref_count); | |
d62a17ae | 195 | |
e83f3b31 | 196 | assert(up->ref_count > 0); |
197 | ||
d62a17ae | 198 | --up->ref_count; |
199 | ||
200 | if (up->ref_count >= 1) | |
201 | return up; | |
202 | ||
c5cdf069 | 203 | if (PIM_DEBUG_TRACE) |
dd3364cb DS |
204 | zlog_debug("pim_upstream free vrf:%s %s flags 0x%x", |
205 | pim->vrf->name, up->sg_str, up->flags); | |
c5cdf069 | 206 | |
95586137 AK |
207 | if (pim_up_mlag_is_local(up)) |
208 | pim_mlag_up_local_del(pim, up); | |
209 | ||
d62a17ae | 210 | THREAD_OFF(up->t_ka_timer); |
211 | THREAD_OFF(up->t_rs_timer); | |
212 | THREAD_OFF(up->t_msdp_reg_timer); | |
213 | ||
214 | if (up->join_state == PIM_UPSTREAM_JOINED) { | |
215 | pim_jp_agg_single_upstream_send(&up->rpf, up, 0); | |
216 | ||
2a27f13b | 217 | if (pim_addr_is_any(up->sg.src)) { |
d62a17ae | 218 | /* if a (*, G) entry in the joined state is being |
219 | * deleted we | |
220 | * need to notify MSDP */ | |
221 | notify_msdp = true; | |
222 | } | |
223 | } | |
224 | ||
225 | join_timer_stop(up); | |
226 | pim_jp_agg_upstream_verification(up, false); | |
227 | up->rpf.source_nexthop.interface = NULL; | |
228 | ||
2a27f13b | 229 | if (!pim_addr_is_any(up->sg.src)) { |
391b8b08 DS |
230 | if (pim->upstream_sg_wheel) |
231 | wheel_remove_item(pim->upstream_sg_wheel, up); | |
d62a17ae | 232 | notify_msdp = true; |
233 | } | |
234 | ||
5e81f5dd | 235 | pim_mroute_del(up->channel_oil, __func__); |
d62a17ae | 236 | upstream_channel_oil_detach(up); |
237 | ||
7692c5ae DS |
238 | for (ALL_LIST_ELEMENTS(up->ifchannels, node, nnode, ch)) |
239 | pim_ifchannel_delete(ch); | |
6a154c88 | 240 | list_delete(&up->ifchannels); |
d62a17ae | 241 | |
7692c5ae DS |
242 | pim_upstream_remove_children(pim, up); |
243 | if (up->sources) | |
6a154c88 | 244 | list_delete(&up->sources); |
7692c5ae | 245 | |
d62a17ae | 246 | if (up->parent && up->parent->sources) |
247 | listnode_delete(up->parent->sources, up); | |
248 | up->parent = NULL; | |
249 | ||
dd3364cb | 250 | rb_pim_upstream_del(&pim->upstream_head, up); |
d62a17ae | 251 | |
252 | if (notify_msdp) { | |
472ad383 | 253 | pim_msdp_up_del(pim, &up->sg); |
d62a17ae | 254 | } |
255 | ||
246445a3 SP |
256 | /* When RP gets deleted, pim_rp_del() deregister addr with Zebra NHT |
257 | * and assign up->upstream_addr as INADDR_ANY. | |
258 | * So before de-registering the upstream address, check if is not equal | |
259 | * to INADDR_ANY. This is done in order to avoid de-registering for | |
260 | * 255.255.255.255 which is maintained for some reason.. | |
261 | */ | |
01adb431 | 262 | if (!pim_addr_is_any(up->upstream_addr)) { |
246445a3 | 263 | /* Deregister addr with Zebra NHT */ |
01adb431 | 264 | pim_addr_to_prefix(&nht_p, up->upstream_addr); |
2dbe669b | 265 | if (PIM_DEBUG_PIM_TRACE) |
5e81f5dd | 266 | zlog_debug( |
2dbe669b DA |
267 | "%s: Deregister upstream %s addr %pFX with Zebra NHT", |
268 | __func__, up->sg_str, &nht_p); | |
4efdb9c6 | 269 | pim_delete_tracked_nexthop(pim, &nht_p, up, NULL); |
d62a17ae | 270 | } |
d62a17ae | 271 | |
172e45dc | 272 | XFREE(MTYPE_PIM_UPSTREAM, up); |
d62a17ae | 273 | |
274 | return NULL; | |
12e41d03 DL |
275 | } |
276 | ||
d62a17ae | 277 | void pim_upstream_send_join(struct pim_upstream *up) |
12e41d03 | 278 | { |
957d93ea | 279 | if (!up->rpf.source_nexthop.interface) { |
23fc858a | 280 | if (PIM_DEBUG_PIM_TRACE) |
15569c58 DA |
281 | zlog_debug("%s: up %s RPF is not present", __func__, |
282 | up->sg_str); | |
957d93ea SP |
283 | return; |
284 | } | |
285 | ||
23fc858a | 286 | if (PIM_DEBUG_PIM_TRACE) { |
d62a17ae | 287 | char rpf_str[PREFIX_STRLEN]; |
288 | pim_addr_dump("<rpf?>", &up->rpf.rpf_addr, rpf_str, | |
289 | sizeof(rpf_str)); | |
15569c58 DA |
290 | zlog_debug("%s: RPF'%s=%s(%s) for Interface %s", __func__, |
291 | up->sg_str, rpf_str, | |
d62a17ae | 292 | pim_upstream_state2str(up->join_state), |
293 | up->rpf.source_nexthop.interface->name); | |
294 | if (pim_rpf_addr_is_inaddr_any(&up->rpf)) { | |
295 | zlog_debug("%s: can't send join upstream: RPF'%s=%s", | |
15569c58 | 296 | __func__, up->sg_str, rpf_str); |
d62a17ae | 297 | /* warning only */ |
298 | } | |
299 | } | |
300 | ||
301 | /* send Join(S,G) to the current upstream neighbor */ | |
302 | pim_jp_agg_single_upstream_send(&up->rpf, up, 1 /* join */); | |
12e41d03 DL |
303 | } |
304 | ||
cc9f21da | 305 | static void on_join_timer(struct thread *t) |
12e41d03 | 306 | { |
d62a17ae | 307 | struct pim_upstream *up; |
12e41d03 | 308 | |
d62a17ae | 309 | up = THREAD_ARG(t); |
12e41d03 | 310 | |
957d93ea | 311 | if (!up->rpf.source_nexthop.interface) { |
23fc858a | 312 | if (PIM_DEBUG_PIM_TRACE) |
15569c58 DA |
313 | zlog_debug("%s: up %s RPF is not present", __func__, |
314 | up->sg_str); | |
cc9f21da | 315 | return; |
957d93ea SP |
316 | } |
317 | ||
d62a17ae | 318 | /* |
319 | * In the case of a HFR we will not ahve anyone to send this to. | |
320 | */ | |
321 | if (PIM_UPSTREAM_FLAG_TEST_FHR(up->flags)) | |
cc9f21da | 322 | return; |
bb6e291f | 323 | |
d62a17ae | 324 | /* |
325 | * Don't send the join if the outgoing interface is a loopback | |
326 | * But since this might change leave the join timer running | |
327 | */ | |
328 | if (up->rpf.source_nexthop | |
329 | .interface && !if_is_loopback(up->rpf.source_nexthop.interface)) | |
330 | pim_upstream_send_join(up); | |
12e41d03 | 331 | |
d62a17ae | 332 | join_timer_start(up); |
12e41d03 DL |
333 | } |
334 | ||
982bff89 | 335 | static void join_timer_stop(struct pim_upstream *up) |
12e41d03 | 336 | { |
47e3ce59 | 337 | struct pim_neighbor *nbr = NULL; |
957d93ea | 338 | |
d62a17ae | 339 | THREAD_OFF(up->t_join_timer); |
7eb90689 | 340 | |
47e3ce59 | 341 | if (up->rpf.source_nexthop.interface) |
9bb93fa0 DL |
342 | nbr = pim_neighbor_find_prefix(up->rpf.source_nexthop.interface, |
343 | &up->rpf.rpf_addr); | |
982bff89 | 344 | |
d62a17ae | 345 | if (nbr) |
c5cdf069 | 346 | pim_jp_agg_remove_group(nbr->upstream_jp_agg, up, nbr); |
982bff89 | 347 | |
d62a17ae | 348 | pim_jp_agg_upstream_verification(up, false); |
982bff89 DS |
349 | } |
350 | ||
d62a17ae | 351 | void join_timer_start(struct pim_upstream *up) |
982bff89 | 352 | { |
d62a17ae | 353 | struct pim_neighbor *nbr = NULL; |
354 | ||
355 | if (up->rpf.source_nexthop.interface) { | |
9bb93fa0 DL |
356 | nbr = pim_neighbor_find_prefix(up->rpf.source_nexthop.interface, |
357 | &up->rpf.rpf_addr); | |
d62a17ae | 358 | |
359 | if (PIM_DEBUG_PIM_EVENTS) { | |
360 | zlog_debug( | |
361 | "%s: starting %d sec timer for upstream (S,G)=%s", | |
15569c58 | 362 | __func__, router->t_periodic, up->sg_str); |
d62a17ae | 363 | } |
364 | } | |
365 | ||
366 | if (nbr) | |
c5cdf069 | 367 | pim_jp_agg_add_group(nbr->upstream_jp_agg, up, 1, nbr); |
d62a17ae | 368 | else { |
369 | THREAD_OFF(up->t_join_timer); | |
36417fcc | 370 | thread_add_timer(router->master, on_join_timer, up, |
5b45753e | 371 | router->t_periodic, &up->t_join_timer); |
d62a17ae | 372 | } |
373 | pim_jp_agg_upstream_verification(up, true); | |
12e41d03 DL |
374 | } |
375 | ||
982bff89 DS |
376 | /* |
377 | * This is only called when we are switching the upstream | |
378 | * J/P from one neighbor to another | |
379 | * | |
380 | * As such we need to remove from the old list and | |
381 | * add to the new list. | |
382 | */ | |
d62a17ae | 383 | void pim_upstream_join_timer_restart(struct pim_upstream *up, |
384 | struct pim_rpf *old) | |
12e41d03 | 385 | { |
d62a17ae | 386 | // THREAD_OFF(up->t_join_timer); |
387 | join_timer_start(up); | |
12e41d03 DL |
388 | } |
389 | ||
390 | static void pim_upstream_join_timer_restart_msec(struct pim_upstream *up, | |
391 | int interval_msec) | |
392 | { | |
d62a17ae | 393 | if (PIM_DEBUG_PIM_EVENTS) { |
394 | zlog_debug("%s: restarting %d msec timer for upstream (S,G)=%s", | |
15569c58 | 395 | __func__, interval_msec, up->sg_str); |
d62a17ae | 396 | } |
397 | ||
398 | THREAD_OFF(up->t_join_timer); | |
36417fcc | 399 | thread_add_timer_msec(router->master, on_join_timer, up, interval_msec, |
d62a17ae | 400 | &up->t_join_timer); |
12e41d03 DL |
401 | } |
402 | ||
3f1f8641 DS |
403 | void pim_update_suppress_timers(uint32_t suppress_time) |
404 | { | |
405 | struct pim_instance *pim; | |
406 | struct vrf *vrf; | |
407 | unsigned int old_rp_ka_time; | |
408 | ||
409 | /* stash the old one so we know which values were manually configured */ | |
410 | old_rp_ka_time = (3 * router->register_suppress_time | |
411 | + router->register_probe_time); | |
412 | router->register_suppress_time = suppress_time; | |
413 | ||
414 | RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) { | |
415 | pim = vrf->info; | |
416 | if (!pim) | |
417 | continue; | |
418 | ||
419 | /* Only adjust if not manually configured */ | |
420 | if (pim->rp_keep_alive_time == old_rp_ka_time) | |
421 | pim->rp_keep_alive_time = PIM_RP_KEEPALIVE_PERIOD; | |
422 | } | |
423 | } | |
424 | ||
fd3af229 | 425 | void pim_upstream_join_suppress(struct pim_upstream *up, struct prefix rpf, |
426 | int holdtime) | |
12e41d03 | 427 | { |
d62a17ae | 428 | long t_joinsuppress_msec; |
810cbaf7 | 429 | long join_timer_remain_msec = 0; |
430 | struct pim_neighbor *nbr = NULL; | |
d62a17ae | 431 | |
957d93ea | 432 | if (!up->rpf.source_nexthop.interface) { |
23fc858a | 433 | if (PIM_DEBUG_PIM_TRACE) |
15569c58 DA |
434 | zlog_debug("%s: up %s RPF is not present", __func__, |
435 | up->sg_str); | |
957d93ea SP |
436 | return; |
437 | } | |
438 | ||
d62a17ae | 439 | t_joinsuppress_msec = |
440 | MIN(pim_if_t_suppressed_msec(up->rpf.source_nexthop.interface), | |
441 | 1000 * holdtime); | |
442 | ||
810cbaf7 | 443 | if (up->t_join_timer) |
444 | join_timer_remain_msec = | |
445 | pim_time_timer_remain_msec(up->t_join_timer); | |
446 | else { | |
447 | /* Remove it from jp agg from the nbr for suppression */ | |
9bb93fa0 DL |
448 | nbr = pim_neighbor_find_prefix(up->rpf.source_nexthop.interface, |
449 | &up->rpf.rpf_addr); | |
810cbaf7 | 450 | if (nbr) { |
451 | join_timer_remain_msec = | |
452 | pim_time_timer_remain_msec(nbr->jp_timer); | |
453 | } | |
454 | } | |
d62a17ae | 455 | |
23fc858a | 456 | if (PIM_DEBUG_PIM_TRACE) { |
d62a17ae | 457 | char rpf_str[INET_ADDRSTRLEN]; |
fd3af229 | 458 | |
459 | pim_addr_dump("<rpf?>", &rpf, rpf_str, sizeof(rpf_str)); | |
d62a17ae | 460 | zlog_debug( |
461 | "%s %s: detected Join%s to RPF'(S,G)=%s: join_timer=%ld msec t_joinsuppress=%ld msec", | |
15569c58 | 462 | __FILE__, __func__, up->sg_str, rpf_str, |
d62a17ae | 463 | join_timer_remain_msec, t_joinsuppress_msec); |
464 | } | |
465 | ||
466 | if (join_timer_remain_msec < t_joinsuppress_msec) { | |
23fc858a | 467 | if (PIM_DEBUG_PIM_TRACE) { |
d62a17ae | 468 | zlog_debug( |
469 | "%s %s: suppressing Join(S,G)=%s for %ld msec", | |
15569c58 | 470 | __FILE__, __func__, up->sg_str, |
d62a17ae | 471 | t_joinsuppress_msec); |
472 | } | |
473 | ||
810cbaf7 | 474 | if (nbr) |
475 | pim_jp_agg_remove_group(nbr->upstream_jp_agg, up, nbr); | |
476 | ||
d62a17ae | 477 | pim_upstream_join_timer_restart_msec(up, t_joinsuppress_msec); |
478 | } | |
12e41d03 DL |
479 | } |
480 | ||
481 | void pim_upstream_join_timer_decrease_to_t_override(const char *debug_label, | |
d62a17ae | 482 | struct pim_upstream *up) |
12e41d03 | 483 | { |
d62a17ae | 484 | long join_timer_remain_msec; |
485 | int t_override_msec; | |
486 | ||
957d93ea | 487 | if (!up->rpf.source_nexthop.interface) { |
23fc858a | 488 | if (PIM_DEBUG_PIM_TRACE) |
15569c58 DA |
489 | zlog_debug("%s: up %s RPF is not present", __func__, |
490 | up->sg_str); | |
957d93ea SP |
491 | return; |
492 | } | |
493 | ||
d62a17ae | 494 | t_override_msec = |
495 | pim_if_t_override_msec(up->rpf.source_nexthop.interface); | |
496 | ||
af9106e5 | 497 | if (up->t_join_timer) { |
498 | join_timer_remain_msec = | |
499 | pim_time_timer_remain_msec(up->t_join_timer); | |
500 | } else { | |
501 | /* upstream join tracked with neighbor jp timer */ | |
502 | struct pim_neighbor *nbr; | |
503 | ||
9bb93fa0 DL |
504 | nbr = pim_neighbor_find_prefix(up->rpf.source_nexthop.interface, |
505 | &up->rpf.rpf_addr); | |
af9106e5 | 506 | if (nbr) |
507 | join_timer_remain_msec = | |
508 | pim_time_timer_remain_msec(nbr->jp_timer); | |
509 | else | |
510 | /* Manipulate such that override takes place */ | |
511 | join_timer_remain_msec = t_override_msec + 1; | |
512 | } | |
513 | ||
23fc858a | 514 | if (PIM_DEBUG_PIM_TRACE) { |
d62a17ae | 515 | char rpf_str[INET_ADDRSTRLEN]; |
fd3af229 | 516 | |
517 | pim_addr_dump("<rpf?>", &up->rpf.rpf_addr, rpf_str, | |
518 | sizeof(rpf_str)); | |
519 | ||
d62a17ae | 520 | zlog_debug( |
521 | "%s: to RPF'%s=%s: join_timer=%ld msec t_override=%d msec", | |
522 | debug_label, up->sg_str, rpf_str, | |
523 | join_timer_remain_msec, t_override_msec); | |
524 | } | |
525 | ||
526 | if (join_timer_remain_msec > t_override_msec) { | |
23fc858a | 527 | if (PIM_DEBUG_PIM_TRACE) { |
d62a17ae | 528 | zlog_debug( |
529 | "%s: decreasing (S,G)=%s join timer to t_override=%d msec", | |
530 | debug_label, up->sg_str, t_override_msec); | |
531 | } | |
532 | ||
533 | pim_upstream_join_timer_restart_msec(up, t_override_msec); | |
534 | } | |
12e41d03 DL |
535 | } |
536 | ||
537 | static void forward_on(struct pim_upstream *up) | |
538 | { | |
d62a17ae | 539 | struct listnode *chnode; |
540 | struct listnode *chnextnode; | |
541 | struct pim_ifchannel *ch = NULL; | |
12e41d03 | 542 | |
d62a17ae | 543 | /* scan (S,G) state */ |
544 | for (ALL_LIST_ELEMENTS(up->ifchannels, chnode, chnextnode, ch)) { | |
545 | if (pim_macro_chisin_oiflist(ch)) | |
546 | pim_forward_start(ch); | |
12e41d03 | 547 | |
d62a17ae | 548 | } /* scan iface channel list */ |
12e41d03 DL |
549 | } |
550 | ||
551 | static void forward_off(struct pim_upstream *up) | |
552 | { | |
d62a17ae | 553 | struct listnode *chnode; |
554 | struct listnode *chnextnode; | |
555 | struct pim_ifchannel *ch; | |
12e41d03 | 556 | |
d62a17ae | 557 | /* scan per-interface (S,G) state */ |
558 | for (ALL_LIST_ELEMENTS(up->ifchannels, chnode, chnextnode, ch)) { | |
12e41d03 | 559 | |
86696f7b | 560 | pim_forward_stop(ch); |
12e41d03 | 561 | |
d62a17ae | 562 | } /* scan iface channel list */ |
12e41d03 DL |
563 | } |
564 | ||
46a9ea8b | 565 | int pim_upstream_could_register(struct pim_upstream *up) |
bb6e291f | 566 | { |
d62a17ae | 567 | struct pim_interface *pim_ifp = NULL; |
568 | ||
8eeaef9b AK |
569 | /* FORCE_PIMREG is a generic flag to let an app like VxLAN-AA register |
570 | * a source on an upstream entry even if the source is not directly | |
571 | * connected on the IIF. | |
572 | */ | |
573 | if (PIM_UPSTREAM_FLAG_TEST_FORCE_PIMREG(up->flags)) | |
574 | return 1; | |
575 | ||
d62a17ae | 576 | if (up->rpf.source_nexthop.interface) |
577 | pim_ifp = up->rpf.source_nexthop.interface->info; | |
578 | else { | |
23fc858a | 579 | if (PIM_DEBUG_PIM_TRACE) |
15569c58 DA |
580 | zlog_debug("%s: up %s RPF is not present", __func__, |
581 | up->sg_str); | |
d62a17ae | 582 | } |
bb6e291f | 583 | |
d62a17ae | 584 | if (pim_ifp && PIM_I_am_DR(pim_ifp) |
585 | && pim_if_connected_to_source(up->rpf.source_nexthop.interface, | |
586 | up->sg.src)) | |
587 | return 1; | |
bb6e291f | 588 | |
d62a17ae | 589 | return 0; |
bb6e291f DS |
590 | } |
591 | ||
0437e105 | 592 | /* Source registration is suppressed for SSM groups. When the SSM range changes |
15a5dafe | 593 | * we re-revaluate register setup for existing upstream entries */ |
9b29ea95 | 594 | void pim_upstream_register_reevaluate(struct pim_instance *pim) |
15a5dafe | 595 | { |
d62a17ae | 596 | struct pim_upstream *up; |
597 | ||
dd3364cb | 598 | frr_each (rb_pim_upstream, &pim->upstream_head, up) { |
d62a17ae | 599 | /* If FHR is set CouldRegister is True. Also check if the flow |
600 | * is actually active; if it is not kat setup will trigger | |
601 | * source | |
602 | * registration whenever the flow becomes active. */ | |
ec836533 AK |
603 | if (!PIM_UPSTREAM_FLAG_TEST_FHR(up->flags) || |
604 | !pim_upstream_is_kat_running(up)) | |
d62a17ae | 605 | continue; |
606 | ||
6f439a70 | 607 | if (pim_is_grp_ssm(pim, up->sg.grp)) { |
d62a17ae | 608 | /* clear the register state for SSM groups */ |
609 | if (up->reg_state != PIM_REG_NOINFO) { | |
610 | if (PIM_DEBUG_PIM_EVENTS) | |
611 | zlog_debug( | |
612 | "Clear register for %s as G is now SSM", | |
613 | up->sg_str); | |
614 | /* remove regiface from the OIL if it is there*/ | |
615 | pim_channel_del_oif(up->channel_oil, | |
9b29ea95 | 616 | pim->regiface, |
1b249e70 AK |
617 | PIM_OIF_FLAG_PROTO_PIM, |
618 | __func__); | |
d62a17ae | 619 | up->reg_state = PIM_REG_NOINFO; |
620 | } | |
621 | } else { | |
622 | /* register ASM sources with the RP */ | |
623 | if (up->reg_state == PIM_REG_NOINFO) { | |
624 | if (PIM_DEBUG_PIM_EVENTS) | |
625 | zlog_debug( | |
626 | "Register %s as G is now ASM", | |
627 | up->sg_str); | |
628 | pim_channel_add_oif(up->channel_oil, | |
9b29ea95 | 629 | pim->regiface, |
1b249e70 AK |
630 | PIM_OIF_FLAG_PROTO_PIM, |
631 | __func__); | |
d62a17ae | 632 | up->reg_state = PIM_REG_JOIN; |
633 | } | |
634 | } | |
635 | } | |
15a5dafe | 636 | } |
637 | ||
69e3538c AK |
638 | /* RFC7761, Section 4.2 “Data Packet Forwarding Rules” says we should |
639 | * forward a S - | |
640 | * 1. along the SPT if SPTbit is set | |
641 | * 2. and along the RPT if SPTbit is not set | |
642 | * If forwarding is hw accelerated i.e. control and dataplane components | |
643 | * are separate you may not be able to reliably set SPT bit on intermediate | |
644 | * routers while still fowarding on the (S,G,rpt). | |
645 | * | |
646 | * This macro is a slight deviation on the RFC and uses "traffic-agnostic" | |
647 | * criteria to decide between using the RPT vs. SPT for forwarding. | |
648 | */ | |
649 | void pim_upstream_update_use_rpt(struct pim_upstream *up, | |
650 | bool update_mroute) | |
651 | { | |
652 | bool old_use_rpt; | |
653 | bool new_use_rpt; | |
654 | ||
2a27f13b | 655 | if (pim_addr_is_any(up->sg.src)) |
69e3538c AK |
656 | return; |
657 | ||
658 | old_use_rpt = !!PIM_UPSTREAM_FLAG_TEST_USE_RPT(up->flags); | |
659 | ||
660 | /* We will use the SPT (IIF=RPF_interface(S) if - | |
661 | * 1. We have decided to join the SPT | |
662 | * 2. We are FHR | |
663 | * 3. Source is directly connected | |
664 | * 4. We are RP (parent's IIF is lo or vrf-device) | |
665 | * In all other cases the source will stay along the RPT and | |
666 | * IIF=RPF_interface(RP). | |
667 | */ | |
668 | if (up->join_state == PIM_UPSTREAM_JOINED || | |
669 | PIM_UPSTREAM_FLAG_TEST_FHR(up->flags) || | |
670 | pim_if_connected_to_source( | |
671 | up->rpf.source_nexthop.interface, | |
672 | up->sg.src) || | |
673 | /* XXX - need to switch this to a more efficient | |
674 | * lookup API | |
675 | */ | |
676 | I_am_RP(up->pim, up->sg.grp)) | |
677 | /* use SPT */ | |
678 | PIM_UPSTREAM_FLAG_UNSET_USE_RPT(up->flags); | |
679 | else | |
680 | /* use RPT */ | |
681 | PIM_UPSTREAM_FLAG_SET_USE_RPT(up->flags); | |
682 | ||
683 | new_use_rpt = !!PIM_UPSTREAM_FLAG_TEST_USE_RPT(up->flags); | |
684 | if (old_use_rpt != new_use_rpt) { | |
685 | if (PIM_DEBUG_PIM_EVENTS) | |
686 | zlog_debug("%s switched from %s to %s", | |
687 | up->sg_str, | |
688 | old_use_rpt?"RPT":"SPT", | |
689 | new_use_rpt?"RPT":"SPT"); | |
690 | if (update_mroute) | |
691 | pim_upstream_mroute_add(up->channel_oil, __func__); | |
692 | } | |
693 | } | |
694 | ||
a749b900 AK |
695 | /* some events like RP change require re-evaluation of SGrpt across |
696 | * all groups | |
697 | */ | |
698 | void pim_upstream_reeval_use_rpt(struct pim_instance *pim) | |
699 | { | |
700 | struct pim_upstream *up; | |
a749b900 | 701 | |
dd3364cb | 702 | frr_each (rb_pim_upstream, &pim->upstream_head, up) { |
2a27f13b | 703 | if (pim_addr_is_any(up->sg.src)) |
a749b900 AK |
704 | continue; |
705 | ||
706 | pim_upstream_update_use_rpt(up, true /*update_mroute*/); | |
707 | } | |
708 | } | |
709 | ||
1eca8576 | 710 | void pim_upstream_switch(struct pim_instance *pim, struct pim_upstream *up, |
d62a17ae | 711 | enum pim_upstream_state new_state) |
12e41d03 | 712 | { |
d62a17ae | 713 | enum pim_upstream_state old_state = up->join_state; |
714 | ||
01adb431 | 715 | if (pim_addr_is_any(up->upstream_addr)) { |
47e3ce59 | 716 | if (PIM_DEBUG_PIM_EVENTS) |
15569c58 DA |
717 | zlog_debug("%s: RPF not configured for %s", __func__, |
718 | up->sg_str); | |
957d93ea SP |
719 | return; |
720 | } | |
721 | ||
722 | if (!up->rpf.source_nexthop.interface) { | |
47e3ce59 | 723 | if (PIM_DEBUG_PIM_EVENTS) |
15569c58 DA |
724 | zlog_debug("%s: RP not reachable for %s", __func__, |
725 | up->sg_str); | |
957d93ea SP |
726 | return; |
727 | } | |
728 | ||
d62a17ae | 729 | if (PIM_DEBUG_PIM_EVENTS) { |
730 | zlog_debug("%s: PIM_UPSTREAM_%s: (S,G) old: %s new: %s", | |
15569c58 | 731 | __func__, up->sg_str, |
d62a17ae | 732 | pim_upstream_state2str(up->join_state), |
733 | pim_upstream_state2str(new_state)); | |
734 | } | |
735 | ||
736 | up->join_state = new_state; | |
737 | if (old_state != new_state) | |
738 | up->state_transition = pim_time_monotonic_sec(); | |
739 | ||
740 | pim_upstream_update_assert_tracking_desired(up); | |
741 | ||
742 | if (new_state == PIM_UPSTREAM_JOINED) { | |
b077f571 | 743 | pim_upstream_inherited_olist_decide(pim, up); |
d62a17ae | 744 | if (old_state != PIM_UPSTREAM_JOINED) { |
745 | int old_fhr = PIM_UPSTREAM_FLAG_TEST_FHR(up->flags); | |
b077f571 | 746 | |
1eca8576 | 747 | pim_msdp_up_join_state_changed(pim, up); |
d62a17ae | 748 | if (pim_upstream_could_register(up)) { |
749 | PIM_UPSTREAM_FLAG_SET_FHR(up->flags); | |
750 | if (!old_fhr | |
751 | && PIM_UPSTREAM_FLAG_TEST_SRC_STREAM( | |
752 | up->flags)) { | |
753 | pim_upstream_keep_alive_timer_start( | |
19b807ca | 754 | up, pim->keep_alive_time); |
d62a17ae | 755 | pim_register_join(up); |
756 | } | |
757 | } else { | |
758 | pim_upstream_send_join(up); | |
759 | join_timer_start(up); | |
760 | } | |
d62a17ae | 761 | } |
c692bd2a AK |
762 | if (old_state != new_state) |
763 | pim_upstream_update_use_rpt(up, true /*update_mroute*/); | |
d62a17ae | 764 | } else { |
c692bd2a AK |
765 | bool old_use_rpt; |
766 | bool new_use_rpt; | |
767 | bool send_xg_jp = false; | |
d62a17ae | 768 | |
769 | forward_off(up); | |
195427c8 MR |
770 | /* |
771 | * RFC 4601 Sec 4.5.7: | |
772 | * JoinDesired(S,G) -> False, set SPTbit to false. | |
773 | */ | |
2a27f13b | 774 | if (!pim_addr_is_any(up->sg.src)) |
195427c8 MR |
775 | up->sptbit = PIM_UPSTREAM_SPTBIT_FALSE; |
776 | ||
d62a17ae | 777 | if (old_state == PIM_UPSTREAM_JOINED) |
1eca8576 | 778 | pim_msdp_up_join_state_changed(pim, up); |
d62a17ae | 779 | |
c692bd2a AK |
780 | if (old_state != new_state) { |
781 | old_use_rpt = | |
782 | !!PIM_UPSTREAM_FLAG_TEST_USE_RPT(up->flags); | |
783 | pim_upstream_update_use_rpt(up, true /*update_mroute*/); | |
784 | new_use_rpt = | |
785 | !!PIM_UPSTREAM_FLAG_TEST_USE_RPT(up->flags); | |
786 | if (new_use_rpt && | |
787 | (new_use_rpt != old_use_rpt) && | |
788 | up->parent) | |
789 | /* we have decided to switch from the SPT back | |
790 | * to the RPT which means we need to cancel | |
791 | * any previously sent SGrpt prunes immediately | |
792 | */ | |
793 | send_xg_jp = true; | |
794 | } | |
795 | ||
d62a17ae | 796 | /* IHR, Trigger SGRpt on *,G IIF to prune S,G from RPT towards |
797 | RP. | |
798 | If I am RP for G then send S,G prune to its IIF. */ | |
c692bd2a AK |
799 | if (pim_upstream_is_sg_rpt(up) && up->parent && |
800 | !I_am_RP(pim, up->sg.grp)) | |
801 | send_xg_jp = true; | |
0a4497f1 | 802 | |
803 | pim_jp_agg_single_upstream_send(&up->rpf, up, 0 /* prune */); | |
c692bd2a AK |
804 | |
805 | if (send_xg_jp) { | |
d62a17ae | 806 | if (PIM_DEBUG_PIM_TRACE_DETAIL) |
807 | zlog_debug( | |
c692bd2a | 808 | "re-join RPT; *,G IIF %s S,G IIF %s ", |
957d93ea SP |
809 | up->parent->rpf.source_nexthop.interface ? |
810 | up->parent->rpf.source_nexthop.interface->name | |
811 | : "Unknown", | |
812 | up->rpf.source_nexthop.interface ? | |
813 | up->rpf.source_nexthop.interface->name : | |
814 | "Unknown"); | |
d62a17ae | 815 | pim_jp_agg_single_upstream_send(&up->parent->rpf, |
816 | up->parent, | |
817 | 1 /* (W,G) Join */); | |
c692bd2a | 818 | } |
d62a17ae | 819 | join_timer_stop(up); |
820 | } | |
12e41d03 DL |
821 | } |
822 | ||
dd3364cb DS |
823 | int pim_upstream_compare(const struct pim_upstream *up1, |
824 | const struct pim_upstream *up2) | |
03417ccd | 825 | { |
62f59b58 | 826 | return pim_sgaddr_cmp(up1->sg, up2->sg); |
03417ccd DS |
827 | } |
828 | ||
6a5de0ad AK |
829 | void pim_upstream_fill_static_iif(struct pim_upstream *up, |
830 | struct interface *incoming) | |
831 | { | |
832 | up->rpf.source_nexthop.interface = incoming; | |
833 | ||
834 | /* reset other parameters to matched a connected incoming interface */ | |
fd3af229 | 835 | pim_addr_to_prefix(&up->rpf.source_nexthop.mrib_nexthop_addr, |
836 | PIMADDR_ANY); | |
6a5de0ad AK |
837 | up->rpf.source_nexthop.mrib_metric_preference = |
838 | ZEBRA_CONNECT_DISTANCE_DEFAULT; | |
839 | up->rpf.source_nexthop.mrib_route_metric = 0; | |
840 | up->rpf.rpf_addr.family = AF_INET; | |
841 | up->rpf.rpf_addr.u.prefix4.s_addr = PIM_NET_INADDR_ANY; | |
842 | ||
843 | } | |
844 | ||
2002dcdb | 845 | static struct pim_upstream *pim_upstream_new(struct pim_instance *pim, |
6fff2cc6 | 846 | pim_sgaddr *sg, |
2002dcdb | 847 | struct interface *incoming, |
0885a9f1 DS |
848 | int flags, |
849 | struct pim_ifchannel *ch) | |
12e41d03 | 850 | { |
d62a17ae | 851 | enum pim_rpf_result rpf_result; |
852 | struct pim_interface *pim_ifp; | |
853 | struct pim_upstream *up; | |
854 | ||
855 | up = XCALLOC(MTYPE_PIM_UPSTREAM, sizeof(*up)); | |
d62a17ae | 856 | |
69e3538c | 857 | up->pim = pim; |
d62a17ae | 858 | up->sg = *sg; |
9bace5c2 | 859 | snprintfrr(up->sg_str, sizeof(up->sg_str), "%pSG", sg); |
0885a9f1 DS |
860 | if (ch) |
861 | ch->upstream = up; | |
862 | ||
dd3364cb | 863 | rb_pim_upstream_add(&pim->upstream_head, up); |
732c209c SP |
864 | /* Set up->upstream_addr as INADDR_ANY, if RP is not |
865 | * configured and retain the upstream data structure | |
866 | */ | |
d9c9a9ee DS |
867 | if (!pim_rp_set_upstream_addr(pim, &up->upstream_addr, sg->src, |
868 | sg->grp)) { | |
23fc858a | 869 | if (PIM_DEBUG_PIM_TRACE) |
d62a17ae | 870 | zlog_debug("%s: Received a (*,G) with no RP configured", |
15569c58 | 871 | __func__); |
d62a17ae | 872 | } |
873 | ||
9b29ea95 | 874 | up->parent = pim_upstream_find_parent(pim, up); |
2a27f13b | 875 | if (pim_addr_is_any(up->sg.src)) { |
d62a17ae | 876 | up->sources = list_new(); |
dd3364cb DS |
877 | up->sources->cmp = |
878 | (int (*)(void *, void *))pim_upstream_compare; | |
d62a17ae | 879 | } else |
880 | up->sources = NULL; | |
881 | ||
9b29ea95 | 882 | pim_upstream_find_new_children(pim, up); |
d62a17ae | 883 | up->flags = flags; |
884 | up->ref_count = 1; | |
885 | up->t_join_timer = NULL; | |
886 | up->t_ka_timer = NULL; | |
887 | up->t_rs_timer = NULL; | |
888 | up->t_msdp_reg_timer = NULL; | |
889 | up->join_state = PIM_UPSTREAM_NOTJOINED; | |
890 | up->reg_state = PIM_REG_NOINFO; | |
891 | up->state_transition = pim_time_monotonic_sec(); | |
15569c58 | 892 | up->channel_oil = pim_channel_oil_add(pim, &up->sg, __func__); |
d62a17ae | 893 | up->sptbit = PIM_UPSTREAM_SPTBIT_FALSE; |
894 | ||
895 | up->rpf.source_nexthop.interface = NULL; | |
fd3af229 | 896 | pim_addr_to_prefix(&up->rpf.source_nexthop.mrib_nexthop_addr, |
897 | PIMADDR_ANY); | |
d62a17ae | 898 | up->rpf.source_nexthop.mrib_metric_preference = |
d17612dd | 899 | router->infinite_assert_metric.metric_preference; |
d62a17ae | 900 | up->rpf.source_nexthop.mrib_route_metric = |
d17612dd | 901 | router->infinite_assert_metric.route_metric; |
fd3af229 | 902 | pim_addr_to_prefix(&up->rpf.rpf_addr, PIMADDR_ANY); |
d62a17ae | 903 | up->ifchannels = list_new(); |
904 | up->ifchannels->cmp = (int (*)(void *, void *))pim_ifchannel_compare; | |
905 | ||
2a27f13b | 906 | if (!pim_addr_is_any(up->sg.src)) { |
9b29ea95 | 907 | wheel_add_item(pim->upstream_sg_wheel, up); |
d62a17ae | 908 | |
ec85b101 AK |
909 | /* Inherit the DF role from the parent (*, G) entry for |
910 | * VxLAN BUM groups | |
911 | */ | |
912 | if (up->parent | |
913 | && PIM_UPSTREAM_FLAG_TEST_MLAG_VXLAN(up->parent->flags) | |
914 | && PIM_UPSTREAM_FLAG_TEST_MLAG_NON_DF(up->parent->flags)) { | |
915 | PIM_UPSTREAM_FLAG_SET_MLAG_NON_DF(up->flags); | |
916 | if (PIM_DEBUG_VXLAN) | |
917 | zlog_debug( | |
918 | "upstream %s inherited mlag non-df flag from parent", | |
919 | up->sg_str); | |
920 | } | |
921 | } | |
922 | ||
02434c43 DS |
923 | if (PIM_UPSTREAM_FLAG_TEST_STATIC_IIF(up->flags) |
924 | || PIM_UPSTREAM_FLAG_TEST_SRC_NOCACHE(up->flags)) { | |
6a5de0ad AK |
925 | pim_upstream_fill_static_iif(up, incoming); |
926 | pim_ifp = up->rpf.source_nexthop.interface->info; | |
927 | assert(pim_ifp); | |
35d6862d AK |
928 | pim_upstream_update_use_rpt(up, |
929 | false /*update_mroute*/); | |
7984af18 | 930 | pim_upstream_mroute_iif_update(up->channel_oil, __func__); |
02434c43 DS |
931 | |
932 | if (PIM_UPSTREAM_FLAG_TEST_SRC_NOCACHE(up->flags)) | |
933 | pim_upstream_keep_alive_timer_start( | |
934 | up, pim->keep_alive_time); | |
01adb431 | 935 | } else if (!pim_addr_is_any(up->upstream_addr)) { |
35d6862d AK |
936 | pim_upstream_update_use_rpt(up, |
937 | false /*update_mroute*/); | |
8c55c132 | 938 | rpf_result = pim_rpf_update(pim, up, NULL, __func__); |
732c209c | 939 | if (rpf_result == PIM_RPF_FAILURE) { |
23fc858a | 940 | if (PIM_DEBUG_PIM_TRACE) |
732c209c SP |
941 | zlog_debug( |
942 | "%s: Attempting to create upstream(%s), Unable to RPF for source", | |
15569c58 | 943 | __func__, up->sg_str); |
d62a17ae | 944 | } |
945 | ||
732c209c | 946 | if (up->rpf.source_nexthop.interface) { |
35d6862d AK |
947 | pim_upstream_mroute_iif_update(up->channel_oil, |
948 | __func__); | |
732c209c | 949 | } |
d62a17ae | 950 | } |
951 | ||
05ca004b AK |
952 | /* send the entry to the MLAG peer */ |
953 | /* XXX - duplicate send is possible here if pim_rpf_update | |
954 | * successfully resolved the nexthop | |
955 | */ | |
22c35834 SK |
956 | if (pim_up_mlag_is_local(up) |
957 | || PIM_UPSTREAM_FLAG_TEST_MLAG_INTERFACE(up->flags)) | |
05ca004b AK |
958 | pim_mlag_up_local_add(pim, up); |
959 | ||
23fc858a | 960 | if (PIM_DEBUG_PIM_TRACE) { |
d62a17ae | 961 | zlog_debug( |
fd3af229 | 962 | "%s: Created Upstream %s upstream_addr %pPAs ref count %d increment", |
ee2bbf7c | 963 | __func__, up->sg_str, &up->upstream_addr, |
15569c58 | 964 | up->ref_count); |
d62a17ae | 965 | } |
966 | ||
967 | return up; | |
12e41d03 DL |
968 | } |
969 | ||
05ca004b AK |
970 | uint32_t pim_up_mlag_local_cost(struct pim_upstream *up) |
971 | { | |
22c35834 SK |
972 | if (!(pim_up_mlag_is_local(up)) |
973 | && !(up->flags & PIM_UPSTREAM_FLAG_MASK_MLAG_INTERFACE)) | |
05ca004b AK |
974 | return router->infinite_assert_metric.route_metric; |
975 | ||
f03999ca AK |
976 | if ((up->rpf.source_nexthop.interface == |
977 | up->pim->vxlan.peerlink_rif) && | |
978 | (up->rpf.source_nexthop.mrib_route_metric < | |
979 | (router->infinite_assert_metric.route_metric - | |
980 | PIM_UPSTREAM_MLAG_PEERLINK_PLUS_METRIC))) | |
981 | return up->rpf.source_nexthop.mrib_route_metric + | |
982 | PIM_UPSTREAM_MLAG_PEERLINK_PLUS_METRIC; | |
983 | ||
05ca004b AK |
984 | return up->rpf.source_nexthop.mrib_route_metric; |
985 | } | |
986 | ||
987 | uint32_t pim_up_mlag_peer_cost(struct pim_upstream *up) | |
988 | { | |
989 | if (!(up->flags & PIM_UPSTREAM_FLAG_MASK_MLAG_PEER)) | |
990 | return router->infinite_assert_metric.route_metric; | |
991 | ||
992 | return up->mlag.peer_mrib_metric; | |
993 | } | |
994 | ||
6fff2cc6 | 995 | struct pim_upstream *pim_upstream_find(struct pim_instance *pim, pim_sgaddr *sg) |
12e41d03 | 996 | { |
d62a17ae | 997 | struct pim_upstream lookup; |
998 | struct pim_upstream *up = NULL; | |
12e41d03 | 999 | |
d62a17ae | 1000 | lookup.sg = *sg; |
dd3364cb | 1001 | up = rb_pim_upstream_find(&pim->upstream_head, &lookup); |
d62a17ae | 1002 | return up; |
12e41d03 DL |
1003 | } |
1004 | ||
6fff2cc6 DL |
1005 | struct pim_upstream *pim_upstream_find_or_add(pim_sgaddr *sg, |
1006 | struct interface *incoming, | |
1007 | int flags, const char *name) | |
e711cd3c | 1008 | { |
69e3538c | 1009 | struct pim_interface *pim_ifp = incoming->info; |
d62a17ae | 1010 | |
69e3538c AK |
1011 | return (pim_upstream_add(pim_ifp->pim, sg, incoming, flags, name, |
1012 | NULL)); | |
e711cd3c DS |
1013 | } |
1014 | ||
d62a17ae | 1015 | void pim_upstream_ref(struct pim_upstream *up, int flags, const char *name) |
1bf16443 | 1016 | { |
05ca004b AK |
1017 | /* if a local MLAG reference is being created we need to send the mroute |
1018 | * to the peer | |
1019 | */ | |
1020 | if (!PIM_UPSTREAM_FLAG_TEST_MLAG_VXLAN(up->flags) && | |
1021 | PIM_UPSTREAM_FLAG_TEST_MLAG_VXLAN(flags)) { | |
1022 | PIM_UPSTREAM_FLAG_SET_MLAG_VXLAN(up->flags); | |
1023 | pim_mlag_up_local_add(up->pim, up); | |
1024 | } | |
1025 | ||
69e3538c AK |
1026 | /* when we go from non-FHR to FHR we need to re-eval traffic |
1027 | * forwarding path | |
1028 | */ | |
1029 | if (!PIM_UPSTREAM_FLAG_TEST_FHR(up->flags) && | |
1030 | PIM_UPSTREAM_FLAG_TEST_FHR(flags)) { | |
1031 | PIM_UPSTREAM_FLAG_SET_FHR(up->flags); | |
1032 | pim_upstream_update_use_rpt(up, true /*update_mroute*/); | |
1033 | } | |
1034 | ||
41a115e4 AK |
1035 | /* re-eval joinDesired; clearing peer-msdp-sa flag can |
1036 | * cause JD to change | |
1037 | */ | |
1038 | if (!PIM_UPSTREAM_FLAG_TEST_SRC_MSDP(up->flags) && | |
1039 | PIM_UPSTREAM_FLAG_TEST_SRC_MSDP(flags)) { | |
1040 | PIM_UPSTREAM_FLAG_SET_SRC_MSDP(up->flags); | |
1041 | pim_upstream_update_join_desired(up->pim, up); | |
1042 | } | |
1043 | ||
d62a17ae | 1044 | up->flags |= flags; |
1045 | ++up->ref_count; | |
23fc858a | 1046 | if (PIM_DEBUG_PIM_TRACE) |
d62a17ae | 1047 | zlog_debug("%s(%s): upstream %s ref count %d increment", |
15569c58 | 1048 | __func__, name, up->sg_str, up->ref_count); |
1bf16443 | 1049 | } |
1050 | ||
6fff2cc6 | 1051 | struct pim_upstream *pim_upstream_add(struct pim_instance *pim, pim_sgaddr *sg, |
d62a17ae | 1052 | struct interface *incoming, int flags, |
0885a9f1 DS |
1053 | const char *name, |
1054 | struct pim_ifchannel *ch) | |
12e41d03 | 1055 | { |
d62a17ae | 1056 | struct pim_upstream *up = NULL; |
1057 | int found = 0; | |
9b29ea95 | 1058 | |
2002dcdb | 1059 | up = pim_upstream_find(pim, sg); |
d62a17ae | 1060 | if (up) { |
1061 | pim_upstream_ref(up, flags, name); | |
1062 | found = 1; | |
1063 | } else { | |
0885a9f1 | 1064 | up = pim_upstream_new(pim, sg, incoming, flags, ch); |
d62a17ae | 1065 | } |
1066 | ||
23fc858a | 1067 | if (PIM_DEBUG_PIM_TRACE) { |
2dbe669b DA |
1068 | if (up) |
1069 | zlog_debug("%s(%s): %s, iif %pFX (%s) found: %d: ref_count: %d", | |
15569c58 | 1070 | __func__, name, |
2dbe669b | 1071 | up->sg_str, &up->rpf.rpf_addr, up->rpf.source_nexthop.interface ? |
957d93ea | 1072 | up->rpf.source_nexthop.interface->name : "Unknown" , |
55785900 | 1073 | found, up->ref_count); |
2dbe669b | 1074 | else |
98a81d2b DL |
1075 | zlog_debug("%s(%s): (%pSG) failure to create", __func__, |
1076 | name, sg); | |
d62a17ae | 1077 | } |
12e41d03 | 1078 | |
d62a17ae | 1079 | return up; |
12e41d03 DL |
1080 | } |
1081 | ||
a53a9b3e AK |
1082 | /* |
1083 | * Passed in up must be the upstream for ch. starch is NULL if no | |
1084 | * information | |
1085 | * This function is copied over from | |
1086 | * pim_upstream_evaluate_join_desired_interface but limited to | |
1087 | * parent (*,G)'s includes/joins. | |
1088 | */ | |
1089 | int pim_upstream_eval_inherit_if(struct pim_upstream *up, | |
1090 | struct pim_ifchannel *ch, | |
1091 | struct pim_ifchannel *starch) | |
1092 | { | |
1093 | /* if there is an explicit prune for this interface we cannot | |
1094 | * add it to the OIL | |
1095 | */ | |
1096 | if (ch) { | |
1097 | if (PIM_IF_FLAG_TEST_S_G_RPT(ch->flags)) | |
1098 | return 0; | |
1099 | } | |
1100 | ||
1101 | /* Check if the OIF can be inherited fron the (*,G) entry | |
1102 | */ | |
1103 | if (starch) { | |
1104 | if (!pim_macro_ch_lost_assert(starch) | |
1105 | && pim_macro_chisin_joins_or_include(starch)) | |
1106 | return 1; | |
1107 | } | |
1108 | ||
1109 | return 0; | |
1110 | } | |
1111 | ||
c8fc07cb DS |
1112 | /* |
1113 | * Passed in up must be the upstream for ch. starch is NULL if no | |
1114 | * information | |
1115 | */ | |
d62a17ae | 1116 | int pim_upstream_evaluate_join_desired_interface(struct pim_upstream *up, |
1117 | struct pim_ifchannel *ch, | |
1118 | struct pim_ifchannel *starch) | |
7a3ddda5 | 1119 | { |
d62a17ae | 1120 | if (ch) { |
1121 | if (PIM_IF_FLAG_TEST_S_G_RPT(ch->flags)) | |
1122 | return 0; | |
1123 | ||
1124 | if (!pim_macro_ch_lost_assert(ch) | |
1125 | && pim_macro_chisin_joins_or_include(ch)) | |
1126 | return 1; | |
1127 | } | |
1128 | ||
1129 | /* | |
1130 | * joins (*,G) | |
1131 | */ | |
1132 | if (starch) { | |
a53a9b3e AK |
1133 | /* XXX: check on this with donald |
1134 | * we are looking for PIM_IF_FLAG_MASK_S_G_RPT in | |
1135 | * upstream flags? | |
1136 | */ | |
1137 | #if 0 | |
d62a17ae | 1138 | if (PIM_IF_FLAG_TEST_S_G_RPT(starch->upstream->flags)) |
1139 | return 0; | |
a53a9b3e | 1140 | #endif |
d62a17ae | 1141 | |
1142 | if (!pim_macro_ch_lost_assert(starch) | |
1143 | && pim_macro_chisin_joins_or_include(starch)) | |
1144 | return 1; | |
1145 | } | |
1146 | ||
1147 | return 0; | |
7a3ddda5 DS |
1148 | } |
1149 | ||
a53a9b3e AK |
1150 | /* Returns true if immediate OIL is empty and is used to evaluate |
1151 | * JoinDesired. See pim_upstream_evaluate_join_desired. | |
12e41d03 | 1152 | */ |
a53a9b3e | 1153 | static bool pim_upstream_empty_immediate_olist(struct pim_instance *pim, |
9b29ea95 | 1154 | struct pim_upstream *up) |
12e41d03 | 1155 | { |
d62a17ae | 1156 | struct interface *ifp; |
a53a9b3e | 1157 | struct pim_ifchannel *ch; |
12e41d03 | 1158 | |
451fda4f | 1159 | FOR_ALL_INTERFACES (pim->vrf, ifp) { |
d62a17ae | 1160 | if (!ifp->info) |
1161 | continue; | |
c8fc07cb | 1162 | |
d62a17ae | 1163 | ch = pim_ifchannel_find(ifp, &up->sg); |
a53a9b3e | 1164 | if (!ch) |
d62a17ae | 1165 | continue; |
12e41d03 | 1166 | |
a53a9b3e AK |
1167 | /* If we have even one immediate OIF we can return with |
1168 | * not-empty | |
1169 | */ | |
1170 | if (pim_upstream_evaluate_join_desired_interface(up, ch, | |
1171 | NULL /* starch */)) | |
1172 | return false; | |
d62a17ae | 1173 | } /* scan iface channel list */ |
12e41d03 | 1174 | |
a53a9b3e AK |
1175 | /* immediate_oil is empty */ |
1176 | return true; | |
1177 | } | |
1178 | ||
41a115e4 AK |
1179 | |
1180 | static inline bool pim_upstream_is_msdp_peer_sa(struct pim_upstream *up) | |
1181 | { | |
1182 | return PIM_UPSTREAM_FLAG_TEST_SRC_MSDP(up->flags); | |
1183 | } | |
1184 | ||
a53a9b3e AK |
1185 | /* |
1186 | * bool JoinDesired(*,G) { | |
1187 | * if (immediate_olist(*,G) != NULL) | |
1188 | * return TRUE | |
1189 | * else | |
1190 | * return FALSE | |
1191 | * } | |
1192 | * | |
1193 | * bool JoinDesired(S,G) { | |
1194 | * return( immediate_olist(S,G) != NULL | |
1195 | * OR ( KeepaliveTimer(S,G) is running | |
1196 | * AND inherited_olist(S,G) != NULL ) ) | |
1197 | * } | |
1198 | */ | |
286bbbec | 1199 | bool pim_upstream_evaluate_join_desired(struct pim_instance *pim, |
a53a9b3e AK |
1200 | struct pim_upstream *up) |
1201 | { | |
1202 | bool empty_imm_oil; | |
1203 | bool empty_inh_oil; | |
1204 | ||
1205 | empty_imm_oil = pim_upstream_empty_immediate_olist(pim, up); | |
1206 | ||
1207 | /* (*,G) */ | |
2a27f13b | 1208 | if (pim_addr_is_any(up->sg.src)) |
a53a9b3e AK |
1209 | return !empty_imm_oil; |
1210 | ||
1211 | /* (S,G) */ | |
1212 | if (!empty_imm_oil) | |
1213 | return true; | |
1214 | empty_inh_oil = pim_upstream_empty_inherited_olist(up); | |
1215 | if (!empty_inh_oil && | |
1216 | (pim_upstream_is_kat_running(up) || | |
41a115e4 | 1217 | pim_upstream_is_msdp_peer_sa(up))) |
a53a9b3e AK |
1218 | return true; |
1219 | ||
1220 | return false; | |
12e41d03 DL |
1221 | } |
1222 | ||
1223 | /* | |
1224 | See also pim_upstream_evaluate_join_desired() above. | |
1225 | */ | |
9b29ea95 DS |
1226 | void pim_upstream_update_join_desired(struct pim_instance *pim, |
1227 | struct pim_upstream *up) | |
12e41d03 | 1228 | { |
d62a17ae | 1229 | int was_join_desired; /* boolean */ |
1230 | int is_join_desired; /* boolean */ | |
1231 | ||
1232 | was_join_desired = PIM_UPSTREAM_FLAG_TEST_DR_JOIN_DESIRED(up->flags); | |
1233 | ||
9b29ea95 | 1234 | is_join_desired = pim_upstream_evaluate_join_desired(pim, up); |
d62a17ae | 1235 | if (is_join_desired) |
1236 | PIM_UPSTREAM_FLAG_SET_DR_JOIN_DESIRED(up->flags); | |
1237 | else | |
1238 | PIM_UPSTREAM_FLAG_UNSET_DR_JOIN_DESIRED(up->flags); | |
1239 | ||
1240 | /* switched from false to true */ | |
47e3ce59 | 1241 | if (is_join_desired && (up->join_state == PIM_UPSTREAM_NOTJOINED)) { |
1eca8576 | 1242 | pim_upstream_switch(pim, up, PIM_UPSTREAM_JOINED); |
d62a17ae | 1243 | return; |
1244 | } | |
1245 | ||
1246 | /* switched from true to false */ | |
1247 | if (!is_join_desired && was_join_desired) { | |
1eca8576 | 1248 | pim_upstream_switch(pim, up, PIM_UPSTREAM_NOTJOINED); |
d62a17ae | 1249 | return; |
1250 | } | |
12e41d03 DL |
1251 | } |
1252 | ||
1253 | /* | |
1254 | RFC 4601 4.5.7. Sending (S,G) Join/Prune Messages | |
1255 | Transitions from Joined State | |
1256 | RPF'(S,G) GenID changes | |
1257 | ||
1258 | The upstream (S,G) state machine remains in Joined state. If the | |
1259 | Join Timer is set to expire in more than t_override seconds, reset | |
1260 | it so that it expires after t_override seconds. | |
1261 | */ | |
9b29ea95 | 1262 | void pim_upstream_rpf_genid_changed(struct pim_instance *pim, |
101b3104 | 1263 | pim_addr neigh_addr) |
12e41d03 | 1264 | { |
d62a17ae | 1265 | struct pim_upstream *up; |
1266 | ||
1267 | /* | |
1268 | * Scan all (S,G) upstreams searching for RPF'(S,G)=neigh_addr | |
1269 | */ | |
dd3364cb | 1270 | frr_each (rb_pim_upstream, &pim->upstream_head, up) { |
101b3104 DL |
1271 | pim_addr rpf_addr; |
1272 | ||
1273 | rpf_addr = pim_addr_from_prefix(&up->rpf.rpf_addr); | |
1274 | ||
1275 | if (PIM_DEBUG_PIM_TRACE) | |
d62a17ae | 1276 | zlog_debug( |
101b3104 | 1277 | "%s: matching neigh=%pPA against upstream (S,G)=%s[%s] joined=%d rpf_addr=%pPA", |
9bb93fa0 DL |
1278 | __func__, &neigh_addr, up->sg_str, |
1279 | pim->vrf->name, | |
d62a17ae | 1280 | up->join_state == PIM_UPSTREAM_JOINED, |
101b3104 | 1281 | &rpf_addr); |
d62a17ae | 1282 | |
1283 | /* consider only (S,G) upstream in Joined state */ | |
1284 | if (up->join_state != PIM_UPSTREAM_JOINED) | |
1285 | continue; | |
1286 | ||
1287 | /* match RPF'(S,G)=neigh_addr */ | |
101b3104 | 1288 | if (pim_addr_cmp(rpf_addr, neigh_addr)) |
d62a17ae | 1289 | continue; |
1290 | ||
1291 | pim_upstream_join_timer_decrease_to_t_override( | |
1292 | "RPF'(S,G) GenID change", up); | |
1293 | } | |
12e41d03 DL |
1294 | } |
1295 | ||
1296 | ||
1297 | void pim_upstream_rpf_interface_changed(struct pim_upstream *up, | |
1298 | struct interface *old_rpf_ifp) | |
1299 | { | |
d62a17ae | 1300 | struct listnode *chnode; |
1301 | struct listnode *chnextnode; | |
1302 | struct pim_ifchannel *ch; | |
1303 | ||
1304 | /* search all ifchannels */ | |
1305 | for (ALL_LIST_ELEMENTS(up->ifchannels, chnode, chnextnode, ch)) { | |
1306 | if (ch->ifassert_state == PIM_IFASSERT_I_AM_LOSER) { | |
1307 | if ( | |
1308 | /* RPF_interface(S) was NOT I */ | |
1309 | (old_rpf_ifp == ch->interface) && | |
1310 | /* RPF_interface(S) stopped being I */ | |
1311 | (ch->upstream->rpf.source_nexthop | |
957d93ea SP |
1312 | .interface) && |
1313 | (ch->upstream->rpf.source_nexthop | |
1314 | .interface != ch->interface)) { | |
d62a17ae | 1315 | assert_action_a5(ch); |
1316 | } | |
1317 | } /* PIM_IFASSERT_I_AM_LOSER */ | |
1318 | ||
1319 | pim_ifchannel_update_assert_tracking_desired(ch); | |
1320 | } | |
12e41d03 DL |
1321 | } |
1322 | ||
1323 | void pim_upstream_update_could_assert(struct pim_upstream *up) | |
1324 | { | |
d62a17ae | 1325 | struct listnode *chnode; |
1326 | struct listnode *chnextnode; | |
1327 | struct pim_ifchannel *ch; | |
1328 | ||
1329 | /* scan per-interface (S,G) state */ | |
1330 | for (ALL_LIST_ELEMENTS(up->ifchannels, chnode, chnextnode, ch)) { | |
1331 | pim_ifchannel_update_could_assert(ch); | |
1332 | } /* scan iface channel list */ | |
12e41d03 DL |
1333 | } |
1334 | ||
1335 | void pim_upstream_update_my_assert_metric(struct pim_upstream *up) | |
1336 | { | |
d62a17ae | 1337 | struct listnode *chnode; |
1338 | struct listnode *chnextnode; | |
1339 | struct pim_ifchannel *ch; | |
12e41d03 | 1340 | |
d62a17ae | 1341 | /* scan per-interface (S,G) state */ |
1342 | for (ALL_LIST_ELEMENTS(up->ifchannels, chnode, chnextnode, ch)) { | |
1343 | pim_ifchannel_update_my_assert_metric(ch); | |
12e41d03 | 1344 | |
d62a17ae | 1345 | } /* scan iface channel list */ |
12e41d03 DL |
1346 | } |
1347 | ||
1348 | static void pim_upstream_update_assert_tracking_desired(struct pim_upstream *up) | |
1349 | { | |
d62a17ae | 1350 | struct listnode *chnode; |
1351 | struct listnode *chnextnode; | |
1352 | struct pim_interface *pim_ifp; | |
1353 | struct pim_ifchannel *ch; | |
1354 | ||
1355 | /* scan per-interface (S,G) state */ | |
1356 | for (ALL_LIST_ELEMENTS(up->ifchannels, chnode, chnextnode, ch)) { | |
1357 | if (!ch->interface) | |
1358 | continue; | |
1359 | pim_ifp = ch->interface->info; | |
1360 | if (!pim_ifp) | |
1361 | continue; | |
1362 | ||
1363 | pim_ifchannel_update_assert_tracking_desired(ch); | |
1364 | ||
1365 | } /* scan iface channel list */ | |
12e41d03 | 1366 | } |
f14248dd | 1367 | |
1bf16443 | 1368 | /* When kat is stopped CouldRegister goes to false so we need to |
1369 | * transition the (S, G) on FHR to NI state and remove reg tunnel | |
1370 | * from the OIL */ | |
9b29ea95 DS |
1371 | static void pim_upstream_fhr_kat_expiry(struct pim_instance *pim, |
1372 | struct pim_upstream *up) | |
1bf16443 | 1373 | { |
d62a17ae | 1374 | if (!PIM_UPSTREAM_FLAG_TEST_FHR(up->flags)) |
1375 | return; | |
1376 | ||
23fc858a | 1377 | if (PIM_DEBUG_PIM_TRACE) |
d62a17ae | 1378 | zlog_debug("kat expired on %s; clear fhr reg state", |
1379 | up->sg_str); | |
1380 | ||
1381 | /* stop reg-stop timer */ | |
1382 | THREAD_OFF(up->t_rs_timer); | |
1383 | /* remove regiface from the OIL if it is there*/ | |
9b29ea95 | 1384 | pim_channel_del_oif(up->channel_oil, pim->regiface, |
1b249e70 | 1385 | PIM_OIF_FLAG_PROTO_PIM, __func__); |
d62a17ae | 1386 | /* clear the register state */ |
1387 | up->reg_state = PIM_REG_NOINFO; | |
1388 | PIM_UPSTREAM_FLAG_UNSET_FHR(up->flags); | |
1bf16443 | 1389 | } |
1390 | ||
1391 | /* When kat is started CouldRegister can go to true. And if it does we | |
1392 | * need to transition the (S, G) on FHR to JOINED state and add reg tunnel | |
1393 | * to the OIL */ | |
1394 | static void pim_upstream_fhr_kat_start(struct pim_upstream *up) | |
1395 | { | |
d62a17ae | 1396 | if (pim_upstream_could_register(up)) { |
23fc858a | 1397 | if (PIM_DEBUG_PIM_TRACE) |
d62a17ae | 1398 | zlog_debug( |
1399 | "kat started on %s; set fhr reg state to joined", | |
1400 | up->sg_str); | |
1401 | ||
1402 | PIM_UPSTREAM_FLAG_SET_FHR(up->flags); | |
1403 | if (up->reg_state == PIM_REG_NOINFO) | |
1404 | pim_register_join(up); | |
69e3538c | 1405 | pim_upstream_update_use_rpt(up, true /*update_mroute*/); |
d62a17ae | 1406 | } |
1bf16443 | 1407 | } |
1408 | ||
f14248dd DS |
1409 | /* |
1410 | * On an RP, the PMBR value must be cleared when the | |
1411 | * Keepalive Timer expires | |
1bf16443 | 1412 | * KAT expiry indicates that flow is inactive. If the flow was created or |
1413 | * maintained by activity now is the time to deref it. | |
f14248dd | 1414 | */ |
ff459c36 AK |
1415 | struct pim_upstream *pim_upstream_keep_alive_timer_proc( |
1416 | struct pim_upstream *up) | |
f14248dd | 1417 | { |
8e5f97e3 | 1418 | struct pim_instance *pim; |
d62a17ae | 1419 | |
8e5f97e3 | 1420 | pim = up->channel_oil->pim; |
d62a17ae | 1421 | |
820b4a40 AK |
1422 | if (PIM_UPSTREAM_FLAG_TEST_DISABLE_KAT_EXPIRY(up->flags)) { |
1423 | /* if the router is a PIM vxlan encapsulator we prevent expiry | |
1424 | * of KAT as the mroute is pre-setup without any traffic | |
1425 | */ | |
1426 | pim_upstream_keep_alive_timer_start(up, pim->keep_alive_time); | |
ff459c36 | 1427 | return up; |
820b4a40 AK |
1428 | } |
1429 | ||
8e5f97e3 | 1430 | if (I_am_RP(pim, up->sg.grp)) { |
d62a17ae | 1431 | pim_br_clear_pmbr(&up->sg); |
1432 | /* | |
1433 | * We need to do more here :) | |
1434 | * But this is the start. | |
1435 | */ | |
1436 | } | |
1437 | ||
1438 | /* source is no longer active - pull the SA from MSDP's cache */ | |
472ad383 | 1439 | pim_msdp_sa_local_del(pim, &up->sg); |
d62a17ae | 1440 | |
a53a9b3e AK |
1441 | /* JoinDesired can change when KAT is started or stopped */ |
1442 | pim_upstream_update_join_desired(pim, up); | |
1443 | ||
d62a17ae | 1444 | /* if entry was created because of activity we need to deref it */ |
1445 | if (PIM_UPSTREAM_FLAG_TEST_SRC_STREAM(up->flags)) { | |
8e5f97e3 | 1446 | pim_upstream_fhr_kat_expiry(pim, up); |
23fc858a | 1447 | if (PIM_DEBUG_PIM_TRACE) |
996c9314 LB |
1448 | zlog_debug( |
1449 | "kat expired on %s[%s]; remove stream reference", | |
1450 | up->sg_str, pim->vrf->name); | |
d62a17ae | 1451 | PIM_UPSTREAM_FLAG_UNSET_SRC_STREAM(up->flags); |
e3e532dd | 1452 | |
1453 | /* Return if upstream entry got deleted.*/ | |
15569c58 | 1454 | if (!pim_upstream_del(pim, up, __func__)) |
e3e532dd | 1455 | return NULL; |
1456 | } | |
02434c43 DS |
1457 | if (PIM_UPSTREAM_FLAG_TEST_SRC_NOCACHE(up->flags)) { |
1458 | PIM_UPSTREAM_FLAG_UNSET_SRC_NOCACHE(up->flags); | |
1459 | ||
15569c58 | 1460 | if (!pim_upstream_del(pim, up, __func__)) |
02434c43 DS |
1461 | return NULL; |
1462 | } | |
1463 | ||
e3e532dd | 1464 | /* upstream reference would have been added to track the local |
1465 | * membership if it is LHR. We have to clear it when KAT expires. | |
1466 | * Otherwise would result in stale entry with uncleared ref count. | |
1467 | */ | |
1468 | if (PIM_UPSTREAM_FLAG_TEST_SRC_LHR(up->flags)) { | |
8022df6a DS |
1469 | struct pim_upstream *parent = up->parent; |
1470 | ||
d62a17ae | 1471 | PIM_UPSTREAM_FLAG_UNSET_SRC_LHR(up->flags); |
15569c58 | 1472 | up = pim_upstream_del(pim, up, __func__); |
8022df6a DS |
1473 | |
1474 | if (parent) { | |
996c9314 LB |
1475 | pim_jp_agg_single_upstream_send(&parent->rpf, parent, |
1476 | true); | |
8022df6a | 1477 | } |
d62a17ae | 1478 | } |
1479 | ||
ff459c36 AK |
1480 | return up; |
1481 | } | |
cc9f21da | 1482 | static void pim_upstream_keep_alive_timer(struct thread *t) |
ff459c36 AK |
1483 | { |
1484 | struct pim_upstream *up; | |
1485 | ||
1486 | up = THREAD_ARG(t); | |
1487 | ||
ea6d91c8 AK |
1488 | /* pull the stats and re-check */ |
1489 | if (pim_upstream_sg_running_proc(up)) | |
1490 | /* kat was restarted because of new activity */ | |
cc9f21da | 1491 | return; |
ea6d91c8 | 1492 | |
ff459c36 | 1493 | pim_upstream_keep_alive_timer_proc(up); |
f14248dd DS |
1494 | } |
1495 | ||
d62a17ae | 1496 | void pim_upstream_keep_alive_timer_start(struct pim_upstream *up, uint32_t time) |
f14248dd | 1497 | { |
d62a17ae | 1498 | if (!PIM_UPSTREAM_FLAG_TEST_SRC_STREAM(up->flags)) { |
23fc858a | 1499 | if (PIM_DEBUG_PIM_TRACE) |
d62a17ae | 1500 | zlog_debug("kat start on %s with no stream reference", |
1501 | up->sg_str); | |
1502 | } | |
1503 | THREAD_OFF(up->t_ka_timer); | |
36417fcc DS |
1504 | thread_add_timer(router->master, pim_upstream_keep_alive_timer, up, |
1505 | time, &up->t_ka_timer); | |
d62a17ae | 1506 | |
1507 | /* any time keepalive is started against a SG we will have to | |
1508 | * re-evaluate our active source database */ | |
1509 | pim_msdp_sa_local_update(up); | |
a53a9b3e AK |
1510 | /* JoinDesired can change when KAT is started or stopped */ |
1511 | pim_upstream_update_join_desired(up->pim, up); | |
1bf16443 | 1512 | } |
1513 | ||
1514 | /* MSDP on RP needs to know if a source is registerable to this RP */ | |
cc9f21da | 1515 | static void pim_upstream_msdp_reg_timer(struct thread *t) |
1bf16443 | 1516 | { |
472ad383 DS |
1517 | struct pim_upstream *up = THREAD_ARG(t); |
1518 | struct pim_instance *pim = up->channel_oil->pim; | |
1bf16443 | 1519 | |
d62a17ae | 1520 | /* source is no longer active - pull the SA from MSDP's cache */ |
472ad383 | 1521 | pim_msdp_sa_local_del(pim, &up->sg); |
1bf16443 | 1522 | } |
cc9f21da | 1523 | |
d62a17ae | 1524 | void pim_upstream_msdp_reg_timer_start(struct pim_upstream *up) |
1bf16443 | 1525 | { |
d62a17ae | 1526 | THREAD_OFF(up->t_msdp_reg_timer); |
36417fcc | 1527 | thread_add_timer(router->master, pim_upstream_msdp_reg_timer, up, |
d62a17ae | 1528 | PIM_MSDP_REG_RXED_PERIOD, &up->t_msdp_reg_timer); |
1bf16443 | 1529 | |
d62a17ae | 1530 | pim_msdp_sa_local_update(up); |
f14248dd | 1531 | } |
cb40b272 DS |
1532 | |
1533 | /* | |
1534 | * 4.2.1 Last-Hop Switchover to the SPT | |
1535 | * | |
1536 | * In Sparse-Mode PIM, last-hop routers join the shared tree towards the | |
1537 | * RP. Once traffic from sources to joined groups arrives at a last-hop | |
1538 | * router, it has the option of switching to receive the traffic on a | |
1539 | * shortest path tree (SPT). | |
1540 | * | |
1541 | * The decision for a router to switch to the SPT is controlled as | |
1542 | * follows: | |
1543 | * | |
1544 | * void | |
1545 | * CheckSwitchToSpt(S,G) { | |
1546 | * if ( ( pim_include(*,G) (-) pim_exclude(S,G) | |
1547 | * (+) pim_include(S,G) != NULL ) | |
1548 | * AND SwitchToSptDesired(S,G) ) { | |
1549 | * # Note: Restarting the KAT will result in the SPT switch | |
1550 | * set KeepaliveTimer(S,G) to Keepalive_Period | |
1551 | * } | |
1552 | * } | |
1553 | * | |
1554 | * SwitchToSptDesired(S,G) is a policy function that is implementation | |
1555 | * defined. An "infinite threshold" policy can be implemented by making | |
1556 | * SwitchToSptDesired(S,G) return false all the time. A "switch on | |
1557 | * first packet" policy can be implemented by making | |
1558 | * SwitchToSptDesired(S,G) return true once a single packet has been | |
1559 | * received for the source and group. | |
1560 | */ | |
2ef4ed70 | 1561 | int pim_upstream_switch_to_spt_desired_on_rp(struct pim_instance *pim, |
6fff2cc6 | 1562 | pim_sgaddr *sg) |
cb40b272 | 1563 | { |
8e5f97e3 | 1564 | if (I_am_RP(pim, sg->grp)) |
d62a17ae | 1565 | return 1; |
a3b58b4a | 1566 | |
d62a17ae | 1567 | return 0; |
cb40b272 | 1568 | } |
d7259eac | 1569 | |
d62a17ae | 1570 | int pim_upstream_is_sg_rpt(struct pim_upstream *up) |
80d9c3a0 | 1571 | { |
d62a17ae | 1572 | struct listnode *chnode; |
1573 | struct pim_ifchannel *ch; | |
f21597f0 | 1574 | |
d62a17ae | 1575 | for (ALL_LIST_ELEMENTS_RO(up->ifchannels, chnode, ch)) { |
1576 | if (PIM_IF_FLAG_TEST_S_G_RPT(ch->flags)) | |
1577 | return 1; | |
1578 | } | |
e43b8697 | 1579 | |
d62a17ae | 1580 | return 0; |
80d9c3a0 | 1581 | } |
3a66b17b DS |
1582 | /* |
1583 | * After receiving a packet set SPTbit: | |
1584 | * void | |
1585 | * Update_SPTbit(S,G,iif) { | |
1586 | * if ( iif == RPF_interface(S) | |
2951a7a4 QY |
1587 | * AND JoinDesired(S,G) == true |
1588 | * AND ( DirectlyConnected(S) == true | |
3a66b17b DS |
1589 | * OR RPF_interface(S) != RPF_interface(RP(G)) |
1590 | * OR inherited_olist(S,G,rpt) == NULL | |
1591 | * OR ( ( RPF'(S,G) == RPF'(*,G) ) AND | |
1592 | * ( RPF'(S,G) != NULL ) ) | |
1593 | * OR ( I_Am_Assert_Loser(S,G,iif) ) { | |
2951a7a4 | 1594 | * Set SPTbit(S,G) to true |
3a66b17b DS |
1595 | * } |
1596 | * } | |
1597 | */ | |
d62a17ae | 1598 | void pim_upstream_set_sptbit(struct pim_upstream *up, |
1599 | struct interface *incoming) | |
3a66b17b | 1600 | { |
d62a17ae | 1601 | struct pim_upstream *starup = up->parent; |
1602 | ||
1603 | // iif == RPF_interfvace(S) | |
1604 | if (up->rpf.source_nexthop.interface != incoming) { | |
23fc858a | 1605 | if (PIM_DEBUG_PIM_TRACE) |
d62a17ae | 1606 | zlog_debug( |
1607 | "%s: Incoming Interface: %s is different than RPF_interface(S) %s", | |
15569c58 | 1608 | __func__, incoming->name, |
d62a17ae | 1609 | up->rpf.source_nexthop.interface->name); |
1610 | return; | |
1611 | } | |
1612 | ||
2951a7a4 | 1613 | // AND JoinDesired(S,G) == true |
6f0f014f | 1614 | if (!pim_upstream_evaluate_join_desired(up->channel_oil->pim, up)) { |
23fc858a | 1615 | if (PIM_DEBUG_PIM_TRACE) |
15569c58 DA |
1616 | zlog_debug("%s: %s Join is not Desired", __func__, |
1617 | up->sg_str); | |
6f0f014f DS |
1618 | return; |
1619 | } | |
d62a17ae | 1620 | |
2951a7a4 | 1621 | // DirectlyConnected(S) == true |
d62a17ae | 1622 | if (pim_if_connected_to_source(up->rpf.source_nexthop.interface, |
1623 | up->sg.src)) { | |
23fc858a | 1624 | if (PIM_DEBUG_PIM_TRACE) |
d62a17ae | 1625 | zlog_debug("%s: %s is directly connected to the source", |
15569c58 | 1626 | __func__, up->sg_str); |
d62a17ae | 1627 | up->sptbit = PIM_UPSTREAM_SPTBIT_TRUE; |
1628 | return; | |
1629 | } | |
1630 | ||
1631 | // OR RPF_interface(S) != RPF_interface(RP(G)) | |
1632 | if (!starup | |
1633 | || up->rpf.source_nexthop | |
1634 | .interface != starup->rpf.source_nexthop.interface) { | |
2de05c60 DS |
1635 | struct pim_upstream *starup = up->parent; |
1636 | ||
23fc858a | 1637 | if (PIM_DEBUG_PIM_TRACE) |
d62a17ae | 1638 | zlog_debug( |
1639 | "%s: %s RPF_interface(S) != RPF_interface(RP(G))", | |
15569c58 | 1640 | __func__, up->sg_str); |
d62a17ae | 1641 | up->sptbit = PIM_UPSTREAM_SPTBIT_TRUE; |
2de05c60 DS |
1642 | |
1643 | pim_jp_agg_single_upstream_send(&starup->rpf, starup, true); | |
d62a17ae | 1644 | return; |
1645 | } | |
1646 | ||
1647 | // OR inherited_olist(S,G,rpt) == NULL | |
1648 | if (pim_upstream_is_sg_rpt(up) | |
1649 | && pim_upstream_empty_inherited_olist(up)) { | |
23fc858a | 1650 | if (PIM_DEBUG_PIM_TRACE) |
d62a17ae | 1651 | zlog_debug("%s: %s OR inherited_olist(S,G,rpt) == NULL", |
15569c58 | 1652 | __func__, up->sg_str); |
d62a17ae | 1653 | up->sptbit = PIM_UPSTREAM_SPTBIT_TRUE; |
1654 | return; | |
1655 | } | |
1656 | ||
1657 | // OR ( ( RPF'(S,G) == RPF'(*,G) ) AND | |
1658 | // ( RPF'(S,G) != NULL ) ) | |
1659 | if (up->parent && pim_rpf_is_same(&up->rpf, &up->parent->rpf)) { | |
23fc858a | 1660 | if (PIM_DEBUG_PIM_TRACE) |
d62a17ae | 1661 | zlog_debug("%s: %s RPF'(S,G) is the same as RPF'(*,G)", |
15569c58 | 1662 | __func__, up->sg_str); |
d62a17ae | 1663 | up->sptbit = PIM_UPSTREAM_SPTBIT_TRUE; |
1664 | return; | |
1665 | } | |
1666 | ||
1667 | return; | |
3a66b17b | 1668 | } |
80d9c3a0 | 1669 | |
d62a17ae | 1670 | const char *pim_upstream_state2str(enum pim_upstream_state join_state) |
d7259eac | 1671 | { |
d62a17ae | 1672 | switch (join_state) { |
1673 | case PIM_UPSTREAM_NOTJOINED: | |
1674 | return "NotJoined"; | |
d62a17ae | 1675 | case PIM_UPSTREAM_JOINED: |
1676 | return "Joined"; | |
d62a17ae | 1677 | } |
1678 | return "Unknown"; | |
d7259eac | 1679 | } |
627ed2a3 | 1680 | |
c35b7e6b QY |
1681 | const char *pim_reg_state2str(enum pim_reg_state reg_state, char *state_str, |
1682 | size_t state_str_len) | |
e0e127b0 | 1683 | { |
d62a17ae | 1684 | switch (reg_state) { |
1685 | case PIM_REG_NOINFO: | |
c35b7e6b | 1686 | strlcpy(state_str, "RegNoInfo", state_str_len); |
d62a17ae | 1687 | break; |
1688 | case PIM_REG_JOIN: | |
c35b7e6b | 1689 | strlcpy(state_str, "RegJoined", state_str_len); |
d62a17ae | 1690 | break; |
1691 | case PIM_REG_JOIN_PENDING: | |
c35b7e6b | 1692 | strlcpy(state_str, "RegJoinPend", state_str_len); |
d62a17ae | 1693 | break; |
1694 | case PIM_REG_PRUNE: | |
c35b7e6b | 1695 | strlcpy(state_str, "RegPrune", state_str_len); |
d62a17ae | 1696 | break; |
d62a17ae | 1697 | } |
1698 | return state_str; | |
e0e127b0 | 1699 | } |
1700 | ||
cc9f21da | 1701 | static void pim_upstream_register_stop_timer(struct thread *t) |
627ed2a3 | 1702 | { |
d62a17ae | 1703 | struct pim_interface *pim_ifp; |
8e5f97e3 | 1704 | struct pim_instance *pim; |
d62a17ae | 1705 | struct pim_upstream *up; |
d62a17ae | 1706 | up = THREAD_ARG(t); |
8e5f97e3 | 1707 | pim = up->channel_oil->pim; |
d62a17ae | 1708 | |
23fc858a | 1709 | if (PIM_DEBUG_PIM_TRACE) { |
d62a17ae | 1710 | char state_str[PIM_REG_STATE_STR_LEN]; |
8dbdb215 | 1711 | zlog_debug("%s: (S,G)=%s[%s] upstream register stop timer %s", |
15569c58 DA |
1712 | __func__, up->sg_str, pim->vrf->name, |
1713 | pim_reg_state2str(up->reg_state, state_str, | |
1714 | sizeof(state_str))); | |
d62a17ae | 1715 | } |
1716 | ||
1717 | switch (up->reg_state) { | |
1718 | case PIM_REG_JOIN_PENDING: | |
1719 | up->reg_state = PIM_REG_JOIN; | |
8e5f97e3 | 1720 | pim_channel_add_oif(up->channel_oil, pim->regiface, |
1b249e70 AK |
1721 | PIM_OIF_FLAG_PROTO_PIM, |
1722 | __func__); | |
2951a7a4 | 1723 | pim_vxlan_update_sg_reg_state(pim, up, true /*reg_join*/); |
d62a17ae | 1724 | break; |
1725 | case PIM_REG_JOIN: | |
1726 | break; | |
1727 | case PIM_REG_PRUNE: | |
cf575d09 | 1728 | /* This is equalent to Couldreg -> False */ |
957d93ea | 1729 | if (!up->rpf.source_nexthop.interface) { |
23fc858a | 1730 | if (PIM_DEBUG_PIM_TRACE) |
957d93ea | 1731 | zlog_debug("%s: up %s RPF is not present", |
15569c58 | 1732 | __func__, up->sg_str); |
cf575d09 | 1733 | up->reg_state = PIM_REG_NOINFO; |
cc9f21da | 1734 | return; |
957d93ea SP |
1735 | } |
1736 | ||
d62a17ae | 1737 | pim_ifp = up->rpf.source_nexthop.interface->info; |
1738 | if (!pim_ifp) { | |
23fc858a | 1739 | if (PIM_DEBUG_PIM_TRACE) |
d62a17ae | 1740 | zlog_debug( |
1741 | "%s: Interface: %s is not configured for pim", | |
15569c58 | 1742 | __func__, |
d62a17ae | 1743 | up->rpf.source_nexthop.interface->name); |
cc9f21da | 1744 | return; |
d62a17ae | 1745 | } |
1746 | up->reg_state = PIM_REG_JOIN_PENDING; | |
1747 | pim_upstream_start_register_stop_timer(up, 1); | |
1748 | ||
1749 | if (((up->channel_oil->cc.lastused / 100) | |
2f5b0028 | 1750 | > pim->keep_alive_time) |
d9c9a9ee | 1751 | && (I_am_RP(pim_ifp->pim, up->sg.grp))) { |
23fc858a | 1752 | if (PIM_DEBUG_PIM_TRACE) |
d62a17ae | 1753 | zlog_debug( |
1754 | "%s: Stop sending the register, because I am the RP and we haven't seen a packet in a while", | |
15569c58 | 1755 | __func__); |
cc9f21da | 1756 | return; |
d62a17ae | 1757 | } |
aea1f845 | 1758 | pim_null_register_send(up); |
d62a17ae | 1759 | break; |
e1d1b1de | 1760 | case PIM_REG_NOINFO: |
d62a17ae | 1761 | break; |
70e7fda8 | 1762 | } |
627ed2a3 DS |
1763 | } |
1764 | ||
d62a17ae | 1765 | void pim_upstream_start_register_stop_timer(struct pim_upstream *up, |
1766 | int null_register) | |
627ed2a3 | 1767 | { |
d62a17ae | 1768 | uint32_t time; |
1769 | ||
50478845 | 1770 | THREAD_OFF(up->t_rs_timer); |
d62a17ae | 1771 | |
1772 | if (!null_register) { | |
e8b7548c CH |
1773 | uint32_t lower = (0.5 * router->register_suppress_time); |
1774 | uint32_t upper = (1.5 * router->register_suppress_time); | |
1775 | time = lower + (frr_weak_random() % (upper - lower + 1)); | |
1776 | /* Make sure we don't wrap around */ | |
1777 | if (time >= router->register_probe_time) | |
1778 | time -= router->register_probe_time; | |
1779 | else | |
1780 | time = 0; | |
d62a17ae | 1781 | } else |
e8b7548c | 1782 | time = router->register_probe_time; |
d62a17ae | 1783 | |
23fc858a | 1784 | if (PIM_DEBUG_PIM_TRACE) { |
d62a17ae | 1785 | zlog_debug( |
1786 | "%s: (S,G)=%s Starting upstream register stop timer %d", | |
15569c58 | 1787 | __func__, up->sg_str, time); |
d62a17ae | 1788 | } |
36417fcc DS |
1789 | thread_add_timer(router->master, pim_upstream_register_stop_timer, up, |
1790 | time, &up->t_rs_timer); | |
627ed2a3 | 1791 | } |
4fdc8f36 | 1792 | |
9b29ea95 DS |
1793 | int pim_upstream_inherited_olist_decide(struct pim_instance *pim, |
1794 | struct pim_upstream *up) | |
4fdc8f36 | 1795 | { |
d62a17ae | 1796 | struct interface *ifp; |
d62a17ae | 1797 | struct pim_ifchannel *ch, *starch; |
d62a17ae | 1798 | struct pim_upstream *starup = up->parent; |
1799 | int output_intf = 0; | |
1800 | ||
46dd6edb | 1801 | if (!up->rpf.source_nexthop.interface) |
23fc858a | 1802 | if (PIM_DEBUG_PIM_TRACE) |
15569c58 DA |
1803 | zlog_debug("%s: up %s RPF is not present", __func__, |
1804 | up->sg_str); | |
d62a17ae | 1805 | |
451fda4f | 1806 | FOR_ALL_INTERFACES (pim->vrf, ifp) { |
22c35834 | 1807 | struct pim_interface *pim_ifp; |
d62a17ae | 1808 | if (!ifp->info) |
1809 | continue; | |
1810 | ||
1811 | ch = pim_ifchannel_find(ifp, &up->sg); | |
1812 | ||
1813 | if (starup) | |
1814 | starch = pim_ifchannel_find(ifp, &starup->sg); | |
1815 | else | |
1816 | starch = NULL; | |
1817 | ||
1818 | if (!ch && !starch) | |
1819 | continue; | |
1820 | ||
22c35834 SK |
1821 | pim_ifp = ifp->info; |
1822 | if (PIM_I_am_DualActive(pim_ifp) | |
1823 | && PIM_UPSTREAM_FLAG_TEST_MLAG_INTERFACE(up->flags) | |
1824 | && (PIM_UPSTREAM_FLAG_TEST_MLAG_NON_DF(up->flags) | |
1825 | || !PIM_UPSTREAM_FLAG_TEST_MLAG_PEER(up->flags))) | |
1826 | continue; | |
d62a17ae | 1827 | if (pim_upstream_evaluate_join_desired_interface(up, ch, |
1828 | starch)) { | |
9443810e | 1829 | int flag = 0; |
d62a17ae | 1830 | |
1831 | if (!ch) | |
1832 | flag = PIM_OIF_FLAG_PROTO_STAR; | |
9443810e SP |
1833 | else { |
1834 | if (PIM_IF_FLAG_TEST_PROTO_IGMP(ch->flags)) | |
1835 | flag = PIM_OIF_FLAG_PROTO_IGMP; | |
1836 | if (PIM_IF_FLAG_TEST_PROTO_PIM(ch->flags)) | |
1837 | flag |= PIM_OIF_FLAG_PROTO_PIM; | |
5428f5ac MR |
1838 | if (starch) |
1839 | flag |= PIM_OIF_FLAG_PROTO_STAR; | |
9443810e | 1840 | } |
d62a17ae | 1841 | |
1b249e70 AK |
1842 | pim_channel_add_oif(up->channel_oil, ifp, flag, |
1843 | __func__); | |
d62a17ae | 1844 | output_intf++; |
1845 | } | |
1846 | } | |
1847 | ||
1848 | return output_intf; | |
b5183fd1 DS |
1849 | } |
1850 | ||
1851 | /* | |
1852 | * For a given upstream, determine the inherited_olist | |
1853 | * and apply it. | |
1854 | * | |
1855 | * inherited_olist(S,G,rpt) = | |
1856 | * ( joins(*,*,RP(G)) (+) joins(*,G) (-) prunes(S,G,rpt) ) | |
1857 | * (+) ( pim_include(*,G) (-) pim_exclude(S,G)) | |
1858 | * (-) ( lost_assert(*,G) (+) lost_assert(S,G,rpt) ) | |
1859 | * | |
1860 | * inherited_olist(S,G) = | |
1861 | * inherited_olist(S,G,rpt) (+) | |
1862 | * joins(S,G) (+) pim_include(S,G) (-) lost_assert(S,G) | |
1863 | * | |
1864 | * return 1 if there are any output interfaces | |
1865 | * return 0 if there are not any output interfaces | |
1866 | */ | |
9b29ea95 DS |
1867 | int pim_upstream_inherited_olist(struct pim_instance *pim, |
1868 | struct pim_upstream *up) | |
b5183fd1 | 1869 | { |
9b29ea95 | 1870 | int output_intf = pim_upstream_inherited_olist_decide(pim, up); |
d62a17ae | 1871 | |
1872 | /* | |
1873 | * If we have output_intf switch state to Join and work like normal | |
1874 | * If we don't have an output_intf that means we are probably a | |
1875 | * switch on a stick so turn on forwarding to just accept the | |
1876 | * incoming packets so we don't bother the other stuff! | |
1877 | */ | |
a53a9b3e AK |
1878 | pim_upstream_update_join_desired(pim, up); |
1879 | ||
1880 | if (!output_intf) | |
d62a17ae | 1881 | forward_on(up); |
1882 | ||
1883 | return output_intf; | |
4fdc8f36 | 1884 | } |
d3dd1804 | 1885 | |
d62a17ae | 1886 | int pim_upstream_empty_inherited_olist(struct pim_upstream *up) |
80d9c3a0 | 1887 | { |
d62a17ae | 1888 | return pim_channel_oil_empty(up->channel_oil); |
80d9c3a0 DS |
1889 | } |
1890 | ||
d3dd1804 DS |
1891 | /* |
1892 | * When we have a new neighbor, | |
1893 | * find upstreams that don't have their rpf_addr | |
1894 | * set and see if the new neighbor allows | |
1895 | * the join to be sent | |
1896 | */ | |
9b29ea95 | 1897 | void pim_upstream_find_new_rpf(struct pim_instance *pim) |
d3dd1804 | 1898 | { |
d62a17ae | 1899 | struct pim_upstream *up; |
7ef66af9 AK |
1900 | struct pim_rpf old; |
1901 | enum pim_rpf_result rpf_result; | |
d62a17ae | 1902 | |
1903 | /* | |
1904 | * Scan all (S,G) upstreams searching for RPF'(S,G)=neigh_addr | |
1905 | */ | |
dd3364cb | 1906 | frr_each (rb_pim_upstream, &pim->upstream_head, up) { |
01adb431 | 1907 | if (pim_addr_is_any(up->upstream_addr)) { |
23fc858a | 1908 | if (PIM_DEBUG_PIM_TRACE) |
957d93ea | 1909 | zlog_debug( |
15569c58 DA |
1910 | "%s: RP not configured for Upstream %s", |
1911 | __func__, up->sg_str); | |
957d93ea SP |
1912 | continue; |
1913 | } | |
1914 | ||
d62a17ae | 1915 | if (pim_rpf_addr_is_inaddr_any(&up->rpf)) { |
23fc858a | 1916 | if (PIM_DEBUG_PIM_TRACE) |
d62a17ae | 1917 | zlog_debug( |
957d93ea | 1918 | "%s: Upstream %s without a path to send join, checking", |
15569c58 | 1919 | __func__, up->sg_str); |
075a475e AK |
1920 | old.source_nexthop.interface = |
1921 | up->rpf.source_nexthop.interface; | |
7ef66af9 | 1922 | rpf_result = pim_rpf_update(pim, up, &old, __func__); |
b36576e4 AK |
1923 | if (rpf_result == PIM_RPF_CHANGED || |
1924 | (rpf_result == PIM_RPF_FAILURE && | |
1925 | old.source_nexthop.interface)) | |
7ef66af9 AK |
1926 | pim_zebra_upstream_rpf_changed(pim, up, &old); |
1927 | /* update kernel multicast forwarding cache (MFC) */ | |
075a475e AK |
1928 | pim_upstream_mroute_iif_update(up->channel_oil, |
1929 | __func__); | |
d62a17ae | 1930 | } |
d3dd1804 | 1931 | } |
7ef66af9 | 1932 | pim_zebra_update_all_interfaces(pim); |
d3dd1804 | 1933 | } |
0f588989 | 1934 | |
d8b87afe | 1935 | unsigned int pim_upstream_hash_key(const void *arg) |
0f588989 | 1936 | { |
d8b87afe | 1937 | const struct pim_upstream *up = arg; |
0f588989 | 1938 | |
62f59b58 | 1939 | return pim_sgaddr_hash(up->sg, 0); |
0f588989 DS |
1940 | } |
1941 | ||
9b29ea95 | 1942 | void pim_upstream_terminate(struct pim_instance *pim) |
0f588989 | 1943 | { |
172e45dc DS |
1944 | struct pim_upstream *up; |
1945 | ||
dd3364cb | 1946 | while ((up = rb_pim_upstream_first(&pim->upstream_head))) { |
15569c58 | 1947 | pim_upstream_del(pim, up, __func__); |
172e45dc | 1948 | } |
0f588989 | 1949 | |
dd3364cb | 1950 | rb_pim_upstream_fini(&pim->upstream_head); |
0c68972d DS |
1951 | |
1952 | if (pim->upstream_sg_wheel) | |
1953 | wheel_delete(pim->upstream_sg_wheel); | |
1954 | pim->upstream_sg_wheel = NULL; | |
0f588989 DS |
1955 | } |
1956 | ||
74df8d6d | 1957 | bool pim_upstream_equal(const void *arg1, const void *arg2) |
0f588989 | 1958 | { |
d62a17ae | 1959 | const struct pim_upstream *up1 = (const struct pim_upstream *)arg1; |
1960 | const struct pim_upstream *up2 = (const struct pim_upstream *)arg2; | |
0f588989 | 1961 | |
62f59b58 | 1962 | return !pim_sgaddr_cmp(up1->sg, up2->sg); |
0f588989 DS |
1963 | } |
1964 | ||
1bf16443 | 1965 | /* rfc4601:section-4.2:"Data Packet Forwarding Rules" defines |
1966 | * the cases where kat has to be restarted on rxing traffic - | |
1967 | * | |
2951a7a4 | 1968 | * if( DirectlyConnected(S) == true AND iif == RPF_interface(S) ) { |
1bf16443 | 1969 | * set KeepaliveTimer(S,G) to Keepalive_Period |
1970 | * # Note: a register state transition or UpstreamJPState(S,G) | |
1971 | * # transition may happen as a result of restarting | |
1972 | * # KeepaliveTimer, and must be dealt with here. | |
1973 | * } | |
1974 | * if( iif == RPF_interface(S) AND UpstreamJPState(S,G) == Joined AND | |
1975 | * inherited_olist(S,G) != NULL ) { | |
1976 | * set KeepaliveTimer(S,G) to Keepalive_Period | |
1977 | * } | |
1978 | */ | |
1979 | static bool pim_upstream_kat_start_ok(struct pim_upstream *up) | |
1980 | { | |
cfa8f7eb AK |
1981 | struct channel_oil *c_oil = up->channel_oil; |
1982 | struct interface *ifp = up->rpf.source_nexthop.interface; | |
1983 | struct pim_interface *pim_ifp; | |
1984 | ||
1985 | /* "iif == RPF_interface(S)" check is not easy to do as the info | |
1986 | * we get from the kernel/ASIC is really a "lookup/key hit". | |
1987 | * So we will do an approximate check here to avoid starting KAT | |
1988 | * because of (S,G,rpt) forwarding on a non-LHR. | |
1989 | */ | |
1990 | if (!ifp) | |
1991 | return false; | |
1992 | ||
1993 | pim_ifp = ifp->info; | |
a9338fa4 | 1994 | if (pim_ifp->mroute_vif_index != *oil_parent(c_oil)) |
cfa8f7eb | 1995 | return false; |
8e5f97e3 | 1996 | |
a1be0939 | 1997 | if (pim_if_connected_to_source(up->rpf.source_nexthop.interface, |
d62a17ae | 1998 | up->sg.src)) { |
1999 | return true; | |
2000 | } | |
2001 | ||
2002 | if ((up->join_state == PIM_UPSTREAM_JOINED) | |
cfa8f7eb AK |
2003 | && !pim_upstream_empty_inherited_olist(up)) { |
2004 | return true; | |
d62a17ae | 2005 | } |
2006 | ||
2007 | return false; | |
1bf16443 | 2008 | } |
2009 | ||
ea6d91c8 | 2010 | static bool pim_upstream_sg_running_proc(struct pim_upstream *up) |
9c5e4d62 | 2011 | { |
ea6d91c8 AK |
2012 | bool rv = false; |
2013 | struct pim_instance *pim = up->pim; | |
d62a17ae | 2014 | |
ea6d91c8 AK |
2015 | if (!up->channel_oil->installed) |
2016 | return rv; | |
d62a17ae | 2017 | |
d62a17ae | 2018 | pim_mroute_update_counters(up->channel_oil); |
2019 | ||
2020 | // Have we seen packets? | |
2021 | if ((up->channel_oil->cc.oldpktcnt >= up->channel_oil->cc.pktcnt) | |
2022 | && (up->channel_oil->cc.lastused / 100 > 30)) { | |
23fc858a | 2023 | if (PIM_DEBUG_PIM_TRACE) { |
d62a17ae | 2024 | zlog_debug( |
8dbdb215 | 2025 | "%s[%s]: %s old packet count is equal or lastused is greater than 30, (%ld,%ld,%lld)", |
15569c58 | 2026 | __func__, up->sg_str, pim->vrf->name, |
d62a17ae | 2027 | up->channel_oil->cc.oldpktcnt, |
2028 | up->channel_oil->cc.pktcnt, | |
2029 | up->channel_oil->cc.lastused / 100); | |
2030 | } | |
ea6d91c8 | 2031 | return rv; |
e43b8697 | 2032 | } |
d62a17ae | 2033 | |
2034 | if (pim_upstream_kat_start_ok(up)) { | |
2035 | /* Add a source reference to the stream if | |
2036 | * one doesn't already exist */ | |
2037 | if (!PIM_UPSTREAM_FLAG_TEST_SRC_STREAM(up->flags)) { | |
23fc858a | 2038 | if (PIM_DEBUG_PIM_TRACE) |
d62a17ae | 2039 | zlog_debug( |
8dbdb215 DS |
2040 | "source reference created on kat restart %s[%s]", |
2041 | up->sg_str, pim->vrf->name); | |
d62a17ae | 2042 | |
15569c58 DA |
2043 | pim_upstream_ref(up, PIM_UPSTREAM_FLAG_MASK_SRC_STREAM, |
2044 | __func__); | |
d62a17ae | 2045 | PIM_UPSTREAM_FLAG_SET_SRC_STREAM(up->flags); |
2046 | pim_upstream_fhr_kat_start(up); | |
2047 | } | |
19b807ca | 2048 | pim_upstream_keep_alive_timer_start(up, pim->keep_alive_time); |
ea6d91c8 AK |
2049 | rv = true; |
2050 | } else if (PIM_UPSTREAM_FLAG_TEST_SRC_LHR(up->flags)) { | |
19b807ca | 2051 | pim_upstream_keep_alive_timer_start(up, pim->keep_alive_time); |
ea6d91c8 AK |
2052 | rv = true; |
2053 | } | |
d62a17ae | 2054 | |
957d93ea SP |
2055 | if ((up->sptbit != PIM_UPSTREAM_SPTBIT_TRUE) && |
2056 | (up->rpf.source_nexthop.interface)) { | |
d62a17ae | 2057 | pim_upstream_set_sptbit(up, up->rpf.source_nexthop.interface); |
850a9f99 | 2058 | } |
ea6d91c8 AK |
2059 | |
2060 | return rv; | |
2061 | } | |
2062 | ||
2063 | /* | |
2064 | * Code to check and see if we've received packets on a S,G mroute | |
2065 | * and if so to set the SPT bit appropriately | |
2066 | */ | |
2067 | static void pim_upstream_sg_running(void *arg) | |
2068 | { | |
2069 | struct pim_upstream *up = (struct pim_upstream *)arg; | |
2070 | struct pim_instance *pim = up->channel_oil->pim; | |
2071 | ||
2072 | // No packet can have arrived here if this is the case | |
2073 | if (!up->channel_oil->installed) { | |
2074 | if (PIM_DEBUG_TRACE) | |
2075 | zlog_debug("%s: %s%s is not installed in mroute", | |
2076 | __func__, up->sg_str, pim->vrf->name); | |
2077 | return; | |
2078 | } | |
2079 | ||
2080 | /* | |
2081 | * This is a bit of a hack | |
2082 | * We've noted that we should rescan but | |
2083 | * we've missed the window for doing so in | |
2084 | * pim_zebra.c for some reason. I am | |
2085 | * only doing this at this point in time | |
2086 | * to get us up and working for the moment | |
2087 | */ | |
2088 | if (up->channel_oil->oil_inherited_rescan) { | |
2089 | if (PIM_DEBUG_TRACE) | |
2090 | zlog_debug( | |
2091 | "%s: Handling unscanned inherited_olist for %s[%s]", | |
2092 | __func__, up->sg_str, pim->vrf->name); | |
2093 | pim_upstream_inherited_olist_decide(pim, up); | |
2094 | up->channel_oil->oil_inherited_rescan = 0; | |
2095 | } | |
2096 | ||
2097 | pim_upstream_sg_running_proc(up); | |
9c5e4d62 DS |
2098 | } |
2099 | ||
9b29ea95 | 2100 | void pim_upstream_add_lhr_star_pimreg(struct pim_instance *pim) |
a7b2b1e2 | 2101 | { |
d62a17ae | 2102 | struct pim_upstream *up; |
a7b2b1e2 | 2103 | |
dd3364cb | 2104 | frr_each (rb_pim_upstream, &pim->upstream_head, up) { |
2a27f13b | 2105 | if (!pim_addr_is_any(up->sg.src)) |
d62a17ae | 2106 | continue; |
a7b2b1e2 | 2107 | |
448139e7 | 2108 | if (!PIM_UPSTREAM_FLAG_TEST_CAN_BE_LHR(up->flags)) |
d62a17ae | 2109 | continue; |
a7b2b1e2 | 2110 | |
9b29ea95 | 2111 | pim_channel_add_oif(up->channel_oil, pim->regiface, |
1b249e70 | 2112 | PIM_OIF_FLAG_PROTO_IGMP, __func__); |
d62a17ae | 2113 | } |
a7b2b1e2 DS |
2114 | } |
2115 | ||
9b29ea95 DS |
2116 | void pim_upstream_spt_prefix_list_update(struct pim_instance *pim, |
2117 | struct prefix_list *pl) | |
df94f9a9 | 2118 | { |
d62a17ae | 2119 | const char *pname = prefix_list_name(pl); |
df94f9a9 | 2120 | |
9b29ea95 DS |
2121 | if (pim->spt.plist && strcmp(pim->spt.plist, pname) == 0) { |
2122 | pim_upstream_remove_lhr_star_pimreg(pim, pname); | |
d62a17ae | 2123 | } |
df94f9a9 DS |
2124 | } |
2125 | ||
2126 | /* | |
2127 | * nlist -> The new prefix list | |
2128 | * | |
2129 | * Per Group Application of pimreg to the OIL | |
2130 | * If the prefix list tells us DENY then | |
2131 | * we need to Switchover to SPT immediate | |
2132 | * so add the pimreg. | |
2133 | * If the prefix list tells us to ACCEPT than | |
2134 | * we need to Never do the SPT so remove | |
2135 | * the interface | |
2136 | * | |
2137 | */ | |
9b29ea95 DS |
2138 | void pim_upstream_remove_lhr_star_pimreg(struct pim_instance *pim, |
2139 | const char *nlist) | |
a7b2b1e2 | 2140 | { |
d62a17ae | 2141 | struct pim_upstream *up; |
d62a17ae | 2142 | struct prefix_list *np; |
2143 | struct prefix g; | |
2144 | enum prefix_list_type apply_new; | |
2145 | ||
c631920c | 2146 | np = prefix_list_lookup(PIM_AFI, nlist); |
d62a17ae | 2147 | |
dd3364cb | 2148 | frr_each (rb_pim_upstream, &pim->upstream_head, up) { |
2a27f13b | 2149 | if (!pim_addr_is_any(up->sg.src)) |
d62a17ae | 2150 | continue; |
2151 | ||
448139e7 | 2152 | if (!PIM_UPSTREAM_FLAG_TEST_CAN_BE_LHR(up->flags)) |
d62a17ae | 2153 | continue; |
2154 | ||
2155 | if (!nlist) { | |
9b29ea95 | 2156 | pim_channel_del_oif(up->channel_oil, pim->regiface, |
1b249e70 | 2157 | PIM_OIF_FLAG_PROTO_IGMP, __func__); |
d62a17ae | 2158 | continue; |
2159 | } | |
c631920c | 2160 | pim_addr_to_prefix(&g, up->sg.grp); |
d62a17ae | 2161 | apply_new = prefix_list_apply(np, &g); |
2162 | if (apply_new == PREFIX_DENY) | |
9b29ea95 | 2163 | pim_channel_add_oif(up->channel_oil, pim->regiface, |
1b249e70 AK |
2164 | PIM_OIF_FLAG_PROTO_IGMP, |
2165 | __func__); | |
d62a17ae | 2166 | else |
9b29ea95 | 2167 | pim_channel_del_oif(up->channel_oil, pim->regiface, |
1b249e70 | 2168 | PIM_OIF_FLAG_PROTO_IGMP, __func__); |
d62a17ae | 2169 | } |
a7b2b1e2 DS |
2170 | } |
2171 | ||
9b29ea95 | 2172 | void pim_upstream_init(struct pim_instance *pim) |
0f588989 | 2173 | { |
c2cfa843 | 2174 | char name[64]; |
9fb302f4 | 2175 | |
772270f3 | 2176 | snprintf(name, sizeof(name), "PIM %s Timer Wheel", pim->vrf->name); |
9b29ea95 | 2177 | pim->upstream_sg_wheel = |
36417fcc | 2178 | wheel_init(router->master, 31000, 100, pim_upstream_hash_key, |
c2cfa843 | 2179 | pim_upstream_sg_running, name); |
9fb302f4 | 2180 | |
dd3364cb | 2181 | rb_pim_upstream_init(&pim->upstream_head); |
0f588989 | 2182 | } |