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