bridge_driver.c 82.0 KB
Newer Older
1
/*
2
 * bridge_driver.c: core driver methods for managing network
3
 *
E
Eric Blake 已提交
4
 * Copyright (C) 2006-2011 Red Hat, Inc.
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
 * Copyright (C) 2006 Daniel P. Berrange
 *
 * 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
 *
 * Author: Daniel P. Berrange <berrange@redhat.com>
 */

#include <config.h>

#include <sys/types.h>
#include <sys/poll.h>
#include <dirent.h>
#include <limits.h>
#include <string.h>
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <sys/utsname.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
#include <paths.h>
#include <pwd.h>
#include <stdio.h>
#include <sys/wait.h>
#include <sys/ioctl.h>

46
#include "virterror_internal.h"
47
#include "datatypes.h"
48
#include "bridge_driver.h"
49 50 51 52
#include "network_conf.h"
#include "driver.h"
#include "buf.h"
#include "util.h"
53
#include "command.h"
54 55 56 57
#include "memory.h"
#include "uuid.h"
#include "iptables.h"
#include "bridge.h"
58
#include "logging.h"
59
#include "dnsmasq.h"
60
#include "util/network.h"
61
#include "configmake.h"
62

63 64
#define NETWORK_PID_DIR LOCALSTATEDIR "/run/libvirt/network"
#define NETWORK_STATE_DIR LOCALSTATEDIR "/lib/libvirt/network"
65

66
#define DNSMASQ_STATE_DIR LOCALSTATEDIR "/lib/libvirt/dnsmasq"
67
#define RADVD_STATE_DIR LOCALSTATEDIR "/lib/libvirt/radvd"
68

69 70
#define VIR_FROM_THIS VIR_FROM_NETWORK

71
#define networkReportError(code, ...)                                   \
72
    virReportErrorHelper(VIR_FROM_NETWORK, code, __FILE__,              \
73
                         __FUNCTION__, __LINE__, __VA_ARGS__)
74

75 76
/* Main driver state */
struct network_driver {
77
    virMutex lock;
78

79
    virNetworkObjList networks;
80 81 82 83 84 85 86 87

    iptablesContext *iptables;
    brControl *brctl;
    char *networkConfigDir;
    char *networkAutostartDir;
    char *logDir;
};

88 89 90

static void networkDriverLock(struct network_driver *driver)
{
91
    virMutexLock(&driver->lock);
92 93 94
}
static void networkDriverUnlock(struct network_driver *driver)
{
95
    virMutexUnlock(&driver->lock);
96 97
}

98 99
static int networkShutdown(void);

100 101
static int networkStartNetworkDaemon(struct network_driver *driver,
                                     virNetworkObjPtr network);
102

103 104
static int networkShutdownNetworkDaemon(struct network_driver *driver,
                                        virNetworkObjPtr network);
105

106 107
static void networkReloadIptablesRules(struct network_driver *driver);

108 109
static struct network_driver *driverState = NULL;

110
static char *
111
networkDnsmasqLeaseFileNameDefault(const char *netname)
112 113 114 115 116 117 118 119
{
    char *leasefile;

    virAsprintf(&leasefile, DNSMASQ_STATE_DIR "/%s.leases",
                netname);
    return leasefile;
}

120 121 122
networkDnsmasqLeaseFileNameFunc networkDnsmasqLeaseFileName =
    networkDnsmasqLeaseFileNameDefault;

123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
static char *
networkRadvdPidfileBasename(const char *netname)
{
    /* this is simple but we want to be sure it's consistently done */
    char *pidfilebase;

    virAsprintf(&pidfilebase, "%s-radvd", netname);
    return pidfilebase;
}

static char *
networkRadvdConfigFileName(const char *netname)
{
    char *configfile;

    virAsprintf(&configfile, RADVD_STATE_DIR "/%s-radvd.conf",
                netname);
    return configfile;
}
142

143 144 145
static char *
networkBridgeDummyNicName(const char *brname)
{
146
    static const char dummyNicSuffix[] = "-nic";
147 148
    char *nicname;

149 150 151 152 153 154 155 156 157 158 159 160 161 162
    if (strlen(brname) + sizeof(dummyNicSuffix) > IFNAMSIZ) {
        /* because the length of an ifname is limited to IFNAMSIZ-1
         * (usually 15), and we're adding 4 more characters, we must
         * truncate the original name to 11 to fit. In order to catch
         * a possible numeric ending (eg virbr0, virbr1, etc), we grab
         * the first 8 and last 3 characters of the string.
         */
         virAsprintf(&nicname, "%.*s%s%s",
                     /* space for last 3 chars + "-nic" + NULL */
                     (int)(IFNAMSIZ - (3 + sizeof(dummyNicSuffix))),
                     brname, brname + strlen(brname) - 3, dummyNicSuffix);
    } else {
         virAsprintf(&nicname, "%s%s", brname, dummyNicSuffix);
    }
163 164 165
    return nicname;
}

166 167 168 169 170 171 172 173 174 175 176
static void
networkFindActiveConfigs(struct network_driver *driver) {
    unsigned int i;

    for (i = 0 ; i < driver->networks.count ; i++) {
        virNetworkObjPtr obj = driver->networks.objs[i];
        virNetworkDefPtr tmp;
        char *config;

        virNetworkObjLock(obj);

177
        if ((config = virNetworkConfigFile(NETWORK_STATE_DIR,
178 179 180 181 182 183 184 185 186 187 188 189
                                           obj->def->name)) == NULL) {
            virNetworkObjUnlock(obj);
            continue;
        }

        if (access(config, R_OK) < 0) {
            VIR_FREE(config);
            virNetworkObjUnlock(obj);
            continue;
        }

        /* Try and load the live config */
190
        tmp = virNetworkDefParseFile(config);
191 192 193 194 195 196 197 198 199 200 201
        VIR_FREE(config);
        if (tmp) {
            obj->newDef = obj->def;
            obj->def = tmp;
        }

        /* If bridge exists, then mark it active */
        if (obj->def->bridge &&
            brHasBridge(driver->brctl, obj->def->bridge) == 0) {
            obj->active = 1;

202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218
            /* Try and read dnsmasq/radvd pids if any */
            if (obj->def->ips && (obj->def->nips > 0)) {
                char *pidpath, *radvdpidbase;

                if (virFileReadPid(NETWORK_PID_DIR, obj->def->name,
                                   &obj->dnsmasqPid) == 0) {
                    /* Check that it's still alive */
                    if (kill(obj->dnsmasqPid, 0) != 0)
                        obj->dnsmasqPid = -1;
                    if (virAsprintf(&pidpath, "/proc/%d/exe", obj->dnsmasqPid) < 0) {
                        virReportOOMError();
                        goto cleanup;
                    }
                    if (virFileLinkPointsTo(pidpath, DNSMASQ) == 0)
                        obj->dnsmasqPid = -1;
                    VIR_FREE(pidpath);
                }
219

220
                if (!(radvdpidbase = networkRadvdPidfileBasename(obj->def->name))) {
221
                    virReportOOMError();
222 223
                    goto cleanup;
                }
224 225 226 227 228 229 230 231 232 233 234 235 236 237 238
                if (virFileReadPid(NETWORK_PID_DIR, radvdpidbase,
                                   &obj->radvdPid) == 0) {
                    /* Check that it's still alive */
                    if (kill(obj->radvdPid, 0) != 0)
                        obj->radvdPid = -1;
                    if (virAsprintf(&pidpath, "/proc/%d/exe", obj->radvdPid) < 0) {
                        virReportOOMError();
                        VIR_FREE(radvdpidbase);
                        goto cleanup;
                    }
                    if (virFileLinkPointsTo(pidpath, RADVD) == 0)
                        obj->radvdPid = -1;
                    VIR_FREE(pidpath);
                }
                VIR_FREE(radvdpidbase);
239 240 241
            }
        }

242
    cleanup:
243 244 245 246 247
        virNetworkObjUnlock(obj);
    }
}


248 249 250
static void
networkAutostartConfigs(struct network_driver *driver) {
    unsigned int i;
251

252
    for (i = 0 ; i < driver->networks.count ; i++) {
253
        virNetworkObjLock(driver->networks.objs[i]);
254
        if (driver->networks.objs[i]->autostart &&
D
Daniel P. Berrange 已提交
255
            !virNetworkObjIsActive(driver->networks.objs[i]) &&
256
            networkStartNetworkDaemon(driver, driver->networks.objs[i]) < 0) {
257
            /* failed to start but already logged */
258
        }
259
        virNetworkObjUnlock(driver->networks.objs[i]);
260 261 262 263 264 265 266 267 268
    }
}

/**
 * networkStartup:
 *
 * Initialization function for the QEmu daemon
 */
static int
269
networkStartup(int privileged) {
270 271
    uid_t uid = geteuid();
    char *base = NULL;
272
    int err;
273 274

    if (VIR_ALLOC(driverState) < 0)
275
        goto error;
276

277 278 279 280
    if (virMutexInit(&driverState->lock) < 0) {
        VIR_FREE(driverState);
        goto error;
    }
281 282
    networkDriverLock(driverState);

283
    if (privileged) {
284
        if (virAsprintf(&driverState->logDir,
285
                        "%s/log/libvirt/qemu", LOCALSTATEDIR) == -1)
286 287
            goto out_of_memory;

288
        if ((base = strdup (SYSCONFDIR "/libvirt")) == NULL)
289 290
            goto out_of_memory;
    } else {
291
        char *userdir = virGetUserDirectory(uid);
292 293 294

        if (!userdir)
            goto error;
295

296
        if (virAsprintf(&driverState->logDir,
297 298
                        "%s/.libvirt/qemu/log", userdir) == -1) {
            VIR_FREE(userdir);
299
            goto out_of_memory;
300
        }
301

302 303
        if (virAsprintf(&base, "%s/.libvirt", userdir) == -1) {
            VIR_FREE(userdir);
304 305
            goto out_of_memory;
        }
306
        VIR_FREE(userdir);
307 308 309 310 311
    }

    /* Configuration paths are either ~/.libvirt/qemu/... (session) or
     * /etc/libvirt/qemu/... (system).
     */
312
    if (virAsprintf(&driverState->networkConfigDir, "%s/qemu/networks", base) == -1)
313 314
        goto out_of_memory;

315 316
    if (virAsprintf(&driverState->networkAutostartDir, "%s/qemu/networks/autostart",
                    base) == -1)
317 318 319 320
        goto out_of_memory;

    VIR_FREE(base);

321
    if ((err = brInit(&driverState->brctl))) {
322
        virReportSystemError(err, "%s",
323 324 325 326 327
                             _("cannot initialize bridge support"));
        goto error;
    }

    if (!(driverState->iptables = iptablesContextNew())) {
328
        goto out_of_memory;
329 330 331
    }


332
    if (virNetworkLoadAllConfigs(&driverState->networks,
333
                                 driverState->networkConfigDir,
334 335 336
                                 driverState->networkAutostartDir) < 0)
        goto error;

337
    networkFindActiveConfigs(driverState);
338
    networkReloadIptablesRules(driverState);
339 340
    networkAutostartConfigs(driverState);

341 342
    networkDriverUnlock(driverState);

343 344
    return 0;

345
out_of_memory:
346
    virReportOOMError();
347 348

error:
349 350 351
    if (driverState)
        networkDriverUnlock(driverState);

352
    VIR_FREE(base);
353
    networkShutdown();
354 355 356 357 358 359 360 361 362 363 364
    return -1;
}

/**
 * networkReload:
 *
 * Function to restart the QEmu daemon, it will recheck the configuration
 * files and update its state and the networking
 */
static int
networkReload(void) {
365 366 367
    if (!driverState)
        return 0;

368
    networkDriverLock(driverState);
369
    virNetworkLoadAllConfigs(&driverState->networks,
370 371
                             driverState->networkConfigDir,
                             driverState->networkAutostartDir);
372
    networkReloadIptablesRules(driverState);
373
    networkAutostartConfigs(driverState);
374
    networkDriverUnlock(driverState);
375 376 377 378 379 380 381 382 383 384 385 386 387
    return 0;
}

