On Sun, Apr 24, 2016 at 2:49 AM, steve.ruan <rsxi...@gmail.com> wrote:
> static routes are useful when connecting multiple > routers with each other. > Logical patch ports are used to connect logical routers together. Static routes are used to select between different logical router ports when exiting a logical router. > > Signed-off-by: steve.ruan <rua...@cn.ibm.com> > Co-authored-by: Gurucharan Shetty <g...@ovn.org> > > Reported-by: Na Zhu <na...@cn.ibm.com> > Reported-by: Dustin Lundquist <dlundqu...@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 | 81 +++++++++++++++++ > 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 | 203 > ++++++++++++++++++++++++++++++++++++++++++ > 7 files changed, 341 insertions(+), 4 deletions(-) > > diff --git a/ovn/northd/ovn-northd.8.xml b/ovn/northd/ovn-northd.8.xml > index 1cd8072..970c352 100644 > --- a/ovn/northd/ovn-northd.8.xml > +++ b/ovn/northd/ovn-northd.8.xml > @@ -689,8 +689,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 e3436da..e2c5a78 100644 > --- a/ovn/northd/ovn-northd.c > +++ b/ovn/northd/ovn-northd.c > @@ -1828,6 +1828,79 @@ add_route(struct hmap *lflows, const struct > ovn_port *op, > } > > static void > +build_static_route_flow(struct hmap *lflows, struct ovn_datapath *od, > + struct hmap *ports, > + const struct nbrec_logical_router_static_route *route) > +{ > + struct ovn_port *out_port, *op; > Both of the above variable definitions should be moved closer to their usage per coding standard > + ovs_be32 prefix, next_hop, mask; > + int len; > len is only used by the else below; per coding standard, it should be moved there > + > + /* 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; > + } > + > + /* find the outgoing port */ > + out_port = NULL; > + len = 0; > len is only used by the else below; per coding standard, it should be moved there > + if (route->output_port){ > I don't see this code path being tested by the below test case. This can be done by adding an output_port for the static route eg) +#install static routes +ovn-nbctl -- --id=@lrt create Logical_Router_Static_Route \ +prefix=172.16.1.0/24 nexthop=20.0.0.2 output_port=R1_R2-- add Logical_Router \ +R1 static_routes @lrt > + 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' in static routes %s", > + route->output_port); > Might be good to mention the route prefix; i.e. VLOG_WARN_RL(&rl, "Bad out port %s for static route %s", route->output_port, route->prefix); > + return; > + } > + } > + else{ /* output_port is not specified, then find the > + * router port with longest net mask match */ > + for (int i = 0; i < od->nbr->n_ports; i++){ > + > + struct nbrec_logical_router_port *lrp = od->nbr->ports[i]; > + op = ovn_port_find(ports, lrp->name); > + if (!op){ /* this should not happen */ > + continue; > + } > + > + if (op->network && !((op->network ^ next_hop) & op->mask)){ > + if (ip_count_cidr_bits(op->mask) > len){ > I don't think the longest prefix match decision is being tested by the below test since there is only a single logical router port to choose in each direction 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` > + len = ip_count_cidr_bits(op->mask); > + out_port = op; > + } > + } > + } > + if (!out_port){ > + static struct vlog_rate_limit rl > + = VLOG_RATE_LIMIT_INIT(5, 1); > + VLOG_WARN_RL(&rl, "no matched out port for next hop %s", > + route->nexthop); > If a route does not have a out_port (i.e. path), I think the route itself should be reported as well i.e. VLOG_WARN_RL(&rl, "No path for static route %s; next hop %s", route->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) > { > @@ -1996,6 +2069,14 @@ build_lrouter_flows(struct hmap *datapaths, struct > hmap *ports, > continue; > } > > + /* convert the routing table to flow */ > + 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); > } > diff --git a/ovn/ovn-nb.ovsschema b/ovn/ovn-nb.ovsschema > index e3e41e3..e67f903 100644 > --- a/ovn/ovn-nb.ovsschema > +++ b/ovn/ovn-nb.ovsschema > @@ -1,7 +1,7 @@ > { > "name": "OVN_Northbound", > - "version": "2.1.0", > - "cksum": "2201582413 4513", > + "version": "2.1.1", > + "cksum": "1773273858 5105", > "tables": { > "Logical_Switch": { > "columns": { > @@ -71,6 +71,11 @@ > "refType": "strong"}, > "min": 0, > "max": "unlimited"}}, > + "static_routes": {"type": {"key": {"type": "uuid", > + "refTable": > "Logical_Router_Static_Route", > + "refType": "strong"}, > + "min": 0, > + "max": "unlimited"}}, > "default_gw": {"type": {"key": "string", "min": 0, "max": > 1}}, > "enabled": {"type": {"key": "boolean", "min": 0, "max": > 1}}, > "external_ids": { > @@ -88,6 +93,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": {"key": "string", "min": 0, > "max": 1}}}, > "isRoot": false} > } > } > diff --git a/ovn/ovn-nb.xml b/ovn/ovn-nb.xml > index 843ae4c..b7091c2 100644 > --- a/ovn/ovn-nb.xml > +++ b/ovn/ovn-nb.xml > @@ -623,6 +623,10 @@ > The router's ports. > </column> > > + <column name="static_routes"> > + One or more static routes, refer to Logical_Router_Static_Route > table. > + </column> > + > <column name="default_gw"> > IP address to use as default gateway, if any. > </column> > @@ -724,4 +728,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 name 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 d28c985..997756e 100644 > --- a/tests/ovn.at > +++ b/tests/ovn.at > @@ -2344,3 +2344,206 @@ 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@openvswitch.org > http://openvswitch.org/mailman/listinfo/dev > _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev