network_conf.c 28.2 KB
Newer Older
1 2 3
/*
 * network_conf.c: network XML handling
 *
4
 * Copyright (C) 2006-2010 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
 * Copyright (C) 2006-2008 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>

28
#include <unistd.h>
29 30
#include <arpa/inet.h>
#include <sys/types.h>
A
Atsushi SAKAI 已提交
31
#include <sys/stat.h>
32
#include <fcntl.h>
33
#include <string.h>
34 35
#include <dirent.h>

36
#include "virterror_internal.h"
37
#include "datatypes.h"
38
#include "network_conf.h"
39
#include "network.h"
40 41 42 43 44
#include "memory.h"
#include "xml.h"
#include "uuid.h"
#include "util.h"
#include "buf.h"
45
#include "c-ctype.h"
46
#include "files.h"
47

48
#define MAX_BRIDGE_ID 256
49 50
#define VIR_FROM_THIS VIR_FROM_NETWORK

51 52 53 54 55 56
VIR_ENUM_DECL(virNetworkForward)

VIR_ENUM_IMPL(virNetworkForward,
              VIR_NETWORK_FORWARD_LAST,
              "none", "nat", "route" )

57
#define virNetworkReportError(code, ...)                                \
58
    virReportErrorHelper(NULL, VIR_FROM_NETWORK, code, __FILE__,        \
59
                         __FUNCTION__, __LINE__, __VA_ARGS__)
60

61
virNetworkObjPtr virNetworkFindByUUID(const virNetworkObjListPtr nets,
62 63
                                      const unsigned char *uuid)
{
64 65
    unsigned int i;

66 67
    for (i = 0 ; i < nets->count ; i++) {
        virNetworkObjLock(nets->objs[i]);
68 69
        if (!memcmp(nets->objs[i]->def->uuid, uuid, VIR_UUID_BUFLEN))
            return nets->objs[i];
70 71
        virNetworkObjUnlock(nets->objs[i]);
    }
72 73 74 75

    return NULL;
}

76
virNetworkObjPtr virNetworkFindByName(const virNetworkObjListPtr nets,
77 78
                                      const char *name)
{
79 80
    unsigned int i;

81 82
    for (i = 0 ; i < nets->count ; i++) {
        virNetworkObjLock(nets->objs[i]);
83 84
        if (STREQ(nets->objs[i]->def->name, name))
            return nets->objs[i];
85 86
        virNetworkObjUnlock(nets->objs[i]);
    }
87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104

    return NULL;
}


void virNetworkDefFree(virNetworkDefPtr def)
{
    int i;

    if (!def)
        return;

    VIR_FREE(def->name);
    VIR_FREE(def->bridge);
    VIR_FREE(def->forwardDev);
    VIR_FREE(def->ipAddress);
    VIR_FREE(def->network);
    VIR_FREE(def->netmask);
105
    VIR_FREE(def->domain);
106 107 108 109 110 111 112

    for (i = 0 ; i < def->nranges && def->ranges ; i++) {
        VIR_FREE(def->ranges[i].start);
        VIR_FREE(def->ranges[i].end);
    }
    VIR_FREE(def->ranges);

113 114 115 116 117 118 119
    for (i = 0 ; i < def->nhosts && def->hosts ; i++) {
        VIR_FREE(def->hosts[i].mac);
        VIR_FREE(def->hosts[i].ip);
        VIR_FREE(def->hosts[i].name);
    }
    VIR_FREE(def->hosts);

120 121
    VIR_FREE(def->tftproot);
    VIR_FREE(def->bootfile);
122
    VIR_FREE(def->bootserver);
123

124 125 126 127 128 129 130 131 132 133 134
    VIR_FREE(def);
}

void virNetworkObjFree(virNetworkObjPtr net)
{
    if (!net)
        return;

    virNetworkDefFree(net->def);
    virNetworkDefFree(net->newDef);

135 136
    virMutexDestroy(&net->lock);

137 138 139
    VIR_FREE(net);
}

140 141 142 143 144 145 146 147 148 149 150
void virNetworkObjListFree(virNetworkObjListPtr nets)
{
    unsigned int i;

    for (i = 0 ; i < nets->count ; i++)
        virNetworkObjFree(nets->objs[i]);

    VIR_FREE(nets->objs);
    nets->count = 0;
}

151
virNetworkObjPtr virNetworkAssignDef(virNetworkObjListPtr nets,
152 153 154 155
                                     const virNetworkDefPtr def)
{
    virNetworkObjPtr network;

156
    if ((network = virNetworkFindByName(nets, def->name))) {
D
Daniel P. Berrange 已提交
157
        if (!virNetworkObjIsActive(network)) {
158 159 160
            virNetworkDefFree(network->def);
            network->def = def;
        } else {
161
            virNetworkDefFree(network->newDef);
162 163 164 165 166 167 168
            network->newDef = def;
        }

        return network;
    }

    if (VIR_ALLOC(network) < 0) {
169
        virReportOOMError();
170 171
        return NULL;
    }
172
    if (virMutexInit(&network->lock) < 0) {
173
        virNetworkReportError(VIR_ERR_INTERNAL_ERROR,
174 175 176 177
                              "%s", _("cannot initialize mutex"));
        VIR_FREE(network);
        return NULL;
    }
178
    virNetworkObjLock(network);
179 180
    network->def = def;

181
    if (VIR_REALLOC_N(nets->objs, nets->count + 1) < 0) {
182
        virReportOOMError();
183 184 185 186 187 188
        VIR_FREE(network);
        return NULL;
    }

    nets->objs[nets->count] = network;
    nets->count++;
189 190 191 192 193

    return network;

}

194
void virNetworkRemoveInactive(virNetworkObjListPtr nets,
195 196
                              const virNetworkObjPtr net)
{
197
    unsigned int i;
198

199
    virNetworkObjUnlock(net);
200
    for (i = 0 ; i < nets->count ; i++) {
201
        virNetworkObjLock(nets->objs[i]);
202
        if (nets->objs[i] == net) {
203
            virNetworkObjUnlock(nets->objs[i]);
204
            virNetworkObjFree(nets->objs[i]);
205

206 207 208
            if (i < (nets->count - 1))
                memmove(nets->objs + i, nets->objs + i + 1,
                        sizeof(*(nets->objs)) * (nets->count - (i + 1)));
209

210 211 212 213 214 215 216
            if (VIR_REALLOC_N(nets->objs, nets->count - 1) < 0) {
                ; /* Failure to reduce memory allocation isn't fatal */
            }
            nets->count--;

            break;
        }
