Merge pull request #20007 from dhewg/prometheus-node-exporter-ucode
[feed/packages.git] / utils / rpcd-mod-wireguard / src / api.c
1 // SPDX-License-Identifier: LGPL-2.1+
2 // Copyright (C) 2023 Andre Heider <a.heider@gmail.com>
3
4 #include <arpa/inet.h>
5 #include <netinet/in.h>
6 #include <sys/socket.h>
7 #include <net/if.h>
8 #include <netdb.h>
9 #include <stdio.h>
10 #include <string.h>
11
12 #include <libubox/blobmsg.h>
13 #include <libubox/blobmsg_json.h>
14
15 #include <libubus.h>
16
17 #include <rpcd/plugin.h>
18
19 #include "wireguard.h"
20
21 static struct blob_buf buf;
22
23 enum {
24 RPC_PK_DEVICE,
25 __RPC_PK_MAX,
26 };
27
28 static const struct blobmsg_policy rpc_privatekey_policy[__RPC_PK_MAX] = {
29 [RPC_PK_DEVICE] = { .name = "private", .type = BLOBMSG_TYPE_STRING },
30 };
31
32 static void rpc_wireguard_add_endpoint(const wg_endpoint *endpoint)
33 {
34 char host[1025]; // NI_MAXHOST
35 char serv[32]; // NI_MAXSERV
36 char res[sizeof(host) + sizeof(serv) + 4];
37 socklen_t addr_len;
38
39 memset(res, 0, sizeof(res));
40 if (endpoint->addr.sa_family == AF_INET)
41 addr_len = sizeof(struct sockaddr_in);
42 else if (endpoint->addr.sa_family == AF_INET6)
43 addr_len = sizeof(struct sockaddr_in6);
44 else
45 return;
46
47 if (getnameinfo(&endpoint->addr, addr_len, host, sizeof(host), serv, sizeof(serv),
48 NI_DGRAM | NI_NUMERICHOST | NI_NUMERICSERV))
49 return;
50
51 if (endpoint->addr.sa_family == AF_INET6 && strchr(host, ':'))
52 snprintf(res, sizeof(res), "[%s]:%s", host, serv);
53 else
54 snprintf(res, sizeof(res), "%s:%s", host, serv);
55 res[sizeof(res) - 1] = 0;
56
57 blobmsg_add_string(&buf, "endpoint", res);
58 }
59
60 static void rpc_wireguard_add_allowedip(const wg_allowedip *allowedip)
61 {
62 char res[INET6_ADDRSTRLEN + 4 + 1];
63
64 memset(res, 0, sizeof(res));
65 if (allowedip->family == AF_INET)
66 inet_ntop(AF_INET, &allowedip->ip4, res, INET6_ADDRSTRLEN);
67 else if (allowedip->family == AF_INET6)
68 inet_ntop(AF_INET6, &allowedip->ip6, res, INET6_ADDRSTRLEN);
69 else
70 return;
71
72 if (!res[0])
73 return;
74
75 sprintf(res + strlen(res), "/%u", allowedip->cidr);
76 res[sizeof(res) - 1] = 0;
77
78 blobmsg_add_string(&buf, NULL, res);
79 }
80
81 static void rpc_wireguard_add_peer(const wg_peer *peer)
82 {
83 void *c;
84 struct wg_allowedip *allowedip;
85
86 rpc_wireguard_add_endpoint(&peer->endpoint);
87
88 c = blobmsg_open_array(&buf, "allowed_ips");
89 wg_for_each_allowedip(peer, allowedip)
90 rpc_wireguard_add_allowedip(allowedip);
91 blobmsg_close_array(&buf, c);
92
93 blobmsg_add_u64(&buf, "last_handshake", peer->last_handshake_time.tv_sec);
94 blobmsg_add_u64(&buf, "rx_bytes", peer->rx_bytes);
95 blobmsg_add_u64(&buf, "tx_bytes", peer->tx_bytes);
96 if (peer->persistent_keepalive_interval)
97 blobmsg_add_u16(&buf, "persistent_keepalive_interval", peer->persistent_keepalive_interval);
98 }
99
100 static void rpc_wireguard_add_device(const wg_device *device)
101 {
102 void *c, *d;
103 wg_peer *peer;
104 wg_key_b64_string key;
105
106 blobmsg_add_u32(&buf, "ifindex", device->ifindex);
107
108 if (device->flags & WGDEVICE_HAS_PUBLIC_KEY) {
109 wg_key_to_base64(key, device->public_key);
110 blobmsg_add_string(&buf, "public_key", key);
111 }
112
113 if (device->listen_port)
114 blobmsg_add_u16(&buf, "listen_port", device->listen_port);
115
116 if (device->fwmark)
117 blobmsg_add_u32(&buf, "fwmark", device->fwmark);
118
119 c = blobmsg_open_table(&buf, "peers");
120 wg_for_each_peer(device, peer) {
121 wg_key_to_base64(key, peer->public_key);
122 d = blobmsg_open_table(&buf, key);
123 rpc_wireguard_add_peer(peer);
124 blobmsg_close_table(&buf, d);
125 }
126 blobmsg_close_table(&buf, c);
127 }
128
129 static int rpc_wireguard_status(struct ubus_context *ctx, struct ubus_object *obj,
130 struct ubus_request_data *req, const char *method, struct blob_attr *msg)
131 {
132 void *c;
133 char *device_names, *device_name;
134 size_t len;
135
136 device_names = wg_list_device_names();
137 if (!device_names)
138 return UBUS_STATUS_NOT_FOUND;
139
140 blob_buf_init(&buf, 0);
141
142 wg_for_each_device_name(device_names, device_name, len) {
143 wg_device *device;
144
145 if (wg_get_device(&device, device_name) < 0)
146 continue;
147
148 c = blobmsg_open_table(&buf, device_name);
149 rpc_wireguard_add_device(device);
150 blobmsg_close_table(&buf, c);
151
152 wg_free_device(device);
153 }
154
155 free(device_names);
156
157 ubus_send_reply(ctx, req, buf.head);
158
159 return UBUS_STATUS_OK;
160 }
161
162 static int rpc_wireguard_genkey(struct ubus_context *ctx, struct ubus_object *obj,
163 struct ubus_request_data *req, const char *method, struct blob_attr *msg)
164 {
165 wg_key private_key, public_key;
166 wg_key_b64_string key;
167
168 wg_generate_private_key(private_key);
169 wg_generate_public_key(public_key, private_key);
170
171 blob_buf_init(&buf, 0);
172 wg_key_to_base64(key, private_key);
173 blobmsg_add_string(&buf, "private", key);
174 wg_key_to_base64(key, public_key);
175 blobmsg_add_string(&buf, "public", key);
176 ubus_send_reply(ctx, req, buf.head);
177
178 return UBUS_STATUS_OK;
179 }
180
181 static int rpc_wireguard_genpsk(struct ubus_context *ctx, struct ubus_object *obj,
182 struct ubus_request_data *req, const char *method, struct blob_attr *msg)
183 {
184 wg_key preshared_key;
185 wg_key_b64_string key;
186
187 wg_generate_preshared_key(preshared_key);
188
189 blob_buf_init(&buf, 0);
190 wg_key_to_base64(key, preshared_key);
191 blobmsg_add_string(&buf, "preshared", key);
192 ubus_send_reply(ctx, req, buf.head);
193
194 return UBUS_STATUS_OK;
195 }
196
197 static int rpc_wireguard_pubkey(struct ubus_context *ctx, struct ubus_object *obj,
198 struct ubus_request_data *req, const char *method, struct blob_attr *msg)
199 {
200 static struct blob_attr *tb[__RPC_PK_MAX];
201 wg_key_b64_string key;
202 wg_key private_key, public_key;
203
204 blobmsg_parse(rpc_privatekey_policy, __RPC_PK_MAX, tb, blob_data(msg), blob_len(msg));
205
206 if (!tb[RPC_PK_DEVICE])
207 return UBUS_STATUS_INVALID_ARGUMENT;
208
209 if (wg_key_from_base64(private_key, blobmsg_get_string(tb[RPC_PK_DEVICE])))
210 return UBUS_STATUS_INVALID_ARGUMENT;
211
212 wg_generate_public_key(public_key, private_key);
213 blob_buf_init(&buf, 0);
214 wg_key_to_base64(key, public_key);
215 blobmsg_add_string(&buf, "public", key);
216 ubus_send_reply(ctx, req, buf.head);
217
218 return UBUS_STATUS_OK;
219 }
220
221 static int rpc_wireguard_api_init(const struct rpc_daemon_ops *ops, struct ubus_context *ctx)
222 {
223 static const struct ubus_method wireguard_methods[] = {
224 UBUS_METHOD_NOARG("status", rpc_wireguard_status),
225 UBUS_METHOD_NOARG("genkey", rpc_wireguard_genkey),
226 UBUS_METHOD_NOARG("genpsk", rpc_wireguard_genpsk),
227 UBUS_METHOD("pubkey", rpc_wireguard_pubkey, rpc_privatekey_policy),
228 };
229
230 static struct ubus_object_type wireguard_type =
231 UBUS_OBJECT_TYPE("rpcd-plugin-wireguard", wireguard_methods);
232
233 static struct ubus_object obj = {
234 .name = "wireguard",
235 .type = &wireguard_type,
236 .methods = wireguard_methods,
237 .n_methods = ARRAY_SIZE(wireguard_methods),
238 };
239
240 return ubus_add_object(ctx, &obj);
241 }
242
243 struct rpc_plugin rpc_plugin = {
244 .init = rpc_wireguard_api_init
245 };