forked from openvswitch/ovs
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathovn.at
1166 lines (1010 loc) · 45 KB
/
ovn.at
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
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 "
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
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
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} => 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).
123 == 123 => Syntax error at `123' expecting field 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 => Extra tokens at end of input.
]])
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 => xreg0[32..63] == 0 && xreg0[0..31] == 0 && xreg1[32..63] == 0 && xreg1[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 570 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 62418 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 477138 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 124410 expressions of 3 terminals with 1 numeric vars (each 3 bits) in terms of operators == != < <= > >= and 1 string vars.
])
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 1207162 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 128282 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 368550 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 116550 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_CHECK([ovstest test-ovn exhaustive --operation=flow --nvars=2 --svars=0 --bits=2 --relops='==' 4], [0],
[Tested converting to flows 128282 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_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_CHECK([ovstest test-ovn exhaustive --operation=flow --nvars=1 --bits=2 --svars=1 --relops='==' 4], [0],
[Tested converting to flows 37994 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_CHECK([ovstest test-ovn exhaustive --operation=flow --nvars=3 --svars=0 --bits=3 --relops='==' 3], [0],
[Tested converting to flows 38394 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])
expr_to_flow () {
echo "$1" | ovstest test-ovn expr-to-flows | sort
}
AT_CHECK([expr_to_flow 'inport == "eth0"'], [0], [reg6=0x5
])
AT_CHECK([expr_to_flow 'inport == "eth1"'], [0], [reg6=0x6
])
AT_CHECK([expr_to_flow 'inport == "eth2"'], [0], [(no flows)
])
AT_CHECK([expr_to_flow 'inport == "eth0" && ip'], [0], [dnl
ip,reg6=0x5
ipv6,reg6=0x5
])
AT_CHECK([expr_to_flow 'inport == "eth1" && ip'], [0], [dnl
ip,reg6=0x6
ipv6,reg6=0x6
])
AT_CHECK([expr_to_flow 'inport == "eth2" && ip'], [0], [(no flows)
])
AT_CHECK([expr_to_flow 'inport == {"eth0", "eth1", "eth2", "LOCAL"}'], [0],
[reg6=0x5
reg6=0x6
reg6=0xfffe
])
AT_CHECK([expr_to_flow 'inport == {"eth0", "eth1", "eth2"} && ip'], [0], [dnl
ip,reg6=0x5
ip,reg6=0x6
ipv6,reg6=0x5
ipv6,reg6=0x6
])
AT_CHECK([expr_to_flow 'inport == "eth0" && inport == "eth1"'], [0], [dnl
(no flows)
])
AT_CLEANUP
AT_SETUP([ovn -- action parsing])
dnl Text before => is input, text after => is expected output.
AT_DATA([test-cases.txt], [[
# Positive tests.
drop; => actions=drop, prereqs=1
next; => actions=resubmit(,27), prereqs=1
next(0); => actions=resubmit(,16), prereqs=1
next(15); => actions=resubmit(,31), prereqs=1
ct_next; => actions=ct(table=27,zone=NXM_NX_REG5[0..15]), prereqs=ip
ct_commit; => actions=ct(commit,zone=NXM_NX_REG5[0..15]), prereqs=ip
output; => actions=resubmit(,64), prereqs=1
outport="eth0"; next; outport="LOCAL"; next; => actions=set_field:0x5->reg7,resubmit(,27),set_field:0xfffe->reg7,resubmit(,27), prereqs=1
tcp.dst=80; => actions=set_field:80->tcp_dst, prereqs=ip.proto == 0x6 && (eth.type == 0x800 || eth.type == 0x86dd)
eth.dst[40] = 1; => actions=set_field:01:00:00:00:00:00/01:00:00:00:00:00->eth_dst, prereqs=1
vlan.pcp = 2; => actions=set_field:0x4000/0xe000->vlan_tci, prereqs=vlan.tci[12]
vlan.tci[13..15] = 2; => actions=set_field:0x4000/0xe000->vlan_tci, prereqs=1
reg0 = reg1; => actions=move:OXM_OF_PKT_REG0[0..31]->OXM_OF_PKT_REG0[32..63], prereqs=1
vlan.pcp = reg0[0..2]; => actions=move:OXM_OF_PKT_REG0[32..34]->NXM_OF_VLAN_TCI[13..15], prereqs=vlan.tci[12]
reg0[10] = vlan.pcp[1]; => actions=move:NXM_OF_VLAN_TCI[14]->OXM_OF_PKT_REG0[42], prereqs=vlan.tci[12]
outport = inport; => actions=move:NXM_NX_REG6[]->NXM_NX_REG7[], prereqs=1
inport = ""; => actions=set_field:0->reg6,set_field:0->in_port, prereqs=1
reg0 <-> reg1; => actions=push:OXM_OF_PKT_REG0[0..31],push:OXM_OF_PKT_REG0[32..63],pop:OXM_OF_PKT_REG0[0..31],pop:OXM_OF_PKT_REG0[32..63], prereqs=1
vlan.pcp <-> reg0[0..2]; => actions=push:OXM_OF_PKT_REG0[32..34],push:NXM_OF_VLAN_TCI[13..15],pop:OXM_OF_PKT_REG0[32..34],pop:NXM_OF_VLAN_TCI[13..15], prereqs=vlan.tci[12]
reg0[10] <-> vlan.pcp[1]; => actions=push:NXM_OF_VLAN_TCI[14],push:OXM_OF_PKT_REG0[42],pop:NXM_OF_VLAN_TCI[14],pop:OXM_OF_PKT_REG0[42], prereqs=vlan.tci[12]
outport <-> inport; => actions=push:NXM_NX_REG6[],push:NXM_NX_REG7[],pop:NXM_NX_REG6[],pop:NXM_NX_REG7[], prereqs=1
ip.ttl--; => actions=dec_ttl, prereqs=ip
ip.ttl = 4; => actions=set_field:4->nw_ttl, prereqs=eth.type == 0x800 || eth.type == 0x86dd
# Contradictionary prerequisites (allowed but not useful):
ip4.src = ip6.src[0..31]; => actions=move:NXM_NX_IPV6_SRC[0..31]->NXM_OF_IP_SRC[], prereqs=eth.type == 0x800 && eth.type == 0x86dd
ip4.src <-> ip6.src[0..31]; => actions=push:NXM_NX_IPV6_SRC[0..31],push:NXM_OF_IP_SRC[],pop:NXM_NX_IPV6_SRC[0..31],pop:NXM_OF_IP_SRC[], prereqs=eth.type == 0x800 && eth.type == 0x86dd
## 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.
# "drop;" must be on its own:
drop; next; => Syntax error at `next' expecting end of input.
next; drop; => Syntax error at `drop' expecting action.
# Missing ";":
next => Syntax error at end of input expecting ';'.
next(); => Syntax error at `)' expecting small integer.
next(10; => Syntax error at `;' expecting `)'.
next(16); => "next" argument must be in range 0 to 15.
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 `='.
ip = 1; => Predicate symbol ip cannot be used in assignment.
ip.proto = 6; => Field ip.proto is not modifiable.
inport = {"a", "b"}; => Assignments require a single value.
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 cannot be used in assignment.
reg0[0] = vlan.present; => Predicate symbol vlan.present cannot be used in assignment.
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.
reg0[0] <-> vlan.present; => Predicate symbol vlan.present cannot be used in exchange.
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.
ip.ttl => Syntax error at end of input expecting `--'.
]])
sed 's/ =>.*//' test-cases.txt > input.txt
sed 's/.* => //' 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 lswitch-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 lport-add lsw0 lp$i$j
if test $j = 1; then
ovn-nbctl lport-set-addresses lp$i$j "f0:00:00:00:00:$i$j 192.168.0.$i$j" unknown
else
ovn-nbctl lport-set-addresses lp$i$j "f0:00:00:00:00:$i$j 192.168.0.$i$j"
ovn-nbctl lport-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
# 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
ovn-sbctl dump-flows -- list multicast_group
# 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 lport numbers, e.g. 11 for vif11.
trim_zeros() {
sed 's/\(00\)\{1,\}$//'
}
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 | trim_zeros >> $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 lport 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 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. When port security is turned on, the lswitch drops packets from the wrong
# MAC address.
#
# 4. The lswitch drops all packets with a VLAN tag.
#
# 5. The lswitch 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 lswitch delivers packets with an unknown destination to lports with
# "unknown" among their MAC addresses (and port security disabled).
#
# 7. The lswitch drops unicast packets that violate an ACL.
#
# 8. The lswitch drops multicast and broadcast packets that violate an ACL.
#
# 9. ARP requests to known IPs are responded directly.
#
# 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
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 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`
test_arp $s f000000000$s $sip $tip f000000000$d #9
test_arp $s f000000000$s $sip $tip_unknown #10
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
# Allow some time for packet forwarding.
# XXX This can be improved.
sleep 1
# Now check the packets actually received against the ones expected.
for i in 1 2 3; do
for j in 1 2 3; do
file=hv$i/vif$i$j-tx.pcap
echo $file
$PYTHON "$top_srcdir/utilities/ovs-pcap.in" $file | trim_zeros > $i$j.packets
sort $i$j.expected > expout
AT_CHECK([sort $i$j.packets], [0], [expout])
echo
done
done
AT_CLEANUP
AT_SETUP([ovn -- 3 HVs, 1 VIFs/HV, 1 GW, 1 LS])
AT_SKIP_IF([test $HAVE_PYTHON = no])
ovn_start
# Configure the Northbound database
ovn-nbctl lswitch-add lsw0
ovn-nbctl lport-add lsw0 lp1
ovn-nbctl lport-set-addresses lp1 f0:00:00:00:00:01
ovn-nbctl lport-add lsw0 lp2
ovn-nbctl lport-set-addresses lp2 f0:00:00:00:00:02
ovn-nbctl lport-add lsw0 lp-vtep
ovn-nbctl lport-set-type lp-vtep vtep
ovn-nbctl lport-set-options lp-vtep vtep-physical-switch=br-vtep vtep-logical-switch=lsw0
ovn-nbctl lport-set-addresses lp-vtep unknown
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
sleep 1
vtep-ctl bind-ls br-vtep br-vtep_n2 0 lsw0
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
ovn-sbctl show
# 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.
trim_zeros() {
sed 's/\(00\)\{1,\}$//'
}
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 | trim_zeros >> $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
# Broadcast and multicast.
# xxx ovn-controller-vtep doesn't handle multicast traffic that is
# xxx sourced from the gateway properly.
#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
# Allow some time for packet forwarding.
# XXX This can be improved.
sleep 1
# Now check the packets actually received against the ones expected.
for i in 1 2 3; do
file=hv$i/vif$i-tx.pcap
echo $file
$PYTHON "$top_srcdir/utilities/ovs-pcap.in" $file | trim_zeros > $i.packets
sort $i.expected > expout
AT_CHECK([sort $i.packets], [0], [expout])
echo
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 lswitch-add ls$i
for j in 1 2 3; do
for k in 1 2 3; do
ovn-nbctl \
-- lport-add ls$i lp$i$j$k \
-- lport-set-addresses lp$i$j$k "f0:00:00:00:0$i:$j$k 192.168.$i$j.$k"
done
done
done
ovn-nbctl create Logical_Router name=lr0
for i in 1 2 3; do
for j in 1 2 3; do
lrp_uuid=`ovn-nbctl \
-- --id=@lrp create Logical_Router_Port name=lrp$i$j \
network=192.168.$i$j.254/24 mac='"00:00:00:00:ff:'$i$j'"' \
-- add Logical_Router lr0 ports @lrp \
-- lport-add ls$i lrp$i$j-attachment`
ovn-nbctl \
set Logical_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_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