On Fri, May 6, 2016 at 12:21 PM, Gurucharan Shetty <g...@ovn.org> wrote:
> Currently we can connect routers via "peer"ing. This limits > the number of routers that can be connected with each other > directly to 2. > > One of the design goals for L3 Gateway is to be able to > have multiple gateways (each with their own router) > connected to a distributed router via a switch. > > With the above goal in mind, this commit gives the general > ability to connect multiple routers via a switch. > > Very interesting work! A minor suggestion/question on tests/ovn.at Given that the test is a little long -- and I can see why it needs to be -- would it be better if it was added in a separate [new] file, i.e. ovn-l3.at ? I ask because I see that this could be similar to what we have in regards to controller vtep: ovn-controller.at and ovn-controller-vtep.at -- flaviof Signed-off-by: Gurucharan Shetty <g...@ovn.org> > --- > This needs the following 2 commits under review to > first go in. > 1. ofproto-dpif: Rename "recurse" to "indentation". > 2. ofproto-dpif: Do not count resubmit to later tables against limit. > --- > ovn/controller/lflow.c | 19 ++-- > ovn/northd/ovn-northd.c | 56 ++++++++++- > ovn/ovn-nb.xml | 7 -- > tests/ovn.at | 236 > +++++++++++++++++++++++++++++++++++++++++++++++ > 4 files changed, 299 insertions(+), 19 deletions(-) > > diff --git a/ovn/controller/lflow.c b/ovn/controller/lflow.c > index 96b7c66..efc427d 100644 > --- a/ovn/controller/lflow.c > +++ b/ovn/controller/lflow.c > @@ -215,15 +215,15 @@ add_logical_flows(struct controller_ctx *ctx, const > struct lport_index *lports, > if (is_switch(ldp)) { > /* For a logical switch datapath, local_datapaths tells us if > there > * are any local ports for this datapath. If not, we can skip > - * processing logical flows if the flow belongs to egress > pipeline > - * or if that logical switch datapath is not patched to any > logical > - * router. > + * processing logical flows if that logical switch datapath > is not > + * patched to any logical router. > * > - * Otherwise, we still need the ingress pipeline because even > if > - * there are no local ports, we still may need to execute the > ingress > - * pipeline after a packet leaves a logical router. Further > - * optimization is possible, but not based on what we know > with > - * local_datapaths right now. > + * Otherwise, we still need both ingress and egress pipeline > + * because even if there are no local ports, we still may > need to > + * execute the ingress pipeline after a packet leaves a > logical > + * router and we need to do egress pipeline for a switch that > + * is connected to only routers. Further optimization is > possible, > + * but not based on what we know with local_datapaths right > now. > * > * A better approach would be a kind of "flood fill" > algorithm: > * > @@ -242,9 +242,6 @@ add_logical_flows(struct controller_ctx *ctx, const > struct lport_index *lports, > */ > > if (!get_local_datapath(local_datapaths, ldp->tunnel_key)) { > - if (!ingress) { > - continue; > - } > if (!get_patched_datapath(patched_datapaths, > ldp->tunnel_key)) { > continue; > diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c > index 9e03606..3a29770 100644 > --- a/ovn/northd/ovn-northd.c > +++ b/ovn/northd/ovn-northd.c > @@ -2110,7 +2110,13 @@ build_lrouter_flows(struct hmap *datapaths, struct > hmap *ports, > free(actions); > free(match); > } > - } else if (op->od->n_router_ports) { > + } else if (op->od->n_router_ports && strcmp(op->nbs->type, > "router")) { > + /* This is a logical switch port that backs a VM or a > container. > + * Extract its addresses. For each of the address, go through > all > + * the router ports attached to the switch (to which this port > + * connects) and if the address in question is reachable from > the > + * router port, add an ARP entry in that router's pipeline. */ > + > for (size_t i = 0; i < op->nbs->n_addresses; i++) { > struct lport_addresses laddrs; > if (!extract_lport_addresses(op->nbs->addresses[i], > &laddrs, > @@ -2158,8 +2164,56 @@ build_lrouter_flows(struct hmap *datapaths, struct > hmap *ports, > > free(laddrs.ipv4_addrs); > } > + } else if (!strcmp(op->nbs->type, "router")) { > + /* This is a logical switch port that connects to a router. */ > + > + /* The peer of this switch port is the router port for which > + * we need to add logical flows such that it can resolve > + * ARP entries for all the other router ports connected to > + * the switch in question. */ > + > + const char *peer_name = smap_get(&op->nbs->options, > + "router-port"); > + if (!peer_name) { > + continue; > + } > + > + struct ovn_port *peer = ovn_port_find(ports, peer_name); > + if (!peer || !peer->nbr || !peer->ip) { > + continue; > + } > + > + for (size_t j = 0; j < op->od->n_router_ports; j++) { > + const char *router_port_name = smap_get( > + > &op->od->router_ports[j]->nbs->options, > + "router-port"); > + struct ovn_port *router_port = ovn_port_find(ports, > + > router_port_name); > + if (!router_port || !router_port->nbr || > !router_port->ip) { > + continue; > + } > + > + /* Skip the router port under consideration. */ > + if (router_port == peer) { > + continue; > + } > + > + if (!router_port->ip) { > + continue; > + } > + char *match = xasprintf("outport == %s && reg0 == "IP_FMT, > + peer->json_key, > + IP_ARGS(router_port->ip)); > + char *actions = xasprintf("eth.dst = "ETH_ADDR_FMT"; > next;", > + > ETH_ADDR_ARGS(router_port->mac)); > + ovn_lflow_add(lflows, peer->od, S_ROUTER_IN_ARP_RESOLVE, > + 101, match, actions); > + free(actions); > + free(match); > + } > } > } > + > HMAP_FOR_EACH (od, key_node, datapaths) { > if (!od->nbr) { > continue; > diff --git a/ovn/ovn-nb.xml b/ovn/ovn-nb.xml > index c01455d..d7fd595 100644 > --- a/ovn/ovn-nb.xml > +++ b/ovn/ovn-nb.xml > @@ -154,13 +154,6 @@ > These options apply when <ref column="type"/> is > <code>router</code>. > </p> > > - <p> > - If a given logical switch has multiple <code>router</code> > ports, the > - <ref table="Logical_Router_Port"/> rows that they reference > must be > - all on the same <ref table="Logical_Router"/> (for different > - subnets). > - </p> > - > <column name="options" key="router-port"> > Required. The <ref column="name"/> of the <ref > table="Logical_Router_Port"/> to which this logical switch port > is > diff --git a/tests/ovn.at b/tests/ovn.at > index b9d7ada..6961be0 100644 > --- a/tests/ovn.at > +++ b/tests/ovn.at > @@ -2545,3 +2545,239 @@ OVS_APP_EXIT_AND_WAIT([ovsdb-server]) > > AT_CLEANUP > > +AT_SETUP([ovn -- 2 HVs, 3 LRs connected via LS, static routes]) > +AT_KEYWORDS([ovnstaticroutes]) > +AT_SKIP_IF([test $HAVE_PYTHON = no]) > +ovn_start > + > +# Logical network: > +# Three LRs - R1, R2 and R3 that are connected to each other via LS "join" > +# 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 R3 has bob ( > 10.32.1.0/24) > +# connected to it. > + > +ovn-nbctl create Logical_Router name=R1 > +ovn-nbctl create Logical_Router name=R2 > +ovn-nbctl create Logical_Router name=R3 > + > +ovn-nbctl lswitch-add foo > +ovn-nbctl lswitch-add alice > +ovn-nbctl lswitch-add bob > +ovn-nbctl lswitch-add join > + > +# Connect foo to R1 > +ovn-nbctl -- --id=@lrp create Logical_Router_port name=foo \ > +network=192.168.1.1/24 mac=\"00:00:01: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:01: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:02:01:02:03\" -- 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:02:01:02:03\" > + > +# Connect bob to R3 > +ovn-nbctl -- --id=@lrp create Logical_Router_port name=bob \ > +network=10.32.1.1/24 mac=\"00:00:03:01:02:03\" -- add Logical_Router R3 \ > +ports @lrp -- lport-add bob rp-bob > + > +ovn-nbctl set Logical_port rp-bob type=router options:router-port=bob \ > +addresses=\"00:00:03:01:02:03\" > + > +# Connect R1 to join > +ovn-nbctl -- --id=@lrp create Logical_Router_port name=R1_join \ > +network=20.0.0.1/24 mac=\"00:00:04:01:02:03\" -- add Logical_Router R1 \ > +ports @lrp -- lport-add join r1-join > + > +ovn-nbctl set Logical_port r1-join type=router > options:router-port=R1_join \ > +addresses='"00:00:04:01:02:03"' > + > +# Connect R2 to join > +ovn-nbctl -- --id=@lrp create Logical_Router_port name=R2_join \ > +network=20.0.0.2/24 mac=\"00:00:04:01:02:04\" -- add Logical_Router R2 \ > +ports @lrp -- lport-add join r2-join > + > +ovn-nbctl set Logical_port r2-join type=router > options:router-port=R2_join \ > +addresses='"00:00:04:01:02:04"' > + > + > +# Connect R3 to join > +ovn-nbctl -- --id=@lrp create Logical_Router_port name=R3_join \ > +network=20.0.0.3/24 mac=\"00:00:04:01:02:05\" -- add Logical_Router R3 \ > +ports @lrp -- lport-add join r3-join > + > +ovn-nbctl set Logical_port r3-join type=router > options:router-port=R3_join \ > +addresses='"00:00:04:01:02:05"' > + > +#install static routes > +ovn-nbctl -- --id=@lrt create Logical_Router_Static_Route \ > +ip_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 \ > +ip_prefix=10.32.1.0/24 nexthop=20.0.0.3 -- add Logical_Router \ > +R1 static_routes @lrt > + > +ovn-nbctl -- --id=@lrt create Logical_Router_Static_Route \ > +ip_prefix=192.168.1.0/24 nexthop=20.0.0.1 -- add Logical_Router \ > +R2 static_routes @lrt > + > +ovn-nbctl -- --id=@lrt create Logical_Router_Static_Route \ > +ip_prefix=10.32.1.0/24 nexthop=20.0.0.3 -- add Logical_Router \ > +R2 static_routes @lrt > + > +ovn-nbctl -- --id=@lrt create Logical_Router_Static_Route \ > +ip_prefix=192.168.1.0/24 nexthop=20.0.0.1 -- add Logical_Router \ > +R3 static_routes @lrt > + > +ovn-nbctl -- --id=@lrt create Logical_Router_Static_Route \ > +ip_prefix=172.16.1.0/24 nexthop=20.0.0.2 -- add Logical_Router \ > +R3 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 10.32.1.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="000001010203" > +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 > +as hv1 ovs-appctl ofproto/trace br-int in_port=1 $packet > + > +# Send ip packets between foo1 and bob1 > +src_mac="f00000010203" > +dst_mac="000001010203" > +src_ip=`ip_to_hex 192 168 1 2` > +dst_ip=`ip_to_hex 10 32 1 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 "---------------------" > +ovn-sbctl dump-flows > +echo "---------------------" > + > +echo "------ hv1 dump ----------" > +as hv1 ovs-ofctl show br-int > +as hv1 ovs-ofctl dump-flows br-int > +echo "------ hv2 dump ----------" > +as hv2 ovs-ofctl show br-int > +as hv2 ovs-ofctl dump-flows br-int > +echo "----------------------------" > + > +# Packet to Expect at bob1 > +src_mac="000003010203" > +dst_mac="f00000010205" > +src_ip=`ip_to_hex 192 168 1 2` > +dst_ip=`ip_to_hex 10 32 1 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="000002010203" > +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 > -- > 1.7.9.5 > > _______________________________________________ > dev mailing list > dev@openvswitch.org > http://openvswitch.org/mailman/listinfo/dev > _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev