nwfilter_learnipaddr.c 24.8 KB
Newer Older
1 2 3 4
/*
 * nwfilter_learnipaddr.c: support for learning IP address used by a VM
 *                         on an interface
 *
5
 * Copyright (C) 2011, 2013, 2014 Red Hat, Inc.
6 7 8 9 10 11 12 13 14 15 16 17 18 19
 * Copyright (C) 2010 IBM Corp.
 * Copyright (C) 2010 Stefan Berger
 *
 * 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
20
 * License along with this library.  If not, see
O
Osier Yang 已提交
21
 * <http://www.gnu.org/licenses/>.
22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
 *
 * Author: Stefan Berger <stefanb@us.ibm.com>
 */

#include <config.h>

#ifdef HAVE_LIBPCAP
# include <pcap.h>
#endif

#include <fcntl.h>
#include <sys/ioctl.h>

#include <arpa/inet.h>
#include <net/ethernet.h>
#include <netinet/ip.h>
#include <netinet/udp.h>
#include <net/if_arp.h>

#include "internal.h"

E
Eric Blake 已提交
43
#include "intprops.h"
44
#include "virbuffer.h"
45
#include "viralloc.h"
46
#include "virlog.h"
47
#include "datatypes.h"
48
#include "virnetdev.h"
49
#include "virerror.h"
50
#include "virthread.h"
51 52 53 54
#include "conf/nwfilter_params.h"
#include "conf/domain_conf.h"
#include "nwfilter_gentech_driver.h"
#include "nwfilter_ebiptables_driver.h"
55
#include "nwfilter_ipaddrmap.h"
56
#include "nwfilter_learnipaddr.h"
57
#include "virstring.h"
58 59 60

#define VIR_FROM_THIS VIR_FROM_NWFILTER

61 62
VIR_LOG_INIT("nwfilter.nwfilter_learnipaddr");

S
Stefan Berger 已提交
63 64 65 66 67
#define IFINDEX2STR(VARNAME, ifindex) \
    char VARNAME[INT_BUFSIZE_BOUND(ifindex)]; \
    snprintf(VARNAME, sizeof(VARNAME), "%d", ifindex);

#define PKT_TIMEOUT_MS 500 /* ms */
68 69 70 71 72 73 74 75 76 77 78

/* structure of an ARP request/reply message */
struct f_arphdr {
    struct arphdr arphdr;
    uint8_t ar_sha[ETH_ALEN];
    uint32_t ar_sip;
    uint8_t ar_tha[ETH_ALEN];
    uint32_t ar_tip;
} ATTRIBUTE_PACKED;


79 80 81 82 83 84 85
struct dhcp_option {
    uint8_t code;
    uint8_t len;
    uint8_t value[0]; /* length varies */
} ATTRIBUTE_PACKED;


86 87 88 89 90 91 92 93 94 95 96 97 98 99
/* structure representing DHCP message */
struct dhcp {
    uint8_t op;
    uint8_t htype;
    uint8_t hlen;
    uint8_t hops;
    uint32_t xid;
    uint16_t secs;
    uint16_t flags;
    uint32_t ciaddr;
    uint32_t yiaddr;
    uint32_t siaddr;
    uint32_t giaddr;
    uint8_t chaddr[16];
100 101 102
    uint8_t zeroes[192];
    uint32_t magic;
    struct dhcp_option options[0];
103 104
} ATTRIBUTE_PACKED;

105
#define DHCP_MSGT_DHCPOFFER 2
106 107 108 109 110
#define DHCP_MSGT_DHCPACK   5


#define DHCP_OPT_BCASTADDRESS 28
#define DHCP_OPT_MESSAGETYPE  53
111 112 113 114 115 116 117 118 119 120 121

struct ether_vlan_header
{
    uint8_t dhost[ETH_ALEN];
    uint8_t shost[ETH_ALEN];
    uint16_t vlan_type;
    uint16_t vlan_flags;
    uint16_t ether_type;
} ATTRIBUTE_PACKED;


