summaryrefslogtreecommitdiff
path: root/tutorial/OVN-Tutorial.md
blob: 5ae8ed5bedd3440da61717ca9b44dc949d3778c5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
OVN Tutorial
============

This tutorial is intended to give you a tour of the basic OVN features using
`ovs-sandbox` as a simulated test environment.  It’s assumed that you have an
understanding of OVS before going through this tutorial. Detail about OVN is
covered in [ovn-architecture(7)], but this tutorial lets you quickly see it in
action.

Getting Started
---------------

For some general information about `ovs-sandbox`, see the “Getting Started”
section of [Tutorial.md].

`ovs-sandbox` does not include OVN support by default.  To enable OVN, you must
pass the `--ovn` flag.  For example, if running it straight from the ovs git
tree you would run:

    $ make sandbox SANDBOXFLAGS="--ovn"

Running the sandbox with OVN enabled does the following additional steps to the
environment:

  1. Creates the `OVN_Northbound` and `OVN_Southbound` databases as described in
     [ovn-nb(5)] and [ovn-sb(5)].

  2. Creates a backup server for `OVN_Southbond` database. Sandbox launch
     screen provides the instructions on accessing the backup database.
     However access to the backup server is not required to go through the
     tutorial.

  3. Creates the `hardware_vtep` database as described in [vtep(5)].

  4. Runs the [ovn-northd(8)], [ovn-controller(8)], and [ovn-controller-vtep(8)]
     daemons.

  5. Makes OVN and VTEP utilities available for use in the environment,
     including [vtep-ctl(8)], [ovn-nbctl(8)], and [ovn-sbctl(8)].

Note that each of these demos assumes you start with a fresh sandbox
environment. **Re-run `ovs-sandbox` before starting each section.**

Using GDB
---------

GDB support is not required to go through the tutorial. See the “Using GDB”
section of [Tutorial.md] for more info. Additional flags exist for launching
the debugger for the OVN programs:

  --gdb-ovn-northd
  --gdb-ovn-controller
  --gdb-ovn-controller-vtep


1) Simple two-port setup
------------------------

This first environment is the simplest OVN example.  It demonstrates using OVN
with a single logical switch that has two logical ports, both residing on the
same hypervisor.

Start by running the setup script for this environment.

[View ovn/env1/setup.sh][env1setup].

    $ ovn/env1/setup.sh

You can use the `ovn-nbctl` utility to see an overview of the logical topology.

    $ ovn-nbctl show
    switch 78687d53-e037-4555-bcd3-f4f8eaf3f2aa (sw0)
        port sw0-port1
            addresses: [“00:00:00:00:00:01”]
        port sw0-port2
            addresses: [“00:00:00:00:00:02”]

The `ovn-sbctl` utility can be used to see into the state stored in the
`OVN_Southbound` database.  The `show` command shows that there is a single
chassis with two logical ports bound to it.  In a more realistic
multi-hypervisor environment, this would list all hypervisors and where all
logical ports are located.

    $ ovn-sbctl show
    Chassis “56b18105-5706-46ef-80c4-ff20979ab068”
        Encap geneve
            ip: “127.0.0.1”
        Port_Binding “sw0-port1”
        Port_Binding “sw0-port2”

OVN creates logical flows to describe how the network should behave in logical
space.  Each chassis then creates OpenFlow flows based on those logical flows
that reflect its own local view of the network.  The `ovn-sbctl` command can
show the logical flows.

    $ ovn-sbctl lflow-list
    Datapath: 2503dd42-14b1-414a-abbf-33e554e09ddc  Pipeline: ingress
      table=0 (ls_in_port_sec_l2  ), priority=100  , match=(eth.src[40]), action=(drop;)
      table=0 (ls_in_port_sec_l2  ), priority=100  , match=(vlan.present), action=(drop;)
      table=0 (ls_in_port_sec_l2  ), priority=50   , match=(inport == “sw0-port1” && eth.src == {00:00:00:00:00:01}), action=(next;)
      table=0 (ls_in_port_sec_l2  ), priority=50   , match=(inport == “sw0-port2” && eth.src == {00:00:00:00:00:02}), action=(next;)
      table=1 (ls_in_port_sec_ip  ), priority=0    , match=(1), action=(next;)
      table=2 (ls_in_port_sec_nd  ), priority=90   , match=(inport == “sw0-port1” && eth.src == 00:00:00:00:00:01 && arp.sha == 00:00:00:00:00:01), action=(next;)
      table=2 (ls_in_port_sec_nd  ), priority=90   , match=(inport == “sw0-port1” && eth.src == 00:00:00:00:00:01 && ip6 && nd && ((nd.sll == 00:00:00:00:00:00 || nd.sll == 00:00:00:00:00:01) || ((nd.tll == 00:00:00:00:00:00 || nd.tll == 00:00:00:00:00:01)))), action=(next;)
      table=2 (ls_in_port_sec_nd  ), priority=90   , match=(inport == “sw0-port2” && eth.src == 00:00:00:00:00:02 && arp.sha == 00:00:00:00:00:02), action=(next;)
      table=2 (ls_in_port_sec_nd  ), priority=90   , match=(inport == “sw0-port2” && eth.src == 00:00:00:00:00:02 && ip6 && nd && ((nd.sll == 00:00:00:00:00:00 || nd.sll == 00:00:00:00:00:02) || ((nd.tll == 00:00:00:00:00:00 || nd.tll == 00:00:00:00:00:02)))), action=(next;)
      table=2 (ls_in_port_sec_nd  ), priority=80   , match=(inport == “sw0-port1” && (arp || nd)), action=(drop;)
      table=2 (ls_in_port_sec_nd  ), priority=80   , match=(inport == “sw0-port2” && (arp || nd)), action=(drop;)
      table=2 (ls_in_port_sec_nd  ), priority=0    , match=(1), action=(next;)
      table=3 (ls_in_pre_acl      ), priority=0    , match=(1), action=(next;)
      table=4 (ls_in_pre_lb       ), priority=0    , match=(1), action=(next;)
      table=5 (ls_in_pre_stateful ), priority=100  , match=(reg0[0] == 1), action=(ct_next;)
      table=5 (ls_in_pre_stateful ), priority=0    , match=(1), action=(next;)
      table=6 (ls_in_acl          ), priority=0    , match=(1), action=(next;)
      table=7 (ls_in_lb           ), priority=0    , match=(1), action=(next;)
      table=8 (ls_in_stateful     ), priority=100  , match=(reg0[1] == 1), action=(ct_commit; next;)
      table=8 (ls_in_stateful     ), priority=100  , match=(reg0[2] == 1), action=(ct_lb;)
      table=8 (ls_in_stateful     ), priority=0    , match=(1), action=(next;)
      table=9 (ls_in_arp_rsp      ), priority=0    , match=(1), action=(next;)
      table=10(ls_in_l2_lkup      ), priority=100  , match=(eth.mcast), action=(outport = “_MC_flood”; output;)
      table=10(ls_in_l2_lkup      ), priority=50   , match=(eth.dst == 00:00:00:00:00:01), action=(outport = “sw0-port1”; output;)
      table=10(ls_in_l2_lkup      ), priority=50   , match=(eth.dst == 00:00:00:00:00:02), action=(outport = “sw0-port2”; output;)
    Datapath: 2503dd42-14b1-414a-abbf-33e554e09ddc  Pipeline: egress
      table=0 (ls_out_pre_lb      ), priority=0    , match=(1), action=(next;)
      table=1 (ls_out_pre_acl     ), priority=0    , match=(1), action=(next;)
      table=2 (ls_out_pre_stateful), priority=100  , match=(reg0[0] == 1), action=(ct_next;)
      table=2 (ls_out_pre_stateful), priority=0    , match=(1), action=(next;)
      table=3 (ls_out_lb          ), priority=0    , match=(1), action=(next;)
      table=4 (ls_out_acl         ), priority=0    , match=(1), action=(next;)
      table=5 (ls_out_stateful    ), priority=100  , match=(reg0[1] == 1), action=(ct_commit; next;)
      table=5 (ls_out_stateful    ), priority=100  , match=(reg0[2] == 1), action=(ct_lb;)
      table=5 (ls_out_stateful    ), priority=0    , match=(1), action=(next;)
      table=6 (ls_out_port_sec_ip ), priority=0    , match=(1), action=(next;)
      table=7 (ls_out_port_sec_l2 ), priority=100  , match=(eth.mcast), action=(output;)
      table=7 (ls_out_port_sec_l2 ), priority=50   , match=(outport == “sw0-port1” && eth.dst == {00:00:00:00:00:01}), action=(output;)
      table=7 (ls_out_port_sec_l2 ), priority=50   , match=(outport == “sw0-port2” && eth.dst == {00:00:00:00:00:02}), action=(output;)

