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 "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 0 : char *pim_channel_oil_dump(struct channel_oil *c_oil, char *buf, size_t size)
39 : {
40 0 : char *out;
41 0 : struct interface *ifp;
42 0 : pim_sgaddr sg;
43 0 : int i;
44 :
45 0 : sg.src = *oil_origin(c_oil);
46 0 : sg.grp = *oil_mcastgrp(c_oil);
47 0 : ifp = pim_if_find_by_vif_index(c_oil->pim, *oil_parent(c_oil));
48 0 : snprintfrr(buf, size, "%pSG IIF: %s, OIFS: ", &sg,
49 : ifp ? ifp->name : "(?)");
50 :
51 0 : out = buf + strlen(buf);
52 0 : for (i = 0; i < MAXVIFS; i++) {
53 0 : if (oil_if_has(c_oil, i) != 0) {
54 0 : ifp = pim_if_find_by_vif_index(c_oil->pim, i);
55 0 : snprintf(out, buf + size - out, "%s ",
56 : ifp ? ifp->name : "(?)");
57 0 : out += strlen(out);
58 : }
59 : }
60 :
61 0 : return buf;
62 : }
63 :
64 5 : int pim_channel_oil_compare(const struct channel_oil *cc1,
65 : const struct channel_oil *cc2)
66 : {
67 5 : struct channel_oil *c1 = (struct channel_oil *)cc1;
68 5 : struct channel_oil *c2 = (struct channel_oil *)cc2;
69 5 : int rv;
70 :
71 5 : rv = pim_addr_cmp(*oil_mcastgrp(c1), *oil_mcastgrp(c2));
72 5 : if (rv)
73 : return rv;
74 2 : rv = pim_addr_cmp(*oil_origin(c1), *oil_origin(c2));
75 2 : if (rv)
76 : return rv;
77 : return 0;
78 : }
79 :
80 1 : void pim_oil_init(struct pim_instance *pim)
81 : {
82 1 : rb_pim_oil_init(&pim->channel_oil_head);
83 1 : }
84 :
85 1 : void pim_oil_terminate(struct pim_instance *pim)
86 : {
87 1 : struct channel_oil *c_oil;
88 :
89 4 : while ((c_oil = rb_pim_oil_pop(&pim->channel_oil_head)))
90 3 : pim_channel_oil_free(c_oil);
91 :
92 1 : rb_pim_oil_fini(&pim->channel_oil_head);
93 1 : }
94 :
95 2 : void pim_channel_oil_free(struct channel_oil *c_oil)
96 : {
97 2 : XFREE(MTYPE_PIM_CHANNEL_OIL, c_oil);
98 2 : }
99 :
100 4 : struct channel_oil *pim_find_channel_oil(struct pim_instance *pim,
101 : pim_sgaddr *sg)
102 : {
103 4 : struct channel_oil *c_oil = NULL;
104 4 : struct channel_oil lookup;
105 :
106 4 : *oil_mcastgrp(&lookup) = sg->grp;
107 4 : *oil_origin(&lookup) = sg->src;
108 :
109 4 : c_oil = rb_pim_oil_find(&pim->channel_oil_head, &lookup);
110 :
111 4 : return c_oil;
112 : }
113 :
114 4 : struct channel_oil *pim_channel_oil_add(struct pim_instance *pim,
115 : pim_sgaddr *sg, const char *name)
116 : {
117 4 : struct channel_oil *c_oil;
118 :
119 4 : c_oil = pim_find_channel_oil(pim, sg);
120 4 : if (c_oil) {
121 2 : ++c_oil->oil_ref_count;
122 :
123 2 : if (!c_oil->up) {
124 : /* channel might be present prior to upstream */
125 2 : 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 2 : pim_channel_update_mute(c_oil);
132 : }
133 :
134 : /* check if the IIF has changed
135 : * XXX - is this really needed
136 : */
137 2 : pim_upstream_mroute_iif_update(c_oil, __func__);
138 :
139 2 : if (PIM_DEBUG_MROUTE)
140 0 : zlog_debug(
141 : "%s(%s): Existing oil for %pSG Ref Count: %d (Post Increment)",
142 : __func__, name, sg, c_oil->oil_ref_count);
143 2 : return c_oil;
144 : }
145 :
146 2 : c_oil = XCALLOC(MTYPE_PIM_CHANNEL_OIL, sizeof(*c_oil));
147 :
148 2 : *oil_mcastgrp(c_oil) = sg->grp;
149 2 : *oil_origin(c_oil) = sg->src;
150 :
151 2 : *oil_parent(c_oil) = MAXVIFS;
152 2 : c_oil->oil_ref_count = 1;
153 2 : c_oil->installed = 0;
154 2 : c_oil->up = pim_upstream_find(pim, sg);
155 2 : c_oil->pim = pim;
156 :
157 2 : rb_pim_oil_add(&pim->channel_oil_head, c_oil);
158 :
159 2 : if (PIM_DEBUG_MROUTE)
160 0 : zlog_debug("%s(%s): c_oil %pSG add", __func__, name, sg);
161 :
162 : return c_oil;
163 : }
164 :
165 2 : struct channel_oil *pim_channel_oil_del(struct channel_oil *c_oil,
166 : const char *name)
167 : {
168 2 : if (PIM_DEBUG_MROUTE) {
169 0 : pim_sgaddr sg = {.src = *oil_origin(c_oil),
170 0 : .grp = *oil_mcastgrp(c_oil)};
171 :
172 0 : zlog_debug(
173 : "%s(%s): Del oil for %pSG, Ref Count: %d (Predecrement)",
174 : __func__, name, &sg, c_oil->oil_ref_count);
175 : }
176 2 : --c_oil->oil_ref_count;
177 :
178 2 : 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 0 : c_oil->up = NULL;
185 0 : rb_pim_oil_del(&c_oil->pim->channel_oil_head, c_oil);
186 :
187 0 : pim_channel_oil_free(c_oil);
188 0 : return NULL;
189 : }
190 :
191 : return c_oil;
192 : }
193 :
194 2 : 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 2 : c_oil = pim_channel_oil_del(c_oil, __func__);
201 2 : if (c_oil) {
202 : /* note: here we assume that c_oil->up has already been
203 : * cleared
204 : */
205 2 : pim_channel_update_mute(c_oil);
206 : }
207 2 : }
208 :
209 2 : int pim_channel_del_oif(struct channel_oil *channel_oil, struct interface *oif,
210 : uint32_t proto_mask, const char *caller)
211 : {
212 2 : struct pim_interface *pim_ifp;
213 :
214 2 : assert(channel_oil);
215 2 : assert(oif);
216 :
217 2 : pim_ifp = oif->info;
218 :
219 2 : 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 2 : if (!(channel_oil->oif_flags[pim_ifp->mroute_vif_index] & proto_mask)) {
228 0 : if (PIM_DEBUG_MROUTE) {
229 0 : 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 0 : return 0;
240 : }
241 :
242 2 : channel_oil->oif_flags[pim_ifp->mroute_vif_index] &= ~proto_mask;
243 :
244 2 : if (channel_oil->oif_flags[pim_ifp->mroute_vif_index] &
245 : PIM_OIF_FLAG_PROTO_ANY) {
246 0 : if (PIM_DEBUG_MROUTE) {
247 0 : 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 0 : return 0;
256 : }
257 :
258 2 : oil_if_set(channel_oil, pim_ifp->mroute_vif_index, 0);
259 : /* clear mute; will be re-evaluated when the OIF becomes valid again */
260 2 : channel_oil->oif_flags[pim_ifp->mroute_vif_index] &= ~PIM_OIF_FLAG_MUTE;
261 :
262 2 : if (pim_upstream_mroute_add(channel_oil, __func__)) {
263 0 : if (PIM_DEBUG_MROUTE) {
264 0 : 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 0 : return -1;
272 : }
273 :
274 2 : --channel_oil->oil_size;
275 :
276 2 : if (PIM_DEBUG_MROUTE) {
277 0 : 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 0 : void pim_channel_del_inherited_oif(struct channel_oil *c_oil,
290 : struct interface *oif, const char *caller)
291 : {
292 0 : struct pim_upstream *up = c_oil->up;
293 :
294 0 : 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 0 : if (up && !pim_addr_is_any(up->sg.src) &&
301 0 : pim_upstream_empty_inherited_olist(up))
302 0 : pim_upstream_update_join_desired(up->pim, up);
303 0 : }
304 :
305 4 : static bool pim_channel_eval_oif_mute(struct channel_oil *c_oil,
306 : struct pim_interface *pim_ifp)
307 : {
308 4 : struct pim_interface *pim_reg_ifp;
309 4 : struct pim_interface *vxlan_ifp;
310 4 : bool do_mute = false;
311 4 : struct pim_instance *pim = c_oil->pim;
312 :
313 4 : if (!c_oil->up)
314 : return do_mute;
315 :
316 1 : pim_reg_ifp = pim->regiface->info;
317 1 : 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 1 : if (PIM_UPSTREAM_FLAG_TEST_NO_PIMREG_DATA(c_oil->up->flags))
322 0 : do_mute = true;
323 :
324 1 : return do_mute;
325 : }
326 :
327 0 : vxlan_ifp = pim_vxlan_get_term_ifp(pim);
328 0 : 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 0 : if (PIM_UPSTREAM_FLAG_TEST_SRC_VXLAN_ORIG(c_oil->up->flags) ||
340 0 : PIM_UPSTREAM_FLAG_TEST_MLAG_NON_DF(c_oil->up->flags) ||
341 0 : pim_vxlan_is_local_sip(c_oil->up))
342 : do_mute = true;
343 :
344 0 : return do_mute;
345 : }
346 :
347 0 : if (PIM_I_am_DualActive(pim_ifp)) {
348 0 : struct pim_upstream *starup = c_oil->up->parent;
349 0 : if (PIM_UPSTREAM_FLAG_TEST_MLAG_INTERFACE(c_oil->up->flags)
350 0 : && (PIM_UPSTREAM_FLAG_TEST_MLAG_NON_DF(c_oil->up->flags)))
351 0 : do_mute = true;
352 :
353 : /* In case entry is (S,G), Negotiation happens at (*.G) */
354 0 : if (starup
355 :
356 : && PIM_UPSTREAM_FLAG_TEST_MLAG_INTERFACE(starup->flags)
357 0 : && (PIM_UPSTREAM_FLAG_TEST_MLAG_NON_DF(starup->flags)))
358 0 : do_mute = true;
359 0 : return do_mute;
360 : }
361 : return do_mute;
362 : }
363 :
364 4 : void pim_channel_update_oif_mute(struct channel_oil *c_oil,
365 : struct pim_interface *pim_ifp)
366 : {
367 4 : bool old_mute;
368 4 : bool new_mute;
369 :
370 : /* If pim_ifp is not a part of the OIL there is nothing to do */
371 4 : if (!oil_if_has(c_oil, pim_ifp->mroute_vif_index))
372 : return;
373 :
374 1 : old_mute = !!(c_oil->oif_flags[pim_ifp->mroute_vif_index] &
375 : PIM_OIF_FLAG_MUTE);
376 1 : new_mute = pim_channel_eval_oif_mute(c_oil, pim_ifp);
377 1 : if (old_mute == new_mute)
378 : return;
379 :
380 0 : if (new_mute)
381 0 : c_oil->oif_flags[pim_ifp->mroute_vif_index] |=
382 : PIM_OIF_FLAG_MUTE;
383 : else
384 0 : c_oil->oif_flags[pim_ifp->mroute_vif_index] &=
385 : ~PIM_OIF_FLAG_MUTE;
386 :
387 0 : 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 4 : static void pim_channel_update_mute(struct channel_oil *c_oil)
394 : {
395 4 : struct pim_interface *pim_reg_ifp;
396 4 : struct pim_interface *vxlan_ifp;
397 :
398 4 : if (c_oil->pim->regiface) {
399 4 : pim_reg_ifp = c_oil->pim->regiface->info;
400 4 : if (pim_reg_ifp)
401 4 : pim_channel_update_oif_mute(c_oil, pim_reg_ifp);
402 : }
403 4 : vxlan_ifp = pim_vxlan_get_term_ifp(c_oil->pim);
404 4 : if (vxlan_ifp)
405 0 : pim_channel_update_oif_mute(c_oil, vxlan_ifp);
406 4 : }
407 :
408 4 : int pim_channel_add_oif(struct channel_oil *channel_oil, struct interface *oif,
409 : uint32_t proto_mask, const char *caller)
410 : {
411 4 : struct pim_interface *pim_ifp;
412 4 : 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 4 : if (!channel_oil) {
419 0 : zlog_warn("Attempt to Add OIF for non-existent channel oil");
420 0 : return -1;
421 : }
422 :
423 4 : pim_ifp = oif->info;
424 :
425 4 : 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 4 : if (channel_oil->oif_flags[pim_ifp->mroute_vif_index] & proto_mask) {
432 1 : channel_oil->oif_flags[pim_ifp->mroute_vif_index] |= proto_mask;
433 :
434 1 : if (PIM_DEBUG_MROUTE) {
435 0 : zlog_debug(
436 : "%s %s: existing protocol mask %u requested OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%pPAs,%pPAs)",
437 : __FILE__, __func__, proto_mask, oif->name,
438 : pim_ifp->mroute_vif_index,
439 : oil_if_has(channel_oil, pim_ifp->mroute_vif_index),
440 : oil_origin(channel_oil),
441 : oil_mcastgrp(channel_oil));
442 : }
443 1 : return -3;
444 : }
445 :
446 : /* Allow other protocol to request subscription of same interface to
447 : * channel (S,G), we need to note this information
448 : */
449 3 : if (channel_oil->oif_flags[pim_ifp->mroute_vif_index]
450 3 : & PIM_OIF_FLAG_PROTO_ANY) {
451 :
452 : /* Updating time here is not required as this time has to
453 : * indicate when the interface is added
454 : */
455 :
456 0 : channel_oil->oif_flags[pim_ifp->mroute_vif_index] |= proto_mask;
457 : /* Check the OIF really exists before returning, and only log
458 : warning otherwise */
459 0 : if (oil_if_has(channel_oil, pim_ifp->mroute_vif_index) < 1) {
460 0 : zlog_warn(
461 : "%s %s: new protocol mask %u requested nonexistent OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%pPAs,%pPAs)",
462 : __FILE__, __func__, proto_mask, oif->name,
463 : pim_ifp->mroute_vif_index,
464 : oil_if_has(channel_oil, pim_ifp->mroute_vif_index),
465 : oil_origin(channel_oil),
466 : oil_mcastgrp(channel_oil));
467 : }
468 :
469 0 : if (PIM_DEBUG_MROUTE) {
470 0 : zlog_debug(
471 : "%s(%s): (S,G)=(%pPAs,%pPAs): proto_mask=%u OIF=%s vif_index=%d added to 0x%x",
472 : __func__, caller, oil_origin(channel_oil),
473 : oil_mcastgrp(channel_oil),
474 : proto_mask, oif->name,
475 : pim_ifp->mroute_vif_index,
476 : channel_oil
477 : ->oif_flags[pim_ifp->mroute_vif_index]);
478 : }
479 0 : return 0;
480 : }
481 :
482 3 : old_ttl = oil_if_has(channel_oil, pim_ifp->mroute_vif_index);
483 :
484 3 : if (old_ttl > 0) {
485 0 : if (PIM_DEBUG_MROUTE) {
486 0 : zlog_debug(
487 : "%s %s: interface %s (vif_index=%d) is existing output for channel (S,G)=(%pPAs,%pPAs)",
488 : __FILE__, __func__, oif->name,
489 : pim_ifp->mroute_vif_index,
490 : oil_origin(channel_oil),
491 : oil_mcastgrp(channel_oil));
492 : }
493 0 : return -4;
494 : }
495 :
496 3 : oil_if_set(channel_oil, pim_ifp->mroute_vif_index, PIM_MROUTE_MIN_TTL);
497 :
498 : /* Some OIFs are held in a muted state i.e. the PIM state machine
499 : * decided to include the OIF but additional status check such as
500 : * MLAG DF role prevent it from being activated for traffic
501 : * forwarding.
502 : */
503 3 : if (pim_channel_eval_oif_mute(channel_oil, pim_ifp))
504 0 : channel_oil->oif_flags[pim_ifp->mroute_vif_index] |=
505 : PIM_OIF_FLAG_MUTE;
506 : else
507 3 : channel_oil->oif_flags[pim_ifp->mroute_vif_index] &=
508 : ~PIM_OIF_FLAG_MUTE;
509 :
510 : /* channel_oil->oil.mfcc_parent != MAXVIFS indicate this entry is not
511 : * valid to get installed in kernel.
512 : */
513 3 : if (*oil_parent(channel_oil) != MAXVIFS) {
514 0 : if (pim_upstream_mroute_add(channel_oil, __func__)) {
515 0 : if (PIM_DEBUG_MROUTE) {
516 0 : zlog_debug(
517 : "%s %s: could not add output interface %s (vif_index=%d) for channel (S,G)=(%pPAs,%pPAs)",
518 : __FILE__, __func__, oif->name,
519 : pim_ifp->mroute_vif_index,
520 : oil_origin(channel_oil),
521 : oil_mcastgrp(channel_oil));
522 : }
523 :
524 0 : oil_if_set(channel_oil, pim_ifp->mroute_vif_index,
525 : old_ttl);
526 0 : return -5;
527 : }
528 : }
529 :
530 6 : channel_oil->oif_creation[pim_ifp->mroute_vif_index] =
531 3 : pim_time_monotonic_sec();
532 3 : ++channel_oil->oil_size;
533 3 : channel_oil->oif_flags[pim_ifp->mroute_vif_index] |= proto_mask;
534 :
535 3 : if (PIM_DEBUG_MROUTE) {
536 0 : zlog_debug(
537 : "%s(%s): (S,G)=(%pPAs,%pPAs): proto_mask=%u OIF=%s vif_index=%d: DONE",
538 : __func__, caller, oil_origin(channel_oil),
539 : oil_mcastgrp(channel_oil),
540 : proto_mask,
541 : oif->name, pim_ifp->mroute_vif_index);
542 : }
543 :
544 : return 0;
545 : }
546 :
547 3 : int pim_channel_oil_empty(struct channel_oil *c_oil)
548 : {
549 3 : static struct channel_oil null_oil;
550 :
551 3 : if (!c_oil)
552 : return 1;
553 :
554 : /* exclude pimreg from the OIL when checking if the inherited_oil is
555 : * non-NULL.
556 : * pimreg device (in all vrfs) uses a vifi of
557 : * 0 (PIM_OIF_PIM_REGISTER_VIF) so we simply mfcc_ttls[0] */
558 2 : if (oil_if_has(c_oil, 0))
559 0 : oil_if_set(&null_oil, 0, 1);
560 : else
561 2 : oil_if_set(&null_oil, 0, 0);
562 :
563 2 : return !oil_if_cmp(&c_oil->oil, &null_oil.oil);
564 : }
|