]> git.proxmox.com Git - mirror_frr.git/blob - pimd/pim_oil.c
zebra: Allow ns delete to happen after under/over flow checks
[mirror_frr.git] / pimd / pim_oil.c
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.
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 */
19
20 #include <zebra.h>
21
22 #include "log.h"
23 #include "memory.h"
24 #include "linklist.h"
25 #include "if.h"
26 #include "hash.h"
27 #include "jhash.h"
28
29 #include "pimd.h"
30 #include "pim_oil.h"
31 #include "pim_str.h"
32 #include "pim_iface.h"
33 #include "pim_time.h"
34
35 // struct list *pim_channel_oil_list = NULL;
36 // struct hash *pim_channel_oil_hash = NULL;
37
38 char *pim_channel_oil_dump(struct channel_oil *c_oil, char *buf, size_t size)
39 {
40 char *out;
41 struct prefix_sg sg;
42 int i;
43
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);
48
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);
53 out += strlen(out);
54 }
55 }
56
57 return buf;
58 }
59
60 static int pim_channel_oil_compare(struct channel_oil *c1,
61 struct channel_oil *c2)
62 {
63 if (ntohl(c1->oil.mfcc_mcastgrp.s_addr)
64 < ntohl(c2->oil.mfcc_mcastgrp.s_addr))
65 return -1;
66
67 if (ntohl(c1->oil.mfcc_mcastgrp.s_addr)
68 > ntohl(c2->oil.mfcc_mcastgrp.s_addr))
69 return 1;
70
71 if (ntohl(c1->oil.mfcc_origin.s_addr)
72 < ntohl(c2->oil.mfcc_origin.s_addr))
73 return -1;
74
75 if (ntohl(c1->oil.mfcc_origin.s_addr)
76 > ntohl(c2->oil.mfcc_origin.s_addr))
77 return 1;
78
79 return 0;
80 }
81
82 static bool pim_oil_equal(const void *arg1, const void *arg2)
83 {
84 const struct channel_oil *c1 = (const struct channel_oil *)arg1;
85 const struct channel_oil *c2 = (const struct channel_oil *)arg2;
86
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 true;
90
91 return false;
92 }
93
94 static unsigned int pim_oil_hash_key(void *arg)
95 {
96 struct channel_oil *oil = (struct channel_oil *)arg;
97
98 return jhash_2words(oil->oil.mfcc_mcastgrp.s_addr,
99 oil->oil.mfcc_origin.s_addr, 0);
100 }
101
102 void pim_oil_init(struct pim_instance *pim)
103 {
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, pim_oil_hash_key,
108 pim_oil_equal, hash_name);
109
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;
114 }
115
116 void pim_oil_terminate(struct pim_instance *pim)
117 {
118 if (pim->channel_oil_list)
119 list_delete(&pim->channel_oil_list);
120
121 if (pim->channel_oil_hash)
122 hash_free(pim->channel_oil_hash);
123 pim->channel_oil_hash = NULL;
124 }
125
126 void pim_channel_oil_free(struct channel_oil *c_oil)
127 {
128 XFREE(MTYPE_PIM_CHANNEL_OIL, c_oil);
129 }
130
131 struct channel_oil *pim_find_channel_oil(struct pim_instance *pim,
132 struct prefix_sg *sg)
133 {
134 struct channel_oil *c_oil = NULL;
135 struct channel_oil lookup;
136
137 lookup.oil.mfcc_mcastgrp = sg->grp;
138 lookup.oil.mfcc_origin = sg->src;
139
140 c_oil = hash_lookup(pim->channel_oil_hash, &lookup);
141
142 return c_oil;
143 }
144
145 struct channel_oil *pim_channel_oil_add(struct pim_instance *pim,
146 struct prefix_sg *sg,
147 int input_vif_index)
148 {
149 struct channel_oil *c_oil;
150 struct interface *ifp;
151
152 c_oil = pim_find_channel_oil(pim, sg);
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(
167 pim, sg); // channel might be present prior to upstream
168 return c_oil;
169 }
170
171 ifp = pim_if_find_by_vif_index(pim, input_vif_index);
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));
181
182 c_oil->oil.mfcc_mcastgrp = sg->grp;
183 c_oil->oil.mfcc_origin = sg->src;
184 c_oil = hash_get(pim->channel_oil_hash, c_oil, hash_alloc_intern);
185
186 c_oil->oil.mfcc_parent = input_vif_index;
187 c_oil->oil_ref_count = 1;
188 c_oil->installed = 0;
189 c_oil->up = pim_upstream_find(pim, sg);
190 c_oil->pim = pim;
191
192 listnode_add_sort(pim->channel_oil_list, c_oil);
193
194 return c_oil;
195 }
196
197 void pim_channel_oil_del(struct channel_oil *c_oil)
198 {
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;
208 listnode_delete(c_oil->pim->channel_oil_list, c_oil);
209 hash_release(c_oil->pim->channel_oil_hash, c_oil);
210
211 pim_channel_oil_free(c_oil);
212 }
213 }
214
215 int pim_channel_del_oif(struct channel_oil *channel_oil, struct interface *oif,
216 uint32_t proto_mask)
217 {
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;
294 }
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);
310 }
311
312 return 0;
313 }
314
315
316 int pim_channel_add_oif(struct channel_oil *channel_oil, struct interface *oif,
317 uint32_t proto_mask)
318 {
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 }
330
331 pim_ifp = oif->info;
332
333 #ifdef PIM_ENFORCE_LOOPFREE_MFC
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 }
364 #endif
365
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;
488 }
489
490 int pim_channel_oil_empty(struct channel_oil *c_oil)
491 {
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));
507 }