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

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

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

46
#define MAX_BRIDGE_ID 256
47 48
#define VIR_FROM_THIS VIR_FROM_NETWORK

49 50 51 52 53 54
VIR_ENUM_DECL(virNetworkForward)

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

55
#define virNetworkReportError(conn, code, fmt...)                            \
56
        virReportErrorHelper(conn, VIR_FROM_NETWORK, code, __FILE__,       \
57
                               __FUNCTION__, __LINE__, fmt)
58

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

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

    return NULL;
}

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

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

    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);
103
    VIR_FREE(def->domain);
104 105 106 107 108 109 110

    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);

111 112 113 114 115 116 117
    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);

118 119
    VIR_FREE(def->tftproot);
    VIR_FREE(def->bootfile);
120
    VIR_FREE(def->bootserver);
121

122 123 124 125 126 127 128 129 130 131 132
    VIR_FREE(def);
}

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

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

133 134
    virMutexDestroy(&net->lock);

135 136 137
    VIR_FREE(net);
}

138 139 140 141 142 143 144 145 146 147 148
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;
}

149
virNetworkObjPtr virNetworkAssignDef(virConnectPtr conn,
150
                                     virNetworkObjListPtr nets,
151 152 153 154
                                     const virNetworkDefPtr def)
{
    virNetworkObjPtr network;

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

        return network;
    }

    if (VIR_ALLOC(network) < 0) {
169
        virReportOOMError(conn);
170 171
        return NULL;
    }
