route-table: Use classifier to store routing table.
authorPravin B Shelar <pshelar@nicira.com>
Thu, 16 Oct 2014 18:38:12 +0000 (11:38 -0700)
committerPravin B Shelar <pshelar@nicira.com>
Mon, 3 Nov 2014 21:27:31 +0000 (13:27 -0800)
Rather than using hmap for storing routing entries we can directly use
classifier which has support for priority and wildcard entries.
This makes route lookup lockless. This help when we use route lookup
for native tunneling.

Signed-off-by: Pravin B Shelar <pshelar@nicira.com>
Acked-by: Thomas Graf <tgraf@noironetworks.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
lib/automake.mk
lib/netdev-vport.c
lib/ovs-router.c [new file with mode: 0644]
lib/ovs-router.h [new file with mode: 0644]
lib/route-table-bsd.c
lib/route-table-stub.c
lib/route-table.c
lib/route-table.h
ofproto/ofproto-dpif-sflow.c
ofproto/ofproto-dpif.c

index 42424aa..1256af1 100644 (file)
@@ -318,6 +318,8 @@ lib_libopenvswitch_la_SOURCES += \
        lib/netlink-socket.h \
        lib/ovs-numa.c \
        lib/ovs-numa.h \
+       lib/ovs-router.c \
+       lib/ovs-router.h \
        lib/rtnetlink-link.c \
        lib/rtnetlink-link.h \
        lib/route-table.c \
index 83f1296..b3b8a11 100644 (file)
@@ -33,6 +33,7 @@
 #include "list.h"
 #include "netdev-provider.h"
 #include "ofpbuf.h"
+#include "ovs-router.h"
 #include "packets.h"
 #include "poll-loop.h"
 #include "route-table.h"
@@ -284,10 +285,11 @@ tunnel_check_status_change__(struct netdev_vport *netdev)
     char iface[IFNAMSIZ];
     bool status = false;
     ovs_be32 route;
+    ovs_be32 gw;
 
     iface[0] = '\0';
     route = netdev->tnl_cfg.ip_dst;