122
static virMutex pendingLearnReqLock = VIR_MUTEX_INITIALIZER;
123 124
static virHashTablePtr pendingLearnReq;

125
static virMutex ifaceMapLock = VIR_MUTEX_INITIALIZER;
S
Stefan Berger 已提交
126 127 128 129 130 131 132 133 134 135 136
static virHashTablePtr ifaceLockMap;

typedef struct _virNWFilterIfaceLock virNWFilterIfaceLock;
typedef virNWFilterIfaceLock *virNWFilterIfaceLockPtr;
struct _virNWFilterIfaceLock {
    char ifname[IF_NAMESIZE];
    virMutex lock;
    int refctr;
};


137
static bool threadsTerminate;
S
Stefan Berger 已提交
138 139 140


int
141 142
virNWFilterLockIface(const char *ifname)
{
S
Stefan Berger 已提交
143 144 145 146 147 148
    virNWFilterIfaceLockPtr ifaceLock;

    virMutexLock(&ifaceMapLock);

    ifaceLock = virHashLookup(ifaceLockMap, ifname);
    if (!ifaceLock) {
149
        if (VIR_ALLOC(ifaceLock) < 0)
S
Stefan Berger 已提交
150 151
            goto err_exit;

152
        if (virMutexInitRecursive(&ifaceLock->lock) < 0) {
153 154
            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                           _("mutex initialization failed"));
S
Stefan Berger 已提交
155 156 157 158 159
            VIR_FREE(ifaceLock);
            goto err_exit;
        }

        if (virStrcpyStatic(ifaceLock->ifname, ifname) == NULL) {
160 161 162 163
            virReportError(VIR_ERR_INTERNAL_ERROR,
                           _("interface name %s does not fit into "
                             "buffer "),
                           ifaceLock->ifname);
S
Stefan Berger 已提交
164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186
            VIR_FREE(ifaceLock);
            goto err_exit;
        }

        while (virHashAddEntry(ifaceLockMap, ifname, ifaceLock)) {
            VIR_FREE(ifaceLock);
            goto err_exit;
        }

        ifaceLock->refctr = 0;
    }

    ifaceLock->refctr++;

    virMutexUnlock(&ifaceMapLock);

    virMutexLock(&ifaceLock->lock);

    return 0;

 err_exit:
    virMutexUnlock(&ifaceMapLock);

187
    return -1;
S
Stefan Berger 已提交
188 189 190 191
}


void
192 193
virNWFilterUnlockIface(const char *ifname)
{
S
Stefan Berger 已提交
194 195 196 197 198 199 200 201 202 203 204
    virNWFilterIfaceLockPtr ifaceLock;

    virMutexLock(&ifaceMapLock);

    ifaceLock = virHashLookup(ifaceLockMap, ifname);

    if (ifaceLock) {
        virMutexUnlock(&ifaceLock->lock);

        ifaceLock->refctr--;
        if (ifaceLock->refctr == 0)
205
            virHashRemoveEntry(ifaceLockMap, ifname);
S
Stefan Berger 已提交
206 207 208 209 210
    }

    virMutexUnlock(&ifaceMapLock);
}

211 212

static void
213 214
virNWFilterIPAddrLearnReqFree(virNWFilterIPAddrLearnReqPtr req)
{
215 216 217 218
    if (!req)
        return;

    VIR_FREE(req->filtername);
219
    virHashFree(req->filterparams);
220 221 222 223 224 225 226 227

    VIR_FREE(req);
}


#if HAVE_LIBPCAP

static int
228 229
virNWFilterRegisterLearnReq(virNWFilterIPAddrLearnReqPtr req)
{
230
    int res = -1;
S
Stefan Berger 已提交
231 232
    IFINDEX2STR(ifindex_str, req->ifindex);

233 234
    virMutexLock(&pendingLearnReqLock);

S
Stefan Berger 已提交
235 236
    if (!virHashLookup(pendingLearnReq, ifindex_str))
        res = virHashAddEntry(pendingLearnReq, ifindex_str, req);
237 238 239 240 241 242

    virMutexUnlock(&pendingLearnReqLock);

    return res;
}

