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