summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOndrej Zajicek (work) <santiago@crfreenet.org>2017-12-07 18:35:46 +0100
committerOndrej Zajicek (work) <santiago@crfreenet.org>2017-12-07 18:35:46 +0100
commit46434a3cad99260b5a659e5df874eab4615bcb36 (patch)
treee5f4842fbdcc2ee0c66d6fdb437cb401a7234fb1
parent4ff15a75c56531fa2d3858d8250dcef1af4e75b6 (diff)
parent7b2c5f3d2826e3175bf31b1c36056c9efc587a2b (diff)
Merge commit '7b2c5f3d2826e3175bf31b1c36056c9efc587a2b' into int-new
-rw-r--r--doc/bird.sgml13
-rw-r--r--lib/socket.h1
-rw-r--r--nest/config.Y3
-rw-r--r--nest/iface.c24
-rw-r--r--nest/iface.h2
-rw-r--r--nest/locks.c1
-rw-r--r--nest/locks.h1
-rw-r--r--nest/neighbor.c8
-rw-r--r--nest/proto.c7
-rw-r--r--nest/protocol.h2
-rw-r--r--nest/rt-fib.c18
-rw-r--r--proto/babel/packets.c1
-rw-r--r--proto/bgp/bgp.c2
-rw-r--r--proto/ospf/iface.c2
-rw-r--r--proto/radv/packets.c1
-rw-r--r--proto/radv/radv.c1
-rw-r--r--proto/rip/packets.c1
-rw-r--r--sysdep/linux/netlink.c29
-rw-r--r--sysdep/unix/io.c12
19 files changed, 117 insertions, 12 deletions
diff --git a/doc/bird.sgml b/doc/bird.sgml
index 4ee91206..b51234f7 100644
--- a/doc/bird.sgml
+++ b/doc/bird.sgml
@@ -600,6 +600,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:
@@ -1331,6 +1340,8 @@ foot).
<cf><m/P/.len</cf> returns the length of path <m/P/.
+ <cf><m/P/.empty</cf> resets path <m/P/ to empty path.
+
<cf>prepend(<m/P/,<m/A/)</cf> prepends ASN <m/A/ to path <m/P/ and
returns the result.
@@ -1369,6 +1380,8 @@ foot).
<cf><m/C/.len</cf> returns the length of clist <m/C/.
+ <cf><m/C/.empty</cf> resets clist <m/C/ to empty clist.
+
<cf>add(<m/C/,<m/P/)</cf> adds pair (or quad) <m/P/ to clist <m/C/ and
returns the result. If item <m/P/ is already in clist <m/C/, it does
nothing. <m/P/ may also be a clist, in that case all its members are
diff --git a/lib/socket.h b/lib/socket.h
index d5281b83..e53ec5ba 100644
--- a/lib/socket.h
+++ b/lib/socket.h
@@ -50,6 +50,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 ef29fb96..ad45a39d 100644
--- a/nest/config.Y
+++ b/nest/config.Y
@@ -65,7 +65,7 @@ proto_postconfig(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(IPV4, IPV6, VPN4, VPN6, ROA4, ROA6)
CF_KEYWORDS(RECEIVE, LIMIT, ACTION, WARN, BLOCK, RESTART, DISABLE, KEEP, FILTERED)
CF_KEYWORDS(PASSWORD, FROM, PASSIVE, TO, ID, EVENTS, PACKETS, PROTOCOLS, INTERFACES)
@@ -225,6 +225,7 @@ proto_item:
| MRTDUMP mrtdump_mask { this_proto->mrtdump = $2; }
| ROUTER ID idval { this_proto->router_id = $3; }
| DESCRIPTION text { this_proto->dsc = $2; }
+ | VRF text { this_proto->vrf = if_get_by_name($2); }
;
diff --git a/nest/iface.c b/nest/iface.c
index dbc4debe..5e0a2450 100644
--- a/nest/iface.c
+++ b/nest/iface.c
@@ -120,7 +120,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)
@@ -137,12 +137,16 @@ 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 && (p->proto_state != PS_DOWN))
+ if (p->ifa_notify &&
+ (p->proto_state != PS_DOWN) &&
+ (!p->vrf || p->vrf == a->iface->master))
{
if (p->debug & D_IFACES)
log(L_TRACE "%s < address %N on interface %s %s",
@@ -178,7 +182,9 @@ 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 && (p->proto_state != PS_DOWN))
+ if (p->if_notify &&
+ (p->proto_state != PS_DOWN) &&
+ (!p->vrf || p->vrf == i->master))
{
if (p->debug & D_IFACES)
log(L_TRACE "%s < interface %s %s", p->name, i->name,
@@ -234,7 +240,9 @@ if_notify_change(unsigned c, struct iface *i)
static uint
if_recalc_flags(struct iface *i UNUSED, uint flags)
{
- if ((flags & IF_ADMIN_UP) && !(flags & (IF_SHUTDOWN | IF_TMP_DOWN)))
+ if ((flags & IF_ADMIN_UP) &&
+ !(flags & (IF_SHUTDOWN | IF_TMP_DOWN)) &&
+ !(i->master_index && !i->master))
flags |= IF_UP;
else
flags &= ~IF_UP;
@@ -835,7 +843,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 59b1253c..ab3f8f35 100644
--- a/nest/iface.h
+++ b/nest/iface.h
@@ -34,6 +34,8 @@ struct iface {
unsigned flags;
unsigned mtu;
unsigned index; /* OS-dependent interface index */
+ unsigned master_index; /* Interface index of master iface */
+ struct iface *master; /* Master iface (e.g. for VRF) */
list addrs; /* Addresses assigned to this interface */
struct ifa *addr4; /* Primary address for IPv4 */
struct ifa *addr6; /* Primary address for IPv6 */
diff --git a/nest/locks.c b/nest/locks.c
index 84b8b0ae..86c9ff14 100644
--- a/nest/locks.c
+++ b/nest/locks.c
@@ -45,6 +45,7 @@ olock_same(struct object_lock *x, struct object_lock *y)
return
x->type == y->type &&
x->iface == y->iface &&
+ x->vrf == y->vrf &&
x->port == y->port &&
x->inst == y->inst &&
ipa_equal(x->addr, y->addr);
diff --git a/nest/locks.h b/nest/locks.h
index 3d58c8ed..37026c68 100644
--- a/nest/locks.h
+++ b/nest/locks.h
@@ -30,6 +30,7 @@ struct object_lock {
uint port; /* ... port number */
uint inst; /* ... instance ID */
struct iface *iface; /* ... interface */
+ struct iface *vrf; /* ... or VRF (if iface is unknown) */
void (*hook)(struct object_lock *); /* Called when the lock succeeds */
void *data; /* User data */
/* ... internal to lock manager, don't touch ... */
diff --git a/nest/neighbor.c b/nest/neighbor.c
index 96475a50..fb05d96c 100644
--- a/nest/neighbor.c
+++ b/nest/neighbor.c
@@ -30,7 +30,8 @@
* when the protocol has explicitly requested it via the %NEF_STICKY
* flag because it wishes to be notified when the node will again become
* a neighbor. Such entries are enqueued in a special list which is walked
- * whenever an interface changes its state to up.
+ * whenever an interface changes its state to up. Neighbor entry VRF
+ * association is implied by respective protocol.
*
* When a neighbor event occurs (a neighbor gets disconnected or a sticky
* inactive neighbor becomes connected), the protocol hook neigh_notify()
@@ -153,8 +154,9 @@ neigh_find2(struct proto *p, ip_addr *a, struct iface *ifa, unsigned flags)
}
else
WALK_LIST(i, iface_list)
- if ((scope = if_connected(a, i, &addr)) >= 0)
- {
+ if ((!p->vrf || p->vrf == i->master) &&
+ ((scope = if_connected(a, i, &addr)) >= 0))
+ {
ifa = i;
break;
}
diff --git a/nest/proto.c b/nest/proto.c
index b28ac569..ecc3b0fe 100644
--- a/nest/proto.c
+++ b/nest/proto.c
@@ -13,6 +13,7 @@
#include "lib/resource.h"
#include "lib/lists.h"
#include "lib/event.h"
+#include "lib/timer.h"
#include "lib/string.h"
#include "conf/conf.h"
#include "nest/route.h"
@@ -673,6 +674,7 @@ proto_init(struct proto_config *c, node *n)
p->proto_state = PS_DOWN;
p->last_state_change = current_time();
+ p->vrf = c->vrf;
insert_node(&p->n, n);
p->event = ev_new(proto_pool);
@@ -819,7 +821,8 @@ 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->net_type != oc->net_type) ||
- (nc->disabled != p->disabled))
+ (nc->disabled != p->disabled) ||
+ (nc->vrf != oc->vrf))
return 0;
p->name = nc->name;
@@ -1645,6 +1648,8 @@ proto_cmd_show(struct proto *p, uint verbose, int cnt)
cli_msg(-1006, " Description: %s", p->cf->dsc);
if (p->cf->router_id)
cli_msg(-1006, " Router ID: %R", p->cf->router_id);
+ if (p->vrf)
+ cli_msg(-1006, " VRF: %s", p->vrf->name);
if (p->proto->show_proto_info)
p->proto->show_proto_info(p);
diff --git a/nest/protocol.h b/nest/protocol.h
index e843b0b4..d7e84a44 100644
--- a/nest/protocol.h
+++ b/nest/protocol.h
@@ -100,6 +100,7 @@ struct proto_config {
u32 router_id; /* Protocol specific router ID */
list channels; /* List of channel configs (struct channel_config) */
+ struct iface *vrf; /* Related VRF instance, NULL if global */
/* Check proto_reconfigure() and proto_copy_config() after changing struct proto_config */
@@ -142,6 +143,7 @@ struct proto {
list channels; /* List of channels to rtables (struct channel) */
struct channel *main_channel; /* Primary channel */
struct rte_src *main_source; /* Primary route source */
+ struct iface *vrf; /* Related VRF instance, NULL if global */
char *name; /* Name of this instance (== cf->name) */
u32 debug; /* Debugging flags */
diff --git a/nest/rt-fib.c b/nest/rt-fib.c
index 11c31d0d..45f47261 100644
--- a/nest/rt-fib.c
+++ b/nest/rt-fib.c
@@ -32,6 +32,24 @@
* Basic FIB operations are performed by functions defined by this module,
* enumerating of FIB contents is accomplished by using the FIB_WALK() macro
* or FIB_ITERATE_START() if you want to do it asynchronously.
+ *
+ * For simple iteration just place the body of the loop between FIB_WALK() and
+ * FIB_WALK_END(). You can't modify the FIB during the iteration (you can modify
+ * data in the node, but not add or remove nodes).
+ *
+ * If you need more freedom, you can use the FIB_ITERATE_*() group of macros.
+ * First, you initialize an iterator with FIB_ITERATE_INIT(). Then you can put
+ * the loop body in between FIB_ITERATE_START() and FIB_ITERATE_END(). In
+ * addition, the iteration can be suspended by calling FIB_ITERATE_PUT().
+ * This'll link the iterator inside the FIB. While suspended, you may modify the
+ * FIB, exit the current function, etc. To resume the iteration, enter the loop
+ * again. You can use FIB_ITERATE_UNLINK() to unlink the iterator (while
+ * iteration is suspended) in cases like premature end of FIB iteration.
+ *
+ * Note that the iterator must not be destroyed when the iteration is suspended,
+ * the FIB would then contain a pointer to invalid memory. Therefore, after each
+ * FIB_ITERATE_INIT() or FIB_ITERATE_PUT() there must be either
+ * FIB_ITERATE_START() or FIB_ITERATE_UNLINK() before the iterator is destroyed.
*/
#undef LOCAL_DEBUG
diff --git a/proto/babel/packets.c b/proto/babel/packets.c
index 4abcf7e4..5b356fae 100644
--- a/proto/babel/packets.c
+++ b/proto/babel/packets.c
@@ -1332,6 +1332,7 @@ babel_open_socket(struct babel_iface *ifa)
sk->dport = ifa->cf->port;
sk->iface = ifa->iface;
sk->saddr = ifa->addr;
+ 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 f4791215..b0814791 100644
--- a/proto/bgp/bgp.c
+++ b/proto/bgp/bgp.c
@@ -939,6 +939,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;
@@ -1331,6 +1332,7 @@ bgp_start(struct proto *P)
lock->addr = p->cf->remote_ip;
lock->port = p->cf->remote_port;
lock->iface = p->cf->iface;
+ lock->vrf = p->cf->iface ? NULL : p->p.vrf;
lock->type = OBJLOCK_TCP;
lock->hook = bgp_start_locked;
lock->data = p;
diff --git a/proto/ospf/iface.c b/proto/ospf/iface.c
index 29d21a07..e3d8d61b 100644
--- a/proto/ospf/iface.c
+++ b/proto/ospf/iface.c
@@ -121,6 +121,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;
@@ -204,6 +205,7 @@ ospf_open_vlink_sk(struct ospf_proto *p)
sk->type = SK_IP;
sk->subtype = ospf_is_v2(p) ? SK_IPV4 : SK_IPV6;
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 e07296e1..7c148b7d 100644
--- a/proto/radv/packets.c
+++ b/proto/radv/packets.c
@@ -387,6 +387,7 @@ radv_sk_open(struct radv_iface *ifa)
sk->subtype = SK_IPV6;
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/radv/radv.c b/proto/radv/radv.c
index c96d7724..e9140115 100644
--- a/proto/radv/radv.c
+++ b/proto/radv/radv.c
@@ -297,7 +297,6 @@ radv_iface_new(struct radv_proto *p, struct iface *iface, struct radv_iface_conf
ifa->timer = tm_new_init(pool, radv_timer, ifa, 0, 0);
struct object_lock *lock = olock_new(pool);
- lock->addr = IPA_NONE;
lock->type = OBJLOCK_IP;
lock->port = ICMPV6_PROTO;
lock->iface = iface;
diff --git a/proto/rip/packets.c b/proto/rip/packets.c
index 4925ca36..891f454f 100644
--- a/proto/rip/packets.c
+++ b/proto/rip/packets.c
@@ -747,6 +747,7 @@ rip_open_socket(struct rip_iface *ifa)
sk->dport = ifa->cf->port;
sk->iface = ifa->iface;
sk->saddr = rip_is_v2(p) ? ifa->iface->addr4->ip : ifa->iface->llv6->ip;
+ sk->vrf = p->p.vrf;
sk->rx_hook = rip_rx_hook;
sk->tx_hook = rip_tx_hook;
diff --git a/sysdep/linux/netlink.c b/sysdep/linux/netlink.c
index 279f3c9c..05c1fa8c 100644
--- a/sysdep/linux/netlink.c
+++ b/sysdep/linux/netlink.c
@@ -324,6 +324,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 },
};
@@ -790,7 +791,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)))
@@ -813,6 +814,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)
{
@@ -832,6 +836,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;
@@ -1091,6 +1098,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(AF_INET, 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 7492e031..cd2558b2 100644
--- a/sysdep/unix/io.c
+++ b/sysdep/unix/io.c
@@ -931,6 +931,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