Now we can start taking a closer look at how `ovn-controller` has programmed the
local switch.  Before looking at the flows, we can use `ovs-ofctl` to verify the
OpenFlow port numbers for each of the logical ports on the switch.  The output
shows that `lport1`, which corresponds with our logical port `sw0-port1`, has an
OpenFlow port number of `1`.  Similarly, `lport2` has an OpenFlow port number of
`2`.

    $ ovs-ofctl show br-int
    OFPT_FEATURES_REPLY (xid=0x2): dpid:00003e1ba878364d
    n_tables:254, n_buffers:0
    capabilities: FLOW_STATS TABLE_STATS PORT_STATS QUEUE_STATS ARP_MATCH_IP
    actions: output enqueue set_vlan_vid set_vlan_pcp strip_vlan mod_dl_src mod_dl_dst mod_nw_src mod_nw_dst mod_nw_tos mod_tp_src mod_tp_dst
     1(lport1): addr:aa:55:aa:55:00:07
         config:     PORT_DOWN
         state:      LINK_DOWN
         speed: 0 Mbps now, 0 Mbps max
     2(lport2): addr:aa:55:aa:55:00:08
         config:     PORT_DOWN
         state:      LINK_DOWN
         speed: 0 Mbps now, 0 Mbps max
     LOCAL(br-int): addr:3e:1b:a8:78:36:4d
         config:     PORT_DOWN
         state:      LINK_DOWN
         speed: 0 Mbps now, 0 Mbps max
    OFPT_GET_CONFIG_REPLY (xid=0x4): frags=normal miss_send_len=0

Finally, use `ovs-ofctl` to see the OpenFlow flows for `br-int`.  Note that some
fields have been omitted for brevity.

    $ ovs-ofctl -O OpenFlow13 dump-flows br-int
    OFPST_FLOW reply (OF1.3) (xid=0x2):
     table=0, priority=100,in_port=1 actions=set_field:0x1->metadata,set_field:0x1->reg6,resubmit(,16)
     table=0, priority=100,in_port=2 actions=set_field:0x1->metadata,set_field:0x2->reg6,resubmit(,16)
     table=16, priority=100,metadata=0x1,vlan_tci=0x1000/0x1000 actions=drop
     table=16, priority=100,metadata=0x1,dl_src=01:00:00:00:00:00/01:00:00:00:00:00 actions=drop
     table=16, priority=50,reg6=0x1,metadata=0x1,dl_src=00:00:00:00:00:01 actions=resubmit(,17)
     table=16, priority=50,reg6=0x2,metadata=0x1,dl_src=00:00:00:00:00:02 actions=resubmit(,17)
     table=17, priority=0,metadata=0x1 actions=resubmit(,18)
     table=18, priority=90,icmp6,reg6=0x2,metadata=0x1,dl_src=00:00:00:00:00:02,icmp_type=136,icmp_code=0,nd_tll=00:00:00:00:00:00 actions=resubmit(,19)
     table=18, priority=90,icmp6,reg6=0x2,metadata=0x1,dl_src=00:00:00:00:00:02,icmp_type=136,icmp_code=0,nd_tll=00:00:00:00:00:02 actions=resubmit(,19)
     table=18, priority=90,icmp6,reg6=0x1,metadata=0x1,dl_src=00:00:00:00:00:01,icmp_type=136,icmp_code=0,nd_tll=00:00:00:00:00:00 actions=resubmit(,19)
     table=18, priority=90,icmp6,reg6=0x1,metadata=0x1,dl_src=00:00:00:00:00:01,icmp_type=136,icmp_code=0,nd_tll=00:00:00:00:00:01 actions=resubmit(,19)
     table=18, priority=90,icmp6,reg6=0x1,metadata=0x1,dl_src=00:00:00:00:00:01,icmp_type=135,icmp_code=0,nd_sll=00:00:00:00:00:01 actions=resubmit(,19)
     table=18, priority=90,icmp6,reg6=0x1,metadata=0x1,dl_src=00:00:00:00:00:01,icmp_type=135,icmp_code=0,nd_sll=00:00:00:00:00:00 actions=resubmit(,19)
     table=18, priority=90,icmp6,reg6=0x2,metadata=0x1,dl_src=00:00:00:00:00:02,icmp_type=135,icmp_code=0,nd_sll=00:00:00:00:00:00 actions=resubmit(,19)
     table=18, priority=90,icmp6,reg6=0x2,metadata=0x1,dl_src=00:00:00:00:00:02,icmp_type=135,icmp_code=0,nd_sll=00:00:00:00:00:02 actions=resubmit(,19)
     table=18, priority=90,arp,reg6=0x1,metadata=0x1,dl_src=00:00:00:00:00:01,arp_sha=00:00:00:00:00:01 actions=resubmit(,19)
     table=18, priority=90,arp,reg6=0x2,metadata=0x1,dl_src=00:00:00:00:00:02,arp_sha=00:00:00:00:00:02 actions=resubmit(,19)
     table=18, priority=80,icmp6,reg6=0x2,metadata=0x1,icmp_type=136,icmp_code=0 actions=drop
     table=18, priority=80,icmp6,reg6=0x1,metadata=0x1,icmp_type=136,icmp_code=0 actions=drop
     table=18, priority=80,icmp6,reg6=0x1,metadata=0x1,icmp_type=135,icmp_code=0 actions=drop
     table=18, priority=80,icmp6,reg6=0x2,metadata=0x1,icmp_type=135,icmp_code=0 actions=drop
     table=18, priority=80,arp,reg6=0x2,metadata=0x1 actions=drop
     table=18, priority=80,arp,reg6=0x1,metadata=0x1 actions=drop
     table=18, priority=0,metadata=0x1 actions=resubmit(,19)
     table=19, priority=0,metadata=0x1 actions=resubmit(,20)
     table=20, priority=0,metadata=0x1 actions=resubmit(,21)
     table=21, priority=0,metadata=0x1 actions=resubmit(,22)
     table=22, priority=0,metadata=0x1 actions=resubmit(,23)
     table=23, priority=0,metadata=0x1 actions=resubmit(,24)
     table=24, priority=0,metadata=0x1 actions=resubmit(,25)
     table=25, priority=0,metadata=0x1 actions=resubmit(,26)
     table=26, priority=100,metadata=0x1,dl_dst=01:00:00:00:00:00/01:00:00:00:00:00 actions=set_field:0xffff->reg7,resubmit(,32)
     table=26, priority=50,metadata=0x1,dl_dst=00:00:00:00:00:01 actions=set_field:0x1->reg7,resubmit(,32)
     table=26, priority=50,metadata=0x1,dl_dst=00:00:00:00:00:02 actions=set_field:0x2->reg7,resubmit(,32)
     table=32, priority=0 actions=resubmit(,33)
     table=33, priority=100,reg7=0x1,metadata=0x1 actions=resubmit(,34)
     table=33, priority=100,reg7=0xffff,metadata=0x1 actions=set_field:0x2->reg7,resubmit(,34),set_field:0x1->reg7,resubmit(,34),set_field:0xffff->reg7
     table=33, priority=100,reg7=0x2,metadata=0x1 actions=resubmit(,34)
     table=34, priority=100,reg6=0x1,reg7=0x1,metadata=0x1 actions=drop
     table=34, priority=100,reg6=0x2,reg7=0x2,metadata=0x1 actions=drop
     table=34, priority=0 actions=set_field:0->reg0,set_field:0->reg1,set_field:0->reg2,resubmit(,48)
     table=48, priority=0,metadata=0x1 actions=resubmit(,49)
     table=49, priority=0,metadata=0x1 actions=resubmit(,50)
     table=50, priority=0,metadata=0x1 actions=resubmit(,51)
     table=51, priority=0,metadata=0x1 actions=resubmit(,52)
     table=52, priority=0,metadata=0x1 actions=resubmit(,53)
     table=53, priority=0,metadata=0x1 actions=resubmit(,54)
     table=54, priority=0,metadata=0x1 actions=resubmit(,55)
     table=55, priority=100,metadata=0x1,dl_dst=01:00:00:00:00:00/01:00:00:00:00:00 actions=resubmit(,64)
     table=55, priority=50,reg7=0x2,metadata=0x1,dl_dst=00:00:00:00:00:02 actions=resubmit(,64)
     table=55, priority=50,reg7=0x1,metadata=0x1,dl_dst=00:00:00:00:00:01 actions=resubmit(,64)
     table=64, priority=100,reg7=0x1,metadata=0x1 actions=output:1

