]> git.proxmox.com Git - mirror_ovs.git/blobdiff - lib/ovs-router.c
flow: Fix buffer overread in flow_hash_symmetric_l3l4().
[mirror_ovs.git] / lib / ovs-router.c
index 82923ff26aa716b965cf755503aa47c602430b94..96871d1b68195b70b02c7dfb3a869ead3233a1ea 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, 2015 Nicira, Inc.
+ * Copyright (c) 2014, 2015, 2016, 2017 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
 #include "unaligned.h"
 #include "unixctl.h"
 #include "util.h"
+#include "openvswitch/vlog.h"
+
+VLOG_DEFINE_THIS_MODULE(ovs_router);
+
+static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
 
 static struct ovs_mutex mutex = OVS_MUTEX_INITIALIZER;
 static struct classifier cls;
@@ -57,6 +62,7 @@ struct ovs_router_entry {
     struct in6_addr src_addr;
     uint8_t plen;
     uint8_t priority;
+    uint32_t mark;
 };
 
 static struct ovs_router_entry *
@@ -69,14 +75,33 @@ ovs_router_entry_cast(const struct cls_rule *cr)
     }
 }
 
+static bool
+ovs_router_lookup_fallback(const struct in6_addr *ip6_dst, char output_bridge[],
+                           struct in6_addr *src6, struct in6_addr *gw6)
+{
+    ovs_be32 src;
+
+    if (!route_table_fallback_lookup(ip6_dst, output_bridge, gw6)) {
+        return false;
+    }
+    if (netdev_get_in4_by_name(output_bridge, (struct in_addr *)&src)) {
+        return false;
+    }
+    if (src6) {
+        in6_addr_set_mapped_ipv4(src6, src);
+    }
+    return true;
+}
+
 bool
-ovs_router_lookup(const struct in6_addr *ip6_dst, char output_bridge[],
+ovs_router_lookup(uint32_t mark, const struct in6_addr *ip6_dst,
+                  char output_bridge[],
                   struct in6_addr *src, struct in6_addr *gw)
 {
     const struct cls_rule *cr;
-    struct flow flow = {.ipv6_dst = *ip6_dst};
+    struct flow flow = {.ipv6_dst = *ip6_dst, .pkt_mark = mark};
 
-    cr = classifier_lookup(&cls, CLS_MAX_VERSION, &flow, NULL);
+    cr = classifier_lookup(&cls, OVS_VERSION_MAX, &flow, NULL);
     if (cr) {
         struct ovs_router_entry *p = ovs_router_entry_cast(cr);
 
@@ -87,20 +112,7 @@ ovs_router_lookup(const struct in6_addr *ip6_dst, char output_bridge[],
         }
         return true;
     }
-    return false;
-}
-
-bool
-ovs_router_lookup4(ovs_be32 ip_dst, char output_bridge[], ovs_be32 *gw)
-{
-    struct in6_addr ip6_dst = in6_addr_mapped_ipv4(ip_dst);
-    struct in6_addr gw6;
-
-    if (ovs_router_lookup(&ip6_dst, output_bridge, NULL, &gw6)) {
-        *gw = in6_addr_get_mapped_ipv4(&gw6);
-        return true;
-    }
-    return route_table_fallback_lookup(ip_dst, output_bridge, gw);
+    return ovs_router_lookup_fallback(ip6_dst, output_bridge, src, gw);
 }
 
 static void
@@ -110,7 +122,8 @@ rt_entry_free(struct ovs_router_entry *p)
     free(p);
 }
 
