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"
29 #include "lib/network.h"
33 #include "pim_mroute.h"
37 #include "pim_iface.h"
38 #include "pim_macro.h"
41 #include "pim_register.h"
42 #include "pim_ifchannel.h"
43 #include "pim_zlookup.h"
46 #include "pim_vxlan.h"
49 static void mroute_read_on(struct pim_instance
*pim
);
51 int pim_mroute_set(struct pim_instance
*pim
, int enable
)
55 socklen_t data_len
= sizeof(data
);
58 * We need to create the VRF table for the pim mroute_socket
60 if (pim
->vrf
->vrf_id
!= VRF_DEFAULT
) {
61 frr_with_privs (&pimd_privs
) {
63 data
= pim
->vrf
->data
.l
.table_id
;
64 err
= setsockopt(pim
->mroute_socket
, PIM_IPPROTO
,
65 MRT_TABLE
, &data
, data_len
);
68 "%s %s: failure: setsockopt(fd=%d,PIM_IPPROTO, MRT_TABLE=%d): errno=%d: %s",
69 __FILE__
, __func__
, pim
->mroute_socket
,
70 data
, errno
, safe_strerror(errno
));
76 frr_with_privs (&pimd_privs
) {
77 opt
= enable
? MRT_INIT
: MRT_DONE
;
79 * *BSD *cares* about what value we pass down
83 err
= setsockopt(pim
->mroute_socket
, PIM_IPPROTO
, opt
, &data
,
87 "%s %s: failure: setsockopt(fd=%d,PIM_IPPROTO,%s=%d): errno=%d: %s",
88 __FILE__
, __func__
, pim
->mroute_socket
,
89 enable
? "MRT_INIT" : "MRT_DONE", data
, errno
,
90 safe_strerror(errno
));
95 #if defined(HAVE_IP_PKTINFO)
97 /* Linux and Solaris IP_PKTINFO */
99 if (setsockopt(pim
->mroute_socket
, PIM_IPPROTO
, IP_PKTINFO
,
102 "Could not set IP_PKTINFO on socket fd=%d: errno=%d: %s",
103 pim
->mroute_socket
, errno
,
104 safe_strerror(errno
));
111 /* Linux and Solaris IPV6_PKTINFO */
113 if (setsockopt(pim
->mroute_socket
, PIM_IPPROTO
,
114 IPV6_RECVPKTINFO
, &data
, data_len
)) {
116 "Could not set IPV6_RECVPKTINFO on socket fd=%d: errno=%d: %s",
117 pim
->mroute_socket
, errno
,
118 safe_strerror(errno
));
122 setsockopt_so_recvbuf(pim
->mroute_socket
, 1024 * 1024 * 8);
124 if (set_nonblocking(pim
->mroute_socket
) < 0) {
126 "Could not set non blocking on socket fd=%d: errno=%d: %s",
127 pim
->mroute_socket
, errno
, safe_strerror(errno
));
133 int upcalls
= GMMSG_WRVIFWHOLE
;
136 err
= setsockopt(pim
->mroute_socket
, PIM_IPPROTO
, opt
, &upcalls
,
140 "Failure to register for VIFWHOLE and WRONGVIF upcalls %d %s",
141 errno
, safe_strerror(errno
));
146 "PIM-SM will not work properly on this platform, until the ability to receive the WRVIFWHOLE upcall");
153 static const char *const gmmsgtype2str
[GMMSG_WRVIFWHOLE
+ 1] = {
154 "<unknown_upcall?>", "NOCACHE", "WRONGVIF", "WHOLEPKT", "WRVIFWHOLE"};
157 int pim_mroute_msg_nocache(int fd
, struct interface
*ifp
, const kernmsg
*msg
)
159 struct pim_interface
*pim_ifp
= ifp
->info
;
160 struct pim_upstream
*up
;
164 rpg
= pim_ifp
? RP(pim_ifp
->pim
, msg
->msg_im_dst
) : NULL
;
166 * If the incoming interface is unknown OR
167 * the Interface type is SSM we don't need to
170 if (!rpg
|| pim_rpf_addr_is_inaddr_any(rpg
)) {
171 if (PIM_DEBUG_MROUTE_DETAIL
)
173 "%s: Interface is not configured correctly to handle incoming packet: Could be !pim_ifp, !SM, !RP",
180 * If we've received a multicast packet that isn't connected to
183 if (!pim_if_connected_to_source(ifp
, msg
->msg_im_src
)) {
184 if (PIM_DEBUG_MROUTE_DETAIL
)
186 "%s: Received incoming packet that doesn't originate on our seg",
191 memset(&sg
, 0, sizeof(sg
));
192 sg
.src
= msg
->msg_im_src
;
193 sg
.grp
= msg
->msg_im_dst
;
195 if (!(PIM_I_am_DR(pim_ifp
))) {
196 if (PIM_DEBUG_MROUTE_DETAIL
)
198 "%s: Interface is not the DR blackholing incoming traffic for %pSG",
202 * We are not the DR, but we are still receiving packets
203 * Let's blackhole those packets for the moment
204 * As that they will be coming up to the cpu
205 * and causing us to consider them.
207 * This *will* create a dangling channel_oil
208 * that I see no way to get rid of. Just noting
209 * this for future reference.
211 up
= pim_upstream_find_or_add(
212 &sg
, ifp
, PIM_UPSTREAM_FLAG_MASK_SRC_NOCACHE
, __func__
);
213 pim_upstream_mroute_add(up
->channel_oil
, __func__
);
218 up
= pim_upstream_find_or_add(&sg
, ifp
, PIM_UPSTREAM_FLAG_MASK_FHR
,
222 * I moved this debug till after the actual add because
223 * I want to take advantage of the up->sg_str being filled in.
225 if (PIM_DEBUG_MROUTE
) {
226 zlog_debug("%s: Adding a Route %s for WHOLEPKT consumption",
227 __func__
, up
->sg_str
);
230 PIM_UPSTREAM_FLAG_SET_SRC_STREAM(up
->flags
);
231 pim_upstream_keep_alive_timer_start(up
, pim_ifp
->pim
->keep_alive_time
);
233 up
->channel_oil
->cc
.pktcnt
++;
234 // resolve mfcc_parent prior to mroute_add in channel_add_oif
235 if (up
->rpf
.source_nexthop
.interface
&&
236 *oil_parent(up
->channel_oil
) >= MAXVIFS
) {
237 pim_upstream_mroute_iif_update(up
->channel_oil
, __func__
);
239 pim_register_join(up
);
240 /* if we have receiver, inherit from parent */
241 pim_upstream_inherited_olist_decide(pim_ifp
->pim
, up
);
246 int pim_mroute_msg_wholepkt(int fd
, struct interface
*ifp
, const char *buf
,
249 struct pim_interface
*pim_ifp
;
252 const ipv_hdr
*ip_hdr
;
253 struct pim_upstream
*up
;
257 ip_hdr
= (const ipv_hdr
*)buf
;
259 memset(&sg
, 0, sizeof(sg
));
260 sg
.src
= IPV_SRC(ip_hdr
);
261 sg
.grp
= IPV_DST(ip_hdr
);
263 up
= pim_upstream_find(pim_ifp
->pim
, &sg
);
265 pim_sgaddr star
= sg
;
266 star
.src
= PIMADDR_ANY
;
268 up
= pim_upstream_find(pim_ifp
->pim
, &star
);
270 if (up
&& PIM_UPSTREAM_FLAG_TEST_CAN_BE_LHR(up
->flags
)) {
271 up
= pim_upstream_add(pim_ifp
->pim
, &sg
, ifp
,
272 PIM_UPSTREAM_FLAG_MASK_SRC_LHR
,
275 if (PIM_DEBUG_MROUTE
)
277 "%s: Unable to create upstream information for %pSG",
281 pim_upstream_keep_alive_timer_start(
282 up
, pim_ifp
->pim
->keep_alive_time
);
283 pim_upstream_inherited_olist(pim_ifp
->pim
, up
);
284 pim_upstream_update_join_desired(pim_ifp
->pim
, up
);
286 if (PIM_DEBUG_MROUTE
)
287 zlog_debug("%s: Creating %s upstream on LHR",
288 __func__
, up
->sg_str
);
291 if (PIM_DEBUG_MROUTE_DETAIL
) {
293 "%s: Unable to find upstream channel WHOLEPKT%pSG",
299 if (!up
->rpf
.source_nexthop
.interface
) {
300 if (PIM_DEBUG_PIM_TRACE
)
301 zlog_debug("%s: up %s RPF is not present", __func__
,
306 pim_ifp
= up
->rpf
.source_nexthop
.interface
->info
;
308 rpg
= pim_ifp
? RP(pim_ifp
->pim
, sg
.grp
) : NULL
;
310 if ((pim_rpf_addr_is_inaddr_any(rpg
)) || (!pim_ifp
) ||
311 (!(PIM_I_am_DR(pim_ifp
)))) {
312 if (PIM_DEBUG_MROUTE
) {
313 zlog_debug("%s: Failed Check send packet", __func__
);
319 * If we've received a register suppress
321 if (!up
->t_rs_timer
) {
322 if (pim_is_grp_ssm(pim_ifp
->pim
, sg
.grp
)) {
323 if (PIM_DEBUG_PIM_REG
)
325 "%pSG register forward skipped as group is SSM",
330 if (!PIM_UPSTREAM_FLAG_TEST_FHR(up
->flags
)) {
331 if (PIM_DEBUG_PIM_REG
)
333 "%s register forward skipped, not FHR",
338 pim_register_send((uint8_t *)buf
+ sizeof(ipv_hdr
),
339 len
- sizeof(ipv_hdr
),
340 pim_ifp
->primary_address
, rpg
, 0, up
);
345 int pim_mroute_msg_wrongvif(int fd
, struct interface
*ifp
, const kernmsg
*msg
)
347 struct pim_ifchannel
*ch
;
348 struct pim_interface
*pim_ifp
;
351 memset(&sg
, 0, sizeof(sg
));
352 sg
.src
= msg
->msg_im_src
;
353 sg
.grp
= msg
->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)=%pSG could not find input interface for input_vif_index=%d",
370 __func__
, &sg
, msg
->msg_im_vif
);
376 if (PIM_DEBUG_MROUTE
)
378 "%s: WRONGVIF (S,G)=%pSG multicast not enabled on interface %s",
379 __func__
, &sg
, ifp
->name
);
383 ch
= pim_ifchannel_find(ifp
, &sg
);
385 pim_sgaddr star_g
= sg
;
386 if (PIM_DEBUG_MROUTE
)
388 "%s: WRONGVIF (S,G)=%pSG could not find channel on interface %s",
389 __func__
, &sg
, ifp
->name
);
391 star_g
.src
= PIMADDR_ANY
;
392 ch
= pim_ifchannel_find(ifp
, &star_g
);
394 if (PIM_DEBUG_MROUTE
)
396 "%s: WRONGVIF (*,G)=%pSG could not find channel on interface %s",
397 __func__
, &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 __func__
, 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 __func__
, 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 __func__
, ch
->sg_str
, ifp
->name
);
446 int pim_mroute_msg_wrvifwhole(int fd
, struct interface
*ifp
, const char *buf
,
449 const ipv_hdr
*ip_hdr
= (const ipv_hdr
*)buf
;
450 struct pim_interface
*pim_ifp
;
451 struct pim_instance
*pim
;
452 struct pim_ifchannel
*ch
;
453 struct pim_upstream
*up
;
459 memset(&sg
, 0, sizeof(sg
));
460 sg
.src
= IPV_SRC(ip_hdr
);
461 sg
.grp
= IPV_DST(ip_hdr
);
463 ch
= pim_ifchannel_find(ifp
, &sg
);
465 if (PIM_DEBUG_MROUTE
)
467 "WRVIFWHOLE (S,G)=%s found ifchannel on interface %s",
468 ch
->sg_str
, ifp
->name
);
473 star_g
.src
= PIMADDR_ANY
;
477 * If the incoming interface is the pimreg, then
478 * we know the callback is associated with a pim register
479 * packet and there is nothing to do here as that
480 * normal pim processing will see the packet and allow
481 * us to do the right thing.
483 if (ifp
== pim
->regiface
) {
487 up
= pim_upstream_find(pim_ifp
->pim
, &sg
);
489 struct pim_upstream
*parent
;
490 struct pim_nexthop source
;
491 struct pim_rpf
*rpf
= RP(pim_ifp
->pim
, sg
.grp
);
493 /* No RPF or No RPF interface or No mcast on RPF interface */
494 if (!rpf
|| !rpf
->source_nexthop
.interface
||
495 !rpf
->source_nexthop
.interface
->info
)
499 * If we have received a WRVIFWHOLE and are at this
500 * point, we could be receiving the packet on the *,G
501 * tree, let's check and if so we can safely drop
504 parent
= pim_upstream_find(pim_ifp
->pim
, &star_g
);
505 if (parent
&& parent
->rpf
.source_nexthop
.interface
== ifp
)
508 pim_ifp
= rpf
->source_nexthop
.interface
->info
;
510 memset(&source
, 0, sizeof(source
));
512 * If we are the fhr that means we are getting a callback during
513 * the pimreg period, so I believe we can ignore this packet
515 if (!PIM_UPSTREAM_FLAG_TEST_FHR(up
->flags
)) {
517 * No if channel, but upstream we are at the RP.
519 * This could be a anycast RP too and we may
520 * not have received a register packet from
521 * the source here at all. So gracefully
522 * bow out of doing a nexthop lookup and
523 * setting the SPTBIT to true
525 if (!(pim_addr_is_any(up
->upstream_register
)) &&
526 pim_nexthop_lookup(pim_ifp
->pim
, &source
,
527 up
->upstream_register
, 0)) {
528 pim_register_stop_send(source
.interface
, &sg
,
529 pim_ifp
->primary_address
,
530 up
->upstream_register
);
531 up
->sptbit
= PIM_UPSTREAM_SPTBIT_TRUE
;
534 pim_upstream_inherited_olist(pim_ifp
->pim
, up
);
535 if (!up
->channel_oil
->installed
)
536 pim_upstream_mroute_add(up
->channel_oil
,
539 if (I_am_RP(pim_ifp
->pim
, up
->sg
.grp
)) {
540 if (pim_nexthop_lookup(pim_ifp
->pim
, &source
,
541 up
->upstream_register
,
543 pim_register_stop_send(
544 source
.interface
, &sg
,
545 pim_ifp
->primary_address
,
546 up
->upstream_register
);
547 up
->sptbit
= PIM_UPSTREAM_SPTBIT_TRUE
;
550 * At this point pimd is connected to
551 * the source, it has a parent, we are not
552 * the RP and the SPTBIT should be set
553 * since we know *the* S,G is on the SPT.
554 * The first time this happens, let's cause
555 * an immediate join to go out so that
556 * the RP can trim this guy immediately
557 * if necessary, instead of waiting
558 * one join/prune send cycle
560 if (up
->sptbit
!= PIM_UPSTREAM_SPTBIT_TRUE
&&
562 up
->rpf
.source_nexthop
.interface
!=
563 up
->parent
->rpf
.source_nexthop
565 up
->sptbit
= PIM_UPSTREAM_SPTBIT_TRUE
;
566 pim_jp_agg_single_upstream_send(
567 &up
->parent
->rpf
, up
->parent
,
571 pim_upstream_keep_alive_timer_start(
572 up
, pim_ifp
->pim
->keep_alive_time
);
573 pim_upstream_inherited_olist(pim_ifp
->pim
, up
);
574 pim_mroute_msg_wholepkt(fd
, ifp
, buf
, len
);
580 if (pim_if_connected_to_source(ifp
, sg
.src
)) {
581 up
= pim_upstream_add(pim_ifp
->pim
, &sg
, ifp
,
582 PIM_UPSTREAM_FLAG_MASK_FHR
, __func__
,
585 if (PIM_DEBUG_MROUTE
)
587 "%pSG: WRONGVIF%s unable to create upstream on interface",
591 PIM_UPSTREAM_FLAG_SET_SRC_STREAM(up
->flags
);
592 pim_upstream_keep_alive_timer_start(
593 up
, pim_ifp
->pim
->keep_alive_time
);
594 up
->channel_oil
->cc
.pktcnt
++;
595 pim_register_join(up
);
596 pim_upstream_inherited_olist(pim_ifp
->pim
, up
);
597 if (!up
->channel_oil
->installed
)
598 pim_upstream_mroute_add(up
->channel_oil
, __func__
);
600 // Send the packet to the RP
601 pim_mroute_msg_wholepkt(fd
, ifp
, buf
, len
);
603 up
= pim_upstream_add(pim_ifp
->pim
, &sg
, ifp
,
604 PIM_UPSTREAM_FLAG_MASK_SRC_NOCACHE
,
606 if (!up
->channel_oil
->installed
)
607 pim_upstream_mroute_add(up
->channel_oil
, __func__
);
614 static int process_igmp_packet(struct pim_instance
*pim
, const char *buf
,
615 size_t buf_size
, ifindex_t ifindex
)
617 struct interface
*ifp
;
618 struct pim_interface
*pim_ifp
;
619 struct in_addr ifaddr
;
620 struct gm_sock
*igmp
;
621 const struct prefix
*connected_src
;
622 const struct ip
*ip_hdr
= (const struct ip
*)buf
;
624 /* We have the IP packet but we do not know which interface this
626 * received on. Find the interface that is on the same subnet as
630 ifp
= if_lookup_by_index(ifindex
, pim
->vrf
->vrf_id
);
632 if (!ifp
|| !ifp
->info
)
635 connected_src
= pim_if_connected_to_source(ifp
, ip_hdr
->ip_src
);
637 if (!connected_src
&& !pim_addr_is_any(ip_hdr
->ip_src
)) {
638 if (PIM_DEBUG_GM_PACKETS
) {
640 "Recv IGMP packet on interface: %s from a non-connected source: %pI4",
641 ifp
->name
, &ip_hdr
->ip_src
);
647 ifaddr
= connected_src
? connected_src
->u
.prefix4
648 : pim_ifp
->primary_address
;
649 igmp
= pim_igmp_sock_lookup_ifaddr(pim_ifp
->gm_socket_list
, ifaddr
);
651 if (PIM_DEBUG_GM_PACKETS
) {
653 "%s(%s): igmp kernel upcall on %s(%p) for %pI4 -> %pI4",
654 __func__
, pim
->vrf
->name
, ifp
->name
, igmp
,
655 &ip_hdr
->ip_src
, &ip_hdr
->ip_dst
);
658 pim_igmp_packet(igmp
, (char *)buf
, buf_size
);
659 else if (PIM_DEBUG_GM_PACKETS
)
661 "No IGMP socket on interface: %s with connected source: %pI4",
668 int pim_mroute_msg(struct pim_instance
*pim
, const char *buf
, size_t buf_size
,
671 struct interface
*ifp
;
672 const ipv_hdr
*ip_hdr
;
675 if (buf_size
< (int)sizeof(ipv_hdr
))
678 ip_hdr
= (const ipv_hdr
*)buf
;
681 if (ip_hdr
->ip_p
== IPPROTO_IGMP
) {
682 process_igmp_packet(pim
, buf
, buf_size
, ifindex
);
683 } else if (ip_hdr
->ip_p
) {
684 if (PIM_DEBUG_MROUTE_DETAIL
) {
686 "%s: no kernel upcall proto=%d src: %pI4 dst: %pI4 msg_size=%ld",
687 __func__
, ip_hdr
->ip_p
, &ip_hdr
->ip_src
,
688 &ip_hdr
->ip_dst
, (long int)buf_size
);
694 if ((ip_hdr
->ip6_vfc
& 0xf) == 0) {
696 msg
= (const kernmsg
*)buf
;
698 ifp
= pim_if_find_by_vif_index(pim
, msg
->msg_im_vif
);
702 if (PIM_DEBUG_MROUTE
) {
705 "%s: pim kernel upcall %s type=%d ip_p=%d from fd=%d for (S,G)=(%pI4,%pI4) on %s vifi=%d size=%ld",
706 __func__
, gmmsgtype2str
[msg
->msg_im_msgtype
],
707 msg
->msg_im_msgtype
, ip_hdr
->ip_p
,
708 pim
->mroute_socket
, &msg
->msg_im_src
,
709 &msg
->msg_im_dst
, ifp
->name
, msg
->msg_im_vif
,
713 "%s: pim kernel upcall %s type=%d ip_p=%d from fd=%d for (S,G)=(%pI6,%pI6) on %s vifi=%d size=%ld",
714 __func__
, gmmsgtype2str
[msg
->msg_im_msgtype
],
715 msg
->msg_im_msgtype
, ip_hdr
->ip6_nxt
,
716 pim
->mroute_socket
, &msg
->msg_im_src
,
717 &msg
->msg_im_dst
, ifp
->name
, msg
->msg_im_vif
,
722 switch (msg
->msg_im_msgtype
) {
724 return pim_mroute_msg_wrongvif(pim
->mroute_socket
, ifp
,
727 return pim_mroute_msg_nocache(pim
->mroute_socket
, ifp
,
730 return pim_mroute_msg_wholepkt(pim
->mroute_socket
, ifp
,
733 case GMMSG_WRVIFWHOLE
:
734 return pim_mroute_msg_wrvifwhole(pim
->mroute_socket
,
735 ifp
, (const char *)msg
,
745 static void mroute_read(struct thread
*t
)
747 struct pim_instance
*pim
;
748 static long long count
;
756 rd
= pim_socket_recvfromto(pim
->mroute_socket
, (uint8_t *)buf
,
757 sizeof(buf
), NULL
, NULL
, NULL
, NULL
,
762 if (errno
== EWOULDBLOCK
|| errno
== EAGAIN
)
766 "%s: failure reading rd=%d: fd=%d: errno=%d: %s",
767 __func__
, rd
, pim
->mroute_socket
, errno
,
768 safe_strerror(errno
));
772 pim_mroute_msg(pim
, buf
, rd
, ifindex
);
775 if (count
% router
->packet_process
== 0)
785 static void mroute_read_on(struct pim_instance
*pim
)
787 thread_add_read(router
->master
, mroute_read
, pim
, pim
->mroute_socket
,
791 static void mroute_read_off(struct pim_instance
*pim
)
793 THREAD_OFF(pim
->thread
);
796 int pim_mroute_socket_enable(struct pim_instance
*pim
)
800 frr_with_privs(&pimd_privs
) {
803 fd
= socket(AF_INET
, SOCK_RAW
, IPPROTO_IGMP
);
805 fd
= socket(AF_INET6
, SOCK_RAW
, IPPROTO_ICMPV6
);
808 zlog_warn("Could not create mroute socket: errno=%d: %s",
810 safe_strerror(errno
));
815 struct icmp6_filter filter
[1];
818 /* Unlike IPv4, this socket is not used for MLD, so just drop
819 * everything with an empty ICMP6 filter. Otherwise we get
820 * all kinds of garbage here, possibly even non-multicast
821 * related ICMPv6 traffic (e.g. ping)
823 * (mroute kernel upcall "packets" are injected directly on the
824 * socket, this sockopt -or any other- has no effect on them)
826 ICMP6_FILTER_SETBLOCKALL(filter
);
827 ret
= setsockopt(fd
, SOL_ICMPV6
, ICMP6_FILTER
, filter
,
831 "(VRF %s) failed to set mroute control filter: %m",
835 #ifdef SO_BINDTODEVICE
836 if (pim
->vrf
->vrf_id
!= VRF_DEFAULT
837 && setsockopt(fd
, SOL_SOCKET
, SO_BINDTODEVICE
,
838 pim
->vrf
->name
, strlen(pim
->vrf
->name
))) {
839 zlog_warn("Could not setsockopt SO_BINDTODEVICE: %s",
840 safe_strerror(errno
));
848 pim
->mroute_socket
= fd
;
849 if (pim_mroute_set(pim
, 1)) {
851 "Could not enable mroute on socket fd=%d: errno=%d: %s",
852 fd
, errno
, safe_strerror(errno
));
854 pim
->mroute_socket
= -1;
858 pim
->mroute_socket_creation
= pim_time_monotonic_sec();
865 int pim_mroute_socket_disable(struct pim_instance
*pim
)
867 if (pim_mroute_set(pim
, 0)) {
869 "Could not disable mroute on socket fd=%d: errno=%d: %s",
870 pim
->mroute_socket
, errno
, safe_strerror(errno
));
874 if (close(pim
->mroute_socket
)) {
875 zlog_warn("Failure closing mroute socket: fd=%d errno=%d: %s",
876 pim
->mroute_socket
, errno
, safe_strerror(errno
));
880 mroute_read_off(pim
);
881 pim
->mroute_socket
= -1;
887 For each network interface (e.g., physical or a virtual tunnel) that
888 would be used for multicast forwarding, a corresponding multicast
889 interface must be added to the kernel.
891 int pim_mroute_add_vif(struct interface
*ifp
, pim_addr ifaddr
,
894 struct pim_interface
*pim_ifp
= ifp
->info
;
898 if (PIM_DEBUG_MROUTE
)
899 zlog_debug("%s: Add Vif %d (%s[%s])", __func__
,
900 pim_ifp
->mroute_vif_index
, ifp
->name
,
901 pim_ifp
->pim
->vrf
->name
);
903 memset(&vc
, 0, sizeof(vc
));
904 vc
.vc_vifi
= pim_ifp
->mroute_vif_index
;
906 #ifdef VIFF_USE_IFINDEX
907 vc
.vc_lcl_ifindex
= ifp
->ifindex
;
909 if (ifaddr
.s_addr
== INADDR_ANY
) {
911 "%s: unnumbered interfaces are not supported on this platform",
915 memcpy(&vc
.vc_lcl_addr
, &ifaddr
, sizeof(vc
.vc_lcl_addr
));
918 vc
.vc_pifi
= ifp
->ifindex
;
921 vc
.vc_threshold
= PIM_MROUTE_MIN_TTL
;
922 vc
.vc_rate_limit
= 0;
925 #ifdef PIM_DVMRP_TUNNEL
926 if (vc
.vc_flags
& VIFF_TUNNEL
) {
927 memcpy(&vc
.vc_rmt_addr
, &vif_remote_addr
,
928 sizeof(vc
.vc_rmt_addr
));
933 err
= setsockopt(pim_ifp
->pim
->mroute_socket
, PIM_IPPROTO
, MRT_ADD_VIF
,
934 (void *)&vc
, sizeof(vc
));
937 "%s: failure: setsockopt(fd=%d,PIM_IPPROTO,MRT_ADD_VIF,vif_index=%d,ifaddr=%pPAs,flag=%d): errno=%d: %s",
938 __func__
, pim_ifp
->pim
->mroute_socket
, ifp
->ifindex
,
939 &ifaddr
, flags
, errno
, safe_strerror(errno
));
946 int pim_mroute_del_vif(struct interface
*ifp
)
948 struct pim_interface
*pim_ifp
= ifp
->info
;
952 if (PIM_DEBUG_MROUTE
)
953 zlog_debug("%s: Del Vif %d (%s[%s])", __func__
,
954 pim_ifp
->mroute_vif_index
, ifp
->name
,
955 pim_ifp
->pim
->vrf
->name
);
957 memset(&vc
, 0, sizeof(vc
));
958 vc
.vc_vifi
= pim_ifp
->mroute_vif_index
;
960 err
= setsockopt(pim_ifp
->pim
->mroute_socket
, PIM_IPPROTO
, MRT_DEL_VIF
,
961 (void *)&vc
, sizeof(vc
));
964 "%s %s: failure: setsockopt(fd=%d,PIM_IPPROTO,MRT_DEL_VIF,vif_index=%d): errno=%d: %s",
965 __FILE__
, __func__
, pim_ifp
->pim
->mroute_socket
,
966 pim_ifp
->mroute_vif_index
, errno
, safe_strerror(errno
));
974 * Prevent creating MFC entry with OIF=IIF.
976 * This is a protection against implementation mistakes.
978 * PIM protocol implicitely ensures loopfree multicast topology.
980 * IGMP must be protected against adding looped MFC entries created
981 * by both source and receiver attached to the same interface. See
983 * We shall allow igmp to create upstream when it is DR for the intf.
984 * Assume RP reachable via non DR.
986 bool pim_mroute_allow_iif_in_oil(struct channel_oil
*c_oil
,
989 #ifdef PIM_ENFORCE_LOOPFREE_MFC
990 struct interface
*ifp_out
;
991 struct pim_interface
*pim_ifp
;
994 PIM_UPSTREAM_FLAG_TEST_ALLOW_IIF_IN_OIL(c_oil
->up
->flags
))
997 ifp_out
= pim_if_find_by_vif_index(c_oil
->pim
, oif_index
);
1000 pim_ifp
= ifp_out
->info
;
1003 if ((c_oil
->oif_flags
[oif_index
] & PIM_OIF_FLAG_PROTO_GM
) &&
1004 PIM_I_am_DR(pim_ifp
))
1013 static inline void pim_mroute_copy(struct channel_oil
*out
,
1014 struct channel_oil
*in
)
1018 *oil_origin(out
) = *oil_origin(in
);
1019 *oil_mcastgrp(out
) = *oil_mcastgrp(in
);
1020 *oil_parent(out
) = *oil_parent(in
);
1022 for (i
= 0; i
< MAXVIFS
; ++i
) {
1023 if (*oil_parent(out
) == i
&&
1024 !pim_mroute_allow_iif_in_oil(in
, i
)) {
1025 oil_if_set(out
, i
, 0);
1029 if (in
->oif_flags
[i
] & PIM_OIF_FLAG_MUTE
)
1030 oil_if_set(out
, i
, 0);
1032 oil_if_set(out
, i
, oil_if_has(in
, i
));
1036 /* This function must not be called directly 0
1037 * use pim_upstream_mroute_add or pim_static_mroute_add instead
1039 static int pim_mroute_add(struct channel_oil
*c_oil
, const char *name
)
1041 struct pim_instance
*pim
= c_oil
->pim
;
1042 struct channel_oil tmp_oil
[1] = { };
1045 pim
->mroute_add_last
= pim_time_monotonic_sec();
1046 ++pim
->mroute_add_events
;
1048 /* Copy the oil to a temporary structure to fixup (without need to
1049 * later restore) before sending the mroute add to the dataplane
1051 pim_mroute_copy(tmp_oil
, c_oil
);
1053 /* The linux kernel *expects* the incoming
1054 * vif to be part of the outgoing list
1055 * in the case of a (*,G).
1057 if (pim_addr_is_any(*oil_origin(c_oil
))) {
1058 oil_if_set(tmp_oil
, *oil_parent(c_oil
), 1);
1062 * If we have an unresolved cache entry for the S,G
1063 * it is owned by the pimreg for the incoming IIF
1064 * So set pimreg as the IIF temporarily to cause
1065 * the packets to be forwarded. Then set it
1066 * to the correct IIF afterwords.
1068 if (!c_oil
->installed
&& !pim_addr_is_any(*oil_origin(c_oil
))
1069 && *oil_parent(c_oil
) != 0) {
1070 *oil_parent(tmp_oil
) = 0;
1072 /* For IPv6 MRT_ADD_MFC is defined to MRT6_ADD_MFC */
1073 err
= setsockopt(pim
->mroute_socket
, PIM_IPPROTO
, MRT_ADD_MFC
,
1074 &tmp_oil
->oil
, sizeof(tmp_oil
->oil
));
1076 if (!err
&& !c_oil
->installed
1077 && !pim_addr_is_any(*oil_origin(c_oil
))
1078 && *oil_parent(c_oil
) != 0) {
1079 *oil_parent(tmp_oil
) = *oil_parent(c_oil
);
1080 err
= setsockopt(pim
->mroute_socket
, PIM_IPPROTO
, MRT_ADD_MFC
,
1081 &tmp_oil
->oil
, sizeof(tmp_oil
->oil
));
1086 "%s %s: failure: setsockopt(fd=%d,PIM_IPPROTO,MRT_ADD_MFC): errno=%d: %s",
1087 __FILE__
, __func__
, pim
->mroute_socket
, errno
,
1088 safe_strerror(errno
));
1092 if (PIM_DEBUG_MROUTE
) {
1094 zlog_debug("%s(%s), vrf %s Added Route: %s", __func__
, name
,
1096 pim_channel_oil_dump(c_oil
, buf
, sizeof(buf
)));
1099 if (!c_oil
->installed
) {
1100 c_oil
->installed
= 1;
1101 c_oil
->mroute_creation
= pim_time_monotonic_sec();
1107 static int pim_upstream_get_mroute_iif(struct channel_oil
*c_oil
,
1110 vifi_t iif
= MAXVIFS
;
1111 struct interface
*ifp
= NULL
;
1112 struct pim_interface
*pim_ifp
;
1113 struct pim_upstream
*up
= c_oil
->up
;
1116 if (PIM_UPSTREAM_FLAG_TEST_USE_RPT(up
->flags
)) {
1118 ifp
= up
->parent
->rpf
.source_nexthop
.interface
;
1120 ifp
= up
->rpf
.source_nexthop
.interface
;
1123 pim_ifp
= (struct pim_interface
*)ifp
->info
;
1125 iif
= pim_ifp
->mroute_vif_index
;
1131 static int pim_upstream_mroute_update(struct channel_oil
*c_oil
,
1136 if (*oil_parent(c_oil
) >= MAXVIFS
) {
1137 /* the c_oil cannot be installed as a mroute yet */
1138 if (PIM_DEBUG_MROUTE
)
1140 "%s(%s) %s mroute not ready to be installed; %s",
1142 pim_channel_oil_dump(c_oil
, buf
,
1145 "uninstall" : "skip");
1146 /* if already installed flush it out as we are going to stop
1147 * updates to it leaving it in a stale state
1149 if (c_oil
->installed
)
1150 pim_mroute_del(c_oil
, name
);
1151 /* return success (skipped) */
1155 return pim_mroute_add(c_oil
, name
);
1158 /* IIF associated with SGrpt entries are re-evaluated when the parent
1159 * (*,G) entries IIF changes
1161 static void pim_upstream_all_sources_iif_update(struct pim_upstream
*up
)
1163 struct listnode
*listnode
;
1164 struct pim_upstream
*child
;
1166 for (ALL_LIST_ELEMENTS_RO(up
->sources
, listnode
,
1168 if (PIM_UPSTREAM_FLAG_TEST_USE_RPT(child
->flags
))
1169 pim_upstream_mroute_iif_update(child
->channel_oil
,
1174 /* In the case of "PIM state machine" added mroutes an upstream entry
1175 * must be present to decide on the SPT-forwarding vs. RPT-forwarding.
1177 int pim_upstream_mroute_add(struct channel_oil
*c_oil
, const char *name
)
1181 iif
= pim_upstream_get_mroute_iif(c_oil
, name
);
1183 if (*oil_parent(c_oil
) != iif
) {
1184 *oil_parent(c_oil
) = iif
;
1185 if (pim_addr_is_any(*oil_origin(c_oil
)) &&
1187 pim_upstream_all_sources_iif_update(c_oil
->up
);
1189 *oil_parent(c_oil
) = iif
;
1192 return pim_upstream_mroute_update(c_oil
, name
);
1195 /* Look for IIF changes and update the dateplane entry only if the IIF
1198 int pim_upstream_mroute_iif_update(struct channel_oil
*c_oil
, const char *name
)
1203 iif
= pim_upstream_get_mroute_iif(c_oil
, name
);
1204 if (*oil_parent(c_oil
) == iif
) {
1208 *oil_parent(c_oil
) = iif
;
1210 if (pim_addr_is_any(*oil_origin(c_oil
)) &&
1212 pim_upstream_all_sources_iif_update(c_oil
->up
);
1214 if (PIM_DEBUG_MROUTE_DETAIL
)
1215 zlog_debug("%s(%s) %s mroute iif update %d",
1217 pim_channel_oil_dump(c_oil
, buf
,
1219 /* XXX: is this hack needed? */
1220 c_oil
->oil_inherited_rescan
= 1;
1221 return pim_upstream_mroute_update(c_oil
, name
);
1224 int pim_static_mroute_add(struct channel_oil
*c_oil
, const char *name
)
1226 return pim_mroute_add(c_oil
, name
);
1229 void pim_static_mroute_iif_update(struct channel_oil
*c_oil
,
1230 int input_vif_index
,
1233 if (*oil_parent(c_oil
) == input_vif_index
)
1236 *oil_parent(c_oil
) = input_vif_index
;
1237 if (input_vif_index
== MAXVIFS
)
1238 pim_mroute_del(c_oil
, name
);
1240 pim_static_mroute_add(c_oil
, name
);
1243 int pim_mroute_del(struct channel_oil
*c_oil
, const char *name
)
1245 struct pim_instance
*pim
= c_oil
->pim
;
1248 pim
->mroute_del_last
= pim_time_monotonic_sec();
1249 ++pim
->mroute_del_events
;
1251 if (!c_oil
->installed
) {
1252 if (PIM_DEBUG_MROUTE
) {
1255 "%s %s: vifi %d for route is %s not installed, do not need to send del req. ",
1256 __FILE__
, __func__
, *oil_parent(c_oil
),
1257 pim_channel_oil_dump(c_oil
, buf
, sizeof(buf
)));
1262 err
= setsockopt(pim
->mroute_socket
, PIM_IPPROTO
, MRT_DEL_MFC
,
1263 &c_oil
->oil
, sizeof(c_oil
->oil
));
1265 if (PIM_DEBUG_MROUTE
)
1267 "%s %s: failure: setsockopt(fd=%d,PIM_IPPROTO,MRT_DEL_MFC): errno=%d: %s",
1268 __FILE__
, __func__
, pim
->mroute_socket
, errno
,
1269 safe_strerror(errno
));
1273 if (PIM_DEBUG_MROUTE
) {
1275 zlog_debug("%s(%s), vrf %s Deleted Route: %s", __func__
, name
,
1277 pim_channel_oil_dump(c_oil
, buf
, sizeof(buf
)));
1280 // Reset kernel installed flag
1281 c_oil
->installed
= 0;
1286 void pim_mroute_update_counters(struct channel_oil
*c_oil
)
1288 struct pim_instance
*pim
= c_oil
->pim
;
1289 pim_sioc_sg_req sgreq
;
1291 c_oil
->cc
.oldpktcnt
= c_oil
->cc
.pktcnt
;
1292 c_oil
->cc
.oldbytecnt
= c_oil
->cc
.bytecnt
;
1293 c_oil
->cc
.oldwrong_if
= c_oil
->cc
.wrong_if
;
1295 if (!c_oil
->installed
) {
1296 c_oil
->cc
.lastused
= 100 * pim
->keep_alive_time
;
1297 if (PIM_DEBUG_MROUTE
) {
1300 sg
.src
= *oil_origin(c_oil
);
1301 sg
.grp
= *oil_mcastgrp(c_oil
);
1302 zlog_debug("Channel%pSG is not installed no need to collect data from kernel",
1309 memset(&sgreq
, 0, sizeof(sgreq
));
1311 pim_zlookup_sg_statistics(c_oil
);
1314 sgreq
.src
= *oil_origin(c_oil
);
1315 sgreq
.grp
= *oil_mcastgrp(c_oil
);
1317 sgreq
.src
= c_oil
->oil
.mf6cc_origin
;
1318 sgreq
.grp
= c_oil
->oil
.mf6cc_mcastgrp
;
1320 if (ioctl(pim
->mroute_socket
, PIM_SIOCGETSGCNT
, &sgreq
)) {
1323 sg
.src
= *oil_origin(c_oil
);
1324 sg
.grp
= *oil_mcastgrp(c_oil
);
1327 "ioctl(PIM_SIOCGETSGCNT=%lu) failure for (S,G)=%pSG: errno=%d: %s",
1328 (unsigned long)PIM_SIOCGETSGCNT
, &sg
, errno
,
1329 safe_strerror(errno
));
1333 c_oil
->cc
.pktcnt
= sgreq
.pktcnt
;
1334 c_oil
->cc
.bytecnt
= sgreq
.bytecnt
;
1335 c_oil
->cc
.wrong_if
= sgreq
.wrong_if
;