[Libvir] [patch 00/12] Virtual networks support

Hey, And here's the actual patches to add virtual networks support ... Cheers, Mark. --

Adds the virNetwork API and hooks up the API to driver methods. The API closely mirrors the domains API with the following exceptions: - Networks don't have IDs - only names and UUIDs - There is no virNetworkCreateFlags We use a seperate driver vtable for the networks support and require at least one network driver. Signed-off-by: Mark McLoughlin <markmc@redhat.com> Index: libvirt/include/libvirt/libvirt.h.in =================================================================== --- libvirt.orig/include/libvirt/libvirt.h.in +++ libvirt/include/libvirt/libvirt.h.in @@ -443,6 +443,90 @@ int virDomainGetVcpus (virDomainPtr do int virDomainAttachDevice(virDomainPtr domain, char *xml); int virDomainDetachDevice(virDomainPtr domain, char *xml); +/* + * Virtual Networks API + */ + +/** + * virNetwork: + * + * a virNetwork is a private structure representing a virtual network. + */ +typedef struct _virNetwork virNetwork; + +/** + * virNetworkPtr: + * + * a virNetworkPtr is pointer to a virNetwork private structure, this is the + * type used to reference a virtual network in the API. + */ +typedef virNetwork *virNetworkPtr; + +/* + * List active networks + */ +int virConnectNumOfNetworks (virConnectPtr conn); +int virConnectListNetworks (virConnectPtr conn, + const char **names, + int maxnames); + +/* + * List inactive networks + */ +int virConnectNumOfDefinedNetworks (virConnectPtr conn); +int virConnectListDefinedNetworks (virConnectPtr conn, + const char **names, + int maxnames); + +/* + * Lookup network by name or uuid + */ +virNetworkPtr virNetworkLookupByName (virConnectPtr conn, + const char *name); +virNetworkPtr virNetworkLookupByUUID (virConnectPtr conn, + const unsigned char *uuid); +virNetworkPtr virNetworkLookupByUUIDString (virConnectPtr conn, + const char *uuid); + +/* + * Create active transient network + */ +virNetworkPtr virNetworkCreateXML (virConnectPtr conn, + const char *xmlDesc); + +/* + * Define inactive persistent network + */ +virNetworkPtr virNetworkDefineXML (virConnectPtr conn, + const char *xmlDesc); + +/* + * Delete persistent network + */ +int virNetworkUndefine (virNetworkPtr network); + +/* + * Activate persistent network + */ +int virNetworkCreate (virNetworkPtr network); + +/* + * Network destroy/free + */ +int virNetworkDestroy (virNetworkPtr network); +int virNetworkFree (virNetworkPtr network); + +/* + * Network informations + */ +const char* virNetworkGetName (virNetworkPtr network); +int virNetworkGetUUID (virNetworkPtr network, + unsigned char *uuid); +int virNetworkGetUUIDString (virNetworkPtr network, + char *buf); +char * virNetworkGetXMLDesc (virNetworkPtr network, + int flags); + #ifdef __cplusplus } #endif Index: libvirt/include/libvirt/virterror.h =================================================================== --- libvirt.orig/include/libvirt/virterror.h +++ libvirt/include/libvirt/virterror.h @@ -48,6 +48,7 @@ typedef enum { VIR_FROM_PROXY, /* Error in the proxy code */ VIR_FROM_CONF, /* Error in the configuration file handling */ VIR_FROM_QEMU, /* Error at the QEMU daemon */ + VIR_FROM_NET, /* Error when operating on a network */ } virErrorDomain; @@ -114,7 +115,9 @@ typedef enum { VIR_ERR_PARSE_FAILED, /* failed to parse a conf file */ VIR_ERR_CONF_SYNTAX, /* failed to parse the syntax of a conf file */ VIR_ERR_WRITE_FAILED, /* failed to write a conf file */ - VIR_ERR_XML_DETAIL /* detail of an XML error */ + VIR_ERR_XML_DETAIL, /* detail of an XML error */ + VIR_ERR_INVALID_NETWORK, /* invalid network object */ + VIR_ERR_NETWORK_EXIST, /* the network already exist */ } virErrorNumber; /** Index: libvirt/src/driver.h =================================================================== --- libvirt.orig/src/driver.h +++ libvirt/src/driver.h @@ -188,6 +188,65 @@ struct _virDriver { virDrvDomainDetachDevice domainDetachDevice; }; +typedef int + (*virDrvNumOfNetworks) (virConnectPtr conn); +typedef int + (*virDrvListNetworks) (virConnectPtr conn, + const char **names, + int maxnames); +typedef int + (*virDrvNumOfDefinedNetworks) (virConnectPtr conn); +typedef int + (*virDrvListDefinedNetworks) (virConnectPtr conn, + const char **names, + int maxnames); +typedef virNetworkPtr + (*virDrvNetworkLookupByUUID) (virConnectPtr conn, + const unsigned char *uuid); +typedef virNetworkPtr + (*virDrvNetworkLookupByName) (virConnectPtr conn, + const char *name); +typedef virNetworkPtr + (*virDrvNetworkCreateXML) (virConnectPtr conn, + const char *xmlDesc); +typedef virNetworkPtr + (*virDrvNetworkDefineXML) (virConnectPtr conn, const char *xml); +typedef int + (*virDrvNetworkUndefine) (virNetworkPtr network); +typedef int + (*virDrvNetworkCreate) (virNetworkPtr network); +typedef int + (*virDrvNetworkDestroy) (virNetworkPtr network); +typedef char * + (*virDrvNetworkDumpXML) (virNetworkPtr network, + int flags); + +typedef struct _virNetworkDriver virNetworkDriver; +typedef virNetworkDriver *virNetworkDriverPtr; + +/** + * _virNetworkDriver: + * + * Structure associated to a network virtualization driver, defining the various + * entry points for it. + */ +struct _virNetworkDriver { + virDrvOpen open; + virDrvClose close; + virDrvNumOfNetworks numOfNetworks; + virDrvListNetworks listNetworks; + virDrvNumOfDefinedNetworks numOfDefinedNetworks; + virDrvListDefinedNetworks listDefinedNetworks; + virDrvNetworkLookupByUUID networkLookupByUUID; + virDrvNetworkLookupByName networkLookupByName; + virDrvNetworkCreateXML networkCreateXML; + virDrvNetworkDefineXML networkDefineXML; + virDrvNetworkUndefine networkUndefine; + virDrvNetworkCreate networkCreate; + virDrvNetworkDestroy networkDestroy; + virDrvNetworkDumpXML networkDumpXML; +}; + /* * Registration @@ -195,6 +254,7 @@ struct _virDriver { * lookup based on the URI given in a virConnectOpen(ReadOnly) */ int virRegisterDriver(virDriverPtr); +int virRegisterNetworkDriver(virNetworkDriverPtr); #ifdef __cplusplus } Index: libvirt/src/hash.c =================================================================== --- libvirt.orig/src/hash.c +++ libvirt/src/hash.c @@ -637,6 +637,20 @@ virDomainFreeName(virDomainPtr domain, c } /** + * virNetworkFreeName: + * @network: a network object + * + * Destroy the network object, this is just used by the network hash callback. + * + * Returns 0 in case of success and -1 in case of failure. + */ +static int +virNetworkFreeName(virNetworkPtr network, const char *name ATTRIBUTE_UNUSED) +{ + return (virNetworkFree(network)); +} + +/** * virGetConnect: * * Allocates a new hypervisor connection structure @@ -660,6 +674,9 @@ virGetConnect(void) { ret->domains = virHashCreate(20); if (ret->domains == NULL) goto failed; + ret->networks = virHashCreate(20); + if (ret->networks == NULL) + goto failed; ret->hashes_mux = xmlNewMutex(); if (ret->hashes_mux == NULL) goto failed; @@ -671,6 +688,8 @@ failed: if (ret != NULL) { if (ret->domains != NULL) virHashFree(ret->domains, (virHashDeallocator) virDomainFreeName); + if (ret->networks != NULL) + virHashFree(ret->networks, (virHashDeallocator) virNetworkFreeName); if (ret->hashes_mux != NULL) xmlFreeMutex(ret->hashes_mux); free(ret); @@ -705,6 +724,8 @@ virFreeConnect(virConnectPtr conn) { if (conn->domains != NULL) virHashFree(conn->domains, (virHashDeallocator) virDomainFreeName); + if (conn->networks != NULL) + virHashFree(conn->networks, (virHashDeallocator) virNetworkFreeName); if (conn->hashes_mux != NULL) xmlFreeMutex(conn->hashes_mux); free(conn); @@ -893,6 +914,139 @@ done: xmlMutexUnlock(conn->hashes_mux); return(ret); } + +/** + * virGetNetwork: + * @conn: the hypervisor connection + * @name: pointer to the network name or NULL + * @uuid: pointer to the uuid or NULL + * + * Lookup if the network is already registered for that connection, + * if yes return a new pointer to it, if no allocate a new structure, + * and register it in the table. In any case a corresponding call to + * virFreeNetwork() is needed to not leak data. + * + * Returns a pointer to the network, or NULL in case of failure + */ +virNetworkPtr +virGetNetwork(virConnectPtr conn, const char *name, const unsigned char *uuid) { + virNetworkPtr ret = NULL; + + if ((!VIR_IS_CONNECT(conn)) || ((name == NULL) && (uuid == NULL)) || + (conn->hashes_mux == NULL)) { + virHashError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + return(NULL); + } + xmlMutexLock(conn->hashes_mux); + + /* TODO search by UUID first as they are better differenciators */ + + ret = (virNetworkPtr) virHashLookup(conn->networks, name); + if (ret != NULL) { + /* TODO check the UUID */ + goto done; + } + + /* + * not found, allocate a new one + */ + ret = (virNetworkPtr) malloc(sizeof(virNetwork)); + if (ret == NULL) { + virHashError(conn, VIR_ERR_NO_MEMORY, _("allocating network")); + goto error; + } + memset(ret, 0, sizeof(virNetwork)); + ret->name = strdup(name); + if (ret->name == NULL) { + virHashError(conn, VIR_ERR_NO_MEMORY, _("allocating network")); + goto error; + } + ret->magic = VIR_NETWORK_MAGIC; + ret->conn = conn; + if (uuid != NULL) + memcpy(&(ret->uuid[0]), uuid, VIR_UUID_BUFLEN); + + if (virHashAddEntry(conn->networks, name, ret) < 0) { + virHashError(conn, VIR_ERR_INTERNAL_ERROR, + _("failed to add network to connection hash table")); + goto error; + } + conn->uses++; +done: + ret->uses++; + xmlMutexUnlock(conn->hashes_mux); + return(ret); + +error: + xmlMutexUnlock(conn->hashes_mux); + if (ret != NULL) { + if (ret->name != NULL) + free(ret->name ); + free(ret); + } + return(NULL); +} + +/** + * virFreeNetwork: + * @conn: the hypervisor connection + * @network: the network to release + * + * Release the given network, if the reference count drops to zero, then + * the network is really freed. + * + * Returns the reference count or -1 in case of failure. + */ +int +virFreeNetwork(virConnectPtr conn, virNetworkPtr network) { + int ret = 0; + + if ((!VIR_IS_CONNECT(conn)) || (!VIR_IS_CONNECTED_NETWORK(network)) || + (network->conn != conn) || (conn->hashes_mux == NULL)) { + virHashError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + return(-1); + } + xmlMutexLock(conn->hashes_mux); + + /* + * decrement the count for the network + */ + network->uses--; + ret = network->uses; + if (ret > 0) + goto done; + + /* TODO search by UUID first as they are better differenciators */ + + if (virHashRemoveEntry(conn->networks, network->name, NULL) < 0) { + virHashError(conn, VIR_ERR_INTERNAL_ERROR, + _("network missing from connection hash table")); + goto done; + } + network->magic = -1; + if (network->name) + free(network->name); + free(network); + + /* + * decrement the count for the connection + */ + conn->uses--; + if (conn->uses > 0) + goto done; + + if (conn->networks != NULL) + virHashFree(conn->networks, (virHashDeallocator) virNetworkFreeName); + if (conn->hashes_mux != NULL) + xmlFreeMutex(conn->hashes_mux); + free(conn); + return(0); + +done: + xmlMutexUnlock(conn->hashes_mux); + return(ret); +} + /* * Local variables: * indent-tabs-mode: nil Index: libvirt/src/internal.h =================================================================== --- libvirt.orig/src/internal.h +++ libvirt/src/internal.h @@ -84,6 +84,16 @@ extern "C" { #define VIR_IS_DOMAIN(obj) ((obj) && (obj)->magic==VIR_DOMAIN_MAGIC) #define VIR_IS_CONNECTED_DOMAIN(obj) (VIR_IS_DOMAIN(obj) && VIR_IS_CONNECT((obj)->conn)) +/** + * VIR_NETWORK_MAGIC: + * + * magic value used to protect the API when pointers to network structures + * are passed down by the uers. + */ +#define VIR_NETWORK_MAGIC 0xDEAD1234 +#define VIR_IS_NETWORK(obj) ((obj) && (obj)->magic==VIR_NETWORK_MAGIC) +#define VIR_IS_CONNECTED_NETWORK(obj) (VIR_IS_NETWORK(obj) && VIR_IS_CONNECT((obj)->conn)) + #define MAX_DRIVERS 10 /* @@ -104,6 +114,10 @@ struct _virConnect { virDriverPtr drivers[MAX_DRIVERS]; int nb_drivers; + /* the list of available network drivers */ + virNetworkDriverPtr networkDrivers[MAX_DRIVERS]; + int nb_network_drivers; + /* extra data needed by drivers */ int handle; /* internal handle used for hypercall */ struct xs_handle *xshandle;/* handle to talk to the xenstore */ @@ -125,8 +139,9 @@ struct _virConnect { void *userData; /* the user data */ /* misc */ - xmlMutexPtr hashes_mux;/* a mutex to protect the domain hash table */ + xmlMutexPtr hashes_mux;/* a mutex to protect the domain and networks hash tables */ virHashTablePtr domains;/* hash table for known domains */ + virHashTablePtr networks;/* hash table for known domains */ int flags; /* a set of connection flags */ }; @@ -158,6 +173,19 @@ struct _virDomain { char *xml; /* the XML description for defined domains */ }; +/** +* _virNetwork: +* +* Internal structure associated to a domain +*/ +struct _virNetwork { + unsigned int magic; /* specific value to check */ + int uses; /* reference count */ + virConnectPtr conn; /* pointer back to the connection */ + char *name; /* the network external name */ + unsigned char uuid[VIR_UUID_BUFLEN]; /* the network unique identifier */ +}; + /* * Internal routines */ @@ -196,6 +224,11 @@ int virFreeDomain (virConnectPtr conn, virDomainPtr domain); virDomainPtr virGetDomainByID(virConnectPtr conn, int id); +virNetworkPtr virGetNetwork (virConnectPtr conn, + const char *name, + const unsigned char *uuid); +int virFreeNetwork (virConnectPtr conn, + virNetworkPtr domain); #ifdef __cplusplus } Index: libvirt/src/libvirt.c =================================================================== --- libvirt.orig/src/libvirt.c +++ libvirt/src/libvirt.c @@ -42,6 +42,7 @@ */ static virDriverPtr virDriverTab[MAX_DRIVERS]; +static virNetworkDriverPtr virNetworkDriverTab[MAX_DRIVERS]; static int initialized = 0; /** @@ -68,8 +69,10 @@ virInitialize(void) /* * should not be needed but... */ - for (i = 0;i < MAX_DRIVERS;i++) + for (i = 0;i < MAX_DRIVERS;i++) { virDriverTab[i] = NULL; + virNetworkDriverTab[i] = NULL; + } /* * Note that the order is important the first ones have a higher priority @@ -135,16 +138,36 @@ virLibDomainError(virDomainPtr domain, v } /** - * virRegisterDriver: - * @driver: pointer to a driver block - * - * Register a virtualization driver + * virLibNetworkError: + * @conn: the connection if available + * @error: the error noumber + * @info: extra information string * - * Returns the driver priority or -1 in case of error. + * Handle an error at the connection level */ -int -virRegisterDriver(virDriverPtr driver) +static void +virLibNetworkError(virNetworkPtr network, virErrorNumber error, + const char *info) +{ + virConnectPtr conn = NULL; + const char *errmsg; + + if (error == VIR_ERR_OK) + return; + + errmsg = __virErrorMsg(error, info); + if (error != VIR_ERR_INVALID_NETWORK) { + conn = network->conn; + } + /* XXX: should be able to pass network pointer here */ + __virRaiseError(conn, NULL, VIR_FROM_NET, error, VIR_ERR_ERROR, + errmsg, info, NULL, 0, 0, errmsg, info); +} + +static int +_virRegisterDriver(void *driver, int isNetwork) { + void **drivers; int i; if (!initialized) @@ -155,13 +178,14 @@ virRegisterDriver(virDriverPtr driver) virLibConnError(NULL, VIR_ERR_INVALID_ARG, __FUNCTION__); return(-1); } + drivers = isNetwork ? (void **) virNetworkDriverTab : (void **) virDriverTab; for (i = 0;i < MAX_DRIVERS;i++) { - if (virDriverTab[i] == driver) + if (drivers[i] == driver) return(i); } for (i = 0;i < MAX_DRIVERS;i++) { - if (virDriverTab[i] == NULL) { - virDriverTab[i] = driver; + if (drivers[i] == NULL) { + drivers[i] = driver; return(i); } } @@ -170,6 +194,34 @@ virRegisterDriver(virDriverPtr driver) } /** + * virRegisterNetworkDriver: + * @driver: pointer to a network driver block + * + * Register a network virtualization driver + * + * Returns the driver priority or -1 in case of error. + */ +int +virRegisterNetworkDriver(virNetworkDriverPtr driver) +{ + return _virRegisterDriver(driver, 1); +} + +/** + * virRegisterDriver: + * @driver: pointer to a driver block + * + * Register a virtualization driver + * + * Returns the driver priority or -1 in case of error. + */ +int +virRegisterDriver(virDriverPtr driver) +{ + return _virRegisterDriver(driver, 0); +} + +/** * virGetVersion: * @libVer: return value for the library version (OUT) * @type: the type of connection/driver looked at @@ -265,7 +317,14 @@ virConnectOpen(const char *name) } } - if (ret->nb_drivers == 0) { + for (i = 0;i < MAX_DRIVERS;i++) { + if ((virNetworkDriverTab[i] != NULL) && (virNetworkDriverTab[i]->open != NULL) && + (res = virNetworkDriverTab[i]->open(ret, name, VIR_DRV_OPEN_QUIET)) == 0) { + ret->networkDrivers[ret->nb_network_drivers++] = virNetworkDriverTab[i]; + } + } + + if (ret->nb_drivers == 0 || ret->nb_network_drivers == 0) { /* we failed to find an adequate driver */ virLibConnError(NULL, VIR_ERR_NO_SUPPORT, name); goto failed; @@ -279,6 +338,10 @@ failed: if ((ret->drivers[i] != NULL) && (ret->drivers[i]->close != NULL)) ret->drivers[i]->close(ret); } + for (i = 0;i < ret->nb_network_drivers;i++) { + if ((ret->networkDrivers[i] != NULL) && (ret->networkDrivers[i]->close != NULL)) + ret->networkDrivers[i]->close(ret); + } virFreeConnect(ret); } return (NULL); @@ -321,6 +384,10 @@ virConnectOpenReadOnly(const char *name) ret->drivers[ret->nb_drivers++] = virDriverTab[i]; } + if ((virNetworkDriverTab[i] != NULL) && (virNetworkDriverTab[i]->open != NULL) && + (res = virNetworkDriverTab[i]->open(ret, name, VIR_DRV_OPEN_QUIET)) == 0) { + ret->networkDrivers[ret->nb_network_drivers++] = virNetworkDriverTab[i]; + } } if (ret->nb_drivers == 0) { if (name == NULL) @@ -341,6 +408,10 @@ failed: if ((ret->drivers[i] != NULL) && (ret->drivers[i]->close != NULL)) ret->drivers[i]->close(ret); } + for (i = 0;i < ret->nb_network_drivers;i++) { + if ((ret->networkDrivers[i] != NULL) && (ret->networkDrivers[i]->close != NULL)) + ret->networkDrivers[i]->close(ret); + } virFreeConnect(ret); } return (NULL); @@ -368,6 +439,10 @@ virConnectClose(virConnectPtr conn) if ((conn->drivers[i] != NULL) && (conn->drivers[i]->close != NULL)) conn->drivers[i]->close(conn); } + for (i = 0;i < conn->nb_network_drivers;i++) { + if ((conn->networkDrivers[i] != NULL) && (conn->networkDrivers[i]->close != NULL)) + conn->networkDrivers[i]->close(conn); + } if (virFreeConnect(conn) < 0) return (-1); return (0); @@ -2053,3 +2128,607 @@ virDomainDetachDevice(virDomainPtr domai virLibConnError(conn, VIR_ERR_CALL_FAILED, __FUNCTION__); return (-1); } + +/** + * virConnectNumOfNetworks: + * @conn: pointer to the hypervisor connection + * + * Provides the number of active networks. + * + * Returns the number of network found or -1 in case of error + */ +int +virConnectNumOfNetworks(virConnectPtr conn) +{ + int ret = -1; + int i; + + if (!VIR_IS_CONNECT(conn)) { + virLibConnError(conn, VIR_ERR_INVALID_CONN, __FUNCTION__); + return (-1); + } + + /* Go though the driver registered entry points */ + for (i = 0;i < conn->nb_network_drivers;i++) { + if ((conn->networkDrivers[i] != NULL) && + (conn->networkDrivers[i]->numOfNetworks != NULL)) { + ret = conn->networkDrivers[i]->numOfNetworks(conn); + if (ret >= 0) + return(ret); + } + } + + return(-1); +} + +/** + * virConnectListNetworks: + * @conn: pointer to the hypervisor connection + * @names: array to collect the list of names of active networks + * @maxnames: size of @names + * + * Collect the list of active networks, and store their names in @names + * + * Returns the number of networks found or -1 in case of error + */ +int +virConnectListNetworks(virConnectPtr conn, const char **names, int maxnames) +{ + int ret = -1; + int i; + + if (!VIR_IS_CONNECT(conn)) { + virLibConnError(conn, VIR_ERR_INVALID_CONN, __FUNCTION__); + return (-1); + } + + if ((names == NULL) || (maxnames <= 0)) { + virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + return (-1); + } + + /* Go though the driver registered entry points */ + for (i = 0;i < conn->nb_drivers;i++) { + if ((conn->networkDrivers[i] != NULL) && + (conn->networkDrivers[i]->listNetworks != NULL)) { + ret = conn->networkDrivers[i]->listNetworks(conn, names, maxnames); + if (ret >= 0) + return(ret); + } + } + + return (-1); +} + +/** + * virConnectNumOfDefinedNetworks: + * @conn: pointer to the hypervisor connection + * + * Provides the number of inactive networks. + * + * Returns the number of networks found or -1 in case of error + */ +int +virConnectNumOfDefinedNetworks(virConnectPtr conn) +{ + int ret = -1; + int i; + + if (!VIR_IS_CONNECT(conn)) { + virLibConnError(conn, VIR_ERR_INVALID_CONN, __FUNCTION__); + return (-1); + } + + /* Go though the driver registered entry points */ + for (i = 0;i < conn->nb_drivers;i++) { + if ((conn->networkDrivers[i] != NULL) && + (conn->networkDrivers[i]->numOfDefinedNetworks != NULL)) { + ret = conn->networkDrivers[i]->numOfDefinedNetworks(conn); + if (ret >= 0) + return(ret); + } + } + + return(-1); +} + +/** + * virConnectListDefinedNetworks: + * @conn: pointer to the hypervisor connection + * @names: pointer to an array to store the names + * @maxnames: size of the array + * + * list the inactive networks, stores the pointers to the names in @names + * + * Returns the number of names provided in the array or -1 in case of error + */ +int +virConnectListDefinedNetworks(virConnectPtr conn, const char **names, + int maxnames) { + int ret = -1; + int i; + + if (!VIR_IS_CONNECT(conn)) { + virLibConnError(conn, VIR_ERR_INVALID_CONN, __FUNCTION__); + return (-1); + } + + if ((names == NULL) || (maxnames <= 0)) { + virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + return (-1); + } + + /* Go though the driver registered entry points */ + for (i = 0;i < conn->nb_drivers;i++) { + if ((conn->networkDrivers[i] != NULL) && + (conn->networkDrivers[i]->listDefinedNetworks != NULL)) { + ret = conn->networkDrivers[i]->listDefinedNetworks(conn, names, maxnames); + if (ret >= 0) + return(ret); + } + } + + return (-1); +} + +/** + * virNetworkLookupByName: + * @conn: pointer to the hypervisor connection + * @name: name for the network + * + * Try to lookup a network on the given hypervisor based on its name. + * + * Returns a new network object or NULL in case of failure + */ +virNetworkPtr +virNetworkLookupByName(virConnectPtr conn, const char *name) +{ + virNetworkPtr ret = NULL; + int i; + + if (!VIR_IS_CONNECT(conn)) { + virLibConnError(conn, VIR_ERR_INVALID_CONN, __FUNCTION__); + return (NULL); + } + if (name == NULL) { + virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + return (NULL); + } + + /* Go though the driver registered entry points */ + for (i = 0;i < conn->nb_drivers;i++) { + if ((conn->networkDrivers[i] != NULL) && + (conn->networkDrivers[i]->networkLookupByName != NULL)) { + ret = conn->networkDrivers[i]->networkLookupByName(conn, name); + if (ret) + return(ret); + } + } + return (NULL); +} + +/** + * virNetworkLookupByUUID: + * @conn: pointer to the hypervisor connection + * @uuid: the raw UUID for the network + * + * Try to lookup a network on the given hypervisor based on its UUID. + * + * Returns a new network object or NULL in case of failure + */ +virNetworkPtr +virNetworkLookupByUUID(virConnectPtr conn, const unsigned char *uuid) +{ + virNetworkPtr ret; + int i; + + if (!VIR_IS_CONNECT(conn)) { + virLibConnError(conn, VIR_ERR_INVALID_CONN, __FUNCTION__); + return (NULL); + } + if (uuid == NULL) { + virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + return (NULL); + } + + /* Go though the driver registered entry points */ + for (i = 0;i < conn->nb_drivers;i++) { + if ((conn->networkDrivers[i] != NULL) && + (conn->networkDrivers[i]->networkLookupByUUID != NULL)) { + ret = conn->networkDrivers[i]->networkLookupByUUID(conn, uuid); + if (ret) + return(ret); + } + } + + return (NULL); +} + +/** + * virNetworkLookupByUUIDString: + * @conn: pointer to the hypervisor connection + * @uuidstr: the string UUID for the network + * + * Try to lookup a network on the given hypervisor based on its UUID. + * + * Returns a new network object or NULL in case of failure + */ +virNetworkPtr +virNetworkLookupByUUIDString(virConnectPtr conn, const char *uuidstr) +{ + int raw[VIR_UUID_BUFLEN], i; + unsigned char uuid[VIR_UUID_BUFLEN]; + int ret; + + if (!VIR_IS_CONNECT(conn)) { + virLibConnError(conn, VIR_ERR_INVALID_CONN, __FUNCTION__); + return (NULL); + } + if (uuidstr == NULL) { + virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + return (NULL); + } + + /* XXX: sexpr_uuid() also supports 'xxxx-xxxx-xxxx-xxxx' format. + * We needn't it here. Right? + */ + ret = sscanf(uuidstr, + "%02x%02x%02x%02x-" + "%02x%02x-" + "%02x%02x-" + "%02x%02x-" + "%02x%02x%02x%02x%02x%02x", + raw + 0, raw + 1, raw + 2, raw + 3, + raw + 4, raw + 5, raw + 6, raw + 7, + raw + 8, raw + 9, raw + 10, raw + 11, + raw + 12, raw + 13, raw + 14, raw + 15); + + if (ret!=VIR_UUID_BUFLEN) { + virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + return (NULL); + } + for (i = 0; i < VIR_UUID_BUFLEN; i++) + uuid[i] = raw[i] & 0xFF; + + return virNetworkLookupByUUID(conn, &uuid[0]); +} + +/** + * virNetworkCreateXML: + * @conn: pointer to the hypervisor connection + * @xmlDesc: an XML description of the network + * + * Create and start a new virtual network, based on an XML description + * similar to the one returned by virNetworkGetXMLDesc() + * + * Returns a new network object or NULL in case of failure + */ +virNetworkPtr +virNetworkCreateXML(virConnectPtr conn, const char *xmlDesc) +{ + virNetworkPtr ret; + int i; + + if (!VIR_IS_CONNECT(conn)) { + virLibConnError(conn, VIR_ERR_INVALID_CONN, __FUNCTION__); + return (NULL); + } + if (xmlDesc == NULL) { + virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + return (NULL); + } + if (conn->flags & VIR_CONNECT_RO) { + virLibConnError(conn, VIR_ERR_OPERATION_DENIED, __FUNCTION__); + return (NULL); + } + + for (i = 0;i < conn->nb_drivers;i++) { + if ((conn->networkDrivers[i] != NULL) && + (conn->networkDrivers[i]->networkCreateXML != NULL)) { + ret = conn->networkDrivers[i]->networkCreateXML(conn, xmlDesc); + if (ret != NULL) + return(ret); + } + } + return(NULL); +} + +/** + * virNetworkDefineXML: + * @conn: pointer to the hypervisor connection + * @xml: the XML description for the network, preferably in UTF-8 + * + * Define a network, but does not create it + * + * Returns NULL in case of error, a pointer to the network otherwise + */ +virNetworkPtr +virNetworkDefineXML(virConnectPtr conn, const char *xml) { + virNetworkPtr ret = NULL; + int i; + + if (!VIR_IS_CONNECT(conn)) { + virLibConnError(conn, VIR_ERR_INVALID_CONN, __FUNCTION__); + return (NULL); + } + if (conn->flags & VIR_CONNECT_RO) { + virLibConnError(conn, VIR_ERR_OPERATION_DENIED, __FUNCTION__); + return (NULL); + } + if (xml == NULL) { + virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + return (NULL); + } + + /* Go though the driver registered entry points */ + for (i = 0;i < conn->nb_drivers;i++) { + if ((conn->networkDrivers[i] != NULL) && + (conn->networkDrivers[i]->networkDefineXML != NULL)) { + ret = conn->networkDrivers[i]->networkDefineXML(conn, xml); + if (ret) + return(ret); + } + } + + return(ret); +} + +/** + * virNetworkUndefine: + * @network: pointer to a defined network + * + * Undefine a network but does not stop it if it is running + * + * Returns 0 in case of success, -1 in case of error + */ +int +virNetworkUndefine(virNetworkPtr network) { + int ret, i; + virConnectPtr conn; + + if (!VIR_IS_CONNECTED_NETWORK(network)) { + virLibNetworkError(network, VIR_ERR_INVALID_NETWORK, __FUNCTION__); + return (-1); + } + conn = network->conn; + if (conn->flags & VIR_CONNECT_RO) { + virLibNetworkError(network, VIR_ERR_OPERATION_DENIED, __FUNCTION__); + return (-1); + } + + /* Go though the driver registered entry points */ + for (i = 0;i < conn->nb_drivers;i++) { + if ((conn->networkDrivers[i] != NULL) && + (conn->networkDrivers[i]->networkUndefine != NULL)) { + ret = conn->networkDrivers[i]->networkUndefine(network); + if (ret >= 0) + return(ret); + } + } + + return(-1); +} + +/** + * virNetworkCreate: + * @network: pointer to a defined network + * + * Create and start a defined network. If the call succeed the network + * moves from the defined to the running networks pools. + * + * Returns 0 in case of success, -1 in case of error + */ +int +virNetworkCreate(virNetworkPtr network) { + int i, ret = -1; + virConnectPtr conn; + if (network == NULL) { + TODO + return (-1); + } + if (!VIR_IS_CONNECTED_NETWORK(network)) { + virLibNetworkError(network, VIR_ERR_INVALID_NETWORK, __FUNCTION__); + return (-1); + } + conn = network->conn; + if (conn->flags & VIR_CONNECT_RO) { + virLibNetworkError(network, VIR_ERR_OPERATION_DENIED, __FUNCTION__); + return (-1); + } + + for (i = 0;i < conn->nb_drivers;i++) { + if ((conn->networkDrivers[i] != NULL) && + (conn->networkDrivers[i]->networkCreate != NULL)) { + ret = conn->networkDrivers[i]->networkCreate(network); + if (ret == 0) + return(ret); + } + } + return(ret); +} + +/** + * virNetworkDestroy: + * @network: a network object + * + * Destroy the network object. The running instance is shutdown if not down + * already and all resources used by it are given back to the hypervisor. + * The data structure is freed and should not be used thereafter if the + * call does not return an error. + * This function may requires priviledged access + * + * Returns 0 in case of success and -1 in case of failure. + */ +int +virNetworkDestroy(virNetworkPtr network) +{ + int i; + virConnectPtr conn; + + if (!VIR_IS_CONNECTED_NETWORK(network)) { + virLibNetworkError(network, VIR_ERR_INVALID_NETWORK, __FUNCTION__); + return (-1); + } + + conn = network->conn; + if (conn->flags & VIR_CONNECT_RO) { + virLibNetworkError(network, VIR_ERR_OPERATION_DENIED, __FUNCTION__); + return (-1); + } + + /* + * Go though the driver registered entry points + */ + for (i = 0;i < conn->nb_drivers;i++) { + if ((conn->networkDrivers[i] != NULL) && + (conn->networkDrivers[i]->networkDestroy != NULL)) { + if (conn->networkDrivers[i]->networkDestroy(network) == 0) + return (0); + } + } + + virLibConnError(conn, VIR_ERR_CALL_FAILED, __FUNCTION__); + return (-1); +} + +/** + * virNetworkFree: + * @network: a network object + * + * Free the network object. The running instance is kept alive. + * The data structure is freed and should not be used thereafter. + * + * Returns 0 in case of success and -1 in case of failure. + */ +int +virNetworkFree(virNetworkPtr network) +{ + if (!VIR_IS_NETWORK(network)) { + virLibNetworkError(network, VIR_ERR_INVALID_NETWORK, __FUNCTION__); + return (-1); + } + if (virFreeNetwork(network->conn, network) < 0) + return (-1); + return(0); +} + +/** + * virNetworkGetName: + * @network: a network object + * + * Get the public name for that network + * + * Returns a pointer to the name or NULL, the string need not be deallocated + * its lifetime will be the same as the network object. + */ +const char * +virNetworkGetName(virNetworkPtr network) +{ + if (!VIR_IS_NETWORK(network)) { + virLibNetworkError(network, VIR_ERR_INVALID_NETWORK, __FUNCTION__); + return (NULL); + } + return (network->name); +} + +/** + * virNetworkGetUUID: + * @network: a network object + * @uuid: pointer to a VIR_UUID_BUFLEN bytes array + * + * Get the UUID for a network + * + * Returns -1 in case of error, 0 in case of success + */ +int +virNetworkGetUUID(virNetworkPtr network, unsigned char *uuid) +{ + if (!VIR_IS_NETWORK(network)) { + virLibNetworkError(network, VIR_ERR_INVALID_NETWORK, __FUNCTION__); + return (-1); + } + if (uuid == NULL) { + virLibNetworkError(network, VIR_ERR_INVALID_ARG, __FUNCTION__); + return (-1); + } + + memcpy(uuid, &network->uuid[0], VIR_UUID_BUFLEN); + + return (0); +} + +/** + * virNetworkGetUUIDString: + * @network: a network object + * @buf: pointer to a VIR_UUID_STRING_BUFLEN bytes array + * + * Get the UUID for a network as string. For more information about + * UUID see RFC4122. + * + * Returns -1 in case of error, 0 in case of success + */ +int +virNetworkGetUUIDString(virNetworkPtr network, char *buf) +{ + unsigned char uuid[VIR_UUID_BUFLEN]; + + if (!VIR_IS_NETWORK(network)) { + virLibNetworkError(network, VIR_ERR_INVALID_NETWORK, __FUNCTION__); + return (-1); + } + if (buf == NULL) { + virLibNetworkError(network, VIR_ERR_INVALID_ARG, __FUNCTION__); + return (-1); + } + + if (virNetworkGetUUID(network, &uuid[0])) + return (-1); + + snprintf(buf, VIR_UUID_STRING_BUFLEN, + "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", + uuid[0], uuid[1], uuid[2], uuid[3], + uuid[4], uuid[5], uuid[6], uuid[7], + uuid[8], uuid[9], uuid[10], uuid[11], + uuid[12], uuid[13], uuid[14], uuid[15]); + return (0); +} + +/** + * virNetworkGetXMLDesc: + * @network: a network object + * @flags: and OR'ed set of extraction flags, not used yet + * + * Provide an XML description of the network. The description may be reused + * later to relaunch the network with virNetworkCreateXML(). + * + * Returns a 0 terminated UTF-8 encoded XML instance, or NULL in case of error. + * the caller must free() the returned value. + */ +char * +virNetworkGetXMLDesc(virNetworkPtr network, int flags) +{ + int i; + char *ret = NULL; + if (!VIR_IS_NETWORK(network)) { + virLibNetworkError(network, VIR_ERR_INVALID_NETWORK, __FUNCTION__); + return (NULL); + } + if (flags != 0) { + virLibNetworkError(network, VIR_ERR_INVALID_ARG, __FUNCTION__); + return (NULL); + } + + for (i = 0;i < network->conn->nb_network_drivers;i++) { + if ((network->conn->networkDrivers[i] != NULL) && + (network->conn->networkDrivers[i]->networkDumpXML != NULL)) { + ret = network->conn->networkDrivers[i]->networkDumpXML(network, flags); + if (ret) + break; + } + } + if (!ret) { + virLibConnError(network->conn, VIR_ERR_CALL_FAILED, __FUNCTION__); + return (NULL); + } + return(ret); +} Index: libvirt/src/libvirt_sym.version =================================================================== --- libvirt.orig/src/libvirt_sym.version +++ libvirt/src/libvirt_sym.version @@ -57,5 +57,24 @@ virDomainAttachDevice; virDomainDetachDevice; + + virConnectNumOfNetworks; + virConnectListNetworks; + virConnectNumOfDefinedNetworks; + virConnectListDefinedNetworks; + virNetworkLookupByName; + virNetworkLookupByUUID; + virNetworkLookupByUUIDString; + virNetworkCreateXML; + virNetworkDefineXML; + virNetworkUndefine; + virNetworkCreate; + virNetworkDestroy; + virNetworkFree; + virNetworkGetName; + virNetworkGetUUID; + virNetworkGetUUIDString; + virNetworkGetXMLDesc; + local: *; }; Index: libvirt/src/virterror.c =================================================================== --- libvirt.orig/src/virterror.c +++ libvirt/src/virterror.c @@ -271,6 +271,9 @@ virDefaultErrorFunc(virErrorPtr err) case VIR_FROM_QEMU: dom = "QEMU "; break; + case VIR_FROM_NET: + dom = "Network "; + break; } if ((err->dom != NULL) && (err->code != VIR_ERR_INVALID_DOMAIN)) { domain = err->dom->name; @@ -585,6 +588,18 @@ __virErrorMsg(virErrorNumber error, cons else errmsg = "%s"; break; + case VIR_ERR_INVALID_NETWORK: + if (info == NULL) + errmsg = _("invalid network pointer in"); + else + errmsg = _("invalid network pointer in %s"); + break; + case VIR_ERR_NETWORK_EXIST: + if (info == NULL) + errmsg = _("this network exists already"); + else + errmsg = _("network %s exists already"); + break; } return (errmsg); } --

