summaryrefslogtreecommitdiff
path: root/lib/tnl-ports.c
diff options
context:
space:
mode:
authorPravin B Shelar <pshelar@nicira.com>2015-09-03 00:42:34 -0700
committerPravin B Shelar <pshelar@nicira.com>2015-09-08 16:24:35 -0700
commit7f9b850474bfae061fa3ba68cb6e463c70c1b1cc (patch)
tree4cbad008c9ff15d890a3198417ac9ba3997fd9f9 /lib/tnl-ports.c
parentc02819293d52f7ea7b714242d871b2b01f57f905 (diff)
downloadopenvswitch-7f9b850474bfae061fa3ba68cb6e463c70c1b1cc.tar.gz
tnl-ports: Add destination IP and MAC address to the match.
Currently tnl-port table wildcard destination ip and mac addresses for given tunnel packet. That could result accepting tunnel packets destined for other hosts. Following patch adds support for matching for ip and mac address. IP address upates to tnl-port table are piggybacked on ovs-router updates. Reported-by: Ben Pfaff <blp@nicira.com> Signed-off-by: Pravin B Shelar <pshelar@nicira.com> Acked-by: Ben Pfaff <blp@nicira.com>
Diffstat (limited to 'lib/tnl-ports.c')
-rw-r--r--lib/tnl-ports.c261
1 files changed, 243 insertions, 18 deletions
diff --git a/lib/tnl-ports.c b/lib/tnl-ports.c
index 8f7a2a435..f510f1eb7 100644
--- a/lib/tnl-ports.c
+++ b/lib/tnl-ports.c
@@ -21,6 +21,7 @@
#include "classifier.h"
#include "dynamic-string.h"
#include "hash.h"
+#include "list.h"
#include "ofpbuf.h"
#include "ovs-thread.h"
#include "odp-util.h"
@@ -33,6 +34,26 @@
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;
@@ -56,10 +77,15 @@ tnl_port_free(struct tnl_port_in *p)
}
static void
-tnl_port_init_flow(struct flow *flow, 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 {
@@ -68,17 +94,17 @@ tnl_port_init_flow(struct flow *flow, ovs_be16 udp_port)
flow->tp_dst = udp_port;
}
-void
-tnl_port_map_insert(odp_port_t port, 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, udp_port);
+ tnl_port_init_flow(&match.flow, mac, addr, udp_port);
- ovs_mutex_lock(&mutex);
do {
cr = classifier_lookup(&cls, CLS_MAX_VERSION, &match.flow, NULL);
p = tnl_port_cast(cr);
@@ -93,6 +119,9 @@ tnl_port_map_insert(odp_port_t port, ovs_be16 udp_port, const char dev_name[])
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);
@@ -100,6 +129,34 @@ tnl_port_map_insert(odp_port_t port, ovs_be16 udp_port, const char dev_name[])
classifier_insert(&cls, &p->cr, CLS_MIN_VERSION, NULL, 0);
}
+}
+
+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;
+
+ 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);
+
+ LIST_FOR_EACH(ip_dev, node, &addr_list) {
+ map_insert(p->port, ip_dev->mac, ip_dev->addr,
+ p->udp_port, p->dev_name);
+ }
+
+out:
ovs_mutex_unlock(&mutex);
}
@@ -109,26 +166,52 @@ tnl_port_unref(const struct cls_rule *cr)
struct tnl_port_in *p = tnl_port_cast(cr);
if (cr && ovs_refcount_unref_relaxed(&p->ref_cnt) == 1) {
- ovs_mutex_lock(&mutex);
if (classifier_remove(&cls, cr)) {
ovsrcu_postpone(tnl_port_free, p);
}
- ovs_mutex_unlock(&mutex);
}
}
-void
-tnl_port_map_delete(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, udp_port);
+ tnl_port_init_flow(&flow, mac, addr, udp_port);
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
@@ -141,13 +224,10 @@ tnl_port_map_lookup(struct flow *flow, struct flow_wildcards *wc)
}
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;
@@ -161,7 +241,7 @@ tnl_port_show(struct unixctl_conn *conn, int argc OVS_UNUSED,
.mask = &wc.masks,
};
- ds_put_format(&ds, "%s (%"PRIu32") : ", p->dev_name, p->portno);
+ 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);
@@ -182,16 +262,161 @@ tnl_port_show(struct unixctl_conn *conn, int argc OVS_UNUSED,
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_u64s);
- unixctl_command_register("tnl/ports/show", "", 0, 0, tnl_port_show, NULL);
+ list_init(&addr_list);
+ list_init(&port_list);
+ unixctl_command_register("tnl/ports/show", "-v", 0, 1, tnl_port_show, NULL);
}