]>
Commit | Line | Data |
---|---|---|
9107431f RB |
1 | OVN Tutorial |
2 | ============ | |
3 | ||
4 | This tutorial is intended to give you a tour of the basic OVN features using | |
5 | `ovs-sandbox` as a simulated test environment. It’s assumed that you have an | |
6 | understanding of OVS before going through this tutorial. Detail about OVN is | |
848cb198 | 7 | covered in [ovn-architecture(7)], but this tutorial lets you quickly see it in |
9107431f RB |
8 | action. |
9 | ||
10 | Getting Started | |
11 | --------------- | |
12 | ||
13 | For some general information about `ovs-sandbox`, see the “Getting Started” | |
14 | section of [Tutorial.md]. | |
15 | ||
16 | `ovs-sandbox` does not include OVN support by default. To enable OVN, you must | |
17 | pass the `--ovn` flag. For example, if running it straight from the ovs git | |
18 | tree you would run: | |
19 | ||
20 | $ make sandbox SANDBOXFLAGS=”--ovn” | |
21 | ||
22 | Running the sandbox with OVN enabled does the following additional steps to the | |
23 | environment: | |
24 | ||
25 | 1. Creates the `OVN_Northbound` and `OVN_Southbound` databases as described in | |
848cb198 | 26 | [ovn-nb(5)] and [ovn-sb(5)]. |
9107431f | 27 | |
848cb198 | 28 | 2. Creates the `hardware_vtep` database as described in [vtep(5)]. |
9107431f | 29 | |
848cb198 RB |
30 | 3. Runs the [ovn-northd(8)], [ovn-controller(8)], and [ovn-controller-vtep(8)] |
31 | daemons. | |
9107431f RB |
32 | |
33 | 4. Makes OVN and VTEP utilities available for use in the environment, | |
848cb198 | 34 | including [vtep-ctl(8)], [ovn-nbctl(8)], and [ovn-sbctl(8)]. |
9107431f RB |
35 | |
36 | Note that each of these demos assumes you start with a fresh sandbox | |
b3ecab7e FF |
37 | environment. **Re-run `ovs-sandbox` before starting each section.** |
38 | ||
39 | Using GDB | |
40 | --------- | |
41 | ||
42 | GDB support is not required to go through the tutorial. See the “Using GDB” | |
43 | section of [Tutorial.md] for more info. Additional flags exist for launching | |
44 | the debugger for the OVN programs: | |
45 | ||
46 | --gdb-ovn-northd | |
47 | --gdb-ovn-controller | |
48 | --gdb-ovn-controller-vtep | |
49 | ||
9107431f RB |
50 | |
51 | 1) Simple two-port setup | |
52 | ------------------------ | |
53 | ||
54 | This first environment is the simplest OVN example. It demonstrates using OVN | |
55 | with a single logical switch that has two logical ports, both residing on the | |
56 | same hypervisor. | |
57 | ||
58 | Start by running the setup script for this environment. | |
59 | ||
60 | [View ovn/env1/setup.sh][env1setup]. | |
61 | ||
62 | $ ovn/env1/setup.sh | |
63 | ||
64 | You can use the `ovn-nbctl` utility to see an overview of the logical topology. | |
65 | ||
66 | $ ovn-nbctl show | |
ea46a4e9 | 67 | switch 78687d53-e037-4555-bcd3-f4f8eaf3f2aa (sw0) |
31ed1192 | 68 | port sw0-port1 |
2fa326a3 | 69 | addresses: 00:00:00:00:00:01 |
31ed1192 | 70 | port sw0-port2 |
2fa326a3 | 71 | addresses: 00:00:00:00:00:02 |
9107431f RB |
72 | |
73 | The `ovn-sbctl` utility can be used to see into the state stored in the | |
74 | `OVN_Southbound` database. The `show` command shows that there is a single | |
75 | chassis with two logical ports bound to it. In a more realistic | |
76 | multi-hypervisor environment, this would list all hypervisors and where all | |
77 | logical ports are located. | |
78 | ||
79 | $ ovn-sbctl show | |
80 | Chassis “56b18105-5706-46ef-80c4-ff20979ab068” | |
81 | Encap geneve | |
82 | ip: “127.0.0.1” | |
83 | Port_Binding “sw0-port1” | |
84 | Port_Binding “sw0-port2” | |
85 | ||
86 | OVN creates logical flows to describe how the network should behave in logical | |
87 | space. Each chassis then creates OpenFlow flows based on those logical flows | |
88 | that reflect its own local view of the network. The `ovn-sbctl` command can | |
89 | show the logical flows. | |
90 | ||
91 | $ ovn-sbctl lflow-list | |
1e25f5ca RB |
92 | Datapath: 2503dd42-14b1-414a-abbf-33e554e09ddc Pipeline: ingress |
93 | table=0 (ls_in_port_sec_l2 ), priority=100 , match=(eth.src[40]), action=(drop;) | |
94 | table=0 (ls_in_port_sec_l2 ), priority=100 , match=(vlan.present), action=(drop;) | |
95 | table=0 (ls_in_port_sec_l2 ), priority=50 , match=(inport == “sw0-port1” && eth.src == {00:00:00:00:00:01}), action=(next;) | |
96 | table=0 (ls_in_port_sec_l2 ), priority=50 , match=(inport == “sw0-port2” && eth.src == {00:00:00:00:00:02}), action=(next;) | |
97 | table=1 (ls_in_port_sec_ip ), priority=0 , match=(1), action=(next;) | |
98 | 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;) | |
99 | 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;) | |
100 | 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;) | |
101 | 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;) | |
102 | table=2 (ls_in_port_sec_nd ), priority=80 , match=(inport == “sw0-port1” && (arp || nd)), action=(drop;) | |
103 | table=2 (ls_in_port_sec_nd ), priority=80 , match=(inport == “sw0-port2” && (arp || nd)), action=(drop;) | |
104 | table=2 (ls_in_port_sec_nd ), priority=0 , match=(1), action=(next;) | |
105 | table=3 (ls_in_pre_acl ), priority=0 , match=(1), action=(next;) | |
106 | table=4 (ls_in_pre_lb ), priority=0 , match=(1), action=(next;) | |
107 | table=5 (ls_in_pre_stateful ), priority=100 , match=(reg0[0] == 1), action=(ct_next;) | |
108 | table=5 (ls_in_pre_stateful ), priority=0 , match=(1), action=(next;) | |
109 | table=6 (ls_in_acl ), priority=0 , match=(1), action=(next;) | |
110 | table=7 (ls_in_lb ), priority=0 , match=(1), action=(next;) | |
111 | table=8 (ls_in_stateful ), priority=100 , match=(reg0[1] == 1), action=(ct_commit; next;) | |
112 | table=8 (ls_in_stateful ), priority=100 , match=(reg0[2] == 1), action=(ct_lb;) | |
113 | table=8 (ls_in_stateful ), priority=0 , match=(1), action=(next;) | |
114 | table=9 (ls_in_arp_rsp ), priority=0 , match=(1), action=(next;) | |
115 | table=10(ls_in_l2_lkup ), priority=100 , match=(eth.mcast), action=(outport = “_MC_flood”; output;) | |
116 | table=10(ls_in_l2_lkup ), priority=50 , match=(eth.dst == 00:00:00:00:00:01), action=(outport = “sw0-port1”; output;) | |
117 | table=10(ls_in_l2_lkup ), priority=50 , match=(eth.dst == 00:00:00:00:00:02), action=(outport = “sw0-port2”; output;) | |
118 | Datapath: 2503dd42-14b1-414a-abbf-33e554e09ddc Pipeline: egress | |
119 | table=0 (ls_out_pre_lb ), priority=0 , match=(1), action=(next;) | |
120 | table=1 (ls_out_pre_acl ), priority=0 , match=(1), action=(next;) | |
121 | table=2 (ls_out_pre_stateful), priority=100 , match=(reg0[0] == 1), action=(ct_next;) | |
122 | table=2 (ls_out_pre_stateful), priority=0 , match=(1), action=(next;) | |
123 | table=3 (ls_out_lb ), priority=0 , match=(1), action=(next;) | |
124 | table=4 (ls_out_acl ), priority=0 , match=(1), action=(next;) | |
125 | table=5 (ls_out_stateful ), priority=100 , match=(reg0[1] == 1), action=(ct_commit; next;) | |
126 | table=5 (ls_out_stateful ), priority=100 , match=(reg0[2] == 1), action=(ct_lb;) | |
127 | table=5 (ls_out_stateful ), priority=0 , match=(1), action=(next;) | |
128 | table=6 (ls_out_port_sec_ip ), priority=0 , match=(1), action=(next;) | |
129 | table=7 (ls_out_port_sec_l2 ), priority=100 , match=(eth.mcast), action=(output;) | |
130 | table=7 (ls_out_port_sec_l2 ), priority=50 , match=(outport == “sw0-port1” && eth.dst == {00:00:00:00:00:01}), action=(output;) | |
131 | table=7 (ls_out_port_sec_l2 ), priority=50 , match=(outport == “sw0-port2” && eth.dst == {00:00:00:00:00:02}), action=(output;) | |
9107431f RB |
132 | |
133 | Now we can start taking a closer look at how `ovn-controller` has programmed the | |
134 | local switch. Before looking at the flows, we can use `ovs-ofctl` to verify the | |
135 | OpenFlow port numbers for each of the logical ports on the switch. The output | |
136 | shows that `lport1`, which corresponds with our logical port `sw0-port1`, has an | |
137 | OpenFlow port number of `1`. Similarly, `lport2` has an OpenFlow port number of | |
138 | `2`. | |
139 | ||
140 | $ ovs-ofctl show br-int | |
141 | OFPT_FEATURES_REPLY (xid=0x2): dpid:00003e1ba878364d | |
142 | n_tables:254, n_buffers:256 | |
143 | capabilities: FLOW_STATS TABLE_STATS PORT_STATS QUEUE_STATS ARP_MATCH_IP | |
144 | 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 | |
145 | 1(lport1): addr:aa:55:aa:55:00:07 | |
146 | config: PORT_DOWN | |
147 | state: LINK_DOWN | |
148 | speed: 0 Mbps now, 0 Mbps max | |
149 | 2(lport2): addr:aa:55:aa:55:00:08 | |
150 | config: PORT_DOWN | |
151 | state: LINK_DOWN | |
152 | speed: 0 Mbps now, 0 Mbps max | |
153 | LOCAL(br-int): addr:3e:1b:a8:78:36:4d | |
154 | config: PORT_DOWN | |
155 | state: LINK_DOWN | |
156 | speed: 0 Mbps now, 0 Mbps max | |
157 | OFPT_GET_CONFIG_REPLY (xid=0x4): frags=normal miss_send_len=0 | |
158 | ||
159 | Finally, use `ovs-ofctl` to see the OpenFlow flows for `br-int`. Note that some | |
160 | fields have been omitted for brevity. | |
161 | ||
162 | $ ovs-ofctl -O OpenFlow13 dump-flows br-int | |
163 | OFPST_FLOW reply (OF1.3) (xid=0x2): | |
b3ecab7e FF |
164 | table=0, priority=100,in_port=1 actions=set_field:0x1->reg5,set_field:0x1->metadata,set_field:0x1->reg6,resubmit(,16) |
165 | table=0, priority=100,in_port=2 actions=set_field:0x2->reg5,set_field:0x1->metadata,set_field:0x2->reg6,resubmit(,16) | |
9107431f | 166 | table=16, priority=100,metadata=0x1,vlan_tci=0x1000/0x1000 actions=drop |
b3ecab7e | 167 | table=16, priority=100,metadata=0x1,dl_src=01:00:00:00:00:00/01:00:00:00:00:00 actions=drop |
9107431f RB |
168 | table=16, priority=50,reg6=0x1,metadata=0x1,dl_src=00:00:00:00:00:01 actions=resubmit(,17) |
169 | table=16, priority=50,reg6=0x2,metadata=0x1,dl_src=00:00:00:00:00:02 actions=resubmit(,17) | |
170 | table=17, priority=0,metadata=0x1 actions=resubmit(,18) | |
b3ecab7e FF |
171 | 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) |
172 | 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) | |
173 | 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) | |
174 | 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) | |
175 | 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) | |
176 | 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) | |
177 | 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) | |
178 | 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) | |
179 | 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) | |
180 | 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) | |
181 | table=18, priority=80,icmp6,reg6=0x2,metadata=0x1,icmp_type=136,icmp_code=0 actions=drop | |
182 | table=18, priority=80,icmp6,reg6=0x1,metadata=0x1,icmp_type=136,icmp_code=0 actions=drop | |
183 | table=18, priority=80,icmp6,reg6=0x1,metadata=0x1,icmp_type=135,icmp_code=0 actions=drop | |
184 | table=18, priority=80,icmp6,reg6=0x2,metadata=0x1,icmp_type=135,icmp_code=0 actions=drop | |
185 | table=18, priority=80,arp,reg6=0x2,metadata=0x1 actions=drop | |
186 | table=18, priority=80,arp,reg6=0x1,metadata=0x1 actions=drop | |
187 | table=18, priority=0,metadata=0x1 actions=resubmit(,19) | |
188 | table=19, priority=0,metadata=0x1 actions=resubmit(,20) | |
189 | table=20, priority=0,metadata=0x1 actions=resubmit(,21) | |
190 | table=21, priority=0,metadata=0x1 actions=resubmit(,22) | |
191 | table=22, 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) | |
192 | table=22, priority=50,metadata=0x1,dl_dst=00:00:00:00:00:01 actions=set_field:0x1->reg7,resubmit(,32) | |
193 | table=22, priority=50,metadata=0x1,dl_dst=00:00:00:00:00:02 actions=set_field:0x2->reg7,resubmit(,32) | |
9107431f | 194 | table=32, priority=0 actions=resubmit(,33) |
b3ecab7e FF |
195 | table=33, priority=100,reg7=0x1,metadata=0x1 actions=set_field:0x1->reg5,resubmit(,34) |
196 | table=33, priority=100,reg7=0xffff,metadata=0x1 actions=set_field:0x2->reg5,set_field:0x2->reg7,resubmit(,34),set_field:0x1->reg5,set_field:0x1->reg7,resubmit(,34),set_field:0xffff->reg7 | |
197 | table=33, priority=100,reg7=0x2,metadata=0x1 actions=set_field:0x2->reg5,resubmit(,34) | |
9107431f RB |
198 | table=34, priority=100,reg6=0x1,reg7=0x1,metadata=0x1 actions=drop |
199 | table=34, priority=100,reg6=0x2,reg7=0x2,metadata=0x1 actions=drop | |
b3ecab7e | 200 | table=34, priority=0 actions=set_field:0->reg0,set_field:0->reg1,set_field:0->reg2,set_field:0->reg3,set_field:0->reg4,resubmit(,48) |
9107431f | 201 | table=48, priority=0,metadata=0x1 actions=resubmit(,49) |
b3ecab7e FF |
202 | table=49, priority=0,metadata=0x1 actions=resubmit(,50) |
203 | table=50, priority=0,metadata=0x1 actions=resubmit(,51) | |
204 | table=51, priority=100,metadata=0x1,dl_dst=01:00:00:00:00:00/01:00:00:00:00:00 actions=resubmit(,64) | |
205 | table=51, priority=50,reg7=0x2,metadata=0x1,dl_dst=00:00:00:00:00:02 actions=resubmit(,64) | |
206 | table=51, priority=50,reg7=0x1,metadata=0x1,dl_dst=00:00:00:00:00:01 actions=resubmit(,64) | |
9107431f RB |
207 | table=64, priority=100,reg7=0x1,metadata=0x1 actions=output:1 |
208 | table=64, priority=100,reg7=0x2,metadata=0x1 actions=output:2 | |
209 | ||
848cb198 | 210 | The `ovs-appctl` command can be used to generate an OpenFlow trace of how a |
9107431f RB |
211 | packet would be processed in this configuration. This first trace shows a |
212 | packet from `sw0-port1` to `sw0-port2`. The packet arrives from port `1` and | |
213 | should be output to port `2`. | |
214 | ||
215 | [View ovn/env1/packet1.sh][env1packet1]. | |
216 | ||
217 | $ ovn/env1/packet1.sh | |
218 | ||
219 | Trace a broadcast packet from `sw0-port1`. The packet arrives from port `1` and | |
220 | should be output to port `2`. | |
221 | ||
222 | [View ovn/env1/packet2.sh][env1packet2]. | |
223 | ||
224 | $ ovn/env1/packet2.sh | |
225 | ||
226 | You can extend this setup by adding additional ports. For example, to add a | |
227 | third port, run this command: | |
228 | ||
229 | [View ovn/env1/add-third-port.sh][env1thirdport]. | |
230 | ||
231 | $ ovn/env1/add-third-port.sh | |
232 | ||
233 | Now if you do another trace of a broadcast packet from `sw0-port1`, you will see | |
234 | that it is output to both ports `2` and `3`. | |
235 | ||
236 | $ ovn/env1/packet2.sh | |
237 | ||
dd52c85c | 238 | The logical port may have an unknown set of Ethernet addresses. When an OVN logical |
239 | switch processes a unicast Ethernet frame whose destination MAC address is not in any | |
240 | logical port’s addresses column, it delivers it to the port (or ports) whose addresses | |
241 | columns include unknown. | |
242 | ||
243 | [View ovn/env1/add-unknown-ports.sh][env1unknownports]. | |
244 | ||
245 | $ ovn/env1/add-unknown-ports.sh | |
246 | ||
247 | This trace shows a packet from `sw0-port1` to `sw0-port4`, `sw0-port5` whose addresses | |
248 | columns include unknown. You will see that it is output to both ports `4` and `5`. | |
249 | ||
250 | [View ovn/env1/packet3.sh][env1packet3]. | |
251 | ||
252 | $ ovn/env1/packet3.sh | |
253 | ||
254 | The logical port would restrict the host to sending packets from and receiving packets | |
255 | to the ethernet addresses defined in the logical port’s port_security column. | |
256 | In addition to the restrictions described for Ethernet addresses above, such an element | |
257 | of port_security restricts the IPv4 or IPv6 addresses from which the host may send and | |
258 | to which it may receive packets to the specified addresses. | |
259 | ||
260 | [View ovn/env1/add-security-ip-ports.sh][env1securityport]. | |
261 | ||
262 | $ ovn/env1/add-security-ip-ports.sh | |
263 | ||
264 | This trace shows a packet from `sw0-port6` to `sw0-port7`. | |
265 | ||
266 | [View ovn/env1/packet4.sh][env1packet4]. | |
267 | ||
268 | $ ovn/env1/packet4.sh | |
269 | ||
9107431f RB |
270 | 2) 2 switches, 4 ports |
271 | ---------------------- | |
272 | ||
273 | This environment is an extension of the last example. The previous example | |
274 | showed two ports on a single logical switch. In this environment we add a | |
275 | second logical switch that also has two ports. This lets you start to see how | |
276 | `ovn-controller` creates flows for isolated networks to co-exist on the same | |
277 | switch. | |
278 | ||
279 | [View ovn/env2/setup.sh][env2setup]. | |
280 | ||
281 | $ ovn/env2/setup.sh | |
282 | ||
283 | View the logical topology with `ovn-nbctl`. | |
284 | ||
285 | $ ovn-nbctl show | |
ea46a4e9 | 286 | switch e3190dc2-89d1-44ed-9308-e7077de782b3 (sw0) |
31ed1192 | 287 | port sw0-port1 |
2fa326a3 | 288 | addresses: 00:00:00:00:00:01 |
31ed1192 | 289 | port sw0-port2 |
2fa326a3 | 290 | addresses: 00:00:00:00:00:02 |
ea46a4e9 | 291 | switch c8ed4c5f-9733-43f6-93da-795b1aabacb1 (sw1) |
31ed1192 | 292 | port sw1-port1 |
2fa326a3 | 293 | addresses: 00:00:00:00:00:03 |
31ed1192 | 294 | port sw1-port2 |
2fa326a3 | 295 | addresses: 00:00:00:00:00:04 |
9107431f RB |
296 | |
297 | Physically, all ports reside on the same chassis. | |
298 | ||
299 | $ ovn-sbctl show | |
300 | Chassis “56b18105-5706-46ef-80c4-ff20979ab068” | |
301 | Encap geneve | |
302 | ip: “127.0.0.1” | |
303 | Port_Binding “sw1-port2” | |
304 | Port_Binding “sw0-port2” | |
305 | Port_Binding “sw0-port1” | |
306 | Port_Binding “sw1-port1” | |
307 | ||
308 | OVN creates separate logical flows for each logical switch. | |
309 | ||
310 | $ ovn-sbctl lflow-list | |
1e25f5ca RB |
311 | Datapath: 7ee908c1-b0d3-4d03-acc9-42cd7ef7f27d Pipeline: ingress |
312 | table=0 (ls_in_port_sec_l2 ), priority=100 , match=(eth.src[40]), action=(drop;) | |
313 | table=0 (ls_in_port_sec_l2 ), priority=100 , match=(vlan.present), action=(drop;) | |
314 | table=0 (ls_in_port_sec_l2 ), priority=50 , match=(inport == "sw1-port1" && eth.src == {00:00:00:00:00:03}), action=(next;) | |
315 | table=0 (ls_in_port_sec_l2 ), priority=50 , match=(inport == "sw1-port2" && eth.src == {00:00:00:00:00:04}), action=(next;) | |
316 | table=1 (ls_in_port_sec_ip ), priority=0 , match=(1), action=(next;) | |
317 | 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;) | |
318 | 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;) | |
319 | 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;) | |
320 | 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;) | |
321 | table=2 (ls_in_port_sec_nd ), priority=80 , match=(inport == "sw1-port1" && (arp || nd)), action=(drop;) | |
322 | table=2 (ls_in_port_sec_nd ), priority=80 , match=(inport == "sw1-port2" && (arp || nd)), action=(drop;) | |
323 | table=2 (ls_in_port_sec_nd ), priority=0 , match=(1), action=(next;) | |
324 | table=3 (ls_in_pre_acl ), priority=0 , match=(1), action=(next;) | |
325 | table=4 (ls_in_pre_lb ), priority=0 , match=(1), action=(next;) | |
326 | table=5 (ls_in_pre_stateful ), priority=100 , match=(reg0[0] == 1), action=(ct_next;) | |
327 | table=5 (ls_in_pre_stateful ), priority=0 , match=(1), action=(next;) | |
328 | table=6 (ls_in_acl ), priority=0 , match=(1), action=(next;) | |
329 | table=7 (ls_in_lb ), priority=0 , match=(1), action=(next;) | |
330 | table=8 (ls_in_stateful ), priority=100 , match=(reg0[1] == 1), action=(ct_commit; next;) | |
331 | table=8 (ls_in_stateful ), priority=100 , match=(reg0[2] == 1), action=(ct_lb;) | |
332 | table=8 (ls_in_stateful ), priority=0 , match=(1), action=(next;) | |
333 | table=9 (ls_in_arp_rsp ), priority=0 , match=(1), action=(next;) | |
334 | table=10(ls_in_l2_lkup ), priority=100 , match=(eth.mcast), action=(outport = "_MC_flood"; output;) | |
335 | table=10(ls_in_l2_lkup ), priority=50 , match=(eth.dst == 00:00:00:00:00:03), action=(outport = "sw1-port1"; output;) | |
336 | table=10(ls_in_l2_lkup ), priority=50 , match=(eth.dst == 00:00:00:00:00:04), action=(outport = "sw1-port2"; output;) | |
337 | Datapath: 7ee908c1-b0d3-4d03-acc9-42cd7ef7f27d Pipeline: egress | |
338 | table=0 (ls_out_pre_lb ), priority=0 , match=(1), action=(next;) | |
339 | table=1 (ls_out_pre_acl ), priority=0 , match=(1), action=(next;) | |
340 | table=2 (ls_out_pre_stateful), priority=100 , match=(reg0[0] == 1), action=(ct_next;) | |
341 | table=2 (ls_out_pre_stateful), priority=0 , match=(1), action=(next;) | |
342 | table=3 (ls_out_lb ), priority=0 , match=(1), action=(next;) | |
343 | table=4 (ls_out_acl ), priority=0 , match=(1), action=(next;) | |
344 | table=5 (ls_out_stateful ), priority=100 , match=(reg0[1] == 1), action=(ct_commit; next;) | |
345 | table=5 (ls_out_stateful ), priority=100 , match=(reg0[2] == 1), action=(ct_lb;) | |
346 | table=5 (ls_out_stateful ), priority=0 , match=(1), action=(next;) | |
347 | table=6 (ls_out_port_sec_ip ), priority=0 , match=(1), action=(next;) | |
348 | table=7 (ls_out_port_sec_l2 ), priority=100 , match=(eth.mcast), action=(output;) | |
349 | table=7 (ls_out_port_sec_l2 ), priority=50 , match=(outport == "sw1-port1" && eth.dst == {00:00:00:00:00:03}), action=(output;) | |
350 | table=7 (ls_out_port_sec_l2 ), priority=50 , match=(outport == "sw1-port2" && eth.dst == {00:00:00:00:00:04}), action=(output;) | |
351 | Datapath: 9ea0c8f9-4f82-4be3-a6c7-6e6f9c2de583 Pipeline: ingress | |
352 | table=0 (ls_in_port_sec_l2 ), priority=100 , match=(eth.src[40]), action=(drop;) | |
353 | table=0 (ls_in_port_sec_l2 ), priority=100 , match=(vlan.present), action=(drop;) | |
354 | table=0 (ls_in_port_sec_l2 ), priority=50 , match=(inport == "sw0-port1" && eth.src == {00:00:00:00:00:01}), action=(next;) | |
355 | table=0 (ls_in_port_sec_l2 ), priority=50 , match=(inport == "sw0-port2" && eth.src == {00:00:00:00:00:02}), action=(next;) | |
356 | table=1 (ls_in_port_sec_ip ), priority=0 , match=(1), action=(next;) | |
357 | 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;) | |
358 | 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;) | |
359 | 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;) | |
360 | 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;) | |
361 | table=2 (ls_in_port_sec_nd ), priority=80 , match=(inport == "sw0-port1" && (arp || nd)), action=(drop;) | |
362 | table=2 (ls_in_port_sec_nd ), priority=80 , match=(inport == "sw0-port2" && (arp || nd)), action=(drop;) | |
363 | table=2 (ls_in_port_sec_nd ), priority=0 , match=(1), action=(next;) | |
364 | table=3 (ls_in_pre_acl ), priority=0 , match=(1), action=(next;) | |
365 | table=4 (ls_in_pre_lb ), priority=0 , match=(1), action=(next;) | |
366 | table=5 (ls_in_pre_stateful ), priority=100 , match=(reg0[0] == 1), action=(ct_next;) | |
367 | table=5 (ls_in_pre_stateful ), priority=0 , match=(1), action=(next;) | |
368 | table=6 (ls_in_acl ), priority=0 , match=(1), action=(next;) | |
369 | table=7 (ls_in_lb ), priority=0 , match=(1), action=(next;) | |
370 | table=8 (ls_in_stateful ), priority=100 , match=(reg0[1] == 1), action=(ct_commit; next;) | |
371 | table=8 (ls_in_stateful ), priority=100 , match=(reg0[2] == 1), action=(ct_lb;) | |
372 | table=8 (ls_in_stateful ), priority=0 , match=(1), action=(next;) | |
373 | table=9 (ls_in_arp_rsp ), priority=0 , match=(1), action=(next;) | |
374 | table=10(ls_in_l2_lkup ), priority=100 , match=(eth.mcast), action=(outport = "_MC_flood"; output;) | |
375 | table=10(ls_in_l2_lkup ), priority=50 , match=(eth.dst == 00:00:00:00:00:01), action=(outport = "sw0-port1"; output;) | |
376 | table=10(ls_in_l2_lkup ), priority=50 , match=(eth.dst == 00:00:00:00:00:02), action=(outport = "sw0-port2"; output;) | |
377 | Datapath: 9ea0c8f9-4f82-4be3-a6c7-6e6f9c2de583 Pipeline: egress | |
378 | table=0 (ls_out_pre_lb ), priority=0 , match=(1), action=(next;) | |
379 | table=1 (ls_out_pre_acl ), priority=0 , match=(1), action=(next;) | |
380 | table=2 (ls_out_pre_stateful), priority=100 , match=(reg0[0] == 1), action=(ct_next;) | |
381 | table=2 (ls_out_pre_stateful), priority=0 , match=(1), action=(next;) | |
382 | table=3 (ls_out_lb ), priority=0 , match=(1), action=(next;) | |
383 | table=4 (ls_out_acl ), priority=0 , match=(1), action=(next;) | |
384 | table=5 (ls_out_stateful ), priority=100 , match=(reg0[1] == 1), action=(ct_commit; next;) | |
385 | table=5 (ls_out_stateful ), priority=100 , match=(reg0[2] == 1), action=(ct_lb;) | |
386 | table=5 (ls_out_stateful ), priority=0 , match=(1), action=(next;) | |
387 | table=6 (ls_out_port_sec_ip ), priority=0 , match=(1), action=(next;) | |
388 | table=7 (ls_out_port_sec_l2 ), priority=100 , match=(eth.mcast), action=(output;) | |
389 | table=7 (ls_out_port_sec_l2 ), priority=50 , match=(outport == "sw0-port1" && eth.dst == {00:00:00:00:00:01}), action=(output;) | |
390 | table=7 (ls_out_port_sec_l2 ), priority=50 , match=(outport == "sw0-port2" && eth.dst == {00:00:00:00:00:02}), action=(output;) | |
9107431f RB |
391 | |
392 | In this setup, `sw0-port1` and `sw0-port2` can send packets to each other, but | |
393 | not to either of the ports on `sw1`. This first trace shows a packet from | |
394 | `sw0-port1` to `sw0-port2`. You should see th packet arrive on OpenFlow port | |
395 | `1` and output to OpenFlow port `2`. | |
396 | ||
397 | [View ovn/env2/packet1.sh][env2packet1]. | |
398 | ||
399 | $ ovn/env2/packet1.sh | |
400 | ||
401 | This next example shows a packet from `sw0-port1` with a destination MAC address | |
402 | of `00:00:00:00:00:03`, which is the MAC address for `sw1-port1`. Since these | |
403 | ports are not on the same logical switch, the packet should just be dropped. | |
404 | ||
405 | [View ovn/env2/packet2.sh][env2packet2]. | |
406 | ||
407 | $ ovn/env2/packet2.sh | |
408 | ||
409 | 3) Two Hypervisors | |
410 | ------------------ | |
411 | ||
412 | The first two examples started by showing OVN on a single hypervisor. A more | |
413 | realistic deployment of OVN would span multiple hypervisors. This example | |
414 | creates a single logical switch with 4 logical ports. It then simulates having | |
415 | two hypervisors with two of the logical ports bound to each hypervisor. | |
416 | ||
417 | [View ovn/env3/setup.sh][env3setup]. | |
418 | ||
419 | $ ovn/env3/setup.sh | |
420 | ||
421 | You can start by viewing the logical topology with `ovn-nbctl`. | |
422 | ||
423 | $ ovn-nbctl show | |
ea46a4e9 | 424 | switch b977dc03-79a5-41ba-9665-341a80e1abfd (sw0) |
31ed1192 | 425 | port sw0-port1 |
2fa326a3 | 426 | addresses: 00:00:00:00:00:01 |
31ed1192 | 427 | port sw0-port2 |
2fa326a3 | 428 | addresses: 00:00:00:00:00:02 |
31ed1192 | 429 | port sw0-port4 |
2fa326a3 | 430 | addresses: 00:00:00:00:00:04 |
31ed1192 | 431 | port sw0-port3 |
2fa326a3 | 432 | addresses: 00:00:00:00:00:03 |
9107431f RB |
433 | |
434 | Using `ovn-sbctl` to view the state of the system, we can see that there are two | |
435 | chassis: one local that we can interact with, and a fake remote chassis. Two | |
436 | logical ports are bound to each. Both chassis have an IP address of localhost, | |
437 | but in a realistic deployment that would be the IP address used for tunnels to | |
438 | that chassis. | |
439 | ||
440 | $ ovn-sbctl show | |
441 | Chassis “56b18105-5706-46ef-80c4-ff20979ab068” | |
442 | Encap geneve | |
443 | ip: “127.0.0.1” | |
444 | Port_Binding “sw0-port2” | |
445 | Port_Binding “sw0-port1” | |
446 | Chassis fakechassis | |
447 | Encap geneve | |
448 | ip: “127.0.0.1” | |
449 | Port_Binding “sw0-port4” | |
450 | Port_Binding “sw0-port3” | |
451 | ||
452 | Packets between `sw0-port1` and `sw0-port2` behave just like the previous | |
453 | examples. Packets to ports on a remote chassis are the interesting part of this | |
454 | example. You may have noticed before that OVN’s logical flows are broken up | |
455 | into ingress and egress tables. Given a packet from `sw0-port1` on the local | |
456 | chassis to `sw0-port3` on the remote chassis, the ingress pipeline is executed | |
457 | on the local switch. OVN then determines that it must forward the packet over a | |
458 | geneve tunnel. When it arrives at the remote chassis, the egress pipeline will | |
459 | be executed there. | |
460 | ||
461 | This first packet trace shows the first part of this example. It’s a packet | |
462 | from `sw0-port1` to `sw0-port3` from the perspective of the local chassis. | |
463 | `sw0-port1` is OpenFlow port `1`. The tunnel to the fake remote chassis is | |
464 | OpenFlow port `3`. You should see the ingress pipeline being executed and then | |
465 | the packet output to port `3`, the geneve tunnel. | |
466 | ||
467 | [View ovn/env3/packet1.sh][env3packet1]. | |
468 | ||
469 | $ ovn/env3/packet1.sh | |
470 | ||
471 | To simulate what would happen when that packet arrives at the remote chassis we | |
472 | can flip this example around. Consider a packet from `sw0-port3` to | |
473 | `sw0-port1`. This trace shows what would happen when that packet arrives at the | |
474 | local chassis. The packet arrives on OpenFlow port `3` (the tunnel). You should | |
475 | then see the egress pipeline get executed and the packet output to OpenFlow port | |
476 | `1`. | |
477 | ||
478 | [View ovn/env3/packet2.sh][env3packet2]. | |
479 | ||
480 | $ ovn/env3/packet2.sh | |
481 | ||
482 | 4) Locally attached networks | |
483 | ---------------------------- | |
484 | ||
485 | While OVN is generally focused on the implementation of logical networks using | |
486 | overlays, it’s also possible to use OVN as a control plane to manage logically | |
487 | direct connectivity to networks that are locally accessible to each chassis. | |
488 | ||
489 | This example includes two hypervisors. Both hypervisors have two ports on them. | |
490 | We want to use OVN to manage the connectivity of these ports to a network | |
491 | attached to each hypervisor that we will call “physnet1”. | |
492 | ||
493 | This scenario requires some additional configuration of `ovn-controller`. We | |
494 | must configure a mapping between `physnet1` and a local OVS bridge that provides | |
495 | connectivity to that network. We call these “bridge mappings”. For our | |
496 | example, the following script creates a bridge called `br-eth1` and then | |
497 | configures `ovn-controller` with a bridge mapping from `physnet1` to `br-eth1`. | |
498 | ||
499 | [View ovn/env4/setup1.sh][env4setup1]. | |
500 | ||
501 | $ ovn/env4/setup1.sh | |
502 | ||
503 | At this point we should be able to see that `ovn-controller` has automatically | |
504 | created patch ports between `br-int` and `br-eth1`. | |
505 | ||
506 | $ ovs-vsctl show | |
507 | aea39214-ebec-4210-aa34-1ae7d6921720 | |
508 | Bridge br-int | |
509 | fail_mode: secure | |
510 | Port “patch-br-int-to-br-eth1” | |
511 | Interface “patch-br-int-to-br-eth1” | |
512 | type: patch | |
513 | options: {peer=”patch-br-eth1-to-br-int”} | |
514 | Port br-int | |
515 | Interface br-int | |
516 | type: internal | |
517 | Bridge “br-eth1” | |
518 | Port “br-eth1” | |
519 | Interface “br-eth1” | |
520 | type: internal | |
521 | Port “patch-br-eth1-to-br-int” | |
522 | Interface “patch-br-eth1-to-br-int” | |
523 | type: patch | |
524 | options: {peer=”patch-br-int-to-br-eth1”} | |
525 | ||
526 | Now we can move on to the next setup phase for this example. We want to create | |
527 | a fake second chassis and then create the topology that tells OVN we want both | |
528 | ports on both hypervisors connected to `physnet1`. The way this is modeled in | |
529 | OVN is by creating a logical switch for each port. The logical switch has the | |
530 | regular VIF port and a `localnet` port. | |
531 | ||
532 | [View ovn/env4/setup2.sh][env4setup2]. | |
533 | ||
534 | $ ovn/env4/setup2.sh | |
535 | ||
536 | The logical topology from `ovn-nbctl` should look like this. | |
537 | ||
538 | $ ovn-nbctl show | |
ea46a4e9 | 539 | switch 5a652488-cfba-4f3e-929d-00010cdfde40 (provnet1-2) |
31ed1192 | 540 | port provnet1-2-physnet1 |
2fa326a3 | 541 | addresses: unknown |
31ed1192 | 542 | port provnet1-2-port1 |
2fa326a3 | 543 | addresses: 00:00:00:00:00:02 |
ea46a4e9 | 544 | switch 5829b60a-eda8-4d78-94f6-7017ff9efcf0 (provnet1-4) |
31ed1192 | 545 | port provnet1-4-port1 |
2fa326a3 | 546 | addresses: 00:00:00:00:00:04 |
31ed1192 | 547 | port provnet1-4-physnet1 |
2fa326a3 | 548 | addresses: unknown |
ea46a4e9 | 549 | switch 06cbbcb6-38e3-418d-a81e-634ec9b54ad6 (provnet1-1) |
31ed1192 | 550 | port provnet1-1-port1 |
2fa326a3 | 551 | addresses: 00:00:00:00:00:01 |
31ed1192 | 552 | port provnet1-1-physnet1 |
2fa326a3 | 553 | addresses: unknown |
ea46a4e9 | 554 | switch 9cba3b3b-59ae-4175-95f5-b6f1cd9c2afb (provnet1-3) |
31ed1192 | 555 | port provnet1-3-physnet1 |
2fa326a3 | 556 | addresses: unknown |
31ed1192 | 557 | port provnet1-3-port1 |
2fa326a3 | 558 | addresses: 00:00:00:00:00:03 |
9107431f RB |
559 | |
560 | `port1` on each logical switch represents a regular logical port for a VIF on a | |
561 | hypervisor. `physnet1` on each logical switch is the special `localnet` port. | |
562 | You can use `ovn-nbctl` to see that this port has a `type` and `options` set. | |
563 | ||
31ed1192 | 564 | $ ovn-nbctl lsp-get-type provnet1-1-physnet1 |
9107431f RB |
565 | localnet |
566 | ||
31ed1192 | 567 | $ ovn-nbctl lsp-get-options provnet1-1-physnet1 |
9107431f RB |
568 | network_name=physnet1 |
569 | ||
570 | The physical topology should reflect that there are two regular ports on each | |
571 | chassis. | |
572 | ||
573 | $ ovn-sbctl show | |
574 | Chassis fakechassis | |
575 | Encap geneve | |
576 | ip: “127.0.0.1” | |
577 | Port_Binding “provnet1-3-port1” | |
578 | Port_Binding “provnet1-4-port1” | |
579 | Chassis “56b18105-5706-46ef-80c4-ff20979ab068” | |
580 | Encap geneve | |
581 | ip: “127.0.0.1” | |
582 | Port_Binding “provnet1-2-port1” | |
583 | Port_Binding “provnet1-1-port1” | |
584 | ||
585 | All four of our ports should be able to communicate with each other, but they do | |
586 | so through `physnet1`. A packet from any of these ports to any destination | |
587 | should be output to the OpenFlow port number that corresponds to the patch port | |
588 | to `br-eth1`. | |
589 | ||
590 | This example assumes following OpenFlow port number mappings: | |
591 | ||
592 | * 1 = patch port to `br-eth1` | |
593 | * 2 = tunnel to the fake second chassis | |
594 | * 3 = lport1, which is the logical port named `provnet1-1-port1` | |
595 | * 4 = lport2, which is the logical port named `provnet1-2-port1` | |
596 | ||
597 | We get those port numbers using `ovs-ofctl`: | |
598 | ||
599 | $ ovs-ofctl show br-int | |
600 | OFPT_FEATURES_REPLY (xid=0x2): dpid:0000765054700040 | |
601 | n_tables:254, n_buffers:256 | |
602 | capabilities: FLOW_STATS TABLE_STATS PORT_STATS QUEUE_STATS ARP_MATCH_IP | |
603 | actions: output enqueue set_vlan_vid set_vlan_pcp strip_vlan mod_dl_src | |
604 | mod_dl_dst mod_nw_src mod_nw_dst mod_nw_tos mod_tp_src mod_tp_dst | |
605 | 1(patch-br-int-to): addr:de:29:14:95:8a:b8 | |
606 | config: 0 | |
607 | state: 0 | |
608 | speed: 0 Mbps now, 0 Mbps max | |
609 | 2(ovn-fakech-0): addr:aa:55:aa:55:00:08 | |
610 | config: PORT_DOWN | |
611 | state: LINK_DOWN | |
612 | speed: 0 Mbps now, 0 Mbps max | |
613 | 3(lport1): addr:aa:55:aa:55:00:09 | |
614 | config: PORT_DOWN | |
615 | state: LINK_DOWN | |
616 | speed: 0 Mbps now, 0 Mbps max | |
617 | 4(lport2): addr:aa:55:aa:55:00:0a | |
618 | config: PORT_DOWN | |
619 | state: LINK_DOWN | |
620 | speed: 0 Mbps now, 0 Mbps max | |
621 | LOCAL(br-int): addr:76:50:54:70:00:40 | |
622 | config: PORT_DOWN | |
623 | state: LINK_DOWN | |
624 | speed: 0 Mbps now, 0 Mbps max | |
625 | OFPT_GET_CONFIG_REPLY (xid=0x4): frags=normal miss_send_len=0 | |
626 | ||
627 | This first trace shows a packet from `provnet1-1-port1` with a destination MAC | |
628 | address of `provnet1-2-port1`. Despite both of these ports being on the same | |
629 | local switch (`lport1` and `lport2`), we expect all packets to be sent out to | |
630 | `br-eth1` (OpenFlow port 1). We then expect the network to handle getting the | |
631 | packet to its destination. In practice, this will be optimized at `br-eth1` and | |
632 | the packet won’t actually go out and back on the network. | |
633 | ||
634 | [View ovn/env4/packet1.sh][env4packet1]. | |
635 | ||
636 | $ ovn/env4/packet1.sh | |
637 | ||
638 | This next trace is a continuation of the previous one. This shows the packet | |
639 | coming back into `br-int` from `br-eth1`. We now expect the packet to be output | |
640 | to `provnet1-2-port1`, which is OpenFlow port 4. | |
641 | ||
642 | [View ovn/env4/packet2.sh][env4packet2]. | |
643 | ||
644 | $ ovn/env4/packet2.sh | |
645 | ||
646 | This next trace shows an example of a packet being sent to a destination on | |
647 | another hypervisor. The source is `provnet1-2-port1`, but the destination is | |
648 | `provnet1-3-port1`, which is on the other fake chassis. As usual, we expect the | |
649 | output to be to OpenFlow port 1, the patch port to `br-et1`. | |
650 | ||
651 | [View ovn/env4/packet3.sh][env4packet3]. | |
652 | ||
653 | $ ovn/env4/packet3.sh | |
654 | ||
655 | This next test shows a broadcast packet. The destination should still only be | |
656 | OpenFlow port 1. | |
657 | ||
658 | [View ovn/env4/packet4.sh][env4packet4] | |
659 | ||
660 | $ ovn/env4/packet4.sh | |
661 | ||
662 | Finally, this last trace shows what happens when a broadcast packet arrives | |
663 | from the network. In this case, it simulates a broadcast that originated from a | |
664 | port on the remote fake chassis and arrived at the local chassis via `br-eth1`. | |
665 | We should see it output to both local ports that are attached to this network | |
666 | (OpenFlow ports 3 and 4). | |
667 | ||
668 | [View ovn/env4/packet5.sh][env4packet5] | |
669 | ||
670 | $ ovn/env4/packet5.sh | |
671 | ||
672 | 5) Locally attached networks with VLANs | |
673 | --------------------------------------- | |
674 | ||
675 | This example is an extension of the previous one. We take the same setup and | |
676 | add two more ports to each hypervisor. Instead of having the new ports directly | |
677 | connected to `physnet1` as before, we indicate that we want them on VLAN 101 of | |
678 | `physnet1`. This shows how `localnet` ports can be used to provide connectivity | |
679 | to either a flat network or a VLAN on that network. | |
680 | ||
681 | [View ovn/env5/setup.sh][env5setup] | |
682 | ||
683 | $ ovn/env5/setup.sh | |
684 | ||
685 | The logical topology shown by `ovn-nbctl` is similar to `env4`, except we now | |
686 | have 8 regular VIF ports connected to `physnet1` instead of 4. The additional 4 | |
687 | ports we have added are all on VLAN 101 of `physnet1`. Note that the `localnet` | |
688 | ports representing connectivity to VLAN 101 of `physnet1` have the `tag` field | |
689 | set to `101`. | |
690 | ||
691 | $ ovn-nbctl show | |
ea46a4e9 | 692 | switch 12ea93d0-694b-48e9-adef-d0ddd3ec4ac9 (provnet1-7-101) |
31ed1192 | 693 | port provnet1-7-physnet1-101 |
9107431f | 694 | parent: , tag:101 |
2fa326a3 | 695 | addresses: unknown |
31ed1192 | 696 | port provnet1-7-101-port1 |
2fa326a3 | 697 | addresses: 00:00:00:00:00:07 |
ea46a4e9 | 698 | switch c9a5ce3a-15ec-48ea-a898-416013463589 (provnet1-4) |
31ed1192 | 699 | port provnet1-4-port1 |
2fa326a3 | 700 | addresses: 00:00:00:00:00:04 |
31ed1192 | 701 | port provnet1-4-physnet1 |
2fa326a3 | 702 | addresses: unknown |
ea46a4e9 | 703 | switch e07d4f7a-2085-4fbb-9937-d6192b79a397 (provnet1-1) |
31ed1192 | 704 | port provnet1-1-physnet1 |
2fa326a3 | 705 | addresses: unknown |
31ed1192 | 706 | port provnet1-1-port1 |
2fa326a3 | 707 | addresses: 00:00:00:00:00:01 |
ea46a4e9 | 708 | switch 6c098474-0509-4219-bc9b-eb4e28dd1aeb (provnet1-2) |
31ed1192 | 709 | port provnet1-2-physnet1 |
2fa326a3 | 710 | addresses: unknown |
31ed1192 | 711 | port provnet1-2-port1 |
2fa326a3 | 712 | addresses: 00:00:00:00:00:02 |
ea46a4e9 | 713 | switch 723c4684-5d58-4202-b8e3-4ba99ad5ed9e (provnet1-8-101) |
31ed1192 | 714 | port provnet1-8-101-port1 |
2fa326a3 | 715 | addresses: 00:00:00:00:00:08 |
31ed1192 | 716 | port provnet1-8-physnet1-101 |
9107431f | 717 | parent: , tag:101 |
2fa326a3 | 718 | addresses: unknown |
ea46a4e9 | 719 | switch 8444e925-ceb2-4b02-ac20-eb2e4cfb954d (provnet1-6-101) |
31ed1192 | 720 | port provnet1-6-physnet1-101 |
9107431f | 721 | parent: , tag:101 |
2fa326a3 | 722 | addresses: unknown |
31ed1192 | 723 | port provnet1-6-101-port1 |
2fa326a3 | 724 | addresses: 00:00:00:00:00:06 |
ea46a4e9 | 725 | switch e11e5605-7c46-4395-b28d-cff57451fc7e (provnet1-3) |
31ed1192 | 726 | port provnet1-3-port1 |
2fa326a3 | 727 | addresses: 00:00:00:00:00:03 |
31ed1192 | 728 | port provnet1-3-physnet1 |
2fa326a3 | 729 | addresses: unknown |
ea46a4e9 | 730 | switch 0706b697-6c92-4d54-bc0a-db5bababb74a (provnet1-5-101) |
31ed1192 | 731 | port provnet1-5-101-port1 |
2fa326a3 | 732 | addresses: 00:00:00:00:00:05 |
31ed1192 | 733 | port provnet1-5-physnet1-101 |
9107431f | 734 | parent: , tag:101 |
2fa326a3 | 735 | addresses: unknown |
9107431f RB |
736 | |
737 | The physical topology shows that we have 4 regular VIF ports on each simulated | |
738 | hypervisor. | |
739 | ||
740 | $ ovn-sbctl show | |
741 | Chassis “56b18105-5706-46ef-80c4-ff20979ab068” | |
742 | Encap geneve | |
743 | ip: “127.0.0.1” | |
744 | Port_Binding “provnet1-6-101-port1” | |
745 | Port_Binding “provnet1-1-port1” | |
746 | Port_Binding “provnet1-2-port1” | |
747 | Port_Binding “provnet1-5-101-port1” | |
748 | Chassis fakechassis | |
749 | Encap geneve | |
750 | ip: “127.0.0.1” | |
751 | Port_Binding “provnet1-4-port1” | |
752 | Port_Binding “provnet1-3-port1” | |
753 | Port_Binding “provnet1-8-101-port1” | |
754 | Port_Binding “provnet1-7-101-port1” | |
755 | ||
756 | All of the traces from the previous example, `env4`, should work in this | |
757 | environment and provide the same result. Now we can show what happens for the | |
758 | ports connected to VLAN 101. This first example shows a packet originating from | |
759 | `provnet1-5-101-port1`, which is OpenFlow port 5. We should see VLAN tag 101 | |
760 | pushed on the packet and then output to OpenFlow port 1, the patch port to | |
761 | `br-eth1` (the bridge providing connectivity to `physnet1`). | |
762 | ||
763 | [View ovn/env5/packet1.sh][env5packet1]. | |
764 | ||
765 | $ ovn/env5/packet1.sh | |
766 | ||
767 | If we look at a broadcast packet arriving on VLAN 101 of `physnet1`, we should | |
768 | see it output to OpenFlow ports 5 and 6 only. | |
769 | ||
770 | [View ovn/env5/packet2.sh][env5packet2]. | |
771 | ||
772 | $ ovn/env5/packet2.sh | |
773 | ||
774 | ||
0df6430e RB |
775 | 6) Stateful ACLs |
776 | ---------------- | |
777 | ||
778 | ACLs provide a way to do distributed packet filtering for OVN networks. One | |
779 | example use of ACLs is that OpenStack Neutron uses them to implement security | |
780 | groups. ACLs are implemented using conntrack integration with OVS. | |
781 | ||
782 | Start with a simple logical switch with 2 logical ports. | |
783 | ||
784 | [View ovn/env6/setup.sh][env6setup]. | |
785 | ||
786 | $ ovn/env6/setup.sh | |
787 | ||
788 | A common use case would be the following policy applied for `sw0-port1`: | |
789 | ||
790 | * Allow outbound IP traffic and associated return traffic. | |
791 | * Allow incoming ICMP requests and associated return traffic. | |
792 | * Allow incoming SSH connections and associated return traffic. | |
793 | * Drop other incoming IP traffic. | |
794 | ||
795 | The following script applies this policy to our environment. | |
796 | ||
797 | [View ovn/env6/add-acls.sh][env6acls]. | |
798 | ||
799 | $ ovn/env6/add-acls.sh | |
800 | ||
801 | We can view the configured ACLs on this network using the `ovn-nbctl` command. | |
802 | ||
803 | $ ovn-nbctl acl-list sw0 | |
804 | from-lport 1002 (inport == “sw0-port1” && ip) allow-related | |
805 | to-lport 1002 (outport == “sw0-port1” && ip && icmp) allow-related | |
806 | to-lport 1002 (outport == “sw0-port1” && ip && tcp && tcp.dst == 22) allow-related | |
807 | to-lport 1001 (outport == “sw0-port1” && ip) drop | |
808 | ||
809 | Now that we have ACLs configured, there are new entries in the logical flow | |
810 | table in the stages `switch_in_pre_acl`, switch_in_acl`, `switch_out_pre_acl`, | |
811 | and `switch_out_acl`. | |
812 | ||
813 | $ ovn-sbctl lflow-list | |
814 | ||
815 | Let’s look more closely at `switch_out_pre_acl` and `switch_out_acl`. | |
816 | ||
817 | In `switch_out_pre_acl`, we match IP traffic and put it through the connection | |
818 | tracker. This populates the connection state fields so that we can apply policy | |
819 | as appropriate. | |
820 | ||
821 | table=0(switch_out_pre_acl), priority= 100, match=(ip), action=(ct_next;) | |
f18d4462 | 822 | table=1(switch_out_pre_acl), priority= 0, match=(1), action=(next;) |
0df6430e RB |
823 | |
824 | In `switch_out_acl`, we allow packets associated with existing connections. We | |
825 | drop packets that are deemed to be invalid (such as non-SYN TCP packet not | |
826 | associated with an existing connection). | |
827 | ||
828 | table=1(switch_out_acl), priority=65535, match=(!ct.est && ct.rel && !ct.new && !ct.inv), action=(next;) | |
829 | table=1(switch_out_acl), priority=65535, match=(ct.est && !ct.rel && !ct.new && !ct.inv), action=(next;) | |
830 | table=1(switch_out_acl), priority=65535, match=(ct.inv), action=(drop;) | |
831 | ||
832 | For new connections, we apply our configured ACL policy to decide whether to | |
833 | allow the connection or not. In this case, we’ll allow ICMP or SSH. Otherwise, | |
834 | we’ll drop the packet. | |
835 | ||
836 | table=1(switch_out_acl), priority= 2002, match=(ct.new && (outport == “sw0-port1” && ip && icmp)), action=(ct_commit; next;) | |
837 | table=1(switch_out_acl), priority= 2002, match=(ct.new && (outport == “sw0-port1” && ip && tcp && tcp.dst == 22)), action=(ct_commit; next;) | |
838 | table=1(switch_out_acl), priority= 2001, match=(outport == “sw0-port1” && ip), action=(drop;) | |
839 | ||
840 | When using ACLs, the default policy is to allow and track IP connections. Based | |
841 | on our above policy, IP traffic directed at `sw0-port1` will never hit this flow | |
842 | at priority 1. | |
843 | ||
844 | table=1(switch_out_acl), priority= 1, match=(ip), action=(ct_commit; next;) | |
845 | table=1(switch_out_acl), priority= 0, match=(1), action=(next;) | |
846 | ||
847 | Note that conntrack integration is not yet supported in ovs-sandbox, so the | |
848 | OpenFlow flows will not represent what you’d see in a real environment. The | |
849 | logical flows described above give a very good idea of what the flows look like, | |
850 | though. | |
851 | ||
852 | [This blog post][openstack-ovn-acl-blog] discusses OVN ACLs from an OpenStack | |
853 | perspective and also provides an example of what the resulting OpenFlow flows | |
854 | look like. | |
855 | ||
a97eef91 NS |
856 | 7) Container Ports |
857 | ------------------ | |
858 | ||
859 | OVN supports containers running directly on the hypervisors and running | |
860 | containers inside VMs. This example shows how OVN supports network | |
861 | virtualization to containers when run inside VMs. Details about how to use | |
862 | docker containers in OVS can be found [here][openvswitch-docker]. | |
863 | ||
864 | To support container traffic created inside a VM and to distinguish network | |
865 | traffic coming from different container vifs, for each container a logical | |
866 | port needs to be created with parent name set to the VM's logical port and | |
867 | the tag set to the vlan tag of the container vif. | |
868 | ||
869 | Start with a simple logical switch with 3 logical ports. | |
870 | ||
871 | [View ovn/env7/setup.sh][env7setup]. | |
872 | ||
873 | $ ovn/env7/setup.sh | |
874 | ||
875 | Lets create a container vif attached to the logical port 'sw0-port1' and | |
876 | another container vif attached to the logical port 'sw0-port2'. | |
877 | ||
878 | [View ovn/env7/add-container-ports.sh][env7contports] | |
879 | ||
880 | $ ovn/env7/add-container-ports.sh | |
881 | ||
882 | Run the `ovn-nbctl` command to see the logical ports | |
883 | ||
884 | $ovn-nbctl show | |
885 | ||
886 | ||
887 | As you can see a logical port 'csw0-cport1' is created on a logical | |
888 | switch 'csw0' whose parent is 'sw0-port1' and it has tag set to 42. | |
889 | And a logical port 'csw0-cport2' is created on the logical switch 'csw0' | |
890 | whose parent is 'sw0-port2' and it has tag set to 43. | |
891 | ||
892 | Bridge 'br-vmport1' represents the ovs bridge running inside the VM | |
893 | connected to the logical port 'sw0-port1'. In this tutorial the ovs port | |
894 | to 'sw0-port1' is created as a patch port with its peer connected to the | |
895 | ovs bridge 'br-vmport1'. An ovs port 'cport1' is added to 'br-vmport1' | |
896 | which represents the container interface connected to the ovs bridge | |
897 | and vlan tag set to 42. Similarly 'br-vmport2' represents the ovs bridge | |
898 | for the logical port 'sw0-port2' and 'cport2' connected to 'br-vmport2' | |
899 | with vlan tag set to 43. | |
900 | ||
901 | This first trace shows a packet from 'csw0-port1' with a destination mac | |
902 | address of 'csw0-port2'. You can see ovs bridge of the vm 'br-vmport1' tags | |
903 | the traffic with vlan id 42 and the traffic reaches to the br-int because | |
904 | of the patch port. As you can see below `ovn-controller` has added a flow | |
905 | to strip the vlan tag and set the reg6 and metadata appropriately. | |
906 | ||
907 | $ ovs-ofctl -O OpenFlow13 dump-flows br-int | |
908 | OFPST_FLOW reply (OF1.3) (xid=0x2): | |
909 | 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) | |
910 | 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) | |
911 | 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) | |
912 | 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) | |
913 | ||
914 | [View ovn/env7/packet1.sh][env7packet1]. | |
915 | ||
916 | $ ovn/env5/packet1.sh | |
917 | ||
918 | ||
919 | The second trace shows a packet from 'csw0-port2' to 'csw0-port1'. | |
920 | ||
921 | [View ovn/env7/packet2.sh][env7packet2]. | |
922 | ||
923 | $ ovn/env5/packet1.sh | |
924 | ||
925 | You can extend this setup by adding additional container ports with two | |
926 | hypervisors. Please see the tutorial 3 above. | |
927 | ||
848cb198 | 928 | [ovn-architecture(7)]:http://openvswitch.org/support/dist-docs/ovn-architecture.7.html |
2552e0e0 | 929 | [Tutorial.md]:https://github.com/openvswitch/ovs/blob/master/tutorial/Tutorial.md |
848cb198 RB |
930 | [ovn-nb(5)]:http://openvswitch.org/support/dist-docs/ovn-nb.5.html |
931 | [ovn-sb(5)]:http://openvswitch.org/support/dist-docs/ovn-sb.5.html | |
932 | [vtep(5)]:http://openvswitch.org/support/dist-docs/vtep.5.html | |
f987d2af | 933 | [ovn-northd(8)]:http://openvswitch.org/support/dist-docs/ovn-northd.8.html |
848cb198 RB |
934 | [ovn-controller(8)]:http://openvswitch.org/support/dist-docs/ovn-controller.8.html |
935 | [ovn-controller-vtep(8)]:http://openvswitch.org/support/dist-docs/ovn-controller-vtep.8.html | |
936 | [vtep-ctl(8)]:http://openvswitch.org/support/dist-docs/vtep-ctl.8.html | |
937 | [ovn-nbctl(8)]:http://openvswitch.org/support/dist-docs/ovn-nbctl.8.html | |
938 | [ovn-sbctl(8)]:http://openvswitch.org/support/dist-docs/ovn-sbctl.8.html | |
2552e0e0 RB |
939 | [env1setup]:https://github.com/openvswitch/ovs/blob/master/tutorial/ovn/env1/setup.sh |
940 | [env1packet1]:https://github.com/openvswitch/ovs/blob/master/tutorial/ovn/env1/packet1.sh | |
941 | [env1packet2]:https://github.com/openvswitch/ovs/blob/master/tutorial/ovn/env1/packet2.sh | |
942 | [env1thirdport]:https://github.com/openvswitch/ovs/blob/master/tutorial/ovn/env1/add-third-port.sh | |
dd52c85c | 943 | [env1unknownports]:https://github.com/nickcooper-zhangtonghao/ovs/blob/master/tutorial/ovn/env1/add-unknown-ports.sh |
944 | [env1securityport]:https://github.com/nickcooper-zhangtonghao/ovs/blob/master/tutorial/ovn/env1/add-security-ip-ports.sh | |
945 | [env1packet3]:https://github.com/nickcooper-zhangtonghao/ovs/blob/master/tutorial/ovn/env1/packet3.sh | |
946 | [env1packet4]:https://github.com/nickcooper-zhangtonghao/ovs/blob/master/tutorial/ovn/env1/packet4.sh | |
2552e0e0 RB |
947 | [env2setup]:https://github.com/openvswitch/ovs/blob/master/tutorial/ovn/env2/setup.sh |
948 | [env2packet1]:https://github.com/openvswitch/ovs/blob/master/tutorial/ovn/env2/packet1.sh | |
949 | [env2packet2]:https://github.com/openvswitch/ovs/blob/master/tutorial/ovn/env2/packet2.sh | |
950 | [env3setup]:https://github.com/openvswitch/ovs/blob/master/tutorial/ovn/env3/setup.sh | |
951 | [env3packet1]:https://github.com/openvswitch/ovs/blob/master/tutorial/ovn/env3/packet1.sh | |
952 | [env3packet2]:https://github.com/openvswitch/ovs/blob/master/tutorial/ovn/env3/packet2.sh | |
953 | [env4setup1]:https://github.com/openvswitch/ovs/blob/master/tutorial/ovn/env4/setup1.sh | |
954 | [env4setup2]:https://github.com/openvswitch/ovs/blob/master/tutorial/ovn/env4/setup2.sh | |
955 | [env4packet1]:https://github.com/openvswitch/ovs/blob/master/tutorial/ovn/env4/packet1.sh | |
956 | [env4packet2]:https://github.com/openvswitch/ovs/blob/master/tutorial/ovn/env4/packet2.sh | |
957 | [env4packet3]:https://github.com/openvswitch/ovs/blob/master/tutorial/ovn/env4/packet3.sh | |
958 | [env4packet4]:https://github.com/openvswitch/ovs/blob/master/tutorial/ovn/env4/packet4.sh | |
959 | [env4packet5]:https://github.com/openvswitch/ovs/blob/master/tutorial/ovn/env4/packet5.sh | |
960 | [env5setup]:https://github.com/openvswitch/ovs/blob/master/tutorial/ovn/env5/setup.sh | |
961 | [env5packet1]:https://github.com/openvswitch/ovs/blob/master/tutorial/ovn/env5/packet1.sh | |
962 | [env5packet2]:https://github.com/openvswitch/ovs/blob/master/tutorial/ovn/env5/packet2.sh | |
963 | [env6setup]:https://github.com/openvswitch/ovs/blob/master/tutorial/ovn/env6/setup.sh | |
964 | [env6acls]:https://github.com/openvswitch/ovs/blob/master/tutorial/ovn/env6/add-acls.sh | |
a97eef91 NS |
965 | [env7setup]:https://github.com/openvswitch/ovs/blob/master/tutorial/ovn/env7/setup.sh |
966 | [env7contports]:https://github.com/openvswitch/ovs/blob/master/tutorial/ovn/env7/add-container-ports.sh | |
967 | [env7packet1]:https://github.com/openvswitch/ovs/blob/master/tutorial/ovn/env7/packet1.sh | |
968 | [env7packet2]:https://github.com/openvswitch/ovs/blob/master/tutorial/ovn/env7/packet2.sh | |
0df6430e | 969 | [openstack-ovn-acl-blog]:http://blog.russellbryant.net/2015/10/22/openstack-security-groups-using-ovn-acls/ |
a97eef91 | 970 | [openvswitch-docker]:http://openvswitch.org/support/dist-docs/INSTALL.Docker.md.txt |