217
        virNetworkObjUnlock(nets->objs[i]);
218
    }
219 220 221 222
}


static int
223
virNetworkDHCPRangeDefParseXML(virNetworkDefPtr def,
224 225 226 227 228 229
                               xmlNodePtr node) {

    xmlNodePtr cur;

    cur = node->children;
    while (cur != NULL) {
230 231
        if (cur->type == XML_ELEMENT_NODE &&
            xmlStrEqual(cur->name, BAD_CAST "range")) {
232 233 234
            char *start, *end;
            virSocketAddr saddr, eaddr;
            int range;
235

236
            if (!(start = (char *) xmlGetProp(cur, BAD_CAST "start"))) {
237 238 239
                cur = cur->next;
                continue;
            }
240 241
            if (!(end = (char *) xmlGetProp(cur, BAD_CAST "end"))) {
                xmlFree(start);
242
                cur = cur->next;
243 244 245
                continue;
            }

246
            if (virSocketParseAddr(start, &saddr, AF_UNSPEC) < 0) {
247 248
                xmlFree(start);
                xmlFree(end);
249
                return -1;
250
            }
251
            if (virSocketParseAddr(end, &eaddr, AF_UNSPEC) < 0) {
252
                xmlFree(start);
253
                xmlFree(end);
254
                return -1;
255 256 257 258
            }

            range = virSocketGetRange(&saddr, &eaddr);
            if (range < 0) {
259
                virNetworkReportError(VIR_ERR_XML_ERROR,
260 261 262 263
                                      _("dhcp range '%s' to '%s' invalid"),
                                      start, end);
                xmlFree(start);
                xmlFree(end);
264
                return -1;
265
            }
266

267 268 269
            if (VIR_REALLOC_N(def->ranges, def->nranges + 1) < 0) {
                xmlFree(start);
                xmlFree(end);
270
                virReportOOMError();
271 272 273 274
                return -1;
            }
            def->ranges[def->nranges].start = (char *)start;
            def->ranges[def->nranges].end = (char *)end;
275
            def->ranges[def->nranges].size = range;
276 277 278 279 280 281 282 283 284 285
            def->nranges++;
        } else if (cur->type == XML_ELEMENT_NODE &&
            xmlStrEqual(cur->name, BAD_CAST "host")) {
            xmlChar *mac, *name, *ip;
            unsigned char addr[6];
            struct in_addr inaddress;

            mac = xmlGetProp(cur, BAD_CAST "mac");
            if ((mac != NULL) &&
                (virParseMacAddr((const char *) mac, &addr[0]) != 0)) {
286
                virNetworkReportError(VIR_ERR_INTERNAL_ERROR,
287 288 289 290 291 292
                                      _("cannot parse MAC address '%s'"),
                                      mac);
                VIR_FREE(mac);
            }
            name = xmlGetProp(cur, BAD_CAST "name");
            if ((name != NULL) && (!c_isalpha(name[0]))) {
293
                virNetworkReportError(VIR_ERR_INTERNAL_ERROR,
294 295 296 297 298 299 300 301 302 303 304 305 306 307 308
                                      _("cannot use name address '%s'"),
                                      name);
                VIR_FREE(name);
            }
            /*
             * You need at least one MAC address or one host name
             */
            if ((mac == NULL) && (name == NULL)) {
                VIR_FREE(mac);
                VIR_FREE(name);
                cur = cur->next;
                continue;
            }
            ip = xmlGetProp(cur, BAD_CAST "ip");
            if (inet_pton(AF_INET, (const char *) ip, &inaddress) <= 0) {
309
                virNetworkReportError(VIR_ERR_INTERNAL_ERROR,
310 311 312 313 314 315 316 317 318 319 320 321
                                      _("cannot parse IP address '%s'"),
                                      ip);
                VIR_FREE(ip);
                VIR_FREE(mac);
                VIR_FREE(name);
                cur = cur->next;
                continue;
            }
            if (VIR_REALLOC_N(def->hosts, def->nhosts + 1) < 0) {
                VIR_FREE(ip);
                VIR_FREE(mac);
                VIR_FREE(name);
322
                virReportOOMError();
323 324 325 326 327 328
                return -1;
            }
            def->hosts[def->nhosts].mac = (char *)mac;
            def->hosts[def->nhosts].name = (char *)name;
            def->hosts[def->nhosts].ip = (char *)ip;
            def->nhosts++;
329 330 331 332 333 334 335 336 337 338 339

        } else if (cur->type == XML_ELEMENT_NODE &&
            xmlStrEqual(cur->name, BAD_CAST "bootp")) {
            xmlChar *file;

            if (!(file = xmlGetProp(cur, BAD_CAST "file"))) {
                cur = cur->next;
                continue;
            }

            def->bootfile = (char *)file;
340
            def->bootserver = (char *) xmlGetProp(cur, BAD_CAST "server");
341 342 343 344 345 346 347 348
        }

        cur = cur->next;
    }

    return 0;
}

