]>
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
,
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
)
162 "%s: Existing channel oil %s points to %d, modifying to point at %d",
165 c_oil
->oil
.mfcc_parent
,
168 c_oil
->oil
.mfcc_parent
= input_vif_index
;
169 ++c_oil
->oil_ref_count
;
170 c_oil
->up
= pim_upstream_find(
171 pim
, sg
); // channel might be present prior to upstream
175 if (input_vif_index
!= MAXVIFS
) {
176 ifp
= pim_if_find_by_vif_index(pim
, input_vif_index
);
180 "%s: (S,G)=%s could not find input interface for input_vif_index=%d",
181 __PRETTY_FUNCTION__
, pim_str_sg_dump(sg
),
186 c_oil
= XCALLOC(MTYPE_PIM_CHANNEL_OIL
, sizeof(*c_oil
));
188 c_oil
->oil
.mfcc_mcastgrp
= sg
->grp
;
189 c_oil
->oil
.mfcc_origin
= sg
->src
;
190 c_oil
= hash_get(pim
->channel_oil_hash
, c_oil
, hash_alloc_intern
);
192 c_oil
->oil
.mfcc_parent
= input_vif_index
;
193 c_oil
->oil_ref_count
= 1;
194 c_oil
->installed
= 0;
195 c_oil
->up
= pim_upstream_find(pim
, sg
);
198 listnode_add_sort(pim
->channel_oil_list
, c_oil
);
203 void pim_channel_oil_del(struct channel_oil
*c_oil
)
205 --c_oil
->oil_ref_count
;
207 if (c_oil
->oil_ref_count
< 1) {
209 * notice that listnode_delete() can't be moved
210 * into pim_channel_oil_free() because the later is
211 * called by list_delete_all_node()
214 listnode_delete(c_oil
->pim
->channel_oil_list
, c_oil
);
215 hash_release(c_oil
->pim
->channel_oil_hash
, c_oil
);
217 pim_channel_oil_free(c_oil
);
221 int pim_channel_del_oif(struct channel_oil
*channel_oil
, struct interface
*oif
,
224 struct pim_interface
*pim_ifp
;
226 zassert(channel_oil
);
232 * Don't do anything if we've been asked to remove a source
233 * that is not actually on it.
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
,
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
,
249 ->oif_flags
[pim_ifp
->mroute_vif_index
],
250 oif
->name
, pim_ifp
->mroute_vif_index
,
252 .mfcc_ttls
[pim_ifp
->mroute_vif_index
],
253 source_str
, group_str
);
258 channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] &= ~proto_mask
;
260 if (channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
]) {
261 if (PIM_DEBUG_MROUTE
) {
262 char group_str
[INET_ADDRSTRLEN
];
263 char source_str
[INET_ADDRSTRLEN
];
264 pim_inet4_dump("<group?>",
265 channel_oil
->oil
.mfcc_mcastgrp
,
266 group_str
, sizeof(group_str
));
267 pim_inet4_dump("<source?>",
268 channel_oil
->oil
.mfcc_origin
, source_str
,
271 "%s %s: other protocol masks remain for requested OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)",
272 __FILE__
, __PRETTY_FUNCTION__
, oif
->name
,
273 pim_ifp
->mroute_vif_index
,
275 .mfcc_ttls
[pim_ifp
->mroute_vif_index
],
276 source_str
, group_str
);
281 channel_oil
->oil
.mfcc_ttls
[pim_ifp
->mroute_vif_index
] = 0;
283 if (pim_mroute_add(channel_oil
, __PRETTY_FUNCTION__
)) {
284 if (PIM_DEBUG_MROUTE
) {
285 char group_str
[INET_ADDRSTRLEN
];
286 char source_str
[INET_ADDRSTRLEN
];
287 pim_inet4_dump("<group?>",
288 channel_oil
->oil
.mfcc_mcastgrp
,
289 group_str
, sizeof(group_str
));
290 pim_inet4_dump("<source?>",
291 channel_oil
->oil
.mfcc_origin
, source_str
,
294 "%s %s: could not remove output interface %s (vif_index=%d) for channel (S,G)=(%s,%s)",
295 __FILE__
, __PRETTY_FUNCTION__
, oif
->name
,
296 pim_ifp
->mroute_vif_index
, source_str
,
302 --channel_oil
->oil_size
;
304 if (PIM_DEBUG_MROUTE
) {
305 char group_str
[INET_ADDRSTRLEN
];
306 char source_str
[INET_ADDRSTRLEN
];
307 pim_inet4_dump("<group?>", channel_oil
->oil
.mfcc_mcastgrp
,
308 group_str
, sizeof(group_str
));
309 pim_inet4_dump("<source?>", channel_oil
->oil
.mfcc_origin
,
310 source_str
, sizeof(source_str
));
312 "%s %s: (S,G)=(%s,%s): proto_mask=%u IIF:%d OIF=%s vif_index=%d",
313 __FILE__
, __PRETTY_FUNCTION__
, source_str
, group_str
,
314 proto_mask
, channel_oil
->oil
.mfcc_parent
, oif
->name
,
315 pim_ifp
->mroute_vif_index
);
322 int pim_channel_add_oif(struct channel_oil
*channel_oil
, struct interface
*oif
,
325 struct pim_interface
*pim_ifp
;
327 bool allow_iif_in_oil
= false;
330 * If we've gotten here we've gone bad, but let's
334 zlog_warn("Attempt to Add OIF for non-existent channel oil");
340 #ifdef PIM_ENFORCE_LOOPFREE_MFC
342 Prevent creating MFC entry with OIF=IIF.
344 This is a protection against implementation mistakes.
346 PIM protocol implicitely ensures loopfree multicast topology.
348 IGMP must be protected against adding looped MFC entries created
349 by both source and receiver attached to the same interface. See
352 if (channel_oil
->up
&&
353 PIM_UPSTREAM_FLAG_TEST_ALLOW_IIF_IN_OIL(
354 channel_oil
->up
->flags
)) {
355 allow_iif_in_oil
= true;
358 if (!allow_iif_in_oil
&&
359 pim_ifp
->mroute_vif_index
== channel_oil
->oil
.mfcc_parent
) {
360 channel_oil
->oil_inherited_rescan
= 1;
361 if (PIM_DEBUG_MROUTE
) {
362 char group_str
[INET_ADDRSTRLEN
];
363 char source_str
[INET_ADDRSTRLEN
];
364 pim_inet4_dump("<group?>",
365 channel_oil
->oil
.mfcc_mcastgrp
,
366 group_str
, sizeof(group_str
));
367 pim_inet4_dump("<source?>",
368 channel_oil
->oil
.mfcc_origin
, source_str
,
371 "%s %s: refusing protocol mask %u request for IIF=OIF=%s (vif_index=%d) for channel (S,G)=(%s,%s)",
372 __FILE__
, __PRETTY_FUNCTION__
, proto_mask
,
373 oif
->name
, pim_ifp
->mroute_vif_index
,
374 source_str
, group_str
);
380 /* Prevent single protocol from subscribing same interface to
381 channel (S,G) multiple times */
382 if (channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] & proto_mask
) {
383 if (PIM_DEBUG_MROUTE
) {
384 char group_str
[INET_ADDRSTRLEN
];
385 char source_str
[INET_ADDRSTRLEN
];
386 pim_inet4_dump("<group?>",
387 channel_oil
->oil
.mfcc_mcastgrp
,
388 group_str
, sizeof(group_str
));
389 pim_inet4_dump("<source?>",
390 channel_oil
->oil
.mfcc_origin
, source_str
,
393 "%s %s: existing protocol mask %u requested OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)",
394 __FILE__
, __PRETTY_FUNCTION__
, proto_mask
,
395 oif
->name
, pim_ifp
->mroute_vif_index
,
397 .mfcc_ttls
[pim_ifp
->mroute_vif_index
],
398 source_str
, group_str
);
403 /* Allow other protocol to request subscription of same interface to
404 * channel (S,G), we need to note this information
406 if (channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
]
407 & PIM_OIF_FLAG_PROTO_ANY
) {
409 /* Updating time here is not required as this time has to
410 * indicate when the interface is added
413 channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] |= proto_mask
;
414 /* Check the OIF really exists before returning, and only log
416 if (channel_oil
->oil
.mfcc_ttls
[pim_ifp
->mroute_vif_index
] < 1) {
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
,
425 source_str
, sizeof(source_str
));
427 "%s %s: new protocol mask %u requested nonexistent OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)",
428 __FILE__
, __PRETTY_FUNCTION__
,
429 proto_mask
, oif
->name
,
430 pim_ifp
->mroute_vif_index
,
431 channel_oil
->oil
.mfcc_ttls
432 [pim_ifp
->mroute_vif_index
],
433 source_str
, group_str
);
440 old_ttl
= channel_oil
->oil
.mfcc_ttls
[pim_ifp
->mroute_vif_index
];
443 if (PIM_DEBUG_MROUTE
) {
444 char group_str
[INET_ADDRSTRLEN
];
445 char source_str
[INET_ADDRSTRLEN
];
446 pim_inet4_dump("<group?>",
447 channel_oil
->oil
.mfcc_mcastgrp
,
448 group_str
, sizeof(group_str
));
449 pim_inet4_dump("<source?>",
450 channel_oil
->oil
.mfcc_origin
, source_str
,
453 "%s %s: interface %s (vif_index=%d) is existing output for channel (S,G)=(%s,%s)",
454 __FILE__
, __PRETTY_FUNCTION__
, oif
->name
,
455 pim_ifp
->mroute_vif_index
, source_str
,
461 channel_oil
->oil
.mfcc_ttls
[pim_ifp
->mroute_vif_index
] =
464 /* channel_oil->oil.mfcc_parent != MAXVIFS indicate this entry is not
465 * valid to get installed in kernel.
467 if (channel_oil
->oil
.mfcc_parent
!= MAXVIFS
) {
468 if (pim_mroute_add(channel_oil
, __PRETTY_FUNCTION__
)) {
469 if (PIM_DEBUG_MROUTE
) {
470 char group_str
[INET_ADDRSTRLEN
];
471 char source_str
[INET_ADDRSTRLEN
];
472 pim_inet4_dump("<group?>",
473 channel_oil
->oil
.mfcc_mcastgrp
,
474 group_str
, sizeof(group_str
));
475 pim_inet4_dump("<source?>",
476 channel_oil
->oil
.mfcc_origin
, source_str
,
479 "%s %s: could not add output interface %s (vif_index=%d) for channel (S,G)=(%s,%s)",
480 __FILE__
, __PRETTY_FUNCTION__
, oif
->name
,
481 pim_ifp
->mroute_vif_index
, source_str
,
485 channel_oil
->oil
.mfcc_ttls
[pim_ifp
->mroute_vif_index
]
491 channel_oil
->oif_creation
[pim_ifp
->mroute_vif_index
] =
492 pim_time_monotonic_sec();
493 ++channel_oil
->oil_size
;
494 channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] |= proto_mask
;
496 if (PIM_DEBUG_MROUTE
) {
497 char group_str
[INET_ADDRSTRLEN
];
498 char source_str
[INET_ADDRSTRLEN
];
499 pim_inet4_dump("<group?>", channel_oil
->oil
.mfcc_mcastgrp
,
500 group_str
, sizeof(group_str
));
501 pim_inet4_dump("<source?>", channel_oil
->oil
.mfcc_origin
,
502 source_str
, sizeof(source_str
));
504 "%s %s: (S,G)=(%s,%s): proto_mask=%u OIF=%s vif_index=%d: DONE",
505 __FILE__
, __PRETTY_FUNCTION__
, source_str
, group_str
,
506 proto_mask
, oif
->name
, pim_ifp
->mroute_vif_index
);
512 int pim_channel_oil_empty(struct channel_oil
*c_oil
)
514 static uint32_t zero
[MAXVIFS
];
515 static int inited
= 0;
520 * Not sure that this is necessary, but I would rather ensure
524 memset(&zero
, 0, sizeof(uint32_t) * MAXVIFS
);
528 return !memcmp(c_oil
->oif_flags
, zero
, MAXVIFS
* sizeof(uint32_t));