The `ovs-appctl` command can be used to generate an OpenFlow trace of how a
packet would be processed in this configuration.  This first trace shows a
packet from `sw0-port1` to `sw0-port2`.  The packet arrives from port `1` and
should be output to port `2`.

[View ovn/env1/packet1.sh][env1packet1].

    $ ovn/env1/packet1.sh

Trace a broadcast packet from `sw0-port1`.  The packet arrives from port `1` and
should be output to port `2`.

[View ovn/env1/packet2.sh][env1packet2].

    $ ovn/env1/packet2.sh

You can extend this setup by adding additional ports.  For example, to add a
third port, run this command:

[View ovn/env1/add-third-port.sh][env1thirdport].

    $ ovn/env1/add-third-port.sh

Now if you do another trace of a broadcast packet from `sw0-port1`, you will see
that it is output to both ports `2` and `3`.

    $ ovn/env1/packet2.sh

The logical port may have an unknown set of Ethernet addresses.  When an OVN logical
switch processes a unicast Ethernet frame whose destination MAC address is not in any
logical port’s addresses column, it delivers it to the port (or ports) whose addresses
columns include unknown.

[View ovn/env1/add-unknown-ports.sh][env1unknownports].

    $ ovn/env1/add-unknown-ports.sh

This trace shows a packet from `sw0-port1` to `sw0-port4`, `sw0-port5` whose addresses
columns include unknown.  You will see that it is output to both ports `4` and `5`.

[View ovn/env1/packet3.sh][env1packet3].

    $ ovn/env1/packet3.sh

The logical port would restrict the host to sending packets from and receiving packets
to the ethernet addresses defined in the logical port’s port_security column.
In addition to the restrictions described for Ethernet addresses above, such an element
of port_security restricts the IPv4 or IPv6 addresses from which the host may send and
to which it may receive packets to the specified addresses.

[View ovn/env1/add-security-ip-ports.sh][env1securityport].

    $ ovn/env1/add-security-ip-ports.sh

This trace shows a packet from `sw0-port6` to `sw0-port7`.

[View ovn/env1/packet4.sh][env1packet4].

    $ ovn/env1/packet4.sh

2) 2 switches, 4 ports
----------------------

This environment is an extension of the last example.  The previous example
showed two ports on a single logical switch.  In this environment we add a
second logical switch that also has two ports.  This lets you start to see how
`ovn-controller` creates flows for isolated networks to co-exist on the same
switch.

[View ovn/env2/setup.sh][env2setup].

    $ ovn/env2/setup.sh

View the logical topology with `ovn-nbctl`.

    $ ovn-nbctl show
    switch e3190dc2-89d1-44ed-9308-e7077de782b3 (sw0)
        port sw0-port1
            addresses: 00:00:00:00:00:01
        port sw0-port2
            addresses: 00:00:00:00:00:02
    switch c8ed4c5f-9733-43f6-93da-795b1aabacb1 (sw1)
        port sw1-port1
            addresses: 00:00:00:00:00:03
        port sw1-port2
            addresses: 00:00:00:00:00:04

Physically, all ports reside on the same chassis.

    $ ovn-sbctl show
    Chassis “56b18105-5706-46ef-80c4-ff20979ab068”
        Encap geneve
            ip: “127.0.0.1”
        Port_Binding “sw1-port2”
        Port_Binding “sw0-port2”
        Port_Binding “sw0-port1”
        Port_Binding “sw1-port1”

