]> git.proxmox.com Git - mirror_ovs.git/blobdiff - ovn/northd/ovn-northd.c
tests: Add a tunnel packet-out test.
[mirror_ovs.git] / ovn / northd / ovn-northd.c
index d0319d59fbdbb066a46361038fd670e3e3777401..44e9430e469de4639e297d05f628bbe166ce03eb 100644 (file)
@@ -29,6 +29,7 @@
 #include "ovn/lib/lex.h"
 #include "ovn/lib/ovn-nb-idl.h"
 #include "ovn/lib/ovn-sb-idl.h"
+#include "ovn/lib/ovn-util.h"
 #include "packets.h"
 #include "poll-loop.h"
 #include "smap.h"
@@ -955,109 +956,6 @@ ovn_lflow_destroy(struct hmap *lflows, struct ovn_lflow *lflow)
     }
 }
 
-struct ipv4_netaddr {
-    ovs_be32 addr;
-    unsigned int plen;
-};
-
-struct ipv6_netaddr {
-    struct in6_addr addr;
-    unsigned int plen;
-};
-
-struct lport_addresses {
-    struct eth_addr ea;
-    size_t n_ipv4_addrs;
-    struct ipv4_netaddr *ipv4_addrs;
-    size_t n_ipv6_addrs;
-    struct ipv6_netaddr *ipv6_addrs;
-};
-
-/*
- * Extracts the mac, ipv4 and ipv6 addresses from the input param 'address'
- * which should be of the format 'MAC [IP1 IP2 ..]" where IPn should be
- * a valid IPv4 or IPv6 address and stores them in the 'ipv4_addrs' and
- * 'ipv6_addrs' fields of input param 'laddrs'.
- * The caller has to free the 'ipv4_addrs' and 'ipv6_addrs' fields.
- * If input param 'store_ipv6' is true only then extracted ipv6 addresses
- * are stored in 'ipv6_addrs' fields.
- * Return true if at least 'MAC' is found in 'address', false otherwise.
- * Eg 1.
- * If 'address' = '00:00:00:00:00:01 10.0.0.4 fe80::ea2a:eaff:fe28:3390/64
- *                 30.0.0.3/23' and 'store_ipv6' = true
- * then returns true with laddrs->n_ipv4_addrs = 2, naddrs->n_ipv6_addrs = 1.
- *
- * Eg. 2
- * If 'address' = '00:00:00:00:00:01 10.0.0.4 fe80::ea2a:eaff:fe28:3390/64
- *                 30.0.0.3/23' and 'store_ipv6' = false
- * then returns true with laddrs->n_ipv4_addrs = 2, naddrs->n_ipv6_addrs = 0.
- *
- * Eg 3. If 'address' = '00:00:00:00:00:01 10.0.0.4 addr 30.0.0.4', then
- * returns true with laddrs->n_ipv4_addrs = 1 and laddrs->n_ipv6_addrs = 0.
- */
-static bool
-extract_lport_addresses(char *address, struct lport_addresses *laddrs,
-                        bool store_ipv6)
-{
-    char *buf = address;
-    int buf_index = 0;
-    char *buf_end = buf + strlen(address);
-    if (!ovs_scan_len(buf, &buf_index, ETH_ADDR_SCAN_FMT,
-                      ETH_ADDR_SCAN_ARGS(laddrs->ea))) {
-        return false;
-    }
-
-    ovs_be32 ip4;
-    struct in6_addr ip6;
-    unsigned int plen;
-    char *error;
-
-    laddrs->n_ipv4_addrs = 0;
-    laddrs->n_ipv6_addrs = 0;
-    laddrs->ipv4_addrs = NULL;
-    laddrs->ipv6_addrs = NULL;
-
-    /* Loop through the buffer and extract the IPv4/IPv6 addresses
-     * and store in the 'laddrs'. Break the loop if invalid data is found.
-     */
-    buf += buf_index;
-    while (buf < buf_end) {
-        buf_index = 0;
-        error = ip_parse_cidr_len(buf, &buf_index, &ip4, &plen);
-        if (!error) {
-            laddrs->n_ipv4_addrs++;
-            laddrs->ipv4_addrs = xrealloc(
-                laddrs->ipv4_addrs,
-                sizeof (struct ipv4_netaddr) * laddrs->n_ipv4_addrs);
-            laddrs->ipv4_addrs[laddrs->n_ipv4_addrs - 1].addr = ip4;
-            laddrs->ipv4_addrs[laddrs->n_ipv4_addrs - 1].plen = plen;
-            buf += buf_index;
-            continue;
-        }
-        free(error);
-        error = ipv6_parse_cidr_len(buf, &buf_index, &ip6, &plen);
-        if (!error && store_ipv6) {
-            laddrs->n_ipv6_addrs++;
-            laddrs->ipv6_addrs = xrealloc(
-                laddrs->ipv6_addrs,
-                sizeof(struct ipv6_netaddr) * laddrs->n_ipv6_addrs);
-            memcpy(&laddrs->ipv6_addrs[laddrs->n_ipv6_addrs - 1].addr, &ip6,
-                   sizeof(struct in6_addr));
-            laddrs->ipv6_addrs[laddrs->n_ipv6_addrs - 1].plen = plen;
-        }
-
-        if (error) {
-            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
-            VLOG_INFO_RL(&rl, "invalid syntax '%s' in address", address);
-            free(error);
-            break;
-        }
-        buf += buf_index;
-    }
-
-    return true;
-}
-
 /* Appends port security constraints on L2 address field 'eth_addr_field'
  * (e.g. "eth.src" or "eth.dst") to 'match'.  'port_security', with
  * 'n_port_security' elements, is the collection of port_security constraints
@@ -1826,6 +1724,74 @@ add_route(struct hmap *lflows, const struct ovn_port *op,
     free(match);
 }
 
+static void
+build_static_route_flow(struct hmap *lflows, struct ovn_datapath *od,
+                        struct hmap *ports,
+                        const struct nbrec_logical_router_static_route *route)
+{
+    ovs_be32 prefix, next_hop, mask;
+
+    /* Verify that next hop is an IP address with 32 bits mask. */
+    char *error = ip_parse_masked(route->nexthop, &next_hop, &mask);
+    if (error || mask != OVS_BE32_MAX) {
+        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
+        VLOG_WARN_RL(&rl, "bad next hop ip address %s", route->nexthop);
+        free(error);
+        return;
+    }
+
+    /* Verify that ip prefix is a valid CIDR address. */
+    error = ip_parse_masked(route->ip_prefix, &prefix, &mask);
+    if (error || !ip_is_cidr(mask)) {
+        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
+        VLOG_WARN_RL(&rl, "bad 'network' in static routes %s",
+                     route->ip_prefix);
+        free(error);
+        return;
+    }
+
+    /* Find the outgoing port. */
+    struct ovn_port *out_port = NULL;
+    if (route->output_port) {
+        out_port = ovn_port_find(ports, route->output_port);
+        if (!out_port) {
+            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
+            VLOG_WARN_RL(&rl, "Bad out port %s for static route %s",
+                         route->output_port, route->ip_prefix);
+            return;
+        }
+    } else {
+        /* output_port is not specified, find the
+         * router port matching the next hop. */
+        int i;
+        for (i = 0; i < od->nbr->n_ports; i++) {
+            struct nbrec_logical_router_port *lrp = od->nbr->ports[i];
+            out_port = ovn_port_find(ports, lrp->name);
+            if (!out_port) {
+                /* This should not happen. */
+                continue;
+            }
+
+            if (out_port->network
+                && !((out_port->network ^ next_hop) & out_port->mask)) {
+                /* There should be only 1 interface that matches the next hop.
+                 * Otherwise, it's a configuration error, because subnets of
+                 * router's interfaces should NOT overlap. */
+                break;
+            }
+        }
+        if (i == od->nbr->n_ports) {
+            /* There is no matched out port. */
+            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
+            VLOG_WARN_RL(&rl, "No path for static route %s; next hop %s",
+                         route->ip_prefix, route->nexthop);
+            return;
+        }
+    }
+
+    add_route(lflows, out_port, prefix, mask, next_hop);
+}
+
 static void
 build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
                     struct hmap *lflows)
@@ -1995,6 +1961,14 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
             continue;
         }
 
+        /* Convert the static routes to flows. */
+        for (int i = 0; i < od->nbr->n_static_routes; i++) {
+            const struct nbrec_logical_router_static_route *route;
+
+            route = od->nbr->static_routes[i];
+            build_static_route_flow(lflows, od, ports, route);
+        }
+
         if (od->gateway && od->gateway_port) {
             add_route(lflows, od->gateway_port, 0, 0, od->gateway);
         }