mirror of
https://github.com/openvswitch/ovs
synced 2025-09-03 07:45:30 +00:00
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.
This commit is contained in:
@@ -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
|
||||
|
@@ -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,
|
||||
|
@@ -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, \
|
||||
|
@@ -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
|
||||
|
@@ -20,21 +20,50 @@
|
||||
|
||||
#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();
|
||||
}
|
||||
|
||||
/* 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;
|
||||
|
10
lib/netdev.c
10
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).
|
||||
|
@@ -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 *);
|
||||
|
@@ -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)
|
||||
{
|
||||
|
@@ -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 *);
|
||||
|
119
lib/rtnetlink-route.c
Normal file
119
lib/rtnetlink-route.c
Normal file
@@ -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);
|
||||
}
|
||||
}
|
55
lib/rtnetlink-route.h
Normal file
55
lib/rtnetlink-route.h
Normal file
@@ -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 */
|
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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}},
|
||||
|
@@ -1102,6 +1102,14 @@
|
||||
</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.
|
||||
|
Reference in New Issue
Block a user