lib: Show tunnel egress interface in ovsdb
authorEthan Jackson <ethan@nicira.com>
Wed, 22 Dec 2010 00:26:21 +0000 (16:26 -0800)
committerEthan Jackson <ethan@nicira.com>
Tue, 4 Jan 2011 20:35:59 +0000 (12:35 -0800)
This commit parses rtnetlink address notifications from the
kernel in order to display the egress interface of tunnels in the
database.

Bug #4103.

14 files changed:
lib/automake.mk
lib/netdev-dummy.c
lib/netdev-linux.c
lib/netdev-provider.h
lib/netdev-vport.c
lib/netdev.c
lib/netdev.h
lib/rtnetlink-link.c
lib/rtnetlink-link.h
lib/rtnetlink-route.c [new file with mode: 0644]
lib/rtnetlink-route.h [new file with mode: 0644]
vswitchd/bridge.c
vswitchd/vswitch.ovsschema
vswitchd/vswitch.xml

index 4c6a87720e9becc9b0eee2a6961e41d3eec83f5c..f5d23a23b116d8328b9cded62c028557eb93b39d 100644 (file)
@@ -191,7 +191,9 @@ lib_libopenvswitch_a_SOURCES += \
        lib/rtnetlink.c \
        lib/rtnetlink.h \
        lib/rtnetlink-link.c \
-       lib/rtnetlink-link.h
+       lib/rtnetlink-link.h \
+       lib/rtnetlink-route.c \
+       lib/rtnetlink-route.h
 endif
 
 if HAVE_OPENSSL
