[ovs-dev] [PATCH] ovn-northd: Add support for static_routes.
Mickey Spiegel
emspiege at us.ibm.com
Thu Apr 21 16:00:05 UTC 2016
For the case where the static route specifies the output_port (logical router port), this patch is not as efficient and streamlined as it could be.
With this patch, in ovn/ovn-nb.ovsschema, the output_port is defined as a string that consists of a uuid:
+ "Logical_Router_Static_Route": {
+ "columns": {
+ "prefix": {"type": "string"},
+ "nexthop": {"type": "string"},
+ "output_port": {"type": "string"}},
Since output_port is a uuid but the ovn/northd/ovn-northd.c ports hmap has key name, in the for (size_t i = 0; i < od->nbr->n_ports; i++) loop in join_logical_ports, this patch walks SHASH_FOR_EACH(node, &od->routes_map) looking for a matching output_port uuid.
Instead the output port should be defined as:
+ "output_port": {"type": {"key": "string", "min": 0, "max": 1}},
just like Logical_Router_Port peer, where the string consists of a name rather than a uuid.
Then the code can use ovn_port_find.
Mickey
-----"dev" <dev-bounces at openvswitch.org> wrote: -----
To: dev at openvswitch.org
From: "steve.ruan"
Sent by: "dev"
Date: 04/20/2016 06:38PM
Cc: Guru Shetty <guru at ovn.org>
Subject: [ovs-dev] [PATCH] ovn-northd: Add support for static_routes.
From: Guru Shetty <guru at ovn.org>
static routes are useful when connecting multiple
routers with each other.
Signed-off-by: steve.ruan <ruansx at cn.ibm.com>
Signed-off-by: Gurucharan Shetty <guru at ovn.org>
Co-authored-by: Gurucharan Shetty <guru at ovn.org>
Reported-by: Na Zhu <nazhu at cn.ibm.com>
Reported-by: Dustin Lundquist <dlundquist at linux.vnet.ibm.com>
Reported-at:
https://bugs.launchpad.net/networking-ovn/+bug/1545140
https://bugs.launchpad.net/networking-ovn/+bug/1539347
---
ovn/northd/ovn-northd.8.xml | 5 +-
ovn/northd/ovn-northd.c | 101 +++++++++++++++++++++
ovn/ovn-nb.ovsschema | 15 +++-
ovn/ovn-nb.xml | 31 +++++++
ovn/utilities/ovn-nbctl.8.xml | 5 ++
ovn/utilities/ovn-nbctl.c | 5 ++
tests/ovn.at | 204 ++++++++++++++++++++++++++++++++++++++++++
7 files changed, 362 insertions(+), 4 deletions(-)
diff --git a/ovn/northd/ovn-northd.8.xml b/ovn/northd/ovn-northd.8.xml
index da776e1..978853c 100644
--- a/ovn/northd/ovn-northd.8.xml
+++ b/ovn/northd/ovn-northd.8.xml
@@ -682,8 +682,9 @@ next;
</p>
<p>
- If the route has a gateway, <var>G</var> is the gateway IP address,
- otherwise it is <code>ip4.dst</code>.
+ If the route has a gateway, <var>G</var> is the gateway IP address.
+ Instead, if the route is from a configured static route, <var>G</var>
+ is the next hop IP address. Else it is <code>ip4.dst</code>.
</p>
</li>
diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c
index 260c02f..5d86219 100644
--- a/ovn/northd/ovn-northd.c
+++ b/ovn/northd/ovn-northd.c
@@ -234,6 +234,18 @@ allocate_tnlid(struct hmap *set, const char *name, uint32_t max,
return 0;
}
+/* Holds the next hop ip address and the logical router port via which
+ * a static route is reachable. */
+struct route_to_port {
+ ovs_be32 ip; /* network address of the route. */
+ ovs_be32 mask; /* network mask of the route. */
+ ovs_be32 next_hop; /* next_hop ip address for the above route. */
+ struct uuid rport; /* output port specified by CMS, or null if not specified */
+ struct ovn_port *ovn_port; /* The logical router port via which the packet
+ * needs to exit to reach the next hop. */
+};
+
+
/* The 'key' comes from nbs->header_.uuid or nbr->header_.uuid or
* sb->external_ids:logical-switch. */
struct ovn_datapath {
@@ -249,6 +261,9 @@ struct ovn_datapath {
/* Logical router data (digested from nbr). */
const struct ovn_port *gateway_port;
ovs_be32 gateway;
+ /* Maps a static route to a ovn logical router port via which packet
+ * needs to exit. */
+ struct shash routes_map;
/* Logical switch data. */
struct ovn_port **router_ports;
@@ -272,6 +287,7 @@ ovn_datapath_create(struct hmap *datapaths, const struct uuid *key,
od->nbs = nbs;
od->nbr = nbr;
hmap_init(&od->port_tnlids);
+ shash_init(&od->routes_map);
od->port_key_hint = 0;
hmap_insert(datapaths, &od->key_node, uuid_hash(&od->key));
return od;
@@ -286,6 +302,7 @@ ovn_datapath_destroy(struct hmap *datapaths, struct ovn_datapath *od)
* use it. */
hmap_remove(datapaths, &od->key_node);
destroy_tnlids(&od->port_tnlids);
+ shash_destroy_free_data(&od->routes_map);
free(od->router_ports);
free(od);
}
@@ -318,6 +335,50 @@ ovn_datapath_from_sbrec(struct hmap *datapaths,
}
static void
+build_static_route(struct ovn_datapath *od,
+ const struct nbrec_logical_router_static_route *route)
+{
+ ovs_be32 prefix, next_hop, mask;
+
+ /* verify nexthop */
+ 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 prefix */
+ error = ip_parse_masked(route->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->prefix);
+ free(error);
+ return;
+ }
+
+ struct uuid lrp_uuid;
+ struct route_to_port *route_port = xmalloc(sizeof *route_port);
+ route_port->ip = prefix;
+ route_port->mask = mask;
+ route_port->next_hop = next_hop;
+ /* The correct ovn_port will be filled while traversing
+ * logical_router_ports. */
+ route_port->ovn_port = NULL;
+ memset(&route_port->rport, 0, sizeof (struct uuid));
+ if (route->output_port
+ && uuid_from_string(&lrp_uuid, route->output_port)){
+ memcpy(&route_port->rport, &lrp_uuid, sizeof (struct uuid));
+ }
+ shash_add(&od->routes_map, route->prefix, route_port);
+}
+
+static void
join_datapaths(struct northd_context *ctx, struct hmap *datapaths,
struct ovs_list *sb_only, struct ovs_list *nb_only,
struct ovs_list *both)
@@ -409,6 +470,14 @@ join_datapaths(struct northd_context *ctx, struct hmap *datapaths,
/* Set the gateway port to NULL. If there is a gateway, it will get
* filled in as we go through the ports later. */
od->gateway_port = NULL;
+
+ /* Handle static routes set in this logical router. */
+ for (int i = 0; i < nbr->n_static_routes; i++) {
+ const struct nbrec_logical_router_static_route *route;
+
+ route = od->nbr->static_routes[i];
+ build_static_route(od, route);
+ }
}
}
@@ -644,6 +713,29 @@ join_logical_ports(struct northd_context *ctx,
od->gateway_port = op;
}
}
+
+ /* Go through the static routes of the router to see which
+ * route is reachable via this logical router port. */
+ struct shash_node *node;
+ SHASH_FOR_EACH(node, &od->routes_map) {
+ struct route_to_port *route_port = node->data;
+
+ if (uuid_equals(&route_port->rport, &nbr->header_.uuid)){
+ route_port->ovn_port = op;
+ }
+ else{
+ /* If 'od' has a static route and 'op' routes to it... */
+ if (!((op->network ^ route_port->next_hop) & op->mask)) {
+ /* .....and if 'op' is a longer match than the current
+ * choice... */
+ const struct ovn_port *curr = route_port->ovn_port;
+ int len = curr ? ip_count_cidr_bits(curr->mask) : 0;
+ if (ip_count_cidr_bits(op->mask) > len) {
+ route_port->ovn_port = op;
+ }
+ }
+ }
+ }
}
}
}
@@ -1986,6 +2078,15 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
continue;
}
+ struct shash_node *node;
+ SHASH_FOR_EACH(node, &od->routes_map) {
+ struct route_to_port *route_port = node->data;
+ if (route_port->ovn_port) {
+ add_route(lflows, route_port->ovn_port, route_port->ip,
+ route_port->mask, route_port->next_hop);
+ }
+ }
+
if (od->gateway && od->gateway_port) {
add_route(lflows, od->gateway_port, 0, 0, od->gateway);
}
diff --git a/ovn/ovn-nb.ovsschema b/ovn/ovn-nb.ovsschema
index 40a7a97..ce2e72d 100644
--- a/ovn/ovn-nb.ovsschema
+++ b/ovn/ovn-nb.ovsschema
@@ -1,7 +1,7 @@
{
"name": "OVN_Northbound",
- "version": "2.0.2",
- "cksum": "4289495412 4436",
+ "version": "2.0.3",
+ "cksum": "3119329675 4999",
"tables": {
"Logical_Switch": {
"columns": {
@@ -72,6 +72,11 @@
"min": 0,
"max": "unlimited"}},
"default_gw": {"type": {"key": "string", "min": 0, "max": 1}},
+ "static_routes": {"type": {"key": {"type": "uuid",
+ "refTable": "Logical_Router_Static_Route",
+ "refType": "strong"},
+ "min": 0,
+ "max": "unlimited"}},
"external_ids": {
"type": {"key": "string", "value": "string",
"min": 0, "max": "unlimited"}}},
@@ -87,6 +92,12 @@
"type": {"key": "string", "value": "string",
"min": 0, "max": "unlimited"}}},
"indexes": [["name"]],
+ "isRoot": false},
+ "Logical_Router_Static_Route": {
+ "columns": {
+ "prefix": {"type": "string"},
+ "nexthop": {"type": "string"},
+ "output_port": {"type": "string"}},
"isRoot": false}
}
}
diff --git a/ovn/ovn-nb.xml b/ovn/ovn-nb.xml
index e65bc3a..9e2c742 100644
--- a/ovn/ovn-nb.xml
+++ b/ovn/ovn-nb.xml
@@ -627,6 +627,10 @@
IP address to use as default gateway, if any.
</column>
+ <column name="static_routes">
+ One or more static routes, mapping IP prefixes to next hop IP addresses.
+ </column>
+
<group title="Common Columns">
<column name="external_ids">
See <em>External IDs</em> at the beginning of this document.
@@ -717,4 +721,31 @@
</column>
</group>
</table>
+
+ <table name="Logical_Router_Static_Route" title="logical router static routes">
+ <p>
+ Each route represents a static route.
+ </p>
+
+ <column name="prefix">
+ <p>
+ Prefix of this route, example 192.168.100.0/24
+ </p>
+ </column>
+
+ <column name="nexthop">
+ <p>
+ Nexthop of this route, nexthop can be a IP address of neutron port, or
+ IP address which has been learnt by dynamic ARP
+ </p>
+ </column>
+
+ <column name="output_port">
+ <p>
+ Port ID of the logical router port table. It may be configured or may
+ not be configured
+ </p>
+ </column>
+ </table>
+
</database>
diff --git a/ovn/utilities/ovn-nbctl.8.xml b/ovn/utilities/ovn-nbctl.8.xml
index 3b337dd..f6ef803 100644
--- a/ovn/utilities/ovn-nbctl.8.xml
+++ b/ovn/utilities/ovn-nbctl.8.xml
@@ -231,6 +231,11 @@
A port within an L3 logical router. Records may be identified by name.
</dd>
+ <dt><code>Logical_Router_Static_Route</code></dt>
+ <dd>
+ A static route belonging to an L3 logical router.
+ </dd>
+
</dl>
<xi:include href="lib/db-ctl-base.xml" xmlns:xi="http://www.w3.org/2003/XInclude"/>
diff --git a/ovn/utilities/ovn-nbctl.c b/ovn/utilities/ovn-nbctl.c
index bdad368..bb3ce3e 100644
--- a/ovn/utilities/ovn-nbctl.c
+++ b/ovn/utilities/ovn-nbctl.c
@@ -1101,6 +1101,11 @@ static const struct ctl_table_class tables[] = {
NULL},
{NULL, NULL, NULL}}},
+ {&nbrec_table_logical_router_static_route,
+ {{&nbrec_table_logical_router_static_route, NULL,
+ NULL},
+ {NULL, NULL, NULL}}},
+
{NULL, {{NULL, NULL, NULL}, {NULL, NULL, NULL}}}
};
,
diff --git a/tests/ovn.at b/tests/ovn.at
index 6fea4e0..af3e1c4 100644
--- a/tests/ovn.at
+++ b/tests/ovn.at
@@ -2192,3 +2192,207 @@ OVS_APP_EXIT_AND_WAIT([ovs-vswitchd])
OVS_APP_EXIT_AND_WAIT([ovsdb-server])
AT_CLEANUP
+
+AT_SETUP([ovn -- 2 HVs, 3 LS, 1 lport/LS, 2 peer LRs, static routes])
+AT_KEYWORDS([ovnstaticroutespeer])
+AT_SKIP_IF([test $HAVE_PYTHON = no])
+ovn_start
+
+# Logical network:
+# Two LRs - R1 and R2 that are connected to each other as peers in 20.0.0.0/24
+# network. R1 has switchess foo (192.168.1.0/24)
+# connected to it.
+# R2 has alice (172.16.1.0/24) and bob (172.16.2.0/24) connected to it.
+
+ovn-nbctl create Logical_Router name=R1
+ovn-nbctl create Logical_Router name=R2
+
+ovn-nbctl lswitch-add foo
+ovn-nbctl lswitch-add alice
+ovn-nbctl lswitch-add bob
+
+# Connect foo to R1
+ovn-nbctl -- --id=@lrp create Logical_Router_port name=foo \
+network=192.168.1.1/24 mac=\"00:00:00:01:02:03\" -- add Logical_Router R1 \
+ports @lrp -- lport-add foo rp-foo
+
+ovn-nbctl set Logical_port rp-foo type=router options:router-port=foo \
+addresses=\"00:00:00:01:02:03\"
+
+# Connect alice to R2
+ovn-nbctl -- --id=@lrp create Logical_Router_port name=alice \
+network=172.16.1.1/24 mac=\"00:00:00:01:02:04\" -- add Logical_Router R2 \
+ports @lrp -- lport-add alice rp-alice
+
+ovn-nbctl set Logical_port rp-alice type=router options:router-port=alice \
+addresses=\"00:00:00:01:02:04\"
+
+# Connect bob to R2
+ovn-nbctl -- --id=@lrp create Logical_Router_port name=bob \
+network=172.16.2.1/24 mac=\"00:00:00:01:02:05\" -- add Logical_Router R2 \
+ports @lrp -- lport-add bob rp-bob
+
+ovn-nbctl set Logical_port rp-bob type=router options:router-port=bob \
+addresses=\"00:00:00:01:02:05\"
+
+# Connect R1 to R2
+lrp1_uuid=`ovn-nbctl -- --id=@lrp create Logical_Router_port name=R1_R2 \
+network="20.0.0.1/24" mac=\"00:00:00:02:03:04\" \
+-- add Logical_Router R1 ports @lrp`
+
+lrp2_uuid=`ovn-nbctl -- --id=@lrp create Logical_Router_port name=R2_R1 \
+network="20.0.0.2/24" mac=\"00:00:00:02:03:05\" \
+-- add Logical_Router R2 ports @lrp`
+
+ovn-nbctl set logical_router_port $lrp1_uuid peer="R2_R1"
+ovn-nbctl set logical_router_port $lrp2_uuid peer="R1_R2"
+
+#install static routes
+ovn-nbctl -- --id=@lrt create Logical_Router_Static_Route \
+prefix=172.16.1.0/24 nexthop=20.0.0.2 -- add Logical_Router \
+R1 static_routes @lrt
+
+ovn-nbctl -- --id=@lrt create Logical_Router_Static_Route \
+prefix=172.16.2.0/24 nexthop=20.0.0.2 -- add Logical_Router \
+R1 static_routes @lrt
+
+ovn-nbctl -- --id=@lrt create Logical_Router_Static_Route \
+prefix=192.168.1.0/24 nexthop=20.0.0.1 -- add Logical_Router \
+R2 static_routes @lrt
+
+# Create logical port foo1 in foo
+ovn-nbctl lport-add foo foo1 \
+-- lport-set-addresses foo1 "f0:00:00:01:02:03 192.168.1.2"
+
+# Create logical port alice1 in alice
+ovn-nbctl lport-add alice alice1 \
+-- lport-set-addresses alice1 "f0:00:00:01:02:04 172.16.1.2"
+
+# Create logical port bob1 in bob
+ovn-nbctl lport-add bob bob1 \
+-- lport-set-addresses bob1 "f0:00:00:01:02:05 172.16.2.2"
+
+# Create two hypervisor and create OVS ports corresponding to logical ports.
+net_add n1
+
+sim_add hv1
+as hv1
+ovs-vsctl add-br br-phys
+ovn_attach n1 br-phys 192.168.0.1
+ovs-vsctl -- add-port br-int hv1-vif1 -- \
+ set interface hv1-vif1 external-ids:iface-id=foo1 \
+ options:tx_pcap=hv1/vif1-tx.pcap \
+ options:rxq_pcap=hv1/vif1-rx.pcap \
+ ofport-request=1
+
+ovs-vsctl -- add-port br-int hv1-vif2 -- \
+ set interface hv1-vif2 external-ids:iface-id=alice1 \
+ options:tx_pcap=hv1/vif2-tx.pcap \
+ options:rxq_pcap=hv1/vif2-rx.pcap \
+ ofport-request=2
+
+sim_add hv2
+as hv2
+ovs-vsctl add-br br-phys
+ovn_attach n1 br-phys 192.168.0.2
+ovs-vsctl -- add-port br-int hv2-vif1 -- \
+ set interface hv2-vif1 external-ids:iface-id=bob1 \
+ options:tx_pcap=hv2/vif1-tx.pcap \
+ options:rxq_pcap=hv2/vif1-rx.pcap \
+ ofport-request=1
+
+
+# Pre-populate the hypervisors' ARP tables so that we don't lose any
+# packets for ARP resolution (native tunneling doesn't queue packets
+# for ARP resolution).
+ovn_populate_arp
+
+# Allow some time for ovn-northd and ovn-controller to catch up.
+# XXX This should be more systematic.
+sleep 1
+
+ip_to_hex() {
+ printf "%02x%02x%02x%02x" "$@"
+}
+trim_zeros() {
+ sed 's/\(00\)\{1,\}$//'
+}
+
+# Send ip packets between foo1 and alice1
+src_mac="f00000010203"
+dst_mac="000000010203"
+src_ip=`ip_to_hex 192 168 1 2`
+dst_ip=`ip_to_hex 172 16 1 2`
+packet=${dst_mac}${src_mac}08004500001c0000000040110000${src_ip}${dst_ip}0035111100080000
+as hv1 ovs-appctl netdev-dummy/receive hv1-vif1 $packet
+
+# Send ip packets between foo1 and bob1
+src_mac="f00000010203"
+dst_mac="000000010203"
+src_ip=`ip_to_hex 192 168 1 2`
+dst_ip=`ip_to_hex 172 16 2 2`
+packet=${dst_mac}${src_mac}08004500001c0000000040110000${src_ip}${dst_ip}0035111100080000
+as hv1 ovs-appctl netdev-dummy/receive hv1-vif1 $packet
+
+echo "---------NB dump-----"
+ovn-nbctl show
+echo "---------------------"
+ovn-nbctl list logical_router
+echo "---------------------"
+ovn-nbctl list logical_router_port
+echo "---------------------"
+
+echo "---------SB dump-----"
+ovn-sbctl list datapath_binding
+echo "---------------------"
+ovn-sbctl list port_binding
+echo "---------------------"
+
+echo "------ hv1 dump ----------"
+as hv1 ovs-ofctl dump-flows br-int
+echo "------ hv2 dump ----------"
+as hv2 ovs-ofctl dump-flows br-int
+
+# Packet to Expect at bob1
+src_mac="000000010205"
+dst_mac="f00000010205"
+src_ip=`ip_to_hex 192 168 1 2`
+dst_ip=`ip_to_hex 172 16 2 2`
+expected=${dst_mac}${src_mac}08004500001c000000003e110200${src_ip}${dst_ip}0035111100080000
+
+$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv2/vif1-tx.pcap | trim_zeros > received.packets
+echo $expected | trim_zeros > expout
+AT_CHECK([cat received.packets], [0], [expout])
+
+# Packet to Expect at alice1
+src_mac="000000010204"
+dst_mac="f00000010204"
+src_ip=`ip_to_hex 192 168 1 2`
+dst_ip=`ip_to_hex 172 16 1 2`
+expected=${dst_mac}${src_mac}08004500001c000000003e110200${src_ip}${dst_ip}0035111100080000
+
+$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap | trim_zeros > received1.packets
+echo $expected | trim_zeros > expout
+AT_CHECK([cat received1.packets], [0], [expout])
+
+for sim in hv1 hv2; do
+ as $sim
+ OVS_APP_EXIT_AND_WAIT([ovn-controller])
+ OVS_APP_EXIT_AND_WAIT([ovs-vswitchd])
+ OVS_APP_EXIT_AND_WAIT([ovsdb-server])
+done
+
+as ovn-sb
+OVS_APP_EXIT_AND_WAIT([ovsdb-server])
+
+as ovn-nb
+OVS_APP_EXIT_AND_WAIT([ovsdb-server])
+
+as northd
+OVS_APP_EXIT_AND_WAIT([ovn-northd])
+
+as main
+OVS_APP_EXIT_AND_WAIT([ovs-vswitchd])
+OVS_APP_EXIT_AND_WAIT([ovsdb-server])
+
+AT_CLEANUP
--
2.5.0
_______________________________________________
dev mailing list
dev at openvswitch.org
http://openvswitch.org/mailman/listinfo/dev
More information about the dev
mailing list