]>
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 pim
->channel_oil_list
->del
= (void (*)(void *))pim_channel_oil_free
;
111 pim
->channel_oil_list
->cmp
=
112 (int (*)(void *, void *))pim_channel_oil_compare
;
115 void pim_oil_terminate(struct pim_instance
*pim
)
117 if (pim
->channel_oil_list
)
118 list_delete_and_null(&pim
->channel_oil_list
);
120 if (pim
->channel_oil_hash
)
121 hash_free(pim
->channel_oil_hash
);
122 pim
->channel_oil_hash
= NULL
;
125 void pim_channel_oil_free(struct channel_oil
*c_oil
)
127 XFREE(MTYPE_PIM_CHANNEL_OIL
, c_oil
);
130 struct channel_oil
*pim_find_channel_oil(struct pim_instance
*pim
,
131 struct prefix_sg
*sg
)
133 struct channel_oil
*c_oil
= NULL
;
134 struct channel_oil lookup
;
136 lookup
.oil
.mfcc_mcastgrp
= sg
->grp
;
137 lookup
.oil
.mfcc_origin
= sg
->src
;
139 c_oil
= hash_lookup(pim
->channel_oil_hash
, &lookup
);
144 struct channel_oil
*pim_channel_oil_add(struct pim_instance
*pim
,
145 struct prefix_sg
*sg
,
148 struct channel_oil
*c_oil
;
149 struct interface
*ifp
;
151 c_oil
= pim_find_channel_oil(pim
, sg
);
153 if (c_oil
->oil
.mfcc_parent
!= input_vif_index
) {
154 c_oil
->oil_inherited_rescan
= 1;
155 if (PIM_DEBUG_MROUTE
)
157 "%s: Existing channel oil %s points to %d, modifying to point at %d",
160 c_oil
->oil
.mfcc_parent
,
163 c_oil
->oil
.mfcc_parent
= input_vif_index
;
164 ++c_oil
->oil_ref_count
;
165 c_oil
->up
= pim_upstream_find(
166 pim
, sg
); // channel might be present prior to upstream
170 ifp
= pim_if_find_by_vif_index(pim
, input_vif_index
);
174 "%s: (S,G)=%s could not find input interface for input_vif_index=%d",
175 __PRETTY_FUNCTION__
, pim_str_sg_dump(sg
),
179 c_oil
= XCALLOC(MTYPE_PIM_CHANNEL_OIL
, sizeof(*c_oil
));
181 c_oil
->oil
.mfcc_mcastgrp
= sg
->grp
;
182 c_oil
->oil
.mfcc_origin
= sg
->src
;
183 c_oil
= hash_get(pim
->channel_oil_hash
, c_oil
, hash_alloc_intern
);
185 c_oil
->oil
.mfcc_parent
= input_vif_index
;
186 c_oil
->oil_ref_count
= 1;
187 c_oil
->installed
= 0;
188 c_oil
->up
= pim_upstream_find(pim
, sg
);
191 listnode_add_sort(pim
->channel_oil_list
, c_oil
);
196 void pim_channel_oil_del(struct channel_oil
*c_oil
)
198 --c_oil
->oil_ref_count
;
200 if (c_oil
->oil_ref_count
< 1) {
202 * notice that listnode_delete() can't be moved
203 * into pim_channel_oil_free() because the later is
204 * called by list_delete_all_node()
207 listnode_delete(c_oil
->pim
->channel_oil_list
, c_oil
);
208 hash_release(c_oil
->pim
->channel_oil_hash
, c_oil
);
210 pim_channel_oil_free(c_oil
);
214 int pim_channel_del_oif(struct channel_oil
*channel_oil
, struct interface
*oif
,
217 struct pim_interface
*pim_ifp
;
219 zassert(channel_oil
);
225 * Don't do anything if we've been asked to remove a source
226 * that is not actually on it.
228 if (!(channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] & proto_mask
)) {
229 if (PIM_DEBUG_MROUTE
) {
230 char group_str
[INET_ADDRSTRLEN
];
231 char source_str
[INET_ADDRSTRLEN
];
232 pim_inet4_dump("<group?>",
233 channel_oil
->oil
.mfcc_mcastgrp
,
234 group_str
, sizeof(group_str
));
235 pim_inet4_dump("<source?>",
236 channel_oil
->oil
.mfcc_origin
, source_str
,
239 "%s %s: no existing protocol mask %u(%u) for requested OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)",
240 __FILE__
, __PRETTY_FUNCTION__
, proto_mask
,
242 ->oif_flags
[pim_ifp
->mroute_vif_index
],
243 oif
->name
, pim_ifp
->mroute_vif_index
,
245 .mfcc_ttls
[pim_ifp
->mroute_vif_index
],
246 source_str
, group_str
);
251 channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] &= ~proto_mask
;
253 if (channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
]) {
254 if (PIM_DEBUG_MROUTE
) {
255 char group_str
[INET_ADDRSTRLEN
];
256 char source_str
[INET_ADDRSTRLEN
];
257 pim_inet4_dump("<group?>",
258 channel_oil
->oil
.mfcc_mcastgrp
,
259 group_str
, sizeof(group_str
));
260 pim_inet4_dump("<source?>",
261 channel_oil
->oil
.mfcc_origin
, source_str
,
264 "%s %s: other protocol masks remain for requested OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)",
265 __FILE__
, __PRETTY_FUNCTION__
, oif
->name
,
266 pim_ifp
->mroute_vif_index
,
268 .mfcc_ttls
[pim_ifp
->mroute_vif_index
],
269 source_str
, group_str
);
274 channel_oil
->oil
.mfcc_ttls
[pim_ifp
->mroute_vif_index
] = 0;
276 if (pim_mroute_add(channel_oil
, __PRETTY_FUNCTION__
)) {
277 if (PIM_DEBUG_MROUTE
) {
278 char group_str
[INET_ADDRSTRLEN
];
279 char source_str
[INET_ADDRSTRLEN
];
280 pim_inet4_dump("<group?>",
281 channel_oil
->oil
.mfcc_mcastgrp
,
282 group_str
, sizeof(group_str
));
283 pim_inet4_dump("<source?>",
284 channel_oil
->oil
.mfcc_origin
, source_str
,
287 "%s %s: could not remove output interface %s (vif_index=%d) for channel (S,G)=(%s,%s)",
288 __FILE__
, __PRETTY_FUNCTION__
, oif
->name
,
289 pim_ifp
->mroute_vif_index
, source_str
,
295 --channel_oil
->oil_size
;
297 if (PIM_DEBUG_MROUTE
) {
298 char group_str
[INET_ADDRSTRLEN
];
299 char source_str
[INET_ADDRSTRLEN
];
300 pim_inet4_dump("<group?>", channel_oil
->oil
.mfcc_mcastgrp
,
301 group_str
, sizeof(group_str
));
302 pim_inet4_dump("<source?>", channel_oil
->oil
.mfcc_origin
,
303 source_str
, sizeof(source_str
));
305 "%s %s: (S,G)=(%s,%s): proto_mask=%u IIF:%d OIF=%s vif_index=%d",
306 __FILE__
, __PRETTY_FUNCTION__
, source_str
, group_str
,
307 proto_mask
, channel_oil
->oil
.mfcc_parent
, oif
->name
,
308 pim_ifp
->mroute_vif_index
);
315 int pim_channel_add_oif(struct channel_oil
*channel_oil
, struct interface
*oif
,
318 struct pim_interface
*pim_ifp
;
322 * If we've gotten here we've gone bad, but let's
326 zlog_warn("Attempt to Add OIF for non-existent channel oil");
332 #ifdef PIM_ENFORCE_LOOPFREE_MFC
334 Prevent creating MFC entry with OIF=IIF.
336 This is a protection against implementation mistakes.
338 PIM protocol implicitely ensures loopfree multicast topology.
340 IGMP must be protected against adding looped MFC entries created
341 by both source and receiver attached to the same interface. See
344 if (pim_ifp
->mroute_vif_index
== channel_oil
->oil
.mfcc_parent
) {
345 channel_oil
->oil_inherited_rescan
= 1;
346 if (PIM_DEBUG_MROUTE
) {
347 char group_str
[INET_ADDRSTRLEN
];
348 char source_str
[INET_ADDRSTRLEN
];
349 pim_inet4_dump("<group?>",
350 channel_oil
->oil
.mfcc_mcastgrp
,
351 group_str
, sizeof(group_str
));
352 pim_inet4_dump("<source?>",
353 channel_oil
->oil
.mfcc_origin
, source_str
,
356 "%s %s: refusing protocol mask %u request for IIF=OIF=%s (vif_index=%d) for channel (S,G)=(%s,%s)",
357 __FILE__
, __PRETTY_FUNCTION__
, proto_mask
,
358 oif
->name
, pim_ifp
->mroute_vif_index
,
359 source_str
, group_str
);
365 /* Prevent single protocol from subscribing same interface to
366 channel (S,G) multiple times */
367 if (channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] & proto_mask
) {
368 if (PIM_DEBUG_MROUTE
) {
369 char group_str
[INET_ADDRSTRLEN
];
370 char source_str
[INET_ADDRSTRLEN
];
371 pim_inet4_dump("<group?>",
372 channel_oil
->oil
.mfcc_mcastgrp
,
373 group_str
, sizeof(group_str
));
374 pim_inet4_dump("<source?>",
375 channel_oil
->oil
.mfcc_origin
, source_str
,
378 "%s %s: existing protocol mask %u requested OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)",
379 __FILE__
, __PRETTY_FUNCTION__
, proto_mask
,
380 oif
->name
, pim_ifp
->mroute_vif_index
,
382 .mfcc_ttls
[pim_ifp
->mroute_vif_index
],
383 source_str
, group_str
);
388 /* Allow other protocol to request subscription of same interface to
389 * channel (S,G), we need to note this information
391 if (channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
]
392 & PIM_OIF_FLAG_PROTO_ANY
) {
394 channel_oil
->oif_creation
[pim_ifp
->mroute_vif_index
] =
395 pim_time_monotonic_sec();
396 channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] |= proto_mask
;
397 /* Check the OIF really exists before returning, and only log
399 if (channel_oil
->oil
.mfcc_ttls
[pim_ifp
->mroute_vif_index
] < 1) {
401 char group_str
[INET_ADDRSTRLEN
];
402 char source_str
[INET_ADDRSTRLEN
];
403 pim_inet4_dump("<group?>",
404 channel_oil
->oil
.mfcc_mcastgrp
,
405 group_str
, sizeof(group_str
));
406 pim_inet4_dump("<source?>",
407 channel_oil
->oil
.mfcc_origin
,
408 source_str
, sizeof(source_str
));
410 "%s %s: new protocol mask %u requested nonexistent OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)",
411 __FILE__
, __PRETTY_FUNCTION__
,
412 proto_mask
, oif
->name
,
413 pim_ifp
->mroute_vif_index
,
414 channel_oil
->oil
.mfcc_ttls
415 [pim_ifp
->mroute_vif_index
],
416 source_str
, group_str
);
423 old_ttl
= channel_oil
->oil
.mfcc_ttls
[pim_ifp
->mroute_vif_index
];
426 if (PIM_DEBUG_MROUTE
) {
427 char group_str
[INET_ADDRSTRLEN
];
428 char source_str
[INET_ADDRSTRLEN
];
429 pim_inet4_dump("<group?>",
430 channel_oil
->oil
.mfcc_mcastgrp
,
431 group_str
, sizeof(group_str
));
432 pim_inet4_dump("<source?>",
433 channel_oil
->oil
.mfcc_origin
, source_str
,
436 "%s %s: interface %s (vif_index=%d) is existing output for channel (S,G)=(%s,%s)",
437 __FILE__
, __PRETTY_FUNCTION__
, oif
->name
,
438 pim_ifp
->mroute_vif_index
, source_str
,
444 channel_oil
->oil
.mfcc_ttls
[pim_ifp
->mroute_vif_index
] =
447 if (pim_mroute_add(channel_oil
, __PRETTY_FUNCTION__
)) {
448 if (PIM_DEBUG_MROUTE
) {
449 char group_str
[INET_ADDRSTRLEN
];
450 char source_str
[INET_ADDRSTRLEN
];
451 pim_inet4_dump("<group?>",
452 channel_oil
->oil
.mfcc_mcastgrp
,
453 group_str
, sizeof(group_str
));
454 pim_inet4_dump("<source?>",
455 channel_oil
->oil
.mfcc_origin
, source_str
,
458 "%s %s: could not add output interface %s (vif_index=%d) for channel (S,G)=(%s,%s)",
459 __FILE__
, __PRETTY_FUNCTION__
, oif
->name
,
460 pim_ifp
->mroute_vif_index
, source_str
,
464 channel_oil
->oil
.mfcc_ttls
[pim_ifp
->mroute_vif_index
] = old_ttl
;
468 channel_oil
->oif_creation
[pim_ifp
->mroute_vif_index
] =
469 pim_time_monotonic_sec();
470 ++channel_oil
->oil_size
;
471 channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] |= proto_mask
;
473 if (PIM_DEBUG_MROUTE
) {
474 char group_str
[INET_ADDRSTRLEN
];
475 char source_str
[INET_ADDRSTRLEN
];
476 pim_inet4_dump("<group?>", channel_oil
->oil
.mfcc_mcastgrp
,
477 group_str
, sizeof(group_str
));
478 pim_inet4_dump("<source?>", channel_oil
->oil
.mfcc_origin
,
479 source_str
, sizeof(source_str
));
481 "%s %s: (S,G)=(%s,%s): proto_mask=%u OIF=%s vif_index=%d: DONE",
482 __FILE__
, __PRETTY_FUNCTION__
, source_str
, group_str
,
483 proto_mask
, oif
->name
, pim_ifp
->mroute_vif_index
);
489 int pim_channel_oil_empty(struct channel_oil
*c_oil
)
491 static uint32_t zero
[MAXVIFS
];
492 static int inited
= 0;
497 * Not sure that this is necessary, but I would rather ensure
501 memset(&zero
, 0, sizeof(uint32_t) * MAXVIFS
);
505 return !memcmp(c_oil
->oif_flags
, zero
, MAXVIFS
* sizeof(uint32_t));