]>
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, pim_oil_hash_key
,
108 pim_oil_equal
, hash_name
);
110 pim
->channel_oil_list
= list_new();
111 if (!pim
->channel_oil_list
) {
112 zlog_err("%s %s: failure: channel_oil_list=list_new()",
113 __FILE__
, __PRETTY_FUNCTION__
);
116 pim
->channel_oil_list
->del
= (void (*)(void *))pim_channel_oil_free
;
117 pim
->channel_oil_list
->cmp
=
118 (int (*)(void *, void *))pim_channel_oil_compare
;
121 void pim_oil_terminate(struct pim_instance
*pim
)
123 if (pim
->channel_oil_list
)
124 list_delete_and_null(&pim
->channel_oil_list
);
126 if (pim
->channel_oil_hash
)
127 hash_free(pim
->channel_oil_hash
);
128 pim
->channel_oil_hash
= NULL
;
131 void pim_channel_oil_free(struct channel_oil
*c_oil
)
133 XFREE(MTYPE_PIM_CHANNEL_OIL
, c_oil
);
136 struct channel_oil
*pim_find_channel_oil(struct pim_instance
*pim
,
137 struct prefix_sg
*sg
)
139 struct channel_oil
*c_oil
= NULL
;
140 struct channel_oil lookup
;
142 lookup
.oil
.mfcc_mcastgrp
= sg
->grp
;
143 lookup
.oil
.mfcc_origin
= sg
->src
;
145 c_oil
= hash_lookup(pim
->channel_oil_hash
, &lookup
);
150 struct channel_oil
*pim_channel_oil_add(struct pim_instance
*pim
,
151 struct prefix_sg
*sg
,
154 struct channel_oil
*c_oil
;
155 struct interface
*ifp
;
157 c_oil
= pim_find_channel_oil(pim
, sg
);
159 if (c_oil
->oil
.mfcc_parent
!= input_vif_index
) {
160 c_oil
->oil_inherited_rescan
= 1;
161 if (PIM_DEBUG_MROUTE
)
163 "%s: Existing channel oil %s points to %d, modifying to point at %d",
166 c_oil
->oil
.mfcc_parent
,
169 c_oil
->oil
.mfcc_parent
= input_vif_index
;
170 ++c_oil
->oil_ref_count
;
171 c_oil
->up
= pim_upstream_find(
172 pim
, sg
); // channel might be present prior to upstream
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
),
185 c_oil
= XCALLOC(MTYPE_PIM_CHANNEL_OIL
, sizeof(*c_oil
));
187 zlog_err("PIM XCALLOC(%zu) failure", sizeof(*c_oil
));
191 c_oil
->oil
.mfcc_mcastgrp
= sg
->grp
;
192 c_oil
->oil
.mfcc_origin
= sg
->src
;
193 c_oil
= hash_get(pim
->channel_oil_hash
, c_oil
, hash_alloc_intern
);
195 c_oil
->oil
.mfcc_parent
= input_vif_index
;
196 c_oil
->oil_ref_count
= 1;
197 c_oil
->installed
= 0;
198 c_oil
->up
= pim_upstream_find(pim
, sg
);
201 listnode_add_sort(pim
->channel_oil_list
, c_oil
);
206 void pim_channel_oil_del(struct channel_oil
*c_oil
)
208 --c_oil
->oil_ref_count
;
210 if (c_oil
->oil_ref_count
< 1) {
212 * notice that listnode_delete() can't be moved
213 * into pim_channel_oil_free() because the later is
214 * called by list_delete_all_node()
217 listnode_delete(c_oil
->pim
->channel_oil_list
, c_oil
);
218 hash_release(c_oil
->pim
->channel_oil_hash
, c_oil
);
220 pim_channel_oil_free(c_oil
);
224 int pim_channel_del_oif(struct channel_oil
*channel_oil
, struct interface
*oif
,
227 struct pim_interface
*pim_ifp
;
229 zassert(channel_oil
);
235 * Don't do anything if we've been asked to remove a source
236 * that is not actually on it.
238 if (!(channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] & proto_mask
)) {
239 if (PIM_DEBUG_MROUTE
) {
240 char group_str
[INET_ADDRSTRLEN
];
241 char source_str
[INET_ADDRSTRLEN
];
242 pim_inet4_dump("<group?>",
243 channel_oil
->oil
.mfcc_mcastgrp
,
244 group_str
, sizeof(group_str
));
245 pim_inet4_dump("<source?>",
246 channel_oil
->oil
.mfcc_origin
, source_str
,
249 "%s %s: no existing protocol mask %u(%u) for requested OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)",
250 __FILE__
, __PRETTY_FUNCTION__
, proto_mask
,
252 ->oif_flags
[pim_ifp
->mroute_vif_index
],
253 oif
->name
, pim_ifp
->mroute_vif_index
,
255 .mfcc_ttls
[pim_ifp
->mroute_vif_index
],
256 source_str
, group_str
);
261 channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] &= ~proto_mask
;
263 if (channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
]) {
264 if (PIM_DEBUG_MROUTE
) {
265 char group_str
[INET_ADDRSTRLEN
];
266 char source_str
[INET_ADDRSTRLEN
];
267 pim_inet4_dump("<group?>",
268 channel_oil
->oil
.mfcc_mcastgrp
,
269 group_str
, sizeof(group_str
));
270 pim_inet4_dump("<source?>",
271 channel_oil
->oil
.mfcc_origin
, source_str
,
274 "%s %s: other protocol masks remain for requested OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)",
275 __FILE__
, __PRETTY_FUNCTION__
, oif
->name
,
276 pim_ifp
->mroute_vif_index
,
278 .mfcc_ttls
[pim_ifp
->mroute_vif_index
],
279 source_str
, group_str
);
284 channel_oil
->oil
.mfcc_ttls
[pim_ifp
->mroute_vif_index
] = 0;
286 if (pim_mroute_add(channel_oil
, __PRETTY_FUNCTION__
)) {
287 if (PIM_DEBUG_MROUTE
) {
288 char group_str
[INET_ADDRSTRLEN
];
289 char source_str
[INET_ADDRSTRLEN
];
290 pim_inet4_dump("<group?>",
291 channel_oil
->oil
.mfcc_mcastgrp
,
292 group_str
, sizeof(group_str
));
293 pim_inet4_dump("<source?>",
294 channel_oil
->oil
.mfcc_origin
, source_str
,
297 "%s %s: could not remove output interface %s (vif_index=%d) for channel (S,G)=(%s,%s)",
298 __FILE__
, __PRETTY_FUNCTION__
, oif
->name
,
299 pim_ifp
->mroute_vif_index
, source_str
,
305 --channel_oil
->oil_size
;
307 if (PIM_DEBUG_MROUTE
) {
308 char group_str
[INET_ADDRSTRLEN
];
309 char source_str
[INET_ADDRSTRLEN
];
310 pim_inet4_dump("<group?>", channel_oil
->oil
.mfcc_mcastgrp
,
311 group_str
, sizeof(group_str
));
312 pim_inet4_dump("<source?>", channel_oil
->oil
.mfcc_origin
,
313 source_str
, sizeof(source_str
));
315 "%s %s: (S,G)=(%s,%s): proto_mask=%u IIF:%d OIF=%s vif_index=%d",
316 __FILE__
, __PRETTY_FUNCTION__
, source_str
, group_str
,
317 proto_mask
, channel_oil
->oil
.mfcc_parent
, oif
->name
,
318 pim_ifp
->mroute_vif_index
);
325 int pim_channel_add_oif(struct channel_oil
*channel_oil
, struct interface
*oif
,
328 struct pim_interface
*pim_ifp
;
332 * If we've gotten here we've gone bad, but let's
336 zlog_warn("Attempt to Add OIF for non-existent channel oil");
342 #ifdef PIM_ENFORCE_LOOPFREE_MFC
344 Prevent creating MFC entry with OIF=IIF.
346 This is a protection against implementation mistakes.
348 PIM protocol implicitely ensures loopfree multicast topology.
350 IGMP must be protected against adding looped MFC entries created
351 by both source and receiver attached to the same interface. See
354 if (pim_ifp
->mroute_vif_index
== channel_oil
->oil
.mfcc_parent
) {
355 channel_oil
->oil_inherited_rescan
= 1;
356 if (PIM_DEBUG_MROUTE
) {
357 char group_str
[INET_ADDRSTRLEN
];
358 char source_str
[INET_ADDRSTRLEN
];
359 pim_inet4_dump("<group?>",
360 channel_oil
->oil
.mfcc_mcastgrp
,
361 group_str
, sizeof(group_str
));
362 pim_inet4_dump("<source?>",
363 channel_oil
->oil
.mfcc_origin
, source_str
,
366 "%s %s: refusing protocol mask %u request for IIF=OIF=%s (vif_index=%d) for channel (S,G)=(%s,%s)",
367 __FILE__
, __PRETTY_FUNCTION__
, proto_mask
,
368 oif
->name
, pim_ifp
->mroute_vif_index
,
369 source_str
, group_str
);
375 /* Prevent single protocol from subscribing same interface to
376 channel (S,G) multiple times */
377 if (channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] & proto_mask
) {
378 if (PIM_DEBUG_MROUTE
) {
379 char group_str
[INET_ADDRSTRLEN
];
380 char source_str
[INET_ADDRSTRLEN
];
381 pim_inet4_dump("<group?>",
382 channel_oil
->oil
.mfcc_mcastgrp
,
383 group_str
, sizeof(group_str
));
384 pim_inet4_dump("<source?>",
385 channel_oil
->oil
.mfcc_origin
, source_str
,
388 "%s %s: existing protocol mask %u requested OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)",
389 __FILE__
, __PRETTY_FUNCTION__
, proto_mask
,
390 oif
->name
, pim_ifp
->mroute_vif_index
,
392 .mfcc_ttls
[pim_ifp
->mroute_vif_index
],
393 source_str
, group_str
);
398 /* Allow other protocol to request subscription of same interface to
399 * channel (S,G), we need to note this information
401 if (channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
]
402 & PIM_OIF_FLAG_PROTO_ANY
) {
404 channel_oil
->oif_creation
[pim_ifp
->mroute_vif_index
] =
405 pim_time_monotonic_sec();
406 channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] |= proto_mask
;
407 /* Check the OIF really exists before returning, and only log
409 if (channel_oil
->oil
.mfcc_ttls
[pim_ifp
->mroute_vif_index
] < 1) {
411 char group_str
[INET_ADDRSTRLEN
];
412 char source_str
[INET_ADDRSTRLEN
];
413 pim_inet4_dump("<group?>",
414 channel_oil
->oil
.mfcc_mcastgrp
,
415 group_str
, sizeof(group_str
));
416 pim_inet4_dump("<source?>",
417 channel_oil
->oil
.mfcc_origin
,
418 source_str
, sizeof(source_str
));
420 "%s %s: new protocol mask %u requested nonexistent OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)",
421 __FILE__
, __PRETTY_FUNCTION__
,
422 proto_mask
, oif
->name
,
423 pim_ifp
->mroute_vif_index
,
424 channel_oil
->oil
.mfcc_ttls
425 [pim_ifp
->mroute_vif_index
],
426 source_str
, group_str
);
433 old_ttl
= channel_oil
->oil
.mfcc_ttls
[pim_ifp
->mroute_vif_index
];
436 if (PIM_DEBUG_MROUTE
) {
437 char group_str
[INET_ADDRSTRLEN
];
438 char source_str
[INET_ADDRSTRLEN
];
439 pim_inet4_dump("<group?>",
440 channel_oil
->oil
.mfcc_mcastgrp
,
441 group_str
, sizeof(group_str
));
442 pim_inet4_dump("<source?>",
443 channel_oil
->oil
.mfcc_origin
, source_str
,
446 "%s %s: interface %s (vif_index=%d) is existing output for channel (S,G)=(%s,%s)",
447 __FILE__
, __PRETTY_FUNCTION__
, oif
->name
,
448 pim_ifp
->mroute_vif_index
, source_str
,
454 channel_oil
->oil
.mfcc_ttls
[pim_ifp
->mroute_vif_index
] =
457 if (pim_mroute_add(channel_oil
, __PRETTY_FUNCTION__
)) {
458 if (PIM_DEBUG_MROUTE
) {
459 char group_str
[INET_ADDRSTRLEN
];
460 char source_str
[INET_ADDRSTRLEN
];
461 pim_inet4_dump("<group?>",
462 channel_oil
->oil
.mfcc_mcastgrp
,
463 group_str
, sizeof(group_str
));
464 pim_inet4_dump("<source?>",
465 channel_oil
->oil
.mfcc_origin
, source_str
,
468 "%s %s: could not add output interface %s (vif_index=%d) for channel (S,G)=(%s,%s)",
469 __FILE__
, __PRETTY_FUNCTION__
, oif
->name
,
470 pim_ifp
->mroute_vif_index
, source_str
,
474 channel_oil
->oil
.mfcc_ttls
[pim_ifp
->mroute_vif_index
] = old_ttl
;
478 channel_oil
->oif_creation
[pim_ifp
->mroute_vif_index
] =
479 pim_time_monotonic_sec();
480 ++channel_oil
->oil_size
;
481 channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] |= proto_mask
;
483 if (PIM_DEBUG_MROUTE
) {
484 char group_str
[INET_ADDRSTRLEN
];
485 char source_str
[INET_ADDRSTRLEN
];
486 pim_inet4_dump("<group?>", channel_oil
->oil
.mfcc_mcastgrp
,
487 group_str
, sizeof(group_str
));
488 pim_inet4_dump("<source?>", channel_oil
->oil
.mfcc_origin
,
489 source_str
, sizeof(source_str
));
491 "%s %s: (S,G)=(%s,%s): proto_mask=%u OIF=%s vif_index=%d: DONE",
492 __FILE__
, __PRETTY_FUNCTION__
, source_str
, group_str
,
493 proto_mask
, oif
->name
, pim_ifp
->mroute_vif_index
);
499 int pim_channel_oil_empty(struct channel_oil
*c_oil
)
501 static uint32_t zero
[MAXVIFS
];
502 static int inited
= 0;
507 * Not sure that this is necessary, but I would rather ensure
511 memset(&zero
, 0, sizeof(uint32_t) * MAXVIFS
);
515 return !memcmp(c_oil
->oif_flags
, zero
, MAXVIFS
* sizeof(uint32_t));