[ovs-dev] [PATCH v3] Windows: Local named pipe implementation

Alin Serdean aserdean at cloudbasesolutions.com
Mon Jul 25 02:29:21 UTC 2016


Currently in the case of command line arguments punix/unix, on Windows
we create a file, write a TCP port number to connect. This is a security
concern.

This patch adds support for the command line arguments punix/unix trying
to mimic AF_UNIX behind a local named pipe.

This patch drops the TCP socket implementation behind command line
arguments punix/unix and switches to the local named pipe implementation.

Since we do not write anything to the file created by the punix/unix
arguments, switch tests to plain file existence.

Man pages and code comments have been updated.

Signed-off-by: Alin Gabriel Serdean <aserdean at cloudbasesolutions.com>
---
v3: squash commits update documentation and code comments
v2: Address comments, fix handle leaks.
---
 lib/automake.mk          |   1 +
 lib/stream-tcp.c         | 115 ----------
 lib/stream-windows.c     | 587 +++++++++++++++++++++++++++++++++++++++++++++++
 lib/unixctl.c            |   5 +-
 lib/unixctl.man          |  12 +-
 lib/vconn-active.man     |   3 +-
 ovsdb/remote-active.man  |   4 +-
 ovsdb/remote-passive.man |   5 +-
 tests/ovsdb-server.at    |   6 +-
 9 files changed, 608 insertions(+), 130 deletions(-)
 create mode 100644 lib/stream-windows.c

diff --git a/lib/automake.mk b/lib/automake.mk
index 71c9d41..9067c95 100644
--- a/lib/automake.mk
+++ b/lib/automake.mk
@@ -293,6 +293,7 @@ lib_libopenvswitch_la_SOURCES += \
 	lib/latch-windows.c \
 	lib/route-table-stub.c \
 	lib/if-notifier-stub.c \
+	lib/stream-windows.c \
 	lib/strsep.c
 else
 lib_libopenvswitch_la_SOURCES += \
diff --git a/lib/stream-tcp.c b/lib/stream-tcp.c
index 2b57ca7..1749fad 100644
--- a/lib/stream-tcp.c
+++ b/lib/stream-tcp.c
@@ -74,64 +74,6 @@ const struct stream_class tcp_stream_class = {
     NULL,                       /* run_wait */
     NULL,                       /* wait */
 };
-
-#ifdef _WIN32
-#include "dirs.h"
-
-static int
-windows_open(const char *name, char *suffix, struct stream **streamp,
-             uint8_t dscp)
-{
-    int error, port;
-    FILE *file;
-    char *suffix_new, *path;
-
-    /* If the path does not contain a ':', assume it is relative to
-     * OVS_RUNDIR. */
-    if (!strchr(suffix, ':')) {
-        path = xasprintf("%s/%s", ovs_rundir(), suffix);
-    } else {
-        path = xstrdup(suffix);
-    }
-
-    file = fopen(path, "r");
-    if (!file) {
-        error = errno;
-        VLOG_DBG("%s: could not open %s (%s)", name, suffix,
-                 ovs_strerror(error));
-        return error;
-    }
-
-    error = fscanf(file, "%d", &port);
-    if (error != 1) {
-        VLOG_ERR("failed to read port from %s", suffix);
-        fclose(file);
-        return EINVAL;
-    }
-    fclose(file);
-
-    suffix_new = xasprintf("127.0.0.1:%d", port);
-
-    error = tcp_open(name, suffix_new, streamp, dscp);
-
-    free(suffix_new);
-    free(path);
-    return error;
-}
-
-const struct stream_class windows_stream_class = {
-    "unix",                     /* name */
-    false,                      /* needs_probes */
-    windows_open,                  /* open */
-    NULL,                       /* close */
-    NULL,                       /* connect */
-    NULL,                       /* recv */
-    NULL,                       /* send */
-    NULL,                       /* run */
-    NULL,                       /* run_wait */
-    NULL,                       /* wait */
-};
-#endif
 
 /* Passive TCP. */
 
