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