]>
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
)
44 sg
.src
= c_oil
->oil
.mfcc_origin
;
45 sg
.grp
= c_oil
->oil
.mfcc_mcastgrp
;
46 snprintf(buf
, size
, "%s IIF: %d, OIFS: ", pim_str_sg_dump(&sg
),
47 c_oil
->oil
.mfcc_parent
);
49 out
= buf
+ strlen(buf
);
50 for (i
= 0; i
< MAXVIFS
; i
++) {
51 if (c_oil
->oil
.mfcc_ttls
[i
] != 0) {
52 snprintf(out
, buf
+ size
- out
, "%d ", i
);
60 static int pim_channel_oil_compare(struct channel_oil
*c1
,
61 struct channel_oil
*c2
)
63 if (ntohl(c1
->oil
.mfcc_mcastgrp
.s_addr
)
64 < ntohl(c2
->oil
.mfcc_mcastgrp
.s_addr
))
67 if (ntohl(c1
->oil
.mfcc_mcastgrp
.s_addr
)
68 > ntohl(c2
->oil
.mfcc_mcastgrp
.s_addr
))
71 if (ntohl(c1
->oil
.mfcc_origin
.s_addr
)
72 < ntohl(c2
->oil
.mfcc_origin
.s_addr
))
75 if (ntohl(c1
->oil
.mfcc_origin
.s_addr
)
76 > ntohl(c2
->oil
.mfcc_origin
.s_addr
))
82 static bool pim_oil_equal(const void *arg1
, const void *arg2
)
84 const struct channel_oil
*c1
= (const struct channel_oil
*)arg1
;
85 const struct channel_oil
*c2
= (const struct channel_oil
*)arg2
;
87 if ((c1
->oil
.mfcc_mcastgrp
.s_addr
== c2
->oil
.mfcc_mcastgrp
.s_addr
)
88 && (c1
->oil
.mfcc_origin
.s_addr
== c2
->oil
.mfcc_origin
.s_addr
))
94 static unsigned int pim_oil_hash_key(void *arg
)
96 struct channel_oil
*oil
= (struct channel_oil
*)arg
;
98 return jhash_2words(oil
->oil
.mfcc_mcastgrp
.s_addr
,
99 oil
->oil
.mfcc_origin
.s_addr
, 0);
102 void pim_oil_init(struct pim_instance
*pim
)
106 snprintf(hash_name
, 64, "PIM %s Oil Hash", pim
->vrf
->name
);
107 pim
->channel_oil_hash
= hash_create_size(8192, pim_oil_hash_key
,
108 pim_oil_equal
, hash_name
);
110 pim
->channel_oil_list
= list_new();
111 pim
->channel_oil_list
->del
= (void (*)(void *))pim_channel_oil_free
;
112 pim
->channel_oil_list
->cmp
=
113 (int (*)(void *, void *))pim_channel_oil_compare
;
116 void pim_oil_terminate(struct pim_instance
*pim
)
118 if (pim
->channel_oil_list
)
119 list_delete(&pim
->channel_oil_list
);
121 if (pim
->channel_oil_hash
)
122 hash_free(pim
->channel_oil_hash
);
123 pim
->channel_oil_hash
= NULL
;
126 void pim_channel_oil_free(struct channel_oil
*c_oil
)
128 XFREE(MTYPE_PIM_CHANNEL_OIL
, c_oil
);
131 struct channel_oil
*pim_find_channel_oil(struct pim_instance
*pim
,
132 struct prefix_sg
*sg
)
134 struct channel_oil
*c_oil
= NULL
;
135 struct channel_oil lookup
;
137 lookup
.oil
.mfcc_mcastgrp
= sg
->grp
;
138 lookup
.oil
.mfcc_origin
= sg
->src
;
140 c_oil
= hash_lookup(pim
->channel_oil_hash
, &lookup
);
145 struct channel_oil
*pim_channel_oil_add(struct pim_instance
*pim
,
146 struct prefix_sg
*sg
,
149 struct channel_oil
*c_oil
;
150 struct interface
*ifp
;
152 c_oil
= pim_find_channel_oil(pim
, sg
);
154 if (c_oil
->oil
.mfcc_parent
!= input_vif_index
) {
155 c_oil
->oil_inherited_rescan
= 1;
156 if (PIM_DEBUG_MROUTE
)
158 "%s: Existing channel oil %s points to %d, modifying to point at %d",
161 c_oil
->oil
.mfcc_parent
,
164 c_oil
->oil
.mfcc_parent
= input_vif_index
;
165 ++c_oil
->oil_ref_count
;
166 c_oil
->up
= pim_upstream_find(
167 pim
, sg
); // channel might be present prior to upstream
171 ifp
= pim_if_find_by_vif_index(pim
, input_vif_index
);
175 "%s: (S,G)=%s could not find input interface for input_vif_index=%d",
176 __PRETTY_FUNCTION__
, pim_str_sg_dump(sg
),
180 c_oil
= XCALLOC(MTYPE_PIM_CHANNEL_OIL
, sizeof(*c_oil
));
182 c_oil
->oil
.mfcc_mcastgrp
= sg
->grp
;
183 c_oil
->oil
.mfcc_origin
= sg
->src
;
184 c_oil
= hash_get(pim
->channel_oil_hash
, c_oil
, hash_alloc_intern
);
186 c_oil
->oil
.mfcc_parent
= input_vif_index
;
187 c_oil
->oil_ref_count
= 1;
188 c_oil
->installed
= 0;
189 c_oil
->up
= pim_upstream_find(pim
, sg
);
192 listnode_add_sort(pim
->channel_oil_list
, c_oil
);
197 void pim_channel_oil_del(struct channel_oil
*c_oil
)
199 --c_oil
->oil_ref_count
;
201 if (c_oil
->oil_ref_count
< 1) {
203 * notice that listnode_delete() can't be moved
204 * into pim_channel_oil_free() because the later is
205 * called by list_delete_all_node()
208 listnode_delete(c_oil
->pim
->channel_oil_list
, c_oil
);
209 hash_release(c_oil
->pim
->channel_oil_hash
, c_oil
);
211 pim_channel_oil_free(c_oil
);
215 int pim_channel_del_oif(struct channel_oil
*channel_oil
, struct interface
*oif
,
218 struct pim_interface
*pim_ifp
;
220 zassert(channel_oil
);
226 * Don't do anything if we've been asked to remove a source
227 * that is not actually on it.
229 if (!(channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] & proto_mask
)) {
230 if (PIM_DEBUG_MROUTE
) {
231 char group_str
[INET_ADDRSTRLEN
];
232 char source_str
[INET_ADDRSTRLEN
];
233 pim_inet4_dump("<group?>",
234 channel_oil
->oil
.mfcc_mcastgrp
,
235 group_str
, sizeof(group_str
));
236 pim_inet4_dump("<source?>",
237 channel_oil
->oil
.mfcc_origin
, source_str
,
240 "%s %s: no existing protocol mask %u(%u) for requested OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)",
241 __FILE__
, __PRETTY_FUNCTION__
, proto_mask
,
243 ->oif_flags
[pim_ifp
->mroute_vif_index
],
244 oif
->name
, pim_ifp
->mroute_vif_index
,
246 .mfcc_ttls
[pim_ifp
->mroute_vif_index
],
247 source_str
, group_str
);
252 channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] &= ~proto_mask
;
254 if (channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
]) {
255 if (PIM_DEBUG_MROUTE
) {
256 char group_str
[INET_ADDRSTRLEN
];
257 char source_str
[INET_ADDRSTRLEN
];
258 pim_inet4_dump("<group?>",
259 channel_oil
->oil
.mfcc_mcastgrp
,
260 group_str
, sizeof(group_str
));
261 pim_inet4_dump("<source?>",
262 channel_oil
->oil
.mfcc_origin
, source_str
,
265 "%s %s: other protocol masks remain for requested OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)",
266 __FILE__
, __PRETTY_FUNCTION__
, oif
->name
,
267 pim_ifp
->mroute_vif_index
,
269 .mfcc_ttls
[pim_ifp
->mroute_vif_index
],
270 source_str
, group_str
);
275 channel_oil
->oil
.mfcc_ttls
[pim_ifp
->mroute_vif_index
] = 0;
277 if (pim_mroute_add(channel_oil
, __PRETTY_FUNCTION__
)) {
278 if (PIM_DEBUG_MROUTE
) {
279 char group_str
[INET_ADDRSTRLEN
];
280 char source_str
[INET_ADDRSTRLEN
];
281 pim_inet4_dump("<group?>",
282 channel_oil
->oil
.mfcc_mcastgrp
,
283 group_str
, sizeof(group_str
));
284 pim_inet4_dump("<source?>",
285 channel_oil
->oil
.mfcc_origin
, source_str
,
288 "%s %s: could not remove output interface %s (vif_index=%d) for channel (S,G)=(%s,%s)",
289 __FILE__
, __PRETTY_FUNCTION__
, oif
->name
,
290 pim_ifp
->mroute_vif_index
, source_str
,
296 --channel_oil
->oil_size
;
298 if (PIM_DEBUG_MROUTE
) {
299 char group_str
[INET_ADDRSTRLEN
];
300 char source_str
[INET_ADDRSTRLEN
];
301 pim_inet4_dump("<group?>", channel_oil
->oil
.mfcc_mcastgrp
,
302 group_str
, sizeof(group_str
));
303 pim_inet4_dump("<source?>", channel_oil
->oil
.mfcc_origin
,
304 source_str
, sizeof(source_str
));
306 "%s %s: (S,G)=(%s,%s): proto_mask=%u IIF:%d OIF=%s vif_index=%d",
307 __FILE__
, __PRETTY_FUNCTION__
, source_str
, group_str
,
308 proto_mask
, channel_oil
->oil
.mfcc_parent
, oif
->name
,
309 pim_ifp
->mroute_vif_index
);
316 int pim_channel_add_oif(struct channel_oil
*channel_oil
, struct interface
*oif
,
319 struct pim_interface
*pim_ifp
;
323 * If we've gotten here we've gone bad, but let's
327 zlog_warn("Attempt to Add OIF for non-existent channel oil");
333 #ifdef PIM_ENFORCE_LOOPFREE_MFC
335 Prevent creating MFC entry with OIF=IIF.
337 This is a protection against implementation mistakes.
339 PIM protocol implicitely ensures loopfree multicast topology.
341 IGMP must be protected against adding looped MFC entries created
342 by both source and receiver attached to the same interface. See
345 if (pim_ifp
->mroute_vif_index
== channel_oil
->oil
.mfcc_parent
) {
346 channel_oil
->oil_inherited_rescan
= 1;
347 if (PIM_DEBUG_MROUTE
) {
348 char group_str
[INET_ADDRSTRLEN
];
349 char source_str
[INET_ADDRSTRLEN
];
350 pim_inet4_dump("<group?>",
351 channel_oil
->oil
.mfcc_mcastgrp
,
352 group_str
, sizeof(group_str
));
353 pim_inet4_dump("<source?>",
354 channel_oil
->oil
.mfcc_origin
, source_str
,
357 "%s %s: refusing protocol mask %u request for IIF=OIF=%s (vif_index=%d) for channel (S,G)=(%s,%s)",
358 __FILE__
, __PRETTY_FUNCTION__
, proto_mask
,
359 oif
->name
, pim_ifp
->mroute_vif_index
,
360 source_str
, group_str
);
366 /* Prevent single protocol from subscribing same interface to
367 channel (S,G) multiple times */
368 if (channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] & proto_mask
) {
369 if (PIM_DEBUG_MROUTE
) {
370 char group_str
[INET_ADDRSTRLEN
];
371 char source_str
[INET_ADDRSTRLEN
];
372 pim_inet4_dump("<group?>",
373 channel_oil
->oil
.mfcc_mcastgrp
,
374 group_str
, sizeof(group_str
));
375 pim_inet4_dump("<source?>",
376 channel_oil
->oil
.mfcc_origin
, source_str
,
379 "%s %s: existing protocol mask %u requested OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)",
380 __FILE__
, __PRETTY_FUNCTION__
, proto_mask
,
381 oif
->name
, pim_ifp
->mroute_vif_index
,
383 .mfcc_ttls
[pim_ifp
->mroute_vif_index
],
384 source_str
, group_str
);
389 /* Allow other protocol to request subscription of same interface to
390 * channel (S,G), we need to note this information
392 if (channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
]
393 & PIM_OIF_FLAG_PROTO_ANY
) {
395 channel_oil
->oif_creation
[pim_ifp
->mroute_vif_index
] =
396 pim_time_monotonic_sec();
397 channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] |= proto_mask
;
398 /* Check the OIF really exists before returning, and only log
400 if (channel_oil
->oil
.mfcc_ttls
[pim_ifp
->mroute_vif_index
] < 1) {
402 char group_str
[INET_ADDRSTRLEN
];
403 char source_str
[INET_ADDRSTRLEN
];
404 pim_inet4_dump("<group?>",
405 channel_oil
->oil
.mfcc_mcastgrp
,
406 group_str
, sizeof(group_str
));
407 pim_inet4_dump("<source?>",
408 channel_oil
->oil
.mfcc_origin
,
409 source_str
, sizeof(source_str
));
411 "%s %s: new protocol mask %u requested nonexistent OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)",
412 __FILE__
, __PRETTY_FUNCTION__
,
413 proto_mask
, oif
->name
,
414 pim_ifp
->mroute_vif_index
,
415 channel_oil
->oil
.mfcc_ttls
416 [pim_ifp
->mroute_vif_index
],
417 source_str
, group_str
);
424 old_ttl
= channel_oil
->oil
.mfcc_ttls
[pim_ifp
->mroute_vif_index
];
427 if (PIM_DEBUG_MROUTE
) {
428 char group_str
[INET_ADDRSTRLEN
];
429 char source_str
[INET_ADDRSTRLEN
];
430 pim_inet4_dump("<group?>",
431 channel_oil
->oil
.mfcc_mcastgrp
,
432 group_str
, sizeof(group_str
));
433 pim_inet4_dump("<source?>",
434 channel_oil
->oil
.mfcc_origin
, source_str
,
437 "%s %s: interface %s (vif_index=%d) is existing output for channel (S,G)=(%s,%s)",
438 __FILE__
, __PRETTY_FUNCTION__
, oif
->name
,
439 pim_ifp
->mroute_vif_index
, source_str
,
445 channel_oil
->oil
.mfcc_ttls
[pim_ifp
->mroute_vif_index
] =
448 if (pim_mroute_add(channel_oil
, __PRETTY_FUNCTION__
)) {
449 if (PIM_DEBUG_MROUTE
) {
450 char group_str
[INET_ADDRSTRLEN
];
451 char source_str
[INET_ADDRSTRLEN
];
452 pim_inet4_dump("<group?>",
453 channel_oil
->oil
.mfcc_mcastgrp
,
454 group_str
, sizeof(group_str
));
455 pim_inet4_dump("<source?>",
456 channel_oil
->oil
.mfcc_origin
, source_str
,
459 "%s %s: could not add output interface %s (vif_index=%d) for channel (S,G)=(%s,%s)",
460 __FILE__
, __PRETTY_FUNCTION__
, oif
->name
,
461 pim_ifp
->mroute_vif_index
, source_str
,
465 channel_oil
->oil
.mfcc_ttls
[pim_ifp
->mroute_vif_index
] = old_ttl
;
469 channel_oil
->oif_creation
[pim_ifp
->mroute_vif_index
] =
470 pim_time_monotonic_sec();
471 ++channel_oil
->oil_size
;
472 channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] |= proto_mask
;
474 if (PIM_DEBUG_MROUTE
) {
475 char group_str
[INET_ADDRSTRLEN
];
476 char source_str
[INET_ADDRSTRLEN
];
477 pim_inet4_dump("<group?>", channel_oil
->oil
.mfcc_mcastgrp
,
478 group_str
, sizeof(group_str
));
479 pim_inet4_dump("<source?>", channel_oil
->oil
.mfcc_origin
,
480 source_str
, sizeof(source_str
));
482 "%s %s: (S,G)=(%s,%s): proto_mask=%u OIF=%s vif_index=%d: DONE",
483 __FILE__
, __PRETTY_FUNCTION__
, source_str
, group_str
,
484 proto_mask
, oif
->name
, pim_ifp
->mroute_vif_index
);
490 int pim_channel_oil_empty(struct channel_oil
*c_oil
)
492 static uint32_t zero
[MAXVIFS
];
493 static int inited
= 0;
498 * Not sure that this is necessary, but I would rather ensure
502 memset(&zero
, 0, sizeof(uint32_t) * MAXVIFS
);
506 return !memcmp(c_oil
->oif_flags
, zero
, MAXVIFS
* sizeof(uint32_t));