From: Ethan Jackson Date: Wed, 22 Dec 2010 00:26:21 +0000 (-0800) Subject: lib: Show tunnel egress interface in ovsdb X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=ea83a2fcd0d31246ece7bdea4c54e162f432e81c;p=openvswitch lib: Show tunnel egress interface in ovsdb This commit parses rtnetlink address notifications from the kernel in order to display the egress interface of tunnels in the database. Bug #4103. --- diff --git a/lib/automake.mk b/lib/automake.mk index 4c6a8772..f5d23a23 100644 --- a/lib/automake.mk +++ b/lib/automake.mk @@ -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 diff --git a/lib/netdev-dummy.c b/lib/netdev-dummy.c index ddcbe363..218a0228 100644 --- a/lib/netdev-dummy.c +++ b/lib/netdev-dummy.c @@ -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, diff --git a/lib/netdev-linux.c b/lib/netdev-linux.c index 7f9a8e32..5654dd46 100644 --- a/lib/netdev-linux.c +++ b/lib/netdev-linux.c @@ -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, \ diff --git a/lib/netdev-provider.h b/lib/netdev-provider.h index d955bb17..038f277f 100644 --- a/lib/netdev-provider.h +++ b/lib/netdev-provider.h @@ -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 diff --git a/lib/netdev-vport.c b/lib/netdev-vport.c index 11db0999..f3985e9d 100644 --- a/lib/netdev-vport.c +++ b/lib/netdev-vport.c @@ -20,21 +20,50 @@ #include #include +#include +#include #include #include #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(); +} + +/* 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; +} /* 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; } -#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; diff --git a/lib/netdev.c b/lib/netdev.c index c7de9064..4b2e59e2 100644 --- a/lib/netdev.c +++ b/lib/netdev.c @@ -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). diff --git a/lib/netdev.h b/lib/netdev.h index 6635a55a..d7d7097b 100644 --- a/lib/netdev.h +++ b/lib/netdev.h @@ -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 *); diff --git a/lib/rtnetlink-link.c b/lib/rtnetlink-link.c index ffd615bf..ad83a1d2 100644 --- a/lib/rtnetlink-link.c +++ b/lib/rtnetlink-link.c @@ -29,7 +29,10 @@ 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) { diff --git a/lib/rtnetlink-link.h b/lib/rtnetlink-link.h index 9248b0ad..c41612d1 100644 --- a/lib/rtnetlink-link.h +++ b/lib/rtnetlink-link.h @@ -17,6 +17,9 @@ #ifndef RTNETLINK_LINK_H #define RTNETLINK_LINK_H 1 +#include + +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 index 00000000..549236e6 --- /dev/null +++ b/lib/rtnetlink-route.c @@ -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 + +#include "rtnetlink-route.h" + +#include +#include +#include +#include + +#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 index 00000000..c9290d37 --- /dev/null +++ b/lib/rtnetlink-route.h @@ -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 +#include + +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 */ diff --git a/vswitchd/bridge.c b/vswitchd/bridge.c index 96a24fd9..d505b533 100644 --- a/vswitchd/bridge.c +++ b/vswitchd/bridge.c @@ -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); } } } diff --git a/vswitchd/vswitch.ovsschema b/vswitchd/vswitch.ovsschema index a1917ee5..d21a85c1 100644 --- a/vswitchd/vswitch.ovsschema +++ b/vswitchd/vswitch.ovsschema @@ -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": { @@ -147,6 +147,10 @@ "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}}, diff --git a/vswitchd/vswitch.xml b/vswitchd/vswitch.xml index 4aa46494..293634b6 100644 --- a/vswitchd/vswitch.xml +++ b/vswitchd/vswitch.xml @@ -1102,6 +1102,14 @@ + + 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 remote_ip. This could be an internal interface + such as a bridge port. + + Key-value pairs for rarely used interface features. Currently, there are none defined.