243

244 245
#endif

246
int
247 248
virNWFilterTerminateLearnReq(const char *ifname)
{
249
    int rc = -1;
250 251 252
    int ifindex;
    virNWFilterIPAddrLearnReqPtr req;

253 254 255 256 257 258 259 260
    /* It's possible that it's already been removed as a result of
     * virNWFilterDeregisterLearnReq during learnIPAddressThread() exit
     */
    if (virNetDevExists(ifname) != 1) {
        virResetLastError();
        return 0;
    }

261 262 263 264
    if (virNetDevGetIndex(ifname, &ifindex) < 0) {
        virResetLastError();
        return rc;
    }
265

266
    IFINDEX2STR(ifindex_str, ifindex);
267

268
    virMutexLock(&pendingLearnReqLock);
269

270 271 272 273
    req = virHashLookup(pendingLearnReq, ifindex_str);
    if (req) {
        rc = 0;
        req->terminate = true;
274 275
    }

276 277
    virMutexUnlock(&pendingLearnReqLock);

278 279 280
    return rc;
}

281

282 283
bool
virNWFilterHasLearnReq(int ifindex)
284
{
285
    void *res;
S
Stefan Berger 已提交
286
    IFINDEX2STR(ifindex_str, ifindex);
287 288 289

    virMutexLock(&pendingLearnReqLock);

S
Stefan Berger 已提交
290
    res = virHashLookup(pendingLearnReq, ifindex_str);
291 292 293

    virMutexUnlock(&pendingLearnReqLock);

294
    return res != NULL;
295 296 297 298
}


static void
299 300
freeLearnReqEntry(void *payload, const void *name ATTRIBUTE_UNUSED)
{
301 302 303 304 305 306 307
    virNWFilterIPAddrLearnReqFree(payload);
}


#ifdef HAVE_LIBPCAP

static virNWFilterIPAddrLearnReqPtr
308 309
virNWFilterDeregisterLearnReq(int ifindex)
{
310
    virNWFilterIPAddrLearnReqPtr res;
S
Stefan Berger 已提交
311
    IFINDEX2STR(ifindex_str, ifindex);
312 313 314

    virMutexLock(&pendingLearnReqLock);

315
    res = virHashSteal(pendingLearnReq, ifindex_str);
316 317 318 319 320 321 322 323 324 325

    virMutexUnlock(&pendingLearnReqLock);

    return res;
}

#endif

#ifdef HAVE_LIBPCAP

326 327 328
static void
procDHCPOpts(struct dhcp *dhcp, int dhcp_opts_len,
             uint32_t *vmaddr, uint32_t *bcastaddr,
329 330
             enum howDetect *howDetected)
{
331 332 333 334 335 336
    struct dhcp_option *dhcpopt = &dhcp->options[0];

    while (dhcp_opts_len >= 2) {

        switch (dhcpopt->code) {

337
        case DHCP_OPT_BCASTADDRESS: /* Broadcast address */
338
            if (dhcp_opts_len >= 6) {
339
                VIR_WARNINGS_NO_CAST_ALIGN
340
                uint32_t *tmp = (uint32_t *)&dhcpopt->value;
341
                VIR_WARNINGS_RESET
342 343 344 345
                (*bcastaddr) = ntohl(*tmp);
            }
        break;

346
        case DHCP_OPT_MESSAGETYPE: /* Message type */
347 348 349
            if (dhcp_opts_len >= 3) {
                uint8_t *val = (uint8_t *)&dhcpopt->value;
                switch (*val) {
350
                case DHCP_MSGT_DHCPACK:
351 352 353 354 355 356 357 358 359 360 361 362 363
                case DHCP_MSGT_DHCPOFFER:
                    *vmaddr = dhcp->yiaddr;
                    *howDetected = DETECT_DHCP;
                break;
                }
            }
        }
        dhcp_opts_len -= (2 + dhcpopt->len);
        dhcpopt = (struct dhcp_option*)((char *)dhcpopt + 2 + dhcpopt->len);
    }
}


