summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/bird.sgml9
-rw-r--r--lib/socket.h1
-rw-r--r--nest/config.Y3
-rw-r--r--nest/iface.c19
-rw-r--r--nest/iface.h2
-rw-r--r--nest/proto.c6
-rw-r--r--nest/protocol.h2
-rw-r--r--proto/babel/packets.c1
-rw-r--r--proto/bgp/bgp.c1
-rw-r--r--proto/ospf/iface.c2
-rw-r--r--proto/radv/packets.c1
-rw-r--r--proto/rip/packets.c1
-rw-r--r--sysdep/linux/netlink.c29
-rw-r--r--sysdep/unix/io.c12
14 files changed, 81 insertions, 8 deletions
diff --git a/doc/bird.sgml b/doc/bird.sgml
index 516f4c6e..03d37f46 100644
--- a/doc/bird.sgml
+++ b/doc/bird.sgml
@@ -598,6 +598,15 @@ agreement").
<tag><label id="proto-table">table <m/name/</tag>
Connect this protocol to a non-default routing table.
+
+ <tag><label id="proto-vrf">vrf "<m/text/"</tag>
+ Associate the protocol with specific VRF. The protocol will be
+ restricted to interfaces assigned to the VRF and will use sockets bound
+ to the VRF. Appropriate VRF interface must exist on OS level. For kernel
+ protocol, an appropriate table still must be explicitly selected by
+ <cf/table/ option. Note that the VRF support in BIRD and Linux kernel
+ (4.11) is still in development and is currently problematic outside of
+ multihop BGP.
</descrip>
<p>There are several options that give sense only with certain protocols:
diff --git a/lib/socket.h b/lib/socket.h
index 2da52127..0769489b 100644
--- a/lib/socket.h
+++ b/lib/socket.h
@@ -26,6 +26,7 @@ typedef struct birdsock {
int ttl; /* Time To Live, -1 = default */
u32 flags;
struct iface *iface; /* Interface; specify this for broad/multicast sockets */
+ struct iface *vrf; /* Related VRF instance, NULL if global */
byte *rbuf, *rpos; /* NULL=allocate automatically */
uint fast_rx; /* RX has higher priority in event loop */
diff --git a/nest/config.Y b/nest/config.Y
index 878224fe..025e8969 100644
--- a/nest/config.Y
+++ b/nest/config.Y
@@ -55,7 +55,7 @@ get_passwords(void)
CF_DECLS
CF_KEYWORDS(ROUTER, ID, PROTOCOL, TEMPLATE, PREFERENCE, DISABLED, DEBUG, ALL, OFF, DIRECT)
-CF_KEYWORDS(INTERFACE, IMPORT, EXPORT, FILTER, NONE, TABLE, STATES, ROUTES, FILTERS)
+CF_KEYWORDS(INTERFACE, IMPORT, EXPORT, FILTER, NONE, VRF, TABLE, STATES, ROUTES, FILTERS)
CF_KEYWORDS(RECEIVE, LIMIT, ACTION, WARN, BLOCK, RESTART, DISABLE, KEEP, FILTERED)
CF_KEYWORDS(PASSWORD, FROM, PASSIVE, TO, ID, EVENTS, PACKETS, PROTOCOLS, INTERFACES)
CF_KEYWORDS(ALGORITHM, KEYED, HMAC, MD5, SHA1, SHA256, SHA384, SHA512)
@@ -227,6 +227,7 @@ proto_item:
| IMPORT LIMIT limit_spec { this_proto->in_limit = $3; }
| EXPORT LIMIT limit_spec { this_proto->out_limit = $3; }
| IMPORT KEEP FILTERED bool { this_proto->in_keep_filtered = $4; }
+ | VRF TEXT { this_proto->vrf = if_get_by_name($2); }
| TABLE rtable { this_proto->table = $2; }
| ROUTER ID idval { this_proto->router_id = $3; }
| DESCRIPTION text { this_proto->dsc = $2; }
diff --git a/nest/iface.c b/nest/iface.c
index ff362938..3dd45065 100644
--- a/nest/iface.c
+++ b/nest/iface.c
@@ -116,7 +116,7 @@ if_what_changed(struct iface *i, struct iface *j)
unsigned c;
if (((i->flags ^ j->flags) & ~(IF_UP | IF_SHUTDOWN | IF_UPDATED | IF_ADMIN_UP | IF_LINK_UP | IF_TMP_DOWN | IF_JUST_CREATED))
- || i->index != j->index)
+ || (i->index != j->index) || (i->master != j->master))
return IF_CHANGE_TOO_MUCH;
c = 0;
if ((i->flags ^ j->flags) & IF_UP)
@@ -133,12 +133,14 @@ if_copy(struct iface *to, struct iface *from)
{
to->flags = from->flags | (to->flags & IF_TMP_DOWN);
to->mtu = from->mtu;
+ to->master_index = from->master_index;
+ to->master = from->master;
}
static inline void
ifa_send_notify(struct proto *p, unsigned c, struct ifa *a)
{
- if (p->ifa_notify)
+ if (p->ifa_notify && (!p->vrf || p->vrf == a->iface->master))
{
if (p->debug & D_IFACES)
log(L_TRACE "%s <%s address %I/%d on interface %s %s",
@@ -175,7 +177,7 @@ ifa_notify_change(unsigned c, struct ifa *a)
static inline void
if_send_notify(struct proto *p, unsigned c, struct iface *i)
{
- if (p->if_notify)
+ if (p->if_notify && (!p->vrf || p->vrf == i->master))
{
if (p->debug & D_IFACES)
log(L_TRACE "%s < interface %s %s", p->name, i->name,
@@ -238,7 +240,8 @@ if_recalc_flags(struct iface *i, unsigned flags)
{
if ((flags & (IF_SHUTDOWN | IF_TMP_DOWN)) ||
!(flags & IF_ADMIN_UP) ||
- !i->addr)
+ !i->addr ||
+ (i->master_index && !i->master))
flags &= ~IF_UP;
else
flags |= IF_UP;
@@ -771,7 +774,13 @@ if_show(void)
if (i->flags & IF_SHUTDOWN)
continue;
- cli_msg(-1001, "%s %s (index=%d)", i->name, (i->flags & IF_UP) ? "up" : "DOWN", i->index);
+ char mbuf[16 + sizeof(i->name)] = {};
+ if (i->master)
+ bsprintf(mbuf, " master=%s", i->master->name);
+ else if (i->master_index)
+ bsprintf(mbuf, " master=#%u", i->master_index);
+
+ cli_msg(-1001, "%s %s (index=%d%s)", i->name, (i->flags & IF_UP) ? "up" : "DOWN", i->index, mbuf);
if (!(i->flags & IF_MULTIACCESS))
type = "PtP";
else
diff --git a/nest/iface.h b/nest/iface.h
index 56710e4a..b8e69838 100644
--- a/nest/iface.h
+++ b/nest/iface.h
@@ -34,8 +34,10 @@ struct iface {
unsigned flags;
unsigned mtu;
unsigned index; /* OS-dependent interface index */
+ unsigned master_index; /* Interface index of master iface */
list addrs; /* Addresses assigned to this interface */
struct ifa *addr; /* Primary address */
+ struct iface *master; /* Master iface (e.g. for VRF) */
list neighbors; /* All neighbors on this interface */
};
diff --git a/nest/proto.c b/nest/proto.c
index 1091b321..64422ee3 100644
--- a/nest/proto.c
+++ b/nest/proto.c
@@ -386,6 +386,7 @@ proto_init(struct proto_config *c)
q->core_state = FS_HUNGRY;
q->export_state = ES_DOWN;
q->last_state_change = now;
+ q->vrf = c->vrf;
add_tail(&initial_proto_list, &q->n);
@@ -409,6 +410,7 @@ proto_reconfigure(struct proto *p, struct proto_config *oc, struct proto_config
/* If there is a too big change in core attributes, ... */
if ((nc->protocol != oc->protocol) ||
(nc->disabled != p->disabled) ||
+ (nc->vrf != oc->vrf) ||
(nc->table->table != oc->table->table))
return 0;
@@ -1474,7 +1476,9 @@ proto_show_limit(struct proto_limit *l, const char *dsc)
void
proto_show_basic_info(struct proto *p)
{
- // cli_msg(-1006, " Table: %s", p->table->name);
+ if (p->vrf)
+ cli_msg(-1006, " VRF: %s", p->vrf->name);
+
cli_msg(-1006, " Preference: %d", p->preference);
cli_msg(-1006, " Input filter: %s", filter_name(p->cf->in_filter));
cli_msg(-1006, " Output filter: %s", filter_name(p->cf->out_filter));
diff --git a/nest/protocol.h b/nest/protocol.h
index ec787355..3c30a581 100644
--- a/nest/protocol.h
+++ b/nest/protocol.h
@@ -94,6 +94,7 @@ struct proto_config {
unsigned preference, disabled; /* Generic parameters */
int in_keep_filtered; /* Routes rejected in import filter are kept */
u32 router_id; /* Protocol specific router ID */
+ struct iface *vrf; /* Related VRF instance, NULL if global */
struct rtable_config *table; /* Table we're attached to */
struct filter *in_filter, *out_filter; /* Attached filters */
struct proto_limit *rx_limit; /* Limit for receiving routes from protocol
@@ -213,6 +214,7 @@ struct proto {
void (*rte_insert)(struct network *, struct rte *);
void (*rte_remove)(struct network *, struct rte *);
+ struct iface *vrf; /* Related VRF instance, NULL if global */
struct rtable *table; /* Our primary routing table */
struct rte_src *main_source; /* Primary route source */
struct announce_hook *main_ahook; /* Primary announcement hook */
diff --git a/proto/babel/packets.c b/proto/babel/packets.c
index 08054832..90421836 100644
--- a/proto/babel/packets.c
+++ b/proto/babel/packets.c
@@ -1080,6 +1080,7 @@ babel_open_socket(struct babel_iface *ifa)
sk->sport = ifa->cf->port;
sk->dport = ifa->cf->port;
sk->iface = ifa->iface;
+ sk->vrf = p->p.vrf;
sk->rx_hook = babel_rx_hook;
sk->tx_hook = babel_tx_hook;
diff --git a/proto/bgp/bgp.c b/proto/bgp/bgp.c
index f706e76e..913685a4 100644
--- a/proto/bgp/bgp.c
+++ b/proto/bgp/bgp.c
@@ -745,6 +745,7 @@ bgp_connect(struct bgp_proto *p) /* Enter Connect state and start establishing c
s->daddr = p->cf->remote_ip;
s->dport = p->cf->remote_port;
s->iface = p->neigh ? p->neigh->iface : NULL;
+ s->vrf = p->p.vrf;
s->ttl = p->cf->ttl_security ? 255 : hops;
s->rbsize = p->cf->enable_extended_messages ? BGP_RX_BUFFER_EXT_SIZE : BGP_RX_BUFFER_SIZE;
s->tbsize = p->cf->enable_extended_messages ? BGP_TX_BUFFER_EXT_SIZE : BGP_TX_BUFFER_SIZE;
diff --git a/proto/ospf/iface.c b/proto/ospf/iface.c
index 280fa4c1..1795ec22 100644
--- a/proto/ospf/iface.c
+++ b/proto/ospf/iface.c
@@ -118,6 +118,7 @@ ospf_sk_open(struct ospf_iface *ifa)
sk->dport = OSPF_PROTO;
sk->saddr = ifa->addr->ip;
sk->iface = ifa->iface;
+ sk->vrf = p->p.vrf;
sk->tos = ifa->cf->tx_tos;
sk->priority = ifa->cf->tx_priority;
@@ -200,6 +201,7 @@ ospf_open_vlink_sk(struct ospf_proto *p)
sock *sk = sk_new(p->p.pool);
sk->type = SK_IP;
sk->dport = OSPF_PROTO;
+ sk->vrf = p->p.vrf;
/* FIXME: configurable tos/priority ? */
sk->tos = IP_PREC_INTERNET_CONTROL;
diff --git a/proto/radv/packets.c b/proto/radv/packets.c
index 19d71f97..8a301854 100644
--- a/proto/radv/packets.c
+++ b/proto/radv/packets.c
@@ -388,6 +388,7 @@ radv_sk_open(struct radv_iface *ifa)
sk->type = SK_IP;
sk->dport = ICMPV6_PROTO;
sk->saddr = ifa->addr->ip;
+ sk->vrf = ifa->ra->p.vrf;
sk->ttl = 255; /* Mandatory for Neighbor Discovery packets */
sk->rx_hook = radv_rx_hook;
diff --git a/proto/rip/packets.c b/proto/rip/packets.c
index 468927e6..722a9012 100644
--- a/proto/rip/packets.c
+++ b/proto/rip/packets.c
@@ -739,6 +739,7 @@ rip_open_socket(struct rip_iface *ifa)
sk->sport = ifa->cf->port;
sk->dport = ifa->cf->port;
sk->iface = ifa->iface;
+ sk->vrf = p->p.vrf;
/*
* For RIPv2, we explicitly choose a primary address, mainly to ensure that
diff --git a/sysdep/linux/netlink.c b/sysdep/linux/netlink.c
index 3658c46b..4802897b 100644
--- a/sysdep/linux/netlink.c
+++ b/sysdep/linux/netlink.c
@@ -300,6 +300,7 @@ struct nl_want_attrs {
static struct nl_want_attrs ifla_attr_want[BIRD_IFLA_MAX] = {
[IFLA_IFNAME] = { 1, 0, 0 },
[IFLA_MTU] = { 1, 1, sizeof(u32) },
+ [IFLA_MASTER] = { 1, 1, sizeof(u32) },
[IFLA_WIRELESS] = { 1, 0, 0 },
};
@@ -618,7 +619,7 @@ nl_parse_link(struct nlmsghdr *h, int scan)
struct iface f = {};
struct iface *ifi;
char *name;
- u32 mtu;
+ u32 mtu, master = 0;
uint fl;
if (!(i = nl_checkin(h, sizeof(*i))) || !nl_parse_attrs(IFLA_RTA(i), ifla_attr_want, a, sizeof(a)))
@@ -641,6 +642,9 @@ nl_parse_link(struct nlmsghdr *h, int scan)
name = RTA_DATA(a[IFLA_IFNAME]);
mtu = rta_get_u32(a[IFLA_MTU]);
+ if (a[IFLA_MASTER])
+ master = rta_get_u32(a[IFLA_MASTER]);
+
ifi = if_find_by_index(i->ifi_index);
if (!new)
{
@@ -660,6 +664,9 @@ nl_parse_link(struct nlmsghdr *h, int scan)
f.index = i->ifi_index;
f.mtu = mtu;
+ f.master_index = master;
+ f.master = if_find_by_index(master);
+
fl = i->ifi_flags;
if (fl & IFF_UP)
f.flags |= IF_ADMIN_UP;
@@ -835,6 +842,26 @@ kif_do_scan(struct kif_proto *p UNUSED)
else
log(L_DEBUG "nl_scan_ifaces: Unknown packet received (type=%d)", h->nlmsg_type);
+ /* Re-resolve master interface for slaves */
+ struct iface *i;
+ WALK_LIST(i, iface_list)
+ if (i->master_index)
+ {
+ struct iface f = {
+ .flags = i->flags,
+ .mtu = i->mtu,
+ .index = i->index,
+ .master_index = i->master_index,
+ .master = if_find_by_index(i->master_index)
+ };
+
+ if (f.master != i->master)
+ {
+ memcpy(f.name, i->name, sizeof(f.name));
+ if_update(&f);
+ }
+ }
+
nl_request_dump(BIRD_AF, RTM_GETADDR);
while (h = nl_get_scan())
if (h->nlmsg_type == RTM_NEWADDR || h->nlmsg_type == RTM_DELADDR)
diff --git a/sysdep/unix/io.c b/sysdep/unix/io.c
index 561d4dea..6d6a0990 100644
--- a/sysdep/unix/io.c
+++ b/sysdep/unix/io.c
@@ -1211,6 +1211,18 @@ sk_setup(sock *s)
}
#endif
+ if (s->vrf && !s->iface)
+ {
+ /* Bind socket to associated VRF interface.
+ This is Linux-specific, but so is SO_BINDTODEVICE. */
+#ifdef SO_BINDTODEVICE
+ struct ifreq ifr = {};
+ strcpy(ifr.ifr_name, s->vrf->name);
+ if (setsockopt(s->fd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr)) < 0)
+ ERR("SO_BINDTODEVICE");
+#endif
+ }
+
if (s->iface)
{
#ifdef SO_BINDTODEVICE