0
0
mirror of https://gitlab.nic.cz/labs/bird.git synced 2024-11-08 12:18:42 +00:00

Bridge: Linux bridge interface - preliminary support

This commit is contained in:
Ondrej Zajicek 2023-10-30 01:50:14 +01:00
parent b5c040d0cf
commit 0c02b06329
10 changed files with 694 additions and 7 deletions

View File

@ -312,7 +312,7 @@ if test "$enable_mpls_kernel" != no ; then
fi fi
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'` all_protocols=`echo $all_protocols | sed 's/ /,/g'`
@ -325,6 +325,7 @@ AH_TEMPLATE([CONFIG_BABEL], [Babel protocol])
AH_TEMPLATE([CONFIG_BFD], [BFD protocol]) AH_TEMPLATE([CONFIG_BFD], [BFD protocol])
AH_TEMPLATE([CONFIG_BGP], [BGP protocol]) AH_TEMPLATE([CONFIG_BGP], [BGP protocol])
AH_TEMPLATE([CONFIG_BMP], [BMP protocol]) AH_TEMPLATE([CONFIG_BMP], [BMP protocol])
AH_TEMPLATE([CONFIG_BRIDGE], [Bridge protocol])
AH_TEMPLATE([CONFIG_L3VPN], [L3VPN protocol]) AH_TEMPLATE([CONFIG_L3VPN], [L3VPN protocol])
AH_TEMPLATE([CONFIG_MRT], [MRT protocol]) AH_TEMPLATE([CONFIG_MRT], [MRT protocol])
AH_TEMPLATE([CONFIG_OSPF], [OSPF protocol]) AH_TEMPLATE([CONFIG_OSPF], [OSPF protocol])

View File