index ddcbe3633b59a74fa1a7acc6a02c7c77c96edc62..218a022883eceb2ec4f3774e6e7677a9cb487648 100644 (file)
@@ -320,6 +320,7 @@ static const struct netdev_class dummy_class = {
     NULL,                       /* get_in6 */
     NULL,                       /* add_router */
     NULL,                       /* get_next_hop */
+    NULL,                       /* get_tnl_iface */
     NULL,                       /* arp_lookup */
 
     netdev_dummy_update_flags,
index 7f9a8e32fd9bc50ccb7e721b48055fd6f264b1f9..5654dd464fe5bfa1c543434c27b93978ab82e7fb 100644 (file)
@@ -2176,6 +2176,7 @@ netdev_linux_poll_remove(struct netdev_notifier *notifier_)
     netdev_linux_get_in6,                                       \
     netdev_linux_add_router,                                    \
     netdev_linux_get_next_hop,                                  \
+    NULL,                       /* get_tnl_iface */             \
     netdev_linux_arp_lookup,                                    \
                                                                 \
     netdev_linux_update_flags,                                  \
index d955bb17af59ff26050c973c9348f06cd8e77112..038f277f4fc79a8ee3c3df2f878c12568d3cdee3 100644 (file)
@@ -518,6 +518,17 @@ struct netdev_class {
     int (*get_next_hop)(const struct in_addr *host, struct in_addr *next_hop,
                         char **netdev_name);
 
+    /* Looks up the name of the interface out of which traffic will egress if
+     * 'netdev' is a tunnel.  If unsuccessful, or 'netdev' is not a tunnel,
+     * will return null.  This function does not necessarily return the
+     * physical interface out which traffic will egress.  Instead it returns
+     * the interface which is assigned 'netdev's remote_ip.  This may be an
+     * internal interface such as a bridge port.
+     *
+     * This function may be set to null if 'netdev' is not a tunnel or it is
+     * not supported. */
+    const char *(*get_tnl_iface)(const struct netdev *netdev);
+
     /* Looks up the ARP table entry for 'ip' on 'netdev' and stores the
      * corresponding MAC address in 'mac'.  A return value of ENXIO, in
      * particular, indicates that there is no ARP table entry for 'ip' on
index 11db0999deae071868c55b799fdf8c403a3c7ace..f3985e9d23184b78ae97f62d33459257dbf529e0 100644 (file)
 
 #include <errno.h>
 #include <fcntl.h>
+#include <sys/socket.h>
+#include <linux/rtnetlink.h>
 #include <net/if.h>
 #include <sys/ioctl.h>
 
 #include "byte-order.h"
+#include "hash.h"
+#include "hmap.h"
 #include "list.h"
 #include "netdev-provider.h"
+#include "netlink.h"
+#include "netlink-socket.h"
+#include "ofpbuf.h"
 #include "openvswitch/datapath-protocol.h"
 #include "openvswitch/tunnel.h"
 #include "packets.h"
+#include "rtnetlink.h"
+#include "rtnetlink-route.h"
+#include "rtnetlink-link.h"
 #include "shash.h"
 #include "socket-util.h"
 #include "vlog.h"
 
 VLOG_DEFINE_THIS_MODULE(netdev_vport);
 
+static struct hmap name_map;
+static struct hmap route_map;
+static struct rtnetlink_notifier netdev_vport_link_notifier;
+static struct rtnetlink_notifier netdev_vport_route_notifier;
+
+struct route_node {
+    struct hmap_node node;      /* Node in route_map. */
+    int rta_oif;                /* Egress interface index. */
+    uint32_t rta_dst;           /* Destination address in host byte order. */
+    unsigned char rtm_dst_len;  /* Destination address length. */
+};
+
+struct name_node {
+    struct hmap_node node; /* Node in name_map. */
+    uint32_t ifi_index;    /* Kernel interface index. */
+
+    char ifname[IFNAMSIZ]; /* Interface name. */
+};
+
 struct netdev_vport_notifier {
     struct netdev_notifier notifier;
     struct list list_node;
@@ -66,6 +95,12 @@ static int netdev_vport_create(const struct netdev_class *, const char *,
                                const struct shash *, struct netdev_dev **);
 static void netdev_vport_poll_notify(const struct netdev *);
 
+static void netdev_vport_tnl_iface_init(void);
+static void netdev_vport_route_change(const struct rtnetlink_route_change *,
+                                      void *);
+static void netdev_vport_link_change(const struct rtnetlink_link_change *,
+                                     void *);
+
 static bool
 is_vport_class(const struct netdev_class *class)
 {
@@ -107,6 +142,13 @@ netdev_vport_get_config(const struct netdev *netdev, void *config)
     }
 }
 
+static int
+netdev_vport_init(void)
+{
+    netdev_vport_tnl_iface_init();
+    return 0;
+}
+
 static int
 netdev_vport_create(const struct netdev_class *netdev_class, const char *name,
                     const struct shash *args,
@@ -387,6 +429,284 @@ netdev_vport_poll_remove(struct netdev_notifier *notifier_)
 
     free(notifier);
 }
+
+static void
+netdev_vport_run(void)
+{
+    rtnetlink_link_notifier_run();
+    rtnetlink_route_notifier_run();
+}
+
+static void
+netdev_vport_wait(void)
+{
+    rtnetlink_link_notifier_wait();
+    rtnetlink_route_notifier_wait();
+}
+\f
+/* get_tnl_iface() implementation. */
+
+static struct name_node *
+name_node_lookup(int ifi_index)
+{
+    struct name_node *nn;
+
+    HMAP_FOR_EACH_WITH_HASH(nn, node, hash_int(ifi_index, 0), &name_map) {
+        if (nn->ifi_index == ifi_index) {
+            return nn;
+        }
+    }
+
+    return NULL;
+}
+
+static struct route_node *
+route_node_lookup(int rta_oif, uint32_t rta_dst, unsigned char rtm_dst_len)
+{
+    uint32_t hash;
+    struct route_node *rn;
+
+    hash = hash_3words(rta_oif, rta_dst, rtm_dst_len);
+    HMAP_FOR_EACH_WITH_HASH(rn, node, hash, &route_map) {
+        if (rn->rta_oif     == rn->rta_oif &&
+            rn->rta_dst     == rn->rta_dst &&
+            rn->rtm_dst_len == rn->rtm_dst_len) {
+            return rn;
+        }
+    }
+
+    return NULL;
+}
+
+/* Resets the name or route map depending on the value of 'is_name'.  Clears
+ * the appropriate map, makes an rtnetlink dump request, and calls the change
+ * callback for each reply from the kernel. One should probably use
+ * netdev_vport_reset_routes or netdev_vport_reset_names instead. */
+static int
+netdev_vport_reset_name_else_route(bool is_name)
+{
+    int error;
+    int nlmsg_type;
+    struct nl_dump dump;
+    struct rtgenmsg *rtmsg;
+    struct ofpbuf request, reply;
+    static struct nl_sock *rtnl_sock;
+
+    if (is_name) {
+        struct name_node *nn, *nn_next;
+
+        HMAP_FOR_EACH_SAFE(nn, nn_next, node, &name_map) {
+            hmap_remove(&name_map, &nn->node);
+            free(nn);
+        }
+    } else {
+        struct route_node *rn, *rn_next;
+
+        HMAP_FOR_EACH_SAFE(rn, rn_next, node, &route_map) {
+            hmap_remove(&route_map, &rn->node);
+            free(rn);
+        }
+    }
+
+    error = nl_sock_create(NETLINK_ROUTE, 0, 0, 0, &rtnl_sock);
+    if (error) {
+        VLOG_WARN_RL(&rl, "Failed to create NETLINK_ROUTE socket");
+        return error;
+    }
+
+    ofpbuf_init(&request, 0);
+
+    nlmsg_type = is_name ? RTM_GETLINK : RTM_GETROUTE;
+    nl_msg_put_nlmsghdr(&request, sizeof *rtmsg, nlmsg_type, NLM_F_REQUEST);
+
+    rtmsg = ofpbuf_put_zeros(&request, sizeof *rtmsg);
+    rtmsg->rtgen_family = AF_INET;
+
+    nl_dump_start(&dump, rtnl_sock, &request);
+
+    while (nl_dump_next(&dump, &reply)) {
+        if (is_name) {
+            struct rtnetlink_link_change change;
+
+            if (rtnetlink_link_parse(&reply, &change)) {
+                netdev_vport_link_change(&change, NULL);
+            }
+        } else {
+            struct rtnetlink_route_change change;
+
+            if (rtnetlink_route_parse(&reply, &change)) {
+                netdev_vport_route_change(&change, NULL);
+            }
+        }
+    }
+
+    error = nl_dump_done(&dump);
+    nl_sock_destroy(rtnl_sock);
+
+    return error;
+}
+
+static int
+netdev_vport_reset_routes(void)
+{
+    return netdev_vport_reset_name_else_route(false);
+}
+
+static int
+netdev_vport_reset_names(void)
+{
+    return netdev_vport_reset_name_else_route(true);
+}
+
+static void
+netdev_vport_route_change(const struct rtnetlink_route_change *change,
+                         void *aux OVS_UNUSED)
+{
+
+    if (!change) {
+        netdev_vport_reset_routes();
+    } else if (change->nlmsg_type == RTM_NEWROUTE) {
+        uint32_t hash;
+        struct route_node *rn;
+
+        if (route_node_lookup(change->rta_oif, change->rta_dst,
+                              change->rtm_dst_len)) {
+            return;
+        }
+
+        rn              = xzalloc(sizeof *rn);
+        rn->rta_oif     = change->rta_oif;
+        rn->rta_dst     = change->rta_dst;
+        rn->rtm_dst_len = change->rtm_dst_len;
+
+        hash = hash_3words(rn->rta_oif, rn->rta_dst, rn->rtm_dst_len);
+        hmap_insert(&route_map, &rn->node, hash);
+    } else if (change->nlmsg_type == RTM_DELROUTE) {
+        struct route_node *rn;
+
+        rn = route_node_lookup(change->rta_oif, change->rta_dst,
+                               change->rtm_dst_len);
+
+        if (rn) {
+            hmap_remove(&route_map, &rn->node);
+            free(rn);
+        }
+    } else {
+        VLOG_WARN_RL(&rl, "Received unexpected rtnetlink message type %d",
+                     change->nlmsg_type);
+    }
+}
+
+static void
+netdev_vport_link_change(const struct rtnetlink_link_change *change,
+                         void *aux OVS_UNUSED)
+{
+
+    if (!change) {
+        netdev_vport_reset_names();
+    } else if (change->nlmsg_type == RTM_NEWLINK) {
+        struct name_node *nn;
+
+        if (name_node_lookup(change->ifi_index)) {
+            return;
+        }
+
+        nn            = xzalloc(sizeof *nn);
+        nn->ifi_index = change->ifi_index;
+
+        strncpy(nn->ifname, change->ifname, IFNAMSIZ);
+        nn->ifname[IFNAMSIZ - 1] = '\0';
+
+        hmap_insert(&name_map, &nn->node, hash_int(nn->ifi_index, 0));
+    } else if (change->nlmsg_type == RTM_DELLINK) {
+        struct name_node *nn;
+
+        nn = name_node_lookup(change->ifi_index);
+
+        if (nn) {
+            hmap_remove(&name_map, &nn->node);
+            free(nn);
+        }
+
+        /* Link deletions do not result in all of the RTM_DELROUTE messages one
+         * would expect.  For now, go ahead and reset route_map whenever a link
+         * is deleted. */
+        netdev_vport_reset_routes();
+    } else {
+        VLOG_WARN_RL(&rl, "Received unexpected rtnetlink message type %d",
+                     change->nlmsg_type);
+    }
+}
+
+static void
+netdev_vport_tnl_iface_init(void)
+{
+    static bool tnl_iface_is_init = false;
+
+    if (!tnl_iface_is_init) {
+        hmap_init(&name_map);
+        hmap_init(&route_map);
+
+        rtnetlink_link_notifier_register(&netdev_vport_link_notifier,
+                                          netdev_vport_link_change, NULL);
+
+        rtnetlink_route_notifier_register(&netdev_vport_route_notifier,
+                                          netdev_vport_route_change, NULL);
+
+        netdev_vport_reset_names();
+        netdev_vport_reset_routes();
+        tnl_iface_is_init = true;
+    }
+}
+
+static const char *
+netdev_vport_get_tnl_iface(const struct netdev *netdev)
+{
+    int dst_len;
+    uint32_t route;
+    struct netdev_dev_vport *ndv;
+    struct tnl_port_config *config;
+    struct route_node *rn, *rn_def, *rn_iter;
+
+    ndv = netdev_dev_vport_cast(netdev_get_dev(netdev));
+    config = (struct tnl_port_config *) ndv->config;
+    route = ntohl(config->daddr);
+
+    dst_len = 0;
+    rn      = NULL;
+    rn_def  = NULL;
+
+    HMAP_FOR_EACH(rn_iter, node, &route_map) {
+        if (rn_iter->rtm_dst_len == 0 && rn_iter->rta_dst == 0) {
+            /* Default route. */
+            rn_def = rn_iter;
+        } else if (rn_iter->rtm_dst_len > dst_len) {
+            uint32_t mask = 0xffffffff << (32 - rn_iter->rtm_dst_len);
+            if ((route & mask) == (rn_iter->rta_dst & mask)) {
+                rn      = rn_iter;
+                dst_len = rn_iter->rtm_dst_len;
+            }
+        }
+    }
+
+    if (!rn) {
+        rn = rn_def;
+    }
+
+    if (rn) {
+        uint32_t hash;
+        struct name_node *nn;
+
+        hash = hash_int(rn->rta_oif, 0);
+        HMAP_FOR_EACH_WITH_HASH(nn, node, hash, &name_map) {
+            if (nn->ifi_index == rn->rta_oif) {
+                return nn->ifname;
+            }
+        }
+    }
+
+    return NULL;
+}
 \f
 /* Helper functions. */
 
@@ -536,7 +856,7 @@ parse_tunnel_config(const struct netdev_dev *dev, const struct shash *args,
             }
         } else if (!strcmp(node->name, "psk") && is_ipsec) {
             ipsec_mech_set = true;
-        } else if (is_ipsec 
+        } else if (is_ipsec
                 && (!strcmp(node->name, "certificate")
                     || !strcmp(node->name, "private_key")
                     || !strcmp(node->name, "use_ssl_cert"))) {
@@ -604,10 +924,10 @@ parse_patch_config(const struct netdev_dev *dev, const struct shash *args,
     return 0;
 }
 \f
-#define VPORT_FUNCTIONS                                     \
-    NULL,                       /* init */                  \
-    NULL,                       /* run */                   \
-    NULL,                       /* wait */                  \
+#define VPORT_FUNCTIONS(TNL_IFACE)                          \
+    netdev_vport_init,                                      \
+    netdev_vport_run,                                       \
+    netdev_vport_wait,                                      \
                                                             \
     netdev_vport_create,                                    \
     netdev_vport_destroy,                                   \
@@ -654,6 +974,7 @@ parse_patch_config(const struct netdev_dev *dev, const struct shash *args,
     NULL,                       /* get_in6 */               \
     NULL,                       /* add_router */            \
     NULL,                       /* get_next_hop */          \
+    TNL_IFACE,                                              \
     NULL,                       /* arp_lookup */            \
                                                             \
     netdev_vport_update_flags,                              \
@@ -665,10 +986,13 @@ void
 netdev_vport_register(void)
 {
     static const struct vport_class vport_classes[] = {
-        { { "gre", VPORT_FUNCTIONS }, parse_tunnel_config },
-        { { "ipsec_gre", VPORT_FUNCTIONS }, parse_tunnel_config },
-        { { "capwap", VPORT_FUNCTIONS }, parse_tunnel_config },
-        { { "patch", VPORT_FUNCTIONS }, parse_patch_config }
+        { { "gre", VPORT_FUNCTIONS(netdev_vport_get_tnl_iface) },
+            parse_tunnel_config },
+        { { "ipsec_gre", VPORT_FUNCTIONS(netdev_vport_get_tnl_iface) },
+            parse_tunnel_config },
+        { { "capwap", VPORT_FUNCTIONS(netdev_vport_get_tnl_iface) },
+            parse_tunnel_config },
+        { { "patch", VPORT_FUNCTIONS(NULL) }, parse_patch_config }
     };
 
     int i;
index c7de9064b80d425a475e1b567ac0f26087ba6ab4..4b2e59e2a6c6738c0c74d679e23152e9678edee0 100644 (file)
@@ -769,6 +769,16 @@ netdev_get_next_hop(const struct netdev *netdev,
     return error;
 }
 
+const char *
+netdev_get_tnl_iface(const struct netdev *netdev)
+{
+    struct netdev_dev *dev = netdev_get_dev(netdev);
+
+    return (dev->netdev_class->get_tnl_iface
+            ? dev->netdev_class->get_tnl_iface(netdev)
+            : NULL);
+}
+
 /* If 'netdev' has an assigned IPv6 address, sets '*in6' to that address and
  * returns 0.  Otherwise, returns a positive errno value and sets '*in6' to
  * all-zero-bits (in6addr_any).
index 6635a55a3f5e5e2a421a61d0e88e5f16fbe0ad83..d7d7097b0eeb4d46841e98d86e213c9b25e89744 100644 (file)
@@ -141,6 +141,7 @@ int netdev_get_in6(const struct netdev *, struct in6_addr *);
 int netdev_add_router(struct netdev *, struct in_addr router);
 int netdev_get_next_hop(const struct netdev *, const struct in_addr *host,
                         struct in_addr *next_hop, char **);
+const char *netdev_get_tnl_iface(const struct netdev *);
 int netdev_arp_lookup(const struct netdev *, uint32_t ip, uint8_t mac[6]);
 
 int netdev_get_flags(const struct netdev *, enum netdev_flags *);
index ffd615bfaf2e79a44088fea5c61f3c5cfd67a0db..ad83a1d2797741ae405bbfabd3561603098beccd 100644 (file)
 static struct rtnetlink *rtn = NULL;
 static struct rtnetlink_link_change rtn_change;
 
-static bool
+/* 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)
 {
index 9248b0ad4c3da45c5e5f2175c86ba8bde9ce6ee7..c41612d1158f868ddd2693a05936228f9f0d1df9 100644 (file)
@@ -17,6 +17,9 @@
 #ifndef RTNETLINK_LINK_H
 #define RTNETLINK_LINK_H 1
 
+#include <stdbool.h>
+
+struct ofpbuf;
 struct rtnetlink_notifier;
 
 /* These functions are Linux specific, so they should be used directly only by
@@ -45,6 +48,8 @@ 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);
 int rtnetlink_link_notifier_register(struct rtnetlink_notifier *,
                                      rtnetlink_link_notify_func *, void *aux);
 void rtnetlink_link_notifier_unregister(struct rtnetlink_notifier *);
diff --git a/lib/rtnetlink-route.c b/lib/rtnetlink-route.c
new file mode 100644 (file)
index 0000000..549236e
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2009, 2010 Nicira Networks.
+ *
+ * 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-route.h"
+
+#include <arpa/inet.h>
+#include <sys/socket.h>
+#include <linux/rtnetlink.h>
+#include <net/if.h>
+
+#include "netlink.h"
+#include "ofpbuf.h"
+#include "rtnetlink.h"
+
+static struct rtnetlink *rtn = NULL;
+static struct rtnetlink_route_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_route_parse(struct ofpbuf *buf,
+                      struct rtnetlink_route_change *change)
+{
+    bool parsed;
+
+    static const struct nl_policy policy[] = {
+        [RTA_DST] = { .type = NL_A_U32, .optional = true  },
+        [RTA_OIF] = { .type = NL_A_U32, .optional = false },
+    };
+
+    static struct nlattr *attrs[ARRAY_SIZE(policy)];
+
+    parsed = nl_policy_parse(buf, NLMSG_HDRLEN + sizeof(struct rtmsg),
+                             policy, attrs, ARRAY_SIZE(policy));
+
+    if (parsed) {
+        const struct nlmsghdr *nlmsg;
+        const struct rtmsg *rtm;
+
+        nlmsg = buf->data;
+        rtm = (const struct rtmsg *) ((const char *) buf->data + NLMSG_HDRLEN);
+
+        if (rtm->rtm_family != AF_INET) {
+            return false;
+        }
+
+        change->nlmsg_type  = nlmsg->nlmsg_type;
+        change->rtm_dst_len = rtm->rtm_dst_len;
+        change->rta_oif     = nl_attr_get_u32(attrs[RTA_OIF]);
+        change->rta_dst     = (attrs[RTA_DST]
+                               ? ntohl(nl_attr_get_be32(attrs[RTA_DST]))
+                               : 0);
+    }
+
+    return parsed;
+}
+
+/* Registers 'cb' to be called with auxiliary data 'aux' with route change
+ * notifications.  The notifier is stored in 'notifier', which callers must
+ * not modify or free.
+ *
+ * Returns 0 if successful, otherwise a positive errno value. */
+int
+rtnetlink_route_notifier_register(struct rtnetlink_notifier *notifier,
+                                 rtnetlink_route_notify_func *cb, void *aux)
+{
+    rtnetlink_parse_func *pf = (rtnetlink_parse_func *) rtnetlink_route_parse;
+    rtnetlink_notify_func *nf = (rtnetlink_notify_func *) cb;
+
+    if (!rtn) {
+        rtn = rtnetlink_create(RTNLGRP_IPV4_ROUTE, pf, &rtn_change);
+    }
+
+    return rtnetlink_notifier_register(rtn, notifier, nf, aux);
+}
+
+/* Cancels notification on 'notifier', which must have previously been
+ * registered with rtnetlink_route_notifier_register(). */
+void
+rtnetlink_route_notifier_unregister(struct rtnetlink_notifier *notifier)
+{
+    rtnetlink_notifier_unregister(rtn, notifier);
+}
+
+/* Calls all of the registered notifiers, passing along any as-yet-unreported
+ * address change events. */
+void
+rtnetlink_route_notifier_run(void)
+{
+    if (rtn) {
+        rtnetlink_notifier_run(rtn);
+    }
+}
+
+/* Causes poll_block() to wake up when address change notifications are ready.
+ */
+void
+rtnetlink_route_notifier_wait(void)
+{
+    if (rtn) {
+        rtnetlink_notifier_wait(rtn);
+    }
+}
diff --git a/lib/rtnetlink-route.h b/lib/rtnetlink-route.h
new file mode 100644 (file)
index 0000000..c9290d3
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2009 Nicira Networks.
+ *
+ * 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_ROUTE_H
+#define RTNETLINK_ROUTE_H 1
+
+#include <stdbool.h>
+#include <stdint.h>
+
+struct ofpbuf;
+struct rtnetlink_notifier;
+
+/* A digested version of a route message sent down by the kernel to indicate
+ * that a route has changed. */
+struct rtnetlink_route_change {
+    /* Copied from struct nlmsghdr. */
+    int nlmsg_type;               /* e.g. RTM_NEWROUTE, RTM_DELROUTE. */
+
+    /* Copied from struct rtmsg. */
+    unsigned char rtm_dst_len;
+
+    /* Extracted from Netlink attributes. */
+    uint32_t rta_dst; /* Destination in host byte order. 0 if missing. */
+    int rta_oif;      /* Output interface index. */
+};
+
+/* Function called to report that a route has changed.  'change' describes
+ * the specific change.  It may be null, in which case the function must assume
+ * everything has changed.  'aux' is as specified in the call to
+ * rtnetlink_route_notifier_register(). */
+typedef
+void rtnetlink_route_notify_func(const struct rtnetlink_route_change *change,
+                                 void *aux);
+
+bool rtnetlink_route_parse(struct ofpbuf *, struct rtnetlink_route_change *);
+int rtnetlink_route_notifier_register(struct rtnetlink_notifier *,
+                                     rtnetlink_route_notify_func *, void *aux);
+void rtnetlink_route_notifier_unregister(struct rtnetlink_notifier *);
+void rtnetlink_route_notifier_run(void);
+void rtnetlink_route_notifier_wait(void);
+
+#endif /* rtnetlink-route.h */
index 96a24fd963e11b6310a6cda9807975541d8f5e70..d505b533c9ecc8e774962740454fa61c7758dfcc 100644 (file)
@@ -1101,6 +1101,14 @@ dpid_from_hash(const void *data, size_t n)
     return eth_addr_to_uint64(hash);
 }
 
+static void
+iface_refresh_tunnel_egress(struct iface *iface)
+{
+    const char *name = netdev_get_tnl_iface(iface->netdev);
+
+    ovsrec_interface_set_tunnel_egress_iface(iface->cfg, name);
+}
+
 static void
 iface_refresh_cfm_stats(struct iface *iface)
 {
@@ -1310,6 +1318,7 @@ bridge_run(void)
                         struct iface *iface = port->ifaces[j];
                         iface_refresh_stats(iface);
                         iface_refresh_cfm_stats(iface);
+                        iface_refresh_tunnel_egress(iface);
                     }
                 }
             }
