]>
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 snprintf(buf
, size
, "%s IIF: %d, OIFS: ", pim_str_sg_dump(&sg
),
47 c_oil
->oil
.mfcc_parent
);
49 out
= buf
+ strlen(buf
);
50 for (i
= 0; i
< MAXVIFS
; i
++) {
51 if (c_oil
->oil
.mfcc_ttls
[i
] != 0) {
52 snprintf(out
, buf
+ size
- out
, "%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 bool 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 pim
->channel_oil_list
->del
= (void (*)(void *))pim_channel_oil_free
;
112 pim
->channel_oil_list
->cmp
=
113 (int (*)(void *, void *))pim_channel_oil_compare
;
116 void pim_oil_terminate(struct pim_instance
*pim
)
118 if (pim
->channel_oil_list
)
119 list_delete(&pim
->channel_oil_list
);
121 if (pim
->channel_oil_hash
)
122 hash_free(pim
->channel_oil_hash
);
123 pim
->channel_oil_hash
= NULL
;
126 void pim_channel_oil_free(struct channel_oil
*c_oil
)
128 XFREE(MTYPE_PIM_CHANNEL_OIL
, c_oil
);
131 struct channel_oil
*pim_find_channel_oil(struct pim_instance
*pim
,
132 struct prefix_sg
*sg
)
134 struct channel_oil
*c_oil
= NULL
;
135 struct channel_oil lookup
;
137 lookup
.oil
.mfcc_mcastgrp
= sg
->grp
;
138 lookup
.oil
.mfcc_origin
= sg
->src
;
140 c_oil
= hash_lookup(pim
->channel_oil_hash
, &lookup
);
145 struct channel_oil
*pim_channel_oil_add(struct pim_instance
*pim
,
146 struct prefix_sg
*sg
,
149 struct channel_oil
*c_oil
;
150 struct interface
*ifp
;
152 c_oil
= pim_find_channel_oil(pim
, sg
);
154 if (c_oil
->oil
.mfcc_parent
!= input_vif_index
) {
155 c_oil
->oil_inherited_rescan
= 1;
156 if (PIM_DEBUG_MROUTE
)
158 "%s: Existing channel oil %s points to %d, modifying to point at %d",
161 c_oil
->oil
.mfcc_parent
,
164 c_oil
->oil
.mfcc_parent
= input_vif_index
;
165 ++c_oil
->oil_ref_count
;
166 c_oil
->up
= pim_upstream_find(
167 pim
, sg
); // channel might be present prior to upstream
171 if (input_vif_index
!= MAXVIFS
) {
172 ifp
= pim_if_find_by_vif_index(pim
, 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
),
182 c_oil
= XCALLOC(MTYPE_PIM_CHANNEL_OIL
, sizeof(*c_oil
));
184 c_oil
->oil
.mfcc_mcastgrp
= sg
->grp
;
185 c_oil
->oil
.mfcc_origin
= sg
->src
;
186 c_oil
= hash_get(pim
->channel_oil_hash
, c_oil
, hash_alloc_intern
);
188 c_oil
->oil
.mfcc_parent
= input_vif_index
;
189 c_oil
->oil_ref_count
= 1;
190 c_oil
->installed
= 0;
191 c_oil
->up
= pim_upstream_find(pim
, sg
);
194 listnode_add_sort(pim
->channel_oil_list
, c_oil
);
199 void pim_channel_oil_del(struct channel_oil
*c_oil
)
201 --c_oil
->oil_ref_count
;
203 if (c_oil
->oil_ref_count
< 1) {
205 * notice that listnode_delete() can't be moved
206 * into pim_channel_oil_free() because the later is
207 * called by list_delete_all_node()
210 listnode_delete(c_oil
->pim
->channel_oil_list
, c_oil
);
211 hash_release(c_oil
->pim
->channel_oil_hash
, c_oil
);
213 pim_channel_oil_free(c_oil
);
217 int pim_channel_del_oif(struct channel_oil
*channel_oil
, struct interface
*oif
,
220 struct pim_interface
*pim_ifp
;
222 zassert(channel_oil
);
228 * Don't do anything if we've been asked to remove a source
229 * that is not actually on it.
231 if (!(channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] & proto_mask
)) {
232 if (PIM_DEBUG_MROUTE
) {
233 char group_str
[INET_ADDRSTRLEN
];
234 char source_str
[INET_ADDRSTRLEN
];
235 pim_inet4_dump("<group?>",
236 channel_oil
->oil
.mfcc_mcastgrp
,
237 group_str
, sizeof(group_str
));
238 pim_inet4_dump("<source?>",
239 channel_oil
->oil
.mfcc_origin
, source_str
,
242 "%s %s: no existing protocol mask %u(%u) for requested OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)",
243 __FILE__
, __PRETTY_FUNCTION__
, proto_mask
,
245 ->oif_flags
[pim_ifp
->mroute_vif_index
],
246 oif
->name
, pim_ifp
->mroute_vif_index
,
248 .mfcc_ttls
[pim_ifp
->mroute_vif_index
],
249 source_str
, group_str
);
254 channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] &= ~proto_mask
;
256 if (channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
]) {
257 if (PIM_DEBUG_MROUTE
) {
258 char group_str
[INET_ADDRSTRLEN
];
259 char source_str
[INET_ADDRSTRLEN
];
260 pim_inet4_dump("<group?>",
261 channel_oil
->oil
.mfcc_mcastgrp
,
262 group_str
, sizeof(group_str
));
263 pim_inet4_dump("<source?>",
264 channel_oil
->oil
.mfcc_origin
, source_str
,
267 "%s %s: other protocol masks remain for requested OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)",
268 __FILE__
, __PRETTY_FUNCTION__
, oif
->name
,
269 pim_ifp
->mroute_vif_index
,
271 .mfcc_ttls
[pim_ifp
->mroute_vif_index
],
272 source_str
, group_str
);
277 channel_oil
->oil
.mfcc_ttls
[pim_ifp
->mroute_vif_index
] = 0;
279 if (pim_mroute_add(channel_oil
, __PRETTY_FUNCTION__
)) {
280 if (PIM_DEBUG_MROUTE
) {
281 char group_str
[INET_ADDRSTRLEN
];
282 char source_str
[INET_ADDRSTRLEN
];
283 pim_inet4_dump("<group?>",
284 channel_oil
->oil
.mfcc_mcastgrp
,
285 group_str
, sizeof(group_str
));
286 pim_inet4_dump("<source?>",
287 channel_oil
->oil
.mfcc_origin
, source_str
,
290 "%s %s: could not remove output interface %s (vif_index=%d) for channel (S,G)=(%s,%s)",
291 __FILE__
, __PRETTY_FUNCTION__
, oif
->name
,
292 pim_ifp
->mroute_vif_index
, source_str
,
298 --channel_oil
->oil_size
;
300 if (PIM_DEBUG_MROUTE
) {
301 char group_str
[INET_ADDRSTRLEN
];
302 char source_str
[INET_ADDRSTRLEN
];
303 pim_inet4_dump("<group?>", channel_oil
->oil
.mfcc_mcastgrp
,
304 group_str
, sizeof(group_str
));
305 pim_inet4_dump("<source?>", channel_oil
->oil
.mfcc_origin
,
306 source_str
, sizeof(source_str
));
308 "%s %s: (S,G)=(%s,%s): proto_mask=%u IIF:%d OIF=%s vif_index=%d",
309 __FILE__
, __PRETTY_FUNCTION__
, source_str
, group_str
,
310 proto_mask
, channel_oil
->oil
.mfcc_parent
, oif
->name
,
311 pim_ifp
->mroute_vif_index
);
318 int pim_channel_add_oif(struct channel_oil
*channel_oil
, struct interface
*oif
,
321 struct pim_interface
*pim_ifp
;
325 * If we've gotten here we've gone bad, but let's
329 zlog_warn("Attempt to Add OIF for non-existent channel oil");
335 #ifdef PIM_ENFORCE_LOOPFREE_MFC
337 Prevent creating MFC entry with OIF=IIF.
339 This is a protection against implementation mistakes.
341 PIM protocol implicitely ensures loopfree multicast topology.
343 IGMP must be protected against adding looped MFC entries created
344 by both source and receiver attached to the same interface. See
347 if (pim_ifp
->mroute_vif_index
== channel_oil
->oil
.mfcc_parent
) {
348 channel_oil
->oil_inherited_rescan
= 1;
349 if (PIM_DEBUG_MROUTE
) {
350 char group_str
[INET_ADDRSTRLEN
];
351 char source_str
[INET_ADDRSTRLEN
];
352 pim_inet4_dump("<group?>",
353 channel_oil
->oil
.mfcc_mcastgrp
,
354 group_str
, sizeof(group_str
));
355 pim_inet4_dump("<source?>",
356 channel_oil
->oil
.mfcc_origin
, source_str
,
359 "%s %s: refusing protocol mask %u request for IIF=OIF=%s (vif_index=%d) for channel (S,G)=(%s,%s)",
360 __FILE__
, __PRETTY_FUNCTION__
, proto_mask
,
361 oif
->name
, pim_ifp
->mroute_vif_index
,
362 source_str
, group_str
);
368 /* Prevent single protocol from subscribing same interface to
369 channel (S,G) multiple times */
370 if (channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] & proto_mask
) {
371 if (PIM_DEBUG_MROUTE
) {
372 char group_str
[INET_ADDRSTRLEN
];
373 char source_str
[INET_ADDRSTRLEN
];
374 pim_inet4_dump("<group?>",
375 channel_oil
->oil
.mfcc_mcastgrp
,
376 group_str
, sizeof(group_str
));
377 pim_inet4_dump("<source?>",
378 channel_oil
->oil
.mfcc_origin
, source_str
,
381 "%s %s: existing protocol mask %u requested OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)",
382 __FILE__
, __PRETTY_FUNCTION__
, proto_mask
,
383 oif
->name
, pim_ifp
->mroute_vif_index
,
385 .mfcc_ttls
[pim_ifp
->mroute_vif_index
],
386 source_str
, group_str
);
391 /* Allow other protocol to request subscription of same interface to
392 * channel (S,G), we need to note this information
394 if (channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
]
395 & PIM_OIF_FLAG_PROTO_ANY
) {
397 /* Updating time here is not required as this time has to
398 * indicate when the interface is added
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 /* channel_oil->oil.mfcc_parent != MAXVIFS indicate this entry is not
453 * valid to get installed in kernel.
455 if (channel_oil
->oil
.mfcc_parent
!= MAXVIFS
) {
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
]
479 channel_oil
->oif_creation
[pim_ifp
->mroute_vif_index
] =
480 pim_time_monotonic_sec();
481 ++channel_oil
->oil_size
;
482 channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] |= proto_mask
;
484 if (PIM_DEBUG_MROUTE
) {
485 char group_str
[INET_ADDRSTRLEN
];
486 char source_str
[INET_ADDRSTRLEN
];
487 pim_inet4_dump("<group?>", channel_oil
->oil
.mfcc_mcastgrp
,
488 group_str
, sizeof(group_str
));
489 pim_inet4_dump("<source?>", channel_oil
->oil
.mfcc_origin
,
490 source_str
, sizeof(source_str
));
492 "%s %s: (S,G)=(%s,%s): proto_mask=%u OIF=%s vif_index=%d: DONE",
493 __FILE__
, __PRETTY_FUNCTION__
, source_str
, group_str
,
494 proto_mask
, oif
->name
, pim_ifp
->mroute_vif_index
);
500 int pim_channel_oil_empty(struct channel_oil
*c_oil
)
502 static uint32_t zero
[MAXVIFS
];
503 static int inited
= 0;
508 * Not sure that this is necessary, but I would rather ensure
512 memset(&zero
, 0, sizeof(uint32_t) * MAXVIFS
);
516 return !memcmp(c_oil
->oif_flags
, zero
, MAXVIFS
* sizeof(uint32_t));