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