172 173 174 175 176 177
    if (virMutexInit(&network->lock) < 0) {
        virNetworkReportError(conn, VIR_ERR_INTERNAL_ERROR,
                              "%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(conn);
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 223 224 225 226 227 228 229 230
}


static int
virNetworkDHCPRangeDefParseXML(virConnectPtr conn,
                               virNetworkDefPtr def,
                               xmlNodePtr node) {

    xmlNodePtr cur;

    cur = node->children;
    while (cur != NULL) {
231 232 233
        if (cur->type == XML_ELEMENT_NODE &&
            xmlStrEqual(cur->name, BAD_CAST "range")) {
            xmlChar *start, *end;
234

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

245 246 247
            if (VIR_REALLOC_N(def->ranges, def->nranges + 1) < 0) {
                xmlFree(start);
                xmlFree(end);
248
                virReportOOMError(conn);
249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298
                return -1;
            }
            def->ranges[def->nranges].start = (char *)start;
            def->ranges[def->nranges].end = (char *)end;
            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)) {
                virNetworkReportError(conn, VIR_ERR_INTERNAL_ERROR,
                                      _("cannot parse MAC address '%s'"),
                                      mac);
                VIR_FREE(mac);
            }
            name = xmlGetProp(cur, BAD_CAST "name");
            if ((name != NULL) && (!c_isalpha(name[0]))) {
                virNetworkReportError(conn, VIR_ERR_INTERNAL_ERROR,
                                      _("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) {
                virNetworkReportError(conn, VIR_ERR_INTERNAL_ERROR,
                                      _("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);
299
                virReportOOMError(conn);
300 301 302 303 304 305
                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++;
306 307 308 309 310 311 312 313 314 315 316

        } 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;
317
            def->bootserver = (char *) xmlGetProp(cur, BAD_CAST "server");
318 319 320 321 322 323 324 325
        }

        cur = cur->next;
    }

    return 0;
}

326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356
static int
virNetworkIPParseXML(virConnectPtr conn,
                     virNetworkDefPtr def,
                     xmlNodePtr node) {
    xmlNodePtr cur;

    cur = node->children;
    while (cur != NULL) {
        if (cur->type == XML_ELEMENT_NODE &&
            xmlStrEqual(cur->name, BAD_CAST "dhcp")) {
            int result = virNetworkDHCPRangeDefParseXML(conn, def, cur);
            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;
}

357 358 359 360 361 362 363 364
static virNetworkDefPtr
virNetworkDefParseXML(virConnectPtr conn,
                      xmlXPathContextPtr ctxt)
{
    virNetworkDefPtr def;
    char *tmp;

    if (VIR_ALLOC(def) < 0) {
365
        virReportOOMError(conn);
366 367 368 369
        return NULL;
    }

    /* Extract network name */
370
    def->name = virXPathString(conn, "string(./name[1])", ctxt);
371 372 373 374 375 376
    if (!def->name) {
        virNetworkReportError(conn, VIR_ERR_NO_NAME, NULL);
        goto error;
    }

    /* Extract network uuid */
377
    tmp = virXPathString(conn, "string(./uuid[1])", ctxt);
378
    if (!tmp) {
379
        if (virUUIDGenerate(def->uuid)) {
380
            virNetworkReportError(conn, VIR_ERR_INTERNAL_ERROR,
381
                                  "%s", _("Failed to generate UUID"));
382 383 384 385 386 387 388 389 390 391 392 393
            goto error;
        }
    } else {
        if (virUUIDParse(tmp, def->uuid) < 0) {
            VIR_FREE(tmp);
            virNetworkReportError(conn, VIR_ERR_INTERNAL_ERROR,
                                  "%s", _("malformed uuid element"));
            goto error;
        }
        VIR_FREE(tmp);
    }

394 395 396
    /* Parse network domain information */
    def->domain = virXPathString(conn, "string(./domain[1]/@name)", ctxt);

397
    /* Parse bridge information */
398 399
    def->bridge = virXPathString(conn, "string(./bridge[1]/@name)", ctxt);
    tmp = virXPathString(conn, "string(./bridge[1]/@stp)", ctxt);
400 401 402
    def->stp = (tmp && STREQ(tmp, "off")) ? 0 : 1;
    VIR_FREE(tmp);

403
    if (virXPathULong(conn, "string(./bridge[1]/@delay)", ctxt, &def->delay) < 0)
404 405
        def->delay = 0;

406 407
    def->ipAddress = virXPathString(conn, "string(./ip[1]/@address)", ctxt);
    def->netmask = virXPathString(conn, "string(./ip[1]/@netmask)", ctxt);
408 409 410 411 412
    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;
413
        xmlNodePtr ip;
414

415
        if (inet_pton(AF_INET, def->ipAddress, &inaddress) <= 0) {
416 417 418 419 420
            virNetworkReportError(conn, VIR_ERR_INTERNAL_ERROR,
                                  _("cannot parse IP address '%s'"),
                                  def->ipAddress);
            goto error;
        }
421
        if (inet_pton(AF_INET, def->netmask, &innetmask) <= 0) {
422 423 424 425 426 427 428 429 430
            virNetworkReportError(conn, VIR_ERR_INTERNAL_ERROR,
                                  _("cannot parse netmask '%s'"),
                                  def->netmask);
            goto error;
        }

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

431
        if (virAsprintf(&def->network, "%s/%s", netaddr, def->netmask) < 0) {
432
            virReportOOMError(conn);
433 434 435
            goto error;
        }

436 437
        if ((ip = virXPathNode(conn, "./ip[1]", ctxt)) &&
            virNetworkIPParseXML(conn, def, ip) < 0)
438 439 440 441 442
            goto error;
    }


    /* IPv4 forwarding setup */
443
    if (virXPathBoolean(conn, "count(./forward) > 0", ctxt)) {
444 445 446 447 448 449 450
        if (!def->ipAddress ||
            !def->netmask) {
            virNetworkReportError(conn, VIR_ERR_INTERNAL_ERROR,
                                  "%s", _("Forwarding requested, but no IPv4 address/netmask provided"));
            goto error;
        }

451
        tmp = virXPathString(conn, "string(./forward[1]/@mode)", ctxt);
452 453 454 455 456 457 458 459 460 461 462 463 464
        if (tmp) {
            if ((def->forwardType = virNetworkForwardTypeFromString(tmp)) < 0) {
                virNetworkReportError(conn, VIR_ERR_INTERNAL_ERROR,
                                      _("unknown forwarding type '%s'"), tmp);
                VIR_FREE(tmp);
                goto error;
            }
            VIR_FREE(tmp);
        } else {
            def->forwardType = VIR_NETWORK_FORWARD_NAT;
        }


465
        def->forwardDev = virXPathString(conn, "string(./forward[1]/@dev)", ctxt);
466 467 468 469 470 471 472 473 474 475 476
    } else {
        def->forwardType = VIR_NETWORK_FORWARD_NONE;
    }

    return def;

 error:
    virNetworkDefFree(def);
    return NULL;
}

477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497
/* Called from SAX on parsing errors in the XML. */
static void
catchXMLError (void *ctx, const char *msg ATTRIBUTE_UNUSED, ...)
{
    xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr) ctx;

    if (ctxt) {
        virConnectPtr conn = ctxt->_private;

        if (conn &&
            conn->err.code == VIR_ERR_NONE &&
            ctxt->lastError.level == XML_ERR_FATAL &&
            ctxt->lastError.message != NULL) {
            virNetworkReportError (conn, VIR_ERR_XML_DETAIL,
                                   _("at line %d: %s"),
                                   ctxt->lastError.line,
                                   ctxt->lastError.message);
        }
    }
}

498 499 500
virNetworkDefPtr virNetworkDefParseString(virConnectPtr conn,
                                          const char *xmlStr)
{
501 502
    xmlParserCtxtPtr pctxt;
    xmlDocPtr xml = NULL;
503
    xmlNodePtr root;
504
    virNetworkDefPtr def = NULL;
505

506 507 508 509 510 511 512 513 514 515 516 517 518 519
    /* Set up a parser context so we can catch the details of XML errors. */
    pctxt = xmlNewParserCtxt ();
    if (!pctxt || !pctxt->sax)
        goto cleanup;
    pctxt->sax->error = catchXMLError;
    pctxt->_private = conn;

    if (conn) virResetError (&conn->err);
    xml = xmlCtxtReadDoc (pctxt, BAD_CAST xmlStr, "network.xml", NULL,
                          XML_PARSE_NOENT | XML_PARSE_NONET |
                          XML_PARSE_NOWARNING);
    if (!xml) {
        if (conn && conn->err.code == VIR_ERR_NONE)
              virNetworkReportError(conn, VIR_ERR_XML_ERROR,
J
Jim Meyering 已提交
520
                                    "%s", _("failed to parse xml document"));
521
        goto cleanup;
522 523 524 525 526
    }

    if ((root = xmlDocGetRootElement(xml)) == NULL) {
        virNetworkReportError(conn, VIR_ERR_INTERNAL_ERROR,
                              "%s", _("missing root element"));
527
        goto cleanup;
528 529 530 531
    }

    def = virNetworkDefParseNode(conn, xml, root);

532 533 534
cleanup:
    xmlFreeParserCtxt (pctxt);
    xmlFreeDoc (xml);
535 536 537 538 539 540
    return def;
}

virNetworkDefPtr virNetworkDefParseFile(virConnectPtr conn,
                                        const char *filename)
{
541 542
    xmlParserCtxtPtr pctxt;
    xmlDocPtr xml = NULL;
543
    xmlNodePtr root;
544
    virNetworkDefPtr def = NULL;
545

546 547 548 549 550 551 552 553 554 555 556 557 558 559
    /* Set up a parser context so we can catch the details of XML errors. */
    pctxt = xmlNewParserCtxt ();
    if (!pctxt || !pctxt->sax)
        goto cleanup;
    pctxt->sax->error = catchXMLError;
    pctxt->_private = conn;

    if (conn) virResetError (&conn->err);
    xml = xmlCtxtReadFile (pctxt, filename, NULL,
                           XML_PARSE_NOENT | XML_PARSE_NONET |
                           XML_PARSE_NOWARNING);
    if (!xml) {
        if (conn && conn->err.code == VIR_ERR_NONE)
              virNetworkReportError(conn, VIR_ERR_XML_ERROR,
J
Jim Meyering 已提交
560
                                    "%s", _("failed to parse xml document"));
561
        goto cleanup;
562 563 564 565 566
    }

    if ((root = xmlDocGetRootElement(xml)) == NULL) {
        virNetworkReportError(conn, VIR_ERR_INTERNAL_ERROR,
                              "%s", _("missing root element"));
567
        goto cleanup;
568 569 570 571
    }

    def = virNetworkDefParseNode(conn, xml, root);

572 573 574
cleanup:
    xmlFreeParserCtxt (pctxt);
    xmlFreeDoc (xml);
575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593
    return def;
}


virNetworkDefPtr virNetworkDefParseNode(virConnectPtr conn,
                                        xmlDocPtr xml,
                                        xmlNodePtr root)
{
    xmlXPathContextPtr ctxt = NULL;
    virNetworkDefPtr def = NULL;

    if (!xmlStrEqual(root->name, BAD_CAST "network")) {
        virNetworkReportError(conn, VIR_ERR_INTERNAL_ERROR,
                              "%s", _("incorrect root element"));
        return NULL;
    }

    ctxt = xmlXPathNewContext(xml);
    if (ctxt == NULL) {
594
        virReportOOMError(conn);
595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636
        goto cleanup;
    }

    ctxt->node = root;
    def = virNetworkDefParseXML(conn, ctxt);

cleanup:
    xmlXPathFreeContext(ctxt);
    return def;
}

char *virNetworkDefFormat(virConnectPtr conn,
                          const virNetworkDefPtr def)
{
    virBuffer buf = VIR_BUFFER_INITIALIZER;
    unsigned char *uuid;
    char *tmp;
    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);
637
    virBufferVSprintf(&buf, " stp='%s' delay='%ld' />\n",
638 639 640
                      def->stp ? "on" : "off",
                      def->delay);

641 642 643
    if (def->domain)
        virBufferVSprintf(&buf, "  <domain name='%s'/>\n", def->domain);

644 645 646 647 648 649 650 651 652 653 654
    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");

655 656 657 658
        if (def->tftproot) {
            virBufferEscapeString(&buf, "    <tftp root='%s' />\n",
                                  def->tftproot);
        }
659
        if ((def->nranges || def->nhosts)) {
660 661 662 663 664
            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);
665 666 667 668 669 670 671 672 673 674
            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");
            }
675
            if (def->bootfile) {
676
                virBufferEscapeString(&buf, "      <bootp file='%s' ",
677
                                      def->bootfile);
678 679 680 681 682
                if (def->bootserver) {
                    virBufferEscapeString(&buf, "server='%s' ",
                                          def->bootserver);
                }
                virBufferAddLit(&buf, "/>\n");
683 684
            }

685 686 687 688 689 690 691 692 693 694 695 696 697 698
            virBufferAddLit(&buf, "    </dhcp>\n");
        }

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

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

    if (virBufferError(&buf))
        goto no_memory;

    return virBufferContentAndReset(&buf);

 no_memory:
699
    virReportOOMError(conn);
700 701 702 703 704
    tmp = virBufferContentAndReset(&buf);
    VIR_FREE(tmp);
    return NULL;
}