-static void rt_init_match(struct match *match, const struct in6_addr *ip6_dst,
+static void rt_init_match(struct match *match, uint32_t mark,
+                          const struct in6_addr *ip6_dst,
                           uint8_t plen)
 {
     struct in6_addr dst;
@@ -122,6 +135,8 @@ static void rt_init_match(struct match *match, const struct in6_addr *ip6_dst,
     memset(match, 0, sizeof *match);
     match->flow.ipv6_dst = dst;
     match->wc.masks.ipv6_dst = mask;
+    match->wc.masks.pkt_mark = UINT32_MAX;
+    match->flow.pkt_mark = mark;
 }
 
 static int
@@ -131,6 +146,7 @@ get_src_addr(const struct in6_addr *ip6_dst,
     struct in6_addr *mask, *addr6;
     int err, n_in6, i, max_plen = -1;
     struct netdev *dev;
+    bool is_ipv4;
 
     err = netdev_open(output_bridge, NULL, &dev);
     if (err) {
@@ -142,10 +158,16 @@ get_src_addr(const struct in6_addr *ip6_dst,
         goto out;
     }
 
+    is_ipv4 = IN6_IS_ADDR_V4MAPPED(ip6_dst);
+
     for (i = 0; i < n_in6; i++) {
         struct in6_addr a1, a2;
         int mask_bits;
 
+        if (is_ipv4 && !IN6_IS_ADDR_V4MAPPED(&addr6[i])) {
+            continue;
+        }
+
         a1 = ipv6_addr_bitand(ip6_dst, &mask[i]);
         a2 = ipv6_addr_bitand(&addr6[i], &mask[i]);
         mask_bits = bitmap_count1(ALIGNED_CAST(const unsigned long *, &mask[i]), 128);
@@ -166,7 +188,8 @@ out:
 }
 
 static int
-ovs_router_insert__(uint8_t priority, const struct in6_addr *ip6_dst,
+ovs_router_insert__(uint32_t mark, uint8_t priority,
+                    const struct in6_addr *ip6_dst,
                     uint8_t plen, const char output_bridge[],
                     const struct in6_addr *gw)
 {
@@ -175,25 +198,35 @@ ovs_router_insert__(uint8_t priority, const struct in6_addr *ip6_dst,
     struct match match;
     int err;
 
-    rt_init_match(&match, ip6_dst, plen);
+    rt_init_match(&match, mark, ip6_dst, plen);
 
     p = xzalloc(sizeof *p);
     ovs_strlcpy(p->output_bridge, output_bridge, sizeof p->output_bridge);
     if (ipv6_addr_is_set(gw)) {
         p->gw = *gw;
     }
+    p->mark = mark;
     p->nw_addr = match.flow.ipv6_dst;
     p->plen = plen;
     p->priority = priority;
     err = get_src_addr(ip6_dst, output_bridge, &p->src_addr);
+    if (err && ipv6_addr_is_set(gw)) {
+        err = get_src_addr(gw, output_bridge, &p->src_addr);
+    }
     if (err) {
+        struct ds ds = DS_EMPTY_INITIALIZER;
+
+        ipv6_format_mapped(ip6_dst, &ds);
+        VLOG_DBG_RL(&rl, "src addr not available for route %s", ds_cstr(&ds));
+        free(p);
+        ds_destroy(&ds);
         return err;
     }
     /* Longest prefix matches first. */
     cls_rule_init(&p->cr, &match, priority);
 
     ovs_mutex_lock(&mutex);
-    cr = classifier_replace(&cls, &p->cr, CLS_MIN_VERSION, NULL, 0);
+    cr = classifier_replace(&cls, &p->cr, OVS_VERSION_MIN, NULL, 0);
     ovs_mutex_unlock(&mutex);
 
     if (cr) {
@@ -206,13 +239,12 @@ ovs_router_insert__(uint8_t priority, const struct in6_addr *ip6_dst,
 }
 
 void
-ovs_router_insert(const struct in6_addr *ip_dst, uint8_t plen,
+ovs_router_insert(uint32_t mark, const struct in6_addr *ip_dst, uint8_t plen,
                   const char output_bridge[], const struct in6_addr *gw)
 {
-    ovs_router_insert__(plen, ip_dst, plen, output_bridge, gw);
+    ovs_router_insert__(mark, plen, ip_dst, plen, output_bridge, gw);
 }
 
-
 static bool
 __rt_entry_delete(const struct cls_rule *cr)
 {
@@ -229,24 +261,27 @@ __rt_entry_delete(const struct cls_rule *cr)
 }
 
 static bool
-rt_entry_delete(uint8_t priority, const struct in6_addr *ip6_dst, uint8_t plen)
+rt_entry_delete(uint32_t mark, uint8_t priority,
+                const struct in6_addr *ip6_dst, uint8_t plen)
 {
     const struct cls_rule *cr;
     struct cls_rule rule;
     struct match match;
     bool res = false;
 
-    rt_init_match(&match, ip6_dst, plen);
+    rt_init_match(&match, mark, ip6_dst, plen);
 
     cls_rule_init(&rule, &match, priority);
 
     /* Find the exact rule. */
-    cr = classifier_find_rule_exactly(&cls, &rule, CLS_MAX_VERSION);
+    cr = classifier_find_rule_exactly(&cls, &rule, OVS_VERSION_MAX);
     if (cr) {
         ovs_mutex_lock(&mutex);
         res = __rt_entry_delete(cr);
         ovs_mutex_unlock(&mutex);
     }
+
+    cls_rule_destroy(&rule);
     return res;
 }
 
@@ -276,34 +311,50 @@ static void
 ovs_router_add(struct unixctl_conn *conn, int argc,
               const char *argv[], void *aux OVS_UNUSED)
 {
-    ovs_be32 ip;
-    unsigned int plen;
+    struct in6_addr gw6 = in6addr_any;
     struct in6_addr ip6;
-    struct in6_addr gw6;
+    uint32_t mark = 0;
+    unsigned int plen;
+    ovs_be32 ip;
     int err;
 
     if (scan_ipv4_route(argv[1], &ip, &plen)) {
         ovs_be32 gw = 0;
-        if (argc > 3 && !ip_parse(argv[3], &gw)) {
-            unixctl_command_reply_error(conn, "Invalid gateway");
-            return;
+
+        if (argc > 3) {
+            if (!ovs_scan(argv[3], "pkt_mark=%"SCNi32, &mark) &&
+                !ip_parse(argv[3], &gw)) {
+                unixctl_command_reply_error(conn, "Invalid pkt_mark or gateway");
+                return;
+            }
         }
         in6_addr_set_mapped_ipv4(&ip6, ip);
-        in6_addr_set_mapped_ipv4(&gw6, gw);
+        if (gw) {
+            in6_addr_set_mapped_ipv4(&gw6, gw);
+        }
         plen += 96;
     } else if (scan_ipv6_route(argv[1], &ip6, &plen)) {
-        gw6 = in6addr_any;
-        if (argc > 3 && !ipv6_parse(argv[3], &gw6)) {
-            unixctl_command_reply_error(conn, "Invalid IPv6 gateway");
-            return;
+        if (argc > 3) {
+            if (!ovs_scan(argv[3], "pkt_mark=%"SCNi32, &mark) &&
+                !ipv6_parse(argv[3], &gw6)) {
+                unixctl_command_reply_error(conn, "Invalid pkt_mark or IPv6 gateway");
+                return;
+            }
         }
     } else {
         unixctl_command_reply_error(conn, "Invalid parameters");
         return;
     }
-    err = ovs_router_insert__(plen + 32, &ip6, plen, argv[2], &gw6);
+    if (argc > 4) {
+        if (!ovs_scan(argv[4], "pkt_mark=%"SCNi32, &mark)) {
+            unixctl_command_reply_error(conn, "Invalid pkt_mark");
+            return;
+        }
+    }
+
+    err = ovs_router_insert__(mark, plen + 32, &ip6, plen, argv[2], &gw6);
     if (err) {
-        unixctl_command_reply(conn, "Error while inserting route.");
+        unixctl_command_reply_error(conn, "Error while inserting route.");
     } else {
         unixctl_command_reply(conn, "OK");
     }
@@ -313,9 +364,10 @@ 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;
     struct in6_addr ip6;
+    uint32_t mark = 0;
+    unsigned int plen;
+    ovs_be32 ip;
 
     if (scan_ipv4_route(argv[1], &ip, &plen)) {
         in6_addr_set_mapped_ipv4(&ip6, ip);
@@ -324,7 +376,14 @@ ovs_router_del(struct unixctl_conn *conn, int argc OVS_UNUSED,
         unixctl_command_reply_error(conn, "Invalid parameters");
         return;
     }
-    if (rt_entry_delete(plen + 32, &ip6, plen)) {
+    if (argc > 2) {
+        if (!ovs_scan(argv[2], "pkt_mark=%"SCNi32, &mark)) {
+            unixctl_command_reply_error(conn, "Invalid pkt_mark");
+            return;
+        }
+    }
+
+    if (rt_entry_delete(mark, plen + 32, &ip6, plen)) {
         unixctl_command_reply(conn, "OK");
         seq_change(tnl_conf_seq);
     } else {
@@ -352,7 +411,12 @@ ovs_router_show(struct unixctl_conn *conn, int argc OVS_UNUSED,
         if (IN6_IS_ADDR_V4MAPPED(&rt->nw_addr)) {
             plen -= 96;
         }
-        ds_put_format(&ds, "/%"PRIu16" dev %s", plen, rt->output_bridge);
+        ds_put_format(&ds, "/%"PRIu8, plen);
+        if (rt->mark) {
+            ds_put_format(&ds, " MARK %"PRIu32, rt->mark);
+        }
+
+        ds_put_format(&ds, " dev %s", rt->output_bridge);
         if (ipv6_addr_is_set(&rt->gw)) {
             ds_put_format(&ds, " GW ");
             ipv6_format_mapped(&rt->gw, &ds);
@@ -366,14 +430,15 @@ ovs_router_show(struct unixctl_conn *conn, int argc OVS_UNUSED,
 }
 
 static void
-ovs_router_lookup_cmd(struct unixctl_conn *conn, int argc OVS_UNUSED,
+ovs_router_lookup_cmd(struct unixctl_conn *conn, int argc,
                       const char *argv[], void *aux OVS_UNUSED)
 {
-    ovs_be32 ip;
+    struct in6_addr gw, src;
+    char iface[IFNAMSIZ];
     struct in6_addr ip6;
     unsigned int plen;
-    char iface[IFNAMSIZ];
-    struct in6_addr gw, src;
+    uint32_t mark = 0;
+    ovs_be32 ip;
 
     if (scan_ipv4_route(argv[1], &ip, &plen) && plen == 32) {
         in6_addr_set_mapped_ipv4(&ip6, ip);
@@ -381,12 +446,18 @@ ovs_router_lookup_cmd(struct unixctl_conn *conn, int argc OVS_UNUSED,
         unixctl_command_reply_error(conn, "Invalid parameters");
         return;
     }
-
-    if (ovs_router_lookup(&ip6, iface, &src, &gw)) {
+    if (argc > 2) {
+        if (!ovs_scan(argv[2], "pkt_mark=%"SCNi32, &mark)) {
+            unixctl_command_reply_error(conn, "Invalid pkt_mark");
+            return;
+        }
+    }
+    if (ovs_router_lookup(mark, &ip6, iface, &src, &gw)) {
         struct ds ds = DS_EMPTY_INITIALIZER;
+
         ds_put_format(&ds, "src ");
         ipv6_format_mapped(&src, &ds);
-        ds_put_format(&ds, "gateway ");
+        ds_put_format(&ds, "\ngateway ");
         ipv6_format_mapped(&gw, &ds);
         ds_put_format(&ds, "\ndev %s\n", iface);
         unixctl_command_reply(conn, ds_cstr(&ds));
@@ -418,11 +489,11 @@ void
 ovs_router_init(void)
 {
     classifier_init(&cls, NULL);
-    unixctl_command_register("ovs/route/add", "ip_addr/prefix_len out_br_name gw", 2, 3,
+    unixctl_command_register("ovs/route/add", "ip_addr/prefix_len out_br_name [gw] [pkt_mark=mark]", 2, 4,
                              ovs_router_add, NULL);
     unixctl_command_register("ovs/route/show", "", 0, 0, ovs_router_show, NULL);
-    unixctl_command_register("ovs/route/del", "ip_addr/prefix_len", 1, 1, ovs_router_del,
-                             NULL);
-    unixctl_command_register("ovs/route/lookup", "ip_addr", 1, 1,
+    unixctl_command_register("ovs/route/del", "ip_addr/prefix_len [pkt_mark=mark]", 1, 2,
+                             ovs_router_del, NULL);
+    unixctl_command_register("ovs/route/lookup", "ip_addr [pkt_mark=mark]", 1, 2,
                              ovs_router_lookup_cmd, NULL);
 }