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