]> git.proxmox.com Git - mirror_frr.git/blame - pimd/pim_oil.c
Merge pull request #1369 from LabNConsulting/working/3.0/cherry-pick/minusS
[mirror_frr.git] / pimd / pim_oil.c
CommitLineData
12e41d03
DL
1/*
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.
ac4d0be5 14
12e41d03
DL
15 You should have received a copy of the GNU General Public License
16 along with this program; see the file COPYING; if not, write to the
17 Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
18 MA 02110-1301 USA
12e41d03
DL
19*/
20
21#include <zebra.h>
22
23#include "log.h"
24#include "memory.h"
25#include "linklist.h"
744d91b3 26#include "if.h"
040d86ad
DS
27#include "hash.h"
28#include "jhash.h"
12e41d03
DL
29
30#include "pimd.h"
31#include "pim_oil.h"
32#include "pim_str.h"
33#include "pim_iface.h"
37653d4f 34#include "pim_time.h"
12e41d03 35
040d86ad
DS
36struct list *pim_channel_oil_list = NULL;
37struct hash *pim_channel_oil_hash = NULL;
38
ac4d0be5 39char *pim_channel_oil_dump(struct channel_oil *c_oil, char *buf, size_t size)
781a1745 40{
ac4d0be5 41 struct prefix_sg sg;
42 int i;
43
44 memset(buf, 0, size);
45 sg.src = c_oil->oil.mfcc_origin;
46 sg.grp = c_oil->oil.mfcc_mcastgrp;
47 sprintf(buf, "%s IIF: %d, OIFS: ", pim_str_sg_dump(&sg),
48 c_oil->oil.mfcc_parent);
49
50 for (i = 0; i < MAXVIFS; i++) {
51 if (c_oil->oil.mfcc_ttls[i] != 0) {
52 char buf1[10];
53 sprintf(buf1, "%d ", i);
54 strcat(buf, buf1);
55 }
56 }
57
58 return buf;
781a1745
DS
59}
60
ac4d0be5 61static int pim_channel_oil_compare(struct channel_oil *c1,
62 struct channel_oil *c2)
040d86ad 63{
ac4d0be5 64 if (ntohl(c1->oil.mfcc_mcastgrp.s_addr)
65 < ntohl(c2->oil.mfcc_mcastgrp.s_addr))
66 return -1;
040d86ad 67
ac4d0be5 68 if (ntohl(c1->oil.mfcc_mcastgrp.s_addr)
69 > ntohl(c2->oil.mfcc_mcastgrp.s_addr))
70 return 1;
040d86ad 71
ac4d0be5 72 if (ntohl(c1->oil.mfcc_origin.s_addr)
73 < ntohl(c2->oil.mfcc_origin.s_addr))
74 return -1;
040d86ad 75
ac4d0be5 76 if (ntohl(c1->oil.mfcc_origin.s_addr)
77 > ntohl(c2->oil.mfcc_origin.s_addr))
78 return 1;
040d86ad 79
ac4d0be5 80 return 0;
040d86ad
DS
81}
82
ac4d0be5 83static int pim_oil_equal(const void *arg1, const void *arg2)
040d86ad 84{
ac4d0be5 85 const struct channel_oil *c1 = (const struct channel_oil *)arg1;
86 const struct channel_oil *c2 = (const struct channel_oil *)arg2;
040d86ad 87
ac4d0be5 88 if ((c1->oil.mfcc_mcastgrp.s_addr == c2->oil.mfcc_mcastgrp.s_addr)
89 && (c1->oil.mfcc_origin.s_addr == c2->oil.mfcc_origin.s_addr))
90 return 1;
040d86ad 91
ac4d0be5 92 return 0;
040d86ad
DS
93}
94
ac4d0be5 95static unsigned int pim_oil_hash_key(void *arg)
040d86ad 96{
ac4d0be5 97 struct channel_oil *oil = (struct channel_oil *)arg;
040d86ad 98
ac4d0be5 99 return jhash_2words(oil->oil.mfcc_mcastgrp.s_addr,
100 oil->oil.mfcc_origin.s_addr, 0);
040d86ad
DS
101}
102
ac4d0be5 103void pim_oil_init(void)
040d86ad 104{
ac4d0be5 105 pim_channel_oil_hash =
106 hash_create_size(8192, pim_oil_hash_key, pim_oil_equal);
107
108 pim_channel_oil_list = list_new();
109 if (!pim_channel_oil_list) {
110 zlog_err("%s %s: failure: channel_oil_list=list_new()",
111 __FILE__, __PRETTY_FUNCTION__);
112 return;
113 }
114 pim_channel_oil_list->del = (void (*)(void *))pim_channel_oil_free;
115 pim_channel_oil_list->cmp =
116 (int (*)(void *, void *))pim_channel_oil_compare;
040d86ad
DS
117}
118
ac4d0be5 119void pim_oil_terminate(void)
040d86ad 120{
ac4d0be5 121 if (pim_channel_oil_list)
122 list_free(pim_channel_oil_list);
123 pim_channel_oil_list = NULL;
040d86ad 124
ac4d0be5 125 if (pim_channel_oil_hash)
126 hash_free(pim_channel_oil_hash);
127 pim_channel_oil_hash = NULL;
040d86ad
DS
128}
129
12e41d03
DL
130void pim_channel_oil_free(struct channel_oil *c_oil)
131{
ac4d0be5 132 XFREE(MTYPE_PIM_CHANNEL_OIL, c_oil);
12e41d03
DL
133}
134
ac4d0be5 135static struct channel_oil *pim_find_channel_oil(struct prefix_sg *sg)
12e41d03 136{
ac4d0be5 137 struct channel_oil *c_oil = NULL;
138 struct channel_oil lookup;
d270b216 139
ac4d0be5 140 lookup.oil.mfcc_mcastgrp = sg->grp;
141 lookup.oil.mfcc_origin = sg->src;
d270b216 142
ac4d0be5 143 c_oil = hash_lookup(pim_channel_oil_hash, &lookup);
d270b216 144
ac4d0be5 145 return c_oil;
12e41d03
DL
146}
147
d270b216
DS
148struct channel_oil *pim_channel_oil_add(struct prefix_sg *sg,
149 int input_vif_index)
12e41d03 150{
ac4d0be5 151 struct channel_oil *c_oil;
152 struct interface *ifp;
153
154 c_oil = pim_find_channel_oil(sg);
155 if (c_oil) {
156 if (c_oil->oil.mfcc_parent != input_vif_index) {
157 c_oil->oil_inherited_rescan = 1;
158 if (PIM_DEBUG_MROUTE)
159 zlog_debug(
160 "%s: Existing channel oil %s points to %d, modifying to point at %d",
161 __PRETTY_FUNCTION__,
162 pim_str_sg_dump(sg),
163 c_oil->oil.mfcc_parent,
164 input_vif_index);
165 }
166 c_oil->oil.mfcc_parent = input_vif_index;
167 ++c_oil->oil_ref_count;
168 c_oil->up = pim_upstream_find(
169 sg); // channel might be present prior to upstream
170 return c_oil;
171 }
172
173 ifp = pim_if_find_by_vif_index(input_vif_index);
174 if (!ifp) {
175 /* warning only */
176 zlog_warn(
177 "%s: (S,G)=%s could not find input interface for input_vif_index=%d",
178 __PRETTY_FUNCTION__, pim_str_sg_dump(sg),
179 input_vif_index);
180 }
181
182 c_oil = XCALLOC(MTYPE_PIM_CHANNEL_OIL, sizeof(*c_oil));
183 if (!c_oil) {
184 zlog_err("PIM XCALLOC(%zu) failure", sizeof(*c_oil));
185 return NULL;
186 }
187
188 c_oil->oil.mfcc_mcastgrp = sg->grp;
189 c_oil->oil.mfcc_origin = sg->src;
190 c_oil = hash_get(pim_channel_oil_hash, c_oil, hash_alloc_intern);
191
192 c_oil->oil.mfcc_parent = input_vif_index;
193 c_oil->oil_ref_count = 1;
194 c_oil->installed = 0;
195 c_oil->up = pim_upstream_find(sg);
196
197 listnode_add_sort(pim_channel_oil_list, c_oil);
198
199 return c_oil;
12e41d03
DL
200}
201
12e41d03
DL
202void pim_channel_oil_del(struct channel_oil *c_oil)
203{
ac4d0be5 204 --c_oil->oil_ref_count;
205
206 if (c_oil->oil_ref_count < 1) {
207 /*
208 * notice that listnode_delete() can't be moved
209 * into pim_channel_oil_free() because the later is
210 * called by list_delete_all_node()
211 */
212 c_oil->up = NULL;
213 listnode_delete(pim_channel_oil_list, c_oil);
214 hash_release(pim_channel_oil_hash, c_oil);
215
216 pim_channel_oil_free(c_oil);
217 }
12e41d03 218}
1865a44a 219
ac4d0be5 220int pim_channel_del_oif(struct channel_oil *channel_oil, struct interface *oif,
221 uint32_t proto_mask)
887fa014 222{
ac4d0be5 223 struct pim_interface *pim_ifp;
224
225 zassert(channel_oil);
226 zassert(oif);
227
228 pim_ifp = oif->info;
229
230 /*
231 * Don't do anything if we've been asked to remove a source
232 * that is not actually on it.
233 */
234 if (!(channel_oil->oif_flags[pim_ifp->mroute_vif_index] & proto_mask)) {
235 if (PIM_DEBUG_MROUTE) {
236 char group_str[INET_ADDRSTRLEN];
237 char source_str[INET_ADDRSTRLEN];
238 pim_inet4_dump("<group?>",
239 channel_oil->oil.mfcc_mcastgrp,
240 group_str, sizeof(group_str));
241 pim_inet4_dump("<source?>",
242 channel_oil->oil.mfcc_origin, source_str,
243 sizeof(source_str));
244 zlog_debug(
245 "%s %s: no existing protocol mask %u(%u) for requested OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)",
246 __FILE__, __PRETTY_FUNCTION__, proto_mask,
247 channel_oil
248 ->oif_flags[pim_ifp->mroute_vif_index],
249 oif->name, pim_ifp->mroute_vif_index,
250 channel_oil->oil
251 .mfcc_ttls[pim_ifp->mroute_vif_index],
252 source_str, group_str);
253 }
254 return 0;
887fa014 255 }
ac4d0be5 256
257 channel_oil->oif_flags[pim_ifp->mroute_vif_index] &= ~proto_mask;
258
259 if (channel_oil->oif_flags[pim_ifp->mroute_vif_index]) {
260 if (PIM_DEBUG_MROUTE) {
261 char group_str[INET_ADDRSTRLEN];
262 char source_str[INET_ADDRSTRLEN];
263 pim_inet4_dump("<group?>",
264 channel_oil->oil.mfcc_mcastgrp,
265 group_str, sizeof(group_str));
266 pim_inet4_dump("<source?>",
267 channel_oil->oil.mfcc_origin, source_str,
268 sizeof(source_str));
269 zlog_debug(
270 "%s %s: other protocol masks remain for requested OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)",
271 __FILE__, __PRETTY_FUNCTION__, oif->name,
272 pim_ifp->mroute_vif_index,
273 channel_oil->oil
274 .mfcc_ttls[pim_ifp->mroute_vif_index],
275 source_str, group_str);
276 }
277 return 0;
278 }
279
280 channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index] = 0;
281
282 if (pim_mroute_add(channel_oil, __PRETTY_FUNCTION__)) {
283 if (PIM_DEBUG_MROUTE) {
284 char group_str[INET_ADDRSTRLEN];
285 char source_str[INET_ADDRSTRLEN];
286 pim_inet4_dump("<group?>",
287 channel_oil->oil.mfcc_mcastgrp,
288 group_str, sizeof(group_str));
289 pim_inet4_dump("<source?>",
290 channel_oil->oil.mfcc_origin, source_str,
291 sizeof(source_str));
292 zlog_debug(
293 "%s %s: could not remove output interface %s (vif_index=%d) for channel (S,G)=(%s,%s)",
294 __FILE__, __PRETTY_FUNCTION__, oif->name,
295 pim_ifp->mroute_vif_index, source_str,
296 group_str);
297 }
298 return -1;
887fa014 299 }
ac4d0be5 300
301 --channel_oil->oil_size;
302
303 if (PIM_DEBUG_MROUTE) {
304 char group_str[INET_ADDRSTRLEN];
305 char source_str[INET_ADDRSTRLEN];
306 pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp,
307 group_str, sizeof(group_str));
308 pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin,
309 source_str, sizeof(source_str));
310 zlog_debug(
311 "%s %s: (S,G)=(%s,%s): proto_mask=%u IIF:%d OIF=%s vif_index=%d",
312 __FILE__, __PRETTY_FUNCTION__, source_str, group_str,
313 proto_mask, channel_oil->oil.mfcc_parent, oif->name,
314 pim_ifp->mroute_vif_index);
315 }
316
317 return 0;
887fa014
DS
318}
319
320
ac4d0be5 321int pim_channel_add_oif(struct channel_oil *channel_oil, struct interface *oif,
322 uint32_t proto_mask)
1865a44a 323{
ac4d0be5 324 struct pim_interface *pim_ifp;
325 int old_ttl;
326
327 /*
328 * If we've gotten here we've gone bad, but let's
329 * not take down pim
330 */
331 if (!channel_oil) {
332 zlog_warn("Attempt to Add OIF for non-existent channel oil");
333 return -1;
334 }
1865a44a 335
ac4d0be5 336 pim_ifp = oif->info;
1865a44a 337
1865a44a 338#ifdef PIM_ENFORCE_LOOPFREE_MFC
ac4d0be5 339 /*
340 Prevent creating MFC entry with OIF=IIF.
341
342 This is a protection against implementation mistakes.
343
344 PIM protocol implicitely ensures loopfree multicast topology.
345
346 IGMP must be protected against adding looped MFC entries created
347 by both source and receiver attached to the same interface. See
348 TODO T22.
349 */
350 if (pim_ifp->mroute_vif_index == channel_oil->oil.mfcc_parent) {
351 channel_oil->oil_inherited_rescan = 1;
352 if (PIM_DEBUG_MROUTE) {
353 char group_str[INET_ADDRSTRLEN];
354 char source_str[INET_ADDRSTRLEN];
355 pim_inet4_dump("<group?>",
356 channel_oil->oil.mfcc_mcastgrp,
357 group_str, sizeof(group_str));
358 pim_inet4_dump("<source?>",
359 channel_oil->oil.mfcc_origin, source_str,
360 sizeof(source_str));
361 zlog_debug(
362 "%s %s: refusing protocol mask %u request for IIF=OIF=%s (vif_index=%d) for channel (S,G)=(%s,%s)",
363 __FILE__, __PRETTY_FUNCTION__, proto_mask,
364 oif->name, pim_ifp->mroute_vif_index,
365 source_str, group_str);
366 }
367 return -2;
368 }
1865a44a
DS
369#endif
370
ac4d0be5 371 /* Prevent single protocol from subscribing same interface to
372 channel (S,G) multiple times */
373 if (channel_oil->oif_flags[pim_ifp->mroute_vif_index] & proto_mask) {
374 if (PIM_DEBUG_MROUTE) {
375 char group_str[INET_ADDRSTRLEN];
376 char source_str[INET_ADDRSTRLEN];
377 pim_inet4_dump("<group?>",
378 channel_oil->oil.mfcc_mcastgrp,
379 group_str, sizeof(group_str));
380 pim_inet4_dump("<source?>",
381 channel_oil->oil.mfcc_origin, source_str,
382 sizeof(source_str));
383 zlog_debug(
384 "%s %s: existing protocol mask %u requested OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)",
385 __FILE__, __PRETTY_FUNCTION__, proto_mask,
386 oif->name, pim_ifp->mroute_vif_index,
387 channel_oil->oil
388 .mfcc_ttls[pim_ifp->mroute_vif_index],
389 source_str, group_str);
390 }
391 return -3;
392 }
393
394 /* Allow other protocol to request subscription of same interface to
395 * channel (S,G), we need to note this information
396 */
397 if (channel_oil->oif_flags[pim_ifp->mroute_vif_index]
398 & PIM_OIF_FLAG_PROTO_ANY) {
399
400 channel_oil->oif_creation[pim_ifp->mroute_vif_index] =
401 pim_time_monotonic_sec();
402 channel_oil->oif_flags[pim_ifp->mroute_vif_index] |= proto_mask;
403 /* Check the OIF really exists before returning, and only log
404 warning otherwise */
405 if (channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index] < 1) {
406 {
407 char group_str[INET_ADDRSTRLEN];
408 char source_str[INET_ADDRSTRLEN];
409 pim_inet4_dump("<group?>",
410 channel_oil->oil.mfcc_mcastgrp,
411 group_str, sizeof(group_str));
412 pim_inet4_dump("<source?>",
413 channel_oil->oil.mfcc_origin,
414 source_str, sizeof(source_str));
415 zlog_warn(
416 "%s %s: new protocol mask %u requested nonexistent OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)",
417 __FILE__, __PRETTY_FUNCTION__,
418 proto_mask, oif->name,
419 pim_ifp->mroute_vif_index,
420 channel_oil->oil.mfcc_ttls
421 [pim_ifp->mroute_vif_index],
422 source_str, group_str);
423 }
424 }
425
426 return 0;
427 }
428
429 old_ttl = channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index];
430
431 if (old_ttl > 0) {
432 if (PIM_DEBUG_MROUTE) {
433 char group_str[INET_ADDRSTRLEN];
434 char source_str[INET_ADDRSTRLEN];
435 pim_inet4_dump("<group?>",
436 channel_oil->oil.mfcc_mcastgrp,
437 group_str, sizeof(group_str));
438 pim_inet4_dump("<source?>",
439 channel_oil->oil.mfcc_origin, source_str,
440 sizeof(source_str));
441 zlog_debug(
442 "%s %s: interface %s (vif_index=%d) is existing output for channel (S,G)=(%s,%s)",
443 __FILE__, __PRETTY_FUNCTION__, oif->name,
444 pim_ifp->mroute_vif_index, source_str,
445 group_str);
446 }
447 return -4;
448 }
449
450 channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index] =
451 PIM_MROUTE_MIN_TTL;
452
453 if (pim_mroute_add(channel_oil, __PRETTY_FUNCTION__)) {
454 if (PIM_DEBUG_MROUTE) {
455 char group_str[INET_ADDRSTRLEN];
456 char source_str[INET_ADDRSTRLEN];
457 pim_inet4_dump("<group?>",
458 channel_oil->oil.mfcc_mcastgrp,
459 group_str, sizeof(group_str));
460 pim_inet4_dump("<source?>",
461 channel_oil->oil.mfcc_origin, source_str,
462 sizeof(source_str));
463 zlog_debug(
464 "%s %s: could not add output interface %s (vif_index=%d) for channel (S,G)=(%s,%s)",
465 __FILE__, __PRETTY_FUNCTION__, oif->name,
466 pim_ifp->mroute_vif_index, source_str,
467 group_str);
468 }
469
470 channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index] = old_ttl;
471 return -5;
472 }
473
474 channel_oil->oif_creation[pim_ifp->mroute_vif_index] =
475 pim_time_monotonic_sec();
476 ++channel_oil->oil_size;
477 channel_oil->oif_flags[pim_ifp->mroute_vif_index] |= proto_mask;
478
479 if (PIM_DEBUG_MROUTE) {
480 char group_str[INET_ADDRSTRLEN];
481 char source_str[INET_ADDRSTRLEN];
482 pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp,
483 group_str, sizeof(group_str));
484 pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin,
485 source_str, sizeof(source_str));
486 zlog_debug(
487 "%s %s: (S,G)=(%s,%s): proto_mask=%u OIF=%s vif_index=%d: DONE",
488 __FILE__, __PRETTY_FUNCTION__, source_str, group_str,
489 proto_mask, oif->name, pim_ifp->mroute_vif_index);
490 }
491
492 return 0;
1865a44a 493}
ce0ddb4e 494
ac4d0be5 495int pim_channel_oil_empty(struct channel_oil *c_oil)
ce0ddb4e 496{
ac4d0be5 497 static uint32_t zero[MAXVIFS];
498 static int inited = 0;
499
500 if (!c_oil)
501 return 1;
502 /*
503 * Not sure that this is necessary, but I would rather ensure
504 * that this works.
505 */
506 if (!inited) {
507 memset(&zero, 0, sizeof(uint32_t) * MAXVIFS);
508 inited = 1;
509 }
510
511 return !memcmp(c_oil->oif_flags, zero, MAXVIFS * sizeof(uint32_t));
ce0ddb4e 512}