Perl is unfashionable and Python is more widely available and understood, so this commit converts one of the OVS uses of Perl into Python.
Signed-off-by: Ben Pfaff <b...@ovn.org> --- tests/automake.mk | 2 +- tests/flowgen.pl | 253 ------------------------------------------------------ tests/flowgen.py | 240 +++++++++++++++++++++++++++++++++++++++++++++++++++ tests/library.at | 2 +- 4 files changed, 242 insertions(+), 255 deletions(-) delete mode 100755 tests/flowgen.pl create mode 100755 tests/flowgen.py diff --git a/tests/automake.mk b/tests/automake.mk index 1ea08fef850d..7eed1064e82b 100644 --- a/tests/automake.mk +++ b/tests/automake.mk @@ -368,7 +368,6 @@ tests_ovstest_SOURCES += \ endif tests_ovstest_LDADD = lib/libopenvswitch.la ovn/lib/libovn.la -dist_check_SCRIPTS = tests/flowgen.pl noinst_PROGRAMS += tests/test-strtok_r tests_test_strtok_r_SOURCES = tests/test-strtok_r.c @@ -379,6 +378,7 @@ tests_test_type_props_SOURCES = tests/test-type-props.c # Python tests. CHECK_PYFILES = \ tests/appctl.py \ + tests/flowgen.py \ tests/ovsdb-monitor-sort.py \ tests/test-daemon.py \ tests/test-json.py \ diff --git a/tests/flowgen.pl b/tests/flowgen.pl deleted file mode 100755 index a0fc345e7251..000000000000 --- a/tests/flowgen.pl +++ /dev/null @@ -1,253 +0,0 @@ -#! /usr/bin/perl - -# Copyright (c) 2009, 2010, 2011, 2012, 2015 Nicira, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at: -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -use strict; -use warnings; - -open(FLOWS, ">&=3");# or die "failed to open fd 3 for writing: $!\n"; -open(PACKETS, ">&=4");# or die "failed to open fd 4 for writing: $!\n"; - -# Print pcap file header. -print PACKETS pack('NnnNNNN', - 0xa1b2c3d4, # magic number - 2, # major version - 4, # minor version - 0, # time zone offset - 0, # time stamp accuracy - 1518, # snaplen - 1); # Ethernet - -output(DL_HEADER => '802.2'); - -for my $dl_header (qw(802.2+SNAP Ethernet)) { - my %a = (DL_HEADER => $dl_header); - for my $dl_vlan (qw(none zero nonzero)) { - my %b = (%a, DL_VLAN => $dl_vlan); - - # Non-IP case. - output(%b, DL_TYPE => 'non-ip'); - - for my $ip_options (qw(no yes)) { - my %c = (%b, DL_TYPE => 'ip', IP_OPTIONS => $ip_options); - for my $ip_fragment (qw(no first middle last)) { - my %d = (%c, IP_FRAGMENT => $ip_fragment); - for my $tp_proto (qw(TCP TCP+options UDP ICMP other)) { - output(%d, TP_PROTO => $tp_proto); - } - } - } - } -} - -sub output { - my (%attrs) = @_; - - # Compose flow. - my (%flow); - $flow{DL_SRC} = "00:02:e3:0f:80:a4"; - $flow{DL_DST} = "00:1a:92:40:ac:05"; - $flow{NW_PROTO} = 0; - $flow{NW_TOS} = 0; - $flow{NW_SRC} = '0.0.0.0'; - $flow{NW_DST} = '0.0.0.0'; - $flow{TP_SRC} = 0; - $flow{TP_DST} = 0; - if (defined($attrs{DL_VLAN})) { - my (%vlan_map) = ('none' => 0xffff, - 'zero' => 0, - 'nonzero' => 0x0123); - $flow{DL_VLAN} = $vlan_map{$attrs{DL_VLAN}}; - } else { - $flow{DL_VLAN} = 0xffff; # OFP_VLAN_NONE - } - if ($attrs{DL_HEADER} eq '802.2') { - $flow{DL_TYPE} = 0x5ff; # OFP_DL_TYPE_NOT_ETH_TYPE - } elsif ($attrs{DL_TYPE} eq 'ip') { - $flow{DL_TYPE} = 0x0800; # ETH_TYPE_IP - $flow{NW_SRC} = '10.0.2.15'; - $flow{NW_DST} = '192.168.1.20'; - $flow{NW_TOS} = 44; - if ($attrs{TP_PROTO} eq 'other') { - $flow{NW_PROTO} = 42; - } elsif ($attrs{TP_PROTO} eq 'TCP' || - $attrs{TP_PROTO} eq 'TCP+options') { - $flow{NW_PROTO} = 6; # IPPROTO_TCP - $flow{TP_SRC} = 6667; - $flow{TP_DST} = 9998; - } elsif ($attrs{TP_PROTO} eq 'UDP') { - $flow{NW_PROTO} = 17; # IPPROTO_UDP - $flow{TP_SRC} = 1112; - $flow{TP_DST} = 2223; - } elsif ($attrs{TP_PROTO} eq 'ICMP') { - $flow{NW_PROTO} = 1; # IPPROTO_ICMP - $flow{TP_SRC} = 8; # echo request - $flow{TP_DST} = 0; # code - } else { - die; - } - if ($attrs{IP_FRAGMENT} ne 'no' && $attrs{IP_FRAGMENT} ne 'first') { - $flow{TP_SRC} = $flow{TP_DST} = 0; - } - } elsif ($attrs{DL_TYPE} eq 'non-ip') { - $flow{DL_TYPE} = 0x5678; - } else { - die; - } - - # Compose packet. - my $packet = ''; - my $wildcards = 1 << 5 | 1 << 6 | 1 << 7 | 32 << 8 | 32 << 14 | 1 << 21; - - $packet .= pack_ethaddr($flow{DL_DST}); - $packet .= pack_ethaddr($flow{DL_SRC}); - if ($flow{DL_VLAN} != 0xffff) { - $packet .= pack('nn', 0x8100, $flow{DL_VLAN}); - } - my $len_ofs = length($packet); - $packet .= pack('n', 0) if $attrs{DL_HEADER} =~ /^802.2/; - if ($attrs{DL_HEADER} eq '802.2') { - $packet .= pack('CCC', 0x42, 0x42, 0x03); # LLC for 802.1D STP. - } else { - if ($attrs{DL_HEADER} eq '802.2+SNAP') { - $packet .= pack('CCC', 0xaa, 0xaa, 0x03); # LLC for SNAP. - $packet .= pack('CCC', 0, 0, 0); # SNAP OUI. - } - $packet .= pack('n', $flow{DL_TYPE}); - if ($attrs{DL_TYPE} eq 'ip') { - my $ip = pack('CCnnnCCnNN', - (4 << 4) | 5, # version, hdrlen - $flow{NW_TOS}, # type of service - 0, # total length (filled in later) - 65432, # id - 0, # frag offset - 64, # ttl - $flow{NW_PROTO}, # protocol - 0, # checksum - 0x0a00020f, # source - 0xc0a80114); # dest - $wildcards &= ~( 1 << 5 | 63 << 8 | 63 << 14 | 1 << 21); - if ($attrs{IP_OPTIONS} eq 'yes') { - substr($ip, 0, 1) = pack('C', (4 << 4) | 8); - $ip .= pack('CCnnnCCCx', - 130, # type - 11, # length - 0x6bc5, # top secret - 0xabcd, - 0x1234, - 1, - 2, - 3); - } - - if ($attrs{IP_FRAGMENT} ne 'no') { - my (%frag_map) = ('first' => 0x2000, # more frags, ofs 0 - 'middle' => 0x2111, # more frags, ofs 0x888 - 'last' => 0x0222); # last frag, ofs 0x1110 - substr($ip, 6, 2) - = pack('n', $frag_map{$attrs{IP_FRAGMENT}}); - } - if ($attrs{IP_FRAGMENT} eq 'no' || $attrs{IP_FRAGMENT} eq 'first') { - if ($attrs{TP_PROTO} =~ '^TCP') { - my $tcp = pack('nnNNnnnn', - $flow{TP_SRC}, # source port - $flow{TP_DST}, # dest port - 87123455, # seqno - 712378912, # ackno - (5 << 12) | 0x02 | 0x10, # hdrlen, SYN, ACK - 5823, # window size - 18923, # checksum - 12893); # urgent pointer - if ($attrs{TP_PROTO} eq 'TCP+options') { - substr($tcp, 12, 2) = pack('n', (6 << 12) | 0x02 | 0x10); - $tcp .= pack('CCn', 2, 4, 1975); # MSS option - } - $tcp .= 'payload'; - $ip .= $tcp; - $wildcards &= ~(1 << 6 | 1 << 7); - } elsif ($attrs{TP_PROTO} eq 'UDP') { - my $len = 15; - my $udp = pack('nnnn', $flow{TP_SRC}, $flow{TP_DST}, $len, 0); - $udp .= chr($len) while length($udp) < $len; - $ip .= $udp; - $wildcards &= ~(1 << 6 | 1 << 7); - } elsif ($attrs{TP_PROTO} eq 'ICMP') { - $ip .= pack('CCnnn', - 8, # echo request - 0, # code - 0, # checksum - 736, # identifier - 931); # sequence number - $wildcards &= ~(1 << 6 | 1 << 7); - } elsif ($attrs{TP_PROTO} eq 'other') { - $ip .= 'other header'; - } else { - die; - } - } - substr($ip, 2, 2) = pack('n', length($ip)); - $packet .= $ip; - } - } - if ($attrs{DL_HEADER} =~ /^802.2/) { - my $len = length ($packet); - $len -= 4 if $flow{DL_VLAN} != 0xffff; - substr($packet, $len_ofs, 2) = pack('n', $len); - } - - print join(' ', map("$_=$attrs{$_}", keys(%attrs))), "\n"; - print join(' ', map("$_=$flow{$_}", keys(%flow))), "\n"; - print "\n"; - - print FLOWS pack('Nn', - $wildcards, # wildcards - 1); # in_port - print FLOWS pack_ethaddr($flow{DL_SRC}); - print FLOWS pack_ethaddr($flow{DL_DST}); - print FLOWS pack('nCxnCCxxNNnn', - $flow{DL_VLAN}, - 0, # DL_VLAN_PCP - $flow{DL_TYPE}, - $flow{NW_TOS}, - $flow{NW_PROTO}, - inet_aton($flow{NW_SRC}), - inet_aton($flow{NW_DST}), - $flow{TP_SRC}, - $flow{TP_DST}); - - print PACKETS pack('NNNN', - 0, # timestamp seconds - 0, # timestamp microseconds - length($packet), # bytes saved - length($packet)), # total length - $packet; -} - -sub pack_ethaddr { - local ($_) = @_; - my $xx = '([0-9a-fA-F][0-9a-fA-F])'; - my (@octets) = /$xx:$xx:$xx:$xx:$xx:$xx/; - @octets == 6 or die $_; - my ($out) = ''; - $out .= pack('C', hex($_)) foreach @octets; - return $out; -} - -sub inet_aton { - local ($_) = @_; - my ($a, $b, $c, $d) = /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/; - defined $d or die $_; - return ($a << 24) | ($b << 16) | ($c << 8) | $d; -} diff --git a/tests/flowgen.py b/tests/flowgen.py new file mode 100755 index 000000000000..221a8f2bccc4 --- /dev/null +++ b/tests/flowgen.py @@ -0,0 +1,240 @@ +#! /usr/bin/env python + +# Copyright (c) 2009, 2010, 2011, 2012, 2015, 2017 Nicira, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os +import socket +import struct + + +def pack_ethaddr(ea): + octets = ea.split(':') + assert len(octets) == 6 + return b''.join([struct.pack('B', int(octet, 16)) for octet in octets]) + + +def output(attrs): + # Compose flow. + + flow = {} + flow['DL_SRC'] = "00:02:e3:0f:80:a4" + flow['DL_DST'] = "00:1a:92:40:ac:05" + flow['NW_PROTO'] = 0 + flow['NW_TOS'] = 0 + flow['NW_SRC'] = '0.0.0.0' + flow['NW_DST'] = '0.0.0.0' + flow['TP_SRC'] = 0 + flow['TP_DST'] = 0 + if 'DL_VLAN' in attrs: + flow['DL_VLAN'] = {'none': 0xffff, + 'zero': 0, + 'nonzero': 0x0123}[attrs['DL_VLAN']] + else: + flow['DL_VLAN'] = 0xffff # OFP_VLAN_NONE + if attrs['DL_HEADER'] == '802.2': + flow['DL_TYPE'] = 0x5ff # OFP_DL_TYPE_NOT_ETH_TYPE + elif attrs['DL_TYPE'] == 'ip': + flow['DL_TYPE'] = 0x0800 # ETH_TYPE_IP + flow['NW_SRC'] = '10.0.2.15' + flow['NW_DST'] = '192.168.1.20' + flow['NW_TOS'] = 44 + if attrs['TP_PROTO'] == 'other': + flow['NW_PROTO'] = 42 + elif attrs['TP_PROTO'] in ('TCP', 'TCP+options'): + flow['NW_PROTO'] = 6 # IPPROTO_TCP + flow['TP_SRC'] = 6667 + flow['TP_DST'] = 9998 + elif attrs['TP_PROTO'] == 'UDP': + flow['NW_PROTO'] = 17 # IPPROTO_UDP + flow['TP_SRC'] = 1112 + flow['TP_DST'] = 2223 + elif attrs['TP_PROTO'] == 'ICMP': + flow['NW_PROTO'] = 1 # IPPROTO_ICMP + flow['TP_SRC'] = 8 # echo request + flow['TP_DST'] = 0 # code + else: + assert False + if attrs['IP_FRAGMENT'] not in ('no', 'first'): + flow['TP_SRC'] = flow['TP_DST'] = 0 + elif attrs['DL_TYPE'] == 'non-ip': + flow['DL_TYPE'] = 0x5678 + else: + assert False + + # Compose packet + packet = b'' + wildcards = 1 << 5 | 1 << 6 | 1 << 7 | 32 << 8 | 32 << 14 | 1 << 21 + + packet += pack_ethaddr(flow['DL_DST']) + packet += pack_ethaddr(flow['DL_SRC']) + if flow['DL_VLAN'] != 0xffff: + packet += struct.pack('>HH', 0x8100, flow['DL_VLAN']) + len_ofs = len(packet) + if attrs['DL_HEADER'].startswith('802.2'): + packet += struct.pack('>H', 0) + if attrs['DL_HEADER'] == '802.2': + packet += struct.pack('BBB', 0x42, 0x42, 0x03) # LLC for 802.1D STP + else: + if attrs['DL_HEADER'] == '802.2+SNAP': + packet += struct.pack('BBB', 0xaa, 0xaa, 0x03) # LLC for SNAP + packet += struct.pack('BBB', 0, 0, 0) # SNAP OUI + packet += struct.pack('>H', flow['DL_TYPE']) + if attrs['DL_TYPE'] == 'ip': + ip = struct.pack('>BBHHHBBHLL', + (4 << 4) | 5, # version, hdrlen + flow['NW_TOS'], # type of service + 0, # total length, filled in later + 65432, # id + 0, # frag offset + 64, # ttl + flow['NW_PROTO'], # protocol + 0, # checksum + 0x0a00020f, # source + 0xc0a80114) # dest + wildcards &= ~(1 << 5 | 63 << 8 | 63 << 14 | 1 << 21) + if attrs['IP_OPTIONS'] == 'yes': + ip = struct.pack('B', (4 << 4) | 8) + ip[1:] + ip += struct.pack('>BBHHHBBBx', + 130, # type + 11, # length + 0x6bc5, # top secret + 0xabcd, + 0x1234, + 1, + 2, + 3) + if attrs['IP_FRAGMENT'] != 'no': + frag_map = {'first': 0x2000, # more frags, ofs 0 + 'middle': 0x2111, # more frags, ofs 0x888 + 'last': 0x0222} # last frag, ofs 0x1110 + ip = (ip[:6] + + struct.pack('>H', frag_map[attrs['IP_FRAGMENT']]) + + ip[8:]) + if attrs['IP_FRAGMENT'] in ('no', 'first'): + if attrs['TP_PROTO'].startswith('TCP'): + tcp = struct.pack('>HHLLHHHH', + flow['TP_SRC'], # source port + flow['TP_DST'], # dest port + 87123455, # seqno + 712378912, # ackno + (5 << 12) | 0x02 | 0x10, + # hdrlen, SYN, ACK + 5823, # window size + 18923, # checksum + 12893) # urgent pointer + if attrs['TP_PROTO'] == 'TCP+options': + tcp = (tcp[:12] + + struct.pack('H', (6 << 12) | 0x02 | 0x10) + + tcp[14:]) + tcp += struct.pack('>BBH', 2, 4, 1975) # MSS option + tcp += b'payload' + ip += tcp + wildcards &= ~(1 << 6 | 1 << 7) + elif attrs['TP_PROTO'] == 'UDP': + udp_len = 15 + udp = struct.pack('>HHHH', + flow['TP_SRC'], + flow['TP_DST'], + udp_len, 0) + while len(udp) < udp_len: + udp += struct.pack('B', udp_len) + ip += udp + wildcards &= ~(1 << 6 | 1 << 7) + elif attrs['TP_PROTO'] == 'ICMP': + ip += struct.pack('>BBHHH', + 8, # echo request + 0, # code + 0, # checksum + 736, # identifier + 931) # sequence number + wildcards &= ~(1 << 6 | 1 << 7) + elif attrs['TP_PROTO'] == 'other': + ip += b'other header' + else: + assert False + ip = ip[:2] + struct.pack('>H', len(ip)) + ip[4:] + packet += ip + if attrs['DL_HEADER'].startswith('802.2'): + packet_len = len(packet) + if flow['DL_VLAN'] != 0xffff: + packet_len -= 4 + packet = (packet[:len_ofs] + + struct.pack('>H', packet_len) + + packet[len_ofs + 2:]) + + print(' '.join(['%s=%s' for k, v in attrs.items()])) + print(' '.join(['%s=%s' for k, v in flow.items()])) + print() + + flows.write(struct.pack('>LH', + wildcards, # wildcards + 1)) # in_port + flows.write(pack_ethaddr(flow['DL_SRC'])) + flows.write(pack_ethaddr(flow['DL_DST'])) + flows.write(struct.pack('>HBxHBBxx', + flow['DL_VLAN'], + 0, # DL_VLAN_PCP + flow['DL_TYPE'], + flow['NW_TOS'], + flow['NW_PROTO'])) + flows.write(socket.inet_aton(flow['NW_SRC'])) + flows.write(socket.inet_aton(flow['NW_DST'])) + flows.write(struct.pack('>HH', flow['TP_SRC'], flow['TP_DST'])) + + packets.write(struct.pack('>LLLL', + 0, # timestamp seconds + 0, # timestamp microseconds + len(packet), # bytes saved + len(packet))) # total length + packets.write(packet) + + +flows = os.fdopen(3, 'wb') +packets = os.fdopen(4, 'wb') + +# Print pcap file header. +packets.write(struct.pack('>LHHLLLL', + 0xa1b2c3d4, # magic number + 2, # major version + 4, # minor version + 0, # time zone offset + 0, # time stamp accuracy + 1518, # snaplen + 1)) # Ethernet + +output({'DL_HEADER': '802.2'}) + +for dl_header in ('802.2+SNAP', 'Ethernet'): + a = {'DL_HEADER': dl_header} + for dl_vlan in ('none', 'zero', 'nonzero'): + b = a.copy() + b['DL_VLAN'] = dl_vlan + + # Non-IP case. + c = b.copy() + c['DL_TYPE'] = 'non-ip' + output(c) + + for ip_options in ('no', 'yes'): + c = b.copy() + c['DL_TYPE'] = 'ip' + c['IP_OPTIONS'] = ip_options + for ip_fragment in ('no', 'first', 'middle', 'last'): + d = c.copy() + d['IP_FRAGMENT'] = ip_fragment + for tp_proto in ('TCP', 'TCP+options', 'UDP', 'ICMP', 'other'): + e = d.copy() + e['TP_PROTO'] = tp_proto + output(e) diff --git a/tests/library.at b/tests/library.at index 6073abfbd6e7..5efbfbb7c02a 100644 --- a/tests/library.at +++ b/tests/library.at @@ -1,7 +1,7 @@ AT_BANNER([library unit tests]) AT_SETUP([flow extractor]) -AT_CHECK([$PERL `which flowgen.pl` >/dev/null 3>flows 4>pcap]) +AT_CHECK([$PYTHON $srcdir/flowgen.py >/dev/null 3>flows 4>pcap]) AT_CHECK([ovstest test-flows flows pcap], [0], [checked 247 packets, 0 errors ]) AT_CLEANUP -- 2.10.2 _______________________________________________ dev mailing list d...@openvswitch.org https://mail.openvswitch.org/mailman/listinfo/ovs-dev