364 365 366 367 368 369 370 371 372 373 374 375 376
/**
 * learnIPAddressThread
 * arg: pointer to virNWFilterIPAddrLearnReq structure
 *
 * Learn the IP address being used on an interface. Use ARP Request and
 * Reply messages, DHCP offers and the first IP packet being sent from
 * the VM to detect the IP address it is using. Detects only one IP address
 * per interface (IP aliasing not supported). The method on how the
 * IP address is detected can be chosen through flags. DETECT_DHCP will
 * require that the IP address is detected from a DHCP OFFER, DETECT_STATIC
 * will require that the IP address was taken from an ARP packet or an IPv4
 * packet. Both flags can be set at the same time.
 */
J
Jiri Denemark 已提交
377
static void
378 379 380
learnIPAddressThread(void *arg)
{
    char errbuf[PCAP_ERRBUF_SIZE] = {0};
S
Stefan Berger 已提交
381
    pcap_t *handle = NULL;
382 383 384 385 386 387
    struct bpf_program fp;
    struct pcap_pkthdr header;
    const u_char *packet;
    struct ether_header *ether_hdr;
    struct ether_vlan_header *vlan_hdr;
    virNWFilterIPAddrLearnReqPtr req = arg;
388
    uint32_t vmaddr = 0, bcastaddr = 0;
389 390 391
    unsigned int ethHdrSize;
    char *listen_if = (strlen(req->linkdev) != 0) ? req->linkdev
                                                  : req->ifname;
392
    int dhcp_opts_len;
393 394
    char macaddr[VIR_MAC_STRING_BUFLEN];
    virBuffer buf = VIR_BUFFER_INITIALIZER;
S
Stefan Berger 已提交
395
    char *filter = NULL;
396
    uint16_t etherType;
S
Stefan Berger 已提交
397
    bool showError = true;
398
    enum howDetect howDetected = 0;
399
    virNWFilterTechDriverPtr techdriver = req->techdriver;
400

401
    if (virNWFilterLockIface(req->ifname) < 0)
S
Stefan Berger 已提交
402 403
       goto err_no_lock;

404 405
    req->status = 0;

S
Stefan Berger 已提交
406
    /* anything change to the VM's interface -- check at least once */
407 408
    if (virNetDevValidateConfig(req->ifname, NULL, req->ifindex) <= 0) {
        virResetLastError();
S
Stefan Berger 已提交
409 410 411 412 413
        req->status = ENODEV;
        goto done;
    }

    handle = pcap_open_live(listen_if, BUFSIZ, 0, PKT_TIMEOUT_MS, errbuf);
414 415

    if (handle == NULL) {
J
Jiri Denemark 已提交
416
        VIR_DEBUG("Couldn't open device %s: %s", listen_if, errbuf);
417 418 419 420
        req->status = ENODEV;
        goto done;
    }

421
    virMacAddrFormat(&req->macaddr, macaddr);
422 423 424

    switch (req->howDetect) {
    case DETECT_DHCP:
S
Stefan Berger 已提交
425
        if (techdriver->applyDHCPOnlyRules(req->ifname,
426
                                           &req->macaddr,
427
                                           NULL, false) < 0) {
S
Stefan Berger 已提交
428 429 430
            req->status = EINVAL;
            goto done;
        }
431
        virBufferAddLit(&buf, "src port 67 and dst port 68");
432
        break;
433
    case DETECT_STATIC:
S
Stefan Berger 已提交
434
        if (techdriver->applyBasicRules(req->ifname,
435
                                        &req->macaddr) < 0) {
S
Stefan Berger 已提交
436 437 438
            req->status = EINVAL;
            goto done;
        }
439 440
        virBufferAsprintf(&buf, "ether host %s or ether dst ff:ff:ff:ff:ff:ff",
                          macaddr);
441 442 443 444
        break;
    default:
        req->status = EINVAL;
        goto done;
445 446 447 448 449 450 451 452 453
    }

    if (virBufferError(&buf)) {
        req->status = ENOMEM;
        goto done;
    }

    filter = virBufferContentAndReset(&buf);

S
Stefan Berger 已提交
454
    if (pcap_compile(handle, &fp, filter, 1, 0) != 0) {
J
Jiri Denemark 已提交
455
        VIR_DEBUG("Couldn't compile filter '%s'", filter);
456 457 458 459
        req->status = EINVAL;
        goto done;
    }

S
Stefan Berger 已提交
460
    if (pcap_setfilter(handle, &fp) != 0) {
J
Jiri Denemark 已提交
461
        VIR_DEBUG("Couldn't set filter '%s'", filter);
S
Stefan Berger 已提交
462 463 464 465 466 467 468
        req->status = EINVAL;
        pcap_freecode(&fp);
        goto done;
    }

    pcap_freecode(&fp);

469 470 471 472
    while (req->status == 0 && vmaddr == 0) {
        packet = pcap_next(handle, &header);

        if (!packet) {
S
Stefan Berger 已提交
473

474
            if (threadsTerminate || req->terminate) {
S
Stefan Berger 已提交
475 476
                req->status = ECANCELED;
                showError = false;
477 478
                break;
            }
S
Stefan Berger 已提交
479 480

            /* check whether VM's dev is still there */
481 482
            if (virNetDevValidateConfig(req->ifname, NULL, req->ifindex) <= 0) {
                virResetLastError();
483
                req->status = ENODEV;
S
Stefan Berger 已提交
484
                showError = false;
485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512
                break;
            }
            continue;
        }

        if (header.len >= sizeof(struct ether_header)) {
            ether_hdr = (struct ether_header*)packet;

            switch (ntohs(ether_hdr->ether_type)) {

            case ETHERTYPE_IP:
                ethHdrSize = sizeof(struct ether_header);
                etherType = ntohs(ether_hdr->ether_type);
                break;

            case ETHERTYPE_VLAN:
                ethHdrSize = sizeof(struct ether_vlan_header);
                vlan_hdr = (struct ether_vlan_header *)packet;
                if (ntohs(vlan_hdr->ether_type) != ETHERTYPE_IP ||
                    header.len < ethHdrSize)
                    continue;
                etherType = ntohs(vlan_hdr->ether_type);
                break;

            default:
                continue;
            }

513
            if (virMacAddrCmpRaw(&req->macaddr, ether_hdr->ether_shost) == 0) {
514
                /* packets from the VM */
515 516 517 518

                if (etherType == ETHERTYPE_IP &&
                    (header.len >= ethHdrSize +
                                   sizeof(struct iphdr))) {
519
                    VIR_WARNINGS_NO_CAST_ALIGN
520 521
                    struct iphdr *iphdr = (struct iphdr*)(packet +
                                                          ethHdrSize);
522
                    VIR_WARNINGS_RESET
523
                    vmaddr = iphdr->saddr;
524 525 526
                    /* skip mcast addresses (224.0.0.0 - 239.255.255.255),
                     * class E (240.0.0.0 - 255.255.255.255, includes eth.
                     * bcast) and zero address in DHCP Requests */
527 528
                    if ((ntohl(vmaddr) & 0xe0000000) == 0xe0000000 ||
                        vmaddr == 0) {
529 530 531 532 533 534 535 536
                        vmaddr = 0;
                        continue;
                    }

                    howDetected = DETECT_STATIC;
                } else if (etherType == ETHERTYPE_ARP &&
                           (header.len >= ethHdrSize +
                                          sizeof(struct f_arphdr))) {
537
                    VIR_WARNINGS_NO_CAST_ALIGN
538 539
                    struct f_arphdr *arphdr = (struct f_arphdr*)(packet +
                                                         ethHdrSize);
540
                    VIR_WARNINGS_RESET
541 542 543 544 545 546 547 548 549 550 551
                    switch (ntohs(arphdr->arphdr.ar_op)) {
                    case ARPOP_REPLY:
                        vmaddr = arphdr->ar_sip;
                        howDetected = DETECT_STATIC;
                    break;
                    case ARPOP_REQUEST:
                        vmaddr = arphdr->ar_tip;
                        howDetected = DETECT_STATIC;
                    break;
                    }
                }
552
            } else if (virMacAddrCmpRaw(&req->macaddr,
553 554 555
                                        ether_hdr->ether_dhost) == 0 ||
                       /* allow Broadcast replies from DHCP server */
                       virMacAddrIsBroadcastRaw(ether_hdr->ether_dhost)) {
556
                /* packets to the VM */
557 558 559
                if (etherType == ETHERTYPE_IP &&
                    (header.len >= ethHdrSize +
                                   sizeof(struct iphdr))) {
560
                    VIR_WARNINGS_NO_CAST_ALIGN
561 562
                    struct iphdr *iphdr = (struct iphdr*)(packet +
                                                          ethHdrSize);
563
                    VIR_WARNINGS_RESET
564 565 566 567
                    if ((iphdr->protocol == IPPROTO_UDP) &&
                        (header.len >= ethHdrSize +
                                       iphdr->ihl * 4 +
                                       sizeof(struct udphdr))) {
568
                        VIR_WARNINGS_NO_CAST_ALIGN
569
                        struct udphdr *udphdr = (struct udphdr *)
570
                                          ((char *)iphdr + iphdr->ihl * 4);
571
                        VIR_WARNINGS_RESET
572 573 574 575 576 577 578 579
                        if (ntohs(udphdr->source) == 67 &&
                            ntohs(udphdr->dest)   == 68 &&
                            header.len >= ethHdrSize +
                                          iphdr->ihl * 4 +
                                          sizeof(struct udphdr) +
                                          sizeof(struct dhcp)) {
                            struct dhcp *dhcp = (struct dhcp *)
                                        ((char *)udphdr + sizeof(udphdr));
580
                            if (dhcp->op == 2 /* BOOTREPLY */ &&
581 582 583
                                virMacAddrCmpRaw(
                                        &req->macaddr,
                                        &dhcp->chaddr[0]) == 0) {
584 585 586 587 588 589 590 591
                                dhcp_opts_len = header.len -
                                    (ethHdrSize + iphdr->ihl * 4 +
                                     sizeof(struct udphdr) +
                                     sizeof(struct dhcp));
                                procDHCPOpts(dhcp, dhcp_opts_len,
                                             &vmaddr,
                                             &bcastaddr,
                                             &howDetected);
592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611
                            }
                        }
                    }
                }
            }
        }
        if (vmaddr && (req->howDetect & howDetected) == 0) {
            vmaddr = 0;
            howDetected = 0;
        }
    } /* while */

 done:
    VIR_FREE(filter);

    if (handle)
        pcap_close(handle);

    if (req->status == 0) {
        int ret;
612 613 614 615 616 617
        virSocketAddr sa;
        sa.len = sizeof(sa.data.inet4);
        sa.data.inet4.sin_family = AF_INET;
        sa.data.inet4.sin_addr.s_addr = vmaddr;
        char *inetaddr;

618 619 620 621 622 623 624 625 626 627
        /* It is necessary to unlock interface here to avoid updateMutex and
         * interface ordering deadlocks. Otherwise we are going to
         * instantiate the filter, which will try to lock updateMutex, and
         * some other thread instantiating a filter in parallel is holding
         * updateMutex and is trying to lock interface, both will deadlock.
         * Also it is safe to unlock interface here because we stopped
         * capturing and applied necessary rules on the interface, while
         * instantiating a new filter doesn't require a locked interface.*/
        virNWFilterUnlockIface(req->ifname);

628
        if ((inetaddr = virSocketAddrFormat(&sa)) != NULL) {
629
            if (virNWFilterIPAddrMapAddIPAddr(req->ifname, inetaddr) < 0) {
630 631 632
                VIR_ERROR(_("Failed to add IP address %s to IP address "
                          "cache for interface %s"), inetaddr, req->ifname);
            }
633

634 635
            ret = virNWFilterInstantiateFilterLate(req->driver,
                                                   NULL,
636
                                                   req->ifname,
637 638
                                                   req->ifindex,
                                                   req->linkdev,
639
                                                   &req->macaddr,
640
                                                   req->filtername,
641
                                                   req->filterparams);
642
            VIR_DEBUG("Result from applying firewall rules on "
J
Jiri Denemark 已提交
643
                      "%s with IP addr %s : %d", req->ifname, inetaddr, ret);
644
            VIR_FREE(inetaddr);
645
        }
S
Stefan Berger 已提交
646 647 648 649 650 651
    } else {
        if (showError)
            virReportSystemError(req->status,
                                 _("encountered an error on interface %s "
                                   "index %d"),
                                 req->ifname, req->ifindex);
652 653

        techdriver->applyDropAllRules(req->ifname);
654
        virNWFilterUnlockIface(req->ifname);
655 656
    }

J
Jiri Denemark 已提交
657
    VIR_DEBUG("pcap thread terminating for interface %s", req->ifname);
658

S
Stefan Berger 已提交
659 660 661

 err_no_lock:
    virNWFilterDeregisterLearnReq(req->ifindex);
662 663 664 665 666 667 668

    virNWFilterIPAddrLearnReqFree(req);
}


