@@ -106,9 +106,10 @@
*
* @param mode the instance operating mode (P2P or multi-peer)
* @param dco the context to initialize
+ * @param dev_node device node, used on Windows to specify certain DCO adapter
* @return true on success, false otherwise
*/
-bool ovpn_dco_init(int mode, dco_context_t *dco);
+bool ovpn_dco_init(int mode, dco_context_t *dco, const char *dev_node);
/**
* Open/create a DCO interface
@@ -284,7 +285,7 @@
}
static inline bool
-ovpn_dco_init(int mode, dco_context_t *dco)
+ovpn_dco_init(int mode, dco_context_t *dco, const char *dev_node)
{
return true;
}
@@ -165,7 +165,7 @@
}
bool
-ovpn_dco_init(int mode, dco_context_t *dco)
+ovpn_dco_init(int mode, dco_context_t *dco, const char *dev_node)
{
if (open_fd(dco) < 0)
{
@@ -391,7 +391,7 @@
}
bool
-ovpn_dco_init(int mode, dco_context_t *dco)
+ovpn_dco_init(int mode, dco_context_t *dco, const char *dev_node)
{
switch (mode)
{
@@ -28,6 +28,7 @@
#include "syshead.h"
#include "dco.h"
+#include "forward.h"
#include "tun.h"
#include "crypto.h"
#include "ssl_common.h"
@@ -42,16 +43,37 @@
#endif
struct tuntap
-create_dco_handle(const char *devname, struct gc_arena *gc)
+create_dco_handle(const char *devname, struct gc_arena *gc, int mode)
{
struct tuntap tt = { .backend_driver = DRIVER_DCO };
const char *device_guid;
tun_open_device(&tt, devname, &device_guid, gc);
+ /* for mp we set mode in ovpn_dco_init(), however for p2p it is too late */
+ tt.dco.mode = (mode == MODE_SERVER) ? dco_mode_mp : dco_mode_p2p;
+
return tt;
}
+/* Sometimes IP Helper API, which we use for setting IP address etc,
+ * complains that interface is not found. Give it some time to settle
+ */
+static void
+dco_wait_ready(DWORD idx)
+{
+ for (int i = 0; i < 20; ++i)
+ {
+ MIB_IPINTERFACE_ROW row = { .InterfaceIndex = idx, .Family = AF_INET };
+ if (GetIpInterfaceEntry(&row) != ERROR_NOT_FOUND)
+ {
+ break;
+ }
+ msg(D_DCO_DEBUG, "interface %ld not yet ready, retrying", idx);
+ Sleep(50);
+ }
+}
+
/**
* Gets version of dco-win driver
*
@@ -104,8 +126,32 @@
}
bool
-ovpn_dco_init(int mode, dco_context_t *dco)
+ovpn_dco_init(int mode, dco_context_t *dco, const char *dev_node)
{
+ /* for p2p dco->mode is already set */
+ dco->mode = (mode == MODE_POINT_TO_POINT) ? dco_mode_p2p : dco_mode_mp;
+
+ if (dco->mode == dco_mode_p2p)
+ {
+ return true;
+ }
+
+ /* open DCO device */
+ struct gc_arena gc = gc_new();
+ const char *device_guid;
+ tun_open_device(dco->tt, dev_node, &device_guid, &gc);
+ gc_free(&gc);
+
+ /* set mp mode */
+ OVPN_MODE m = OVPN_MODE_MP;
+ DWORD bytes_returned = 0;
+ if (!DeviceIoControl(dco->tt->hand, OVPN_IOCTL_SET_MODE, &m, sizeof(m), NULL, 0, &bytes_returned, NULL))
+ {
+ msg(M_ERR, "DeviceIoControl(OVPN_IOCTL_SET_MODE) failed");
+ }
+
+ dco_wait_ready(dco->tt->adapter_index);
+
return true;
}
@@ -116,19 +162,19 @@
return 0;
}
-static void
-dco_wait_ready(DWORD idx)
+void
+dco_p2p_start_vpn(struct tuntap *tt)
{
- for (int i = 0; i < 20; ++i)
+ DWORD bytes_returned = 0;
+ if (!DeviceIoControl(tt->hand, OVPN_IOCTL_START_VPN, NULL, 0, NULL, 0, &bytes_returned, NULL))
{
- MIB_IPINTERFACE_ROW row = {.InterfaceIndex = idx, .Family = AF_INET};
- if (GetIpInterfaceEntry(&row) != ERROR_NOT_FOUND)
- {
- break;
- }
- msg(D_DCO_DEBUG, "interface %ld not yet ready, retrying", idx);
- Sleep(50);
+ msg(M_ERR, "DeviceIoControl(OVPN_IOCTL_START_VPN) failed");
}
+
+ /* Sometimes IP Helper API, which we use for setting IP address etc,
+ * complains that interface is not found. Give it some time to settle
+ */
+ dco_wait_ready(tt->adapter_index);
}
void
@@ -141,17 +187,10 @@
*/
tt->dco.tt = tt;
- DWORD bytes_returned = 0;
- if (!DeviceIoControl(tt->hand, OVPN_IOCTL_START_VPN, NULL, 0, NULL, 0,
- &bytes_returned, NULL))
+ if (tt->dco.mode == dco_mode_p2p)
{
- msg(M_ERR, "DeviceIoControl(OVPN_IOCTL_START_VPN) failed");
+ dco_p2p_start_vpn(tt);
}
-
- /* Sometimes IP Helper API, which we use for setting IP address etc,
- * complains that interface is not found. Give it some time to settle
- */
- dco_wait_ready(tt->adapter_index);
}
static void
@@ -207,14 +246,67 @@
}
void
-dco_create_socket(HANDLE handle, struct addrinfo *remoteaddr, bool bind_local,
- struct addrinfo *bind, int timeout,
- struct signal_info *sig_info)
+dco_mp_start_vpn(HANDLE handle, struct link_socket *sock)
+{
+ msg(D_DCO_DEBUG, "%s", __func__);
+
+ int ai_family = sock->info.lsa->bind_local->ai_family;
+ struct addrinfo *local = sock->info.lsa->bind_local;
+ struct addrinfo *cur = NULL;
+
+ for (cur = local; cur; cur = cur->ai_next)
+ {
+ if (cur->ai_family == ai_family)
+ {
+ break;
+ }
+ }
+ if (!cur)
+ {
+ msg(M_FATAL, "%s: Socket bind failed: Addr to bind has no %s record",
+ __func__, addr_family_name(ai_family));
+ }
+
+ OVPN_MP_START_VPN in, out;
+ in.IPv6Only = sock->info.bind_ipv6_only ? 1 : 0;
+ if (ai_family == AF_INET)
+ {
+ memcpy(&in.ListenAddress.Addr4, cur->ai_addr, sizeof(struct sockaddr_in));
+ }
+ else
+ {
+ memcpy(&in.ListenAddress.Addr6, cur->ai_addr, sizeof(struct sockaddr_in6));
+ }
+
+ /* in multipeer mode control channel packets are prepended with remote peer's sockaddr */
+ sock->sockflags |= SF_PREPEND_SA;
+
+ DWORD bytes_returned = 0;
+ if (!DeviceIoControl(handle, OVPN_IOCTL_MP_START_VPN, &in, sizeof(in), &out, sizeof(out),
+ &bytes_returned, NULL))
+ {
+ msg(M_ERR, "DeviceIoControl(OVPN_IOCTL_MP_START_VPN) failed");
+ }
+
+ if (out.ListenAddress.Addr4.sin_family == AF_INET)
+ {
+ memcpy(&sock->info.lsa->actual.dest.addr.in4, &out.ListenAddress.Addr4, sizeof(struct sockaddr_in));
+ }
+ else
+ {
+ memcpy(&sock->info.lsa->actual.dest.addr.in6, &out.ListenAddress.Addr6, sizeof(struct sockaddr_in6));
+ }
+}
+
+void
+dco_p2p_new_peer(HANDLE handle, struct link_socket *sock, struct signal_info *sig_info)
{
msg(D_DCO_DEBUG, "%s", __func__);
OVPN_NEW_PEER peer = { 0 };
+ struct addrinfo *remoteaddr = sock->info.lsa->current_remote;
+
struct sockaddr *local = NULL;
struct sockaddr *remote = remoteaddr->ai_addr;
@@ -228,9 +320,10 @@
peer.Proto = OVPN_PROTO_UDP;
}
- if (bind_local)
+ if (sock->bind_local)
{
/* Use first local address with correct address family */
+ struct addrinfo *bind = sock->info.lsa->bind_local;
while (bind && !local)
{
if (bind->ai_family == remote->sa_family)
@@ -241,7 +334,7 @@
}
}
- if (bind_local && !local)
+ if (sock->bind_local && !local)
{
msg(M_FATAL, "DCO: Socket bind failed: Address to bind lacks %s record",
addr_family_name(remote->sa_family));
@@ -290,17 +383,131 @@
}
else
{
- dco_connect_wait(handle, &ov, timeout, sig_info);
+ dco_connect_wait(handle, &ov, get_server_poll_remaining_time(sock->server_poll_timeout), sig_info);
}
}
}
+static struct sockaddr *
+dco_get_source_ip_for_dst(const struct sockaddr *dst)
+{
+ SOCKADDR_INET dst_in = { 0 };
+
+ /* Cast the destination address to SOCKADDR_INET */
+ if (dst->sa_family == AF_INET)
+ {
+ dst_in.Ipv4 = *((struct sockaddr_in *)dst);
+ dst_in.si_family = AF_INET;
+ }
+ else if (dst->sa_family == AF_INET6)
+ {
+ dst_in.Ipv6 = *((struct sockaddr_in6 *)dst);
+ dst_in.si_family = AF_INET6;
+ }
+ else
+ {
+ msg(M_FATAL, "%s: unknown dst address family %d", __func__, dst->sa_family);
+ }
+
+ /* Get the best source address */
+ MIB_IPFORWARD_ROW2 row;
+ SOCKADDR_INET best_src = { 0 };
+ DWORD ret = GetBestRoute2(NULL, 0, NULL, &dst_in, 0, &row, &best_src);
+ if (ret != NO_ERROR)
+ {
+ msg(M_WARN, "%s: GetBestRoute2() failed with error: %ld\n", __func__, ret);
+ return NULL;
+ }
+
+ /* Allocate and copy the best source address to return as sockaddr */
+ struct sockaddr *src = NULL;
+ if (best_src.si_family == AF_INET6)
+ {
+ struct sockaddr_in6 *src_in6 = (struct sockaddr_in6 *)malloc(sizeof(struct sockaddr_in6));
+ if (src_in6 == NULL)
+ {
+ msg(M_FATAL, "%s: malloc failed", __func__);
+ }
+ *src_in6 = best_src.Ipv6;
+ src = (struct sockaddr *)src_in6;
+ }
+ else if (best_src.si_family == AF_INET)
+ {
+ struct sockaddr_in *src_in = (struct sockaddr_in *)malloc(sizeof(struct sockaddr_in));
+ if (src_in == NULL)
+ {
+ msg(M_FATAL, "%s: malloc failed", __func__);
+ }
+ *src_in = best_src.Ipv4;
+ src = (struct sockaddr *)src_in;
+ }
+ else
+ {
+ msg(M_FATAL, "%s: unknown src family %d", __func__, best_src.si_family);
+ }
+
+ return src;
+}
+
int
dco_new_peer(dco_context_t *dco, unsigned int peerid, int sd,
struct sockaddr *localaddr, struct sockaddr *remoteaddr,
struct in_addr *vpn_ipv4, struct in6_addr *vpn_ipv6)
{
msg(D_DCO_DEBUG, "%s: peer-id %d, fd %d", __func__, peerid, sd);
+
+ if (dco->mode == dco_mode_p2p)
+ {
+ /* no-op for p2p */
+ return 0;
+ }
+
+ OVPN_MP_NEW_PEER newPeer = {0};
+
+ if (remoteaddr)
+ {
+ struct sockaddr *local = dco_get_source_ip_for_dst(remoteaddr);
+ if (local)
+ {
+ if (local->sa_family == AF_INET)
+ {
+ memcpy(&newPeer.Local.Addr4, local, sizeof(struct sockaddr_in));
+ }
+ else
+ {
+ memcpy(&newPeer.Local.Addr6, local, sizeof(struct sockaddr_in6));
+ }
+ free(local);
+ }
+
+ if (remoteaddr->sa_family == AF_INET)
+ {
+ memcpy(&newPeer.Remote.Addr4, remoteaddr, sizeof(struct sockaddr_in));
+ }
+ else
+ {
+ memcpy(&newPeer.Remote.Addr6, remoteaddr, sizeof(struct sockaddr_in6));
+ }
+ }
+
+ if (vpn_ipv4)
+ {
+ newPeer.VpnAddr4 = *vpn_ipv4;
+ }
+
+ if (vpn_ipv6)
+ {
+ newPeer.VpnAddr6 = *vpn_ipv6;
+ }
+
+ newPeer.PeerId = peerid;
+
+ DWORD bytesReturned;
+ if (!DeviceIoControl(dco->tt->hand, OVPN_IOCTL_MP_NEW_PEER, &newPeer, sizeof(newPeer), NULL, 0, &bytesReturned, NULL))
+ {
+ msg(M_ERR, "DeviceIoControl(OVPN_IOCTL_MP_NEW_PEER) failed");
+ }
+
return 0;
}
@@ -309,9 +516,20 @@
{
msg(D_DCO_DEBUG, "%s: peer-id %d", __func__, peerid);
+ OVPN_MP_DEL_PEER del_peer = { peerid };
+ VOID *buf = NULL;
+ DWORD len = 0;
+ DWORD ioctl = OVPN_IOCTL_DEL_PEER;
+
+ if (dco->mode == dco_mode_mp)
+ {
+ ioctl = OVPN_IOCTL_MP_DEL_PEER;
+ buf = &del_peer;
+ len = sizeof(del_peer);
+ }
+
DWORD bytes_returned = 0;
- if (!DeviceIoControl(dco->tt->hand, OVPN_IOCTL_DEL_PEER, NULL,
- 0, NULL, 0, &bytes_returned, NULL))
+ if (!DeviceIoControl(dco->tt->hand, ioctl, buf, len, NULL, 0, &bytes_returned, NULL))
{
msg(M_WARN | M_ERRNO, "DeviceIoControl(OVPN_IOCTL_DEL_PEER) failed");
return -1;
@@ -326,19 +544,30 @@
msg(D_DCO_DEBUG, "%s: peer-id %d, keepalive %d/%d, mss %d", __func__,
peerid, keepalive_interval, keepalive_timeout, mss);
- OVPN_SET_PEER peer;
+ OVPN_MP_SET_PEER mp_peer = { peerid, keepalive_interval, keepalive_timeout, mss };
+ OVPN_SET_PEER peer = { keepalive_interval, keepalive_timeout, mss };
+ VOID *buf = NULL;
+ DWORD len = 0;
+ DWORD ioctl = (dco->mode == dco_mode_mp) ? OVPN_IOCTL_MP_SET_PEER : OVPN_IOCTL_SET_PEER;
- peer.KeepaliveInterval = keepalive_interval;
- peer.KeepaliveTimeout = keepalive_timeout;
- peer.MSS = mss;
+ if (dco->mode == dco_mode_mp)
+ {
+ buf = &mp_peer;
+ len = sizeof(OVPN_MP_SET_PEER);
+ }
+ else
+ {
+ buf = &peer;
+ len = sizeof(OVPN_SET_PEER);
+ }
DWORD bytes_returned = 0;
- if (!DeviceIoControl(dco->tt->hand, OVPN_IOCTL_SET_PEER, &peer,
- sizeof(peer), NULL, 0, &bytes_returned, NULL))
+ if (!DeviceIoControl(dco->tt->hand, ioctl, buf, len, NULL, 0, &bytes_returned, NULL))
{
- msg(M_WARN | M_ERRNO, "DeviceIoControl(OVPN_IOCTL_SET_PEER) failed");
+ msg(M_WARN | M_ERRNO, "DeviceIoControl(OVPN_IOCTL_MP_SET_PEER) failed");
return -1;
}
+
return 0;
}
@@ -397,9 +626,20 @@
{
msg(D_DCO_DEBUG, "%s: peer-id %d", __func__, peer_id);
+ OVPN_MP_SWAP_KEYS swap = {peer_id};
+ DWORD ioctl = OVPN_IOCTL_SWAP_KEYS;
+ VOID *buf = NULL;
+ DWORD len = 0;
+
+ if (dco->mode == dco_mode_mp)
+ {
+ ioctl = OVPN_IOCTL_MP_SWAP_KEYS;
+ buf = &swap;
+ len = sizeof(swap);
+ }
+
DWORD bytes_returned = 0;
- if (!DeviceIoControl(dco->tt->hand, OVPN_IOCTL_SWAP_KEYS, NULL, 0, NULL, 0,
- &bytes_returned, NULL))
+ if (!DeviceIoControl(dco->tt->hand, ioctl, buf, len, NULL, 0, &bytes_returned, NULL))
{
msg(M_ERR, "DeviceIoControl(OVPN_IOCTL_SWAP_KEYS) failed");
return -1;
@@ -31,19 +31,28 @@
typedef OVPN_KEY_SLOT dco_key_slot_t;
typedef OVPN_CIPHER_ALG dco_cipher_t;
+typedef enum {
+ dco_mode_uninit,
+ dco_mode_p2p,
+ dco_mode_mp
+} dco_mode_type;
+
struct dco_context {
struct tuntap *tt;
+ dco_mode_type mode;
+
};
typedef struct dco_context dco_context_t;
struct tuntap
-create_dco_handle(const char *devname, struct gc_arena *gc);
+create_dco_handle(const char *devname, struct gc_arena *gc, int mode);
void
-dco_create_socket(HANDLE handle, struct addrinfo *remoteaddr, bool bind_local,
- struct addrinfo *bind, int timeout,
- struct signal_info *sig_info);
+dco_mp_start_vpn(HANDLE handle, struct link_socket *sock);
+
+void
+dco_p2p_new_peer(HANDLE handle, struct link_socket *sock, struct signal_info *sig_info);
void
dco_start_tun(struct tuntap *tt);
@@ -1315,7 +1315,8 @@
}
else
{
- sockethandle_t sh = { .is_handle = true, .h = c->c1.tuntap->hand };
+ bool prepend_sa = c->options.mode == MODE_SERVER && dco_enabled(&c->options);
+ sockethandle_t sh = { .is_handle = true, .h = c->c1.tuntap->hand, .prepend_sa = prepend_sa };
sockethandle_finalize(sh, &c->c1.tuntap->reads, &c->c2.buf, NULL);
}
#else /* ifdef _WIN32 */
@@ -1987,7 +1987,7 @@
#endif
if (dco_enabled(&c->options))
{
- ovpn_dco_init(c->mode, &c->c1.tuntap->dco);
+ ovpn_dco_init(c->mode, &c->c1.tuntap->dco, c->options.dev_node);
}
/* open the tun device */
@@ -47,6 +47,23 @@
OVPN_PROTO Proto;
} OVPN_NEW_PEER, * POVPN_NEW_PEER;
+typedef struct _OVPN_MP_NEW_PEER {
+ union {
+ SOCKADDR_IN Addr4;
+ SOCKADDR_IN6 Addr6;
+ } Local;
+
+ union {
+ SOCKADDR_IN Addr4;
+ SOCKADDR_IN6 Addr6;
+ } Remote;
+
+ IN_ADDR VpnAddr4;
+ IN6_ADDR VpnAddr6;
+
+ int PeerId;
+} OVPN_MP_NEW_PEER, * POVPN_MP_NEW_PEER;
+
typedef struct _OVPN_STATS {
LONG LostInControlPackets;
LONG LostOutControlPackets;
@@ -94,10 +111,25 @@
int PeerId;
} OVPN_CRYPTO_DATA, * POVPN_CRYPTO_DATA;
+#define CRYPTO_OPTIONS_AEAD_TAG_END (1<<1)
+#define CRYPTO_OPTIONS_64BIT_PKTID (1<<2)
+
+typedef struct _OVPN_CRYPTO_DATA_V2 {
+ OVPN_CRYPTO_DATA V1;
+ UINT32 CryptoOptions;
+} OVPN_CRYPTO_DATA_V2, * POVPN_CRYPTO_DATA_V2;
+
+typedef struct _OVPN_MP_SET_PEER {
+ int PeerId;
+ LONG KeepaliveInterval;
+ LONG KeepaliveTimeout;
+ LONG MSS;
+} OVPN_MP_SET_PEER, * POVPN_MP_SET_PEER;
+
typedef struct _OVPN_SET_PEER {
- LONG KeepaliveInterval;
- LONG KeepaliveTimeout;
- LONG MSS;
+ LONG KeepaliveInterval;
+ LONG KeepaliveTimeout;
+ LONG MSS;
} OVPN_SET_PEER, * POVPN_SET_PEER;
typedef struct _OVPN_VERSION {
@@ -106,6 +138,50 @@
LONG Patch;
} OVPN_VERSION, * POVPN_VERSION;
+typedef enum {
+ OVPN_MODE_P2P,
+ OVPN_MODE_MP
+} OVPN_MODE;
+
+typedef struct _OVPN_SET_MODE {
+ OVPN_MODE Mode;
+} OVPN_SET_MODE, * POVPN_SET_MODE;
+
+typedef struct _OVPN_MP_START_VPN {
+ union {
+ SOCKADDR_IN Addr4;
+ SOCKADDR_IN6 Addr6;
+ } ListenAddress;
+ int IPv6Only;
+} OVPN_MP_START_VPN, * POVPN_MP_START_VPN;
+
+typedef enum {
+ OVPN_CMD_DEL_PEER,
+ OVPN_CMD_SWAP_KEYS
+} OVPN_NOTIFY_CMD;
+
+typedef enum {
+ OVPN_DEL_PEER_REASON_TEARDOWN,
+ OVPN_DEL_PEER_REASON_USERSPACE,
+ OVPN_DEL_PEER_REASON_EXPIRED,
+ OVPN_DEL_PEER_REASON_TRANSPORT_ERROR,
+ OVPN_DEL_PEER_REASON_TRANSPORT_DISCONNECT
+} OVPN_DEL_PEER_REASON;
+
+typedef struct _OVPN_NOTIFY_EVENT {
+ OVPN_NOTIFY_CMD Cmd;
+ int PeerId;
+ OVPN_DEL_PEER_REASON DelPeerReason;
+} OVPN_NOTIFY_EVENT, * POVPN_NOTIFY_EVENT;
+
+typedef struct _OVPN_MP_DEL_PEER {
+ int PeerId;
+} OVPN_MP_DEL_PEER, * POVPN_MP_DEL_PEER;
+
+typedef struct _OVPN_MP_SWAP_KEYS {
+ int PeerId;
+} OVPN_MP_SWAP_KEYS, * POVPN_MP_SWAP_KEYS;
+
#define OVPN_IOCTL_NEW_PEER CTL_CODE(FILE_DEVICE_UNKNOWN, 1, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define OVPN_IOCTL_GET_STATS CTL_CODE(FILE_DEVICE_UNKNOWN, 2, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define OVPN_IOCTL_NEW_KEY CTL_CODE(FILE_DEVICE_UNKNOWN, 3, METHOD_BUFFERED, FILE_ANY_ACCESS)
@@ -114,3 +190,14 @@
#define OVPN_IOCTL_START_VPN CTL_CODE(FILE_DEVICE_UNKNOWN, 6, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define OVPN_IOCTL_DEL_PEER CTL_CODE(FILE_DEVICE_UNKNOWN, 7, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define OVPN_IOCTL_GET_VERSION CTL_CODE(FILE_DEVICE_UNKNOWN, 8, METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define OVPN_IOCTL_NEW_KEY_V2 CTL_CODE(FILE_DEVICE_UNKNOWN, 9, METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define OVPN_IOCTL_SET_MODE CTL_CODE(FILE_DEVICE_UNKNOWN, 10, METHOD_BUFFERED, FILE_ANY_ACCESS)
+
+#define OVPN_IOCTL_MP_START_VPN CTL_CODE(FILE_DEVICE_UNKNOWN, 11, METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define OVPN_IOCTL_MP_NEW_PEER CTL_CODE(FILE_DEVICE_UNKNOWN, 12, METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define OVPN_IOCTL_MP_SET_PEER CTL_CODE(FILE_DEVICE_UNKNOWN, 13, METHOD_BUFFERED, FILE_ANY_ACCESS)
+
+#define OVPN_IOCTL_NOTIFY_EVENT CTL_CODE(FILE_DEVICE_UNKNOWN, 14, METHOD_BUFFERED, FILE_ANY_ACCESS)
+
+#define OVPN_IOCTL_MP_DEL_PEER CTL_CODE(FILE_DEVICE_UNKNOWN, 15, METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define OVPN_IOCTL_MP_SWAP_KEYS CTL_CODE(FILE_DEVICE_UNKNOWN, 16, METHOD_BUFFERED, FILE_ANY_ACCESS)
@@ -2144,7 +2144,7 @@
struct tuntap *tt;
ALLOC_OBJ(tt, struct tuntap);
- *tt = create_dco_handle(c->options.dev_node, &c->gc);
+ *tt = create_dco_handle(c->options.dev_node, &c->gc, c->options.mode);
/* Ensure we can "safely" cast the handle to a socket */
static_assert(sizeof(sock->sd) == sizeof(tt->hand), "HANDLE and SOCKET size differs");
@@ -2152,12 +2152,14 @@
c->c1.tuntap = tt;
}
- dco_create_socket(c->c1.tuntap->hand,
- sock->info.lsa->current_remote,
- sock->bind_local, sock->info.lsa->bind_local,
- get_server_poll_remaining_time(sock->server_poll_timeout),
- sig_info);
-
+ if (c->options.mode == MODE_SERVER)
+ {
+ dco_mp_start_vpn(c->c1.tuntap->hand, sock);
+ }
+ else
+ {
+ dco_p2p_new_peer(c->c1.tuntap->hand, sock, sig_info);
+ }
sock->sockflags |= SF_DCO_WIN;
if (sig_info->signal_received)
@@ -2207,16 +2209,16 @@
resolve_remote(sock, 2, &remote_dynamic, sig_info);
/* If a valid remote has been found, create the socket with its addrinfo */
- if (sock->info.lsa->current_remote)
- {
#if defined(_WIN32)
- if (dco_enabled(&c->options))
- {
- create_socket_dco_win(c, sock, sig_info);
- goto done;
- }
- else
+ if (dco_enabled(&c->options))
+ {
+ create_socket_dco_win(c, sock, sig_info);
+ goto done;
+ }
+ else
#endif
+ {
+ if (sock->info.lsa->current_remote)
{
create_socket(sock, sock->info.lsa->current_remote);
}
@@ -3753,6 +3755,91 @@
return sock->writes.iostate;
}
+void
+read_sockaddr_from_overlapped(struct overlapped_io *io, struct sockaddr *dst, int overlapped_ret)
+{
+ if (overlapped_ret >= 0 && io->addr_defined)
+ {
+ /* TODO(jjo): streamline this mess */
+ /* in this func we don't have relevant info about the PF_ of this
+ * endpoint, as link_socket_actual will be zero for the 1st received packet
+ *
+ * Test for inets PF_ possible sizes
+ */
+ switch (io->addrlen)
+ {
+ case sizeof(struct sockaddr_in):
+ case sizeof(struct sockaddr_in6):
+ /* TODO(jjo): for some reason (?) I'm getting 24,28 for AF_INET6
+ * under _WIN32*/
+ case sizeof(struct sockaddr_in6) - 4:
+ break;
+
+ default:
+ bad_address_length(io->addrlen, af_addr_size(io->addr.sin_family));
+ }
+
+ switch (io->addr.sin_family)
+ {
+ case AF_INET:
+ memcpy(dst, &io->addr, sizeof(struct sockaddr_in));
+ break;
+
+ case AF_INET6:
+ memcpy(dst, &io->addr, sizeof(struct sockaddr_in));
+ break;
+ }
+ }
+ else
+ {
+ CLEAR(*dst);
+ }
+}
+
+/**
+ * @brief Extracts a sockaddr from a packet payload.
+ *
+ * Reads a sockaddr structure from the start of the packet buffer and writes it to `dst`.
+ *
+ * @param[in] buf Packet buffer containing the payload.
+ * @param[out] dst Destination buffer for the extracted sockaddr.
+ * @return Length of the extracted sockaddr
+ */
+static int
+read_sockaddr_from_packet(struct buffer *buf, struct sockaddr *dst)
+{
+ int sa_len = 0;
+
+ const struct sockaddr *sa = (const struct sockaddr *)BPTR(buf);
+ switch (sa->sa_family)
+ {
+ case AF_INET:
+ if (buf_len(buf) < sizeof(struct sockaddr_in))
+ {
+ msg(M_FATAL, "ERROR: received incoming packet with too short length of %d -- must be at least %zu.", buf_len(buf), sizeof(struct sockaddr_in));
+ }
+ memcpy(dst, sa, sizeof(struct sockaddr_in));
+ buf_advance(buf, sizeof(struct sockaddr_in));
+ sa_len = sizeof(struct sockaddr_in);
+ break;
+
+ case AF_INET6:
+ if (buf_len(buf) < sizeof(struct sockaddr_in6))
+ {
+ msg(M_FATAL, "ERROR: received incoming packet with too short length of %d -- must be at least %zu.", buf_len(buf), sizeof(struct sockaddr_in6));
+ }
+ memcpy(dst, sa, sizeof(struct sockaddr_in6));
+ buf_advance(buf, sizeof(struct sockaddr_in6));
+ sa_len = sizeof(struct sockaddr_in6);
+ break;
+
+ default:
+ msg(M_FATAL, "ERROR: received incoming packet with invalid address family %d.", sa->sa_family);
+ }
+
+ return sa_len;
+}
+
/* Returns the number of bytes successfully read */
int
sockethandle_finalize(sockethandle_t sh,
@@ -3826,45 +3913,14 @@
ASSERT(0);
}
- /* return from address if requested */
+ if (from && ret > 0 && sh.is_handle && sh.prepend_sa)
+ {
+ ret -= read_sockaddr_from_packet(buf, &from->dest.addr.sa);
+ }
+
if (!sh.is_handle && from)
{
- if (ret >= 0 && io->addr_defined)
- {
- /* TODO(jjo): streamline this mess */
- /* in this func we don't have relevant info about the PF_ of this
- * endpoint, as link_socket_actual will be zero for the 1st received packet
- *
- * Test for inets PF_ possible sizes
- */
- switch (io->addrlen)
- {
- case sizeof(struct sockaddr_in):
- case sizeof(struct sockaddr_in6):
- /* TODO(jjo): for some reason (?) I'm getting 24,28 for AF_INET6
- * under _WIN32*/
- case sizeof(struct sockaddr_in6)-4:
- break;
-
- default:
- bad_address_length(io->addrlen, af_addr_size(io->addr.sin_family));
- }
-
- switch (io->addr.sin_family)
- {
- case AF_INET:
- from->dest.addr.in4 = io->addr;
- break;
-
- case AF_INET6:
- from->dest.addr.in6 = io->addr6;
- break;
- }
- }
- else
- {
- CLEAR(from->dest.addr);
- }
+ read_sockaddr_from_overlapped(io, &from->dest.addr.sa, ret);
}
if (buf)
@@ -224,6 +224,7 @@
#define SF_HOST_RANDOMIZE (1<<3)
#define SF_GETADDRINFO_DGRAM (1<<4)
#define SF_DCO_WIN (1<<5)
+#define SF_PREPEND_SA (1<<6)
unsigned int sockflags;
int mark;
const char *bind_dev;
@@ -287,6 +288,7 @@
HANDLE h;
};
bool is_handle;
+ bool prepend_sa; /* are incoming packets prepended with sockaddr? */
} sockethandle_t;
int sockethandle_finalize(sockethandle_t sh,
@@ -1042,6 +1044,7 @@
{
*from = sock->info.lsa->actual;
sh.is_handle = true;
+ sh.prepend_sa = sock->sockflags & SF_PREPEND_SA;
}
return sockethandle_finalize(sh, &sock->reads, buf, from);
}
@@ -1112,6 +1115,24 @@
err = SocketHandleGetLastError(sh);
}
}
+
+ /* dco-win mp requires control packets to be prepended with sockaddr */
+ if (sock->sockflags & SF_PREPEND_SA)
+ {
+ if (to->dest.addr.sa.sa_family == AF_INET)
+ {
+ struct sockaddr_in sa;
+ memcpy(&sa, &to->dest.addr.in4, sizeof(sa));
+ buf_write_prepend(buf, &sa, sizeof(sa));
+ }
+ else
+ {
+ struct sockaddr_in6 sa;
+ memcpy(&sa, &to->dest.addr.in6, sizeof(sa));
+ buf_write_prepend(buf, &sa, sizeof(sa));
+ }
+ }
+
socket_send_queue(sock, buf, to);
if (status < 0)
{