705 706 707 708
int virNetworkSaveXML(virConnectPtr conn,
                      const char *configDir,
                      virNetworkDefPtr def,
                      const char *xml)
709
{
710
    char *configFile = NULL;
711 712 713 714
    int fd = -1, ret = -1;
    size_t towrite;
    int err;

715
    if ((configFile = virNetworkConfigFile(conn, configDir, def->name)) == NULL)
716 717 718
        goto cleanup;

    if ((err = virFileMakePath(configDir))) {
719 720 721
        virReportSystemError(conn, err,
                             _("cannot create config directory '%s'"),
                             configDir);
722 723 724
        goto cleanup;
    }

725
    if ((fd = open(configFile,
726 727
                   O_WRONLY | O_CREAT | O_TRUNC,
                   S_IRUSR | S_IWUSR )) < 0) {
728 729
        virReportSystemError(conn, errno,
                             _("cannot create config file '%s'"),
730
                             configFile);
731 732 733 734 735
        goto cleanup;
    }

    towrite = strlen(xml);
    if (safewrite(fd, xml, towrite) < 0) {
736 737
        virReportSystemError(conn, errno,
                             _("cannot write config file '%s'"),
738
                             configFile);
739 740 741 742
        goto cleanup;
    }

    if (close(fd) < 0) {
743 744
        virReportSystemError(conn, errno,
                             _("cannot save config file '%s'"),
745
                             configFile);
746 747 748 749 750 751 752 753 754
        goto cleanup;
    }

    ret = 0;

 cleanup:
    if (fd != -1)
        close(fd);

755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775
    VIR_FREE(configFile);

    return ret;
}

