]>
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
)
43 sg
.src
= c_oil
->oil
.mfcc_origin
;
44 sg
.grp
= c_oil
->oil
.mfcc_mcastgrp
;
45 sprintf(buf
, "%s IIF: %d, OIFS: ", pim_str_sg_dump(&sg
),
46 c_oil
->oil
.mfcc_parent
);
48 for (i
= 0; i
< MAXVIFS
; i
++) {
49 if (c_oil
->oil
.mfcc_ttls
[i
] != 0) {
51 sprintf(buf1
, "%d ", i
);
59 static int pim_channel_oil_compare(struct channel_oil
*c1
,
60 struct channel_oil
*c2
)
62 if (ntohl(c1
->oil
.mfcc_mcastgrp
.s_addr
)
63 < ntohl(c2
->oil
.mfcc_mcastgrp
.s_addr
))
66 if (ntohl(c1
->oil
.mfcc_mcastgrp
.s_addr
)
67 > ntohl(c2
->oil
.mfcc_mcastgrp
.s_addr
))
70 if (ntohl(c1
->oil
.mfcc_origin
.s_addr
)
71 < ntohl(c2
->oil
.mfcc_origin
.s_addr
))
74 if (ntohl(c1
->oil
.mfcc_origin
.s_addr
)
75 > ntohl(c2
->oil
.mfcc_origin
.s_addr
))
81 static int pim_oil_equal(const void *arg1
, const void *arg2
)
83 const struct channel_oil
*c1
= (const struct channel_oil
*)arg1
;
84 const struct channel_oil
*c2
= (const struct channel_oil
*)arg2
;
86 if ((c1
->oil
.mfcc_mcastgrp
.s_addr
== c2
->oil
.mfcc_mcastgrp
.s_addr
)
87 && (c1
->oil
.mfcc_origin
.s_addr
== c2
->oil
.mfcc_origin
.s_addr
))
93 static unsigned int pim_oil_hash_key(void *arg
)
95 struct channel_oil
*oil
= (struct channel_oil
*)arg
;
97 return jhash_2words(oil
->oil
.mfcc_mcastgrp
.s_addr
,
98 oil
->oil
.mfcc_origin
.s_addr
, 0);
101 void pim_oil_init(struct pim_instance
*pim
)
105 snprintf(hash_name
, 64, "PIM %s Oil Hash", pim
->vrf
->name
);
106 pim
->channel_oil_hash
= hash_create_size(8192, pim_oil_hash_key
,
107 pim_oil_equal
, hash_name
);
109 pim
->channel_oil_list
= list_new();
110 if (!pim
->channel_oil_list
) {
111 zlog_err("%s %s: failure: channel_oil_list=list_new()",
112 __FILE__
, __PRETTY_FUNCTION__
);
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_and_null(&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 ifp
= pim_if_find_by_vif_index(pim
, input_vif_index
);
179 "%s: (S,G)=%s could not find input interface for input_vif_index=%d",
180 __PRETTY_FUNCTION__
, pim_str_sg_dump(sg
),
184 c_oil
= XCALLOC(MTYPE_PIM_CHANNEL_OIL
, sizeof(*c_oil
));
186 zlog_err("PIM XCALLOC(%zu) failure", sizeof(*c_oil
));
190 c_oil
->oil
.mfcc_mcastgrp
= sg
->grp
;
191 c_oil
->oil
.mfcc_origin
= sg
->src
;
192 c_oil
= hash_get(pim
->channel_oil_hash
, c_oil
, hash_alloc_intern
);
194 c_oil
->oil
.mfcc_parent
= input_vif_index
;
195 c_oil
->oil_ref_count
= 1;
196 c_oil
->installed
= 0;
197 c_oil
->up
= pim_upstream_find(pim
, sg
);
200 listnode_add_sort(pim
->channel_oil_list
, c_oil
);
205 void pim_channel_oil_del(struct channel_oil
*c_oil
)
207 --c_oil
->oil_ref_count
;
209 if (c_oil
->oil_ref_count
< 1) {
211 * notice that listnode_delete() can't be moved
212 * into pim_channel_oil_free() because the later is
213 * called by list_delete_all_node()
216 listnode_delete(c_oil
->pim
->channel_oil_list
, c_oil
);
217 hash_release(c_oil
->pim
->channel_oil_hash
, c_oil
);
219 pim_channel_oil_free(c_oil
);
223 int pim_channel_del_oif(struct channel_oil
*channel_oil
, struct interface
*oif
,
226 struct pim_interface
*pim_ifp
;
228 zassert(channel_oil
);
234 * Don't do anything if we've been asked to remove a source
235 * that is not actually on it.
237 if (!(channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] & proto_mask
)) {
238 if (PIM_DEBUG_MROUTE
) {
239 char group_str
[INET_ADDRSTRLEN
];
240 char source_str
[INET_ADDRSTRLEN
];
241 pim_inet4_dump("<group?>",
242 channel_oil
->oil
.mfcc_mcastgrp
,
243 group_str
, sizeof(group_str
));
244 pim_inet4_dump("<source?>",
245 channel_oil
->oil
.mfcc_origin
, source_str
,
248 "%s %s: no existing protocol mask %u(%u) for requested OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)",
249 __FILE__
, __PRETTY_FUNCTION__
, proto_mask
,
251 ->oif_flags
[pim_ifp
->mroute_vif_index
],
252 oif
->name
, pim_ifp
->mroute_vif_index
,
254 .mfcc_ttls
[pim_ifp
->mroute_vif_index
],
255 source_str
, group_str
);
260 channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] &= ~proto_mask
;
262 if (channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
]) {
263 if (PIM_DEBUG_MROUTE
) {
264 char group_str
[INET_ADDRSTRLEN
];
265 char source_str
[INET_ADDRSTRLEN
];
266 pim_inet4_dump("<group?>",
267 channel_oil
->oil
.mfcc_mcastgrp
,
268 group_str
, sizeof(group_str
));
269 pim_inet4_dump("<source?>",
270 channel_oil
->oil
.mfcc_origin
, source_str
,
273 "%s %s: other protocol masks remain for requested OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)",
274 __FILE__
, __PRETTY_FUNCTION__
, oif
->name
,
275 pim_ifp
->mroute_vif_index
,
277 .mfcc_ttls
[pim_ifp
->mroute_vif_index
],
278 source_str
, group_str
);
283 channel_oil
->oil
.mfcc_ttls
[pim_ifp
->mroute_vif_index
] = 0;
285 if (pim_mroute_add(channel_oil
, __PRETTY_FUNCTION__
)) {
286 if (PIM_DEBUG_MROUTE
) {
287 char group_str
[INET_ADDRSTRLEN
];
288 char source_str
[INET_ADDRSTRLEN
];
289 pim_inet4_dump("<group?>",
290 channel_oil
->oil
.mfcc_mcastgrp
,
291 group_str
, sizeof(group_str
));
292 pim_inet4_dump("<source?>",
293 channel_oil
->oil
.mfcc_origin
, source_str
,
296 "%s %s: could not remove output interface %s (vif_index=%d) for channel (S,G)=(%s,%s)",
297 __FILE__
, __PRETTY_FUNCTION__
, oif
->name
,
298 pim_ifp
->mroute_vif_index
, source_str
,
304 --channel_oil
->oil_size
;
306 if (PIM_DEBUG_MROUTE
) {
307 char group_str
[INET_ADDRSTRLEN
];
308 char source_str
[INET_ADDRSTRLEN
];
309 pim_inet4_dump("<group?>", channel_oil
->oil
.mfcc_mcastgrp
,
310 group_str
, sizeof(group_str
));
311 pim_inet4_dump("<source?>", channel_oil
->oil
.mfcc_origin
,
312 source_str
, sizeof(source_str
));
314 "%s %s: (S,G)=(%s,%s): proto_mask=%u IIF:%d OIF=%s vif_index=%d",
315 __FILE__
, __PRETTY_FUNCTION__
, source_str
, group_str
,
316 proto_mask
, channel_oil
->oil
.mfcc_parent
, oif
->name
,
317 pim_ifp
->mroute_vif_index
);
324 int pim_channel_add_oif(struct channel_oil
*channel_oil
, struct interface
*oif
,
327 struct pim_interface
*pim_ifp
;
331 * If we've gotten here we've gone bad, but let's
335 zlog_warn("Attempt to Add OIF for non-existent channel oil");
341 #ifdef PIM_ENFORCE_LOOPFREE_MFC
343 Prevent creating MFC entry with OIF=IIF.
345 This is a protection against implementation mistakes.
347 PIM protocol implicitely ensures loopfree multicast topology.
349 IGMP must be protected against adding looped MFC entries created
350 by both source and receiver attached to the same interface. See
353 if (pim_ifp
->mroute_vif_index
== channel_oil
->oil
.mfcc_parent
) {
354 channel_oil
->oil_inherited_rescan
= 1;
355 if (PIM_DEBUG_MROUTE
) {
356 char group_str
[INET_ADDRSTRLEN
];
357 char source_str
[INET_ADDRSTRLEN
];
358 pim_inet4_dump("<group?>",
359 channel_oil
->oil
.mfcc_mcastgrp
,
360 group_str
, sizeof(group_str
));
361 pim_inet4_dump("<source?>",
362 channel_oil
->oil
.mfcc_origin
, source_str
,
365 "%s %s: refusing protocol mask %u request for IIF=OIF=%s (vif_index=%d) for channel (S,G)=(%s,%s)",
366 __FILE__
, __PRETTY_FUNCTION__
, proto_mask
,
367 oif
->name
, pim_ifp
->mroute_vif_index
,
368 source_str
, group_str
);
374 /* Prevent single protocol from subscribing same interface to
375 channel (S,G) multiple times */
376 if (channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] & proto_mask
) {
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: existing protocol mask %u requested OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)",
388 __FILE__
, __PRETTY_FUNCTION__
, proto_mask
,
389 oif
->name
, pim_ifp
->mroute_vif_index
,
391 .mfcc_ttls
[pim_ifp
->mroute_vif_index
],
392 source_str
, group_str
);
397 /* Allow other protocol to request subscription of same interface to
398 * channel (S,G), we need to note this information
400 if (channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
]
401 & PIM_OIF_FLAG_PROTO_ANY
) {
403 channel_oil
->oif_creation
[pim_ifp
->mroute_vif_index
] =
404 pim_time_monotonic_sec();
405 channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] |= proto_mask
;
406 /* Check the OIF really exists before returning, and only log
408 if (channel_oil
->oil
.mfcc_ttls
[pim_ifp
->mroute_vif_index
] < 1) {
410 char group_str
[INET_ADDRSTRLEN
];
411 char source_str
[INET_ADDRSTRLEN
];
412 pim_inet4_dump("<group?>",
413 channel_oil
->oil
.mfcc_mcastgrp
,
414 group_str
, sizeof(group_str
));
415 pim_inet4_dump("<source?>",
416 channel_oil
->oil
.mfcc_origin
,
417 source_str
, sizeof(source_str
));
419 "%s %s: new protocol mask %u requested nonexistent OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)",
420 __FILE__
, __PRETTY_FUNCTION__
,
421 proto_mask
, oif
->name
,
422 pim_ifp
->mroute_vif_index
,
423 channel_oil
->oil
.mfcc_ttls
424 [pim_ifp
->mroute_vif_index
],
425 source_str
, group_str
);
432 old_ttl
= channel_oil
->oil
.mfcc_ttls
[pim_ifp
->mroute_vif_index
];
435 if (PIM_DEBUG_MROUTE
) {
436 char group_str
[INET_ADDRSTRLEN
];
437 char source_str
[INET_ADDRSTRLEN
];
438 pim_inet4_dump("<group?>",
439 channel_oil
->oil
.mfcc_mcastgrp
,
440 group_str
, sizeof(group_str
));
441 pim_inet4_dump("<source?>",
442 channel_oil
->oil
.mfcc_origin
, source_str
,
445 "%s %s: interface %s (vif_index=%d) is existing output for channel (S,G)=(%s,%s)",
446 __FILE__
, __PRETTY_FUNCTION__
, oif
->name
,
447 pim_ifp
->mroute_vif_index
, source_str
,
453 channel_oil
->oil
.mfcc_ttls
[pim_ifp
->mroute_vif_index
] =
456 if (pim_mroute_add(channel_oil
, __PRETTY_FUNCTION__
)) {
457 if (PIM_DEBUG_MROUTE
) {
458 char group_str
[INET_ADDRSTRLEN
];
459 char source_str
[INET_ADDRSTRLEN
];
460 pim_inet4_dump("<group?>",
461 channel_oil
->oil
.mfcc_mcastgrp
,
462 group_str
, sizeof(group_str
));
463 pim_inet4_dump("<source?>",
464 channel_oil
->oil
.mfcc_origin
, source_str
,
467 "%s %s: could not add output interface %s (vif_index=%d) for channel (S,G)=(%s,%s)",
468 __FILE__
, __PRETTY_FUNCTION__
, oif
->name
,
469 pim_ifp
->mroute_vif_index
, source_str
,
473 channel_oil
->oil
.mfcc_ttls
[pim_ifp
->mroute_vif_index
] = old_ttl
;
477 channel_oil
->oif_creation
[pim_ifp
->mroute_vif_index
] =
478 pim_time_monotonic_sec();
479 ++channel_oil
->oil_size
;
480 channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] |= proto_mask
;
482 if (PIM_DEBUG_MROUTE
) {
483 char group_str
[INET_ADDRSTRLEN
];
484 char source_str
[INET_ADDRSTRLEN
];
485 pim_inet4_dump("<group?>", channel_oil
->oil
.mfcc_mcastgrp
,
486 group_str
, sizeof(group_str
));
487 pim_inet4_dump("<source?>", channel_oil
->oil
.mfcc_origin
,
488 source_str
, sizeof(source_str
));
490 "%s %s: (S,G)=(%s,%s): proto_mask=%u OIF=%s vif_index=%d: DONE",
491 __FILE__
, __PRETTY_FUNCTION__
, source_str
, group_str
,
492 proto_mask
, oif
->name
, pim_ifp
->mroute_vif_index
);
498 int pim_channel_oil_empty(struct channel_oil
*c_oil
)
500 static uint32_t zero
[MAXVIFS
];
501 static int inited
= 0;
506 * Not sure that this is necessary, but I would rather ensure
510 memset(&zero
, 0, sizeof(uint32_t) * MAXVIFS
);
514 return !memcmp(c_oil
->oif_flags
, zero
, MAXVIFS
* sizeof(uint32_t));