]>
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(void)
104 pim_channel_oil_hash
=
105 hash_create_size(8192, pim_oil_hash_key
, pim_oil_equal
, NULL
);
107 pim_channel_oil_list
= list_new();
108 if (!pim_channel_oil_list
) {
109 zlog_err("%s %s: failure: channel_oil_list=list_new()",
110 __FILE__
, __PRETTY_FUNCTION__
);
113 pim_channel_oil_list
->del
= (void (*)(void *))pim_channel_oil_free
;
114 pim_channel_oil_list
->cmp
=
115 (int (*)(void *, void *))pim_channel_oil_compare
;
118 void pim_oil_terminate(void)
120 if (pim_channel_oil_list
)
121 list_free(pim_channel_oil_list
);
122 pim_channel_oil_list
= NULL
;
124 if (pim_channel_oil_hash
)
125 hash_free(pim_channel_oil_hash
);
126 pim_channel_oil_hash
= NULL
;
129 void pim_channel_oil_free(struct channel_oil
*c_oil
)
131 XFREE(MTYPE_PIM_CHANNEL_OIL
, c_oil
);
134 static struct channel_oil
*pim_find_channel_oil(struct prefix_sg
*sg
)
136 struct channel_oil
*c_oil
= NULL
;
137 struct channel_oil lookup
;
139 lookup
.oil
.mfcc_mcastgrp
= sg
->grp
;
140 lookup
.oil
.mfcc_origin
= sg
->src
;
142 c_oil
= hash_lookup(pim_channel_oil_hash
, &lookup
);
147 struct channel_oil
*pim_channel_oil_add(struct prefix_sg
*sg
,
150 struct channel_oil
*c_oil
;
151 struct interface
*ifp
;
153 c_oil
= pim_find_channel_oil(sg
);
155 if (c_oil
->oil
.mfcc_parent
!= input_vif_index
) {
156 c_oil
->oil_inherited_rescan
= 1;
157 if (PIM_DEBUG_MROUTE
)
159 "%s: Existing channel oil %s points to %d, modifying to point at %d",
162 c_oil
->oil
.mfcc_parent
,
165 c_oil
->oil
.mfcc_parent
= input_vif_index
;
166 ++c_oil
->oil_ref_count
;
167 c_oil
->up
= pim_upstream_find(
168 sg
); // channel might be present prior to upstream
172 ifp
= pim_if_find_by_vif_index(input_vif_index
);
176 "%s: (S,G)=%s could not find input interface for input_vif_index=%d",
177 __PRETTY_FUNCTION__
, pim_str_sg_dump(sg
),
181 c_oil
= XCALLOC(MTYPE_PIM_CHANNEL_OIL
, sizeof(*c_oil
));
183 zlog_err("PIM XCALLOC(%zu) failure", sizeof(*c_oil
));
187 c_oil
->oil
.mfcc_mcastgrp
= sg
->grp
;
188 c_oil
->oil
.mfcc_origin
= sg
->src
;
189 c_oil
= hash_get(pim_channel_oil_hash
, c_oil
, hash_alloc_intern
);
191 c_oil
->oil
.mfcc_parent
= input_vif_index
;
192 c_oil
->oil_ref_count
= 1;
193 c_oil
->installed
= 0;
194 c_oil
->up
= pim_upstream_find(sg
);
196 listnode_add_sort(pim_channel_oil_list
, c_oil
);
201 void pim_channel_oil_del(struct channel_oil
*c_oil
)
203 --c_oil
->oil_ref_count
;
205 if (c_oil
->oil_ref_count
< 1) {
207 * notice that listnode_delete() can't be moved
208 * into pim_channel_oil_free() because the later is
209 * called by list_delete_all_node()
212 listnode_delete(pim_channel_oil_list
, c_oil
);
213 hash_release(pim_channel_oil_hash
, c_oil
);
215 pim_channel_oil_free(c_oil
);
219 int pim_channel_del_oif(struct channel_oil
*channel_oil
, struct interface
*oif
,
222 struct pim_interface
*pim_ifp
;
224 zassert(channel_oil
);
230 * Don't do anything if we've been asked to remove a source
231 * that is not actually on it.
233 if (!(channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] & proto_mask
)) {
234 if (PIM_DEBUG_MROUTE
) {
235 char group_str
[INET_ADDRSTRLEN
];
236 char source_str
[INET_ADDRSTRLEN
];
237 pim_inet4_dump("<group?>",
238 channel_oil
->oil
.mfcc_mcastgrp
,
239 group_str
, sizeof(group_str
));
240 pim_inet4_dump("<source?>",
241 channel_oil
->oil
.mfcc_origin
, source_str
,
244 "%s %s: no existing protocol mask %u(%u) for requested OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)",
245 __FILE__
, __PRETTY_FUNCTION__
, proto_mask
,
247 ->oif_flags
[pim_ifp
->mroute_vif_index
],
248 oif
->name
, pim_ifp
->mroute_vif_index
,
250 .mfcc_ttls
[pim_ifp
->mroute_vif_index
],
251 source_str
, group_str
);
256 channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] &= ~proto_mask
;
258 if (channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
]) {
259 if (PIM_DEBUG_MROUTE
) {
260 char group_str
[INET_ADDRSTRLEN
];
261 char source_str
[INET_ADDRSTRLEN
];
262 pim_inet4_dump("<group?>",
263 channel_oil
->oil
.mfcc_mcastgrp
,
264 group_str
, sizeof(group_str
));
265 pim_inet4_dump("<source?>",
266 channel_oil
->oil
.mfcc_origin
, source_str
,
269 "%s %s: other protocol masks remain for requested OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)",
270 __FILE__
, __PRETTY_FUNCTION__
, oif
->name
,
271 pim_ifp
->mroute_vif_index
,
273 .mfcc_ttls
[pim_ifp
->mroute_vif_index
],
274 source_str
, group_str
);
279 channel_oil
->oil
.mfcc_ttls
[pim_ifp
->mroute_vif_index
] = 0;
281 if (pim_mroute_add(channel_oil
, __PRETTY_FUNCTION__
)) {
282 if (PIM_DEBUG_MROUTE
) {
283 char group_str
[INET_ADDRSTRLEN
];
284 char source_str
[INET_ADDRSTRLEN
];
285 pim_inet4_dump("<group?>",
286 channel_oil
->oil
.mfcc_mcastgrp
,
287 group_str
, sizeof(group_str
));
288 pim_inet4_dump("<source?>",
289 channel_oil
->oil
.mfcc_origin
, source_str
,
292 "%s %s: could not remove output interface %s (vif_index=%d) for channel (S,G)=(%s,%s)",
293 __FILE__
, __PRETTY_FUNCTION__
, oif
->name
,
294 pim_ifp
->mroute_vif_index
, source_str
,
300 --channel_oil
->oil_size
;
302 if (PIM_DEBUG_MROUTE
) {
303 char group_str
[INET_ADDRSTRLEN
];
304 char source_str
[INET_ADDRSTRLEN
];
305 pim_inet4_dump("<group?>", channel_oil
->oil
.mfcc_mcastgrp
,
306 group_str
, sizeof(group_str
));
307 pim_inet4_dump("<source?>", channel_oil
->oil
.mfcc_origin
,
308 source_str
, sizeof(source_str
));
310 "%s %s: (S,G)=(%s,%s): proto_mask=%u IIF:%d OIF=%s vif_index=%d",
311 __FILE__
, __PRETTY_FUNCTION__
, source_str
, group_str
,
312 proto_mask
, channel_oil
->oil
.mfcc_parent
, oif
->name
,
313 pim_ifp
->mroute_vif_index
);
320 int pim_channel_add_oif(struct channel_oil
*channel_oil
, struct interface
*oif
,
323 struct pim_interface
*pim_ifp
;
327 * If we've gotten here we've gone bad, but let's
331 zlog_warn("Attempt to Add OIF for non-existent channel oil");
337 #ifdef PIM_ENFORCE_LOOPFREE_MFC
339 Prevent creating MFC entry with OIF=IIF.
341 This is a protection against implementation mistakes.
343 PIM protocol implicitely ensures loopfree multicast topology.
345 IGMP must be protected against adding looped MFC entries created
346 by both source and receiver attached to the same interface. See
349 if (pim_ifp
->mroute_vif_index
== channel_oil
->oil
.mfcc_parent
) {
350 channel_oil
->oil_inherited_rescan
= 1;
351 if (PIM_DEBUG_MROUTE
) {
352 char group_str
[INET_ADDRSTRLEN
];
353 char source_str
[INET_ADDRSTRLEN
];
354 pim_inet4_dump("<group?>",
355 channel_oil
->oil
.mfcc_mcastgrp
,
356 group_str
, sizeof(group_str
));
357 pim_inet4_dump("<source?>",
358 channel_oil
->oil
.mfcc_origin
, source_str
,
361 "%s %s: refusing protocol mask %u request for IIF=OIF=%s (vif_index=%d) for channel (S,G)=(%s,%s)",
362 __FILE__
, __PRETTY_FUNCTION__
, proto_mask
,
363 oif
->name
, pim_ifp
->mroute_vif_index
,
364 source_str
, group_str
);
370 /* Prevent single protocol from subscribing same interface to
371 channel (S,G) multiple times */
372 if (channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] & proto_mask
) {
373 if (PIM_DEBUG_MROUTE
) {
374 char group_str
[INET_ADDRSTRLEN
];
375 char source_str
[INET_ADDRSTRLEN
];
376 pim_inet4_dump("<group?>",
377 channel_oil
->oil
.mfcc_mcastgrp
,
378 group_str
, sizeof(group_str
));
379 pim_inet4_dump("<source?>",
380 channel_oil
->oil
.mfcc_origin
, source_str
,
383 "%s %s: existing protocol mask %u requested OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)",
384 __FILE__
, __PRETTY_FUNCTION__
, proto_mask
,
385 oif
->name
, pim_ifp
->mroute_vif_index
,
387 .mfcc_ttls
[pim_ifp
->mroute_vif_index
],
388 source_str
, group_str
);
393 /* Allow other protocol to request subscription of same interface to
394 * channel (S,G), we need to note this information
396 if (channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
]
397 & PIM_OIF_FLAG_PROTO_ANY
) {
399 channel_oil
->oif_creation
[pim_ifp
->mroute_vif_index
] =
400 pim_time_monotonic_sec();
401 channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] |= proto_mask
;
402 /* Check the OIF really exists before returning, and only log
404 if (channel_oil
->oil
.mfcc_ttls
[pim_ifp
->mroute_vif_index
] < 1) {
406 char group_str
[INET_ADDRSTRLEN
];
407 char source_str
[INET_ADDRSTRLEN
];
408 pim_inet4_dump("<group?>",
409 channel_oil
->oil
.mfcc_mcastgrp
,
410 group_str
, sizeof(group_str
));
411 pim_inet4_dump("<source?>",
412 channel_oil
->oil
.mfcc_origin
,
413 source_str
, sizeof(source_str
));
415 "%s %s: new protocol mask %u requested nonexistent OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)",
416 __FILE__
, __PRETTY_FUNCTION__
,
417 proto_mask
, oif
->name
,
418 pim_ifp
->mroute_vif_index
,
419 channel_oil
->oil
.mfcc_ttls
420 [pim_ifp
->mroute_vif_index
],
421 source_str
, group_str
);
428 old_ttl
= channel_oil
->oil
.mfcc_ttls
[pim_ifp
->mroute_vif_index
];
431 if (PIM_DEBUG_MROUTE
) {
432 char group_str
[INET_ADDRSTRLEN
];
433 char source_str
[INET_ADDRSTRLEN
];
434 pim_inet4_dump("<group?>",
435 channel_oil
->oil
.mfcc_mcastgrp
,
436 group_str
, sizeof(group_str
));
437 pim_inet4_dump("<source?>",
438 channel_oil
->oil
.mfcc_origin
, source_str
,
441 "%s %s: interface %s (vif_index=%d) is existing output for channel (S,G)=(%s,%s)",
442 __FILE__
, __PRETTY_FUNCTION__
, oif
->name
,
443 pim_ifp
->mroute_vif_index
, source_str
,
449 channel_oil
->oil
.mfcc_ttls
[pim_ifp
->mroute_vif_index
] =
452 if (pim_mroute_add(channel_oil
, __PRETTY_FUNCTION__
)) {
453 if (PIM_DEBUG_MROUTE
) {
454 char group_str
[INET_ADDRSTRLEN
];
455 char source_str
[INET_ADDRSTRLEN
];
456 pim_inet4_dump("<group?>",
457 channel_oil
->oil
.mfcc_mcastgrp
,
458 group_str
, sizeof(group_str
));
459 pim_inet4_dump("<source?>",
460 channel_oil
->oil
.mfcc_origin
, source_str
,
463 "%s %s: could not add output interface %s (vif_index=%d) for channel (S,G)=(%s,%s)",
464 __FILE__
, __PRETTY_FUNCTION__
, oif
->name
,
465 pim_ifp
->mroute_vif_index
, source_str
,
469 channel_oil
->oil
.mfcc_ttls
[pim_ifp
->mroute_vif_index
] = old_ttl
;
473 channel_oil
->oif_creation
[pim_ifp
->mroute_vif_index
] =
474 pim_time_monotonic_sec();
475 ++channel_oil
->oil_size
;
476 channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] |= proto_mask
;
478 if (PIM_DEBUG_MROUTE
) {
479 char group_str
[INET_ADDRSTRLEN
];
480 char source_str
[INET_ADDRSTRLEN
];
481 pim_inet4_dump("<group?>", channel_oil
->oil
.mfcc_mcastgrp
,
482 group_str
, sizeof(group_str
));
483 pim_inet4_dump("<source?>", channel_oil
->oil
.mfcc_origin
,
484 source_str
, sizeof(source_str
));
486 "%s %s: (S,G)=(%s,%s): proto_mask=%u OIF=%s vif_index=%d: DONE",
487 __FILE__
, __PRETTY_FUNCTION__
, source_str
, group_str
,
488 proto_mask
, oif
->name
, pim_ifp
->mroute_vif_index
);
494 int pim_channel_oil_empty(struct channel_oil
*c_oil
)
496 static uint32_t zero
[MAXVIFS
];
497 static int inited
= 0;
502 * Not sure that this is necessary, but I would rather ensure
506 memset(&zero
, 0, sizeof(uint32_t) * MAXVIFS
);
510 return !memcmp(c_oil
->oif_flags
, zero
, MAXVIFS
* sizeof(uint32_t));