]> git.proxmox.com Git - mirror_ovs.git/blobdiff - lib/netlink-socket.c
dpif-netlink-rtnl: Support GENEVE creation
[mirror_ovs.git] / lib / netlink-socket.c
index cad949096d507ea8ccf4c044d90cc9f13fddfd92..ccfd55e9220663f55fa5fd1828f73de8850289d0 100644 (file)
@@ -25,7 +25,7 @@
 #include "coverage.h"
 #include "openvswitch/dynamic-string.h"
 #include "hash.h"
-#include "hmap.h"
+#include "openvswitch/hmap.h"
 #include "netlink.h"
 #include "netlink-protocol.h"
 #include "odp-netlink.h"
@@ -59,6 +59,23 @@ static void log_nlmsg(const char *function, int error,
                       const void *message, size_t size, int protocol);
 #ifdef _WIN32
 static int get_sock_pid_from_kernel(struct nl_sock *sock);
+static int set_sock_property(struct nl_sock *sock);
+static int nl_sock_transact(struct nl_sock *sock, const struct ofpbuf *request,
+                            struct ofpbuf **replyp);
+
+/* In the case DeviceIoControl failed and GetLastError returns with
+ * ERROR_NOT_FOUND means we lost communication with the kernel device.
+ * CloseHandle will fail because the handle in 'theory' does not exist.
+ * The only remaining option is to crash and allow the service to be restarted
+ * via service manager.  This is the only way to close the handle from both
+ * userspace and kernel. */
+void
+lost_communication(DWORD last_err)
+{
+    if (last_err == ERROR_NOT_FOUND) {
+        ovs_abort(0, "lost communication with the kernel device");
+    }
+}
 #endif
 \f
 /* Netlink sockets. */
@@ -127,6 +144,7 @@ nl_sock_create(int protocol, struct nl_sock **sockp)
     sock = xmalloc(sizeof *sock);
 
 #ifdef _WIN32
