Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added experimental support for Level-2 tunnel (Tunnel=ethernet option… #750

Open
wants to merge 6 commits into
base: latestw_all
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions README-TAP-Windows.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@

Experimental tunnel management has been added on Windows. It relies on
TAP-Windows V9 driver, avilable here:

https://build.openvpn.net/downloads/releases/tap-windows-9.23.3-I601-Win10.exe

At the moment (2024/09/23) this patch has been tested only on Windows10-19045
and TAP-Windows driver 9.23.3.601. Most of the tests used client mode
(ssh.exe). Server mode (sshd.exe) has only been tested superficially.

Since TAP driver only supports level-2 tunnel, option "Tunnel=ethernet" must
be provided by "-o" command line option or by configuration file.

A notable difference between Linux TAP and Windows TAP is that on Windows
there is no dynamic TAP device instance creation. This is why we need to
statically create a number of TAP adapter instances by installing TAP driver
for the desired number of times. Each adapter created this way must be
renamed manually, the names must begin with a common prefix ("SSH Tunnel" by
default). SSH will search a free adapter into the set of adapters whose name
begins with the prefix. The number of adapters created in this way is the number
of simultaneous sessions that can be opened. For clients (ssh.exe) one
single instance is usually enough, but for servers (sshd.exe) a largest number
of reserved adapters is required. The name prefix SSH looks for can be
configured by appendig a ":" caracter and the prefix it to "ethernet" string
in "Tunnel" (or "PermitTunnel" sor sshd) option.
(e.g: "Tunnel=ethernet:MY_PREFIX")) . At the moment, only
ASCII characters are allowed, although Windows uses WCHAR for adapter names
(property "FriendlyName").

