summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--configure.ac3
-rw-r--r--nest/proto.c3
-rw-r--r--nest/protocol.h4
-rw-r--r--proto/wireguard/Makefile8
-rw-r--r--proto/wireguard/config.Y48
-rw-r--r--proto/wireguard/wireguard.c345
-rw-r--r--proto/wireguard/wireguard.h22
7 files changed, 431 insertions, 2 deletions
diff --git a/configure.ac b/configure.ac
index eabb3d56..3ec309bf 100644
--- a/configure.ac
+++ b/configure.ac
@@ -304,7 +304,7 @@ if test "$enable_mpls_kernel" != no ; then
fi
fi
-all_protocols="$proto_bfd babel bgp mrt ospf perf pipe radv rip rpki static"
+all_protocols="$proto_bfd babel bgp mrt ospf perf pipe radv rip rpki static wireguard"
all_protocols=`echo $all_protocols | sed 's/ /,/g'`
@@ -322,6 +322,7 @@ AH_TEMPLATE([CONFIG_RADV], [RAdv protocol])
AH_TEMPLATE([CONFIG_RIP], [RIP protocol])
AH_TEMPLATE([CONFIG_RPKI], [RPKI protocol])
AH_TEMPLATE([CONFIG_STATIC], [Static protocol])
+AH_TEMPLATE([CONFIG_WIREGUARD], [Wireguard protocol])
AC_MSG_CHECKING([protocols])
protocols=`echo "$with_protocols" | sed 's/,/ /g'`
diff --git a/nest/proto.c b/nest/proto.c
index aebb5458..d1ef07e8 100644
--- a/nest/proto.c
+++ b/nest/proto.c
@@ -1514,6 +1514,9 @@ protos_build(void)
#ifdef CONFIG_PERF
proto_build(&proto_perf);
#endif
+#ifdef CONFIG_WIREGUARD
+ proto_build(&proto_wireguard);
+#endif
proto_pool = rp_new(&root_pool, "Protocols");
proto_shutdown_timer = tm_new(proto_pool);
diff --git a/nest/protocol.h b/nest/protocol.h
index d82e3983..285dcf69 100644
--- a/nest/protocol.h
+++ b/nest/protocol.h
@@ -53,6 +53,7 @@ enum protocol_class {
PROTOCOL_RIP,
PROTOCOL_RPKI,
PROTOCOL_STATIC,
+ PROTOCOL_WG,
PROTOCOL__MAX
};
@@ -103,7 +104,8 @@ void protos_dump_all(void);
extern struct protocol
proto_device, proto_radv, proto_rip, proto_static, proto_mrt,
proto_ospf, proto_perf,
- proto_pipe, proto_bgp, proto_bfd, proto_babel, proto_rpki;
+ proto_pipe, proto_bgp, proto_bfd, proto_babel, proto_rpki,
+ proto_wireguard;
/*
* Routing Protocol Instance
diff --git a/proto/wireguard/Makefile b/proto/wireguard/Makefile
new file mode 100644
index 00000000..26a7970f
--- /dev/null
+++ b/proto/wireguard/Makefile
@@ -0,0 +1,8 @@
+WG := $(srcdir)/../WireGuard/contrib/examples/embeddable-wg-library
+
+src := wireguard.c
+obj := $(src-o-files)
+$(all-daemon)
+$(cf-local)
+
+tests_objs := $(tests_objs) $(src-o-files)
diff --git a/proto/wireguard/config.Y b/proto/wireguard/config.Y
new file mode 100644
index 00000000..dfe621bd
--- /dev/null
+++ b/proto/wireguard/config.Y
@@ -0,0 +1,48 @@
+/*
+ * BIRD -- Wireguard Protocol Configuration
+ *
+ * (c) 1999 Martin Mares <mj@ucw.cz>
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+CF_HDR
+
+#include "proto/wireguard/wireguard.h"
+
+CF_DEFINES
+
+#define WG_CFG ((struct wg_config *) this_proto)
+
+CF_DECLS
+
+CF_KEYWORDS(WIREGUARD, PEERS)
+
+CF_GRAMMAR
+
+proto: wireguard_proto '}' ;
+
+wireguard_proto_start: proto_start WIREGUARD {
+ this_proto = proto_config_new(&proto_wireguard, $1);
+ }
+ ;
+
+wireguard_proto:
+ wireguard_proto_start proto_name '{'
+ | wireguard_proto proto_channel ';' { this_proto->net_type = $2->net_type; }
+ | wireguard_proto proto_item ';'
+ | wireguard_proto INTERFACE TEXT ';' { WG_CFG->ifname = $3; }
+ | wireguard_proto PEERS '{' peers '}'
+ ;
+
+peers:
+ /* empty */
+ | peers peer
+
+peer: ipa pubkey ';'
+
+pubkey: TEXT
+
+CF_CODE
+
+CF_END
diff --git a/proto/wireguard/wireguard.c b/proto/wireguard/wireguard.c
new file mode 100644
index 00000000..e3fcbd47
--- /dev/null
+++ b/proto/wireguard/wireguard.c
@@ -0,0 +1,345 @@
+// Based on proto/rip/rip.c
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "nest/protocol.h"
+#include "nest/iface.h"
+#include "sysdep/linux/wireguard.h"
+#include "wireguard.h"
+#include "proto/bgp/bgp.h"
+
+static void
+wg_init_entry(void *e_)
+{
+ struct wg_entry *e UNUSED = e_;
+// debug("wg_init_entry\n");
+}
+
+static void
+wg_postconfig(struct proto_config *C UNUSED)
+{
+ debug("postconfig\n");
+}
+
+static void
+wg_if_notify(struct proto *P, unsigned flags, struct iface *i)
+{
+ struct wg_proto *p = (struct wg_proto *) P;
+ struct wg_config *c = (struct wg_config *) P->cf;
+
+ debug("WG: if_notify %p %s %d %s\n", i, i->name, flags, c->ifname);
+ if (c->ifname && !strcmp(i->name, c->ifname)) {
+ debug("WG: found ifname\n");
+ p->iface = i;
+ }
+}
+
+static void
+wg_rt_notify(struct proto *P, struct channel *ch UNUSED, struct network *n,
+ struct rte *new, struct rte *old UNUSED)
+{
+ struct wg_proto *p = (struct wg_proto *) P;
+ struct wg_config *c = (struct wg_config *) P->cf;
+ struct wg_entry *en;
+ struct iface *iface = NULL;
+ const char *ifname = NULL;
+
+// debug("WG: notify\n");
+
+ if (new) {
+ iface = new->attrs->nh.iface;
+ ifname = iface ? iface->name : NULL;
+
+ if (iface && iface == p->iface) {
+ struct eattr *t;
+
+ debug("WG: found iface\n");
+ en = fib_get(&p->rtable, n->n.addr);
+
+ debug("WG: notify new %d %N\n",
+ new->attrs->dest, n->n.addr);
+
+ t = ea_find(new->attrs->eattrs, EA_CODE(PROTOCOL_BGP, BA_TUNNEL_ENCAP));
+ if (t && t->u.ptr) {
+ log(L_TRACE "WG: Attr %x %x %d", t->flags, t->type, t->u.ptr->length);
+
+ struct wg_device *dev = NULL;
+
+ if (wg_get_device(&dev, ifname) == 0) {
+ bool found = false;
+ struct wg_peer *peer = NULL;
+ wg_for_each_peer(dev, peer) {
+ // Look for public key
+ size_t len = 32; // FIXME
+ // MIN(32, t->u.ptr->length)
+ if (memcmp(peer->public_key, &t->u.ptr->data[t->u.ptr->length - 32], len) != 0) {
+ log(L_TRACE "WG: Not found");
+ continue;
+ }
+
+ log(L_TRACE "WG: Found");
+ found = true;
+
+ if (found) {
+ // Add allowed ip
+ struct wg_allowedip *allowedip = malloc(sizeof(struct wg_allowedip));
+ memset(allowedip, 0, sizeof(struct wg_allowedip));
+
+ if (p->p.cf->net_type == NET_IP4) {
+ allowedip->family = AF_INET;
+ allowedip->ip4.s_addr = ip4_to_u32(ip4_hton(net4_prefix(n->n.addr)));
+ } else if (p->p.cf->net_type == NET_IP6) {
+ allowedip->family = AF_INET6;
+ ip6_addr addr = ip6_hton(net6_prefix(n->n.addr));
+ memcpy(allowedip->ip6.s6_addr, &addr, 16);
+ }
+
+ allowedip->cidr = net_pxlen(n->n.addr);
+ peer->last_allowedip->next_allowedip = allowedip;
+ peer->last_allowedip = allowedip;
+
+ int res = wg_set_device(dev);
+ log(L_TRACE "WG: wg_set_device %d", res);
+ break;
+ }
+ }
+
+ wg_free_device(dev);
+ dev = NULL;
+ }
+
+
+/*
+ struct sub_tlv_remove_endpoint {
+ u32 asn;
+ u8 address_family;
+ union {
+ ip4_addr ip4;
+ ip6_addr ip6;
+ };
+ };
+
+ struct sub_tlv_udp_dest_port {
+ u16 port;
+ };
+
+ struct sub_tlv_wireguard {
+ u8 pubkey[32];
+ };
+
+ struct sub_tlv {
+ u8 type;
+ union {
+ struct {
+ u8 length;
+ struct sub_tlv_value value[];
+ } s8;
+ struct {
+ u16 length;
+ struct sub_tlv_value value[];
+ } s16;
+ } u;
+ };
+
+ struct bgp_tunnel_encap_attr {
+ u16 type;
+ u16 length;
+ struct sub_tlv stlv[];
+ };
+*/
+ } else {
+ log(L_TRACE "WG: No Attr");
+ }
+
+ // old_metric = en->valid ? en->metric : -1;
+
+// en->valid = RIP_ENTRY_VALID;
+// en->metric = rt_metric;
+// en->tag = rt_tag;
+// en->from = (new->attrs->src->proto == P) ? new->u.rip.from : NULL;
+// en->iface = new->attrs->iface;
+// en->next_hop = new->attrs->gw;
+ }
+
+ } else {
+ debug("WG: notify withdraw %N\n",
+ n->n.addr);
+
+ /* Withdraw */
+ en = fib_find(&p->rtable, n->n.addr);
+
+ if (!en) // || en->valid != RIP_ENTRY_VALID)
+ return;
+
+ struct wg_device *dev = NULL;
+
+ if (wg_get_device(&dev, c->ifname) == 0) {
+ bool found = false;
+ struct wg_peer *peer = NULL;
+ wg_for_each_peer(dev, peer) {
+ // Remove from all peers
+ found = true;
+
+ if (found) {
+ // Add allowed ip
+ struct wg_allowedip *allowedip = malloc(sizeof(struct wg_allowedip));
+ memset(allowedip, 0, sizeof(struct wg_allowedip));
+
+ if (p->p.cf->net_type == NET_IP4) {
+ allowedip->family = AF_INET;
+ allowedip->ip4.s_addr = ip4_to_u32(ip4_hton(net4_prefix(n->n.addr)));
+ } else if (p->p.cf->net_type == NET_IP6) {
+ allowedip->family = AF_INET6;
+ ip6_addr addr = ip6_hton(net6_prefix(n->n.addr));
+ memcpy(allowedip->ip6.s6_addr, &addr, 16);
+ }
+
+ allowedip->cidr = net_pxlen(n->n.addr);
+
+ struct wg_allowedip *ip = NULL;
+ struct wg_allowedip *previp = NULL;
+
+ wg_for_each_allowedip(peer, ip) {
+ if (allowedip->family != ip->family) {
+ previp = ip;
+ continue;
+ }
+
+ if (allowedip->cidr != ip->cidr) {
+ previp = ip;
+ continue;
+ }
+
+ if (memcmp(&allowedip->ip6, &ip->ip6, sizeof(struct in6_addr))) {
+ previp = ip;
+ continue;
+ }
+
+ debug("WG: found ip\n");
+
+ if (!previp) {
+ debug("WG: remove first\n");
+ peer->first_allowedip = ip->next_allowedip;
+ if (peer->last_allowedip == ip) {
+ peer->last_allowedip = NULL;
+ peer->first_allowedip = NULL;
+ }
+ } else {
+ debug("WG: remove middle\n");
+ // Remove
+ if (peer->last_allowedip == ip)
+ peer->last_allowedip = previp;
+
+ previp->next_allowedip = ip->next_allowedip;
+ }
+
+ free(ip);
+ break;
+ }
+
+ peer->flags |= WGPEER_REPLACE_ALLOWEDIPS;
+ int res = wg_set_device(dev);
+ log(L_TRACE "WG: wg_set_device %d", res);
+ }
+ }
+
+ wg_free_device(dev);
+ dev = NULL;
+
+ /*
+ old_metric = en->metric;
+
+ en->valid = RIP_ENTRY_STALE;
+ en->metric = p->infinity;
+ en->tag = 0;
+ en->from = NULL;
+ en->iface = NULL;
+ en->next_hop = IPA_NONE;
+*/
+ }
+ }
+
+// TRACE(D_EVENTS, "wg notify %s %s", src_table->name, ifname?ifname:"(null)");
+}
+
+static void
+wg_reload_routes(struct channel *C)
+{
+ struct wg_proto *p UNUSED = (struct wg_proto *) C->proto;
+
+ debug("reload routes\n");
+
+// TODO
+// WALK_LIST(c, p->channels)
+// channel_request_feeding(c);
+}
+
+
+static struct proto *
+wg_init(struct proto_config *C)
+{
+ struct wg_config *c UNUSED = (struct wg_config *) C;
+ struct proto *P = proto_new(C);
+ struct wg_proto *p UNUSED = (struct wg_proto *) P;
+
+ debug("init\n");
+
+ P->main_channel = proto_add_channel(P, proto_cf_main_channel(C));
+
+ P->if_notify = wg_if_notify;
+ P->rt_notify = wg_rt_notify;
+ P->reload_routes = wg_reload_routes;
+// P->accept_ra_types = RA_ANY;
+
+ return P;
+}
+
+
+static int
+wg_start(struct proto *P)
+{
+ struct wg_config *cf UNUSED = (struct wg_config *) P->cf;
+ struct wg_proto *p = (struct wg_proto *) P;
+
+ debug("start\n");
+ fib_init(&p->rtable, P->pool, P->net_type, sizeof(struct wg_entry),
+ OFFSETOF(struct wg_entry, n), 0, wg_init_entry);
+ return PS_UP;
+}
+
+static void
+wg_dump(struct proto *P)
+{
+ struct wg_proto *p = (struct wg_proto *) P;
+ int i;
+
+ i = 0;
+ FIB_WALK(&p->rtable, struct wg_entry, en)
+ {
+// struct wg_entry *en = (struct wg_entry *) e;
+ debug("WG: entry #%d:\n",
+ i++);
+ }
+ FIB_WALK_END;
+}
+
+
+struct protocol proto_wireguard = {
+ .name = "Wireguard",
+ .template = "wg%d",
+ .class = PROTOCOL_WG,
+ .channel_mask = NB_ANY,
+ .proto_size = sizeof(struct wg_proto),
+ .config_size = sizeof(struct wg_config),
+ .postconfig = wg_postconfig,
+ .init = wg_init,
+ .start = wg_start,
+ .dump = wg_dump,
+/* .multitable = 1,
+ .preference = DEF_PREF_PIPE,
+ .cleanup = wg_cleanup,
+ .reconfigure = wg_reconfigure,
+ .copy_config = wg_copy_config,
+ .get_status = wg_get_status,
+ .show_proto_info = wg_show_proto_info*/
+};
diff --git a/proto/wireguard/wireguard.h b/proto/wireguard/wireguard.h
new file mode 100644
index 00000000..992bb12b
--- /dev/null
+++ b/proto/wireguard/wireguard.h
@@ -0,0 +1,22 @@
+#ifndef _BIRD_WIREGUARD_H
+#define _BIRD_WIREGUARD_H
+
+#include "nest/protocol.h"
+
+struct wg_config {
+ struct proto_config c;
+ const char *ifname;
+};
+
+struct wg_proto {
+ struct proto p;
+ struct fib rtable;
+ struct iface *iface;
+};
+
+struct wg_entry {
+ struct fib_node n;
+};
+
+
+#endif /* _BIRD_WIREGUARD_H */