Home | History | Annotate | Download | only in net
      1 """Convenience functions for use by network tests or whomever.
      2 
      3 This library is to release in the public repository.
      4 """
      5 
      6 import commands, os, re, socket, sys, time, struct
      7 from autotest_lib.client.common_lib import error
      8 from autotest_lib.client.bin import utils as bin_utils
      9 
     10 TIMEOUT = 10 # Used for socket timeout and barrier timeout
     11 
     12 
     13 class network_utils(object):
     14     def reset(self, ignore_status=False):
     15         bin_utils.system('service network restart', ignore_status=ignore_status)
     16 
     17 
     18     def start(self, ignore_status=False):
     19         bin_utils.system('service network start', ignore_status=ignore_status)
     20 
     21 
     22     def stop(self, ignore_status=False):
     23         bin_utils.system('service network stop', ignore_status=ignore_status)
     24 
     25 
     26     def list(self):
     27         bin_utils.system('ifconfig -a')
     28 
     29 
     30     def get_ip_local(self, query_ip, netmask="24"):
     31         """
     32         Get ip address in local system which can communicate with query_ip.
     33 
     34         @param query_ip: IP of client which wants to communicate with
     35                 autotest machine.
     36         @return: IP address which can communicate with query_ip
     37         """
     38         ip = bin_utils.system_output("ip addr show to %s/%s" %
     39                                         (query_ip, netmask))
     40         ip = re.search(r"inet ([0-9.]*)/",ip)
     41         if ip is None:
     42             return ip
     43         return ip.group(1)
     44 
     45 
     46     def disable_ip_local_loopback(self, ignore_status=False):
     47         bin_utils.system(
     48                 "echo '1' > /proc/sys/net/ipv4/route/no_local_loopback",
     49                 ignore_status=ignore_status)
     50         bin_utils.system('echo 1 > /proc/sys/net/ipv4/route/flush',
     51                      ignore_status=ignore_status)
     52 
     53 
     54     def enable_ip_local_loopback(self, ignore_status=False):
     55         bin_utils.system(
     56                 "echo '0' > /proc/sys/net/ipv4/route/no_local_loopback",
     57                 ignore_status=ignore_status)
     58         bin_utils.system('echo 1 > /proc/sys/net/ipv4/route/flush',
     59                      ignore_status=ignore_status)
     60 
     61 
     62     def process_mpstat(self, mpstat_out, sample_count, loud = True):
     63         """Parses mpstat output of the following two forms:
     64         02:10:17     0    0.00    0.00    0.00    0.00    0.00    0.00   \
     65         0.00  100.00   1012.87
     66         02:10:13 PM    0    0.00    0.00    0.00    0.00    0.00    0.00 \
     67         0.00  100.00   1019.00
     68         """
     69         mpstat_keys = ['time', 'CPU', 'user', 'nice', 'sys', 'iowait', 'irq',
     70                        'soft', 'steal', 'idle', 'intr/s']
     71         if loud:
     72             print mpstat_out
     73 
     74         # Remove the optional AM/PM appearing in time format
     75         mpstat_out = mpstat_out.replace('AM', '')
     76         mpstat_out = mpstat_out.replace('PM', '')
     77 
     78         regex = re.compile('(\S+)')
     79         stats = []
     80         for line in mpstat_out.splitlines()[3:]:
     81             match = regex.findall(line)
     82             # Skip the "Average" computed by mpstat. We are gonna compute the
     83             # average ourself.  Pick only the aggregate 'all' CPU results
     84             if match and match[0] != 'Average:' and match[1] == 'all':
     85                 stats.append(dict(zip(mpstat_keys, match)))
     86 
     87         if sample_count >= 5:
     88             # Throw away first and last sample
     89             stats = stats[1:-1]
     90 
     91         cpu_stats = {}
     92         for key in ['user', 'nice', 'sys', 'iowait', 'irq', 'soft', 'steal',
     93                     'idle', 'intr/s']:
     94             x = [float(row[key]) for row in stats]
     95             if len(x):
     96                 count = len(x)
     97             else:
     98                 print 'net_utils.network_utils.process_mpstat: count is 0!!!\n'
     99                 count = 1
    100             cpu_stats[key] = sum(x) / count
    101 
    102         return cpu_stats
    103 
    104 
    105 def network():
    106     try:
    107         from autotest_lib.client.bin.net import site_net_utils
    108         return site_net_utils.network_utils()
    109     except:
    110         return network_utils()
    111 
    112 
    113 class network_interface(object):
    114 
    115     ENABLE, DISABLE = (True, False)
    116 
    117     def __init__(self, name):
    118         autodir = os.environ['AUTODIR']
    119         self.ethtool = 'ethtool'
    120         self._name = name
    121         self.was_down = self.is_down()
    122         self.orig_ipaddr = self.get_ipaddr()
    123         self.was_loopback_enabled = self.is_loopback_enabled()
    124         self._socket = socket.socket(socket.PF_PACKET, socket.SOCK_RAW)
    125         self._socket.settimeout(TIMEOUT)
    126         self._socket.bind((name, raw_socket.ETH_P_ALL))
    127 
    128 
    129     def restore(self):
    130         self.set_ipaddr(self.orig_ipaddr)
    131         # TODO (msb): The additional conditional guard needs cleanup:
    132         #             Underlying driver should simply perform a noop
    133         #             for disabling loopback on an already-disabled device,
    134         #             instead of returning non-zero exit code.
    135 
    136         # To avoid sending a RST to the autoserv ssh connection
    137         # don't disable loopback until the IP address is restored.
    138         if not self.was_loopback_enabled and self.is_loopback_enabled():
    139             self.disable_loopback()
    140         if self.was_down:
    141             self.down()
    142 
    143 
    144     def get_name(self):
    145         return self._name
    146 
    147 
    148     def parse_ethtool(self, field, match, option='', next_field=''):
    149         output = bin_utils.system_output('%s %s %s' % (self.ethtool,
    150                                                    option, self._name))
    151         if output:
    152             match = re.search('\n\s*%s:\s*(%s)%s' %
    153                               (field, match, next_field), output, re.S)
    154             if match:
    155                 return match.group(1)
    156 
    157         return ''
    158 
    159 
    160     def get_stats(self):
    161         stats = {}
    162         stats_path = '/sys/class/net/%s/statistics/' % self._name
    163         for stat in os.listdir(stats_path):
    164             f = open(stats_path + stat, 'r')
    165             if f:
    166                 stats[stat] = int(f.read())
    167                 f.close()
    168         return stats
    169 
    170 
    171     def get_stats_diff(self, orig_stats):
    172         stats = self.get_stats()
    173         for stat in stats.keys():
    174             if stat in orig_stats:
    175                 stats[stat] = stats[stat] - orig_stats[stat]
    176             else:
    177                 stats[stat] = stats[stat]
    178         return stats
    179 
    180 
    181     def get_driver(self):
    182         driver_path = os.readlink('/sys/class/net/%s/device/driver' %
    183                                   self._name)
    184         return os.path.basename(driver_path)
    185 
    186 
    187     def get_carrier(self):
    188         f = open('/sys/class/net/%s/carrier' % self._name)
    189         if not f:
    190             return ''
    191         carrier = f.read().strip()
    192         f.close()
    193         return carrier
    194 
    195 
    196     def get_supported_link_modes(self):
    197         result = self.parse_ethtool('Supported link modes', '.*',
    198                                     next_field='Supports auto-negotiation')
    199         return result.split()
    200 
    201 
    202     def get_advertised_link_modes(self):
    203         result = self.parse_ethtool('Advertised link modes', '.*',
    204                                     next_field='Advertised auto-negotiation')
    205         return result.split()
    206 
    207 
    208     def is_autoneg_advertised(self):
    209         result = self.parse_ethtool('Advertised auto-negotiation',
    210                                         'Yes|No')
    211         return result == 'Yes'
    212 
    213 
    214     def get_speed(self):
    215         return int(self.parse_ethtool('Speed', '\d+'))
    216 
    217 
    218     def is_full_duplex(self):
    219         result = self.parse_ethtool('Duplex', 'Full|Half')
    220         return result == 'Full'
    221 
    222 
    223     def is_autoneg_on(self):
    224         result = self.parse_ethtool('Auto-negotiation', 'on|off')
    225         return result == 'on'
    226 
    227 
    228     def get_wakeon(self):
    229         return self.parse_ethtool('Wake-on', '\w+')
    230 
    231 
    232     def is_rx_summing_on(self):
    233         result = self.parse_ethtool('rx-checksumming', 'on|off', '-k')
    234         return result == 'on'
    235 
    236 
    237     def is_tx_summing_on(self):
    238         result = self.parse_ethtool('tx-checksumming', 'on|off', '-k')
    239         return result == 'on'
    240 
    241 
    242     def is_scatter_gather_on(self):
    243         result = self.parse_ethtool('scatter-gather', 'on|off', '-k')
    244         return result == 'on'
    245 
    246 
    247     def is_tso_on(self):
    248         result = self.parse_ethtool('tcp segmentation offload',
    249                                     'on|off', '-k')
    250         return result == 'on'
    251 
    252 
    253     def is_pause_autoneg_on(self):
    254         result = self.parse_ethtool('Autonegotiate', 'on|off', '-a')
    255         return result == 'on'
    256 
    257 
    258     def is_tx_pause_on(self):
    259         result = self.parse_ethtool('TX', 'on|off', '-a')
    260         return result == 'on'
    261 
    262 
    263     def is_rx_pause_on(self):
    264         result = self.parse_ethtool('RX', 'on|off', '-a')
    265         return result == 'on'
    266 
    267 
    268     def _set_loopback(self, mode, enable_disable):
    269         return bin_utils.system('%s -L %s %s %s' %
    270                       (self.ethtool, self._name, mode, enable_disable),
    271                       ignore_status=True)
    272 
    273 
    274     def enable_loopback(self):
    275         # If bonded do not set loopback mode.
    276         # Try mac loopback first then phy loopback
    277         # If both fail, raise an error
    278         if bond().is_enabled():
    279             raise error.TestError('Unable to enable loopback while '
    280                                   'bonding is enabled.')
    281         if (self._set_loopback('phyint', 'enable') > 0 and
    282             self._set_loopback('mac', 'enable') > 0):
    283             raise error.TestError('Unable to enable loopback')
    284         # Add a 1 second wait for drivers which do not have
    285         # a synchronous loopback enable
    286         # TODO (msb); Remove this wait once the drivers are fixed
    287         if self.get_driver() in ['tg3', 'bnx2x']:
    288             time.sleep(1)
    289         self.wait_for_carrier(timeout=30)
    290 
    291 
    292     def disable_loopback(self):
    293         # Try mac loopback first then phy loopback
    294         # If both fail, raise an error
    295         if (self._set_loopback('phyint', 'disable') > 0 and
    296             self._set_loopback('mac', 'disable') > 0):
    297             raise error.TestError('Unable to disable loopback')
    298 
    299 
    300     def is_loopback_enabled(self):
    301         # Don't try ethtool -l on a bonded host
    302         if bond().is_enabled():
    303             return False
    304         output = bin_utils.system_output('%s -l %s'
    305                                          % (self.ethtool, self._name))
    306         if output:
    307             return 'enabled' in output
    308         return False
    309 
    310 
    311     def enable_promisc(self):
    312         bin_utils.system('ifconfig %s promisc' % self._name)
    313 
    314 
    315     def disable_promisc(self):
    316         bin_utils.system('ifconfig %s -promisc' % self._name)
    317 
    318 
    319     def get_hwaddr(self):
    320         f = open('/sys/class/net/%s/address' % self._name)
    321         hwaddr = f.read().strip()
    322         f.close()
    323         return hwaddr
    324 
    325 
    326     def set_hwaddr(self, hwaddr):
    327         bin_utils.system('ifconfig %s hw ether %s' % (self._name, hwaddr))
    328 
    329 
    330     def add_maddr(self, maddr):
    331         bin_utils.system('ip maddr add %s dev %s' % (maddr, self._name))
    332 
    333 
    334     def del_maddr(self, maddr):
    335         bin_utils.system('ip maddr del %s dev %s' % (maddr, self._name))
    336 
    337 
    338     def get_ipaddr(self):
    339         ipaddr = "0.0.0.0"
    340         output = bin_utils.system_output('ifconfig %s' % self._name)
    341         if output:
    342             match = re.search("inet addr:([\d\.]+)", output)
    343             if match:
    344                 ipaddr = match.group(1)
    345         return ipaddr
    346 
    347 
    348     def set_ipaddr(self, ipaddr):
    349         bin_utils.system('ifconfig %s %s' % (self._name, ipaddr))
    350 
    351 
    352     def is_down(self):
    353         output = bin_utils.system_output('ifconfig %s' % self._name)
    354         if output:
    355             return 'UP' not in output
    356         return False
    357 
    358     def up(self):
    359         bin_utils.system('ifconfig %s up' % self._name)
    360 
    361 
    362     def down(self):
    363         bin_utils.system('ifconfig %s down' % self._name)
    364 
    365 
    366     def wait_for_carrier(self, timeout=60):
    367         while timeout and self.get_carrier() != '1':
    368             timeout -= 1
    369             time.sleep(1)
    370         if timeout == 0:
    371             raise error.TestError('Timed out waiting for carrier.')
    372 
    373 
    374     def send(self, buf):
    375         self._socket.send(buf)
    376 
    377 
    378     def recv(self, len):
    379         return self._socket.recv(len)
    380 
    381 
    382     def flush(self):
    383         self._socket.close()
    384         self._socket = socket.socket(socket.PF_PACKET, socket.SOCK_RAW)
    385         self._socket.settimeout(TIMEOUT)
    386         self._socket.bind((self._name, raw_socket.ETH_P_ALL))
    387 
    388 
    389 def netif(name):
    390     try:
    391         from autotest_lib.client.bin.net import site_net_utils
    392         return site_net_utils.network_interface(name)
    393     except:
    394         return network_interface(name)
    395 
    396 
    397 class bonding(object):
    398     """This class implements bonding interface abstraction."""
    399 
    400     NO_MODE = 0
    401     AB_MODE = 1
    402     AD_MODE = 2
    403 
    404     def is_enabled(self):
    405         raise error.TestError('Undefined')
    406 
    407 
    408     def is_bondable(self):
    409         raise error.TestError('Undefined')
    410 
    411 
    412     def enable(self):
    413         raise error.TestError('Undefined')
    414 
    415 
    416     def disable(self):
    417         raise error.TestError('Undefined')
    418 
    419 
    420     def get_mii_status(self):
    421         return {}
    422 
    423 
    424     def get_mode(self):
    425         return bonding.NO_MODE
    426 
    427 
    428     def wait_for_state_change(self):
    429         """Wait for bonding state change.
    430 
    431         Wait up to 90 seconds to successfully ping the gateway.
    432         This is to know when LACP state change has converged.
    433         (0 seconds is 3x lacp timeout, use by protocol)
    434         """
    435 
    436         netif('eth0').wait_for_carrier(timeout=60)
    437         wait_time = 0
    438         while wait_time < 100:
    439             time.sleep(10)
    440             if not bin_utils.ping_default_gateway():
    441                 return True
    442             wait_time += 10
    443         return False
    444 
    445 
    446     def get_active_interfaces(self):
    447         return []
    448 
    449 
    450     def get_slave_interfaces(self):
    451         return []
    452 
    453 
    454 def bond():
    455     try:
    456         from autotest_lib.client.bin.net import site_net_utils
    457         return site_net_utils.bonding()
    458     except:
    459         return bonding()
    460 
    461 
    462 class raw_socket(object):
    463     """This class implements an raw socket abstraction."""
    464     ETH_P_ALL = 0x0003 # Use for binding a RAW Socket to all protocols
    465     SOCKET_TIMEOUT = 1
    466     def __init__(self, iface_name):
    467         """Initialize an interface for use.
    468 
    469         Args:
    470           iface_name: 'eth0'  interface name ('eth0, eth1,...')
    471         """
    472         self._name = iface_name
    473         self._socket = None
    474         self._socket_timeout = raw_socket.SOCKET_TIMEOUT
    475         socket.setdefaulttimeout(self._socket_timeout)
    476         if self._name is None:
    477             raise error.TestError('Invalid interface name')
    478 
    479 
    480     def socket(self):
    481         return self._socket
    482 
    483 
    484     def socket_timeout(self):
    485         """Get the timeout use by recv_from"""
    486         return self._socket_timeout
    487 
    488 
    489     def set_socket_timeout(self, timeout):
    490         """Set the timeout use by recv_from.
    491 
    492         Args:
    493           timeout: time in seconds
    494         """
    495         self._socket_timeout = timeout
    496 
    497     def open(self, protocol=None):
    498         """Opens the raw socket to send and receive.
    499 
    500         Args:
    501           protocol : short in host byte order. None if ALL
    502         """
    503         if self._socket is not None:
    504             raise error.TestError('Raw socket already open')
    505 
    506         if protocol is None:
    507             self._socket = socket.socket(socket.PF_PACKET,
    508                                          socket.SOCK_RAW)
    509 
    510             self._socket.bind((self._name, self.ETH_P_ALL))
    511         else:
    512             self._socket = socket.socket(socket.PF_PACKET,
    513                                          socket.SOCK_RAW,
    514                                          socket.htons(protocol))
    515             self._socket.bind((self._name, self.ETH_P_ALL))
    516 
    517         self._socket.settimeout(1) # always running with 1 second timeout
    518 
    519     def close(self):
    520         """ Close the raw socket"""
    521         if self._socket is not None:
    522             self._socket.close()
    523             self._socket = None
    524         else:
    525             raise error.TestError('Raw socket not open')
    526 
    527 
    528     def recv(self, timeout):
    529         """Synchroneous receive.
    530 
    531         Receives one packet from the interface and returns its content
    532         in a string. Wait up to timeout for the packet if timeout is
    533         not 0. This function filters out all the packets that are
    534         less than the minimum ethernet packet size (60+crc).
    535 
    536         Args:
    537           timeout: max time in seconds to wait for the read to complete.
    538                    '0', wait for ever until a valid packet is received
    539 
    540         Returns:
    541           packet:    None no packet was received
    542                      a binary string containing the received packet.
    543           time_left: amount of time left in timeout
    544         """
    545         if self._socket is None:
    546             raise error.TestError('Raw socket not open')
    547 
    548         time_left = timeout
    549         packet = None
    550         while time_left or (timeout == 0):
    551             try:
    552                 packet = self._socket.recv(ethernet.ETH_PACKET_MAX_SIZE)
    553                 if len(packet) >= (ethernet.ETH_PACKET_MIN_SIZE-4):
    554                     break
    555                 packet = None
    556                 if timeout and time_left:
    557                     time_left -= raw_socket.SOCKET_TIMEOUT
    558             except socket.timeout:
    559                 packet = None
    560                 if timeout and time_left:
    561                     time_left -= raw_socket.SOCKET_TIMEOUT
    562 
    563         return packet, time_left
    564 
    565 
    566     def send(self, packet):
    567         """Send an ethernet packet."""
    568         if self._socket is None:
    569             raise error.TestError('Raw socket not open')
    570 
    571         self._socket.send(packet)
    572 
    573 
    574     def send_to(self, dst_mac, src_mac, protocol, payload):
    575         """Send an ethernet frame.
    576 
    577         Send an ethernet frame, formating the header.
    578 
    579         Args:
    580           dst_mac: 'byte string'
    581           src_mac: 'byte string'
    582           protocol: short in host byte order
    583           payload: 'byte string'
    584         """
    585         if self._socket is None:
    586             raise error.TestError('Raw socket not open')
    587         try:
    588             packet = ethernet.pack(dst_mac, src_mac, protocol, payload)
    589         except:
    590             raise error.TestError('Invalid Packet')
    591         self.send(packet)
    592 
    593 
    594     def recv_from(self, dst_mac, src_mac, protocol):
    595         """Receive an ethernet frame that matches the dst, src and proto.
    596 
    597         Filters all received packet to find a matching one, then unpack
    598         it and present it to the caller as a frame.
    599 
    600         Waits up to self._socket_timeout for a matching frame before
    601         returning.
    602 
    603         Args:
    604           dst_mac: 'byte string'. None do not use in filter.
    605           src_mac: 'byte string'. None do not use in filter.
    606           protocol: short in host byte order. None do not use in filter.
    607 
    608         Returns:
    609           ethernet frame: { 'dst' : byte string,
    610                             'src' : byte string,
    611                             'proto' : short in host byte order,
    612                             'payload' : byte string
    613                           }
    614         """
    615         start_time = time.clock()
    616         timeout = self._socket_timeout
    617         while 1:
    618             frame = None
    619             packet, timeout = self.recv(timeout)
    620             if packet is not None:
    621                 frame = ethernet.unpack(packet)
    622                 if ((src_mac is None or frame['src'] == src_mac) and
    623                     (dst_mac is None or frame['dst'] == dst_mac) and
    624                     (protocol is None or frame['proto'] == protocol)):
    625                     break;
    626                 elif (timeout == 0 or
    627                       time.clock() - start_time > float(self._socket_timeout)):
    628                     frame = None
    629                     break
    630             else:
    631                 if (timeout == 0 or
    632                     time.clock() - start_time > float(self._socket_timeout)):
    633                     frame = None
    634                     break
    635                 continue
    636 
    637         return frame
    638 
    639 
    640 class ethernet(object):
    641     """Provide ethernet packet manipulation methods."""
    642     HDR_LEN = 14     # frame header length
    643     CHECKSUM_LEN = 4 # frame checksum length
    644 
    645     # Ethernet payload types - http://standards.ieee.org/regauth/ethertype
    646     ETH_TYPE_IP        = 0x0800 # IP protocol
    647     ETH_TYPE_ARP       = 0x0806 # address resolution protocol
    648     ETH_TYPE_CDP       = 0x2000 # Cisco Discovery Protocol
    649     ETH_TYPE_8021Q     = 0x8100 # IEEE 802.1Q VLAN tagging
    650     ETH_TYPE_IP6       = 0x86DD # IPv6 protocol
    651     ETH_TYPE_LOOPBACK  = 0x9000 # used to test interfaces
    652     ETH_TYPE_LLDP      = 0x88CC # LLDP frame type
    653 
    654     ETH_PACKET_MAX_SIZE = 1518  # maximum ethernet frane size
    655     ETH_PACKET_MIN_SIZE = 64    # minimum ethernet frane size
    656 
    657     ETH_LLDP_DST_MAC = '01:80:C2:00:00:0E' # LLDP destination mac
    658 
    659     FRAME_KEY_DST_MAC = 'dst' # frame destination mac address
    660     FRAME_KEY_SRC_MAC = 'src' # frame source mac address
    661     FRAME_KEY_PROTO = 'proto' # frame protocol
    662     FRAME_KEY_PAYLOAD = 'payload' # frame payload
    663 
    664 
    665     def __init__(self):
    666         pass;
    667 
    668 
    669     @staticmethod
    670     def mac_string_to_binary(hwaddr):
    671         """Converts a MAC address text string to byte string.
    672 
    673         Converts a MAC text string from a text string 'aa:aa:aa:aa:aa:aa'
    674         to a byte string 'xxxxxxxxxxxx'
    675 
    676         Args:
    677           hwaddr: a text string containing the MAC address to convert.
    678 
    679         Returns:
    680           A byte string.
    681         """
    682         val = ''.join([chr(b) for b in [int(c, 16) \
    683                                         for c in hwaddr.split(':',6)]])
    684         return val
    685 
    686 
    687     @staticmethod
    688     def mac_binary_to_string(hwaddr):
    689         """Converts a MAC address byte string to text string.
    690 
    691         Converts a MAC byte string 'xxxxxxxxxxxx' to a text string
    692         'aa:aa:aa:aa:aa:aa'
    693 
    694         Args:
    695           hwaddr: a byte string containing the MAC address to convert.
    696 
    697         Returns:
    698          A text string.
    699         """
    700         return "%02x:%02x:%02x:%02x:%02x:%02x" % tuple(map(ord,hwaddr))
    701 
    702 
    703     @staticmethod
    704     def pack(dst, src, protocol, payload):
    705         """Pack a frame in a byte string.
    706 
    707         Args:
    708           dst: destination mac in byte string format
    709           src: src mac address in byte string format
    710           protocol: short in network byte order
    711           payload: byte string payload data
    712 
    713         Returns:
    714           An ethernet frame with header and payload in a byte string.
    715         """
    716         # numbers are converted to network byte order (!)
    717         frame = struct.pack("!6s6sH", dst, src, protocol) + payload
    718         return frame
    719 
    720 
    721     @staticmethod
    722     def unpack(raw_frame):
    723         """Unpack a raw ethernet frame.
    724 
    725         Returns:
    726           None on error
    727             { 'dst' : byte string,
    728               'src' : byte string,
    729               'proto' : short in host byte order,
    730               'payload' : byte string
    731             }
    732         """
    733         packet_len = len(raw_frame)
    734         if packet_len < ethernet.HDR_LEN:
    735             return None
    736 
    737         payload_len = packet_len - ethernet.HDR_LEN
    738         frame = {}
    739         frame[ethernet.FRAME_KEY_DST_MAC], \
    740         frame[ethernet.FRAME_KEY_SRC_MAC], \
    741         frame[ethernet.FRAME_KEY_PROTO] = \
    742             struct.unpack("!6s6sH", raw_frame[:ethernet.HDR_LEN])
    743         frame[ethernet.FRAME_KEY_PAYLOAD] = \
    744             raw_frame[ethernet.HDR_LEN:ethernet.HDR_LEN+payload_len]
    745         return frame
    746 
    747 
    748 def ethernet_packet():
    749     try:
    750         from autotest_lib.client.bin.net import site_net_utils
    751         return site_net_utils.ethernet()
    752     except:
    753         return ethernet()
    754