/**
 * networkActive:
 *
 * Checks if the QEmu daemon is active, i.e. has an active domain or
 * an active network
 *
 * Returns 1 if active, 0 otherwise
 */
static int
networkActive(void) {
388
    unsigned int i;
389
    int active = 0;
390

391 392 393
    if (!driverState)
        return 0;

394
    networkDriverLock(driverState);
395 396
    for (i = 0 ; i < driverState->networks.count ; i++) {
        virNetworkObjPtr net = driverState->networks.objs[i];
397
        virNetworkObjLock(net);
D
Daniel P. Berrange 已提交
398
        if (virNetworkObjIsActive(net))
399
            active = 1;
400
        virNetworkObjUnlock(net);
401
    }
402
    networkDriverUnlock(driverState);
403
    return active;
404 405 406 407 408 409 410 411 412 413 414 415
}

/**
 * networkShutdown:
 *
 * Shutdown the QEmu daemon, it will stop all active domains and networks
 */
static int
networkShutdown(void) {
    if (!driverState)
        return -1;

416 417
    networkDriverLock(driverState);

418
    /* free inactive networks */
419
    virNetworkObjListFree(&driverState->networks);
420 421 422 423 424 425 426 427 428 429

    VIR_FREE(driverState->logDir);
    VIR_FREE(driverState->networkConfigDir);
    VIR_FREE(driverState->networkAutostartDir);

    if (driverState->brctl)
        brShutdown(driverState->brctl);
    if (driverState->iptables)
        iptablesContextFree(driverState->iptables);

430
    networkDriverUnlock(driverState);
431
    virMutexDestroy(&driverState->lock);
432

433 434 435 436 437 438
    VIR_FREE(driverState);

    return 0;
}


439 440 441 442
static int
networkBuildDnsmasqHostsfile(dnsmasqContext *dctx,
                             virNetworkIpDefPtr ipdef,
                             virNetworkDNSDefPtr dnsdef)
443
{
444
    unsigned int i, j;
445

446 447 448 449
    for (i = 0; i < ipdef->nhosts; i++) {
        virNetworkDHCPHostDefPtr host = &(ipdef->hosts[i]);
        if ((host->mac) && VIR_SOCKET_HAS_ADDR(&host->ip))
            dnsmasqAddDhcpHost(dctx, host->mac, &host->ip, host->name);
450
    }
451

452 453 454 455 456 457 458 459
    if (dnsdef) {
        for (i = 0; i < dnsdef->nhosts; i++) {
            virNetworkDNSHostsDefPtr host = &(dnsdef->hosts[i]);
            if (VIR_SOCKET_HAS_ADDR(&host->ip)) {
                for (j = 0; j < host->nnames; j++)
                    dnsmasqAddHost(dctx, &host->ip, host->names[j]);
            }
        }
460 461
    }

462
    return 0;
463 464 465
}


466
static int
467
networkBuildDnsmasqArgv(virNetworkObjPtr network,
468
                        virNetworkIpDefPtr ipdef,
469
                        const char *pidfile,
470 471 472
                        virCommandPtr cmd,
                        dnsmasqContext *dctx)
{
473
    int r, ret = -1;
474
    int nbleases = 0;
475 476
    int ii;
    virNetworkIpDefPtr tmpipdef;
477 478

    /*
479
     * NB, be careful about syntax for dnsmasq options in long format.
480 481 482 483 484 485 486 487 488 489 490 491 492
     *
     * If the flag has a mandatory argument, it can be given using
     * either syntax:
     *
     *     --foo bar
     *     --foo=bar
     *
     * If the flag has a optional argument, it *must* be given using
     * the syntax:
     *
     *     --foo=bar
     *
     * It is hard to determine whether a flag is optional or not,
493 494
     * without reading the dnsmasq source :-( The manpage is not
     * very explicit on this.
495
     */
496 497 498 499 500

    /*
     * Needed to ensure dnsmasq uses same algorithm for processing
     * multiple namedriver entries in /etc/resolv.conf as GLibC.
     */
501
    virCommandAddArgList(cmd, "--strict-order", "--bind-interfaces", NULL);
502

503 504
    if (network->def->domain)
        virCommandAddArgList(cmd, "--domain", network->def->domain, NULL);
505

506 507
    if (pidfile)
        virCommandAddArgPair(cmd, "--pid-file", pidfile);
508

509
    /* *no* conf file */
510
    virCommandAddArg(cmd, "--conf-file=");
511

512 513 514
    virCommandAddArgList(cmd,
                         "--except-interface", "lo",
                         NULL);
515

516 517 518 519 520 521 522
    /* If this is an isolated network, set the default route option
     * (3) to be empty to avoid setting a default route that's
     * guaranteed to not work.
     */
    if (network->def->forwardType == VIR_NETWORK_FORWARD_NONE)
        virCommandAddArg(cmd, "--dhcp-option=3");

523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540
    if (network->def->dns != NULL) {
        virNetworkDNSDefPtr dns = network->def->dns;
        int i;

        for (i = 0; i < dns->ntxtrecords; i++) {
            char *record = NULL;
            if (virAsprintf(&record, "%s,%s",
                            dns->txtrecords[i].name,
                            dns->txtrecords[i].value) < 0) {
                virReportOOMError();
                goto cleanup;
            }

            virCommandAddArgPair(cmd, "--txt-record", record);
            VIR_FREE(record);
        }
    }

541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558
    /*
     * --interface does not actually work with dnsmasq < 2.47,
     * due to DAD for ipv6 addresses on the interface.
     *
     * virCommandAddArgList(cmd, "--interface", ipdef->bridge, NULL);
     *
     * So listen on all defined IPv[46] addresses
     */
    for (ii = 0;
         (tmpipdef = virNetworkDefGetIpByIndex(network->def, AF_UNSPEC, ii));
         ii++) {
        char *ipaddr = virSocketFormatAddr(&tmpipdef->address);
        if (!ipaddr)
            goto cleanup;
        virCommandAddArgList(cmd, "--listen-address", ipaddr, NULL);
        VIR_FREE(ipaddr);
    }

559
    if (ipdef) {
560 561 562 563 564 565 566 567 568 569 570
        for (r = 0 ; r < ipdef->nranges ; r++) {
            char *saddr = virSocketFormatAddr(&ipdef->ranges[r].start);
            if (!saddr)
                goto cleanup;
            char *eaddr = virSocketFormatAddr(&ipdef->ranges[r].end);
            if (!eaddr) {
                VIR_FREE(saddr);
                goto cleanup;
            }
            virCommandAddArg(cmd, "--dhcp-range");
            virCommandAddArgFormat(cmd, "%s,%s", saddr, eaddr);
571
            VIR_FREE(saddr);
572 573 574
            VIR_FREE(eaddr);
            nbleases += virSocketGetRange(&ipdef->ranges[r].start,
                                          &ipdef->ranges[r].end);
575
        }
576

577 578 579 580 581 582 583 584 585 586 587 588 589
        /*
         * For static-only DHCP, i.e. with no range but at least one host element,
         * we have to add a special --dhcp-range option to enable the service in
         * dnsmasq.
         */
        if (!ipdef->nranges && ipdef->nhosts) {
            char *bridgeaddr = virSocketFormatAddr(&ipdef->address);
            if (!bridgeaddr)
                goto cleanup;
            virCommandAddArg(cmd, "--dhcp-range");
            virCommandAddArgFormat(cmd, "%s,static", bridgeaddr);
            VIR_FREE(bridgeaddr);
        }
590

591
        if (ipdef->nranges > 0) {
592 593 594 595 596
            char *leasefile = networkDnsmasqLeaseFileName(network->def->name);
            if (!leasefile)
                goto cleanup;
            virCommandAddArgFormat(cmd, "--dhcp-leasefile=%s", leasefile);
            VIR_FREE(leasefile);
597 598
            virCommandAddArgFormat(cmd, "--dhcp-lease-max=%d", nbleases);
        }
599

600 601
        if (ipdef->nranges || ipdef->nhosts)
            virCommandAddArg(cmd, "--dhcp-no-override");
602

603 604 605 606
        /* add domain to any non-qualified hostnames in /etc/hosts or addn-hosts */
        if (network->def->domain)
           virCommandAddArg(cmd, "--expand-hosts");

607
        if (networkBuildDnsmasqHostsfile(dctx, ipdef, network->def->dns) >= 0) {
608 609 610 611 612 613
            if (dctx->hostsfile->nhosts)
                virCommandAddArgPair(cmd, "--dhcp-hostsfile",
                                     dctx->hostsfile->path);
            if (dctx->addnhostsfile->nhosts)
                virCommandAddArgPair(cmd, "--addn-hosts",
                                     dctx->addnhostsfile->path);
614
        }
615

616 617 618 619 620 621 622 623 624
        if (ipdef->tftproot) {
            virCommandAddArgList(cmd, "--enable-tftp",
                                 "--tftp-root", ipdef->tftproot,
                                 NULL);
        }
        if (ipdef->bootfile) {
            virCommandAddArg(cmd, "--dhcp-boot");
            if (VIR_SOCKET_HAS_ADDR(&ipdef->bootserver)) {
                char *bootserver = virSocketFormatAddr(&ipdef->bootserver);
625

626 627 628 629 630 631 632 633
                if (!bootserver)
                    goto cleanup;
                virCommandAddArgFormat(cmd, "%s%s%s",
                                       ipdef->bootfile, ",,", bootserver);
                VIR_FREE(bootserver);
            } else {
                virCommandAddArg(cmd, ipdef->bootfile);
            }
634
        }
635 636
    }

637 638 639
    ret = 0;
cleanup:
    return ret;
640 641
}

642 643
int
networkBuildDhcpDaemonCommandLine(virNetworkObjPtr network, virCommandPtr *cmdout,
644
                                  char *pidfile, dnsmasqContext *dctx)
645
{
646
    virCommandPtr cmd = NULL;
647
    int ret = -1, ii;
648
    virNetworkIpDefPtr ipdef;
649 650

    network->dnsmasqPid = -1;
651

652 653 654 655 656 657 658
    /* Look for first IPv4 address that has dhcp defined. */
    /* We support dhcp config on 1 IPv4 interface only. */
    for (ii = 0;
         (ipdef = virNetworkDefGetIpByIndex(network->def, AF_INET, ii));
         ii++) {
        if (ipdef->nranges || ipdef->nhosts)
            break;
659
    }
660
    /* If no IPv4 addresses had dhcp info, pick the first (if there were any). */
661
    if (!ipdef)
662 663 664 665 666 667 668 669
        ipdef = virNetworkDefGetIpByIndex(network->def, AF_INET, 0);

    /* If there are no IP addresses at all (v4 or v6), return now, since
     * there won't be any address for dnsmasq to listen on anyway.
     * If there are any addresses, even if no dhcp ranges or static entries,
     * we should continue and run dnsmasq, just for the DNS capabilities.
     */
    if (!virNetworkDefGetIpByIndex(network->def, AF_UNSPEC, 0))
670
        return 0;
671

672
    cmd = virCommandNew(DNSMASQ);
673
    if (networkBuildDnsmasqArgv(network, ipdef, pidfile, cmd, dctx) < 0) {
674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692
        goto cleanup;
    }

    if (cmdout)
        *cmdout = cmd;
    ret = 0;
cleanup:
    if (ret < 0)
        virCommandFree(cmd);
    return ret;
}

static int
networkStartDhcpDaemon(virNetworkObjPtr network)
{
    virCommandPtr cmd = NULL;
    char *pidfile = NULL;
    int ret = -1;
    int err;
693
    dnsmasqContext *dctx = NULL;
694

L
Laine Stump 已提交
695
    if ((err = virFileMakePath(NETWORK_PID_DIR)) != 0) {
696
        virReportSystemError(err,
697 698
                             _("cannot create directory %s"),
                             NETWORK_PID_DIR);
699
        goto cleanup;
700
    }
L
Laine Stump 已提交
701
    if ((err = virFileMakePath(NETWORK_STATE_DIR)) != 0) {
702
        virReportSystemError(err,
703 704
                             _("cannot create directory %s"),
                             NETWORK_STATE_DIR);
705
        goto cleanup;
706 707 708
    }

    if (!(pidfile = virFilePid(NETWORK_PID_DIR, network->def->name))) {
709
        virReportOOMError();
710
        goto cleanup;
711 712
    }

713 714 715 716 717 718 719
    if ((err = virFileMakePath(DNSMASQ_STATE_DIR)) != 0) {
        virReportSystemError(err,
                             _("cannot create directory %s"),
                             DNSMASQ_STATE_DIR);
        goto cleanup;
    }

720 721 722 723 724 725 726 727 728 729
    dctx = dnsmasqContextNew(network->def->name, DNSMASQ_STATE_DIR);
    if (dctx == NULL)
        goto cleanup;

    ret = networkBuildDhcpDaemonCommandLine(network, &cmd, pidfile, dctx);
    if (ret < 0)
        goto cleanup;

    ret = dnsmasqSave(dctx);
    if (ret < 0)
730
        goto cleanup;
731

732
    if (virCommandRun(cmd, NULL) < 0)
733 734 735
        goto cleanup;

    /*
736 737 738 739 740
     * There really is no race here - when dnsmasq daemonizes, its
     * leader process stays around until its child has actually
     * written its pidfile. So by time virCommandRun exits it has
     * waitpid'd and guaranteed the proess has started and written a
     * pid
741 742 743 744 745
     */

    if (virFileReadPid(NETWORK_PID_DIR, network->def->name,
                       &network->dnsmasqPid) < 0)
        goto cleanup;
746

747 748 749
    ret = 0;
cleanup:
    VIR_FREE(pidfile);
750
    virCommandFree(cmd);
751
    dnsmasqContextFree(dctx);
752 753 754
    return ret;
}

755 756 757 758 759 760 761 762 763 764 765 766 767 768
static int
networkStartRadvd(virNetworkObjPtr network)
{
    char *pidfile = NULL;
    char *radvdpidbase = NULL;
    virBuffer configbuf = VIR_BUFFER_INITIALIZER;;
    char *configstr = NULL;
    char *configfile = NULL;
    virCommandPtr cmd = NULL;
    int ret = -1, err, ii;
    virNetworkIpDefPtr ipdef;

    network->radvdPid = -1;

E
Eric Blake 已提交
769
    if (!virFileIsExecutable(RADVD)) {
770 771 772 773 774 775 776
        virReportSystemError(errno,
                             _("Cannot find %s - "
                               "Possibly the package isn't installed"),
                             RADVD);
        goto cleanup;
    }

777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800
    if ((err = virFileMakePath(NETWORK_PID_DIR)) != 0) {
        virReportSystemError(err,
                             _("cannot create directory %s"),
                             NETWORK_PID_DIR);
        goto cleanup;
    }
    if ((err = virFileMakePath(RADVD_STATE_DIR)) != 0) {
        virReportSystemError(err,
                             _("cannot create directory %s"),
                             RADVD_STATE_DIR);
        goto cleanup;
    }

    /* construct pidfile name */
    if (!(radvdpidbase = networkRadvdPidfileBasename(network->def->name))) {
        virReportOOMError();
        goto cleanup;
    }
    if (!(pidfile = virFilePid(NETWORK_PID_DIR, radvdpidbase))) {
        virReportOOMError();
        goto cleanup;
    }

    /* create radvd config file appropriate for this network */
801
    virBufferAsprintf(&configbuf, "interface %s\n"
802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822
                      "{\n"
                      "  AdvSendAdvert on;\n"
                      "  AdvManagedFlag off;\n"
                      "  AdvOtherConfigFlag off;\n"
                      "\n",
                      network->def->bridge);
    for (ii = 0;
         (ipdef = virNetworkDefGetIpByIndex(network->def, AF_INET6, ii));
         ii++) {
        int prefix;
        char *netaddr;

        prefix = virNetworkIpDefPrefix(ipdef);
        if (prefix < 0) {
            networkReportError(VIR_ERR_INTERNAL_ERROR,
                               _("bridge  '%s' has an invalid prefix"),
                               network->def->bridge);
            goto cleanup;
        }
        if (!(netaddr = virSocketFormatAddr(&ipdef->address)))
            goto cleanup;
823
        virBufferAsprintf(&configbuf,
824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892
                          "  prefix %s/%d\n"
                          "  {\n"
                          "    AdvOnLink on;\n"
                          "    AdvAutonomous on;\n"
                          "    AdvRouterAddr off;\n"
                          "  };\n",
                          netaddr, prefix);
        VIR_FREE(netaddr);
    }

    virBufferAddLit(&configbuf, "};\n");

    if (virBufferError(&configbuf)) {
        virReportOOMError();
        goto cleanup;
    }
    if (!(configstr = virBufferContentAndReset(&configbuf))) {
        virReportOOMError();
        goto cleanup;
    }

    /* construct the filename */
    if (!(configfile = networkRadvdConfigFileName(network->def->name))) {
        virReportOOMError();
        goto cleanup;
    }
    /* write the file */
    if (virFileWriteStr(configfile, configstr, 0600) < 0) {
        virReportSystemError(errno,
                             _("couldn't write radvd config file '%s'"),
                             configfile);
        goto cleanup;
    }

    /* prevent radvd from daemonizing itself with "--debug 1", and use
     * a dummy pidfile name - virCommand will create the pidfile we
     * want to use (this is necessary because radvd's internal
     * daemonization and pidfile creation causes a race, and the
     * virFileReadPid() below will fail if we use them).
     * Unfortunately, it isn't possible to tell radvd to not create
     * its own pidfile, so we just let it do so, with a slightly
     * different name. Unused, but harmless.
     */
    cmd = virCommandNewArgList(RADVD, "--debug", "1",
                               "--config", configfile,
                               "--pidfile", NULL);
    virCommandAddArgFormat(cmd, "%s-bin", pidfile);

    virCommandSetPidFile(cmd, pidfile);
    virCommandDaemonize(cmd);

    if (virCommandRun(cmd, NULL) < 0)
        goto cleanup;

    if (virFileReadPid(NETWORK_PID_DIR, radvdpidbase,
                       &network->radvdPid) < 0)
        goto cleanup;

    ret = 0;
cleanup:
    virCommandFree(cmd);
    VIR_FREE(configfile);
    VIR_FREE(configstr);
    virBufferFreeAndReset(&configbuf);
    VIR_FREE(radvdpidbase);
    VIR_FREE(pidfile);
    return ret;
}

893
static int
894
networkAddMasqueradingIptablesRules(struct network_driver *driver,
895 896
                                    virNetworkObjPtr network,
                                    virNetworkIpDefPtr ipdef)
897 898
{
    int prefix = virNetworkIpDefPrefix(ipdef);
899 900 901 902 903 904 905

    if (prefix < 0) {
        networkReportError(VIR_ERR_INTERNAL_ERROR,
                           _("Invalid prefix or netmask for '%s'"),
                           network->def->bridge);
        goto masqerr1;
    }
906

907
    /* allow forwarding packets from the bridge interface */
908
    if (iptablesAddForwardAllowOut(driver->iptables,
909
                                   &ipdef->address,
910
                                   prefix,
911 912 913 914 915
                                   network->def->bridge,
                                   network->def->forwardDev) < 0) {
        networkReportError(VIR_ERR_SYSTEM_ERROR,
                           _("failed to add iptables rule to allow forwarding from '%s'"),
                           network->def->bridge);
916 917 918
        goto masqerr1;
    }

919 920 921
    /* allow forwarding packets to the bridge interface if they are
     * part of an existing connection
     */
922
    if (iptablesAddForwardAllowRelatedIn(driver->iptables,
923
                                         &ipdef->address,
924
                                         prefix,
925 926 927 928 929
                                         network->def->bridge,
                                         network->def->forwardDev) < 0) {
        networkReportError(VIR_ERR_SYSTEM_ERROR,
                           _("failed to add iptables rule to allow forwarding to '%s'"),
                           network->def->bridge);
930 931 932
        goto masqerr2;
    }

933 934 935 936 937
    /*
     * Enable masquerading.
     *
     * We need to end up with 3 rules in the table in this order
     *
E
Eric Blake 已提交
938 939
     *  1. protocol=tcp with sport mapping restriction
     *  2. protocol=udp with sport mapping restriction
940 941 942
     *  3. generic any protocol
     *
     * The sport mappings are required, because default IPtables
E
Eric Blake 已提交
943
     * MASQUERADE maintain port numbers unchanged where possible.
944 945 946 947 948 949 950 951 952 953 954 955 956
     *
     * NFS can be configured to only "trust" port numbers < 1023.
     *
     * Guests using NAT thus need to be prevented from having port
     * numbers < 1023, otherwise they can bypass the NFS "security"
     * check on the source port number.
     *
     * Since we use '--insert' to add rules to the header of the
     * chain, we actually need to add them in the reverse of the
     * order just mentioned !
     */

    /* First the generic masquerade rule for other protocols */
957
    if (iptablesAddForwardMasquerade(driver->iptables,
958
                                     &ipdef->address,
959
                                     prefix,
960 961 962 963 964
                                     network->def->forwardDev,
                                     NULL) < 0) {
        networkReportError(VIR_ERR_SYSTEM_ERROR,
                           _("failed to add iptables rule to enable masquerading to '%s'"),
                           network->def->forwardDev ? network->def->forwardDev : NULL);
965 966 967
        goto masqerr3;
    }

968
    /* UDP with a source port restriction */
969
    if (iptablesAddForwardMasquerade(driver->iptables,
970
                                     &ipdef->address,
971
                                     prefix,
972 973 974 975 976
                                     network->def->forwardDev,
                                     "udp") < 0) {
        networkReportError(VIR_ERR_SYSTEM_ERROR,
                           _("failed to add iptables rule to enable UDP masquerading to '%s'"),
                           network->def->forwardDev ? network->def->forwardDev : NULL);
977 978 979 980
        goto masqerr4;
    }

    /* TCP with a source port restriction */
981
    if (iptablesAddForwardMasquerade(driver->iptables,
982
                                     &ipdef->address,
983
                                     prefix,
984 985 986 987 988
                                     network->def->forwardDev,
                                     "tcp") < 0) {
        networkReportError(VIR_ERR_SYSTEM_ERROR,
                           _("failed to add iptables rule to enable TCP masquerading to '%s'"),
                           network->def->forwardDev ? network->def->forwardDev : NULL);
989 990 991
        goto masqerr5;
    }

992
    return 0;
993

994 995
 masqerr5:
    iptablesRemoveForwardMasquerade(driver->iptables,
996
                                    &ipdef->address,
997
                                    prefix,
998 999 1000 1001
                                    network->def->forwardDev,
                                    "udp");
 masqerr4:
    iptablesRemoveForwardMasquerade(driver->iptables,
1002
                                    &ipdef->address,
1003
                                    prefix,
1004 1005
                                    network->def->forwardDev,
                                    NULL);
1006 1007
 masqerr3:
    iptablesRemoveForwardAllowRelatedIn(driver->iptables,
1008
                                        &ipdef->address,
1009
                                        prefix,
1010 1011
                                        network->def->bridge,
                                        network->def->forwardDev);
1012 1013
 masqerr2:
    iptablesRemoveForwardAllowOut(driver->iptables,
1014
                                  &ipdef->address,
1015
                                  prefix,
1016 1017 1018
                                  network->def->bridge,
                                  network->def->forwardDev);
 masqerr1:
1019
    return -1;
1020 1021
}