OVN creates separate logical flows for each logical switch.

    $ ovn-sbctl lflow-list
    Datapath: 7ee908c1-b0d3-4d03-acc9-42cd7ef7f27d  Pipeline: ingress
      table=0 (ls_in_port_sec_l2  ), priority=100  , match=(eth.src[40]), action=(drop;)
      table=0 (ls_in_port_sec_l2  ), priority=100  , match=(vlan.present), action=(drop;)
      table=0 (ls_in_port_sec_l2  ), priority=50   , match=(inport == "sw1-port1" && eth.src == {00:00:00:00:00:03}), action=(next;)
      table=0 (ls_in_port_sec_l2  ), priority=50   , match=(inport == "sw1-port2" && eth.src == {00:00:00:00:00:04}), action=(next;)
      table=1 (ls_in_port_sec_ip  ), priority=0    , match=(1), action=(next;)
      table=2 (ls_in_port_sec_nd  ), priority=90   , match=(inport == "sw1-port1" && eth.src == 00:00:00:00:00:03 && arp.sha == 00:00:00:00:00:03), action=(next;)
      table=2 (ls_in_port_sec_nd  ), priority=90   , match=(inport == "sw1-port1" && eth.src == 00:00:00:00:00:03 && ip6 && nd && ((nd.sll == 00:00:00:00:00:00 || nd.sll == 00:00:00:00:00:03) || ((nd.tll == 00:00:00:00:00:00 || nd.tll == 00:00:00:00:00:03)))), action=(next;)
      table=2 (ls_in_port_sec_nd  ), priority=90   , match=(inport == "sw1-port2" && eth.src == 00:00:00:00:00:04 && arp.sha == 00:00:00:00:00:04), action=(next;)
      table=2 (ls_in_port_sec_nd  ), priority=90   , match=(inport == "sw1-port2" && eth.src == 00:00:00:00:00:04 && ip6 && nd && ((nd.sll == 00:00:00:00:00:00 || nd.sll == 00:00:00:00:00:04) || ((nd.tll == 00:00:00:00:00:00 || nd.tll == 00:00:00:00:00:04)))), action=(next;)
      table=2 (ls_in_port_sec_nd  ), priority=80   , match=(inport == "sw1-port1" && (arp || nd)), action=(drop;)
      table=2 (ls_in_port_sec_nd  ), priority=80   , match=(inport == "sw1-port2" && (arp || nd)), action=(drop;)
      table=2 (ls_in_port_sec_nd  ), priority=0    , match=(1), action=(next;)
      table=3 (ls_in_pre_acl      ), priority=0    , match=(1), action=(next;)
      table=4 (ls_in_pre_lb       ), priority=0    , match=(1), action=(next;)
      table=5 (ls_in_pre_stateful ), priority=100  , match=(reg0[0] == 1), action=(ct_next;)
      table=5 (ls_in_pre_stateful ), priority=0    , match=(1), action=(next;)
      table=6 (ls_in_acl          ), priority=0    , match=(1), action=(next;)
      table=7 (ls_in_lb           ), priority=0    , match=(1), action=(next;)
      table=8 (ls_in_stateful     ), priority=100  , match=(reg0[1] == 1), action=(ct_commit; next;)
      table=8 (ls_in_stateful     ), priority=100  , match=(reg0[2] == 1), action=(ct_lb;)
      table=8 (ls_in_stateful     ), priority=0    , match=(1), action=(next;)
      table=9 (ls_in_arp_rsp      ), priority=0    , match=(1), action=(next;)
      table=10(ls_in_l2_lkup      ), priority=100  , match=(eth.mcast), action=(outport = "_MC_flood"; output;)
      table=10(ls_in_l2_lkup      ), priority=50   , match=(eth.dst == 00:00:00:00:00:03), action=(outport = "sw1-port1"; output;)
      table=10(ls_in_l2_lkup      ), priority=50   , match=(eth.dst == 00:00:00:00:00:04), action=(outport = "sw1-port2"; output;)
    Datapath: 7ee908c1-b0d3-4d03-acc9-42cd7ef7f27d  Pipeline: egress
      table=0 (ls_out_pre_lb      ), priority=0    , match=(1), action=(next;)
      table=1 (ls_out_pre_acl     ), priority=0    , match=(1), action=(next;)
      table=2 (ls_out_pre_stateful), priority=100  , match=(reg0[0] == 1), action=(ct_next;)
      table=2 (ls_out_pre_stateful), priority=0    , match=(1), action=(next;)
      table=3 (ls_out_lb          ), priority=0    , match=(1), action=(next;)
      table=4 (ls_out_acl         ), priority=0    , match=(1), action=(next;)
      table=5 (ls_out_stateful    ), priority=100  , match=(reg0[1] == 1), action=(ct_commit; next;)
      table=5 (ls_out_stateful    ), priority=100  , match=(reg0[2] == 1), action=(ct_lb;)
      table=5 (ls_out_stateful    ), priority=0    , match=(1), action=(next;)
      table=6 (ls_out_port_sec_ip ), priority=0    , match=(1), action=(next;)
      table=7 (ls_out_port_sec_l2 ), priority=100  , match=(eth.mcast), action=(output;)
      table=7 (ls_out_port_sec_l2 ), priority=50   , match=(outport == "sw1-port1" && eth.dst == {00:00:00:00:00:03}), action=(output;)
      table=7 (ls_out_port_sec_l2 ), priority=50   , match=(outport == "sw1-port2" && eth.dst == {00:00:00:00:00:04}), action=(output;)
    Datapath: 9ea0c8f9-4f82-4be3-a6c7-6e6f9c2de583  Pipeline: ingress
      table=0 (ls_in_port_sec_l2  ), priority=100  , match=(eth.src[40]), action=(drop;)
      table=0 (ls_in_port_sec_l2  ), priority=100  , match=(vlan.present), action=(drop;)
      table=0 (ls_in_port_sec_l2  ), priority=50   , match=(inport == "sw0-port1" && eth.src == {00:00:00:00:00:01}), action=(next;)
      table=0 (ls_in_port_sec_l2  ), priority=50   , match=(inport == "sw0-port2" && eth.src == {00:00:00:00:00:02}), action=(next;)
      table=1 (ls_in_port_sec_ip  ), priority=0    , match=(1), action=(next;)
      table=2 (ls_in_port_sec_nd  ), priority=90   , match=(inport == "sw0-port1" && eth.src == 00:00:00:00:00:01 && arp.sha == 00:00:00:00:00:01), action=(next;)
      table=2 (ls_in_port_sec_nd  ), priority=90   , match=(inport == "sw0-port1" && eth.src == 00:00:00:00:00:01 && ip6 && nd && ((nd.sll == 00:00:00:00:00:00 || nd.sll == 00:00:00:00:00:01) || ((nd.tll == 00:00:00:00:00:00 || nd.tll == 00:00:00:00:00:01)))), action=(next;)
      table=2 (ls_in_port_sec_nd  ), priority=90   , match=(inport == "sw0-port2" && eth.src == 00:00:00:00:00:02 && arp.sha == 00:00:00:00:00:02), action=(next;)
      table=2 (ls_in_port_sec_nd  ), priority=90   , match=(inport == "sw0-port2" && eth.src == 00:00:00:00:00:02 && ip6 && nd && ((nd.sll == 00:00:00:00:00:00 || nd.sll == 00:00:00:00:00:02) || ((nd.tll == 00:00:00:00:00:00 || nd.tll == 00:00:00:00:00:02)))), action=(next;)
      table=2 (ls_in_port_sec_nd  ), priority=80   , match=(inport == "sw0-port1" && (arp || nd)), action=(drop;)
      table=2 (ls_in_port_sec_nd  ), priority=80   , match=(inport == "sw0-port2" && (arp || nd)), action=(drop;)
      table=2 (ls_in_port_sec_nd  ), priority=0    , match=(1), action=(next;)
      table=3 (ls_in_pre_acl      ), priority=0    , match=(1), action=(next;)
      table=4 (ls_in_pre_lb       ), priority=0    , match=(1), action=(next;)
      table=5 (ls_in_pre_stateful ), priority=100  , match=(reg0[0] == 1), action=(ct_next;)
      table=5 (ls_in_pre_stateful ), priority=0    , match=(1), action=(next;)
      table=6 (ls_in_acl          ), priority=0    , match=(1), action=(next;)
      table=7 (ls_in_lb           ), priority=0    , match=(1), action=(next;)
      table=8 (ls_in_stateful     ), priority=100  , match=(reg0[1] == 1), action=(ct_commit; next;)
      table=8 (ls_in_stateful     ), priority=100  , match=(reg0[2] == 1), action=(ct_lb;)
      table=8 (ls_in_stateful     ), priority=0    , match=(1), action=(next;)
      table=9 (ls_in_arp_rsp      ), priority=0    , match=(1), action=(next;)
      table=10(ls_in_l2_lkup      ), priority=100  , match=(eth.mcast), action=(outport = "_MC_flood"; output;)
      table=10(ls_in_l2_lkup      ), priority=50   , match=(eth.dst == 00:00:00:00:00:01), action=(outport = "sw0-port1"; output;)
      table=10(ls_in_l2_lkup      ), priority=50   , match=(eth.dst == 00:00:00:00:00:02), action=(outport = "sw0-port2"; output;)
    Datapath: 9ea0c8f9-4f82-4be3-a6c7-6e6f9c2de583  Pipeline: egress
      table=0 (ls_out_pre_lb      ), priority=0    , match=(1), action=(next;)
      table=1 (ls_out_pre_acl     ), priority=0    , match=(1), action=(next;)
      table=2 (ls_out_pre_stateful), priority=100  , match=(reg0[0] == 1), action=(ct_next;)
      table=2 (ls_out_pre_stateful), priority=0    , match=(1), action=(next;)
      table=3 (ls_out_lb          ), priority=0    , match=(1), action=(next;)
      table=4 (ls_out_acl         ), priority=0    , match=(1), action=(next;)
      table=5 (ls_out_stateful    ), priority=100  , match=(reg0[1] == 1), action=(ct_commit; next;)
      table=5 (ls_out_stateful    ), priority=100  , match=(reg0[2] == 1), action=(ct_lb;)
      table=5 (ls_out_stateful    ), priority=0    , match=(1), action=(next;)
      table=6 (ls_out_port_sec_ip ), priority=0    , match=(1), action=(next;)
      table=7 (ls_out_port_sec_l2 ), priority=100  , match=(eth.mcast), action=(output;)
      table=7 (ls_out_port_sec_l2 ), priority=50   , match=(outport == "sw0-port1" && eth.dst == {00:00:00:00:00:01}), action=(output;)
      table=7 (ls_out_port_sec_l2 ), priority=50   , match=(outport == "sw0-port2" && eth.dst == {00:00:00:00:00:02}), action=(output;)

In this setup, `sw0-port1` and `sw0-port2` can send packets to each other, but
not to either of the ports on `sw1`.  This first trace shows a packet from
`sw0-port1` to `sw0-port2`.  You should see th packet arrive on OpenFlow port
`1` and output to OpenFlow port `2`.

[View ovn/env2/packet1.sh][env2packet1].

    $ ovn/env2/packet1.sh

This next example shows a packet from `sw0-port1` with a destination MAC address
of `00:00:00:00:00:03`, which is the MAC address for `sw1-port1`.  Since these
ports are not on the same logical switch, the packet should just be dropped.

[View ovn/env2/packet2.sh][env2packet2].

    $ ovn/env2/packet2.sh

3) Two Hypervisors
------------------

The first two examples started by showing OVN on a single hypervisor.  A more
realistic deployment of OVN would span multiple hypervisors.  This example
creates a single logical switch with 4 logical ports.  It then simulates having
two hypervisors with two of the logical ports bound to each hypervisor.

[View ovn/env3/setup.sh][env3setup].

    $ ovn/env3/setup.sh

You can start by viewing the logical topology with `ovn-nbctl`.

    $ ovn-nbctl show
    switch b977dc03-79a5-41ba-9665-341a80e1abfd (sw0)
        port sw0-port1
            addresses: 00:00:00:00:00:01
        port sw0-port2
            addresses: 00:00:00:00:00:02
        port sw0-port4
            addresses: 00:00:00:00:00:04
        port sw0-port3
            addresses: 00:00:00:00:00:03

Using `ovn-sbctl` to view the state of the system, we can see that there are two
chassis: one local that we can interact with, and a fake remote chassis. Two
logical ports are bound to each.  Both chassis have an IP address of localhost,
but in a realistic deployment that would be the IP address used for tunnels to
that chassis.

    $ ovn-sbctl show
    Chassis “56b18105-5706-46ef-80c4-ff20979ab068”
        Encap geneve
            ip: “127.0.0.1”
        Port_Binding “sw0-port2”
        Port_Binding “sw0-port1”
    Chassis fakechassis
        Encap geneve
            ip: “127.0.0.1”
        Port_Binding “sw0-port4”
        Port_Binding “sw0-port3”

Packets between `sw0-port1` and `sw0-port2` behave just like the previous
examples.  Packets to ports on a remote chassis are the interesting part of this
example.  You may have noticed before that OVN’s logical flows are broken up
into ingress and egress tables.  Given a packet from `sw0-port1` on the local
chassis to `sw0-port3` on the remote chassis, the ingress pipeline is executed
on the local switch.  OVN then determines that it must forward the packet over a
geneve tunnel.  When it arrives at the remote chassis, the egress pipeline will
be executed there.

This first packet trace shows the first part of this example.  It’s a packet
from `sw0-port1` to `sw0-port3` from the perspective of the local chassis.
`sw0-port1` is OpenFlow port `1`.  The tunnel to the fake remote chassis is
OpenFlow port `3`.  You should see the ingress pipeline being executed and then
the packet output to port `3`, the geneve tunnel.

[View ovn/env3/packet1.sh][env3packet1].

    $ ovn/env3/packet1.sh

To simulate what would happen when that packet arrives at the remote chassis we
can flip this example around.  Consider a packet from `sw0-port3` to
`sw0-port1`.  This trace shows what would happen when that packet arrives at the
local chassis.  The packet arrives on OpenFlow port `3` (the tunnel).  You should
then see the egress pipeline get executed and the packet output to OpenFlow port
`1`.

[View ovn/env3/packet2.sh][env3packet2].

    $ ovn/env3/packet2.sh

4) Locally attached networks
----------------------------

While OVN is generally focused on the implementation of logical networks using
overlays, it’s also possible to use OVN as a control plane to manage logically
direct connectivity to networks that are locally accessible to each chassis.

This example includes two hypervisors.  Both hypervisors have two ports on them.
We want to use OVN to manage the connectivity of these ports to a network
attached to each hypervisor that we will call “physnet1”.

This scenario requires some additional configuration of `ovn-controller`.  We
must configure a mapping between `physnet1` and a local OVS bridge that provides
connectivity to that network.  We call these “bridge mappings”.  For our
example, the following script creates a bridge called `br-eth1` and then
configures `ovn-controller` with a bridge mapping from `physnet1` to `br-eth1`.

We want to create a fake second chassis and then create the topology that tells
OVN we want both ports on both hypervisors connected to `physnet1`.  The way this
is modeled in OVN is by creating a logical switch for each port.  The logical
switch has the regular VIF port and a `localnet` port.

[View ovn/env4/setup.sh][env4setup].

    $ ovn/env4/setup.sh

At this point we should be able to see that `ovn-controller` has automatically
created patch ports between `br-int` and `br-eth1`.

    $ ovs-vsctl show
    c0a06d85-d70a-4e11-9518-76a92588b34e
        Bridge "br-eth1"
            Port "patch-provnet1-1-physnet1-to-br-int"
                Interface "patch-provnet1-1-physnet1-to-br-int"
                    type: patch
                    options: {peer="patch-br-int-to-provnet1-1-physnet1"}
            Port "br-eth1"
                Interface "br-eth1"
                    type: internal
            Port "patch-provnet1-2-physnet1-to-br-int"
                Interface "patch-provnet1-2-physnet1-to-br-int"
                    type: patch
                    options: {peer="patch-br-int-to-provnet1-2-physnet1"}
        Bridge br-int
            fail_mode: secure
            Port "ovn-fakech-0"
                Interface "ovn-fakech-0"
                    type: geneve
                    options: {key=flow, remote_ip="127.0.0.1"}
            Port "patch-br-int-to-provnet1-2-physnet1"
                Interface "patch-br-int-to-provnet1-2-physnet1"
                    type: patch
                    options: {peer="patch-provnet1-2-physnet1-to-br-int"}
            Port br-int
                Interface br-int
                    type: internal
            Port "patch-br-int-to-provnet1-1-physnet1"
                Interface "patch-br-int-to-provnet1-1-physnet1"
                    type: patch
                    options: {peer="patch-provnet1-1-physnet1-to-br-int"}
            Port "lport2"
                Interface "lport2"
            Port "lport1"
                Interface "lport1


The logical topology from `ovn-nbctl` should look like this.

    $ ovn-nbctl show
        switch 9db81140-5504-4f60-be3d-2bee45b57e27 (provnet1-2)
        port provnet1-2-port1
            addresses: ["00:00:00:00:00:02"]
        port provnet1-2-physnet1
            addresses: ["unknown"]
        switch cf175cb9-35c5-41cf-8bc7-2d322cdbead0 (provnet1-3)
        port provnet1-3-physnet1
            addresses: ["unknown"]
        port provnet1-3-port1
            addresses: ["00:00:00:00:00:03"]
        switch b85f7af6-8055-4db2-ba93-efc7887cf38f (provnet1-1)
        port provnet1-1-port1
            addresses: ["00:00:00:00:00:01"]
        port provnet1-1-physnet1
            addresses: ["unknown"]
        switch 63a5e276-8807-417d-bbec-a7e907e106b1 (provnet1-4)
        port provnet1-4-port1
            addresses: ["00:00:00:00:00:04"]
        port provnet1-4-physnet1
            addresses: ["unknown"]

