#include <arpa/inet.h>
#include <inttypes.h>
#include <linux/gen_stats.h>
+#include <linux/if_ether.h>
#include <linux/if_tun.h>
#include <linux/ip.h>
#include <linux/types.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <netpacket/packet.h>
-#include <net/ethernet.h>
#include <net/if.h>
-#include <linux/if_tunnel.h>
#include <net/if_arp.h>
#include <net/if_packet.h>
#include <net/route.h>
VLOG_DEFINE_THIS_MODULE(netdev_linux);
-COVERAGE_DEFINE(netdev_get_vlan_vid);
COVERAGE_DEFINE(netdev_set_policing);
COVERAGE_DEFINE(netdev_arp_lookup);
COVERAGE_DEFINE(netdev_get_ifindex);
#define TC_RTAB_SIZE 1024
#endif
-static struct nln_notifier netdev_linux_cache_notifier;
+static struct nln_notifier *netdev_linux_cache_notifier = NULL;
static int cache_notifier_refcount;
enum {
VALID_IN4 = 1 << 2,
VALID_IN6 = 1 << 3,
VALID_MTU = 1 << 4,
- VALID_CARRIER = 1 << 5,
- VALID_POLICING = 1 << 6,
- VALID_HAVE_VPORT_STATS = 1 << 7
+ VALID_POLICING = 1 << 5,
+ VALID_HAVE_VPORT_STATS = 1 << 6
};
struct tap_state {
struct in_addr address, netmask;
struct in6_addr in6;
int mtu;
- int carrier;
+ bool carrier;
+ long long int carrier_resets;
uint32_t kbits_rate; /* Policing data. */
uint32_t kbits_burst;
bool have_vport_stats;
const uint8_t[ETH_ADDR_LEN]);
static int get_stats_via_netlink(int ifindex, struct netdev_stats *stats);
static int get_stats_via_proc(const char *netdev_name, struct netdev_stats *stats);
+static int get_carrier_via_sysfs(const char *name, bool *carrier);
static int af_packet_sock(void);
static void netdev_linux_miimon_run(void);
static void netdev_linux_miimon_wait(void);
static void
netdev_linux_run(void)
{
- rtnetlink_link_notifier_run();
+ rtnetlink_link_run();
netdev_linux_miimon_run();
}
static void
netdev_linux_wait(void)
{
- rtnetlink_link_notifier_wait();
+ rtnetlink_link_wait();
netdev_linux_miimon_wait();
}
if (is_netdev_linux_class(netdev_class)) {
dev = netdev_dev_linux_cast(base_dev);
+
+ if (dev->carrier != change->running) {
+ dev->carrier = change->running;
+ dev->carrier_resets++;
+ }
+
netdev_dev_linux_changed(dev);
}
}
shash_init(&device_shash);
netdev_dev_get_devices(&netdev_linux_class, &device_shash);
SHASH_FOR_EACH (node, &device_shash) {
+ bool carrier;
+
dev = node->data;
+
+ get_carrier_via_sysfs(node->name, &carrier);
+ if (dev->carrier != carrier) {
+ dev->carrier = carrier;
+ dev->carrier_resets++;
+ }
+
netdev_dev_linux_changed(dev);
}
shash_destroy(&device_shash);
struct netdev_dev **netdev_devp)
{
struct netdev_dev_linux *netdev_dev;
- int error;
if (!cache_notifier_refcount) {
- error = rtnetlink_link_notifier_register(&netdev_linux_cache_notifier,
- netdev_linux_cache_cb, NULL);
- if (error) {
- return error;
+ assert(!netdev_linux_cache_notifier);
+
+ netdev_linux_cache_notifier =
+ rtnetlink_link_notifier_create(netdev_linux_cache_cb, NULL);
+
+ if (!netdev_linux_cache_notifier) {
+ return EINVAL;
}
}
cache_notifier_refcount++;
netdev_dev = xzalloc(sizeof *netdev_dev);
netdev_dev->change_seq = 1;
netdev_dev_init(&netdev_dev->netdev_dev, name, class);
+ get_carrier_via_sysfs(name, &netdev_dev->carrier);
*netdev_devp = &netdev_dev->netdev_dev;
return 0;
cache_notifier_refcount--;
if (!cache_notifier_refcount) {
- rtnetlink_link_notifier_unregister(&netdev_linux_cache_notifier);
+ assert(netdev_linux_cache_notifier);
+ rtnetlink_link_notifier_destroy(netdev_linux_cache_notifier);
+ netdev_linux_cache_notifier = NULL;
}
} else if (class == &netdev_tap_class) {
destroy_tap(netdev_dev);
free(netdev);
}
-/* Initializes 'sset' with a list of the names of all known network devices. */
-static int
-netdev_linux_enumerate(struct sset *sset)
-{
- struct if_nameindex *names;
-
- names = if_nameindex();
- if (names) {
- size_t i;
-
- for (i = 0; names[i].if_name != NULL; i++) {
- sset_add(sset, names[i].if_name);
- }
- if_freenameindex(names);
- return 0;
- } else {
- VLOG_WARN("could not obtain list of network device names: %s",
- strerror(errno));
- return errno;
- }
-}
-
static int
netdev_linux_listen(struct netdev *netdev_)
{
{
struct netdev_dev_linux *netdev_dev =
netdev_dev_linux_cast(netdev_get_dev(netdev_));
- int error = 0;
- char *fn = NULL;
- int fd = -1;
if (netdev_dev->miimon_interval > 0) {
*carrier = netdev_dev->miimon;
- return 0;
+ } else {
+ *carrier = netdev_dev->carrier;
}
- if (!(netdev_dev->cache_valid & VALID_CARRIER)) {
- char line[8];
- int retval;
-
- fn = xasprintf("/sys/class/net/%s/carrier",
- netdev_get_name(netdev_));
- fd = open(fn, O_RDONLY);
- if (fd < 0) {
- error = errno;
- VLOG_WARN_RL(&rl, "%s: open failed: %s", fn, strerror(error));
- goto exit;
- }
-
- retval = read(fd, line, sizeof line);
- if (retval < 0) {
- error = errno;
- if (error == EINVAL) {
- /* This is the normal return value when we try to check carrier
- * if the network device is not up. */
- } else {
- VLOG_WARN_RL(&rl, "%s: read failed: %s", fn, strerror(error));
- }
- goto exit;
- } else if (retval == 0) {
- error = EPROTO;
- VLOG_WARN_RL(&rl, "%s: unexpected end of file", fn);
- goto exit;
- }
-
- if (line[0] != '0' && line[0] != '1') {
- error = EPROTO;
- VLOG_WARN_RL(&rl, "%s: value is %c (expected 0 or 1)",
- fn, line[0]);
- goto exit;
- }
- netdev_dev->carrier = line[0] != '0';
- netdev_dev->cache_valid |= VALID_CARRIER;
- }
- *carrier = netdev_dev->carrier;
- error = 0;
+ return 0;
+}
-exit:
- if (fd >= 0) {
- close(fd);
- }
- free(fn);
- return error;
+static long long int
+netdev_linux_get_carrier_resets(const struct netdev *netdev)
+{
+ return netdev_dev_linux_cast(netdev_get_dev(netdev))->carrier_resets;
}
static int
ETHTOOL_SSET, "ETHTOOL_SSET");
}
-/* If 'netdev_name' is the name of a VLAN network device (e.g. one created with
- * vconfig(8)), sets '*vlan_vid' to the VLAN VID associated with that device
- * and returns 0. Otherwise returns a errno value (specifically ENOENT if
- * 'netdev_name' is the name of a network device that is not a VLAN device) and
- * sets '*vlan_vid' to -1. */
-static int
-netdev_linux_get_vlan_vid(const struct netdev *netdev, int *vlan_vid)
-{
- const char *netdev_name = netdev_get_name(netdev);
- struct ds line = DS_EMPTY_INITIALIZER;
- FILE *stream = NULL;
- int error;
- char *fn;
-
- COVERAGE_INC(netdev_get_vlan_vid);
- fn = xasprintf("/proc/net/vlan/%s", netdev_name);
- stream = fopen(fn, "r");
- if (!stream) {
- error = errno;
- goto done;
- }
-
- if (ds_get_line(&line, stream)) {
- if (ferror(stream)) {
- error = errno;
- VLOG_ERR_RL(&rl, "error reading \"%s\": %s", fn, strerror(errno));
- } else {
- error = EPROTO;
- VLOG_ERR_RL(&rl, "unexpected end of file reading \"%s\"", fn);
- }
- goto done;
- }
-
- if (!sscanf(ds_cstr(&line), "%*s VID: %d", vlan_vid)) {
- error = EPROTO;
- VLOG_ERR_RL(&rl, "parse error reading \"%s\" line 1: \"%s\"",
- fn, ds_cstr(&line));
- goto done;
- }
-
- error = 0;
-
-done:
- free(fn);
- if (stream) {
- fclose(stream);
- }
- ds_destroy(&line);
- if (error) {
- *vlan_vid = -1;
- }
- return error;
-}
-
#define POLICE_ADD_CMD "/sbin/tc qdisc add dev %s handle ffff: ingress"
#define POLICE_CONFIG_CMD "/sbin/tc filter add dev %s parent ffff: protocol ip prio 50 u32 match ip src 0.0.0.0/0 police rate %dkbit burst %dk mtu 65535 drop flowid :1"
return netdev_dev_linux_cast(netdev_get_dev(netdev))->change_seq;
}
-#define NETDEV_LINUX_CLASS(NAME, CREATE, ENUMERATE, GET_STATS, SET_STATS) \
+#define NETDEV_LINUX_CLASS(NAME, CREATE, GET_STATS, SET_STATS) \
{ \
NAME, \
\
netdev_linux_open, \
netdev_linux_close, \
\
- ENUMERATE, \
- \
netdev_linux_listen, \
netdev_linux_recv, \
netdev_linux_recv_wait, \
netdev_linux_set_mtu, \
netdev_linux_get_ifindex, \
netdev_linux_get_carrier, \
+ netdev_linux_get_carrier_resets, \
netdev_linux_set_miimon_interval, \
GET_STATS, \
SET_STATS, \
\
netdev_linux_get_features, \
netdev_linux_set_advertisements, \
- netdev_linux_get_vlan_vid, \
\
netdev_linux_set_policing, \
netdev_linux_get_qos_types, \
NETDEV_LINUX_CLASS(
"system",
netdev_linux_create,
- netdev_linux_enumerate,
netdev_linux_get_stats,
NULL); /* set_stats */
NETDEV_LINUX_CLASS(
"tap",
netdev_linux_create_tap,
- NULL, /* enumerate */
netdev_pseudo_get_stats,
NULL); /* set_stats */
NETDEV_LINUX_CLASS(
"internal",
netdev_linux_create,
- NULL, /* enumerate */
netdev_pseudo_get_stats,
netdev_vport_set_stats);
\f
return tc_bytes_to_ticks(Bps, MAX(burst_bytes, min_burst));
}
\f
+/* Linux-only functions declared in netdev-linux.h */
+
+/* Returns a fd for an AF_INET socket or a negative errno value. */
+int
+netdev_linux_get_af_inet_sock(void)
+{
+ int error = netdev_linux_init();
+ return error ? -error : af_inet_sock;
+}
+
+/* Modifies the 'flag' bit in ethtool's flags field for 'netdev'. If
+ * 'enable' is true, the bit is set. Otherwise, it is cleared. */
+int
+netdev_linux_ethtool_set_flag(struct netdev *netdev, uint32_t flag,
+ const char *flag_name, bool enable)
+{
+ const char *netdev_name = netdev_get_name(netdev);
+ struct ethtool_value evalue;
+ uint32_t new_flags;
+ int error;
+
+ memset(&evalue, 0, sizeof evalue);
+ error = netdev_linux_do_ethtool(netdev_name,
+ (struct ethtool_cmd *)&evalue,
+ ETHTOOL_GFLAGS, "ETHTOOL_GFLAGS");
+ if (error) {
+ return error;
+ }
+
+ evalue.data = new_flags = (evalue.data & ~flag) | (enable ? flag : 0);
+ error = netdev_linux_do_ethtool(netdev_name,
+ (struct ethtool_cmd *)&evalue,
+ ETHTOOL_SFLAGS, "ETHTOOL_SFLAGS");
+ if (error) {
+ return error;
+ }
+
+ memset(&evalue, 0, sizeof evalue);
+ error = netdev_linux_do_ethtool(netdev_name,
+ (struct ethtool_cmd *)&evalue,
+ ETHTOOL_GFLAGS, "ETHTOOL_GFLAGS");
+ if (error) {
+ return error;
+ }
+
+ if (new_flags != evalue.data) {
+ VLOG_WARN_RL(&rl, "attempt to %s ethtool %s flag on network "
+ "device %s failed", enable ? "enable" : "disable",
+ flag_name, netdev_name);
+ return EOPNOTSUPP;
+ }
+
+ return 0;
+}
+\f
+/* Utility functions. */
+
/* Copies 'src' into 'dst', performing format conversion in the process. */
static void
netdev_stats_from_rtnl_link_stats(struct netdev_stats *dst,
dst->tx_window_errors = src->tx_window_errors;
}
-\f
-/* Utility functions. */
-
static int
get_stats_via_netlink(int ifindex, struct netdev_stats *stats)
{
return ENODEV;
}
+static int
+get_carrier_via_sysfs(const char *name, bool *carrier)
+{
+ char line[8];
+ int retval;
+
+ int error = 0;
+ char *fn = NULL;
+ int fd = -1;
+
+ *carrier = false;
+
+ fn = xasprintf("/sys/class/net/%s/carrier", name);
+ fd = open(fn, O_RDONLY);
+ if (fd < 0) {
+ error = errno;
+ VLOG_WARN_RL(&rl, "%s: open failed: %s", fn, strerror(error));
+ goto exit;
+ }
+
+ retval = read(fd, line, sizeof line);
+ if (retval < 0) {
+ error = errno;
+ if (error == EINVAL) {
+ /* This is the normal return value when we try to check carrier if
+ * the network device is not up. */
+ } else {
+ VLOG_WARN_RL(&rl, "%s: read failed: %s", fn, strerror(error));
+ }
+ goto exit;
+ } else if (retval == 0) {
+ error = EPROTO;
+ VLOG_WARN_RL(&rl, "%s: unexpected end of file", fn);
+ goto exit;
+ }
+
+ if (line[0] != '0' && line[0] != '1') {
+ error = EPROTO;
+ VLOG_WARN_RL(&rl, "%s: value is %c (expected 0 or 1)", fn, line[0]);
+ goto exit;
+ }
+ *carrier = line[0] != '0';
+ error = 0;
+
+exit:
+ if (fd >= 0) {
+ close(fd);
+ }
+ free(fn);
+ return error;
+}
+
static int
get_flags(const struct netdev *netdev, int *flags)
{
}
}
-/* Modifies the 'flag' bit in ethtool's flags field for 'netdev'. If
- * 'enable' is true, the bit is set. Otherwise, it is cleared. */
-int
-netdev_linux_ethtool_set_flag(struct netdev *netdev, uint32_t flag,
- const char *flag_name, bool enable)
-{
- const char *netdev_name = netdev_get_name(netdev);
- struct ethtool_value evalue;
- uint32_t new_flags;
- int error;
-
- memset(&evalue, 0, sizeof evalue);
- error = netdev_linux_do_ethtool(netdev_name,
- (struct ethtool_cmd *)&evalue,
- ETHTOOL_GFLAGS, "ETHTOOL_GFLAGS");
- if (error) {
- return error;
- }
-
- evalue.data = new_flags = (evalue.data & ~flag) | (enable ? flag : 0);
- error = netdev_linux_do_ethtool(netdev_name,
- (struct ethtool_cmd *)&evalue,
- ETHTOOL_SFLAGS, "ETHTOOL_SFLAGS");
- if (error) {
- return error;
- }
-
- memset(&evalue, 0, sizeof evalue);
- error = netdev_linux_do_ethtool(netdev_name,
- (struct ethtool_cmd *)&evalue,
- ETHTOOL_GFLAGS, "ETHTOOL_GFLAGS");
- if (error) {
- return error;
- }
-
- if (new_flags != evalue.data) {
- VLOG_WARN_RL(&rl, "attempt to %s ethtool %s flag on network "
- "device %s failed", enable ? "enable" : "disable",
- flag_name, netdev_name);
- return EOPNOTSUPP;
- }
-
- return 0;
-}
-
static int
netdev_linux_do_ioctl(const char *name, struct ifreq *ifr, int cmd,
const char *cmd_name)