@ -45,6 +45,7 @@ enum protocol_class {
PROTOCOL_BFD, PROTOCOL_BFD,
PROTOCOL_BGP, PROTOCOL_BGP,
PROTOCOL_BMP, PROTOCOL_BMP,
PROTOCOL_BRIDGE,
PROTOCOL_DEVICE, PROTOCOL_DEVICE,
PROTOCOL_DIRECT, PROTOCOL_DIRECT,
PROTOCOL_KERNEL, PROTOCOL_KERNEL,
@ -106,7 +107,7 @@ void protos_dump_all(void);
extern struct protocol extern struct protocol
proto_device, proto_radv, proto_rip, proto_static, proto_mrt, 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; proto_pipe, proto_bgp, proto_bmp, proto_bfd, proto_babel, proto_rpki;
/* /*

View File

@ -482,7 +482,8 @@ typedef struct rta {
#define RTS_PERF 15 /* Perf checker */ #define RTS_PERF 15 /* Perf checker */
#define RTS_L3VPN 16 /* MPLS L3VPN */ #define RTS_L3VPN 16 /* MPLS L3VPN */
#define RTS_AGGREGATED 17 /* Aggregated route */ #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_NONE 0 /* Undefined next hop */
#define RTD_UNICAST 1 /* Next hop is neighbor router */ #define RTD_UNICAST 1 /* Next hop is neighbor router */

View File

@ -78,6 +78,7 @@ const char * const rta_src_names[RTS_MAX] = {
[RTS_PERF] = "Perf", [RTS_PERF] = "Perf",
[RTS_L3VPN] = "L3VPN", [RTS_L3VPN] = "L3VPN",
[RTS_AGGREGATED] = "aggregated", [RTS_AGGREGATED] = "aggregated",
[RTS_BRIDGE] = "bridge",
}; };
const char * rta_dest_names[RTD_MAX] = { const char * rta_dest_names[RTD_MAX] = {
@ -1311,7 +1312,7 @@ rta_dump(rta *a)
"RTS_STAT_DEV", "RTS_REDIR", "RTS_RIP", "RTS_STAT_DEV", "RTS_REDIR", "RTS_RIP",
"RTS_OSPF", "RTS_OSPF_IA", "RTS_OSPF_EXT1", "RTS_OSPF", "RTS_OSPF_IA", "RTS_OSPF_EXT1",
"RTS_OSPF_EXT2", "RTS_BGP", "RTS_PIPE", "RTS_BABEL", "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" }; static char *rtd[] = { "", " DEV", " HOLE", " UNREACH", " PROHIBIT" };
debug("pref=%d uc=%d %s %s%s h=%04x", debug("pref=%d uc=%d %s %s%s h=%04x",

1
proto/bridge/Doc Normal file
View File

@ -0,0 +1 @@
S bridge.c

6
proto/bridge/Makefile Normal file
View File

@ -0,0 +1,6 @@
src := bridge.c
obj := $(src-o-files)
$(all-daemon)
$(cf-local)
tests_objs := $(tests_objs) $(src-o-files)

285
proto/bridge/bridge.c Normal file
View File

@ -0,0 +1,285 @@
/*
* 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);
}

53
proto/bridge/bridge.h Normal file
View File

@ -0,0 +1,53 @@
/*
* 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

59
proto/bridge/config.Y Normal file
View File

@ -0,0 +1,59 @@
/*
* 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

View File

@ -27,6 +27,8 @@
#include "lib/hash.h" #include "lib/hash.h"
#include "conf/conf.h" #include "conf/conf.h"
#include "proto/bridge/bridge.h"
#include CONFIG_INCLUDE_NLSYS_H #include CONFIG_INCLUDE_NLSYS_H
#define krt_ipv4(p) ((p)->af == AF_INET) #define krt_ipv4(p) ((p)->af == AF_INET)
@ -40,6 +42,7 @@ struct nl_parse_state
int scan; int scan;
u32 rta_flow; u32 rta_flow;
u32 bridge_id;
}; };
/* /*
@ -210,6 +213,34 @@ nl_request_dump_route(int af, int table_id)
nl_scan.last_hdr = NULL; 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 * static struct nlmsghdr *
nl_get_reply(struct nl_sock *nl) nl_get_reply(struct nl_sock *nl)
@ -444,6 +475,16 @@ static struct nl_want_attrs rtm_attr_want_mpls[BIRD_RTA_MAX] = {
#endif #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 static int
nl_parse_attrs(struct rtattr *a, struct nl_want_attrs *want, struct rtattr **k, int ksize) nl_parse_attrs(struct rtattr *a, struct nl_want_attrs *want, struct rtattr **k, int ksize)
{ {
@ -493,6 +534,9 @@ static inline ip_addr rta_get_ipa(struct rtattr *a)
return ipa_from_ip6(rta_get_ip6(a)); 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) static inline ip_addr rta_get_via(struct rtattr *a)
{ {
struct rtvia *v = RTA_DATA(a); struct rtvia *v = RTA_DATA(a);
@ -1248,7 +1292,7 @@ static HASH(struct krt_proto) nl_table_map;
#define RTH_FN(a,i) a ^ u32_hash(i) #define RTH_FN(a,i) a ^ u32_hash(i)
#define RTH_REHASH rth_rehash #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) HASH_DEFINE_REHASH_FN(RTH, struct krt_proto)
@ -1858,6 +1902,207 @@ krt_do_scan(struct krt_proto *p)
} }
} }
/*
* 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 * Asynchronous Netlink interface
*/ */
@ -1883,18 +2128,25 @@ nl_async_msg(struct nlmsghdr *h)
DBG("KRT: Received async route notification (%d)\n", h->nlmsg_type); DBG("KRT: Received async route notification (%d)\n", h->nlmsg_type);
nl_parse_route(&s, h); nl_parse_route(&s, h);
break; break;
case RTM_NEWLINK: case RTM_NEWLINK:
case RTM_DELLINK: case RTM_DELLINK:
DBG("KRT: Received async link notification (%d)\n", h->nlmsg_type); DBG("KRT: Received async link notification (%d)\n", h->nlmsg_type);
if (kif_proto) if (kif_proto)
nl_parse_link(h, 0); nl_parse_link(h, 0);
break; break;
case RTM_NEWADDR: case RTM_NEWADDR:
case RTM_DELADDR: case RTM_DELADDR:
DBG("KRT: Received async address notification (%d)\n", h->nlmsg_type); DBG("KRT: Received async address notification (%d)\n", h->nlmsg_type);
if (kif_proto) if (kif_proto)
nl_parse_addr(h, 0); nl_parse_addr(h, 0);
break; break;
case RTM_NEWNEIGH:
case RTM_DELNEIGH:
nl_parse_fdb(&s, h);
default: default:
DBG("KRT: Received unknown async notification (%d)\n", h->nlmsg_type); DBG("KRT: Received unknown async notification (%d)\n", h->nlmsg_type);
} }
@ -1981,7 +2233,7 @@ nl_open_async(void)
bzero(&sa, sizeof(sa)); bzero(&sa, sizeof(sa));
sa.nl_family = AF_NETLINK; sa.nl_family = AF_NETLINK;
sa.nl_groups = RTMGRP_LINK | sa.nl_groups = RTMGRP_LINK | RTMGRP_NEIGH |
RTMGRP_IPV4_IFADDR | RTMGRP_IPV4_ROUTE | RTMGRP_IPV4_IFADDR | RTMGRP_IPV4_ROUTE |
RTMGRP_IPV6_IFADDR | RTMGRP_IPV6_ROUTE; RTMGRP_IPV6_IFADDR | RTMGRP_IPV6_ROUTE;
@ -2038,7 +2290,8 @@ void
krt_sys_io_init(void) krt_sys_io_init(void)
{ {
nl_linpool = lp_new_default(krt_pool); 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
@ -2141,6 +2394,32 @@ krt_sys_get_attr(const eattr *a, byte *buf, int buflen UNUSED)
} }
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 void
kif_sys_start(struct kif_proto *p UNUSED) kif_sys_start(struct kif_proto *p UNUSED)