nwfilter_conf.c 105.9 KB
Newer Older
1 2 3 4
/*
 * nwfilter_conf.c: network filter XML processing
 *                  (derived from storage_conf.c)
 *
5
 * Copyright (C) 2006-2014 Red Hat, Inc.
6 7
 * Copyright (C) 2006-2008 Daniel P. Berrange
 *
8 9
 * Copyright (C) 2010-2011 IBM Corporation
 * Copyright (C) 2010-2011 Stefan Berger
10 11 12 13 14 15 16 17 18 19 20 21
 *
 * 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
22
 * License along with this library.  If not, see
O
Osier Yang 已提交
23
 * <http://www.gnu.org/licenses/>.
24 25 26 27 28 29
 *
 * Author: Stefan Berger <stefanb@us.ibm.com>
 */

#include <config.h>

30 31
#include <sys/types.h>
#include <sys/stat.h>
32 33
#include <fcntl.h>
#include <dirent.h>
34 35 36
#if HAVE_NET_ETHERNET_H
# include <net/ethernet.h>
#endif
37
#include <unistd.h>
38 39 40

#include "internal.h"

41
#include "viruuid.h"
42
#include "viralloc.h"
43
#include "virerror.h"
44 45 46 47
#include "datatypes.h"
#include "nwfilter_params.h"
#include "nwfilter_conf.h"
#include "domain_conf.h"
48
#include "c-ctype.h"
E
Eric Blake 已提交
49
#include "virfile.h"
50
#include "virstring.h"
51 52 53 54 55 56

#define VIR_FROM_THIS VIR_FROM_NWFILTER


VIR_ENUM_IMPL(virNWFilterRuleAction, VIR_NWFILTER_RULE_ACTION_LAST,
              "drop",
57
              "accept",
58 59 60
              "reject",
              "return",
              "continue");
61 62 63

VIR_ENUM_IMPL(virNWFilterJumpTarget, VIR_NWFILTER_RULE_ACTION_LAST,
              "DROP",
64
              "ACCEPT",
65 66 67
              "REJECT",
              "RETURN",
              "CONTINUE");
68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84

VIR_ENUM_IMPL(virNWFilterRuleDirection, VIR_NWFILTER_RULE_DIRECTION_LAST,
              "in",
              "out",
              "inout");

VIR_ENUM_IMPL(virNWFilterChainPolicy, VIR_NWFILTER_CHAIN_POLICY_LAST,
              "ACCEPT",
              "DROP");

VIR_ENUM_IMPL(virNWFilterEbtablesTable, VIR_NWFILTER_EBTABLES_TABLE_LAST,
              "filter",
              "nat",
              "broute");

VIR_ENUM_IMPL(virNWFilterChainSuffix, VIR_NWFILTER_CHAINSUFFIX_LAST,
              "root",
S
Stefan Berger 已提交
85
              "mac",
S
Stefan Berger 已提交
86
              "vlan",
S
Stefan Berger 已提交
87
              "stp",
88
              "arp",
89
              "rarp",
90 91
              "ipv4",
              "ipv6");
92

S
Stefan Berger 已提交
93 94 95
VIR_ENUM_IMPL(virNWFilterRuleProtocol, VIR_NWFILTER_RULE_PROTOCOL_LAST,
              "none",
              "mac",
S
Stefan Berger 已提交
96
              "vlan",
S
Stefan Berger 已提交
97
              "stp",
S
Stefan Berger 已提交
98
              "arp",
99
              "rarp",
S
Stefan Berger 已提交
100 101 102 103 104 105
              "ip",
              "ipv6",
              "tcp",
              "icmp",
              "igmp",
              "udp",
106 107 108
              "udplite",
              "esp",
              "ah",
S
Stefan Berger 已提交
109
              "sctp",
110 111 112 113 114 115 116 117 118
              "all",
              "tcp-ipv6",
              "icmpv6",
              "udp-ipv6",
              "udplite-ipv6",
              "esp-ipv6",
              "ah-ipv6",
              "sctp-ipv6",
              "all-ipv6");
S
Stefan Berger 已提交
119

120 121 122 123 124 125 126 127 128

/*
 * a map entry for a simple static int-to-string map
 */
struct int_map {
    int32_t attr;
    const char *val;
};

129 130 131
#define INTMAP_ENTRY(ATT, VAL) { .attr = ATT, .val = VAL }
#define INTMAP_ENTRY_LAST      { .val = NULL }

132 133
static const struct int_map chain_priorities[] = {
    INTMAP_ENTRY(NWFILTER_ROOT_FILTER_PRI, "root"),
S
Stefan Berger 已提交
134
    INTMAP_ENTRY(NWFILTER_MAC_FILTER_PRI,  "mac"),
S
Stefan Berger 已提交
135
    INTMAP_ENTRY(NWFILTER_VLAN_FILTER_PRI, "vlan"),
136 137
    INTMAP_ENTRY(NWFILTER_IPV4_FILTER_PRI, "ipv4"),
    INTMAP_ENTRY(NWFILTER_IPV6_FILTER_PRI, "ipv6"),
138
    INTMAP_ENTRY(NWFILTER_ARP_FILTER_PRI,  "arp"),
139 140 141
    INTMAP_ENTRY(NWFILTER_RARP_FILTER_PRI, "rarp"),
    INTMAP_ENTRY_LAST,
};
142 143 144 145

/*
 * only one filter update allowed
 */
146
static virRWLock updateLock;
147
static bool initialized;
148

149
void
150 151
virNWFilterReadLockFilterUpdates(void)
{
152 153 154 155
    virRWLockRead(&updateLock);
}

void
156 157
virNWFilterWriteLockFilterUpdates(void)
{
158
    virRWLockWrite(&updateLock);
159 160
}

161
void
162 163
virNWFilterUnlockFilterUpdates(void)
{
164
    virRWLockUnlock(&updateLock);
165 166 167
}


168

169 170 171
/*
 * attribute names for the rules XML
 */
172 173 174 175 176 177 178
static const char srcmacaddr_str[]    = "srcmacaddr";
static const char srcmacmask_str[]    = "srcmacmask";
static const char dstmacaddr_str[]    = "dstmacaddr";
static const char dstmacmask_str[]    = "dstmacmask";
static const char arpsrcmacaddr_str[] = "arpsrcmacaddr";
static const char arpdstmacaddr_str[] = "arpdstmacaddr";
static const char arpsrcipaddr_str[]  = "arpsrcipaddr";
179
static const char arpsrcipmask_str[]  = "arpsrcipmask";
180
static const char arpdstipaddr_str[]  = "arpdstipaddr";
181
static const char arpdstipmask_str[]  = "arpdstipmask";
182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197
static const char srcipaddr_str[]     = "srcipaddr";
static const char srcipmask_str[]     = "srcipmask";
static const char dstipaddr_str[]     = "dstipaddr";
static const char dstipmask_str[]     = "dstipmask";
static const char srcipfrom_str[]     = "srcipfrom";
static const char srcipto_str[]       = "srcipto";
static const char dstipfrom_str[]     = "dstipfrom";
static const char dstipto_str[]       = "dstipto";
static const char srcportstart_str[]  = "srcportstart";
static const char srcportend_str[]    = "srcportend";
static const char dstportstart_str[]  = "dstportstart";
static const char dstportend_str[]    = "dstportend";
static const char dscp_str[]          = "dscp";
static const char state_str[]         = "state";
static const char ipset_str[]         = "ipset";
static const char ipsetflags_str[]    = "ipsetflags";
198 199 200 201 202 203 204 205

#define SRCMACADDR    srcmacaddr_str
#define SRCMACMASK    srcmacmask_str
#define DSTMACADDR    dstmacaddr_str
#define DSTMACMASK    dstmacmask_str
#define ARPSRCMACADDR arpsrcmacaddr_str
#define ARPDSTMACADDR arpdstmacaddr_str
#define ARPSRCIPADDR  arpsrcipaddr_str
206
#define ARPSRCIPMASK  arpsrcipmask_str
207
#define ARPDSTIPADDR  arpdstipaddr_str
208
#define ARPDSTIPMASK  arpdstipmask_str
209 210 211 212
#define SRCIPADDR     srcipaddr_str
#define SRCIPMASK     srcipmask_str
#define DSTIPADDR     dstipaddr_str
#define DSTIPMASK     dstipmask_str
S
Stefan Berger 已提交
213 214 215 216
#define SRCIPFROM     srcipfrom_str
#define SRCIPTO       srcipto_str
#define DSTIPFROM     dstipfrom_str
#define DSTIPTO       dstipto_str
217 218 219 220 221
#define SRCPORTSTART  srcportstart_str
#define SRCPORTEND    srcportend_str
#define DSTPORTSTART  dstportstart_str
#define DSTPORTEND    dstportend_str
#define DSCP          dscp_str
222
#define STATE         state_str
S
Stefan Berger 已提交
223 224
#define IPSET         ipset_str
#define IPSETFLAGS    ipsetflags_str
225 226 227 228 229 230 231 232


/**
 * intMapGetByInt:
 * @intmap: Pointer to int-to-string map
 * @attr: The attribute to look up
 * @res: Pointer to string pointer for result
 *
233
 * Returns 0 if value was found with result returned, -1 otherwise.
234 235 236
 *
 * lookup a map entry given the integer.
 */
237
static int
238 239
intMapGetByInt(const struct int_map *intmap, int32_t attr, const char **res)
{
240
    size_t i = 0;
241 242
    bool found = false;

243 244 245
    while (intmap[i].val && !found) {
        if (intmap[i].attr == attr) {
            *res = intmap[i].val;
246
            found = true;
247 248 249
        }
        i++;
    }
250
    return (found) ? 0 : -1;
251 252 253 254 255 256 257 258 259 260
}


/**
 * intMapGetByString:
 * @intmap: Pointer to int-to-string map
 * @str: Pointer to string for which to find the entry
 * @casecmp : Whether to ignore case when doing string matching
 * @result: Pointer to int for result
 *
261
 * Returns 0 if entry was found, -1 otherwise.
262 263
 *
 * Do a lookup in the map trying to find an integer key using the string
264
 * value. Returns 0 if entry was found with result returned, -1 otherwise.
265
 */
266
static int
267 268 269
intMapGetByString(const struct int_map *intmap, const char *str, int casecmp,
                  int32_t *result)
{
270
    size_t i = 0;
271 272
    bool found = false;

273
    while (intmap[i].val && !found) {
274 275
        if ((casecmp && STRCASEEQ(intmap[i].val, str)) ||
            STREQ(intmap[i].val, str)) {
276
            *result = intmap[i].attr;
277
            found = true;
278 279 280
        }
        i++;
    }
281
    return (found) ? 0 : -1;
282 283 284 285
}


void
286 287
virNWFilterRuleDefFree(virNWFilterRuleDefPtr def)
{
288
    size_t i;
289 290 291
    if (!def)
        return;

292 293
    for (i = 0; i < def->nVarAccess; i++)
        virNWFilterVarAccessFree(def->varAccess[i]);
294

295 296 297
    for (i = 0; i < def->nstrings; i++)
        VIR_FREE(def->strings[i]);

298
    VIR_FREE(def->varAccess);
299
    VIR_FREE(def->strings);
300 301 302 303 304 305

    VIR_FREE(def);
}


static void
306 307
virNWFilterIncludeDefFree(virNWFilterIncludeDefPtr inc)
{
308 309 310 311 312 313 314 315 316
    if (!inc)
        return;
    virNWFilterHashTableFree(inc->params);
    VIR_FREE(inc->filterref);
    VIR_FREE(inc);
}


static void
317 318
virNWFilterEntryFree(virNWFilterEntryPtr entry)
{
319 320 321 322 323 324 325 326 327 328
    if (!entry)
        return;

    virNWFilterRuleDefFree(entry->rule);
    virNWFilterIncludeDefFree(entry->include);
    VIR_FREE(entry);
}


void
329 330
virNWFilterDefFree(virNWFilterDefPtr def)
{
331
    size_t i;
332 333 334 335 336 337 338 339 340
    if (!def)
        return;

    VIR_FREE(def->name);

    for (i = 0; i < def->nentries; i++)
        virNWFilterEntryFree(def->filterEntries[i]);

    VIR_FREE(def->filterEntries);
341
    VIR_FREE(def->chainsuffix);
342 343 344 345 346 347

    VIR_FREE(def);
}


void
348 349
virNWFilterObjFree(virNWFilterObjPtr obj)
{
350 351 352 353 354 355 356 357 358 359 360 361 362
    if (!obj)
        return;

    virNWFilterDefFree(obj->def);
    virNWFilterDefFree(obj->newDef);

    virMutexDestroy(&obj->lock);

    VIR_FREE(obj);
}


void
363
virNWFilterObjListFree(virNWFilterObjListPtr nwfilters)
364
{
365
    size_t i;
366
    for (i = 0; i < nwfilters->count; i++)
367 368 369
        virNWFilterObjFree(nwfilters->objs[i]);
    VIR_FREE(nwfilters->objs);
    nwfilters->count = 0;
370 371 372 373
}


static int
374
virNWFilterRuleDefAddVar(virNWFilterRuleDefPtr nwf,
375 376 377
                         nwItemDesc *item,
                         const char *var)
{
378
    size_t i = 0;
379
    virNWFilterVarAccessPtr varAccess;
380

381 382 383 384 385 386 387 388 389
    varAccess = virNWFilterVarAccessParse(var);
    if (varAccess == NULL)
        return -1;

    if (nwf->varAccess) {
        for (i = 0; i < nwf->nVarAccess; i++)
            if (virNWFilterVarAccessEqual(nwf->varAccess[i], varAccess)) {
                virNWFilterVarAccessFree(varAccess);
                item->varAccess = nwf->varAccess[i];
390 391 392 393
                return 0;
            }
    }

394
    if (VIR_EXPAND_N(nwf->varAccess, nwf->nVarAccess, 1) < 0) {
S
Stefan Berger 已提交
395
        virNWFilterVarAccessFree(varAccess);
396
        return -1;
397 398
    }

399 400
    nwf->varAccess[nwf->nVarAccess - 1] = varAccess;
    item->varAccess = varAccess;
401 402 403 404 405

    return 0;
}


406 407 408 409 410
static char *
virNWFilterRuleDefAddString(virNWFilterRuleDefPtr nwf,
                            const char *string,
                            size_t maxstrlen)
{
411
    char *tmp;
412

413 414 415
    if (VIR_STRNDUP(tmp, string, maxstrlen) < 0 ||
        VIR_APPEND_ELEMENT_COPY(nwf->strings, nwf->nstrings, tmp) < 0)
        VIR_FREE(tmp);
416

417
    return tmp;
418 419 420
}


421
void
422 423
virNWFilterObjRemove(virNWFilterObjListPtr nwfilters,
                     virNWFilterObjPtr nwfilter)
424
{
425
    size_t i;
426

427
    virNWFilterObjUnlock(nwfilter);
428

429
    for (i = 0; i < nwfilters->count; i++) {
430 431 432 433
        virNWFilterObjLock(nwfilters->objs[i]);
        if (nwfilters->objs[i] == nwfilter) {
            virNWFilterObjUnlock(nwfilters->objs[i]);
            virNWFilterObjFree(nwfilters->objs[i]);
434

435
            VIR_DELETE_ELEMENT(nwfilters->objs, i, nwfilters->count);
436 437
            break;
        }
438
        virNWFilterObjUnlock(nwfilters->objs[i]);
439 440 441
    }
}

442 443 444 445 446 447
union data {
    void *v;
    char *c;
    unsigned char *uc;
    unsigned int ui;
};
448

449
typedef bool (*valueValidator)(enum attrDatatype datatype, union data *valptr,
450 451
                               virNWFilterRuleDefPtr nwf,
                               nwItemDesc *item);
452
typedef bool (*valueFormatter)(virBufferPtr buf,
453 454
                               virNWFilterRuleDefPtr nwf,
                               nwItemDesc *item);
455 456 457 458

typedef struct _virXMLAttr2Struct virXMLAttr2Struct;
struct _virXMLAttr2Struct
{
459
    const char *name;           /* attribute name */
460
    enum attrDatatype datatype;
461 462 463
    int dataIdx;                /* offset of the hasXYZ boolean */
    valueValidator validator;   /* beyond-standard checkers */
    valueFormatter formatter;   /* beyond-standard formatter */
464
    size_t maxstrlen;
465 466 467 468
};


static const struct int_map macProtoMap[] = {
469
    INTMAP_ENTRY(ETHERTYPE_ARP,    "arp"),
470
    INTMAP_ENTRY(ETHERTYPE_REVARP, "rarp"),
471 472 473
    INTMAP_ENTRY(ETHERTYPE_IP,     "ipv4"),
    INTMAP_ENTRY(ETHERTYPE_IPV6,   "ipv6"),
    INTMAP_ENTRY(ETHERTYPE_VLAN,   "vlan"),
474
    INTMAP_ENTRY_LAST
475 476 477 478
};


static bool
479
checkMacProtocolID(enum attrDatatype datatype, union data *value,
480 481
                   virNWFilterRuleDefPtr nwf ATTRIBUTE_UNUSED,
                   nwItemDesc *item ATTRIBUTE_UNUSED)
482 483 484 485
{
    int32_t res = -1;

    if (datatype == DATATYPE_STRING) {
486
        if (intMapGetByString(macProtoMap, value->c, 1, &res) < 0)
487
            res = -1;
488 489 490
        datatype = DATATYPE_UINT16;
    } else if (datatype == DATATYPE_UINT16 ||
               datatype == DATATYPE_UINT16_HEX) {
491
        res = value->ui;
492 493
        if (res < 0x600)
            res = -1;
494 495 496 497
    }

    if (res != -1) {
        nwf->p.ethHdrFilter.dataProtocolID.u.u16 = res;
498
        nwf->p.ethHdrFilter.dataProtocolID.datatype = datatype;
499
        return true;
500 501
    }

502
    return false;
503 504 505 506 507
}


static bool
macProtocolIDFormatter(virBufferPtr buf,
508 509
                       virNWFilterRuleDefPtr nwf,
                       nwItemDesc *item ATTRIBUTE_UNUSED)
510 511
{
    const char *str = NULL;
512
    bool asHex = true;
513 514 515

    if (intMapGetByInt(macProtoMap,
                       nwf->p.ethHdrFilter.dataProtocolID.u.u16,
516
                       &str) == 0) {
517
        virBufferAdd(buf, str, -1);
518
    } else {
519 520
        if (nwf->p.ethHdrFilter.dataProtocolID.datatype == DATATYPE_UINT16)
            asHex = false;
521
        virBufferAsprintf(buf, asHex ? "0x%x" : "%d",
522
                          nwf->p.ethHdrFilter.dataProtocolID.u.u16);
523
    }
524
    return true;
525 526 527
}


