[ovs-dev] [PATCH v2 3/9] tests: Convert flowgen utility from Perl to Python.
Ben Pfaff
blp at ovn.org
Mon Nov 20 18:07:15 UTC 2017
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 <blp at 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
More information about the dev
mailing list