]> git.proxmox.com Git - mirror_frr.git/blame - pimd/pim_oil.c
Merge branch 'stable/3.0' into tmp-3.0-master-merge
[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
611925dc
DS
35// struct list *pim_channel_oil_list = NULL;
36// struct hash *pim_channel_oil_hash = NULL;
040d86ad 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
611925dc 102void pim_oil_init(struct pim_instance *pim)
040d86ad 103{
9fb302f4
DS
104 char hash_name[64];
105
106 snprintf(hash_name, 64, "PIM %s Oil Hash", pim->vrf->name);
107 pim->channel_oil_hash = hash_create_size(8192,
108 pim_oil_hash_key,
109 pim_oil_equal,
110 hash_name);
d62a17ae 111
611925dc
DS
112 pim->channel_oil_list = list_new();
113 if (!pim->channel_oil_list) {
d62a17ae 114 zlog_err("%s %s: failure: channel_oil_list=list_new()",
115 __FILE__, __PRETTY_FUNCTION__);
116 return;
117 }
611925dc
DS
118 pim->channel_oil_list->del = (void (*)(void *))pim_channel_oil_free;
119 pim->channel_oil_list->cmp =
d62a17ae 120 (int (*)(void *, void *))pim_channel_oil_compare;
040d86ad
DS
121}
122
611925dc 123void pim_oil_terminate(struct pim_instance *pim)
040d86ad 124{
611925dc 125 if (pim->channel_oil_list)
69ccd63e 126 list_delete(pim->channel_oil_list);
611925dc 127 pim->channel_oil_list = NULL;
040d86ad 128
611925dc
DS
129 if (pim->channel_oil_hash)
130 hash_free(pim->channel_oil_hash);
131 pim->channel_oil_hash = NULL;
040d86ad
DS
132}
133
12e41d03
DL
134void pim_channel_oil_free(struct channel_oil *c_oil)
135{
d62a17ae 136 XFREE(MTYPE_PIM_CHANNEL_OIL, c_oil);
12e41d03
DL
137}
138
611925dc
DS
139static struct channel_oil *pim_find_channel_oil(struct pim_instance *pim,
140 struct prefix_sg *sg)
12e41d03 141{
d62a17ae 142 struct channel_oil *c_oil = NULL;
143 struct channel_oil lookup;
d270b216 144
d62a17ae 145 lookup.oil.mfcc_mcastgrp = sg->grp;
146 lookup.oil.mfcc_origin = sg->src;
d270b216 147
611925dc 148 c_oil = hash_lookup(pim->channel_oil_hash, &lookup);
d270b216 149
d62a17ae 150 return c_oil;
12e41d03
DL
151}
152
611925dc
DS
153struct channel_oil *pim_channel_oil_add(struct pim_instance *pim,
154 struct prefix_sg *sg,
d270b216 155 int input_vif_index)
12e41d03 156{
d62a17ae 157 struct channel_oil *c_oil;
158 struct interface *ifp;
159
611925dc 160 c_oil = pim_find_channel_oil(pim, sg);
d62a17ae 161 if (c_oil) {
162 if (c_oil->oil.mfcc_parent != input_vif_index) {
163 c_oil->oil_inherited_rescan = 1;
164 if (PIM_DEBUG_MROUTE)
165 zlog_debug(
166 "%s: Existing channel oil %s points to %d, modifying to point at %d",
167 __PRETTY_FUNCTION__,
168 pim_str_sg_dump(sg),
169 c_oil->oil.mfcc_parent,
170 input_vif_index);
171 }
172 c_oil->oil.mfcc_parent = input_vif_index;
173 ++c_oil->oil_ref_count;
174 c_oil->up = pim_upstream_find(
611925dc 175 pim, sg); // channel might be present prior to upstream
d62a17ae 176 return c_oil;
177 }
178
611925dc 179 ifp = pim_if_find_by_vif_index(pim, input_vif_index);
d62a17ae 180 if (!ifp) {
181 /* warning only */
182 zlog_warn(
183 "%s: (S,G)=%s could not find input interface for input_vif_index=%d",
184 __PRETTY_FUNCTION__, pim_str_sg_dump(sg),
185 input_vif_index);
186 }
187
188 c_oil = XCALLOC(MTYPE_PIM_CHANNEL_OIL, sizeof(*c_oil));
189 if (!c_oil) {
190 zlog_err("PIM XCALLOC(%zu) failure", sizeof(*c_oil));
191 return NULL;
192 }
193
194 c_oil->oil.mfcc_mcastgrp = sg->grp;
195 c_oil->oil.mfcc_origin = sg->src;
611925dc 196 c_oil = hash_get(pim->channel_oil_hash, c_oil, hash_alloc_intern);
d62a17ae 197
198 c_oil->oil.mfcc_parent = input_vif_index;
199 c_oil->oil_ref_count = 1;
200 c_oil->installed = 0;
611925dc
DS
201 c_oil->up = pim_upstream_find(pim, sg);
202 c_oil->pim = pim;
d62a17ae 203
611925dc 204 listnode_add_sort(pim->channel_oil_list, c_oil);
d62a17ae 205
206 return c_oil;
12e41d03
DL
207}
208
12e41d03
DL
209void pim_channel_oil_del(struct channel_oil *c_oil)
210{
d62a17ae 211 --c_oil->oil_ref_count;
212
213 if (c_oil->oil_ref_count < 1) {
214 /*
215 * notice that listnode_delete() can't be moved
216 * into pim_channel_oil_free() because the later is
217 * called by list_delete_all_node()
218 */
219 c_oil->up = NULL;
611925dc
DS
220 listnode_delete(c_oil->pim->channel_oil_list, c_oil);
221 hash_release(c_oil->pim->channel_oil_hash, c_oil);
d62a17ae 222
223 pim_channel_oil_free(c_oil);
224 }
12e41d03 225}
1865a44a 226
d62a17ae 227int pim_channel_del_oif(struct channel_oil *channel_oil, struct interface *oif,
228 uint32_t proto_mask)
887fa014 229{
d62a17ae 230 struct pim_interface *pim_ifp;
231
232 zassert(channel_oil);
233 zassert(oif);
234
235 pim_ifp = oif->info;
236
237 /*
238 * Don't do anything if we've been asked to remove a source
239 * that is not actually on it.
240 */
241 if (!(channel_oil->oif_flags[pim_ifp->mroute_vif_index] & proto_mask)) {
242 if (PIM_DEBUG_MROUTE) {
243 char group_str[INET_ADDRSTRLEN];
244 char source_str[INET_ADDRSTRLEN];
245 pim_inet4_dump("<group?>",
246 channel_oil->oil.mfcc_mcastgrp,
247 group_str, sizeof(group_str));
248 pim_inet4_dump("<source?>",
249 channel_oil->oil.mfcc_origin, source_str,
250 sizeof(source_str));
251 zlog_debug(
252 "%s %s: no existing protocol mask %u(%u) for requested OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)",
253 __FILE__, __PRETTY_FUNCTION__, proto_mask,
254 channel_oil
255 ->oif_flags[pim_ifp->mroute_vif_index],
256 oif->name, pim_ifp->mroute_vif_index,
257 channel_oil->oil
258 .mfcc_ttls[pim_ifp->mroute_vif_index],
259 source_str, group_str);
260 }
261 return 0;
262 }
263
264 channel_oil->oif_flags[pim_ifp->mroute_vif_index] &= ~proto_mask;
265
266 if (channel_oil->oif_flags[pim_ifp->mroute_vif_index]) {
267 if (PIM_DEBUG_MROUTE) {
268 char group_str[INET_ADDRSTRLEN];
269 char source_str[INET_ADDRSTRLEN];
270 pim_inet4_dump("<group?>",
271 channel_oil->oil.mfcc_mcastgrp,
272 group_str, sizeof(group_str));
273 pim_inet4_dump("<source?>",
274 channel_oil->oil.mfcc_origin, source_str,
275 sizeof(source_str));
276 zlog_debug(
277 "%s %s: other protocol masks remain for requested OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)",
278 __FILE__, __PRETTY_FUNCTION__, oif->name,
279 pim_ifp->mroute_vif_index,
280 channel_oil->oil
281 .mfcc_ttls[pim_ifp->mroute_vif_index],
282 source_str, group_str);
283 }
284 return 0;
285 }
286
287 channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index] = 0;
288
289 if (pim_mroute_add(channel_oil, __PRETTY_FUNCTION__)) {
290 if (PIM_DEBUG_MROUTE) {
291 char group_str[INET_ADDRSTRLEN];
292 char source_str[INET_ADDRSTRLEN];
293 pim_inet4_dump("<group?>",
294 channel_oil->oil.mfcc_mcastgrp,
295 group_str, sizeof(group_str));
296 pim_inet4_dump("<source?>",
297 channel_oil->oil.mfcc_origin, source_str,
298 sizeof(source_str));
299 zlog_debug(
300 "%s %s: could not remove output interface %s (vif_index=%d) for channel (S,G)=(%s,%s)",
301 __FILE__, __PRETTY_FUNCTION__, oif->name,
302 pim_ifp->mroute_vif_index, source_str,
303 group_str);
304 }
305 return -1;
887fa014 306 }
d62a17ae 307
308 --channel_oil->oil_size;
309
310 if (PIM_DEBUG_MROUTE) {
311 char group_str[INET_ADDRSTRLEN];
312 char source_str[INET_ADDRSTRLEN];
313 pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp,
314 group_str, sizeof(group_str));
315 pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin,
316 source_str, sizeof(source_str));
317 zlog_debug(
318 "%s %s: (S,G)=(%s,%s): proto_mask=%u IIF:%d OIF=%s vif_index=%d",
319 __FILE__, __PRETTY_FUNCTION__, source_str, group_str,
320 proto_mask, channel_oil->oil.mfcc_parent, oif->name,
321 pim_ifp->mroute_vif_index);
887fa014 322 }
d62a17ae 323
324 return 0;
887fa014
DS
325}
326
327
d62a17ae 328int pim_channel_add_oif(struct channel_oil *channel_oil, struct interface *oif,
329 uint32_t proto_mask)
1865a44a 330{
d62a17ae 331 struct pim_interface *pim_ifp;
332 int old_ttl;
333
334 /*
335 * If we've gotten here we've gone bad, but let's
336 * not take down pim
337 */
338 if (!channel_oil) {
339 zlog_warn("Attempt to Add OIF for non-existent channel oil");
340 return -1;
341 }
1865a44a 342
d62a17ae 343 pim_ifp = oif->info;
1865a44a 344
1865a44a 345#ifdef PIM_ENFORCE_LOOPFREE_MFC
d62a17ae 346 /*
347 Prevent creating MFC entry with OIF=IIF.
348
349 This is a protection against implementation mistakes.
350
351 PIM protocol implicitely ensures loopfree multicast topology.
352
353 IGMP must be protected against adding looped MFC entries created
354 by both source and receiver attached to the same interface. See
355 TODO T22.
356 */
357 if (pim_ifp->mroute_vif_index == channel_oil->oil.mfcc_parent) {
358 channel_oil->oil_inherited_rescan = 1;
359 if (PIM_DEBUG_MROUTE) {
360 char group_str[INET_ADDRSTRLEN];
361 char source_str[INET_ADDRSTRLEN];
362 pim_inet4_dump("<group?>",
363 channel_oil->oil.mfcc_mcastgrp,
364 group_str, sizeof(group_str));
365 pim_inet4_dump("<source?>",
366 channel_oil->oil.mfcc_origin, source_str,
367 sizeof(source_str));
368 zlog_debug(
369 "%s %s: refusing protocol mask %u request for IIF=OIF=%s (vif_index=%d) for channel (S,G)=(%s,%s)",
370 __FILE__, __PRETTY_FUNCTION__, proto_mask,
371 oif->name, pim_ifp->mroute_vif_index,
372 source_str, group_str);
373 }
374 return -2;
375 }
1865a44a
DS
376#endif
377
d62a17ae 378 /* Prevent single protocol from subscribing same interface to
379 channel (S,G) multiple times */
380 if (channel_oil->oif_flags[pim_ifp->mroute_vif_index] & proto_mask) {
381 if (PIM_DEBUG_MROUTE) {
382 char group_str[INET_ADDRSTRLEN];
383 char source_str[INET_ADDRSTRLEN];
384 pim_inet4_dump("<group?>",
385 channel_oil->oil.mfcc_mcastgrp,
386 group_str, sizeof(group_str));
387 pim_inet4_dump("<source?>",
388 channel_oil->oil.mfcc_origin, source_str,
389 sizeof(source_str));
390 zlog_debug(
391 "%s %s: existing protocol mask %u requested OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)",
392 __FILE__, __PRETTY_FUNCTION__, proto_mask,
393 oif->name, pim_ifp->mroute_vif_index,
394 channel_oil->oil
395 .mfcc_ttls[pim_ifp->mroute_vif_index],
396 source_str, group_str);
397 }
398 return -3;
399 }
400
401 /* Allow other protocol to request subscription of same interface to
402 * channel (S,G), we need to note this information
403 */
404 if (channel_oil->oif_flags[pim_ifp->mroute_vif_index]
405 & PIM_OIF_FLAG_PROTO_ANY) {
406
407 channel_oil->oif_creation[pim_ifp->mroute_vif_index] =
408 pim_time_monotonic_sec();
409 channel_oil->oif_flags[pim_ifp->mroute_vif_index] |= proto_mask;
410 /* Check the OIF really exists before returning, and only log
411 warning otherwise */
412 if (channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index] < 1) {
413 {
414 char group_str[INET_ADDRSTRLEN];
415 char source_str[INET_ADDRSTRLEN];
416 pim_inet4_dump("<group?>",
417 channel_oil->oil.mfcc_mcastgrp,
418 group_str, sizeof(group_str));
419 pim_inet4_dump("<source?>",
420 channel_oil->oil.mfcc_origin,
421 source_str, sizeof(source_str));
422 zlog_warn(
423 "%s %s: new protocol mask %u requested nonexistent OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)",
424 __FILE__, __PRETTY_FUNCTION__,
425 proto_mask, oif->name,
426 pim_ifp->mroute_vif_index,
427 channel_oil->oil.mfcc_ttls
428 [pim_ifp->mroute_vif_index],
429 source_str, group_str);
430 }
431 }
432
433 return 0;
434 }
435
436 old_ttl = channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index];
437
438 if (old_ttl > 0) {
439 if (PIM_DEBUG_MROUTE) {
440 char group_str[INET_ADDRSTRLEN];
441 char source_str[INET_ADDRSTRLEN];
442 pim_inet4_dump("<group?>",
443 channel_oil->oil.mfcc_mcastgrp,
444 group_str, sizeof(group_str));
445 pim_inet4_dump("<source?>",
446 channel_oil->oil.mfcc_origin, source_str,
447 sizeof(source_str));
448 zlog_debug(
449 "%s %s: interface %s (vif_index=%d) is existing output for channel (S,G)=(%s,%s)",
450 __FILE__, __PRETTY_FUNCTION__, oif->name,
451 pim_ifp->mroute_vif_index, source_str,
452 group_str);
453 }
454 return -4;
455 }
456
457 channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index] =
458 PIM_MROUTE_MIN_TTL;
459
460 if (pim_mroute_add(channel_oil, __PRETTY_FUNCTION__)) {
461 if (PIM_DEBUG_MROUTE) {
462 char group_str[INET_ADDRSTRLEN];
463 char source_str[INET_ADDRSTRLEN];
464 pim_inet4_dump("<group?>",
465 channel_oil->oil.mfcc_mcastgrp,
466 group_str, sizeof(group_str));
467 pim_inet4_dump("<source?>",
468 channel_oil->oil.mfcc_origin, source_str,
469 sizeof(source_str));
470 zlog_debug(
471 "%s %s: could not add output interface %s (vif_index=%d) for channel (S,G)=(%s,%s)",
472 __FILE__, __PRETTY_FUNCTION__, oif->name,
473 pim_ifp->mroute_vif_index, source_str,
474 group_str);
475 }
476
477 channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index] = old_ttl;
478 return -5;
479 }
480
481 channel_oil->oif_creation[pim_ifp->mroute_vif_index] =
482 pim_time_monotonic_sec();
483 ++channel_oil->oil_size;
484 channel_oil->oif_flags[pim_ifp->mroute_vif_index] |= proto_mask;
485
486 if (PIM_DEBUG_MROUTE) {
487 char group_str[INET_ADDRSTRLEN];
488 char source_str[INET_ADDRSTRLEN];
489 pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp,
490 group_str, sizeof(group_str));
491 pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin,
492 source_str, sizeof(source_str));
493 zlog_debug(
494 "%s %s: (S,G)=(%s,%s): proto_mask=%u OIF=%s vif_index=%d: DONE",
495 __FILE__, __PRETTY_FUNCTION__, source_str, group_str,
496 proto_mask, oif->name, pim_ifp->mroute_vif_index);
497 }
498
499 return 0;
1865a44a 500}
ce0ddb4e 501
d62a17ae 502int pim_channel_oil_empty(struct channel_oil *c_oil)
ce0ddb4e 503{
d62a17ae 504 static uint32_t zero[MAXVIFS];
505 static int inited = 0;
506
507 if (!c_oil)
508 return 1;
509 /*
510 * Not sure that this is necessary, but I would rather ensure
511 * that this works.
512 */
513 if (!inited) {
514 memset(&zero, 0, sizeof(uint32_t) * MAXVIFS);
515 inited = 1;
516 }
517
518 return !memcmp(c_oil->oif_flags, zero, MAXVIFS * sizeof(uint32_t));
ce0ddb4e 519}