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