Signed-off-by: Jarno Rajahalme <[email protected]>
---
lib/tnl-router.c | 217 +++++++++++++++++++++++++++++++++---------------------
1 file changed, 133 insertions(+), 84 deletions(-)
diff --git a/lib/tnl-router.c b/lib/tnl-router.c
index 4397509..aae457a 100644
--- a/lib/tnl-router.c
+++ b/lib/tnl-router.c
@@ -29,34 +29,19 @@
#include "classifier.h"
#include "command-line.h"
#include "compiler.h"
-#include "dirs.h"
#include "dpif.h"
#include "dynamic-string.h"
-#include "fat-rwlock.h"
-#include "flow.h"
-#include "match.h"
#include "netdev.h"
-#include "netlink.h"
-#include "odp-util.h"
-#include "ofp-parse.h"
-#include "ofpbuf.h"
-#include "ovs-thread.h"
#include "packets.h"
-#include "shash.h"
-#include "simap.h"
-#include "smap.h"
-#include "sset.h"
-#include "timeval.h"
#include "tnl-router.h"
#include "tnl-ports.h"
#include "unixctl.h"
#include "util.h"
-#include "list.h"
-#include "util.h"
static struct classifier cls;
-static struct ovs_mutex mutex = OVS_MUTEX_INITIALIZER;
-static int version, c_ver;
+
+static atomic_int version;
+static atomic_int committed_version;
struct tnl_route_entry {
struct cls_rule cr;
@@ -69,18 +54,27 @@ struct tnl_route_entry {
bool
tnl_route_reconfigured(void)
{
- if (version == c_ver) {
+ int current_ver, committed_ver;
+
+ atomic_read_relaxed(&version, ¤t_ver);
+ atomic_read_relaxed(&committed_version, &committed_ver);
+
+ if (committed_ver == current_ver) {
return false;
}
- /* version is not commited, send refconfigure event. */
- c_ver++;
+ /* version is not committed, send refconfigure event. */
+ atomic_store_relaxed(&committed_version, current_ver);
return true;
}
static struct tnl_route_entry *
tnl_route_entry_cast(const struct cls_rule *cr)
{
- return cr ? CONTAINER_OF(cr, struct tnl_route_entry, cr) : NULL;
+ if (offsetof(struct tnl_route_entry, cr) == 0) {
+ return CONTAINER_OF(cr, struct tnl_route_entry, cr);
+ } else {
+ return cr ? CONTAINER_OF(cr, struct tnl_route_entry, cr) : NULL;
+ }
}
int
@@ -94,9 +88,8 @@ tnl_route_lookup(ovs_be32 ip_dst, char output_bridge[],
ovs_be32 *gw)
cr = classifier_lookup(&cls, &flow, NULL);
if (cr) {
- struct tnl_route_entry *p;
+ struct tnl_route_entry *p = tnl_route_entry_cast(cr);
- p = tnl_route_entry_cast(cr);
strncpy(output_bridge, p->output_bridge, IFNAMSIZ);
*gw = p->gw;
return 0;
@@ -104,103 +97,159 @@ tnl_route_lookup(ovs_be32 ip_dst, char output_bridge[],
ovs_be32 *gw)
return -ENOENT;
}
+/* To be removed. */
bool
tnl_route_check_dev(const char output_bridge[])
{
struct tnl_route_entry *rt;
bool res = false;
- ovs_mutex_lock(&mutex);
-
CLS_FOR_EACH(rt, cr, &cls) {
if (!strncmp(output_bridge, rt->output_bridge, IFNAMSIZ)) {
res = true;
}
}
- ovs_mutex_unlock(&mutex);
return res;
}
static void
-rt_entry_insert(ovs_be32 ip_dst, ovs_be32 mask,
- const char output_bridge[], ovs_be32 gw)
+rt_entry_free(struct tnl_route_entry *p)
+{
+ cls_rule_destroy(&p->cr);
+ free(p);
+}
+
+static void
+rt_entry_insert(ovs_be32 ip_dst, uint8_t plen, const char output_bridge[],
+ ovs_be32 gw)
{
const struct cls_rule *cr;
struct tnl_route_entry *p;
- struct flow_wildcards wc;
- struct flow flow;
struct match match;
+ int old_version;
+ ovs_be32 mask = htonl(UINT32_MAX << (32 - plen));
- memset(&flow, 0, sizeof(flow));
- flow.nw_dst = ip_dst;
+ ip_dst &= mask; /* Clear out insignificant bits. */
- cr = classifier_lookup(&cls, &flow, NULL);
- if (cr) {
- return;
- }
+ memset(&match, 0, sizeof match);
+ match.flow.nw_dst = ip_dst;
+ match.wc.masks.nw_dst = mask;
p = xzalloc(sizeof *p);
strncpy(p->output_bridge, output_bridge, IFNAMSIZ);
p->gw = gw;
p->nw_addr = ip_dst;
p->mask = mask;
+ cls_rule_init(&p->cr, &match, plen); /* Longest prefix matches first. */
+
+ cr = classifier_replace(&cls, &p->cr);
+ if (cr) {
+ /* An old rule with the same match was displaced. */
+ ovsrcu_postpone(rt_entry_free, tnl_route_entry_cast(cr));
+ }
+ atomic_add_relaxed(&version, 1, &old_version);
+}
+
+static bool
+rt_entry_delete(ovs_be32 ip_dst, uint8_t plen)
+{
+ struct cls_rule *cr;
+ struct cls_rule rule;
+ struct match match;
+ ovs_be32 mask = htonl(UINT32_MAX << (32 - plen));
+
+ ip_dst &= mask; /* Clear out insignificant bits. */
- memset(&wc, 0, sizeof(wc));
- wc.masks.nw_dst = mask;
- /* Need to check IP address. */
- match_init(&match, &flow, &wc);
- cls_rule_init(&p->cr, &match, 1);
+ memset(&match, 0, sizeof match);
+ match.flow.nw_dst = ip_dst;
+ match.wc.masks.nw_dst = mask;
+ cls_rule_init(&rule, &match, plen);
- ovs_mutex_lock(&mutex);
- classifier_insert(&cls, &p->cr);
- version++;
- ovs_mutex_unlock(&mutex);
+ /* Find the exact rule. */
+ cr = classifier_find_rule_exactly(&cls, &rule);
+ if (cr) {
+ /* Remove it. */
+ cr = classifier_remove(&cls, cr);
+ if (cr) {
+ int old_version;
+
+ ovsrcu_postpone(rt_entry_free, tnl_route_entry_cast(cr));
+ atomic_add_relaxed(&version, 1, &old_version);
+ return true;
+ }
+ }
+ return false;
+}
+
+static bool
+scan_ipv4_route(const char *s, ovs_be32 *addr, unsigned int *plen)
+{
+ int len, max_plen, n;
+ int slen = strlen(s);
+ uint8_t *ip = (uint8_t *)addr;
+
+ *addr = htons(0);
+ if (!ovs_scan(s, "%"SCNu8"%n", &ip[0], &n)) {
+ return false;
+ }
+ len = n;
+ max_plen = 8;
+ for (int i = 1; i < 4; i++) {
+ if (ovs_scan(s + len, ".%"SCNu8"%n", &ip[i], &n)) {
+ len += n;
+ max_plen += 8;
+ } else {
+ break;
+ }
+ }
+ if (len == slen && max_plen == 32) {
+ *plen = 32;
+ return true;
+ }
+ if (ovs_scan(s + len, "/%u%n", plen, &n)
+ && len + n == slen && *plen <= max_plen) {
+ return true;
+ }
+ return false;
}
static void
tnl_route_add(struct unixctl_conn *conn, int argc,
const char *argv[], void *aux OVS_UNUSED)
{
- ovs_be32 ip, mask, gw;
-
- inet_aton(argv[1], (struct in_addr *)&ip);
- inet_aton(argv[2], (struct in_addr *)&mask);
-
- if (argc == 5) {
- inet_aton(argv[4], (struct in_addr *)&gw);
+ ovs_be32 ip, gw;
+ unsigned int plen;
+
+ if (scan_ipv4_route(argv[1], &ip, &plen)) {
+ if (argc > 3) {
+ inet_aton(argv[3], (struct in_addr *)&gw);
+ } else {
+ gw = 0;
+ }
+ rt_entry_insert(ip, plen, argv[2], gw);
+ unixctl_command_reply(conn, "OK");
} else {
- gw = 0;
+ unixctl_command_reply(conn, "Invalid parameters");
}
-
- rt_entry_insert(ip, mask, argv[3], gw);
- unixctl_command_reply(conn, "OK");
}
static void
tnl_route_del(struct unixctl_conn *conn, int argc OVS_UNUSED,
const char *argv[], void *aux OVS_UNUSED)
{
- struct cls_rule *cr;
- struct flow flow;
- ovs_be32 ip, mask;
+ ovs_be32 ip;
+ unsigned int plen;
- inet_aton(argv[1], (struct in_addr *)&ip);
- inet_aton(argv[2], (struct in_addr *)&mask);
+ if (scan_ipv4_route(argv[1], &ip, &plen)) {
- ovs_mutex_lock(&mutex);
- memset(&flow, 0, sizeof(flow));
- flow.nw_dst = ip & mask;
-
- cr = classifier_lookup(&cls, &flow, NULL);
- if (cr) {
- classifier_remove(&cls, cr);
- version++;
- unixctl_command_reply(conn, "OK");
+ if (rt_entry_delete(ip, plen)) {
+ unixctl_command_reply(conn, "OK");
+ } else {
+ unixctl_command_reply(conn, "Not found");
+ }
} else {
- unixctl_command_reply(conn, "Not found");
+ unixctl_command_reply(conn, "Invalid parameters");
}
-
- ovs_mutex_unlock(&mutex);
}
static void
@@ -210,28 +259,28 @@ tnl_route_show(struct unixctl_conn *conn, int argc
OVS_UNUSED,
struct tnl_route_entry *rt;
struct ds ds = DS_EMPTY_INITIALIZER;
- ovs_mutex_lock(&mutex);
-
CLS_FOR_EACH(rt, cr, &cls) {
ds_put_format(&ds, "IP "IP_FMT" Mask "IP_FMT" dev %s",
- IP_ARGS(rt->nw_addr), IP_ARGS(rt->mask),
rt->output_bridge);
+ IP_ARGS(rt->nw_addr), IP_ARGS(rt->mask),
+ rt->output_bridge);
if (rt->gw) {
- ds_put_format(&ds, " GW "IP_FMT, IP_ARGS(rt->gw));
+ ds_put_format(&ds, " GW "IP_FMT, IP_ARGS(rt->gw));
}
- ds_put_format(&ds, "\n");
+ ds_put_format(&ds, "\n");
}
unixctl_command_reply(conn, ds_cstr(&ds));
ds_destroy(&ds);
-
- ovs_mutex_unlock(&mutex);
}
+/* May not be called more than once. */
void
tnl_route_unixctl_register(void)
{
+ classifier_init(&cls, NULL);
/* XXX: Add documentation for these commands. */
- unixctl_command_register("tnl/route/add", "ip mask dev gw", 3, 4,
tnl_route_add, NULL);
+ unixctl_command_register("tnl/route/add", "ip mask dev gw", 2, 3,
+ tnl_route_add, NULL);
unixctl_command_register("tnl/route/show", "", 0, 0, tnl_route_show, NULL);
- unixctl_command_register("tnl/route/del", "ip mask", 2, 2, tnl_route_del,
NULL);
- classifier_init(&cls, NULL);
+ unixctl_command_register("tnl/route/del", "ip mask", 1, 1, tnl_route_del,
+ NULL);
}
--
1.7.10.4
_______________________________________________
dev mailing list
[email protected]
http://openvswitch.org/mailman/listinfo/dev