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"
44 static void mroute_read_on(struct pim_instance
*pim
);
46 static int pim_mroute_set(struct pim_instance
*pim
, int enable
)
50 socklen_t opt_len
= sizeof(opt
);
54 * We need to create the VRF table for the pim mroute_socket
56 if (pim
->vrf_id
!= VRF_DEFAULT
) {
57 if (pimd_privs
.change(ZPRIVS_RAISE
))
59 "pim_mroute_socket_enable: could not raise privs, %s",
60 safe_strerror(errno
));
62 opt
= pim
->vrf
->data
.l
.table_id
;
63 err
= setsockopt(pim
->mroute_socket
, IPPROTO_IP
, MRT_TABLE
,
67 "%s %s: failure: setsockopt(fd=%d,IPPROTO_IP, MRT_TABLE=%d): errno=%d: %s",
68 __FILE__
, __PRETTY_FUNCTION__
,
69 pim
->mroute_socket
, opt
, errno
,
70 safe_strerror(errno
));
74 if (pimd_privs
.change(ZPRIVS_LOWER
))
76 "pim_mroute_socket_enable: could not lower privs, %s",
77 safe_strerror(errno
));
80 opt
= enable
? MRT_INIT
: MRT_DONE
;
81 err
= setsockopt(pim
->mroute_socket
, IPPROTO_IP
, opt
, &opt
, opt_len
);
84 "%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,%s=%d): errno=%d: %s",
85 __FILE__
, __PRETTY_FUNCTION__
, pim
->mroute_socket
,
86 enable
? "MRT_INIT" : "MRT_DONE", opt
, errno
,
87 safe_strerror(errno
));
91 setsockopt_so_recvbuf(pim
->mroute_socket
, 1024 * 1024 * 8);
93 flags
= fcntl(pim
->mroute_socket
, F_GETFL
, 0);
95 zlog_warn("Could not get flags on socket fd:%d %d %s",
96 pim
->mroute_socket
, errno
, safe_strerror(errno
));
97 close(pim
->mroute_socket
);
100 if (fcntl(pim
->mroute_socket
, F_SETFL
, flags
| O_NONBLOCK
)) {
101 zlog_warn("Could not set O_NONBLOCK on socket fd:%d %d %s",
102 pim
->mroute_socket
, errno
, safe_strerror(errno
));
103 close(pim
->mroute_socket
);
109 int upcalls
= IGMPMSG_WRVIFWHOLE
;
112 err
= setsockopt(pim
->mroute_socket
, IPPROTO_IP
, opt
, &upcalls
,
116 "Failure to register for VIFWHOLE and WRONGVIF upcalls %d %s",
117 errno
, safe_strerror(errno
));
122 "PIM-SM will not work properly on this platform, until the ability to receive the WRVIFWHOLE upcall");
129 static const char *igmpmsgtype2str
[IGMPMSG_WRVIFWHOLE
+ 1] = {
130 "<unknown_upcall?>", "NOCACHE", "WRONGVIF", "WHOLEPKT", "WRVIFWHOLE"};
132 static int pim_mroute_msg_nocache(int fd
, struct interface
*ifp
,
133 const struct igmpmsg
*msg
)
135 struct pim_interface
*pim_ifp
= ifp
->info
;
136 struct pim_upstream
*up
;
140 rpg
= RP(pim_ifp
->pim
, msg
->im_dst
);
142 * If the incoming interface is unknown OR
143 * the Interface type is SSM we don't need to
146 if ((pim_rpf_addr_is_inaddr_none(rpg
)) || (!pim_ifp
)
147 || (!(PIM_I_am_DR(pim_ifp
)))) {
148 if (PIM_DEBUG_MROUTE_DETAIL
)
150 "%s: Interface is not configured correctly to handle incoming packet: Could be !DR, !pim_ifp, !SM, !RP",
151 __PRETTY_FUNCTION__
);
156 * If we've received a multicast packet that isn't connected to
159 if (!pim_if_connected_to_source(ifp
, msg
->im_src
)) {
160 if (PIM_DEBUG_MROUTE_DETAIL
)
162 "%s: Received incoming packet that doesn't originate on our seg",
163 __PRETTY_FUNCTION__
);
167 memset(&sg
, 0, sizeof(struct prefix_sg
));
168 sg
.src
= msg
->im_src
;
169 sg
.grp
= msg
->im_dst
;
171 up
= pim_upstream_find_or_add(&sg
, ifp
, PIM_UPSTREAM_FLAG_MASK_FHR
,
172 __PRETTY_FUNCTION__
);
174 if (PIM_DEBUG_MROUTE
) {
176 "%s: Failure to add upstream information for %s",
177 __PRETTY_FUNCTION__
, pim_str_sg_dump(&sg
));
183 * I moved this debug till after the actual add because
184 * I want to take advantage of the up->sg_str being filled in.
186 if (PIM_DEBUG_MROUTE
) {
187 zlog_debug("%s: Adding a Route %s for WHOLEPKT consumption",
188 __PRETTY_FUNCTION__
, up
->sg_str
);
191 PIM_UPSTREAM_FLAG_SET_SRC_STREAM(up
->flags
);
192 pim_upstream_keep_alive_timer_start(up
, qpim_keep_alive_time
);
194 up
->channel_oil
->cc
.pktcnt
++;
195 PIM_UPSTREAM_FLAG_SET_FHR(up
->flags
);
196 // resolve mfcc_parent prior to mroute_add in channel_add_oif
197 if (up
->channel_oil
->oil
.mfcc_parent
>= MAXVIFS
) {
199 vif_index
= pim_if_find_vifindex_by_ifindex(
201 up
->rpf
.source_nexthop
.interface
->ifindex
);
202 up
->channel_oil
->oil
.mfcc_parent
= vif_index
;
204 pim_register_join(up
);
209 static int pim_mroute_msg_wholepkt(int fd
, struct interface
*ifp
,
212 struct pim_interface
*pim_ifp
;
215 const struct ip
*ip_hdr
;
216 struct pim_upstream
*up
;
220 ip_hdr
= (const struct ip
*)buf
;
222 memset(&sg
, 0, sizeof(struct prefix_sg
));
223 sg
.src
= ip_hdr
->ip_src
;
224 sg
.grp
= ip_hdr
->ip_dst
;
226 up
= pim_upstream_find(pim_ifp
->pim
, &sg
);
228 struct prefix_sg star
= sg
;
229 star
.src
.s_addr
= INADDR_ANY
;
231 up
= pim_upstream_find(pim_ifp
->pim
, &star
);
233 if (up
&& PIM_UPSTREAM_FLAG_TEST_SRC_IGMP(up
->flags
)) {
234 up
= pim_upstream_add(&sg
, ifp
,
235 PIM_UPSTREAM_FLAG_MASK_SRC_LHR
,
236 __PRETTY_FUNCTION__
);
238 if (PIM_DEBUG_MROUTE
)
240 "%s: Unable to create upstream information for %s",
242 pim_str_sg_dump(&sg
));
245 pim_upstream_keep_alive_timer_start(
246 up
, qpim_keep_alive_time
);
247 pim_upstream_inherited_olist(pim_ifp
->pim
, up
);
248 pim_upstream_switch(up
, PIM_UPSTREAM_JOINED
);
250 if (PIM_DEBUG_MROUTE
)
251 zlog_debug("%s: Creating %s upstream on LHR",
252 __PRETTY_FUNCTION__
, up
->sg_str
);
255 if (PIM_DEBUG_MROUTE_DETAIL
) {
257 "%s: Unable to find upstream channel WHOLEPKT%s",
258 __PRETTY_FUNCTION__
, pim_str_sg_dump(&sg
));
263 pim_ifp
= up
->rpf
.source_nexthop
.interface
->info
;
265 rpg
= RP(pim_ifp
->pim
, sg
.grp
);
267 if ((pim_rpf_addr_is_inaddr_none(rpg
)) || (!pim_ifp
)
268 || (!(PIM_I_am_DR(pim_ifp
)))) {
269 if (PIM_DEBUG_MROUTE
) {
270 zlog_debug("%s: Failed Check send packet",
271 __PRETTY_FUNCTION__
);
277 * If we've received a register suppress
279 if (!up
->t_rs_timer
) {
280 if (pim_is_grp_ssm(sg
.grp
)) {
281 if (PIM_DEBUG_PIM_REG
)
283 "%s register forward skipped as group is SSM",
284 pim_str_sg_dump(&sg
));
287 pim_register_send((uint8_t *)buf
+ sizeof(struct ip
),
288 ntohs(ip_hdr
->ip_len
) - sizeof(struct ip
),
289 pim_ifp
->primary_address
, rpg
, 0, up
);
294 static int pim_mroute_msg_wrongvif(int fd
, struct interface
*ifp
,
295 const struct igmpmsg
*msg
)
297 struct pim_ifchannel
*ch
;
298 struct pim_interface
*pim_ifp
;
301 memset(&sg
, 0, sizeof(struct prefix_sg
));
302 sg
.src
= msg
->im_src
;
303 sg
.grp
= msg
->im_dst
;
306 Send Assert(S,G) on iif as response to WRONGVIF kernel upcall.
308 RFC 4601 4.8.2. PIM-SSM-Only Routers
310 iif is the incoming interface of the packet.
311 if (iif is in inherited_olist(S,G)) {
312 send Assert(S,G) on iif
317 if (PIM_DEBUG_MROUTE
)
319 "%s: WRONGVIF (S,G)=%s could not find input interface for input_vif_index=%d",
320 __PRETTY_FUNCTION__
, pim_str_sg_dump(&sg
),
327 if (PIM_DEBUG_MROUTE
)
329 "%s: WRONGVIF (S,G)=%s multicast not enabled on interface %s",
330 __PRETTY_FUNCTION__
, pim_str_sg_dump(&sg
),
335 ch
= pim_ifchannel_find(ifp
, &sg
);
337 struct prefix_sg star_g
= sg
;
338 if (PIM_DEBUG_MROUTE
)
340 "%s: WRONGVIF (S,G)=%s could not find channel on interface %s",
341 __PRETTY_FUNCTION__
, pim_str_sg_dump(&sg
),
344 star_g
.src
.s_addr
= INADDR_ANY
;
345 ch
= pim_ifchannel_find(ifp
, &star_g
);
347 if (PIM_DEBUG_MROUTE
)
349 "%s: WRONGVIF (*,G)=%s could not find channel on interface %s",
351 pim_str_sg_dump(&star_g
), ifp
->name
);
357 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
359 Transitions from NoInfo State
361 An (S,G) data packet arrives on interface I, AND
362 CouldAssert(S,G,I)==TRUE An (S,G) data packet arrived on an
363 downstream interface that is in our (S,G) outgoing interface
364 list. We optimistically assume that we will be the assert
365 winner for this (S,G), and so we transition to the "I am Assert
366 Winner" state and perform Actions A1 (below), which will
367 initiate the assert negotiation for (S,G).
370 if (ch
->ifassert_state
!= PIM_IFASSERT_NOINFO
) {
371 if (PIM_DEBUG_MROUTE
) {
373 "%s: WRONGVIF (S,G)=%s channel is not on Assert NoInfo state for interface %s",
374 __PRETTY_FUNCTION__
, ch
->sg_str
, ifp
->name
);
379 if (!PIM_IF_FLAG_TEST_COULD_ASSERT(ch
->flags
)) {
380 if (PIM_DEBUG_MROUTE
) {
382 "%s: WRONGVIF (S,G)=%s interface %s is not downstream for channel",
383 __PRETTY_FUNCTION__
, ch
->sg_str
, ifp
->name
);
388 if (assert_action_a1(ch
)) {
389 if (PIM_DEBUG_MROUTE
) {
391 "%s: WRONGVIF (S,G)=%s assert_action_a1 failure on interface %s",
392 __PRETTY_FUNCTION__
, ch
->sg_str
, ifp
->name
);
400 static int pim_mroute_msg_wrvifwhole(int fd
, struct interface
*ifp
,
403 const struct ip
*ip_hdr
= (const struct ip
*)buf
;
404 struct pim_interface
*pim_ifp
;
405 struct pim_ifchannel
*ch
;
406 struct pim_upstream
*up
;
407 struct prefix_sg star_g
;
409 struct channel_oil
*oil
;
413 memset(&sg
, 0, sizeof(struct prefix_sg
));
414 sg
.src
= ip_hdr
->ip_src
;
415 sg
.grp
= ip_hdr
->ip_dst
;
417 ch
= pim_ifchannel_find(ifp
, &sg
);
419 if (PIM_DEBUG_MROUTE
)
421 "WRVIFWHOLE (S,G)=%s found ifchannel on interface %s",
422 ch
->sg_str
, ifp
->name
);
427 star_g
.src
.s_addr
= INADDR_ANY
;
429 ch
= pim_ifchannel_find(ifp
, &star_g
);
432 if (PIM_DEBUG_MROUTE
)
433 zlog_debug ("WRVIFWHOLE (*,G)=%s found ifchannel on interface %s",
434 pim_str_sg_dump (&star_g
), ifp
->name
);
439 up
= pim_upstream_find(pim_ifp
->pim
, &sg
);
441 struct pim_upstream
*parent
;
442 struct pim_nexthop source
;
443 struct pim_rpf
*rpf
= RP(pim_ifp
->pim
, sg
.grp
);
444 if (!rpf
|| !rpf
->source_nexthop
.interface
)
448 * If we have received a WRVIFWHOLE and are at this
449 * point, we could be receiving the packet on the *,G
450 * tree, let's check and if so we can safely drop
453 parent
= pim_upstream_find(pim_ifp
->pim
, &star_g
);
454 if (parent
&& parent
->rpf
.source_nexthop
.interface
== ifp
)
457 pim_ifp
= rpf
->source_nexthop
.interface
->info
;
459 memset(&source
, 0, sizeof(source
));
461 * If we are the fhr that means we are getting a callback during
462 * the pimreg period, so I believe we can ignore this packet
464 if (!PIM_UPSTREAM_FLAG_TEST_FHR(up
->flags
)) {
465 // No if channel, but upstream we are at the RP.
466 if (pim_nexthop_lookup(pim_ifp
->pim
, &source
,
467 up
->upstream_register
, 0)
469 pim_register_stop_send(source
.interface
, &sg
,
470 pim_ifp
->primary_address
,
471 up
->upstream_register
);
472 if (!up
->channel_oil
)
473 up
->channel_oil
= pim_channel_oil_add(
475 pim_ifp
->mroute_vif_index
);
476 pim_upstream_inherited_olist(pim_ifp
->pim
, up
);
477 if (!up
->channel_oil
->installed
)
478 pim_mroute_add(up
->channel_oil
,
479 __PRETTY_FUNCTION__
);
480 pim_upstream_set_sptbit(up
, ifp
);
482 if (I_am_RP(pim_ifp
->pim
, up
->sg
.grp
)) {
483 if (pim_nexthop_lookup(pim_ifp
->pim
, &source
,
484 up
->upstream_register
, 0)
486 pim_register_stop_send(
487 source
.interface
, &sg
,
488 pim_ifp
->primary_address
,
489 up
->upstream_register
);
490 up
->sptbit
= PIM_UPSTREAM_SPTBIT_TRUE
;
492 pim_upstream_keep_alive_timer_start(
493 up
, qpim_keep_alive_time
);
494 pim_upstream_inherited_olist(pim_ifp
->pim
, up
);
495 pim_mroute_msg_wholepkt(fd
, ifp
, buf
);
501 oil
= pim_channel_oil_add(pim_ifp
->pim
, &sg
, pim_ifp
->mroute_vif_index
);
503 pim_mroute_add(oil
, __PRETTY_FUNCTION__
);
504 if (pim_if_connected_to_source(ifp
, sg
.src
)) {
505 up
= pim_upstream_add(&sg
, ifp
, PIM_UPSTREAM_FLAG_MASK_FHR
,
506 __PRETTY_FUNCTION__
);
508 if (PIM_DEBUG_MROUTE
)
510 "%s: WRONGVIF%s unable to create upstream on interface",
511 pim_str_sg_dump(&sg
), ifp
->name
);
514 PIM_UPSTREAM_FLAG_SET_SRC_STREAM(up
->flags
);
515 pim_upstream_keep_alive_timer_start(up
, qpim_keep_alive_time
);
516 up
->channel_oil
= oil
;
517 up
->channel_oil
->cc
.pktcnt
++;
518 pim_register_join(up
);
519 pim_upstream_inherited_olist(pim_ifp
->pim
, up
);
521 // Send the packet to the RP
522 pim_mroute_msg_wholepkt(fd
, ifp
, buf
);
528 static int pim_mroute_msg(struct pim_instance
*pim
, const char *buf
,
531 struct interface
*ifp
;
532 struct pim_interface
*pim_ifp
;
533 const struct ip
*ip_hdr
;
534 const struct igmpmsg
*msg
;
535 char ip_src_str
[INET_ADDRSTRLEN
] = "";
536 char ip_dst_str
[INET_ADDRSTRLEN
] = "";
537 char src_str
[INET_ADDRSTRLEN
] = "<src?>";
538 char grp_str
[INET_ADDRSTRLEN
] = "<grp?>";
539 struct in_addr ifaddr
;
540 struct igmp_sock
*igmp
;
542 ip_hdr
= (const struct ip
*)buf
;
544 if (ip_hdr
->ip_p
== IPPROTO_IGMP
) {
546 /* We have the IP packet but we do not know which interface this
548 * received on. Find the interface that is on the same subnet as
552 ifp
= pim_if_lookup_address_vrf(ip_hdr
->ip_src
, pim
->vrf_id
);
555 if (PIM_DEBUG_MROUTE_DETAIL
) {
556 pim_inet4_dump("<src?>", ip_hdr
->ip_src
,
557 ip_src_str
, sizeof(ip_src_str
));
558 pim_inet4_dump("<dst?>", ip_hdr
->ip_dst
,
559 ip_dst_str
, sizeof(ip_dst_str
));
562 "%s: igmp kernel upcall could not find usable interface for %s -> %s",
563 __PRETTY_FUNCTION__
, ip_src_str
,
569 ifaddr
= pim_find_primary_addr(ifp
);
570 igmp
= pim_igmp_sock_lookup_ifaddr(pim_ifp
->igmp_socket_list
,
573 if (PIM_DEBUG_MROUTE
) {
574 pim_inet4_dump("<src?>", ip_hdr
->ip_src
, ip_src_str
,
576 pim_inet4_dump("<dst?>", ip_hdr
->ip_dst
, ip_dst_str
,
580 "%s: igmp kernel upcall on %s(%p) for %s -> %s",
581 __PRETTY_FUNCTION__
, ifp
->name
, igmp
,
582 ip_src_str
, ip_dst_str
);
585 pim_igmp_packet(igmp
, (char *)buf
, buf_size
);
587 } else if (ip_hdr
->ip_p
) {
588 if (PIM_DEBUG_MROUTE_DETAIL
) {
589 pim_inet4_dump("<src?>", ip_hdr
->ip_src
, src_str
,
591 pim_inet4_dump("<grp?>", ip_hdr
->ip_dst
, grp_str
,
594 "%s: no kernel upcall proto=%d src: %s dst: %s msg_size=%d",
595 __PRETTY_FUNCTION__
, ip_hdr
->ip_p
, src_str
,
600 msg
= (const struct igmpmsg
*)buf
;
602 ifp
= pim_if_find_by_vif_index(pim
, msg
->im_vif
);
606 if (PIM_DEBUG_MROUTE
) {
607 pim_inet4_dump("<src?>", msg
->im_src
, src_str
,
609 pim_inet4_dump("<grp?>", msg
->im_dst
, grp_str
,
612 "%s: pim kernel upcall %s type=%d ip_p=%d from fd=%d for (S,G)=(%s,%s) on %s vifi=%d size=%d",
614 igmpmsgtype2str
[msg
->im_msgtype
],
615 msg
->im_msgtype
, ip_hdr
->ip_p
,
616 pim
->mroute_socket
, src_str
, grp_str
, ifp
->name
,
617 msg
->im_vif
, buf_size
);
620 switch (msg
->im_msgtype
) {
621 case IGMPMSG_WRONGVIF
:
622 return pim_mroute_msg_wrongvif(pim
->mroute_socket
, ifp
,
625 case IGMPMSG_NOCACHE
:
626 return pim_mroute_msg_nocache(pim
->mroute_socket
, ifp
,
629 case IGMPMSG_WHOLEPKT
:
630 return pim_mroute_msg_wholepkt(pim
->mroute_socket
, ifp
,
633 case IGMPMSG_WRVIFWHOLE
:
634 return pim_mroute_msg_wrvifwhole(
635 pim
->mroute_socket
, ifp
, (const char *)msg
);
645 static int mroute_read(struct thread
*t
)
647 struct pim_instance
*pim
;
648 static long long count
;
657 rd
= read(pim
->mroute_socket
, buf
, sizeof(buf
));
661 if (errno
== EWOULDBLOCK
|| errno
== EAGAIN
)
664 if (PIM_DEBUG_MROUTE
)
666 "%s: failure reading rd=%d: fd=%d: errno=%d: %s",
667 __PRETTY_FUNCTION__
, rd
,
668 pim
->mroute_socket
, errno
,
669 safe_strerror(errno
));
673 result
= pim_mroute_msg(pim
, buf
, rd
);
676 if (count
% qpim_packet_process
== 0)
686 static void mroute_read_on(struct pim_instance
*pim
)
688 thread_add_read(master
, mroute_read
, pim
, pim
->mroute_socket
,
692 static void mroute_read_off(struct pim_instance
*pim
)
694 THREAD_OFF(pim
->thread
);
697 int pim_mroute_socket_enable(struct pim_instance
*pim
)
701 if (pimd_privs
.change(ZPRIVS_RAISE
))
702 zlog_err("pim_mroute_socket_enable: could not raise privs, %s",
703 safe_strerror(errno
));
705 fd
= socket(AF_INET
, SOCK_RAW
, IPPROTO_IGMP
);
707 if (pimd_privs
.change(ZPRIVS_LOWER
))
708 zlog_err("pim_mroute_socket_enable: could not lower privs, %s",
709 safe_strerror(errno
));
712 zlog_warn("Could not create mroute socket: errno=%d: %s", errno
,
713 safe_strerror(errno
));
717 pim
->mroute_socket
= fd
;
718 if (pim_mroute_set(pim
, 1)) {
720 "Could not enable mroute on socket fd=%d: errno=%d: %s",
721 fd
, errno
, safe_strerror(errno
));
723 pim
->mroute_socket
= -1;
727 pim
->mroute_socket_creation
= pim_time_monotonic_sec();
734 int pim_mroute_socket_disable(struct pim_instance
*pim
)
736 if (pim_mroute_set(pim
, 0)) {
738 "Could not disable mroute on socket fd=%d: errno=%d: %s",
739 pim
->mroute_socket
, errno
, safe_strerror(errno
));
743 if (close(pim
->mroute_socket
)) {
744 zlog_warn("Failure closing mroute socket: fd=%d errno=%d: %s",
745 pim
->mroute_socket
, errno
, safe_strerror(errno
));
749 mroute_read_off(pim
);
750 pim
->mroute_socket
= -1;
756 For each network interface (e.g., physical or a virtual tunnel) that
757 would be used for multicast forwarding, a corresponding multicast
758 interface must be added to the kernel.
760 int pim_mroute_add_vif(struct interface
*ifp
, struct in_addr ifaddr
,
763 struct pim_interface
*pim_ifp
= ifp
->info
;
767 memset(&vc
, 0, sizeof(vc
));
768 vc
.vifc_vifi
= pim_ifp
->mroute_vif_index
;
769 #ifdef VIFF_USE_IFINDEX
770 vc
.vifc_lcl_ifindex
= ifp
->ifindex
;
772 if (ifaddr
.s_addr
== INADDR_ANY
) {
774 "%s: unnumbered interfaces are not supported on this platform",
775 __PRETTY_FUNCTION__
);
778 memcpy(&vc
.vifc_lcl_addr
, &ifaddr
, sizeof(vc
.vifc_lcl_addr
));
780 vc
.vifc_flags
= flags
;
781 vc
.vifc_threshold
= PIM_MROUTE_MIN_TTL
;
782 vc
.vifc_rate_limit
= 0;
784 #ifdef PIM_DVMRP_TUNNEL
785 if (vc
.vifc_flags
& VIFF_TUNNEL
) {
786 memcpy(&vc
.vifc_rmt_addr
, &vif_remote_addr
,
787 sizeof(vc
.vifc_rmt_addr
));
791 err
= setsockopt(pim_ifp
->pim
->mroute_socket
, IPPROTO_IP
, MRT_ADD_VIF
,
792 (void *)&vc
, sizeof(vc
));
794 char ifaddr_str
[INET_ADDRSTRLEN
];
796 pim_inet4_dump("<ifaddr?>", ifaddr
, ifaddr_str
,
800 "%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_ADD_VIF,vif_index=%d,ifaddr=%s,flag=%d): errno=%d: %s",
801 __FILE__
, __PRETTY_FUNCTION__
,
802 pim_ifp
->pim
->mroute_socket
, ifp
->ifindex
, ifaddr_str
,
803 flags
, errno
, safe_strerror(errno
));
810 int pim_mroute_del_vif(struct interface
*ifp
)
812 struct pim_interface
*pim_ifp
= ifp
->info
;
816 if (PIM_DEBUG_MROUTE
)
817 zlog_debug("%s %s: Del Vif %d (%s) ", __FILE__
,
818 __PRETTY_FUNCTION__
, pim_ifp
->mroute_vif_index
,
821 memset(&vc
, 0, sizeof(vc
));
822 vc
.vifc_vifi
= pim_ifp
->mroute_vif_index
;
824 err
= setsockopt(pim_ifp
->pim
->mroute_socket
, IPPROTO_IP
, MRT_DEL_VIF
,
825 (void *)&vc
, sizeof(vc
));
828 "%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_DEL_VIF,vif_index=%d): errno=%d: %s",
829 __FILE__
, __PRETTY_FUNCTION__
,
830 pim_ifp
->pim
->mroute_socket
, pim_ifp
->mroute_vif_index
,
831 errno
, safe_strerror(errno
));
838 int pim_mroute_add(struct channel_oil
*c_oil
, const char *name
)
840 struct pim_instance
*pim
;
843 int orig_iif_vif
= 0;
850 pim
->mroute_add_last
= pim_time_monotonic_sec();
851 ++pim
->mroute_add_events
;
853 /* Do not install route if incoming interface is undefined. */
854 if (c_oil
->oil
.mfcc_parent
>= MAXVIFS
) {
855 if (PIM_DEBUG_MROUTE
) {
858 "%s(%s) %s Attempting to add vifi that is invalid to mroute table",
859 __PRETTY_FUNCTION__
, name
,
860 pim_channel_oil_dump(c_oil
, buf
, sizeof(buf
)));
865 /* The linux kernel *expects* the incoming
866 * vif to be part of the outgoing list
867 * in the case of a (*,G).
869 if (c_oil
->oil
.mfcc_origin
.s_addr
== INADDR_ANY
) {
870 orig
= c_oil
->oil
.mfcc_ttls
[c_oil
->oil
.mfcc_parent
];
871 c_oil
->oil
.mfcc_ttls
[c_oil
->oil
.mfcc_parent
] = 1;
875 * If we have an unresolved cache entry for the S,G
876 * it is owned by the pimreg for the incoming IIF
877 * So set pimreg as the IIF temporarily to cause
878 * the packets to be forwarded. Then set it
879 * to the correct IIF afterwords.
881 if (!c_oil
->installed
&& c_oil
->oil
.mfcc_origin
.s_addr
!= INADDR_ANY
882 && c_oil
->oil
.mfcc_parent
!= 0) {
883 orig_iif_vif
= c_oil
->oil
.mfcc_parent
;
884 c_oil
->oil
.mfcc_parent
= 0;
886 err
= setsockopt(pim
->mroute_socket
, IPPROTO_IP
, MRT_ADD_MFC
,
887 &c_oil
->oil
, sizeof(c_oil
->oil
));
889 if (!err
&& !c_oil
->installed
890 && c_oil
->oil
.mfcc_origin
.s_addr
!= INADDR_ANY
891 && orig_iif_vif
!= 0) {
892 c_oil
->oil
.mfcc_parent
= orig_iif_vif
;
893 err
= setsockopt(pim
->mroute_socket
, IPPROTO_IP
, MRT_ADD_MFC
,
894 &c_oil
->oil
, sizeof(c_oil
->oil
));
897 if (c_oil
->oil
.mfcc_origin
.s_addr
== INADDR_ANY
)
898 c_oil
->oil
.mfcc_ttls
[c_oil
->oil
.mfcc_parent
] = orig
;
902 "%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_ADD_MFC): errno=%d: %s",
903 __FILE__
, __PRETTY_FUNCTION__
, pim
->mroute_socket
,
904 errno
, safe_strerror(errno
));
908 if (PIM_DEBUG_MROUTE
) {
910 zlog_debug("%s(%s), Added Route: %s", __PRETTY_FUNCTION__
, name
,
911 pim_channel_oil_dump(c_oil
, buf
, sizeof(buf
)));
914 c_oil
->installed
= 1;
918 int pim_mroute_del(struct channel_oil
*c_oil
, const char *name
)
920 struct pim_instance
*pim
;
928 pim
->mroute_del_last
= pim_time_monotonic_sec();
929 ++pim
->mroute_del_events
;
931 if (!c_oil
->installed
) {
932 if (PIM_DEBUG_MROUTE
) {
935 "%s %s: vifi %d for route is %s not installed, do not need to send del req. ",
936 __FILE__
, __PRETTY_FUNCTION__
,
937 c_oil
->oil
.mfcc_parent
,
938 pim_channel_oil_dump(c_oil
, buf
, sizeof(buf
)));
943 err
= setsockopt(pim
->mroute_socket
, IPPROTO_IP
, MRT_DEL_MFC
,
944 &c_oil
->oil
, sizeof(c_oil
->oil
));
946 if (PIM_DEBUG_MROUTE
)
948 "%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_DEL_MFC): errno=%d: %s",
949 __FILE__
, __PRETTY_FUNCTION__
,
950 pim
->mroute_socket
, errno
,
951 safe_strerror(errno
));
955 if (PIM_DEBUG_MROUTE
) {
957 zlog_debug("%s(%s), Deleted Route: %s", __PRETTY_FUNCTION__
,
958 name
, pim_channel_oil_dump(c_oil
, buf
, sizeof(buf
)));
961 // Reset kernel installed flag
962 c_oil
->installed
= 0;
967 void pim_mroute_update_counters(struct channel_oil
*c_oil
)
969 struct pim_instance
*pim
;
970 struct sioc_sg_req sgreq
;
977 c_oil
->cc
.oldpktcnt
= c_oil
->cc
.pktcnt
;
978 c_oil
->cc
.oldbytecnt
= c_oil
->cc
.bytecnt
;
979 c_oil
->cc
.oldwrong_if
= c_oil
->cc
.wrong_if
;
981 if (!c_oil
->installed
) {
982 c_oil
->cc
.lastused
= 100 * qpim_keep_alive_time
;
983 if (PIM_DEBUG_MROUTE
) {
986 sg
.src
= c_oil
->oil
.mfcc_origin
;
987 sg
.grp
= c_oil
->oil
.mfcc_mcastgrp
;
988 if (PIM_DEBUG_MROUTE
)
990 "Channel(%s) is not installed no need to collect data from kernel",
991 pim_str_sg_dump(&sg
));
996 memset(&sgreq
, 0, sizeof(sgreq
));
997 sgreq
.src
= c_oil
->oil
.mfcc_origin
;
998 sgreq
.grp
= c_oil
->oil
.mfcc_mcastgrp
;
1000 pim_zlookup_sg_statistics(c_oil
);
1001 if (ioctl(pim
->mroute_socket
, SIOCGETSGCNT
, &sgreq
)) {
1002 if (PIM_DEBUG_MROUTE
) {
1003 struct prefix_sg sg
;
1005 sg
.src
= c_oil
->oil
.mfcc_origin
;
1006 sg
.grp
= c_oil
->oil
.mfcc_mcastgrp
;
1009 "ioctl(SIOCGETSGCNT=%lu) failure for (S,G)=(%s): errno=%d: %s",
1010 (unsigned long)SIOCGETSGCNT
,
1011 pim_str_sg_dump(&sg
), errno
,
1012 safe_strerror(errno
));
1017 c_oil
->cc
.pktcnt
= sgreq
.pktcnt
;
1018 c_oil
->cc
.bytecnt
= sgreq
.bytecnt
;
1019 c_oil
->cc
.wrong_if
= sgreq
.wrong_if
;