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