S
Stefan Berger 已提交
528 529 530 531 532 533 534 535
static bool
checkVlanVlanID(enum attrDatatype datatype, union data *value,
                virNWFilterRuleDefPtr nwf,
                nwItemDesc *item ATTRIBUTE_UNUSED)
{
    int32_t res;

    res = value->ui;
536
    if (res < 0 || res > 4095)
S
Stefan Berger 已提交
537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555
        res = -1;

    if (res != -1) {
        nwf->p.vlanHdrFilter.dataVlanID.u.u16 = res;
        nwf->p.vlanHdrFilter.dataVlanID.datatype = datatype;
        return true;
    }

    return false;
}

static bool
checkVlanProtocolID(enum attrDatatype datatype, union data *value,
                    virNWFilterRuleDefPtr nwf,
                    nwItemDesc *item ATTRIBUTE_UNUSED)
{
    int32_t res = -1;

    if (datatype == DATATYPE_STRING) {
556
        if (intMapGetByString(macProtoMap, value->c, 1, &res) < 0)
S
Stefan Berger 已提交
557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584
            res = -1;
        datatype = DATATYPE_UINT16;
    } else if (datatype == DATATYPE_UINT16 ||
               datatype == DATATYPE_UINT16_HEX) {
        res = value->ui;
        if (res < 0x3c)
            res = -1;
    }

    if (res != -1) {
        nwf->p.vlanHdrFilter.dataVlanEncap.u.u16 = res;
        nwf->p.vlanHdrFilter.dataVlanEncap.datatype = datatype;
        return true;
    }

    return false;
}

static bool
vlanProtocolIDFormatter(virBufferPtr buf,
                        virNWFilterRuleDefPtr nwf,
                        nwItemDesc *item ATTRIBUTE_UNUSED)
{
    const char *str = NULL;
    bool asHex = true;

    if (intMapGetByInt(macProtoMap,
                       nwf->p.vlanHdrFilter.dataVlanEncap.u.u16,
585
                       &str) == 0) {
S
Stefan Berger 已提交
586 587 588 589 590 591 592 593 594 595
        virBufferAdd(buf, str, -1);
    } else {
        if (nwf->p.vlanHdrFilter.dataVlanEncap.datatype == DATATYPE_UINT16)
            asHex = false;
        virBufferAsprintf(buf, asHex ? "0x%x" : "%d",
                          nwf->p.vlanHdrFilter.dataVlanEncap.u.u16);
    }
    return true;
}

596 597 598 599 600 601 602 603 604
/* generic function to check for a valid (ipv4,ipv6, mac) mask
 * A mask is valid of there is a sequence of 1's followed by a sequence
 * of 0s or only 1s or only 0s
 */
static bool
checkValidMask(unsigned char *data, int len)
{
    uint32_t idx = 0;
    uint8_t mask = 0x80;
605
    bool checkones = true;
606 607 608 609

    while ((idx >> 3) < len) {
        if (checkones) {
            if (!(data[idx>>3] & mask))
610
                checkones = false;
611 612
        } else {
            if ((data[idx>>3] & mask))
613
                return false;
614 615 616 617 618 619 620
        }

        idx++;
        mask >>= 1;
        if (!mask)
            mask = 0x80;
    }
621
    return true;
622 623 624 625 626
}


static bool
checkMACMask(enum attrDatatype datatype ATTRIBUTE_UNUSED,
627
             union data *macMask,
628 629
             virNWFilterRuleDefPtr nwf ATTRIBUTE_UNUSED,
             nwItemDesc *item ATTRIBUTE_UNUSED)
630
{
631
    return checkValidMask(macMask->uc, 6);
632 633 634 635 636 637 638
}


/*
 * supported arp opcode -- see 'ebtables -h arp' for the naming
 */
static const struct int_map arpOpcodeMap[] = {
639 640 641 642 643 644 645 646 647 648
    INTMAP_ENTRY(1, "Request"),
    INTMAP_ENTRY(2, "Reply"),
    INTMAP_ENTRY(3, "Request_Reverse"),
    INTMAP_ENTRY(4, "Reply_Reverse"),
    INTMAP_ENTRY(5, "DRARP_Request"),
    INTMAP_ENTRY(6, "DRARP_Reply"),
    INTMAP_ENTRY(7, "DRARP_Error"),
    INTMAP_ENTRY(8, "InARP_Request"),
    INTMAP_ENTRY(9, "ARP_NAK"),
    INTMAP_ENTRY_LAST
649 650 651 652 653
};


static bool
arpOpcodeValidator(enum attrDatatype datatype,
654
                   union data *value,
655 656
                   virNWFilterRuleDefPtr nwf,
                   nwItemDesc *item ATTRIBUTE_UNUSED)
657 658 659 660
{
    int32_t res = -1;

    if (datatype == DATATYPE_STRING) {
661
        if (intMapGetByString(arpOpcodeMap, value->c, 1, &res) < 0)
662
            res = -1;
663 664 665
        datatype = DATATYPE_UINT16;
    } else if (datatype == DATATYPE_UINT16 ||
               datatype == DATATYPE_UINT16_HEX) {
666
        res = (uint32_t)value->ui;
667 668 669 670
    }

    if (res != -1) {
        nwf->p.arpHdrFilter.dataOpcode.u.u16 = res;
671
        nwf->p.arpHdrFilter.dataOpcode.datatype = datatype;
672
        return true;
673
    }
674
    return false;
675 676 677 678 679
}


static bool
arpOpcodeFormatter(virBufferPtr buf,
680 681
                   virNWFilterRuleDefPtr nwf,
                   nwItemDesc *item ATTRIBUTE_UNUSED)
682 683 684 685 686
{
    const char *str = NULL;

    if (intMapGetByInt(arpOpcodeMap,
                       nwf->p.arpHdrFilter.dataOpcode.u.u16,
687
                       &str) == 0) {
688
        virBufferAdd(buf, str, -1);
689
    } else {
690
        virBufferAsprintf(buf, "%d", nwf->p.arpHdrFilter.dataOpcode.u.u16);
691
    }
692
    return true;
693 694 695 696
}


static const struct int_map ipProtoMap[] = {
697 698
    INTMAP_ENTRY(IPPROTO_TCP, "tcp"),
    INTMAP_ENTRY(IPPROTO_UDP, "udp"),
699
#ifdef IPPROTO_UDPLITE
700
    INTMAP_ENTRY(IPPROTO_UDPLITE, "udplite"),
701
#endif
702 703 704 705
    INTMAP_ENTRY(IPPROTO_ESP, "esp"),
    INTMAP_ENTRY(IPPROTO_AH,  "ah"),
    INTMAP_ENTRY(IPPROTO_ICMP, "icmp"),
    INTMAP_ENTRY(IPPROTO_IGMP, "igmp"),
706
#ifdef IPPROTO_SCTP
707
    INTMAP_ENTRY(IPPROTO_SCTP, "sctp"),
708
#endif
709 710
    INTMAP_ENTRY(IPPROTO_ICMPV6, "icmpv6"),
    INTMAP_ENTRY_LAST
711 712 713
};


714 715 716 717 718
static bool
checkIPProtocolID(enum attrDatatype datatype,
                  union data *value,
                  virNWFilterRuleDefPtr nwf,
                  nwItemDesc *item ATTRIBUTE_UNUSED)
719 720 721 722
{
    int32_t res = -1;

    if (datatype == DATATYPE_STRING) {
723
        if (intMapGetByString(ipProtoMap, value->c, 1, &res) < 0)
724
            res = -1;
725 726 727
        datatype = DATATYPE_UINT8_HEX;
    } else if (datatype == DATATYPE_UINT8 ||
               datatype == DATATYPE_UINT8_HEX) {
728
        res = (uint32_t)value->ui;
729 730 731 732
    }

    if (res != -1) {
        nwf->p.ipHdrFilter.ipHdr.dataProtocolID.u.u8 = res;
733
        nwf->p.ipHdrFilter.ipHdr.dataProtocolID.datatype = datatype;
734
        return true;
735
    }
736
    return false;
737 738 739 740 741
}


static bool
formatIPProtocolID(virBufferPtr buf,
742 743
                   virNWFilterRuleDefPtr nwf,
                   nwItemDesc *item ATTRIBUTE_UNUSED)
744 745
{
    const char *str = NULL;
746
    bool asHex = true;
747 748 749

    if (intMapGetByInt(ipProtoMap,
                       nwf->p.ipHdrFilter.ipHdr.dataProtocolID.u.u8,
750
                       &str) == 0) {
751
        virBufferAdd(buf, str, -1);
752
    } else {
753 754
        if (nwf->p.ipHdrFilter.ipHdr.dataProtocolID.datatype == DATATYPE_UINT8)
            asHex = false;
755
        virBufferAsprintf(buf, asHex ? "0x%x" : "%d",
756
                          nwf->p.ipHdrFilter.ipHdr.dataProtocolID.u.u8);
757
    }
758
    return true;
759 760 761 762
}


static bool
763
dscpValidator(enum attrDatatype datatype, union data *val,
764 765
              virNWFilterRuleDefPtr nwf,
              nwItemDesc *item ATTRIBUTE_UNUSED)
766
{
767
    uint8_t dscp = val->ui;
768
    if (dscp > 63)
769
        return false;
770

771
    nwf->p.ipHdrFilter.ipHdr.dataDSCP.datatype = datatype;
772

773
    return true;
774 775
}

776 777

static const struct int_map stateMatchMap[] = {
778 779 780 781 782
    INTMAP_ENTRY(RULE_FLAG_STATE_NEW,           "NEW"),
    INTMAP_ENTRY(RULE_FLAG_STATE_ESTABLISHED,   "ESTABLISHED"),
    INTMAP_ENTRY(RULE_FLAG_STATE_RELATED,       "RELATED"),
    INTMAP_ENTRY(RULE_FLAG_STATE_INVALID,       "INVALID"),
    INTMAP_ENTRY(RULE_FLAG_STATE_NONE,          "NONE"),
783 784 785 786 787 788 789 790 791
    INTMAP_ENTRY_LAST,
};


static int
parseStringItems(const struct int_map *int_map,
                 const char *input, int32_t *flags, char sep)
{
    int rc = 0;
792
    size_t i, j;
793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811
    bool found;

    i = 0;
    while (input[i]) {
        found = false;
        while (c_isspace(input[i]) || input[i] == sep)
            i++;
        if (!input[i])
            break;
        for (j = 0; int_map[j].val; j++) {
            if (STRCASEEQLEN(&input[i], int_map[j].val,
                             strlen(int_map[j].val))) {
                *flags |= int_map[j].attr;
                i += strlen(int_map[j].val);
                found = true;
                break;
            }
        }
        if (!found) {
812
            rc = -1;
813 814 815 816 817 818 819 820 821 822 823
            break;
        }
    }
    return rc;
}


static int
printStringItems(virBufferPtr buf, const struct int_map *int_map,
                 int32_t flags, const char *sep)
{
824 825
    size_t i;
    unsigned int c = 0;
826 827 828 829 830 831 832
    int32_t mask = 0x1;

    while (mask) {
        if ((mask & flags)) {
            for (i = 0; int_map[i].val; i++) {
                if (mask == int_map[i].attr) {
                    if (c >= 1)
833 834
                        virBufferAdd(buf, sep, -1);
                    virBufferAdd(buf, int_map[i].val, -1);
835 836 837 838
                    c++;
                }
            }
            flags ^= mask;
839
        }
840 841 842
        if (!flags)
            break;
        mask <<= 1;
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
    }

    return 0;
}


static int
parseStateMatch(const char *statematch, int32_t *flags)
{
    int rc = parseStringItems(stateMatchMap, statematch, flags, ',');

    if ((*flags & RULE_FLAG_STATE_NONE))
        *flags = RULE_FLAG_STATE_NONE;

    return rc;
}


void
virNWFilterPrintStateMatchFlags(virBufferPtr buf, const char *prefix,
                                int32_t flags, bool disp_none)
{
    if (!disp_none && (flags & RULE_FLAG_STATE_NONE))
        return;

868
    virBufferAdd(buf, prefix, -1);
869 870 871 872 873 874 875 876 877 878 879 880 881

    printStringItems(buf, stateMatchMap, flags, ",");
}


static bool
stateValidator(enum attrDatatype datatype ATTRIBUTE_UNUSED, union data *val,
               virNWFilterRuleDefPtr nwf,
               nwItemDesc *item)
{
    char *input = val->c;
    int32_t flags = 0;

882 883
    if (parseStateMatch(input, &flags) < 0)
        return false;
884 885 886 887 888 889

    item->u.u16 = flags;
    nwf->flags |= flags;

    item->datatype = DATATYPE_UINT16;

890
    return true;
891 892 893 894 895 896 897 898 899 900 901 902 903 904
}


static bool
stateFormatter(virBufferPtr buf,
               virNWFilterRuleDefPtr nwf ATTRIBUTE_UNUSED,
               nwItemDesc *item)
{
    virNWFilterPrintStateMatchFlags(buf, "", item->u.u16, true);

    return true;
}


905 906

static const struct int_map tcpFlags[] = {
907 908 909 910
    INTMAP_ENTRY(0x1,  "FIN"),
    INTMAP_ENTRY(0x2,  "SYN"),
    INTMAP_ENTRY(0x4,  "RST"),
    INTMAP_ENTRY(0x8,  "PSH"),
911 912 913
    INTMAP_ENTRY(0x10, "ACK"),
    INTMAP_ENTRY(0x20, "URG"),
    INTMAP_ENTRY(0x3F, "ALL"),
914
    INTMAP_ENTRY(0x0,  "NONE"),
915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936
    INTMAP_ENTRY_LAST
};


static bool
tcpFlagsValidator(enum attrDatatype datatype ATTRIBUTE_UNUSED, union data *val,
                  virNWFilterRuleDefPtr nwf ATTRIBUTE_UNUSED,
                  nwItemDesc *item)
{
    bool rc = false;
    char *s_mask = val->c;
    char *sep = strchr(val->c, '/');
    char *s_flags;
    int32_t mask = 0, flags = 0;

    if (!sep)
        return false;

    s_flags = sep + 1;

    *sep = '\0';

937
    if (parseStringItems(tcpFlags, s_mask, &mask, ',') == 0 &&
938
        parseStringItems(tcpFlags, s_flags, &flags, ',') == 0) {
939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961
        item->u.tcpFlags.mask  = mask  & 0x3f;
        item->u.tcpFlags.flags = flags & 0x3f;
        rc = true;
    }

    *sep = '/';

    return rc;
}


static void
printTCPFlags(virBufferPtr buf, uint8_t flags)
{
    if (flags == 0)
        virBufferAddLit(buf, "NONE");
    else if (flags == 0x3f)
        virBufferAddLit(buf, "ALL");
    else
        printStringItems(buf, tcpFlags, flags, ",");
}


962 963 964 965 966
char *
virNWFilterPrintTCPFlags(uint8_t flags)
{
    virBuffer buf = VIR_BUFFER_INITIALIZER;
    printTCPFlags(&buf, flags);
967
    if (virBufferCheckError(&buf) < 0)
968 969
        return NULL;
    return virBufferContentAndReset(&buf);
970 971 972 973 974 975 976 977
}


static bool
tcpFlagsFormatter(virBufferPtr buf,
                  virNWFilterRuleDefPtr nwf ATTRIBUTE_UNUSED,
                  nwItemDesc *item)
{
978 979 980
    printTCPFlags(buf, item->u.tcpFlags.mask);
    virBufferAddLit(buf, "/");
    printTCPFlags(buf, item->u.tcpFlags.flags);
981 982 983 984

    return true;
}

S
Stefan Berger 已提交
985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005
static bool
ipsetValidator(enum attrDatatype datatype ATTRIBUTE_UNUSED, union data *val,
               virNWFilterRuleDefPtr nwf ATTRIBUTE_UNUSED,
               nwItemDesc *item)
{
    const char *errmsg = NULL;

    if (virStrcpy(item->u.ipset.setname, val->c,
                  sizeof(item->u.ipset.setname)) == NULL) {
        errmsg = _("ipset name is too long");
        goto arg_err_exit;
    }

    if (item->u.ipset.setname[strspn(item->u.ipset.setname,
                                     VALID_IPSETNAME)] != 0) {
        errmsg = _("ipset name contains invalid characters");
        goto arg_err_exit;
    }

    return true;

1006
 arg_err_exit:
1007 1008
    virReportError(VIR_ERR_INVALID_ARG,
                   "%s", errmsg);
S
Stefan Berger 已提交
1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 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
    return false;
}

static bool
ipsetFormatter(virBufferPtr buf,
               virNWFilterRuleDefPtr nwf ATTRIBUTE_UNUSED,
               nwItemDesc *item)
{
    virBufferAdd(buf, item->u.ipset.setname, -1);

    return true;
}

static bool
ipsetFlagsValidator(enum attrDatatype datatype ATTRIBUTE_UNUSED, union data *val,
                    virNWFilterRuleDefPtr nwf ATTRIBUTE_UNUSED, nwItemDesc *item)
{
    const char *errmsg = NULL;
    size_t idx = 0;

    item->u.ipset.numFlags = 0;
    item->u.ipset.flags = 0;

    errmsg = _("malformed ipset flags");

    while (item->u.ipset.numFlags < 6) {
        if (STRCASEEQLEN(&val->c[idx], "src", 3)) {
            item->u.ipset.flags |= (1 << item->u.ipset.numFlags);
        } else if (!STRCASEEQLEN(&val->c[idx], "dst", 3)) {
            goto arg_err_exit;
        }
        item->u.ipset.numFlags++;
        idx += 3;
        if (val->c[idx] != ',')
            break;
        idx++;
    }

    if (val->c[idx] != '\0')
        goto arg_err_exit;

    return true;

1052
 arg_err_exit:
1053 1054
    virReportError(VIR_ERR_INVALID_ARG,
                   "%s", errmsg);
S
Stefan Berger 已提交
1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075
    return false;
}

static bool
ipsetFlagsFormatter(virBufferPtr buf,
                    virNWFilterRuleDefPtr nwf ATTRIBUTE_UNUSED,
                    nwItemDesc *item)
{
    uint8_t ctr;

    for (ctr = 0; ctr < item->u.ipset.numFlags; ctr++) {
        if (ctr != 0)
            virBufferAddLit(buf, ",");
        if ((item->u.ipset.flags & (1 << ctr)))
            virBufferAddLit(buf, "src");
        else
            virBufferAddLit(buf, "dst");
    }

    return true;
}
1076

