]> git.proxmox.com Git - mirror_frr.git/blob - pimd/pim_oil.c
Merge pull request #4518 from sarav511/dr_lhr
[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, const char *name)
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_DETAIL)
161 zlog_debug(
162 "%s: Existing channel oil %pSG4 points to %d, modifying to point at %d",
163 __PRETTY_FUNCTION__, sg,
164 c_oil->oil.mfcc_parent,
165 input_vif_index);
166 }
167 c_oil->oil.mfcc_parent = input_vif_index;
168 ++c_oil->oil_ref_count;
169 /* channel might be present prior to upstream */
170 c_oil->up = pim_upstream_find(pim, sg);
171
172 if (PIM_DEBUG_MROUTE)
173 zlog_debug(
174 "%s(%s): Existing oil for %pSG4 Ref Count: %d (Post Increment)",
175 __PRETTY_FUNCTION__, name, sg,
176 c_oil->oil_ref_count);
177 return c_oil;
178 }
179
180 if (input_vif_index != MAXVIFS) {
181 ifp = pim_if_find_by_vif_index(pim, input_vif_index);
182 if (!ifp) {
183 /* warning only */
184 zlog_warn(
185 "%s:%s (S,G)=%pSG4 could not find input interface for input_vif_index=%d",
186 __PRETTY_FUNCTION__, name, sg, input_vif_index);
187 }
188 }
189
190 c_oil = XCALLOC(MTYPE_PIM_CHANNEL_OIL, sizeof(*c_oil));
191
192 c_oil->oil.mfcc_mcastgrp = sg->grp;
193 c_oil->oil.mfcc_origin = sg->src;
194 c_oil = hash_get(pim->channel_oil_hash, c_oil, hash_alloc_intern);
195
196 c_oil->oil.mfcc_parent = input_vif_index;
197 c_oil->oil_ref_count = 1;
198 c_oil->installed = 0;
199 c_oil->up = pim_upstream_find(pim, sg);
200 c_oil->pim = pim;
201
202 listnode_add_sort(pim->channel_oil_list, c_oil);
203
204 if (PIM_DEBUG_MROUTE)
205 zlog_debug(
206 "%s(%s): New oil for %pSG4 vif_index: %d Ref Count: 1 (Post Increment)",
207 __PRETTY_FUNCTION__, name, sg, input_vif_index);
208 return c_oil;
209 }
210
211 void pim_channel_oil_del(struct channel_oil *c_oil, const char *name)
212 {
213 if (PIM_DEBUG_MROUTE) {
214 struct prefix_sg sg = {.src = c_oil->oil.mfcc_mcastgrp,
215 .grp = c_oil->oil.mfcc_origin};
216
217 zlog_debug(
218 "%s(%s): Del oil for %pSG4, Ref Count: %d (Predecrement)",
219 __PRETTY_FUNCTION__, name, &sg, c_oil->oil_ref_count);
220 }
221 --c_oil->oil_ref_count;
222
223 if (c_oil->oil_ref_count < 1) {
224 /*
225 * notice that listnode_delete() can't be moved
226 * into pim_channel_oil_free() because the later is
227 * called by list_delete_all_node()
228 */
229 c_oil->up = NULL;
230 listnode_delete(c_oil->pim->channel_oil_list, c_oil);
231 hash_release(c_oil->pim->channel_oil_hash, c_oil);
232
233 pim_channel_oil_free(c_oil);
234 }
235 }
236
237 int pim_channel_del_oif(struct channel_oil *channel_oil, struct interface *oif,
238 uint32_t proto_mask)
239 {
240 struct pim_interface *pim_ifp;
241
242 zassert(channel_oil);
243 zassert(oif);
244
245 pim_ifp = oif->info;
246
247 /*
248 * Don't do anything if we've been asked to remove a source
249 * that is not actually on it.
250 */
251 if (!(channel_oil->oif_flags[pim_ifp->mroute_vif_index] & proto_mask)) {
252 if (PIM_DEBUG_MROUTE) {
253 char group_str[INET_ADDRSTRLEN];
254 char source_str[INET_ADDRSTRLEN];
255 pim_inet4_dump("<group?>",
256 channel_oil->oil.mfcc_mcastgrp,
257 group_str, sizeof(group_str));
258 pim_inet4_dump("<source?>",
259 channel_oil->oil.mfcc_origin, source_str,
260 sizeof(source_str));
261 zlog_debug(
262 "%s %s: no existing protocol mask %u(%u) for requested OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)",
263 __FILE__, __PRETTY_FUNCTION__, proto_mask,
264 channel_oil
265 ->oif_flags[pim_ifp->mroute_vif_index],
266 oif->name, pim_ifp->mroute_vif_index,
267 channel_oil->oil
268 .mfcc_ttls[pim_ifp->mroute_vif_index],
269 source_str, group_str);
270 }
271 return 0;
272 }
273
274 channel_oil->oif_flags[pim_ifp->mroute_vif_index] &= ~proto_mask;
275
276 if (channel_oil->oif_flags[pim_ifp->mroute_vif_index]) {
277 if (PIM_DEBUG_MROUTE) {
278 char group_str[INET_ADDRSTRLEN];
279 char source_str[INET_ADDRSTRLEN];
280 pim_inet4_dump("<group?>",
281 channel_oil->oil.mfcc_mcastgrp,
282 group_str, sizeof(group_str));
283 pim_inet4_dump("<source?>",
284 channel_oil->oil.mfcc_origin, source_str,
285 sizeof(source_str));
286 zlog_debug(
287 "%s %s: other protocol masks remain for requested OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)",
288 __FILE__, __PRETTY_FUNCTION__, oif->name,
289 pim_ifp->mroute_vif_index,
290 channel_oil->oil
291 .mfcc_ttls[pim_ifp->mroute_vif_index],
292 source_str, group_str);
293 }
294 return 0;
295 }
296
297 channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index] = 0;
298
299 if (pim_mroute_add(channel_oil, __PRETTY_FUNCTION__)) {
300 if (PIM_DEBUG_MROUTE) {
301 char group_str[INET_ADDRSTRLEN];
302 char source_str[INET_ADDRSTRLEN];
303 pim_inet4_dump("<group?>",
304 channel_oil->oil.mfcc_mcastgrp,
305 group_str, sizeof(group_str));
306 pim_inet4_dump("<source?>",
307 channel_oil->oil.mfcc_origin, source_str,
308 sizeof(source_str));
309 zlog_debug(
310 "%s %s: could not remove output interface %s (vif_index=%d) for channel (S,G)=(%s,%s)",
311 __FILE__, __PRETTY_FUNCTION__, oif->name,
312 pim_ifp->mroute_vif_index, source_str,
313 group_str);
314 }
315 return -1;
316 }
317
318 --channel_oil->oil_size;
319
320 if (PIM_DEBUG_MROUTE) {
321 char group_str[INET_ADDRSTRLEN];
322 char source_str[INET_ADDRSTRLEN];
323 pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp,
324 group_str, sizeof(group_str));
325 pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin,
326 source_str, sizeof(source_str));
327 zlog_debug(
328 "%s %s: (S,G)=(%s,%s): proto_mask=%u IIF:%d OIF=%s vif_index=%d",
329 __FILE__, __PRETTY_FUNCTION__, source_str, group_str,
330 proto_mask, channel_oil->oil.mfcc_parent, oif->name,
331 pim_ifp->mroute_vif_index);
332 }
333
334 return 0;
335 }
336
337
338 int pim_channel_add_oif(struct channel_oil *channel_oil, struct interface *oif,
339 uint32_t proto_mask)
340 {
341 struct pim_interface *pim_ifp;
342 int old_ttl;
343 bool allow_iif_in_oil = false;
344
345 /*
346 * If we've gotten here we've gone bad, but let's
347 * not take down pim
348 */
349 if (!channel_oil) {
350 zlog_warn("Attempt to Add OIF for non-existent channel oil");
351 return -1;
352 }
353
354 pim_ifp = oif->info;
355
356 #ifdef PIM_ENFORCE_LOOPFREE_MFC
357 /*
358 Prevent creating MFC entry with OIF=IIF.
359
360 This is a protection against implementation mistakes.
361
362 PIM protocol implicitely ensures loopfree multicast topology.
363
364 IGMP must be protected against adding looped MFC entries created
365 by both source and receiver attached to the same interface. See
366 TODO T22.
367 We shall allow igmp to create upstream when it is DR for the intf.
368 Assume RP reachable via non DR.
369 */
370 if ((channel_oil->up &&
371 PIM_UPSTREAM_FLAG_TEST_ALLOW_IIF_IN_OIL(channel_oil->up->flags)) ||
372 ((proto_mask == PIM_OIF_FLAG_PROTO_IGMP) && PIM_I_am_DR(pim_ifp))) {
373 allow_iif_in_oil = true;
374 }
375
376 if (!allow_iif_in_oil &&
377 pim_ifp->mroute_vif_index == channel_oil->oil.mfcc_parent) {
378 channel_oil->oil_inherited_rescan = 1;
379 if (PIM_DEBUG_MROUTE) {
380 char group_str[INET_ADDRSTRLEN];
381 char source_str[INET_ADDRSTRLEN];
382 pim_inet4_dump("<group?>",
383 channel_oil->oil.mfcc_mcastgrp,
384 group_str, sizeof(group_str));
385 pim_inet4_dump("<source?>",
386 channel_oil->oil.mfcc_origin, source_str,
387 sizeof(source_str));
388 zlog_debug(
389 "%s %s: refusing protocol mask %u request for IIF=OIF=%s (vif_index=%d) for channel (S,G)=(%s,%s)",
390 __FILE__, __PRETTY_FUNCTION__, proto_mask,
391 oif->name, pim_ifp->mroute_vif_index,
392 source_str, group_str);
393 }
394 return -2;
395 }
396 #endif
397
398 /* Prevent single protocol from subscribing same interface to
399 channel (S,G) multiple times */
400 if (channel_oil->oif_flags[pim_ifp->mroute_vif_index] & proto_mask) {
401 if (PIM_DEBUG_MROUTE) {
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, source_str,
409 sizeof(source_str));
410 zlog_debug(
411 "%s %s: existing protocol mask %u requested OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)",
412 __FILE__, __PRETTY_FUNCTION__, proto_mask,
413 oif->name, pim_ifp->mroute_vif_index,
414 channel_oil->oil
415 .mfcc_ttls[pim_ifp->mroute_vif_index],
416 source_str, group_str);
417 }
418 return -3;
419 }
420
421 /* Allow other protocol to request subscription of same interface to
422 * channel (S,G), we need to note this information
423 */
424 if (channel_oil->oif_flags[pim_ifp->mroute_vif_index]
425 & PIM_OIF_FLAG_PROTO_ANY) {
426
427 /* Updating time here is not required as this time has to
428 * indicate when the interface is added
429 */
430
431 channel_oil->oif_flags[pim_ifp->mroute_vif_index] |= proto_mask;
432 /* Check the OIF really exists before returning, and only log
433 warning otherwise */
434 if (channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index] < 1) {
435 {
436 char group_str[INET_ADDRSTRLEN];
437 char source_str[INET_ADDRSTRLEN];
438 pim_inet4_dump("<group?>",
439 channel_oil->oil.mfcc_mcastgrp,
440 group_str, sizeof(group_str));
441 pim_inet4_dump("<source?>",
442 channel_oil->oil.mfcc_origin,
443 source_str, sizeof(source_str));
444 zlog_warn(
445 "%s %s: new protocol mask %u requested nonexistent OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)",
446 __FILE__, __PRETTY_FUNCTION__,
447 proto_mask, oif->name,
448 pim_ifp->mroute_vif_index,
449 channel_oil->oil.mfcc_ttls
450 [pim_ifp->mroute_vif_index],
451 source_str, group_str);
452 }
453 }
454
455 return 0;
456 }
457
458 old_ttl = channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index];
459
460 if (old_ttl > 0) {
461 if (PIM_DEBUG_MROUTE) {
462 char group_str[INET_ADDRSTRLEN];
463 char source_str[INET_ADDRSTRLEN];
464 pim_inet4_dump("<group?>",
465 channel_oil->oil.mfcc_mcastgrp,
466 group_str, sizeof(group_str));
467 pim_inet4_dump("<source?>",
468 channel_oil->oil.mfcc_origin, source_str,
469 sizeof(source_str));
470 zlog_debug(
471 "%s %s: interface %s (vif_index=%d) is existing output for channel (S,G)=(%s,%s)",
472 __FILE__, __PRETTY_FUNCTION__, oif->name,
473 pim_ifp->mroute_vif_index, source_str,
474 group_str);
475 }
476 return -4;
477 }
478
479 channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index] =
480 PIM_MROUTE_MIN_TTL;
481
482 /* channel_oil->oil.mfcc_parent != MAXVIFS indicate this entry is not
483 * valid to get installed in kernel.
484 */
485 if (channel_oil->oil.mfcc_parent != MAXVIFS) {
486 if (pim_mroute_add(channel_oil, __PRETTY_FUNCTION__)) {
487 if (PIM_DEBUG_MROUTE) {
488 char group_str[INET_ADDRSTRLEN];
489 char source_str[INET_ADDRSTRLEN];
490 pim_inet4_dump("<group?>",
491 channel_oil->oil.mfcc_mcastgrp,
492 group_str, sizeof(group_str));
493 pim_inet4_dump("<source?>",
494 channel_oil->oil.mfcc_origin, source_str,
495 sizeof(source_str));
496 zlog_debug(
497 "%s %s: could not add output interface %s (vif_index=%d) for channel (S,G)=(%s,%s)",
498 __FILE__, __PRETTY_FUNCTION__, oif->name,
499 pim_ifp->mroute_vif_index, source_str,
500 group_str);
501 }
502
503 channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index]
504 = old_ttl;
505 return -5;
506 }
507 }
508
509 channel_oil->oif_creation[pim_ifp->mroute_vif_index] =
510 pim_time_monotonic_sec();
511 ++channel_oil->oil_size;
512 channel_oil->oif_flags[pim_ifp->mroute_vif_index] |= proto_mask;
513
514 if (PIM_DEBUG_MROUTE) {
515 char group_str[INET_ADDRSTRLEN];
516 char source_str[INET_ADDRSTRLEN];
517 pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp,
518 group_str, sizeof(group_str));
519 pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin,
520 source_str, sizeof(source_str));
521 zlog_debug(
522 "%s %s: (S,G)=(%s,%s): proto_mask=%u OIF=%s vif_index=%d: DONE",
523 __FILE__, __PRETTY_FUNCTION__, source_str, group_str,
524 proto_mask, oif->name, pim_ifp->mroute_vif_index);
525 }
526
527 return 0;
528 }
529
530 int pim_channel_oil_empty(struct channel_oil *c_oil)
531 {
532 static uint32_t zero[MAXVIFS];
533 static int inited = 0;
534
535 if (!c_oil)
536 return 1;
537 /*
538 * Not sure that this is necessary, but I would rather ensure
539 * that this works.
540 */
541 if (!inited) {
542 memset(&zero, 0, sizeof(uint32_t) * MAXVIFS);
543 inited = 1;
544 }
545
546 return !memcmp(c_oil->oif_flags, zero, MAXVIFS * sizeof(uint32_t));
547 }