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 struct channel_oil
*c_oil
;
195 if (PIM_DEBUG_MROUTE_DETAIL
)
196 zlog_debug("%s: Interface is not the DR blackholing incoming traffic for %s",
197 __PRETTY_FUNCTION__
, pim_str_sg_dump(&sg
));
200 * We are not the DR, but we are still receiving packets
201 * Let's blackhole those packets for the moment
202 * As that they will be coming up to the cpu
203 * and causing us to consider them.
205 c_oil
= pim_channel_oil_add(pim_ifp
->pim
, &sg
,
206 pim_ifp
->mroute_vif_index
);
207 pim_mroute_add(c_oil
, __PRETTY_FUNCTION__
);
212 up
= pim_upstream_find_or_add(&sg
, ifp
, PIM_UPSTREAM_FLAG_MASK_FHR
,
213 __PRETTY_FUNCTION__
);
215 if (PIM_DEBUG_MROUTE
) {
217 "%s: Failure to add upstream information for %s",
218 __PRETTY_FUNCTION__
, pim_str_sg_dump(&sg
));
224 * I moved this debug till after the actual add because
225 * I want to take advantage of the up->sg_str being filled in.
227 if (PIM_DEBUG_MROUTE
) {
228 zlog_debug("%s: Adding a Route %s for WHOLEPKT consumption",
229 __PRETTY_FUNCTION__
, up
->sg_str
);
232 PIM_UPSTREAM_FLAG_SET_SRC_STREAM(up
->flags
);
233 pim_upstream_keep_alive_timer_start(up
, pim_ifp
->pim
->keep_alive_time
);
235 up
->channel_oil
->cc
.pktcnt
++;
236 PIM_UPSTREAM_FLAG_SET_FHR(up
->flags
);
237 // resolve mfcc_parent prior to mroute_add in channel_add_oif
238 if (up
->rpf
.source_nexthop
.interface
&&
239 up
->channel_oil
->oil
.mfcc_parent
>= MAXVIFS
) {
241 vif_index
= pim_if_find_vifindex_by_ifindex(
243 up
->rpf
.source_nexthop
.interface
->ifindex
);
244 up
->channel_oil
->oil
.mfcc_parent
= vif_index
;
246 pim_register_join(up
);
251 static int pim_mroute_msg_wholepkt(int fd
, struct interface
*ifp
,
254 struct pim_interface
*pim_ifp
;
257 const struct ip
*ip_hdr
;
258 struct pim_upstream
*up
;
262 ip_hdr
= (const struct ip
*)buf
;
264 memset(&sg
, 0, sizeof(struct prefix_sg
));
265 sg
.src
= ip_hdr
->ip_src
;
266 sg
.grp
= ip_hdr
->ip_dst
;
268 up
= pim_upstream_find(pim_ifp
->pim
, &sg
);
270 struct prefix_sg star
= sg
;
271 star
.src
.s_addr
= INADDR_ANY
;
273 up
= pim_upstream_find(pim_ifp
->pim
, &star
);
275 if (up
&& PIM_UPSTREAM_FLAG_TEST_SRC_IGMP(up
->flags
)) {
276 up
= pim_upstream_add(pim_ifp
->pim
, &sg
, ifp
,
277 PIM_UPSTREAM_FLAG_MASK_SRC_LHR
,
278 __PRETTY_FUNCTION__
, NULL
);
280 if (PIM_DEBUG_MROUTE
)
282 "%s: Unable to create upstream information for %s",
284 pim_str_sg_dump(&sg
));
287 pim_upstream_keep_alive_timer_start(
288 up
, pim_ifp
->pim
->keep_alive_time
);
289 pim_upstream_inherited_olist(pim_ifp
->pim
, up
);
290 pim_upstream_switch(pim_ifp
->pim
, up
,
291 PIM_UPSTREAM_JOINED
);
293 if (PIM_DEBUG_MROUTE
)
294 zlog_debug("%s: Creating %s upstream on LHR",
295 __PRETTY_FUNCTION__
, up
->sg_str
);
298 if (PIM_DEBUG_MROUTE_DETAIL
) {
300 "%s: Unable to find upstream channel WHOLEPKT%s",
301 __PRETTY_FUNCTION__
, pim_str_sg_dump(&sg
));
306 if (!up
->rpf
.source_nexthop
.interface
) {
308 zlog_debug("%s: up %s RPF is not present",
309 __PRETTY_FUNCTION__
, up
->sg_str
);
313 pim_ifp
= up
->rpf
.source_nexthop
.interface
->info
;
315 rpg
= pim_ifp
? RP(pim_ifp
->pim
, sg
.grp
) : NULL
;
317 if ((pim_rpf_addr_is_inaddr_none(rpg
)) || (!pim_ifp
)
318 || (!(PIM_I_am_DR(pim_ifp
)))) {
319 if (PIM_DEBUG_MROUTE
) {
320 zlog_debug("%s: Failed Check send packet",
321 __PRETTY_FUNCTION__
);
327 * If we've received a register suppress
329 if (!up
->t_rs_timer
) {
330 if (pim_is_grp_ssm(pim_ifp
->pim
, sg
.grp
)) {
331 if (PIM_DEBUG_PIM_REG
)
333 "%s register forward skipped as group is SSM",
334 pim_str_sg_dump(&sg
));
337 pim_register_send((uint8_t *)buf
+ sizeof(struct ip
),
338 ntohs(ip_hdr
->ip_len
) - sizeof(struct ip
),
339 pim_ifp
->primary_address
, rpg
, 0, up
);
344 static int pim_mroute_msg_wrongvif(int fd
, struct interface
*ifp
,
345 const struct igmpmsg
*msg
)
347 struct pim_ifchannel
*ch
;
348 struct pim_interface
*pim_ifp
;
351 memset(&sg
, 0, sizeof(struct prefix_sg
));
352 sg
.src
= msg
->im_src
;
353 sg
.grp
= msg
->im_dst
;
356 Send Assert(S,G) on iif as response to WRONGVIF kernel upcall.
358 RFC 4601 4.8.2. PIM-SSM-Only Routers
360 iif is the incoming interface of the packet.
361 if (iif is in inherited_olist(S,G)) {
362 send Assert(S,G) on iif
367 if (PIM_DEBUG_MROUTE
)
369 "%s: WRONGVIF (S,G)=%s could not find input interface for input_vif_index=%d",
370 __PRETTY_FUNCTION__
, pim_str_sg_dump(&sg
),
377 if (PIM_DEBUG_MROUTE
)
379 "%s: WRONGVIF (S,G)=%s multicast not enabled on interface %s",
380 __PRETTY_FUNCTION__
, pim_str_sg_dump(&sg
),
385 ch
= pim_ifchannel_find(ifp
, &sg
);
387 struct prefix_sg star_g
= sg
;
388 if (PIM_DEBUG_MROUTE
)
390 "%s: WRONGVIF (S,G)=%s could not find channel on interface %s",
391 __PRETTY_FUNCTION__
, pim_str_sg_dump(&sg
),
394 star_g
.src
.s_addr
= INADDR_ANY
;
395 ch
= pim_ifchannel_find(ifp
, &star_g
);
397 if (PIM_DEBUG_MROUTE
)
399 "%s: WRONGVIF (*,G)=%s could not find channel on interface %s",
401 pim_str_sg_dump(&star_g
), ifp
->name
);
407 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
409 Transitions from NoInfo State
411 An (S,G) data packet arrives on interface I, AND
412 CouldAssert(S,G,I)==TRUE An (S,G) data packet arrived on an
413 downstream interface that is in our (S,G) outgoing interface
414 list. We optimistically assume that we will be the assert
415 winner for this (S,G), and so we transition to the "I am Assert
416 Winner" state and perform Actions A1 (below), which will
417 initiate the assert negotiation for (S,G).
420 if (ch
->ifassert_state
!= PIM_IFASSERT_NOINFO
) {
421 if (PIM_DEBUG_MROUTE
) {
423 "%s: WRONGVIF (S,G)=%s channel is not on Assert NoInfo state for interface %s",
424 __PRETTY_FUNCTION__
, ch
->sg_str
, ifp
->name
);
429 if (!PIM_IF_FLAG_TEST_COULD_ASSERT(ch
->flags
)) {
430 if (PIM_DEBUG_MROUTE
) {
432 "%s: WRONGVIF (S,G)=%s interface %s is not downstream for channel",
433 __PRETTY_FUNCTION__
, ch
->sg_str
, ifp
->name
);
438 if (assert_action_a1(ch
)) {
439 if (PIM_DEBUG_MROUTE
) {
441 "%s: WRONGVIF (S,G)=%s assert_action_a1 failure on interface %s",
442 __PRETTY_FUNCTION__
, ch
->sg_str
, ifp
->name
);
450 static int pim_mroute_msg_wrvifwhole(int fd
, struct interface
*ifp
,
453 const struct ip
*ip_hdr
= (const struct ip
*)buf
;
454 struct pim_interface
*pim_ifp
;
455 struct pim_ifchannel
*ch
;
456 struct pim_upstream
*up
;
457 struct prefix_sg star_g
;
459 struct channel_oil
*oil
;
463 memset(&sg
, 0, sizeof(struct prefix_sg
));
464 sg
.src
= ip_hdr
->ip_src
;
465 sg
.grp
= ip_hdr
->ip_dst
;
467 ch
= pim_ifchannel_find(ifp
, &sg
);
469 if (PIM_DEBUG_MROUTE
)
471 "WRVIFWHOLE (S,G)=%s found ifchannel on interface %s",
472 ch
->sg_str
, ifp
->name
);
477 star_g
.src
.s_addr
= INADDR_ANY
;
479 ch
= pim_ifchannel_find(ifp
, &star_g
);
482 if (PIM_DEBUG_MROUTE
)
483 zlog_debug ("WRVIFWHOLE (*,G)=%s found ifchannel on interface %s",
484 pim_str_sg_dump (&star_g
), ifp
->name
);
489 up
= pim_upstream_find(pim_ifp
->pim
, &sg
);
491 struct pim_upstream
*parent
;
492 struct pim_nexthop source
;
493 struct pim_rpf
*rpf
= RP(pim_ifp
->pim
, sg
.grp
);
494 if (!rpf
|| !rpf
->source_nexthop
.interface
)
498 * If we have received a WRVIFWHOLE and are at this
499 * point, we could be receiving the packet on the *,G
500 * tree, let's check and if so we can safely drop
503 parent
= pim_upstream_find(pim_ifp
->pim
, &star_g
);
504 if (parent
&& parent
->rpf
.source_nexthop
.interface
== ifp
)
507 pim_ifp
= rpf
->source_nexthop
.interface
->info
;
509 memset(&source
, 0, sizeof(source
));
511 * If we are the fhr that means we are getting a callback during
512 * the pimreg period, so I believe we can ignore this packet
514 if (!PIM_UPSTREAM_FLAG_TEST_FHR(up
->flags
)) {
515 // No if channel, but upstream we are at the RP.
516 if (pim_nexthop_lookup(pim_ifp
->pim
, &source
,
517 up
->upstream_register
, 0)) {
518 pim_register_stop_send(source
.interface
, &sg
,
519 pim_ifp
->primary_address
,
520 up
->upstream_register
);
521 up
->sptbit
= PIM_UPSTREAM_SPTBIT_TRUE
;
523 if (!up
->channel_oil
)
524 up
->channel_oil
= pim_channel_oil_add(
526 pim_ifp
->mroute_vif_index
);
527 pim_upstream_inherited_olist(pim_ifp
->pim
, up
);
528 if (!up
->channel_oil
->installed
)
529 pim_mroute_add(up
->channel_oil
,
530 __PRETTY_FUNCTION__
);
532 if (I_am_RP(pim_ifp
->pim
, up
->sg
.grp
)) {
533 if (pim_nexthop_lookup(pim_ifp
->pim
, &source
,
534 up
->upstream_register
,
536 pim_register_stop_send(
537 source
.interface
, &sg
,
538 pim_ifp
->primary_address
,
539 up
->upstream_register
);
540 up
->sptbit
= PIM_UPSTREAM_SPTBIT_TRUE
;
542 pim_upstream_keep_alive_timer_start(
543 up
, pim_ifp
->pim
->keep_alive_time
);
544 pim_upstream_inherited_olist(pim_ifp
->pim
, up
);
545 pim_mroute_msg_wholepkt(fd
, ifp
, buf
);
551 oil
= pim_channel_oil_add(pim_ifp
->pim
, &sg
, pim_ifp
->mroute_vif_index
);
553 pim_mroute_add(oil
, __PRETTY_FUNCTION__
);
554 if (pim_if_connected_to_source(ifp
, sg
.src
)) {
555 up
= pim_upstream_add(pim_ifp
->pim
, &sg
, ifp
,
556 PIM_UPSTREAM_FLAG_MASK_FHR
,
557 __PRETTY_FUNCTION__
, NULL
);
559 if (PIM_DEBUG_MROUTE
)
561 "%s: WRONGVIF%s unable to create upstream on interface",
562 pim_str_sg_dump(&sg
), ifp
->name
);
565 PIM_UPSTREAM_FLAG_SET_SRC_STREAM(up
->flags
);
566 pim_upstream_keep_alive_timer_start(
567 up
, pim_ifp
->pim
->keep_alive_time
);
568 up
->channel_oil
= oil
;
569 up
->channel_oil
->cc
.pktcnt
++;
570 pim_register_join(up
);
571 pim_upstream_inherited_olist(pim_ifp
->pim
, up
);
573 // Send the packet to the RP
574 pim_mroute_msg_wholepkt(fd
, ifp
, buf
);
580 static int pim_mroute_msg(struct pim_instance
*pim
, const char *buf
,
581 int buf_size
, ifindex_t ifindex
)
583 struct interface
*ifp
;
584 struct pim_interface
*pim_ifp
;
585 const struct ip
*ip_hdr
;
586 const struct igmpmsg
*msg
;
587 char ip_src_str
[INET_ADDRSTRLEN
] = "";
588 char ip_dst_str
[INET_ADDRSTRLEN
] = "";
589 char src_str
[INET_ADDRSTRLEN
] = "<src?>";
590 char grp_str
[INET_ADDRSTRLEN
] = "<grp?>";
591 struct in_addr ifaddr
;
592 struct igmp_sock
*igmp
;
594 ip_hdr
= (const struct ip
*)buf
;
596 if (ip_hdr
->ip_p
== IPPROTO_IGMP
) {
598 /* We have the IP packet but we do not know which interface this
600 * received on. Find the interface that is on the same subnet as
604 ifp
= if_lookup_by_index(ifindex
, pim
->vrf_id
);
606 if (!ifp
|| !ifp
->info
)
610 ifaddr
= pim_find_primary_addr(ifp
);
611 igmp
= pim_igmp_sock_lookup_ifaddr(pim_ifp
->igmp_socket_list
,
614 if (PIM_DEBUG_MROUTE
) {
615 pim_inet4_dump("<src?>", ip_hdr
->ip_src
, ip_src_str
,
617 pim_inet4_dump("<dst?>", ip_hdr
->ip_dst
, ip_dst_str
,
621 "%s(%s): igmp kernel upcall on %s(%p) for %s -> %s",
622 __PRETTY_FUNCTION__
, pim
->vrf
->name
, ifp
->name
,
623 igmp
, ip_src_str
, ip_dst_str
);
626 pim_igmp_packet(igmp
, (char *)buf
, buf_size
);
628 } else if (ip_hdr
->ip_p
) {
629 if (PIM_DEBUG_MROUTE_DETAIL
) {
630 pim_inet4_dump("<src?>", ip_hdr
->ip_src
, src_str
,
632 pim_inet4_dump("<grp?>", ip_hdr
->ip_dst
, grp_str
,
635 "%s: no kernel upcall proto=%d src: %s dst: %s msg_size=%d",
636 __PRETTY_FUNCTION__
, ip_hdr
->ip_p
, src_str
,
641 msg
= (const struct igmpmsg
*)buf
;
643 ifp
= pim_if_find_by_vif_index(pim
, msg
->im_vif
);
647 if (PIM_DEBUG_MROUTE
) {
648 pim_inet4_dump("<src?>", msg
->im_src
, src_str
,
650 pim_inet4_dump("<grp?>", msg
->im_dst
, grp_str
,
653 "%s: pim kernel upcall %s type=%d ip_p=%d from fd=%d for (S,G)=(%s,%s) on %s vifi=%d size=%d",
655 igmpmsgtype2str
[msg
->im_msgtype
],
656 msg
->im_msgtype
, ip_hdr
->ip_p
,
657 pim
->mroute_socket
, src_str
, grp_str
, ifp
->name
,
658 msg
->im_vif
, buf_size
);
661 switch (msg
->im_msgtype
) {
662 case IGMPMSG_WRONGVIF
:
663 return pim_mroute_msg_wrongvif(pim
->mroute_socket
, ifp
,
666 case IGMPMSG_NOCACHE
:
667 return pim_mroute_msg_nocache(pim
->mroute_socket
, ifp
,
670 case IGMPMSG_WHOLEPKT
:
671 return pim_mroute_msg_wholepkt(pim
->mroute_socket
, ifp
,
674 case IGMPMSG_WRVIFWHOLE
:
675 return pim_mroute_msg_wrvifwhole(
676 pim
->mroute_socket
, ifp
, (const char *)msg
);
686 static int mroute_read(struct thread
*t
)
688 struct pim_instance
*pim
;
689 static long long count
;
698 rd
= pim_socket_recvfromto(pim
->mroute_socket
, (uint8_t *)buf
,
699 sizeof(buf
), NULL
, NULL
, NULL
, NULL
,
704 if (errno
== EWOULDBLOCK
|| errno
== EAGAIN
)
707 if (PIM_DEBUG_MROUTE
)
709 "%s: failure reading rd=%d: fd=%d: errno=%d: %s",
710 __PRETTY_FUNCTION__
, rd
,
711 pim
->mroute_socket
, errno
,
712 safe_strerror(errno
));
716 result
= pim_mroute_msg(pim
, buf
, rd
, ifindex
);
719 if (count
% router
->packet_process
== 0)
729 static void mroute_read_on(struct pim_instance
*pim
)
731 thread_add_read(router
->master
, mroute_read
, pim
, pim
->mroute_socket
,
735 static void mroute_read_off(struct pim_instance
*pim
)
737 THREAD_OFF(pim
->thread
);
740 int pim_mroute_socket_enable(struct pim_instance
*pim
)
744 frr_elevate_privs(&pimd_privs
) {
746 fd
= socket(AF_INET
, SOCK_RAW
, IPPROTO_IGMP
);
749 zlog_warn("Could not create mroute socket: errno=%d: %s",
751 safe_strerror(errno
));
755 #ifdef SO_BINDTODEVICE
756 if (pim
->vrf
->vrf_id
!= VRF_DEFAULT
757 && setsockopt(fd
, SOL_SOCKET
, SO_BINDTODEVICE
,
758 pim
->vrf
->name
, strlen(pim
->vrf
->name
))) {
759 zlog_warn("Could not setsockopt SO_BINDTODEVICE: %s",
760 safe_strerror(errno
));
768 pim
->mroute_socket
= fd
;
769 if (pim_mroute_set(pim
, 1)) {
771 "Could not enable mroute on socket fd=%d: errno=%d: %s",
772 fd
, errno
, safe_strerror(errno
));
774 pim
->mroute_socket
= -1;
778 pim
->mroute_socket_creation
= pim_time_monotonic_sec();
785 int pim_mroute_socket_disable(struct pim_instance
*pim
)
787 if (pim_mroute_set(pim
, 0)) {
789 "Could not disable mroute on socket fd=%d: errno=%d: %s",
790 pim
->mroute_socket
, errno
, safe_strerror(errno
));
794 if (close(pim
->mroute_socket
)) {
795 zlog_warn("Failure closing mroute socket: fd=%d errno=%d: %s",
796 pim
->mroute_socket
, errno
, safe_strerror(errno
));
800 mroute_read_off(pim
);
801 pim
->mroute_socket
= -1;
807 For each network interface (e.g., physical or a virtual tunnel) that
808 would be used for multicast forwarding, a corresponding multicast
809 interface must be added to the kernel.
811 int pim_mroute_add_vif(struct interface
*ifp
, struct in_addr ifaddr
,
814 struct pim_interface
*pim_ifp
= ifp
->info
;
818 if (PIM_DEBUG_MROUTE
)
819 zlog_debug("%s: Add Vif %d (%s[%s])", __PRETTY_FUNCTION__
,
820 pim_ifp
->mroute_vif_index
, ifp
->name
,
821 pim_ifp
->pim
->vrf
->name
);
823 memset(&vc
, 0, sizeof(vc
));
824 vc
.vifc_vifi
= pim_ifp
->mroute_vif_index
;
825 #ifdef VIFF_USE_IFINDEX
826 vc
.vifc_lcl_ifindex
= ifp
->ifindex
;
828 if (ifaddr
.s_addr
== INADDR_ANY
) {
830 "%s: unnumbered interfaces are not supported on this platform",
831 __PRETTY_FUNCTION__
);
834 memcpy(&vc
.vifc_lcl_addr
, &ifaddr
, sizeof(vc
.vifc_lcl_addr
));
836 vc
.vifc_flags
= flags
;
837 vc
.vifc_threshold
= PIM_MROUTE_MIN_TTL
;
838 vc
.vifc_rate_limit
= 0;
840 #ifdef PIM_DVMRP_TUNNEL
841 if (vc
.vifc_flags
& VIFF_TUNNEL
) {
842 memcpy(&vc
.vifc_rmt_addr
, &vif_remote_addr
,
843 sizeof(vc
.vifc_rmt_addr
));
847 err
= setsockopt(pim_ifp
->pim
->mroute_socket
, IPPROTO_IP
, MRT_ADD_VIF
,
848 (void *)&vc
, sizeof(vc
));
850 char ifaddr_str
[INET_ADDRSTRLEN
];
852 pim_inet4_dump("<ifaddr?>", ifaddr
, ifaddr_str
,
856 "%s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_ADD_VIF,vif_index=%d,ifaddr=%s,flag=%d): errno=%d: %s",
857 __PRETTY_FUNCTION__
, pim_ifp
->pim
->mroute_socket
,
858 ifp
->ifindex
, ifaddr_str
, flags
, errno
,
859 safe_strerror(errno
));
866 int pim_mroute_del_vif(struct interface
*ifp
)
868 struct pim_interface
*pim_ifp
= ifp
->info
;
872 if (PIM_DEBUG_MROUTE
)
873 zlog_debug("%s: Del Vif %d (%s[%s])", __PRETTY_FUNCTION__
,
874 pim_ifp
->mroute_vif_index
, ifp
->name
,
875 pim_ifp
->pim
->vrf
->name
);
877 memset(&vc
, 0, sizeof(vc
));
878 vc
.vifc_vifi
= pim_ifp
->mroute_vif_index
;
880 err
= setsockopt(pim_ifp
->pim
->mroute_socket
, IPPROTO_IP
, MRT_DEL_VIF
,
881 (void *)&vc
, sizeof(vc
));
884 "%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_DEL_VIF,vif_index=%d): errno=%d: %s",
885 __FILE__
, __PRETTY_FUNCTION__
,
886 pim_ifp
->pim
->mroute_socket
, pim_ifp
->mroute_vif_index
,
887 errno
, safe_strerror(errno
));
894 int pim_mroute_add(struct channel_oil
*c_oil
, const char *name
)
896 struct pim_instance
*pim
= c_oil
->pim
;
899 int orig_iif_vif
= 0;
900 struct pim_interface
*pim_reg_ifp
;
902 bool pimreg_ttl_reset
= false;
903 struct pim_interface
*vxlan_ifp
;
905 bool orig_term_ttl_reset
= false;
907 pim
->mroute_add_last
= pim_time_monotonic_sec();
908 ++pim
->mroute_add_events
;
910 /* Do not install route if incoming interface is undefined. */
911 if (c_oil
->oil
.mfcc_parent
>= MAXVIFS
) {
912 if (PIM_DEBUG_MROUTE
) {
915 "%s(%s) %s Attempting to add vifi that is invalid to mroute table",
916 __PRETTY_FUNCTION__
, name
,
917 pim_channel_oil_dump(c_oil
, buf
, sizeof(buf
)));
922 /* The linux kernel *expects* the incoming
923 * vif to be part of the outgoing list
924 * in the case of a (*,G).
926 if (c_oil
->oil
.mfcc_origin
.s_addr
== INADDR_ANY
) {
927 orig
= c_oil
->oil
.mfcc_ttls
[c_oil
->oil
.mfcc_parent
];
928 c_oil
->oil
.mfcc_ttls
[c_oil
->oil
.mfcc_parent
] = 1;
932 /* suppress pimreg in the OIL if the mroute is not supposed to
933 * trigger register encapsulated data
935 if (PIM_UPSTREAM_FLAG_TEST_NO_PIMREG_DATA(c_oil
->up
->flags
)) {
936 pim_reg_ifp
= pim
->regiface
->info
;
938 c_oil
->oil
.mfcc_ttls
[pim_reg_ifp
->mroute_vif_index
];
939 c_oil
->oil
.mfcc_ttls
[pim_reg_ifp
->mroute_vif_index
] = 0;
940 /* remember to flip it back after MFC programming */
941 pimreg_ttl_reset
= true;
944 vxlan_ifp
= pim_vxlan_get_term_ifp(pim
);
945 /* 1. vxlan termination device must never be added to the
946 * origination mroute (and that can actually happen because
947 * of XG inheritance from the termination mroute) otherwise
948 * traffic will end up looping.
949 * 2. vxlan termination device should be removed from the non-DF
950 * to prevent duplicates to the overlay rxer
953 (PIM_UPSTREAM_FLAG_TEST_SRC_VXLAN_ORIG(c_oil
->up
->flags
) ||
954 PIM_UPSTREAM_FLAG_TEST_MLAG_NON_DF(c_oil
->up
->flags
))) {
955 orig_term_ttl_reset
= true;
957 c_oil
->oil
.mfcc_ttls
[vxlan_ifp
->mroute_vif_index
];
958 c_oil
->oil
.mfcc_ttls
[vxlan_ifp
->mroute_vif_index
] = 0;
963 * If we have an unresolved cache entry for the S,G
964 * it is owned by the pimreg for the incoming IIF
965 * So set pimreg as the IIF temporarily to cause
966 * the packets to be forwarded. Then set it
967 * to the correct IIF afterwords.
969 if (!c_oil
->installed
&& c_oil
->oil
.mfcc_origin
.s_addr
!= INADDR_ANY
970 && c_oil
->oil
.mfcc_parent
!= 0) {
971 orig_iif_vif
= c_oil
->oil
.mfcc_parent
;
972 c_oil
->oil
.mfcc_parent
= 0;
974 err
= setsockopt(pim
->mroute_socket
, IPPROTO_IP
, MRT_ADD_MFC
,
975 &c_oil
->oil
, sizeof(c_oil
->oil
));
977 if (!err
&& !c_oil
->installed
978 && c_oil
->oil
.mfcc_origin
.s_addr
!= INADDR_ANY
979 && orig_iif_vif
!= 0) {
980 c_oil
->oil
.mfcc_parent
= orig_iif_vif
;
981 err
= setsockopt(pim
->mroute_socket
, IPPROTO_IP
, MRT_ADD_MFC
,
982 &c_oil
->oil
, sizeof(c_oil
->oil
));
985 if (c_oil
->oil
.mfcc_origin
.s_addr
== INADDR_ANY
)
986 c_oil
->oil
.mfcc_ttls
[c_oil
->oil
.mfcc_parent
] = orig
;
988 if (pimreg_ttl_reset
)
989 c_oil
->oil
.mfcc_ttls
[pim_reg_ifp
->mroute_vif_index
] =
992 if (orig_term_ttl_reset
)
993 c_oil
->oil
.mfcc_ttls
[vxlan_ifp
->mroute_vif_index
] =
998 "%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_ADD_MFC): errno=%d: %s",
999 __FILE__
, __PRETTY_FUNCTION__
, pim
->mroute_socket
,
1000 errno
, safe_strerror(errno
));
1004 if (PIM_DEBUG_MROUTE
) {
1006 zlog_debug("%s(%s), vrf %s Added Route: %s",
1007 __PRETTY_FUNCTION__
, name
, pim
->vrf
->name
,
1008 pim_channel_oil_dump(c_oil
, buf
, sizeof(buf
)));
1011 c_oil
->installed
= 1;
1012 c_oil
->mroute_creation
= pim_time_monotonic_sec();
1017 int pim_mroute_del(struct channel_oil
*c_oil
, const char *name
)
1019 struct pim_instance
*pim
= c_oil
->pim
;
1022 pim
->mroute_del_last
= pim_time_monotonic_sec();
1023 ++pim
->mroute_del_events
;
1025 if (!c_oil
->installed
) {
1026 if (PIM_DEBUG_MROUTE
) {
1029 "%s %s: vifi %d for route is %s not installed, do not need to send del req. ",
1030 __FILE__
, __PRETTY_FUNCTION__
,
1031 c_oil
->oil
.mfcc_parent
,
1032 pim_channel_oil_dump(c_oil
, buf
, sizeof(buf
)));
1037 err
= setsockopt(pim
->mroute_socket
, IPPROTO_IP
, MRT_DEL_MFC
,
1038 &c_oil
->oil
, sizeof(c_oil
->oil
));
1040 if (PIM_DEBUG_MROUTE
)
1042 "%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_DEL_MFC): errno=%d: %s",
1043 __FILE__
, __PRETTY_FUNCTION__
,
1044 pim
->mroute_socket
, errno
,
1045 safe_strerror(errno
));
1049 if (PIM_DEBUG_MROUTE
) {
1051 zlog_debug("%s(%s), vrf %s Deleted Route: %s",
1052 __PRETTY_FUNCTION__
, name
, pim
->vrf
->name
,
1053 pim_channel_oil_dump(c_oil
, buf
, sizeof(buf
)));
1056 // Reset kernel installed flag
1057 c_oil
->installed
= 0;
1062 void pim_mroute_update_counters(struct channel_oil
*c_oil
)
1064 struct pim_instance
*pim
= c_oil
->pim
;
1065 struct sioc_sg_req sgreq
;
1067 c_oil
->cc
.oldpktcnt
= c_oil
->cc
.pktcnt
;
1068 c_oil
->cc
.oldbytecnt
= c_oil
->cc
.bytecnt
;
1069 c_oil
->cc
.oldwrong_if
= c_oil
->cc
.wrong_if
;
1071 if (!c_oil
->installed
) {
1072 c_oil
->cc
.lastused
= 100 * pim
->keep_alive_time
;
1073 if (PIM_DEBUG_MROUTE
) {
1074 struct prefix_sg sg
;
1076 sg
.src
= c_oil
->oil
.mfcc_origin
;
1077 sg
.grp
= c_oil
->oil
.mfcc_mcastgrp
;
1078 if (PIM_DEBUG_MROUTE
)
1080 "Channel%s is not installed no need to collect data from kernel",
1081 pim_str_sg_dump(&sg
));
1086 memset(&sgreq
, 0, sizeof(sgreq
));
1087 sgreq
.src
= c_oil
->oil
.mfcc_origin
;
1088 sgreq
.grp
= c_oil
->oil
.mfcc_mcastgrp
;
1090 pim_zlookup_sg_statistics(c_oil
);
1091 if (ioctl(pim
->mroute_socket
, SIOCGETSGCNT
, &sgreq
)) {
1092 if (PIM_DEBUG_MROUTE
) {
1093 struct prefix_sg sg
;
1095 sg
.src
= c_oil
->oil
.mfcc_origin
;
1096 sg
.grp
= c_oil
->oil
.mfcc_mcastgrp
;
1099 "ioctl(SIOCGETSGCNT=%lu) failure for (S,G)=%s: errno=%d: %s",
1100 (unsigned long)SIOCGETSGCNT
,
1101 pim_str_sg_dump(&sg
), errno
,
1102 safe_strerror(errno
));
1107 c_oil
->cc
.pktcnt
= sgreq
.pktcnt
;
1108 c_oil
->cc
.bytecnt
= sgreq
.bytecnt
;
1109 c_oil
->cc
.wrong_if
= sgreq
.wrong_if
;