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