@@ -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)
{
Attention is currently required from: flichtenheld, plaisthos. Hello plaisthos, flichtenheld, I'd like you to do a code review. Please visit http://gerrit.openvpn.net/c/openvpn/+/815?usp=email to review the following change. Change subject: dco-win: multipeer support ...................................................................... dco-win: multipeer support This is the main commit for dco-win multipeer implementation. This adds concept of "mode" to DCO implementation, which is peer-to-peer or multipeer. Depends on mode, some functions use MP-specific IOCTL commands, which include peer-id as a part of input. The driver initialization accomodates server mode, in which tun device is created before transport. Since on Windows the socket is visible to the kernel only, control channel packets have to be prepended with remote sockaddr of the peer - this allows userspace to distinguish among peers. Sadly there is no reliable way to get peer local address, such as on Linux/FreeBSD, so we have to do a bit of guesswork figure out IP address based on remote IP and local routing table, which may backfire if there are multiple IPs assigned to the same network adapter. However, as for now peer-specific local IP is not used by the driver. We use instead the result of bind() to the listening address. Existing sockethandle_finalize() function has been refactored to accomodate packets with possibly prepended sockaddr. Change-Id: Ia267276d61fa1425ba205f54ba6eb89021f32dba Signed-off-by: Lev Stipakov <lev@openvpn.net> --- M src/openvpn/dco.h M src/openvpn/dco_freebsd.c M src/openvpn/dco_linux.c M src/openvpn/dco_win.c M src/openvpn/dco_win.h M src/openvpn/forward.c M src/openvpn/init.c M src/openvpn/ovpn_dco_win.h M src/openvpn/socket.c M src/openvpn/socket.h 10 files changed, 518 insertions(+), 103 deletions(-) git pull ssh://gerrit.openvpn.net:29418/openvpn refs/changes/15/815/1