rpcd-mod-wireguard: add new package

The rpcd wireguard plugin provides essential functions similar to the `wg`
tool.

It uses the embeddable-wg-library [0], which describes itself as:
"This is a mini single-file library, meant to be embedded directly into the
source code of your program. Copy wireguard.c and wireguard.h into your
project. They should build with any C89 compiler. There are no dependencies
except libc"

The plugin does exactly that and therefor inherits the LGPL license.

At this point it provides the following functions:

'wireguard'
    "status":{}
    "genkey":{}
    "genpsk":{}
    "pubkey":{"private":"String"}

Examples:

$ ubus call wireguard status
{
        "wg0": {
                "ifindex": 12,
                "public_key": "<base64 encoded public key>",
                "listen_port": 1234,
                "peers": {
                        "<base64 encoded public peer key>": {
                                "allowed_ips": [
                                        "192.168.1.123/32"
                                ],
                                "last_handshake": 0,
                                "rx_bytes": 0,
                                "tx_bytes": 0
                        },
                        "<another base64 encoded public peer key>": {
                                "endpoint": "<ip:port>",
                                "allowed_ips": [
                                        "192.168.1.124/32"
                                ],
                                "last_handshake": 1676287619,
                                "rx_bytes": 8731604,
                                "tx_bytes": 88333652
                        }
                }
        }
}

$ ubus call wireguard genpsk
{
        "preshared": "EKQJ3XI/6xLoifAoGb5bNA39De1tiwZ3x7h8OS2zKkE="
}

$ ubus call wireguard genkey
{
        "private": "IFyGkfXlO+WO8DMO3cqhaDZ8rBfioP5pVnAoQlEpXnI=",
        "public": "uF2O6/ZXZjKnUnxBnldElBYIXfpyvvtUnZfKP+BSBSI="
}

$ ubus call wireguard pubkey '{"private":"IFyGkfXlO+WO8DMO3cqhaDZ8rBfioP5pVnAoQlEpXnI="}'
{
        "public": "uF2O6/ZXZjKnUnxBnldElBYIXfpyvvtUnZfKP+BSBSI="
}

Size comparison:
52436 /usr/bin/wg
18544 /usr/lib/rpcd/wireguard.so

[0] https://git.zx2c4.com/wireguard-tools/tree/contrib/embeddable-wg-library

Signed-off-by: Andre Heider <a.heider@gmail.com>
This commit is contained in:
Andre Heider 2023-02-13 19:19:40 +01:00
parent 09e06f9594
commit c23789a576
5 changed files with 2146 additions and 0 deletions

View File

