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
16 along with this program; see the file COPYING; if not, write to the
17 Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
28 #include "pim_mroute.h"
32 #include "pim_iface.h"
33 #include "pim_macro.h"
36 #include "pim_register.h"
39 extern struct zebra_privs_t pimd_privs
;
41 static void mroute_read_on(void);
43 static int pim_mroute_set(int fd
, int enable
)
46 int opt
= enable
? MRT_INIT
: MRT_DONE
;
47 socklen_t opt_len
= sizeof(opt
);
49 err
= setsockopt(fd
, IPPROTO_IP
, opt
, &opt
, opt_len
);
52 zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,%s=%d): errno=%d: %s",
53 __FILE__
, __PRETTY_FUNCTION__
,
54 fd
, enable
? "MRT_INIT" : "MRT_DONE", opt
, e
, safe_strerror(e
));
60 zlog_info("%s %s: setsockopt(fd=%d,IPPROTO_IP,MRT_INIT,opt=%d): ok",
61 __FILE__
, __PRETTY_FUNCTION__
,
69 pim_mroute_connected_to_source (struct interface
*ifp
, struct in_addr src
)
71 struct listnode
*cnode
;
77 p
.prefixlen
= IPV4_MAX_BITLEN
;
79 for (ALL_LIST_ELEMENTS_RO (ifp
->connected
, cnode
, c
))
81 if ((c
->address
->family
== AF_INET
) &&
82 prefix_match (CONNECTED_PREFIX (c
), &p
))
91 static const char *igmpmsgtype2str
[IGMPMSG_WHOLEPKT
+ 1] = {
98 pim_mroute_msg_nocache (int fd
, struct interface
*ifp
, const struct igmpmsg
*msg
,
99 const char *src_str
, const char *grp_str
)
101 struct pim_interface
*pim_ifp
= ifp
->info
;
102 struct pim_upstream
*up
;
105 rpg
= RP(msg
->im_dst
);
107 * If the incoming interface is unknown OR
108 * the Interface type is SSM we don't need to
111 if ((rpg
->rpf_addr
.s_addr
== INADDR_NONE
) ||
113 (!(PIM_I_am_DR(pim_ifp
))) ||
114 (pim_ifp
->itype
== PIM_INTERFACE_SSM
))
118 * If we've received a multicast packet that isn't connected to
121 if (!pim_mroute_connected_to_source (ifp
, msg
->im_src
))
123 if (PIM_DEBUG_PIM_TRACE
)
124 zlog_debug ("%s: Received incoming packet that does originate on our seg",
125 __PRETTY_FUNCTION__
);
129 if (PIM_DEBUG_PIM_TRACE
) {
130 zlog_debug("%s: Adding a Route for %s from %s for WHOLEPKT consumption",
131 __PRETTY_FUNCTION__
, grp_str
, src_str
);
134 up
= pim_upstream_add(msg
->im_src
, msg
->im_dst
, ifp
);
136 if (PIM_DEBUG_PIM_TRACE
) {
137 zlog_debug("%s: Failure to add upstream information for (%s,%s)",
144 pim_upstream_keep_alive_timer_start (up
, PIM_KEEPALIVE_PERIOD
);
146 up
->channel_oil
= pim_channel_oil_add(msg
->im_dst
,
148 pim_ifp
->mroute_vif_index
);
149 if (!up
->channel_oil
) {
150 if (PIM_DEBUG_PIM_TRACE
) {
151 zlog_debug("%s: Failure to add channel oil for (%s,%s)",
157 up
->channel_oil
->cc
.pktcnt
++;
159 pim_channel_add_oif(up
->channel_oil
, pim_regiface
, PIM_OIF_FLAG_PROTO_SOURCE
);
165 pim_mroute_msg_wholepkt (int fd
, struct interface
*ifp
, const char *buf
,
166 const char *src_str
, const char *grp_str
)
168 struct pim_interface
*pim_ifp
;
169 struct in_addr group
;
172 const struct ip
*ip_hdr
;
173 struct pim_upstream
*up
;
175 ip_hdr
= (const struct ip
*)buf
;
177 src
= ip_hdr
->ip_src
;
178 group
= ip_hdr
->ip_dst
;
180 up
= pim_upstream_find(src
, group
);
182 if (PIM_DEBUG_PIM_TRACE
) {
183 zlog_debug("%s: Unable to find upstream channel WHOLEPKT(%s,%s)",
184 __PRETTY_FUNCTION__
, src_str
, grp_str
);
189 pim_ifp
= up
->rpf
.source_nexthop
.interface
->info
;
193 if ((rpg
->rpf_addr
.s_addr
== INADDR_NONE
) ||
195 (!(PIM_I_am_DR(pim_ifp
))) ||
196 (pim_ifp
->itype
== PIM_INTERFACE_SSM
)) {
197 if (PIM_DEBUG_PIM_TRACE
) {
198 zlog_debug("%s: Failed Check send packet", __PRETTY_FUNCTION__
);
203 pim_register_send((const struct ip
*)(buf
+ sizeof(struct ip
)), rpg
);
208 pim_mroute_msg_wrongvif (int fd
, struct interface
*ifp
, const struct igmpmsg
*msg
,
209 const char *src_str
, const char *grp_str
)
211 struct pim_ifchannel
*ch
;
212 struct pim_interface
*pim_ifp
;
215 Send Assert(S,G) on iif as response to WRONGVIF kernel upcall.
217 RFC 4601 4.8.2. PIM-SSM-Only Routers
219 iif is the incoming interface of the packet.
220 if (iif is in inherited_olist(S,G)) {
221 send Assert(S,G) on iif
226 if (PIM_DEBUG_PIM_TRACE
) {
227 zlog_debug("%s: WRONGVIF (S,G)=(%s,%s) could not find input interface for input_vif_index=%d",
229 src_str
, grp_str
, msg
->im_vif
);
236 if (PIM_DEBUG_PIM_TRACE
) {
237 zlog_debug("%s: WRONGVIF (S,G)=(%s,%s) multicast not enabled on interface %s",
239 src_str
, grp_str
, ifp
->name
);
244 ch
= pim_ifchannel_find(ifp
, msg
->im_src
, msg
->im_dst
);
246 if (PIM_DEBUG_PIM_TRACE
) {
247 zlog_debug("%s: WRONGVIF (S,G)=(%s,%s) could not find channel on interface %s",
249 src_str
, grp_str
, ifp
->name
);
255 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
257 Transitions from NoInfo State
259 An (S,G) data packet arrives on interface I, AND
260 CouldAssert(S,G,I)==TRUE An (S,G) data packet arrived on an
261 downstream interface that is in our (S,G) outgoing interface
262 list. We optimistically assume that we will be the assert
263 winner for this (S,G), and so we transition to the "I am Assert
264 Winner" state and perform Actions A1 (below), which will
265 initiate the assert negotiation for (S,G).
268 if (ch
->ifassert_state
!= PIM_IFASSERT_NOINFO
) {
269 if (PIM_DEBUG_PIM_TRACE
) {
270 zlog_debug("%s: WRONGVIF (S,G)=(%s,%s) channel is not on Assert NoInfo state for interface %s",
272 src_str
, grp_str
, ifp
->name
);
277 if (!PIM_IF_FLAG_TEST_COULD_ASSERT(ch
->flags
)) {
278 if (PIM_DEBUG_PIM_TRACE
) {
279 zlog_debug("%s: WRONGVIF (S,G)=(%s,%s) interface %s is not downstream for channel",
281 src_str
, grp_str
, ifp
->name
);
286 if (assert_action_a1(ch
)) {
287 if (PIM_DEBUG_PIM_TRACE
) {
288 zlog_debug("%s: WRONGVIF (S,G)=(%s,%s) assert_action_a1 failure on interface %s",
290 src_str
, grp_str
, ifp
->name
);
298 int pim_mroute_msg(int fd
, const char *buf
, int buf_size
)
300 struct interface
*ifp
;
301 const struct ip
*ip_hdr
;
302 const struct igmpmsg
*msg
;
303 char src_str
[100] = "<src?>";
304 char grp_str
[100] = "<grp?>";
306 ip_hdr
= (const struct ip
*) buf
;
308 /* kernel upcall must have protocol=0 */
310 /* this is not a kernel upcall */
311 if (PIM_DEBUG_PIM_TRACE
) {
312 pim_inet4_dump("<src?>", ip_hdr
->ip_src
, src_str
, sizeof(src_str
));
313 pim_inet4_dump("<grp?>", ip_hdr
->ip_dst
, grp_str
, sizeof(grp_str
));
314 zlog_debug("%s: not a kernel upcall proto=%d src: %s dst: %s msg_size=%d",
315 __PRETTY_FUNCTION__
, ip_hdr
->ip_p
, src_str
, grp_str
, buf_size
);
320 msg
= (const struct igmpmsg
*) buf
;
322 ifp
= pim_if_find_by_vif_index(msg
->im_vif
);
324 if (PIM_DEBUG_PIM_TRACE
) {
325 pim_inet4_dump("<src?>", msg
->im_src
, src_str
, sizeof(src_str
));
326 pim_inet4_dump("<grp?>", msg
->im_dst
, grp_str
, sizeof(grp_str
));
327 zlog_warn("%s: kernel upcall %s type=%d ip_p=%d from fd=%d for (S,G)=(%s,%s) on %s vifi=%d",
329 igmpmsgtype2str
[msg
->im_msgtype
],
335 ifp
? ifp
->name
: "<ifname?>",
339 switch (msg
->im_msgtype
) {
340 case IGMPMSG_WRONGVIF
:
341 return pim_mroute_msg_wrongvif(fd
, ifp
, msg
, src_str
, grp_str
);
343 case IGMPMSG_NOCACHE
:
344 return pim_mroute_msg_nocache(fd
, ifp
, msg
, src_str
, grp_str
);
346 case IGMPMSG_WHOLEPKT
:
347 return pim_mroute_msg_wholepkt(fd
, ifp
, (const char *)msg
, src_str
, grp_str
);
356 static int mroute_read_msg(int fd
)
358 const int msg_min_size
= MAX(sizeof(struct ip
), sizeof(struct igmpmsg
));
362 if (((int) sizeof(buf
)) < msg_min_size
) {
363 zlog_err("%s: fd=%d: buf size=%zu lower than msg_min=%d",
364 __PRETTY_FUNCTION__
, fd
, sizeof(buf
), msg_min_size
);
368 rd
= read(fd
, buf
, sizeof(buf
));
370 zlog_warn("%s: failure reading fd=%d: errno=%d: %s",
371 __PRETTY_FUNCTION__
, fd
, errno
, safe_strerror(errno
));
375 if (rd
< msg_min_size
) {
376 zlog_warn("%s: short message reading fd=%d: read=%d msg_min=%d",
377 __PRETTY_FUNCTION__
, fd
, rd
, msg_min_size
);
381 return pim_mroute_msg(fd
, buf
, rd
);
384 static int mroute_read(struct thread
*t
)
390 zassert(!THREAD_ARG(t
));
393 zassert(fd
== qpim_mroute_socket_fd
);
395 result
= mroute_read_msg(fd
);
398 qpim_mroute_socket_reader
= 0;
404 static void mroute_read_on()
406 zassert(!qpim_mroute_socket_reader
);
407 zassert(PIM_MROUTE_IS_ENABLED
);
409 THREAD_READ_ON(master
, qpim_mroute_socket_reader
,
410 mroute_read
, 0, qpim_mroute_socket_fd
);
413 static void mroute_read_off()
415 THREAD_OFF(qpim_mroute_socket_reader
);
418 int pim_mroute_socket_enable()
422 if (PIM_MROUTE_IS_ENABLED
)
425 if ( pimd_privs
.change (ZPRIVS_RAISE
) )
426 zlog_err ("pim_mroute_socket_enable: could not raise privs, %s",
427 safe_strerror (errno
) );
429 fd
= socket(AF_INET
, SOCK_RAW
, IPPROTO_IGMP
);
431 if ( pimd_privs
.change (ZPRIVS_LOWER
) )
432 zlog_err ("pim_mroute_socket_enable: could not lower privs, %s",
433 safe_strerror (errno
) );
436 zlog_warn("Could not create mroute socket: errno=%d: %s",
437 errno
, safe_strerror(errno
));
441 if (pim_mroute_set(fd
, 1)) {
442 zlog_warn("Could not enable mroute on socket fd=%d: errno=%d: %s",
443 fd
, errno
, safe_strerror(errno
));
448 qpim_mroute_socket_fd
= fd
;
450 qpim_mroute_socket_creation
= pim_time_monotonic_sec();
453 zassert(PIM_MROUTE_IS_ENABLED
);
458 int pim_mroute_socket_disable()
460 if (PIM_MROUTE_IS_DISABLED
)
463 if (pim_mroute_set(qpim_mroute_socket_fd
, 0)) {
464 zlog_warn("Could not disable mroute on socket fd=%d: errno=%d: %s",
465 qpim_mroute_socket_fd
, errno
, safe_strerror(errno
));
469 if (close(qpim_mroute_socket_fd
)) {
470 zlog_warn("Failure closing mroute socket: fd=%d errno=%d: %s",
471 qpim_mroute_socket_fd
, errno
, safe_strerror(errno
));
476 qpim_mroute_socket_fd
= -1;
478 zassert(PIM_MROUTE_IS_DISABLED
);
484 For each network interface (e.g., physical or a virtual tunnel) that
485 would be used for multicast forwarding, a corresponding multicast
486 interface must be added to the kernel.
488 int pim_mroute_add_vif(struct interface
*ifp
, struct in_addr ifaddr
, unsigned char flags
)
490 struct pim_interface
*pim_ifp
= ifp
->info
;
494 if (PIM_MROUTE_IS_DISABLED
) {
495 zlog_warn("%s: global multicast is disabled",
496 __PRETTY_FUNCTION__
);
500 memset(&vc
, 0, sizeof(vc
));
501 vc
.vifc_vifi
= pim_ifp
->mroute_vif_index
;
502 #ifdef VIFF_USE_IFINDEX
503 vc
.vifc_lcl_ifindex
= ifp
->ifindex
;
505 if (ifaddr
.s_addr
== INADDR_ANY
) {
506 zlog_warn("%s: unnumbered interfaces are not supported on this platform",
507 __PRETTY_FUNCTION__
);
510 memcpy(&vc
.vifc_lcl_addr
, &ifaddr
, sizeof(vc
.vifc_lcl_addr
));
512 vc
.vifc_flags
= flags
;
513 vc
.vifc_threshold
= PIM_MROUTE_MIN_TTL
;
514 vc
.vifc_rate_limit
= 0;
516 #ifdef PIM_DVMRP_TUNNEL
517 if (vc
.vifc_flags
& VIFF_TUNNEL
) {
518 memcpy(&vc
.vifc_rmt_addr
, &vif_remote_addr
, sizeof(vc
.vifc_rmt_addr
));
522 err
= setsockopt(qpim_mroute_socket_fd
, IPPROTO_IP
, MRT_ADD_VIF
, (void*) &vc
, sizeof(vc
));
524 char ifaddr_str
[100];
527 pim_inet4_dump("<ifaddr?>", ifaddr
, ifaddr_str
, sizeof(ifaddr_str
));
529 zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_ADD_VIF,vif_index=%d,ifaddr=%s,flag=%d): errno=%d: %s",
530 __FILE__
, __PRETTY_FUNCTION__
,
531 qpim_mroute_socket_fd
, ifp
->ifindex
, ifaddr_str
, flags
,
532 e
, safe_strerror(e
));
540 int pim_mroute_del_vif(int vif_index
)
545 if (PIM_MROUTE_IS_DISABLED
) {
546 zlog_warn("%s: global multicast is disabled",
547 __PRETTY_FUNCTION__
);
551 memset(&vc
, 0, sizeof(vc
));
552 vc
.vifc_vifi
= vif_index
;
554 err
= setsockopt(qpim_mroute_socket_fd
, IPPROTO_IP
, MRT_DEL_VIF
, (void*) &vc
, sizeof(vc
));
557 zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_DEL_VIF,vif_index=%d): errno=%d: %s",
558 __FILE__
, __PRETTY_FUNCTION__
,
559 qpim_mroute_socket_fd
, vif_index
,
560 e
, safe_strerror(e
));
568 int pim_mroute_add(struct channel_oil
*c_oil
)
573 qpim_mroute_add_last
= pim_time_monotonic_sec();
574 ++qpim_mroute_add_events
;
576 if (PIM_MROUTE_IS_DISABLED
) {
577 zlog_warn("%s: global multicast is disabled",
578 __PRETTY_FUNCTION__
);
582 /* The linux kernel *expects* the incoming
583 * vif to be part of the outgoing list
584 * in the case of a (*,G).
586 if (c_oil
->oil
.mfcc_origin
.s_addr
== INADDR_ANY
)
588 orig
= c_oil
->oil
.mfcc_ttls
[c_oil
->oil
.mfcc_parent
];
589 c_oil
->oil
.mfcc_ttls
[c_oil
->oil
.mfcc_parent
] = 1;
592 err
= setsockopt(qpim_mroute_socket_fd
, IPPROTO_IP
, MRT_ADD_MFC
,
593 &c_oil
->oil
, sizeof(c_oil
->oil
));
595 if (c_oil
->oil
.mfcc_origin
.s_addr
== INADDR_ANY
)
596 c_oil
->oil
.mfcc_ttls
[c_oil
->oil
.mfcc_parent
] = orig
;
600 zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_ADD_MFC): errno=%d: %s",
601 __FILE__
, __PRETTY_FUNCTION__
,
602 qpim_mroute_socket_fd
,
603 e
, safe_strerror(e
));
608 c_oil
->installed
= 1;
612 int pim_mroute_del (struct channel_oil
*c_oil
)
616 qpim_mroute_del_last
= pim_time_monotonic_sec();
617 ++qpim_mroute_del_events
;
619 if (PIM_MROUTE_IS_DISABLED
) {
620 zlog_warn("%s: global multicast is disabled",
621 __PRETTY_FUNCTION__
);
625 err
= setsockopt(qpim_mroute_socket_fd
, IPPROTO_IP
, MRT_DEL_MFC
, &c_oil
->oil
, sizeof(c_oil
->oil
));
628 zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_DEL_MFC): errno=%d: %s",
629 __FILE__
, __PRETTY_FUNCTION__
,
630 qpim_mroute_socket_fd
,
631 e
, safe_strerror(e
));
636 c_oil
->installed
= 0;
642 pim_mroute_update_counters (struct channel_oil
*c_oil
)
644 struct sioc_sg_req sgreq
;
646 memset (&sgreq
, 0, sizeof(sgreq
));
647 sgreq
.src
= c_oil
->oil
.mfcc_origin
;
648 sgreq
.grp
= c_oil
->oil
.mfcc_mcastgrp
;
650 c_oil
->cc
.oldpktcnt
= c_oil
->cc
.pktcnt
;
651 c_oil
->cc
.oldbytecnt
= c_oil
->cc
.bytecnt
;
652 c_oil
->cc
.oldwrong_if
= c_oil
->cc
.wrong_if
;
654 if (ioctl (qpim_mroute_socket_fd
, SIOCGETSGCNT
, &sgreq
))
657 char source_str
[100];
659 pim_inet4_dump("<group?>", c_oil
->oil
.mfcc_mcastgrp
, group_str
, sizeof(group_str
));
660 pim_inet4_dump("<source?>", c_oil
->oil
.mfcc_origin
, source_str
, sizeof(source_str
));
662 zlog_warn ("ioctl(SIOCGETSGCNT=%lu) failure for (S,G)=(%s,%s): errno=%d: %s",
663 (unsigned long)SIOCGETSGCNT
,
667 safe_strerror(errno
));
671 c_oil
->cc
.pktcnt
= sgreq
.pktcnt
;
672 c_oil
->cc
.bytecnt
= sgreq
.bytecnt
;
673 c_oil
->cc
.wrong_if
= sgreq
.wrong_if
;