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