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_elevate_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_elevate_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 *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_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 PIM_UPSTREAM_FLAG_SET_FHR(up
->flags
);
232 // resolve mfcc_parent prior to mroute_add in channel_add_oif
233 if (up
->rpf
.source_nexthop
.interface
&&
234 up
->channel_oil
->oil
.mfcc_parent
>= MAXVIFS
) {
236 vif_index
= pim_if_find_vifindex_by_ifindex(
238 up
->rpf
.source_nexthop
.interface
->ifindex
);
239 pim_channel_oil_change_iif(pim_ifp
->pim
, up
->channel_oil
,
240 vif_index
, __PRETTY_FUNCTION__
);
242 pim_register_join(up
);
247 static int pim_mroute_msg_wholepkt(int fd
, struct interface
*ifp
,
250 struct pim_interface
*pim_ifp
;
253 const struct ip
*ip_hdr
;
254 struct pim_upstream
*up
;
258 ip_hdr
= (const struct ip
*)buf
;
260 memset(&sg
, 0, sizeof(struct prefix_sg
));
261 sg
.src
= ip_hdr
->ip_src
;
262 sg
.grp
= ip_hdr
->ip_dst
;
264 up
= pim_upstream_find(pim_ifp
->pim
, &sg
);
266 struct prefix_sg star
= sg
;
267 star
.src
.s_addr
= INADDR_ANY
;
269 up
= pim_upstream_find(pim_ifp
->pim
, &star
);
271 if (up
&& PIM_UPSTREAM_FLAG_TEST_SRC_IGMP(up
->flags
)) {
272 up
= pim_upstream_add(pim_ifp
->pim
, &sg
, ifp
,
273 PIM_UPSTREAM_FLAG_MASK_SRC_LHR
,
274 __PRETTY_FUNCTION__
, NULL
);
276 if (PIM_DEBUG_MROUTE
)
278 "%s: Unable to create upstream information for %s",
280 pim_str_sg_dump(&sg
));
283 pim_upstream_keep_alive_timer_start(
284 up
, pim_ifp
->pim
->keep_alive_time
);
285 pim_upstream_inherited_olist(pim_ifp
->pim
, up
);
286 pim_upstream_switch(pim_ifp
->pim
, up
,
287 PIM_UPSTREAM_JOINED
);
289 if (PIM_DEBUG_MROUTE
)
290 zlog_debug("%s: Creating %s upstream on LHR",
291 __PRETTY_FUNCTION__
, up
->sg_str
);
294 if (PIM_DEBUG_MROUTE_DETAIL
) {
296 "%s: Unable to find upstream channel WHOLEPKT%s",
297 __PRETTY_FUNCTION__
, pim_str_sg_dump(&sg
));
302 if (!up
->rpf
.source_nexthop
.interface
) {
304 zlog_debug("%s: up %s RPF is not present",
305 __PRETTY_FUNCTION__
, up
->sg_str
);
309 pim_ifp
= up
->rpf
.source_nexthop
.interface
->info
;
311 rpg
= pim_ifp
? RP(pim_ifp
->pim
, sg
.grp
) : NULL
;
313 if ((pim_rpf_addr_is_inaddr_none(rpg
)) || (!pim_ifp
)
314 || (!(PIM_I_am_DR(pim_ifp
)))) {
315 if (PIM_DEBUG_MROUTE
) {
316 zlog_debug("%s: Failed Check send packet",
317 __PRETTY_FUNCTION__
);
323 * If we've received a register suppress
325 if (!up
->t_rs_timer
) {
326 if (pim_is_grp_ssm(pim_ifp
->pim
, sg
.grp
)) {
327 if (PIM_DEBUG_PIM_REG
)
329 "%s register forward skipped as group is SSM",
330 pim_str_sg_dump(&sg
));
333 pim_register_send((uint8_t *)buf
+ sizeof(struct ip
),
334 ntohs(ip_hdr
->ip_len
) - sizeof(struct ip
),
335 pim_ifp
->primary_address
, rpg
, 0, up
);
340 static int pim_mroute_msg_wrongvif(int fd
, struct interface
*ifp
,
341 const struct igmpmsg
*msg
)
343 struct pim_ifchannel
*ch
;
344 struct pim_interface
*pim_ifp
;
347 memset(&sg
, 0, sizeof(struct prefix_sg
));
348 sg
.src
= msg
->im_src
;
349 sg
.grp
= msg
->im_dst
;
352 Send Assert(S,G) on iif as response to WRONGVIF kernel upcall.
354 RFC 4601 4.8.2. PIM-SSM-Only Routers
356 iif is the incoming interface of the packet.
357 if (iif is in inherited_olist(S,G)) {
358 send Assert(S,G) on iif
363 if (PIM_DEBUG_MROUTE
)
365 "%s: WRONGVIF (S,G)=%s could not find input interface for input_vif_index=%d",
366 __PRETTY_FUNCTION__
, pim_str_sg_dump(&sg
),
373 if (PIM_DEBUG_MROUTE
)
375 "%s: WRONGVIF (S,G)=%s multicast not enabled on interface %s",
376 __PRETTY_FUNCTION__
, pim_str_sg_dump(&sg
),
381 ch
= pim_ifchannel_find(ifp
, &sg
);
383 struct prefix_sg star_g
= sg
;
384 if (PIM_DEBUG_MROUTE
)
386 "%s: WRONGVIF (S,G)=%s could not find channel on interface %s",
387 __PRETTY_FUNCTION__
, pim_str_sg_dump(&sg
),
390 star_g
.src
.s_addr
= INADDR_ANY
;
391 ch
= pim_ifchannel_find(ifp
, &star_g
);
393 if (PIM_DEBUG_MROUTE
)
395 "%s: WRONGVIF (*,G)=%s could not find channel on interface %s",
397 pim_str_sg_dump(&star_g
), ifp
->name
);
403 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
405 Transitions from NoInfo State
407 An (S,G) data packet arrives on interface I, AND
408 CouldAssert(S,G,I)==TRUE An (S,G) data packet arrived on an
409 downstream interface that is in our (S,G) outgoing interface
410 list. We optimistically assume that we will be the assert
411 winner for this (S,G), and so we transition to the "I am Assert
412 Winner" state and perform Actions A1 (below), which will
413 initiate the assert negotiation for (S,G).
416 if (ch
->ifassert_state
!= PIM_IFASSERT_NOINFO
) {
417 if (PIM_DEBUG_MROUTE
) {
419 "%s: WRONGVIF (S,G)=%s channel is not on Assert NoInfo state for interface %s",
420 __PRETTY_FUNCTION__
, ch
->sg_str
, ifp
->name
);
425 if (!PIM_IF_FLAG_TEST_COULD_ASSERT(ch
->flags
)) {
426 if (PIM_DEBUG_MROUTE
) {
428 "%s: WRONGVIF (S,G)=%s interface %s is not downstream for channel",
429 __PRETTY_FUNCTION__
, ch
->sg_str
, ifp
->name
);
434 if (assert_action_a1(ch
)) {
435 if (PIM_DEBUG_MROUTE
) {
437 "%s: WRONGVIF (S,G)=%s assert_action_a1 failure on interface %s",
438 __PRETTY_FUNCTION__
, ch
->sg_str
, ifp
->name
);
446 static int pim_mroute_msg_wrvifwhole(int fd
, struct interface
*ifp
,
449 const struct ip
*ip_hdr
= (const struct ip
*)buf
;
450 struct pim_interface
*pim_ifp
;
451 struct pim_ifchannel
*ch
;
452 struct pim_upstream
*up
;
453 struct prefix_sg star_g
;
458 memset(&sg
, 0, sizeof(struct prefix_sg
));
459 sg
.src
= ip_hdr
->ip_src
;
460 sg
.grp
= ip_hdr
->ip_dst
;
462 ch
= pim_ifchannel_find(ifp
, &sg
);
464 if (PIM_DEBUG_MROUTE
)
466 "WRVIFWHOLE (S,G)=%s found ifchannel on interface %s",
467 ch
->sg_str
, ifp
->name
);
472 star_g
.src
.s_addr
= INADDR_ANY
;
474 ch
= pim_ifchannel_find(ifp
, &star_g
);
477 if (PIM_DEBUG_MROUTE
)
478 zlog_debug ("WRVIFWHOLE (*,G)=%s found ifchannel on interface %s",
479 pim_str_sg_dump (&star_g
), ifp
->name
);
484 up
= pim_upstream_find(pim_ifp
->pim
, &sg
);
486 struct pim_upstream
*parent
;
487 struct pim_nexthop source
;
488 struct pim_rpf
*rpf
= RP(pim_ifp
->pim
, sg
.grp
);
489 if (!rpf
|| !rpf
->source_nexthop
.interface
)
493 * If we have received a WRVIFWHOLE and are at this
494 * point, we could be receiving the packet on the *,G
495 * tree, let's check and if so we can safely drop
498 parent
= pim_upstream_find(pim_ifp
->pim
, &star_g
);
499 if (parent
&& parent
->rpf
.source_nexthop
.interface
== ifp
)
502 pim_ifp
= rpf
->source_nexthop
.interface
->info
;
504 memset(&source
, 0, sizeof(source
));
506 * If we are the fhr that means we are getting a callback during
507 * the pimreg period, so I believe we can ignore this packet
509 if (!PIM_UPSTREAM_FLAG_TEST_FHR(up
->flags
)) {
510 // No if channel, but upstream we are at the RP.
511 if (pim_nexthop_lookup(pim_ifp
->pim
, &source
,
512 up
->upstream_register
, 0)) {
513 pim_register_stop_send(source
.interface
, &sg
,
514 pim_ifp
->primary_address
,
515 up
->upstream_register
);
516 up
->sptbit
= PIM_UPSTREAM_SPTBIT_TRUE
;
519 pim_upstream_inherited_olist(pim_ifp
->pim
, up
);
520 if (!up
->channel_oil
->installed
)
521 pim_mroute_add(up
->channel_oil
,
522 __PRETTY_FUNCTION__
);
524 if (I_am_RP(pim_ifp
->pim
, up
->sg
.grp
)) {
525 if (pim_nexthop_lookup(pim_ifp
->pim
, &source
,
526 up
->upstream_register
,
528 pim_register_stop_send(
529 source
.interface
, &sg
,
530 pim_ifp
->primary_address
,
531 up
->upstream_register
);
532 up
->sptbit
= PIM_UPSTREAM_SPTBIT_TRUE
;
534 pim_upstream_keep_alive_timer_start(
535 up
, pim_ifp
->pim
->keep_alive_time
);
536 pim_upstream_inherited_olist(pim_ifp
->pim
, up
);
537 pim_mroute_msg_wholepkt(fd
, ifp
, buf
);
543 if (pim_if_connected_to_source(ifp
, sg
.src
)) {
544 up
= pim_upstream_add(pim_ifp
->pim
, &sg
, ifp
,
545 PIM_UPSTREAM_FLAG_MASK_FHR
,
546 __PRETTY_FUNCTION__
, NULL
);
548 if (PIM_DEBUG_MROUTE
)
550 "%s: WRONGVIF%s unable to create upstream on interface",
551 pim_str_sg_dump(&sg
), ifp
->name
);
554 PIM_UPSTREAM_FLAG_SET_SRC_STREAM(up
->flags
);
555 pim_upstream_keep_alive_timer_start(
556 up
, pim_ifp
->pim
->keep_alive_time
);
557 up
->channel_oil
->cc
.pktcnt
++;
558 pim_register_join(up
);
559 pim_upstream_inherited_olist(pim_ifp
->pim
, up
);
561 // Send the packet to the RP
562 pim_mroute_msg_wholepkt(fd
, ifp
, buf
);
564 up
= pim_upstream_add(pim_ifp
->pim
, &sg
, ifp
,
565 PIM_UPSTREAM_FLAG_MASK_SRC_NOCACHE
,
566 __PRETTY_FUNCTION__
, NULL
);
567 if (!up
->channel_oil
->installed
)
568 pim_mroute_add(up
->channel_oil
, __PRETTY_FUNCTION__
);
574 static int pim_mroute_msg(struct pim_instance
*pim
, const char *buf
,
575 int buf_size
, ifindex_t ifindex
)
577 struct interface
*ifp
;
578 struct pim_interface
*pim_ifp
;
579 const struct ip
*ip_hdr
;
580 const struct igmpmsg
*msg
;
581 char ip_src_str
[INET_ADDRSTRLEN
] = "";
582 char ip_dst_str
[INET_ADDRSTRLEN
] = "";
583 char src_str
[INET_ADDRSTRLEN
] = "<src?>";
584 char grp_str
[INET_ADDRSTRLEN
] = "<grp?>";
585 struct in_addr ifaddr
;
586 struct igmp_sock
*igmp
;
588 ip_hdr
= (const struct ip
*)buf
;
590 if (ip_hdr
->ip_p
== IPPROTO_IGMP
) {
592 /* We have the IP packet but we do not know which interface this
594 * received on. Find the interface that is on the same subnet as
598 ifp
= if_lookup_by_index(ifindex
, pim
->vrf_id
);
600 if (!ifp
|| !ifp
->info
)
604 ifaddr
= pim_find_primary_addr(ifp
);
605 igmp
= pim_igmp_sock_lookup_ifaddr(pim_ifp
->igmp_socket_list
,
608 if (PIM_DEBUG_MROUTE
) {
609 pim_inet4_dump("<src?>", ip_hdr
->ip_src
, ip_src_str
,
611 pim_inet4_dump("<dst?>", ip_hdr
->ip_dst
, ip_dst_str
,
615 "%s(%s): igmp kernel upcall on %s(%p) for %s -> %s",
616 __PRETTY_FUNCTION__
, pim
->vrf
->name
, ifp
->name
,
617 igmp
, ip_src_str
, ip_dst_str
);
620 pim_igmp_packet(igmp
, (char *)buf
, buf_size
);
622 } else if (ip_hdr
->ip_p
) {
623 if (PIM_DEBUG_MROUTE_DETAIL
) {
624 pim_inet4_dump("<src?>", ip_hdr
->ip_src
, src_str
,
626 pim_inet4_dump("<grp?>", ip_hdr
->ip_dst
, grp_str
,
629 "%s: no kernel upcall proto=%d src: %s dst: %s msg_size=%d",
630 __PRETTY_FUNCTION__
, ip_hdr
->ip_p
, src_str
,
635 msg
= (const struct igmpmsg
*)buf
;
637 ifp
= pim_if_find_by_vif_index(pim
, msg
->im_vif
);
641 if (PIM_DEBUG_MROUTE
) {
642 pim_inet4_dump("<src?>", msg
->im_src
, src_str
,
644 pim_inet4_dump("<grp?>", msg
->im_dst
, grp_str
,
647 "%s: pim kernel upcall %s type=%d ip_p=%d from fd=%d for (S,G)=(%s,%s) on %s vifi=%d size=%d",
649 igmpmsgtype2str
[msg
->im_msgtype
],
650 msg
->im_msgtype
, ip_hdr
->ip_p
,
651 pim
->mroute_socket
, src_str
, grp_str
, ifp
->name
,
652 msg
->im_vif
, buf_size
);
655 switch (msg
->im_msgtype
) {
656 case IGMPMSG_WRONGVIF
:
657 return pim_mroute_msg_wrongvif(pim
->mroute_socket
, ifp
,
660 case IGMPMSG_NOCACHE
:
661 return pim_mroute_msg_nocache(pim
->mroute_socket
, ifp
,
664 case IGMPMSG_WHOLEPKT
:
665 return pim_mroute_msg_wholepkt(pim
->mroute_socket
, ifp
,
668 case IGMPMSG_WRVIFWHOLE
:
669 return pim_mroute_msg_wrvifwhole(
670 pim
->mroute_socket
, ifp
, (const char *)msg
);
680 static int mroute_read(struct thread
*t
)
682 struct pim_instance
*pim
;
683 static long long count
;
692 rd
= pim_socket_recvfromto(pim
->mroute_socket
, (uint8_t *)buf
,
693 sizeof(buf
), NULL
, NULL
, NULL
, NULL
,
698 if (errno
== EWOULDBLOCK
|| errno
== EAGAIN
)
701 if (PIM_DEBUG_MROUTE
)
703 "%s: failure reading rd=%d: fd=%d: errno=%d: %s",
704 __PRETTY_FUNCTION__
, rd
,
705 pim
->mroute_socket
, errno
,
706 safe_strerror(errno
));
710 result
= pim_mroute_msg(pim
, buf
, rd
, ifindex
);
713 if (count
% router
->packet_process
== 0)
723 static void mroute_read_on(struct pim_instance
*pim
)
725 thread_add_read(router
->master
, mroute_read
, pim
, pim
->mroute_socket
,
729 static void mroute_read_off(struct pim_instance
*pim
)
731 THREAD_OFF(pim
->thread
);
734 int pim_mroute_socket_enable(struct pim_instance
*pim
)
738 frr_elevate_privs(&pimd_privs
) {
740 fd
= socket(AF_INET
, SOCK_RAW
, IPPROTO_IGMP
);
743 zlog_warn("Could not create mroute socket: errno=%d: %s",
745 safe_strerror(errno
));
749 #ifdef SO_BINDTODEVICE
750 if (pim
->vrf
->vrf_id
!= VRF_DEFAULT
751 && setsockopt(fd
, SOL_SOCKET
, SO_BINDTODEVICE
,
752 pim
->vrf
->name
, strlen(pim
->vrf
->name
))) {
753 zlog_warn("Could not setsockopt SO_BINDTODEVICE: %s",
754 safe_strerror(errno
));
762 pim
->mroute_socket
= fd
;
763 if (pim_mroute_set(pim
, 1)) {
765 "Could not enable mroute on socket fd=%d: errno=%d: %s",
766 fd
, errno
, safe_strerror(errno
));
768 pim
->mroute_socket
= -1;
772 pim
->mroute_socket_creation
= pim_time_monotonic_sec();
779 int pim_mroute_socket_disable(struct pim_instance
*pim
)
781 if (pim_mroute_set(pim
, 0)) {
783 "Could not disable mroute on socket fd=%d: errno=%d: %s",
784 pim
->mroute_socket
, errno
, safe_strerror(errno
));
788 if (close(pim
->mroute_socket
)) {
789 zlog_warn("Failure closing mroute socket: fd=%d errno=%d: %s",
790 pim
->mroute_socket
, errno
, safe_strerror(errno
));
794 mroute_read_off(pim
);
795 pim
->mroute_socket
= -1;
801 For each network interface (e.g., physical or a virtual tunnel) that
802 would be used for multicast forwarding, a corresponding multicast
803 interface must be added to the kernel.
805 int pim_mroute_add_vif(struct interface
*ifp
, struct in_addr ifaddr
,
808 struct pim_interface
*pim_ifp
= ifp
->info
;
812 if (PIM_DEBUG_MROUTE
)
813 zlog_debug("%s: Add Vif %d (%s[%s])", __PRETTY_FUNCTION__
,
814 pim_ifp
->mroute_vif_index
, ifp
->name
,
815 pim_ifp
->pim
->vrf
->name
);
817 memset(&vc
, 0, sizeof(vc
));
818 vc
.vifc_vifi
= pim_ifp
->mroute_vif_index
;
819 #ifdef VIFF_USE_IFINDEX
820 vc
.vifc_lcl_ifindex
= ifp
->ifindex
;
822 if (ifaddr
.s_addr
== INADDR_ANY
) {
824 "%s: unnumbered interfaces are not supported on this platform",
825 __PRETTY_FUNCTION__
);
828 memcpy(&vc
.vifc_lcl_addr
, &ifaddr
, sizeof(vc
.vifc_lcl_addr
));
830 vc
.vifc_flags
= flags
;
831 vc
.vifc_threshold
= PIM_MROUTE_MIN_TTL
;
832 vc
.vifc_rate_limit
= 0;
834 #ifdef PIM_DVMRP_TUNNEL
835 if (vc
.vifc_flags
& VIFF_TUNNEL
) {
836 memcpy(&vc
.vifc_rmt_addr
, &vif_remote_addr
,
837 sizeof(vc
.vifc_rmt_addr
));
841 err
= setsockopt(pim_ifp
->pim
->mroute_socket
, IPPROTO_IP
, MRT_ADD_VIF
,
842 (void *)&vc
, sizeof(vc
));
844 char ifaddr_str
[INET_ADDRSTRLEN
];
846 pim_inet4_dump("<ifaddr?>", ifaddr
, ifaddr_str
,
850 "%s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_ADD_VIF,vif_index=%d,ifaddr=%s,flag=%d): errno=%d: %s",
851 __PRETTY_FUNCTION__
, pim_ifp
->pim
->mroute_socket
,
852 ifp
->ifindex
, ifaddr_str
, flags
, errno
,
853 safe_strerror(errno
));
860 int pim_mroute_del_vif(struct interface
*ifp
)
862 struct pim_interface
*pim_ifp
= ifp
->info
;
866 if (PIM_DEBUG_MROUTE
)
867 zlog_debug("%s: Del Vif %d (%s[%s])", __PRETTY_FUNCTION__
,
868 pim_ifp
->mroute_vif_index
, ifp
->name
,
869 pim_ifp
->pim
->vrf
->name
);
871 memset(&vc
, 0, sizeof(vc
));
872 vc
.vifc_vifi
= pim_ifp
->mroute_vif_index
;
874 err
= setsockopt(pim_ifp
->pim
->mroute_socket
, IPPROTO_IP
, MRT_DEL_VIF
,
875 (void *)&vc
, sizeof(vc
));
878 "%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_DEL_VIF,vif_index=%d): errno=%d: %s",
879 __FILE__
, __PRETTY_FUNCTION__
,
880 pim_ifp
->pim
->mroute_socket
, pim_ifp
->mroute_vif_index
,
881 errno
, safe_strerror(errno
));
888 int pim_mroute_add(struct channel_oil
*c_oil
, const char *name
)
890 struct pim_instance
*pim
= c_oil
->pim
;
893 int orig_iif_vif
= 0;
894 struct pim_interface
*pim_reg_ifp
= NULL
;
895 int orig_pimreg_ttl
= 0;
896 bool pimreg_ttl_reset
= false;
897 struct pim_interface
*vxlan_ifp
= NULL
;
898 int orig_term_ttl
= 0;
899 bool orig_term_ttl_reset
= false;
901 pim
->mroute_add_last
= pim_time_monotonic_sec();
902 ++pim
->mroute_add_events
;
904 /* Do not install route if incoming interface is undefined. */
905 if (c_oil
->oil
.mfcc_parent
>= MAXVIFS
) {
906 if (PIM_DEBUG_MROUTE
) {
909 "%s(%s) %s Attempting to add vifi that is invalid to mroute table",
910 __PRETTY_FUNCTION__
, name
,
911 pim_channel_oil_dump(c_oil
, buf
, sizeof(buf
)));
916 /* The linux kernel *expects* the incoming
917 * vif to be part of the outgoing list
918 * in the case of a (*,G).
920 if (c_oil
->oil
.mfcc_origin
.s_addr
== INADDR_ANY
) {
921 orig
= c_oil
->oil
.mfcc_ttls
[c_oil
->oil
.mfcc_parent
];
922 c_oil
->oil
.mfcc_ttls
[c_oil
->oil
.mfcc_parent
] = 1;
926 /* suppress pimreg in the OIL if the mroute is not supposed to
927 * trigger register encapsulated data
929 if (PIM_UPSTREAM_FLAG_TEST_NO_PIMREG_DATA(c_oil
->up
->flags
)) {
930 pim_reg_ifp
= pim
->regiface
->info
;
932 c_oil
->oil
.mfcc_ttls
[pim_reg_ifp
->mroute_vif_index
];
933 c_oil
->oil
.mfcc_ttls
[pim_reg_ifp
->mroute_vif_index
] = 0;
934 /* remember to flip it back after MFC programming */
935 pimreg_ttl_reset
= true;
938 vxlan_ifp
= pim_vxlan_get_term_ifp(pim
);
939 /* 1. vxlan termination device must never be added to the
940 * origination mroute (and that can actually happen because
941 * of XG inheritance from the termination mroute) otherwise
942 * traffic will end up looping.
943 * 2. vxlan termination device should be removed from the non-DF
944 * to prevent duplicates to the overlay rxer
947 (PIM_UPSTREAM_FLAG_TEST_SRC_VXLAN_ORIG(c_oil
->up
->flags
) ||
948 PIM_UPSTREAM_FLAG_TEST_MLAG_NON_DF(c_oil
->up
->flags
))) {
949 orig_term_ttl_reset
= true;
951 c_oil
->oil
.mfcc_ttls
[vxlan_ifp
->mroute_vif_index
];
952 c_oil
->oil
.mfcc_ttls
[vxlan_ifp
->mroute_vif_index
] = 0;
957 * If we have an unresolved cache entry for the S,G
958 * it is owned by the pimreg for the incoming IIF
959 * So set pimreg as the IIF temporarily to cause
960 * the packets to be forwarded. Then set it
961 * to the correct IIF afterwords.
963 if (!c_oil
->installed
&& c_oil
->oil
.mfcc_origin
.s_addr
!= INADDR_ANY
964 && c_oil
->oil
.mfcc_parent
!= 0) {
965 orig_iif_vif
= c_oil
->oil
.mfcc_parent
;
966 c_oil
->oil
.mfcc_parent
= 0;
968 err
= setsockopt(pim
->mroute_socket
, IPPROTO_IP
, MRT_ADD_MFC
,
969 &c_oil
->oil
, sizeof(c_oil
->oil
));
971 if (!err
&& !c_oil
->installed
972 && c_oil
->oil
.mfcc_origin
.s_addr
!= INADDR_ANY
973 && orig_iif_vif
!= 0) {
974 c_oil
->oil
.mfcc_parent
= orig_iif_vif
;
975 err
= setsockopt(pim
->mroute_socket
, IPPROTO_IP
, MRT_ADD_MFC
,
976 &c_oil
->oil
, sizeof(c_oil
->oil
));
979 if (c_oil
->oil
.mfcc_origin
.s_addr
== INADDR_ANY
)
980 c_oil
->oil
.mfcc_ttls
[c_oil
->oil
.mfcc_parent
] = orig
;
982 if (pimreg_ttl_reset
) {
984 c_oil
->oil
.mfcc_ttls
[pim_reg_ifp
->mroute_vif_index
] =
988 if (orig_term_ttl_reset
)
989 c_oil
->oil
.mfcc_ttls
[vxlan_ifp
->mroute_vif_index
] =
994 "%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_ADD_MFC): errno=%d: %s",
995 __FILE__
, __PRETTY_FUNCTION__
, pim
->mroute_socket
,
996 errno
, safe_strerror(errno
));
1000 if (PIM_DEBUG_MROUTE
) {
1002 zlog_debug("%s(%s), vrf %s Added Route: %s",
1003 __PRETTY_FUNCTION__
, name
, pim
->vrf
->name
,
1004 pim_channel_oil_dump(c_oil
, buf
, sizeof(buf
)));
1007 c_oil
->installed
= 1;
1008 c_oil
->mroute_creation
= pim_time_monotonic_sec();
1013 int pim_mroute_del(struct channel_oil
*c_oil
, const char *name
)
1015 struct pim_instance
*pim
= c_oil
->pim
;
1018 pim
->mroute_del_last
= pim_time_monotonic_sec();
1019 ++pim
->mroute_del_events
;
1021 if (!c_oil
->installed
) {
1022 if (PIM_DEBUG_MROUTE
) {
1025 "%s %s: vifi %d for route is %s not installed, do not need to send del req. ",
1026 __FILE__
, __PRETTY_FUNCTION__
,
1027 c_oil
->oil
.mfcc_parent
,
1028 pim_channel_oil_dump(c_oil
, buf
, sizeof(buf
)));
1033 err
= setsockopt(pim
->mroute_socket
, IPPROTO_IP
, MRT_DEL_MFC
,
1034 &c_oil
->oil
, sizeof(c_oil
->oil
));
1036 if (PIM_DEBUG_MROUTE
)
1038 "%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_DEL_MFC): errno=%d: %s",
1039 __FILE__
, __PRETTY_FUNCTION__
,
1040 pim
->mroute_socket
, errno
,
1041 safe_strerror(errno
));
1045 if (PIM_DEBUG_MROUTE
) {
1047 zlog_debug("%s(%s), vrf %s Deleted Route: %s",
1048 __PRETTY_FUNCTION__
, name
, pim
->vrf
->name
,
1049 pim_channel_oil_dump(c_oil
, buf
, sizeof(buf
)));
1052 // Reset kernel installed flag
1053 c_oil
->installed
= 0;
1058 void pim_mroute_update_counters(struct channel_oil
*c_oil
)
1060 struct pim_instance
*pim
= c_oil
->pim
;
1061 struct sioc_sg_req sgreq
;
1063 c_oil
->cc
.oldpktcnt
= c_oil
->cc
.pktcnt
;
1064 c_oil
->cc
.oldbytecnt
= c_oil
->cc
.bytecnt
;
1065 c_oil
->cc
.oldwrong_if
= c_oil
->cc
.wrong_if
;
1067 if (!c_oil
->installed
) {
1068 c_oil
->cc
.lastused
= 100 * pim
->keep_alive_time
;
1069 if (PIM_DEBUG_MROUTE
) {
1070 struct prefix_sg sg
;
1072 sg
.src
= c_oil
->oil
.mfcc_origin
;
1073 sg
.grp
= c_oil
->oil
.mfcc_mcastgrp
;
1074 if (PIM_DEBUG_MROUTE
)
1076 "Channel%s is not installed no need to collect data from kernel",
1077 pim_str_sg_dump(&sg
));
1082 memset(&sgreq
, 0, sizeof(sgreq
));
1083 sgreq
.src
= c_oil
->oil
.mfcc_origin
;
1084 sgreq
.grp
= c_oil
->oil
.mfcc_mcastgrp
;
1086 pim_zlookup_sg_statistics(c_oil
);
1087 if (ioctl(pim
->mroute_socket
, SIOCGETSGCNT
, &sgreq
)) {
1088 if (PIM_DEBUG_MROUTE
) {
1089 struct prefix_sg sg
;
1091 sg
.src
= c_oil
->oil
.mfcc_origin
;
1092 sg
.grp
= c_oil
->oil
.mfcc_mcastgrp
;
1095 "ioctl(SIOCGETSGCNT=%lu) failure for (S,G)=%s: errno=%d: %s",
1096 (unsigned long)SIOCGETSGCNT
,
1097 pim_str_sg_dump(&sg
), errno
,
1098 safe_strerror(errno
));
1103 c_oil
->cc
.pktcnt
= sgreq
.pktcnt
;
1104 c_oil
->cc
.bytecnt
= sgreq
.bytecnt
;
1105 c_oil
->cc
.wrong_if
= sgreq
.wrong_if
;