349
static int
350
virNetworkIPParseXML(virNetworkDefPtr def,
351 352 353 354 355 356 357
                     xmlNodePtr node) {
    xmlNodePtr cur;

    cur = node->children;
    while (cur != NULL) {
        if (cur->type == XML_ELEMENT_NODE &&
            xmlStrEqual(cur->name, BAD_CAST "dhcp")) {
358
            int result = virNetworkDHCPRangeDefParseXML(def, cur);
359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378
            if (result)
                return result;

        } else if (cur->type == XML_ELEMENT_NODE &&
            xmlStrEqual(cur->name, BAD_CAST "tftp")) {
            xmlChar *root;

            if (!(root = xmlGetProp(cur, BAD_CAST "root"))) {
                cur = cur->next;
                continue;
            }

            def->tftproot = (char *)root;
        }

        cur = cur->next;
    }
    return 0;
}

379
static virNetworkDefPtr
380
virNetworkDefParseXML(xmlXPathContextPtr ctxt)
381 382 383 384 385
{
    virNetworkDefPtr def;
    char *tmp;

    if (VIR_ALLOC(def) < 0) {
386
        virReportOOMError();
387 388 389 390
        return NULL;
    }

    /* Extract network name */
391
    def->name = virXPathString("string(./name[1])", ctxt);
392
    if (!def->name) {
393
        virNetworkReportError(VIR_ERR_NO_NAME, NULL);
394 395 396 397
        goto error;
    }

    /* Extract network uuid */
398
    tmp = virXPathString("string(./uuid[1])", ctxt);
399
    if (!tmp) {
400
        if (virUUIDGenerate(def->uuid)) {
401
            virNetworkReportError(VIR_ERR_INTERNAL_ERROR,
402
                                  "%s", _("Failed to generate UUID"));
403 404 405 406 407
            goto error;
        }
    } else {
        if (virUUIDParse(tmp, def->uuid) < 0) {
            VIR_FREE(tmp);
408
            virNetworkReportError(VIR_ERR_INTERNAL_ERROR,
409 410 411 412 413 414
                                  "%s", _("malformed uuid element"));
            goto error;
        }
        VIR_FREE(tmp);
    }

415
    /* Parse network domain information */
416
    def->domain = virXPathString("string(./domain[1]/@name)", ctxt);
417

418
    /* Parse bridge information */
419 420
    def->bridge = virXPathString("string(./bridge[1]/@name)", ctxt);
    tmp = virXPathString("string(./bridge[1]/@stp)", ctxt);
421 422 423
    def->stp = (tmp && STREQ(tmp, "off")) ? 0 : 1;
    VIR_FREE(tmp);

424
    if (virXPathULong("string(./bridge[1]/@delay)", ctxt, &def->delay) < 0)
425 426
        def->delay = 0;

427 428
    def->ipAddress = virXPathString("string(./ip[1]/@address)", ctxt);
    def->netmask = virXPathString("string(./ip[1]/@netmask)", ctxt);
429 430 431 432 433
    if (def->ipAddress &&
        def->netmask) {
        /* XXX someday we want IPv6 too, so inet_aton won't work there */
        struct in_addr inaddress, innetmask;
        char *netaddr;
434
        xmlNodePtr ip;
435

436
        if (inet_pton(AF_INET, def->ipAddress, &inaddress) <= 0) {
437
            virNetworkReportError(VIR_ERR_INTERNAL_ERROR,
438 439 440 441
                                  _("cannot parse IP address '%s'"),
                                  def->ipAddress);
            goto error;
        }
442
        if (inet_pton(AF_INET, def->netmask, &innetmask) <= 0) {
443
            virNetworkReportError(VIR_ERR_INTERNAL_ERROR,
444 445 446 447 448 449 450 451
                                  _("cannot parse netmask '%s'"),
                                  def->netmask);
            goto error;
        }

        inaddress.s_addr &= innetmask.s_addr;
        netaddr = inet_ntoa(inaddress);

452
        if (virAsprintf(&def->network, "%s/%s", netaddr, def->netmask) < 0) {
453
            virReportOOMError();
454 455 456
            goto error;
        }

457
        if ((ip = virXPathNode("./ip[1]", ctxt)) &&
458
            virNetworkIPParseXML(def, ip) < 0)
459 460 461 462 463
            goto error;
    }


    /* IPv4 forwarding setup */
464
    if (virXPathBoolean("count(./forward) > 0", ctxt)) {
465 466
        if (!def->ipAddress ||
            !def->netmask) {
467
            virNetworkReportError(VIR_ERR_INTERNAL_ERROR,
468 469 470 471
                                  "%s", _("Forwarding requested, but no IPv4 address/netmask provided"));
            goto error;
        }

472
        tmp = virXPathString("string(./forward[1]/@mode)", ctxt);
473 474
        if (tmp) {
            if ((def->forwardType = virNetworkForwardTypeFromString(tmp)) < 0) {
475
                virNetworkReportError(VIR_ERR_INTERNAL_ERROR,
476 477 478 479 480 481 482 483 484 485
                                      _("unknown forwarding type '%s'"), tmp);
                VIR_FREE(tmp);
                goto error;
            }
            VIR_FREE(tmp);
        } else {
            def->forwardType = VIR_NETWORK_FORWARD_NAT;
        }


486
        def->forwardDev = virXPathString("string(./forward[1]/@dev)", ctxt);
487 488 489 490 491 492 493 494 495 496 497
    } else {
        def->forwardType = VIR_NETWORK_FORWARD_NONE;
    }

    return def;

 error:
    virNetworkDefFree(def);
    return NULL;
}

