xref: /freebsd/contrib/wireguard-tools/ipc-freebsd.h (revision 5ca8e32633c4ffbbcd6762e5888b6a4ba0708c6c)
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 }
361