1077 1078 1079 1080
#define COMMON_MAC_PROPS(STRUCT) \
    {\
        .name = SRCMACADDR,\
        .datatype = DATATYPE_MACADDR,\
1081 1082
            .dataIdx = offsetof(virNWFilterRuleDef,\
                            p.STRUCT.ethHdr.dataSrcMACAddr),\
1083 1084 1085 1086
    },\
    {\
        .name = SRCMACMASK,\
        .datatype = DATATYPE_MACMASK,\
1087 1088
        .dataIdx = offsetof(virNWFilterRuleDef,\
                            p.STRUCT.ethHdr.dataSrcMACMask),\
1089 1090 1091 1092
    },\
    {\
        .name = DSTMACADDR,\
        .datatype = DATATYPE_MACADDR,\
1093 1094
        .dataIdx = offsetof(virNWFilterRuleDef,\
                            p.STRUCT.ethHdr.dataDstMACAddr),\
1095 1096 1097 1098
    },\
    {\
        .name = DSTMACMASK,\
        .datatype = DATATYPE_MACMASK,\
1099 1100
        .dataIdx = offsetof(virNWFilterRuleDef,\
                            p.STRUCT.ethHdr.dataDstMACMask),\
1101 1102 1103
    }


1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115
#define COMMENT_PROP(STRUCT) \
    {\
        .name = "comment",\
        .datatype = DATATYPE_STRINGCOPY,\
        .dataIdx = offsetof(virNWFilterRuleDef, p.STRUCT.dataComment),\
        .maxstrlen = MAX_COMMENT_LENGTH,\
    }

#define COMMENT_PROP_IPHDR(STRUCT) \
    COMMENT_PROP(STRUCT.ipHdr)


1116 1117 1118 1119
static const virXMLAttr2Struct macAttributes[] = {
    COMMON_MAC_PROPS(ethHdrFilter),
    {
        .name = "protocolid",
1120
        .datatype = DATATYPE_UINT16 | DATATYPE_UINT16_HEX | DATATYPE_STRING,
1121
        .dataIdx = offsetof(virNWFilterRuleDef, p.ethHdrFilter.dataProtocolID),
1122 1123
        .validator = checkMacProtocolID,
        .formatter = macProtocolIDFormatter,
1124
    },
1125
    COMMENT_PROP(ethHdrFilter),
1126 1127 1128 1129 1130
    {
        .name = NULL,
    }
};

S
Stefan Berger 已提交
1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151
static const virXMLAttr2Struct vlanAttributes[] = {
    COMMON_MAC_PROPS(ethHdrFilter),
    {
        .name = "vlanid",
        .datatype = DATATYPE_UINT16 | DATATYPE_UINT16_HEX,
        .dataIdx = offsetof(virNWFilterRuleDef, p.vlanHdrFilter.dataVlanID),
        .validator = checkVlanVlanID,
    },
    {
        .name = "encap-protocol",
        .datatype = DATATYPE_UINT16 | DATATYPE_UINT16_HEX | DATATYPE_STRING,
        .dataIdx = offsetof(virNWFilterRuleDef, p.vlanHdrFilter.dataVlanEncap),
        .validator = checkVlanProtocolID,
        .formatter = vlanProtocolIDFormatter,
    },
    COMMENT_PROP(vlanHdrFilter),
    {
        .name = NULL,
    }
};

S
Stefan Berger 已提交
1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283
/* STP is documented by IEEE 802.1D; for a synopsis,
 * see http://www.javvin.com/protocolSTP.html */
static const virXMLAttr2Struct stpAttributes[] = {
    /* spanning tree uses a special destination MAC address */
    {
        .name = SRCMACADDR,
        .datatype = DATATYPE_MACADDR,
        .dataIdx = offsetof(virNWFilterRuleDef,
                            p.stpHdrFilter.ethHdr.dataSrcMACAddr),
    },
    {
        .name = SRCMACMASK,
        .datatype = DATATYPE_MACMASK,
        .dataIdx = offsetof(virNWFilterRuleDef,
                            p.stpHdrFilter.ethHdr.dataSrcMACMask),
    },
    {
        .name = "type",
        .datatype = DATATYPE_UINT8 | DATATYPE_UINT8_HEX,
        .dataIdx = offsetof(virNWFilterRuleDef, p.stpHdrFilter.dataType),
    },
    {
        .name = "flags",
        .datatype = DATATYPE_UINT8 | DATATYPE_UINT8_HEX,
        .dataIdx = offsetof(virNWFilterRuleDef, p.stpHdrFilter.dataFlags),
    },
    {
        .name = "root-priority",
        .datatype = DATATYPE_UINT16 | DATATYPE_UINT16_HEX,
        .dataIdx = offsetof(virNWFilterRuleDef, p.stpHdrFilter.dataRootPri),
    },
    {
        .name = "root-priority-hi",
        .datatype = DATATYPE_UINT16 | DATATYPE_UINT16_HEX,
        .dataIdx = offsetof(virNWFilterRuleDef, p.stpHdrFilter.dataRootPriHi),
    },
    {
        .name = "root-address",
        .datatype = DATATYPE_MACADDR,
        .dataIdx = offsetof(virNWFilterRuleDef, p.stpHdrFilter.dataRootAddr),
    },
    {
        .name = "root-address-mask",
        .datatype = DATATYPE_MACMASK,
        .dataIdx = offsetof(virNWFilterRuleDef, p.stpHdrFilter.dataRootAddrMask),
    },
    {
        .name = "root-cost",
        .datatype = DATATYPE_UINT32 | DATATYPE_UINT32_HEX,
        .dataIdx = offsetof(virNWFilterRuleDef, p.stpHdrFilter.dataRootCost),
    },
    {
        .name = "root-cost-hi",
        .datatype = DATATYPE_UINT32 | DATATYPE_UINT32_HEX,
        .dataIdx = offsetof(virNWFilterRuleDef, p.stpHdrFilter.dataRootCostHi),
    },
    {
        .name = "sender-priority",
        .datatype = DATATYPE_UINT16 | DATATYPE_UINT16_HEX,
        .dataIdx = offsetof(virNWFilterRuleDef, p.stpHdrFilter.dataSndrPrio),
    },
    {
        .name = "sender-priority-hi",
        .datatype = DATATYPE_UINT16 | DATATYPE_UINT16_HEX,
        .dataIdx = offsetof(virNWFilterRuleDef, p.stpHdrFilter.dataSndrPrioHi),
    },
    {
        .name = "sender-address",
        .datatype = DATATYPE_MACADDR,
        .dataIdx = offsetof(virNWFilterRuleDef, p.stpHdrFilter.dataSndrAddr),
    },
    {
        .name = "sender-address-mask",
        .datatype = DATATYPE_MACMASK,
        .dataIdx = offsetof(virNWFilterRuleDef, p.stpHdrFilter.dataSndrAddrMask),
    },
    {
        .name = "port",
        .datatype = DATATYPE_UINT16 | DATATYPE_UINT16_HEX,
        .dataIdx = offsetof(virNWFilterRuleDef, p.stpHdrFilter.dataPort),
    },
    {
        .name = "port-hi",
        .datatype = DATATYPE_UINT16 | DATATYPE_UINT16_HEX,
        .dataIdx = offsetof(virNWFilterRuleDef, p.stpHdrFilter.dataPortHi),
    },
    {
        .name = "age",
        .datatype = DATATYPE_UINT16 | DATATYPE_UINT16_HEX,
        .dataIdx = offsetof(virNWFilterRuleDef, p.stpHdrFilter.dataAge),
    },
    {
        .name = "age-hi",
        .datatype = DATATYPE_UINT16 | DATATYPE_UINT16_HEX,
        .dataIdx = offsetof(virNWFilterRuleDef, p.stpHdrFilter.dataAgeHi),
    },
    {
        .name = "max-age",
        .datatype = DATATYPE_UINT16 | DATATYPE_UINT16_HEX,
        .dataIdx = offsetof(virNWFilterRuleDef, p.stpHdrFilter.dataMaxAge),
    },
    {
        .name = "max-age-hi",
        .datatype = DATATYPE_UINT16 | DATATYPE_UINT16_HEX,
        .dataIdx = offsetof(virNWFilterRuleDef, p.stpHdrFilter.dataMaxAgeHi),
    },
    {
        .name = "hello-time",
        .datatype = DATATYPE_UINT16 | DATATYPE_UINT16_HEX,
        .dataIdx = offsetof(virNWFilterRuleDef, p.stpHdrFilter.dataHelloTime),
    },
    {
        .name = "hello-time-hi",
        .datatype = DATATYPE_UINT16 | DATATYPE_UINT16_HEX,
        .dataIdx = offsetof(virNWFilterRuleDef, p.stpHdrFilter.dataHelloTimeHi),
    },
    {
        .name = "forward-delay",
        .datatype = DATATYPE_UINT16 | DATATYPE_UINT16_HEX,
        .dataIdx = offsetof(virNWFilterRuleDef, p.stpHdrFilter.dataFwdDelay),
    },
    {
        .name = "forward-delay-hi",
        .datatype = DATATYPE_UINT16 | DATATYPE_UINT16_HEX,
        .dataIdx = offsetof(virNWFilterRuleDef, p.stpHdrFilter.dataFwdDelayHi),
    },
    COMMENT_PROP(stpHdrFilter),
    {
        .name = NULL,
    }
};

1284 1285 1286 1287
static const virXMLAttr2Struct arpAttributes[] = {
    COMMON_MAC_PROPS(arpHdrFilter),
    {
        .name = "hwtype",
1288
        .datatype = DATATYPE_UINT16 | DATATYPE_UINT16_HEX,
1289 1290 1291
        .dataIdx = offsetof(virNWFilterRuleDef, p.arpHdrFilter.dataHWType),
    }, {
        .name = "protocoltype",
1292
        .datatype = DATATYPE_UINT16 | DATATYPE_UINT16_HEX,
1293 1294 1295
        .dataIdx = offsetof(virNWFilterRuleDef, p.arpHdrFilter.dataProtocolType),
    }, {
        .name = "opcode",
1296
        .datatype = DATATYPE_UINT16 | DATATYPE_UINT16_HEX | DATATYPE_STRING,
1297
        .dataIdx = offsetof(virNWFilterRuleDef, p.arpHdrFilter.dataOpcode),
1298 1299
        .validator = arpOpcodeValidator,
        .formatter = arpOpcodeFormatter,
1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311
    }, {
        .name = ARPSRCMACADDR,
        .datatype = DATATYPE_MACADDR,
        .dataIdx = offsetof(virNWFilterRuleDef, p.arpHdrFilter.dataARPSrcMACAddr),
    }, {
        .name = ARPDSTMACADDR,
        .datatype = DATATYPE_MACADDR,
        .dataIdx = offsetof(virNWFilterRuleDef, p.arpHdrFilter.dataARPDstMACAddr),
    }, {
        .name = ARPSRCIPADDR,
        .datatype = DATATYPE_IPADDR,
        .dataIdx = offsetof(virNWFilterRuleDef, p.arpHdrFilter.dataARPSrcIPAddr),
1312 1313 1314 1315
    }, {
        .name = ARPSRCIPMASK,
        .datatype = DATATYPE_IPMASK,
        .dataIdx = offsetof(virNWFilterRuleDef, p.arpHdrFilter.dataARPSrcIPMask),
1316 1317 1318 1319
    }, {
        .name = ARPDSTIPADDR,
        .datatype = DATATYPE_IPADDR,
        .dataIdx = offsetof(virNWFilterRuleDef, p.arpHdrFilter.dataARPDstIPAddr),
1320 1321 1322 1323
    }, {
        .name = ARPDSTIPMASK,
        .datatype = DATATYPE_IPMASK,
        .dataIdx = offsetof(virNWFilterRuleDef, p.arpHdrFilter.dataARPDstIPMask),
1324 1325 1326 1327
    }, {
        .name = "gratuitous",
        .datatype = DATATYPE_BOOLEAN,
        .dataIdx = offsetof(virNWFilterRuleDef, p.arpHdrFilter.dataGratuitousARP),
1328
    },
1329
    COMMENT_PROP(arpHdrFilter),
1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346
    {
        .name = NULL,
    }
};

static const virXMLAttr2Struct ipAttributes[] = {
    COMMON_MAC_PROPS(ipHdrFilter),
    {
        .name = SRCIPADDR,
        .datatype = DATATYPE_IPADDR,
        .dataIdx = offsetof(virNWFilterRuleDef, p.ipHdrFilter.ipHdr.dataSrcIPAddr),
    },
    {
        .name = SRCIPMASK,
        .datatype = DATATYPE_IPMASK,
        .dataIdx = offsetof(virNWFilterRuleDef, p.ipHdrFilter.ipHdr.dataSrcIPMask),
    },
1347 1348 1349 1350 1351
    {
        .name = DSTIPADDR,
        .datatype = DATATYPE_IPADDR,
        .dataIdx = offsetof(virNWFilterRuleDef, p.ipHdrFilter.ipHdr.dataDstIPAddr),
    },
1352 1353 1354 1355 1356 1357 1358
    {
        .name = DSTIPMASK,
        .datatype = DATATYPE_IPMASK,
        .dataIdx = offsetof(virNWFilterRuleDef, p.ipHdrFilter.ipHdr.dataDstIPMask),
    },
    {
        .name = "protocol",
1359
        .datatype = DATATYPE_STRING | DATATYPE_UINT8 | DATATYPE_UINT8_HEX,
1360
        .dataIdx = offsetof(virNWFilterRuleDef, p.ipHdrFilter.ipHdr.dataProtocolID),
1361 1362
        .validator = checkIPProtocolID,
        .formatter = formatIPProtocolID,
1363 1364 1365
    },
    {
        .name = SRCPORTSTART,
1366
        .datatype = DATATYPE_UINT16 | DATATYPE_UINT16_HEX,
1367 1368 1369 1370
        .dataIdx = offsetof(virNWFilterRuleDef, p.ipHdrFilter.portData.dataSrcPortStart),
    },
    {
        .name = SRCPORTEND,
1371
        .datatype = DATATYPE_UINT16 | DATATYPE_UINT16_HEX,
1372 1373 1374 1375
        .dataIdx = offsetof(virNWFilterRuleDef, p.ipHdrFilter.portData.dataSrcPortEnd),
    },
    {
        .name = DSTPORTSTART,
1376
        .datatype = DATATYPE_UINT16 | DATATYPE_UINT16_HEX,
1377 1378 1379 1380
        .dataIdx = offsetof(virNWFilterRuleDef, p.ipHdrFilter.portData.dataDstPortStart),
    },
    {
        .name = DSTPORTEND,
1381
        .datatype = DATATYPE_UINT16 | DATATYPE_UINT16_HEX,
1382 1383 1384 1385
        .dataIdx = offsetof(virNWFilterRuleDef, p.ipHdrFilter.portData.dataDstPortEnd),
    },
    {
        .name = DSCP,
1386
        .datatype = DATATYPE_UINT8 | DATATYPE_UINT8_HEX,
1387 1388 1389
        .dataIdx = offsetof(virNWFilterRuleDef, p.ipHdrFilter.ipHdr.dataDSCP),
        .validator = dscpValidator,
    },
1390
    COMMENT_PROP_IPHDR(ipHdrFilter),
1391 1392 1393 1394 1395 1396
    {
        .name = NULL,
    }
};


1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408
static const virXMLAttr2Struct ipv6Attributes[] = {
    COMMON_MAC_PROPS(ipv6HdrFilter),
    {
        .name = SRCIPADDR,
        .datatype = DATATYPE_IPV6ADDR,
        .dataIdx = offsetof(virNWFilterRuleDef, p.ipv6HdrFilter.ipHdr.dataSrcIPAddr),
    },
    {
        .name = SRCIPMASK,
        .datatype = DATATYPE_IPV6MASK,
        .dataIdx = offsetof(virNWFilterRuleDef, p.ipv6HdrFilter.ipHdr.dataSrcIPMask),
    },
1409 1410 1411 1412 1413
    {
        .name = DSTIPADDR,
        .datatype = DATATYPE_IPV6ADDR,
        .dataIdx = offsetof(virNWFilterRuleDef, p.ipv6HdrFilter.ipHdr.dataDstIPAddr),
    },
1414 1415 1416 1417 1418 1419 1420
    {
        .name = DSTIPMASK,
        .datatype = DATATYPE_IPV6MASK,
        .dataIdx = offsetof(virNWFilterRuleDef, p.ipv6HdrFilter.ipHdr.dataDstIPMask),
    },
    {
        .name = "protocol",
1421
        .datatype = DATATYPE_STRING | DATATYPE_UINT8 | DATATYPE_UINT8_HEX,
1422
        .dataIdx = offsetof(virNWFilterRuleDef, p.ipv6HdrFilter.ipHdr.dataProtocolID),
1423 1424
        .validator = checkIPProtocolID,
        .formatter = formatIPProtocolID,
1425 1426 1427
    },
    {
        .name = SRCPORTSTART,
1428
        .datatype = DATATYPE_UINT16 | DATATYPE_UINT16_HEX,
1429 1430 1431 1432
        .dataIdx = offsetof(virNWFilterRuleDef, p.ipv6HdrFilter.portData.dataSrcPortStart),
    },
    {
        .name = SRCPORTEND,
1433
        .datatype = DATATYPE_UINT16 | DATATYPE_UINT16_HEX,
1434 1435 1436 1437
        .dataIdx = offsetof(virNWFilterRuleDef, p.ipv6HdrFilter.portData.dataSrcPortEnd),
    },
    {
        .name = DSTPORTSTART,
1438
        .datatype = DATATYPE_UINT16 | DATATYPE_UINT16_HEX,
1439 1440 1441 1442
        .dataIdx = offsetof(virNWFilterRuleDef, p.ipv6HdrFilter.portData.dataDstPortStart),
    },
    {
        .name = DSTPORTEND,
1443
        .datatype = DATATYPE_UINT16 | DATATYPE_UINT16_HEX,
1444 1445
        .dataIdx = offsetof(virNWFilterRuleDef, p.ipv6HdrFilter.portData.dataDstPortEnd),
    },
1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465
    {
        .name = "type",
        .datatype = DATATYPE_UINT8 | DATATYPE_UINT8_HEX,
        .dataIdx = offsetof(virNWFilterRuleDef, p.ipv6HdrFilter.dataICMPTypeStart),
    },
    {
        .name = "typeend",
        .datatype = DATATYPE_UINT8 | DATATYPE_UINT8_HEX,
        .dataIdx = offsetof(virNWFilterRuleDef, p.ipv6HdrFilter.dataICMPTypeEnd),
    },
    {
        .name = "code",
        .datatype = DATATYPE_UINT8 | DATATYPE_UINT8_HEX,
        .dataIdx = offsetof(virNWFilterRuleDef, p.ipv6HdrFilter.dataICMPCodeStart),
    },
    {
        .name = "codeend",
        .datatype = DATATYPE_UINT8 | DATATYPE_UINT8_HEX,
        .dataIdx = offsetof(virNWFilterRuleDef, p.ipv6HdrFilter.dataICMPCodeEnd),
    },
1466
    COMMENT_PROP_IPHDR(ipv6HdrFilter),
1467 1468 1469 1470 1471 1472
    {
        .name = NULL,
    }
};


