AT_BANNER([system-ovn]) AT_SETUP([ovn -- 2 LRs connected via LS, gateway router, SNAT and DNAT]) AT_KEYWORDS([ovnnat]) CHECK_CONNTRACK() CHECK_CONNTRACK_NAT() ovn_start OVS_TRAFFIC_VSWITCHD_START() ADD_BR([br-int]) # Set external-ids in br-int needed for ovn-controller ovs-vsctl \ -- set Open_vSwitch . external-ids:system-id=hv1 \ -- set Open_vSwitch . external-ids:ovn-remote=unix:$ovs_base/ovn-sb/ovn-sb.sock \ -- set Open_vSwitch . external-ids:ovn-encap-type=geneve \ -- set Open_vSwitch . external-ids:ovn-encap-ip=169.0.0.1 \ -- set bridge br-int fail-mode=secure other-config:disable-in-band=true # Start ovn-controller start_daemon ovn-controller # Logical network: # Two LRs - R1 and R2 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) and # bar (192.168.2.0/24) connected to it. R2 has alice (172.16.1.0/24) connected # to it. R2 is a gateway router on which we add NAT rules. # # foo -- R1 -- join - R2 -- alice # | # bar ---- ovn-nbctl create Logical_Router name=R1 ovn-nbctl create Logical_Router name=R2 options:chassis=hv1 ovn-nbctl ls-add foo ovn-nbctl ls-add bar ovn-nbctl ls-add alice ovn-nbctl ls-add join # Connect foo to R1 ovn-nbctl lrp-add R1 foo 00:00:01: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:01:01:02:03\" # Connect bar to R1 ovn-nbctl lrp-add R1 bar 00:00:01: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:01:01:02:04\" # Connect alice to R2 ovn-nbctl lrp-add R2 alice 00:00:02:01:02:03 172.16.1.1/24 ovn-nbctl lsp-add alice rp-alice -- set Logical_Switch_Port rp-alice \ type=router options:router-port=alice addresses=\"00:00:02:01:02:03\" # Connect R1 to join ovn-nbctl lrp-add R1 R1_join 00:00:04:01:02:03 20.0.0.1/24 ovn-nbctl lsp-add join r1-join -- set Logical_Switch_Port r1-join \ type=router options:router-port=R1_join addresses='"00:00:04:01:02:03"' # Connect R2 to join ovn-nbctl lrp-add R2 R2_join 00:00:04:01:02:04 20.0.0.2/24 ovn-nbctl lsp-add join r2-join -- set Logical_Switch_Port r2-join \ type=router options:router-port=R2_join addresses='"00:00:04:01:02:04"' # Static routes. ovn-nbctl lr-route-add R1 172.16.1.0/24 20.0.0.2 ovn-nbctl lr-route-add R2 192.168.0.0/16 20.0.0.1 # Logical port 'foo1' in switch 'foo'. ADD_NAMESPACES(foo1) ADD_VETH(foo1, foo1, br-int, "192.168.1.2/24", "f0:00:00:01:02:03", \ "192.168.1.1") ovn-nbctl lsp-add foo foo1 \ -- lsp-set-addresses foo1 "f0:00:00:01:02:03 192.168.1.2" # Logical port 'alice1' in switch 'alice'. ADD_NAMESPACES(alice1) ADD_VETH(alice1, alice1, br-int, "172.16.1.2/24", "f0:00:00:01:02:04", \ "172.16.1.1") ovn-nbctl lsp-add alice alice1 \ -- lsp-set-addresses alice1 "f0:00:00:01:02:04 172.16.1.2" # Logical port 'bar1' in switch 'bar'. ADD_NAMESPACES(bar1) ADD_VETH(bar1, bar1, br-int, "192.168.2.2/24", "f0:00:00:01:02:05", \ "192.168.2.1") ovn-nbctl lsp-add bar bar1 \ -- lsp-set-addresses bar1 "f0:00:00:01:02:05 192.168.2.2" # Add a DNAT rule. ovn-nbctl -- --id=@nat create nat type="dnat" logical_ip=192.168.1.2 \ external_ip=30.0.0.2 -- add logical_router R2 nat @nat # Add a SNAT rule ovn-nbctl -- --id=@nat create nat type="snat" logical_ip=192.168.2.2 \ external_ip=30.0.0.1 -- add logical_router R2 nat @nat # wait for ovn-controller to catch up. ovn-nbctl --wait=hv sync OVS_WAIT_UNTIL([ovs-ofctl dump-flows br-int | grep 'nat(src=30.0.0.1)']) # 'alice1' should be able to ping 'foo1' directly. NS_CHECK_EXEC([alice1], [ping -q -c 3 -i 0.3 -w 2 192.168.1.2 | FORMAT_PING], \ [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) # North-South DNAT: 'alice1' should also be able to ping 'foo1' via 30.0.0.2 NS_CHECK_EXEC([alice1], [ping -q -c 3 -i 0.3 -w 2 30.0.0.2 | FORMAT_PING], \ [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) # Check conntrack entries. AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(172.16.1.2) | \ sed -e 's/zone=[[0-9]]*/zone=/'], [0], [dnl icmp,orig=(src=172.16.1.2,dst=30.0.0.2,id=,type=8,code=0),reply=(src=192.168.1.2,dst=172.16.1.2,id=,type=0,code=0),zone= ]) # South-North SNAT: 'bar1' pings 'alice1'. But 'alice1' receives traffic # from 30.0.0.1 NS_CHECK_EXEC([bar1], [ping -q -c 3 -i 0.3 -w 2 172.16.1.2 | FORMAT_PING], \ [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) # We verify that SNAT indeed happened via 'dump-conntrack' command. AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(30.0.0.1) | \ sed -e 's/zone=[[0-9]]*/zone=/'], [0], [dnl icmp,orig=(src=192.168.2.2,dst=172.16.1.2,id=,type=8,code=0),reply=(src=172.16.1.2,dst=30.0.0.1,id=,type=0,code=0),zone= ]) # Add static routes to handle east-west NAT. ovn-nbctl lr-route-add R1 30.0.0.0/24 20.0.0.2 # wait for ovn-controller to catch up. ovn-nbctl --wait=hv sync # Flush conntrack entries for easier output parsing of next test. AT_CHECK([ovs-appctl dpctl/flush-conntrack]) # East-west DNAT and SNAT: 'bar1' pings 30.0.0.2. 'foo1' receives it. NS_CHECK_EXEC([bar1], [ping -q -c 3 -i 0.3 -w 2 30.0.0.2 | FORMAT_PING], \ [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) # As we have a static route that sends all packets with destination # 30.0.0.2 to R2, it hits the DNAT rule and converts 30.0.0.2 to 192.168.1.2 AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(30.0.0.2) | \ sed -e 's/zone=[[0-9]]*/zone=/'], [0], [dnl icmp,orig=(src=192.168.2.2,dst=30.0.0.2,id=,type=8,code=0),reply=(src=192.168.1.2,dst=192.168.2.2,id=,type=0,code=0),zone= ]) # As we have a SNAT rule that converts 192.168.2.2 to 30.0.0.1, the source is # SNATted and 'foo1' receives it. AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(30.0.0.1) | \ sed -e 's/zone=[[0-9]]*/zone=/'], [0], [dnl icmp,orig=(src=192.168.2.2,dst=192.168.1.2,id=,type=8,code=0),reply=(src=192.168.1.2,dst=30.0.0.1,id=,type=0,code=0),zone= ]) OVS_APP_EXIT_AND_WAIT([ovn-controller]) 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 OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d /connection dropped.*/d"]) AT_CLEANUP AT_SETUP([ovn -- 2 LRs connected via LS, gateway router, easy SNAT]) AT_KEYWORDS([ovnnat]) CHECK_CONNTRACK() CHECK_CONNTRACK_NAT() ovn_start OVS_TRAFFIC_VSWITCHD_START() ADD_BR([br-int]) # Set external-ids in br-int needed for ovn-controller ovs-vsctl \ -- set Open_vSwitch . external-ids:system-id=hv1 \ -- set Open_vSwitch . external-ids:ovn-remote=unix:$ovs_base/ovn-sb/ovn-sb.sock \ -- set Open_vSwitch . external-ids:ovn-encap-type=geneve \ -- set Open_vSwitch . external-ids:ovn-encap-ip=169.0.0.1 \ -- set bridge br-int fail-mode=secure other-config:disable-in-band=true # Start ovn-controller start_daemon ovn-controller # Logical network: # Two LRs - R1 and R2 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) connected to it. # R2 is a gateway router on which we add NAT rules. # # foo -- R1 -- join - R2 -- alice ovn-nbctl lr-add R1 ovn-nbctl lr-add R2 -- set Logical_Router R2 options:chassis=hv1 ovn-nbctl ls-add foo ovn-nbctl ls-add alice ovn-nbctl ls-add join ovn-nbctl lrp-add R1 foo 00:00:01:01:02:03 192.168.1.1/24 ovn-nbctl lrp-add R2 alice 00:00:02:01:02:03 172.16.1.1/24 ovn-nbctl lrp-add R1 R1_join 00:00:04:01:02:03 20.0.0.1/24 ovn-nbctl lrp-add R2 R2_join 00:00:04:01:02:04 20.0.0.2/24 # Connect foo to R1 ovn-nbctl lsp-add foo rp-foo -- set Logical_Switch_Port rp-foo \ type=router options:router-port=foo addresses=\"00:00:01:01:02:03\" # Connect alice to R2 ovn-nbctl lsp-add alice rp-alice -- set Logical_Switch_Port rp-alice \ type=router options:router-port=alice addresses=\"00:00:02:01:02:03\" # Connect R1 to join ovn-nbctl lsp-add join r1-join -- set Logical_Switch_Port r1-join \ type=router options:router-port=R1_join addresses='"00:00:04:01:02:03"' # Connect R2 to join ovn-nbctl lsp-add join r2-join -- set Logical_Switch_Port r2-join \ type=router options:router-port=R2_join addresses='"00:00:04:01:02:04"' # Static routes. ovn-nbctl lr-route-add R1 172.16.1.0/24 20.0.0.2 ovn-nbctl lr-route-add R2 192.168.0.0/16 20.0.0.1 # Logical port 'foo1' in switch 'foo'. ADD_NAMESPACES(foo1) ADD_VETH(foo1, foo1, br-int, "192.168.1.2/24", "f0:00:00:01:02:03", \ "192.168.1.1") ovn-nbctl lsp-add foo foo1 \ -- lsp-set-addresses foo1 "f0:00:00:01:02:03 192.168.1.2" # Logical port 'alice1' in switch 'alice'. ADD_NAMESPACES(alice1) ADD_VETH(alice1, alice1, br-int, "172.16.1.2/24", "f0:00:00:01:02:04", \ "172.16.1.1") ovn-nbctl lsp-add alice alice1 \ -- lsp-set-addresses alice1 "f0:00:00:01:02:04 172.16.1.2" # Add a SNAT rule ovn-nbctl -- --id=@nat create nat type="snat" logical_ip=192.168.1.2 \ external_ip=172.16.1.1 -- add logical_router R2 nat @nat ovn-nbctl --wait=hv sync OVS_WAIT_UNTIL([ovs-ofctl dump-flows br-int | grep 'nat(src=172.16.1.1)']) # South-North SNAT: 'foo1' pings 'alice1'. But 'alice1' receives traffic # from 172.16.1.1 NS_CHECK_EXEC([foo1], [ping -q -c 3 -i 0.3 -w 2 172.16.1.2 | FORMAT_PING], \ [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) # We verify that SNAT indeed happened via 'dump-conntrack' command. AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(172.16.1.1) | \ sed -e 's/zone=[[0-9]]*/zone=/'], [0], [dnl icmp,orig=(src=192.168.1.2,dst=172.16.1.2,id=,type=8,code=0),reply=(src=172.16.1.2,dst=172.16.1.1,id=,type=0,code=0),zone= ]) OVS_APP_EXIT_AND_WAIT([ovn-controller]) 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 OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d /connection dropped.*/d"]) AT_CLEANUP AT_SETUP([ovn -- multiple gateway routers, SNAT and DNAT]) AT_KEYWORDS([ovnnat]) CHECK_CONNTRACK() CHECK_CONNTRACK_NAT() ovn_start OVS_TRAFFIC_VSWITCHD_START() ADD_BR([br-int]) # Set external-ids in br-int needed for ovn-controller ovs-vsctl \ -- set Open_vSwitch . external-ids:system-id=hv1 \ -- set Open_vSwitch . external-ids:ovn-remote=unix:$ovs_base/ovn-sb/ovn-sb.sock \ -- set Open_vSwitch . external-ids:ovn-encap-type=geneve \ -- set Open_vSwitch . external-ids:ovn-encap-ip=169.0.0.1 \ -- set bridge br-int fail-mode=secure other-config:disable-in-band=true # Start ovn-controller start_daemon ovn-controller # 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) and # bar (192.168.2.0/24) connected to it. R2 has alice (172.16.1.0/24) connected # to it. R3 has bob (172.16.1.0/24) connected to it. Note how both alice and # bob have the same subnet behind it. We are trying to simulate external # network via those 2 switches. In real world the switch ports of these # switches will have addresses set as "unknown" to make them learning switches. # Or those switches will be "localnet" ones. # # foo -- R1 -- join - R2 -- alice # | | # bar ---- - R3 --- bob ovn-nbctl create Logical_Router name=R1 ovn-nbctl create Logical_Router name=R2 options:chassis=hv1 ovn-nbctl create Logical_Router name=R3 options:chassis=hv1 ovn-nbctl ls-add foo ovn-nbctl ls-add bar ovn-nbctl ls-add alice ovn-nbctl ls-add bob ovn-nbctl ls-add join # Connect foo to R1 ovn-nbctl lrp-add R1 foo 00:00:01: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:01:01:02:03\" # Connect bar to R1 ovn-nbctl lrp-add R1 bar 00:00:01: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:01:01:02:04\" # Connect alice to R2 ovn-nbctl lrp-add R2 alice 00:00:02:01:02:03 172.16.1.1/24 ovn-nbctl lsp-add alice rp-alice -- set Logical_Switch_Port rp-alice \ type=router options:router-port=alice addresses=\"00:00:02:01:02:03\" # Connect bob to R3 ovn-nbctl lrp-add R3 bob 00:00:03:01:02:03 172.16.1.2/24 ovn-nbctl lsp-add bob rp-bob -- set Logical_Switch_Port rp-bob \ type=router options:router-port=bob addresses=\"00:00:03:01:02:03\" # Connect R1 to join ovn-nbctl lrp-add R1 R1_join 00:00:04:01:02:03 20.0.0.1/24 ovn-nbctl lsp-add join r1-join -- set Logical_Switch_Port r1-join \ type=router options:router-port=R1_join addresses='"00:00:04:01:02:03"' # Connect R2 to join ovn-nbctl lrp-add R2 R2_join 00:00:04:01:02:04 20.0.0.2/24 ovn-nbctl lsp-add join r2-join -- set Logical_Switch_Port r2-join \ type=router options:router-port=R2_join addresses='"00:00:04:01:02:04"' # Connect R3 to join ovn-nbctl lrp-add R3 R3_join 00:00:04:01:02:05 20.0.0.3/24 ovn-nbctl lsp-add join r3-join -- set Logical_Switch_Port r3-join \ type=router options:router-port=R3_join addresses='"00:00:04:01:02:05"' # Install static routes with source ip address as the policy for routing. # We want traffic from 'foo' to go via R2 and traffic of 'bar' to go via R3. ovn-nbctl --policy="src-ip" lr-route-add R1 192.168.1.0/24 20.0.0.2 ovn-nbctl --policy="src-ip" lr-route-add R1 192.168.2.0/24 20.0.0.3 # Static routes. ovn-nbctl lr-route-add R2 192.168.0.0/16 20.0.0.1 ovn-nbctl lr-route-add R3 192.168.0.0/16 20.0.0.1 # For gateway routers R2 and R3, set a force SNAT rule. ovn-nbctl set logical_router R2 options:dnat_force_snat_ip=20.0.0.2 ovn-nbctl set logical_router R3 options:dnat_force_snat_ip=20.0.0.3 # Logical port 'foo1' in switch 'foo'. ADD_NAMESPACES(foo1) ADD_VETH(foo1, foo1, br-int, "192.168.1.2/24", "f0:00:00:01:02:03", \ "192.168.1.1") ovn-nbctl lsp-add foo foo1 \ -- lsp-set-addresses foo1 "f0:00:00:01:02:03 192.168.1.2" # Logical port 'alice1' in switch 'alice'. ADD_NAMESPACES(alice1) ADD_VETH(alice1, alice1, br-int, "172.16.1.3/24", "f0:00:00:01:02:04", \ "172.16.1.1") ovn-nbctl lsp-add alice alice1 \ -- lsp-set-addresses alice1 "f0:00:00:01:02:04 172.16.1.3" # Logical port 'bar1' in switch 'bar'. ADD_NAMESPACES(bar1) ADD_VETH(bar1, bar1, br-int, "192.168.2.2/24", "f0:00:00:01:02:05", \ "192.168.2.1") ovn-nbctl lsp-add bar bar1 \ -- lsp-set-addresses bar1 "f0:00:00:01:02:05 192.168.2.2" # Logical port 'bob1' in switch 'bob'. ADD_NAMESPACES(bob1) ADD_VETH(bob1, bob1, br-int, "172.16.1.4/24", "f0:00:00:01:02:06", \ "172.16.1.2") ovn-nbctl lsp-add bob bob1 \ -- lsp-set-addresses bob1 "f0:00:00:01:02:06 172.16.1.4" # Router R2 # Add a DNAT rule. ovn-nbctl -- --id=@nat create nat type="dnat" logical_ip=192.168.1.2 \ external_ip=30.0.0.2 -- add logical_router R2 nat @nat # Add a SNAT rule ovn-nbctl -- --id=@nat create nat type="snat" logical_ip=192.168.1.2 \ external_ip=30.0.0.1 -- add logical_router R2 nat @nat # Router R3 # Add a DNAT rule. ovn-nbctl -- --id=@nat create nat type="dnat" logical_ip=192.168.1.2 \ external_ip=30.0.0.3 -- add logical_router R3 nat @nat # Add a SNAT rule ovn-nbctl -- --id=@nat create nat type="snat" logical_ip=192.168.2.2 \ external_ip=30.0.0.4 -- add logical_router R3 nat @nat # wait for ovn-controller to catch up. ovn-nbctl --wait=hv sync OVS_WAIT_UNTIL([ovs-ofctl dump-flows br-int | grep 'nat(src=30.0.0.4)']) # North-South DNAT: 'alice1' should be able to ping 'foo1' via 30.0.0.2 NS_CHECK_EXEC([alice1], [ping -q -c 3 -i 0.3 -w 2 30.0.0.2 | FORMAT_PING], \ [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) # Check conntrack entries. AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(172.16.1.3) | \ sed -e 's/zone=[[0-9]]*/zone=/'], [0], [dnl icmp,orig=(src=172.16.1.3,dst=30.0.0.2,id=,type=8,code=0),reply=(src=192.168.1.2,dst=172.16.1.3,id=,type=0,code=0),zone= ]) # But foo1 should receive traffic from 20.0.0.2 AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(20.0.0.2) | \ sed -e 's/zone=[[0-9]]*/zone=/'], [0], [dnl icmp,orig=(src=172.16.1.3,dst=192.168.1.2,id=,type=8,code=0),reply=(src=192.168.1.2,dst=20.0.0.2,id=,type=0,code=0),zone= ]) # North-South DNAT: 'bob1' should be able to ping 'foo1' via 30.0.0.3 NS_CHECK_EXEC([bob1], [ping -q -c 3 -i 0.3 -w 2 30.0.0.3 | FORMAT_PING], \ [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) # Check conntrack entries. AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(172.16.1.4) | \ sed -e 's/zone=[[0-9]]*/zone=/'], [0], [dnl icmp,orig=(src=172.16.1.4,dst=30.0.0.3,id=,type=8,code=0),reply=(src=192.168.1.2,dst=172.16.1.4,id=,type=0,code=0),zone= ]) # But foo1 should receive traffic from 20.0.0.3 AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(20.0.0.3) | \ sed -e 's/zone=[[0-9]]*/zone=/'], [0], [dnl icmp,orig=(src=172.16.1.4,dst=192.168.1.2,id=,type=8,code=0),reply=(src=192.168.1.2,dst=20.0.0.3,id=,type=0,code=0),zone= ]) # South-North SNAT: 'bar1' pings 'bob1'. But 'bob1' receives traffic # from 30.0.0.4 NS_CHECK_EXEC([bar1], [ping -q -c 3 -i 0.3 -w 2 172.16.1.4 | FORMAT_PING], \ [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) # We verify that SNAT indeed happened via 'dump-conntrack' command. AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(30.0.0.4) | \ sed -e 's/zone=[[0-9]]*/zone=/'], [0], [dnl icmp,orig=(src=192.168.2.2,dst=172.16.1.4,id=,type=8,code=0),reply=(src=172.16.1.4,dst=30.0.0.4,id=,type=0,code=0),zone= ]) # South-North SNAT: 'foo1' pings 'alice1'. But 'alice1' receives traffic # from 30.0.0.1 NS_CHECK_EXEC([foo1], [ping -q -c 3 -i 0.3 -w 2 172.16.1.3 | FORMAT_PING], \ [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) # We verify that SNAT indeed happened via 'dump-conntrack' command. AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(30.0.0.1) | \ sed -e 's/zone=[[0-9]]*/zone=/'], [0], [dnl icmp,orig=(src=192.168.1.2,dst=172.16.1.3,id=,type=8,code=0),reply=(src=172.16.1.3,dst=30.0.0.1,id=,type=0,code=0),zone= ]) OVS_APP_EXIT_AND_WAIT([ovn-controller]) 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 OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d /connection dropped.*/d"]) AT_CLEANUP AT_SETUP([ovn -- load-balancing]) AT_KEYWORDS([ovnlb]) CHECK_CONNTRACK() CHECK_CONNTRACK_NAT() ovn_start OVS_TRAFFIC_VSWITCHD_START() ADD_BR([br-int]) # Set external-ids in br-int needed for ovn-controller ovs-vsctl \ -- set Open_vSwitch . external-ids:system-id=hv1 \ -- set Open_vSwitch . external-ids:ovn-remote=unix:$ovs_base/ovn-sb/ovn-sb.sock \ -- set Open_vSwitch . external-ids:ovn-encap-type=geneve \ -- set Open_vSwitch . external-ids:ovn-encap-ip=169.0.0.1 \ -- set bridge br-int fail-mode=secure other-config:disable-in-band=true # Start ovn-controller start_daemon ovn-controller # Logical network: # 2 logical switches "foo" (192.168.1.0/24) and "bar" (172.16.1.0/24) # connected to a router R1. # foo has foo1 to act as a client. # bar has bar1, bar2, bar3 to act as servers. # # Loadbalancer VIPs in 30.0.0.0/24 network. ovn-nbctl create Logical_Router name=R1 ovn-nbctl ls-add foo ovn-nbctl ls-add bar # Connect foo to R1 ovn-nbctl lrp-add R1 foo 00:00:01: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:01:01:02:03\" # Connect bar to R1 ovn-nbctl lrp-add R1 bar 00:00:01:01:02:04 172.16.1.1/24 ovn-nbctl lsp-add bar rp-bar -- set Logical_Switch_Port rp-bar \ type=router options:router-port=bar addresses=\"00:00:01:01:02:04\" # Create logical port 'foo1' in switch 'foo'. ADD_NAMESPACES(foo1) ADD_VETH(foo1, foo1, br-int, "192.168.1.2/24", "f0:00:00:01:02:03", \ "192.168.1.1") ovn-nbctl lsp-add foo foo1 \ -- lsp-set-addresses foo1 "f0:00:00:01:02:03 192.168.1.2" # Create logical ports 'bar1', 'bar2', 'bar3' in switch 'bar'. ADD_NAMESPACES(bar1) ADD_VETH(bar1, bar1, br-int, "172.16.1.2/24", "f0:00:0f:01:02:03", \ "172.16.1.1") ovn-nbctl lsp-add bar bar1 \ -- lsp-set-addresses bar1 "f0:00:0f:01:02:03 172.16.1.2" ADD_NAMESPACES(bar2) ADD_VETH(bar2, bar2, br-int, "172.16.1.3/24", "f0:00:0f:01:02:04", \ "172.16.1.1") ovn-nbctl lsp-add bar bar2 \ -- lsp-set-addresses bar2 "f0:00:0f:01:02:04 172.16.1.3" ADD_NAMESPACES(bar3) ADD_VETH(bar3, bar3, br-int, "172.16.1.4/24", "f0:00:0f:01:02:05", \ "172.16.1.1") ovn-nbctl lsp-add bar bar3 \ -- lsp-set-addresses bar3 "f0:00:0f:01:02:05 172.16.1.4" # Config OVN load-balancer with a VIP. uuid=`ovn-nbctl create load_balancer vips:30.0.0.1="172.16.1.2,172.16.1.3,172.16.1.4"` ovn-nbctl set logical_switch foo load_balancer=$uuid # Create another load-balancer with another VIP. uuid=`ovn-nbctl create load_balancer vips:30.0.0.3="172.16.1.2,172.16.1.3,172.16.1.4"` ovn-nbctl add logical_switch foo load_balancer $uuid # Config OVN load-balancer with another VIP (this time with ports). ovn-nbctl set load_balancer $uuid vips:'"30.0.0.2:8000"'='"172.16.1.2:80,172.16.1.3:80,172.16.1.4:80"' # Wait for ovn-controller to catch up. ovn-nbctl --wait=hv sync OVS_WAIT_UNTIL([ovs-ofctl -O OpenFlow13 dump-groups br-int | \ grep 'nat(dst=172.16.1.4:80)']) # Start webservers in 'bar1', 'bar2' and 'bar3'. OVS_START_L7([bar1], [http]) OVS_START_L7([bar2], [http]) OVS_START_L7([bar3], [http]) dnl Should work with the virtual IP 30.0.0.1 address through NAT for i in `seq 1 20`; do echo Request $i NS_CHECK_EXEC([foo1], [wget 30.0.0.1 -t 5 -T 1 --retry-connrefused -v -o wget$i.log]) done dnl Each server should have at least one connection. AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(30.0.0.1) | \ sed -e 's/zone=[[0-9]]*/zone=/'], [0], [dnl tcp,orig=(src=192.168.1.2,dst=30.0.0.1,sport=,dport=),reply=(src=172.16.1.2,dst=192.168.1.2,sport=,dport=),zone=,protoinfo=(state=) tcp,orig=(src=192.168.1.2,dst=30.0.0.1,sport=,dport=),reply=(src=172.16.1.3,dst=192.168.1.2,sport=,dport=),zone=,protoinfo=(state=) tcp,orig=(src=192.168.1.2,dst=30.0.0.1,sport=,dport=),reply=(src=172.16.1.4,dst=192.168.1.2,sport=,dport=),zone=,protoinfo=(state=) ]) dnl Should work with the virtual IP 30.0.0.3 address through NAT for i in `seq 1 20`; do echo Request $i NS_CHECK_EXEC([foo1], [wget 30.0.0.3 -t 5 -T 1 --retry-connrefused -v -o wget$i.log]) done dnl Each server should have at least one connection. AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(30.0.0.3) | \ sed -e 's/zone=[[0-9]]*/zone=/'], [0], [dnl tcp,orig=(src=192.168.1.2,dst=30.0.0.3,sport=,dport=),reply=(src=172.16.1.2,dst=192.168.1.2,sport=,dport=),zone=,protoinfo=(state=) tcp,orig=(src=192.168.1.2,dst=30.0.0.3,sport=,dport=),reply=(src=172.16.1.3,dst=192.168.1.2,sport=,dport=),zone=,protoinfo=(state=) tcp,orig=(src=192.168.1.2,dst=30.0.0.3,sport=,dport=),reply=(src=172.16.1.4,dst=192.168.1.2,sport=,dport=),zone=,protoinfo=(state=) ]) dnl Test load-balancing that includes L4 ports in NAT. for i in `seq 1 20`; do echo Request $i NS_CHECK_EXEC([foo1], [wget 30.0.0.2:8000 -t 5 -T 1 --retry-connrefused -v -o wget$i.log]) done dnl Each server should have at least one connection. AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(30.0.0.2) | \ sed -e 's/zone=[[0-9]]*/zone=/'], [0], [dnl tcp,orig=(src=192.168.1.2,dst=30.0.0.2,sport=,dport=),reply=(src=172.16.1.2,dst=192.168.1.2,sport=,dport=),zone=,protoinfo=(state=) tcp,orig=(src=192.168.1.2,dst=30.0.0.2,sport=,dport=),reply=(src=172.16.1.3,dst=192.168.1.2,sport=,dport=),zone=,protoinfo=(state=) tcp,orig=(src=192.168.1.2,dst=30.0.0.2,sport=,dport=),reply=(src=172.16.1.4,dst=192.168.1.2,sport=,dport=),zone=,protoinfo=(state=) ]) OVS_APP_EXIT_AND_WAIT([ovn-controller]) 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 OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d"]) AT_CLEANUP AT_SETUP([ovn -- load-balancing - same subnet.]) AT_KEYWORDS([ovnlb]) CHECK_CONNTRACK() CHECK_CONNTRACK_NAT() ovn_start OVS_TRAFFIC_VSWITCHD_START() ADD_BR([br-int]) # Set external-ids in br-int needed for ovn-controller ovs-vsctl \ -- set Open_vSwitch . external-ids:system-id=hv1 \ -- set Open_vSwitch . external-ids:ovn-remote=unix:$ovs_base/ovn-sb/ovn-sb.sock \ -- set Open_vSwitch . external-ids:ovn-encap-type=geneve \ -- set Open_vSwitch . external-ids:ovn-encap-ip=169.0.0.1 \ -- set bridge br-int fail-mode=secure other-config:disable-in-band=true # Start ovn-controller start_daemon ovn-controller # Logical network: # 1 logical switch "foo" (192.168.1.0/24) connected to router R1. # foo has foo1, foo2, foo3, foo4 as logical ports. # # Loadbalancer VIPs in 30.0.0.0/24 network. Router is needed for default # gateway. We will test load-balancing with foo1 as a client and foo2, foo3 and # foo4 as servers. ovn-nbctl create Logical_Router name=R1 ovn-nbctl ls-add foo # Connect foo to R1 ovn-nbctl lrp-add R1 foo 00:00:01: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:01:01:02:03\" # Create logical port 'foo1', 'foo2', 'foo3' and 'foo4' in switch 'foo'. ADD_NAMESPACES(foo1, foo2, foo3, foo4) for i in `seq 1 4`; do j=`expr $i + 1` ADD_VETH(foo$i, foo$i, br-int, "192.168.1.$j/24", "f0:00:00:01:02:0$j", \ "192.168.1.1") ovn-nbctl lsp-add foo foo$i \ -- lsp-set-addresses foo$i "f0:00:00:01:02:0$j 192.168.1.$j" done # Config OVN load-balancer with a VIP. uuid=`ovn-nbctl create load_balancer vips:30.0.0.1="192.168.1.3,192.168.1.4,192.168.1.5"` ovn-nbctl set logical_switch foo load_balancer=$uuid # Config OVN load-balancer with another VIP (this time with ports). ovn-nbctl set load_balancer $uuid vips:'"30.0.0.2:8000"'='"192.168.1.3:80,192.168.1.4:80,192.168.1.5:80"' # Wait for ovn-controller to catch up. ovn-nbctl --wait=hv sync OVS_WAIT_UNTIL([ovs-ofctl -O OpenFlow13 dump-groups br-int | \ grep 'nat(dst=192.168.1.5:80)']) # Start webservers in 'foo2', 'foo3' and 'foo4'. OVS_START_L7([foo2], [http]) OVS_START_L7([foo3], [http]) OVS_START_L7([foo4], [http]) dnl Should work with the virtual IP address through NAT for i in `seq 1 20`; do echo Request $i NS_CHECK_EXEC([foo1], [wget 30.0.0.1 -t 5 -T 1 --retry-connrefused -v -o wget$i.log]) done dnl Each server should have at least one connection. AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(30.0.0.1) | \ sed -e 's/zone=[[0-9]]*/zone=/'], [0], [dnl tcp,orig=(src=192.168.1.2,dst=30.0.0.1,sport=,dport=),reply=(src=192.168.1.3,dst=192.168.1.2,sport=,dport=),zone=,protoinfo=(state=) tcp,orig=(src=192.168.1.2,dst=30.0.0.1,sport=,dport=),reply=(src=192.168.1.4,dst=192.168.1.2,sport=,dport=),zone=,protoinfo=(state=) tcp,orig=(src=192.168.1.2,dst=30.0.0.1,sport=,dport=),reply=(src=192.168.1.5,dst=192.168.1.2,sport=,dport=),zone=,protoinfo=(state=) ]) dnl Test load-balancing that includes L4 ports in NAT. for i in `seq 1 20`; do echo Request $i NS_CHECK_EXEC([foo1], [wget 30.0.0.2:8000 -t 5 -T 1 --retry-connrefused -v -o wget$i.log]) done dnl Each server should have at least one connection. AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(30.0.0.2) | \ sed -e 's/zone=[[0-9]]*/zone=/'], [0], [dnl tcp,orig=(src=192.168.1.2,dst=30.0.0.2,sport=,dport=),reply=(src=192.168.1.3,dst=192.168.1.2,sport=,dport=),zone=,protoinfo=(state=) tcp,orig=(src=192.168.1.2,dst=30.0.0.2,sport=,dport=),reply=(src=192.168.1.4,dst=192.168.1.2,sport=,dport=),zone=,protoinfo=(state=) tcp,orig=(src=192.168.1.2,dst=30.0.0.2,sport=,dport=),reply=(src=192.168.1.5,dst=192.168.1.2,sport=,dport=),zone=,protoinfo=(state=) ]) OVS_APP_EXIT_AND_WAIT([ovn-controller]) 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 OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d"]) AT_CLEANUP AT_SETUP([ovn -- load balancing in gateway router]) AT_KEYWORDS([ovnlb]) CHECK_CONNTRACK() CHECK_CONNTRACK_NAT() ovn_start OVS_TRAFFIC_VSWITCHD_START() ADD_BR([br-int]) # Set external-ids in br-int needed for ovn-controller ovs-vsctl \ -- set Open_vSwitch . external-ids:system-id=hv1 \ -- set Open_vSwitch . external-ids:ovn-remote=unix:$ovs_base/ovn-sb/ovn-sb.sock \ -- set Open_vSwitch . external-ids:ovn-encap-type=geneve \ -- set Open_vSwitch . external-ids:ovn-encap-ip=169.0.0.1 \ -- set bridge br-int fail-mode=secure other-config:disable-in-band=true # Start ovn-controller start_daemon ovn-controller # Logical network: # Two LRs - R1 and R2 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) and # bar (192.168.2.0/24) connected to it. R2 has alice (172.16.1.0/24) connected # to it. R2 is a gateway router on which we add load-balancing rules. # # foo -- R1 -- join - R2 -- alice # | # bar ---- ovn-nbctl create Logical_Router name=R1 ovn-nbctl create Logical_Router name=R2 options:chassis=hv1 ovn-nbctl ls-add foo ovn-nbctl ls-add bar ovn-nbctl ls-add alice ovn-nbctl ls-add join # Connect foo to R1 ovn-nbctl lrp-add R1 foo 00:00:01: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:01:01:02:03\" # Connect bar to R1 ovn-nbctl lrp-add R1 bar 00:00:01: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:01:01:02:04\" # Connect alice to R2 ovn-nbctl lrp-add R2 alice 00:00:02:01:02:03 172.16.1.1/24 ovn-nbctl lsp-add alice rp-alice -- set Logical_Switch_Port rp-alice \ type=router options:router-port=alice addresses=\"00:00:02:01:02:03\" # Connect R1 to join ovn-nbctl lrp-add R1 R1_join 00:00:04:01:02:03 20.0.0.1/24 ovn-nbctl lsp-add join r1-join -- set Logical_Switch_Port r1-join \ type=router options:router-port=R1_join addresses='"00:00:04:01:02:03"' # Connect R2 to join ovn-nbctl lrp-add R2 R2_join 00:00:04:01:02:04 20.0.0.2/24 ovn-nbctl lsp-add join r2-join -- set Logical_Switch_Port r2-join \ type=router options:router-port=R2_join addresses='"00:00:04:01:02:04"' # Static routes. ovn-nbctl lr-route-add R1 172.16.1.0/24 20.0.0.2 ovn-nbctl lr-route-add R2 192.168.0.0/16 20.0.0.1 # Logical port 'foo1' in switch 'foo'. ADD_NAMESPACES(foo1) ADD_VETH(foo1, foo1, br-int, "192.168.1.2/24", "f0:00:00:01:02:03", \ "192.168.1.1") ovn-nbctl lsp-add foo foo1 \ -- lsp-set-addresses foo1 "f0:00:00:01:02:03 192.168.1.2" # Logical port 'alice1' in switch 'alice'. ADD_NAMESPACES(alice1) ADD_VETH(alice1, alice1, br-int, "172.16.1.2/24", "f0:00:00:01:02:04", \ "172.16.1.1") ovn-nbctl lsp-add alice alice1 \ -- lsp-set-addresses alice1 "f0:00:00:01:02:04 172.16.1.2" # Logical port 'bar1' in switch 'bar'. ADD_NAMESPACES(bar1) ADD_VETH(bar1, bar1, br-int, "192.168.2.2/24", "f0:00:00:01:02:05", \ "192.168.2.1") ovn-nbctl lsp-add bar bar1 \ -- lsp-set-addresses bar1 "f0:00:00:01:02:05 192.168.2.2" # Config OVN load-balancer with a VIP. uuid=`ovn-nbctl create load_balancer vips:30.0.0.1="192.168.1.2,192.168.2.2"` ovn-nbctl set logical_router R2 load_balancer=$uuid # Config OVN load-balancer with another VIP (this time with ports). ovn-nbctl set load_balancer $uuid vips:'"30.0.0.2:8000"'='"192.168.1.2:80,192.168.2.2:80"' # Wait for ovn-controller to catch up. ovn-nbctl --wait=hv sync OVS_WAIT_UNTIL([ovs-ofctl -O OpenFlow13 dump-groups br-int | \ grep 'nat(dst=192.168.2.2:80)']) # Start webservers in 'foo1', 'bar1'. OVS_START_L7([foo1], [http]) OVS_START_L7([bar1], [http]) dnl Should work with the virtual IP address through NAT for i in `seq 1 20`; do echo Request $i NS_CHECK_EXEC([alice1], [wget 30.0.0.1 -t 5 -T 1 --retry-connrefused -v -o wget$i.log]) done dnl Each server should have at least one connection. AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(30.0.0.1) | sed -e 's/zone=[[0-9]]*/zone=/'], [0], [dnl tcp,orig=(src=172.16.1.2,dst=30.0.0.1,sport=,dport=),reply=(src=192.168.1.2,dst=172.16.1.2,sport=,dport=),zone=,protoinfo=(state=) tcp,orig=(src=172.16.1.2,dst=30.0.0.1,sport=,dport=),reply=(src=192.168.2.2,dst=172.16.1.2,sport=,dport=),zone=,protoinfo=(state=) ]) dnl Test load-balancing that includes L4 ports in NAT. for i in `seq 1 20`; do echo Request $i NS_CHECK_EXEC([alice1], [wget 30.0.0.2:8000 -t 5 -T 1 --retry-connrefused -v -o wget$i.log]) done dnl Each server should have at least one connection. AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(30.0.0.2) | sed -e 's/zone=[[0-9]]*/zone=/'], [0], [dnl tcp,orig=(src=172.16.1.2,dst=30.0.0.2,sport=,dport=),reply=(src=192.168.1.2,dst=172.16.1.2,sport=,dport=),zone=,protoinfo=(state=) tcp,orig=(src=172.16.1.2,dst=30.0.0.2,sport=,dport=),reply=(src=192.168.2.2,dst=172.16.1.2,sport=,dport=),zone=,protoinfo=(state=) ]) OVS_APP_EXIT_AND_WAIT([ovn-controller]) 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 OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d /connection dropped.*/d"]) AT_CLEANUP AT_SETUP([ovn -- multiple gateway routers, load-balancing]) AT_KEYWORDS([ovnlb]) CHECK_CONNTRACK() CHECK_CONNTRACK_NAT() ovn_start OVS_TRAFFIC_VSWITCHD_START() ADD_BR([br-int]) # Set external-ids in br-int needed for ovn-controller ovs-vsctl \ -- set Open_vSwitch . external-ids:system-id=hv1 \ -- set Open_vSwitch . external-ids:ovn-remote=unix:$ovs_base/ovn-sb/ovn-sb.sock \ -- set Open_vSwitch . external-ids:ovn-encap-type=geneve \ -- set Open_vSwitch . external-ids:ovn-encap-ip=169.0.0.1 \ -- set bridge br-int fail-mode=secure other-config:disable-in-band=true # Start ovn-controller start_daemon ovn-controller # 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) and # bar (192.168.2.0/24) connected to it. R2 has alice (172.16.1.0/24) connected # to it. R3 has bob (172.16.1.0/24) connected to it. Note how both alice and # bob have the same subnet behind it. We are trying to simulate external # network via those 2 switches. In real world the switch ports of these # switches will have addresses set as "unknown" to make them learning switches. # Or those switches will be "localnet" ones. # # foo -- R1 -- join - R2 -- alice # | | # bar ---- - R3 --- bob ovn-nbctl create Logical_Router name=R1 ovn-nbctl create Logical_Router name=R2 options:chassis=hv1 ovn-nbctl create Logical_Router name=R3 options:chassis=hv1 ovn-nbctl ls-add foo ovn-nbctl ls-add bar ovn-nbctl ls-add alice ovn-nbctl ls-add bob ovn-nbctl ls-add join # Connect foo to R1 ovn-nbctl lrp-add R1 foo 00:00:01: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:01:01:02:03\" # Connect bar to R1 ovn-nbctl lrp-add R1 bar 00:00:01: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:01:01:02:04\" # Connect alice to R2 ovn-nbctl lrp-add R2 alice 00:00:02:01:02:03 172.16.1.1/24 ovn-nbctl lsp-add alice rp-alice -- set Logical_Switch_Port rp-alice \ type=router options:router-port=alice addresses=\"00:00:02:01:02:03\" # Connect bob to R3 ovn-nbctl lrp-add R3 bob 00:00:03:01:02:03 172.16.1.2/24 ovn-nbctl lsp-add bob rp-bob -- set Logical_Switch_Port rp-bob \ type=router options:router-port=bob addresses=\"00:00:03:01:02:03\" # Connect R1 to join ovn-nbctl lrp-add R1 R1_join 00:00:04:01:02:03 20.0.0.1/24 ovn-nbctl lsp-add join r1-join -- set Logical_Switch_Port r1-join \ type=router options:router-port=R1_join addresses='"00:00:04:01:02:03"' # Connect R2 to join ovn-nbctl lrp-add R2 R2_join 00:00:04:01:02:04 20.0.0.2/24 ovn-nbctl lsp-add join r2-join -- set Logical_Switch_Port r2-join \ type=router options:router-port=R2_join addresses='"00:00:04:01:02:04"' # Connect R3 to join ovn-nbctl lrp-add R3 R3_join 00:00:04:01:02:05 20.0.0.3/24 ovn-nbctl lsp-add join r3-join -- set Logical_Switch_Port r3-join \ type=router options:router-port=R3_join addresses='"00:00:04:01:02:05"' # Install static routes with source ip address as the policy for routing. # We want traffic from 'foo' to go via R2 and traffic of 'bar' to go via R3. ovn-nbctl --policy="src-ip" lr-route-add R1 192.168.1.0/24 20.0.0.2 ovn-nbctl --policy="src-ip" lr-route-add R1 192.168.2.0/24 20.0.0.3 # Static routes. ovn-nbctl lr-route-add R2 192.168.0.0/16 20.0.0.1 ovn-nbctl lr-route-add R3 192.168.0.0/16 20.0.0.1 # For gateway routers R2 and R3, set a force SNAT rule. ovn-nbctl set logical_router R2 options:lb_force_snat_ip=20.0.0.2 ovn-nbctl set logical_router R3 options:lb_force_snat_ip=20.0.0.3 # Logical port 'foo1' in switch 'foo'. ADD_NAMESPACES(foo1) ADD_VETH(foo1, foo1, br-int, "192.168.1.2/24", "f0:00:00:01:02:03", \ "192.168.1.1") ovn-nbctl lsp-add foo foo1 \ -- lsp-set-addresses foo1 "f0:00:00:01:02:03 192.168.1.2" # Logical port 'alice1' in switch 'alice'. ADD_NAMESPACES(alice1) ADD_VETH(alice1, alice1, br-int, "172.16.1.3/24", "f0:00:00:01:02:04", \ "172.16.1.1") ovn-nbctl lsp-add alice alice1 \ -- lsp-set-addresses alice1 "f0:00:00:01:02:04 172.16.1.3" # Logical port 'bar1' in switch 'bar'. ADD_NAMESPACES(bar1) ADD_VETH(bar1, bar1, br-int, "192.168.2.2/24", "f0:00:00:01:02:05", \ "192.168.2.1") ovn-nbctl lsp-add bar bar1 \ -- lsp-set-addresses bar1 "f0:00:00:01:02:05 192.168.2.2" # Logical port 'bob1' in switch 'bob'. ADD_NAMESPACES(bob1) ADD_VETH(bob1, bob1, br-int, "172.16.1.4/24", "f0:00:00:01:02:06", \ "172.16.1.2") ovn-nbctl lsp-add bob bob1 \ -- lsp-set-addresses bob1 "f0:00:00:01:02:06 172.16.1.4" # Config OVN load-balancer with a VIP. uuid=`ovn-nbctl create load_balancer vips:30.0.0.1="192.168.1.2,192.168.2.2"` ovn-nbctl set logical_router R2 load_balancer=$uuid ovn-nbctl set logical_router R3 load_balancer=$uuid # Wait for ovn-controller to catch up. ovn-nbctl --wait=hv sync OVS_WAIT_UNTIL([ovs-ofctl -O OpenFlow13 dump-groups br-int | \ grep 'nat(dst=192.168.2.2)']) # Start webservers in 'foo1', 'bar1'. OVS_START_L7([foo1], [http]) OVS_START_L7([bar1], [http]) dnl Should work with the virtual IP address through NAT for i in `seq 1 20`; do echo Request $i NS_CHECK_EXEC([alice1], [wget 30.0.0.1 -t 5 -T 1 --retry-connrefused -v -o wget$i.log]) done dnl Each server should have at least one connection. AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(30.0.0.1) | sed -e 's/zone=[[0-9]]*/zone=/'], [0], [dnl tcp,orig=(src=172.16.1.3,dst=30.0.0.1,sport=,dport=),reply=(src=192.168.1.2,dst=172.16.1.3,sport=,dport=),zone=,protoinfo=(state=) tcp,orig=(src=172.16.1.3,dst=30.0.0.1,sport=,dport=),reply=(src=192.168.2.2,dst=172.16.1.3,sport=,dport=),zone=,protoinfo=(state=) ]) dnl Force SNAT should have worked. AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(20.0.0) | sed -e 's/zone=[[0-9]]*/zone=/'], [0], [dnl tcp,orig=(src=172.16.1.3,dst=192.168.1.2,sport=,dport=),reply=(src=192.168.1.2,dst=20.0.0.2,sport=,dport=),zone=,protoinfo=(state=) tcp,orig=(src=172.16.1.3,dst=192.168.2.2,sport=,dport=),reply=(src=192.168.2.2,dst=20.0.0.2,sport=,dport=),zone=,protoinfo=(state=) ]) OVS_APP_EXIT_AND_WAIT([ovn-controller]) 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 OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d /connection dropped.*/d"]) AT_CLEANUP AT_SETUP([ovn -- DNAT and SNAT on distributed router - N/S]) AT_KEYWORDS([ovnnat]) CHECK_CONNTRACK() CHECK_CONNTRACK_NAT() ovn_start OVS_TRAFFIC_VSWITCHD_START() ADD_BR([br-int]) # Set external-ids in br-int needed for ovn-controller ovs-vsctl \ -- set Open_vSwitch . external-ids:system-id=hv1 \ -- set Open_vSwitch . external-ids:ovn-remote=unix:$ovs_base/ovn-sb/ovn-sb.sock \ -- set Open_vSwitch . external-ids:ovn-encap-type=geneve \ -- set Open_vSwitch . external-ids:ovn-encap-ip=169.0.0.1 \ -- set bridge br-int fail-mode=secure other-config:disable-in-band=true # Start ovn-controller start_daemon ovn-controller # Logical network: # One LR R1 with switches foo (192.168.1.0/24), bar (192.168.2.0/24), # and alice (172.16.1.0/24) connected to it. The port between R1 and # alice is the router gateway port where the R1 NAT rules are applied. # # foo -- R1 -- alice # | # bar ---- ovn-nbctl lr-add R1 ovn-nbctl ls-add foo ovn-nbctl ls-add bar ovn-nbctl ls-add alice ovn-nbctl lrp-add R1 foo 00:00:01:01:02:03 192.168.1.1/24 ovn-nbctl lrp-add R1 bar 00:00:01:01:02:04 192.168.2.1/24 ovn-nbctl lrp-add R1 alice 00:00:02:01:02:03 172.16.1.1/24 \ -- set Logical_Router_Port alice options:redirect-chassis=hv1 # Connect foo to R1 ovn-nbctl lsp-add foo rp-foo -- set Logical_Switch_Port rp-foo \ type=router options:router-port=foo \ -- lsp-set-addresses rp-foo router # Connect bar to R1 ovn-nbctl lsp-add bar rp-bar -- set Logical_Switch_Port rp-bar \ type=router options:router-port=bar \ -- lsp-set-addresses rp-bar router # Connect alice to R1 ovn-nbctl lsp-add alice rp-alice -- set Logical_Switch_Port rp-alice \ type=router options:router-port=alice \ -- lsp-set-addresses rp-alice router # Logical port 'foo1' in switch 'foo'. ADD_NAMESPACES(foo1) ADD_VETH(foo1, foo1, br-int, "192.168.1.2/24", "f0:00:00:01:02:03", \ "192.168.1.1") ovn-nbctl lsp-add foo foo1 \ -- lsp-set-addresses foo1 "f0:00:00:01:02:03 192.168.1.2" # Logical port 'foo2' in switch 'foo'. ADD_NAMESPACES(foo2) ADD_VETH(foo2, foo2, br-int, "192.168.1.3/24", "f0:00:00:01:02:06", \ "192.168.1.1") ovn-nbctl lsp-add foo foo2 \ -- lsp-set-addresses foo2 "f0:00:00:01:02:06 192.168.1.3" # Logical port 'bar1' in switch 'bar'. ADD_NAMESPACES(bar1) ADD_VETH(bar1, bar1, br-int, "192.168.2.2/24", "f0:00:00:01:02:04", \ "192.168.2.1") ovn-nbctl lsp-add bar bar1 \ -- lsp-set-addresses bar1 "f0:00:00:01:02:04 192.168.2.2" # Logical port 'alice1' in switch 'alice'. ADD_NAMESPACES(alice1) ADD_VETH(alice1, alice1, br-int, "172.16.1.2/24", "f0:00:00:01:02:05", \ "172.16.1.1") ovn-nbctl lsp-add alice alice1 \ -- lsp-set-addresses alice1 "f0:00:00:01:02:05 172.16.1.2" # Add DNAT rules AT_CHECK([ovn-nbctl lr-nat-add R1 dnat_and_snat 172.16.1.3 192.168.1.2 foo1 00:00:02:02:03:04]) AT_CHECK([ovn-nbctl lr-nat-add R1 dnat_and_snat 172.16.1.4 192.168.1.3 foo2 00:00:02:02:03:05]) # Add a SNAT rule AT_CHECK([ovn-nbctl lr-nat-add R1 snat 172.16.1.1 192.168.0.0/16]) ovn-nbctl --wait=hv sync OVS_WAIT_UNTIL([ovs-ofctl dump-flows br-int | grep 'nat(src=172.16.1.1)']) # North-South DNAT: 'alice1' pings 'foo1' using 172.16.1.3. NS_CHECK_EXEC([alice1], [ping -q -c 3 -i 0.3 -w 2 172.16.1.3 | FORMAT_PING], \ [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) # We verify that DNAT indeed happened via 'dump-conntrack' command. AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(172.16.1.3) | \ sed -e 's/zone=[[0-9]]*/zone=/'], [0], [dnl icmp,orig=(src=172.16.1.2,dst=172.16.1.3,id=,type=8,code=0),reply=(src=192.168.1.2,dst=172.16.1.2,id=,type=0,code=0),zone= ]) # South-North SNAT: 'foo2' pings 'alice1'. But 'alice1' receives traffic # from 172.16.1.4 NS_CHECK_EXEC([foo2], [ping -q -c 3 -i 0.3 -w 2 172.16.1.2 | FORMAT_PING], \ [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) # We verify that SNAT indeed happened via 'dump-conntrack' command. AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(172.16.1.4) | \ sed -e 's/zone=[[0-9]]*/zone=/'], [0], [dnl icmp,orig=(src=192.168.1.3,dst=172.16.1.2,id=,type=8,code=0),reply=(src=172.16.1.2,dst=172.16.1.4,id=,type=0,code=0),zone= ]) # South-North SNAT: 'bar1' pings 'alice1'. But 'alice1' receives traffic # from 172.16.1.1 NS_CHECK_EXEC([bar1], [ping -q -c 3 -i 0.3 -w 2 172.16.1.2 | FORMAT_PING], \ [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) # We verify that SNAT indeed happened via 'dump-conntrack' command. AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(172.16.1.1) | \ sed -e 's/zone=[[0-9]]*/zone=/'], [0], [dnl icmp,orig=(src=192.168.2.2,dst=172.16.1.2,id=,type=8,code=0),reply=(src=172.16.1.2,dst=172.16.1.1,id=,type=0,code=0),zone= ]) OVS_APP_EXIT_AND_WAIT([ovn-controller]) 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 OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d /connection dropped.*/d"]) AT_CLEANUP AT_SETUP([ovn -- DNAT and SNAT on distributed router - E/W]) AT_KEYWORDS([ovnnat]) CHECK_CONNTRACK() CHECK_CONNTRACK_NAT() ovn_start OVS_TRAFFIC_VSWITCHD_START() ADD_BR([br-int]) # Set external-ids in br-int needed for ovn-controller ovs-vsctl \ -- set Open_vSwitch . external-ids:system-id=hv1 \ -- set Open_vSwitch . external-ids:ovn-remote=unix:$ovs_base/ovn-sb/ovn-sb.sock \ -- set Open_vSwitch . external-ids:ovn-encap-type=geneve \ -- set Open_vSwitch . external-ids:ovn-encap-ip=169.0.0.1 \ -- set bridge br-int fail-mode=secure other-config:disable-in-band=true # Start ovn-controller start_daemon ovn-controller # Logical network: # One LR R1 with switches foo (192.168.1.0/24), bar (192.168.2.0/24), # and alice (172.16.1.0/24) connected to it. The port between R1 and # alice is the router gateway port where the R1 NAT rules are applied. # # foo -- R1 -- alice # | # bar ---- ovn-nbctl lr-add R1 ovn-nbctl ls-add foo ovn-nbctl ls-add bar ovn-nbctl ls-add alice ovn-nbctl lrp-add R1 foo 00:00:01:01:02:03 192.168.1.1/24 ovn-nbctl lrp-add R1 bar 00:00:01:01:02:04 192.168.2.1/24 ovn-nbctl lrp-add R1 alice 00:00:02:01:02:03 172.16.1.1/24 \ -- set Logical_Router_Port alice options:redirect-chassis=hv1 # Connect foo to R1 ovn-nbctl lsp-add foo rp-foo -- set Logical_Switch_Port rp-foo \ type=router options:router-port=foo \ -- lsp-set-addresses rp-foo router # Connect bar to R1 ovn-nbctl lsp-add bar rp-bar -- set Logical_Switch_Port rp-bar \ type=router options:router-port=bar \ -- lsp-set-addresses rp-bar router # Connect alice to R1 ovn-nbctl lsp-add alice rp-alice -- set Logical_Switch_Port rp-alice \ type=router options:router-port=alice \ -- lsp-set-addresses rp-alice router # Logical port 'foo1' in switch 'foo'. ADD_NAMESPACES(foo1) ADD_VETH(foo1, foo1, br-int, "192.168.1.2/24", "f0:00:00:01:02:03", \ "192.168.1.1") ovn-nbctl lsp-add foo foo1 \ -- lsp-set-addresses foo1 "f0:00:00:01:02:03 192.168.1.2" # Logical port 'foo2' in switch 'foo'. ADD_NAMESPACES(foo2) ADD_VETH(foo2, foo2, br-int, "192.168.1.3/24", "f0:00:00:01:02:06", \ "192.168.1.1") ovn-nbctl lsp-add foo foo2 \ -- lsp-set-addresses foo2 "f0:00:00:01:02:06 192.168.1.3" # Logical port 'bar1' in switch 'bar'. ADD_NAMESPACES(bar1) ADD_VETH(bar1, bar1, br-int, "192.168.2.2/24", "f0:00:00:01:02:04", \ "192.168.2.1") ovn-nbctl lsp-add bar bar1 \ -- lsp-set-addresses bar1 "f0:00:00:01:02:04 192.168.2.2" # Logical port 'alice1' in switch 'alice'. ADD_NAMESPACES(alice1) ADD_VETH(alice1, alice1, br-int, "172.16.1.2/24", "f0:00:00:01:02:05", \ "172.16.1.1") ovn-nbctl lsp-add alice alice1 \ -- lsp-set-addresses alice1 "f0:00:00:01:02:05 172.16.1.2" # Add DNAT rules AT_CHECK([ovn-nbctl lr-nat-add R1 dnat_and_snat 172.16.1.3 192.168.1.2 foo1 00:00:02:02:03:04]) AT_CHECK([ovn-nbctl lr-nat-add R1 dnat_and_snat 172.16.1.4 192.168.2.2 bar1 00:00:02:02:03:05]) # Add a SNAT rule AT_CHECK([ovn-nbctl lr-nat-add R1 snat 172.16.1.1 192.168.0.0/16]) ovn-nbctl --wait=hv sync OVS_WAIT_UNTIL([ovs-ofctl dump-flows br-int | grep 'nat(src=172.16.1.1)']) echo "------ hv dump ------" ovs-ofctl show br-int ovs-ofctl dump-flows br-int echo "---------------------" # East-West No NAT: 'foo1' pings 'bar1' using 192.168.2.2. NS_CHECK_EXEC([foo1], [ping -q -c 3 -i 0.3 -w 2 192.168.2.2 | FORMAT_PING], \ [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) # We verify that no NAT happened via 'dump-conntrack' command. AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(192.168.2.2) | \ sed -e 's/zone=[[0-9]]*/zone=/' | wc -l], [0], [0 ]) # East-West No NAT: 'foo2' pings 'bar1' using 192.168.2.2. NS_CHECK_EXEC([foo2], [ping -q -c 3 -i 0.3 -w 2 192.168.2.2 | FORMAT_PING], \ [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) # We verify that no NAT happened via 'dump-conntrack' command. AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(192.168.2.2) | \ sed -e 's/zone=[[0-9]]*/zone=/' | wc -l], [0], [0 ]) # East-West No NAT: 'bar1' pings 'foo2' using 192.168.1.3. NS_CHECK_EXEC([bar1], [ping -q -c 3 -i 0.3 -w 2 192.168.1.3 | FORMAT_PING], \ [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) # We verify that no NAT happened via 'dump-conntrack' command. AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(192.168.2.2) | \ sed -e 's/zone=[[0-9]]*/zone=/' | wc -l], [0], [0 ]) # East-West NAT: 'foo1' pings 'bar1' using 172.16.1.4. NS_CHECK_EXEC([foo1], [ping -q -c 3 -i 0.3 -w 2 172.16.1.4 | FORMAT_PING], \ [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) # Check conntrack entries. First SNAT of 'foo1' address happens. # Then DNAT of 'bar1' address happens (listed first below). AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(172.16.1.3) | \ sed -e 's/zone=[[0-9]]*/zone=/'], [0], [dnl icmp,orig=(src=172.16.1.3,dst=172.16.1.4,id=,type=8,code=0),reply=(src=192.168.2.2,dst=172.16.1.3,id=,type=0,code=0),zone= icmp,orig=(src=192.168.1.2,dst=172.16.1.4,id=,type=8,code=0),reply=(src=172.16.1.4,dst=172.16.1.3,id=,type=0,code=0),zone= ]) # East-West NAT: 'foo2' pings 'bar1' using 172.16.1.4. NS_CHECK_EXEC([foo2], [ping -q -c 3 -i 0.3 -w 2 172.16.1.4 | FORMAT_PING], \ [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) # Check conntrack entries. First SNAT of 'foo2' address happens. # Then DNAT of 'bar1' address happens (listed first below). AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(172.16.1.1) | \ sed -e 's/zone=[[0-9]]*/zone=/'], [0], [dnl icmp,orig=(src=172.16.1.1,dst=172.16.1.4,id=,type=8,code=0),reply=(src=192.168.2.2,dst=172.16.1.1,id=,type=0,code=0),zone= icmp,orig=(src=192.168.1.3,dst=172.16.1.4,id=,type=8,code=0),reply=(src=172.16.1.4,dst=172.16.1.1,id=,type=0,code=0),zone= ]) OVS_APP_EXIT_AND_WAIT([ovn-controller]) 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 OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d /connection dropped.*/d"]) AT_CLEANUP