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