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 bin_utils 9 10TIMEOUT = 10 # Used for socket timeout and barrier timeout 11 12 13class 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 105def 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 113class 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 389def 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 397class 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 454def 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 462class 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 640class 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 748def 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