/**
 * virNWFilterLearnIPAddress
669
 * @techdriver : driver to build firewalls
670
 * @ifname: the name of the interface
S
Stefan Berger 已提交
671
 * @ifindex: the index of the interface
672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688
 * @linkdev : the name of the link device; currently only used in case of a
 *     macvtap device
 * @macaddr : the MAC address of the interface
 * @filtername : the name of the top-level filter to apply to the interface
 *               once its IP address has been detected
 * @driver : the network filter driver
 * @howDetect : the method on how the thread is supposed to detect the
 *              IP address; must choose any of the available flags
 *
 * Instruct to learn the IP address being used on a given interface (ifname).
 * Unless there already is a thread attempting to learn the IP address
 * being used on the interface, a thread is started that will listen on
 * the traffic being sent on the interface (or link device) with the
 * MAC address that is provided. Will then launch the application of the
 * firewall rules on the interface.
 */
int
689 690
virNWFilterLearnIPAddress(virNWFilterTechDriverPtr techdriver,
                          const char *ifname,
S
Stefan Berger 已提交
691
                          int ifindex,
692
                          const char *linkdev,
693
                          const virMacAddr *macaddr,
694
                          const char *filtername,
695
                          virHashTablePtr filterparams,
696
                          virNWFilterDriverStatePtr driver,
697 698
                          enum howDetect howDetect)
{
699
    int rc;
J
Jiri Denemark 已提交
700
    virThread thread;
701
    virNWFilterIPAddrLearnReqPtr req = NULL;
702
    virHashTablePtr ht = NULL;
703 704

