This patch adds some helpers to assist virt test to setup the bridge or macvtap based guest networking. Changes from v1: * Fixed undefined variable errors on the exception class definitions Changes from v2: * On RHEL5, the io operation TUNGETFEATURES = 0x800454cf may return an integer bigger than the max signed integer, triggering an OverflowError. Let's catch that exception and make vnet_hdr_probe() return False in case it happens. Signed-off-by: Jason Wang <jasowang@xxxxxxxxxx> Signed-off-by: Lucas Meneghel Rodrigues <lmr@xxxxxxxxxx> fixup --- client/virt/virt_utils.py | 212 +++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 212 insertions(+), 0 deletions(-) diff --git a/client/virt/virt_utils.py b/client/virt/virt_utils.py index 0f02f3d..0700509 100644 --- a/client/virt/virt_utils.py +++ b/client/virt/virt_utils.py @@ -6,6 +6,7 @@ KVM test utility functions. import time, string, random, socket, os, signal, re, logging, commands, cPickle import fcntl, shelve, ConfigParser, threading, sys, UserDict, inspect +import struct from autotest_lib.client.bin import utils, os_dep from autotest_lib.client.common_lib import error, logging_config import rss_client, aexpect @@ -15,6 +16,20 @@ try: except ImportError: KOJI_INSTALLED = False +# From include/linux/sockios.h +SIOCSIFHWADDR = 0x8924 +SIOCGIFHWADDR = 0x8927 +SIOCSIFFLAGS = 0x8914 +SIOCGIFINDEX = 0x8933 +SIOCBRADDIF = 0x89a2 +# From linux/include/linux/if_tun.h +TUNSETIFF = 0x400454ca +TUNGETIFF = 0x800454d2 +TUNGETFEATURES = 0x800454cf +IFF_UP = 0x1 +IFF_TAP = 0x0002 +IFF_NO_PI = 0x1000 +IFF_VNET_HDR = 0x4000 def _lock_file(filename): f = open(filename, "w") @@ -36,6 +51,80 @@ def is_vm(obj): return obj.__class__.__name__ == "VM" +class NetError(Exception): + pass + + +class TAPModuleError(NetError): + def __init__(self, devname): + NetError.__init__(self, devname) + self.devname = devname + + def __str__(self): + return "Can't open %s" % self.devname + +class TAPNotExistError(NetError): + def __init__(self, ifname): + NetError.__init__(self, ifname) + self.ifname = ifname + + def __str__(self): + return "Interface %s does not exist" % self.ifname + + +class TAPCreationError(NetError): + def __init__(self, ifname, details=None): + NetError.__init__(self, ifname, details) + self.ifname = ifname + self.details = details + + def __str__(self): + e_msg = "Cannot create TAP device %s" % self.ifname + if self.details is not None: + e_msg += ": %s" % self.details + return e_msg + + +class TAPBringUpError(NetError): + def __init__(self, ifname): + NetError.__init__(self, ifname) + self.ifname = ifname + + def __str__(self): + return "Cannot bring up TAP %s" % self.ifname + + +class BRAddIfError(NetError): + def __init__(self, ifname, brname, details): + NetError.__init__(self, ifname, brname, details) + self.ifname = ifname + self.brname = brname + self.details = details + + def __str__(self): + return ("Can not add if %s to bridge %s: %s" % + (self.ifname, self.brname, self.details)) + + +class HwAddrSetError(NetError): + def __init__(self, ifname, mac): + NetError.__init__(self, ifname, mac) + self.ifname = ifname + self.mac = mac + + def __str__(self): + return "Can not set mac %s to interface %s" % (self.mac, self.ifname) + + +class HwAddrGetError(NetError): + def __init__(self, ifname): + NetError.__init__(self, ifname) + self.ifname = ifname + + def __str__(self): + return "Can not get mac of interface %s" % self.ifname + + class Env(UserDict.IterableUserDict): """ A dict-like object containing global objects used by tests. @@ -2311,3 +2400,126 @@ def install_host_kernel(job, params): else: logging.info('Chose %s, using the current kernel for the host', install_type) + + +def if_nametoindex(ifname): + """ + Map an interface name into its corresponding index. + Returns 0 on error, as 0 is not a valid index + + @param ifname: interface name + """ + index = 0 + ctrl_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, 0) + ifr = struct.pack("16si", ifname, 0) + r = fcntl.ioctl(ctrl_sock, SIOCGIFINDEX, ifr) + index = struct.unpack("16si", r)[1] + ctrl_sock.close() + return index + + +def vnet_hdr_probe(tapfd): + """ + Check if the IFF_VNET_HDR is support by tun. + + @param tapfd: the file descriptor of /dev/net/tun + """ + u = struct.pack("I", 0) + try: + r = fcntl.ioctl(tapfd, TUNGETFEATURES, u) + except OverflowError: + return False + flags = struct.unpack("I", r)[0] + if flags & IFF_VNET_HDR: + return True + else: + return False + + +def open_tap(devname, ifname, vnet_hdr=True): + """ + Open a tap device and returns its file descriptor which is used by + fd=<fd> parameter of qemu-kvm. + + @param ifname: TAP interface name + @param vnet_hdr: Whether enable the vnet header + """ + try: + tapfd = os.open(devname, os.O_RDWR) + except OSError, e: + raise TAPModuleError(devname, "open", e) + flags = IFF_TAP | IFF_NO_PI + if vnet_hdr and vnet_hdr_probe(tapfd): + flags |= IFF_VNET_HDR + + ifr = struct.pack("16sh", ifname, flags) + try: + r = fcntl.ioctl(tapfd, TUNSETIFF, ifr) + except IOError, details: + raise TAPCreationError(ifname, details) + ifname = struct.unpack("16sh", r)[0].strip("\x00") + return tapfd + + +def add_to_bridge(ifname, brname): + """ + Add a TAP device to bridge + + @param ifname: Name of TAP device + @param brname: Name of the bridge + """ + ctrl_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, 0) + index = if_nametoindex(ifname) + if index == 0: + raise TAPNotExistError(ifname) + ifr = struct.pack("16si", brname, index) + try: + r = fcntl.ioctl(ctrl_sock, SIOCBRADDIF, ifr) + except IOError, details: + raise BRAddIfError(ifname, brname, details) + ctrl_sock.close() + + +def bring_up_ifname(ifname): + """ + Bring up an interface + + @param ifname: Name of the interface + """ + ctrl_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, 0) + ifr = struct.pack("16si", ifname, IFF_UP) + try: + fcntl.ioctl(ctrl_sock, SIOCSIFFLAGS, ifr) + except IOError: + raise TAPBringUpError(ifname) + ctrl_sock.close() + + +def if_set_macaddress(ifname, mac): + """ + Set the mac address for an interface + + @param ifname: Name of the interface + @mac: Mac address + """ + ctrl_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, 0) + + ifr = struct.pack("256s", ifname) + try: + mac_dev = fcntl.ioctl(ctrl_sock, SIOCGIFHWADDR, ifr)[18:24] + mac_dev = ":".join(["%02x" % ord(m) for m in mac_dev]) + except IOError, e: + raise HwAddrGetError(ifname) + + if mac_dev.lower() == mac.lower(): + return + + ifr = struct.pack("16sH14s", ifname, 1, + "".join([chr(int(m, 16)) for m in mac.split(":")])) + try: + fcntl.ioctl(ctrl_sock, SIOCSIFHWADDR, ifr) + except IOError, e: + logging.info(e) + raise HwAddrSetError(ifname, mac) + ctrl_sock.close() + -- 1.7.5.2 -- To unsubscribe from this list: send the line "unsubscribe kvm" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html