0
0
mirror of https://gitlab.nic.cz/labs/bird.git synced 2024-12-24 02:31:55 +00:00
bird/proto/l3vpn/l3vpn.c
Jan Moskyto Matejka e1b7c6177a L3VPN: new protocol
2016-04-07 10:08:43 +02:00

193 lines
4.7 KiB
C

#include "l3vpn.h"
static void
l3vpn_alloc_mpls_label(struct l3vpn_proto *p, struct l3vpn_ip_to_mpls *litm, ip_addr gw, struct iface *iface)
{
u32 label;
net *n;
for (label = p->last_label + 1; label <= MPLS_LABEL_MAX; label++) {
net_addr_union nu = { .mpls = NET_ADDR_MPLS(label) };
n = net_get(p->mpls->table, &nu.n);
if (!n->routes)
goto have_label;
}
for (label = 16; label <= p->last_label; label++) {
net_addr_union nu = { .mpls = NET_ADDR_MPLS(label) };
n = net_get(p->mpls->table, &nu.n);
if (!n->routes)
goto have_label;
}
return;
have_label:;
p->last_label = label;
rta a = {};
a.gw = gw;
a.src = p->p.main_source;
a.iface = iface;
rte *e = rte_get_temp(rta_lookup(&a));
e->net = n;
rte_update2(p->mpls, n, e, p->p.main_source);
litm->ad.length = sizeof(u32);
memcpy(litm->ad.data, &label, sizeof(u32));
}
static ea_list *
l3vpn_get_mpls_label_ea(struct l3vpn_proto *p, ip_addr gw, struct iface *iface)
{
net_addr_union nu;
net_fill_ip_host(&nu.n, gw);
struct l3vpn_ip_to_mpls *litm = fib_get(&p->iptompls, &nu.n);
if (!litm->ad.length)
l3vpn_alloc_mpls_label(p, litm, gw, iface);
if (!litm->ad.length)
return NULL;
return &litm->el;
}
static void
l3vpn_iptompls_init(void *ptr)
{
struct l3vpn_ip_to_mpls *litm = ptr;
litm->el.count = 1;
litm->ea.id = EA_GEN_MPLS_STACK;
litm->ea.type = EAF_TYPE_INT_SET;
litm->ea.u.ptr = &litm->ad;
}
static void
l3vpn_rt_notify(struct proto *P, struct channel *ch, net *n, rte *new, rte *old, ea_list *ea)
{
struct l3vpn_proto *p = (struct l3vpn_proto *) P;
if (!new && !old)
return;
if (ch == p->mpls) {
TRACE(D_EVENTS, "Ignoring MPLS route to %N", &n->n.addr[0]);
return;
}
if (new && ch == new->sender) {
TRACE(D_EVENTS, "Ignoring back-bounced route to %N", &n->n.addr[0]);
return;
}
net_addr_union new_dst = { .n = n->n.addr[0] };
if (ch == p->vpn) {
switch (new_dst.n.type) {
case NET_VPN4:
if (new_dst.vpn4.rd != p->rd) {
TRACE(D_EVENTS, "Ignoring route to %N with alien RD", &new_dst);
return; /* Ignoring routes with alien RD */
}
net_fill_ip4(&new_dst.n, net4_prefix(&new_dst.n), net4_pxlen(&new_dst.n));
break;
case NET_VPN6:
if (new_dst.vpn6.rd != p->rd) {
TRACE(D_EVENTS, "Ignoring route to %N with alien RD", &new_dst);
return; /* Ignoring routes with alien RD */
}
new_dst.vpn6.type = NET_IP6;
net_fill_ip6(&new_dst.n, net6_prefix(&new_dst.n), net6_pxlen(&new_dst.n));
break;
default:
ASSERT(0);
}
TRACE(D_EVENTS, "Converted VPN route %N to IP route %N", &n->n.addr[0], &new_dst);
}
if (ch == p->ip) {
switch (new_dst.n.type) {
case NET_IP4:
net_fill_vpn4(&new_dst.n, net4_prefix(&new_dst.n), net4_pxlen(&new_dst.n), p->rd);
break;
case NET_IP6:
net_fill_vpn6(&new_dst.n, net6_prefix(&new_dst.n), net6_pxlen(&new_dst.n), p->rd);
break;
default:
ASSERT(0);
}
TRACE(D_EVENTS, "Converted IP route %N to VPN route %N", &n->n.addr[0], &new_dst);
}
rte *e = NULL;
if (new) {
rta a;
memcpy(&a, new->attrs, sizeof(rta));
a.hostentry = NULL;
ea_list *mpls_ea = l3vpn_get_mpls_label_ea(p, a.gw, a.iface);
a.eattrs = mpls_ea;
mpls_ea->next = ea;
e = rte_get_temp(rta_lookup(&a));
e->pflags = 0;
/* Copy protocol specific embedded attributes. */
memcpy(&(e->u), &(new->u), sizeof(e->u));
e->pref = new->pref;
e->pflags = new->pflags;
/* FIXME: Add also VPN's MPLS label if ch == p->ip */
}
struct rte_src *src = (new ? new->attrs->src : old->attrs->src);
if (ch == p->ip) {
net *nn = net_get(p->vpn->table, &new_dst.n);
if (e) e->net = nn;
rte_update2(p->vpn, nn, e, src);
} else {
net *nn = net_get(p->ip->table, &new_dst.n);
if (e) e->net = nn;
rte_update2(p->ip, nn, e, src);
}
}
static struct proto *
l3vpn_init(struct proto_config *CF)
{
struct l3vpn_config *cf = (struct l3vpn_config *) CF;
struct proto *P = proto_new(CF);
struct l3vpn_proto *p = (struct l3vpn_proto *) P;
p->vpn = proto_add_channel(P, cf->vpn);
p->ip = proto_add_channel(P, cf->ip);
p->mpls = proto_add_channel(P, cf->mpls);
p->rd = cf->rd;
p->last_label = 16;
P->rt_notify = l3vpn_rt_notify;
return P;
}
static int
l3vpn_start(struct proto *P)
{
struct l3vpn_proto *p = (struct l3vpn_proto *) P;
fib_init(&p->iptompls, P->pool, p->ip->net_type, sizeof(struct l3vpn_ip_to_mpls),
OFFSETOF(struct l3vpn_ip_to_mpls, n), 0, l3vpn_iptompls_init);
return PS_UP;
}
struct protocol proto_l3vpn = {
.name = "L3VPN",
.template = "l3vpn%d",
.proto_size = sizeof(struct l3vpn_proto),
.config_size = sizeof(struct l3vpn_config),
.channel_mask = NB_IP4 | NB_IP6 | NB_VPN4 | NB_VPN6 | NB_MPLS,
.init = l3vpn_init,
.start = l3vpn_start,
};