    if (howDetect == 0)
705
        return -1;
706

707
    if (!techdriver->canApplyBasicRules()) {
708 709 710 711
        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                       _("IP parameter must be provided since "
                         "snooping the IP address does not work "
                         "possibly due to missing tools"));
712
        return -1;
S
Stefan Berger 已提交
713 714
    }

715
    if (VIR_ALLOC(req) < 0)
716 717 718
        goto err_no_req;

    ht = virNWFilterHashTableCreate(0);
719
    if (ht == NULL)
S
Stefan Berger 已提交
720
        goto err_free_req;
721

722
    if (virNWFilterHashTablePutAll(filterparams, ht) < 0)
723 724
        goto err_free_ht;

725
    if (VIR_STRDUP(req->filtername, filtername) < 0)
726 727 728
        goto err_free_ht;

    if (virStrcpyStatic(req->ifname, ifname) == NULL) {
729 730 731
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("Destination buffer for ifname ('%s') "
                         "not large enough"), ifname);
732 733 734 735 736
        goto err_free_ht;
    }

    if (linkdev) {
        if (virStrcpyStatic(req->linkdev, linkdev) == NULL) {
737 738 739
            virReportError(VIR_ERR_INTERNAL_ERROR,
                           _("Destination buffer for linkdev ('%s') "
                             "not large enough"), linkdev);
740 741 742
            goto err_free_ht;
        }
    }