+    sock->overlapped.hEvent = NULL;
     sock->handle = CreateFile(OVS_DEVICE_NAME_USER,
                               GENERIC_READ | GENERIC_WRITE,
                               FILE_SHARE_READ | FILE_SHARE_WRITE,
@@ -164,6 +182,10 @@ nl_sock_create(int protocol, struct nl_sock **sockp)
     if (retval != 0) {
         goto error;
     }
+    retval = set_sock_property(sock);
+    if (retval != 0) {
+        goto error;
+    }
 #else
     if (setsockopt(sock->fd, SOL_SOCKET, SO_RCVBUFFORCE,
                    &rcvbuf, sizeof rcvbuf)) {
@@ -272,6 +294,7 @@ get_sock_pid_from_kernel(struct nl_sock *sock)
     if (!DeviceIoControl(sock->handle, OVS_IOCTL_GET_PID,
                          NULL, 0, &pid, sizeof(pid),
                          &bytes, NULL)) {
+        lost_communication(GetLastError());
         retval = EINVAL;
     } else {
         if (bytes < sizeof(pid)) {
@@ -283,6 +306,76 @@ get_sock_pid_from_kernel(struct nl_sock *sock)
 
     return retval;
 }
+
+/* Used for setting and managing socket properties in userspace and kernel.
+ * Currently two attributes are tracked - pid and protocol
+ * protocol - supplied by userspace based on the netlink family. Windows uses
+ *            this property to set the value in kernel datapath.
+ *            eg: (NETLINK_GENERIC/ NETLINK_NETFILTER)
+ * pid -      generated by windows kernel and set in userspace. The property
+ *            is not modified.
+ * Also verify if Protocol and PID in Kernel reflects the values in userspace
+ * */
+static int
+set_sock_property(struct nl_sock *sock)
+{
+    static const struct nl_policy ovs_socket_policy[] = {
+        [OVS_NL_ATTR_SOCK_PROTO] = { .type = NL_A_BE32, .optional = true },
+        [OVS_NL_ATTR_SOCK_PID] = { .type = NL_A_BE32, .optional = true }
+    };
+
+    struct ofpbuf request, *reply;
+    struct ovs_header *ovs_header;
+    struct nlattr *attrs[ARRAY_SIZE(ovs_socket_policy)];
+    int retval = 0;
+    int error;
+
+    ofpbuf_init(&request, 0);
+    nl_msg_put_genlmsghdr(&request, 0, OVS_WIN_NL_CTRL_FAMILY_ID, 0,
+                          OVS_CTRL_CMD_SOCK_PROP, OVS_WIN_CONTROL_VERSION);
+    ovs_header = ofpbuf_put_uninit(&request, sizeof *ovs_header);
+    ovs_header->dp_ifindex = 0;
+
+    nl_msg_put_be32(&request, OVS_NL_ATTR_SOCK_PROTO, sock->protocol);
+    /* pid is already set as part of get_sock_pid_from_kernel()
+     * This is added to maintain consistency
+     */
+    nl_msg_put_be32(&request, OVS_NL_ATTR_SOCK_PID, sock->pid);
+
+    error = nl_sock_transact(sock, &request, &reply);
+    ofpbuf_uninit(&request);
+    if (error) {
+        retval = EINVAL;
+    }
+
+    if (!nl_policy_parse(reply,
+                         NLMSG_HDRLEN + GENL_HDRLEN + sizeof *ovs_header,
+                         ovs_socket_policy, attrs,
+                         ARRAY_SIZE(ovs_socket_policy))) {
+        ofpbuf_delete(reply);
+        retval = EINVAL;
+    }
+    /* Verify if the properties are setup properly */
+    if (attrs[OVS_NL_ATTR_SOCK_PROTO]) {
+        int protocol = nl_attr_get_be32(attrs[OVS_NL_ATTR_SOCK_PROTO]);
+        if (protocol != sock->protocol) {
+            VLOG_ERR("Invalid protocol returned:%d expected:%d",
+                     protocol, sock->protocol);
+            retval = EINVAL;
+        }
+    }
+
+    if (attrs[OVS_NL_ATTR_SOCK_PID]) {
+        int pid = nl_attr_get_be32(attrs[OVS_NL_ATTR_SOCK_PID]);
+        if (pid != sock->pid) {
+            VLOG_ERR("Invalid pid returned:%d expected:%d",
+                     pid, sock->pid);
+            retval = EINVAL;
+        }
+    }
+
+    return retval;
+}
 #endif  /* _WIN32 */
 
 #ifdef _WIN32
@@ -348,6 +441,30 @@ nl_sock_join_mcgroup(struct nl_sock *sock, unsigned int multicast_group)
 }
 
 #ifdef _WIN32
+int
+nl_sock_subscribe_packet__(struct nl_sock *sock, bool subscribe)
+{
+    struct ofpbuf request;
+    uint64_t request_stub[128];
+    struct ovs_header *ovs_header;
+    struct nlmsghdr *nlmsg;
+    int error;
+
+    ofpbuf_use_stub(&request, request_stub, sizeof request_stub);
+    nl_msg_put_genlmsghdr(&request, 0, OVS_WIN_NL_CTRL_FAMILY_ID, 0,
+                          OVS_CTRL_CMD_PACKET_SUBSCRIBE_REQ,
+                          OVS_WIN_CONTROL_VERSION);
+
+    ovs_header = ofpbuf_put_uninit(&request, sizeof *ovs_header);
+    ovs_header->dp_ifindex = 0;
+    nl_msg_put_u8(&request, OVS_NL_ATTR_PACKET_SUBSCRIBE, subscribe ? 1 : 0);
+    nl_msg_put_u32(&request, OVS_NL_ATTR_PACKET_PID, sock->pid);
+
+    error = nl_sock_send(sock, &request, true);
+    ofpbuf_uninit(&request);
+    return error;
+}
+
 int
 nl_sock_subscribe_packets(struct nl_sock *sock)
 {
@@ -383,30 +500,6 @@ nl_sock_unsubscribe_packets(struct nl_sock *sock)
     sock->read_ioctl = OVS_IOCTL_READ;
     return 0;
 }
-
-int
-nl_sock_subscribe_packet__(struct nl_sock *sock, bool subscribe)
-{
-    struct ofpbuf request;
-    uint64_t request_stub[128];
-    struct ovs_header *ovs_header;
-    struct nlmsghdr *nlmsg;
-    int error;
-
-    ofpbuf_use_stub(&request, request_stub, sizeof request_stub);
-    nl_msg_put_genlmsghdr(&request, 0, OVS_WIN_NL_CTRL_FAMILY_ID, 0,
-                          OVS_CTRL_CMD_PACKET_SUBSCRIBE_REQ,
-                          OVS_WIN_CONTROL_VERSION);
-
-    ovs_header = ofpbuf_put_uninit(&request, sizeof *ovs_header);
-    ovs_header->dp_ifindex = 0;
-    nl_msg_put_u8(&request, OVS_NL_ATTR_PACKET_SUBSCRIBE, subscribe ? 1 : 0);
-    nl_msg_put_u32(&request, OVS_NL_ATTR_PACKET_PID, sock->pid);
-
-    error = nl_sock_send(sock, &request, true);
-    ofpbuf_uninit(&request);
-    return error;
-}
 #endif
 
 /* Tries to make 'sock' stop listening to 'multicast_group'.  Returns 0 if
@@ -459,11 +552,12 @@ nl_sock_send__(struct nl_sock *sock, const struct ofpbuf *msg,
         if (!DeviceIoControl(sock->handle, OVS_IOCTL_WRITE,
                              msg->data, msg->size, NULL, 0,
                              &bytes, NULL)) {
+            lost_communication(GetLastError());
             retval = -1;
             /* XXX: Map to a more appropriate error based on GetLastError(). */
             errno = EINVAL;
             VLOG_DBG_RL(&rl, "fatal driver failure in write: %s",
-                ovs_lasterror_to_string());
+                        ovs_lasterror_to_string());
         } else {
             retval = msg->size;
         }
