#include "easyxs/easyxs.h"
#include "wireguard.h"
#define PERL_NS "Linux::WireGuard"
#define IPV4_STRLEN sizeof( ((struct sockaddr_in*) NULL)->sin_addr.s_addr)
#define IPV6_STRLEN sizeof( ((struct sockaddr_in6*) NULL)->sin6_addr)
static HV* _wgallowedip_to_hv (pTHX_ wg_allowedip* allowedip) {
HV* ip_hv = newHV();
hv_stores(ip_hv, "family", newSVuv(allowedip->family));
SV* addr_sv = NULL;
switch (allowedip->family) {
case AF_INET:
addr_sv = newSVpvn((char*) &allowedip->ip4.s_addr, IPV4_STRLEN);
break;
case AF_INET6:
addr_sv = newSVpvn((char*) &allowedip->ip6.s6_addr, IPV6_STRLEN);
break;
default:
assert(0);
}
hv_stores(ip_hv, "addr", addr_sv);
hv_stores(ip_hv, "cidr", newSVuv(allowedip->cidr));
return ip_hv;
}
static HV* _wgpeer_to_hv (pTHX_ wg_peer *peer) {
wg_allowedip* allowedip;
HV* hv = newHV();
// hv_stores(hv, "flags", newSViv(peer->flags));
hv_stores(hv, "public_key", (peer->flags & WGPEER_HAS_PUBLIC_KEY) ? newSVpvn((char*) peer->public_key, sizeof(peer->public_key)) : &PL_sv_undef);
hv_stores(hv, "preshared_key", (peer->flags & WGPEER_HAS_PRESHARED_KEY) ? newSVpvn( (char*) peer->preshared_key, sizeof(peer->preshared_key)) : &PL_sv_undef);
unsigned endpoint_len = 0;
switch (peer->endpoint.addr.sa_family) {
case 0:
break;
case AF_INET:
endpoint_len = sizeof(struct sockaddr_in);
break;
case AF_INET6:
endpoint_len = sizeof(struct sockaddr_in6);
break;
default:
assert(0);
}
hv_stores(hv, "endpoint", endpoint_len ? newSVpvn((char*) &peer->endpoint, endpoint_len) : &PL_sv_undef);
hv_stores(hv, "rx_bytes", newSVuv(peer->rx_bytes));
hv_stores(hv, "tx_bytes", newSVuv(peer->tx_bytes));
hv_stores(hv, "persistent_keepalive_interval", (peer->flags & WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL) ? newSVuv(peer->persistent_keepalive_interval) : &PL_sv_undef);
hv_stores(hv, "last_handshake_time_sec", newSViv(peer->last_handshake_time.tv_sec));
hv_stores(hv, "last_handshake_time_nsec", newSViv(peer->last_handshake_time.tv_nsec));
AV* allowed_ips = newAV();
hv_stores(hv, "allowed_ips", newRV_noinc((SV*) allowed_ips));
wg_for_each_allowedip(peer, allowedip) {
HV* ip_hv = _wgallowedip_to_hv(aTHX_ allowedip);
av_push(allowed_ips, newRV_noinc((SV*)ip_hv));
}
return hv;
}
static HV* _wgdev_to_hv (pTHX_ wg_device *dev) {
wg_peer *peer;
HV* dev_hv = newHV();
hv_stores(dev_hv, "name", newSVpv(dev->name, 0));
hv_stores(dev_hv, "ifindex", newSVuv(dev->ifindex));
// hv_stores(dev_hv, "flags", newSViv(dev->flags));
hv_stores(dev_hv, "public_key", dev->flags & WGDEVICE_HAS_PUBLIC_KEY ? newSVpvn((char*) dev->public_key, sizeof(dev->public_key)) : &PL_sv_undef);
hv_stores(dev_hv, "private_key", dev->flags & WGDEVICE_HAS_PRIVATE_KEY ? newSVpvn((char*) dev->private_key, sizeof(dev->private_key)) : &PL_sv_undef);
hv_stores(dev_hv, "fwmark", dev->flags & WGDEVICE_HAS_FWMARK ? newSVuv(dev->fwmark) : &PL_sv_undef);
hv_stores(dev_hv, "listen_port", dev->flags & WGDEVICE_HAS_LISTEN_PORT ? newSVuv(dev->listen_port) : &PL_sv_undef);
AV* peers = newAV();
hv_stores(dev_hv, "peers", newRV_noinc((SV*) peers));
wg_for_each_peer(dev, peer) {
HV* peer_hv = _wgpeer_to_hv(aTHX_ peer);
av_push(peers, newRV_noinc((SV*) peer_hv));
}
return dev_hv;
}
// Doesn’t seem to be useful:
#define _LWG_CREATE_CONST_UV(ns, theconst) \
newCONSTSUB(gv_stashpv(ns, 0), #theconst, newSVuv(theconst));
// ----------------------------------------------------------------------
MODULE = Linux::WireGuard PACKAGE = Linux::WireGuard
PROTOTYPES: DISABLE
void
list_device_names()
PPCODE:
char *device_names, *device_name;
size_t len;
device_names = wg_list_device_names();
if (!device_names) {
croak("Failed to retrieve device names: %s", strerror(errno));
}
unsigned count=0;
wg_for_each_device_name(device_names, device_name, len) {
count++;
mXPUSHp(device_name, len);
}
free(device_names);
XSRETURN(count);
SV*
get_device (SV* name_sv)
CODE:
wg_device *dev;
const char* devname = exs_SvPVbyte_nolen(name_sv);
if (wg_get_device(&dev, devname) < 0) {
croak("Failed to retrieve device `%s`: %s", devname, strerror(errno));
}
HV* dev_hv = _wgdev_to_hv(aTHX_ dev);
wg_free_device(dev);
RETVAL = newRV_noinc((SV*) dev_hv);
OUTPUT:
RETVAL
void
add_device (SV* name_sv)
ALIAS:
del_device = 1
CODE:
const char* devname = exs_SvPVbyte_nolen(name_sv);
int result = ix ? wg_del_device(devname) : wg_add_device(devname);
if (result) {
croak("Failed to %s device `%s`: %s", ix ? "delete" : "add", devname, strerror(errno));
}
SV*
generate_private_key()
ALIAS:
generate_preshared_key = 1
CODE:
wg_key key;
if (ix) {
wg_generate_preshared_key(key);
}
else {
wg_generate_private_key(key);
}
RETVAL = newSVpv((char*) key, sizeof(wg_key));
OUTPUT:
RETVAL
SV*
generate_public_key(SV* private_key_sv)
CODE:
wg_key public_key;
if (SvROK(private_key_sv)) {
croak("Reference is nonsensical here!");
}
STRLEN keylen;
const char* private_key_char = SvPVbyte(private_key_sv, keylen);
if (keylen != sizeof(wg_key)) {
croak("Key must be exactly %lu characters, not %lu!", sizeof(wg_key), keylen);
}
wg_generate_public_key(public_key, (const void*) private_key_char);
RETVAL = newSVpv((char*) public_key, sizeof(wg_key));
OUTPUT:
RETVAL