]> git.proxmox.com Git - mirror_frr.git/blob - pimd/pim_oil.c
Merge pull request #13649 from donaldsharp/unlock_the_node_or_else
[mirror_frr.git] / pimd / pim_oil.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * PIM for Quagga
4 * Copyright (C) 2008 Everton da Silva Marques
5 */
6
7 #include <zebra.h>
8
9 #include "log.h"
10 #include "memory.h"
11 #include "linklist.h"
12 #include "if.h"
13 #include "hash.h"
14 #include "jhash.h"
15
16 #include "pimd.h"
17 #include "pim_oil.h"
18 #include "pim_str.h"
19 #include "pim_iface.h"
20 #include "pim_time.h"
21 #include "pim_vxlan.h"
22
23 static void pim_channel_update_mute(struct channel_oil *c_oil);
24
25 char *pim_channel_oil_dump(struct channel_oil *c_oil, char *buf, size_t size)
26 {
27 char *out;
28 struct interface *ifp;
29 pim_sgaddr sg;
30 int i;
31
32 sg.src = *oil_origin(c_oil);
33 sg.grp = *oil_mcastgrp(c_oil);
34 ifp = pim_if_find_by_vif_index(c_oil->pim, *oil_parent(c_oil));
35 snprintfrr(buf, size, "%pSG IIF: %s, OIFS: ", &sg,
36 ifp ? ifp->name : "(?)");
37
38 out = buf + strlen(buf);
39 for (i = 0; i < MAXVIFS; i++) {
40 if (oil_if_has(c_oil, i) != 0) {
41 ifp = pim_if_find_by_vif_index(c_oil->pim, i);
42 snprintf(out, buf + size - out, "%s ",
43 ifp ? ifp->name : "(?)");
44 out += strlen(out);
45 }
46 }
47
48 return buf;
49 }
50
51 int pim_channel_oil_compare(const struct channel_oil *cc1,
52 const struct channel_oil *cc2)
53 {
54 struct channel_oil *c1 = (struct channel_oil *)cc1;
55 struct channel_oil *c2 = (struct channel_oil *)cc2;
56 int rv;
57
58 rv = pim_addr_cmp(*oil_mcastgrp(c1), *oil_mcastgrp(c2));
59 if (rv)
60 return rv;
61 rv = pim_addr_cmp(*oil_origin(c1), *oil_origin(c2));
62 if (rv)
63 return rv;
64 return 0;
65 }
66
67 void pim_oil_init(struct pim_instance *pim)
68 {
69 rb_pim_oil_init(&pim->channel_oil_head);
70 }
71
72 void pim_oil_terminate(struct pim_instance *pim)
73 {
74 struct channel_oil *c_oil;
75
76 while ((c_oil = rb_pim_oil_pop(&pim->channel_oil_head)))
77 pim_channel_oil_free(c_oil);
78
79 rb_pim_oil_fini(&pim->channel_oil_head);
80 }
81
82 void pim_channel_oil_free(struct channel_oil *c_oil)
83 {
84 XFREE(MTYPE_PIM_CHANNEL_OIL, c_oil);
85 }
86
87 struct channel_oil *pim_find_channel_oil(struct pim_instance *pim,
88 pim_sgaddr *sg)
89 {
90 struct channel_oil *c_oil = NULL;
91 struct channel_oil lookup;
92
93 *oil_mcastgrp(&lookup) = sg->grp;
94 *oil_origin(&lookup) = sg->src;
95
96 c_oil = rb_pim_oil_find(&pim->channel_oil_head, &lookup);
97
98 return c_oil;
99 }
100
101 struct channel_oil *pim_channel_oil_add(struct pim_instance *pim,
102 pim_sgaddr *sg, const char *name)
103 {
104 struct channel_oil *c_oil;
105
106 c_oil = pim_find_channel_oil(pim, sg);
107 if (c_oil) {
108 ++c_oil->oil_ref_count;
109
110 if (!c_oil->up) {
111 /* channel might be present prior to upstream */
112 c_oil->up = pim_upstream_find(
113 pim, sg);
114 /* if the upstream entry is being anchored to an
115 * already existing channel OIL we need to re-evaluate
116 * the "Mute" state on AA OIFs
117 */
118 pim_channel_update_mute(c_oil);
119 }
120
121 /* check if the IIF has changed
122 * XXX - is this really needed
123 */
124 pim_upstream_mroute_iif_update(c_oil, __func__);
125
126 if (PIM_DEBUG_MROUTE)
127 zlog_debug(
128 "%s(%s): Existing oil for %pSG Ref Count: %d (Post Increment)",
129 __func__, name, sg, c_oil->oil_ref_count);
130 return c_oil;
131 }
132
133 c_oil = XCALLOC(MTYPE_PIM_CHANNEL_OIL, sizeof(*c_oil));
134
135 *oil_mcastgrp(c_oil) = sg->grp;
136 *oil_origin(c_oil) = sg->src;
137
138 *oil_parent(c_oil) = MAXVIFS;
139 c_oil->oil_ref_count = 1;
140 c_oil->installed = 0;
141 c_oil->up = pim_upstream_find(pim, sg);
142 c_oil->pim = pim;
143
144 rb_pim_oil_add(&pim->channel_oil_head, c_oil);
145
146 if (PIM_DEBUG_MROUTE)
147 zlog_debug("%s(%s): c_oil %pSG add", __func__, name, sg);
148
149 return c_oil;
150 }
151
152
153 /*
154 * Clean up mroute and channel oil created for dropping pkts from directly
155 * connected source when the interface was non DR.
156 */
157 void pim_clear_nocache_state(struct pim_interface *pim_ifp)
158 {
159 struct channel_oil *c_oil;
160
161 frr_each_safe (rb_pim_oil, &pim_ifp->pim->channel_oil_head, c_oil) {
162
163 if ((!c_oil->up) ||
164 !(PIM_UPSTREAM_FLAG_TEST_SRC_NOCACHE(c_oil->up->flags)))
165 continue;
166
167 if (*oil_parent(c_oil) != pim_ifp->mroute_vif_index)
168 continue;
169
170 EVENT_OFF(c_oil->up->t_ka_timer);
171 PIM_UPSTREAM_FLAG_UNSET_SRC_NOCACHE(c_oil->up->flags);
172 PIM_UPSTREAM_FLAG_UNSET_SRC_STREAM(c_oil->up->flags);
173 pim_upstream_del(pim_ifp->pim, c_oil->up, __func__);
174 }
175 }
176
177 struct channel_oil *pim_channel_oil_del(struct channel_oil *c_oil,
178 const char *name)
179 {
180 if (PIM_DEBUG_MROUTE) {
181 pim_sgaddr sg = {.src = *oil_origin(c_oil),
182 .grp = *oil_mcastgrp(c_oil)};
183
184 zlog_debug(
185 "%s(%s): Del oil for %pSG, Ref Count: %d (Predecrement)",
186 __func__, name, &sg, c_oil->oil_ref_count);
187 }
188 --c_oil->oil_ref_count;
189
190 if (c_oil->oil_ref_count < 1) {
191 /*
192 * notice that listnode_delete() can't be moved
193 * into pim_channel_oil_free() because the later is
194 * called by list_delete_all_node()
195 */
196 c_oil->up = NULL;
197 rb_pim_oil_del(&c_oil->pim->channel_oil_head, c_oil);
198
199 pim_channel_oil_free(c_oil);
200 return NULL;
201 }
202
203 return c_oil;
204 }
205
206 void pim_channel_oil_upstream_deref(struct channel_oil *c_oil)
207 {
208 /* The upstream entry associated with a channel_oil is abt to be
209 * deleted. If the channel_oil is kept around because of other
210 * references we need to remove upstream based states out of it.
211 */
212 c_oil = pim_channel_oil_del(c_oil, __func__);
213 if (c_oil) {
214 /* note: here we assume that c_oil->up has already been
215 * cleared
216 */
217 pim_channel_update_mute(c_oil);
218 }
219 }
220
221 int pim_channel_del_oif(struct channel_oil *channel_oil, struct interface *oif,
222 uint32_t proto_mask, const char *caller)
223 {
224 struct pim_interface *pim_ifp;
225
226 assert(channel_oil);
227 assert(oif);
228
229 pim_ifp = oif->info;
230
231 assertf(pim_ifp->mroute_vif_index >= 0,
232 "trying to del OIF %s with VIF (%d)", oif->name,
233 pim_ifp->mroute_vif_index);
234
235 /*
236 * Don't do anything if we've been asked to remove a source
237 * that is not actually on it.
238 */
239 if (!(channel_oil->oif_flags[pim_ifp->mroute_vif_index] & proto_mask)) {
240 if (PIM_DEBUG_MROUTE) {
241 zlog_debug(
242 "%s %s: no existing protocol mask %u(%u) for requested OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%pPAs,%pPAs)",
243 __FILE__, __func__, proto_mask,
244 channel_oil
245 ->oif_flags[pim_ifp->mroute_vif_index],
246 oif->name, pim_ifp->mroute_vif_index,
247 oil_if_has(channel_oil, pim_ifp->mroute_vif_index),
248 oil_origin(channel_oil),
249 oil_mcastgrp(channel_oil));
250 }
251 return 0;
252 }
253
254 channel_oil->oif_flags[pim_ifp->mroute_vif_index] &= ~proto_mask;
255
256 if (channel_oil->oif_flags[pim_ifp->mroute_vif_index] &
257 PIM_OIF_FLAG_PROTO_ANY) {
258 if (PIM_DEBUG_MROUTE) {
259 zlog_debug(
260 "%s %s: other protocol masks remain for requested OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%pPAs,%pPAs)",
261 __FILE__, __func__, oif->name,
262 pim_ifp->mroute_vif_index,
263 oil_if_has(channel_oil, pim_ifp->mroute_vif_index),
264 oil_origin(channel_oil),
265 oil_mcastgrp(channel_oil));
266 }
267 return 0;
268 }
269
270 oil_if_set(channel_oil, pim_ifp->mroute_vif_index, 0);
271 /* clear mute; will be re-evaluated when the OIF becomes valid again */
272 channel_oil->oif_flags[pim_ifp->mroute_vif_index] &= ~PIM_OIF_FLAG_MUTE;
273
274 if (pim_upstream_mroute_add(channel_oil, __func__)) {
275 if (PIM_DEBUG_MROUTE) {
276 zlog_debug(
277 "%s %s: could not remove output interface %s (vif_index=%d) for channel (S,G)=(%pPAs,%pPAs)",
278 __FILE__, __func__, oif->name,
279 pim_ifp->mroute_vif_index,
280 oil_origin(channel_oil),
281 oil_mcastgrp(channel_oil));
282 }
283 return -1;
284 }
285
286 --channel_oil->oil_size;
287
288 if (PIM_DEBUG_MROUTE) {
289 zlog_debug(
290 "%s(%s): (S,G)=(%pPAs,%pPAs): proto_mask=%u IIF:%d OIF=%s vif_index=%d",
291 __func__, caller, oil_origin(channel_oil),
292 oil_mcastgrp(channel_oil),
293 proto_mask,
294 *oil_parent(channel_oil), oif->name,
295 pim_ifp->mroute_vif_index);
296 }
297
298 return 0;
299 }
300
301 void pim_channel_del_inherited_oif(struct channel_oil *c_oil,
302 struct interface *oif, const char *caller)
303 {
304 struct pim_upstream *up = c_oil->up;
305
306 pim_channel_del_oif(c_oil, oif, PIM_OIF_FLAG_PROTO_STAR,
307 caller);
308
309 /* if an inherited OIF is being removed join-desired can change
310 * if the inherited OIL is now empty and KAT is running
311 */
312 if (up && !pim_addr_is_any(up->sg.src) &&
313 pim_upstream_empty_inherited_olist(up))
314 pim_upstream_update_join_desired(up->pim, up);
315 }
316
317 static bool pim_channel_eval_oif_mute(struct channel_oil *c_oil,
318 struct pim_interface *pim_ifp)
319 {
320 struct pim_interface *pim_reg_ifp;
321 struct pim_interface *vxlan_ifp;
322 bool do_mute = false;
323 struct pim_instance *pim = c_oil->pim;
324
325 if (!c_oil->up)
326 return do_mute;
327
328 pim_reg_ifp = pim->regiface->info;
329 if (pim_ifp == pim_reg_ifp) {
330 /* suppress pimreg in the OIL if the mroute is not supposed to
331 * trigger register encapsulated data
332 */
333 if (PIM_UPSTREAM_FLAG_TEST_NO_PIMREG_DATA(c_oil->up->flags))
334 do_mute = true;
335
336 return do_mute;
337 }
338
339 vxlan_ifp = pim_vxlan_get_term_ifp(pim);
340 if (pim_ifp == vxlan_ifp) {
341 /* 1. vxlan termination device must never be added to the
342 * origination mroute (and that can actually happen because
343 * of XG inheritance from the termination mroute) otherwise
344 * traffic will end up looping.
345 * PS: This check has also been extended to non-orig mroutes
346 * that have a local SIP as such mroutes can move back and
347 * forth between orig<=>non-orig type.
348 * 2. vxlan termination device should be removed from the non-DF
349 * to prevent duplicates to the overlay rxer
350 */
351 if (PIM_UPSTREAM_FLAG_TEST_SRC_VXLAN_ORIG(c_oil->up->flags) ||
352 PIM_UPSTREAM_FLAG_TEST_MLAG_NON_DF(c_oil->up->flags) ||
353 pim_vxlan_is_local_sip(c_oil->up))
354 do_mute = true;
355
356 return do_mute;
357 }
358
359 if (PIM_I_am_DualActive(pim_ifp)) {
360 struct pim_upstream *starup = c_oil->up->parent;
361 if (PIM_UPSTREAM_FLAG_TEST_MLAG_INTERFACE(c_oil->up->flags)
362 && (PIM_UPSTREAM_FLAG_TEST_MLAG_NON_DF(c_oil->up->flags)))
363 do_mute = true;
364
365 /* In case entry is (S,G), Negotiation happens at (*.G) */
366 if (starup
367
368 && PIM_UPSTREAM_FLAG_TEST_MLAG_INTERFACE(starup->flags)
369 && (PIM_UPSTREAM_FLAG_TEST_MLAG_NON_DF(starup->flags)))
370 do_mute = true;
371 return do_mute;
372 }
373 return do_mute;
374 }
375
376 void pim_channel_update_oif_mute(struct channel_oil *c_oil,
377 struct pim_interface *pim_ifp)
378 {
379 bool old_mute;
380 bool new_mute;
381
382 /* If pim_ifp is not a part of the OIL there is nothing to do */
383 if (!oil_if_has(c_oil, pim_ifp->mroute_vif_index))
384 return;
385
386 old_mute = !!(c_oil->oif_flags[pim_ifp->mroute_vif_index] &
387 PIM_OIF_FLAG_MUTE);
388 new_mute = pim_channel_eval_oif_mute(c_oil, pim_ifp);
389 if (old_mute == new_mute)
390 return;
391
392 if (new_mute)
393 c_oil->oif_flags[pim_ifp->mroute_vif_index] |=
394 PIM_OIF_FLAG_MUTE;
395 else
396 c_oil->oif_flags[pim_ifp->mroute_vif_index] &=
397 ~PIM_OIF_FLAG_MUTE;
398
399 pim_upstream_mroute_add(c_oil, __func__);
400 }
401
402 /* pim_upstream has been set or cleared on the c_oil. re-eval mute state
403 * on all existing OIFs
404 */
405 static void pim_channel_update_mute(struct channel_oil *c_oil)
406 {
407 struct pim_interface *pim_reg_ifp;
408 struct pim_interface *vxlan_ifp;
409
410 if (c_oil->pim->regiface) {
411 pim_reg_ifp = c_oil->pim->regiface->info;
412 if (pim_reg_ifp)
413 pim_channel_update_oif_mute(c_oil, pim_reg_ifp);
414 }
415 vxlan_ifp = pim_vxlan_get_term_ifp(c_oil->pim);
416 if (vxlan_ifp)
417 pim_channel_update_oif_mute(c_oil, vxlan_ifp);
418 }
419
420 int pim_channel_add_oif(struct channel_oil *channel_oil, struct interface *oif,
421 uint32_t proto_mask, const char *caller)
422 {
423 struct pim_interface *pim_ifp;
424 int old_ttl;
425
426 /*
427 * If we've gotten here we've gone bad, but let's
428 * not take down pim
429 */
430 if (!channel_oil) {
431 zlog_warn("Attempt to Add OIF for non-existent channel oil");
432 return -1;
433 }
434
435 pim_ifp = oif->info;
436
437 assertf(pim_ifp->mroute_vif_index >= 0,
438 "trying to add OIF %s with VIF (%d)", oif->name,
439 pim_ifp->mroute_vif_index);
440
441 /* Prevent single protocol from subscribing same interface to
442 channel (S,G) multiple times */
443 if (channel_oil->oif_flags[pim_ifp->mroute_vif_index] & proto_mask) {
444 channel_oil->oif_flags[pim_ifp->mroute_vif_index] |= proto_mask;
445
446 if (PIM_DEBUG_MROUTE) {
447 zlog_debug(
448 "%s %s: existing protocol mask %u requested OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%pPAs,%pPAs)",
449 __FILE__, __func__, proto_mask, oif->name,
450 pim_ifp->mroute_vif_index,
451 oil_if_has(channel_oil, pim_ifp->mroute_vif_index),
452 oil_origin(channel_oil),
453 oil_mcastgrp(channel_oil));
454 }
455 return -3;
456 }
457
458 /* Allow other protocol to request subscription of same interface to
459 * channel (S,G), we need to note this information
460 */
461 if (channel_oil->oif_flags[pim_ifp->mroute_vif_index]
462 & PIM_OIF_FLAG_PROTO_ANY) {
463
464 /* Updating time here is not required as this time has to
465 * indicate when the interface is added
466 */
467
468 channel_oil->oif_flags[pim_ifp->mroute_vif_index] |= proto_mask;
469 /* Check the OIF really exists before returning, and only log
470 warning otherwise */
471 if (oil_if_has(channel_oil, pim_ifp->mroute_vif_index) < 1) {
472 zlog_warn(
473 "%s %s: new protocol mask %u requested nonexistent OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%pPAs,%pPAs)",
474 __FILE__, __func__, proto_mask, oif->name,
475 pim_ifp->mroute_vif_index,
476 oil_if_has(channel_oil, pim_ifp->mroute_vif_index),
477 oil_origin(channel_oil),
478 oil_mcastgrp(channel_oil));
479 }
480
481 if (PIM_DEBUG_MROUTE) {
482 zlog_debug(
483 "%s(%s): (S,G)=(%pPAs,%pPAs): proto_mask=%u OIF=%s vif_index=%d added to 0x%x",
484 __func__, caller, oil_origin(channel_oil),
485 oil_mcastgrp(channel_oil),
486 proto_mask, oif->name,
487 pim_ifp->mroute_vif_index,
488 channel_oil
489 ->oif_flags[pim_ifp->mroute_vif_index]);
490 }
491 return 0;
492 }
493
494 old_ttl = oil_if_has(channel_oil, pim_ifp->mroute_vif_index);
495
496 if (old_ttl > 0) {
497 if (PIM_DEBUG_MROUTE) {
498 zlog_debug(
499 "%s %s: interface %s (vif_index=%d) is existing output for channel (S,G)=(%pPAs,%pPAs)",
500 __FILE__, __func__, oif->name,
501 pim_ifp->mroute_vif_index,
502 oil_origin(channel_oil),
503 oil_mcastgrp(channel_oil));
504 }
505 return -4;
506 }
507
508 oil_if_set(channel_oil, pim_ifp->mroute_vif_index, PIM_MROUTE_MIN_TTL);
509
510 /* Some OIFs are held in a muted state i.e. the PIM state machine
511 * decided to include the OIF but additional status check such as
512 * MLAG DF role prevent it from being activated for traffic
513 * forwarding.
514 */
515 if (pim_channel_eval_oif_mute(channel_oil, pim_ifp))
516 channel_oil->oif_flags[pim_ifp->mroute_vif_index] |=
517 PIM_OIF_FLAG_MUTE;
518 else
519 channel_oil->oif_flags[pim_ifp->mroute_vif_index] &=
520 ~PIM_OIF_FLAG_MUTE;
521
522 /* channel_oil->oil.mfcc_parent != MAXVIFS indicate this entry is not
523 * valid to get installed in kernel.
524 */
525 if (*oil_parent(channel_oil) != MAXVIFS) {
526 if (pim_upstream_mroute_add(channel_oil, __func__)) {
527 if (PIM_DEBUG_MROUTE) {
528 zlog_debug(
529 "%s %s: could not add output interface %s (vif_index=%d) for channel (S,G)=(%pPAs,%pPAs)",
530 __FILE__, __func__, oif->name,
531 pim_ifp->mroute_vif_index,
532 oil_origin(channel_oil),
533 oil_mcastgrp(channel_oil));
534 }
535
536 oil_if_set(channel_oil, pim_ifp->mroute_vif_index,
537 old_ttl);
538 return -5;
539 }
540 }
541
542 channel_oil->oif_creation[pim_ifp->mroute_vif_index] =
543 pim_time_monotonic_sec();
544 ++channel_oil->oil_size;
545 channel_oil->oif_flags[pim_ifp->mroute_vif_index] |= proto_mask;
546
547 if (PIM_DEBUG_MROUTE) {
548 zlog_debug(
549 "%s(%s): (S,G)=(%pPAs,%pPAs): proto_mask=%u OIF=%s vif_index=%d: DONE",
550 __func__, caller, oil_origin(channel_oil),
551 oil_mcastgrp(channel_oil),
552 proto_mask,
553 oif->name, pim_ifp->mroute_vif_index);
554 }
555
556 return 0;
557 }
558
559 int pim_channel_oil_empty(struct channel_oil *c_oil)
560 {
561 static struct channel_oil null_oil;
562
563 if (!c_oil)
564 return 1;
565
566 /* exclude pimreg from the OIL when checking if the inherited_oil is
567 * non-NULL.
568 * pimreg device (in all vrfs) uses a vifi of
569 * 0 (PIM_OIF_PIM_REGISTER_VIF) so we simply mfcc_ttls[0] */
570 if (oil_if_has(c_oil, 0))
571 oil_if_set(&null_oil, 0, 1);
572 else
573 oil_if_set(&null_oil, 0, 0);
574
575 return !oil_if_cmp(&c_oil->oil, &null_oil.oil);
576 }