/*
- * Copyright (c) 2011 Nicira Networks.
+ * Copyright (c) 2011, 2012, 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.
#include "route-table.h"
-#include <assert.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <linux/rtnetlink.h>
#include "hash.h"
#include "hmap.h"
#include "netlink.h"
+#include "netlink-notifier.h"
#include "netlink-socket.h"
#include "ofpbuf.h"
-#include "rtnetlink.h"
+#include "rtnetlink-link.h"
#include "vlog.h"
VLOG_DEFINE_THIS_MODULE(route_table);
struct route_data rd; /* Data associated with this node. */
};
+struct name_node {
+ struct hmap_node node; /* Node in name_map. */
+ uint32_t ifi_index; /* Kernel interface index. */
+
+ char ifname[IFNAMSIZ]; /* Interface name. */
+};
+
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20);
static unsigned int register_count = 0;
-static struct rtnetlink *rtn = NULL;
+static struct nln *nln = NULL;
static struct route_table_msg rtmsg;
-static struct rtnetlink_notifier notifier;
+static struct nln_notifier *route_notifier = NULL;
+static struct nln_notifier *name_notifier = NULL;
static bool route_table_valid = false;
+static bool name_table_valid = false;
static struct hmap route_map;
+static struct hmap name_map;
static int route_table_reset(void);
static void route_table_handle_msg(const struct route_table_msg *);
static void route_map_clear(void);
static uint32_t hash_route_data(const struct route_data *);
+static void name_table_init(void);
+static void name_table_uninit(void);
+static int name_table_reset(void);
+static void name_table_change(const struct rtnetlink_link_change *, void *);
+static void name_map_clear(void);
+static struct name_node *name_node_lookup(int ifi_index);
+
+/* Populates 'name' with the name of the interface traffic destined for 'ip'
+ * is likely to egress out of (see route_table_get_ifindex).
+ *
+ * Returns true if successful, otherwise false. */
+bool
+route_table_get_name(ovs_be32 ip, char name[IFNAMSIZ])
+{
+ int ifindex;
+
+ if (!name_table_valid) {
+ name_table_reset();
+ }
+
+ if (route_table_get_ifindex(ip, &ifindex)) {
+ struct name_node *nn;
+
+ nn = name_node_lookup(ifindex);
+ if (nn) {
+ ovs_strlcpy(name, nn->ifname, IFNAMSIZ);
+ return true;
+ }
+ }
+
+ return false;
+}
+
/* Populates 'ifindex' with the interface index traffic destined for 'ip' is
* likely to egress. There is no hard guarantee that traffic destined for 'ip'
* will egress out the specified interface. 'ifindex' may refer to an
route_table_register(void)
{
if (!register_count) {
- rtnetlink_parse_func *pf;
- rtnetlink_notify_func *nf;
-
- assert(!rtn);
+ ovs_assert(!nln);
+ ovs_assert(!route_notifier);
- pf = (rtnetlink_parse_func *) route_table_parse;
- nf = (rtnetlink_notify_func *) route_table_change;
+ nln = nln_create(NETLINK_ROUTE, RTNLGRP_IPV4_ROUTE,
+ (nln_parse_func *) route_table_parse, &rtmsg);
- rtn = rtnetlink_create(RTNLGRP_IPV4_ROUTE, pf, &rtmsg);
- rtnetlink_notifier_register(rtn, ¬ifier, nf, NULL);
+ route_notifier =
+ nln_notifier_create(nln, (nln_notify_func *) route_table_change,
+ NULL);
hmap_init(&route_map);
route_table_reset();
+ name_table_init();
}
register_count++;
register_count--;
if (!register_count) {
- rtnetlink_destroy(rtn);
- rtn = NULL;
+ nln_notifier_destroy(route_notifier);
+ route_notifier = NULL;
+ nln_destroy(nln);
+ nln = NULL;
route_map_clear();
hmap_destroy(&route_map);
+ name_table_uninit();
}
}
void
route_table_run(void)
{
- if (rtn) {
- rtnetlink_notifier_run(rtn);
+ if (nln) {
+ rtnetlink_link_run();
+ nln_run(nln);
}
}
void
route_table_wait(void)
{
- if (rtn) {
- rtnetlink_notifier_wait(rtn);
+ if (nln) {
+ rtnetlink_link_wait();
+ nln_wait(nln);
}
}
static int
route_table_reset(void)
{
- int error;
struct nl_dump dump;
struct rtgenmsg *rtmsg;
struct ofpbuf request, reply;
- static struct nl_sock *rtnl_sock;
route_map_clear();
route_table_valid = true;
- error = nl_sock_create(NETLINK_ROUTE, 0, 0, 0, &rtnl_sock);
- if (error) {
- VLOG_WARN_RL(&rl, "failed to reset routing table, "
- "cannot create RTNETLINK_ROUTE socket");
- return error;
- }
-
ofpbuf_init(&request, 0);
nl_msg_put_nlmsghdr(&request, sizeof *rtmsg, RTM_GETROUTE, NLM_F_REQUEST);
rtmsg = ofpbuf_put_zeros(&request, sizeof *rtmsg);
rtmsg->rtgen_family = AF_INET;
- nl_dump_start(&dump, rtnl_sock, &request);
+ nl_dump_start(&dump, NETLINK_ROUTE, &request);
+ ofpbuf_uninit(&request);
while (nl_dump_next(&dump, &reply)) {
struct route_table_msg msg;
}
}
- error = nl_dump_done(&dump);
- nl_sock_destroy(rtnl_sock);
-
- return error;
+ return nl_dump_done(&dump);
}
[RTA_OIF] = { .type = NL_A_U32, .optional = false },
};
- static struct nlattr *attrs[ARRAY_SIZE(policy)];
+ struct nlattr *attrs[ARRAY_SIZE(policy)];
parsed = nl_policy_parse(buf, NLMSG_HDRLEN + sizeof(struct rtmsg),
policy, attrs, ARRAY_SIZE(policy));
const struct nlmsghdr *nlmsg;
nlmsg = buf->data;
- rtm = (const struct rtmsg *) ((const char *) buf->data + NLMSG_HDRLEN);
+ rtm = ofpbuf_at(buf, NLMSG_HDRLEN, sizeof *rtm);
if (rtm->rtm_family != AF_INET) {
VLOG_DBG_RL(&rl, "received non AF_INET rtnetlink route message");
{
return hash_bytes(rd, sizeof *rd, 0);
}
+\f
+/* name_table . */
+
+static void
+name_table_init(void)
+{
+ hmap_init(&name_map);
+ name_notifier = rtnetlink_link_notifier_create(name_table_change, NULL);
+ name_table_valid = false;
+}
+
+static void
+name_table_uninit(void)
+{
+ rtnetlink_link_notifier_destroy(name_notifier);
+ name_notifier = NULL;
+ name_map_clear();
+ hmap_destroy(&name_map);
+}
+
+static int
+name_table_reset(void)
+{
+ struct nl_dump dump;
+ struct rtgenmsg *rtmsg;
+ struct ofpbuf request, reply;
+
+ name_table_valid = true;
+ name_map_clear();
+
+ ofpbuf_init(&request, 0);
+ nl_msg_put_nlmsghdr(&request, sizeof *rtmsg, RTM_GETLINK, NLM_F_REQUEST);
+ rtmsg = ofpbuf_put_zeros(&request, sizeof *rtmsg);
+ rtmsg->rtgen_family = AF_INET;
+
+ nl_dump_start(&dump, NETLINK_ROUTE, &request);
+ ofpbuf_uninit(&request);
+
+ while (nl_dump_next(&dump, &reply)) {
+ struct rtnetlink_link_change change;
+
+ if (rtnetlink_link_parse(&reply, &change)
+ && change.nlmsg_type == RTM_NEWLINK
+ && !name_node_lookup(change.ifi_index)) {
+ struct name_node *nn;
+
+ nn = xzalloc(sizeof *nn);
+ nn->ifi_index = change.ifi_index;
+ ovs_strlcpy(nn->ifname, change.ifname, IFNAMSIZ);
+ hmap_insert(&name_map, &nn->node, hash_int(nn->ifi_index, 0));
+ }
+ }
+ return nl_dump_done(&dump);
+}
+
+static void
+name_table_change(const struct rtnetlink_link_change *change OVS_UNUSED,
+ void *aux OVS_UNUSED)
+{
+ /* Changes to interface status can cause routing table changes that some
+ * versions of the linux kernel do not advertise for some reason. */
+ route_table_valid = false;
+ name_table_valid = false;
+}
+
+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 void
+name_map_clear(void)
+{
+ struct name_node *nn, *nn_next;
+
+ HMAP_FOR_EACH_SAFE(nn, nn_next, node, &name_map) {
+ hmap_remove(&name_map, &nn->node);
+ free(nn);
+ }
+}