File Coverage

libwireguard.c
Criterion Covered Total %
statement 296 932 31.7
branch 100 458 21.8
condition n/a
subroutine n/a
pod n/a
total 396 1390 28.4


line stmt bran cond sub pod time code
1             // SPDX-License-Identifier: LGPL-2.1+
2             /*
3             * Copyright (C) 2015-2020 Jason A. Donenfeld . All Rights Reserved.
4             * Copyright (C) 2008-2012 Pablo Neira Ayuso .
5             */
6              
7             #define _GNU_SOURCE
8              
9             #include
10             #include
11             #include
12             #include
13             #include
14             #include
15             #include
16             #include
17             #include
18             #include
19             #include
20             #include
21             #include
22             #include
23             #include
24              
25             #include "wireguard.h"
26              
27             /* wireguard.h netlink uapi: */
28              
29             #define WG_GENL_NAME "wireguard"
30             #define WG_GENL_VERSION 1
31              
32             enum wg_cmd {
33             WG_CMD_GET_DEVICE,
34             WG_CMD_SET_DEVICE,
35             __WG_CMD_MAX
36             };
37              
38             enum wgdevice_flag {
39             WGDEVICE_F_REPLACE_PEERS = 1U << 0
40             };
41             enum wgdevice_attribute {
42             WGDEVICE_A_UNSPEC,
43             WGDEVICE_A_IFINDEX,
44             WGDEVICE_A_IFNAME,
45             WGDEVICE_A_PRIVATE_KEY,
46             WGDEVICE_A_PUBLIC_KEY,
47             WGDEVICE_A_FLAGS,
48             WGDEVICE_A_LISTEN_PORT,
49             WGDEVICE_A_FWMARK,
50             WGDEVICE_A_PEERS,
51             __WGDEVICE_A_LAST
52             };
53              
54             enum wgpeer_flag {
55             WGPEER_F_REMOVE_ME = 1U << 0,
56             WGPEER_F_REPLACE_ALLOWEDIPS = 1U << 1
57             };
58             enum wgpeer_attribute {
59             WGPEER_A_UNSPEC,
60             WGPEER_A_PUBLIC_KEY,
61             WGPEER_A_PRESHARED_KEY,
62             WGPEER_A_FLAGS,
63             WGPEER_A_ENDPOINT,
64             WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL,
65             WGPEER_A_LAST_HANDSHAKE_TIME,
66             WGPEER_A_RX_BYTES,
67             WGPEER_A_TX_BYTES,
68             WGPEER_A_ALLOWEDIPS,
69             WGPEER_A_PROTOCOL_VERSION,
70             __WGPEER_A_LAST
71             };
72              
73             enum wgallowedip_attribute {
74             WGALLOWEDIP_A_UNSPEC,
75             WGALLOWEDIP_A_FAMILY,
76             WGALLOWEDIP_A_IPADDR,
77             WGALLOWEDIP_A_CIDR_MASK,
78             __WGALLOWEDIP_A_LAST
79             };
80              
81             /* libmnl mini library: */
82              
83             #define MNL_SOCKET_AUTOPID 0
84             #define MNL_ALIGNTO 4
85             #define MNL_ALIGN(len) (((len)+MNL_ALIGNTO-1) & ~(MNL_ALIGNTO-1))
86             #define MNL_NLMSG_HDRLEN MNL_ALIGN(sizeof(struct nlmsghdr))
87             #define MNL_ATTR_HDRLEN MNL_ALIGN(sizeof(struct nlattr))
88              
89             enum mnl_attr_data_type {
90             MNL_TYPE_UNSPEC,
91             MNL_TYPE_U8,
92             MNL_TYPE_U16,
93             MNL_TYPE_U32,
94             MNL_TYPE_U64,
95             MNL_TYPE_STRING,
96             MNL_TYPE_FLAG,
97             MNL_TYPE_MSECS,
98             MNL_TYPE_NESTED,
99             MNL_TYPE_NESTED_COMPAT,
100             MNL_TYPE_NUL_STRING,
101             MNL_TYPE_BINARY,
102             MNL_TYPE_MAX,
103             };
104              
105             #define mnl_attr_for_each(attr, nlh, offset) \
106             for ((attr) = mnl_nlmsg_get_payload_offset((nlh), (offset)); \
107             mnl_attr_ok((attr), (char *)mnl_nlmsg_get_payload_tail(nlh) - (char *)(attr)); \
108             (attr) = mnl_attr_next(attr))
109              
110             #define mnl_attr_for_each_nested(attr, nest) \
111             for ((attr) = mnl_attr_get_payload(nest); \
112             mnl_attr_ok((attr), (char *)mnl_attr_get_payload(nest) + mnl_attr_get_payload_len(nest) - (char *)(attr)); \
113             (attr) = mnl_attr_next(attr))
114              
115             #define mnl_attr_for_each_payload(payload, payload_size) \
116             for ((attr) = (payload); \
117             mnl_attr_ok((attr), (char *)(payload) + payload_size - (char *)(attr)); \
118             (attr) = mnl_attr_next(attr))
119              
120             #define MNL_CB_ERROR -1
121             #define MNL_CB_STOP 0
122             #define MNL_CB_OK 1
123              
124             typedef int (*mnl_attr_cb_t)(const struct nlattr *attr, void *data);
125             typedef int (*mnl_cb_t)(const struct nlmsghdr *nlh, void *data);
126              
127             #ifndef MNL_ARRAY_SIZE
128             #define MNL_ARRAY_SIZE(a) (sizeof(a)/sizeof((a)[0]))
129             #endif
130              
131 3           static size_t mnl_ideal_socket_buffer_size(void)
132             {
133             static size_t size = 0;
134              
135 3 100         if (size)
136 2           return size;
137 1           size = (size_t)sysconf(_SC_PAGESIZE);
138 1 50         if (size > 8192)
139 0           size = 8192;
140 1           return size;
141             }
142              
143 0           static size_t mnl_nlmsg_size(size_t len)
144             {
145 0           return len + MNL_NLMSG_HDRLEN;
146             }
147              
148 1           static struct nlmsghdr *mnl_nlmsg_put_header(void *buf)
149             {
150 1           int len = MNL_ALIGN(sizeof(struct nlmsghdr));
151 1           struct nlmsghdr *nlh = buf;
152              
153 1           memset(buf, 0, len);
154 1           nlh->nlmsg_len = len;
155 1           return nlh;
156             }
157              
158 1           static void *mnl_nlmsg_put_extra_header(struct nlmsghdr *nlh, size_t size)
159             {
160 1           char *ptr = (char *)nlh + nlh->nlmsg_len;
161 1           size_t len = MNL_ALIGN(size);
162 1           nlh->nlmsg_len += len;
163 1           memset(ptr, 0, len);
164 1           return ptr;
165             }
166              
167 0           static void *mnl_nlmsg_get_payload(const struct nlmsghdr *nlh)
168             {
169 0           return (void *)nlh + MNL_NLMSG_HDRLEN;
170             }
171              
172 2           static void *mnl_nlmsg_get_payload_offset(const struct nlmsghdr *nlh, size_t offset)
173             {
174 2           return (void *)nlh + MNL_NLMSG_HDRLEN + MNL_ALIGN(offset);
175             }
176              
177 4           static bool mnl_nlmsg_ok(const struct nlmsghdr *nlh, int len)
178             {
179 7 50         return len >= (int)sizeof(struct nlmsghdr) &&
180 7 100         nlh->nlmsg_len >= sizeof(struct nlmsghdr) &&
    50          
181 3           (int)nlh->nlmsg_len <= len;
182             }
183              
184 2           static struct nlmsghdr *mnl_nlmsg_next(const struct nlmsghdr *nlh, int *len)
185             {
186 2           *len -= MNL_ALIGN(nlh->nlmsg_len);
187 2           return (struct nlmsghdr *)((void *)nlh + MNL_ALIGN(nlh->nlmsg_len));
188             }
189              
190 57           static void *mnl_nlmsg_get_payload_tail(const struct nlmsghdr *nlh)
191             {
192 57           return (void *)nlh + MNL_ALIGN(nlh->nlmsg_len);
193             }
194              
195 3           static bool mnl_nlmsg_seq_ok(const struct nlmsghdr *nlh, unsigned int seq)
196             {
197 3 50         return nlh->nlmsg_seq && seq ? nlh->nlmsg_seq == seq : true;
    50          
    50          
198             }
199              
200 3           static bool mnl_nlmsg_portid_ok(const struct nlmsghdr *nlh, unsigned int portid)
201             {
202 3 50         return nlh->nlmsg_pid && portid ? nlh->nlmsg_pid == portid : true;
    50          
    50          
203             }
204              
205 110           static uint16_t mnl_attr_get_type(const struct nlattr *attr)
206             {
207 110           return attr->nla_type & NLA_TYPE_MASK;
208             }
209              
210 2           static uint16_t mnl_attr_get_payload_len(const struct nlattr *attr)
211             {
212 2           return attr->nla_len - MNL_ATTR_HDRLEN;
213             }
214              
215 6           static void *mnl_attr_get_payload(const struct nlattr *attr)
216             {
217 6           return (void *)attr + MNL_ATTR_HDRLEN;
218             }
219              
220 59           static bool mnl_attr_ok(const struct nlattr *attr, int len)
221             {
222 115 50         return len >= (int)sizeof(struct nlattr) &&
223 115 100         attr->nla_len >= sizeof(struct nlattr) &&
    50          
224 56           (int)attr->nla_len <= len;
225             }
226              
227 56           static struct nlattr *mnl_attr_next(const struct nlattr *attr)
228             {
229 56           return (struct nlattr *)((void *)attr + MNL_ALIGN(attr->nla_len));
230             }
231              
232 0           static int mnl_attr_type_valid(const struct nlattr *attr, uint16_t max)
233             {
234 0 0         if (mnl_attr_get_type(attr) > max) {
235 0           errno = EOPNOTSUPP;
236 0           return -1;
237             }
238 0           return 1;
239             }
240              
241 0           static int __mnl_attr_validate(const struct nlattr *attr,
242             enum mnl_attr_data_type type, size_t exp_len)
243             {
244 0           uint16_t attr_len = mnl_attr_get_payload_len(attr);
245 0           const char *attr_data = mnl_attr_get_payload(attr);
246              
247 0 0         if (attr_len < exp_len) {
248 0           errno = ERANGE;
249 0           return -1;
250             }
251 0           switch(type) {
252             case MNL_TYPE_FLAG:
253 0 0         if (attr_len > 0) {
254 0           errno = ERANGE;
255 0           return -1;
256             }
257 0           break;
258             case MNL_TYPE_NUL_STRING:
259 0 0         if (attr_len == 0) {
260 0           errno = ERANGE;
261 0           return -1;
262             }
263 0 0         if (attr_data[attr_len-1] != '\0') {
264 0           errno = EINVAL;
265 0           return -1;
266             }
267 0           break;
268             case MNL_TYPE_STRING:
269 0 0         if (attr_len == 0) {
270 0           errno = ERANGE;
271 0           return -1;
272             }
273 0           break;
274             case MNL_TYPE_NESTED:
275              
276 0 0         if (attr_len == 0)
277 0           break;
278              
279 0 0         if (attr_len < MNL_ATTR_HDRLEN) {
280 0           errno = ERANGE;
281 0           return -1;
282             }
283 0           break;
284             default:
285              
286 0           break;
287             }
288 0 0         if (exp_len && attr_len > exp_len) {
    0          
289 0           errno = ERANGE;
290 0           return -1;
291             }
292 0           return 0;
293             }
294              
295             static const size_t mnl_attr_data_type_len[MNL_TYPE_MAX] = {
296             [MNL_TYPE_U8] = sizeof(uint8_t),
297             [MNL_TYPE_U16] = sizeof(uint16_t),
298             [MNL_TYPE_U32] = sizeof(uint32_t),
299             [MNL_TYPE_U64] = sizeof(uint64_t),
300             [MNL_TYPE_MSECS] = sizeof(uint64_t),
301             };
302              
303 0           static int mnl_attr_validate(const struct nlattr *attr, enum mnl_attr_data_type type)
304             {
305             int exp_len;
306              
307 0 0         if (type >= MNL_TYPE_MAX) {
308 0           errno = EINVAL;
309 0           return -1;
310             }
311 0           exp_len = mnl_attr_data_type_len[type];
312 0           return __mnl_attr_validate(attr, type, exp_len);
313             }
314              
315 2           static int mnl_attr_parse(const struct nlmsghdr *nlh, unsigned int offset,
316             mnl_attr_cb_t cb, void *data)
317             {
318 2           int ret = MNL_CB_OK;
319             const struct nlattr *attr;
320              
321 57 100         mnl_attr_for_each(attr, nlh, offset)
322 55 50         if ((ret = cb(attr, data)) <= MNL_CB_STOP)
323 0           return ret;
324 2           return ret;
325             }
326              
327 1           static int mnl_attr_parse_nested(const struct nlattr *nested, mnl_attr_cb_t cb,
328             void *data)
329             {
330 1           int ret = MNL_CB_OK;
331             const struct nlattr *attr;
332              
333 2 100         mnl_attr_for_each_nested(attr, nested)
334 1 50         if ((ret = cb(attr, data)) <= MNL_CB_STOP)
335 0           return ret;
336 1           return ret;
337             }
338              
339 0           static uint8_t mnl_attr_get_u8(const struct nlattr *attr)
340             {
341 0           return *((uint8_t *)mnl_attr_get_payload(attr));
342             }
343              
344 0           static uint16_t mnl_attr_get_u16(const struct nlattr *attr)
345             {
346 0           return *((uint16_t *)mnl_attr_get_payload(attr));
347             }
348              
349 0           static uint32_t mnl_attr_get_u32(const struct nlattr *attr)
350             {
351 0           return *((uint32_t *)mnl_attr_get_payload(attr));
352             }
353              
354 0           static uint64_t mnl_attr_get_u64(const struct nlattr *attr)
355             {
356             uint64_t tmp;
357 0           memcpy(&tmp, mnl_attr_get_payload(attr), sizeof(tmp));
358 0           return tmp;
359             }
360              
361 3           static const char *mnl_attr_get_str(const struct nlattr *attr)
362             {
363 3           return mnl_attr_get_payload(attr);
364             }
365              
366 0           static void mnl_attr_put(struct nlmsghdr *nlh, uint16_t type, size_t len,
367             const void *data)
368             {
369 0           struct nlattr *attr = mnl_nlmsg_get_payload_tail(nlh);
370 0           uint16_t payload_len = MNL_ALIGN(sizeof(struct nlattr)) + len;
371             int pad;
372              
373 0           attr->nla_type = type;
374 0           attr->nla_len = payload_len;
375 0           memcpy(mnl_attr_get_payload(attr), data, len);
376 0           nlh->nlmsg_len += MNL_ALIGN(payload_len);
377 0           pad = MNL_ALIGN(len) - len;
378 0 0         if (pad > 0)
379 0           memset(mnl_attr_get_payload(attr) + len, 0, pad);
380 0           }
381              
382 0           static void mnl_attr_put_u16(struct nlmsghdr *nlh, uint16_t type, uint16_t data)
383             {
384 0           mnl_attr_put(nlh, type, sizeof(uint16_t), &data);
385 0           }
386              
387 0           static void mnl_attr_put_u32(struct nlmsghdr *nlh, uint16_t type, uint32_t data)
388             {
389 0           mnl_attr_put(nlh, type, sizeof(uint32_t), &data);
390 0           }
391              
392 0           static void mnl_attr_put_strz(struct nlmsghdr *nlh, uint16_t type, const char *data)
393             {
394 0           mnl_attr_put(nlh, type, strlen(data)+1, data);
395 0           }
396              
397 0           static struct nlattr *mnl_attr_nest_start(struct nlmsghdr *nlh, uint16_t type)
398             {
399 0           struct nlattr *start = mnl_nlmsg_get_payload_tail(nlh);
400              
401 0           start->nla_type = NLA_F_NESTED | type;
402 0           nlh->nlmsg_len += MNL_ALIGN(sizeof(struct nlattr));
403 0           return start;
404             }
405              
406 0           static bool mnl_attr_put_check(struct nlmsghdr *nlh, size_t buflen,
407             uint16_t type, size_t len, const void *data)
408             {
409 0 0         if (nlh->nlmsg_len + MNL_ATTR_HDRLEN + MNL_ALIGN(len) > buflen)
410 0           return false;
411 0           mnl_attr_put(nlh, type, len, data);
412 0           return true;
413             }
414              
415 0           static bool mnl_attr_put_u8_check(struct nlmsghdr *nlh, size_t buflen,
416             uint16_t type, uint8_t data)
417             {
418 0           return mnl_attr_put_check(nlh, buflen, type, sizeof(uint8_t), &data);
419             }
420              
421 0           static bool mnl_attr_put_u16_check(struct nlmsghdr *nlh, size_t buflen,
422             uint16_t type, uint16_t data)
423             {
424 0           return mnl_attr_put_check(nlh, buflen, type, sizeof(uint16_t), &data);
425             }
426              
427 0           static bool mnl_attr_put_u32_check(struct nlmsghdr *nlh, size_t buflen,
428             uint16_t type, uint32_t data)
429             {
430 0           return mnl_attr_put_check(nlh, buflen, type, sizeof(uint32_t), &data);
431             }
432              
433 0           static struct nlattr *mnl_attr_nest_start_check(struct nlmsghdr *nlh, size_t buflen,
434             uint16_t type)
435             {
436 0 0         if (nlh->nlmsg_len + MNL_ATTR_HDRLEN > buflen)
437 0           return NULL;
438 0           return mnl_attr_nest_start(nlh, type);
439             }
440              
441 0           static void mnl_attr_nest_end(struct nlmsghdr *nlh, struct nlattr *start)
442             {
443 0           start->nla_len = mnl_nlmsg_get_payload_tail(nlh) - (void *)start;
444 0           }
445              
446 0           static void mnl_attr_nest_cancel(struct nlmsghdr *nlh, struct nlattr *start)
447             {
448 0           nlh->nlmsg_len -= mnl_nlmsg_get_payload_tail(nlh) - (void *)start;
449 0           }
450              
451 0           static int mnl_cb_noop(__attribute__((unused)) const struct nlmsghdr *nlh, __attribute__((unused)) void *data)
452             {
453 0           return MNL_CB_OK;
454             }
455              
456 0           static int mnl_cb_error(const struct nlmsghdr *nlh, __attribute__((unused)) void *data)
457             {
458 0           const struct nlmsgerr *err = mnl_nlmsg_get_payload(nlh);
459              
460 0 0         if (nlh->nlmsg_len < mnl_nlmsg_size(sizeof(struct nlmsgerr))) {
461 0           errno = EBADMSG;
462 0           return MNL_CB_ERROR;
463             }
464              
465 0 0         if (err->error < 0)
466 0           errno = -err->error;
467             else
468 0           errno = err->error;
469              
470 0 0         return err->error == 0 ? MNL_CB_STOP : MNL_CB_ERROR;
471             }
472              
473 1           static int mnl_cb_stop(__attribute__((unused)) const struct nlmsghdr *nlh, __attribute__((unused)) void *data)
474             {
475 1           return MNL_CB_STOP;
476             }
477              
478             static const mnl_cb_t default_cb_array[NLMSG_MIN_TYPE] = {
479             [NLMSG_NOOP] = mnl_cb_noop,
480             [NLMSG_ERROR] = mnl_cb_error,
481             [NLMSG_DONE] = mnl_cb_stop,
482             [NLMSG_OVERRUN] = mnl_cb_noop,
483             };
484              
485 2           static int __mnl_cb_run(const void *buf, size_t numbytes,
486             unsigned int seq, unsigned int portid,
487             mnl_cb_t cb_data, void *data,
488             const mnl_cb_t *cb_ctl_array,
489             unsigned int cb_ctl_array_len)
490             {
491 2           int ret = MNL_CB_OK, len = numbytes;
492 2           const struct nlmsghdr *nlh = buf;
493              
494 4 100         while (mnl_nlmsg_ok(nlh, len)) {
495              
496 3 50         if (!mnl_nlmsg_portid_ok(nlh, portid)) {
497 0           errno = ESRCH;
498 0           return -1;
499             }
500              
501 3 50         if (!mnl_nlmsg_seq_ok(nlh, seq)) {
502 0           errno = EPROTO;
503 0           return -1;
504             }
505              
506 3 50         if (nlh->nlmsg_flags & NLM_F_DUMP_INTR) {
507 0           errno = EINTR;
508 0           return -1;
509             }
510              
511 3 100         if (nlh->nlmsg_type >= NLMSG_MIN_TYPE) {
512 2 50         if (cb_data){
513 2           ret = cb_data(nlh, data);
514 2 50         if (ret <= MNL_CB_STOP)
515 0           goto out;
516             }
517 1 50         } else if (nlh->nlmsg_type < cb_ctl_array_len) {
518 0 0         if (cb_ctl_array && cb_ctl_array[nlh->nlmsg_type]) {
    0          
519 0           ret = cb_ctl_array[nlh->nlmsg_type](nlh, data);
520 0 0         if (ret <= MNL_CB_STOP)
521 0           goto out;
522             }
523 1 50         } else if (default_cb_array[nlh->nlmsg_type]) {
524 1           ret = default_cb_array[nlh->nlmsg_type](nlh, data);
525 1 50         if (ret <= MNL_CB_STOP)
526 1           goto out;
527             }
528 2           nlh = mnl_nlmsg_next(nlh, &len);
529             }
530             out:
531 2           return ret;
532             }
533              
534 0           static int mnl_cb_run2(const void *buf, size_t numbytes, unsigned int seq,
535             unsigned int portid, mnl_cb_t cb_data, void *data,
536             const mnl_cb_t *cb_ctl_array, unsigned int cb_ctl_array_len)
537             {
538 0           return __mnl_cb_run(buf, numbytes, seq, portid, cb_data, data,
539             cb_ctl_array, cb_ctl_array_len);
540             }
541              
542 2           static int mnl_cb_run(const void *buf, size_t numbytes, unsigned int seq,
543             unsigned int portid, mnl_cb_t cb_data, void *data)
544             {
545 2           return __mnl_cb_run(buf, numbytes, seq, portid, cb_data, data, NULL, 0);
546             }
547              
548             struct mnl_socket {
549             int fd;
550             struct sockaddr_nl addr;
551             };
552              
553 1           static unsigned int mnl_socket_get_portid(const struct mnl_socket *nl)
554             {
555 1           return nl->addr.nl_pid;
556             }
557              
558 1           static struct mnl_socket *__mnl_socket_open(int bus, int flags)
559             {
560             struct mnl_socket *nl;
561              
562 1           nl = calloc(1, sizeof(struct mnl_socket));
563 1 50         if (nl == NULL)
564 0           return NULL;
565              
566 1           nl->fd = socket(AF_NETLINK, SOCK_RAW | flags, bus);
567 1 50         if (nl->fd == -1) {
568 0           free(nl);
569 0           return NULL;
570             }
571              
572 1           return nl;
573             }
574              
575 1           static struct mnl_socket *mnl_socket_open(int bus)
576             {
577 1           return __mnl_socket_open(bus, 0);
578             }
579              
580 1           static int mnl_socket_bind(struct mnl_socket *nl, unsigned int groups, pid_t pid)
581             {
582             int ret;
583             socklen_t addr_len;
584              
585 1           nl->addr.nl_family = AF_NETLINK;
586 1           nl->addr.nl_groups = groups;
587 1           nl->addr.nl_pid = pid;
588              
589 1           ret = bind(nl->fd, (struct sockaddr *) &nl->addr, sizeof (nl->addr));
590 1 50         if (ret < 0)
591 0           return ret;
592              
593 1           addr_len = sizeof(nl->addr);
594 1           ret = getsockname(nl->fd, (struct sockaddr *) &nl->addr, &addr_len);
595 1 50         if (ret < 0)
596 0           return ret;
597              
598 1 50         if (addr_len != sizeof(nl->addr)) {
599 0           errno = EINVAL;
600 0           return -1;
601             }
602 1 50         if (nl->addr.nl_family != AF_NETLINK) {
603 0           errno = EINVAL;
604 0           return -1;
605             }
606 1           return 0;
607             }
608              
609 1           static ssize_t mnl_socket_sendto(const struct mnl_socket *nl, const void *buf,
610             size_t len)
611             {
612             static const struct sockaddr_nl snl = {
613             .nl_family = AF_NETLINK
614             };
615 1           return sendto(nl->fd, buf, len, 0,
616             (struct sockaddr *) &snl, sizeof(snl));
617             }
618              
619 2           static ssize_t mnl_socket_recvfrom(const struct mnl_socket *nl, void *buf,
620             size_t bufsiz)
621             {
622             ssize_t ret;
623             struct sockaddr_nl addr;
624 2           struct iovec iov = {
625             .iov_base = buf,
626             .iov_len = bufsiz,
627             };
628 2           struct msghdr msg = {
629             .msg_name = &addr,
630             .msg_namelen = sizeof(struct sockaddr_nl),
631             .msg_iov = &iov,
632             .msg_iovlen = 1,
633             .msg_control = NULL,
634             .msg_controllen = 0,
635             .msg_flags = 0,
636             };
637 2           ret = recvmsg(nl->fd, &msg, 0);
638 2 50         if (ret == -1)
639 0           return ret;
640              
641 2 50         if (msg.msg_flags & MSG_TRUNC) {
642 0           errno = ENOSPC;
643 0           return -1;
644             }
645 2 50         if (msg.msg_namelen != sizeof(struct sockaddr_nl)) {
646 0           errno = EINVAL;
647 0           return -1;
648             }
649 2           return ret;
650             }
651              
652 1           static int mnl_socket_close(struct mnl_socket *nl)
653             {
654 1           int ret = close(nl->fd);
655 1           free(nl);
656 1           return ret;
657             }
658              
659             /* mnlg mini library: */
660              
661             struct mnlg_socket {
662             struct mnl_socket *nl;
663             char *buf;
664             uint16_t id;
665             uint8_t version;
666             unsigned int seq;
667             unsigned int portid;
668             };
669              
670 0           static struct nlmsghdr *__mnlg_msg_prepare(struct mnlg_socket *nlg, uint8_t cmd,
671             uint16_t flags, uint16_t id,
672             uint8_t version)
673             {
674             struct nlmsghdr *nlh;
675             struct genlmsghdr *genl;
676              
677 0           nlh = mnl_nlmsg_put_header(nlg->buf);
678 0           nlh->nlmsg_type = id;
679 0           nlh->nlmsg_flags = flags;
680 0           nlg->seq = time(NULL);
681 0           nlh->nlmsg_seq = nlg->seq;
682              
683 0           genl = mnl_nlmsg_put_extra_header(nlh, sizeof(struct genlmsghdr));
684 0           genl->cmd = cmd;
685 0           genl->version = version;
686              
687 0           return nlh;
688             }
689              
690 0           static struct nlmsghdr *mnlg_msg_prepare(struct mnlg_socket *nlg, uint8_t cmd,
691             uint16_t flags)
692             {
693 0           return __mnlg_msg_prepare(nlg, cmd, flags, nlg->id, nlg->version);
694             }
695              
696 0           static int mnlg_socket_send(struct mnlg_socket *nlg, const struct nlmsghdr *nlh)
697             {
698 0           return mnl_socket_sendto(nlg->nl, nlh, nlh->nlmsg_len);
699             }
700              
701 0           static int mnlg_cb_noop(const struct nlmsghdr *nlh, void *data)
702             {
703             (void)nlh;
704             (void)data;
705 0           return MNL_CB_OK;
706             }
707              
708 0           static int mnlg_cb_error(const struct nlmsghdr *nlh, void *data)
709             {
710 0           const struct nlmsgerr *err = mnl_nlmsg_get_payload(nlh);
711             (void)data;
712              
713 0 0         if (nlh->nlmsg_len < mnl_nlmsg_size(sizeof(struct nlmsgerr))) {
714 0           errno = EBADMSG;
715 0           return MNL_CB_ERROR;
716             }
717             /* Netlink subsystems returns the errno value with different signess */
718 0 0         if (err->error < 0)
719 0           errno = -err->error;
720             else
721 0           errno = err->error;
722              
723 0 0         return err->error == 0 ? MNL_CB_STOP : MNL_CB_ERROR;
724             }
725              
726 0           static int mnlg_cb_stop(const struct nlmsghdr *nlh, void *data)
727             {
728             (void)data;
729 0 0         if (nlh->nlmsg_flags & NLM_F_MULTI && nlh->nlmsg_len == mnl_nlmsg_size(sizeof(int))) {
    0          
730 0           int error = *(int *)mnl_nlmsg_get_payload(nlh);
731             /* Netlink subsystems returns the errno value with different signess */
732 0 0         if (error < 0)
733 0           errno = -error;
734             else
735 0           errno = error;
736              
737 0 0         return error == 0 ? MNL_CB_STOP : MNL_CB_ERROR;
738             }
739 0           return MNL_CB_STOP;
740             }
741              
742             static const mnl_cb_t mnlg_cb_array[] = {
743             [NLMSG_NOOP] = mnlg_cb_noop,
744             [NLMSG_ERROR] = mnlg_cb_error,
745             [NLMSG_DONE] = mnlg_cb_stop,
746             [NLMSG_OVERRUN] = mnlg_cb_noop,
747             };
748              
749 0           static int mnlg_socket_recv_run(struct mnlg_socket *nlg, mnl_cb_t data_cb, void *data)
750             {
751             int err;
752              
753             do {
754 0           err = mnl_socket_recvfrom(nlg->nl, nlg->buf,
755             mnl_ideal_socket_buffer_size());
756 0 0         if (err <= 0)
757 0           break;
758 0           err = mnl_cb_run2(nlg->buf, err, nlg->seq, nlg->portid,
759             data_cb, data, mnlg_cb_array, MNL_ARRAY_SIZE(mnlg_cb_array));
760 0 0         } while (err > 0);
761              
762 0           return err;
763             }
764              
765 0           static int get_family_id_attr_cb(const struct nlattr *attr, void *data)
766             {
767 0           const struct nlattr **tb = data;
768 0           int type = mnl_attr_get_type(attr);
769              
770 0 0         if (mnl_attr_type_valid(attr, CTRL_ATTR_MAX) < 0)
771 0           return MNL_CB_ERROR;
772              
773 0           if (type == CTRL_ATTR_FAMILY_ID &&
774 0           mnl_attr_validate(attr, MNL_TYPE_U16) < 0)
775 0           return MNL_CB_ERROR;
776 0           tb[type] = attr;
777 0           return MNL_CB_OK;
778             }
779              
780 0           static int get_family_id_cb(const struct nlmsghdr *nlh, void *data)
781             {
782 0           uint16_t *p_id = data;
783 0           struct nlattr *tb[CTRL_ATTR_MAX + 1] = { 0 };
784              
785 0           mnl_attr_parse(nlh, sizeof(struct genlmsghdr), get_family_id_attr_cb, tb);
786 0 0         if (!tb[CTRL_ATTR_FAMILY_ID])
787 0           return MNL_CB_ERROR;
788 0           *p_id = mnl_attr_get_u16(tb[CTRL_ATTR_FAMILY_ID]);
789 0           return MNL_CB_OK;
790             }
791              
792 0           static struct mnlg_socket *mnlg_socket_open(const char *family_name, uint8_t version)
793             {
794             struct mnlg_socket *nlg;
795             struct nlmsghdr *nlh;
796             int err;
797              
798 0           nlg = malloc(sizeof(*nlg));
799 0 0         if (!nlg)
800 0           return NULL;
801 0           nlg->id = 0;
802              
803 0           err = -ENOMEM;
804 0           nlg->buf = malloc(mnl_ideal_socket_buffer_size());
805 0 0         if (!nlg->buf)
806 0           goto err_buf_alloc;
807              
808 0           nlg->nl = mnl_socket_open(NETLINK_GENERIC);
809 0 0         if (!nlg->nl) {
810 0           err = -errno;
811 0           goto err_mnl_socket_open;
812             }
813              
814 0 0         if (mnl_socket_bind(nlg->nl, 0, MNL_SOCKET_AUTOPID) < 0) {
815 0           err = -errno;
816 0           goto err_mnl_socket_bind;
817             }
818              
819 0           nlg->portid = mnl_socket_get_portid(nlg->nl);
820              
821 0           nlh = __mnlg_msg_prepare(nlg, CTRL_CMD_GETFAMILY,
822             NLM_F_REQUEST | NLM_F_ACK, GENL_ID_CTRL, 1);
823 0           mnl_attr_put_strz(nlh, CTRL_ATTR_FAMILY_NAME, family_name);
824              
825 0 0         if (mnlg_socket_send(nlg, nlh) < 0) {
826 0           err = -errno;
827 0           goto err_mnlg_socket_send;
828             }
829              
830 0           errno = 0;
831 0 0         if (mnlg_socket_recv_run(nlg, get_family_id_cb, &nlg->id) < 0) {
832 0 0         errno = errno == ENOENT ? EPROTONOSUPPORT : errno;
833 0 0         err = errno ? -errno : -ENOSYS;
834 0           goto err_mnlg_socket_recv_run;
835             }
836              
837 0           nlg->version = version;
838 0           errno = 0;
839 0           return nlg;
840              
841             err_mnlg_socket_recv_run:
842             err_mnlg_socket_send:
843             err_mnl_socket_bind:
844 0           mnl_socket_close(nlg->nl);
845             err_mnl_socket_open:
846 0           free(nlg->buf);
847             err_buf_alloc:
848 0           free(nlg);
849 0           errno = -err;
850 0           return NULL;
851             }
852              
853 0           static void mnlg_socket_close(struct mnlg_socket *nlg)
854             {
855 0           mnl_socket_close(nlg->nl);
856 0           free(nlg->buf);
857 0           free(nlg);
858 0           }
859              
860             /* wireguard-specific parts: */
861              
862             struct string_list {
863             char *buffer;
864             size_t len;
865             size_t cap;
866             };
867              
868 0           static int string_list_add(struct string_list *list, const char *str)
869             {
870 0           size_t len = strlen(str) + 1;
871              
872 0 0         if (len == 1)
873 0           return 0;
874              
875 0 0         if (len >= list->cap - list->len) {
876             char *new_buffer;
877 0           size_t new_cap = list->cap * 2;
878              
879 0 0         if (new_cap < list->len +len + 1)
880 0           new_cap = list->len + len + 1;
881 0           new_buffer = realloc(list->buffer, new_cap);
882 0 0         if (!new_buffer)
883 0           return -errno;
884 0           list->buffer = new_buffer;
885 0           list->cap = new_cap;
886             }
887 0           memcpy(list->buffer + list->len, str, len);
888 0           list->len += len;
889 0           list->buffer[list->len] = '\0';
890 0           return 0;
891             }
892              
893             struct interface {
894             const char *name;
895             bool is_wireguard;
896             };
897              
898 1           static int parse_linkinfo(const struct nlattr *attr, void *data)
899             {
900 1           struct interface *interface = data;
901              
902 1 50         if (mnl_attr_get_type(attr) == IFLA_INFO_KIND && !strcmp(WG_GENL_NAME, mnl_attr_get_str(attr)))
    50          
903 0           interface->is_wireguard = true;
904 1           return MNL_CB_OK;
905             }
906              
907 55           static int parse_infomsg(const struct nlattr *attr, void *data)
908             {
909 55           struct interface *interface = data;
910              
911 55 100         if (mnl_attr_get_type(attr) == IFLA_LINKINFO)
912 1           return mnl_attr_parse_nested(attr, parse_linkinfo, data);
913 54 100         else if (mnl_attr_get_type(attr) == IFLA_IFNAME)
914 2           interface->name = mnl_attr_get_str(attr);
915 54           return MNL_CB_OK;
916             }
917              
918 2           static int read_devices_cb(const struct nlmsghdr *nlh, void *data)
919             {
920 2           struct string_list *list = data;
921 2           struct interface interface = { 0 };
922             int ret;
923              
924 2           ret = mnl_attr_parse(nlh, sizeof(struct ifinfomsg), parse_infomsg, &interface);
925 2 50         if (ret != MNL_CB_OK)
926 0           return ret;
927 2 50         if (interface.name && interface.is_wireguard)
    50          
928 0           ret = string_list_add(list, interface.name);
929 2 50         if (ret < 0)
930 0           return ret;
931 2 50         if (nlh->nlmsg_type != NLMSG_DONE)
932 2           return MNL_CB_OK + 1;
933 2           return MNL_CB_OK;
934             }
935              
936 1           static int fetch_device_names(struct string_list *list)
937             {
938 1           struct mnl_socket *nl = NULL;
939 1           char *rtnl_buffer = NULL;
940             size_t message_len;
941             unsigned int portid, seq;
942             ssize_t len;
943 1           int ret = 0;
944             struct nlmsghdr *nlh;
945             struct ifinfomsg *ifm;
946              
947 1           ret = -ENOMEM;
948 1           rtnl_buffer = calloc(mnl_ideal_socket_buffer_size(), 1);
949 1 50         if (!rtnl_buffer)
950 0           goto cleanup;
951              
952 1           nl = mnl_socket_open(NETLINK_ROUTE);
953 1 50         if (!nl) {
954 0           ret = -errno;
955 0           goto cleanup;
956             }
957              
958 1 50         if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
959 0           ret = -errno;
960 0           goto cleanup;
961             }
962              
963 1           seq = time(NULL);
964 1           portid = mnl_socket_get_portid(nl);
965 1           nlh = mnl_nlmsg_put_header(rtnl_buffer);
966 1           nlh->nlmsg_type = RTM_GETLINK;
967 1           nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_DUMP;
968 1           nlh->nlmsg_seq = seq;
969 1           ifm = mnl_nlmsg_put_extra_header(nlh, sizeof(*ifm));
970 1           ifm->ifi_family = AF_UNSPEC;
971 1           message_len = nlh->nlmsg_len;
972              
973 1 50         if (mnl_socket_sendto(nl, rtnl_buffer, message_len) < 0) {
974 0           ret = -errno;
975 0           goto cleanup;
976             }
977              
978             another:
979 2 50         if ((len = mnl_socket_recvfrom(nl, rtnl_buffer, mnl_ideal_socket_buffer_size())) < 0) {
980 0           ret = -errno;
981 0           goto cleanup;
982             }
983 2 50         if ((len = mnl_cb_run(rtnl_buffer, len, seq, portid, read_devices_cb, list)) < 0) {
984             /* Netlink returns NLM_F_DUMP_INTR if the set of all tunnels changed
985             * during the dump. That's unfortunate, but is pretty common on busy
986             * systems that are adding and removing tunnels all the time. Rather
987             * than retrying, potentially indefinitely, we just work with the
988             * partial results. */
989 0 0         if (errno != EINTR) {
990 0           ret = -errno;
991 0           goto cleanup;
992             }
993             }
994 2 100         if (len == MNL_CB_OK + 1)
995 1           goto another;
996 1           ret = 0;
997              
998             cleanup:
999 1           free(rtnl_buffer);
1000 1 50         if (nl)
1001 1           mnl_socket_close(nl);
1002 1           return ret;
1003             }
1004              
1005 0           static int add_del_iface(const char *ifname, bool add)
1006             {
1007 0           struct mnl_socket *nl = NULL;
1008             char *rtnl_buffer;
1009             ssize_t len;
1010             int ret;
1011             struct nlmsghdr *nlh;
1012             struct ifinfomsg *ifm;
1013             struct nlattr *nest;
1014              
1015 0           rtnl_buffer = calloc(mnl_ideal_socket_buffer_size(), 1);
1016 0 0         if (!rtnl_buffer) {
1017 0           ret = -ENOMEM;
1018 0           goto cleanup;
1019             }
1020              
1021 0           nl = mnl_socket_open(NETLINK_ROUTE);
1022 0 0         if (!nl) {
1023 0           ret = -errno;
1024 0           goto cleanup;
1025             }
1026              
1027 0 0         if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
1028 0           ret = -errno;
1029 0           goto cleanup;
1030             }
1031              
1032 0           nlh = mnl_nlmsg_put_header(rtnl_buffer);
1033 0 0         nlh->nlmsg_type = add ? RTM_NEWLINK : RTM_DELLINK;
1034 0 0         nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | (add ? NLM_F_CREATE | NLM_F_EXCL : 0);
1035 0           nlh->nlmsg_seq = time(NULL);
1036 0           ifm = mnl_nlmsg_put_extra_header(nlh, sizeof(*ifm));
1037 0           ifm->ifi_family = AF_UNSPEC;
1038 0           mnl_attr_put_strz(nlh, IFLA_IFNAME, ifname);
1039 0           nest = mnl_attr_nest_start(nlh, IFLA_LINKINFO);
1040 0           mnl_attr_put_strz(nlh, IFLA_INFO_KIND, WG_GENL_NAME);
1041 0           mnl_attr_nest_end(nlh, nest);
1042              
1043 0 0         if (mnl_socket_sendto(nl, rtnl_buffer, nlh->nlmsg_len) < 0) {
1044 0           ret = -errno;
1045 0           goto cleanup;
1046             }
1047 0 0         if ((len = mnl_socket_recvfrom(nl, rtnl_buffer, mnl_ideal_socket_buffer_size())) < 0) {
1048 0           ret = -errno;
1049 0           goto cleanup;
1050             }
1051 0 0         if (mnl_cb_run(rtnl_buffer, len, nlh->nlmsg_seq, mnl_socket_get_portid(nl), NULL, NULL) < 0) {
1052 0           ret = -errno;
1053 0           goto cleanup;
1054             }
1055 0           ret = 0;
1056              
1057             cleanup:
1058 0           free(rtnl_buffer);
1059 0 0         if (nl)
1060 0           mnl_socket_close(nl);
1061 0           return ret;
1062             }
1063              
1064 0           int wg_set_device(wg_device *dev)
1065             {
1066 0           int ret = 0;
1067 0           wg_peer *peer = NULL;
1068 0           wg_allowedip *allowedip = NULL;
1069             struct nlattr *peers_nest, *peer_nest, *allowedips_nest, *allowedip_nest;
1070             struct nlmsghdr *nlh;
1071             struct mnlg_socket *nlg;
1072              
1073 0           nlg = mnlg_socket_open(WG_GENL_NAME, WG_GENL_VERSION);
1074 0 0         if (!nlg)
1075 0           return -errno;
1076              
1077             again:
1078 0           nlh = mnlg_msg_prepare(nlg, WG_CMD_SET_DEVICE, NLM_F_REQUEST | NLM_F_ACK);
1079 0           mnl_attr_put_strz(nlh, WGDEVICE_A_IFNAME, dev->name);
1080              
1081 0 0         if (!peer) {
1082 0           uint32_t flags = 0;
1083              
1084 0 0         if (dev->flags & WGDEVICE_HAS_PRIVATE_KEY)
1085 0           mnl_attr_put(nlh, WGDEVICE_A_PRIVATE_KEY, sizeof(dev->private_key), dev->private_key);
1086 0 0         if (dev->flags & WGDEVICE_HAS_LISTEN_PORT)
1087 0           mnl_attr_put_u16(nlh, WGDEVICE_A_LISTEN_PORT, dev->listen_port);
1088 0 0         if (dev->flags & WGDEVICE_HAS_FWMARK)
1089 0           mnl_attr_put_u32(nlh, WGDEVICE_A_FWMARK, dev->fwmark);
1090 0 0         if (dev->flags & WGDEVICE_REPLACE_PEERS)
1091 0           flags |= WGDEVICE_F_REPLACE_PEERS;
1092 0 0         if (flags)
1093 0           mnl_attr_put_u32(nlh, WGDEVICE_A_FLAGS, flags);
1094             }
1095 0 0         if (!dev->first_peer)
1096 0           goto send;
1097 0           peers_nest = peer_nest = allowedips_nest = allowedip_nest = NULL;
1098 0           peers_nest = mnl_attr_nest_start(nlh, WGDEVICE_A_PEERS);
1099 0 0         for (peer = peer ? peer : dev->first_peer; peer; peer = peer->next_peer) {
    0          
1100 0           uint32_t flags = 0;
1101              
1102 0           peer_nest = mnl_attr_nest_start_check(nlh, mnl_ideal_socket_buffer_size(), 0);
1103 0 0         if (!peer_nest)
1104 0           goto toobig_peers;
1105 0 0         if (!mnl_attr_put_check(nlh, mnl_ideal_socket_buffer_size(), WGPEER_A_PUBLIC_KEY, sizeof(peer->public_key), peer->public_key))
1106 0           goto toobig_peers;
1107 0 0         if (peer->flags & WGPEER_REMOVE_ME)
1108 0           flags |= WGPEER_F_REMOVE_ME;
1109 0 0         if (!allowedip) {
1110 0 0         if (peer->flags & WGPEER_REPLACE_ALLOWEDIPS)
1111 0           flags |= WGPEER_F_REPLACE_ALLOWEDIPS;
1112 0 0         if (peer->flags & WGPEER_HAS_PRESHARED_KEY) {
1113 0 0         if (!mnl_attr_put_check(nlh, mnl_ideal_socket_buffer_size(), WGPEER_A_PRESHARED_KEY, sizeof(peer->preshared_key), peer->preshared_key))
1114 0           goto toobig_peers;
1115             }
1116 0 0         if (peer->endpoint.addr.sa_family == AF_INET) {
1117 0 0         if (!mnl_attr_put_check(nlh, mnl_ideal_socket_buffer_size(), WGPEER_A_ENDPOINT, sizeof(peer->endpoint.addr4), &peer->endpoint.addr4))
1118 0           goto toobig_peers;
1119 0 0         } else if (peer->endpoint.addr.sa_family == AF_INET6) {
1120 0 0         if (!mnl_attr_put_check(nlh, mnl_ideal_socket_buffer_size(), WGPEER_A_ENDPOINT, sizeof(peer->endpoint.addr6), &peer->endpoint.addr6))
1121 0           goto toobig_peers;
1122             }
1123 0 0         if (peer->flags & WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL) {
1124 0 0         if (!mnl_attr_put_u16_check(nlh, mnl_ideal_socket_buffer_size(), WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL, peer->persistent_keepalive_interval))
1125 0           goto toobig_peers;
1126             }
1127             }
1128 0 0         if (flags) {
1129 0 0         if (!mnl_attr_put_u32_check(nlh, mnl_ideal_socket_buffer_size(), WGPEER_A_FLAGS, flags))
1130 0           goto toobig_peers;
1131             }
1132 0 0         if (peer->first_allowedip) {
1133 0 0         if (!allowedip)
1134 0           allowedip = peer->first_allowedip;
1135 0           allowedips_nest = mnl_attr_nest_start_check(nlh, mnl_ideal_socket_buffer_size(), WGPEER_A_ALLOWEDIPS);
1136 0 0         if (!allowedips_nest)
1137 0           goto toobig_allowedips;
1138 0 0         for (; allowedip; allowedip = allowedip->next_allowedip) {
1139 0           allowedip_nest = mnl_attr_nest_start_check(nlh, mnl_ideal_socket_buffer_size(), 0);
1140 0 0         if (!allowedip_nest)
1141 0           goto toobig_allowedips;
1142 0 0         if (!mnl_attr_put_u16_check(nlh, mnl_ideal_socket_buffer_size(), WGALLOWEDIP_A_FAMILY, allowedip->family))
1143 0           goto toobig_allowedips;
1144 0 0         if (allowedip->family == AF_INET) {
1145 0 0         if (!mnl_attr_put_check(nlh, mnl_ideal_socket_buffer_size(), WGALLOWEDIP_A_IPADDR, sizeof(allowedip->ip4), &allowedip->ip4))
1146 0           goto toobig_allowedips;
1147 0 0         } else if (allowedip->family == AF_INET6) {
1148 0 0         if (!mnl_attr_put_check(nlh, mnl_ideal_socket_buffer_size(), WGALLOWEDIP_A_IPADDR, sizeof(allowedip->ip6), &allowedip->ip6))
1149 0           goto toobig_allowedips;
1150             }
1151 0 0         if (!mnl_attr_put_u8_check(nlh, mnl_ideal_socket_buffer_size(), WGALLOWEDIP_A_CIDR_MASK, allowedip->cidr))
1152 0           goto toobig_allowedips;
1153 0           mnl_attr_nest_end(nlh, allowedip_nest);
1154 0           allowedip_nest = NULL;
1155             }
1156 0           mnl_attr_nest_end(nlh, allowedips_nest);
1157 0           allowedips_nest = NULL;
1158             }
1159              
1160 0           mnl_attr_nest_end(nlh, peer_nest);
1161 0           peer_nest = NULL;
1162             }
1163 0           mnl_attr_nest_end(nlh, peers_nest);
1164 0           peers_nest = NULL;
1165 0           goto send;
1166             toobig_allowedips:
1167 0 0         if (allowedip_nest)
1168 0           mnl_attr_nest_cancel(nlh, allowedip_nest);
1169 0 0         if (allowedips_nest)
1170 0           mnl_attr_nest_end(nlh, allowedips_nest);
1171 0           mnl_attr_nest_end(nlh, peer_nest);
1172 0           mnl_attr_nest_end(nlh, peers_nest);
1173 0           goto send;
1174             toobig_peers:
1175 0 0         if (peer_nest)
1176 0           mnl_attr_nest_cancel(nlh, peer_nest);
1177 0           mnl_attr_nest_end(nlh, peers_nest);
1178 0           goto send;
1179             send:
1180 0 0         if (mnlg_socket_send(nlg, nlh) < 0) {
1181 0           ret = -errno;
1182 0           goto out;
1183             }
1184 0           errno = 0;
1185 0 0         if (mnlg_socket_recv_run(nlg, NULL, NULL) < 0) {
1186 0 0         ret = errno ? -errno : -EINVAL;
1187 0           goto out;
1188             }
1189 0 0         if (peer)
1190 0           goto again;
1191              
1192             out:
1193 0           mnlg_socket_close(nlg);
1194 0           errno = -ret;
1195 0           return ret;
1196             }
1197              
1198 0           static int parse_allowedip(const struct nlattr *attr, void *data)
1199             {
1200 0           wg_allowedip *allowedip = data;
1201              
1202 0           switch (mnl_attr_get_type(attr)) {
1203             case WGALLOWEDIP_A_UNSPEC:
1204 0           break;
1205             case WGALLOWEDIP_A_FAMILY:
1206 0 0         if (!mnl_attr_validate(attr, MNL_TYPE_U16))
1207 0           allowedip->family = mnl_attr_get_u16(attr);
1208 0           break;
1209             case WGALLOWEDIP_A_IPADDR:
1210 0 0         if (mnl_attr_get_payload_len(attr) == sizeof(allowedip->ip4))
1211 0           memcpy(&allowedip->ip4, mnl_attr_get_payload(attr), sizeof(allowedip->ip4));
1212 0 0         else if (mnl_attr_get_payload_len(attr) == sizeof(allowedip->ip6))
1213 0           memcpy(&allowedip->ip6, mnl_attr_get_payload(attr), sizeof(allowedip->ip6));
1214 0           break;
1215             case WGALLOWEDIP_A_CIDR_MASK:
1216 0 0         if (!mnl_attr_validate(attr, MNL_TYPE_U8))
1217 0           allowedip->cidr = mnl_attr_get_u8(attr);
1218 0           break;
1219             }
1220              
1221 0           return MNL_CB_OK;
1222             }
1223              
1224 0           static int parse_allowedips(const struct nlattr *attr, void *data)
1225             {
1226 0           wg_peer *peer = data;
1227 0           wg_allowedip *new_allowedip = calloc(1, sizeof(wg_allowedip));
1228             int ret;
1229              
1230 0 0         if (!new_allowedip)
1231 0           return MNL_CB_ERROR;
1232 0 0         if (!peer->first_allowedip)
1233 0           peer->first_allowedip = peer->last_allowedip = new_allowedip;
1234             else {
1235 0           peer->last_allowedip->next_allowedip = new_allowedip;
1236 0           peer->last_allowedip = new_allowedip;
1237             }
1238 0           ret = mnl_attr_parse_nested(attr, parse_allowedip, new_allowedip);
1239 0 0         if (!ret)
1240 0           return ret;
1241 0 0         if (!((new_allowedip->family == AF_INET && new_allowedip->cidr <= 32) || (new_allowedip->family == AF_INET6 && new_allowedip->cidr <= 128))) {
    0          
    0          
    0          
1242 0           errno = EAFNOSUPPORT;
1243 0           return MNL_CB_ERROR;
1244             }
1245 0           return MNL_CB_OK;
1246             }
1247              
1248 0           bool wg_key_is_zero(const wg_key key)
1249             {
1250 0           volatile uint8_t acc = 0;
1251             unsigned int i;
1252              
1253 0 0         for (i = 0; i < sizeof(wg_key); ++i) {
1254 0           acc |= key[i];
1255 0           __asm__ ("" : "=r" (acc) : "0" (acc));
1256             }
1257 0           return 1 & ((acc - 1) >> 8);
1258             }
1259              
1260 0           static int parse_peer(const struct nlattr *attr, void *data)
1261             {
1262 0           wg_peer *peer = data;
1263              
1264 0           switch (mnl_attr_get_type(attr)) {
1265             case WGPEER_A_UNSPEC:
1266 0           break;
1267             case WGPEER_A_PUBLIC_KEY:
1268 0 0         if (mnl_attr_get_payload_len(attr) == sizeof(peer->public_key)) {
1269 0           memcpy(peer->public_key, mnl_attr_get_payload(attr), sizeof(peer->public_key));
1270 0           peer->flags |= WGPEER_HAS_PUBLIC_KEY;
1271             }
1272 0           break;
1273             case WGPEER_A_PRESHARED_KEY:
1274 0 0         if (mnl_attr_get_payload_len(attr) == sizeof(peer->preshared_key)) {
1275 0           memcpy(peer->preshared_key, mnl_attr_get_payload(attr), sizeof(peer->preshared_key));
1276 0 0         if (!wg_key_is_zero(peer->preshared_key))
1277 0           peer->flags |= WGPEER_HAS_PRESHARED_KEY;
1278             }
1279 0           break;
1280             case WGPEER_A_ENDPOINT: {
1281             struct sockaddr *addr;
1282              
1283 0 0         if (mnl_attr_get_payload_len(attr) < sizeof(*addr))
1284 0           break;
1285 0           addr = mnl_attr_get_payload(attr);
1286 0 0         if (addr->sa_family == AF_INET && mnl_attr_get_payload_len(attr) == sizeof(peer->endpoint.addr4))
    0          
1287 0           memcpy(&peer->endpoint.addr4, addr, sizeof(peer->endpoint.addr4));
1288 0 0         else if (addr->sa_family == AF_INET6 && mnl_attr_get_payload_len(attr) == sizeof(peer->endpoint.addr6))
    0          
1289 0           memcpy(&peer->endpoint.addr6, addr, sizeof(peer->endpoint.addr6));
1290 0           break;
1291             }
1292             case WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL:
1293 0 0         if (!mnl_attr_validate(attr, MNL_TYPE_U16))
1294 0           peer->persistent_keepalive_interval = mnl_attr_get_u16(attr);
1295 0           break;
1296             case WGPEER_A_LAST_HANDSHAKE_TIME:
1297 0 0         if (mnl_attr_get_payload_len(attr) == sizeof(peer->last_handshake_time))
1298 0           memcpy(&peer->last_handshake_time, mnl_attr_get_payload(attr), sizeof(peer->last_handshake_time));
1299 0           break;
1300             case WGPEER_A_RX_BYTES:
1301 0 0         if (!mnl_attr_validate(attr, MNL_TYPE_U64))
1302 0           peer->rx_bytes = mnl_attr_get_u64(attr);
1303 0           break;
1304             case WGPEER_A_TX_BYTES:
1305 0 0         if (!mnl_attr_validate(attr, MNL_TYPE_U64))
1306 0           peer->tx_bytes = mnl_attr_get_u64(attr);
1307 0           break;
1308             case WGPEER_A_ALLOWEDIPS:
1309 0           return mnl_attr_parse_nested(attr, parse_allowedips, peer);
1310             }
1311              
1312 0           return MNL_CB_OK;
1313             }
1314              
1315 0           static int parse_peers(const struct nlattr *attr, void *data)
1316             {
1317 0           wg_device *device = data;
1318 0           wg_peer *new_peer = calloc(1, sizeof(wg_peer));
1319             int ret;
1320              
1321 0 0         if (!new_peer)
1322 0           return MNL_CB_ERROR;
1323 0 0         if (!device->first_peer)
1324 0           device->first_peer = device->last_peer = new_peer;
1325             else {
1326 0           device->last_peer->next_peer = new_peer;
1327 0           device->last_peer = new_peer;
1328             }
1329 0           ret = mnl_attr_parse_nested(attr, parse_peer, new_peer);
1330 0 0         if (!ret)
1331 0           return ret;
1332 0 0         if (!(new_peer->flags & WGPEER_HAS_PUBLIC_KEY)) {
1333 0           errno = ENXIO;
1334 0           return MNL_CB_ERROR;
1335             }
1336 0           return MNL_CB_OK;
1337             }
1338              
1339 0           static int parse_device(const struct nlattr *attr, void *data)
1340             {
1341 0           wg_device *device = data;
1342              
1343 0           switch (mnl_attr_get_type(attr)) {
1344             case WGDEVICE_A_UNSPEC:
1345 0           break;
1346             case WGDEVICE_A_IFINDEX:
1347 0 0         if (!mnl_attr_validate(attr, MNL_TYPE_U32))
1348 0           device->ifindex = mnl_attr_get_u32(attr);
1349 0           break;
1350             case WGDEVICE_A_IFNAME:
1351 0 0         if (!mnl_attr_validate(attr, MNL_TYPE_STRING)) {
1352 0           strncpy(device->name, mnl_attr_get_str(attr), sizeof(device->name) - 1);
1353 0           device->name[sizeof(device->name) - 1] = '\0';
1354             }
1355 0           break;
1356             case WGDEVICE_A_PRIVATE_KEY:
1357 0 0         if (mnl_attr_get_payload_len(attr) == sizeof(device->private_key)) {
1358 0           memcpy(device->private_key, mnl_attr_get_payload(attr), sizeof(device->private_key));
1359 0           device->flags |= WGDEVICE_HAS_PRIVATE_KEY;
1360             }
1361 0           break;
1362             case WGDEVICE_A_PUBLIC_KEY:
1363 0 0         if (mnl_attr_get_payload_len(attr) == sizeof(device->public_key)) {
1364 0           memcpy(device->public_key, mnl_attr_get_payload(attr), sizeof(device->public_key));
1365 0           device->flags |= WGDEVICE_HAS_PUBLIC_KEY;
1366             }
1367 0           break;
1368             case WGDEVICE_A_LISTEN_PORT:
1369 0 0         if (!mnl_attr_validate(attr, MNL_TYPE_U16))
1370 0           device->listen_port = mnl_attr_get_u16(attr);
1371 0           break;
1372             case WGDEVICE_A_FWMARK:
1373 0 0         if (!mnl_attr_validate(attr, MNL_TYPE_U32))
1374 0           device->fwmark = mnl_attr_get_u32(attr);
1375 0           break;
1376             case WGDEVICE_A_PEERS:
1377 0           return mnl_attr_parse_nested(attr, parse_peers, device);
1378             }
1379              
1380 0           return MNL_CB_OK;
1381             }
1382              
1383 0           static int read_device_cb(const struct nlmsghdr *nlh, void *data)
1384             {
1385 0           return mnl_attr_parse(nlh, sizeof(struct genlmsghdr), parse_device, data);
1386             }
1387              
1388 0           static void coalesce_peers(wg_device *device)
1389             {
1390 0           wg_peer *old_next_peer, *peer = device->first_peer;
1391              
1392 0 0         while (peer && peer->next_peer) {
    0          
1393 0 0         if (memcmp(peer->public_key, peer->next_peer->public_key, sizeof(wg_key))) {
1394 0           peer = peer->next_peer;
1395 0           continue;
1396             }
1397 0 0         if (!peer->first_allowedip) {
1398 0           peer->first_allowedip = peer->next_peer->first_allowedip;
1399 0           peer->last_allowedip = peer->next_peer->last_allowedip;
1400             } else {
1401 0           peer->last_allowedip->next_allowedip = peer->next_peer->first_allowedip;
1402 0           peer->last_allowedip = peer->next_peer->last_allowedip;
1403             }
1404 0           old_next_peer = peer->next_peer;
1405 0           peer->next_peer = old_next_peer->next_peer;
1406 0           free(old_next_peer);
1407             }
1408 0           }
1409              
1410 0           int wg_get_device(wg_device **device, const char *device_name)
1411             {
1412 0           int ret = 0;
1413             struct nlmsghdr *nlh;
1414             struct mnlg_socket *nlg;
1415              
1416             try_again:
1417 0           *device = calloc(1, sizeof(wg_device));
1418 0 0         if (!*device)
1419 0           return -errno;
1420              
1421 0           nlg = mnlg_socket_open(WG_GENL_NAME, WG_GENL_VERSION);
1422 0 0         if (!nlg) {
1423 0           wg_free_device(*device);
1424 0           *device = NULL;
1425 0           return -errno;
1426             }
1427              
1428 0           nlh = mnlg_msg_prepare(nlg, WG_CMD_GET_DEVICE, NLM_F_REQUEST | NLM_F_ACK | NLM_F_DUMP);
1429 0           mnl_attr_put_strz(nlh, WGDEVICE_A_IFNAME, device_name);
1430 0 0         if (mnlg_socket_send(nlg, nlh) < 0) {
1431 0           ret = -errno;
1432 0           goto out;
1433             }
1434 0           errno = 0;
1435 0 0         if (mnlg_socket_recv_run(nlg, read_device_cb, *device) < 0) {
1436 0 0         ret = errno ? -errno : -EINVAL;
1437 0           goto out;
1438             }
1439 0           coalesce_peers(*device);
1440              
1441             out:
1442 0 0         if (nlg)
1443 0           mnlg_socket_close(nlg);
1444 0 0         if (ret) {
1445 0           wg_free_device(*device);
1446 0 0         if (ret == -EINTR)
1447 0           goto try_again;
1448 0           *device = NULL;
1449             }
1450 0           errno = -ret;
1451 0           return ret;
1452             }
1453              
1454             /* first\0second\0third\0forth\0last\0\0 */
1455 1           char *wg_list_device_names(void)
1456             {
1457 1           struct string_list list = { 0 };
1458 1           int ret = fetch_device_names(&list);
1459              
1460 1           errno = -ret;
1461 1 50         if (errno) {
1462 0           free(list.buffer);
1463 0           return NULL;
1464             }
1465 1 50         return list.buffer ?: strdup("\0");
1466             }
1467              
1468 0           int wg_add_device(const char *device_name)
1469             {
1470 0           return add_del_iface(device_name, true);
1471             }
1472              
1473 0           int wg_del_device(const char *device_name)
1474             {
1475 0           return add_del_iface(device_name, false);
1476             }
1477              
1478 0           void wg_free_device(wg_device *dev)
1479             {
1480             wg_peer *peer, *np;
1481             wg_allowedip *allowedip, *na;
1482              
1483 0 0         if (!dev)
1484 0           return;
1485 0 0         for (peer = dev->first_peer, np = peer ? peer->next_peer : NULL; peer; peer = np, np = peer ? peer->next_peer : NULL) {
    0          
    0          
1486 0 0         for (allowedip = peer->first_allowedip, na = allowedip ? allowedip->next_allowedip : NULL; allowedip; allowedip = na, na = allowedip ? allowedip->next_allowedip : NULL)
    0          
    0          
1487 0           free(allowedip);
1488 0           free(peer);
1489             }
1490 0           free(dev);
1491             }
1492              
1493 0           static void encode_base64(char dest[static 4], const uint8_t src[static 3])
1494             {
1495 0           const uint8_t input[] = { (src[0] >> 2) & 63, ((src[0] << 4) | (src[1] >> 4)) & 63, ((src[1] << 2) | (src[2] >> 6)) & 63, src[2] & 63 };
1496             unsigned int i;
1497              
1498 0 0         for (i = 0; i < 4; ++i)
1499 0           dest[i] = input[i] + 'A'
1500 0           + (((25 - input[i]) >> 8) & 6)
1501 0           - (((51 - input[i]) >> 8) & 75)
1502 0           - (((61 - input[i]) >> 8) & 15)
1503 0           + (((62 - input[i]) >> 8) & 3);
1504              
1505 0           }
1506              
1507 0           void wg_key_to_base64(wg_key_b64_string base64, const wg_key key)
1508             {
1509             unsigned int i;
1510              
1511 0 0         for (i = 0; i < 32 / 3; ++i)
1512 0           encode_base64(&base64[i * 4], &key[i * 3]);
1513 0           encode_base64(&base64[i * 4], (const uint8_t[]){ key[i * 3 + 0], key[i * 3 + 1], 0 });
1514 0           base64[sizeof(wg_key_b64_string) - 2] = '=';
1515 0           base64[sizeof(wg_key_b64_string) - 1] = '\0';
1516 0           }
1517              
1518 0           static int decode_base64(const char src[static 4])
1519             {
1520 0           int val = 0;
1521             unsigned int i;
1522              
1523 0 0         for (i = 0; i < 4; ++i)
1524 0           val |= (-1
1525 0           + ((((('A' - 1) - src[i]) & (src[i] - ('Z' + 1))) >> 8) & (src[i] - 64))
1526 0           + ((((('a' - 1) - src[i]) & (src[i] - ('z' + 1))) >> 8) & (src[i] - 70))
1527 0           + ((((('0' - 1) - src[i]) & (src[i] - ('9' + 1))) >> 8) & (src[i] + 5))
1528 0           + ((((('+' - 1) - src[i]) & (src[i] - ('+' + 1))) >> 8) & 63)
1529 0           + ((((('/' - 1) - src[i]) & (src[i] - ('/' + 1))) >> 8) & 64)
1530 0           ) << (18 - 6 * i);
1531 0           return val;
1532             }
1533              
1534 0           int wg_key_from_base64(wg_key key, const wg_key_b64_string base64)
1535             {
1536             unsigned int i;
1537             int val;
1538 0           volatile uint8_t ret = 0;
1539              
1540 0 0         if (strlen(base64) != sizeof(wg_key_b64_string) - 1 || base64[sizeof(wg_key_b64_string) - 2] != '=') {
    0          
1541 0           errno = EINVAL;
1542 0           goto out;
1543             }
1544              
1545 0 0         for (i = 0; i < 32 / 3; ++i) {
1546 0           val = decode_base64(&base64[i * 4]);
1547 0           ret |= (uint32_t)val >> 31;
1548 0           key[i * 3 + 0] = (val >> 16) & 0xff;
1549 0           key[i * 3 + 1] = (val >> 8) & 0xff;
1550 0           key[i * 3 + 2] = val & 0xff;
1551             }
1552 0           val = decode_base64((const char[]){ base64[i * 4 + 0], base64[i * 4 + 1], base64[i * 4 + 2], 'A' });
1553 0           ret |= ((uint32_t)val >> 31) | (val & 0xff);
1554 0           key[i * 3 + 0] = (val >> 16) & 0xff;
1555 0           key[i * 3 + 1] = (val >> 8) & 0xff;
1556 0           errno = EINVAL & ~((ret - 1) >> 8);
1557             out:
1558 0           return -errno;
1559             }
1560              
1561             typedef int64_t fe[16];
1562              
1563 12270           static __attribute__((noinline)) void memzero_explicit(void *s, size_t count)
1564             {
1565 12270           memset(s, 0, count);
1566 12270           __asm__ __volatile__("": :"r"(s) :"memory");
1567 12270           }
1568              
1569 12234           static void carry(fe o)
1570             {
1571             int i;
1572              
1573 207978 100         for (i = 0; i < 16; ++i) {
1574 195744 100         o[(i + 1) % 16] += (i == 15 ? 38 : 1) * (o[i] >> 16);
1575 195744           o[i] &= 0xffff;
1576             }
1577 12234           }
1578              
1579 2044           static void cswap(fe p, fe q, int b)
1580             {
1581             int i;
1582 2044           int64_t t, c = ~(b - 1);
1583              
1584 34748 100         for (i = 0; i < 16; ++i) {
1585 32704           t = c & (p[i] ^ q[i]);
1586 32704           p[i] ^= t;
1587 32704           q[i] ^= t;
1588             }
1589              
1590 2044           memzero_explicit(&t, sizeof(t));
1591 2044           memzero_explicit(&c, sizeof(c));
1592 2044           memzero_explicit(&b, sizeof(b));
1593 2044           }
1594              
1595 2           static void pack(uint8_t *o, const fe n)
1596             {
1597             int i, j, b;
1598             fe m, t;
1599              
1600 2           memcpy(t, n, sizeof(t));
1601 2           carry(t);
1602 2           carry(t);
1603 2           carry(t);
1604 6 100         for (j = 0; j < 2; ++j) {
1605 4           m[0] = t[0] - 0xffed;
1606 60 100         for (i = 1; i < 15; ++i) {
1607 56           m[i] = t[i] - 0xffff - ((m[i - 1] >> 16) & 1);
1608 56           m[i - 1] &= 0xffff;
1609             }
1610 4           m[15] = t[15] - 0x7fff - ((m[14] >> 16) & 1);
1611 4           b = (m[15] >> 16) & 1;
1612 4           m[14] &= 0xffff;
1613 4           cswap(t, m, 1 - b);
1614             }
1615 34 100         for (i = 0; i < 16; ++i) {
1616 32           o[2 * i] = t[i] & 0xff;
1617 32           o[2 * i + 1] = t[i] >> 8;
1618             }
1619              
1620 2           memzero_explicit(m, sizeof(m));
1621 2           memzero_explicit(t, sizeof(t));
1622 2           memzero_explicit(&b, sizeof(b));
1623 2           }
1624              
1625 2040           static void add(fe o, const fe a, const fe b)
1626             {
1627             int i;
1628              
1629 34680 100         for (i = 0; i < 16; ++i)
1630 32640           o[i] = a[i] + b[i];
1631 2040           }
1632              
1633 2040           static void subtract(fe o, const fe a, const fe b)
1634             {
1635             int i;
1636              
1637 34680 100         for (i = 0; i < 16; ++i)
1638 32640           o[i] = a[i] - b[i];
1639 2040           }
1640              
1641 6114           static void multmod(fe o, const fe a, const fe b)
1642             {
1643             int i, j;
1644 6114           int64_t t[31] = { 0 };
1645              
1646 103938 100         for (i = 0; i < 16; ++i) {
1647 1663008 100         for (j = 0; j < 16; ++j)
1648 1565184           t[i + j] += a[i] * b[j];
1649             }
1650 97824 100         for (i = 0; i < 15; ++i)
1651 91710           t[i] += 38 * t[i + 16];
1652 6114           memcpy(o, t, sizeof(fe));
1653 6114           carry(o);
1654 6114           carry(o);
1655              
1656 6114           memzero_explicit(t, sizeof(t));
1657 6114           }
1658              
1659 2           static void invert(fe o, const fe i)
1660             {
1661             fe c;
1662             int a;
1663              
1664 2           memcpy(c, i, sizeof(c));
1665 510 100         for (a = 253; a >= 0; --a) {
1666 508           multmod(c, c, c);
1667 508 100         if (a != 2 && a != 4)
    100          
1668 504           multmod(c, c, i);
1669             }
1670 2           memcpy(o, c, sizeof(fe));
1671              
1672 2           memzero_explicit(c, sizeof(c));
1673 2           }
1674              
1675 4           static void clamp_key(uint8_t *z)
1676             {
1677 4           z[31] = (z[31] & 127) | 64;
1678 4           z[0] &= 248;
1679 4           }
1680              
1681 2           void wg_generate_public_key(wg_key public_key, const wg_key private_key)
1682             {
1683             int i, r;
1684             uint8_t z[32];
1685 2           fe a = { 1 }, b = { 9 }, c = { 0 }, d = { 1 }, e, f;
1686              
1687 2           memcpy(z, private_key, sizeof(z));
1688 2           clamp_key(z);
1689              
1690 512 100         for (i = 254; i >= 0; --i) {
1691 510           r = (z[i >> 3] >> (i & 7)) & 1;
1692 510           cswap(a, b, r);
1693 510           cswap(c, d, r);
1694 510           add(e, a, c);
1695 510           subtract(a, a, c);
1696 510           add(c, b, d);
1697 510           subtract(b, b, d);
1698 510           multmod(d, e, e);
1699 510           multmod(f, a, a);
1700 510           multmod(a, c, a);
1701 510           multmod(c, b, e);
1702 510           add(e, a, c);
1703 510           subtract(a, a, c);
1704 510           multmod(b, a, a);
1705 510           subtract(c, d, f);
1706 510           multmod(a, c, (const fe){ 0xdb41, 1 });
1707 510           add(a, a, d);
1708 510           multmod(c, c, a);
1709 510           multmod(a, d, f);
1710 510           multmod(d, b, (const fe){ 9 });
1711 510           multmod(b, e, e);
1712 510           cswap(a, b, r);
1713 510           cswap(c, d, r);
1714             }
1715 2           invert(c, c);
1716 2           multmod(a, a, c);
1717 2           pack(public_key, a);
1718              
1719 2           memzero_explicit(&r, sizeof(r));
1720 2           memzero_explicit(z, sizeof(z));
1721 2           memzero_explicit(a, sizeof(a));
1722 2           memzero_explicit(b, sizeof(b));
1723 2           memzero_explicit(c, sizeof(c));
1724 2           memzero_explicit(d, sizeof(d));
1725 2           memzero_explicit(e, sizeof(e));
1726 2           memzero_explicit(f, sizeof(f));
1727 2           }
1728              
1729 2           void wg_generate_private_key(wg_key private_key)
1730             {
1731 2           wg_generate_preshared_key(private_key);
1732 2           clamp_key(private_key);
1733 2           }
1734              
1735 4           void wg_generate_preshared_key(wg_key preshared_key)
1736             {
1737             ssize_t ret;
1738             size_t i;
1739             int fd;
1740             #if defined(__OpenBSD__) || (defined(__APPLE__) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_12) || (defined(__GLIBC__) && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 25)))
1741             if (!getentropy(preshared_key, sizeof(wg_key)))
1742             return;
1743             #endif
1744             #if defined(__NR_getrandom) && defined(__linux__)
1745             if (syscall(__NR_getrandom, preshared_key, sizeof(wg_key), 0) == sizeof(wg_key))
1746             return;
1747             #endif
1748 4           fd = open("/dev/urandom", O_RDONLY);
1749 4 50         assert(fd >= 0);
1750 8 100         for (i = 0; i < sizeof(wg_key); i += ret) {
1751 4           ret = read(fd, preshared_key + i, sizeof(wg_key) - i);
1752 4 50         assert(ret > 0);
1753             }
1754 4           close(fd);
1755 4           }