#include "fatal-signal.h"
#include "ofpbuf.h"
#include "openflow/openflow.h"
+#include "ovs-thread.h"
#include "packets.h"
#include "poll-loop.h"
#include "socket-util.h"
int fd;
};
-static const struct netdev_rx_class netdev_rx_bsd_class;
-
struct netdev_bsd {
struct netdev up;
unsigned int cache_valid;
int ifindex;
uint8_t etheraddr[ETH_ADDR_LEN];
struct in_addr in4;
+ struct in_addr netmask;
struct in6_addr in6;
int mtu;
int carrier;
VALID_CARRIER = 1 << 5
};
-/* An AF_INET socket (used for ioctl operations). */
-static int af_inet_sock = -1;
-
-#if defined(__NetBSD__)
-/* AF_LINK socket used for netdev_bsd_get_stats and set_etheraddr */
-static int af_link_sock = -1;
-#endif /* defined(__NetBSD__) */
-
#define PCAP_SNAPLEN 2048
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20);
-static int netdev_bsd_do_ioctl(const char *, struct ifreq *, unsigned long cmd,
- const char *cmd_name);
static void destroy_tap(int fd, const char *name);
static int get_flags(const struct netdev *, int *flagsp);
static int set_flags(const char *, int flags);
static int ifr_get_flags(const struct ifreq *);
static void ifr_set_flags(struct ifreq *, int flags);
-static int netdev_bsd_init(void);
+#ifdef __NetBSD__
+static int af_link_ioctl(int command, const void *arg);
+#endif
+
+static void netdev_bsd_run(void);
static bool
is_netdev_bsd_class(const struct netdev_class *netdev_class)
{
- return netdev_class->init == netdev_bsd_init;
+ return netdev_class->run == netdev_bsd_run;
}
static struct netdev_bsd *
static struct netdev_rx_bsd *
netdev_rx_bsd_cast(const struct netdev_rx *rx)
{
- netdev_rx_assert_class(rx, &netdev_rx_bsd_class);
+ ovs_assert(is_netdev_bsd_class(netdev_get_class(rx->netdev)));
return CONTAINER_OF(rx, struct netdev_rx_bsd, up);
}
return netdev_bsd_cast(netdev)->kernel_name;
}
-/* Initialize the AF_INET socket used for ioctl operations */
-static int
-netdev_bsd_init(void)
-{
- static int status = -1;
-
- if (status >= 0) { /* already initialized */
- return status;
- }
-
- af_inet_sock = socket(AF_INET, SOCK_DGRAM, 0);
- status = af_inet_sock >= 0 ? 0 : errno;
- if (status) {
- VLOG_ERR("failed to create inet socket: %s", ovs_strerror(status));
- return status;
- }
-
-#if defined(__NetBSD__)
- af_link_sock = socket(AF_LINK, SOCK_DGRAM, 0);
- status = af_link_sock >= 0 ? 0 : errno;
- if (status) {
- VLOG_ERR("failed to create link socket: %s", ovs_strerror(status));
- close(af_inet_sock);
- af_inet_sock = -1;
- }
-#endif /* defined(__NetBSD__) */
-
- return status;
-}
-
/*
* Perform periodic work needed by netdev. In BSD netdevs it checks for any
* interface status changes, and eventually calls all the user callbacks.
dev->cache_valid = 0;
netdev_bsd_changed(dev);
}
+ netdev_close(base_dev);
}
} else {
/*
shash_init(&device_shash);
netdev_get_devices(&netdev_bsd_class, &device_shash);
SHASH_FOR_EACH (node, &device_shash) {
- dev = node->data;
+ struct netdev *netdev = node->data;
+ dev = netdev_bsd_cast(netdev);
dev->cache_valid = 0;
netdev_bsd_changed(dev);
+ netdev_close(netdev);
}
shash_destroy(&device_shash);
}
return 0;
}
-/* Allocate a netdev_bsd structure */
+static struct netdev *
+netdev_bsd_alloc(void)
+{
+ struct netdev_bsd *netdev = xzalloc(sizeof *netdev);
+ return &netdev->up;
+}
+
static int
-netdev_bsd_create_system(const struct netdev_class *class, const char *name,
- struct netdev **netdevp)
+netdev_bsd_construct_system(struct netdev *netdev_)
{
- struct netdev_bsd *netdev;
+ struct netdev_bsd *netdev = netdev_bsd_cast(netdev_);
enum netdev_flags flags;
int error;
return error;
}
- netdev = xzalloc(sizeof *netdev);
netdev->change_seq = 1;
- netdev_init(&netdev->up, name, class);
netdev->tap_fd = -1;
- netdev->kernel_name = xstrdup(name);
+ netdev->kernel_name = xstrdup(netdev_->name);
/* Verify that the netdev really exists by attempting to read its flags */
- error = netdev_get_flags(&netdev->up, &flags);
+ error = netdev_get_flags(netdev_, &flags);
if (error == ENXIO) {
free(netdev->kernel_name);
- netdev_uninit(&netdev->up, false);
- free(netdev);
cache_notifier_unref();
return error;
}
- *netdevp = &netdev->up;
return 0;
}
-/*
- * Allocate a netdev_bsd structure with 'tap' class.
- */
static int
-netdev_bsd_create_tap(const struct netdev_class *class, const char *name,
- struct netdev **netdevp)
+netdev_bsd_construct_tap(struct netdev *netdev_)
{
- struct netdev_bsd *netdev = NULL;
+ struct netdev_bsd *netdev = netdev_bsd_cast(netdev_);
+ const char *name = netdev_->name;
int error = 0;
struct ifreq ifr;
char *kernel_name = NULL;
goto error;
}
- /* allocate the device structure and set the internal flag */
- netdev = xzalloc(sizeof *netdev);
-
memset(&ifr, 0, sizeof(ifr));
/* Create a tap device by opening /dev/tap. The TAPGIFNAME ioctl is used
/* Change the name of the tap device */
#if defined(SIOCSIFNAME)
ifr.ifr_data = (void *)name;
- if (ioctl(af_inet_sock, SIOCSIFNAME, &ifr) == -1) {
- error = errno;
+ error = af_inet_ioctl(SIOCSIFNAME, &ifr);
+ if (error) {
destroy_tap(netdev->tap_fd, ifr.ifr_name);
goto error_unref_notifier;
}
/* Turn device UP */
ifr_set_flags(&ifr, IFF_UP);
strncpy(ifr.ifr_name, kernel_name, sizeof ifr.ifr_name);
- if (ioctl(af_inet_sock, SIOCSIFFLAGS, &ifr) == -1) {
- error = errno;
+ error = af_inet_ioctl(SIOCSIFFLAGS, &ifr);
+ if (error) {
destroy_tap(netdev->tap_fd, kernel_name);
goto error_unref_notifier;
}
- /* initialize the device structure and
- * link the structure to its netdev */
- netdev_init(&netdev->up, name, class);
netdev->kernel_name = kernel_name;
- *netdevp = &netdev->up;
return 0;
error_unref_notifier:
cache_notifier_unref();
error:
- free(netdev);
free(kernel_name);
return error;
}
static void
-netdev_bsd_destroy(struct netdev *netdev_)
+netdev_bsd_destruct(struct netdev *netdev_)
{
struct netdev_bsd *netdev = netdev_bsd_cast(netdev_);
pcap_close(netdev->pcap);
}
free(netdev->kernel_name);
+}
+
+static void
+netdev_bsd_dealloc(struct netdev *netdev_)
+{
+ struct netdev_bsd *netdev = netdev_bsd_cast(netdev_);
+
free(netdev);
}
return error;
}
+static struct netdev_rx *
+netdev_bsd_rx_alloc(void)
+{
+ struct netdev_rx_bsd *rx = xzalloc(sizeof *rx);
+ return &rx->up;
+}
+
static int
-netdev_bsd_rx_open(struct netdev *netdev_, struct netdev_rx **rxp)
+netdev_bsd_rx_construct(struct netdev_rx *rx_)
{
+ struct netdev_rx_bsd *rx = netdev_rx_bsd_cast(rx_);
+ struct netdev *netdev_ = rx->up.netdev;
struct netdev_bsd *netdev = netdev_bsd_cast(netdev_);
- struct netdev_rx_bsd *rx;
- pcap_t *pcap;
- int fd;
-
if (!strcmp(netdev_get_type(netdev_), "tap")) {
- pcap = NULL;
- fd = netdev->tap_fd;
+ rx->pcap_handle = NULL;
+ rx->fd = netdev->tap_fd;
} else {
int error = netdev_bsd_open_pcap(netdev_get_kernel_name(netdev_),
- &pcap, &fd);
+ &rx->pcap_handle, &rx->fd);
if (error) {
return error;
}
netdev_bsd_changed(netdev);
}
- rx = xmalloc(sizeof *rx);
- netdev_rx_init(&rx->up, netdev_, &netdev_rx_bsd_class);
- rx->pcap_handle = pcap;
- rx->fd = fd;
-
- *rxp = &rx->up;
return 0;
}
static void
-netdev_rx_bsd_destroy(struct netdev_rx *rx_)
+netdev_bsd_rx_destruct(struct netdev_rx *rx_)
{
struct netdev_rx_bsd *rx = netdev_rx_bsd_cast(rx_);
if (rx->pcap_handle) {
pcap_close(rx->pcap_handle);
}
+}
+
+static void
+netdev_bsd_rx_dealloc(struct netdev_rx *rx_)
+{
+ struct netdev_rx_bsd *rx = netdev_rx_bsd_cast(rx_);
+
free(rx);
}
}
}
-
static int
-netdev_rx_bsd_recv(struct netdev_rx *rx_, void *data, size_t size)
+netdev_bsd_rx_recv(struct netdev_rx *rx_, void *data, size_t size)
{
struct netdev_rx_bsd *rx = netdev_rx_bsd_cast(rx_);
* when a packet is ready to be received with netdev_rx_recv() on 'rx'.
*/
static void
-netdev_rx_bsd_wait(struct netdev_rx *rx_)
+netdev_bsd_rx_wait(struct netdev_rx *rx_)
{
struct netdev_rx_bsd *rx = netdev_rx_bsd_cast(rx_);
/* Discards all packets waiting to be received from 'rx'. */
static int
-netdev_rx_bsd_drain(struct netdev_rx *rx_)
+netdev_bsd_rx_drain(struct netdev_rx *rx_)
{
struct ifreq ifr;
struct netdev_rx_bsd *rx = netdev_rx_bsd_cast(rx_);
struct ifreq ifr;
int error;
- error = netdev_bsd_do_ioctl(netdev_get_kernel_name(netdev_), &ifr,
+ error = af_inet_ifreq_ioctl(netdev_get_kernel_name(netdev_), &ifr,
SIOCGIFMTU, "SIOCGIFMTU");
if (error) {
return error;
if (!(netdev->cache_valid & VALID_CARRIER)) {
struct ifmediareq ifmr;
+ int error;
memset(&ifmr, 0, sizeof(ifmr));
strncpy(ifmr.ifm_name, netdev_get_kernel_name(netdev_),
sizeof ifmr.ifm_name);
- if (ioctl(af_inet_sock, SIOCGIFMEDIA, &ifmr) == -1) {
+ error = af_inet_ioctl(SIOCGIFMEDIA, &ifmr);
+ if (error) {
VLOG_DBG_RL(&rl, "%s: ioctl(SIOCGIFMEDIA) failed: %s",
- netdev_get_name(netdev_), ovs_strerror(errno));
- return errno;
+ netdev_get_name(netdev_), ovs_strerror(error));
+ return error;
}
netdev->carrier = (ifmr.ifm_status & IFM_ACTIVE) == IFM_ACTIVE;
return 0;
#elif defined(__NetBSD__)
struct ifdatareq ifdr;
- int saved_errno;
- int ret;
+ int error;
memset(&ifdr, 0, sizeof(ifdr));
strncpy(ifdr.ifdr_name, netdev_get_kernel_name(netdev_),
sizeof(ifdr.ifdr_name));
- ret = ioctl(af_link_sock, SIOCGIFDATA, &ifdr);
- saved_errno = errno;
- if (ret == -1) {
- return saved_errno;
+ error = af_link_ioctl(SIOCGIFDATA, &ifdr);
+ if (!error) {
+ convert_stats(stats, &ifdr.ifdr_data);
}
- convert_stats(stats, &ifdr.ifdr_data);
- return 0;
+ return error;
#else
#error not implemented
#endif
/* We make two SIOCGIFMEDIA ioctl calls. The first to determine the
* number of supported modes, and a second with a buffer to retrieve
* them. */
- if (ioctl(af_inet_sock, SIOCGIFMEDIA, &ifmr) == -1) {
+ error = af_inet_ioctl(SIOCGIFMEDIA, &ifmr);
+ if (error) {
VLOG_DBG_RL(&rl, "%s: ioctl(SIOCGIFMEDIA) failed: %s",
- netdev_get_name(netdev), ovs_strerror(errno));
- return errno;
+ netdev_get_name(netdev), ovs_strerror(error));
+ return error;
}
media_list = xcalloc(ifmr.ifm_count, sizeof(int));
goto cleanup;
}
- if (ioctl(af_inet_sock, SIOCGIFMEDIA, &ifmr) == -1) {
+ error = af_inet_ioctl(SIOCGIFMEDIA, &ifmr);
+ if (error) {
VLOG_DBG_RL(&rl, "%s: ioctl(SIOCGIFMEDIA) failed: %s",
- netdev_get_name(netdev), ovs_strerror(errno));
- error = errno;
+ netdev_get_name(netdev), ovs_strerror(error));
goto cleanup;
}
}
/*
- * If 'netdev' has an assigned IPv4 address, sets '*in4' to that address (if
- * 'in4' is non-null) and returns true. Otherwise, returns false.
+ * If 'netdev' has an assigned IPv4 address, sets '*in4' to that address and
+ * '*netmask' to its netmask and returns true. Otherwise, returns false.
*/
static int
netdev_bsd_get_in4(const struct netdev *netdev_, struct in_addr *in4,
int error;
ifr.ifr_addr.sa_family = AF_INET;
- error = netdev_bsd_do_ioctl(netdev_get_kernel_name(netdev_), &ifr,
+ error = af_inet_ifreq_ioctl(netdev_get_kernel_name(netdev_), &ifr,
SIOCGIFADDR, "SIOCGIFADDR");
if (error) {
return error;
sin = (struct sockaddr_in *) &ifr.ifr_addr;
netdev->in4 = sin->sin_addr;
- netdev->cache_valid |= VALID_IN4;
- error = netdev_bsd_do_ioctl(netdev_get_kernel_name(netdev_), &ifr,
+ error = af_inet_ifreq_ioctl(netdev_get_kernel_name(netdev_), &ifr,
SIOCGIFNETMASK, "SIOCGIFNETMASK");
if (error) {
return error;
}
- *netmask = ((struct sockaddr_in*)&ifr.ifr_addr)->sin_addr;
+ netdev->netmask = sin->sin_addr;
+ netdev->cache_valid |= VALID_IN4;
}
*in4 = netdev->in4;
+ *netmask = netdev->netmask;
return in4->s_addr == INADDR_ANY ? EADDRNOTAVAIL : 0;
}
error = do_set_addr(netdev_, SIOCSIFADDR, "SIOCSIFADDR", addr);
if (!error) {
- netdev->cache_valid |= VALID_IN4;
- netdev->in4 = addr;
if (addr.s_addr != INADDR_ANY) {
error = do_set_addr(netdev_, SIOCSIFNETMASK,
"SIOCSIFNETMASK", mask);
+ if (!error) {
+ netdev->cache_valid |= VALID_IN4;
+ netdev->in4 = addr;
+ netdev->netmask = mask;
+ }
}
netdev_bsd_changed(netdev);
}
}
#if defined(__NetBSD__)
-static struct netdev *
-find_netdev_by_kernel_name(const char *kernel_name)
+static char *
+netdev_bsd_kernel_name_to_ovs_name(const char *kernel_name)
{
+ char *ovs_name = NULL;
struct shash device_shash;
struct shash_node *node;
shash_init(&device_shash);
netdev_get_devices(&netdev_tap_class, &device_shash);
SHASH_FOR_EACH(node, &device_shash) {
- struct netdev_bsd * const dev = node->data;
+ struct netdev *netdev = node->data;
+ struct netdev_bsd * const dev = netdev_bsd_cast(netdev);
if (!strcmp(dev->kernel_name, kernel_name)) {
- shash_destroy(&device_shash);
- return &dev->up;
+ free(ovs_name);
+ ovs_name = xstrdup(netdev_get_name(&dev->up));
}
+ netdev_close(netdev);
}
shash_destroy(&device_shash);
- return NULL;
-}
-static const char *
-netdev_bsd_convert_kernel_name_to_ovs_name(const char *kernel_name)
-{
- const struct netdev * const netdev =
- find_netdev_by_kernel_name(kernel_name);
-
- if (netdev == NULL) {
- return NULL;
- }
- return netdev_get_name(netdev);
+ return ovs_name ? ovs_name : xstrdup(kernel_name);
}
#endif
if ((i == RTA_IFP) && sa->sa_family == AF_LINK) {
const struct sockaddr_dl * const sdl =
(const struct sockaddr_dl *)sa;
- const size_t nlen = sdl->sdl_nlen;
- char * const kernel_name = xmalloc(nlen + 1);
- const char *name;
-
- memcpy(kernel_name, sdl->sdl_data, nlen);
- kernel_name[nlen] = 0;
- name = netdev_bsd_convert_kernel_name_to_ovs_name(kernel_name);
- if (name == NULL) {
- ifname = xstrdup(kernel_name);
- } else {
- ifname = xstrdup(name);
- }
+ char *kernel_name;
+
+ kernel_name = xmemdup0(sdl->sdl_data, sdl->sdl_nlen);
+ ifname = netdev_bsd_kernel_name_to_ovs_name(kernel_name);
free(kernel_name);
}
RT_ADVANCE(cp, sa);
{
struct ifreq ifr;
make_in4_sockaddr(&ifr.ifr_addr, addr);
- return netdev_bsd_do_ioctl(netdev_get_kernel_name(netdev), &ifr, ioctl_nr,
+ return af_inet_ifreq_ioctl(netdev_get_kernel_name(netdev), &ifr, ioctl_nr,
ioctl_name);
}
const struct netdev_class netdev_bsd_class = {
"system",
- netdev_bsd_init,
+ NULL, /* init */
netdev_bsd_run,
netdev_bsd_wait,
- netdev_bsd_create_system,
- netdev_bsd_destroy,
+ netdev_bsd_alloc,
+ netdev_bsd_construct_system,
+ netdev_bsd_destruct,
+ netdev_bsd_dealloc,
NULL, /* get_config */
NULL, /* set_config */
NULL, /* get_tunnel_config */
- netdev_bsd_rx_open,
-
netdev_bsd_send,
netdev_bsd_send_wait,
netdev_bsd_update_flags,
- netdev_bsd_change_seq
+ netdev_bsd_change_seq,
+
+ netdev_bsd_rx_alloc,
+ netdev_bsd_rx_construct,
+ netdev_bsd_rx_destruct,
+ netdev_bsd_rx_dealloc,
+ netdev_bsd_rx_recv,
+ netdev_bsd_rx_wait,
+ netdev_bsd_rx_drain,
};
const struct netdev_class netdev_tap_class = {
"tap",
- netdev_bsd_init,
+ NULL, /* init */
netdev_bsd_run,
netdev_bsd_wait,
- netdev_bsd_create_tap,
- netdev_bsd_destroy,
+ netdev_bsd_alloc,
+ netdev_bsd_construct_tap,
+ netdev_bsd_destruct,
+ netdev_bsd_dealloc,
NULL, /* get_config */
NULL, /* set_config */
NULL, /* get_tunnel_config */
- netdev_bsd_rx_open,
-
netdev_bsd_send,
netdev_bsd_send_wait,
netdev_bsd_update_flags,
- netdev_bsd_change_seq
-};
+ netdev_bsd_change_seq,
-static const struct netdev_rx_class netdev_rx_bsd_class = {
- netdev_rx_bsd_destroy,
- netdev_rx_bsd_recv,
- netdev_rx_bsd_wait,
- netdev_rx_bsd_drain,
+ netdev_bsd_rx_alloc,
+ netdev_bsd_rx_construct,
+ netdev_bsd_rx_destruct,
+ netdev_bsd_rx_dealloc,
+ netdev_bsd_rx_recv,
+ netdev_bsd_rx_wait,
+ netdev_bsd_rx_drain,
};
\f
close(fd);
strcpy(ifr.ifr_name, name);
/* XXX What to do if this call fails? */
- ioctl(af_inet_sock, SIOCIFDESTROY, &ifr);
+ af_inet_ioctl(SIOCIFDESTROY, &ifr);
}
static int
struct ifreq ifr;
int error;
- error = netdev_bsd_do_ioctl(netdev_get_kernel_name(netdev), &ifr,
+ error = af_inet_ifreq_ioctl(netdev_get_kernel_name(netdev), &ifr,
SIOCGIFFLAGS, "SIOCGIFFLAGS");
*flags = ifr_get_flags(&ifr);
ifr_set_flags(&ifr, flags);
- return netdev_bsd_do_ioctl(name, &ifr, SIOCSIFFLAGS, "SIOCSIFFLAGS");
+ return af_inet_ifreq_ioctl(name, &ifr, SIOCSIFFLAGS, "SIOCSIFFLAGS");
}
static int
{
#if defined(__FreeBSD__)
struct ifreq ifr;
+ int error;
memset(&ifr, 0, sizeof ifr);
strncpy(ifr.ifr_name, netdev_name, sizeof ifr.ifr_name);
ifr.ifr_addr.sa_family = hwaddr_family;
ifr.ifr_addr.sa_len = hwaddr_len;
memcpy(ifr.ifr_addr.sa_data, mac, hwaddr_len);
- if (ioctl(af_inet_sock, SIOCSIFLLADDR, &ifr) < 0) {
+ error = af_inet_ioctl(SIOCSIFLLADDR, &ifr);
+ if (error) {
VLOG_ERR("ioctl(SIOCSIFLLADDR) on %s device failed: %s",
- netdev_name, ovs_strerror(errno));
- return errno;
+ netdev_name, ovs_strerror(error));
+ return error;
}
return 0;
#elif defined(__NetBSD__)
struct if_laddrreq req;
struct sockaddr_dl *sdl;
struct sockaddr_storage oldaddr;
- int ret;
+ int error;
/*
* get the old address, add new one, and then remove old one.
req.addr.ss_family = hwaddr_family;
sdl = (struct sockaddr_dl *)&req.addr;
sdl->sdl_alen = hwaddr_len;
- ret = ioctl(af_link_sock, SIOCGLIFADDR, &req);
- if (ret == -1) {
- return errno;
+
+ error = af_link_ioctl(SIOCGLIFADDR, &req);
+ if (error) {
+ return error;
}
if (!memcmp(&sdl->sdl_data[sdl->sdl_nlen], mac, hwaddr_len)) {
return 0;
sdl->sdl_alen = hwaddr_len;
sdl->sdl_family = hwaddr_family;
memcpy(sdl->sdl_data, mac, hwaddr_len);
- ret = ioctl(af_link_sock, SIOCALIFADDR, &req);
- if (ret == -1) {
- return errno;
+ error = af_link_ioctl(SIOCALIFADDR, &req);
+ if (error) {
+ return error;
}
memset(&req, 0, sizeof(req));
strncpy(req.iflr_name, netdev_name, sizeof(req.iflr_name));
req.addr = oldaddr;
- ret = ioctl(af_link_sock, SIOCDLIFADDR, &req);
- if (ret == -1) {
- return errno;
- }
- return 0;
+ return af_link_ioctl(SIOCDLIFADDR, &req);
#else
#error not implemented
#endif
}
-static int
-netdev_bsd_do_ioctl(const char *name, struct ifreq *ifr, unsigned long cmd,
- const char *cmd_name)
-{
- strncpy(ifr->ifr_name, name, sizeof ifr->ifr_name);
- if (ioctl(af_inet_sock, cmd, ifr) == -1) {
- VLOG_DBG_RL(&rl, "%s: ioctl(%s) failed: %s", name, cmd_name,
- ovs_strerror(errno));
- return errno;
- }
- return 0;
-}
-
static int
ifr_get_flags(const struct ifreq *ifr)
{
ifr->ifr_flagshigh = flags >> 16;
#endif
}
+
+/* Calls ioctl() on an AF_LINK sock, passing the specified 'command' and
+ * 'arg'. Returns 0 if successful, otherwise a positive errno value. */
+int
+af_link_ioctl(int command, const void *arg)
+{
+ static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER;
+ static int sock;
+
+ if (ovsthread_once_start(&once)) {
+ sock = socket(AF_LINK, SOCK_DGRAM, 0);
+ if (sock < 0) {
+ sock = -errno;
+ VLOG_ERR("failed to create link socket: %s", ovs_strerror(errno));
+ }
+ ovsthread_once_done(&once);
+ }
+
+ return (sock < 0 ? -sock
+ : ioctl(sock, command, arg) == -1 ? errno
+ : 0);
+}