J
Jiri Denemark 已提交
498 499 500
static virNetworkDefPtr
virNetworkDefParse(const char *xmlStr,
                   const char *filename)
501
{
J
Jiri Denemark 已提交
502
    xmlDocPtr xml;
503
    virNetworkDefPtr def = NULL;
504

J
Jiri Denemark 已提交
505 506 507
    if ((xml = virXMLParse(filename, xmlStr, "network.xml"))) {
        def = virNetworkDefParseNode(xml, xmlDocGetRootElement(xml));
        xmlFreeDoc(xml);
508 509 510 511 512
    }

    return def;
}

J
Jiri Denemark 已提交
513
virNetworkDefPtr virNetworkDefParseString(const char *xmlStr)
514
{
J
Jiri Denemark 已提交
515 516
    return virNetworkDefParse(xmlStr, NULL);
}
517

J
Jiri Denemark 已提交
518 519 520
virNetworkDefPtr virNetworkDefParseFile(const char *filename)
{
    return virNetworkDefParse(NULL, filename);
521 522 523
}


524
virNetworkDefPtr virNetworkDefParseNode(xmlDocPtr xml,
525 526 527 528 529 530
                                        xmlNodePtr root)
{
    xmlXPathContextPtr ctxt = NULL;
    virNetworkDefPtr def = NULL;

    if (!xmlStrEqual(root->name, BAD_CAST "network")) {
531
        virNetworkReportError(VIR_ERR_INTERNAL_ERROR,
532 533 534 535 536 537
                              "%s", _("incorrect root element"));
        return NULL;
    }

    ctxt = xmlXPathNewContext(xml);
    if (ctxt == NULL) {
538
        virReportOOMError();
539 540 541 542
        goto cleanup;
    }

    ctxt->node = root;
543
    def = virNetworkDefParseXML(ctxt);
544 545 546 547 548 549

cleanup:
    xmlXPathFreeContext(ctxt);
    return def;
}

550
char *virNetworkDefFormat(const virNetworkDefPtr def)
551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578
{
    virBuffer buf = VIR_BUFFER_INITIALIZER;
    unsigned char *uuid;
    char uuidstr[VIR_UUID_STRING_BUFLEN];

    virBufferAddLit(&buf, "<network>\n");
    virBufferEscapeString(&buf, "  <name>%s</name>\n", def->name);

    uuid = def->uuid;
    virUUIDFormat(uuid, uuidstr);
    virBufferVSprintf(&buf, "  <uuid>%s</uuid>\n", uuidstr);

    if (def->forwardType != VIR_NETWORK_FORWARD_NONE) {
        const char *mode = virNetworkForwardTypeToString(def->forwardType);
        if (mode) {
            if (def->forwardDev) {
                virBufferEscapeString(&buf, "  <forward dev='%s'",
                                      def->forwardDev);
            } else {
                virBufferAddLit(&buf, "  <forward");
            }
            virBufferVSprintf(&buf, " mode='%s'/>\n", mode);
        }
    }

    virBufferAddLit(&buf, "  <bridge");
    if (def->bridge)
        virBufferEscapeString(&buf, " name='%s'", def->bridge);
579
    virBufferVSprintf(&buf, " stp='%s' delay='%ld' />\n",
580 581 582
                      def->stp ? "on" : "off",
                      def->delay);

583 584 585
    if (def->domain)
        virBufferVSprintf(&buf, "  <domain name='%s'/>\n", def->domain);

586 587 588 589 590 591 592 593 594 595 596
    if (def->ipAddress || def->netmask) {
        virBufferAddLit(&buf, "  <ip");

        if (def->ipAddress)
            virBufferVSprintf(&buf, " address='%s'", def->ipAddress);

        if (def->netmask)
            virBufferVSprintf(&buf, " netmask='%s'", def->netmask);

        virBufferAddLit(&buf, ">\n");

597 598 599 600
        if (def->tftproot) {
            virBufferEscapeString(&buf, "    <tftp root='%s' />\n",
                                  def->tftproot);
        }
601
        if ((def->nranges || def->nhosts)) {
602 603 604 605 606
            int i;
            virBufferAddLit(&buf, "    <dhcp>\n");
            for (i = 0 ; i < def->nranges ; i++)
                virBufferVSprintf(&buf, "      <range start='%s' end='%s' />\n",
                                  def->ranges[i].start, def->ranges[i].end);
607 608 609 610 611 612 613 614 615 616
            for (i = 0 ; i < def->nhosts ; i++) {
                virBufferAddLit(&buf, "      <host ");
                if (def->hosts[i].mac)
                    virBufferVSprintf(&buf, "mac='%s' ", def->hosts[i].mac);
                if (def->hosts[i].name)
                    virBufferVSprintf(&buf, "name='%s' ", def->hosts[i].name);
                if (def->hosts[i].ip)
                    virBufferVSprintf(&buf, "ip='%s' ", def->hosts[i].ip);
                virBufferAddLit(&buf, "/>\n");
            }
617
            if (def->bootfile) {
618
                virBufferEscapeString(&buf, "      <bootp file='%s' ",
619
                                      def->bootfile);
620 621 622 623 624
                if (def->bootserver) {
                    virBufferEscapeString(&buf, "server='%s' ",
                                          def->bootserver);
                }
                virBufferAddLit(&buf, "/>\n");
625 626
            }

627 628 629 630 631 632 633 634 635 636 637 638 639 640
            virBufferAddLit(&buf, "    </dhcp>\n");
        }

        virBufferAddLit(&buf, "  </ip>\n");
    }

    virBufferAddLit(&buf, "</network>\n");

    if (virBufferError(&buf))
        goto no_memory;

    return virBufferContentAndReset(&buf);

 no_memory:
641
    virReportOOMError();
642
    virBufferFreeAndReset(&buf);
643 644 645
    return NULL;
}

