]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
L3VPN: new protocol
authorJan Moskyto Matejka <mq@ucw.cz>
Mon, 4 Apr 2016 14:16:18 +0000 (16:16 +0200)
committerJan Moskyto Matejka <mq@ucw.cz>
Thu, 7 Apr 2016 08:08:43 +0000 (10:08 +0200)
configure.in
lib/mpls.h
nest/proto.c
nest/protocol.h
nest/rt-attr.c
proto/l3vpn/Doc [new file with mode: 0644]
proto/l3vpn/Makefile [new file with mode: 0644]
proto/l3vpn/config.Y [new file with mode: 0644]
proto/l3vpn/l3vpn.c [new file with mode: 0644]
proto/l3vpn/l3vpn.h [new file with mode: 0644]
sysdep/autoconf.h.in

index 1c2c2fe1dae3e17186a164a144056a079bc2afbb..2b8f04693cb690b8c09dc7e1aed8b5659485104f 100644 (file)
@@ -176,7 +176,7 @@ fi
 AC_SUBST(iproutedir)
 
 # 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'`
 
index ac2808c432e03eb8b67e62605b8b928645158127..9064a3de5f8fe2a6ad06f86409e56941d787f2bc 100644 (file)
@@ -12,6 +12,7 @@
 
 #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_LABEL_MAX     ((1<<MPLS_PXLEN)-1) /* Maximal possible label value. */
 
 /*
  *   RFC 3032 updated by RFC 5462:
index f712fe5f4e7153743c3e9ee8d6116195fbe23bf0..1dcdec64b6a45d6e2bc9794e92a3a46ab0f3836b 100644 (file)
@@ -1262,6 +1262,9 @@ protos_build(void)
   proto_build(&proto_bfd);
   bfd_init_all();
 #endif
+#ifdef CONFIG_L3VPN
+  proto_build(&proto_l3vpn);
+#endif
 
   proto_pool = rp_new(&root_pool, "Protocols");
   proto_shutdown_timer = tm_new(proto_pool);
index 19f5d0701edd96641e3a5add6880276acb6c36d0..c67682f059c2a07c78ca811f8145699014f2d13c 100644 (file)
@@ -81,7 +81,7 @@ void protos_dump_all(void);
 
 extern struct protocol
   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
index 7d664d33a58f5f9e7adee4120c6cb7e53d06e160..185cbf9cc01e9324fc186402494b3bb8c3dd486a 100644 (file)
@@ -675,7 +675,7 @@ get_generic_attr(eattr *a, byte **buf, int buflen UNUSED)
     {
       *buf += bsprintf(*buf, "mpls");
       struct adata *ad = a->u.ptr;
-      u32 *z = ad->data;
+      u32 *z = (void *) ad->data;
       int len = ad->length / sizeof(u32);
       int i;
 
diff --git a/proto/l3vpn/Doc b/proto/l3vpn/Doc
new file mode 100644 (file)
index 0000000..ca2fd13
--- /dev/null
@@ -0,0 +1 @@
+S l3vpn.c
diff --git a/proto/l3vpn/Makefile b/proto/l3vpn/Makefile
new file mode 100644 (file)
index 0000000..72f696a
--- /dev/null
@@ -0,0 +1,5 @@
+source=l3vpn.c
+root-rel=../../
+dir-name=proto/l3vpn
+
+include ../../Rules
diff --git a/proto/l3vpn/config.Y b/proto/l3vpn/config.Y
new file mode 100644 (file)
index 0000000..e27e31a
--- /dev/null
@@ -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
diff --git a/proto/l3vpn/l3vpn.c b/proto/l3vpn/l3vpn.c
new file mode 100644 (file)
index 0000000..a9ab52f
--- /dev/null
@@ -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,
+};
diff --git a/proto/l3vpn/l3vpn.h b/proto/l3vpn/l3vpn.h
new file mode 100644 (file)
index 0000000..7ed2970
--- /dev/null
@@ -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
index a9e46e274c2f85488bf4f3b2f88295404ad139a6..916f6c638b0473c3e11d77a16f043704b316b3bc 100644 (file)
@@ -43,6 +43,7 @@
 #undef CONFIG_BGP
 #undef CONFIG_OSPF
 #undef CONFIG_PIPE
+#undef CONFIG_L3VPN
 
 /* We use multithreading */
 #undef USE_PTHREADS