`port1` on each logical switch represents a regular logical port for a VIF on a
hypervisor.  `physnet1` on each logical switch is the special `localnet` port.
You can use `ovn-nbctl` to see that this port has a `type` and `options` set.

    $ ovn-nbctl lsp-get-type provnet1-1-physnet1
    localnet

    $ ovn-nbctl lsp-get-options provnet1-1-physnet1
    network_name=physnet1

The physical topology should reflect that there are two regular ports on each
chassis.

    $ ovn-sbctl show
    Chassis "56b18105-5706-46ef-80c4-ff20979ab068"
        hostname: sandbox
        Encap geneve
            ip: "127.0.0.1"
        Port_Binding "provnet1-1-port1"
        Port_Binding "provnet1-2-port1"
    Chassis fakechassis
        Encap geneve
            ip: "127.0.0.1"
        Port_Binding "provnet1-3-port1"
        Port_Binding "provnet1-4-port1"

All four of our ports should be able to communicate with each other, but they do
so through `physnet1`.  A packet from any of these ports to any destination
should be output to the OpenFlow port number that corresponds to the patch port
to `br-eth1`.

This example assumes following OpenFlow port number mappings:

* 1 = tunnel to the fake second chassis
* 2 = `lport1`, which is the logical port named `provnet1-1-port1`
* 3 = `patch-br-int-to-provnet1-1-physnet1`, patch port to `br-eth1`
* 4 = `lport2`, which is the logical port named `provnet1-2-port1`
* 5 = `patch-br-int-to-provnet1-2-physnet1`, patch port to `br-eth1`

We get those port numbers using `ovs-ofctl`:

    $ ovs-ofctl show br-int
    OFPT_FEATURES_REPLY (xid=0x2): dpid:00002a84824b0d40
    n_tables:254, n_buffers:0
    capabilities: FLOW_STATS TABLE_STATS PORT_STATS QUEUE_STATS ARP_MATCH_IP
    actions: output enqueue set_vlan_vid set_vlan_pcp strip_vlan mod_dl_src mod_dl_dst
     1(ovn-fakech-0): addr:aa:55:aa:55:00:0e
         config:     PORT_DOWN
         state:      LINK_DOWN
         speed: 0 Mbps now, 0 Mbps max
     2(lport1): addr:aa:55:aa:55:00:0f
         config:     PORT_DOWN
         state:      LINK_DOWN
         speed: 0 Mbps now, 0 Mbps max
     3(patch-br-int-to): addr:7a:6f:8a:d5:69:2a
         config:     0
         state:      0
         speed: 0 Mbps now, 0 Mbps max
     4(lport2): addr:aa:55:aa:55:00:10
         config:     PORT_DOWN
         state:      LINK_DOWN
         speed: 0 Mbps now, 0 Mbps max
     5(patch-br-int-to): addr:4a:fd:c1:11:fc:a5
         config:     0
         state:      0
         speed: 0 Mbps now, 0 Mbps max
     LOCAL(br-int): addr:2a:84:82:4b:0d:40
         config:     PORT_DOWN
         state:      LINK_DOWN
         speed: 0 Mbps now, 0 Mbps max
    OFPT_GET_CONFIG_REPLY (xid=0x4): frags=normal miss_send_len=0

This first trace shows a packet from `provnet1-1-port1` with a destination MAC
address of `provnet1-2-port1`.  We expect the packets from `lport1`(OpenFlow port 2)
to be sent out to `lport2`(OpenFlow port 4).  For example, the following topology
illustrates how the packets travel from `lport1` to `lport2`.

    `lport1` --> `patch-br-int-to-provnet1-1-physnet1`(OpenFlow port 3)
    --> `br-eth1` --> `patch-br-int-to-provnet1-2-physnet1` --> `lport2`(OpenFlow port 4)

Similarly, We expect the packets from `provnet1-2-port1` to be sent out to
`provnet1-1-port1`.  We then expect the network to handle getting the packet to its
destination.  In practice, this will be optimized at `br-eth1` and the packet won’t
actually go out and back on the network.

[View ovn/env4/packet1.sh][env4packet1].

    $ ovn/env4/packet1.sh

This next trace shows an example of a packet being sent to a destination on another
hypervisor.  The source is `provnet1-1-port1`, but the destination is `provnet1-3-port1`,
which is on the other fake chassis.  As usual, we expect the output to be to `br-eth1`
(`patch-br-int-to-provnet1-1-physnet1`, OpenFlow port 3).

[View ovn/env4/packet2.sh][env4packet2].

    $ ovn/env4/packet2.sh

This next test shows a broadcast packet.  The destination should still only be
OpenFlow port 3 and 4.

[View ovn/env4/packet3.sh][env4packet3]

    $ ovn/env4/packet3.sh

Finally, this last trace shows what happens when a broadcast packet arrives
from the network.  In this case, it simulates a broadcast that originated from a
port on the remote fake chassis and arrived at the local chassis via `br-eth1`.
We should see it output to both local ports that are attached to this network
(OpenFlow ports 2 and 4).

[View ovn/env4/packet4.sh][env4packet4]

    $ ovn/env4/packet4.sh

5) Locally attached networks with VLANs
---------------------------------------

This example is an extension of the previous one.  We take the same setup and
add two more ports to each hypervisor.  Instead of having the new ports directly
connected to `physnet1` as before, we indicate that we want them on VLAN 101 of
`physnet1`.  This shows how `localnet` ports can be used to provide connectivity
to either a flat network or a VLAN on that network.

[View ovn/env5/setup.sh][env5setup]

    $ ovn/env5/setup.sh

The logical topology shown by `ovn-nbctl` is similar to `env4`, except we now
have 8 regular VIF ports connected to `physnet1` instead of 4.  The additional 4
ports we have added are all on VLAN 101 of `physnet1`.  Note that the `localnet`
ports representing connectivity to VLAN 101 of `physnet1` have the `tag` field
set to `101`.

    $ ovn-nbctl show
        switch 3e60b940-00bf-44c6-9db6-04abf28d7e5f (provnet1-1)
        port provnet1-1-physnet1
            addresses: ["unknown"]
        port provnet1-1-port1
            addresses: ["00:00:00:00:00:01"]
        switch 87f6bea0-f74d-4f39-aa65-ca1f94670429 (provnet1-2)
        port provnet1-2-port1
            addresses: ["00:00:00:00:00:02"]
        port provnet1-2-physnet1
            addresses: ["unknown"]
        switch e6c9cb69-a056-428d-aa40-e903ce416dcd (provnet1-6-101)
        port provnet1-6-101-port1
            addresses: ["00:00:00:00:00:06"]
        port provnet1-6-physnet1-101
            parent:
            tag: 101
            addresses: ["unknown"]
        switch 5f8f72ca-6030-4f66-baea-fe6174eb54df (provnet1-4)
        port provnet1-4-port1
            addresses: ["00:00:00:00:00:04"]
        port provnet1-4-physnet1
            addresses: ["unknown"]
        switch 15d585eb-d2c1-45ea-a946-b08de0eb2f55 (provnet1-7-101)
        port provnet1-7-physnet1-101
            parent:
            tag: 101
            addresses: ["unknown"]
        port provnet1-7-101-port1
            addresses: ["00:00:00:00:00:07"]
        switch 7be4aabe-1bb0-4e16-a755-a1f6d81c1c2f (provnet1-5-101)
        port provnet1-5-101-port1
            addresses: ["00:00:00:00:00:05"]
        port provnet1-5-physnet1-101
            parent:
            tag: 101
            addresses: ["unknown"]
        switch 9bbdbf0e-50f3-4286-ba5a-29bf347531bb (provnet1-8-101)
        port provnet1-8-101-port1
            addresses: ["00:00:00:00:00:08"]
        port provnet1-8-physnet1-101
            parent:
            tag: 101
            addresses: ["unknown"]
        switch 70d053f7-2bca-4dff-96ae-bd728d3ba1d2 (provnet1-3)
        port provnet1-3-physnet1
            addresses: ["unknown"]
        port provnet1-3-port1
            addresses: ["00:00:00:00:00:03"]