int virNetworkSaveConfig(virConnectPtr conn,
                         const char *configDir,
                         virNetworkDefPtr def)
{
    int ret = -1;
    char *xml;

    if (!(xml = virNetworkDefFormat(conn, def)))
        goto cleanup;

    if (virNetworkSaveXML(conn, configDir, def, xml))
        goto cleanup;

    ret = 0;
cleanup:
    VIR_FREE(xml);
776 777 778
    return ret;
}

779

780
virNetworkObjPtr virNetworkLoadConfig(virConnectPtr conn,
781
                                      virNetworkObjListPtr nets,
782 783
                                      const char *configDir,
                                      const char *autostartDir,
784
                                      const char *name)
785 786 787 788 789 790
{
    char *configFile = NULL, *autostartLink = NULL;
    virNetworkDefPtr def = NULL;
    virNetworkObjPtr net;
    int autostart;

791
    if ((configFile = virNetworkConfigFile(conn, configDir, name)) == NULL)
792
        goto error;
793
    if ((autostartLink = virNetworkConfigFile(conn, autostartDir, name)) == NULL)
794 795 796 797 798
        goto error;

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

799
    if (!(def = virNetworkDefParseFile(conn, configFile)))
800 801
        goto error;

802
    if (!STREQ(name, def->name)) {
803 804 805 806 807 808 809
        virNetworkReportError(conn, VIR_ERR_INTERNAL_ERROR,
                              _("Network config filename '%s'"
                                " does not match network name '%s'"),
                              configFile, def->name);
        goto error;
    }

810
    /* Generate a bridge if none is specified, but don't check for collisions
811 812
     * if a bridge is hardcoded, so the network is at least defined
     */
813
    if (virNetworkSetBridgeName(conn, nets, def, 0))
814 815
        goto error;

816 817 818 819
    if (!(net = virNetworkAssignDef(conn, nets, def)))
        goto error;

    net->autostart = autostart;
820
    net->persistent = 1;
821

822 823 824
    VIR_FREE(configFile);
    VIR_FREE(autostartLink);

825 826 827 828 829 830 831 832 833 834
    return net;

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

int virNetworkLoadAllConfigs(virConnectPtr conn,
835
                             virNetworkObjListPtr nets,
836 837 838 839 840 841 842 843 844
                             const char *configDir,
                             const char *autostartDir)
{
    DIR *dir;
    struct dirent *entry;

    if (!(dir = opendir(configDir))) {
        if (errno == ENOENT)
            return 0;
845 846 847
        virReportSystemError(conn, errno,
                             _("Failed to open dir '%s'"),
                             configDir);
848 849 850 851
        return -1;
    }

    while ((entry = readdir(dir))) {
852 853
        virNetworkObjPtr net;

854 855 856
        if (entry->d_name[0] == '.')
            continue;

857
        if (!virFileStripSuffix(entry->d_name, ".xml"))
858 859 860 861
            continue;

        /* NB: ignoring errors, so one malformed config doesn't
           kill the whole process */
862 863 864 865 866 867 868
        net = virNetworkLoadConfig(conn,
                                   nets,
                                   configDir,
                                   autostartDir,
                                   entry->d_name);
        if (net)
            virNetworkObjUnlock(net);
869 870 871 872 873 874 875 876
    }

    closedir(dir);

    return 0;
}

int virNetworkDeleteConfig(virConnectPtr conn,
877 878
                           const char *configDir,
                           const char *autostartDir,
879 880
                           virNetworkObjPtr net)
{
881 882
    char *configFile = NULL;
    char *autostartLink = NULL;
R
Ryota Ozaki 已提交
883
    int ret = -1;
884 885 886 887 888

    if ((configFile = virNetworkConfigFile(conn, configDir, net->def->name)) == NULL)
        goto error;
    if ((autostartLink = virNetworkConfigFile(conn, autostartDir, net->def->name)) == NULL)
        goto error;
889 890

    /* Not fatal if this doesn't work */
891
    unlink(autostartLink);
892

893
    if (unlink(configFile) < 0) {
894 895
        virReportSystemError(conn, errno,
                             _("cannot remove config file '%s'"),
896 897
                             configFile);
        goto error;
898 899
    }

R
Ryota Ozaki 已提交
900
    ret = 0;
901 902 903 904

error:
    VIR_FREE(configFile);
    VIR_FREE(autostartLink);
R
Ryota Ozaki 已提交
905
    return ret;
906 907 908 909 910 911 912 913 914
}

char *virNetworkConfigFile(virConnectPtr conn,
                           const char *dir,
                           const char *name)
{
    char *ret = NULL;

    if (virAsprintf(&ret, "%s/%s.xml", dir, name) < 0) {
915
        virReportOOMError(conn);
916 917 918 919
        return NULL;
    }

    return ret;
920
}
D
Daniel P. Berrange 已提交
921

922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941
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;
}

