ipc: freebsd: add initial FreeBSD support
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
This commit is contained in:
		
							parent
							
								
									576e40056d
								
							
						
					
					
						commit
						f51349c52b
					
				| @ -52,6 +52,9 @@ WIREGUARD_TOOLS_VERSION = $(patsubst v%,%,$(shell GIT_DIR="$(PWD)/../.git" git d | ||||
| ifneq ($(WIREGUARD_TOOLS_VERSION),) | ||||
| CFLAGS += -D'WIREGUARD_TOOLS_VERSION="$(WIREGUARD_TOOLS_VERSION)"' | ||||
| endif | ||||
| ifeq ($(PLATFORM),freebsd) | ||||
| LDLIBS += -lnv | ||||
| endif | ||||
| ifeq ($(PLATFORM),haiku) | ||||
| LDLIBS += -lnetwork -lbsd | ||||
| endif | ||||
|  | ||||
							
								
								
									
										349
									
								
								src/ipc-freebsd.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										349
									
								
								src/ipc-freebsd.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,349 @@ | ||||
| // SPDX-License-Identifier: MIT
 | ||||
| /*
 | ||||
|  * Copyright (C) 2015-2021 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved. | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #include <sys/nv.h> | ||||
| #include <sys/sockio.h> | ||||
| #include <dev/if_wg/if_wg.h> | ||||
| 
 | ||||
| #define IPC_SUPPORTS_KERNEL_INTERFACE | ||||
| 
 | ||||
| static int get_dgram_socket(void) | ||||
| { | ||||
| 	static int sock = -1; | ||||
| 	if (sock < 0) | ||||
| 		sock = socket(AF_INET, SOCK_DGRAM, 0); | ||||
| 	return sock; | ||||
| } | ||||
| 
 | ||||
| static int kernel_get_wireguard_interfaces(struct string_list *list) | ||||
| { | ||||
| 	struct ifgroupreq ifgr = { .ifgr_name = "wg" }; | ||||
| 	struct ifg_req *ifg; | ||||
| 	int s = get_dgram_socket(), ret = 0; | ||||
| 
 | ||||
| 	if (s < 0) | ||||
| 		return -errno; | ||||
| 
 | ||||
| 	if (ioctl(s, SIOCGIFGMEMB, (caddr_t)&ifgr) < 0) | ||||
| 		return errno == ENOENT ? 0 : -errno; | ||||
| 
 | ||||
| 	ifgr.ifgr_groups = calloc(1, ifgr.ifgr_len); | ||||
| 	if (!ifgr.ifgr_groups) | ||||
| 		return -errno; | ||||
| 	if (ioctl(s, SIOCGIFGMEMB, (caddr_t)&ifgr) < 0) { | ||||
| 		ret = -errno; | ||||
| 		goto out; | ||||
| 	} | ||||
| 
 | ||||
| 	for (ifg = ifgr.ifgr_groups; ifg && ifgr.ifgr_len > 0; ++ifg) { | ||||
| 		if ((ret = string_list_add(list, ifg->ifgrq_member)) < 0) | ||||
| 			goto out; | ||||
| 		ifgr.ifgr_len -= sizeof(struct ifg_req); | ||||
| 	} | ||||
| 
 | ||||
| out: | ||||
| 	free(ifgr.ifgr_groups); | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static int kernel_get_device(struct wgdevice **device, const char *ifname) | ||||
| { | ||||
| 	struct wg_data_io wgd = { 0 }; | ||||
| 	nvlist_t *nvl_device = NULL; | ||||
| 	const nvlist_t *const *nvl_peers; | ||||
| 	struct wgdevice *dev = NULL; | ||||
| 	size_t size, peer_count, i; | ||||
| 	uint64_t number; | ||||
| 	const void *binary; | ||||
| 	int ret = 0, s; | ||||
| 
 | ||||
| 	*device = NULL; | ||||
| 	s = get_dgram_socket(); | ||||
| 	if (s < 0) | ||||
| 		goto err; | ||||
| 
 | ||||
| 	strlcpy(wgd.wgd_name, ifname, sizeof(wgd.wgd_name)); | ||||
| 	if (ioctl(s, SIOCGWG, &wgd) < 0) | ||||
| 		goto err; | ||||
| 
 | ||||
| 	wgd.wgd_data = malloc(wgd.wgd_size); | ||||
| 	if (!wgd.wgd_data) | ||||
| 		goto err; | ||||
| 	if (ioctl(s, SIOCGWG, &wgd) < 0) | ||||
| 		goto err; | ||||
| 
 | ||||
| 	dev = calloc(1, sizeof(*dev)); | ||||
| 	if (!dev) | ||||
| 		goto err; | ||||
| 	strlcpy(dev->name, ifname, sizeof(dev->name)); | ||||
| 	nvl_device = nvlist_unpack(wgd.wgd_data, wgd.wgd_size, 0); | ||||
| 	if (!nvl_device) | ||||
| 		goto err; | ||||
| 
 | ||||
| 	if (nvlist_exists_number(nvl_device, "listen-port")) { | ||||
| 		number = nvlist_get_number(nvl_device, "listen-port"); | ||||
| 		if (number <= UINT16_MAX) { | ||||
| 			dev->listen_port = number; | ||||
| 			dev->flags |= WGDEVICE_HAS_LISTEN_PORT; | ||||
| 		} | ||||
| 	} | ||||
| 	if (nvlist_exists_number(nvl_device, "user-cookie")) { | ||||
| 		number = nvlist_get_number(nvl_device, "user-cookie"); | ||||
| 		if (number <= UINT32_MAX) { | ||||
| 			dev->fwmark = number; | ||||
| 			dev->flags |= WGDEVICE_HAS_FWMARK; | ||||
| 		} | ||||
| 	} | ||||
| 	if (nvlist_exists_binary(nvl_device, "public-key")) { | ||||
| 		binary = nvlist_get_binary(nvl_device, "public-key", &size); | ||||
| 		if (binary && size == sizeof(dev->public_key)) { | ||||
| 			memcpy(dev->public_key, binary, sizeof(dev->public_key)); | ||||
| 			dev->flags |= WGDEVICE_HAS_PUBLIC_KEY; | ||||
| 		} | ||||
| 	} | ||||
| 	if (nvlist_exists_binary(nvl_device, "private-key")) { | ||||
| 		binary = nvlist_get_binary(nvl_device, "private-key", &size); | ||||
| 		if (binary && size == sizeof(dev->private_key)) { | ||||
| 			memcpy(dev->private_key, binary, sizeof(dev->private_key)); | ||||
| 			dev->flags |= WGDEVICE_HAS_PRIVATE_KEY; | ||||
| 		} | ||||
| 	} | ||||
| 	if (!nvlist_exists_nvlist_array(nvl_device, "peers")) | ||||
| 		goto skip_peers; | ||||
| 	nvl_peers = nvlist_get_nvlist_array(nvl_device, "peers", &peer_count); | ||||
| 	if (!nvl_peers) | ||||
| 		goto skip_peers; | ||||
| 	for (i = 0; i < peer_count; ++i) { | ||||
| 		struct wgpeer *peer; | ||||
| 		struct wgallowedip *aip; | ||||
| 		const nvlist_t *const *nvl_aips; | ||||
| 		size_t aip_count, j; | ||||
| 
 | ||||
| 		peer = calloc(1, sizeof(*peer)); | ||||
| 		if (!peer) | ||||
| 			goto err_peer; | ||||
| 		if (nvlist_exists_binary(nvl_peers[i], "public-key")) { | ||||
| 			binary = nvlist_get_binary(nvl_peers[i], "public-key", &size); | ||||
| 			if (binary && size == sizeof(peer->public_key)) { | ||||
| 				memcpy(peer->public_key, binary, sizeof(peer->public_key)); | ||||
| 				peer->flags |= WGPEER_HAS_PUBLIC_KEY; | ||||
| 			} | ||||
| 		} | ||||
| 		if (nvlist_exists_binary(nvl_peers[i], "preshared-key")) { | ||||
| 			binary = nvlist_get_binary(nvl_peers[i], "preshared-key", &size); | ||||
| 			if (binary && size == sizeof(peer->preshared_key)) { | ||||
| 				memcpy(peer->preshared_key, binary, sizeof(peer->preshared_key)); | ||||
| 				peer->flags |= WGPEER_HAS_PRESHARED_KEY; | ||||
| 			} | ||||
| 		} | ||||
| 		if (nvlist_exists_number(nvl_peers[i], "persistent-keepalive-interval")) { | ||||
| 			number = nvlist_get_number(nvl_peers[i], "persistent-keepalive-interval"); | ||||
| 			if (number <= UINT16_MAX) { | ||||
| 				peer->persistent_keepalive_interval = number; | ||||
| 				peer->flags |= WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL; | ||||
| 			} | ||||
| 		} | ||||
| 		if (nvlist_exists_binary(nvl_peers[i], "endpoint")) { | ||||
| 			const struct sockaddr *endpoint = nvlist_get_binary(nvl_peers[i], "endpoint", &size); | ||||
| 			if (endpoint && size <= sizeof(peer->endpoint) && size >= sizeof(peer->endpoint.addr) && | ||||
| 			    (endpoint->sa_family == AF_INET || endpoint->sa_family == AF_INET6)) | ||||
| 				memcpy(&peer->endpoint.addr, endpoint, size); | ||||
| 		} | ||||
| 		if (nvlist_exists_number(nvl_peers[i], "rx-bytes")) | ||||
| 			peer->rx_bytes = nvlist_get_number(nvl_peers[i], "rx-bytes"); | ||||
| 		if (nvlist_exists_number(nvl_peers[i], "tx-bytes")) | ||||
| 			peer->tx_bytes = nvlist_get_number(nvl_peers[i], "tx-bytes"); | ||||
| 		if (nvlist_exists_binary(nvl_peers[i], "last-handshake-time")) { | ||||
| 			binary = nvlist_get_binary(nvl_peers[i], "last-handshake-time", &size); | ||||
| 			if (binary && size == sizeof(peer->last_handshake_time)) | ||||
| 				memcpy(&peer->last_handshake_time, binary, sizeof(peer->last_handshake_time)); | ||||
| 		} | ||||
| 
 | ||||
| 		if (!nvlist_exists_nvlist_array(nvl_peers[i], "allowed-ips")) | ||||
| 			goto skip_allowed_ips; | ||||
| 		nvl_aips = nvlist_get_nvlist_array(nvl_peers[i], "allowed-ips", &aip_count); | ||||
| 		if (!aip_count || !nvl_aips) | ||||
| 			goto skip_allowed_ips; | ||||
| 		for (j = 0; j < aip_count; ++j) { | ||||
| 			aip = calloc(1, sizeof(*aip)); | ||||
| 			if (!aip) | ||||
| 				goto err_allowed_ips; | ||||
| 			if (!nvlist_exists_number(nvl_aips[j], "cidr")) | ||||
| 				continue; | ||||
| 			number = nvlist_get_number(nvl_aips[j], "cidr"); | ||||
| 			if (nvlist_exists_binary(nvl_aips[j], "ipv4")) { | ||||
| 				binary = nvlist_get_binary(nvl_aips[j], "ipv4", &size); | ||||
| 				if (!binary || number > 32) { | ||||
| 					ret = EINVAL; | ||||
| 					goto err_allowed_ips; | ||||
| 				} | ||||
| 				aip->family = AF_INET; | ||||
| 				aip->cidr = number; | ||||
| 				memcpy(&aip->ip4, binary, sizeof(aip->ip4)); | ||||
| 			} else if (nvlist_exists_binary(nvl_aips[j], "ipv6")) { | ||||
| 				binary = nvlist_get_binary(nvl_aips[j], "ipv6", &size); | ||||
| 				if (!binary || number > 128) { | ||||
| 					ret = EINVAL; | ||||
| 					goto err_allowed_ips; | ||||
| 				} | ||||
| 				aip->family = AF_INET6; | ||||
| 				aip->cidr = number; | ||||
| 				memcpy(&aip->ip6, binary, sizeof(aip->ip6)); | ||||
| 			} else | ||||
| 				continue; | ||||
| 
 | ||||
| 			if (!peer->first_allowedip) | ||||
| 				peer->first_allowedip = aip; | ||||
| 			else | ||||
| 				peer->last_allowedip->next_allowedip = aip; | ||||
| 			peer->last_allowedip = aip; | ||||
| 			continue; | ||||
| 
 | ||||
| 		err_allowed_ips: | ||||
| 			if (!ret) | ||||
| 				ret = -errno; | ||||
| 			free(aip); | ||||
| 			goto err_peer; | ||||
| 		} | ||||
| 	skip_allowed_ips: | ||||
| 		if (!dev->first_peer) | ||||
| 			dev->first_peer = peer; | ||||
| 		else | ||||
| 			dev->last_peer->next_peer = peer; | ||||
| 		dev->last_peer = peer; | ||||
| 		continue; | ||||
| 
 | ||||
| 	err_peer: | ||||
| 		if (!ret) | ||||
| 			ret = -errno; | ||||
| 		free(peer); | ||||
| 		goto err; | ||||
| 	} | ||||
| 
 | ||||
| skip_peers: | ||||
| 	free(wgd.wgd_data); | ||||
| 	nvlist_destroy(nvl_device); | ||||
| 	*device = dev; | ||||
| 	return 0; | ||||
| 
 | ||||
| err: | ||||
| 	if (!ret) | ||||
| 		ret = -errno; | ||||
| 	free(wgd.wgd_data); | ||||
| 	nvlist_destroy(nvl_device); | ||||
| 	free(dev); | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| static int kernel_set_device(struct wgdevice *dev) | ||||
| { | ||||
| 	struct wg_data_io wgd = { 0 }; | ||||
| 	nvlist_t *nvl_device = NULL, **nvl_peers = NULL; | ||||
| 	size_t peer_count = 0, i = 0; | ||||
| 	struct wgpeer *peer; | ||||
| 	int ret = 0, s; | ||||
| 
 | ||||
| 	strlcpy(wgd.wgd_name, dev->name, sizeof(wgd.wgd_name)); | ||||
| 
 | ||||
| 	nvl_device = nvlist_create(0); | ||||
| 	if (!nvl_device) | ||||
| 		goto err; | ||||
| 
 | ||||
| 	for_each_wgpeer(dev, peer) | ||||
| 		++peer_count; | ||||
| 	if (peer_count) { | ||||
| 		nvl_peers = calloc(peer_count, sizeof(*nvl_peers)); | ||||
| 		if (!nvl_peers) | ||||
| 			goto err; | ||||
| 	} | ||||
| 	if (dev->flags & WGDEVICE_HAS_PRIVATE_KEY) | ||||
| 		nvlist_add_binary(nvl_device, "private-key", dev->private_key, sizeof(dev->private_key)); | ||||
| 	if (dev->flags & WGDEVICE_HAS_LISTEN_PORT) | ||||
| 		nvlist_add_number(nvl_device, "listen-port", dev->listen_port); | ||||
| 	if (dev->flags & WGDEVICE_HAS_FWMARK) | ||||
| 		nvlist_add_number(nvl_device, "user-cookie", dev->fwmark); | ||||
| 	if (dev->flags & WGDEVICE_REPLACE_PEERS) | ||||
| 		nvlist_add_bool(nvl_device, "replace-peers", true); | ||||
| 
 | ||||
| 	for_each_wgpeer(dev, peer) { | ||||
| 		size_t aip_count = 0, j = 0; | ||||
| 		nvlist_t **nvl_aips = NULL; | ||||
| 		struct wgallowedip *aip; | ||||
| 
 | ||||
| 		nvl_peers[i]  = nvlist_create(0); | ||||
| 		if (!nvl_peers[i]) | ||||
| 			goto err_peer; | ||||
| 		for_each_wgallowedip(peer, aip) | ||||
| 			++aip_count; | ||||
| 		if (aip_count) { | ||||
| 			nvl_aips = calloc(aip_count, sizeof(*nvl_aips)); | ||||
| 			if (!nvl_aips) | ||||
| 				goto err_peer; | ||||
| 		} | ||||
| 		nvlist_add_binary(nvl_peers[i], "public-key", peer->public_key, sizeof(peer->public_key)); | ||||
| 		if (peer->flags & WGPEER_HAS_PRESHARED_KEY) | ||||
| 			nvlist_add_binary(nvl_peers[i], "preshared-key", peer->preshared_key, sizeof(peer->preshared_key)); | ||||
| 		if (peer->flags & WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL) | ||||
| 			nvlist_add_number(nvl_peers[i], "persistent-keepalive-interval", peer->persistent_keepalive_interval); | ||||
| 		if (peer->endpoint.addr.sa_family == AF_INET || peer->endpoint.addr.sa_family == AF_INET6) | ||||
| 			nvlist_add_binary(nvl_peers[i], "endpoint", &peer->endpoint.addr, peer->endpoint.addr.sa_len); | ||||
| 		if (peer->flags & WGPEER_REPLACE_ALLOWEDIPS) | ||||
| 			nvlist_add_bool(nvl_peers[i], "replace-allowedips", true); | ||||
| 		if (peer->flags & WGPEER_REMOVE_ME) | ||||
| 			nvlist_add_bool(nvl_peers[i], "remove", true); | ||||
| 		for_each_wgallowedip(peer, aip) { | ||||
| 			nvl_aips[j] = nvlist_create(0); | ||||
| 			if (!nvl_aips[j]) | ||||
| 				goto err_peer; | ||||
| 			nvlist_add_number(nvl_aips[j], "cidr", aip->cidr); | ||||
| 			if (aip->family == AF_INET) | ||||
| 				nvlist_add_binary(nvl_aips[j], "ipv4", &aip->ip4, sizeof(aip->ip4)); | ||||
| 			else if (aip->family == AF_INET6) | ||||
| 				nvlist_add_binary(nvl_aips[j], "ipv6", &aip->ip6, sizeof(aip->ip6)); | ||||
| 			++j; | ||||
| 		} | ||||
| 		if (j) { | ||||
| 			nvlist_add_nvlist_array(nvl_peers[i], "allowed-ips", (const nvlist_t *const *)nvl_aips, j); | ||||
| 			for (j = 0; j < aip_count; ++j) | ||||
| 				nvlist_destroy(nvl_aips[j]); | ||||
| 			free(nvl_aips); | ||||
| 		} | ||||
| 		++i; | ||||
| 		continue; | ||||
| 
 | ||||
| 	err_peer: | ||||
| 		ret = -errno; | ||||
| 		for (j = 0; j < aip_count && nvl_aips; ++j) | ||||
| 			nvlist_destroy(nvl_aips[j]); | ||||
| 		free(nvl_aips); | ||||
| 		nvlist_destroy(nvl_peers[i]); | ||||
| 		goto err; | ||||
| 	} | ||||
| 	if (i) { | ||||
| 		nvlist_add_nvlist_array(nvl_device, "peers", (const nvlist_t *const *)nvl_peers, i); | ||||
| 		for (i = 0; i < peer_count; ++i) | ||||
| 			nvlist_destroy(nvl_peers[i]); | ||||
| 		free(nvl_peers); | ||||
| 	} | ||||
| 	wgd.wgd_data = nvlist_pack(nvl_device, &wgd.wgd_size); | ||||
| 	nvlist_destroy(nvl_device); | ||||
| 	if (!wgd.wgd_data) | ||||
| 		goto err; | ||||
| 	s = get_dgram_socket(); | ||||
| 	if (s < 0) | ||||
| 		return -errno; | ||||
| 	return ioctl(s, SIOCSWG, &wgd); | ||||
| 
 | ||||
| err: | ||||
| 	if (!ret) | ||||
| 		ret = -errno; | ||||
| 	for (i = 0; i < peer_count && nvl_peers; ++i) | ||||
| 		nvlist_destroy(nvl_peers[i]); | ||||
| 	free(nvl_peers); | ||||
| 	nvlist_destroy(nvl_device); | ||||
| 	return ret; | ||||
| } | ||||
| @ -45,6 +45,8 @@ static int string_list_add(struct string_list *list, const char *str) | ||||
| #include "ipc-linux.h" | ||||
| #elif defined(__OpenBSD__) | ||||
| #include "ipc-openbsd.h" | ||||
| #elif defined(__FreeBSD__) | ||||
| #include "ipc-freebsd.h" | ||||
| #endif | ||||
| 
 | ||||
| /* first\0second\0third\0forth\0last\0\0 */ | ||||
|  | ||||
							
								
								
									
										16
									
								
								src/uapi/freebsd/dev/if_wg/if_wg.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								src/uapi/freebsd/dev/if_wg/if_wg.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,16 @@ | ||||
| #ifndef __IF_WG_H__ | ||||
| #define __IF_WG_H__ | ||||
| 
 | ||||
| #include <net/if.h> | ||||
| #include <netinet/in.h> | ||||
| 
 | ||||
| struct wg_data_io { | ||||
| 	char wgd_name[IFNAMSIZ]; | ||||
| 	void *wgd_data; | ||||
| 	size_t wgd_size; | ||||
| }; | ||||
| 
 | ||||
| #define SIOCSWG _IOWR('i', 210, struct wg_data_io) | ||||
| #define SIOCGWG _IOWR('i', 211, struct wg_data_io) | ||||
| 
 | ||||
| #endif | ||||
		Reference in New Issue
	
	Block a user
	 Jason A. Donenfeld
						Jason A. Donenfeld