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:
parent
b5c040d0cf
commit
0c02b06329
@ -312,7 +312,7 @@ if test "$enable_mpls_kernel" != no ; then
|
||||
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'`
|
||||
|
||||
@ -325,6 +325,7 @@ AH_TEMPLATE([CONFIG_BABEL], [Babel protocol])
|
||||
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])
|
||||
|
@ -45,6 +45,7 @@ enum protocol_class {
|
||||
PROTOCOL_BFD,
|
||||
PROTOCOL_BGP,
|
||||
PROTOCOL_BMP,
|
||||
PROTOCOL_BRIDGE,
|
||||
PROTOCOL_DEVICE,
|
||||
PROTOCOL_DIRECT,
|
||||
PROTOCOL_KERNEL,
|
||||
@ -106,7 +107,7 @@ void protos_dump_all(void);
|
||||
|
||||
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;
|
||||
|
||||
/*
|
||||
|
@ -482,7 +482,8 @@ typedef struct rta {
|
||||
#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 */
|
||||
|
@ -78,6 +78,7 @@ const char * const rta_src_names[RTS_MAX] = {
|
||||
[RTS_PERF] = "Perf",
|
||||
[RTS_L3VPN] = "L3VPN",
|
||||
[RTS_AGGREGATED] = "aggregated",
|
||||
[RTS_BRIDGE] = "bridge",
|
||||
};
|
||||
|
||||
const char * rta_dest_names[RTD_MAX] = {
|
||||
@ -1311,7 +1312,7 @@ rta_dump(rta *a)
|
||||
"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" };
|
||||
|
||||
debug("pref=%d uc=%d %s %s%s h=%04x",
|
||||
|
1
proto/bridge/Doc
Normal file
1
proto/bridge/Doc
Normal file
@ -0,0 +1 @@
|
||||
S bridge.c
|
6
proto/bridge/Makefile
Normal file
6
proto/bridge/Makefile
Normal 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
285
proto/bridge/bridge.c
Normal 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
53
proto/bridge/bridge.h
Normal 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
59
proto/bridge/config.Y
Normal 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
|
@ -27,6 +27,8 @@
|
||||
#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)
|
||||
@ -40,6 +42,7 @@ struct nl_parse_state
|
||||
int scan;
|
||||
|
||||
u32 rta_flow;
|
||||
u32 bridge_id;
|
||||
};
|
||||
|
||||
/*
|
||||
@ -210,6 +213,34 @@ nl_request_dump_route(int af, int table_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)
|
||||
@ -444,6 +475,16 @@ static struct nl_want_attrs rtm_attr_want_mpls[BIRD_RTA_MAX] = {
|
||||
#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)
|
||||
{
|
||||
@ -493,6 +534,9 @@ static inline ip_addr rta_get_ipa(struct rtattr *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)
|
||||
{
|
||||
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_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)
|
||||
|
||||
@ -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
|
||||
*/
|
||||
@ -1883,18 +2128,25 @@ nl_async_msg(struct nlmsghdr *h)
|
||||
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);
|
||||
}
|
||||
@ -1981,7 +2233,7 @@ nl_open_async(void)
|
||||
|
||||
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;
|
||||
|
||||
@ -2038,7 +2290,8 @@ void
|
||||
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
|
||||
@ -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
|
||||
kif_sys_start(struct kif_proto *p UNUSED)
|
||||
|
Loading…
Reference in New Issue
Block a user