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
31 #include "pim_mroute.h"
35 #include "pim_iface.h"
36 #include "pim_macro.h"
39 #include "pim_register.h"
40 #include "pim_ifchannel.h"
41 #include "pim_zlookup.h"
45 static void mroute_read_on(struct pim_instance
*pim
);
47 static int pim_mroute_set(struct pim_instance
*pim
, int enable
)
51 socklen_t opt_len
= sizeof(opt
);
55 * We need to create the VRF table for the pim mroute_socket
57 if (pim
->vrf_id
!= VRF_DEFAULT
) {
58 if (pimd_privs
.change(ZPRIVS_RAISE
))
60 "pim_mroute_socket_enable: could not raise privs, %s",
61 safe_strerror(errno
));
63 opt
= pim
->vrf
->data
.l
.table_id
;
64 err
= setsockopt(pim
->mroute_socket
, IPPROTO_IP
, MRT_TABLE
,
68 "%s %s: failure: setsockopt(fd=%d,IPPROTO_IP, MRT_TABLE=%d): errno=%d: %s",
69 __FILE__
, __PRETTY_FUNCTION__
,
70 pim
->mroute_socket
, opt
, errno
,
71 safe_strerror(errno
));
75 if (pimd_privs
.change(ZPRIVS_LOWER
))
77 "pim_mroute_socket_enable: could not lower privs, %s",
78 safe_strerror(errno
));
81 opt
= enable
? MRT_INIT
: MRT_DONE
;
82 err
= setsockopt(pim
->mroute_socket
, IPPROTO_IP
, opt
, &opt
, opt_len
);
85 "%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,%s=%d): errno=%d: %s",
86 __FILE__
, __PRETTY_FUNCTION__
, pim
->mroute_socket
,
87 enable
? "MRT_INIT" : "MRT_DONE", opt
, errno
,
88 safe_strerror(errno
));
92 #if defined(HAVE_IP_PKTINFO)
94 /* Linux and Solaris IP_PKTINFO */
96 if (setsockopt(pim
->mroute_socket
, IPPROTO_IP
, IP_PKTINFO
, &opt
,
99 "Could not set IP_PKTINFO on socket fd=%d: errno=%d: %s",
100 pim
->mroute_socket
, errno
,
101 safe_strerror(errno
));
106 setsockopt_so_recvbuf(pim
->mroute_socket
, 1024 * 1024 * 8);
108 flags
= fcntl(pim
->mroute_socket
, F_GETFL
, 0);
110 zlog_warn("Could not get flags on socket fd:%d %d %s",
111 pim
->mroute_socket
, errno
, safe_strerror(errno
));
112 close(pim
->mroute_socket
);
115 if (fcntl(pim
->mroute_socket
, F_SETFL
, flags
| O_NONBLOCK
)) {
116 zlog_warn("Could not set O_NONBLOCK on socket fd:%d %d %s",
117 pim
->mroute_socket
, errno
, safe_strerror(errno
));
118 close(pim
->mroute_socket
);
124 int upcalls
= IGMPMSG_WRVIFWHOLE
;
127 err
= setsockopt(pim
->mroute_socket
, IPPROTO_IP
, opt
, &upcalls
,
131 "Failure to register for VIFWHOLE and WRONGVIF upcalls %d %s",
132 errno
, safe_strerror(errno
));
137 "PIM-SM will not work properly on this platform, until the ability to receive the WRVIFWHOLE upcall");
144 static const char *igmpmsgtype2str
[IGMPMSG_WRVIFWHOLE
+ 1] = {
145 "<unknown_upcall?>", "NOCACHE", "WRONGVIF", "WHOLEPKT", "WRVIFWHOLE"};
147 static int pim_mroute_msg_nocache(int fd
, struct interface
*ifp
,
148 const struct igmpmsg
*msg
)
150 struct pim_interface
*pim_ifp
= ifp
->info
;
151 struct pim_upstream
*up
;
155 rpg
= pim_ifp
? RP(pim_ifp
->pim
, msg
->im_dst
) : NULL
;
157 * If the incoming interface is unknown OR
158 * the Interface type is SSM we don't need to
161 if (!rpg
|| (pim_rpf_addr_is_inaddr_none(rpg
))
162 || (!(PIM_I_am_DR(pim_ifp
)))) {
163 if (PIM_DEBUG_MROUTE_DETAIL
)
165 "%s: Interface is not configured correctly to handle incoming packet: Could be !DR, !pim_ifp, !SM, !RP",
166 __PRETTY_FUNCTION__
);
171 * If we've received a multicast packet that isn't connected to
174 if (!pim_if_connected_to_source(ifp
, msg
->im_src
)) {
175 if (PIM_DEBUG_MROUTE_DETAIL
)
177 "%s: Received incoming packet that doesn't originate on our seg",
178 __PRETTY_FUNCTION__
);
182 memset(&sg
, 0, sizeof(struct prefix_sg
));
183 sg
.src
= msg
->im_src
;
184 sg
.grp
= msg
->im_dst
;
186 up
= pim_upstream_find_or_add(&sg
, ifp
, PIM_UPSTREAM_FLAG_MASK_FHR
,
187 __PRETTY_FUNCTION__
);
189 if (PIM_DEBUG_MROUTE
) {
191 "%s: Failure to add upstream information for %s",
192 __PRETTY_FUNCTION__
, pim_str_sg_dump(&sg
));
198 * I moved this debug till after the actual add because
199 * I want to take advantage of the up->sg_str being filled in.
201 if (PIM_DEBUG_MROUTE
) {
202 zlog_debug("%s: Adding a Route %s for WHOLEPKT consumption",
203 __PRETTY_FUNCTION__
, up
->sg_str
);
206 PIM_UPSTREAM_FLAG_SET_SRC_STREAM(up
->flags
);
207 pim_upstream_keep_alive_timer_start(up
, pim_ifp
->pim
->keep_alive_time
);
209 up
->channel_oil
->cc
.pktcnt
++;
210 PIM_UPSTREAM_FLAG_SET_FHR(up
->flags
);
211 // resolve mfcc_parent prior to mroute_add in channel_add_oif
212 if (up
->channel_oil
->oil
.mfcc_parent
>= MAXVIFS
) {
214 vif_index
= pim_if_find_vifindex_by_ifindex(
216 up
->rpf
.source_nexthop
.interface
->ifindex
);
217 up
->channel_oil
->oil
.mfcc_parent
= vif_index
;
219 pim_register_join(up
);
224 static int pim_mroute_msg_wholepkt(int fd
, struct interface
*ifp
,
227 struct pim_interface
*pim_ifp
;
230 const struct ip
*ip_hdr
;
231 struct pim_upstream
*up
;
235 ip_hdr
= (const struct ip
*)buf
;
237 memset(&sg
, 0, sizeof(struct prefix_sg
));
238 sg
.src
= ip_hdr
->ip_src
;
239 sg
.grp
= ip_hdr
->ip_dst
;
241 up
= pim_upstream_find(pim_ifp
->pim
, &sg
);
243 struct prefix_sg star
= sg
;
244 star
.src
.s_addr
= INADDR_ANY
;
246 up
= pim_upstream_find(pim_ifp
->pim
, &star
);
248 if (up
&& PIM_UPSTREAM_FLAG_TEST_SRC_IGMP(up
->flags
)) {
249 up
= pim_upstream_add(pim_ifp
->pim
, &sg
, ifp
,
250 PIM_UPSTREAM_FLAG_MASK_SRC_LHR
,
251 __PRETTY_FUNCTION__
, NULL
);
253 if (PIM_DEBUG_MROUTE
)
255 "%s: Unable to create upstream information for %s",
257 pim_str_sg_dump(&sg
));
260 pim_upstream_keep_alive_timer_start(
261 up
, pim_ifp
->pim
->keep_alive_time
);
262 pim_upstream_inherited_olist(pim_ifp
->pim
, up
);
263 pim_upstream_switch(pim_ifp
->pim
, up
,
264 PIM_UPSTREAM_JOINED
);
266 if (PIM_DEBUG_MROUTE
)
267 zlog_debug("%s: Creating %s upstream on LHR",
268 __PRETTY_FUNCTION__
, up
->sg_str
);
271 if (PIM_DEBUG_MROUTE_DETAIL
) {
273 "%s: Unable to find upstream channel WHOLEPKT%s",
274 __PRETTY_FUNCTION__
, pim_str_sg_dump(&sg
));
279 pim_ifp
= up
->rpf
.source_nexthop
.interface
->info
;
281 rpg
= pim_ifp
? RP(pim_ifp
->pim
, sg
.grp
) : NULL
;
283 if ((pim_rpf_addr_is_inaddr_none(rpg
)) || (!pim_ifp
)
284 || (!(PIM_I_am_DR(pim_ifp
)))) {
285 if (PIM_DEBUG_MROUTE
) {
286 zlog_debug("%s: Failed Check send packet",
287 __PRETTY_FUNCTION__
);
293 * If we've received a register suppress
295 if (!up
->t_rs_timer
) {
296 if (pim_is_grp_ssm(pim_ifp
->pim
, sg
.grp
)) {
297 if (PIM_DEBUG_PIM_REG
)
299 "%s register forward skipped as group is SSM",
300 pim_str_sg_dump(&sg
));
303 pim_register_send((uint8_t *)buf
+ sizeof(struct ip
),
304 ntohs(ip_hdr
->ip_len
) - sizeof(struct ip
),
305 pim_ifp
->primary_address
, rpg
, 0, up
);
310 static int pim_mroute_msg_wrongvif(int fd
, struct interface
*ifp
,
311 const struct igmpmsg
*msg
)
313 struct pim_ifchannel
*ch
;
314 struct pim_interface
*pim_ifp
;
317 memset(&sg
, 0, sizeof(struct prefix_sg
));
318 sg
.src
= msg
->im_src
;
319 sg
.grp
= msg
->im_dst
;
322 Send Assert(S,G) on iif as response to WRONGVIF kernel upcall.
324 RFC 4601 4.8.2. PIM-SSM-Only Routers
326 iif is the incoming interface of the packet.
327 if (iif is in inherited_olist(S,G)) {
328 send Assert(S,G) on iif
333 if (PIM_DEBUG_MROUTE
)
335 "%s: WRONGVIF (S,G)=%s could not find input interface for input_vif_index=%d",
336 __PRETTY_FUNCTION__
, pim_str_sg_dump(&sg
),
343 if (PIM_DEBUG_MROUTE
)
345 "%s: WRONGVIF (S,G)=%s multicast not enabled on interface %s",
346 __PRETTY_FUNCTION__
, pim_str_sg_dump(&sg
),
351 ch
= pim_ifchannel_find(ifp
, &sg
);
353 struct prefix_sg star_g
= sg
;
354 if (PIM_DEBUG_MROUTE
)
356 "%s: WRONGVIF (S,G)=%s could not find channel on interface %s",
357 __PRETTY_FUNCTION__
, pim_str_sg_dump(&sg
),
360 star_g
.src
.s_addr
= INADDR_ANY
;
361 ch
= pim_ifchannel_find(ifp
, &star_g
);
363 if (PIM_DEBUG_MROUTE
)
365 "%s: WRONGVIF (*,G)=%s could not find channel on interface %s",
367 pim_str_sg_dump(&star_g
), ifp
->name
);
373 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
375 Transitions from NoInfo State
377 An (S,G) data packet arrives on interface I, AND
378 CouldAssert(S,G,I)==TRUE An (S,G) data packet arrived on an
379 downstream interface that is in our (S,G) outgoing interface
380 list. We optimistically assume that we will be the assert
381 winner for this (S,G), and so we transition to the "I am Assert
382 Winner" state and perform Actions A1 (below), which will
383 initiate the assert negotiation for (S,G).
386 if (ch
->ifassert_state
!= PIM_IFASSERT_NOINFO
) {
387 if (PIM_DEBUG_MROUTE
) {
389 "%s: WRONGVIF (S,G)=%s channel is not on Assert NoInfo state for interface %s",
390 __PRETTY_FUNCTION__
, ch
->sg_str
, ifp
->name
);
395 if (!PIM_IF_FLAG_TEST_COULD_ASSERT(ch
->flags
)) {
396 if (PIM_DEBUG_MROUTE
) {
398 "%s: WRONGVIF (S,G)=%s interface %s is not downstream for channel",
399 __PRETTY_FUNCTION__
, ch
->sg_str
, ifp
->name
);
404 if (assert_action_a1(ch
)) {
405 if (PIM_DEBUG_MROUTE
) {
407 "%s: WRONGVIF (S,G)=%s assert_action_a1 failure on interface %s",
408 __PRETTY_FUNCTION__
, ch
->sg_str
, ifp
->name
);
416 static int pim_mroute_msg_wrvifwhole(int fd
, struct interface
*ifp
,
419 const struct ip
*ip_hdr
= (const struct ip
*)buf
;
420 struct pim_interface
*pim_ifp
;
421 struct pim_ifchannel
*ch
;
422 struct pim_upstream
*up
;
423 struct prefix_sg star_g
;
425 struct channel_oil
*oil
;
429 memset(&sg
, 0, sizeof(struct prefix_sg
));
430 sg
.src
= ip_hdr
->ip_src
;
431 sg
.grp
= ip_hdr
->ip_dst
;
433 ch
= pim_ifchannel_find(ifp
, &sg
);
435 if (PIM_DEBUG_MROUTE
)
437 "WRVIFWHOLE (S,G)=%s found ifchannel on interface %s",
438 ch
->sg_str
, ifp
->name
);
443 star_g
.src
.s_addr
= INADDR_ANY
;
445 ch
= pim_ifchannel_find(ifp
, &star_g
);
448 if (PIM_DEBUG_MROUTE
)
449 zlog_debug ("WRVIFWHOLE (*,G)=%s found ifchannel on interface %s",
450 pim_str_sg_dump (&star_g
), ifp
->name
);
455 up
= pim_upstream_find(pim_ifp
->pim
, &sg
);
457 struct pim_upstream
*parent
;
458 struct pim_nexthop source
;
459 struct pim_rpf
*rpf
= RP(pim_ifp
->pim
, sg
.grp
);
460 if (!rpf
|| !rpf
->source_nexthop
.interface
)
464 * If we have received a WRVIFWHOLE and are at this
465 * point, we could be receiving the packet on the *,G
466 * tree, let's check and if so we can safely drop
469 parent
= pim_upstream_find(pim_ifp
->pim
, &star_g
);
470 if (parent
&& parent
->rpf
.source_nexthop
.interface
== ifp
)
473 pim_ifp
= rpf
->source_nexthop
.interface
->info
;
475 memset(&source
, 0, sizeof(source
));
477 * If we are the fhr that means we are getting a callback during
478 * the pimreg period, so I believe we can ignore this packet
480 if (!PIM_UPSTREAM_FLAG_TEST_FHR(up
->flags
)) {
481 // No if channel, but upstream we are at the RP.
482 if (pim_nexthop_lookup(pim_ifp
->pim
, &source
,
483 up
->upstream_register
, 0)
485 pim_register_stop_send(source
.interface
, &sg
,
486 pim_ifp
->primary_address
,
487 up
->upstream_register
);
488 up
->sptbit
= PIM_UPSTREAM_SPTBIT_TRUE
;
490 if (!up
->channel_oil
)
491 up
->channel_oil
= pim_channel_oil_add(
493 pim_ifp
->mroute_vif_index
);
494 pim_upstream_inherited_olist(pim_ifp
->pim
, up
);
495 if (!up
->channel_oil
->installed
)
496 pim_mroute_add(up
->channel_oil
,
497 __PRETTY_FUNCTION__
);
499 if (I_am_RP(pim_ifp
->pim
, up
->sg
.grp
)) {
500 if (pim_nexthop_lookup(pim_ifp
->pim
, &source
,
501 up
->upstream_register
, 0)
503 pim_register_stop_send(
504 source
.interface
, &sg
,
505 pim_ifp
->primary_address
,
506 up
->upstream_register
);
507 up
->sptbit
= PIM_UPSTREAM_SPTBIT_TRUE
;
509 pim_upstream_keep_alive_timer_start(
510 up
, pim_ifp
->pim
->keep_alive_time
);
511 pim_upstream_inherited_olist(pim_ifp
->pim
, up
);
512 pim_mroute_msg_wholepkt(fd
, ifp
, buf
);
518 oil
= pim_channel_oil_add(pim_ifp
->pim
, &sg
, pim_ifp
->mroute_vif_index
);
520 pim_mroute_add(oil
, __PRETTY_FUNCTION__
);
521 if (pim_if_connected_to_source(ifp
, sg
.src
)) {
522 up
= pim_upstream_add(pim_ifp
->pim
, &sg
, ifp
,
523 PIM_UPSTREAM_FLAG_MASK_FHR
,
524 __PRETTY_FUNCTION__
, NULL
);
526 if (PIM_DEBUG_MROUTE
)
528 "%s: WRONGVIF%s unable to create upstream on interface",
529 pim_str_sg_dump(&sg
), ifp
->name
);
532 PIM_UPSTREAM_FLAG_SET_SRC_STREAM(up
->flags
);
533 pim_upstream_keep_alive_timer_start(
534 up
, pim_ifp
->pim
->keep_alive_time
);
535 up
->channel_oil
= oil
;
536 up
->channel_oil
->cc
.pktcnt
++;
537 pim_register_join(up
);
538 pim_upstream_inherited_olist(pim_ifp
->pim
, up
);
540 // Send the packet to the RP
541 pim_mroute_msg_wholepkt(fd
, ifp
, buf
);
547 static int pim_mroute_msg(struct pim_instance
*pim
, const char *buf
,
548 int buf_size
, ifindex_t ifindex
)
550 struct interface
*ifp
;
551 struct pim_interface
*pim_ifp
;
552 const struct ip
*ip_hdr
;
553 const struct igmpmsg
*msg
;
554 char ip_src_str
[INET_ADDRSTRLEN
] = "";
555 char ip_dst_str
[INET_ADDRSTRLEN
] = "";
556 char src_str
[INET_ADDRSTRLEN
] = "<src?>";
557 char grp_str
[INET_ADDRSTRLEN
] = "<grp?>";
558 struct in_addr ifaddr
;
559 struct igmp_sock
*igmp
;
561 ip_hdr
= (const struct ip
*)buf
;
563 if (ip_hdr
->ip_p
== IPPROTO_IGMP
) {
565 /* We have the IP packet but we do not know which interface this
567 * received on. Find the interface that is on the same subnet as
571 ifp
= if_lookup_by_index(ifindex
, pim
->vrf_id
);
573 if (!ifp
|| !ifp
->info
)
577 ifaddr
= pim_find_primary_addr(ifp
);
578 igmp
= pim_igmp_sock_lookup_ifaddr(pim_ifp
->igmp_socket_list
,
581 if (PIM_DEBUG_MROUTE
) {
582 pim_inet4_dump("<src?>", ip_hdr
->ip_src
, ip_src_str
,
584 pim_inet4_dump("<dst?>", ip_hdr
->ip_dst
, ip_dst_str
,
588 "%s(%s): igmp kernel upcall on %s(%p) for %s -> %s",
589 __PRETTY_FUNCTION__
, pim
->vrf
->name
, ifp
->name
,
590 igmp
, ip_src_str
, ip_dst_str
);
593 pim_igmp_packet(igmp
, (char *)buf
, buf_size
);
595 } else if (ip_hdr
->ip_p
) {
596 if (PIM_DEBUG_MROUTE_DETAIL
) {
597 pim_inet4_dump("<src?>", ip_hdr
->ip_src
, src_str
,
599 pim_inet4_dump("<grp?>", ip_hdr
->ip_dst
, grp_str
,
602 "%s: no kernel upcall proto=%d src: %s dst: %s msg_size=%d",
603 __PRETTY_FUNCTION__
, ip_hdr
->ip_p
, src_str
,
608 msg
= (const struct igmpmsg
*)buf
;
610 ifp
= pim_if_find_by_vif_index(pim
, msg
->im_vif
);
614 if (PIM_DEBUG_MROUTE
) {
615 pim_inet4_dump("<src?>", msg
->im_src
, src_str
,
617 pim_inet4_dump("<grp?>", msg
->im_dst
, grp_str
,
620 "%s: pim kernel upcall %s type=%d ip_p=%d from fd=%d for (S,G)=(%s,%s) on %s vifi=%d size=%d",
622 igmpmsgtype2str
[msg
->im_msgtype
],
623 msg
->im_msgtype
, ip_hdr
->ip_p
,
624 pim
->mroute_socket
, src_str
, grp_str
, ifp
->name
,
625 msg
->im_vif
, buf_size
);
628 switch (msg
->im_msgtype
) {
629 case IGMPMSG_WRONGVIF
:
630 return pim_mroute_msg_wrongvif(pim
->mroute_socket
, ifp
,
633 case IGMPMSG_NOCACHE
:
634 return pim_mroute_msg_nocache(pim
->mroute_socket
, ifp
,
637 case IGMPMSG_WHOLEPKT
:
638 return pim_mroute_msg_wholepkt(pim
->mroute_socket
, ifp
,
641 case IGMPMSG_WRVIFWHOLE
:
642 return pim_mroute_msg_wrvifwhole(
643 pim
->mroute_socket
, ifp
, (const char *)msg
);
653 static int mroute_read(struct thread
*t
)
655 struct pim_instance
*pim
;
656 static long long count
;
665 rd
= pim_socket_recvfromto(pim
->mroute_socket
, (uint8_t *)buf
,
666 sizeof(buf
), NULL
, NULL
, NULL
, NULL
,
671 if (errno
== EWOULDBLOCK
|| errno
== EAGAIN
)
674 if (PIM_DEBUG_MROUTE
)
676 "%s: failure reading rd=%d: fd=%d: errno=%d: %s",
677 __PRETTY_FUNCTION__
, rd
,
678 pim
->mroute_socket
, errno
,
679 safe_strerror(errno
));
683 result
= pim_mroute_msg(pim
, buf
, rd
, ifindex
);
686 if (count
% qpim_packet_process
== 0)
696 static void mroute_read_on(struct pim_instance
*pim
)
698 thread_add_read(master
, mroute_read
, pim
, pim
->mroute_socket
,
702 static void mroute_read_off(struct pim_instance
*pim
)
704 THREAD_OFF(pim
->thread
);
707 int pim_mroute_socket_enable(struct pim_instance
*pim
)
711 if (pimd_privs
.change(ZPRIVS_RAISE
))
712 zlog_err("pim_mroute_socket_enable: could not raise privs, %s",
713 safe_strerror(errno
));
715 fd
= socket(AF_INET
, SOCK_RAW
, IPPROTO_IGMP
);
718 zlog_warn("Could not create mroute socket: errno=%d: %s", errno
,
719 safe_strerror(errno
));
723 #ifdef SO_BINDTODEVICE
724 if (pim
->vrf
->vrf_id
!= VRF_DEFAULT
725 && setsockopt(fd
, SOL_SOCKET
, SO_BINDTODEVICE
, pim
->vrf
->name
,
726 strlen(pim
->vrf
->name
))) {
727 zlog_warn("Could not setsockopt SO_BINDTODEVICE: %s",
728 safe_strerror(errno
));
734 if (pimd_privs
.change(ZPRIVS_LOWER
))
735 zlog_err("pim_mroute_socket_enable: could not lower privs, %s",
736 safe_strerror(errno
));
738 pim
->mroute_socket
= fd
;
739 if (pim_mroute_set(pim
, 1)) {
741 "Could not enable mroute on socket fd=%d: errno=%d: %s",
742 fd
, errno
, safe_strerror(errno
));
744 pim
->mroute_socket
= -1;
748 pim
->mroute_socket_creation
= pim_time_monotonic_sec();
755 int pim_mroute_socket_disable(struct pim_instance
*pim
)
757 if (pim_mroute_set(pim
, 0)) {
759 "Could not disable mroute on socket fd=%d: errno=%d: %s",
760 pim
->mroute_socket
, errno
, safe_strerror(errno
));
764 if (close(pim
->mroute_socket
)) {
765 zlog_warn("Failure closing mroute socket: fd=%d errno=%d: %s",
766 pim
->mroute_socket
, errno
, safe_strerror(errno
));
770 mroute_read_off(pim
);
771 pim
->mroute_socket
= -1;
777 For each network interface (e.g., physical or a virtual tunnel) that
778 would be used for multicast forwarding, a corresponding multicast
779 interface must be added to the kernel.
781 int pim_mroute_add_vif(struct interface
*ifp
, struct in_addr ifaddr
,
784 struct pim_interface
*pim_ifp
= ifp
->info
;
788 if (PIM_DEBUG_MROUTE
)
789 zlog_debug("%s: Add Vif %d (%s[%s])", __PRETTY_FUNCTION__
,
790 pim_ifp
->mroute_vif_index
, ifp
->name
,
791 pim_ifp
->pim
->vrf
->name
);
793 memset(&vc
, 0, sizeof(vc
));
794 vc
.vifc_vifi
= pim_ifp
->mroute_vif_index
;
795 #ifdef VIFF_USE_IFINDEX
796 vc
.vifc_lcl_ifindex
= ifp
->ifindex
;
798 if (ifaddr
.s_addr
== INADDR_ANY
) {
800 "%s: unnumbered interfaces are not supported on this platform",
801 __PRETTY_FUNCTION__
);
804 memcpy(&vc
.vifc_lcl_addr
, &ifaddr
, sizeof(vc
.vifc_lcl_addr
));
806 vc
.vifc_flags
= flags
;
807 vc
.vifc_threshold
= PIM_MROUTE_MIN_TTL
;
808 vc
.vifc_rate_limit
= 0;
810 #ifdef PIM_DVMRP_TUNNEL
811 if (vc
.vifc_flags
& VIFF_TUNNEL
) {
812 memcpy(&vc
.vifc_rmt_addr
, &vif_remote_addr
,
813 sizeof(vc
.vifc_rmt_addr
));
817 err
= setsockopt(pim_ifp
->pim
->mroute_socket
, IPPROTO_IP
, MRT_ADD_VIF
,
818 (void *)&vc
, sizeof(vc
));
820 char ifaddr_str
[INET_ADDRSTRLEN
];
822 pim_inet4_dump("<ifaddr?>", ifaddr
, ifaddr_str
,
826 "%s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_ADD_VIF,vif_index=%d,ifaddr=%s,flag=%d): errno=%d: %s",
827 __PRETTY_FUNCTION__
, pim_ifp
->pim
->mroute_socket
,
828 ifp
->ifindex
, ifaddr_str
, flags
, errno
,
829 safe_strerror(errno
));
836 int pim_mroute_del_vif(struct interface
*ifp
)
838 struct pim_interface
*pim_ifp
= ifp
->info
;
842 if (PIM_DEBUG_MROUTE
)
843 zlog_debug("%s: Del Vif %d (%s[%s])", __PRETTY_FUNCTION__
,
844 pim_ifp
->mroute_vif_index
, ifp
->name
,
845 pim_ifp
->pim
->vrf
->name
);
847 memset(&vc
, 0, sizeof(vc
));
848 vc
.vifc_vifi
= pim_ifp
->mroute_vif_index
;
850 err
= setsockopt(pim_ifp
->pim
->mroute_socket
, IPPROTO_IP
, MRT_DEL_VIF
,
851 (void *)&vc
, sizeof(vc
));
854 "%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_DEL_VIF,vif_index=%d): errno=%d: %s",
855 __FILE__
, __PRETTY_FUNCTION__
,
856 pim_ifp
->pim
->mroute_socket
, pim_ifp
->mroute_vif_index
,
857 errno
, safe_strerror(errno
));
864 int pim_mroute_add(struct channel_oil
*c_oil
, const char *name
)
866 struct pim_instance
*pim
= c_oil
->pim
;
869 int orig_iif_vif
= 0;
871 pim
->mroute_add_last
= pim_time_monotonic_sec();
872 ++pim
->mroute_add_events
;
874 /* Do not install route if incoming interface is undefined. */
875 if (c_oil
->oil
.mfcc_parent
>= MAXVIFS
) {
876 if (PIM_DEBUG_MROUTE
) {
879 "%s(%s) %s Attempting to add vifi that is invalid to mroute table",
880 __PRETTY_FUNCTION__
, name
,
881 pim_channel_oil_dump(c_oil
, buf
, sizeof(buf
)));
886 /* The linux kernel *expects* the incoming
887 * vif to be part of the outgoing list
888 * in the case of a (*,G).
890 if (c_oil
->oil
.mfcc_origin
.s_addr
== INADDR_ANY
) {
891 orig
= c_oil
->oil
.mfcc_ttls
[c_oil
->oil
.mfcc_parent
];
892 c_oil
->oil
.mfcc_ttls
[c_oil
->oil
.mfcc_parent
] = 1;
896 * If we have an unresolved cache entry for the S,G
897 * it is owned by the pimreg for the incoming IIF
898 * So set pimreg as the IIF temporarily to cause
899 * the packets to be forwarded. Then set it
900 * to the correct IIF afterwords.
902 if (!c_oil
->installed
&& c_oil
->oil
.mfcc_origin
.s_addr
!= INADDR_ANY
903 && c_oil
->oil
.mfcc_parent
!= 0) {
904 orig_iif_vif
= c_oil
->oil
.mfcc_parent
;
905 c_oil
->oil
.mfcc_parent
= 0;
907 err
= setsockopt(pim
->mroute_socket
, IPPROTO_IP
, MRT_ADD_MFC
,
908 &c_oil
->oil
, sizeof(c_oil
->oil
));
910 if (!err
&& !c_oil
->installed
911 && c_oil
->oil
.mfcc_origin
.s_addr
!= INADDR_ANY
912 && orig_iif_vif
!= 0) {
913 c_oil
->oil
.mfcc_parent
= orig_iif_vif
;
914 err
= setsockopt(pim
->mroute_socket
, IPPROTO_IP
, MRT_ADD_MFC
,
915 &c_oil
->oil
, sizeof(c_oil
->oil
));
918 if (c_oil
->oil
.mfcc_origin
.s_addr
== INADDR_ANY
)
919 c_oil
->oil
.mfcc_ttls
[c_oil
->oil
.mfcc_parent
] = orig
;
923 "%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_ADD_MFC): errno=%d: %s",
924 __FILE__
, __PRETTY_FUNCTION__
, pim
->mroute_socket
,
925 errno
, safe_strerror(errno
));
929 if (PIM_DEBUG_MROUTE
) {
931 zlog_debug("%s(%s), vrf %s Added Route: %s",
932 __PRETTY_FUNCTION__
, name
, pim
->vrf
->name
,
933 pim_channel_oil_dump(c_oil
, buf
, sizeof(buf
)));
936 c_oil
->installed
= 1;
940 int pim_mroute_del(struct channel_oil
*c_oil
, const char *name
)
942 struct pim_instance
*pim
= c_oil
->pim
;
945 pim
->mroute_del_last
= pim_time_monotonic_sec();
946 ++pim
->mroute_del_events
;
948 if (!c_oil
->installed
) {
949 if (PIM_DEBUG_MROUTE
) {
952 "%s %s: vifi %d for route is %s not installed, do not need to send del req. ",
953 __FILE__
, __PRETTY_FUNCTION__
,
954 c_oil
->oil
.mfcc_parent
,
955 pim_channel_oil_dump(c_oil
, buf
, sizeof(buf
)));
960 err
= setsockopt(pim
->mroute_socket
, IPPROTO_IP
, MRT_DEL_MFC
,
961 &c_oil
->oil
, sizeof(c_oil
->oil
));
963 if (PIM_DEBUG_MROUTE
)
965 "%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_DEL_MFC): errno=%d: %s",
966 __FILE__
, __PRETTY_FUNCTION__
,
967 pim
->mroute_socket
, errno
,
968 safe_strerror(errno
));
972 if (PIM_DEBUG_MROUTE
) {
974 zlog_debug("%s(%s), vrf %s Deleted Route: %s",
975 __PRETTY_FUNCTION__
, name
, pim
->vrf
->name
,
976 pim_channel_oil_dump(c_oil
, buf
, sizeof(buf
)));
979 // Reset kernel installed flag
980 c_oil
->installed
= 0;
985 void pim_mroute_update_counters(struct channel_oil
*c_oil
)
987 struct pim_instance
*pim
= c_oil
->pim
;
988 struct sioc_sg_req sgreq
;
990 c_oil
->cc
.oldpktcnt
= c_oil
->cc
.pktcnt
;
991 c_oil
->cc
.oldbytecnt
= c_oil
->cc
.bytecnt
;
992 c_oil
->cc
.oldwrong_if
= c_oil
->cc
.wrong_if
;
994 if (!c_oil
->installed
) {
995 c_oil
->cc
.lastused
= 100 * pim
->keep_alive_time
;
996 if (PIM_DEBUG_MROUTE
) {
999 sg
.src
= c_oil
->oil
.mfcc_origin
;
1000 sg
.grp
= c_oil
->oil
.mfcc_mcastgrp
;
1001 if (PIM_DEBUG_MROUTE
)
1003 "Channel(%s) is not installed no need to collect data from kernel",
1004 pim_str_sg_dump(&sg
));
1009 memset(&sgreq
, 0, sizeof(sgreq
));
1010 sgreq
.src
= c_oil
->oil
.mfcc_origin
;
1011 sgreq
.grp
= c_oil
->oil
.mfcc_mcastgrp
;
1013 pim_zlookup_sg_statistics(c_oil
);
1014 if (ioctl(pim
->mroute_socket
, SIOCGETSGCNT
, &sgreq
)) {
1015 if (PIM_DEBUG_MROUTE
) {
1016 struct prefix_sg sg
;
1018 sg
.src
= c_oil
->oil
.mfcc_origin
;
1019 sg
.grp
= c_oil
->oil
.mfcc_mcastgrp
;
1022 "ioctl(SIOCGETSGCNT=%lu) failure for (S,G)=(%s): errno=%d: %s",
1023 (unsigned long)SIOCGETSGCNT
,
1024 pim_str_sg_dump(&sg
), errno
,
1025 safe_strerror(errno
));
1030 c_oil
->cc
.pktcnt
= sgreq
.pktcnt
;
1031 c_oil
->cc
.bytecnt
= sgreq
.bytecnt
;
1032 c_oil
->cc
.wrong_if
= sgreq
.wrong_if
;