]>
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 struct channel_oil
*pim_channel_oil_add(struct pim_instance
*pim
,
150 struct prefix_sg
*sg
,
151 int input_vif_index
, const char *name
)
153 struct channel_oil
*c_oil
;
154 struct interface
*ifp
;
156 c_oil
= pim_find_channel_oil(pim
, sg
);
158 if (c_oil
->oil
.mfcc_parent
!= input_vif_index
) {
159 c_oil
->oil_inherited_rescan
= 1;
160 if (PIM_DEBUG_MROUTE_DETAIL
)
162 "%s: Existing channel oil %pSG4 points to %d, modifying to point at %d",
163 __PRETTY_FUNCTION__
, sg
,
164 c_oil
->oil
.mfcc_parent
,
167 c_oil
->oil
.mfcc_parent
= input_vif_index
;
168 ++c_oil
->oil_ref_count
;
169 /* channel might be present prior to upstream */
170 c_oil
->up
= pim_upstream_find(pim
, sg
);
172 if (PIM_DEBUG_MROUTE
)
174 "%s(%s): Existing oil for %pSG4 Ref Count: %d (Post Increment)",
175 __PRETTY_FUNCTION__
, name
, sg
,
176 c_oil
->oil_ref_count
);
180 if (input_vif_index
!= MAXVIFS
) {
181 ifp
= pim_if_find_by_vif_index(pim
, input_vif_index
);
185 "%s:%s (S,G)=%pSG4 could not find input interface for input_vif_index=%d",
186 __PRETTY_FUNCTION__
, name
, sg
, input_vif_index
);
190 c_oil
= XCALLOC(MTYPE_PIM_CHANNEL_OIL
, sizeof(*c_oil
));
192 c_oil
->oil
.mfcc_mcastgrp
= sg
->grp
;
193 c_oil
->oil
.mfcc_origin
= sg
->src
;
194 c_oil
= hash_get(pim
->channel_oil_hash
, c_oil
, hash_alloc_intern
);
196 c_oil
->oil
.mfcc_parent
= input_vif_index
;
197 c_oil
->oil_ref_count
= 1;
198 c_oil
->installed
= 0;
199 c_oil
->up
= pim_upstream_find(pim
, sg
);
202 listnode_add_sort(pim
->channel_oil_list
, c_oil
);
204 if (PIM_DEBUG_MROUTE
)
206 "%s(%s): New oil for %pSG4 vif_index: %d Ref Count: 1 (Post Increment)",
207 __PRETTY_FUNCTION__
, name
, sg
, input_vif_index
);
211 void pim_channel_oil_del(struct channel_oil
*c_oil
, const char *name
)
213 if (PIM_DEBUG_MROUTE
) {
214 struct prefix_sg sg
= {.src
= c_oil
->oil
.mfcc_mcastgrp
,
215 .grp
= c_oil
->oil
.mfcc_origin
};
218 "%s(%s): Del oil for %pSG4, Ref Count: %d (Predecrement)",
219 __PRETTY_FUNCTION__
, name
, &sg
, c_oil
->oil_ref_count
);
221 --c_oil
->oil_ref_count
;
223 if (c_oil
->oil_ref_count
< 1) {
225 * notice that listnode_delete() can't be moved
226 * into pim_channel_oil_free() because the later is
227 * called by list_delete_all_node()
230 listnode_delete(c_oil
->pim
->channel_oil_list
, c_oil
);
231 hash_release(c_oil
->pim
->channel_oil_hash
, c_oil
);
233 pim_channel_oil_free(c_oil
);
237 int pim_channel_del_oif(struct channel_oil
*channel_oil
, struct interface
*oif
,
240 struct pim_interface
*pim_ifp
;
242 zassert(channel_oil
);
248 * Don't do anything if we've been asked to remove a source
249 * that is not actually on it.
251 if (!(channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] & proto_mask
)) {
252 if (PIM_DEBUG_MROUTE
) {
253 char group_str
[INET_ADDRSTRLEN
];
254 char source_str
[INET_ADDRSTRLEN
];
255 pim_inet4_dump("<group?>",
256 channel_oil
->oil
.mfcc_mcastgrp
,
257 group_str
, sizeof(group_str
));
258 pim_inet4_dump("<source?>",
259 channel_oil
->oil
.mfcc_origin
, source_str
,
262 "%s %s: no existing protocol mask %u(%u) for requested OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)",
263 __FILE__
, __PRETTY_FUNCTION__
, proto_mask
,
265 ->oif_flags
[pim_ifp
->mroute_vif_index
],
266 oif
->name
, pim_ifp
->mroute_vif_index
,
268 .mfcc_ttls
[pim_ifp
->mroute_vif_index
],
269 source_str
, group_str
);
274 channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] &= ~proto_mask
;
276 if (channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
]) {
277 if (PIM_DEBUG_MROUTE
) {
278 char group_str
[INET_ADDRSTRLEN
];
279 char source_str
[INET_ADDRSTRLEN
];
280 pim_inet4_dump("<group?>",
281 channel_oil
->oil
.mfcc_mcastgrp
,
282 group_str
, sizeof(group_str
));
283 pim_inet4_dump("<source?>",
284 channel_oil
->oil
.mfcc_origin
, source_str
,
287 "%s %s: other protocol masks remain for requested OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)",
288 __FILE__
, __PRETTY_FUNCTION__
, oif
->name
,
289 pim_ifp
->mroute_vif_index
,
291 .mfcc_ttls
[pim_ifp
->mroute_vif_index
],
292 source_str
, group_str
);
297 channel_oil
->oil
.mfcc_ttls
[pim_ifp
->mroute_vif_index
] = 0;
299 if (pim_mroute_add(channel_oil
, __PRETTY_FUNCTION__
)) {
300 if (PIM_DEBUG_MROUTE
) {
301 char group_str
[INET_ADDRSTRLEN
];
302 char source_str
[INET_ADDRSTRLEN
];
303 pim_inet4_dump("<group?>",
304 channel_oil
->oil
.mfcc_mcastgrp
,
305 group_str
, sizeof(group_str
));
306 pim_inet4_dump("<source?>",
307 channel_oil
->oil
.mfcc_origin
, source_str
,
310 "%s %s: could not remove output interface %s (vif_index=%d) for channel (S,G)=(%s,%s)",
311 __FILE__
, __PRETTY_FUNCTION__
, oif
->name
,
312 pim_ifp
->mroute_vif_index
, source_str
,
318 --channel_oil
->oil_size
;
320 if (PIM_DEBUG_MROUTE
) {
321 char group_str
[INET_ADDRSTRLEN
];
322 char source_str
[INET_ADDRSTRLEN
];
323 pim_inet4_dump("<group?>", channel_oil
->oil
.mfcc_mcastgrp
,
324 group_str
, sizeof(group_str
));
325 pim_inet4_dump("<source?>", channel_oil
->oil
.mfcc_origin
,
326 source_str
, sizeof(source_str
));
328 "%s %s: (S,G)=(%s,%s): proto_mask=%u IIF:%d OIF=%s vif_index=%d",
329 __FILE__
, __PRETTY_FUNCTION__
, source_str
, group_str
,
330 proto_mask
, channel_oil
->oil
.mfcc_parent
, oif
->name
,
331 pim_ifp
->mroute_vif_index
);
338 int pim_channel_add_oif(struct channel_oil
*channel_oil
, struct interface
*oif
,
341 struct pim_interface
*pim_ifp
;
343 bool allow_iif_in_oil
= false;
346 * If we've gotten here we've gone bad, but let's
350 zlog_warn("Attempt to Add OIF for non-existent channel oil");
356 #ifdef PIM_ENFORCE_LOOPFREE_MFC
358 Prevent creating MFC entry with OIF=IIF.
360 This is a protection against implementation mistakes.
362 PIM protocol implicitely ensures loopfree multicast topology.
364 IGMP must be protected against adding looped MFC entries created
365 by both source and receiver attached to the same interface. See
368 if (channel_oil
->up
&&
369 PIM_UPSTREAM_FLAG_TEST_ALLOW_IIF_IN_OIL(
370 channel_oil
->up
->flags
)) {
371 allow_iif_in_oil
= true;
374 if (!allow_iif_in_oil
&&
375 pim_ifp
->mroute_vif_index
== channel_oil
->oil
.mfcc_parent
) {
376 channel_oil
->oil_inherited_rescan
= 1;
377 if (PIM_DEBUG_MROUTE
) {
378 char group_str
[INET_ADDRSTRLEN
];
379 char source_str
[INET_ADDRSTRLEN
];
380 pim_inet4_dump("<group?>",
381 channel_oil
->oil
.mfcc_mcastgrp
,
382 group_str
, sizeof(group_str
));
383 pim_inet4_dump("<source?>",
384 channel_oil
->oil
.mfcc_origin
, source_str
,
387 "%s %s: refusing protocol mask %u request for IIF=OIF=%s (vif_index=%d) for channel (S,G)=(%s,%s)",
388 __FILE__
, __PRETTY_FUNCTION__
, proto_mask
,
389 oif
->name
, pim_ifp
->mroute_vif_index
,
390 source_str
, group_str
);
396 /* Prevent single protocol from subscribing same interface to
397 channel (S,G) multiple times */
398 if (channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] & proto_mask
) {
399 if (PIM_DEBUG_MROUTE
) {
400 char group_str
[INET_ADDRSTRLEN
];
401 char source_str
[INET_ADDRSTRLEN
];
402 pim_inet4_dump("<group?>",
403 channel_oil
->oil
.mfcc_mcastgrp
,
404 group_str
, sizeof(group_str
));
405 pim_inet4_dump("<source?>",
406 channel_oil
->oil
.mfcc_origin
, source_str
,
409 "%s %s: existing protocol mask %u requested OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)",
410 __FILE__
, __PRETTY_FUNCTION__
, proto_mask
,
411 oif
->name
, pim_ifp
->mroute_vif_index
,
413 .mfcc_ttls
[pim_ifp
->mroute_vif_index
],
414 source_str
, group_str
);
419 /* Allow other protocol to request subscription of same interface to
420 * channel (S,G), we need to note this information
422 if (channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
]
423 & PIM_OIF_FLAG_PROTO_ANY
) {
425 /* Updating time here is not required as this time has to
426 * indicate when the interface is added
429 channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] |= proto_mask
;
430 /* Check the OIF really exists before returning, and only log
432 if (channel_oil
->oil
.mfcc_ttls
[pim_ifp
->mroute_vif_index
] < 1) {
434 char group_str
[INET_ADDRSTRLEN
];
435 char source_str
[INET_ADDRSTRLEN
];
436 pim_inet4_dump("<group?>",
437 channel_oil
->oil
.mfcc_mcastgrp
,
438 group_str
, sizeof(group_str
));
439 pim_inet4_dump("<source?>",
440 channel_oil
->oil
.mfcc_origin
,
441 source_str
, sizeof(source_str
));
443 "%s %s: new protocol mask %u requested nonexistent OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)",
444 __FILE__
, __PRETTY_FUNCTION__
,
445 proto_mask
, oif
->name
,
446 pim_ifp
->mroute_vif_index
,
447 channel_oil
->oil
.mfcc_ttls
448 [pim_ifp
->mroute_vif_index
],
449 source_str
, group_str
);
456 old_ttl
= channel_oil
->oil
.mfcc_ttls
[pim_ifp
->mroute_vif_index
];
459 if (PIM_DEBUG_MROUTE
) {
460 char group_str
[INET_ADDRSTRLEN
];
461 char source_str
[INET_ADDRSTRLEN
];
462 pim_inet4_dump("<group?>",
463 channel_oil
->oil
.mfcc_mcastgrp
,
464 group_str
, sizeof(group_str
));
465 pim_inet4_dump("<source?>",
466 channel_oil
->oil
.mfcc_origin
, source_str
,
469 "%s %s: interface %s (vif_index=%d) is existing output for channel (S,G)=(%s,%s)",
470 __FILE__
, __PRETTY_FUNCTION__
, oif
->name
,
471 pim_ifp
->mroute_vif_index
, source_str
,
477 channel_oil
->oil
.mfcc_ttls
[pim_ifp
->mroute_vif_index
] =
480 /* channel_oil->oil.mfcc_parent != MAXVIFS indicate this entry is not
481 * valid to get installed in kernel.
483 if (channel_oil
->oil
.mfcc_parent
!= MAXVIFS
) {
484 if (pim_mroute_add(channel_oil
, __PRETTY_FUNCTION__
)) {
485 if (PIM_DEBUG_MROUTE
) {
486 char group_str
[INET_ADDRSTRLEN
];
487 char source_str
[INET_ADDRSTRLEN
];
488 pim_inet4_dump("<group?>",
489 channel_oil
->oil
.mfcc_mcastgrp
,
490 group_str
, sizeof(group_str
));
491 pim_inet4_dump("<source?>",
492 channel_oil
->oil
.mfcc_origin
, source_str
,
495 "%s %s: could not add output interface %s (vif_index=%d) for channel (S,G)=(%s,%s)",
496 __FILE__
, __PRETTY_FUNCTION__
, oif
->name
,
497 pim_ifp
->mroute_vif_index
, source_str
,
501 channel_oil
->oil
.mfcc_ttls
[pim_ifp
->mroute_vif_index
]
507 channel_oil
->oif_creation
[pim_ifp
->mroute_vif_index
] =
508 pim_time_monotonic_sec();
509 ++channel_oil
->oil_size
;
510 channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] |= proto_mask
;
512 if (PIM_DEBUG_MROUTE
) {
513 char group_str
[INET_ADDRSTRLEN
];
514 char source_str
[INET_ADDRSTRLEN
];
515 pim_inet4_dump("<group?>", channel_oil
->oil
.mfcc_mcastgrp
,
516 group_str
, sizeof(group_str
));
517 pim_inet4_dump("<source?>", channel_oil
->oil
.mfcc_origin
,
518 source_str
, sizeof(source_str
));
520 "%s %s: (S,G)=(%s,%s): proto_mask=%u OIF=%s vif_index=%d: DONE",
521 __FILE__
, __PRETTY_FUNCTION__
, source_str
, group_str
,
522 proto_mask
, oif
->name
, pim_ifp
->mroute_vif_index
);
528 int pim_channel_oil_empty(struct channel_oil
*c_oil
)
530 static uint32_t zero
[MAXVIFS
];
531 static int inited
= 0;
536 * Not sure that this is necessary, but I would rather ensure
540 memset(&zero
, 0, sizeof(uint32_t) * MAXVIFS
);
544 return !memcmp(c_oil
->oif_flags
, zero
, MAXVIFS
* sizeof(uint32_t));