# OVN_CHECK_PACKETS([PCAP], [EXPECTED]) # # This compares packets read from PCAP, in pcap format, to those read # from EXPECTED, which is a text file containing packets as hex # strings, one per line. If PCAP contains fewer packets than # EXPECTED, it waits up to 10 seconds for more packets to appear. # # The implementation is an m4 macro that is mostly implemented in # terms of a shell function. This reduces the size of the generated # testsuite file since the shell function is only emitted once even # when this macro is invoked many times. m4_divert_text([PREPARE_TESTS], [ovn_check_packets__ () { echo echo "checking packets in $1 against $2:" rcv_pcap=$1 rcv_text=`echo "$rcv_pcap.packets" | sed 's/\.pcap//'` exp_text=$2 exp_n=`wc -l < "$exp_text"` ovs_wait_cond () { $PYTHON "$top_srcdir/utilities/ovs-pcap.in" $rcv_pcap > $rcv_text rcv_n=`wc -l < "$rcv_text"` test $rcv_n -ge $exp_n } ovs_wait || echo "expected $exp_n packets, only received $rcv_n" sort $exp_text > expout } ]) m4_define([OVN_CHECK_PACKETS], [ovn_check_packets__ "$1" "$2" AT_CHECK([sort $rcv_text], [0], [expout])]) AT_BANNER([OVN components]) AT_SETUP([ovn -- lexer]) dnl For lines without =>, input and expected output are identical. dnl For lines with =>, input precedes => and expected output follows =>. AT_DATA([test-cases.txt], [dnl foo bar baz quuxquuxquux _abcd_ a.b.c.d a123_.456 "abc\u0020def" => "abc def" " => error("Input ends inside quoted string.")dnl " $foo $bar $baz $quuxquuxquux $_abcd_ $a.b.c.d $a123_.456 $1 => error("`$' must be followed by a valid identifier.") 1 a/*b*/c => a c a//b c => a a/**/b => a b a/*/b => a error("`/*' without matching `*/'.") a/*/**/b => a b a/b => a error("`/' is only valid as part of `//' or `/*'.") b 0 1 12345 18446744073709551615 18446744073709551616 => error("Decimal constants must be less than 2**64.") 9999999999999999999999 => error("Decimal constants must be less than 2**64.") 01 => error("Decimal constants must not have leading zeros.") 0/0 0/1 1/0 => error("Value contains unmasked 1-bits.") 1/1 128/384 1/3 1/ => error("Integer constant expected.") 1/0x123 => error("Value and mask have incompatible formats.") 0x1234 0x01234 => 0x1234 0x0 => 0 0x000 => 0 0xfedcba9876543210 0XFEDCBA9876543210 => 0xfedcba9876543210 0xfedcba9876543210fedcba9876543210 0x0000fedcba9876543210fedcba9876543210 => 0xfedcba9876543210fedcba9876543210 0x => error("Hex digits expected following 0x.") 0X => error("Hex digits expected following 0X.") 0x0/0x0 => 0/0 0x0/0x1 => 0/0x1 0x1/0x0 => error("Value contains unmasked 1-bits.") 0xffff/0x1ffff 0x. => error("Invalid syntax in hexadecimal constant.") 192.168.128.1 1.2.3.4 255.255.255.255 0.0.0.0 256.1.2.3 => error("Invalid numeric constant.") 192.168.0.0/16 192.168.0.0/255.255.0.0 => 192.168.0.0/16 192.168.0.0/255.255.255.0 => 192.168.0.0/24 192.168.0.0/255.255.0.255 192.168.0.0/255.0.0.0 => error("Value contains unmasked 1-bits.") 192.168.0.0/32 192.168.0.0/255.255.255.255 => 192.168.0.0/32 1.2.3.4:5 => 1.2.3.4 : 5 :: ::1 ff00::1234 => ff00::1234 2001:db8:85a3::8a2e:370:7334 2001:db8:85a3:0:0:8a2e:370:7334 => 2001:db8:85a3::8a2e:370:7334 2001:0db8:85a3:0000:0000:8a2e:0370:7334 => 2001:db8:85a3::8a2e:370:7334 ::ffff:192.0.2.128 ::ffff:c000:0280 => ::ffff:192.0.2.128 ::1/::1 ::1/ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff => ::1/128 ::1/128 ff00::/8 ff00::/ff00:: => ff00::/8 01:23:45:67:ab:cd 01:23:45:67:AB:CD => 01:23:45:67:ab:cd fe:dc:ba:98:76:54 FE:DC:ba:98:76:54 => fe:dc:ba:98:76:54 01:00:00:00:00:00/01:00:00:00:00:00 ff:ff:ff:ff:ff:ff/ff:ff:ff:ff:ff:ff fe:ff:ff:ff:ff:ff/ff:ff:ff:ff:ff:ff ff:ff:ff:ff:ff:ff/fe:ff:ff:ff:ff:ff => error("Value contains unmasked 1-bits.") fe:x => error("Invalid numeric constant.") 00:01:02:03:04:x => error("Invalid numeric constant.") # Test that operators are tokenized as expected, even without white space. (){}[[]]==!=<<=>>=!&&||..,;=<->--: => ( ) { } [[ ]] == != < <= > >= ! && || .. , ; = <-> -- : & => error("`&' is only valid as part of `&&'.") | => error("`|' is only valid as part of `||'.") - => error("`-' is only valid as part of `--'.") ^ => error("Invalid character `^' in input.") ]) AT_CAPTURE_FILE([input.txt]) sed 's/ =>.*//' test-cases.txt > input.txt sed 's/.* => //' test-cases.txt > expout AT_CHECK([ovstest test-ovn lex < input.txt], [0], [expout]) AT_CLEANUP dnl The OVN expression parser needs to know what fields overlap with one dnl another. This test therefore verifies that all the smaller registers dnl are defined as terms of subfields of the larger ones. dnl dnl When we add or remove registers this test needs to be updated, of course. AT_SETUP([ovn -- registers]) AT_CHECK([ovstest test-ovn dump-symtab | grep reg | sort], [0], [[reg0 = xxreg0[96..127] reg1 = xxreg0[64..95] reg2 = xxreg0[32..63] reg3 = xxreg0[0..31] reg4 = xxreg1[96..127] reg5 = xxreg1[64..95] reg6 = xxreg1[32..63] reg7 = xxreg1[0..31] reg8 = xreg4[32..63] reg9 = xreg4[0..31] xreg0 = xxreg0[64..127] xreg1 = xxreg0[0..63] xreg2 = xxreg1[64..127] xreg3 = xxreg1[0..63] xreg4 = OXM_OF_PKT_REG4 xxreg0 = NXM_NX_XXREG0 xxreg1 = NXM_NX_XXREG1 ]]) AT_CLEANUP dnl Check that the OVN conntrack field definitions are correct. AT_SETUP([ovn -- conntrack fields]) AT_CHECK([ovstest test-ovn dump-symtab | grep ^ct | sort], [0], [[ct.dnat = ct_state[7] ct.est = ct_state[1] ct.inv = ct_state[4] ct.new = ct_state[0] ct.rel = ct_state[2] ct.rpl = ct_state[3] ct.snat = ct_state[6] ct.trk = ct_state[5] ct_label = NXM_NX_CT_LABEL ct_label.blocked = ct_label[0] ct_mark = NXM_NX_CT_MARK ct_state = NXM_NX_CT_STATE ]]) AT_CLEANUP AT_SETUP([ovn -- compsition]) AT_CHECK([ovstest test-ovn composition 2], [0], [ignore]) AT_CLEANUP AT_SETUP([ovn -- expression parser]) dnl For lines without =>, input and expected output are identical. dnl For lines with =>, input precedes => and expected output follows =>. AT_DATA([test-cases.txt], [[ eth.type == 0x800 eth.type==0x800 => eth.type == 0x800 eth.type[0..15] == 0x800 => eth.type == 0x800 vlan.present vlan.present == 1 => vlan.present !(vlan.present == 0) => vlan.present !(vlan.present != 1) => vlan.present !vlan.present vlan.present == 0 => !vlan.present vlan.present != 1 => !vlan.present !(vlan.present == 1) => !vlan.present !(vlan.present != 0) => !vlan.present eth.dst[0] eth.dst[0] == 1 => eth.dst[0] eth.dst[0] != 0 => eth.dst[0] !(eth.dst[0] == 0) => eth.dst[0] !(eth.dst[0] != 1) => eth.dst[0] !eth.dst[0] eth.dst[0] == 0 => !eth.dst[0] eth.dst[0] != 1 => !eth.dst[0] !(eth.dst[0] == 1) => !eth.dst[0] !(eth.dst[0] != 0) => !eth.dst[0] vlan.tci[12..15] == 0x3 vlan.tci == 0x3000/0xf000 => vlan.tci[12..15] == 0x3 vlan.tci[12..15] != 0x3 vlan.tci != 0x3000/0xf000 => vlan.tci[12..15] != 0x3 !vlan.pcp => vlan.pcp == 0 !(vlan.pcp) => vlan.pcp == 0 vlan.pcp == 0x4 vlan.pcp != 0x4 vlan.pcp > 0x4 vlan.pcp >= 0x4 vlan.pcp < 0x4 vlan.pcp <= 0x4 !(vlan.pcp != 0x4) => vlan.pcp == 0x4 !(vlan.pcp == 0x4) => vlan.pcp != 0x4 !(vlan.pcp <= 0x4) => vlan.pcp > 0x4 !(vlan.pcp < 0x4) => vlan.pcp >= 0x4 !(vlan.pcp >= 0x4) => vlan.pcp < 0x4 !(vlan.pcp > 0x4) => vlan.pcp <= 0x4 0x4 == vlan.pcp => vlan.pcp == 0x4 0x4 != vlan.pcp => vlan.pcp != 0x4 0x4 < vlan.pcp => vlan.pcp > 0x4 0x4 <= vlan.pcp => vlan.pcp >= 0x4 0x4 > vlan.pcp => vlan.pcp < 0x4 0x4 >= vlan.pcp => vlan.pcp <= 0x4 !(0x4 != vlan.pcp) => vlan.pcp == 0x4 !(0x4 == vlan.pcp) => vlan.pcp != 0x4 !(0x4 >= vlan.pcp) => vlan.pcp > 0x4 !(0x4 > vlan.pcp) => vlan.pcp >= 0x4 !(0x4 <= vlan.pcp) => vlan.pcp < 0x4 !(0x4 < vlan.pcp) => vlan.pcp <= 0x4 1 < vlan.pcp < 4 => vlan.pcp > 0x1 && vlan.pcp < 0x4 1 <= vlan.pcp <= 4 => vlan.pcp >= 0x1 && vlan.pcp <= 0x4 1 < vlan.pcp <= 4 => vlan.pcp > 0x1 && vlan.pcp <= 0x4 1 <= vlan.pcp < 4 => vlan.pcp >= 0x1 && vlan.pcp < 0x4 1 <= vlan.pcp <= 4 => vlan.pcp >= 0x1 && vlan.pcp <= 0x4 4 > vlan.pcp > 1 => vlan.pcp < 0x4 && vlan.pcp > 0x1 4 >= vlan.pcp > 1 => vlan.pcp <= 0x4 && vlan.pcp > 0x1 4 > vlan.pcp >= 1 => vlan.pcp < 0x4 && vlan.pcp >= 0x1 4 >= vlan.pcp >= 1 => vlan.pcp <= 0x4 && vlan.pcp >= 0x1 !(1 < vlan.pcp < 4) => vlan.pcp <= 0x1 || vlan.pcp >= 0x4 !(1 <= vlan.pcp <= 4) => vlan.pcp < 0x1 || vlan.pcp > 0x4 !(1 < vlan.pcp <= 4) => vlan.pcp <= 0x1 || vlan.pcp > 0x4 !(1 <= vlan.pcp < 4) => vlan.pcp < 0x1 || vlan.pcp >= 0x4 !(1 <= vlan.pcp <= 4) => vlan.pcp < 0x1 || vlan.pcp > 0x4 !(4 > vlan.pcp > 1) => vlan.pcp >= 0x4 || vlan.pcp <= 0x1 !(4 >= vlan.pcp > 1) => vlan.pcp > 0x4 || vlan.pcp <= 0x1 !(4 > vlan.pcp >= 1) => vlan.pcp >= 0x4 || vlan.pcp < 0x1 !(4 >= vlan.pcp >= 1) => vlan.pcp > 0x4 || vlan.pcp < 0x1 vlan.pcp == {1, 2, 3, 4} => vlan.pcp == 0x1 || vlan.pcp == 0x2 || vlan.pcp == 0x3 || vlan.pcp == 0x4 vlan.pcp == 1 || ((vlan.pcp == 2 || vlan.pcp == 3) || vlan.pcp == 4) => vlan.pcp == 0x1 || vlan.pcp == 0x2 || vlan.pcp == 0x3 || vlan.pcp == 0x4 vlan.pcp != {1, 2, 3, 4} => vlan.pcp != 0x1 && vlan.pcp != 0x2 && vlan.pcp != 0x3 && vlan.pcp != 0x4 vlan.pcp == 1 && ((vlan.pcp == 2 && vlan.pcp == 3) && vlan.pcp == 4) => vlan.pcp == 0x1 && vlan.pcp == 0x2 && vlan.pcp == 0x3 && vlan.pcp == 0x4 vlan.pcp == 1 && !((vlan.pcp == 2 && vlan.pcp == 3) && vlan.pcp == 4) => vlan.pcp == 0x1 && (vlan.pcp != 0x2 || vlan.pcp != 0x3 || vlan.pcp != 0x4) vlan.pcp == 1 && (!(vlan.pcp == 2 && vlan.pcp == 3) && vlan.pcp == 4) => vlan.pcp == 0x1 && (vlan.pcp != 0x2 || vlan.pcp != 0x3) && vlan.pcp == 0x4 vlan.pcp == 1 && !(!(vlan.pcp == 2 && vlan.pcp == 3) && vlan.pcp == 4) => vlan.pcp == 0x1 && ((vlan.pcp == 0x2 && vlan.pcp == 0x3) || vlan.pcp != 0x4) ip4.src == {10.0.0.0/8, 192.168.0.0/16, 172.16.20.0/24, 8.8.8.8} => ip4.src[24..31] == 0xa || ip4.src[16..31] == 0xc0a8 || ip4.src[8..31] == 0xac1014 || ip4.src == 0x8080808 ip6.src == ::1 => ip6.src == 0x1 ip4.src == 1.2.3.4 => ip4.src == 0x1020304 ip4.src == ::1.2.3.4/::ffff:ffff => ip4.src == 0x1020304 ip6.src == ::1 => ip6.src == 0x1 1 0 !1 => 0 !0 => 1 inport == "eth0" !(inport != "eth0") => inport == "eth0" ip4.src == "eth0" => Integer field ip4.src is not compatible with string constant. inport == 1 => String field inport is not compatible with integer constant. ip4.src = 1.2.3.4 => Syntax error at `=' expecting relational operator. ip4.src > {1, 2, 3} => Only == and != operators may be used with value sets. eth.type > 0x800 => Only == and != operators may be used with nominal field eth.type. vlan.present > 0 => Only == and != operators may be used with Boolean field vlan.present. inport != "eth0" => Nominal field inport may only be tested for equality (taking enclosing `!' operators into account). !(inport == "eth0") => Nominal field inport may only be tested for equality (taking enclosing `!' operators into account). eth.type != 0x800 => Nominal field eth.type may only be tested for equality (taking enclosing `!' operators into account). !(eth.type == 0x800) => Nominal field eth.type may only be tested for equality (taking enclosing `!' operators into account). inport = "eth0" => Syntax error at `=' expecting relational operator. 123 == 123 => Syntax error at `123' expecting field name. $name => Syntax error at `$name' expecting address set name. 123 == xyzzy => Syntax error at `xyzzy' expecting field name. xyzzy == 1 => Syntax error at `xyzzy' expecting field name. inport[1] == 1 => Cannot select subfield of string field inport. eth.type[] == 1 => Syntax error at `@:>@' expecting small integer. eth.type[::1] == 1 => Syntax error at `::1' expecting small integer. eth.type[18446744073709551615] == 1 => Syntax error at `18446744073709551615' expecting small integer. eth.type[5!] => Syntax error at `!' expecting `@:>@'. eth.type[5..1] => Invalid bit range 5 to 1. eth.type[12..16] => Cannot select bits 12 to 16 of 16-bit field eth.type. eth.type[10] == 1 => Cannot select subfield of nominal field eth.type. eth.type => Explicit `!= 0' is required for inequality test of multibit field against 0. !(!(vlan.pcp)) => Explicit `!= 0' is required for inequality test of multibit field against 0. 123 => Syntax error at end of input expecting relational operator. 123 x => Syntax error at `x' expecting relational operator. {1, "eth0"} => Syntax error at `"eth0"' expecting integer. eth.type == xyzzy => Syntax error at `xyzzy' expecting constant. (1 x) => Syntax error at `x' expecting `)'. !0x800 != eth.type => Missing parentheses around operand of !. eth.type == 0x800 || eth.type == 0x86dd && ip.proto == 17 => && and || must be parenthesized when used together. eth.dst == {} => Syntax error at `}' expecting constant. eth.src > 00:00:00:00:11:11/00:00:00:00:ff:ff => Only == and != operators may be used with masked constants. Consider using subfields instead (e.g. eth.src[0..15] > 0x1111 in place of eth.src > 00:00:00:00:11:11/00:00:00:00:ff:ff). ip4.src == ::1 => 128-bit constant is not compatible with 32-bit field ip4.src. 1 == eth.type == 2 => Range expressions must have the form `x < field < y' or `x > field > y', with each `<' optionally replaced by `<=' or `>' by `>='). eth.dst[40] x => Syntax error at `x' expecting end of input. ip4.src == {1.2.3.4, $set1, $unknownset} => Syntax error at `$unknownset' expecting address set name. eth.src == {$set3, badmac, 00:00:00:00:00:01} => Syntax error at `badmac' expecting constant. ]]) sed 's/ =>.*//' test-cases.txt > input.txt sed 's/.* => //' test-cases.txt > expout AT_CHECK([ovstest test-ovn parse-expr < input.txt], [0], [expout]) AT_CLEANUP AT_SETUP([ovn -- expression annotation]) dnl Input precedes =>, expected output follows =>. AT_DATA([test-cases.txt], [[ ip4.src == 1.2.3.4 => ip4.src == 0x1020304 && eth.type == 0x800 ip4.src != 1.2.3.4 => ip4.src != 0x1020304 && eth.type == 0x800 ip.proto == 123 => ip.proto == 0x7b && (eth.type == 0x800 || eth.type == 0x86dd) ip.proto == {123, 234} => (ip.proto == 0x7b && (eth.type == 0x800 || eth.type == 0x86dd)) || (ip.proto == 0xea && (eth.type == 0x800 || eth.type == 0x86dd)) ip4.src == 1.2.3.4 && ip4.dst == 5.6.7.8 => ip4.src == 0x1020304 && eth.type == 0x800 && ip4.dst == 0x5060708 && eth.type == 0x800 ip => eth.type == 0x800 || eth.type == 0x86dd ip == 1 => eth.type == 0x800 || eth.type == 0x86dd ip[0] == 1 => eth.type == 0x800 || eth.type == 0x86dd ip > 0 => Only == and != operators may be used with nominal field ip. !ip => Nominal predicate ip may only be tested positively, e.g. `ip' or `ip == 1' but not `!ip' or `ip == 0'. ip == 0 => Nominal predicate ip may only be tested positively, e.g. `ip' or `ip == 1' but not `!ip' or `ip == 0'. vlan.present => vlan.tci[12] !vlan.present => !vlan.tci[12] !vlan.pcp => vlan.tci[13..15] == 0 && vlan.tci[12] vlan.pcp == 1 && vlan.vid == 2 => vlan.tci[13..15] == 0x1 && vlan.tci[12] && vlan.tci[0..11] == 0x2 && vlan.tci[12] !reg0 && !reg1 && !reg2 && !reg3 => xxreg0[96..127] == 0 && xxreg0[64..95] == 0 && xxreg0[32..63] == 0 && xxreg0[0..31] == 0 ip.first_frag => ip.frag[0] && (eth.type == 0x800 || eth.type == 0x86dd) && (!ip.frag[1] || (eth.type != 0x800 && eth.type != 0x86dd)) !ip.first_frag => !ip.frag[0] || (eth.type != 0x800 && eth.type != 0x86dd) || (ip.frag[1] && (eth.type == 0x800 || eth.type == 0x86dd)) ip.later_frag => ip.frag[1] && (eth.type == 0x800 || eth.type == 0x86dd) bad_prereq != 0 => Error parsing expression `xyzzy' encountered as prerequisite or predicate of initial expression: Syntax error at `xyzzy' expecting field name. self_recurse != 0 => Error parsing expression `self_recurse != 0' encountered as prerequisite or predicate of initial expression: Recursive expansion of symbol `self_recurse'. mutual_recurse_1 != 0 => Error parsing expression `mutual_recurse_2 != 0' encountered as prerequisite or predicate of initial expression: Error parsing expression `mutual_recurse_1 != 0' encountered as prerequisite or predicate of initial expression: Recursive expansion of symbol `mutual_recurse_1'. mutual_recurse_2 != 0 => Error parsing expression `mutual_recurse_1 != 0' encountered as prerequisite or predicate of initial expression: Error parsing expression `mutual_recurse_2 != 0' encountered as prerequisite or predicate of initial expression: Recursive expansion of symbol `mutual_recurse_2'. ]]) sed 's/ =>.*//' test-cases.txt > input.txt sed 's/.* => //' test-cases.txt > expout AT_CHECK([ovstest test-ovn annotate-expr < input.txt], [0], [expout]) AT_CLEANUP AT_SETUP([ovn -- 1-term expression conversion]) AT_CHECK([ovstest test-ovn exhaustive --operation=convert 1], [0], [Tested converting all 1-terminal expressions with 2 numeric vars (each 3 bits) in terms of operators == != < <= > >= and 2 string vars. ]) AT_CLEANUP AT_SETUP([ovn -- 2-term expression conversion]) AT_CHECK([ovstest test-ovn exhaustive --operation=convert 2], [0], [Tested converting 578 expressions of 2 terminals with 2 numeric vars (each 3 bits) in terms of operators == != < <= > >= and 2 string vars. ]) AT_CLEANUP AT_SETUP([ovn -- 3-term expression conversion]) AT_CHECK([ovstest test-ovn exhaustive --operation=convert --bits=2 3], [0], [Tested converting 67410 expressions of 3 terminals with 2 numeric vars (each 2 bits) in terms of operators == != < <= > >= and 2 string vars. ]) AT_CLEANUP AT_SETUP([ovn -- 3-term numeric expression simplification]) AT_CHECK([ovstest test-ovn exhaustive --operation=simplify --nvars=2 --svars=0 3], [0], [Tested simplifying 490770 expressions of 3 terminals with 2 numeric vars (each 3 bits) in terms of operators == != < <= > >=. ]) AT_CLEANUP AT_SETUP([ovn -- 4-term string expression simplification]) AT_CHECK([ovstest test-ovn exhaustive --operation=simplify --nvars=0 --svars=4 4], [0], [Tested simplifying 21978 expressions of 4 terminals with 4 string vars. ]) AT_CLEANUP AT_SETUP([ovn -- 3-term mixed expression simplification]) AT_CHECK([ovstest test-ovn exhaustive --operation=simplify --nvars=1 --svars=1 3], [0], [Tested simplifying 127890 expressions of 3 terminals with 1 numeric vars (each 3 bits) in terms of operators == != < <= > >= and 1 string vars. ]) AT_CLEANUP AT_SETUP([ovn -- simplification special cases]) simplify() { echo "$1" | ovstest test-ovn simplify-expr } AT_CHECK([simplify 'eth.dst == 0/0'], [0], [1 ]) AT_CHECK([simplify 'eth.dst != 0/0'], [0], [0 ]) AT_CHECK([simplify 'tcp.dst >= 0'], [0], [ip.proto == 0x6 && (eth.type == 0x800 || eth.type == 0x86dd) ]) AT_CHECK([simplify 'tcp.dst <= 65535'], [0], [ip.proto == 0x6 && (eth.type == 0x800 || eth.type == 0x86dd) ]) AT_CHECK([simplify 'tcp.dst > 0'], [0], [[(tcp.dst[0] || tcp.dst[1] || tcp.dst[2] || tcp.dst[3] || tcp.dst[4] || tcp.dst[5] || tcp.dst[6] || tcp.dst[7] || tcp.dst[8] || tcp.dst[9] || tcp.dst[10] || tcp.dst[11] || tcp.dst[12] || tcp.dst[13] || tcp.dst[14] || tcp.dst[15]) && ip.proto == 0x6 && (eth.type == 0x800 || eth.type == 0x86dd) ]]) AT_CHECK([simplify 'tcp.dst < 65535'], [0], [[(!tcp.dst[0] || !tcp.dst[1] || !tcp.dst[2] || !tcp.dst[3] || !tcp.dst[4] || !tcp.dst[5] || !tcp.dst[6] || !tcp.dst[7] || !tcp.dst[8] || !tcp.dst[9] || !tcp.dst[10] || !tcp.dst[11] || !tcp.dst[12] || !tcp.dst[13] || !tcp.dst[14] || !tcp.dst[15]) && ip.proto == 0x6 && (eth.type == 0x800 || eth.type == 0x86dd) ]]) AT_CLEANUP AT_SETUP([ovn -- is_chassis_resident simplification]) simplify() { echo "$1" | ovstest test-ovn simplify-expr } AT_CHECK([simplify 'is_chassis_resident("eth1")'], [0], [1 ]) AT_CHECK([simplify 'is_chassis_resident("eth2")'], [0], [0 ]) AT_CHECK([simplify '!is_chassis_resident("eth1")'], [0], [0 ]) AT_CHECK([simplify '!is_chassis_resident("eth2")'], [0], [1 ]) AT_CLEANUP AT_SETUP([ovn -- 4-term numeric expression normalization]) AT_CHECK([ovstest test-ovn exhaustive --operation=normalize --nvars=3 --svars=0 --bits=1 4], [0], [Tested normalizing 1874026 expressions of 4 terminals with 3 numeric vars (each 1 bits) in terms of operators == != < <= > >=. ]) AT_CLEANUP AT_SETUP([ovn -- 4-term string expression normalization]) AT_CHECK([ovstest test-ovn exhaustive --operation=normalize --nvars=0 --svars=3 --bits=1 4], [0], [Tested normalizing 11242 expressions of 4 terminals with 3 string vars. ]) AT_CLEANUP AT_SETUP([ovn -- 4-term mixed expression normalization]) AT_CHECK([ovstest test-ovn exhaustive --operation=normalize --nvars=1 --bits=1 --svars=2 4], [0], [Tested normalizing 175978 expressions of 4 terminals with 1 numeric vars (each 1 bits) in terms of operators == != < <= > >= and 2 string vars. ]) AT_CLEANUP AT_SETUP([ovn -- 5-term numeric expression normalization]) AT_CHECK([ovstest test-ovn exhaustive --operation=normalize --nvars=3 --svars=0 --bits=1 --relops='==' 5], [0], [Tested normalizing 1317600 expressions of 5 terminals with 3 numeric vars (each 1 bits) in terms of operators ==. ]) AT_CLEANUP AT_SETUP([ovn -- 5-term string expression normalization]) AT_CHECK([ovstest test-ovn exhaustive --operation=normalize --nvars=0 --svars=3 --bits=1 --relops='==' 5], [0], [Tested normalizing 368550 expressions of 5 terminals with 3 string vars. ]) AT_CLEANUP AT_SETUP([ovn -- 5-term mixed expression normalization]) AT_CHECK([ovstest test-ovn exhaustive --operation=normalize --nvars=1 --svars=1 --bits=1 --relops='==' 5], [0], [Tested normalizing 216000 expressions of 5 terminals with 1 numeric vars (each 1 bits) in terms of operators == and 1 string vars. ]) AT_CLEANUP AT_SETUP([ovn -- 4-term numeric expressions to flows]) AT_KEYWORDS([expression]) AT_CHECK([ovstest test-ovn exhaustive --operation=flow --nvars=2 --svars=0 --bits=2 --relops='==' 4], [0], [Tested converting to flows 175978 expressions of 4 terminals with 2 numeric vars (each 2 bits) in terms of operators ==. ]) AT_CLEANUP AT_SETUP([ovn -- 4-term string expressions to flows]) AT_KEYWORDS([expression]) AT_CHECK([ovstest test-ovn exhaustive --operation=flow --nvars=0 --svars=4 4], [0], [Tested converting to flows 21978 expressions of 4 terminals with 4 string vars. ]) AT_CLEANUP AT_SETUP([ovn -- 4-term mixed expressions to flows]) AT_KEYWORDS([expression]) AT_CHECK([ovstest test-ovn exhaustive --operation=flow --nvars=1 --bits=2 --svars=1 --relops='==' 4], [0], [Tested converting to flows 48312 expressions of 4 terminals with 1 numeric vars (each 2 bits) in terms of operators == and 1 string vars. ]) AT_CLEANUP AT_SETUP([ovn -- 3-term numeric expressions to flows]) AT_KEYWORDS([expression]) AT_CHECK([ovstest test-ovn exhaustive --operation=flow --nvars=3 --svars=0 --bits=3 --relops='==' 3], [0], [Tested converting to flows 41328 expressions of 3 terminals with 3 numeric vars (each 3 bits) in terms of operators ==. ]) AT_CLEANUP AT_SETUP([ovn -- converting expressions to flows -- string fields]) AT_KEYWORDS([expression]) expr_to_flow () { echo "$1" | ovstest test-ovn expr-to-flows | sort } AT_CHECK([expr_to_flow 'inport == "eth0"'], [0], [reg14=0x5 ]) AT_CHECK([expr_to_flow 'inport == "eth1"'], [0], [reg14=0x6 ]) AT_CHECK([expr_to_flow 'inport == "eth2"'], [0], [(no flows) ]) AT_CHECK([expr_to_flow 'inport == "eth0" && ip'], [0], [dnl ip,reg14=0x5 ipv6,reg14=0x5 ]) AT_CHECK([expr_to_flow 'inport == "eth1" && ip'], [0], [dnl ip,reg14=0x6 ipv6,reg14=0x6 ]) AT_CHECK([expr_to_flow 'inport == "eth2" && ip'], [0], [(no flows) ]) AT_CHECK([expr_to_flow 'inport == {"eth0", "eth1", "eth2", "LOCAL"}'], [0], [reg14=0x5 reg14=0x6 reg14=0xfffe ]) AT_CHECK([expr_to_flow 'inport == {"eth0", "eth1", "eth2"} && ip'], [0], [dnl ip,reg14=0x5 ip,reg14=0x6 ipv6,reg14=0x5 ipv6,reg14=0x6 ]) AT_CHECK([expr_to_flow 'inport == "eth0" && inport == "eth1"'], [0], [dnl (no flows) ]) AT_CLEANUP AT_SETUP([ovn -- converting expressions to flows -- address sets]) AT_KEYWORDS([expression]) expr_to_flow () { echo "$1" | ovstest test-ovn expr-to-flows | sort } AT_CHECK([expr_to_flow 'ip4.src == {10.0.0.1, 10.0.0.2, 10.0.0.3}'], [0], [dnl ip,nw_src=10.0.0.1 ip,nw_src=10.0.0.2 ip,nw_src=10.0.0.3 ]) AT_CHECK([expr_to_flow 'ip4.src == $set1'], [0], [dnl ip,nw_src=10.0.0.1 ip,nw_src=10.0.0.2 ip,nw_src=10.0.0.3 ]) AT_CHECK([expr_to_flow 'ip4.src == {1.2.3.4, $set1}'], [0], [dnl ip,nw_src=1.2.3.4 ip,nw_src=10.0.0.1 ip,nw_src=10.0.0.2 ip,nw_src=10.0.0.3 ]) AT_CHECK([expr_to_flow 'ip4.src == {1.2.0.0/20, 5.5.5.0/24, $set1}'], [0], [dnl ip,nw_src=1.2.0.0/20 ip,nw_src=10.0.0.1 ip,nw_src=10.0.0.2 ip,nw_src=10.0.0.3 ip,nw_src=5.5.5.0/24 ]) AT_CHECK([expr_to_flow 'ip6.src == {::1, ::2, ::3}'], [0], [dnl ipv6,ipv6_src=::1 ipv6,ipv6_src=::2 ipv6,ipv6_src=::3 ]) AT_CHECK([expr_to_flow 'ip6.src == {::1, $set2, ::4}'], [0], [dnl ipv6,ipv6_src=::1 ipv6,ipv6_src=::2 ipv6,ipv6_src=::3 ipv6,ipv6_src=::4 ]) AT_CHECK([expr_to_flow 'eth.src == {00:00:00:00:00:01, 00:00:00:00:00:02, 00:00:00:00:00:03}'], [0], [dnl dl_src=00:00:00:00:00:01 dl_src=00:00:00:00:00:02 dl_src=00:00:00:00:00:03 ]) AT_CHECK([expr_to_flow 'eth.src == {$set3}'], [0], [dnl dl_src=00:00:00:00:00:01 dl_src=00:00:00:00:00:02 dl_src=00:00:00:00:00:03 ]) AT_CHECK([expr_to_flow 'eth.src == {00:00:00:00:00:01, $set3, ba:be:be:ef:de:ad, $set3}'], [0], [dnl dl_src=00:00:00:00:00:01 dl_src=00:00:00:00:00:02 dl_src=00:00:00:00:00:03 dl_src=ba:be:be:ef:de:ad ]) AT_CLEANUP AT_SETUP([ovn -- action parsing]) dnl Unindented text is input (a set of OVN logical actions). dnl Indented text is expected output. AT_DATA([test-cases.txt], [[# drop drop; encodes as drop drop; next; Syntax error at `next' expecting end of input. next; drop; Syntax error at `drop' expecting action. # output output; encodes as resubmit(,64) # next next; encodes as resubmit(,19) next(11); formats as next; encodes as resubmit(,19) next(0); encodes as resubmit(,8) next(23); encodes as resubmit(,31) next(); Syntax error at `)' expecting "pipeline" or "table". next(10; Syntax error at `;' expecting `)'. next(24); "next" action cannot advance beyond table 23. next(table=11); formats as next; encodes as resubmit(,19) next(pipeline=ingress); formats as next; encodes as resubmit(,19) next(table=11, pipeline=ingress); formats as next; encodes as resubmit(,19) next(pipeline=ingress, table=11); formats as next; encodes as resubmit(,19) next(pipeline=egress); "next" action cannot advance from ingress to egress pipeline (use "output" action instead) next(table=10); formats as next(10); encodes as resubmit(,18) # Loading a constant value. tcp.dst=80; formats as tcp.dst = 80; encodes as set_field:80->tcp_dst has prereqs ip.proto == 0x6 && (eth.type == 0x800 || eth.type == 0x86dd) eth.dst[40] = 1; encodes as set_field:01:00:00:00:00:00/01:00:00:00:00:00->eth_dst vlan.pcp = 2; encodes as set_field:0x4000/0xe000->vlan_tci has prereqs vlan.tci[12] vlan.tci[13..15] = 2; encodes as set_field:0x4000/0xe000->vlan_tci inport = ""; encodes as set_field:0->reg14 ip.ttl=4; formats as ip.ttl = 4; encodes as set_field:4->nw_ttl has prereqs eth.type == 0x800 || eth.type == 0x86dd outport="eth0"; next; outport="LOCAL"; next; formats as outport = "eth0"; next; outport = "LOCAL"; next; encodes as set_field:0x5->reg15,resubmit(,19),set_field:0xfffe->reg15,resubmit(,19) inport[1] = 1; Cannot select subfield of string field inport. ip.proto[1] = 1; Cannot select subfield of nominal field ip.proto. eth.dst[40] == 1; Syntax error at `==' expecting `=' or `<->'. ip = 1; Predicate symbol ip used where lvalue required. ip.proto = 6; Field ip.proto is not modifiable. inport = {"a", "b"}; Syntax error at `{' expecting constant. inport = {}; Syntax error at `{' expecting constant. bad_prereq = 123; Error parsing expression `xyzzy' encountered as prerequisite or predicate of initial expression: Syntax error at `xyzzy' expecting field name. self_recurse = 123; Error parsing expression `self_recurse != 0' encountered as prerequisite or predicate of initial expression: Error parsing expression `self_recurse != 0' encountered as prerequisite or predicate of initial expression: Recursive expansion of symbol `self_recurse'. vlan.present = 0; Predicate symbol vlan.present used where lvalue required. # Moving one field into another. reg0=reg1; formats as reg0 = reg1; encodes as move:NXM_NX_XXREG0[64..95]->NXM_NX_XXREG0[96..127] vlan.pcp = reg0[0..2]; encodes as move:NXM_NX_XXREG0[96..98]->NXM_OF_VLAN_TCI[13..15] has prereqs vlan.tci[12] reg0[10] = vlan.pcp[1]; encodes as move:NXM_OF_VLAN_TCI[14]->NXM_NX_XXREG0[106] has prereqs vlan.tci[12] outport = inport; encodes as move:NXM_NX_REG14[]->NXM_NX_REG15[] reg0[0] = vlan.present; Predicate symbol vlan.present used where lvalue required. reg0 = reg1[0..10]; Can't assign 11-bit value to 32-bit destination. inport = reg0; Can't assign integer field (reg0) to string field (inport). inport = big_string; String fields inport and big_string are incompatible for assignment. ip.proto = reg0[0..7]; Field ip.proto is not modifiable. # Exchanging fields. reg0 <-> reg1; encodes as push:NXM_NX_XXREG0[64..95],push:NXM_NX_XXREG0[96..127],pop:NXM_NX_XXREG0[64..95],pop:NXM_NX_XXREG0[96..127] vlan.pcp <-> reg0[0..2]; encodes as push:NXM_NX_XXREG0[96..98],push:NXM_OF_VLAN_TCI[13..15],pop:NXM_NX_XXREG0[96..98],pop:NXM_OF_VLAN_TCI[13..15] has prereqs vlan.tci[12] reg0[10] <-> vlan.pcp[1]; encodes as push:NXM_OF_VLAN_TCI[14],push:NXM_NX_XXREG0[106],pop:NXM_OF_VLAN_TCI[14],pop:NXM_NX_XXREG0[106] has prereqs vlan.tci[12] outport <-> inport; encodes as push:NXM_NX_REG14[],push:NXM_NX_REG15[],pop:NXM_NX_REG14[],pop:NXM_NX_REG15[] reg0[0] <-> vlan.present; Predicate symbol vlan.present used where lvalue required. reg0 <-> reg1[0..10]; Can't exchange 32-bit field with 11-bit field. inport <-> reg0; Can't exchange string field (inport) with integer field (reg0). inport <-> big_string; String fields inport and big_string are incompatible for exchange. ip.proto <-> reg0[0..7]; Field ip.proto is not modifiable. reg0[0..7] <-> ip.proto; Field ip.proto is not modifiable. # TTL decrement. ip.ttl--; encodes as dec_ttl has prereqs ip ip.ttl Syntax error at end of input expecting `--'. # load balancing. ct_lb; encodes as ct(table=19,zone=NXM_NX_REG13[0..15],nat) has prereqs ip ct_lb(); formats as ct_lb; encodes as ct(table=19,zone=NXM_NX_REG13[0..15],nat) has prereqs ip ct_lb(192.168.1.2:80, 192.168.1.3:80); encodes as group:1 has prereqs ip ct_lb(192.168.1.2, 192.168.1.3, ); formats as ct_lb(192.168.1.2, 192.168.1.3); encodes as group:2 has prereqs ip ct_lb(192.168.1.2:); Syntax error at `)' expecting port number. ct_lb(192.168.1.2:123456); Syntax error at `123456' expecting port number. ct_lb(foo); Syntax error at `foo' expecting IPv4 address. # ct_next ct_next; encodes as ct(table=19,zone=NXM_NX_REG13[0..15]) has prereqs ip # ct_commit ct_commit; encodes as ct(commit,zone=NXM_NX_REG13[0..15]) has prereqs ip ct_commit(); formats as ct_commit; encodes as ct(commit,zone=NXM_NX_REG13[0..15]) has prereqs ip ct_commit(ct_mark=1); formats as ct_commit(ct_mark=0x1); encodes as ct(commit,zone=NXM_NX_REG13[0..15],exec(set_field:0x1->ct_mark)) has prereqs ip ct_commit(ct_mark=1/1); formats as ct_commit(ct_mark=0x1/0x1); encodes as ct(commit,zone=NXM_NX_REG13[0..15],exec(set_field:0x1/0x1->ct_mark)) has prereqs ip ct_commit(ct_label=1); formats as ct_commit(ct_label=0x1); encodes as ct(commit,zone=NXM_NX_REG13[0..15],exec(set_field:0x1->ct_label)) has prereqs ip ct_commit(ct_label=1/1); formats as ct_commit(ct_label=0x1/0x1); encodes as ct(commit,zone=NXM_NX_REG13[0..15],exec(set_field:0x1/0x1->ct_label)) has prereqs ip ct_commit(ct_mark=1, ct_label=2); formats as ct_commit(ct_mark=0x1, ct_label=0x2); encodes as ct(commit,zone=NXM_NX_REG13[0..15],exec(set_field:0x1->ct_mark,set_field:0x2->ct_label)) has prereqs ip ct_commit(ct_label=0x01020304050607080910111213141516); formats as ct_commit(ct_label=0x1020304050607080910111213141516); encodes as ct(commit,zone=NXM_NX_REG13[0..15],exec(set_field:0x1020304050607080910111213141516->ct_label)) has prereqs ip ct_commit(ct_label=0x181716151413121110090807060504030201); formats as ct_commit(ct_label=0x16151413121110090807060504030201); encodes as ct(commit,zone=NXM_NX_REG13[0..15],exec(set_field:0x16151413121110090807060504030201->ct_label)) has prereqs ip ct_commit(ct_label=0x1000000000000000000000000000000/0x1000000000000000000000000000000); encodes as ct(commit,zone=NXM_NX_REG13[0..15],exec(set_field:0x1000000000000000000000000000000/0x1000000000000000000000000000000->ct_label)) has prereqs ip ct_commit(ct_label=18446744073709551615); formats as ct_commit(ct_label=0xffffffffffffffff); encodes as ct(commit,zone=NXM_NX_REG13[0..15],exec(set_field:0xffffffffffffffff->ct_label)) has prereqs ip ct_commit(ct_label=18446744073709551616); Decimal constants must be less than 2**64. # ct_dnat ct_dnat; encodes as ct(table=19,zone=NXM_NX_REG11[0..15],nat) has prereqs ip ct_dnat(192.168.1.2); encodes as ct(commit,table=19,zone=NXM_NX_REG11[0..15],nat(dst=192.168.1.2)) has prereqs ip ct_dnat(192.168.1.2, 192.168.1.3); Syntax error at `,' expecting `)'. ct_dnat(foo); Syntax error at `foo' expecting IPv4 address. ct_dnat(foo, bar); Syntax error at `foo' expecting IPv4 address. ct_dnat(); Syntax error at `)' expecting IPv4 address. # ct_snat ct_snat; encodes as ct(table=19,zone=NXM_NX_REG12[0..15],nat) has prereqs ip ct_snat(192.168.1.2); encodes as ct(commit,table=19,zone=NXM_NX_REG12[0..15],nat(src=192.168.1.2)) has prereqs ip ct_snat(192.168.1.2, 192.168.1.3); Syntax error at `,' expecting `)'. ct_snat(foo); Syntax error at `foo' expecting IPv4 address. ct_snat(foo, bar); Syntax error at `foo' expecting IPv4 address. ct_snat(); Syntax error at `)' expecting IPv4 address. # ct_clear ct_clear; encodes as ct_clear # clone clone { ip4.dst = 255.255.255.255; output; }; next; encodes as clone(set_field:255.255.255.255->ip_dst,resubmit(,64)),resubmit(,19) has prereqs eth.type == 0x800 # arp arp { eth.dst = ff:ff:ff:ff:ff:ff; output; }; output; encodes as controller(userdata=00.00.00.00.00.00.00.00.00.19.00.10.80.00.06.06.ff.ff.ff.ff.ff.ff.00.00.ff.ff.00.10.00.00.23.20.00.0e.ff.f8.40.00.00.00),resubmit(,64) has prereqs ip4 arp { }; formats as arp { drop; }; encodes as controller(userdata=00.00.00.00.00.00.00.00) has prereqs ip4 # get_arp get_arp(outport, ip4.dst); encodes as push:NXM_NX_REG0[],push:NXM_OF_IP_DST[],pop:NXM_NX_REG0[],set_field:00:00:00:00:00:00->eth_dst,resubmit(,65),pop:NXM_NX_REG0[] has prereqs eth.type == 0x800 get_arp(inport, reg0); encodes as push:NXM_NX_REG15[],push:NXM_NX_REG0[],push:NXM_NX_XXREG0[96..127],push:NXM_NX_REG14[],pop:NXM_NX_REG15[],pop:NXM_NX_REG0[],set_field:00:00:00:00:00:00->eth_dst,resubmit(,65),pop:NXM_NX_REG0[],pop:NXM_NX_REG15[] get_arp; Syntax error at `;' expecting `('. get_arp(); Syntax error at `)' expecting field name. get_arp(inport); Syntax error at `)' expecting `,'. get_arp(inport ip4.dst); Syntax error at `ip4.dst' expecting `,'. get_arp(inport, ip4.dst; Syntax error at `;' expecting `)'. get_arp(inport, eth.dst); Cannot use 48-bit field eth.dst[0..47] where 32-bit field is required. get_arp(inport, outport); Cannot use string field outport where numeric field is required. get_arp(reg0, ip4.dst); Cannot use numeric field reg0 where string field is required. # put_arp put_arp(inport, arp.spa, arp.sha); encodes as push:NXM_NX_REG0[],push:NXM_OF_ETH_SRC[],push:NXM_NX_ARP_SHA[],push:NXM_OF_ARP_SPA[],pop:NXM_NX_REG0[],pop:NXM_OF_ETH_SRC[],controller(userdata=00.00.00.01.00.00.00.00),pop:NXM_OF_ETH_SRC[],pop:NXM_NX_REG0[] has prereqs eth.type == 0x806 && eth.type == 0x806 # put_dhcp_opts reg1[0] = put_dhcp_opts(offerip = 1.2.3.4, router = 10.0.0.1); encodes as controller(userdata=00.00.00.02.00.00.00.00.00.01.de.10.00.00.00.40.01.02.03.04.03.04.0a.00.00.01,pause) reg2[5] = put_dhcp_opts(offerip=10.0.0.4,router=10.0.0.1,netmask=255.255.254.0,mtu=1400,domain="ovn.org"); formats as reg2[5] = put_dhcp_opts(offerip = 10.0.0.4, router = 10.0.0.1, netmask = 255.255.254.0, mtu = 1400, domain = "ovn.org"); encodes as controller(userdata=00.00.00.02.00.00.00.00.00.01.de.10.00.00.00.25.0a.00.00.04.03.04.0a.00.00.01.01.04.ff.ff.fe.00.1a.02.05.78.0f.07.6f.76.6e.2e.6f.72.67,pause) reg0[15] = put_dhcp_opts(offerip=10.0.0.4,router=10.0.0.1,netmask=255.255.255.0,mtu=1400,ip_forward_enable=1,default_ttl=121,dns_server={8.8.8.8,7.7.7.7},classless_static_route={30.0.0.0/24,10.0.0.4,40.0.0.0/16,10.0.0.6,0.0.0.0/0,10.0.0.1},ethernet_encap=1,router_discovery=0); formats as reg0[15] = put_dhcp_opts(offerip = 10.0.0.4, router = 10.0.0.1, netmask = 255.255.255.0, mtu = 1400, ip_forward_enable = 1, default_ttl = 121, dns_server = {8.8.8.8, 7.7.7.7}, classless_static_route = {30.0.0.0/24, 10.0.0.4, 40.0.0.0/16, 10.0.0.6, 0.0.0.0/0, 10.0.0.1}, ethernet_encap = 1, router_discovery = 0); encodes as controller(userdata=00.00.00.02.00.00.00.00.00.01.de.10.00.00.00.6f.0a.00.00.04.03.04.0a.00.00.01.01.04.ff.ff.ff.00.1a.02.05.78.13.01.01.17.01.79.06.08.08.08.08.08.07.07.07.07.79.14.18.1e.00.00.0a.00.00.04.10.28.00.0a.00.00.06.00.0a.00.00.01.24.01.01.1f.01.00,pause) reg1[0..1] = put_dhcp_opts(offerip = 1.2.3.4, router = 10.0.0.1); Cannot use 2-bit field reg1[0..1] where 1-bit field is required. reg1[0] = put_dhcp_opts(); put_dhcp_opts requires offerip to be specified. reg1[0] = put_dhcp_opts(x = 1.2.3.4, router = 10.0.0.1); Syntax error at `x' expecting DHCPv4 option name. reg1[0] = put_dhcp_opts(router = 10.0.0.1); put_dhcp_opts requires offerip to be specified. reg1[0] = put_dhcp_opts(offerip=1.2.3.4, "hi"); Syntax error at `"hi"'. reg1[0] = put_dhcp_opts(offerip=1.2.3.4, xyzzy); Syntax error at `xyzzy' expecting DHCPv4 option name. reg1[0] = put_dhcp_opts(offerip="xyzzy"); DHCPv4 option offerip requires numeric value. reg1[0] = put_dhcp_opts(offerip=1.2.3.4, domain=1.2.3.4); DHCPv4 option domain requires string value. # nd_na nd_na { eth.src = 12:34:56:78:9a:bc; nd.tll = 12:34:56:78:9a:bc; outport = inport; inport = ""; /* Allow sending out inport. */ output; }; formats as nd_na { eth.src = 12:34:56:78:9a:bc; nd.tll = 12:34:56:78:9a:bc; outport = inport; inport = ""; output; }; encodes as controller(userdata=00.00.00.03.00.00.00.00.00.19.00.10.80.00.08.06.12.34.56.78.9a.bc.00.00.00.19.00.10.80.00.42.06.12.34.56.78.9a.bc.00.00.ff.ff.00.18.00.00.23.20.00.06.00.20.00.00.00.00.00.01.1c.04.00.01.1e.04.00.19.00.10.00.01.1c.04.00.00.00.00.00.00.00.00.ff.ff.00.10.00.00.23.20.00.0e.ff.f8.40.00.00.00) has prereqs nd_ns # get_nd get_nd(outport, ip6.dst); encodes as push:NXM_NX_XXREG0[],push:NXM_NX_IPV6_DST[],pop:NXM_NX_XXREG0[],set_field:00:00:00:00:00:00->eth_dst,resubmit(,65),pop:NXM_NX_XXREG0[] has prereqs eth.type == 0x86dd get_nd(inport, xxreg0); encodes as push:NXM_NX_REG15[],push:NXM_NX_REG14[],pop:NXM_NX_REG15[],set_field:00:00:00:00:00:00->eth_dst,resubmit(,65),pop:NXM_NX_REG15[] get_nd; Syntax error at `;' expecting `('. get_nd(); Syntax error at `)' expecting field name. get_nd(inport); Syntax error at `)' expecting `,'. get_nd(inport ip6.dst); Syntax error at `ip6.dst' expecting `,'. get_nd(inport, ip6.dst; Syntax error at `;' expecting `)'. get_nd(inport, eth.dst); Cannot use 48-bit field eth.dst[0..47] where 128-bit field is required. get_nd(inport, outport); Cannot use string field outport where numeric field is required. get_nd(xxreg0, ip6.dst); Cannot use numeric field xxreg0 where string field is required. # put_nd put_nd(inport, nd.target, nd.sll); encodes as push:NXM_NX_XXREG0[],push:NXM_OF_ETH_SRC[],push:NXM_NX_ND_SLL[],push:NXM_NX_ND_TARGET[],pop:NXM_NX_XXREG0[],pop:NXM_OF_ETH_SRC[],controller(userdata=00.00.00.04.00.00.00.00),pop:NXM_OF_ETH_SRC[],pop:NXM_NX_XXREG0[] has prereqs ((icmp6.type == 0x87 && eth.type == 0x86dd && ip.proto == 0x3a && (eth.type == 0x800 || eth.type == 0x86dd)) || (icmp6.type == 0x88 && eth.type == 0x86dd && ip.proto == 0x3a && (eth.type == 0x800 || eth.type == 0x86dd))) && icmp6.code == 0 && eth.type == 0x86dd && ip.proto == 0x3a && (eth.type == 0x800 || eth.type == 0x86dd) && ip.ttl == 0xff && (eth.type == 0x800 || eth.type == 0x86dd) && icmp6.type == 0x87 && eth.type == 0x86dd && ip.proto == 0x3a && (eth.type == 0x800 || eth.type == 0x86dd) && icmp6.code == 0 && eth.type == 0x86dd && ip.proto == 0x3a && (eth.type == 0x800 || eth.type == 0x86dd) && ip.ttl == 0xff && (eth.type == 0x800 || eth.type == 0x86dd) # put_dhcpv6_opts reg1[0] = put_dhcpv6_opts(ia_addr = ae70::4, server_id = 00:00:00:00:10:02); encodes as controller(userdata=00.00.00.05.00.00.00.00.00.01.de.10.00.00.00.40.00.05.00.10.ae.70.00.00.00.00.00.00.00.00.00.00.00.00.00.04.00.02.00.06.00.00.00.00.10.02,pause) reg1[0] = put_dhcpv6_opts(); encodes as controller(userdata=00.00.00.05.00.00.00.00.00.01.de.10.00.00.00.40,pause) reg1[0] = put_dhcpv6_opts(dns_server={ae70::1,ae70::2}); formats as reg1[0] = put_dhcpv6_opts(dns_server = {ae70::1, ae70::2}); encodes as controller(userdata=00.00.00.05.00.00.00.00.00.01.de.10.00.00.00.40.00.17.00.20.ae.70.00.00.00.00.00.00.00.00.00.00.00.00.00.01.ae.70.00.00.00.00.00.00.00.00.00.00.00.00.00.02,pause) reg1[0] = put_dhcpv6_opts(server_id=12:34:56:78:9a:bc, dns_server={ae70::1,ae89::2}); formats as reg1[0] = put_dhcpv6_opts(server_id = 12:34:56:78:9a:bc, dns_server = {ae70::1, ae89::2}); encodes as controller(userdata=00.00.00.05.00.00.00.00.00.01.de.10.00.00.00.40.00.02.00.06.12.34.56.78.9a.bc.00.17.00.20.ae.70.00.00.00.00.00.00.00.00.00.00.00.00.00.01.ae.89.00.00.00.00.00.00.00.00.00.00.00.00.00.02,pause) reg1[0] = put_dhcpv6_opts(domain_search = "ovn.org"); encodes as controller(userdata=00.00.00.05.00.00.00.00.00.01.de.10.00.00.00.40.00.18.00.07.6f.76.6e.2e.6f.72.67,pause) reg1[0] = put_dhcpv6_opts(x = 1.2.3.4); Syntax error at `x' expecting DHCPv6 option name. reg1[0] = put_dhcpv6_opts(ia_addr=ae70::4, "hi"); Syntax error at `"hi"'. reg1[0] = put_dhcpv6_opts(ia_addr=ae70::4, xyzzy); Syntax error at `xyzzy' expecting DHCPv6 option name. reg1[0] = put_dhcpv6_opts(ia_addr="ae70::4"); DHCPv6 option ia_addr requires numeric value. reg1[0] = put_dhcpv6_opts(ia_addr=ae70::4, domain_search=ae70::1); DHCPv6 option domain_search requires string value. # set_queue set_queue(0); encodes as set_queue:0 set_queue(61440); encodes as set_queue:61440 set_queue(65535); Queue ID 65535 for set_queue is not in valid range 0 to 61440. # dns_lookup reg1[0] = dns_lookup(); encodes as controller(userdata=00.00.00.06.00.00.00.00.00.01.de.10.00.00.00.40,pause) has prereqs udp reg1[0] = dns_lookup("foo"); dns_lookup doesn't take any parameters # Contradictionary prerequisites (allowed but not useful): ip4.src = ip6.src[0..31]; encodes as move:NXM_NX_IPV6_SRC[0..31]->NXM_OF_IP_SRC[] has prereqs eth.type == 0x800 && eth.type == 0x86dd ip4.src <-> ip6.src[0..31]; encodes as push:NXM_NX_IPV6_SRC[0..31],push:NXM_OF_IP_SRC[],pop:NXM_NX_IPV6_SRC[0..31],pop:NXM_OF_IP_SRC[] has prereqs eth.type == 0x800 && eth.type == 0x86dd # Miscellaneous negative tests. ; Syntax error at `;'. xyzzy; Syntax error at `xyzzy' expecting action. next; 123; Syntax error at `123'. next; xyzzy; Syntax error at `xyzzy' expecting action. next Syntax error at end of input expecting `;'. ]]) sed '/^[[ ]]/d' test-cases.txt > input.txt cp test-cases.txt expout AT_CHECK([ovstest test-ovn parse-actions < input.txt], [0], [expout]) AT_CLEANUP AT_BANNER([OVN end-to-end tests]) # 3 hypervisors, one logical switch, 3 logical ports per hypervisor AT_SETUP([ovn -- 3 HVs, 1 LS, 3 lports/HV]) AT_KEYWORDS([ovnarp]) AT_SKIP_IF([test $HAVE_PYTHON = no]) ovn_start # Create hypervisors hv[123]. # Add vif1[123] to hv1, vif2[123] to hv2, vif3[123] to hv3. # Add all of the vifs to a single logical switch lsw0. # Turn on port security on all the vifs except vif[123]1. # Make vif13, vif2[23], vif3[123] destinations for unknown MACs. # Add some ACLs for Ethertypes 1234, 1235, 1236. ovn-nbctl ls-add lsw0 net_add n1 for i in 1 2 3; do sim_add hv$i as hv$i ovs-vsctl add-br br-phys ovn_attach n1 br-phys 192.168.0.$i for j in 1 2 3; do ovs-vsctl add-port br-int vif$i$j -- set Interface vif$i$j external-ids:iface-id=lp$i$j options:tx_pcap=hv$i/vif$i$j-tx.pcap options:rxq_pcap=hv$i/vif$i$j-rx.pcap ofport-request=$i$j ovn-nbctl lsp-add lsw0 lp$i$j if test $j = 1; then ovn-nbctl lsp-set-addresses lp$i$j "f0:00:00:00:00:$i$j 192.168.0.$i$j" unknown else if test $j = 3; then ip_addrs="192.168.0.$i$j fe80::ea2a:eaff:fe28:$i$j/64 192.169.0.$i$j" else ip_addrs="192.168.0.$i$j" fi ovn-nbctl lsp-set-addresses lp$i$j "f0:00:00:00:00:$i$j $ip_addrs" ovn-nbctl lsp-set-port-security lp$i$j f0:00:00:00:00:$i$j fi done done ovn-nbctl acl-add lsw0 from-lport 1000 'eth.type == 0x1234' drop ovn-nbctl acl-add lsw0 from-lport 1000 'eth.type == 0x1235 && inport == "lp11"' drop ovn-nbctl acl-add lsw0 to-lport 1000 'eth.type == 0x1236 && outport == "lp33"' drop ovn-nbctl create Address_Set name=set1 addresses=\"f0:00:00:00:00:11\",\"f0:00:00:00:00:21\",\"f0:00:00:00:00:31\" ovn-nbctl acl-add lsw0 to-lport 1000 'eth.type == 0x1237 && eth.src == $set1 && outport == "lp33"' drop # Pre-populate the hypervisors' ARP tables so that we don't lose any # packets for ARP resolution (native tunneling doesn't queue packets # for ARP resolution). ovn_populate_arp # Allow some time for ovn-northd and ovn-controller to catch up. # XXX This should be more systematic. sleep 1 # Make sure there is no attempt to adding duplicated flows by ovn-controller AT_FAIL_IF([test -n "`grep duplicate hv1/ovn-controller.log`"]) AT_FAIL_IF([test -n "`grep duplicate hv2/ovn-controller.log`"]) AT_FAIL_IF([test -n "`grep duplicate hv3/ovn-controller.log`"]) # Given the name of a logical port, prints the name of the hypervisor # on which it is located. vif_to_hv() { echo hv${1%?} } # test_packet INPORT DST SRC ETHTYPE OUTPORT... # # This shell function causes a packet to be received on INPORT. The packet's # content has Ethernet destination DST and source SRC (each exactly 12 hex # digits) and Ethernet type ETHTYPE (4 hex digits). The OUTPORTs (zero or # more) list the VIFs on which the packet should be received. INPORT and the # OUTPORTs are specified as logical switch port numbers, e.g. 11 for vif11. for i in 1 2 3; do for j in 1 2 3; do : > $i$j.expected done done test_packet() { local inport=$1 packet=$2$3$4; shift; shift; shift; shift hv=`vif_to_hv $inport` vif=vif$inport as $hv ovs-appctl netdev-dummy/receive $vif $packet for outport; do echo $packet >> $outport.expected done } # test_arp INPORT SHA SPA TPA [REPLY_HA] # # Causes a packet to be received on INPORT. The packet is an ARP # request with SHA, SPA, and TPA as specified. If REPLY_HA is provided, then # it should be the hardware address of the target to expect to receive in an # ARP reply; otherwise no reply is expected. # # INPORT is an logical switch port number, e.g. 11 for vif11. # SHA and REPLY_HA are each 12 hex digits. # SPA and TPA are each 8 hex digits. test_arp() { local inport=$1 sha=$2 spa=$3 tpa=$4 reply_ha=$5 local request=ffffffffffff${sha}08060001080006040001${sha}${spa}ffffffffffff${tpa} hv=`vif_to_hv $inport` as $hv ovs-appctl netdev-dummy/receive vif$inport $request if test X$reply_ha = X; then # Expect to receive the broadcast ARP on the other logical switch ports # if no reply is expected. local i j for i in 1 2 3; do for j in 1 2 3; do if test $i$j != $inport; then echo $request >> $i$j.expected fi done done else # Expect to receive the reply, if any. local reply=${sha}${reply_ha}08060001080006040002${reply_ha}${tpa}${sha}${spa} echo $reply >> $inport.expected fi } ip_to_hex() { printf "%02x%02x%02x%02x" "$@" } # Send packets between all pairs of source and destination ports: # # 1. Unicast packets are delivered to exactly one logical switch port # (except that packets destined to their input ports are dropped). # # 2. Broadcast and multicast are delivered to all logical switch ports # except the input port. # # 3. When port security is turned on, the switch drops packets from the wrong # MAC address. # # 4. The switch drops all packets with a VLAN tag. # # 5. The switch drops all packets with a multicast source address. (This only # affects behavior when port security is turned off, since otherwise port # security would drop the packet anyway.) # # 6. The switch delivers packets with an unknown destination to logical # switch ports with "unknown" among their MAC addresses (and port # security disabled). # # 7. The switch drops unicast packets that violate an ACL. # # 8. The switch drops multicast and broadcast packets that violate an ACL. # # 9. OVN generates responses to ARP requests for known IPs, except for # requests from a port for the port's own IP. # # 10. No response to ARP requests for unknown IPs. for is in 1 2 3; do for js in 1 2 3; do s=$is$js bcast= unknown= bacl2= bacl3= for id in 1 2 3; do for jd in 1 2 3; do d=$id$jd if test $d != $s; then unicast=$d; else unicast=; fi test_packet $s f000000000$d f000000000$s $s$d $unicast #1 if test $d != $s && test $js = 1; then impersonate=$d else impersonate= fi test_packet $s f000000000$d f00000000055 55$d $impersonate #3 if test $d != $s && test $s != 11; then acl2=$d; else acl2=; fi if test $d != $s && test $d != 33; then acl3=$d; else acl3=; fi if test $d = $s || (test $js = 1 && test $d = 33); then # Source of 11, 21, or 31 and dest of 33 should be dropped # due to the 4th ACL that uses address_set(set1). acl4= else acl4=$d fi test_packet $s f000000000$d f000000000$s 1234 #7, acl1 test_packet $s f000000000$d f000000000$s 1235 $acl2 #7, acl2 test_packet $s f000000000$d f000000000$s 1236 $acl3 #7, acl3 test_packet $s f000000000$d f000000000$s 1237 $acl4 #7, acl4 test_packet $s f000000000$d f00000000055 810000091234 #4 test_packet $s f000000000$d 0100000000$s $s$d #5 if test $d != $s && test $jd = 1; then unknown="$unknown $d" fi bcast="$bcast $unicast" bacl2="$bacl2 $acl2" bacl3="$bacl3 $acl3" sip=`ip_to_hex 192 168 0 $i$j` tip=`ip_to_hex 192 168 0 $id$jd` tip_unknown=`ip_to_hex 11 11 11 11` if test $d != $s; then reply_ha=f000000000$d else reply_ha= fi test_arp $s f000000000$s $sip $tip $reply_ha #9 test_arp $s f000000000$s $sip $tip_unknown #10 if test $jd = 3; then # lsp[123]3 has an additional ip 192.169.0.[123]3. tip=`ip_to_hex 192 169 0 $id$jd` test_arp $s f000000000$s $sip $tip $reply_ha #9 fi done done # Broadcast and multicast. test_packet $s ffffffffffff f000000000$s ${s}ff $bcast #2 test_packet $s 010000000000 f000000000$s ${s}ff $bcast #2 if test $js = 1; then bcast_impersonate=$bcast else bcast_impersonate= fi test_packet $s 010000000000 f00000000044 44ff $bcast_impersonate #3 test_packet $s f0000000ffff f000000000$s ${s}66 $unknown #6 test_packet $s ffffffffffff f000000000$s 1234 #8, acl1 test_packet $s ffffffffffff f000000000$s 1235 $bacl2 #8, acl2 test_packet $s ffffffffffff f000000000$s 1236 $bacl3 #8, acl3 test_packet $s 010000000000 f000000000$s 1234 #8, acl1 test_packet $s 010000000000 f000000000$s 1235 $bacl2 #8, acl2 test_packet $s 010000000000 f000000000$s 1236 $bacl3 #8, acl3 done done # set address for lp13 with invalid characters. # lp13 should be configured with only 192.168.0.13. ovn-nbctl lsp-set-addresses lp13 "f0:00:00:00:00:13 192.168.0.13 invalid 192.169.0.13" # Allow some time for ovn-northd and ovn-controller to catch up. # XXX This should be more systematic. sleep 1 sip=`ip_to_hex 192 168 0 11` tip=`ip_to_hex 192 168 0 13` test_arp 11 f00000000011 $sip $tip f00000000013 tip=`ip_to_hex 192 169 0 13` #arp request for 192.169.0.13 should be flooded test_arp 11 f00000000011 $sip $tip # dump information and flows with counters ovn-sbctl dump-flows -- list multicast_group echo "------ hv1 dump ------" as hv1 ovs-vsctl show as hv1 ovs-ofctl -O OpenFlow13 dump-flows br-int echo "------ hv2 dump ------" as hv2 ovs-vsctl show as hv2 ovs-ofctl -O OpenFlow13 dump-flows br-int echo "------ hv3 dump ------" as hv3 ovs-vsctl show as hv3 ovs-ofctl -O OpenFlow13 dump-flows br-int # Now check the packets actually received against the ones expected. for i in 1 2 3; do for j in 1 2 3; do OVN_CHECK_PACKETS([hv$i/vif$i$j-tx.pcap], [$i$j.expected]) done done OVN_CLEANUP([hv1],[hv2],[hv3]) AT_CLEANUP AT_SETUP([ovn -- trace 1 LS, 3 LSPs]) AT_SKIP_IF([test $HAVE_PYTHON = no]) ovn_start # Create a logical switch and some logical ports. # Turn on port security on all lports except ls1. # Make ls1 a destination for unknown MACs. # Add some ACLs for Ethertypes 1234, 1235, 1236. ovn-nbctl ls-add lsw0 ovn-sbctl chassis-add hv0 geneve 127.0.0.1 for i in 1 2 3; do ovn-nbctl lsp-add lsw0 lp$i done ovn-nbctl --wait=sb sync for i in 1 2 3; do ovn-sbctl lsp-bind lp$i hv0 if test $i = 1; then ovn-nbctl lsp-set-addresses lp$i "f0:00:00:00:00:0$i 192.168.0.$i" unknown else if test $i = 3; then ip_addrs="192.168.0.$i fe80::ea2a:eaff:fe28:$i/64 192.169.0.$i" else ip_addrs="192.168.0.$i" fi ovn-nbctl lsp-set-addresses lp$i "f0:00:00:00:00:$i $ip_addrs" ovn-nbctl lsp-set-port-security lp$i f0:00:00:00:00:$i fi done ovn-nbctl acl-add lsw0 from-lport 1000 'eth.type == 0x1234' drop ovn-nbctl acl-add lsw0 from-lport 1000 'eth.type == 0x1235 && inport == "lp1"' drop ovn-nbctl acl-add lsw0 to-lport 1000 'eth.type == 0x1236 && outport == "lp3"' drop ovn-nbctl create Address_Set name=set1 addresses=\"f0:00:00:00:00:01\",\"f0:00:00:00:00:02\" ovn-nbctl acl-add lsw0 to-lport 1000 'eth.type == 0x1237 && eth.src == $set1 && outport == "lp3"' drop ovn-nbctl --wait=sb sync on_exit 'kill `cat ovn-trace.pid`' ovn-trace --detach --pidfile --no-chdir # test_packet INPORT DST SRC [-vlan] [-eth TYPE] OUTPORT... # # This shell function causes a packet to be received on INPORT. The packet's # content has Ethernet destination DST and source SRC (each exactly 12 hex # digits) and Ethernet type ETHTYPE (4 hex digits). The OUTPORTs (zero or # more) list the VIFs on which the packet should be received. INPORT and the # OUTPORTs are specified as logical switch port numbers, e.g. 11 for vif11. test_packet() { local inport=$1 eth_dst=$2 eth_src=$3; shift; shift; shift uflow="inport==\"lp$inport\" && eth.dst==$eth_dst && eth.src==$eth_src" while :; do case $1 in # ( -vlan) uflow="$uflow && vlan.vid == 1234"; shift ;; # ( -eth) uflow="$uflow && eth.type == 0x$2"; shift; shift ;; # ( *) break ;; esac done for outport; do echo "output(\"lp$outport\");" done > expout AT_CAPTURE_FILE([trace]) AT_CHECK([ovs-appctl -t ovn-trace trace --all lsw0 "$uflow" | tee trace | sed '1,/Minimal trace/d'], [0], [expout]) } # test_arp INPORT SHA SPA TPA [REPLY_HA] # # Causes a packet to be received on INPORT. The packet is an ARP # request with SHA, SPA, and TPA as specified. If REPLY_HA is provided, then # it should be the hardware address of the target to expect to receive in an # ARP reply; otherwise no reply is expected. # # INPORT is an logical switch port number, e.g. 11 for vif11. # SHA and REPLY_HA are each 12 hex digits. # SPA and TPA are each 8 hex digits. test_arp() { local inport=$1 sha=$2 spa=$3 tpa=$4 reply_ha=$5 local request="inport == \"lp$inport\" && eth.dst == ff:ff:ff:ff:ff:ff && eth.src == $sha && arp.op == 1 && arp.sha == $sha && arp.spa == $spa && arp.tha == ff:ff:ff:ff:ff:ff && arp.tpa == $tpa" if test -z "$reply_ha"; then reply= local i for i in 1 2 3; do if test $i != $inport; then reply="${reply}output(\"lp$i\"); " fi done else reply="\ eth.dst = $sha; eth.src = $reply_ha; arp.op = 2; arp.tha = $sha; arp.sha = $reply_ha; arp.tpa = $spa; arp.spa = $tpa; output(\"lp$inport\"); " fi AT_CAPTURE_FILE([trace]) AT_CHECK_UNQUOTED([ovs-appctl -t ovn-trace trace --all lsw0 "$request" | tee trace | sed '1,/Minimal trace/d'], [0], [$reply]) } # Send packets between all pairs of source and destination ports: # # 1. Unicast packets are delivered to exactly one logical switch port # (except that packets destined to their input ports are dropped). # # 2. Broadcast and multicast are delivered to all logical switch ports # except the input port. # # 3. When port security is turned on, the switch drops packets from the wrong # MAC address. # # 4. The switch drops all packets with a VLAN tag. # # 5. The switch drops all packets with a multicast source address. (This only # affects behavior when port security is turned off, since otherwise port # security would drop the packet anyway.) # # 6. The switch delivers packets with an unknown destination to logical # switch ports with "unknown" among their MAC addresses (and port # security disabled). # # 7. The switch drops unicast packets that violate an ACL. # # 8. The switch drops multicast and broadcast packets that violate an ACL. # # 9. OVN generates responses to ARP requests for known IPs, except for # requests from a port for the port's own IP. # # 10. No response to ARP requests for unknown IPs. for s in 1 2 3; do bcast= unknown= bacl2= bacl3= for d in 1 2 3; do echo echo "lp$s -> lp$d" if test $d != $s; then unicast=$d; else unicast=; fi test_packet $s f0:00:00:00:00:0$d f0:00:00:00:00:0$s $unicast #1 if test $d != $s && test $s = 1; then impersonate=$d else impersonate= fi test_packet $s f0:00:00:00:00:0$d f0:00:00:00:00:55 $impersonate #3 if test $d != $s && test $s != 1; then acl2=$d; else acl2=; fi if test $d != $s && test $d != 3; then acl3=$d; else acl3=; fi if test $d = $s || ( (test $s = 1 || test $s = 2) && test $d = 3); then # Source of 1 or 2 and dest of 3 should be dropped # due to the 4th ACL that uses address_set(set1). acl4= else acl4=$d fi #7, acl1 to acl4: test_packet $s f0:00:00:00:00:0$d f0:00:00:00:00:0$s -eth 1234 test_packet $s f0:00:00:00:00:0$d f0:00:00:00:00:0$s -eth 1235 $acl2 test_packet $s f0:00:00:00:00:0$d f0:00:00:00:00:0$s -eth 1236 $acl3 test_packet $s f0:00:00:00:00:0$d f0:00:00:00:00:0$s -eth 1237 $acl4 test_packet $s f0:00:00:00:00:0$d f0:00:00:00:00:55 -vlan #4 test_packet $s f0:00:00:00:00:0$d 01:00:00:00:00:0$s #5 if test $d != $s && test $d = 1; then unknown="$unknown $d" fi bcast="$bcast $unicast" bacl2="$bacl2 $acl2" bacl3="$bacl3 $acl3" sip=192.168.0.$s tip=192.168.0.$d tip_unknown=11.11.11.11 if test $d != $s; then reply_ha=f0:00:00:00:00:0$d; else reply_ha=; fi test_arp $s f0:00:00:00:00:0$s $sip $tip $reply_ha #9 test_arp $s f0:00:00:00:00:0$s $sip $tip_unknown #10 if test $d = 3; then # lp3 has an additional ip 192.169.0.[123]3. tip=192.169.0.$d test_arp $s f0:00:00:00:00:0$s $sip $tip $reply_ha #9 fi done # Broadcast and multicast. test_packet $s ff:ff:ff:ff:ff:ff f0:00:00:00:00:0$s $bcast #2 test_packet $s 01:00:00:00:00:00 f0:00:00:00:00:0$s $bcast #2 if test $s = 1; then bcast_impersonate=$bcast else bcast_impersonate= fi test_packet $s 01:00:00:00:00:00 f0:00:00:00:00:44 $bcast_impersonate #3 test_packet $s f0:00:00:00:ff:ff f0:00:00:00:00:0$s $unknown #6 #8, acl1 to acl3: test_packet $s ff:ff:ff:ff:ff:ff f0:00:00:00:00:0$s -eth 1234 test_packet $s ff:ff:ff:ff:ff:ff f0:00:00:00:00:0$s -eth 1235 $bacl2 test_packet $s ff:ff:ff:ff:ff:ff f0:00:00:00:00:0$s -eth 1236 $bacl3 #8, acl1 to acl3: test_packet $s 01:00:00:00:00:00 f0:00:00:00:00:0$s -eth 1234 test_packet $s 01:00:00:00:00:00 f0:00:00:00:00:0$s -eth 1235 $bacl2 test_packet $s 01:00:00:00:00:00 f0:00:00:00:00:0$s -eth 1236 $bacl3 done AT_CLEANUP # 2 hypervisors, 4 logical ports per HV # 2 locally attached networks (one flat, one vlan tagged over same device) # 2 ports per HV on each network AT_SETUP([ovn -- 2 HVs, 4 lports/HV, localnet ports]) AT_SKIP_IF([test $HAVE_PYTHON = no]) ovn_start # In this test cases we create 3 switches, all connected to same # physical network (through br-phys on each HV). Each switch has # VIF ports across 2 HVs. Each HV has 5 VIF ports. The first digit # of VIF port name indicates the hypervisor it is bound to, e.g. # lp23 means VIF 3 on hv2. # # Each switch's VLAN tag and their logical switch ports are: # - ls1: # - untagged # - ports: lp11, lp12, lp21, lp22 # # - ls2: # - tagged with VLAN 101 # - ports: lp13, lp14, lp23, lp24 # - ls3: # - untagged # - ports: lp15, lp25 # # Note: a localnet port is created for each switch to connect to # physical network. for i in 1 2 3; do ls_name=ls$i ovn-nbctl ls-add $ls_name ln_port_name=ln$i if test $i -eq 2; then ovn-nbctl lsp-add $ls_name $ln_port_name "" 101 else ovn-nbctl lsp-add $ls_name $ln_port_name fi ovn-nbctl lsp-set-addresses $ln_port_name unknown ovn-nbctl lsp-set-type $ln_port_name localnet ovn-nbctl lsp-set-options $ln_port_name network_name=phys done # lsp_to_ls LSP # # Prints the name of the logical switch that contains LSP. lsp_to_ls () { case $1 in dnl ( lp?[[12]]) echo ls1 ;; dnl ( lp?[[34]]) echo ls2 ;; dnl ( lp?5) echo ls3 ;; dnl ( *) AT_FAIL_IF([:]) ;; esac } net_add n1 for i in 1 2; do sim_add hv$i as hv$i ovs-vsctl add-br br-phys ovs-vsctl set open . external-ids:ovn-bridge-mappings=phys:br-phys ovn_attach n1 br-phys 192.168.0.$i for j in 1 2 3 4 5; do ovs-vsctl add-port br-int vif$i$j -- \ set Interface vif$i$j external-ids:iface-id=lp$i$j \ options:tx_pcap=hv$i/vif$i$j-tx.pcap \ options:rxq_pcap=hv$i/vif$i$j-rx.pcap \ ofport-request=$i$j lsp_name=lp$i$j ls_name=$(lsp_to_ls $lsp_name) ovn-nbctl lsp-add $ls_name $lsp_name ovn-nbctl lsp-set-addresses $lsp_name f0:00:00:00:00:$i$j ovn-nbctl lsp-set-port-security $lsp_name f0:00:00:00:00:$i$j OVS_WAIT_UNTIL([test x`ovn-nbctl lsp-get-up $lsp_name` = xup]) done done ovn-nbctl --wait=sb sync ovn-sbctl dump-flows ovn_populate_arp # XXX This is now the 3rd copy of these functions in this file ... # Given the name of a logical port, prints the name of the hypervisor # on which it is located. vif_to_hv() { echo hv${1%?} } # # test_packet INPORT DST SRC ETHTYPE EOUT LOUT # # This shell function causes a packet to be received on INPORT. The packet's # content has Ethernet destination DST and source SRC (each exactly 12 hex # digits) and Ethernet type ETHTYPE (4 hex digits). INPORT is specified as # logical switch port numbers, e.g. 11 for vif11. # # EOUT is the end-to-end output port, that is, where the packet will end up # after possibly bouncing through one or more localnet ports. LOUT is the # logical output port, which might be a localnet port, as seen by ovn-trace # (which doesn't know what localnet ports are connected to and therefore can't # figure out the end-to-end answer). for i in 1 2; do for j in 1 2 3 4 5; do : > $i$j.expected done done test_packet() { local inport=$1 dst=$2 src=$3 eth=$4 eout=$5 lout=$6 echo "$@" # First try tracing the packet. uflow="inport==\"lp$inport\" && eth.dst==$dst && eth.src==$src && eth.type==0x$eth" if test $lout != drop; then echo "output(\"$lout\");" fi > expout AT_CAPTURE_FILE([trace]) AT_CHECK([ovn-trace --all $(lsp_to_ls lp$inport) "$uflow" | tee trace | sed '1,/Minimal trace/d'], [0], [expout]) # Then actually send a packet, for an end-to-end test. local packet=$(echo $dst$src | sed 's/://g')${eth} hv=`vif_to_hv $inport` vif=vif$inport as $hv ovs-appctl netdev-dummy/receive $vif $packet if test $eout != drop; then echo $packet >> ${eout#lp}.expected fi } # lp11 and lp21 are on the same network (phys, untagged) # and on different hypervisors test_packet 11 f0:00:00:00:00:21 f0:00:00:00:00:11 1121 lp21 lp21 test_packet 21 f0:00:00:00:00:11 f0:00:00:00:00:21 2111 lp11 lp11 # lp11 and lp12 are on the same network (phys, untagged) # and on the same hypervisor test_packet 11 f0:00:00:00:00:12 f0:00:00:00:00:11 1112 lp12 lp12 test_packet 12 f0:00:00:00:00:11 f0:00:00:00:00:12 1211 lp11 lp11 # lp13 and lp23 are on the same network (phys, VLAN 101) # and on different hypervisors test_packet 13 f0:00:00:00:00:23 f0:00:00:00:00:13 1323 lp23 lp23 test_packet 23 f0:00:00:00:00:13 f0:00:00:00:00:23 2313 lp13 lp13 # lp13 and lp14 are on the same network (phys, VLAN 101) # and on the same hypervisor test_packet 13 f0:00:00:00:00:14 f0:00:00:00:00:13 1314 lp14 lp14 test_packet 14 f0:00:00:00:00:13 f0:00:00:00:00:14 1413 lp13 lp13 # lp11 and lp15 are on the same network (phys, untagged), # same hypervisor, and on different switches test_packet 11 f0:00:00:00:00:15 f0:00:00:00:00:11 1115 lp15 ln1 test_packet 15 f0:00:00:00:00:11 f0:00:00:00:00:15 1511 lp11 ln3 # lp11 and lp25 are on the same network (phys, untagged), # different hypervisors, and on different switches test_packet 11 f0:00:00:00:00:25 f0:00:00:00:00:11 1125 lp25 ln1 test_packet 25 f0:00:00:00:00:11 f0:00:00:00:00:25 2511 lp11 ln3 # Ports that should not be able to communicate test_packet 11 f0:00:00:00:00:13 f0:00:00:00:00:11 1113 drop ln1 test_packet 11 f0:00:00:00:00:23 f0:00:00:00:00:11 1123 drop ln1 test_packet 21 f0:00:00:00:00:13 f0:00:00:00:00:21 2113 drop ln1 test_packet 21 f0:00:00:00:00:23 f0:00:00:00:00:21 2123 drop ln1 test_packet 13 f0:00:00:00:00:11 f0:00:00:00:00:13 1311 drop ln2 test_packet 13 f0:00:00:00:00:21 f0:00:00:00:00:13 1321 drop ln2 test_packet 23 f0:00:00:00:00:11 f0:00:00:00:00:23 2311 drop ln2 test_packet 23 f0:00:00:00:00:21 f0:00:00:00:00:23 2321 drop ln2 # Dump a bunch of info helpful for debugging if there's a failure. echo "------ OVN dump ------" ovn-nbctl show ovn-sbctl show echo "------ hv1 dump ------" as hv1 ovs-vsctl show as hv1 ovs-ofctl -O OpenFlow13 dump-flows br-int echo "------ hv2 dump ------" as hv2 ovs-vsctl show as hv2 ovs-ofctl -O OpenFlow13 dump-flows br-int # Now check the packets actually received against the ones expected. for i in 1 2; do for j in 1 2 3 4 5; do OVN_CHECK_PACKETS([hv$i/vif$i$j-tx.pcap], [$i$j.expected]) done done OVN_CLEANUP([hv1],[hv2]) AT_CLEANUP AT_SETUP([ovn -- vtep: 3 HVs, 1 VIFs/HV, 1 GW, 1 LS]) AT_KEYWORDS([vtep]) AT_SKIP_IF([test $HAVE_PYTHON = no]) ovn_start # Configure the Northbound database ovn-nbctl ls-add lsw0 ovn-nbctl lsp-add lsw0 lp1 ovn-nbctl lsp-set-addresses lp1 f0:00:00:00:00:01 ovn-nbctl lsp-add lsw0 lp2 ovn-nbctl lsp-set-addresses lp2 f0:00:00:00:00:02 ovn-nbctl lsp-add lsw0 lp-vtep ovn-nbctl lsp-set-type lp-vtep vtep ovn-nbctl lsp-set-options lp-vtep vtep-physical-switch=br-vtep vtep-logical-switch=lsw0 ovn-nbctl lsp-set-addresses lp-vtep unknown # lpr, lr and lrp1 are used for the ARP request handling test only. ovn-nbctl lsp-add lsw0 lpr ovn-nbctl lr-add lr ovn-nbctl lrp-add lr lrp1 f0:00:00:00:00:f1 192.168.1.1/24 ovn-nbctl set Logical_Switch_Port lpr type=router \ options:router-port=lrp1 \ addresses='"f0:00:00:00:00:f1 192.168.1.1"' net_add n1 # Network to connect hv1, hv2, and vtep net_add n2 # Network to connect vtep and hv3 # Create hypervisor hv1 connected to n1 sim_add hv1 as hv1 ovs-vsctl add-br br-phys ovn_attach n1 br-phys 192.168.0.1 ovs-vsctl add-port br-int vif1 -- set Interface vif1 external-ids:iface-id=lp1 options:tx_pcap=hv1/vif1-tx.pcap options:rxq_pcap=hv1/vif1-rx.pcap ofport-request=1 # Create hypervisor hv2 connected to n1 sim_add hv2 as hv2 ovs-vsctl add-br br-phys ovn_attach n1 br-phys 192.168.0.2 ovs-vsctl add-port br-int vif2 -- set Interface vif2 external-ids:iface-id=lp2 options:tx_pcap=hv2/vif2-tx.pcap options:rxq_pcap=hv2/vif2-rx.pcap ofport-request=1 # Start the vtep emulator with a leg in both networks sim_add vtep as vtep ovsdb-tool create "$ovs_base"/vtep/vtep.db "$abs_top_srcdir"/vtep/vtep.ovsschema || return 1 ovs-appctl -t ovsdb-server ovsdb-server/add-db "$ovs_base"/vtep/vtep.db ovs-vsctl add-br br-phys net_attach n1 br-phys mac=`ovs-vsctl get Interface br-phys mac_in_use | sed s/\"//g` arp_table="$arp_table $sandbox,br-phys,192.168.0.3,$mac" ovs-appctl netdev-dummy/ip4addr br-phys 192.168.0.3/24 >/dev/null || return 1 ovs-appctl ovs/route/add 192.168.0.3/24 br-phys >/dev/null || return 1 ovs-vsctl add-br br-vtep net_attach n2 br-vtep vtep-ctl add-ps br-vtep vtep-ctl set Physical_Switch br-vtep tunnel_ips=192.168.0.3 vtep-ctl add-ls lsw0 start_daemon ovs-vtep br-vtep start_daemon ovn-controller-vtep --vtep-db=unix:"$ovs_base"/vtep/db.sock --ovnsb-db=unix:"$ovs_base"/ovn-sb/ovn-sb.sock OVS_WAIT_UNTIL([vtep-ctl bind-ls br-vtep br-vtep_n2 0 lsw0]) OVS_WAIT_UNTIL([test -n "`as vtep vtep-ctl get-replication-mode lsw0 | grep -- source`"]) # It takes more time for the update to be processed by ovs-vtep. sleep 1 # Add hv3 on the other side of the vtep sim_add hv3 as hv3 ovs-vsctl add-br br-phys net_attach n2 br-phys ovs-vsctl add-port br-phys vif3 -- set Interface vif3 options:tx_pcap=hv3/vif3-tx.pcap options:rxq_pcap=hv3/vif3-rx.pcap ofport-request=1 # Pre-populate the hypervisors' ARP tables so that we don't lose any # packets for ARP resolution (native tunneling doesn't queue packets # for ARP resolution). ovn_populate_arp # Allow some time for ovn-northd and ovn-controller to catch up. # XXX This should be more systematic. sleep 1 # test_packet INPORT DST SRC ETHTYPE OUTPORT... # # This shell function causes a packet to be received on INPORT. The packet's # content has Ethernet destination DST and source SRC (each exactly 12 hex # digits) and Ethernet type ETHTYPE (4 hex digits). The OUTPORTs (zero or # more) list the VIFs on which the packet should be received. INPORT and the # OUTPORTs are specified as logical switch port numbers, e.g. 1 for vif1. for i in 1 2 3; do : > $i.expected done test_packet() { local inport=$1 packet=$2$3$4; shift; shift; shift; shift #hv=hv`echo $inport | sed 's/^\(.\).*/\1/'` hv=hv$inport vif=vif$inport as $hv ovs-appctl netdev-dummy/receive $vif $packet for outport; do echo $packet >> $outport.expected done } # Send packets between all pairs of source and destination ports: # # 1. Unicast packets are delivered to exactly one logical switch port # (except that packets destined to their input ports are dropped). # # 2. Broadcast and multicast are delivered to all logical switch ports # except the input port. # # 3. The switch delivers packets with an unknown destination to logical # switch ports with "unknown" among their MAC addresses (and port # security disabled). for s in 1 2 3; do bcast= unknown= for d in 1 2 3; do if test $d != $s; then unicast=$d; else unicast=; fi test_packet $s f0000000000$d f0000000000$s 00$s$d $unicast #1 # The vtep (vif3) is the only one configured for "unknown" if test $d != $s && test $d = 3; then unknown="$unknown $d" fi bcast="$bcast $unicast" done # Broadcast and multicast. test_packet $s ffffffffffff f0000000000$s 0${s}ff $bcast #2 test_packet $s 010000000000 f0000000000$s 0${s}ff $bcast #2 test_packet $s f0000000ffff f0000000000$s 0${s}66 $unknown #3 done # ARP request should not be responded to by logical switch router # type arp responder on HV1 and HV2 and should reach directly to # vif1 and vif2 ip_to_hex() { printf "%02x%02x%02x%02x" "$@" } sha=f00000000003 spa=`ip_to_hex 192 168 1 2` tpa=`ip_to_hex 192 168 1 1` request=ffffffffffff${sha}08060001080006040001${sha}${spa}ffffffffffff${tpa} as hv3 ovs-appctl netdev-dummy/receive vif3 $request echo $request >> 1.expected echo $request >> 2.expected # dump information with counters echo "------ OVN dump ------" ovn-nbctl show ovn-sbctl show echo "---------SB dump-----" ovn-sbctl list datapath_binding echo "---------------------" ovn-sbctl list port_binding echo "---------------------" ovn-sbctl dump-flows echo "------ hv1 dump ------" as hv1 ovs-vsctl show as hv1 ovs-ofctl -O OpenFlow13 show br-int as hv1 ovs-ofctl -O OpenFlow13 dump-flows br-int echo "------ hv2 dump ------" as hv2 ovs-vsctl show as hv2 ovs-ofctl -O OpenFlow13 show br-int as hv2 ovs-ofctl -O OpenFlow13 dump-flows br-int echo "------ hv3 dump ------" as hv3 ovs-vsctl show # note: hv3 has no logical port bind, thus it should not have br-int AT_CHECK([as hv3 ovs-ofctl -O OpenFlow13 show br-int], [1], [], [ovs-ofctl: br-int is not a bridge or a socket ]) # Now check the packets actually received against the ones expected. for i in 1 2 3; do OVN_CHECK_PACKETS([hv$i/vif$i-tx.pcap], [$i.expected]) done # Gracefully terminate daemons OVN_CLEANUP([hv1],[hv2],[vtep]) OVN_CLEANUP_VSWITCH([hv3]) AT_CLEANUP # Similar test to "hardware GW" AT_SETUP([ovn -- 3 HVs, 1 VIFs/HV, 1 software GW, 1 LS]) AT_SKIP_IF([test $HAVE_PYTHON = no]) ovn_start # Configure the Northbound database ovn-nbctl ls-add lsw0 ovn-nbctl lsp-add lsw0 lp1 ovn-nbctl lsp-set-addresses lp1 f0:00:00:00:00:01 ovn-nbctl lsp-add lsw0 lp2 ovn-nbctl lsp-set-addresses lp2 f0:00:00:00:00:02 ovn-nbctl lsp-add lsw0 lp-gw ovn-nbctl lsp-set-type lp-gw l2gateway ovn-nbctl lsp-set-options lp-gw network_name=physnet1 l2gateway-chassis=hv_gw ovn-nbctl lsp-set-addresses lp-gw unknown net_add n1 # Network to connect hv1, hv2, and gw net_add n2 # Network to connect gw and hv3 # Create hypervisor hv1 connected to n1 sim_add hv1 as hv1 ovs-vsctl add-br br-phys ovn_attach n1 br-phys 192.168.0.1 ovs-vsctl add-port br-int vif1 -- set Interface vif1 external-ids:iface-id=lp1 options:tx_pcap=hv1/vif1-tx.pcap options:rxq_pcap=hv1/vif1-rx.pcap ofport-request=1 # Create hypervisor hv2 connected to n1 sim_add hv2 as hv2 ovs-vsctl add-br br-phys ovn_attach n1 br-phys 192.168.0.2 ovs-vsctl add-port br-int vif2 -- set Interface vif2 external-ids:iface-id=lp2 options:tx_pcap=hv2/vif2-tx.pcap options:rxq_pcap=hv2/vif2-rx.pcap ofport-request=1 # Create hypervisor hv_gw connected to n1 and n2 # connect br-phys bridge to n1; connect hv-gw bridge to n2 sim_add hv_gw as hv_gw ovs-vsctl add-br br-phys ovn_attach n1 br-phys 192.168.0.3 ovs-vsctl add-br br-phys2 net_attach n2 br-phys2 ovs-vsctl set open . external_ids:ovn-bridge-mappings="physnet1:br-phys2" # Add hv3 on the other side of the GW sim_add hv3 as hv3 ovs-vsctl add-br br-phys net_attach n2 br-phys ovs-vsctl add-port br-phys vif3 -- set Interface vif3 options:tx_pcap=hv3/vif3-tx.pcap options:rxq_pcap=hv3/vif3-rx.pcap ofport-request=1 # Pre-populate the hypervisors' ARP tables so that we don't lose any # packets for ARP resolution (native tunneling doesn't queue packets # for ARP resolution). ovn_populate_arp # Allow some time for ovn-northd and ovn-controller to catch up. # XXX This should be more systematic. sleep 1 # test_packet INPORT DST SRC ETHTYPE OUTPORT... # # This shell function causes a packet to be received on INPORT. The packet's # content has Ethernet destination DST and source SRC (each exactly 12 hex # digits) and Ethernet type ETHTYPE (4 hex digits). The OUTPORTs (zero or # more) list the VIFs on which the packet should be received. INPORT and the # OUTPORTs are specified as lport numbers, e.g. 1 for vif1. for i in 1 2 3; do : > $i.expected done test_packet() { local inport=$1 packet=$2$3$4; shift; shift; shift; shift #hv=hv`echo $inport | sed 's/^\(.\).*/\1/'` hv=hv$inport vif=vif$inport as $hv ovs-appctl netdev-dummy/receive $vif $packet for outport; do echo $packet >> $outport.expected done } # Send packets between all pairs of source and destination ports: # # 1. Unicast packets are delivered to exactly one lport (except that packets # destined to their input ports are dropped). # # 2. Broadcast and multicast are delivered to all lports except the input port. # # 3. The lswitch delivers packets with an unknown destination to lports with # "unknown" among their MAC addresses (and port security disabled). for s in 1 2 3 ; do bcast= unknown= for d in 1 2 3 ; do if test $d != $s; then unicast=$d; else unicast=; fi test_packet $s f0000000000$d f0000000000$s 00$s$d $unicast #1 # The vtep (vif3) is the only one configured for "unknown" if test $d != $s && test $d = 3; then unknown="$unknown $d" fi bcast="$bcast $unicast" done test_packet $s ffffffffffff f0000000000$s 0${s}ff $bcast #2 test_packet $s 010000000000 f0000000000$s 0${s}ff $bcast #3 test_packet $s f0000000ffff f0000000000$s 0${s}66 $unknown #4 done echo "------ ovn-nbctl show ------" ovn-nbctl show echo "------ ovn-sbctl show ------" ovn-sbctl show echo "------ hv1 ------" as hv1 ovs-vsctl show echo "------ hv1 br-int ------" as hv1 ovs-ofctl -O OpenFlow13 dump-flows br-int echo "------ hv1 br-phys ------" as hv1 ovs-ofctl -O OpenFlow13 dump-flows br-phys echo "------ hv2 ------" as hv2 ovs-vsctl show echo "------ hv2 br-int ------" as hv2 ovs-ofctl -O OpenFlow13 dump-flows br-int echo "------ hv2 br-phys ------" as hv2 ovs-ofctl -O OpenFlow13 dump-flows br-phys echo "------ hv_gw ------" as hv_gw ovs-vsctl show echo "------ hv_gw br-phys ------" as hv_gw ovs-ofctl -O OpenFlow13 dump-flows br-phys echo "------ hv_gw br-phys2 ------" as hv_gw ovs-ofctl -O OpenFlow13 dump-flows br-phys2 echo "------ hv3 ------" as hv3 ovs-vsctl show echo "------ hv3 br-phys ------" as hv3 ovs-ofctl -O OpenFlow13 dump-flows br-phys # Now check the packets actually received against the ones expected. for i in 1 2 3; do OVN_CHECK_PACKETS([hv$i/vif$i-tx.pcap], [$i.expected]) done AT_CLEANUP # 3 hypervisors, 3 logical switches with 3 logical ports each, 1 logical router AT_SETUP([ovn -- 3 HVs, 3 LS, 3 lports/LS, 1 LR]) AT_SKIP_IF([test $HAVE_PYTHON = no]) ovn_start # Logical network: # # Three logical switches ls1, ls2, ls3. # One logical router lr0 connected to ls[123], # with nine subnets, three per logical switch: # # lrp11 on ls1 for subnet 192.168.11.0/24 # lrp12 on ls1 for subnet 192.168.12.0/24 # lrp13 on ls1 for subnet 192.168.13.0/24 # ... # lrp33 on ls3 for subnet 192.168.33.0/24 # # 27 VIFs, 9 per LS, 3 per subnet: lp[123][123][123], where the first two # digits are the subnet and the last digit distinguishes the VIF. for i in 1 2 3; do ovn-nbctl ls-add ls$i for j in 1 2 3; do for k in 1 2 3; do # Add "unknown" to MAC addresses for lp?11, so packets for # MAC-IP bindings discovered via ARP later have somewhere to go. if test $j$k = 11; then unknown=unknown; else unknown=; fi ovn-nbctl \ -- lsp-add ls$i lp$i$j$k \ -- lsp-set-addresses lp$i$j$k "f0:00:00:00:0$i:$j$k \ 192.168.$i$j.$k" $unknown done done done ovn-nbctl lr-add lr0 for i in 1 2 3; do for j in 1 2 3; do ovn-nbctl lrp-add lr0 lrp$i$j 00:00:00:00:ff:$i$j 192.168.$i$j.254/24 ovn-nbctl \ -- lsp-add ls$i lrp$i$j-attachment \ -- set Logical_Switch_Port lrp$i$j-attachment type=router \ options:router-port=lrp$i$j \ addresses='"00:00:00:00:ff:'$i$j'"' done done ovn-nbctl set Logical_Switch_Port lrp33-attachment \ addresses='"00:00:00:00:ff:33 192.168.33.254"' # Physical network: # # Three hypervisors hv[123]. # lp?1[123] spread across hv[123]: lp?11 on hv1, lp?12 on hv2, lp?13 on hv3. # lp?2[123] spread across hv[23]: lp?21 and lp?22 on hv2, lp?23 on hv3. # lp?3[123] all on hv3. # Given the name of a logical port, prints the name of the hypervisor # on which it is located. vif_to_hv() { case $1 in dnl ( ?11) echo 1 ;; dnl ( ?12 | ?21 | ?22) echo 2 ;; dnl ( ?13 | ?23 | ?3?) echo 3 ;; esac } # Given the name of a logical port, prints the name of its logical router # port, e.g. "vif_to_lrp 123" yields 12. vif_to_lrp() { echo ${1%?} } # Given the name of a logical port, prints the name of its logical # switch, e.g. "vif_to_ls 123" yields 1. vif_to_ls() { echo ${1%??} } net_add n1 for i in 1 2 3; do sim_add hv$i as hv$i ovs-vsctl add-br br-phys ovn_attach n1 br-phys 192.168.0.$i done for i in 1 2 3; do for j in 1 2 3; do for k in 1 2 3; do hv=`vif_to_hv $i$j$k` as hv$hv ovs-vsctl \ -- add-port br-int vif$i$j$k \ -- set Interface vif$i$j$k \ external-ids:iface-id=lp$i$j$k \ options:tx_pcap=hv$hv/vif$i$j$k-tx.pcap \ options:rxq_pcap=hv$hv/vif$i$j$k-rx.pcap \ ofport-request=$i$j$k done done done # Pre-populate the hypervisors' ARP tables so that we don't lose any # packets for ARP resolution (native tunneling doesn't queue packets # for ARP resolution). ovn_populate_arp # Allow some time for ovn-northd and ovn-controller to catch up. # XXX This should be more systematic. sleep 1 # test_ip INPORT SRC_MAC DST_MAC SRC_IP DST_IP OUTPORT... # # This shell function causes a packet to be received on INPORT. The packet's # content has Ethernet destination DST and source SRC (each exactly 12 hex # digits) and Ethernet type ETHTYPE (4 hex digits). The OUTPORTs (zero or # more) list the VIFs on which the packet should be received. INPORT and the # OUTPORTs are specified as logical switch port numbers, e.g. 123 for vif123. for i in 1 2 3; do for j in 1 2 3; do for k in 1 2 3; do : > $i$j$k.expected done done done test_ip() { # This packet has bad checksums but logical L3 routing doesn't check. local inport=$1 src_mac=$2 dst_mac=$3 src_ip=$4 dst_ip=$5 local packet=${dst_mac}${src_mac}08004500001c0000000040110000${src_ip}${dst_ip}0035111100080000 shift; shift; shift; shift; shift hv=hv`vif_to_hv $inport` as $hv ovs-appctl netdev-dummy/receive vif$inport $packet #as $hv ovs-appctl ofproto/trace br-int in_port=$inport $packet in_ls=`vif_to_ls $inport` in_lrp=`vif_to_lrp $inport` for outport; do out_ls=`vif_to_ls $outport` if test $in_ls = $out_ls; then # Ports on the same logical switch receive exactly the same packet. echo $packet else # Routing decrements TTL and updates source and dest MAC # (and checksum). out_lrp=`vif_to_lrp $outport` echo f00000000${outport}00000000ff${out_lrp}08004500001c00000000"3f1101"00${src_ip}${dst_ip}0035111100080000 fi >> $outport.expected done } as hv1 ovs-vsctl --columns=name,ofport list interface as hv1 ovn-sbctl list port_binding as hv1 ovn-sbctl list datapath_binding as hv1 ovn-sbctl dump-flows as hv1 ovs-ofctl dump-flows br-int # Send IP packets between all pairs of source and destination ports: # # 1. Unicast IP packets are delivered to exactly one logical switch port # (except that packets destined to their input ports are dropped). # # 2. Broadcast IP packets are delivered to all logical switch ports # except the input port. ip_to_hex() { printf "%02x%02x%02x%02x" "$@" } for is in 1 2 3; do for js in 1 2 3; do for ks in 1 2 3; do bcast= s=$is$js$ks smac=f00000000$s sip=`ip_to_hex 192 168 $is$js $ks` for id in 1 2 3; do for jd in 1 2 3; do for kd in 1 2 3; do d=$id$jd$kd dip=`ip_to_hex 192 168 $id$jd $kd` if test $is = $id; then dmac=f00000000$d; else dmac=00000000ff$is$js; fi if test $d != $s; then unicast=$d; else unicast=; fi test_ip $s $smac $dmac $sip $dip $unicast #1 if test $id = $is && test $d != $s; then bcast="$bcast $d"; fi done done done test_ip $s $smac ffffffffffff $sip ffffffff $bcast #2 done done done # 3. Send an IP packet from every logical port to every other subnet, # to an IP address that does not have a static IP-MAC binding. # This should generate a broadcast ARP request for the destination # IP address in the destination subnet. for is in 1 2 3; do for js in 1 2 3; do for ks in 1 2 3; do s=$is$js$ks smac=f00000000$s sip=`ip_to_hex 192 168 $is$js $ks` for id in 1 2 3; do for jd in 1 2 3; do if test $is$js = $id$jd; then continue fi # Send the packet. dmac=00000000ff$is$js # Calculate a 4th octet for the destination that is # unique per $s, avoids the .1 .2 .3 and .254 IP addresses # that have static MAC bindings, and fits in the range # 0-255. o4=`expr $is '*' 9 + $js '*' 3 + $ks + 10` dip=`ip_to_hex 192 168 $id$jd $o4` test_ip $s $smac $dmac $sip $dip # Every LP on the destination subnet's lswitch should # receive the ARP request. lrmac=00000000ff$id$jd lrip=`ip_to_hex 192 168 $id$jd 254` arp=ffffffffffff${lrmac}08060001080006040001${lrmac}${lrip}000000000000${dip} for jd2 in 1 2 3; do for kd in 1 2 3; do echo $arp >> $id$jd2$kd.expected done done done done done done done # test_arp INPORT SHA SPA TPA [REPLY_HA] # # Causes a packet to be received on INPORT. The packet is an ARP # request with SHA, SPA, and TPA as specified. If REPLY_HA is provided, then # it should be the hardware address of the target to expect to receive in an # ARP reply; otherwise no reply is expected. # # INPORT is an logical switch port number, e.g. 11 for vif11. # SHA and REPLY_HA are each 12 hex digits. # SPA and TPA are each 8 hex digits. test_arp() { local inport=$1 sha=$2 spa=$3 tpa=$4 reply_ha=$5 local request=ffffffffffff${sha}08060001080006040001${sha}${spa}ffffffffffff${tpa} hv=hv`vif_to_hv $inport` as $hv ovs-appctl netdev-dummy/receive vif$inport $request as $hv ovs-appctl ofproto/trace br-int in_port=$inport $request # Expect to receive the broadcast ARP on the other logical switch ports if # IP address is not configured to the switch patch port. local i=`vif_to_ls $inport` local j k for j in 1 2 3; do for k in 1 2 3; do # 192.168.33.254 is configured to the switch patch port for lrp33, # so no ARP flooding expected for it. if test $i$j$k != $inport && test $tpa != `ip_to_hex 192 168 33 254`; then echo $request >> $i$j$k.expected fi done done # Expect to receive the reply, if any. if test X$reply_ha != X; then lrp=`vif_to_lrp $inport` local reply=${sha}00000000ff${lrp}08060001080006040002${reply_ha}${tpa}${sha}${spa} echo $reply >> $inport.expected fi } # Test router replies to ARP requests from all source ports: # # 4. Router replies to query for its MAC address from port's own IP address. # # 5. Router replies to query for its MAC address from any random IP address # in its subnet. # # 6. Router replies to query for its MAC address from another subnet. # # 7. No reply to query for IP address other than router IP. for i in 1 2 3; do for j in 1 2 3; do for k in 1 2 3; do smac=f00000000$i$j$k # Source MAC sip=`ip_to_hex 192 168 $i$j $k` # Source IP rip=`ip_to_hex 192 168 $i$j 254` # Router IP rmac=00000000ff$i$j # Router MAC otherip=`ip_to_hex 192 168 $i$j 55` # Some other IP in subnet test_arp $i$j$k $smac $sip $rip $rmac #4 test_arp $i$j$k $smac $otherip $rip $rmac #5 test_arp $i$j$k $smac 0a123456 $rip $rmac #6 test_arp $i$j$k $smac $sip $otherip #7 done done done # Allow some time for packet forwarding. # XXX This can be improved. sleep 1 # 8. Generate an ARP reply for each of the IP addresses ARPed for # earlier as #3. # # Here, the $s is the VIF that originated the ARP request and $d is # the VIF that sends the ARP reply, which is somewhat backward but # it means that $s and $d are the same as #3. : > mac_bindings.expected for is in 1 2 3; do for js in 1 2 3; do for ks in 1 2 3; do s=$is$js$ks for id in 1 2 3; do for jd in 1 2 3; do if test $is$js = $id$jd; then continue fi kd=1 d=$id$jd$kd o4=`expr $is '*' 9 + $js '*' 3 + $ks + 10` host_ip=`ip_to_hex 192 168 $id$jd $o4` host_mac=8000000000$o4 lrmac=00000000ff$id$jd lrip=`ip_to_hex 192 168 $id$jd 254` arp=${lrmac}${host_mac}08060001080006040002${host_mac}${host_ip}${lrmac}${lrip} echo echo echo hv=hv`vif_to_hv $d` as $hv ovs-appctl netdev-dummy/receive vif$d $arp #as $hv ovs-appctl ofproto/trace br-int in_port=$d $arp #as $hv ovs-ofctl dump-flows br-int table=19 host_ip_pretty=192.168.$id$jd.$o4 host_mac_pretty=80:00:00:00:00:$o4 echo lrp$id$jd,$host_ip_pretty,$host_mac_pretty >> mac_bindings.expected done done done done done # Allow some time for packet forwarding. # XXX This can be improved. sleep 1 # 9. Send an IP packet from every logical port to every other subnet. These # are the same packets already sent as #3, but now the destinations' IP-MAC # bindings have been discovered via ARP, so instead of provoking an ARP # request, these packets now get routed to their destinations (which don't # have static MAC bindings, so they go to the port we've designated as # accepting "unknown" MACs.) for is in 1 2 3; do for js in 1 2 3; do for ks in 1 2 3; do s=$is$js$ks smac=f00000000$s sip=`ip_to_hex 192 168 $is$js $ks` for id in 1 2 3; do for jd in 1 2 3; do if test $is$js = $id$jd; then continue fi # Send the packet. dmac=00000000ff$is$js # Calculate a 4th octet for the destination that is # unique per $s, avoids the .1 .2 .3 and .254 IP addresses # that have static MAC bindings, and fits in the range # 0-255. o4=`expr $is '*' 9 + $js '*' 3 + $ks + 10` dip=`ip_to_hex 192 168 $id$jd $o4` test_ip $s $smac $dmac $sip $dip # Expect the packet egress. host_mac=8000000000$o4 outport=${id}11 out_lrp=$id$jd echo ${host_mac}00000000ff${out_lrp}08004500001c00000000"3f1101"00${sip}${dip}0035111100080000 >> $outport.expected done done done done done ovn-sbctl -f csv -d bare --no-heading \ -- --columns=logical_port,ip,mac list mac_binding > mac_bindings # Now check the packets actually received against the ones expected. for i in 1 2 3; do for j in 1 2 3; do for k in 1 2 3; do OVN_CHECK_PACKETS([hv`vif_to_hv $i$j$k`/vif$i$j$k-tx.pcap], [$i$j$k.expected]) done done done # Check the MAC bindings against those expected. AT_CHECK_UNQUOTED([sort < mac_bindings], [0], [`sort < mac_bindings.expected` ]) # Gracefully terminate daemons OVN_CLEANUP([hv1], [hv2], [hv3]) AT_CLEANUP # 3 hypervisors, one logical switch, 3 logical ports per hypervisor AT_SETUP([ovn -- portsecurity : 3 HVs, 1 LS, 3 lports/HV]) AT_SKIP_IF([test $HAVE_PYTHON = no]) ovn_start # Create hypervisors hv[123]. # Add vif1[123] to hv1, vif2[123] to hv2, vif3[123] to hv3. # Add all of the vifs to a single logical switch lsw0. # Turn off port security on vifs vif[123]1 # Turn on l2 port security on vifs vif[123]2 # Turn of l2 and l3 port security on vifs vif[123]3 # Make vif13, vif2[23], vif3[123] destinations for unknown MACs. ovn-nbctl ls-add lsw0 net_add n1 for i in 1 2 3; do sim_add hv$i as hv$i ovs-vsctl add-br br-phys ovn_attach n1 br-phys 192.168.0.$i for j in 1 2 3; do ovs-vsctl add-port br-int vif$i$j -- set Interface vif$i$j external-ids:iface-id=lp$i$j options:tx_pcap=hv$i/vif$i$j-tx.pcap options:rxq_pcap=hv$i/vif$i$j-rx.pcap ofport-request=$i$j ovn-nbctl lsp-add lsw0 lp$i$j if test $j = 1; then ovn-nbctl lsp-set-addresses lp$i$j "f0:00:00:00:00:$i$j 192.168.0.$i$j" unknown elif test $j = 2; then ovn-nbctl lsp-set-addresses lp$i$j "f0:00:00:00:00:$i$j 192.168.0.$i$j" ovn-nbctl lsp-set-port-security lp$i$j f0:00:00:00:00:$i$j else extra_addr="f0:00:00:00:0$i:$i$j fe80::ea2a:eaff:fe28:$i$j" ovn-nbctl lsp-set-addresses lp$i$j "f0:00:00:00:00:$i$j 192.168.0.$i$j" "$extra_addr" ovn-nbctl lsp-set-port-security lp$i$j "f0:00:00:00:00:$i$j 192.168.0.$i$j" "$extra_addr" fi done done # Pre-populate the hypervisors' ARP tables so that we don't lose any # packets for ARP resolution (native tunneling doesn't queue packets # for ARP resolution). ovn_populate_arp # Allow some time for ovn-northd and ovn-controller to catch up. # XXX This should be more systematic. sleep 1 # Given the name of a logical port, prints the name of the hypervisor # on which it is located. vif_to_hv() { echo hv${1%?} } for i in 1 2 3; do for j in 1 2 3; do : > $i$j.expected done done # test_ip INPORT SRC_MAC DST_MAC SRC_IP DST_IP OUTPORT... # # This shell function causes an ip packet to be received on INPORT. # The packet's content has Ethernet destination DST and source SRC # (each exactly 12 hex digits) and Ethernet type ETHTYPE (4 hex digits). # The OUTPORTs (zero or more) list the VIFs on which the packet should # be received. INPORT and the OUTPORTs are specified as logical switch # port numbers, e.g. 11 for vif11. test_ip() { # This packet has bad checksums but logical L3 routing doesn't check. local inport=$1 src_mac=$2 dst_mac=$3 src_ip=$4 dst_ip=$5 local packet=${dst_mac}${src_mac}08004500001c0000000040110000${src_ip}${dst_ip}0035111100080000 shift; shift; shift; shift; shift hv=`vif_to_hv $inport` as $hv ovs-appctl netdev-dummy/receive vif$inport $packet #as $hv ovs-appctl ofproto/trace br-int in_port=$inport $packet for outport; do echo $packet >> $outport.expected done } # test_arp INPORT SHA SPA TPA DROP [REPLY_HA] # # Causes a packet to be received on INPORT. The packet is an ARP # request with SHA, SPA, and TPA as specified. If REPLY_HA is provided, then # it should be the hardware address of the target to expect to receive in an # ARP reply; otherwise no reply is expected. # # INPORT is an logical switch port number, e.g. 11 for vif11. # SHA and REPLY_HA are each 12 hex digits. # SPA and TPA are each 8 hex digits. test_arp() { local inport=$1 smac=$2 sha=$3 spa=$4 tpa=$5 drop=$6 reply_ha=$7 local request=ffffffffffff${smac}08060001080006040001${sha}${spa}ffffffffffff${tpa} hv=`vif_to_hv $inport` as $hv ovs-appctl netdev-dummy/receive vif$inport $request #as $hv ovs-appctl ofproto/trace br-int in_port=$inport $request if test $drop != 1; then if test X$reply_ha = X; then # Expect to receive the broadcast ARP on the other logical switch ports # if no reply is expected. local i j for i in 1 2 3; do for j in 1 2 3; do if test $i$j != $inport; then echo $request >> $i$j.expected fi done done else # Expect to receive the reply, if any. local reply=${smac}${reply_ha}08060001080006040002${reply_ha}${tpa}${sha}${spa} echo $reply >> $inport.expected fi fi } # test_ipv6 INPORT SRC_MAC DST_MAC SRC_IP DST_IP OUTPORT... # This function is similar to test_ip() except that it sends # ipv6 packet test_ipv6() { local inport=$1 src_mac=$2 dst_mac=$3 src_ip=$4 dst_ip=$5 local packet=${dst_mac}${src_mac}86dd6000000000083aff${src_ip}${dst_ip}0000000000000000 shift; shift; shift; shift; shift hv=`vif_to_hv $inport` as $hv ovs-appctl netdev-dummy/receive vif$inport $packet #as $hv ovs-appctl ofproto/trace br-int in_port=$inport $packet for outport; do echo $packet >> $outport.expected done } # test_icmpv6 INPORT SRC_MAC DST_MAC SRC_IP DST_IP ICMP_TYPE OUTPORT... # This function is similar to test_ipv6() except it specifies the ICMPv6 type # of the test packet test_icmpv6() { local inport=$1 src_mac=$2 dst_mac=$3 src_ip=$4 dst_ip=$5 icmp_type=$6 local packet=${dst_mac}${src_mac}86dd6000000000083aff${src_ip}${dst_ip}${icmp_type}00000000000000 shift; shift; shift; shift; shift; shift hv=`vif_to_hv $inport` as $hv ovs-appctl netdev-dummy/receive vif$inport $packet #as $hv ovs-appctl ofproto/trace br-int in_port=$inport $packet for outport; do echo $packet >> $outport.expected done } ip_to_hex() { printf "%02x%02x%02x%02x" "$@" } # no port security sip=`ip_to_hex 192 168 0 12` tip=`ip_to_hex 192 168 0 13` # the arp packet should be allowed even if lp[123]1 is # not configured with mac f00000000023 and ip 192.168.0.12 for i in 1 2 3; do test_arp ${i}1 f00000000023 f00000000023 $sip $tip 0 f00000000013 for j in 1 2 3; do if test $i != $j; then test_ip ${i}1 f000000000${i}1 f000000000${j}1 $sip $tip ${j}1 fi done done # l2 port security sip=`ip_to_hex 192 168 0 12` tip=`ip_to_hex 192 168 0 13` # arp packet should be allowed since lp22 is configured with # mac f00000000022 test_arp 22 f00000000022 f00000000022 $sip $tip 0 f00000000013 # arp packet should not be allowed since lp32 is not configured with # mac f00000000021 test_arp 32 f00000000021 f00000000021 $sip $tip 1 # arp packet with sha set to f00000000021 should not be allowed # for lp12 test_arp 12 f00000000012 f00000000021 $sip $tip 1 # ip packets should be allowed and received since lp[123]2 do not # have l3 port security sip=`ip_to_hex 192 168 0 55` tip=`ip_to_hex 192 168 0 66` for i in 1 2 3; do for j in 1 2 3; do if test $i != $j; then test_ip ${i}2 f000000000${i}2 f000000000${j}2 $sip $tip ${j}2 fi done done # ipv6 packets should be received by lp[123]2 # lp[123]1 can send ipv6 traffic as there is no port security sip=fe800000000000000000000000000000 tip=ff020000000000000000000000000000 for i in 1 2 3; do test_ipv6 ${i}1 f000000000${i}1 f000000000${i}2 $sip $tip ${i}2 done # l2 and l3 port security sip=`ip_to_hex 192 168 0 13` tip=`ip_to_hex 192 168 0 22` # arp packet should be allowed since lp13 is configured with # f00000000013 and 192.168.0.13 test_arp 13 f00000000013 f00000000013 $sip $tip 0 f00000000022 # the arp packet should be dropped because lp23 is not configured # with mac f00000000022 sip=`ip_to_hex 192 168 0 13` tip=`ip_to_hex 192 168 0 22` test_arp 23 f00000000022 f00000000022 $sip $tip 1 # the arp packet should be dropped because lp33 is not configured # with ip 192.168.0.55 spa=`ip_to_hex 192 168 0 55` tpa=`ip_to_hex 192 168 0 22` test_arp 33 f00000000031 f00000000031 $spa $tpa 1 # ip packets should not be received by lp[123]3 since # l3 port security is enabled sip=`ip_to_hex 192 168 0 55` tip=`ip_to_hex 192 168 0 66` for i in 1 2 3; do for j in 1 2 3; do test_ip ${i}2 f000000000${i}2 f000000000${j}3 $sip $tip done done # ipv6 packets should be dropped for lp[123]3 since # it is configured with only ipv4 address sip=fe800000000000000000000000000000 tip=ff020000000000000000000000000000 for i in 1 2 3; do test_ipv6 ${i}3 f000000000${i}3 f00000000022 $sip $tip done # ipv6 packets should not be received by lp[123]3 with mac f000000000$[123]3 # lp[123]1 can send ipv6 traffic as there is no port security for i in 1 2 3; do test_ipv6 ${i}1 f000000000${i}1 f000000000${i}3 $sip $tip done # lp13 has extra port security with mac f0000000113 and ipv6 addr # fe80::ea2a:eaff:fe28:0012 # ipv4 packet should be dropped for lp13 with mac f0000000113 sip=`ip_to_hex 192 168 0 13` tip=`ip_to_hex 192 168 0 23` test_ip 13 f00000000113 f00000000023 $sip $tip # ipv6 packet should be received by lp[123]3 with mac f00000000${i}${i}3 # and ip6.dst as fe80::ea2a:eaff:fe28:0${i}${i}3. # lp11 can send ipv6 traffic as there is no port security sip=ee800000000000000000000000000000 for i in 1 2 3; do tip=fe80000000000000ea2aeafffe2800${i}3 test_ipv6 11 f00000000011 f00000000${i}${i}3 $sip $tip ${i}3 done # ipv6 packet should not be received by lp33 with mac f0000000333 # and ip6.dst as fe80::ea2a:eaff:fe28:0023 as it is # configured with fe80::ea2a:eaff:fe28:0033 # lp11 can send ipv6 traffic as there is no port security sip=ee800000000000000000000000000000 tip=fe80000000000000ea2aeafffe280023 test_ipv6 11 f00000000011 f00000000333 $sip $tip # ipv6 packet should be allowed for lp[123]3 with mac f0000000${i}${i}3 # and ip6.src fe80::ea2a:eaff:fe28:0${i}${i}3 and ip6.src ::. # and should be dropped for any other ip6.src # lp21 can receive ipv6 traffic as there is no port security tip=ee800000000000000000000000000000 for i in 1 2 3; do sip=fe80000000000000ea2aeafffe2800${i}3 test_ipv6 ${i}3 f00000000${i}${i}3 f00000000021 $sip $tip 21 # Test ICMPv6 MLD reports (v1 and v2) and NS for DAD sip=00000000000000000000000000000000 test_icmpv6 ${i}3 f00000000${i}${i}3 f00000000021 $sip ff020000000000000000000000160000 83 21 test_icmpv6 ${i}3 f00000000${i}${i}3 f00000000021 $sip ff020000000000000000000000160000 8f 21 test_icmpv6 ${i}3 f00000000${i}${i}3 f00000000021 $sip ff0200000000000000ea2aeafffe2800 87 21 # Traffic to non-multicast traffic should be dropped test_icmpv6 ${i}3 f00000000${i}${i}3 f00000000021 $sip $tip 83 # Traffic of other ICMPv6 types should be dropped test_icmpv6 ${i}3 f00000000${i}${i}3 f00000000021 $sip ff020000000000000000000000160000 80 # should be dropped sip=ae80000000000000ea2aeafffe2800aa test_ipv6 ${i}3 f00000000${i}${i}3 f00000000021 $sip $tip done # configure lsp13 to send and received IPv4 packets with an address range ovn-nbctl lsp-set-port-security lp13 "f0:00:00:00:00:13 192.168.0.13 20.0.0.4/24 10.0.0.0/24" sleep 2 sip=`ip_to_hex 10 0 0 13` tip=`ip_to_hex 192 168 0 22` # arp packet with inner ip 10.0.0.13 should be allowed for lsp13 test_arp 13 f00000000013 f00000000013 $sip $tip 0 f00000000022 sip=`ip_to_hex 10 0 0 14` tip=`ip_to_hex 192 168 0 23` # IPv4 packet from lsp13 with src ip 10.0.0.14 destined to lsp23 # with dst ip 192.168.0.23 should be allowed test_ip 13 f00000000013 f00000000023 $sip $tip 23 sip=`ip_to_hex 192 168 0 33` tip=`ip_to_hex 10 0 0 15` # IPv4 packet from lsp33 with src ip 192.168.0.33 destined to lsp13 # with dst ip 10.0.0.15 should be received by lsp13 test_ip 33 f00000000033 f00000000013 $sip $tip 13 sip=`ip_to_hex 192 168 0 33` tip=`ip_to_hex 20 0 0 4` # IPv4 packet from lsp33 with src ip 192.168.0.33 destined to lsp13 # with dst ip 20.0.0.4 should be received by lsp13 test_ip 33 f00000000033 f00000000013 $sip $tip 13 sip=`ip_to_hex 192 168 0 33` tip=`ip_to_hex 20 0 0 5` # IPv4 packet from lsp33 with src ip 192.168.0.33 destined to lsp13 # with dst ip 20.0.0.5 should not be received by lsp13 test_ip 33 f00000000033 f00000000013 $sip $tip sip=`ip_to_hex 192 168 0 33` tip=`ip_to_hex 20 0 0 255` # IPv4 packet from lsp33 with src ip 192.168.0.33 destined to lsp13 # with dst ip 20.0.0.255 should be received by lsp13 test_ip 33 f00000000033 f00000000013 $sip $tip 13 sip=`ip_to_hex 192 168 0 33` tip=`ip_to_hex 192 168 0 255` # IPv4 packet from lsp33 with src ip 192.168.0.33 destined to lsp13 # with dst ip 192.168.0.255 should not be received by lsp13 test_ip 33 f00000000033 f00000000013 $sip $tip sip=`ip_to_hex 192 168 0 33` tip=`ip_to_hex 224 0 0 4` # IPv4 packet from lsp33 with src ip 192.168.0.33 destined to lsp13 # with dst ip 224.0.0.4 should be received by lsp13 test_ip 33 f00000000033 f00000000013 $sip $tip 13 #dump information including flow counters ovn-nbctl show ovn-sbctl dump-flows -- list multicast_group echo "------ hv1 dump ------" as hv1 ovs-vsctl show as hv1 ovs-ofctl -O OpenFlow13 show br-int as hv1 ovs-ofctl -O OpenFlow13 dump-flows br-int echo "------ hv2 dump ------" as hv2 ovs-vsctl show as hv2 ovs-ofctl -O OpenFlow13 show br-int as hv2 ovs-ofctl -O OpenFlow13 dump-flows br-int echo "------ hv3 dump ------" as hv3 ovs-vsctl show as hv3 ovs-ofctl -O OpenFlow13 show br-int as hv3 ovs-ofctl -O OpenFlow13 dump-flows br-int # Now check the packets actually received against the ones expected. for i in 1 2 3; do for j in 1 2 3; do OVN_CHECK_PACKETS([hv$i/vif$i$j-tx.pcap], [$i$j.expected]) done done OVN_CLEANUP([hv1],[hv2],[hv3]) AT_CLEANUP AT_SETUP([ovn -- 2 HVs, 2 LS, 1 lport/LS, 2 peer LRs]) AT_SKIP_IF([test $HAVE_PYTHON = no]) ovn_start # Logical network: # Two LRs - R1 and R2 that are connected to each other as peers in 20.0.0.0/24 # network. R1 has a switchs ls1 (191.168.1.0/24) connected to it. # R2 has ls2 (172.16.1.0/24) connected to it. ls1_lp1_mac="f0:00:00:01:02:03" rp_ls1_mac="00:00:00:01:02:03" rp_ls2_mac="00:00:00:01:02:04" ls2_lp1_mac="f0:00:00:01:02:04" ls1_lp1_ip="192.168.1.2" ls2_lp1_ip="172.16.1.2" ovn-nbctl lr-add R1 ovn-nbctl lr-add R2 ovn-nbctl ls-add ls1 ovn-nbctl ls-add ls2 # Connect ls1 to R1 ovn-nbctl lrp-add R1 ls1 $rp_ls1_mac 192.168.1.1/24 ovn-nbctl lsp-add ls1 rp-ls1 -- set Logical_Switch_Port rp-ls1 type=router \ options:router-port=ls1 addresses=\"$rp_ls1_mac\" # Connect ls2 to R2 ovn-nbctl lrp-add R2 ls2 $rp_ls2_mac 172.16.1.1/24 ovn-nbctl lsp-add ls2 rp-ls2 -- set Logical_Switch_Port rp-ls2 type=router \ options:router-port=ls2 addresses=\"$rp_ls2_mac\" # Connect R1 to R2 ovn-nbctl lrp-add R1 R1_R2 00:00:00:02:03:04 20.0.0.1/24 peer=R2_R1 ovn-nbctl lrp-add R2 R2_R1 00:00:00:02:03:05 20.0.0.2/24 peer=R1_R2 ovn-nbctl lr-route-add R1 "0.0.0.0/0" 20.0.0.2 ovn-nbctl lr-route-add R2 "0.0.0.0/0" 20.0.0.1 # Create logical port ls1-lp1 in ls1 ovn-nbctl lsp-add ls1 ls1-lp1 \ -- lsp-set-addresses ls1-lp1 "$ls1_lp1_mac $ls1_lp1_ip" # Create logical port ls2-lp1 in ls2 ovn-nbctl lsp-add ls2 ls2-lp1 \ -- lsp-set-addresses ls2-lp1 "$ls2_lp1_mac $ls2_lp1_ip" # Create two hypervisor and create OVS ports corresponding to logical ports. net_add n1 sim_add hv1 as hv1 ovs-vsctl add-br br-phys ovn_attach n1 br-phys 192.168.0.1 ovs-vsctl -- add-port br-int hv1-vif1 -- \ set interface hv1-vif1 external-ids:iface-id=ls1-lp1 \ options:tx_pcap=hv1/vif1-tx.pcap \ options:rxq_pcap=hv1/vif1-rx.pcap \ ofport-request=1 sim_add hv2 as hv2 ovs-vsctl add-br br-phys ovn_attach n1 br-phys 192.168.0.2 ovs-vsctl -- add-port br-int hv2-vif1 -- \ set interface hv2-vif1 external-ids:iface-id=ls2-lp1 \ options:tx_pcap=hv2/vif1-tx.pcap \ options:rxq_pcap=hv2/vif1-rx.pcap \ ofport-request=1 # Pre-populate the hypervisors' ARP tables so that we don't lose any # packets for ARP resolution (native tunneling doesn't queue packets # for ARP resolution). ovn_populate_arp # Allow some time for ovn-northd and ovn-controller to catch up. # XXX This should be more systematic. sleep 1 # Packet to send. packet="inport==\"ls1-lp1\" && eth.src==$ls1_lp1_mac && eth.dst==$rp_ls1_mac && ip4 && ip.ttl==64 && ip4.src==$ls1_lp1_ip && ip4.dst==$ls2_lp1_ip && udp && udp.src==53 && udp.dst==4369" as hv1 ovs-appctl -t ovn-controller inject-pkt "$packet" echo "---------NB dump-----" ovn-nbctl show echo "---------------------" ovn-nbctl list logical_router echo "---------------------" ovn-nbctl list logical_router_port echo "---------------------" echo "---------SB dump-----" ovn-sbctl list datapath_binding echo "---------------------" ovn-sbctl list port_binding echo "---------------------" echo "------ hv1 dump ----------" as hv1 ovs-ofctl show br-int as hv1 ovs-ofctl dump-flows br-int echo "------ hv2 dump ----------" as hv2 ovs-ofctl show br-int as hv2 ovs-ofctl dump-flows br-int # Packet to Expect # The TTL should be decremented by 2. packet="eth.src==$rp_ls2_mac && eth.dst==$ls2_lp1_mac && ip4 && ip.ttl==62 && ip4.src==$ls1_lp1_ip && ip4.dst==$ls2_lp1_ip && udp && udp.src==53 && udp.dst==4369" echo $packet | ovstest test-ovn expr-to-packets > expected OVN_CHECK_PACKETS([hv2/vif1-tx.pcap], [expected]) OVN_CLEANUP([hv1],[hv2]) AT_CLEANUP AT_SETUP([ovn -- 1 HV, 1 LS, 2 lport/LS, 1 LR]) AT_KEYWORDS([router-admin-state]) AT_SKIP_IF([test $HAVE_PYTHON = no]) ovn_start # Logical network: # One LR - R1 has switch ls1 with two subnets attached to it (191.168.1.0/24 # and 172.16.1.0/24) connected to it. ovn-nbctl lr-add R1 ovn-nbctl ls-add ls1 # Connect ls1 to R1 ovn-nbctl lrp-add R1 ls1 00:00:00:01:02:03 192.168.1.1/24 172.16.1.1/24 ovn-nbctl lsp-add ls1 rp-ls1 -- set Logical_Switch_Port rp-ls1 type=router \ options:router-port=ls1 addresses=\"00:00:00:01:02:03\" # Create logical port ls1-lp1 in ls1 ovn-nbctl lsp-add ls1 ls1-lp1 \ -- lsp-set-addresses ls1-lp1 "f0:00:00:01:02:03 192.168.1.2" # Create logical port ls1-lp2 in ls1 ovn-nbctl lsp-add ls1 ls1-lp2 \ -- lsp-set-addresses ls1-lp2 "f0:00:00:01:02:04 172.16.1.2" # Create one hypervisor and create OVS ports corresponding to logical ports. net_add n1 sim_add hv1 as hv1 ovs-vsctl add-br br-phys ovn_attach n1 br-phys 192.168.0.1 ovs-vsctl -- add-port br-int vif1 -- \ set interface vif1 external-ids:iface-id=ls1-lp1 \ options:tx_pcap=hv1/vif1-tx.pcap \ options:rxq_pcap=hv1/vif1-rx.pcap \ ofport-request=1 ovs-vsctl -- add-port br-int vif2 -- \ set interface vif2 external-ids:iface-id=ls1-lp2 \ options:tx_pcap=hv1/vif2-tx.pcap \ options:rxq_pcap=hv1/vif2-rx.pcap \ ofport-request=1 # Allow some time for ovn-northd and ovn-controller to catch up. # XXX This should be more systematic. sleep 1 # Send ip packets between the two ports. ip_to_hex() { printf "%02x%02x%02x%02x" "$@" } # Packet to send. src_mac="f00000010203" dst_mac="000000010203" src_ip=`ip_to_hex 192 168 1 2` dst_ip=`ip_to_hex 172 16 1 2` packet=${dst_mac}${src_mac}08004500001c0000000040110000${src_ip}${dst_ip}0035111100080000 as hv1 ovs-appctl netdev-dummy/receive vif1 $packet echo "---------NB dump-----" ovn-nbctl show echo "---------------------" ovn-nbctl list logical_router echo "---------------------" ovn-nbctl list logical_router_port echo "---------------------" echo "---------SB dump-----" ovn-sbctl list datapath_binding echo "---------------------" ovn-sbctl list logical_flow echo "---------------------" echo "------ hv1 dump ----------" as hv1 ovs-ofctl dump-flows br-int #Disable router R1 ovn-nbctl set Logical_Router R1 enabled=false # Allow some time for ovn-northd and ovn-controller to catch up. # XXX This should be more systematic. sleep 1 echo "---------SB dump-----" ovn-sbctl list datapath_binding echo "---------------------" ovn-sbctl list logical_flow echo "---------------------" echo "------ hv1 dump ----------" as hv1 ovs-ofctl dump-flows br-int as hv1 ovs-appctl netdev-dummy/receive vif1 $packet # Packet to Expect expect_src_mac="000000010203" expect_dst_mac="f00000010204" echo "${expect_dst_mac}${expect_src_mac}08004500001c000000003f110100${src_ip}${dst_ip}0035111100080000" > expected OVN_CHECK_PACKETS([hv1/vif2-tx.pcap], [expected]) as hv1 OVS_APP_EXIT_AND_WAIT([ovn-controller]) OVS_APP_EXIT_AND_WAIT([ovs-vswitchd]) OVS_APP_EXIT_AND_WAIT([ovsdb-server]) as ovn-sb OVS_APP_EXIT_AND_WAIT([ovsdb-server]) as ovn-nb OVS_APP_EXIT_AND_WAIT([ovsdb-server]) as northd OVS_APP_EXIT_AND_WAIT([ovn-northd]) as main OVS_APP_EXIT_AND_WAIT([ovs-vswitchd]) OVS_APP_EXIT_AND_WAIT([ovsdb-server]) AT_CLEANUP AT_SETUP([ovn -- 1 HV, 2 LSs, 1 lport/LS, 1 LR]) AT_KEYWORDS([router-admin-state]) AT_SKIP_IF([test $HAVE_PYTHON = no]) ovn_start # Logical network: # One LR - R1 has switch ls1 (191.168.1.0/24) connected to it, # and has switch ls2 (172.16.1.0/24) connected to it. ovn-nbctl lr-add R1 ovn-nbctl ls-add ls1 ovn-nbctl ls-add ls2 # Connect ls1 to R1 ovn-nbctl lrp-add R1 ls1 00:00:00:01:02:03 192.168.1.1/24 ovn-nbctl lsp-add ls1 rp-ls1 -- set Logical_Switch_Port rp-ls1 type=router \ options:router-port=ls1 addresses=\"00:00:00:01:02:03\" # Connect ls2 to R1 ovn-nbctl lrp-add R1 ls2 00:00:00:01:02:04 172.16.1.1/24 ovn-nbctl lsp-add ls2 rp-ls2 -- set Logical_Switch_Port rp-ls2 type=router \ options:router-port=ls2 addresses=\"00:00:00:01:02:04\" # Create logical port ls1-lp1 in ls1 ovn-nbctl lsp-add ls1 ls1-lp1 \ -- lsp-set-addresses ls1-lp1 "f0:00:00:01:02:03 192.168.1.2" # Create logical port ls2-lp1 in ls2 ovn-nbctl lsp-add ls2 ls2-lp1 \ -- lsp-set-addresses ls2-lp1 "f0:00:00:01:02:04 172.16.1.2" # Create one hypervisor and create OVS ports corresponding to logical ports. net_add n1 sim_add hv1 as hv1 ovs-vsctl add-br br-phys ovn_attach n1 br-phys 192.168.0.1 ovs-vsctl -- add-port br-int vif1 -- \ set interface vif1 external-ids:iface-id=ls1-lp1 \ options:tx_pcap=hv1/vif1-tx.pcap \ options:rxq_pcap=hv1/vif1-rx.pcap \ ofport-request=1 ovs-vsctl -- add-port br-int vif2 -- \ set interface vif2 external-ids:iface-id=ls2-lp1 \ options:tx_pcap=hv1/vif2-tx.pcap \ options:rxq_pcap=hv1/vif2-rx.pcap \ ofport-request=1 # Allow some time for ovn-northd and ovn-controller to catch up. # XXX This should be more systematic. sleep 1 # Send ip packets between the two ports. ip_to_hex() { printf "%02x%02x%02x%02x" "$@" } # Packet to send. src_mac="f00000010203" dst_mac="000000010203" src_ip=`ip_to_hex 192 168 1 2` dst_ip=`ip_to_hex 172 16 1 2` packet=${dst_mac}${src_mac}08004500001c0000000040110000${src_ip}${dst_ip}0035111100080000 as hv1 ovs-appctl netdev-dummy/receive vif1 $packet echo "---------NB dump-----" ovn-nbctl show echo "---------------------" ovn-nbctl list logical_router echo "---------------------" ovn-nbctl list logical_router_port echo "---------------------" echo "---------SB dump-----" ovn-sbctl list datapath_binding echo "---------------------" ovn-sbctl list logical_flow echo "---------------------" echo "------ hv1 dump ----------" as hv1 ovs-ofctl dump-flows br-int #Disable router R1 ovn-nbctl set Logical_Router R1 enabled=false echo "---------SB dump-----" ovn-sbctl list datapath_binding echo "---------------------" ovn-sbctl list logical_flow echo "---------------------" echo "------ hv1 dump ----------" as hv1 ovs-ofctl dump-flows br-int # Allow some time for the disabling of logical router R1 to propagate. # XXX This should be more systematic. sleep 1 as hv1 ovs-appctl netdev-dummy/receive vif1 $packet # Packet to Expect expect_src_mac="000000010204" expect_dst_mac="f00000010204" echo "${expect_dst_mac}${expect_src_mac}08004500001c000000003f110100${src_ip}${dst_ip}0035111100080000" > expected OVN_CHECK_PACKETS([hv1/vif2-tx.pcap], [expected]) OVN_CLEANUP([hv1]) AT_CLEANUP AT_SETUP([ovn -- 2 HVs, 3 LS, 1 lport/LS, 2 peer LRs, static routes]) AT_SKIP_IF([test $HAVE_PYTHON = no]) ovn_start # Logical network: # Two LRs - R1 and R2 that are connected to each other as peers in 20.0.0.0/24 # network. R1 has switchess foo (192.168.1.0/24) # connected to it. # R2 has alice (172.16.1.0/24) and bob (172.16.2.0/24) connected to it. ovn-nbctl lr-add R1 ovn-nbctl lr-add R2 ovn-nbctl ls-add foo ovn-nbctl ls-add alice ovn-nbctl ls-add bob # Connect foo to R1 ovn-nbctl lrp-add R1 foo 00:00:00:01:02:03 192.168.1.1/24 ovn-nbctl lsp-add foo rp-foo -- set Logical_Switch_Port rp-foo type=router \ options:router-port=foo addresses=\"00:00:00:01:02:03\" # Connect alice to R2 ovn-nbctl lrp-add R2 alice 00:00:00:01:02:04 172.16.1.1/24 ovn-nbctl lsp-add alice rp-alice -- set Logical_Switch_Port rp-alice \ type=router options:router-port=alice addresses=\"00:00:00:01:02:04\" # Connect bob to R2 ovn-nbctl lrp-add R2 bob 00:00:00:01:02:05 172.16.2.1/24 ovn-nbctl lsp-add bob rp-bob -- set Logical_Switch_Port rp-bob type=router \ options:router-port=bob addresses=\"00:00:00:01:02:05\" # Connect R1 to R2 ovn-nbctl lrp-add R1 R1_R2 00:00:00:02:03:04 20.0.0.1/24 peer=R2_R1 ovn-nbctl lrp-add R2 R2_R1 00:00:00:02:03:05 20.0.0.2/24 peer=R1_R2 #install static routes ovn-nbctl lr-route-add R1 172.16.1.0/24 20.0.0.2 ovn-nbctl lr-route-add R2 172.16.2.0/24 20.0.0.2 R1_R2 ovn-nbctl lr-route-add R2 192.168.1.0/24 20.0.0.1 # Create logical port foo1 in foo ovn-nbctl lsp-add foo foo1 \ -- lsp-set-addresses foo1 "f0:00:00:01:02:03 192.168.1.2" # Create logical port alice1 in alice ovn-nbctl lsp-add alice alice1 \ -- lsp-set-addresses alice1 "f0:00:00:01:02:04 172.16.1.2" # Create logical port bob1 in bob ovn-nbctl lsp-add bob bob1 \ -- lsp-set-addresses bob1 "f0:00:00:01:02:05 172.16.2.2" # Create two hypervisor and create OVS ports corresponding to logical ports. net_add n1 sim_add hv1 as hv1 ovs-vsctl add-br br-phys ovn_attach n1 br-phys 192.168.0.1 ovs-vsctl -- add-port br-int hv1-vif1 -- \ set interface hv1-vif1 external-ids:iface-id=foo1 \ options:tx_pcap=hv1/vif1-tx.pcap \ options:rxq_pcap=hv1/vif1-rx.pcap \ ofport-request=1 ovs-vsctl -- add-port br-int hv1-vif2 -- \ set interface hv1-vif2 external-ids:iface-id=alice1 \ options:tx_pcap=hv1/vif2-tx.pcap \ options:rxq_pcap=hv1/vif2-rx.pcap \ ofport-request=2 sim_add hv2 as hv2 ovs-vsctl add-br br-phys ovn_attach n1 br-phys 192.168.0.2 ovs-vsctl -- add-port br-int hv2-vif1 -- \ set interface hv2-vif1 external-ids:iface-id=bob1 \ options:tx_pcap=hv2/vif1-tx.pcap \ options:rxq_pcap=hv2/vif1-rx.pcap \ ofport-request=1 # Pre-populate the hypervisors' ARP tables so that we don't lose any # packets for ARP resolution (native tunneling doesn't queue packets # for ARP resolution). ovn_populate_arp # Allow some time for ovn-northd and ovn-controller to catch up. # XXX This should be more systematic. sleep 1 ip_to_hex() { printf "%02x%02x%02x%02x" "$@" } # Send ip packets between foo1 and alice1 src_mac="f00000010203" dst_mac="000000010203" src_ip=`ip_to_hex 192 168 1 2` dst_ip=`ip_to_hex 172 16 1 2` packet=${dst_mac}${src_mac}08004500001c0000000040110000${src_ip}${dst_ip}0035111100080000 as hv1 ovs-appctl netdev-dummy/receive hv1-vif1 $packet # Send ip packets between foo1 and bob1 src_mac="f00000010203" dst_mac="000000010203" src_ip=`ip_to_hex 192 168 1 2` dst_ip=`ip_to_hex 172 16 2 2` packet=${dst_mac}${src_mac}08004500001c0000000040110000${src_ip}${dst_ip}0035111100080000 as hv1 ovs-appctl netdev-dummy/receive hv1-vif1 $packet echo "---------NB dump-----" ovn-nbctl show echo "---------------------" ovn-nbctl list logical_router echo "---------------------" ovn-nbctl list logical_router_port echo "---------------------" echo "---------SB dump-----" ovn-sbctl list datapath_binding echo "---------------------" ovn-sbctl list port_binding echo "---------------------" echo "------ hv1 dump ----------" as hv1 ovs-ofctl dump-flows br-int echo "------ hv2 dump ----------" as hv2 ovs-ofctl dump-flows br-int # Packet to Expect at bob1 src_mac="000000010205" dst_mac="f00000010205" src_ip=`ip_to_hex 192 168 1 2` dst_ip=`ip_to_hex 172 16 2 2` echo "${dst_mac}${src_mac}08004500001c000000003e110200${src_ip}${dst_ip}0035111100080000" > expected OVN_CHECK_PACKETS([hv2/vif1-tx.pcap], [expected]) # Packet to Expect at alice1 src_mac="000000010204" dst_mac="f00000010204" src_ip=`ip_to_hex 192 168 1 2` dst_ip=`ip_to_hex 172 16 1 2` echo "${dst_mac}${src_mac}08004500001c000000003e110200${src_ip}${dst_ip}0035111100080000" > expected OVN_CHECK_PACKETS([hv1/vif2-tx.pcap], [expected]) OVN_CLEANUP([hv1],[hv2]) AT_CLEANUP AT_SETUP([ovn -- send gratuitous arp on localnet]) AT_SKIP_IF([test $HAVE_PYTHON = no]) ovn_start ovn-nbctl ls-add lsw0 net_add n1 sim_add hv as hv ovs-vsctl \ -- add-br br-phys \ -- add-br br-eth0 ovn_attach n1 br-phys 192.168.0.1 AT_CHECK([ovs-vsctl set Open_vSwitch . external-ids:ovn-bridge-mappings=physnet1:br-eth0]) AT_CHECK([ovs-vsctl add-port br-eth0 snoopvif -- set Interface snoopvif options:tx_pcap=hv/snoopvif-tx.pcap options:rxq_pcap=hv/snoopvif-rx.pcap]) # Create a vif. AT_CHECK([ovn-nbctl lsp-add lsw0 localvif1]) AT_CHECK([ovn-nbctl lsp-set-addresses localvif1 "f0:00:00:00:00:01 192.168.1.2"]) AT_CHECK([ovn-nbctl lsp-set-port-security localvif1 "f0:00:00:00:00:01"]) # Create a localnet port. AT_CHECK([ovn-nbctl lsp-add lsw0 ln_port]) AT_CHECK([ovn-nbctl lsp-set-addresses ln_port unknown]) AT_CHECK([ovn-nbctl lsp-set-type ln_port localnet]) AT_CHECK([ovn-nbctl lsp-set-options ln_port network_name=physnet1]) AT_CHECK([ovs-vsctl add-port br-int localvif1 -- set Interface localvif1 external_ids:iface-id=localvif1]) # Wait for packet to be received. echo "fffffffffffff0000000000108060001080006040001f00000000001c0a80102000000000000c0a80102" > expected OVN_CHECK_PACKETS([hv/snoopvif-tx.pcap], [expected]) # Delete the localnet ports. AT_CHECK([ovs-vsctl del-port localvif1]) AT_CHECK([ovn-nbctl lsp-del ln_port]) OVN_CLEANUP([hv]) AT_CLEANUP AT_SETUP([ovn -- 2 HVs, 3 LRs connected via LS, static routes]) AT_SKIP_IF([test $HAVE_PYTHON = no]) ovn_start # Logical network: # Three LRs - R1, R2 and R3 that are connected to each other via LS "join" # in 20.0.0.0/24 network. R1 has switchess foo (192.168.1.0/24) # connected to it. R2 has alice (172.16.1.0/24) and R3 has bob (10.32.1.0/24) # connected to it. ovn-nbctl lr-add R1 ovn-nbctl lr-add R2 ovn-nbctl lr-add R3 ovn-nbctl ls-add foo ovn-nbctl ls-add alice ovn-nbctl ls-add bob ovn-nbctl ls-add join # Connect foo to R1 ovn-nbctl lrp-add R1 foo 00:00:01:01:02:03 192.168.1.1/24 ovn-nbctl lsp-add foo rp-foo -- set Logical_Switch_Port rp-foo type=router \ options:router-port=foo addresses=\"00:00:01:01:02:03\" # Connect alice to R2 ovn-nbctl lrp-add R2 alice 00:00:02:01:02:03 172.16.1.1/24 ovn-nbctl lsp-add alice rp-alice -- set Logical_Switch_Port rp-alice \ type=router options:router-port=alice addresses=\"00:00:02:01:02:03\" # Connect bob to R3 ovn-nbctl lrp-add R3 bob 00:00:03:01:02:03 10.32.1.1/24 ovn-nbctl lsp-add bob rp-bob -- set Logical_Switch_Port rp-bob \ type=router options:router-port=bob addresses=\"00:00:03:01:02:03\" # Connect R1 to join ovn-nbctl lrp-add R1 R1_join 00:00:04:01:02:03 20.0.0.1/24 ovn-nbctl lsp-add join r1-join -- set Logical_Switch_Port r1-join \ type=router options:router-port=R1_join addresses='"00:00:04:01:02:03"' # Connect R2 to join ovn-nbctl lrp-add R2 R2_join 00:00:04:01:02:04 20.0.0.2/24 ovn-nbctl lsp-add join r2-join -- set Logical_Switch_Port r2-join \ type=router options:router-port=R2_join addresses='"00:00:04:01:02:04"' # Connect R3 to join ovn-nbctl lrp-add R3 R3_join 00:00:04:01:02:05 20.0.0.3/24 ovn-nbctl lsp-add join r3-join -- set Logical_Switch_Port r3-join \ type=router options:router-port=R3_join addresses='"00:00:04:01:02:05"' #install static routes ovn-nbctl lr-route-add R1 172.16.1.0/24 20.0.0.2 ovn-nbctl lr-route-add R1 10.32.1.0/24 20.0.0.3 ovn-nbctl lr-route-add R2 192.168.1.0/24 20.0.0.1 ovn-nbctl lr-route-add R2 10.32.1.0/24 20.0.0.3 ovn-nbctl lr-route-add R3 192.168.1.0/24 20.0.0.1 ovn-nbctl lr-route-add R3 172.16.1.0/24 20.0.0.2 # Create logical port foo1 in foo ovn-nbctl lsp-add foo foo1 \ -- lsp-set-addresses foo1 "f0:00:00:01:02:03 192.168.1.2" # Create logical port alice1 in alice ovn-nbctl lsp-add alice alice1 \ -- lsp-set-addresses alice1 "f0:00:00:01:02:04 172.16.1.2" # Create logical port bob1 in bob ovn-nbctl lsp-add bob bob1 \ -- lsp-set-addresses bob1 "f0:00:00:01:02:05 10.32.1.2" # Create two hypervisor and create OVS ports corresponding to logical ports. net_add n1 sim_add hv1 as hv1 ovs-vsctl add-br br-phys ovn_attach n1 br-phys 192.168.0.1 ovs-vsctl -- add-port br-int hv1-vif1 -- \ set interface hv1-vif1 external-ids:iface-id=foo1 \ options:tx_pcap=hv1/vif1-tx.pcap \ options:rxq_pcap=hv1/vif1-rx.pcap \ ofport-request=1 ovs-vsctl -- add-port br-int hv1-vif2 -- \ set interface hv1-vif2 external-ids:iface-id=alice1 \ options:tx_pcap=hv1/vif2-tx.pcap \ options:rxq_pcap=hv1/vif2-rx.pcap \ ofport-request=2 sim_add hv2 as hv2 ovs-vsctl add-br br-phys ovn_attach n1 br-phys 192.168.0.2 ovs-vsctl -- add-port br-int hv2-vif1 -- \ set interface hv2-vif1 external-ids:iface-id=bob1 \ options:tx_pcap=hv2/vif1-tx.pcap \ options:rxq_pcap=hv2/vif1-rx.pcap \ ofport-request=1 # Pre-populate the hypervisors' ARP tables so that we don't lose any # packets for ARP resolution (native tunneling doesn't queue packets # for ARP resolution). ovn_populate_arp # Allow some time for ovn-northd and ovn-controller to catch up. # XXX This should be more systematic. sleep 1 ip_to_hex() { printf "%02x%02x%02x%02x" "$@" } # Send ip packets between foo1 and alice1 src_mac="f00000010203" dst_mac="000001010203" src_ip=`ip_to_hex 192 168 1 2` dst_ip=`ip_to_hex 172 16 1 2` packet=${dst_mac}${src_mac}08004500001c0000000040110000${src_ip}${dst_ip}0035111100080000 as hv1 ovs-appctl netdev-dummy/receive hv1-vif1 $packet as hv1 ovs-appctl ofproto/trace br-int in_port=1 $packet # Send ip packets between foo1 and bob1 src_mac="f00000010203" dst_mac="000001010203" src_ip=`ip_to_hex 192 168 1 2` dst_ip=`ip_to_hex 10 32 1 2` packet=${dst_mac}${src_mac}08004500001c0000000040110000${src_ip}${dst_ip}0035111100080000 as hv1 ovs-appctl netdev-dummy/receive hv1-vif1 $packet echo "---------NB dump-----" ovn-nbctl show echo "---------------------" ovn-nbctl list logical_router echo "---------------------" ovn-nbctl list logical_router_port echo "---------------------" echo "---------SB dump-----" ovn-sbctl list datapath_binding echo "---------------------" ovn-sbctl list port_binding echo "---------------------" ovn-sbctl dump-flows echo "---------------------" echo "------ hv1 dump ----------" as hv1 ovs-ofctl show br-int as hv1 ovs-ofctl dump-flows br-int echo "------ hv2 dump ----------" as hv2 ovs-ofctl show br-int as hv2 ovs-ofctl dump-flows br-int echo "----------------------------" # Packet to Expect at bob1 src_mac="000003010203" dst_mac="f00000010205" src_ip=`ip_to_hex 192 168 1 2` dst_ip=`ip_to_hex 10 32 1 2` echo "${dst_mac}${src_mac}08004500001c000000003e110200${src_ip}${dst_ip}0035111100080000" > expected OVN_CHECK_PACKETS([hv2/vif1-tx.pcap], [expected]) # Packet to Expect at alice1 src_mac="000002010203" dst_mac="f00000010204" src_ip=`ip_to_hex 192 168 1 2` dst_ip=`ip_to_hex 172 16 1 2` echo "${dst_mac}${src_mac}08004500001c000000003e110200${src_ip}${dst_ip}0035111100080000" > expected OVN_CHECK_PACKETS([hv1/vif2-tx.pcap], [expected]) OVN_CLEANUP([hv1],[hv2]) AT_CLEANUP AT_SETUP([ovn -- dhcpv4 : 1 HV, 2 LS, 2 LSPs/LS]) AT_SKIP_IF([test $HAVE_PYTHON = no]) ovn_start ovn-nbctl ls-add ls1 ovn-nbctl lsp-add ls1 ls1-lp1 \ -- lsp-set-addresses ls1-lp1 "f0:00:00:00:00:01 10.0.0.4" ovn-nbctl lsp-set-port-security ls1-lp1 "f0:00:00:00:00:01 10.0.0.4" ovn-nbctl lsp-add ls1 ls1-lp2 \ -- lsp-set-addresses ls1-lp2 "f0:00:00:00:00:02 10.0.0.6 20.0.0.4" ovn-nbctl lsp-set-port-security ls1-lp2 "f0:00:00:00:00:02 10.0.0.6 20.0.0.4" ovn-nbctl ls-add ls2 ovn-nbctl lsp-add ls2 ls2-lp1 \ -- lsp-set-addresses ls2-lp1 "f0:00:00:00:00:03 30.0.0.6 40.0.0.4" ovn-nbctl lsp-set-port-security ls2-lp1 "f0:00:00:00:00:03 30.0.0.6 40.0.0.4" ovn-nbctl lsp-add ls2 ls2-lp2 \ -- lsp-set-addresses ls2-lp2 "f0:00:00:00:00:04 30.0.0.7" ovn-nbctl lsp-set-port-security ls2-lp2 "f0:00:00:00:00:04 30.0.0.7" d1="$(ovn-nbctl create DHCP_Options cidr=10.0.0.0/24 \ options="\"server_id\"=\"10.0.0.1\" \"server_mac\"=\"ff:10:00:00:00:01\" \ \"lease_time\"=\"3600\" \"router\"=\"10.0.0.1\"")" ovn-nbctl lsp-set-dhcpv4-options ls1-lp1 ${d1} ovn-nbctl lsp-set-dhcpv4-options ls1-lp2 ${d1} d2="$(ovn-nbctl create DHCP_Options cidr=30.0.0.0/24 \ options="\"server_id\"=\"30.0.0.1\" \"server_mac\"=\"ff:10:00:00:00:02\" \ \"lease_time\"=\"3600\"")" ovn-nbctl lsp-set-dhcpv4-options ls2-lp2 ${d2} net_add n1 sim_add hv1 as hv1 ovs-vsctl add-br br-phys ovn_attach n1 br-phys 192.168.0.1 ovs-vsctl -- add-port br-int hv1-vif1 -- \ set interface hv1-vif1 external-ids:iface-id=ls1-lp1 \ options:tx_pcap=hv1/vif1-tx.pcap \ options:rxq_pcap=hv1/vif1-rx.pcap \ ofport-request=1 ovs-vsctl -- add-port br-int hv1-vif2 -- \ set interface hv1-vif2 external-ids:iface-id=ls1-lp2 \ options:tx_pcap=hv1/vif2-tx.pcap \ options:rxq_pcap=hv1/vif2-rx.pcap \ ofport-request=2 ovs-vsctl -- add-port br-int hv1-vif3 -- \ set interface hv1-vif3 external-ids:iface-id=ls2-lp1 \ options:tx_pcap=hv1/vif3-tx.pcap \ options:rxq_pcap=hv1/vif3-rx.pcap \ ofport-request=3 ovs-vsctl -- add-port br-int hv1-vif4 -- \ set interface hv1-vif4 external-ids:iface-id=ls2-lp2 \ options:tx_pcap=hv1/vif4-tx.pcap \ options:rxq_pcap=hv1/vif4-rx.pcap \ ofport-request=4 ovn_populate_arp sleep 2 as hv1 ovs-vsctl show # This shell function sends a DHCP request packet # test_dhcp INPORT SRC_MAC DHCP_TYPE OFFER_IP ... test_dhcp() { local inport=$1 src_mac=$2 dhcp_type=$3 offer_ip=$4 use_ip=$5 shift; shift; shift; shift; shift; if test $use_ip != 0; then src_ip=$1 dst_ip=$2 shift; shift; else src_ip=`ip_to_hex 0 0 0 0` dst_ip=`ip_to_hex 255 255 255 255` fi local request=ffffffffffff${src_mac}0800451001100000000080110000${src_ip}${dst_ip} # udp header and dhcp header request=${request}0044004300fc0000 request=${request}010106006359aa760000000000000000000000000000000000000000${src_mac} # client hardware padding request=${request}00000000000000000000 # server hostname request=${request}0000000000000000000000000000000000000000000000000000000000000000 request=${request}0000000000000000000000000000000000000000000000000000000000000000 # boot file name request=${request}0000000000000000000000000000000000000000000000000000000000000000 request=${request}0000000000000000000000000000000000000000000000000000000000000000 request=${request}0000000000000000000000000000000000000000000000000000000000000000 request=${request}0000000000000000000000000000000000000000000000000000000000000000 # dhcp magic cookie request=${request}63825363 # dhcp message type request=${request}3501${dhcp_type}ff if test $offer_ip != 0; then local srv_mac=$1 srv_ip=$2 expected_dhcp_opts=$3 # total IP length will be the IP length of the request packet # (which is 272 in our case) + 8 (padding bytes) + (expected_dhcp_opts / 2) ip_len=`expr 280 + ${#expected_dhcp_opts} / 2` udp_len=`expr $ip_len - 20` ip_len=$(printf "%x" $ip_len) udp_len=$(printf "%x" $udp_len) # $ip_len var will be in 3 digits i.e 134. So adding a '0' before $ip_len local reply=${src_mac}${srv_mac}080045100${ip_len}000000008011XXXX${srv_ip}${offer_ip} # udp header and dhcp header. # $udp_len var will be in 3 digits. So adding a '0' before $udp_len reply=${reply}004300440${udp_len}0000020106006359aa760000000000000000 # your ip address reply=${reply}${offer_ip} # next server ip address, relay agent ip address, client mac address reply=${reply}0000000000000000${src_mac} # client hardware padding reply=${reply}00000000000000000000 # server hostname reply=${reply}0000000000000000000000000000000000000000000000000000000000000000 reply=${reply}0000000000000000000000000000000000000000000000000000000000000000 # boot file name reply=${reply}0000000000000000000000000000000000000000000000000000000000000000 reply=${reply}0000000000000000000000000000000000000000000000000000000000000000 reply=${reply}0000000000000000000000000000000000000000000000000000000000000000 reply=${reply}0000000000000000000000000000000000000000000000000000000000000000 # dhcp magic cookie reply=${reply}63825363 # dhcp message type local dhcp_reply_type=02 if test $dhcp_type = 03; then dhcp_reply_type=05 fi reply=${reply}3501${dhcp_reply_type}${expected_dhcp_opts}00000000ff00000000 echo $reply >> $inport.expected else for outport; do echo $request >> $outport.expected done fi as hv1 ovs-appctl netdev-dummy/receive hv1-vif$inport $request } reset_pcap_file() { local iface=$1 local pcap_file=$2 ovs-vsctl -- set Interface $iface options:tx_pcap=dummy-tx.pcap \ options:rxq_pcap=dummy-rx.pcap rm -f ${pcap_file}*.pcap ovs-vsctl -- set Interface $iface options:tx_pcap=${pcap_file}-tx.pcap \ options:rxq_pcap=${pcap_file}-rx.pcap } ip_to_hex() { printf "%02x%02x%02x%02x" "$@" } AT_CAPTURE_FILE([ofctl_monitor0.log]) as hv1 ovs-ofctl monitor br-int resume --detach --no-chdir \ --pidfile=ovs-ofctl0.pid 2> ofctl_monitor0.log echo "---------NB dump-----" ovn-nbctl show echo "---------------------" echo "---------SB dump-----" ovn-sbctl list datapath_binding echo "---------------------" ovn-sbctl list logical_flow echo "---------------------" echo "---------------------" ovn-sbctl dump-flows echo "---------------------" echo "------ hv1 dump ----------" as hv1 ovs-ofctl dump-flows br-int # Send DHCPDISCOVER. offer_ip=`ip_to_hex 10 0 0 4` server_ip=`ip_to_hex 10 0 0 1` expected_dhcp_opts=330400000e100104ffffff0003040a00000136040a000001 test_dhcp 1 f00000000001 01 $offer_ip 0 ff1000000001 $server_ip $expected_dhcp_opts # NXT_RESUMEs should be 1. OVS_WAIT_UNTIL([test 1 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`]) $PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif1-tx.pcap > 1.packets cat 1.expected | cut -c -48 > expout AT_CHECK([cat 1.packets | cut -c -48], [0], [expout]) # Skipping the IPv4 checksum. cat 1.expected | cut -c 53- > expout AT_CHECK([cat 1.packets | cut -c 53-], [0], [expout]) # ovs-ofctl also resumes the packets and this causes other ports to receive # the DHCP request packet. So reset the pcap files so that its easier to test. reset_pcap_file hv1-vif1 hv1/vif1 reset_pcap_file hv1-vif2 hv1/vif2 rm -f 1.expected rm -f 2.expected # Send DHCPREQUEST. offer_ip=`ip_to_hex 10 0 0 6` server_ip=`ip_to_hex 10 0 0 1` expected_dhcp_opts=330400000e100104ffffff0003040a00000136040a000001 test_dhcp 2 f00000000002 03 $offer_ip 0 ff1000000001 $server_ip $expected_dhcp_opts # NXT_RESUMEs should be 2. OVS_WAIT_UNTIL([test 2 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`]) $PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > 2.packets cat 2.expected | cut -c -48 > expout AT_CHECK([cat 2.packets | cut -c -48], [0], [expout]) # Skipping the IPv4 checksum. cat 2.expected | cut -c 53- > expout AT_CHECK([cat 2.packets | cut -c 53-], [0], [expout]) reset_pcap_file hv1-vif1 hv1/vif1 reset_pcap_file hv1-vif2 hv1/vif2 rm -f 1.expected rm -f 2.expected # Send Invalid DHCPv4 packet on ls1-lp2. It should be received by ovn-controller # but should be resumed without the reply. # ls1-lp1 (vif1-tx.pcap) should receive the DHCPv4 request packet twice, # one from ovn-controller and the other from "ovs-ofctl resume." offer_ip=0 test_dhcp 2 f00000000002 08 $offer_ip 0 1 1 # NXT_RESUMEs should be 3. OVS_WAIT_UNTIL([test 3 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`]) # vif1-tx.pcap should have received the DHCPv4 (invalid) request packet OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [1.expected]) reset_pcap_file hv1-vif1 hv1/vif1 reset_pcap_file hv1-vif2 hv1/vif2 rm -f 1.expected rm -f 2.expected # Send DHCPv4 packet on ls2-lp1. It doesn't have any DHCPv4 options defined. # ls2-lp2 (vif4-tx.pcap) should receive the DHCPv4 request packet once. test_dhcp 3 f00000000003 01 0 4 0 # Send DHCPv4 packet on ls2-lp2. "router" DHCPv4 option is not defined for # this lport. test_dhcp 4 f00000000004 01 0 3 0 # NXT_RESUMEs should be 3. OVS_WAIT_UNTIL([test 3 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`]) OVN_CHECK_PACKETS([hv1/vif3-tx.pcap], [3.expected]) OVN_CHECK_PACKETS([hv1/vif4-tx.pcap], [4.expected]) # Send DHCPREQUEST with ip4.src set to 10.0.0.6 and ip4.dst set to 10.0.0.1. offer_ip=`ip_to_hex 10 0 0 6` server_ip=`ip_to_hex 10 0 0 1` expected_dhcp_opts=330400000e100104ffffff0003040a00000136040a000001 src_ip=$offer_ip dst_ip=$server_ip test_dhcp 2 f00000000002 03 $offer_ip 1 $src_ip $dst_ip ff1000000001 $server_ip $expected_dhcp_opts # NXT_RESUMEs should be 4. OVS_WAIT_UNTIL([test 4 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`]) $PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > 2.packets cat 2.expected | cut -c -48 > expout AT_CHECK([cat 2.packets | cut -c -48], [0], [expout]) # Skipping the IPv4 checksum. cat 2.expected | cut -c 53- > expout AT_CHECK([cat 2.packets | cut -c 53-], [0], [expout]) reset_pcap_file hv1-vif1 hv1/vif1 reset_pcap_file hv1-vif2 hv1/vif2 rm -f 1.expected rm -f 2.expected # Send DHCPREQUEST with ip4.src set to 10.0.0.6 and ip4.dst set to 255.255.255.255. offer_ip=`ip_to_hex 10 0 0 6` server_ip=`ip_to_hex 10 0 0 1` expected_dhcp_opts=330400000e100104ffffff0003040a00000136040a000001 src_ip=$offer_ip dst_ip=`ip_to_hex 255 255 255 255` test_dhcp 2 f00000000002 03 $offer_ip 1 $src_ip $dst_ip ff1000000001 $server_ip $expected_dhcp_opts # NXT_RESUMEs should be 5. OVS_WAIT_UNTIL([test 5 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`]) $PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > 2.packets cat 2.expected | cut -c -48 > expout AT_CHECK([cat 2.packets | cut -c -48], [0], [expout]) # Skipping the IPv4 checksum. cat 2.expected | cut -c 53- > expout AT_CHECK([cat 2.packets | cut -c 53-], [0], [expout]) reset_pcap_file hv1-vif1 hv1/vif1 reset_pcap_file hv1-vif2 hv1/vif2 rm -f 1.expected rm -f 2.expected # Send DHCPREQUEST with ip4.src set to 10.0.0.6 and ip4.dst set to 10.0.0.4. # The packet should not be received by ovn-controller. src_ip=`ip_to_hex 10 0 0 6` dst_ip=`ip_to_hex 10 0 0 4` test_dhcp 2 f00000000002 03 0 1 $src_ip $dst_ip 1 # NXT_RESUMEs should be 5. OVS_WAIT_UNTIL([test 5 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`]) # vif1-tx.pcap should have received the DHCPv4 request packet OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [1.expected]) as hv1 OVS_APP_EXIT_AND_WAIT([ovn-controller]) OVS_APP_EXIT_AND_WAIT([ovs-vswitchd]) OVS_APP_EXIT_AND_WAIT([ovsdb-server]) as ovn-sb OVS_APP_EXIT_AND_WAIT([ovsdb-server]) as ovn-nb OVS_APP_EXIT_AND_WAIT([ovsdb-server]) as northd OVS_APP_EXIT_AND_WAIT([ovn-northd]) as main OVS_APP_EXIT_AND_WAIT([ovs-vswitchd]) OVS_APP_EXIT_AND_WAIT([ovsdb-server]) AT_CLEANUP AT_SETUP([ovn -- dhcpv6 : 1 HV, 2 LS, 5 LSPs]) AT_SKIP_IF([test $HAVE_PYTHON = no]) ovn_start ovn-nbctl ls-add ls1 ovn-nbctl lsp-add ls1 ls1-lp1 \ -- lsp-set-addresses ls1-lp1 "f0:00:00:00:00:01 10.0.0.4 ae70::4" ovn-nbctl lsp-set-port-security ls1-lp1 "f0:00:00:00:00:01 10.0.0.4 ae70::4" ovn-nbctl lsp-add ls1 ls1-lp2 \ -- lsp-set-addresses ls1-lp2 "f0:00:00:00:00:02 ae70::5" ovn-nbctl lsp-set-port-security ls1-lp2 "f0:00:00:00:00:02 ae70::5" ovn-nbctl lsp-add ls1 ls1-lp3 \ -- lsp-set-addresses ls1-lp3 "f0:00:00:00:00:22 ae70::22" ovn-nbctl lsp-set-port-security ls1-lp3 "f0:00:00:00:00:22 ae70::22" d1="$(ovn-nbctl create DHCP_Options cidr="ae70\:\:/64" \ options="\"server_id\"=\"00:00:00:10:00:01\"")" ovn-nbctl lsp-set-dhcpv6-options ls1-lp1 ${d1} ovn-nbctl lsp-set-dhcpv6-options ls1-lp2 ${d1} d2="$(ovn-nbctl create DHCP_Options cidr="ae70\:\:/64" \ options="\"dhcpv6_stateless\"=\"true\" \"server_id\"=\"00:00:00:10:00:01\"")" ovn-nbctl lsp-set-dhcpv6-options ls1-lp3 ${d2} ovn-nbctl ls-add ls2 ovn-nbctl lsp-add ls2 ls2-lp1 \ -- lsp-set-addresses ls2-lp1 "f0:00:00:00:00:03 be70::3" ovn-nbctl lsp-set-port-security ls2-lp1 "f0:00:00:00:00:03 be70::3" ovn-nbctl lsp-add ls2 ls2-lp2 \ -- lsp-set-addresses ls2-lp2 "f0:00:00:00:00:04 be70::4" ovn-nbctl lsp-set-port-security ls2-lp2 "f0:00:00:00:00:04 be70::4" net_add n1 sim_add hv1 as hv1 ovs-vsctl add-br br-phys ovn_attach n1 br-phys 192.168.0.1 ovs-vsctl -- add-port br-int hv1-vif1 -- \ set interface hv1-vif1 external-ids:iface-id=ls1-lp1 \ options:tx_pcap=hv1/vif1-tx.pcap \ options:rxq_pcap=hv1/vif1-rx.pcap \ ofport-request=1 ovs-vsctl -- add-port br-int hv1-vif2 -- \ set interface hv1-vif2 external-ids:iface-id=ls1-lp2 \ options:tx_pcap=hv1/vif2-tx.pcap \ options:rxq_pcap=hv1/vif2-rx.pcap \ ofport-request=2 ovs-vsctl -- add-port br-int hv1-vif3 -- \ set interface hv1-vif3 external-ids:iface-id=ls2-lp1 \ options:tx_pcap=hv1/vif3-tx.pcap \ options:rxq_pcap=hv1/vif3-rx.pcap \ ofport-request=3 ovs-vsctl -- add-port br-int hv1-vif4 -- \ set interface hv1-vif4 external-ids:iface-id=ls2-lp2 \ options:tx_pcap=hv1/vif4-tx.pcap \ options:rxq_pcap=hv1/vif4-rx.pcap \ ofport-request=4 ovs-vsctl -- add-port br-int hv1-vif5 -- \ set interface hv1-vif5 external-ids:iface-id=ls1-lp3 \ options:tx_pcap=hv1/vif5-tx.pcap \ options:rxq_pcap=hv1/vif5-rx.pcap \ ofport-request=5 ovn_populate_arp sleep 2 trim_zeros() { sed 's/\(00\)\{1,\}$//' } # This shell function sends a DHCPv6 request packet # test_dhcpv6 INPORT SRC_MAC SRC_LLA DHCPv6_MSG_TYPE OFFER_IP OUTPORT... # The OUTPORTs (zero or more) list the VIFs on which the original DHCPv6 # packet should be received twice (one from ovn-controller and the other # from the "ovs-ofctl monitor br-int resume" test_dhcpv6() { local inport=$1 src_mac=$2 src_lla=$3 msg_code=$4 offer_ip=$5 local request=ffffffffffff${src_mac}86dd00000000002a1101${src_lla} # dst ip ff02::1:2 request=${request}ff020000000000000000000000010002 # udp header and dhcpv6 header request=${request}02220223002affff${msg_code}010203 # Client identifier request=${request}0001000a00030001${src_mac} # IA-NA (Identity Association for Non Temporary Address) request=${request}0003000c0102030400000e1000001518 shift; shift; shift; shift; shift; if test $offer_ip != 0; then local server_mac=000000100001 local server_lla=fe80000000000000020000fffe100001 local reply_code=07 if test $msg_code = 01; then reply_code=02 fi local msg_len=54 if test $offer_ip = 1; then msg_len=28 fi local reply=${src_mac}${server_mac}86dd0000000000${msg_len}1101${server_lla}${src_lla} # udp header and dhcpv6 header reply=${reply}0223022200${msg_len}ffff${reply_code}010203 # Client identifier reply=${reply}0001000a00030001${src_mac} # IA-NA if test $offer_ip != 1; then reply=${reply}0003002801020304ffffffffffffffff00050018${offer_ip}ffffffffffffffff fi # Server identifier reply=${reply}0002000a00030001${server_mac} echo $reply | trim_zeros >> $inport.expected else for outport; do echo $request | trim_zeros >> $outport.expected done fi as hv1 ovs-appctl netdev-dummy/receive hv1-vif$inport $request } reset_pcap_file() { local iface=$1 local pcap_file=$2 ovs-vsctl -- set Interface $iface options:tx_pcap=dummy-tx.pcap \ options:rxq_pcap=dummy-rx.pcap rm -f ${pcap_file}*.pcap ovs-vsctl -- set Interface $iface options:tx_pcap=${pcap_file}-tx.pcap \ options:rxq_pcap=${pcap_file}-rx.pcap } AT_CAPTURE_FILE([ofctl_monitor0.log]) as hv1 ovs-ofctl monitor br-int resume --detach --no-chdir \ --pidfile=ovs-ofctl0.pid 2> ofctl_monitor0.log echo "---------NB dump-----" ovn-nbctl show echo "---------------------" echo "---------SB dump-----" ovn-sbctl list datapath_binding echo "---------------------" ovn-sbctl list logical_flow echo "---------------------" echo "---------------------" ovn-sbctl dump-flows echo "---------------------" echo "------ hv1 dump ----------" as hv1 ovs-ofctl dump-flows br-int src_mac=f00000000001 src_lla=fe80000000000000f20000fffe000001 offer_ip=ae700000000000000000000000000004 test_dhcpv6 1 $src_mac $src_lla 01 $offer_ip # NXT_RESUMEs should be 1. OVS_WAIT_UNTIL([test 1 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`]) $PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif1-tx.pcap | trim_zeros > 1.packets # cat 1.expected | trim_zeros > expout cat 1.expected | cut -c -120 > expout AT_CHECK([cat 1.packets | cut -c -120], [0], [expout]) # Skipping the UDP checksum cat 1.expected | cut -c 125- > expout AT_CHECK([cat 1.packets | cut -c 125-], [0], [expout]) rm 1.expected # Send invalid packet on ls1-lp2. ovn-controller should resume the packet # without any modifications and the packet should be received by ls1-lp1. # ls1-lp1 will receive the packet twice, one from the ovn-controller after the # resume and the other from ovs-ofctl monitor resume. reset_pcap_file hv1-vif1 hv1/vif1 reset_pcap_file hv1-vif2 hv1/vif2 src_mac=f00000000002 src_lla=fe80000000000000f20000fffe000002 offer_ip=ae700000000000000000000000000005 # Set invalid msg_type test_dhcpv6 2 $src_mac $src_lla 10 0 1 1 # NXT_RESUMEs should be 2. OVS_WAIT_UNTIL([test 2 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`]) # vif2-tx.pcap should not have received the DHCPv6 reply packet rm 2.packets $PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap | trim_zeros > 2.packets AT_CHECK([cat 2.packets], [0], []) # vif1-tx.pcap should have received the DHCPv6 (invalid) request packet $PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif1-tx.pcap | trim_zeros > 1.packets cat 1.expected > expout AT_CHECK([cat 1.packets], [0], [expout]) # Send DHCPv6 packet on ls2-lp1. native DHCPv6 is disabled on this port. # There should be no DHCPv6 reply from ovn-controller and the request packet # should be received by ls2-lp2. src_mac=f00000000003 src_lla=fe80000000000000f20000fffe000003 test_dhcpv6 3 $src_mac $src_lla 01 0 4 # NXT_RESUMEs should be 2 only. OVS_WAIT_UNTIL([test 2 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`]) # vif3-tx.pcap should not have received the DHCPv6 reply packet $PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif3-tx.pcap | trim_zeros > 3.packets AT_CHECK([cat 3.packets], [0], []) # vif4-tx.pcap should have received the DHCPv6 request packet $PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif4-tx.pcap | trim_zeros > 4.packets cat 4.expected > expout AT_CHECK([cat 4.packets], [0], [expout]) # Send DHCPv6 packet on ls1-lp3. native DHCPv6 works as stateless mode for this port. # The DHCPv6 reply should doesn't contian offer_ip. src_mac=f00000000022 src_lla=fe80000000000000f20000fffe000022 reset_pcap_file hv1-vif5 hv1/vif5 test_dhcpv6 5 $src_mac $src_lla 01 1 5 # NXT_RESUMEs should be 3. OVS_WAIT_UNTIL([test 3 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`]) $PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif5-tx.pcap | trim_zeros > 5.packets # Skipping the UDP checksum cat 5.expected | cut -c 1-120,125- > expout AT_CHECK([cat 5.packets | cut -c 1-120,125- ], [0], [expout]) as hv1 OVS_APP_EXIT_AND_WAIT([ovn-controller]) OVS_APP_EXIT_AND_WAIT([ovs-vswitchd]) OVS_APP_EXIT_AND_WAIT([ovsdb-server]) as ovn-sb OVS_APP_EXIT_AND_WAIT([ovsdb-server]) as ovn-nb OVS_APP_EXIT_AND_WAIT([ovsdb-server]) as northd OVS_APP_EXIT_AND_WAIT([ovn-northd]) as main OVS_APP_EXIT_AND_WAIT([ovs-vswitchd]) OVS_APP_EXIT_AND_WAIT([ovsdb-server]) AT_CLEANUP AT_SETUP([ovn -- 2 HVs, 2 LRs connected via LS, gateway router]) AT_SKIP_IF([test $HAVE_PYTHON = no]) ovn_start # Logical network: # Two LRs - R1 and R2 that are connected to each other via LS "join" # in 20.0.0.0/24 network. R1 has switchess foo (192.168.1.0/24) # connected to it. R2 has alice (172.16.1.0/24) connected to it. # R2 is a gateway router. # Create two hypervisor and create OVS ports corresponding to logical ports. net_add n1 sim_add hv1 as hv1 ovs-vsctl add-br br-phys ovn_attach n1 br-phys 192.168.0.1 ovs-vsctl -- add-port br-int hv1-vif1 -- \ set interface hv1-vif1 external-ids:iface-id=foo1 \ options:tx_pcap=hv1/vif1-tx.pcap \ options:rxq_pcap=hv1/vif1-rx.pcap \ ofport-request=1 sim_add hv2 as hv2 ovs-vsctl add-br br-phys ovn_attach n1 br-phys 192.168.0.2 ovs-vsctl -- add-port br-int hv2-vif1 -- \ set interface hv2-vif1 external-ids:iface-id=alice1 \ options:tx_pcap=hv2/vif1-tx.pcap \ options:rxq_pcap=hv2/vif1-rx.pcap \ ofport-request=1 # Pre-populate the hypervisors' ARP tables so that we don't lose any # packets for ARP resolution (native tunneling doesn't queue packets # for ARP resolution). ovn_populate_arp ovn-nbctl create Logical_Router name=R1 ovn-nbctl create Logical_Router name=R2 options:chassis="hv2" ovn-nbctl ls-add foo ovn-nbctl ls-add alice ovn-nbctl ls-add join # Connect foo to R1 ovn-nbctl lrp-add R1 foo 00:00:01:01:02:03 192.168.1.1/24 ovn-nbctl lsp-add foo rp-foo -- set Logical_Switch_Port rp-foo \ type=router options:router-port=foo addresses=\"00:00:01:01:02:03\" # Connect alice to R2 ovn-nbctl lrp-add R2 alice 00:00:02:01:02:03 172.16.1.1/24 ovn-nbctl lsp-add alice rp-alice -- set Logical_Switch_Port rp-alice \ type=router options:router-port=alice addresses=\"00:00:02:01:02:03\" # Connect R1 to join ovn-nbctl lrp-add R1 R1_join 00:00:04:01:02:03 20.0.0.1/24 ovn-nbctl lsp-add join r1-join -- set Logical_Switch_Port r1-join \ type=router options:router-port=R1_join addresses='"00:00:04:01:02:03"' # Connect R2 to join ovn-nbctl lrp-add R2 R2_join 00:00:04:01:02:04 20.0.0.2/24 ovn-nbctl lsp-add join r2-join -- set Logical_Switch_Port r2-join \ type=router options:router-port=R2_join addresses='"00:00:04:01:02:04"' #install static routes ovn-nbctl -- --id=@lrt create Logical_Router_Static_Route \ ip_prefix=172.16.1.0/24 nexthop=20.0.0.2 -- add Logical_Router \ R1 static_routes @lrt ovn-nbctl -- --id=@lrt create Logical_Router_Static_Route \ ip_prefix=192.168.1.0/24 nexthop=20.0.0.1 -- add Logical_Router \ R2 static_routes @lrt # Create logical port foo1 in foo ovn-nbctl lsp-add foo foo1 \ -- lsp-set-addresses foo1 "f0:00:00:01:02:03 192.168.1.2" # Create logical port alice1 in alice ovn-nbctl lsp-add alice alice1 \ -- lsp-set-addresses alice1 "f0:00:00:01:02:04 172.16.1.2" # Allow some time for ovn-northd and ovn-controller to catch up. # XXX This should be more systematic. sleep 2 ip_to_hex() { printf "%02x%02x%02x%02x" "$@" } # Send ip packets between foo1 and alice1 src_mac="f00000010203" dst_mac="000001010203" src_ip=`ip_to_hex 192 168 1 2` dst_ip=`ip_to_hex 172 16 1 2` packet=${dst_mac}${src_mac}08004500001c0000000040110000${src_ip}${dst_ip}0035111100080000 echo "---------NB dump-----" ovn-nbctl show echo "---------------------" ovn-nbctl list logical_router echo "---------------------" ovn-nbctl list logical_router_port echo "---------------------" echo "---------SB dump-----" ovn-sbctl list datapath_binding echo "---------------------" ovn-sbctl list port_binding echo "---------------------" ovn-sbctl dump-flows echo "---------------------" ovn-sbctl list chassis ovn-sbctl list encap echo "---------------------" # Packet to Expect at alice1 src_mac="000002010203" dst_mac="f00000010204" src_ip=`ip_to_hex 192 168 1 2` dst_ip=`ip_to_hex 172 16 1 2` expected=${dst_mac}${src_mac}08004500001c000000003e110200${src_ip}${dst_ip}0035111100080000 as hv1 ovs-appctl netdev-dummy/receive hv1-vif1 $packet as hv1 ovs-appctl ofproto/trace br-int in_port=1 $packet echo "------ hv1 dump after packet 1 ----------" as hv1 ovs-ofctl show br-int as hv1 ovs-ofctl dump-flows br-int echo "------ hv2 dump after packet 1 ----------" as hv2 ovs-ofctl show br-int as hv2 ovs-ofctl dump-flows br-int echo "----------------------------" echo $expected > expected OVN_CHECK_PACKETS([hv2/vif1-tx.pcap], [expected]) # Delete the router and re-create it. Things should work as before. ovn-nbctl lr-del R2 ovn-nbctl create Logical_Router name=R2 options:chassis="hv2" # Connect alice to R2 ovn-nbctl lrp-add R2 alice 00:00:02:01:02:03 172.16.1.1/24 # Connect R2 to join ovn-nbctl lrp-add R2 R2_join 00:00:04:01:02:04 20.0.0.2/24 ovn-nbctl -- --id=@lrt create Logical_Router_Static_Route \ ip_prefix=192.168.1.0/24 nexthop=20.0.0.1 -- add Logical_Router \ R2 static_routes @lrt # Wait for ovn-controller to catch up. sleep 1 # Send the packet again. as hv1 ovs-appctl netdev-dummy/receive hv1-vif1 $packet echo "------ hv1 dump after packet 2 ----------" as hv1 ovs-ofctl show br-int as hv1 ovs-ofctl dump-flows br-int echo "------ hv2 dump after packet 2 ----------" as hv2 ovs-ofctl show br-int as hv2 ovs-ofctl dump-flows br-int echo "----------------------------" echo $expected >> expected OVN_CHECK_PACKETS([hv2/vif1-tx.pcap], [expected]) OVN_CLEANUP([hv1],[hv2]) AT_CLEANUP AT_SETUP([ovn -- icmp_reply: 1 HVs, 2 LSs, 1 lport/LS, 1 LR]) AT_KEYWORDS([router-icmp-reply]) AT_SKIP_IF([test $HAVE_PYTHON = no]) ovn_start # Logical network: # One LR - R1 has switch ls1 (191.168.1.0/24) connected to it, # and has switch ls2 (172.16.1.0/24) connected to it. ovn-nbctl lr-add R1 ovn-nbctl ls-add ls1 ovn-nbctl ls-add ls2 # Connect ls1 to R1 ovn-nbctl lrp-add R1 ls1 00:00:00:01:02:f1 192.168.1.1/24 ovn-nbctl lsp-add ls1 rp-ls1 -- set Logical_Switch_Port rp-ls1 \ type=router options:router-port=ls1 addresses=\"00:00:00:01:02:f1\" # Connect ls2 to R1 ovn-nbctl lrp-add R1 ls2 00:00:00:01:02:f2 172.16.1.1/24 ovn-nbctl lsp-add ls2 rp-ls2 -- set Logical_Switch_Port rp-ls2 \ type=router options:router-port=ls2 addresses=\"00:00:00:01:02:f2\" # Create logical port ls1-lp1 in ls1 ovn-nbctl lsp-add ls1 ls1-lp1 \ -- lsp-set-addresses ls1-lp1 "00:00:00:01:02:03 192.168.1.2" # Create logical port ls2-lp1 in ls2 ovn-nbctl lsp-add ls2 ls2-lp1 \ -- lsp-set-addresses ls2-lp1 "00:00:00:01:02:04 172.16.1.2" # Create one hypervisor and create OVS ports corresponding to logical ports. net_add n1 sim_add hv1 as hv1 ovs-vsctl add-br br-phys ovn_attach n1 br-phys 192.168.0.1 ovs-vsctl -- add-port br-int vif1 -- \ set interface vif1 external-ids:iface-id=ls1-lp1 \ options:tx_pcap=hv1/vif1-tx.pcap \ options:rxq_pcap=hv1/vif1-rx.pcap \ ofport-request=1 ovs-vsctl -- add-port br-int vif2 -- \ set interface vif2 external-ids:iface-id=ls2-lp1 \ options:tx_pcap=hv1/vif2-tx.pcap \ options:rxq_pcap=hv1/vif2-rx.pcap \ ofport-request=1 # Allow some time for ovn-northd and ovn-controller to catch up. # XXX This should be more systematic. sleep 1 ip_to_hex() { printf "%02x%02x%02x%02x" "$@" } for i in 1 2; do : > vif$i.expected done # test_ipv4_icmp_request INPORT ETH_SRC ETH_DST IPV4_SRC IPV4_DST IP_CHKSUM ICMP_CHKSUM [EXP_IP_CHKSUM EXP_ICMP_CHKSUM] # # Causes a packet to be received on INPORT. The packet is an ICMPv4 # request with ETH_SRC, ETH_DST, IPV4_SRC, IPV4_DST, IP_CHSUM and # ICMP_CHKSUM as specified. If EXP_IP_CHKSUM and EXP_ICMP_CHKSUM are # provided, then it should be the ip and icmp checksums of the packet # responded; otherwise, no reply is expected. # In the absence of an ip checksum calculation helpers, this relies # on the caller to provide the checksums for the ip and icmp headers. # XXX This should be more systematic. # # INPORT is an lport number, e.g. 11 for vif11. # ETH_SRC and ETH_DST are each 12 hex digits. # IPV4_SRC and IPV4_DST are each 8 hex digits. # IP_CHSUM and ICMP_CHKSUM are each 4 hex digits. # EXP_IP_CHSUM and EXP_ICMP_CHKSUM are each 4 hex digits. test_ipv4_icmp_request() { local inport=$1 eth_src=$2 eth_dst=$3 ipv4_src=$4 ipv4_dst=$5 ip_chksum=$6 icmp_chksum=$7 local exp_ip_chksum=$8 exp_icmp_chksum=$9 shift; shift; shift; shift; shift; shift; shift shift; shift # Use ttl to exercise section 4.2.2.9 of RFC1812 local ip_ttl=01 local icmp_id=5fbf local icmp_seq=0001 local icmp_data=$(seq 1 56 | xargs printf "%02x") local icmp_type_code_request=0800 local icmp_payload=${icmp_type_code_request}${icmp_chksum}${icmp_id}${icmp_seq}${icmp_data} local packet=${eth_dst}${eth_src}08004500005400004000${ip_ttl}01${ip_chksum}${ipv4_src}${ipv4_dst}${icmp_payload} as hv1 ovs-appctl netdev-dummy/receive vif$inport $packet if test X$exp_icmp_chksum != X; then # Expect to receive the reply, if any. In same port where packet was sent. # Note: src and dst fields are expected to be reversed. local icmp_type_code_response=0000 local reply_icmp_ttl=fe local reply_icmp_payload=${icmp_type_code_response}${exp_icmp_chksum}${icmp_id}${icmp_seq}${icmp_data} local reply=${eth_src}${eth_dst}08004500005400004000${reply_icmp_ttl}01${exp_ip_chksum}${ipv4_dst}${ipv4_src}${reply_icmp_payload} echo $reply >> vif$inport.expected fi } # Send ping packet to router's ip addresses, from each of the 2 logical ports. rtr_l1_ip=$(ip_to_hex 192 168 1 1) rtr_l2_ip=$(ip_to_hex 172 16 1 1) l1_ip=$(ip_to_hex 192 168 1 2) l2_ip=$(ip_to_hex 172 16 1 2) # Ping router ip address that is on same subnet as the logical port test_ipv4_icmp_request 1 000000010203 0000000102f1 $l1_ip $rtr_l1_ip 0000 8510 02ff 8d10 test_ipv4_icmp_request 2 000000010204 0000000102f2 $l2_ip $rtr_l2_ip 0000 8510 02ff 8d10 # Ping router ip address that is on the other side of the logical ports test_ipv4_icmp_request 1 000000010203 0000000102f1 $l1_ip $rtr_l2_ip 0000 8510 02ff 8d10 test_ipv4_icmp_request 2 000000010204 0000000102f2 $l2_ip $rtr_l1_ip 0000 8510 02ff 8d10 echo "---------NB dump-----" ovn-nbctl show echo "---------------------" ovn-nbctl list logical_router echo "---------------------" ovn-nbctl list logical_router_port echo "---------------------" echo "---------SB dump-----" ovn-sbctl list datapath_binding echo "---------------------" ovn-sbctl list logical_flow echo "---------------------" echo "------ hv1 dump ----------" as hv1 ovs-ofctl dump-flows br-int # Now check the packets actually received against the ones expected. for inport in 1 2; do OVN_CHECK_PACKETS([hv1/vif${inport}-tx.pcap], [vif$inport.expected]) done OVN_CLEANUP([hv1]) AT_CLEANUP # 1 hypervisor, 1 port # make sure that the port state is properly set to up and back down # when created and deleted. AT_SETUP([ovn -- port state up and down]) ovn_start ovn-nbctl ls-add ls1 ovn-nbctl lsp-add ls1 lp1 ovn-nbctl lsp-set-addresses lp1 unknown net_add n1 sim_add hv1 as hv1 ovs-vsctl add-br br-phys as hv1 ovn_attach n1 br-phys 192.168.0.1 as hv1 ovs-vsctl add-port br-int vif1 -- set Interface vif1 external-ids:iface-id=lp1 OVS_WAIT_UNTIL([test x`ovn-nbctl lsp-get-up lp1` = xup]) as hv1 ovs-vsctl del-port br-int vif1 OVS_WAIT_UNTIL([test x`ovn-nbctl lsp-get-up lp1` = xdown]) OVN_CLEANUP([hv1]) AT_CLEANUP # 1 hypervisor, 1 port # make sure that the OF rules created to support a datapath are added/cleared # when logical switch is created and removed. AT_SETUP([ovn -- datapath rules added/removed]) AT_KEYWORDS([cleanup]) ovn_start net_add n1 sim_add hv1 as hv1 ovs-vsctl add-br br-phys as hv1 ovn_attach n1 br-phys 192.168.0.1 # This shell function checks if OF rules in br-int have clauses # related to OVN datapaths. The caller determines if it should find # a match in the output, or not. # # EXPECT_DATAPATH param determines whether flows that refer to # datapath to should be present or not. 0 means # they should not be. # STAGE_INFO param is a simple string to help identify the stage # in the test when this function was invoked. test_datapath_in_of_rules() { local expect_datapath=$1 stage_info=$2 echo "------ ovn-nbctl show ${stage_info} ------" ovn-nbctl show echo "------ ovn-sbctl show ${stage_info} ------" ovn-sbctl show echo "------ OF rules ${stage_info} ------" AT_CHECK([ovs-ofctl dump-flows br-int], [0], [stdout]) # if there is a datapath mentioned in the output, check for the # magic keyword that represents one, based on the exit status of # a quiet grep if test $expect_datapath != 0; then AT_CHECK([grep -q -i 'metadata=' stdout], [0], [ignore-nolog]) else AT_CHECK([grep -q -i 'metadata=' stdout], [1], [ignore-nolog]) fi } test_datapath_in_of_rules 0 "before ls+port create" ovn-nbctl ls-add ls1 ovn-nbctl lsp-add ls1 lp1 ovn-nbctl lsp-set-addresses lp1 unknown as hv1 ovs-vsctl add-port br-int vif1 -- set Interface vif1 external-ids:iface-id=lp1 OVS_WAIT_UNTIL([test x`ovn-nbctl lsp-get-up lp1` = xup]) test_datapath_in_of_rules 1 "after port is bound" as hv1 ovs-vsctl del-port br-int vif1 OVS_WAIT_UNTIL([test x`ovn-nbctl lsp-get-up lp1` = xdown]) ovn-nbctl lsp-set-addresses lp1 ovn-nbctl lsp-del lp1 ovn-nbctl ls-del ls1 # wait for earlier changes to take effect AT_CHECK([ovn-nbctl --timeout=3 --wait=sb sync], [0], [ignore]) # ensure OF rules are no longer present. There used to be a bug here. test_datapath_in_of_rules 0 "after lport+ls removal" OVN_CLEANUP([hv1]) AT_CLEANUP AT_SETUP([ovn -- nd_na ]) AT_SKIP_IF([test $HAVE_PYTHON = no]) ovn_start #TODO: since patch port for IPv6 logical router port is not ready not, # so we are not going to test vifs on different lswitches cases. Try # to update for that once relevant stuff implemented. # In this test cases we create 1 lswitch, it has 2 VIF ports attached # with. NS packet we test, from one VIF for another VIF, will be replied # by local ovn-controller, but not by target VIF. # Create hypervisors and logical switch lsw0. ovn-nbctl ls-add lsw0 net_add n1 sim_add hv1 as hv1 ovs-vsctl add-br br-phys ovn_attach n1 br-phys 192.168.0.2 # Add vif1 to hv1 and lsw0, turn on l2 port security on vif1. ovs-vsctl add-port br-int vif1 -- set Interface vif1 external-ids:iface-id=lp1 options:tx_pcap=hv1/vif1-tx.pcap options:rxq_pcap=hv1/vif1-rx.pcap ofport-request=1 ovn-nbctl lsp-add lsw0 lp1 ovn-nbctl lsp-set-addresses lp1 "fa:16:3e:94:05:98 192.168.0.3 fd81:ce49:a948:0:f816:3eff:fe94:598" ovn-nbctl lsp-set-port-security lp1 "fa:16:3e:94:05:98 192.168.0.3 fd81:ce49:a948:0:f816:3eff:fe94:598" # Add vif2 to hv1 and lsw0, turn on l2 port security on vif2. ovs-vsctl add-port br-int vif2 -- set Interface vif2 external-ids:iface-id=lp2 options:tx_pcap=hv1/vif2-tx.pcap options:rxq_pcap=hv1/vif2-rx.pcap ofport-request=2 ovn-nbctl lsp-add lsw0 lp2 ovn-nbctl lsp-set-addresses lp2 "fa:16:3e:a1:f9:ae 192.168.0.4 fd81:ce49:a948:0:f816:3eff:fea1:f9ae" ovn-nbctl lsp-set-port-security lp2 "fa:16:3e:a1:f9:ae 192.168.0.4 fd81:ce49:a948:0:f816:3eff:fea1:f9ae" # Add ACL rule for ICMPv6 on lsw0 ovn-nbctl acl-add lsw0 from-lport 1002 'ip6 && icmp6' allow-related ovn-nbctl acl-add lsw0 to-lport 1002 'outport == "lp1" && ip6 && icmp6' allow-related ovn-nbctl acl-add lsw0 to-lport 1002 'outport == "lp2" && ip6 && icmp6' allow-related # Allow some time for ovn-northd and ovn-controller to catch up. # XXX This should be more systematic. sleep 1 # Given the name of a logical port, prints the name of the hypervisor # on which it is located. vif_to_hv() { echo hv1${1%?} } for i in 1 2; do : > $i.expected done # Complete Neighbor Solicitation packet and Neighbor Advertisement packet # vif1 -> NS -> vif2. vif1 <- NA <- ovn-controller. # vif2 will not receive NS packet, since ovn-controller will reply for it. ns_packet=3333ffa1f9aefa163e94059886dd6000000000203afffd81ce49a9480000f8163efffe940598fd81ce49a9480000f8163efffea1f9ae8700e01160000000fd81ce49a9480000f8163efffea1f9ae0101fa163e940598 na_packet=fa163e940598fa163ea1f9ae86dd6000000000203afffd81ce49a9480000f8163efffea1f9aefd81ce49a9480000f8163efffe9405988800e9ed60000000fd81ce49a9480000f8163efffea1f9ae0201fa163ea1f9ae as hv1 ovs-appctl netdev-dummy/receive vif1 $ns_packet echo $na_packet >> 1.expected echo "------ hv1 dump ------" as hv1 ovs-vsctl show as hv1 ovs-ofctl -O OpenFlow13 show br-int as hv1 ovs-ofctl -O OpenFlow13 dump-flows br-int for i in 1 2; do OVN_CHECK_PACKETS([hv1/vif$i-tx.pcap], [$i.expected]) done OVN_CLEANUP([hv1]) AT_CLEANUP AT_SETUP([ovn -- address sets modification/removal smoke test]) ovn_start net_add n1 sim_add hv1 as hv1 ovs-vsctl add-br br-phys ovn_attach n1 br-phys 192.168.0.1 row=`ovn-nbctl create Address_Set name=set1 addresses=\"1.1.1.1\"` ovn-nbctl set Address_Set $row name=set1 addresses=\"1.1.1.1,1.1.1.2\" ovn-nbctl destroy Address_Set $row sleep 1 # A bug previously existed in the address set support code # that caused ovn-controller to crash after an address set # was updated and then removed. This test case ensures # that ovn-controller is at least still running after # creating, updating, and deleting an address set. AT_CHECK([ovs-appctl -t ovn-controller version], [0], [ignore]) OVN_CLEANUP([hv1]) AT_CLEANUP AT_SETUP([ovn -- ipam]) AT_SKIP_IF([test $HAVE_PYTHON = no]) ovn_start # Add a port to a switch that does not have a subnet set, then set the # subnet which should result in an address being allocated for the port. ovn-nbctl ls-add sw0 ovn-nbctl lsp-add sw0 p0 -- lsp-set-addresses p0 dynamic ovn-nbctl --wait=sb add Logical-Switch sw0 other_config subnet=192.168.1.0/24 AT_CHECK([ovn-nbctl get Logical-Switch-Port p0 dynamic_addresses], [0], ["0a:00:00:00:00:01 192.168.1.2" ]) # Add 9 more ports to sw0, addresses should all be unique. for n in `seq 1 9`; do ovn-nbctl --wait=sb lsp-add sw0 "p$n" -- lsp-set-addresses "p$n" dynamic done AT_CHECK([ovn-nbctl get Logical-Switch-Port p1 dynamic_addresses], [0], ["0a:00:00:00:00:02 192.168.1.3" ]) AT_CHECK([ovn-nbctl get Logical-Switch-Port p2 dynamic_addresses], [0], ["0a:00:00:00:00:03 192.168.1.4" ]) AT_CHECK([ovn-nbctl get Logical-Switch-Port p3 dynamic_addresses], [0], ["0a:00:00:00:00:04 192.168.1.5" ]) AT_CHECK([ovn-nbctl get Logical-Switch-Port p4 dynamic_addresses], [0], ["0a:00:00:00:00:05 192.168.1.6" ]) AT_CHECK([ovn-nbctl get Logical-Switch-Port p5 dynamic_addresses], [0], ["0a:00:00:00:00:06 192.168.1.7" ]) AT_CHECK([ovn-nbctl get Logical-Switch-Port p6 dynamic_addresses], [0], ["0a:00:00:00:00:07 192.168.1.8" ]) AT_CHECK([ovn-nbctl get Logical-Switch-Port p7 dynamic_addresses], [0], ["0a:00:00:00:00:08 192.168.1.9" ]) AT_CHECK([ovn-nbctl get Logical-Switch-Port p8 dynamic_addresses], [0], ["0a:00:00:00:00:09 192.168.1.10" ]) AT_CHECK([ovn-nbctl get Logical-Switch-Port p9 dynamic_addresses], [0], ["0a:00:00:00:00:0a 192.168.1.11" ]) # Trying similar tests with a second switch. MAC addresses should be unique # across both switches but IP's only need to be unique within the same switch. ovn-nbctl ls-add sw1 ovn-nbctl lsp-add sw1 p10 -- lsp-set-addresses p10 dynamic ovn-nbctl --wait=sb add Logical-Switch sw1 other_config subnet=192.168.1.0/24 AT_CHECK([ovn-nbctl get Logical-Switch-Port p10 dynamic_addresses], [0], ["0a:00:00:00:00:0b 192.168.1.2" ]) for n in `seq 11 19`; do ovn-nbctl --wait=sb lsp-add sw1 "p$n" -- lsp-set-addresses "p$n" dynamic done AT_CHECK([ovn-nbctl get Logical-Switch-Port p11 dynamic_addresses], [0], ["0a:00:00:00:00:0c 192.168.1.3" ]) AT_CHECK([ovn-nbctl get Logical-Switch-Port p12 dynamic_addresses], [0], ["0a:00:00:00:00:0d 192.168.1.4" ]) AT_CHECK([ovn-nbctl get Logical-Switch-Port p13 dynamic_addresses], [0], ["0a:00:00:00:00:0e 192.168.1.5" ]) AT_CHECK([ovn-nbctl get Logical-Switch-Port p14 dynamic_addresses], [0], ["0a:00:00:00:00:0f 192.168.1.6" ]) AT_CHECK([ovn-nbctl get Logical-Switch-Port p15 dynamic_addresses], [0], ["0a:00:00:00:00:10 192.168.1.7" ]) AT_CHECK([ovn-nbctl get Logical-Switch-Port p16 dynamic_addresses], [0], ["0a:00:00:00:00:11 192.168.1.8" ]) AT_CHECK([ovn-nbctl get Logical-Switch-Port p17 dynamic_addresses], [0], ["0a:00:00:00:00:12 192.168.1.9" ]) AT_CHECK([ovn-nbctl get Logical-Switch-Port p18 dynamic_addresses], [0], ["0a:00:00:00:00:13 192.168.1.10" ]) AT_CHECK([ovn-nbctl get Logical-Switch-Port p19 dynamic_addresses], [0], ["0a:00:00:00:00:14 192.168.1.11" ]) # Change a port's address to test for multiple ip's for a single address entry # and addresses set by the user. ovn-nbctl lsp-set-addresses p0 "0a:00:00:00:00:15 192.168.1.12 192.168.1.14" ovn-nbctl --wait=sb lsp-add sw0 p20 -- lsp-set-addresses p20 dynamic AT_CHECK([ovn-nbctl get Logical-Switch-Port p20 dynamic_addresses], [0], ["0a:00:00:00:00:16 192.168.1.13" ]) # Test for logical router port address management. ovn-nbctl create Logical_Router name=R1 ovn-nbctl -- --id=@lrp create Logical_Router_port name=sw0 \ network="192.168.1.1/24" mac=\"0a:00:00:00:00:17\" \ -- add Logical_Router R1 ports @lrp -- lsp-add sw0 rp-sw0 \ -- set Logical_Switch_Port rp-sw0 type=router options:router-port=sw0 ovn-nbctl --wait=sb lsp-add sw0 p21 -- lsp-set-addresses p21 dynamic AT_CHECK([ovn-nbctl get Logical-Switch-Port p21 dynamic_addresses], [0], ["0a:00:00:00:00:18 192.168.1.15" ]) # Test for address reuse after logical port is deleted. ovn-nbctl lsp-del p0 ovn-nbctl --wait=sb lsp-add sw0 p23 -- lsp-set-addresses p23 dynamic AT_CHECK([ovn-nbctl get Logical-Switch-Port p23 dynamic_addresses], [0], ["0a:00:00:00:00:19 192.168.1.2" ]) # Test for multiple addresses to one logical port. ovn-nbctl lsp-add sw0 p25 -- lsp-set-addresses p25 \ "0a:00:00:00:00:1a 192.168.1.12" "0a:00:00:00:00:1b 192.168.1.14" ovn-nbctl --wait=sb lsp-add sw0 p26 -- lsp-set-addresses p26 dynamic AT_CHECK([ovn-nbctl get Logical-Switch-Port p26 dynamic_addresses], [0], ["0a:00:00:00:00:1c 192.168.1.16" ]) # Test for exhausting subnet address space. ovn-nbctl ls-add sw2 -- add Logical-Switch sw2 other_config subnet=172.16.1.0/30 ovn-nbctl --wait=sb lsp-add sw2 p27 -- lsp-set-addresses p27 dynamic AT_CHECK([ovn-nbctl get Logical-Switch-Port p27 dynamic_addresses], [0], ["0a:00:00:00:00:1d 172.16.1.2" ]) ovn-nbctl --wait=sb lsp-add sw2 p28 -- lsp-set-addresses p28 dynamic AT_CHECK([ovn-nbctl get Logical-Switch-Port p28 dynamic_addresses], [0], ["0a:00:00:00:00:1e" ]) # Test that address management does not add duplicate MAC for lsp/lrp peers. ovn-nbctl create Logical_Router name=R2 ovn-nbctl ls-add sw3 ovn-nbctl lsp-add sw3 p29 -- lsp-set-addresses p29 \ "0a:00:00:00:00:1f" ovn-nbctl -- --id=@lrp create Logical_Router_port name=sw3 \ network="192.168.2.1/24" mac=\"0a:00:00:00:00:1f\" \ -- add Logical_Router R2 ports @lrp -- lsp-add sw3 rp-sw3 \ -- set Logical_Switch_Port rp-sw3 type=router options:router-port=sw3 ovn-nbctl --wait=sb lsp-add sw0 p30 -- lsp-set-addresses p30 dynamic AT_CHECK([ovn-nbctl get Logical-Switch-Port p30 dynamic_addresses], [0], ["0a:00:00:00:00:20 192.168.1.17" ]) # Test static MAC address with dynamically allocated IP ovn-nbctl --wait=sb lsp-add sw0 p31 -- lsp-set-addresses p31 \ "fe:dc:ba:98:76:54 dynamic" AT_CHECK([ovn-nbctl get Logical-Switch-Port p31 dynamic_addresses], [0], ["fe:dc:ba:98:76:54 192.168.1.18" ]) # Update the static MAC address with dynamically allocated IP and check # if the MAC address is updated in 'Logical_Switch_Port.dynamic_adddresses' ovn-nbctl --wait=sb lsp-set-addresses p31 "fe:dc:ba:98:76:55 dynamic" ovn-nbctl get Logical-Switch-Port p31 dynamic_addresses AT_CHECK([ovn-nbctl get Logical-Switch-Port p31 dynamic_addresses], [0], ["fe:dc:ba:98:76:55 192.168.1.18" ]) ovn-nbctl --wait=sb lsp-set-addresses p31 "dynamic" AT_CHECK([ovn-nbctl get Logical-Switch-Port p31 dynamic_addresses], [0], ["fe:dc:ba:98:76:55 192.168.1.18" ]) ovn-nbctl --wait=sb lsp-set-addresses p31 "fe:dc:ba:98:76:56 dynamic" AT_CHECK([ovn-nbctl get Logical-Switch-Port p31 dynamic_addresses], [0], ["fe:dc:ba:98:76:56 192.168.1.18" ]) # Test the exclude_ips from the IPAM list ovn-nbctl --wait=sb set logical_switch sw0 \ other_config:exclude_ips="192.168.1.19 192.168.1.21 192.168.1.23..192.168.1.50" ovn-nbctl --wait=sb lsp-add sw0 p32 -- lsp-set-addresses p32 \ "dynamic" # 192.168.1.20 should be assigned as 192.168.1.19 is excluded. AT_CHECK([ovn-nbctl get Logical-Switch-Port p32 dynamic_addresses], [0], ["0a:00:00:00:00:21 192.168.1.20" ]) ovn-nbctl --wait=sb lsp-add sw0 p33 -- lsp-set-addresses p33 \ "dynamic" # 192.168.1.22 should be assigned as 192.168.1.21 is excluded. AT_CHECK([ovn-nbctl get Logical-Switch-Port p33 dynamic_addresses], [0], ["0a:00:00:00:00:22 192.168.1.22" ]) ovn-nbctl --wait=sb lsp-add sw0 p34 -- lsp-set-addresses p34 \ "dynamic" # 192.168.1.51 should be assigned as 192.168.1.23-192.168.1.50 is excluded. AT_CHECK([ovn-nbctl get Logical-Switch-Port p34 dynamic_addresses], [0], ["0a:00:00:00:00:23 192.168.1.51" ]) # Now clear the exclude_ips list. 192.168.1.19 should be assigned. ovn-nbctl --wait=sb set Logical-switch sw0 other_config:exclude_ips="invalid" ovn-nbctl --wait=sb lsp-add sw0 p35 -- lsp-set-addresses p35 \ "dynamic" AT_CHECK([ovn-nbctl get Logical-Switch-Port p35 dynamic_addresses], [0], ["0a:00:00:00:00:24 192.168.1.19" ]) # Set invalid data in exclude_ips list. It should be ignored. ovn-nbctl --wait=sb set Logical-switch sw0 other_config:exclude_ips="182.168.1.30" ovn-nbctl --wait=sb lsp-add sw0 p36 -- lsp-set-addresses p36 \ "dynamic" # 192.168.1.21 should be assigned as that's the next free one. AT_CHECK([ovn-nbctl get Logical-Switch-Port p36 dynamic_addresses], [0], ["0a:00:00:00:00:25 192.168.1.21" ]) # Clear the dynamic addresses assignment request. ovn-nbctl --wait=sb clear logical_switch_port p36 addresses AT_CHECK([ovn-nbctl get Logical-Switch-Port p36 dynamic_addresses], [0], [[[]] ]) # Set IPv6 prefix ovn-nbctl --wait=sb set Logical-switch sw0 other_config:ipv6_prefix="aef0::" ovn-nbctl --wait=sb lsp-add sw0 p37 -- lsp-set-addresses p37 \ "dynamic" # With prefix aef0 and mac 0a:00:00:00:00:26, the dynamic IPv6 should be # - aef0::800:ff:fe00:26 (EUI64) AT_CHECK([ovn-nbctl get Logical-Switch-Port p37 dynamic_addresses], [0], ["0a:00:00:00:00:26 192.168.1.21 aef0::800:ff:fe00:26" ]) ovn-nbctl --wait=sb ls-add sw4 ovn-nbctl --wait=sb set Logical-switch sw4 other_config:ipv6_prefix="bef0::" ovn-nbctl --wait=sb lsp-add sw4 p38 -- lsp-set-addresses p38 \ "dynamic" AT_CHECK([ovn-nbctl get Logical-Switch-Port p38 dynamic_addresses], [0], ["0a:00:00:00:00:27 bef0::800:ff:fe00:27" ]) ovn-nbctl --wait=sb lsp-add sw4 p39 -- lsp-set-addresses p39 \ "f0:00:00:00:10:12 dynamic" AT_CHECK([ovn-nbctl get Logical-Switch-Port p39 dynamic_addresses], [0], ["f0:00:00:00:10:12 bef0::f200:ff:fe00:1012" ]) # Clear the other_config for sw4. No dynamic ip should be assigned. ovn-nbctl --wait=sb clear Logical-switch sw4 other_config ovn-nbctl --wait=sb lsp-add sw4 p40 -- lsp-set-addresses p40 \ "dynamic" AT_CHECK([ovn-nbctl get Logical-Switch-Port p40 dynamic_addresses], [0], [[[]] ]) # Test the case where IPv4 addresses are exhausted and IPv6 prefix is set ovn-nbctl --wait=sb set Logical-switch sw4 other_config:subnet=192.168.2.0/30 \ -- set Logical-switch sw4 other_config:ipv6_prefix="bef0::" # Now p40 should be assigned with dynamic addresses. AT_CHECK([ovn-nbctl get Logical-Switch-Port p40 dynamic_addresses], [0], ["0a:00:00:00:00:28 192.168.2.2 bef0::800:ff:fe00:28" ]) ovn-nbctl --wait=sb lsp-add sw4 p41 -- lsp-set-addresses p41 \ "dynamic" # p41 should not have IPv4 address (as the pool is exhausted). AT_CHECK([ovn-nbctl get Logical-Switch-Port p41 dynamic_addresses], [0], ["0a:00:00:00:00:29 bef0::800:ff:fe00:29" ]) as ovn-sb OVS_APP_EXIT_AND_WAIT([ovsdb-server]) as ovn-nb OVS_APP_EXIT_AND_WAIT([ovsdb-server]) as northd OVS_APP_EXIT_AND_WAIT([ovn-northd]) AT_CLEANUP AT_SETUP([ovn -- ipam connectivity]) AT_SKIP_IF([test $HAVE_PYTHON = no]) ovn_start ovn-nbctl lr-add R1 # Test for a ping using dynamically allocated addresses. ovn-nbctl ls-add foo -- add Logical_Switch foo other_config subnet=192.168.1.0/24 ovn-nbctl ls-add alice -- add Logical_Switch alice other_config subnet=192.168.2.0/24 # Connect foo to R1 ovn-nbctl lrp-add R1 foo 00:00:00:01:02:03 192.168.1.1/24 ovn-nbctl lsp-add foo rp-foo -- set Logical_Switch_Port rp-foo type=router \ options:router-port=foo \ -- lsp-set-addresses rp-foo router # Connect alice to R1 ovn-nbctl lrp-add R1 alice 00:00:00:01:02:04 192.168.2.1/24 ovn-nbctl lsp-add alice rp-alice -- set Logical_Switch_Port rp-alice type=router \ options:router-port=alice addresses=\"00:00:00:01:02:04\" # Create logical port foo1 in foo ovn-nbctl --wait=sb lsp-add foo foo1 \ -- lsp-set-addresses foo1 "dynamic" AT_CHECK([ovn-nbctl --timeout=10 wait-until Logical-Switch-Port foo1 dynamic_addresses='"0a:00:00:00:00:01 192.168.1.2"'], [0]) # Create logical port alice1 in alice ovn-nbctl --wait=sb lsp-add alice alice1 \ -- lsp-set-addresses alice1 "dynamic" AT_CHECK([ovn-nbctl --timeout=10 wait-until Logical-Switch-Port alice1 dynamic_addresses='"0a:00:00:00:00:02 192.168.2.2"']) # Create logical port foo2 in foo ovn-nbctl --wait=sb lsp-add foo foo2 \ -- lsp-set-addresses foo2 "dynamic" AT_CHECK([ovn-nbctl --timeout=10 wait-until Logical-Switch-Port foo2 dynamic_addresses='"0a:00:00:00:00:03 192.168.1.3"']) # Create a hypervisor and create OVS ports corresponding to logical ports. net_add n1 sim_add hv1 as hv1 ovs-vsctl add-br br-phys ovn_attach n1 br-phys 192.168.0.1 ovs-vsctl -- add-port br-int hv1-vif1 -- \ set interface hv1-vif1 external-ids:iface-id=foo1 \ options:tx_pcap=hv1/vif1-tx.pcap \ options:rxq_pcap=hv1/vif1-rx.pcap \ ofport-request=1 ovs-vsctl -- add-port br-int hv1-vif2 -- \ set interface hv1-vif2 external-ids:iface-id=foo2 \ options:tx_pcap=hv1/vif2-tx.pcap \ options:rxq_pcap=hv1/vif2-rx.pcap \ ofport-request=2 ovs-vsctl -- add-port br-int hv1-vif3 -- \ set interface hv1-vif3 external-ids:iface-id=alice1 \ options:tx_pcap=hv1/vif3-tx.pcap \ options:rxq_pcap=hv1/vif3-rx.pcap \ ofport-request=3 # Allow some time for ovn-northd and ovn-controller to catch up. # XXX This should be more systematic. sleep 1 ip_to_hex() { printf "%02x%02x%02x%02x" "$@" } # Send ip packets between foo1 and foo2 src_mac="0a0000000001" dst_mac="0a0000000003" src_ip=`ip_to_hex 192 168 1 2` dst_ip=`ip_to_hex 192 168 1 3` packet=${dst_mac}${src_mac}08004500001c0000000040110000${src_ip}${dst_ip}0035111100080000 as hv1 ovs-appctl netdev-dummy/receive hv1-vif1 $packet # Send ip packets between foo1 and alice1 src_mac="0a0000000001" dst_mac="000000010203" src_ip=`ip_to_hex 192 168 1 2` dst_ip=`ip_to_hex 192 168 2 2` packet=${dst_mac}${src_mac}08004500001c0000000040110000${src_ip}${dst_ip}0035111100080000 as hv1 ovs-appctl netdev-dummy/receive hv1-vif1 $packet echo "---------NB dump-----" ovn-nbctl show echo "---------------------" ovn-nbctl list logical_router echo "---------------------" ovn-nbctl list logical_router_port echo "---------------------" echo "---------SB dump-----" ovn-sbctl list datapath_binding echo "---------------------" ovn-sbctl list port_binding echo "---------------------" echo "------ hv1 dump ----------" as hv1 ovs-ofctl dump-flows br-int # Packet to Expect at foo2 src_mac="0a0000000001" dst_mac="0a0000000003" src_ip=`ip_to_hex 192 168 1 2` dst_ip=`ip_to_hex 192 168 1 3` expected=${dst_mac}${src_mac}08004500001c0000000040110000${src_ip}${dst_ip}0035111100080000 $PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > received1.packets echo $expected > expout AT_CHECK([cat received1.packets], [0], [expout]) # Packet to Expect at alice1 src_mac="000000010204" dst_mac="0a0000000002" src_ip=`ip_to_hex 192 168 1 2` dst_ip=`ip_to_hex 192 168 2 2` expected=${dst_mac}${src_mac}08004500001c000000003f110100${src_ip}${dst_ip}0035111100080000 $PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif3-tx.pcap > received2.packets echo $expected > expout AT_CHECK([cat received2.packets], [0], [expout]) OVN_CLEANUP([hv1]) AT_CLEANUP AT_SETUP([ovn -- ovs-vswitchd restart]) AT_KEYWORDS([vswitchd]) AT_SKIP_IF([test $HAVE_PYTHON = no]) ovn_start ovn-nbctl ls-add ls1 ovn-nbctl lsp-add ls1 ls1-lp1 \ -- lsp-set-addresses ls1-lp1 "f0:00:00:00:00:01 10.0.0.4" ovn-nbctl lsp-set-port-security ls1-lp1 "f0:00:00:00:00:01 10.0.0.4" net_add n1 sim_add hv1 as hv1 ovs-vsctl add-br br-phys ovn_attach n1 br-phys 192.168.0.1 ovs-vsctl -- add-port br-int hv1-vif1 -- \ set interface hv1-vif1 external-ids:iface-id=ls1-lp1 \ options:tx_pcap=hv1/vif1-tx.pcap \ options:rxq_pcap=hv1/vif1-rx.pcap \ ofport-request=1 ovn_populate_arp sleep 2 as hv1 ovs-vsctl show echo "---------------------" ovn-sbctl dump-flows echo "---------------------" echo "------ hv1 dump ----------" as hv1 ovs-ofctl dump-flows br-int total_flows=`as hv1 ovs-ofctl dump-flows br-int | wc -l` echo "Total flows before vswitchd restart = " $total_flows # Code taken from ovs-save utility save_flows () { echo "ovs-ofctl add-flows br-int - << EOF" > restore_flows.sh as hv1 ovs-ofctl dump-flows "br-int" | sed -e '/NXST_FLOW/d' \ -e 's/\(idle\|hard\)_age=[^,]*,//g' >> restore_flows.sh echo "EOF" >> restore_flows.sh } restart_vswitchd () { restore_flows=$1 if test $restore_flows = true; then save_flows fi as hv1 OVS_APP_EXIT_AND_WAIT([ovs-vswitchd]) if test $restore_flows = true; then as hv1 ovs-vsctl --no-wait set open_vswitch . other_config:flow-restore-wait="true" fi as hv1 start_daemon ovs-vswitchd --enable-dummy=system -vvconn -vofproto_dpif -vunixctl ovs-ofctl dump-flows br-int if test $restore_flows = true; then sh ./restore_flows.sh echo "Flows after restore" as hv1 ovs-ofctl dump-flows br-int ovs-vsctl --no-wait --if-exists remove open_vswitch . other_config \ flow-restore-wait="true" fi } # Save the flows, restart vswitchd and restore the flows restart_vswitchd true OVS_WAIT_UNTIL([ total_flows_after_restart=`as hv1 ovs-ofctl dump-flows br-int | wc -l` echo "Total flows after vswitchd restart = " $total_flows_after_restart test "${total_flows}" = "${total_flows_after_restart}" ]) # Restart vswitchd without restoring restart_vswitchd false OVS_WAIT_UNTIL([ total_flows_after_restart=`as hv1 ovs-ofctl dump-flows br-int | wc -l` echo "Total flows after vswitchd restart = " $total_flows_after_restart test "${total_flows}" = "${total_flows_after_restart}" ]) OVN_CLEANUP([hv1]) AT_CLEANUP AT_SETUP([ovn -- send arp for nexthop]) AT_SKIP_IF([test $HAVE_PYTHON = no]) ovn_start # Topology: Two LSs - ls1 and ls2 are connected via router r0 # Create logical switches ovn-nbctl ls-add ls1 ovn-nbctl ls-add ls2 # Create router ovn-nbctl create Logical_Router name=lr0 # Add router ls1p1 port to gateway router ovn-nbctl lrp-add lr0 lrp-ls1lp1 f0:00:00:00:00:01 192.168.0.1/24 ovn-nbctl lsp-add ls1 ls1lp1 -- set Logical_Switch_Port ls1lp1 \ type=router options:router-port=lrp-ls1lp1 \ addresses='"f0:00:00:00:00:01 192.168.0.1"' # Add router ls2p2 port to gateway router ovn-nbctl lrp-add lr0 lrp-ls2lp1 f0:00:00:00:00:02 192.168.1.1/24 ovn-nbctl lsp-add ls2 ls2lp1 -- set Logical_Switch_Port ls2lp1 \ type=router options:router-port=lrp-ls2lp1 \ addresses='"f0:00:00:00:00:02 192.168.1.1"' # Set default gateway (nexthop) to 192.168.1.254 ovn-nbctl lr-route-add lr0 "0.0.0.0/0" 192.168.1.254 lrp-ls2lp1 # Create logical port ls1lp2 in ls1 ovn-nbctl lsp-add ls1 ls1lp2 \ -- lsp-set-addresses ls1lp2 "f0:00:00:00:00:03 192.168.0.2" # Create logical port ls2lp2 in ls2 ovn-nbctl lsp-add ls2 ls2lp2 \ -- lsp-set-addresses ls2lp2 "f0:00:00:00:00:04 192.168.1.10" net_add n1 sim_add hv1 as hv1 ovs-vsctl add-br br-phys ovn_attach n1 br-phys 192.168.0.1 ovs-vsctl -- add-port br-int hv1-ls1lp2 -- \ set interface hv1-ls1lp2 external-ids:iface-id=ls1lp2 \ options:tx_pcap=hv1/ls1lp2-tx.pcap \ options:rxq_pcap=hv1/ls1lp2-rx.pcap \ ofport-request=1 ovs-vsctl -- add-port br-int hv1-ls2lp2 -- \ set interface hv1-ls2lp2 external-ids:iface-id=ls2lp2 \ options:tx_pcap=hv1/ls2lp2-tx.pcap \ options:rxq_pcap=hv1/ls2lp2-rx.pcap \ ofport-request=2 # Allow some time for ovn-northd and ovn-controller to catch up. # XXX This should be more systematic. sleep 1 echo "---------NB dump-----" ovn-nbctl show echo "---------------------" ovn-nbctl list logical_router echo "---------------------" ovn-nbctl list logical_router_port echo "---------------------" echo "---------SB dump-----" ovn-sbctl list datapath_binding echo "---------------------" ovn-sbctl list port_binding echo "---------------------" ovn-sbctl dump-flows echo "---------------------" ovn-sbctl list chassis ovn-sbctl list encap echo "---------------------" echo "------Flows dump-----" as hv1 ovs-ofctl dump-flows echo "---------------------" ip_to_hex() { printf "%02x%02x%02x%02x" "$@" } src_mac="f00000000003" dst_mac="f00000000001" src_ip=`ip_to_hex 192 168 0 2` dst_ip=`ip_to_hex 8 8 8 8` packet=${dst_mac}${src_mac}08004500001c0000000040110000${src_ip}${dst_ip}0035111100080000 # Send IP packet destined to 8.8.8.8 from lsp1lp2 as hv1 ovs-appctl netdev-dummy/receive hv1-ls1lp2 $packet trim_zeros() { sed 's/\(00\)\{1,\}$//' } # ARP packet should be received with Target IP Address set to 192.168.1.254 and # not 8.8.8.8 $PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/ls2lp2-tx.pcap | trim_zeros > packets expected="fffffffffffff0000000000208060001080006040001f00000000002c0a80101000000000000c0a801fe" echo $expected > expout AT_CHECK([cat packets], [0], [expout]) cat packets OVN_CLEANUP([hv1]) AT_CLEANUP AT_SETUP([ovn -- send gratuitous arp for nat ips in localnet]) AT_SKIP_IF([test $HAVE_PYTHON = no]) ovn_start # Create logical switch ovn-nbctl ls-add ls0 # Create gateway router ovn-nbctl create Logical_Router name=lr0 options:chassis=hv1 # Add router port to gateway router ovn-nbctl lrp-add lr0 lrp0 f0:00:00:00:00:01 192.168.0.1/24 ovn-nbctl lsp-add ls0 lrp0-rp -- set Logical_Switch_Port lrp0-rp \ type=router options:router-port=lrp0 addresses='"f0:00:00:00:00:01"' # Add nat-address option ovn-nbctl lsp-set-options lrp0-rp router-port=lrp0 nat-addresses="f0:00:00:00:00:01 192.168.0.2" net_add n1 sim_add hv1 as hv1 ovs-vsctl \ -- add-br br-phys \ -- add-br br-eth0 ovn_attach n1 br-phys 192.168.0.1 AT_CHECK([ovs-vsctl set Open_vSwitch . external-ids:ovn-bridge-mappings=physnet1:br-eth0]) AT_CHECK([ovs-vsctl add-port br-eth0 snoopvif -- set Interface snoopvif options:tx_pcap=hv1/snoopvif-tx.pcap options:rxq_pcap=hv1/snoopvif-rx.pcap]) # Create a localnet port. AT_CHECK([ovn-nbctl lsp-add ls0 ln_port]) AT_CHECK([ovn-nbctl lsp-set-addresses ln_port unknown]) AT_CHECK([ovn-nbctl lsp-set-type ln_port localnet]) AT_CHECK([ovn-nbctl lsp-set-options ln_port network_name=physnet1]) # Wait for packet to be received. OVS_WAIT_UNTIL([test `wc -c < "hv1/snoopvif-tx.pcap"` -ge 50]) trim_zeros() { sed 's/\(00\)\{1,\}$//' } $PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/snoopvif-tx.pcap | trim_zeros > packets expected="fffffffffffff0000000000108060001080006040001f00000000001c0a80002000000000000c0a80002" echo $expected > expout AT_CHECK([sort packets], [0], [expout]) cat packets OVN_CLEANUP([hv1]) AT_CLEANUP AT_SETUP([ovn -- send gratuitous arp with nat-addresses router in localnet]) AT_SKIP_IF([test $HAVE_PYTHON = no]) ovn_start # Create logical switch ovn-nbctl ls-add ls0 # Create gateway router ovn-nbctl create Logical_Router name=lr0 options:chassis=hv1 # Add router port to gateway router ovn-nbctl lrp-add lr0 lrp0 f0:00:00:00:00:01 192.168.0.1/24 ovn-nbctl lsp-add ls0 lrp0-rp -- set Logical_Switch_Port lrp0-rp \ type=router options:router-port=lrp0 addresses='"f0:00:00:00:00:01"' # Add nat-address option ovn-nbctl lsp-set-options lrp0-rp router-port=lrp0 nat-addresses="router" # Add NAT rules AT_CHECK([ovn-nbctl lr-nat-add lr0 snat 192.168.0.1 10.0.0.0/24]) AT_CHECK([ovn-nbctl lr-nat-add lr0 dnat 192.168.0.2 10.0.0.1]) # Add load balancers AT_CHECK([ovn-nbctl lb-add lb0 192.168.0.3:80 10.0.0.2:80,10.0.0.3:80]) AT_CHECK([ovn-nbctl lr-lb-add lr0 lb0]) AT_CHECK([ovn-nbctl lb-add lb1 192.168.0.3:8080 10.0.0.2:8080,10.0.0.3:8080]) AT_CHECK([ovn-nbctl lr-lb-add lr0 lb1]) net_add n1 sim_add hv1 as hv1 ovs-vsctl \ -- add-br br-phys \ -- add-br br-eth0 ovn_attach n1 br-phys 192.168.0.1 AT_CHECK([ovs-vsctl set Open_vSwitch . external-ids:ovn-bridge-mappings=physnet1:br-eth0]) AT_CHECK([ovs-vsctl add-port br-eth0 snoopvif -- set Interface snoopvif options:tx_pcap=hv1/snoopvif-tx.pcap options:rxq_pcap=hv1/snoopvif-rx.pcap]) # Create a localnet port. AT_CHECK([ovn-nbctl lsp-add ls0 ln_port]) AT_CHECK([ovn-nbctl lsp-set-addresses ln_port unknown]) AT_CHECK([ovn-nbctl lsp-set-type ln_port localnet]) AT_CHECK([ovn-nbctl lsp-set-options ln_port network_name=physnet1]) # Wait for packet to be received. OVS_WAIT_UNTIL([test `wc -c < "hv1/snoopvif-tx.pcap"` -ge 50]) trim_zeros() { sed 's/\(00\)\{1,\}$//' } $PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/snoopvif-tx.pcap | trim_zeros > packets expected="fffffffffffff0000000000108060001080006040001f00000000001c0a80001000000000000c0a80001" echo $expected > expout expected="fffffffffffff0000000000108060001080006040001f00000000001c0a80002000000000000c0a80002" echo $expected >> expout expected="fffffffffffff0000000000108060001080006040001f00000000001c0a80003000000000000c0a80003" echo $expected >> expout AT_CHECK([sort packets], [0], [expout]) cat packets OVN_CLEANUP([hv1]) AT_CLEANUP AT_SETUP([ovn -- delete mac bindings]) ovn_start net_add n1 sim_add hv1 as hv1 ovs-vsctl -- add-br br-phys ovn_attach n1 br-phys 192.168.0.1 # Create logical switch ls0 ovn-nbctl ls-add ls0 # Create ports lp0, lp1 in ls0 ovn-nbctl lsp-add ls0 lp0 ovn-nbctl lsp-add ls0 lp1 ovn-nbctl lsp-set-addresses lp0 "f0:00:00:00:00:01 192.168.0.1" ovn-nbctl lsp-set-addresses lp1 "f0:00:00:00:00:02 192.168.0.2" dp_uuid=`ovn-sbctl find datapath | grep uuid | cut -f2 -d ":" | cut -f2 -d " "` ovn-sbctl create MAC_Binding ip=10.0.0.1 datapath=$dp_uuid logical_port=lp0 mac="mac1" ovn-sbctl create MAC_Binding ip=10.0.0.1 datapath=$dp_uuid logical_port=lp1 mac="mac2" ovn-sbctl find MAC_Binding # Delete port lp0 and check that its MAC_Binding is deleted. ovn-nbctl lsp-del lp0 ovn-sbctl find MAC_Binding OVS_WAIT_UNTIL([test `ovn-sbctl find MAC_Binding logical_port=lp0 | wc -l` = 0]) # Delete logical switch ls0 and check that its MAC_Binding is deleted. ovn-nbctl ls-del ls0 ovn-sbctl find MAC_Binding OVS_WAIT_UNTIL([test `ovn-sbctl find MAC_Binding | wc -l` = 0]) OVN_CLEANUP([hv1]) AT_CLEANUP AT_SETUP([ovn -- conntrack zone allocation]) AT_SKIP_IF([test $HAVE_PYTHON = no]) ovn_start # Logical network: # 2 logical switches "foo" (192.168.1.0/24) and "bar" (172.16.1.0/24) # connected to a router R1. # foo has foo1 to act as a client. # bar has bar1, bar2, bar3 to act as servers. net_add n1 sim_add hv1 as hv1 ovs-vsctl add-br br-phys ovn_attach n1 br-phys 192.168.0.1 for i in foo1 bar1 bar2 bar3; do ovs-vsctl -- add-port br-int $i -- \ set interface $i external-ids:iface-id=$i \ options:tx_pcap=hv1/$i-tx.pcap \ options:rxq_pcap=hv1/$i-rx.pcap done ovn-nbctl create Logical_Router name=R1 ovn-nbctl ls-add foo ovn-nbctl ls-add bar # Connect foo to R1 ovn-nbctl lrp-add R1 foo 00:00:01:01:02:03 192.168.1.1/24 ovn-nbctl lsp-add foo rp-foo -- set Logical_Switch_Port rp-foo \ type=router options:router-port=foo addresses=\"00:00:01:01:02:03\" # Connect bar to R1 ovn-nbctl lrp-add R1 bar 00:00:01:01:02:04 172.16.1.1/24 ovn-nbctl lsp-add bar rp-bar -- set Logical_Switch_Port rp-bar \ type=router options:router-port=bar addresses=\"00:00:01:01:02:04\" # Create logical port foo1 in foo ovn-nbctl lsp-add foo foo1 \ -- lsp-set-addresses foo1 "f0:00:00:01:02:03 192.168.1.2" # Create logical port bar1, bar2 and bar3 in bar for i in `seq 1 3`; do ip=`expr $i + 1` ovn-nbctl lsp-add bar bar$i \ -- lsp-set-addresses bar$i "f0:00:0a:01:02:$i 172.16.1.$ip" done OVS_WAIT_UNTIL([test `ovs-ofctl dump-flows br-int table=0 | grep REG13 | wc -l` -eq 4]) OVN_CLEANUP([hv1]) AT_CLEANUP AT_SETUP([ovn -- tag allocation]) ovn_start AT_CHECK([ovn-nbctl ls-add ls0]) AT_CHECK([ovn-nbctl lsp-add ls0 parent1]) AT_CHECK([ovn-nbctl lsp-add ls0 parent2]) AT_CHECK([ovn-nbctl ls-add ls1]) dnl When a tag is provided, no allocation is done AT_CHECK([ovn-nbctl --wait=sb lsp-add ls1 c0 parent1 3]) AT_CHECK([ovn-nbctl lsp-get-tag c0], [0], [3 ]) dnl The same 'tag' gets created in southbound database. AT_CHECK([ovn-sbctl --data=bare --no-heading --columns=tag find port_binding \ logical_port="c0"], [0], [3 ]) dnl Allocate tags and see it getting created in both NB and SB AT_CHECK([ovn-nbctl --wait=sb lsp-add ls1 c1 parent1 0]) AT_CHECK([ovn-nbctl lsp-get-tag c1], [0], [1 ]) AT_CHECK([ovn-sbctl --data=bare --no-heading --columns=tag find port_binding \ logical_port="c1"], [0], [1 ]) AT_CHECK([ovn-nbctl --wait=sb lsp-add ls1 c2 parent1 0]) AT_CHECK([ovn-nbctl lsp-get-tag c2], [0], [2 ]) AT_CHECK([ovn-sbctl --data=bare --no-heading --columns=tag find port_binding \ logical_port="c2"], [0], [2 ]) AT_CHECK([ovn-nbctl --wait=sb lsp-add ls1 c3 parent1 0]) AT_CHECK([ovn-nbctl lsp-get-tag c3], [0], [4 ]) AT_CHECK([ovn-sbctl --data=bare --no-heading --columns=tag find port_binding \ logical_port="c3"], [0], [4 ]) dnl A different parent. AT_CHECK([ovn-nbctl --wait=sb lsp-add ls1 c4 parent2 0]) AT_CHECK([ovn-nbctl lsp-get-tag c4], [0], [1 ]) AT_CHECK([ovn-sbctl --data=bare --no-heading --columns=tag find port_binding \ logical_port="c4"], [0], [1 ]) AT_CHECK([ovn-nbctl --wait=sb lsp-add ls1 c5 parent2 0]) AT_CHECK([ovn-nbctl lsp-get-tag c5], [0], [2 ]) AT_CHECK([ovn-sbctl --data=bare --no-heading --columns=tag find port_binding \ logical_port="c5"], [0], [2 ]) dnl Delete a logical port and create a new one. AT_CHECK([ovn-nbctl --wait=sb lsp-del c1]) AT_CHECK([ovn-nbctl --wait=sb lsp-add ls1 c6 parent1 0]) AT_CHECK([ovn-nbctl lsp-get-tag c6], [0], [1 ]) AT_CHECK([ovn-sbctl --data=bare --no-heading --columns=tag find port_binding \ logical_port="c6"], [0], [1 ]) dnl Restart northd to see that the same allocation remains. as northd OVS_APP_EXIT_AND_WAIT([ovn-northd]) start_daemon ovn-northd \ --ovnnb-db=unix:"$ovs_base"/ovn-nb/ovn-nb.sock \ --ovnsb-db=unix:"$ovs_base"/ovn-sb/ovn-sb.sock dnl Create a switch to make sure that ovn-northd has run through the main loop. AT_CHECK([ovn-nbctl --wait=sb ls-add ls-dummy]) AT_CHECK([ovn-nbctl lsp-get-tag c0], [0], [3 ]) AT_CHECK([ovn-nbctl lsp-get-tag c6], [0], [1 ]) AT_CHECK([ovn-nbctl lsp-get-tag c2], [0], [2 ]) AT_CHECK([ovn-nbctl lsp-get-tag c3], [0], [4 ]) AT_CHECK([ovn-nbctl lsp-get-tag c4], [0], [1 ]) AT_CHECK([ovn-nbctl lsp-get-tag c5], [0], [2 ]) dnl Create a switch port with a tag that has already been allocated. dnl It should go through fine with a duplicate tag. AT_CHECK([ovn-nbctl --wait=sb lsp-add ls1 c7 parent2 2]) AT_CHECK([ovn-nbctl lsp-get-tag c7], [0], [2 ]) AT_CHECK([ovn-sbctl --data=bare --no-heading --columns=tag find port_binding \ logical_port="c7"], [0], [2 ]) AT_CHECK([ovn-nbctl lsp-get-tag c5], [0], [2 ]) AT_CHECK([ovn-nbctl ls-add ls2]) dnl When there is no parent_name provided (for say, 'localnet'), 'tag_request' dnl gets copied to 'tag' AT_CHECK([ovn-nbctl --wait=sb lsp-add ls2 local0 "" 25]) AT_CHECK([ovn-nbctl lsp-get-tag local0], [0], [25 ]) dnl The same 'tag' gets created in southbound database. AT_CHECK([ovn-sbctl --data=bare --no-heading --columns=tag find port_binding \ logical_port="local0"], [0], [25 ]) dnl If 'tag_request' is 0 for localnet, nothing gets written to 'tag' AT_CHECK([ovn-nbctl --wait=sb lsp-add ls2 local1 "" 0]) AT_CHECK([ovn-nbctl lsp-get-tag local1]) dnl change the tag_request. AT_CHECK([ovn-nbctl --wait=sb set logical_switch_port local1 tag_request=50]) AT_CHECK([ovn-nbctl lsp-get-tag local1], [0], [50 ]) AT_CLEANUP AT_SETUP([ovn -- lsp deletion and broadcast-flow deletion on localnet]) ovn_start ovn-nbctl ls-add lsw0 net_add n1 for i in 1 2; do sim_add hv$i as hv$i ovs-vsctl add-br br-phys ovn_attach n1 br-phys 192.168.0.$i ovs-vsctl add-br br-eth0 AT_CHECK([ovs-vsctl set Open_vSwitch . external-ids:ovn-bridge-mappings=physnet1:br-eth0]) done # Create a localnet port. AT_CHECK([ovn-nbctl lsp-add lsw0 ln_port]) AT_CHECK([ovn-nbctl lsp-set-addresses ln_port unknown]) AT_CHECK([ovn-nbctl lsp-set-type ln_port localnet]) AT_CHECK([ovn-nbctl lsp-set-options ln_port network_name=physnet1]) # Create 3 vifs. AT_CHECK([ovn-nbctl lsp-add lsw0 localvif1]) AT_CHECK([ovn-nbctl lsp-set-addresses localvif1 "f0:00:00:00:00:01 192.168.1.1"]) AT_CHECK([ovn-nbctl lsp-set-port-security localvif1 "f0:00:00:00:00:01"]) AT_CHECK([ovn-nbctl lsp-add lsw0 localvif2]) AT_CHECK([ovn-nbctl lsp-set-addresses localvif2 "f0:00:00:00:00:01 192.168.1.2"]) AT_CHECK([ovn-nbctl lsp-set-port-security localvif2 "f0:00:00:00:00:02"]) AT_CHECK([ovn-nbctl lsp-add lsw0 localvif3]) AT_CHECK([ovn-nbctl lsp-set-addresses localvif3 "f0:00:00:00:00:03 192.168.1.3"]) AT_CHECK([ovn-nbctl lsp-set-port-security localvif3 "f0:00:00:00:00:03"]) # Bind the localvif1 to hv1. as hv1 AT_CHECK([ovs-vsctl add-port br-int localvif1 -- set Interface localvif1 external_ids:iface-id=localvif1]) # On hv1, check that there are no flows outputting bcast to tunnel OVS_WAIT_UNTIL([test `ovs-ofctl dump-flows br-int table=32 | ofctl_strip | grep output | wc -l` -eq 0]) # On hv2, check that no flow outputs bcast to tunnel to hv1. as hv2 OVS_WAIT_UNTIL([test `ovs-ofctl dump-flows br-int table=32 | ofctl_strip | grep output | wc -l` -eq 0]) # Now bind vif2 on hv2. AT_CHECK([ovs-vsctl add-port br-int localvif2 -- set Interface localvif2 external_ids:iface-id=localvif2]) # At this point, the broadcast flow on vif2 should be deleted. # because, there is now a localnet vif bound (table=32 programming logic) OVS_WAIT_UNTIL([test `ovs-ofctl dump-flows br-int table=32 | ofctl_strip | grep output | wc -l` -eq 0]) # Verify that the local net patch port exists on hv2. OVS_WAIT_UNTIL([test `ovs-vsctl show | grep "Port patch-br-int-to-ln_port" | wc -l` -eq 1]) # Now bind vif3 on hv2. AT_CHECK([ovs-vsctl add-port br-int localvif3 -- set Interface localvif3 external_ids:iface-id=localvif3]) # Verify that the local net patch port still exists on hv2 OVS_WAIT_UNTIL([test `ovs-vsctl show | grep "Port patch-br-int-to-ln_port" | wc -l` -eq 1]) # Delete localvif2 AT_CHECK([ovn-nbctl lsp-del localvif2]) # Verify that the local net patch port still exists on hv2, # because, localvif3 is still bound. OVS_WAIT_UNTIL([test `ovs-vsctl show | grep "Port patch-br-int-to-ln_port" | wc -l` -eq 1]) OVN_CLEANUP([hv1],[hv2]) AT_CLEANUP AT_SETUP([ovn -- ACL logging]) AT_KEYWORDS([ovn]) ovn_start net_add n1 sim_add hv as hv ovs-vsctl add-br br-phys ovn_attach n1 br-phys 192.168.0.1 for i in lp1 lp2; do ovs-vsctl -- add-port br-int $i -- \ set interface $i external-ids:iface-id=$i \ options:tx_pcap=hv/$i-tx.pcap \ options:rxq_pcap=hv/$i-rx.pcap done lp1_mac="f0:00:00:00:00:01" lp1_ip="192.168.1.2" lp2_mac="f0:00:00:00:00:02" lp2_ip="192.168.1.3" ovn-nbctl ls-add lsw0 ovn-nbctl --wait=sb lsp-add lsw0 lp1 ovn-nbctl --wait=sb lsp-add lsw0 lp2 ovn-nbctl lsp-set-addresses lp1 $lp1_mac ovn-nbctl lsp-set-addresses lp2 $lp2_mac ovn-nbctl --wait=sb sync ovn-nbctl acl-add lsw0 to-lport 1000 'tcp.dst==80' drop ovn-nbctl --log --severity=alert --name=drop-flow acl-add lsw0 to-lport 1000 'tcp.dst==81' drop ovn-nbctl acl-add lsw0 to-lport 1000 'tcp.dst==82' allow ovn-nbctl --log --severity=info --name=allow-flow acl-add lsw0 to-lport 1000 'tcp.dst==83' allow ovn-nbctl acl-add lsw0 to-lport 1000 'tcp.dst==84' allow-related ovn-nbctl --log acl-add lsw0 to-lport 1000 'tcp.dst==85' allow-related ovn-nbctl acl-add lsw0 to-lport 1000 'tcp.dst==86' reject ovn-nbctl --log --severity=alert --name=reject-flow acl-add lsw0 to-lport 1000 'tcp.dst==87' reject ovn-sbctl dump-flows # Send packet that should be dropped without logging. packet="inport==\"lp1\" && eth.src==$lp1_mac && eth.dst==$lp2_mac && ip4 && ip.ttl==64 && ip4.src==$lp1_ip && ip4.dst==$lp2_ip && tcp && tcp.flags==2 && tcp.src==4360 && tcp.dst==80" as hv ovs-appctl -t ovn-controller inject-pkt "$packet" # Send packet that should be dropped with logging. packet="inport==\"lp1\" && eth.src==$lp1_mac && eth.dst==$lp2_mac && ip4 && ip.ttl==64 && ip4.src==$lp1_ip && ip4.dst==$lp2_ip && tcp && tcp.flags==2 && tcp.src==4361 && tcp.dst==81" as hv ovs-appctl -t ovn-controller inject-pkt "$packet" # Send packet that should be allowed without logging. packet="inport==\"lp1\" && eth.src==$lp1_mac && eth.dst==$lp2_mac && ip4 && ip.ttl==64 && ip4.src==$lp1_ip && ip4.dst==$lp2_ip && tcp && tcp.flags==2 && tcp.src==4362 && tcp.dst==82" as hv ovs-appctl -t ovn-controller inject-pkt "$packet" # Send packet that should be allowed with logging. packet="inport==\"lp1\" && eth.src==$lp1_mac && eth.dst==$lp2_mac && ip4 && ip.ttl==64 && ip4.src==$lp1_ip && ip4.dst==$lp2_ip && tcp && tcp.flags==2 && tcp.src==4363 && tcp.dst==83" as hv ovs-appctl -t ovn-controller inject-pkt "$packet" # Send packet that should allow related flows without logging. packet="inport==\"lp1\" && eth.src==$lp1_mac && eth.dst==$lp2_mac && ip4 && ip.ttl==64 && ip4.src==$lp1_ip && ip4.dst==$lp2_ip && tcp && tcp.flags==2 && tcp.src==4364 && tcp.dst==84" as hv ovs-appctl -t ovn-controller inject-pkt "$packet" # Send packet that should allow related flows with logging. packet="inport==\"lp1\" && eth.src==$lp1_mac && eth.dst==$lp2_mac && ip4 && ip.ttl==64 && ip4.src==$lp1_ip && ip4.dst==$lp2_ip && tcp && tcp.flags==2 && tcp.src==4365 && tcp.dst==85" as hv ovs-appctl -t ovn-controller inject-pkt "$packet" # Send packet that should allow related flows with logging. packet="inport==\"lp1\" && eth.src==$lp1_mac && eth.dst==$lp2_mac && ip4 && ip.ttl==64 && ip4.src==$lp1_ip && ip4.dst==$lp2_ip && tcp && tcp.flags==2 && tcp.src==4366 && tcp.dst==86" as hv ovs-appctl -t ovn-controller inject-pkt "$packet" # Send packet that should allow related flows with logging. packet="inport==\"lp1\" && eth.src==$lp1_mac && eth.dst==$lp2_mac && ip4 && ip.ttl==64 && ip4.src==$lp1_ip && ip4.dst==$lp2_ip && tcp && tcp.flags==2 && tcp.src==4367 && tcp.dst==87" as hv ovs-appctl -t ovn-controller inject-pkt "$packet" OVS_WAIT_UNTIL([ test 4 = $(grep -c 'acl_log' hv/ovn-controller.log) ]) AT_CHECK([grep 'acl_log' hv/ovn-controller.log | sed 's/.*name=/name=/'], [0], [dnl name="drop-flow", verdict=drop, severity=alert: tcp,vlan_tci=0x0000,dl_src=f0:00:00:00:00:01,dl_dst=f0:00:00:00:00:02,nw_src=192.168.1.2,nw_dst=192.168.1.3,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=4361,tp_dst=81,tcp_flags=syn name="allow-flow", verdict=allow, severity=info: tcp,vlan_tci=0x0000,dl_src=f0:00:00:00:00:01,dl_dst=f0:00:00:00:00:02,nw_src=192.168.1.2,nw_dst=192.168.1.3,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=4363,tp_dst=83,tcp_flags=syn name="", verdict=allow, severity=info: tcp,vlan_tci=0x0000,dl_src=f0:00:00:00:00:01,dl_dst=f0:00:00:00:00:02,nw_src=192.168.1.2,nw_dst=192.168.1.3,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=4365,tp_dst=85,tcp_flags=syn name="reject-flow", verdict=reject, severity=alert: tcp,vlan_tci=0x0000,dl_src=f0:00:00:00:00:01,dl_dst=f0:00:00:00:00:02,nw_src=192.168.1.2,nw_dst=192.168.1.3,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=4367,tp_dst=87,tcp_flags=syn ]) OVN_CLEANUP([hv]) AT_CLEANUP AT_SETUP([ovn -- DSCP marking check]) AT_KEYWORDS([ovn]) ovn_start ovn-nbctl ls-add lsw0 ovn-nbctl --wait=sb lsp-add lsw0 lp1 ovn-nbctl --wait=sb lsp-add lsw0 lp2 ovn-nbctl lsp-set-addresses lp1 f0:00:00:00:00:01 ovn-nbctl lsp-set-addresses lp2 f0:00:00:00:00:02 ovn-nbctl lsp-set-port-security lp1 f0:00:00:00:00:01 ovn-nbctl lsp-set-port-security lp2 f0:00:00:00:00:02 ovn-nbctl --wait=sb sync net_add n1 sim_add hv as hv ovs-vsctl add-br br-phys ovn_attach n1 br-phys 192.168.0.1 ovs-vsctl add-port br-int vif1 -- set Interface vif1 external-ids:iface-id=lp1 options:tx_pcap=vif1-tx.pcap options:rxq_pcap=vif1-rx.pcap ofport-request=1 ovs-vsctl add-port br-int vif2 -- set Interface vif2 external-ids:iface-id=lp2 options:tx_pcap=vif2-tx.pcap options:rxq_pcap=vif2-rx.pcap ofport-request=2 AT_CAPTURE_FILE([trace]) ovn_trace () { ovn-trace --all "$@" | tee trace | sed '1,/Minimal trace/d' } # Extracts nw_tos from the final flow from ofproto/trace output and prints # it on stdout. Prints "none" if no nw_tos was included. get_final_nw_tos() { if flow=$(grep '^Final flow:' stdout); then :; else # The output didn't have a final flow. return 99 fi tos=$(echo "$flow" | sed -n 's/.*nw_tos=\([[0-9]]\{1,\}\).*/\1/p') case $tos in '') echo none ;; *) echo $tos ;; esac } # check_tos TOS # # Checks that a packet from 1.1.1.1 to 1.1.1.2 gets its DSCP set to TOS. check_tos() { # First check with ovn-trace for logical flows. echo "checking for tos $1" (if test $1 != 0; then echo "ip.dscp = $1;"; fi; echo 'output("lp2");') > expout AT_CHECK_UNQUOTED([ovn_trace lsw0 'inport == "lp1" && eth.src == f0:00:00:00:00:01 && eth.dst == f0:00:00:00:00:02 && ip4.src == 1.1.1.1 && ip4.dst == 1.1.1.2'], [0], [expout]) # Then re-check with ofproto/trace for a physical packet. AT_CHECK([ovs-appctl ofproto/trace br-int 'in_port=1,dl_src=f0:00:00:00:00:01,dl_dst=f0:00:00:00:00:02,dl_type=0x800,nw_src=1.1.1.1,nw_dst=1.1.1.2'], [0], [stdout-nolog]) AT_CHECK_UNQUOTED([get_final_nw_tos], [0], [`expr $1 \* 4` ]) } # check at L2 AT_CHECK([ovn_trace lsw0 'inport == "lp1" && eth.src == f0:00:00:00:00:01 && eth.dst == f0:00:00:00:00:02'], [0], [output("lp2"); ]) AT_CHECK([ovs-appctl ofproto/trace br-int 'in_port=1,dl_src=f0:00:00:00:00:01,dl_dst=f0:00:00:00:00:02'], [0], [stdout-nolog]) AT_CHECK([get_final_nw_tos], [0], [none ]) # check at L3 without dscp marking check_tos 0 # Mark DSCP with a valid value qos_id=$(ovn-nbctl --wait=hv -- --id=@lp1-qos create QoS priority=100 action=dscp=48 match="inport\=\=\"lp1\"" direction="from-lport" -- set Logical_Switch lsw0 qos_rules=@lp1-qos) check_tos 48 # Update the DSCP marking ovn-nbctl --wait=hv set QoS $qos_id action=dscp=63 check_tos 63 ovn-nbctl --wait=hv set QoS $qos_id match="outport\=\=\"lp2\"" direction="to-lport" check_tos 63 # Disable DSCP marking ovn-nbctl --wait=hv clear Logical_Switch lsw0 qos_rules check_tos 0 OVN_CLEANUP([hv]) AT_CLEANUP AT_SETUP([ovn -- read-only sb db:ptcp access]) AT_SKIP_IF([test $HAVE_PYTHON = no]) : > .$1.db.~lock~ ovsdb-tool create ovn-sb.db "$abs_top_srcdir"/ovn/ovn-sb.ovsschema # Add read-only remote to sb ovsdb-server AT_CHECK( [ovsdb-tool transact ovn-sb.db \ ['["OVN_Southbound", {"op": "insert", "table": "SB_Global", "row": { "connections": ["set", [["named-uuid", "xyz"]]]}}, {"op": "insert", "table": "Connection", "uuid-name": "xyz", "row": {"target": "ptcp:0:127.0.0.1", "read_only": true}}]']], [0], [ignore], [ignore]) start_daemon ovsdb-server --remote=punix:ovn-sb.sock --remote=db:OVN_Southbound,SB_Global,connections ovn-sb.db PARSE_LISTENING_PORT([ovsdb-server.log], [TCP_PORT]) # read-only accesses should succeed AT_CHECK([ovn-sbctl --db=tcp:127.0.0.1:$TCP_PORT list SB_Global], [0], [stdout], [ignore]) AT_CHECK([ovn-sbctl --db=tcp:127.0.0.1:$TCP_PORT list Connection], [0], [stdout], [ignore]) # write access should fail AT_CHECK([ovn-sbctl --db=tcp:127.0.0.1:$TCP_PORT chassis-add ch vxlan 1.2.4.8], [1], [ignore], [ovn-sbctl: transaction error: {"details":"insert operation not allowed when database server is in read only mode","error":"not allowed"} ]) OVS_APP_EXIT_AND_WAIT([ovsdb-server]) AT_CLEANUP AT_SETUP([ovn -- read-only sb db:pssl access]) AT_SKIP_IF([test $HAVE_PYTHON = no]) AT_SKIP_IF([test "$HAVE_OPENSSL" = no]) PKIDIR="$(cd $abs_top_builddir/tests && pwd)" AT_SKIP_IF([expr "$PKIDIR" : ".*[ '\" \\]"]) : > .$1.db.~lock~ ovsdb-tool create ovn-sb.db "$abs_top_srcdir"/ovn/ovn-sb.ovsschema # Add read-only remote to sb ovsdb-server AT_CHECK( [ovsdb-tool transact ovn-sb.db \ ['["OVN_Southbound", {"op": "insert", "table": "SB_Global", "row": { "connections": ["set", [["named-uuid", "xyz"]]]}}, {"op": "insert", "table": "Connection", "uuid-name": "xyz", "row": {"target": "pssl:0:127.0.0.1", "read_only": true}}]']], [0], [ignore], [ignore]) start_daemon ovsdb-server --remote=punix:ovn-sb.sock \ --remote=db:OVN_Southbound,SB_Global,connections \ --private-key="$PKIDIR/testpki-privkey2.pem" \ --certificate="$PKIDIR/testpki-cert2.pem" \ --ca-cert="$PKIDIR/testpki-cacert.pem" \ ovn-sb.db PARSE_LISTENING_PORT([ovsdb-server.log], [TCP_PORT]) # read-only accesses should succeed AT_CHECK([ovn-sbctl --db=ssl:127.0.0.1:$TCP_PORT \ --private-key=$PKIDIR/testpki-privkey.pem \ --certificate=$PKIDIR/testpki-cert.pem \ --ca-cert=$PKIDIR/testpki-cacert.pem \ list SB_Global], [0], [stdout], [ignore]) AT_CHECK([ovn-sbctl --db=ssl:127.0.0.1:$TCP_PORT \ --private-key=$PKIDIR/testpki-privkey.pem \ --certificate=$PKIDIR/testpki-cert.pem \ --ca-cert=$PKIDIR/testpki-cacert.pem \ list Connection], [0], [stdout], [ignore]) # write access should fail AT_CHECK([ovn-sbctl --db=ssl:127.0.0.1:$TCP_PORT \ --private-key=$PKIDIR/testpki-privkey.pem \ --certificate=$PKIDIR/testpki-cert.pem \ --ca-cert=$PKIDIR/testpki-cacert.pem \ chassis-add ch vxlan 1.2.4.8], [1], [ignore], [ovn-sbctl: transaction error: {"details":"insert operation not allowed when database server is in read only mode","error":"not allowed"} ]) OVS_APP_EXIT_AND_WAIT([ovsdb-server]) AT_CLEANUP AT_SETUP([ovn -- nb connection/ssl commands]) AT_SKIP_IF([test $HAVE_PYTHON = no]) AT_SKIP_IF([test "$HAVE_OPENSSL" = no]) PKIDIR="$(cd $abs_top_builddir/tests && pwd)" AT_SKIP_IF([expr "$PKIDIR" : ".*[ '\" \\]"]) : > .$1.db.~lock~ ovsdb-tool create ovn-nb.db "$abs_top_srcdir"/ovn/ovn-nb.ovsschema # Start nb db server using db connection/ssl entries (unpopulated initially) start_daemon ovsdb-server --remote=punix:ovnnb_db.sock \ --remote=db:OVN_Northbound,NB_Global,connections \ --private-key=db:OVN_Northbound,SSL,private_key \ --certificate=db:OVN_Northbound,SSL,certificate \ --ca-cert=db:OVN_Northbound,SSL,ca_cert \ ovn-nb.db # Populate SSL configuration entries in nb db AT_CHECK( [ovn-nbctl set-ssl $PKIDIR/testpki-privkey.pem \ $PKIDIR/testpki-cert.pem \ $PKIDIR/testpki-cacert.pem], [0], [stdout], [ignore]) # Populate a passive SSL connection in nb db AT_CHECK([ovn-nbctl set-connection pssl:0:127.0.0.1], [0], [stdout], [ignore]) PARSE_LISTENING_PORT([ovsdb-server.log], [TCP_PORT]) # Verify SSL connetivity to nb db server AT_CHECK([ovn-nbctl --db=ssl:127.0.0.1:$TCP_PORT \ --private-key=$PKIDIR/testpki-privkey.pem \ --certificate=$PKIDIR/testpki-cert.pem \ --ca-cert=$PKIDIR/testpki-cacert.pem \ list NB_Global], [0], [stdout], [ignore]) AT_CHECK([ovn-nbctl --db=ssl:127.0.0.1:$TCP_PORT \ --private-key=$PKIDIR/testpki-privkey.pem \ --certificate=$PKIDIR/testpki-cert.pem \ --ca-cert=$PKIDIR/testpki-cacert.pem \ list Connection], [0], [stdout], [ignore]) AT_CHECK([ovn-nbctl --db=ssl:127.0.0.1:$TCP_PORT \ --private-key=$PKIDIR/testpki-privkey.pem \ --certificate=$PKIDIR/testpki-cert.pem \ --ca-cert=$PKIDIR/testpki-cacert.pem \ get-connection], [0], [stdout], [ignore]) OVS_APP_EXIT_AND_WAIT([ovsdb-server]) AT_CLEANUP AT_SETUP([ovn -- sb connection/ssl commands]) AT_SKIP_IF([test $HAVE_PYTHON = no]) AT_SKIP_IF([test "$HAVE_OPENSSL" = no]) PKIDIR="$(cd $abs_top_builddir/tests && pwd)" AT_SKIP_IF([expr "$PKIDIR" : ".*[ '\" \\]"]) : > .$1.db.~lock~ ovsdb-tool create ovn-sb.db "$abs_top_srcdir"/ovn/ovn-sb.ovsschema # Start sb db server using db connection/ssl entries (unpopulated initially) start_daemon ovsdb-server --remote=punix:ovnsb_db.sock \ --remote=db:OVN_Southbound,SB_Global,connections \ --private-key=db:OVN_Southbound,SSL,private_key \ --certificate=db:OVN_Southbound,SSL,certificate \ --ca-cert=db:OVN_Southbound,SSL,ca_cert \ ovn-sb.db # Populate SSL configuration entries in sb db AT_CHECK( [ovn-sbctl set-ssl $PKIDIR/testpki-privkey.pem \ $PKIDIR/testpki-cert.pem \ $PKIDIR/testpki-cacert.pem], [0], [stdout], [ignore]) # Populate a passive SSL connection in sb db AT_CHECK([ovn-sbctl set-connection pssl:0:127.0.0.1], [0], [stdout], [ignore]) PARSE_LISTENING_PORT([ovsdb-server.log], [TCP_PORT]) # Verify SSL connetivity to sb db server AT_CHECK([ovn-sbctl --db=ssl:127.0.0.1:$TCP_PORT \ --private-key=$PKIDIR/testpki-privkey.pem \ --certificate=$PKIDIR/testpki-cert.pem \ --ca-cert=$PKIDIR/testpki-cacert.pem \ list SB_Global], [0], [stdout], [ignore]) AT_CHECK([ovn-sbctl --db=ssl:127.0.0.1:$TCP_PORT \ --private-key=$PKIDIR/testpki-privkey.pem \ --certificate=$PKIDIR/testpki-cert.pem \ --ca-cert=$PKIDIR/testpki-cacert.pem \ list Connection], [0], [stdout], [ignore]) AT_CHECK([ovn-sbctl --db=ssl:127.0.0.1:$TCP_PORT \ --private-key=$PKIDIR/testpki-privkey.pem \ --certificate=$PKIDIR/testpki-cert.pem \ --ca-cert=$PKIDIR/testpki-cacert.pem \ get-connection], [0], [stdout], [ignore]) OVS_APP_EXIT_AND_WAIT([ovsdb-server]) AT_CLEANUP AT_SETUP([ovn -- nested containers]) ovn_start # Physical network: # 2 HVs. HV1 has 2 VMs - "VM1" and "bar3". HV2 has 1 VM - "VM2" # Logical network: # 3 Logical switches - "mgmt" (172.16.1.0/24), "foo" (192.168.1.0/24) # and "bar" (192.168.2.0/24). They are all connected to router R1. ovn-nbctl lr-add R1 ovn-nbctl ls-add mgmt ovn-nbctl ls-add foo ovn-nbctl ls-add bar # Connect mgmt to R1 ovn-nbctl lrp-add R1 mgmt 00:00:00:01:02:02 172.16.1.1/24 ovn-nbctl lsp-add mgmt rp-mgmt -- set Logical_Switch_Port rp-mgmt type=router \ options:router-port=mgmt addresses=\"00:00:00:01:02:02\" # Connect foo to R1 ovn-nbctl lrp-add R1 foo 00:00:00:01:02:03 192.168.1.1/24 ovn-nbctl lsp-add foo rp-foo -- set Logical_Switch_Port rp-foo type=router \ options:router-port=foo addresses=\"00:00:00:01:02:03\" # Connect bar to R1 ovn-nbctl lrp-add R1 bar 00:00:00:01:02:04 192.168.2.1/24 ovn-nbctl lsp-add bar rp-bar -- set Logical_Switch_Port rp-bar type=router \ options:router-port=bar addresses=\"00:00:00:01:02:04\" # "mgmt" has VM1 and VM2 connected ovn-nbctl lsp-add mgmt vm1 \ -- lsp-set-addresses vm1 "f0:00:00:01:02:03 172.16.1.2" ovn-nbctl lsp-add mgmt vm2 \ -- lsp-set-addresses vm2 "f0:00:00:01:02:04 172.16.1.3" # "foo1" and "foo2" are containers belonging to switch "foo" # "foo1" has "VM1" as parent_port and "foo2" has "VM2" as parent_port. ovn-nbctl lsp-add foo foo1 vm1 1 \ -- lsp-set-addresses foo1 "f0:00:00:01:02:05 192.168.1.2" ovn-nbctl lsp-add foo foo2 vm2 2 \ -- lsp-set-addresses foo2 "f0:00:00:01:02:06 192.168.1.3" # "bar1" and "bar2" are containers belonging to switch "bar" # "bar1" has "VM1" as parent_port and "bar2" has "VM2" as parent_port. ovn-nbctl lsp-add bar bar1 vm1 2 \ -- lsp-set-addresses bar1 "f0:00:00:01:02:07 192.168.2.2" ovn-nbctl lsp-add bar bar2 vm2 1 \ -- lsp-set-addresses bar2 "f0:00:00:01:02:08 192.168.2.3" # bar3 is a standalone VM belonging to switch "bar" ovn-nbctl lsp-add bar bar3 \ -- lsp-set-addresses bar3 "f0:00:00:01:02:09 192.168.2.4" # Create two hypervisor and create OVS ports corresponding to logical ports. net_add n1 sim_add hv1 as hv1 ovs-vsctl add-br br-phys ovn_attach n1 br-phys 192.168.0.1 ovs-vsctl -- add-port br-int vm1 -- \ set interface vm1 external-ids:iface-id=vm1 \ options:tx_pcap=hv1/vm1-tx.pcap \ options:rxq_pcap=hv1/vm1-rx.pcap \ ofport-request=1 ovs-vsctl -- add-port br-int bar3 -- \ set interface bar3 external-ids:iface-id=bar3 \ options:tx_pcap=hv1/bar3-tx.pcap \ options:rxq_pcap=hv1/bar3-rx.pcap \ ofport-request=2 sim_add hv2 as hv2 ovs-vsctl add-br br-phys ovn_attach n1 br-phys 192.168.0.2 ovs-vsctl -- add-port br-int vm2 -- \ set interface vm2 external-ids:iface-id=vm2 \ options:tx_pcap=hv2/vm2-tx.pcap \ options:rxq_pcap=hv2/vm2-rx.pcap \ ofport-request=1 # Pre-populate the hypervisors' ARP tables so that we don't lose any # packets for ARP resolution (native tunneling doesn't queue packets # for ARP resolution). ovn_populate_arp # Allow some time for ovn-northd and ovn-controller to catch up. # XXX This should be more systematic. sleep 1 ip_to_hex() { printf "%02x%02x%02x%02x" "$@" } # Send ip packets between foo1 and foo2 (same switch, different HVs and # different VLAN tags). src_mac="f00000010205" dst_mac="f00000010206" src_ip=`ip_to_hex 192 168 1 2` dst_ip=`ip_to_hex 192 168 1 3` packet=${dst_mac}${src_mac}8100000108004500001c0000000040110000${src_ip}${dst_ip}0035111100080000 as hv1 ovs-appctl netdev-dummy/receive vm1 $packet # expected packet at foo2 packet=${dst_mac}${src_mac}8100000208004500001c0000000040110000${src_ip}${dst_ip}0035111100080000 echo $packet > expected OVN_CHECK_PACKETS([hv2/vm2-tx.pcap], [expected]) # Send ip packets between foo1 and bar2 (different switch, different HV) src_mac="f00000010205" dst_mac="000000010203" src_ip=`ip_to_hex 192 168 1 2` dst_ip=`ip_to_hex 192 168 2 3` packet=${dst_mac}${src_mac}8100000108004500001c0000000040110000${src_ip}${dst_ip}0035111100080000 as hv1 ovs-appctl netdev-dummy/receive vm1 $packet # expected packet at bar2 src_mac="000000010204" dst_mac="f00000010208" packet=${dst_mac}${src_mac}8100000108004500001c000000003f110100${src_ip}${dst_ip}0035111100080000 echo $packet >> expected OVN_CHECK_PACKETS([hv2/vm2-tx.pcap], [expected]) # Send ip packets between foo1 and bar1 # (different switch, loopback to same vm but different tag) src_mac="f00000010205" dst_mac="000000010203" src_ip=`ip_to_hex 192 168 1 2` dst_ip=`ip_to_hex 192 168 2 2` packet=${dst_mac}${src_mac}8100000108004500001c0000000040110000${src_ip}${dst_ip}0035111100080000 as hv1 ovs-appctl netdev-dummy/receive vm1 $packet # expected packet at bar1 src_mac="000000010204" dst_mac="f00000010207" packet=${dst_mac}${src_mac}8100000208004500001c000000003f110100${src_ip}${dst_ip}0035111100080000 echo $packet > expected1 OVN_CHECK_PACKETS([hv1/vm1-tx.pcap], [expected1]) # Send ip packets between bar1 and bar3 # (same switch. But one is container and another is a standalone VM) src_mac="f00000010207" dst_mac="f00000010209" src_ip=`ip_to_hex 192 168 2 2` dst_ip=`ip_to_hex 192 168 2 3` packet=${dst_mac}${src_mac}8100000208004500001c0000000040110000${src_ip}${dst_ip}0035111100080000 as hv1 ovs-appctl netdev-dummy/receive vm1 $packet # expected packet at bar3 packet=${dst_mac}${src_mac}08004500001c0000000040110000${src_ip}${dst_ip}0035111100080000 echo $packet > expected OVN_CHECK_PACKETS([hv1/bar3-tx.pcap], [expected]) # Send ip packets between foo1 and vm1. (different switch, container to the VM hosting it.) src_mac="f00000010205" dst_mac="000000010203" src_ip=`ip_to_hex 192 168 1 2` dst_ip=`ip_to_hex 172 16 1 2` packet=${dst_mac}${src_mac}8100000108004500001c0000000040110000${src_ip}${dst_ip}0035111100080000 as hv1 ovs-appctl netdev-dummy/receive vm1 $packet # expected packet at vm1 src_mac="000000010202" dst_mac="f00000010203" packet=${dst_mac}${src_mac}08004500001c000000003f110100${src_ip}${dst_ip}0035111100080000 echo $packet >> expected1 OVN_CHECK_PACKETS([hv1/vm1-tx.pcap], [expected1]) # Send packets from vm1 to bar1. (different switch, A hosting VM to a container inside it) src_mac="f00000010203" dst_mac="000000010202" src_ip=`ip_to_hex 172 16 1 2` dst_ip=`ip_to_hex 192 168 2 2` packet=${dst_mac}${src_mac}08004500001c0000000040110000${src_ip}${dst_ip}0035111100080000 as hv1 ovs-appctl netdev-dummy/receive vm1 $packet # expected packet at vm1 src_mac="000000010204" dst_mac="f00000010207" packet=${dst_mac}${src_mac}8100000208004500001c000000003f110100${src_ip}${dst_ip}0035111100080000 echo $packet >> expected1 OVN_CHECK_PACKETS([hv1/vm1-tx.pcap], [expected1]) OVN_CLEANUP([hv1],[hv2]) AT_CLEANUP AT_SETUP([ovn -- 3 HVs, 3 LRs connected via LS, source IP based routes]) AT_SKIP_IF([test $HAVE_PYTHON = no]) ovn_start # Logical network: # Three LRs - R1, R2 and R3 that are connected to each other via LS "join" # in 20.0.0.0/24 network. R1 has switchess foo (192.168.1.0/24) and bar # (192.168.2.0/24) connected to it. # # R2 and R3 are gateway routers. # R2 has alice (172.16.1.0/24) and R3 has bob (172.16.1.0/24) # connected to it. Note how both alice and bob have the same subnet behind it. # We are trying to simulate external network via those 2 switches. In real # world the switch ports of these switches will have addresses set as "unknown" # to make them learning switches. Or those switches will be "localnet" ones. # Create three hypervisors and create OVS ports corresponding to logical ports. net_add n1 sim_add hv1 as hv1 ovs-vsctl add-br br-phys ovn_attach n1 br-phys 192.168.0.1 ovs-vsctl -- add-port br-int hv1-vif1 -- \ set interface hv1-vif1 external-ids:iface-id=foo1 \ options:tx_pcap=hv1/vif1-tx.pcap \ options:rxq_pcap=hv1/vif1-rx.pcap \ ofport-request=1 ovs-vsctl -- add-port br-int hv1-vif2 -- \ set interface hv1-vif2 external-ids:iface-id=bar1 \ options:tx_pcap=hv1/vif2-tx.pcap \ options:rxq_pcap=hv1/vif2-rx.pcap \ ofport-request=2 sim_add hv2 as hv2 ovs-vsctl add-br br-phys ovn_attach n1 br-phys 192.168.0.2 ovs-vsctl -- add-port br-int hv2-vif1 -- \ set interface hv2-vif1 external-ids:iface-id=alice1 \ options:tx_pcap=hv2/vif1-tx.pcap \ options:rxq_pcap=hv2/vif1-rx.pcap \ ofport-request=1 sim_add hv3 as hv3 ovs-vsctl add-br br-phys ovn_attach n1 br-phys 192.168.0.3 ovs-vsctl -- add-port br-int hv3-vif1 -- \ set interface hv3-vif1 external-ids:iface-id=bob1 \ options:tx_pcap=hv3/vif1-tx.pcap \ options:rxq_pcap=hv3/vif1-rx.pcap \ ofport-request=1 ovn-nbctl create Logical_Router name=R1 ovn-nbctl create Logical_Router name=R2 options:chassis="hv2" ovn-nbctl create Logical_Router name=R3 options:chassis="hv3" ovn-nbctl ls-add foo ovn-nbctl ls-add bar ovn-nbctl ls-add alice ovn-nbctl ls-add bob ovn-nbctl ls-add join # Connect foo to R1 ovn-nbctl lrp-add R1 foo 00:00:01:01:02:03 192.168.1.1/24 ovn-nbctl lsp-add foo rp-foo -- set Logical_Switch_Port rp-foo type=router \ options:router-port=foo addresses=\"00:00:01:01:02:03\" # Connect bar to R1 ovn-nbctl lrp-add R1 bar 00:00:01:01:02:04 192.168.2.1/24 ovn-nbctl lsp-add bar rp-bar -- set Logical_Switch_Port rp-bar type=router \ options:router-port=bar addresses=\"00:00:01:01:02:04\" # Connect alice to R2 ovn-nbctl lrp-add R2 alice 00:00:02:01:02:03 172.16.1.1/24 ovn-nbctl lsp-add alice rp-alice -- set Logical_Switch_Port rp-alice \ type=router options:router-port=alice addresses=\"00:00:02:01:02:03\" # Connect bob to R3 ovn-nbctl lrp-add R3 bob 00:00:03:01:02:03 172.16.1.2/24 ovn-nbctl lsp-add bob rp-bob -- set Logical_Switch_Port rp-bob \ type=router options:router-port=bob addresses=\"00:00:03:01:02:03\" # Connect R1 to join ovn-nbctl lrp-add R1 R1_join 00:00:04:01:02:03 20.0.0.1/24 ovn-nbctl lsp-add join r1-join -- set Logical_Switch_Port r1-join \ type=router options:router-port=R1_join addresses='"00:00:04:01:02:03"' # Connect R2 to join ovn-nbctl lrp-add R2 R2_join 00:00:04:01:02:04 20.0.0.2/24 ovn-nbctl lsp-add join r2-join -- set Logical_Switch_Port r2-join \ type=router options:router-port=R2_join addresses='"00:00:04:01:02:04"' # Connect R3 to join ovn-nbctl lrp-add R3 R3_join 00:00:04:01:02:05 20.0.0.3/24 ovn-nbctl lsp-add join r3-join -- set Logical_Switch_Port r3-join \ type=router options:router-port=R3_join addresses='"00:00:04:01:02:05"' # Install static routes with source ip address as the policy for routing. # We want traffic from 'foo' to go via R2 and traffic of 'bar' to go via R3. ovn-nbctl --policy="src-ip" lr-route-add R1 192.168.1.0/24 20.0.0.2 ovn-nbctl --policy="src-ip" lr-route-add R1 192.168.2.0/24 20.0.0.3 # Install static routes with destination ip address as the policy for routing. ovn-nbctl lr-route-add R2 192.168.0.0/16 20.0.0.1 ovn-nbctl lr-route-add R3 192.168.0.0/16 20.0.0.1 # Create logical port foo1 in foo ovn-nbctl lsp-add foo foo1 \ -- lsp-set-addresses foo1 "f0:00:00:01:02:03 192.168.1.2" # Create logical port bar1 in bar ovn-nbctl lsp-add bar bar1 \ -- lsp-set-addresses bar1 "f0:00:00:01:02:04 192.168.2.2" # Create logical port alice1 in alice ovn-nbctl lsp-add alice alice1 \ -- lsp-set-addresses alice1 "f0:00:00:01:02:05 172.16.1.3" # Create logical port bob1 in bob ovn-nbctl lsp-add bob bob1 \ -- lsp-set-addresses bob1 "f0:00:00:01:02:06 172.16.1.4" # Pre-populate the hypervisors' ARP tables so that we don't lose any # packets for ARP resolution (native tunneling doesn't queue packets # for ARP resolution). ovn_populate_arp # Allow some time for ovn-northd and ovn-controller to catch up. # XXX This should be more systematic. sleep 1 ip_to_hex() { printf "%02x%02x%02x%02x" "$@" } trim_zeros() { sed 's/\(00\)\{1,\}$//' } # Send ip packets between foo1 and bar1 # (East-west traffic should flow normally) src_mac="f00000010203" dst_mac="000001010203" src_ip=`ip_to_hex 192 168 1 2` dst_ip=`ip_to_hex 192 168 2 2` packet=${dst_mac}${src_mac}08004500001c0000000040110000${src_ip}${dst_ip}0035111100080000 as hv1 ovs-appctl netdev-dummy/receive hv1-vif1 $packet # Send ip packets between foo1 and alice1 src_mac="f00000010203" dst_mac="000001010203" src_ip=`ip_to_hex 192 168 1 2` dst_ip=`ip_to_hex 172 16 1 3` packet=${dst_mac}${src_mac}08004500001c0000000040110000${src_ip}${dst_ip}0035111100080000 as hv1 ovs-appctl netdev-dummy/receive hv1-vif1 $packet as hv1 ovs-appctl ofproto/trace br-int in_port=1 $packet # Send ip packets between bar1 and bob1 src_mac="f00000010204" dst_mac="000001010204" src_ip=`ip_to_hex 192 168 2 2` dst_ip=`ip_to_hex 172 16 1 4` packet=${dst_mac}${src_mac}08004500001c0000000040110000${src_ip}${dst_ip}0035111100080000 as hv1 ovs-appctl netdev-dummy/receive hv1-vif2 $packet #as hv1 ovs-appctl ofproto/trace br-int in_port=2 $packet # Packet to expect at bar1 src_mac="000001010204" dst_mac="f00000010204" src_ip=`ip_to_hex 192 168 1 2` dst_ip=`ip_to_hex 192 168 2 2` expected=${dst_mac}${src_mac}08004500001c000000003f110100${src_ip}${dst_ip}0035111100080000 echo $expected > expected OVN_CHECK_PACKETS([hv1/vif2-tx.pcap], [expected]) # Packet to Expect at alice1 src_mac="000002010203" dst_mac="f00000010205" src_ip=`ip_to_hex 192 168 1 2` dst_ip=`ip_to_hex 172 16 1 3` expected=${dst_mac}${src_mac}08004500001c000000003e110200${src_ip}${dst_ip}0035111100080000 echo $expected > expected OVN_CHECK_PACKETS([hv2/vif1-tx.pcap], [expected]) # Packet to Expect at bob1 src_mac="000003010203" dst_mac="f00000010206" src_ip=`ip_to_hex 192 168 2 2` dst_ip=`ip_to_hex 172 16 1 4` expected=${dst_mac}${src_mac}08004500001c000000003e110200${src_ip}${dst_ip}0035111100080000 echo $expected > expected OVN_CHECK_PACKETS([hv3/vif1-tx.pcap], [expected]) for sim in hv1 hv2 hv3; do as $sim OVS_APP_EXIT_AND_WAIT([ovn-controller]) OVS_APP_EXIT_AND_WAIT([ovs-vswitchd]) OVS_APP_EXIT_AND_WAIT([ovsdb-server]) done as ovn-sb OVS_APP_EXIT_AND_WAIT([ovsdb-server]) as ovn-nb OVS_APP_EXIT_AND_WAIT([ovsdb-server]) as northd OVS_APP_EXIT_AND_WAIT([ovn-northd]) as main OVS_APP_EXIT_AND_WAIT([ovs-vswitchd]) OVS_APP_EXIT_AND_WAIT([ovsdb-server]) AT_CLEANUP AT_SETUP([ovn -- dns lookup : 1 HV, 2 LS, 2 LSPs/LS]) AT_SKIP_IF([test $HAVE_PYTHON = no]) ovn_start ovn-nbctl ls-add ls1 ovn-nbctl lsp-add ls1 ls1-lp1 \ -- lsp-set-addresses ls1-lp1 "f0:00:00:00:00:01 10.0.0.4 aef0::4" ovn-nbctl lsp-set-port-security ls1-lp1 "f0:00:00:00:00:01 10.0.0.4 aef0::4" ovn-nbctl lsp-add ls1 ls1-lp2 \ -- lsp-set-addresses ls1-lp2 "f0:00:00:00:00:02 10.0.0.6 20.0.0.4" ovn-nbctl lsp-set-port-security ls1-lp2 "f0:00:00:00:00:02 10.0.0.6 20.0.0.4" DNS1=`ovn-nbctl create DNS records={}` DNS2=`ovn-nbctl create DNS records={}` ovn-nbctl set DNS $DNS1 records:vm1.ovn.org="10.0.0.4 aef0::4" ovn-nbctl set DNS $DNS1 records:vm2.ovn.org="10.0.0.6 20.0.0.4" ovn-nbctl set DNS $DNS2 records:vm3.ovn.org="40.0.0.4" ovn-nbctl set Logical_switch ls1 dns_records="$DNS1" net_add n1 sim_add hv1 as hv1 ovs-vsctl add-br br-phys ovn_attach n1 br-phys 192.168.0.1 ovs-vsctl -- add-port br-int hv1-vif1 -- \ set interface hv1-vif1 external-ids:iface-id=ls1-lp1 \ options:tx_pcap=hv1/vif1-tx.pcap \ options:rxq_pcap=hv1/vif1-rx.pcap \ ofport-request=1 ovs-vsctl -- add-port br-int hv1-vif2 -- \ set interface hv1-vif2 external-ids:iface-id=ls1-lp2 \ options:tx_pcap=hv1/vif2-tx.pcap \ options:rxq_pcap=hv1/vif2-rx.pcap \ ofport-request=2 ovn_populate_arp sleep 2 as hv1 ovs-vsctl show echo "*************************" ovn-sbctl list DNS echo "*************************" ip_to_hex() { printf "%02x%02x%02x%02x" "$@" } reset_pcap_file() { local iface=$1 local pcap_file=$2 ovs-vsctl -- set Interface $iface options:tx_pcap=dummy-tx.pcap \ options:rxq_pcap=dummy-rx.pcap rm -f ${pcap_file}*.pcap ovs-vsctl -- set Interface $iface options:tx_pcap=${pcap_file}-tx.pcap \ options:rxq_pcap=${pcap_file}-rx.pcap } # set_dns_params host_name # Sets the dns_req_data and dns_resp_data set_dns_params() { local hname=$1 local ttl=00000e10 an_count=0001 type=0001 case $hname in vm1) # vm1.ovn.org query_name=03766d31036f766e036f726700 # IPv4 address - 10.0.0.4 expected_dns_answer=${query_name}00010001${ttl}00040a000004 ;; vm2) # vm2.ovn.org query_name=03766d32036f766e036f726700 # IPv4 address - 10.0.0.6 expected_dns_answer=${query_name}00010001${ttl}00040a000006 # IPv4 address - 20.0.0.4 expected_dns_answer=${expected_dns_answer}${query_name}00010001${ttl}000414000004 an_count=0002 ;; vm3) # vm3.ovn.org query_name=03766d33036f766e036f726700 # IPv4 address - 40.0.0.4 expected_dns_answer=${query_name}00010001${ttl}000428000004 ;; vm1_ipv6_only) # vm1.ovn.org query_name=03766d31036f766e036f726700 # IPv6 address - aef0::4 type=001c expected_dns_answer=${query_name}${type}0001${ttl}0010aef00000000000000000000000000004 ;; vm1_ipv4_v6) # vm1.ovn.org query_name=03766d31036f766e036f726700 type=00ff an_count=0002 # IPv4 address - 10.0.0.4 # IPv6 address - aef0::4 expected_dns_answer=${query_name}00010001${ttl}00040a000004 expected_dns_answer=${expected_dns_answer}${query_name}001c0001${ttl}0010 expected_dns_answer=${expected_dns_answer}aef00000000000000000000000000004 ;; vm1_invalid_type) # vm1.ovn.org query_name=03766d31036f766e036f726700 # IPv6 address - aef0::4 type=0002 ;; vm1_incomplete) # set type to none type='' esac # TTL - 3600 local dns_req_header=010201200001000000000000 local dns_resp_header=010281200001${an_count}00000000 dns_req_data=${dns_req_header}${query_name}${type}0001 dns_resp_data=${dns_resp_header}${query_name}${type}0001${expected_dns_answer} } # This shell function sends a DNS request packet # test_dns INPORT SRC_MAC DST_MAC SRC_IP DST_IP DNS_QUERY EXPEC test_dns() { local inport=$1 src_mac=$2 dst_mac=$3 src_ip=$4 dst_ip=$5 dns_reply=$6 local dns_query_data=$7 shift; shift; shift; shift; shift; shift; shift; # Packet size => IPv4 header (20) + UDP header (8) + # DNS data (header + query) ip_len=`expr 28 + ${#dns_query_data} / 2` udp_len=`expr $ip_len - 20` ip_len=$(printf "%x" $ip_len) udp_len=$(printf "%x" $udp_len) local request=${dst_mac}${src_mac}0800450000${ip_len}0000000080110000 request=${request}${src_ip}${dst_ip}9234003500${udp_len}0000 # dns data request=${request}${dns_query_data} if test $dns_reply != 0; then local dns_reply=$1 ip_len=`expr 28 + ${#dns_reply} / 2` udp_len=`expr $ip_len - 20` ip_len=$(printf "%x" $ip_len) udp_len=$(printf "%x" $udp_len) local reply=${src_mac}${dst_mac}0800450000${ip_len}0000000080110000 reply=${reply}${dst_ip}${src_ip}0035923400${udp_len}0000${dns_reply} echo $reply >> $inport.expected else for outport; do echo $request >> $outport.expected done fi as hv1 ovs-appctl netdev-dummy/receive hv1-vif$inport $request } AT_CAPTURE_FILE([ofctl_monitor0.log]) as hv1 ovs-ofctl monitor br-int resume --detach --no-chdir \ --pidfile=ovs-ofctl0.pid 2> ofctl_monitor0.log set_dns_params vm2 src_ip=`ip_to_hex 10 0 0 4` dst_ip=`ip_to_hex 10 0 0 1` dns_reply=1 test_dns 1 f00000000001 f000000000f0 $src_ip $dst_ip $dns_reply $dns_req_data $dns_resp_data # NXT_RESUMEs should be 1. OVS_WAIT_UNTIL([test 1 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`]) $PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif1-tx.pcap > 1.packets cat 1.expected | cut -c -48 > expout AT_CHECK([cat 1.packets | cut -c -48], [0], [expout]) # Skipping the IPv4 checksum. cat 1.expected | cut -c 53- > expout AT_CHECK([cat 1.packets | cut -c 53-], [0], [expout]) reset_pcap_file hv1-vif1 hv1/vif1 reset_pcap_file hv1-vif2 hv1/vif2 rm -f 1.expected rm -f 2.expected set_dns_params vm1 src_ip=`ip_to_hex 10 0 0 6` dst_ip=`ip_to_hex 10 0 0 1` dns_reply=1 test_dns 2 f00000000002 f000000000f0 $src_ip $dst_ip $dns_reply $dns_req_data $dns_resp_data # NXT_RESUMEs should be 2. OVS_WAIT_UNTIL([test 2 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`]) $PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > 2.packets cat 2.expected | cut -c -48 > expout AT_CHECK([cat 2.packets | cut -c -48], [0], [expout]) # Skipping the IPv4 checksum. cat 2.expected | cut -c 53- > expout AT_CHECK([cat 2.packets | cut -c 53-], [0], [expout]) reset_pcap_file hv1-vif1 hv1/vif1 reset_pcap_file hv1-vif2 hv1/vif2 rm -f 1.expected rm -f 2.expected # Clear the query name options for ls1-lp2 ovn-nbctl --wait=hv remove DNS $DNS1 records vm2.ovn.org set_dns_params vm2 src_ip=`ip_to_hex 10 0 0 4` dst_ip=`ip_to_hex 10 0 0 1` dns_reply=0 test_dns 1 f00000000001 f00000000002 $src_ip $dst_ip $dns_reply $dns_req_data # NXT_RESUMEs should be 3. OVS_WAIT_UNTIL([test 3 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`]) $PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif1-tx.pcap > 1.packets AT_CHECK([cat 1.packets], [0], []) reset_pcap_file hv1-vif1 hv1/vif1 reset_pcap_file hv1-vif2 hv1/vif2 rm -f 1.expected rm -f 2.expected # Clear the query name for ls1-lp1 # Since ls1 has no query names configued, # ovn-northd should not add the DNS flows. ovn-nbctl --wait=hv remove DNS $DNS1 records vm1.ovn.org set_dns_params vm1 src_ip=`ip_to_hex 10 0 0 6` dst_ip=`ip_to_hex 10 0 0 1` dns_reply=0 test_dns 2 f00000000002 f000000000f0 $src_ip $dst_ip $dns_reply $dns_req_data # NXT_RESUMEs should be 3 only. OVS_WAIT_UNTIL([test 3 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`]) $PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > 2.packets AT_CHECK([cat 2.packets], [0], []) reset_pcap_file hv1-vif1 hv1/vif1 reset_pcap_file hv1-vif2 hv1/vif2 rm -f 1.expected rm -f 2.expected # Test IPv6 (AAAA records) using IPv4 packet. # Add back the DNS options for ls1-lp1. ovn-nbctl set DNS $DNS1 records:vm1.ovn.org="10.0.0.4 aef0::4" set_dns_params vm1_ipv6_only src_ip=`ip_to_hex 10 0 0 6` dst_ip=`ip_to_hex 10 0 0 1` dns_reply=1 test_dns 2 f00000000002 f000000000f0 $src_ip $dst_ip $dns_reply $dns_req_data $dns_resp_data # NXT_RESUMEs should be 4. OVS_WAIT_UNTIL([test 4 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`]) $PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > 2.packets cat 2.expected | cut -c -48 > expout AT_CHECK([cat 2.packets | cut -c -48], [0], [expout]) # Skipping the IPv4 checksum. cat 2.expected | cut -c 53- > expout AT_CHECK([cat 2.packets | cut -c 53-], [0], [expout]) reset_pcap_file hv1-vif1 hv1/vif1 reset_pcap_file hv1-vif2 hv1/vif2 rm -f 1.expected rm -f 2.expected # Test both IPv4 (A) and IPv6 (AAAA records) using IPv4 packet. set_dns_params vm1_ipv4_v6 src_ip=`ip_to_hex 10 0 0 6` dst_ip=`ip_to_hex 10 0 0 1` dns_reply=1 test_dns 2 f00000000002 f000000000f0 $src_ip $dst_ip $dns_reply $dns_req_data $dns_resp_data # NXT_RESUMEs should be 5. OVS_WAIT_UNTIL([test 5 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`]) $PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > 2.packets cat 2.expected | cut -c -48 > expout AT_CHECK([cat 2.packets | cut -c -48], [0], [expout]) # Skipping the IPv4 checksum. cat 2.expected | cut -c 53- > expout AT_CHECK([cat 2.packets | cut -c 53-], [0], [expout]) reset_pcap_file hv1-vif1 hv1/vif1 reset_pcap_file hv1-vif2 hv1/vif2 rm -f 1.expected rm -f 2.expected # Invalid type. set_dns_params vm1_invalid_type src_ip=`ip_to_hex 10 0 0 6` dst_ip=`ip_to_hex 10 0 0 1` dns_reply=0 test_dns 2 f00000000002 f000000000f0 $src_ip $dst_ip $dns_reply $dns_req_data # NXT_RESUMEs should be 6. OVS_WAIT_UNTIL([test 6 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`]) $PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > 2.packets AT_CHECK([cat 2.packets], [0], []) reset_pcap_file hv1-vif1 hv1/vif1 reset_pcap_file hv1-vif2 hv1/vif2 rm -f 1.expected rm -f 2.expected # Incomplete DNS packet. set_dns_params vm1_incomplete src_ip=`ip_to_hex 10 0 0 6` dst_ip=`ip_to_hex 10 0 0 1` dns_reply=0 test_dns 2 f00000000002 f000000000f0 $src_ip $dst_ip $dns_reply $dns_req_data # NXT_RESUMEs should be 7. OVS_WAIT_UNTIL([test 7 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`]) $PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > 2.packets AT_CHECK([cat 2.packets], [0], []) reset_pcap_file hv1-vif1 hv1/vif1 reset_pcap_file hv1-vif2 hv1/vif2 rm -f 1.expected rm -f 2.expected # Add one more DNS record to the ls1. ovn-nbctl --wait=hv set Logical_switch ls1 dns_records="$DNS1 $DNS2" set_dns_params vm3 src_ip=`ip_to_hex 10 0 0 4` dst_ip=`ip_to_hex 10 0 0 1` dns_reply=1 test_dns 1 f00000000001 f000000000f0 $src_ip $dst_ip $dns_reply $dns_req_data $dns_resp_data # NXT_RESUMEs should be 8. OVS_WAIT_UNTIL([test 8 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`]) $PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif1-tx.pcap > 1.packets cat 1.expected | cut -c -48 > expout AT_CHECK([cat 1.packets | cut -c -48], [0], [expout]) # Skipping the IPv4 checksum. cat 1.expected | cut -c 53- > expout AT_CHECK([cat 1.packets | cut -c 53-], [0], [expout]) reset_pcap_file hv1-vif1 hv1/vif1 reset_pcap_file hv1-vif2 hv1/vif2 rm -f 1.expected rm -f 2.expected as hv1 OVS_APP_EXIT_AND_WAIT([ovn-controller]) OVS_APP_EXIT_AND_WAIT([ovs-vswitchd]) OVS_APP_EXIT_AND_WAIT([ovsdb-server]) as ovn-sb OVS_APP_EXIT_AND_WAIT([ovsdb-server]) as ovn-nb OVS_APP_EXIT_AND_WAIT([ovsdb-server]) as northd OVS_APP_EXIT_AND_WAIT([ovn-northd]) as main OVS_APP_EXIT_AND_WAIT([ovs-vswitchd]) OVS_APP_EXIT_AND_WAIT([ovsdb-server]) AT_CLEANUP AT_SETUP([ovn -- packet test with HA distributed router gateway port]) AT_SKIP_IF([test $HAVE_PYTHON = no]) ovn_start net_add n1 sim_add hv1 as hv1 ovs-vsctl add-br br-phys ovn_attach n1 br-phys 192.168.0.1 ovs-vsctl -- add-port br-int hv1-vif1 -- \ set interface hv1-vif1 external-ids:iface-id=foo1 \ options:tx_pcap=hv1/vif1-tx.pcap \ options:rxq_pcap=hv1/vif1-rx.pcap \ ofport-request=1 sim_add gw1 as gw1 ovs-vsctl add-br br-phys ovn_attach n1 br-phys 192.168.0.2 sim_add gw2 as gw2 ovs-vsctl add-br br-phys ovn_attach n1 br-phys 192.168.0.4 sim_add ext1 as ext1 ovs-vsctl add-br br-phys ovn_attach n1 br-phys 192.168.0.3 ovs-vsctl -- add-port br-int ext1-vif1 -- \ set interface ext1-vif1 external-ids:iface-id=outside1 \ options:tx_pcap=ext1/vif1-tx.pcap \ options:rxq_pcap=ext1/vif1-rx.pcap \ ofport-request=1 # Pre-populate the hypervisors' ARP tables so that we don't lose any # packets for ARP resolution (native tunneling doesn't queue packets # for ARP resolution). ovn_populate_arp ovn-nbctl create Logical_Router name=R1 ovn-nbctl ls-add foo ovn-nbctl ls-add alice ovn-nbctl ls-add outside # Connect foo to R1 ovn-nbctl lrp-add R1 foo 00:00:01:01:02:03 192.168.1.1/24 ovn-nbctl lsp-add foo rp-foo -- set Logical_Switch_Port rp-foo \ type=router options:router-port=foo \ -- lsp-set-addresses rp-foo router # Connect alice to R1 as distributed router gateway port on gw1 ovn-nbctl lrp-add R1 alice 00:00:02:01:02:03 172.16.1.1/24 ovn-nbctl \ --id=@gc0 create Gateway_Chassis name=alice_gw1 \ chassis_name=gw1 \ priority=20 -- \ --id=@gc1 create Gateway_Chassis name=alice_gw2 \ chassis_name=gw2 \ priority=10 -- \ set Logical_Router_Port alice 'gateway_chassis=[@gc0,@gc1]' ovn-nbctl lsp-add alice rp-alice -- set Logical_Switch_Port rp-alice \ type=router options:router-port=alice \ -- lsp-set-addresses rp-alice router # Create logical port foo1 in foo ovn-nbctl lsp-add foo foo1 \ -- lsp-set-addresses foo1 "f0:00:00:01:02:03 192.168.1.2" # Create logical port outside1 in outside ovn-nbctl lsp-add outside outside1 \ -- lsp-set-addresses outside1 "f0:00:00:01:02:04 172.16.1.3" # Create localnet port in alice ovn-nbctl lsp-add alice ln-alice ovn-nbctl lsp-set-addresses ln-alice unknown ovn-nbctl lsp-set-type ln-alice localnet ovn-nbctl lsp-set-options ln-alice network_name=phys # Create localnet port in outside ovn-nbctl lsp-add outside ln-outside ovn-nbctl lsp-set-addresses ln-outside unknown ovn-nbctl lsp-set-type ln-outside localnet ovn-nbctl lsp-set-options ln-outside network_name=phys # Create bridge-mappings on gw1, gw2 and ext1, hv1 doesn't need # mapping to the external network, is the one generating packets as gw1 ovs-vsctl set open . external-ids:ovn-bridge-mappings=phys:br-phys as gw2 ovs-vsctl set open . external-ids:ovn-bridge-mappings=phys:br-phys as ext1 ovs-vsctl set open . external-ids:ovn-bridge-mappings=phys:br-phys AT_CHECK([ovn-nbctl --timeout=3 --wait=sb sync], [0], [ignore]) # Allow some time for ovn-northd and ovn-controller to catch up. # XXX This should be more systematic. sleep 2 ip_to_hex() { printf "%02x%02x%02x%02x" "$@" } reset_pcap_file() { local iface=$1 local pcap_file=$2 ovs-vsctl -- set Interface $iface options:tx_pcap=dummy-tx.pcap \ options:rxq_pcap=dummy-rx.pcap rm -f ${pcap_file}*.pcap ovs-vsctl -- set Interface $iface options:tx_pcap=${pcap_file}-tx.pcap \ options:rxq_pcap=${pcap_file}-rx.pcap } test_ip_packet() { local active_gw=$1 local backup_gw=$2 # Send ip packet between foo1 and outside1 src_mac="f00000010203" # foo1 mac dst_mac="000001010203" # rp-foo mac (internal router leg) src_ip=`ip_to_hex 192 168 1 2` dst_ip=`ip_to_hex 172 16 1 3` packet=${dst_mac}${src_mac}08004500001c0000000040110000${src_ip}${dst_ip}0035111100080000 # ARP request packet to expect at outside1 #arp_request=ffffffffffff${src_mac}08060001080006040001${src_mac}${src_ip}000000000000${dst_ip} as hv1 ovs-appctl netdev-dummy/receive hv1-vif1 $packet # Send ARP reply from outside1 back to the router # XXX: note, we could avoid this if we plug this port into a netns # and setup the IP address into the port, so the kernel would simply reply src_mac="000002010203" reply_mac="f00000010204" dst_ip=`ip_to_hex 172 16 1 3` src_ip=`ip_to_hex 172 16 1 1` arp_reply=${src_mac}${reply_mac}08060001080006040002${reply_mac}${dst_ip}${src_mac}${src_ip} as ext1 ovs-appctl netdev-dummy/receive ext1-vif1 $arp_reply # Packet to Expect at ext1 chassis, outside1 port src_mac="000002010203" dst_mac="f00000010204" src_ip=`ip_to_hex 192 168 1 2` dst_ip=`ip_to_hex 172 16 1 3` expected=${dst_mac}${src_mac}08004500001c000000003f110100${src_ip}${dst_ip}0035111100080000 echo $expected > ext1-vif1.expected as $active_gw reset_pcap_file br-phys_n1 $active_gw/br-phys_n1 as $backup_gw reset_pcap_file br-phys_n1 $backup_gw/br-phys_n1 as ext1 reset_pcap_file ext1-vif1 ext1/vif1 # Resend packet from foo1 to outside1 as hv1 ovs-appctl netdev-dummy/receive hv1-vif1 $packet sleep 1 OVN_CHECK_PACKETS([ext1/vif1-tx.pcap], [ext1-vif1.expected]) $PYTHON "$top_srcdir/utilities/ovs-pcap.in" $active_gw/br-phys_n1-tx.pcap > packets AT_CHECK([grep $expected packets | sort], [0], [expout]) $PYTHON "$top_srcdir/utilities/ovs-pcap.in" $backup_gw/br-phys_n1-tx.pcap > packets AT_CHECK([grep $expected packets | sort], [0], []) } test_ip_packet gw1 gw2 ovn-nbctl --timeout=3 --wait=hv \ --id=@gc0 create Gateway_Chassis name=alice_gw1 \ chassis_name=gw1 \ priority=10 -- \ --id=@gc1 create Gateway_Chassis name=alice_gw2 \ chassis_name=gw2 \ priority=20 -- \ set Logical_Router_Port alice 'gateway_chassis=[@gc0,@gc1]' test_ip_packet gw2 gw1 OVN_CLEANUP([hv1],[gw1],[gw2],[ext1]) AT_CLEANUP AT_SETUP([ovn -- 1 LR with distributed router gateway port]) AT_SKIP_IF([test $HAVE_PYTHON = no]) ovn_start # Logical network: # One LR R1 that has switches foo (192.168.1.0/24) and # alice (172.16.1.0/24) connected to it. The logical port # between R1 and alice has a "redirect-chassis" specified, # i.e. it is the distributed router gateway port. # Switch alice also has a localnet port defined. # An additional switch outside has a localnet port and the # same subnet as alice (172.16.1.0/24). # Physical network: # Three hypervisors hv[123]. # hv1 hosts vif foo1. # hv2 is the "redirect-chassis" that hosts the distributed # router gateway port. # hv3 hosts vif outside1. # In order to show that connectivity works only through hv2, # an initial round of tests is run without any bridge-mapping # defined for the localnet on hv2. These tests are expected # to fail. # Subsequent tests are run after defining the bridge-mapping # for the localnet on hv2. These tests are expected to succeed. # Create three hypervisors and create OVS ports corresponding to logical ports. net_add n1 sim_add hv1 as hv1 ovs-vsctl add-br br-phys ovn_attach n1 br-phys 192.168.0.1 ovs-vsctl -- add-port br-int hv1-vif1 -- \ set interface hv1-vif1 external-ids:iface-id=foo1 \ options:tx_pcap=hv1/vif1-tx.pcap \ options:rxq_pcap=hv1/vif1-rx.pcap \ ofport-request=1 sim_add hv2 as hv2 ovs-vsctl add-br br-phys ovn_attach n1 br-phys 192.168.0.2 sim_add hv3 as hv3 ovs-vsctl add-br br-phys ovn_attach n1 br-phys 192.168.0.3 ovs-vsctl -- add-port br-int hv3-vif1 -- \ set interface hv3-vif1 external-ids:iface-id=outside1 \ options:tx_pcap=hv3/vif1-tx.pcap \ options:rxq_pcap=hv3/vif1-rx.pcap \ ofport-request=1 # Pre-populate the hypervisors' ARP tables so that we don't lose any # packets for ARP resolution (native tunneling doesn't queue packets # for ARP resolution). ovn_populate_arp ovn-nbctl create Logical_Router name=R1 ovn-nbctl ls-add foo ovn-nbctl ls-add alice ovn-nbctl ls-add outside # Connect foo to R1 ovn-nbctl lrp-add R1 foo 00:00:01:01:02:03 192.168.1.1/24 ovn-nbctl lsp-add foo rp-foo -- set Logical_Switch_Port rp-foo \ type=router options:router-port=foo \ -- lsp-set-addresses rp-foo router # Connect alice to R1 as distributed router gateway port on hv2 ovn-nbctl lrp-add R1 alice 00:00:02:01:02:03 172.16.1.1/24 \ -- set Logical_Router_Port alice options:redirect-chassis="hv2" ovn-nbctl lsp-add alice rp-alice -- set Logical_Switch_Port rp-alice \ type=router options:router-port=alice \ -- lsp-set-addresses rp-alice router # Create logical port foo1 in foo ovn-nbctl lsp-add foo foo1 \ -- lsp-set-addresses foo1 "f0:00:00:01:02:03 192.168.1.2" # Create logical port outside1 in outside ovn-nbctl lsp-add outside outside1 \ -- lsp-set-addresses outside1 "f0:00:00:01:02:04 172.16.1.3" # Create localnet port in alice ovn-nbctl lsp-add alice ln-alice ovn-nbctl lsp-set-addresses ln-alice unknown ovn-nbctl lsp-set-type ln-alice localnet ovn-nbctl lsp-set-options ln-alice network_name=phys # Create localnet port in outside ovn-nbctl lsp-add outside ln-outside ovn-nbctl lsp-set-addresses ln-outside unknown ovn-nbctl lsp-set-type ln-outside localnet ovn-nbctl lsp-set-options ln-outside network_name=phys # Create bridge-mappings on hv1 and hv3, leaving hv2 for later as hv1 ovs-vsctl set open . external-ids:ovn-bridge-mappings=phys:br-phys as hv3 ovs-vsctl set open . external-ids:ovn-bridge-mappings=phys:br-phys # Allow some time for ovn-northd and ovn-controller to catch up. # XXX This should be more systematic. sleep 2 echo "---------NB dump-----" ovn-nbctl show echo "---------------------" ovn-nbctl list logical_router echo "---------------------" ovn-nbctl list logical_router_port echo "---------------------" echo "---------SB dump-----" ovn-sbctl list datapath_binding echo "---------------------" ovn-sbctl list port_binding echo "---------------------" ovn-sbctl dump-flows echo "---------------------" ovn-sbctl list chassis ovn-sbctl list encap echo "------ Gateway_Chassis dump (SBDB) -------" ovn-sbctl list Gateway_Chassis echo "------ Port_Binding chassisredirect -------" ovn-sbctl find Port_Binding type=chassisredirect echo "-------------------------------------------" echo "------ hv1 dump ----------" as hv1 ovs-ofctl show br-int as hv1 ovs-ofctl dump-flows br-int echo "------ hv2 dump ----------" as hv2 ovs-ofctl show br-int as hv2 ovs-ofctl dump-flows br-int echo "------ hv3 dump ----------" as hv3 ovs-ofctl show br-int as hv3 ovs-ofctl dump-flows br-int echo "--------------------------" # Check that redirect mapping is programmed only on hv2 AT_CHECK([as hv1 ovs-ofctl dump-flows br-int table=33 | grep =0x3,metadata=0x1 | wc -l], [0], [0 ]) AT_CHECK([as hv2 ovs-ofctl dump-flows br-int table=33 | grep =0x3,metadata=0x1 | grep load:0x2- | wc -l], [0], [1 ]) # Check that hv1 sends chassisredirect port traffic to hv2 AT_CHECK([as hv1 ovs-ofctl dump-flows br-int table=32 | grep =0x3,metadata=0x1 | grep output | wc -l], [0], [1 ]) AT_CHECK([as hv2 ovs-ofctl dump-flows br-int table=32 | grep =0x3,metadata=0x1 | wc -l], [0], [0 ]) # Check that arp reply on distributed gateway port is only programmed on hv2 AT_CHECK([as hv1 ovs-ofctl dump-flows br-int | grep arp | grep =0x2,metadata=0x1 | wc -l], [0], [0 ]) AT_CHECK([as hv2 ovs-ofctl dump-flows br-int | grep arp | grep =0x2,metadata=0x1 | wc -l], [0], [1 ]) ip_to_hex() { printf "%02x%02x%02x%02x" "$@" } : > hv2-vif1.expected : > hv3-vif1.expected # test_arp INPORT SHA SPA TPA [REPLY_HA] # # Causes a packet to be received on INPORT. The packet is an ARP # request with SHA, SPA, and TPA as specified. If REPLY_HA is provided, then # it should be the hardware address of the target to expect to receive in an # ARP reply; otherwise no reply is expected. # # INPORT is an logical switch port number, e.g. 11 for vif11. # SHA and REPLY_HA are each 12 hex digits. # SPA and TPA are each 8 hex digits. test_arp() { local hv=$1 inport=$2 sha=$3 spa=$4 tpa=$5 reply_ha=$6 local request=ffffffffffff${sha}08060001080006040001${sha}${spa}ffffffffffff${tpa} as hv$hv ovs-appctl netdev-dummy/receive hv${hv}-vif$inport $request if test X$reply_ha != X; then # Expect to receive the reply, if any. local reply=${sha}${reply_ha}08060001080006040002${reply_ha}${tpa}${sha}${spa} echo $reply >> hv${hv}-vif$inport.expected fi } rtr_ip=$(ip_to_hex 172 16 1 1) foo_ip=$(ip_to_hex 192 168 1 2) outside_ip=$(ip_to_hex 172 16 1 3) echo $rtr_ip echo $foo_ip echo $outside_ip # ARP for router IP address from outside1, no response expected test_arp 3 1 f00000010204 $outside_ip $rtr_ip # Now check the packets actually received against the ones expected. OVN_CHECK_PACKETS([hv3/vif1-tx.pcap], [hv3-vif1.expected]) # Send ip packet between foo1 and outside1 src_mac="f00000010203" dst_mac="000001010203" src_ip=`ip_to_hex 192 168 1 2` dst_ip=`ip_to_hex 172 16 1 3` packet=${dst_mac}${src_mac}08004500001c0000000040110000${src_ip}${dst_ip}0035111100080000 # Now check the packets actually received against the ones expected. OVN_CHECK_PACKETS([hv3/vif1-tx.pcap], [hv3-vif1.expected]) # Now add bridge-mappings on hv2, which should make everything work as hv2 ovs-vsctl set open . external-ids:ovn-bridge-mappings=phys:br-phys # Allow some time for ovn-northd and ovn-controller to catch up. # XXX This should be more systematic. sleep 2 # ARP for router IP address from outside1 test_arp 3 1 f00000010204 $outside_ip $rtr_ip 000002010203 # Now check the packets actually received against the ones expected. OVN_CHECK_PACKETS([hv3/vif1-tx.pcap], [hv3-vif1.expected]) # Send ip packet between foo1 and outside1 src_mac="f00000010203" dst_mac="000001010203" src_ip=`ip_to_hex 192 168 1 2` dst_ip=`ip_to_hex 172 16 1 3` packet=${dst_mac}${src_mac}08004500001c0000000040110000${src_ip}${dst_ip}0035111100080000 # ARP request packet to expect at outside1 src_mac="000002010203" src_ip=`ip_to_hex 172 16 1 1` arp_request=ffffffffffff${src_mac}08060001080006040001${src_mac}${src_ip}000000000000${dst_ip} as hv1 ovs-appctl netdev-dummy/receive hv1-vif1 $packet echo $arp_request >> hv3-vif1.expected OVN_CHECK_PACKETS([hv3/vif1-tx.pcap], [hv3-vif1.expected]) # Send ARP reply from outside1 back to the router reply_mac="f00000010204" arp_reply=${src_mac}${reply_mac}08060001080006040002${reply_mac}${dst_ip}${src_mac}${src_ip} as hv3 ovs-appctl netdev-dummy/receive hv3-vif1 $arp_reply # Allow some time for ovn-northd and ovn-controller to catch up. # XXX This should be more systematic. sleep 1 # Packet to Expect at outside1 src_mac="000002010203" dst_mac="f00000010204" src_ip=`ip_to_hex 192 168 1 2` dst_ip=`ip_to_hex 172 16 1 3` expected=${dst_mac}${src_mac}08004500001c000000003f110100${src_ip}${dst_ip}0035111100080000 # Resend packet from foo1 to outside1 as hv1 ovs-appctl netdev-dummy/receive hv1-vif1 $packet echo "------ hv1 dump ----------" as hv1 ovs-ofctl show br-int as hv1 ovs-ofctl dump-flows br-int echo "------ hv2 dump ----------" as hv2 ovs-ofctl show br-int as hv2 ovs-ofctl dump-flows br-int echo "------ hv3 dump ----------" as hv3 ovs-ofctl show br-int as hv3 ovs-ofctl dump-flows br-int echo "----------------------------" echo $expected >> hv3-vif1.expected OVN_CHECK_PACKETS([hv3/vif1-tx.pcap], [hv3-vif1.expected]) #Check ovn-trace over "chassisredirect" port AT_CAPTURE_FILE([trace]) ovn_trace () { ovn-trace --all "$@" | tee trace | sed '1,/Minimal trace/d' } echo 'ip.ttl--;' > expout echo 'eth.src = 00:00:02:01:02:03;' >> expout echo 'eth.dst = f0:00:00:01:02:04;' >> expout echo 'output("ln-alice");' >> expout AT_CHECK_UNQUOTED([ovn_trace foo 'inport == "foo1" && eth.src == f0:00:00:01:02:03 && eth.dst == 00:00:01:01:02:03 && ip4.src == 192.168.1.2 && ip4.dst == 172.16.1.3 && ip.ttl == 0xff'], [0], [expout]) # Create logical port alice1 in alice on hv1 as hv1 ovs-vsctl -- add-port br-int hv1-vif2 -- \ set interface hv1-vif2 external-ids:iface-id=alice1 \ options:tx_pcap=hv1/vif2-tx.pcap \ options:rxq_pcap=hv1/vif2-rx.pcap \ ofport-request=1 ovn-nbctl lsp-add alice alice1 \ -- lsp-set-addresses alice1 "f0:00:00:01:02:05 172.16.1.4" # Create logical port foo2 in foo on hv2 as hv2 ovs-vsctl -- add-port br-int hv2-vif1 -- \ set interface hv2-vif1 external-ids:iface-id=foo2 \ options:tx_pcap=hv2/vif1-tx.pcap \ options:rxq_pcap=hv2/vif1-rx.pcap \ ofport-request=1 ovn-nbctl lsp-add foo foo2 \ -- lsp-set-addresses foo2 "f0:00:00:01:02:06 192.168.1.3" # Allow some time for ovn-northd and ovn-controller to catch up. # XXX This should be more systematic. sleep 1 : > hv1-vif2.expected # Send ip packet between alice1 and foo2 src_mac="f00000010205" dst_mac="000002010203" src_ip=`ip_to_hex 172 16 1 4` dst_ip=`ip_to_hex 192 168 1 3` packet=${dst_mac}${src_mac}08004500001c0000000040110000${src_ip}${dst_ip}0035111100080000 as hv1 ovs-appctl netdev-dummy/receive hv1-vif2 $packet # Packet to Expect at foo2 src_mac="000001010203" dst_mac="f00000010206" src_ip=`ip_to_hex 172 16 1 4` dst_ip=`ip_to_hex 192 168 1 3` expected=${dst_mac}${src_mac}08004500001c000000003f110100${src_ip}${dst_ip}0035111100080000 echo $expected >> hv2-vif1.expected OVN_CHECK_PACKETS([hv2/vif1-tx.pcap], [hv2-vif1.expected]) AT_CHECK([ovn-sbctl --bare --columns _uuid find Port_Binding logical_port=cr-alice | wc --lines], [0], [1 ]) ovn-nbctl --timeout=3 --wait=sb remove Logical_Router_Port alice options redirect-chassis AT_CHECK([ovn-sbctl find Port_Binding logical_port=cr-alice | wc --lines], [0], [0 ]) OVN_CLEANUP([hv1],[hv2],[hv3]) AT_CLEANUP AT_SETUP([ovn -- send gratuitous arp for NAT rules on distributed router]) AT_SKIP_IF([test $HAVE_PYTHON = no]) ovn_start # Create logical switches ovn-nbctl ls-add ls0 ovn-nbctl ls-add ls1 # Create distributed router ovn-nbctl create Logical_Router name=lr0 # Add distributed gateway port to distributed router ovn-nbctl lrp-add lr0 lrp0 f0:00:00:00:00:01 192.168.0.1/24 \ -- set Logical_Router_Port lrp0 options:redirect-chassis="hv2" ovn-nbctl lsp-add ls0 lrp0-rp -- set Logical_Switch_Port lrp0-rp \ type=router options:router-port=lrp0 addresses="router" # Add router port to ls1 ovn-nbctl lrp-add lr0 lrp1 f0:00:00:00:00:02 10.0.0.1/24 ovn-nbctl lsp-add ls1 lrp1-rp -- set Logical_Switch_Port lrp1-rp \ type=router options:router-port=lrp1 addresses="router" # Add logical ports for NAT rules ovn-nbctl lsp-add ls1 foo1 \ -- lsp-set-addresses foo1 "00:00:00:00:00:03 10.0.0.3" ovn-nbctl lsp-add ls1 foo2 \ -- lsp-set-addresses foo2 "00:00:00:00:00:04 10.0.0.4" # Add nat-addresses option ovn-nbctl lsp-set-options lrp0-rp router-port=lrp0 nat-addresses="router" # Add NAT rules AT_CHECK([ovn-nbctl lr-nat-add lr0 snat 192.168.0.1 10.0.0.0/24]) AT_CHECK([ovn-nbctl lr-nat-add lr0 dnat 192.168.0.2 10.0.0.2]) AT_CHECK([ovn-nbctl lr-nat-add lr0 dnat_and_snat 192.168.0.3 10.0.0.3 foo1 f0:00:00:00:00:03]) AT_CHECK([ovn-nbctl lr-nat-add lr0 dnat_and_snat 192.168.0.4 10.0.0.4 foo2 f0:00:00:00:00:04]) net_add n1 sim_add hv1 as hv1 ovs-vsctl add-br br-phys ovn_attach n1 br-phys 192.168.0.1 AT_CHECK([ovs-vsctl set Open_vSwitch . external-ids:ovn-bridge-mappings=physnet1:br-phys]) AT_CHECK([ovs-vsctl add-port br-phys snoopvif -- set Interface snoopvif options:tx_pcap=hv1/snoopvif-tx.pcap options:rxq_pcap=hv1/snoopvif-rx.pcap]) sim_add hv2 as hv2 ovs-vsctl add-br br-phys ovn_attach n1 br-phys 192.168.0.2 # Initially test with no bridge-mapping on hv2, expect to receive no packets sim_add hv3 as hv3 ovs-vsctl add-br br-phys ovn_attach n1 br-phys 192.168.0.3 # Initially test with no bridge-mapping on hv3 # Create a localnet port. AT_CHECK([ovn-nbctl lsp-add ls0 ln_port]) AT_CHECK([ovn-nbctl lsp-set-addresses ln_port unknown]) AT_CHECK([ovn-nbctl lsp-set-type ln_port localnet]) AT_CHECK([ovn-nbctl lsp-set-options ln_port network_name=physnet1]) # Allow some time for ovn-northd and ovn-controller to catch up. # XXX This should be more systematic. sleep 2 # Expect no packets when hv2 bridge-mapping is not present : > packets OVN_CHECK_PACKETS([hv1/snoopvif-tx.pcap], [packets]) # Add bridge-mapping on hv2 AT_CHECK([as hv2 ovs-vsctl set Open_vSwitch . external-ids:ovn-bridge-mappings=physnet1:br-phys]) # Wait for packets to be received. OVS_WAIT_UNTIL([test `wc -c < "hv1/snoopvif-tx.pcap"` -ge 100]) trim_zeros() { sed 's/\(00\)\{1,\}$//' } $PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/snoopvif-tx.pcap | trim_zeros > packets expected="fffffffffffff0000000000108060001080006040001f00000000001c0a80001000000000000c0a80001" echo $expected > expout expected="fffffffffffff0000000000108060001080006040001f00000000001c0a80002000000000000c0a80002" echo $expected >> expout AT_CHECK([sort packets], [0], [expout]) sort packets | cat # Temporarily remove nat-addresses option to avoid race conditions # due to GARP backoff ovn-nbctl lsp-set-options lrp0-rp router-port=lrp0 nat-addresses="" reset_pcap_file() { local iface=$1 local pcap_file=$2 ovs-vsctl -- set Interface $iface options:tx_pcap=dummy-tx.pcap \ options:rxq_pcap=dummy-rx.pcap rm -f ${pcap_file}*.pcap ovs-vsctl -- set Interface $iface options:tx_pcap=${pcap_file}-tx.pcap \ options:rxq_pcap=${pcap_file}-rx.pcap } as hv1 reset_pcap_file snoopvif hv1/snoopvif # Add OVS ports for foo1 and foo2 on hv3 ovs-vsctl -- add-port br-int hv3-vif1 -- \ set interface hv3-vif1 external-ids:iface-id=foo1 \ ofport-request=1 ovs-vsctl -- add-port br-int hv3-vif2 -- \ set interface hv3-vif2 external-ids:iface-id=foo2 \ ofport-request=2 # Add bridge-mapping on hv3 AT_CHECK([as hv3 ovs-vsctl set Open_vSwitch . external-ids:ovn-bridge-mappings=physnet1:br-phys]) # Re-add nat-addresses option ovn-nbctl lsp-set-options lrp0-rp router-port=lrp0 nat-addresses="router" # Wait for packets to be received. OVS_WAIT_UNTIL([test `wc -c < "hv1/snoopvif-tx.pcap"` -ge 250]) trim_zeros() { sed 's/\(00\)\{1,\}$//' } $PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/snoopvif-tx.pcap | trim_zeros > packets expected="fffffffffffff0000000000308060001080006040001f00000000003c0a80003000000000000c0a80003" echo $expected >> expout expected="fffffffffffff0000000000408060001080006040001f00000000004c0a80004000000000000c0a80004" echo $expected >> expout AT_CHECK([sort packets], [0], [expout]) sort packets | cat OVN_CLEANUP([hv1],[hv2],[hv3]) AT_CLEANUP AT_SETUP([ovn -- /32 router IP address]) AT_SKIP_IF([test $HAVE_PYTHON = no]) ovn_start # Logical network: # 2 LS 'foo' and 'alice' connected via router R1. # R1 connects to 'alice' with a /32 IP address. We use static routes and # nexthop to push traffic to a logical port in switch 'alice' ovn-nbctl lr-add R1 ovn-nbctl ls-add foo ovn-nbctl ls-add alice # Connect foo to R1 ovn-nbctl lrp-add R1 foo 00:00:00:01:02:03 192.168.1.1/24 ovn-nbctl lsp-add foo rp-foo -- set Logical_Switch_Port rp-foo type=router \ options:router-port=foo addresses=\"00:00:00:01:02:03\" # Connect alice to R1. ovn-nbctl lrp-add R1 alice 00:00:00:01:02:04 172.16.1.1/32 ovn-nbctl lsp-add alice rp-alice -- set Logical_Switch_Port rp-alice \ type=router options:router-port=alice addresses=\"00:00:00:01:02:04\" # Create logical port foo1 in foo ovn-nbctl lsp-add foo foo1 \ -- lsp-set-addresses foo1 "f0:00:00:01:02:03 192.168.1.2" # Create logical port alice1 in alice ovn-nbctl lsp-add alice alice1 \ -- lsp-set-addresses alice1 "f0:00:00:01:02:04 10.0.0.2" #install default route in R1 to use alice1's IP address as nexthop ovn-nbctl lr-route-add R1 0.0.0.0/0 10.0.0.2 alice # Create two hypervisor and create OVS ports corresponding to logical ports. net_add n1 sim_add hv1 as hv1 ovs-vsctl add-br br-phys ovn_attach n1 br-phys 192.168.0.1 ovs-vsctl -- add-port br-int hv1-vif1 -- \ set interface hv1-vif1 external-ids:iface-id=foo1 \ options:tx_pcap=hv1/vif1-tx.pcap \ options:rxq_pcap=hv1/vif1-rx.pcap \ ofport-request=1 sim_add hv2 as hv2 ovs-vsctl add-br br-phys ovn_attach n1 br-phys 192.168.0.2 ovs-vsctl -- add-port br-int hv2-vif1 -- \ set interface hv2-vif1 external-ids:iface-id=alice1 \ options:tx_pcap=hv2/vif1-tx.pcap \ options:rxq_pcap=hv2/vif1-rx.pcap \ ofport-request=1 # Pre-populate the hypervisors' ARP tables so that we don't lose any # packets for ARP resolution (native tunneling doesn't queue packets # for ARP resolution). ovn_populate_arp # Allow some time for ovn-northd and ovn-controller to catch up. # XXX This should be more systematic. sleep 1 ip_to_hex() { printf "%02x%02x%02x%02x" "$@" } # Send ip packets between foo1 and alice1 src_mac="f00000010203" dst_mac="000000010203" src_ip=`ip_to_hex 192 168 1 2` dst_ip=`ip_to_hex 10 0 0 2` packet=${dst_mac}${src_mac}08004500001c0000000040110000${src_ip}${dst_ip}0035111100080000 # Send the first packet to trigger a ARP response and population of # mac_bindings table. as hv1 ovs-appctl netdev-dummy/receive hv1-vif1 $packet OVS_WAIT_UNTIL([test `ovn-sbctl find mac_binding ip="10.0.0.2" | wc -l` -gt 0]) # Send the second packet to reach the destination. as hv1 ovs-appctl netdev-dummy/receive hv1-vif1 $packet # Packet to Expect at 'alice1' src_mac="000000010204" dst_mac="f00000010204" src_ip=`ip_to_hex 192 168 1 2` dst_ip=`ip_to_hex 10 0 0 2` echo "${dst_mac}${src_mac}08004500001c000000003f110100${src_ip}${dst_ip}0035111100080000" > expected OVN_CHECK_PACKETS([hv2/vif1-tx.pcap], [expected]) OVN_CLEANUP([hv1],[hv2]) AT_CLEANUP AT_SETUP([ovn -- 2 HVs, 1 lport/HV, localport ports]) AT_SKIP_IF([test $HAVE_PYTHON = no]) ovn_start ovn-nbctl ls-add ls1 # Add localport to the switch ovn-nbctl lsp-add ls1 lp01 ovn-nbctl lsp-set-addresses lp01 f0:00:00:00:00:01 ovn-nbctl lsp-set-type lp01 localport net_add n1 for i in 1 2; do sim_add hv$i as hv$i ovs-vsctl add-br br-phys ovn_attach n1 br-phys 192.168.0.$i ovs-vsctl add-port br-int vif01 -- \ set Interface vif01 external-ids:iface-id=lp01 \ options:tx_pcap=hv${i}/vif01-tx.pcap \ options:rxq_pcap=hv${i}/vif01-rx.pcap \ ofport-request=${i}0 ovs-vsctl add-port br-int vif${i}1 -- \ set Interface vif${i}1 external-ids:iface-id=lp${i}1 \ options:tx_pcap=hv${i}/vif${i}1-tx.pcap \ options:rxq_pcap=hv${i}/vif${i}1-rx.pcap \ ofport-request=${i}1 ovn-nbctl lsp-add ls1 lp${i}1 ovn-nbctl lsp-set-addresses lp${i}1 f0:00:00:00:00:${i}1 ovn-nbctl lsp-set-port-security lp${i}1 f0:00:00:00:00:${i}1 OVS_WAIT_UNTIL([test x`ovn-nbctl lsp-get-up lp${i}1` = xup]) done ovn-nbctl --wait=sb sync ovn-sbctl dump-flows ovn_populate_arp # Given the name of a logical port, prints the name of the hypervisor # on which it is located. vif_to_hv() { echo hv${1%?} } # # test_packet INPORT DST SRC ETHTYPE EOUT LOUT DEFHV # # This shell function causes a packet to be received on INPORT. The packet's # content has Ethernet destination DST and source SRC (each exactly 12 hex # digits) and Ethernet type ETHTYPE (4 hex digits). INPORT is specified as # logical switch port numbers, e.g. 11 for vif11. # # EOUT is the end-to-end output port, that is, where the packet will end up # after possibly bouncing through one or more localnet ports. LOUT is the # logical output port, which might be a localnet port, as seen by ovn-trace # (which doesn't know what localnet ports are connected to and therefore can't # figure out the end-to-end answer). # # DEFHV is the default hypervisor from where the packet is going to be sent # if the source port is a localport. for i in 1 2; do for j in 0 1; do : > $i$j.expected done done test_packet() { local inport=$1 dst=$2 src=$3 eth=$4 eout=$5 lout=$6 defhv=$7 echo "$@" # First try tracing the packet. uflow="inport==\"lp$inport\" && eth.dst==$dst && eth.src==$src && eth.type==0x$eth" if test $lout != drop; then echo "output(\"$lout\");" fi > expout AT_CAPTURE_FILE([trace]) AT_CHECK([ovn-trace --all ls1 "$uflow" | tee trace | sed '1,/Minimal trace/d'], [0], [expout]) # Then actually send a packet, for an end-to-end test. local packet=$(echo $dst$src | sed 's/://g')${eth} hv=`vif_to_hv $inport` # If hypervisor 0 (localport) use the defhv parameter if test $hv = hv0; then hv=$defhv fi vif=vif$inport as $hv ovs-appctl netdev-dummy/receive $vif $packet if test $eout != drop; then echo $packet >> ${eout#lp}.expected fi } # lp11 and lp21 are on different hypervisors test_packet 11 f0:00:00:00:00:21 f0:00:00:00:00:11 1121 lp21 lp21 test_packet 21 f0:00:00:00:00:11 f0:00:00:00:00:21 2111 lp11 lp11 # Both VIFs should be able to reach the localport on their own HV test_packet 11 f0:00:00:00:00:01 f0:00:00:00:00:11 1101 lp01 lp01 test_packet 21 f0:00:00:00:00:01 f0:00:00:00:00:21 2101 lp01 lp01 # Packet sent from localport on same hv should reach the vif test_packet 01 f0:00:00:00:00:11 f0:00:00:00:00:01 0111 lp11 lp11 hv1 test_packet 01 f0:00:00:00:00:21 f0:00:00:00:00:01 0121 lp21 lp21 hv2 # Packet sent from localport on different hv should be dropped test_packet 01 f0:00:00:00:00:21 f0:00:00:00:00:01 0121 drop lp21 hv1 test_packet 01 f0:00:00:00:00:11 f0:00:00:00:00:01 0111 drop lp11 hv2 # Now check the packets actually received against the ones expected. for i in 1 2; do for j in 0 1; do OVN_CHECK_PACKETS([hv$i/vif$i$j-tx.pcap], [$i$j.expected]) done done OVN_CLEANUP([hv1],[hv2]) AT_CLEANUP AT_SETUP([ovn -- 1 LR with HA distributed router gateway port]) AT_SKIP_IF([test $HAVE_PYTHON = no]) ovn_start net_add n1 # create gateways with external network connectivity for i in 1 2; do sim_add gw$i as gw$i ovs-vsctl add-br br-phys ovn_attach n1 br-phys 192.168.0.$i ovs-vsctl set open . external-ids:ovn-bridge-mappings=phys:br-phys done ovn-nbctl ls-add inside ovn-nbctl ls-add outside # create hypervisors with a vif port each to an internal network for i in 1 2; do sim_add hv$i as hv$i ovs-vsctl add-br br-phys ovn_attach n1 br-phys 192.168.0.1$i ovs-vsctl -- add-port br-int hv$i-vif1 -- \ set interface hv$i-vif1 external-ids:iface-id=inside$i \ options:tx_pcap=hv$i/vif1-tx.pcap \ options:rxq_pcap=hv$i/vif1-rx.pcap \ ofport-request=1 ovn-nbctl lsp-add inside inside$i \ -- lsp-set-addresses inside$i "f0:00:00:01:22:$i 192.168.1.10$i" done ovn_populate_arp ovn-nbctl create Logical_Router name=R1 # Connect inside to R1 ovn-nbctl lrp-add R1 inside 00:00:01:01:02:03 192.168.1.1/24 ovn-nbctl lsp-add inside rp-inside -- set Logical_Switch_Port rp-inside \ type=router options:router-port=inside \ -- lsp-set-addresses rp-inside router # Connect outside to R1 as distributed router gateway port on gw1+gw2 ovn-nbctl lrp-add R1 outside 00:00:02:01:02:04 192.168.0.101/24 ovn-nbctl --id=@gc0 create Gateway_Chassis \ name=outside_gw1 chassis_name=gw1 priority=20 -- \ --id=@gc1 create Gateway_Chassis \ name=outside_gw2 chassis_name=gw2 priority=10 -- \ set Logical_Router_Port outside 'gateway_chassis=[@gc0,@gc1]' ovn-nbctl lsp-add outside rp-outside -- set Logical_Switch_Port rp-outside \ type=router options:router-port=outside \ -- lsp-set-addresses rp-outside router # Create localnet port in outside ovn-nbctl lsp-add outside ln-outside ovn-nbctl lsp-set-addresses ln-outside unknown ovn-nbctl lsp-set-type ln-outside localnet ovn-nbctl lsp-set-options ln-outside network_name=phys # Allow some time for ovn-northd and ovn-controller to catch up. # XXX This should be more systematic. ovn-nbctl --wait=hv --timeout=3 sync echo "---------NB dump-----" ovn-nbctl show echo "---------------------" ovn-nbctl list logical_router echo "---------------------" ovn-nbctl list logical_router_port echo "---------------------" echo "---------SB dump-----" ovn-sbctl list datapath_binding echo "---------------------" ovn-sbctl list port_binding echo "---------------------" ovn-sbctl dump-flows echo "---------------------" ovn-sbctl list chassis ovn-sbctl list encap echo "---------------------" echo "------ Gateway_Chassis dump (SBDB) -------" ovn-sbctl list Gateway_Chassis echo "------ Port_Binding chassisredirect -------" ovn-sbctl find Port_Binding type=chassisredirect echo "-------------------------------------------" for chassis in gw1 gw2 hv1 hv2; do as $chassis echo "------ $chassis dump ----------" ovs-ofctl show br-int ovs-ofctl dump-flows br-int echo "--------------------------" done function bfd_dump() { for chassis in gw1 gw2 hv1 hv2; do as $chassis echo "------ $chassis dump (BFD)----" echo "BFD (from $chassis):" # dump BFD config and status to the other chassis for chassis2 in gw1 gw2 hv1 hv2; do if [[ "$chassis" != "$chassis2" ]]; then echo " -> $chassis2:" echo " $(ovs-vsctl --bare --columns bfd,bfd_status find Interface name=ovn-$chassis2-0)" fi done echo "--------------------------" done } bfd_dump hv1_gw1_ofport=$(as hv1 ovs-vsctl --bare --columns ofport find Interface name=ovn-gw1-0) hv1_gw2_ofport=$(as hv1 ovs-vsctl --bare --columns ofport find Interface name=ovn-gw2-0) hv2_gw1_ofport=$(as hv2 ovs-vsctl --bare --columns ofport find Interface name=ovn-gw1-0) hv2_gw2_ofport=$(as hv2 ovs-vsctl --bare --columns ofport find Interface name=ovn-gw2-0) echo $hv1_gw1_ofport echo $hv1_gw2_ofport echo $hv2_gw1_ofport echo $hv2_gw2_ofport echo "--- hv1 ---" as hv1 ovs-ofctl dump-flows br-int table=32 echo "--- hv2 ---" as hv2 ovs-ofctl dump-flows br-int table=32 gw1_chassis=$(ovn-sbctl --bare --columns=_uuid find Chassis name=gw1) gw2_chassis=$(ovn-sbctl --bare --columns=_uuid find Chassis name=gw2) AT_CHECK([as hv1 ovs-ofctl dump-flows br-int table=32 | grep active_backup | grep slaves:$hv1_gw1_ofport,$hv1_gw2_ofport | wc -l], [0], [1 ]) AT_CHECK([as hv2 ovs-ofctl dump-flows br-int table=32 | grep active_backup | grep slaves:$hv2_gw1_ofport,$hv2_gw2_ofport | wc -l], [0], [1 ]) sleep 3 # let BFD sessions settle so we get the right flows on the right chassis # make sure that flows for handling the outside router port reside on gw1 AT_CHECK([as gw1 ovs-ofctl dump-flows br-int table=23 | grep 00:00:02:01:02:04 | wc -l], [0], [[1 ]]) AT_CHECK([as gw2 ovs-ofctl dump-flows br-int table=23 | grep 00:00:02:01:02:04 | wc -l], [0], [[0 ]]) # make sure ARP responder flows for outside router port reside on gw1 too AT_CHECK([as gw1 ovs-ofctl dump-flows br-int table=9 | grep arp_tpa=192.168.0.101 | wc -l], [0], [[1 ]]) AT_CHECK([as gw2 ovs-ofctl dump-flows br-int table=9 | grep arp_tpa=192.168.0.101 | wc -l], [0], [[0 ]]) # check that the chassis redirect port has been claimed by the gw1 chassis AT_CHECK([ovn-sbctl --columns chassis --bare find Port_Binding logical_port=cr-outside | grep $gw1_chassis | wc -l], [0],[[1 ]]) # at this point, we invert the priority of the gw chassis between gw1 and gw2 ovn-nbctl --id=@gc0 create Gateway_Chassis \ name=outside_gw1 chassis_name=gw1 priority=10 -- \ --id=@gc1 create Gateway_Chassis \ name=outside_gw2 chassis_name=gw2 priority=20 -- \ set Logical_Router_Port outside 'gateway_chassis=[@gc0,@gc1]' # XXX: Let the change propagate down to the ovn-controllers ovn-nbctl --wait=hv --timeout=3 sync # we make sure that the hypervisors noticed, and inverted the slave ports AT_CHECK([as hv1 ovs-ofctl dump-flows br-int table=32 | grep active_backup | grep slaves:$hv1_gw2_ofport,$hv1_gw1_ofport | wc -l], [0], [1 ]) AT_CHECK([as hv2 ovs-ofctl dump-flows br-int table=32 | grep active_backup | grep slaves:$hv2_gw2_ofport,$hv2_gw1_ofport | wc -l], [0], [1 ]) # check that the chassis redirect port has been reclaimed by the gw2 chassis AT_CHECK([ovn-sbctl --columns chassis --bare find Port_Binding logical_port=cr-outside | grep $gw2_chassis | wc -l], [0],[[1 ]]) # check BFD enablement on tunnel ports from gw1 ######### as gw1 for chassis in gw2 hv1 hv2; do echo "checking gw1 -> $chassis" AT_CHECK([ovs-vsctl --bare --columns bfd find Interface name=ovn-$chassis-0],[0], [[enable=true ]]) done # check BFD enablement on tunnel ports from gw2 ########## as gw2 for chassis in gw1 hv1 hv2; do echo "checking gw2 -> $chassis" AT_CHECK([ovs-vsctl --bare --columns bfd find Interface name=ovn-$chassis-0],[0], [[enable=true ]]) done # check BFD enablement on tunnel ports from hv1 ########### as hv1 for chassis in gw1 gw2; do echo "checking hv1 -> $chassis" AT_CHECK([ovs-vsctl --bare --columns bfd find Interface name=ovn-$chassis-0],[0], [[enable=true ]]) done # make sure BFD is not enabled to hv2, we don't need it AT_CHECK([ovs-vsctl --bare --columns bfd find Interface name=ovn-hv2-0],[0], [[enable=false ]]) # check BFD enablement on tunnel ports from hv2 ########## as hv2 for chassis in gw1 gw2; do echo "checking hv2 -> $chassis" AT_CHECK([ovs-vsctl --bare --columns bfd find Interface name=ovn-$chassis-0],[0], [[enable=true ]]) done # make sure BFD is not enabled to hv1, we don't need it AT_CHECK([ovs-vsctl --bare --columns bfd find Interface name=ovn-hv1-0],[0], [[enable=false ]]) sleep 3 # let BFD sessions settle so we get the right flows on the right chassis # make sure that flows for handling the outside router port reside on gw2 now AT_CHECK([as gw2 ovs-ofctl dump-flows br-int table=23 | grep 00:00:02:01:02:04 | wc -l], [0], [[1 ]]) AT_CHECK([as gw1 ovs-ofctl dump-flows br-int table=23 | grep 00:00:02:01:02:04 | wc -l], [0], [[0 ]]) # disconnect GW2 from the network, GW1 should take over as gw2 port=${sandbox}_br-phys as main ovs-vsctl del-port n1 $port sleep 4 bfd_dump # make sure that flows for handling the outside router port reside on gw2 now AT_CHECK([as gw1 ovs-ofctl dump-flows br-int table=23 | grep 00:00:02:01:02:04 | wc -l], [0], [[1 ]]) AT_CHECK([as gw2 ovs-ofctl dump-flows br-int table=23 | grep 00:00:02:01:02:04 | wc -l], [0], [[0 ]]) # check that the chassis redirect port has been reclaimed by the gw1 chassis AT_CHECK([ovn-sbctl --columns chassis --bare find Port_Binding logical_port=cr-outside | grep $gw1_chassis | wc -l], [0],[[1 ]]) OVN_CLEANUP([gw1],[gw2],[hv1],[hv2]) AT_CLEANUP AT_SETUP([ovn -- send gratuitous ARP for NAT rules on HA distributed router]) AT_SKIP_IF([test $HAVE_PYTHON = no]) ovn_start ovn-nbctl ls-add ls0 ovn-nbctl ls-add ls1 ovn-nbctl create Logical_Router name=lr0 ovn-nbctl lrp-add lr0 lrp0 f0:00:00:00:00:01 192.168.0.100/24 ovn-nbctl --id=@gc0 create Gateway_Chassis \ name=outside_gw1 chassis_name=hv2 priority=10 -- \ --id=@gc1 create Gateway_Chassis \ name=outside_gw2 chassis_name=hv3 priority=1 -- \ set Logical_Router_Port lrp0 'gateway_chassis=[@gc0,@gc1]' ovn-nbctl lsp-add ls0 lrp0-rp -- set Logical_Switch_Port lrp0-rp \ type=router options:router-port=lrp0 addresses="router" ovn-nbctl lrp-add lr0 lrp1 f0:00:00:00:00:02 10.0.0.1/24 ovn-nbctl lsp-add ls1 lrp1-rp -- set Logical_Switch_Port lrp1-rp \ type=router options:router-port=lrp1 addresses="router" # Add NAT rules AT_CHECK([ovn-nbctl lr-nat-add lr0 snat 192.168.0.100 10.0.0.0/24]) net_add n1 sim_add hv1 as hv1 ovs-vsctl add-br br-phys ovn_attach n1 br-phys 192.168.0.1 AT_CHECK([ovs-vsctl set Open_vSwitch . external-ids:ovn-bridge-mappings=physnet1:br-phys]) AT_CHECK([ovs-vsctl add-port br-phys snoopvif -- set Interface snoopvif options:tx_pcap=hv1/snoopvif-tx.pcap options:rxq_pcap=hv1/snoopvif-rx.pcap]) sim_add hv2 as hv2 ovs-vsctl add-br br-phys ovn_attach n1 br-phys 192.168.0.2 AT_CHECK([as hv2 ovs-vsctl set Open_vSwitch . external-ids:ovn-bridge-mappings=physnet1:br-phys]) sim_add hv3 as hv3 ovs-vsctl add-br br-phys ovn_attach n1 br-phys 192.168.0.3 AT_CHECK([as hv3 ovs-vsctl set Open_vSwitch . external-ids:ovn-bridge-mappings=physnet1:br-phys]) # Create a localnet port. AT_CHECK([ovn-nbctl lsp-add ls0 ln_port]) AT_CHECK([ovn-nbctl lsp-set-addresses ln_port unknown]) AT_CHECK([ovn-nbctl lsp-set-type ln_port localnet]) AT_CHECK([ovn-nbctl lsp-set-options ln_port network_name=physnet1]) # wait for earlier changes to take effect AT_CHECK([ovn-nbctl --timeout=3 --wait=sb sync], [0], [ignore]) reset_pcap_file() { local iface=$1 local pcap_file=$2 ovs-vsctl -- set Interface $iface options:tx_pcap=dummy-tx.pcap \ options:rxq_pcap=dummy-rx.pcap rm -f ${pcap_file}*.pcap ovs-vsctl -- set Interface $iface options:tx_pcap=${pcap_file}-tx.pcap \ options:rxq_pcap=${pcap_file}-rx.pcap } as hv1 reset_pcap_file snoopvif hv1/snoopvif as hv2 reset_pcap_file br-phys_n1 hv2/br-phys_n1 as hv3 reset_pcap_file br-phys_n1 hv3/br-phys_n1 # add nat-addresses option ovn-nbctl --wait=sb lsp-set-options lrp0-rp router-port=lrp0 nat-addresses="router" # Wait for packets to be received through hv2. OVS_WAIT_UNTIL([test `wc -c < "hv1/snoopvif-tx.pcap"` -ge 100]) trim_zeros() { sed 's/\(00\)\{1,\}$//' } only_broadcast_from_lrp1() { grep "fffffffffffff00000000001" } garp="fffffffffffff0000000000108060001080006040001f00000000001c0a80064000000000000c0a80064" echo $garp > expout $PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/snoopvif-tx.pcap | trim_zeros | only_broadcast_from_lrp1 | uniq > hv1_snoop_tx echo "packets on hv1-snoopvif:" cat hv1_snoop_tx AT_CHECK([sort hv1_snoop_tx], [0], [expout]) $PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv2/br-phys_n1-tx.pcap | trim_zeros | only_broadcast_from_lrp1 | uniq > hv2_br_phys_tx echo "packets on hv2 br-phys tx" cat hv2_br_phys_tx AT_CHECK([grep $garp hv2_br_phys_tx | sort], [0], [expout]) $PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv3/br-phys_n1-tx.pcap | trim_zeros | only_broadcast_from_lrp1 | uniq > hv3_br_phys_tx echo "packets on hv3 br-phys tx" cat hv3_br_phys_tx AT_CHECK([grep $garp hv3_br_phys_tx | sort], [0], []) # at this point, we invert the priority of the gw chassis between hv2 and hv3 ovn-nbctl --wait=hv \ --id=@gc0 create Gateway_Chassis \ name=outside_gw1 chassis_name=hv2 priority=1 -- \ --id=@gc1 create Gateway_Chassis \ name=outside_gw2 chassis_name=hv3 priority=10 -- \ set Logical_Router_Port lrp0 'gateway_chassis=[@gc0,@gc1]' as hv1 reset_pcap_file snoopvif hv1/snoopvif as hv2 reset_pcap_file br-phys_n1 hv2/br-phys_n1 as hv3 reset_pcap_file br-phys_n1 hv3/br-phys_n1 # Wait for packets to be received. OVS_WAIT_UNTIL([test `wc -c < "hv1/snoopvif-tx.pcap"` -ge 100]) trim_zeros() { sed 's/\(00\)\{1,\}$//' } $PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/snoopvif-tx.pcap | trim_zeros | only_broadcast_from_lrp1 | uniq > hv1_snoopvif_tx AT_CHECK([sort hv1_snoopvif_tx], [0], [expout]) $PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv3/br-phys_n1-tx.pcap | trim_zeros | only_broadcast_from_lrp1 | uniq > hv3_br_phys_tx AT_CHECK([grep $garp hv3_br_phys_tx | sort], [0], [expout]) $PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv2/br-phys_n1-tx.pcap | trim_zeros | only_broadcast_from_lrp1 | uniq > hv2_br_phys_tx AT_CHECK([grep $garp hv2_br_phys_tx | sort], [0], []) OVN_CLEANUP([hv1],[hv2],[hv3]) AT_CLEANUP AT_SETUP([ovn -- ensure one gw controller restart in HA doesn't bounce the master]) AT_SKIP_IF([test $HAVE_PYTHON = no]) ovn_start net_add n1 # create two gateways with external network connectivity for i in 1 2; do sim_add gw$i as gw$i ovs-vsctl add-br br-phys ovn_attach n1 br-phys 192.168.0.$i ovs-vsctl set open . external-ids:ovn-bridge-mappings=phys:br-phys done ovn-nbctl ls-add inside ovn-nbctl ls-add outside # create one hypervisors with a vif port the internal network sim_add hv1 as hv1 ovs-vsctl add-br br-phys ovn_attach n1 br-phys 192.168.0.11 ovs-vsctl -- add-port br-int hv1-vif1 -- \ set interface hv1-vif1 external-ids:iface-id=inside1 \ options:tx_pcap=hv1/vif1-tx.pcap \ options:rxq_pcap=hv1/vif1-rx.pcap \ ofport-request=1 ovn-nbctl lsp-add inside inside1 \ -- lsp-set-addresses inside1 "f0:00:00:01:22:01 192.168.1.101" ovn_populate_arp ovn-nbctl create Logical_Router name=R1 # Connect inside to R1 ovn-nbctl lrp-add R1 inside 00:00:01:01:02:03 192.168.1.1/24 ovn-nbctl lsp-add inside rp-inside -- set Logical_Switch_Port rp-inside \ type=router options:router-port=inside \ -- lsp-set-addresses rp-inside router # Connect outside to R1 as distributed router gateway port on gw1+gw2 ovn-nbctl lrp-add R1 outside 00:00:02:01:02:04 192.168.0.101/24 ovn-nbctl --id=@gc0 create Gateway_Chassis \ name=outside_gw1 chassis_name=gw1 priority=20 -- \ --id=@gc1 create Gateway_Chassis \ name=outside_gw2 chassis_name=gw2 priority=10 -- \ set Logical_Router_Port outside 'gateway_chassis=[@gc0,@gc1]' ovn-nbctl lsp-add outside rp-outside -- set Logical_Switch_Port rp-outside \ type=router options:router-port=outside \ -- lsp-set-addresses rp-outside router # Create localnet port in outside ovn-nbctl lsp-add outside ln-outside ovn-nbctl lsp-set-addresses ln-outside unknown ovn-nbctl lsp-set-type ln-outside localnet ovn-nbctl lsp-set-options ln-outside network_name=phys # Allow some time for ovn-northd and ovn-controller to catch up. ovn-nbctl --wait=hv --timeout=3 sync # currently when ovn-controller is restarted, the old entry is deleted # and a new one is created, which leaves the Gateway_Chassis with # an empty chassis for a while. NOTE: restarting ovn-controller in tests # doesn't have the same effect because "name" is conserved, and the # Chassis entry is not replaced. truncate -s 0 gw1/ovn-controller.log gw2_chassis=$(ovn-sbctl --bare --columns=_uuid find Chassis name=gw2) ovn-sbctl destroy Chassis $gw2_chassis # Ensure ovn-controller has processed latest sbdb update # ovn-nbctl --wait=hv sync AT_CHECK([grep "Releasing lport" gw1/ovn-controller.log], [1], []) OVN_CLEANUP([gw1],[gw2],[hv1]) AT_CLEANUP