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 *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
) {
303 if (PIM_DEBUG_PIM_TRACE
)
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 zlog_warn("%s: failure reading rd=%d: fd=%d: errno=%d: %s",
702 __PRETTY_FUNCTION__
, rd
, pim
->mroute_socket
,
703 errno
, safe_strerror(errno
));
707 result
= pim_mroute_msg(pim
, buf
, rd
, ifindex
);
710 if (count
% router
->packet_process
== 0)
720 static void mroute_read_on(struct pim_instance
*pim
)
722 thread_add_read(router
->master
, mroute_read
, pim
, pim
->mroute_socket
,
726 static void mroute_read_off(struct pim_instance
*pim
)
728 THREAD_OFF(pim
->thread
);
731 int pim_mroute_socket_enable(struct pim_instance
*pim
)
735 frr_with_privs(&pimd_privs
) {
737 fd
= socket(AF_INET
, SOCK_RAW
, IPPROTO_IGMP
);
740 zlog_warn("Could not create mroute socket: errno=%d: %s",
742 safe_strerror(errno
));
746 #ifdef SO_BINDTODEVICE
747 if (pim
->vrf
->vrf_id
!= VRF_DEFAULT
748 && setsockopt(fd
, SOL_SOCKET
, SO_BINDTODEVICE
,
749 pim
->vrf
->name
, strlen(pim
->vrf
->name
))) {
750 zlog_warn("Could not setsockopt SO_BINDTODEVICE: %s",
751 safe_strerror(errno
));
759 pim
->mroute_socket
= fd
;
760 if (pim_mroute_set(pim
, 1)) {
762 "Could not enable mroute on socket fd=%d: errno=%d: %s",
763 fd
, errno
, safe_strerror(errno
));
765 pim
->mroute_socket
= -1;
769 pim
->mroute_socket_creation
= pim_time_monotonic_sec();
776 int pim_mroute_socket_disable(struct pim_instance
*pim
)
778 if (pim_mroute_set(pim
, 0)) {
780 "Could not disable mroute on socket fd=%d: errno=%d: %s",
781 pim
->mroute_socket
, errno
, safe_strerror(errno
));
785 if (close(pim
->mroute_socket
)) {
786 zlog_warn("Failure closing mroute socket: fd=%d errno=%d: %s",
787 pim
->mroute_socket
, errno
, safe_strerror(errno
));
791 mroute_read_off(pim
);
792 pim
->mroute_socket
= -1;
798 For each network interface (e.g., physical or a virtual tunnel) that
799 would be used for multicast forwarding, a corresponding multicast
800 interface must be added to the kernel.
802 int pim_mroute_add_vif(struct interface
*ifp
, struct in_addr ifaddr
,
805 struct pim_interface
*pim_ifp
= ifp
->info
;
809 if (PIM_DEBUG_MROUTE
)
810 zlog_debug("%s: Add Vif %d (%s[%s])", __PRETTY_FUNCTION__
,
811 pim_ifp
->mroute_vif_index
, ifp
->name
,
812 pim_ifp
->pim
->vrf
->name
);
814 memset(&vc
, 0, sizeof(vc
));
815 vc
.vifc_vifi
= pim_ifp
->mroute_vif_index
;
816 #ifdef VIFF_USE_IFINDEX
817 vc
.vifc_lcl_ifindex
= ifp
->ifindex
;
819 if (ifaddr
.s_addr
== INADDR_ANY
) {
821 "%s: unnumbered interfaces are not supported on this platform",
822 __PRETTY_FUNCTION__
);
825 memcpy(&vc
.vifc_lcl_addr
, &ifaddr
, sizeof(vc
.vifc_lcl_addr
));
827 vc
.vifc_flags
= flags
;
828 vc
.vifc_threshold
= PIM_MROUTE_MIN_TTL
;
829 vc
.vifc_rate_limit
= 0;
831 #ifdef PIM_DVMRP_TUNNEL
832 if (vc
.vifc_flags
& VIFF_TUNNEL
) {
833 memcpy(&vc
.vifc_rmt_addr
, &vif_remote_addr
,
834 sizeof(vc
.vifc_rmt_addr
));
838 err
= setsockopt(pim_ifp
->pim
->mroute_socket
, IPPROTO_IP
, MRT_ADD_VIF
,
839 (void *)&vc
, sizeof(vc
));
841 char ifaddr_str
[INET_ADDRSTRLEN
];
843 pim_inet4_dump("<ifaddr?>", ifaddr
, ifaddr_str
,
847 "%s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_ADD_VIF,vif_index=%d,ifaddr=%s,flag=%d): errno=%d: %s",
848 __PRETTY_FUNCTION__
, pim_ifp
->pim
->mroute_socket
,
849 ifp
->ifindex
, ifaddr_str
, flags
, errno
,
850 safe_strerror(errno
));
857 int pim_mroute_del_vif(struct interface
*ifp
)
859 struct pim_interface
*pim_ifp
= ifp
->info
;
863 if (PIM_DEBUG_MROUTE
)
864 zlog_debug("%s: Del Vif %d (%s[%s])", __PRETTY_FUNCTION__
,
865 pim_ifp
->mroute_vif_index
, ifp
->name
,
866 pim_ifp
->pim
->vrf
->name
);
868 memset(&vc
, 0, sizeof(vc
));
869 vc
.vifc_vifi
= pim_ifp
->mroute_vif_index
;
871 err
= setsockopt(pim_ifp
->pim
->mroute_socket
, IPPROTO_IP
, MRT_DEL_VIF
,
872 (void *)&vc
, sizeof(vc
));
875 "%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_DEL_VIF,vif_index=%d): errno=%d: %s",
876 __FILE__
, __PRETTY_FUNCTION__
,
877 pim_ifp
->pim
->mroute_socket
, pim_ifp
->mroute_vif_index
,
878 errno
, safe_strerror(errno
));
885 int pim_mroute_add(struct channel_oil
*c_oil
, const char *name
)
887 struct pim_instance
*pim
= c_oil
->pim
;
890 int orig_iif_vif
= 0;
891 struct pim_interface
*pim_reg_ifp
= NULL
;
892 int orig_pimreg_ttl
= 0;
893 bool pimreg_ttl_reset
= false;
894 struct pim_interface
*vxlan_ifp
= NULL
;
895 int orig_term_ttl
= 0;
896 bool orig_term_ttl_reset
= false;
898 pim
->mroute_add_last
= pim_time_monotonic_sec();
899 ++pim
->mroute_add_events
;
901 /* Do not install route if incoming interface is undefined. */
902 if (c_oil
->oil
.mfcc_parent
>= MAXVIFS
) {
903 if (PIM_DEBUG_MROUTE
) {
906 "%s(%s) %s Attempting to add vifi that is invalid to mroute table",
907 __PRETTY_FUNCTION__
, name
,
908 pim_channel_oil_dump(c_oil
, buf
, sizeof(buf
)));
913 /* The linux kernel *expects* the incoming
914 * vif to be part of the outgoing list
915 * in the case of a (*,G).
917 if (c_oil
->oil
.mfcc_origin
.s_addr
== INADDR_ANY
) {
918 orig
= c_oil
->oil
.mfcc_ttls
[c_oil
->oil
.mfcc_parent
];
919 c_oil
->oil
.mfcc_ttls
[c_oil
->oil
.mfcc_parent
] = 1;
923 /* suppress pimreg in the OIL if the mroute is not supposed to
924 * trigger register encapsulated data
926 if (PIM_UPSTREAM_FLAG_TEST_NO_PIMREG_DATA(c_oil
->up
->flags
)) {
927 pim_reg_ifp
= pim
->regiface
->info
;
929 c_oil
->oil
.mfcc_ttls
[pim_reg_ifp
->mroute_vif_index
];
930 c_oil
->oil
.mfcc_ttls
[pim_reg_ifp
->mroute_vif_index
] = 0;
931 /* remember to flip it back after MFC programming */
932 pimreg_ttl_reset
= true;
935 vxlan_ifp
= pim_vxlan_get_term_ifp(pim
);
936 /* 1. vxlan termination device must never be added to the
937 * origination mroute (and that can actually happen because
938 * of XG inheritance from the termination mroute) otherwise
939 * traffic will end up looping.
940 * 2. vxlan termination device should be removed from the non-DF
941 * to prevent duplicates to the overlay rxer
944 (PIM_UPSTREAM_FLAG_TEST_SRC_VXLAN_ORIG(c_oil
->up
->flags
) ||
945 PIM_UPSTREAM_FLAG_TEST_MLAG_NON_DF(c_oil
->up
->flags
))) {
946 orig_term_ttl_reset
= true;
948 c_oil
->oil
.mfcc_ttls
[vxlan_ifp
->mroute_vif_index
];
949 c_oil
->oil
.mfcc_ttls
[vxlan_ifp
->mroute_vif_index
] = 0;
954 * If we have an unresolved cache entry for the S,G
955 * it is owned by the pimreg for the incoming IIF
956 * So set pimreg as the IIF temporarily to cause
957 * the packets to be forwarded. Then set it
958 * to the correct IIF afterwords.
960 if (!c_oil
->installed
&& c_oil
->oil
.mfcc_origin
.s_addr
!= INADDR_ANY
961 && c_oil
->oil
.mfcc_parent
!= 0) {
962 orig_iif_vif
= c_oil
->oil
.mfcc_parent
;
963 c_oil
->oil
.mfcc_parent
= 0;
965 err
= setsockopt(pim
->mroute_socket
, IPPROTO_IP
, MRT_ADD_MFC
,
966 &c_oil
->oil
, sizeof(c_oil
->oil
));
968 if (!err
&& !c_oil
->installed
969 && c_oil
->oil
.mfcc_origin
.s_addr
!= INADDR_ANY
970 && orig_iif_vif
!= 0) {
971 c_oil
->oil
.mfcc_parent
= orig_iif_vif
;
972 err
= setsockopt(pim
->mroute_socket
, IPPROTO_IP
, MRT_ADD_MFC
,
973 &c_oil
->oil
, sizeof(c_oil
->oil
));
976 if (c_oil
->oil
.mfcc_origin
.s_addr
== INADDR_ANY
)
977 c_oil
->oil
.mfcc_ttls
[c_oil
->oil
.mfcc_parent
] = orig
;
979 if (pimreg_ttl_reset
) {
981 c_oil
->oil
.mfcc_ttls
[pim_reg_ifp
->mroute_vif_index
] =
985 if (orig_term_ttl_reset
)
986 c_oil
->oil
.mfcc_ttls
[vxlan_ifp
->mroute_vif_index
] =
991 "%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_ADD_MFC): errno=%d: %s",
992 __FILE__
, __PRETTY_FUNCTION__
, pim
->mroute_socket
,
993 errno
, safe_strerror(errno
));
997 if (PIM_DEBUG_MROUTE
) {
999 zlog_debug("%s(%s), vrf %s Added Route: %s",
1000 __PRETTY_FUNCTION__
, name
, pim
->vrf
->name
,
1001 pim_channel_oil_dump(c_oil
, buf
, sizeof(buf
)));
1004 c_oil
->installed
= 1;
1005 c_oil
->mroute_creation
= pim_time_monotonic_sec();
1010 int pim_mroute_del(struct channel_oil
*c_oil
, const char *name
)
1012 struct pim_instance
*pim
= c_oil
->pim
;
1015 pim
->mroute_del_last
= pim_time_monotonic_sec();
1016 ++pim
->mroute_del_events
;
1018 if (!c_oil
->installed
) {
1019 if (PIM_DEBUG_MROUTE
) {
1022 "%s %s: vifi %d for route is %s not installed, do not need to send del req. ",
1023 __FILE__
, __PRETTY_FUNCTION__
,
1024 c_oil
->oil
.mfcc_parent
,
1025 pim_channel_oil_dump(c_oil
, buf
, sizeof(buf
)));
1030 err
= setsockopt(pim
->mroute_socket
, IPPROTO_IP
, MRT_DEL_MFC
,
1031 &c_oil
->oil
, sizeof(c_oil
->oil
));
1033 if (PIM_DEBUG_MROUTE
)
1035 "%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_DEL_MFC): errno=%d: %s",
1036 __FILE__
, __PRETTY_FUNCTION__
,
1037 pim
->mroute_socket
, errno
,
1038 safe_strerror(errno
));
1042 if (PIM_DEBUG_MROUTE
) {
1044 zlog_debug("%s(%s), vrf %s Deleted Route: %s",
1045 __PRETTY_FUNCTION__
, name
, pim
->vrf
->name
,
1046 pim_channel_oil_dump(c_oil
, buf
, sizeof(buf
)));
1049 // Reset kernel installed flag
1050 c_oil
->installed
= 0;
1055 void pim_mroute_update_counters(struct channel_oil
*c_oil
)
1057 struct pim_instance
*pim
= c_oil
->pim
;
1058 struct sioc_sg_req sgreq
;
1060 c_oil
->cc
.oldpktcnt
= c_oil
->cc
.pktcnt
;
1061 c_oil
->cc
.oldbytecnt
= c_oil
->cc
.bytecnt
;
1062 c_oil
->cc
.oldwrong_if
= c_oil
->cc
.wrong_if
;
1064 if (!c_oil
->installed
) {
1065 c_oil
->cc
.lastused
= 100 * pim
->keep_alive_time
;
1066 if (PIM_DEBUG_MROUTE
) {
1067 struct prefix_sg sg
;
1069 sg
.src
= c_oil
->oil
.mfcc_origin
;
1070 sg
.grp
= c_oil
->oil
.mfcc_mcastgrp
;
1071 if (PIM_DEBUG_MROUTE
)
1073 "Channel%s is not installed no need to collect data from kernel",
1074 pim_str_sg_dump(&sg
));
1079 memset(&sgreq
, 0, sizeof(sgreq
));
1080 sgreq
.src
= c_oil
->oil
.mfcc_origin
;
1081 sgreq
.grp
= c_oil
->oil
.mfcc_mcastgrp
;
1083 pim_zlookup_sg_statistics(c_oil
);
1084 if (ioctl(pim
->mroute_socket
, SIOCGETSGCNT
, &sgreq
)) {
1085 struct prefix_sg sg
;
1087 sg
.src
= c_oil
->oil
.mfcc_origin
;
1088 sg
.grp
= c_oil
->oil
.mfcc_mcastgrp
;
1090 zlog_warn("ioctl(SIOCGETSGCNT=%lu) failure for (S,G)=%s: errno=%d: %s",
1091 (unsigned long)SIOCGETSGCNT
, pim_str_sg_dump(&sg
),
1092 errno
, safe_strerror(errno
));
1096 c_oil
->cc
.pktcnt
= sgreq
.pktcnt
;
1097 c_oil
->cc
.bytecnt
= sgreq
.bytecnt
;
1098 c_oil
->cc
.wrong_if
= sgreq
.wrong_if
;