From patchwork Thu Jun 2 04:23:06 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lucas Meneghel Rodrigues X-Patchwork-Id: 842662 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by demeter2.kernel.org (8.14.4/8.14.3) with ESMTP id p524VU7k006572 for ; Thu, 2 Jun 2011 04:31:31 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752000Ab1FBEXY (ORCPT ); Thu, 2 Jun 2011 00:23:24 -0400 Received: from mx1.redhat.com ([209.132.183.28]:31126 "EHLO mx1.redhat.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751847Ab1FBEXX (ORCPT ); Thu, 2 Jun 2011 00:23:23 -0400 Received: from int-mx02.intmail.prod.int.phx2.redhat.com (int-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.12]) by mx1.redhat.com (8.14.4/8.14.4) with ESMTP id p524NN7K006755 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK); Thu, 2 Jun 2011 00:23:23 -0400 Received: from justice.redhat.com (vpn-11-207.rdu.redhat.com [10.11.11.207]) by int-mx02.intmail.prod.int.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id p524NBpv014756; Thu, 2 Jun 2011 00:23:20 -0400 From: Lucas Meneghel Rodrigues To: autotest@test.kernel.org Cc: kvm@vger.kernel.org, Lucas Meneghel Rodrigues , Jason Wang Subject: [PATCH 2/5] KVM test: Add helpers to control the TAP/bridge Date: Thu, 2 Jun 2011 01:23:06 -0300 Message-Id: <1306988589-17085-3-git-send-email-lmr@redhat.com> In-Reply-To: <1306988589-17085-1-git-send-email-lmr@redhat.com> References: <1306988589-17085-1-git-send-email-lmr@redhat.com> X-Scanned-By: MIMEDefang 2.67 on 10.5.11.12 Sender: kvm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org X-Greylist: IP, sender and recipient auto-whitelisted, not delayed by milter-greylist-4.2.6 (demeter2.kernel.org [140.211.167.43]); Thu, 02 Jun 2011 04:31:31 +0000 (UTC) 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 Signed-off-by: Lucas Meneghel Rodrigues 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= 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() +