]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
Merge commit '76dd4730eac6a632ab28ef85d266cbfc0abc89ca' into pim
authorOndrej Zajicek (work) <santiago@crfreenet.org>
Sat, 3 Mar 2018 12:08:38 +0000 (13:08 +0100)
committerOndrej Zajicek (work) <santiago@crfreenet.org>
Sat, 3 Mar 2018 19:29:58 +0000 (20:29 +0100)
14 files changed:
1  2 
lib/socket.h
nest/iface.c
nest/iface.h
nest/route.h
nest/rt-table.c
proto/radv/radv.c
sysdep/unix/Doc
sysdep/unix/Makefile
sysdep/unix/io.c
sysdep/unix/main.c
sysdep/unix/mkrt.Y
sysdep/unix/mkrt.c
sysdep/unix/mkrt.h
sysdep/unix/unix.h

diff --cc lib/socket.h
index 11bcff71cf21cf26059f25cb8fe8d46218e5761a,7fed4ca3355a99a09e43f2b11ac8265d52bca95c..f7d1b3aa86f43e2ad8a12920ab772983df15b6ab
  #include <errno.h>
  
  #include "lib/resource.h"
 +#ifdef HAVE_LIBSSH
 +#define LIBSSH_LEGACY_0_4
 +#include <libssh/libssh.h>
 +#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 b165e6e74902dadddb40556cb2893e2e7d72e487,4c3e077443ec52f0b3d7c1448ab9f3a840980953..16b06c7c24d126934a52bb2ccb0d35bb8f75c2bc
  #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 dc44dfadbc1debebb18cdf45834c6f7a48a704aa,042543ed03ac538e2da2630b92c01756c1164f1f..3bd8d03184b7c494f7fde3461c261a1c8d54ad27
@@@ -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 b2ae4686d72afe36b4db9433b12051b5a80ba718,0a3e5cdae5f8c35fcf999f02a402ab7787f79154..27f1192805ff7e12d0a8aedbdf49a50da8a10eb3
@@@ -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 3e15a17d98c7bdc46c396252bf19aeb97b0f9557,464107c3fbafd6acb5eb08b1663bb150609aa630..dc6a224a8fdef6c474e8eac0c341691f46f6f648
@@@ -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);
index 8a79dfaf245851f57768dafc14a5c40590dbce18,4c845f7abb845c5062ca5feb087c3a4fd4b3990a..3c3e650a4b9a9401b59100ab6d5510d1c4a9995f
@@@ -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 a17f425b92e0574601ad65dc769d7a4ea702e6e2,a17f425b92e0574601ad65dc769d7a4ea702e6e2..6471e457e7fc0c8d629884b984f47543e3b0b68d
@@@ -1,3 -1,3 +1,4 @@@
  S log.c
  S krt.c
++S mkrt.c
  # io.c is documented under Resources
index f592399c1ba85c8cbda5b4c899301bfddd025942,88b7a95e4ef0c6c73fd107a64e0796274b309230..3cb851147453d41258f284e65e9ac87e1152733c
@@@ -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)
index 13b5e63981b481f24ba55845837ebef28d92a051,d6e51dcc8b3051308e639f725cb26006b6b9a2de..e4b8cc373afe43e4983022c7ea588cad39f76e9f
@@@ -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
index 2251d3fb7a1f6d23c32935909e2f76e32ade45d3,1f47680e53351cf398118194cb653368a9e2db5d..cee8114c8d4d517b1763a2262cdf95345c2de5b3
@@@ -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();
  
index 0000000000000000000000000000000000000000,8be6facac7baa3f3accce54439f6c07ccdcc19a1..e86399471fbbb30876ba9516a012a60a74c13672
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,39 +1,41 @@@
 - *    BIRD -- UNIX Multicast route syncer configuration
+ /*
 -CF_ADDTO(proto, mkrt_proto '}' { mkrt_config_finish(this_proto); })
++ *    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 */
 -mkrt_proto_start: proto_start MKERNEL { this_proto = mkrt_config_init($1); }
++CF_ADDTO(proto, mkrt_proto '}')
 - | proto_channel
++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
index 0000000000000000000000000000000000000000,77a81355e44b716c8a023d67262ba57a2982500d..d8566254efac182337862d9f5a7e3c369fa09ed0
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,585 +1,503 @@@
 - *  BIRD -- Multicast routing kernel