@@ -553,6 +647,7 @@ nl_sock_recv__(struct nl_sock *sock, struct ofpbuf *buf, bool wait)
         DWORD bytes;
         if (!DeviceIoControl(sock->handle, sock->read_ioctl,
                              NULL, 0, tail, sizeof tail, &bytes, NULL)) {
+            lost_communication(GetLastError());
             VLOG_DBG_RL(&rl, "fatal driver failure in transact: %s",
                         ovs_lasterror_to_string());
             retval = -1;
@@ -803,6 +898,7 @@ nl_sock_transact_multiple__(struct nl_sock *sock,
             }
         } else if (!ret) {
             /* XXX: Map to a more appropriate error. */
+            lost_communication(GetLastError());
             error = EINVAL;
             VLOG_DBG_RL(&rl, "fatal driver failure: %s",
                 ovs_lasterror_to_string());
@@ -810,6 +906,8 @@ nl_sock_transact_multiple__(struct nl_sock *sock,
         }
 
         if (reply_len != 0) {
+            request_nlmsg = nl_msg_nlmsghdr(txn->request);
+
             if (reply_len < sizeof *reply_nlmsg) {
                 nl_sock_record_errors__(transactions, n, 0);
                 VLOG_DBG_RL(&rl, "insufficient length of reply %#"PRIu32
@@ -818,7 +916,6 @@ nl_sock_transact_multiple__(struct nl_sock *sock,
             }
 
             /* Validate the sequence number in the reply. */
-            request_nlmsg = nl_msg_nlmsghdr(txn->request);
             reply_nlmsg = (struct nlmsghdr *)reply_buf;
 
             if (request_nlmsg->nlmsg_seq != reply_nlmsg->nlmsg_seq) {
@@ -1191,6 +1288,7 @@ pend_io_request(struct nl_sock *sock)
 
     ovs_header = ofpbuf_put_uninit(&request, sizeof *ovs_header);
     ovs_header->dp_ifindex = 0;
+    nlmsg->nlmsg_len = request.size;
 
     if (!DeviceIoControl(sock->handle, OVS_IOCTL_WRITE,
                          request.data, request.size,
@@ -1198,6 +1296,7 @@ pend_io_request(struct nl_sock *sock)
         error = GetLastError();
         /* Check if the I/O got pended */
         if (error != ERROR_IO_INCOMPLETE && error != ERROR_IO_PENDING) {
+            lost_communication(error);
             VLOG_ERR("nl_sock_wait failed - %s\n", ovs_format_message(error));
             retval = EINVAL;
         }
@@ -1621,7 +1720,9 @@ nl_transact(int protocol, const struct ofpbuf *request,
 
     error = nl_pool_alloc(protocol, &sock);
     if (error) {
-        *replyp = NULL;
+        if (replyp) {
+            *replyp = NULL;
+        }
         return error;
     }