diff --git a/lib/socket.h b/lib/socket.h index 11bcff71..f7d1b3aa 100644 --- a/lib/socket.h +++ b/lib/socket.h @@ -17,6 +17,9 @@ #include #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); diff --git a/nest/iface.c b/nest/iface.c index b165e6e7..16b06c7c 100644 --- a/nest/iface.c +++ b/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 */ diff --git a/nest/iface.h b/nest/iface.h index dc44dfad..3bd8d031 100644 --- a/nest/iface.h +++ b/nest/iface.h @@ -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]; }; diff --git a/nest/route.h b/nest/route.h index b2ae4686..27f11928 100644 --- a/nest/route.h +++ b/nest/route.h @@ -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); diff --git a/nest/rt-table.c b/nest/rt-table.c index 3e15a17d..dc6a224a 100644 --- a/nest/rt-table.c +++ b/nest/rt-table.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) diff --git a/proto/radv/radv.c b/proto/radv/radv.c index 8a79dfaf..3c3e650a 100644 --- a/proto/radv/radv.c +++ b/proto/radv/radv.c @@ -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 diff --git a/sysdep/unix/Doc b/sysdep/unix/Doc index a17f425b..6471e457 100644 --- a/sysdep/unix/Doc +++ b/sysdep/unix/Doc @@ -1,3 +1,4 @@ S log.c S krt.c +S mkrt.c # io.c is documented under Resources diff --git a/sysdep/unix/Makefile b/sysdep/unix/Makefile index f592399c..3cb85114 100644 --- a/sysdep/unix/Makefile +++ b/sysdep/unix/Makefile @@ -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) diff --git a/sysdep/unix/io.c b/sysdep/unix/io.c index 13b5e639..e4b8cc37 100644 --- a/sysdep/unix/io.c +++ b/sysdep/unix/io.c @@ -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 diff --git a/sysdep/unix/main.c b/sysdep/unix/main.c index 2251d3fb..cee8114c 100644 --- a/sysdep/unix/main.c +++ b/sysdep/unix/main.c @@ -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(); diff --git a/sysdep/unix/mkrt.Y b/sysdep/unix/mkrt.Y new file mode 100644 index 00000000..e8639947 --- /dev/null +++ b/sysdep/unix/mkrt.Y @@ -0,0 +1,41 @@ +/* + * 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_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 diff --git a/sysdep/unix/mkrt.c b/sysdep/unix/mkrt.c new file mode 100644 index 00000000..d8566254 --- /dev/null +++ b/sysdep/unix/mkrt.c @@ -0,0 +1,503 @@ +/* + * BIRD -- UNIX Kernel Multicast Routing + * + * (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. + */ + +/** + * 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 + + +/* + * 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, +}; diff --git a/sysdep/unix/mkrt.h b/sysdep/unix/mkrt.h new file mode 100644 index 00000000..38b0c2f7 --- /dev/null +++ b/sysdep/unix/mkrt.h @@ -0,0 +1,51 @@ +/* + * BIRD -- UNIX Kernel Multicast Routing + * + * (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. + */ + +#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 diff --git a/sysdep/unix/unix.h b/sysdep/unix/unix.h index cb12fad8..49f8ead7 100644 --- a/sysdep/unix/unix.h +++ b/sysdep/unix/unix.h @@ -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])); }