#include "ovs-router.h"
+#include <sys/types.h>
+#include <netinet/in.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 "unixctl.h"
#include "util.h"
#include "unaligned.h"
-#include "unixctl.h"
-#include "util.h"
#include "openvswitch/vlog.h"
VLOG_DEFINE_THIS_MODULE(ovs_router);
static struct ovs_mutex mutex = OVS_MUTEX_INITIALIZER;
static struct classifier cls;
+/* By default, use the system routing table. For system-independent testing,
+ * the unit tests disable using the system routing table. */
+static bool use_system_routing_table = true;
+
struct ovs_router_entry {
struct cls_rule cr;
char output_bridge[IFNAMSIZ];
struct in6_addr src_addr;
uint8_t plen;
uint8_t priority;
+ bool local;
uint32_t mark;
};
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;
- }
+ return cr ? CONTAINER_OF(cr, struct ovs_router_entry, cr) : NULL;
+}
+
+/* Disables obtaining routes from the system routing table, for testing
+ * purposes. */
+void
+ovs_router_disable_system_routing_table(void)
+{
+ use_system_routing_table = false;
}
static bool
{
ovs_be32 src;
- if (!route_table_fallback_lookup(ip6_dst, output_bridge, gw6)) {
+ if (!use_system_routing_table
+ || !route_table_fallback_lookup(ip6_dst, output_bridge, gw6)) {
return false;
}
if (netdev_get_in4_by_name(output_bridge, (struct in_addr *)&src)) {
const struct cls_rule *cr;
struct flow flow = {.ipv6_dst = *ip6_dst, .pkt_mark = mark};
+ if (src && ipv6_addr_is_set(src)) {
+ const struct cls_rule *cr_src;
+ struct flow flow_src = {.ipv6_dst = *src, .pkt_mark = mark};
+
+ cr_src = classifier_lookup(&cls, OVS_VERSION_MAX, &flow_src, NULL);
+ if (cr_src) {
+ struct ovs_router_entry *p_src = ovs_router_entry_cast(cr_src);
+ if (!p_src->local) {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ }
+
cr = classifier_lookup(&cls, OVS_VERSION_MAX, &flow, NULL);
if (cr) {
struct ovs_router_entry *p = ovs_router_entry_cast(cr);
ovs_strlcpy(output_bridge, p->output_bridge, IFNAMSIZ);
*gw = p->gw;
- if (src) {
+ if (src && !ipv6_addr_is_set(src)) {
*src = p->src_addr;
}
return true;
}
static int
-ovs_router_insert__(uint32_t mark, uint8_t priority,
+ovs_router_insert__(uint32_t mark, uint8_t priority, bool local,
const struct in6_addr *ip6_dst,
uint8_t plen, const char output_bridge[],
const struct in6_addr *gw)
p->mark = mark;
p->nw_addr = match.flow.ipv6_dst;
p->plen = plen;
+ p->local = local;
p->priority = priority;
err = get_src_addr(ip6_dst, output_bridge, &p->src_addr);
if (err && ipv6_addr_is_set(gw)) {
void
ovs_router_insert(uint32_t mark, const struct in6_addr *ip_dst, uint8_t plen,
- const char output_bridge[], const struct in6_addr *gw)
+ bool local, const char output_bridge[],
+ const struct in6_addr *gw)
{
- ovs_router_insert__(mark, plen, ip_dst, plen, output_bridge, gw);
+ if (use_system_routing_table) {
+ uint8_t priority = local ? plen + 64 : plen;
+ ovs_router_insert__(mark, priority, local, ip_dst, plen, output_bridge, gw);
+ }
}
-static bool
-__rt_entry_delete(const struct cls_rule *cr)
+static void
+rt_entry_delete__(const struct cls_rule *cr)
{
struct ovs_router_entry *p = ovs_router_entry_cast(cr);
tnl_port_map_delete_ipdev(p->output_bridge);
- /* Remove it. */
- cr = classifier_remove(&cls, cr);
- if (cr) {
- ovsrcu_postpone(rt_entry_free, ovs_router_entry_cast(cr));
- return true;
- }
- return false;
+ classifier_remove_assert(&cls, cr);
+ ovsrcu_postpone(rt_entry_free, ovs_router_entry_cast(cr));
}
static bool
cr = classifier_find_rule_exactly(&cls, &rule, OVS_VERSION_MAX);
if (cr) {
ovs_mutex_lock(&mutex);
- res = __rt_entry_delete(cr);
+ rt_entry_delete__(cr);
ovs_mutex_unlock(&mutex);
+
+ res = true;
}
cls_rule_destroy(&rule);
}
}
- err = ovs_router_insert__(mark, plen + 32, &ip6, plen, argv[2], &gw6);
+ err = ovs_router_insert__(mark, plen + 32, false, &ip6, plen, argv[2], &gw6);
if (err) {
unixctl_command_reply_error(conn, "Error while inserting route.");
} else {
ds_put_format(&ds, "Route Table:\n");
CLS_FOR_EACH(rt, cr, &cls) {
uint8_t plen;
- if (rt->priority == rt->plen) {
+ if (rt->priority == rt->plen || rt->local) {
ds_put_format(&ds, "Cached: ");
} else {
ds_put_format(&ds, "User: ");
}
ds_put_format(&ds, " SRC ");
ipv6_format_mapped(&rt->src_addr, &ds);
+ if (rt->local) {
+ ds_put_format(&ds, " local");
+ }
ds_put_format(&ds, "\n");
}
unixctl_command_reply(conn, ds_cstr(&ds));
ovs_router_lookup_cmd(struct unixctl_conn *conn, int argc,
const char *argv[], void *aux OVS_UNUSED)
{
- struct in6_addr gw, src;
+ struct in6_addr gw, src = in6addr_any;
char iface[IFNAMSIZ];
struct in6_addr ip6;
unsigned int plen;
classifier_defer(&cls);
CLS_FOR_EACH(rt, cr, &cls) {
if (rt->priority == rt->plen) {
- __rt_entry_delete(&rt->cr);
+ rt_entry_delete__(&rt->cr);
}
}
classifier_publish(&cls);