+ /*
 - *  (c) 2016 Ondrej Hlavaty <aearsis@eideo.cz>
++ *    BIRD -- UNIX Kernel Multicast Routing
+  *
 - *  Can be freely distributed and used under the terms of the GNU GPL.
++ *    (c) 2016 Ondrej Hlavaty <aearsis@eideo.cz>
++ *    (c) 2018 Ondrej Zajicek <santiago@crfreenet.org>
++ *    (c) 2018 CZ.NIC z.s.p.o.
+  *
 -/*
 - * DOC: Multicast route kernel synchronization
++ *    Can be freely distributed and used under the terms of the GNU GPL.
+  */
 - * 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.
++/**
++ * 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 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 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.
+  *
 - * 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.
++ * 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.
+  *
 - * 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.
++ * 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.
+  *
 - * Part of the protocol is global and "static", without the need to configure.
 - * Another part is a usual proto instance.
++ * 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.
+  *
 -#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
++ * 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"
 -#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)
 -static struct mkrt_config *mkrt_cf;
 -
 -/* Global code for SK_IGMP sockets */
++#include "unix.h"
++#include "mkrt.h"
 -struct mkrt_iface {
 -  struct mkrt_iface *next;
 -  struct iface *iface;
 -  list sockets;
 -};
++#include <linux/mroute.h>
 -static struct mkrt_global {
 -  pool *pool;
 -  list sockets;
 -  HASH(struct mkrt_iface) ifaces;
 -} mkrt_global;
 -void
 -mkrt_io_init(void)
++/*
++ *    MRT socket options
++ */
 -  mkrt_global.pool = rp_new(&root_pool, "Multicast kernel Syncer");
 -  HASH_INIT(mkrt_global.ifaces, mkrt_global.pool, 6);
 -  init_list(&mkrt_global.sockets);
++static inline int
++sk_mrt_init4(sock *s)
+ {
 -static struct mkrt_iface *
 -mkrt_iface_find(struct mkrt_proto *p, unsigned ifindex)
++  int y = 1;
++  return setsockopt(s->fd, IPPROTO_IP, MRT_INIT, &y, sizeof(y));
+ }
 -  return HASH_FIND(mkrt_global.ifaces, HASH_I, ifindex);
++static inline int
++sk_mrt_done4(sock *s)
+ {
 -static struct mkrt_iface *
 -mkrt_iface_get(unsigned ifindex)
++  return setsockopt(s->fd, IPPROTO_IP, MRT_DONE, NULL, 0);
+ }
 -  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);
++static inline int
++sk_mrt_add_mif4(sock *s, struct mif *mif)
+ {
 -  HASH_INSERT(mkrt_global.ifaces, HASH_I, ifa);
 -  return ifa;
++  struct vifctl vc = {
++    .vifc_vifi = mif->index,
++    .vifc_flags = VIFF_USE_IFINDEX,
++    .vifc_lcl_ifindex = mif->iface->index,
++  };
 -/*
 - * 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)
++  return setsockopt(s->fd, IPPROTO_IP, MRT_ADD_VIF, &vc, sizeof(vc));
+ }
 -  ASSERT(s->type == SK_IGMP);
++static inline int
++sk_mrt_del_mif4(sock *s, struct mif *mif)
+ {
 -  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);
++  struct vifctl vc = {
++    .vifc_vifi = mif->index,
++  };
 -/*
 - * 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)
++  return setsockopt(s->fd, IPPROTO_IP, MRT_DEL_VIF, &vc, sizeof(vc));
+ }
 -  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);
++static inline int
++sk_mrt_add_mfc4(sock *s, ip4_addr src, ip4_addr grp, u32 iifs, u32 oifs, int mif_index)
+ {
 -  to->faddr = to->laddr = IPA_NONE;
 -  to->lifindex = 0;
 -  to->rbuf = to->rpos = NULL;
 -  to->rbsize = 0;
++  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;
 -/*
 - * Forward a packet to all sockets on a list.
 - */
 -static inline void
 -mkrt_rx_forward_all(list *sockets, sock *sk, int len)
++  return setsockopt(s->fd, IPPROTO_IP, MRT_ADD_MFC, &mc, sizeof(mc));
+ }
 -  node *n, *next;
