netdev-linux: Cache error code from ether-addr ioctl.
authorPravin B Shelar <pshelar@nicira.com>
Fri, 9 Mar 2012 21:32:50 +0000 (13:32 -0800)
committerPravin B Shelar <pshelar@nicira.com>
Fri, 9 Mar 2012 21:32:50 +0000 (13:32 -0800)
Signed-off-by: Pravin B Shelar <pshelar@nicira.com>
lib/netdev-linux.c
lib/rtnetlink-link.c
lib/rtnetlink-link.h

index f8f9c7e..b9b0379 100644 (file)
@@ -377,6 +377,7 @@ struct netdev_dev_linux {
     int vport_stats_error;      /* Cached error code from vport_get_stats().
                                    0 or an errno value. */
     int netdev_mtu_error;       /* Cached error code from SIOCGIFMTU or SIOCSIFMTU. */
+    int ether_addr_error;       /* Cached error code from set/get etheraddr. */
 
     struct ethtool_drvinfo drvinfo;  /* Cached from ETHTOOL_GDRVINFO. */
     struct tc *tc;
@@ -417,8 +418,7 @@ 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, int hwaddr_family,
-                         const uint8_t[ETH_ADDR_LEN]);
+static int set_etheraddr(const char *netdev_name, 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 af_packet_sock(void);
@@ -541,6 +541,12 @@ netdev_dev_linux_update(struct netdev_dev_linux *dev,
             dev->netdev_mtu_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;
+        }
+
     } else {
         netdev_dev_linux_changed(dev, change->ifi_flags, 0);
     }
@@ -1008,37 +1014,49 @@ netdev_linux_set_etheraddr(struct netdev *netdev_,
                                 netdev_dev_linux_cast(netdev_get_dev(netdev_));
     int error;
 
-    if (!(netdev_dev->cache_valid & VALID_ETHERADDR)
-        || !eth_addr_equals(netdev_dev->etheraddr, mac)) {
-        error = set_etheraddr(netdev_get_name(netdev_), ARPHRD_ETHER, mac);
+    if (netdev_dev->cache_valid & VALID_ETHERADDR) {
+        if (netdev_dev->ether_addr_error) {
+            return netdev_dev->ether_addr_error;
+        }
+        if (eth_addr_equals(netdev_dev->etheraddr, mac)) {
+            return 0;
+        }
+        netdev_dev->cache_valid &= ~VALID_ETHERADDR;
+    }
+
+    error = set_etheraddr(netdev_get_name(netdev_), mac);
+    if (!error || error == ENODEV) {
+        netdev_dev->ether_addr_error = error;
+        netdev_dev->cache_valid |= VALID_ETHERADDR;
         if (!error) {
-            netdev_dev->cache_valid |= VALID_ETHERADDR;
             memcpy(netdev_dev->etheraddr, mac, ETH_ADDR_LEN);
         }
-    } else {
-        error = 0;
     }
+
     return error;
 }
 
-/* Returns a pointer to 'netdev''s MAC address.  The caller must not modify or
- * free the returned buffer. */
+/* 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])
 {
     struct netdev_dev_linux *netdev_dev =
                                 netdev_dev_linux_cast(netdev_get_dev(netdev_));
+
     if (!(netdev_dev->cache_valid & VALID_ETHERADDR)) {
         int error = get_etheraddr(netdev_get_name(netdev_),
                                   netdev_dev->etheraddr);
-        if (error) {
-            return error;
-        }
+
+        netdev_dev->ether_addr_error = error;
         netdev_dev->cache_valid |= VALID_ETHERADDR;
     }
-    memcpy(mac, netdev_dev->etheraddr, ETH_ADDR_LEN);
-    return 0;
+
+    if (!netdev_dev->ether_addr_error) {
+        memcpy(mac, netdev_dev->etheraddr, ETH_ADDR_LEN);
+    }
+
+    return netdev_dev->ether_addr_error;
 }
 
 /* Returns the maximum size of transmitted (and received) packets on 'netdev',
@@ -4374,14 +4392,14 @@ get_etheraddr(const char *netdev_name, uint8_t ea[ETH_ADDR_LEN])
 }
 
 static int
-set_etheraddr(const char *netdev_name, int hwaddr_family,
+set_etheraddr(const char *netdev_name,
               const uint8_t mac[ETH_ADDR_LEN])
 {
     struct ifreq ifr;
 
     memset(&ifr, 0, sizeof ifr);
     ovs_strzcpy(ifr.ifr_name, netdev_name, sizeof ifr.ifr_name);
-    ifr.ifr_hwaddr.sa_family = hwaddr_family;
+    ifr.ifr_hwaddr.sa_family = ARPHRD_ETHER;
     memcpy(ifr.ifr_hwaddr.sa_data, mac, ETH_ADDR_LEN);
     COVERAGE_INC(netdev_set_hwaddr);
     if (ioctl(af_inet_sock, SIOCSIFHWADDR, &ifr) < 0) {
index d14f0e3..07350a6 100644 (file)
@@ -46,6 +46,7 @@ rtnetlink_link_parse(struct ofpbuf *buf,
         [IFLA_IFNAME] = { .type = NL_A_STRING, .optional = false },
         [IFLA_MASTER] = { .type = NL_A_U32,    .optional = true },
         [IFLA_MTU]    = { .type = NL_A_U32,    .optional = true },
+        [IFLA_ADDRESS] = { .type = NL_A_UNSPEC, .optional = true },
     };
 
     static struct nlattr *attrs[ARRAY_SIZE(policy)];
@@ -72,6 +73,12 @@ rtnetlink_link_parse(struct ofpbuf *buf,
                                   ? nl_attr_get_u32(attrs[IFLA_MTU])
                                   : 0);
 
+        if (attrs[IFLA_ADDRESS] &&
+            nl_attr_get_size(attrs[IFLA_ADDRESS]) == ETH_ALEN) {
+            memcpy(change->addr, nl_attr_get(attrs[IFLA_ADDRESS]), ETH_ALEN);
+        } else {
+            memset(change->addr, 0, ETH_ALEN);
+        }
     }
 
     return parsed;
index cfdb24b..7c64c94 100644 (file)
@@ -18,6 +18,8 @@
 #define RTNETLINK_LINK_H 1
 
 #include <stdbool.h>
+#include <stdint.h>
+#include <linux/if_ether.h>
 
 struct ofpbuf;
 struct nln_notifier;
@@ -38,6 +40,7 @@ struct rtnetlink_link_change {
     const char *ifname;         /* Name of network device. */
     int master_ifindex;         /* Ifindex of datapath master (0 if none). */
     int mtu;                    /* Current MTU. */
+    uint8_t addr[ETH_ALEN];
     unsigned int ifi_flags;     /* Flags of network device. */
 };