#include "seq.h"
#include "socket-util.h"
#include "util.h"
-#include "vlog.h"
+#include "openvswitch/vlog.h"
VLOG_DEFINE_THIS_MODULE(netlink_socket);
struct nl_sock {
#ifdef _WIN32
HANDLE handle;
+ OVERLAPPED overlapped;
+ DWORD read_ioctl;
#else
int fd;
#endif
sock = xmalloc(sizeof *sock);
#ifdef _WIN32
- sock->handle = CreateFileA("\\\\.\\OpenVSwitchDevice",
- GENERIC_READ | GENERIC_WRITE,
- FILE_SHARE_READ | FILE_SHARE_WRITE,
- NULL, OPEN_EXISTING,
- FILE_ATTRIBUTE_NORMAL, NULL);
-
- int last_error = GetLastError();
+ sock->handle = CreateFile(OVS_DEVICE_NAME_USER,
+ GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL, OPEN_EXISTING,
+ FILE_FLAG_OVERLAPPED, NULL);
if (sock->handle == INVALID_HANDLE_VALUE) {
- VLOG_ERR("fcntl: %s", ovs_strerror(last_error));
+ VLOG_ERR("fcntl: %s", ovs_lasterror_to_string());
+ goto error;
+ }
+
+ memset(&sock->overlapped, 0, sizeof sock->overlapped);
+ sock->overlapped.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+ if (sock->overlapped.hEvent == NULL) {
+ VLOG_ERR("fcntl: %s", ovs_lasterror_to_string());
goto error;
}
+ /* Initialize the type/ioctl to Generic */
+ sock->read_ioctl = OVS_IOCTL_READ;
#else
sock->fd = socket(AF_NETLINK, SOCK_RAW, protocol);
if (sock->fd < 0) {
}
}
#ifdef _WIN32
+ if (sock->overlapped.hEvent) {
+ CloseHandle(sock->overlapped.hEvent);
+ }
if (sock->handle != INVALID_HANDLE_VALUE) {
CloseHandle(sock->handle);
}
{
if (sock) {
#ifdef _WIN32
+ if (sock->overlapped.hEvent) {
+ CloseHandle(sock->overlapped.hEvent);
+ }
CloseHandle(sock->handle);
#else
close(sock->fd);
}
#endif /* _WIN32 */
+#ifdef _WIN32
+static int __inline
+nl_sock_mcgroup(struct nl_sock *sock, unsigned int multicast_group, bool join)
+{
+ 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_MC_SUBSCRIBE_REQ,
+ OVS_WIN_CONTROL_VERSION);
+
+ ovs_header = ofpbuf_put_uninit(&request, sizeof *ovs_header);
+ ovs_header->dp_ifindex = 0;
+
+ nl_msg_put_u32(&request, OVS_NL_ATTR_MCAST_GRP, multicast_group);
+ nl_msg_put_u8(&request, OVS_NL_ATTR_MCAST_JOIN, join ? 1 : 0);
+
+ error = nl_sock_send(sock, &request, true);
+ ofpbuf_uninit(&request);
+ return error;
+}
+#endif
/* Tries to add 'sock' as a listener for 'multicast_group'. Returns 0 if
* successful, otherwise a positive errno value.
*
nl_sock_join_mcgroup(struct nl_sock *sock, unsigned int multicast_group)
{
#ifdef _WIN32
-#define OVS_VPORT_MCGROUP_FALLBACK_ID 33
- struct ofpbuf msg_buf;
- struct message_multicast
- {
- struct nlmsghdr;
- /* if true, join; if else, leave */
- unsigned char join;
- unsigned int groupId;
- };
-
- struct message_multicast msg = { 0 };
-
- msg.nlmsg_len = sizeof(struct message_multicast);
- msg.nlmsg_type = OVS_VPORT_MCGROUP_FALLBACK_ID;
- msg.nlmsg_flags = 0;
- msg.nlmsg_seq = 0;
- msg.nlmsg_pid = sock->pid;
-
- msg.join = 1;
- msg.groupId = multicast_group;
- msg_buf.base_ = &msg;
- msg_buf.data_ = &msg;
- msg_buf.size_ = msg.nlmsg_len;
-
- nl_sock_send__(sock, &msg_buf, msg.nlmsg_seq, 0);
+ /* Set the socket type as a "multicast" socket */
+ sock->read_ioctl = OVS_IOCTL_READ_EVENT;
+ int error = nl_sock_mcgroup(sock, multicast_group, true);
+ if (error) {
+ sock->read_ioctl = OVS_IOCTL_READ;
+ VLOG_WARN("could not join multicast group %u (%s)",
+ multicast_group, ovs_strerror(error));
+ return error;
+ }
#else
if (setsockopt(sock->fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP,
&multicast_group, sizeof multicast_group) < 0) {
return 0;
}
+#ifdef _WIN32
+int
+nl_sock_subscribe_packets(struct nl_sock *sock)
+{
+ int error;
+
+ if (sock->read_ioctl != OVS_IOCTL_READ) {
+ return EINVAL;
+ }
+
+ error = nl_sock_subscribe_packet__(sock, true);
+ if (error) {
+ VLOG_WARN("could not unsubscribe packets (%s)",
+ ovs_strerror(errno));
+ return error;
+ }
+ sock->read_ioctl = OVS_IOCTL_READ_PACKET;
+
+ return 0;
+}
+
+int
+nl_sock_unsubscribe_packets(struct nl_sock *sock)
+{
+ ovs_assert(sock->read_ioctl == OVS_IOCTL_READ_PACKET);
+
+ int error = nl_sock_subscribe_packet__(sock, false);
+ if (error) {
+ VLOG_WARN("could not subscribe to packets (%s)",
+ ovs_strerror(errno));
+ return error;
+ }
+
+ 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
* successful, otherwise a positive errno value.
*
nl_sock_leave_mcgroup(struct nl_sock *sock, unsigned int multicast_group)
{
#ifdef _WIN32
- struct ofpbuf msg_buf;
- struct message_multicast
- {
- struct nlmsghdr;
- /* if true, join; if else, leave*/
- unsigned char join;
- };
-
- struct message_multicast msg = { 0 };
- nl_msg_put_nlmsghdr(&msg, sizeof(struct message_multicast),
- multicast_group, 0);
- msg.join = 0;
-
- msg_buf.base_ = &msg;
- msg_buf.data_ = &msg;
- msg_buf.size_ = msg.nlmsg_len;
-
- nl_sock_send__(sock, &msg_buf, msg.nlmsg_seq, 0);
+ int error = nl_sock_mcgroup(sock, multicast_group, false);
+ if (error) {
+ VLOG_WARN("could not leave multicast group %u (%s)",
+ multicast_group, ovs_strerror(error));
+ return error;
+ }
+ sock->read_ioctl = OVS_IOCTL_READ;
#else
if (setsockopt(sock->fd, SOL_NETLINK, NETLINK_DROP_MEMBERSHIP,
&multicast_group, sizeof multicast_group) < 0) {
DWORD bytes;
if (!DeviceIoControl(sock->handle, OVS_IOCTL_WRITE,
- ofpbuf_data(msg), ofpbuf_size(msg), NULL, 0,
- &bytes, NULL)) {
+ ofpbuf_data(msg), ofpbuf_size(msg), NULL, 0,
+ &bytes, NULL)) {
retval = -1;
/* XXX: Map to a more appropriate error based on GetLastError(). */
errno = EINVAL;
nlmsghdr->nlmsg_len = UINT32_MAX;
#ifdef _WIN32
DWORD bytes;
- if (!DeviceIoControl(sock->handle, OVS_IOCTL_READ,
+ if (!DeviceIoControl(sock->handle, sock->read_ioctl,
NULL, 0, tail, sizeof tail, &bytes, NULL)) {
retval = -1;
errno = EINVAL;
} else {
if (retval >= buf->allocated) {
ofpbuf_reinit(buf, retval);
+ nlmsghdr = ofpbuf_base(buf);
+ nlmsghdr->nlmsg_len = UINT32_MAX;
}
memcpy(ofpbuf_data(buf), tail, retval);
ofpbuf_set_size(buf, retval);
iovs[i].iov_len = ofpbuf_size(txn->request);
}
+#ifndef _WIN32
memset(&msg, 0, sizeof msg);
msg.msg_iov = iovs;
msg.msg_iovlen = n;
do {
-#ifdef _WIN32
- DWORD last_error = 0;
- bool result = FALSE;
- for (i = 0; i < n; i++) {
- result = WriteFile((HANDLE)sock->handle, iovs[i].iov_base, iovs[i].iov_len,
- &error, NULL);
- last_error = GetLastError();
- if (last_error != ERROR_SUCCESS && !result) {
- error = EAGAIN;
- errno = EAGAIN;
- } else {
- error = 0;
- }
- }
-#else
error = sendmsg(sock->fd, &msg, 0) < 0 ? errno : 0;
-#endif
} while (error == EINTR);
for (i = 0; i < n; i++) {
struct nl_transaction *txn = transactions[i];
- log_nlmsg(__func__, error, ofpbuf_data(txn->request), ofpbuf_size(txn->request),
- sock->protocol);
+ log_nlmsg(__func__, error, ofpbuf_data(txn->request),
+ ofpbuf_size(txn->request), sock->protocol);
}
if (!error) {
COVERAGE_ADD(netlink_sent, n);
base_seq += i + 1;
}
ofpbuf_uninit(&tmp_reply);
+#else
+ error = 0;
+ uint8_t reply_buf[65536];
+ for (i = 0; i < n; i++) {
+ DWORD reply_len;
+ struct nl_transaction *txn = transactions[i];
+ struct nlmsghdr *request_nlmsg, *reply_nlmsg;
+
+ if (!DeviceIoControl(sock->handle, OVS_IOCTL_TRANSACT,
+ ofpbuf_data(txn->request),
+ ofpbuf_size(txn->request),
+ reply_buf, sizeof reply_buf,
+ &reply_len, NULL)) {
+ /* XXX: Map to a more appropriate error. */
+ error = EINVAL;
+ break;
+ }
+
+ if (reply_len < sizeof *reply_nlmsg) {
+ nl_sock_record_errors__(transactions, n, 0);
+ VLOG_DBG_RL(&rl, "insufficient length of reply %#"PRIu32
+ " for seq: %#"PRIx32, reply_len, request_nlmsg->nlmsg_seq);
+ break;
+ }
+
+ /* 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) {
+ ovs_assert(request_nlmsg->nlmsg_seq == reply_nlmsg->nlmsg_seq);
+ VLOG_DBG_RL(&rl, "mismatched seq request %#"PRIx32
+ ", reply %#"PRIx32, request_nlmsg->nlmsg_seq,
+ reply_nlmsg->nlmsg_seq);
+ break;
+ }
+
+ /* Handle errors embedded within the netlink message. */
+ ofpbuf_use_stub(&tmp_reply, reply_buf, sizeof reply_buf);
+ ofpbuf_set_size(&tmp_reply, sizeof reply_buf);
+ if (nl_msg_nlmsgerr(&tmp_reply, &txn->error)) {
+ if (txn->reply) {
+ ofpbuf_clear(txn->reply);
+ }
+ if (txn->error) {
+ VLOG_DBG_RL(&rl, "received NAK error=%d (%s)",
+ error, ovs_strerror(txn->error));
+ }
+ } else {
+ txn->error = 0;
+ if (txn->reply) {
+ /* Copy the reply to the buffer specified by the caller. */
+ if (reply_len > txn->reply->allocated) {
+ ofpbuf_reinit(txn->reply, reply_len);
+ }
+ memcpy(ofpbuf_data(txn->reply), reply_buf, reply_len);
+ ofpbuf_set_size(txn->reply, reply_len);
+ }
+ }
+ ofpbuf_uninit(&tmp_reply);
+
+ /* Count the number of successful transactions. */
+ (*done)++;
+
+ }
+
+ if (!error) {
+ COVERAGE_ADD(netlink_sent, n);
+ }
+#endif
return error;
}
return status == EOF ? 0 : status;
}
+#ifdef _WIN32
+/* Pend an I/O request in the driver. The driver completes the I/O whenever
+ * an event or a packet is ready to be read. Once the I/O is completed
+ * the overlapped structure event associated with the pending I/O will be set
+ */
+static int
+pend_io_request(struct nl_sock *sock)
+{
+ struct ofpbuf request;
+ uint64_t request_stub[128];
+ struct ovs_header *ovs_header;
+ struct nlmsghdr *nlmsg;
+ uint32_t seq;
+ int retval;
+ int error;
+ DWORD bytes;
+ OVERLAPPED *overlapped = CONST_CAST(OVERLAPPED *, &sock->overlapped);
+
+ int ovs_msg_size = sizeof (struct nlmsghdr) + sizeof (struct genlmsghdr) +
+ sizeof (struct ovs_header);
+
+ ofpbuf_use_stub(&request, request_stub, sizeof request_stub);
+
+ seq = nl_sock_allocate_seq(sock, 1);
+ nl_msg_put_genlmsghdr(&request, 0, OVS_WIN_NL_CTRL_FAMILY_ID, 0,
+ OVS_CTRL_CMD_WIN_PEND_REQ, OVS_WIN_CONTROL_VERSION);
+ nlmsg = nl_msg_nlmsghdr(&request);
+ nlmsg->nlmsg_seq = seq;
+ nlmsg->nlmsg_pid = sock->pid;
+
+ ovs_header = ofpbuf_put_uninit(&request, sizeof *ovs_header);
+ ovs_header->dp_ifindex = 0;
+
+ if (!DeviceIoControl(sock->handle, OVS_IOCTL_WRITE,
+ ofpbuf_data(&request), ofpbuf_size(&request),
+ NULL, 0, &bytes, overlapped)) {
+ error = GetLastError();
+ /* Check if the I/O got pended */
+ if (error != ERROR_IO_INCOMPLETE && error != ERROR_IO_PENDING) {
+ VLOG_ERR("nl_sock_wait failed - %s\n", ovs_format_message(error));
+ retval = EINVAL;
+ goto done;
+ }
+ } else {
+ /* The I/O was completed synchronously */
+ poll_immediate_wake();
+ }
+ retval = 0;
+
+done:
+ ofpbuf_uninit(&request);
+ return retval;
+}
+#endif /* _WIN32 */
+
/* Causes poll_block() to wake up when any of the specified 'events' (which is
- * a OR'd combination of POLLIN, POLLOUT, etc.) occur on 'sock'. */
+ * a OR'd combination of POLLIN, POLLOUT, etc.) occur on 'sock'.
+ * On Windows, 'sock' is not treated as const, and may be modified. */
void
nl_sock_wait(const struct nl_sock *sock, short int events)
{
#ifdef _WIN32
- poll_fd_wait(sock->handle, events);
+ if (sock->overlapped.Internal != STATUS_PENDING) {
+ pend_io_request(CONST_CAST(struct nl_sock *, sock));
+ /* XXX: poll_wevent_wait(sock->overlapped.hEvent); */
+ }
+ poll_immediate_wake(); /* XXX: temporary. */
#else
poll_fd_wait(sock->fd, events);
#endif
nl_sock_fd(const struct nl_sock *sock)
{
#ifdef _WIN32
- return sock->handle;
+ BUILD_ASSERT_DECL(sizeof sock->handle == sizeof(int));
+ return (int)sock->handle;
#else
return sock->fd;
#endif
do_lookup_genl_family(const char *name, struct nlattr **attrs,
struct ofpbuf **replyp)
{
- struct nl_sock *sock;
struct nlmsghdr *nlmsg;
struct ofpbuf *reply;
int error;
family_name = OVS_FLOW_FAMILY;
family_version = OVS_FLOW_VERSION;
family_attrmax = OVS_FLOW_ATTR_MAX;
+ } else if (!strcmp(name, OVS_WIN_NETDEV_FAMILY)) {
+ family_id = OVS_WIN_NL_NETDEV_FAMILY_ID;
+ family_name = OVS_WIN_NETDEV_FAMILY;
+ family_version = OVS_WIN_NETDEV_VERSION;
+ family_attrmax = OVS_WIN_NETDEV_ATTR_MAX;
} else {
ofpbuf_delete(reply);
return EINVAL;
if (!nl_policy_parse(reply, NLMSG_HDRLEN + GENL_HDRLEN,
family_policy, attrs, ARRAY_SIZE(family_policy))
|| nl_attr_get_u16(attrs[CTRL_ATTR_FAMILY_ID]) == 0) {
- nl_sock_destroy(sock);
ofpbuf_delete(reply);
return EPROTO;
}