1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058
static void
networkRemoveMasqueradingIptablesRules(struct network_driver *driver,
                                       virNetworkObjPtr network,
                                       virNetworkIpDefPtr ipdef)
{
    int prefix = virNetworkIpDefPrefix(ipdef);

    if (prefix >= 0) {
        iptablesRemoveForwardMasquerade(driver->iptables,
                                        &ipdef->address,
                                        prefix,
                                        network->def->forwardDev,
                                        "tcp");
        iptablesRemoveForwardMasquerade(driver->iptables,
                                        &ipdef->address,
                                        prefix,
                                        network->def->forwardDev,
                                        "udp");
        iptablesRemoveForwardMasquerade(driver->iptables,
                                        &ipdef->address,
                                        prefix,
                                        network->def->forwardDev,
                                        NULL);

        iptablesRemoveForwardAllowRelatedIn(driver->iptables,
                                            &ipdef->address,
                                            prefix,
                                            network->def->bridge,
                                            network->def->forwardDev);
        iptablesRemoveForwardAllowOut(driver->iptables,
                                      &ipdef->address,
                                      prefix,
                                      network->def->bridge,
                                      network->def->forwardDev);
    }
}

1059
static int
1060
networkAddRoutingIptablesRules(struct network_driver *driver,
1061
                               virNetworkObjPtr network,
1062 1063
                               virNetworkIpDefPtr ipdef)
{
1064
    int prefix = virNetworkIpDefPrefix(ipdef);
1065 1066 1067 1068 1069 1070 1071

    if (prefix < 0) {
        networkReportError(VIR_ERR_INTERNAL_ERROR,
                           _("Invalid prefix or netmask for '%s'"),
                           network->def->bridge);
        goto routeerr1;
    }
1072

1073
    /* allow routing packets from the bridge interface */
1074
    if (iptablesAddForwardAllowOut(driver->iptables,
1075
                                   &ipdef->address,
1076
                                   prefix,
1077 1078 1079 1080 1081
                                   network->def->bridge,
                                   network->def->forwardDev) < 0) {
        networkReportError(VIR_ERR_SYSTEM_ERROR,
                           _("failed to add iptables rule to allow routing from '%s'"),
                           network->def->bridge);
1082 1083 1084 1085
        goto routeerr1;
    }

    /* allow routing packets to the bridge interface */
1086
    if (iptablesAddForwardAllowIn(driver->iptables,
1087
                                  &ipdef->address,
1088
                                  prefix,
1089 1090 1091 1092 1093
                                  network->def->bridge,
                                  network->def->forwardDev) < 0) {
        networkReportError(VIR_ERR_SYSTEM_ERROR,
                           _("failed to add iptables rule to allow routing to '%s'"),
                           network->def->bridge);
1094 1095 1096
        goto routeerr2;
    }

1097
    return 0;
1098

1099
routeerr2:
1100
    iptablesRemoveForwardAllowOut(driver->iptables,
1101
                                  &ipdef->address,
1102
                                  prefix,
1103 1104
                                  network->def->bridge,
                                  network->def->forwardDev);
1105
routeerr1:
1106
    return -1;
1107 1108
}

1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130
static void
networkRemoveRoutingIptablesRules(struct network_driver *driver,
                                  virNetworkObjPtr network,
                                  virNetworkIpDefPtr ipdef)
{
    int prefix = virNetworkIpDefPrefix(ipdef);

    if (prefix >= 0) {
        iptablesRemoveForwardAllowIn(driver->iptables,
                                     &ipdef->address,
                                     prefix,
                                     network->def->bridge,
                                     network->def->forwardDev);

        iptablesRemoveForwardAllowOut(driver->iptables,
                                      &ipdef->address,
                                      prefix,
                                      network->def->bridge,
                                      network->def->forwardDev);
    }
}

1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166
/* Add all once/network rules required for IPv6 (if any IPv6 addresses are defined) */
static int
networkAddGeneralIp6tablesRules(struct network_driver *driver,
                               virNetworkObjPtr network)
{

    if (!virNetworkDefGetIpByIndex(network->def, AF_INET6, 0))
        return 0;

    /* Catch all rules to block forwarding to/from bridges */

    if (iptablesAddForwardRejectOut(driver->iptables, AF_INET6,
                                    network->def->bridge) < 0) {
        networkReportError(VIR_ERR_SYSTEM_ERROR,
                           _("failed to add ip6tables rule to block outbound traffic from '%s'"),
                           network->def->bridge);
        goto err1;
    }

    if (iptablesAddForwardRejectIn(driver->iptables, AF_INET6,
                                   network->def->bridge) < 0) {
        networkReportError(VIR_ERR_SYSTEM_ERROR,
                           _("failed to add ip6tables rule to block inbound traffic to '%s'"),
                           network->def->bridge);
        goto err2;
    }

    /* Allow traffic between guests on the same bridge */
    if (iptablesAddForwardAllowCross(driver->iptables, AF_INET6,
                                     network->def->bridge) < 0) {
        networkReportError(VIR_ERR_SYSTEM_ERROR,
                           _("failed to add ip6tables rule to allow cross bridge traffic on '%s'"),
                           network->def->bridge);
        goto err3;
    }

1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183
    /* allow DNS over IPv6 */
    if (iptablesAddTcpInput(driver->iptables, AF_INET6,
                            network->def->bridge, 53) < 0) {
        networkReportError(VIR_ERR_SYSTEM_ERROR,
                           _("failed to add ip6tables rule to allow DNS requests from '%s'"),
                           network->def->bridge);
        goto err4;
    }

    if (iptablesAddUdpInput(driver->iptables, AF_INET6,
                            network->def->bridge, 53) < 0) {
        networkReportError(VIR_ERR_SYSTEM_ERROR,
                           _("failed to add ip6tables rule to allow DNS requests from '%s'"),
                           network->def->bridge);
        goto err5;
    }

1184 1185 1186
    return 0;

    /* unwind in reverse order from the point of failure */
1187 1188 1189 1190
err5:
    iptablesRemoveTcpInput(driver->iptables, AF_INET6, network->def->bridge, 53);
err4:
    iptablesRemoveForwardAllowCross(driver->iptables, AF_INET6, network->def->bridge);
1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210
err3:
    iptablesRemoveForwardRejectIn(driver->iptables, AF_INET6, network->def->bridge);
err2:
    iptablesRemoveForwardRejectOut(driver->iptables, AF_INET6, network->def->bridge);
err1:
    return -1;
}

static void
networkRemoveGeneralIp6tablesRules(struct network_driver *driver,
                                  virNetworkObjPtr network)
{
    if (!virNetworkDefGetIpByIndex(network->def, AF_INET6, 0))
        return;

    iptablesRemoveForwardAllowCross(driver->iptables, AF_INET6, network->def->bridge);
    iptablesRemoveForwardRejectIn(driver->iptables, AF_INET6, network->def->bridge);
    iptablesRemoveForwardRejectOut(driver->iptables, AF_INET6, network->def->bridge);
}

1211
static int
1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225
networkAddGeneralIptablesRules(struct network_driver *driver,
                               virNetworkObjPtr network)
{
    int ii;
    virNetworkIpDefPtr ipv4def;

    /* First look for first IPv4 address that has dhcp or tftpboot defined. */
    /* We support dhcp config on 1 IPv4 interface only. */
    for (ii = 0;
         (ipv4def = virNetworkDefGetIpByIndex(network->def, AF_INET, ii));
         ii++) {
        if (ipv4def->nranges || ipv4def->nhosts || ipv4def->tftproot)
            break;
    }
1226 1227

    /* allow DHCP requests through to dnsmasq */
1228

1229 1230
    if (iptablesAddTcpInput(driver->iptables, AF_INET,
                            network->def->bridge, 67) < 0) {
1231 1232 1233
        networkReportError(VIR_ERR_SYSTEM_ERROR,
                           _("failed to add iptables rule to allow DHCP requests from '%s'"),
                           network->def->bridge);
1234 1235 1236
        goto err1;
    }

1237 1238
    if (iptablesAddUdpInput(driver->iptables, AF_INET,
                            network->def->bridge, 67) < 0) {
1239 1240 1241
        networkReportError(VIR_ERR_SYSTEM_ERROR,
                           _("failed to add iptables rule to allow DHCP requests from '%s'"),
                           network->def->bridge);
1242 1243 1244
        goto err2;
    }

1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255
    /* If we are doing local DHCP service on this network, attempt to
     * add a rule that will fixup the checksum of DHCP response
     * packets back to the guests (but report failure without
     * aborting, since not all iptables implementations support it).
     */

    if (ipv4def && (ipv4def->nranges || ipv4def->nhosts) &&
        (iptablesAddOutputFixUdpChecksum(driver->iptables,
                                         network->def->bridge, 68) < 0)) {
        VIR_WARN("Could not add rule to fixup DHCP response checksums "
                 "on network '%s'.", network->def->name);
1256
        VIR_WARN("May need to update iptables package & kernel to support CHECKSUM rule.");
1257 1258
    }

1259
    /* allow DNS requests through to dnsmasq */
1260 1261
    if (iptablesAddTcpInput(driver->iptables, AF_INET,
                            network->def->bridge, 53) < 0) {
1262 1263 1264
        networkReportError(VIR_ERR_SYSTEM_ERROR,
                           _("failed to add iptables rule to allow DNS requests from '%s'"),
                           network->def->bridge);
1265 1266 1267
        goto err3;
    }

1268 1269
    if (iptablesAddUdpInput(driver->iptables, AF_INET,
                            network->def->bridge, 53) < 0) {
1270 1271 1272
        networkReportError(VIR_ERR_SYSTEM_ERROR,
                           _("failed to add iptables rule to allow DNS requests from '%s'"),
                           network->def->bridge);
1273 1274 1275
        goto err4;
    }

1276 1277
    /* allow TFTP requests through to dnsmasq if necessary */
    if (ipv4def && ipv4def->tftproot &&
1278 1279
        iptablesAddUdpInput(driver->iptables, AF_INET,
                            network->def->bridge, 69) < 0) {
1280 1281 1282
        networkReportError(VIR_ERR_SYSTEM_ERROR,
                           _("failed to add iptables rule to allow TFTP requests from '%s'"),
                           network->def->bridge);
1283
        goto err5;
1284 1285
    }

1286 1287
    /* Catch all rules to block forwarding to/from bridges */

1288 1289
    if (iptablesAddForwardRejectOut(driver->iptables, AF_INET,
                                    network->def->bridge) < 0) {
1290 1291 1292
        networkReportError(VIR_ERR_SYSTEM_ERROR,
                           _("failed to add iptables rule to block outbound traffic from '%s'"),
                           network->def->bridge);
1293
        goto err6;
1294 1295
    }

1296 1297
    if (iptablesAddForwardRejectIn(driver->iptables, AF_INET,
                                   network->def->bridge) < 0) {
1298 1299 1300
        networkReportError(VIR_ERR_SYSTEM_ERROR,
                           _("failed to add iptables rule to block inbound traffic to '%s'"),
                           network->def->bridge);
1301
        goto err7;
1302 1303 1304
    }

    /* Allow traffic between guests on the same bridge */
1305 1306
    if (iptablesAddForwardAllowCross(driver->iptables, AF_INET,
                                     network->def->bridge) < 0) {
1307 1308 1309
        networkReportError(VIR_ERR_SYSTEM_ERROR,
                           _("failed to add iptables rule to allow cross bridge traffic on '%s'"),
                           network->def->bridge);
1310
        goto err8;
1311 1312
    }

1313 1314 1315 1316 1317
    /* add IPv6 general rules, if needed */
    if (networkAddGeneralIp6tablesRules(driver, network) < 0) {
        goto err9;
    }

1318
    return 0;
1319

1320
    /* unwind in reverse order from the point of failure */
1321 1322
err9:
    iptablesRemoveForwardAllowCross(driver->iptables, AF_INET, network->def->bridge);
1323
err8:
1324
    iptablesRemoveForwardRejectIn(driver->iptables, AF_INET, network->def->bridge);
1325
err7:
1326
    iptablesRemoveForwardRejectOut(driver->iptables, AF_INET, network->def->bridge);
1327 1328
err6:
    if (ipv4def && ipv4def->tftproot) {
1329
        iptablesRemoveUdpInput(driver->iptables, AF_INET, network->def->bridge, 69);
1330
    }
1331
err5:
1332
    iptablesRemoveUdpInput(driver->iptables, AF_INET, network->def->bridge, 53);
1333
err4:
1334
    iptablesRemoveTcpInput(driver->iptables, AF_INET, network->def->bridge, 53);
1335
err3:
1336
    iptablesRemoveUdpInput(driver->iptables, AF_INET, network->def->bridge, 67);
1337
err2:
1338
    iptablesRemoveTcpInput(driver->iptables, AF_INET, network->def->bridge, 67);
1339
err1:
1340
    return -1;
1341 1342 1343
}