@@ -198,60 +140,3 @@ const struct pstream_class ptcp_pstream_class = {
     NULL,
     NULL,
 };
-
-#ifdef _WIN32
-static int
-pwindows_open(const char *name, char *suffix, struct pstream **pstreamp,
-              uint8_t dscp)
-{
-    int error;
-    char *suffix_new, *path;
-    FILE *file;
-    struct pstream *listener;
-
-    suffix_new = xstrdup("0:127.0.0.1");
-
-    /* If the path does not contain a ':', assume it is relative to
-     * OVS_RUNDIR. */
-    if (!strchr(suffix, ':')) {
-        path = xasprintf("%s/%s", ovs_rundir(), suffix);
-    } else {
-        path = xstrdup(suffix);
-    }
-
-    error = new_pstream(suffix_new, name, pstreamp, dscp, path, false);
-    if (error) {
-        goto exit;
-    }
-    listener = *pstreamp;
-
-    file = fopen(path, "w");
-    if (!file) {
-        error = errno;
-        VLOG_DBG("could not open %s (%s)", path, ovs_strerror(error));
-        goto exit;
-    }
-
-    fprintf(file, "%d\n", ntohs(listener->bound_port));
-    if (fflush(file) == EOF) {
-        error = EIO;
-        VLOG_ERR("write failed for %s", path);
-        fclose(file);
-        goto exit;
-    }
-    fclose(file);
-
-exit:
-    free(suffix_new);
-    return error;
-}
-
-const struct pstream_class pwindows_pstream_class = {
-    "punix",
-    false,
-    pwindows_open,
-    NULL,
-    NULL,
-    NULL,
-};
-#endif
diff --git a/lib/stream-windows.c b/lib/stream-windows.c
new file mode 100644
index 0000000..88fe17d
--- /dev/null
+++ b/lib/stream-windows.c
@@ -0,0 +1,587 @@
+/*
+ * Copyright (c) 2016 Cloudbase Solutions Srl
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <config.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <netdb.h>
+#include <poll.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strsafe.h>
+#include <unistd.h>
+#include "packets.h"
+#include "poll-loop.h"
+#include "socket-util.h"
+#include "dirs.h"
+#include "util.h"
+#include "stream-provider.h"
+#include "openvswitch/vlog.h"
+
+VLOG_DEFINE_THIS_MODULE(stream_windows);
+
+static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(10, 25);
+
+static void maybe_unlink_and_free(char *path);
+
+/* Buffer size suggested at the creation of the named pipe for reading and
+ * and writing operations */
+#define BUFSIZE 65000
+
+/* Default local prefix of a named pipe */
+#define LOCAL_PREFIX "\\\\.\\pipe\\"
+
+/* This function has the purpose to remove all the slashes received in s */
+char*
+remove_slashes(char *s) {
+    char *p1, *p2;
+    p1 = p2 = s;
+
+    while (*p1) {
+        if ((*p1) == '\\' || (*p1) == '/') {
+            p1++;
+        } else {
+            *p2 = *p1;
+            p2++;
+            p1++;
+        }
+    }
+    *p2 = '\0';
+    return s;
+}
+
+/* Active named pipe descriptor stream. */
+struct windows_stream
+{
+    struct stream stream;
+    HANDLE fd;
+    /* Overlapped operations used for reading/writing */
+    OVERLAPPED read;
+    OVERLAPPED write;
+    /* Flag to check if a reading/writing is pending*/
+    bool read_pending;
+    bool write_pending;
+    /* Flag to check if fd is a server HANDLE. In the case of a server handle
+     * we have to issue a disconnect before closing the actual handle */
+    bool server;
+    bool retry_connect;
+    char *pipe_path;
+};
+
+static struct windows_stream *
+stream_windows_cast(struct stream *stream)
+{
+    stream_assert_class(stream, &windows_stream_class);
+    return CONTAINER_OF(stream, struct windows_stream, stream);
+}
+
+HANDLE
+create_snpipe(char *path) {
+    return CreateFile(path, GENERIC_READ | GENERIC_WRITE, 0, NULL,
+                      OPEN_EXISTING,
+                      FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED |
+                      FILE_FLAG_NO_BUFFERING,
+                      NULL);
+}
+
+/* Active named pipe open */
+static int
+windows_open(const char *name, char *suffix, struct stream **streamp,
+             uint8_t dscp OVS_UNUSED)
+{
+    char *connect_path;
+    HANDLE npipe;
+    DWORD mode = PIPE_READMODE_BYTE;
+    char* path;
+    FILE* file;
+    bool retry = false;
+    /* If the path does not contain a ':', assume it is relative to
+     * OVS_RUNDIR. */
+    if (!strchr(suffix, ':')) {
+        path = xasprintf("%s/%s", ovs_rundir(), suffix);
+    } else {
+        path = xstrdup(suffix);
+    }
+
+    /* In the punix/unix argument the assumption is that there is a file
+     * created in the path (name) */
+    file = fopen(path, "r");
+    if (!file) {
+        free(path);
+        VLOG_DBG_RL(&rl, "%s: could not open %s (%s)", name, suffix,
+                    ovs_strerror(errno));
+        return ENOENT;
+    } else {
+        fclose(file);
+    }
+
+    /* Valid pipe names do not have slashes. The assumption is the named pipe
+     * was created on the same path with the slash stripped path and the
+     * default prefix \\.\pipe\ appended.
+     * Strip the slashed from the parameter name and append the default prefix
+     */
+    connect_path = xasprintf("%s%s", LOCAL_PREFIX, remove_slashes(path));
+    free(path);
+
+    /* Try to connect to the named pipe. In the case all pipe instances are
+     * busy we set the retry flag to true and we retry  again during the
+     * connect function. Use overlapped flag and no buffering to ensure
+     * an asynchronous operations */
+    npipe = create_snpipe(connect_path);
+
+    if (npipe == INVALID_HANDLE_VALUE && GetLastError() == ERROR_PIPE_BUSY) {
+        retry = true;
+    }
+
+    if (!retry && npipe == INVALID_HANDLE_VALUE) {
+        VLOG_ERR_RL(&rl, "Could not connect to named pipe: %s",
+                    ovs_lasterror_to_string());
+        free(connect_path);
+        return ENOENT;
+    }
+    if (!retry && !SetNamedPipeHandleState(npipe, &mode, NULL, NULL)) {
+        VLOG_ERR_RL(&rl, "Could not set named pipe options: %s",
+                    ovs_lasterror_to_string());
+        free(connect_path);
+        CloseHandle(npipe);
+        return ENOENT;
+    }
+    struct windows_stream *s = xmalloc(sizeof *s);
+    stream_init(&s->stream, &windows_stream_class, 0, name);
+    s->pipe_path = connect_path;
+    s->fd = npipe;
+    /* This is a active stream */
+    s->server = false;
+    /* Create events for reading and writing to be signaled later */
+    memset(&s->read, 0, sizeof(s->read));
+    memset(&s->write, 0, sizeof(s->write));
+    s->read.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
+    s->write.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
+    /* Initial read and write operations are not pending */
+    s->read_pending = false;
+    s->write_pending = false;
+    s->retry_connect = retry;
+    *streamp = &s->stream;
+    return 0;
+}
+
+/* Active named pipe close */
+static void
+windows_close(struct stream *stream)
+{
+    struct windows_stream *s = stream_windows_cast(stream);
+    /* Disconnect the named pipe in case it was a passive stream at first */
+    if (s->server) {
+        DisconnectNamedPipe(s->fd);
+    }
+    CloseHandle(s->fd);
+    CloseHandle(s->read.hEvent);
+    CloseHandle(s->write.hEvent);
+    if (s->pipe_path) {
+        free(s->pipe_path);
+    }
+    free(s);
+}
+
+/* Active named pipe connect */
+static int
+windows_connect(struct stream *stream)
+{
+    /* Stub function for connect. We already to the connect in the open
+     * function */
+    struct windows_stream *s = stream_windows_cast(stream);
+
+    if (!s->retry_connect) {
+        return 0;
+    } else {
+        HANDLE npipe;
+        npipe = create_snpipe(s->pipe_path);
+        if (npipe == INVALID_HANDLE_VALUE) {
+            if (GetLastError() == ERROR_PIPE_BUSY) {
+                return EAGAIN;
+            } else {
+                s->retry_connect = false;
+                return ENOENT;
+            }
+        }
+        s->retry_connect = false;
+        s->fd = npipe;
+        return 0;
+    }
+}
+
+/* Active named pipe receive */
+static ssize_t
+windows_recv(struct stream *stream, void *buffer, size_t n)
+{
+    struct windows_stream *s = stream_windows_cast(stream);
+    ssize_t retval = 0;
+    boolean result = false;
+    DWORD last_error = 0;
+    LPOVERLAPPED  ov = NULL;
+    ov = &s->read;
+
+    /* If the read operation was pending we verify its result */
+    if (s->read_pending) {
+        if (!GetOverlappedResult(s->fd, ov, &(DWORD)retval, FALSE)) {
+            last_error = GetLastError();
+            if (last_error == ERROR_IO_INCOMPLETE) {
+                /* If the operation is still pending retry again */
+                s->read_pending = true;
+                return -EAGAIN;
+            } else if (last_error == ERROR_PIPE_NOT_CONNECTED ||
+                       last_error == ERROR_BAD_PIPE ||
+                       last_error == ERROR_NO_DATA ||
+                       last_error == ERROR_BROKEN_PIPE) {
+                /* If the pipe was disconnected return 0 */
+                return 0;
+            } else {
+                VLOG_ERR_RL(&rl, "Could not receive data on named pipe. Last "
+                            "error: %s", ovs_lasterror_to_string());
+                return -EINVAL;
+            }
+        }
+        s->read_pending = false;
+        return retval;
+    }
+
+    result = ReadFile(s->fd, buffer, n, &(DWORD)retval, ov);
+
+    if (!result && GetLastError() == ERROR_IO_PENDING) {
+        /* Pend the read operation */
+        s->read_pending = true;
+        return -EAGAIN;
+    } else if (!result) {
+        last_error = GetLastError();
+        if (last_error == ERROR_PIPE_NOT_CONNECTED ||
+            last_error == ERROR_BAD_PIPE ||
+            last_error == ERROR_NO_DATA ||
+            last_error == ERROR_BROKEN_PIPE) {
+            /* If the pipe was disconnected return 0 */
+            return 0;
+        }
+        VLOG_ERR_RL(&rl, "Could not receive data synchronous on named pipe."
+                    "Last error: %s", ovs_lasterror_to_string());
+        return -EINVAL;
+    }
+
+    return retval;
+}
+
+/* Active named pipe send */
+static ssize_t
+windows_send(struct stream *stream, const void *buffer, size_t n)
+{
+    struct windows_stream *s = stream_windows_cast(stream);
+    ssize_t retval = 0;
+    boolean result = false;
+    DWORD last_error = 0;
+    LPOVERLAPPED  ov = NULL;
+    ov = &s->write;
+
+    /* If the send operation was pending we verify the result */
+    if (s->write_pending) {
+        if (!GetOverlappedResult(s->fd, ov, &(DWORD)retval, FALSE)) {
+            last_error = GetLastError();
+            if (last_error == ERROR_IO_INCOMPLETE) {
+                /* If the operation is still pending retry again */
+                s->write_pending = true;
+                return -EAGAIN;
+            } else if (last_error == ERROR_PIPE_NOT_CONNECTED ||
+                       last_error == ERROR_BAD_PIPE ||
+                       last_error == ERROR_NO_DATA ||
+                       last_error == ERROR_BROKEN_PIPE) {
+                /* If the pipe was disconnected return connection reset */
+                return -WSAECONNRESET;
+            } else {
+                VLOG_ERR_RL(&rl, "Could not send data on named pipe. Last "
+                            "error: %s", ovs_lasterror_to_string());
+                return -EINVAL;
+            }
+        }
+        s->write_pending = false;
+        return retval;
+    }
+
+    result = WriteFile(s->fd, buffer, n, &(DWORD)retval, ov);
+    last_error = GetLastError();
+    if (!result && GetLastError() == ERROR_IO_PENDING) {
+        /* Pend the send operation */
+        s->write_pending = true;
+        return -EAGAIN;
+    } else if (!result && (last_error == ERROR_PIPE_NOT_CONNECTED ||
+                           last_error == ERROR_BAD_PIPE ||
+                           last_error == ERROR_NO_DATA ||
+                           last_error == ERROR_BROKEN_PIPE)) {
+        /* If the pipe was disconnected return connection reset */
+        return -WSAECONNRESET;
+    } else if (!result) {
+        VLOG_ERR_RL(&rl, "Could not send data on synchronous named pipe. Last "
+                    "error: %s", ovs_lasterror_to_string());
+        return -EINVAL;
+    }
+    return (retval > 0 ? retval : -EAGAIN);
+}
+
+/* Active named pipe wait */
+static void
+windows_wait(struct stream *stream, enum stream_wait_type wait)
+{
+    struct windows_stream *s = stream_windows_cast(stream);
+    switch (wait) {
+    case STREAM_SEND:
+        poll_wevent_wait(s->write.hEvent);
+        break;
+
+    case STREAM_CONNECT:
+        poll_immediate_wake();
+        break;
+
+    case STREAM_RECV:
+        poll_wevent_wait(s->read.hEvent);
+        break;
+
+    default:
+        OVS_NOT_REACHED();
+    }
+}
+
+/* Passive named pipe descriptor stream. */
+const struct stream_class windows_stream_class = {
+    "unix",                     /* name */
+    false,                      /* needs_probes */
+    windows_open,               /* open */
+    windows_close,              /* close */
+    windows_connect,            /* connect */
+    windows_recv,               /* recv */
+    windows_send,               /* send */
+    NULL,                       /* run */
+    NULL,                       /* run_wait */
+    windows_wait,               /* wait */
+};
+
+struct pwindows_pstream
+{
+    struct pstream pstream;
+    HANDLE fd;
+    /* Unlink path to be deleted during close */
+    char* unlink_path;
+    /* Overlapped operation used for connect */
+    OVERLAPPED connect;
+    /* Flag to check if an operation is pending */
+    bool pending;
+    /* Named pipe path used for creation */
+    char* pipe_path;
+};
+
+const struct pstream_class pwindows_pstream_class;
+
+static struct pwindows_pstream *
+pwindows_pstream_cast(struct pstream *pstream)
+{
+    pstream_assert_class(pstream, &pwindows_pstream_class);
+    return CONTAINER_OF(pstream, struct pwindows_pstream, pstream);
+}
+
+/* Create a named pipe with read/write access, overlapped, message mode for
+ * writing, byte mode for reading with a maximum of 64 active instances */
+HANDLE
+create_pnpipe(char *name)
+{
+    SECURITY_ATTRIBUTES sa;
+    sa.nLength = sizeof(sa);
+    sa.lpSecurityDescriptor = NULL;
+    sa.bInheritHandle = TRUE;
+    if (strlen(name) > 256) {
+        VLOG_ERR_RL(&rl, "Named pipe name too long. Creation terminated");
+        return INVALID_HANDLE_VALUE;
+    }
+    return CreateNamedPipe(name, PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
+                           PIPE_TYPE_MESSAGE | PIPE_READMODE_BYTE | PIPE_WAIT,
+                           64, BUFSIZE, BUFSIZE, 0, &sa);
+}
+
+/* Passive named pipe connect. This function creates a new named pipe and
+ * passes the old handle to the active stream */
+static int
+pwindows_accept(struct pstream *pstream, struct stream **new_streamp)
+{
+    struct pwindows_pstream *p = pwindows_pstream_cast(pstream);
+    DWORD last_error = 0;
+    DWORD cbRet;
+    HANDLE npipe;
+
+    /* If the connect operation was pending verify the result */
+    if (p->pending) {
+        if (!GetOverlappedResult(p->fd, &p->connect, &cbRet, FALSE)) {
+            last_error = GetLastError();
+            if (last_error == ERROR_IO_INCOMPLETE) {
+                /* If the operation is still pending retry again */
+                p->pending = true;
+                return EAGAIN;
+            } else {
+                VLOG_ERR_RL(&rl, "Could not connect named pipe. Last "
+                            "error: %s", ovs_lasterror_to_string());
+                return EINVAL;
+            }
+        }
+        p->pending = false;
+    }
+
+    if (!p->pending && !ConnectNamedPipe(p->fd, &p->connect)) {
+        last_error = GetLastError();
+        if (last_error == ERROR_IO_PENDING) {
+            /* Pend the connect operation */
+            p->pending = true;
+            return EAGAIN;
+        } else if (last_error != ERROR_PIPE_CONNECTED) {
+            VLOG_ERR_RL(&rl, "Could not connect synchronous named pipe. Last "
+                        "error: %s", ovs_lasterror_to_string());
+            return EINVAL;
+        } else {
+            /* If the pipe is connected signal an event */
+            SetEvent(&p->connect.hEvent);
+        }
+    }
+
+    npipe = create_pnpipe(p->pipe_path);
+    if (npipe == INVALID_HANDLE_VALUE) {
+        VLOG_ERR_RL(&rl, "Could not create a new named pipe after connect. ",
+                    ovs_lasterror_to_string());
+        return ENOENT;
+    }
+
+    /* Give the handle p->fd to the new created active stream and specify it
+     * was created by an active stream */
+    struct windows_stream *p_temp = xmalloc(sizeof *p_temp);
+    stream_init(&p_temp->stream, &windows_stream_class, 0, "unix");
+    p_temp->fd = p->fd;
+    /* Specify it was created by a passive stream */
+    p_temp->server = true;
+    /* Create events for read/write operations */
+    memset(&p_temp->read, 0, sizeof(p_temp->read));
+    memset(&p_temp->write, 0, sizeof(p_temp->write));
+    p_temp->read.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
+    p_temp->write.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
+    p_temp->read_pending = false;
+    p_temp->write_pending = false;
+    p_temp->retry_connect = false;
+    p_temp->pipe_path = NULL;
+    *new_streamp = &p_temp->stream;
+
+    /* The passive handle p->fd will be the new created handle */
+    p->fd = npipe;
+    memset(&p->connect, 0, sizeof(p->connect));
+    p->connect.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
+    p->pending = false;
+    return 0;
+}
+
+/* Passive named pipe close */
+static void
+pwindows_close(struct pstream *pstream)
+{
+    struct pwindows_pstream *p = pwindows_pstream_cast(pstream);
+    DisconnectNamedPipe(p->fd);
+    CloseHandle(p->fd);
+    CloseHandle(p->connect.hEvent);
+    maybe_unlink_and_free(p->unlink_path);
+    free(p->pipe_path);
+    free(p);
+}
+
+/* Passive named pipe wait */
+void
+pwindows_wait(struct pstream *pstream)
+{
+    struct pwindows_pstream *p = pwindows_pstream_cast(pstream);
+    poll_wevent_wait(p->connect.hEvent);
+}
+
+/* Passive named pipe */
+static int
+pwindows_open(const char *name OVS_UNUSED, char *suffix,
+              struct pstream **pstreamp, uint8_t dscp OVS_UNUSED)
+{
+    char *bind_path;
+    int error;
+    HANDLE npipe;
+    char* orig_path;
+
+    char* path;
+    if (!strchr(suffix, ':')) {
+        path = xasprintf("%s/%s", ovs_rundir(), suffix);
+    } else {
+        path = xstrdup(suffix);
+    }
+
+    /* Try to create a file under the path location */
+    FILE *file = fopen(path, "w");
+    if (!file) {
+        free(path);
+        error = errno;
+        VLOG_DBG_RL(&rl, "could not open %s (%s)", path, ovs_strerror(error));
+        return error;
+    } else {
+        fclose(file);
+    }
+
+    orig_path = xstrdup(path);
+    /* Strip slashes from path and create a named pipe using that name */
+    bind_path = xasprintf("%s%s", LOCAL_PREFIX, remove_slashes(path));
+    free(path);
+
+    npipe = create_pnpipe(bind_path);
+
+    if (npipe == INVALID_HANDLE_VALUE) {
+        VLOG_ERR_RL(&rl, "Could not create named pipe. Last error: %s",
+                    ovs_lasterror_to_string());
+        return ENOENT;
+    }
+
+    struct pwindows_pstream *p = xmalloc(sizeof *p);
+    pstream_init(&p->pstream, &pwindows_pstream_class, name);
+    p->fd = npipe;
+    p->unlink_path = orig_path;
+    memset(&p->connect, 0, sizeof(p->connect));
+    p->connect.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
+    p->pending = false;
+    p->pipe_path = bind_path;
+    *pstreamp = &p->pstream;
+    return 0;
+}
+
+const struct pstream_class pwindows_pstream_class = {
+    "punix",
+    false,                   /* probes */
+    pwindows_open,           /* open */
+    pwindows_close,          /* close */
+    pwindows_accept,         /* accept */
+    pwindows_wait,           /* wait */
+};
+
+/* Helper functions. */
+static void
+maybe_unlink_and_free(char *path)
+{
+    if (path) {
+        fatal_signal_unlink_file_now(path);
+        free(path);
+    }
+}
diff --git a/lib/unixctl.c b/lib/unixctl.c
index 5e5d26c..5fa36b0 100644
--- a/lib/unixctl.c
+++ b/lib/unixctl.c
@@ -195,7 +195,7 @@ unixctl_command_reply_error(struct unixctl_conn *conn, const char *error)
  *      - An absolute path (starting with '/') that gives the exact name of
  *        the Unix domain socket to listen on.
  *
