diff --git a/src/openvz_conf.c b/src/openvz_conf.c index aa8493a..7746787 100644 --- a/src/openvz_conf.c +++ b/src/openvz_conf.c @@ -52,6 +52,7 @@ static char *openvzLocateConfDir(void); static int openvzGetVPSUUID(int vpsid, char *uuidstr); static int openvzLocateConfFile(int vpsid, char *conffile, int maxlen); static int openvzAssignUUIDs(void); +static char *openvzGetDefinedBridge(int veid, const char *ifname); void openvzError (virConnectPtr conn, virErrorNumber code, const char *fmt, ...) @@ -241,6 +242,9 @@ openvzReadNetworkConf(virConnectPtr conn, int veid) { while (*next != '\0' && *next != ',') next++; if (STRPREFIX(p, "ifname=")) { p += 7; + /* skip in libvirt */ + } else if (STRPREFIX(p, "host_ifname=")) { + p += 12; len = next - p; if (len > 16) { openvzError(conn, VIR_ERR_INTERNAL_ERROR, @@ -248,16 +252,16 @@ openvzReadNetworkConf(virConnectPtr conn, int veid) { goto error; } - if (VIR_ALLOC_N(net->data.bridge.brname, len+1) < 0) + if (VIR_ALLOC_N(net->ifname, len+1) < 0) goto no_memory; - strncpy(net->data.bridge.brname, p, len); - net->data.bridge.brname[len] = '\0'; - } else if (STRPREFIX(p, "host_ifname=")) { - p += 12; - //skip in libvirt + strncpy(net->ifname, p, len); + net->ifname[len] = '\0'; } else if (STRPREFIX(p, "mac=")) { p += 4; + /* skip in libvirt */ + } else if (STRPREFIX(p, "host_mac=")) { + p += 9; len = next - p; if (len != 17) { //should be 17 openvzError(conn, VIR_ERR_INTERNAL_ERROR, @@ -266,18 +270,23 @@ openvzReadNetworkConf(virConnectPtr conn, int veid) { } strncpy(cpy_temp, p, len); cpy_temp[len] = '\0'; - if (openvzParseMac(cpy_temp, net->mac)<0) { + if (openvzParseMac(cpy_temp, net->mac) < 0) { openvzError(conn, VIR_ERR_INTERNAL_ERROR, _("Wrong MAC address")); goto error; } - } else if (STRPREFIX(p, "host_mac=")) { - p += 9; - //skip in libvirt } p = ++next; } while (p < token + strlen(token)); + /* set bridge device */ + net->data.bridge.brname = openvzGetDefinedBridge(veid, net->ifname); + if (net->data.bridge.brname == NULL) { + openvzError(conn, VIR_ERR_INTERNAL_ERROR, "%s %s", + _("Cannot find bridge for interface"), net->ifname); + goto error; + } + token = strtok_r(NULL, ";", &saveptr); } } @@ -638,3 +647,82 @@ static int openvzAssignUUIDs(void) return 0; } +int +openvzSetDefinedBridge(int veid, const char *brname, const char *ifname) +{ + char conf_file[PATH_MAX]; + int fd, ret; + char line[1024]; + char iden[1024]; + char pattern[1024]; + char bridge_buf[1024]; + FILE *fp; + + /* locate and open an OpenVZ config file */ + if (openvzLocateConfFile(veid, conf_file, PATH_MAX) < 0) + return -1; + if ( (fd = open(conf_file, O_RDONLY)) == -1) + return -1; + + /* locate a line, starting with "^#BRIDGE(ifname): " */ + snprintf(pattern, sizeof(pattern), "#BRIDGE(%s):", ifname); + for ( ; ; ) { + if ( (ret = openvz_readline(fd, line, sizeof(line))) == -1) + return -1; + + if (ret == 0) { /* EoF, line was not found */ + break; + } + + sscanf(line, "%s %s\n", iden, bridge_buf); + if (STREQ(iden, pattern)) + return 0; /* found a string */ + } + close(fd); + + if ( (fp = fopen(conf_file, "a")) == NULL) + return -1; + + /* Record failure if fprintf or fclose fails, + and be careful always to close the stream. */ + if ((fprintf(fp, "\n#BRIDGE(%s): %s\n", ifname, brname) <= 0) + + (fclose(fp) == EOF)) + return -1; +} + +static char * +openvzGetDefinedBridge(int veid, const char *ifname) +{ + char conf_file[PATH_MAX]; + int fd, ret; + char line[1024]; + char iden[1024]; + char pattern[1024]; + char bridge_buf[1024]; + char *rc = NULL; + + /* locate and open an OpenVZ config file */ + if (openvzLocateConfFile(veid, conf_file, PATH_MAX) < 0) + return NULL; + if ( (fd = open(conf_file, O_RDONLY)) == -1) + return NULL; + + /* locate a line, starting with "^#BRIDGE(ifname): " */ + snprintf(pattern, sizeof(pattern), "#BRIDGE(%s):", ifname); + for ( ; ; ) { + if ( (ret = openvz_readline(fd, line, sizeof(line))) == -1) + break; + + if (ret == 0) /* EoF, line was not found */ + break; + + sscanf(line, "%s %s\n", iden, bridge_buf); + if (STREQ(iden, pattern)) { + rc = strdup(bridge_buf); + break; + } + } + + close(fd); + return rc; +} diff --git a/src/openvz_conf.h b/src/openvz_conf.h index 68c00ea..4fc6094 100644 --- a/src/openvz_conf.h +++ b/src/openvz_conf.h @@ -61,5 +61,6 @@ int strtoI(const char *str); int openvzCheckEmptyMac(const unsigned char *mac); char *openvzMacToString(const unsigned char *mac); int openvzSetDefinedUUID(int vpsid, unsigned char *uuid); +int openvzSetDefinedBridge(int veid, const char *brname, const char *ifname); #endif /* OPENVZ_CONF_H */ diff --git a/src/openvz_driver.c b/src/openvz_driver.c index f68841e..26902c4 100644 --- a/src/openvz_driver.c +++ b/src/openvz_driver.c @@ -55,6 +55,7 @@ #include "openvz_conf.h" #include "nodeinfo.h" #include "memory.h" +#include "bridge.h" #define OPENVZ_MAX_ARG 28 #define CMDBUF_LEN 1488 @@ -323,6 +324,33 @@ static int openvzDomainReboot(virDomainPtr dom, return 0; } +static char * +openvzGenerateMac(void) +{ + char mac[6] = { + 0x52, + 0x54, + 0x00, + 1 + (int)(256*(rand()/(RAND_MAX+1.0))), + 1 + (int)(256*(rand()/(RAND_MAX+1.0))), + 1 + (int)(256*(rand()/(RAND_MAX+1.0))) + }; + return openvzMacToString(mac); +} + +static char * +openvzGenerateVethName(int veid, char *dev_name_ve) +{ + char dev_name[32]; + int ifNo = 0; + + if (sscanf(dev_name_ve, "%*[^0-9]%d", &ifNo) != 1) + return NULL; + if (snprintf(dev_name, sizeof(dev_name), "veth%d.%d", veid, ifNo) < 7) + return NULL; + return strdup(dev_name); +} + static int openvzDomainSetNetwork(virConnectPtr conn, const char *vpsid, virDomainNetDefPtr net) @@ -364,18 +392,61 @@ openvzDomainSetNetwork(virConnectPtr conn, const char *vpsid, if (openvzCheckEmptyMac(net->mac) > 0) mac = openvzMacToString(net->mac); - if (net->type == VIR_DOMAIN_NET_TYPE_BRIDGE && - net->data.bridge.brname != NULL) { + if (net->type == VIR_DOMAIN_NET_TYPE_BRIDGE) { + static int vnetNo = 0; char opt[1024]; + char *mac_ve; + char dev_name_ve[20]; + int veid = strtoI(vpsid); + //--netif_add ifname[,mac,host_ifname,host_mac] ADD_ARG_LIT("--netif_add") ; - strncpy(opt, net->data.bridge.brname, 256); + + /* generate interface name in ve and copy it to options */ + snprintf(dev_name_ve, sizeof(dev_name_ve), "eth%d", vnetNo++); + strncpy(opt, dev_name_ve, sizeof(opt)); + + /* generate mac address in ve and copy it to options */ + mac_ve = openvzGenerateMac(); + if (!mac_ve) { + openvzError(conn, VIR_ERR_INTERNAL_ERROR, + _("Could not generate mac address for VE")); + rc = -1; + goto exit; + } + strncat(opt, ",", sizeof(opt)-strlen(opt)-1); + strncat(opt, mac_ve, sizeof(opt)-strlen(opt)-1); + VIR_FREE(mac_ve); + + /* if user doesn't specified host interface name, + * than we need to generate it */ + if (net->ifname == NULL) { + net->ifname = openvzGenerateVethName(veid, dev_name_ve); + if (net->ifname == NULL) { + openvzError(conn, VIR_ERR_INTERNAL_ERROR, + _("Could not generate veth name")); + rc = -1; + goto exit; + } + } + + /* we also need to save the bridge name, corresponding to this host + * interface, in openvz config */ + openvzSetDefinedBridge(veid, net->data.bridge.brname, net->ifname); + + /* and now append it to command line */ + strncat(opt, ",", sizeof(opt)-strlen(opt)-1); + strncat(opt, net->ifname, sizeof(opt)-strlen(opt)-1); + + /* if user specified mac address of host interface, append it too, + * otherwise, it will be generated automatically */ if (mac != NULL) { - strcat(opt, ","); - strcat(opt, mac); + strncat(opt, ",", sizeof(opt)-strlen(opt)-1); + strncat(opt, mac, sizeof(opt)-strlen(opt)-1); } + ADD_ARG_LIT(opt) ; - }else if (net->type == VIR_DOMAIN_NET_TYPE_ETHERNET && + } else if (net->type == VIR_DOMAIN_NET_TYPE_ETHERNET && net->data.ethernet.ipaddr != NULL) { //--ipadd ip ADD_ARG_LIT("--ipadd") ; @@ -490,6 +561,40 @@ openvzDomainDefineXML(virConnectPtr conn, const char *xml) return dom; } +static int +openvzSetBridges(virConnectPtr conn, const char *vpsid, + virDomainNetDefPtr net) +{ + int rc; + char *veth; + char *bridge; + brControl *brctl = NULL; + + if (brInit(&brctl) != 0) + return -1; + + for ( ; net; net=net->next) { + switch (net->type) { + case VIR_DOMAIN_NET_TYPE_BRIDGE: + veth = net->ifname; + bridge = net->data.bridge.brname; + if (rc = brAddInterface(brctl, bridge, veth)) { + openvzError(conn, VIR_ERR_INTERNAL_ERROR, + _("failed to add %s device to %s: %s"), + veth, bridge, strerror(rc)); + goto exit; + } + break; + } + } + + rc = 0; + +exit: + brShutdown(brctl); + return rc; +} + static virDomainPtr openvzDomainCreateLinux(virConnectPtr conn, const char *xml, unsigned int flags ATTRIBUTE_UNUSED) @@ -556,6 +661,12 @@ openvzDomainCreateLinux(virConnectPtr conn, const char *xml, goto exit; } + if (openvzSetBridges(conn, vmdef->name, vmdef->nets) < 0) { + openvzError(conn, VIR_ERR_INTERNAL_ERROR, + _("Could not configure bridges")); + goto exit; + } + vm->pid = strtoI(vmdef->name); vm->def->id = vm->pid; vm->state = VIR_DOMAIN_RUNNING; @@ -602,6 +713,12 @@ openvzDomainCreate(virDomainPtr dom) return -1; } + if (openvzSetBridges(dom->conn, vm->def->name, vm->def->nets) < 0) { + openvzError(dom->conn, VIR_ERR_INTERNAL_ERROR, + _("Could not configure bridges")); + return -1; + } + vm->pid = strtoI(vm->def->name); vm->def->id = vm->pid; vm->state = VIR_DOMAIN_RUNNING;