]>
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 sprintf(buf
, "%s IIF: %d, OIFS: ", pim_str_sg_dump(&sg
),
47 c_oil
->oil
.mfcc_parent
);
49 for (i
= 0; i
< MAXVIFS
; i
++) {
50 if (c_oil
->oil
.mfcc_ttls
[i
] != 0) {
52 sprintf(buf1
, "%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 int 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,
112 pim
->channel_oil_list
= list_new();
113 if (!pim
->channel_oil_list
) {
114 zlog_err("%s %s: failure: channel_oil_list=list_new()",
115 __FILE__
, __PRETTY_FUNCTION__
);
118 pim
->channel_oil_list
->del
= (void (*)(void *))pim_channel_oil_free
;
119 pim
->channel_oil_list
->cmp
=
120 (int (*)(void *, void *))pim_channel_oil_compare
;
123 void pim_oil_terminate(struct pim_instance
*pim
)
125 if (pim
->channel_oil_list
)
126 list_delete_and_null(&pim
->channel_oil_list
);
128 if (pim
->channel_oil_hash
)
129 hash_free(pim
->channel_oil_hash
);
130 pim
->channel_oil_hash
= NULL
;
133 void pim_channel_oil_free(struct channel_oil
*c_oil
)
135 XFREE(MTYPE_PIM_CHANNEL_OIL
, c_oil
);
138 struct channel_oil
*pim_find_channel_oil(struct pim_instance
*pim
,
139 struct prefix_sg
*sg
)
141 struct channel_oil
*c_oil
= NULL
;
142 struct channel_oil lookup
;
144 lookup
.oil
.mfcc_mcastgrp
= sg
->grp
;
145 lookup
.oil
.mfcc_origin
= sg
->src
;
147 c_oil
= hash_lookup(pim
->channel_oil_hash
, &lookup
);
152 struct channel_oil
*pim_channel_oil_add(struct pim_instance
*pim
,
153 struct prefix_sg
*sg
,
156 struct channel_oil
*c_oil
;
157 struct interface
*ifp
;
159 c_oil
= pim_find_channel_oil(pim
, sg
);
161 if (c_oil
->oil
.mfcc_parent
!= input_vif_index
) {
162 c_oil
->oil_inherited_rescan
= 1;
163 if (PIM_DEBUG_MROUTE
)
165 "%s: Existing channel oil %s points to %d, modifying to point at %d",
168 c_oil
->oil
.mfcc_parent
,
171 c_oil
->oil
.mfcc_parent
= input_vif_index
;
172 ++c_oil
->oil_ref_count
;
173 c_oil
->up
= pim_upstream_find(
174 pim
, sg
); // channel might be present prior to upstream
178 ifp
= pim_if_find_by_vif_index(pim
, input_vif_index
);
182 "%s: (S,G)=%s could not find input interface for input_vif_index=%d",
183 __PRETTY_FUNCTION__
, pim_str_sg_dump(sg
),
187 c_oil
= XCALLOC(MTYPE_PIM_CHANNEL_OIL
, sizeof(*c_oil
));
189 zlog_err("PIM XCALLOC(%zu) failure", sizeof(*c_oil
));
193 c_oil
->oil
.mfcc_mcastgrp
= sg
->grp
;
194 c_oil
->oil
.mfcc_origin
= sg
->src
;
195 c_oil
= hash_get(pim
->channel_oil_hash
, c_oil
, hash_alloc_intern
);
197 c_oil
->oil
.mfcc_parent
= input_vif_index
;
198 c_oil
->oil_ref_count
= 1;
199 c_oil
->installed
= 0;
200 c_oil
->up
= pim_upstream_find(pim
, sg
);
203 listnode_add_sort(pim
->channel_oil_list
, c_oil
);
208 void pim_channel_oil_del(struct channel_oil
*c_oil
)
210 --c_oil
->oil_ref_count
;
212 if (c_oil
->oil_ref_count
< 1) {
214 * notice that listnode_delete() can't be moved
215 * into pim_channel_oil_free() because the later is
216 * called by list_delete_all_node()
219 listnode_delete(c_oil
->pim
->channel_oil_list
, c_oil
);
220 hash_release(c_oil
->pim
->channel_oil_hash
, c_oil
);
222 pim_channel_oil_free(c_oil
);
226 int pim_channel_del_oif(struct channel_oil
*channel_oil
, struct interface
*oif
,
229 struct pim_interface
*pim_ifp
;
231 zassert(channel_oil
);
237 * Don't do anything if we've been asked to remove a source
238 * that is not actually on it.
240 if (!(channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] & proto_mask
)) {
241 if (PIM_DEBUG_MROUTE
) {
242 char group_str
[INET_ADDRSTRLEN
];
243 char source_str
[INET_ADDRSTRLEN
];
244 pim_inet4_dump("<group?>",
245 channel_oil
->oil
.mfcc_mcastgrp
,
246 group_str
, sizeof(group_str
));
247 pim_inet4_dump("<source?>",
248 channel_oil
->oil
.mfcc_origin
, source_str
,
251 "%s %s: no existing protocol mask %u(%u) for requested OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)",
252 __FILE__
, __PRETTY_FUNCTION__
, proto_mask
,
254 ->oif_flags
[pim_ifp
->mroute_vif_index
],
255 oif
->name
, pim_ifp
->mroute_vif_index
,
257 .mfcc_ttls
[pim_ifp
->mroute_vif_index
],
258 source_str
, group_str
);
263 channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] &= ~proto_mask
;
265 if (channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
]) {
266 if (PIM_DEBUG_MROUTE
) {
267 char group_str
[INET_ADDRSTRLEN
];
268 char source_str
[INET_ADDRSTRLEN
];
269 pim_inet4_dump("<group?>",
270 channel_oil
->oil
.mfcc_mcastgrp
,
271 group_str
, sizeof(group_str
));
272 pim_inet4_dump("<source?>",
273 channel_oil
->oil
.mfcc_origin
, source_str
,
276 "%s %s: other protocol masks remain for requested OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)",
277 __FILE__
, __PRETTY_FUNCTION__
, oif
->name
,
278 pim_ifp
->mroute_vif_index
,
280 .mfcc_ttls
[pim_ifp
->mroute_vif_index
],
281 source_str
, group_str
);
286 channel_oil
->oil
.mfcc_ttls
[pim_ifp
->mroute_vif_index
] = 0;
288 if (pim_mroute_add(channel_oil
, __PRETTY_FUNCTION__
)) {
289 if (PIM_DEBUG_MROUTE
) {
290 char group_str
[INET_ADDRSTRLEN
];
291 char source_str
[INET_ADDRSTRLEN
];
292 pim_inet4_dump("<group?>",
293 channel_oil
->oil
.mfcc_mcastgrp
,
294 group_str
, sizeof(group_str
));
295 pim_inet4_dump("<source?>",
296 channel_oil
->oil
.mfcc_origin
, source_str
,
299 "%s %s: could not remove output interface %s (vif_index=%d) for channel (S,G)=(%s,%s)",
300 __FILE__
, __PRETTY_FUNCTION__
, oif
->name
,
301 pim_ifp
->mroute_vif_index
, source_str
,
307 --channel_oil
->oil_size
;
309 if (PIM_DEBUG_MROUTE
) {
310 char group_str
[INET_ADDRSTRLEN
];
311 char source_str
[INET_ADDRSTRLEN
];
312 pim_inet4_dump("<group?>", channel_oil
->oil
.mfcc_mcastgrp
,
313 group_str
, sizeof(group_str
));
314 pim_inet4_dump("<source?>", channel_oil
->oil
.mfcc_origin
,
315 source_str
, sizeof(source_str
));
317 "%s %s: (S,G)=(%s,%s): proto_mask=%u IIF:%d OIF=%s vif_index=%d",
318 __FILE__
, __PRETTY_FUNCTION__
, source_str
, group_str
,
319 proto_mask
, channel_oil
->oil
.mfcc_parent
, oif
->name
,
320 pim_ifp
->mroute_vif_index
);
327 int pim_channel_add_oif(struct channel_oil
*channel_oil
, struct interface
*oif
,
330 struct pim_interface
*pim_ifp
;
334 * If we've gotten here we've gone bad, but let's
338 zlog_warn("Attempt to Add OIF for non-existent channel oil");
344 #ifdef PIM_ENFORCE_LOOPFREE_MFC
346 Prevent creating MFC entry with OIF=IIF.
348 This is a protection against implementation mistakes.
350 PIM protocol implicitely ensures loopfree multicast topology.
352 IGMP must be protected against adding looped MFC entries created
353 by both source and receiver attached to the same interface. See
356 if (pim_ifp
->mroute_vif_index
== channel_oil
->oil
.mfcc_parent
) {
357 channel_oil
->oil_inherited_rescan
= 1;
358 if (PIM_DEBUG_MROUTE
) {
359 char group_str
[INET_ADDRSTRLEN
];
360 char source_str
[INET_ADDRSTRLEN
];
361 pim_inet4_dump("<group?>",
362 channel_oil
->oil
.mfcc_mcastgrp
,
363 group_str
, sizeof(group_str
));
364 pim_inet4_dump("<source?>",
365 channel_oil
->oil
.mfcc_origin
, source_str
,
368 "%s %s: refusing protocol mask %u request for IIF=OIF=%s (vif_index=%d) for channel (S,G)=(%s,%s)",
369 __FILE__
, __PRETTY_FUNCTION__
, proto_mask
,
370 oif
->name
, pim_ifp
->mroute_vif_index
,
371 source_str
, group_str
);
377 /* Prevent single protocol from subscribing same interface to
378 channel (S,G) multiple times */
379 if (channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] & proto_mask
) {
380 if (PIM_DEBUG_MROUTE
) {
381 char group_str
[INET_ADDRSTRLEN
];
382 char source_str
[INET_ADDRSTRLEN
];
383 pim_inet4_dump("<group?>",
384 channel_oil
->oil
.mfcc_mcastgrp
,
385 group_str
, sizeof(group_str
));
386 pim_inet4_dump("<source?>",
387 channel_oil
->oil
.mfcc_origin
, source_str
,
390 "%s %s: existing protocol mask %u requested OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)",
391 __FILE__
, __PRETTY_FUNCTION__
, proto_mask
,
392 oif
->name
, pim_ifp
->mroute_vif_index
,
394 .mfcc_ttls
[pim_ifp
->mroute_vif_index
],
395 source_str
, group_str
);
400 /* Allow other protocol to request subscription of same interface to
401 * channel (S,G), we need to note this information
403 if (channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
]
404 & PIM_OIF_FLAG_PROTO_ANY
) {
406 channel_oil
->oif_creation
[pim_ifp
->mroute_vif_index
] =
407 pim_time_monotonic_sec();
408 channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] |= proto_mask
;
409 /* Check the OIF really exists before returning, and only log
411 if (channel_oil
->oil
.mfcc_ttls
[pim_ifp
->mroute_vif_index
] < 1) {
413 char group_str
[INET_ADDRSTRLEN
];
414 char source_str
[INET_ADDRSTRLEN
];
415 pim_inet4_dump("<group?>",
416 channel_oil
->oil
.mfcc_mcastgrp
,
417 group_str
, sizeof(group_str
));
418 pim_inet4_dump("<source?>",
419 channel_oil
->oil
.mfcc_origin
,
420 source_str
, sizeof(source_str
));
422 "%s %s: new protocol mask %u requested nonexistent OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)",
423 __FILE__
, __PRETTY_FUNCTION__
,
424 proto_mask
, oif
->name
,
425 pim_ifp
->mroute_vif_index
,
426 channel_oil
->oil
.mfcc_ttls
427 [pim_ifp
->mroute_vif_index
],
428 source_str
, group_str
);
435 old_ttl
= channel_oil
->oil
.mfcc_ttls
[pim_ifp
->mroute_vif_index
];
438 if (PIM_DEBUG_MROUTE
) {
439 char group_str
[INET_ADDRSTRLEN
];
440 char source_str
[INET_ADDRSTRLEN
];
441 pim_inet4_dump("<group?>",
442 channel_oil
->oil
.mfcc_mcastgrp
,
443 group_str
, sizeof(group_str
));
444 pim_inet4_dump("<source?>",
445 channel_oil
->oil
.mfcc_origin
, source_str
,
448 "%s %s: interface %s (vif_index=%d) is existing output for channel (S,G)=(%s,%s)",
449 __FILE__
, __PRETTY_FUNCTION__
, oif
->name
,
450 pim_ifp
->mroute_vif_index
, source_str
,
456 channel_oil
->oil
.mfcc_ttls
[pim_ifp
->mroute_vif_index
] =
459 if (pim_mroute_add(channel_oil
, __PRETTY_FUNCTION__
)) {
460 if (PIM_DEBUG_MROUTE
) {
461 char group_str
[INET_ADDRSTRLEN
];
462 char source_str
[INET_ADDRSTRLEN
];
463 pim_inet4_dump("<group?>",
464 channel_oil
->oil
.mfcc_mcastgrp
,
465 group_str
, sizeof(group_str
));
466 pim_inet4_dump("<source?>",
467 channel_oil
->oil
.mfcc_origin
, source_str
,
470 "%s %s: could not add output interface %s (vif_index=%d) for channel (S,G)=(%s,%s)",
471 __FILE__
, __PRETTY_FUNCTION__
, oif
->name
,
472 pim_ifp
->mroute_vif_index
, source_str
,
476 channel_oil
->oil
.mfcc_ttls
[pim_ifp
->mroute_vif_index
] = old_ttl
;
480 channel_oil
->oif_creation
[pim_ifp
->mroute_vif_index
] =
481 pim_time_monotonic_sec();
482 ++channel_oil
->oil_size
;
483 channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] |= proto_mask
;
485 if (PIM_DEBUG_MROUTE
) {
486 char group_str
[INET_ADDRSTRLEN
];
487 char source_str
[INET_ADDRSTRLEN
];
488 pim_inet4_dump("<group?>", channel_oil
->oil
.mfcc_mcastgrp
,
489 group_str
, sizeof(group_str
));
490 pim_inet4_dump("<source?>", channel_oil
->oil
.mfcc_origin
,
491 source_str
, sizeof(source_str
));
493 "%s %s: (S,G)=(%s,%s): proto_mask=%u OIF=%s vif_index=%d: DONE",
494 __FILE__
, __PRETTY_FUNCTION__
, source_str
, group_str
,
495 proto_mask
, oif
->name
, pim_ifp
->mroute_vif_index
);
501 int pim_channel_oil_empty(struct channel_oil
*c_oil
)
503 static uint32_t zero
[MAXVIFS
];
504 static int inited
= 0;
509 * Not sure that this is necessary, but I would rather ensure
513 memset(&zero
, 0, sizeof(uint32_t) * MAXVIFS
);
517 return !memcmp(c_oil
->oif_flags
, zero
, MAXVIFS
* sizeof(uint32_t));