- * For Windows, a kernel assigned TCP port is used and written in 'path'
+ * For Windows, a local named pipe is used and a file is written to the 'path'
  * which may be:
  *
  *      - NULL, in which case <rundir>/<program>.ctl is used.
@@ -442,7 +442,8 @@ unixctl_server_destroy(struct unixctl_server *server)
  * be the name of a unixctl server socket.  If it does not start with '/', it
  * will be prefixed with the rundir (e.g. /usr/local/var/run/openvswitch).
  *
- * On Windows, connects to a localhost TCP port as written inside 'path'.
+ * On Windows, connects to a local named pipe, and a file which resides in
+ * 'path' is used to mimic the behaviour of a unix socket.
  * 'path' should be an absolute path of the file.
  *
  * Returns 0 if successful, otherwise a positive errno value.  If successful,
diff --git a/lib/unixctl.man b/lib/unixctl.man
index b681c7d..1b7aa0c 100644
--- a/lib/unixctl.man
+++ b/lib/unixctl.man
@@ -7,11 +7,13 @@ not used at all, the default socket is
 \fB at RUNDIR@/\*(PN.\fIpid\fB.ctl\fR, where \fIpid\fR is \fB\*(PN\fR's
 process ID.
 .IP
-On Windows, uses a kernel chosen TCP port on the localhost to listen
-for runtime management commands.  The kernel chosen TCP port value is written
-in a file whose absolute path is pointed by \fIsocket\fR. If \fB\-\-unixctl\fR
-is not used at all, the file is created as \fB\*(PN.ctl\fR in the configured
-\fIOVS_RUNDIR\fR directory.
+On Windows, uses a local named pipe to listen for runtime management
+commands.  A file is written to mimic the behaviour of a unix socket.
+The file has the absolute path is pointed by \fIsocket\fR.
+If \fB\-\-unixctl\fR is not used at all, the file is created as
+\fB\*(PN.ctl\fR in the configured \fIOVS_RUNDIR\fR directory.
+The chosen name of the named pipe is the absolute path with slashes
+stripped out.
 .IP
 Specifying \fBnone\fR for \fIsocket\fR disables the control socket
 feature.
diff --git a/lib/vconn-active.man b/lib/vconn-active.man
index 252438d..a5818b3 100644
--- a/lib/vconn-active.man
+++ b/lib/vconn-active.man
@@ -11,4 +11,5 @@ If \fIport\fR is not specified, it defaults to 6653.
 \fBunix:\fIfile\fR
 On POSIX, a Unix domain server socket named \fIfile\fR.
 .IP
-On Windows, a localhost TCP port written in \fIfile\fR.
+On Windows, a local named pipe and a file is written in \fIfile\fR to
+mimic the behaviour of a unix socket.
diff --git a/ovsdb/remote-active.man b/ovsdb/remote-active.man
index 22b350c..9974603 100644
--- a/ovsdb/remote-active.man
+++ b/ovsdb/remote-active.man
@@ -14,5 +14,5 @@ square brackets, e.g.: \fBtcp:[::1]:6640\fR.
 .IP "\fBunix:\fIfile\fR"
 On POSIX, connect to the Unix domain server socket named \fIfile\fR.
 .IP
-On Windows, connect to a localhost TCP port whose value is written in
-\fIfile\fR.
+On Windows, connect to a local named pipe and a file is written in
+\fIfile\fR to mimic the behaviour of a unix socket.
diff --git a/ovsdb/remote-passive.man b/ovsdb/remote-passive.man
index a05f796..0f5f878 100644
--- a/ovsdb/remote-passive.man
+++ b/ovsdb/remote-passive.man
@@ -22,5 +22,6 @@ an IPv6 address, then wrap \fIip\fR with square brackets, e.g.:
 On POSIX, listen on the Unix domain server socket named \fIfile\fR for a
 connection.
 .IP
-On Windows, listen on a kernel chosen TCP port on the localhost. The kernel
-chosen TCP port value is written in \fIfile\fR.
+On Windows, listen on a local named pipe. A file is written in
+\fIfile\fR to mimic the behaviour of a unix socket. The named pipe name
+is chosen by stripping the slashes of the file.
diff --git a/tests/ovsdb-server.at b/tests/ovsdb-server.at
index e70498d..d9d2469 100644
--- a/tests/ovsdb-server.at
+++ b/tests/ovsdb-server.at
@@ -388,7 +388,7 @@ AT_CHECK([ovsdb-server --detach --no-chdir --pidfile db])
 AT_CHECK([test ! -e socket1])
 AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/add-remote punix:socket1])
 if test "$IS_WIN32" = "yes"; then
-  OVS_WAIT_UNTIL([test -s socket1])
+  OVS_WAIT_UNTIL([test -e socket1])
 else
   OVS_WAIT_UNTIL([test -S socket1])
 fi
@@ -399,7 +399,7 @@ AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/list-remotes],
 AT_CHECK([test ! -e socket2])
 AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/add-remote punix:socket2])
 if test "$IS_WIN32" = "yes"; then
-  OVS_WAIT_UNTIL([test -s socket2])
+  OVS_WAIT_UNTIL([test -e socket2])
 else
   OVS_WAIT_UNTIL([test -S socket2])
 fi
@@ -416,7 +416,7 @@ ovs-appctl: ovsdb-server: server returned an error
 AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/remove-remote punix:socket1])
 OVS_WAIT_UNTIL([test ! -e socket1])
 if test "$IS_WIN32" = "yes"; then
-  AT_CHECK([test -s socket2])
+  AT_CHECK([test -e socket2])
 else
   AT_CHECK([test -S socket2])
 fi
-- 
1.9.5.msysgit.0



More information about the dev mailing list