S
Stefan Berger 已提交
743 744

    req->ifindex = ifindex;
745
    virMacAddrSet(&req->macaddr, macaddr);
746 747 748 749
    req->driver = driver;
    req->filterparams = ht;
    ht = NULL;
    req->howDetect = howDetect;
750
    req->techdriver = techdriver;
751 752 753

    rc = virNWFilterRegisterLearnReq(req);

754
    if (rc < 0)
S
Stefan Berger 已提交
755
        goto err_free_req;
756

J
Jiri Denemark 已提交
757 758 759 760
    if (virThreadCreate(&thread,
                        false,
                        learnIPAddressThread,
                        req) != 0)
S
Stefan Berger 已提交
761
        goto err_dereg_req;
762 763 764

    return 0;

765
 err_dereg_req:
S
Stefan Berger 已提交
766
    virNWFilterDeregisterLearnReq(ifindex);
767
 err_free_ht:
768
    virHashFree(ht);
769
 err_free_req:
770
    virNWFilterIPAddrLearnReqFree(req);
771
 err_no_req:
772
    return -1;
773 774 775 776 777
}

#else

int
778 779
virNWFilterLearnIPAddress(virNWFilterTechDriverPtr techdriver ATTRIBUTE_UNUSED,
                          const char *ifname ATTRIBUTE_UNUSED,
S
Stefan Berger 已提交
780
                          int ifindex ATTRIBUTE_UNUSED,
781
                          const char *linkdev ATTRIBUTE_UNUSED,
782
                          const virMacAddr *macaddr ATTRIBUTE_UNUSED,
783
                          const char *filtername ATTRIBUTE_UNUSED,
784
                          virHashTablePtr filterparams ATTRIBUTE_UNUSED,
785
                          virNWFilterDriverStatePtr driver ATTRIBUTE_UNUSED,
786 787
                          enum howDetect howDetect ATTRIBUTE_UNUSED)
{
788 789 790 791
    virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                   _("IP parameter must be given since libvirt "
                     "was not compiled with IP address learning "
                     "support"));
