From 3552e2fc575575566fbda9a2b075f7ff5b3ed3a7 Mon Sep 17 00:00:00 2001 From: Gurucharan Shetty Date: Tue, 4 Oct 2016 09:49:10 -0700 Subject: [PATCH] ovn-controller: Container can have connection to a hosting VM. A Container running inside a VM can have a connection to the hosting VM (parent port) in the logical topology (for e.g via a router). So we should be able to loop-back into the same VM, even if the final packet delivered does not have any tags in it. Reported-by: Dustin Spinhirne Signed-off-by: Gurucharan Shetty Acked-by: Ben Pfaff --- AUTHORS | 1 + ovn/controller/physical.c | 27 +++--- tests/ovn.at | 194 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 210 insertions(+), 12 deletions(-) diff --git a/AUTHORS b/AUTHORS index 9925b9f1835..48ec51b8781 100644 --- a/AUTHORS +++ b/AUTHORS @@ -321,6 +321,7 @@ Derek Cormier derek.cormier@lab.ntt.co.jp Dhaval Badiani dbadiani@vmware.com DK Moon dkmoon@nicira.com Ding Zhi zhi.ding@6wind.com +Dustin Spinhirne dspinhirne@vmware.com Edwin Chiu echiu@vmware.com Eivind Bulie Haanaes Enas Ahmad enas.ahmad@kaust.edu.sa diff --git a/ovn/controller/physical.c b/ovn/controller/physical.c index 1a9153179e6..c5866b4c6b2 100644 --- a/ovn/controller/physical.c +++ b/ovn/controller/physical.c @@ -211,6 +211,7 @@ consider_port_binding(enum mf_field_id mff_ovn_geneve, */ int tag = 0; + bool nested_container = false; ofp_port_t ofport; bool is_remote = false; if (binding->parent_port && *binding->parent_port) { @@ -221,6 +222,7 @@ consider_port_binding(enum mf_field_id mff_ovn_geneve, binding->parent_port)); if (ofport) { tag = *binding->tag; + nested_container = true; } } else { ofport = u16_to_ofp(simap_get(&localvif_to_ofport, @@ -288,6 +290,12 @@ consider_port_binding(enum mf_field_id mff_ovn_geneve, if (tag || !strcmp(binding->type, "localnet") || !strcmp(binding->type, "l2gateway")) { match_set_dl_vlan(&match, htons(tag)); + if (nested_container) { + /* When a packet comes from a container sitting behind a + * parent_port, we should let it loopback to other containers + * or the parent_port itself. */ + put_load(MLF_ALLOW_LOOPBACK, MFF_LOG_FLAGS, 0, 1, ofpacts_p); + } ofpact_put_STRIP_VLAN(ofpacts_p); } @@ -385,15 +393,18 @@ consider_port_binding(enum mf_field_id mff_ovn_geneve, * ======================= * * If the packet is supposed to hair-pin because the "loopback" - * flag is set, temporarily set the in_port to zero, resubmit to + * flag is set (or if the destination is a nested container), + * temporarily set the in_port to zero, resubmit to * table 65 for logical-to-physical translation, then restore * the port number. */ match_init_catchall(&match); ofpbuf_clear(ofpacts_p); match_set_metadata(&match, htonll(dp_key)); - match_set_reg_masked(&match, MFF_LOG_FLAGS - MFF_REG0, - MLF_ALLOW_LOOPBACK, MLF_ALLOW_LOOPBACK); match_set_reg(&match, MFF_LOG_OUTPORT - MFF_REG0, port_key); + if (!nested_container) { + match_set_reg_masked(&match, MFF_LOG_FLAGS - MFF_REG0, + MLF_ALLOW_LOOPBACK, MLF_ALLOW_LOOPBACK); + } put_stack(MFF_IN_PORT, ofpact_put_STACK_PUSH(ofpacts_p)); put_load(0, MFF_IN_PORT, 0, 16, ofpacts_p); @@ -417,22 +428,14 @@ consider_port_binding(enum mf_field_id mff_ovn_geneve, vlan_vid = ofpact_put_SET_VLAN_VID(ofpacts_p); vlan_vid->vlan_vid = tag; vlan_vid->push_vlan_if_needed = true; - - /* A packet might need to hair-pin back into its ingress - * OpenFlow port (to a different logical port, which we already - * checked back in table 34), so set the in_port to zero. */ - put_stack(MFF_IN_PORT, ofpact_put_STACK_PUSH(ofpacts_p)); - put_load(0, MFF_IN_PORT, 0, 16, ofpacts_p); } ofpact_put_OUTPUT(ofpacts_p)->port = ofport; if (tag) { /* Revert the tag added to the packets headed to containers * in the previous step. If we don't do this, the packets * that are to be broadcasted to a VM in the same logical - * switch will also contain the tag. Also revert the zero'd - * in_port. */ + * switch will also contain the tag. */ ofpact_put_STRIP_VLAN(ofpacts_p); - put_stack(MFF_IN_PORT, ofpact_put_STACK_POP(ofpacts_p)); } ofctrl_add_flow(flow_table, OFTABLE_LOG_TO_PHY, 100, &match, ofpacts_p); diff --git a/tests/ovn.at b/tests/ovn.at index f98ae5523ff..56d47e758bc 100644 --- a/tests/ovn.at +++ b/tests/ovn.at @@ -5330,3 +5330,197 @@ OVS_WAIT_UNTIL([test `ovs-vsctl show | grep "Port patch-br-int-to-ln_port" | wc OVN_CLEANUP([hv1],[hv2]) AT_CLEANUP + +AT_SETUP([ovn -- nested containers]) +ovn_start + +# Physical network: +# 2 HVs. HV1 has 2 VMs - "VM1" and "bar3". HV2 has 1 VM - "VM2" + +# Logical network: +# 3 Logical switches - "mgmt" (172.16.1.0/24), "foo" (192.168.1.0/24) +# and "bar" (192.168.2.0/24). They are all connected to router R1. + +ovn-nbctl lr-add R1 +ovn-nbctl ls-add mgmt +ovn-nbctl ls-add foo +ovn-nbctl ls-add bar + +# Connect mgmt to R1 +ovn-nbctl lrp-add R1 mgmt 00:00:00:01:02:02 172.16.1.1/24 +ovn-nbctl lsp-add mgmt rp-mgmt -- set Logical_Switch_Port rp-mgmt type=router \ + options:router-port=mgmt addresses=\"00:00:00:01:02:02\" + +# Connect foo to R1 +ovn-nbctl lrp-add R1 foo 00:00:00:01:02:03 192.168.1.1/24 +ovn-nbctl lsp-add foo rp-foo -- set Logical_Switch_Port rp-foo type=router \ + options:router-port=foo addresses=\"00:00:00:01:02:03\" + +# Connect bar to R1 +ovn-nbctl lrp-add R1 bar 00:00:00:01:02:04 192.168.2.1/24 +ovn-nbctl lsp-add bar rp-bar -- set Logical_Switch_Port rp-bar type=router \ + options:router-port=bar addresses=\"00:00:00:01:02:04\" + +# "mgmt" has VM1 and VM2 connected +ovn-nbctl lsp-add mgmt vm1 \ +-- lsp-set-addresses vm1 "f0:00:00:01:02:03 172.16.1.2" + +ovn-nbctl lsp-add mgmt vm2 \ +-- lsp-set-addresses vm2 "f0:00:00:01:02:04 172.16.1.3" + +# "foo1" and "foo2" are containers belonging to switch "foo" +# "foo1" has "VM1" as parent_port and "foo2" has "VM2" as parent_port. +ovn-nbctl lsp-add foo foo1 vm1 1 \ +-- lsp-set-addresses foo1 "f0:00:00:01:02:05 192.168.1.2" + +ovn-nbctl lsp-add foo foo2 vm2 2 \ +-- lsp-set-addresses foo2 "f0:00:00:01:02:06 192.168.1.3" + +# "bar1" and "bar2" are containers belonging to switch "bar" +# "bar1" has "VM1" as parent_port and "bar2" has "VM2" as parent_port. +ovn-nbctl lsp-add bar bar1 vm1 2 \ +-- lsp-set-addresses bar1 "f0:00:00:01:02:07 192.168.2.2" + +ovn-nbctl lsp-add bar bar2 vm2 1 \ +-- lsp-set-addresses bar2 "f0:00:00:01:02:08 192.168.2.3" + +# bar3 is a standalone VM belonging to switch "bar" +ovn-nbctl lsp-add bar bar3 \ +-- lsp-set-addresses bar3 "f0:00:00:01:02:09 192.168.2.4" + +# 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 vm1 -- \ + set interface vm1 external-ids:iface-id=vm1 \ + options:tx_pcap=hv1/vm1-tx.pcap \ + options:rxq_pcap=hv1/vm1-rx.pcap \ + ofport-request=1 + +ovs-vsctl -- add-port br-int bar3 -- \ + set interface bar3 external-ids:iface-id=bar3 \ + options:tx_pcap=hv1/bar3-tx.pcap \ + options:rxq_pcap=hv1/bar3-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 vm2 -- \ + set interface vm2 external-ids:iface-id=vm2 \ + options:tx_pcap=hv2/vm2-tx.pcap \ + options:rxq_pcap=hv2/vm2-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" "$@" +} + +# Send ip packets between foo1 and foo2 (same switch, different HVs and +# different VLAN tags). +src_mac="f00000010205" +dst_mac="f00000010206" +src_ip=`ip_to_hex 192 168 1 2` +dst_ip=`ip_to_hex 192 168 1 3` +packet=${dst_mac}${src_mac}8100000108004500001c0000000040110000${src_ip}${dst_ip}0035111100080000 +as hv1 ovs-appctl netdev-dummy/receive vm1 $packet + +# expected packet at foo2 +packet=${dst_mac}${src_mac}8100000208004500001c0000000040110000${src_ip}${dst_ip}0035111100080000 +echo $packet > expected +OVN_CHECK_PACKETS([hv2/vm2-tx.pcap], [expected]) + +# Send ip packets between foo1 and bar2 (different switch, different HV) +src_mac="f00000010205" +dst_mac="000000010203" +src_ip=`ip_to_hex 192 168 1 2` +dst_ip=`ip_to_hex 192 168 2 3` +packet=${dst_mac}${src_mac}8100000108004500001c0000000040110000${src_ip}${dst_ip}0035111100080000 +as hv1 ovs-appctl netdev-dummy/receive vm1 $packet + +# expected packet at bar2 +src_mac="000000010204" +dst_mac="f00000010208" +packet=${dst_mac}${src_mac}8100000108004500001c000000003f110100${src_ip}${dst_ip}0035111100080000 +echo $packet >> expected +OVN_CHECK_PACKETS([hv2/vm2-tx.pcap], [expected]) + +# Send ip packets between foo1 and bar1 +# (different switch, loopback to same vm but different tag) +src_mac="f00000010205" +dst_mac="000000010203" +src_ip=`ip_to_hex 192 168 1 2` +dst_ip=`ip_to_hex 192 168 2 2` +packet=${dst_mac}${src_mac}8100000108004500001c0000000040110000${src_ip}${dst_ip}0035111100080000 +as hv1 ovs-appctl netdev-dummy/receive vm1 $packet + +# expected packet at bar1 +src_mac="000000010204" +dst_mac="f00000010207" +packet=${dst_mac}${src_mac}8100000208004500001c000000003f110100${src_ip}${dst_ip}0035111100080000 +echo $packet > expected1 +OVN_CHECK_PACKETS([hv1/vm1-tx.pcap], [expected1]) + +# Send ip packets between bar1 and bar3 +# (same switch. But one is container and another is a standalone VM) +src_mac="f00000010207" +dst_mac="f00000010209" +src_ip=`ip_to_hex 192 168 2 2` +dst_ip=`ip_to_hex 192 168 2 3` +packet=${dst_mac}${src_mac}8100000208004500001c0000000040110000${src_ip}${dst_ip}0035111100080000 +as hv1 ovs-appctl netdev-dummy/receive vm1 $packet + +# expected packet at bar3 +packet=${dst_mac}${src_mac}08004500001c0000000040110000${src_ip}${dst_ip}0035111100080000 +echo $packet > expected +OVN_CHECK_PACKETS([hv1/bar3-tx.pcap], [expected]) + +# Send ip packets between foo1 and vm1. +(different switch, container to the VM hosting it.) +src_mac="f00000010205" +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}8100000108004500001c0000000040110000${src_ip}${dst_ip}0035111100080000 +as hv1 ovs-appctl netdev-dummy/receive vm1 $packet + +# expected packet at vm1 +src_mac="000000010202" +dst_mac="f00000010203" +packet=${dst_mac}${src_mac}08004500001c000000003f110100${src_ip}${dst_ip}0035111100080000 +echo $packet >> expected1 +OVN_CHECK_PACKETS([hv1/vm1-tx.pcap], [expected1]) + +# Send packets from vm1 to bar1. +(different switch, A hosting VM to a container inside it) +src_mac="f00000010203" +dst_mac="000000010202" +src_ip=`ip_to_hex 172 16 1 2` +dst_ip=`ip_to_hex 192 168 2 2` +packet=${dst_mac}${src_mac}08004500001c0000000040110000${src_ip}${dst_ip}0035111100080000 +as hv1 ovs-appctl netdev-dummy/receive vm1 $packet + +# expected packet at vm1 +src_mac="000000010204" +dst_mac="f00000010207" +packet=${dst_mac}${src_mac}8100000208004500001c000000003f110100${src_ip}${dst_ip}0035111100080000 +echo $packet >> expected1 +OVN_CHECK_PACKETS([hv1/vm1-tx.pcap], [expected1]) + +OVN_CLEANUP([hv1],[hv2]) + +AT_CLEANUP