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