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