3 * Copyright (C) 2008 Everton da Silva Marques
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.
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.
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
28 #include "lib_errors.h"
32 #include "pim_mroute.h"
36 #include "pim_iface.h"
37 #include "pim_macro.h"
40 #include "pim_register.h"
41 #include "pim_ifchannel.h"
42 #include "pim_zlookup.h"
45 #include "pim_vxlan.h"
47 static void mroute_read_on(struct pim_instance
*pim
);
49 static int pim_mroute_set(struct pim_instance
*pim
, int enable
)
53 socklen_t data_len
= sizeof(data
);
57 * We need to create the VRF table for the pim mroute_socket
59 if (pim
->vrf_id
!= VRF_DEFAULT
) {
60 frr_with_privs(&pimd_privs
) {
62 data
= pim
->vrf
->data
.l
.table_id
;
63 err
= setsockopt(pim
->mroute_socket
, IPPROTO_IP
,
68 "%s %s: failure: setsockopt(fd=%d,IPPROTO_IP, MRT_TABLE=%d): errno=%d: %s",
69 __FILE__
, __PRETTY_FUNCTION__
,
70 pim
->mroute_socket
, data
, errno
,
71 safe_strerror(errno
));
78 frr_with_privs(&pimd_privs
) {
79 opt
= enable
? MRT_INIT
: MRT_DONE
;
81 * *BSD *cares* about what value we pass down
85 err
= setsockopt(pim
->mroute_socket
, IPPROTO_IP
,
86 opt
, &data
, data_len
);
89 "%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,%s=%d): errno=%d: %s",
90 __FILE__
, __PRETTY_FUNCTION__
,
92 enable
? "MRT_INIT" : "MRT_DONE", data
, errno
,
93 safe_strerror(errno
));
98 #if defined(HAVE_IP_PKTINFO)
100 /* Linux and Solaris IP_PKTINFO */
102 if (setsockopt(pim
->mroute_socket
, IPPROTO_IP
, IP_PKTINFO
,
105 "Could not set IP_PKTINFO on socket fd=%d: errno=%d: %s",
106 pim
->mroute_socket
, errno
,
107 safe_strerror(errno
));
112 setsockopt_so_recvbuf(pim
->mroute_socket
, 1024 * 1024 * 8);
114 flags
= fcntl(pim
->mroute_socket
, F_GETFL
, 0);
116 zlog_warn("Could not get flags on socket fd:%d %d %s",
117 pim
->mroute_socket
, errno
, safe_strerror(errno
));
118 close(pim
->mroute_socket
);
121 if (fcntl(pim
->mroute_socket
, F_SETFL
, flags
| O_NONBLOCK
)) {
122 zlog_warn("Could not set O_NONBLOCK on socket fd:%d %d %s",
123 pim
->mroute_socket
, errno
, safe_strerror(errno
));
124 close(pim
->mroute_socket
);
130 int upcalls
= IGMPMSG_WRVIFWHOLE
;
133 err
= setsockopt(pim
->mroute_socket
, IPPROTO_IP
, opt
, &upcalls
,
137 "Failure to register for VIFWHOLE and WRONGVIF upcalls %d %s",
138 errno
, safe_strerror(errno
));
143 "PIM-SM will not work properly on this platform, until the ability to receive the WRVIFWHOLE upcall");
150 static const char *const igmpmsgtype2str
[IGMPMSG_WRVIFWHOLE
+ 1] = {
151 "<unknown_upcall?>", "NOCACHE", "WRONGVIF", "WHOLEPKT", "WRVIFWHOLE"};
153 static int pim_mroute_msg_nocache(int fd
, struct interface
*ifp
,
154 const struct igmpmsg
*msg
)
156 struct pim_interface
*pim_ifp
= ifp
->info
;
157 struct pim_upstream
*up
;
161 rpg
= pim_ifp
? RP(pim_ifp
->pim
, msg
->im_dst
) : NULL
;
163 * If the incoming interface is unknown OR
164 * the Interface type is SSM we don't need to
167 if (!rpg
|| pim_rpf_addr_is_inaddr_none(rpg
)) {
168 if (PIM_DEBUG_MROUTE_DETAIL
)
170 "%s: Interface is not configured correctly to handle incoming packet: Could be !pim_ifp, !SM, !RP",
171 __PRETTY_FUNCTION__
);
177 * If we've received a multicast packet that isn't connected to
180 if (!pim_if_connected_to_source(ifp
, msg
->im_src
)) {
181 if (PIM_DEBUG_MROUTE_DETAIL
)
183 "%s: Received incoming packet that doesn't originate on our seg",
184 __PRETTY_FUNCTION__
);
188 memset(&sg
, 0, sizeof(struct prefix_sg
));
189 sg
.src
= msg
->im_src
;
190 sg
.grp
= msg
->im_dst
;
192 if (!(PIM_I_am_DR(pim_ifp
))) {
193 if (PIM_DEBUG_MROUTE_DETAIL
)
194 zlog_debug("%s: Interface is not the DR blackholing incoming traffic for %s",
195 __PRETTY_FUNCTION__
, pim_str_sg_dump(&sg
));
198 * We are not the DR, but we are still receiving packets
199 * Let's blackhole those packets for the moment
200 * As that they will be coming up to the cpu
201 * and causing us to consider them.
203 * This *will* create a dangling channel_oil
204 * that I see no way to get rid of. Just noting
205 * this for future reference.
207 up
= pim_upstream_find_or_add(
208 &sg
, ifp
, PIM_UPSTREAM_FLAG_MASK_SRC_NOCACHE
,
209 __PRETTY_FUNCTION__
);
210 pim_upstream_mroute_add(up
->channel_oil
, __PRETTY_FUNCTION__
);
215 up
= pim_upstream_find_or_add(&sg
, ifp
, PIM_UPSTREAM_FLAG_MASK_FHR
,
216 __PRETTY_FUNCTION__
);
219 * I moved this debug till after the actual add because
220 * I want to take advantage of the up->sg_str being filled in.
222 if (PIM_DEBUG_MROUTE
) {
223 zlog_debug("%s: Adding a Route %s for WHOLEPKT consumption",
224 __PRETTY_FUNCTION__
, up
->sg_str
);
227 PIM_UPSTREAM_FLAG_SET_SRC_STREAM(up
->flags
);
228 pim_upstream_keep_alive_timer_start(up
, pim_ifp
->pim
->keep_alive_time
);
230 up
->channel_oil
->cc
.pktcnt
++;
231 // resolve mfcc_parent prior to mroute_add in channel_add_oif
232 if (up
->rpf
.source_nexthop
.interface
&&
233 up
->channel_oil
->oil
.mfcc_parent
>= MAXVIFS
) {
234 pim_upstream_mroute_iif_update(up
->channel_oil
, __func__
);
236 pim_register_join(up
);
241 static int pim_mroute_msg_wholepkt(int fd
, struct interface
*ifp
,
244 struct pim_interface
*pim_ifp
;
247 const struct ip
*ip_hdr
;
248 struct pim_upstream
*up
;
252 ip_hdr
= (const struct ip
*)buf
;
254 memset(&sg
, 0, sizeof(struct prefix_sg
));
255 sg
.src
= ip_hdr
->ip_src
;
256 sg
.grp
= ip_hdr
->ip_dst
;
258 up
= pim_upstream_find(pim_ifp
->pim
, &sg
);
260 struct prefix_sg star
= sg
;
261 star
.src
.s_addr
= INADDR_ANY
;
263 up
= pim_upstream_find(pim_ifp
->pim
, &star
);
265 if (up
&& PIM_UPSTREAM_FLAG_TEST_SRC_IGMP(up
->flags
)) {
266 up
= pim_upstream_add(pim_ifp
->pim
, &sg
, ifp
,
267 PIM_UPSTREAM_FLAG_MASK_SRC_LHR
,
268 __PRETTY_FUNCTION__
, NULL
);
270 if (PIM_DEBUG_MROUTE
)
272 "%s: Unable to create upstream information for %s",
274 pim_str_sg_dump(&sg
));
277 pim_upstream_keep_alive_timer_start(
278 up
, pim_ifp
->pim
->keep_alive_time
);
279 pim_upstream_inherited_olist(pim_ifp
->pim
, up
);
280 pim_upstream_update_join_desired(pim_ifp
->pim
, up
);
282 if (PIM_DEBUG_MROUTE
)
283 zlog_debug("%s: Creating %s upstream on LHR",
284 __PRETTY_FUNCTION__
, up
->sg_str
);
287 if (PIM_DEBUG_MROUTE_DETAIL
) {
289 "%s: Unable to find upstream channel WHOLEPKT%s",
290 __PRETTY_FUNCTION__
, pim_str_sg_dump(&sg
));
295 if (!up
->rpf
.source_nexthop
.interface
) {
296 if (PIM_DEBUG_PIM_TRACE
)
297 zlog_debug("%s: up %s RPF is not present",
298 __PRETTY_FUNCTION__
, up
->sg_str
);
302 pim_ifp
= up
->rpf
.source_nexthop
.interface
->info
;
304 rpg
= pim_ifp
? RP(pim_ifp
->pim
, sg
.grp
) : NULL
;
306 if ((pim_rpf_addr_is_inaddr_none(rpg
)) || (!pim_ifp
)
307 || (!(PIM_I_am_DR(pim_ifp
)))) {
308 if (PIM_DEBUG_MROUTE
) {
309 zlog_debug("%s: Failed Check send packet",
310 __PRETTY_FUNCTION__
);
316 * If we've received a register suppress
318 if (!up
->t_rs_timer
) {
319 if (pim_is_grp_ssm(pim_ifp
->pim
, sg
.grp
)) {
320 if (PIM_DEBUG_PIM_REG
)
322 "%s register forward skipped as group is SSM",
323 pim_str_sg_dump(&sg
));
327 if (!PIM_UPSTREAM_FLAG_TEST_FHR(up
->flags
)) {
328 if (PIM_DEBUG_PIM_REG
)
330 "%s register forward skipped, not FHR",
335 pim_register_send((uint8_t *)buf
+ sizeof(struct ip
),
336 ntohs(ip_hdr
->ip_len
) - sizeof(struct ip
),
337 pim_ifp
->primary_address
, rpg
, 0, up
);
342 static int pim_mroute_msg_wrongvif(int fd
, struct interface
*ifp
,
343 const struct igmpmsg
*msg
)
345 struct pim_ifchannel
*ch
;
346 struct pim_interface
*pim_ifp
;
349 memset(&sg
, 0, sizeof(struct prefix_sg
));
350 sg
.src
= msg
->im_src
;
351 sg
.grp
= msg
->im_dst
;
354 Send Assert(S,G) on iif as response to WRONGVIF kernel upcall.
356 RFC 4601 4.8.2. PIM-SSM-Only Routers
358 iif is the incoming interface of the packet.
359 if (iif is in inherited_olist(S,G)) {
360 send Assert(S,G) on iif
365 if (PIM_DEBUG_MROUTE
)
367 "%s: WRONGVIF (S,G)=%s could not find input interface for input_vif_index=%d",
368 __PRETTY_FUNCTION__
, pim_str_sg_dump(&sg
),
375 if (PIM_DEBUG_MROUTE
)
377 "%s: WRONGVIF (S,G)=%s multicast not enabled on interface %s",
378 __PRETTY_FUNCTION__
, pim_str_sg_dump(&sg
),
383 ch
= pim_ifchannel_find(ifp
, &sg
);
385 struct prefix_sg star_g
= sg
;
386 if (PIM_DEBUG_MROUTE
)
388 "%s: WRONGVIF (S,G)=%s could not find channel on interface %s",
389 __PRETTY_FUNCTION__
, pim_str_sg_dump(&sg
),
392 star_g
.src
.s_addr
= INADDR_ANY
;
393 ch
= pim_ifchannel_find(ifp
, &star_g
);
395 if (PIM_DEBUG_MROUTE
)
397 "%s: WRONGVIF (*,G)=%s could not find channel on interface %s",
399 pim_str_sg_dump(&star_g
), ifp
->name
);
405 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
407 Transitions from NoInfo State
409 An (S,G) data packet arrives on interface I, AND
410 CouldAssert(S,G,I)==TRUE An (S,G) data packet arrived on an
411 downstream interface that is in our (S,G) outgoing interface
412 list. We optimistically assume that we will be the assert
413 winner for this (S,G), and so we transition to the "I am Assert
414 Winner" state and perform Actions A1 (below), which will
415 initiate the assert negotiation for (S,G).
418 if (ch
->ifassert_state
!= PIM_IFASSERT_NOINFO
) {
419 if (PIM_DEBUG_MROUTE
) {
421 "%s: WRONGVIF (S,G)=%s channel is not on Assert NoInfo state for interface %s",
422 __PRETTY_FUNCTION__
, ch
->sg_str
, ifp
->name
);
427 if (!PIM_IF_FLAG_TEST_COULD_ASSERT(ch
->flags
)) {
428 if (PIM_DEBUG_MROUTE
) {
430 "%s: WRONGVIF (S,G)=%s interface %s is not downstream for channel",
431 __PRETTY_FUNCTION__
, ch
->sg_str
, ifp
->name
);
436 if (assert_action_a1(ch
)) {
437 if (PIM_DEBUG_MROUTE
) {
439 "%s: WRONGVIF (S,G)=%s assert_action_a1 failure on interface %s",
440 __PRETTY_FUNCTION__
, ch
->sg_str
, ifp
->name
);
448 static int pim_mroute_msg_wrvifwhole(int fd
, struct interface
*ifp
,
451 const struct ip
*ip_hdr
= (const struct ip
*)buf
;
452 struct pim_interface
*pim_ifp
;
453 struct pim_ifchannel
*ch
;
454 struct pim_upstream
*up
;
455 struct prefix_sg star_g
;
460 memset(&sg
, 0, sizeof(struct prefix_sg
));
461 sg
.src
= ip_hdr
->ip_src
;
462 sg
.grp
= ip_hdr
->ip_dst
;
464 ch
= pim_ifchannel_find(ifp
, &sg
);
466 if (PIM_DEBUG_MROUTE
)
468 "WRVIFWHOLE (S,G)=%s found ifchannel on interface %s",
469 ch
->sg_str
, ifp
->name
);
474 star_g
.src
.s_addr
= INADDR_ANY
;
476 ch
= pim_ifchannel_find(ifp
, &star_g
);
479 if (PIM_DEBUG_MROUTE
)
480 zlog_debug ("WRVIFWHOLE (*,G)=%s found ifchannel on interface %s",
481 pim_str_sg_dump (&star_g
), ifp
->name
);
486 up
= pim_upstream_find(pim_ifp
->pim
, &sg
);
488 struct pim_upstream
*parent
;
489 struct pim_nexthop source
;
490 struct pim_rpf
*rpf
= RP(pim_ifp
->pim
, sg
.grp
);
491 if (!rpf
|| !rpf
->source_nexthop
.interface
)
495 * If we have received a WRVIFWHOLE and are at this
496 * point, we could be receiving the packet on the *,G
497 * tree, let's check and if so we can safely drop
500 parent
= pim_upstream_find(pim_ifp
->pim
, &star_g
);
501 if (parent
&& parent
->rpf
.source_nexthop
.interface
== ifp
)
504 pim_ifp
= rpf
->source_nexthop
.interface
->info
;
506 memset(&source
, 0, sizeof(source
));
508 * If we are the fhr that means we are getting a callback during
509 * the pimreg period, so I believe we can ignore this packet
511 if (!PIM_UPSTREAM_FLAG_TEST_FHR(up
->flags
)) {
512 // No if channel, but upstream we are at the RP.
513 if (pim_nexthop_lookup(pim_ifp
->pim
, &source
,
514 up
->upstream_register
, 0)) {
515 pim_register_stop_send(source
.interface
, &sg
,
516 pim_ifp
->primary_address
,
517 up
->upstream_register
);
518 up
->sptbit
= PIM_UPSTREAM_SPTBIT_TRUE
;
521 pim_upstream_inherited_olist(pim_ifp
->pim
, up
);
522 if (!up
->channel_oil
->installed
)
523 pim_upstream_mroute_add(up
->channel_oil
,
524 __PRETTY_FUNCTION__
);
526 if (I_am_RP(pim_ifp
->pim
, up
->sg
.grp
)) {
527 if (pim_nexthop_lookup(pim_ifp
->pim
, &source
,
528 up
->upstream_register
,
530 pim_register_stop_send(
531 source
.interface
, &sg
,
532 pim_ifp
->primary_address
,
533 up
->upstream_register
);
534 up
->sptbit
= PIM_UPSTREAM_SPTBIT_TRUE
;
536 pim_upstream_keep_alive_timer_start(
537 up
, pim_ifp
->pim
->keep_alive_time
);
538 pim_upstream_inherited_olist(pim_ifp
->pim
, up
);
539 pim_mroute_msg_wholepkt(fd
, ifp
, buf
);
545 if (pim_if_connected_to_source(ifp
, sg
.src
)) {
546 up
= pim_upstream_add(pim_ifp
->pim
, &sg
, ifp
,
547 PIM_UPSTREAM_FLAG_MASK_FHR
,
548 __PRETTY_FUNCTION__
, NULL
);
550 if (PIM_DEBUG_MROUTE
)
552 "%s: WRONGVIF%s unable to create upstream on interface",
553 pim_str_sg_dump(&sg
), ifp
->name
);
556 PIM_UPSTREAM_FLAG_SET_SRC_STREAM(up
->flags
);
557 pim_upstream_keep_alive_timer_start(
558 up
, pim_ifp
->pim
->keep_alive_time
);
559 up
->channel_oil
->cc
.pktcnt
++;
560 pim_register_join(up
);
561 pim_upstream_inherited_olist(pim_ifp
->pim
, up
);
562 if (!up
->channel_oil
->installed
)
563 pim_upstream_mroute_add(up
->channel_oil
, __func__
);
565 // Send the packet to the RP
566 pim_mroute_msg_wholepkt(fd
, ifp
, buf
);
568 up
= pim_upstream_add(pim_ifp
->pim
, &sg
, ifp
,
569 PIM_UPSTREAM_FLAG_MASK_SRC_NOCACHE
,
570 __PRETTY_FUNCTION__
, NULL
);
571 if (!up
->channel_oil
->installed
)
572 pim_upstream_mroute_add(up
->channel_oil
,
573 __PRETTY_FUNCTION__
);
579 static int pim_mroute_msg(struct pim_instance
*pim
, const char *buf
,
580 int buf_size
, ifindex_t ifindex
)
582 struct interface
*ifp
;
583 struct pim_interface
*pim_ifp
;
584 const struct ip
*ip_hdr
;
585 const struct igmpmsg
*msg
;
586 char ip_src_str
[INET_ADDRSTRLEN
] = "";
587 char ip_dst_str
[INET_ADDRSTRLEN
] = "";
588 char src_str
[INET_ADDRSTRLEN
] = "<src?>";
589 char grp_str
[INET_ADDRSTRLEN
] = "<grp?>";
590 struct in_addr ifaddr
;
591 struct igmp_sock
*igmp
;
593 if (buf_size
< (int)sizeof(struct ip
))
596 ip_hdr
= (const struct ip
*)buf
;
598 if (ip_hdr
->ip_p
== IPPROTO_IGMP
) {
600 /* We have the IP packet but we do not know which interface this
602 * received on. Find the interface that is on the same subnet as
606 ifp
= if_lookup_by_index(ifindex
, pim
->vrf_id
);
608 if (!ifp
|| !ifp
->info
)
612 ifaddr
= pim_find_primary_addr(ifp
);
613 igmp
= pim_igmp_sock_lookup_ifaddr(pim_ifp
->igmp_socket_list
,
616 if (PIM_DEBUG_MROUTE
) {
617 pim_inet4_dump("<src?>", ip_hdr
->ip_src
, ip_src_str
,
619 pim_inet4_dump("<dst?>", ip_hdr
->ip_dst
, ip_dst_str
,
623 "%s(%s): igmp kernel upcall on %s(%p) for %s -> %s",
624 __PRETTY_FUNCTION__
, pim
->vrf
->name
, ifp
->name
,
625 igmp
, ip_src_str
, ip_dst_str
);
628 pim_igmp_packet(igmp
, (char *)buf
, buf_size
);
630 } else if (ip_hdr
->ip_p
) {
631 if (PIM_DEBUG_MROUTE_DETAIL
) {
632 pim_inet4_dump("<src?>", ip_hdr
->ip_src
, src_str
,
634 pim_inet4_dump("<grp?>", ip_hdr
->ip_dst
, grp_str
,
637 "%s: no kernel upcall proto=%d src: %s dst: %s msg_size=%d",
638 __PRETTY_FUNCTION__
, ip_hdr
->ip_p
, src_str
,
643 msg
= (const struct igmpmsg
*)buf
;
645 ifp
= pim_if_find_by_vif_index(pim
, msg
->im_vif
);
649 if (PIM_DEBUG_MROUTE
) {
650 pim_inet4_dump("<src?>", msg
->im_src
, src_str
,
652 pim_inet4_dump("<grp?>", msg
->im_dst
, grp_str
,
655 "%s: pim kernel upcall %s type=%d ip_p=%d from fd=%d for (S,G)=(%s,%s) on %s vifi=%d size=%d",
657 igmpmsgtype2str
[msg
->im_msgtype
],
658 msg
->im_msgtype
, ip_hdr
->ip_p
,
659 pim
->mroute_socket
, src_str
, grp_str
, ifp
->name
,
660 msg
->im_vif
, buf_size
);
663 switch (msg
->im_msgtype
) {
664 case IGMPMSG_WRONGVIF
:
665 return pim_mroute_msg_wrongvif(pim
->mroute_socket
, ifp
,
668 case IGMPMSG_NOCACHE
:
669 return pim_mroute_msg_nocache(pim
->mroute_socket
, ifp
,
672 case IGMPMSG_WHOLEPKT
:
673 return pim_mroute_msg_wholepkt(pim
->mroute_socket
, ifp
,
676 case IGMPMSG_WRVIFWHOLE
:
677 return pim_mroute_msg_wrvifwhole(
678 pim
->mroute_socket
, ifp
, (const char *)msg
);
688 static int mroute_read(struct thread
*t
)
690 struct pim_instance
*pim
;
691 static long long count
;
700 rd
= pim_socket_recvfromto(pim
->mroute_socket
, (uint8_t *)buf
,
701 sizeof(buf
), NULL
, NULL
, NULL
, NULL
,
706 if (errno
== EWOULDBLOCK
|| errno
== EAGAIN
)
709 zlog_warn("%s: failure reading rd=%d: fd=%d: errno=%d: %s",
710 __PRETTY_FUNCTION__
, rd
, pim
->mroute_socket
,
711 errno
, safe_strerror(errno
));
715 result
= pim_mroute_msg(pim
, buf
, rd
, ifindex
);
718 if (count
% router
->packet_process
== 0)
728 static void mroute_read_on(struct pim_instance
*pim
)
730 thread_add_read(router
->master
, mroute_read
, pim
, pim
->mroute_socket
,
734 static void mroute_read_off(struct pim_instance
*pim
)
736 THREAD_OFF(pim
->thread
);
739 int pim_mroute_socket_enable(struct pim_instance
*pim
)
743 frr_with_privs(&pimd_privs
) {
745 fd
= socket(AF_INET
, SOCK_RAW
, IPPROTO_IGMP
);
748 zlog_warn("Could not create mroute socket: errno=%d: %s",
750 safe_strerror(errno
));
754 #ifdef SO_BINDTODEVICE
755 if (pim
->vrf
->vrf_id
!= VRF_DEFAULT
756 && setsockopt(fd
, SOL_SOCKET
, SO_BINDTODEVICE
,
757 pim
->vrf
->name
, strlen(pim
->vrf
->name
))) {
758 zlog_warn("Could not setsockopt SO_BINDTODEVICE: %s",
759 safe_strerror(errno
));
767 pim
->mroute_socket
= fd
;
768 if (pim_mroute_set(pim
, 1)) {
770 "Could not enable mroute on socket fd=%d: errno=%d: %s",
771 fd
, errno
, safe_strerror(errno
));
773 pim
->mroute_socket
= -1;
777 pim
->mroute_socket_creation
= pim_time_monotonic_sec();
784 int pim_mroute_socket_disable(struct pim_instance
*pim
)
786 if (pim_mroute_set(pim
, 0)) {
788 "Could not disable mroute on socket fd=%d: errno=%d: %s",
789 pim
->mroute_socket
, errno
, safe_strerror(errno
));
793 if (close(pim
->mroute_socket
)) {
794 zlog_warn("Failure closing mroute socket: fd=%d errno=%d: %s",
795 pim
->mroute_socket
, errno
, safe_strerror(errno
));
799 mroute_read_off(pim
);
800 pim
->mroute_socket
= -1;
806 For each network interface (e.g., physical or a virtual tunnel) that
807 would be used for multicast forwarding, a corresponding multicast
808 interface must be added to the kernel.
810 int pim_mroute_add_vif(struct interface
*ifp
, struct in_addr ifaddr
,
813 struct pim_interface
*pim_ifp
= ifp
->info
;
817 if (PIM_DEBUG_MROUTE
)
818 zlog_debug("%s: Add Vif %d (%s[%s])", __PRETTY_FUNCTION__
,
819 pim_ifp
->mroute_vif_index
, ifp
->name
,
820 pim_ifp
->pim
->vrf
->name
);
822 memset(&vc
, 0, sizeof(vc
));
823 vc
.vifc_vifi
= pim_ifp
->mroute_vif_index
;
824 #ifdef VIFF_USE_IFINDEX
825 vc
.vifc_lcl_ifindex
= ifp
->ifindex
;
827 if (ifaddr
.s_addr
== INADDR_ANY
) {
829 "%s: unnumbered interfaces are not supported on this platform",
830 __PRETTY_FUNCTION__
);
833 memcpy(&vc
.vifc_lcl_addr
, &ifaddr
, sizeof(vc
.vifc_lcl_addr
));
835 vc
.vifc_flags
= flags
;
836 vc
.vifc_threshold
= PIM_MROUTE_MIN_TTL
;
837 vc
.vifc_rate_limit
= 0;
839 #ifdef PIM_DVMRP_TUNNEL
840 if (vc
.vifc_flags
& VIFF_TUNNEL
) {
841 memcpy(&vc
.vifc_rmt_addr
, &vif_remote_addr
,
842 sizeof(vc
.vifc_rmt_addr
));
846 err
= setsockopt(pim_ifp
->pim
->mroute_socket
, IPPROTO_IP
, MRT_ADD_VIF
,
847 (void *)&vc
, sizeof(vc
));
849 char ifaddr_str
[INET_ADDRSTRLEN
];
851 pim_inet4_dump("<ifaddr?>", ifaddr
, ifaddr_str
,
855 "%s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_ADD_VIF,vif_index=%d,ifaddr=%s,flag=%d): errno=%d: %s",
856 __PRETTY_FUNCTION__
, pim_ifp
->pim
->mroute_socket
,
857 ifp
->ifindex
, ifaddr_str
, flags
, errno
,
858 safe_strerror(errno
));
865 int pim_mroute_del_vif(struct interface
*ifp
)
867 struct pim_interface
*pim_ifp
= ifp
->info
;
871 if (PIM_DEBUG_MROUTE
)
872 zlog_debug("%s: Del Vif %d (%s[%s])", __PRETTY_FUNCTION__
,
873 pim_ifp
->mroute_vif_index
, ifp
->name
,
874 pim_ifp
->pim
->vrf
->name
);
876 memset(&vc
, 0, sizeof(vc
));
877 vc
.vifc_vifi
= pim_ifp
->mroute_vif_index
;
879 err
= setsockopt(pim_ifp
->pim
->mroute_socket
, IPPROTO_IP
, MRT_DEL_VIF
,
880 (void *)&vc
, sizeof(vc
));
883 "%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_DEL_VIF,vif_index=%d): errno=%d: %s",
884 __FILE__
, __PRETTY_FUNCTION__
,
885 pim_ifp
->pim
->mroute_socket
, pim_ifp
->mroute_vif_index
,
886 errno
, safe_strerror(errno
));
894 * Prevent creating MFC entry with OIF=IIF.
896 * This is a protection against implementation mistakes.
898 * PIM protocol implicitely ensures loopfree multicast topology.
900 * IGMP must be protected against adding looped MFC entries created
901 * by both source and receiver attached to the same interface. See
903 * We shall allow igmp to create upstream when it is DR for the intf.
904 * Assume RP reachable via non DR.
906 bool pim_mroute_allow_iif_in_oil(struct channel_oil
*c_oil
,
909 #ifdef PIM_ENFORCE_LOOPFREE_MFC
910 struct interface
*ifp_out
;
911 struct pim_interface
*pim_ifp
;
914 PIM_UPSTREAM_FLAG_TEST_ALLOW_IIF_IN_OIL(c_oil
->up
->flags
))
917 ifp_out
= pim_if_find_by_vif_index(c_oil
->pim
, oif_index
);
920 pim_ifp
= ifp_out
->info
;
923 if ((c_oil
->oif_flags
[oif_index
] & PIM_OIF_FLAG_PROTO_IGMP
) &&
924 PIM_I_am_DR(pim_ifp
))
933 static inline void pim_mroute_copy(struct mfcctl
*oil
,
934 struct channel_oil
*c_oil
)
938 oil
->mfcc_origin
= c_oil
->oil
.mfcc_origin
;
939 oil
->mfcc_mcastgrp
= c_oil
->oil
.mfcc_mcastgrp
;
940 oil
->mfcc_parent
= c_oil
->oil
.mfcc_parent
;
942 for (i
= 0; i
< MAXVIFS
; ++i
) {
943 if ((oil
->mfcc_parent
== i
) &&
944 !pim_mroute_allow_iif_in_oil(c_oil
, i
)) {
945 oil
->mfcc_ttls
[i
] = 0;
949 if (c_oil
->oif_flags
[i
] & PIM_OIF_FLAG_MUTE
)
950 oil
->mfcc_ttls
[i
] = 0;
952 oil
->mfcc_ttls
[i
] = c_oil
->oil
.mfcc_ttls
[i
];
956 /* This function must not be called directly 0
957 * use pim_upstream_mroute_add or pim_static_mroute_add instead
959 static int pim_mroute_add(struct channel_oil
*c_oil
, const char *name
)
961 struct pim_instance
*pim
= c_oil
->pim
;
962 struct mfcctl tmp_oil
;
965 pim
->mroute_add_last
= pim_time_monotonic_sec();
966 ++pim
->mroute_add_events
;
968 /* Copy the oil to a temporary structure to fixup (without need to
969 * later restore) before sending the mroute add to the dataplane
971 pim_mroute_copy(&tmp_oil
, c_oil
);
973 /* The linux kernel *expects* the incoming
974 * vif to be part of the outgoing list
975 * in the case of a (*,G).
977 if (c_oil
->oil
.mfcc_origin
.s_addr
== INADDR_ANY
) {
978 tmp_oil
.mfcc_ttls
[c_oil
->oil
.mfcc_parent
] = 1;
982 * If we have an unresolved cache entry for the S,G
983 * it is owned by the pimreg for the incoming IIF
984 * So set pimreg as the IIF temporarily to cause
985 * the packets to be forwarded. Then set it
986 * to the correct IIF afterwords.
988 if (!c_oil
->installed
&& c_oil
->oil
.mfcc_origin
.s_addr
!= INADDR_ANY
989 && c_oil
->oil
.mfcc_parent
!= 0) {
990 tmp_oil
.mfcc_parent
= 0;
992 err
= setsockopt(pim
->mroute_socket
, IPPROTO_IP
, MRT_ADD_MFC
,
993 &tmp_oil
, sizeof(tmp_oil
));
995 if (!err
&& !c_oil
->installed
996 && c_oil
->oil
.mfcc_origin
.s_addr
!= INADDR_ANY
997 && c_oil
->oil
.mfcc_parent
!= 0) {
998 tmp_oil
.mfcc_parent
= c_oil
->oil
.mfcc_parent
;
999 err
= setsockopt(pim
->mroute_socket
, IPPROTO_IP
, MRT_ADD_MFC
,
1000 &tmp_oil
, sizeof(tmp_oil
));
1005 "%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_ADD_MFC): errno=%d: %s",
1006 __FILE__
, __PRETTY_FUNCTION__
, pim
->mroute_socket
,
1007 errno
, safe_strerror(errno
));
1011 if (PIM_DEBUG_MROUTE
) {
1013 zlog_debug("%s(%s), vrf %s Added Route: %s",
1014 __PRETTY_FUNCTION__
, name
, pim
->vrf
->name
,
1015 pim_channel_oil_dump(c_oil
, buf
, sizeof(buf
)));
1018 c_oil
->installed
= 1;
1019 c_oil
->mroute_creation
= pim_time_monotonic_sec();
1024 static int pim_upstream_get_mroute_iif(struct channel_oil
*c_oil
,
1027 vifi_t iif
= MAXVIFS
;
1028 struct interface
*ifp
= NULL
;
1029 struct pim_interface
*pim_ifp
;
1030 struct pim_upstream
*up
= c_oil
->up
;
1033 if (PIM_UPSTREAM_FLAG_TEST_USE_RPT(up
->flags
)) {
1035 ifp
= up
->parent
->rpf
.source_nexthop
.interface
;
1037 ifp
= up
->rpf
.source_nexthop
.interface
;
1040 pim_ifp
= (struct pim_interface
*)ifp
->info
;
1042 iif
= pim_ifp
->mroute_vif_index
;
1048 static int pim_upstream_mroute_update(struct channel_oil
*c_oil
,
1053 if (c_oil
->oil
.mfcc_parent
>= MAXVIFS
) {
1054 /* the c_oil cannot be installed as a mroute yet */
1055 if (PIM_DEBUG_MROUTE
)
1057 "%s(%s) %s mroute not ready to be installed; %s",
1059 pim_channel_oil_dump(c_oil
, buf
,
1062 "uninstall" : "skip");
1063 /* if already installed flush it out as we are going to stop
1064 * updates to it leaving it in a stale state
1066 if (c_oil
->installed
)
1067 pim_mroute_del(c_oil
, name
);
1068 /* return success (skipped) */
1072 return pim_mroute_add(c_oil
, name
);
1075 /* IIF associated with SGrpt entries are re-evaluated when the parent
1076 * (*,G) entries IIF changes
1078 static void pim_upstream_all_sources_iif_update(struct pim_upstream
*up
)
1080 struct listnode
*listnode
;
1081 struct pim_upstream
*child
;
1083 for (ALL_LIST_ELEMENTS_RO(up
->sources
, listnode
,
1085 if (PIM_UPSTREAM_FLAG_TEST_USE_RPT(child
->flags
))
1086 pim_upstream_mroute_iif_update(child
->channel_oil
,
1091 /* In the case of "PIM state machine" added mroutes an upstream entry
1092 * must be present to decide on the SPT-forwarding vs. RPT-forwarding.
1094 int pim_upstream_mroute_add(struct channel_oil
*c_oil
, const char *name
)
1098 iif
= pim_upstream_get_mroute_iif(c_oil
, name
);
1100 if (c_oil
->oil
.mfcc_parent
!= iif
) {
1101 c_oil
->oil
.mfcc_parent
= iif
;
1102 if (c_oil
->oil
.mfcc_origin
.s_addr
== INADDR_ANY
&&
1104 pim_upstream_all_sources_iif_update(c_oil
->up
);
1106 c_oil
->oil
.mfcc_parent
= iif
;
1109 return pim_upstream_mroute_update(c_oil
, name
);
1112 /* Look for IIF changes and update the dateplane entry only if the IIF
1115 int pim_upstream_mroute_iif_update(struct channel_oil
*c_oil
, const char *name
)
1120 iif
= pim_upstream_get_mroute_iif(c_oil
, name
);
1121 if (c_oil
->oil
.mfcc_parent
== iif
) {
1125 c_oil
->oil
.mfcc_parent
= iif
;
1127 if (c_oil
->oil
.mfcc_origin
.s_addr
== INADDR_ANY
&&
1129 pim_upstream_all_sources_iif_update(c_oil
->up
);
1131 if (PIM_DEBUG_MROUTE_DETAIL
)
1132 zlog_debug("%s(%s) %s mroute iif update %d",
1134 pim_channel_oil_dump(c_oil
, buf
,
1136 /* XXX: is this hack needed? */
1137 c_oil
->oil_inherited_rescan
= 1;
1138 return pim_upstream_mroute_update(c_oil
, name
);
1141 int pim_static_mroute_add(struct channel_oil
*c_oil
, const char *name
)
1143 return pim_mroute_add(c_oil
, name
);
1146 void pim_static_mroute_iif_update(struct channel_oil
*c_oil
,
1147 int input_vif_index
,
1150 if (c_oil
->oil
.mfcc_parent
== input_vif_index
)
1153 c_oil
->oil
.mfcc_parent
= input_vif_index
;
1154 if (input_vif_index
== MAXVIFS
)
1155 pim_mroute_del(c_oil
, name
);
1157 pim_static_mroute_add(c_oil
, name
);
1160 int pim_mroute_del(struct channel_oil
*c_oil
, const char *name
)
1162 struct pim_instance
*pim
= c_oil
->pim
;
1165 pim
->mroute_del_last
= pim_time_monotonic_sec();
1166 ++pim
->mroute_del_events
;
1168 if (!c_oil
->installed
) {
1169 if (PIM_DEBUG_MROUTE
) {
1172 "%s %s: vifi %d for route is %s not installed, do not need to send del req. ",
1173 __FILE__
, __PRETTY_FUNCTION__
,
1174 c_oil
->oil
.mfcc_parent
,
1175 pim_channel_oil_dump(c_oil
, buf
, sizeof(buf
)));
1180 err
= setsockopt(pim
->mroute_socket
, IPPROTO_IP
, MRT_DEL_MFC
,
1181 &c_oil
->oil
, sizeof(c_oil
->oil
));
1183 if (PIM_DEBUG_MROUTE
)
1185 "%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_DEL_MFC): errno=%d: %s",
1186 __FILE__
, __PRETTY_FUNCTION__
,
1187 pim
->mroute_socket
, errno
,
1188 safe_strerror(errno
));
1192 if (PIM_DEBUG_MROUTE
) {
1194 zlog_debug("%s(%s), vrf %s Deleted Route: %s",
1195 __PRETTY_FUNCTION__
, name
, pim
->vrf
->name
,
1196 pim_channel_oil_dump(c_oil
, buf
, sizeof(buf
)));
1199 // Reset kernel installed flag
1200 c_oil
->installed
= 0;
1205 void pim_mroute_update_counters(struct channel_oil
*c_oil
)
1207 struct pim_instance
*pim
= c_oil
->pim
;
1208 struct sioc_sg_req sgreq
;
1210 c_oil
->cc
.oldpktcnt
= c_oil
->cc
.pktcnt
;
1211 c_oil
->cc
.oldbytecnt
= c_oil
->cc
.bytecnt
;
1212 c_oil
->cc
.oldwrong_if
= c_oil
->cc
.wrong_if
;
1214 if (!c_oil
->installed
) {
1215 c_oil
->cc
.lastused
= 100 * pim
->keep_alive_time
;
1216 if (PIM_DEBUG_MROUTE
) {
1217 struct prefix_sg sg
;
1219 sg
.src
= c_oil
->oil
.mfcc_origin
;
1220 sg
.grp
= c_oil
->oil
.mfcc_mcastgrp
;
1221 if (PIM_DEBUG_MROUTE
)
1223 "Channel%s is not installed no need to collect data from kernel",
1224 pim_str_sg_dump(&sg
));
1229 memset(&sgreq
, 0, sizeof(sgreq
));
1230 sgreq
.src
= c_oil
->oil
.mfcc_origin
;
1231 sgreq
.grp
= c_oil
->oil
.mfcc_mcastgrp
;
1233 pim_zlookup_sg_statistics(c_oil
);
1234 if (ioctl(pim
->mroute_socket
, SIOCGETSGCNT
, &sgreq
)) {
1235 struct prefix_sg sg
;
1237 sg
.src
= c_oil
->oil
.mfcc_origin
;
1238 sg
.grp
= c_oil
->oil
.mfcc_mcastgrp
;
1240 zlog_warn("ioctl(SIOCGETSGCNT=%lu) failure for (S,G)=%s: errno=%d: %s",
1241 (unsigned long)SIOCGETSGCNT
, pim_str_sg_dump(&sg
),
1242 errno
, safe_strerror(errno
));
1246 c_oil
->cc
.pktcnt
= sgreq
.pktcnt
;
1247 c_oil
->cc
.bytecnt
= sgreq
.bytecnt
;
1248 c_oil
->cc
.wrong_if
= sgreq
.wrong_if
;