fi
fi
-all_protocols="aggregator $proto_bfd babel bgp l3vpn mrt ospf perf pipe radv rip rpki static"
+all_protocols="aggregator $proto_bfd babel bgp bridge l3vpn mrt ospf perf pipe radv rip rpki static"
all_protocols=`echo $all_protocols | sed 's/ /,/g'`
AH_TEMPLATE([CONFIG_BFD], [BFD protocol])
AH_TEMPLATE([CONFIG_BGP], [BGP protocol])
AH_TEMPLATE([CONFIG_BMP], [BMP protocol])
+AH_TEMPLATE([CONFIG_BRIDGE], [Bridge protocol])
AH_TEMPLATE([CONFIG_L3VPN], [L3VPN protocol])
AH_TEMPLATE([CONFIG_MRT], [MRT protocol])
AH_TEMPLATE([CONFIG_OSPF], [OSPF protocol])
PROTOCOL_BFD,
PROTOCOL_BGP,
PROTOCOL_BMP,
+ PROTOCOL_BRIDGE,
PROTOCOL_DEVICE,
PROTOCOL_DIRECT,
PROTOCOL_KERNEL,
extern struct protocol
proto_device, proto_radv, proto_rip, proto_static, proto_mrt,
- proto_ospf, proto_perf, proto_l3vpn, proto_aggregator,
+ proto_ospf, proto_perf, proto_l3vpn, proto_aggregator, proto_bridge,
proto_pipe, proto_bgp, proto_bmp, proto_bfd, proto_babel, proto_rpki;
/*
#define RTS_PERF 15 /* Perf checker */
#define RTS_L3VPN 16 /* MPLS L3VPN */
#define RTS_AGGREGATED 17 /* Aggregated route */
-#define RTS_MAX 18
+#define RTS_BRIDGE 18
+#define RTS_MAX 19
#define RTD_NONE 0 /* Undefined next hop */
#define RTD_UNICAST 1 /* Next hop is neighbor router */
[RTS_PERF] = "Perf",
[RTS_L3VPN] = "L3VPN",
[RTS_AGGREGATED] = "aggregated",
+ [RTS_BRIDGE] = "bridge",
};
const char * rta_dest_names[RTD_MAX] = {
"RTS_STAT_DEV", "RTS_REDIR", "RTS_RIP",
"RTS_OSPF", "RTS_OSPF_IA", "RTS_OSPF_EXT1",
"RTS_OSPF_EXT2", "RTS_BGP", "RTS_PIPE", "RTS_BABEL",
- "RTS_RPKI", "RTS_PERF", "RTS_AGGREGATED", };
+ "RTS_RPKI", "RTS_PERF", "RTS_AGGREGATED", "RTS_BRIDGE" };
static char *rtd[] = { "", " DEV", " HOLE", " UNREACH", " PROHIBIT" };
RDUMP("pref=%d uc=%d %s %s%s h=%04x",
--- /dev/null
+S bridge.c
--- /dev/null
+src := bridge.c
+obj := $(src-o-files)
+$(all-daemon)
+$(cf-local)
+
+tests_objs := $(tests_objs) $(src-o-files)
--- /dev/null
+/*
+ * BIRD -- Linux Bridge Interface
+ *
+ * (c) 2023 Ondrej Zajicek <santiago@crfreenet.org>
+ * (c) 2023 CZ.NIC z.s.p.o.
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+/**
+ * DOC: Bridge
+ *
+ * The Bridge protocol is responsible for synchronization of BIRD ethernet
+ * table with Linux kernel bridge interface (although the code is mostly
+ * OS-independent, as Linux-specific parts are in the Netlink code). It is
+ * similar to (and based on) the Kernel protocol, but the differences are
+ * large enough to treat it as an independent protocol.
+ */
+
+/*
+ * TODO:
+ * - Better two-way synchronization, including initial clean-up
+ * - Wait for existence (and active state) of the bridge device
+ * - Check for consistency of vlan_filtering flag
+ * - Channel should be R_ANY for BUM routes, but RA_OPTIMAL for others
+ * - Configuration of VIDs?
+ */
+
+#undef LOCAL_DEBUG
+
+#include "nest/bird.h"
+#include "nest/iface.h"
+#include "nest/protocol.h"
+#include "nest/route.h"
+#include "nest/mpls.h"
+#include "nest/cli.h"
+#include "conf/conf.h"
+#include "filter/filter.h"
+#include "filter/data.h"
+#include "lib/string.h"
+
+#include "bridge.h"
+
+void
+kbr_got_route(struct kbr_proto *p, const net_addr *n, rte *e, int src UNUSED, int scan UNUSED)
+{
+ struct channel *c = p->p.main_channel;
+
+ if (e) e->attrs->pref = c->preference;
+ rte_update2(c, n, e, p->p.main_source);
+}
+
+static void
+kbr_scan(timer *t)
+{
+ struct kbr_proto *p = t->data;
+ struct channel *c = p->p.main_channel;
+
+ TRACE(D_EVENTS, "Scanning bridge table");
+
+ rt_refresh_begin(c->table, c);
+ kbr_do_scan(p);
+ rt_refresh_end(c->table, c);
+}
+
+static void
+kbr_rt_notify(struct proto *P, struct channel *c0 UNUSED, net *net, rte *new, rte *old)
+{
+ struct kbr_proto *p UNUSED = (void *) P;
+
+ rte *new_gw = (new && ipa_nonzero(new->attrs->nh.gw)) ? new : NULL;
+ rte *old_gw = (old && ipa_nonzero(old->attrs->nh.gw)) ? old : NULL;
+
+ /*
+ * This code handles peculiarities of Linux bridge behavior, where the bridge
+ * device has attached both network interfaces and a tunnel (VXLAN) device.
+ * For 'remote' MAC addresses, forwarding entries in the bridge device point
+ * to the tunnel device. The tunnel device has another forwarding table with
+ * forwarding entries, this time with IP addresses of remote endpoints.
+ *
+ * BUM frames are propagated by the bridge device to all attached devices, so
+ * there is no need to have a bridge forwarding entry, but they must have a
+ * tunnel forwarding entry for each destination.
+ */
+
+ if (mac_zero(net_mac_addr(net->n.addr)))
+ {
+ /* For BUM routes, we have multiple tunnel entries, but no bridge entry */
+ kbr_update_fdb(net->n.addr, new_gw, old_gw, 1);
+ return;
+ }
+
+ /* For regular routes, we have one bridge entry, perhaps also one tunnel entry */
+ kbr_replace_fdb(net->n.addr, new, old, 0);
+ kbr_replace_fdb(net->n.addr, new_gw, old_gw, 1);
+}
+
+static inline int
+kbr_is_installed(struct channel *c, net *n)
+{
+ return n->routes && bmap_test(&c->export_map, n->routes->id);
+}
+
+static void
+kbr_flush_routes(struct kbr_proto *p)
+{
+ struct channel *c = p->p.main_channel;
+
+ TRACE(D_EVENTS, "Flushing bridge routes");
+ FIB_WALK(&c->table->fib, net, n)
+ {
+ if (kbr_is_installed(c, n))
+ kbr_rt_notify(&p->p, c, n, NULL, n->routes);
+ }
+ FIB_WALK_END;
+}
+
+
+static int
+kbr_preexport(struct channel *C, rte *e)
+{
+ struct kbr_proto *p = (void *) C->proto;
+
+ /* Reject our own routes */
+ if (e->src->proto == &p->p)
+ return -1;
+
+ return 0;
+}
+
+static void
+kbr_reload_routes(struct channel *C)
+{
+ struct kbr_proto *p = (void *) C->proto;
+
+ tm_start(p->scan_timer, 0);
+}
+
+static inline u32
+kbr_metric(rte *e)
+{
+ u32 metric = ea_get_int(e->attrs->eattrs, EA_GEN_IGP_METRIC, e->attrs->igp_metric);
+ return MIN(metric, IGP_METRIC_UNKNOWN);
+}
+
+static int
+kbr_rte_better(rte *new, rte *old)
+{
+ /* This is hack, we should have full BGP-style comparison */
+ return kbr_metric(new) < kbr_metric(old);
+}
+
+static void
+kbr_postconfig(struct proto_config *CF)
+{
+ struct kbr_config *cf = (void *) CF;
+
+ if (! proto_cf_main_channel(CF))
+ cf_error("Channel not specified");
+
+ if (!cf->bridge_dev)
+ cf_error("Bridge device not specified");
+}
+
+static struct proto *
+kbr_init(struct proto_config *CF)
+{
+ struct proto *P = proto_new(CF);
+ // struct kbr_proto *p = (void *) P;
+ // struct kbr_config *cf = (void *) CF;
+
+ P->main_channel = proto_add_channel(P, proto_cf_main_channel(CF));
+
+ P->rt_notify = kbr_rt_notify;
+ P->preexport = kbr_preexport;
+ P->reload_routes = kbr_reload_routes;
+ P->rte_better = kbr_rte_better;
+
+ return P;
+}
+
+static int
+kbr_start(struct proto *P)
+{
+ struct kbr_proto *p = (void *) P;
+ struct kbr_config *cf = (void *) P->cf;
+
+ p->bridge_dev = cf->bridge_dev;
+ p->vlan_filtering = cf->vlan_filtering;
+
+ p->scan_timer = tm_new_init(p->p.pool, kbr_scan, p, cf->scan_time, 0);
+ tm_start(p->scan_timer, 100 MS);
+
+ kbr_sys_start(p);
+
+ return PS_UP;
+}
+
+static int
+kbr_shutdown(struct proto *P UNUSED)
+{
+ struct kbr_proto *p = (void *) P;
+
+ kbr_flush_routes(p);
+ kbr_sys_shutdown(p);
+
+ return PS_DOWN;
+}
+
+static int
+kbr_reconfigure(struct proto *P, struct proto_config *CF)
+{
+ struct kbr_proto *p = (void *) P;
+ struct kbr_config *cf = (void *) CF;
+
+ if ((p->bridge_dev != cf->bridge_dev) ||
+ (p->vlan_filtering != cf->vlan_filtering))
+ return 0;
+
+ if (!proto_configure_channel(P, &P->main_channel, proto_cf_main_channel(CF)))
+ return 0;
+
+ return 1;
+}
+
+static void
+kbr_copy_config(struct proto_config *dest UNUSED, struct proto_config *src UNUSED)
+{
+ /* Just a shallow copy, not many items here */
+}
+
+const char * const kbr_src_names[KBR_SRC_MAX] = {
+ [KBR_SRC_BIRD] = "bird",
+ [KBR_SRC_LOCAL] = "local",
+ [KBR_SRC_STATIC] = "static",
+ [KBR_SRC_DYNAMIC] = "dynamic",
+};
+
+static int
+kbr_get_attr(const eattr *a, byte *buf, int buflen UNUSED)
+{
+ switch (a->id)
+ {
+ case EA_KBR_SOURCE:;
+ const char *src = (a->u.data < KBR_SRC_MAX) ? kbr_src_names[a->u.data] : "?";
+ bsprintf(buf, "source: %s", src);
+ return GA_FULL;
+
+ default:
+ return GA_UNKNOWN;
+ }
+}
+
+static void
+kbr_get_route_info(rte *rte, byte *buf)
+{
+ eattr *a = ea_find(rte->attrs->eattrs, EA_KBR_SOURCE);
+ char src = (a && a->u.data < KBR_SRC_MAX) ? "BLSD"[a->u.data] : '?';
+
+ bsprintf(buf, " %c (%u)", src, rte->attrs->pref);
+}
+
+
+struct protocol proto_bridge = {
+ .name = "Bridge",
+ .template = "bridge%d",
+ .class = PROTOCOL_BRIDGE,
+ .channel_mask = NB_ETH,
+ .proto_size = sizeof(struct kbr_proto),
+ .config_size = sizeof(struct kbr_config),
+ .postconfig = kbr_postconfig,
+ .init = kbr_init,
+ .start = kbr_start,
+ .shutdown = kbr_shutdown,
+ .reconfigure = kbr_reconfigure,
+ .copy_config = kbr_copy_config,
+ .get_attr = kbr_get_attr,
+ .get_route_info = kbr_get_route_info,
+};
+
+void
+bridge_build(void)
+{
+ proto_build(&proto_bridge);
+}
--- /dev/null
+/*
+ * BIRD -- Linux Bridge Interface
+ *
+ * (c) 2023 Ondrej Zajicek <santiago@crfreenet.org>
+ * (c) 2023 CZ.NIC z.s.p.o.
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+#ifndef _BIRD_BRIDGE_H_
+#define _BIRD_BRIDGE_H_
+
+
+#define EA_KBR_SOURCE EA_CODE(PROTOCOL_BRIDGE, 0)
+
+#define KBR_SRC_BIRD 0
+#define KBR_SRC_LOCAL 1
+#define KBR_SRC_STATIC 2
+#define KBR_SRC_DYNAMIC 3
+#define KBR_SRC_MAX 4
+
+
+struct kbr_config {
+ struct proto_config c;
+
+ struct iface *bridge_dev;
+ btime scan_time;
+ int vlan_filtering;
+};
+
+struct kbr_proto {
+ struct proto p;
+
+ struct iface *bridge_dev;
+ timer *scan_timer;
+ int vlan_filtering;
+
+ struct kbr_proto *hash_next;
+};
+
+void kbr_got_route(struct kbr_proto *p, const net_addr *n, rte *e, int src, int scan);
+
+
+/* krt sysdep */
+
+int kbr_sys_start(struct kbr_proto *p);
+void kbr_sys_shutdown(struct kbr_proto *p);
+
+void kbr_replace_fdb(const net_addr *n, rte *new, rte *old, int tunnel);
+void kbr_update_fdb(const net_addr *n, rte *new, rte *old, int tunnel);
+void kbr_do_scan(struct kbr_proto *p);
+
+#endif
--- /dev/null
+/*
+ * BIRD -- Linux Bridge Interface
+ *
+ * (c) 2023 Ondrej Zajicek <santiago@crfreenet.org>
+ * (c) 2023 CZ.NIC z.s.p.o.
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+CF_HDR
+
+#include "proto/bridge/bridge.h"
+
+
+CF_DEFINES
+
+#define KBR_CFG ((struct kbr_config *) this_proto)
+
+
+CF_DECLS
+
+CF_KEYWORDS(BRIDGE, BRIDGE, DEVICE, VLAN, FILTERING, SCAN, TIME, KBR_SOURCE)
+
+
+CF_GRAMMAR
+
+proto: kbr_proto;
+
+
+kbr_proto_start: proto_start BRIDGE
+{
+ this_proto = proto_config_new(&proto_bridge, $1);
+ this_proto->net_type = NET_ETH;
+
+ KBR_CFG->scan_time = 60 S_;
+};
+
+kbr_proto_item:
+ proto_item
+ | proto_channel { $1->ra_mode = RA_ANY; }
+ | BRIDGE DEVICE text { KBR_CFG->bridge_dev = if_get_by_name($3); }
+ | VLAN FILTERING bool { KBR_CFG->vlan_filtering = $3; }
+ | SCAN TIME expr_us { KBR_CFG->scan_time = $3; }
+ ;
+
+kbr_proto_opts:
+ /* empty */
+ | kbr_proto_opts kbr_proto_item ';'
+ ;
+
+kbr_proto:
+ kbr_proto_start proto_name '{' kbr_proto_opts '}';
+
+
+dynamic_attr: KBR_SOURCE { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_KBR_SOURCE); } ;
+
+CF_CODE
+
+CF_END
#include "lib/hash.h"
#include "conf/conf.h"
+#include "proto/bridge/bridge.h"
+
#include CONFIG_INCLUDE_NLSYS_H
#define krt_ipv4(p) ((p)->af == AF_INET)
int scan;
u32 rta_flow;
+ u32 bridge_id;
};
/*
nl_scan.last_hdr = NULL;
}
+static void
+nl_request_dump_neigh(int af, int bridge_id)
+{
+ struct {
+ struct nlmsghdr nh;
+ struct ndmsg ndm;
+ struct rtattr rta;
+ u32 master_id;
+ } req = {
+ .nh.nlmsg_type = RTM_GETNEIGH,
+ .nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg)),
+ .nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP,
+ .nh.nlmsg_seq = ++(nl_scan.seq),
+ .ndm.ndm_family = af,
+ };
+
+ if (bridge_id)
+ {
+ req.rta.rta_type = NDA_MASTER;
+ req.rta.rta_len = RTA_LENGTH(4);
+ req.master_id = bridge_id;
+ req.nh.nlmsg_len = NLMSG_ALIGN(req.nh.nlmsg_len) + req.rta.rta_len;
+ }
+
+ send(nl_scan.fd, &req, req.nh.nlmsg_len, 0);
+ nl_scan.last_hdr = NULL;
+}
+
static struct nlmsghdr *
nl_get_reply(struct nl_sock *nl)
#endif
+#define BIRD_NDA_MAX (NDA_MASTER+1)
+
+static struct nl_want_attrs ndm_attr_want[BIRD_NDA_MAX] = {
+ [NDA_LLADDR] = { 1, 1, sizeof(mac_addr) },
+ [NDA_VLAN] = { 1, 1, sizeof(u16) },
+ [NDA_VNI] = { 1, 1, sizeof(u32) },
+ [NDA_MASTER] = { 1, 1, sizeof(u32) },
+};
+
+
static int
nl_parse_attrs(struct rtattr *a, struct nl_want_attrs *want, struct rtattr **k, int ksize)
{
return ipa_from_ip6(rta_get_ip6(a));
}
+static inline mac_addr rta_get_mac(struct rtattr *a)
+{ return *(mac_addr *) RTA_DATA(a); }
+
static inline ip_addr rta_get_via(struct rtattr *a)
{
struct rtvia *v = RTA_DATA(a);
#define RTH_FN(a,i) a ^ u32_hash(i)
#define RTH_REHASH rth_rehash
-#define RTH_PARAMS /8, *2, 2, 2, 6, 20
+#define RTH_PARAMS /8, *1, 2, 2, 4, 20
HASH_DEFINE_REHASH_FN(RTH, struct krt_proto)
}
}
+
+/*
+ * FDB entries
+ */
+
+static inline u32
+kbr_bridge_id(struct kbr_proto *p)
+{
+ return p->bridge_dev->index;
+}
+
+static HASH(struct kbr_proto) nl_bridge_map;
+
+#define BRH_KEY(p) kbr_bridge_id(p)
+#define BRH_NEXT(p) p->hash_next
+#define BRH_EQ(i1,i2) i1 == i2
+#define BRH_FN(i) u32_hash(i)
+
+#define BRH_REHASH brh_rehash
+#define BRH_PARAMS /8, *1, 2, 2, 4, 20
+
+HASH_DEFINE_REHASH_FN(BRH, struct kbr_proto);
+
+static int
+nl_send_fdb(const net_addr *n0, rte *e, int op, int tunnel)
+{
+ const net_addr_eth *n = (void *) n0;
+
+ struct {
+ struct nlmsghdr h;
+ struct ndmsg n;
+ char buf[0];
+ } *r;
+
+ int rsize = sizeof(*r) + 256;
+ r = alloca(rsize);
+
+ memset(&r->h, 0, sizeof(r->h));
+ memset(&r->n, 0, sizeof(r->n));
+ r->h.nlmsg_type = op ? RTM_NEWNEIGH : RTM_DELNEIGH;
+ r->h.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg));
+ r->h.nlmsg_flags = op | NLM_F_REQUEST | NLM_F_ACK;
+
+ r->n.ndm_family = AF_BRIDGE;
+ r->n.ndm_state = NUD_NOARP;
+ r->n.ndm_flags = (tunnel ? NTF_SELF : NTF_MASTER) | NTF_EXT_LEARNED;
+
+ nl_add_attr(&r->h, rsize, NDA_LLADDR, n->mac.addr, 6);
+
+ if (n->vid)
+ nl_add_attr_u16(&r->h, rsize, NDA_VLAN, n->vid);
+
+ struct nexthop *nh = &e->attrs->nh;
+ ASSERT(e->attrs->dest == RTD_UNICAST && !nh->next);
+ r->n.ndm_ifindex = nh->iface->index;
+
+ if (tunnel)
+ {
+ ASSERT(ipa_nonzero(nh->gw));
+ nl_add_attr_ipa(&r->h, rsize, NDA_DST, nh->gw);
+
+ if (nh->labels)
+ nl_add_attr_u32(&r->h, rsize, NDA_VNI, nh->label[0]);
+
+ r->n.ndm_state |= NUD_PERMANENT;
+ }
+
+ /* Ignore missing for DELETE */
+ return nl_exchange(&r->h, (op == NL_OP_DELETE));
+}
+
+void
+kbr_replace_fdb(const net_addr *n, rte *new, rte *old, int tunnel)
+{
+ int err = 0;
+
+ if (old && new)
+ {
+ err = nl_send_fdb(n, new, NL_OP_REPLACE, tunnel);
+ }
+ else
+ {
+ if (old)
+ nl_send_fdb(n, old, NL_OP_DELETE, tunnel);
+
+ if (new)
+ err = nl_send_fdb(n, new, NL_OP_ADD, tunnel);
+ }
+
+ if (err < 0)
+ log(L_WARN "NL error %m");
+}
+
+void
+kbr_update_fdb(const net_addr *n, rte *new, rte *old, int tunnel)
+{
+ int err = 0;
+
+ if (old)
+ nl_send_fdb(n, old, NL_OP_DELETE, tunnel);
+
+ if (new)
+ err = nl_send_fdb(n, new, NL_OP_APPEND, tunnel);
+
+ if (err < 0)
+ log(L_WARN "NL error %m");
+}
+
+#ifndef NDM_RTA
+#define NDM_RTA(n) (struct rtattr *)(((char *) n) + NLMSG_ALIGN(sizeof(struct ndmsg)))
+#endif
+
+static void
+nl_parse_fdb(struct nl_parse_state *s, struct nlmsghdr *h)
+{
+ struct ndmsg *nd;
+ struct rtattr *a[BIRD_NDA_MAX];
+ int new = (h->nlmsg_type == RTM_NEWNEIGH);
+
+ if (!(nd = nl_checkin(h, sizeof(*nd))))
+ return;
+
+ if (nd->ndm_family != AF_BRIDGE)
+ return;
+
+ if (!nl_parse_attrs(NDM_RTA(nd), ndm_attr_want, a, sizeof(a)))
+ return;
+
+ if (!a[NDA_LLADDR] || !a[NDA_MASTER])
+ return;
+
+ uint vid = a[NDA_VLAN] ? rta_get_u16(a[NDA_VLAN]) : 0;
+
+ net_addr n;
+ net_fill_eth(&n, rta_get_mac(a[NDA_LLADDR]), vid);
+
+ u32 bridge_id = rta_get_u32(a[NDA_MASTER]);
+
+ /* Should be filtered by kernel */
+ if (s->bridge_id && (bridge_id != s->bridge_id))
+ return;
+
+ /* Do we know this bridge? */
+ struct kbr_proto *p = HASH_FIND(nl_bridge_map, BRH, bridge_id);
+ if (!p)
+ return;
+
+ /* Accept VLAN-tagged entries when vlan filtering is enabled */
+ if (!vid != !p->vlan_filtering)
+ return;
+
+ struct iface *oif = if_find_by_index(nd->ndm_ifindex);
+ if (!oif)
+ return;
+
+ rta ra = {
+ .source = RTS_BRIDGE,
+ .scope = SCOPE_UNIVERSE,
+ .dest = RTD_UNICAST,
+ .nh.iface = oif,
+ };
+
+ int src;
+ if (nd->ndm_flags & NTF_EXT_LEARNED)
+ // src = KBR_SRC_BIRD;
+ return;
+ else if (nd->ndm_state & NUD_PERMANENT)
+ src = KBR_SRC_LOCAL;
+ else if (nd->ndm_state & NUD_NOARP)
+ src = KBR_SRC_STATIC;
+ else
+ src = KBR_SRC_DYNAMIC;
+
+ ea_set_attr_u32(&ra.eattrs, s->pool, EA_KBR_SOURCE, 0, EAF_TYPE_INT, src);
+
+ rte *e = new ? rte_get_temp(&ra, p->p.main_source) : NULL;
+
+ DBG("FDB %s %N dev %s [%x %x %x]\n", (new ? "add" : "del"), &n, oif->name, nd->ndm_state, nd->ndm_flags, nd->ndm_type);
+ kbr_got_route(p, &n, e, src, s->scan);
+}
+
+void
+kbr_do_scan(struct kbr_proto *p)
+{
+ struct nl_parse_state s = {
+ .pool = nl_linpool,
+ .scan = 1,
+ .bridge_id = kbr_bridge_id(p),
+ };
+
+ nl_request_dump_neigh(AF_BRIDGE, kbr_bridge_id(p));
+
+ struct nlmsghdr *h;
+ while (h = nl_get_scan())
+ {
+ if (h->nlmsg_type == RTM_NEWNEIGH || h->nlmsg_type == RTM_DELNEIGH)
+ nl_parse_fdb(&s, h);
+ }
+}
+
+
/*
* Asynchronous Netlink interface
*/
DBG("KRT: Received async route notification (%d)\n", h->nlmsg_type);
nl_parse_route(&s, h);
break;
+
case RTM_NEWLINK:
case RTM_DELLINK:
DBG("KRT: Received async link notification (%d)\n", h->nlmsg_type);
if (kif_proto)
nl_parse_link(h, 0);
break;
+
case RTM_NEWADDR:
case RTM_DELADDR:
DBG("KRT: Received async address notification (%d)\n", h->nlmsg_type);
if (kif_proto)
nl_parse_addr(h, 0);
break;
+
+ case RTM_NEWNEIGH:
+ case RTM_DELNEIGH:
+ nl_parse_fdb(&s, h);
+
default:
DBG("KRT: Received unknown async notification (%d)\n", h->nlmsg_type);
}
bzero(&sa, sizeof(sa));
sa.nl_family = AF_NETLINK;
- sa.nl_groups = RTMGRP_LINK |
+ sa.nl_groups = RTMGRP_LINK | RTMGRP_NEIGH |
RTMGRP_IPV4_IFADDR | RTMGRP_IPV4_ROUTE |
RTMGRP_IPV6_IFADDR | RTMGRP_IPV6_ROUTE;
krt_sys_io_init(void)
{
nl_linpool = lp_new_default(krt_pool);
- HASH_INIT(nl_table_map, krt_pool, 6);
+ HASH_INIT(nl_table_map, krt_pool, 4);
+ HASH_INIT(nl_bridge_map, krt_pool, 4);
}
int
}
+int
+kbr_sys_start(struct kbr_proto *p)
+{
+ struct kbr_proto *old = HASH_FIND(nl_bridge_map, BRH, kbr_bridge_id(p));
+
+ if (old)
+ {
+ log(L_ERR "%s: Bridge device %s already registered by %s",
+ p->p.name, p->bridge_dev->name, old->p.name);
+ return 0;
+ }
+
+ HASH_INSERT2(nl_bridge_map, BRH, krt_pool, p);
+
+ nl_open();
+ nl_open_async();
+
+ return 1;
+}
+
+void
+kbr_sys_shutdown(struct kbr_proto *p)
+{
+ HASH_REMOVE2(nl_bridge_map, BRH, krt_pool, p);
+}
+
void
kif_sys_start(struct kif_proto *p UNUSED)