++static inline int
++sk_mrt_del_mfc4(sock *s, ip4_addr src, ip4_addr grp)
+ {
 -  WALK_LIST_DELSAFE(n, next, *sockets)
 -    mkrt_rx_forward(sk, SKIP_BACK(sock, n, n), len);
++  struct mfcctl mc = {
++    .mfcc_origin = ip4_to_in4(src),
++    .mfcc_mcastgrp = ip4_to_in4(grp),
++  };
 -/***************
 -  Mkernel proto
 - ***************/
++  return setsockopt(s->fd, IPPROTO_IP, MRT_DEL_MFC, &mc, sizeof(mc));
+ }
 - * Call a setsockopt with a MRT_ option.
+ /*
 -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);
 -}
++ *    MIF handling
+  */
 -static inline vifi_t
 -mkrt_alloc_vifi(struct mkrt_proto *p, struct iface *iface)
 -  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;
 -      }
 -}
++void
++mkrt_register_mif(struct mkrt_proto *p, struct mif *mif)
+ {
 -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;
++  TRACE(D_EVENTS, "Registering interface %s MIF %i", mif->iface->name, mif->index);
 -static void
 -mkrt_add_vif(struct mkrt_proto *p, struct iface *i)
++  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);
+ }
 -  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;
++void
++mkrt_unregister_mif(struct mkrt_proto *p, struct mif *mif)
+ {
 -err:
 -  log(L_ERR "Error while assigning %s VIF %i: %m", i->name, i->vifi, err);
 -  mkrt_free_vifi(p, i->vifi);
++  TRACE(D_EVENTS, "Unregistering interface %s MIF %i", mif->iface->name, mif->index);
 -static int
 -mkrt_del_vif(struct mkrt_proto *p, struct iface *i)
++  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);
+ }
 -  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;
++void
++mkrt_register_mif_group(struct mkrt_proto *p, struct mif_group *grp)
+ {
 -  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;
++  ASSERT(!grp->owner);
++  grp->owner = &p->p;
 -static struct mkrt_mfc_group *
 -mkrt_mfc_get(struct mkrt_proto *p, ip_addr ga)
++  WALK_ARRAY(grp->mifs, MIFS_MAX, mif)
++    if (mif)
++      mkrt_register_mif(p, mif);
+ }
 -  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;
++void
++mkrt_unregister_mif_group(struct mkrt_proto *p, struct mif_group *grp)
+ {
 - * Add a MFC entry for (S, G) with parent vifi, according to the route.
++  grp->owner = NULL;
++
++  WALK_ARRAY(grp->mifs, MIFS_MAX, mif)
++    if (mif)
++      mkrt_unregister_mif(p, mif);
+ }
++
+ /*
 -static int
 -mkrt_mfc_update(struct mkrt_proto *p, ip_addr group, ip_addr source, int vifi, struct rte *rte)
++ *    MFC handling
+  */
 -  struct mfcctl mc = {0};
 -  int err;
++
++static void
++mkrt_init_mfc(void *G)
+ {
 -  mc.mfcc_origin = ipa_to_in4(source);
 -  mc.mfcc_mcastgrp = ipa_to_in4(group);
 -  mc.mfcc_parent = vifi;
++  struct mkrt_mfc_group *grp = G;
 -  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;
++  init_list(&grp->sources);
++}
 -  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);
++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);
 -  return err;
++  struct mkrt_mfc_source *src;
++  WALK_LIST(src, grp->sources)
++    if (ip4_equal(src->addr, source))
++      return src;
 -struct mfc_request {
 -  ip_addr *group, *source;
 -  vifi_t vifi;
++  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;
+ }
 -/*
 - * Expand the attributes from the struct mfc_request and call mkrt_mfc_update,
 - * and pass back the result.
 - */
++struct mfc_result {
+   u32 iifs, oifs;
+ };
 -mkrt_mfc_call_update(struct proto *p, void *data, rte *rte)
+ static void
 -  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;
 -    }
++mkrt_resolve_mfc_hook(struct proto *p UNUSED, void *data, rte *rte)
+ {
 -mkrt_mfc_resolve(struct mkrt_proto *p, ip_addr group, ip_addr source, vifi_t vifi)
++  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
 -  struct iface *iface = p->vif_map[vifi];
++mkrt_resolve_mfc(struct mkrt_proto *p, ip4_addr src, ip4_addr grp, int mif_index)
+ {
 -  TRACE(D_EVENTS, "MFC miss for (%I, %I, %s)", source, group, iface ? iface->name : "??");
++  struct mif *mif = (mif_index < MIFS_MAX) ? p->mif_group->mifs[mif_index] : NULL;
 -  net_addr_mgrp4 n0 = NET_ADDR_MGRP4(ipa_to_ip4(group));
++  TRACE(D_EVENTS, "MFC miss for (%I4, %I4, %s)", src, grp, mif ? mif->iface->name : "?");
 -  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);
++  net_addr_mgrp4 n0 = NET_ADDR_MGRP4(grp);
++  struct mfc_result 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);
++  rt_examine(p->p.main_channel, (net_addr *) &n0, mkrt_resolve_mfc_hook, &res);
 - * entries are now wrong. Instead of correcting them, flush the cache.
++  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
 -mkrt_mfc_clean(struct mkrt_proto *p, struct mkrt_mfc_group *mg)
++ * entries are now wrong. Instead of correcting them, just flush the cache.
+  */
+ static void
 -  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);
 -    }
++mkrt_reset_mfc_group(struct mkrt_proto *p, struct mkrt_mfc_group *grp)
+ {
 -mkrt_mfc_free(struct mkrt_proto *p, struct mkrt_mfc_group *mg)
++  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_clean(p, mg);
 -  HASH_REMOVE(p->mfc_groups, HASH_MFC, mg);
 -  mb_free(mg);
++mkrt_free_mfc_group(struct mkrt_proto *p, struct mkrt_mfc_group *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)
++  mkrt_reset_mfc_group(p, grp);
++  fib_delete(&p->mfc_groups, grp);
+ }
 -  struct igmpmsg *msg = (struct igmpmsg *) sk->rbuf;
 -  u8 igmp_type = * (u8 *) sk_rx_buffer(sk, &len);
++static void
++mkrt_rt_notify(struct proto *P, struct channel *c UNUSED, net *net, rte *new, rte *old UNUSED, ea_list *attrs UNUSED)
+ {
 -  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;
 -    }
++  struct mkrt_proto *p = (void *) P;
++  struct mkrt_mfc_group *grp = fib_find(&p->mfc_groups, net->n.addr);
 -mkrt_rx_hook(sock *sk, int len)
++  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
 -  /* Do not forward upcalls, IGMP cannot parse them */
 -  if (mkrt_control_message(p, sk, 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);
 -  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);
++  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;
 -  return 1;
++  case IGMPMSG_WRONGVIF:
++  case IGMPMSG_WHOLEPKT:
++    /* These should not happen unless some PIM-specific MRT options are enabled */
++    return 1;
 -  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);
++  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)
+ {
 -  cc->ra_mode = RA_OPTIMAL;
++  struct mkrt_proto *p = sk->data;
 -mkrt_init_sock(struct mkrt_proto *p)
++  log(L_ERR "%s: Socket error: %M", p->p.name, err);
+ }
+ static int
 -  sock *sk;
 -
 -  if (!(sk = sk_new(p->p.pool)))
 -    goto err;
 -
++mkrt_open_socket(struct mkrt_proto *p)
+ {
 -    goto err_sk;
++  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)
 -  p->igmp_sock = sk;
++  {
++    sk_log_error(sk, p->p.name);
++    goto err;
++  }
 -  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;
 -    }
++  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);
 -  log(L_DEBUG "Multicast control socket open with fd %i", sk->fd);
 -  return 0;
++    goto err;
++  }
 -err_sk:
 -  rfree(sk);
 -  p->igmp_sock = NULL;
++  p->mrt_sock = sk;
++  return 1;
 -  return -1;
+ err:
 -void
 -mkrt_rt_notify(struct proto *P, struct channel *c, net *net, rte *new, rte *old, ea_list *attrs)
++  rfree(sk);
++  return 0;
+ }
 -  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);
++static void
++mkrt_close_socket(struct mkrt_proto *p)
+ {
 -mkrt_if_notify(struct proto *P, uint flags, struct iface *iface)
++  sk_mrt_done4(p->mrt_sock);
++  rfree(p->mrt_sock);
++  p->mrt_sock = NULL;
+ }
++
++/*
++ *    Protocol glue
++ */
++
++static struct mkrt_config *mkrt_cf;
++
+ static void
 -  struct mkrt_proto *p = (struct mkrt_proto *) P;
++mkrt_preconfig(struct protocol *P UNUSED, struct config *c UNUSED)
+ {
 -  if (iface->flags & IF_IGNORE)
 -    return;
++  mkrt_cf = NULL;
++}
 -  if (flags & IF_CHANGE_UP)
 -    mkrt_add_vif(p, iface);
