mirror of
https://gitlab.nic.cz/labs/bird.git
synced 2025-01-03 07:31:54 +00:00
L3VPN: new protocol
This commit is contained in:
parent
ec11f99243
commit
e1b7c6177a
@ -176,7 +176,7 @@ fi
|
|||||||
AC_SUBST(iproutedir)
|
AC_SUBST(iproutedir)
|
||||||
|
|
||||||
# all_protocols="$proto_bfd bgp ospf pipe radv rip static"
|
# all_protocols="$proto_bfd bgp ospf pipe radv rip static"
|
||||||
all_protocols="$proto_bfd ospf pipe radv rip static"
|
all_protocols="$proto_bfd l3vpn ospf pipe radv rip static"
|
||||||
|
|
||||||
all_protocols=`echo $all_protocols | sed 's/ /,/g'`
|
all_protocols=`echo $all_protocols | sed 's/ /,/g'`
|
||||||
|
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
|
|
||||||
#define MPLS_STACK_LENGTH 8 /* Adjust this if you need deeper MPLS stack */
|
#define MPLS_STACK_LENGTH 8 /* Adjust this if you need deeper MPLS stack */
|
||||||
#define MPLS_PXLEN 20 /* Length of the label in bits. Constant. */
|
#define MPLS_PXLEN 20 /* Length of the label in bits. Constant. */
|
||||||
|
#define MPLS_LABEL_MAX ((1<<MPLS_PXLEN)-1) /* Maximal possible label value. */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* RFC 3032 updated by RFC 5462:
|
* RFC 3032 updated by RFC 5462:
|
||||||
|
@ -1262,6 +1262,9 @@ protos_build(void)
|
|||||||
proto_build(&proto_bfd);
|
proto_build(&proto_bfd);
|
||||||
bfd_init_all();
|
bfd_init_all();
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef CONFIG_L3VPN
|
||||||
|
proto_build(&proto_l3vpn);
|
||||||
|
#endif
|
||||||
|
|
||||||
proto_pool = rp_new(&root_pool, "Protocols");
|
proto_pool = rp_new(&root_pool, "Protocols");
|
||||||
proto_shutdown_timer = tm_new(proto_pool);
|
proto_shutdown_timer = tm_new(proto_pool);
|
||||||
|
@ -81,7 +81,7 @@ void protos_dump_all(void);
|
|||||||
|
|
||||||
extern struct protocol
|
extern struct protocol
|
||||||
proto_device, proto_radv, proto_rip, proto_static,
|
proto_device, proto_radv, proto_rip, proto_static,
|
||||||
proto_ospf, proto_pipe, proto_bgp, proto_bfd;
|
proto_ospf, proto_pipe, proto_l3vpn, proto_bgp, proto_bfd;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Routing Protocol Instance
|
* Routing Protocol Instance
|
||||||
|
@ -675,7 +675,7 @@ get_generic_attr(eattr *a, byte **buf, int buflen UNUSED)
|
|||||||
{
|
{
|
||||||
*buf += bsprintf(*buf, "mpls");
|
*buf += bsprintf(*buf, "mpls");
|
||||||
struct adata *ad = a->u.ptr;
|
struct adata *ad = a->u.ptr;
|
||||||
u32 *z = ad->data;
|
u32 *z = (void *) ad->data;
|
||||||
int len = ad->length / sizeof(u32);
|
int len = ad->length / sizeof(u32);
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
1
proto/l3vpn/Doc
Normal file
1
proto/l3vpn/Doc
Normal file
@ -0,0 +1 @@
|
|||||||
|
S l3vpn.c
|
5
proto/l3vpn/Makefile
Normal file
5
proto/l3vpn/Makefile
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
source=l3vpn.c
|
||||||
|
root-rel=../../
|
||||||
|
dir-name=proto/l3vpn
|
||||||
|
|
||||||
|
include ../../Rules
|
80
proto/l3vpn/config.Y
Normal file
80
proto/l3vpn/config.Y
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
/*
|
||||||
|
* BIRD -- Layer3 VPN Protocol Configuration
|
||||||
|
*
|
||||||
|
* (c) 2011-2013 Yandex, LLC
|
||||||
|
* Author: Alexander V. Chernikov <melifaro@yandex-team.ru>
|
||||||
|
*
|
||||||
|
* Can be freely distributed and used under the terms of the GNU GPL.
|
||||||
|
*/
|
||||||
|
|
||||||
|
CF_HDR
|
||||||
|
|
||||||
|
#include "proto/l3vpn/l3vpn.h"
|
||||||
|
|
||||||
|
CF_DEFINES
|
||||||
|
|
||||||
|
#define L3VPN_CFG ((struct l3vpn_config *) this_proto)
|
||||||
|
|
||||||
|
CF_DECLS
|
||||||
|
|
||||||
|
CF_KEYWORDS(L3VPN, RD, LABEL, AUTO)
|
||||||
|
|
||||||
|
CF_GRAMMAR
|
||||||
|
|
||||||
|
CF_ADDTO(proto, l3vpn_proto l3vpn_proto_end '}' )
|
||||||
|
|
||||||
|
l3vpn_proto_start: proto_start L3VPN {
|
||||||
|
this_proto = proto_config_new(&proto_l3vpn, $1);
|
||||||
|
};
|
||||||
|
|
||||||
|
l3vpn_proto:
|
||||||
|
l3vpn_proto_start proto_name '{'
|
||||||
|
| l3vpn_proto proto_item ';'
|
||||||
|
| l3vpn_proto proto_channel ';' {
|
||||||
|
switch (((struct channel_config *)$2)->net_type) {
|
||||||
|
case NET_IP4:
|
||||||
|
case NET_IP6:
|
||||||
|
if (L3VPN_CFG->ip)
|
||||||
|
cf_error("Multiple IP channels not supported");
|
||||||
|
if (L3VPN_CFG->vpn &&
|
||||||
|
((L3VPN_CFG->vpn->net_type == NET_VPN4 && ((struct channel_config *)$2)->net_type == NET_IP6)
|
||||||
|
|| (L3VPN_CFG->vpn->net_type == NET_VPN6 && ((struct channel_config *)$2)->net_type == NET_IP4)))
|
||||||
|
cf_error("Mismatched IP/VPN families");
|
||||||
|
|
||||||
|
L3VPN_CFG->ip = $2;
|
||||||
|
break;
|
||||||
|
case NET_VPN4:
|
||||||
|
case NET_VPN6:
|
||||||
|
if (L3VPN_CFG->vpn)
|
||||||
|
cf_error("Multiple VPN channels not supported");
|
||||||
|
|
||||||
|
if (L3VPN_CFG->ip &&
|
||||||
|
((L3VPN_CFG->ip->net_type == NET_IP4 && ((struct channel_config *)$2)->net_type == NET_VPN6)
|
||||||
|
|| (L3VPN_CFG->ip->net_type == NET_IP6 && ((struct channel_config *)$2)->net_type == NET_VPN4)))
|
||||||
|
cf_error("Mismatched IP/VPN families");
|
||||||
|
|
||||||
|
L3VPN_CFG->vpn = $2;
|
||||||
|
break;
|
||||||
|
case NET_MPLS:
|
||||||
|
if (L3VPN_CFG->mpls)
|
||||||
|
cf_error("Multiple MPLS channels not supported");
|
||||||
|
else
|
||||||
|
L3VPN_CFG->mpls = $2;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
cf_error("Unsupported channel type");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
| l3vpn_proto RD VPN_RD ';' { L3VPN_CFG->rd = $3; }
|
||||||
|
;
|
||||||
|
|
||||||
|
l3vpn_proto_end:
|
||||||
|
{
|
||||||
|
if ((!L3VPN_CFG->ip) || (!L3VPN_CFG->vpn) || (!L3VPN_CFG->mpls))
|
||||||
|
cf_error("Need all IP, VPN and MPLS channels configured.");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
CF_CODE
|
||||||
|
|
||||||
|
CF_END
|
192
proto/l3vpn/l3vpn.c
Normal file
192
proto/l3vpn/l3vpn.c
Normal file
@ -0,0 +1,192 @@
|
|||||||
|
#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,
|
||||||
|
};
|
49
proto/l3vpn/l3vpn.h
Normal file
49
proto/l3vpn/l3vpn.h
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
/*
|
||||||
|
* BIRD -- Layer3 VPN Protocol Configuration
|
||||||
|
*
|
||||||
|
* (c) 2011-2013 Yandex, LLC
|
||||||
|
* Author: Alexander V. Chernikov <melifaro@yandex-team.ru>
|
||||||
|
*
|
||||||
|
* (c) 2016 CZ.NIC, z.s.p.o.
|
||||||
|
* Updated by Jan Moskyto Matejka <mq@ucw.cz>
|
||||||
|
*
|
||||||
|
* Can be freely distributed and used under the terms of the GNU GPL.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _BIRD_L3VPN_H_
|
||||||
|
#define _BIRD_L3VPN_H_
|
||||||
|
|
||||||
|
#include "nest/protocol.h"
|
||||||
|
#include "nest/route.h"
|
||||||
|
|
||||||
|
struct l3vpn_config {
|
||||||
|
struct proto_config c;
|
||||||
|
struct channel_config *vpn;
|
||||||
|
struct channel_config *ip;
|
||||||
|
struct channel_config *mpls;
|
||||||
|
u64 rd; /* VPN route distinguisher */
|
||||||
|
};
|
||||||
|
|
||||||
|
struct l3vpn_proto {
|
||||||
|
struct proto p;
|
||||||
|
struct channel *vpn;
|
||||||
|
struct channel *ip;
|
||||||
|
struct channel *mpls;
|
||||||
|
struct fib iptompls; /* FIB to lookup IP->MPLS mappings */
|
||||||
|
|
||||||
|
u64 rd; /* VPN route distinguisher */
|
||||||
|
u32 last_label; /* Last allocated label */
|
||||||
|
};
|
||||||
|
|
||||||
|
extern struct protocol proto_l3vpn;
|
||||||
|
|
||||||
|
struct l3vpn_ip_to_mpls {
|
||||||
|
ea_list el;
|
||||||
|
eattr ea;
|
||||||
|
struct adata ad;
|
||||||
|
struct fib_node n;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define L3VPN_LABEL_AUTO (1<<20)
|
||||||
|
|
||||||
|
#endif
|
@ -43,6 +43,7 @@
|
|||||||
#undef CONFIG_BGP
|
#undef CONFIG_BGP
|
||||||
#undef CONFIG_OSPF
|
#undef CONFIG_OSPF
|
||||||
#undef CONFIG_PIPE
|
#undef CONFIG_PIPE
|
||||||
|
#undef CONFIG_L3VPN
|
||||||
|
|
||||||
/* We use multithreading */
|
/* We use multithreading */
|
||||||
#undef USE_PTHREADS
|
#undef USE_PTHREADS
|
||||||
|
Loading…
Reference in New Issue
Block a user