Linux-WireGuard

 view release on metacpan or  search on metacpan

wireguard-tools/contrib/embeddable-wg-library/wireguard.c  view on Meta::CPAN

	}

	if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
		ret = -errno;
		goto cleanup;
	}

	nlh = mnl_nlmsg_put_header(rtnl_buffer);
	nlh->nlmsg_type = add ? RTM_NEWLINK : RTM_DELLINK;
	nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | (add ? NLM_F_CREATE | NLM_F_EXCL : 0);
	nlh->nlmsg_seq = time(NULL);
	ifm = mnl_nlmsg_put_extra_header(nlh, sizeof(*ifm));
	ifm->ifi_family = AF_UNSPEC;
	mnl_attr_put_strz(nlh, IFLA_IFNAME, ifname);
	nest = mnl_attr_nest_start(nlh, IFLA_LINKINFO);
	mnl_attr_put_strz(nlh, IFLA_INFO_KIND, WG_GENL_NAME);
	mnl_attr_nest_end(nlh, nest);

	if (mnl_socket_sendto(nl, rtnl_buffer, nlh->nlmsg_len) < 0) {
		ret = -errno;
		goto cleanup;
	}
	if ((len = mnl_socket_recvfrom(nl, rtnl_buffer, mnl_ideal_socket_buffer_size())) < 0) {
		ret = -errno;
		goto cleanup;
	}
	if (mnl_cb_run(rtnl_buffer, len, nlh->nlmsg_seq, mnl_socket_get_portid(nl), NULL, NULL) < 0) {
		ret = -errno;
		goto cleanup;
	}
	ret = 0;

cleanup:
	free(rtnl_buffer);
	if (nl)
		mnl_socket_close(nl);
	return ret;
}