646
int virNetworkSaveXML(const char *configDir,
647 648
                      virNetworkDefPtr def,
                      const char *xml)
649
{
650
    char *configFile = NULL;
651 652 653 654
    int fd = -1, ret = -1;
    size_t towrite;
    int err;

655
    if ((configFile = virNetworkConfigFile(configDir, def->name)) == NULL)
656 657 658
        goto cleanup;

    if ((err = virFileMakePath(configDir))) {
659
        virReportSystemError(err,
660 661
                             _("cannot create config directory '%s'"),
                             configDir);
662 663 664
        goto cleanup;
    }

665
    if ((fd = open(configFile,
666 667
                   O_WRONLY | O_CREAT | O_TRUNC,
                   S_IRUSR | S_IWUSR )) < 0) {
668
        virReportSystemError(errno,
669
                             _("cannot create config file '%s'"),
670
                             configFile);
671 672 673 674 675
        goto cleanup;
    }

    towrite = strlen(xml);
    if (safewrite(fd, xml, towrite) < 0) {
676
        virReportSystemError(errno,
677
                             _("cannot write config file '%s'"),
678
                             configFile);
679 680 681
        goto cleanup;
    }

682
    if (VIR_CLOSE(fd) < 0) {
683
        virReportSystemError(errno,
684
                             _("cannot save config file '%s'"),
685
                             configFile);
686 687 688 689 690 691
        goto cleanup;
    }

    ret = 0;

 cleanup:
692
    VIR_FORCE_CLOSE(fd);
693

694 695 696 697 698
    VIR_FREE(configFile);

    return ret;
}

699
int virNetworkSaveConfig(const char *configDir,
700 701 702 703 704
                         virNetworkDefPtr def)
{
    int ret = -1;
    char *xml;

705
    if (!(xml = virNetworkDefFormat(def)))
706 707
        goto cleanup;

708
    if (virNetworkSaveXML(configDir, def, xml))
709 710 711 712 713
        goto cleanup;

    ret = 0;
cleanup:
    VIR_FREE(xml);
714 715 716
    return ret;
}

717

718
virNetworkObjPtr virNetworkLoadConfig(virNetworkObjListPtr nets,
719 720
                                      const char *configDir,
                                      const char *autostartDir,
721
                                      const char *name)
