Improve support for virtual networking

This commit is contained in:
Daniel P. Berrange 2007-03-13 22:43:22 +00:00
parent 06b3f3ced3
commit a534de5281
11 changed files with 596 additions and 192 deletions

View File

@ -1,3 +1,39 @@
Tue Mar 13 18:04:14 EST 2007 Daniel P. Berrange <berrange@redhat.com>
* qemud/qemud.c: Use the --dhcp-leasefile arg to dnsmasq to
ensure multiple instances of dnsmasq can co-exist without
splaterring each other's leases.
* qemud/bridge.c: Use a single ifname arg instead of two separate
ifnameOrFmt & ifname args to simply use by callers
* qemud/conf.c: Autogenerate a MAC address if none is provided.
Added support for ethernet, bridge, mcast, client and server
networking config types in XML parser, generator and command
line args. Use the 'vlan' attribute for all QEMU nics to ensure
they don't all appear on same virtual LAN. Ensure order of
QEMU NICs matches order they are declared in the XML. Tweak
existing XML generation of 'network' type to match syntax
used by Xen for specifying tap device name. Add support for
creating isolated networks, or networks forwarding to an
explicit named physical device.
* qemud/default-network.xml: Add <forward/> to make default network
automatically forward to any active physical device.
* qemud/internal.h: Added struct fields for new types of network
config, and for tracking network forward device.
* qemud/iptables.h, qemud/iptables.c: Allow an optional target
device name to be passed in to restrict IP tables forwarding
rules.
* qemud/Makefile.am: Create $sysconfdir/lib/libvirt for DHCP
leases file
* libvirt.spec.in: Include %{_sysconfdir}/lib/libvirt for DHCP
leases file
Mon Mar 12 23:17:11 EST 2007 Daniel P. Berrange <berrange@redhat.com>
* libvirt.spec.in: Don't run UUID generation on first install,

View File

@ -113,6 +113,7 @@ fi
%config %attr(0700, root, root) %{_sysconfdir}/libvirt/qemu/networks/autostart/default.xml
%{_sysconfdir}/rc.d/init.d/libvirtd
%dir %{_localstatedir}/run/libvirt/
%dir %{_localstatedir}/lib/libvirt/
%attr(4755, root, root) %{_libexecdir}/libvirt_proxy
%attr(0755, root, root) %{_sbindir}/libvirt_qemud
%doc docs/libvirt.rng

View File

@ -28,12 +28,14 @@ install-data-local:
test -e $(DESTDIR)$(sysconfdir)/libvirt/qemu/networks/autostart/default.xml || \
ln -s ../default.xml $(DESTDIR)$(sysconfdir)/libvirt/qemu/networks/autostart/default.xml
mkdir -p $(DESTDIR)$(localstatedir)/run/libvirt
mkdir -p $(DESTDIR)$(localstatedir)/lib/libvirt
uninstall-local:
rm -f $(DESTDIR)$(sysconfdir)/libvirt/qemu/networks/autostart/default.xml
rm -f $(DESTDIR)$(sysconfdir)/libvirt/qemu/networks/default.xml
rmdir $(DESTDIR)$(sysconfdir)/libvirt/qemu/networks/autostart || :
rmdir $(DESTDIR)$(localstatedir)/run/libvirt || :
rmdir $(DESTDIR)$(localstatedir)/lib/libvirt || :
EXTRA_DIST = libvirtd.in default-network.xml

View File

