lib: refactor match_format() output functions
[sliver-openvswitch.git] / tests / flowgen.pl
1 #! /usr/bin/perl
2
3 # Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
4 #
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at:
8 #
9 #     http://www.apache.org/licenses/LICENSE-2.0
10 #
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16
17 use strict;
18 use warnings;
19
20 open(FLOWS, ">&=3");# or die "failed to open fd 3 for writing: $!\n";
21 open(PACKETS, ">&=4");# or die "failed to open fd 4 for writing: $!\n";
22
23 # Print pcap file header.
24 print PACKETS pack('NnnNNNN',
25                    0xa1b2c3d4,  # magic number
26                    2,           # major version
27                    4,           # minor version
28                    0,           # time zone offset
29                    0,           # time stamp accuracy
30                    1518,        # snaplen
31                    1);          # Ethernet
32
33 output(DL_HEADER => '802.2');
34
35 for my $dl_header (qw(802.2+SNAP Ethernet)) {
36     my %a = (DL_HEADER => $dl_header);
37     for my $dl_vlan (qw(none zero nonzero)) {
38         my %b = (%a, DL_VLAN => $dl_vlan);
39
40         # Non-IP case.
41         output(%b, DL_TYPE => 'non-ip');
42
43         for my $ip_options (qw(no yes)) {
44             my %c = (%b, DL_TYPE => 'ip', IP_OPTIONS => $ip_options);
45             for my $ip_fragment (qw(no first middle last)) {
46                 my %d = (%c, IP_FRAGMENT => $ip_fragment);
47                 for my $tp_proto (qw(TCP TCP+options UDP ICMP other)) {
48                     output(%d, TP_PROTO => $tp_proto);
49                 }
50             }
51         }
52     }
53 }
54
55 sub output {
56     my (%attrs) = @_;
57
58     # Compose flow.
59     my (%flow);
60     $flow{DL_SRC} = "00:02:e3:0f:80:a4";
61     $flow{DL_DST} = "00:1a:92:40:ac:05";
62     $flow{NW_PROTO} = 0;
63     $flow{NW_TOS} = 0;
64     $flow{NW_SRC} = '0.0.0.0';
65     $flow{NW_DST} = '0.0.0.0';
66     $flow{TP_SRC} = 0;
67     $flow{TP_DST} = 0;
68     if (defined($attrs{DL_VLAN})) {
69         my (%vlan_map) = ('none' => 0xffff,
70                           'zero' => 0,
71                           'nonzero' => 0x0123);
72         $flow{DL_VLAN} = $vlan_map{$attrs{DL_VLAN}};
73     } else {
74         $flow{DL_VLAN} = 0xffff; # OFP_VLAN_NONE
75     }
76     if ($attrs{DL_HEADER} eq '802.2') {
77         $flow{DL_TYPE} = 0x5ff; # OFP_DL_TYPE_NOT_ETH_TYPE
78     } elsif ($attrs{DL_TYPE} eq 'ip') {
79         $flow{DL_TYPE} = 0x0800; # ETH_TYPE_IP
80         $flow{NW_SRC} = '10.0.2.15';
81         $flow{NW_DST} = '192.168.1.20';
82         $flow{NW_TOS} = 44;
83         if ($attrs{TP_PROTO} eq 'other') {
84             $flow{NW_PROTO} = 42;
85         } elsif ($attrs{TP_PROTO} eq 'TCP' ||
86                  $attrs{TP_PROTO} eq 'TCP+options') {
87             $flow{NW_PROTO} = 6; # IPPROTO_TCP
88             $flow{TP_SRC} = 6667;
89             $flow{TP_DST} = 9998;
90         } elsif ($attrs{TP_PROTO} eq 'UDP') {
91             $flow{NW_PROTO} = 17; # IPPROTO_UDP
92             $flow{TP_SRC} = 1112;
93             $flow{TP_DST} = 2223;
94         } elsif ($attrs{TP_PROTO} eq 'ICMP') {
95             $flow{NW_PROTO} = 1; # IPPROTO_ICMP
96             $flow{TP_SRC} = 8;   # echo request
97             $flow{TP_DST} = 0;   # code
98         } else {
99             die;
100         }
101         if ($attrs{IP_FRAGMENT} ne 'no' && $attrs{IP_FRAGMENT} ne 'first') {
102             $flow{TP_SRC} = $flow{TP_DST} = 0;
103         }
104     } elsif ($attrs{DL_TYPE} eq 'non-ip') {
105         $flow{DL_TYPE} = 0x5678;
106     } else {
107         die;
108     }
109
110     # Compose packet.
111     my $packet = '';
112     my $wildcards = 1 << 5 | 1 << 6 | 1 << 7 | 32 << 8 | 32 << 14 | 1 << 21;
113
114     $packet .= pack_ethaddr($flow{DL_DST});
115     $packet .= pack_ethaddr($flow{DL_SRC});
116     if ($flow{DL_VLAN} != 0xffff) {
117         $packet .= pack('nn', 0x8100, $flow{DL_VLAN});
118     } else {
119         $wildcards |= 1 << 20;   # OFPFW10_DL_VLAN_PCP
120     }
121     my $len_ofs = length($packet);
122     $packet .= pack('n', 0) if $attrs{DL_HEADER} =~ /^802.2/;
123     if ($attrs{DL_HEADER} eq '802.2') {
124         $packet .= pack('CCC', 0x42, 0x42, 0x03); # LLC for 802.1D STP.
125     } else {
126         if ($attrs{DL_HEADER} eq '802.2+SNAP') {
127             $packet .= pack('CCC', 0xaa, 0xaa, 0x03); # LLC for SNAP.
128             $packet .= pack('CCC', 0, 0, 0);          # SNAP OUI.
129         }
130         $packet .= pack('n', $flow{DL_TYPE});
131         if ($attrs{DL_TYPE} eq 'ip') {
132             my $ip = pack('CCnnnCCnNN',
133                           (4 << 4) | 5,    # version, hdrlen
134                           $flow{NW_TOS},   # type of service
135                           0,               # total length (filled in later)
136                           65432,           # id
137                           0,               # frag offset
138                           64,              # ttl
139                           $flow{NW_PROTO}, # protocol
140                           0,               # checksum
141                           0x0a00020f,      # source
142                           0xc0a80114);     # dest
143             $wildcards &= ~( 1 << 5 | 63 << 8 | 63 << 14 | 1 << 21);
144             if ($attrs{IP_OPTIONS} eq 'yes') {
145                 substr($ip, 0, 1) = pack('C', (4 << 4) | 8);
146                 $ip .= pack('CCnnnCCCx',
147                             130,       # type
148                             11,        # length
149                             0x6bc5,    # top secret
150                             0xabcd,
151                             0x1234,
152                             1,
153                             2,
154                             3);
155             }
156
157             if ($attrs{IP_FRAGMENT} ne 'no') {
158                 my (%frag_map) = ('first' => 0x2000, # more frags, ofs 0
159                                   'middle' => 0x2111, # more frags, ofs 0x888
160                                   'last' => 0x0222); # last frag, ofs 0x1110
161                 substr($ip, 6, 2)
162                   = pack('n', $frag_map{$attrs{IP_FRAGMENT}});
163             }
164             if ($attrs{IP_FRAGMENT} eq 'no' || $attrs{IP_FRAGMENT} eq 'first') {
165                 if ($attrs{TP_PROTO} =~ '^TCP') {
166                     my $tcp = pack('nnNNnnnn',
167                                    $flow{TP_SRC},     # source port
168                                    $flow{TP_DST},     # dest port
169                                    87123455,          # seqno
170                                    712378912,         # ackno
171                                    (5 << 12) | 0x02 | 0x10, # hdrlen, SYN, ACK
172                                    5823,                    # window size
173                                    18923,                   # checksum
174                                    12893); # urgent pointer
175                     if ($attrs{TP_PROTO} eq 'TCP+options') {
176                         substr($tcp, 12, 2) = pack('n', (6 << 12) | 0x02 | 0x10);
177                         $tcp .= pack('CCn', 2, 4, 1975); # MSS option
178                     }
179                     $tcp .= 'payload';
180                     $ip .= $tcp;
181                     $wildcards &= ~(1 << 6 | 1 << 7);
182                 } elsif ($attrs{TP_PROTO} eq 'UDP') {
183                     my $len = 15;
184                     my $udp = pack('nnnn', $flow{TP_SRC}, $flow{TP_DST}, $len, 0);
185                     $udp .= chr($len) while length($udp) < $len;
186                     $ip .= $udp;
187                     $wildcards &= ~(1 << 6 | 1 << 7);
188                 } elsif ($attrs{TP_PROTO} eq 'ICMP') {
189                     $ip .= pack('CCnnn',
190                                 8,        # echo request
191                                 0,        # code
192                                 0,        # checksum
193                                 736,      # identifier
194                                 931);     # sequence number
195                     $wildcards &= ~(1 << 6 | 1 << 7);
196                 } elsif ($attrs{TP_PROTO} eq 'other') {
197                     $ip .= 'other header';
198                 } else {
199                     die;
200                 }
201             }
202             substr($ip, 2, 2) = pack('n', length($ip));
203             $packet .= $ip;
204         }
205     }
206     if ($attrs{DL_HEADER} =~ /^802.2/) {
207         my $len = length ($packet);
208         $len -= 4 if $flow{DL_VLAN} != 0xffff;
209         substr($packet, $len_ofs, 2) = pack('n', $len);
210     }
211
212     print join(' ', map("$_=$attrs{$_}", keys(%attrs))), "\n";
213     print join(' ', map("$_=$flow{$_}", keys(%flow))), "\n";
214     print "\n";
215
216     print FLOWS pack('Nn',
217                      $wildcards, # wildcards
218                      1);         # in_port
219     print FLOWS pack_ethaddr($flow{DL_SRC});
220     print FLOWS pack_ethaddr($flow{DL_DST});
221     print FLOWS pack('nCxnCCxxNNnn',
222                      $flow{DL_VLAN},
223                      0,          # DL_VLAN_PCP
224                      $flow{DL_TYPE},
225                      $flow{NW_TOS},
226                      $flow{NW_PROTO},
227                      inet_aton($flow{NW_SRC}),
228                      inet_aton($flow{NW_DST}),
229                      $flow{TP_SRC},
230                      $flow{TP_DST});
231
232     print PACKETS pack('NNNN',
233                        0,                # timestamp seconds
234                        0,                # timestamp microseconds
235                        length($packet),  # bytes saved
236                        length($packet)), # total length
237                   $packet;
238 }
239
240 sub pack_ethaddr {
241     local ($_) = @_;
242     my $xx = '([0-9a-fA-F][0-9a-fA-F])';
243     my (@octets) = /$xx:$xx:$xx:$xx:$xx:$xx/;
244     @octets == 6 or die $_;
245     my ($out) = '';
246     $out .= pack('C', hex($_)) foreach @octets;
247     return $out;
248 }
249
250 sub inet_aton {
251     local ($_) = @_;
252     my ($a, $b, $c, $d) = /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/;
253     defined $d or die $_;
254     return ($a << 24) | ($b << 16) | ($c << 8) | $d;
255 }