static void
1344 1345 1346 1347 1348
networkRemoveGeneralIptablesRules(struct network_driver *driver,
                                  virNetworkObjPtr network)
{
    int ii;
    virNetworkIpDefPtr ipv4def;
1349

1350 1351
    networkRemoveGeneralIp6tablesRules(driver, network);

1352 1353 1354 1355 1356
    for (ii = 0;
         (ipv4def = virNetworkDefGetIpByIndex(network->def, AF_INET, ii));
         ii++) {
        if (ipv4def->nranges || ipv4def->nhosts || ipv4def->tftproot)
            break;
1357
    }
1358

1359 1360 1361
    iptablesRemoveForwardAllowCross(driver->iptables, AF_INET, network->def->bridge);
    iptablesRemoveForwardRejectIn(driver->iptables, AF_INET, network->def->bridge);
    iptablesRemoveForwardRejectOut(driver->iptables, AF_INET, network->def->bridge);
1362
    if (ipv4def && ipv4def->tftproot) {
1363
        iptablesRemoveUdpInput(driver->iptables, AF_INET, network->def->bridge, 69);
1364
    }
1365 1366
    iptablesRemoveUdpInput(driver->iptables, AF_INET, network->def->bridge, 53);
    iptablesRemoveTcpInput(driver->iptables, AF_INET, network->def->bridge, 53);
1367 1368 1369 1370
    if (ipv4def && (ipv4def->nranges || ipv4def->nhosts)) {
        iptablesRemoveOutputFixUdpChecksum(driver->iptables,
                                           network->def->bridge, 68);
    }
1371 1372
    iptablesRemoveUdpInput(driver->iptables, AF_INET, network->def->bridge, 67);
    iptablesRemoveTcpInput(driver->iptables, AF_INET, network->def->bridge, 67);
1373 1374
}

1375 1376 1377 1378 1379
static int
networkAddIpSpecificIptablesRules(struct network_driver *driver,
                                  virNetworkObjPtr network,
                                  virNetworkIpDefPtr ipdef)
{
1380 1381 1382
    /* NB: in the case of IPv6, routing rules are added when the
     * forward mode is NAT. This is because IPv6 has no NAT.
     */
1383

1384 1385 1386 1387 1388 1389 1390 1391
    if (network->def->forwardType == VIR_NETWORK_FORWARD_NAT) {
        if (VIR_SOCKET_IS_FAMILY(&ipdef->address, AF_INET))
            return networkAddMasqueradingIptablesRules(driver, network, ipdef);
        else if (VIR_SOCKET_IS_FAMILY(&ipdef->address, AF_INET6))
            return networkAddRoutingIptablesRules(driver, network, ipdef);
    } else if (network->def->forwardType == VIR_NETWORK_FORWARD_ROUTE) {
        return networkAddRoutingIptablesRules(driver, network, ipdef);
    }
1392 1393 1394 1395 1396 1397 1398 1399
    return 0;
}

static void
networkRemoveIpSpecificIptablesRules(struct network_driver *driver,
                                     virNetworkObjPtr network,
                                     virNetworkIpDefPtr ipdef)
{
1400 1401 1402 1403 1404 1405
    if (network->def->forwardType == VIR_NETWORK_FORWARD_NAT) {
        if (VIR_SOCKET_IS_FAMILY(&ipdef->address, AF_INET))
            networkRemoveMasqueradingIptablesRules(driver, network, ipdef);
        else if (VIR_SOCKET_IS_FAMILY(&ipdef->address, AF_INET6))
            networkRemoveRoutingIptablesRules(driver, network, ipdef);
    } else if (network->def->forwardType == VIR_NETWORK_FORWARD_ROUTE) {
1406
        networkRemoveRoutingIptablesRules(driver, network, ipdef);
1407
    }
1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460
}

/* Add all rules for all ip addresses (and general rules) on a network */
static int
networkAddIptablesRules(struct network_driver *driver,
                        virNetworkObjPtr network)
{
    int ii;
    virNetworkIpDefPtr ipdef;

    /* Add "once per network" rules */
    if (networkAddGeneralIptablesRules(driver, network) < 0)
        return -1;

    for (ii = 0;
         (ipdef = virNetworkDefGetIpByIndex(network->def, AF_UNSPEC, ii));
         ii++) {
        /* Add address-specific iptables rules */
        if (networkAddIpSpecificIptablesRules(driver, network, ipdef) < 0) {
            goto err;
        }
    }
    return 0;

err:
    /* The final failed call to networkAddIpSpecificIptablesRules will
     * have removed any rules it created, but we need to remove those
     * added for previous IP addresses.
     */
    while ((--ii >= 0) &&
           (ipdef = virNetworkDefGetIpByIndex(network->def, AF_UNSPEC, ii))) {
        networkRemoveIpSpecificIptablesRules(driver, network, ipdef);
    }
    networkRemoveGeneralIptablesRules(driver, network);
    return -1;
}

/* Remove all rules for all ip addresses (and general rules) on a network */
static void
networkRemoveIptablesRules(struct network_driver *driver,
                           virNetworkObjPtr network)
{
    int ii;
    virNetworkIpDefPtr ipdef;

    for (ii = 0;
         (ipdef = virNetworkDefGetIpByIndex(network->def, AF_UNSPEC, ii));
         ii++) {
        networkRemoveIpSpecificIptablesRules(driver, network, ipdef);
    }
    networkRemoveGeneralIptablesRules(driver, network);
}

1461 1462 1463 1464 1465
static void
networkReloadIptablesRules(struct network_driver *driver)
{
    unsigned int i;

1466
    VIR_INFO("Reloading iptables rules");
1467 1468 1469 1470

    for (i = 0 ; i < driver->networks.count ; i++) {
        virNetworkObjLock(driver->networks.objs[i]);
        if (virNetworkObjIsActive(driver->networks.objs[i])) {
1471 1472 1473 1474
            networkRemoveIptablesRules(driver, driver->networks.objs[i]);
            if (networkAddIptablesRules(driver, driver->networks.objs[i]) < 0) {
                /* failed to add but already logged */
            }
1475 1476 1477 1478 1479
        }
        virNetworkObjUnlock(driver->networks.objs[i]);
    }
}

1480
/* Enable IP Forwarding. Return 0 for success, -1 for failure. */
1481
static int
1482
networkEnableIpForwarding(bool enableIPv4, bool enableIPv6)
1483
{
1484 1485 1486 1487 1488 1489
    int ret = 0;
    if (enableIPv4)
        ret = virFileWriteStr("/proc/sys/net/ipv4/ip_forward", "1\n", 0);
    if (enableIPv6 && ret == 0)
        ret = virFileWriteStr("/proc/sys/net/ipv6/conf/all/forwarding", "1\n", 0);
    return ret;
1490 1491
}

1492 1493
#define SYSCTL_PATH "/proc/sys"

1494 1495
static int
networkSetIPv6Sysctls(virNetworkObjPtr network)
1496 1497 1498 1499
{
    char *field = NULL;
    int ret = -1;

1500 1501 1502 1503 1504 1505 1506 1507 1508
    if (!virNetworkDefGetIpByIndex(network->def, AF_INET6, 0)) {
        /* Only set disable_ipv6 if there are no ipv6 addresses defined for
         * the network.
         */
        if (virAsprintf(&field, SYSCTL_PATH "/net/ipv6/conf/%s/disable_ipv6",
                        network->def->bridge) < 0) {
            virReportOOMError();
            goto cleanup;
        }
1509

1510 1511 1512 1513 1514 1515
        if (access(field, W_OK) < 0 && errno == ENOENT) {
            VIR_DEBUG("ipv6 appears to already be disabled on %s",
                      network->def->bridge);
            ret = 0;
            goto cleanup;
        }
1516

1517 1518 1519 1520 1521 1522 1523
        if (virFileWriteStr(field, "1", 0) < 0) {
            virReportSystemError(errno,
                                 _("cannot write to %s to disable IPv6 on bridge %s"),
                                 field, network->def->bridge);
            goto cleanup;
        }
        VIR_FREE(field);
1524 1525
    }

1526 1527 1528 1529 1530 1531 1532 1533 1534
    /* The rest of the ipv6 sysctl tunables should always be set,
     * whether or not we're using ipv6 on this bridge.
     */

    /* Prevent guests from hijacking the host network by sending out
     * their own router advertisements.
     */
    if (virAsprintf(&field, SYSCTL_PATH "/net/ipv6/conf/%s/accept_ra",
                    network->def->bridge) < 0) {
1535
        virReportOOMError();
1536 1537 1538
        goto cleanup;
    }

1539
    if (virFileWriteStr(field, "0", 0) < 0) {
1540
        virReportSystemError(errno,
1541 1542 1543 1544 1545
                             _("cannot disable %s"), field);
        goto cleanup;
    }
    VIR_FREE(field);

1546 1547 1548 1549 1550
    /* All interfaces used as a gateway (which is what this is, by
     * definition), must always have autoconf=0.
     */
    if (virAsprintf(&field, SYSCTL_PATH "/net/ipv6/conf/%s/autoconf",
                    network->def->bridge) < 0) {
1551
        virReportOOMError();
1552 1553 1554
        goto cleanup;
    }

1555
    if (virFileWriteStr(field, "1", 0) < 0) {
1556
        virReportSystemError(errno,
1557 1558 1559 1560 1561 1562 1563 1564 1565 1566
                             _("cannot enable %s"), field);
        goto cleanup;
    }

    ret = 0;
cleanup:
    VIR_FREE(field);
    return ret;
}

1567 1568 1569 1570 1571 1572
#define PROC_NET_ROUTE "/proc/net/route"

/* XXX: This function can be a lot more exhaustive, there are certainly
 *      other scenarios where we can ruin host network connectivity.
 * XXX: Using a proper library is preferred over parsing /proc
 */
1573 1574
static int
networkCheckRouteCollision(virNetworkObjPtr network)
1575
{
1576
    int ret = 0, len;
1577 1578 1579 1580 1581
    char *cur, *buf = NULL;
    enum {MAX_ROUTE_SIZE = 1024*64};

    /* Read whole routing table into memory */
    if ((len = virFileReadAll(PROC_NET_ROUTE, MAX_ROUTE_SIZE, &buf)) < 0)
1582
        goto out;
1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600

    /* Dropping the last character shouldn't hurt */
    if (len > 0)
        buf[len-1] = '\0';

    VIR_DEBUG("%s output:\n%s", PROC_NET_ROUTE, buf);

    if (!STRPREFIX (buf, "Iface"))
        goto out;

    /* First line is just headings, skip it */
    cur = strchr(buf, '\n');
    if (cur)
        cur++;

    while (cur) {
        char iface[17], dest[128], mask[128];
        unsigned int addr_val, mask_val;
1601 1602
        virNetworkIpDefPtr ipdef;
        int num, ii;
1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630

        /* NUL-terminate the line, so sscanf doesn't go beyond a newline.  */
        char *nl = strchr(cur, '\n');
        if (nl) {
            *nl++ = '\0';
        }

        num = sscanf(cur, "%16s %127s %*s %*s %*s %*s %*s %127s",
                     iface, dest, mask);
        cur = nl;

        if (num != 3) {
            VIR_DEBUG("Failed to parse %s", PROC_NET_ROUTE);
            continue;
        }

        if (virStrToLong_ui(dest, NULL, 16, &addr_val) < 0) {
            VIR_DEBUG("Failed to convert network address %s to uint", dest);
            continue;
        }

        if (virStrToLong_ui(mask, NULL, 16, &mask_val) < 0) {
            VIR_DEBUG("Failed to convert network mask %s to uint", mask);
            continue;
        }

        addr_val &= mask_val;

1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654
        for (ii = 0;
             (ipdef = virNetworkDefGetIpByIndex(network->def, AF_INET, ii));
             ii++) {

            unsigned int net_dest;
            virSocketAddr netmask;

            if (virNetworkIpDefNetmask(ipdef, &netmask) < 0) {
                VIR_WARN("Failed to get netmask of '%s'",
                         network->def->bridge);
                continue;
            }

            net_dest = (ipdef->address.data.inet4.sin_addr.s_addr &
                        netmask.data.inet4.sin_addr.s_addr);

            if ((net_dest == addr_val) &&
                (netmask.data.inet4.sin_addr.s_addr == mask_val)) {
                networkReportError(VIR_ERR_INTERNAL_ERROR,
                                   _("Network is already in use by interface %s"),
                                   iface);
                ret = -1;
                goto out;
            }
1655 1656 1657 1658 1659 1660 1661 1662
        }
    }

out:
    VIR_FREE(buf);
    return ret;
}