The physical topology shows that we have 4 regular VIF ports on each simulated
hypervisor.

    $ ovn-sbctl show
    Chassis fakechassis
        Encap geneve
        ip: "127.0.0.1"
        Port_Binding "provnet1-3-port1"
        Port_Binding "provnet1-8-101-port1"
        Port_Binding "provnet1-7-101-port1"
        Port_Binding "provnet1-4-port1"
    Chassis "56b18105-5706-46ef-80c4-ff20979ab068"
        hostname: sandbox
        Encap geneve
        ip: "127.0.0.1"
        Port_Binding "provnet1-2-port1"
        Port_Binding "provnet1-5-101-port1"
        Port_Binding "provnet1-1-port1"
        Port_Binding "provnet1-6-101-port1"

All of the traces from the previous example, `env4`, should work in this
environment and provide the same result.  Now we can show what happens for the
ports connected to VLAN 101.  This first example shows a packet originating from
`provnet1-5-101-port1`, which is OpenFlow port 6.  We should see VLAN tag 101
pushed on the packet and then output to OpenFlow port 7, the patch port to
`br-eth1` (the bridge providing connectivity to `physnet1`), and finally arrives
on OpenFlow port 8.

[View ovn/env5/packet1.sh][env5packet1].

    $ ovn/env5/packet1.sh

If we look at a broadcast packet arriving on VLAN 101 of `physnet1`, we should
see it output to OpenFlow ports 6 and 8 only.

[View ovn/env5/packet2.sh][env5packet2].

    $ ovn/env5/packet2.sh


6) Stateful ACLs
----------------

ACLs provide a way to do distributed packet filtering for OVN networks.  One
example use of ACLs is that OpenStack Neutron uses them to implement security
groups.  ACLs are implemented using conntrack integration with OVS.

Start with a simple logical switch with 2 logical ports.

[View ovn/env6/setup.sh][env6setup].

    $ ovn/env6/setup.sh

A common use case would be the following policy applied for `sw0-port1`:

* Allow outbound IP traffic and associated return traffic.
* Allow incoming ICMP requests and associated return traffic.
* Allow incoming SSH connections and associated return traffic.
* Drop other incoming IP traffic.

The following script applies this policy to our environment.

[View ovn/env6/add-acls.sh][env6acls].

    $ ovn/env6/add-acls.sh

We can view the configured ACLs on this network using the `ovn-nbctl` command.

    $ ovn-nbctl acl-list sw0
    from-lport  1002 (inport == “sw0-port1” && ip) allow-related
      to-lport  1002 (outport == “sw0-port1” && ip && icmp) allow-related
      to-lport  1002 (outport == “sw0-port1” && ip && tcp && tcp.dst == 22) allow-related
      to-lport  1001 (outport == “sw0-port1” && ip) drop