S
Stefan Berger 已提交
1473 1474 1475 1476 1477 1478 1479
#define COMMON_L3_MAC_PROPS(STRUCT) \
    {\
        .name = SRCMACADDR,\
        .datatype = DATATYPE_MACADDR,\
        .dataIdx = offsetof(virNWFilterRuleDef, p.STRUCT.dataSrcMACAddr),\
    }

1480
#define COMMON_IP_PROPS(STRUCT, ADDRTYPE, MASKTYPE) \
S
Stefan Berger 已提交
1481 1482 1483
    COMMON_L3_MAC_PROPS(STRUCT),\
    {\
        .name = SRCIPADDR,\
1484
        .datatype = ADDRTYPE,\
S
Stefan Berger 已提交
1485 1486 1487 1488
        .dataIdx = offsetof(virNWFilterRuleDef, p.STRUCT.ipHdr.dataSrcIPAddr),\
    },\
    {\
        .name = SRCIPMASK,\
1489
        .datatype = MASKTYPE,\
S
Stefan Berger 已提交
1490 1491
        .dataIdx = offsetof(virNWFilterRuleDef, p.STRUCT.ipHdr.dataSrcIPMask),\
    },\
1492 1493 1494 1495 1496
    {\
        .name = DSTIPADDR,\
        .datatype = ADDRTYPE,\
        .dataIdx = offsetof(virNWFilterRuleDef, p.STRUCT.ipHdr.dataDstIPAddr),\
    },\
S
Stefan Berger 已提交
1497 1498
    {\
        .name = DSTIPMASK,\
1499
        .datatype = MASKTYPE,\
S
Stefan Berger 已提交
1500 1501 1502 1503
        .dataIdx = offsetof(virNWFilterRuleDef, p.STRUCT.ipHdr.dataDstIPMask),\
    },\
    {\
        .name = SRCIPFROM,\
1504
        .datatype = ADDRTYPE,\
S
Stefan Berger 已提交
1505 1506 1507 1508
        .dataIdx = offsetof(virNWFilterRuleDef, p.STRUCT.ipHdr.dataSrcIPFrom),\
    },\
    {\
        .name = SRCIPTO,\
1509
        .datatype = ADDRTYPE,\
S
Stefan Berger 已提交
1510 1511 1512 1513
        .dataIdx = offsetof(virNWFilterRuleDef, p.STRUCT.ipHdr.dataSrcIPTo),\
    },\
    {\
        .name = DSTIPFROM,\
1514
        .datatype = ADDRTYPE,\
S
Stefan Berger 已提交
1515 1516 1517 1518 1519 1520 1521 1522 1523
        .dataIdx = offsetof(virNWFilterRuleDef, p.STRUCT.ipHdr.dataDstIPFrom),\
    },\
    {\
        .name = DSTIPTO,\
        .datatype = DATATYPE_IPADDR,\
        .dataIdx = offsetof(virNWFilterRuleDef, p.STRUCT.ipHdr.dataDstIPTo),\
    },\
    {\
        .name = DSCP,\
1524
        .datatype = DATATYPE_UINT8 | DATATYPE_UINT8_HEX,\
S
Stefan Berger 已提交
1525
        .dataIdx = offsetof(virNWFilterRuleDef, p.STRUCT.ipHdr.dataDSCP),\
1526
        .validator = dscpValidator,\
1527 1528 1529 1530 1531
    },\
    {\
        .name = "connlimit-above",\
        .datatype = DATATYPE_UINT16,\
        .dataIdx = offsetof(virNWFilterRuleDef, p.STRUCT.ipHdr.dataConnlimitAbove),\
1532 1533 1534 1535 1536 1537 1538
    },\
    {\
        .name = STATE,\
        .datatype = DATATYPE_STRING,\
        .dataIdx = offsetof(virNWFilterRuleDef, p.STRUCT.ipHdr.dataState),\
        .validator = stateValidator,\
        .formatter = stateFormatter,\
S
Stefan Berger 已提交
1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552
    },\
    {\
        .name = IPSET,\
        .datatype = DATATYPE_IPSETNAME,\
        .dataIdx = offsetof(virNWFilterRuleDef, p.STRUCT.ipHdr.dataIPSet),\
        .validator = ipsetValidator,\
        .formatter = ipsetFormatter,\
    },\
    {\
        .name = IPSETFLAGS,\
        .datatype = DATATYPE_IPSETFLAGS,\
        .dataIdx = offsetof(virNWFilterRuleDef, p.STRUCT.ipHdr.dataIPSetFlags),\
        .validator = ipsetFlagsValidator,\
        .formatter = ipsetFlagsFormatter,\
S
Stefan Berger 已提交
1553 1554 1555 1556 1557
    }

#define COMMON_PORT_PROPS(STRUCT) \
    {\
        .name = SRCPORTSTART,\
1558
        .datatype = DATATYPE_UINT16 | DATATYPE_UINT16_HEX,\
S
Stefan Berger 已提交
1559 1560 1561 1562
        .dataIdx = offsetof(virNWFilterRuleDef, p.STRUCT.portData.dataSrcPortStart),\
    },\
    {\
        .name = SRCPORTEND,\
1563
        .datatype = DATATYPE_UINT16 | DATATYPE_UINT16_HEX,\
S
Stefan Berger 已提交
1564 1565 1566 1567
        .dataIdx = offsetof(virNWFilterRuleDef, p.STRUCT.portData.dataSrcPortEnd),\
    },\
    {\
        .name = DSTPORTSTART,\
1568
        .datatype = DATATYPE_UINT16 | DATATYPE_UINT16_HEX,\
S
Stefan Berger 已提交
1569 1570 1571 1572
        .dataIdx = offsetof(virNWFilterRuleDef, p.STRUCT.portData.dataDstPortStart),\
    },\
    {\
        .name = DSTPORTEND,\
1573
        .datatype = DATATYPE_UINT16 | DATATYPE_UINT16_HEX,\
S
Stefan Berger 已提交
1574 1575 1576 1577
        .dataIdx = offsetof(virNWFilterRuleDef, p.STRUCT.portData.dataDstPortEnd),\
    }

static const virXMLAttr2Struct tcpAttributes[] = {
1578
    COMMON_IP_PROPS(tcpHdrFilter, DATATYPE_IPADDR, DATATYPE_IPMASK),
S
Stefan Berger 已提交
1579 1580 1581
    COMMON_PORT_PROPS(tcpHdrFilter),
    {
        .name = "option",
1582
        .datatype = DATATYPE_UINT8 | DATATYPE_UINT8_HEX,
S
Stefan Berger 已提交
1583 1584
        .dataIdx = offsetof(virNWFilterRuleDef, p.tcpHdrFilter.dataTCPOption),
    },
1585 1586 1587 1588 1589 1590 1591
    {
        .name = "flags",
        .datatype = DATATYPE_STRING,
        .dataIdx = offsetof(virNWFilterRuleDef, p.tcpHdrFilter.dataTCPFlags),
        .validator = tcpFlagsValidator,
        .formatter = tcpFlagsFormatter,
    },
1592
    COMMENT_PROP_IPHDR(tcpHdrFilter),
S
Stefan Berger 已提交
1593 1594 1595 1596 1597 1598
    {
        .name = NULL,
    }
};

static const virXMLAttr2Struct udpAttributes[] = {
1599
    COMMON_IP_PROPS(udpHdrFilter, DATATYPE_IPADDR, DATATYPE_IPMASK),
S
Stefan Berger 已提交
1600
    COMMON_PORT_PROPS(udpHdrFilter),
1601
    COMMENT_PROP_IPHDR(udpHdrFilter),
S
Stefan Berger 已提交
1602 1603 1604 1605 1606
    {
        .name = NULL,
    }
};

1607
static const virXMLAttr2Struct udpliteAttributes[] = {
1608
    COMMON_IP_PROPS(udpliteHdrFilter, DATATYPE_IPADDR, DATATYPE_IPMASK),
1609
    COMMENT_PROP_IPHDR(udpliteHdrFilter),
1610 1611 1612 1613 1614 1615
    {
        .name = NULL,
    }
};

static const virXMLAttr2Struct espAttributes[] = {
1616
    COMMON_IP_PROPS(espHdrFilter, DATATYPE_IPADDR, DATATYPE_IPMASK),
1617
    COMMENT_PROP_IPHDR(espHdrFilter),
1618 1619 1620 1621 1622 1623
    {
        .name = NULL,
    }
};

static const virXMLAttr2Struct ahAttributes[] = {
1624
    COMMON_IP_PROPS(ahHdrFilter, DATATYPE_IPADDR, DATATYPE_IPMASK),
1625
    COMMENT_PROP_IPHDR(ahHdrFilter),
1626 1627 1628 1629
    {
        .name = NULL,
    }
};
S
Stefan Berger 已提交
1630 1631

static const virXMLAttr2Struct sctpAttributes[] = {
1632
    COMMON_IP_PROPS(sctpHdrFilter, DATATYPE_IPADDR, DATATYPE_IPMASK),
S
Stefan Berger 已提交
1633
    COMMON_PORT_PROPS(sctpHdrFilter),
1634
    COMMENT_PROP_IPHDR(sctpHdrFilter),
S
Stefan Berger 已提交
1635 1636 1637 1638 1639 1640 1641
    {
        .name = NULL,
    }
};


static const virXMLAttr2Struct icmpAttributes[] = {
1642
    COMMON_IP_PROPS(icmpHdrFilter, DATATYPE_IPADDR, DATATYPE_IPMASK),
S
Stefan Berger 已提交
1643 1644
    {
        .name = "type",
1645
        .datatype = DATATYPE_UINT8 | DATATYPE_UINT8_HEX,
S
Stefan Berger 已提交
1646 1647 1648 1649
        .dataIdx = offsetof(virNWFilterRuleDef, p.icmpHdrFilter.dataICMPType),
    },
    {
        .name = "code",
1650
        .datatype = DATATYPE_UINT8 | DATATYPE_UINT8_HEX,
S
Stefan Berger 已提交
1651 1652
        .dataIdx = offsetof(virNWFilterRuleDef, p.icmpHdrFilter.dataICMPCode),
    },
1653
    COMMENT_PROP_IPHDR(icmpHdrFilter),
S
Stefan Berger 已提交
1654 1655 1656 1657 1658 1659 1660
    {
        .name = NULL,
    }
};


static const virXMLAttr2Struct allAttributes[] = {
1661
    COMMON_IP_PROPS(allHdrFilter, DATATYPE_IPADDR, DATATYPE_IPMASK),
1662
    COMMENT_PROP_IPHDR(allHdrFilter),
S
Stefan Berger 已提交
1663 1664 1665 1666 1667 1668 1669
    {
        .name = NULL,
    }
};


static const virXMLAttr2Struct igmpAttributes[] = {
1670
    COMMON_IP_PROPS(igmpHdrFilter, DATATYPE_IPADDR, DATATYPE_IPMASK),
1671
    COMMENT_PROP_IPHDR(igmpHdrFilter),
1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682
    {
        .name = NULL,
    }
};


static const virXMLAttr2Struct tcpipv6Attributes[] = {
    COMMON_IP_PROPS(tcpHdrFilter, DATATYPE_IPV6ADDR, DATATYPE_IPV6MASK),
    COMMON_PORT_PROPS(tcpHdrFilter),
    {
        .name = "option",
1683
        .datatype = DATATYPE_UINT8 | DATATYPE_UINT8_HEX,
1684 1685
        .dataIdx = offsetof(virNWFilterRuleDef, p.tcpHdrFilter.dataTCPOption),
    },
1686
    COMMENT_PROP_IPHDR(tcpHdrFilter),
1687 1688 1689 1690 1691 1692 1693 1694
    {
        .name = NULL,
    }
};

static const virXMLAttr2Struct udpipv6Attributes[] = {
    COMMON_IP_PROPS(udpHdrFilter, DATATYPE_IPV6ADDR, DATATYPE_IPV6MASK),
    COMMON_PORT_PROPS(udpHdrFilter),
1695
    COMMENT_PROP_IPHDR(udpHdrFilter),
1696 1697 1698 1699 1700 1701 1702 1703
    {
        .name = NULL,
    }
};


static const virXMLAttr2Struct udpliteipv6Attributes[] = {
    COMMON_IP_PROPS(udpliteHdrFilter, DATATYPE_IPV6ADDR, DATATYPE_IPV6MASK),
1704
    COMMENT_PROP_IPHDR(udpliteHdrFilter),
1705 1706 1707 1708 1709 1710 1711 1712
    {
        .name = NULL,
    }
};


static const virXMLAttr2Struct espipv6Attributes[] = {
    COMMON_IP_PROPS(espHdrFilter, DATATYPE_IPV6ADDR, DATATYPE_IPV6MASK),
1713
    COMMENT_PROP_IPHDR(espHdrFilter),
1714 1715 1716 1717 1718 1719 1720 1721
    {
        .name = NULL,
    }
};


static const virXMLAttr2Struct ahipv6Attributes[] = {
    COMMON_IP_PROPS(ahHdrFilter, DATATYPE_IPV6ADDR, DATATYPE_IPV6MASK),
1722
    COMMENT_PROP_IPHDR(ahHdrFilter),
1723 1724 1725 1726 1727 1728 1729 1730 1731
    {
        .name = NULL,
    }
};


static const virXMLAttr2Struct sctpipv6Attributes[] = {
    COMMON_IP_PROPS(sctpHdrFilter, DATATYPE_IPV6ADDR, DATATYPE_IPV6MASK),
    COMMON_PORT_PROPS(sctpHdrFilter),
1732
    COMMENT_PROP_IPHDR(sctpHdrFilter),
1733 1734 1735 1736 1737 1738 1739 1740 1741 1742
    {
        .name = NULL,
    }
};


static const virXMLAttr2Struct icmpv6Attributes[] = {
    COMMON_IP_PROPS(icmpHdrFilter, DATATYPE_IPV6ADDR, DATATYPE_IPV6MASK),
    {
        .name = "type",
1743
        .datatype = DATATYPE_UINT8 | DATATYPE_UINT8_HEX,
1744 1745 1746 1747
        .dataIdx = offsetof(virNWFilterRuleDef, p.icmpHdrFilter.dataICMPType),
    },
    {
        .name = "code",
1748
        .datatype = DATATYPE_UINT8 | DATATYPE_UINT8_HEX,
1749 1750
        .dataIdx = offsetof(virNWFilterRuleDef, p.icmpHdrFilter.dataICMPCode),
    },
1751
    COMMENT_PROP_IPHDR(icmpHdrFilter),
1752 1753 1754 1755 1756 1757 1758 1759
    {
        .name = NULL,
    }
};


static const virXMLAttr2Struct allipv6Attributes[] = {
    COMMON_IP_PROPS(allHdrFilter, DATATYPE_IPV6ADDR, DATATYPE_IPV6MASK),
1760
    COMMENT_PROP_IPHDR(allHdrFilter),
S
Stefan Berger 已提交
1761 1762 1763 1764 1765 1766
    {
        .name = NULL,
    }
};


1767 1768 1769 1770
typedef struct _virAttributes virAttributes;
struct _virAttributes {
    const char *id;
    const virXMLAttr2Struct *att;
1771
    virNWFilterRuleProtocolType prtclType;
1772 1773
};

1774 1775 1776 1777
#define PROTOCOL_ENTRY(ID, ATT, PRTCLTYPE) \
    { .id = ID, .att = ATT, .prtclType = PRTCLTYPE }
#define PROTOCOL_ENTRY_LAST { .id = NULL }

1778 1779

static const virAttributes virAttr[] = {
1780 1781 1782 1783 1784 1785 1786 1787 1788
    PROTOCOL_ENTRY("arp",     arpAttributes,     VIR_NWFILTER_RULE_PROTOCOL_ARP),
    PROTOCOL_ENTRY("rarp",    arpAttributes,     VIR_NWFILTER_RULE_PROTOCOL_RARP),
    PROTOCOL_ENTRY("mac",     macAttributes,     VIR_NWFILTER_RULE_PROTOCOL_MAC),
    PROTOCOL_ENTRY("vlan",    vlanAttributes,    VIR_NWFILTER_RULE_PROTOCOL_VLAN),
    PROTOCOL_ENTRY("stp",     stpAttributes,     VIR_NWFILTER_RULE_PROTOCOL_STP),
    PROTOCOL_ENTRY("ip",      ipAttributes,      VIR_NWFILTER_RULE_PROTOCOL_IP),
    PROTOCOL_ENTRY("ipv6",    ipv6Attributes,    VIR_NWFILTER_RULE_PROTOCOL_IPV6),
    PROTOCOL_ENTRY("tcp",     tcpAttributes,     VIR_NWFILTER_RULE_PROTOCOL_TCP),
    PROTOCOL_ENTRY("udp",     udpAttributes,     VIR_NWFILTER_RULE_PROTOCOL_UDP),
1789
    PROTOCOL_ENTRY("udplite", udpliteAttributes, VIR_NWFILTER_RULE_PROTOCOL_UDPLITE),
1790 1791 1792 1793 1794 1795 1796 1797
    PROTOCOL_ENTRY("esp",     espAttributes,     VIR_NWFILTER_RULE_PROTOCOL_ESP),
    PROTOCOL_ENTRY("ah",      ahAttributes,      VIR_NWFILTER_RULE_PROTOCOL_AH),
    PROTOCOL_ENTRY("sctp",    sctpAttributes,    VIR_NWFILTER_RULE_PROTOCOL_SCTP),
    PROTOCOL_ENTRY("icmp",    icmpAttributes,    VIR_NWFILTER_RULE_PROTOCOL_ICMP),
    PROTOCOL_ENTRY("all",     allAttributes,     VIR_NWFILTER_RULE_PROTOCOL_ALL),
    PROTOCOL_ENTRY("igmp",    igmpAttributes,    VIR_NWFILTER_RULE_PROTOCOL_IGMP),
    PROTOCOL_ENTRY("tcp-ipv6",     tcpipv6Attributes,     VIR_NWFILTER_RULE_PROTOCOL_TCPoIPV6),
    PROTOCOL_ENTRY("udp-ipv6",     udpipv6Attributes,     VIR_NWFILTER_RULE_PROTOCOL_UDPoIPV6),
1798
    PROTOCOL_ENTRY("udplite-ipv6", udpliteipv6Attributes, VIR_NWFILTER_RULE_PROTOCOL_UDPLITEoIPV6),
1799 1800 1801 1802 1803
    PROTOCOL_ENTRY("esp-ipv6",     espipv6Attributes,     VIR_NWFILTER_RULE_PROTOCOL_ESPoIPV6),
    PROTOCOL_ENTRY("ah-ipv6",      ahipv6Attributes,      VIR_NWFILTER_RULE_PROTOCOL_AHoIPV6),
    PROTOCOL_ENTRY("sctp-ipv6",    sctpipv6Attributes,    VIR_NWFILTER_RULE_PROTOCOL_SCTPoIPV6),
    PROTOCOL_ENTRY("icmpv6",       icmpv6Attributes,      VIR_NWFILTER_RULE_PROTOCOL_ICMPV6),
    PROTOCOL_ENTRY("all-ipv6",     allipv6Attributes,     VIR_NWFILTER_RULE_PROTOCOL_ALLoIPV6),
1804
    PROTOCOL_ENTRY_LAST
1805 1806 1807
};

