#include "ovs-atomic.h"
#include "packets.h"
#include "poll-loop.h"
-#include "rtnetlink-link.h"
+#include "rtnetlink.h"
#include "shash.h"
#include "socket-util.h"
#include "sset.h"
uint16_t tp_vlan_tpid;
};
+/* Linux 2.6.27 introduced ethtool_cmd_speed
+ *
+ * To avoid revisiting problems reported with using configure to detect
+ * compatibility (see report at
+ * http://openvswitch.org/pipermail/dev/2014-October/047978.html)
+ * unconditionally replace ethtool_cmd_speed. */
+#define ethtool_cmd_speed rpl_ethtool_cmd_speed
+static inline uint32_t rpl_ethtool_cmd_speed(const struct ethtool_cmd *ep)
+{
+ return ep->speed | (ep->speed_hi << 16);
+}
+
+/* Linux 2.6.30 introduced supported and advertised flags for
+ * 1G base KX, and 10G base KX4, KR and R. */
+#ifndef SUPPORTED_1000baseKX_Full
+#define SUPPORTED_1000baseKX_Full (1 << 17)
+#define SUPPORTED_10000baseKX4_Full (1 << 18)
+#define SUPPORTED_10000baseKR_Full (1 << 19)
+#define SUPPORTED_10000baseR_FEC (1 << 20)
+#define ADVERTISED_1000baseKX_Full (1 << 17)
+#define ADVERTISED_10000baseKX4_Full (1 << 18)
+#define ADVERTISED_10000baseKR_Full (1 << 19)
+#define ADVERTISED_10000baseR_FEC (1 << 20)
+#endif
+
+/* Linux 3.5 introduced supported and advertised flags for
+ * 40G base KR4, CR4, SR4 and LR4. */
+#ifndef SUPPORTED_40000baseKR4_Full
+#define SUPPORTED_40000baseKR4_Full (1 << 23)
+#define SUPPORTED_40000baseCR4_Full (1 << 24)
+#define SUPPORTED_40000baseSR4_Full (1 << 25)
+#define SUPPORTED_40000baseLR4_Full (1 << 26)
+#define ADVERTISED_40000baseKR4_Full (1 << 23)
+#define ADVERTISED_40000baseCR4_Full (1 << 24)
+#define ADVERTISED_40000baseSR4_Full (1 << 25)
+#define ADVERTISED_40000baseLR4_Full (1 << 26)
+#endif
+
/* Linux 2.6.35 introduced IFLA_STATS64 and rtnl_link_stats64.
*
* Tests for rtnl_link_stats64 don't seem to consistently work, e.g. on
/* The following are figured out "on demand" only. They are only valid
* when the corresponding VALID_* bit in 'cache_valid' is set. */
int ifindex;
- uint8_t etheraddr[ETH_ADDR_LEN];
+ struct eth_addr etheraddr;
struct in_addr address, netmask;
struct in6_addr in6;
int mtu;
int netdev_policing_error; /* Cached error code from set policing. */
int get_features_error; /* Cached error code from ETHTOOL_GSET. */
int get_ifindex_error; /* Cached error code from SIOCGIFINDEX. */
+ int in4_error; /* Cached error code from reading in4 addr. */
+ int in6_error; /* Cached error code from reading in6 addr. */
enum netdev_features current; /* Cached from ETHTOOL_GSET. */
enum netdev_features advertised; /* Cached from ETHTOOL_GSET. */
static int do_set_addr(struct netdev *netdev,
int ioctl_nr, const char *ioctl_name,
struct in_addr addr);
-static int get_etheraddr(const char *netdev_name, uint8_t ea[ETH_ADDR_LEN]);
-static int set_etheraddr(const char *netdev_name, const uint8_t[ETH_ADDR_LEN]);
+static int get_etheraddr(const char *netdev_name, struct eth_addr *ea);
+static int set_etheraddr(const char *netdev_name, const struct eth_addr);
static int get_stats_via_netlink(const struct netdev *, struct netdev_stats *);
static int af_packet_sock(void);
static bool netdev_linux_miimon_enabled(void);
}
\f
static void netdev_linux_update(struct netdev_linux *netdev,
- const struct rtnetlink_link_change *)
+ const struct rtnetlink_change *)
OVS_REQUIRES(netdev->mutex);
static void netdev_linux_changed(struct netdev_linux *netdev,
unsigned int ifi_flags, unsigned int mask)
OVS_REQUIRES(netdev->mutex);
-/* Returns a NETLINK_ROUTE socket listening for RTNLGRP_LINK changes, or NULL
+/* Returns a NETLINK_ROUTE socket listening for RTNLGRP_LINK,
+ * RTNLGRP_IPV4_IFADDR and RTNLGRP_IPV6_IFADDR changes, or NULL
* if no such socket could be created. */
static struct nl_sock *
netdev_linux_notify_sock(void)
{
static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER;
static struct nl_sock *sock;
+ unsigned int mcgroups[3] = {RTNLGRP_LINK, RTNLGRP_IPV4_IFADDR,
+ RTNLGRP_IPV6_IFADDR};
if (ovsthread_once_start(&once)) {
int error;
error = nl_sock_create(NETLINK_ROUTE, &sock);
if (!error) {
- error = nl_sock_join_mcgroup(sock, RTNLGRP_LINK);
- if (error) {
- nl_sock_destroy(sock);
- sock = NULL;
+ size_t i;
+
+ for (i = 0; i < ARRAY_SIZE(mcgroups); i++) {
+ error = nl_sock_join_mcgroup(sock, mcgroups[i]);
+ if (error) {
+ nl_sock_destroy(sock);
+ sock = NULL;
+ break;
+ }
}
}
ovsthread_once_done(&once);
ofpbuf_use_stub(&buf, buf_stub, sizeof buf_stub);
error = nl_sock_recv(sock, &buf, false);
if (!error) {
- struct rtnetlink_link_change change;
+ struct rtnetlink_change change;
- if (rtnetlink_link_parse(&buf, &change)) {
+ if (rtnetlink_parse(&buf, &change)) {
struct netdev *netdev_ = netdev_from_name(change.ifname);
if (netdev_ && is_netdev_linux_class(netdev_->netdev_class)) {
struct netdev_linux *netdev = netdev_linux_cast(netdev_);
static void
netdev_linux_update(struct netdev_linux *dev,
- const struct rtnetlink_link_change *change)
+ const struct rtnetlink_change *change)
OVS_REQUIRES(dev->mutex)
{
- if (change->nlmsg_type == RTM_NEWLINK) {
- /* Keep drv-info */
- netdev_linux_changed(dev, change->ifi_flags, VALID_DRVINFO);
+ if (rtnetlink_type_is_rtnlgrp_link(change->nlmsg_type)){
+ if (change->nlmsg_type == RTM_NEWLINK) {
+ /* Keep drv-info, in4, in6. */
+ netdev_linux_changed(dev, change->ifi_flags,
+ VALID_DRVINFO | VALID_IN4 | VALID_IN6);
+
+ /* Update netdev from rtnl-change msg. */
+ if (change->mtu) {
+ dev->mtu = change->mtu;
+ dev->cache_valid |= VALID_MTU;
+ dev->netdev_mtu_error = 0;
+ }
- /* Update netdev from rtnl-change msg. */
- if (change->mtu) {
- dev->mtu = change->mtu;
- dev->cache_valid |= VALID_MTU;
- dev->netdev_mtu_error = 0;
- }
+ if (!eth_addr_is_zero(change->mac)) {
+ dev->etheraddr = change->mac;
+ dev->cache_valid |= VALID_ETHERADDR;
+ dev->ether_addr_error = 0;
+ }
- if (!eth_addr_is_zero(change->addr)) {
- memcpy(dev->etheraddr, change->addr, ETH_ADDR_LEN);
- dev->cache_valid |= VALID_ETHERADDR;
- dev->ether_addr_error = 0;
+ dev->ifindex = change->if_index;
+ dev->cache_valid |= VALID_IFINDEX;
+ dev->get_ifindex_error = 0;
+ } else {
+ netdev_linux_changed(dev, change->ifi_flags, 0);
}
-
- dev->ifindex = change->ifi_index;
- dev->cache_valid |= VALID_IFINDEX;
- dev->get_ifindex_error = 0;
-
+ } else if (rtnetlink_type_is_rtnlgrp_addr(change->nlmsg_type)) {
+ /* Invalidates in4, in6. */
+ netdev_linux_changed(dev, dev->ifi_flags,
+ ~(VALID_IN4 | VALID_IN6));
} else {
- netdev_linux_changed(dev, change->ifi_flags, 0);
+ OVS_NOT_REACHED();
}
}
if (retval < 0) {
return errno;
- } else if (retval > size) {
- return EMSGSIZE;
}
dp_packet_set_size(buffer, dp_packet_size(buffer) + retval);
dp_packet_delete(buffer);
} else {
dp_packet_pad(buffer);
- dp_packet_set_dp_hash(buffer, 0);
+ dp_packet_rss_invalidate(buffer);
packets[0] = buffer;
*c = 1;
}
/* Attempts to set 'netdev''s MAC address to 'mac'. Returns 0 if successful,
* otherwise a positive errno value. */
static int
-netdev_linux_set_etheraddr(struct netdev *netdev_,
- const uint8_t mac[ETH_ADDR_LEN])
+netdev_linux_set_etheraddr(struct netdev *netdev_, const struct eth_addr mac)
{
struct netdev_linux *netdev = netdev_linux_cast(netdev_);
enum netdev_flags old_flags = 0;
netdev->ether_addr_error = error;
netdev->cache_valid |= VALID_ETHERADDR;
if (!error) {
- memcpy(netdev->etheraddr, mac, ETH_ADDR_LEN);
+ netdev->etheraddr = mac;
}
}
/* Copies 'netdev''s MAC address to 'mac' which is passed as param. */
static int
-netdev_linux_get_etheraddr(const struct netdev *netdev_,
- uint8_t mac[ETH_ADDR_LEN])
+netdev_linux_get_etheraddr(const struct netdev *netdev_, struct eth_addr *mac)
{
struct netdev_linux *netdev = netdev_linux_cast(netdev_);
int error;
ovs_mutex_lock(&netdev->mutex);
if (!(netdev->cache_valid & VALID_ETHERADDR)) {
netdev->ether_addr_error = get_etheraddr(netdev_get_name(netdev_),
- netdev->etheraddr);
+ &netdev->etheraddr);
netdev->cache_valid |= VALID_ETHERADDR;
}
error = netdev->ether_addr_error;
if (!error) {
- memcpy(mac, netdev->etheraddr, ETH_ADDR_LEN);
+ *mac = netdev->etheraddr;
}
ovs_mutex_unlock(&netdev->mutex);
int error;
error = get_stats_via_vport__(netdev_, stats);
- if (error && error != ENOENT) {
+ if (error && error != ENOENT && error != ENODEV) {
VLOG_WARN_RL(&rl, "%s: obtaining netdev stats via vport failed "
"(%s)",
netdev_get_name(netdev_), ovs_strerror(error));
if (ecmd.supported & SUPPORTED_1000baseT_Half) {
netdev->supported |= NETDEV_F_1GB_HD;
}
- if (ecmd.supported & SUPPORTED_1000baseT_Full) {
+ if ((ecmd.supported & SUPPORTED_1000baseT_Full) ||
+ (ecmd.supported & SUPPORTED_1000baseKX_Full)) {
netdev->supported |= NETDEV_F_1GB_FD;
}
- if (ecmd.supported & SUPPORTED_10000baseT_Full) {
+ if ((ecmd.supported & SUPPORTED_10000baseT_Full) ||
+ (ecmd.supported & SUPPORTED_10000baseKX4_Full) ||
+ (ecmd.supported & SUPPORTED_10000baseKR_Full) ||
+ (ecmd.supported & SUPPORTED_10000baseR_FEC)) {
netdev->supported |= NETDEV_F_10GB_FD;
}
+ if ((ecmd.supported & SUPPORTED_40000baseKR4_Full) ||
+ (ecmd.supported & SUPPORTED_40000baseCR4_Full) ||
+ (ecmd.supported & SUPPORTED_40000baseSR4_Full) ||
+ (ecmd.supported & SUPPORTED_40000baseLR4_Full)) {
+ netdev->supported |= NETDEV_F_40GB_FD;
+ }
if (ecmd.supported & SUPPORTED_TP) {
netdev->supported |= NETDEV_F_COPPER;
}
if (ecmd.advertising & ADVERTISED_1000baseT_Half) {
netdev->advertised |= NETDEV_F_1GB_HD;
}
- if (ecmd.advertising & ADVERTISED_1000baseT_Full) {
+ if ((ecmd.advertising & ADVERTISED_1000baseT_Full) ||
+ (ecmd.advertising & ADVERTISED_1000baseKX_Full)) {
netdev->advertised |= NETDEV_F_1GB_FD;
}
- if (ecmd.advertising & ADVERTISED_10000baseT_Full) {
+ if ((ecmd.advertising & ADVERTISED_10000baseT_Full) ||
+ (ecmd.advertising & ADVERTISED_10000baseKX4_Full) ||
+ (ecmd.advertising & ADVERTISED_10000baseKR_Full) ||
+ (ecmd.advertising & ADVERTISED_10000baseR_FEC)) {
netdev->advertised |= NETDEV_F_10GB_FD;
}
+ if ((ecmd.advertising & ADVERTISED_40000baseKR4_Full) ||
+ (ecmd.advertising & ADVERTISED_40000baseCR4_Full) ||
+ (ecmd.advertising & ADVERTISED_40000baseSR4_Full) ||
+ (ecmd.advertising & ADVERTISED_40000baseLR4_Full)) {
+ netdev->advertised |= NETDEV_F_40GB_FD;
+ }
if (ecmd.advertising & ADVERTISED_TP) {
netdev->advertised |= NETDEV_F_COPPER;
}
}
/* Current settings. */
- speed = ecmd.speed;
+ speed = ethtool_cmd_speed(&ecmd);
if (speed == SPEED_10) {
netdev->current = ecmd.duplex ? NETDEV_F_10MB_FD : NETDEV_F_10MB_HD;
} else if (speed == SPEED_100) {
if (!error) {
error = netdev_linux_get_ipv4(netdev_, &netdev->netmask,
SIOCGIFNETMASK, "SIOCGIFNETMASK");
- if (!error) {
- netdev->cache_valid |= VALID_IN4;
- }
}
+ netdev->in4_error = error;
+ netdev->cache_valid |= VALID_IN4;
} else {
- error = 0;
+ error = netdev->in4_error;
}
if (!error) {
ovs_mutex_lock(&netdev->mutex);
error = do_set_addr(netdev_, SIOCSIFADDR, "SIOCSIFADDR", address);
if (!error) {
- netdev->cache_valid |= VALID_IN4;
netdev->address = address;
netdev->netmask = netmask;
if (address.s_addr != INADDR_ANY) {
"SIOCSIFNETMASK", netmask);
}
}
+
+ if (!error) {
+ netdev->cache_valid |= VALID_IN4;
+ netdev->in4_error = 0;
+ } else {
+ netdev->cache_valid &= ~VALID_IN4;
+ }
ovs_mutex_unlock(&netdev->mutex);
return error;
ifname);
}
-/* If 'netdev' has an assigned IPv6 address, sets '*in6' to that address (if
- * 'in6' is non-null) and returns true. Otherwise, returns false. */
+/* If 'netdev' has an assigned IPv6 address, sets '*in6' to that address.
+ * Otherwise, sets '*in6' to 'in6addr_any' and returns the corresponding
+ * error. */
static int
netdev_linux_get_in6(const struct netdev *netdev_, struct in6_addr *in6)
{
struct netdev_linux *netdev = netdev_linux_cast(netdev_);
+ int error;
ovs_mutex_lock(&netdev->mutex);
if (!(netdev->cache_valid & VALID_IN6)) {
char line[128];
netdev->in6 = in6addr_any;
+ netdev->in6_error = EADDRNOTAVAIL;
file = fopen("/proc/net/if_inet6", "r");
if (file != NULL) {
&& !strcmp(name, ifname))
{
netdev->in6 = in6_tmp;
+ netdev->in6_error = 0;
break;
}
}
fclose(file);
+ } else {
+ netdev->in6_error = EOPNOTSUPP;
}
netdev->cache_valid |= VALID_IN6;
}
*in6 = netdev->in6;
+ error = netdev->in6_error;
ovs_mutex_unlock(&netdev->mutex);
- return 0;
+ return error;
}
static void
* ENXIO indicates that there is not ARP table entry for 'ip' on 'netdev'. */
static int
netdev_linux_arp_lookup(const struct netdev *netdev,
- ovs_be32 ip, uint8_t mac[ETH_ADDR_LEN])
+ ovs_be32 ip, struct eth_addr *mac)
{
struct arpreq r;
struct sockaddr_in sin;
}
COVERAGE_INC(netdev_set_ethtool);
- evalue.data = new_flags = (evalue.data & ~flag) | (enable ? flag : 0);
+ new_flags = (evalue.data & ~flag) | (enable ? flag : 0);
+ if (new_flags == evalue.data) {
+ return 0;
+ }
+ evalue.data = new_flags;
error = netdev_linux_do_ethtool(netdev_name,
(struct ethtool_cmd *)&evalue,
ETHTOOL_SFLAGS, "ETHTOOL_SFLAGS");
}
static int
-get_etheraddr(const char *netdev_name, uint8_t ea[ETH_ADDR_LEN])
+get_etheraddr(const char *netdev_name, struct eth_addr *ea)
{
struct ifreq ifr;
int hwaddr_family;
}
hwaddr_family = ifr.ifr_hwaddr.sa_family;
if (hwaddr_family != AF_UNSPEC && hwaddr_family != ARPHRD_ETHER) {
- VLOG_WARN("%s device has unknown hardware address family %d",
+ VLOG_INFO("%s device has unknown hardware address family %d",
netdev_name, hwaddr_family);
+ return EINVAL;
}
memcpy(ea, ifr.ifr_hwaddr.sa_data, ETH_ADDR_LEN);
return 0;
}
static int
-set_etheraddr(const char *netdev_name,
- const uint8_t mac[ETH_ADDR_LEN])
+set_etheraddr(const char *netdev_name, const struct eth_addr mac)
{
struct ifreq ifr;
int error;
memset(&ifr, 0, sizeof ifr);
ovs_strzcpy(ifr.ifr_name, netdev_name, sizeof ifr.ifr_name);
ifr.ifr_hwaddr.sa_family = ARPHRD_ETHER;
- memcpy(ifr.ifr_hwaddr.sa_data, mac, ETH_ADDR_LEN);
+ memcpy(ifr.ifr_hwaddr.sa_data, &mac, ETH_ADDR_LEN);
COVERAGE_INC(netdev_set_hwaddr);
error = af_inet_ioctl(SIOCSIFHWADDR, &ifr);
if (error) {