]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/wireguard-tools/ipc-freebsd.h
Merge llvm-project release/16.x llvmorg-16.0.2-0-g18ddebe1a1a9
[FreeBSD/FreeBSD.git] / contrib / wireguard-tools / ipc-freebsd.h
1 // SPDX-License-Identifier: MIT
2 /*
3  * Copyright (C) 2015-2021 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
4  *
5  */
6
7 #include <assert.h>
8 #include <sys/nv.h>
9 #include <sys/sockio.h>
10 #include <dev/wg/if_wg.h>
11
12 #define IPC_SUPPORTS_KERNEL_INTERFACE
13
14 static int get_dgram_socket(void)
15 {
16         static int sock = -1;
17         if (sock < 0)
18                 sock = socket(AF_INET, SOCK_DGRAM, 0);
19         return sock;
20 }
21
22 static int kernel_get_wireguard_interfaces(struct string_list *list)
23 {
24         struct ifgroupreq ifgr = { .ifgr_name = "wg" };
25         struct ifg_req *ifg;
26         int s = get_dgram_socket(), ret = 0;
27
28         if (s < 0)
29                 return -errno;
30
31         if (ioctl(s, SIOCGIFGMEMB, (caddr_t)&ifgr) < 0)
32                 return errno == ENOENT ? 0 : -errno;
33
34         ifgr.ifgr_groups = calloc(1, ifgr.ifgr_len);
35         if (!ifgr.ifgr_groups)
36                 return -errno;
37         if (ioctl(s, SIOCGIFGMEMB, (caddr_t)&ifgr) < 0) {
38                 ret = -errno;
39                 goto out;
40         }
41
42         for (ifg = ifgr.ifgr_groups; ifg && ifgr.ifgr_len > 0; ++ifg) {
43                 if ((ret = string_list_add(list, ifg->ifgrq_member)) < 0)
44                         goto out;
45                 ifgr.ifgr_len -= sizeof(struct ifg_req);
46         }
47
48 out:
49         free(ifgr.ifgr_groups);
50         return ret;
51 }
52
53 static int kernel_get_device(struct wgdevice **device, const char *ifname)
54 {
55         struct wg_data_io wgd = { 0 };
56         nvlist_t *nvl_device = NULL;
57         const nvlist_t *const *nvl_peers;
58         struct wgdevice *dev = NULL;
59         size_t size, peer_count, i;
60         uint64_t number;
61         const void *binary;
62         int ret = 0, s;
63
64         *device = NULL;
65         s = get_dgram_socket();
66         if (s < 0)
67                 goto err;
68
69         strlcpy(wgd.wgd_name, ifname, sizeof(wgd.wgd_name));
70         if (ioctl(s, SIOCGWG, &wgd) < 0)
71                 goto err;
72
73         wgd.wgd_data = malloc(wgd.wgd_size);
74         if (!wgd.wgd_data)
75                 goto err;
76         if (ioctl(s, SIOCGWG, &wgd) < 0)
77                 goto err;
78
79         dev = calloc(1, sizeof(*dev));
80         if (!dev)
81                 goto err;
82         strlcpy(dev->name, ifname, sizeof(dev->name));
83         nvl_device = nvlist_unpack(wgd.wgd_data, wgd.wgd_size, 0);
84         if (!nvl_device)
85                 goto err;
86
87         if (nvlist_exists_number(nvl_device, "listen-port")) {
88                 number = nvlist_get_number(nvl_device, "listen-port");
89                 if (number <= UINT16_MAX) {
90                         dev->listen_port = number;
91                         dev->flags |= WGDEVICE_HAS_LISTEN_PORT;
92                 }
93         }
94         if (nvlist_exists_number(nvl_device, "user-cookie")) {
95                 number = nvlist_get_number(nvl_device, "user-cookie");
96                 if (number <= UINT32_MAX) {
97                         dev->fwmark = number;
98                         dev->flags |= WGDEVICE_HAS_FWMARK;
99                 }
100         }
101         if (nvlist_exists_binary(nvl_device, "public-key")) {
102                 binary = nvlist_get_binary(nvl_device, "public-key", &size);
103                 if (binary && size == sizeof(dev->public_key)) {
104                         memcpy(dev->public_key, binary, sizeof(dev->public_key));
105                         dev->flags |= WGDEVICE_HAS_PUBLIC_KEY;
106                 }
107         }
108         if (nvlist_exists_binary(nvl_device, "private-key")) {
109                 binary = nvlist_get_binary(nvl_device, "private-key", &size);
110                 if (binary && size == sizeof(dev->private_key)) {
111                         memcpy(dev->private_key, binary, sizeof(dev->private_key));
112                         dev->flags |= WGDEVICE_HAS_PRIVATE_KEY;
113                 }
114         }
115         if (!nvlist_exists_nvlist_array(nvl_device, "peers"))
116                 goto skip_peers;
117         nvl_peers = nvlist_get_nvlist_array(nvl_device, "peers", &peer_count);
118         if (!nvl_peers)
119                 goto skip_peers;
120         for (i = 0; i < peer_count; ++i) {
121                 struct wgpeer *peer;
122                 struct wgallowedip *aip = NULL;
123                 const nvlist_t *const *nvl_aips;
124                 size_t aip_count, j;
125
126                 peer = calloc(1, sizeof(*peer));
127                 if (!peer)
128                         goto err_peer;
129                 if (nvlist_exists_binary(nvl_peers[i], "public-key")) {
130                         binary = nvlist_get_binary(nvl_peers[i], "public-key", &size);
131                         if (binary && size == sizeof(peer->public_key)) {
132                                 memcpy(peer->public_key, binary, sizeof(peer->public_key));
133                                 peer->flags |= WGPEER_HAS_PUBLIC_KEY;
134                         }
135                 }
136                 if (nvlist_exists_binary(nvl_peers[i], "preshared-key")) {
137                         binary = nvlist_get_binary(nvl_peers[i], "preshared-key", &size);
138                         if (binary && size == sizeof(peer->preshared_key)) {
139                                 memcpy(peer->preshared_key, binary, sizeof(peer->preshared_key));
140                                 if (!key_is_zero(peer->preshared_key))
141                                         peer->flags |= WGPEER_HAS_PRESHARED_KEY;
142                         }
143                 }
144                 if (nvlist_exists_number(nvl_peers[i], "persistent-keepalive-interval")) {
145                         number = nvlist_get_number(nvl_peers[i], "persistent-keepalive-interval");
146                         if (number <= UINT16_MAX) {
147                                 peer->persistent_keepalive_interval = number;
148                                 peer->flags |= WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL;
149                         }
150                 }
151                 if (nvlist_exists_binary(nvl_peers[i], "endpoint")) {
152                         const struct sockaddr *endpoint = nvlist_get_binary(nvl_peers[i], "endpoint", &size);
153                         if (endpoint && size <= sizeof(peer->endpoint) && size >= sizeof(peer->endpoint.addr) &&
154                             (endpoint->sa_family == AF_INET || endpoint->sa_family == AF_INET6))
155                                 memcpy(&peer->endpoint.addr, endpoint, size);
156                 }
157                 if (nvlist_exists_number(nvl_peers[i], "rx-bytes"))
158                         peer->rx_bytes = nvlist_get_number(nvl_peers[i], "rx-bytes");
159                 if (nvlist_exists_number(nvl_peers[i], "tx-bytes"))
160                         peer->tx_bytes = nvlist_get_number(nvl_peers[i], "tx-bytes");
161                 if (nvlist_exists_binary(nvl_peers[i], "last-handshake-time")) {
162                         binary = nvlist_get_binary(nvl_peers[i], "last-handshake-time", &size);
163                         if (binary && size == sizeof(peer->last_handshake_time))
164                                 memcpy(&peer->last_handshake_time, binary, sizeof(peer->last_handshake_time));
165                 }
166
167                 if (!nvlist_exists_nvlist_array(nvl_peers[i], "allowed-ips"))
168                         goto skip_allowed_ips;
169                 nvl_aips = nvlist_get_nvlist_array(nvl_peers[i], "allowed-ips", &aip_count);
170                 if (!aip_count || !nvl_aips)
171                         goto skip_allowed_ips;
172                 for (j = 0; j < aip_count; ++j) {
173                         if (!nvlist_exists_number(nvl_aips[j], "cidr"))
174                                 continue;
175                         if (!nvlist_exists_binary(nvl_aips[j], "ipv4") && !nvlist_exists_binary(nvl_aips[j], "ipv6"))
176                                 continue;
177                         aip = calloc(1, sizeof(*aip));
178                         if (!aip)
179                                 goto err_allowed_ips;
180                         number = nvlist_get_number(nvl_aips[j], "cidr");
181                         if (nvlist_exists_binary(nvl_aips[j], "ipv4")) {
182                                 binary = nvlist_get_binary(nvl_aips[j], "ipv4", &size);
183                                 if (!binary || number > 32) {
184                                         ret = EINVAL;
185                                         goto err_allowed_ips;
186                                 }
187                                 aip->family = AF_INET;
188                                 aip->cidr = number;
189                                 memcpy(&aip->ip4, binary, sizeof(aip->ip4));
190                         } else {
191                                 assert(nvlist_exists_binary(nvl_aips[j], "ipv6"));
192                                 binary = nvlist_get_binary(nvl_aips[j], "ipv6", &size);
193                                 if (!binary || number > 128) {
194                                         ret = EINVAL;
195                                         goto err_allowed_ips;
196                                 }
197                                 aip->family = AF_INET6;
198                                 aip->cidr = number;
199                                 memcpy(&aip->ip6, binary, sizeof(aip->ip6));
200                         }
201
202                         if (!peer->first_allowedip)
203                                 peer->first_allowedip = aip;
204                         else
205                                 peer->last_allowedip->next_allowedip = aip;
206                         peer->last_allowedip = aip;
207                         aip = NULL;
208                         continue;
209
210                 err_allowed_ips:
211                         if (!ret)
212                                 ret = -errno;
213                         free(aip);
214                         goto err_peer;
215                 }
216
217                 /* Nothing leaked, hopefully -- ownership transferred or aip freed. */
218                 assert(aip == NULL);
219         skip_allowed_ips:
220                 if (!dev->first_peer)
221                         dev->first_peer = peer;
222                 else
223                         dev->last_peer->next_peer = peer;
224                 dev->last_peer = peer;
225                 continue;
226
227         err_peer:
228                 if (!ret)
229                         ret = -errno;
230                 free(peer);
231                 goto err;
232         }
233
234 skip_peers:
235         free(wgd.wgd_data);
236         nvlist_destroy(nvl_device);
237         *device = dev;
238         return 0;
239
240 err:
241         if (!ret)
242                 ret = -errno;
243         free(wgd.wgd_data);
244         nvlist_destroy(nvl_device);
245         free(dev);
246         return ret;
247 }
248
249
250 static int kernel_set_device(struct wgdevice *dev)
251 {
252         struct wg_data_io wgd = { 0 };
253         nvlist_t *nvl_device = NULL, **nvl_peers = NULL;
254         size_t peer_count = 0, i = 0;
255         struct wgpeer *peer;
256         int ret = 0, s;
257
258         strlcpy(wgd.wgd_name, dev->name, sizeof(wgd.wgd_name));
259
260         nvl_device = nvlist_create(0);
261         if (!nvl_device)
262                 goto err;
263
264         for_each_wgpeer(dev, peer)
265                 ++peer_count;
266         if (peer_count) {
267                 nvl_peers = calloc(peer_count, sizeof(*nvl_peers));
268                 if (!nvl_peers)
269                         goto err;
270         }
271         if (dev->flags & WGDEVICE_HAS_PRIVATE_KEY)
272                 nvlist_add_binary(nvl_device, "private-key", dev->private_key, sizeof(dev->private_key));
273         if (dev->flags & WGDEVICE_HAS_LISTEN_PORT)
274                 nvlist_add_number(nvl_device, "listen-port", dev->listen_port);
275         if (dev->flags & WGDEVICE_HAS_FWMARK)
276                 nvlist_add_number(nvl_device, "user-cookie", dev->fwmark);
277         if (dev->flags & WGDEVICE_REPLACE_PEERS)
278                 nvlist_add_bool(nvl_device, "replace-peers", true);
279
280         for_each_wgpeer(dev, peer) {
281                 size_t aip_count = 0, j = 0;
282                 nvlist_t **nvl_aips = NULL;
283                 struct wgallowedip *aip;
284
285                 nvl_peers[i]  = nvlist_create(0);
286                 if (!nvl_peers[i])
287                         goto err_peer;
288                 for_each_wgallowedip(peer, aip)
289                         ++aip_count;
290                 if (aip_count) {
291                         nvl_aips = calloc(aip_count, sizeof(*nvl_aips));
292                         if (!nvl_aips)
293                                 goto err_peer;
294                 }
295                 nvlist_add_binary(nvl_peers[i], "public-key", peer->public_key, sizeof(peer->public_key));
296                 if (peer->flags & WGPEER_HAS_PRESHARED_KEY)
297                         nvlist_add_binary(nvl_peers[i], "preshared-key", peer->preshared_key, sizeof(peer->preshared_key));
298                 if (peer->flags & WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL)
299                         nvlist_add_number(nvl_peers[i], "persistent-keepalive-interval", peer->persistent_keepalive_interval);
300                 if (peer->endpoint.addr.sa_family == AF_INET || peer->endpoint.addr.sa_family == AF_INET6)
301                         nvlist_add_binary(nvl_peers[i], "endpoint", &peer->endpoint.addr, peer->endpoint.addr.sa_len);
302                 if (peer->flags & WGPEER_REPLACE_ALLOWEDIPS)
303                         nvlist_add_bool(nvl_peers[i], "replace-allowedips", true);
304                 if (peer->flags & WGPEER_REMOVE_ME)
305                         nvlist_add_bool(nvl_peers[i], "remove", true);
306                 for_each_wgallowedip(peer, aip) {
307                         nvl_aips[j] = nvlist_create(0);
308                         if (!nvl_aips[j])
309                                 goto err_peer;
310                         nvlist_add_number(nvl_aips[j], "cidr", aip->cidr);
311                         if (aip->family == AF_INET)
312                                 nvlist_add_binary(nvl_aips[j], "ipv4", &aip->ip4, sizeof(aip->ip4));
313                         else if (aip->family == AF_INET6)
314                                 nvlist_add_binary(nvl_aips[j], "ipv6", &aip->ip6, sizeof(aip->ip6));
315                         ++j;
316                 }
317                 if (j) {
318                         nvlist_add_nvlist_array(nvl_peers[i], "allowed-ips", (const nvlist_t *const *)nvl_aips, j);
319                         for (j = 0; j < aip_count; ++j)
320                                 nvlist_destroy(nvl_aips[j]);
321                         free(nvl_aips);
322                 }
323                 ++i;
324                 continue;
325
326         err_peer:
327                 ret = -errno;
328                 for (j = 0; j < aip_count && nvl_aips; ++j)
329                         nvlist_destroy(nvl_aips[j]);
330                 free(nvl_aips);
331                 nvlist_destroy(nvl_peers[i]);
332                 nvl_peers[i] = NULL;
333                 goto err;
334         }
335         if (i) {
336                 nvlist_add_nvlist_array(nvl_device, "peers", (const nvlist_t *const *)nvl_peers, i);
337                 for (i = 0; i < peer_count; ++i)
338                         nvlist_destroy(nvl_peers[i]);
339                 free(nvl_peers);
340                 nvl_peers = NULL;
341         }
342         wgd.wgd_data = nvlist_pack(nvl_device, &wgd.wgd_size);
343         nvlist_destroy(nvl_device);
344         nvl_device = NULL;
345         if (!wgd.wgd_data)
346                 goto err;
347         s = get_dgram_socket();
348         if (s < 0)
349                 return -errno;
350         return ioctl(s, SIOCSWG, &wgd);
351
352 err:
353         if (!ret)
354                 ret = -errno;
355         for (i = 0; i < peer_count && nvl_peers; ++i)
356                 nvlist_destroy(nvl_peers[i]);
357         free(nvl_peers);
358         nvlist_destroy(nvl_device);
359         return ret;
360 }