static int
1808
virNWFilterRuleDetailsParse(xmlNodePtr node,
1809 1810 1811
                            virNWFilterRuleDefPtr nwf,
                            const virXMLAttr2Struct *att)
{
1812
    int rc = 0, g_rc = 0;
1813 1814
    int idx = 0;
    char *prop;
1815
    bool found = false;
1816
    enum attrDatatype datatype, att_datatypes;
1817
    virNWFilterEntryItemFlags *flags, match_flag = 0, flags_set = 0;
1818 1819
    nwItemDesc *item;
    int int_val;
1820
    unsigned int uint_val;
1821
    union data data;
1822 1823
    valueValidator validator;
    char *match = virXMLPropString(node, "match");
1824
    virSocketAddr ipaddr;
1825
    int base;
1826 1827 1828 1829 1830 1831

    if (match && STREQ(match, "no"))
        match_flag = NWFILTER_ENTRY_ITEM_FLAG_IS_NEG;
    VIR_FREE(match);
    match = NULL;

1832
    while (att[idx].name != NULL) {
1833 1834
        prop = virXMLPropString(node, att[idx].name);

1835
        VIR_WARNINGS_NO_CAST_ALIGN
1836
        item = (nwItemDesc *)((char *)nwf + att[idx].dataIdx);
1837
        VIR_WARNINGS_RESET
1838 1839 1840 1841
        flags = &item->flags;
        flags_set = match_flag;

        if (prop) {
1842
            found = false;
1843 1844 1845 1846 1847

            validator = NULL;

            if (STRPREFIX(prop, "$")) {
                flags_set |= NWFILTER_ENTRY_ITEM_FLAG_HAS_VAR;
1848
                if (virNWFilterRuleDefAddVar(nwf,
1849
                                             item,
1850
                                             &prop[1]) < 0)
1851
                    rc = -1;
1852
                found = true;
1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865
            }

            datatype = 1;

            att_datatypes = att[idx].datatype;

            while (datatype <= DATATYPE_LAST && found == 0 && rc == 0) {
                if ((att_datatypes & datatype)) {

                    att_datatypes ^= datatype;

                    validator = att[idx].validator;

1866
                    base = 10;
1867

1868 1869 1870
                    switch (datatype) {
                        case DATATYPE_UINT8_HEX:
                            base = 16;
1871
                            /* fallthrough */
1872
                        case DATATYPE_UINT8:
1873
                            if (virStrToLong_ui(prop, NULL, base, &uint_val) >= 0) {
1874
                                if (uint_val <= 0xff) {
1875
                                    item->u.u8 = uint_val;
1876
                                    found = true;
1877
                                    data.ui = uint_val;
1878
                                } else {
1879
                                    rc = -1;
1880 1881
                                }
                            } else {
1882
                                rc = -1;
1883
                            }
1884 1885
                        break;

1886 1887
                        case DATATYPE_UINT16_HEX:
                            base = 16;
1888
                            /* fallthrough */
1889
                        case DATATYPE_UINT16:
1890
                            if (virStrToLong_ui(prop, NULL, base, &uint_val) >= 0) {
1891
                                if (uint_val <= 0xffff) {
1892
                                    item->u.u16 = uint_val;
1893
                                    found = true;
1894
                                    data.ui = uint_val;
1895
                                } else {
1896
                                    rc = -1;
1897 1898
                                }
                            } else {
1899
                                rc = -1;
1900
                            }
1901 1902
                        break;

S
Stefan Berger 已提交
1903 1904 1905 1906 1907 1908
                        case DATATYPE_UINT32_HEX:
                            base = 16;
                            /* fallthrough */
                        case DATATYPE_UINT32:
                            if (virStrToLong_ui(prop, NULL, base, &uint_val) >= 0) {
                                item->u.u32 = uint_val;
1909
                                found = true;
S
Stefan Berger 已提交
1910
                                data.ui = uint_val;
1911
                            } else {
S
Stefan Berger 已提交
1912
                                rc = -1;
1913
                            }
S
Stefan Berger 已提交
1914 1915
                        break;

1916
                        case DATATYPE_IPADDR:
1917
                            if (virSocketAddrParseIPv4(&item->u.ipaddr, prop) < 0)
1918
                                rc = -1;
1919
                            found = true;
1920 1921 1922
                        break;

                        case DATATYPE_IPMASK:
1923 1924
                            if (virStrToLong_ui(prop, NULL, 10, &uint_val) == 0) {
                                if (uint_val <= 32) {
1925
                                    if (!validator)
1926
                                        item->u.u8 = (uint8_t)uint_val;
1927
                                    found = true;
1928
                                    data.ui = uint_val;
1929
                                } else {
1930
                                    rc = -1;
1931
                                }
1932
                            } else {
1933
                                if (virSocketAddrParseIPv4(&ipaddr, prop) < 0) {
1934 1935
                                    rc = -1;
                                } else {
1936
                                    int_val = virSocketAddrGetNumNetmaskBits(&ipaddr);
1937
                                    if (int_val >= 0)
1938
                                        item->u.u8 = int_val;
1939 1940
                                    else
                                        rc = -1;
1941
                                    found = true;
1942
                                }
1943 1944 1945 1946
                            }
                        break;

                        case DATATYPE_MACADDR:
1947 1948
                            if (virMacAddrParse(prop,
                                                &item->u.macaddr) < 0) {
1949 1950
                                rc = -1;
                            }
1951
                            found = true;
1952 1953 1954 1955
                        break;

                        case DATATYPE_MACMASK:
                            validator = checkMACMask;
1956 1957
                            if (virMacAddrParse(prop,
                                                &item->u.macaddr) < 0) {
1958 1959
                                rc = -1;
                            }
1960
                            data.v = &item->u.macaddr;
1961
                            found = true;
1962 1963
                        break;

1964
                        case DATATYPE_IPV6ADDR:
1965
                            if (virSocketAddrParseIPv6(&item->u.ipaddr, prop) < 0)
1966
                                rc = -1;
1967
                            found = true;
1968 1969 1970
                        break;

                        case DATATYPE_IPV6MASK:
1971 1972
                            if (virStrToLong_ui(prop, NULL, 10, &uint_val) == 0) {
                                if (uint_val <= 128) {
1973
                                    if (!validator)
1974
                                        item->u.u8 = (uint8_t)uint_val;
1975
                                    found = true;
1976
                                    data.ui = uint_val;
1977
                                } else {
1978
                                    rc = -1;
1979
                                }
1980
                            } else {
1981
                                if (virSocketAddrParseIPv6(&ipaddr, prop) < 0) {
1982 1983
                                    rc = -1;
                                } else {
1984
                                    int_val = virSocketAddrGetNumNetmaskBits(&ipaddr);
1985
                                    if (int_val >= 0)
1986
                                        item->u.u8 = int_val;
1987 1988
                                    else
                                        rc = -1;
1989
                                    found = true;
1990
                                }
1991 1992 1993
                            }
                        break;

1994
                        case DATATYPE_STRING:
S
Stefan Berger 已提交
1995 1996
                        case DATATYPE_IPSETFLAGS:
                        case DATATYPE_IPSETNAME:
1997
                            if (!validator) {
1998
                                /* not supported */
1999 2000 2001
                                rc = -1;
                                break;
                            }
2002
                            data.c = prop;
2003
                            found = true;
2004 2005
                        break;

2006 2007 2008 2009 2010 2011 2012 2013
                        case DATATYPE_STRINGCOPY:
                            if (!(item->u.string =
                                  virNWFilterRuleDefAddString(nwf, prop,
                                                       att[idx].maxstrlen))) {
                                rc = -1;
                                break;
                            }
                            data.c = item->u.string;
2014
                            found = true;
2015 2016
                        break;

2017 2018 2019 2020 2021 2022 2023 2024 2025
                        case DATATYPE_BOOLEAN:
                            if (STREQ(prop, "true") ||
                                STREQ(prop, "1") ||
                                STREQ(prop, "yes"))
                                item->u.boolean = true;
                            else
                                item->u.boolean = false;

                            data.ui = item->u.boolean;
2026
                            found = true;
2027 2028
                        break;

2029 2030 2031 2032 2033 2034 2035 2036
                        case DATATYPE_LAST:
                        default:
                        break;
                    }
                }

                if (rc != 0 && att_datatypes != 0) {
                    rc = 0;
2037
                    found = false;
2038 2039 2040 2041 2042
                }

                datatype <<= 1;
            } /* while */

2043
            if (found && rc == 0) {
2044 2045 2046
                *flags = NWFILTER_ENTRY_ITEM_FLAG_EXISTS | flags_set;
                item->datatype = datatype >> 1;
                if (validator) {
2047
                    if (!validator(datatype >> 1, &data, nwf, item)) {
2048 2049 2050 2051 2052 2053 2054
                        rc = -1;
                        *flags = 0;
                    }
                }
            }

            if (!found || rc) {
2055 2056 2057
                virReportError(VIR_ERR_INTERNAL_ERROR,
                               _("%s has illegal value %s"),
                               att[idx].name, prop);
2058 2059 2060 2061
                rc = -1;
            }
            VIR_FREE(prop);
        }
2062 2063 2064 2065 2066 2067

        if (rc) {
            g_rc = rc;
            rc = 0;
        }

2068 2069 2070
        idx++;
    }

2071
    return g_rc;
2072 2073 2074 2075 2076 2077
}




static virNWFilterIncludeDefPtr
2078
virNWFilterIncludeParse(xmlNodePtr cur)
2079 2080 2081
{
    virNWFilterIncludeDefPtr ret;

2082
    if (VIR_ALLOC(ret) < 0)
2083 2084 2085 2086
        return NULL;

    ret->filterref = virXMLPropString(cur, "filter");
    if (!ret->filterref) {
2087 2088 2089
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       "%s",
                       _("rule node requires action attribute"));
2090 2091 2092 2093 2094 2095 2096
        goto err_exit;
    }

    ret->params = virNWFilterParseParamAttributes(cur);
    if (!ret->params)
        goto err_exit;

2097
 cleanup:
2098 2099
    return ret;

2100
 err_exit:
2101 2102 2103 2104 2105
    virNWFilterIncludeDefFree(ret);
    ret = NULL;
    goto cleanup;
}

S
Stefan Berger 已提交
2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118
static void
virNWFilterRuleDefFixupIPSet(ipHdrDataDefPtr ipHdr)
{
    if (HAS_ENTRY_ITEM(&ipHdr->dataIPSet) &&
        !HAS_ENTRY_ITEM(&ipHdr->dataIPSetFlags)) {
        ipHdr->dataIPSetFlags.flags = NWFILTER_ENTRY_ITEM_FLAG_EXISTS;
        ipHdr->dataIPSetFlags.u.ipset.numFlags = 1;
        ipHdr->dataIPSetFlags.u.ipset.flags = 1;
    } else {
        ipHdr->dataIPSet.flags = 0;
        ipHdr->dataIPSetFlags.flags = 0;
    }
}
2119

2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131

/*
 * virNWFilterRuleValidate
 *
 * Perform some basic rule validation to prevent rules from being
 * defined that cannot be instantiated.
 */
static int
virNWFilterRuleValidate(virNWFilterRuleDefPtr rule)
{
    int ret = 0;
    portDataDefPtr portData = NULL;
J
Ján Tomko 已提交
2132 2133
    nwItemDescPtr dataProtocolID = NULL;
    const char *protocol = NULL;
2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179

    switch (rule->prtclType) {
    case VIR_NWFILTER_RULE_PROTOCOL_IP:
        portData = &rule->p.ipHdrFilter.portData;
        protocol = "IP";
        dataProtocolID = &rule->p.ipHdrFilter.ipHdr.dataProtocolID;
        /* fall through */
    case VIR_NWFILTER_RULE_PROTOCOL_IPV6:
        if (portData == NULL) {
            portData = &rule->p.ipv6HdrFilter.portData;
            protocol = "IPv6";
            dataProtocolID = &rule->p.ipv6HdrFilter.ipHdr.dataProtocolID;
        }
        if (HAS_ENTRY_ITEM(&portData->dataSrcPortStart) ||
            HAS_ENTRY_ITEM(&portData->dataDstPortStart) ||
            HAS_ENTRY_ITEM(&portData->dataSrcPortEnd) ||
            HAS_ENTRY_ITEM(&portData->dataDstPortEnd)) {
            if (HAS_ENTRY_ITEM(dataProtocolID)) {
                switch (dataProtocolID->u.u8) {
                case 6:   /* tcp */
                case 17:  /* udp */
                case 33:  /* dccp */
                case 132: /* sctp */
                    break;
                default:
                    ret = -1;
                }
            } else {
                ret = -1;
            }
            if (ret < 0) {
                virReportError(VIR_ERR_INTERNAL_ERROR,
                               _("%s rule with port specification requires "
                                 "protocol specification with protocol to be "
                                 "either one of tcp(6), udp(17), dccp(33), or "
                                 "sctp(132)"), protocol);
            }
        }
        break;
    default:
        break;
    }

    return ret;
}

