blob: 52d4f0d85280c6de1c86a201c613404a8401f4a6 [file] [log] [blame]
Everton Marques871dbcf2009-08-11 15:43:05 -03001/*
2 PIM for Quagga
3 Copyright (C) 2008 Everton da Silva Marques
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful, but
10 WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; see the file COPYING; if not, write to the
16 Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
17 MA 02110-1301 USA
18
19 $QuaggaId: $Format:%an, %ai, %h$ $
20*/
21
22#include <zebra.h>
23
24#include "if.h"
25#include "log.h"
26#include "vty.h"
27#include "memory.h"
28#include "prefix.h"
29
30#include "pimd.h"
31#include "pim_iface.h"
32#include "pim_igmp.h"
33#include "pim_mroute.h"
34#include "pim_oil.h"
35#include "pim_str.h"
36#include "pim_pim.h"
37#include "pim_neighbor.h"
38#include "pim_ifchannel.h"
39#include "pim_rand.h"
40#include "pim_sock.h"
Everton Marques567f9272010-02-19 19:07:00 -020041#include "pim_time.h"
Everton Marques96f91ae2009-10-07 18:41:45 -030042#include "pim_ssmpingd.h"
Everton Marques871dbcf2009-08-11 15:43:05 -030043
44static void pim_if_igmp_join_del_all(struct interface *ifp);
45
46void pim_if_init()
47{
48 if_init();
49}
50
51static void *if_list_clean(struct pim_interface *pim_ifp)
52{
53 if (pim_ifp->igmp_join_list) {
54 list_delete(pim_ifp->igmp_join_list);
55 }
56
57 if (pim_ifp->igmp_socket_list) {
58 list_delete(pim_ifp->igmp_socket_list);
59 }
60
61 if (pim_ifp->pim_neighbor_list) {
62 list_delete(pim_ifp->pim_neighbor_list);
63 }
64
65 if (pim_ifp->pim_ifchannel_list) {
66 list_delete(pim_ifp->pim_ifchannel_list);
67 }
68
69 XFREE(MTYPE_PIM_INTERFACE, pim_ifp);
70
71 return 0;
72}
73
74struct pim_interface *pim_if_new(struct interface *ifp, int igmp, int pim)
75{
76 struct pim_interface *pim_ifp;
77
78 zassert(ifp);
79 zassert(!ifp->info);
80
81 pim_ifp = XMALLOC(MTYPE_PIM_INTERFACE, sizeof(*pim_ifp));
82 if (!pim_ifp) {
83 zlog_err("PIM XMALLOC(%d) failure", sizeof(*pim_ifp));
84 return 0;
85 }
86
87 pim_ifp->options = 0;
88 pim_ifp->mroute_vif_index = -1;
89
Leonard Herve236b0152009-08-11 15:51:52 -030090 pim_ifp->igmp_default_robustness_variable = IGMP_DEFAULT_ROBUSTNESS_VARIABLE;
91 pim_ifp->igmp_default_query_interval = IGMP_GENERAL_QUERY_INTERVAL;
92 pim_ifp->igmp_query_max_response_time_dsec = IGMP_QUERY_MAX_RESPONSE_TIME_DSEC;
93 pim_ifp->igmp_specific_query_max_response_time_dsec = IGMP_SPECIFIC_QUERY_MAX_RESPONSE_TIME_DSEC;
Everton Marques871dbcf2009-08-11 15:43:05 -030094
95 /*
96 RFC 3376: 8.3. Query Response Interval
97 The number of seconds represented by the [Query Response Interval]
98 must be less than the [Query Interval].
99 */
100 zassert(pim_ifp->igmp_query_max_response_time_dsec < pim_ifp->igmp_default_query_interval);
101
102 if (pim)
103 PIM_IF_DO_PIM(pim_ifp->options);
104 if (igmp)
105 PIM_IF_DO_IGMP(pim_ifp->options);
106
107#if 0
108 /* FIXME: Should join? */
109 PIM_IF_DO_IGMP_LISTEN_ALLROUTERS(pim_ifp->options);
110#endif
111
112 pim_ifp->igmp_join_list = 0;
113 pim_ifp->igmp_socket_list = 0;
114 pim_ifp->pim_neighbor_list = 0;
115 pim_ifp->pim_ifchannel_list = 0;
116
117 /* list of struct igmp_sock */
118 pim_ifp->igmp_socket_list = list_new();
119 if (!pim_ifp->igmp_socket_list) {
120 zlog_err("%s %s: failure: igmp_socket_list=list_new()",
121 __FILE__, __PRETTY_FUNCTION__);
122 return if_list_clean(pim_ifp);
123 }
124 pim_ifp->igmp_socket_list->del = (void (*)(void *)) igmp_sock_free;
125
126 /* list of struct pim_neighbor */
127 pim_ifp->pim_neighbor_list = list_new();
128 if (!pim_ifp->pim_neighbor_list) {
129 zlog_err("%s %s: failure: pim_neighbor_list=list_new()",
130 __FILE__, __PRETTY_FUNCTION__);
131 return if_list_clean(pim_ifp);
132 }
133 pim_ifp->pim_neighbor_list->del = (void (*)(void *)) pim_neighbor_free;
134
135 /* list of struct pim_ifchannel */
136 pim_ifp->pim_ifchannel_list = list_new();
137 if (!pim_ifp->pim_ifchannel_list) {
138 zlog_err("%s %s: failure: pim_ifchannel_list=list_new()",
139 __FILE__, __PRETTY_FUNCTION__);
140 return if_list_clean(pim_ifp);
141 }
142 pim_ifp->pim_ifchannel_list->del = (void (*)(void *)) pim_ifchannel_free;
143
144 ifp->info = pim_ifp;
145
146 pim_sock_reset(ifp);
147
148 zassert(PIM_IF_TEST_PIM(pim_ifp->options) || PIM_IF_TEST_IGMP(pim_ifp->options));
149
150 if (PIM_MROUTE_IS_ENABLED) {
151 pim_if_add_vif(ifp);
152 }
153
154 return pim_ifp;
155}
156
157void pim_if_delete(struct interface *ifp)
158{
159 struct pim_interface *pim_ifp;
160
161 zassert(ifp);
162 pim_ifp = ifp->info;
163 zassert(pim_ifp);
164
165 if (pim_ifp->igmp_join_list) {
166 pim_if_igmp_join_del_all(ifp);
167 }
168 zassert(!pim_ifp->igmp_join_list);
169
170 zassert(pim_ifp->igmp_socket_list);
171 zassert(!listcount(pim_ifp->igmp_socket_list));
172
173 zassert(pim_ifp->pim_neighbor_list);
174 zassert(!listcount(pim_ifp->pim_neighbor_list));
175
176 zassert(pim_ifp->pim_ifchannel_list);
177 zassert(!listcount(pim_ifp->pim_ifchannel_list));
178
179 if (PIM_MROUTE_IS_ENABLED) {
180 pim_if_del_vif(ifp);
181 }
182
183 list_delete(pim_ifp->igmp_socket_list);
184 list_delete(pim_ifp->pim_neighbor_list);
185 list_delete(pim_ifp->pim_ifchannel_list);
186
187 XFREE(MTYPE_PIM_INTERFACE, pim_ifp);
188
189 ifp->info = 0;
190}
191
192void pim_if_update_could_assert(struct interface *ifp)
193{
194 struct pim_interface *pim_ifp;
195 struct listnode *node;
196 struct listnode *next_node;
197 struct pim_ifchannel *ch;
198
199 pim_ifp = ifp->info;
200 zassert(pim_ifp);
201
202 for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, node, next_node, ch)) {
203 pim_ifchannel_update_could_assert(ch);
204 }
205}
206
207static void pim_if_update_my_assert_metric(struct interface *ifp)
208{
209 struct pim_interface *pim_ifp;
210 struct listnode *node;
211 struct listnode *next_node;
212 struct pim_ifchannel *ch;
213
214 pim_ifp = ifp->info;
215 zassert(pim_ifp);
216
217 for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, node, next_node, ch)) {
218 pim_ifchannel_update_my_assert_metric(ch);
219 }
220}
221
222static void pim_addr_change(struct interface *ifp)
223{
224 struct pim_interface *pim_ifp;
225
226 pim_ifp = ifp->info;
227 zassert(pim_ifp);
228
229 pim_if_dr_election(ifp); /* Done TODO T30 */
230 pim_if_update_join_desired(pim_ifp); /* depends on DR */
231 pim_if_update_could_assert(ifp); /* depends on DR */
232 pim_if_update_my_assert_metric(ifp); /* depends on could_assert */
233 pim_if_update_assert_tracking_desired(ifp); /* depends on DR, join_desired */
234
235 /*
236 RFC 4601: 4.3.1. Sending Hello Messages
237
238 1) Before an interface goes down or changes primary IP address, a
239 Hello message with a zero HoldTime should be sent immediately
240 (with the old IP address if the IP address changed).
241 -- FIXME See CAVEAT C13
242
243 2) After an interface has changed its IP address, it MUST send a
244 Hello message with its new IP address.
245 -- DONE below
246
247 3) If an interface changes one of its secondary IP addresses, a
248 Hello message with an updated Address_List option and a non-zero
249 HoldTime should be sent immediately.
250 -- FIXME See TODO T31
251 */
252 pim_ifp->pim_ifstat_hello_sent = 0; /* reset hello counter */
253 if (pim_ifp->pim_sock_fd < 0)
254 return;
255 pim_hello_restart_now(ifp); /* send hello and restart timer */
256}
257
258static void on_primary_address_change(struct interface *ifp,
259 const char *caller,
260 struct in_addr old_addr,
261 struct in_addr new_addr)
262{
263 struct pim_interface *pim_ifp;
264
265 {
266 char old_str[100];
267 char new_str[100];
268 pim_inet4_dump("<old?>", old_addr, old_str, sizeof(old_str));
269 pim_inet4_dump("<new?>", new_addr, new_str, sizeof(new_str));
270 zlog_info("%s: %s: primary address changed from %s to %s on interface %s",
271 __PRETTY_FUNCTION__, caller,
272 old_str, new_str, ifp->name);
273 }
274
275 pim_ifp = ifp->info;
276
277 if (pim_ifp) {
278 if (PIM_IF_TEST_PIM(pim_ifp->options)) {
279 pim_addr_change(ifp);
280 }
281 }
282}
283
284static void detect_primary_address_change(struct interface *ifp,
285 const char *caller)
286{
287 struct pim_interface *pim_ifp;
288 struct in_addr new_prim_addr;
289
290 pim_ifp = ifp->info;
291 if (!pim_ifp)
292 return;
293
294 new_prim_addr = pim_find_primary_addr(ifp);
295
296 if (PIM_DEBUG_ZEBRA) {
297 char new_prim_str[100];
298 char old_prim_str[100];
299 pim_inet4_dump("<new?>", new_prim_addr, new_prim_str, sizeof(new_prim_str));
300 pim_inet4_dump("<old?>", pim_ifp->primary_address, old_prim_str, sizeof(old_prim_str));
301 zlog_debug("%s: old primary addr %s, new primary addr %s on interface %s",
302 __PRETTY_FUNCTION__,
303 old_prim_str, new_prim_str, ifp->name);
304 }
305
306 if (new_prim_addr.s_addr != pim_ifp->primary_address.s_addr) {
307 struct in_addr old_addr = pim_ifp->primary_address;
308 pim_ifp->primary_address = new_prim_addr;
309
310 on_primary_address_change(ifp, caller, old_addr, new_prim_addr);
311 }
312}
313
314void pim_if_addr_add(struct connected *ifc)
315{
316 struct pim_interface *pim_ifp;
317 struct interface *ifp;
318 struct in_addr ifaddr;
319
320 zassert(ifc);
321
322 ifp = ifc->ifp;
323 zassert(ifp);
324 pim_ifp = ifp->info;
325 if (!pim_ifp)
326 return;
327
328 if (!if_is_operative(ifp))
329 return;
330
331 ifaddr = ifc->address->u.prefix4;
332
333 detect_primary_address_change(ifp, __PRETTY_FUNCTION__);
334
335 if (PIM_IF_TEST_IGMP(pim_ifp->options)) {
336 struct igmp_sock *igmp;
337
338 /* lookup IGMP socket */
339 igmp = pim_igmp_sock_lookup_ifaddr(pim_ifp->igmp_socket_list,
340 ifaddr);
341 if (!igmp) {
342 /* if addr new, add IGMP socket */
343 pim_igmp_sock_add(pim_ifp->igmp_socket_list, ifaddr, ifp);
344 }
345 } /* igmp */
346
347 if (PIM_IF_TEST_PIM(pim_ifp->options)) {
348
349 /* Interface has a valid primary address ? */
350 if (PIM_INADDR_ISNOT_ANY(pim_ifp->primary_address)) {
351
352 /* Interface has a valid socket ? */
353 if (pim_ifp->pim_sock_fd < 0) {
354 if (pim_sock_add(ifp)) {
355 zlog_warn("Failure creating PIM socket for interface %s",
356 ifp->name);
357 }
358 }
359
360 }
361 } /* pim */
362
363 if (PIM_MROUTE_IS_ENABLED) {
364 /*
365 PIM or IGMP is enabled on interface, and there is at least one
366 address assigned, then try to create a vif_index.
367 */
368 if (pim_ifp->mroute_vif_index < 0) {
369 pim_if_add_vif(ifp);
370 }
371 }
372}
373
374static void pim_if_addr_del_igmp(struct connected *ifc)
375{
376 struct pim_interface *pim_ifp = ifc->ifp->info;
377 struct igmp_sock *igmp;
378 struct in_addr ifaddr;
379
380 if (ifc->address->family != AF_INET) {
381 /* non-IPv4 address */
382 return;
383 }
384
385 if (!pim_ifp) {
386 /* IGMP not enabled on interface */
387 return;
388 }
389
390 ifaddr = ifc->address->u.prefix4;
391
392 /* lookup IGMP socket */
393 igmp = pim_igmp_sock_lookup_ifaddr(pim_ifp->igmp_socket_list,
394 ifaddr);
395 if (igmp) {
396 /* if addr found, del IGMP socket */
397 igmp_sock_delete(igmp);
398 }
399}
400
401static void pim_if_addr_del_pim(struct connected *ifc)
402{
403 struct pim_interface *pim_ifp = ifc->ifp->info;
404
405 if (ifc->address->family != AF_INET) {
406 /* non-IPv4 address */
407 return;
408 }
409
410 if (!pim_ifp) {
411 /* PIM not enabled on interface */
412 return;
413 }
414
415 if (PIM_INADDR_ISNOT_ANY(pim_ifp->primary_address)) {
416 /* Interface keeps a valid primary address */
417 return;
418 }
419
420 if (pim_ifp->pim_sock_fd < 0) {
421 /* Interface does not hold a valid socket any longer */
422 return;
423 }
424
425 /*
426 pim_sock_delete() closes the socket, stops read and timer threads,
427 and kills all neighbors.
428 */
429 pim_sock_delete(ifc->ifp, "last address has been removed from interface");
430}
431
432void pim_if_addr_del(struct connected *ifc)
433{
434 struct interface *ifp;
435
436 zassert(ifc);
437 ifp = ifc->ifp;
438 zassert(ifp);
439
440 detect_primary_address_change(ifp, __PRETTY_FUNCTION__);
441
442 pim_if_addr_del_igmp(ifc);
443 pim_if_addr_del_pim(ifc);
444}
445
446void pim_if_addr_add_all(struct interface *ifp)
447{
448 struct connected *ifc;
449 struct listnode *node;
450 struct listnode *nextnode;
451
452 /* PIM/IGMP enabled ? */
453 if (!ifp->info)
454 return;
455
456 for (ALL_LIST_ELEMENTS(ifp->connected, node, nextnode, ifc)) {
457 struct prefix *p = ifc->address;
458
459 if (p->family != AF_INET)
460 continue;
461
462 pim_if_addr_add(ifc);
463 }
464}
465
466void pim_if_addr_del_all(struct interface *ifp)
467{
468 struct connected *ifc;
469 struct listnode *node;
470 struct listnode *nextnode;
471
472 /* PIM/IGMP enabled ? */
473 if (!ifp->info)
474 return;
475
476 for (ALL_LIST_ELEMENTS(ifp->connected, node, nextnode, ifc)) {
477 struct prefix *p = ifc->address;
478
479 if (p->family != AF_INET)
480 continue;
481
482 pim_if_addr_del(ifc);
483 }
484}
485
486void pim_if_addr_del_all_igmp(struct interface *ifp)
487{
488 struct connected *ifc;
489 struct listnode *node;
490 struct listnode *nextnode;
491
492 /* PIM/IGMP enabled ? */
493 if (!ifp->info)
494 return;
495
496 for (ALL_LIST_ELEMENTS(ifp->connected, node, nextnode, ifc)) {
497 struct prefix *p = ifc->address;
498
499 if (p->family != AF_INET)
500 continue;
501
502 pim_if_addr_del_igmp(ifc);
503 }
504}
505
506void pim_if_addr_del_all_pim(struct interface *ifp)
507{
508 struct connected *ifc;
509 struct listnode *node;
510 struct listnode *nextnode;
511
512 /* PIM/IGMP enabled ? */
513 if (!ifp->info)
514 return;
515
516 for (ALL_LIST_ELEMENTS(ifp->connected, node, nextnode, ifc)) {
517 struct prefix *p = ifc->address;
518
519 if (p->family != AF_INET)
520 continue;
521
522 pim_if_addr_del_pim(ifc);
523 }
524}
525
526static struct in_addr find_first_addr(struct interface *ifp)
527{
528 struct connected *ifc;
529 struct listnode *node;
530 struct in_addr addr;
531
532 for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, ifc)) {
533 struct prefix *p = ifc->address;
534
535 if (p->family != AF_INET)
536 continue;
537
538 if (PIM_INADDR_IS_ANY(p->u.prefix4)) {
539 zlog_warn("%s: null IPv4 address connected to interface %s",
540 __PRETTY_FUNCTION__, ifp->name);
541 continue;
542 }
543
544 return p->u.prefix4;
545 }
546
547 addr.s_addr = PIM_NET_INADDR_ANY;
548
549 return addr;
550}
551
552struct in_addr pim_find_primary_addr(struct interface *ifp)
553{
554 return find_first_addr(ifp);
555}
556
557/*
558 pim_if_add_vif() uses ifindex as vif_index
559
560 see also pim_if_find_vifindex_by_ifindex()
561 */
562int pim_if_add_vif(struct interface *ifp)
563{
564 struct pim_interface *pim_ifp = ifp->info;
565 struct in_addr ifaddr;
566
567 zassert(pim_ifp);
568
569 if (pim_ifp->mroute_vif_index > 0) {
570 zlog_warn("%s: vif_index=%d > 0 on interface %s ifindex=%d",
571 __PRETTY_FUNCTION__,
572 pim_ifp->mroute_vif_index, ifp->name, ifp->ifindex);
573 return -1;
574 }
575
576 if (ifp->ifindex < 1) {
577 zlog_warn("%s: ifindex=%d < 1 on interface %s",
578 __PRETTY_FUNCTION__,
579 ifp->ifindex, ifp->name);
580 return -2;
581 }
582
583 if (ifp->ifindex >= MAXVIFS) {
584 zlog_warn("%s: ifindex=%d >= MAXVIFS=%d on interface %s",
585 __PRETTY_FUNCTION__,
586 ifp->ifindex, MAXVIFS, ifp->name);
587 return -3;
588 }
589
590 ifaddr = pim_ifp->primary_address;
591 if (PIM_INADDR_IS_ANY(ifaddr)) {
592 zlog_warn("%s: could not get address for interface %s ifindex=%d",
593 __PRETTY_FUNCTION__,
594 ifp->name, ifp->ifindex);
595 return -4;
596 }
597
598 if (pim_mroute_add_vif(ifp->ifindex, ifaddr)) {
599 /* pim_mroute_add_vif reported error */
600 return -5;
601 }
602
603 pim_ifp->mroute_vif_index = ifp->ifindex;
604
605 /*
606 Update highest vif_index
607 */
608 if (pim_ifp->mroute_vif_index > qpim_mroute_oif_highest_vif_index) {
609 qpim_mroute_oif_highest_vif_index = pim_ifp->mroute_vif_index;
610 }
611
612 return 0;
613}
614
615static int iflist_find_highest_vif_index()
616{
617 struct listnode *ifnode;
618 struct interface *ifp;
619 struct pim_interface *pim_ifp;
620 int highest_vif_index = -1;
621
622 for (ALL_LIST_ELEMENTS_RO(iflist, ifnode, ifp)) {
623 pim_ifp = ifp->info;
624 if (!pim_ifp)
625 continue;
626
627 if (pim_ifp->mroute_vif_index > highest_vif_index) {
628 highest_vif_index = pim_ifp->mroute_vif_index;
629 }
630 }
631
632 return highest_vif_index;
633}
634
635int pim_if_del_vif(struct interface *ifp)
636{
637 struct pim_interface *pim_ifp = ifp->info;
638 int old_vif_index;
639
640 if (pim_ifp->mroute_vif_index < 1) {
641 zlog_warn("%s: vif_index=%d < 1 on interface %s ifindex=%d",
642 __PRETTY_FUNCTION__,
643 pim_ifp->mroute_vif_index, ifp->name, ifp->ifindex);
644 return -1;
645 }
646
647 if (pim_mroute_del_vif(pim_ifp->mroute_vif_index)) {
648 /* pim_mroute_del_vif reported error */
649 return -2;
650 }
651
652 /*
653 Update highest vif_index
654 */
655
656 /* save old vif_index in order to compare with highest below */
657 old_vif_index = pim_ifp->mroute_vif_index;
658
659 pim_ifp->mroute_vif_index = -1;
660
661 if (old_vif_index == qpim_mroute_oif_highest_vif_index) {
662 qpim_mroute_oif_highest_vif_index = iflist_find_highest_vif_index();
663 }
664
665 return 0;
666}
667
668void pim_if_add_vif_all()
669{
670 struct listnode *ifnode;
671 struct listnode *ifnextnode;
672 struct interface *ifp;
673
674 for (ALL_LIST_ELEMENTS(iflist, ifnode, ifnextnode, ifp)) {
675 if (!ifp->info)
676 continue;
677
678 pim_if_add_vif(ifp);
679 }
680}
681
682void pim_if_del_vif_all()
683{
684 struct listnode *ifnode;
685 struct listnode *ifnextnode;
686 struct interface *ifp;
687
688 for (ALL_LIST_ELEMENTS(iflist, ifnode, ifnextnode, ifp)) {
689 if (!ifp->info)
690 continue;
691
692 pim_if_del_vif(ifp);
693 }
694}
695
696struct interface *pim_if_find_by_vif_index(int vif_index)
697{
698 struct listnode *ifnode;
699 struct interface *ifp;
700
701 for (ALL_LIST_ELEMENTS_RO(iflist, ifnode, ifp)) {
702 if (ifp->info) {
703 struct pim_interface *pim_ifp;
704 pim_ifp = ifp->info;
705 if (vif_index == pim_ifp->mroute_vif_index)
706 return ifp;
707 }
708 }
709
710 return 0;
711}
712
713/*
714 pim_if_add_vif() uses ifindex as vif_index
715 */
716int pim_if_find_vifindex_by_ifindex(int ifindex)
717{
718 return ifindex;
719}
720
721int pim_if_lan_delay_enabled(struct interface *ifp)
722{
723 struct pim_interface *pim_ifp;
724
725 pim_ifp = ifp->info;
726 zassert(pim_ifp);
727 zassert(pim_ifp->pim_number_of_nonlandelay_neighbors >= 0);
728
729 return pim_ifp->pim_number_of_nonlandelay_neighbors == 0;
730}
731
732uint16_t pim_if_effective_propagation_delay_msec(struct interface *ifp)
733{
734 if (pim_if_lan_delay_enabled(ifp)) {
735 struct pim_interface *pim_ifp;
736 pim_ifp = ifp->info;
737 return pim_ifp->pim_neighbors_highest_propagation_delay_msec;
738 }
739 else {
740 return PIM_DEFAULT_PROPAGATION_DELAY_MSEC;
741 }
742}
743
744uint16_t pim_if_effective_override_interval_msec(struct interface *ifp)
745{
746 if (pim_if_lan_delay_enabled(ifp)) {
747 struct pim_interface *pim_ifp;
748 pim_ifp = ifp->info;
749 return pim_ifp->pim_neighbors_highest_override_interval_msec;
750 }
751 else {
752 return PIM_DEFAULT_OVERRIDE_INTERVAL_MSEC;
753 }
754}
755
756int pim_if_t_override_msec(struct interface *ifp)
757{
758 int effective_override_interval_msec;
759 int t_override_msec;
760
761 effective_override_interval_msec =
762 pim_if_effective_override_interval_msec(ifp);
763
764 t_override_msec = pim_rand_next(0, effective_override_interval_msec);
765
766 return t_override_msec;
767}
768
769uint16_t pim_if_jp_override_interval_msec(struct interface *ifp)
770{
771 return pim_if_effective_propagation_delay_msec(ifp) +
772 pim_if_effective_override_interval_msec(ifp);
773}
774
775/*
776 RFC 4601: 4.1.6. State Summarization Macros
777
778 The function NBR( I, A ) uses information gathered through PIM Hello
779 messages to map the IP address A of a directly connected PIM
780 neighbor router on interface I to the primary IP address of the same
781 router (Section 4.3.4). The primary IP address of a neighbor is the
782 address that it uses as the source of its PIM Hello messages.
783*/
784struct pim_neighbor *pim_if_find_neighbor(struct interface *ifp,
785 struct in_addr addr)
786{
787 struct listnode *neighnode;
788 struct pim_neighbor *neigh;
789 struct pim_interface *pim_ifp;
790
791 zassert(ifp);
792
793 pim_ifp = ifp->info;
794 if (!pim_ifp) {
795 zlog_warn("%s: multicast not enabled on interface %s",
796 __PRETTY_FUNCTION__,
797 ifp->name);
798 return 0;
799 }
800
801 for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_neighbor_list, neighnode, neigh)) {
802
803 /* primary address ? */
804 if (neigh->source_addr.s_addr == addr.s_addr)
805 return neigh;
806
807 /* secondary address ? */
808 if (pim_neighbor_find_secondary(neigh, addr))
809 return neigh;
810 }
811
812 if (PIM_DEBUG_PIM_TRACE) {
813 char addr_str[100];
814 pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
815 zlog_debug("%s: neighbor not found for address %s on interface %s",
816 __PRETTY_FUNCTION__,
817 addr_str, ifp->name);
818 }
819
820 return 0;
821}
822
823long pim_if_t_suppressed_msec(struct interface *ifp)
824{
825 struct pim_interface *pim_ifp;
826 long t_suppressed_msec;
827
828 pim_ifp = ifp->info;
829 zassert(pim_ifp);
830
831 /* join suppression disabled ? */
832 if (PIM_IF_TEST_PIM_CAN_DISABLE_JOIN_SUPRESSION(pim_ifp->options))
833 return 0;
834
835 /* t_suppressed = t_periodic * rand(1.1, 1.4) */
836
837 t_suppressed_msec = qpim_t_periodic * pim_rand_next(1100, 1400);
838
839 return t_suppressed_msec;
840}
841
842static void igmp_join_free(struct igmp_join *ij)
843{
844 XFREE(MTYPE_PIM_IGMP_JOIN, ij);
845}
846
847static struct igmp_join *igmp_join_find(struct list *join_list,
848 struct in_addr group_addr,
849 struct in_addr source_addr)
850{
851 struct listnode *node;
852 struct igmp_join *ij;
853
854 zassert(join_list);
855
856 for (ALL_LIST_ELEMENTS_RO(join_list, node, ij)) {
857 if ((group_addr.s_addr == ij->group_addr.s_addr) &&
858 (source_addr.s_addr == ij->source_addr.s_addr))
859 return ij;
860 }
861
862 return 0;
863}
864
865static int igmp_join_sock(const char *ifname,
866 int ifindex,
867 struct in_addr group_addr,
868 struct in_addr source_addr)
869{
870 int join_fd;
871
872 join_fd = pim_socket_raw(IPPROTO_IGMP);
873 if (join_fd < 0) {
874 return -1;
875 }
876
877 if (pim_socket_join_source(join_fd, ifindex, group_addr, source_addr, ifname)) {
878 close(join_fd);
879 return -2;
880 }
881
882 return join_fd;
883}
884
885static struct igmp_join *igmp_join_new(struct interface *ifp,
886 struct in_addr group_addr,
887 struct in_addr source_addr)
888{
889 struct pim_interface *pim_ifp;
890 struct igmp_join *ij;
891 int join_fd;
892
893 pim_ifp = ifp->info;
894 zassert(pim_ifp);
895
896 join_fd = igmp_join_sock(ifp->name, ifp->ifindex, group_addr, source_addr);
897 if (join_fd < 0) {
898 char group_str[100];
899 char source_str[100];
900 pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str));
901 pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
902 zlog_warn("%s: igmp_join_sock() failure for IGMP group %s source %s on interface %s",
903 __PRETTY_FUNCTION__,
904 group_str, source_str, ifp->name);
905 return 0;
906 }
907
908 ij = XMALLOC(MTYPE_PIM_IGMP_JOIN, sizeof(*ij));
909 if (!ij) {
910 char group_str[100];
911 char source_str[100];
912 pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str));
913 pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
914 zlog_err("%s: XMALLOC(%d) failure for IGMP group %s source %s on interface %s",
915 __PRETTY_FUNCTION__,
916 sizeof(*ij), group_str, source_str, ifp->name);
917 close(join_fd);
918 return 0;
919 }
920
Everton Marques567f9272010-02-19 19:07:00 -0200921 ij->sock_fd = join_fd;
922 ij->group_addr = group_addr;
923 ij->source_addr = source_addr;
924 ij->sock_creation = pim_time_monotonic_sec();
Everton Marques871dbcf2009-08-11 15:43:05 -0300925
926 listnode_add(pim_ifp->igmp_join_list, ij);
927
928 return ij;
929}
930
931int pim_if_igmp_join_add(struct interface *ifp,
932 struct in_addr group_addr,
933 struct in_addr source_addr)
934{
935 struct pim_interface *pim_ifp;
936 struct igmp_join *ij;
937
938 pim_ifp = ifp->info;
939 if (!pim_ifp) {
940 zlog_warn("%s: multicast not enabled on interface %s",
941 __PRETTY_FUNCTION__,
942 ifp->name);
943 return -1;
944 }
945
946 if (!pim_ifp->igmp_join_list) {
947 pim_ifp->igmp_join_list = list_new();
948 if (!pim_ifp->igmp_join_list) {
949 zlog_err("%s %s: failure: igmp_join_list=list_new()",
950 __FILE__, __PRETTY_FUNCTION__);
951 return -2;
952 }
953 pim_ifp->igmp_join_list->del = (void (*)(void *)) igmp_join_free;
954 }
955
956 ij = igmp_join_find(pim_ifp->igmp_join_list, group_addr, source_addr);
957 if (ij) {
958 char group_str[100];
959 char source_str[100];
960 pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str));
961 pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
962 zlog_warn("%s: can't re-join existing IGMP group %s source %s on interface %s",
963 __PRETTY_FUNCTION__,
964 group_str, source_str, ifp->name);
965 return -3;
966 }
967
968 ij = igmp_join_new(ifp, group_addr, source_addr);
969 if (!ij) {
970 char group_str[100];
971 char source_str[100];
972 pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str));
973 pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
974 zlog_warn("%s: igmp_join_new() failure for IGMP group %s source %s on interface %s",
975 __PRETTY_FUNCTION__,
976 group_str, source_str, ifp->name);
977 return -4;
978 }
979
Everton Marques567f9272010-02-19 19:07:00 -0200980 {
981 char group_str[100];
982 char source_str[100];
983 pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str));
984 pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
Everton Marques9986fb32010-02-22 09:09:09 -0300985 zlog_debug("%s: issued static igmp join for channel (S,G)=(%s,%s) on interface %s",
986 __PRETTY_FUNCTION__,
987 source_str, group_str, ifp->name);
Everton Marques567f9272010-02-19 19:07:00 -0200988 }
989
Everton Marques871dbcf2009-08-11 15:43:05 -0300990 return 0;
991}
992
993
994
995int pim_if_igmp_join_del(struct interface *ifp,
996 struct in_addr group_addr,
997 struct in_addr source_addr)
998{
999 struct pim_interface *pim_ifp;
1000 struct igmp_join *ij;
1001
1002 pim_ifp = ifp->info;
1003 if (!pim_ifp) {
1004 zlog_warn("%s: multicast not enabled on interface %s",
1005 __PRETTY_FUNCTION__,
1006 ifp->name);
1007 return -1;
1008 }
1009
1010 if (!pim_ifp->igmp_join_list) {
1011 zlog_warn("%s: no IGMP join on interface %s",
1012 __PRETTY_FUNCTION__,
1013 ifp->name);
1014 return -2;
1015 }
1016
1017 ij = igmp_join_find(pim_ifp->igmp_join_list, group_addr, source_addr);
1018 if (!ij) {
1019 char group_str[100];
1020 char source_str[100];
1021 pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str));
1022 pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
1023 zlog_warn("%s: could not find IGMP group %s source %s on interface %s",
1024 __PRETTY_FUNCTION__,
1025 group_str, source_str, ifp->name);
1026 return -3;
1027 }
1028
1029 if (close(ij->sock_fd)) {
1030 int e = errno;
1031 char group_str[100];
1032 char source_str[100];
1033 pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str));
1034 pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
1035 zlog_warn("%s: failure closing sock_fd=%d for IGMP group %s source %s on interface %s: errno=%d: %s",
1036 __PRETTY_FUNCTION__,
Everton Marquese96f0af2009-08-11 15:48:02 -03001037 ij->sock_fd, group_str, source_str, ifp->name, e, safe_strerror(e));
Everton Marques871dbcf2009-08-11 15:43:05 -03001038 /* warning only */
1039 }
1040 listnode_delete(pim_ifp->igmp_join_list, ij);
1041 igmp_join_free(ij);
1042 if (listcount(pim_ifp->igmp_join_list) < 1) {
1043 list_delete(pim_ifp->igmp_join_list);
1044 pim_ifp->igmp_join_list = 0;
1045 }
1046
1047 return 0;
1048}
1049
1050static void pim_if_igmp_join_del_all(struct interface *ifp)
1051{
1052 struct pim_interface *pim_ifp;
1053 struct listnode *node;
1054 struct listnode *nextnode;
1055 struct igmp_join *ij;
1056
1057 pim_ifp = ifp->info;
1058 if (!pim_ifp) {
1059 zlog_warn("%s: multicast not enabled on interface %s",
1060 __PRETTY_FUNCTION__,
1061 ifp->name);
1062 return;
1063 }
1064
1065 if (!pim_ifp->igmp_join_list)
1066 return;
1067
1068 for (ALL_LIST_ELEMENTS(pim_ifp->igmp_join_list, node, nextnode, ij))
1069 pim_if_igmp_join_del(ifp, ij->group_addr, ij->source_addr);
1070}
1071
1072/*
1073 RFC 4601
1074
1075 Transitions from "I am Assert Loser" State
1076
1077 Current Winner's GenID Changes or NLT Expires
1078
1079 The Neighbor Liveness Timer associated with the current winner
1080 expires or we receive a Hello message from the current winner
1081 reporting a different GenID from the one it previously reported.
1082 This indicates that the current winner's interface or router has
1083 gone down (and may have come back up), and so we must assume it no
1084 longer knows it was the winner.
1085 */
1086void pim_if_assert_on_neighbor_down(struct interface *ifp,
1087 struct in_addr neigh_addr)
1088{
1089 struct pim_interface *pim_ifp;
1090 struct listnode *node;
1091 struct listnode *next_node;
1092 struct pim_ifchannel *ch;
1093
1094 pim_ifp = ifp->info;
1095 zassert(pim_ifp);
1096
1097 for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, node, next_node, ch)) {
1098 /* Is (S,G,I) assert loser ? */
1099 if (ch->ifassert_state != PIM_IFASSERT_I_AM_LOSER)
1100 continue;
1101 /* Dead neighbor was winner ? */
1102 if (ch->ifassert_winner.s_addr != neigh_addr.s_addr)
1103 continue;
1104
1105 assert_action_a5(ch);
1106 }
1107}
1108
1109void pim_if_update_join_desired(struct pim_interface *pim_ifp)
1110{
1111 struct listnode *ch_node;
1112 struct pim_ifchannel *ch;
1113
1114 /* clear off flag from interface's upstreams */
1115 for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_ifchannel_list, ch_node, ch)) {
1116 PIM_UPSTREAM_FLAG_UNSET_DR_JOIN_DESIRED_UPDATED(ch->upstream->flags);
1117 }
1118
1119 /* scan per-interface (S,G,I) state on this I interface */
1120 for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_ifchannel_list, ch_node, ch)) {
1121 struct pim_upstream *up = ch->upstream;
1122
1123 if (PIM_UPSTREAM_FLAG_TEST_DR_JOIN_DESIRED_UPDATED(up->flags))
1124 continue;
1125
1126 /* update join_desired for the global (S,G) state */
1127 pim_upstream_update_join_desired(up);
1128 PIM_UPSTREAM_FLAG_SET_DR_JOIN_DESIRED_UPDATED(up->flags);
1129 }
1130}
1131
1132void pim_if_update_assert_tracking_desired(struct interface *ifp)
1133{
1134 struct pim_interface *pim_ifp;
1135 struct listnode *node;
1136 struct listnode *next_node;
1137 struct pim_ifchannel *ch;
1138
1139 pim_ifp = ifp->info;
1140 if (!pim_ifp)
1141 return;
1142
1143 for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, node, next_node, ch)) {
1144 pim_ifchannel_update_assert_tracking_desired(ch);
1145 }
1146}