1663 1664 1665 1666
static int
networkAddAddrToBridge(struct network_driver *driver,
                       virNetworkObjPtr network,
                       virNetworkIpDefPtr ipdef)
1667
{
1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692
    int prefix = virNetworkIpDefPrefix(ipdef);

    if (prefix < 0) {
        networkReportError(VIR_ERR_INTERNAL_ERROR,
                           _("bridge '%s' has an invalid netmask or IP address"),
                           network->def->bridge);
        return -1;
    }

    if (brAddInetAddress(driver->brctl, network->def->bridge,
                         &ipdef->address, prefix) < 0) {
        networkReportError(VIR_ERR_INTERNAL_ERROR,
                           _("cannot set IP address on bridge '%s'"),
                           network->def->bridge);
        return -1;
    }

    return 0;
}

static int
networkStartNetworkDaemon(struct network_driver *driver,
                          virNetworkObjPtr network)
{
    int ii, err;
1693
    bool v4present = false, v6present = false;
1694 1695
    virErrorPtr save_err = NULL;
    virNetworkIpDefPtr ipdef;
1696
    char *macTapIfName = NULL;
1697

D
Daniel P. Berrange 已提交
1698
    if (virNetworkObjIsActive(network)) {
1699
        networkReportError(VIR_ERR_OPERATION_INVALID,
1700
                           "%s", _("network is already active"));
1701 1702 1703
        return -1;
    }

1704 1705
    /* Check to see if any network IP collides with an existing route */
    if (networkCheckRouteCollision(network) < 0)
1706 1707
        return -1;

1708
    /* Create and configure the bridge device */
1709
    if ((err = brAddBridge(driver->brctl, network->def->bridge))) {
1710
        virReportSystemError(err,
1711 1712
                             _("cannot create bridge '%s'"),
                             network->def->bridge);
1713 1714 1715
        return -1;
    }

1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738
    if (network->def->mac_specified) {
        /* To set a mac for the bridge, we need to define a dummy tap
         * device, set its mac, then attach it to the bridge. As long
         * as its mac address is lower than any other interface that
         * gets attached, the bridge will always maintain this mac
         * address.
         */
        macTapIfName = networkBridgeDummyNicName(network->def->bridge);
        if (!macTapIfName) {
            virReportOOMError();
            goto err0;
        }
        if ((err = brAddTap(driver->brctl, network->def->bridge,
                            &macTapIfName, network->def->mac, 0, false, NULL))) {
            virReportSystemError(err,
                                 _("cannot create dummy tap device '%s' to set mac"
                                   " address on bridge '%s'"),
                                 macTapIfName, network->def->bridge);
            VIR_FREE(macTapIfName);
            goto err0;
        }
    }

1739
    /* Set bridge options */
E
Eric Blake 已提交
1740 1741
    if (brSetForwardDelay(driver->brctl, network->def->bridge,
                          network->def->delay)) {
1742 1743 1744
        networkReportError(VIR_ERR_INTERNAL_ERROR,
                           _("cannot set forward delay on bridge '%s'"),
                           network->def->bridge);
1745
        goto err1;
1746 1747
    }

E
Eric Blake 已提交
1748 1749
    if (brSetEnableSTP(driver->brctl, network->def->bridge,
                       network->def->stp ? 1 : 0)) {
1750
        networkReportError(VIR_ERR_INTERNAL_ERROR,
1751 1752
                           _("cannot set STP '%s' on bridge '%s'"),
                           network->def->stp ? "on" : "off", network->def->bridge);
1753
        goto err1;
1754 1755
    }

1756 1757 1758 1759
    /* Disable IPv6 on the bridge if there are no IPv6 addresses
     * defined, and set other IPv6 sysctl tunables appropriately.
     */
    if (networkSetIPv6Sysctls(network) < 0)
1760
        goto err1;
1761

1762 1763 1764 1765 1766 1767 1768 1769 1770
    /* Add "once per network" rules */
    if (networkAddIptablesRules(driver, network) < 0)
        goto err1;

    for (ii = 0;
         (ipdef = virNetworkDefGetIpByIndex(network->def, AF_UNSPEC, ii));
         ii++) {
        if (VIR_SOCKET_IS_FAMILY(&ipdef->address, AF_INET))
            v4present = true;
1771 1772
        if (VIR_SOCKET_IS_FAMILY(&ipdef->address, AF_INET6))
            v6present = true;
1773

1774 1775 1776
        /* Add the IP address/netmask to the bridge */
        if (networkAddAddrToBridge(driver, network, ipdef) < 0) {
            goto err2;
1777
        }
1778 1779
    }

1780
    /* Bring up the bridge interface */
1781
    if ((err = brSetInterfaceUp(driver->brctl, network->def->bridge, 1))) {
1782
        virReportSystemError(err,
1783 1784
                             _("failed to bring the bridge '%s' up"),
                             network->def->bridge);
1785
        goto err2;
1786 1787
    }

1788
    /* If forwardType != NONE, turn on global IP forwarding */
1789
    if (network->def->forwardType != VIR_NETWORK_FORWARD_NONE &&
1790
        networkEnableIpForwarding(v4present, v6present) < 0) {
1791
        virReportSystemError(errno, "%s",
1792
                             _("failed to enable IP forwarding"));
1793
        goto err3;
1794 1795
    }

1796

1797 1798
    /* start dnsmasq if there are any IP addresses (v4 or v6) */
    if ((v4present || v6present) && networkStartDhcpDaemon(network) < 0)
1799
        goto err3;
1800

1801 1802 1803 1804
    /* start radvd if there are any ipv6 addresses */
    if (v6present && networkStartRadvd(network) < 0)
        goto err4;

1805
    /* Persist the live configuration now we have bridge info  */
1806
    if (virNetworkSaveConfig(NETWORK_STATE_DIR, network->def) < 0) {
1807
        goto err5;
1808 1809
    }

1810
    VIR_FREE(macTapIfName);
1811
    VIR_INFO("Starting up network '%s'", network->def->name);
1812 1813 1814 1815
    network->active = 1;

    return 0;

1816 1817 1818 1819 1820 1821 1822 1823 1824
 err5:
    if (!save_err)
        save_err = virSaveLastError();

    if (network->radvdPid > 0) {
        kill(network->radvdPid, SIGTERM);
        network->radvdPid = -1;
    }

1825 1826 1827 1828
 err4:
    if (!save_err)
        save_err = virSaveLastError();

1829 1830 1831 1832 1833
    if (network->dnsmasqPid > 0) {
        kill(network->dnsmasqPid, SIGTERM);
        network->dnsmasqPid = -1;
    }

1834 1835 1836
 err3:
    if (!save_err)
        save_err = virSaveLastError();
1837
    if ((err = brSetInterfaceUp(driver->brctl, network->def->bridge, 0))) {
1838
        char ebuf[1024];
1839
        VIR_WARN("Failed to bring down bridge '%s' : %s",
1840
                 network->def->bridge, virStrerror(err, ebuf, sizeof ebuf));
1841 1842
    }

1843 1844 1845 1846 1847 1848
 err2:
    if (!save_err)
        save_err = virSaveLastError();
    networkRemoveIptablesRules(driver, network);

 err1:
1849 1850 1851 1852 1853 1854 1855 1856 1857
    if (!save_err)
        save_err = virSaveLastError();

    if ((err = brDeleteTap(driver->brctl, macTapIfName))) {
        char ebuf[1024];
        VIR_WARN("Failed to delete dummy tap device '%s' on bridge '%s' : %s",
                 macTapIfName, network->def->bridge,
                 virStrerror(err, ebuf, sizeof ebuf));
    }
1858
    VIR_FREE(macTapIfName);
1859 1860

 err0:
1861 1862
    if (!save_err)
        save_err = virSaveLastError();
1863
    if ((err = brDeleteBridge(driver->brctl, network->def->bridge))) {
1864
        char ebuf[1024];
1865
        VIR_WARN("Failed to delete bridge '%s' : %s",
1866
                 network->def->bridge, virStrerror(err, ebuf, sizeof ebuf));
1867 1868
    }

1869 1870 1871 1872
    if (save_err) {
        virSetError(save_err);
        virFreeError(save_err);
    }
1873 1874 1875 1876
    return -1;
}


1877 1878 1879
static int networkShutdownNetworkDaemon(struct network_driver *driver,
                                        virNetworkObjPtr network)
{
1880
    int err;
1881
    char *stateFile;
1882
    char *macTapIfName;
1883

1884
    VIR_INFO("Shutting down network '%s'", network->def->name);
1885

D
Daniel P. Berrange 已提交
1886
    if (!virNetworkObjIsActive(network))
1887 1888
        return 0;

1889
    stateFile = virNetworkConfigFile(NETWORK_STATE_DIR, network->def->name);
1890 1891 1892 1893 1894 1895
    if (!stateFile)
        return -1;

    unlink(stateFile);
    VIR_FREE(stateFile);

1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908
    if (network->radvdPid > 0) {
        char *radvdpidbase;

        kill(network->radvdPid, SIGTERM);
        /* attempt to delete the pidfile we created */
        if (!(radvdpidbase = networkRadvdPidfileBasename(network->def->name))) {
            virReportOOMError();
        } else {
            virFileDeletePid(NETWORK_PID_DIR, radvdpidbase);
            VIR_FREE(radvdpidbase);
        }
    }

1909 1910 1911
    if (network->dnsmasqPid > 0)
        kill(network->dnsmasqPid, SIGTERM);

1912
    char ebuf[1024];
1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927

    if (network->def->mac_specified) {
        macTapIfName = networkBridgeDummyNicName(network->def->bridge);
        if (!macTapIfName) {
            virReportOOMError();
        } else {
            if ((err = brDeleteTap(driver->brctl, macTapIfName))) {
                VIR_WARN("Failed to delete dummy tap device '%s' on bridge '%s' : %s",
                         macTapIfName, network->def->bridge,
                         virStrerror(err, ebuf, sizeof ebuf));
            }
            VIR_FREE(macTapIfName);
        }
    }

1928
    if ((err = brSetInterfaceUp(driver->brctl, network->def->bridge, 0))) {
1929
        VIR_WARN("Failed to bring down bridge '%s' : %s",
1930
                 network->def->bridge, virStrerror(err, ebuf, sizeof ebuf));
1931 1932
    }

1933 1934
    networkRemoveIptablesRules(driver, network);

1935
    if ((err = brDeleteBridge(driver->brctl, network->def->bridge))) {
1936
        VIR_WARN("Failed to delete bridge '%s' : %s",
1937
                 network->def->bridge, virStrerror(err, ebuf, sizeof ebuf));
1938 1939
    }

1940
    /* See if its still alive and really really kill it */
1941
    if (network->dnsmasqPid > 0 &&
1942
        (kill(network->dnsmasqPid, 0) == 0))
1943 1944
        kill(network->dnsmasqPid, SIGKILL);
    network->dnsmasqPid = -1;
1945 1946 1947 1948 1949 1950

    if (network->radvdPid > 0 &&
        (kill(network->radvdPid, 0) == 0))
        kill(network->radvdPid, SIGKILL);
    network->radvdPid = -1;

1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962
    network->active = 0;

    if (network->newDef) {
        virNetworkDefFree(network->def);
        network->def = network->newDef;
        network->newDef = NULL;
    }

    return 0;
}