2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194
static void
virNWFilterRuleDefFixup(virNWFilterRuleDefPtr rule)
{
#define COPY_NEG_SIGN(A, B) \
    (A).flags = ((A).flags & ~NWFILTER_ENTRY_ITEM_FLAG_IS_NEG) | \
                ((B).flags &  NWFILTER_ENTRY_ITEM_FLAG_IS_NEG);

    switch (rule->prtclType) {
    case VIR_NWFILTER_RULE_PROTOCOL_MAC:
        COPY_NEG_SIGN(rule->p.ethHdrFilter.ethHdr.dataSrcMACMask,
                      rule->p.ethHdrFilter.ethHdr.dataSrcMACAddr);
        COPY_NEG_SIGN(rule->p.ethHdrFilter.ethHdr.dataDstMACMask,
                      rule->p.ethHdrFilter.ethHdr.dataDstMACAddr);
    break;

S
Stefan Berger 已提交
2195 2196 2197 2198 2199 2200 2201
    case VIR_NWFILTER_RULE_PROTOCOL_VLAN:
        COPY_NEG_SIGN(rule->p.vlanHdrFilter.ethHdr.dataSrcMACMask,
                      rule->p.vlanHdrFilter.ethHdr.dataSrcMACAddr);
        COPY_NEG_SIGN(rule->p.vlanHdrFilter.ethHdr.dataDstMACMask,
                      rule->p.vlanHdrFilter.ethHdr.dataDstMACAddr);
    break;

S
Stefan Berger 已提交
2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226
    case VIR_NWFILTER_RULE_PROTOCOL_STP:
        COPY_NEG_SIGN(rule->p.stpHdrFilter.ethHdr.dataSrcMACMask,
                      rule->p.stpHdrFilter.ethHdr.dataSrcMACAddr);
        COPY_NEG_SIGN(rule->p.stpHdrFilter.dataRootPriHi,
                      rule->p.stpHdrFilter.dataRootPri);
        COPY_NEG_SIGN(rule->p.stpHdrFilter.dataRootAddrMask,
                      rule->p.stpHdrFilter.dataRootAddr);
        COPY_NEG_SIGN(rule->p.stpHdrFilter.dataRootCostHi,
                      rule->p.stpHdrFilter.dataRootCost);
        COPY_NEG_SIGN(rule->p.stpHdrFilter.dataSndrPrioHi,
                      rule->p.stpHdrFilter.dataSndrPrio);
        COPY_NEG_SIGN(rule->p.stpHdrFilter.dataSndrAddrMask,
                      rule->p.stpHdrFilter.dataSndrAddr);
        COPY_NEG_SIGN(rule->p.stpHdrFilter.dataPortHi,
                      rule->p.stpHdrFilter.dataPort);
        COPY_NEG_SIGN(rule->p.stpHdrFilter.dataAgeHi,
                      rule->p.stpHdrFilter.dataAge);
        COPY_NEG_SIGN(rule->p.stpHdrFilter.dataMaxAgeHi,
                      rule->p.stpHdrFilter.dataMaxAge);
        COPY_NEG_SIGN(rule->p.stpHdrFilter.dataHelloTimeHi,
                      rule->p.stpHdrFilter.dataHelloTime);
        COPY_NEG_SIGN(rule->p.stpHdrFilter.dataFwdDelayHi,
                      rule->p.stpHdrFilter.dataFwdDelay);
    break;

2227 2228 2229 2230 2231
    case VIR_NWFILTER_RULE_PROTOCOL_IP:
        COPY_NEG_SIGN(rule->p.ipHdrFilter.ipHdr.dataSrcIPMask,
                      rule->p.ipHdrFilter.ipHdr.dataSrcIPAddr);
        COPY_NEG_SIGN(rule->p.ipHdrFilter.ipHdr.dataDstIPMask,
                      rule->p.ipHdrFilter.ipHdr.dataDstIPAddr);
S
Stefan Berger 已提交
2232
        virNWFilterRuleDefFixupIPSet(&rule->p.ipHdrFilter.ipHdr);
2233 2234
    break;

2235 2236 2237 2238 2239
    case VIR_NWFILTER_RULE_PROTOCOL_IPV6:
        COPY_NEG_SIGN(rule->p.ipv6HdrFilter.ipHdr.dataSrcIPMask,
                      rule->p.ipv6HdrFilter.ipHdr.dataSrcIPAddr);
        COPY_NEG_SIGN(rule->p.ipv6HdrFilter.ipHdr.dataDstIPMask,
                      rule->p.ipv6HdrFilter.ipHdr.dataDstIPAddr);
2240 2241 2242 2243 2244 2245
        COPY_NEG_SIGN(rule->p.ipv6HdrFilter.dataICMPTypeEnd,
                      rule->p.ipv6HdrFilter.dataICMPTypeStart);
        COPY_NEG_SIGN(rule->p.ipv6HdrFilter.dataICMPCodeStart,
                      rule->p.ipv6HdrFilter.dataICMPTypeStart);
        COPY_NEG_SIGN(rule->p.ipv6HdrFilter.dataICMPCodeEnd,
                      rule->p.ipv6HdrFilter.dataICMPTypeStart);
S
Stefan Berger 已提交
2246
        virNWFilterRuleDefFixupIPSet(&rule->p.ipv6HdrFilter.ipHdr);
2247 2248
    break;

2249
    case VIR_NWFILTER_RULE_PROTOCOL_ARP:
2250
    case VIR_NWFILTER_RULE_PROTOCOL_RARP:
2251 2252 2253
    case VIR_NWFILTER_RULE_PROTOCOL_NONE:
    break;

S
Stefan Berger 已提交
2254
    case VIR_NWFILTER_RULE_PROTOCOL_TCP:
2255
    case VIR_NWFILTER_RULE_PROTOCOL_TCPoIPV6:
S
Stefan Berger 已提交
2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269
        COPY_NEG_SIGN(rule->p.tcpHdrFilter.ipHdr.dataSrcIPMask,
                      rule->p.tcpHdrFilter.ipHdr.dataSrcIPAddr);
        COPY_NEG_SIGN(rule->p.tcpHdrFilter.ipHdr.dataDstIPMask,
                      rule->p.tcpHdrFilter.ipHdr.dataDstIPAddr);
        COPY_NEG_SIGN(rule->p.tcpHdrFilter.ipHdr.dataSrcIPTo,
                      rule->p.tcpHdrFilter.ipHdr.dataSrcIPFrom);
        COPY_NEG_SIGN(rule->p.tcpHdrFilter.ipHdr.dataDstIPTo,
                      rule->p.tcpHdrFilter.ipHdr.dataDstIPFrom);
        COPY_NEG_SIGN(rule->p.tcpHdrFilter.portData.dataSrcPortEnd,
                      rule->p.tcpHdrFilter.portData.dataSrcPortStart);
        COPY_NEG_SIGN(rule->p.tcpHdrFilter.portData.dataDstPortStart,
                      rule->p.tcpHdrFilter.portData.dataSrcPortStart);
        COPY_NEG_SIGN(rule->p.tcpHdrFilter.portData.dataDstPortEnd,
                      rule->p.tcpHdrFilter.portData.dataSrcPortStart);
S
Stefan Berger 已提交
2270
        virNWFilterRuleDefFixupIPSet(&rule->p.tcpHdrFilter.ipHdr);
S
Stefan Berger 已提交
2271 2272 2273
    break;

    case VIR_NWFILTER_RULE_PROTOCOL_UDP:
2274
    case VIR_NWFILTER_RULE_PROTOCOL_UDPoIPV6:
S
Stefan Berger 已提交
2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288
        COPY_NEG_SIGN(rule->p.udpHdrFilter.ipHdr.dataSrcIPMask,
                      rule->p.udpHdrFilter.ipHdr.dataSrcIPAddr);
        COPY_NEG_SIGN(rule->p.udpHdrFilter.ipHdr.dataDstIPMask,
                      rule->p.udpHdrFilter.ipHdr.dataDstIPAddr);
        COPY_NEG_SIGN(rule->p.udpHdrFilter.ipHdr.dataSrcIPTo,
                      rule->p.udpHdrFilter.ipHdr.dataSrcIPFrom);
        COPY_NEG_SIGN(rule->p.udpHdrFilter.ipHdr.dataDstIPTo,
                      rule->p.udpHdrFilter.ipHdr.dataDstIPFrom);
        COPY_NEG_SIGN(rule->p.udpHdrFilter.portData.dataSrcPortEnd,
                      rule->p.udpHdrFilter.portData.dataSrcPortStart);
        COPY_NEG_SIGN(rule->p.udpHdrFilter.portData.dataDstPortStart,
                      rule->p.udpHdrFilter.portData.dataSrcPortStart);
        COPY_NEG_SIGN(rule->p.udpHdrFilter.portData.dataDstPortEnd,
                      rule->p.udpHdrFilter.portData.dataSrcPortStart);
S
Stefan Berger 已提交
2289
        virNWFilterRuleDefFixupIPSet(&rule->p.udpHdrFilter.ipHdr);
S
Stefan Berger 已提交
2290 2291
    break;

2292
    case VIR_NWFILTER_RULE_PROTOCOL_UDPLITE:
2293
    case VIR_NWFILTER_RULE_PROTOCOL_UDPLITEoIPV6:
2294 2295 2296 2297 2298 2299 2300 2301
        COPY_NEG_SIGN(rule->p.udpliteHdrFilter.ipHdr.dataSrcIPMask,
                      rule->p.udpliteHdrFilter.ipHdr.dataSrcIPAddr);
        COPY_NEG_SIGN(rule->p.udpliteHdrFilter.ipHdr.dataDstIPMask,
                      rule->p.udpliteHdrFilter.ipHdr.dataDstIPAddr);
        COPY_NEG_SIGN(rule->p.udpliteHdrFilter.ipHdr.dataSrcIPTo,
                      rule->p.udpliteHdrFilter.ipHdr.dataSrcIPFrom);
        COPY_NEG_SIGN(rule->p.udpliteHdrFilter.ipHdr.dataDstIPTo,
                      rule->p.udpliteHdrFilter.ipHdr.dataDstIPFrom);
S
Stefan Berger 已提交
2302
        virNWFilterRuleDefFixupIPSet(&rule->p.udpliteHdrFilter.ipHdr);
2303 2304 2305
    break;

    case VIR_NWFILTER_RULE_PROTOCOL_ESP:
2306
    case VIR_NWFILTER_RULE_PROTOCOL_ESPoIPV6:
2307 2308 2309 2310 2311 2312 2313 2314
        COPY_NEG_SIGN(rule->p.espHdrFilter.ipHdr.dataSrcIPMask,
                      rule->p.espHdrFilter.ipHdr.dataSrcIPAddr);
        COPY_NEG_SIGN(rule->p.espHdrFilter.ipHdr.dataDstIPMask,
                      rule->p.espHdrFilter.ipHdr.dataDstIPAddr);
        COPY_NEG_SIGN(rule->p.espHdrFilter.ipHdr.dataSrcIPTo,
                      rule->p.espHdrFilter.ipHdr.dataSrcIPFrom);
        COPY_NEG_SIGN(rule->p.espHdrFilter.ipHdr.dataDstIPTo,
                      rule->p.espHdrFilter.ipHdr.dataDstIPFrom);
S
Stefan Berger 已提交
2315
        virNWFilterRuleDefFixupIPSet(&rule->p.espHdrFilter.ipHdr);
2316 2317 2318
    break;

    case VIR_NWFILTER_RULE_PROTOCOL_AH:
2319
    case VIR_NWFILTER_RULE_PROTOCOL_AHoIPV6:
2320 2321 2322 2323 2324 2325 2326 2327
        COPY_NEG_SIGN(rule->p.ahHdrFilter.ipHdr.dataSrcIPMask,
                      rule->p.ahHdrFilter.ipHdr.dataSrcIPAddr);
        COPY_NEG_SIGN(rule->p.ahHdrFilter.ipHdr.dataDstIPMask,
                      rule->p.ahHdrFilter.ipHdr.dataDstIPAddr);
        COPY_NEG_SIGN(rule->p.ahHdrFilter.ipHdr.dataSrcIPTo,
                      rule->p.ahHdrFilter.ipHdr.dataSrcIPFrom);
        COPY_NEG_SIGN(rule->p.ahHdrFilter.ipHdr.dataDstIPTo,
                      rule->p.ahHdrFilter.ipHdr.dataDstIPFrom);
S
Stefan Berger 已提交
2328
        virNWFilterRuleDefFixupIPSet(&rule->p.ahHdrFilter.ipHdr);
2329 2330
    break;

S
Stefan Berger 已提交
2331
    case VIR_NWFILTER_RULE_PROTOCOL_SCTP:
2332
    case VIR_NWFILTER_RULE_PROTOCOL_SCTPoIPV6:
S
Stefan Berger 已提交
2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346
        COPY_NEG_SIGN(rule->p.sctpHdrFilter.ipHdr.dataSrcIPMask,
                      rule->p.sctpHdrFilter.ipHdr.dataSrcIPAddr);
        COPY_NEG_SIGN(rule->p.sctpHdrFilter.ipHdr.dataDstIPMask,
                      rule->p.sctpHdrFilter.ipHdr.dataDstIPAddr);
        COPY_NEG_SIGN(rule->p.sctpHdrFilter.ipHdr.dataSrcIPTo,
                      rule->p.sctpHdrFilter.ipHdr.dataSrcIPFrom);
        COPY_NEG_SIGN(rule->p.sctpHdrFilter.ipHdr.dataDstIPTo,
                      rule->p.sctpHdrFilter.ipHdr.dataDstIPFrom);
        COPY_NEG_SIGN(rule->p.sctpHdrFilter.portData.dataSrcPortEnd,
                      rule->p.sctpHdrFilter.portData.dataSrcPortStart);
        COPY_NEG_SIGN(rule->p.sctpHdrFilter.portData.dataDstPortStart,
                      rule->p.sctpHdrFilter.portData.dataSrcPortStart);
        COPY_NEG_SIGN(rule->p.sctpHdrFilter.portData.dataDstPortEnd,
                      rule->p.sctpHdrFilter.portData.dataSrcPortStart);
S
Stefan Berger 已提交
2347
        virNWFilterRuleDefFixupIPSet(&rule->p.sctpHdrFilter.ipHdr);
S
Stefan Berger 已提交
2348 2349 2350
    break;

    case VIR_NWFILTER_RULE_PROTOCOL_ICMP:
2351
    case VIR_NWFILTER_RULE_PROTOCOL_ICMPV6:
S
Stefan Berger 已提交
2352 2353 2354 2355 2356 2357 2358 2359 2360 2361
        COPY_NEG_SIGN(rule->p.icmpHdrFilter.ipHdr.dataSrcIPMask,
                      rule->p.icmpHdrFilter.ipHdr.dataSrcIPAddr);
        COPY_NEG_SIGN(rule->p.icmpHdrFilter.ipHdr.dataDstIPMask,
                      rule->p.icmpHdrFilter.ipHdr.dataDstIPAddr);
        COPY_NEG_SIGN(rule->p.icmpHdrFilter.ipHdr.dataSrcIPTo,
                      rule->p.icmpHdrFilter.ipHdr.dataSrcIPFrom);
        COPY_NEG_SIGN(rule->p.icmpHdrFilter.ipHdr.dataDstIPTo,
                      rule->p.icmpHdrFilter.ipHdr.dataDstIPFrom);
        COPY_NEG_SIGN(rule->p.icmpHdrFilter.dataICMPCode,
                      rule->p.icmpHdrFilter.dataICMPType);
S
Stefan Berger 已提交
2362
        virNWFilterRuleDefFixupIPSet(&rule->p.icmpHdrFilter.ipHdr);
S
Stefan Berger 已提交
2363 2364 2365
    break;

    case VIR_NWFILTER_RULE_PROTOCOL_ALL:
2366
    case VIR_NWFILTER_RULE_PROTOCOL_ALLoIPV6:
S
Stefan Berger 已提交
2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385
        COPY_NEG_SIGN(rule->p.allHdrFilter.ipHdr.dataSrcIPMask,
                      rule->p.allHdrFilter.ipHdr.dataSrcIPAddr);
        COPY_NEG_SIGN(rule->p.allHdrFilter.ipHdr.dataDstIPMask,
                      rule->p.allHdrFilter.ipHdr.dataDstIPAddr);
        COPY_NEG_SIGN(rule->p.allHdrFilter.ipHdr.dataSrcIPTo,
                      rule->p.allHdrFilter.ipHdr.dataSrcIPFrom);
        COPY_NEG_SIGN(rule->p.allHdrFilter.ipHdr.dataDstIPTo,
                      rule->p.allHdrFilter.ipHdr.dataDstIPFrom);
    break;

    case VIR_NWFILTER_RULE_PROTOCOL_IGMP:
        COPY_NEG_SIGN(rule->p.igmpHdrFilter.ipHdr.dataSrcIPMask,
                      rule->p.igmpHdrFilter.ipHdr.dataSrcIPAddr);
        COPY_NEG_SIGN(rule->p.igmpHdrFilter.ipHdr.dataDstIPMask,
                      rule->p.igmpHdrFilter.ipHdr.dataDstIPAddr);
        COPY_NEG_SIGN(rule->p.igmpHdrFilter.ipHdr.dataSrcIPTo,
                      rule->p.igmpHdrFilter.ipHdr.dataSrcIPFrom);
        COPY_NEG_SIGN(rule->p.igmpHdrFilter.ipHdr.dataDstIPTo,
                      rule->p.igmpHdrFilter.ipHdr.dataDstIPFrom);
S
Stefan Berger 已提交
2386
        virNWFilterRuleDefFixupIPSet(&rule->p.igmpHdrFilter.ipHdr);
S
Stefan Berger 已提交
2387 2388 2389 2390 2391
    break;

    case VIR_NWFILTER_RULE_PROTOCOL_LAST:
    break;
    }
2392 2393 2394 2395 2396
#undef COPY_NEG_SIGN
}


static virNWFilterRuleDefPtr
2397
virNWFilterRuleParse(xmlNodePtr node)
2398 2399 2400 2401
{
    char *action;
    char *direction;
    char *prio;
2402
    char *statematch;
2403
    bool found;
2404
    int found_i = 0;
2405
    int priority;
2406 2407 2408 2409

    xmlNodePtr cur;
    virNWFilterRuleDefPtr ret;

2410
    if (VIR_ALLOC(ret) < 0)
2411 2412
        return NULL;

2413 2414 2415 2416
    action     = virXMLPropString(node, "action");
    direction  = virXMLPropString(node, "direction");
    prio       = virXMLPropString(node, "priority");
    statematch = virXMLPropString(node, "statematch");
2417 2418

    if (!action) {
2419 2420 2421
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       "%s",
                       _("rule node requires action attribute"));
2422 2423 2424 2425
        goto err_exit;
    }

    if ((ret->action = virNWFilterRuleActionTypeFromString(action)) < 0) {
2426
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
2427 2428
                       "%s",
                       _("unknown rule action attribute value"));
2429 2430 2431 2432
        goto err_exit;
    }

    if (!direction) {
2433 2434 2435
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       "%s",
                       _("rule node requires direction attribute"));
2436 2437 2438 2439
        goto err_exit;
    }

    if ((ret->tt = virNWFilterRuleDirectionTypeFromString(direction)) < 0) {
2440
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
2441 2442
                       "%s",
                       _("unknown rule direction attribute value"));
2443 2444 2445 2446 2447 2448
        goto err_exit;
    }

    ret->priority = MAX_RULE_PRIORITY / 2;

    if (prio) {
2449 2450 2451
        if (virStrToLong_i(prio, NULL, 10, &priority) >= 0) {
            if (priority <= MAX_RULE_PRIORITY &&
                priority >= MIN_RULE_PRIORITY)
2452 2453 2454 2455
                ret->priority = priority;
        }
    }

2456 2457 2458 2459
    if (statematch &&
        (STREQ(statematch, "0") || STRCASEEQ(statematch, "false")))
        ret->flags |= RULE_FLAG_NO_STATEMATCH;

2460 2461
    cur = node->children;

2462
    found = false;
2463 2464 2465

    while (cur != NULL) {
        if (cur->type == XML_ELEMENT_NODE) {
2466
            size_t i = 0;
2467 2468 2469 2470 2471 2472 2473
            while (1) {
                if (found)
                    i = found_i;

                if (xmlStrEqual(cur->name, BAD_CAST virAttr[i].id)) {

                    found_i = i;
2474
                    found = true;
2475 2476
                    ret->prtclType = virAttr[i].prtclType;

2477
                    if (virNWFilterRuleDetailsParse(cur,
2478 2479
                                                    ret,
                                                    virAttr[i].att) < 0) {
2480
                        goto err_exit;
2481
                    }
2482 2483
                    if (virNWFilterRuleValidate(ret) < 0)
                        goto err_exit;
2484 2485 2486 2487 2488 2489
                    break;
                }
                if (!found) {
                    i++;
                    if (!virAttr[i].id)
                        break;
2490
                } else {
2491
                   break;
2492
                }
2493 2494 2495 2496 2497 2498 2499 2500
            }
        }

        cur = cur->next;
    }

    virNWFilterRuleDefFixup(ret);

2501
 cleanup:
2502 2503 2504
    VIR_FREE(prio);
    VIR_FREE(action);
    VIR_FREE(direction);
2505
    VIR_FREE(statematch);
2506 2507 2508

    return ret;

2509
 err_exit:
2510 2511 2512 2513 2514
    virNWFilterRuleDefFree(ret);
    ret = NULL;
    goto cleanup;
}

2515 2516 2517 2518
static bool
virNWFilterIsValidChainName(const char *chainname)
{
    if (strlen(chainname) > MAX_CHAIN_SUFFIX_SIZE) {
2519 2520 2521 2522
        virReportError(VIR_ERR_INVALID_ARG,
                       _("Name of chain is longer than "
                         "%u characters"),
                       MAX_CHAIN_SUFFIX_SIZE);
2523 2524 2525 2526
        return false;
    }

    if (chainname[strspn(chainname, VALID_CHAINNAME)] != 0) {
2527
        virReportError(VIR_ERR_INVALID_ARG, "%s",
2528
                       _("Chain name contains invalid characters"));
2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542
        return false;
    }

    return true;
}

/*
 * Test whether the name of the chain is supported.
 * It current has to have a prefix of either one of the strings found in
 * virNWFilterChainSuffixTypeToString().
 */
static const char *
virNWFilterIsAllowedChain(const char *chainname)
{
2543
    virNWFilterChainSuffixType i;
2544 2545
    const char *name;
    char *msg;
2546 2547 2548 2549 2550 2551 2552 2553 2554 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 2579
    virBuffer buf = VIR_BUFFER_INITIALIZER;
    bool printed = false;

    if (!virNWFilterIsValidChainName(chainname))
        return NULL;

    for (i = 0; i < VIR_NWFILTER_CHAINSUFFIX_LAST; i++) {
        name = virNWFilterChainSuffixTypeToString(i);
        if (i == VIR_NWFILTER_CHAINSUFFIX_ROOT) {
            /* allow 'root' as a complete name but not as a prefix */
            if (STREQ(chainname, name))
                return name;
            if (STRPREFIX(chainname, name))
                return NULL;
        }
        if (STRPREFIX(chainname, name))
            return name;
    }

    virBufferAsprintf(&buf,
                      _("Invalid chain name '%s'. Please use a chain name "
                      "called '%s' or any of the following prefixes: "),
                      chainname,
                      virNWFilterChainSuffixTypeToString(
                          VIR_NWFILTER_CHAINSUFFIX_ROOT));
    for (i = 0; i < VIR_NWFILTER_CHAINSUFFIX_LAST; i++) {
        if (i == VIR_NWFILTER_CHAINSUFFIX_ROOT)
            continue;
        if (printed)
            virBufferAddLit(&buf, ", ");
        virBufferAdd(&buf, virNWFilterChainSuffixTypeToString(i), -1);
        printed = true;
    }

2580
    if (virBufferCheckError(&buf) < 0)
2581 2582 2583 2584
        goto err_exit;

    msg = virBufferContentAndReset(&buf);

2585
    virReportError(VIR_ERR_INVALID_ARG, "%s", msg);
2586 2587
    VIR_FREE(msg);

2588
 err_exit:
2589 2590
    return NULL;
}
2591 2592