792
    return -1;
793 794 795 796 797 798 799 800 801
}
#endif /* HAVE_LIBPCAP */


/**
 * virNWFilterLearnInit
 * Initialization of this layer
 */
int
802 803
virNWFilterLearnInit(void)
{
S
Stefan Berger 已提交
804 805 806 807

    if (pendingLearnReq)
        return 0;

808
    VIR_DEBUG("Initializing IP address learning");
S
Stefan Berger 已提交
809 810
    threadsTerminate = false;

811
    pendingLearnReq = virHashCreate(0, freeLearnReqEntry);
812
    if (!pendingLearnReq)
813
        return -1;
814

E
Eric Blake 已提交
815
    ifaceLockMap = virHashCreate(0, virHashValueFree);
S
Stefan Berger 已提交
816 817
    if (!ifaceLockMap) {
        virNWFilterLearnShutdown();
818
        return -1;
S
Stefan Berger 已提交
819 820
    }

821 822 823 824
    return 0;
}


825
void
826 827
virNWFilterLearnThreadsTerminate(bool allowNewThreads)
{
828 829 830 831 832 833 834 835 836
    threadsTerminate = true;

    while (virHashSize(pendingLearnReq) != 0)
        usleep((PKT_TIMEOUT_MS * 1000) / 3);

    if (allowNewThreads)
        threadsTerminate = false;
}

837 838 839 840 841
/**
 * virNWFilterLearnShutdown
 * Shutdown of this layer
 */
void
842 843 844 845
virNWFilterLearnShutdown(void)
{
    if (!pendingLearnReq)
        return;
S
Stefan Berger 已提交
846

847
    virNWFilterLearnThreadsTerminate(false);
S
Stefan Berger 已提交
848

849
    virHashFree(pendingLearnReq);
850 851
    pendingLearnReq = NULL;

852
    virHashFree(ifaceLockMap);
S
Stefan Berger 已提交
853
    ifaceLockMap = NULL;
854
}