<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:
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 */
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)
| 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; }
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)
{
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",
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,
{
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;
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
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 */
};
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);
/* 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;
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));
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
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 */
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;
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;
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;
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;
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;
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
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 },
};
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)))
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)
{
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;
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)
}
#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