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:256
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:256
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
|