@ -190,27 +190,22 @@ brDeleteInterface(brControl *ctl,
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)
if (!ctl || !ctl->fd || !bridge || !ifname || !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"))
if (strstr(ifname, "%d"))
subst = 1;
if ((fd = open("/dev/net/tun", O_RDWR)) < 0)
@ -225,19 +220,19 @@ brAddTap(brControl *ctl,
try.ifr_flags = IFF_TAP|IFF_NO_PI;
if (subst) {
len = snprintf(try.ifr_name, maxlen, ifnameOrFmt, id);
len = snprintf(try.ifr_name, maxlen, ifname, id);
if (len >= maxlen) {
errno = EADDRINUSE;
goto error;
}
} else {
len = strlen(ifnameOrFmt);
len = strlen(ifname);
if (len >= maxlen - 1) {
errno = EINVAL;
goto error;
}
strncpy(try.ifr_name, ifnameOrFmt, len);
strncpy(try.ifr_name, ifname, len);
try.ifr_name[len] = '\0';
}

View File

@ -49,7 +49,6 @@ int brDeleteInterface (brControl *ctl,
int brAddTap (brControl *ctl,
const char *bridge,
const char *ifnameOrFmt,
char *ifname,
int maxlen,
int *tapfd);

View File

@ -444,6 +444,15 @@ static struct qemud_vm_disk_def *qemudParseDiskXML(struct qemud_server *server,
return NULL;
}
static void qemudRandomMAC(struct qemud_vm_net_def *net) {
net->mac[0] = 0x52;
net->mac[1] = 0x54;
net->mac[2] = 0x00;
net->mac[3] = 1 + (int)(256*(rand()/(RAND_MAX+1.0)));
net->mac[4] = 1 + (int)(256*(rand()/(RAND_MAX+1.0)));
net->mac[5] = 1 + (int)(256*(rand()/(RAND_MAX+1.0)));
}
/* Parse the XML definition for a network interface */
static struct qemud_vm_net_def *qemudParseInterfaceXML(struct qemud_server *server,
@ -453,7 +462,11 @@ static struct qemud_vm_net_def *qemudParseInterfaceXML(struct qemud_server *serv
xmlChar *macaddr = NULL;
xmlChar *type = NULL;
xmlChar *network = NULL;
xmlChar *tapifname = NULL;
xmlChar *bridge = NULL;
xmlChar *ifname = NULL;
xmlChar *script = NULL;
xmlChar *address = NULL;
xmlChar *port = NULL;
if (!net) {
qemudReportError(server, VIR_ERR_NO_MEMORY, "net");
@ -466,8 +479,8 @@ static struct qemud_vm_net_def *qemudParseInterfaceXML(struct qemud_server *serv
if (type != NULL) {
if (xmlStrEqual(type, BAD_CAST "user"))
net->type = QEMUD_NET_USER;
else if (xmlStrEqual(type, BAD_CAST "tap"))
net->type = QEMUD_NET_TAP;
else if (xmlStrEqual(type, BAD_CAST "ethernet"))
net->type = QEMUD_NET_ETHERNET;
else if (xmlStrEqual(type, BAD_CAST "server"))
net->type = QEMUD_NET_SERVER;
else if (xmlStrEqual(type, BAD_CAST "client"))
@ -476,10 +489,8 @@ static struct qemud_vm_net_def *qemudParseInterfaceXML(struct qemud_server *serv
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;
*/
else if (xmlStrEqual(type, BAD_CAST "bridge"))
net->type = QEMUD_NET_BRIDGE;
else
net->type = QEMUD_NET_USER;
xmlFree(type);
@ -496,17 +507,32 @@ static struct qemud_vm_net_def *qemudParseInterfaceXML(struct qemud_server *serv
(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");
} else if ((network == NULL) &&
(net->type == QEMUD_NET_BRIDGE) &&
(xmlStrEqual(cur->name, BAD_CAST "source"))) {
bridge = xmlGetProp(cur, BAD_CAST "dev");
} else if ((network == NULL) &&
((net->type == QEMUD_NET_SERVER) ||
(net->type == QEMUD_NET_CLIENT) ||
(net->type == QEMUD_NET_MCAST)) &&
(xmlStrEqual(cur->name, BAD_CAST "source"))) {
address = xmlGetProp(cur, BAD_CAST "address");
port = xmlGetProp(cur, BAD_CAST "port");
} else if ((ifname == NULL) &&
((net->type == QEMUD_NET_NETWORK) ||
(net->type == QEMUD_NET_ETHERNET) ||
(net->type == QEMUD_NET_BRIDGE)) &&
xmlStrEqual(cur->name, BAD_CAST "target")) {
ifname = xmlGetProp(cur, BAD_CAST "dev");
} else if ((script == NULL) &&
(net->type == QEMUD_NET_ETHERNET) &&
xmlStrEqual(cur->name, BAD_CAST "script")) {
script = xmlGetProp(cur, BAD_CAST "path");
}
}
cur = cur->next;
}
net->vlan = 0;
if (macaddr) {
unsigned int mac[6];
sscanf((const char *)macaddr, "%02x:%02x:%02x:%02x:%02x:%02x",
@ -524,6 +550,9 @@ static struct qemud_vm_net_def *qemudParseInterfaceXML(struct qemud_server *serv
net->mac[5] = mac[5];
xmlFree(macaddr);
macaddr = NULL;
} else {
qemudRandomMAC(net);
}
if (net->type == QEMUD_NET_NETWORK) {
@ -533,7 +562,7 @@ static struct qemud_vm_net_def *qemudParseInterfaceXML(struct qemud_server *serv
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) {
} else if ((len = xmlStrlen(network)) >= (QEMUD_MAX_NAME_LEN-1)) {
qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
"Network name '%s' too long", network);
goto error;
@ -542,20 +571,118 @@ static struct qemud_vm_net_def *qemudParseInterfaceXML(struct qemud_server *serv
net->dst.network.name[len] = '\0';
}
if (network)
if (network) {
xmlFree(network);
network = NULL;
}
if (tapifname != NULL) {
if ((len == xmlStrlen(tapifname)) >= BR_IFNAME_MAXLEN) {
if (ifname != NULL) {
if ((len = xmlStrlen(ifname)) >= (BR_IFNAME_MAXLEN-1)) {
qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
"TAP interface name '%s' is too long", tapifname);
"TAP interface name '%s' is too long", ifname);
goto error;
} else {
strncpy(net->dst.network.tapifname, (char *)tapifname, len);
net->dst.network.tapifname[len] = '\0';
strncpy(net->dst.network.ifname, (char *)ifname, len);
net->dst.network.ifname[len] = '\0';
}
xmlFree(tapifname);
xmlFree(ifname);
ifname = NULL;
}
} else if (net->type == QEMUD_NET_ETHERNET) {
int len;
if (script != NULL) {
if ((len = xmlStrlen(script)) >= (PATH_MAX-1)) {
qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
"TAP script path '%s' is too long", script);
goto error;
} else {
strncpy(net->dst.ethernet.script, (char *)script, len);
net->dst.ethernet.script[len] = '\0';
}
xmlFree(script);
script = NULL;
}
if (ifname != NULL) {
if ((len = xmlStrlen(ifname)) >= (BR_IFNAME_MAXLEN-1)) {
qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
"TAP interface name '%s' is too long", ifname);
goto error;
} else {
strncpy(net->dst.ethernet.ifname, (char *)ifname, len);
net->dst.ethernet.ifname[len] = '\0';
}
xmlFree(ifname);
}
} else if (net->type == QEMUD_NET_BRIDGE) {
int len;
if (bridge == NULL) {
qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
"No <source> 'dev' attribute specified with <interface type='bridge'/>");
goto error;
} else if ((len = xmlStrlen(bridge)) >= (BR_IFNAME_MAXLEN-1)) {
qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
"TAP bridge path '%s' is too long", bridge);
goto error;
} else {
strncpy(net->dst.bridge.brname, (char *)bridge, len);
net->dst.bridge.brname[len] = '\0';
}
xmlFree(bridge);
bridge = NULL;
if (ifname != NULL) {
if ((len = xmlStrlen(ifname)) >= (BR_IFNAME_MAXLEN-1)) {
qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
"TAP interface name '%s' is too long", ifname);
goto error;
} else {
strncpy(net->dst.bridge.ifname, (char *)ifname, len);
net->dst.bridge.ifname[len] = '\0';
}
xmlFree(ifname);
}
} else if (net->type == QEMUD_NET_CLIENT ||
net->type == QEMUD_NET_SERVER ||
net->type == QEMUD_NET_MCAST) {
int len;
char *ret;
if (port == NULL) {
qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
"No <source> 'port' attribute specified with socket interface");
goto error;
}
if (!(net->dst.socket.port = strtol((char*)port, &ret, 10)) &&
ret == (char*)port) {
qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
"Cannot parse <source> 'port' attribute with socket interface");
goto error;
}
xmlFree(port);
port = NULL;
if (address == NULL) {
if (net->type == QEMUD_NET_CLIENT ||
net->type == QEMUD_NET_MCAST) {
qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
"No <source> 'address' attribute specified with socket interface");
goto error;
}
} else if ((len = xmlStrlen(address)) >= (BR_INET_ADDR_MAXLEN)) {
qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
"IP address '%s' is too long", address);
goto error;
}
if (address == NULL) {
net->dst.socket.address[0] = '\0';
} else {
strncpy(net->dst.socket.address, (char*)address,len);
net->dst.socket.address[len] = '\0';
}
xmlFree(address);
}
return net;
@ -563,8 +690,16 @@ static struct qemud_vm_net_def *qemudParseInterfaceXML(struct qemud_server *serv
error:
if (network)
xmlFree(network);
if (tapifname)
xmlFree(tapifname);
if (address)
xmlFree(address);
if (port)
xmlFree(port);
if (ifname)
xmlFree(ifname);
if (script)
xmlFree(script);
if (bridge)
xmlFree(bridge);
free(net);
return NULL;
}
@ -886,14 +1021,20 @@ static struct qemud_vm_def *qemudParseXML(struct qemud_server *server,
obj = xmlXPathEval(BAD_CAST "/domain/devices/disk", ctxt);
if ((obj != NULL) && (obj->type == XPATH_NODESET) &&
(obj->nodesetval != NULL) && (obj->nodesetval->nodeNr >= 0)) {
struct qemud_vm_disk_def *prev = NULL;
for (i = 0; i < obj->nodesetval->nodeNr; i++) {
struct qemud_vm_disk_def *disk;
if (!(disk = qemudParseDiskXML(server, obj->nodesetval->nodeTab[i]))) {
goto error;
}
def->ndisks++;
disk->next = def->disks;
def->disks = disk;
disk->next = NULL;
if (i == 0) {
def->disks = disk;
} else {
prev->next = disk;
}
prev = disk;
}
}
xmlXPathFreeObject(obj);
@ -903,14 +1044,20 @@ static struct qemud_vm_def *qemudParseXML(struct qemud_server *server,
obj = xmlXPathEval(BAD_CAST "/domain/devices/interface", ctxt);
if ((obj != NULL) && (obj->type == XPATH_NODESET) &&
(obj->nodesetval != NULL) && (obj->nodesetval->nodeNr >= 0)) {
struct qemud_vm_net_def *prev = NULL;
for (i = 0; i < obj->nodesetval->nodeNr; i++) {
struct qemud_vm_net_def *net;
if (!(net = qemudParseInterfaceXML(server, obj->nodesetval->nodeTab[i]))) {
goto error;
}
def->nnets++;
net->next = def->nets;
def->nets = net;
net->next = NULL;
if (i == 0) {
def->nets = net;
} else {
prev->next = net;
}
prev = net;
}
}
xmlXPathFreeObject(obj);
@ -933,49 +1080,65 @@ static struct qemud_vm_def *qemudParseXML(struct qemud_server *server,
static char *
qemudNetworkIfaceConnect(struct qemud_server *server,
struct qemud_vm *vm,
struct qemud_vm_net_def *net)
struct qemud_vm_net_def *net,
int vlan)
{
struct qemud_network *network;
const char *tapifname;
struct qemud_network *network = NULL;
char *brname;
char *ifname;
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";
if (net->type == QEMUD_NET_NETWORK) {
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;
}
brname = network->bridge;
if (net->dst.network.ifname[0] == '\0' ||
strchr(net->dst.network.ifname, '%')) {
strcpy(net->dst.network.ifname, "vnet%d");
}
ifname = net->dst.network.ifname;
} else if (net->type == QEMUD_NET_BRIDGE) {
brname = net->dst.bridge.brname;
if (net->dst.bridge.ifname[0] == '\0' ||
strchr(net->dst.bridge.ifname, '%')) {
strcpy(net->dst.bridge.ifname, "vnet%d");
}
ifname = net->dst.bridge.ifname;
} else {
tapifname = net->dst.network.tapifname;
qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
"Network type %d is not supported", net->type);
goto error;
}
if ((err = brAddTap(server->brctl, network->bridge, tapifname,
&net->dst.network.tapifname[0], BR_IFNAME_MAXLEN, &tapfd))) {
if ((err = brAddTap(server->brctl, brname,
ifname, 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));
ifname, brname, strerror(err));
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;
if (net->type == QEMUD_NET_NETWORK && network->def->forward) {
if ((err = iptablesAddPhysdevForward(server->iptables, ifname, network->def->forwardDev))) {
qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
"Failed to add iptables rule to allow bridging from '%s' :%s",
ifname, strerror(err));
goto error;
}
}
snprintf(tapfdstr, sizeof(tapfdstr), "tap,fd=%d,script=", tapfd);
snprintf(tapfdstr, sizeof(tapfdstr), "tap,fd=%d,script=,vlan=%d", tapfd, vlan);
if (!(retval = strdup(tapfdstr)))
goto no_memory;
@ -990,7 +1153,8 @@ qemudNetworkIfaceConnect(struct qemud_server *server,
return retval;
no_memory:
iptablesRemovePhysdevForward(server->iptables, net->dst.network.tapifname);
if (net->type == QEMUD_NET_NETWORK && network->def->forward)
iptablesRemovePhysdevForward(server->iptables, ifname, network->def->forwardDev);
qemudReportError(server, VIR_ERR_NO_MEMORY, "tapfds");
error:
if (retval)
@ -1137,36 +1301,88 @@ int qemudBuildCommandLine(struct qemud_server *server,
if (!((*argv)[++n] = strdup("none")))
goto no_memory;
} else {
int vlan = 0;
while (net) {
char nic[3+1+7+1+17+1];
if (!net->mac[0] && !net->mac[1] && !net->mac[2] &&
!net->mac[3] && !net->mac[4] && !net->mac[5]) {
strncpy(nic, "nic", 4);
} else {
sprintf(nic, "nic,macaddr=%02x:%02x:%02x:%02x:%02x:%02x",
net->mac[0], net->mac[1],
net->mac[2], net->mac[3],
net->mac[4], net->mac[5]);
}
sprintf(nic, "nic,macaddr=%02x:%02x:%02x:%02x:%02x:%02x,vlan=%d",
net->mac[0], net->mac[1],
net->mac[2], net->mac[3],
net->mac[4], net->mac[5],
vlan);
if (!((*argv)[++n] = strdup("-net")))
goto no_memory;
if (!((*argv)[++n] = strdup(nic)))
goto no_memory;
if (!((*argv)[++n] = strdup("-net")))
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)))
switch (net->type) {
case QEMUD_NET_NETWORK:
case QEMUD_NET_BRIDGE:
if (!((*argv)[++n] = qemudNetworkIfaceConnect(server, vm, net, vlan)))
goto error;
break;
case QEMUD_NET_ETHERNET:
{
char arg[PATH_MAX];
if (snprintf(arg, PATH_MAX-1, "tap,ifname=%s,script=%s,vlan=%d",
net->dst.ethernet.ifname,
net->dst.ethernet.script,
vlan) >= (PATH_MAX-1))
goto error;
if (!((*argv)[++n] = strdup(arg)))
goto no_memory;
}
break;
case QEMUD_NET_CLIENT:
case QEMUD_NET_SERVER:
case QEMUD_NET_MCAST:
{
char arg[PATH_MAX];
const char *mode = NULL;
switch (net->type) {
case QEMUD_NET_CLIENT:
mode = "connect";
break;
case QEMUD_NET_SERVER:
mode = "listen";
break;
case QEMUD_NET_MCAST:
mode = "mcast";
break;
}
if (snprintf(arg, PATH_MAX-1, "socket,%s=%s:%d,vlan=%d",
mode,
net->dst.socket.address,
net->dst.socket.port,
vlan) >= (PATH_MAX-1))
goto error;
if (!((*argv)[++n] = strdup(arg)))
goto no_memory;
}
break;
case QEMUD_NET_USER:
default:
{
char arg[PATH_MAX];
if (snprintf(arg, PATH_MAX-1, "user,vlan=%d", vlan) >= (PATH_MAX-1))
goto error;
if (!((*argv)[++n] = strdup(arg)))
goto no_memory;
}
}
net = net->next;
vlan++;
}
}
@ -1567,7 +1783,7 @@ static struct qemud_network_def *qemudParseNetworkXML(struct qemud_server *serve
xmlDocPtr xml) {
xmlNodePtr root = NULL;
xmlXPathContextPtr ctxt = NULL;
xmlXPathObjectPtr obj = NULL;
xmlXPathObjectPtr obj = NULL, tmp = NULL;
struct qemud_network_def *def;
if (!(def = calloc(1, sizeof(struct qemud_network_def)))) {
@ -1620,6 +1836,31 @@ static struct qemud_network_def *qemudParseNetworkXML(struct qemud_server *serve
}
xmlXPathFreeObject(obj);
obj = xmlXPathEval(BAD_CAST "count(/network/forward) > 0", ctxt);
if ((obj != NULL) && (obj->type == XPATH_BOOLEAN) &&
obj->boolval) {
def->forward = 1;
tmp = xmlXPathEval(BAD_CAST "string(/network/forward[1]/@dev)", ctxt);
if ((tmp != NULL) && (tmp->type == XPATH_STRING) &&
(tmp->stringval != NULL) && (tmp->stringval[0] != 0)) {
int len;
if ((len = xmlStrlen(tmp->stringval)) >= (BR_IFNAME_MAXLEN-1)) {
qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
"forward device name '%s' is too long",
(char*)tmp->stringval);
goto error;
}
strcpy(def->forwardDev, (char*)tmp->stringval);
} else {
def->forwardDev[0] = '\0';
}
xmlXPathFreeObject(tmp);
tmp = NULL;
} else {
def->forward = 0;
}
xmlXPathFreeObject(obj);
/* Parse bridge information */
obj = xmlXPathEval(BAD_CAST "/network/bridge[1]", ctxt);
if ((obj != NULL) && (obj->type == XPATH_NODESET) &&
@ -1649,6 +1890,8 @@ static struct qemud_network_def *qemudParseNetworkXML(struct qemud_server *serve
the caller ? */
if (obj)
xmlXPathFreeObject(obj);
if (tmp)
xmlXPathFreeObject(tmp);
if (ctxt)
xmlXPathFreeContext(ctxt);
qemudFreeNetworkDef(def);
@ -2320,34 +2563,65 @@ char *qemudGenerateXML(struct qemud_server *server,
while (net) {
const char *types[] = {
"user",
"tap",
"ethernet",
"server",
"client",
"mcast",
"network",
"vde",
"bridge",
};
if (qemudBufferPrintf(&buf, " <interface type='%s'>\n",
types[net->type]) < 0)
goto no_memory;
if (net->mac[0] && net->mac[1] && net->mac[2] &&
net->mac[3] && net->mac[4] && net->mac[5] &&
qemudBufferPrintf(&buf, " <mac address='%02x:%02x:%02x:%02x:%02x:%02x'/>\n",
if (qemudBufferPrintf(&buf, " <mac address='%02x:%02x:%02x:%02x:%02x:%02x'/>\n",
net->mac[0], net->mac[1], net->mac[2],
net->mac[3], net->mac[4], net->mac[5]) < 0)
goto no_memory;
if (net->type == QEMUD_NET_NETWORK) {
if (qemudBufferPrintf(&buf, " <source network='%s'", net->dst.network.name) < 0)
switch (net->type) {
case QEMUD_NET_NETWORK:
if (qemudBufferPrintf(&buf, " <source network='%s'/>\n", 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 (net->dst.network.ifname[0] != '\0') {
if (qemudBufferPrintf(&buf, " <target dev='%s'/>\n", net->dst.network.ifname) < 0)
goto no_memory;
}
break;
if (qemudBufferPrintf(&buf, "/>\n") < 0)
case QEMUD_NET_ETHERNET:
if (net->dst.ethernet.ifname[0] != '\0') {
if (qemudBufferPrintf(&buf, " <target dev='%s'/>\n", net->dst.ethernet.ifname) < 0)
goto no_memory;
}
if (net->dst.ethernet.script[0] != '\0') {
if (qemudBufferPrintf(&buf, " <script path='%s'/>\n", net->dst.ethernet.script) < 0)
goto no_memory;
}
break;
case QEMUD_NET_BRIDGE:
if (qemudBufferPrintf(&buf, " <source dev='%s'/>\n", net->dst.bridge.brname) < 0)
goto no_memory;
if (net->dst.bridge.ifname[0] != '\0') {
if (qemudBufferPrintf(&buf, " <target dev='%s'/>\n", net->dst.bridge.ifname) < 0)
goto no_memory;
}
break;
case QEMUD_NET_SERVER:
case QEMUD_NET_CLIENT:
case QEMUD_NET_MCAST:
if (net->dst.socket.address[0] != '\0') {
if (qemudBufferPrintf(&buf, " <source address='%s' port='%d'/>\n",
net->dst.socket.address, net->dst.socket.port) < 0)
goto no_memory;
} else {
if (qemudBufferPrintf(&buf, " <source port='%d'/>\n",
net->dst.socket.port) < 0)
goto no_memory;
}
}
if (qemudBufferPrintf(&buf, " </interface>\n") < 0)
@ -2428,6 +2702,15 @@ char *qemudGenerateNetworkXML(struct qemud_server *server,
uuid[12], uuid[13], uuid[14], uuid[15]) < 0)
goto no_memory;
if (def->forward) {
if (def->forwardDev[0]) {
qemudBufferPrintf(&buf, " <forward dev='%s'/>\n",
def->forwardDev);
} else {
qemudBufferAdd(&buf, " <forward/>\n");
}
}
if ((def->bridge != '\0' || def->disableSTP || def->forwardDelay) &&
qemudBufferPrintf(&buf, " <bridge name='%s' stp='%s' delay='%d' />\n",
def->bridge,

View File

@ -1,6 +1,7 @@
<network>
<name>default</name>
<bridge name="virbr0" />
<forward/>
<ip address="192.168.122.1" netmask="255.255.255.0">
<dhcp>
<range start="192.168.122.2" end="192.168.122.254" />

View File

@ -103,43 +103,35 @@ struct qemud_vm_disk_def {
/* 5 different types of networking config */
enum qemud_vm_net_type {
QEMUD_NET_USER,
QEMUD_NET_TAP,
QEMUD_NET_ETHERNET,
QEMUD_NET_SERVER,
QEMUD_NET_CLIENT,
QEMUD_NET_MCAST,
QEMUD_NET_NETWORK,
/* QEMUD_NET_VDE*/
QEMUD_NET_BRIDGE,
};
/* Stores the virtual network interface configuration */
struct qemud_vm_net_def {
int type;
int vlan;
unsigned char mac[QEMUD_MAC_ADDRESS_LEN];
union {
struct {
char ifname[NAME_MAX];
char ifname[BR_IFNAME_MAXLEN];
char script[PATH_MAX];
} tap;
} ethernet;
struct {
struct sockaddr_in listen;
char address[BR_INET_ADDR_MAXLEN];
int port;
} server;
struct {
struct sockaddr_in connect;
int port;
} client;
struct {
struct sockaddr_in group;
int port;
} mcast;
struct {
char vlan[PATH_MAX];
} vde;
} socket; /* any of NET_CLIENT or NET_SERVER or NET_MCAST */
struct {
char name[QEMUD_MAX_NAME_LEN];
char tapifname[BR_IFNAME_MAXLEN];
char ifname[BR_IFNAME_MAXLEN];
} network;
struct {
char brname[BR_IFNAME_MAXLEN];
char ifname[BR_IFNAME_MAXLEN];
} bridge;
} dst;
struct qemud_vm_net_def *next;
@ -249,6 +241,9 @@ struct qemud_network_def {
int disableSTP;
int forwardDelay;
int forward;
char forwardDev[BR_IFNAME_MAXLEN];
char ipAddress[BR_INET_ADDR_MAXLEN];
char netmask[BR_INET_ADDR_MAXLEN];

View File

@ -532,20 +532,17 @@ iptablesInput(iptablesContext *ctx,
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;
return iptablesAddRemoveRule(ctx->input_filter,
action,
"--in-interface", iface,
"--protocol", tcp ? "tcp" : "udp",
"--destination-port", portstr,
"--jump", "ACCEPT",
NULL);
}
int
@ -583,106 +580,158 @@ iptablesRemoveUdpInput(iptablesContext *ctx,
static int
iptablesPhysdevForward(iptablesContext *ctx,
const char *iface,
const char *target,
int action)
{
return iptablesAddRemoveRule(ctx->forward_filter,
action,
"--match", "physdev",
"--physdev-in", iface,
"--jump", "ACCEPT",
NULL);
if (target && target[0]) {
return iptablesAddRemoveRule(ctx->forward_filter,
action,
"--match", "physdev",
"--physdev-in", iface,
"--out", target,
"--jump", "ACCEPT",
NULL);
} else {
return iptablesAddRemoveRule(ctx->forward_filter,
action,
"--match", "physdev",
"--physdev-in", iface,
"--jump", "ACCEPT",
NULL);
}
}
int
iptablesAddPhysdevForward(iptablesContext *ctx,
const char *iface)
const char *iface,
const char *target)
{
return iptablesPhysdevForward(ctx, iface, ADD);
return iptablesPhysdevForward(ctx, iface, target, ADD);
}
int
iptablesRemovePhysdevForward(iptablesContext *ctx,
const char *iface)
const char *iface,
const char *target)
{
return iptablesPhysdevForward(ctx, iface, REMOVE);
return iptablesPhysdevForward(ctx, iface, target, REMOVE);
}
static int
iptablesInterfaceForward(iptablesContext *ctx,
const char *iface,
const char *target,
int action)
{
return iptablesAddRemoveRule(ctx->forward_filter,
action,
"--in-interface", iface,
"--jump", "ACCEPT",
NULL);
if (target && target[0]) {
return iptablesAddRemoveRule(ctx->forward_filter,
action,
"--in-interface", iface,
"--out-interface", target,
"--jump", "ACCEPT",
NULL);
} else {
return iptablesAddRemoveRule(ctx->forward_filter,
action,
"--in-interface", iface,
"--jump", "ACCEPT",
NULL);
}
}
int
iptablesAddInterfaceForward(iptablesContext *ctx,
const char *iface)
const char *iface,
const char *target)
{
return iptablesInterfaceForward(ctx, iface, ADD);
return iptablesInterfaceForward(ctx, iface, target, ADD);
}
int
iptablesRemoveInterfaceForward(iptablesContext *ctx,
const char *iface)
const char *iface,
const char *target)
{
return iptablesInterfaceForward(ctx, iface, REMOVE);
return iptablesInterfaceForward(ctx, iface, target, REMOVE);
}
static int
iptablesStateForward(iptablesContext *ctx,
const char *iface,
const char *target,
int action)
{
return iptablesAddRemoveRule(ctx->forward_filter,
action,
"--out-interface", iface,
"--match", "state",
"--state", "ESTABLISHED,RELATED",
"--jump", "ACCEPT",
NULL);
if (target && target[0]) {
return iptablesAddRemoveRule(ctx->forward_filter,
action,
"--in-interface", target,
"--out-interface", iface,
"--match", "state",
"--state", "ESTABLISHED,RELATED",
"--jump", "ACCEPT",
NULL);
} else {
return iptablesAddRemoveRule(ctx->forward_filter,
action,
"--out-interface", iface,
"--match", "state",
"--state", "ESTABLISHED,RELATED",
"--jump", "ACCEPT",
NULL);
}
}
int
iptablesAddStateForward(iptablesContext *ctx,
const char *iface)
const char *iface,
const char *target)
{
return iptablesStateForward(ctx, iface, ADD);
return iptablesStateForward(ctx, iface, target, ADD);
}
int
iptablesRemoveStateForward(iptablesContext *ctx,
const char *iface)
const char *iface,
const char *target)
{
return iptablesStateForward(ctx, iface, REMOVE);
return iptablesStateForward(ctx, iface, target, REMOVE);
}
static int
iptablesNonBridgedMasq(iptablesContext *ctx,
const char *target,
int action)
{
return iptablesAddRemoveRule(ctx->nat_postrouting,
action,
"--match", "physdev",
"!", "--physdev-is-bridged",
"--jump", "MASQUERADE",
NULL);
if (target && target[0]) {
return iptablesAddRemoveRule(ctx->nat_postrouting,
action,
"--out-interface", target,
"--match", "physdev",
"!", "--physdev-is-bridged",
"--jump", "MASQUERADE",
NULL);
} else {
return iptablesAddRemoveRule(ctx->nat_postrouting,
action,
"--match", "physdev",
"!", "--physdev-is-bridged",
"--jump", "MASQUERADE",
NULL);
}
}
int
iptablesAddNonBridgedMasq(iptablesContext *ctx)
iptablesAddNonBridgedMasq(iptablesContext *ctx,
const char *target)
{
return iptablesNonBridgedMasq(ctx, ADD);
return iptablesNonBridgedMasq(ctx, target, ADD);
}
int
iptablesRemoveNonBridgedMasq(iptablesContext *ctx)
iptablesRemoveNonBridgedMasq(iptablesContext *ctx,
const char *target)
{
return iptablesNonBridgedMasq(ctx, REMOVE);
return iptablesNonBridgedMasq(ctx, target, REMOVE);
}
/*

View File

@ -42,22 +42,30 @@ int iptablesRemoveUdpInput (iptablesContext *ctx,
int port);
int iptablesAddPhysdevForward (iptablesContext *ctx,
const char *iface);
const char *iface,
const char *target);
int iptablesRemovePhysdevForward (iptablesContext *ctx,
const char *iface);
const char *iface,
const char *target);
int iptablesAddInterfaceForward (iptablesContext *ctx,
const char *iface);
const char *iface,
const char *target);
int iptablesRemoveInterfaceForward (iptablesContext *ctx,
const char *iface);
const char *iface,
const char *target);
int iptablesAddStateForward (iptablesContext *ctx,
const char *iface);
const char *iface,
const char *target);
int iptablesRemoveStateForward (iptablesContext *ctx,
const char *iface);
const char *iface,
const char *target);
int iptablesAddNonBridgedMasq (iptablesContext *ctx);
int iptablesRemoveNonBridgedMasq (iptablesContext *ctx);
int iptablesAddNonBridgedMasq (iptablesContext *ctx,
const char *target);
int iptablesRemoveNonBridgedMasq (iptablesContext *ctx,
const char *target);
#endif /* __QEMUD_IPTABLES_H__ */

View File

@ -1032,7 +1032,18 @@ static void
qemudNetworkIfaceDisconnect(struct qemud_server *server,
struct qemud_vm *vm ATTRIBUTE_UNUSED,
struct qemud_vm_net_def *net) {
iptablesRemovePhysdevForward(server->iptables, net->dst.network.tapifname);
struct qemud_network *network;
if (net->type != QEMUD_NET_NETWORK)
return;
if (!(network = qemudFindNetworkByName(server, net->dst.network.name))) {
return;
} else if (network->bridge[0] == '\0') {
return;
}
if (network->def->forward)
iptablesRemovePhysdevForward(server->iptables, net->dst.network.ifname, network->def->forwardDev);
}
int qemudShutdownVMDaemon(struct qemud_server *server, struct qemud_vm *vm) {
@ -1105,7 +1116,7 @@ qemudBuildDnsmasqArgv(struct qemud_server *server,
struct qemud_network *network,
char ***argv) {
int i, len;
char buf[BR_INET_ADDR_MAXLEN * 2];
char buf[PATH_MAX];
struct qemud_dhcp_range_def *range;
len =
@ -1114,8 +1125,10 @@ qemudBuildDnsmasqArgv(struct qemud_server *server,
1 + /* --bind-interfaces */
2 + /* --pid-file "" */
2 + /* --conf-file "" */
/*2 + *//* --interface virbr0 */
2 + /* --except-interface lo */
2 + /* --listen-address 10.0.0.1 */
1 + /* --dhcp-leasefile=path */
(2 * network->def->nranges) + /* --dhcp-range 10.0.0.2,10.0.0.254 */
1; /* NULL */
@ -1142,11 +1155,29 @@ qemudBuildDnsmasqArgv(struct qemud_server *server,
APPEND_ARG(*argv, i++, "--conf-file");
APPEND_ARG(*argv, i++, "");
/*
* XXX does not actually work, due to some kind of
* race condition setting up ipv6 addresses on the
* interface. A sleep(10) makes it work, but that's
* clearly not practical
*
* APPEND_ARG(*argv, i++, "--interface");
* APPEND_ARG(*argv, i++, network->def->bridge);
*/
APPEND_ARG(*argv, i++, "--listen-address");
APPEND_ARG(*argv, i++, network->def->ipAddress);
APPEND_ARG(*argv, i++, "--except-interface");
APPEND_ARG(*argv, i++, "lo");
APPEND_ARG(*argv, i++, "--listen-address");
APPEND_ARG(*argv, i++, network->def->ipAddress);
/*
* NB, dnsmasq command line arg bug means we need to
* use a single arg '--dhcp-leasefile=path' rather than
* two separate args in '--dhcp-leasefile path' style
*/
snprintf(buf, sizeof(buf), "--dhcp-leasefile=%s/lib/libvirt/dhcp-%s.leases",
LOCAL_STATE_DIR, network->def->name);
APPEND_ARG(*argv, i++, buf);
range = network->def->ranges;
while (range) {
@ -1211,7 +1242,7 @@ qemudAddIptablesRules(struct qemud_server *server,
}
/* allow bridging from the bridge interface itself */
if ((err = iptablesAddPhysdevForward(server->iptables, network->bridge))) {
if ((err = iptablesAddPhysdevForward(server->iptables, network->bridge, network->def->forwardDev))) {
qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
"failed to add iptables rule to allow bridging from '%s' : %s\n",
network->bridge, strerror(err));
@ -1219,7 +1250,7 @@ qemudAddIptablesRules(struct qemud_server *server,
}
/* allow forwarding packets from the bridge interface */
if ((err = iptablesAddInterfaceForward(server->iptables, network->bridge))) {
if ((err = iptablesAddInterfaceForward(server->iptables, network->bridge, network->def->forwardDev))) {
qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
"failed to add iptables rule to allow forwarding from '%s' : %s\n",
network->bridge, strerror(err));
@ -1227,7 +1258,7 @@ qemudAddIptablesRules(struct qemud_server *server,
}
/* allow forwarding packets to the bridge interface if they are part of an existing connection */
if ((err = iptablesAddStateForward(server->iptables, network->bridge))) {
if ((err = iptablesAddStateForward(server->iptables, network->bridge, network->def->forwardDev))) {
qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
"failed to add iptables rule to allow forwarding to '%s' : %s\n",
network->bridge, strerror(err));
@ -1235,7 +1266,7 @@ qemudAddIptablesRules(struct qemud_server *server,
}
/* enable masquerading */
if ((err = iptablesAddNonBridgedMasq(server->iptables))) {
if ((err = iptablesAddNonBridgedMasq(server->iptables, network->def->forwardDev))) {
qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
"failed to add iptables rule to enable masquerading : %s\n",
strerror(err));
@ -1281,13 +1312,13 @@ qemudAddIptablesRules(struct qemud_server *server,
err6:
iptablesRemoveTcpInput(server->iptables, network->bridge, 67);
err5:
iptablesRemoveNonBridgedMasq(server->iptables);
iptablesRemoveNonBridgedMasq(server->iptables, network->def->forwardDev);
err4:
iptablesRemoveStateForward(server->iptables, network->bridge);
iptablesRemoveStateForward(server->iptables, network->bridge, network->def->forwardDev);
err3:
iptablesRemoveInterfaceForward(server->iptables, network->bridge);
iptablesRemoveInterfaceForward(server->iptables, network->bridge, network->def->forwardDev);
err2:
iptablesRemovePhysdevForward(server->iptables, network->bridge);
iptablesRemovePhysdevForward(server->iptables, network->bridge, network->def->forwardDev);
err1:
return 0;
}
@ -1295,14 +1326,16 @@ qemudAddIptablesRules(struct qemud_server *server,
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);
if (network->def->forward) {
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, network->def->forwardDev);
iptablesRemoveStateForward(server->iptables, network->bridge, network->def->forwardDev);
iptablesRemoveInterfaceForward(server->iptables, network->bridge, network->def->forwardDev);
iptablesRemovePhysdevForward(server->iptables, network->bridge, network->def->forwardDev);
}
}
static int
@ -1379,10 +1412,12 @@ int qemudStartNetworkDaemon(struct qemud_server *server,
goto err_delbr;
}
if (!qemudAddIptablesRules(server, network))
if (network->def->forward &&
!qemudAddIptablesRules(server, network))
goto err_delbr1;
if (!qemudEnableIpForwarding()) {
if (network->def->forward &&
!qemudEnableIpForwarding()) {
qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
"failed to enable IP forwarding : %s\n", strerror(err));
goto err_delbr2;