index a1917ee5c30454af66d7858679050afe35e888e4..d21a85c1414ad96abc4fe14a4d5ff24afd5b82d3 100644 (file)
@@ -1,6 +1,6 @@
 {"name": "Open_vSwitch",
- "version": "1.0.0",
- "cksum": "514853437 13985",
+ "version": "1.0.1",
+ "cksum": "665434435 14130",
  "tables": {
    "Open_vSwitch": {
      "columns": {
        "ingress_policing_burst": {
          "type": {"key": {"type": "integer",
                           "minInteger": 0}}},
+       "tunnel_egress_iface": {
+         "type": {"key": {"type": "string"},
+                  "min": 0, "max": 1},
+         "ephemeral": true},
        "mac": {
          "type": {"key": {"type": "string"},
                   "min": 0, "max": 1}},
index 4aa46494877cad63074eec97cea349293dca5a1a..293634b6ef5541b4296625a5f1957ffea5813293 100644 (file)
         </dl>
       </column>
 
+      <column name="tunnel_egress_iface">
+        Egress interface for tunnels.  Currently only relevant for GRE and
+        CAPWAP tunnels.  On Linux systems, this column will show the name of
+        the interface which is responsible for routing traffic destined for the
+        configured <code>remote_ip</code>.  This could be an internal interface
+        such as a bridge port.
+      </column>
+
       <column name="other_config">
         Key-value pairs for rarely used interface features.  Currently,
         there are none defined.