Signed-off-by: Jarno Rajahalme <jrajaha...@nicira.com>
---
 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, &current_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
dev@openvswitch.org
http://openvswitch.org/mailman/listinfo/dev

Reply via email to