1963 1964 1965 1966 1967
static virNetworkPtr networkLookupByUUID(virConnectPtr conn,
                                         const unsigned char *uuid) {
    struct network_driver *driver = conn->networkPrivateData;
    virNetworkObjPtr network;
    virNetworkPtr ret = NULL;
1968

1969
    networkDriverLock(driver);
1970
    network = virNetworkFindByUUID(&driver->networks, uuid);
1971
    networkDriverUnlock(driver);
1972
    if (!network) {
1973 1974
        networkReportError(VIR_ERR_NO_NETWORK,
                           "%s", _("no network with matching uuid"));
1975
        goto cleanup;
1976 1977
    }

1978 1979 1980
    ret = virGetNetwork(conn, network->def->name, network->def->uuid);

cleanup:
1981 1982
    if (network)
        virNetworkObjUnlock(network);
1983
    return ret;
1984 1985
}

1986 1987 1988 1989 1990 1991
static virNetworkPtr networkLookupByName(virConnectPtr conn,
                                         const char *name) {
    struct network_driver *driver = conn->networkPrivateData;
    virNetworkObjPtr network;
    virNetworkPtr ret = NULL;

1992
    networkDriverLock(driver);
1993
    network = virNetworkFindByName(&driver->networks, name);
1994
    networkDriverUnlock(driver);
1995
    if (!network) {
1996 1997
        networkReportError(VIR_ERR_NO_NETWORK,
                           _("no network with matching name '%s'"), name);
1998
        goto cleanup;
1999 2000
    }

2001 2002 2003
    ret = virGetNetwork(conn, network->def->name, network->def->uuid);

cleanup:
2004 2005
    if (network)
        virNetworkObjUnlock(network);
2006
    return ret;
2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024
}

static virDrvOpenStatus networkOpenNetwork(virConnectPtr conn,
                                           virConnectAuthPtr auth ATTRIBUTE_UNUSED,
                                           int flags ATTRIBUTE_UNUSED) {
    if (!driverState)
        return VIR_DRV_OPEN_DECLINED;

    conn->networkPrivateData = driverState;
    return VIR_DRV_OPEN_SUCCESS;
}

static int networkCloseNetwork(virConnectPtr conn) {
    conn->networkPrivateData = NULL;
    return 0;
}

static int networkNumNetworks(virConnectPtr conn) {
2025
    int nactive = 0, i;
2026
    struct network_driver *driver = conn->networkPrivateData;
2027

2028 2029 2030
    networkDriverLock(driver);
    for (i = 0 ; i < driver->networks.count ; i++) {
        virNetworkObjLock(driver->networks.objs[i]);
D
Daniel P. Berrange 已提交
2031
        if (virNetworkObjIsActive(driver->networks.objs[i]))
2032
            nactive++;
2033 2034 2035
        virNetworkObjUnlock(driver->networks.objs[i]);
    }
    networkDriverUnlock(driver);
2036

2037 2038 2039 2040
    return nactive;
}

static int networkListNetworks(virConnectPtr conn, char **const names, int nnames) {
2041
    struct network_driver *driver = conn->networkPrivateData;
2042
    int got = 0, i;
2043

2044
    networkDriverLock(driver);
2045
    for (i = 0 ; i < driver->networks.count && got < nnames ; i++) {
2046
        virNetworkObjLock(driver->networks.objs[i]);
D
Daniel P. Berrange 已提交
2047
        if (virNetworkObjIsActive(driver->networks.objs[i])) {
2048
            if (!(names[got] = strdup(driver->networks.objs[i]->def->name))) {
2049
                virNetworkObjUnlock(driver->networks.objs[i]);
2050
                virReportOOMError();
2051 2052 2053 2054
                goto cleanup;
            }
            got++;
        }
2055
        virNetworkObjUnlock(driver->networks.objs[i]);
2056
    }
2057 2058
    networkDriverUnlock(driver);

2059 2060 2061
    return got;

 cleanup:
2062
    networkDriverUnlock(driver);
2063 2064 2065 2066 2067 2068
    for (i = 0 ; i < got ; i++)
        VIR_FREE(names[i]);
    return -1;
}

static int networkNumDefinedNetworks(virConnectPtr conn) {
2069
    int ninactive = 0, i;
2070
    struct network_driver *driver = conn->networkPrivateData;
2071

2072 2073 2074
    networkDriverLock(driver);
    for (i = 0 ; i < driver->networks.count ; i++) {
        virNetworkObjLock(driver->networks.objs[i]);
D
Daniel P. Berrange 已提交
2075
        if (!virNetworkObjIsActive(driver->networks.objs[i]))
2076
            ninactive++;
2077 2078 2079
        virNetworkObjUnlock(driver->networks.objs[i]);
    }
    networkDriverUnlock(driver);
2080

2081 2082 2083 2084
    return ninactive;
}

static int networkListDefinedNetworks(virConnectPtr conn, char **const names, int nnames) {
2085
    struct network_driver *driver = conn->networkPrivateData;
2086
    int got = 0, i;
2087

2088
    networkDriverLock(driver);
2089
    for (i = 0 ; i < driver->networks.count && got < nnames ; i++) {
2090
        virNetworkObjLock(driver->networks.objs[i]);
D
Daniel P. Berrange 已提交
2091
        if (!virNetworkObjIsActive(driver->networks.objs[i])) {
2092
            if (!(names[got] = strdup(driver->networks.objs[i]->def->name))) {
2093
                virNetworkObjUnlock(driver->networks.objs[i]);
2094
                virReportOOMError();
2095 2096 2097 2098
                goto cleanup;
            }
            got++;
        }
2099
        virNetworkObjUnlock(driver->networks.objs[i]);
2100
    }
2101
    networkDriverUnlock(driver);
2102 2103 2104
    return got;

 cleanup:
2105
    networkDriverUnlock(driver);
2106 2107 2108 2109 2110
    for (i = 0 ; i < got ; i++)
        VIR_FREE(names[i]);
    return -1;
}

2111 2112 2113

static int networkIsActive(virNetworkPtr net)
{
2114
    struct network_driver *driver = net->conn->networkPrivateData;
2115 2116 2117 2118 2119 2120 2121
    virNetworkObjPtr obj;
    int ret = -1;

    networkDriverLock(driver);
    obj = virNetworkFindByUUID(&driver->networks, net->uuid);
    networkDriverUnlock(driver);
    if (!obj) {
2122
        networkReportError(VIR_ERR_NO_NETWORK, NULL);
2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134
        goto cleanup;
    }
    ret = virNetworkObjIsActive(obj);

cleanup:
    if (obj)
        virNetworkObjUnlock(obj);
    return ret;
}

static int networkIsPersistent(virNetworkPtr net)
{
2135
    struct network_driver *driver = net->conn->networkPrivateData;
2136 2137 2138 2139 2140 2141 2142
    virNetworkObjPtr obj;
    int ret = -1;

    networkDriverLock(driver);
    obj = virNetworkFindByUUID(&driver->networks, net->uuid);
    networkDriverUnlock(driver);
    if (!obj) {
2143
        networkReportError(VIR_ERR_NO_NETWORK, NULL);
2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154
        goto cleanup;
    }
    ret = obj->persistent;

cleanup:
    if (obj)
        virNetworkObjUnlock(obj);
    return ret;
}


2155
static virNetworkPtr networkCreate(virConnectPtr conn, const char *xml) {
2156
    struct network_driver *driver = conn->networkPrivateData;
2157
    virNetworkDefPtr def;
2158
    virNetworkObjPtr network = NULL;
2159
    virNetworkPtr ret = NULL;
2160

2161 2162
    networkDriverLock(driver);

2163
    if (!(def = virNetworkDefParseString(xml)))
2164
        goto cleanup;
2165

2166 2167 2168
    if (virNetworkObjIsDuplicate(&driver->networks, def, 1) < 0)
        goto cleanup;

2169
    if (virNetworkSetBridgeName(&driver->networks, def, 1))
2170 2171
        goto cleanup;

2172 2173
    virNetworkSetBridgeMacAddr(def);

2174
    if (!(network = virNetworkAssignDef(&driver->networks,
2175 2176 2177
                                        def)))
        goto cleanup;
    def = NULL;
2178

2179
    if (networkStartNetworkDaemon(driver, network) < 0) {
2180 2181
        virNetworkRemoveInactive(&driver->networks,
                                 network);
2182
        network = NULL;
2183
        goto cleanup;
2184 2185
    }

2186
    VIR_INFO("Creating network '%s'", network->def->name);
2187 2188 2189 2190
    ret = virGetNetwork(conn, network->def->name, network->def->uuid);

cleanup:
    virNetworkDefFree(def);
2191 2192 2193
    if (network)
        virNetworkObjUnlock(network);
    networkDriverUnlock(driver);
2194
    return ret;
2195 2196 2197
}

static virNetworkPtr networkDefine(virConnectPtr conn, const char *xml) {
2198
    struct network_driver *driver = conn->networkPrivateData;
2199
    virNetworkIpDefPtr ipdef, ipv4def = NULL;
2200
    virNetworkDefPtr def;
2201
    virNetworkObjPtr network = NULL;
2202
    virNetworkPtr ret = NULL;
2203
    int ii;
2204
    dnsmasqContext* dctx = NULL;
2205

2206 2207
    networkDriverLock(driver);

2208
    if (!(def = virNetworkDefParseString(xml)))
2209
        goto cleanup;
2210

E
Eric Blake 已提交
2211
    if (virNetworkObjIsDuplicate(&driver->networks, def, 0) < 0)
2212 2213
        goto cleanup;

2214
    if (virNetworkSetBridgeName(&driver->networks, def, 1))
2215 2216
        goto cleanup;

2217 2218
    virNetworkSetBridgeMacAddr(def);

2219
    if (!(network = virNetworkAssignDef(&driver->networks,
2220 2221 2222
                                        def)))
        goto cleanup;
    def = NULL;
2223

2224 2225
    network->persistent = 1;

2226
    if (virNetworkSaveConfig(driver->networkConfigDir,
2227
                             network->newDef ? network->newDef : network->def) < 0) {
2228 2229
        virNetworkRemoveInactive(&driver->networks,
                                 network);
2230
        network = NULL;
2231
        goto cleanup;
2232 2233
    }

2234
    /* We only support dhcp on one IPv4 address per defined network */
2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250
    for (ii = 0;
         (ipdef = virNetworkDefGetIpByIndex(network->def, AF_UNSPEC, ii));
         ii++) {
        if (VIR_SOCKET_IS_FAMILY(&ipdef->address, AF_INET)) {
            if (ipdef->nranges || ipdef->nhosts) {
                if (ipv4def) {
                    networkReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                                       "%s", _("Multiple dhcp sections found. dhcp is supported only for a single IPv4 address on each network"));
                    goto cleanup;
                } else {
                    ipv4def = ipdef;
                }
            }
        }
    }
    if (ipv4def) {
2251 2252 2253 2254
        dctx = dnsmasqContextNew(network->def->name, DNSMASQ_STATE_DIR);
        if (dctx == NULL ||
            networkBuildDnsmasqHostsfile(dctx, ipv4def, network->def->dns) < 0 ||
            dnsmasqSave(dctx) < 0)
2255 2256 2257
            goto cleanup;
    }

2258
    VIR_INFO("Defining network '%s'", network->def->name);
2259 2260 2261 2262
    ret = virGetNetwork(conn, network->def->name, network->def->uuid);

cleanup:
    virNetworkDefFree(def);
2263
    dnsmasqContextFree(dctx);
2264 2265 2266
    if (network)
        virNetworkObjUnlock(network);
    networkDriverUnlock(driver);
2267
    return ret;
2268 2269 2270
}

