]> git.proxmox.com Git - mirror_frr.git/blame - pimd/pim_oil.c
pimd: Convert vif and ifindex lookups to use 'struct pim_instance *'
[mirror_frr.git] / pimd / pim_oil.c
CommitLineData
12e41d03 1/*
896014f4
DL
2 * PIM for Quagga
3 * Copyright (C) 2008 Everton da Silva Marques
4 *
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.
9 *
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.
14 *
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
18 */
12e41d03
DL
19
20#include <zebra.h>
21
22#include "log.h"
23#include "memory.h"
24#include "linklist.h"
744d91b3 25#include "if.h"
040d86ad
DS
26#include "hash.h"
27#include "jhash.h"
12e41d03
DL
28
29#include "pimd.h"
30#include "pim_oil.h"
31#include "pim_str.h"
32#include "pim_iface.h"
37653d4f 33#include "pim_time.h"
12e41d03 34
040d86ad
DS
35struct list *pim_channel_oil_list = NULL;
36struct hash *pim_channel_oil_hash = NULL;
37
d62a17ae 38char *pim_channel_oil_dump(struct channel_oil *c_oil, char *buf, size_t size)
781a1745 39{
d62a17ae 40 struct prefix_sg sg;
41 int i;
42
43 memset(buf, 0, 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);
48
49 for (i = 0; i < MAXVIFS; i++) {
50 if (c_oil->oil.mfcc_ttls[i] != 0) {
51 char buf1[10];
52 sprintf(buf1, "%d ", i);
53 strcat(buf, buf1);
54 }
55 }
56
57 return buf;
781a1745
DS
58}
59
d62a17ae 60static int pim_channel_oil_compare(struct channel_oil *c1,
61 struct channel_oil *c2)
040d86ad 62{
d62a17ae 63 if (ntohl(c1->oil.mfcc_mcastgrp.s_addr)
64 < ntohl(c2->oil.mfcc_mcastgrp.s_addr))
65 return -1;
040d86ad 66
d62a17ae 67 if (ntohl(c1->oil.mfcc_mcastgrp.s_addr)
68 > ntohl(c2->oil.mfcc_mcastgrp.s_addr))
69 return 1;
040d86ad 70
d62a17ae 71 if (ntohl(c1->oil.mfcc_origin.s_addr)
72 < ntohl(c2->oil.mfcc_origin.s_addr))
73 return -1;
040d86ad 74
d62a17ae 75 if (ntohl(c1->oil.mfcc_origin.s_addr)
76 > ntohl(c2->oil.mfcc_origin.s_addr))
77 return 1;
040d86ad 78
d62a17ae 79 return 0;
040d86ad
DS
80}
81
d62a17ae 82static int pim_oil_equal(const void *arg1, const void *arg2)
040d86ad 83{
d62a17ae 84 const struct channel_oil *c1 = (const struct channel_oil *)arg1;
85 const struct channel_oil *c2 = (const struct channel_oil *)arg2;
040d86ad 86
d62a17ae 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))
89 return 1;
040d86ad 90
d62a17ae 91 return 0;
040d86ad
DS
92}
93
d62a17ae 94static unsigned int pim_oil_hash_key(void *arg)
040d86ad 95{
d62a17ae 96 struct channel_oil *oil = (struct channel_oil *)arg;
040d86ad 97
d62a17ae 98 return jhash_2words(oil->oil.mfcc_mcastgrp.s_addr,
99 oil->oil.mfcc_origin.s_addr, 0);
040d86ad
DS
100}
101
d62a17ae 102void pim_oil_init(void)
040d86ad 103{
d62a17ae 104 pim_channel_oil_hash =
105 hash_create_size(8192, pim_oil_hash_key, pim_oil_equal, NULL);
106
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__);
111 return;
112 }
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;
040d86ad
DS
116}
117
d62a17ae 118void pim_oil_terminate(void)
040d86ad 119{
d62a17ae 120 if (pim_channel_oil_list)
121 list_free(pim_channel_oil_list);
122 pim_channel_oil_list = NULL;
040d86ad 123
d62a17ae 124 if (pim_channel_oil_hash)
125 hash_free(pim_channel_oil_hash);
126 pim_channel_oil_hash = NULL;
040d86ad
DS
127}
128
12e41d03
DL
129void pim_channel_oil_free(struct channel_oil *c_oil)
130{
d62a17ae 131 XFREE(MTYPE_PIM_CHANNEL_OIL, c_oil);
12e41d03
DL
132}
133
d62a17ae 134static struct channel_oil *pim_find_channel_oil(struct prefix_sg *sg)
12e41d03 135{
d62a17ae 136 struct channel_oil *c_oil = NULL;
137 struct channel_oil lookup;
d270b216 138
d62a17ae 139 lookup.oil.mfcc_mcastgrp = sg->grp;
140 lookup.oil.mfcc_origin = sg->src;
d270b216 141
d62a17ae 142 c_oil = hash_lookup(pim_channel_oil_hash, &lookup);
d270b216 143
d62a17ae 144 return c_oil;
12e41d03
DL
145}
146
d270b216
DS
147struct channel_oil *pim_channel_oil_add(struct prefix_sg *sg,
148 int input_vif_index)
12e41d03 149{
d62a17ae 150 struct channel_oil *c_oil;
151 struct interface *ifp;
152
153 c_oil = pim_find_channel_oil(sg);
154 if (c_oil) {
155 if (c_oil->oil.mfcc_parent != input_vif_index) {
156 c_oil->oil_inherited_rescan = 1;
157 if (PIM_DEBUG_MROUTE)
158 zlog_debug(
159 "%s: Existing channel oil %s points to %d, modifying to point at %d",
160 __PRETTY_FUNCTION__,
161 pim_str_sg_dump(sg),
162 c_oil->oil.mfcc_parent,
163 input_vif_index);
164 }
165 c_oil->oil.mfcc_parent = input_vif_index;
166 ++c_oil->oil_ref_count;
167 c_oil->up = pim_upstream_find(
9b29ea95 168 pimg, sg); // channel might be present prior to upstream
d62a17ae 169 return c_oil;
170 }
171
7cfc7bcf 172 ifp = pim_if_find_by_vif_index(pimg, input_vif_index);
d62a17ae 173 if (!ifp) {
174 /* warning only */
175 zlog_warn(
176 "%s: (S,G)=%s could not find input interface for input_vif_index=%d",
177 __PRETTY_FUNCTION__, pim_str_sg_dump(sg),
178 input_vif_index);
179 }
180
181 c_oil = XCALLOC(MTYPE_PIM_CHANNEL_OIL, sizeof(*c_oil));
182 if (!c_oil) {
183 zlog_err("PIM XCALLOC(%zu) failure", sizeof(*c_oil));
184 return NULL;
185 }
186
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);
190
191 c_oil->oil.mfcc_parent = input_vif_index;
192 c_oil->oil_ref_count = 1;
193 c_oil->installed = 0;
9b29ea95 194 c_oil->up = pim_upstream_find(pimg, sg);
d62a17ae 195
196 listnode_add_sort(pim_channel_oil_list, c_oil);
197
198 return c_oil;
12e41d03
DL
199}
200
12e41d03
DL
201void pim_channel_oil_del(struct channel_oil *c_oil)
202{
d62a17ae 203 --c_oil->oil_ref_count;
204
205 if (c_oil->oil_ref_count < 1) {
206 /*
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()
210 */
211 c_oil->up = NULL;
212 listnode_delete(pim_channel_oil_list, c_oil);
213 hash_release(pim_channel_oil_hash, c_oil);
214
215 pim_channel_oil_free(c_oil);
216 }
12e41d03 217}
1865a44a 218
d62a17ae 219int pim_channel_del_oif(struct channel_oil *channel_oil, struct interface *oif,
220 uint32_t proto_mask)
887fa014 221{
d62a17ae 222 struct pim_interface *pim_ifp;
223
224 zassert(channel_oil);
225 zassert(oif);
226
227 pim_ifp = oif->info;
228
229 /*
230 * Don't do anything if we've been asked to remove a source
231 * that is not actually on it.
232 */
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,
242 sizeof(source_str));
243 zlog_debug(
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,
246 channel_oil
247 ->oif_flags[pim_ifp->mroute_vif_index],
248 oif->name, pim_ifp->mroute_vif_index,
249 channel_oil->oil
250 .mfcc_ttls[pim_ifp->mroute_vif_index],
251 source_str, group_str);
252 }
253 return 0;
254 }
255
256 channel_oil->oif_flags[pim_ifp->mroute_vif_index] &= ~proto_mask;
257
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,
267 sizeof(source_str));
268 zlog_debug(
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,
272 channel_oil->oil
273 .mfcc_ttls[pim_ifp->mroute_vif_index],
274 source_str, group_str);
275 }
276 return 0;
277 }
278
279 channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index] = 0;
280
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,
290 sizeof(source_str));
291 zlog_debug(
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,
295 group_str);
296 }
297 return -1;
887fa014 298 }
d62a17ae 299
300 --channel_oil->oil_size;
301
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));
309 zlog_debug(
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);
887fa014 314 }
d62a17ae 315
316 return 0;
887fa014
DS
317}
318
319
d62a17ae 320int pim_channel_add_oif(struct channel_oil *channel_oil, struct interface *oif,
321 uint32_t proto_mask)
1865a44a 322{
d62a17ae 323 struct pim_interface *pim_ifp;
324 int old_ttl;
325
326 /*
327 * If we've gotten here we've gone bad, but let's
328 * not take down pim
329 */
330 if (!channel_oil) {
331 zlog_warn("Attempt to Add OIF for non-existent channel oil");
332 return -1;
333 }
1865a44a 334
d62a17ae 335 pim_ifp = oif->info;
1865a44a 336
1865a44a 337#ifdef PIM_ENFORCE_LOOPFREE_MFC
d62a17ae 338 /*
339 Prevent creating MFC entry with OIF=IIF.
340
341 This is a protection against implementation mistakes.
342
343 PIM protocol implicitely ensures loopfree multicast topology.
344
345 IGMP must be protected against adding looped MFC entries created
346 by both source and receiver attached to the same interface. See
347 TODO T22.
348 */
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,
359 sizeof(source_str));
360 zlog_debug(
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);
365 }
366 return -2;
367 }
1865a44a
DS
368#endif
369
d62a17ae 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,
381 sizeof(source_str));
382 zlog_debug(
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,
386 channel_oil->oil
387 .mfcc_ttls[pim_ifp->mroute_vif_index],
388 source_str, group_str);
389 }
390 return -3;
391 }
392
393 /* Allow other protocol to request subscription of same interface to
394 * channel (S,G), we need to note this information
395 */
396 if (channel_oil->oif_flags[pim_ifp->mroute_vif_index]
397 & PIM_OIF_FLAG_PROTO_ANY) {
398
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
403 warning otherwise */
404 if (channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index] < 1) {
405 {
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));
414 zlog_warn(
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);
422 }
423 }
424
425 return 0;
426 }
427
428 old_ttl = channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index];
429
430 if (old_ttl > 0) {
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,
439 sizeof(source_str));
440 zlog_debug(
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,
444 group_str);
445 }
446 return -4;
447 }
448
449 channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index] =
450 PIM_MROUTE_MIN_TTL;
451
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,
461 sizeof(source_str));
462 zlog_debug(
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,
466 group_str);
467 }
468
469 channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index] = old_ttl;
470 return -5;
471 }
472
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;
477
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));
485 zlog_debug(
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);
489 }
490
491 return 0;
1865a44a 492}
ce0ddb4e 493
d62a17ae 494int pim_channel_oil_empty(struct channel_oil *c_oil)
ce0ddb4e 495{
d62a17ae 496 static uint32_t zero[MAXVIFS];
497 static int inited = 0;
498
499 if (!c_oil)
500 return 1;
501 /*
502 * Not sure that this is necessary, but I would rather ensure
503 * that this works.
504 */
505 if (!inited) {
506 memset(&zero, 0, sizeof(uint32_t) * MAXVIFS);
507 inited = 1;
508 }
509
510 return !memcmp(c_oil->oif_flags, zero, MAXVIFS * sizeof(uint32_t));
ce0ddb4e 511}