It is advised to specify "-w any" in command line, and let the program
choose a free adapter. Although it is possible to specify a particular tunnel
device number (the "IfIndex" adapter's property), it isn't handy to get it,
since Windows puts al kind of adapters (TAP, ethernet...) togather, and
enumeration is not intuitive. A way to get it is the powershell command
"Get-NetAdapter".

SSH only establishes the L2 tunnel, it is on the user (both on the client and the
server side) to configure the TAP device (IP addresses, gateway, routes..).

2 changes: 1 addition & 1 deletion clientloop.c
Original file line number Diff line number Diff line change
Expand Up @@ -1861,7 +1861,7 @@ client_request_tun_fwd(struct ssh *ssh, int tun_mode,
debug("Requesting tun unit %d in mode %d", local_tun, tun_mode);

/* Open local tunnel device */
if ((fd = tun_open(local_tun, tun_mode, &ifname)) == -1) {
if ((fd = tun_open(local_tun, tun_mode, options.tun_options, &ifname)) == -1) {
error("Tunnel device open failed.");
return NULL;
}
Expand Down
1 change: 1 addition & 0 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -770,6 +770,7 @@ int main(void) { if (NSVersionOfRunTimeLibrary("System") >= (60 << 16))
AC_DEFINE_UNQUOTED([BIND_8_COMPAT], [1],
[Define if your resolver libs need this for getrrsetbyname])
AC_DEFINE([SSH_TUN_FREEBSD], [1], [Open tunnel devices the FreeBSD way])
AC_DEFINE([SSH_TUN_TAP_WINDOWS_V9], [1], [Use TAP-Windows Adapter V9 (>=W10 only)])
AC_DEFINE([SSH_TUN_COMPAT_AF], [1],
[Use tunnel device compatibility to OpenBSD])
AC_DEFINE([SSH_TUN_PREPEND_AF], [1],
Expand Down
3 changes: 3 additions & 0 deletions contrib/win32/openssh/config.h.vs
Original file line number Diff line number Diff line change
Expand Up @@ -1474,6 +1474,9 @@
/* Open tunnel devices the FreeBSD way */
/* #undef SSH_TUN_FREEBSD */

/* Use TAP-Windows Adapter V9 (>=W10 only) */
#define SSH_TUN_TAP_WINDOWS_V9 1

/* Open tunnel devices the Linux tun/tap way */
/* #undef SSH_TUN_LINUX */

Expand Down
169 changes: 88 additions & 81 deletions contrib/win32/win32compat/fileio.c
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,7 @@ createFile_flags_setup(int flags, mode_t mode, struct createFile_flags* cf_flags
}

/*only following create and status flags currently supported*/
if (c_s_flags & ~(O_NONBLOCK | O_APPEND | O_CREAT | O_TRUNC | O_EXCL | O_BINARY)) {
if (c_s_flags & ~(O_NONBLOCK | O_APPEND | O_CREAT | O_TRUNC | O_EXCL | O_BINARY | O_SYSTEM)) {
debug3("open - ERROR: Unsupported flags: %d", flags);
errno = ENOTSUP;
return -1;
Expand Down Expand Up @@ -354,7 +354,14 @@ createFile_flags_setup(int flags, mode_t mode, struct createFile_flags* cf_flags
if (c_s_flags & O_APPEND)
cf_flags->dwDesiredAccess = FILE_APPEND_DATA;

cf_flags->dwFlagsAndAttributes = FILE_FLAG_OVERLAPPED | FILE_FLAG_BACKUP_SEMANTICS;
// Hack to deal with TAP driver requirements: FILE_ATTRIBUTE_SYSTEM, no FILE_FLAG_BACKUP_SEMANTICS
if (c_s_flags & O_SYSTEM) {
cf_flags->dwFlagsAndAttributes = FILE_FLAG_OVERLAPPED | FILE_ATTRIBUTE_SYSTEM;
mode = USHRT_MAX;
}
else {
cf_flags->dwFlagsAndAttributes = FILE_FLAG_OVERLAPPED | FILE_FLAG_BACKUP_SEMANTICS;
}

// If the mode is USHRT_MAX then we will inherit the permissions from the parent folder.
if (mode != USHRT_MAX) {
Expand Down Expand Up @@ -427,12 +434,12 @@ file_in_chroot_jail(HANDLE handle) {
if (!final_path)
return 0;

const wchar_t* uncPrefix = L"UNC\\";
int isUNCPath = memcmp(final_path, uncPrefix, 2 * wcslen(uncPrefix));
if (0 == isUNCPath) {
debug3("symlink points to UNCPath");
return 1;
const wchar_t* uncPrefix = L"UNC\\";
int isUNCPath = memcmp(final_path, uncPrefix, 2 * wcslen(uncPrefix));

if (0 == isUNCPath) {
debug3("symlink points to UNCPath");
return 1;
}

to_wlower_case(final_path);
Expand Down Expand Up @@ -682,35 +689,35 @@ WriteCompletionRoutine(_In_ DWORD dwErrorCode,
*((__int64*)&lpOverlapped->Offset) += dwNumberOfBytesTransfered;
}

int
fileio_write_wrapper(struct w32_io* pio, const void* buf, size_t bytes_to_copy)
{
int bytes_written = 0;
if (bytes_to_copy <= WRITE_BUFFER_SIZE) {
bytes_written = fileio_write(pio, buf, bytes_to_copy);
return bytes_written;
}
void* chunk_buf = NULL;
int chunk_count = 0;
int bytes_copied = -1;
size_t chunk_size = 0;
for (int i = 0; i < bytes_to_copy; i += WRITE_BUFFER_SIZE, chunk_count++) {
chunk_buf = (BYTE*)buf + chunk_count * WRITE_BUFFER_SIZE;
chunk_size = ((bytes_to_copy - i) >= WRITE_BUFFER_SIZE) ? WRITE_BUFFER_SIZE : (bytes_to_copy - i);
bytes_written = fileio_write(pio, chunk_buf, chunk_size);
if (bytes_written == -1)
return bytes_copied;
if (bytes_copied == -1)
bytes_copied = 0;
bytes_copied += bytes_written;
}
return bytes_copied;
int
fileio_write_wrapper(struct w32_io* pio, const void* buf, size_t bytes_to_copy)
{
int bytes_written = 0;
if (bytes_to_copy <= WRITE_BUFFER_SIZE) {
bytes_written = fileio_write(pio, buf, bytes_to_copy);
return bytes_written;
}

void* chunk_buf = NULL;
int chunk_count = 0;
int bytes_copied = -1;
size_t chunk_size = 0;

for (int i = 0; i < bytes_to_copy; i += WRITE_BUFFER_SIZE, chunk_count++) {
chunk_buf = (BYTE*)buf + chunk_count * WRITE_BUFFER_SIZE;
chunk_size = ((bytes_to_copy - i) >= WRITE_BUFFER_SIZE) ? WRITE_BUFFER_SIZE : (bytes_to_copy - i);
bytes_written = fileio_write(pio, chunk_buf, chunk_size);

if (bytes_written == -1)
return bytes_copied;

if (bytes_copied == -1)
bytes_copied = 0;

bytes_copied += bytes_written;
}
return bytes_copied;

}

/* write() implementation */
Expand Down Expand Up @@ -947,51 +954,51 @@ fileio_lseek(struct w32_io* pio, unsigned __int64 offset, int origin)
return 0;
}

/* fdopen() to be used on pipe handles */
static FILE*
fileio_fdopen_pipe(struct w32_io* pio, const char *mode)
{
int fd_flags = 0;
FILE* ret;
debug4("fdopen - io:%p", pio);
if (mode[1] == '\0') {
switch (*mode) {
case 'r':
fd_flags = _O_RDONLY;
break;
case 'w':
break;
case 'a':
fd_flags = _O_APPEND;
break;
default:
errno = ENOTSUP;
debug3("fdopen - ERROR unsupported mode %s", mode);
return NULL;
}
}
else {
errno = ENOTSUP;
debug3("fdopen - ERROR unsupported mode %s", mode);
return NULL;
}
int fd = _open_osfhandle((intptr_t)pio->handle, fd_flags);
if (fd == -1 || (ret = _fdopen(fd, mode)) == NULL) {
errno = EOTHER;
debug3("fdopen - ERROR:%d _open_osfhandle()", errno);
return NULL;
}
// overwrite underlying win32 handle - its expected to be closed via fclose
// and close pio
pio->handle = NULL;
/* fdopen() to be used on pipe handles */
static FILE*
fileio_fdopen_pipe(struct w32_io* pio, const char *mode)
{
int fd_flags = 0;
FILE* ret;
debug4("fdopen - io:%p", pio);

if (mode[1] == '\0') {
switch (*mode) {
case 'r':
fd_flags = _O_RDONLY;
break;
case 'w':
break;
case 'a':
fd_flags = _O_APPEND;
break;
default:
errno = ENOTSUP;
debug3("fdopen - ERROR unsupported mode %s", mode);
return NULL;
}
}
else {
errno = ENOTSUP;
debug3("fdopen - ERROR unsupported mode %s", mode);
return NULL;
}

int fd = _open_osfhandle((intptr_t)pio->handle, fd_flags);

if (fd == -1 || (ret = _fdopen(fd, mode)) == NULL) {
errno = EOTHER;
debug3("fdopen - ERROR:%d _open_osfhandle()", errno);
return NULL;
}

// overwrite underlying win32 handle - its expected to be closed via fclose
// and close pio
pio->handle = NULL;
int w32_close(int);
w32_close(pio->table_index);
return ret;
}
w32_close(pio->table_index);
return ret;
}

/* fdopen() to be used on file handles */
static FILE*
Expand Down
2 changes: 2 additions & 0 deletions contrib/win32/win32compat/inc/fcntl.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ void* w32_fd_to_handle(int fd);
#define O_SEQUENTIAL _O_SEQUENTIAL
#define O_RANDOM _O_RANDOM
#define O_U16TEXT _O_U16TEXT
// Hack to deal with TAP driver requirements: FILE_ATTRIBUTE_SYSTEM, no FILE_FLAG_BACKUP_SEMANTICS
#define O_SYSTEM 0x4000000

/*
* open() POSIX specific modes and flags.
Expand Down
4 changes: 2 additions & 2 deletions misc.c
Original file line number Diff line number Diff line change
Expand Up @@ -1492,10 +1492,10 @@ percent_dollar_expand(const char *string, ...)
}

int
tun_open(int tun, int mode, char **ifname)
tun_open(int tun, int mode, const char* tun_options, char **ifname)
{
#if defined(CUSTOM_SYS_TUN_OPEN)
return (sys_tun_open(tun, mode, ifname));
return (sys_tun_open(tun, mode, tun_options, ifname));
#elif defined(SSH_TUN_OPENBSD)
struct ifreq ifr;
char name[100];
Expand Down
2 changes: 1 addition & 1 deletion misc.h
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ void freeargs(arglist *);
void duplicateargs(arglist *, const arglist *);
#endif

int tun_open(int, int, char **);
int tun_open(int, int, const char *, char **);

/* Common definitions for ssh tunnel device forwarding */
#define SSH_TUNMODE_NO 0x00
Expand Down
Loading