Skip to content

Commit

Permalink
Introduce Wireguard support
Browse files Browse the repository at this point in the history
  • Loading branch information
dasJ authored and ajs124 committed Mar 21, 2021
1 parent 82f19ba commit e0be373
Show file tree
Hide file tree
Showing 5 changed files with 2,005 additions and 8 deletions.
11 changes: 11 additions & 0 deletions configure.ac
Expand Up @@ -54,6 +54,12 @@ AC_ARG_ENABLE([mpls-kernel],
[enable_mpls_kernel=try]
)

AC_ARG_ENABLE([wireguard],
[AS_HELP_STRING([--enable-wireguard], [enable WireGuard interface support @<:@yes@:>@])],
[],
[enable_wireguard=yes]
)

AC_ARG_WITH([protocols],
[AS_HELP_STRING([--with-protocols=LIST], [include specified routing protocols @<:@all@:>@])],
[],
Expand Down Expand Up @@ -149,6 +155,10 @@ fi
# This is assumed to be necessary for proper BIRD build
CFLAGS="$CFLAGS -fno-strict-aliasing -fno-strict-overflow"

if test "$enable_wireguard" != no ; then
AC_DEFINE([WITH_WIREGUARD], [1], [Define to 1 if WireGuard is enabled])
fi

if test "$bird_cflags_default" = yes ; then
BIRD_CHECK_GCC_OPTION([bird_cv_c_option_wno_pointer_sign], [-Wno-pointer-sign], [-Wall])
BIRD_CHECK_GCC_OPTION([bird_cv_c_option_wno_missing_init], [-Wno-missing-field-initializers], [-Wall -Wextra])
Expand Down Expand Up @@ -464,6 +474,7 @@ AC_MSG_RESULT([ Iproute2 directory: $iproutedir])
AC_MSG_RESULT([ System configuration: $sysdesc])
AC_MSG_RESULT([ Debugging: $enable_debug])
AC_MSG_RESULT([ POSIX threads: $enable_pthreads])
AC_MSG_RESULT([ WireGuard: $enable_wireguard])
AC_MSG_RESULT([ Routing protocols: $protocols])
AC_MSG_RESULT([ LibSSH support in RPKI: $enable_libssh])
AC_MSG_RESULT([ Kernel MPLS support: $enable_mpls_kernel])
Expand Down
2 changes: 1 addition & 1 deletion sysdep/linux/Makefile
@@ -1,4 +1,4 @@
src := netlink.c
src := netlink.c wireguard.c
obj := $(src-o-files)
$(all-daemon)
$(conf-y-targets): $(s)netlink.Y
Expand Down
142 changes: 135 additions & 7 deletions sysdep/linux/netlink.c
Expand Up @@ -26,12 +26,18 @@
#include "lib/socket.h"
#include "lib/string.h"
#include "lib/hash.h"
#include "lib/birdlib.h"
#include "conf/conf.h"

#include <asm/types.h>
#include <linux/if.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#ifdef WITH_WIREGUARD
#include "stdlib.h" // free()
#include "wireguard.h"
#else
#include <linux/if.h>
#endif

#ifdef HAVE_MPLS_KERNEL
#include <linux/lwtunnel.h>
Expand Down Expand Up @@ -1232,6 +1238,124 @@ nh_bufsize(struct nexthop *nh)
return rv;
}

#ifdef WITH_WIREGUARD
static inline bool nl_net_overlaps(ip_addr a, ip_addr b, u32 cidr, u32 family)
{
u32 i;
u32 mask;

// v6-mapped v4 addresses
if (family == AF_INET)
cidr += (128 - 32);

for (i=0; i<4; i++) {
mask = ~0;
if ((cidr - i*32) / 32 < 1)
mask <<= 32 - cidr%32;

if ((a.addr[i] & mask) != (b.addr[i] & mask))
return false;
}
return true;
}