722 723 724 725 726 727
{
    char *configFile = NULL, *autostartLink = NULL;
    virNetworkDefPtr def = NULL;
    virNetworkObjPtr net;
    int autostart;

728
    if ((configFile = virNetworkConfigFile(configDir, name)) == NULL)
729
        goto error;
730
    if ((autostartLink = virNetworkConfigFile(autostartDir, name)) == NULL)
731 732 733 734 735
        goto error;

    if ((autostart = virFileLinkPointsTo(autostartLink, configFile)) < 0)
        goto error;

736
    if (!(def = virNetworkDefParseFile(configFile)))
737 738
        goto error;

739
    if (!STREQ(name, def->name)) {
740
        virNetworkReportError(VIR_ERR_INTERNAL_ERROR,
741 742 743 744 745 746
                              _("Network config filename '%s'"
                                " does not match network name '%s'"),
                              configFile, def->name);
        goto error;
    }

747
    /* Generate a bridge if none is specified, but don't check for collisions
748 749
     * if a bridge is hardcoded, so the network is at least defined
     */
750
    if (virNetworkSetBridgeName(nets, def, 0))
751 752
        goto error;

753
    if (!(net = virNetworkAssignDef(nets, def)))
754 755 756
        goto error;

    net->autostart = autostart;
757
    net->persistent = 1;
758

759 760 761
    VIR_FREE(configFile);
    VIR_FREE(autostartLink);

762 763 764 765 766 767 768 769 770
    return net;

error:
    VIR_FREE(configFile);
    VIR_FREE(autostartLink);
    virNetworkDefFree(def);
    return NULL;
}

771
int virNetworkLoadAllConfigs(virNetworkObjListPtr nets,
772 773 774 775 776 777 778 779 780
                             const char *configDir,
                             const char *autostartDir)
{
    DIR *dir;
    struct dirent *entry;

    if (!(dir = opendir(configDir))) {
        if (errno == ENOENT)
            return 0;
781
        virReportSystemError(errno,
782 783
                             _("Failed to open dir '%s'"),
                             configDir);
784 785 786 787
        return -1;
    }

    while ((entry = readdir(dir))) {
788 789
        virNetworkObjPtr net;

790 791 792
        if (entry->d_name[0] == '.')
            continue;

793
        if (!virFileStripSuffix(entry->d_name, ".xml"))
794 795 796 797
            continue;

        /* NB: ignoring errors, so one malformed config doesn't
           kill the whole process */
798
        net = virNetworkLoadConfig(nets,
799 800 801 802 803
                                   configDir,
                                   autostartDir,
                                   entry->d_name);
        if (net)
            virNetworkObjUnlock(net);
804 805 806 807 808 809 810
    }

    closedir(dir);

    return 0;
}

811
int virNetworkDeleteConfig(const char *configDir,
812
                           const char *autostartDir,
813 814
                           virNetworkObjPtr net)
{
815 816
    char *configFile = NULL;
    char *autostartLink = NULL;
R
Ryota Ozaki 已提交
817
    int ret = -1;
818

819
    if ((configFile = virNetworkConfigFile(configDir, net->def->name)) == NULL)
820
        goto error;
821
    if ((autostartLink = virNetworkConfigFile(autostartDir, net->def->name)) == NULL)
822
        goto error;
823 824

    /* Not fatal if this doesn't work */
825
    unlink(autostartLink);
826

827
    if (unlink(configFile) < 0) {
828
        virReportSystemError(errno,
829
                             _("cannot remove config file '%s'"),
830 831
                             configFile);
        goto error;
832 833
    }

R
Ryota Ozaki 已提交
834
    ret = 0;
835 836 837 838

error:
    VIR_FREE(configFile);
    VIR_FREE(autostartLink);
R
Ryota Ozaki 已提交
839
    return ret;
840 841
}

842
char *virNetworkConfigFile(const char *dir,
843 844 845 846 847
                           const char *name)
{
    char *ret = NULL;

    if (virAsprintf(&ret, "%s/%s.xml", dir, name) < 0) {
848
        virReportOOMError();
849 850 851 852
        return NULL;
    }

    return ret;
853
}
D
Daniel P. Berrange 已提交
854

855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873
int virNetworkBridgeInUse(const virNetworkObjListPtr nets,
                          const char *bridge,
                          const char *skipname)
{
    unsigned int i;
    unsigned int ret = 0;

    for (i = 0 ; i < nets->count ; i++) {
        virNetworkObjLock(nets->objs[i]);
        if (nets->objs[i]->def->bridge &&
            STREQ(nets->objs[i]->def->bridge, bridge) &&
            !(skipname && STREQ(nets->objs[i]->def->name, skipname)))
                ret = 1;
        virNetworkObjUnlock(nets->objs[i]);
    }

    return ret;
}

