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:
parent
09e06f9594
commit
c23789a576
5 changed files with 2146 additions and 0 deletions
31
utils/rpcd-mod-wireguard/Makefile
Normal file
31
utils/rpcd-mod-wireguard/Makefile
Normal 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))
|
10
utils/rpcd-mod-wireguard/src/CMakeLists.txt
Normal file
10
utils/rpcd-mod-wireguard/src/CMakeLists.txt
Normal 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)
|
245
utils/rpcd-mod-wireguard/src/api.c
Normal file
245
utils/rpcd-mod-wireguard/src/api.c
Normal 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
|
||||
};
|
1755
utils/rpcd-mod-wireguard/src/wireguard.c
Normal file
1755
utils/rpcd-mod-wireguard/src/wireguard.c
Normal file
File diff suppressed because it is too large
Load diff
105
utils/rpcd-mod-wireguard/src/wireguard.h
Normal file
105
utils/rpcd-mod-wireguard/src/wireguard.h
Normal 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
|
Loading…
Reference in a new issue