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, ¤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 dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev