rtnetlink: Extend rtnetlink to support RTNLGRP_IPV4_IFADDR and
authorAlex Wang <alexw@nicira.com>
Fri, 24 Jul 2015 21:03:06 +0000 (14:03 -0700)
committerAlex Wang <alexw@nicira.com>
Tue, 28 Jul 2015 16:34:31 +0000 (09:34 -0700)
RTNLGRP_IPV6_IFADDR.

This commit renames the rtnetlink-link.{c,h} to rtnetlink.{c,h}
and extends the module to support RTNLGRP_IPV4_IFADDR and
RTNLGRP_IPV4_IFADDR multicast groups.  A later patch will start
using this module to react to interface address changes.

Signed-off-by: Alex Wang <alexw@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
lib/automake.mk
lib/netdev-linux.c
lib/route-table.c
lib/rtnetlink-link.c [deleted file]
lib/rtnetlink-link.h [deleted file]
lib/rtnetlink.c [new file with mode: 0644]
lib/rtnetlink.h [new file with mode: 0644]
lib/vlandev.c

index bd23ef7..f048174 100644 (file)
@@ -343,8 +343,8 @@ lib_libopenvswitch_la_SOURCES += \
        lib/netlink-socket.h \
        lib/ovs-numa.c \
        lib/ovs-numa.h \
-       lib/rtnetlink-link.c \
-       lib/rtnetlink-link.h \
+       lib/rtnetlink.c \
+       lib/rtnetlink.h \
        lib/route-table.c \
        lib/route-table.h
 endif
index 0656f36..9213d8b 100644 (file)
@@ -66,7 +66,7 @@
 #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"
@@ -541,7 +541,7 @@ netdev_rxq_linux_cast(const struct netdev_rxq *rx)
 }
 \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)
@@ -601,9 +601,9 @@ netdev_linux_run(void)
         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_);
@@ -674,7 +674,7 @@ netdev_linux_changed(struct netdev_linux *dev,
 
 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) {
@@ -694,10 +694,9 @@ netdev_linux_update(struct netdev_linux *dev,
             dev->ether_addr_error = 0;
         }
 
-        dev->ifindex = change->ifi_index;
+        dev->ifindex = change->if_index;
         dev->cache_valid |= VALID_IFINDEX;
         dev->get_ifindex_error = 0;
-
     } else {
         netdev_linux_changed(dev, change->ifi_flags, 0);
     }
index a5a42ca..7d1837c 100644 (file)
@@ -30,7 +30,7 @@
 #include "netlink-socket.h"
 #include "ofpbuf.h"
 #include "ovs-router.h"
-#include "rtnetlink-link.h"
+#include "rtnetlink.h"
 #include "openvswitch/vlog.h"
 
 VLOG_DEFINE_THIS_MODULE(route_table);
@@ -74,7 +74,7 @@ static void route_table_change(const struct route_table_msg *, void *);
 static void route_map_clear(void);
 
 static void name_table_init(void);
-static void name_table_change(const struct rtnetlink_link_change *, void *);
+static void name_table_change(const struct rtnetlink_change *, void *);
 
 uint64_t
 route_table_get_change_seq(void)
