From dbef1347a0d1d0cb306beb1c9f088e41ddf75dae Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Fri, 4 Nov 2016 10:03:59 +0000 Subject: doc: Convert tutorial/OVN-Tutorial to rST There's a mismash of absolute and relative URLs, but these will be resolved by the move to Sphinx. In addition, the URLs pointing to the test scripts are removed as they will break when we move to Sphinx. This is because they won't be published with the Sphinx docs, ruling out relative links, and OVS evolves too fast to rely on non-breaking links to GitHub. Better to rely on shell examples like we do elsewhere and let the user figure it out. Signed-off-by: Stephen Finucane Signed-off-by: Russell Bryant --- tutorial/OVN-Tutorial.md | 1036 -------------------------------------------- tutorial/automake.mk | 2 +- tutorial/ovn-tutorial.rst | 975 +++++++++++++++++++++++++++++++++++++++++ tutorial/ovn/env1/setup.sh | 2 +- tutorial/ovn/env6/setup.sh | 2 +- 5 files changed, 978 insertions(+), 1039 deletions(-) delete mode 100644 tutorial/OVN-Tutorial.md create mode 100644 tutorial/ovn-tutorial.rst (limited to 'tutorial') diff --git a/tutorial/OVN-Tutorial.md b/tutorial/OVN-Tutorial.md deleted file mode 100644 index a9f591f7e..000000000 --- a/tutorial/OVN-Tutorial.md +++ /dev/null @@ -1,1036 +0,0 @@ -OVN Tutorial -============ - -This tutorial is intended to give you a tour of the basic OVN features using -`ovs-sandbox` as a simulated test environment. It’s assumed that you have an -understanding of OVS before going through this tutorial. Detail about OVN is -covered in [ovn-architecture(7)], but this tutorial lets you quickly see it in -action. - -Getting Started ---------------- - -For some general information about `ovs-sandbox`, see the “Getting Started” -section of [Tutorial.md]. - -`ovs-sandbox` does not include OVN support by default. To enable OVN, you must -pass the `--ovn` flag. For example, if running it straight from the ovs git -tree you would run: - - $ make sandbox SANDBOXFLAGS="--ovn" - -Running the sandbox with OVN enabled does the following additional steps to the -environment: - - 1. Creates the `OVN_Northbound` and `OVN_Southbound` databases as described in - [ovn-nb(5)] and [ovn-sb(5)]. - - 2. Creates a backup server for `OVN_Southbond` database. Sandbox launch - screen provides the instructions on accessing the backup database. - However access to the backup server is not required to go through the - tutorial. - - 3. Creates the `hardware_vtep` database as described in [vtep(5)]. - - 4. Runs the [ovn-northd(8)], [ovn-controller(8)], and [ovn-controller-vtep(8)] - daemons. - - 5. Makes OVN and VTEP utilities available for use in the environment, - including [vtep-ctl(8)], [ovn-nbctl(8)], and [ovn-sbctl(8)]. - -Note that each of these demos assumes you start with a fresh sandbox -environment. **Re-run `ovs-sandbox` before starting each section.** - -Using GDB ---------- - -GDB support is not required to go through the tutorial. See the “Using GDB” -section of [Tutorial.md] for more info. Additional flags exist for launching -the debugger for the OVN programs: - - --gdb-ovn-northd - --gdb-ovn-controller - --gdb-ovn-controller-vtep - - -1) Simple two-port setup ------------------------- - -This first environment is the simplest OVN example. It demonstrates using OVN -with a single logical switch that has two logical ports, both residing on the -same hypervisor. - -Start by running the setup script for this environment. - -[View ovn/env1/setup.sh][env1setup]. - - $ ovn/env1/setup.sh - -You can use the `ovn-nbctl` utility to see an overview of the logical topology. - - $ ovn-nbctl show - switch 78687d53-e037-4555-bcd3-f4f8eaf3f2aa (sw0) - port sw0-port1 - addresses: [“00:00:00:00:00:01”] - port sw0-port2 - addresses: [“00:00:00:00:00:02”] - -The `ovn-sbctl` utility can be used to see into the state stored in the -`OVN_Southbound` database. The `show` command shows that there is a single -chassis with two logical ports bound to it. In a more realistic -multi-hypervisor environment, this would list all hypervisors and where all -logical ports are located. - - $ ovn-sbctl show - Chassis “56b18105-5706-46ef-80c4-ff20979ab068” - Encap geneve - ip: “127.0.0.1” - Port_Binding “sw0-port1” - Port_Binding “sw0-port2” - -OVN creates logical flows to describe how the network should behave in logical -space. Each chassis then creates OpenFlow flows based on those logical flows -that reflect its own local view of the network. The `ovn-sbctl` command can -show the logical flows. - - $ ovn-sbctl lflow-list - Datapath: 2503dd42-14b1-414a-abbf-33e554e09ddc Pipeline: ingress - table=0 (ls_in_port_sec_l2 ), priority=100 , match=(eth.src[40]), action=(drop;) - table=0 (ls_in_port_sec_l2 ), priority=100 , match=(vlan.present), action=(drop;) - table=0 (ls_in_port_sec_l2 ), priority=50 , match=(inport == “sw0-port1” && eth.src == {00:00:00:00:00:01}), action=(next;) - table=0 (ls_in_port_sec_l2 ), priority=50 , match=(inport == “sw0-port2” && eth.src == {00:00:00:00:00:02}), action=(next;) - table=1 (ls_in_port_sec_ip ), priority=0 , match=(1), action=(next;) - table=2 (ls_in_port_sec_nd ), priority=90 , match=(inport == “sw0-port1” && eth.src == 00:00:00:00:00:01 && arp.sha == 00:00:00:00:00:01), action=(next;) - table=2 (ls_in_port_sec_nd ), priority=90 , match=(inport == “sw0-port1” && eth.src == 00:00:00:00:00:01 && ip6 && nd && ((nd.sll == 00:00:00:00:00:00 || nd.sll == 00:00:00:00:00:01) || ((nd.tll == 00:00:00:00:00:00 || nd.tll == 00:00:00:00:00:01)))), action=(next;) - table=2 (ls_in_port_sec_nd ), priority=90 , match=(inport == “sw0-port2” && eth.src == 00:00:00:00:00:02 && arp.sha == 00:00:00:00:00:02), action=(next;) - table=2 (ls_in_port_sec_nd ), priority=90 , match=(inport == “sw0-port2” && eth.src == 00:00:00:00:00:02 && ip6 && nd && ((nd.sll == 00:00:00:00:00:00 || nd.sll == 00:00:00:00:00:02) || ((nd.tll == 00:00:00:00:00:00 || nd.tll == 00:00:00:00:00:02)))), action=(next;) - table=2 (ls_in_port_sec_nd ), priority=80 , match=(inport == “sw0-port1” && (arp || nd)), action=(drop;) - table=2 (ls_in_port_sec_nd ), priority=80 , match=(inport == “sw0-port2” && (arp || nd)), action=(drop;) - table=2 (ls_in_port_sec_nd ), priority=0 , match=(1), action=(next;) - table=3 (ls_in_pre_acl ), priority=0 , match=(1), action=(next;) - table=4 (ls_in_pre_lb ), priority=0 , match=(1), action=(next;) - table=5 (ls_in_pre_stateful ), priority=100 , match=(reg0[0] == 1), action=(ct_next;) - table=5 (ls_in_pre_stateful ), priority=0 , match=(1), action=(next;) - table=6 (ls_in_acl ), priority=0 , match=(1), action=(next;) - table=7 (ls_in_lb ), priority=0 , match=(1), action=(next;) - table=8 (ls_in_stateful ), priority=100 , match=(reg0[1] == 1), action=(ct_commit; next;) - table=8 (ls_in_stateful ), priority=100 , match=(reg0[2] == 1), action=(ct_lb;) - table=8 (ls_in_stateful ), priority=0 , match=(1), action=(next;) - table=9 (ls_in_arp_rsp ), priority=0 , match=(1), action=(next;) - table=10(ls_in_l2_lkup ), priority=100 , match=(eth.mcast), action=(outport = “_MC_flood”; output;) - table=10(ls_in_l2_lkup ), priority=50 , match=(eth.dst == 00:00:00:00:00:01), action=(outport = “sw0-port1”; output;) - table=10(ls_in_l2_lkup ), priority=50 , match=(eth.dst == 00:00:00:00:00:02), action=(outport = “sw0-port2”; output;) - Datapath: 2503dd42-14b1-414a-abbf-33e554e09ddc Pipeline: egress - table=0 (ls_out_pre_lb ), priority=0 , match=(1), action=(next;) - table=1 (ls_out_pre_acl ), priority=0 , match=(1), action=(next;) - table=2 (ls_out_pre_stateful), priority=100 , match=(reg0[0] == 1), action=(ct_next;) - table=2 (ls_out_pre_stateful), priority=0 , match=(1), action=(next;) - table=3 (ls_out_lb ), priority=0 , match=(1), action=(next;) - table=4 (ls_out_acl ), priority=0 , match=(1), action=(next;) - table=5 (ls_out_stateful ), priority=100 , match=(reg0[1] == 1), action=(ct_commit; next;) - table=5 (ls_out_stateful ), priority=100 , match=(reg0[2] == 1), action=(ct_lb;) - table=5 (ls_out_stateful ), priority=0 , match=(1), action=(next;) - table=6 (ls_out_port_sec_ip ), priority=0 , match=(1), action=(next;) - table=7 (ls_out_port_sec_l2 ), priority=100 , match=(eth.mcast), action=(output;) - table=7 (ls_out_port_sec_l2 ), priority=50 , match=(outport == “sw0-port1” && eth.dst == {00:00:00:00:00:01}), action=(output;) - table=7 (ls_out_port_sec_l2 ), priority=50 , match=(outport == “sw0-port2” && eth.dst == {00:00:00:00:00:02}), action=(output;) - -Now we can start taking a closer look at how `ovn-controller` has programmed the -local switch. Before looking at the flows, we can use `ovs-ofctl` to verify the -OpenFlow port numbers for each of the logical ports on the switch. The output -shows that `lport1`, which corresponds with our logical port `sw0-port1`, has an -OpenFlow port number of `1`. Similarly, `lport2` has an OpenFlow port number of -`2`. - - $ ovs-ofctl show br-int - OFPT_FEATURES_REPLY (xid=0x2): dpid:00003e1ba878364d - n_tables:254, n_buffers:0 - capabilities: FLOW_STATS TABLE_STATS PORT_STATS QUEUE_STATS ARP_MATCH_IP - actions: output enqueue set_vlan_vid set_vlan_pcp strip_vlan mod_dl_src mod_dl_dst mod_nw_src mod_nw_dst mod_nw_tos mod_tp_src mod_tp_dst - 1(lport1): addr:aa:55:aa:55:00:07 - config: PORT_DOWN - state: LINK_DOWN - speed: 0 Mbps now, 0 Mbps max - 2(lport2): addr:aa:55:aa:55:00:08 - config: PORT_DOWN - state: LINK_DOWN - speed: 0 Mbps now, 0 Mbps max - LOCAL(br-int): addr:3e:1b:a8:78:36:4d - config: PORT_DOWN - state: LINK_DOWN - speed: 0 Mbps now, 0 Mbps max - OFPT_GET_CONFIG_REPLY (xid=0x4): frags=normal miss_send_len=0 - -Finally, use `ovs-ofctl` to see the OpenFlow flows for `br-int`. Note that some -fields have been omitted for brevity. - - $ ovs-ofctl -O OpenFlow13 dump-flows br-int - OFPST_FLOW reply (OF1.3) (xid=0x2): - table=0, priority=100,in_port=1 actions=set_field:0x1->metadata,set_field:0x1->reg6,resubmit(,16) - table=0, priority=100,in_port=2 actions=set_field:0x1->metadata,set_field:0x2->reg6,resubmit(,16) - table=16, priority=100,metadata=0x1,vlan_tci=0x1000/0x1000 actions=drop - table=16, priority=100,metadata=0x1,dl_src=01:00:00:00:00:00/01:00:00:00:00:00 actions=drop - table=16, priority=50,reg6=0x1,metadata=0x1,dl_src=00:00:00:00:00:01 actions=resubmit(,17) - table=16, priority=50,reg6=0x2,metadata=0x1,dl_src=00:00:00:00:00:02 actions=resubmit(,17) - table=17, priority=0,metadata=0x1 actions=resubmit(,18) - table=18, priority=90,icmp6,reg6=0x2,metadata=0x1,dl_src=00:00:00:00:00:02,icmp_type=136,icmp_code=0,nd_tll=00:00:00:00:00:00 actions=resubmit(,19) - table=18, priority=90,icmp6,reg6=0x2,metadata=0x1,dl_src=00:00:00:00:00:02,icmp_type=136,icmp_code=0,nd_tll=00:00:00:00:00:02 actions=resubmit(,19) - table=18, priority=90,icmp6,reg6=0x1,metadata=0x1,dl_src=00:00:00:00:00:01,icmp_type=136,icmp_code=0,nd_tll=00:00:00:00:00:00 actions=resubmit(,19) - table=18, priority=90,icmp6,reg6=0x1,metadata=0x1,dl_src=00:00:00:00:00:01,icmp_type=136,icmp_code=0,nd_tll=00:00:00:00:00:01 actions=resubmit(,19) - table=18, priority=90,icmp6,reg6=0x1,metadata=0x1,dl_src=00:00:00:00:00:01,icmp_type=135,icmp_code=0,nd_sll=00:00:00:00:00:01 actions=resubmit(,19) - table=18, priority=90,icmp6,reg6=0x1,metadata=0x1,dl_src=00:00:00:00:00:01,icmp_type=135,icmp_code=0,nd_sll=00:00:00:00:00:00 actions=resubmit(,19) - table=18, priority=90,icmp6,reg6=0x2,metadata=0x1,dl_src=00:00:00:00:00:02,icmp_type=135,icmp_code=0,nd_sll=00:00:00:00:00:00 actions=resubmit(,19) - table=18, priority=90,icmp6,reg6=0x2,metadata=0x1,dl_src=00:00:00:00:00:02,icmp_type=135,icmp_code=0,nd_sll=00:00:00:00:00:02 actions=resubmit(,19) - table=18, priority=90,arp,reg6=0x1,metadata=0x1,dl_src=00:00:00:00:00:01,arp_sha=00:00:00:00:00:01 actions=resubmit(,19) - table=18, priority=90,arp,reg6=0x2,metadata=0x1,dl_src=00:00:00:00:00:02,arp_sha=00:00:00:00:00:02 actions=resubmit(,19) - table=18, priority=80,icmp6,reg6=0x2,metadata=0x1,icmp_type=136,icmp_code=0 actions=drop - table=18, priority=80,icmp6,reg6=0x1,metadata=0x1,icmp_type=136,icmp_code=0 actions=drop - table=18, priority=80,icmp6,reg6=0x1,metadata=0x1,icmp_type=135,icmp_code=0 actions=drop - table=18, priority=80,icmp6,reg6=0x2,metadata=0x1,icmp_type=135,icmp_code=0 actions=drop - table=18, priority=80,arp,reg6=0x2,metadata=0x1 actions=drop - table=18, priority=80,arp,reg6=0x1,metadata=0x1 actions=drop - table=18, priority=0,metadata=0x1 actions=resubmit(,19) - table=19, priority=0,metadata=0x1 actions=resubmit(,20) - table=20, priority=0,metadata=0x1 actions=resubmit(,21) - table=21, priority=0,metadata=0x1 actions=resubmit(,22) - table=22, priority=0,metadata=0x1 actions=resubmit(,23) - table=23, priority=0,metadata=0x1 actions=resubmit(,24) - table=24, priority=0,metadata=0x1 actions=resubmit(,25) - table=25, priority=0,metadata=0x1 actions=resubmit(,26) - table=26, priority=100,metadata=0x1,dl_dst=01:00:00:00:00:00/01:00:00:00:00:00 actions=set_field:0xffff->reg7,resubmit(,32) - table=26, priority=50,metadata=0x1,dl_dst=00:00:00:00:00:01 actions=set_field:0x1->reg7,resubmit(,32) - table=26, priority=50,metadata=0x1,dl_dst=00:00:00:00:00:02 actions=set_field:0x2->reg7,resubmit(,32) - table=32, priority=0 actions=resubmit(,33) - table=33, priority=100,reg7=0x1,metadata=0x1 actions=resubmit(,34) - table=33, priority=100,reg7=0xffff,metadata=0x1 actions=set_field:0x2->reg7,resubmit(,34),set_field:0x1->reg7,resubmit(,34),set_field:0xffff->reg7 - table=33, priority=100,reg7=0x2,metadata=0x1 actions=resubmit(,34) - table=34, priority=100,reg6=0x1,reg7=0x1,metadata=0x1 actions=drop - table=34, priority=100,reg6=0x2,reg7=0x2,metadata=0x1 actions=drop - table=34, priority=0 actions=set_field:0->reg0,set_field:0->reg1,set_field:0->reg2,resubmit(,48) - table=48, priority=0,metadata=0x1 actions=resubmit(,49) - table=49, priority=0,metadata=0x1 actions=resubmit(,50) - table=50, priority=0,metadata=0x1 actions=resubmit(,51) - table=51, priority=0,metadata=0x1 actions=resubmit(,52) - table=52, priority=0,metadata=0x1 actions=resubmit(,53) - table=53, priority=0,metadata=0x1 actions=resubmit(,54) - table=54, priority=0,metadata=0x1 actions=resubmit(,55) - table=55, priority=100,metadata=0x1,dl_dst=01:00:00:00:00:00/01:00:00:00:00:00 actions=resubmit(,64) - table=55, priority=50,reg7=0x2,metadata=0x1,dl_dst=00:00:00:00:00:02 actions=resubmit(,64) - table=55, priority=50,reg7=0x1,metadata=0x1,dl_dst=00:00:00:00:00:01 actions=resubmit(,64) - table=64, priority=100,reg7=0x1,metadata=0x1 actions=output:1 - -The `ovs-appctl` command can be used to generate an OpenFlow trace of how a -packet would be processed in this configuration. This first trace shows a -packet from `sw0-port1` to `sw0-port2`. The packet arrives from port `1` and -should be output to port `2`. - -[View ovn/env1/packet1.sh][env1packet1]. - - $ ovn/env1/packet1.sh - -Trace a broadcast packet from `sw0-port1`. The packet arrives from port `1` and -should be output to port `2`. - -[View ovn/env1/packet2.sh][env1packet2]. - - $ ovn/env1/packet2.sh - -You can extend this setup by adding additional ports. For example, to add a -third port, run this command: - -[View ovn/env1/add-third-port.sh][env1thirdport]. - - $ ovn/env1/add-third-port.sh - -Now if you do another trace of a broadcast packet from `sw0-port1`, you will see -that it is output to both ports `2` and `3`. - - $ ovn/env1/packet2.sh - -The logical port may have an unknown set of Ethernet addresses. When an OVN logical -switch processes a unicast Ethernet frame whose destination MAC address is not in any -logical port’s addresses column, it delivers it to the port (or ports) whose addresses -columns include unknown. - -[View ovn/env1/add-unknown-ports.sh][env1unknownports]. - - $ ovn/env1/add-unknown-ports.sh - -This trace shows a packet from `sw0-port1` to `sw0-port4`, `sw0-port5` whose addresses -columns include unknown. You will see that it is output to both ports `4` and `5`. - -[View ovn/env1/packet3.sh][env1packet3]. - - $ ovn/env1/packet3.sh - -The logical port would restrict the host to sending packets from and receiving packets -to the ethernet addresses defined in the logical port’s port_security column. -In addition to the restrictions described for Ethernet addresses above, such an element -of port_security restricts the IPv4 or IPv6 addresses from which the host may send and -to which it may receive packets to the specified addresses. - -[View ovn/env1/add-security-ip-ports.sh][env1securityport]. - - $ ovn/env1/add-security-ip-ports.sh - -This trace shows a packet from `sw0-port6` to `sw0-port7`. - -[View ovn/env1/packet4.sh][env1packet4]. - - $ ovn/env1/packet4.sh - -2) 2 switches, 4 ports ----------------------- - -This environment is an extension of the last example. The previous example -showed two ports on a single logical switch. In this environment we add a -second logical switch that also has two ports. This lets you start to see how -`ovn-controller` creates flows for isolated networks to co-exist on the same -switch. - -[View ovn/env2/setup.sh][env2setup]. - - $ ovn/env2/setup.sh - -View the logical topology with `ovn-nbctl`. - - $ ovn-nbctl show - switch e3190dc2-89d1-44ed-9308-e7077de782b3 (sw0) - port sw0-port1 - addresses: 00:00:00:00:00:01 - port sw0-port2 - addresses: 00:00:00:00:00:02 - switch c8ed4c5f-9733-43f6-93da-795b1aabacb1 (sw1) - port sw1-port1 - addresses: 00:00:00:00:00:03 - port sw1-port2 - addresses: 00:00:00:00:00:04 - -Physically, all ports reside on the same chassis. - - $ ovn-sbctl show - Chassis “56b18105-5706-46ef-80c4-ff20979ab068” - Encap geneve - ip: “127.0.0.1” - Port_Binding “sw1-port2” - Port_Binding “sw0-port2” - Port_Binding “sw0-port1” - Port_Binding “sw1-port1” - -OVN creates separate logical flows for each logical switch. - - $ ovn-sbctl lflow-list - Datapath: 7ee908c1-b0d3-4d03-acc9-42cd7ef7f27d Pipeline: ingress - table=0 (ls_in_port_sec_l2 ), priority=100 , match=(eth.src[40]), action=(drop;) - table=0 (ls_in_port_sec_l2 ), priority=100 , match=(vlan.present), action=(drop;) - table=0 (ls_in_port_sec_l2 ), priority=50 , match=(inport == "sw1-port1" && eth.src == {00:00:00:00:00:03}), action=(next;) - table=0 (ls_in_port_sec_l2 ), priority=50 , match=(inport == "sw1-port2" && eth.src == {00:00:00:00:00:04}), action=(next;) - table=1 (ls_in_port_sec_ip ), priority=0 , match=(1), action=(next;) - table=2 (ls_in_port_sec_nd ), priority=90 , match=(inport == "sw1-port1" && eth.src == 00:00:00:00:00:03 && arp.sha == 00:00:00:00:00:03), action=(next;) - table=2 (ls_in_port_sec_nd ), priority=90 , match=(inport == "sw1-port1" && eth.src == 00:00:00:00:00:03 && ip6 && nd && ((nd.sll == 00:00:00:00:00:00 || nd.sll == 00:00:00:00:00:03) || ((nd.tll == 00:00:00:00:00:00 || nd.tll == 00:00:00:00:00:03)))), action=(next;) - table=2 (ls_in_port_sec_nd ), priority=90 , match=(inport == "sw1-port2" && eth.src == 00:00:00:00:00:04 && arp.sha == 00:00:00:00:00:04), action=(next;) - table=2 (ls_in_port_sec_nd ), priority=90 , match=(inport == "sw1-port2" && eth.src == 00:00:00:00:00:04 && ip6 && nd && ((nd.sll == 00:00:00:00:00:00 || nd.sll == 00:00:00:00:00:04) || ((nd.tll == 00:00:00:00:00:00 || nd.tll == 00:00:00:00:00:04)))), action=(next;) - table=2 (ls_in_port_sec_nd ), priority=80 , match=(inport == "sw1-port1" && (arp || nd)), action=(drop;) - table=2 (ls_in_port_sec_nd ), priority=80 , match=(inport == "sw1-port2" && (arp || nd)), action=(drop;) - table=2 (ls_in_port_sec_nd ), priority=0 , match=(1), action=(next;) - table=3 (ls_in_pre_acl ), priority=0 , match=(1), action=(next;) - table=4 (ls_in_pre_lb ), priority=0 , match=(1), action=(next;) - table=5 (ls_in_pre_stateful ), priority=100 , match=(reg0[0] == 1), action=(ct_next;) - table=5 (ls_in_pre_stateful ), priority=0 , match=(1), action=(next;) - table=6 (ls_in_acl ), priority=0 , match=(1), action=(next;) - table=7 (ls_in_lb ), priority=0 , match=(1), action=(next;) - table=8 (ls_in_stateful ), priority=100 , match=(reg0[1] == 1), action=(ct_commit; next;) - table=8 (ls_in_stateful ), priority=100 , match=(reg0[2] == 1), action=(ct_lb;) - table=8 (ls_in_stateful ), priority=0 , match=(1), action=(next;) - table=9 (ls_in_arp_rsp ), priority=0 , match=(1), action=(next;) - table=10(ls_in_l2_lkup ), priority=100 , match=(eth.mcast), action=(outport = "_MC_flood"; output;) - table=10(ls_in_l2_lkup ), priority=50 , match=(eth.dst == 00:00:00:00:00:03), action=(outport = "sw1-port1"; output;) - table=10(ls_in_l2_lkup ), priority=50 , match=(eth.dst == 00:00:00:00:00:04), action=(outport = "sw1-port2"; output;) - Datapath: 7ee908c1-b0d3-4d03-acc9-42cd7ef7f27d Pipeline: egress - table=0 (ls_out_pre_lb ), priority=0 , match=(1), action=(next;) - table=1 (ls_out_pre_acl ), priority=0 , match=(1), action=(next;) - table=2 (ls_out_pre_stateful), priority=100 , match=(reg0[0] == 1), action=(ct_next;) - table=2 (ls_out_pre_stateful), priority=0 , match=(1), action=(next;) - table=3 (ls_out_lb ), priority=0 , match=(1), action=(next;) - table=4 (ls_out_acl ), priority=0 , match=(1), action=(next;) - table=5 (ls_out_stateful ), priority=100 , match=(reg0[1] == 1), action=(ct_commit; next;) - table=5 (ls_out_stateful ), priority=100 , match=(reg0[2] == 1), action=(ct_lb;) - table=5 (ls_out_stateful ), priority=0 , match=(1), action=(next;) - table=6 (ls_out_port_sec_ip ), priority=0 , match=(1), action=(next;) - table=7 (ls_out_port_sec_l2 ), priority=100 , match=(eth.mcast), action=(output;) - table=7 (ls_out_port_sec_l2 ), priority=50 , match=(outport == "sw1-port1" && eth.dst == {00:00:00:00:00:03}), action=(output;) - table=7 (ls_out_port_sec_l2 ), priority=50 , match=(outport == "sw1-port2" && eth.dst == {00:00:00:00:00:04}), action=(output;) - Datapath: 9ea0c8f9-4f82-4be3-a6c7-6e6f9c2de583 Pipeline: ingress - table=0 (ls_in_port_sec_l2 ), priority=100 , match=(eth.src[40]), action=(drop;) - table=0 (ls_in_port_sec_l2 ), priority=100 , match=(vlan.present), action=(drop;) - table=0 (ls_in_port_sec_l2 ), priority=50 , match=(inport == "sw0-port1" && eth.src == {00:00:00:00:00:01}), action=(next;) - table=0 (ls_in_port_sec_l2 ), priority=50 , match=(inport == "sw0-port2" && eth.src == {00:00:00:00:00:02}), action=(next;) - table=1 (ls_in_port_sec_ip ), priority=0 , match=(1), action=(next;) - table=2 (ls_in_port_sec_nd ), priority=90 , match=(inport == "sw0-port1" && eth.src == 00:00:00:00:00:01 && arp.sha == 00:00:00:00:00:01), action=(next;) - table=2 (ls_in_port_sec_nd ), priority=90 , match=(inport == "sw0-port1" && eth.src == 00:00:00:00:00:01 && ip6 && nd && ((nd.sll == 00:00:00:00:00:00 || nd.sll == 00:00:00:00:00:01) || ((nd.tll == 00:00:00:00:00:00 || nd.tll == 00:00:00:00:00:01)))), action=(next;) - table=2 (ls_in_port_sec_nd ), priority=90 , match=(inport == "sw0-port2" && eth.src == 00:00:00:00:00:02 && arp.sha == 00:00:00:00:00:02), action=(next;) - table=2 (ls_in_port_sec_nd ), priority=90 , match=(inport == "sw0-port2" && eth.src == 00:00:00:00:00:02 && ip6 && nd && ((nd.sll == 00:00:00:00:00:00 || nd.sll == 00:00:00:00:00:02) || ((nd.tll == 00:00:00:00:00:00 || nd.tll == 00:00:00:00:00:02)))), action=(next;) - table=2 (ls_in_port_sec_nd ), priority=80 , match=(inport == "sw0-port1" && (arp || nd)), action=(drop;) - table=2 (ls_in_port_sec_nd ), priority=80 , match=(inport == "sw0-port2" && (arp || nd)), action=(drop;) - table=2 (ls_in_port_sec_nd ), priority=0 , match=(1), action=(next;) - table=3 (ls_in_pre_acl ), priority=0 , match=(1), action=(next;) - table=4 (ls_in_pre_lb ), priority=0 , match=(1), action=(next;) - table=5 (ls_in_pre_stateful ), priority=100 , match=(reg0[0] == 1), action=(ct_next;) - table=5 (ls_in_pre_stateful ), priority=0 , match=(1), action=(next;) - table=6 (ls_in_acl ), priority=0 , match=(1), action=(next;) - table=7 (ls_in_lb ), priority=0 , match=(1), action=(next;) - table=8 (ls_in_stateful ), priority=100 , match=(reg0[1] == 1), action=(ct_commit; next;) - table=8 (ls_in_stateful ), priority=100 , match=(reg0[2] == 1), action=(ct_lb;) - table=8 (ls_in_stateful ), priority=0 , match=(1), action=(next;) - table=9 (ls_in_arp_rsp ), priority=0 , match=(1), action=(next;) - table=10(ls_in_l2_lkup ), priority=100 , match=(eth.mcast), action=(outport = "_MC_flood"; output;) - table=10(ls_in_l2_lkup ), priority=50 , match=(eth.dst == 00:00:00:00:00:01), action=(outport = "sw0-port1"; output;) - table=10(ls_in_l2_lkup ), priority=50 , match=(eth.dst == 00:00:00:00:00:02), action=(outport = "sw0-port2"; output;) - Datapath: 9ea0c8f9-4f82-4be3-a6c7-6e6f9c2de583 Pipeline: egress - table=0 (ls_out_pre_lb ), priority=0 , match=(1), action=(next;) - table=1 (ls_out_pre_acl ), priority=0 , match=(1), action=(next;) - table=2 (ls_out_pre_stateful), priority=100 , match=(reg0[0] == 1), action=(ct_next;) - table=2 (ls_out_pre_stateful), priority=0 , match=(1), action=(next;) - table=3 (ls_out_lb ), priority=0 , match=(1), action=(next;) - table=4 (ls_out_acl ), priority=0 , match=(1), action=(next;) - table=5 (ls_out_stateful ), priority=100 , match=(reg0[1] == 1), action=(ct_commit; next;) - table=5 (ls_out_stateful ), priority=100 , match=(reg0[2] == 1), action=(ct_lb;) - table=5 (ls_out_stateful ), priority=0 , match=(1), action=(next;) - table=6 (ls_out_port_sec_ip ), priority=0 , match=(1), action=(next;) - table=7 (ls_out_port_sec_l2 ), priority=100 , match=(eth.mcast), action=(output;) - table=7 (ls_out_port_sec_l2 ), priority=50 , match=(outport == "sw0-port1" && eth.dst == {00:00:00:00:00:01}), action=(output;) - table=7 (ls_out_port_sec_l2 ), priority=50 , match=(outport == "sw0-port2" && eth.dst == {00:00:00:00:00:02}), action=(output;) - -In this setup, `sw0-port1` and `sw0-port2` can send packets to each other, but -not to either of the ports on `sw1`. This first trace shows a packet from -`sw0-port1` to `sw0-port2`. You should see th packet arrive on OpenFlow port -`1` and output to OpenFlow port `2`. - -[View ovn/env2/packet1.sh][env2packet1]. - - $ ovn/env2/packet1.sh - -This next example shows a packet from `sw0-port1` with a destination MAC address -of `00:00:00:00:00:03`, which is the MAC address for `sw1-port1`. Since these -ports are not on the same logical switch, the packet should just be dropped. - -[View ovn/env2/packet2.sh][env2packet2]. - - $ ovn/env2/packet2.sh - -3) Two Hypervisors ------------------- - -The first two examples started by showing OVN on a single hypervisor. A more -realistic deployment of OVN would span multiple hypervisors. This example -creates a single logical switch with 4 logical ports. It then simulates having -two hypervisors with two of the logical ports bound to each hypervisor. - -[View ovn/env3/setup.sh][env3setup]. - - $ ovn/env3/setup.sh - -You can start by viewing the logical topology with `ovn-nbctl`. - - $ ovn-nbctl show - switch b977dc03-79a5-41ba-9665-341a80e1abfd (sw0) - port sw0-port1 - addresses: 00:00:00:00:00:01 - port sw0-port2 - addresses: 00:00:00:00:00:02 - port sw0-port4 - addresses: 00:00:00:00:00:04 - port sw0-port3 - addresses: 00:00:00:00:00:03 - -Using `ovn-sbctl` to view the state of the system, we can see that there are two -chassis: one local that we can interact with, and a fake remote chassis. Two -logical ports are bound to each. Both chassis have an IP address of localhost, -but in a realistic deployment that would be the IP address used for tunnels to -that chassis. - - $ ovn-sbctl show - Chassis “56b18105-5706-46ef-80c4-ff20979ab068” - Encap geneve - ip: “127.0.0.1” - Port_Binding “sw0-port2” - Port_Binding “sw0-port1” - Chassis fakechassis - Encap geneve - ip: “127.0.0.1” - Port_Binding “sw0-port4” - Port_Binding “sw0-port3” - -Packets between `sw0-port1` and `sw0-port2` behave just like the previous -examples. Packets to ports on a remote chassis are the interesting part of this -example. You may have noticed before that OVN’s logical flows are broken up -into ingress and egress tables. Given a packet from `sw0-port1` on the local -chassis to `sw0-port3` on the remote chassis, the ingress pipeline is executed -on the local switch. OVN then determines that it must forward the packet over a -geneve tunnel. When it arrives at the remote chassis, the egress pipeline will -be executed there. - -This first packet trace shows the first part of this example. It’s a packet -from `sw0-port1` to `sw0-port3` from the perspective of the local chassis. -`sw0-port1` is OpenFlow port `1`. The tunnel to the fake remote chassis is -OpenFlow port `3`. You should see the ingress pipeline being executed and then -the packet output to port `3`, the geneve tunnel. - -[View ovn/env3/packet1.sh][env3packet1]. - - $ ovn/env3/packet1.sh - -To simulate what would happen when that packet arrives at the remote chassis we -can flip this example around. Consider a packet from `sw0-port3` to -`sw0-port1`. This trace shows what would happen when that packet arrives at the -local chassis. The packet arrives on OpenFlow port `3` (the tunnel). You should -then see the egress pipeline get executed and the packet output to OpenFlow port -`1`. - -[View ovn/env3/packet2.sh][env3packet2]. - - $ ovn/env3/packet2.sh - -4) Locally attached networks ----------------------------- - -While OVN is generally focused on the implementation of logical networks using -overlays, it’s also possible to use OVN as a control plane to manage logically -direct connectivity to networks that are locally accessible to each chassis. - -This example includes two hypervisors. Both hypervisors have two ports on them. -We want to use OVN to manage the connectivity of these ports to a network -attached to each hypervisor that we will call “physnet1”. - -This scenario requires some additional configuration of `ovn-controller`. We -must configure a mapping between `physnet1` and a local OVS bridge that provides -connectivity to that network. We call these “bridge mappings”. For our -example, the following script creates a bridge called `br-eth1` and then -configures `ovn-controller` with a bridge mapping from `physnet1` to `br-eth1`. - -We want to create a fake second chassis and then create the topology that tells -OVN we want both ports on both hypervisors connected to `physnet1`. The way this -is modeled in OVN is by creating a logical switch for each port. The logical -switch has the regular VIF port and a `localnet` port. - -[View ovn/env4/setup.sh][env4setup]. - - $ ovn/env4/setup.sh - -At this point we should be able to see that `ovn-controller` has automatically -created patch ports between `br-int` and `br-eth1`. - - $ ovs-vsctl show - c0a06d85-d70a-4e11-9518-76a92588b34e - Bridge "br-eth1" - Port "patch-provnet1-1-physnet1-to-br-int" - Interface "patch-provnet1-1-physnet1-to-br-int" - type: patch - options: {peer="patch-br-int-to-provnet1-1-physnet1"} - Port "br-eth1" - Interface "br-eth1" - type: internal - Port "patch-provnet1-2-physnet1-to-br-int" - Interface "patch-provnet1-2-physnet1-to-br-int" - type: patch - options: {peer="patch-br-int-to-provnet1-2-physnet1"} - Bridge br-int - fail_mode: secure - Port "ovn-fakech-0" - Interface "ovn-fakech-0" - type: geneve - options: {key=flow, remote_ip="127.0.0.1"} - Port "patch-br-int-to-provnet1-2-physnet1" - Interface "patch-br-int-to-provnet1-2-physnet1" - type: patch - options: {peer="patch-provnet1-2-physnet1-to-br-int"} - Port br-int - Interface br-int - type: internal - Port "patch-br-int-to-provnet1-1-physnet1" - Interface "patch-br-int-to-provnet1-1-physnet1" - type: patch - options: {peer="patch-provnet1-1-physnet1-to-br-int"} - Port "lport2" - Interface "lport2" - Port "lport1" - Interface "lport1 - - -The logical topology from `ovn-nbctl` should look like this. - - $ ovn-nbctl show - switch 9db81140-5504-4f60-be3d-2bee45b57e27 (provnet1-2) - port provnet1-2-port1 - addresses: ["00:00:00:00:00:02"] - port provnet1-2-physnet1 - addresses: ["unknown"] - switch cf175cb9-35c5-41cf-8bc7-2d322cdbead0 (provnet1-3) - port provnet1-3-physnet1 - addresses: ["unknown"] - port provnet1-3-port1 - addresses: ["00:00:00:00:00:03"] - switch b85f7af6-8055-4db2-ba93-efc7887cf38f (provnet1-1) - port provnet1-1-port1 - addresses: ["00:00:00:00:00:01"] - port provnet1-1-physnet1 - addresses: ["unknown"] - switch 63a5e276-8807-417d-bbec-a7e907e106b1 (provnet1-4) - port provnet1-4-port1 - addresses: ["00:00:00:00:00:04"] - port provnet1-4-physnet1 - addresses: ["unknown"] - -`port1` on each logical switch represents a regular logical port for a VIF on a -hypervisor. `physnet1` on each logical switch is the special `localnet` port. -You can use `ovn-nbctl` to see that this port has a `type` and `options` set. - - $ ovn-nbctl lsp-get-type provnet1-1-physnet1 - localnet - - $ ovn-nbctl lsp-get-options provnet1-1-physnet1 - network_name=physnet1 - -The physical topology should reflect that there are two regular ports on each -chassis. - - $ ovn-sbctl show - Chassis "56b18105-5706-46ef-80c4-ff20979ab068" - hostname: sandbox - Encap geneve - ip: "127.0.0.1" - Port_Binding "provnet1-1-port1" - Port_Binding "provnet1-2-port1" - Chassis fakechassis - Encap geneve - ip: "127.0.0.1" - Port_Binding "provnet1-3-port1" - Port_Binding "provnet1-4-port1" - -All four of our ports should be able to communicate with each other, but they do -so through `physnet1`. A packet from any of these ports to any destination -should be output to the OpenFlow port number that corresponds to the patch port -to `br-eth1`. - -This example assumes following OpenFlow port number mappings: - -* 1 = tunnel to the fake second chassis -* 2 = `lport1`, which is the logical port named `provnet1-1-port1` -* 3 = `patch-br-int-to-provnet1-1-physnet1`, patch port to `br-eth1` -* 4 = `lport2`, which is the logical port named `provnet1-2-port1` -* 5 = `patch-br-int-to-provnet1-2-physnet1`, patch port to `br-eth1` - -We get those port numbers using `ovs-ofctl`: - - $ ovs-ofctl show br-int - OFPT_FEATURES_REPLY (xid=0x2): dpid:00002a84824b0d40 - n_tables:254, n_buffers:0 - capabilities: FLOW_STATS TABLE_STATS PORT_STATS QUEUE_STATS ARP_MATCH_IP - actions: output enqueue set_vlan_vid set_vlan_pcp strip_vlan mod_dl_src mod_dl_dst - 1(ovn-fakech-0): addr:aa:55:aa:55:00:0e - config: PORT_DOWN - state: LINK_DOWN - speed: 0 Mbps now, 0 Mbps max - 2(lport1): addr:aa:55:aa:55:00:0f - config: PORT_DOWN - state: LINK_DOWN - speed: 0 Mbps now, 0 Mbps max - 3(patch-br-int-to): addr:7a:6f:8a:d5:69:2a - config: 0 - state: 0 - speed: 0 Mbps now, 0 Mbps max - 4(lport2): addr:aa:55:aa:55:00:10 - config: PORT_DOWN - state: LINK_DOWN - speed: 0 Mbps now, 0 Mbps max - 5(patch-br-int-to): addr:4a:fd:c1:11:fc:a5 - config: 0 - state: 0 - speed: 0 Mbps now, 0 Mbps max - LOCAL(br-int): addr:2a:84:82:4b:0d:40 - config: PORT_DOWN - state: LINK_DOWN - speed: 0 Mbps now, 0 Mbps max - OFPT_GET_CONFIG_REPLY (xid=0x4): frags=normal miss_send_len=0 - -This first trace shows a packet from `provnet1-1-port1` with a destination MAC -address of `provnet1-2-port1`. We expect the packets from `lport1`(OpenFlow port 2) -to be sent out to `lport2`(OpenFlow port 4). For example, the following topology -illustrates how the packets travel from `lport1` to `lport2`. - - `lport1` --> `patch-br-int-to-provnet1-1-physnet1`(OpenFlow port 3) - --> `br-eth1` --> `patch-br-int-to-provnet1-2-physnet1` --> `lport2`(OpenFlow port 4) - -Similarly, We expect the packets from `provnet1-2-port1` to be sent out to -`provnet1-1-port1`. We then expect the network to handle getting the packet to its -destination. In practice, this will be optimized at `br-eth1` and the packet won’t -actually go out and back on the network. - -[View ovn/env4/packet1.sh][env4packet1]. - - $ ovn/env4/packet1.sh - -This next trace shows an example of a packet being sent to a destination on another -hypervisor. The source is `provnet1-1-port1`, but the destination is `provnet1-3-port1`, -which is on the other fake chassis. As usual, we expect the output to be to `br-eth1` -(`patch-br-int-to-provnet1-1-physnet1`, OpenFlow port 3). - -[View ovn/env4/packet2.sh][env4packet2]. - - $ ovn/env4/packet2.sh - -This next test shows a broadcast packet. The destination should still only be -OpenFlow port 3 and 4. - -[View ovn/env4/packet3.sh][env4packet3] - - $ ovn/env4/packet3.sh - -Finally, this last trace shows what happens when a broadcast packet arrives -from the network. In this case, it simulates a broadcast that originated from a -port on the remote fake chassis and arrived at the local chassis via `br-eth1`. -We should see it output to both local ports that are attached to this network -(OpenFlow ports 2 and 4). - -[View ovn/env4/packet4.sh][env4packet4] - - $ ovn/env4/packet4.sh - -5) Locally attached networks with VLANs ---------------------------------------- - -This example is an extension of the previous one. We take the same setup and -add two more ports to each hypervisor. Instead of having the new ports directly -connected to `physnet1` as before, we indicate that we want them on VLAN 101 of -`physnet1`. This shows how `localnet` ports can be used to provide connectivity -to either a flat network or a VLAN on that network. - -[View ovn/env5/setup.sh][env5setup] - - $ ovn/env5/setup.sh - -The logical topology shown by `ovn-nbctl` is similar to `env4`, except we now -have 8 regular VIF ports connected to `physnet1` instead of 4. The additional 4 -ports we have added are all on VLAN 101 of `physnet1`. Note that the `localnet` -ports representing connectivity to VLAN 101 of `physnet1` have the `tag` field -set to `101`. - - $ ovn-nbctl show - switch 3e60b940-00bf-44c6-9db6-04abf28d7e5f (provnet1-1) - port provnet1-1-physnet1 - addresses: ["unknown"] - port provnet1-1-port1 - addresses: ["00:00:00:00:00:01"] - switch 87f6bea0-f74d-4f39-aa65-ca1f94670429 (provnet1-2) - port provnet1-2-port1 - addresses: ["00:00:00:00:00:02"] - port provnet1-2-physnet1 - addresses: ["unknown"] - switch e6c9cb69-a056-428d-aa40-e903ce416dcd (provnet1-6-101) - port provnet1-6-101-port1 - addresses: ["00:00:00:00:00:06"] - port provnet1-6-physnet1-101 - parent: - tag: 101 - addresses: ["unknown"] - switch 5f8f72ca-6030-4f66-baea-fe6174eb54df (provnet1-4) - port provnet1-4-port1 - addresses: ["00:00:00:00:00:04"] - port provnet1-4-physnet1 - addresses: ["unknown"] - switch 15d585eb-d2c1-45ea-a946-b08de0eb2f55 (provnet1-7-101) - port provnet1-7-physnet1-101 - parent: - tag: 101 - addresses: ["unknown"] - port provnet1-7-101-port1 - addresses: ["00:00:00:00:00:07"] - switch 7be4aabe-1bb0-4e16-a755-a1f6d81c1c2f (provnet1-5-101) - port provnet1-5-101-port1 - addresses: ["00:00:00:00:00:05"] - port provnet1-5-physnet1-101 - parent: - tag: 101 - addresses: ["unknown"] - switch 9bbdbf0e-50f3-4286-ba5a-29bf347531bb (provnet1-8-101) - port provnet1-8-101-port1 - addresses: ["00:00:00:00:00:08"] - port provnet1-8-physnet1-101 - parent: - tag: 101 - addresses: ["unknown"] - switch 70d053f7-2bca-4dff-96ae-bd728d3ba1d2 (provnet1-3) - port provnet1-3-physnet1 - addresses: ["unknown"] - port provnet1-3-port1 - addresses: ["00:00:00:00:00:03"] - -The physical topology shows that we have 4 regular VIF ports on each simulated -hypervisor. - - $ ovn-sbctl show - Chassis fakechassis - Encap geneve - ip: "127.0.0.1" - Port_Binding "provnet1-3-port1" - Port_Binding "provnet1-8-101-port1" - Port_Binding "provnet1-7-101-port1" - Port_Binding "provnet1-4-port1" - Chassis "56b18105-5706-46ef-80c4-ff20979ab068" - hostname: sandbox - Encap geneve - ip: "127.0.0.1" - Port_Binding "provnet1-2-port1" - Port_Binding "provnet1-5-101-port1" - Port_Binding "provnet1-1-port1" - Port_Binding "provnet1-6-101-port1" - -All of the traces from the previous example, `env4`, should work in this -environment and provide the same result. Now we can show what happens for the -ports connected to VLAN 101. This first example shows a packet originating from -`provnet1-5-101-port1`, which is OpenFlow port 6. We should see VLAN tag 101 -pushed on the packet and then output to OpenFlow port 7, the patch port to -`br-eth1` (the bridge providing connectivity to `physnet1`), and finally arrives -on OpenFlow port 8. - -[View ovn/env5/packet1.sh][env5packet1]. - - $ ovn/env5/packet1.sh - -If we look at a broadcast packet arriving on VLAN 101 of `physnet1`, we should -see it output to OpenFlow ports 6 and 8 only. - -[View ovn/env5/packet2.sh][env5packet2]. - - $ ovn/env5/packet2.sh - - -6) Stateful ACLs ----------------- - -ACLs provide a way to do distributed packet filtering for OVN networks. One -example use of ACLs is that OpenStack Neutron uses them to implement security -groups. ACLs are implemented using conntrack integration with OVS. - -Start with a simple logical switch with 2 logical ports. - -[View ovn/env6/setup.sh][env6setup]. - - $ ovn/env6/setup.sh - -A common use case would be the following policy applied for `sw0-port1`: - -* Allow outbound IP traffic and associated return traffic. -* Allow incoming ICMP requests and associated return traffic. -* Allow incoming SSH connections and associated return traffic. -* Drop other incoming IP traffic. - -The following script applies this policy to our environment. - -[View ovn/env6/add-acls.sh][env6acls]. - - $ ovn/env6/add-acls.sh - -We can view the configured ACLs on this network using the `ovn-nbctl` command. - - $ ovn-nbctl acl-list sw0 - from-lport 1002 (inport == “sw0-port1” && ip) allow-related - to-lport 1002 (outport == “sw0-port1” && ip && icmp) allow-related - to-lport 1002 (outport == “sw0-port1” && ip && tcp && tcp.dst == 22) allow-related - to-lport 1001 (outport == “sw0-port1” && ip) drop - -Now that we have ACLs configured, there are new entries in the logical flow -table in the stages `switch_in_pre_acl`, switch_in_acl`, `switch_out_pre_acl`, -and `switch_out_acl`. - - $ ovn-sbctl lflow-list - -Let’s look more closely at `switch_out_pre_acl` and `switch_out_acl`. - -In `switch_out_pre_acl`, we match IP traffic and put it through the connection -tracker. This populates the connection state fields so that we can apply policy -as appropriate. - - table=0(switch_out_pre_acl), priority= 100, match=(ip), action=(ct_next;) - table=1(switch_out_pre_acl), priority= 0, match=(1), action=(next;) - -In `switch_out_acl`, we allow packets associated with existing connections. We -drop packets that are deemed to be invalid (such as non-SYN TCP packet not -associated with an existing connection). - - table=1(switch_out_acl), priority=65535, match=(!ct.est && ct.rel && !ct.new && !ct.inv), action=(next;) - table=1(switch_out_acl), priority=65535, match=(ct.est && !ct.rel && !ct.new && !ct.inv), action=(next;) - table=1(switch_out_acl), priority=65535, match=(ct.inv), action=(drop;) - -For new connections, we apply our configured ACL policy to decide whether to -allow the connection or not. In this case, we’ll allow ICMP or SSH. Otherwise, -we’ll drop the packet. - - table=1(switch_out_acl), priority= 2002, match=(ct.new && (outport == “sw0-port1” && ip && icmp)), action=(ct_commit; next;) - table=1(switch_out_acl), priority= 2002, match=(ct.new && (outport == “sw0-port1” && ip && tcp && tcp.dst == 22)), action=(ct_commit; next;) - table=1(switch_out_acl), priority= 2001, match=(outport == “sw0-port1” && ip), action=(drop;) - -When using ACLs, the default policy is to allow and track IP connections. Based -on our above policy, IP traffic directed at `sw0-port1` will never hit this flow -at priority 1. - - table=1(switch_out_acl), priority= 1, match=(ip), action=(ct_commit; next;) - table=1(switch_out_acl), priority= 0, match=(1), action=(next;) - -Note that conntrack integration is not yet supported in ovs-sandbox, so the -OpenFlow flows will not represent what you’d see in a real environment. The -logical flows described above give a very good idea of what the flows look like, -though. - -[This blog post][openstack-ovn-acl-blog] discusses OVN ACLs from an OpenStack -perspective and also provides an example of what the resulting OpenFlow flows -look like. - -7) Container Ports ------------------- - -OVN supports containers running directly on the hypervisors and running -containers inside VMs. This example shows how OVN supports network -virtualization to containers when run inside VMs. Details about how to use -docker containers in OVS can be found [here][openvswitch-docker]. - -To support container traffic created inside a VM and to distinguish network -traffic coming from different container vifs, for each container a logical -port needs to be created with parent name set to the VM's logical port and -the tag set to the vlan tag of the container vif. - -Start with a simple logical switch with 3 logical ports. - -[View ovn/env7/setup.sh][env7setup]. - - $ ovn/env7/setup.sh - -Lets create a container vif attached to the logical port 'sw0-port1' and -another container vif attached to the logical port 'sw0-port2'. - -[View ovn/env7/add-container-ports.sh][env7contports] - - $ ovn/env7/add-container-ports.sh - -Run the `ovn-nbctl` command to see the logical ports - - $ovn-nbctl show - - -As you can see a logical port 'csw0-cport1' is created on a logical -switch 'csw0' whose parent is 'sw0-port1' and it has tag set to 42. -And a logical port 'csw0-cport2' is created on the logical switch 'csw0' -whose parent is 'sw0-port2' and it has tag set to 43. - -Bridge 'br-vmport1' represents the ovs bridge running inside the VM -connected to the logical port 'sw0-port1'. In this tutorial the ovs port -to 'sw0-port1' is created as a patch port with its peer connected to the -ovs bridge 'br-vmport1'. An ovs port 'cport1' is added to 'br-vmport1' -which represents the container interface connected to the ovs bridge -and vlan tag set to 42. Similarly 'br-vmport2' represents the ovs bridge -for the logical port 'sw0-port2' and 'cport2' connected to 'br-vmport2' -with vlan tag set to 43. - -This first trace shows a packet from 'csw0-port1' with a destination mac -address of 'csw0-port2'. You can see ovs bridge of the vm 'br-vmport1' tags -the traffic with vlan id 42 and the traffic reaches to the br-int because -of the patch port. As you can see below `ovn-controller` has added a flow -to strip the vlan tag and set the reg6 and metadata appropriately. - - $ ovs-ofctl -O OpenFlow13 dump-flows br-int - OFPST_FLOW reply (OF1.3) (xid=0x2): - cookie=0x0, duration=2767.032s, table=0, n_packets=0, n_bytes=0, priority=150,in_port=3,dl_vlan=42 actions=pop_vlan,set_field:0x3->reg5,set_field:0x2->metadata,set_field:0x1->reg6,resubmit(,16) - cookie=0x0, duration=2767.002s, table=0, n_packets=0, n_bytes=0, priority=150,in_port=4,dl_vlan=43 actions=pop_vlan,set_field:0x4->reg5,set_field:0x2->metadata,set_field:0x2->reg6,resubmit(,16) - cookie=0x0, duration=2767.032s, table=0, n_packets=0, n_bytes=0, priority=100,in_port=3 actions=set_field:0x1->reg5,set_field:0x1->metadata,set_field:0x1->reg6,resubmit(,16) - cookie=0x0, duration=2767.001s, table=0, n_packets=0, n_bytes=0, priority=100,in_port=4 actions=set_field:0x2->reg5,set_field:0x1->metadata,set_field:0x2->reg6,resubmit(,16) - -[View ovn/env7/packet1.sh][env7packet1]. - - $ ovn/env7/packet1.sh - - -The second trace shows a packet from 'csw0-port2' to 'csw0-port1'. - -[View ovn/env7/packet2.sh][env7packet2]. - - $ ovn/env7/packet2.sh - -You can extend this setup by adding additional container ports with two -hypervisors. Please see the tutorial 3 above. - -8) L2Gateway Ports ------------------- - -L2Gateway provides a way to connect logical switch ports of type `l2gateway` to -a physical network. The difference between `l2gateway` ports and `localnet` ports -is that an `l2gateway` port is bound to a specific chassis. A single chassis -serves as the L2 gateway to the physical network and all traffic between -chassis continues to go over geneve tunnels. - -Start with a simple logical switch with 3 logical ports. - -[View ovn/env8/setup.sh][env8setup]. - - $ ovn/env8/setup.sh - -This first example shows a packet originating from `lport1`, which is OpenFlow port 1. -We expect all packets from `lport1` to be sent out to br-eth1 (`patch-br-int-to-sw0-port3`, -OpenFlow port 3). The patch port to br-eth1 provides connectivity to the physical network. - -[View ovn/env8/packet1.sh][env8packet1]. - - $ ovn/env8/packet1.sh - -The last trace shows what happens when a broadcast packet arrives from the network. -In this case, it simulates a broadcast that originated from a port on the physical network -and arrived at the local chassis via br-eth1. We should see it output to the local port `lport1` -and `lport2`. - -[View ovn/env8/packet2.sh][env8packet2]. - - $ ovn/env8/packet2.sh - -[ovn-architecture(7)]:http://openvswitch.org/support/dist-docs/ovn-architecture.7.html -[Tutorial.md]:https://github.com/openvswitch/ovs/blob/master/tutorial/tutorial.rst -[ovn-nb(5)]:http://openvswitch.org/support/dist-docs/ovn-nb.5.html -[ovn-sb(5)]:http://openvswitch.org/support/dist-docs/ovn-sb.5.html -[vtep(5)]:http://openvswitch.org/support/dist-docs/vtep.5.html -[ovn-northd(8)]:http://openvswitch.org/support/dist-docs/ovn-northd.8.html -[ovn-controller(8)]:http://openvswitch.org/support/dist-docs/ovn-controller.8.html -[ovn-controller-vtep(8)]:http://openvswitch.org/support/dist-docs/ovn-controller-vtep.8.html -[vtep-ctl(8)]:http://openvswitch.org/support/dist-docs/vtep-ctl.8.html -[ovn-nbctl(8)]:http://openvswitch.org/support/dist-docs/ovn-nbctl.8.html -[ovn-sbctl(8)]:http://openvswitch.org/support/dist-docs/ovn-sbctl.8.html -[env1setup]:https://github.com/openvswitch/ovs/blob/master/tutorial/ovn/env1/setup.sh -[env1packet1]:https://github.com/openvswitch/ovs/blob/master/tutorial/ovn/env1/packet1.sh -[env1packet2]:https://github.com/openvswitch/ovs/blob/master/tutorial/ovn/env1/packet2.sh -[env1thirdport]:https://github.com/openvswitch/ovs/blob/master/tutorial/ovn/env1/add-third-port.sh -[env1unknownports]:https://github.com/nickcooper-zhangtonghao/ovs/blob/master/tutorial/ovn/env1/add-unknown-ports.sh -[env1securityport]:https://github.com/nickcooper-zhangtonghao/ovs/blob/master/tutorial/ovn/env1/add-security-ip-ports.sh -[env1packet3]:https://github.com/nickcooper-zhangtonghao/ovs/blob/master/tutorial/ovn/env1/packet3.sh -[env1packet4]:https://github.com/nickcooper-zhangtonghao/ovs/blob/master/tutorial/ovn/env1/packet4.sh -[env2setup]:https://github.com/openvswitch/ovs/blob/master/tutorial/ovn/env2/setup.sh -[env2packet1]:https://github.com/openvswitch/ovs/blob/master/tutorial/ovn/env2/packet1.sh -[env2packet2]:https://github.com/openvswitch/ovs/blob/master/tutorial/ovn/env2/packet2.sh -[env3setup]:https://github.com/openvswitch/ovs/blob/master/tutorial/ovn/env3/setup.sh -[env3packet1]:https://github.com/openvswitch/ovs/blob/master/tutorial/ovn/env3/packet1.sh -[env3packet2]:https://github.com/openvswitch/ovs/blob/master/tutorial/ovn/env3/packet2.sh -[env4setup]:https://github.com/openvswitch/ovs/blob/master/tutorial/ovn/env4/setup.sh -[env4packet1]:https://github.com/openvswitch/ovs/blob/master/tutorial/ovn/env4/packet1.sh -[env4packet2]:https://github.com/openvswitch/ovs/blob/master/tutorial/ovn/env4/packet2.sh -[env4packet3]:https://github.com/openvswitch/ovs/blob/master/tutorial/ovn/env4/packet3.sh -[env4packet4]:https://github.com/openvswitch/ovs/blob/master/tutorial/ovn/env4/packet4.sh -[env5setup]:https://github.com/openvswitch/ovs/blob/master/tutorial/ovn/env5/setup.sh -[env5packet1]:https://github.com/openvswitch/ovs/blob/master/tutorial/ovn/env5/packet1.sh -[env5packet2]:https://github.com/openvswitch/ovs/blob/master/tutorial/ovn/env5/packet2.sh -[env6setup]:https://github.com/openvswitch/ovs/blob/master/tutorial/ovn/env6/setup.sh -[env6acls]:https://github.com/openvswitch/ovs/blob/master/tutorial/ovn/env6/add-acls.sh -[env7setup]:https://github.com/openvswitch/ovs/blob/master/tutorial/ovn/env7/setup.sh -[env7contports]:https://github.com/openvswitch/ovs/blob/master/tutorial/ovn/env7/add-container-ports.sh -[env7packet1]:https://github.com/openvswitch/ovs/blob/master/tutorial/ovn/env7/packet1.sh -[env7packet2]:https://github.com/openvswitch/ovs/blob/master/tutorial/ovn/env7/packet2.sh -[env8setup]:https://github.com/nickcooper-zhangtonghao/ovs/blob/master/tutorial/ovn/env8/setup.sh -[env8packet1]:https://github.com/nickcooper-zhangtonghao/ovs/blob/master/tutorial/ovn/env8/packet1.sh -[env8packet2]:https://github.com/nickcooper-zhangtonghao/ovs/blob/master/tutorial/ovn/env8/packet2.sh -[openstack-ovn-acl-blog]:http://blog.russellbryant.net/2015/10/22/openstack-security-groups-using-ovn-acls/ -[openvswitch-docker]:http://openvswitch.org/support/dist-docs/INSTALL.Docker.rst.txt diff --git a/tutorial/automake.mk b/tutorial/automake.mk index cd7baeccc..79f9b6835 100644 --- a/tutorial/automake.mk +++ b/tutorial/automake.mk @@ -1,6 +1,6 @@ docs += \ tutorial/tutorial.rst \ - tutorial/OVN-Tutorial.md + tutorial/ovn-tutorial.rst EXTRA_DIST += \ tutorial/ovs-sandbox \ tutorial/t-setup \ diff --git a/tutorial/ovn-tutorial.rst b/tutorial/ovn-tutorial.rst new file mode 100644 index 000000000..8d9fc1a43 --- /dev/null +++ b/tutorial/ovn-tutorial.rst @@ -0,0 +1,975 @@ +.. + Licensed under the Apache License, Version 2.0 (the "License"); you may + not use this file except in compliance with the License. You may obtain + a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + License for the specific language governing permissions and limitations + under the License. + + Convention for heading levels in Open vSwitch documentation: + + ======= Heading 0 (reserved for the title in a document) + ------- Heading 1 + ~~~~~~~ Heading 2 + +++++++ Heading 3 + ''''''' Heading 4 + + Avoid deeper levels because they do not render well. + +============ +OVN Tutorial +============ + +This tutorial is intended to give you a tour of the basic OVN features using +``ovs-sandbox`` as a simulated test environment. It's assumed that you have an +understanding of OVS before going through this tutorial. Detail about OVN is +covered in ovn-architecture_, but this tutorial lets you quickly see it in +action. + +Getting Started +--------------- + +For some general information about ``ovs-sandbox``, see the "Getting Started" +section of the tutorial_. + +``ovs-sandbox`` does not include OVN support by default. To enable OVN, you +must pass the ``--ovn`` flag. For example, if running it straight from the ovs +git tree you would run:: + + $ make sandbox SANDBOXFLAGS="--ovn" + +Running the sandbox with OVN enabled does the following additional steps to the +environment: + +1. Creates the ``OVN_Northbound`` and ``OVN_Southbound`` databases as described in + `ovn-nb(5)`_ and `ovn-sb(5)`_. + +2. Creates a backup server for ``OVN_Southbond`` database. Sandbox launch + screen provides the instructions on accessing the backup database. However + access to the backup server is not required to go through the tutorial. + +3. Creates the ``hardware_vtep`` database as described in `vtep(5)`_. + +4. Runs the `ovn-northd(8)`_, `ovn-controller(8)`_, and + `ovn-controller-vtep(8)`_ daemons. + +5. Makes OVN and VTEP utilities available for use in the environment, including + `vtep-ctl(8)`_, `ovn-nbctl(8)`_, and `ovn-sbctl(8)`_. + +Note that each of these demos assumes you start with a fresh sandbox +environment. **Re-run `ovs-sandbox` before starting each section.** + +Using GDB +--------- + +GDB support is not required to go through the tutorial. See the "Using GDB" +section of the `tutorial`_ for more info. Additional flags exist for launching +the debugger for the OVN programs:: + + --gdb-ovn-northd + --gdb-ovn-controller + --gdb-ovn-controller-vtep + +Simple Two Port Setup +--------------------- + +This first environment is the simplest OVN example. It demonstrates using OVN +with a single logical switch that has two logical ports, both residing on the +same hypervisor. + +Start by running the setup script for this environment:: + + $ ovn/env1/setup.sh + +You can use the ``ovn-nbctl`` utility to see an overview of the logical +topology:: + + $ ovn-nbctl show + switch 78687d53-e037-4555-bcd3-f4f8eaf3f2aa (sw0) + port sw0-port1 + addresses: ["00:00:00:00:00:01"] + port sw0-port2 + addresses: ["00:00:00:00:00:02"] + +The ``ovn-sbctl`` utility can be used to see into the state stored in the +``OVN_Southbound`` database. The ``show`` command shows that there is a single +chassis with two logical ports bound to it. In a more realistic +multi-hypervisor environment, this would list all hypervisors and where all +logical ports are located:: + + $ ovn-sbctl show + Chassis "56b18105-5706-46ef-80c4-ff20979ab068" + Encap geneve + ip: "127.0.0.1" + Port_Binding "sw0-port1" + Port_Binding "sw0-port2" + +OVN creates logical flows to describe how the network should behave in logical +space. Each chassis then creates OpenFlow flows based on those logical flows +that reflect its own local view of the network. The ``ovn-sbctl`` command can +show the logical flows:: + + $ ovn-sbctl lflow-list + Datapath: 2503dd42-14b1-414a-abbf-33e554e09ddc Pipeline: ingress + table=0 (ls_in_port_sec_l2 ), priority=100 , match=(eth.src[40]), action=(drop;) + table=0 (ls_in_port_sec_l2 ), priority=100 , match=(vlan.present), action=(drop;) + table=0 (ls_in_port_sec_l2 ), priority=50 , match=(inport == "sw0-port1" && eth.src == {00:00:00:00:00:01}), action=(next;) + table=0 (ls_in_port_sec_l2 ), priority=50 , match=(inport == "sw0-port2" && eth.src == {00:00:00:00:00:02}), action=(next;) + table=1 (ls_in_port_sec_ip ), priority=0 , match=(1), action=(next;) + table=2 (ls_in_port_sec_nd ), priority=90 , match=(inport == "sw0-port1" && eth.src == 00:00:00:00:00:01 && arp.sha == 00:00:00:00:00:01), action=(next;) + table=2 (ls_in_port_sec_nd ), priority=90 , match=(inport == "sw0-port1" && eth.src == 00:00:00:00:00:01 && ip6 && nd && ((nd.sll == 00:00:00:00:00:00 || nd.sll == 00:00:00:00:00:01) || ((nd.tll == 00:00:00:00:00:00 || nd.tll == 00:00:00:00:00:01)))), action=(next;) + table=2 (ls_in_port_sec_nd ), priority=90 , match=(inport == "sw0-port2" && eth.src == 00:00:00:00:00:02 && arp.sha == 00:00:00:00:00:02), action=(next;) + table=2 (ls_in_port_sec_nd ), priority=90 , match=(inport == "sw0-port2" && eth.src == 00:00:00:00:00:02 && ip6 && nd && ((nd.sll == 00:00:00:00:00:00 || nd.sll == 00:00:00:00:00:02) || ((nd.tll == 00:00:00:00:00:00 || nd.tll == 00:00:00:00:00:02)))), action=(next;) + table=2 (ls_in_port_sec_nd ), priority=80 , match=(inport == "sw0-port1" && (arp || nd)), action=(drop;) + table=2 (ls_in_port_sec_nd ), priority=80 , match=(inport == "sw0-port2" && (arp || nd)), action=(drop;) + table=2 (ls_in_port_sec_nd ), priority=0 , match=(1), action=(next;) + table=3 (ls_in_pre_acl ), priority=0 , match=(1), action=(next;) + table=4 (ls_in_pre_lb ), priority=0 , match=(1), action=(next;) + table=5 (ls_in_pre_stateful ), priority=100 , match=(reg0[0] == 1), action=(ct_next;) + table=5 (ls_in_pre_stateful ), priority=0 , match=(1), action=(next;) + table=6 (ls_in_acl ), priority=0 , match=(1), action=(next;) + table=7 (ls_in_lb ), priority=0 , match=(1), action=(next;) + table=8 (ls_in_stateful ), priority=100 , match=(reg0[1] == 1), action=(ct_commit; next;) + table=8 (ls_in_stateful ), priority=100 , match=(reg0[2] == 1), action=(ct_lb;) + table=8 (ls_in_stateful ), priority=0 , match=(1), action=(next;) + table=9 (ls_in_arp_rsp ), priority=0 , match=(1), action=(next;) + table=10(ls_in_l2_lkup ), priority=100 , match=(eth.mcast), action=(outport = "_MC_flood"; output;) + table=10(ls_in_l2_lkup ), priority=50 , match=(eth.dst == 00:00:00:00:00:01), action=(outport = "sw0-port1"; output;) + table=10(ls_in_l2_lkup ), priority=50 , match=(eth.dst == 00:00:00:00:00:02), action=(outport = "sw0-port2"; output;) + Datapath: 2503dd42-14b1-414a-abbf-33e554e09ddc Pipeline: egress + table=0 (ls_out_pre_lb ), priority=0 , match=(1), action=(next;) + table=1 (ls_out_pre_acl ), priority=0 , match=(1), action=(next;) + table=2 (ls_out_pre_stateful), priority=100 , match=(reg0[0] == 1), action=(ct_next;) + table=2 (ls_out_pre_stateful), priority=0 , match=(1), action=(next;) + table=3 (ls_out_lb ), priority=0 , match=(1), action=(next;) + table=4 (ls_out_acl ), priority=0 , match=(1), action=(next;) + table=5 (ls_out_stateful ), priority=100 , match=(reg0[1] == 1), action=(ct_commit; next;) + table=5 (ls_out_stateful ), priority=100 , match=(reg0[2] == 1), action=(ct_lb;) + table=5 (ls_out_stateful ), priority=0 , match=(1), action=(next;) + table=6 (ls_out_port_sec_ip ), priority=0 , match=(1), action=(next;) + table=7 (ls_out_port_sec_l2 ), priority=100 , match=(eth.mcast), action=(output;) + table=7 (ls_out_port_sec_l2 ), priority=50 , match=(outport == "sw0-port1" && eth.dst == {00:00:00:00:00:01}), action=(output;) + table=7 (ls_out_port_sec_l2 ), priority=50 , match=(outport == "sw0-port2" && eth.dst == {00:00:00:00:00:02}), action=(output;) + +Now we can start taking a closer look at how ``ovn-controller`` has programmed +the local switch. Before looking at the flows, we can use ``ovs-ofctl`` to +verify the OpenFlow port numbers for each of the logical ports on the switch. +The output shows that ``lport1``, which corresponds with our logical port +``sw0-port1``, has an OpenFlow port number of ``1``. Similarly, ``lport2`` has +an OpenFlow port number of ``2``:: + + $ ovs-ofctl show br-int + OFPT_FEATURES_REPLY (xid=0x2): dpid:00003e1ba878364d + n_tables:254, n_buffers:0 + capabilities: FLOW_STATS TABLE_STATS PORT_STATS QUEUE_STATS ARP_MATCH_IP + actions: output enqueue set_vlan_vid set_vlan_pcp strip_vlan mod_dl_src mod_dl_dst mod_nw_src mod_nw_dst mod_nw_tos mod_tp_src mod_tp_dst + 1(lport1): addr:aa:55:aa:55:00:07 + config: PORT_DOWN + state: LINK_DOWN + speed: 0 Mbps now, 0 Mbps max + 2(lport2): addr:aa:55:aa:55:00:08 + config: PORT_DOWN + state: LINK_DOWN + speed: 0 Mbps now, 0 Mbps max + LOCAL(br-int): addr:3e:1b:a8:78:36:4d + config: PORT_DOWN + state: LINK_DOWN + speed: 0 Mbps now, 0 Mbps max + OFPT_GET_CONFIG_REPLY (xid=0x4): frags=normal miss_send_len=0 + +Finally, use ``ovs-ofctl`` to see the OpenFlow flows for ``br-int``. Note that +some fields have been omitted for brevity:: + + $ ovs-ofctl -O OpenFlow13 dump-flows br-int + OFPST_FLOW reply (OF1.3) (xid=0x2): + table=0, priority=100,in_port=1 actions=set_field:0x1->metadata,set_field:0x1->reg6,resubmit(,16) + table=0, priority=100,in_port=2 actions=set_field:0x1->metadata,set_field:0x2->reg6,resubmit(,16) + table=16, priority=100,metadata=0x1,vlan_tci=0x1000/0x1000 actions=drop + table=16, priority=100,metadata=0x1,dl_src=01:00:00:00:00:00/01:00:00:00:00:00 actions=drop + table=16, priority=50,reg6=0x1,metadata=0x1,dl_src=00:00:00:00:00:01 actions=resubmit(,17) + table=16, priority=50,reg6=0x2,metadata=0x1,dl_src=00:00:00:00:00:02 actions=resubmit(,17) + table=17, priority=0,metadata=0x1 actions=resubmit(,18) + table=18, priority=90,icmp6,reg6=0x2,metadata=0x1,dl_src=00:00:00:00:00:02,icmp_type=136,icmp_code=0,nd_tll=00:00:00:00:00:00 actions=resubmit(,19) + table=18, priority=90,icmp6,reg6=0x2,metadata=0x1,dl_src=00:00:00:00:00:02,icmp_type=136,icmp_code=0,nd_tll=00:00:00:00:00:02 actions=resubmit(,19) + table=18, priority=90,icmp6,reg6=0x1,metadata=0x1,dl_src=00:00:00:00:00:01,icmp_type=136,icmp_code=0,nd_tll=00:00:00:00:00:00 actions=resubmit(,19) + table=18, priority=90,icmp6,reg6=0x1,metadata=0x1,dl_src=00:00:00:00:00:01,icmp_type=136,icmp_code=0,nd_tll=00:00:00:00:00:01 actions=resubmit(,19) + table=18, priority=90,icmp6,reg6=0x1,metadata=0x1,dl_src=00:00:00:00:00:01,icmp_type=135,icmp_code=0,nd_sll=00:00:00:00:00:01 actions=resubmit(,19) + table=18, priority=90,icmp6,reg6=0x1,metadata=0x1,dl_src=00:00:00:00:00:01,icmp_type=135,icmp_code=0,nd_sll=00:00:00:00:00:00 actions=resubmit(,19) + table=18, priority=90,icmp6,reg6=0x2,metadata=0x1,dl_src=00:00:00:00:00:02,icmp_type=135,icmp_code=0,nd_sll=00:00:00:00:00:00 actions=resubmit(,19) + table=18, priority=90,icmp6,reg6=0x2,metadata=0x1,dl_src=00:00:00:00:00:02,icmp_type=135,icmp_code=0,nd_sll=00:00:00:00:00:02 actions=resubmit(,19) + table=18, priority=90,arp,reg6=0x1,metadata=0x1,dl_src=00:00:00:00:00:01,arp_sha=00:00:00:00:00:01 actions=resubmit(,19) + table=18, priority=90,arp,reg6=0x2,metadata=0x1,dl_src=00:00:00:00:00:02,arp_sha=00:00:00:00:00:02 actions=resubmit(,19) + table=18, priority=80,icmp6,reg6=0x2,metadata=0x1,icmp_type=136,icmp_code=0 actions=drop + table=18, priority=80,icmp6,reg6=0x1,metadata=0x1,icmp_type=136,icmp_code=0 actions=drop + table=18, priority=80,icmp6,reg6=0x1,metadata=0x1,icmp_type=135,icmp_code=0 actions=drop + table=18, priority=80,icmp6,reg6=0x2,metadata=0x1,icmp_type=135,icmp_code=0 actions=drop + table=18, priority=80,arp,reg6=0x2,metadata=0x1 actions=drop + table=18, priority=80,arp,reg6=0x1,metadata=0x1 actions=drop + table=18, priority=0,metadata=0x1 actions=resubmit(,19) + table=19, priority=0,metadata=0x1 actions=resubmit(,20) + table=20, priority=0,metadata=0x1 actions=resubmit(,21) + table=21, priority=0,metadata=0x1 actions=resubmit(,22) + table=22, priority=0,metadata=0x1 actions=resubmit(,23) + table=23, priority=0,metadata=0x1 actions=resubmit(,24) + table=24, priority=0,metadata=0x1 actions=resubmit(,25) + table=25, priority=0,metadata=0x1 actions=resubmit(,26) + table=26, priority=100,metadata=0x1,dl_dst=01:00:00:00:00:00/01:00:00:00:00:00 actions=set_field:0xffff->reg7,resubmit(,32) + table=26, priority=50,metadata=0x1,dl_dst=00:00:00:00:00:01 actions=set_field:0x1->reg7,resubmit(,32) + table=26, priority=50,metadata=0x1,dl_dst=00:00:00:00:00:02 actions=set_field:0x2->reg7,resubmit(,32) + table=32, priority=0 actions=resubmit(,33) + table=33, priority=100,reg7=0x1,metadata=0x1 actions=resubmit(,34) + table=33, priority=100,reg7=0xffff,metadata=0x1 actions=set_field:0x2->reg7,resubmit(,34),set_field:0x1->reg7,resubmit(,34),set_field:0xffff->reg7 + table=33, priority=100,reg7=0x2,metadata=0x1 actions=resubmit(,34) + table=34, priority=100,reg6=0x1,reg7=0x1,metadata=0x1 actions=drop + table=34, priority=100,reg6=0x2,reg7=0x2,metadata=0x1 actions=drop + table=34, priority=0 actions=set_field:0->reg0,set_field:0->reg1,set_field:0->reg2,resubmit(,48) + table=48, priority=0,metadata=0x1 actions=resubmit(,49) + table=49, priority=0,metadata=0x1 actions=resubmit(,50) + table=50, priority=0,metadata=0x1 actions=resubmit(,51) + table=51, priority=0,metadata=0x1 actions=resubmit(,52) + table=52, priority=0,metadata=0x1 actions=resubmit(,53) + table=53, priority=0,metadata=0x1 actions=resubmit(,54) + table=54, priority=0,metadata=0x1 actions=resubmit(,55) + table=55, priority=100,metadata=0x1,dl_dst=01:00:00:00:00:00/01:00:00:00:00:00 actions=resubmit(,64) + table=55, priority=50,reg7=0x2,metadata=0x1,dl_dst=00:00:00:00:00:02 actions=resubmit(,64) + table=55, priority=50,reg7=0x1,metadata=0x1,dl_dst=00:00:00:00:00:01 actions=resubmit(,64) + table=64, priority=100,reg7=0x1,metadata=0x1 actions=output:1 + +The ``ovs-appctl`` command can be used to generate an OpenFlow trace of how a +packet would be processed in this configuration. This first trace shows a +packet from ``sw0-port1`` to ``sw0-port2``. The packet arrives from port ``1`` +and should be output to port ``2``:: + + $ ovn/env1/packet1.sh + +Trace a broadcast packet from ``sw0-port1``. The packet arrives from port +``1`` and should be output to port ``2``:: + + $ ovn/env1/packet2.sh + +You can extend this setup by adding additional ports. For example, to add a +third port, run this command:: + + $ ovn/env1/add-third-port.sh + +Now if you do another trace of a broadcast packet from ``sw0-port1``, you will +see that it is output to both ports ``2`` and ``3``:: + + $ ovn/env1/packet2.sh + +The logical port may have an unknown set of Ethernet addresses. When an OVN logical +switch processes a unicast Ethernet frame whose destination MAC address is not in any +logical port's addresses column, it delivers it to the port (or ports) whose addresses +columns include unknown:: + + $ ovn/env1/add-unknown-ports.sh + +This trace shows a packet from ``sw0-port1`` to ``sw0-port4``, ``sw0-port5`` +whose addresses columns include unknown. You will see that it is output to +both ports ``4`` and ``5``:: + + $ ovn/env1/packet3.sh + +The logical port would restrict the host to sending packets from and receiving +packets to the ethernet addresses defined in the logical port's +``port_security`` column. In addition to the restrictions described for +Ethernet addresses above, such an element of port_security restricts the IPv4 +or IPv6 addresses from which the host may send and to which it may receive +packets to the specified addresses:: + + $ ovn/env1/add-security-ip-ports.sh + +This trace shows a packet from ``sw0-port6`` to ``sw0-port7``:: + + $ ovn/env1/packet4.sh + +Two Switches, Four Ports +------------------------ + +This environment is an extension of the last example. The previous example +showed two ports on a single logical switch. In this environment we add a +second logical switch that also has two ports. This lets you start to see how +``ovn-controller`` creates flows for isolated networks to co-exist on the same +switch:: + + $ ovn/env2/setup.sh + +View the logical topology with ``ovn-nbctl``:: + + $ ovn-nbctl show + switch e3190dc2-89d1-44ed-9308-e7077de782b3 (sw0) + port sw0-port1 + addresses: 00:00:00:00:00:01 + port sw0-port2 + addresses: 00:00:00:00:00:02 + switch c8ed4c5f-9733-43f6-93da-795b1aabacb1 (sw1) + port sw1-port1 + addresses: 00:00:00:00:00:03 + port sw1-port2 + addresses: 00:00:00:00:00:04 + +Physically, all ports reside on the same chassis:: + + $ ovn-sbctl show + Chassis "56b18105-5706-46ef-80c4-ff20979ab068" + Encap geneve + ip: "127.0.0.1" + Port_Binding "sw1-port2" + Port_Binding "sw0-port2" + Port_Binding "sw0-port1" + Port_Binding "sw1-port1" + +OVN creates separate logical flows for each logical switch:: + + $ ovn-sbctl lflow-list + Datapath: 7ee908c1-b0d3-4d03-acc9-42cd7ef7f27d Pipeline: ingress + table=0 (ls_in_port_sec_l2 ), priority=100 , match=(eth.src[40]), action=(drop;) + table=0 (ls_in_port_sec_l2 ), priority=100 , match=(vlan.present), action=(drop;) + table=0 (ls_in_port_sec_l2 ), priority=50 , match=(inport == "sw1-port1" && eth.src == {00:00:00:00:00:03}), action=(next;) + table=0 (ls_in_port_sec_l2 ), priority=50 , match=(inport == "sw1-port2" && eth.src == {00:00:00:00:00:04}), action=(next;) + table=1 (ls_in_port_sec_ip ), priority=0 , match=(1), action=(next;) + table=2 (ls_in_port_sec_nd ), priority=90 , match=(inport == "sw1-port1" && eth.src == 00:00:00:00:00:03 && arp.sha == 00:00:00:00:00:03), action=(next;) + table=2 (ls_in_port_sec_nd ), priority=90 , match=(inport == "sw1-port1" && eth.src == 00:00:00:00:00:03 && ip6 && nd && ((nd.sll == 00:00:00:00:00:00 || nd.sll == 00:00:00:00:00:03) || ((nd.tll == 00:00:00:00:00:00 || nd.tll == 00:00:00:00:00:03)))), action=(next;) + table=2 (ls_in_port_sec_nd ), priority=90 , match=(inport == "sw1-port2" && eth.src == 00:00:00:00:00:04 && arp.sha == 00:00:00:00:00:04), action=(next;) + table=2 (ls_in_port_sec_nd ), priority=90 , match=(inport == "sw1-port2" && eth.src == 00:00:00:00:00:04 && ip6 && nd && ((nd.sll == 00:00:00:00:00:00 || nd.sll == 00:00:00:00:00:04) || ((nd.tll == 00:00:00:00:00:00 || nd.tll == 00:00:00:00:00:04)))), action=(next;) + table=2 (ls_in_port_sec_nd ), priority=80 , match=(inport == "sw1-port1" && (arp || nd)), action=(drop;) + table=2 (ls_in_port_sec_nd ), priority=80 , match=(inport == "sw1-port2" && (arp || nd)), action=(drop;) + table=2 (ls_in_port_sec_nd ), priority=0 , match=(1), action=(next;) + table=3 (ls_in_pre_acl ), priority=0 , match=(1), action=(next;) + table=4 (ls_in_pre_lb ), priority=0 , match=(1), action=(next;) + table=5 (ls_in_pre_stateful ), priority=100 , match=(reg0[0] == 1), action=(ct_next;) + table=5 (ls_in_pre_stateful ), priority=0 , match=(1), action=(next;) + table=6 (ls_in_acl ), priority=0 , match=(1), action=(next;) + table=7 (ls_in_lb ), priority=0 , match=(1), action=(next;) + table=8 (ls_in_stateful ), priority=100 , match=(reg0[1] == 1), action=(ct_commit; next;) + table=8 (ls_in_stateful ), priority=100 , match=(reg0[2] == 1), action=(ct_lb;) + table=8 (ls_in_stateful ), priority=0 , match=(1), action=(next;) + table=9 (ls_in_arp_rsp ), priority=0 , match=(1), action=(next;) + table=10(ls_in_l2_lkup ), priority=100 , match=(eth.mcast), action=(outport = "_MC_flood"; output;) + table=10(ls_in_l2_lkup ), priority=50 , match=(eth.dst == 00:00:00:00:00:03), action=(outport = "sw1-port1"; output;) + table=10(ls_in_l2_lkup ), priority=50 , match=(eth.dst == 00:00:00:00:00:04), action=(outport = "sw1-port2"; output;) + Datapath: 7ee908c1-b0d3-4d03-acc9-42cd7ef7f27d Pipeline: egress + table=0 (ls_out_pre_lb ), priority=0 , match=(1), action=(next;) + table=1 (ls_out_pre_acl ), priority=0 , match=(1), action=(next;) + table=2 (ls_out_pre_stateful), priority=100 , match=(reg0[0] == 1), action=(ct_next;) + table=2 (ls_out_pre_stateful), priority=0 , match=(1), action=(next;) + table=3 (ls_out_lb ), priority=0 , match=(1), action=(next;) + table=4 (ls_out_acl ), priority=0 , match=(1), action=(next;) + table=5 (ls_out_stateful ), priority=100 , match=(reg0[1] == 1), action=(ct_commit; next;) + table=5 (ls_out_stateful ), priority=100 , match=(reg0[2] == 1), action=(ct_lb;) + table=5 (ls_out_stateful ), priority=0 , match=(1), action=(next;) + table=6 (ls_out_port_sec_ip ), priority=0 , match=(1), action=(next;) + table=7 (ls_out_port_sec_l2 ), priority=100 , match=(eth.mcast), action=(output;) + table=7 (ls_out_port_sec_l2 ), priority=50 , match=(outport == "sw1-port1" && eth.dst == {00:00:00:00:00:03}), action=(output;) + table=7 (ls_out_port_sec_l2 ), priority=50 , match=(outport == "sw1-port2" && eth.dst == {00:00:00:00:00:04}), action=(output;) + Datapath: 9ea0c8f9-4f82-4be3-a6c7-6e6f9c2de583 Pipeline: ingress + table=0 (ls_in_port_sec_l2 ), priority=100 , match=(eth.src[40]), action=(drop;) + table=0 (ls_in_port_sec_l2 ), priority=100 , match=(vlan.present), action=(drop;) + table=0 (ls_in_port_sec_l2 ), priority=50 , match=(inport == "sw0-port1" && eth.src == {00:00:00:00:00:01}), action=(next;) + table=0 (ls_in_port_sec_l2 ), priority=50 , match=(inport == "sw0-port2" && eth.src == {00:00:00:00:00:02}), action=(next;) + table=1 (ls_in_port_sec_ip ), priority=0 , match=(1), action=(next;) + table=2 (ls_in_port_sec_nd ), priority=90 , match=(inport == "sw0-port1" && eth.src == 00:00:00:00:00:01 && arp.sha == 00:00:00:00:00:01), action=(next;) + table=2 (ls_in_port_sec_nd ), priority=90 , match=(inport == "sw0-port1" && eth.src == 00:00:00:00:00:01 && ip6 && nd && ((nd.sll == 00:00:00:00:00:00 || nd.sll == 00:00:00:00:00:01) || ((nd.tll == 00:00:00:00:00:00 || nd.tll == 00:00:00:00:00:01)))), action=(next;) + table=2 (ls_in_port_sec_nd ), priority=90 , match=(inport == "sw0-port2" && eth.src == 00:00:00:00:00:02 && arp.sha == 00:00:00:00:00:02), action=(next;) + table=2 (ls_in_port_sec_nd ), priority=90 , match=(inport == "sw0-port2" && eth.src == 00:00:00:00:00:02 && ip6 && nd && ((nd.sll == 00:00:00:00:00:00 || nd.sll == 00:00:00:00:00:02) || ((nd.tll == 00:00:00:00:00:00 || nd.tll == 00:00:00:00:00:02)))), action=(next;) + table=2 (ls_in_port_sec_nd ), priority=80 , match=(inport == "sw0-port1" && (arp || nd)), action=(drop;) + table=2 (ls_in_port_sec_nd ), priority=80 , match=(inport == "sw0-port2" && (arp || nd)), action=(drop;) + table=2 (ls_in_port_sec_nd ), priority=0 , match=(1), action=(next;) + table=3 (ls_in_pre_acl ), priority=0 , match=(1), action=(next;) + table=4 (ls_in_pre_lb ), priority=0 , match=(1), action=(next;) + table=5 (ls_in_pre_stateful ), priority=100 , match=(reg0[0] == 1), action=(ct_next;) + table=5 (ls_in_pre_stateful ), priority=0 , match=(1), action=(next;) + table=6 (ls_in_acl ), priority=0 , match=(1), action=(next;) + table=7 (ls_in_lb ), priority=0 , match=(1), action=(next;) + table=8 (ls_in_stateful ), priority=100 , match=(reg0[1] == 1), action=(ct_commit; next;) + table=8 (ls_in_stateful ), priority=100 , match=(reg0[2] == 1), action=(ct_lb;) + table=8 (ls_in_stateful ), priority=0 , match=(1), action=(next;) + table=9 (ls_in_arp_rsp ), priority=0 , match=(1), action=(next;) + table=10(ls_in_l2_lkup ), priority=100 , match=(eth.mcast), action=(outport = "_MC_flood"; output;) + table=10(ls_in_l2_lkup ), priority=50 , match=(eth.dst == 00:00:00:00:00:01), action=(outport = "sw0-port1"; output;) + table=10(ls_in_l2_lkup ), priority=50 , match=(eth.dst == 00:00:00:00:00:02), action=(outport = "sw0-port2"; output;) + Datapath: 9ea0c8f9-4f82-4be3-a6c7-6e6f9c2de583 Pipeline: egress + table=0 (ls_out_pre_lb ), priority=0 , match=(1), action=(next;) + table=1 (ls_out_pre_acl ), priority=0 , match=(1), action=(next;) + table=2 (ls_out_pre_stateful), priority=100 , match=(reg0[0] == 1), action=(ct_next;) + table=2 (ls_out_pre_stateful), priority=0 , match=(1), action=(next;) + table=3 (ls_out_lb ), priority=0 , match=(1), action=(next;) + table=4 (ls_out_acl ), priority=0 , match=(1), action=(next;) + table=5 (ls_out_stateful ), priority=100 , match=(reg0[1] == 1), action=(ct_commit; next;) + table=5 (ls_out_stateful ), priority=100 , match=(reg0[2] == 1), action=(ct_lb;) + table=5 (ls_out_stateful ), priority=0 , match=(1), action=(next;) + table=6 (ls_out_port_sec_ip ), priority=0 , match=(1), action=(next;) + table=7 (ls_out_port_sec_l2 ), priority=100 , match=(eth.mcast), action=(output;) + table=7 (ls_out_port_sec_l2 ), priority=50 , match=(outport == "sw0-port1" && eth.dst == {00:00:00:00:00:01}), action=(output;) + table=7 (ls_out_port_sec_l2 ), priority=50 , match=(outport == "sw0-port2" && eth.dst == {00:00:00:00:00:02}), action=(output;) + +In this setup, ``sw0-port1`` and ``sw0-port2`` can send packets to each other, +but not to either of the ports on ``sw1``. This first trace shows a packet +from ``sw0-port1`` to ``sw0-port2``. You should see th packet arrive on +OpenFlow port ``1`` and output to OpenFlow port ``2``:: + + $ ovn/env2/packet1.sh + +This next example shows a packet from ``sw0-port1`` with a destination MAC +address of ``00:00:00:00:00:03``, which is the MAC address for ``sw1-port1``. +Since these ports are not on the same logical switch, the packet should just be +dropped:: + + $ ovn/env2/packet2.sh + + +Two Hypervisors +--------------- + +The first two examples started by showing OVN on a single hypervisor. A more +realistic deployment of OVN would span multiple hypervisors. This example +creates a single logical switch with 4 logical ports. It then simulates having +two hypervisors with two of the logical ports bound to each hypervisor:: + + $ ovn/env3/setup.sh + +You can start by viewing the logical topology with ``ovn-nbctl``:: + + $ ovn-nbctl show + switch b977dc03-79a5-41ba-9665-341a80e1abfd (sw0) + port sw0-port1 + addresses: 00:00:00:00:00:01 + port sw0-port2 + addresses: 00:00:00:00:00:02 + port sw0-port4 + addresses: 00:00:00:00:00:04 + port sw0-port3 + addresses: 00:00:00:00:00:03 + +Using ``ovn-sbctl`` to view the state of the system, we can see that there are +two chassis: one local that we can interact with, and a fake remote chassis. +Two logical ports are bound to each. Both chassis have an IP address of +localhost, but in a realistic deployment that would be the IP address used for +tunnels to that chassis:: + + $ ovn-sbctl show + Chassis "56b18105-5706-46ef-80c4-ff20979ab068" + Encap geneve + ip: "127.0.0.1" + Port_Binding "sw0-port2" + Port_Binding "sw0-port1" + Chassis fakechassis + Encap geneve + ip: "127.0.0.1" + Port_Binding "sw0-port4" + Port_Binding "sw0-port3" + +Packets between ``sw0-port1`` and ``sw0-port2`` behave just like the previous +examples. Packets to ports on a remote chassis are the interesting part of +this example. You may have noticed before that OVN's logical flows are broken +up into ingress and egress tables. Given a packet from ``sw0-port1`` on the +local chassis to ``sw0-port3`` on the remote chassis, the ingress pipeline is +executed on the local switch. OVN then determines that it must forward the +packet over a geneve tunnel. When it arrives at the remote chassis, the egress +pipeline will be executed there. + +This first packet trace shows the first part of this example. It's a packet +from ``sw0-port1`` to ``sw0-port3`` from the perspective of the local chassis. +``sw0-port1`` is OpenFlow port ``1``. The tunnel to the fake remote chassis is +OpenFlow port ``3``. You should see the ingress pipeline being executed and +then the packet output to port ``3``, the geneve tunnel:: + + $ ovn/env3/packet1.sh + +To simulate what would happen when that packet arrives at the remote chassis we +can flip this example around. Consider a packet from ``sw0-port3`` to +``sw0-port1``. This trace shows what would happen when that packet arrives at +the local chassis. The packet arrives on OpenFlow port ``3`` (the tunnel). +You should then see the egress pipeline get executed and the packet output to +OpenFlow port ``1``:: + + $ ovn/env3/packet2.sh + +Locally Attached Networks +------------------------- + +While OVN is generally focused on the implementation of logical networks using +overlays, it's also possible to use OVN as a control plane to manage logically +direct connectivity to networks that are locally accessible to each chassis. + +This example includes two hypervisors. Both hypervisors have two ports on +them. We want to use OVN to manage the connectivity of these ports to a +network attached to each hypervisor that we will call "physnet1". + +This scenario requires some additional configuration of ``ovn-controller``. We +must configure a mapping between ``physnet1`` and a local OVS bridge that +provides connectivity to that network. We call these "bridge mappings". For +our example, the following script creates a bridge called ``br-eth1`` and then +configures ``ovn-controller`` with a bridge mapping from ``physnet1`` to +``br-eth1``. + +We want to create a fake second chassis and then create the topology that tells +OVN we want both ports on both hypervisors connected to ``physnet1``. The way +this is modeled in OVN is by creating a logical switch for each port. The +logical switch has the regular VIF port and a ``localnet`` port:: + + $ ovn/env4/setup.sh + +At this point we should be able to see that ``ovn-controller`` has +automatically created patch ports between ``br-int`` and ``br-eth1``:: + + $ ovs-vsctl show + c0a06d85-d70a-4e11-9518-76a92588b34e + Bridge "br-eth1" + Port "patch-provnet1-1-physnet1-to-br-int" + Interface "patch-provnet1-1-physnet1-to-br-int" + type: patch + options: {peer="patch-br-int-to-provnet1-1-physnet1"} + Port "br-eth1" + Interface "br-eth1" + type: internal + Port "patch-provnet1-2-physnet1-to-br-int" + Interface "patch-provnet1-2-physnet1-to-br-int" + type: patch + options: {peer="patch-br-int-to-provnet1-2-physnet1"} + Bridge br-int + fail_mode: secure + Port "ovn-fakech-0" + Interface "ovn-fakech-0" + type: geneve + options: {key=flow, remote_ip="127.0.0.1"} + Port "patch-br-int-to-provnet1-2-physnet1" + Interface "patch-br-int-to-provnet1-2-physnet1" + type: patch + options: {peer="patch-provnet1-2-physnet1-to-br-int"} + Port br-int + Interface br-int + type: internal + Port "patch-br-int-to-provnet1-1-physnet1" + Interface "patch-br-int-to-provnet1-1-physnet1" + type: patch + options: {peer="patch-provnet1-1-physnet1-to-br-int"} + Port "lport2" + Interface "lport2" + Port "lport1" + Interface "lport1 + + +The logical topology from ``ovn-nbctl`` should look like this:: + + $ ovn-nbctl show + switch 9db81140-5504-4f60-be3d-2bee45b57e27 (provnet1-2) + port provnet1-2-port1 + addresses: ["00:00:00:00:00:02"] + port provnet1-2-physnet1 + addresses: ["unknown"] + switch cf175cb9-35c5-41cf-8bc7-2d322cdbead0 (provnet1-3) + port provnet1-3-physnet1 + addresses: ["unknown"] + port provnet1-3-port1 + addresses: ["00:00:00:00:00:03"] + switch b85f7af6-8055-4db2-ba93-efc7887cf38f (provnet1-1) + port provnet1-1-port1 + addresses: ["00:00:00:00:00:01"] + port provnet1-1-physnet1 + addresses: ["unknown"] + switch 63a5e276-8807-417d-bbec-a7e907e106b1 (provnet1-4) + port provnet1-4-port1 + addresses: ["00:00:00:00:00:04"] + port provnet1-4-physnet1 + addresses: ["unknown"] + +``port1`` on each logical switch represents a regular logical port for a VIF on +a hypervisor. ``physnet1`` on each logical switch is the special ``localnet`` +port. You can use ``ovn-nbctl`` to see that this port has a ``type`` and +``options`` set:: + + $ ovn-nbctl lsp-get-type provnet1-1-physnet1 + localnet + + $ ovn-nbctl lsp-get-options provnet1-1-physnet1 + network_name=physnet1 + +The physical topology should reflect that there are two regular ports on each +chassis:: + + $ ovn-sbctl show + Chassis "56b18105-5706-46ef-80c4-ff20979ab068" + hostname: sandbox + Encap geneve + ip: "127.0.0.1" + Port_Binding "provnet1-1-port1" + Port_Binding "provnet1-2-port1" + Chassis fakechassis + Encap geneve + ip: "127.0.0.1" + Port_Binding "provnet1-3-port1" + Port_Binding "provnet1-4-port1" + +All four of our ports should be able to communicate with each other, but they +do so through ``physnet1``. A packet from any of these ports to any +destination should be output to the OpenFlow port number that corresponds to +the patch port to ``br-eth1``. + +This example assumes following OpenFlow port number mappings: + +* ``1`` = tunnel to the fake second chassis +* ``2`` = ``lport1``, which is the logical port named ``provnet1-1-port1`` +* ``3`` = ``patch-br-int-to-provnet1-1-physnet1``, patch port to ``br-eth1`` +* ``4`` = ``lport2``, which is the logical port named ``provnet1-2-port1`` +* ``5`` = ``patch-br-int-to-provnet1-2-physnet1``, patch port to ``br-eth1`` + +We get those port numbers using ``ovs-ofctl``:: + + $ ovs-ofctl show br-int + OFPT_FEATURES_REPLY (xid=0x2): dpid:00002a84824b0d40 + n_tables:254, n_buffers:0 + capabilities: FLOW_STATS TABLE_STATS PORT_STATS QUEUE_STATS ARP_MATCH_IP + actions: output enqueue set_vlan_vid set_vlan_pcp strip_vlan mod_dl_src mod_dl_dst + 1(ovn-fakech-0): addr:aa:55:aa:55:00:0e + config: PORT_DOWN + state: LINK_DOWN + speed: 0 Mbps now, 0 Mbps max + 2(lport1): addr:aa:55:aa:55:00:0f + config: PORT_DOWN + state: LINK_DOWN + speed: 0 Mbps now, 0 Mbps max + 3(patch-br-int-to): addr:7a:6f:8a:d5:69:2a + config: 0 + state: 0 + speed: 0 Mbps now, 0 Mbps max + 4(lport2): addr:aa:55:aa:55:00:10 + config: PORT_DOWN + state: LINK_DOWN + speed: 0 Mbps now, 0 Mbps max + 5(patch-br-int-to): addr:4a:fd:c1:11:fc:a5 + config: 0 + state: 0 + speed: 0 Mbps now, 0 Mbps max + LOCAL(br-int): addr:2a:84:82:4b:0d:40 + config: PORT_DOWN + state: LINK_DOWN + speed: 0 Mbps now, 0 Mbps max + OFPT_GET_CONFIG_REPLY (xid=0x4): frags=normal miss_send_len=0 + +This first trace shows a packet from ``provnet1-1-port1`` with a destination +MAC address of ``provnet1-2-port1``. We expect the packets from ``lport1`` +(OpenFlow port 2) to be sent out to ``lport2`` (OpenFlow port 4). For example, +the following topology illustrates how the packets travel from ``lport1`` to +``lport2``:: + + `lport1` --> `patch-br-int-to-provnet1-1-physnet1`(OpenFlow port 3) + --> `br-eth1` --> `patch-br-int-to-provnet1-2-physnet1` --> `lport2`(OpenFlow port 4) + +Similarly, We expect the packets from ``provnet1-2-port1`` to be sent out to +``provnet1-1-port1``. We then expect the network to handle getting the packet +to its destination. In practice, this will be optimized at ``br-eth1`` and the +packet won't actually go out and back on the network:: + + $ ovn/env4/packet1.sh + +This next trace shows an example of a packet being sent to a destination on +another hypervisor. The source is ``provnet1-1-port1``, but the destination is +``provnet1-3-port1``, which is on the other fake chassis. As usual, we expect +the output to be to ``br-eth1`` (``patch-br-int-to-provnet1-1-physnet1``, +OpenFlow port 3):: + + $ ovn/env4/packet2.sh + +This next test shows a broadcast packet. The destination should still only be +OpenFlow port 3 and 4:: + + $ ovn/env4/packet3.sh + +Finally, this last trace shows what happens when a broadcast packet arrives +from the network. In this case, it simulates a broadcast that originated from a +port on the remote fake chassis and arrived at the local chassis via ``br-eth1``. +We should see it output to both local ports that are attached to this network +(OpenFlow ports 2 and 4):: + + $ ovn/env4/packet4.sh + +Locally Attached Networks with VLANs +------------------------------------ + +This example is an extension of the previous one. We take the same setup and +add two more ports to each hypervisor. Instead of having the new ports +directly connected to ``physnet1`` as before, we indicate that we want them on +VLAN 101 of ``physnet1``. This shows how ``localnet`` ports can be used to +provide connectivity to either a flat network or a VLAN on that network:: + + $ ovn/env5/setup.sh + +The logical topology shown by ``ovn-nbctl`` is similar to ``env4``, except we +now have 8 regular VIF ports connected to ``physnet1`` instead of 4. The +additional 4 ports we have added are all on VLAN 101 of ``physnet1``. Note +that the ``localnet`` ports representing connectivity to VLAN 101 of +``physnet1`` have the ``tag`` field set to ``101``:: + + $ ovn-nbctl show + switch 3e60b940-00bf-44c6-9db6-04abf28d7e5f (provnet1-1) + port provnet1-1-physnet1 + addresses: ["unknown"] + port provnet1-1-port1 + addresses: ["00:00:00:00:00:01"] + switch 87f6bea0-f74d-4f39-aa65-ca1f94670429 (provnet1-2) + port provnet1-2-port1 + addresses: ["00:00:00:00:00:02"] + port provnet1-2-physnet1 + addresses: ["unknown"] + switch e6c9cb69-a056-428d-aa40-e903ce416dcd (provnet1-6-101) + port provnet1-6-101-port1 + addresses: ["00:00:00:00:00:06"] + port provnet1-6-physnet1-101 + parent: + tag: 101 + addresses: ["unknown"] + switch 5f8f72ca-6030-4f66-baea-fe6174eb54df (provnet1-4) + port provnet1-4-port1 + addresses: ["00:00:00:00:00:04"] + port provnet1-4-physnet1 + addresses: ["unknown"] + switch 15d585eb-d2c1-45ea-a946-b08de0eb2f55 (provnet1-7-101) + port provnet1-7-physnet1-101 + parent: + tag: 101 + addresses: ["unknown"] + port provnet1-7-101-port1 + addresses: ["00:00:00:00:00:07"] + switch 7be4aabe-1bb0-4e16-a755-a1f6d81c1c2f (provnet1-5-101) + port provnet1-5-101-port1 + addresses: ["00:00:00:00:00:05"] + port provnet1-5-physnet1-101 + parent: + tag: 101 + addresses: ["unknown"] + switch 9bbdbf0e-50f3-4286-ba5a-29bf347531bb (provnet1-8-101) + port provnet1-8-101-port1 + addresses: ["00:00:00:00:00:08"] + port provnet1-8-physnet1-101 + parent: + tag: 101 + addresses: ["unknown"] + switch 70d053f7-2bca-4dff-96ae-bd728d3ba1d2 (provnet1-3) + port provnet1-3-physnet1 + addresses: ["unknown"] + port provnet1-3-port1 + addresses: ["00:00:00:00:00:03"] + +The physical topology shows that we have 4 regular VIF ports on each simulated +hypervisor:: + + $ ovn-sbctl show + Chassis fakechassis + Encap geneve + ip: "127.0.0.1" + Port_Binding "provnet1-3-port1" + Port_Binding "provnet1-8-101-port1" + Port_Binding "provnet1-7-101-port1" + Port_Binding "provnet1-4-port1" + Chassis "56b18105-5706-46ef-80c4-ff20979ab068" + hostname: sandbox + Encap geneve + ip: "127.0.0.1" + Port_Binding "provnet1-2-port1" + Port_Binding "provnet1-5-101-port1" + Port_Binding "provnet1-1-port1" + Port_Binding "provnet1-6-101-port1" + +All of the traces from the previous example, ``env4``, should work in this +environment and provide the same result. Now we can show what happens for the +ports connected to VLAN 101. This first example shows a packet originating +from ``provnet1-5-101-port1``, which is OpenFlow port 6. We should see VLAN +tag 101 pushed on the packet and then output to OpenFlow port 7, the patch port +to ``br-eth1`` (the bridge providing connectivity to ``physnet1``), and finally +arrives on OpenFlow port 8. + + $ ovn/env5/packet1.sh + +If we look at a broadcast packet arriving on VLAN 101 of ``physnet1``, we +should see it output to OpenFlow ports 6 and 8 only:: + + $ ovn/env5/packet2.sh + +Stateful ACLs +------------- + +ACLs provide a way to do distributed packet filtering for OVN networks. One +example use of ACLs is that OpenStack Neutron uses them to implement security +groups. ACLs are implemented using conntrack integration with OVS. + +Start with a simple logical switch with 2 logical ports:: + + $ ovn/env6/setup.sh + +A common use case would be the following policy applied for ``sw0-port1``: + +* Allow outbound IP traffic and associated return traffic. +* Allow incoming ICMP requests and associated return traffic. +* Allow incoming SSH connections and associated return traffic. +* Drop other incoming IP traffic. + +The following script applies this policy to our environment:: + + $ ovn/env6/add-acls.sh + +We can view the configured ACLs on this network using the ``ovn-nbctl`` +command:: + + $ ovn-nbctl acl-list sw0 + from-lport 1002 (inport == "sw0-port1" && ip) allow-related + to-lport 1002 (outport == "sw0-port1" && ip && icmp) allow-related + to-lport 1002 (outport == "sw0-port1" && ip && tcp && tcp.dst == 22) allow-related + to-lport 1001 (outport == "sw0-port1" && ip) drop + +Now that we have ACLs configured, there are new entries in the logical flow +table in the stages ``switch_in_pre_acl``, ``switch_in_acl``, +``switch_out_pre_acl``, and ``switch_out_acl``. + + $ ovn-sbctl lflow-list + +Let's look more closely at ``switch_out_pre_acl`` and ``switch_out_acl``. + +In ``switch_out_pre_acl``, we match IP traffic and put it through the +connection tracker. This populates the connection state fields so that we can +apply policy as appropriate:: + + table=0(switch_out_pre_acl), priority= 100, match=(ip), action=(ct_next;) + table=1(switch_out_pre_acl), priority= 0, match=(1), action=(next;) + +In ``switch_out_acl``, we allow packets associated with existing connections. +We drop packets that are deemed to be invalid (such as non-SYN TCP packet not +associated with an existing connection):: + + table=1(switch_out_acl), priority=65535, match=(!ct.est && ct.rel && !ct.new && !ct.inv), action=(next;) + table=1(switch_out_acl), priority=65535, match=(ct.est && !ct.rel && !ct.new && !ct.inv), action=(next;) + table=1(switch_out_acl), priority=65535, match=(ct.inv), action=(drop;) + +For new connections, we apply our configured ACL policy to decide whether to +allow the connection or not. In this case, we'll allow ICMP or SSH. +Otherwise, we'll drop the packet:: + + table=1(switch_out_acl), priority= 2002, match=(ct.new && (outport == "sw0-port1" && ip && icmp)), action=(ct_commit; next;) + table=1(switch_out_acl), priority= 2002, match=(ct.new && (outport == "sw0-port1" && ip && tcp && tcp.dst == 22)), action=(ct_commit; next;) + table=1(switch_out_acl), priority= 2001, match=(outport == "sw0-port1" && ip), action=(drop;) + +When using ACLs, the default policy is to allow and track IP connections. +Based on our above policy, IP traffic directed at ``sw0-port1`` will never hit +this flow at priority 1:: + + table=1(switch_out_acl), priority= 1, match=(ip), action=(ct_commit; next;) + table=1(switch_out_acl), priority= 0, match=(1), action=(next;) + +Note that conntrack integration is not yet supported in ovs-sandbox, so the +OpenFlow flows will not represent what you'd see in a real environment. The +logical flows described above give a very good idea of what the flows look +like, though. + +`This blog post +`__ +discusses OVN ACLs from an OpenStack perspective and also provides an example +of what the resulting OpenFlow flows look like. + +Container Ports +--------------- + +OVN supports containers running directly on the hypervisors and running +containers inside VMs. This example shows how OVN supports network +virtualization to containers when run inside VMs. Details about how to use +docker containers in OVS can be found in the `Docker installlation guide +<../INSTALL.Docker.rst>`__. + +To support container traffic created inside a VM and to distinguish network +traffic coming from different container vifs, for each container a logical port +needs to be created with parent name set to the VM's logical port and the tag +set to the vlan tag of the container vif. + +Start with a simple logical switch with three logical ports:: + + $ ovn/env7/setup.sh + +Lets create a container vif attached to the logical port ``sw0-port1`` and +another container vif attached to the logical port ``sw0-port2``:: + + $ ovn/env7/add-container-ports.sh + +Run the ``ovn-nbctl`` command to see the logical ports:: + + $ovn-nbctl show + +As you can see a logical port ``csw0-cport1`` is created on a logical switch +'csw0' whose parent is ``sw0-port1`` and it has tag set to ``42``. In +addition, a logical port ``csw0-cport2`` is created on the logical switch +``csw0`` whose parent is ``sw0-port2`` and it has tag set to ``43``. + +Bridge ``br-vmport1`` represents the ovs bridge running inside the VM connected +to the logical port ``sw0-port1``. In this tutorial the ovs port to +``sw0-port1`` is created as a patch port with its peer connected to the ovs +bridge ``br-vmport1``. An ovs port ``cport1`` is added to ``br-vmport1`` which +represents the container interface connected to the ovs bridge and vlan tag set +to ``42``. Similarly ``br-vmport2`` represents the ovs bridge for the logical +port ``sw0-port2`` and ``cport2`` connected to ``br-vmport2`` with vlan tag set +to ``43``. + +This first trace shows a packet from ``csw0-port1`` with a destination mac +address of ``csw0-port2``. You can see ovs bridge of the vm ``br-vmport1`` tags +the traffic with vlan id ``42`` and the traffic reaches to the br-int because +of the patch port. As you can see below ``ovn-controller`` has added a flow to +strip the vlan tag and set the reg6 and metadata appropriately:: + + $ ovs-ofctl -O OpenFlow13 dump-flows br-int + OFPST_FLOW reply (OF1.3) (xid=0x2): + cookie=0x0, duration=2767.032s, table=0, n_packets=0, n_bytes=0, priority=150,in_port=3,dl_vlan=42 actions=pop_vlan,set_field:0x3->reg5,set_field:0x2->metadata,set_field:0x1->reg6,resubmit(,16) + cookie=0x0, duration=2767.002s, table=0, n_packets=0, n_bytes=0, priority=150,in_port=4,dl_vlan=43 actions=pop_vlan,set_field:0x4->reg5,set_field:0x2->metadata,set_field:0x2->reg6,resubmit(,16) + cookie=0x0, duration=2767.032s, table=0, n_packets=0, n_bytes=0, priority=100,in_port=3 actions=set_field:0x1->reg5,set_field:0x1->metadata,set_field:0x1->reg6,resubmit(,16) + cookie=0x0, duration=2767.001s, table=0, n_packets=0, n_bytes=0, priority=100,in_port=4 actions=set_field:0x2->reg5,set_field:0x1->metadata,set_field:0x2->reg6,resubmit(,16) + +:: + + $ ovn/env7/packet1.sh + +The second trace shows a packet from ``csw0-port2`` to ``csw0-port1``:: + + $ ovn/env7/packet2.sh + +You can extend this setup by adding additional container ports with two +hypervisors. Refer to tutorial three above. + +L2Gateway Ports +--------------- + +L2Gateway provides a way to connect logical switch ports of type ``l2gateway`` +to a physical network. The difference between ``l2gateway`` ports and +``localnet`` ports is that an ``l2gateway`` port is bound to a specific +chassis. A single chassis serves as the L2 gateway to the physical network and +all traffic between chassis continues to go over geneve tunnels. + +Start with a simple logical switch with three logical ports:: + + $ ovn/env8/setup.sh + +This first example shows a packet originating from ``lport1``, which is +OpenFlow port 1. We expect all packets from ``lport1`` to be sent out to +``br-eth1`` (``patch-br-int-to-sw0-port3``, OpenFlow port 3). The patch port +to ``br-eth1`` provides connectivity to the physical network. + + $ ovn/env8/packet1.sh + +The last trace shows what happens when a broadcast packet arrives from the +network. In this case, it simulates a broadcast that originated from a port on +the physical network and arrived at the local chassis via ``br-eth1``. We +should see it output to the local ports ``lport1`` and ``lport2``:: + + $ ovn/env8/packet2.sh + +.. _ovn-architecture: http://openvswitch.org/support/dist-docs/ovn-architecture.7.html +.. _Tutorial: https://github.com/openvswitch/ovs/blob/master/tutorial/tutorial.rst +.. _ovn-nb(5): http://openvswitch.org/support/dist-docs/ovn-nb.5.html +.. _ovn-sb(5): http://openvswitch.org/support/dist-docs/ovn-sb.5.html +.. _vtep(5): http://openvswitch.org/support/dist-docs/vtep.5.html +.. _ovn-northd(8): http://openvswitch.org/support/dist-docs/ovn-northd.8.html +.. _ovn-controller(8): http://openvswitch.org/support/dist-docs/ovn-controller.8.html +.. _ovn-controller-vtep(8): http://openvswitch.org/support/dist-docs/ovn-controller-vtep.8.html +.. _vtep-ctl(8): http://openvswitch.org/support/dist-docs/vtep-ctl.8.html +.. _ovn-nbctl(8): http://openvswitch.org/support/dist-docs/ovn-nbctl.8.html +.. _ovn-sbctl(8): http://openvswitch.org/support/dist-docs/ovn-sbctl.8.html diff --git a/tutorial/ovn/env1/setup.sh b/tutorial/ovn/env1/setup.sh index 08496432e..80a5d0d79 100755 --- a/tutorial/ovn/env1/setup.sh +++ b/tutorial/ovn/env1/setup.sh @@ -14,7 +14,7 @@ # # -# See "Simple two-port setup" in tutorial/OVN-Tutorial.md. +# See "Simple two-port setup" in tutorial/ovn-tutorial.rst. # set -o xtrace diff --git a/tutorial/ovn/env6/setup.sh b/tutorial/ovn/env6/setup.sh index 08496432e..80a5d0d79 100755 --- a/tutorial/ovn/env6/setup.sh +++ b/tutorial/ovn/env6/setup.sh @@ -14,7 +14,7 @@ # # -# See "Simple two-port setup" in tutorial/OVN-Tutorial.md. +# See "Simple two-port setup" in tutorial/ovn-tutorial.rst. # set -o xtrace -- cgit v1.2.1