/*
- * Copyright (c) 2014 Nicira, Inc.
+ * Copyright (c) 2014, 2015 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 "classifier.h"
#include "dynamic-string.h"
#include "hash.h"
+#include "list.h"
#include "ofpbuf.h"
#include "ovs-thread.h"
#include "odp-util.h"
#include "tnl-arp-cache.h"
#include "tnl-ports.h"
+#include "ovs-thread.h"
#include "unixctl.h"
#include "util.h"
+static struct ovs_mutex mutex = OVS_MUTEX_INITIALIZER;
static struct classifier cls; /* Tunnel ports. */
+struct ip_device {
+ struct netdev *dev;
+ struct eth_addr mac;
+ ovs_be32 addr;
+ uint64_t change_seq;
+ struct ovs_list node;
+ char dev_name[IFNAMSIZ];
+};
+
+static struct ovs_list addr_list;
+
+struct tnl_port {
+ odp_port_t port;
+ ovs_be16 udp_port;
+ char dev_name[IFNAMSIZ];
+ struct ovs_list node;
+};
+
+static struct ovs_list port_list;
+
struct tnl_port_in {
struct cls_rule cr;
odp_port_t portno;
}
static void
-tnl_port_init_flow(struct flow *flow, ovs_be32 ip_dst, ovs_be16 udp_port)
+tnl_port_init_flow(struct flow *flow, struct eth_addr mac,
+ ovs_be32 addr, ovs_be16 udp_port)
{
memset(flow, 0, sizeof *flow);
+
flow->dl_type = htons(ETH_TYPE_IP);
+ flow->dl_dst = mac;
+ flow->nw_dst = addr;
+
if (udp_port) {
flow->nw_proto = IPPROTO_UDP;
} else {
flow->nw_proto = IPPROTO_GRE;
}
flow->tp_dst = udp_port;
- /* When matching on incoming flow from remove tnl end point,
- * our dst ip address is source ip for them. */
- flow->nw_src = ip_dst;
}
-void
-tnl_port_map_insert(odp_port_t port, ovs_be32 ip_dst, ovs_be16 udp_port,
- const char dev_name[])
+static void
+map_insert(odp_port_t port, struct eth_addr mac, ovs_be32 addr,
+ ovs_be16 udp_port, const char dev_name[])
{
const struct cls_rule *cr;
struct tnl_port_in *p;
struct match match;
memset(&match, 0, sizeof match);
- tnl_port_init_flow(&match.flow, ip_dst, udp_port);
+ tnl_port_init_flow(&match.flow, mac, addr, udp_port);
do {
- cr = classifier_lookup(&cls, &match.flow, NULL);
+ cr = classifier_lookup(&cls, CLS_MAX_VERSION, &match.flow, NULL);
p = tnl_port_cast(cr);
/* Try again if the rule was released before we get the reference. */
} while (p && !ovs_refcount_try_ref_rcu(&p->ref_cnt));
- if (p) {
- return; /* Added refcount of an existing port. */
+ if (!p) {
+ p = xzalloc(sizeof *p);
+ p->portno = port;
+
+ match.wc.masks.dl_type = OVS_BE16_MAX;
+ match.wc.masks.nw_proto = 0xff;
+ match.wc.masks.nw_frag = 0xff; /* XXX: No fragments support. */
+ match.wc.masks.tp_dst = OVS_BE16_MAX;
+ match.wc.masks.nw_dst = OVS_BE32_MAX;
+ match.wc.masks.vlan_tci = OVS_BE16_MAX;
+ memset(&match.wc.masks.dl_dst, 0xff, sizeof (struct eth_addr));
+
+ cls_rule_init(&p->cr, &match, 0); /* Priority == 0. */
+ ovs_refcount_init(&p->ref_cnt);
+ ovs_strlcpy(p->dev_name, dev_name, sizeof p->dev_name);
+
+ classifier_insert(&cls, &p->cr, CLS_MIN_VERSION, NULL, 0);
}
+}
- p = xzalloc(sizeof *p);
- p->portno = port;
+void
+tnl_port_map_insert(odp_port_t port,
+ ovs_be16 udp_port, const char dev_name[])
+{
+ struct tnl_port *p;
+ struct ip_device *ip_dev;
- match.wc.masks.dl_type = OVS_BE16_MAX;
- match.wc.masks.nw_proto = 0xff;
- match.wc.masks.nw_frag = 0xff; /* XXX: No fragments support. */
- match.wc.masks.tp_dst = OVS_BE16_MAX;
- match.wc.masks.nw_src = OVS_BE32_MAX;
+ ovs_mutex_lock(&mutex);
+ LIST_FOR_EACH(p, node, &port_list) {
+ if (udp_port == p->udp_port) {
+ goto out;
+ }
+ }
+
+ p = xzalloc(sizeof *p);
+ p->port = port;
+ p->udp_port = udp_port;
+ ovs_strlcpy(p->dev_name, dev_name, sizeof p->dev_name);
+ list_insert(&port_list, &p->node);
- cls_rule_init(&p->cr, &match, 0); /* Priority == 0. */
- ovs_refcount_init(&p->ref_cnt);
- strncpy(p->dev_name, dev_name, IFNAMSIZ);
+ LIST_FOR_EACH(ip_dev, node, &addr_list) {
+ map_insert(p->port, ip_dev->mac, ip_dev->addr,
+ p->udp_port, p->dev_name);
+ }
- classifier_insert(&cls, &p->cr);
+out:
+ ovs_mutex_unlock(&mutex);
}
static void
}
}
-void
-tnl_port_map_delete(ovs_be32 ip_dst, ovs_be16 udp_port)
+static void
+map_delete(struct eth_addr mac, ovs_be32 addr, ovs_be16 udp_port)
{
const struct cls_rule *cr;
struct flow flow;
- tnl_port_init_flow(&flow, ip_dst, udp_port);
+ tnl_port_init_flow(&flow, mac, addr, udp_port);
- cr = classifier_lookup(&cls, &flow, NULL);
+ cr = classifier_lookup(&cls, CLS_MAX_VERSION, &flow, NULL);
tnl_port_unref(cr);
}
+void
+tnl_port_map_delete(ovs_be16 udp_port)
+{
+ struct tnl_port *p, *next;
+ struct ip_device *ip_dev;
+ bool found = false;
+
+ ovs_mutex_lock(&mutex);
+ LIST_FOR_EACH_SAFE(p, next, node, &port_list) {
+ if (p->udp_port == udp_port) {
+ list_remove(&p->node);
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ goto out;
+ }
+ LIST_FOR_EACH(ip_dev, node, &addr_list) {
+ map_delete(ip_dev->mac, ip_dev->addr, udp_port);
+ }
+
+ free(p);
+out:
+ ovs_mutex_unlock(&mutex);
+}
+
+/* 'flow' is non-const to allow for temporary modifications during the lookup.
+ * Any changes are restored before returning. */
odp_port_t
-tnl_port_map_lookup(const struct flow *flow, struct flow_wildcards *wc)
+tnl_port_map_lookup(struct flow *flow, struct flow_wildcards *wc)
{
- const struct cls_rule *cr = classifier_lookup(&cls, flow, wc);
+ const struct cls_rule *cr = classifier_lookup(&cls, CLS_MAX_VERSION, flow,
+ wc);
return (cr) ? tnl_port_cast(cr)->portno : ODPP_NONE;
}
static void
-tnl_port_show(struct unixctl_conn *conn, int argc OVS_UNUSED,
- const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED)
+tnl_port_show_v(struct ds *ds)
{
- struct ds ds = DS_EMPTY_INITIALIZER;
const struct tnl_port_in *p;
- ds_put_format(&ds, "Listening ports:\n");
CLS_FOR_EACH(p, cr, &cls) {
struct odputil_keybuf keybuf;
struct odputil_keybuf maskbuf;
size_t key_len, mask_len;
struct flow_wildcards wc;
struct ofpbuf buf;
+ struct odp_flow_key_parms odp_parms = {
+ .flow = &flow,
+ .mask = &wc.masks,
+ };
- ds_put_format(&ds, "%s (%"PRIu32") : ", p->dev_name, p->portno);
- minimask_expand(&p->cr.match.mask, &wc);
- miniflow_expand(&p->cr.match.flow, &flow);
+ ds_put_format(ds, "%s (%"PRIu32") : ", p->dev_name, p->portno);
+ minimask_expand(p->cr.match.mask, &wc);
+ miniflow_expand(p->cr.match.flow, &flow);
/* Key. */
+ odp_parms.odp_in_port = flow.in_port.odp_port;
+ odp_parms.support.recirc = true;
ofpbuf_use_stack(&buf, &keybuf, sizeof keybuf);
- odp_flow_key_from_flow(&buf, &flow, &wc.masks,
- flow.in_port.odp_port, true);
- key = ofpbuf_data(&buf);
- key_len = ofpbuf_size(&buf);
+ odp_flow_key_from_flow(&odp_parms, &buf);
+ key = buf.data;
+ key_len = buf.size;
+
/* mask*/
+ odp_parms.odp_in_port = wc.masks.in_port.odp_port;
+ odp_parms.support.recirc = false;
ofpbuf_use_stack(&buf, &maskbuf, sizeof maskbuf);
- odp_flow_key_from_mask(&buf, &wc.masks, &flow,
- odp_to_u32(wc.masks.in_port.odp_port),
- SIZE_MAX, false);
- mask = ofpbuf_data(&buf);
- mask_len = ofpbuf_size(&buf);
+ odp_flow_key_from_mask(&odp_parms, &buf);
+ mask = buf.data;
+ mask_len = buf.size;
/* build string. */
- odp_flow_format(key, key_len, mask, mask_len, NULL, &ds, false);
- ds_put_format(&ds, "\n");
+ odp_flow_format(key, key_len, mask, mask_len, NULL, ds, false);
+ ds_put_format(ds, "\n");
+ }
+}
+
+static void
+tnl_port_show(struct unixctl_conn *conn, int argc OVS_UNUSED,
+ const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED)
+{
+ struct ds ds = DS_EMPTY_INITIALIZER;
+ struct tnl_port *p;
+
+ ds_put_format(&ds, "Listening ports:\n");
+ ovs_mutex_lock(&mutex);
+ if (argc > 1) {
+ if (!strcasecmp(argv[1], "-v")) {
+ tnl_port_show_v(&ds);
+ goto out;
+ }
}
+
+ LIST_FOR_EACH(p, node, &port_list) {
+ ds_put_format(&ds, "%s (%"PRIu32")\n", p->dev_name, p->port);
+ }
+
+out:
+ ovs_mutex_unlock(&mutex);
unixctl_command_reply(conn, ds_cstr(&ds));
ds_destroy(&ds);
}
+static void
+map_insert_ipdev(struct ip_device *ip_dev)
+{
+ struct tnl_port *p;
+
+ LIST_FOR_EACH(p, node, &port_list) {
+ map_insert(p->port, ip_dev->mac, ip_dev->addr,
+ p->udp_port, p->dev_name);
+ }
+}
+
+static void
+insert_ipdev(const char dev_name[])
+{
+ struct ip_device *ip_dev;
+ enum netdev_flags flags;
+ struct netdev *dev;
+ int error;
+
+ error = netdev_open(dev_name, NULL, &dev);
+ if (error) {
+ return;
+ }
+
+ error = netdev_get_flags(dev, &flags);
+ if (error || (flags & NETDEV_LOOPBACK)) {
+ netdev_close(dev);
+ return;
+ }
+
+ ip_dev = xzalloc(sizeof *ip_dev);
+ ip_dev->dev = dev;
+ ip_dev->change_seq = netdev_get_change_seq(dev);
+ error = netdev_get_etheraddr(ip_dev->dev, &ip_dev->mac);
+ if (error) {
+ return;
+ }
+ error = netdev_get_in4(ip_dev->dev, (struct in_addr *)&ip_dev->addr, NULL);
+ if (error) {
+ return;
+ }
+ ovs_strlcpy(ip_dev->dev_name, netdev_get_name(dev), sizeof ip_dev->dev_name);
+
+ list_insert(&addr_list, &ip_dev->node);
+ map_insert_ipdev(ip_dev);
+}
+
+static void
+delete_ipdev(struct ip_device *ip_dev)
+{
+ struct tnl_port *p;
+
+ LIST_FOR_EACH(p, node, &port_list) {
+ map_delete(ip_dev->mac, ip_dev->addr, p->udp_port);
+ }
+
+ list_remove(&ip_dev->node);
+ netdev_close(ip_dev->dev);
+ free(ip_dev);
+}
+
+void
+tnl_port_map_insert_ipdev(const char dev_name[])
+{
+ struct ip_device *ip_dev;
+
+ ovs_mutex_lock(&mutex);
+
+ LIST_FOR_EACH(ip_dev, node, &addr_list) {
+ if (!strcmp(netdev_get_name(ip_dev->dev), dev_name)) {
+ if (ip_dev->change_seq == netdev_get_change_seq(ip_dev->dev)) {
+ goto out;
+ }
+ /* Address changed. */
+ delete_ipdev(ip_dev);
+ break;
+ }
+ }
+ insert_ipdev(dev_name);
+
+out:
+ ovs_mutex_unlock(&mutex);
+}
+
+void
+tnl_port_map_delete_ipdev(const char dev_name[])
+{
+ struct ip_device *ip_dev, *next;
+
+ ovs_mutex_lock(&mutex);
+ LIST_FOR_EACH_SAFE(ip_dev, next, node, &addr_list) {
+ if (!strcmp(netdev_get_name(ip_dev->dev), dev_name)) {
+ delete_ipdev(ip_dev);
+ }
+ }
+ ovs_mutex_unlock(&mutex);
+}
+
+void
+tnl_port_map_run(void)
+{
+ struct ip_device *ip_dev;
+
+ ovs_mutex_lock(&mutex);
+ LIST_FOR_EACH(ip_dev, node, &addr_list) {
+ char dev_name[IFNAMSIZ];
+
+ if (ip_dev->change_seq == netdev_get_change_seq(ip_dev->dev)) {
+ continue;
+ }
+
+ /* Address changed. */
+ ovs_strlcpy(dev_name, ip_dev->dev_name, sizeof dev_name);
+ delete_ipdev(ip_dev);
+ insert_ipdev(dev_name);
+ }
+ ovs_mutex_unlock(&mutex);
+}
+
void
tnl_port_map_init(void)
{
- classifier_init(&cls, flow_segment_u32s);
- unixctl_command_register("tnl/ports/show", "", 0, 0, tnl_port_show, NULL);
+ classifier_init(&cls, flow_segment_u64s);
+ list_init(&addr_list);
+ list_init(&port_list);
+ unixctl_command_register("tnl/ports/show", "-v", 0, 1, tnl_port_show, NULL);
}