2012-07-25 22:51:05 +02:00
|
|
|
|
/*
|
2014-06-11 09:14:54 -07:00
|
|
|
|
* Copyright (c) 2011, 2013, 2014 Gaetano Catalli.
|
2014-03-26 05:13:43 +09:00
|
|
|
|
* Copyright (c) 2013, 2014 YAMAMOTO Takashi.
|
2012-07-25 22:51:05 +02:00
|
|
|
|
*
|
|
|
|
|
* 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.
|
|
|
|
|
*/
|
|
|
|
|
|
2016-03-15 11:52:58 -04:00
|
|
|
|
#if !defined(__MACH__)
|
2012-07-25 22:51:05 +02:00
|
|
|
|
#include <config.h>
|
|
|
|
|
|
2013-05-10 14:39:19 -07:00
|
|
|
|
#include "netdev-provider.h"
|
2012-07-25 22:51:05 +02:00
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <errno.h>
|
|
|
|
|
#include <fcntl.h>
|
|
|
|
|
#include <sys/types.h>
|
|
|
|
|
#include <sys/time.h>
|
|
|
|
|
#include <sys/ioctl.h>
|
|
|
|
|
#include <sys/socket.h>
|
|
|
|
|
#include <sys/sockio.h>
|
2015-05-19 12:24:26 -07:00
|
|
|
|
#include <net/bpf.h>
|
2012-07-25 22:51:05 +02:00
|
|
|
|
#include <ifaddrs.h>
|
|
|
|
|
#include <pcap/pcap.h>
|
|
|
|
|
#include <net/if.h>
|
|
|
|
|
#include <net/if_dl.h>
|
|
|
|
|
#include <net/if_media.h>
|
|
|
|
|
#include <net/if_tap.h>
|
|
|
|
|
#include <netinet/in.h>
|
2013-05-21 17:49:54 +09:00
|
|
|
|
#ifdef HAVE_NET_IF_MIB_H
|
2012-07-25 22:51:05 +02:00
|
|
|
|
#include <net/if_mib.h>
|
2013-05-21 17:49:54 +09:00
|
|
|
|
#endif
|
2012-07-25 22:51:05 +02:00
|
|
|
|
#include <poll.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
#include <unistd.h>
|
|
|
|
|
#include <sys/sysctl.h>
|
2013-05-21 17:50:01 +09:00
|
|
|
|
#if defined(__NetBSD__)
|
|
|
|
|
#include <net/route.h>
|
2013-08-14 05:57:41 +09:00
|
|
|
|
#include <netinet/if_inarp.h>
|
2013-05-21 17:50:01 +09:00
|
|
|
|
#endif
|
2012-07-25 22:51:05 +02:00
|
|
|
|
|
|
|
|
|
#include "rtbsd.h"
|
|
|
|
|
#include "coverage.h"
|
2015-02-25 12:01:53 -08:00
|
|
|
|
#include "dp-packet.h"
|
2014-03-20 10:54:37 -07:00
|
|
|
|
#include "dpif-netdev.h"
|
2016-03-03 10:20:46 -08:00
|
|
|
|
#include "openvswitch/dynamic-string.h"
|
2012-07-25 22:51:05 +02:00
|
|
|
|
#include "fatal-signal.h"
|
|
|
|
|
#include "openflow/openflow.h"
|
2013-08-07 23:48:59 -07:00
|
|
|
|
#include "ovs-thread.h"
|
2012-07-25 22:51:05 +02:00
|
|
|
|
#include "packets.h"
|
2017-11-03 13:53:53 +08:00
|
|
|
|
#include "openvswitch/poll-loop.h"
|
2016-07-12 16:37:34 -05:00
|
|
|
|
#include "openvswitch/shash.h"
|
2013-12-13 03:33:46 +00:00
|
|
|
|
#include "socket-util.h"
|
2012-07-25 22:51:05 +02:00
|
|
|
|
#include "svec.h"
|
2012-11-06 13:14:55 -08:00
|
|
|
|
#include "util.h"
|
2014-12-15 14:10:38 +01:00
|
|
|
|
#include "openvswitch/vlog.h"
|
2012-07-25 22:51:05 +02:00
|
|
|
|
|
|
|
|
|
VLOG_DEFINE_THIS_MODULE(netdev_bsd);
|
|
|
|
|
|
2014-03-26 05:13:43 +09:00
|
|
|
|
struct netdev_rxq_bsd {
|
|
|
|
|
struct netdev_rxq up;
|
2012-07-25 22:51:05 +02:00
|
|
|
|
|
2013-05-10 14:39:19 -07:00
|
|
|
|
/* Packet capture descriptor for a system network device.
|
|
|
|
|
* For a tap device this is NULL. */
|
|
|
|
|
pcap_t *pcap_handle;
|
2012-07-25 22:51:05 +02:00
|
|
|
|
|
2013-05-10 14:39:19 -07:00
|
|
|
|
/* Selectable file descriptor for the network device.
|
|
|
|
|
* This descriptor will be used for polling operations. */
|
|
|
|
|
int fd;
|
2012-07-25 22:51:05 +02:00
|
|
|
|
};
|
|
|
|
|
|
2013-03-15 15:54:36 -07:00
|
|
|
|
struct netdev_bsd {
|
|
|
|
|
struct netdev up;
|
2013-08-09 21:34:02 -07:00
|
|
|
|
|
|
|
|
|
/* Never changes after initialization. */
|
|
|
|
|
char *kernel_name;
|
|
|
|
|
|
|
|
|
|
/* Protects all members below. */
|
|
|
|
|
struct ovs_mutex mutex;
|
|
|
|
|
|
2012-07-25 22:51:05 +02:00
|
|
|
|
unsigned int cache_valid;
|
|
|
|
|
|
|
|
|
|
int ifindex;
|
2015-08-28 14:55:11 -07:00
|
|
|
|
struct eth_addr etheraddr;
|
2012-07-25 22:51:05 +02:00
|
|
|
|
int mtu;
|
|
|
|
|
int carrier;
|
|
|
|
|
|
2013-05-10 14:39:19 -07:00
|
|
|
|
int tap_fd; /* TAP character device, if any, otherwise -1. */
|
|
|
|
|
|
|
|
|
|
/* Used for sending packets on non-tap devices. */
|
|
|
|
|
pcap_t *pcap;
|
|
|
|
|
int fd;
|
2012-07-25 22:51:05 +02:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
enum {
|
2016-03-24 09:30:57 -07:00
|
|
|
|
VALID_IFINDEX = 1 << 0,
|
2012-07-25 22:51:05 +02:00
|
|
|
|
VALID_ETHERADDR = 1 << 1,
|
2016-03-24 09:30:57 -07:00
|
|
|
|
VALID_IN = 1 << 2,
|
|
|
|
|
VALID_MTU = 1 << 3,
|
|
|
|
|
VALID_CARRIER = 1 << 4
|
2012-07-25 22:51:05 +02:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
#define PCAP_SNAPLEN 2048
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Notifier used to invalidate device informations in case of status change.
|
|
|
|
|
*
|
|
|
|
|
* It will be registered with a 'rtbsd_notifier_register()' when the first
|
|
|
|
|
* device will be created with the call of either 'netdev_bsd_tap_create()' or
|
|
|
|
|
* 'netdev_bsd_system_create()'.
|
|
|
|
|
*
|
|
|
|
|
* The callback associated with this notifier ('netdev_bsd_cache_cb()') will
|
|
|
|
|
* invalidate cached information about the device.
|
|
|
|
|
*/
|
|
|
|
|
static struct rtbsd_notifier netdev_bsd_cache_notifier;
|
|
|
|
|
static int cache_notifier_refcount;
|
|
|
|
|
|
|
|
|
|
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20);
|
|
|
|
|
|
|
|
|
|
static void destroy_tap(int fd, const char *name);
|
2013-03-15 15:54:36 -07:00
|
|
|
|
static int get_flags(const struct netdev *, int *flagsp);
|
2013-05-10 08:55:25 -07:00
|
|
|
|
static int set_flags(const char *, int flags);
|
2012-07-25 22:51:05 +02:00
|
|
|
|
static int do_set_addr(struct netdev *netdev,
|
2013-08-13 07:20:22 +09:00
|
|
|
|
unsigned long ioctl_nr, const char *ioctl_name,
|
2012-07-25 22:51:05 +02:00
|
|
|
|
struct in_addr addr);
|
2015-08-28 14:55:11 -07:00
|
|
|
|
static int get_etheraddr(const char *netdev_name, struct eth_addr *ea);
|
2012-07-25 22:51:05 +02:00
|
|
|
|
static int set_etheraddr(const char *netdev_name, int hwaddr_family,
|
2015-08-28 14:55:11 -07:00
|
|
|
|
int hwaddr_len, const struct eth_addr);
|
2012-07-25 22:51:05 +02:00
|
|
|
|
static int get_ifindex(const struct netdev *, int *ifindexp);
|
|
|
|
|
|
2013-05-21 17:49:54 +09:00
|
|
|
|
static int ifr_get_flags(const struct ifreq *);
|
|
|
|
|
static void ifr_set_flags(struct ifreq *, int flags);
|
|
|
|
|
|
2013-08-07 23:48:59 -07:00
|
|
|
|
#ifdef __NetBSD__
|
2013-08-13 07:20:22 +09:00
|
|
|
|
static int af_link_ioctl(unsigned long command, const void *arg);
|
2013-08-07 23:48:59 -07:00
|
|
|
|
#endif
|
|
|
|
|
|
2016-05-19 11:22:36 -07:00
|
|
|
|
static void netdev_bsd_run(const struct netdev_class *);
|
2014-03-20 10:54:37 -07:00
|
|
|
|
static int netdev_bsd_get_mtu(const struct netdev *netdev_, int *mtup);
|
2012-07-25 22:51:05 +02:00
|
|
|
|
|
|
|
|
|
static bool
|
|
|
|
|
is_netdev_bsd_class(const struct netdev_class *netdev_class)
|
|
|
|
|
{
|
2013-08-07 23:48:59 -07:00
|
|
|
|
return netdev_class->run == netdev_bsd_run;
|
2012-07-25 22:51:05 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static struct netdev_bsd *
|
|
|
|
|
netdev_bsd_cast(const struct netdev *netdev)
|
|
|
|
|
{
|
2013-03-15 15:54:36 -07:00
|
|
|
|
ovs_assert(is_netdev_bsd_class(netdev_get_class(netdev)));
|
2013-03-08 15:09:42 -08:00
|
|
|
|
return CONTAINER_OF(netdev, struct netdev_bsd, up);
|
2012-07-25 22:51:05 +02:00
|
|
|
|
}
|
|
|
|
|
|
2014-03-26 05:13:43 +09:00
|
|
|
|
static struct netdev_rxq_bsd *
|
|
|
|
|
netdev_rxq_bsd_cast(const struct netdev_rxq *rxq)
|
2013-05-10 14:39:19 -07:00
|
|
|
|
{
|
2014-03-26 05:13:43 +09:00
|
|
|
|
ovs_assert(is_netdev_bsd_class(netdev_get_class(rxq->netdev)));
|
|
|
|
|
return CONTAINER_OF(rxq, struct netdev_rxq_bsd, up);
|
2013-05-10 14:39:19 -07:00
|
|
|
|
}
|
|
|
|
|
|
2013-05-21 17:49:55 +09:00
|
|
|
|
static const char *
|
|
|
|
|
netdev_get_kernel_name(const struct netdev *netdev)
|
|
|
|
|
{
|
|
|
|
|
return netdev_bsd_cast(netdev)->kernel_name;
|
|
|
|
|
}
|
|
|
|
|
|
2012-07-25 22:51:05 +02:00
|
|
|
|
/*
|
|
|
|
|
* Perform periodic work needed by netdev. In BSD netdevs it checks for any
|
|
|
|
|
* interface status changes, and eventually calls all the user callbacks.
|
|
|
|
|
*/
|
|
|
|
|
static void
|
2016-05-19 11:22:36 -07:00
|
|
|
|
netdev_bsd_run(const struct netdev_class *netdev_class OVS_UNUSED)
|
2012-07-25 22:51:05 +02:00
|
|
|
|
{
|
|
|
|
|
rtbsd_notifier_run();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Arranges for poll_block() to wake up if the "run" member function needs to
|
|
|
|
|
* be called.
|
|
|
|
|
*/
|
|
|
|
|
static void
|
2016-05-19 11:22:36 -07:00
|
|
|
|
netdev_bsd_wait(const struct netdev_class *netdev_class OVS_UNUSED)
|
2012-07-25 22:51:05 +02:00
|
|
|
|
{
|
|
|
|
|
rtbsd_notifier_wait();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Invalidate cache in case of interface status change. */
|
|
|
|
|
static void
|
|
|
|
|
netdev_bsd_cache_cb(const struct rtbsd_change *change,
|
2013-05-10 16:23:03 -04:00
|
|
|
|
void *aux OVS_UNUSED)
|
2012-07-25 22:51:05 +02:00
|
|
|
|
{
|
2013-03-15 15:54:36 -07:00
|
|
|
|
struct netdev_bsd *dev;
|
2012-07-25 22:51:05 +02:00
|
|
|
|
|
|
|
|
|
if (change) {
|
2013-03-15 15:54:36 -07:00
|
|
|
|
struct netdev *base_dev = netdev_from_name(change->if_name);
|
2012-07-25 22:51:05 +02:00
|
|
|
|
|
|
|
|
|
if (base_dev) {
|
|
|
|
|
const struct netdev_class *netdev_class =
|
2013-03-15 15:54:36 -07:00
|
|
|
|
netdev_get_class(base_dev);
|
2012-07-25 22:51:05 +02:00
|
|
|
|
|
|
|
|
|
if (is_netdev_bsd_class(netdev_class)) {
|
2013-03-15 15:54:36 -07:00
|
|
|
|
dev = netdev_bsd_cast(base_dev);
|
2012-07-25 22:51:05 +02:00
|
|
|
|
dev->cache_valid = 0;
|
2014-04-03 00:17:34 -07:00
|
|
|
|
netdev_change_seq_changed(base_dev);
|
2012-07-25 22:51:05 +02:00
|
|
|
|
}
|
2013-07-25 16:27:39 -07:00
|
|
|
|
netdev_close(base_dev);
|
2012-07-25 22:51:05 +02:00
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
/*
|
|
|
|
|
* XXX the API is lacking, we should be able to iterate on the list of
|
|
|
|
|
* netdevs without having to store the info in a temp shash.
|
|
|
|
|
*/
|
|
|
|
|
struct shash device_shash;
|
|
|
|
|
struct shash_node *node;
|
|
|
|
|
|
|
|
|
|
shash_init(&device_shash);
|
2013-03-15 15:54:36 -07:00
|
|
|
|
netdev_get_devices(&netdev_bsd_class, &device_shash);
|
2012-07-25 22:51:05 +02:00
|
|
|
|
SHASH_FOR_EACH (node, &device_shash) {
|
2013-08-02 12:19:49 -07:00
|
|
|
|
struct netdev *netdev = node->data;
|
|
|
|
|
dev = netdev_bsd_cast(netdev);
|
2012-07-25 22:51:05 +02:00
|
|
|
|
dev->cache_valid = 0;
|
2014-04-03 00:17:34 -07:00
|
|
|
|
netdev_change_seq_changed(netdev);
|
2013-07-25 16:27:39 -07:00
|
|
|
|
netdev_close(netdev);
|
2012-07-25 22:51:05 +02:00
|
|
|
|
}
|
|
|
|
|
shash_destroy(&device_shash);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
cache_notifier_ref(void)
|
|
|
|
|
{
|
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
|
|
if (!cache_notifier_refcount) {
|
|
|
|
|
ret = rtbsd_notifier_register(&netdev_bsd_cache_notifier,
|
2014-05-19 17:06:45 +09:00
|
|
|
|
netdev_bsd_cache_cb, NULL);
|
2012-07-25 22:51:05 +02:00
|
|
|
|
if (ret) {
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
cache_notifier_refcount++;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
cache_notifier_unref(void)
|
|
|
|
|
{
|
|
|
|
|
cache_notifier_refcount--;
|
|
|
|
|
if (cache_notifier_refcount == 0) {
|
|
|
|
|
rtbsd_notifier_unregister(&netdev_bsd_cache_notifier);
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2013-08-09 21:21:38 -07:00
|
|
|
|
static struct netdev *
|
|
|
|
|
netdev_bsd_alloc(void)
|
|
|
|
|
{
|
|
|
|
|
struct netdev_bsd *netdev = xzalloc(sizeof *netdev);
|
|
|
|
|
return &netdev->up;
|
|
|
|
|
}
|
|
|
|
|
|
2012-07-25 22:51:05 +02:00
|
|
|
|
static int
|
2013-08-09 21:21:38 -07:00
|
|
|
|
netdev_bsd_construct_system(struct netdev *netdev_)
|
2012-07-25 22:51:05 +02:00
|
|
|
|
{
|
2013-08-09 21:21:38 -07:00
|
|
|
|
struct netdev_bsd *netdev = netdev_bsd_cast(netdev_);
|
2013-03-15 15:54:36 -07:00
|
|
|
|
enum netdev_flags flags;
|
2012-07-25 22:51:05 +02:00
|
|
|
|
int error;
|
|
|
|
|
|
|
|
|
|
error = cache_notifier_ref();
|
|
|
|
|
if (error) {
|
|
|
|
|
return error;
|
|
|
|
|
}
|
|
|
|
|
|
Use "error-checking" mutexes in place of other kinds wherever possible.
We've seen a number of deadlocks in the tree since thread safety was
introduced. So far, all of these are self-deadlocks, that is, a single
thread acquiring a lock and then attempting to re-acquire the same lock
recursively. When this has happened, the process simply hung, and it was
somewhat difficult to find the cause.
POSIX "error-checking" mutexes check for this specific problem (and
others). This commit switches from other types of mutexes to
error-checking mutexes everywhere that we can, that is, everywhere that
we're not using recursive mutexes. This ought to help find problems more
quickly in the future.
There might be performance advantages to other kinds of mutexes in some
cases. However, the existing mutex type choices were just guesses, so I'd
rather go for easy detection of errors until we know that other mutex
types actually perform better in specific cases. Also, I did a quick
microbenchmark of glibc mutex types on my host and found that the
error checking mutexes weren't any slower than the other types, at least
when the mutex is uncontended.
Signed-off-by: Ben Pfaff <blp@nicira.com>
Acked-by: Ethan Jackson <ethan@nicira.com>
2013-08-20 13:40:02 -07:00
|
|
|
|
ovs_mutex_init(&netdev->mutex);
|
2013-03-15 15:54:36 -07:00
|
|
|
|
netdev->tap_fd = -1;
|
2013-08-09 21:21:38 -07:00
|
|
|
|
netdev->kernel_name = xstrdup(netdev_->name);
|
2012-07-25 22:51:05 +02:00
|
|
|
|
|
2013-03-15 15:54:36 -07:00
|
|
|
|
/* Verify that the netdev really exists by attempting to read its flags */
|
2013-08-09 21:21:38 -07:00
|
|
|
|
error = netdev_get_flags(netdev_, &flags);
|
2013-03-15 15:54:36 -07:00
|
|
|
|
if (error == ENXIO) {
|
2013-07-25 14:03:32 -07:00
|
|
|
|
free(netdev->kernel_name);
|
2013-03-15 15:54:36 -07:00
|
|
|
|
cache_notifier_unref();
|
2015-12-24 15:50:47 +08:00
|
|
|
|
ovs_mutex_destroy(&netdev->mutex);
|
2013-03-15 15:54:36 -07:00
|
|
|
|
return error;
|
|
|
|
|
}
|
|
|
|
|
|
2012-07-25 22:51:05 +02:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
2013-08-09 21:21:38 -07:00
|
|
|
|
netdev_bsd_construct_tap(struct netdev *netdev_)
|
2012-07-25 22:51:05 +02:00
|
|
|
|
{
|
2013-08-09 21:21:38 -07:00
|
|
|
|
struct netdev_bsd *netdev = netdev_bsd_cast(netdev_);
|
|
|
|
|
const char *name = netdev_->name;
|
2012-07-25 22:51:05 +02:00
|
|
|
|
int error = 0;
|
|
|
|
|
struct ifreq ifr;
|
2013-05-21 17:49:55 +09:00
|
|
|
|
char *kernel_name = NULL;
|
2012-07-25 22:51:05 +02:00
|
|
|
|
|
|
|
|
|
error = cache_notifier_ref();
|
|
|
|
|
if (error) {
|
|
|
|
|
goto error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
memset(&ifr, 0, sizeof(ifr));
|
|
|
|
|
|
|
|
|
|
/* Create a tap device by opening /dev/tap. The TAPGIFNAME ioctl is used
|
|
|
|
|
* to retrieve the name of the tap device. */
|
Use "error-checking" mutexes in place of other kinds wherever possible.
We've seen a number of deadlocks in the tree since thread safety was
introduced. So far, all of these are self-deadlocks, that is, a single
thread acquiring a lock and then attempting to re-acquire the same lock
recursively. When this has happened, the process simply hung, and it was
somewhat difficult to find the cause.
POSIX "error-checking" mutexes check for this specific problem (and
others). This commit switches from other types of mutexes to
error-checking mutexes everywhere that we can, that is, everywhere that
we're not using recursive mutexes. This ought to help find problems more
quickly in the future.
There might be performance advantages to other kinds of mutexes in some
cases. However, the existing mutex type choices were just guesses, so I'd
rather go for easy detection of errors until we know that other mutex
types actually perform better in specific cases. Also, I did a quick
microbenchmark of glibc mutex types on my host and found that the
error checking mutexes weren't any slower than the other types, at least
when the mutex is uncontended.
Signed-off-by: Ben Pfaff <blp@nicira.com>
Acked-by: Ethan Jackson <ethan@nicira.com>
2013-08-20 13:40:02 -07:00
|
|
|
|
ovs_mutex_init(&netdev->mutex);
|
2013-03-15 15:54:36 -07:00
|
|
|
|
netdev->tap_fd = open("/dev/tap", O_RDWR);
|
|
|
|
|
if (netdev->tap_fd < 0) {
|
2012-07-25 22:51:05 +02:00
|
|
|
|
error = errno;
|
2013-06-24 10:54:49 -07:00
|
|
|
|
VLOG_WARN("opening \"/dev/tap\" failed: %s", ovs_strerror(error));
|
2013-07-25 14:14:09 -07:00
|
|
|
|
goto error_unref_notifier;
|
2012-07-25 22:51:05 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Retrieve tap name (e.g. tap0) */
|
2013-03-15 15:54:36 -07:00
|
|
|
|
if (ioctl(netdev->tap_fd, TAPGIFNAME, &ifr) == -1) {
|
2012-07-25 22:51:05 +02:00
|
|
|
|
/* XXX Need to destroy the device? */
|
|
|
|
|
error = errno;
|
2013-07-25 14:41:12 -07:00
|
|
|
|
close(netdev->tap_fd);
|
2013-07-25 14:14:09 -07:00
|
|
|
|
goto error_unref_notifier;
|
2012-07-25 22:51:05 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Change the name of the tap device */
|
2013-05-21 17:49:54 +09:00
|
|
|
|
#if defined(SIOCSIFNAME)
|
2012-07-25 22:51:05 +02:00
|
|
|
|
ifr.ifr_data = (void *)name;
|
2013-08-09 21:14:23 -07:00
|
|
|
|
error = af_inet_ioctl(SIOCSIFNAME, &ifr);
|
|
|
|
|
if (error) {
|
2013-03-15 15:54:36 -07:00
|
|
|
|
destroy_tap(netdev->tap_fd, ifr.ifr_name);
|
2013-07-25 14:14:09 -07:00
|
|
|
|
goto error_unref_notifier;
|
2012-07-25 22:51:05 +02:00
|
|
|
|
}
|
2013-05-21 17:49:55 +09:00
|
|
|
|
kernel_name = xstrdup(name);
|
2013-05-21 17:49:54 +09:00
|
|
|
|
#else
|
|
|
|
|
/*
|
|
|
|
|
* NetBSD doesn't support inteface renaming.
|
|
|
|
|
*/
|
|
|
|
|
VLOG_INFO("tap %s is created for bridge %s", ifr.ifr_name, name);
|
2013-05-21 17:49:55 +09:00
|
|
|
|
kernel_name = xstrdup(ifr.ifr_name);
|
2013-05-21 17:49:54 +09:00
|
|
|
|
#endif
|
2012-07-25 22:51:05 +02:00
|
|
|
|
|
|
|
|
|
/* set non-blocking. */
|
2013-03-15 15:54:36 -07:00
|
|
|
|
error = set_nonblocking(netdev->tap_fd);
|
2012-07-25 22:51:05 +02:00
|
|
|
|
if (error) {
|
2013-05-21 17:49:55 +09:00
|
|
|
|
destroy_tap(netdev->tap_fd, kernel_name);
|
2013-07-25 14:14:09 -07:00
|
|
|
|
goto error_unref_notifier;
|
2012-07-25 22:51:05 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Turn device UP */
|
2013-05-21 17:49:54 +09:00
|
|
|
|
ifr_set_flags(&ifr, IFF_UP);
|
2015-02-20 12:32:08 -08:00
|
|
|
|
ovs_strlcpy(ifr.ifr_name, kernel_name, sizeof ifr.ifr_name);
|
2013-08-09 21:14:23 -07:00
|
|
|
|
error = af_inet_ioctl(SIOCSIFFLAGS, &ifr);
|
|
|
|
|
if (error) {
|
2013-05-21 17:49:55 +09:00
|
|
|
|
destroy_tap(netdev->tap_fd, kernel_name);
|
2013-07-25 14:14:09 -07:00
|
|
|
|
goto error_unref_notifier;
|
2012-07-25 22:51:05 +02:00
|
|
|
|
}
|
|
|
|
|
|
2013-05-21 17:49:55 +09:00
|
|
|
|
netdev->kernel_name = kernel_name;
|
2012-07-25 22:51:05 +02:00
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
2013-07-25 14:14:09 -07:00
|
|
|
|
error_unref_notifier:
|
2013-08-09 21:34:02 -07:00
|
|
|
|
ovs_mutex_destroy(&netdev->mutex);
|
2012-07-25 22:51:05 +02:00
|
|
|
|
cache_notifier_unref();
|
|
|
|
|
error:
|
2013-05-21 17:49:55 +09:00
|
|
|
|
free(kernel_name);
|
2012-07-25 22:51:05 +02:00
|
|
|
|
return error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2013-08-09 21:21:38 -07:00
|
|
|
|
netdev_bsd_destruct(struct netdev *netdev_)
|
2012-07-25 22:51:05 +02:00
|
|
|
|
{
|
2013-03-15 15:54:36 -07:00
|
|
|
|
struct netdev_bsd *netdev = netdev_bsd_cast(netdev_);
|
2012-07-25 22:51:05 +02:00
|
|
|
|
|
|
|
|
|
cache_notifier_unref();
|
|
|
|
|
|
2013-03-15 15:54:36 -07:00
|
|
|
|
if (netdev->tap_fd >= 0) {
|
2013-05-21 17:49:55 +09:00
|
|
|
|
destroy_tap(netdev->tap_fd, netdev_get_kernel_name(netdev_));
|
2012-07-25 22:51:05 +02:00
|
|
|
|
}
|
2013-03-15 15:54:36 -07:00
|
|
|
|
if (netdev->pcap) {
|
|
|
|
|
pcap_close(netdev->pcap);
|
2013-05-10 14:39:19 -07:00
|
|
|
|
}
|
2013-05-21 17:49:55 +09:00
|
|
|
|
free(netdev->kernel_name);
|
2013-08-09 21:34:02 -07:00
|
|
|
|
ovs_mutex_destroy(&netdev->mutex);
|
2013-08-09 21:21:38 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
netdev_bsd_dealloc(struct netdev *netdev_)
|
|
|
|
|
{
|
|
|
|
|
struct netdev_bsd *netdev = netdev_bsd_cast(netdev_);
|
|
|
|
|
|
2012-07-25 22:51:05 +02:00
|
|
|
|
free(netdev);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
2013-05-10 14:39:19 -07:00
|
|
|
|
netdev_bsd_open_pcap(const char *name, pcap_t **pcapp, int *fdp)
|
2012-07-25 22:51:05 +02:00
|
|
|
|
{
|
|
|
|
|
char errbuf[PCAP_ERRBUF_SIZE];
|
2013-05-10 14:39:19 -07:00
|
|
|
|
pcap_t *pcap = NULL;
|
2012-07-25 22:51:05 +02:00
|
|
|
|
int one = 1;
|
2013-05-10 14:39:19 -07:00
|
|
|
|
int error;
|
|
|
|
|
int fd;
|
2012-07-25 22:51:05 +02:00
|
|
|
|
|
2013-05-10 14:39:19 -07:00
|
|
|
|
/* Open the pcap device. The device is opened in non-promiscuous mode
|
2012-07-25 22:51:05 +02:00
|
|
|
|
* because the interface flags are manually set by the caller. */
|
|
|
|
|
errbuf[0] = '\0';
|
2013-05-10 14:39:19 -07:00
|
|
|
|
pcap = pcap_open_live(name, PCAP_SNAPLEN, 0, 1000, errbuf);
|
|
|
|
|
if (!pcap) {
|
|
|
|
|
VLOG_ERR_RL(&rl, "%s: pcap_open_live failed: %s", name, errbuf);
|
2012-07-25 22:51:05 +02:00
|
|
|
|
error = EIO;
|
|
|
|
|
goto error;
|
2013-05-10 14:39:19 -07:00
|
|
|
|
}
|
|
|
|
|
if (errbuf[0] != '\0') {
|
|
|
|
|
VLOG_WARN_RL(&rl, "%s: pcap_open_live: %s", name, errbuf);
|
2012-07-25 22:51:05 +02:00
|
|
|
|
}
|
|
|
|
|
|
2013-05-10 14:39:19 -07:00
|
|
|
|
/* Get the underlying fd. */
|
|
|
|
|
fd = pcap_get_selectable_fd(pcap);
|
2012-07-25 22:51:05 +02:00
|
|
|
|
if (fd == -1) {
|
2013-05-10 14:39:19 -07:00
|
|
|
|
VLOG_WARN_RL(&rl, "%s: no selectable file descriptor", name);
|
2012-07-25 22:51:05 +02:00
|
|
|
|
error = errno;
|
|
|
|
|
goto error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Set non-blocking mode. Also the BIOCIMMEDIATE ioctl must be called
|
|
|
|
|
* on the file descriptor returned by pcap_get_selectable_fd to achieve
|
|
|
|
|
* a real non-blocking behaviour.*/
|
2013-05-10 14:39:19 -07:00
|
|
|
|
error = pcap_setnonblock(pcap, 1, errbuf);
|
2012-07-25 22:51:05 +02:00
|
|
|
|
if (error == -1) {
|
|
|
|
|
error = errno;
|
|
|
|
|
goto error;
|
|
|
|
|
}
|
|
|
|
|
|
2013-05-10 14:39:19 -07:00
|
|
|
|
/* This call assure that reads return immediately upon packet
|
|
|
|
|
* reception. Otherwise, a read will block until either the kernel
|
|
|
|
|
* buffer becomes full or a timeout occurs. */
|
|
|
|
|
if (ioctl(fd, BIOCIMMEDIATE, &one) < 0 ) {
|
|
|
|
|
VLOG_ERR_RL(&rl, "ioctl(BIOCIMMEDIATE) on %s device failed: %s",
|
2013-06-24 10:54:49 -07:00
|
|
|
|
name, ovs_strerror(errno));
|
2012-07-25 22:51:05 +02:00
|
|
|
|
error = errno;
|
|
|
|
|
goto error;
|
|
|
|
|
}
|
|
|
|
|
|
2013-05-10 14:39:19 -07:00
|
|
|
|
/* Capture only incoming packets. */
|
|
|
|
|
error = pcap_setdirection(pcap, PCAP_D_IN);
|
2012-07-25 22:51:05 +02:00
|
|
|
|
if (error == -1) {
|
|
|
|
|
error = errno;
|
|
|
|
|
goto error;
|
|
|
|
|
}
|
|
|
|
|
|
2013-05-10 14:39:19 -07:00
|
|
|
|
*pcapp = pcap;
|
|
|
|
|
*fdp = fd;
|
2012-07-25 22:51:05 +02:00
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
error:
|
2013-05-10 14:39:19 -07:00
|
|
|
|
if (pcap) {
|
|
|
|
|
pcap_close(pcap);
|
2012-07-25 22:51:05 +02:00
|
|
|
|
}
|
2013-05-10 14:39:19 -07:00
|
|
|
|
*pcapp = NULL;
|
|
|
|
|
*fdp = -1;
|
2012-07-25 22:51:05 +02:00
|
|
|
|
return error;
|
|
|
|
|
}
|
|
|
|
|
|
2014-03-26 05:13:43 +09:00
|
|
|
|
static struct netdev_rxq *
|
|
|
|
|
netdev_bsd_rxq_alloc(void)
|
2013-08-09 21:21:38 -07:00
|
|
|
|
{
|
2014-03-26 05:13:43 +09:00
|
|
|
|
struct netdev_rxq_bsd *rxq = xzalloc(sizeof *rxq);
|
|
|
|
|
return &rxq->up;
|
2013-08-09 21:21:38 -07:00
|
|
|
|
}
|
|
|
|
|
|
2013-05-10 14:39:19 -07:00
|
|
|
|
static int
|
2014-03-26 05:13:43 +09:00
|
|
|
|
netdev_bsd_rxq_construct(struct netdev_rxq *rxq_)
|
2013-05-10 14:39:19 -07:00
|
|
|
|
{
|
2014-03-26 05:13:43 +09:00
|
|
|
|
struct netdev_rxq_bsd *rxq = netdev_rxq_bsd_cast(rxq_);
|
|
|
|
|
struct netdev *netdev_ = rxq->up.netdev;
|
2013-03-15 15:54:36 -07:00
|
|
|
|
struct netdev_bsd *netdev = netdev_bsd_cast(netdev_);
|
2013-08-09 21:34:02 -07:00
|
|
|
|
int error;
|
2013-05-10 14:39:19 -07:00
|
|
|
|
|
|
|
|
|
if (!strcmp(netdev_get_type(netdev_), "tap")) {
|
2014-03-26 05:13:43 +09:00
|
|
|
|
rxq->pcap_handle = NULL;
|
|
|
|
|
rxq->fd = netdev->tap_fd;
|
2013-08-09 21:34:02 -07:00
|
|
|
|
error = 0;
|
2013-05-10 14:39:19 -07:00
|
|
|
|
} else {
|
2013-08-09 21:34:02 -07:00
|
|
|
|
ovs_mutex_lock(&netdev->mutex);
|
|
|
|
|
error = netdev_bsd_open_pcap(netdev_get_kernel_name(netdev_),
|
2014-03-26 05:13:43 +09:00
|
|
|
|
&rxq->pcap_handle, &rxq->fd);
|
2013-08-09 21:34:02 -07:00
|
|
|
|
ovs_mutex_unlock(&netdev->mutex);
|
2013-05-10 14:39:19 -07:00
|
|
|
|
}
|
|
|
|
|
|
2013-08-09 21:34:02 -07:00
|
|
|
|
return error;
|
2013-05-10 14:39:19 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2014-03-26 05:13:43 +09:00
|
|
|
|
netdev_bsd_rxq_destruct(struct netdev_rxq *rxq_)
|
2013-05-10 14:39:19 -07:00
|
|
|
|
{
|
2014-03-26 05:13:43 +09:00
|
|
|
|
struct netdev_rxq_bsd *rxq = netdev_rxq_bsd_cast(rxq_);
|
2013-05-10 14:39:19 -07:00
|
|
|
|
|
2014-03-26 05:13:43 +09:00
|
|
|
|
if (rxq->pcap_handle) {
|
|
|
|
|
pcap_close(rxq->pcap_handle);
|
2013-05-10 14:39:19 -07:00
|
|
|
|
}
|
2013-08-09 21:21:38 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2014-03-26 05:13:43 +09:00
|
|
|
|
netdev_bsd_rxq_dealloc(struct netdev_rxq *rxq_)
|
2013-08-09 21:21:38 -07:00
|
|
|
|
{
|
2014-03-26 05:13:43 +09:00
|
|
|
|
struct netdev_rxq_bsd *rxq = netdev_rxq_bsd_cast(rxq_);
|
2013-08-09 21:21:38 -07:00
|
|
|
|
|
2014-03-26 05:13:43 +09:00
|
|
|
|
free(rxq);
|
2013-05-10 14:39:19 -07:00
|
|
|
|
}
|
2012-07-25 22:51:05 +02:00
|
|
|
|
|
|
|
|
|
/* The recv callback of the netdev class returns the number of bytes of the
|
|
|
|
|
* received packet.
|
|
|
|
|
*
|
|
|
|
|
* This can be done by the pcap_next() function. Unfortunately pcap_next() does
|
|
|
|
|
* not make difference between a missing packet on the capture interface and
|
|
|
|
|
* an error during the file capture. We can use the pcap_dispatch() function
|
|
|
|
|
* instead, which is able to distinguish between errors and null packet.
|
|
|
|
|
*
|
|
|
|
|
* To make pcap_dispatch() returns the number of bytes read from the interface
|
|
|
|
|
* we need to define the following callback and argument.
|
|
|
|
|
*/
|
|
|
|
|
struct pcap_arg {
|
|
|
|
|
void *data;
|
|
|
|
|
int size;
|
|
|
|
|
int retval;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* This callback will be executed on every captured packet.
|
|
|
|
|
*
|
|
|
|
|
* If the packet captured by pcap_dispatch() does not fit the pcap buffer,
|
|
|
|
|
* pcap returns a truncated packet and we follow this behavior.
|
|
|
|
|
*
|
|
|
|
|
* The argument args->retval is the packet size in bytes.
|
|
|
|
|
*/
|
|
|
|
|
static void
|
|
|
|
|
proc_pkt(u_char *args_, const struct pcap_pkthdr *hdr, const u_char *packet)
|
|
|
|
|
{
|
2014-04-30 13:33:54 +09:00
|
|
|
|
struct pcap_arg *args = ALIGNED_CAST(struct pcap_arg *, args_);
|
2012-07-25 22:51:05 +02:00
|
|
|
|
|
|
|
|
|
if (args->size < hdr->len) {
|
|
|
|
|
VLOG_WARN_RL(&rl, "packet truncated");
|
|
|
|
|
args->retval = args->size;
|
|
|
|
|
} else {
|
|
|
|
|
args->retval = hdr->len;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* copy the packet to our buffer */
|
|
|
|
|
memcpy(args->data, packet, args->retval);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* This function attempts to receive a packet from the specified network
|
|
|
|
|
* device. It is assumed that the network device is a system device or a tap
|
|
|
|
|
* device opened as a system one. In this case the read operation is performed
|
2014-03-26 05:13:43 +09:00
|
|
|
|
* from rxq->pcap.
|
2012-07-25 22:51:05 +02:00
|
|
|
|
*/
|
|
|
|
|
static int
|
2015-02-22 03:21:09 -08:00
|
|
|
|
netdev_rxq_bsd_recv_pcap(struct netdev_rxq_bsd *rxq, struct dp_packet *buffer)
|
2012-07-25 22:51:05 +02:00
|
|
|
|
{
|
|
|
|
|
struct pcap_arg arg;
|
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
|
|
/* prepare the pcap argument to store the packet */
|
2015-02-22 03:21:09 -08:00
|
|
|
|
arg.size = dp_packet_tailroom(buffer);
|
|
|
|
|
arg.data = dp_packet_data(buffer);
|
2012-07-25 22:51:05 +02:00
|
|
|
|
|
|
|
|
|
for (;;) {
|
2014-03-26 05:13:43 +09:00
|
|
|
|
ret = pcap_dispatch(rxq->pcap_handle, 1, proc_pkt, (u_char *) &arg);
|
2012-07-25 22:51:05 +02:00
|
|
|
|
|
|
|
|
|
if (ret > 0) {
|
2015-02-22 03:21:09 -08:00
|
|
|
|
dp_packet_set_size(buffer, dp_packet_size(buffer) + arg.retval);
|
2014-01-15 17:17:00 +09:00
|
|
|
|
return 0;
|
2012-07-25 22:51:05 +02:00
|
|
|
|
}
|
|
|
|
|
if (ret == -1) {
|
|
|
|
|
if (errno == EINTR) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-01-15 17:17:00 +09:00
|
|
|
|
return EAGAIN;
|
2012-07-25 22:51:05 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* This function attempts to receive a packet from the specified network
|
2013-05-10 14:39:19 -07:00
|
|
|
|
* device. It is assumed that the network device is a tap device and
|
2014-03-26 05:13:43 +09:00
|
|
|
|
* 'rxq->fd' is initialized with the tap file descriptor.
|
2012-07-25 22:51:05 +02:00
|
|
|
|
*/
|
|
|
|
|
static int
|
2015-02-22 03:21:09 -08:00
|
|
|
|
netdev_rxq_bsd_recv_tap(struct netdev_rxq_bsd *rxq, struct dp_packet *buffer)
|
2012-07-25 22:51:05 +02:00
|
|
|
|
{
|
2015-02-22 03:21:09 -08:00
|
|
|
|
size_t size = dp_packet_tailroom(buffer);
|
2014-01-15 17:17:00 +09:00
|
|
|
|
|
2012-07-25 22:51:05 +02:00
|
|
|
|
for (;;) {
|
2015-02-22 03:21:09 -08:00
|
|
|
|
ssize_t retval = read(rxq->fd, dp_packet_data(buffer), size);
|
2012-07-25 22:51:05 +02:00
|
|
|
|
if (retval >= 0) {
|
2015-02-22 03:21:09 -08:00
|
|
|
|
dp_packet_set_size(buffer, dp_packet_size(buffer) + retval);
|
2014-01-15 17:17:00 +09:00
|
|
|
|
return 0;
|
2012-07-25 22:51:05 +02:00
|
|
|
|
} else if (errno != EINTR) {
|
|
|
|
|
if (errno != EAGAIN) {
|
|
|
|
|
VLOG_WARN_RL(&rl, "error receiving Ethernet packet on %s: %s",
|
2014-03-26 05:13:43 +09:00
|
|
|
|
ovs_strerror(errno), netdev_rxq_get_name(&rxq->up));
|
2012-07-25 22:51:05 +02:00
|
|
|
|
}
|
2014-01-15 17:17:00 +09:00
|
|
|
|
return errno;
|
2012-07-25 22:51:05 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
2018-04-19 19:40:44 +02:00
|
|
|
|
netdev_bsd_rxq_recv(struct netdev_rxq *rxq_, struct dp_packet_batch *batch,
|
|
|
|
|
int *qfill)
|
2012-07-25 22:51:05 +02:00
|
|
|
|
{
|
2014-03-26 05:13:43 +09:00
|
|
|
|
struct netdev_rxq_bsd *rxq = netdev_rxq_bsd_cast(rxq_);
|
|
|
|
|
struct netdev *netdev = rxq->up.netdev;
|
2015-02-25 12:01:53 -08:00
|
|
|
|
struct dp_packet *packet;
|
2014-03-20 10:54:37 -07:00
|
|
|
|
ssize_t retval;
|
|
|
|
|
int mtu;
|
|
|
|
|
|
2014-03-26 05:13:43 +09:00
|
|
|
|
if (netdev_bsd_get_mtu(netdev, &mtu)) {
|
2014-03-20 10:54:37 -07:00
|
|
|
|
mtu = ETH_PAYLOAD_MAX;
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-25 16:29:59 +00:00
|
|
|
|
/* Assume Ethernet port. No need to set packet_type. */
|
2015-02-25 12:01:53 -08:00
|
|
|
|
packet = dp_packet_new_with_headroom(VLAN_ETH_HEADER_LEN + mtu,
|
2014-06-23 11:43:57 -07:00
|
|
|
|
DP_NETDEV_HEADROOM);
|
2014-03-26 05:13:43 +09:00
|
|
|
|
retval = (rxq->pcap_handle
|
2015-02-22 03:21:09 -08:00
|
|
|
|
? netdev_rxq_bsd_recv_pcap(rxq, packet)
|
|
|
|
|
: netdev_rxq_bsd_recv_tap(rxq, packet));
|
2014-03-20 10:54:37 -07:00
|
|
|
|
|
|
|
|
|
if (retval) {
|
2015-02-25 12:01:53 -08:00
|
|
|
|
dp_packet_delete(packet);
|
2014-03-20 10:54:37 -07:00
|
|
|
|
} else {
|
2018-12-10 20:17:53 +03:00
|
|
|
|
dp_packet_batch_init_packet(batch, packet);
|
2014-03-20 10:54:37 -07:00
|
|
|
|
}
|
2018-04-19 19:40:44 +02:00
|
|
|
|
|
|
|
|
|
if (qfill) {
|
|
|
|
|
*qfill = -ENOTSUP;
|
|
|
|
|
}
|
|
|
|
|
|
2014-03-20 10:54:37 -07:00
|
|
|
|
return retval;
|
2012-07-25 22:51:05 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Registers with the poll loop to wake up from the next call to poll_block()
|
2014-03-26 05:13:43 +09:00
|
|
|
|
* when a packet is ready to be received with netdev_rxq_recv() on 'rxq'.
|
2012-07-25 22:51:05 +02:00
|
|
|
|
*/
|
|
|
|
|
static void
|
2014-03-26 05:13:43 +09:00
|
|
|
|
netdev_bsd_rxq_wait(struct netdev_rxq *rxq_)
|
2012-07-25 22:51:05 +02:00
|
|
|
|
{
|
2014-03-26 05:13:43 +09:00
|
|
|
|
struct netdev_rxq_bsd *rxq = netdev_rxq_bsd_cast(rxq_);
|
2012-07-25 22:51:05 +02:00
|
|
|
|
|
2014-03-26 05:13:43 +09:00
|
|
|
|
poll_fd_wait(rxq->fd, POLLIN);
|
2012-07-25 22:51:05 +02:00
|
|
|
|
}
|
|
|
|
|
|
2014-03-26 05:13:43 +09:00
|
|
|
|
/* Discards all packets waiting to be received from 'rxq'. */
|
2012-07-25 22:51:05 +02:00
|
|
|
|
static int
|
2014-03-26 05:13:43 +09:00
|
|
|
|
netdev_bsd_rxq_drain(struct netdev_rxq *rxq_)
|
2012-07-25 22:51:05 +02:00
|
|
|
|
{
|
|
|
|
|
struct ifreq ifr;
|
2014-03-26 05:13:43 +09:00
|
|
|
|
struct netdev_rxq_bsd *rxq = netdev_rxq_bsd_cast(rxq_);
|
2012-07-25 22:51:05 +02:00
|
|
|
|
|
2024-11-29 23:48:15 +01:00
|
|
|
|
memset(&ifr, 0, sizeof ifr);
|
2014-03-26 05:13:43 +09:00
|
|
|
|
strcpy(ifr.ifr_name, netdev_get_kernel_name(netdev_rxq_get_netdev(rxq_)));
|
|
|
|
|
if (ioctl(rxq->fd, BIOCFLUSH, &ifr) == -1) {
|
2012-07-25 22:51:05 +02:00
|
|
|
|
VLOG_DBG_RL(&rl, "%s: ioctl(BIOCFLUSH) failed: %s",
|
2014-03-26 05:13:43 +09:00
|
|
|
|
netdev_rxq_get_name(rxq_), ovs_strerror(errno));
|
2012-07-25 22:51:05 +02:00
|
|
|
|
return errno;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Send a packet on the specified network device. The device could be either a
|
|
|
|
|
* system or a tap device.
|
|
|
|
|
*/
|
|
|
|
|
static int
|
2014-09-03 14:37:35 -07:00
|
|
|
|
netdev_bsd_send(struct netdev *netdev_, int qid OVS_UNUSED,
|
2017-12-14 14:59:25 +03:00
|
|
|
|
struct dp_packet_batch *batch,
|
2016-07-27 17:44:41 +03:00
|
|
|
|
bool concurrent_txq OVS_UNUSED)
|
2012-07-25 22:51:05 +02:00
|
|
|
|
{
|
2013-03-15 15:54:36 -07:00
|
|
|
|
struct netdev_bsd *dev = netdev_bsd_cast(netdev_);
|
2013-05-10 14:39:19 -07:00
|
|
|
|
const char *name = netdev_get_name(netdev_);
|
2018-12-10 20:17:53 +03:00
|
|
|
|
struct dp_packet *packet;
|
2013-08-09 21:34:02 -07:00
|
|
|
|
int error;
|
2012-07-25 22:51:05 +02:00
|
|
|
|
|
2013-08-09 21:34:02 -07:00
|
|
|
|
ovs_mutex_lock(&dev->mutex);
|
2013-05-10 14:39:19 -07:00
|
|
|
|
if (dev->tap_fd < 0 && !dev->pcap) {
|
2013-08-09 21:34:02 -07:00
|
|
|
|
error = netdev_bsd_open_pcap(name, &dev->pcap, &dev->fd);
|
|
|
|
|
} else {
|
|
|
|
|
error = 0;
|
2012-07-25 22:51:05 +02:00
|
|
|
|
}
|
|
|
|
|
|
2018-12-10 20:17:53 +03:00
|
|
|
|
DP_PACKET_BATCH_FOR_EACH (i, packet, batch) {
|
|
|
|
|
const void *data = dp_packet_data(packet);
|
|
|
|
|
size_t size = dp_packet_size(packet);
|
ofp-actions: Add truncate action.
The patch adds a new action to support packet truncation. The new action
is formatted as 'output(port=n,max_len=m)', as output to port n, with
packet size being MIN(original_size, m).
One use case is to enable port mirroring to send smaller packets to the
destination port so that only useful packet information is mirrored/copied,
saving some performance overhead of copying entire packet payload. Example
use case is below as well as shown in the testcases:
- Output to port 1 with max_len 100 bytes.
- The output packet size on port 1 will be MIN(original_packet_size, 100).
# ovs-ofctl add-flow br0 'actions=output(port=1,max_len=100)'
- The scope of max_len is limited to output action itself. The following
packet size of output:1 and output:2 will be intact.
# ovs-ofctl add-flow br0 \
'actions=output(port=1,max_len=100),output:1,output:2'
- The Datapath actions shows:
# Datapath actions: trunc(100),1,1,2
Tested-at: https://travis-ci.org/williamtu/ovs-travis/builds/140037134
Signed-off-by: William Tu <u9012063@gmail.com>
Acked-by: Pravin B Shelar <pshelar@ovn.org>
2016-06-24 07:42:30 -07:00
|
|
|
|
|
2014-06-23 11:43:58 -07:00
|
|
|
|
while (!error) {
|
|
|
|
|
ssize_t retval;
|
|
|
|
|
if (dev->tap_fd >= 0) {
|
|
|
|
|
retval = write(dev->tap_fd, data, size);
|
2013-08-09 21:34:02 -07:00
|
|
|
|
} else {
|
2014-06-23 11:43:58 -07:00
|
|
|
|
retval = pcap_inject(dev->pcap, data, size);
|
|
|
|
|
}
|
|
|
|
|
if (retval < 0) {
|
|
|
|
|
if (errno == EINTR) {
|
|
|
|
|
continue;
|
|
|
|
|
} else {
|
|
|
|
|
error = errno;
|
|
|
|
|
if (error != EAGAIN) {
|
|
|
|
|
VLOG_WARN_RL(&rl, "error sending Ethernet packet on"
|
|
|
|
|
" %s: %s", name, ovs_strerror(error));
|
|
|
|
|
}
|
2013-08-09 21:34:02 -07:00
|
|
|
|
}
|
2014-06-23 11:43:58 -07:00
|
|
|
|
} else if (retval != size) {
|
|
|
|
|
VLOG_WARN_RL(&rl, "sent partial Ethernet packet "
|
|
|
|
|
"(%"PRIuSIZE" bytes of "
|
|
|
|
|
"%"PRIuSIZE") on %s", retval, size, name);
|
|
|
|
|
error = EMSGSIZE;
|
|
|
|
|
} else {
|
|
|
|
|
break;
|
2012-07-25 22:51:05 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2013-08-09 21:34:02 -07:00
|
|
|
|
|
|
|
|
|
ovs_mutex_unlock(&dev->mutex);
|
2017-12-14 14:59:25 +03:00
|
|
|
|
dp_packet_delete_batch(batch, true);
|
2014-03-20 10:56:51 -07:00
|
|
|
|
|
2013-08-09 21:34:02 -07:00
|
|
|
|
return error;
|
2012-07-25 22:51:05 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Registers with the poll loop to wake up from the next call to poll_block()
|
|
|
|
|
* when the packet transmission queue has sufficient room to transmit a packet
|
|
|
|
|
* with netdev_send().
|
|
|
|
|
*/
|
|
|
|
|
static void
|
2014-09-03 14:37:35 -07:00
|
|
|
|
netdev_bsd_send_wait(struct netdev *netdev_, int qid OVS_UNUSED)
|
2012-07-25 22:51:05 +02:00
|
|
|
|
{
|
2013-03-15 15:54:36 -07:00
|
|
|
|
struct netdev_bsd *dev = netdev_bsd_cast(netdev_);
|
2012-07-25 22:51:05 +02:00
|
|
|
|
|
2013-08-09 21:34:02 -07:00
|
|
|
|
ovs_mutex_lock(&dev->mutex);
|
2013-05-10 14:39:19 -07:00
|
|
|
|
if (dev->tap_fd >= 0) {
|
2012-07-25 22:51:05 +02:00
|
|
|
|
/* TAP device always accepts packets. */
|
|
|
|
|
poll_immediate_wake();
|
2013-05-10 14:39:19 -07:00
|
|
|
|
} else if (dev->pcap) {
|
|
|
|
|
poll_fd_wait(dev->fd, POLLOUT);
|
|
|
|
|
} else {
|
|
|
|
|
/* We haven't even tried to send a packet yet. */
|
|
|
|
|
poll_immediate_wake();
|
2012-07-25 22:51:05 +02:00
|
|
|
|
}
|
2013-08-09 21:34:02 -07:00
|
|
|
|
ovs_mutex_unlock(&dev->mutex);
|
2012-07-25 22:51:05 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Attempts to set 'netdev''s MAC address to 'mac'. Returns 0 if successful,
|
|
|
|
|
* otherwise a positive errno value.
|
|
|
|
|
*/
|
|
|
|
|
static int
|
|
|
|
|
netdev_bsd_set_etheraddr(struct netdev *netdev_,
|
2015-08-28 14:55:11 -07:00
|
|
|
|
const struct eth_addr mac)
|
2012-07-25 22:51:05 +02:00
|
|
|
|
{
|
2013-03-15 15:54:36 -07:00
|
|
|
|
struct netdev_bsd *netdev = netdev_bsd_cast(netdev_);
|
2013-08-09 21:34:02 -07:00
|
|
|
|
int error = 0;
|
2012-07-25 22:51:05 +02:00
|
|
|
|
|
2013-08-09 21:34:02 -07:00
|
|
|
|
ovs_mutex_lock(&netdev->mutex);
|
2013-03-15 15:54:36 -07:00
|
|
|
|
if (!(netdev->cache_valid & VALID_ETHERADDR)
|
|
|
|
|
|| !eth_addr_equals(netdev->etheraddr, mac)) {
|
2013-05-21 17:49:55 +09:00
|
|
|
|
error = set_etheraddr(netdev_get_kernel_name(netdev_), AF_LINK,
|
|
|
|
|
ETH_ADDR_LEN, mac);
|
2012-07-25 22:51:05 +02:00
|
|
|
|
if (!error) {
|
2013-03-15 15:54:36 -07:00
|
|
|
|
netdev->cache_valid |= VALID_ETHERADDR;
|
2015-08-28 14:55:11 -07:00
|
|
|
|
netdev->etheraddr = mac;
|
2014-04-03 00:17:34 -07:00
|
|
|
|
netdev_change_seq_changed(netdev_);
|
2012-07-25 22:51:05 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
2013-08-09 21:34:02 -07:00
|
|
|
|
ovs_mutex_unlock(&netdev->mutex);
|
|
|
|
|
|
2012-07-25 22:51:05 +02:00
|
|
|
|
return error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Returns a pointer to 'netdev''s MAC address. The caller must not modify or
|
|
|
|
|
* free the returned buffer.
|
|
|
|
|
*/
|
|
|
|
|
static int
|
2015-08-28 14:55:11 -07:00
|
|
|
|
netdev_bsd_get_etheraddr(const struct netdev *netdev_, struct eth_addr *mac)
|
2012-07-25 22:51:05 +02:00
|
|
|
|
{
|
2013-03-15 15:54:36 -07:00
|
|
|
|
struct netdev_bsd *netdev = netdev_bsd_cast(netdev_);
|
2013-08-09 21:34:02 -07:00
|
|
|
|
int error = 0;
|
2012-07-25 22:51:05 +02:00
|
|
|
|
|
2013-08-09 21:34:02 -07:00
|
|
|
|
ovs_mutex_lock(&netdev->mutex);
|
2013-03-15 15:54:36 -07:00
|
|
|
|
if (!(netdev->cache_valid & VALID_ETHERADDR)) {
|
2013-08-09 21:34:02 -07:00
|
|
|
|
error = get_etheraddr(netdev_get_kernel_name(netdev_),
|
2015-08-28 14:55:11 -07:00
|
|
|
|
&netdev->etheraddr);
|
2013-08-09 21:34:02 -07:00
|
|
|
|
if (!error) {
|
|
|
|
|
netdev->cache_valid |= VALID_ETHERADDR;
|
2012-07-25 22:51:05 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
2013-08-09 21:34:02 -07:00
|
|
|
|
if (!error) {
|
2015-08-28 14:55:11 -07:00
|
|
|
|
*mac = netdev->etheraddr;
|
2013-08-09 21:34:02 -07:00
|
|
|
|
}
|
|
|
|
|
ovs_mutex_unlock(&netdev->mutex);
|
2012-07-25 22:51:05 +02:00
|
|
|
|
|
2013-08-09 21:34:02 -07:00
|
|
|
|
return error;
|
2012-07-25 22:51:05 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Returns the maximum size of transmitted (and received) packets on 'netdev',
|
|
|
|
|
* in bytes, not including the hardware header; thus, this is typically 1500
|
|
|
|
|
* bytes for Ethernet devices.
|
|
|
|
|
*/
|
|
|
|
|
static int
|
|
|
|
|
netdev_bsd_get_mtu(const struct netdev *netdev_, int *mtup)
|
|
|
|
|
{
|
2013-03-15 15:54:36 -07:00
|
|
|
|
struct netdev_bsd *netdev = netdev_bsd_cast(netdev_);
|
2013-08-09 21:34:02 -07:00
|
|
|
|
int error = 0;
|
2012-07-25 22:51:05 +02:00
|
|
|
|
|
2013-08-09 21:34:02 -07:00
|
|
|
|
ovs_mutex_lock(&netdev->mutex);
|
2013-03-15 15:54:36 -07:00
|
|
|
|
if (!(netdev->cache_valid & VALID_MTU)) {
|
2012-07-25 22:51:05 +02:00
|
|
|
|
struct ifreq ifr;
|
|
|
|
|
|
2024-11-29 23:48:15 +01:00
|
|
|
|
memset(&ifr, 0, sizeof ifr);
|
2013-08-09 21:14:23 -07:00
|
|
|
|
error = af_inet_ifreq_ioctl(netdev_get_kernel_name(netdev_), &ifr,
|
2013-05-21 17:49:55 +09:00
|
|
|
|
SIOCGIFMTU, "SIOCGIFMTU");
|
2013-08-09 21:34:02 -07:00
|
|
|
|
if (!error) {
|
|
|
|
|
netdev->mtu = ifr.ifr_mtu;
|
|
|
|
|
netdev->cache_valid |= VALID_MTU;
|
2012-07-25 22:51:05 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
2013-08-09 21:34:02 -07:00
|
|
|
|
if (!error) {
|
|
|
|
|
*mtup = netdev->mtu;
|
|
|
|
|
}
|
|
|
|
|
ovs_mutex_unlock(&netdev->mutex);
|
2012-07-25 22:51:05 +02:00
|
|
|
|
|
2015-04-16 14:19:37 -07:00
|
|
|
|
return error;
|
2012-07-25 22:51:05 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
2013-08-09 21:34:02 -07:00
|
|
|
|
netdev_bsd_get_ifindex(const struct netdev *netdev_)
|
2012-07-25 22:51:05 +02:00
|
|
|
|
{
|
2013-08-09 21:34:02 -07:00
|
|
|
|
struct netdev_bsd *netdev = netdev_bsd_cast(netdev_);
|
2012-07-25 22:51:05 +02:00
|
|
|
|
int ifindex, error;
|
|
|
|
|
|
2013-08-09 21:34:02 -07:00
|
|
|
|
ovs_mutex_lock(&netdev->mutex);
|
|
|
|
|
error = get_ifindex(netdev_, &ifindex);
|
|
|
|
|
ovs_mutex_unlock(&netdev->mutex);
|
|
|
|
|
|
2012-07-25 22:51:05 +02:00
|
|
|
|
return error ? -error : ifindex;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
netdev_bsd_get_carrier(const struct netdev *netdev_, bool *carrier)
|
|
|
|
|
{
|
2013-03-15 15:54:36 -07:00
|
|
|
|
struct netdev_bsd *netdev = netdev_bsd_cast(netdev_);
|
2013-08-09 21:34:02 -07:00
|
|
|
|
int error = 0;
|
2012-07-25 22:51:05 +02:00
|
|
|
|
|
2013-08-09 21:34:02 -07:00
|
|
|
|
ovs_mutex_lock(&netdev->mutex);
|
2013-03-15 15:54:36 -07:00
|
|
|
|
if (!(netdev->cache_valid & VALID_CARRIER)) {
|
2012-07-25 22:51:05 +02:00
|
|
|
|
struct ifmediareq ifmr;
|
|
|
|
|
|
|
|
|
|
memset(&ifmr, 0, sizeof(ifmr));
|
2015-02-20 12:32:08 -08:00
|
|
|
|
ovs_strlcpy(ifmr.ifm_name, netdev_get_kernel_name(netdev_),
|
|
|
|
|
sizeof ifmr.ifm_name);
|
2012-07-25 22:51:05 +02:00
|
|
|
|
|
2013-08-09 21:14:23 -07:00
|
|
|
|
error = af_inet_ioctl(SIOCGIFMEDIA, &ifmr);
|
2013-08-09 21:34:02 -07:00
|
|
|
|
if (!error) {
|
|
|
|
|
netdev->carrier = (ifmr.ifm_status & IFM_ACTIVE) == IFM_ACTIVE;
|
|
|
|
|
netdev->cache_valid |= VALID_CARRIER;
|
|
|
|
|
|
|
|
|
|
/* If the interface doesn't report whether the media is active,
|
|
|
|
|
* just assume it is active. */
|
|
|
|
|
if ((ifmr.ifm_status & IFM_AVALID) == 0) {
|
|
|
|
|
netdev->carrier = true;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
2012-07-25 22:51:05 +02:00
|
|
|
|
VLOG_DBG_RL(&rl, "%s: ioctl(SIOCGIFMEDIA) failed: %s",
|
2013-08-09 21:14:23 -07:00
|
|
|
|
netdev_get_name(netdev_), ovs_strerror(error));
|
2012-07-25 22:51:05 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
2013-08-09 21:34:02 -07:00
|
|
|
|
if (!error) {
|
|
|
|
|
*carrier = netdev->carrier;
|
|
|
|
|
}
|
|
|
|
|
ovs_mutex_unlock(&netdev->mutex);
|
2012-07-25 22:51:05 +02:00
|
|
|
|
|
2013-08-09 21:34:02 -07:00
|
|
|
|
return error;
|
2012-07-25 22:51:05 +02:00
|
|
|
|
}
|
|
|
|
|
|
2013-05-21 17:50:03 +09:00
|
|
|
|
static void
|
2014-02-05 19:08:10 +09:00
|
|
|
|
convert_stats_system(struct netdev_stats *stats, const struct if_data *ifd)
|
2013-05-21 17:50:03 +09:00
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
* note: UINT64_MAX means unsupported
|
|
|
|
|
*/
|
|
|
|
|
stats->rx_packets = ifd->ifi_ipackets;
|
|
|
|
|
stats->tx_packets = ifd->ifi_opackets;
|
|
|
|
|
stats->rx_bytes = ifd->ifi_obytes;
|
|
|
|
|
stats->tx_bytes = ifd->ifi_ibytes;
|
|
|
|
|
stats->rx_errors = ifd->ifi_ierrors;
|
|
|
|
|
stats->tx_errors = ifd->ifi_oerrors;
|
|
|
|
|
stats->rx_dropped = ifd->ifi_iqdrops;
|
|
|
|
|
stats->tx_dropped = UINT64_MAX;
|
|
|
|
|
stats->multicast = ifd->ifi_imcasts;
|
|
|
|
|
stats->collisions = ifd->ifi_collisions;
|
|
|
|
|
stats->rx_length_errors = UINT64_MAX;
|
|
|
|
|
stats->rx_over_errors = UINT64_MAX;
|
|
|
|
|
stats->rx_crc_errors = UINT64_MAX;
|
|
|
|
|
stats->rx_frame_errors = UINT64_MAX;
|
|
|
|
|
stats->rx_fifo_errors = UINT64_MAX;
|
|
|
|
|
stats->rx_missed_errors = UINT64_MAX;
|
|
|
|
|
stats->tx_aborted_errors = UINT64_MAX;
|
|
|
|
|
stats->tx_carrier_errors = UINT64_MAX;
|
|
|
|
|
stats->tx_fifo_errors = UINT64_MAX;
|
|
|
|
|
stats->tx_heartbeat_errors = UINT64_MAX;
|
|
|
|
|
stats->tx_window_errors = UINT64_MAX;
|
|
|
|
|
}
|
|
|
|
|
|
2014-02-05 19:08:10 +09:00
|
|
|
|
static void
|
|
|
|
|
convert_stats_tap(struct netdev_stats *stats, const struct if_data *ifd)
|
|
|
|
|
{
|
|
|
|
|
/*
|
2014-03-26 05:13:43 +09:00
|
|
|
|
* Similar to convert_stats_system but swapping rxq and tx
|
2014-02-05 19:08:10 +09:00
|
|
|
|
* because 'ifd' is stats for the network interface side of the
|
|
|
|
|
* tap device and what the caller wants is one for the character
|
|
|
|
|
* device side.
|
|
|
|
|
*
|
|
|
|
|
* note: UINT64_MAX means unsupported
|
|
|
|
|
*/
|
|
|
|
|
stats->rx_packets = ifd->ifi_opackets;
|
|
|
|
|
stats->tx_packets = ifd->ifi_ipackets;
|
|
|
|
|
stats->rx_bytes = ifd->ifi_ibytes;
|
|
|
|
|
stats->tx_bytes = ifd->ifi_obytes;
|
|
|
|
|
stats->rx_errors = ifd->ifi_oerrors;
|
|
|
|
|
stats->tx_errors = ifd->ifi_ierrors;
|
|
|
|
|
stats->rx_dropped = UINT64_MAX;
|
|
|
|
|
stats->tx_dropped = ifd->ifi_iqdrops;
|
|
|
|
|
stats->multicast = ifd->ifi_omcasts;
|
|
|
|
|
stats->collisions = UINT64_MAX;
|
|
|
|
|
stats->rx_length_errors = UINT64_MAX;
|
|
|
|
|
stats->rx_over_errors = UINT64_MAX;
|
|
|
|
|
stats->rx_crc_errors = UINT64_MAX;
|
|
|
|
|
stats->rx_frame_errors = UINT64_MAX;
|
|
|
|
|
stats->rx_fifo_errors = UINT64_MAX;
|
|
|
|
|
stats->rx_missed_errors = UINT64_MAX;
|
|
|
|
|
stats->tx_aborted_errors = UINT64_MAX;
|
|
|
|
|
stats->tx_carrier_errors = UINT64_MAX;
|
|
|
|
|
stats->tx_fifo_errors = UINT64_MAX;
|
|
|
|
|
stats->tx_heartbeat_errors = UINT64_MAX;
|
|
|
|
|
stats->tx_window_errors = UINT64_MAX;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
convert_stats(const struct netdev *netdev, struct netdev_stats *stats,
|
|
|
|
|
const struct if_data *ifd)
|
|
|
|
|
{
|
|
|
|
|
if (netdev_bsd_cast(netdev)->tap_fd == -1) {
|
|
|
|
|
convert_stats_system(stats, ifd);
|
|
|
|
|
} else {
|
|
|
|
|
convert_stats_tap(stats, ifd);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-07-25 22:51:05 +02:00
|
|
|
|
/* Retrieves current device stats for 'netdev'. */
|
|
|
|
|
static int
|
2013-05-21 17:49:59 +09:00
|
|
|
|
netdev_bsd_get_stats(const struct netdev *netdev_, struct netdev_stats *stats)
|
2012-07-25 22:51:05 +02:00
|
|
|
|
{
|
2013-05-21 17:49:54 +09:00
|
|
|
|
#if defined(__FreeBSD__)
|
2012-07-25 22:51:05 +02:00
|
|
|
|
int if_count, i;
|
|
|
|
|
int mib[6];
|
|
|
|
|
size_t len;
|
|
|
|
|
struct ifmibdata ifmd;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
mib[0] = CTL_NET;
|
|
|
|
|
mib[1] = PF_LINK;
|
|
|
|
|
mib[2] = NETLINK_GENERIC;
|
|
|
|
|
mib[3] = IFMIB_SYSTEM;
|
|
|
|
|
mib[4] = IFMIB_IFCOUNT;
|
|
|
|
|
|
|
|
|
|
len = sizeof(if_count);
|
|
|
|
|
|
|
|
|
|
if (sysctl(mib, 5, &if_count, &len, (void *)0, 0) == -1) {
|
|
|
|
|
VLOG_DBG_RL(&rl, "%s: sysctl failed: %s",
|
2013-06-24 10:54:49 -07:00
|
|
|
|
netdev_get_name(netdev_), ovs_strerror(errno));
|
2012-07-25 22:51:05 +02:00
|
|
|
|
return errno;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mib[5] = IFDATA_GENERAL;
|
|
|
|
|
mib[3] = IFMIB_IFDATA;
|
|
|
|
|
len = sizeof(ifmd);
|
|
|
|
|
for (i = 1; i <= if_count; i++) {
|
2019-01-23 12:09:46 -08:00
|
|
|
|
mib[4] = i; /* row */
|
2012-07-25 22:51:05 +02:00
|
|
|
|
if (sysctl(mib, 6, &ifmd, &len, (void *)0, 0) == -1) {
|
|
|
|
|
VLOG_DBG_RL(&rl, "%s: sysctl failed: %s",
|
2013-06-24 10:54:49 -07:00
|
|
|
|
netdev_get_name(netdev_), ovs_strerror(errno));
|
2012-07-25 22:51:05 +02:00
|
|
|
|
return errno;
|
|
|
|
|
} else if (!strcmp(ifmd.ifmd_name, netdev_get_name(netdev_))) {
|
2014-06-25 07:41:41 +00:00
|
|
|
|
convert_stats(netdev_, stats, &ifmd.ifmd_data);
|
2012-07-25 22:51:05 +02:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
2013-05-21 17:49:59 +09:00
|
|
|
|
#elif defined(__NetBSD__)
|
|
|
|
|
struct ifdatareq ifdr;
|
2013-08-07 23:48:59 -07:00
|
|
|
|
int error;
|
2013-05-21 17:49:59 +09:00
|
|
|
|
|
|
|
|
|
memset(&ifdr, 0, sizeof(ifdr));
|
2015-02-20 12:32:08 -08:00
|
|
|
|
ovs_strlcpy(ifdr.ifdr_name, netdev_get_kernel_name(netdev_),
|
|
|
|
|
sizeof(ifdr.ifdr_name));
|
2013-08-07 23:48:59 -07:00
|
|
|
|
error = af_link_ioctl(SIOCGIFDATA, &ifdr);
|
|
|
|
|
if (!error) {
|
2014-02-05 19:08:10 +09:00
|
|
|
|
convert_stats(netdev_, stats, &ifdr.ifdr_data);
|
2013-05-21 17:49:59 +09:00
|
|
|
|
}
|
2013-08-07 23:48:59 -07:00
|
|
|
|
return error;
|
2013-05-21 17:49:59 +09:00
|
|
|
|
#else
|
|
|
|
|
#error not implemented
|
2013-05-21 17:49:54 +09:00
|
|
|
|
#endif
|
2012-07-25 22:51:05 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static uint32_t
|
|
|
|
|
netdev_bsd_parse_media(int media)
|
|
|
|
|
{
|
|
|
|
|
uint32_t supported = 0;
|
|
|
|
|
bool half_duplex = media & IFM_HDX ? true : false;
|
|
|
|
|
|
|
|
|
|
switch (IFM_SUBTYPE(media)) {
|
|
|
|
|
case IFM_10_2:
|
|
|
|
|
case IFM_10_5:
|
|
|
|
|
case IFM_10_STP:
|
|
|
|
|
case IFM_10_T:
|
|
|
|
|
supported |= half_duplex ? NETDEV_F_10MB_HD : NETDEV_F_10MB_FD;
|
|
|
|
|
supported |= NETDEV_F_COPPER;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case IFM_10_FL:
|
|
|
|
|
supported |= half_duplex ? NETDEV_F_10MB_HD : NETDEV_F_10MB_FD;
|
|
|
|
|
supported |= NETDEV_F_FIBER;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case IFM_100_T2:
|
|
|
|
|
case IFM_100_T4:
|
|
|
|
|
case IFM_100_TX:
|
|
|
|
|
case IFM_100_VG:
|
|
|
|
|
supported |= half_duplex ? NETDEV_F_100MB_HD : NETDEV_F_100MB_FD;
|
|
|
|
|
supported |= NETDEV_F_COPPER;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case IFM_100_FX:
|
|
|
|
|
supported |= half_duplex ? NETDEV_F_100MB_HD : NETDEV_F_100MB_FD;
|
|
|
|
|
supported |= NETDEV_F_FIBER;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case IFM_1000_CX:
|
|
|
|
|
case IFM_1000_T:
|
|
|
|
|
supported |= half_duplex ? NETDEV_F_1GB_HD : NETDEV_F_1GB_FD;
|
|
|
|
|
supported |= NETDEV_F_COPPER;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case IFM_1000_LX:
|
|
|
|
|
case IFM_1000_SX:
|
|
|
|
|
supported |= half_duplex ? NETDEV_F_1GB_HD : NETDEV_F_1GB_FD;
|
|
|
|
|
supported |= NETDEV_F_FIBER;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case IFM_10G_CX4:
|
|
|
|
|
supported |= NETDEV_F_10GB_FD;
|
|
|
|
|
supported |= NETDEV_F_COPPER;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case IFM_10G_LR:
|
|
|
|
|
case IFM_10G_SR:
|
|
|
|
|
supported |= NETDEV_F_10GB_FD;
|
|
|
|
|
supported |= NETDEV_F_FIBER;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (IFM_SUBTYPE(media) == IFM_AUTO) {
|
|
|
|
|
supported |= NETDEV_F_AUTONEG;
|
|
|
|
|
}
|
|
|
|
|
/*
|
|
|
|
|
if (media & IFM_ETH_FMASK) {
|
|
|
|
|
supported |= NETDEV_F_PAUSE;
|
|
|
|
|
}
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
return supported;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Stores the features supported by 'netdev' into each of '*current',
|
|
|
|
|
* '*advertised', '*supported', and '*peer' that are non-null. Each value is a
|
|
|
|
|
* bitmap of "enum ofp_port_features" bits, in host byte order. Returns 0 if
|
|
|
|
|
* successful, otherwise a positive errno value. On failure, all of the
|
|
|
|
|
* passed-in values are set to 0.
|
|
|
|
|
*/
|
|
|
|
|
static int
|
|
|
|
|
netdev_bsd_get_features(const struct netdev *netdev,
|
2013-05-10 16:23:03 -04:00
|
|
|
|
enum netdev_features *current, uint32_t *advertised,
|
|
|
|
|
enum netdev_features *supported, uint32_t *peer)
|
2012-07-25 22:51:05 +02:00
|
|
|
|
{
|
|
|
|
|
struct ifmediareq ifmr;
|
|
|
|
|
int *media_list;
|
|
|
|
|
int i;
|
|
|
|
|
int error;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* XXX Look into SIOCGIFCAP instead of SIOCGIFMEDIA */
|
|
|
|
|
|
|
|
|
|
memset(&ifmr, 0, sizeof(ifmr));
|
2015-02-20 12:32:08 -08:00
|
|
|
|
ovs_strlcpy(ifmr.ifm_name, netdev_get_name(netdev), sizeof ifmr.ifm_name);
|
2012-07-25 22:51:05 +02:00
|
|
|
|
|
|
|
|
|
/* We make two SIOCGIFMEDIA ioctl calls. The first to determine the
|
|
|
|
|
* number of supported modes, and a second with a buffer to retrieve
|
|
|
|
|
* them. */
|
2013-08-09 21:14:23 -07:00
|
|
|
|
error = af_inet_ioctl(SIOCGIFMEDIA, &ifmr);
|
|
|
|
|
if (error) {
|
2012-07-25 22:51:05 +02:00
|
|
|
|
VLOG_DBG_RL(&rl, "%s: ioctl(SIOCGIFMEDIA) failed: %s",
|
2013-08-09 21:14:23 -07:00
|
|
|
|
netdev_get_name(netdev), ovs_strerror(error));
|
|
|
|
|
return error;
|
2012-07-25 22:51:05 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
media_list = xcalloc(ifmr.ifm_count, sizeof(int));
|
|
|
|
|
ifmr.ifm_ulist = media_list;
|
|
|
|
|
|
2013-04-22 22:19:59 +09:00
|
|
|
|
if (IFM_TYPE(ifmr.ifm_current) != IFM_ETHER) {
|
2012-07-25 22:51:05 +02:00
|
|
|
|
VLOG_DBG_RL(&rl, "%s: doesn't appear to be ethernet",
|
|
|
|
|
netdev_get_name(netdev));
|
|
|
|
|
error = EINVAL;
|
|
|
|
|
goto cleanup;
|
|
|
|
|
}
|
|
|
|
|
|
2013-08-09 21:14:23 -07:00
|
|
|
|
error = af_inet_ioctl(SIOCGIFMEDIA, &ifmr);
|
|
|
|
|
if (error) {
|
2012-07-25 22:51:05 +02:00
|
|
|
|
VLOG_DBG_RL(&rl, "%s: ioctl(SIOCGIFMEDIA) failed: %s",
|
2013-08-09 21:14:23 -07:00
|
|
|
|
netdev_get_name(netdev), ovs_strerror(error));
|
2012-07-25 22:51:05 +02:00
|
|
|
|
goto cleanup;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Current settings. */
|
|
|
|
|
*current = netdev_bsd_parse_media(ifmr.ifm_active);
|
|
|
|
|
|
|
|
|
|
/* Advertised features. */
|
|
|
|
|
*advertised = netdev_bsd_parse_media(ifmr.ifm_current);
|
|
|
|
|
|
|
|
|
|
/* Supported features. */
|
|
|
|
|
*supported = 0;
|
|
|
|
|
for (i = 0; i < ifmr.ifm_count; i++) {
|
|
|
|
|
*supported |= netdev_bsd_parse_media(ifmr.ifm_ulist[i]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Peer advertisements. */
|
|
|
|
|
*peer = 0; /* XXX */
|
|
|
|
|
|
|
|
|
|
error = 0;
|
|
|
|
|
cleanup:
|
|
|
|
|
free(media_list);
|
|
|
|
|
return error;
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-17 10:08:11 +02:00
|
|
|
|
static int
|
|
|
|
|
netdev_bsd_get_speed(const struct netdev *netdev, uint32_t *current,
|
|
|
|
|
uint32_t *max)
|
|
|
|
|
{
|
|
|
|
|
enum netdev_features f_current, f_supported, f_advertised, f_peer;
|
|
|
|
|
int error;
|
|
|
|
|
|
|
|
|
|
error = netdev_bsd_get_features(netdev, &f_current, &f_advertised,
|
|
|
|
|
&f_supported, &f_peer);
|
|
|
|
|
if (error) {
|
|
|
|
|
return error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*current = MIN(UINT32_MAX,
|
|
|
|
|
netdev_features_to_bps(f_current, 0) / 1000000ULL);
|
|
|
|
|
*max = MIN(UINT32_MAX,
|
|
|
|
|
netdev_features_to_bps(f_supported, 0) / 1000000ULL);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2012-07-25 22:51:05 +02:00
|
|
|
|
/*
|
|
|
|
|
* Assigns 'addr' as 'netdev''s IPv4 address and 'mask' as its netmask. If
|
|
|
|
|
* 'addr' is INADDR_ANY, 'netdev''s IPv4 address is cleared. Returns a
|
|
|
|
|
* positive errno value.
|
|
|
|
|
*/
|
|
|
|
|
static int
|
|
|
|
|
netdev_bsd_set_in4(struct netdev *netdev_, struct in_addr addr,
|
2013-05-10 16:23:03 -04:00
|
|
|
|
struct in_addr mask)
|
2012-07-25 22:51:05 +02:00
|
|
|
|
{
|
2013-03-15 15:54:36 -07:00
|
|
|
|
struct netdev_bsd *netdev = netdev_bsd_cast(netdev_);
|
2012-07-25 22:51:05 +02:00
|
|
|
|
int error;
|
|
|
|
|
|
2013-08-09 21:34:02 -07:00
|
|
|
|
ovs_mutex_lock(&netdev->mutex);
|
2012-07-25 22:51:05 +02:00
|
|
|
|
error = do_set_addr(netdev_, SIOCSIFADDR, "SIOCSIFADDR", addr);
|
|
|
|
|
if (!error) {
|
|
|
|
|
if (addr.s_addr != INADDR_ANY) {
|
|
|
|
|
error = do_set_addr(netdev_, SIOCSIFNETMASK,
|
|
|
|
|
"SIOCSIFNETMASK", mask);
|
|
|
|
|
}
|
2014-04-03 00:17:34 -07:00
|
|
|
|
netdev_change_seq_changed(netdev_);
|
2012-07-25 22:51:05 +02:00
|
|
|
|
}
|
2013-08-09 21:34:02 -07:00
|
|
|
|
ovs_mutex_unlock(&netdev->mutex);
|
|
|
|
|
|
2012-07-25 22:51:05 +02:00
|
|
|
|
return error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
2016-03-24 09:30:57 -07:00
|
|
|
|
netdev_bsd_get_addr_list(const struct netdev *netdev_,
|
|
|
|
|
struct in6_addr **addr, struct in6_addr **mask, int *n_cnt)
|
2012-07-25 22:51:05 +02:00
|
|
|
|
{
|
2013-03-15 15:54:36 -07:00
|
|
|
|
struct netdev_bsd *netdev = netdev_bsd_cast(netdev_);
|
2016-03-24 09:30:57 -07:00
|
|
|
|
int error;
|
2012-07-25 22:51:05 +02:00
|
|
|
|
|
2016-03-24 09:30:57 -07:00
|
|
|
|
if (!(netdev->cache_valid & VALID_IN)) {
|
2016-03-24 09:30:57 -07:00
|
|
|
|
netdev_get_addrs_list_flush();
|
2012-07-25 22:51:05 +02:00
|
|
|
|
}
|
2016-03-24 09:30:57 -07:00
|
|
|
|
error = netdev_get_addrs(netdev_get_name(netdev_), addr, mask, n_cnt);
|
|
|
|
|
if (!error) {
|
2016-03-24 09:30:57 -07:00
|
|
|
|
netdev->cache_valid |= VALID_IN;
|
2016-03-24 09:30:57 -07:00
|
|
|
|
}
|
|
|
|
|
return error;
|
2012-07-25 22:51:05 +02:00
|
|
|
|
}
|
|
|
|
|
|
2013-05-21 17:50:01 +09:00
|
|
|
|
#if defined(__NetBSD__)
|
2013-07-25 16:27:39 -07:00
|
|
|
|
static char *
|
|
|
|
|
netdev_bsd_kernel_name_to_ovs_name(const char *kernel_name)
|
2013-05-21 17:50:01 +09:00
|
|
|
|
{
|
2013-07-25 16:27:39 -07:00
|
|
|
|
char *ovs_name = NULL;
|
2013-05-21 17:50:01 +09:00
|
|
|
|
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) {
|
2013-08-02 12:19:49 -07:00
|
|
|
|
struct netdev *netdev = node->data;
|
|
|
|
|
struct netdev_bsd * const dev = netdev_bsd_cast(netdev);
|
2013-05-21 17:50:01 +09:00
|
|
|
|
|
|
|
|
|
if (!strcmp(dev->kernel_name, kernel_name)) {
|
2013-07-25 16:27:39 -07:00
|
|
|
|
free(ovs_name);
|
|
|
|
|
ovs_name = xstrdup(netdev_get_name(&dev->up));
|
2013-05-21 17:50:01 +09:00
|
|
|
|
}
|
2013-07-25 16:27:39 -07:00
|
|
|
|
netdev_close(netdev);
|
2013-05-21 17:50:01 +09:00
|
|
|
|
}
|
|
|
|
|
shash_destroy(&device_shash);
|
|
|
|
|
|
2013-07-25 16:27:39 -07:00
|
|
|
|
return ovs_name ? ovs_name : xstrdup(kernel_name);
|
2013-05-21 17:50:01 +09:00
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
static int
|
2013-05-24 08:33:51 -04:00
|
|
|
|
netdev_bsd_get_next_hop(const struct in_addr *host OVS_UNUSED,
|
|
|
|
|
struct in_addr *next_hop OVS_UNUSED,
|
|
|
|
|
char **netdev_name OVS_UNUSED)
|
2013-05-21 17:50:01 +09:00
|
|
|
|
{
|
|
|
|
|
#if defined(__NetBSD__)
|
|
|
|
|
static int seq = 0;
|
|
|
|
|
struct sockaddr_in sin;
|
|
|
|
|
struct sockaddr_dl sdl;
|
|
|
|
|
int s;
|
|
|
|
|
int i;
|
|
|
|
|
struct {
|
|
|
|
|
struct rt_msghdr h;
|
|
|
|
|
char space[512];
|
|
|
|
|
} buf;
|
|
|
|
|
struct rt_msghdr *rtm = &buf.h;
|
|
|
|
|
const pid_t pid = getpid();
|
|
|
|
|
char *cp;
|
|
|
|
|
ssize_t ssz;
|
|
|
|
|
bool gateway = false;
|
|
|
|
|
char *ifname = NULL;
|
|
|
|
|
int saved_errno;
|
|
|
|
|
|
|
|
|
|
memset(next_hop, 0, sizeof(*next_hop));
|
|
|
|
|
*netdev_name = NULL;
|
|
|
|
|
|
|
|
|
|
memset(&sin, 0, sizeof(sin));
|
|
|
|
|
sin.sin_len = sizeof(sin);
|
|
|
|
|
sin.sin_family = AF_INET;
|
|
|
|
|
sin.sin_port = 0;
|
|
|
|
|
sin.sin_addr = *host;
|
|
|
|
|
|
|
|
|
|
memset(&sdl, 0, sizeof(sdl));
|
|
|
|
|
sdl.sdl_len = sizeof(sdl);
|
|
|
|
|
sdl.sdl_family = AF_LINK;
|
|
|
|
|
|
|
|
|
|
s = socket(PF_ROUTE, SOCK_RAW, 0);
|
|
|
|
|
memset(&buf, 0, sizeof(buf));
|
|
|
|
|
rtm->rtm_flags = RTF_HOST|RTF_UP;
|
|
|
|
|
rtm->rtm_version = RTM_VERSION;
|
|
|
|
|
rtm->rtm_addrs = RTA_DST|RTA_IFP;
|
|
|
|
|
cp = (void *)&buf.space;
|
|
|
|
|
memcpy(cp, &sin, sizeof(sin));
|
|
|
|
|
RT_ADVANCE(cp, (struct sockaddr *)(void *)&sin);
|
|
|
|
|
memcpy(cp, &sdl, sizeof(sdl));
|
|
|
|
|
RT_ADVANCE(cp, (struct sockaddr *)(void *)&sdl);
|
|
|
|
|
rtm->rtm_msglen = cp - (char *)(void *)rtm;
|
|
|
|
|
rtm->rtm_seq = ++seq;
|
|
|
|
|
rtm->rtm_type = RTM_GET;
|
|
|
|
|
rtm->rtm_pid = pid;
|
|
|
|
|
write(s, rtm, rtm->rtm_msglen);
|
|
|
|
|
memset(&buf, 0, sizeof(buf));
|
|
|
|
|
do {
|
|
|
|
|
ssz = read(s, &buf, sizeof(buf));
|
|
|
|
|
} while (ssz > 0 && (rtm->rtm_seq != seq || rtm->rtm_pid != pid));
|
|
|
|
|
saved_errno = errno;
|
|
|
|
|
close(s);
|
|
|
|
|
if (ssz <= 0) {
|
|
|
|
|
if (ssz < 0) {
|
|
|
|
|
return saved_errno;
|
|
|
|
|
}
|
|
|
|
|
return EPIPE; /* XXX */
|
|
|
|
|
}
|
|
|
|
|
cp = (void *)&buf.space;
|
|
|
|
|
for (i = 1; i; i <<= 1) {
|
|
|
|
|
if ((rtm->rtm_addrs & i) != 0) {
|
|
|
|
|
const struct sockaddr *sa = (const void *)cp;
|
|
|
|
|
|
|
|
|
|
if ((i == RTA_GATEWAY) && sa->sa_family == AF_INET) {
|
|
|
|
|
const struct sockaddr_in * const sin =
|
2014-04-30 13:33:54 +09:00
|
|
|
|
ALIGNED_CAST(const struct sockaddr_in *, sa);
|
2013-05-21 17:50:01 +09:00
|
|
|
|
|
|
|
|
|
*next_hop = sin->sin_addr;
|
|
|
|
|
gateway = true;
|
|
|
|
|
}
|
|
|
|
|
if ((i == RTA_IFP) && sa->sa_family == AF_LINK) {
|
|
|
|
|
const struct sockaddr_dl * const sdl =
|
2014-04-30 13:33:54 +09:00
|
|
|
|
ALIGNED_CAST(const struct sockaddr_dl *, sa);
|
2013-07-25 15:38:29 -07:00
|
|
|
|
char *kernel_name;
|
2013-05-21 17:50:01 +09:00
|
|
|
|
|
2013-07-25 15:38:29 -07:00
|
|
|
|
kernel_name = xmemdup0(sdl->sdl_data, sdl->sdl_nlen);
|
2013-07-25 16:27:39 -07:00
|
|
|
|
ifname = netdev_bsd_kernel_name_to_ovs_name(kernel_name);
|
2013-05-21 17:50:01 +09:00
|
|
|
|
free(kernel_name);
|
|
|
|
|
}
|
|
|
|
|
RT_ADVANCE(cp, sa);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (ifname == NULL) {
|
|
|
|
|
return ENXIO;
|
|
|
|
|
}
|
|
|
|
|
if (!gateway) {
|
|
|
|
|
*next_hop = *host;
|
|
|
|
|
}
|
|
|
|
|
*netdev_name = ifname;
|
|
|
|
|
VLOG_DBG("host " IP_FMT " next-hop " IP_FMT " if %s",
|
|
|
|
|
IP_ARGS(host->s_addr), IP_ARGS(next_hop->s_addr), *netdev_name);
|
|
|
|
|
return 0;
|
|
|
|
|
#else
|
|
|
|
|
return EOPNOTSUPP;
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
2013-08-14 05:57:41 +09:00
|
|
|
|
static int
|
|
|
|
|
netdev_bsd_arp_lookup(const struct netdev *netdev OVS_UNUSED,
|
|
|
|
|
ovs_be32 ip OVS_UNUSED,
|
2015-08-28 14:55:11 -07:00
|
|
|
|
struct eth_addr *mac OVS_UNUSED)
|
2013-08-14 05:57:41 +09:00
|
|
|
|
{
|
|
|
|
|
#if defined(__NetBSD__)
|
|
|
|
|
const struct rt_msghdr *rtm;
|
|
|
|
|
size_t needed;
|
|
|
|
|
char *buf;
|
|
|
|
|
const char *cp;
|
|
|
|
|
const char *ep;
|
|
|
|
|
int mib[6];
|
|
|
|
|
int error;
|
|
|
|
|
|
|
|
|
|
buf = NULL;
|
|
|
|
|
mib[0] = CTL_NET;
|
|
|
|
|
mib[1] = PF_ROUTE;
|
|
|
|
|
mib[2] = 0;
|
|
|
|
|
mib[3] = AF_INET;
|
|
|
|
|
mib[4] = NET_RT_FLAGS;
|
2016-12-06 00:04:00 -05:00
|
|
|
|
#ifdef RTF_LLINFO
|
2013-08-14 05:57:41 +09:00
|
|
|
|
mib[5] = RTF_LLINFO;
|
2016-12-06 00:04:00 -05:00
|
|
|
|
#else
|
|
|
|
|
mib[5] = 0;
|
|
|
|
|
#endif
|
2013-08-14 05:57:41 +09:00
|
|
|
|
if (sysctl(mib, 6, NULL, &needed, NULL, 0) == -1) {
|
|
|
|
|
error = errno;
|
|
|
|
|
goto error;
|
|
|
|
|
}
|
|
|
|
|
buf = xmalloc(needed);
|
|
|
|
|
if (sysctl(mib, 6, buf, &needed, NULL, 0) == -1) {
|
|
|
|
|
error = errno;
|
|
|
|
|
goto error;
|
|
|
|
|
}
|
|
|
|
|
ep = buf + needed;
|
|
|
|
|
for (cp = buf; cp < ep; cp += rtm->rtm_msglen) {
|
|
|
|
|
const struct sockaddr_inarp *sina;
|
|
|
|
|
const struct sockaddr_dl *sdl;
|
|
|
|
|
|
|
|
|
|
rtm = (const void *)cp;
|
|
|
|
|
sina = (const void *)(rtm + 1);
|
|
|
|
|
if (ip != sina->sin_addr.s_addr) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
sdl = (const void *)
|
|
|
|
|
((const char *)(const void *)sina + RT_ROUNDUP(sina->sin_len));
|
|
|
|
|
if (sdl->sdl_alen == ETH_ADDR_LEN) {
|
|
|
|
|
memcpy(mac, &sdl->sdl_data[sdl->sdl_nlen], ETH_ADDR_LEN);
|
|
|
|
|
error = 0;
|
|
|
|
|
goto error;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
error = ENXIO;
|
|
|
|
|
error:
|
|
|
|
|
free(buf);
|
|
|
|
|
return error;
|
|
|
|
|
#else
|
|
|
|
|
return EOPNOTSUPP;
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
2012-07-25 22:51:05 +02:00
|
|
|
|
static void
|
|
|
|
|
make_in4_sockaddr(struct sockaddr *sa, struct in_addr addr)
|
|
|
|
|
{
|
|
|
|
|
struct sockaddr_in sin;
|
|
|
|
|
memset(&sin, 0, sizeof sin);
|
|
|
|
|
sin.sin_family = AF_INET;
|
|
|
|
|
sin.sin_addr = addr;
|
|
|
|
|
sin.sin_port = 0;
|
|
|
|
|
|
|
|
|
|
memset(sa, 0, sizeof *sa);
|
|
|
|
|
memcpy(sa, &sin, sizeof sin);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
do_set_addr(struct netdev *netdev,
|
2013-08-13 07:20:22 +09:00
|
|
|
|
unsigned long ioctl_nr, const char *ioctl_name,
|
|
|
|
|
struct in_addr addr)
|
2012-07-25 22:51:05 +02:00
|
|
|
|
{
|
|
|
|
|
struct ifreq ifr;
|
2024-11-29 23:48:15 +01:00
|
|
|
|
|
|
|
|
|
memset(&ifr, 0, sizeof ifr);
|
2012-07-25 22:51:05 +02:00
|
|
|
|
make_in4_sockaddr(&ifr.ifr_addr, addr);
|
2013-08-09 21:14:23 -07:00
|
|
|
|
return af_inet_ifreq_ioctl(netdev_get_kernel_name(netdev), &ifr, ioctl_nr,
|
2013-05-21 17:49:55 +09:00
|
|
|
|
ioctl_name);
|
2012-07-25 22:51:05 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
nd_to_iff_flags(enum netdev_flags nd)
|
|
|
|
|
{
|
|
|
|
|
int iff = 0;
|
|
|
|
|
if (nd & NETDEV_UP) {
|
|
|
|
|
iff |= IFF_UP;
|
|
|
|
|
}
|
|
|
|
|
if (nd & NETDEV_PROMISC) {
|
|
|
|
|
iff |= IFF_PROMISC;
|
2013-05-21 17:49:54 +09:00
|
|
|
|
#if defined(IFF_PPROMISC)
|
2012-07-25 22:51:05 +02:00
|
|
|
|
iff |= IFF_PPROMISC;
|
2013-05-21 17:49:54 +09:00
|
|
|
|
#endif
|
2012-07-25 22:51:05 +02:00
|
|
|
|
}
|
2013-09-07 12:35:14 +03:00
|
|
|
|
if (nd & NETDEV_LOOPBACK) {
|
|
|
|
|
iff |= IFF_LOOPBACK;
|
|
|
|
|
}
|
2012-07-25 22:51:05 +02:00
|
|
|
|
return iff;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
iff_to_nd_flags(int iff)
|
|
|
|
|
{
|
|
|
|
|
enum netdev_flags nd = 0;
|
|
|
|
|
if (iff & IFF_UP) {
|
|
|
|
|
nd |= NETDEV_UP;
|
|
|
|
|
}
|
|
|
|
|
if (iff & IFF_PROMISC) {
|
|
|
|
|
nd |= NETDEV_PROMISC;
|
|
|
|
|
}
|
2013-09-07 12:35:14 +03:00
|
|
|
|
if (iff & IFF_LOOPBACK) {
|
|
|
|
|
nd |= NETDEV_LOOPBACK;
|
|
|
|
|
}
|
2012-07-25 22:51:05 +02:00
|
|
|
|
return nd;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
2013-03-15 15:54:36 -07:00
|
|
|
|
netdev_bsd_update_flags(struct netdev *netdev_, enum netdev_flags off,
|
2013-05-10 16:23:03 -04:00
|
|
|
|
enum netdev_flags on, enum netdev_flags *old_flagsp)
|
2012-07-25 22:51:05 +02:00
|
|
|
|
{
|
|
|
|
|
int old_flags, new_flags;
|
|
|
|
|
int error;
|
|
|
|
|
|
2013-03-15 15:54:36 -07:00
|
|
|
|
error = get_flags(netdev_, &old_flags);
|
2012-07-25 22:51:05 +02:00
|
|
|
|
if (!error) {
|
|
|
|
|
*old_flagsp = iff_to_nd_flags(old_flags);
|
|
|
|
|
new_flags = (old_flags & ~nd_to_iff_flags(off)) | nd_to_iff_flags(on);
|
|
|
|
|
if (new_flags != old_flags) {
|
2013-05-21 17:49:55 +09:00
|
|
|
|
error = set_flags(netdev_get_kernel_name(netdev_), new_flags);
|
2014-04-03 00:17:34 -07:00
|
|
|
|
netdev_change_seq_changed(netdev_);
|
2012-07-25 22:51:05 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return error;
|
|
|
|
|
}
|
|
|
|
|
|
2018-10-16 19:47:16 +03:00
|
|
|
|
#define NETDEV_BSD_CLASS_COMMON \
|
|
|
|
|
.run = netdev_bsd_run, \
|
|
|
|
|
.wait = netdev_bsd_wait, \
|
|
|
|
|
.alloc = netdev_bsd_alloc, \
|
|
|
|
|
.destruct = netdev_bsd_destruct, \
|
|
|
|
|
.dealloc = netdev_bsd_dealloc, \
|
|
|
|
|
.send = netdev_bsd_send, \
|
|
|
|
|
.send_wait = netdev_bsd_send_wait, \
|
|
|
|
|
.set_etheraddr = netdev_bsd_set_etheraddr, \
|
|
|
|
|
.get_etheraddr = netdev_bsd_get_etheraddr, \
|
|
|
|
|
.get_mtu = netdev_bsd_get_mtu, \
|
|
|
|
|
.get_ifindex = netdev_bsd_get_ifindex, \
|
|
|
|
|
.get_carrier = netdev_bsd_get_carrier, \
|
|
|
|
|
.get_stats = netdev_bsd_get_stats, \
|
|
|
|
|
.get_features = netdev_bsd_get_features, \
|
2023-07-17 10:08:11 +02:00
|
|
|
|
.get_speed = netdev_bsd_get_speed, \
|
2018-10-16 19:47:16 +03:00
|
|
|
|
.set_in4 = netdev_bsd_set_in4, \
|
|
|
|
|
.get_addr_list = netdev_bsd_get_addr_list, \
|
|
|
|
|
.get_next_hop = netdev_bsd_get_next_hop, \
|
|
|
|
|
.arp_lookup = netdev_bsd_arp_lookup, \
|
|
|
|
|
.update_flags = netdev_bsd_update_flags, \
|
|
|
|
|
.rxq_alloc = netdev_bsd_rxq_alloc, \
|
|
|
|
|
.rxq_construct = netdev_bsd_rxq_construct, \
|
|
|
|
|
.rxq_destruct = netdev_bsd_rxq_destruct, \
|
|
|
|
|
.rxq_dealloc = netdev_bsd_rxq_dealloc, \
|
|
|
|
|
.rxq_recv = netdev_bsd_rxq_recv, \
|
|
|
|
|
.rxq_wait = netdev_bsd_rxq_wait, \
|
|
|
|
|
.rxq_drain = netdev_bsd_rxq_drain
|
|
|
|
|
|
|
|
|
|
const struct netdev_class netdev_bsd_class = {
|
|
|
|
|
NETDEV_BSD_CLASS_COMMON,
|
|
|
|
|
.type = "system",
|
|
|
|
|
.construct = netdev_bsd_construct_system,
|
|
|
|
|
};
|
2012-07-25 22:51:05 +02:00
|
|
|
|
|
2018-10-16 19:47:16 +03:00
|
|
|
|
const struct netdev_class netdev_tap_class = {
|
|
|
|
|
NETDEV_BSD_CLASS_COMMON,
|
|
|
|
|
.type = "tap",
|
|
|
|
|
.construct = netdev_bsd_construct_tap,
|
|
|
|
|
};
|
2012-07-25 22:51:05 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
destroy_tap(int fd, const char *name)
|
|
|
|
|
{
|
|
|
|
|
struct ifreq ifr;
|
|
|
|
|
|
|
|
|
|
close(fd);
|
2024-11-29 23:48:15 +01:00
|
|
|
|
memset(&ifr, 0, sizeof ifr);
|
2012-07-25 22:51:05 +02:00
|
|
|
|
strcpy(ifr.ifr_name, name);
|
|
|
|
|
/* XXX What to do if this call fails? */
|
2013-08-09 21:14:23 -07:00
|
|
|
|
af_inet_ioctl(SIOCIFDESTROY, &ifr);
|
2012-07-25 22:51:05 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
2013-03-15 15:54:36 -07:00
|
|
|
|
get_flags(const struct netdev *netdev, int *flags)
|
2012-07-25 22:51:05 +02:00
|
|
|
|
{
|
|
|
|
|
struct ifreq ifr;
|
|
|
|
|
int error;
|
|
|
|
|
|
2024-11-29 23:48:15 +01:00
|
|
|
|
memset(&ifr, 0, sizeof ifr);
|
2013-08-09 21:14:23 -07:00
|
|
|
|
error = af_inet_ifreq_ioctl(netdev_get_kernel_name(netdev), &ifr,
|
2013-03-15 15:54:36 -07:00
|
|
|
|
SIOCGIFFLAGS, "SIOCGIFFLAGS");
|
2012-07-25 22:51:05 +02:00
|
|
|
|
|
2013-05-21 17:49:54 +09:00
|
|
|
|
*flags = ifr_get_flags(&ifr);
|
2012-07-25 22:51:05 +02:00
|
|
|
|
|
|
|
|
|
return error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
2013-05-10 08:55:25 -07:00
|
|
|
|
set_flags(const char *name, int flags)
|
2012-07-25 22:51:05 +02:00
|
|
|
|
{
|
|
|
|
|
struct ifreq ifr;
|
|
|
|
|
|
2024-11-29 23:48:15 +01:00
|
|
|
|
memset(&ifr, 0, sizeof ifr);
|
2013-05-21 17:49:54 +09:00
|
|
|
|
ifr_set_flags(&ifr, flags);
|
2012-07-25 22:51:05 +02:00
|
|
|
|
|
2013-08-09 21:14:23 -07:00
|
|
|
|
return af_inet_ifreq_ioctl(name, &ifr, SIOCSIFFLAGS, "SIOCSIFFLAGS");
|
2012-07-25 22:51:05 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
get_ifindex(const struct netdev *netdev_, int *ifindexp)
|
|
|
|
|
{
|
2013-03-15 15:54:36 -07:00
|
|
|
|
struct netdev_bsd *netdev = netdev_bsd_cast(netdev_);
|
2012-07-25 22:51:05 +02:00
|
|
|
|
*ifindexp = 0;
|
2013-03-15 15:54:36 -07:00
|
|
|
|
if (!(netdev->cache_valid & VALID_IFINDEX)) {
|
2012-07-25 22:51:05 +02:00
|
|
|
|
int ifindex = if_nametoindex(netdev_get_name(netdev_));
|
|
|
|
|
if (ifindex <= 0) {
|
|
|
|
|
return errno;
|
|
|
|
|
}
|
2013-03-15 15:54:36 -07:00
|
|
|
|
netdev->cache_valid |= VALID_IFINDEX;
|
|
|
|
|
netdev->ifindex = ifindex;
|
2012-07-25 22:51:05 +02:00
|
|
|
|
}
|
2013-03-15 15:54:36 -07:00
|
|
|
|
*ifindexp = netdev->ifindex;
|
2012-07-25 22:51:05 +02:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
2015-08-28 14:55:11 -07:00
|
|
|
|
get_etheraddr(const char *netdev_name, struct eth_addr *ea)
|
2012-07-25 22:51:05 +02:00
|
|
|
|
{
|
|
|
|
|
struct ifaddrs *head;
|
|
|
|
|
struct ifaddrs *ifa;
|
|
|
|
|
struct sockaddr_dl *sdl;
|
|
|
|
|
|
|
|
|
|
if (getifaddrs(&head) != 0) {
|
|
|
|
|
VLOG_ERR("getifaddrs on %s device failed: %s", netdev_name,
|
2013-06-24 10:54:49 -07:00
|
|
|
|
ovs_strerror(errno));
|
2012-07-25 22:51:05 +02:00
|
|
|
|
return errno;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (ifa = head; ifa; ifa = ifa->ifa_next) {
|
|
|
|
|
if (ifa->ifa_addr->sa_family == AF_LINK) {
|
|
|
|
|
if (!strcmp(ifa->ifa_name, netdev_name)) {
|
2014-04-30 13:33:54 +09:00
|
|
|
|
sdl = ALIGNED_CAST(struct sockaddr_dl *, ifa->ifa_addr);
|
2012-07-25 22:51:05 +02:00
|
|
|
|
if (sdl) {
|
|
|
|
|
memcpy(ea, LLADDR(sdl), sdl->sdl_alen);
|
|
|
|
|
freeifaddrs(head);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VLOG_ERR("could not find ethernet address for %s device", netdev_name);
|
|
|
|
|
freeifaddrs(head);
|
|
|
|
|
return ENODEV;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
2013-05-21 17:49:54 +09:00
|
|
|
|
set_etheraddr(const char *netdev_name OVS_UNUSED, int hwaddr_family OVS_UNUSED,
|
|
|
|
|
int hwaddr_len OVS_UNUSED,
|
2015-08-28 14:55:11 -07:00
|
|
|
|
const struct eth_addr mac OVS_UNUSED)
|
2012-07-25 22:51:05 +02:00
|
|
|
|
{
|
2013-05-22 20:56:47 -07:00
|
|
|
|
#if defined(__FreeBSD__)
|
2012-07-25 22:51:05 +02:00
|
|
|
|
struct ifreq ifr;
|
2013-08-09 21:14:23 -07:00
|
|
|
|
int error;
|
2012-07-25 22:51:05 +02:00
|
|
|
|
|
|
|
|
|
memset(&ifr, 0, sizeof ifr);
|
2015-02-20 12:32:08 -08:00
|
|
|
|
ovs_strlcpy(ifr.ifr_name, netdev_name, sizeof ifr.ifr_name);
|
2012-07-25 22:51:05 +02:00
|
|
|
|
ifr.ifr_addr.sa_family = hwaddr_family;
|
|
|
|
|
ifr.ifr_addr.sa_len = hwaddr_len;
|
2015-08-28 14:55:11 -07:00
|
|
|
|
memcpy(ifr.ifr_addr.sa_data, &mac, hwaddr_len);
|
2013-08-09 21:14:23 -07:00
|
|
|
|
error = af_inet_ioctl(SIOCSIFLLADDR, &ifr);
|
|
|
|
|
if (error) {
|
2012-07-25 22:51:05 +02:00
|
|
|
|
VLOG_ERR("ioctl(SIOCSIFLLADDR) on %s device failed: %s",
|
2013-08-09 21:14:23 -07:00
|
|
|
|
netdev_name, ovs_strerror(error));
|
|
|
|
|
return error;
|
2012-07-25 22:51:05 +02:00
|
|
|
|
}
|
|
|
|
|
return 0;
|
2013-05-22 20:56:47 -07:00
|
|
|
|
#elif defined(__NetBSD__)
|
|
|
|
|
struct if_laddrreq req;
|
|
|
|
|
struct sockaddr_dl *sdl;
|
|
|
|
|
struct sockaddr_storage oldaddr;
|
2013-08-07 23:48:59 -07:00
|
|
|
|
int error;
|
2013-05-22 20:56:47 -07:00
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* get the old address, add new one, and then remove old one.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
if (hwaddr_len != ETH_ADDR_LEN) {
|
|
|
|
|
/* just to be safe about sockaddr storage size */
|
|
|
|
|
return EOPNOTSUPP;
|
|
|
|
|
}
|
|
|
|
|
memset(&req, 0, sizeof(req));
|
2015-02-20 12:32:08 -08:00
|
|
|
|
ovs_strlcpy(req.iflr_name, netdev_name, sizeof(req.iflr_name));
|
2013-05-22 20:56:47 -07:00
|
|
|
|
req.addr.ss_len = sizeof(req.addr);
|
|
|
|
|
req.addr.ss_family = hwaddr_family;
|
|
|
|
|
sdl = (struct sockaddr_dl *)&req.addr;
|
|
|
|
|
sdl->sdl_alen = hwaddr_len;
|
2013-08-07 23:48:59 -07:00
|
|
|
|
|
|
|
|
|
error = af_link_ioctl(SIOCGLIFADDR, &req);
|
|
|
|
|
if (error) {
|
|
|
|
|
return error;
|
2013-05-22 20:56:47 -07:00
|
|
|
|
}
|
2015-10-14 04:55:26 +00:00
|
|
|
|
if (!memcmp(&sdl->sdl_data[sdl->sdl_nlen], &mac, hwaddr_len)) {
|
2013-05-22 20:56:47 -07:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
oldaddr = req.addr;
|
|
|
|
|
|
|
|
|
|
memset(&req, 0, sizeof(req));
|
2015-02-20 12:32:08 -08:00
|
|
|
|
ovs_strlcpy(req.iflr_name, netdev_name, sizeof(req.iflr_name));
|
2013-05-22 20:56:47 -07:00
|
|
|
|
req.flags = IFLR_ACTIVE;
|
|
|
|
|
sdl = (struct sockaddr_dl *)&req.addr;
|
|
|
|
|
sdl->sdl_len = offsetof(struct sockaddr_dl, sdl_data) + hwaddr_len;
|
|
|
|
|
sdl->sdl_alen = hwaddr_len;
|
|
|
|
|
sdl->sdl_family = hwaddr_family;
|
2015-10-14 04:55:26 +00:00
|
|
|
|
memcpy(sdl->sdl_data, &mac, hwaddr_len);
|
2013-08-07 23:48:59 -07:00
|
|
|
|
error = af_link_ioctl(SIOCALIFADDR, &req);
|
|
|
|
|
if (error) {
|
|
|
|
|
return error;
|
2013-05-22 20:56:47 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
memset(&req, 0, sizeof(req));
|
2015-02-20 12:32:08 -08:00
|
|
|
|
ovs_strlcpy(req.iflr_name, netdev_name, sizeof(req.iflr_name));
|
2013-05-22 20:56:47 -07:00
|
|
|
|
req.addr = oldaddr;
|
2013-08-07 23:48:59 -07:00
|
|
|
|
return af_link_ioctl(SIOCDLIFADDR, &req);
|
2013-05-22 20:56:47 -07:00
|
|
|
|
#else
|
|
|
|
|
#error not implemented
|
2013-05-21 17:49:54 +09:00
|
|
|
|
#endif
|
2012-07-25 22:51:05 +02:00
|
|
|
|
}
|
|
|
|
|
|
2013-05-21 17:49:54 +09:00
|
|
|
|
static int
|
|
|
|
|
ifr_get_flags(const struct ifreq *ifr)
|
|
|
|
|
{
|
|
|
|
|
#ifdef HAVE_STRUCT_IFREQ_IFR_FLAGSHIGH
|
2015-04-05 00:59:26 +08:00
|
|
|
|
return (ifr->ifr_flagshigh << 16) | (ifr->ifr_flags & 0xffff);
|
2013-05-21 17:49:54 +09:00
|
|
|
|
#else
|
2013-05-23 10:16:16 -04:00
|
|
|
|
return ifr->ifr_flags;
|
2013-05-21 17:49:54 +09:00
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
ifr_set_flags(struct ifreq *ifr, int flags)
|
|
|
|
|
{
|
|
|
|
|
#ifdef HAVE_STRUCT_IFREQ_IFR_FLAGSHIGH
|
2015-04-05 00:59:26 +08:00
|
|
|
|
ifr->ifr_flags = flags & 0xffff;
|
2013-05-21 17:49:54 +09:00
|
|
|
|
ifr->ifr_flagshigh = flags >> 16;
|
2015-04-05 00:59:26 +08:00
|
|
|
|
#else
|
|
|
|
|
ifr->ifr_flags = flags;
|
2013-05-21 17:49:54 +09:00
|
|
|
|
#endif
|
|
|
|
|
}
|
2013-08-07 23:48:59 -07:00
|
|
|
|
|
2014-06-25 07:41:41 +00:00
|
|
|
|
#if defined(__NetBSD__)
|
2013-08-07 23:48:59 -07:00
|
|
|
|
/* Calls ioctl() on an AF_LINK sock, passing the specified 'command' and
|
|
|
|
|
* 'arg'. Returns 0 if successful, otherwise a positive errno value. */
|
|
|
|
|
int
|
2013-08-13 07:20:22 +09:00
|
|
|
|
af_link_ioctl(unsigned long command, const void *arg)
|
2013-08-07 23:48:59 -07:00
|
|
|
|
{
|
|
|
|
|
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);
|
|
|
|
|
}
|
2014-06-25 07:41:41 +00:00
|
|
|
|
#endif
|
2016-03-15 11:52:58 -04:00
|
|
|
|
#endif /* !defined(__MACH__) */
|