-    if (route_table_get_name(route, iface)) {
+    if (ovs_router_lookup(route, iface, &gw)) {
         struct netdev *egress_netdev;
 
         if (!netdev_open(iface, "system", &egress_netdev)) {
diff --git a/lib/ovs-router.c b/lib/ovs-router.c
new file mode 100644 (file)
index 0000000..c119c94
--- /dev/null
@@ -0,0 +1,275 @@
+/*
+ * Copyright (c) 2014 Nicira, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <config.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "classifier.h"
+#include "command-line.h"
+#include "compiler.h"
+#include "dpif.h"
+#include "dynamic-string.h"
+#include "netdev.h"
+#include "packets.h"
+#include "ovs-router.h"
+#include "unixctl.h"
+#include "util.h"
+
+static struct classifier cls;
+
+struct ovs_router_entry {
+    struct cls_rule cr;
+    char output_bridge[IFNAMSIZ];
+    ovs_be32 gw;
+    ovs_be32 nw_addr;
+    uint8_t plen;
+    uint8_t priority;
+};
+
+static struct ovs_router_entry *
+ovs_router_entry_cast(const struct cls_rule *cr)
+{
+    if (offsetof(struct ovs_router_entry, cr) == 0) {
+        return CONTAINER_OF(cr, struct ovs_router_entry, cr);
+    } else {
+        return cr ? CONTAINER_OF(cr, struct ovs_router_entry, cr) : NULL;
+    }
+}
+
+bool
+ovs_router_lookup(ovs_be32 ip_dst, char output_bridge[], ovs_be32 *gw)
+{
+    const struct cls_rule *cr;
+    struct flow flow = {.nw_dst = ip_dst};
+
+    cr = classifier_lookup(&cls, &flow, NULL);
+    if (cr) {
+        struct ovs_router_entry *p = ovs_router_entry_cast(cr);
+
+        strncpy(output_bridge, p->output_bridge, IFNAMSIZ);
+        *gw = p->gw;
+        return true;
+    }
+    return false;
+}
+
+static void
+rt_entry_free(struct ovs_router_entry *p)
+{
+    cls_rule_destroy(&p->cr);
+    free(p);
+}
+
+static void rt_init_match(struct match *match, ovs_be32 ip_dst, uint8_t plen)
+{
+    ovs_be32 mask;
+
+    mask = htonl(UINT32_MAX << (32 - plen));
+
+    ip_dst &= mask; /* Clear out insignificant bits. */
+    memset(match, 0, sizeof *match);
+    match->flow.nw_dst = ip_dst;
+    match->wc.masks.nw_dst = mask;
+}
+
+static void
+ovs_router_insert__(uint8_t priority, ovs_be32 ip_dst, uint8_t plen,
+                    const char output_bridge[],
+                    ovs_be32 gw)
+{
+    const struct cls_rule *cr;
+    struct ovs_router_entry *p;
+    struct match match;
+
+    rt_init_match(&match, ip_dst, plen);
+
+    p = xzalloc(sizeof *p);
+    strncpy(p->output_bridge, output_bridge, IFNAMSIZ);
+    p->gw = gw;
+    p->nw_addr = match.flow.nw_dst;
+    p->plen = plen;
+    p->priority = priority;
+    cls_rule_init(&p->cr, &match, priority); /* 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, ovs_router_entry_cast(cr));
+    }
+}
+
+void
+ovs_router_insert(ovs_be32 ip_dst, uint8_t plen, const char output_bridge[],
+                  ovs_be32 gw)
+{
+    ovs_router_insert__(plen, ip_dst, plen, output_bridge, gw);
+}
+
+static bool
+rt_entry_delete(uint8_t priority, ovs_be32 ip_dst, uint8_t plen)
+{
+    struct cls_rule *cr;
+    struct cls_rule rule;
+    struct match match;
+
+    rt_init_match(&match, ip_dst, plen);
+
+    cls_rule_init(&rule, &match, priority);
+
+    /* Find the exact rule. */
+    cr = classifier_find_rule_exactly(&cls, &rule);
+    if (cr) {
+        /* Remove it. */
+        cr = classifier_remove(&cls, cr);
+        if (cr) {
+
+            ovsrcu_postpone(rt_entry_free, ovs_router_entry_cast(cr));
+            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
+ovs_router_add(struct unixctl_conn *conn, int argc,
+              const char *argv[], void *aux OVS_UNUSED)
+{
+    ovs_be32 ip, gw;
+    unsigned int plen;
+
+    if (scan_ipv4_route(argv[1], &ip, &plen)) {
+        if (argc > 3) {
+            inet_pton(AF_INET, argv[3], (struct in_addr *)&gw);
+        } else {
+            gw = 0;
+        }
+        ovs_router_insert__(plen + 32, ip, plen, argv[2], gw);
+        unixctl_command_reply(conn, "OK");
+    } else {
+        unixctl_command_reply(conn, "Invalid parameters");
+    }
+}
+
+static void
+ovs_router_del(struct unixctl_conn *conn, int argc OVS_UNUSED,
+              const char *argv[], void *aux OVS_UNUSED)
+{
+    ovs_be32 ip;
+    unsigned int plen;
+
+    if (scan_ipv4_route(argv[1], &ip, &plen)) {
+
+        if (rt_entry_delete(plen + 32, ip, plen)) {
+            unixctl_command_reply(conn, "OK");
+        } else {
+            unixctl_command_reply(conn, "Not found");
+        }
+    } else {
+        unixctl_command_reply(conn, "Invalid parameters");
+    }
+}
+
+static void
+ovs_router_show(struct unixctl_conn *conn, int argc OVS_UNUSED,
+               const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED)
+{
+    struct ovs_router_entry *rt;
+    struct ds ds = DS_EMPTY_INITIALIZER;
+
+    ds_put_format(&ds, "Route Table:\n");
+    CLS_FOR_EACH(rt, cr, &cls) {
+        if (rt->priority == rt->plen) {
+            ds_put_format(&ds, "Cached: ");
+        } else {
+            ds_put_format(&ds, "User: ");
+        }
+        ds_put_format(&ds, IP_FMT"/%"PRIu16" dev %s",
+                      IP_ARGS(rt->nw_addr), rt->plen,
+                      rt->output_bridge);
+        if (rt->gw) {
+            ds_put_format(&ds, " GW "IP_FMT, IP_ARGS(rt->gw));
+        }
+        ds_put_format(&ds, "\n");
+    }
+    unixctl_command_reply(conn, ds_cstr(&ds));
+    ds_destroy(&ds);
+}
+
+void
+ovs_router_flush(void)
+{
+    struct ovs_router_entry *rt;
+
+    CLS_FOR_EACH_SAFE(rt, cr, &cls) {
+        if (rt->priority == rt->plen) {
+            classifier_remove(&cls, &rt->cr);
+        }
+    }
+}
+
+/* May not be called more than once. */
+void
+ovs_router_unixctl_register(void)
+{
+    classifier_init(&cls, NULL);
+    /* XXX: Add documentation for these commands. */
+    unixctl_command_register("ovs/route/add", "ip mask dev gw", 2, 3,
+                             ovs_router_add, NULL);
+    unixctl_command_register("ovs/route/show", "", 0, 0, ovs_router_show, NULL);
+    unixctl_command_register("ovs/route/del", "ip mask", 1, 1, ovs_router_del,
+                             NULL);
+}
diff --git a/lib/ovs-router.h b/lib/ovs-router.h
new file mode 100644 (file)
index 0000000..7992497
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2014 Nicira, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef OVS_TNL_ROUTER_H
+#define OVS_TNL_ROUTER_H 1
+
+#include <stddef.h>
+#include <stdint.h>
+#include <net/if.h>
+
+#include "packets.h"
+#include "timeval.h"
+#include "unixctl.h"
+#include "util.h"
+
+#ifdef  __cplusplus
+extern "C" {
+#endif
+
+bool ovs_router_lookup(ovs_be32 ip_dst, char out_dev[], ovs_be32 *gw);
+void ovs_router_insert(ovs_be32 ip_dst, uint8_t plen, const char output_bridge[],
+                       ovs_be32 gw);
+void ovs_router_flush(void);
+
+void ovs_router_unixctl_register(void);
+#ifdef  __cplusplus
+}
+#endif
+
+#endif
index f14fea0..6039e3f 100644 (file)
@@ -35,7 +35,7 @@ static int pid;
 static unsigned int register_count = 0;
 
 bool
-route_table_get_name(ovs_be32 ip, char name[IFNAMSIZ])
+ovs_router_lookup(ovs_be32 ip, char name[], ovs_be32 *gw)
 {
     struct {
         struct rt_msghdr rtm;
@@ -93,6 +93,7 @@ route_table_get_name(ovs_be32 ip, char name[IFNAMSIZ])
                     namelen = IFNAMSIZ - 1;
                 memcpy(name, ifp->sdl_data, namelen);
                 name[namelen] = '\0';
+                *gw = 0;
                 return true;
             }
 #if defined(__FreeBSD__)
index dab4fd2..85c2583 100644 (file)
@@ -18,8 +18,9 @@
 #include "compiler.h"
 
 bool
-route_table_get_name(ovs_be32 ip OVS_UNUSED, char name[IFNAMSIZ] OVS_UNUSED)
+ovs_router_lookup(ovs_be32 ip_dst OVS_UNUSED, char output_bridge[], ovs_be32 *gw)
 {
+    *gw = 0;
     name[0] = '\0';
     return false;
 }
index dd2e85f..cb8f0f7 100644 (file)
 #include <net/if.h>
 
 #include "hash.h"
-#include "hmap.h"
 #include "netlink.h"
 #include "netlink-notifier.h"
 #include "netlink-socket.h"
 #include "ofpbuf.h"
+#include "ovs-router.h"
 #include "rtnetlink-link.h"
 #include "vlog.h"
 
@@ -40,7 +40,7 @@ struct route_data {
     unsigned char rtm_dst_len;
 
     /* Extracted from Netlink attributes. */
-    uint32_t rta_dst; /* Destination in host byte order. 0 if missing. */
+    ovs_be32 rta_dst; /* 0 if missing. */
     char ifname[IFNAMSIZ]; /* Interface name. */
 };
 
@@ -52,11 +52,6 @@ struct route_table_msg {
     struct route_data rd; /* Data parsed from this message. */
 };
 
-struct route_node {
-    struct hmap_node node; /* Node in route_map. */
-    struct route_data rd;  /* Data associated with this node. */
-};
-
 static struct ovs_mutex route_table_mutex = OVS_MUTEX_INITIALIZER;
 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20);
 
@@ -71,60 +66,17 @@ static struct nln_notifier *route_notifier = NULL;
 static struct nln_notifier *name_notifier = NULL;
 
 static bool route_table_valid = false;
-static struct hmap route_map;
 
 static int route_table_reset(void);
 static void route_table_handle_msg(const struct route_table_msg *);
 static bool route_table_parse(struct ofpbuf *, struct route_table_msg *);
 static void route_table_change(const struct route_table_msg *, void *);
-static struct route_node *route_node_lookup(const struct route_data *);
-static struct route_node *route_node_lookup_by_ip(uint32_t ip);
 static void route_map_clear(void);
-static uint32_t hash_route_data(const struct route_data *);
 
 static void name_table_init(void);
 static void name_table_uninit(void);
 static void name_table_change(const struct rtnetlink_link_change *, void *);
 
-/* Populates 'name' with the name of the interface traffic destined for 'ip'
- * is likely to egress out of.
- *
- * Returns true if successful, otherwise false. */
-bool
-route_table_get_name(ovs_be32 ip_, char name[IFNAMSIZ])
-    OVS_REQUIRES(route_table_mutex)
-{
-    struct route_node *rn;
-    uint32_t ip = ntohl(ip_);
-    bool res = false;
-
-    ovs_mutex_lock(&route_table_mutex);
-    if (!route_table_valid) {
-        route_table_reset();
-    }
-
-    rn = route_node_lookup_by_ip(ip);
-
-    if (rn) {
-        ovs_strlcpy(name, rn->rd.ifname, IFNAMSIZ);
-        res = true;
-        goto out;
-    }
-
-    /* Choose a default route. */
-    HMAP_FOR_EACH(rn, node, &route_map) {
-        if (rn->rd.rta_dst == 0 && rn->rd.rtm_dst_len == 0) {
-            ovs_strlcpy(name, rn->rd.ifname, IFNAMSIZ);
-            res = true;
-            break;
-        }
-    }
-
-out:
-    ovs_mutex_unlock(&route_table_mutex);
-    return res;
-}
-
 uint64_t
 route_table_get_change_seq(void)
 {
@@ -149,7 +101,6 @@ route_table_register(void)
             nln_notifier_create(nln, (nln_notify_func *) route_table_change,
                                 NULL);
 
-        hmap_init(&route_map);
         route_table_reset();
         name_table_init();
     }
@@ -175,7 +126,6 @@ route_table_unregister(void)
         nln = NULL;
 
         route_map_clear();
-        hmap_destroy(&route_map);
         name_table_uninit();
     }
     ovs_mutex_unlock(&route_table_mutex);
@@ -299,7 +249,7 @@ route_table_parse(struct ofpbuf *buf, struct route_table_msg *change)
         }
 
         if (attrs[RTA_DST]) {
-            change->rd.rta_dst = ntohl(nl_attr_get_be32(attrs[RTA_DST]));
+            change->rd.rta_dst = nl_attr_get_be32(attrs[RTA_DST]);
         }
 
     } else {
@@ -319,74 +269,19 @@ route_table_change(const struct route_table_msg *change OVS_UNUSED,
 static void
 route_table_handle_msg(const struct route_table_msg *change)
 {
-    if (change->relevant && change->nlmsg_type == RTM_NEWROUTE &&
-        !route_node_lookup(&change->rd)) {
-        struct route_node *rn;
-
-        rn = xzalloc(sizeof *rn);
-        memcpy(&rn->rd, &change->rd, sizeof change->rd);
+    if (change->relevant && change->nlmsg_type == RTM_NEWROUTE) {
+        const struct route_data *rd = &change->rd;
 
-        hmap_insert(&route_map, &rn->node, hash_route_data(&rn->rd));
+        ovs_router_insert(rd->rta_dst, rd->rtm_dst_len, rd->ifname, 0);
     }
 }
 
-static struct route_node *
-route_node_lookup(const struct route_data *rd)
-{
-    struct route_node *rn;
-
-    HMAP_FOR_EACH_WITH_HASH(rn, node, hash_route_data(rd), &route_map) {
-        if (!memcmp(&rn->rd, rd, sizeof *rd)) {
-            return rn;
-        }
-    }
-
-    return NULL;
-}
-
-static struct route_node *
-route_node_lookup_by_ip(uint32_t ip)
-{
-    int dst_len;
-    struct route_node *rn, *rn_ret;
-
-    dst_len = -1;
-    rn_ret  = NULL;
-
-    HMAP_FOR_EACH(rn, node, &route_map) {
-        uint32_t mask = 0xffffffff << (32 - rn->rd.rtm_dst_len);
-
-        if (rn->rd.rta_dst == 0 && rn->rd.rtm_dst_len == 0) {
-            /* Default route. */
-            continue;
-        }
-
-        if (rn->rd.rtm_dst_len > dst_len &&
-            (ip & mask) == (rn->rd.rta_dst & mask)) {
-            rn_ret  = rn;
-            dst_len = rn->rd.rtm_dst_len;
-        }
-    }
-
-    return rn_ret;
-}
-
 static void
 route_map_clear(void)
 {
-    struct route_node *rn, *rn_next;
-
-    HMAP_FOR_EACH_SAFE(rn, rn_next, node, &route_map) {
-        hmap_remove(&route_map, &rn->node);
-        free(rn);
-    }
+    ovs_router_flush();
 }
 
-static uint32_t
-hash_route_data(const struct route_data *rd)
-{
-    return hash_bytes(rd, sizeof *rd, 0);
-}
 \f
 /* name_table . */
 
index 2c5967e..709dfb0 100644 (file)
@@ -25,7 +25,6 @@
 
 #include "openvswitch/types.h"
 
-bool route_table_get_name(ovs_be32 ip, char name[IFNAMSIZ]);
 uint64_t route_table_get_change_seq(void);
 void route_table_register(void);
 void route_table_unregister(void);
index 32cf51b..92e2ae8 100644 (file)
@@ -32,6 +32,7 @@
 #include "ofproto.h"
 #include "packets.h"
 #include "poll-loop.h"
+#include "ovs-router.h"
 #include "route-table.h"
 #include "sflow_api.h"
 #include "socket-util.h"
@@ -261,7 +262,9 @@ sflow_choose_agent_address(const char *agent_device,
 
         if (inet_parse_active(target, SFL_DEFAULT_COLLECTOR_PORT, &sa.ss)
             && sa.ss.ss_family == AF_INET) {
-            if (route_table_get_name(sa.sin.sin_addr.s_addr, name)
+            ovs_be32 gw;
+
+            if (ovs_router_lookup(sa.sin.sin_addr.s_addr, name, &gw)
                 && !netdev_get_in4_by_name(name, &in4)) {
                 goto success;
             }
index d965d38..9f4dbc8 100644 (file)
@@ -58,6 +58,7 @@
 #include "ofproto-dpif-sflow.h"
 #include "ofproto-dpif-upcall.h"
 #include "ofproto-dpif-xlate.h"
+#include "ovs-router.h"
 #include "poll-loop.h"
 #include "seq.h"
 #include "simap.h"
@@ -1225,6 +1226,7 @@ construct(struct ofproto *ofproto_)
     guarded_list_init(&ofproto->pins);
 
     ofproto_dpif_unixctl_init();
+    ovs_router_unixctl_register();
 
     hmap_init(&ofproto->vlandev_map);
     hmap_init(&ofproto->realdev_vid_map);