int wg_set_device(wg_device *dev)
{
	int ret = 0;
	wg_peer *peer = NULL;
	wg_allowedip *allowedip = NULL;
	struct nlattr *peers_nest, *peer_nest, *allowedips_nest, *allowedip_nest;
	struct nlmsghdr *nlh;
	struct mnlg_socket *nlg;

	nlg = mnlg_socket_open(WG_GENL_NAME, WG_GENL_VERSION);
	if (!nlg)
		return -errno;

again:
	nlh = mnlg_msg_prepare(nlg, WG_CMD_SET_DEVICE, NLM_F_REQUEST | NLM_F_ACK);
	mnl_attr_put_strz(nlh, WGDEVICE_A_IFNAME, dev->name);

	if (!peer) {
		uint32_t flags = 0;

		if (dev->flags & WGDEVICE_HAS_PRIVATE_KEY)
			mnl_attr_put(nlh, WGDEVICE_A_PRIVATE_KEY, sizeof(dev->private_key), dev->private_key);
		if (dev->flags & WGDEVICE_HAS_LISTEN_PORT)
			mnl_attr_put_u16(nlh, WGDEVICE_A_LISTEN_PORT, dev->listen_port);
		if (dev->flags & WGDEVICE_HAS_FWMARK)
			mnl_attr_put_u32(nlh, WGDEVICE_A_FWMARK, dev->fwmark);
		if (dev->flags & WGDEVICE_REPLACE_PEERS)
			flags |= WGDEVICE_F_REPLACE_PEERS;
		if (flags)
			mnl_attr_put_u32(nlh, WGDEVICE_A_FLAGS, flags);
	}
	if (!dev->first_peer)
		goto send;
	peers_nest = peer_nest = allowedips_nest = allowedip_nest = NULL;
	peers_nest = mnl_attr_nest_start(nlh, WGDEVICE_A_PEERS);
	for (peer = peer ? peer : dev->first_peer; peer; peer = peer->next_peer) {
		uint32_t flags = 0;

		peer_nest = mnl_attr_nest_start_check(nlh, mnl_ideal_socket_buffer_size(), 0);
		if (!peer_nest)
			goto toobig_peers;
		if (!mnl_attr_put_check(nlh, mnl_ideal_socket_buffer_size(), WGPEER_A_PUBLIC_KEY, sizeof(peer->public_key), peer->public_key))
			goto toobig_peers;
		if (peer->flags & WGPEER_REMOVE_ME)
			flags |= WGPEER_F_REMOVE_ME;
		if (!allowedip) {
			if (peer->flags & WGPEER_REPLACE_ALLOWEDIPS)
				flags |= WGPEER_F_REPLACE_ALLOWEDIPS;
			if (peer->flags & WGPEER_HAS_PRESHARED_KEY) {
				if (!mnl_attr_put_check(nlh, mnl_ideal_socket_buffer_size(), WGPEER_A_PRESHARED_KEY, sizeof(peer->preshared_key), peer->preshared_key))
					goto toobig_peers;
			}
			if (peer->endpoint.addr.sa_family == AF_INET) {
				if (!mnl_attr_put_check(nlh, mnl_ideal_socket_buffer_size(), WGPEER_A_ENDPOINT, sizeof(peer->endpoint.addr4), &peer->endpoint.addr4))
					goto toobig_peers;
			} else if (peer->endpoint.addr.sa_family == AF_INET6) {
				if (!mnl_attr_put_check(nlh, mnl_ideal_socket_buffer_size(), WGPEER_A_ENDPOINT, sizeof(peer->endpoint.addr6), &peer->endpoint.addr6))
					goto toobig_peers;
			}
			if (peer->flags & WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL) {
				if (!mnl_attr_put_u16_check(nlh, mnl_ideal_socket_buffer_size(), WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL, peer->persistent_keepalive_interval))
					goto toobig_peers;
			}
		}
		if (flags) {
			if (!mnl_attr_put_u32_check(nlh, mnl_ideal_socket_buffer_size(), WGPEER_A_FLAGS, flags))
				goto toobig_peers;
		}
		if (peer->first_allowedip) {
			if (!allowedip)
				allowedip = peer->first_allowedip;
			allowedips_nest = mnl_attr_nest_start_check(nlh, mnl_ideal_socket_buffer_size(), WGPEER_A_ALLOWEDIPS);
			if (!allowedips_nest)
				goto toobig_allowedips;
			for (; allowedip; allowedip = allowedip->next_allowedip) {
				allowedip_nest = mnl_attr_nest_start_check(nlh, mnl_ideal_socket_buffer_size(), 0);
				if (!allowedip_nest)
					goto toobig_allowedips;
				if (!mnl_attr_put_u16_check(nlh, mnl_ideal_socket_buffer_size(), WGALLOWEDIP_A_FAMILY, allowedip->family))
					goto toobig_allowedips;
				if (allowedip->family == AF_INET) {
					if (!mnl_attr_put_check(nlh, mnl_ideal_socket_buffer_size(), WGALLOWEDIP_A_IPADDR, sizeof(allowedip->ip4), &allowedip->ip4))

wireguard-tools/contrib/embeddable-wg-library/wireguard.c  view on Meta::CPAN

		if (mnl_attr_get_payload_len(attr) == sizeof(peer->last_handshake_time))
			memcpy(&peer->last_handshake_time, mnl_attr_get_payload(attr), sizeof(peer->last_handshake_time));
		break;
	case WGPEER_A_RX_BYTES:
		if (!mnl_attr_validate(attr, MNL_TYPE_U64))
			peer->rx_bytes = mnl_attr_get_u64(attr);
		break;
	case WGPEER_A_TX_BYTES:
		if (!mnl_attr_validate(attr, MNL_TYPE_U64))
			peer->tx_bytes = mnl_attr_get_u64(attr);
		break;
	case WGPEER_A_ALLOWEDIPS:
		return mnl_attr_parse_nested(attr, parse_allowedips, peer);
	}

	return MNL_CB_OK;
}

static int parse_peers(const struct nlattr *attr, void *data)
{
	wg_device *device = data;
	wg_peer *new_peer = calloc(1, sizeof(wg_peer));
	int ret;

	if (!new_peer)
		return MNL_CB_ERROR;
	if (!device->first_peer)
		device->first_peer = device->last_peer = new_peer;
	else {
		device->last_peer->next_peer = new_peer;
		device->last_peer = new_peer;
	}
	ret = mnl_attr_parse_nested(attr, parse_peer, new_peer);
	if (!ret)
		return ret;
	if (!(new_peer->flags & WGPEER_HAS_PUBLIC_KEY)) {
		errno = ENXIO;
		return MNL_CB_ERROR;
	}
	return MNL_CB_OK;
}

static int parse_device(const struct nlattr *attr, void *data)
{
	wg_device *device = data;

	switch (mnl_attr_get_type(attr)) {
	case WGDEVICE_A_UNSPEC:
		break;
	case WGDEVICE_A_IFINDEX:
		if (!mnl_attr_validate(attr, MNL_TYPE_U32))
			device->ifindex = mnl_attr_get_u32(attr);
		break;
	case WGDEVICE_A_IFNAME:
		if (!mnl_attr_validate(attr, MNL_TYPE_STRING)) {
			strncpy(device->name, mnl_attr_get_str(attr), sizeof(device->name) - 1);
			device->name[sizeof(device->name) - 1] = '\0';
		}
		break;
	case WGDEVICE_A_PRIVATE_KEY:
		if (mnl_attr_get_payload_len(attr) == sizeof(device->private_key)) {
			memcpy(device->private_key, mnl_attr_get_payload(attr), sizeof(device->private_key));
			device->flags |= WGDEVICE_HAS_PRIVATE_KEY;
		}
		break;
	case WGDEVICE_A_PUBLIC_KEY:
		if (mnl_attr_get_payload_len(attr) == sizeof(device->public_key)) {
			memcpy(device->public_key, mnl_attr_get_payload(attr), sizeof(device->public_key));
			device->flags |= WGDEVICE_HAS_PUBLIC_KEY;
		}
		break;
	case WGDEVICE_A_LISTEN_PORT:
		if (!mnl_attr_validate(attr, MNL_TYPE_U16))
			device->listen_port = mnl_attr_get_u16(attr);
		break;
	case WGDEVICE_A_FWMARK:
		if (!mnl_attr_validate(attr, MNL_TYPE_U32))
			device->fwmark = mnl_attr_get_u32(attr);
		break;
	case WGDEVICE_A_PEERS:
		return mnl_attr_parse_nested(attr, parse_peers, device);
	}

	return MNL_CB_OK;
}

static int read_device_cb(const struct nlmsghdr *nlh, void *data)
{
	return mnl_attr_parse(nlh, sizeof(struct genlmsghdr), parse_device, data);
}

static void coalesce_peers(wg_device *device)
{
	wg_peer *old_next_peer, *peer = device->first_peer;

	while (peer && peer->next_peer) {
		if (memcmp(peer->public_key, peer->next_peer->public_key, sizeof(wg_key))) {
			peer = peer->next_peer;
			continue;
		}
		if (!peer->first_allowedip) {
			peer->first_allowedip = peer->next_peer->first_allowedip;
			peer->last_allowedip = peer->next_peer->last_allowedip;
		} else {
			peer->last_allowedip->next_allowedip = peer->next_peer->first_allowedip;
			peer->last_allowedip = peer->next_peer->last_allowedip;
		}
		old_next_peer = peer->next_peer;
		peer->next_peer = old_next_peer->next_peer;
		free(old_next_peer);
	}
}

int wg_get_device(wg_device **device, const char *device_name)
{
	int ret = 0;
	struct nlmsghdr *nlh;
	struct mnlg_socket *nlg;

try_again:
	*device = calloc(1, sizeof(wg_device));
	if (!*device)

wireguard-tools/contrib/embeddable-wg-library/wireguard.c  view on Meta::CPAN

	memzero_explicit(t, sizeof(t));
	memzero_explicit(&b, sizeof(b));
}

static void add(fe o, const fe a, const fe b)
{
	int i;

	for (i = 0; i < 16; ++i)
		o[i] = a[i] + b[i];
}

static void subtract(fe o, const fe a, const fe b)
{
	int i;

	for (i = 0; i < 16; ++i)
		o[i] = a[i] - b[i];
}

static void multmod(fe o, const fe a, const fe b)
{
	int i, j;
	int64_t t[31] = { 0 };

	for (i = 0; i < 16; ++i) {
		for (j = 0; j < 16; ++j)
			t[i + j] += a[i] * b[j];
	}
	for (i = 0; i < 15; ++i)
		t[i] += 38 * t[i + 16];
	memcpy(o, t, sizeof(fe));
	carry(o);
	carry(o);

	memzero_explicit(t, sizeof(t));
}

static void invert(fe o, const fe i)
{
	fe c;
	int a;

	memcpy(c, i, sizeof(c));
	for (a = 253; a >= 0; --a) {
		multmod(c, c, c);
		if (a != 2 && a != 4)
			multmod(c, c, i);
	}
	memcpy(o, c, sizeof(fe));

	memzero_explicit(c, sizeof(c));
}

static void clamp_key(uint8_t *z)
{
	z[31] = (z[31] & 127) | 64;
	z[0] &= 248;
}

void wg_generate_public_key(wg_key public_key, const wg_key private_key)
{
	int i, r;
	uint8_t z[32];
	fe a = { 1 }, b = { 9 }, c = { 0 }, d = { 1 }, e, f;

	memcpy(z, private_key, sizeof(z));
	clamp_key(z);

	for (i = 254; i >= 0; --i) {
		r = (z[i >> 3] >> (i & 7)) & 1;
		cswap(a, b, r);
		cswap(c, d, r);
		add(e, a, c);
		subtract(a, a, c);
		add(c, b, d);
		subtract(b, b, d);
		multmod(d, e, e);
		multmod(f, a, a);
		multmod(a, c, a);
		multmod(c, b, e);
		add(e, a, c);
		subtract(a, a, c);
		multmod(b, a, a);
		subtract(c, d, f);
		multmod(a, c, (const fe){ 0xdb41, 1 });
		add(a, a, d);
		multmod(c, c, a);
		multmod(a, d, f);
		multmod(d, b, (const fe){ 9 });
		multmod(b, e, e);
		cswap(a, b, r);
		cswap(c, d, r);
	}
	invert(c, c);
	multmod(a, a, c);
	pack(public_key, a);

	memzero_explicit(&r, sizeof(r));
	memzero_explicit(z, sizeof(z));
	memzero_explicit(a, sizeof(a));
	memzero_explicit(b, sizeof(b));
	memzero_explicit(c, sizeof(c));
	memzero_explicit(d, sizeof(d));
	memzero_explicit(e, sizeof(e));
	memzero_explicit(f, sizeof(f));
}

void wg_generate_private_key(wg_key private_key)
{
	wg_generate_preshared_key(private_key);
	clamp_key(private_key);
}

void wg_generate_preshared_key(wg_key preshared_key)
{
	ssize_t ret;
	size_t i;
	int fd;
#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)))
	if (!getentropy(preshared_key, sizeof(wg_key)))
		return;
#endif
#if defined(__NR_getrandom) && defined(__linux__)
	if (syscall(__NR_getrandom, preshared_key, sizeof(wg_key), 0) == sizeof(wg_key))
		return;
#endif
	fd = open("/dev/urandom", O_RDONLY);
	assert(fd >= 0);
	for (i = 0; i < sizeof(wg_key); i += ret) {
		ret = read(fd, preshared_key + i, sizeof(wg_key) - i);
		assert(ret > 0);
	}
	close(fd);
}



( run in 1.796 second using v1.01-cache-2.11-cpan-39bf76dae61 )