diff options
author | Mikael Magnusson <mikma@users.sourceforge.net> | 2019-02-28 00:44:40 +0100 |
---|---|---|
committer | Mikael Magnusson <mikma@users.sourceforge.net> | 2021-01-12 23:34:17 +0100 |
commit | 1b186fa5158a0a1c84ee2a30f8803c518a538623 (patch) | |
tree | 51e7259ffc5db874654853ec32b475eef3c1a9a9 | |
parent | dad58d2c4b331f839f8256fd29792dea368c70a4 (diff) |
Wireguard: Initial commit
-rw-r--r-- | configure.ac | 3 | ||||
-rw-r--r-- | nest/proto.c | 3 | ||||
-rw-r--r-- | nest/protocol.h | 4 | ||||
-rw-r--r-- | proto/wireguard/Makefile | 8 | ||||
-rw-r--r-- | proto/wireguard/config.Y | 48 | ||||
-rw-r--r-- | proto/wireguard/wireguard.c | 345 | ||||
-rw-r--r-- | proto/wireguard/wireguard.h | 22 |
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 */ |