]>
git.proxmox.com Git - mirror_frr.git/blob - pimd/pim_oil.c
3 * Copyright (C) 2008 Everton da Silva Marques
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.
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.
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
32 #include "pim_iface.h"
35 // struct list *pim_channel_oil_list = NULL;
36 // struct hash *pim_channel_oil_hash = NULL;
38 char *pim_channel_oil_dump(struct channel_oil
*c_oil
, char *buf
, size_t size
)
41 struct interface
*ifp
;
45 sg
.src
= c_oil
->oil
.mfcc_origin
;
46 sg
.grp
= c_oil
->oil
.mfcc_mcastgrp
;
47 ifp
= pim_if_find_by_vif_index(c_oil
->pim
, c_oil
->oil
.mfcc_parent
);
48 snprintf(buf
, size
, "%s IIF: %s, OIFS: ", pim_str_sg_dump(&sg
),
49 ifp
? ifp
->name
: "(?)");
51 out
= buf
+ strlen(buf
);
52 for (i
= 0; i
< MAXVIFS
; i
++) {
53 if (c_oil
->oil
.mfcc_ttls
[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
: "(?)");
64 static int pim_channel_oil_compare(struct channel_oil
*c1
,
65 struct channel_oil
*c2
)
67 if (ntohl(c1
->oil
.mfcc_mcastgrp
.s_addr
)
68 < ntohl(c2
->oil
.mfcc_mcastgrp
.s_addr
))
71 if (ntohl(c1
->oil
.mfcc_mcastgrp
.s_addr
)
72 > ntohl(c2
->oil
.mfcc_mcastgrp
.s_addr
))
75 if (ntohl(c1
->oil
.mfcc_origin
.s_addr
)
76 < ntohl(c2
->oil
.mfcc_origin
.s_addr
))
79 if (ntohl(c1
->oil
.mfcc_origin
.s_addr
)
80 > ntohl(c2
->oil
.mfcc_origin
.s_addr
))
86 static bool pim_oil_equal(const void *arg1
, const void *arg2
)
88 const struct channel_oil
*c1
= (const struct channel_oil
*)arg1
;
89 const struct channel_oil
*c2
= (const struct channel_oil
*)arg2
;
91 if ((c1
->oil
.mfcc_mcastgrp
.s_addr
== c2
->oil
.mfcc_mcastgrp
.s_addr
)
92 && (c1
->oil
.mfcc_origin
.s_addr
== c2
->oil
.mfcc_origin
.s_addr
))
98 static unsigned int pim_oil_hash_key(const void *arg
)
100 const struct channel_oil
*oil
= arg
;
102 return jhash_2words(oil
->oil
.mfcc_mcastgrp
.s_addr
,
103 oil
->oil
.mfcc_origin
.s_addr
, 0);
106 void pim_oil_init(struct pim_instance
*pim
)
110 snprintf(hash_name
, 64, "PIM %s Oil Hash", pim
->vrf
->name
);
111 pim
->channel_oil_hash
= hash_create_size(8192, pim_oil_hash_key
,
112 pim_oil_equal
, hash_name
);
114 pim
->channel_oil_list
= list_new();
115 pim
->channel_oil_list
->del
= (void (*)(void *))pim_channel_oil_free
;
116 pim
->channel_oil_list
->cmp
=
117 (int (*)(void *, void *))pim_channel_oil_compare
;
120 void pim_oil_terminate(struct pim_instance
*pim
)
122 if (pim
->channel_oil_list
)
123 list_delete(&pim
->channel_oil_list
);
125 if (pim
->channel_oil_hash
)
126 hash_free(pim
->channel_oil_hash
);
127 pim
->channel_oil_hash
= NULL
;
130 void pim_channel_oil_free(struct channel_oil
*c_oil
)
132 XFREE(MTYPE_PIM_CHANNEL_OIL
, c_oil
);
135 struct channel_oil
*pim_find_channel_oil(struct pim_instance
*pim
,
136 struct prefix_sg
*sg
)
138 struct channel_oil
*c_oil
= NULL
;
139 struct channel_oil lookup
;
141 lookup
.oil
.mfcc_mcastgrp
= sg
->grp
;
142 lookup
.oil
.mfcc_origin
= sg
->src
;
144 c_oil
= hash_lookup(pim
->channel_oil_hash
, &lookup
);
149 void pim_channel_oil_change_iif(struct pim_instance
*pim
,
150 struct channel_oil
*c_oil
,
154 int old_vif_index
= c_oil
->oil
.mfcc_parent
;
155 struct prefix_sg sg
= {.src
= c_oil
->oil
.mfcc_mcastgrp
,
156 .grp
= c_oil
->oil
.mfcc_origin
};
158 if (c_oil
->oil
.mfcc_parent
== input_vif_index
) {
159 if (PIM_DEBUG_MROUTE_DETAIL
)
160 zlog_debug("%s(%s): Existing channel oil %pSG4 already using %d as IIF",
161 __PRETTY_FUNCTION__
, name
, &sg
,
167 if (PIM_DEBUG_MROUTE_DETAIL
)
168 zlog_debug("%s(%s): Changing channel oil %pSG4 IIF from %d to %d installed: %d",
169 __PRETTY_FUNCTION__
, name
, &sg
,
170 c_oil
->oil
.mfcc_parent
, input_vif_index
,
173 c_oil
->oil
.mfcc_parent
= input_vif_index
;
174 if (c_oil
->installed
) {
175 if (input_vif_index
== MAXVIFS
)
176 pim_mroute_del(c_oil
, name
);
178 pim_mroute_add(c_oil
, name
);
180 if (old_vif_index
== MAXVIFS
)
181 pim_mroute_add(c_oil
, name
);
186 struct channel_oil
*pim_channel_oil_add(struct pim_instance
*pim
,
187 struct prefix_sg
*sg
,
188 int input_vif_index
, const char *name
)
190 struct channel_oil
*c_oil
;
191 struct interface
*ifp
;
193 c_oil
= pim_find_channel_oil(pim
, sg
);
195 if (c_oil
->oil
.mfcc_parent
!= input_vif_index
) {
196 c_oil
->oil_inherited_rescan
= 1;
197 if (PIM_DEBUG_MROUTE_DETAIL
)
199 "%s: Existing channel oil %pSG4 points to %d, modifying to point at %d",
200 __PRETTY_FUNCTION__
, sg
,
201 c_oil
->oil
.mfcc_parent
,
204 pim_channel_oil_change_iif(pim
, c_oil
, input_vif_index
,
206 ++c_oil
->oil_ref_count
;
207 /* channel might be present prior to upstream */
208 c_oil
->up
= pim_upstream_find(pim
, sg
);
210 if (PIM_DEBUG_MROUTE
)
212 "%s(%s): Existing oil for %pSG4 Ref Count: %d (Post Increment)",
213 __PRETTY_FUNCTION__
, name
, sg
,
214 c_oil
->oil_ref_count
);
218 if (input_vif_index
!= MAXVIFS
) {
219 ifp
= pim_if_find_by_vif_index(pim
, input_vif_index
);
223 "%s:%s (S,G)=%pSG4 could not find input interface for input_vif_index=%d",
224 __PRETTY_FUNCTION__
, name
, sg
, input_vif_index
);
228 c_oil
= XCALLOC(MTYPE_PIM_CHANNEL_OIL
, sizeof(*c_oil
));
230 c_oil
->oil
.mfcc_mcastgrp
= sg
->grp
;
231 c_oil
->oil
.mfcc_origin
= sg
->src
;
232 c_oil
= hash_get(pim
->channel_oil_hash
, c_oil
, hash_alloc_intern
);
234 c_oil
->oil
.mfcc_parent
= input_vif_index
;
235 c_oil
->oil_ref_count
= 1;
236 c_oil
->installed
= 0;
237 c_oil
->up
= pim_upstream_find(pim
, sg
);
240 listnode_add_sort(pim
->channel_oil_list
, c_oil
);
242 if (PIM_DEBUG_MROUTE
)
244 "%s(%s): New oil for %pSG4 vif_index: %d Ref Count: 1 (Post Increment)",
245 __PRETTY_FUNCTION__
, name
, sg
, input_vif_index
);
249 void pim_channel_oil_del(struct channel_oil
*c_oil
, const char *name
)
251 if (PIM_DEBUG_MROUTE
) {
252 struct prefix_sg sg
= {.src
= c_oil
->oil
.mfcc_mcastgrp
,
253 .grp
= c_oil
->oil
.mfcc_origin
};
256 "%s(%s): Del oil for %pSG4, Ref Count: %d (Predecrement)",
257 __PRETTY_FUNCTION__
, name
, &sg
, c_oil
->oil_ref_count
);
259 --c_oil
->oil_ref_count
;
261 if (c_oil
->oil_ref_count
< 1) {
263 * notice that listnode_delete() can't be moved
264 * into pim_channel_oil_free() because the later is
265 * called by list_delete_all_node()
268 listnode_delete(c_oil
->pim
->channel_oil_list
, c_oil
);
269 hash_release(c_oil
->pim
->channel_oil_hash
, c_oil
);
271 pim_channel_oil_free(c_oil
);
275 int pim_channel_del_oif(struct channel_oil
*channel_oil
, struct interface
*oif
,
278 struct pim_interface
*pim_ifp
;
280 zassert(channel_oil
);
286 * Don't do anything if we've been asked to remove a source
287 * that is not actually on it.
289 if (!(channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] & proto_mask
)) {
290 if (PIM_DEBUG_MROUTE
) {
291 char group_str
[INET_ADDRSTRLEN
];
292 char source_str
[INET_ADDRSTRLEN
];
293 pim_inet4_dump("<group?>",
294 channel_oil
->oil
.mfcc_mcastgrp
,
295 group_str
, sizeof(group_str
));
296 pim_inet4_dump("<source?>",
297 channel_oil
->oil
.mfcc_origin
, source_str
,
300 "%s %s: no existing protocol mask %u(%u) for requested OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)",
301 __FILE__
, __PRETTY_FUNCTION__
, proto_mask
,
303 ->oif_flags
[pim_ifp
->mroute_vif_index
],
304 oif
->name
, pim_ifp
->mroute_vif_index
,
306 .mfcc_ttls
[pim_ifp
->mroute_vif_index
],
307 source_str
, group_str
);
312 channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] &= ~proto_mask
;
314 if (channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
]) {
315 if (PIM_DEBUG_MROUTE
) {
316 char group_str
[INET_ADDRSTRLEN
];
317 char source_str
[INET_ADDRSTRLEN
];
318 pim_inet4_dump("<group?>",
319 channel_oil
->oil
.mfcc_mcastgrp
,
320 group_str
, sizeof(group_str
));
321 pim_inet4_dump("<source?>",
322 channel_oil
->oil
.mfcc_origin
, source_str
,
325 "%s %s: other protocol masks remain for requested OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)",
326 __FILE__
, __PRETTY_FUNCTION__
, oif
->name
,
327 pim_ifp
->mroute_vif_index
,
329 .mfcc_ttls
[pim_ifp
->mroute_vif_index
],
330 source_str
, group_str
);
335 channel_oil
->oil
.mfcc_ttls
[pim_ifp
->mroute_vif_index
] = 0;
337 if (pim_mroute_add(channel_oil
, __PRETTY_FUNCTION__
)) {
338 if (PIM_DEBUG_MROUTE
) {
339 char group_str
[INET_ADDRSTRLEN
];
340 char source_str
[INET_ADDRSTRLEN
];
341 pim_inet4_dump("<group?>",
342 channel_oil
->oil
.mfcc_mcastgrp
,
343 group_str
, sizeof(group_str
));
344 pim_inet4_dump("<source?>",
345 channel_oil
->oil
.mfcc_origin
, source_str
,
348 "%s %s: could not remove output interface %s (vif_index=%d) for channel (S,G)=(%s,%s)",
349 __FILE__
, __PRETTY_FUNCTION__
, oif
->name
,
350 pim_ifp
->mroute_vif_index
, source_str
,
356 --channel_oil
->oil_size
;
358 if (PIM_DEBUG_MROUTE
) {
359 char group_str
[INET_ADDRSTRLEN
];
360 char source_str
[INET_ADDRSTRLEN
];
361 pim_inet4_dump("<group?>", channel_oil
->oil
.mfcc_mcastgrp
,
362 group_str
, sizeof(group_str
));
363 pim_inet4_dump("<source?>", channel_oil
->oil
.mfcc_origin
,
364 source_str
, sizeof(source_str
));
366 "%s %s: (S,G)=(%s,%s): proto_mask=%u IIF:%d OIF=%s vif_index=%d",
367 __FILE__
, __PRETTY_FUNCTION__
, source_str
, group_str
,
368 proto_mask
, channel_oil
->oil
.mfcc_parent
, oif
->name
,
369 pim_ifp
->mroute_vif_index
);
376 int pim_channel_add_oif(struct channel_oil
*channel_oil
, struct interface
*oif
,
379 struct pim_interface
*pim_ifp
;
381 bool allow_iif_in_oil
= false;
384 * If we've gotten here we've gone bad, but let's
388 zlog_warn("Attempt to Add OIF for non-existent channel oil");
394 #ifdef PIM_ENFORCE_LOOPFREE_MFC
396 Prevent creating MFC entry with OIF=IIF.
398 This is a protection against implementation mistakes.
400 PIM protocol implicitely ensures loopfree multicast topology.
402 IGMP must be protected against adding looped MFC entries created
403 by both source and receiver attached to the same interface. See
405 We shall allow igmp to create upstream when it is DR for the intf.
406 Assume RP reachable via non DR.
408 if ((channel_oil
->up
&&
409 PIM_UPSTREAM_FLAG_TEST_ALLOW_IIF_IN_OIL(channel_oil
->up
->flags
)) ||
410 ((proto_mask
== PIM_OIF_FLAG_PROTO_IGMP
) && PIM_I_am_DR(pim_ifp
))) {
411 allow_iif_in_oil
= true;
414 if (!allow_iif_in_oil
&&
415 pim_ifp
->mroute_vif_index
== channel_oil
->oil
.mfcc_parent
) {
416 channel_oil
->oil_inherited_rescan
= 1;
417 if (PIM_DEBUG_MROUTE
) {
418 char group_str
[INET_ADDRSTRLEN
];
419 char source_str
[INET_ADDRSTRLEN
];
420 pim_inet4_dump("<group?>",
421 channel_oil
->oil
.mfcc_mcastgrp
,
422 group_str
, sizeof(group_str
));
423 pim_inet4_dump("<source?>",
424 channel_oil
->oil
.mfcc_origin
, source_str
,
427 "%s %s: refusing protocol mask %u request for IIF=OIF=%s (vif_index=%d) for channel (S,G)=(%s,%s)",
428 __FILE__
, __PRETTY_FUNCTION__
, proto_mask
,
429 oif
->name
, pim_ifp
->mroute_vif_index
,
430 source_str
, group_str
);
436 /* Prevent single protocol from subscribing same interface to
437 channel (S,G) multiple times */
438 if (channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] & proto_mask
) {
439 if (PIM_DEBUG_MROUTE
) {
440 char group_str
[INET_ADDRSTRLEN
];
441 char source_str
[INET_ADDRSTRLEN
];
442 pim_inet4_dump("<group?>",
443 channel_oil
->oil
.mfcc_mcastgrp
,
444 group_str
, sizeof(group_str
));
445 pim_inet4_dump("<source?>",
446 channel_oil
->oil
.mfcc_origin
, source_str
,
449 "%s %s: existing protocol mask %u requested OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)",
450 __FILE__
, __PRETTY_FUNCTION__
, proto_mask
,
451 oif
->name
, pim_ifp
->mroute_vif_index
,
453 .mfcc_ttls
[pim_ifp
->mroute_vif_index
],
454 source_str
, group_str
);
459 /* Allow other protocol to request subscription of same interface to
460 * channel (S,G), we need to note this information
462 if (channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
]
463 & PIM_OIF_FLAG_PROTO_ANY
) {
465 /* Updating time here is not required as this time has to
466 * indicate when the interface is added
469 channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] |= proto_mask
;
470 /* Check the OIF really exists before returning, and only log
472 if (channel_oil
->oil
.mfcc_ttls
[pim_ifp
->mroute_vif_index
] < 1) {
474 char group_str
[INET_ADDRSTRLEN
];
475 char source_str
[INET_ADDRSTRLEN
];
476 pim_inet4_dump("<group?>",
477 channel_oil
->oil
.mfcc_mcastgrp
,
478 group_str
, sizeof(group_str
));
479 pim_inet4_dump("<source?>",
480 channel_oil
->oil
.mfcc_origin
,
481 source_str
, sizeof(source_str
));
483 "%s %s: new protocol mask %u requested nonexistent OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)",
484 __FILE__
, __PRETTY_FUNCTION__
,
485 proto_mask
, oif
->name
,
486 pim_ifp
->mroute_vif_index
,
487 channel_oil
->oil
.mfcc_ttls
488 [pim_ifp
->mroute_vif_index
],
489 source_str
, group_str
);
496 old_ttl
= channel_oil
->oil
.mfcc_ttls
[pim_ifp
->mroute_vif_index
];
499 if (PIM_DEBUG_MROUTE
) {
500 char group_str
[INET_ADDRSTRLEN
];
501 char source_str
[INET_ADDRSTRLEN
];
502 pim_inet4_dump("<group?>",
503 channel_oil
->oil
.mfcc_mcastgrp
,
504 group_str
, sizeof(group_str
));
505 pim_inet4_dump("<source?>",
506 channel_oil
->oil
.mfcc_origin
, source_str
,
509 "%s %s: interface %s (vif_index=%d) is existing output for channel (S,G)=(%s,%s)",
510 __FILE__
, __PRETTY_FUNCTION__
, oif
->name
,
511 pim_ifp
->mroute_vif_index
, source_str
,
517 channel_oil
->oil
.mfcc_ttls
[pim_ifp
->mroute_vif_index
] =
520 /* channel_oil->oil.mfcc_parent != MAXVIFS indicate this entry is not
521 * valid to get installed in kernel.
523 if (channel_oil
->oil
.mfcc_parent
!= MAXVIFS
) {
524 if (pim_mroute_add(channel_oil
, __PRETTY_FUNCTION__
)) {
525 if (PIM_DEBUG_MROUTE
) {
526 char group_str
[INET_ADDRSTRLEN
];
527 char source_str
[INET_ADDRSTRLEN
];
528 pim_inet4_dump("<group?>",
529 channel_oil
->oil
.mfcc_mcastgrp
,
530 group_str
, sizeof(group_str
));
531 pim_inet4_dump("<source?>",
532 channel_oil
->oil
.mfcc_origin
, source_str
,
535 "%s %s: could not add output interface %s (vif_index=%d) for channel (S,G)=(%s,%s)",
536 __FILE__
, __PRETTY_FUNCTION__
, oif
->name
,
537 pim_ifp
->mroute_vif_index
, source_str
,
541 channel_oil
->oil
.mfcc_ttls
[pim_ifp
->mroute_vif_index
]
547 channel_oil
->oif_creation
[pim_ifp
->mroute_vif_index
] =
548 pim_time_monotonic_sec();
549 ++channel_oil
->oil_size
;
550 channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] |= proto_mask
;
552 if (PIM_DEBUG_MROUTE
) {
553 char group_str
[INET_ADDRSTRLEN
];
554 char source_str
[INET_ADDRSTRLEN
];
555 pim_inet4_dump("<group?>", channel_oil
->oil
.mfcc_mcastgrp
,
556 group_str
, sizeof(group_str
));
557 pim_inet4_dump("<source?>", channel_oil
->oil
.mfcc_origin
,
558 source_str
, sizeof(source_str
));
560 "%s %s: (S,G)=(%s,%s): proto_mask=%u OIF=%s vif_index=%d: DONE",
561 __FILE__
, __PRETTY_FUNCTION__
, source_str
, group_str
,
562 proto_mask
, oif
->name
, pim_ifp
->mroute_vif_index
);
568 int pim_channel_oil_empty(struct channel_oil
*c_oil
)
570 static uint32_t zero
[MAXVIFS
];
571 static int inited
= 0;
576 * Not sure that this is necessary, but I would rather ensure
580 memset(&zero
, 0, sizeof(uint32_t) * MAXVIFS
);
584 return !memcmp(c_oil
->oif_flags
, zero
, MAXVIFS
* sizeof(uint32_t));