static int networkUndefine(virNetworkPtr net) {
2271
    struct network_driver *driver = net->conn->networkPrivateData;
2272
    virNetworkObjPtr network;
2273 2274
    virNetworkIpDefPtr ipdef;
    bool dhcp_present = false, v6present = false;
2275
    int ret = -1, ii;
2276

2277 2278
    networkDriverLock(driver);

2279
    network = virNetworkFindByUUID(&driver->networks, net->uuid);
2280
    if (!network) {
2281
        networkReportError(VIR_ERR_NO_NETWORK,
2282 2283
                           "%s", _("no network with matching uuid"));
        goto cleanup;
2284 2285
    }

D
Daniel P. Berrange 已提交
2286
    if (virNetworkObjIsActive(network)) {
2287
        networkReportError(VIR_ERR_OPERATION_INVALID,
2288 2289
                           "%s", _("network is still active"));
        goto cleanup;
2290 2291
    }

2292
    if (virNetworkDeleteConfig(driver->networkConfigDir,
2293 2294
                               driver->networkAutostartDir,
                               network) < 0)
2295
        goto cleanup;
2296

2297 2298
    /* we only support dhcp on one IPv4 address per defined network */
    for (ii = 0;
2299
         (ipdef = virNetworkDefGetIpByIndex(network->def, AF_UNSPEC, ii));
2300
         ii++) {
2301 2302 2303 2304 2305 2306
        if (VIR_SOCKET_IS_FAMILY(&ipdef->address, AF_INET)) {
            if (ipdef->nranges || ipdef->nhosts)
                dhcp_present = true;
        } else if (VIR_SOCKET_IS_FAMILY(&ipdef->address, AF_INET6)) {
            v6present = true;
        }
2307
    }
2308 2309

    if (dhcp_present) {
2310
        char *leasefile;
2311 2312 2313 2314 2315 2316
        dnsmasqContext *dctx = dnsmasqContextNew(network->def->name, DNSMASQ_STATE_DIR);
        if (dctx == NULL)
            goto cleanup;

        dnsmasqDelete(dctx);
        dnsmasqContextFree(dctx);
2317 2318 2319 2320 2321 2322

        leasefile = networkDnsmasqLeaseFileName(network->def->name);
        if (!leasefile)
            goto cleanup;
        unlink(leasefile);
        VIR_FREE(leasefile);
2323 2324
    }

2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345
    if (v6present) {
        char *configfile = networkRadvdConfigFileName(network->def->name);

        if (!configfile) {
            virReportOOMError();
            goto cleanup;
        }
        unlink(configfile);
        VIR_FREE(configfile);

        char *radvdpidbase = networkRadvdPidfileBasename(network->def->name);

        if (!(radvdpidbase)) {
            virReportOOMError();
            goto cleanup;
        }
        virFileDeletePid(NETWORK_PID_DIR, radvdpidbase);
        VIR_FREE(radvdpidbase);

    }

2346
    VIR_INFO("Undefining network '%s'", network->def->name);
2347 2348
    virNetworkRemoveInactive(&driver->networks,
                             network);
2349
    network = NULL;
2350
    ret = 0;
2351

2352
cleanup:
2353 2354 2355
    if (network)
        virNetworkObjUnlock(network);
    networkDriverUnlock(driver);
2356
    return ret;
2357 2358 2359
}

static int networkStart(virNetworkPtr net) {
2360 2361 2362
    struct network_driver *driver = net->conn->networkPrivateData;
    virNetworkObjPtr network;
    int ret = -1;
2363

2364
    networkDriverLock(driver);
2365
    network = virNetworkFindByUUID(&driver->networks, net->uuid);
2366

2367
    if (!network) {
2368
        networkReportError(VIR_ERR_NO_NETWORK,
2369 2370
                           "%s", _("no network with matching uuid"));
        goto cleanup;
2371 2372
    }

2373
    ret = networkStartNetworkDaemon(driver, network);
2374 2375

cleanup:
2376 2377
    if (network)
        virNetworkObjUnlock(network);
2378
    networkDriverUnlock(driver);
2379
    return ret;
2380 2381 2382
}

static int networkDestroy(virNetworkPtr net) {
2383 2384 2385
    struct network_driver *driver = net->conn->networkPrivateData;
    virNetworkObjPtr network;
    int ret = -1;
2386

2387
    networkDriverLock(driver);
2388
    network = virNetworkFindByUUID(&driver->networks, net->uuid);
2389

2390
    if (!network) {
2391
        networkReportError(VIR_ERR_NO_NETWORK,
2392 2393
                           "%s", _("no network with matching uuid"));
        goto cleanup;
2394 2395
    }

D
Daniel P. Berrange 已提交
2396
    if (!virNetworkObjIsActive(network)) {
2397
        networkReportError(VIR_ERR_OPERATION_INVALID,
2398 2399 2400 2401
                           "%s", _("network is not active"));
        goto cleanup;
    }

2402
    ret = networkShutdownNetworkDaemon(driver, network);
2403
    if (!network->persistent) {
2404 2405 2406 2407
        virNetworkRemoveInactive(&driver->networks,
                                 network);
        network = NULL;
    }
2408

2409
cleanup:
2410 2411
    if (network)
        virNetworkObjUnlock(network);
2412
    networkDriverUnlock(driver);
2413 2414 2415
    return ret;
}

2416
static char *networkGetXMLDesc(virNetworkPtr net, int flags ATTRIBUTE_UNUSED) {
2417 2418 2419
    struct network_driver *driver = net->conn->networkPrivateData;
    virNetworkObjPtr network;
    char *ret = NULL;
2420

2421
    networkDriverLock(driver);
2422
    network = virNetworkFindByUUID(&driver->networks, net->uuid);
2423 2424
    networkDriverUnlock(driver);

2425
    if (!network) {
2426
        networkReportError(VIR_ERR_NO_NETWORK,
2427 2428
                           "%s", _("no network with matching uuid"));
        goto cleanup;
2429 2430
    }

2431
    ret = virNetworkDefFormat(network->def);
2432 2433

cleanup:
2434 2435
    if (network)
        virNetworkObjUnlock(network);
2436
    return ret;
2437 2438 2439
}

static char *networkGetBridgeName(virNetworkPtr net) {
2440 2441 2442 2443
    struct network_driver *driver = net->conn->networkPrivateData;
    virNetworkObjPtr network;
    char *bridge = NULL;

2444
    networkDriverLock(driver);
2445
    network = virNetworkFindByUUID(&driver->networks, net->uuid);
2446 2447
    networkDriverUnlock(driver);

2448
    if (!network) {
2449
        networkReportError(VIR_ERR_NO_NETWORK,
2450 2451
                           "%s", _("no network with matching id"));
        goto cleanup;
2452 2453
    }

2454
    if (!(network->def->bridge)) {
2455
        networkReportError(VIR_ERR_INTERNAL_ERROR,
2456 2457 2458 2459 2460
                           _("network '%s' does not have a bridge name."),
                           network->def->name);
        goto cleanup;
    }

2461
    bridge = strdup(network->def->bridge);
2462
    if (!bridge)
2463
        virReportOOMError();
2464 2465

cleanup:
2466 2467
    if (network)
        virNetworkObjUnlock(network);
2468 2469 2470 2471 2472
    return bridge;
}

static int networkGetAutostart(virNetworkPtr net,
                             int *autostart) {
2473 2474 2475
    struct network_driver *driver = net->conn->networkPrivateData;
    virNetworkObjPtr network;
    int ret = -1;
2476

2477
    networkDriverLock(driver);
2478
    network = virNetworkFindByUUID(&driver->networks, net->uuid);
2479
    networkDriverUnlock(driver);
2480
    if (!network) {
2481
        networkReportError(VIR_ERR_NO_NETWORK,
2482
                           "%s", _("no network with matching uuid"));
2483
        goto cleanup;
2484 2485 2486
    }

    *autostart = network->autostart;
2487
    ret = 0;
2488

2489
cleanup:
2490 2491
    if (network)
        virNetworkObjUnlock(network);
2492
    return ret;
2493 2494 2495
}

static int networkSetAutostart(virNetworkPtr net,
2496
                               int autostart) {
2497 2498
    struct network_driver *driver = net->conn->networkPrivateData;
    virNetworkObjPtr network;
2499
    char *configFile = NULL, *autostartLink = NULL;
2500
    int ret = -1;
2501

2502
    networkDriverLock(driver);
2503
    network = virNetworkFindByUUID(&driver->networks, net->uuid);
2504

2505
    if (!network) {
2506
        networkReportError(VIR_ERR_NO_NETWORK,
2507
                           "%s", _("no network with matching uuid"));
2508
        goto cleanup;
2509 2510
    }

2511
    if (!network->persistent) {
2512
        networkReportError(VIR_ERR_OPERATION_INVALID,
2513
                           "%s", _("cannot set autostart for transient network"));
2514 2515 2516
        goto cleanup;
    }

2517 2518
    autostart = (autostart != 0);

2519
    if (network->autostart != autostart) {
2520
        if ((configFile = virNetworkConfigFile(driver->networkConfigDir, network->def->name)) == NULL)
2521
            goto cleanup;
2522
        if ((autostartLink = virNetworkConfigFile(driver->networkAutostartDir, network->def->name)) == NULL)
2523 2524
            goto cleanup;

2525
        if (autostart) {
2526
            if (virFileMakePath(driver->networkAutostartDir)) {
2527
                virReportSystemError(errno,
2528 2529
                                     _("cannot create autostart directory '%s'"),
                                     driver->networkAutostartDir);
2530 2531
                goto cleanup;
            }
2532

2533
            if (symlink(configFile, autostartLink) < 0) {
2534
                virReportSystemError(errno,
2535
                                     _("Failed to create symlink '%s' to '%s'"),
2536
                                     autostartLink, configFile);
2537 2538 2539
                goto cleanup;
            }
        } else {
2540
            if (unlink(autostartLink) < 0 && errno != ENOENT && errno != ENOTDIR) {
2541
                virReportSystemError(errno,
2542
                                     _("Failed to delete symlink '%s'"),
2543
                                     autostartLink);
2544 2545
                goto cleanup;
            }
2546 2547
        }

2548
        network->autostart = autostart;
2549
    }
2550
    ret = 0;
2551

2552
cleanup:
2553 2554
    VIR_FREE(configFile);
    VIR_FREE(autostartLink);
2555 2556
    if (network)
        virNetworkObjUnlock(network);
2557
    networkDriverUnlock(driver);
2558
    return ret;
2559 2560 2561 2562 2563
}


static virNetworkDriver networkDriver = {
    "Network",
2564 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582
    .open = networkOpenNetwork, /* 0.2.0 */
    .close = networkCloseNetwork, /* 0.2.0 */
    .numOfNetworks = networkNumNetworks, /* 0.2.0 */
    .listNetworks = networkListNetworks, /* 0.2.0 */
    .numOfDefinedNetworks = networkNumDefinedNetworks, /* 0.2.0 */
    .listDefinedNetworks = networkListDefinedNetworks, /* 0.2.0 */
    .networkLookupByUUID = networkLookupByUUID, /* 0.2.0 */
    .networkLookupByName = networkLookupByName, /* 0.2.0 */
    .networkCreateXML = networkCreate, /* 0.2.0 */
    .networkDefineXML = networkDefine, /* 0.2.0 */
    .networkUndefine = networkUndefine, /* 0.2.0 */
    .networkCreate = networkStart, /* 0.2.0 */
    .networkDestroy = networkDestroy, /* 0.2.0 */
    .networkGetXMLDesc = networkGetXMLDesc, /* 0.2.0 */
    .networkGetBridgeName = networkGetBridgeName, /* 0.2.0 */
    .networkGetAutostart = networkGetAutostart, /* 0.2.1 */
    .networkSetAutostart = networkSetAutostart, /* 0.2.1 */
    .networkIsActive = networkIsActive, /* 0.7.3 */
    .networkIsPersistent = networkIsPersistent, /* 0.7.3 */
2583 2584 2585
};

static virStateDriver networkStateDriver = {
2586
    "Network",
2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597
    networkStartup,
    networkShutdown,
    networkReload,
    networkActive,
};

int networkRegister(void) {
    virRegisterNetworkDriver(&networkDriver);
    virRegisterStateDriver(&networkStateDriver);
    return 0;
}