static void nl_handle_wireguard(char *iface, int op, ip_addr gw, net_addr *net)
{
int err;
wg_device *dev; // Current device
wg_peer *peer; // Current peer in the list
wg_peer *viathis = NULL; // Route via this peer
u8 viathisCidr = 0; // viathis has this CIDR
wg_allowedip *allowedip; // Current IP in the list
ip_addr allowedip2; // The same, but as ip_addr
wg_allowedip *previp; // Previous IP in the list
bool freed; // Whether this allowed IP was freed
struct wg_allowedip *new_ip; // New allowed IP to insert

log(L_DEBUG "nl_handle_wireguard: %s route to %N on %s", op ? "Adding" : "Removing", net, iface);

err = wg_get_device(&dev, iface);
if (err != 0)
return; // No kernel support or not a Wireguard interface

wg_for_each_peer(dev, peer)
{
previp = NULL;
wg_for_each_allowedip(peer, allowedip)
{
freed = false;
// Only if everything is v4/v6
if ((allowedip->family == AF_INET && ipa_is_ip4(gw) && net->type == NET_IP4)
|| (allowedip->family == AF_INET6 && ipa_is_ip6(gw) && net->type == NET_IP6))
{
// We need the allowed IP as ipa
allowedip2 = allowedip->family == AF_INET6 ? ipa_from_in6(allowedip->ip6) : ipa_from_in4(allowedip->ip4);
// Handle gateway (route via this peer?)
if (ipa_nonzero(gw) && nl_net_overlaps(allowedip2, gw, MAX(allowedip->cidr, net->pxlen), allowedip->family))
if (viathis == NULL || viathisCidr < allowedip->cidr)
{
viathis = peer;
viathisCidr = allowedip->cidr;
log(L_DEBUG "nl_handle_wireguard: handle gateway, set via_this = %x, cidr = %d for allowedip2 = %I, gw = %I", viathis, viathisCidr, allowedip2, gw);
}

// Handle network (do we need to remove the net from a peer?)
if (nl_net_overlaps(allowedip2, net_prefix(net), MAX(allowedip->cidr, net->pxlen), allowedip->family)) {
// Remove the IP
if (previp == NULL)
// This was the first
peer->first_allowedip = allowedip->next_allowedip;
else
// This was somewhere in the middle
previp->next_allowedip = allowedip->next_allowedip;
if (peer->last_allowedip == allowedip) {
// This was the last IP
peer->last_allowedip = previp;
previp->next_allowedip = NULL;
}
peer->flags |= WGPEER_REPLACE_ALLOWEDIPS;
free(allowedip);
freed = true;
}
}
if (!freed)
previp = allowedip;
}
}

// Skip this if the route is to be removed
if (op != NL_OP_DELETE && viathis != NULL)
{
// Add new IP
new_ip = malloc(sizeof(wg_allowedip));
new_ip->cidr = net->pxlen;
new_ip->next_allowedip = NULL;
if (net->type == NET_IP4)
{
new_ip->family = AF_INET;
new_ip->ip4 = ipa_to_in4(net_prefix(net));
}
else
{
new_ip->family = AF_INET6;
new_ip->ip6 = ipa_to_in6(net_prefix(net));
}

viathis->flags |= WGPEER_REPLACE_ALLOWEDIPS;
viathis->last_allowedip->next_allowedip = new_ip;
viathis->last_allowedip = new_ip;
}

err = wg_set_device(dev);
if (err != 0)
log(L_WARN "Could not update Wireguard interface because of error %d", err);

wg_free_device(dev);
return;

}
#endif

static int
nl_send_route(struct krt_proto *p, rte *e, int op, int dest, struct nexthop *nh)
{
Expand Down Expand Up @@ -1358,11 +1482,10 @@ nl_send_route(struct krt_proto *p, rte *e, int op, int dest, struct nexthop *nh)
nl_add_multipath(&r->h, rsize, nh, p->af, eattrs);
else
{
nl_add_attr_u32(&r->h, rsize, RTA_OIF, nh->iface->index);
nl_add_nexthop(&r->h, rsize, nh, p->af);

if (nh->flags & RNF_ONLINK)
r->r.rtm_flags |= RTNH_F_ONLINK;
nl_add_attr_u32(&r->h, rsize, RTA_OIF, nh->iface->index);
nl_add_nexthop(&r->h, rsize, nh, p->af);
if (nh->flags & RNF_ONLINK)
r->r.rtm_flags |= RTNH_F_ONLINK;
}
break;
case RTD_BLACKHOLE:
Expand All @@ -1379,6 +1502,11 @@ nl_send_route(struct krt_proto *p, rte *e, int op, int dest, struct nexthop *nh)
default:
bug("krt_capable inconsistent with nl_send_route");
}
#ifdef WITH_WIREGUARD
if ((dest == RTD_UNICAST && ipa_nonzero(nh->gw)) || op == NL_OP_DELETE)
nl_handle_wireguard(nh->iface->name, op, nh->gw, net->n.addr);
#endif


/* Ignore missing for DELETE */
return nl_exchange(&r->h, (op == NL_OP_DELETE));
Expand Down Expand Up @@ -1414,7 +1542,7 @@ nl_delete_rte(struct krt_proto *p, rte *e)

/* For IPv6, we just repeatedly request DELETE until we get error */
do
err = nl_send_route(p, e, NL_OP_DELETE, RTD_NONE, NULL);
err = nl_send_route(p, e, NL_OP_DELETE, RTD_NONE, &(e->attrs->nh));
while (krt_ecmp6(p) && !err);

return err;
Expand Down

0 comments on commit e0be373

Please sign in to comment.