static virNWFilterDefPtr
2593 2594
virNWFilterDefParseXML(xmlXPathContextPtr ctxt)
{
2595 2596 2597 2598
    virNWFilterDefPtr ret;
    xmlNodePtr curr = ctxt->node;
    char *uuid = NULL;
    char *chain = NULL;
2599
    char *chain_pri_s = NULL;
2600
    virNWFilterEntryPtr entry;
2601
    int chain_priority;
2602
    const char *name_prefix;
2603

2604
    if (VIR_ALLOC(ret) < 0)
2605 2606 2607 2608
        return NULL;

    ret->name = virXPathString("string(./@name)", ctxt);
    if (!ret->name) {
2609 2610
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       "%s", _("filter has no name"));
2611 2612 2613
        goto cleanup;
    }

2614 2615 2616
    chain_pri_s = virXPathString("string(./@priority)", ctxt);
    if (chain_pri_s) {
        if (virStrToLong_i(chain_pri_s, NULL, 10, &chain_priority) < 0) {
2617 2618 2619
            virReportError(VIR_ERR_INVALID_ARG,
                           _("Could not parse chain priority '%s'"),
                           chain_pri_s);
2620 2621 2622 2623
            goto cleanup;
        }
        if (chain_priority < NWFILTER_MIN_FILTER_PRIORITY ||
            chain_priority > NWFILTER_MAX_FILTER_PRIORITY) {
2624 2625 2626 2627 2628 2629
            virReportError(VIR_ERR_INVALID_ARG,
                           _("Priority '%d' is outside valid "
                             "range of [%d,%d]"),
                           chain_priority,
                           NWFILTER_MIN_FILTER_PRIORITY,
                           NWFILTER_MAX_FILTER_PRIORITY);
2630 2631 2632 2633
            goto cleanup;
        }
    }

2634 2635
    chain = virXPathString("string(./@chain)", ctxt);
    if (chain) {
2636 2637
        name_prefix = virNWFilterIsAllowedChain(chain);
        if (name_prefix == NULL)
2638
            goto cleanup;
2639
        ret->chainsuffix = chain;
2640 2641 2642 2643 2644

        if (chain_pri_s) {
            ret->chainPriority = chain_priority;
        } else {
            /* assign default priority if none can be found via lookup */
2645
            if (!name_prefix ||
2646 2647
                 intMapGetByString(chain_priorities, name_prefix, 0,
                                   &ret->chainPriority) < 0) {
2648
                /* assign default chain priority */
2649 2650 2651
                ret->chainPriority = (NWFILTER_MAX_FILTER_PRIORITY +
                                      NWFILTER_MIN_FILTER_PRIORITY) / 2;
            }
2652
        }
2653 2654
        chain = NULL;
    } else {
2655 2656
        if (VIR_STRDUP(ret->chainsuffix,
                       virNWFilterChainSuffixTypeToString(VIR_NWFILTER_CHAINSUFFIX_ROOT)) < 0)
2657
            goto cleanup;
2658 2659 2660 2661 2662
    }

    uuid = virXPathString("string(./uuid)", ctxt);
    if (uuid == NULL) {
        if (virUUIDGenerate(ret->uuid) < 0) {
2663 2664
            virReportError(VIR_ERR_INTERNAL_ERROR,
                           "%s", _("unable to generate uuid"));
2665 2666 2667 2668
            goto cleanup;
        }
    } else {
        if (virUUIDParse(uuid, ret->uuid) < 0) {
2669 2670
            virReportError(VIR_ERR_XML_ERROR,
                           "%s", _("malformed uuid element"));
2671 2672 2673 2674 2675 2676 2677 2678 2679
            goto cleanup;
        }
        VIR_FREE(uuid);
    }

    curr = curr->children;

    while (curr != NULL) {
        if (curr->type == XML_ELEMENT_NODE) {
2680
            if (VIR_ALLOC(entry) < 0)
2681 2682
                goto cleanup;

2683
            if (xmlStrEqual(curr->name, BAD_CAST "rule")) {
2684 2685
                if (!(entry->rule = virNWFilterRuleParse(curr))) {
                    virNWFilterEntryFree(entry);
2686
                    goto cleanup;
2687
                }
2688
            } else if (xmlStrEqual(curr->name, BAD_CAST "filterref")) {
2689 2690
                if (!(entry->include = virNWFilterIncludeParse(curr))) {
                    virNWFilterEntryFree(entry);
2691
                    goto cleanup;
2692
                }
2693
            }
2694 2695

            if (entry->rule || entry->include) {
2696 2697
                if (VIR_APPEND_ELEMENT_COPY(ret->filterEntries,
                                            ret->nentries, entry) < 0) {
2698
                    virNWFilterEntryFree(entry);
2699 2700
                    goto cleanup;
                }
2701
            } else {
2702
                virNWFilterEntryFree(entry);
2703
            }
2704 2705 2706 2707 2708
        }
        curr = curr->next;
    }

    VIR_FREE(chain);
2709
    VIR_FREE(chain_pri_s);
2710 2711 2712 2713

    return ret;

 cleanup:
2714
    virNWFilterDefFree(ret);
2715 2716
    VIR_FREE(chain);
    VIR_FREE(uuid);
2717
    VIR_FREE(chain_pri_s);
2718 2719 2720 2721 2722
    return NULL;
}


virNWFilterDefPtr
2723
virNWFilterDefParseNode(xmlDocPtr xml,
2724 2725
                        xmlNodePtr root)
{
2726 2727 2728 2729
    xmlXPathContextPtr ctxt = NULL;
    virNWFilterDefPtr def = NULL;

    if (STRNEQ((const char *)root->name, "filter")) {
2730 2731 2732
        virReportError(VIR_ERR_XML_ERROR,
                       "%s",
                       _("unknown root element for nw filter"));
2733 2734 2735 2736 2737 2738 2739 2740 2741 2742
        goto cleanup;
    }

    ctxt = xmlXPathNewContext(xml);
    if (ctxt == NULL) {
        virReportOOMError();
        goto cleanup;
    }

    ctxt->node = root;
2743
    def = virNWFilterDefParseXML(ctxt);
2744

2745
 cleanup:
2746 2747 2748 2749 2750 2751
    xmlXPathFreeContext(ctxt);
    return def;
}


static virNWFilterDefPtr
2752
virNWFilterDefParse(const char *xmlStr,
2753 2754
                    const char *filename)
{
2755 2756
    virNWFilterDefPtr def = NULL;
    xmlDocPtr xml;
2757

C
Cole Robinson 已提交
2758
    if ((xml = virXMLParse(filename, xmlStr, _("(nwfilter_definition)")))) {
2759 2760
        def = virNWFilterDefParseNode(xml, xmlDocGetRootElement(xml));
        xmlFreeDoc(xml);
2761 2762
    }

2763
    return def;
2764 2765 2766 2767
}


virNWFilterDefPtr
2768
virNWFilterDefParseString(const char *xmlStr)
2769
{
2770
    return virNWFilterDefParse(xmlStr, NULL);
2771 2772 2773 2774
}


virNWFilterDefPtr
2775
virNWFilterDefParseFile(const char *filename)
2776
{
2777
    return virNWFilterDefParse(NULL, filename);
2778 2779 2780
}


2781 2782 2783
virNWFilterObjPtr
virNWFilterObjFindByUUID(virNWFilterObjListPtr nwfilters,
                         const unsigned char *uuid)
2784
{
2785
    size_t i;
2786

2787
    for (i = 0; i < nwfilters->count; i++) {
2788 2789 2790 2791
        virNWFilterObjLock(nwfilters->objs[i]);
        if (!memcmp(nwfilters->objs[i]->def->uuid, uuid, VIR_UUID_BUFLEN))
            return nwfilters->objs[i];
        virNWFilterObjUnlock(nwfilters->objs[i]);
2792 2793 2794 2795 2796 2797
    }

    return NULL;
}


2798 2799
virNWFilterObjPtr
virNWFilterObjFindByName(virNWFilterObjListPtr nwfilters, const char *name)
2800
{
2801
    size_t i;
2802

2803
    for (i = 0; i < nwfilters->count; i++) {
2804
        virNWFilterObjLock(nwfilters->objs[i]);
2805
        if (STREQ_NULLABLE(nwfilters->objs[i]->def->name, name))
2806 2807
            return nwfilters->objs[i];
        virNWFilterObjUnlock(nwfilters->objs[i]);
2808 2809 2810 2811 2812 2813
    }

    return NULL;
}


2814
int virNWFilterSaveXML(const char *configDir,
2815 2816 2817
                       virNWFilterDefPtr def,
                       const char *xml)
{
J
Ján Tomko 已提交
2818
    char uuidstr[VIR_UUID_STRING_BUFLEN];
2819
    char *configFile = NULL;
2820
    int ret = -1;
2821

2822
    if ((configFile = virNWFilterConfigFile(configDir, def->name)) == NULL)
2823 2824
        goto cleanup;

2825 2826
    if (virFileMakePath(configDir) < 0) {
        virReportSystemError(errno,
2827 2828 2829 2830 2831
                             _("cannot create config directory '%s'"),
                             configDir);
        goto cleanup;
    }

J
Ján Tomko 已提交
2832 2833 2834 2835
    virUUIDFormat(def->uuid, uuidstr);
    ret = virXMLSaveFile(configFile,
                         virXMLPickShellSafeComment(def->name, uuidstr),
                         "nwfilter-edit", xml);
2836 2837 2838 2839 2840 2841 2842

 cleanup:
    VIR_FREE(configFile);
    return ret;
}


2843
int virNWFilterSaveConfig(const char *configDir,
2844 2845 2846 2847 2848
                          virNWFilterDefPtr def)
{
    int ret = -1;
    char *xml;

2849
    if (!(xml = virNWFilterDefFormat(def)))
2850 2851
        goto cleanup;

2852
    if (virNWFilterSaveXML(configDir, def, xml) < 0)
2853 2854 2855
        goto cleanup;

    ret = 0;
2856
 cleanup:
2857 2858 2859 2860 2861 2862
    VIR_FREE(xml);
    return ret;
}


static int
2863
_virNWFilterDefLoopDetect(virNWFilterObjListPtr nwfilters,
2864 2865 2866 2867
                          virNWFilterDefPtr def,
                          const char *filtername)
{
    int rc = 0;
2868
    size_t i;
2869
    virNWFilterEntryPtr entry;
2870
    virNWFilterObjPtr obj;
2871 2872 2873 2874 2875 2876 2877 2878 2879

    if (!def)
        return 0;

    for (i = 0; i < def->nentries; i++) {
        entry = def->filterEntries[i];
        if (entry->include) {

            if (STREQ(filtername, entry->include->filterref)) {
2880
                rc = -1;
2881 2882 2883
                break;
            }

2884 2885
            obj = virNWFilterObjFindByName(nwfilters,
                                           entry->include->filterref);
2886
            if (obj) {
2887
                rc = _virNWFilterDefLoopDetect(nwfilters,
2888 2889
                                               obj->def, filtername);

2890
                virNWFilterObjUnlock(obj);
2891 2892
                if (rc < 0)
                    break;
2893 2894 2895 2896 2897 2898 2899 2900 2901 2902
            }
        }
    }

    return rc;
}


/*
 * virNWFilterDefLoopDetect:
2903
 * @nwfilters : the nwfilters to search
J
Ján Tomko 已提交
2904
 * @def : the filter definition that may add a loop and is to be tested
2905 2906 2907 2908
 *
 * Detect a loop introduced through the filters being able to
 * reference each other.
 *
2909
 * Returns 0 in case no loop was detected, -1 otherwise.
2910 2911
 */
static int
2912
virNWFilterDefLoopDetect(virNWFilterObjListPtr nwfilters,
2913 2914
                         virNWFilterDefPtr def)
{
2915
    return _virNWFilterDefLoopDetect(nwfilters, def, def->name);
2916 2917 2918 2919 2920 2921 2922 2923 2924
}

int nCallbackDriver;
#define MAX_CALLBACK_DRIVER 10
static virNWFilterCallbackDriverPtr callbackDrvArray[MAX_CALLBACK_DRIVER];

void
virNWFilterRegisterCallbackDriver(virNWFilterCallbackDriverPtr cbd)
{
2925
    if (nCallbackDriver < MAX_CALLBACK_DRIVER)
2926 2927 2928
        callbackDrvArray[nCallbackDriver++] = cbd;
}

2929 2930 2931
void
virNWFilterUnRegisterCallbackDriver(virNWFilterCallbackDriverPtr cbd)
{
2932
    size_t i = 0;
2933 2934 2935 2936 2937 2938 2939 2940 2941 2942 2943 2944

    while (i < nCallbackDriver && callbackDrvArray[i] != cbd)
        i++;

    if (i < nCallbackDriver) {
        memmove(&callbackDrvArray[i], &callbackDrvArray[i+1],
                (nCallbackDriver - i - 1) * sizeof(callbackDrvArray[i]));
        callbackDrvArray[i] = 0;
        nCallbackDriver--;
    }
}

2945 2946 2947
void
virNWFilterCallbackDriversLock(void)
{
2948
    size_t i;
2949 2950 2951 2952 2953 2954 2955 2956

    for (i = 0; i < nCallbackDriver; i++)
        callbackDrvArray[i]->vmDriverLock();
}

void
virNWFilterCallbackDriversUnlock(void)
{
2957
    size_t i;
2958 2959 2960 2961 2962

    for (i = 0; i < nCallbackDriver; i++)
        callbackDrvArray[i]->vmDriverUnlock();
}

2963

2964
static virDomainObjListIterator virNWFilterDomainFWUpdateCB;
2965
static void *virNWFilterDomainFWUpdateOpaque;
2966

2967 2968 2969 2970 2971 2972
/**
 * virNWFilterInstFiltersOnAllVMs:
 * Apply all filters on all running VMs. Don't terminate in case of an
 * error. This should be called upon reloading of the driver.
 */
int
2973
virNWFilterInstFiltersOnAllVMs(void)
2974
{
2975
    size_t i;
2976
    struct domUpdateCBStruct cb = {
2977
        .opaque = virNWFilterDomainFWUpdateOpaque,
2978 2979 2980 2981 2982
        .step = STEP_APPLY_CURRENT,
        .skipInterfaces = NULL, /* not needed */
    };

    for (i = 0; i < nCallbackDriver; i++)
2983
        callbackDrvArray[i]->vmFilterRebuild(virNWFilterDomainFWUpdateCB,
2984 2985 2986 2987
                                             &cb);

    return 0;
}
2988 2989

static int
2990
virNWFilterTriggerVMFilterRebuild(void)
2991
{
2992
    size_t i;
2993
    int ret = 0;
2994
    struct domUpdateCBStruct cb = {
2995
        .opaque = virNWFilterDomainFWUpdateOpaque,
2996
        .step = STEP_APPLY_NEW,
2997
        .skipInterfaces = virHashCreate(0, NULL),
2998 2999
    };

3000
    if (!cb.skipInterfaces)
3001
        return -1;
3002

3003
    for (i = 0; i < nCallbackDriver; i++) {
3004
        if (callbackDrvArray[i]->vmFilterRebuild(virNWFilterDomainFWUpdateCB,
3005 3006
                                                 &cb) < 0)
            ret = -1;
3007 3008
    }

3009
    if (ret < 0) {
3010
        cb.step = STEP_TEAR_NEW; /* rollback */
3011 3012

        for (i = 0; i < nCallbackDriver; i++)
3013
            callbackDrvArray[i]->vmFilterRebuild(virNWFilterDomainFWUpdateCB,
3014 3015
                                                 &cb);
    } else {
3016
        cb.step = STEP_TEAR_OLD; /* switch over */
3017 3018

        for (i = 0; i < nCallbackDriver; i++)
3019
            callbackDrvArray[i]->vmFilterRebuild(virNWFilterDomainFWUpdateCB,
3020 3021 3022
                                                 &cb);
    }

3023
    virHashFree(cb.skipInterfaces);
3024

3025
    return ret;
3026 3027 3028 3029
}


int
3030
virNWFilterTestUnassignDef(virNWFilterObjPtr nwfilter)
3031 3032 3033
{
    int rc = 0;

3034
    nwfilter->wantRemoved = 1;
3035
    /* trigger the update on VMs referencing the filter */
3036
    if (virNWFilterTriggerVMFilterRebuild())
3037
        rc = -1;
3038

3039
    nwfilter->wantRemoved = 0;
S
Stefan Berger 已提交
3040

3041 3042 3043
    return rc;
}

3044
static bool
3045
virNWFilterDefEqual(const virNWFilterDef *def1, virNWFilterDefPtr def2,
3046 3047 3048 3049 3050 3051 3052 3053 3054 3055 3056 3057 3058 3059 3060 3061 3062 3063
                    bool cmpUUIDs)
{
    bool ret = false;
    unsigned char rem_uuid[VIR_UUID_BUFLEN];
    char *xml1, *xml2 = NULL;

    if (!cmpUUIDs) {
        /* make sure the UUIDs are equal */
        memcpy(rem_uuid, def2->uuid, sizeof(rem_uuid));
        memcpy(def2->uuid, def1->uuid, sizeof(def2->uuid));
    }

    if (!(xml1 = virNWFilterDefFormat(def1)) ||
        !(xml2 = virNWFilterDefFormat(def2)))
        goto cleanup;

    ret = STREQ(xml1, xml2);

3064
 cleanup:
3065 3066 3067 3068 3069 3070 3071 3072
    if (!cmpUUIDs)
        memcpy(def2->uuid, rem_uuid, sizeof(rem_uuid));

    VIR_FREE(xml1);
    VIR_FREE(xml2);

    return ret;
}
3073

3074
virNWFilterObjPtr
3075
virNWFilterObjAssignDef(virNWFilterObjListPtr nwfilters,
3076
                        virNWFilterDefPtr def)
