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