char *virNetworkAllocateBridge(virConnectPtr conn,
942 943
                               const virNetworkObjListPtr nets,
                               const char *template)
944 945 946 947 948
{

    int id = 0;
    char *newname;

949 950 951
    if (!template)
        template = "virbr%d";

952 953 954
    do {
        char try[50];

955
        snprintf(try, sizeof(try), template, id);
956 957 958 959 960 961 962 963 964 965

        if (!virNetworkBridgeInUse(nets, try, NULL)) {
            if (!(newname = strdup(try))) {
                virReportOOMError(conn);
                return NULL;
            }
            return newname;
        }

        id++;
966
    } while (id <= MAX_BRIDGE_ID);
967 968 969 970 971 972 973 974 975

    virNetworkReportError(conn, VIR_ERR_INTERNAL_ERROR,
                          _("Bridge generation exceeded max id %d"),
                          MAX_BRIDGE_ID);
    return NULL;
}

int virNetworkSetBridgeName(virConnectPtr conn,
                            const virNetworkObjListPtr nets,
976 977
                            virNetworkDefPtr def,
                            int check_collision) {
978 979 980

    int ret = -1;

981
    if (def->bridge && !strstr(def->bridge, "%d")) {
982 983 984 985 986
        /* 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)) {
987 988 989 990 991 992 993
            networkReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
                               _("bridge name '%s' already in use."),
                               def->bridge);
            goto error;
        }
    } else {
        /* Allocate a bridge name */
994
        if (!(def->bridge = virNetworkAllocateBridge(conn, nets, def->bridge)))
995 996 997 998 999 1000 1001
            goto error;
    }

    ret = 0;
error:
    return ret;
}
1002

1003 1004
void virNetworkObjLock(virNetworkObjPtr obj)
{
1005
    virMutexLock(&obj->lock);
1006 1007 1008 1009
}

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