#include <config.h>
#include "netlink-socket.h"
-#include <assert.h>
#include <errno.h>
#include <inttypes.h>
#include <stdlib.h>
\f
/* Netlink sockets. */
-struct nl_sock
-{
+struct nl_sock {
int fd;
uint32_t next_seq;
uint32_t pid;
max_iovs = sysconf(_SC_UIO_MAXIOV);
if (max_iovs < _XOPEN_IOV_MAX) {
if (max_iovs == -1 && errno) {
- VLOG_WARN("sysconf(_SC_UIO_MAXIOV): %s", strerror(errno));
+ VLOG_WARN("sysconf(_SC_UIO_MAXIOV): %s", ovs_strerror(errno));
}
max_iovs = _XOPEN_IOV_MAX;
} else if (max_iovs > MAX_IOVS) {
}
*sockp = NULL;
- sock = malloc(sizeof *sock);
- if (sock == NULL) {
- return ENOMEM;
- }
+ sock = xmalloc(sizeof *sock);
sock->fd = socket(AF_NETLINK, SOCK_RAW, protocol);
if (sock->fd < 0) {
- VLOG_ERR("fcntl: %s", strerror(errno));
+ VLOG_ERR("fcntl: %s", ovs_strerror(errno));
goto error;
}
sock->protocol = protocol;
rcvbuf = 1024 * 1024;
if (setsockopt(sock->fd, SOL_SOCKET, SO_RCVBUFFORCE,
&rcvbuf, sizeof rcvbuf)) {
- VLOG_WARN_RL(&rl, "setting %d-byte socket receive buffer failed (%s)",
- rcvbuf, strerror(errno));
+ /* Only root can use SO_RCVBUFFORCE. Everyone else gets EPERM.
+ * Warn only if the failure is therefore unexpected. */
+ if (errno != EPERM) {
+ VLOG_WARN_RL(&rl, "setting %d-byte socket receive buffer failed "
+ "(%s)", rcvbuf, ovs_strerror(errno));
+ }
}
retval = get_socket_rcvbuf(sock->fd);
remote.nl_family = AF_NETLINK;
remote.nl_pid = 0;
if (connect(sock->fd, (struct sockaddr *) &remote, sizeof remote) < 0) {
- VLOG_ERR("connect(0): %s", strerror(errno));
+ VLOG_ERR("connect(0): %s", ovs_strerror(errno));
goto error;
}
/* Obtain pid assigned by kernel. */
local_size = sizeof local;
if (getsockname(sock->fd, (struct sockaddr *) &local, &local_size) < 0) {
- VLOG_ERR("getsockname: %s", strerror(errno));
+ VLOG_ERR("getsockname: %s", ovs_strerror(errno));
goto error;
}
if (local_size < sizeof local || local.nl_family != AF_NETLINK) {
if (setsockopt(sock->fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP,
&multicast_group, sizeof multicast_group) < 0) {
VLOG_WARN("could not join multicast group %u (%s)",
- multicast_group, strerror(errno));
+ multicast_group, ovs_strerror(errno));
return errno;
}
return 0;
int
nl_sock_leave_mcgroup(struct nl_sock *sock, unsigned int multicast_group)
{
- assert(!sock->dump);
+ ovs_assert(!sock->dump);
if (setsockopt(sock->fd, SOL_NETLINK, NETLINK_DROP_MEMBERSHIP,
&multicast_group, sizeof multicast_group) < 0) {
VLOG_WARN("could not leave multicast group %u (%s)",
- multicast_group, strerror(errno));
+ multicast_group, ovs_strerror(errno));
return errno;
}
return 0;
}
static int
-nl_sock_send__(struct nl_sock *sock, const struct ofpbuf *msg, bool wait)
+nl_sock_send__(struct nl_sock *sock, const struct ofpbuf *msg,
+ uint32_t nlmsg_seq, bool wait)
{
struct nlmsghdr *nlmsg = nl_msg_nlmsghdr(msg);
int error;
nlmsg->nlmsg_len = msg->size;
- nlmsg->nlmsg_seq = nl_sock_allocate_seq(sock, 1);
+ nlmsg->nlmsg_seq = nlmsg_seq;
nlmsg->nlmsg_pid = sock->pid;
do {
int retval;
}
/* Tries to send 'msg', which must contain a Netlink message, to the kernel on
- * 'sock'. nlmsg_len in 'msg' will be finalized to match msg->size, and
- * nlmsg_pid will be set to 'sock''s pid, before the message is sent.
+ * 'sock'. nlmsg_len in 'msg' will be finalized to match msg->size, nlmsg_pid
+ * will be set to 'sock''s pid, and nlmsg_seq will be initialized to a fresh
+ * sequence number, before the message is sent.
*
* Returns 0 if successful, otherwise a positive errno value. If
* 'wait' is true, then the send will wait until buffer space is ready;
* otherwise, returns EAGAIN if the 'sock' send buffer is full. */
int
nl_sock_send(struct nl_sock *sock, const struct ofpbuf *msg, bool wait)
+{
+ return nl_sock_send_seq(sock, msg, nl_sock_allocate_seq(sock, 1), wait);
+}
+
+/* Tries to send 'msg', which must contain a Netlink message, to the kernel on
+ * 'sock'. nlmsg_len in 'msg' will be finalized to match msg->size, nlmsg_pid
+ * will be set to 'sock''s pid, and nlmsg_seq will be initialized to
+ * 'nlmsg_seq', before the message is sent.
+ *
+ * Returns 0 if successful, otherwise a positive errno value. If
+ * 'wait' is true, then the send will wait until buffer space is ready;
+ * otherwise, returns EAGAIN if the 'sock' send buffer is full.
+ *
+ * This function is suitable for sending a reply to a request that was received
+ * with sequence number 'nlmsg_seq'. Otherwise, use nl_sock_send() instead. */
+int
+nl_sock_send_seq(struct nl_sock *sock, const struct ofpbuf *msg,
+ uint32_t nlmsg_seq, bool wait)
{
int error = nl_sock_cow__(sock);
if (error) {
return error;
}
- return nl_sock_send__(sock, msg, wait);
+ return nl_sock_send__(sock, msg, nlmsg_seq, wait);
}
/* This stress option is useful for testing that OVS properly tolerates
struct msghdr msg;
ssize_t retval;
- assert(buf->allocated >= sizeof *nlmsghdr);
+ ovs_assert(buf->allocated >= sizeof *nlmsghdr);
ofpbuf_clear(buf);
iov[0].iov_base = buf->base;
}
if (txn->error) {
VLOG_DBG_RL(&rl, "received NAK error=%d (%s)",
- error, strerror(txn->error));
+ error, ovs_strerror(txn->error));
}
} else {
txn->error = 0;
if (error == ENOBUFS) {
VLOG_DBG_RL(&rl, "receive buffer overflow, resending request");
} else if (error) {
- VLOG_ERR_RL(&rl, "transaction error (%s)", strerror(error));
+ VLOG_ERR_RL(&rl, "transaction error (%s)", ovs_strerror(error));
nl_sock_record_errors__(transactions, n, error);
}
}
struct nl_transaction *transactionp;
struct nl_transaction transaction;
- transaction.request = (struct ofpbuf *) request;
+ transaction.request = CONST_CAST(struct ofpbuf *, request);
transaction.reply = replyp ? ofpbuf_new(1024) : NULL;
transactionp = &transaction;
}
nl_msg_nlmsghdr(request)->nlmsg_flags |= NLM_F_DUMP | NLM_F_ACK;
- dump->status = nl_sock_send__(sock, request, true);
+ dump->status = nl_sock_send__(sock, request, nl_sock_allocate_seq(sock, 1),
+ true);
dump->seq = nl_msg_nlmsghdr(request)->nlmsg_seq;
}
if (nl_msg_nlmsgerr(&dump->buffer, &retval)) {
VLOG_INFO_RL(&rl, "netlink dump request error (%s)",
- strerror(retval));
+ ovs_strerror(retval));
return retval && retval != EAGAIN ? retval : EPROTO;
}
while (!dump->status) {
struct ofpbuf reply;
if (!nl_dump_next(dump, &reply)) {
- assert(dump->status);
+ ovs_assert(dump->status);
}
}
}
ofpbuf_delete(reply);
- assert(*number != 0);
+ ovs_assert(*number != 0);
}
return *number > 0 ? 0 : -*number;
}
if (e) {
ds_put_format(&ds, " error(%d", e->error);
if (e->error < 0) {
- ds_put_format(&ds, "(%s)", strerror(-e->error));
+ ds_put_format(&ds, "(%s)", ovs_strerror(-e->error));
}
ds_put_cstr(&ds, ", in-reply-to(");
nlmsghdr_to_string(&e->msg, protocol, &ds);
if (error) {
ds_put_format(&ds, " done(%d", *error);
if (*error < 0) {
- ds_put_format(&ds, "(%s)", strerror(-*error));
+ ds_put_format(&ds, "(%s)", ovs_strerror(-*error));
}
ds_put_cstr(&ds, ")");
} else {
ofpbuf_use_const(&buffer, message, size);
nlmsg = nlmsg_to_string(&buffer, protocol);
- VLOG_DBG_RL(&rl, "%s (%s): %s", function, strerror(error), nlmsg);
+ VLOG_DBG_RL(&rl, "%s (%s): %s", function, ovs_strerror(error), nlmsg);
free(nlmsg);
}