+
+static void
+netdev_vport_run(void)
+{
+ rtnetlink_link_notifier_run();
+ route_table_run();
+}
+
+static void
+netdev_vport_wait(void)
+{
+ rtnetlink_link_notifier_wait();
+ route_table_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;
+}
+
+/* Queries the kernel for fresh data to populate the name map with. */
+static int
+netdev_vport_reset_names(void)
+{
+ int error;
+ struct nl_dump dump;
+ struct rtgenmsg *rtmsg;
+ struct ofpbuf request, reply;
+ static struct nl_sock *rtnl_sock;
+ struct name_node *nn, *nn_next;
+
+ HMAP_FOR_EACH_SAFE(nn, nn_next, node, &name_map) {
+ hmap_remove(&name_map, &nn->node);
+ free(nn);
+ }
+
+ 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);
+
+ 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, rtnl_sock, &request);
+
+ while (nl_dump_next(&dump, &reply)) {
+ struct rtnetlink_link_change change;
+
+ if (rtnetlink_link_parse(&reply, &change)) {
+ netdev_vport_link_change(&change, NULL);
+ }
+ }
+
+ error = nl_dump_done(&dump);
+ nl_sock_destroy(rtnl_sock);
+
+ return error;
+}
+
+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);
+ }
+
+ } 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);
+
+ rtnetlink_link_notifier_register(&netdev_vport_link_notifier,
+ netdev_vport_link_change, NULL);
+
+ netdev_vport_reset_names();
+ tnl_iface_is_init = true;
+ }
+}
+
+static const char *
+netdev_vport_get_tnl_iface(const struct netdev *netdev)
+{
+ int ifindex;
+ uint32_t route;
+ struct netdev_dev_vport *ndv;
+ struct tnl_port_config *config;
+
+ ndv = netdev_dev_vport_cast(netdev_get_dev(netdev));
+ config = (struct tnl_port_config *) ndv->config;
+ route = config->daddr;
+
+ if (route_table_get_ifindex(route, &ifindex)) {
+ struct name_node *nn;
+ HMAP_FOR_EACH_WITH_HASH(nn, node, hash_int(ifindex, 0), &name_map) {
+ if (nn->ifi_index == ifindex) {
+ return nn->ifname;
+ }
+ }
+ }
+
+ return NULL;
+}