874
char *virNetworkAllocateBridge(const virNetworkObjListPtr nets,
875
                               const char *template)
876 877 878 879 880
{

    int id = 0;
    char *newname;

881 882 883
    if (!template)
        template = "virbr%d";

884
    do {
885 886 887 888 889
        if (virAsprintf(&newname, template, id) < 0) {
            virReportOOMError();
            return NULL;
        }
        if (!virNetworkBridgeInUse(nets, newname, NULL)) {
890 891
            return newname;
        }
892
        VIR_FREE(newname);
893 894

        id++;
895
    } while (id <= MAX_BRIDGE_ID);
896

897
    virNetworkReportError(VIR_ERR_INTERNAL_ERROR,
898 899 900 901 902
                          _("Bridge generation exceeded max id %d"),
                          MAX_BRIDGE_ID);
    return NULL;
}

903
int virNetworkSetBridgeName(const virNetworkObjListPtr nets,
904 905
                            virNetworkDefPtr def,
                            int check_collision) {
906 907 908

    int ret = -1;

909
    if (def->bridge && !strstr(def->bridge, "%d")) {
910 911 912 913 914
        /* We may want to skip collision detection in this case (ex. when
         * loading configs at daemon startup, so the network is at least
         * defined. */
        if (check_collision &&
            virNetworkBridgeInUse(nets, def->bridge, def->name)) {
915 916 917
            virNetworkReportError(VIR_ERR_INTERNAL_ERROR,
                                  _("bridge name '%s' already in use."),
                                  def->bridge);
918 919 920 921
            goto error;
        }
    } else {
        /* Allocate a bridge name */
922
        if (!(def->bridge = virNetworkAllocateBridge(nets, def->bridge)))
923 924 925 926 927 928 929
            goto error;
    }

    ret = 0;
error:
    return ret;
}
930

931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995

/*
 * virNetworkObjIsDuplicate:
 * @doms : virNetworkObjListPtr to search
 * @def  : virNetworkDefPtr definition of network to lookup
 * @check_active: If true, ensure that network is not active
 *
 * Returns: -1 on error
 *          0 if network is new
 *          1 if network is a duplicate
 */
int
virNetworkObjIsDuplicate(virNetworkObjListPtr doms,
                         virNetworkDefPtr def,
                         unsigned int check_active)
{
    int ret = -1;
    int dupVM = 0;
    virNetworkObjPtr vm = NULL;

    /* See if a VM with matching UUID already exists */
    vm = virNetworkFindByUUID(doms, def->uuid);
    if (vm) {
        /* UUID matches, but if names don't match, refuse it */
        if (STRNEQ(vm->def->name, def->name)) {
            char uuidstr[VIR_UUID_STRING_BUFLEN];
            virUUIDFormat(vm->def->uuid, uuidstr);
            virNetworkReportError(VIR_ERR_OPERATION_FAILED,
                                  _("network '%s' is already defined with uuid %s"),
                                  vm->def->name, uuidstr);
            goto cleanup;
        }

        if (check_active) {
            /* UUID & name match, but if VM is already active, refuse it */
            if (virNetworkObjIsActive(vm)) {
                virNetworkReportError(VIR_ERR_OPERATION_INVALID,
                                      _("network is already active as '%s'"),
                                      vm->def->name);
                goto cleanup;
            }
        }

        dupVM = 1;
    } else {
        /* UUID does not match, but if a name matches, refuse it */
        vm = virNetworkFindByName(doms, def->name);
        if (vm) {
            char uuidstr[VIR_UUID_STRING_BUFLEN];
            virUUIDFormat(vm->def->uuid, uuidstr);
            virNetworkReportError(VIR_ERR_OPERATION_FAILED,
                                  _("network '%s' already exists with uuid %s"),
                                  def->name, uuidstr);
            goto cleanup;
        }
    }

    ret = dupVM;
cleanup:
    if (vm)
        virNetworkObjUnlock(vm);
    return ret;
}


996 997
void virNetworkObjLock(virNetworkObjPtr obj)
{
998
    virMutexLock(&obj->lock);
999 1000 1001 1002
}

void virNetworkObjUnlock(virNetworkObjPtr obj)
{
1003
    virMutexUnlock(&obj->lock);
D
Daniel P. Berrange 已提交
1004
}