From: Ondrej Zajicek (work) Date: Sat, 3 Mar 2018 12:08:38 +0000 (+0100) Subject: Merge commit '76dd4730eac6a632ab28ef85d266cbfc0abc89ca' into pim X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=c461ab7e9095b665846df90bfe36cd9675838e4e;p=thirdparty%2Fbird.git Merge commit '76dd4730eac6a632ab28ef85d266cbfc0abc89ca' into pim --- c461ab7e9095b665846df90bfe36cd9675838e4e diff --cc lib/socket.h index 11bcff71c,7fed4ca33..f7d1b3aa8 --- a/lib/socket.h +++ b/lib/socket.h @@@ -12,29 -12,6 +12,32 @@@ #include #include "lib/resource.h" +#ifdef HAVE_LIBSSH +#define LIBSSH_LEGACY_0_4 +#include +#endif + ++struct mif_group; ++struct mif; ++ +#ifdef HAVE_LIBSSH +struct ssh_sock { + const char *username; /* (Required) SSH user name */ + const char *server_hostkey_path; /* (Optional) Filepath to the SSH public key of remote side, can be knownhost file */ + const char *client_privkey_path; /* (Optional) Filepath to the SSH private key of BIRD */ + const char *subsystem; /* (Optional) Name of SSH subsytem */ + ssh_session session; /* Internal */ + ssh_channel channel; /* Internal */ + int state; /* Internal */ +#define SK_SSH_CONNECT 0 /* Start state */ +#define SK_SSH_SERVER_KNOWN 1 /* Internal */ +#define SK_SSH_USERAUTH 2 /* Internal */ +#define SK_SSH_CHANNEL 3 /* Internal */ +#define SK_SSH_SESSION 4 /* Internal */ +#define SK_SSH_SUBSYSTEM 5 /* Internal */ +#define SK_SSH_ESTABLISHED 6 /* Final state */ +}; +#endif typedef struct birdsock { resource r; @@@ -105,6 -79,6 +108,7 @@@ int sk_join_group(sock *s, ip_addr madd 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); diff --cc nest/iface.c index b165e6e74,4c3e07744..16b06c7c2 --- a/nest/iface.c +++ b/nest/iface.c @@@ -30,9 -30,8 +30,10 @@@ #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" static pool *if_pool; @@@ -716,63 -648,8 +717,130 @@@ if_init(void if_pool = rp_new(&root_pool, "Interfaces"); init_list(&iface_list); neigh_init(if_pool); + global_mif_group = mif_get_group(); +} + +/* + * 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) +{ + struct mif_group *grp = mb_allocz(if_pool, sizeof(struct mif_group)); + init_list(&grp->sockets); + + return grp; +} + +static inline int u32_cto(uint x) { return ffs(~x) - 1; } + +struct mif * +mif_get(struct mif_group *grp, struct iface *iface) +{ + WALK_ARRAY(grp->mifs, MIFS_MAX, mif) + if (mif && (mif->iface == iface)) + return mif->uc++, mif; + + int i = u32_cto(grp->indexes); + if (i < 0) + return NULL; + + struct mif *mif = mb_allocz(if_pool, sizeof(struct mif)); + mif->iface = iface; + mif->index = i; + mif->uc = 1; + init_list(&mif->sockets); + + grp->mifs[mif->index] = mif; + MIFS_SET(mif, grp->indexes); + ++ if (grp->owner) ++ mkrt_register_mif((struct mkrt_proto *) grp->owner, mif); ++ + return mif; +} + +void +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); + + grp->mifs[mif->index] = NULL; + MIFS_CLR(mif, grp->indexes); + + 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 */ diff --cc nest/iface.h index dc44dfadb,042543ed0..3bd8d0318 --- a/nest/iface.h +++ b/nest/iface.h @@@ -45,21 -39,7 +45,22 @@@ struct iface list neighbors; /* All neighbors on this interface */ }; -#define IF_UP 1 /* IF_ADMIN_UP and IP address known */ +struct mif { + struct iface *iface; + uint index; /* MIF (VIF) index for multicast routes */ + uint uc; /* Use count */ + list sockets; /* Listening per-iface IGMP sockets */ +}; + +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]; +}; + +#define IF_UP 1 /* Currently just IF_ADMIN_UP */ #define IF_MULTIACCESS 2 #define IF_BROADCAST 4 #define IF_MULTICAST 8 diff --cc nest/route.h index b2ae4686d,0a3e5cdae..27f119280 --- a/nest/route.h +++ b/nest/route.h @@@ -16,6 -17,6 +16,7 @@@ struct ea_list; struct protocol; struct proto; ++struct channel; struct rte_src; struct symbol; struct filter; @@@ -294,13 -295,17 +295,13 @@@ void *net_route(rtable *tab, const net_ int net_roa_check(rtable *tab, const net_addr *n, u32 asn); rte *rte_find(net *net, struct rte_src *src); rte *rte_get_temp(struct rta *); -void rte_update2(struct channel *c, net_addr *n, rte *new, struct rte_src *src); +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 */ -void rte_discard(rtable *tab, rte *old); --int rt_examine(rtable *t, net_addr *a, struct proto *p, struct filter *filter); -int rt_examine2(net *n, struct proto *p, struct filter *filter, void (*callback)(struct proto *, void *, rte *), void *data); -rte *rt_export_merged(struct channel *c, net *net, rte **rt_free, struct ea_list **tmpa, int silent); ++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); -int rt_route(struct channel *c, net_addr *net, void (*callback)(struct proto *, void *, rte *), void *data); void rt_schedule_prune(rtable *t); -u32 rt_get_igp_metric(rte *rt); void rte_dump(rte *); void rte_free(rte *); rte *rte_do_cow(rte *); diff --cc nest/rt-table.c index 3e15a17d9,464107c3f..dc6a224a8 --- a/nest/rt-table.c +++ b/nest/rt-table.c @@@ -1406,22 -1405,32 +1406,27 @@@ rte_discard(rte *old) /* Non-filtered r /* 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) -{ - net *n = net_find(t, a); - return rt_examine2(n, p, filter, NULL, NULL); -} - -/* If rte would be exported to p, call the callback */ -int -rt_examine2(net *n, struct proto *p, struct filter *filter, void (*callback)(struct proto *, void *, rte *), void *data) ++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)) return 0; rte_update_lock(); /* Rest is stripped down export_filter() */ - ea_list *tmpa = make_tmp_attrs(rt, rte_update_pool); + 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); - if (callback && v > 0) - callback(p, data, rt); - /* Discard temporary rte */ if (rt != n->routes) rte_free(rt); diff --cc proto/radv/radv.c index 8a79dfaf2,4c845f7ab..3c3e650a4 --- a/proto/radv/radv.c +++ b/proto/radv/radv.c @@@ -558,8 -311,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); - struct channel *c =ra->p.main_channel; - return rt_examine(c->table, &cf->trigger, &ra->p, c->out_filter); ++ return rt_examine(p->p.main_channel, &cf->trigger, NULL, NULL); } static void diff --cc sysdep/unix/Doc index a17f425b9,a17f425b9..6471e457e --- a/sysdep/unix/Doc +++ b/sysdep/unix/Doc @@@ -1,3 -1,3 +1,4 @@@ S log.c S krt.c ++S mkrt.c # io.c is documented under Resources diff --cc sysdep/unix/Makefile index f592399c1,88b7a95e4..3cb851147 --- a/sysdep/unix/Makefile +++ b/sysdep/unix/Makefile @@@ -1,8 -1,5 +1,8 @@@ - src := io.c krt.c log.c main.c random.c -src := io.c krt.c log.c main.c random.c mkrt.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) diff --cc sysdep/unix/io.c index 13b5e6398,d6e51dcc8..e4b8cc373 --- a/sysdep/unix/io.c +++ b/sysdep/unix/io.c @@@ -588,6 -920,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 diff --cc sysdep/unix/main.c index 2251d3fb7,1f47680e5..cee8114c8 --- a/sysdep/unix/main.c +++ b/sysdep/unix/main.c @@@ -39,6 -36,6 +39,7 @@@ #include "unix.h" #include "krt.h" ++#include "mkrt.h" /* * Debugging @@@ -851,6 -808,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(); diff --cc sysdep/unix/mkrt.Y index 000000000,8be6facac..e86399471 mode 000000,100644..100644 --- a/sysdep/unix/mkrt.Y +++ b/sysdep/unix/mkrt.Y @@@ -1,0 -1,39 +1,41 @@@ + /* - * BIRD -- UNIX Multicast route syncer configuration ++ * BIRD -- UNIX Kernel Multicast Routing Configuration + * + * (c) 2016 Ondrej Hlavaty + * + * 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_config_finish(this_proto); }) ++CF_ADDTO(proto, mkrt_proto '}') + -mkrt_proto_start: proto_start MKERNEL { this_proto = mkrt_config_init($1); } ++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 ++ | proto_channel { this_proto->net_type = $1->net_type; } + ; + + CF_CODE + + CF_END diff --cc sysdep/unix/mkrt.c index 000000000,77a81355e..d8566254e mode 000000,100644..100644 --- a/sysdep/unix/mkrt.c +++ b/sysdep/unix/mkrt.c @@@ -1,0 -1,585 +1,503 @@@ + /* - * BIRD -- Multicast routing kernel ++ * BIRD -- UNIX Kernel Multicast Routing + * - * (c) 2016 Ondrej Hlavaty ++ * (c) 2016 Ondrej Hlavaty ++ * (c) 2018 Ondrej Zajicek ++ * (c) 2018 CZ.NIC z.s.p.o. + * - * Can be freely distributed and used under the terms of the GNU GPL. ++ * Can be freely distributed and used under the terms of the GNU GPL. + */ + -/* - * DOC: Multicast route kernel synchronization ++/** ++ * 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. + * - * This protocol is the BIRD's interface to the kernel part of multicast - * routing. It assignes the VIF indices to interfaces, forwards the IGMP - * packets to SK_IGMP sockets and, of course, adds MFC entries to the kernel. ++ * 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 in current kernel is a bit tricky. There must be exactly one - * socket on which setsockopt MRT_INIT is called, then multicast forwarding is - * enabled. Every multicast routing table update must be done through this - * protocol. ++ * 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. + * - * Also, that socket is the only one that receives IGMP packets on non-joined - * groups. These packets IGMP protocol needs to receive, so we forward them - * internally. To simulate sane behavior, protocol can open socket with type - * SK_IGMP, which is almost as a SK_IP, IPPROTO_IGMP socket but receives copy - * of all packets. ++ * 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. + * - * As always with system-dependent code, prepare for everything. Because the - * BSD kernel knows nothing apart from (S,G) routes, and Linux even blocked the - * (*,G) routes for something not being a regular (*,G) routes, we must add the - * routes in reaction to missed packets. This is very bad, but probably the - * only solution, until someone rewrites the kernel part. ++ * 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. + * - * Part of the protocol is global and "static", without the need to configure. - * Another part is a usual proto instance. ++ * 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 "sysdep/unix/unix.h" -#include "sysdep/unix/mkrt.h" - -#define HASH_I_KEY(n) n->iface->index -#define HASH_I_NEXT(n) n->next -#define HASH_I_EQ(a,b) (a == b) -#define HASH_I_FN(n) n + -#define HASH_MFC_KEY(n) n->ga -#define HASH_MFC_NEXT(n) n->next -#define HASH_MFC_EQ(a,b) ipa_equal(a, b) -#define HASH_MFC_FN(k) ipa_hash(k) ++#include "unix.h" ++#include "mkrt.h" + -static struct mkrt_config *mkrt_cf; - -/* Global code for SK_IGMP sockets */ ++#include + -struct mkrt_iface { - struct mkrt_iface *next; - struct iface *iface; - list sockets; -}; + -static struct mkrt_global { - pool *pool; - list sockets; - HASH(struct mkrt_iface) ifaces; -} mkrt_global; ++/* ++ * MRT socket options ++ */ + -void -mkrt_io_init(void) ++static inline int ++sk_mrt_init4(sock *s) + { - mkrt_global.pool = rp_new(&root_pool, "Multicast kernel Syncer"); - HASH_INIT(mkrt_global.ifaces, mkrt_global.pool, 6); - init_list(&mkrt_global.sockets); ++ int y = 1; ++ return setsockopt(s->fd, IPPROTO_IP, MRT_INIT, &y, sizeof(y)); + } + -static struct mkrt_iface * -mkrt_iface_find(struct mkrt_proto *p, unsigned ifindex) ++static inline int ++sk_mrt_done4(sock *s) + { - return HASH_FIND(mkrt_global.ifaces, HASH_I, ifindex); ++ return setsockopt(s->fd, IPPROTO_IP, MRT_DONE, NULL, 0); + } + -static struct mkrt_iface * -mkrt_iface_get(unsigned ifindex) ++static inline int ++sk_mrt_add_mif4(sock *s, struct mif *mif) + { - struct mkrt_iface *ifa = HASH_FIND(mkrt_global.ifaces, HASH_I, ifindex); - if (ifa) - return ifa; - - ifa = mb_allocz(mkrt_global.pool, sizeof(struct mkrt_iface)); - init_list(&ifa->sockets); - ifa->iface = if_find_by_index(ifindex); ++ struct vifctl vc = { ++ .vifc_vifi = mif->index, ++ .vifc_flags = VIFF_USE_IFINDEX, ++ .vifc_lcl_ifindex = mif->iface->index, ++ }; + - HASH_INSERT(mkrt_global.ifaces, HASH_I, ifa); - return ifa; ++ return setsockopt(s->fd, IPPROTO_IP, MRT_ADD_VIF, &vc, sizeof(vc)); + } + -/* - * Add the socket into the list of sockets that are passed a copy of every IGMP - * packet received on the control socket. - */ -void -mkrt_listen(sock *s) ++static inline int ++sk_mrt_del_mif4(sock *s, struct mif *mif) + { - ASSERT(s->type == SK_IGMP); ++ struct vifctl vc = { ++ .vifc_vifi = mif->index, ++ }; + - if (s->iface) - { - struct mkrt_iface *i = mkrt_iface_get(s->iface->index); - add_tail(&i->sockets, &s->n); - } - else - add_tail(&mkrt_global.sockets, &s->n); - - log(L_INFO "Socket fd %i getting IGMP", s->fd); ++ return setsockopt(s->fd, IPPROTO_IP, MRT_DEL_VIF, &vc, sizeof(vc)); + } + -/* - * Forward a packet from one socket to another. Emulates the receiving routine. - * Socket is in exactly the same state as if it received the packet itself, but - * must not modify it to preserve it for others. - */ -static inline void -mkrt_rx_forward(sock *from, sock *to, int len) ++static inline int ++sk_mrt_add_mfc4(sock *s, ip4_addr src, ip4_addr grp, u32 iifs, u32 oifs, int mif_index) + { - if (!to->rx_hook) - return; - - to->faddr = from->faddr; - if (to->flags & SKF_LADDR_RX) - { - to->laddr = from->laddr; - to->lifindex = from->lifindex; - } - - to->rbuf = from->rbuf; - to->rpos = from->rpos; - to->rbsize = from->rbsize; - - to->rx_hook(to, len); ++ 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; + - to->faddr = to->laddr = IPA_NONE; - to->lifindex = 0; - to->rbuf = to->rpos = NULL; - to->rbsize = 0; ++ return setsockopt(s->fd, IPPROTO_IP, MRT_ADD_MFC, &mc, sizeof(mc)); + } + -/* - * Forward a packet to all sockets on a list. - */ -static inline void -mkrt_rx_forward_all(list *sockets, sock *sk, int len) ++static inline int ++sk_mrt_del_mfc4(sock *s, ip4_addr src, ip4_addr grp) + { - node *n, *next; ++ struct mfcctl mc = { ++ .mfcc_origin = ip4_to_in4(src), ++ .mfcc_mcastgrp = ip4_to_in4(grp), ++ }; + - WALK_LIST_DELSAFE(n, next, *sockets) - mkrt_rx_forward(sk, SKIP_BACK(sock, n, n), len); ++ return setsockopt(s->fd, IPPROTO_IP, MRT_DEL_MFC, &mc, sizeof(mc)); + } + -/*************** - Mkernel proto - ***************/ + + /* - * Call a setsockopt with a MRT_ option. ++ * MIF handling + */ -static inline int -mkrt_call(struct mkrt_proto *mkrt, int option_name, const void *val, socklen_t len) -{ - return setsockopt(mkrt->igmp_sock->fd, IPPROTO_IP, option_name, val, len); -} + -static inline vifi_t -mkrt_alloc_vifi(struct mkrt_proto *p, struct iface *iface) ++void ++mkrt_register_mif(struct mkrt_proto *p, struct mif *mif) + { - if (p->vif_count >= MAXVIFS) - { - log(L_ERR "Maximum number of interfaces for multicast routing reached."); - return -1; - } - - for (vifi_t i = 0; ; i = (i + 1) % MAXVIFS) - if (p->vif_map[i] == NULL) - { - p->vif_map[i] = iface; - iface->vifi = i; - p->vif_count++; - return i; - } -} ++ TRACE(D_EVENTS, "Registering interface %s MIF %i", mif->iface->name, mif->index); + -static inline void -mkrt_free_vifi(struct mkrt_proto *p, vifi_t vifi) -{ - p->vif_count -= p->vif_map[vifi] != NULL; - p->vif_map[vifi] = NULL; ++ 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); + } + -static void -mkrt_add_vif(struct mkrt_proto *p, struct iface *i) ++void ++mkrt_unregister_mif(struct mkrt_proto *p, struct mif *mif) + { - int err; - - if (i->flags & IF_VIFI_ASSIGNED) - return; - - mkrt_alloc_vifi(p, i); - - struct vifctl vc = {0}; - vc.vifc_vifi = i->vifi; - vc.vifc_flags = VIFF_USE_IFINDEX; - vc.vifc_lcl_ifindex = i->index; - - if ((err = mkrt_call(p, MRT_ADD_VIF, &vc, sizeof(vc))) < 0) - goto err; - - TRACE(D_EVENTS, "Iface %s (%i) assigned VIF %i", i->name, i->index, i->vifi); - - i->flags |= IF_VIFI_ASSIGNED; - return; ++ TRACE(D_EVENTS, "Unregistering interface %s MIF %i", mif->iface->name, mif->index); + -err: - log(L_ERR "Error while assigning %s VIF %i: %m", i->name, i->vifi, err); - mkrt_free_vifi(p, i->vifi); ++ 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); + } + -static int -mkrt_del_vif(struct mkrt_proto *p, struct iface *i) ++void ++mkrt_register_mif_group(struct mkrt_proto *p, struct mif_group *grp) + { - int err; - - if (!i->flags & IF_VIFI_ASSIGNED) - return 0; - - struct vifctl vc = {0}; - vc.vifc_vifi = i->vifi; - - if ((err = mkrt_call(p, MRT_DEL_VIF, &vc, sizeof(vc))) < 0) - goto err; ++ ASSERT(!grp->owner); ++ grp->owner = &p->p; + - mkrt_free_vifi(p, i->vifi); - i->flags &= ~IF_VIFI_ASSIGNED; - return 0; -err: - log(L_ERR "Error while unassigning %s VIF %i: %m", i->name, i->vifi, err); - return err; ++ WALK_ARRAY(grp->mifs, MIFS_MAX, mif) ++ if (mif) ++ mkrt_register_mif(p, mif); + } + -static struct mkrt_mfc_group * -mkrt_mfc_get(struct mkrt_proto *p, ip_addr ga) ++void ++mkrt_unregister_mif_group(struct mkrt_proto *p, struct mif_group *grp) + { - struct mkrt_mfc_group *mg = HASH_FIND(p->mfc_groups, HASH_MFC, ga); - if (mg) - return mg; - - mg = mb_allocz(p->p.pool, sizeof(struct mkrt_mfc_group)); - mg->ga = ga; - init_list(&mg->sources); - HASH_INSERT(p->mfc_groups, HASH_MFC, mg); - return mg; ++ grp->owner = NULL; ++ ++ WALK_ARRAY(grp->mifs, MIFS_MAX, mif) ++ if (mif) ++ mkrt_unregister_mif(p, mif); + } + ++ + /* - * Add a MFC entry for (S, G) with parent vifi, according to the route. ++ * MFC handling + */ -static int -mkrt_mfc_update(struct mkrt_proto *p, ip_addr group, ip_addr source, int vifi, struct rte *rte) ++ ++static void ++mkrt_init_mfc(void *G) + { - struct mfcctl mc = {0}; - int err; ++ struct mkrt_mfc_group *grp = G; + - mc.mfcc_origin = ipa_to_in4(source); - mc.mfcc_mcastgrp = ipa_to_in4(group); - mc.mfcc_parent = vifi; ++ init_list(&grp->sources); ++} + - if (rte && RTE_MGRP_ISSET(p->vif_map[vifi], rte->u.mkrt.iifs)) - for (int i = 0; i < MAXVIFS; i++) - if (RTE_MGRP_ISSET(p->vif_map[i], rte->u.mkrt.oifs)) - mc.mfcc_ttls[i] = 1; ++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); + - TRACE(D_EVENTS, "%s MFC entry for (%I, %I)", (vifi > 0) ? "Add" : "Delete", source, group); - if ((err = mkrt_call(p, (vifi > 0) ? MRT_ADD_MFC : MRT_DEL_MFC, &mc, sizeof(mc)) < 0)) - log(L_WARN "Mkernel: failed to %s MFC entry: %m", (vifi > 0) ? "add" : "delete", err); ++ struct mkrt_mfc_source *src; ++ WALK_LIST(src, grp->sources) ++ if (ip4_equal(src->addr, source)) ++ return src; + - return err; ++ 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_request { - ip_addr *group, *source; - vifi_t vifi; ++struct mfc_result { + u32 iifs, oifs; + }; + -/* - * Expand the attributes from the struct mfc_request and call mkrt_mfc_update, - * and pass back the result. - */ + static void -mkrt_mfc_call_update(struct proto *p, void *data, rte *rte) ++mkrt_resolve_mfc_hook(struct proto *p UNUSED, void *data, rte *rte) + { - struct mfc_request *req = data; - mkrt_mfc_update((struct mkrt_proto *) p, *req->group, *req->source, req->vifi, rte); - if (rte) - { - req->iifs = rte->u.mkrt.iifs; - req->oifs = rte->u.mkrt.oifs; - } ++ 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_mfc_resolve(struct mkrt_proto *p, ip_addr group, ip_addr source, vifi_t vifi) ++mkrt_resolve_mfc(struct mkrt_proto *p, ip4_addr src, ip4_addr grp, int mif_index) + { - struct iface *iface = p->vif_map[vifi]; ++ struct mif *mif = (mif_index < MIFS_MAX) ? p->mif_group->mifs[mif_index] : NULL; + - TRACE(D_EVENTS, "MFC miss for (%I, %I, %s)", source, group, iface ? iface->name : "??"); ++ TRACE(D_EVENTS, "MFC miss for (%I4, %I4, %s)", src, grp, mif ? mif->iface->name : "?"); + - net_addr_mgrp4 n0 = NET_ADDR_MGRP4(ipa_to_ip4(group)); ++ net_addr_mgrp4 n0 = NET_ADDR_MGRP4(grp); ++ struct mfc_result res = {}; + - struct mfc_request req = { &group, &source, vifi }; - if (!rt_route(p->p.main_channel, (net_addr *) &n0, mkrt_mfc_call_update, &req)) - mkrt_mfc_call_update((struct proto *) p, &req, NULL); ++ rt_examine(p->p.main_channel, (net_addr *) &n0, mkrt_resolve_mfc_hook, &res); + - struct mkrt_mfc_group *grp = mkrt_mfc_get(p, group); - struct mkrt_mfc_source *src = mb_alloc(p->p.pool, sizeof(struct mkrt_mfc_source)); - src->addr = source; - src->vifi = vifi; - src->iifs = req.iifs; - src->oifs = req.oifs; - add_tail(&grp->sources, NODE src); ++ 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, flush the cache. ++ * entries are now wrong. Instead of correcting them, just flush the cache. + */ + static void -mkrt_mfc_clean(struct mkrt_proto *p, struct mkrt_mfc_group *mg) ++mkrt_reset_mfc_group(struct mkrt_proto *p, struct mkrt_mfc_group *grp) + { - struct mkrt_mfc_source *n, *next; - WALK_LIST_DELSAFE(n, next, mg->sources) - { - mkrt_mfc_update(p, mg->ga, n->addr, -1, NULL); - rem_node(NODE n); - mb_free(n); - } ++ 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_mfc_free(struct mkrt_proto *p, struct mkrt_mfc_group *mg) ++mkrt_free_mfc_group(struct mkrt_proto *p, struct mkrt_mfc_group *grp) + { - mkrt_mfc_clean(p, mg); - HASH_REMOVE(p->mfc_groups, HASH_MFC, mg); - mb_free(mg); ++ mkrt_reset_mfc_group(p, grp); ++ fib_delete(&p->mfc_groups, grp); + } + -/* - * An IGMP message received on the socket can be not only a packet received - * from the network, but also a so-called upcall from the kernel. We must process them here. - */ -static int -mkrt_control_message(struct mkrt_proto *p, sock *sk, int len) ++static void ++mkrt_rt_notify(struct proto *P, struct channel *c UNUSED, net *net, rte *new, rte *old UNUSED, ea_list *attrs UNUSED) + { - struct igmpmsg *msg = (struct igmpmsg *) sk->rbuf; - u8 igmp_type = * (u8 *) sk_rx_buffer(sk, &len); ++ struct mkrt_proto *p = (void *) P; ++ struct mkrt_mfc_group *grp = fib_find(&p->mfc_groups, net->n.addr); + - switch (igmp_type) - { - case IGMPMSG_NOCACHE: - mkrt_mfc_resolve(p, ipa_from_in4(msg->im_dst), ipa_from_in4(msg->im_src), msg->im_vif); - return 1; - - case IGMPMSG_WRONGVIF: - case IGMPMSG_WHOLEPKT: - /* Neither should ever happen. IGMPMSG_WRONGVIF is a common situation, - * and this upcall is called only when switching to (S,G) tree in other - * PIM variants. - * - * Similarly, the WHOLEPKT should be called only when we add the register - * VIF and ask kernel for giving us whole packets - */ - return 1; - - default: - return 0; - } ++ 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, int len) ++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); + - /* Do not forward upcalls, IGMP cannot parse them */ - if (mkrt_control_message(p, 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; + - mkrt_rx_forward_all(&mkrt_global.sockets, sk, len); - - struct mkrt_iface *ifa = mkrt_iface_find(p, sk->lifindex); - if (ifa) - mkrt_rx_forward_all(&ifa->sockets, sk, len); ++ case IGMPMSG_WRONGVIF: ++ case IGMPMSG_WHOLEPKT: ++ /* These should not happen unless some PIM-specific MRT options are enabled */ ++ return 1; + - 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) + { - log(L_TRACE "IGMP error: %m", err); -} - -static void -mkrt_preconfig(struct protocol *P UNUSED, struct config *c UNUSED) -{ - mkrt_cf = NULL; -} - -struct proto_config * -mkrt_config_init(int class) -{ - if (mkrt_cf) - cf_error("Kernel multicast route syncer already defined"); - - mkrt_cf = (struct mkrt_config *) proto_config_new(&proto_mkrt, class); - return (struct proto_config *) mkrt_cf; -} - -void -mkrt_config_finish(struct proto_config *pc) -{ - struct channel_config *cc = proto_cf_main_channel(pc); - - if (!cc) - cc = channel_config_new(NULL, NET_MGRP4, pc); ++ struct mkrt_proto *p = sk->data; + - cc->ra_mode = RA_OPTIMAL; ++ log(L_ERR "%s: Socket error: %M", p->p.name, err); + } + + static int -mkrt_init_sock(struct mkrt_proto *p) ++mkrt_open_socket(struct mkrt_proto *p) + { - sock *sk; - - if (!(sk = sk_new(p->p.pool))) - goto err; - ++ 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) - goto err_sk; ++ { ++ sk_log_error(sk, p->p.name); ++ goto err; ++ } + - p->igmp_sock = sk; ++ 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); + - int v = 1; - if (mkrt_call(p, MRT_INIT, &v, sizeof(v)) < 0) - { - if (errno == EADDRINUSE) - log(L_ERR "Mkernel: Another multicast routing daemon is running"); - else - log(L_ERR "Mkernel: Cannot enable multicast features in kernel: %m", errno); - goto err_sk; - } ++ goto err; ++ } + - log(L_DEBUG "Multicast control socket open with fd %i", sk->fd); - return 0; ++ p->mrt_sock = sk; ++ return 1; + -err_sk: - rfree(sk); - p->igmp_sock = NULL; + err: - return -1; ++ rfree(sk); ++ return 0; + } + -void -mkrt_rt_notify(struct proto *P, struct channel *c, net *net, rte *new, rte *old, ea_list *attrs) ++static void ++mkrt_close_socket(struct mkrt_proto *p) + { - struct mkrt_proto *p = (struct mkrt_proto *) P; - net_addr *n = net->n.addr; - struct mkrt_mfc_group *mg = mkrt_mfc_get(p, net_prefix(n)); - - /* Drop all MFC entries (possibly along with the state information) for a group */ - if (new) - mkrt_mfc_clean(p, mg); - else - mkrt_mfc_free(p, mg); ++ 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_if_notify(struct proto *P, uint flags, struct iface *iface) ++mkrt_preconfig(struct protocol *P UNUSED, struct config *c UNUSED) + { - struct mkrt_proto *p = (struct mkrt_proto *) P; ++ mkrt_cf = NULL; ++} + - if (iface->flags & IF_IGNORE) - return; ++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; ++} + - if (flags & IF_CHANGE_UP) - mkrt_add_vif(p, iface); ++void ++mkrt_postconfig(struct proto_config *CF) ++{ ++ // struct mkrt_config *cf = (void *) CF; + - if (flags & IF_CHANGE_DOWN) - mkrt_del_vif(p, iface); ++ if (EMPTY_LIST(CF->channels)) ++ cf_error("Channel not specified"); + } + + static struct proto * -mkrt_init(struct proto_config *c) ++mkrt_init(struct proto_config *CF) + { - struct mkrt_proto *p = proto_new(c); ++ struct mkrt_proto *p = proto_new(CF); + - p->p.main_channel = proto_add_channel(&p->p, proto_cf_main_channel(c)); ++ p->p.main_channel = proto_add_channel(&p->p, proto_cf_main_channel(CF)); + + p->p.rt_notify = mkrt_rt_notify; - p->p.if_notify = mkrt_if_notify; ++ ++ p->mif_group = global_mif_group; + + return &p->p; + } + + static int + mkrt_start(struct proto *P) + { - struct mkrt_proto *p = (struct mkrt_proto *) P; ++ struct mkrt_proto *p = (void *) P; + - p->vif_count = 0; ++ 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); + - HASH_INIT(p->mfc_groups, p->p.pool, 6); ++ if (!mkrt_open_socket(p)) ++ return PS_START; + - if (mkrt_init_sock(p) < 0) - return PS_DOWN; ++ mkrt_register_mif_group(p, p->mif_group); + + return PS_UP; + } + + static int + mkrt_shutdown(struct proto *P) + { - struct mkrt_proto *p = (struct mkrt_proto *) P; - mkrt_call(p, MRT_DONE, NULL, 0); - rfree(p->igmp_sock); ++ 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 = (struct mkrt_proto *) P; - struct mkrt_mfc_source *s; - - debug("\tVIFs as in bitmaps:\n\t\t"); - for (int i = MAXVIFS; i >= 0; i--) - if (p->vif_map[i]) - debug("%s ", p->vif_map[i]->name); - debug("\n\t(S,G) entries in MFC in kernel:\n"); - HASH_WALK(p->mfc_groups, next, group) - { - WALK_LIST(s, group->sources) - debug("\t\t(%I, %I, %s) -> %b %b\n", s->addr, group->ga, p->vif_map[s->vifi]->name, s->iifs, s->oifs); - } - HASH_WALK_END; ++ 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_mkrt = { - .name = "mkernel", - .template = "mkernel%d", - .proto_size = sizeof(struct mkrt_proto), - .config_size = sizeof(struct proto_config), - .channel_mask = NB_MGRP, - .preconfig = mkrt_preconfig, - .init = mkrt_init, - .start = mkrt_start, - .shutdown = mkrt_shutdown, - .dump = mkrt_dump, ++ ++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, + }; diff --cc sysdep/unix/mkrt.h index 000000000,586dfc93c..38b0c2f76 mode 000000,100644..100644 --- a/sysdep/unix/mkrt.h +++ b/sysdep/unix/mkrt.h @@@ -1,0 -1,55 +1,51 @@@ + /* - * BIRD -- Multicast routing kernel ++ * BIRD -- UNIX Kernel Multicast Routing + * - * (c) 2016 Ondrej Hlavaty ++ * (c) 2016 Ondrej Hlavaty ++ * (c) 2018 Ondrej Zajicek ++ * (c) 2018 CZ.NIC z.s.p.o. + * - * Can be freely distributed and used under the terms of the GNU GPL. ++ * 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" -#include "lib/hash.h" - -#include + -extern struct protocol proto_mkrt; + -/* Proto state - kernel comm */ ++extern struct protocol proto_unix_mkrt; + + struct mkrt_config { + struct proto_config cf; + }; + + struct mkrt_mfc_group { - struct mkrt_mfc_group *next; - ip_addr ga; - list sources; ++ list sources; /* List of MFC entries (struct mkrt_mfc_source) */ ++ struct fib_node n; + }; + + struct mkrt_mfc_source { + node n; - ip_addr addr; ++ ip4_addr addr; + - /* remember these for dumping */ - vifi_t vifi; - u32 iifs, oifs; ++ int parent; /* MIF index of valid incoming iface */ ++ u32 iifs, oifs; /* Values from the multicast route */ + }; + + struct mkrt_proto { + struct proto p; + - sock *igmp_sock; - unsigned vif_count; - struct iface *vif_map[MAXVIFS]; ++ struct mif_group *mif_group; /* Associated MIF group for multicast routes */ ++ sock *mrt_sock; /* MRT control socket */ + - HASH(struct mkrt_mfc_group) mfc_groups; ++ struct fib mfc_groups; /* MFC entries/groups managed by protocol */ + }; + -void mkrt_io_init(void); -unsigned mkrt_get_vif(struct iface *i); -void mkrt_listen(sock *s); -void mkrt_stop(sock *s); ++struct proto_config *mkrt_init_config(int class); + -struct proto_config *mkrt_config_init(int class); -void mkrt_config_finish(struct proto_config *); ++#endif diff --cc sysdep/unix/unix.h index cb12fad84,dcaab7297..49f8ead79 --- a/sysdep/unix/unix.h +++ b/sysdep/unix/unix.h @@@ -58,6 -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])); }