@@ -113,7 +113,7 @@ route_table_run(void)
 {
     ovs_mutex_lock(&route_table_mutex);
     if (nln) {
-        rtnetlink_link_run();
+        rtnetlink_run();
         nln_run(nln);
 
         if (!route_table_valid) {
@@ -130,7 +130,7 @@ route_table_wait(void)
 {
     ovs_mutex_lock(&route_table_mutex);
     if (nln) {
-        rtnetlink_link_wait();
+        rtnetlink_wait();
         nln_wait(nln);
     }
     ovs_mutex_unlock(&route_table_mutex);
@@ -278,12 +278,12 @@ route_table_fallback_lookup(ovs_be32 ip_dst OVS_UNUSED,
 static void
 name_table_init(void)
 {
-    name_notifier = rtnetlink_link_notifier_create(name_table_change, NULL);
+    name_notifier = rtnetlink_notifier_create(name_table_change, NULL);
 }
 
 
 static void
-name_table_change(const struct rtnetlink_link_change *change OVS_UNUSED,
+name_table_change(const struct rtnetlink_change *change OVS_UNUSED,
                   void *aux OVS_UNUSED)
 {
     /* Changes to interface status can cause routing table changes that some
diff --git a/lib/rtnetlink-link.c b/lib/rtnetlink-link.c
deleted file mode 100644 (file)
index 308338f..0000000
+++ /dev/null
@@ -1,138 +0,0 @@
-/*
- * Copyright (c) 2009, 2010, 2013 Nicira, Inc.
- *
- * 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.
- */
-
-#include <config.h>
-
-#include "rtnetlink-link.h"
-
-#include <sys/socket.h>
-#include <linux/rtnetlink.h>
-#include <net/if.h>
-
-#include "netlink.h"
-#include "netlink-notifier.h"
-#include "ofpbuf.h"
-
-static struct nln *nln = NULL;
-static struct rtnetlink_link_change rtn_change;
-
-/* Parses a rtnetlink message 'buf' into 'change'.  If 'buf' is unparseable,
- * leaves 'change' untouched and returns false.  Otherwise, populates 'change'
- * and returns true. */
-bool
-rtnetlink_link_parse(struct ofpbuf *buf,
-                     struct rtnetlink_link_change *change)
-{
-    bool parsed;
-
-    /* Policy for RTNLGRP_LINK messages.
-     *
-     * There are *many* more fields in these messages, but currently we
-     * only care about these fields. */
-    static const struct nl_policy policy[] = {
-        [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 },
-    };
-
-    struct nlattr *attrs[ARRAY_SIZE(policy)];
-
-    parsed = nl_policy_parse(buf, NLMSG_HDRLEN + sizeof(struct ifinfomsg),
-                             policy, attrs, ARRAY_SIZE(policy));
-
-    if (parsed) {
-        const struct nlmsghdr *nlmsg;
-        const struct ifinfomsg *ifinfo;
-
-        nlmsg  = buf->data;
-        ifinfo = ofpbuf_at(buf, NLMSG_HDRLEN, sizeof *ifinfo);
-
-        change->nlmsg_type     = nlmsg->nlmsg_type;
-        change->ifi_index      = ifinfo->ifi_index;
-        change->ifname         = nl_attr_get_string(attrs[IFLA_IFNAME]);
-        change->ifi_flags      = ifinfo->ifi_flags;
-        change->master_ifindex = (attrs[IFLA_MASTER]
-                                  ? nl_attr_get_u32(attrs[IFLA_MASTER])
-                                  : 0);
-        change->mtu            = (attrs[IFLA_MTU]
-                                  ? 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;
-}
-
-static bool
-rtnetlink_link_parse_cb(struct ofpbuf *buf, void *change)
-{
-    return rtnetlink_link_parse(buf, change);
-}
-
-/* Registers 'cb' to be called with auxiliary data 'aux' with network device
- * change notifications.  The notifier is stored in 'notifier', which the
- * caller must not modify or free.
- *
- * This is probably not the function that you want.  You should probably be
- * using dpif_port_poll() or netdev_change_seq(), which unlike this function
- * are not Linux-specific.
- *
- * Returns an initialized nln_notifier if successful, NULL otherwise. */
-struct nln_notifier *
-rtnetlink_link_notifier_create(rtnetlink_link_notify_func *cb, void *aux)
-{
-    if (!nln) {
-        nln = nln_create(NETLINK_ROUTE, RTNLGRP_LINK, rtnetlink_link_parse_cb,
-                         &rtn_change);
-    }
-
-    return nln_notifier_create(nln, (nln_notify_func *) cb, aux);
-}
-
-/* Destroys 'notifier', which must have previously been created with
- * rtnetlink_link_notifier_register(). */
-void
-rtnetlink_link_notifier_destroy(struct nln_notifier *notifier)
-{
-    nln_notifier_destroy(notifier);
-}
-
-/* Calls all of the registered notifiers, passing along any as-yet-unreported
- * netdev change events. */
-void
-rtnetlink_link_run(void)
-{
-    if (nln) {
-        nln_run(nln);
-    }
-}
-
-/* Causes poll_block() to wake up when network device change notifications are
- * ready. */
-void
-rtnetlink_link_wait(void)
-{
-    if (nln) {
-        nln_wait(nln);
-    }
-}
diff --git a/lib/rtnetlink-link.h b/lib/rtnetlink-link.h
deleted file mode 100644 (file)
index d0d9a75..0000000
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (c) 2009 Nicira, Inc.
- *
- * 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.
- */
-
-#ifndef RTNETLINK_LINK_H
-#define RTNETLINK_LINK_H 1
-
-#include <stdbool.h>
-#include <stdint.h>
-#include <linux/if_ether.h>
-
-struct ofpbuf;
-struct nln_notifier;
-
-/* These functions are Linux specific, so they should be used directly only by
- * Linux-specific code. */
-
-/* A digested version of an rtnetlink_link message sent down by the kernel to
- * indicate that a network device has been created, destroyed or changed.  */
-struct rtnetlink_link_change {
-    /* Copied from struct nlmsghdr. */
-    int nlmsg_type;             /* e.g. RTM_NEWLINK, RTM_DELLINK. */
-
-    /* Copied from struct ifinfomsg. */
-    int ifi_index;              /* Index of network device. */
-
-    /* Extracted from Netlink attributes. */
-    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. */
-};
-
-/* Function called to report that a netdev has changed.  'change' describes the
- * specific change.  It may be null if the buffer of change information
- * overflowed, in which case the function must assume that every device may
- * have changed.  'aux' is as specified in the call to
- * rtnetlink_link_notifier_register().  */
-typedef
-void rtnetlink_link_notify_func(const struct rtnetlink_link_change *change,
-                                void *aux);
-
-bool rtnetlink_link_parse(struct ofpbuf *buf,
-                          struct rtnetlink_link_change *change);
-struct nln_notifier *
-rtnetlink_link_notifier_create(rtnetlink_link_notify_func *, void *aux);
-void rtnetlink_link_notifier_destroy(struct nln_notifier *);
-void rtnetlink_link_run(void);
-void rtnetlink_link_wait(void);
-#endif /* rtnetlink-link.h */
diff --git a/lib/rtnetlink.c b/lib/rtnetlink.c
new file mode 100644 (file)
index 0000000..1ecd23b
--- /dev/null
@@ -0,0 +1,179 @@
+/*
+ * Copyright (c) 2009, 2010, 2013, 2015 Nicira, Inc.
+ *
+ * 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.
+ */
+
+#include <config.h>
+
+#include "rtnetlink.h"
+
+#include <sys/socket.h>
+#include <linux/rtnetlink.h>
+#include <net/if.h>
+
+#include "netlink.h"
+#include "netlink-notifier.h"
+#include "ofpbuf.h"
+
+static struct nln *nln = NULL;
+static struct rtnetlink_change rtn_change;
+
+/* Returns true if the given netlink msg type corresponds to RTNLGRP_LINK. */
+bool
+rtnetlink_type_is_rtnlgrp_link(uint16_t type)
+{
+    return type == RTM_NEWLINK || type == RTM_DELLINK;
+}
+
+/* Returns true if the given netlink msg type corresponds to
+ * RTNLGRP_IPV4_IFADDR or RTNLGRP_IPV6_IFADDR. */
+bool
+rtnetlink_type_is_rtnlgrp_addr(uint16_t type)
+{
+    return type == RTM_NEWADDR || type == RTM_DELADDR;
+}
+
+/* Parses a rtnetlink message 'buf' into 'change'.  If 'buf' is unparseable,
+ * leaves 'change' untouched and returns false.  Otherwise, populates 'change'
+ * and returns true. */
+bool
+rtnetlink_parse(struct ofpbuf *buf, struct rtnetlink_change *change)
+{
+    const struct nlmsghdr *nlmsg = buf->data;
+    bool parsed = false;
+
+    if (rtnetlink_type_is_rtnlgrp_link(nlmsg->nlmsg_type)) {
+        /* Policy for RTNLGRP_LINK messages.
+         *
+         * There are *many* more fields in these messages, but currently we
+         * only care about these fields. */
+        static const struct nl_policy policy[] = {
+            [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 },
+        };
+
+        struct nlattr *attrs[ARRAY_SIZE(policy)];
+
+        parsed = nl_policy_parse(buf, NLMSG_HDRLEN + sizeof(struct ifinfomsg),
+                                 policy, attrs, ARRAY_SIZE(policy));
+
+        if (parsed) {
+            const struct ifinfomsg *ifinfo;
+
+            ifinfo = ofpbuf_at(buf, NLMSG_HDRLEN, sizeof *ifinfo);
+
+            change->nlmsg_type     = nlmsg->nlmsg_type;
+            change->if_index       = ifinfo->ifi_index;
+            change->ifname         = nl_attr_get_string(attrs[IFLA_IFNAME]);
+            change->ifi_flags      = ifinfo->ifi_flags;
+            change->master_ifindex = (attrs[IFLA_MASTER]
+                                      ? nl_attr_get_u32(attrs[IFLA_MASTER])
+                                      : 0);
+            change->mtu            = (attrs[IFLA_MTU]
+                                      ? 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);
+            }
+        }
+    } else if (rtnetlink_type_is_rtnlgrp_addr(nlmsg->nlmsg_type)) {
+        /* Policy for RTNLGRP_IPV4_IFADDR/RTNLGRP_IPV6_IFADDR messages.
+         *
+         * There are *many* more fields in these messages, but currently we
+         * only care about these fields. */
+        static const struct nl_policy policy[] = {
+            [IFA_LABEL] = { .type = NL_A_STRING, .optional = false },
+        };
+
+        struct nlattr *attrs[ARRAY_SIZE(policy)];
+
+        parsed = nl_policy_parse(buf, NLMSG_HDRLEN + sizeof(struct ifaddrmsg),
+                                 policy, attrs, ARRAY_SIZE(policy));
+
+        if (parsed) {
+            const struct ifaddrmsg *ifaddr;
+
+            ifaddr = ofpbuf_at(buf, NLMSG_HDRLEN, sizeof *ifaddr);
+
+            change->nlmsg_type     = nlmsg->nlmsg_type;
+            change->if_index       = ifaddr->ifa_index;
+            change->ifname         = nl_attr_get_string(attrs[IFA_LABEL]);
+        }
+    }
+
+    return parsed;
+}
+
+static bool
+rtnetlink_parse_cb(struct ofpbuf *buf, void *change)
+{
+    return rtnetlink_parse(buf, change);
+}
+
+/* Registers 'cb' to be called with auxiliary data 'aux' with network device
+ * change notifications.  The notifier is stored in 'notifier', which the
+ * caller must not modify or free.
+ *
+ * This is probably not the function that you want.  You should probably be
+ * using dpif_port_poll() or netdev_change_seq(), which unlike this function
+ * are not Linux-specific.
+ *
+ * xxx Joins more multicast groups when needed.
+ *
+ * Returns an initialized nln_notifier if successful, NULL otherwise. */
+struct nln_notifier *
+rtnetlink_notifier_create(rtnetlink_notify_func *cb, void *aux)
+{
+    if (!nln) {
+        nln = nln_create(NETLINK_ROUTE, RTNLGRP_LINK, rtnetlink_parse_cb,
+                         &rtn_change);
+    }
+
+    return nln_notifier_create(nln, (nln_notify_func *) cb, aux);
+}
+
+/* Destroys 'notifier', which must have previously been created with
+ * rtnetlink_notifier_register(). */
+void
+rtnetlink_notifier_destroy(struct nln_notifier *notifier)
+{
+    nln_notifier_destroy(notifier);
+}
+
+/* Calls all of the registered notifiers, passing along any as-yet-unreported
+ * netdev change events. */
+void
+rtnetlink_run(void)
+{
+    if (nln) {
+        nln_run(nln);
+    }
+}
+
+/* Causes poll_block() to wake up when network device change notifications are
+ * ready. */
+void
+rtnetlink_wait(void)
+{
+    if (nln) {
+        nln_wait(nln);
+    }
+}
diff --git a/lib/rtnetlink.h b/lib/rtnetlink.h
new file mode 100644 (file)
index 0000000..f2da394
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2009, 2015 Nicira, Inc.
+ *
+ * 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.
+ */
+
+#ifndef RTNETLINK_LINK_H
+#define RTNETLINK_LINK_H 1
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <linux/if_ether.h>
+
+struct ofpbuf;
+struct nln_notifier;
+
+/* These functions are Linux specific, so they should be used directly only by
+ * Linux-specific code. */
+
+/* A digested version of an rtnetlink_link message sent down by the kernel to
+ * indicate that a network device's status (link or address) has been changed.
+ */
+struct rtnetlink_change {
+    /* Copied from struct nlmsghdr. */
+    int nlmsg_type;             /* e.g. RTM_NEWLINK, RTM_DELLINK. */
+
+    /* Common attributes. */
+    int if_index;               /* Index of network device. */
+    const char *ifname;         /* Name of network device. */
+
+    /* Network device link status. */
+    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. */
+
+    /* Network device address status. */
+    /* xxx To be added when needed. */
+};
+
+/* Function called to report that a netdev has changed.  'change' describes the
+ * specific change.  It may be null if the buffer of change information
+ * overflowed, in which case the function must assume that every device may
+ * have changed.  'aux' is as specified in the call to
+ * rtnetlink_notifier_register().  */
+typedef
+void rtnetlink_notify_func(const struct rtnetlink_change *change,
+                           void *aux);
+
+bool rtnetlink_type_is_rtnlgrp_link(uint16_t type);
+bool rtnetlink_type_is_rtnlgrp_addr(uint16_t type);
+bool rtnetlink_parse(struct ofpbuf *buf, struct rtnetlink_change *change);
+struct nln_notifier *
+rtnetlink_notifier_create(rtnetlink_notify_func *, void *aux);
+void rtnetlink_notifier_destroy(struct nln_notifier *);
+void rtnetlink_run(void);
+void rtnetlink_wait(void);
+#endif /* rtnetlink.h */
index 99c99de..d2a3191 100644 (file)
@@ -162,7 +162,7 @@ vlandev_get_name(const char *real_dev_name, int vid)
 /* The Linux vlandev implementation. */
 
 #ifdef __linux__
-#include "rtnetlink-link.h"
+#include "rtnetlink.h"
 #include <linux/if_vlan.h>
 #include <linux/sockios.h>
 #include "netdev-linux.h"
@@ -171,7 +171,7 @@ static struct nln_notifier *vlan_cache_notifier;
 static bool cache_valid;
 
 static void
-vlan_cache_cb(const struct rtnetlink_link_change *change OVS_UNUSED,
+vlan_cache_cb(const struct rtnetlink_change *change OVS_UNUSED,
               void *aux OVS_UNUSED)
 {
     cache_valid = false;
@@ -185,8 +185,8 @@ vlandev_linux_refresh(void)
     FILE *stream;
 
     if (!vlan_cache_notifier) {
-        vlan_cache_notifier = rtnetlink_link_notifier_create(vlan_cache_cb,
-                                                             NULL);
+        vlan_cache_notifier = rtnetlink_notifier_create(vlan_cache_cb,
+                                                        NULL);
         if (!vlan_cache_notifier) {
             return EINVAL;
         }