3077
{
3078
    virNWFilterObjPtr nwfilter;
3079

3080
    nwfilter = virNWFilterObjFindByUUID(nwfilters, def->uuid);
3081

3082
    if (nwfilter) {
3083
        if (STRNEQ(def->name, nwfilter->def->name)) {
3084 3085 3086 3087
            virReportError(VIR_ERR_OPERATION_FAILED,
                           _("filter with same UUID but different name "
                             "('%s') already exists"),
                           nwfilter->def->name);
3088
            virNWFilterObjUnlock(nwfilter);
3089 3090
            return NULL;
        }
3091
        virNWFilterObjUnlock(nwfilter);
3092 3093 3094 3095 3096 3097 3098 3099 3100 3101 3102
    } else {
        nwfilter = virNWFilterObjFindByName(nwfilters, def->name);
        if (nwfilter) {
            char uuidstr[VIR_UUID_STRING_BUFLEN];
            virUUIDFormat(nwfilter->def->uuid, uuidstr);
            virReportError(VIR_ERR_OPERATION_FAILED,
                           _("filter '%s' already exists with uuid %s"),
                           def->name, uuidstr);
            virNWFilterObjUnlock(nwfilter);
            return NULL;
        }
3103 3104
    }

3105
    if (virNWFilterDefLoopDetect(nwfilters, def) < 0) {
3106 3107
        virReportError(VIR_ERR_OPERATION_FAILED,
                       "%s", _("filter would introduce a loop"));
3108 3109 3110
        return NULL;
    }

S
Stefan Berger 已提交
3111

3112
    if ((nwfilter = virNWFilterObjFindByName(nwfilters, def->name))) {
3113 3114 3115 3116 3117 3118 3119

        if (virNWFilterDefEqual(def, nwfilter->def, false)) {
            virNWFilterDefFree(nwfilter->def);
            nwfilter->def = def;
            return nwfilter;
        }

3120
        nwfilter->newDef = def;
3121
        /* trigger the update on VMs referencing the filter */
3122
        if (virNWFilterTriggerVMFilterRebuild()) {
3123 3124
            nwfilter->newDef = NULL;
            virNWFilterObjUnlock(nwfilter);
3125 3126 3127
            return NULL;
        }

3128 3129 3130 3131
        virNWFilterDefFree(nwfilter->def);
        nwfilter->def = def;
        nwfilter->newDef = NULL;
        return nwfilter;
3132 3133
    }

3134
    if (VIR_ALLOC(nwfilter) < 0)
3135 3136
        return NULL;

3137
    if (virMutexInitRecursive(&nwfilter->lock) < 0) {
3138 3139
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       "%s", _("cannot initialize mutex"));
3140
        VIR_FREE(nwfilter);
3141 3142
        return NULL;
    }
3143 3144
    virNWFilterObjLock(nwfilter);
    nwfilter->active = 0;
3145

3146 3147
    if (VIR_APPEND_ELEMENT_COPY(nwfilters->objs,
                                nwfilters->count, nwfilter) < 0) {
3148 3149
        virNWFilterObjUnlock(nwfilter);
        virNWFilterObjFree(nwfilter);
3150 3151
        return NULL;
    }
3152
    nwfilter->def = def;
3153

3154
    return nwfilter;
3155 3156 3157
}


3158
static virNWFilterObjPtr
3159
virNWFilterObjLoad(virNWFilterObjListPtr nwfilters,
3160 3161
                   const char *file,
                   const char *path)
3162 3163
{
    virNWFilterDefPtr def;
3164
    virNWFilterObjPtr nwfilter;
3165

3166
    if (!(def = virNWFilterDefParseFile(path)))
3167 3168 3169
        return NULL;

    if (!virFileMatchesNameSuffix(file, def->name, ".xml")) {
3170 3171 3172
        virReportError(VIR_ERR_XML_ERROR,
                       _("network filter config filename '%s' does not match name '%s'"),
                       path, def->name);
3173 3174 3175 3176
        virNWFilterDefFree(def);
        return NULL;
    }

3177
    if (!(nwfilter = virNWFilterObjAssignDef(nwfilters, def))) {
3178 3179 3180 3181
        virNWFilterDefFree(def);
        return NULL;
    }

3182
    return nwfilter;
3183 3184 3185 3186
}


int
3187
virNWFilterLoadAllConfigs(virNWFilterObjListPtr nwfilters,
3188
                          const char *configDir)
3189 3190 3191
{
    DIR *dir;
    struct dirent *entry;
E
Eric Blake 已提交
3192
    int ret = -1;
3193 3194

    if (!(dir = opendir(configDir))) {
3195
        if (errno == ENOENT)
3196 3197 3198 3199 3200 3201
            return 0;
        virReportSystemError(errno, _("Failed to open dir '%s'"),
                             configDir);
        return -1;
    }

E
Eric Blake 已提交
3202
    while ((ret = virDirRead(dir, &entry, configDir)) > 0) {
3203
        char *path;
3204
        virNWFilterObjPtr nwfilter;
3205 3206 3207 3208 3209 3210 3211

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

        if (!virFileHasSuffix(entry->d_name, ".xml"))
            continue;

3212
        if (!(path = virFileBuildPath(configDir, entry->d_name, NULL)))
3213 3214
            continue;

3215
        nwfilter = virNWFilterObjLoad(nwfilters, entry->d_name, path);
3216 3217
        if (nwfilter)
            virNWFilterObjUnlock(nwfilter);
3218 3219

        VIR_FREE(path);
3220 3221 3222
    }

    closedir(dir);
E
Eric Blake 已提交
3223
    return ret;
3224 3225 3226 3227
}


int
3228 3229
virNWFilterObjSaveDef(virNWFilterDriverStatePtr driver,
                      virNWFilterDefPtr def)
3230
{
J
Ján Tomko 已提交
3231
    char uuidstr[VIR_UUID_STRING_BUFLEN];
3232
    char *xml;
3233 3234
    int ret = -1;
    char *configFile = NULL;
3235

3236 3237 3238 3239 3240 3241 3242 3243 3244 3245
    if (virFileMakePath(driver->configDir) < 0) {
        virReportSystemError(errno,
                             _("cannot create config directory %s"),
                             driver->configDir);
        goto error;
    }

    if (!(configFile = virFileBuildPath(driver->configDir,
                                        def->name, ".xml"))) {
        goto error;
3246 3247
    }

3248
    if (!(xml = virNWFilterDefFormat(def))) {
3249 3250
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       "%s", _("failed to generate XML"));
3251
        goto error;
3252 3253
    }

J
Ján Tomko 已提交
3254
    virUUIDFormat(def->uuid, uuidstr);
3255
    ret = virXMLSaveFile(configFile,
J
Ján Tomko 已提交
3256 3257
                         virXMLPickShellSafeComment(def->name, uuidstr),
                         "nwfilter-edit", xml);
3258 3259
    VIR_FREE(xml);

3260 3261
 error:
    VIR_FREE(configFile);
3262 3263 3264 3265 3266
    return ret;
}


int
3267 3268
virNWFilterObjDeleteDef(const char *configDir,
                        virNWFilterObjPtr nwfilter)
3269
{
3270 3271 3272 3273 3274 3275
    int ret = -1;
    char *configFile = NULL;

    if (!(configFile = virFileBuildPath(configDir,
                                        nwfilter->def->name, ".xml"))) {
        goto error;
3276 3277
    }

3278
    if (unlink(configFile) < 0) {
3279 3280 3281
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("cannot remove config for %s"),
                       nwfilter->def->name);
3282
        goto error;
3283 3284
    }

3285 3286 3287 3288
    ret = 0;
 error:
    VIR_FREE(configFile);
    return ret;
3289 3290 3291 3292
}


static void
3293
virNWIPAddressFormat(virBufferPtr buf, virSocketAddrPtr ipaddr)
3294
{
3295
    char *output = virSocketAddrFormat(ipaddr);
3296 3297

    if (output) {
3298
        virBufferAdd(buf, output, -1);
3299
        VIR_FREE(output);
3300 3301 3302 3303 3304
    }
}


static void
3305
virNWFilterRuleDefDetailsFormat(virBufferPtr buf,
3306 3307 3308 3309
                                const char *type,
                                const virXMLAttr2Struct *att,
                                virNWFilterRuleDefPtr def)
{
3310
    size_t i = 0, j;
3311 3312
    bool typeShown = false;
    bool neverShown = true;
3313
    bool asHex;
3314 3315 3316 3317 3318 3319 3320 3321
    enum match {
        MATCH_NONE = 0,
        MATCH_YES,
        MATCH_NO
    } matchShown = MATCH_NONE;
    nwItemDesc *item;

    while (att[i].name) {
3322
        VIR_WARNINGS_NO_CAST_ALIGN
3323
        item = (nwItemDesc *)((char *)def + att[i].dataIdx);
3324
        VIR_WARNINGS_RESET
3325
        virNWFilterEntryItemFlags flags = item->flags;
3326 3327
        if ((flags & NWFILTER_ENTRY_ITEM_FLAG_EXISTS)) {
            if (!typeShown) {
3328
                virBufferAsprintf(buf, "<%s", type);
3329 3330
                typeShown = true;
                neverShown = false;
3331 3332 3333 3334 3335 3336 3337 3338 3339 3340 3341 3342 3343 3344 3345 3346 3347 3348 3349 3350 3351 3352
            }

            if ((flags & NWFILTER_ENTRY_ITEM_FLAG_IS_NEG)) {
                if (matchShown == MATCH_NONE) {
                    virBufferAddLit(buf, " match='no'");
                    matchShown = MATCH_NO;
                } else if (matchShown == MATCH_YES) {
                    virBufferAddLit(buf, "/>\n");
                    typeShown = 0;
                    matchShown = MATCH_NONE;
                    continue;
                }
            } else {
                if (matchShown == MATCH_NO) {
                    virBufferAddLit(buf, "/>\n");
                    typeShown = 0;
                    matchShown = MATCH_NONE;
                    continue;
                }
                matchShown = MATCH_YES;
            }

3353
            virBufferAsprintf(buf, " %s='",
3354
                              att[i].name);
S
Stefan Berger 已提交
3355
            if (att[i].formatter && !(flags & NWFILTER_ENTRY_ITEM_FLAG_HAS_VAR)) {
3356
               if (!att[i].formatter(buf, def, item)) {
3357 3358 3359 3360
                  virReportError(VIR_ERR_INTERNAL_ERROR,
                                 _("formatter for %s %s reported error"),
                                 type,
                                 att[i].name);
3361 3362 3363
                   goto err_exit;
               }
            } else if ((flags & NWFILTER_ENTRY_ITEM_FLAG_HAS_VAR)) {
3364 3365
                virBufferAddChar(buf, '$');
                virNWFilterVarAccessPrint(item->varAccess, buf);
3366
            } else {
3367 3368 3369
               asHex = false;

               switch (item->datatype) {
3370

3371 3372
               case DATATYPE_UINT8_HEX:
                   asHex = true;
3373
                   /* fallthrough */
3374
               case DATATYPE_IPMASK:
3375
               case DATATYPE_IPV6MASK:
3376
                   /* display all masks in CIDR format */
3377
               case DATATYPE_UINT8:
3378
                   virBufferAsprintf(buf, asHex ? "0x%x" : "%d",
3379
                                     item->u.u8);
3380 3381
               break;

3382 3383
               case DATATYPE_UINT16_HEX:
                   asHex = true;
3384
                   /* fallthrough */
3385
               case DATATYPE_UINT16:
3386
                   virBufferAsprintf(buf, asHex ? "0x%x" : "%d",
3387
                                     item->u.u16);
3388 3389
               break;

S
Stefan Berger 已提交
3390 3391 3392 3393 3394 3395 3396 3397
               case DATATYPE_UINT32_HEX:
                   asHex = true;
                   /* fallthrough */
               case DATATYPE_UINT32:
                   virBufferAsprintf(buf, asHex ? "0x%x" : "%u",
                                     item->u.u32);
               break;

3398
               case DATATYPE_IPADDR:
3399
               case DATATYPE_IPV6ADDR:
3400
                   virNWIPAddressFormat(buf,
3401
                                        &item->u.ipaddr);
3402 3403 3404 3405 3406
               break;

               case DATATYPE_MACMASK:
               case DATATYPE_MACADDR:
                   for (j = 0; j < 6; j++)
3407
                       virBufferAsprintf(buf, "%02x%s",
3408 3409
                                         item->u.macaddr.addr[j],
                                         (j < 5) ? ":" : "");
3410 3411
               break;

3412 3413 3414 3415
               case DATATYPE_STRINGCOPY:
                   virBufferEscapeString(buf, "%s", item->u.string);
               break;

3416
               case DATATYPE_BOOLEAN:
W
Wei Jiangang 已提交
3417
                   if (item->u.boolean)
3418 3419 3420 3421 3422
                       virBufferAddLit(buf, "true");
                   else
                       virBufferAddLit(buf, "false");
               break;

3423 3424
               case DATATYPE_STRING:
               default:
3425
                   virBufferAsprintf(buf,
3426 3427 3428 3429 3430 3431 3432 3433 3434 3435 3436 3437
                                     "UNSUPPORTED DATATYPE 0x%02x\n",
                                     att[i].datatype);
               }
            }
            virBufferAddLit(buf, "'");
        }
        i++;
    }
    if (typeShown)
       virBufferAddLit(buf, "/>\n");

    if (neverShown)
3438
       virBufferAsprintf(buf,
3439
                         "<%s/>\n", type);
3440

3441
 err_exit:
3442 3443 3444 3445
    return;
}


3446 3447
static int
virNWFilterRuleDefFormat(virBufferPtr buf, virNWFilterRuleDefPtr def)
3448
{
3449
    size_t i;
3450
    bool subelement = false;
3451

3452
    virBufferAsprintf(buf, "<rule action='%s' direction='%s' priority='%d'",
3453 3454 3455 3456
                      virNWFilterRuleActionTypeToString(def->action),
                      virNWFilterRuleDirectionTypeToString(def->tt),
                      def->priority);

3457
    if ((def->flags & RULE_FLAG_NO_STATEMATCH))
3458
        virBufferAddLit(buf, " statematch='false'");
3459

3460
    virBufferAdjustIndent(buf, 2);
3461 3462 3463
    i = 0;
    while (virAttr[i].id) {
        if (virAttr[i].prtclType == def->prtclType) {
3464 3465 3466
            if (!subelement)
                virBufferAddLit(buf, ">\n");
            virNWFilterRuleDefDetailsFormat(buf,
3467 3468 3469
                                            virAttr[i].id,
                                            virAttr[i].att,
                                            def);
3470
            subelement = true;
3471 3472 3473 3474 3475
            break;
        }
        i++;
    }

3476 3477 3478 3479 3480 3481
    virBufferAdjustIndent(buf, -2);
    if (subelement)
        virBufferAddLit(buf, "</rule>\n");
    else
        virBufferAddLit(buf, "/>\n");
    return 0;
3482 3483 3484
}


3485 3486
static int
virNWFilterEntryFormat(virBufferPtr buf, virNWFilterEntryPtr entry)
3487 3488
{
    if (entry->rule)
3489 3490 3491
        return virNWFilterRuleDefFormat(buf, entry->rule);
    return virNWFilterFormatParamAttributes(buf, entry->include->params,
                                            entry->include->filterref);
3492 3493 3494 3495
}


char *
3496
virNWFilterDefFormat(const virNWFilterDef *def)
3497 3498 3499
{
    virBuffer buf = VIR_BUFFER_INITIALIZER;
    char uuid[VIR_UUID_STRING_BUFLEN];
3500
    size_t i;
3501

3502
    virBufferAsprintf(&buf, "<filter name='%s' chain='%s'",
3503
                      def->name,
3504
                      def->chainsuffix);
3505 3506 3507
    if (def->chainPriority != 0)
        virBufferAsprintf(&buf, " priority='%d'",
                          def->chainPriority);
3508
    virBufferAddLit(&buf, ">\n");
3509
    virBufferAdjustIndent(&buf, 2);
3510 3511

    virUUIDFormat(def->uuid, uuid);
3512
    virBufferAsprintf(&buf, "<uuid>%s</uuid>\n", uuid);
3513 3514

    for (i = 0; i < def->nentries; i++) {
3515
        if (virNWFilterEntryFormat(&buf, def->filterEntries[i]) < 0)
3516 3517 3518
            goto err_exit;
    }

3519
    virBufferAdjustIndent(&buf, -2);
3520 3521
    virBufferAddLit(&buf, "</filter>\n");

3522 3523
    if (virBufferCheckError(&buf) < 0)
        goto err_exit;
3524 3525 3526 3527 3528 3529 3530 3531 3532

    return virBufferContentAndReset(&buf);

 err_exit:
    virBufferFreeAndReset(&buf);
    return NULL;
}


3533
char *virNWFilterConfigFile(const char *dir,
3534 3535 3536 3537
                            const char *name)
{
    char *ret = NULL;

3538
    ignore_value(virAsprintf(&ret, "%s/%s.xml", dir, name));
3539 3540 3541 3542
    return ret;
}


3543 3544
int virNWFilterConfLayerInit(virDomainObjListIterator domUpdateCB,
                             void *opaque)
3545
{
3546 3547 3548
    if (initialized)
        return -1;

3549
    virNWFilterDomainFWUpdateCB = domUpdateCB;
3550
    virNWFilterDomainFWUpdateOpaque = opaque;
3551

3552 3553
    initialized = true;

3554
    if (virRWLockInit(&updateLock) < 0)
3555
        return -1;
3556 3557 3558 3559 3560 3561 3562

    return 0;
}


void virNWFilterConfLayerShutdown(void)
{
3563 3564 3565
    if (!initialized)
        return;

3566
    virRWLockDestroy(&updateLock);
3567 3568

    initialized = false;
3569 3570
    virNWFilterDomainFWUpdateOpaque = NULL;
    virNWFilterDomainFWUpdateCB = NULL;
3571 3572 3573
}


3574
void virNWFilterObjLock(virNWFilterObjPtr obj)
3575 3576 3577 3578
{
    virMutexLock(&obj->lock);
}

3579
void virNWFilterObjUnlock(virNWFilterObjPtr obj)
3580 3581 3582
{
    virMutexUnlock(&obj->lock);
}
3583 3584 3585 3586 3587 3588 3589 3590 3591 3592 3593 3594 3595 3596 3597 3598 3599 3600 3601 3602 3603 3604 3605 3606 3607 3608


bool virNWFilterRuleIsProtocolIPv4(virNWFilterRuleDefPtr rule)
{
    if (rule->prtclType >= VIR_NWFILTER_RULE_PROTOCOL_TCP &&
        rule->prtclType <= VIR_NWFILTER_RULE_PROTOCOL_ALL)
        return true;
    return false;
}


bool virNWFilterRuleIsProtocolIPv6(virNWFilterRuleDefPtr rule)
{
    if (rule->prtclType >= VIR_NWFILTER_RULE_PROTOCOL_TCPoIPV6 &&
        rule->prtclType <= VIR_NWFILTER_RULE_PROTOCOL_ALLoIPV6)
        return true;
    return false;
}


bool virNWFilterRuleIsProtocolEthernet(virNWFilterRuleDefPtr rule)
{
    if (rule->prtclType <= VIR_NWFILTER_RULE_PROTOCOL_IPV6)
        return true;
    return false;
}