From e12569bb305f86d59c5b3a594903a96c869aeed3 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Thu, 8 Dec 2016 12:55:27 +0000 Subject: doc: Populate 'tutorials' section Rename 'tutorial' to 'ovs-advanced' and 'ovn-tutorial' to 'ovn-basics'. Signed-off-by: Stephen Finucane Signed-off-by: Ben Pfaff --- tutorial/automake.mk | 3 - tutorial/ovn-tutorial.rst | 976 --------------------------------------------- tutorial/ovn/env1/setup.sh | 2 +- tutorial/ovn/env6/setup.sh | 2 +- tutorial/tutorial.rst | 872 ---------------------------------------- 5 files changed, 2 insertions(+), 1853 deletions(-) delete mode 100644 tutorial/ovn-tutorial.rst delete mode 100644 tutorial/tutorial.rst (limited to 'tutorial') diff --git a/tutorial/automake.mk b/tutorial/automake.mk index 79f9b6835..5509062ea 100644 --- a/tutorial/automake.mk +++ b/tutorial/automake.mk @@ -1,6 +1,3 @@ -docs += \ - tutorial/tutorial.rst \ - tutorial/ovn-tutorial.rst EXTRA_DIST += \ tutorial/ovs-sandbox \ tutorial/t-setup \ diff --git a/tutorial/ovn-tutorial.rst b/tutorial/ovn-tutorial.rst deleted file mode 100644 index bb5f6201f..000000000 --- a/tutorial/ovn-tutorial.rst +++ /dev/null @@ -1,976 +0,0 @@ -.. - 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 ---------------- - -.. TODO(stephenfin): Update Docker link when this is moved. - -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`. - -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 80a5d0d79..a9c6f39a7 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.rst. +# See "Simple two-port setup" in Documentation/tutorial/ovn-basics.rst. # set -o xtrace diff --git a/tutorial/ovn/env6/setup.sh b/tutorial/ovn/env6/setup.sh index 80a5d0d79..a9c6f39a7 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.rst. +# See "Simple two-port setup" in Documentation/tutorial/ovn-basics.rst. # set -o xtrace diff --git a/tutorial/tutorial.rst b/tutorial/tutorial.rst deleted file mode 100644 index 422bc0d61..000000000 --- a/tutorial/tutorial.rst +++ /dev/null @@ -1,872 +0,0 @@ -.. - 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. - -======================================= -Open vSwitch Advanced Features Tutorial -======================================= - -Many tutorials cover the basics of OpenFlow. This is not such a tutorial. -Rather, a knowledge of the basics of OpenFlow is a prerequisite. If you do not -already understand how an OpenFlow flow table works, please go read a basic -tutorial and then continue reading here afterward. - -It is also important to understand the basics of Open vSwitch before you begin. -If you have never used ovs-vsctl or ovs-ofctl before, you should learn a little -about them before proceeding. - -Most of the features covered in this tutorial are Open vSwitch extensions to -OpenFlow. Also, most of the features in this tutorial are specific to the -software Open vSwitch implementation. If you are using an Open vSwitch port to -an ASIC-based hardware switch, this tutorial will not help you. - -This tutorial does not cover every aspect of the features that it mentions. -You can find the details elsewhere in the Open vSwitch documentation, -especially ``ovs-ofctl(8)`` and the comments in the -``include/openflow/nicira-ext.h`` and ``include/openvswitch/meta-flow.h`` -header files. - -Getting Started ---------------- - -This is a hands-on tutorial. To get the most out of it, you will need Open -vSwitch binaries. You do not, on the other hand, need any physical networking -hardware or even supervisor privilege on your system. Instead, we will use a -script called ``ovs-sandbox``, which accompanies the tutorial, that constructs -a software simulated network environment based on Open vSwitch. - -.. TODO(stephenfin): Update installation guide link when this is moved. - -You can use ``ovs-sandbox`` three ways: - -* If you have already installed Open vSwitch on your system, then you should be - able to just run ``ovs-sandbox`` from this directory without any options. - -* If you have not installed Open vSwitch (and you do not want to install it), - then you can build Open vSwitch according to the instructions in the - `installation guide`, without installing it. Then run ``./ovs-sandbox -b - DIRECTORY`` from this directory, substituting the Open vSwitch build - directory for ``DIRECTORY``. - -* As a slight variant on the latter, you can run ``make sandbox`` from an Open - vSwitch build directory. - -When you run ``ovs-sandbox``, it does the following: - -1. **CAUTION:** Deletes any subdirectory of the current directory named - "sandbox" and any files in that directory. - -2. Creates a new directory "sandbox" in the current directory. - -3. Sets up special environment variables that ensure that Open vSwitch programs - will look inside the "sandbox" directory instead of in the Open vSwitch - installation directory. - -4. If you are using a built but not installed Open vSwitch, installs the Open - vSwitch manpages in a subdirectory of "sandbox" and adjusts the ``MANPATH`` - environment variable to point to this directory. This means that you can - use, for example, ``man ovs-vsctl`` to see a manpage for the ``ovs-vsctl`` - program that you built. - -5. Creates an empty Open vSwitch configuration database under "sandbox". - -6. Starts ``ovsdb-server`` running under "sandbox". - -7. Starts ``ovs-vswitchd`` running under "sandbox", passing special options - that enable a special "dummy" mode for testing. - -8. Starts a nested interactive shell inside "sandbox". - -At this point, you can run all the usual Open vSwitch utilities from the nested -shell environment. You can, for example, use ``ovs-vsctl`` to create a bridge: - - $ ovs-vsctl add-br br0 - -From Open vSwitch's perspective, the bridge that you create this way is as real -as any other. You can, for example, connect it to an OpenFlow controller or -use ``ovs-ofctl`` to examine and modify it and its OpenFlow flow table. On the -other hand, the bridge is not visible to the operating system's network stack, -so ``ifconfig`` or ``ip`` cannot see it or affect it, which means that -utilities like ``ping`` and ``tcpdump`` will not work either. (That has its -good side, too: you can't screw up your computer's network stack by -manipulating a sandboxed OVS.) - -When you're done using OVS from the sandbox, exit the nested shell (by entering -the "exit" shell command or pressing Control+D). This will kill the daemons -that ``ovs-sandbox`` started, but it leaves the "sandbox" directory and its -contents in place. - -The sandbox directory contains log files for the Open vSwitch dameons. You can -examine them while you're running in the sandboxed environment or after you -exit. - -Using GDB ---------- - -GDB support is not required to go through the tutorial. It is added in case -user wants to explore the internals of OVS programs. - -GDB can already be used to debug any running process, with the usual -``gdb `` command. - -``ovs-sandbox`` also has a ``-g`` option for launching ovs-vswitchd under GDB. -This option can be handy for setting break points before ovs-vswitchd runs, or -for catching early segfaults. Similarly, a ``-d`` option can be used to run -ovsdb-server under GDB. Both options can be specified at the same time. - -In addition, a ``-e`` option also launches ovs-vswitchd under GDB. However, -instead of displaying a ``gdb>`` prompt and waiting for user input, -ovs-vswitchd will start to execute immediately. ``-r`` option is the -corresponding option for running ovsdb-server under gdb with immediate -execution. - -To avoid GDB mangling with the sandbox sub shell terminal, ``ovs-sandbox`` -starts a new xterm to run each GDB session. For systems that do not support X -windows, GDB support is effectively disabled. - -When launching sandbox through the build tree's make file, the ``-g`` option -can be passed via the ``SANDBOXFLAGS`` environment variable. ``make sandbox -SANDBOXFLAGS=-g`` will start the sandbox with ovs-vswitchd running under GDB in -its own xterm if X is available. - -Motivation ----------- - -The goal of this tutorial is to demonstrate the power of Open vSwitch flow -tables. The tutorial works through the implementation of a MAC-learning switch -with VLAN trunk and access ports. Outside of the Open vSwitch features that we -will discuss, OpenFlow provides at least two ways to implement such a switch: - -1. An OpenFlow controller to implement MAC learning in a "reactive" fashion. - Whenever a new MAC appears on the switch, or a MAC moves from one switch - port to another, the controller adjusts the OpenFlow flow table to match. - -2. The "normal" action. OpenFlow defines this action to submit a packet to - "the traditional non-OpenFlow pipeline of the switch". That is, if a flow - uses this action, then the packets in the flow go through the switch in the - same way that they would if OpenFlow was not configured on the switch. - -Each of these approaches has unfortunate pitfalls. In the first approach, -using an OpenFlow controller to implement MAC learning, has a significant cost -in terms of network bandwidth and latency. It also makes the controller more -difficult to scale to large numbers of switches, which is especially important -in environments with thousands of hypervisors (each of which contains a virtual -OpenFlow switch). MAC learning at an OpenFlow controller also behaves poorly -if the OpenFlow controller fails, slows down, or becomes unavailable due to -network problems. - -The second approach, using the "normal" action, has different problems. First, -little about the "normal" action is standardized, so it behaves differently on -switches from different vendors, and the available features and how those -features are configured (usually not through OpenFlow) varies widely. Second, -"normal" does not work well with other OpenFlow actions. It is -"all-or-nothing", with little potential to adjust its behavior slightly or to -compose it with other features. - -Scenario --------- - -We will construct Open vSwitch flow tables for a VLAN-capable, -MAC-learning switch that has four ports: - -p1 - a trunk port that carries all VLANs, on OpenFlow port 1. - -p2 - an access port for VLAN 20, on OpenFlow port 2. - -p3, p4 - both access ports for VLAN 30, on OpenFlow ports 3 and 4, respectively. - -.. note:: - The ports' names are not significant. You could call them eth1 through eth4, - or any other names you like. - -.. note:: - An OpenFlow switch always has a "local" port as well. This scenario won't - use the local port. - -Our switch design will consist of five main flow tables, each of which -implements one stage in the switch pipeline: - -Table 0 - Admission control. - -Table 1 - VLAN input processing. - -Table 2 - Learn source MAC and VLAN for ingress port. - -Table 3 - Look up learned port for destination MAC and VLAN. - -Table 4 - Output processing. - -The section below describes how to set up the scenario, followed by a section -for each OpenFlow table. - -You can cut and paste the ``ovs-vsctl`` and ``ovs-ofctl`` commands in each of -the sections below into your ``ovs-sandbox`` shell. They are also available as -shell scripts in this directory, named ``t-setup``, ``t-stage0``, ``t-stage1``, -..., ``t-stage4``. The ``ovs-appctl`` test commands are intended for cutting -and pasting and are not supplied separately. - -Setup ------ - -To get started, start ``ovs-sandbox``. Inside the interactive shell that it -starts, run this command:: - - $ ovs-vsctl add-br br0 -- set Bridge br0 fail-mode=secure - -This command creates a new bridge "br0" and puts "br0" into so-called -"fail-secure" mode. For our purpose, this just means that the OpenFlow flow -table starts out empty. - -.. note:: - If we did not do this, then the flow table would start out with a single flow - that executes the "normal" action. We could use that feature to yield a - switch that behaves the same as the switch we are currently building, but - with the caveats described under "Motivation" above.) - -The new bridge has only one port on it so far, the "local port" br0. We need -to add ``p1``, ``p2``, ``p3``, and ``p4``. A shell ``for`` loop is one way to -do it:: - - for i in 1 2 3 4; do - ovs-vsctl add-port br0 p$i -- set Interface p$i ofport_request=$i - ovs-ofctl mod-port br0 p$i up - done - -In addition to adding a port, the ``ovs-vsctl`` command above sets its -``ofport_request`` column to ensure that port ``p1`` is assigned OpenFlow port -1, ``p2`` is assigned OpenFlow port 2, and so on. - -.. note:: - We could omit setting the ofport_request and let Open vSwitch choose port - numbers for us, but it's convenient for the purposes of this tutorial because - we can talk about OpenFlow port 1 and know that it corresponds to ``p1``. - -The ``ovs-ofctl`` command above brings up the simulated interfaces, which are -down initially, using an OpenFlow request. The effect is similar to ``ifconfig -up``, but the sandbox's interfaces are not visible to the operating system and -therefore ``ifconfig`` would not affect them. - -We have not configured anything related to VLANs or MAC learning. That's -because we're going to implement those features in the flow table. - -To see what we've done so far to set up the scenario, you can run a command -like ``ovs-vsctl show`` or ``ovs-ofctl show br0``. - -Implementing Table 0: Admission control ---------------------------------------- - -Table 0 is where packets enter the switch. We use this stage to discard -packets that for one reason or another are invalid. For example, packets with -a multicast source address are not valid, so we can add a flow to drop them at -ingress to the switch with:: - - $ ovs-ofctl add-flow br0 \ - "table=0, dl_src=01:00:00:00:00:00/01:00:00:00:00:00, actions=drop" - -A switch should also not forward IEEE 802.1D Spanning Tree Protocol (STP) -packets, so we can also add a flow to drop those and other packets with -reserved multicast protocols:: - - $ ovs-ofctl add-flow br0 \ - "table=0, dl_dst=01:80:c2:00:00:00/ff:ff:ff:ff:ff:f0, actions=drop" - -We could add flows to drop other protocols, but these demonstrate the pattern. - -We need one more flow, with a priority lower than the default, so that flows -that don't match either of the "drop" flows we added above go on to pipeline -stage 1 in OpenFlow table 1:: - - $ ovs-ofctl add-flow br0 "table=0, priority=0, actions=resubmit(,1)" - -.. note:: - The "resubmit" action is an Open vSwitch extension to OpenFlow. - -Testing Table 0 ---------------- - -If we were using Open vSwitch to set up a physical or a virtual switch, then we -would naturally test it by sending packets through it one way or another, -perhaps with common network testing tools like ``ping`` and ``tcpdump`` or more -specialized tools like Scapy. That's difficult with our simulated switch, -since it's not visible to the operating system. - -But our simulated switch has a few specialized testing tools. The most -powerful of these tools is ``ofproto/trace``. Given a switch and the -specification of a flow, ``ofproto/trace`` shows, step-by-step, how such a flow -would be treated as it goes through the switch. - -Example 1 -~~~~~~~~~ - -Try this command:: - - $ ovs-appctl ofproto/trace br0 in_port=1,dl_dst=01:80:c2:00:00:05 - -The output should look something like this:: - - Flow: metadata=0,in_port=1,vlan_tci=0x0000,dl_src=00:00:00:00:00:00,dl_dst=01:80:c2:00:00:05,dl_type=0x0000 - Rule: table=0 cookie=0 dl_dst=01:80:c2:00:00:00/ff:ff:ff:ff:ff:f0 - OpenFlow actions=drop - - Final flow: unchanged - Datapath actions: drop - -The first block of lines describes an OpenFlow table lookup. The first line -shows the fields used for the table lookup (which is mostly zeros because -that's the default if we don't specify everything). The second line gives the -OpenFlow flow that the fields matched (called a "rule" because that is the name -used inside Open vSwitch for an OpenFlow flow). In this case, we see that this -packet that has a reserved multicast destination address matches the rule that -drops those packets. The third line gives the rule's OpenFlow actions. - -The second block of lines summarizes the results, which are not very -interesting here. - -Example 2 -~~~~~~~~~ - -Try another command:: - - $ ovs-appctl ofproto/trace br0 in_port=1,dl_dst=01:80:c2:00:00:10 - -The output should be:: - - Flow: metadata=0,in_port=1,vlan_tci=0x0000,dl_src=00:00:00:00:00:00,dl_dst=01:80:c2:00:00:10,dl_type=0x0000 - Rule: table=0 cookie=0 priority=0 - OpenFlow actions=resubmit(,1) - - Resubmitted flow: unchanged - Resubmitted regs: reg0=0x0 reg1=0x0 reg2=0x0 reg3=0x0 reg4=0x0 reg5=0x0 reg6=0x0 reg7=0x0 - Resubmitted odp: drop - No match - - Final flow: unchanged - Datapath actions: drop - -This time the flow we handed to ``ofproto/trace`` doesn't match any of our -"drop" rules, so it falls through to the low-priority "resubmit" rule, which we -see in the rule and the actions selected in the first block. The "resubmit" -causes a second lookup in OpenFlow table 1, described by the additional block -of indented text in the output. We haven't yet added any flows to OpenFlow -table 1, so no flow actually matches in the second lookup. Therefore, the -packet is still actually dropped, which means that the externally observable -results would be identical to our first example. - -Implementing Table 1: VLAN Input Processing -------------------------------------------- - -A packet that enters table 1 has already passed basic validation in table 0. -The purpose of table 1 is validate the packet's VLAN, based on the VLAN -configuration of the switch port through which the packet entered the switch. -We will also use it to attach a VLAN header to packets that arrive on an access -port, which allows later processing stages to rely on the packet's VLAN always -being part of the VLAN header, reducing special cases. - -Let's start by adding a low-priority flow that drops all packets, before we add -flows that pass through acceptable packets. You can think of this as a -"default drop" rule:: - - $ ovs-ofctl add-flow br0 "table=1, priority=0, actions=drop" - -Our trunk port ``p1``, on OpenFlow port 1, is an easy case. ``p1`` accepts any -packet regardless of whether it has a VLAN header or what the VLAN was, so we -can add a flow that resubmits everything on input port 1 to the next table:: - - $ ovs-ofctl add-flow br0 \ - "table=1, priority=99, in_port=1, actions=resubmit(,2)" - -On the access ports, we want to accept any packet that has no VLAN header, tag -it with the access port's VLAN number, and then pass it along to the next -stage:: - - $ ovs-ofctl add-flows br0 - <<'EOF' - table=1, priority=99, in_port=2, vlan_tci=0, actions=mod_vlan_vid:20, resubmit(,2) - table=1, priority=99, in_port=3, vlan_tci=0, actions=mod_vlan_vid:30, resubmit(,2) - table=1, priority=99, in_port=4, vlan_tci=0, actions=mod_vlan_vid:30, resubmit(,2) - EOF - -We don't write any rules that match packets with 802.1Q that enter this stage -on any of the access ports, so the "default drop" rule we added earlier causes -them to be dropped, which is ordinarily what we want for access ports. - -.. note:: - Another variation of access ports allows ingress of packets tagged with VLAN - 0 (aka 802.1p priority tagged packets). To allow such packets, replace - ``vlan_tci=0`` by ``vlan_tci=0/0xfff`` above. - -Testing Table 1 ---------------- - -``ofproto/trace`` allows us to test the ingress VLAN rules that we added above. - -Example 1: Packet on Trunk Port -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Here's a test of a packet coming in on the trunk port:: - - $ ovs-appctl ofproto/trace br0 in_port=1,vlan_tci=5 - -The output shows the lookup in table 0, the resubmit to table 1, and the -resubmit to table 2 (which does nothing because we haven't put anything there -yet):: - - Flow: metadata=0,in_port=1,vlan_tci=0x0005,dl_src=00:00:00:00:00:00,dl_dst=00:00:00:00:00:00,dl_type=0x0000 - Rule: table=0 cookie=0 priority=0 - OpenFlow actions=resubmit(,1) - - Resubmitted flow: unchanged - Resubmitted regs: reg0=0x0 reg1=0x0 reg2=0x0 reg3=0x0 reg4=0x0 reg5=0x0 reg6=0x0 reg7=0x0 - Resubmitted odp: drop - Rule: table=1 cookie=0 priority=99,in_port=1 - OpenFlow actions=resubmit(,2) - - Resubmitted flow: unchanged - Resubmitted regs: reg0=0x0 reg1=0x0 reg2=0x0 reg3=0x0 reg4=0x0 reg5=0x0 reg6=0x0 reg7=0x0 - Resubmitted odp: drop - No match - - Final flow: unchanged - Datapath actions: drop - -Example 2: Valid Packet on Access Port -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Here's a test of a valid packet (a packet without an 802.1Q header) coming in -on access port ``p2``:: - - $ ovs-appctl ofproto/trace br0 in_port=2 - -The output is similar to that for the previous case, except that it -additionally tags the packet with ``p2``'s VLAN 20 before it passes it along to -table 2:: - - Flow: metadata=0,in_port=2,vlan_tci=0x0000,dl_src=00:00:00:00:00:00,dl_dst=00:00:00:00:00:00,dl_type=0x0000 - Rule: table=0 cookie=0 priority=0 - OpenFlow actions=resubmit(,1) - - Resubmitted flow: unchanged - Resubmitted regs: reg0=0x0 reg1=0x0 reg2=0x0 reg3=0x0 reg4=0x0 reg5=0x0 reg6=0x0 reg7=0x0 - Resubmitted odp: drop - Rule: table=1 cookie=0 priority=99,in_port=2,vlan_tci=0x0000 - OpenFlow actions=mod_vlan_vid:20,resubmit(,2) - - Resubmitted flow: metadata=0,in_port=2,dl_vlan=20,dl_vlan_pcp=0,dl_src=00:00:00:00:00:00,dl_dst=00:00:00:00:00:00,dl_type=0x0000 - Resubmitted regs: reg0=0x0 reg1=0x0 reg2=0x0 reg3=0x0 reg4=0x0 reg5=0x0 reg6=0x0 reg7=0x0 - Resubmitted odp: drop - No match - - Final flow: unchanged - Datapath actions: drop - -Example 3: Invalid Packet on Access Port -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -This tests an invalid packet (one that includes an 802.1Q header) coming in on -access port ``p2``:: - - $ ovs-appctl ofproto/trace br0 in_port=2,vlan_tci=5 - -The output shows the packet matching the default drop rule:: - - Flow: metadata=0,in_port=2,vlan_tci=0x0005,dl_src=00:00:00:00:00:00,dl_dst=00:00:00:00:00:00,dl_type=0x0000 - Rule: table=0 cookie=0 priority=0 - OpenFlow actions=resubmit(,1) - - Resubmitted flow: unchanged - Resubmitted regs: reg0=0x0 reg1=0x0 reg2=0x0 reg3=0x0 reg4=0x0 reg5=0x0 reg6=0x0 reg7=0x0 - Resubmitted odp: drop - Rule: table=1 cookie=0 priority=0 - OpenFlow actions=drop - - Final flow: unchanged - Datapath actions: drop - -Implementing Table 2: MAC+VLAN Learning for Ingress Port --------------------------------------------------------- - -This table allows the switch we're implementing to learn that the packet's -source MAC is located on the packet's ingress port in the packet's VLAN. - -.. note:: - This table is a good example why table 1 added a VLAN tag to packets that - entered the switch through an access port. We want to associate a MAC+VLAN - with a port regardless of whether the VLAN in question was originally part of - the packet or whether it was an assumed VLAN associated with an access port. - -It only takes a single flow to do this. The following command adds it:: - - $ ovs-ofctl add-flow br0 \ - "table=2 actions=learn(table=10, NXM_OF_VLAN_TCI[0..11], \ - NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[], \ - load:NXM_OF_IN_PORT[]->NXM_NX_REG0[0..15]), \ - resubmit(,3)" - -The "learn" action (an Open vSwitch extension to OpenFlow) modifies a flow -table based on the content of the flow currently being processed. Here's how -you can interpret each part of the "learn" action above: - -``table=10`` - Modify flow table 10. This will be the MAC learning table. - -``NXM_OF_VLAN_TCI[0..11]`` - Make the flow that we add to flow table 10 match the same VLAN ID that the - packet we're currently processing contains. This effectively scopes the - MAC learning entry to a single VLAN, which is the ordinary behavior for a - VLAN-aware switch. - -``NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[]`` - Make the flow that we add to flow table 10 match, as Ethernet destination, - the Ethernet source address of the packet we're currently processing. - -``load:NXM_OF_IN_PORT[]->NXM_NX_REG0[0..15]`` - Whereas the preceding parts specify fields for the new flow to match, this - specifies an action for the flow to take when it matches. The action is - for the flow to load the ingress port number of the current packet into - register 0 (a special field that is an Open vSwitch extension to OpenFlow). - -.. note:: - A real use of "learn" for MAC learning would probably involve two additional - elements. First, the "learn" action would specify a hard_timeout for the new - flow, to enable a learned MAC to eventually expire if no new packets were - seen from a given source within a reasonable interval. Second, one would - usually want to limit resource consumption by using the Flow_Table table in - the Open vSwitch configuration database to specify a maximum number of flows - in table 10. - -This definitely calls for examples. - -Testing Table 2 ---------------- - -Example 1 -~~~~~~~~~ - -Try the following test command:: - - $ ovs-appctl ofproto/trace br0 \ - in_port=1,vlan_tci=20,dl_src=50:00:00:00:00:01 -generate - -The output shows that "learn" was executed, but it isn't otherwise informative, -so we won't include it here. - -The ``-generate`` keyword is new. Ordinarily, ``ofproto/trace`` has no side -effects: "output" actions do not actually output packets, "learn" actions do -not actually modify the flow table, and so on. With ``-generate``, though, -``ofproto/trace`` does execute "learn" actions. That's important now, because -we want to see the effect of the "learn" action on table 10. You can see that -by running:: - - $ ovs-ofctl dump-flows br0 table=10 - -which (omitting the ``duration`` and ``idle_age`` fields, which will vary based -on how soon you ran this command after the previous one, as well as some other -uninteresting fields) prints something like:: - - NXST_FLOW reply (xid=0x4): - table=10, vlan_tci=0x0014/0x0fff,dl_dst=50:00:00:00:00:01 actions=load:0x1->NXM_NX_REG0[0..15] - -You can see that the packet coming in on VLAN ``20`` with source MAC -``50:00:00:00:00:01`` became a flow that matches VLAN ``20`` (written in -hexadecimal) and destination MAC ``50:00:00:00:00:01``. The flow loads port -number ``1``, the input port for the flow we tested, into register 0. - -Example 2 -~~~~~~~~~ - -Here's a second test command:: - - $ ovs-appctl ofproto/trace br0 \ - in_port=2,dl_src=50:00:00:00:00:01 -generate - -The flow that this command tests has the same source MAC and VLAN as example 1, -although the VLAN comes from an access port VLAN rather than an 802.1Q header. -If we again dump the flows for table 10 with:: - - $ ovs-ofctl dump-flows br0 table=10 - -then we see that the flow we saw previously has changed to indicate that the -learned port is port 2, as we would expect:: - - NXST_FLOW reply (xid=0x4): - table=10, vlan_tci=0x0014/0x0fff,dl_dst=50:00:00:00:00:01 actions=load:0x2->NXM_NX_REG0[0..15] - -Implementing Table 3: Look Up Destination Port ----------------------------------------------- - -This table figures out what port we should send the packet to based on the -destination MAC and VLAN. That is, if we've learned the location of the -destination (from table 2 processing some previous packet with that destination -as its source), then we want to send the packet there. - -We need only one flow to do the lookup:: - - $ ovs-ofctl add-flow br0 \ - "table=3 priority=50 actions=resubmit(,10), resubmit(,4)" - -The flow's first action resubmits to table 10, the table that the "learn" -action modifies. As you saw previously, the learned flows in this table write -the learned port into register 0. If the destination for our packet hasn't -been learned, then there will be no matching flow, and so the "resubmit" turns -into a no-op. Because registers are initialized to 0, we can use a register 0 -value of 0 in our next pipeline stage as a signal to flood the packet. - -The second action resubmits to table 4, continuing to the next pipeline stage. - -We can add another flow to skip the learning table lookup for multicast and -broadcast packets, since those should always be flooded:: - - $ ovs-ofctl add-flow br0 \ - "table=3 priority=99 dl_dst=01:00:00:00:00:00/01:00:00:00:00:00 \ - actions=resubmit(,4)" - -.. note:: - We don't strictly need to add this flow, because multicast addresses will - never show up in our learning table. (In turn, that's because we put a flow - into table 0 to drop packets that have a multicast source address.) - -Testing Table 3 ---------------- - -Example -~~~~~~~ - -Here's a command that should cause OVS to learn that ``f0:00:00:00:00:01`` is -on ``p1`` in VLAN ``20``:: - - $ ovs-appctl ofproto/trace br0 \ - in_port=1,dl_vlan=20,dl_src=f0:00:00:00:00:01,dl_dst=90:00:00:00:00:01 \ - -generate - -Here's an excerpt from the output that shows (from the "no match" looking up -the resubmit to table 10) that the flow's destination was unknown:: - - Resubmitted flow: unchanged - Resubmitted regs: reg0=0x0 reg1=0x0 reg2=0x0 reg3=0x0 reg4=0x0 reg5=0x0 reg6=0x0 reg7=0x0 - Resubmitted odp: drop - Rule: table=3 cookie=0 priority=50 - OpenFlow actions=resubmit(,10),resubmit(,4) - - Resubmitted flow: unchanged - Resubmitted regs: reg0=0x0 reg1=0x0 reg2=0x0 reg3=0x0 reg4=0x0 reg5=0x0 reg6=0x0 reg7=0x0 - Resubmitted odp: drop - No match - -You can verify that the packet's source was learned two ways. The most direct -way is to dump the learning table with:: - - $ ovs-ofctl dump-flows br0 table=10 - -which ought to show roughly the following, with extraneous details removed:: - - table=10, vlan_tci=0x0014/0x0fff,dl_dst=f0:00:00:00:00:01 actions=load:0x1->NXM_NX_REG0[0..15] - -.. note:: - If you tried the examples for the previous step, or if you did some of your - own experiments, then you might see additional flows there. These - additional flows are harmless. If they bother you, then you can remove - them with `ovs-ofctl del-flows br0 table=10`. - -The other way is to inject a packet to take advantage of the learning entry. -For example, we can inject a packet on p2 whose destination is the MAC address -that we just learned on p1: - - $ ovs-appctl ofproto/trace br0 \ - in_port=2,dl_src=90:00:00:00:00:01,dl_dst=f0:00:00:00:00:01 -generate - -Here's an interesting excerpt from that command's output. This group of lines -traces the ``resubmit(,10)``, showing that the packet matched the learned flow -for the first MAC we used, loading the OpenFlow port number for the learned -port ``p1`` into register ``0``:: - - Resubmitted flow: unchanged - Resubmitted regs: reg0=0x0 reg1=0x0 reg2=0x0 reg3=0x0 reg4=0x0 reg5=0x0 reg6=0x0 reg7=0x0 - Resubmitted odp: drop - Rule: table=10 cookie=0 vlan_tci=0x0014/0x0fff,dl_dst=f0:00:00:00:00:01 - OpenFlow actions=load:0x1->NXM_NX_REG0[0..15] - -If you read the commands above carefully, then you might have noticed that they -simply have the Ethernet source and destination addresses exchanged. That -means that if we now rerun the first ``ovs-appctl`` command above, e.g.: - - $ ovs-appctl ofproto/trace br0 \ - in_port=1,dl_vlan=20,dl_src=f0:00:00:00:00:01,dl_dst=90:00:00:00:00:01 \ - -generate - -then we see in the output that the destination has now been learned:: - - Resubmitted flow: unchanged - Resubmitted regs: reg0=0x0 reg1=0x0 reg2=0x0 reg3=0x0 reg4=0x0 reg5=0x0 reg6=0x0 reg7=0x0 - Resubmitted odp: drop - Rule: table=10 cookie=0 vlan_tci=0x0014/0x0fff,dl_dst=90:00:00:00:00:01 - OpenFlow actions=load:0x2->NXM_NX_REG0[0..15] - - -Implementing Table 4: Output Processing ---------------------------------------- - -At entry to stage 4, we know that register 0 contains either the desired output -port or is zero if the packet should be flooded. We also know that the -packet's VLAN is in its 802.1Q header, even if the VLAN was implicit because -the packet came in on an access port. - -The job of the final pipeline stage is to actually output packets. The job is -trivial for output to our trunk port ``p1``:: - - $ ovs-ofctl add-flow br0 "table=4 reg0=1 actions=1" - -For output to the access ports, we just have to strip the VLAN header before -outputting the packet:: - - $ ovs-ofctl add-flows br0 - <<'EOF' - table=4 reg0=2 actions=strip_vlan,2 - table=4 reg0=3 actions=strip_vlan,3 - table=4 reg0=4 actions=strip_vlan,4 - EOF - -The only slightly tricky part is flooding multicast and broadcast packets and -unicast packets with unlearned destinations. For those, we need to make sure -that we only output the packets to the ports that carry our packet's VLAN, and -that we include the 802.1Q header in the copy output to the trunk port but not -in copies output to access ports:: - - $ ovs-ofctl add-flows br0 - <<'EOF' - table=4 reg0=0 priority=99 dl_vlan=20 actions=1,strip_vlan,2 - table=4 reg0=0 priority=99 dl_vlan=30 actions=1,strip_vlan,3,4 - table=4 reg0=0 priority=50 actions=1 - EOF - -.. note:: - Our rules rely on the standard OpenFlow behavior that an output action will - not forward a packet back out the port it came in on. That is, if a packet - comes in on p1, and we've learned that the packet's destination MAC is also - on p1, so that we end up with ``actions=1`` as our actions, the switch will - not forward the packet back out its input port. The - multicast/broadcast/unknown destination cases above also rely on this - behavior. - -Testing Table 4 ---------------- - -Example 1: Broadcast, Multicast, and Unknown Destination -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Try tracing a broadcast packet arriving on ``p1`` in VLAN ``30``:: - - $ ovs-appctl ofproto/trace br0 \ - in_port=1,dl_dst=ff:ff:ff:ff:ff:ff,dl_vlan=30 - -The interesting part of the output is the final line, which shows that the -switch would remove the 802.1Q header and then output the packet to ``p3`` -and ``p4``, which are access ports for VLAN ``30``:: - - Datapath actions: pop_vlan,3,4 - -Similarly, if we trace a broadcast packet arriving on ``p3``:: - - $ ovs-appctl ofproto/trace br0 in_port=3,dl_dst=ff:ff:ff:ff:ff:ff - -then we see that it is output to ``p1`` with an 802.1Q tag and then to ``p4`` -without one:: - - Datapath actions: push_vlan(vid=30,pcp=0),1,pop_vlan,4 - -.. note:: - Open vSwitch could simplify the datapath actions here to just - ``4,push_vlan(vid=30,pcp=0),1`` but it is not smart enough to do so. - -The following are also broadcasts, but the result is to drop the packets -because the VLAN only belongs to the input port:: - - $ ovs-appctl ofproto/trace br0 \ - in_port=1,dl_dst=ff:ff:ff:ff:ff:ff - $ ovs-appctl ofproto/trace br0 \ - in_port=1,dl_dst=ff:ff:ff:ff:ff:ff,dl_vlan=55 - -Try some other broadcast cases on your own:: - - $ ovs-appctl ofproto/trace br0 - in_port=1,dl_dst=ff:ff:ff:ff:ff:ff,dl_vlan=20 - $ ovs-appctl ofproto/trace br0 - in_port=2,dl_dst=ff:ff:ff:ff:ff:ff - $ ovs-appctl ofproto/trace br0 - in_port=4,dl_dst=ff:ff:ff:ff:ff:ff - -You can see the same behavior with multicast packets and with unicast -packets whose destination has not been learned, e.g.:: - - $ ovs-appctl ofproto/trace br0 \ - in_port=4,dl_dst=01:00:00:00:00:00 - $ ovs-appctl ofproto/trace br0 \ - in_port=1,dl_dst=90:12:34:56:78:90,dl_vlan=20 - $ ovs-appctl ofproto/trace br0 \ - in_port=1,dl_dst=90:12:34:56:78:90,dl_vlan=30 - -Example 2: MAC Learning -~~~~~~~~~~~~~~~~~~~~~~~ - -Let's follow the same pattern as we did for table 3. First learn a MAC on port -``p1`` in VLAN ``30``:: - - $ ovs-appctl ofproto/trace br0 \ - in_port=1,dl_vlan=30,dl_src=10:00:00:00:00:01,dl_dst=20:00:00:00:00:01 \ - -generate - -You can see from the last line of output that the packet's destination is -unknown, so it gets flooded to both ``p3`` and ``p4``, the other ports in VLAN -``30``:: - - Datapath actions: pop_vlan,3,4 - -Then reverse the MACs and learn the first flow's destination on port ``p4``:: - - $ ovs-appctl ofproto/trace br0 \ - in_port=4,dl_src=20:00:00:00:00:01,dl_dst=10:00:00:00:00:01 -generate - -The last line of output shows that the this packet's destination is known to be -``p1``, as learned from our previous command:: - - Datapath actions: push_vlan(vid=30,pcp=0),1 - -Now, if we rerun our first command:: - - $ ovs-appctl ofproto/trace br0 \ - in_port=1,dl_vlan=30,dl_src=10:00:00:00:00:01,dl_dst=20:00:00:00:00:01 \ - -generate - -...we can see that the result is no longer a flood but to the specified learned -destination port ``p4``: - - Datapath actions: pop_vlan,4 - -Contact -======= - -bugs@openvswitch.org -http://openvswitch.org/ -- cgit v1.2.1