]> git.proxmox.com Git - mirror_frr.git/blame - pimd/pim_oil.c
pimd: Cleanup dest buffer to small
[mirror_frr.git] / pimd / pim_oil.c
CommitLineData
12e41d03
DL
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
16 along with this program; see the file COPYING; if not, write to the
17 Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
18 MA 02110-1301 USA
12e41d03
DL
19*/
20
21#include <zebra.h>
22
23#include "log.h"
24#include "memory.h"
25#include "linklist.h"
744d91b3 26#include "if.h"
040d86ad
DS
27#include "hash.h"
28#include "jhash.h"
12e41d03
DL
29
30#include "pimd.h"
31#include "pim_oil.h"
32#include "pim_str.h"
33#include "pim_iface.h"
37653d4f 34#include "pim_time.h"
12e41d03 35
040d86ad
DS
36struct list *pim_channel_oil_list = NULL;
37struct hash *pim_channel_oil_hash = NULL;
38
781a1745
DS
39char *
40pim_channel_oil_dump (struct channel_oil *c_oil, char *buf, size_t size)
41{
42 struct prefix_sg sg;
43 int i;
44
45 memset (buf, 0, size);
46 sg.src = c_oil->oil.mfcc_origin;
47 sg.grp = c_oil->oil.mfcc_mcastgrp;
48 sprintf(buf, "%s IIF: %d, OIFS: ",
49 pim_str_sg_dump (&sg), c_oil->oil.mfcc_parent);
50
51 for (i = 0 ; i < MAXVIFS ; i++)
52 {
53 if (c_oil->oil.mfcc_ttls[i] != 0)
54 {
55 char buf1[10];
56 sprintf(buf1, "%d ", i);
57 strcat(buf, buf1);
58 }
59 }
60
61 return buf;
62}
63
040d86ad
DS
64static int
65pim_channel_oil_compare (struct channel_oil *c1, struct channel_oil *c2)
66{
67 if (ntohl(c1->oil.mfcc_mcastgrp.s_addr) < ntohl(c2->oil.mfcc_mcastgrp.s_addr))
68 return -1;
69
70 if (ntohl(c1->oil.mfcc_mcastgrp.s_addr) > ntohl(c2->oil.mfcc_mcastgrp.s_addr))
71 return 1;
72
73 if (ntohl(c1->oil.mfcc_origin.s_addr) < ntohl(c2->oil.mfcc_origin.s_addr))
74 return -1;
75
76 if (ntohl(c1->oil.mfcc_origin.s_addr) > ntohl(c2->oil.mfcc_origin.s_addr))
77 return 1;
78
79 return 0;
80}
81
82static int
83pim_oil_equal (const void *arg1, const void *arg2)
84{
85 const struct channel_oil *c1 = (const struct channel_oil *)arg1;
86 const struct channel_oil *c2 = (const struct channel_oil *)arg2;
87
88 if ((c1->oil.mfcc_mcastgrp.s_addr == c2->oil.mfcc_mcastgrp.s_addr) &&
5e998588 89 (c1->oil.mfcc_origin.s_addr == c2->oil.mfcc_origin.s_addr))
040d86ad
DS
90 return 1;
91
92 return 0;
93}
94
95static unsigned int
96pim_oil_hash_key (void *arg)
97{
98 struct channel_oil *oil = (struct channel_oil *)arg;
99
100 return jhash_2words (oil->oil.mfcc_mcastgrp.s_addr, oil->oil.mfcc_origin.s_addr, 0);
101}
102
103void
104pim_oil_init (void)
105{
106 pim_channel_oil_hash = hash_create_size (8192, pim_oil_hash_key,
107 pim_oil_equal);
108
109 pim_channel_oil_list = list_new();
110 if (!pim_channel_oil_list) {
111 zlog_err("%s %s: failure: channel_oil_list=list_new()",
112 __FILE__, __PRETTY_FUNCTION__);
113 return;
114 }
115 pim_channel_oil_list->del = (void (*)(void *)) pim_channel_oil_free;
116 pim_channel_oil_list->cmp = (int (*)(void *, void *)) pim_channel_oil_compare;
117}
118
119void
120pim_oil_terminate (void)
121{
122 if (pim_channel_oil_list)
123 list_free(pim_channel_oil_list);
124 pim_channel_oil_list = NULL;
125
126 if (pim_channel_oil_hash)
693c9259
DS
127 hash_free (pim_channel_oil_hash);
128 pim_channel_oil_hash = NULL;
040d86ad
DS
129}
130
12e41d03
DL
131void pim_channel_oil_free(struct channel_oil *c_oil)
132{
133 XFREE(MTYPE_PIM_CHANNEL_OIL, c_oil);
134}
135
7293a053
DS
136static void
137pim_del_channel_oil (struct channel_oil *c_oil)
12e41d03
DL
138{
139 /*
140 notice that listnode_delete() can't be moved
141 into pim_channel_oil_free() because the later is
142 called by list_delete_all_node()
143 */
040d86ad
DS
144 listnode_delete(pim_channel_oil_list, c_oil);
145 hash_release (pim_channel_oil_hash, c_oil);
12e41d03
DL
146
147 pim_channel_oil_free(c_oil);
148}
149
7293a053 150static struct channel_oil *
4ed0af70 151pim_add_channel_oil (struct prefix_sg *sg,
7293a053 152 int input_vif_index)
12e41d03
DL
153{
154 struct channel_oil *c_oil;
7293a053 155 struct interface *ifp;
12e41d03 156
7293a053
DS
157 ifp = pim_if_find_by_vif_index(input_vif_index);
158 if (!ifp) {
12e41d03 159 /* warning only */
05e451f8 160 zlog_warn("%s: (S,G)=%s could not find input interface for input_vif_index=%d",
12e41d03 161 __PRETTY_FUNCTION__,
05e451f8 162 pim_str_sg_dump (sg), input_vif_index);
12e41d03
DL
163 }
164
165 c_oil = XCALLOC(MTYPE_PIM_CHANNEL_OIL, sizeof(*c_oil));
166 if (!c_oil) {
167 zlog_err("PIM XCALLOC(%zu) failure", sizeof(*c_oil));
7293a053 168 return NULL;
12e41d03
DL
169 }
170
4ed0af70
DS
171 c_oil->oil.mfcc_mcastgrp = sg->grp;
172 c_oil->oil.mfcc_origin = sg->src;
040d86ad
DS
173 c_oil = hash_get (pim_channel_oil_hash, c_oil, hash_alloc_intern);
174
12e41d03
DL
175 c_oil->oil.mfcc_parent = input_vif_index;
176 c_oil->oil_ref_count = 1;
58302dc7 177 c_oil->installed = 0;
12e41d03 178
040d86ad 179 listnode_add_sort(pim_channel_oil_list, c_oil);
12e41d03
DL
180
181 return c_oil;
182}
183
4ed0af70 184static struct channel_oil *pim_find_channel_oil(struct prefix_sg *sg)
12e41d03 185{
040d86ad
DS
186 struct channel_oil *c_oil = NULL;
187 struct channel_oil lookup;
12e41d03 188
040d86ad
DS
189 lookup.oil.mfcc_mcastgrp = sg->grp;
190 lookup.oil.mfcc_origin = sg->src;
191
192 c_oil = hash_lookup (pim_channel_oil_hash, &lookup);
193
194 return c_oil;
12e41d03
DL
195}
196
4ed0af70 197struct channel_oil *pim_channel_oil_add(struct prefix_sg *sg,
12e41d03
DL
198 int input_vif_index)
199{
200 struct channel_oil *c_oil;
201
05e451f8 202 c_oil = pim_find_channel_oil(sg);
12e41d03 203 if (c_oil) {
8d90ffd0 204 if (c_oil->oil.mfcc_parent != input_vif_index)
b5183fd1
DS
205 {
206 c_oil->oil_inherited_rescan = 1;
207 if (PIM_DEBUG_MROUTE)
208 zlog_debug ("%s: Existing channel oil %s points to %d, modifying to point at %d",
209 __PRETTY_FUNCTION__, pim_str_sg_dump(sg), c_oil->oil.mfcc_parent, input_vif_index);
210 }
8d90ffd0 211 c_oil->oil.mfcc_parent = input_vif_index;
12e41d03
DL
212 ++c_oil->oil_ref_count;
213 return c_oil;
214 }
215
05e451f8 216 return pim_add_channel_oil(sg, input_vif_index);
12e41d03
DL
217}
218
219void pim_channel_oil_del(struct channel_oil *c_oil)
220{
221 --c_oil->oil_ref_count;
222
223 if (c_oil->oil_ref_count < 1) {
7293a053 224 pim_del_channel_oil(c_oil);
12e41d03
DL
225 }
226}
1865a44a 227
887fa014
DS
228int
229pim_channel_del_oif (struct channel_oil *channel_oil,
230 struct interface *oif,
231 uint32_t proto_mask)
232{
233 struct pim_interface *pim_ifp;
234
235 zassert (channel_oil);
236 zassert (oif);
237
238 pim_ifp = oif->info;
239
887fa014
DS
240 /*
241 * Don't do anything if we've been asked to remove a source
242 * that is not actually on it.
243 */
244 if (!(channel_oil->oif_flags[pim_ifp->mroute_vif_index] & proto_mask))
245 {
246 if (PIM_DEBUG_MROUTE)
247 {
eaa54bdb
DW
248 char group_str[INET_ADDRSTRLEN];
249 char source_str[INET_ADDRSTRLEN];
887fa014
DS
250 pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
251 pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
252 zlog_debug("%s %s: no existing protocol mask %u(%u) for requested OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)",
253 __FILE__, __PRETTY_FUNCTION__,
254 proto_mask, channel_oil->oif_flags[pim_ifp->mroute_vif_index],
255 oif->name, pim_ifp->mroute_vif_index,
256 channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index],
257 source_str, group_str);
258 }
259 return 0;
260 }
261
262 channel_oil->oif_flags[pim_ifp->mroute_vif_index] &= ~proto_mask;
263
264 if (channel_oil->oif_flags[pim_ifp->mroute_vif_index])
265 {
266 if (PIM_DEBUG_MROUTE)
267 {
eaa54bdb
DW
268 char group_str[INET_ADDRSTRLEN];
269 char source_str[INET_ADDRSTRLEN];
887fa014
DS
270 pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
271 pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
272 zlog_debug("%s %s: other protocol masks remain for requested OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)",
273 __FILE__, __PRETTY_FUNCTION__,
274 oif->name, pim_ifp->mroute_vif_index,
275 channel_oil->oil.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
6a78764e 283 if (pim_mroute_add (channel_oil, __PRETTY_FUNCTION__)) {
887fa014
DS
284 if (PIM_DEBUG_MROUTE)
285 {
eaa54bdb
DW
286 char group_str[INET_ADDRSTRLEN];
287 char source_str[INET_ADDRSTRLEN];
887fa014
DS
288 pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
289 pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
290 zlog_debug("%s %s: could not remove output interface %s (vif_index=%d) for channel (S,G)=(%s,%s)",
291 __FILE__, __PRETTY_FUNCTION__,
292 oif->name, pim_ifp->mroute_vif_index,
293 source_str, group_str);
294 }
295 return -1;
296 }
297
4bbcefe9
DS
298 if (PIM_DEBUG_MROUTE)
299 {
300 char group_str[INET_ADDRSTRLEN];
301 char source_str[INET_ADDRSTRLEN];
302 pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
303 pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
304 zlog_debug("%s %s: (S,G)=(%s,%s): proto_mask=%u OIF=%s vif_index=%d",
305 __FILE__, __PRETTY_FUNCTION__,
306 source_str, group_str,
307 proto_mask, oif->name, pim_ifp->mroute_vif_index);
308 }
309
887fa014
DS
310 return 0;
311}
312
313
1865a44a
DS
314int pim_channel_add_oif(struct channel_oil *channel_oil,
315 struct interface *oif,
316 uint32_t proto_mask)
317{
318 struct pim_interface *pim_ifp;
319 int old_ttl;
320
75395e6a
DS
321 /*
322 * If we've gotten here we've gone bad, but let's
323 * not take down pim
324 */
325 if (!channel_oil)
326 {
327 zlog_warn ("Attempt to Add OIF for non-existent channel oil");
328 return -1;
329 }
1865a44a
DS
330
331 pim_ifp = oif->info;
332
1865a44a
DS
333#ifdef PIM_ENFORCE_LOOPFREE_MFC
334 /*
335 Prevent creating MFC entry with OIF=IIF.
336
337 This is a protection against implementation mistakes.
338
339 PIM protocol implicitely ensures loopfree multicast topology.
340
341 IGMP must be protected against adding looped MFC entries created
342 by both source and receiver attached to the same interface. See
343 TODO T22.
344 */
345 if (pim_ifp->mroute_vif_index == channel_oil->oil.mfcc_parent) {
b5183fd1 346 channel_oil->oil_inherited_rescan = 1;
e6dad1ab
DS
347 if (PIM_DEBUG_MROUTE)
348 {
eaa54bdb
DW
349 char group_str[INET_ADDRSTRLEN];
350 char source_str[INET_ADDRSTRLEN];
e6dad1ab
DS
351 pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
352 pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
353 zlog_debug("%s %s: refusing protocol mask %u request for IIF=OIF=%s (vif_index=%d) for channel (S,G)=(%s,%s)",
354 __FILE__, __PRETTY_FUNCTION__,
355 proto_mask, oif->name, pim_ifp->mroute_vif_index,
356 source_str, group_str);
357 }
1865a44a
DS
358 return -2;
359 }
360#endif
361
1865a44a
DS
362 /* Prevent single protocol from subscribing same interface to
363 channel (S,G) multiple times */
364 if (channel_oil->oif_flags[pim_ifp->mroute_vif_index] & proto_mask) {
e6dad1ab
DS
365 if (PIM_DEBUG_MROUTE)
366 {
eaa54bdb
DW
367 char group_str[INET_ADDRSTRLEN];
368 char source_str[INET_ADDRSTRLEN];
e6dad1ab
DS
369 pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
370 pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
371 zlog_debug("%s %s: existing protocol mask %u requested OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)",
372 __FILE__, __PRETTY_FUNCTION__,
373 proto_mask, oif->name, pim_ifp->mroute_vif_index,
374 channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index],
375 source_str, group_str);
376 }
1865a44a
DS
377 return -3;
378 }
379
380 /* Allow other protocol to request subscription of same interface to
781a1745
DS
381 * channel (S,G), we need to note this information
382 */
1865a44a
DS
383 if (channel_oil->oif_flags[pim_ifp->mroute_vif_index] & PIM_OIF_FLAG_PROTO_ANY) {
384
781a1745
DS
385 channel_oil->oif_creation[pim_ifp->mroute_vif_index] = pim_time_monotonic_sec();
386 channel_oil->oif_flags[pim_ifp->mroute_vif_index] |= proto_mask;
1865a44a
DS
387 /* Check the OIF really exists before returning, and only log
388 warning otherwise */
389 if (channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index] < 1) {
781a1745
DS
390 {
391 char group_str[INET_ADDRSTRLEN];
392 char source_str[INET_ADDRSTRLEN];
393 pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
394 pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
395 zlog_warn("%s %s: new protocol mask %u requested nonexistent OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)",
396 __FILE__, __PRETTY_FUNCTION__,
397 proto_mask, oif->name, pim_ifp->mroute_vif_index,
398 channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index],
399 source_str, group_str);
400 }
1865a44a
DS
401 }
402
403 return 0;
404 }
405
406 old_ttl = channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index];
407
408 if (old_ttl > 0) {
e6dad1ab
DS
409 if (PIM_DEBUG_MROUTE)
410 {
eaa54bdb
DW
411 char group_str[INET_ADDRSTRLEN];
412 char source_str[INET_ADDRSTRLEN];
e6dad1ab
DS
413 pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
414 pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
415 zlog_debug("%s %s: interface %s (vif_index=%d) is existing output for channel (S,G)=(%s,%s)",
416 __FILE__, __PRETTY_FUNCTION__,
417 oif->name, pim_ifp->mroute_vif_index,
418 source_str, group_str);
419 }
1865a44a
DS
420 return -4;
421 }
422
423 channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index] = PIM_MROUTE_MIN_TTL;
424
6a78764e 425 if (pim_mroute_add(channel_oil, __PRETTY_FUNCTION__)) {
e6dad1ab
DS
426 if (PIM_DEBUG_MROUTE)
427 {
eaa54bdb
DW
428 char group_str[INET_ADDRSTRLEN];
429 char source_str[INET_ADDRSTRLEN];
e6dad1ab
DS
430 pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
431 pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
432 zlog_debug("%s %s: could not add output interface %s (vif_index=%d) for channel (S,G)=(%s,%s)",
433 __FILE__, __PRETTY_FUNCTION__,
434 oif->name, pim_ifp->mroute_vif_index,
435 source_str, group_str);
436 }
1865a44a
DS
437
438 channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index] = old_ttl;
439 return -5;
440 }
441
442 channel_oil->oif_creation[pim_ifp->mroute_vif_index] = pim_time_monotonic_sec();
443 ++channel_oil->oil_size;
444 channel_oil->oif_flags[pim_ifp->mroute_vif_index] |= proto_mask;
445
446 if (PIM_DEBUG_MROUTE) {
eaa54bdb
DW
447 char group_str[INET_ADDRSTRLEN];
448 char source_str[INET_ADDRSTRLEN];
1865a44a
DS
449 pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
450 pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
451 zlog_debug("%s %s: (S,G)=(%s,%s): proto_mask=%u OIF=%s vif_index=%d: DONE",
452 __FILE__, __PRETTY_FUNCTION__,
453 source_str, group_str,
454 proto_mask, oif->name, pim_ifp->mroute_vif_index);
455 }
456
457 return 0;
458}
ce0ddb4e
DS
459
460int
461pim_channel_oil_empty (struct channel_oil *c_oil)
462{
463 static uint32_t zero[MAXVIFS];
464 static int inited = 0;
465
466 if (!c_oil)
467 return 1;
468 /*
469 * Not sure that this is necessary, but I would rather ensure
470 * that this works.
471 */
472 if (!inited)
473 {
474 memset(&zero, 0, sizeof(uint32_t) * MAXVIFS);
475 inited = 1;
476 }
477
478 return !memcmp(c_oil->oif_flags, zero, MAXVIFS * sizeof(uint32_t));
479}