mirror of
https://gitlab.nic.cz/labs/bird.git
synced 2025-01-03 15:41:54 +00:00
Merge commit '76dd4730eac6a632ab28ef85d266cbfc0abc89ca' into pim
This commit is contained in:
commit
c461ab7e90
@ -17,6 +17,9 @@
|
||||
#include <libssh/libssh.h>
|
||||
#endif
|
||||
|
||||
struct mif_group;
|
||||
struct mif;
|
||||
|
||||
#ifdef HAVE_LIBSSH
|
||||
struct ssh_sock {
|
||||
const char *username; /* (Required) SSH user name */
|
||||
@ -105,6 +108,7 @@ int sk_join_group(sock *s, ip_addr maddr); /* Join multicast group on sk iface *
|
||||
int sk_leave_group(sock *s, ip_addr maddr); /* Leave multicast group on sk iface */
|
||||
int sk_set_router_alert(sock *s, int ra);
|
||||
int sk_setup_broadcast(sock *s);
|
||||
int sk_setup_igmp(sock *s, struct mif_group *grp, struct mif *mif);
|
||||
int sk_set_ttl(sock *s, int ttl); /* Set transmit TTL for given socket */
|
||||
int sk_set_min_ttl(sock *s, int ttl); /* Set minimal accepted TTL for given socket */
|
||||
int sk_set_md5_auth(sock *s, ip_addr local, ip_addr remote, struct iface *ifa, char *passwd, int setkey);
|
||||
|
68
nest/iface.c
68
nest/iface.c
@ -30,6 +30,7 @@
|
||||
#include "nest/protocol.h"
|
||||
#include "nest/cli.h"
|
||||
#include "lib/resource.h"
|
||||
#include "lib/socket.h"
|
||||
#include "lib/string.h"
|
||||
#include "conf/conf.h"
|
||||
#include "sysdep/unix/krt.h"
|
||||
@ -723,6 +724,10 @@ if_init(void)
|
||||
* Multicast Ifaces
|
||||
*/
|
||||
|
||||
struct mkrt_proto;
|
||||
void mkrt_register_mif(struct mkrt_proto *p, struct mif *mif);
|
||||
void mkrt_unregister_mif(struct mkrt_proto *p, struct mif *mif);
|
||||
|
||||
static struct mif_group *
|
||||
mif_get_group(void)
|
||||
{
|
||||
@ -754,6 +759,9 @@ mif_get(struct mif_group *grp, struct iface *iface)
|
||||
grp->mifs[mif->index] = mif;
|
||||
MIFS_SET(mif, grp->indexes);
|
||||
|
||||
if (grp->owner)
|
||||
mkrt_register_mif((struct mkrt_proto *) grp->owner, mif);
|
||||
|
||||
return mif;
|
||||
}
|
||||
|
||||
@ -763,6 +771,9 @@ mif_free(struct mif_group *grp, struct mif *mif)
|
||||
if (--mif->uc)
|
||||
return;
|
||||
|
||||
if (grp->owner)
|
||||
mkrt_unregister_mif((struct mkrt_proto *) grp->owner, mif);
|
||||
|
||||
node *n;
|
||||
WALK_LIST_FIRST(n, mif->sockets)
|
||||
rem_node(n);
|
||||
@ -773,6 +784,63 @@ mif_free(struct mif_group *grp, struct mif *mif)
|
||||
mb_free(mif);
|
||||
}
|
||||
|
||||
/*
|
||||
* Move socket from global list to MIF based lists. These lists are used to
|
||||
* deliver IGMP messages by mif_forward_igmp().
|
||||
*/
|
||||
void
|
||||
mif_listen_igmp(struct mif_group *grp, struct mif *mif, sock *s)
|
||||
{
|
||||
rem_node(&s->n);
|
||||
add_tail(mif ? &mif->sockets : &grp->sockets, &s->n);
|
||||
}
|
||||
|
||||
/*
|
||||
* Forward a packet from one socket to another. Emulates the receiving routine.
|
||||
* Socket is in the same state as if it received the packet itself, but must not
|
||||
* modify it to preserve it for others.
|
||||
*/
|
||||
static void
|
||||
mif_do_forward(sock *src, sock *dst, int len)
|
||||
{
|
||||
if (!dst->rx_hook)
|
||||
return;
|
||||
|
||||
dst->faddr = src->faddr;
|
||||
dst->laddr = src->laddr;
|
||||
dst->lifindex = src->lifindex;
|
||||
|
||||
dst->rbuf = src->rbuf;
|
||||
dst->rpos = src->rpos;
|
||||
dst->rbsize = src->rbsize;
|
||||
|
||||
dst->rx_hook(dst, len);
|
||||
|
||||
dst->faddr = dst->laddr = IPA_NONE;
|
||||
dst->lifindex = 0;
|
||||
|
||||
dst->rbuf = dst->rpos = NULL;
|
||||
dst->rbsize = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Forward a packet to all sockets registered for given MIF. It is used to
|
||||
* deliver IGMP messages from the MRT control socket to IGMP sockets.
|
||||
*/
|
||||
void
|
||||
mif_forward_igmp(struct mif_group *grp, struct mif *mif, sock *src, int len)
|
||||
{
|
||||
node *n, *nn;
|
||||
sock *dst;
|
||||
|
||||
WALK_LIST2_DELSAFE(dst, n, nn, grp->sockets, n)
|
||||
mif_do_forward(src, dst, len);
|
||||
|
||||
if (mif)
|
||||
WALK_LIST2_DELSAFE(dst, n, nn, mif->sockets, n)
|
||||
mif_do_forward(src, dst, len);
|
||||
}
|
||||
|
||||
/*
|
||||
* Interface Pattern Lists
|
||||
*/
|
||||
|
@ -56,6 +56,7 @@ struct mif_group {
|
||||
uint indexes;
|
||||
uint uc; /* Use count, not implemented */
|
||||
list sockets; /* Listening global IGMP sockets */
|
||||
struct proto *owner; /* MKernel responsible for MIFs */
|
||||
struct mif *mifs[MIFS_MAX];
|
||||
};
|
||||
|
||||
|
@ -16,6 +16,7 @@
|
||||
struct ea_list;
|
||||
struct protocol;
|
||||
struct proto;
|
||||
struct channel;
|
||||
struct rte_src;
|
||||
struct symbol;
|
||||
struct filter;
|
||||
@ -296,7 +297,7 @@ rte *rte_find(net *net, struct rte_src *src);
|
||||
rte *rte_get_temp(struct rta *);
|
||||
void rte_update2(struct channel *c, const net_addr *n, rte *new, struct rte_src *src);
|
||||
/* rte_update() moved to protocol.h to avoid dependency conflicts */
|
||||
int rt_examine(rtable *t, net_addr *a, struct proto *p, struct filter *filter);
|
||||
int rt_examine(struct channel *c, net_addr *a, void (*cb)(struct proto *, void *, rte *), void *data);
|
||||
rte *rt_export_merged(struct channel *c, net *net, rte **rt_free, struct ea_list **tmpa, linpool *pool, int silent);
|
||||
void rt_refresh_begin(rtable *t, struct channel *c);
|
||||
void rt_refresh_end(rtable *t, struct channel *c);
|
||||
|
@ -1406,9 +1406,10 @@ rte_discard(rte *old) /* Non-filtered route deletion, used during garbage collec
|
||||
|
||||
/* Check rtable for best route to given net whether it would be exported do p */
|
||||
int
|
||||
rt_examine(rtable *t, net_addr *a, struct proto *p, struct filter *filter)
|
||||
rt_examine(struct channel *c, net_addr *a, void (*cb)(struct proto *, void *, rte *), void *data)
|
||||
{
|
||||
net *n = net_find(t, a);
|
||||
struct proto *p = c->proto;
|
||||
net *n = net_find(c->table, a);
|
||||
rte *rt = n ? n->routes : NULL;
|
||||
|
||||
if (!rte_is_valid(rt))
|
||||
@ -1420,7 +1421,11 @@ rt_examine(rtable *t, net_addr *a, struct proto *p, struct filter *filter)
|
||||
ea_list *tmpa = rte_make_tmp_attrs(rt, rte_update_pool);
|
||||
int v = p->import_control ? p->import_control(p, &rt, &tmpa, rte_update_pool) : 0;
|
||||
if (v == RIC_PROCESS)
|
||||
v = (f_run(filter, &rt, &tmpa, rte_update_pool, FF_FORCE_TMPATTR) <= F_ACCEPT);
|
||||
v = (f_run(c->out_filter, &rt, &tmpa, rte_update_pool, FF_FORCE_TMPATTR) <= F_ACCEPT);
|
||||
|
||||
/* Call callback when route is exported */
|
||||
if (cb && (v > 0))
|
||||
cb(p, data, rt);
|
||||
|
||||
/* Discard temporary rte */
|
||||
if (rt != n->routes)
|
||||
|
@ -558,8 +558,7 @@ radv_check_active(struct radv_proto *p)
|
||||
if (!radv_trigger_valid(cf))
|
||||
return 1;
|
||||
|
||||
struct channel *c = p->p.main_channel;
|
||||
return rt_examine(c->table, &cf->trigger, &p->p, c->out_filter);
|
||||
return rt_examine(p->p.main_channel, &cf->trigger, NULL, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -1,3 +1,4 @@
|
||||
S log.c
|
||||
S krt.c
|
||||
S mkrt.c
|
||||
# io.c is documented under Resources
|
||||
|
@ -1,8 +1,8 @@
|
||||
src := io.c krt.c log.c main.c random.c
|
||||
src := io.c krt.c log.c main.c mkrt.c random.c
|
||||
obj := $(src-o-files)
|
||||
$(all-daemon)
|
||||
$(cf-local)
|
||||
$(conf-y-targets): $(s)krt.Y
|
||||
$(conf-y-targets): $(s)krt.Y $(s)mkrt.Y
|
||||
|
||||
src := $(filter-out main.c, $(src))
|
||||
tests_objs := $(tests_objs) $(src-o-files)
|
||||
|
@ -588,6 +588,39 @@ sk_setup_broadcast(sock *s)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void mif_listen_igmp(struct mif_group *grp, struct mif *mif, sock *s);
|
||||
|
||||
/**
|
||||
* sk_setup_igmp - enable IGMP reception for given socket
|
||||
* @s: socket
|
||||
* @grp: MIF group
|
||||
* @mif: MIF iface, optional
|
||||
*
|
||||
* IGMP sockets need to receive all IGMP packets regardless of multicast group.
|
||||
* The generic multicast API is insufficient for this and both Linux and BSDs
|
||||
* handle this by passing IGMP packets to multicast routing control socket.
|
||||
*
|
||||
* When sk_setup_igmp() is called for a socket, the socket is transfered from
|
||||
* general socket list to the appropriate MIF socket list and BIRD forwards all
|
||||
* packets received on a multicast routing control socket to this socket
|
||||
* internally. That means, all IGMP packets even for groups that no one is
|
||||
* joined are received here. As a side effect, no packets are received when
|
||||
* mkernel protocol is down and therefore a multicast control socket is closed.
|
||||
*
|
||||
* The socket must have type SK_IP, dport IPPROTO_IGMP and zero rbsize.
|
||||
*
|
||||
* Result: 0 for success, -1 for an error.
|
||||
*/
|
||||
|
||||
int
|
||||
sk_setup_igmp(sock *s, struct mif_group *grp, struct mif *mif)
|
||||
{
|
||||
ASSERT((s->type == SK_IP) && (s->dport == IPPROTO_IGMP) && !s->rbsize);
|
||||
|
||||
mif_listen_igmp(grp, mif, s);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* sk_set_ttl - set transmit TTL for given socket
|
||||
* @s: socket
|
||||
|
@ -39,6 +39,7 @@
|
||||
|
||||
#include "unix.h"
|
||||
#include "krt.h"
|
||||
#include "mkrt.h"
|
||||
|
||||
/*
|
||||
* Debugging
|
||||
@ -851,6 +852,7 @@ main(int argc, char **argv)
|
||||
protos_build();
|
||||
proto_build(&proto_unix_kernel);
|
||||
proto_build(&proto_unix_iface);
|
||||
proto_build(&proto_unix_mkrt);
|
||||
|
||||
struct config *conf = read_config();
|
||||
|
||||
|
41
sysdep/unix/mkrt.Y
Normal file
41
sysdep/unix/mkrt.Y
Normal file
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* BIRD -- UNIX Kernel Multicast Routing Configuration
|
||||
*
|
||||
* (c) 2016 Ondrej Hlavaty <aearsis@eideo.cz>
|
||||
*
|
||||
* Can be freely distributed and used under the terms of the GNU GPL.
|
||||
*/
|
||||
|
||||
CF_HDR
|
||||
|
||||
#include "sysdep/unix/mkrt.h"
|
||||
|
||||
CF_DEFINES
|
||||
|
||||
#define THIS_MKRT ((struct mkrt_config *) this_proto)
|
||||
|
||||
CF_DECLS
|
||||
|
||||
CF_KEYWORDS(MKERNEL)
|
||||
|
||||
CF_GRAMMAR
|
||||
|
||||
/* Kernel interface protocol */
|
||||
|
||||
CF_ADDTO(proto, mkrt_proto '}')
|
||||
|
||||
mkrt_proto_start: proto_start MKERNEL { this_proto = mkrt_init_config($1); }
|
||||
;
|
||||
|
||||
mkrt_proto:
|
||||
mkrt_proto_start proto_name '{'
|
||||
| mkrt_proto mkrt_proto_item ';'
|
||||
|
||||
mkrt_proto_item:
|
||||
proto_item
|
||||
| proto_channel { this_proto->net_type = $1->net_type; }
|
||||
;
|
||||
|
||||
CF_CODE
|
||||
|
||||
CF_END
|
503
sysdep/unix/mkrt.c
Normal file
503
sysdep/unix/mkrt.c
Normal file
@ -0,0 +1,503 @@
|
||||
/*
|
||||
* BIRD -- UNIX Kernel Multicast Routing
|
||||
*
|
||||
* (c) 2016 Ondrej Hlavaty <aearsis@eideo.cz>
|
||||
* (c) 2018 Ondrej Zajicek <santiago@crfreenet.org>
|
||||
* (c) 2018 CZ.NIC z.s.p.o.
|
||||
*
|
||||
* Can be freely distributed and used under the terms of the GNU GPL.
|
||||
*/
|
||||
|
||||
/**
|
||||
* DOC: Kernel Multicast Routing
|
||||
*
|
||||
* This protocol is the interface to the kernel part of multicast routing. It
|
||||
* handles registration of multicast interfaces (MIFs), maintenance of kernel
|
||||
* Multicast Forwarding Cache (MFC), and reception of incoming IGMP packets.
|
||||
*
|
||||
* Multicast forwarding in Linux and BSD kernels is a bit tricky. There must be
|
||||
* exactly one socket on which setsockopt MRT_INIT is called, then multicast
|
||||
* forwarding is enabled and kernel multicast routing table is maintained until
|
||||
* the socket is closed. This MRT control socket is stored in &mrt_sock field.
|
||||
*
|
||||
* Multicast forwarding works only on interfaces registered as MIFs, with
|
||||
* assigned MIF index. While MIFs and MIF indexes are handled by OS-independent
|
||||
* code in iface.c, actual MIF registration by OS kernel is handled here. The
|
||||
* MKernel protocol is associated with a MIF group by mkrt_register_mif_group(),
|
||||
* after that it receive mkrt_register_mif() / mkrt_unregister_mif() calls for
|
||||
* changes in given MIF group.
|
||||
*
|
||||
* Unlike kernel unicast routing API, which is proactive, kernel multicast
|
||||
* routing API is designed as reactive. Kernel keeps MFC entries for encountered
|
||||
* (S, G) flows and when a new flow is noticed, BIRD receives cache miss message
|
||||
* (%IGMPMSG_NOCACHE) from kernel and responds with adding appropriate (S, G)
|
||||
* MFC entry to the kernel, see mkrt_resolve_mfc(). Therefore, regular route
|
||||
* notifications handled by mkrt_rt_notify() are not directly translated to
|
||||
* kernel route updates.
|
||||
*
|
||||
* Although there is also support for (*, G) MFC entries in Linux (using
|
||||
* %MRT_ADD_MFC_PROXY), their behavior is strange and not matching our needs,
|
||||
* and there is no equivalent in BSD, we do not use them and we manage with
|
||||
* traditional (S, G) MFC entries.
|
||||
*
|
||||
* Finally, the MRT control socket is the only one that receives all IGMP
|
||||
* packets, even those from non-joined groups. IGMP protocol needs to receive
|
||||
* these packets, so we forward them internally. To simulate the sane behavior,
|
||||
* a protocol can open an IGMP socket and use sk_setup_igmp() to register it to
|
||||
* reception of all IGMP packets. The socket is relinked to internal MIF socket
|
||||
* list. MKernel protocol then use mif_forward_igmp() to forward packets
|
||||
* received on the MRT control socket to all sockets on these lists.
|
||||
*/
|
||||
|
||||
#include "nest/bird.h"
|
||||
#include "nest/iface.h"
|
||||
#include "lib/socket.h"
|
||||
|
||||
#include "unix.h"
|
||||
#include "mkrt.h"
|
||||
|
||||
#include <linux/mroute.h>
|
||||
|
||||
|
||||
/*
|
||||
* MRT socket options
|
||||
*/
|
||||
|
||||
static inline int
|
||||
sk_mrt_init4(sock *s)
|
||||
{
|
||||
int y = 1;
|
||||
return setsockopt(s->fd, IPPROTO_IP, MRT_INIT, &y, sizeof(y));
|
||||
}
|
||||
|
||||
static inline int
|
||||
sk_mrt_done4(sock *s)
|
||||
{
|
||||
return setsockopt(s->fd, IPPROTO_IP, MRT_DONE, NULL, 0);
|
||||
}
|
||||
|
||||
static inline int
|
||||
sk_mrt_add_mif4(sock *s, struct mif *mif)
|
||||
{
|
||||
struct vifctl vc = {
|
||||
.vifc_vifi = mif->index,
|
||||
.vifc_flags = VIFF_USE_IFINDEX,
|
||||
.vifc_lcl_ifindex = mif->iface->index,
|
||||
};
|
||||
|
||||
return setsockopt(s->fd, IPPROTO_IP, MRT_ADD_VIF, &vc, sizeof(vc));
|
||||
}
|
||||
|
||||
static inline int
|
||||
sk_mrt_del_mif4(sock *s, struct mif *mif)
|
||||
{
|
||||
struct vifctl vc = {
|
||||
.vifc_vifi = mif->index,
|
||||
};
|
||||
|
||||
return setsockopt(s->fd, IPPROTO_IP, MRT_DEL_VIF, &vc, sizeof(vc));
|
||||
}
|
||||
|
||||
static inline int
|
||||
sk_mrt_add_mfc4(sock *s, ip4_addr src, ip4_addr grp, u32 iifs, u32 oifs, int mif_index)
|
||||
{
|
||||
struct mfcctl mc = {
|
||||
.mfcc_origin = ip4_to_in4(src),
|
||||
.mfcc_mcastgrp = ip4_to_in4(grp),
|
||||
.mfcc_parent = mif_index,
|
||||
};
|
||||
|
||||
if (BIT32_TEST(&iifs, mif_index) && oifs)
|
||||
for (int i = 0; i < MIFS_MAX; i++)
|
||||
if (BIT32_TEST(&oifs, i) && (i != mif_index))
|
||||
mc.mfcc_ttls[i] = 1;
|
||||
|
||||
return setsockopt(s->fd, IPPROTO_IP, MRT_ADD_MFC, &mc, sizeof(mc));
|
||||
}
|
||||
|
||||
static inline int
|
||||
sk_mrt_del_mfc4(sock *s, ip4_addr src, ip4_addr grp)
|
||||
{
|
||||
struct mfcctl mc = {
|
||||
.mfcc_origin = ip4_to_in4(src),
|
||||
.mfcc_mcastgrp = ip4_to_in4(grp),
|
||||
};
|
||||
|
||||
return setsockopt(s->fd, IPPROTO_IP, MRT_DEL_MFC, &mc, sizeof(mc));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* MIF handling
|
||||
*/
|
||||
|
||||
void
|
||||
mkrt_register_mif(struct mkrt_proto *p, struct mif *mif)
|
||||
{
|
||||
TRACE(D_EVENTS, "Registering interface %s MIF %i", mif->iface->name, mif->index);
|
||||
|
||||
if (sk_mrt_add_mif4(p->mrt_sock, mif) < 0)
|
||||
log(L_ERR "%s: Cannot register interface %s MIF %i: %m",
|
||||
p->p.name, mif->iface->name, mif->index);
|
||||
}
|
||||
|
||||
void
|
||||
mkrt_unregister_mif(struct mkrt_proto *p, struct mif *mif)
|
||||
{
|
||||
TRACE(D_EVENTS, "Unregistering interface %s MIF %i", mif->iface->name, mif->index);
|
||||
|
||||
if (sk_mrt_del_mif4(p->mrt_sock, mif) < 0)
|
||||
log(L_ERR "%s: Cannot unregister interface %s MIF %i: %m",
|
||||
p->p.name, mif->iface->name, mif->index);
|
||||
}
|
||||
|
||||
void
|
||||
mkrt_register_mif_group(struct mkrt_proto *p, struct mif_group *grp)
|
||||
{
|
||||
ASSERT(!grp->owner);
|
||||
grp->owner = &p->p;
|
||||
|
||||
WALK_ARRAY(grp->mifs, MIFS_MAX, mif)
|
||||
if (mif)
|
||||
mkrt_register_mif(p, mif);
|
||||
}
|
||||
|
||||
void
|
||||
mkrt_unregister_mif_group(struct mkrt_proto *p, struct mif_group *grp)
|
||||
{
|
||||
grp->owner = NULL;
|
||||
|
||||
WALK_ARRAY(grp->mifs, MIFS_MAX, mif)
|
||||
if (mif)
|
||||
mkrt_unregister_mif(p, mif);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* MFC handling
|
||||
*/
|
||||
|
||||
static void
|
||||
mkrt_init_mfc(void *G)
|
||||
{
|
||||
struct mkrt_mfc_group *grp = G;
|
||||
|
||||
init_list(&grp->sources);
|
||||
}
|
||||
|
||||
static struct mkrt_mfc_source *
|
||||
mkrt_get_mfc(struct mkrt_proto *p, ip4_addr source, ip4_addr group)
|
||||
{
|
||||
net_addr_mgrp4 n = NET_ADDR_MGRP4(group);
|
||||
struct mkrt_mfc_group *grp = fib_get(&p->mfc_groups, (net_addr *) &n);
|
||||
|
||||
struct mkrt_mfc_source *src;
|
||||
WALK_LIST(src, grp->sources)
|
||||
if (ip4_equal(src->addr, source))
|
||||
return src;
|
||||
|
||||
src = mb_allocz(p->p.pool, sizeof(struct mkrt_mfc_source));
|
||||
src->addr = source;
|
||||
src->parent = -1;
|
||||
add_tail(&grp->sources, NODE src);
|
||||
|
||||
return src;
|
||||
}
|
||||
|
||||
struct mfc_result {
|
||||
u32 iifs, oifs;
|
||||
};
|
||||
|
||||
static void
|
||||
mkrt_resolve_mfc_hook(struct proto *p UNUSED, void *data, rte *rte)
|
||||
{
|
||||
struct mfc_result *res = data;
|
||||
res->iifs = rta_iifs(rte->attrs);
|
||||
res->oifs = rta_oifs(rte->attrs);
|
||||
}
|
||||
|
||||
/*
|
||||
* Resolve the MFC miss by adding a MFC entry. If no matching entry in the
|
||||
* routing table exists, add an empty one to satisfy the kernel.
|
||||
*/
|
||||
static void
|
||||
mkrt_resolve_mfc(struct mkrt_proto *p, ip4_addr src, ip4_addr grp, int mif_index)
|
||||
{
|
||||
struct mif *mif = (mif_index < MIFS_MAX) ? p->mif_group->mifs[mif_index] : NULL;
|
||||
|
||||
TRACE(D_EVENTS, "MFC miss for (%I4, %I4, %s)", src, grp, mif ? mif->iface->name : "?");
|
||||
|
||||
net_addr_mgrp4 n0 = NET_ADDR_MGRP4(grp);
|
||||
struct mfc_result res = {};
|
||||
|
||||
rt_examine(p->p.main_channel, (net_addr *) &n0, mkrt_resolve_mfc_hook, &res);
|
||||
|
||||
struct mkrt_mfc_source *mfc = mkrt_get_mfc(p, src, grp);
|
||||
mfc->iifs = res.iifs;
|
||||
mfc->oifs = res.oifs;
|
||||
mfc->parent = mif_index;
|
||||
|
||||
TRACE(D_EVENTS, "Adding MFC entry for (%I4, %I4)", src, grp);
|
||||
|
||||
if (sk_mrt_add_mfc4(p->mrt_sock, src, grp, mfc->iifs, mfc->oifs, mfc->parent) < 0)
|
||||
log(L_ERR "%s: Failed to add MFC entry: %m", p->p.name);
|
||||
}
|
||||
|
||||
static void
|
||||
mkrt_remove_mfc(struct mkrt_proto *p, struct mkrt_mfc_source *src, ip4_addr grp)
|
||||
{
|
||||
TRACE(D_EVENTS, "Removing MFC entry for (%I4, %I4)", src->addr, grp);
|
||||
|
||||
if (sk_mrt_del_mfc4(p->mrt_sock, src->addr, grp) < 0)
|
||||
log(L_ERR "%s: Failed to remove MFC entry: %m", p->p.name);
|
||||
|
||||
rem_node(NODE src);
|
||||
mb_free(src);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Because a route in the internal table has changed, all the corresponding MFC
|
||||
* entries are now wrong. Instead of correcting them, just flush the cache.
|
||||
*/
|
||||
static void
|
||||
mkrt_reset_mfc_group(struct mkrt_proto *p, struct mkrt_mfc_group *grp)
|
||||
{
|
||||
ip4_addr group = net4_prefix(grp->n.addr);
|
||||
|
||||
struct mkrt_mfc_source *src;
|
||||
WALK_LIST_FIRST(src, grp->sources)
|
||||
mkrt_remove_mfc(p, src, group);
|
||||
}
|
||||
|
||||
static void
|
||||
mkrt_free_mfc_group(struct mkrt_proto *p, struct mkrt_mfc_group *grp)
|
||||
{
|
||||
mkrt_reset_mfc_group(p, grp);
|
||||
fib_delete(&p->mfc_groups, grp);
|
||||
}
|
||||
|
||||
static void
|
||||
mkrt_rt_notify(struct proto *P, struct channel *c UNUSED, net *net, rte *new, rte *old UNUSED, ea_list *attrs UNUSED)
|
||||
{
|
||||
struct mkrt_proto *p = (void *) P;
|
||||
struct mkrt_mfc_group *grp = fib_find(&p->mfc_groups, net->n.addr);
|
||||
|
||||
if (!grp)
|
||||
return;
|
||||
|
||||
/* Drop all MFC entries (possibly along with the state information) for a group */
|
||||
if (new)
|
||||
mkrt_reset_mfc_group(p, grp);
|
||||
else
|
||||
mkrt_free_mfc_group(p, grp);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* On MRT control socket, we receive not only regular IGMP messages but also
|
||||
* so-called upcalls from the kernel. We must process them here.
|
||||
*/
|
||||
void mif_forward_igmp(struct mif_group *grp, struct mif *mif, sock *src, int len);
|
||||
|
||||
static int
|
||||
mkrt_rx_hook(sock *sk, uint len)
|
||||
{
|
||||
struct mkrt_proto *p = sk->data;
|
||||
struct igmpmsg *msg = (void *) sk->rbuf;
|
||||
u8 igmp_type = * (u8 *) sk_rx_buffer(sk, &len);
|
||||
|
||||
switch (igmp_type)
|
||||
{
|
||||
case IGMPMSG_NOCACHE:
|
||||
mkrt_resolve_mfc(p, ip4_from_in4(msg->im_src), ip4_from_in4(msg->im_dst), msg->im_vif);
|
||||
return 1;
|
||||
|
||||
case IGMPMSG_WRONGVIF:
|
||||
case IGMPMSG_WHOLEPKT:
|
||||
/* These should not happen unless some PIM-specific MRT options are enabled */
|
||||
return 1;
|
||||
|
||||
default:
|
||||
// FIXME: Use sk->lifindex or msg->im_vif ?
|
||||
mif_forward_igmp(p->mif_group, NULL, sk, len);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
mkrt_err_hook(sock *sk, int err)
|
||||
{
|
||||
struct mkrt_proto *p = sk->data;
|
||||
|
||||
log(L_ERR "%s: Socket error: %M", p->p.name, err);
|
||||
}
|
||||
|
||||
static int
|
||||
mkrt_open_socket(struct mkrt_proto *p)
|
||||
{
|
||||
sock *sk = sk_new(p->p.pool);
|
||||
sk->type = SK_IP;
|
||||
sk->subtype = SK_IPV4;
|
||||
sk->dport = IPPROTO_IGMP;
|
||||
sk->flags = SKF_LADDR_RX;
|
||||
|
||||
sk->data = p;
|
||||
sk->ttl = 1;
|
||||
sk->rx_hook = mkrt_rx_hook;
|
||||
sk->err_hook = mkrt_err_hook;
|
||||
|
||||
sk->rbsize = 4096;
|
||||
sk->tbsize = 0;
|
||||
|
||||
if (sk_open(sk) < 0)
|
||||
{
|
||||
sk_log_error(sk, p->p.name);
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (sk_mrt_init4(sk) < 0)
|
||||
{
|
||||
if (errno == EADDRINUSE)
|
||||
log(L_ERR "%s: Another multicast daemon is running", p->p.name);
|
||||
else
|
||||
log(L_ERR "%s: Cannot enable multicast in kernel: %m", p->p.name);
|
||||
|
||||
goto err;
|
||||
}
|
||||
|
||||
p->mrt_sock = sk;
|
||||
return 1;
|
||||
|
||||
err:
|
||||
rfree(sk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
mkrt_close_socket(struct mkrt_proto *p)
|
||||
{
|
||||
sk_mrt_done4(p->mrt_sock);
|
||||
rfree(p->mrt_sock);
|
||||
p->mrt_sock = NULL;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Protocol glue
|
||||
*/
|
||||
|
||||
static struct mkrt_config *mkrt_cf;
|
||||
|
||||
static void
|
||||
mkrt_preconfig(struct protocol *P UNUSED, struct config *c UNUSED)
|
||||
{
|
||||
mkrt_cf = NULL;
|
||||
}
|
||||
|
||||
struct proto_config *
|
||||
mkrt_init_config(int class)
|
||||
{
|
||||
if (mkrt_cf)
|
||||
cf_error("Multicast kernel protocol already defined");
|
||||
|
||||
mkrt_cf = (struct mkrt_config *) proto_config_new(&proto_unix_mkrt, class);
|
||||
return (struct proto_config *) mkrt_cf;
|
||||
}
|
||||
|
||||
void
|
||||
mkrt_postconfig(struct proto_config *CF)
|
||||
{
|
||||
// struct mkrt_config *cf = (void *) CF;
|
||||
|
||||
if (EMPTY_LIST(CF->channels))
|
||||
cf_error("Channel not specified");
|
||||
}
|
||||
|
||||
static struct proto *
|
||||
mkrt_init(struct proto_config *CF)
|
||||
{
|
||||
struct mkrt_proto *p = proto_new(CF);
|
||||
|
||||
p->p.main_channel = proto_add_channel(&p->p, proto_cf_main_channel(CF));
|
||||
|
||||
p->p.rt_notify = mkrt_rt_notify;
|
||||
|
||||
p->mif_group = global_mif_group;
|
||||
|
||||
return &p->p;
|
||||
}
|
||||
|
||||
static int
|
||||
mkrt_start(struct proto *P)
|
||||
{
|
||||
struct mkrt_proto *p = (void *) P;
|
||||
|
||||
fib_init(&p->mfc_groups, p->p.pool, NET_MGRP4, sizeof(struct mkrt_mfc_group),
|
||||
OFFSETOF(struct mkrt_mfc_group, n), 6, mkrt_init_mfc);
|
||||
|
||||
if (!mkrt_open_socket(p))
|
||||
return PS_START;
|
||||
|
||||
mkrt_register_mif_group(p, p->mif_group);
|
||||
|
||||
return PS_UP;
|
||||
}
|
||||
|
||||
static int
|
||||
mkrt_shutdown(struct proto *P)
|
||||
{
|
||||
struct mkrt_proto *p = (void *) P;
|
||||
|
||||
if (p->p.proto_state == PS_START)
|
||||
return PS_DOWN;
|
||||
|
||||
mkrt_unregister_mif_group(p, p->mif_group);
|
||||
mkrt_close_socket(p);
|
||||
|
||||
return PS_DOWN;
|
||||
}
|
||||
|
||||
static int
|
||||
mkrt_reconfigure(struct proto *p, struct proto_config *CF)
|
||||
{
|
||||
// struct mkrt_config *o = (void *) p->cf;
|
||||
// struct mkrt_config *n = (void *) CF;
|
||||
|
||||
if (!proto_configure_channel(p, &p->main_channel, proto_cf_main_channel(CF)))
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void
|
||||
mkrt_dump(struct proto *P)
|
||||
{
|
||||
struct mkrt_proto *p = (void *) P;
|
||||
|
||||
debug("\t(S,G) entries in MFC in kernel:\n");
|
||||
FIB_WALK(&p->mfc_groups, struct mkrt_mfc_group, grp)
|
||||
{
|
||||
struct mkrt_mfc_source *src;
|
||||
WALK_LIST(src, grp->sources)
|
||||
debug("\t\t(%I4, %I4, %d) -> %b %b\n",
|
||||
src->addr, net4_prefix(grp->n.addr), src->parent, src->iifs, src->oifs);
|
||||
}
|
||||
FIB_WALK_END;
|
||||
}
|
||||
|
||||
|
||||
struct protocol proto_unix_mkrt = {
|
||||
.name = "MKernel",
|
||||
.template = "mkernel%d",
|
||||
.channel_mask = NB_MGRP4,
|
||||
.proto_size = sizeof(struct mkrt_proto),
|
||||
.config_size = sizeof(struct mkrt_config),
|
||||
.preconfig = mkrt_preconfig,
|
||||
.postconfig = mkrt_postconfig,
|
||||
.init = mkrt_init,
|
||||
.start = mkrt_start,
|
||||
.shutdown = mkrt_shutdown,
|
||||
.reconfigure = mkrt_reconfigure,
|
||||
.dump = mkrt_dump,
|
||||
};
|
51
sysdep/unix/mkrt.h
Normal file
51
sysdep/unix/mkrt.h
Normal file
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* BIRD -- UNIX Kernel Multicast Routing
|
||||
*
|
||||
* (c) 2016 Ondrej Hlavaty <aearsis@eideo.cz>
|
||||
* (c) 2018 Ondrej Zajicek <santiago@crfreenet.org>
|
||||
* (c) 2018 CZ.NIC z.s.p.o.
|
||||
*
|
||||
* Can be freely distributed and used under the terms of the GNU GPL.
|
||||
*/
|
||||
|
||||
#ifndef _BIRD_MKRT_H_
|
||||
#define _BIRD_MKRT_H_
|
||||
|
||||
#include "nest/bird.h"
|
||||
#include "nest/iface.h"
|
||||
#include "nest/protocol.h"
|
||||
#include "nest/route.h"
|
||||
#include "lib/socket.h"
|
||||
|
||||
|
||||
extern struct protocol proto_unix_mkrt;
|
||||
|
||||
struct mkrt_config {
|
||||
struct proto_config cf;
|
||||
};
|
||||
|
||||
struct mkrt_mfc_group {
|
||||
list sources; /* List of MFC entries (struct mkrt_mfc_source) */
|
||||
struct fib_node n;
|
||||
};
|
||||
|
||||
struct mkrt_mfc_source {
|
||||
node n;
|
||||
ip4_addr addr;
|
||||
|
||||
int parent; /* MIF index of valid incoming iface */
|
||||
u32 iifs, oifs; /* Values from the multicast route */
|
||||
};
|
||||
|
||||
struct mkrt_proto {
|
||||
struct proto p;
|
||||
|
||||
struct mif_group *mif_group; /* Associated MIF group for multicast routes */
|
||||
sock *mrt_sock; /* MRT control socket */
|
||||
|
||||
struct fib mfc_groups; /* MFC entries/groups managed by protocol */
|
||||
};
|
||||
|
||||
struct proto_config *mkrt_init_config(int class);
|
||||
|
||||
#endif
|
@ -58,6 +58,9 @@ typedef struct sockaddr_bird {
|
||||
static inline ip_addr ipa_from_in4(struct in_addr a)
|
||||
{ return ipa_from_u32(ntohl(a.s_addr)); }
|
||||
|
||||
static inline ip4_addr ip4_from_in4(struct in_addr a)
|
||||
{ return ip4_from_u32(ntohl(a.s_addr)); }
|
||||
|
||||
static inline ip_addr ipa_from_in6(struct in6_addr a)
|
||||
{ return ipa_build6(ntohl(a.s6_addr32[0]), ntohl(a.s6_addr32[1]), ntohl(a.s6_addr32[2]), ntohl(a.s6_addr32[3])); }
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user