++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_DOWN)
 -    mkrt_del_vif(p, iface);
++void
++mkrt_postconfig(struct proto_config *CF)
++{
++  // struct mkrt_config *cf = (void *) CF;
 -mkrt_init(struct proto_config *c)
++  if (EMPTY_LIST(CF->channels))
++    cf_error("Channel not specified");
+ }
+ static struct proto *
 -  struct mkrt_proto *p = proto_new(c);
++mkrt_init(struct proto_config *CF)
+ {
 -  p->p.main_channel = proto_add_channel(&p->p, proto_cf_main_channel(c));
++  struct mkrt_proto *p = proto_new(CF);
 -  p->p.if_notify = mkrt_if_notify;
++  p->p.main_channel = proto_add_channel(&p->p, proto_cf_main_channel(CF));
+   p->p.rt_notify = mkrt_rt_notify;
 -  struct mkrt_proto *p = (struct mkrt_proto *) P;
++
++  p->mif_group = global_mif_group;
+   return &p->p;
+ }
+ static int
+ mkrt_start(struct proto *P)
+ {
 -  p->vif_count = 0;
++  struct mkrt_proto *p = (void *) P;
 -  HASH_INIT(p->mfc_groups, p->p.pool, 6);
++  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_init_sock(p) < 0)
 -    return PS_DOWN;
++  if (!mkrt_open_socket(p))
++    return PS_START;
 -  struct mkrt_proto *p = (struct mkrt_proto *) P;
 -  mkrt_call(p, MRT_DONE, NULL, 0);
 -  rfree(p->igmp_sock);
++  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;
 -  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;
++
++  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 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 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,
+ };
index 0000000000000000000000000000000000000000,586dfc93c49711e1876051deed1fc67faa158dd0..38b0c2f76b3a76df2524bc0bd787fe86adad45ac
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,55 +1,51 @@@
 - *  BIRD -- Multicast routing kernel
+ /*
 - *  (c) 2016 Ondrej Hlavaty <aearsis@eideo.cz>
++ *    BIRD -- UNIX Kernel Multicast Routing
+  *
 - *  Can be freely distributed and used under the terms of the GNU GPL.
++ *    (c) 2016 Ondrej Hlavaty <aearsis@eideo.cz>
++ *    (c) 2018 Ondrej Zajicek <santiago@crfreenet.org>
++ *    (c) 2018 CZ.NIC z.s.p.o.
+  *
 -#include "lib/hash.h"
 -
 -#include <linux/mroute.h>
++ *    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_mkrt;
 -/* Proto state - kernel comm */
 -  struct mkrt_mfc_group *next;
 -  ip_addr ga;
 -  list sources;
++extern struct protocol proto_unix_mkrt;
+ struct mkrt_config {
+   struct proto_config cf;
+ };
+ struct mkrt_mfc_group {
 -  ip_addr addr;
++  list sources;                               /* List of MFC entries (struct mkrt_mfc_source) */
++  struct fib_node n;
+ };
+ struct mkrt_mfc_source {
+   node n;
 -  /* remember these for dumping  */
 -  vifi_t vifi;
 -  u32 iifs, oifs;
++  ip4_addr addr;
 -  sock *igmp_sock;
 -  unsigned vif_count;
 -  struct iface *vif_map[MAXVIFS];
++  int parent;                         /* MIF index of valid incoming iface */
++  u32 iifs, oifs;                     /* Values from the multicast route */
+ };
+ struct mkrt_proto {
+   struct proto p;
 -  HASH(struct mkrt_mfc_group) mfc_groups;
++  struct mif_group *mif_group;                /* Associated MIF group for multicast routes */
++  sock *mrt_sock;                     /* MRT control socket */
 -void mkrt_io_init(void);
 -unsigned mkrt_get_vif(struct iface *i);
 -void mkrt_listen(sock *s);
 -void mkrt_stop(sock *s);
++  struct fib mfc_groups;              /* MFC entries/groups managed by protocol */
+ };
 -struct proto_config *mkrt_config_init(int class);
 -void mkrt_config_finish(struct proto_config *);
++struct proto_config *mkrt_init_config(int class);
++#endif
index cb12fad844bb65da77b3d95960e982e0452ca817,dcaab72973612e3bbcceb16f2f687ce41338990c..49f8ead7930872f4bb2e7918744ee9ebe7133610
@@@ -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])); }