Now that we have ACLs configured, there are new entries in the logical flow
table in the stages `switch_in_pre_acl`, switch_in_acl`, `switch_out_pre_acl`,
and `switch_out_acl`.

    $ ovn-sbctl lflow-list

Let’s look more closely at `switch_out_pre_acl` and `switch_out_acl`.

In `switch_out_pre_acl`, we match IP traffic and put it through the connection
tracker.  This populates the connection state fields so that we can apply policy
as appropriate.

    table=0(switch_out_pre_acl), priority=  100, match=(ip), action=(ct_next;)
    table=1(switch_out_pre_acl), priority=    0, match=(1), action=(next;)

In `switch_out_acl`, we allow packets associated with existing connections.  We
drop packets that are deemed to be invalid (such as non-SYN TCP packet not
associated with an existing connection).

    table=1(switch_out_acl), priority=65535, match=(!ct.est && ct.rel && !ct.new && !ct.inv), action=(next;)
    table=1(switch_out_acl), priority=65535, match=(ct.est && !ct.rel && !ct.new && !ct.inv), action=(next;)
    table=1(switch_out_acl), priority=65535, match=(ct.inv), action=(drop;)

For new connections, we apply our configured ACL policy to decide whether to
allow the connection or not.  In this case, we’ll allow ICMP or SSH.  Otherwise,
we’ll drop the packet.

    table=1(switch_out_acl), priority= 2002, match=(ct.new && (outport == “sw0-port1” && ip && icmp)), action=(ct_commit; next;)
    table=1(switch_out_acl), priority= 2002, match=(ct.new && (outport == “sw0-port1” && ip && tcp && tcp.dst == 22)), action=(ct_commit; next;)
    table=1(switch_out_acl), priority= 2001, match=(outport == “sw0-port1” && ip), action=(drop;)

When using ACLs, the default policy is to allow and track IP connections.  Based
on our above policy, IP traffic directed at `sw0-port1` will never hit this flow
at priority 1.

    table=1(switch_out_acl), priority=    1, match=(ip), action=(ct_commit; next;)
    table=1(switch_out_acl), priority=    0, match=(1), action=(next;)

Note that conntrack integration is not yet supported in ovs-sandbox, so the
OpenFlow flows will not represent what you’d see in a real environment.  The
logical flows described above give a very good idea of what the flows look like,
though.

[This blog post][openstack-ovn-acl-blog] discusses OVN ACLs from an OpenStack
perspective and also provides an example of what the resulting OpenFlow flows
look like.

7) Container Ports
------------------

OVN supports containers running directly on the hypervisors and running
containers inside VMs. This example shows how OVN supports network
virtualization to containers when run inside VMs. Details about how to use
docker containers in OVS can be found [here][openvswitch-docker].

To support container traffic created inside a VM and to distinguish network
traffic coming from different container vifs, for each container a logical
port needs to be created with parent name set to the VM's logical port and
the tag set to the vlan tag of the container vif.

Start with a simple logical switch with 3 logical ports.

[View ovn/env7/setup.sh][env7setup].

    $ ovn/env7/setup.sh

Lets create a container vif attached to the logical port 'sw0-port1' and
another container vif attached to the logical port 'sw0-port2'.

[View ovn/env7/add-container-ports.sh][env7contports]

    $ ovn/env7/add-container-ports.sh

Run the `ovn-nbctl` command to see the logical ports

    $ovn-nbctl show


As you can see a logical port 'csw0-cport1' is created on a logical
switch 'csw0' whose parent is 'sw0-port1' and it has tag set to 42.
And a logical port 'csw0-cport2' is created on the logical switch 'csw0'
whose parent is 'sw0-port2' and it has tag set to 43.

Bridge 'br-vmport1' represents the ovs bridge running inside the VM
connected to the logical port 'sw0-port1'. In this tutorial the ovs port
to 'sw0-port1' is created as a patch port with its peer connected to the
ovs bridge 'br-vmport1'. An ovs port 'cport1' is added to 'br-vmport1'
which represents the container interface connected to the ovs bridge
and vlan tag set to 42. Similarly 'br-vmport2' represents the ovs bridge
for the logical port 'sw0-port2' and 'cport2' connected to 'br-vmport2'
with vlan tag set to 43.

This first trace shows a packet from 'csw0-port1' with a destination mac
address of 'csw0-port2'. You can see ovs bridge of the vm 'br-vmport1' tags
the traffic with vlan id 42 and the traffic reaches to the br-int because
of the patch port. As you can see below `ovn-controller` has added a flow
to strip the vlan tag and set the reg6 and metadata appropriately.

    $ ovs-ofctl -O OpenFlow13 dump-flows br-int
    OFPST_FLOW reply (OF1.3) (xid=0x2):
    cookie=0x0, duration=2767.032s, table=0, n_packets=0, n_bytes=0, priority=150,in_port=3,dl_vlan=42 actions=pop_vlan,set_field:0x3->reg5,set_field:0x2->metadata,set_field:0x1->reg6,resubmit(,16)
    cookie=0x0, duration=2767.002s, table=0, n_packets=0, n_bytes=0, priority=150,in_port=4,dl_vlan=43 actions=pop_vlan,set_field:0x4->reg5,set_field:0x2->metadata,set_field:0x2->reg6,resubmit(,16)
    cookie=0x0, duration=2767.032s, table=0, n_packets=0, n_bytes=0, priority=100,in_port=3 actions=set_field:0x1->reg5,set_field:0x1->metadata,set_field:0x1->reg6,resubmit(,16)
    cookie=0x0, duration=2767.001s, table=0, n_packets=0, n_bytes=0, priority=100,in_port=4 actions=set_field:0x2->reg5,set_field:0x1->metadata,set_field:0x2->reg6,resubmit(,16)

[View ovn/env7/packet1.sh][env7packet1].

    $ ovn/env5/packet1.sh


The second trace shows a packet from 'csw0-port2' to 'csw0-port1'.

[View ovn/env7/packet2.sh][env7packet2].

    $ ovn/env5/packet1.sh

You can extend this setup by adding additional container ports with two
hypervisors. Please see the tutorial 3 above.

8) L2Gateway Ports
------------------

L2Gateway provides a way to connect logical switch ports of type `l2gateway` to
a physical network.  The difference between `l2gateway` ports and `localnet` ports
is that an `l2gateway` port is bound to a specific chassis.  A single chassis
serves as the L2 gateway to the physical network and all traffic between
chassis continues to go over geneve tunnels.

Start with a simple logical switch with 3 logical ports.

[View ovn/env8/setup.sh][env8setup].

    $ ovn/env8/setup.sh

This first example shows a packet originating from `lport1`, which is OpenFlow port 1.
We expect all packets from `lport1` to be sent out to br-eth1 (`patch-br-int-to-sw0-port3`,
OpenFlow port 3).  The patch port to br-eth1 provides connectivity to the physical network.

[View ovn/env8/packet1.sh][env8packet1].

    $ ovn/env8/packet1.sh

The last trace shows what happens when a broadcast packet arrives from the network.
In this case, it simulates a broadcast that originated from a port on the physical network
and arrived at the local chassis via br-eth1. We should see it output to the local port `lport1`
and `lport2`.

[View ovn/env8/packet2.sh][env8packet2].

    $ ovn/env8/packet2.sh

[ovn-architecture(7)]:http://openvswitch.org/support/dist-docs/ovn-architecture.7.html
[Tutorial.md]:https://github.com/openvswitch/ovs/blob/master/tutorial/Tutorial.md
[ovn-nb(5)]:http://openvswitch.org/support/dist-docs/ovn-nb.5.html
[ovn-sb(5)]:http://openvswitch.org/support/dist-docs/ovn-sb.5.html
[vtep(5)]:http://openvswitch.org/support/dist-docs/vtep.5.html
[ovn-northd(8)]:http://openvswitch.org/support/dist-docs/ovn-northd.8.html
[ovn-controller(8)]:http://openvswitch.org/support/dist-docs/ovn-controller.8.html
[ovn-controller-vtep(8)]:http://openvswitch.org/support/dist-docs/ovn-controller-vtep.8.html
[vtep-ctl(8)]:http://openvswitch.org/support/dist-docs/vtep-ctl.8.html
[ovn-nbctl(8)]:http://openvswitch.org/support/dist-docs/ovn-nbctl.8.html
[ovn-sbctl(8)]:http://openvswitch.org/support/dist-docs/ovn-sbctl.8.html
[env1setup]:https://github.com/openvswitch/ovs/blob/master/tutorial/ovn/env1/setup.sh
[env1packet1]:https://github.com/openvswitch/ovs/blob/master/tutorial/ovn/env1/packet1.sh
[env1packet2]:https://github.com/openvswitch/ovs/blob/master/tutorial/ovn/env1/packet2.sh
[env1thirdport]:https://github.com/openvswitch/ovs/blob/master/tutorial/ovn/env1/add-third-port.sh
[env1unknownports]:https://github.com/nickcooper-zhangtonghao/ovs/blob/master/tutorial/ovn/env1/add-unknown-ports.sh
[env1securityport]:https://github.com/nickcooper-zhangtonghao/ovs/blob/master/tutorial/ovn/env1/add-security-ip-ports.sh
[env1packet3]:https://github.com/nickcooper-zhangtonghao/ovs/blob/master/tutorial/ovn/env1/packet3.sh
[env1packet4]:https://github.com/nickcooper-zhangtonghao/ovs/blob/master/tutorial/ovn/env1/packet4.sh
[env2setup]:https://github.com/openvswitch/ovs/blob/master/tutorial/ovn/env2/setup.sh
[env2packet1]:https://github.com/openvswitch/ovs/blob/master/tutorial/ovn/env2/packet1.sh
[env2packet2]:https://github.com/openvswitch/ovs/blob/master/tutorial/ovn/env2/packet2.sh
[env3setup]:https://github.com/openvswitch/ovs/blob/master/tutorial/ovn/env3/setup.sh
[env3packet1]:https://github.com/openvswitch/ovs/blob/master/tutorial/ovn/env3/packet1.sh
[env3packet2]:https://github.com/openvswitch/ovs/blob/master/tutorial/ovn/env3/packet2.sh
[env4setup]:https://github.com/openvswitch/ovs/blob/master/tutorial/ovn/env4/setup.sh
[env4packet1]:https://github.com/openvswitch/ovs/blob/master/tutorial/ovn/env4/packet1.sh
[env4packet2]:https://github.com/openvswitch/ovs/blob/master/tutorial/ovn/env4/packet2.sh
[env4packet3]:https://github.com/openvswitch/ovs/blob/master/tutorial/ovn/env4/packet3.sh
[env4packet4]:https://github.com/openvswitch/ovs/blob/master/tutorial/ovn/env4/packet4.sh
[env5setup]:https://github.com/openvswitch/ovs/blob/master/tutorial/ovn/env5/setup.sh
[env5packet1]:https://github.com/openvswitch/ovs/blob/master/tutorial/ovn/env5/packet1.sh
[env5packet2]:https://github.com/openvswitch/ovs/blob/master/tutorial/ovn/env5/packet2.sh
[env6setup]:https://github.com/openvswitch/ovs/blob/master/tutorial/ovn/env6/setup.sh
[env6acls]:https://github.com/openvswitch/ovs/blob/master/tutorial/ovn/env6/add-acls.sh
[env7setup]:https://github.com/openvswitch/ovs/blob/master/tutorial/ovn/env7/setup.sh
[env7contports]:https://github.com/openvswitch/ovs/blob/master/tutorial/ovn/env7/add-container-ports.sh
[env7packet1]:https://github.com/openvswitch/ovs/blob/master/tutorial/ovn/env7/packet1.sh
[env7packet2]:https://github.com/openvswitch/ovs/blob/master/tutorial/ovn/env7/packet2.sh
[env8setup]:https://github.com/nickcooper-zhangtonghao/ovs/blob/master/tutorial/ovn/env8/setup.sh
[env8packet1]:https://github.com/nickcooper-zhangtonghao/ovs/blob/master/tutorial/ovn/env8/packet1.sh
[env8packet2]:https://github.com/nickcooper-zhangtonghao/ovs/blob/master/tutorial/ovn/env8/packet2.sh
[openstack-ovn-acl-blog]:http://blog.russellbryant.net/2015/10/22/openstack-security-groups-using-ovn-acls/
[openvswitch-docker]:http://openvswitch.org/support/dist-docs/INSTALL.Docker.md.txt