@ -0,0 +1,31 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=rpcd-mod-wireguard
PKG_RELEASE=1
PKG_LICENSE:=LGPL-2.1+
PKG_BUILD_FLAGS:=gc-sections
include $(INCLUDE_DIR)/package.mk
include $(INCLUDE_DIR)/cmake.mk
define Package/rpcd-mod-wireguard
SECTION:=libs
CATEGORY:=Libraries
TITLE:=WireGuard rpcd module
DEPENDS:=+rpcd +kmod-wireguard
MAINTAINER:=Andre Heider <a.heider@gmail.com>
endef
define Package/rpcd-mod-wireguard/install
$(INSTALL_DIR) $(1)/usr/lib/rpcd
$(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/lib/rpcd/wireguard.so \
$(1)/usr/lib/rpcd/
endef
define Package/rpcd-mod-wireguard/postinst
#!/bin/sh
[ -n "$$IPKG_INSTROOT" ] || /etc/init.d/rpcd reload
endef
$(eval $(call BuildPackage,rpcd-mod-wireguard))

View File

@ -0,0 +1,10 @@
cmake_minimum_required(VERSION 2.8.12)
PROJECT(rpcd-mod-wireguard)
ADD_DEFINITIONS(-Os -Wall -Werror --std=gnu99 -g3 -Wmissing-declarations)
SET(SOURCES wireguard.c api.c)
ADD_LIBRARY(rpcd-mod-wireguard SHARED ${SOURCES})
SET_TARGET_PROPERTIES(rpcd-mod-wireguard PROPERTIES OUTPUT_NAME wireguard PREFIX "")
INSTALL(TARGETS rpcd-mod-wireguard LIBRARY DESTINATION lib/rpcd)

View File

@ -0,0 +1,245 @@
// SPDX-License-Identifier: LGPL-2.1+
// Copyright (C) 2023 Andre Heider <a.heider@gmail.com>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <net/if.h>
#include <netdb.h>
#include <stdio.h>
#include <string.h>
#include <libubox/blobmsg.h>
#include <libubox/blobmsg_json.h>
#include <libubus.h>
#include <rpcd/plugin.h>
#include "wireguard.h"
static struct blob_buf buf;
enum {
RPC_PK_DEVICE,
__RPC_PK_MAX,
};
static const struct blobmsg_policy rpc_privatekey_policy[__RPC_PK_MAX] = {
[RPC_PK_DEVICE] = { .name = "private", .type = BLOBMSG_TYPE_STRING },
};
static void rpc_wireguard_add_endpoint(const wg_endpoint *endpoint)
{
char host[1025]; // NI_MAXHOST
char serv[32]; // NI_MAXSERV
char res[sizeof(host) + sizeof(serv) + 4];
socklen_t addr_len;
memset(res, 0, sizeof(res));
if (endpoint->addr.sa_family == AF_INET)
addr_len = sizeof(struct sockaddr_in);
else if (endpoint->addr.sa_family == AF_INET6)
addr_len = sizeof(struct sockaddr_in6);
else
return;
if (getnameinfo(&endpoint->addr, addr_len, host, sizeof(host), serv, sizeof(serv),
NI_DGRAM | NI_NUMERICHOST | NI_NUMERICSERV))
return;
if (endpoint->addr.sa_family == AF_INET6 && strchr(host, ':'))
snprintf(res, sizeof(res), "[%s]:%s", host, serv);
else
snprintf(res, sizeof(res), "%s:%s", host, serv);
res[sizeof(res) - 1] = 0;
blobmsg_add_string(&buf, "endpoint", res);
}
static void rpc_wireguard_add_allowedip(const wg_allowedip *allowedip)
{
char res[INET6_ADDRSTRLEN + 4 + 1];
memset(res, 0, sizeof(res));
if (allowedip->family == AF_INET)
inet_ntop(AF_INET, &allowedip->ip4, res, INET6_ADDRSTRLEN);
else if (allowedip->family == AF_INET6)
inet_ntop(AF_INET6, &allowedip->ip6, res, INET6_ADDRSTRLEN);
else
return;
if (!res[0])
return;
sprintf(res + strlen(res), "/%u", allowedip->cidr);
res[sizeof(res) - 1] = 0;
blobmsg_add_string(&buf, NULL, res);
}
static void rpc_wireguard_add_peer(const wg_peer *peer)
{
void *c;
struct wg_allowedip *allowedip;
rpc_wireguard_add_endpoint(&peer->endpoint);
c = blobmsg_open_array(&buf, "allowed_ips");
wg_for_each_allowedip(peer, allowedip)
rpc_wireguard_add_allowedip(allowedip);
blobmsg_close_array(&buf, c);
blobmsg_add_u64(&buf, "last_handshake", peer->last_handshake_time.tv_sec);
blobmsg_add_u64(&buf, "rx_bytes", peer->rx_bytes);
blobmsg_add_u64(&buf, "tx_bytes", peer->tx_bytes);
if (peer->persistent_keepalive_interval)
blobmsg_add_u16(&buf, "persistent_keepalive_interval", peer->persistent_keepalive_interval);
}
static void rpc_wireguard_add_device(const wg_device *device)
{
void *c, *d;
wg_peer *peer;
wg_key_b64_string key;
blobmsg_add_u32(&buf, "ifindex", device->ifindex);
if (device->flags & WGDEVICE_HAS_PUBLIC_KEY) {
wg_key_to_base64(key, device->public_key);
blobmsg_add_string(&buf, "public_key", key);
}
if (device->listen_port)
blobmsg_add_u16(&buf, "listen_port", device->listen_port);
if (device->fwmark)
blobmsg_add_u32(&buf, "fwmark", device->fwmark);
c = blobmsg_open_table(&buf, "peers");
wg_for_each_peer(device, peer) {
wg_key_to_base64(key, peer->public_key);
d = blobmsg_open_table(&buf, key);
rpc_wireguard_add_peer(peer);
blobmsg_close_table(&buf, d);
}
blobmsg_close_table(&buf, c);
}
static int rpc_wireguard_status(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method, struct blob_attr *msg)
{
void *c;
char *device_names, *device_name;
size_t len;
device_names = wg_list_device_names();
if (!device_names)
return UBUS_STATUS_NOT_FOUND;
blob_buf_init(&buf, 0);
wg_for_each_device_name(device_names, device_name, len) {
wg_device *device;
if (wg_get_device(&device, device_name) < 0)
continue;
c = blobmsg_open_table(&buf, device_name);
rpc_wireguard_add_device(device);
blobmsg_close_table(&buf, c);
wg_free_device(device);
}
free(device_names);
ubus_send_reply(ctx, req, buf.head);
return UBUS_STATUS_OK;
}
static int rpc_wireguard_genkey(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method, struct blob_attr *msg)
{
wg_key private_key, public_key;
wg_key_b64_string key;
wg_generate_private_key(private_key);
wg_generate_public_key(public_key, private_key);
blob_buf_init(&buf, 0);
wg_key_to_base64(key, private_key);
blobmsg_add_string(&buf, "private", key);
wg_key_to_base64(key, public_key);
blobmsg_add_string(&buf, "public", key);
ubus_send_reply(ctx, req, buf.head);
return UBUS_STATUS_OK;
}
static int rpc_wireguard_genpsk(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method, struct blob_attr *msg)
{
wg_key preshared_key;
wg_key_b64_string key;
wg_generate_preshared_key(preshared_key);
blob_buf_init(&buf, 0);
wg_key_to_base64(key, preshared_key);
blobmsg_add_string(&buf, "preshared", key);
ubus_send_reply(ctx, req, buf.head);
return UBUS_STATUS_OK;
}
static int rpc_wireguard_pubkey(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method, struct blob_attr *msg)
{
static struct blob_attr *tb[__RPC_PK_MAX];
wg_key_b64_string key;
wg_key private_key, public_key;
blobmsg_parse(rpc_privatekey_policy, __RPC_PK_MAX, tb, blob_data(msg), blob_len(msg));
if (!tb[RPC_PK_DEVICE])
return UBUS_STATUS_INVALID_ARGUMENT;
if (wg_key_from_base64(private_key, blobmsg_get_string(tb[RPC_PK_DEVICE])))
return UBUS_STATUS_INVALID_ARGUMENT;
wg_generate_public_key(public_key, private_key);
blob_buf_init(&buf, 0);
wg_key_to_base64(key, public_key);
blobmsg_add_string(&buf, "public", key);
ubus_send_reply(ctx, req, buf.head);
return UBUS_STATUS_OK;
}
static int rpc_wireguard_api_init(const struct rpc_daemon_ops *ops, struct ubus_context *ctx)
{
static const struct ubus_method wireguard_methods[] = {
UBUS_METHOD_NOARG("status", rpc_wireguard_status),
UBUS_METHOD_NOARG("genkey", rpc_wireguard_genkey),
UBUS_METHOD_NOARG("genpsk", rpc_wireguard_genpsk),
UBUS_METHOD("pubkey", rpc_wireguard_pubkey, rpc_privatekey_policy),
};
static struct ubus_object_type wireguard_type =
UBUS_OBJECT_TYPE("rpcd-plugin-wireguard", wireguard_methods);
static struct ubus_object obj = {
.name = "wireguard",
.type = &wireguard_type,
.methods = wireguard_methods,
.n_methods = ARRAY_SIZE(wireguard_methods),
};
return ubus_add_object(ctx, &obj);
}
struct rpc_plugin rpc_plugin = {
.init = rpc_wireguard_api_init
};

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,105 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
/*
* Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
*/
#ifndef WIREGUARD_H
#define WIREGUARD_H
#include <net/if.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <time.h>
#include <stdint.h>
#include <stdbool.h>
typedef uint8_t wg_key[32];
typedef char wg_key_b64_string[((sizeof(wg_key) + 2) / 3) * 4 + 1];
/* Cross platform __kernel_timespec */
struct timespec64 {
int64_t tv_sec;
int64_t tv_nsec;
};
typedef struct wg_allowedip {
uint16_t family;
union {
struct in_addr ip4;
struct in6_addr ip6;
};
uint8_t cidr;
struct wg_allowedip *next_allowedip;
} wg_allowedip;
enum wg_peer_flags {
WGPEER_REMOVE_ME = 1U << 0,
WGPEER_REPLACE_ALLOWEDIPS = 1U << 1,
WGPEER_HAS_PUBLIC_KEY = 1U << 2,
WGPEER_HAS_PRESHARED_KEY = 1U << 3,
WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL = 1U << 4
};
typedef union wg_endpoint {
struct sockaddr addr;
struct sockaddr_in addr4;
struct sockaddr_in6 addr6;
} wg_endpoint;
typedef struct wg_peer {
enum wg_peer_flags flags;
wg_key public_key;
wg_key preshared_key;
wg_endpoint endpoint;
struct timespec64 last_handshake_time;
uint64_t rx_bytes, tx_bytes;
uint16_t persistent_keepalive_interval;
struct wg_allowedip *first_allowedip, *last_allowedip;
struct wg_peer *next_peer;
} wg_peer;
enum wg_device_flags {
WGDEVICE_REPLACE_PEERS = 1U << 0,
WGDEVICE_HAS_PRIVATE_KEY = 1U << 1,
WGDEVICE_HAS_PUBLIC_KEY = 1U << 2,
WGDEVICE_HAS_LISTEN_PORT = 1U << 3,
WGDEVICE_HAS_FWMARK = 1U << 4
};
typedef struct wg_device {
char name[IFNAMSIZ];
uint32_t ifindex;
enum wg_device_flags flags;
wg_key public_key;
wg_key private_key;
uint32_t fwmark;
uint16_t listen_port;
struct wg_peer *first_peer, *last_peer;
} wg_device;
#define wg_for_each_device_name(__names, __name, __len) for ((__name) = (__names), (__len) = 0; ((__len) = strlen(__name)); (__name) += (__len) + 1)
#define wg_for_each_peer(__dev, __peer) for ((__peer) = (__dev)->first_peer; (__peer); (__peer) = (__peer)->next_peer)
#define wg_for_each_allowedip(__peer, __allowedip) for ((__allowedip) = (__peer)->first_allowedip; (__allowedip); (__allowedip) = (__allowedip)->next_allowedip)
int wg_set_device(wg_device *dev);
int wg_get_device(wg_device **dev, const char *device_name);
int wg_add_device(const char *device_name);
int wg_del_device(const char *device_name);
void wg_free_device(wg_device *dev);
char *wg_list_device_names(void); /* first\0second\0third\0forth\0last\0\0 */
void wg_key_to_base64(wg_key_b64_string base64, const wg_key key);
int wg_key_from_base64(wg_key key, const wg_key_b64_string base64);
bool wg_key_is_zero(const wg_key key);
void wg_generate_public_key(wg_key public_key, const wg_key private_key);
void wg_generate_private_key(wg_key private_key);
void wg_generate_preshared_key(wg_key preshared_key);
#endif