[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