Add virNetwork support to virError in order that the network on which the error occurred can be reported. Note, the issue here is that virError is a public structure and we're arguably breaking ABI if we enlarge it. However, it's unlikely anyone would define a virError structure themself. Signed-off-by: Mark McLoughlin <markmc@redhat.com> Index: libvirt/include/libvirt/virterror.h =================================================================== --- libvirt.orig/include/libvirt/virterror.h +++ libvirt/include/libvirt/virterror.h @@ -72,6 +72,7 @@ struct _virError { char *str3; /* extra string information */ int int1; /* extra number information */ int int2; /* extra number information */ + virNetworkPtr net; /* the network if available */ }; /** Index: libvirt/src/conf.c =================================================================== --- libvirt.orig/src/conf.c +++ libvirt/src/conf.c @@ -95,7 +95,7 @@ virConfError(virConfPtr conf ATTRIBUTE_U return; errmsg = __virErrorMsg(error, info); - __virRaiseError(NULL, NULL, VIR_FROM_CONF, error, VIR_ERR_ERROR, + __virRaiseError(NULL, NULL, NULL, VIR_FROM_CONF, error, VIR_ERR_ERROR, errmsg, info, NULL, line, 0, errmsg, info, line); } Index: libvirt/src/hash.c =================================================================== --- libvirt.orig/src/hash.c +++ libvirt/src/hash.c @@ -617,7 +617,7 @@ virHashError(virConnectPtr conn, virErro return; errmsg = __virErrorMsg(error, info); - __virRaiseError(conn, NULL, VIR_FROM_NONE, error, VIR_ERR_ERROR, + __virRaiseError(conn, NULL, NULL, VIR_FROM_NONE, error, VIR_ERR_ERROR, errmsg, info, NULL, 0, 0, errmsg, info); } Index: libvirt/src/internal.h =================================================================== --- libvirt.orig/src/internal.h +++ libvirt/src/internal.h @@ -200,6 +200,7 @@ char *virDomainGetVMInfo(virDomainPtr do ************************************************************************/ void __virRaiseError(virConnectPtr conn, virDomainPtr dom, + virNetworkPtr net, int domain, int code, virErrorLevel level, Index: libvirt/src/libvirt.c =================================================================== --- libvirt.orig/src/libvirt.c +++ libvirt/src/libvirt.c @@ -107,7 +107,7 @@ virLibConnError(virConnectPtr conn, virE return; errmsg = __virErrorMsg(error, info); - __virRaiseError(conn, NULL, VIR_FROM_NONE, error, VIR_ERR_ERROR, + __virRaiseError(conn, NULL, NULL, VIR_FROM_NONE, error, VIR_ERR_ERROR, errmsg, info, NULL, 0, 0, errmsg, info); } @@ -133,7 +133,7 @@ virLibDomainError(virDomainPtr domain, v if (error != VIR_ERR_INVALID_DOMAIN) { conn = domain->conn; } - __virRaiseError(conn, domain, VIR_FROM_DOM, error, VIR_ERR_ERROR, + __virRaiseError(conn, domain, NULL, VIR_FROM_DOM, error, VIR_ERR_ERROR, errmsg, info, NULL, 0, 0, errmsg, info); } @@ -159,8 +159,7 @@ virLibNetworkError(virNetworkPtr network if (error != VIR_ERR_INVALID_NETWORK) { conn = network->conn; } - /* XXX: should be able to pass network pointer here */ - __virRaiseError(conn, NULL, VIR_FROM_NET, error, VIR_ERR_ERROR, + __virRaiseError(conn, NULL, network, VIR_FROM_NET, error, VIR_ERR_ERROR, errmsg, info, NULL, 0, 0, errmsg, info); } Index: libvirt/src/proxy_internal.c =================================================================== --- libvirt.orig/src/proxy_internal.c +++ libvirt/src/proxy_internal.c @@ -116,7 +116,7 @@ virProxyError(virConnectPtr conn, virErr return; errmsg = __virErrorMsg(error, info); - __virRaiseError(conn, NULL, VIR_FROM_PROXY, error, VIR_ERR_ERROR, + __virRaiseError(conn, NULL, NULL, VIR_FROM_PROXY, error, VIR_ERR_ERROR, errmsg, info, NULL, 0, 0, errmsg, info); } Index: libvirt/src/qemu_internal.c =================================================================== --- libvirt.orig/src/qemu_internal.c +++ libvirt/src/qemu_internal.c @@ -140,7 +140,7 @@ qemuError(virConnectPtr con, return; errmsg = __virErrorMsg(error, info); - __virRaiseError(con, dom, VIR_FROM_QEMU, error, VIR_ERR_ERROR, + __virRaiseError(con, dom, NULL, VIR_FROM_QEMU, error, VIR_ERR_ERROR, errmsg, info, NULL, 0, 0, errmsg, info, 0); } Index: libvirt/src/sexpr.c =================================================================== --- libvirt.orig/src/sexpr.c +++ libvirt/src/sexpr.c @@ -38,7 +38,7 @@ virSexprError(virErrorNumber error, cons return; errmsg = __virErrorMsg(error, info); - __virRaiseError(NULL, NULL, VIR_FROM_SEXPR, error, VIR_ERR_ERROR, + __virRaiseError(NULL, NULL, NULL, VIR_FROM_SEXPR, error, VIR_ERR_ERROR, errmsg, info, NULL, 0, 0, errmsg, info); } Index: libvirt/src/test.c =================================================================== --- libvirt.orig/src/test.c +++ libvirt/src/test.c @@ -198,7 +198,7 @@ testError(virConnectPtr con, return; errmsg = __virErrorMsg(error, info); - __virRaiseError(con, dom, VIR_FROM_XEN, error, VIR_ERR_ERROR, + __virRaiseError(con, dom, NULL, VIR_FROM_XEN, error, VIR_ERR_ERROR, errmsg, info, NULL, 0, 0, errmsg, info, 0); } Index: libvirt/src/virterror.c =================================================================== --- libvirt.orig/src/virterror.c +++ libvirt/src/virterror.c @@ -17,7 +17,7 @@ #include "internal.h" static virError lastErr = /* the last error */ -{ 0, 0, NULL, VIR_ERR_NONE, NULL, NULL, NULL, NULL, NULL, 0, 0 }; +{ 0, 0, NULL, VIR_ERR_NONE, NULL, NULL, NULL, NULL, NULL, 0, 0, NULL }; static virErrorFunc virErrorHandler = NULL; /* global error handlet */ static void *virUserData = NULL; /* associated data */ @@ -230,7 +230,7 @@ virConnSetErrorFunc(virConnectPtr conn, void virDefaultErrorFunc(virErrorPtr err) { - const char *lvl = "", *dom = "", *domain = ""; + const char *lvl = "", *dom = "", *domain = "", *network = ""; int len; if ((err == NULL) || (err->code == VIR_ERR_OK)) @@ -277,24 +277,27 @@ virDefaultErrorFunc(virErrorPtr err) } if ((err->dom != NULL) && (err->code != VIR_ERR_INVALID_DOMAIN)) { domain = err->dom->name; + } else if ((err->net != NULL) && (err->code != VIR_ERR_INVALID_NETWORK)) { + network = err->net->name; } len = strlen(err->message); if ((err->domain == VIR_FROM_XML) && (err->code == VIR_ERR_XML_DETAIL) && (err->int1 != 0)) - fprintf(stderr, "libvir: %s%s %s: line %d: %s", - dom, lvl, domain, err->int1, err->message); + fprintf(stderr, "libvir: %s%s %s%s: line %d: %s", + dom, lvl, domain, network, err->int1, err->message); else if ((len == 0) || (err->message[len - 1] != '\n')) - fprintf(stderr, "libvir: %s%s %s: %s\n", - dom, lvl, domain, err->message); + fprintf(stderr, "libvir: %s%s %s%s: %s\n", + dom, lvl, domain, network, err->message); else - fprintf(stderr, "libvir: %s%s %s: %s", - dom, lvl, domain, err->message); + fprintf(stderr, "libvir: %s%s %s%s: %s", + dom, lvl, domain, network, err->message); } /** * __virRaiseError: * @conn: the connection to the hypervisor if available * @dom: the domain if available + * @net: the network if available * @domain: the virErrorDomain indicating where it's coming from * @code: the virErrorNumber code for the error * @level: the virErrorLevel for the error @@ -310,7 +313,7 @@ virDefaultErrorFunc(virErrorPtr err) * immediately if a callback is found and store it for later handling. */ void -__virRaiseError(virConnectPtr conn, virDomainPtr dom, +__virRaiseError(virConnectPtr conn, virDomainPtr dom, virNetworkPtr net, int domain, int code, virErrorLevel level, const char *str1, const char *str2, const char *str3, int int1, int int2, const char *msg, ...) @@ -349,6 +352,7 @@ __virRaiseError(virConnectPtr conn, virD virResetError(to); to->conn = conn; to->dom = dom; + to->net = net; to->domain = domain; to->code = code; to->message = str; Index: libvirt/src/xen_internal.c =================================================================== --- libvirt.orig/src/xen_internal.c +++ libvirt/src/xen_internal.c @@ -476,7 +476,7 @@ virXenError(virErrorNumber error, const return; errmsg = __virErrorMsg(error, info); - __virRaiseError(NULL, NULL, VIR_FROM_XEN, error, VIR_ERR_ERROR, + __virRaiseError(NULL, NULL, NULL, VIR_FROM_XEN, error, VIR_ERR_ERROR, errmsg, info, NULL, value, 0, errmsg, info, value); } Index: libvirt/src/xend_internal.c =================================================================== --- libvirt.orig/src/xend_internal.c +++ libvirt/src/xend_internal.c @@ -153,7 +153,7 @@ virXendError(virConnectPtr conn, virErro return; errmsg = __virErrorMsg(error, info); - __virRaiseError(conn, NULL, VIR_FROM_XEND, error, VIR_ERR_ERROR, + __virRaiseError(conn, NULL, NULL, VIR_FROM_XEND, error, VIR_ERR_ERROR, errmsg, info, NULL, 0, 0, errmsg, info); } @@ -174,7 +174,7 @@ virXendErrorInt(virConnectPtr conn, virE return; errmsg = __virErrorMsg(error, NULL); - __virRaiseError(conn, NULL, VIR_FROM_XEND, error, VIR_ERR_ERROR, + __virRaiseError(conn, NULL, NULL, VIR_FROM_XEND, error, VIR_ERR_ERROR, errmsg, NULL, NULL, val, 0, errmsg, val); } Index: libvirt/src/xm_internal.c =================================================================== --- libvirt.orig/src/xm_internal.c +++ libvirt/src/xm_internal.c @@ -116,7 +116,7 @@ xenXMError(virConnectPtr conn, virErrorN return; errmsg = __virErrorMsg(error, info); - __virRaiseError(conn, NULL, VIR_FROM_XEND, error, VIR_ERR_ERROR, + __virRaiseError(conn, NULL, NULL, VIR_FROM_XEND, error, VIR_ERR_ERROR, errmsg, info, NULL, 0, 0, errmsg, info); } Index: libvirt/src/xml.c =================================================================== --- libvirt.orig/src/xml.c +++ libvirt/src/xml.c @@ -34,7 +34,7 @@ virXMLError(virErrorNumber error, const return; errmsg = __virErrorMsg(error, info); - __virRaiseError(NULL, NULL, VIR_FROM_XML, error, VIR_ERR_ERROR, + __virRaiseError(NULL, NULL, NULL, VIR_FROM_XML, error, VIR_ERR_ERROR, errmsg, info, NULL, value, 0, errmsg, info, value); } Index: libvirt/src/xmlrpc.c =================================================================== --- libvirt.orig/src/xmlrpc.c +++ libvirt/src/xmlrpc.c @@ -39,7 +39,7 @@ static void xmlRpcError(virErrorNumber e return; errmsg = __virErrorMsg(error, info); - __virRaiseError(NULL, NULL, VIR_FROM_RPC, error, VIR_ERR_ERROR, + __virRaiseError(NULL, NULL, NULL, VIR_FROM_RPC, error, VIR_ERR_ERROR, errmsg, info, NULL, value, 0, errmsg, info, value); } Index: libvirt/src/xs_internal.c =================================================================== --- libvirt.orig/src/xs_internal.c +++ libvirt/src/xs_internal.c @@ -106,7 +106,7 @@ virXenStoreError(virConnectPtr conn, vir return; errmsg = __virErrorMsg(error, info); - __virRaiseError(conn, NULL, VIR_FROM_XENSTORE, error, VIR_ERR_ERROR, + __virRaiseError(conn, NULL, NULL, VIR_FROM_XENSTORE, error, VIR_ERR_ERROR, errmsg, info, NULL, 0, 0, errmsg, info); } --

Implement the virsh net-* commands. These too mirror the domain commands. Signed-off-by: Mark McLoughlin <markmc@redhat.com> Index: libvirt-foo/src/virsh.c =================================================================== --- libvirt-foo.orig/src/virsh.c 2007-02-14 16:04:55.000000000 +0000 +++ libvirt-foo.orig/src/virsh.c 2007-02-14 16:04:55.000000000 +0000 @@ -10,7 +10,7 @@ * Daniel P. Berrange <berrange@redhat.com> * * - * $Id: virsh.c,v 1.50 2007/02/14 16:04:55 markmc Exp $ + * $Id: virsh.c,v 1.51 2007/02/14 15:44:58 markmc Exp $ */ #define _GNU_SOURCE /* isblank() */ @@ -206,6 +206,14 @@ static virDomainPtr vshCommandOptDomainB vshCommandOptDomainBy(_ctl, _cmd, _optname, _name, \ VSH_BYID|VSH_BYUUID|VSH_BYNAME) +static virNetworkPtr vshCommandOptNetworkBy(vshControl * ctl, vshCmd * cmd, + const char *optname, char **name, int flag); + +/* default is lookup by Name and UUID */ +#define vshCommandOptNetwork(_ctl, _cmd, _optname, _name) \ + vshCommandOptNetworkBy(_ctl, _cmd, _optname, _name, \ + VSH_BYUUID|VSH_BYNAME) + static void vshPrintExtra(vshControl * ctl, const char *format, ...); static void vshDebug(vshControl * ctl, int level, const char *format, ...); @@ -1626,6 +1634,459 @@ cmdDomuuid(vshControl * ctl, vshCmd * cm /* + * "net-create" command + */ +static vshCmdInfo info_network_create[] = { + {"syntax", "create a network from an XML <file>"}, + {"help", gettext_noop("create a network from an XML file")}, + {"desc", gettext_noop("Create a network.")}, + {NULL, NULL} +}; + +static vshCmdOptDef opts_network_create[] = { + {"file", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("file containing an XML network description")}, + {NULL, 0, 0, NULL} +}; + +static int +cmdNetworkCreate(vshControl * ctl, vshCmd * cmd) +{ + virNetworkPtr network; + char *from; + int found; + int ret = TRUE; + char buffer[BUFSIZ]; + int fd, l; + + if (!vshConnectionUsability(ctl, ctl->conn, TRUE)) + return FALSE; + + from = vshCommandOptString(cmd, "file", &found); + if (!found) + return FALSE; + + fd = open(from, O_RDONLY); + if (fd < 0) { + vshError(ctl, FALSE, _("Failed to read description file %s"), from); + return(FALSE); + } + l = read(fd, &buffer[0], sizeof(buffer)); + if ((l <= 0) || (l >= (int) sizeof(buffer))) { + vshError(ctl, FALSE, _("Failed to read description file %s"), from); + close(fd); + return(FALSE); + } + buffer[l] = 0; + network = virNetworkCreateXML(ctl->conn, &buffer[0]); + if (network != NULL) { + vshPrint(ctl, _("Network %s created from %s\n"), + virNetworkGetName(network), from); + } else { + vshError(ctl, FALSE, _("Failed to create network from %s"), from); + ret = FALSE; + } + return ret; +} + + +/* + * "net-define" command + */ +static vshCmdInfo info_network_define[] = { + {"syntax", "define a network from an XML <file>"}, + {"help", gettext_noop("define (but don't start) a network from an XML file")}, + {"desc", gettext_noop("Define a network.")}, + {NULL, NULL} +}; + +static vshCmdOptDef opts_network_define[] = { + {"file", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("file conatining an XML network description")}, + {NULL, 0, 0, NULL} +}; + +static int +cmdNetworkDefine(vshControl * ctl, vshCmd * cmd) +{ + virNetworkPtr network; + char *from; + int found; + int ret = TRUE; + char buffer[BUFSIZ]; + int fd, l; + + if (!vshConnectionUsability(ctl, ctl->conn, TRUE)) + return FALSE; + + from = vshCommandOptString(cmd, "file", &found); + if (!found) + return FALSE; + + fd = open(from, O_RDONLY); + if (fd < 0) { + vshError(ctl, FALSE, _("Failed to read description file %s"), from); + return(FALSE); + } + l = read(fd, &buffer[0], sizeof(buffer)); + if ((l <= 0) || (l >= (int) sizeof(buffer))) { + vshError(ctl, FALSE, _("Failed to read description file %s"), from); + close(fd); + return(FALSE); + } + buffer[l] = 0; + network = virNetworkDefineXML(ctl->conn, &buffer[0]); + if (network != NULL) { + vshPrint(ctl, _("Network %s defined from %s\n"), + virNetworkGetName(network), from); + } else { + vshError(ctl, FALSE, _("Failed to define network from %s"), from); + ret = FALSE; + } + return ret; +} + + +/* + * "net-destroy" command + */ +static vshCmdInfo info_network_destroy[] = { + {"syntax", "net-destroy <network>"}, + {"help", gettext_noop("destroy a network")}, + {"desc", gettext_noop("Destroy a given network.")}, + {NULL, NULL} +}; + +static vshCmdOptDef opts_network_destroy[] = { + {"network", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("network name, id or uuid")}, + {NULL, 0, 0, NULL} +}; + +static int +cmdNetworkDestroy(vshControl * ctl, vshCmd * cmd) +{ + virNetworkPtr network; + int ret = TRUE; + char *name; + + if (!vshConnectionUsability(ctl, ctl->conn, TRUE)) + return FALSE; + + if (!(network = vshCommandOptNetwork(ctl, cmd, "network", &name))) + return FALSE; + + if (virNetworkDestroy(network) == 0) { + vshPrint(ctl, _("Network %s destroyed\n"), name); + } else { + vshError(ctl, FALSE, _("Failed to destroy network %s"), name); + ret = FALSE; + virNetworkFree(network); + } + + return ret; +} + + +/* + * "net-dumpxml" command + */ +static vshCmdInfo info_network_dumpxml[] = { + {"syntax", "net-dumpxml <name>"}, + {"help", gettext_noop("network information in XML")}, + {"desc", gettext_noop("Ouput the network information as an XML dump to stdout.")}, + {NULL, NULL} +}; + +static vshCmdOptDef opts_network_dumpxml[] = { + {"network", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("network name, id or uuid")}, + {NULL, 0, 0, NULL} +}; + +static int +cmdNetworkDumpXML(vshControl * ctl, vshCmd * cmd) +{ + virNetworkPtr network; + int ret = TRUE; + char *dump; + + if (!vshConnectionUsability(ctl, ctl->conn, TRUE)) + return FALSE; + + if (!(network = vshCommandOptNetwork(ctl, cmd, "network", NULL))) + return FALSE; + + dump = virNetworkGetXMLDesc(network, 0); + if (dump != NULL) { + printf("%s", dump); + free(dump); + } else { + ret = FALSE; + } + + virNetworkFree(network); + return ret; +} + + +/* + * "net-list" command + */ +static vshCmdInfo info_network_list[] = { + {"syntax", "net-list [ --inactive | --all ]"}, + {"help", gettext_noop("list networks")}, + {"desc", gettext_noop("Returns list of networks.")}, + {NULL, NULL} +}; + +static vshCmdOptDef opts_network_list[] = { + {"inactive", VSH_OT_BOOL, 0, gettext_noop("list inactive networks")}, + {"all", VSH_OT_BOOL, 0, gettext_noop("list inactive & active networks")}, + {NULL, 0, 0, NULL} +}; + +static int +cmdNetworkList(vshControl * ctl, vshCmd * cmd ATTRIBUTE_UNUSED) +{ + int inactive = vshCommandOptBool(cmd, "inactive"); + int all = vshCommandOptBool(cmd, "all"); + int active = !inactive || all ? 1 : 0; + int maxactive = 0, maxinactive = 0, i; + const char **activeNames = NULL, **inactiveNames = NULL; + inactive |= all; + + if (!vshConnectionUsability(ctl, ctl->conn, TRUE)) + return FALSE; + + if (active) { + maxactive = virConnectNumOfNetworks(ctl->conn); + if (maxactive < 0) { + vshError(ctl, FALSE, _("Failed to list active networks")); + return FALSE; + } + if (maxactive) { + activeNames = vshMalloc(ctl, sizeof(int) * maxactive); + + if ((maxactive = virConnectListNetworks(ctl->conn, &activeNames[0], maxactive)) < 0) { + vshError(ctl, FALSE, _("Failed to list active networks")); + free(activeNames); + return FALSE; + } + + qsort(&activeNames[0], maxactive, sizeof(int), namesorter); + } + } + if (inactive) { + maxinactive = virConnectNumOfDefinedNetworks(ctl->conn); + if (maxinactive < 0) { + vshError(ctl, FALSE, _("Failed to list inactive networks")); + if (activeNames) + free(activeNames); + return FALSE; + } + if (maxinactive) { + inactiveNames = vshMalloc(ctl, sizeof(char *) * maxinactive); + + if ((maxinactive = virConnectListDefinedNetworks(ctl->conn, inactiveNames, maxinactive)) < 0) { + vshError(ctl, FALSE, _("Failed to list inactive networks")); + if (activeNames) + free(activeNames); + free(inactiveNames); + return FALSE; + } + + qsort(&inactiveNames[0], maxinactive, sizeof(char*), namesorter); + } + } + vshPrintExtra(ctl, "%-20s\n", _("Name")); + vshPrintExtra(ctl, "----------------------------------\n"); + + for (i = 0; i < maxactive; i++) { + virNetworkPtr network = virNetworkLookupByName(ctl->conn, activeNames[i]); + + /* this kind of work with networks is not atomic operation */ + if (!network) { + free(activeNames[i]); + continue; + } + + vshPrint(ctl, "%-20s\n", + virNetworkGetName(network)); + virNetworkFree(network); + free(activeNames[i]); + } + for (i = 0; i < maxinactive; i++) { + virNetworkPtr network = virNetworkLookupByName(ctl->conn, inactiveNames[i]); + + /* this kind of work with networks is not atomic operation */ + if (!network) { + free(inactiveNames[i]); + continue; + } + + vshPrint(ctl, "%-20s\n", + inactiveNames[i]); + + virNetworkFree(network); + free(inactiveNames[i]); + } + if (activeNames) + free(activeNames); + if (inactiveNames) + free(inactiveNames); + return TRUE; +} + + +/* + * "net-name" command + */ +static vshCmdInfo info_network_name[] = { + {"syntax", "net-name <network>"}, + {"help", gettext_noop("convert a network UUID to network name")}, + {NULL, NULL} +}; + +static vshCmdOptDef opts_network_name[] = { + {"network", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("network uuid")}, + {NULL, 0, 0, NULL} +}; + +static int +cmdNetworkName(vshControl * ctl, vshCmd * cmd) +{ + virNetworkPtr network; + + if (!vshConnectionUsability(ctl, ctl->conn, TRUE)) + return FALSE; + if (!(network = vshCommandOptNetworkBy(ctl, cmd, "network", NULL, + VSH_BYUUID))) + return FALSE; + + vshPrint(ctl, "%s\n", virNetworkGetName(network)); + virNetworkFree(network); + return TRUE; +} + + +/* + * "net-start" command + */ +static vshCmdInfo info_network_start[] = { + {"syntax", "start <network>"}, + {"help", gettext_noop("start a (previously defined) inactive network")}, + {"desc", gettext_noop("Start a network.")}, + {NULL, NULL} +}; + +static vshCmdOptDef opts_network_start[] = { + {"name", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("name of the inactive network")}, + {NULL, 0, 0, NULL} +}; + +static int +cmdNetworkStart(vshControl * ctl, vshCmd * cmd) +{ + virNetworkPtr network; + char *name; + int found; + int ret = TRUE; + + if (!vshConnectionUsability(ctl, ctl->conn, TRUE)) + return FALSE; + + name = vshCommandOptString(cmd, "name", &found); + if (!found) + return FALSE; + + network = virNetworkLookupByName(ctl->conn, name); + if (!network) + return FALSE; + + if (virNetworkCreate(network) == 0) { + vshPrint(ctl, _("Network %s started\n"), + name); + } else { + vshError(ctl, FALSE, _("Failed to start network %s"), name); + ret = FALSE; + } + return ret; +} + + +/* + * "net-undefine" command + */ +static vshCmdInfo info_network_undefine[] = { + {"syntax", "net-undefine <network>"}, + {"help", gettext_noop("undefine an inactive network")}, + {"desc", gettext_noop("Undefine the configuration for an inactive network.")}, + {NULL, NULL} +}; + +static vshCmdOptDef opts_network_undefine[] = { + {"network", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("network name or uuid")}, + {NULL, 0, 0, NULL} +}; + +static int +cmdNetworkUndefine(vshControl * ctl, vshCmd * cmd) +{ + virNetworkPtr network; + int ret = TRUE; + char *name; + + if (!vshConnectionUsability(ctl, ctl->conn, TRUE)) + return FALSE; + + if (!(network = vshCommandOptNetwork(ctl, cmd, "network", &name))) + return FALSE; + + if (virNetworkUndefine(network) == 0) { + vshPrint(ctl, _("Network %s has been undefined\n"), name); + } else { + vshError(ctl, FALSE, _("Failed to undefine network %s"), name); + ret = FALSE; + } + + return ret; +} + + +/* + * "net-uuid" command + */ +static vshCmdInfo info_network_uuid[] = { + {"syntax", "net-uuid <network>"}, + {"help", gettext_noop("convert a network name to network UUID")}, + {NULL, NULL} +}; + +static vshCmdOptDef opts_network_uuid[] = { + {"network", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("network name")}, + {NULL, 0, 0, NULL} +}; + +static int +cmdNetworkUuid(vshControl * ctl, vshCmd * cmd) +{ + virNetworkPtr network; + char uuid[VIR_UUID_STRING_BUFLEN]; + + if (!vshConnectionUsability(ctl, ctl->conn, TRUE)) + return FALSE; + + if (!(network = vshCommandOptNetworkBy(ctl, cmd, "network", NULL, + VSH_BYNAME))) + return FALSE; + + if (virNetworkGetUUIDString(network, uuid) != -1) + vshPrint(ctl, "%s\n", uuid); + else + vshError(ctl, FALSE, _("failed to get network UUID")); + + return TRUE; +} + + +/* * "version" command */ static vshCmdInfo info_version[] = { @@ -1821,6 +2282,15 @@ static vshCmdDef commands[] = { {"dumpxml", cmdDumpXML, opts_dumpxml, info_dumpxml}, {"help", cmdHelp, opts_help, info_help}, {"list", cmdList, opts_list, info_list}, + {"net-create", cmdNetworkCreate, opts_network_create, info_network_create}, + {"net-define", cmdNetworkDefine, opts_network_define, info_network_define}, + {"net-destroy", cmdNetworkDestroy, opts_network_destroy, info_network_destroy}, + {"net-dumpxml", cmdNetworkDumpXML, opts_network_dumpxml, info_network_dumpxml}, + {"net-list", cmdNetworkList, opts_network_list, info_network_list}, + {"net-name", cmdNetworkName, opts_network_name, info_network_name}, + {"net-start", cmdNetworkStart, opts_network_start, info_network_start}, + {"net-undefine", cmdNetworkUndefine, opts_network_undefine, info_network_undefine}, + {"net-uuid", cmdNetworkUuid, opts_network_uuid, info_network_uuid}, {"nodeinfo", cmdNodeinfo, NULL, info_nodeinfo}, {"quit", cmdQuit, NULL, info_quit}, {"reboot", cmdReboot, opts_reboot, info_reboot}, @@ -2118,6 +2588,43 @@ vshCommandOptDomainBy(vshControl * ctl, return dom; } +static virNetworkPtr +vshCommandOptNetworkBy(vshControl * ctl, vshCmd * cmd, const char *optname, + char **name, int flag) +{ + virNetworkPtr network = NULL; + char *n; + + if (!(n = vshCommandOptString(cmd, optname, NULL))) { + vshError(ctl, FALSE, _("undefined network name")); + return NULL; + } + + vshDebug(ctl, 5, "%s: found option <%s>: %s\n", + cmd->def->name, optname, n); + + if (name) + *name = n; + + /* try it by UUID */ + if (network==NULL && (flag & VSH_BYUUID) && strlen(n)==VIR_UUID_STRING_BUFLEN-1) { + vshDebug(ctl, 5, "%s: <%s> tring as network UUID\n", + cmd->def->name, optname); + network = virNetworkLookupByUUIDString(ctl->conn, n); + } + /* try it by NAME */ + if (network==NULL && (flag & VSH_BYNAME)) { + vshDebug(ctl, 5, "%s: <%s> tring as network NAME\n", + cmd->def->name, optname); + network = virNetworkLookupByName(ctl->conn, n); + } + + if (!network) + vshError(ctl, FALSE, _("failed to get network '%s'"), n); + + return network; +} + /* * Executes command(s) and returns return code from last command */ --

Hooks up the networking API methods in the qemu driver and extends the protocol to support the methods. Also, implements the protocol in qemud, but just hooks it up to driver stubs. Note, we share the connection to qemud with the qemud domains driver. Signed-off-by: Mark McLoughlin <markmc@redhat.com> Index: libvirt-foo/qemud/dispatch.c =================================================================== --- libvirt-foo.orig/qemud/dispatch.c 2007-02-15 08:39:58.000000000 +0000 +++ libvirt-foo.orig/qemud/dispatch.c 2007-02-15 08:39:58.000000000 +0000 @@ -462,6 +462,243 @@ static int qemudDispatchDomainUndefine(s return 0; } +static int qemudDispatchNumNetworks(struct qemud_server *server, struct qemud_client *client, + struct qemud_packet *in, struct qemud_packet *out) { + if (in->header.dataSize != 0) + return -1; + + int nnetworks = qemudNumNetworks(server); + if (nnetworks < 0) { + if (qemudDispatchFailure(server, client, out) < 0) + return -1; + } else { + out->header.type = QEMUD_PKT_NUM_NETWORKS; + out->header.dataSize = sizeof(out->data.numNetworksReply); + out->data.numNetworksReply.numNetworks = nnetworks; + } + return 0; +} + +static int qemudDispatchListNetworks(struct qemud_server *server, struct qemud_client *client, + struct qemud_packet *in, struct qemud_packet *out) { + char **names; + int i; + if (in->header.dataSize != 0) + return -1; + + if (!(names = malloc(sizeof(char *)*QEMUD_MAX_NUM_NETWORKS))) + return -1; + + for (i = 0 ; i < QEMUD_MAX_NUM_NETWORKS ; i++) { + names[i] = out->data.listNetworksReply.networks[i]; + } + + int nnetworks = qemudListNetworks(server, + names, + QEMUD_MAX_NUM_NETWORKS); + free(names); + if (nnetworks < 0) { + if (qemudDispatchFailure(server, client, out) < 0) + return -1; + } else { + out->header.type = QEMUD_PKT_LIST_NETWORKS; + out->header.dataSize = sizeof(out->data.listNetworksReply); + out->data.listNetworksReply.numNetworks = nnetworks; + } + return 0; +} + +static int qemudDispatchNumDefinedNetworks(struct qemud_server *server, struct qemud_client *client, + struct qemud_packet *in, struct qemud_packet *out) { + if (in->header.dataSize != 0) + return -1; + + int nnetworks = qemudNumDefinedNetworks(server); + if (nnetworks < 0) { + if (qemudDispatchFailure(server, client, out) < 0) + return -1; + } else { + out->header.type = QEMUD_PKT_NUM_DEFINED_NETWORKS; + out->header.dataSize = sizeof(out->data.numDefinedNetworksReply); + out->data.numDefinedNetworksReply.numNetworks = nnetworks; + } + return 0; +} + +static int qemudDispatchListDefinedNetworks(struct qemud_server *server, struct qemud_client *client, + struct qemud_packet *in, struct qemud_packet *out) { + char **names; + int i; + if (in->header.dataSize != 0) + return -1; + + if (!(names = malloc(sizeof(char *)*QEMUD_MAX_NUM_NETWORKS))) + return -1; + + for (i = 0 ; i < QEMUD_MAX_NUM_NETWORKS ; i++) { + names[i] = out->data.listDefinedNetworksReply.networks[i]; + } + + int nnetworks = qemudListDefinedNetworks(server, + names, + QEMUD_MAX_NUM_NETWORKS); + free(names); + if (nnetworks < 0) { + if (qemudDispatchFailure(server, client, out) < 0) + return -1; + } else { + out->header.type = QEMUD_PKT_LIST_DEFINED_NETWORKS; + out->header.dataSize = sizeof(out->data.listDefinedNetworksReply); + out->data.listDefinedNetworksReply.numNetworks = nnetworks; + } + return 0; +} + +static int qemudDispatchNetworkLookupByName(struct qemud_server *server, struct qemud_client *client, + struct qemud_packet *in, struct qemud_packet *out) { + if (in->header.dataSize != sizeof(in->data.networkLookupByNameRequest)) + return -1; + + /* Paranoia NULL termination */ + in->data.networkLookupByNameRequest.name[QEMUD_MAX_NAME_LEN-1] = '\0'; + struct qemud_network *network = qemudFindNetworkByName(server, in->data.networkLookupByNameRequest.name); + if (!network) { + if (qemudDispatchFailure(server, client, out) < 0) + return -1; + } else { + out->header.type = QEMUD_PKT_NETWORK_LOOKUP_BY_NAME; + out->header.dataSize = sizeof(out->data.networkLookupByNameReply); + memcpy(out->data.networkLookupByNameReply.uuid, network->def.uuid, QEMUD_UUID_RAW_LEN); + } + return 0; +} + +static int qemudDispatchNetworkLookupByUUID(struct qemud_server *server, struct qemud_client *client, + struct qemud_packet *in, struct qemud_packet *out) { + if (in->header.dataSize != sizeof(in->data.networkLookupByUUIDRequest)) + return -1; + + struct qemud_network *network = qemudFindNetworkByUUID(server, in->data.networkLookupByUUIDRequest.uuid); + if (!network) { + if (qemudDispatchFailure(server, client, out) < 0) + return -1; + } else { + out->header.type = QEMUD_PKT_NETWORK_LOOKUP_BY_UUID; + out->header.dataSize = sizeof(out->data.networkLookupByUUIDReply); + strncpy(out->data.networkLookupByUUIDReply.name, network->def.name, QEMUD_MAX_NAME_LEN-1); + out->data.networkLookupByUUIDReply.name[QEMUD_MAX_NAME_LEN-1] = '\0'; + } + return 0; +} + +static int qemudDispatchNetworkCreate(struct qemud_server *server, struct qemud_client *client, + struct qemud_packet *in, struct qemud_packet *out) { + if (in->header.dataSize != sizeof(in->data.networkCreateRequest)) + return -1; + + in->data.networkCreateRequest.xml[QEMUD_MAX_XML_LEN-1] ='\0'; + + struct qemud_network *network = qemudNetworkCreate(server, in->data.networkCreateRequest.xml); + if (!network) { + if (qemudDispatchFailure(server, client, out) < 0) + return -1; + } else { + out->header.type = QEMUD_PKT_NETWORK_CREATE; + out->header.dataSize = sizeof(out->data.networkCreateReply); + memcpy(out->data.networkCreateReply.uuid, network->def.uuid, QEMUD_UUID_RAW_LEN); + strncpy(out->data.networkCreateReply.name, network->def.name, QEMUD_MAX_NAME_LEN-1); + out->data.networkCreateReply.name[QEMUD_MAX_NAME_LEN-1] = '\0'; + } + return 0; +} + +static int qemudDispatchNetworkDefine(struct qemud_server *server, struct qemud_client *client, + struct qemud_packet *in, struct qemud_packet *out) { + if (in->header.dataSize != sizeof(in->data.networkDefineRequest)) + return -1; + + in->data.networkDefineRequest.xml[QEMUD_MAX_XML_LEN-1] ='\0'; + + struct qemud_network *network = qemudNetworkDefine(server, in->data.networkDefineRequest.xml); + if (!network) { + if (qemudDispatchFailure(server, client, out) < 0) + return -1; + } else { + out->header.type = QEMUD_PKT_NETWORK_DEFINE; + out->header.dataSize = sizeof(out->data.networkDefineReply); + memcpy(out->data.networkDefineReply.uuid, network->def.uuid, QEMUD_UUID_RAW_LEN); + strncpy(out->data.networkDefineReply.name, network->def.name, QEMUD_MAX_NAME_LEN-1); + out->data.networkDefineReply.name[QEMUD_MAX_NAME_LEN-1] = '\0'; + } + return 0; +} + +static int qemudDispatchNetworkUndefine(struct qemud_server *server, struct qemud_client *client, + struct qemud_packet *in, struct qemud_packet *out) { + if (in->header.dataSize != sizeof(in->data.networkUndefineRequest)) + return -1; + + int ret = qemudNetworkUndefine(server, in->data.networkUndefineRequest.uuid); + if (ret < 0) { + if (qemudDispatchFailure(server, client, out) < 0) + return -1; + } else { + out->header.type = QEMUD_PKT_NETWORK_UNDEFINE; + out->header.dataSize = 0; + } + return 0; +} + +static int qemudDispatchNetworkStart(struct qemud_server *server, struct qemud_client *client, + struct qemud_packet *in, struct qemud_packet *out) { + if (in->header.dataSize != sizeof(in->data.networkStartRequest)) + return -1; + + struct qemud_network *network = qemudFindNetworkByUUID(server, in->data.networkStartRequest.uuid); + if (!network || qemudNetworkStart(server, network) < 0) { + if (qemudDispatchFailure(server, client, out) < 0) + return -1; + } else { + out->header.type = QEMUD_PKT_NETWORK_START; + out->header.dataSize = 0; + } + return 0; +} + +static int qemudDispatchNetworkDestroy(struct qemud_server *server, struct qemud_client *client, + struct qemud_packet *in, struct qemud_packet *out) { + if (in->header.dataSize != sizeof(in->data.networkDestroyRequest)) + return -1; + + int ret = qemudNetworkDestroy(server, in->data.networkDestroyRequest.uuid); + if (ret < 0) { + if (qemudDispatchFailure(server, client, out) < 0) + return -1; + } else { + out->header.type = QEMUD_PKT_NETWORK_DESTROY; + out->header.dataSize = 0; + } + return 0; +} + +static int qemudDispatchNetworkDumpXML(struct qemud_server *server, struct qemud_client *client, + struct qemud_packet *in, struct qemud_packet *out) { + if (in->header.dataSize != sizeof(in->data.networkDumpXMLRequest)) + return -1; + + int ret = qemudNetworkDumpXML(server, + in->data.networkDumpXMLRequest.uuid, + out->data.networkDumpXMLReply.xml, QEMUD_MAX_XML_LEN); + if (ret < 0) { + if (qemudDispatchFailure(server, client, out) < 0) + return -1; + } else { + out->header.type = QEMUD_PKT_NETWORK_DUMP_XML; + out->header.dataSize = sizeof(out->data.networkDumpXMLReply); + } + return 0; +} + typedef int (*clientFunc)(struct qemud_server *server, struct qemud_client *client, struct qemud_packet *in, struct qemud_packet *out); @@ -490,7 +727,19 @@ clientFunc funcsTransmitRW[QEMUD_PKT_MAX qemudDispatchNumDefinedDomains, qemudDispatchDomainStart, qemudDispatchDomainDefine, - qemudDispatchDomainUndefine + qemudDispatchDomainUndefine, + qemudDispatchNumNetworks, + qemudDispatchListNetworks, + qemudDispatchNumDefinedNetworks, + qemudDispatchListDefinedNetworks, + qemudDispatchNetworkLookupByUUID, + qemudDispatchNetworkLookupByName, + qemudDispatchNetworkCreate, + qemudDispatchNetworkDefine, + qemudDispatchNetworkUndefine, + qemudDispatchNetworkStart, + qemudDispatchNetworkDestroy, + qemudDispatchNetworkDumpXML, }; clientFunc funcsTransmitRO[QEMUD_PKT_MAX] = { @@ -515,6 +764,18 @@ clientFunc funcsTransmitRO[QEMUD_PKT_MAX NULL, NULL, NULL, + qemudDispatchNumNetworks, + qemudDispatchListNetworks, + qemudDispatchNumDefinedNetworks, + qemudDispatchListDefinedNetworks, + qemudDispatchNetworkLookupByUUID, + qemudDispatchNetworkLookupByName, + NULL, + NULL, + NULL, + NULL, + NULL, + qemudDispatchNetworkDumpXML, }; /* Index: libvirt-foo/qemud/driver.c =================================================================== --- libvirt-foo.orig/qemud/driver.c 2007-02-15 08:39:58.000000000 +0000 +++ libvirt-foo.orig/qemud/driver.c 2007-02-15 08:39:58.000000000 +0000 @@ -545,6 +545,70 @@ int qemudDomainUndefine(struct qemud_ser return 0; } +struct qemud_network *qemudFindNetworkByUUID(const struct qemud_server *server, + const unsigned char *uuid) { + server = NULL; uuid = NULL; + return NULL; +} + +struct qemud_network *qemudFindNetworkByName(const struct qemud_server *server, + const char *name) { + server = NULL; name = NULL; + return NULL; +} + +int qemudNumNetworks(struct qemud_server *server) { + server = NULL; + return 0; +} + +int qemudListNetworks(struct qemud_server *server, char *const*names, int nnames) { + server = NULL; names = NULL; nnames = 0; + return 0; +} + +int qemudNumDefinedNetworks(struct qemud_server *server) { + server = NULL; + return 0; +} + +int qemudListDefinedNetworks(struct qemud_server *server, char *const*names, int nnames) { + server = NULL; names = NULL; nnames = 0; + return 0; +} + +struct qemud_network *qemudNetworkCreate(struct qemud_server *server, const char *xml) { + server = NULL; xml = NULL; + return NULL; +} + +struct qemud_network *qemudNetworkDefine(struct qemud_server *server, const char *xml) { + server = NULL; xml = NULL; + return NULL; +} + +int qemudNetworkUndefine(struct qemud_server *server, const unsigned char *uuid) { + qemudReportError(server, VIR_ERR_INVALID_NETWORK, "no network with matching uuid"); + uuid = NULL; + return -1; +} + +int qemudNetworkStart(struct qemud_server *server, struct qemud_network *network) { + server = NULL; network = NULL; + return 1; +} + +int qemudNetworkDestroy(struct qemud_server *server, const unsigned char *uuid) { + uuid = NULL; + qemudReportError(server, VIR_ERR_INVALID_NETWORK, "no network with matching uuid"); + return -1; +} + +int qemudNetworkDumpXML(struct qemud_server *server, const unsigned char *uuid, char *xml, int xmllen) { + qemudReportError(server, VIR_ERR_INVALID_NETWORK, "no network with matching uuid"); + uuid = NULL; xml = NULL; xmllen = 0; + return -1; +} /* * Local variables: Index: libvirt-foo/qemud/driver.h =================================================================== --- libvirt-foo.orig/qemud/driver.h 2007-02-15 08:39:24.000000000 +0000 +++ libvirt-foo.orig/qemud/driver.h 2007-02-15 08:39:24.000000000 +0000 @@ -86,6 +86,34 @@ struct qemud_vm *qemudDomainDefine(struc int qemudDomainUndefine(struct qemud_server *server, const unsigned char *uuid); +struct qemud_network *qemudFindNetworkByUUID(const struct qemud_server *server, + const unsigned char *uuid); +struct qemud_network *qemudFindNetworkByName(const struct qemud_server *server, + const char *name); + +int qemudNumNetworks(struct qemud_server *server); +int qemudListNetworks(struct qemud_server *server, + char *const*names, + int nnames); +int qemudNumDefinedNetworks(struct qemud_server *server); +int qemudListDefinedNetworks(struct qemud_server *server, + char *const*names, + int nnames); +struct qemud_network *qemudNetworkCreate(struct qemud_server *server, + const char *xml); +struct qemud_network *qemudNetworkDefine(struct qemud_server *server, + const char *xml); +int qemudNetworkUndefine(struct qemud_server *server, + const unsigned char *uuid); +int qemudNetworkStart(struct qemud_server *server, + struct qemud_network *network); +int qemudNetworkDestroy(struct qemud_server *server, + const unsigned char *uuid); +int qemudNetworkDumpXML(struct qemud_server *server, + const unsigned char *uuid, + char *xml, + int xmllen); + #endif Index: libvirt-foo/qemud/internal.h =================================================================== --- libvirt-foo.orig/qemud/internal.h 2007-02-15 08:39:24.000000000 +0000 +++ libvirt-foo.orig/qemud/internal.h 2007-02-15 08:39:24.000000000 +0000 @@ -199,6 +199,18 @@ struct qemud_vm { struct qemud_vm *next; }; +/* Virtual Network main configuration */ +struct qemud_network_def { + unsigned char uuid[QEMUD_UUID_RAW_LEN]; + char name[QEMUD_MAX_NAME_LEN]; +}; + +/* Virtual Network runtime state */ +struct qemud_network { + struct qemud_network_def def; + struct qemud_network *next; +}; + /* Stores the per-client connection state */ struct qemud_client { int fd; Index: libvirt-foo/qemud/protocol.h =================================================================== --- libvirt-foo.orig/qemud/protocol.h 2007-02-15 08:39:24.000000000 +0000 +++ libvirt-foo.orig/qemud/protocol.h 2007-02-15 08:39:24.000000000 +0000 @@ -50,6 +50,18 @@ enum { QEMUD_PKT_DOMAIN_START, QEMUD_PKT_DOMAIN_DEFINE, QEMUD_PKT_DOMAIN_UNDEFINE, + QEMUD_PKT_NUM_NETWORKS, + QEMUD_PKT_LIST_NETWORKS, + QEMUD_PKT_NUM_DEFINED_NETWORKS, + QEMUD_PKT_LIST_DEFINED_NETWORKS, + QEMUD_PKT_NETWORK_LOOKUP_BY_UUID, + QEMUD_PKT_NETWORK_LOOKUP_BY_NAME, + QEMUD_PKT_NETWORK_CREATE, + QEMUD_PKT_NETWORK_DEFINE, + QEMUD_PKT_NETWORK_UNDEFINE, + QEMUD_PKT_NETWORK_START, + QEMUD_PKT_NETWORK_DESTROY, + QEMUD_PKT_NETWORK_DUMP_XML, QEMUD_PKT_MAX, } qemud_packet_type; @@ -62,6 +74,7 @@ enum { #define QEMUD_MAX_NAME_LEN 50 #define QEMUD_MAX_XML_LEN 4096 #define QEMUD_MAX_NUM_DOMAINS 100 +#define QEMUD_MAX_NUM_NETWORKS 100 #define QEMUD_MAX_ERROR_LEN 1024 /* Possible guest VM states */ @@ -200,6 +213,63 @@ union qemud_packet_data { struct { unsigned char uuid[QEMUD_UUID_RAW_LEN]; } domainUndefineRequest; + struct { + int32_t numNetworks; + } numNetworksReply; + struct { + int32_t numNetworks; + char networks[QEMUD_MAX_NUM_NETWORKS][QEMUD_MAX_NAME_LEN]; + } listNetworksReply; + struct { + int32_t numNetworks; + } numDefinedNetworksReply; + struct { + int32_t numNetworks; + char networks[QEMUD_MAX_NUM_NETWORKS][QEMUD_MAX_NAME_LEN]; + } listDefinedNetworksReply; + struct { + char name[QEMUD_MAX_NAME_LEN]; + } networkLookupByNameRequest; + struct { + int32_t id; + unsigned char uuid[QEMUD_UUID_RAW_LEN]; + } networkLookupByNameReply; + struct { + unsigned char uuid[QEMUD_UUID_RAW_LEN]; + } networkLookupByUUIDRequest; + struct { + int32_t id; + char name[QEMUD_MAX_NAME_LEN]; + } networkLookupByUUIDReply; + struct { + char xml[QEMUD_MAX_XML_LEN]; + } networkCreateRequest; + struct { + unsigned char uuid[QEMUD_UUID_RAW_LEN]; + char name[QEMUD_MAX_NAME_LEN]; + } networkCreateReply; + struct { + char xml[QEMUD_MAX_XML_LEN]; + } networkDefineRequest; + struct { + unsigned char uuid[QEMUD_UUID_RAW_LEN]; + char name[QEMUD_MAX_NAME_LEN]; + } networkDefineReply; + struct { + unsigned char uuid[QEMUD_UUID_RAW_LEN]; + } networkUndefineRequest; + struct { + unsigned char uuid[QEMUD_UUID_RAW_LEN]; + } networkStartRequest; + struct { + unsigned char uuid[QEMUD_UUID_RAW_LEN]; + } networkDestroyRequest; + struct { + unsigned char uuid[QEMUD_UUID_RAW_LEN]; + } networkDumpXMLRequest; + struct { + char xml[QEMUD_MAX_XML_LEN]; + } networkDumpXMLReply; }; /* Each packet has header & data */ Index: libvirt-foo/src/qemu_internal.c =================================================================== --- libvirt-foo.orig/src/qemu_internal.c 2007-02-15 08:39:59.000000000 +0000 +++ libvirt-foo.orig/src/qemu_internal.c 2007-02-15 08:39:59.000000000 +0000 @@ -806,6 +806,276 @@ static int qemuUndefine(virDomainPtr dom return ret; } +static int qemuNetworkOpen(virConnectPtr conn, + const char *name, + int flags) { + xmlURIPtr uri = NULL; + int ret = -1; + + if (conn->qemud_fd == -1) + return 0; + + if (name) + uri = xmlParseURI(name); + + if (uri && !strcmp(uri->scheme, "qemu")) + ret = qemuOpen(conn, name, flags); + else if (geteuid() == 0) + ret = qemuOpen(conn, "qemu:///system", flags); + else + ret = qemuOpen(conn, "qemu:///session", flags); + + if (uri) + xmlFreeURI(uri); + + return ret; +} + +static int qemuNumOfNetworks(virConnectPtr conn) { + struct qemud_packet req, reply; + + req.header.type = QEMUD_PKT_NUM_NETWORKS; + req.header.dataSize = 0; + + if (qemuProcessRequest(conn, NULL, &req, &reply) < 0) { + return -1; + } + + return reply.data.numNetworksReply.numNetworks; +} + +static int qemuListNetworks(virConnectPtr conn, + const char **names, + int maxnames) { + struct qemud_packet req, reply; + int i, nNetworks; + + req.header.type = QEMUD_PKT_LIST_NETWORKS; + req.header.dataSize = 0; + + if (qemuProcessRequest(conn, NULL, &req, &reply) < 0) { + return -1; + } + + nNetworks = reply.data.listNetworksReply.numNetworks; + if (nNetworks > maxnames) + return -1; + + for (i = 0 ; i < nNetworks ; i++) { + reply.data.listNetworksReply.networks[i][QEMUD_MAX_NAME_LEN-1] = '\0'; + names[i] = strdup(reply.data.listNetworksReply.networks[i]); + } + + return nNetworks; +} + +static int qemuNumOfDefinedNetworks(virConnectPtr conn) { + struct qemud_packet req, reply; + + req.header.type = QEMUD_PKT_NUM_DEFINED_NETWORKS; + req.header.dataSize = 0; + + if (qemuProcessRequest(conn, NULL, &req, &reply) < 0) { + return -1; + } + + return reply.data.numDefinedNetworksReply.numNetworks; +} + +static int qemuListDefinedNetworks(virConnectPtr conn, + const char **names, + int maxnames) { + struct qemud_packet req, reply; + int i, nNetworks; + + req.header.type = QEMUD_PKT_LIST_DEFINED_NETWORKS; + req.header.dataSize = 0; + + if (qemuProcessRequest(conn, NULL, &req, &reply) < 0) { + return -1; + } + + nNetworks = reply.data.listDefinedNetworksReply.numNetworks; + if (nNetworks > maxnames) + return -1; + + for (i = 0 ; i < nNetworks ; i++) { + reply.data.listDefinedNetworksReply.networks[i][QEMUD_MAX_NAME_LEN-1] = '\0'; + names[i] = strdup(reply.data.listDefinedNetworksReply.networks[i]); + } + + return nNetworks; +} + +static virNetworkPtr qemuNetworkLookupByUUID(virConnectPtr conn, + const unsigned char *uuid) { + struct qemud_packet req, reply; + virNetworkPtr network; + + req.header.type = QEMUD_PKT_NETWORK_LOOKUP_BY_UUID; + req.header.dataSize = sizeof(req.data.networkLookupByUUIDRequest); + memmove(req.data.networkLookupByUUIDRequest.uuid, uuid, QEMUD_UUID_RAW_LEN); + + if (qemuProcessRequest(conn, NULL, &req, &reply) < 0) { + return NULL; + } + + reply.data.networkLookupByUUIDReply.name[QEMUD_MAX_NAME_LEN-1] = '\0'; + + if (!(network = virGetNetwork(conn, + reply.data.networkLookupByUUIDReply.name, + uuid))) + return NULL; + + return network; +} + +static virNetworkPtr qemuNetworkLookupByName(virConnectPtr conn, + const char *name) { + struct qemud_packet req, reply; + virNetworkPtr network; + + if (strlen(name) > (QEMUD_MAX_NAME_LEN-1)) + return NULL; + + req.header.type = QEMUD_PKT_NETWORK_LOOKUP_BY_NAME; + req.header.dataSize = sizeof(req.data.networkLookupByNameRequest); + strcpy(req.data.networkLookupByNameRequest.name, name); + + if (qemuProcessRequest(conn, NULL, &req, &reply) < 0) { + return NULL; + } + + if (!(network = virGetNetwork(conn, + name, + reply.data.networkLookupByNameReply.uuid))) + return NULL; + + return network; +} + +static virNetworkPtr qemuNetworkCreateXML(virConnectPtr conn, + const char *xmlDesc) { + struct qemud_packet req, reply; + virNetworkPtr network; + int len = strlen(xmlDesc); + + if (len > (QEMUD_MAX_XML_LEN-1)) { + return NULL; + } + + req.header.type = QEMUD_PKT_NETWORK_CREATE; + req.header.dataSize = sizeof(req.data.networkCreateRequest); + strcpy(req.data.networkCreateRequest.xml, xmlDesc); + req.data.networkCreateRequest.xml[QEMUD_MAX_XML_LEN-1] = '\0'; + + if (qemuProcessRequest(conn, NULL, &req, &reply) < 0) { + return NULL; + } + + reply.data.networkCreateReply.name[QEMUD_MAX_NAME_LEN-1] = '\0'; + + if (!(network = virGetNetwork(conn, + reply.data.networkCreateReply.name, + reply.data.networkCreateReply.uuid))) + return NULL; + + return network; +} + + +static virNetworkPtr qemuNetworkDefineXML(virConnectPtr conn, + const char *xml) { + struct qemud_packet req, reply; + virNetworkPtr network; + int len = strlen(xml); + + if (len > (QEMUD_MAX_XML_LEN-1)) { + return NULL; + } + + req.header.type = QEMUD_PKT_NETWORK_DEFINE; + req.header.dataSize = sizeof(req.data.networkDefineRequest); + strcpy(req.data.networkDefineRequest.xml, xml); + req.data.networkDefineRequest.xml[QEMUD_MAX_XML_LEN-1] = '\0'; + + if (qemuProcessRequest(conn, NULL, &req, &reply) < 0) { + return NULL; + } + + reply.data.networkDefineReply.name[QEMUD_MAX_NAME_LEN-1] = '\0'; + + if (!(network = virGetNetwork(conn, + reply.data.networkDefineReply.name, + reply.data.networkDefineReply.uuid))) + return NULL; + + return network; +} + +static int qemuNetworkUndefine(virNetworkPtr network) { + struct qemud_packet req, reply; + int ret = 0; + + req.header.type = QEMUD_PKT_NETWORK_UNDEFINE; + req.header.dataSize = sizeof(req.data.networkUndefineRequest); + memcpy(req.data.networkUndefineRequest.uuid, network->uuid, QEMUD_UUID_RAW_LEN); + + if (qemuProcessRequest(network->conn, NULL, &req, &reply) < 0) { + ret = -1; + goto cleanup; + } + + cleanup: + if (virFreeNetwork(network->conn, network) < 0) + ret = -1; + + return ret; +} + +static int qemuNetworkCreate(virNetworkPtr network) { + struct qemud_packet req, reply; + + req.header.type = QEMUD_PKT_NETWORK_START; + req.header.dataSize = sizeof(req.data.networkStartRequest); + memcpy(req.data.networkStartRequest.uuid, network->uuid, QEMUD_UUID_RAW_LEN); + + if (qemuProcessRequest(network->conn, NULL, &req, &reply) < 0) { + return -1; + } + + return 0; +} + +static int qemuNetworkDestroy(virNetworkPtr network) { + struct qemud_packet req, reply; + + req.header.type = QEMUD_PKT_NETWORK_DESTROY; + req.header.dataSize = sizeof(req.data.networkDestroyRequest); + memcpy(req.data.networkDestroyRequest.uuid, network->uuid, QEMUD_UUID_RAW_LEN); + + if (qemuProcessRequest(network->conn, NULL, &req, &reply) < 0) { + return -1; + } + + return 0; +} + +static char * qemuNetworkDumpXML(virNetworkPtr network, int flags ATTRIBUTE_UNUSED) { + struct qemud_packet req, reply; + + req.header.type = QEMUD_PKT_NETWORK_DUMP_XML; + req.header.dataSize = sizeof(req.data.networkDumpXMLRequest); + memmove(req.data.networkDumpXMLRequest.uuid, network->uuid, QEMUD_UUID_RAW_LEN); + + if (qemuProcessRequest(network->conn, NULL, &req, &reply) < 0) { + return NULL; + } + + reply.data.networkDumpXMLReply.xml[QEMUD_MAX_XML_LEN-1] = '\0'; + + return strdup(reply.data.networkDumpXMLReply.xml); +} static virDriver qemuDriver = { VIR_DRV_QEMU, @@ -849,8 +1119,26 @@ static virDriver qemuDriver = { NULL, /* domainDetachDevice */ }; +static virNetworkDriver qemuNetworkDriver = { + qemuNetworkOpen, /* open */ + qemuClose, /* close */ + qemuNumOfNetworks, /* numOfNetworks */ + qemuListNetworks, /* listNetworks */ + qemuNumOfDefinedNetworks, /* numOfDefinedNetworks */ + qemuListDefinedNetworks, /* listDefinedNetworks */ + qemuNetworkLookupByUUID, /* networkLookupByUUID */ + qemuNetworkLookupByName, /* networkLookupByName */ + qemuNetworkCreateXML , /* networkCreateXML */ + qemuNetworkDefineXML , /* networkDefineXML */ + qemuNetworkUndefine, /* networkUndefine */ + qemuNetworkCreate, /* networkCreate */ + qemuNetworkDestroy, /* networkDestroy */ + qemuNetworkDumpXML, /* networkDumpXML */ +}; + void qemuRegister(void) { virRegisterDriver(&qemuDriver); + virRegisterNetworkDriver(&qemuNetworkDriver); } --

Implement the qemud's network stubs, but don't actually do anything when starting a network. Note, an example XML file would be: <network> <name>TestNetwork</name> <uuid>ac57b808-bfdd-4aa3-8da8-0bd4ac1faceb</uuid> <ip address="10.0.0.1" netmask="255.255.255.0"> <dhcp> <range start="10.0.0.16" end="10.0.0.32" /> <range start="10.0.0.128" end="10.0.0.254" /> </dhcp> </ip> </network> Signed-off-by: Mark McLoughlin <markmc@redhat.com> Index: libvirt-foo/qemud/conf.c =================================================================== --- libvirt-foo.orig/qemud/conf.c 2007-02-14 14:57:31.000000000 +0000 +++ libvirt-foo.orig/qemud/conf.c 2007-02-14 14:57:31.000000000 +0000 @@ -1098,17 +1098,186 @@ struct qemud_vm *qemudLoadConfigXML(stru } +void qemudFreeNetwork(struct qemud_network *network) { + free(network); +} + + +static int qemudSaveNetworkConfig(struct qemud_server *server, + struct qemud_network *network) { + char *xml; + int fd, ret = -1; + int towrite; + + if (!(xml = qemudGenerateNetworkXML(server, network))) { + return -1; + } + + if (qemudEnsureConfigDir(server, server->networkConfigDir) < 0) { + goto cleanup; + } + + if ((fd = open(network->configFile, + O_WRONLY | O_CREAT | O_TRUNC, + S_IRUSR | S_IWUSR )) < 0) { + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "cannot create config file %s", network->configFile); + goto cleanup; + } + + towrite = strlen(xml); + if (write(fd, xml, towrite) != towrite) { + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "cannot write config file %s", network->configFile); + goto cleanup; + } + + if (close(fd) < 0) { + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "cannot save config file %s", network->configFile); + goto cleanup; + } + + ret = 0; + + cleanup: + + free(xml); + + return ret; +} + + +static int qemudParseNetworkXML(struct qemud_server *server, + xmlDocPtr xml, + struct qemud_network *network) { + xmlNodePtr root = NULL; + xmlXPathContextPtr ctxt = NULL; + xmlXPathObjectPtr obj = NULL; + + /* Prepare parser / xpath context */ + root = xmlDocGetRootElement(xml); + if ((root == NULL) || (!xmlStrEqual(root->name, BAD_CAST "network"))) { + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "%s", "incorrect root element"); + goto error; + } + + ctxt = xmlXPathNewContext(xml); + if (ctxt == NULL) { + qemudReportError(server, VIR_ERR_NO_MEMORY, "xmlXPathContext"); + goto error; + } + + + /* Extract network name */ + obj = xmlXPathEval(BAD_CAST "string(/network/name[1])", ctxt); + if ((obj == NULL) || (obj->type != XPATH_STRING) || + (obj->stringval == NULL) || (obj->stringval[0] == 0)) { + qemudReportError(server, VIR_ERR_NO_NAME, NULL); + goto error; + } + if (strlen((const char *)obj->stringval) >= (QEMUD_MAX_NAME_LEN-1)) { + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "%s", "network name length too long"); + goto error; + } + strcpy(network->def.name, (const char *)obj->stringval); + xmlXPathFreeObject(obj); + + + /* Extract network uuid */ + obj = xmlXPathEval(BAD_CAST "string(/network/uuid[1])", ctxt); + if ((obj == NULL) || (obj->type != XPATH_STRING) || + (obj->stringval == NULL) || (obj->stringval[0] == 0)) { + /* XXX auto-generate a UUID */ + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "%s", "missing uuid element"); + goto error; + } + if (qemudParseUUID((const char *)obj->stringval, network->def.uuid) < 0) { + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "%s", "malformed uuid element"); + goto error; + } + xmlXPathFreeObject(obj); + + xmlXPathFreeContext(ctxt); + + return 0; + + error: + /* XXX free all the stuff in the qemud_network struct, or leave it upto + the caller ? */ + if (obj) + xmlXPathFreeObject(obj); + if (ctxt) + xmlXPathFreeContext(ctxt); + return -1; +} + + +struct qemud_network *qemudLoadNetworkConfigXML(struct qemud_server *server, + const char *file, + const char *doc, + int save) { + struct qemud_network *network = NULL; + xmlDocPtr xml; + + if (!(xml = xmlReadDoc(BAD_CAST doc, file ? file : "network.xml", NULL, + XML_PARSE_NOENT | XML_PARSE_NONET | + XML_PARSE_NOERROR | XML_PARSE_NOWARNING))) { + qemudReportError(server, VIR_ERR_XML_ERROR, NULL); + return NULL; + } + + if (!(network = calloc(1, sizeof(struct qemud_network)))) { + qemudReportError(server, VIR_ERR_NO_MEMORY, "network"); + return NULL; + } + + if (qemudParseNetworkXML(server, xml, network) < 0) { + xmlFreeDoc(xml); + qemudFreeNetwork(network); + return NULL; + } + xmlFreeDoc(xml); + + if (qemudFindNetworkByUUID(server, network->def.uuid) || + qemudFindNetworkByName(server, network->def.name)) { + qemudReportError(server, VIR_ERR_NETWORK_EXIST, network->def.name); + qemudFreeNetwork(network); + return NULL; + } + + if (file) { + strncpy(network->configFile, file, PATH_MAX); + network->configFile[PATH_MAX-1] = '\0'; + } else { + if (save) { + if (qemudMakeConfigPath(server->networkConfigDir, network->def.name, ".xml", network->configFile, PATH_MAX) < 0) { + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "cannot construct config file path"); + qemudFreeNetwork(network); + return NULL; + } + + if (qemudSaveNetworkConfig(server, network) < 0) { + qemudFreeNetwork(network); + return NULL; + } + } else { + network->configFile[0] = '\0'; + } + } + + return network; +} + + /* Load a guest from its persistent config file */ static void qemudLoadConfig(struct qemud_server *server, - const char *file) { + const char *file, + int isGuest) { FILE *fh; struct stat st; - struct qemud_vm *vm; char xml[QEMUD_MAX_XML_LEN]; int ret; if (!(fh = fopen(file, "r"))) { - qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "cannot open guest config file %s", file); + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "cannot open config file %s", file); return; } @@ -1118,7 +1287,7 @@ static void qemudLoadConfig(struct qemud } if (st.st_size >= QEMUD_MAX_XML_LEN) { - qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "guest config too large in file %s", file); + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "config too large in file %s", file); goto cleanup; } @@ -1128,10 +1297,20 @@ static void qemudLoadConfig(struct qemud } xml[st.st_size] = '\0'; - if ((vm = qemudLoadConfigXML(server, file, xml, 1))) { - vm->next = server->inactivevms; - server->inactivevms = vm; - server->ninactivevms++; + if (isGuest) { + struct qemud_vm *vm; + if ((vm = qemudLoadConfigXML(server, file, xml, 1))) { + vm->next = server->inactivevms; + server->inactivevms = vm; + server->ninactivevms++; + } + } else { + struct qemud_network *network; + if ((network = qemudLoadNetworkConfigXML(server, file, xml, 1))) { + network->next = server->inactivenetworks; + server->inactivenetworks = network; + server->ninactivenetworks++; + } } cleanup: @@ -1141,7 +1320,8 @@ static void qemudLoadConfig(struct qemud static int qemudScanConfigDir(struct qemud_server *server, - const char *configDir) { + const char *configDir, + int isGuest) { DIR *dir; struct dirent *entry; @@ -1159,7 +1339,7 @@ int qemudScanConfigDir(struct qemud_serv if (qemudMakeConfigPath(configDir, entry->d_name, NULL, file, PATH_MAX) < 0) continue; - qemudLoadConfig(server, file); + qemudLoadConfig(server, file, isGuest); } closedir(dir); @@ -1169,7 +1349,9 @@ int qemudScanConfigDir(struct qemud_serv /* Scan for all guest and network config files */ int qemudScanConfigs(struct qemud_server *server) { - return qemudScanConfigDir(server, server->configDir); + if (qemudScanConfigDir(server, server->configDir, 0) < 0) + return -1; + return qemudScanConfigDir(server, server->networkConfigDir, 1); } /* Simple grow-on-demand string buffer */ @@ -1424,19 +1606,54 @@ char *qemudGenerateXML(struct qemud_serv } -int qemudDeleteConfigXML(struct qemud_server *server, struct qemud_vm *vm) { - if (!vm->configFile[0]) { - qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "no config file for guest %s", vm->def.name); +char *qemudGenerateNetworkXML(struct qemud_server *server, + struct qemud_network *network) { + struct qemudBuffer buf; + unsigned char *uuid; + + buf.len = QEMUD_MAX_XML_LEN; + buf.used = 0; + buf.data = malloc(buf.len); + + if (qemudBufferPrintf(&buf, "<network>\n") < 0) + goto no_memory; + + if (qemudBufferPrintf(&buf, " <name>%s</name>\n", network->def.name) < 0) + goto no_memory; + + uuid = network->def.uuid; + if (qemudBufferPrintf(&buf, " <uuid>%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x</uuid>\n", + uuid[0], uuid[1], uuid[2], uuid[3], + uuid[4], uuid[5], uuid[6], uuid[7], + uuid[8], uuid[9], uuid[10], uuid[11], + uuid[12], uuid[13], uuid[14], uuid[15]) < 0) + goto no_memory; + + if (qemudBufferAdd(&buf, "</network>\n") < 0) + goto no_memory; + + return buf.data; + + no_memory: + qemudReportError(server, VIR_ERR_NO_MEMORY, "xml"); + free(buf.data); + return NULL; +} + + +int qemudDeleteConfig(struct qemud_server *server, + const char *configFile, + const char *name) { + if (!configFile[0]) { + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "no config file for %s", name); return -1; } - if (unlink(vm->configFile) < 0) { - qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "cannot remove config for guest %s", vm->def.name); + if (unlink(configFile) < 0) { + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "cannot remove config for %s", name); return -1; } - vm->configFile[0] = '\0'; - return 0; } Index: libvirt-foo/qemud/conf.h =================================================================== --- libvirt-foo.orig/qemud/conf.h 2007-02-14 15:06:41.000000000 +0000 +++ libvirt-foo.orig/qemud/conf.h 2007-02-14 15:06:41.000000000 +0000 @@ -30,18 +30,26 @@ int qemudBuildCommandLine(struct qemud_s struct qemud_vm *vm, char ***argv); +int qemudScanConfigs(struct qemud_server *server); +int qemudDeleteConfig(struct qemud_server *server, + const char *configFile, + const char *name); + void qemudFreeVM(struct qemud_vm *vm); struct qemud_vm *qemudLoadConfigXML(struct qemud_server *server, const char *file, const char *doc, int persist); -int qemudScanConfigs(struct qemud_server *server); char *qemudGenerateXML(struct qemud_server *server, struct qemud_vm *vm); -int qemudDeleteConfigXML(struct qemud_server *server, - struct qemud_vm *vm); - +void qemudFreeNetwork(struct qemud_network *network); +struct qemud_network *qemudLoadNetworkConfigXML(struct qemud_server *server, + const char *file, + const char *doc, + int persist); +char *qemudGenerateNetworkXML(struct qemud_server *server, + struct qemud_network *network); #endif Index: libvirt-foo/qemud/driver.c =================================================================== --- libvirt-foo.orig/qemud/driver.c 2007-02-14 15:46:58.000000000 +0000 +++ libvirt-foo.orig/qemud/driver.c 2007-02-14 15:46:58.000000000 +0000 @@ -522,9 +522,11 @@ int qemudDomainUndefine(struct qemud_ser return -1; } - if (qemudDeleteConfigXML(server, vm) < 0) + if (qemudDeleteConfig(server, vm->configFile, vm->def.name) < 0) return -1; + vm->configFile[0] = '\0'; + while (curr) { if (curr == vm) { if (prev) { @@ -547,67 +549,198 @@ int qemudDomainUndefine(struct qemud_ser struct qemud_network *qemudFindNetworkByUUID(const struct qemud_server *server, const unsigned char *uuid) { - server = NULL; uuid = NULL; + struct qemud_network *network = server->activenetworks; + + while (network) { + if (!memcmp(network->def.uuid, uuid, QEMUD_UUID_RAW_LEN)) + return network; + network = network->next; + } + + network = server->inactivenetworks; + while (network) { + if (!memcmp(network->def.uuid, uuid, QEMUD_UUID_RAW_LEN)) + return network; + network = network->next; + } + return NULL; } struct qemud_network *qemudFindNetworkByName(const struct qemud_server *server, const char *name) { - server = NULL; name = NULL; + struct qemud_network *network = server->activenetworks; + + while (network) { + if (!strcmp(network->def.name, name)) + return network; + network = network->next; + } + + network = server->inactivenetworks; + while (network) { + if (!strcmp(network->def.name, name)) + return network; + network = network->next; + } + return NULL; } int qemudNumNetworks(struct qemud_server *server) { - server = NULL; - return 0; + return server->nactivenetworks; } int qemudListNetworks(struct qemud_server *server, char *const*names, int nnames) { - server = NULL; names = NULL; nnames = 0; - return 0; + struct qemud_network *network = server->activenetworks; + int got = 0; + while (network && got < nnames) { + strncpy(names[got], network->def.name, QEMUD_MAX_NAME_LEN-1); + names[got][QEMUD_MAX_NAME_LEN-1] = '\0'; + network = network->next; + got++; + } + return got; } int qemudNumDefinedNetworks(struct qemud_server *server) { - server = NULL; - return 0; + return server->ninactivenetworks; } int qemudListDefinedNetworks(struct qemud_server *server, char *const*names, int nnames) { - server = NULL; names = NULL; nnames = 0; - return 0; + struct qemud_network *network = server->inactivenetworks; + int got = 0; + while (network && got < nnames) { + strncpy(names[got], network->def.name, QEMUD_MAX_NAME_LEN-1); + names[got][QEMUD_MAX_NAME_LEN-1] = '\0'; + network = network->next; + got++; + } + return got; } struct qemud_network *qemudNetworkCreate(struct qemud_server *server, const char *xml) { - server = NULL; xml = NULL; - return NULL; + struct qemud_network *network; + + if (!(network = qemudLoadNetworkConfigXML(server, NULL, xml, 0))) { + return NULL; + } + + if (qemudStartNetworkDaemon(server, network) < 0) { + qemudFreeNetwork(network); + return NULL; + } + + network->next = server->activenetworks; + server->activenetworks = network; + server->nactivenetworks++; + + return network; } struct qemud_network *qemudNetworkDefine(struct qemud_server *server, const char *xml) { - server = NULL; xml = NULL; - return NULL; + struct qemud_network *network; + + if (!(network = qemudLoadNetworkConfigXML(server, NULL, xml, 1))) { + return NULL; + } + + network->next = server->inactivenetworks; + server->inactivenetworks = network; + server->ninactivenetworks++; + + return network; } int qemudNetworkUndefine(struct qemud_server *server, const unsigned char *uuid) { - qemudReportError(server, VIR_ERR_INVALID_NETWORK, "no network with matching uuid"); - uuid = NULL; - return -1; + struct qemud_network *network = qemudFindNetworkByUUID(server, uuid); + struct qemud_network *prev = NULL, *curr = server->inactivenetworks; + + if (!network) { + qemudReportError(server, VIR_ERR_INVALID_DOMAIN, "no network with matching uuid"); + return -1; + } + + if (qemudDeleteConfig(server, network->configFile, network->def.name) < 0) + return -1; + + network->configFile[0] = '\0'; + + while (curr) { + if (curr == network) { + if (prev) { + prev->next = curr->next; + } else { + server->inactivenetworks = curr->next; + } + server->ninactivenetworks--; + break; + } + + prev = curr; + curr = curr->next; + } + + qemudFreeNetwork(network); + + return 0; } int qemudNetworkStart(struct qemud_server *server, struct qemud_network *network) { - server = NULL; network = NULL; - return 1; + struct qemud_network *prev = NULL, *curr = server->inactivenetworks; + if (qemudStartNetworkDaemon(server, network) < 0) { + return 1; + } + + while (curr) { + if (curr == network) { + if (prev) + prev->next = curr->next; + else + server->inactivenetworks = curr->next; + server->ninactivenetworks--; + break; + } + prev = curr; + curr = curr->next; + } + + network->next = server->activenetworks; + server->activenetworks = network; + server->nactivenetworks++; + + return 0; } int qemudNetworkDestroy(struct qemud_server *server, const unsigned char *uuid) { - uuid = NULL; - qemudReportError(server, VIR_ERR_INVALID_NETWORK, "no network with matching uuid"); - return -1; + struct qemud_network *network = qemudFindNetworkByUUID(server, uuid); + if (!network) { + qemudReportError(server, VIR_ERR_INVALID_NETWORK, "no network with matching uuid"); + return -1; + } + + if (qemudShutdownNetworkDaemon(server, network) < 0) + return -1; + + return 0; } int qemudNetworkDumpXML(struct qemud_server *server, const unsigned char *uuid, char *xml, int xmllen) { - qemudReportError(server, VIR_ERR_INVALID_NETWORK, "no network with matching uuid"); - uuid = NULL; xml = NULL; xmllen = 0; - return -1; + struct qemud_network *network = qemudFindNetworkByUUID(server, uuid); + char *networkxml; + if (!network) { + qemudReportError(server, VIR_ERR_INVALID_NETWORK, "no network with matching uuid"); + return -1; + } + + networkxml = qemudGenerateNetworkXML(server, network); + if (!networkxml) + return -1; + + strncpy(xml, networkxml, xmllen); + xml[xmllen-1] = '\0'; + + return 0; } /* Index: libvirt-foo/qemud/internal.h =================================================================== --- libvirt-foo.orig/qemud/internal.h 2007-02-14 15:46:58.000000000 +0000 +++ libvirt-foo.orig/qemud/internal.h 2007-02-14 15:46:58.000000000 +0000 @@ -207,6 +207,8 @@ struct qemud_network_def { /* Virtual Network runtime state */ struct qemud_network { + char configFile[PATH_MAX]; + struct qemud_network_def def; struct qemud_network *next; }; @@ -243,7 +245,12 @@ struct qemud_server { int ninactivevms; struct qemud_vm *inactivevms; int nextvmid; + int nactivenetworks; + struct qemud_network *activenetworks; + int ninactivenetworks; + struct qemud_network *inactivenetworks; char configDir[PATH_MAX]; + char networkConfigDir[PATH_MAX]; char errorMessage[QEMUD_MAX_ERROR_LEN]; int errorCode; }; @@ -254,6 +261,13 @@ int qemudStartVMDaemon(struct qemud_serv int qemudShutdownVMDaemon(struct qemud_server *server, struct qemud_vm *vm); +int qemudStartNetworkDaemon(struct qemud_server *server, + struct qemud_network *network); + +int qemudShutdownNetworkDaemon(struct qemud_server *server, + struct qemud_network *network); + + #endif /* Index: libvirt-foo/qemud/qemud.c =================================================================== --- libvirt-foo.orig/qemud/qemud.c 2007-02-14 14:53:23.000000000 +0000 +++ libvirt-foo.orig/qemud/qemud.c 2007-02-14 14:53:23.000000000 +0000 @@ -251,6 +251,9 @@ static struct qemud_server *qemudInitial if (snprintf(server->configDir, sizeof(server->configDir), "%s/qemud", SYSCONF_DIR) >= (int)sizeof(server->configDir)) { goto cleanup; } + if (snprintf(server->networkConfigDir, sizeof(server->networkConfigDir), "%s/qemud/networks", SYSCONF_DIR) >= (int)sizeof(server->networkConfigDir)) { + goto cleanup; + } } else { struct passwd *pw; int uid; @@ -264,8 +267,13 @@ static struct qemud_server *qemudInitial if (snprintf(server->configDir, sizeof(server->configDir), "%s/.qemud", pw->pw_dir) >= (int)sizeof(server->configDir)) { goto cleanup; } + + if (snprintf(server->networkConfigDir, sizeof(server->networkConfigDir), "%s/.qemud.d/networks", pw->pw_dir) >= (int)sizeof(server->networkConfigDir)) { + goto cleanup; + } } + if (qemudListen(server, sys) < 0) { goto cleanup; } @@ -696,6 +704,20 @@ static int qemudDispatchVMFailure(struct } +int qemudStartNetworkDaemon(struct qemud_server *server, + struct qemud_network *network) { + server = NULL; network = NULL; + return 0; +} + + +int qemudShutdownNetworkDaemon(struct qemud_server *server, + struct qemud_network *network) { + server = NULL; network = NULL; + return 0; +} + + static int qemudDispatchPoll(struct qemud_server *server, struct pollfd *fds) { struct qemud_socket *sock = server->sockets; struct qemud_client *client = server->clients; --

Add support for creating and deleting bridges. Also add a <bridge> element for configuring the bridges and an <ip> element for setting and IP address on the bridge interface. Note, we require a number of kernel headers in order to interface with the bridge and tun/tap drivers. We also optionally use libsysfs to configure various bridge parameters. Signed-off-by: Mark McLoughlin <markmc@redhat.com> Index: libvirt-foo/configure.in =================================================================== --- libvirt-foo.orig/configure.in 2007-02-14 14:29:38.000000000 +0000 +++ libvirt-foo.orig/configure.in 2007-02-14 14:29:38.000000000 +0000 @@ -102,6 +102,28 @@ then dnl search for the Xen store library AC_SEARCH_LIBS(xs_read, [xenstore], [], [AC_MSG_ERROR([Xen store library not found])]) +dnl +dnl check for libsyfs (>= 2.0.0); allow disabling bridge parameters support altogether +dnl +AC_ARG_ENABLE(bridge-params, + AC_HELP_STRING([--disable-bridge-params], + [disable support for setting bridge parameters using libsysfs [default=no]]),, + enable_bridge_params=yes) + +if test x"$enable_bridge_params" == "xyes"; then + AC_CHECK_LIB(sysfs, sysfs_open_device, + [AC_CHECK_HEADER(sysfs/libsysfs.h, + AC_DEFINE(ENABLE_BRIDGE_PARAMS, , [enable setting bridge parameters using libsysfs]) + SYSFS_LIBS="-lsysfs" AC_SUBST(SYSFS_LIBS), + AC_MSG_ERROR([You must install libsysfs in order to compile libvirt]))]) +fi + +dnl +dnl check for kernel headers required by qemud/bridge.c +dnl +AC_CHECK_HEADERS(linux/param.h linux/sockios.h linux/if_bridge.h linux/if_tun.h,, + AC_MSG_ERROR([You must install kernel-headers in order to compile libvirt])) + dnl ========================================================================== dnl find libxml2 library, borrowed from xmlsec dnl ========================================================================== Index: libvirt-foo/qemud/Makefile.am =================================================================== --- libvirt-foo.orig/qemud/Makefile.am 2007-02-14 14:38:46.000000000 +0000 +++ libvirt-foo.orig/qemud/Makefile.am 2007-02-14 14:38:46.000000000 +0000 @@ -7,13 +7,14 @@ libexec_PROGRAMS = libvirt_qemud libvirt_qemud_SOURCES = qemud.c internal.h protocol.h \ driver.c driver.h \ dispatch.c dispatch.h \ - conf.c conf.h + conf.c conf.h \ + bridge.c bridge.h #-D_XOPEN_SOURCE=600 -D_XOPEN_SOURCE_EXTENDED=1 -D_POSIX_C_SOURCE=199506L libvirt_qemud_CFLAGS = \ -I$(top_srcdir)/include -I$(top_builddir)/include $(LIBXML_CFLAGS) \ -Werror -Wall -Wextra -DLOCAL_STATE_DIR="\"$(localstatedir)\"" \ -DSYSCONF_DIR="\"$(sysconfdir)\"" -libvirt_qemud_LDFLAGS = $(LIBXML_LIBS) +libvirt_qemud_LDFLAGS = $(LIBXML_LIBS) $(SYSFS_LIBS) libvirt_qemud_DEPENDENCIES = libvirt_qemud_LDADD = Index: libvirt-foo/qemud/bridge.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ /dev/null 1970-01-01 00:00:00.000000000 +0000 @@ -0,0 +1,609 @@ +/* + * Copyright (C) 2007 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Mark McLoughlin <markmc@redhat.com> + */ + +#include <config.h> + +#include "bridge.h" + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <arpa/inet.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/ioctl.h> + +#include <linux/param.h> /* HZ */ +#include <linux/sockios.h> /* SIOCBRADDBR etc. */ +#include <linux/if_bridge.h> /* SYSFS_BRIDGE_ATTR */ +#include <linux/if_tun.h> /* IFF_TUN, IFF_NO_PI */ + +#include "internal.h" + +#define MAX_BRIDGE_ID 256 + +#define JIFFIES_TO_MS(j) (((j)*1000)/HZ) +#define MS_TO_JIFFIES(ms) (((ms)*HZ)/1000) + +struct _brControl { + int fd; +}; + +int +brInit(brControl **ctlp) +{ + int fd; + + if (!ctlp || *ctlp) + return EINVAL; + + fd = socket(AF_INET, SOCK_STREAM, 0); + if (fd < 0) + return errno; + + *ctlp = (brControl *)malloc(sizeof(struct _brControl)); + if (!*ctlp) + return ENOMEM; + + (*ctlp)->fd = fd; + + return 0; +} + +void +brShutdown(brControl *ctl) +{ + if (!ctl) + return; + + close(ctl->fd); + ctl->fd = 0; + + free(ctl); +} + +int +brAddBridge(brControl *ctl, + const char *nameOrFmt, + char *name, + int maxlen) +{ + int id, subst; + + if (!ctl || !ctl->fd || !nameOrFmt || !name) + return EINVAL; + + if (maxlen >= BR_IFNAME_MAXLEN) + maxlen = BR_IFNAME_MAXLEN; + + subst = id = 0; + + if (strstr(nameOrFmt, "%d")) + subst = 1; + + do { + char try[BR_IFNAME_MAXLEN]; + int len; + + if (subst) { + len = snprintf(try, maxlen, nameOrFmt, id); + if (len >= maxlen) + return EADDRINUSE; + } else { + len = strlen(nameOrFmt); + if (len >= maxlen - 1) + return EINVAL; + + strncpy(try, nameOrFmt, len); + try[len] = '\0'; + } + + if (ioctl(ctl->fd, SIOCBRADDBR, try) == 0) { + strncpy(name, try, maxlen); + return 0; + } + + id++; + } while (subst && id <= MAX_BRIDGE_ID); + + return errno; +} + +int +brDeleteBridge(brControl *ctl, + const char *name) +{ + if (!ctl || !ctl->fd || !name) + return EINVAL; + + return ioctl(ctl->fd, SIOCBRDELBR, name) == 0 ? 0 : errno; +} + +static int +brAddDelInterface(brControl *ctl, + int cmd, + const char *bridge, + const char *iface) +{ + struct ifreq ifr; + int len; + + if (!ctl || !ctl->fd || !bridge || !iface) + return EINVAL; + + if ((len = strlen(bridge)) >= BR_IFNAME_MAXLEN) + return EINVAL; + + memset(&ifr, 0, sizeof(struct ifreq)); + + strncpy(ifr.ifr_name, bridge, len); + ifr.ifr_name[len] = '\0'; + + if (!(ifr.ifr_ifindex = if_nametoindex(iface))) + return ENODEV; + + return ioctl(ctl->fd, cmd, &ifr) == 0 ? 0 : errno; +} + +int +brAddInterface(brControl *ctl, + const char *bridge, + const char *iface) +{ + return brAddDelInterface(ctl, SIOCBRADDIF, bridge, iface); +} + +int +brDeleteInterface(brControl *ctl, + const char *bridge, + const char *iface) +{ + return brAddDelInterface(ctl, SIOCBRDELIF, bridge, iface); +} + +int +brAddTap(brControl *ctl, + const char *bridge, + const char *ifnameOrFmt, + char *ifname, + int maxlen, + int *tapfd) +{ + int id, subst, fd; + + if (!ctl || !ctl->fd || !bridge || !ifnameOrFmt || !tapfd) + return EINVAL; + + if (!ifname) + maxlen = BR_IFNAME_MAXLEN; + else if (maxlen >= BR_IFNAME_MAXLEN) + maxlen = BR_IFNAME_MAXLEN; + + subst = id = 0; + + if (strstr(ifnameOrFmt, "%d")) + subst = 1; + + if ((fd = open("/dev/net/tun", O_RDWR)) < 0) + return errno; + + do { + struct ifreq try; + int len; + + memset(&try, 0, sizeof(struct ifreq)); + + try.ifr_flags = IFF_TAP|IFF_NO_PI; + + if (subst) { + len = snprintf(try.ifr_name, maxlen, ifnameOrFmt, id); + if (len >= maxlen) { + errno = EADDRINUSE; + goto error; + } + } else { + len = strlen(ifnameOrFmt); + if (len >= maxlen - 1) { + errno = EINVAL; + goto error; + } + + strncpy(try.ifr_name, ifnameOrFmt, len); + try.ifr_name[len] = '\0'; + } + + if (ioctl(fd, TUNSETIFF, &try) == 0) { + if ((errno = brAddInterface(ctl, bridge, try.ifr_name))) + goto error; + if ((errno = brSetInterfaceUp(ctl, try.ifr_name, 1))) + goto error; + if (ifname) + strncpy(ifname, try.ifr_name, maxlen); + *tapfd = fd; + return 0; + } + + id++; + } while (subst && id <= MAX_BRIDGE_ID); + + error: + close(fd); + + return errno; +} + +int +brSetInterfaceUp(brControl *ctl, + const char *ifname, + int up) +{ + struct ifreq ifr; + int len; + int flags; + + if (!ctl || !ifname) + return EINVAL; + + if ((len = strlen(ifname)) >= BR_IFNAME_MAXLEN) + return EINVAL; + + memset(&ifr, 0, sizeof(struct ifreq)); + + strncpy(ifr.ifr_name, ifname, len); + ifr.ifr_name[len] = '\0'; + + if (ioctl(ctl->fd, SIOCGIFFLAGS, &ifr) < 0) + return errno; + + flags = up ? (ifr.ifr_flags | IFF_UP) : (ifr.ifr_flags & ~IFF_UP); + + if (ifr.ifr_flags != flags) { + ifr.ifr_flags = flags; + + if (ioctl(ctl->fd, SIOCSIFFLAGS, &ifr) < 0) + return errno; + } + + return 0; +} + +int +brGetInterfaceUp(brControl *ctl, + const char *ifname, + int *up) +{ + struct ifreq ifr; + int len; + + if (!ctl || !ifname) + return EINVAL; + + if ((len = strlen(ifname)) >= BR_IFNAME_MAXLEN) + return EINVAL; + + memset(&ifr, 0, sizeof(struct ifreq)); + + strncpy(ifr.ifr_name, ifname, len); + ifr.ifr_name[len] = '\0'; + + if (ioctl(ctl->fd, SIOCGIFFLAGS, &ifr) < 0) + return errno; + + *up = (ifr.ifr_flags & IFF_UP) ? 1 : 0; + + return 0; +} + +static int +brSetInetAddr(brControl *ctl, + const char *ifname, + int cmd, + const char *addr) +{ + struct ifreq ifr; + struct in_addr inaddr; + int len, ret; + + if (!ctl || !ctl->fd || !ifname || !addr) + return EINVAL; + + if ((len = strlen(ifname)) >= BR_IFNAME_MAXLEN) + return EINVAL; + + memset(&ifr, 0, sizeof(struct ifreq)); + + strncpy(ifr.ifr_name, ifname, len); + ifr.ifr_name[len] = '\0'; + + if ((ret = inet_pton(AF_INET, addr, &inaddr)) < 0) + return errno; + else if (ret == 0) + return EINVAL; + + ((struct sockaddr_in *)&ifr.ifr_addr)->sin_family = AF_INET; + ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr = inaddr; + + if (ioctl(ctl->fd, cmd, &ifr) < 0) + return errno; + + return 0; +} + +static int +brGetInetAddr(brControl *ctl, + const char *ifname, + int cmd, + char *addr, + int maxlen) +{ + struct ifreq ifr; + struct in_addr *inaddr; + int len; + + if (!ctl || !ctl->fd || !ifname || !addr) + return EINVAL; + + if ((len = strlen(ifname)) >= BR_IFNAME_MAXLEN) + return EINVAL; + + memset(&ifr, 0, sizeof(struct ifreq)); + + strncpy(ifr.ifr_name, ifname, len); + ifr.ifr_name[len] = '\0'; + + if (ioctl(ctl->fd, cmd, &ifr) < 0) + return errno; + + if (maxlen < BR_INET_ADDR_MAXLEN || ifr.ifr_addr.sa_family != AF_INET) + return EFAULT; + + inaddr = &((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr; + + if (!inet_ntop(AF_INET, inaddr, addr, maxlen)) + return errno; + + return 0; +} + +int +brSetInetAddress(brControl *ctl, + const char *ifname, + const char *addr) +{ + return brSetInetAddr(ctl, ifname, SIOCSIFADDR, addr); +} + +int +brGetInetAddress(brControl *ctl, + const char *ifname, + char *addr, + int maxlen) +{ + return brGetInetAddr(ctl, ifname, SIOCGIFADDR, addr, maxlen); +} + +int +brSetInetNetmask(brControl *ctl, + const char *ifname, + const char *addr) +{ + return brSetInetAddr(ctl, ifname, SIOCSIFNETMASK, addr); +} + +int +brGetInetNetmask(brControl *ctl, + const char *ifname, + char *addr, + int maxlen) +{ + return brGetInetAddr(ctl, ifname, SIOCGIFNETMASK, addr, maxlen); +} + +#ifdef ENABLE_BRIDGE_PARAMS + +#include <sysfs/libsysfs.h> + +static int +brSysfsPrep(struct sysfs_class_device **dev, + struct sysfs_attribute **attr, + const char *bridge, + const char *attrname) +{ + *dev = NULL; + *attr = NULL; + + if (!(*dev = sysfs_open_class_device("net", bridge))) + return errno; + + if (!(*attr = sysfs_get_classdev_attr(*dev, attrname))) { + int err = errno; + + sysfs_close_class_device(*dev); + *dev = NULL; + + return err; + } + + return 0; +} + +static int +brSysfsWriteInt(struct sysfs_attribute *attr, + int value) +{ + char buf[32]; + int len; + + len = snprintf(buf, sizeof(buf), "%d\n", value); + + if (len > (int)sizeof(buf)) + len = sizeof(buf); /* paranoia, shouldn't happen */ + + return sysfs_write_attribute(attr, buf, len) == 0 ? 0 : errno; +} + +int +brSetForwardDelay(brControl *ctl, + const char *bridge, + int delay) +{ + struct sysfs_class_device *dev; + struct sysfs_attribute *attr; + int err = 0; + + if (!ctl || !bridge) + return EINVAL; + + if ((err = brSysfsPrep(&dev, &attr, bridge, SYSFS_BRIDGE_ATTR "/forward_delay"))) + return err; + + err = brSysfsWriteInt(attr, MS_TO_JIFFIES(delay)); + + sysfs_close_class_device(dev); + + return err; +} + +int +brGetForwardDelay(brControl *ctl, + const char *bridge, + int *delayp) +{ + struct sysfs_class_device *dev; + struct sysfs_attribute *attr; + int err = 0; + + if (!ctl || !bridge || !delayp) + return EINVAL; + + if ((err = brSysfsPrep(&dev, &attr, bridge, SYSFS_BRIDGE_ATTR "/forward_delay"))) + return err; + + *delayp = strtoul(attr->value, NULL, 0); + + if (errno != ERANGE) { + *delayp = JIFFIES_TO_MS(*delayp); + } else { + err = errno; + } + + sysfs_close_class_device(dev); + + return err; +} + +int +brSetEnableSTP(brControl *ctl, + const char *bridge, + int enable) +{ + struct sysfs_class_device *dev; + struct sysfs_attribute *attr; + int err = 0; + + if (!ctl || !bridge) + return EINVAL; + + if ((err = brSysfsPrep(&dev, &attr, bridge, SYSFS_BRIDGE_ATTR "/stp_state"))) + return err; + + err = brSysfsWriteInt(attr, (enable == 0) ? 0 : 1); + + sysfs_close_class_device(dev); + + return err; +} + +int +brGetEnableSTP(brControl *ctl, + const char *bridge, + int *enablep) +{ + struct sysfs_class_device *dev; + struct sysfs_attribute *attr; + int err = 0; + + if (!ctl || !bridge || !enablep) + return EINVAL; + + if ((err = brSysfsPrep(&dev, &attr, bridge, SYSFS_BRIDGE_ATTR "/stp_state"))) + return err; + + *enablep = strtoul(attr->value, NULL, 0); + + if (errno != ERANGE) { + *enablep = (*enablep == 0) ? 0 : 1; + } else { + err = errno; + } + + sysfs_close_class_device(dev); + + return err; +} + +#else /* ENABLE_BRIDGE_PARAMS */ + +int +brSetForwardDelay(brControl *ctl ATTRIBUTE_UNUSED, + const char *bridge ATTRIBUTE_UNUSED, + int delay ATTRIBUTE_UNUSED) +{ + return 0; +} + +int +brGetForwardDelay(brControl *ctl ATTRIBUTE_UNUSED, + const char *bridge ATTRIBUTE_UNUSED, + int *delay ATTRIBUTE_UNUSED) +{ + return 0; +} + +int +brSetEnableSTP(brControl *ctl ATTRIBUTE_UNUSED, + const char *bridge ATTRIBUTE_UNUSED, + int enable ATTRIBUTE_UNUSED) +{ + return 0; +} + +int +brGetEnableSTP(brControl *ctl ATTRIBUTE_UNUSED, + const char *bridge ATTRIBUTE_UNUSED, + int *enable ATTRIBUTE_UNUSED) +{ + return 0; +} + +#endif /* ENABLE_BRIDGE_PARAMS */ + +/* + * Local variables: + * indent-tabs-mode: nil + * c-indent-level: 4 + * c-basic-offset: 4 + * tab-width: 4 + * End: + */ Index: libvirt-foo/qemud/bridge.h =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ /dev/null 1970-01-01 00:00:00.000000000 +0000 @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2007 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Mark McLoughlin <markmc@redhat.com> + */ + +#ifndef __QEMUD_BRIDGE_H__ +#define __QEMUD_BRIDGE_H__ + +#include <net/if.h> +#include <netinet/in.h> + +#define BR_IFNAME_MAXLEN IF_NAMESIZE +#define BR_INET_ADDR_MAXLEN INET_ADDRSTRLEN + +typedef struct _brControl brControl; + +int brInit (brControl **ctl); +void brShutdown (brControl *ctl); + +int brAddBridge (brControl *ctl, + const char *nameOrFmt, + char *name, + int maxlen); +int brDeleteBridge (brControl *ctl, + const char *name); + +int brAddInterface (brControl *ctl, + const char *bridge, + const char *iface); +int brDeleteInterface (brControl *ctl, + const char *bridge, + const char *iface); + +int brAddTap (brControl *ctl, + const char *bridge, + const char *ifnameOrFmt, + char *ifname, + int maxlen, + int *tapfd); + +int brSetInterfaceUp (brControl *ctl, + const char *ifname, + int up); +int brGetInterfaceUp (brControl *ctl, + const char *ifname, + int *up); + +int brSetInetAddress (brControl *ctl, + const char *ifname, + const char *addr); +int brGetInetAddress (brControl *ctl, + const char *ifname, + char *addr, + int maxlen); +int brSetInetNetmask (brControl *ctl, + const char *ifname, + const char *netmask); +int brGetInetNetmask (brControl *ctl, + const char *ifname, + char *netmask, + int maxlen); + +int brSetForwardDelay (brControl *ctl, + const char *bridge, + int delay); +int brGetForwardDelay (brControl *ctl, + const char *bridge, + int *delay); +int brSetEnableSTP (brControl *ctl, + const char *bridge, + int enable); +int brGetEnableSTP (brControl *ctl, + const char *bridge, + int *enable); + +#endif /* __QEMUD_BRIDGE_H__ */ + +/* + * Local variables: + * indent-tabs-mode: nil + * c-indent-level: 4 + * c-basic-offset: 4 + * tab-width: 4 + * End: + */ Index: libvirt-foo/qemud/conf.c =================================================================== --- libvirt-foo.orig/qemud/conf.c 2007-02-14 15:54:56.000000000 +0000 +++ libvirt-foo.orig/qemud/conf.c 2007-02-14 15:54:56.000000000 +0000 @@ -1145,6 +1145,64 @@ static int qemudSaveNetworkConfig(struct } +static int qemudParseBridgeXML(struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_network *network, + xmlNodePtr node) { + xmlChar *name, *stp, *delay; + + name = xmlGetProp(node, BAD_CAST "name"); + if (name != NULL) { + strncpy(network->def.bridge, (const char *)name, IF_NAMESIZE-1); + network->def.bridge[IF_NAMESIZE-1] = '\0'; + xmlFree(name); + name = NULL; + } + + stp = xmlGetProp(node, BAD_CAST "stp"); + if (stp != NULL) { + if (xmlStrEqual(stp, BAD_CAST "off")) { + network->def.disableSTP = 1; + } + xmlFree(stp); + stp = NULL; + } + + delay = xmlGetProp(node, BAD_CAST "delay"); + if (delay != NULL) { + network->def.forwardDelay = strtol((const char *)delay, NULL, 10); + xmlFree(delay); + delay = NULL; + } + + return 1; +} + + +static int qemudParseInetXML(struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_network *network, + xmlNodePtr node) { + xmlChar *address, *netmask; + + address = xmlGetProp(node, BAD_CAST "address"); + if (address != NULL) { + strncpy(network->def.ipAddress, (const char *)address, BR_INET_ADDR_MAXLEN-1); + network->def.ipAddress[BR_INET_ADDR_MAXLEN-1] = '\0'; + xmlFree(address); + address = NULL; + } + + netmask = xmlGetProp(node, BAD_CAST "netmask"); + if (netmask != NULL) { + strncpy(network->def.netmask, (const char *)netmask, BR_INET_ADDR_MAXLEN-1); + network->def.netmask[BR_INET_ADDR_MAXLEN-1] = '\0'; + xmlFree(netmask); + netmask = NULL; + } + + return 1; +} + + static int qemudParseNetworkXML(struct qemud_server *server, xmlDocPtr xml, struct qemud_network *network) { @@ -1195,6 +1253,26 @@ static int qemudParseNetworkXML(struct q } xmlXPathFreeObject(obj); + /* Parse bridge information */ + obj = xmlXPathEval(BAD_CAST "/network/bridge[1]", ctxt); + if ((obj != NULL) && (obj->type == XPATH_NODESET) && + (obj->nodesetval != NULL) && (obj->nodesetval->nodeNr > 0)) { + if (!qemudParseBridgeXML(server, network, obj->nodesetval->nodeTab[0])) { + goto error; + } + } + xmlXPathFreeObject(obj); + + /* Parse IP information */ + obj = xmlXPathEval(BAD_CAST "/network/ip[1]", ctxt); + if ((obj != NULL) && (obj->type == XPATH_NODESET) && + (obj->nodesetval != NULL) && (obj->nodesetval->nodeNr > 0)) { + if (!qemudParseInetXML(server, network, obj->nodesetval->nodeTab[0])) { + goto error; + } + } + xmlXPathFreeObject(obj); + xmlXPathFreeContext(ctxt); return 0; @@ -1209,7 +1287,6 @@ static int qemudParseNetworkXML(struct q return -1; } - struct qemud_network *qemudLoadNetworkConfigXML(struct qemud_server *server, const char *file, const char *doc, @@ -1629,6 +1706,28 @@ char *qemudGenerateNetworkXML(struct qem uuid[12], uuid[13], uuid[14], uuid[15]) < 0) goto no_memory; + if (qemudBufferPrintf(&buf, " <bridge name='%s' stp='%s' delay='%d' />\n", + network->def.bridge, + network->def.disableSTP ? "off" : "on", + network->def.forwardDelay) < 0) + goto no_memory; + + if (network->def.ipAddress[0] || network->def.netmask[0]) { + if (qemudBufferAdd(&buf, " <ip") < 0) + goto no_memory; + + if (network->def.ipAddress[0] && + qemudBufferPrintf(&buf, " address='%s'", network->def.ipAddress) < 0) + goto no_memory; + + if (network->def.netmask[0] && + qemudBufferPrintf(&buf, " netmask='%s'", network->def.netmask) < 0) + goto no_memory; + + if (qemudBufferAdd(&buf, "/>\n") < 0) + goto no_memory; + } + if (qemudBufferAdd(&buf, "</network>\n") < 0) goto no_memory; Index: libvirt-foo/qemud/internal.h =================================================================== --- libvirt-foo.orig/qemud/internal.h 2007-02-14 15:54:56.000000000 +0000 +++ libvirt-foo.orig/qemud/internal.h 2007-02-14 15:54:56.000000000 +0000 @@ -30,6 +30,7 @@ #include <gnutls/gnutls.h> #include "protocol.h" +#include "bridge.h" #ifdef __GNUC__ #ifdef HAVE_ANSIDECL_H @@ -203,6 +204,13 @@ struct qemud_vm { struct qemud_network_def { unsigned char uuid[QEMUD_UUID_RAW_LEN]; char name[QEMUD_MAX_NAME_LEN]; + + char bridge[BR_IFNAME_MAXLEN]; + int disableSTP; + int forwardDelay; + + char ipAddress[BR_INET_ADDR_MAXLEN]; + char netmask[BR_INET_ADDR_MAXLEN]; }; /* Virtual Network runtime state */ @@ -210,6 +218,11 @@ struct qemud_network { char configFile[PATH_MAX]; struct qemud_network_def def; + + char bridge[BR_IFNAME_MAXLEN]; + + unsigned int active : 1; + struct qemud_network *next; }; @@ -249,6 +262,7 @@ struct qemud_server { struct qemud_network *activenetworks; int ninactivenetworks; struct qemud_network *inactivenetworks; + brControl *brctl; char configDir[PATH_MAX]; char networkConfigDir[PATH_MAX]; char errorMessage[QEMUD_MAX_ERROR_LEN]; Index: libvirt-foo/qemud/qemud.c =================================================================== --- libvirt-foo.orig/qemud/qemud.c 2007-02-14 15:54:56.000000000 +0000 +++ libvirt-foo.orig/qemud/qemud.c 2007-02-14 15:54:56.000000000 +0000 @@ -706,14 +706,115 @@ static int qemudDispatchVMFailure(struct int qemudStartNetworkDaemon(struct qemud_server *server, struct qemud_network *network) { - server = NULL; network = NULL; + const char *name; + int err; + + if (network->active) { + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, + "network is already active"); + return -1; + } + + if (!server->brctl && (err = brInit(&server->brctl))) { + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, + "cannot initialize bridge support: %s", strerror(err)); + return -1; + } + + if (network->def.bridge[0] == '\0' || + strchr(network->def.bridge, '%')) { + name = "vnet%d"; + } else { + name = network->def.bridge; + } + + if ((err = brAddBridge(server->brctl, name, network->bridge, sizeof(network->bridge)))) { + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, + "cannot create bridge '%s' : %s", name, strerror(err)); + return -1; + } + + if (network->def.ipAddress[0] && + (err = brSetInetAddress(server->brctl, network->bridge, network->def.ipAddress))) { + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, + "cannot set IP address on bridge '%s' to '%s' : %s\n", + network->bridge, network->def.ipAddress, strerror(err)); + goto err_delbr; + } + + if (network->def.netmask[0] && + (err = brSetInetNetmask(server->brctl, network->bridge, network->def.netmask))) { + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, + "cannot set netmask on bridge '%s' to '%s' : %s\n", + network->bridge, network->def.netmask, strerror(err)); + goto err_delbr; + } + + if (network->def.ipAddress[0] && + (err = brSetInterfaceUp(server->brctl, network->bridge, 1))) { + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, + "failed to bring the bridge '%s' up : %s\n", + network->bridge, strerror(err)); + goto err_delbr; + } + + network->active = 1; + return 0; + + err_delbr: + if ((err = brDeleteBridge(server->brctl, network->bridge))) { + printf("Damn! Couldn't delete bridge '%s' : %s\n", + network->bridge, strerror(err)); + } + + return -1; } int qemudShutdownNetworkDaemon(struct qemud_server *server, struct qemud_network *network) { - server = NULL; network = NULL; + struct qemud_network *prev, *curr; + int err; + + if (!network->active) + return 0; + + if (network->def.ipAddress[0] && + (err = brSetInterfaceUp(server->brctl, network->bridge, 0))) { + printf("Damn! Failed to bring down bridge '%s' : %s\n", + network->bridge, strerror(err)); + } + + if ((err = brDeleteBridge(server->brctl, network->bridge))) { + printf("Damn! Failed to delete bridge '%s' : %s\n", + network->bridge, strerror(err)); + } + + /* Move it to inactive networks list */ + prev = NULL; + curr = server->activenetworks; + while (curr) { + if (curr == network) { + if (prev) { + prev->next = curr->next; + } else { + server->activenetworks = curr->next; + } + server->nactivenetworks--; + + curr->next = server->inactivenetworks; + server->inactivenetworks = curr; + server->ninactivenetworks++; + break; + } + prev = curr; + curr = curr->next; + } + + network->bridge[0] = '\0'; + network->active = 0; + return 0; } @@ -723,6 +824,7 @@ static int qemudDispatchPoll(struct qemu struct qemud_client *client = server->clients; struct qemud_vm *vm = server->activevms; struct qemud_vm *tmp; + struct qemud_network *network, *prevnet; int ret = 0; int fd = 0; @@ -806,6 +908,25 @@ static int qemudDispatchPoll(struct qemu } } + /* Cleanup any networks too */ + network = server->inactivenetworks; + prevnet = NULL; + while (network) { + if (!network->configFile[0]) { + struct qemud_network *next = network->next; + if (prevnet) { + prevnet->next = next; + } else { + server->inactivenetworks = next; + } + qemudFreeNetwork(network); + network = next; + } else { + prevnet = network; + network = network->next; + } + } + return ret; } @@ -896,6 +1017,8 @@ static void qemudCleanup(struct qemud_se close(sock->fd); sock = sock->next; } + if (server->brctl) + brShutdown(server->brctl); free(server); } --

Add support for starting dnsmasq and a <dhcp> element to configure the behaviour of the dhcp server. Note, there's quite a bit more interesting stuff we can do with dnsmasq to e.g. pre-define a set of mac <-> ip address mappings but we can add that later. Signed-off-by: Mark McLoughlin <markmc@redhat.com> Index: libvirt-foo/qemud/conf.c =================================================================== --- libvirt-foo.orig/qemud/conf.c 2007-02-14 15:59:02.000000000 +0000 +++ libvirt-foo.orig/qemud/conf.c 2007-02-14 15:59:02.000000000 +0000 @@ -1099,6 +1099,12 @@ struct qemud_vm *qemudLoadConfigXML(stru void qemudFreeNetwork(struct qemud_network *network) { + struct qemud_dhcp_range_def *range = network->def.ranges; + while (range) { + struct qemud_dhcp_range_def *next = range->next; + free(range); + range = next; + } free(network); } @@ -1177,11 +1183,61 @@ static int qemudParseBridgeXML(struct qe return 1; } +static int qemudParseDhcpRangesXML(struct qemud_server *server, + struct qemud_network *network, + xmlNodePtr node) { + + xmlNodePtr cur; + + cur = node->children; + while (cur != NULL) { + struct qemud_dhcp_range_def *range; + xmlChar *start, *end; + + if (cur->type != XML_ELEMENT_NODE || + !xmlStrEqual(cur->name, BAD_CAST "range")) { + cur = cur->next; + continue; + } + + if (!(range = calloc(1, sizeof(struct qemud_dhcp_range_def)))) { + qemudReportError(server, VIR_ERR_NO_MEMORY, "range"); + return 0; + } + + start = xmlGetProp(cur, BAD_CAST "start"); + end = xmlGetProp(cur, BAD_CAST "end"); + + if (start && start[0] && end && end[0]) { + strncpy(range->start, (const char *)start, BR_INET_ADDR_MAXLEN-1); + range->start[BR_INET_ADDR_MAXLEN-1] = '\0'; + + strncpy(range->end, (const char *)end, BR_INET_ADDR_MAXLEN-1); + range->end[BR_INET_ADDR_MAXLEN-1] = '\0'; + + range->next = network->def.ranges; + network->def.ranges = range; + network->def.nranges++; + } else { + free(range); + } + + if (start) + xmlFree(start); + if (end) + xmlFree(end); + + cur = cur->next; + } + + return 1; +} static int qemudParseInetXML(struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_network *network, xmlNodePtr node) { xmlChar *address, *netmask; + xmlNodePtr cur; address = xmlGetProp(node, BAD_CAST "address"); if (address != NULL) { @@ -1199,6 +1255,15 @@ static int qemudParseInetXML(struct qemu netmask = NULL; } + cur = node->children; + while (cur != NULL) { + if (cur->type == XML_ELEMENT_NODE && + xmlStrEqual(cur->name, BAD_CAST "dhcp") && + !qemudParseDhcpRangesXML(server, network, cur)) + return 0; + cur = cur->next; + } + return 1; } @@ -1724,7 +1789,24 @@ char *qemudGenerateNetworkXML(struct qem qemudBufferPrintf(&buf, " netmask='%s'", network->def.netmask) < 0) goto no_memory; - if (qemudBufferAdd(&buf, "/>\n") < 0) + if (qemudBufferAdd(&buf, ">\n") < 0) + goto no_memory; + + if (network->def.ranges) { + struct qemud_dhcp_range_def *range = network->def.ranges; + if (qemudBufferAdd(&buf, " <dhcp>\n") < 0) + goto no_memory; + while (range) { + if (qemudBufferPrintf(&buf, " <range start='%s' end='%s' />\n", + range->start, range->end) < 0) + goto no_memory; + range = range->next; + } + if (qemudBufferAdd(&buf, " </dhcp>\n") < 0) + goto no_memory; + } + + if (qemudBufferAdd(&buf, " </ip>\n") < 0) goto no_memory; } Index: libvirt-foo/qemud/internal.h =================================================================== --- libvirt-foo.orig/qemud/internal.h 2007-02-14 15:59:02.000000000 +0000 +++ libvirt-foo.orig/qemud/internal.h 2007-02-14 15:59:02.000000000 +0000 @@ -200,6 +200,14 @@ struct qemud_vm { struct qemud_vm *next; }; +/* Store start and end addresses of a dhcp range */ +struct qemud_dhcp_range_def { + char start[BR_INET_ADDR_MAXLEN]; + char end[BR_INET_ADDR_MAXLEN]; + + struct qemud_dhcp_range_def *next; +}; + /* Virtual Network main configuration */ struct qemud_network_def { unsigned char uuid[QEMUD_UUID_RAW_LEN]; @@ -211,6 +219,9 @@ struct qemud_network_def { char ipAddress[BR_INET_ADDR_MAXLEN]; char netmask[BR_INET_ADDR_MAXLEN]; + + int nranges; + struct qemud_dhcp_range_def *ranges; }; /* Virtual Network runtime state */ @@ -220,6 +231,7 @@ struct qemud_network { struct qemud_network_def def; char bridge[BR_IFNAME_MAXLEN]; + int dnsmasqPid; unsigned int active : 1; Index: libvirt-foo/qemud/qemud.c =================================================================== --- libvirt-foo.orig/qemud/qemud.c 2007-02-14 15:59:02.000000000 +0000 +++ libvirt-foo.orig/qemud/qemud.c 2007-02-14 15:59:02.000000000 +0000 @@ -703,6 +703,105 @@ static int qemudDispatchVMFailure(struct return 0; } +static int +qemudBuildDnsmasqArgv(struct qemud_server *server, + struct qemud_network *network, + char ***argv) { + int i, len; + char buf[BR_INET_ADDR_MAXLEN * 2]; + struct qemud_dhcp_range_def *range; + + len = + 1 + /* dnsmasq */ + 1 + /* --keep-in-foreground */ + 1 + /* --bind-interfaces */ + 2 + /* --pid-file "" */ + 2 + /* --conf-file "" */ + 2 + /* --except-interface lo */ + 2 + /* --listen-address 10.0.0.1 */ + (2 * network->def.nranges) + /* --dhcp-range 10.0.0.2,10.0.0.254 */ + 1; /* NULL */ + + if (!(*argv = malloc(len * sizeof(char *)))) + goto no_memory; + + memset(*argv, 0, len * sizeof(char *)); + +#define APPEND_ARG(v, n, s) do { \ + if (!((v)[(n)] = strdup(s))) \ + goto no_memory; \ + } while (0) + + i = 0; + + APPEND_ARG(*argv, i++, "dnsmasq"); + + APPEND_ARG(*argv, i++, "--keep-in-foreground"); + APPEND_ARG(*argv, i++, "--bind-interfaces"); + + APPEND_ARG(*argv, i++, "--pid-file"); + APPEND_ARG(*argv, i++, ""); + + APPEND_ARG(*argv, i++, "--conf-file"); + APPEND_ARG(*argv, i++, ""); + + APPEND_ARG(*argv, i++, "--except-interface"); + APPEND_ARG(*argv, i++, "lo"); + + APPEND_ARG(*argv, i++, "--listen-address"); + APPEND_ARG(*argv, i++, network->def.ipAddress); + + range = network->def.ranges; + while (range) { + snprintf(buf, sizeof(buf), "%s,%s", + range->start, range->end); + + APPEND_ARG(*argv, i++, "--dhcp-range"); + APPEND_ARG(*argv, i++, buf); + + range = range->next; + } + +#undef APPEND_ARG + + return 0; + + no_memory: + if (argv) { + for (i = 0; (*argv)[i]; i++) + free((*argv)[i]); + free(*argv); + } + qemudReportError(server, VIR_ERR_NO_MEMORY, "dnsmasq argv"); + return -1; +} + + +static int +dhcpStartDhcpDaemon(struct qemud_server *server, + struct qemud_network *network) +{ + char **argv; + int ret, i; + + if (network->def.ipAddress[0] == '\0') { + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, + "cannot start dhcp daemon without IP address for server"); + return -1; + } + + argv = NULL; + if (qemudBuildDnsmasqArgv(server, network, &argv) < 0) + return -1; + + ret = qemudExec(server, argv, &network->dnsmasqPid, NULL, NULL); + + for (i = 0; argv[i]; i++) + free(argv[i]); + free(argv); + + return ret; +} int qemudStartNetworkDaemon(struct qemud_server *server, struct qemud_network *network) { @@ -758,10 +857,21 @@ int qemudStartNetworkDaemon(struct qemud goto err_delbr; } + if (network->def.ranges && + dhcpStartDhcpDaemon(server, network) < 0) + goto err_delbr1; + network->active = 1; return 0; + err_delbr1: + if (network->def.ipAddress[0] && + (err = brSetInterfaceUp(server->brctl, network->bridge, 0))) { + printf("Damn! Failed to bring down bridge '%s' : %s\n", + network->bridge, strerror(err)); + } + err_delbr: if ((err = brDeleteBridge(server->brctl, network->bridge))) { printf("Damn! Couldn't delete bridge '%s' : %s\n", @@ -780,6 +890,9 @@ int qemudShutdownNetworkDaemon(struct qe if (!network->active) return 0; + if (network->dnsmasqPid > 0) + kill(network->dnsmasqPid, SIGTERM); + if (network->def.ipAddress[0] && (err = brSetInterfaceUp(server->brctl, network->bridge, 0))) { printf("Damn! Failed to bring down bridge '%s' : %s\n", @@ -812,7 +925,15 @@ int qemudShutdownNetworkDaemon(struct qe curr = curr->next; } + if (network->dnsmasqPid > 0 && + waitpid(network->dnsmasqPid, NULL, WNOHANG) != network->dnsmasqPid) { + kill(network->dnsmasqPid, SIGKILL); + if (waitpid(network->dnsmasqPid, NULL, 0) != network->dnsmasqPid) + printf("Got unexpected pid for dnsmasq, damn\n"); + } + network->bridge[0] = '\0'; + network->dnsmasqPid = -1; network->active = 0; return 0; --

Add support for a "network" type of network interface e.g. <interface type='network'> <source network="Foo" /> </interface> When starting a QEMU instance with one of these interfaces, create a TAP device, bring it up, enslave it to the appropriate bridge and pass the tap file descriptor to qemu. Signed-off-by: Mark McLoughlin <markmc@redhat.com> Index: libvirt-foo/qemud/conf.c =================================================================== --- libvirt-foo.orig/qemud/conf.c 2007-02-14 16:03:29.000000000 +0000 +++ libvirt-foo.orig/qemud/conf.c 2007-02-14 16:03:29.000000000 +0000 @@ -366,6 +366,8 @@ static struct qemud_vm_net_def *qemudPar xmlNodePtr cur; xmlChar *macaddr = NULL; xmlChar *type = NULL; + xmlChar *network = NULL; + xmlChar *tapifname = NULL; if (!net) { qemudReportError(server, VIR_ERR_NO_MEMORY, "net"); @@ -386,6 +388,8 @@ static struct qemud_vm_net_def *qemudPar net->type = QEMUD_NET_CLIENT; else if (xmlStrEqual(type, BAD_CAST "mcast")) net->type = QEMUD_NET_MCAST; + else if (xmlStrEqual(type, BAD_CAST "network")) + net->type = QEMUD_NET_NETWORK; /* else if (xmlStrEqual(type, BAD_CAST "vde")) typ = QEMUD_NET_VDE; @@ -402,6 +406,14 @@ static struct qemud_vm_net_def *qemudPar if ((macaddr == NULL) && (xmlStrEqual(cur->name, BAD_CAST "mac"))) { macaddr = xmlGetProp(cur, BAD_CAST "address"); + } else if ((network == NULL) && + (net->type == QEMUD_NET_NETWORK) && + (xmlStrEqual(cur->name, BAD_CAST "source"))) { + network = xmlGetProp(cur, BAD_CAST "network"); + } else if ((tapifname == NULL) && + (net->type == QEMUD_NET_NETWORK) && + xmlStrEqual(cur->name, BAD_CAST "tap")) { + tapifname = xmlGetProp(cur, BAD_CAST "ifname"); } } cur = cur->next; @@ -421,7 +433,47 @@ static struct qemud_vm_net_def *qemudPar xmlFree(macaddr); } + if (net->type == QEMUD_NET_NETWORK) { + int len; + + if (network == NULL) { + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, + "No <source> 'network' attribute specified with <interface type='network'/>"); + goto error; + } else if ((len = xmlStrlen(network)) >= QEMUD_MAX_NAME_LEN) { + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, + "Network name '%s' too long", network); + goto error; + } else { + strncpy(net->dst.network.name, (char *)network, len); + net->dst.network.name[len] = '\0'; + } + + if (network) + xmlFree(network); + + if (tapifname != NULL) { + if ((len == xmlStrlen(tapifname)) >= BR_IFNAME_MAXLEN) { + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, + "TAP interface name '%s' is too long", tapifname); + goto error; + } else { + strncpy(net->dst.network.tapifname, (char *)tapifname, len); + net->dst.network.tapifname[len] = '\0'; + } + xmlFree(tapifname); + } + } + return net; + + error: + if (network) + xmlFree(network); + if (tapifname) + xmlFree(tapifname); + free(net); + return NULL; } @@ -770,6 +822,68 @@ static int qemudParseXML(struct qemud_se } +static char * +qemudNetworkIfaceConnect(struct qemud_server *server, + struct qemud_vm *vm, + struct qemud_vm_net_def *net) +{ + struct qemud_network *network; + const char *tapifname; + char tapfdstr[4+3+32+7]; + char *retval = NULL; + int err; + int tapfd = -1; + int *tapfds; + + if (!(network = qemudFindNetworkByName(server, net->dst.network.name))) { + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, + "Network '%s' not found", net->dst.network.name); + goto error; + } else if (network->bridge[0] == '\0') { + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, + "Network '%s' not active", net->dst.network.name); + goto error; + } + + if (net->dst.network.tapifname[0] == '\0' || + strchr(net->dst.network.tapifname, '%')) { + tapifname = "vnet%d"; + } else { + tapifname = net->dst.network.tapifname; + } + + if ((err = brAddTap(server->brctl, network->bridge, tapifname, + &net->dst.network.tapifname[0], BR_IFNAME_MAXLEN, &tapfd))) { + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, + "Failed to add tap interface '%s' to bridge '%s' : %s", + tapifname, network->bridge, strerror(err)); + goto error; + } + + snprintf(tapfdstr, sizeof(tapfdstr), "tap,fd=%d,script=", tapfd); + + if (!(retval = strdup(tapfdstr))) + goto no_memory; + + if (!(tapfds = realloc(vm->tapfds, sizeof(int) * (vm->ntapfds+2)))) + goto no_memory; + + vm->tapfds = tapfds; + vm->tapfds[vm->ntapfds++] = tapfd; + vm->tapfds[vm->ntapfds] = -1; + + return retval; + + no_memory: + qemudReportError(server, VIR_ERR_NO_MEMORY, "tapfds"); + error: + if (retval) + free(retval); + if (tapfd != -1) + close(tapfd); + return NULL; +} + /* * Constructs a argv suitable for launching qemu with config defined * for a given virtual machine. @@ -921,9 +1035,15 @@ int qemudBuildCommandLine(struct qemud_s goto no_memory; if (!((*argv)[++n] = strdup("-net"))) goto no_memory; - /* XXX don't hardcode user */ - if (!((*argv)[++n] = strdup("user"))) - goto no_memory; + + if (net->type != QEMUD_NET_NETWORK) { + /* XXX don't hardcode user */ + if (!((*argv)[++n] = strdup("user"))) + goto no_memory; + } else { + if (!((*argv)[++n] = qemudNetworkIfaceConnect(server, vm, net))) + goto error; + } net = net->next; } @@ -948,12 +1068,20 @@ int qemudBuildCommandLine(struct qemud_s return 0; no_memory: + qemudReportError(server, VIR_ERR_NO_MEMORY, "argv"); + error: + if (vm->tapfds) { + for (i = 0; vm->tapfds[i] != -1; i++) + close(vm->tapfds[i]); + free(vm->tapfds); + vm->tapfds = NULL; + vm->ntapfds = 0; + } if (argv) { for (i = 0 ; i < n ; i++) free((*argv)[i]); free(*argv); } - qemudReportError(server, VIR_ERR_NO_MEMORY, "argv"); return -1; } @@ -1716,6 +1844,18 @@ char *qemudGenerateXML(struct qemud_serv net->mac[3], net->mac[4], net->mac[5]) < 0) goto no_memory; + if (net->type == QEMUD_NET_NETWORK) { + if (qemudBufferPrintf(&buf, " <network name='%s", net->dst.network.name) < 0) + goto no_memory; + + if (net->dst.network.tapifname[0] != '\0' && + qemudBufferPrintf(&buf, " tapifname='%s'", net->dst.network.tapifname) < 0) + goto no_memory; + + if (qemudBufferPrintf(&buf, "/>\n") < 0) + goto no_memory; + } + if (qemudBufferPrintf(&buf, " </interface>\n") < 0) goto no_memory; Index: libvirt-foo/qemud/internal.h =================================================================== --- libvirt-foo.orig/qemud/internal.h 2007-02-14 16:03:29.000000000 +0000 +++ libvirt-foo.orig/qemud/internal.h 2007-02-14 16:03:29.000000000 +0000 @@ -95,6 +95,7 @@ enum qemud_vm_net_type { QEMUD_NET_SERVER, QEMUD_NET_CLIENT, QEMUD_NET_MCAST, + QEMUD_NET_NETWORK, /* QEMUD_NET_VDE*/ }; @@ -123,6 +124,10 @@ struct qemud_vm_net_def { struct { char vlan[PATH_MAX]; } vde; + struct { + char name[QEMUD_MAX_NAME_LEN]; + char tapifname[BR_IFNAME_MAXLEN]; + } network; } dst; struct qemud_vm_net_def *next; @@ -193,6 +198,9 @@ struct qemud_vm { int monitor; int pid; + int *tapfds; + int ntapfds; + char configFile[PATH_MAX]; struct qemud_vm_def def; Index: libvirt-foo/qemud/qemud.c =================================================================== --- libvirt-foo.orig/qemud/qemud.c 2007-02-14 16:03:29.000000000 +0000 +++ libvirt-foo.orig/qemud/qemud.c 2007-02-14 16:03:29.000000000 +0000 @@ -333,8 +333,23 @@ static int qemudDispatchServer(struct qe static int +qemudLeaveFdOpen(int *openfds, int fd) +{ + int i; + + if (!openfds) + return 0; + + for (i = 0; openfds[i] != -1; i++) + if (fd == openfds[i]) + return 1; + + return 0; +} + +static int qemudExec(struct qemud_server *server, char **argv, - int *retpid, int *outfd, int *errfd) { + int *retpid, int *outfd, int *errfd, int *openfds) { int pid, null; int pipeout[2] = {-1,-1}; int pipeerr[2] = {-1,-1}; @@ -392,7 +407,8 @@ qemudExec(struct qemud_server *server, c for (i = 0; i < open_max; i++) if (i != STDOUT_FILENO && i != STDERR_FILENO && - i != STDIN_FILENO) + i != STDIN_FILENO && + !qemudLeaveFdOpen(openfds, i)) close(i); execvp(argv[0], argv); @@ -429,10 +445,20 @@ int qemudStartVMDaemon(struct qemud_serv if (qemudBuildCommandLine(server, vm, &argv) < 0) return -1; - if (qemudExec(server, argv, &vm->pid, &vm->stdout, &vm->stderr) == 0) { + if (qemudExec(server, argv, &vm->pid, &vm->stdout, &vm->stderr, vm->tapfds) == 0) { vm->def.id = server->nextvmid++; ret = 0; } + + if (vm->tapfds) { + for (i = 0; vm->tapfds[i] != -1; i++) { + close(vm->tapfds[i]); + vm->tapfds[i] = -1; + } + free(vm->tapfds); + vm->tapfds = NULL; + vm->ntapfds = 0; + } for (i = 0 ; argv[i] ; i++) free(argv[i]); @@ -632,8 +658,17 @@ static int qemudVMData(struct qemud_serv } } +static void +qemudNetworkIfaceDisconnect(struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_vm *vm ATTRIBUTE_UNUSED, + struct qemud_vm_net_def *net) { + /* FIXME: will be needed to remove iptables rules */ + net = NULL; +} + int qemudShutdownVMDaemon(struct qemud_server *server, struct qemud_vm *vm) { struct qemud_vm *prev = NULL, *curr = server->activevms; + struct qemud_vm_net_def *net; /* Already cleaned-up */ if (vm->pid < 0) @@ -676,6 +711,13 @@ int qemudShutdownVMDaemon(struct qemud_s curr->monitor = -1; server->nvmfds -= 2; + net = vm->def.nets; + while (net) { + if (net->type == QEMUD_NET_NETWORK) + qemudNetworkIfaceDisconnect(server, vm, net); + net = net->next; + } + if (waitpid(vm->pid, NULL, WNOHANG) != vm->pid) { kill(vm->pid, SIGKILL); if (waitpid(vm->pid, NULL, 0) != vm->pid) { @@ -794,7 +836,7 @@ dhcpStartDhcpDaemon(struct qemud_server if (qemudBuildDnsmasqArgv(server, network, &argv) < 0) return -1; - ret = qemudExec(server, argv, &network->dnsmasqPid, NULL, NULL); + ret = qemudExec(server, argv, &network->dnsmasqPid, NULL, NULL, NULL); for (i = 0; argv[i]; i++) free(argv[i]); --

In order to be able to call virNetworkGetBridgeName() from virDomainParseXMLIfDesc() we need to pass the virConnectPtr to virDomainParseXMLDesc() Also, pass the connection pointer to virXMLError(). Note, we need this so that we can lookup which bridge the guest's backend interface should be enslaved to in order for the guest to join a specific network. Signed-off-by: Mark McLoughlin <markmc@redhat.com> Index: libvirt-foo/src/xend_internal.c =================================================================== --- libvirt-foo.orig/src/xend_internal.c 2007-02-14 15:37:08.000000000 +0000 +++ libvirt-foo.orig/src/xend_internal.c 2007-02-14 15:37:08.000000000 +0000 @@ -2828,7 +2828,7 @@ xenDaemonCreateLinux(virConnectPtr conn, return (NULL); } - sexpr = virDomainParseXMLDesc(xmlDesc, &name, conn->xendConfigVersion); + sexpr = virDomainParseXMLDesc(conn, xmlDesc, &name, conn->xendConfigVersion); if ((sexpr == NULL) || (name == NULL)) { virXendError(conn, VIR_ERR_XML_ERROR, "domain"); if (sexpr != NULL) @@ -2897,7 +2897,7 @@ xenDaemonAttachDevice(virDomainPtr domai if (strcmp(virDomainGetOSType(domain), "linux")) hvm = 1; - sexpr = virParseXMLDevice(xml, hvm, domain->conn->xendConfigVersion); + sexpr = virParseXMLDevice(domain->conn, xml, hvm, domain->conn->xendConfigVersion); if (sexpr == NULL) return (-1); if (!memcmp(sexpr, "(device ", 8)) { @@ -2954,7 +2954,7 @@ virDomainPtr xenDaemonDomainDefineXML(vi if (conn->xendConfigVersion < 3) return(NULL); - sexpr = virDomainParseXMLDesc(xmlDesc, &name, conn->xendConfigVersion); + sexpr = virDomainParseXMLDesc(conn, xmlDesc, &name, conn->xendConfigVersion); if ((sexpr == NULL) || (name == NULL)) { virXendError(conn, VIR_ERR_XML_ERROR, "domain"); if (sexpr != NULL) Index: libvirt-foo/src/xm_internal.c =================================================================== --- libvirt-foo.orig/src/xm_internal.c 2007-02-14 15:37:08.000000000 +0000 +++ libvirt-foo.orig/src/xm_internal.c 2007-02-14 15:37:08.000000000 +0000 @@ -1287,7 +1287,7 @@ int xenXMDomainCreate(virDomainPtr domai if (!(xml = xenXMDomainDumpXML(domain, 0))) return (-1); - if (!(sexpr = virDomainParseXMLDesc(xml, NULL, domain->conn->xendConfigVersion))) { + if (!(sexpr = virDomainParseXMLDesc(domain->conn, xml, NULL, domain->conn->xendConfigVersion))) { free(xml); return (-1); } Index: libvirt-foo/src/xml.c =================================================================== --- libvirt-foo.orig/src/xml.c 2007-02-14 15:37:08.000000000 +0000 +++ libvirt-foo.orig/src/xml.c 2007-02-14 15:37:08.000000000 +0000 @@ -26,7 +26,7 @@ #include "xs_internal.h" /* for xenStoreDomainGetNetworkID */ static void -virXMLError(virErrorNumber error, const char *info, int value) +virXMLError(virConnectPtr conn, virErrorNumber error, const char *info, int value) { const char *errmsg; @@ -34,7 +34,7 @@ virXMLError(virErrorNumber error, const return; errmsg = __virErrorMsg(error, info); - __virRaiseError(NULL, NULL, NULL, VIR_FROM_XML, error, VIR_ERR_ERROR, + __virRaiseError(conn, NULL, NULL, VIR_FROM_XML, error, VIR_ERR_ERROR, errmsg, info, NULL, value, 0, errmsg, info, value); } @@ -62,7 +62,7 @@ virBufferGrow(virBufferPtr buf, unsigned newbuf = (char *) realloc(buf->content, size); if (newbuf == NULL) { - virXMLError(VIR_ERR_NO_MEMORY, _("growing buffer"), size); + virXMLError(NULL, VIR_ERR_NO_MEMORY, _("growing buffer"), size); return (-1); } buf->content = newbuf; @@ -114,11 +114,11 @@ virBufferNew(unsigned int size) virBufferPtr buf; if (!(buf = malloc(sizeof(*buf)))) { - virXMLError(VIR_ERR_NO_MEMORY, _("allocate new buffer"), sizeof(*buf)); + virXMLError(NULL, VIR_ERR_NO_MEMORY, _("allocate new buffer"), sizeof(*buf)); return NULL; } if (size && (buf->content = malloc(size))==NULL) { - virXMLError(VIR_ERR_NO_MEMORY, _("allocate buffer content"), size); + virXMLError(NULL, VIR_ERR_NO_MEMORY, _("allocate buffer content"), size); free(buf); return NULL; } @@ -213,6 +213,7 @@ virBufferStrcat(virBufferPtr buf, ...) #ifndef PROXY /** * virtDomainParseXMLGraphicsDescImage: + * @conn: pointer to the hypervisor connection * @node: node containing graphics description * @buf: a buffer for the result S-Expr * @xendConfigVersion: xend configuration file format @@ -224,7 +225,7 @@ virBufferStrcat(virBufferPtr buf, ...) * * Returns 0 in case of success, -1 in case of error */ -static int virDomainParseXMLGraphicsDescImage(xmlNodePtr node, virBufferPtr buf, int xendConfigVersion) +static int virDomainParseXMLGraphicsDescImage(virConnectPtr conn ATTRIBUTE_UNUSED, xmlNodePtr node, virBufferPtr buf, int xendConfigVersion) { xmlChar *graphics_type = NULL; @@ -271,6 +272,7 @@ static int virDomainParseXMLGraphicsDesc /** * virtDomainParseXMLGraphicsDescVFB: + * @conn: pointer to the hypervisor connection * @node: node containing graphics description * @buf: a buffer for the result S-Expr * @@ -281,7 +283,7 @@ static int virDomainParseXMLGraphicsDesc * * Returns 0 in case of success, -1 in case of error */ -static int virDomainParseXMLGraphicsDescVFB(xmlNodePtr node, virBufferPtr buf) +static int virDomainParseXMLGraphicsDescVFB(virConnectPtr conn ATTRIBUTE_UNUSED, xmlNodePtr node, virBufferPtr buf) { xmlChar *graphics_type = NULL; @@ -329,6 +331,7 @@ static int virDomainParseXMLGraphicsDesc /** * virDomainParseXMLOSDescHVM: + * @conn: pointer to the hypervisor connection * @node: node containing HVM OS description * @buf: a buffer for the result S-Expr * @ctxt: a path context representing the XML description @@ -343,7 +346,7 @@ static int virDomainParseXMLGraphicsDesc * Returns 0 in case of success, -1 in case of error. */ static int -virDomainParseXMLOSDescHVM(xmlNodePtr node, virBufferPtr buf, xmlXPathContextPtr ctxt, int vcpus, int xendConfigVersion) +virDomainParseXMLOSDescHVM(virConnectPtr conn, xmlNodePtr node, virBufferPtr buf, xmlXPathContextPtr ctxt, int vcpus, int xendConfigVersion) { xmlXPathObjectPtr obj = NULL; xmlNodePtr cur, txt; @@ -376,12 +379,12 @@ virDomainParseXMLOSDescHVM(xmlNodePtr no } if ((type == NULL) || (!xmlStrEqual(type, BAD_CAST "hvm"))) { /* VIR_ERR_OS_TYPE */ - virXMLError(VIR_ERR_OS_TYPE, (const char *) type, 0); + virXMLError(conn, VIR_ERR_OS_TYPE, (const char *) type, 0); return (-1); } virBufferAdd(buf, "(image (hvm ", 12); if (loader == NULL) { - virXMLError(VIR_ERR_NO_KERNEL, NULL, 0); + virXMLError(conn, VIR_ERR_NO_KERNEL, NULL, 0); goto error; } else { virBufferVSprintf(buf, "(kernel '%s')", (const char *) loader); @@ -391,7 +394,7 @@ virDomainParseXMLOSDescHVM(xmlNodePtr no obj = xmlXPathEval(BAD_CAST "string(/domain/devices/emulator[1])", ctxt); if ((obj == NULL) || (obj->type != XPATH_STRING) || (obj->stringval == NULL) || (obj->stringval[0] == 0)) { - virXMLError(VIR_ERR_NO_KERNEL, NULL, 0); /* TODO: error */ + virXMLError(conn, VIR_ERR_NO_KERNEL, NULL, 0); /* TODO: error */ goto error; } virBufferVSprintf(buf, "(device_model '%s')", @@ -410,7 +413,7 @@ virDomainParseXMLOSDescHVM(xmlNodePtr no virBufferVSprintf(buf, "(boot c)", (const char *) boot_dev); } else { /* Any other type of boot dev is unsupported right now */ - virXMLError(VIR_ERR_XML_ERROR, NULL, 0); + virXMLError(conn, VIR_ERR_XML_ERROR, NULL, 0); } /* get the 1st floppy device file */ @@ -491,7 +494,7 @@ virDomainParseXMLOSDescHVM(xmlNodePtr no obj = xmlXPathEval(BAD_CAST "count(domain/devices/console) > 0", ctxt); if ((obj == NULL) || (obj->type != XPATH_BOOLEAN)) { - virXMLError(VIR_ERR_XML_ERROR, NULL, 0); + virXMLError(conn, VIR_ERR_XML_ERROR, NULL, 0); goto error; } if (obj->boolval) { @@ -504,7 +507,7 @@ virDomainParseXMLOSDescHVM(xmlNodePtr no obj = xmlXPathEval(BAD_CAST "/domain/devices/graphics[1]", ctxt); if ((obj != NULL) && (obj->type == XPATH_NODESET) && (obj->nodesetval != NULL) && (obj->nodesetval->nodeNr > 0)) { - res = virDomainParseXMLGraphicsDescImage(obj->nodesetval->nodeTab[0], buf, xendConfigVersion); + res = virDomainParseXMLGraphicsDescImage(conn, obj->nodesetval->nodeTab[0], buf, xendConfigVersion); if (res != 0) { goto error; } @@ -527,6 +530,7 @@ virDomainParseXMLOSDescHVM(xmlNodePtr no /** * virDomainParseXMLOSDescPV: + * @conn: pointer to the hypervisor connection * @node: node containing PV OS description * @buf: a buffer for the result S-Expr * @ctxt: a path context representing the XML description @@ -540,7 +544,7 @@ virDomainParseXMLOSDescHVM(xmlNodePtr no * Returns 0 in case of success, -1 in case of error. */ static int -virDomainParseXMLOSDescPV(xmlNodePtr node, virBufferPtr buf, xmlXPathContextPtr ctxt, int xendConfigVersion) +virDomainParseXMLOSDescPV(virConnectPtr conn, xmlNodePtr node, virBufferPtr buf, xmlXPathContextPtr ctxt, int xendConfigVersion) { xmlNodePtr cur, txt; xmlXPathObjectPtr obj = NULL; @@ -590,12 +594,12 @@ virDomainParseXMLOSDescPV(xmlNodePtr nod } if ((type != NULL) && (!xmlStrEqual(type, BAD_CAST "linux"))) { /* VIR_ERR_OS_TYPE */ - virXMLError(VIR_ERR_OS_TYPE, (const char *) type, 0); + virXMLError(conn, VIR_ERR_OS_TYPE, (const char *) type, 0); return (-1); } virBufferAdd(buf, "(image (linux ", 14); if (kernel == NULL) { - virXMLError(VIR_ERR_NO_KERNEL, NULL, 0); + virXMLError(conn, VIR_ERR_NO_KERNEL, NULL, 0); return (-1); } else { virBufferVSprintf(buf, "(kernel '%s')", (const char *) kernel); @@ -613,7 +617,7 @@ virDomainParseXMLOSDescPV(xmlNodePtr nod obj = xmlXPathEval(BAD_CAST "/domain/devices/graphics[1]", ctxt); if ((obj != NULL) && (obj->type == XPATH_NODESET) && (obj->nodesetval != NULL) && (obj->nodesetval->nodeNr > 0)) { - res = virDomainParseXMLGraphicsDescImage(obj->nodesetval->nodeTab[0], buf, xendConfigVersion); + res = virDomainParseXMLGraphicsDescImage(conn, obj->nodesetval->nodeTab[0], buf, xendConfigVersion); if (res != 0) { goto error; } @@ -642,7 +646,7 @@ virCatchXMLParseError(void *ctx, const c if ((ctxt != NULL) && (ctxt->lastError.level == XML_ERR_FATAL) && (ctxt->lastError.message != NULL)) { - virXMLError(VIR_ERR_XML_DETAIL, ctxt->lastError.message, + virXMLError(NULL, VIR_ERR_XML_DETAIL, ctxt->lastError.message, ctxt->lastError.line); } } @@ -650,6 +654,7 @@ virCatchXMLParseError(void *ctx, const c /** * virDomainParseXMLDiskDesc: * @node: node containing disk description + * @conn: pointer to the hypervisor connection * @buf: a buffer for the result S-Expr * @xendConfigVersion: xend configuration file format * @@ -661,7 +666,7 @@ virCatchXMLParseError(void *ctx, const c * Returns 0 in case of success, -1 in case of error. */ static int -virDomainParseXMLDiskDesc(xmlNodePtr node, virBufferPtr buf, int hvm, int xendConfigVersion) +virDomainParseXMLDiskDesc(virConnectPtr conn, xmlNodePtr node, virBufferPtr buf, int hvm, int xendConfigVersion) { xmlNodePtr cur; xmlChar *type = NULL; @@ -713,7 +718,7 @@ virDomainParseXMLDiskDesc(xmlNodePtr nod } if (source == NULL) { - virXMLError(VIR_ERR_NO_SOURCE, (const char *) target, 0); + virXMLError(conn, VIR_ERR_NO_SOURCE, (const char *) target, 0); if (target != NULL) xmlFree(target); @@ -722,7 +727,7 @@ virDomainParseXMLDiskDesc(xmlNodePtr nod return (-1); } if (target == NULL) { - virXMLError(VIR_ERR_NO_TARGET, (const char *) source, 0); + virXMLError(conn, VIR_ERR_NO_TARGET, (const char *) source, 0); if (source != NULL) xmlFree(source); if (device != NULL) @@ -816,6 +821,7 @@ virDomainParseXMLDiskDesc(xmlNodePtr nod /** * virDomainParseXMLIfDesc: + * @conn: pointer to the hypervisor connection * @node: node containing the interface description * @buf: a buffer for the result S-Expr * @@ -827,7 +833,7 @@ virDomainParseXMLDiskDesc(xmlNodePtr nod * Returns 0 in case of success, -1 in case of error. */ static int -virDomainParseXMLIfDesc(xmlNodePtr node, virBufferPtr buf, int hvm) +virDomainParseXMLIfDesc(virConnectPtr conn ATTRIBUTE_UNUSED, xmlNodePtr node, virBufferPtr buf, int hvm) { xmlNodePtr cur; xmlChar *type = NULL; @@ -902,6 +908,7 @@ virDomainParseXMLIfDesc(xmlNodePtr node, /** * virDomainParseXMLDesc: + * @conn: pointer to the hypervisor connection * @xmldesc: string with the XML description * @xendConfigVersion: xend configuration file format * @@ -914,7 +921,7 @@ virDomainParseXMLIfDesc(xmlNodePtr node, * the caller must free() the returned value. */ char * -virDomainParseXMLDesc(const char *xmldesc, char **name, int xendConfigVersion) +virDomainParseXMLDesc(virConnectPtr conn, const char *xmldesc, char **name, int xendConfigVersion) { xmlDocPtr xml = NULL; xmlNodePtr node; @@ -945,6 +952,9 @@ virDomainParseXMLDesc(const char *xmldes goto error; } + /* TODO pass the connection point to the error handler: + * pctxt->userData = virConnectPtr; + */ pctxt->sax->error = virCatchXMLParseError; xml = xmlCtxtReadDoc(pctxt, (const xmlChar *) xmldesc, "domain.xml", NULL, @@ -976,13 +986,13 @@ virDomainParseXMLDesc(const char *xmldes obj = xmlXPathEval(BAD_CAST "string(/domain/name[1])", ctxt); if ((obj == NULL) || (obj->type != XPATH_STRING) || (obj->stringval == NULL) || (obj->stringval[0] == 0)) { - virXMLError(VIR_ERR_NO_NAME, xmldesc, 0); + virXMLError(conn, VIR_ERR_NO_NAME, xmldesc, 0); goto error; } virBufferVSprintf(&buf, "(name '%s')", obj->stringval); nam = strdup((const char *) obj->stringval); if (nam == NULL) { - virXMLError(VIR_ERR_NO_MEMORY, "copying name", 0); + virXMLError(conn, VIR_ERR_NO_MEMORY, "copying name", 0); goto error; } xmlXPathFreeObject(obj); @@ -1069,17 +1079,17 @@ virDomainParseXMLDesc(const char *xmldes ((tmpobj->type != XPATH_STRING) || (tmpobj->stringval == NULL) || (tmpobj->stringval[0] == 0))) { xmlXPathFreeObject(tmpobj); - virXMLError(VIR_ERR_OS_TYPE, nam, 0); + virXMLError(conn, VIR_ERR_OS_TYPE, nam, 0); goto error; } if ((tmpobj == NULL) || !xmlStrEqual(tmpobj->stringval, BAD_CAST "hvm")) { - res = virDomainParseXMLOSDescPV(obj->nodesetval->nodeTab[0], + res = virDomainParseXMLOSDescPV(conn, obj->nodesetval->nodeTab[0], &buf, ctxt, xendConfigVersion); } else { hvm = 1; - res = virDomainParseXMLOSDescHVM(obj->nodesetval->nodeTab[0], + res = virDomainParseXMLOSDescHVM(conn, obj->nodesetval->nodeTab[0], &buf, ctxt, vcpus, xendConfigVersion); } @@ -1088,7 +1098,7 @@ virDomainParseXMLDesc(const char *xmldes if (res != 0) goto error; } else if (bootloader == 0) { - virXMLError(VIR_ERR_NO_OS, nam, 0); + virXMLError(conn, VIR_ERR_NO_OS, nam, 0); goto error; } xmlXPathFreeObject(obj); @@ -1099,7 +1109,7 @@ virDomainParseXMLDesc(const char *xmldes if ((obj != NULL) && (obj->type == XPATH_NODESET) && (obj->nodesetval != NULL) && (obj->nodesetval->nodeNr >= 0)) { for (i = 0; i < obj->nodesetval->nodeNr; i++) { - res = virDomainParseXMLDiskDesc(obj->nodesetval->nodeTab[i], &buf, hvm, xendConfigVersion); + res = virDomainParseXMLDiskDesc(conn, obj->nodesetval->nodeTab[i], &buf, hvm, xendConfigVersion); if (res != 0) { goto error; } @@ -1112,7 +1122,7 @@ virDomainParseXMLDesc(const char *xmldes (obj->nodesetval != NULL) && (obj->nodesetval->nodeNr >= 0)) { for (i = 0; i < obj->nodesetval->nodeNr; i++) { virBufferAdd(&buf, "(device ", 8); - res = virDomainParseXMLIfDesc(obj->nodesetval->nodeTab[i], &buf, hvm); + res = virDomainParseXMLIfDesc(conn, obj->nodesetval->nodeTab[i], &buf, hvm); if (res != 0) { goto error; } @@ -1127,7 +1137,7 @@ virDomainParseXMLDesc(const char *xmldes if ((obj != NULL) && (obj->type == XPATH_NODESET) && (obj->nodesetval != NULL) && (obj->nodesetval->nodeNr >= 0)) { for (i = 0; i < obj->nodesetval->nodeNr; i++) { - res = virDomainParseXMLGraphicsDescVFB(obj->nodesetval->nodeTab[i], &buf); + res = virDomainParseXMLGraphicsDescVFB(conn, obj->nodesetval->nodeTab[i], &buf); if (res != 0) { goto error; } @@ -1232,6 +1242,7 @@ unsigned char *virParseUUID(char **ptr, #ifndef PROXY /** * virParseXMLDevice: + * @conn: pointer to the hypervisor connection * @xmldesc: string with the XML description * @hvm: 1 for fully virtualized guest, 0 for paravirtualized * @xendConfigVersion: xend configuration file format @@ -1245,7 +1256,7 @@ unsigned char *virParseUUID(char **ptr, * the caller must free() the returned value. */ char * -virParseXMLDevice(char *xmldesc, int hvm, int xendConfigVersion) +virParseXMLDevice(virConnectPtr conn, char *xmldesc, int hvm, int xendConfigVersion) { xmlDocPtr xml = NULL; xmlNodePtr node; @@ -1265,11 +1276,11 @@ virParseXMLDevice(char *xmldesc, int hvm if (node == NULL) goto error; if (xmlStrEqual(node->name, BAD_CAST "disk")) { - if (virDomainParseXMLDiskDesc(node, &buf, hvm, xendConfigVersion) != 0) + if (virDomainParseXMLDiskDesc(conn, node, &buf, hvm, xendConfigVersion) != 0) goto error; } else if (xmlStrEqual(node->name, BAD_CAST "interface")) { - if (virDomainParseXMLIfDesc(node, &buf, hvm) != 0) + if (virDomainParseXMLIfDesc(conn, node, &buf, hvm) != 0) goto error; } cleanup: Index: libvirt-foo/src/xml.h =================================================================== --- libvirt-foo.orig/src/xml.h 2006-11-16 18:11:29.000000000 +0000 +++ libvirt-foo.orig/src/xml.h 2006-11-16 18:11:29.000000000 +0000 @@ -29,9 +29,9 @@ void virBufferFree(virBufferPtr buf); int virBufferAdd(virBufferPtr buf, const char *str, int len); int virBufferVSprintf(virBufferPtr buf, const char *format, ...); int virBufferStrcat(virBufferPtr buf, ...); -char *virDomainParseXMLDesc(const char *xmldesc, char **name, int xendConfigVersion); +char *virDomainParseXMLDesc(virConnectPtr conn, const char *xmldesc, char **name, int xendConfigVersion); unsigned char *virParseUUID(char **ptr, const char *uuid); -char *virParseXMLDevice(char *xmldesc, int hvm, int xendConfigVersion); +char *virParseXMLDevice(virConnectPtr conn, char *xmldesc, int hvm, int xendConfigVersion); int virDomainXMLDevID(virDomainPtr domain, char *xmldesc, char *class, char *ref); #ifdef __cplusplus Index: libvirt-foo/tests/xml2sexprtest.c =================================================================== --- libvirt-foo.orig/tests/xml2sexprtest.c 2006-12-13 14:08:51.000000000 +0000 +++ libvirt-foo.orig/tests/xml2sexprtest.c 2006-12-13 14:08:51.000000000 +0000 @@ -24,7 +24,7 @@ static int testCompareFiles(const char * if (virtTestLoadFile(sexpr, &sexprPtr, MAX_FILE) < 0) goto fail; - if (!(gotsexpr = virDomainParseXMLDesc(xmlData, &gotname, xendConfigVersion))) + if (!(gotsexpr = virDomainParseXMLDesc(NULL, xmlData, &gotname, xendConfigVersion))) goto fail; if (getenv("DEBUG_TESTS")) { --

Add virNetworkGetBridgeName() so that we can look up what bridge a Xen guest should connect to in order to join a given network. Note, we need this so that we can lookup which bridge the guest's backend interface should be enslaved to in order for the guest to join a specific network. Signed-off-by: Mark McLoughlin <markmc@redhat.com> Index: libvirt-foo/include/libvirt/libvirt.h.in =================================================================== --- libvirt-foo.orig/include/libvirt/libvirt.h.in 2007-02-14 15:33:23.000000000 +0000 +++ libvirt-foo.orig/include/libvirt/libvirt.h.in 2007-02-14 15:33:23.000000000 +0000 @@ -526,6 +526,7 @@ int virNetworkGetUUIDString (virNetwor char *buf); char * virNetworkGetXMLDesc (virNetworkPtr network, int flags); +char * virNetworkGetBridgeName (virNetworkPtr network); #ifdef __cplusplus } Index: libvirt-foo/src/driver.h =================================================================== --- libvirt-foo.orig/src/driver.h 2007-02-14 15:33:23.000000000 +0000 +++ libvirt-foo.orig/src/driver.h 2007-02-14 15:33:23.000000000 +0000 @@ -220,6 +220,8 @@ typedef int typedef char * (*virDrvNetworkDumpXML) (virNetworkPtr network, int flags); +typedef char * + (*virDrvNetworkGetBridgeName) (virNetworkPtr network); typedef struct _virNetworkDriver virNetworkDriver; typedef virNetworkDriver *virNetworkDriverPtr; @@ -245,6 +247,7 @@ struct _virNetworkDriver { virDrvNetworkCreate networkCreate; virDrvNetworkDestroy networkDestroy; virDrvNetworkDumpXML networkDumpXML; + virDrvNetworkGetBridgeName networkGetBridgeName; }; Index: libvirt-foo/src/libvirt.c =================================================================== --- libvirt-foo.orig/src/libvirt.c 2007-02-14 15:37:08.000000000 +0000 +++ libvirt-foo.orig/src/libvirt.c 2007-02-14 15:37:08.000000000 +0000 @@ -2731,3 +2731,38 @@ virNetworkGetXMLDesc(virNetworkPtr netwo } return(ret); } + +/** + * virNetworkGetBridgeName: + * @network: a network object + * + * Return a bridge interface name to which a domain may connect + * a network interface in order to join the network. + * + * Returns a 0 terminated interface name, or NULL in case of error. + * the caller must free() the returned value. + */ +char * +virNetworkGetBridgeName(virNetworkPtr network) +{ + int i; + char *ret = NULL; + if (!VIR_IS_NETWORK(network)) { + virLibNetworkError(network, VIR_ERR_INVALID_NETWORK, __FUNCTION__); + return (NULL); + } + + for (i = 0;i < network->conn->nb_network_drivers;i++) { + if ((network->conn->networkDrivers[i] != NULL) && + (network->conn->networkDrivers[i]->networkGetBridgeName != NULL)) { + ret = network->conn->networkDrivers[i]->networkGetBridgeName(network); + if (ret) + break; + } + } + if (!ret) { + virLibConnError(network->conn, VIR_ERR_CALL_FAILED, __FUNCTION__); + return (NULL); + } + return(ret); +} Index: libvirt-foo/qemud/dispatch.c =================================================================== --- libvirt-foo.orig/qemud/dispatch.c 2007-02-14 15:46:58.000000000 +0000 +++ libvirt-foo.orig/qemud/dispatch.c 2007-02-14 15:46:58.000000000 +0000 @@ -699,6 +699,24 @@ static int qemudDispatchNetworkDumpXML(s return 0; } +static int qemudDispatchNetworkGetBridgeName(struct qemud_server *server, struct qemud_client *client, + struct qemud_packet *in, struct qemud_packet *out) { + if (in->header.dataSize != sizeof(in->data.networkGetBridgeNameRequest)) + return -1; + + int ret = qemudNetworkGetBridgeName(server, + in->data.networkDumpXMLRequest.uuid, + out->data.networkGetBridgeNameReply.ifname, QEMUD_MAX_IFNAME_LEN); + if (ret < 0) { + if (qemudDispatchFailure(server, client, out) < 0) + return -1; + } else { + out->header.type = QEMUD_PKT_NETWORK_GET_BRIDGE_NAME; + out->header.dataSize = sizeof(out->data.networkGetBridgeNameReply); + } + return 0; +} + typedef int (*clientFunc)(struct qemud_server *server, struct qemud_client *client, struct qemud_packet *in, struct qemud_packet *out); @@ -740,6 +758,7 @@ clientFunc funcsTransmitRW[QEMUD_PKT_MAX qemudDispatchNetworkStart, qemudDispatchNetworkDestroy, qemudDispatchNetworkDumpXML, + qemudDispatchNetworkGetBridgeName, }; clientFunc funcsTransmitRO[QEMUD_PKT_MAX] = { @@ -776,6 +795,7 @@ clientFunc funcsTransmitRO[QEMUD_PKT_MAX NULL, NULL, qemudDispatchNetworkDumpXML, + qemudDispatchNetworkGetBridgeName, }; /* Index: libvirt-foo/qemud/driver.c =================================================================== --- libvirt-foo.orig/qemud/driver.c 2007-02-14 15:54:56.000000000 +0000 +++ libvirt-foo.orig/qemud/driver.c 2007-02-14 15:54:56.000000000 +0000 @@ -743,6 +743,20 @@ int qemudNetworkDumpXML(struct qemud_ser return 0; } +int qemudNetworkGetBridgeName(struct qemud_server *server, const unsigned char *uuid, char *ifname, int ifnamelen) { + struct qemud_network *network = qemudFindNetworkByUUID(server, uuid); + + if (!network) { + qemudReportError(server, VIR_ERR_INVALID_NETWORK, "no network with matching id"); + return -1; + } + + strncpy(ifname, network->bridge, ifnamelen); + ifname[ifnamelen-1] = '\0'; + + return 0; +} + /* * Local variables: * indent-tabs-mode: nil Index: libvirt-foo/qemud/driver.h =================================================================== --- libvirt-foo.orig/qemud/driver.h 2007-02-14 15:46:58.000000000 +0000 +++ libvirt-foo.orig/qemud/driver.h 2007-02-14 15:46:58.000000000 +0000 @@ -113,6 +113,10 @@ int qemudNetworkDumpXML(struct qemud_ser const unsigned char *uuid, char *xml, int xmllen); +int qemudNetworkGetBridgeName(struct qemud_server *server, + const unsigned char *uuid, + char *ifname, + int ifnamelen); #endif Index: libvirt-foo/qemud/protocol.h =================================================================== --- libvirt-foo.orig/qemud/protocol.h 2007-02-14 15:46:58.000000000 +0000 +++ libvirt-foo.orig/qemud/protocol.h 2007-02-14 15:46:58.000000000 +0000 @@ -26,6 +26,7 @@ #define QEMUD_PROTOCOL_H__ #include <stdint.h> +#include <net/if.h> /* for IF_NAMESIZE */ /* List of different packet types which can be sent */ enum { @@ -62,6 +63,7 @@ enum { QEMUD_PKT_NETWORK_START, QEMUD_PKT_NETWORK_DESTROY, QEMUD_PKT_NETWORK_DUMP_XML, + QEMUD_PKT_NETWORK_GET_BRIDGE_NAME, QEMUD_PKT_MAX, } qemud_packet_type; @@ -73,6 +75,7 @@ enum { #define QEMUD_UUID_RAW_LEN 16 #define QEMUD_MAX_NAME_LEN 50 #define QEMUD_MAX_XML_LEN 4096 +#define QEMUD_MAX_IFNAME_LEN IF_NAMESIZE #define QEMUD_MAX_NUM_DOMAINS 100 #define QEMUD_MAX_NUM_NETWORKS 100 #define QEMUD_MAX_ERROR_LEN 1024 @@ -270,6 +273,12 @@ union qemud_packet_data { struct { char xml[QEMUD_MAX_XML_LEN]; } networkDumpXMLReply; + struct { + unsigned char uuid[QEMUD_UUID_RAW_LEN]; + } networkGetBridgeNameRequest; + struct { + char ifname[QEMUD_MAX_IFNAME_LEN]; + } networkGetBridgeNameReply; }; /* Each packet has header & data */ Index: libvirt-foo/src/qemu_internal.c =================================================================== --- libvirt-foo.orig/src/qemu_internal.c 2007-02-14 15:51:56.000000000 +0000 +++ libvirt-foo.orig/src/qemu_internal.c 2007-02-14 15:51:56.000000000 +0000 @@ -1077,6 +1077,22 @@ static char * qemuNetworkDumpXML(virNetw return strdup(reply.data.networkDumpXMLReply.xml); } +static char * qemuNetworkGetBridgeName(virNetworkPtr network) { + struct qemud_packet req, reply; + + req.header.type = QEMUD_PKT_NETWORK_GET_BRIDGE_NAME; + req.header.dataSize = sizeof(req.data.networkGetBridgeNameRequest); + memmove(req.data.networkGetBridgeNameRequest.uuid, network->uuid, QEMUD_UUID_RAW_LEN); + + if (qemuProcessRequest(network->conn, NULL, &req, &reply) < 0) { + return NULL; + } + + reply.data.networkGetBridgeNameReply.ifname[QEMUD_MAX_IFNAME_LEN-1] = '\0'; + + return strdup(reply.data.networkGetBridgeNameReply.ifname); +} + static virDriver qemuDriver = { VIR_DRV_QEMU, "QEMU", @@ -1134,6 +1150,7 @@ static virNetworkDriver qemuNetworkDrive qemuNetworkCreate, /* networkCreate */ qemuNetworkDestroy, /* networkDestroy */ qemuNetworkDumpXML, /* networkDumpXML */ + qemuNetworkGetBridgeName, /* networkGetBridgeName */ }; void qemuRegister(void) { @@ -1141,7 +1158,6 @@ void qemuRegister(void) { virRegisterDriver(&qemuNetworkDriver); } - /* * Local variables: * indent-tabs-mode: nil Index: libvirt-foo/src/libvirt_sym.version =================================================================== --- libvirt-foo.orig/src/libvirt_sym.version 2007-02-14 15:33:23.000000000 +0000 +++ libvirt-foo.orig/src/libvirt_sym.version 2007-02-14 15:33:23.000000000 +0000 @@ -75,6 +75,7 @@ virNetworkGetUUID; virNetworkGetUUIDString; virNetworkGetXMLDesc; + virNetworkGetBridgeName; local: *; }; --

When create Xen guests with <interface type='network'>, lookup the bridge associated with the network and configure the guest to connect to that bridge. Signed-off-by: Mark McLoughlin <markmc@redhat.com> Index: libvirt-foo/src/xml.c =================================================================== --- libvirt-foo.orig/src/xml.c 2007-02-14 16:13:36.000000000 +0000 +++ libvirt-foo.orig/src/xml.c 2007-02-14 16:13:36.000000000 +0000 @@ -842,6 +842,7 @@ virDomainParseXMLIfDesc(virConnectPtr co xmlChar *script = NULL; xmlChar *ip = NULL; int typ = 0; + int ret = -1; type = xmlGetProp(node, BAD_CAST "type"); if (type != NULL) { @@ -849,6 +850,8 @@ virDomainParseXMLIfDesc(virConnectPtr co typ = 0; else if (xmlStrEqual(type, BAD_CAST "ethernet")) typ = 1; + else if (xmlStrEqual(type, BAD_CAST "network")) + typ = 2; xmlFree(type); } cur = node->children; @@ -858,8 +861,10 @@ virDomainParseXMLIfDesc(virConnectPtr co (xmlStrEqual(cur->name, BAD_CAST "source"))) { if (typ == 0) source = xmlGetProp(cur, BAD_CAST "bridge"); - else + else if (typ == 1) source = xmlGetProp(cur, BAD_CAST "dev"); + else + source = xmlGetProp(cur, BAD_CAST "network"); } else if ((mac == NULL) && (xmlStrEqual(cur->name, BAD_CAST "mac"))) { mac = xmlGetProp(cur, BAD_CAST "address"); @@ -884,8 +889,18 @@ virDomainParseXMLIfDesc(virConnectPtr co if (source != NULL) { if (typ == 0) virBufferVSprintf(buf, "(bridge '%s')", (const char *) source); - else /* TODO does that work like that ? */ + else if (typ == 1) /* TODO does that work like that ? */ virBufferVSprintf(buf, "(dev '%s')", (const char *) source); + else { + virNetworkPtr network = virNetworkLookupByName(conn, (const char *) source); + char *bridge; + if (!network || !(bridge = virNetworkGetBridgeName(network))) { + virXMLError(conn, VIR_ERR_NO_SOURCE, (const char *) source, 0); + goto error; + } + virBufferVSprintf(buf, "(bridge '%s')", bridge); + free(bridge); + } } if (script != NULL) virBufferVSprintf(buf, "(script '%s')", script); @@ -895,6 +910,8 @@ virDomainParseXMLIfDesc(virConnectPtr co virBufferAdd(buf, "(type ioemu)", 12); virBufferAdd(buf, ")", 1); + ret = 0; + error: if (mac != NULL) xmlFree(mac); if (source != NULL) @@ -903,7 +920,7 @@ virDomainParseXMLIfDesc(virConnectPtr co xmlFree(script); if (ip != NULL) xmlFree(ip); - return (0); + return (ret); } /** --

For each network we need the following iptables rules: 1) Allow frames to be forwarded from the bridge interface to other bridge ports: -I FORWARD -m physdev --physdev-in $(iface) -j ACCEPT 2) Allow IP packets from the bridge interface to be forwarded to other network interfaces: -I FORWARD -i $(iface) -j ACCEPT 3) Masquerade any outgoing packets, with the exception of those frames which are just being forwarded across a bridge: -t nat -I POSTROUTING -m physdev ! --physdev-is-bridged -j MASQUERADE 4) Allow IP packets be forwarded to the bridge interface from other network interfaces if they are part of an established connection: -I FORWARD -o $(iface) --match state --state ESTABLISHED,RELATED -j ACCEPT 5) Allow DHCP and DNS requests in from the bridge interface so that dnsmasq can handle them: -I INPUT -i vnet0 -p tcp --dport 67 -j ACCEPT -I INPUT -i vnet0 -p tcp --dport 53 -j ACCEPT -I INPUT -i vnet0 -p udp --dport 53 -j ACCEPT Of course, we also need to enable IP forwarding via /proc/sys/net/ipv4/ip_forward Also, for every TAP interface we enslave to a network's bridge, we must allow frames to be forwarded from that interface to other bridge ports as in (1). Note, all this has a fatal flaw - it does not attempt to integrate with any system tool which may be managing iptables rules. So, on Fedora, if you run "service iptables restart" all the rules added by qemud would be wiped out. For this reason we add two configure options --with-iptables-prefix and --with-iptables-dir so that we create our rules in a chains with the given prefix and save the rules in a file for each chain in the given directory. This would allow us to integrate in a scheme like the one proposed here: https://bugzilla.redhat.com/227011 Signed-off-by: Mark McLoughlin <markmc@redhat.com> Index: libvirt-foo/qemud/Makefile.am =================================================================== --- libvirt-foo.orig/qemud/Makefile.am 2007-02-14 15:59:34.000000000 +0000 +++ libvirt-foo.orig/qemud/Makefile.am 2007-02-14 15:59:34.000000000 +0000 @@ -8,7 +8,8 @@ libvirt_qemud_SOURCES = qemud.c internal driver.c driver.h \ dispatch.c dispatch.h \ conf.c conf.h \ - bridge.c bridge.h + bridge.c bridge.h \ + iptables.c iptables.h #-D_XOPEN_SOURCE=600 -D_XOPEN_SOURCE_EXTENDED=1 -D_POSIX_C_SOURCE=199506L libvirt_qemud_CFLAGS = \ -I$(top_srcdir)/include -I$(top_builddir)/include $(LIBXML_CFLAGS) \ Index: libvirt-foo/qemud/qemud.c =================================================================== --- libvirt-foo.orig/qemud/qemud.c 2007-02-14 16:05:37.000000000 +0000 +++ libvirt-foo.orig/qemud/qemud.c 2007-02-14 16:05:37.000000000 +0000 @@ -50,6 +50,7 @@ #include "dispatch.h" #include "driver.h" #include "conf.h" +#include "iptables.h" static void reapchild(int sig ATTRIBUTE_UNUSED) { /* We explicitly waitpid the child later */ @@ -659,11 +660,10 @@ static int qemudVMData(struct qemud_serv } static void -qemudNetworkIfaceDisconnect(struct qemud_server *server ATTRIBUTE_UNUSED, +qemudNetworkIfaceDisconnect(struct qemud_server *server, struct qemud_vm *vm ATTRIBUTE_UNUSED, struct qemud_vm_net_def *net) { - /* FIXME: will be needed to remove iptables rules */ - net = NULL; + iptablesRemovePhysdevForward(server->iptables, net->dst.network.tapifname); } int qemudShutdownVMDaemon(struct qemud_server *server, struct qemud_vm *vm) { @@ -845,6 +845,129 @@ dhcpStartDhcpDaemon(struct qemud_server return ret; } +static int +qemudAddIptablesRules(struct qemud_server *server, + struct qemud_network *network) { + int err; + + if (!server->iptables && !(server->iptables = iptablesContextNew())) { + qemudReportError(server, VIR_ERR_NO_MEMORY, "iptables support"); + return 1; + } + + /* allow bridging from the bridge interface itself */ + if ((err = iptablesAddPhysdevForward(server->iptables, network->bridge))) { + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, + "failed to add iptables rule to allow bridging from '%s' : %s\n", + network->bridge, strerror(err)); + goto err1; + } + + /* allow forwarding packets from the bridge interface */ + if ((err = iptablesAddInterfaceForward(server->iptables, network->bridge))) { + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, + "failed to add iptables rule to allow forwarding from '%s' : %s\n", + network->bridge, strerror(err)); + goto err2; + } + + /* allow forwarding packets to the bridge interface if they are part of an existing connection */ + if ((err = iptablesAddStateForward(server->iptables, network->bridge))) { + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, + "failed to add iptables rule to allow forwarding to '%s' : %s\n", + network->bridge, strerror(err)); + goto err3; + } + + /* enable masquerading */ + if ((err = iptablesAddNonBridgedMasq(server->iptables))) { + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, + "failed to add iptables rule to enable masquerading : %s\n", + strerror(err)); + goto err4; + } + + /* allow DHCP requests through to dnsmasq */ + if ((err = iptablesAddTcpInput(server->iptables, network->bridge, 67))) { + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, + "failed to add iptables rule to allow DHCP requests from '%s' : %s\n", + network->bridge, strerror(err)); + goto err5; + } + + if ((err = iptablesAddUdpInput(server->iptables, network->bridge, 67))) { + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, + "failed to add iptables rule to allow DHCP requests from '%s' : %s\n", + network->bridge, strerror(err)); + goto err6; + } + + /* allow DNS requests through to dnsmasq */ + if ((err = iptablesAddTcpInput(server->iptables, network->bridge, 53))) { + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, + "failed to add iptables rule to allow DNS requests from '%s' : %s\n", + network->bridge, strerror(err)); + goto err7; + } + + if ((err = iptablesAddUdpInput(server->iptables, network->bridge, 53))) { + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, + "failed to add iptables rule to allow DNS requests from '%s' : %s\n", + network->bridge, strerror(err)); + goto err8; + } + + return 1; + + err8: + iptablesRemoveTcpInput(server->iptables, network->bridge, 53); + err7: + iptablesRemoveUdpInput(server->iptables, network->bridge, 67); + err6: + iptablesRemoveTcpInput(server->iptables, network->bridge, 67); + err5: + iptablesRemoveNonBridgedMasq(server->iptables); + err4: + iptablesRemoveStateForward(server->iptables, network->bridge); + err3: + iptablesRemoveInterfaceForward(server->iptables, network->bridge); + err2: + iptablesRemovePhysdevForward(server->iptables, network->bridge); + err1: + return 0; +} + +static void +qemudRemoveIptablesRules(struct qemud_server *server, + struct qemud_network *network) { + iptablesRemoveUdpInput(server->iptables, network->bridge, 53); + iptablesRemoveTcpInput(server->iptables, network->bridge, 53); + iptablesRemoveUdpInput(server->iptables, network->bridge, 67); + iptablesRemoveTcpInput(server->iptables, network->bridge, 67); + iptablesRemoveNonBridgedMasq(server->iptables); + iptablesRemoveStateForward(server->iptables, network->bridge); + iptablesRemoveInterfaceForward(server->iptables, network->bridge); + iptablesRemovePhysdevForward(server->iptables, network->bridge); +} + +static int +qemudEnableIpForwarding(void) +{ +#define PROC_IP_FORWARD "/proc/sys/net/ipv4/ip_forward" + + int fd; + + if ((fd = open(PROC_IP_FORWARD, O_WRONLY|O_TRUNC)) == -1 || + write(fd, "1\n", 2) < 0) + return 0; + + close (fd); + + return 1; + +#undef PROC_IP_FORWARD +} + int qemudStartNetworkDaemon(struct qemud_server *server, struct qemud_network *network) { const char *name; @@ -899,14 +1022,26 @@ int qemudStartNetworkDaemon(struct qemud goto err_delbr; } + if (!qemudAddIptablesRules(server, network)) + goto err_delbr1; + + if (!qemudEnableIpForwarding()) { + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, + "failed to enable IP forwarding : %s\n", strerror(err)); + goto err_delbr2; + } + if (network->def.ranges && dhcpStartDhcpDaemon(server, network) < 0) - goto err_delbr1; + goto err_delbr2; network->active = 1; return 0; + err_delbr2: + qemudRemoveIptablesRules(server, network); + err_delbr1: if (network->def.ipAddress[0] && (err = brSetInterfaceUp(server->brctl, network->bridge, 0))) { @@ -935,6 +1070,8 @@ int qemudShutdownNetworkDaemon(struct qe if (network->dnsmasqPid > 0) kill(network->dnsmasqPid, SIGTERM); + qemudRemoveIptablesRules(server, network); + if (network->def.ipAddress[0] && (err = brSetInterfaceUp(server->brctl, network->bridge, 0))) { printf("Damn! Failed to bring down bridge '%s' : %s\n", @@ -1182,6 +1319,8 @@ static void qemudCleanup(struct qemud_se } if (server->brctl) brShutdown(server->brctl); + if (server->iptables) + iptablesContextFree(server->iptables); free(server); } Index: libvirt-foo/qemud/conf.c =================================================================== --- libvirt-foo.orig/qemud/conf.c 2007-02-14 16:05:37.000000000 +0000 +++ libvirt-foo.orig/qemud/conf.c 2007-02-14 16:05:37.000000000 +0000 @@ -43,6 +43,7 @@ #include "internal.h" #include "conf.h" #include "driver.h" +#include "iptables.h" static int qemudParseUUID(const char *uuid, unsigned char *rawuuid) { @@ -860,6 +861,13 @@ qemudNetworkIfaceConnect(struct qemud_se goto error; } + if ((err = iptablesAddPhysdevForward(server->iptables, net->dst.network.tapifname))) { + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, + "Failed to add iptables rule to allow bridging from '%s' :%s", + net->dst.network.tapifname, strerror(err)); + goto error; + } + snprintf(tapfdstr, sizeof(tapfdstr), "tap,fd=%d,script=", tapfd); if (!(retval = strdup(tapfdstr))) @@ -875,6 +883,7 @@ qemudNetworkIfaceConnect(struct qemud_se return retval; no_memory: + iptablesRemovePhysdevForward(server->iptables, net->dst.network.tapifname); qemudReportError(server, VIR_ERR_NO_MEMORY, "tapfds"); error: if (retval) Index: libvirt-foo/qemud/iptables.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ /dev/null 1970-01-01 00:00:00.000000000 +0000 @@ -0,0 +1,699 @@ +/* + * Copyright (C) 2007 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Mark McLoughlin <markmc@redhat.com> + */ + +#include <config.h> + +#include "iptables.h" + +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <errno.h> +#include <limits.h> +#include <unistd.h> +#include <fcntl.h> +#include <paths.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/wait.h> + +enum { + ADD = 0, + REMOVE +}; + +enum { + WITH_ERRORS = 0, + NO_ERRORS +}; + +typedef struct +{ + char *table; + char *chain; + +#ifdef IPTABLES_DIR + + char dir[PATH_MAX]; + char path[PATH_MAX]; + + int nrules; + char **rules; + +#endif /* IPTABLES_DIR */ + +} iptRules; + +struct _iptablesContext +{ + iptRules *input_filter; + iptRules *forward_filter; + iptRules *nat_postrouting; +}; + +#ifdef IPTABLES_DIR +static int +writeRules(const char *path, + char * const *rules, + int nrules) +{ + char tmp[PATH_MAX]; + FILE *f; + int istmp; + int i; + + if (nrules == 0 && unlink(path) == 0) + return 0; + + if (snprintf(tmp, PATH_MAX, "%s.new", path) >= PATH_MAX) + return EINVAL; + + istmp = 1; + + if (!(f = fopen(tmp, "w"))) { + istmp = 0; + if (!(f = fopen(path, "w"))) + return errno; + } + + for (i = 0; i < nrules; i++) { + if (fputs(rules[i], f) == EOF || + fputc('\n', f) == EOF) { + fclose(f); + if (istmp) + unlink(tmp); + return errno; + } + } + + fclose(f); + + if (istmp && rename(tmp, path) < 0) { + unlink(tmp); + return errno; + } + + if (istmp) + unlink(tmp); + + return 0; +} + +static int +ensureDir(const char *path) +{ + struct stat st; + char parent[PATH_MAX]; + char *p; + int err; + + if (stat(path, &st) >= 0) + return 0; + + strncpy(parent, path, PATH_MAX); + parent[PATH_MAX - 1] = '\0'; + + if (!(p = strrchr(parent, '/'))) + return EINVAL; + + if (p == parent) + return EPERM; + + *p = '\0'; + + if ((err = ensureDir(parent))) + return err; + + if (mkdir(path, 0700) < 0 && errno != EEXIST) + return errno; + + return 0; +} + +static int +buildDir(const char *table, + char *path, + int maxlen) +{ + if (snprintf(path, maxlen, IPTABLES_DIR "/%s", table) >= maxlen) + return EINVAL; + else + return 0; +} + +static int +buildPath(const char *table, + const char *chain, + char *path, + int maxlen) +{ + if (snprintf(path, maxlen, IPTABLES_DIR "/%s/%s.chain", table, chain) >= maxlen) + return EINVAL; + else + return 0; +} + +static int +iptRulesAppend(iptRules *rules, + const char *rule) +{ + char **r; + int err; + + if (!(r = (char **)realloc(rules->rules, sizeof(char *) * (rules->nrules+1)))) + return ENOMEM; + + rules->rules = r; + + if (!(rules->rules[rules->nrules] = strdup(rule))) + return ENOMEM; + + rules->nrules++; + + if ((err = ensureDir(rules->dir))) + return err; + + if ((err = writeRules(rules->path, rules->rules, rules->nrules))) + return err; + + return 0; +} + +static int +iptRulesRemove(iptRules *rules, + const char *rule) +{ + int i; + int err; + + for (i = 0; i < rules->nrules; i++) + if (!strcmp(rules->rules[i], rule)) + break; + + if (i >= rules->nrules) + return EINVAL; + + free(rules->rules[i]); + + memmove(&rules->rules[i], + &rules->rules[i+1], + (rules->nrules - i - 1) * sizeof (char *)); + + rules->nrules--; + + if ((err = writeRules(rules->path, rules->rules, rules->nrules))) + return err; + + return 0; +} +#endif /* IPTABLES_DIR */ + +static void +iptRulesFree(iptRules *rules) +{ + if (rules->table) { + free(rules->chain); + rules->chain = NULL; + } + + if (rules->chain) { + free(rules->chain); + rules->chain = NULL; + } + +#ifdef IPTABLES_DIR + { + int i; + + rules->dir[0] = '\0'; + rules->path[0] = '\0'; + + for (i = 0; i < rules->nrules; i++) { + free(rules->rules[i]); + rules->rules[i] = NULL; + } + + rules->nrules = 0; + + if (rules->rules) { + free(rules->rules); + rules->rules = NULL; + } + } +#endif /* IPTABLES_DIR */ + + free(rules); +} + +static iptRules * +iptRulesNew(const char *table, + const char *chain) +{ + iptRules *rules; + + if (!(rules = (iptRules *)malloc(sizeof (iptRules)))) + return NULL; + + memset (rules, 0, sizeof (iptRules)); + + if (!(rules->table = strdup(table))) + goto error; + + if (!(rules->chain = strdup(chain))) + goto error; + +#ifdef IPTABLES_DIR + if (buildDir(table, rules->dir, sizeof(rules->dir))) + goto error; + + if (buildPath(table, chain, rules->path, sizeof(rules->path))) + goto error; + + rules->rules = NULL; + rules->nrules = 0; +#endif /* IPTABLES_DIR */ + + return rules; + + error: + iptRulesFree(rules); + return NULL; +} + +static int +iptablesSpawn(int errors, char * const *argv) +{ + pid_t pid, ret; + int status; + int null = -1; + + if (errors == NO_ERRORS && (null = open(_PATH_DEVNULL, O_RDONLY)) < 0) + return errno; + + pid = fork(); + if (pid == -1) { + if (errors == NO_ERRORS) + close(null); + return errno; + } + + if (pid == 0) { /* child */ + int i, open_max = sysconf(_SC_OPEN_MAX); + + for (i = 0; i < open_max; i++) { + if (i != STDOUT_FILENO && + i != STDERR_FILENO && + i != STDIN_FILENO) + close(i); + else if (errors == NO_ERRORS) + dup2(null, i); + } + + execvp(argv[0], argv); + + _exit (1); + } + + if (errors == NO_ERRORS) + close(null); + + while ((ret = waitpid(pid, &status, 0) == -1) && errno == EINTR); + if (ret == -1) + return errno; + + if (errors == NO_ERRORS) + return 0; + else + return (WIFEXITED(status) && WEXITSTATUS(status) == 0) ? 0 : EINVAL; +} + +static int +iptablesAddRemoveChain(iptRules *rules, int action) +{ + char **argv; + int retval = ENOMEM; + int n; + + n = 1 + /* /sbin/iptables */ + 2 + /* --table foo */ + 2; /* --new-chain bar */ + + if (!(argv = (char **)malloc(sizeof(char *) * (n+1)))) + goto error; + + memset(argv, 0, sizeof(char *) * (n + 1)); + + n = 0; + + if (!(argv[n++] = strdup(IPTABLES_PATH))) + goto error; + + if (!(argv[n++] = strdup("--table"))) + goto error; + + if (!(argv[n++] = strdup(rules->table))) + goto error; + + if (!(argv[n++] = strdup(action == ADD ? "--new-chain" : "--delete-chain"))) + goto error; + + if (!(argv[n++] = strdup(rules->chain))) + goto error; + + retval = iptablesSpawn(NO_ERRORS, argv); + + error: + if (argv) { + n = 0; + while (argv[n]) + free(argv[n++]); + free(argv); + } + + return retval; +} + +static int +iptablesAddRemoveRule(iptRules *rules, int action, const char *arg, ...) +{ + va_list args; + int retval = ENOMEM; + char **argv; + char *rule = NULL, *p; + const char *s; + int n, rulelen; + + n = 1 + /* /sbin/iptables */ + 2 + /* --table foo */ + 2 + /* --insert bar */ + 1; /* arg */ + + rulelen = strlen(arg) + 1; + + va_start(args, arg); + while ((s = va_arg(args, const char *))) { + n++; + rulelen += strlen(s) + 1; + } + + va_end(args); + + if (!(argv = (char **)malloc(sizeof(char *) * (n + 1)))) + goto error; + + if (!(rule = (char *)malloc(rulelen))) + goto error; + + memset(argv, 0, sizeof(char *) * (n + 1)); + + n = 0; + + if (!(argv[n++] = strdup(IPTABLES_PATH))) + goto error; + + if (!(argv[n++] = strdup("--table"))) + goto error; + + if (!(argv[n++] = strdup(rules->table))) + goto error; + + if (!(argv[n++] = strdup(action == ADD ? "--insert" : "--delete"))) + goto error; + + if (!(argv[n++] = strdup(rules->chain))) + goto error; + + if (!(argv[n++] = strdup(arg))) + goto error; + + p = strcpy(rule, arg); + p += strlen(arg); + + va_start(args, arg); + + while ((s = va_arg(args, const char *))) { + if (!(argv[n++] = strdup(s))) + goto error; + + *(p++) = ' '; + strcpy(p, s); + p += strlen(s); + } + + va_end(args); + + *p = '\0'; + + if (action == ADD && + (retval = iptablesAddRemoveChain(rules, action))) + goto error; + + if ((retval = iptablesSpawn(WITH_ERRORS, argv))) + goto error; + + if (action == REMOVE && + (retval = iptablesAddRemoveChain(rules, action))) + goto error; + +#ifdef IPTABLES_DIR + if (action == ADD) + retval = iptRulesAppend(rules, rule); + else + retval = iptRulesRemove(rules, rule); +#endif /* IPTABLES_DIR */ + + error: + if (rule) + free(rule); + + if (argv) { + n = 0; + while (argv[n]) + free(argv[n++]); + free(argv); + } + + return retval; +} + +iptablesContext * +iptablesContextNew(void) +{ + iptablesContext *ctx; + + if (!(ctx = (iptablesContext *) malloc(sizeof (iptablesContext)))) + return NULL; + + if (!(ctx->input_filter = iptRulesNew("filter", IPTABLES_PREFIX "INPUT"))) + goto error; + + if (!(ctx->forward_filter = iptRulesNew("filter", IPTABLES_PREFIX "FORWARD"))) + goto error; + + if (!(ctx->nat_postrouting = iptRulesNew("nat", IPTABLES_PREFIX "POSTROUTING"))) + goto error; + + return ctx; + + error: + iptablesContextFree(ctx); + return NULL; +} + +void +iptablesContextFree(iptablesContext *ctx) +{ + iptRulesFree(ctx->input_filter); + iptRulesFree(ctx->forward_filter); + iptRulesFree(ctx->nat_postrouting); + free(ctx); +} + +static int +iptablesInput(iptablesContext *ctx, + const char *iface, + int port, + int action, + int tcp) +{ + char portstr[32]; + int ret; + + snprintf(portstr, sizeof(portstr), "%d", port); + portstr[sizeof(portstr) - 1] = '\0'; + + ret = iptablesAddRemoveRule(ctx->input_filter, + action, + "--in-interface", iface, + "--protocol", tcp ? "tcp" : "udp", + "--destination-port", portstr, + "--jump", "ACCEPT", + NULL); + + return ret; +} + +int +iptablesAddTcpInput(iptablesContext *ctx, + const char *iface, + int port) +{ + return iptablesInput(ctx, iface, port, ADD, 1); +} + +int +iptablesRemoveTcpInput(iptablesContext *ctx, + const char *iface, + int port) +{ + return iptablesInput(ctx, iface, port, REMOVE, 1); +} + +int +iptablesAddUdpInput(iptablesContext *ctx, + const char *iface, + int port) +{ + return iptablesInput(ctx, iface, port, ADD, 0); +} + +int +iptablesRemoveUdpInput(iptablesContext *ctx, + const char *iface, + int port) +{ + return iptablesInput(ctx, iface, port, REMOVE, 0); +} + +static int +iptablesPhysdevForward(iptablesContext *ctx, + const char *iface, + int action) +{ + return iptablesAddRemoveRule(ctx->forward_filter, + action, + "--match", "physdev", + "--physdev-in", iface, + "--jump", "ACCEPT", + NULL); +} + +int +iptablesAddPhysdevForward(iptablesContext *ctx, + const char *iface) +{ + return iptablesPhysdevForward(ctx, iface, ADD); +} + +int +iptablesRemovePhysdevForward(iptablesContext *ctx, + const char *iface) +{ + return iptablesPhysdevForward(ctx, iface, REMOVE); +} + +static int +iptablesInterfaceForward(iptablesContext *ctx, + const char *iface, + int action) +{ + return iptablesAddRemoveRule(ctx->forward_filter, + action, + "--in-interface", iface, + "--jump", "ACCEPT", + NULL); +} + +int +iptablesAddInterfaceForward(iptablesContext *ctx, + const char *iface) +{ + return iptablesInterfaceForward(ctx, iface, ADD); +} + +int +iptablesRemoveInterfaceForward(iptablesContext *ctx, + const char *iface) +{ + return iptablesInterfaceForward(ctx, iface, REMOVE); +} + +static int +iptablesStateForward(iptablesContext *ctx, + const char *iface, + int action) +{ + return iptablesAddRemoveRule(ctx->forward_filter, + action, + "--out-interface", iface, + "--match", "state", + "--state", "ESTABLISHED,RELATED", + "--jump", "ACCEPT", + NULL); +} + +int +iptablesAddStateForward(iptablesContext *ctx, + const char *iface) +{ + return iptablesStateForward(ctx, iface, ADD); +} + +int +iptablesRemoveStateForward(iptablesContext *ctx, + const char *iface) +{ + return iptablesStateForward(ctx, iface, REMOVE); +} + +static int +iptablesNonBridgedMasq(iptablesContext *ctx, + int action) +{ + return iptablesAddRemoveRule(ctx->nat_postrouting, + action, + "--match", "physdev", + "!", "--physdev-is-bridged", + "--jump", "MASQUERADE", + NULL); +} + +int +iptablesAddNonBridgedMasq(iptablesContext *ctx) +{ + return iptablesNonBridgedMasq(ctx, ADD); +} + +int +iptablesRemoveNonBridgedMasq(iptablesContext *ctx) +{ + return iptablesNonBridgedMasq(ctx, REMOVE); +} + +/* + * Local variables: + * indent-tabs-mode: nil + * c-indent-level: 4 + * c-basic-offset: 4 + * tab-width: 4 + * End: + */ Index: libvirt-foo/qemud/iptables.h =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ /dev/null 1970-01-01 00:00:00.000000000 +0000 @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2007 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Mark McLoughlin <markmc@redhat.com> + */ + +#ifndef __QEMUD_IPTABLES_H__ +#define __QEMUD_IPTABLES_H__ + +typedef struct _iptablesContext iptablesContext; + +iptablesContext *iptablesContextNew (void); +void iptablesContextFree (iptablesContext *ctx); + +int iptablesAddTcpInput (iptablesContext *ctx, + const char *iface, + int port); +int iptablesRemoveTcpInput (iptablesContext *ctx, + const char *iface, + int port); + +int iptablesAddUdpInput (iptablesContext *ctx, + const char *iface, + int port); +int iptablesRemoveUdpInput (iptablesContext *ctx, + const char *iface, + int port); + +int iptablesAddPhysdevForward (iptablesContext *ctx, + const char *iface); +int iptablesRemovePhysdevForward (iptablesContext *ctx, + const char *iface); + +int iptablesAddInterfaceForward (iptablesContext *ctx, + const char *iface); +int iptablesRemoveInterfaceForward (iptablesContext *ctx, + const char *iface); + +int iptablesAddStateForward (iptablesContext *ctx, + const char *iface); +int iptablesRemoveStateForward (iptablesContext *ctx, + const char *iface); + +int iptablesAddNonBridgedMasq (iptablesContext *ctx); +int iptablesRemoveNonBridgedMasq (iptablesContext *ctx); + +#endif /* __QEMUD_IPTABLES_H__ */ + +/* + * Local variables: + * indent-tabs-mode: nil + * c-indent-level: 4 + * c-basic-offset: 4 + * tab-width: 4 + * End: + */ Index: libvirt-foo/configure.in =================================================================== --- libvirt-foo.orig/configure.in 2007-02-14 15:59:02.000000000 +0000 +++ libvirt-foo.orig/configure.in 2007-02-14 15:59:02.000000000 +0000 @@ -79,6 +79,31 @@ dnl fi dnl +dnl allow the creation of iptables rules in chains with a +dnl specific prefix rather than in the standard toplevel chains +dnl +AC_ARG_WITH(iptables-prefix, + AC_HELP_STRING([--with-iptables-prefix=prefix], + [prefix used for iptables chains, default is to use standard toplevel chains]), + [IPTABLES_PREFIX=$withval]) +AC_DEFINE_UNQUOTED(IPTABLES_PREFIX, "$IPTABLES_PREFIX", [prefix used for iptables chains]) + +dnl +dnl also support saving the various chains to files +dnl in e.g. /etc/sysconfig/iptables.d +dnl +AC_ARG_WITH(iptables-dir, + AC_HELP_STRING([--with-iptables-dir=path], + [directory used to save iptables chains, defaults to not saving]), + [IPTABLES_DIR=$withval]) +if test x"$IPTABLES_DIR" != "x"; then + AC_DEFINE_UNQUOTED(IPTABLES_DIR, "$IPTABLES_DIR", [directory used for saving iptables chains]) +fi + +AC_PATH_PROG(IPTABLES_PATH, iptables, /sbin/iptables) +AC_DEFINE_UNQUOTED(IPTABLES_PATH, "$IPTABLES_PATH", [path to iptables binary]) + +dnl dnl Specify the xen-distribution directory to be able to compile on a dnl non-xenified host dnl Index: libvirt-foo/qemud/internal.h =================================================================== --- libvirt-foo.orig/qemud/internal.h 2007-02-14 16:05:37.000000000 +0000 +++ libvirt-foo.orig/qemud/internal.h 2007-02-14 16:05:37.000000000 +0000 @@ -31,6 +31,7 @@ #include "protocol.h" #include "bridge.h" +#include "iptables.h" #ifdef __GNUC__ #ifdef HAVE_ANSIDECL_H @@ -283,6 +284,7 @@ struct qemud_server { int ninactivenetworks; struct qemud_network *inactivenetworks; brControl *brctl; + iptablesContext *iptables; char configDir[PATH_MAX]; char networkConfigDir[PATH_MAX]; char errorMessage[QEMUD_MAX_ERROR_LEN]; --
participants (1)
-
markmc@redhat.com