diff options
author | Ondrej Zajicek (work) <santiago@crfreenet.org> | 2017-12-07 18:35:46 +0100 |
---|---|---|
committer | Ondrej Zajicek (work) <santiago@crfreenet.org> | 2017-12-07 18:35:46 +0100 |
commit | 46434a3cad99260b5a659e5df874eab4615bcb36 (patch) | |
tree | e5f4842fbdcc2ee0c66d6fdb437cb401a7234fb1 | |
parent | 4ff15a75c56531fa2d3858d8250dcef1af4e75b6 (diff) | |
parent | 7b2c5f3d2826e3175bf31b1c36056c9efc587a2b (diff) |
Merge commit '7b2c5f3d2826e3175bf31b1c36056c9efc587a2b' into int-new
-rw-r--r-- | doc/bird.sgml | 13 | ||||
-rw-r--r-- | lib/socket.h | 1 | ||||
-rw-r--r-- | nest/config.Y | 3 | ||||
-rw-r--r-- | nest/iface.c | 24 | ||||
-rw-r--r-- | nest/iface.h | 2 | ||||
-rw-r--r-- | nest/locks.c | 1 | ||||
-rw-r--r-- | nest/locks.h | 1 | ||||
-rw-r--r-- | nest/neighbor.c | 8 | ||||
-rw-r--r-- | nest/proto.c | 7 | ||||
-rw-r--r-- | nest/protocol.h | 2 | ||||
-rw-r--r-- | nest/rt-fib.c | 18 | ||||
-rw-r--r-- | proto/babel/packets.c | 1 | ||||
-rw-r--r-- | proto/bgp/bgp.c | 2 | ||||
-rw-r--r-- | proto/ospf/iface.c | 2 | ||||
-rw-r--r-- | proto/radv/packets.c | 1 | ||||
-rw-r--r-- | proto/radv/radv.c | 1 | ||||
-rw-r--r-- | proto/rip/packets.c | 1 | ||||
-rw-r--r-- | sysdep/linux/netlink.c | 29 | ||||
-rw-r--r-- | sysdep/unix/io.c | 12 |
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 |