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,
20 $QuaggaId: $Format:%an, %ai, %h$ $
30 #include "pim_mroute.h"
34 #include "pim_iface.h"
35 #include "pim_macro.h"
38 #include "pim_register.h"
41 extern struct zebra_privs_t pimd_privs
;
43 static void mroute_read_on(void);
45 static int pim_mroute_set(int fd
, int enable
)
48 int opt
= enable
? MRT_INIT
: MRT_DONE
;
49 socklen_t opt_len
= sizeof(opt
);
51 err
= setsockopt(fd
, IPPROTO_IP
, opt
, &opt
, opt_len
);
54 zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,%s=%d): errno=%d: %s",
55 __FILE__
, __PRETTY_FUNCTION__
,
56 fd
, enable
? "MRT_INIT" : "MRT_DONE", opt
, e
, safe_strerror(e
));
62 zlog_info("%s %s: setsockopt(fd=%d,IPPROTO_IP,MRT_INIT,opt=%d): ok",
63 __FILE__
, __PRETTY_FUNCTION__
,
71 pim_mroute_connected_to_source (struct interface
*ifp
, struct in_addr src
)
73 struct listnode
*cnode
;
79 p
.prefixlen
= IPV4_MAX_BITLEN
;
81 for (ALL_LIST_ELEMENTS_RO (ifp
->connected
, cnode
, c
))
83 if ((c
->address
->family
== AF_INET
) &&
84 prefix_match (CONNECTED_PREFIX (c
), &p
))
93 static const char *igmpmsgtype2str
[IGMPMSG_WHOLEPKT
+ 1] = {
100 pim_mroute_msg_nocache (int fd
, struct interface
*ifp
, const struct igmpmsg
*msg
,
101 const char *src_str
, const char *grp_str
)
103 struct pim_interface
*pim_ifp
= ifp
->info
;
104 struct pim_upstream
*up
;
107 rpg
= RP(msg
->im_dst
);
109 * If the incoming interface is unknown OR
110 * the Interface type is SSM we don't need to
113 if ((rpg
->rpf_addr
.s_addr
== INADDR_NONE
) ||
115 (!(PIM_I_am_DR(pim_ifp
))) ||
116 (pim_ifp
->itype
== PIM_INTERFACE_SSM
))
120 * If we've received a multicast packet that isn't connected to
123 if (!pim_mroute_connected_to_source (ifp
, msg
->im_src
))
125 if (PIM_DEBUG_PIM_TRACE
)
126 zlog_debug ("%s: Received incoming packet that does originate on our seg",
127 __PRETTY_FUNCTION__
);
131 if (PIM_DEBUG_PIM_TRACE
) {
132 zlog_debug("%s: Adding a Route for %s from %s for WHOLEPKT consumption",
133 __PRETTY_FUNCTION__
, grp_str
, src_str
);
136 up
= pim_upstream_add(msg
->im_src
, msg
->im_dst
, ifp
);
138 if (PIM_DEBUG_PIM_TRACE
) {
139 zlog_debug("%s: Failure to add upstream information for (%s,%s)",
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)",
158 pim_channel_add_oif(up
->channel_oil
, pim_regiface
, PIM_OIF_FLAG_PROTO_SOURCE
);
164 pim_mroute_msg_wholepkt (int fd
, struct interface
*ifp
, const char *buf
,
165 const char *src_str
, const char *grp_str
)
167 struct pim_interface
*pim_ifp
;
168 struct in_addr group
;
171 const struct ip
*ip_hdr
;
172 struct pim_upstream
*up
;
174 ip_hdr
= (const struct ip
*)buf
;
176 src
= ip_hdr
->ip_src
;
177 group
= ip_hdr
->ip_dst
;
179 up
= pim_upstream_find(src
, group
);
181 if (PIM_DEBUG_PIM_TRACE
) {
182 zlog_debug("%s: Unable to find upstream channel WHOLEPKT(%s,%s)",
183 __PRETTY_FUNCTION__
, src_str
, grp_str
);
188 pim_ifp
= up
->rpf
.source_nexthop
.interface
->info
;
192 if ((rpg
->rpf_addr
.s_addr
== INADDR_NONE
) ||
194 (!(PIM_I_am_DR(pim_ifp
))) ||
195 (pim_ifp
->itype
== PIM_INTERFACE_SSM
)) {
196 if (PIM_DEBUG_PIM_TRACE
) {
197 zlog_debug("%s: Failed Check send packet", __PRETTY_FUNCTION__
);
202 pim_register_send((const struct ip
*)(buf
+ sizeof(struct ip
)), rpg
);
207 pim_mroute_msg_wrongvif (int fd
, struct interface
*ifp
, const struct igmpmsg
*msg
,
208 const char *src_str
, const char *grp_str
)
210 struct pim_ifchannel
*ch
;
211 struct pim_interface
*pim_ifp
;
214 Send Assert(S,G) on iif as response to WRONGVIF kernel upcall.
216 RFC 4601 4.8.2. PIM-SSM-Only Routers
218 iif is the incoming interface of the packet.
219 if (iif is in inherited_olist(S,G)) {
220 send Assert(S,G) on iif
225 if (PIM_DEBUG_PIM_TRACE
) {
226 zlog_debug("%s: WRONGVIF (S,G)=(%s,%s) could not find input interface for input_vif_index=%d",
228 src_str
, grp_str
, msg
->im_vif
);
235 if (PIM_DEBUG_PIM_TRACE
) {
236 zlog_debug("%s: WRONGVIF (S,G)=(%s,%s) multicast not enabled on interface %s",
238 src_str
, grp_str
, ifp
->name
);
243 ch
= pim_ifchannel_find(ifp
, msg
->im_src
, msg
->im_dst
);
245 if (PIM_DEBUG_PIM_TRACE
) {
246 zlog_debug("%s: WRONGVIF (S,G)=(%s,%s) could not find channel on interface %s",
248 src_str
, grp_str
, ifp
->name
);
254 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
256 Transitions from NoInfo State
258 An (S,G) data packet arrives on interface I, AND
259 CouldAssert(S,G,I)==TRUE An (S,G) data packet arrived on an
260 downstream interface that is in our (S,G) outgoing interface
261 list. We optimistically assume that we will be the assert
262 winner for this (S,G), and so we transition to the "I am Assert
263 Winner" state and perform Actions A1 (below), which will
264 initiate the assert negotiation for (S,G).
267 if (ch
->ifassert_state
!= PIM_IFASSERT_NOINFO
) {
268 if (PIM_DEBUG_PIM_TRACE
) {
269 zlog_debug("%s: WRONGVIF (S,G)=(%s,%s) channel is not on Assert NoInfo state for interface %s",
271 src_str
, grp_str
, ifp
->name
);
276 if (!PIM_IF_FLAG_TEST_COULD_ASSERT(ch
->flags
)) {
277 if (PIM_DEBUG_PIM_TRACE
) {
278 zlog_debug("%s: WRONGVIF (S,G)=(%s,%s) interface %s is not downstream for channel",
280 src_str
, grp_str
, ifp
->name
);
285 if (assert_action_a1(ch
)) {
286 if (PIM_DEBUG_PIM_TRACE
) {
287 zlog_debug("%s: WRONGVIF (S,G)=(%s,%s) assert_action_a1 failure on interface %s",
289 src_str
, grp_str
, ifp
->name
);
297 int pim_mroute_msg(int fd
, const char *buf
, int buf_size
)
299 struct interface
*ifp
;
300 const struct ip
*ip_hdr
;
301 const struct igmpmsg
*msg
;
302 char src_str
[100] = "<src?>";
303 char grp_str
[100] = "<grp?>";
305 ip_hdr
= (const struct ip
*) buf
;
307 /* kernel upcall must have protocol=0 */
309 /* this is not a kernel upcall */
310 if (PIM_DEBUG_PIM_TRACE
) {
311 pim_inet4_dump("<src?>", ip_hdr
->ip_src
, src_str
, sizeof(src_str
));
312 pim_inet4_dump("<grp?>", ip_hdr
->ip_dst
, grp_str
, sizeof(grp_str
));
313 zlog_debug("%s: not a kernel upcall proto=%d src: %s dst: %s msg_size=%d",
314 __PRETTY_FUNCTION__
, ip_hdr
->ip_p
, src_str
, grp_str
, buf_size
);
315 //zlog_hexdump(buf, 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 zlog_hexdump(buf
, buf_size
);
348 return pim_mroute_msg_wholepkt(fd
, ifp
, (const char *)msg
, src_str
, grp_str
);
357 static int mroute_read_msg(int fd
)
359 const int msg_min_size
= MAX(sizeof(struct ip
), sizeof(struct igmpmsg
));
363 if (((int) sizeof(buf
)) < msg_min_size
) {
364 zlog_err("%s: fd=%d: buf size=%zu lower than msg_min=%d",
365 __PRETTY_FUNCTION__
, fd
, sizeof(buf
), msg_min_size
);
369 rd
= read(fd
, buf
, sizeof(buf
));
371 zlog_warn("%s: failure reading fd=%d: errno=%d: %s",
372 __PRETTY_FUNCTION__
, fd
, errno
, safe_strerror(errno
));
376 if (rd
< msg_min_size
) {
377 zlog_warn("%s: short message reading fd=%d: read=%d msg_min=%d",
378 __PRETTY_FUNCTION__
, fd
, rd
, msg_min_size
);
382 return pim_mroute_msg(fd
, buf
, rd
);
385 static int mroute_read(struct thread
*t
)
391 zassert(!THREAD_ARG(t
));
394 zassert(fd
== qpim_mroute_socket_fd
);
396 result
= mroute_read_msg(fd
);
399 qpim_mroute_socket_reader
= 0;
405 static void mroute_read_on()
407 zassert(!qpim_mroute_socket_reader
);
408 zassert(PIM_MROUTE_IS_ENABLED
);
410 THREAD_READ_ON(master
, qpim_mroute_socket_reader
,
411 mroute_read
, 0, qpim_mroute_socket_fd
);
414 static void mroute_read_off()
416 THREAD_OFF(qpim_mroute_socket_reader
);
419 int pim_mroute_socket_enable()
423 if (PIM_MROUTE_IS_ENABLED
)
426 if ( pimd_privs
.change (ZPRIVS_RAISE
) )
427 zlog_err ("pim_mroute_socket_enable: could not raise privs, %s",
428 safe_strerror (errno
) );
430 fd
= socket(AF_INET
, SOCK_RAW
, IPPROTO_IGMP
);
432 if ( pimd_privs
.change (ZPRIVS_LOWER
) )
433 zlog_err ("pim_mroute_socket_enable: could not lower privs, %s",
434 safe_strerror (errno
) );
437 zlog_warn("Could not create mroute socket: errno=%d: %s",
438 errno
, safe_strerror(errno
));
442 if (pim_mroute_set(fd
, 1)) {
443 zlog_warn("Could not enable mroute on socket fd=%d: errno=%d: %s",
444 fd
, errno
, safe_strerror(errno
));
449 qpim_mroute_socket_fd
= fd
;
451 qpim_mroute_socket_creation
= pim_time_monotonic_sec();
454 zassert(PIM_MROUTE_IS_ENABLED
);
459 int pim_mroute_socket_disable()
461 if (PIM_MROUTE_IS_DISABLED
)
464 if (pim_mroute_set(qpim_mroute_socket_fd
, 0)) {
465 zlog_warn("Could not disable mroute on socket fd=%d: errno=%d: %s",
466 qpim_mroute_socket_fd
, errno
, safe_strerror(errno
));
470 if (close(qpim_mroute_socket_fd
)) {
471 zlog_warn("Failure closing mroute socket: fd=%d errno=%d: %s",
472 qpim_mroute_socket_fd
, errno
, safe_strerror(errno
));
477 qpim_mroute_socket_fd
= -1;
479 zassert(PIM_MROUTE_IS_DISABLED
);
485 For each network interface (e.g., physical or a virtual tunnel) that
486 would be used for multicast forwarding, a corresponding multicast
487 interface must be added to the kernel.
489 int pim_mroute_add_vif(struct interface
*ifp
, struct in_addr ifaddr
, unsigned char flags
)
491 struct pim_interface
*pim_ifp
= ifp
->info
;
495 if (PIM_MROUTE_IS_DISABLED
) {
496 zlog_warn("%s: global multicast is disabled",
497 __PRETTY_FUNCTION__
);
501 memset(&vc
, 0, sizeof(vc
));
502 vc
.vifc_vifi
= pim_ifp
->mroute_vif_index
;
503 vc
.vifc_lcl_ifindex
= ifp
->ifindex
;
504 vc
.vifc_flags
= flags
;
505 vc
.vifc_threshold
= PIM_MROUTE_MIN_TTL
;
506 vc
.vifc_rate_limit
= 0;
508 #ifdef PIM_DVMRP_TUNNEL
509 if (vc
.vifc_flags
& VIFF_TUNNEL
) {
510 memcpy(&vc
.vifc_rmt_addr
, &vif_remote_addr
, sizeof(vc
.vifc_rmt_addr
));
514 err
= setsockopt(qpim_mroute_socket_fd
, IPPROTO_IP
, MRT_ADD_VIF
, (void*) &vc
, sizeof(vc
));
516 char ifaddr_str
[100];
519 pim_inet4_dump("<ifaddr?>", ifaddr
, ifaddr_str
, sizeof(ifaddr_str
));
521 zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_ADD_VIF,vif_index=%d,ifaddr=%s,flag=%d): errno=%d: %s",
522 __FILE__
, __PRETTY_FUNCTION__
,
523 qpim_mroute_socket_fd
, ifp
->ifindex
, ifaddr_str
, flags
,
524 e
, safe_strerror(e
));
532 int pim_mroute_del_vif(int vif_index
)
537 if (PIM_MROUTE_IS_DISABLED
) {
538 zlog_warn("%s: global multicast is disabled",
539 __PRETTY_FUNCTION__
);
543 memset(&vc
, 0, sizeof(vc
));
544 vc
.vifc_vifi
= vif_index
;
546 err
= setsockopt(qpim_mroute_socket_fd
, IPPROTO_IP
, MRT_DEL_VIF
, (void*) &vc
, sizeof(vc
));
549 zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_DEL_VIF,vif_index=%d): errno=%d: %s",
550 __FILE__
, __PRETTY_FUNCTION__
,
551 qpim_mroute_socket_fd
, vif_index
,
552 e
, safe_strerror(e
));
560 int pim_mroute_add(struct mfcctl
*mc
)
565 qpim_mroute_add_last
= pim_time_monotonic_sec();
566 ++qpim_mroute_add_events
;
568 if (PIM_MROUTE_IS_DISABLED
) {
569 zlog_warn("%s: global multicast is disabled",
570 __PRETTY_FUNCTION__
);
574 /* The linux kernel *expects* the incoming
575 * vif to be part of the outgoing list
576 * in the case of a (*,G).
578 if (mc
->mfcc_origin
.s_addr
== INADDR_ANY
)
580 orig
= mc
->mfcc_ttls
[mc
->mfcc_parent
];
581 mc
->mfcc_ttls
[mc
->mfcc_parent
] = 1;
584 err
= setsockopt(qpim_mroute_socket_fd
, IPPROTO_IP
, MRT_ADD_MFC
,
587 if (mc
->mfcc_origin
.s_addr
== INADDR_ANY
)
588 mc
->mfcc_ttls
[mc
->mfcc_parent
] = orig
;
592 zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_ADD_MFC): errno=%d: %s",
593 __FILE__
, __PRETTY_FUNCTION__
,
594 qpim_mroute_socket_fd
,
595 e
, safe_strerror(e
));
603 int pim_mroute_del(struct mfcctl
*mc
)
607 qpim_mroute_del_last
= pim_time_monotonic_sec();
608 ++qpim_mroute_del_events
;
610 if (PIM_MROUTE_IS_DISABLED
) {
611 zlog_warn("%s: global multicast is disabled",
612 __PRETTY_FUNCTION__
);
616 err
= setsockopt(qpim_mroute_socket_fd
, IPPROTO_IP
, MRT_DEL_MFC
, mc
, sizeof(*mc
));
619 zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_DEL_MFC): errno=%d: %s",
620 __FILE__
, __PRETTY_FUNCTION__
,
621 qpim_mroute_socket_fd
,
622 e
, safe_strerror(e
));