nwfilter_learnipaddr.c 24.7 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 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

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

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

/* 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;


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


84 85 86 87 88 89 90 91 92 93 94 95 96 97
/* 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];
98 99 100
    uint8_t zeroes[192];
    uint32_t magic;
    struct dhcp_option options[0];
101 102
} ATTRIBUTE_PACKED;

103
#define DHCP_MSGT_DHCPOFFER 2
104 105 106 107 108
#define DHCP_MSGT_DHCPACK   5


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

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;


static virMutex pendingLearnReqLock;
static virHashTablePtr pendingLearnReq;

S
Stefan Berger 已提交
123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
static virMutex ifaceMapLock;
static virHashTablePtr ifaceLockMap;

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


static bool threadsTerminate = false;


int
virNWFilterLockIface(const char *ifname) {
    virNWFilterIfaceLockPtr ifaceLock;

    virMutexLock(&ifaceMapLock);

    ifaceLock = virHashLookup(ifaceLockMap, ifname);
    if (!ifaceLock) {
146
        if (VIR_ALLOC(ifaceLock) < 0)
S
Stefan Berger 已提交
147 148
            goto err_exit;

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

        if (virStrcpyStatic(ifaceLock->ifname, ifname) == NULL) {
157 158 159 160
            virReportError(VIR_ERR_INTERNAL_ERROR,
                           _("interface name %s does not fit into "
                             "buffer "),
                           ifaceLock->ifname);
S
Stefan Berger 已提交
161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183
            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);

184
    return -1;
S
Stefan Berger 已提交
185 186 187 188
}


static void
189
freeIfaceLock(void *payload, const void *name ATTRIBUTE_UNUSED) {
S
Stefan Berger 已提交
190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206
    VIR_FREE(payload);
}


void
virNWFilterUnlockIface(const char *ifname) {
    virNWFilterIfaceLockPtr ifaceLock;

    virMutexLock(&ifaceMapLock);

    ifaceLock = virHashLookup(ifaceLockMap, ifname);

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

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

    virMutexUnlock(&ifaceMapLock);
}

213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230

static void
virNWFilterIPAddrLearnReqFree(virNWFilterIPAddrLearnReqPtr req) {
    if (!req)
        return;

    VIR_FREE(req->filtername);
    virNWFilterHashTableFree(req->filterparams);

    VIR_FREE(req);
}


#if HAVE_LIBPCAP

static int
virNWFilterRegisterLearnReq(virNWFilterIPAddrLearnReqPtr req) {
    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 247
int
virNWFilterTerminateLearnReq(const char *ifname) {
248
    int rc = -1;
249 250 251
    int ifindex;
    virNWFilterIPAddrLearnReqPtr req;

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

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

265
    IFINDEX2STR(ifindex_str, ifindex);
266

267
    virMutexLock(&pendingLearnReqLock);
268

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

275 276
    virMutexUnlock(&pendingLearnReqLock);

277 278 279
    return rc;
}

280 281

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

    virMutexLock(&pendingLearnReqLock);

S
Stefan Berger 已提交
288
    res = virHashLookup(pendingLearnReq, ifindex_str);
289 290 291 292 293 294 295 296

    virMutexUnlock(&pendingLearnReqLock);

    return res;
}


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


#ifdef HAVE_LIBPCAP

static virNWFilterIPAddrLearnReqPtr
S
Stefan Berger 已提交
305
virNWFilterDeregisterLearnReq(int ifindex) {
306
    virNWFilterIPAddrLearnReqPtr res;
S
Stefan Berger 已提交
307
    IFINDEX2STR(ifindex_str, ifindex);
308 309 310

    virMutexLock(&pendingLearnReqLock);

311
    res = virHashSteal(pendingLearnReq, ifindex_str);
312 313 314 315 316 317 318 319 320 321

    virMutexUnlock(&pendingLearnReqLock);

    return res;
}

#endif

#ifdef HAVE_LIBPCAP

322 323 324 325 326 327 328 329 330 331
static void
procDHCPOpts(struct dhcp *dhcp, int dhcp_opts_len,
             uint32_t *vmaddr, uint32_t *bcastaddr,
             enum howDetect *howDetected) {
    struct dhcp_option *dhcpopt = &dhcp->options[0];

    while (dhcp_opts_len >= 2) {

        switch (dhcpopt->code) {

332
        case DHCP_OPT_BCASTADDRESS: /* Broadcast address */
333
            if (dhcp_opts_len >= 6) {
334
                VIR_WARNINGS_NO_CAST_ALIGN
335
                uint32_t *tmp = (uint32_t *)&dhcpopt->value;
336
                VIR_WARNINGS_RESET
337 338 339 340
                (*bcastaddr) = ntohl(*tmp);
            }
        break;

341
        case DHCP_OPT_MESSAGETYPE: /* Message type */
342 343 344
            if (dhcp_opts_len >= 3) {
                uint8_t *val = (uint8_t *)&dhcpopt->value;
                switch (*val) {
345
                case DHCP_MSGT_DHCPACK:
346 347 348 349 350 351 352 353 354 355 356 357 358
                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);
    }
}


359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375
/**
 * 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.
 */
static void *
learnIPAddressThread(void *arg)
{
    char errbuf[PCAP_ERRBUF_SIZE] = {0};
S
Stefan Berger 已提交
376
    pcap_t *handle = NULL;
377 378 379 380 381 382
    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;
383
    uint32_t vmaddr = 0, bcastaddr = 0;
384 385 386
    unsigned int ethHdrSize;
    char *listen_if = (strlen(req->linkdev) != 0) ? req->linkdev
                                                  : req->ifname;
387
    int dhcp_opts_len;
388 389
    char macaddr[VIR_MAC_STRING_BUFLEN];
    virBuffer buf = VIR_BUFFER_INITIALIZER;
S
Stefan Berger 已提交
390
    char *filter = NULL;
391
    uint16_t etherType;
S
Stefan Berger 已提交
392
    bool showError = true;
393
    enum howDetect howDetected = 0;
394
    virNWFilterTechDriverPtr techdriver = req->techdriver;
395

396
    if (virNWFilterLockIface(req->ifname) < 0)
S
Stefan Berger 已提交
397 398
       goto err_no_lock;

399 400
    req->status = 0;

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

    handle = pcap_open_live(listen_if, BUFSIZ, 0, PKT_TIMEOUT_MS, errbuf);
409 410 411 412 413 414 415

    if (handle == NULL) {
        VIR_DEBUG("Couldn't open device %s: %s\n", listen_if, errbuf);
        req->status = ENODEV;
        goto done;
    }

416
    virMacAddrFormat(&req->macaddr, macaddr);
417 418 419

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

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

    filter = virBufferContentAndReset(&buf);

S
Stefan Berger 已提交
445 446
    if (pcap_compile(handle, &fp, filter, 1, 0) != 0) {
        VIR_DEBUG("Couldn't compile filter '%s'.\n", filter);
447 448 449 450
        req->status = EINVAL;
        goto done;
    }

S
Stefan Berger 已提交
451 452 453 454 455 456 457 458 459
    if (pcap_setfilter(handle, &fp) != 0) {
        VIR_DEBUG("Couldn't set filter '%s'.\n", filter);
        req->status = EINVAL;
        pcap_freecode(&fp);
        goto done;
    }

    pcap_freecode(&fp);

460 461 462 463
    while (req->status == 0 && vmaddr == 0) {
        packet = pcap_next(handle, &header);

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

465
            if (threadsTerminate || req->terminate) {
S
Stefan Berger 已提交
466 467
                req->status = ECANCELED;
                showError = false;
468 469
                break;
            }
S
Stefan Berger 已提交
470 471

            /* check whether VM's dev is still there */
472 473
            if (virNetDevValidateConfig(req->ifname, NULL, req->ifindex) <= 0) {
                virResetLastError();
474
                req->status = ENODEV;
S
Stefan Berger 已提交
475
                showError = false;
476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503
                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;
            }

504
            if (virMacAddrCmpRaw(&req->macaddr, ether_hdr->ether_shost) == 0) {
505
                /* packets from the VM */
506 507 508 509

                if (etherType == ETHERTYPE_IP &&
                    (header.len >= ethHdrSize +
                                   sizeof(struct iphdr))) {
510
                    VIR_WARNINGS_NO_CAST_ALIGN
511 512
                    struct iphdr *iphdr = (struct iphdr*)(packet +
                                                          ethHdrSize);
513
                    VIR_WARNINGS_RESET
514
                    vmaddr = iphdr->saddr;
515 516 517
                    /* 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 */
518 519
                    if ((ntohl(vmaddr) & 0xe0000000) == 0xe0000000 ||
                        vmaddr == 0) {
520 521 522 523 524 525 526 527
                        vmaddr = 0;
                        continue;
                    }

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

609
        if ((inetaddr = virSocketAddrFormat(&sa)) != NULL) {
610
            if (virNWFilterIPAddrMapAddIPAddr(req->ifname, inetaddr) < 0) {
611 612 613
                VIR_ERROR(_("Failed to add IP address %s to IP address "
                          "cache for interface %s"), inetaddr, req->ifname);
            }
614

615 616
            ret = virNWFilterInstantiateFilterLate(NULL,
                                                   req->ifname,
617 618 619
                                                   req->ifindex,
                                                   req->linkdev,
                                                   req->nettype,
620
                                                   &req->macaddr,
621 622 623 624 625 626
                                                   req->filtername,
                                                   req->filterparams,
                                                   req->driver);
            VIR_DEBUG("Result from applying firewall rules on "
                      "%s with IP addr %s : %d\n", req->ifname, inetaddr, ret);
        }
S
Stefan Berger 已提交
627 628 629 630 631 632
    } else {
        if (showError)
            virReportSystemError(req->status,
                                 _("encountered an error on interface %s "
                                   "index %d"),
                                 req->ifname, req->ifindex);
633 634

        techdriver->applyDropAllRules(req->ifname);
635 636 637 638 639 640
    }

    memset(&req->thread, 0x0, sizeof(req->thread));

    VIR_DEBUG("pcap thread terminating for interface %s\n",req->ifname);

S
Stefan Berger 已提交
641 642 643 644
    virNWFilterUnlockIface(req->ifname);

 err_no_lock:
    virNWFilterDeregisterLearnReq(req->ifindex);
645 646 647 648 649 650 651 652 653

    virNWFilterIPAddrLearnReqFree(req);

    return 0;
}


/**
 * virNWFilterLearnIPAddress
654
 * @techdriver : driver to build firewalls
655
 * @ifname: the name of the interface
S
Stefan Berger 已提交
656
 * @ifindex: the index of the interface
657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674
 * @linkdev : the name of the link device; currently only used in case of a
 *     macvtap device
 * @nettype : the type of interface
 * @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
675 676
virNWFilterLearnIPAddress(virNWFilterTechDriverPtr techdriver,
                          const char *ifname,
S
Stefan Berger 已提交
677
                          int ifindex,
678 679
                          const char *linkdev,
                          enum virDomainNetType nettype,
680
                          const virMacAddrPtr macaddr,
681 682 683 684 685 686 687 688 689
                          const char *filtername,
                          virNWFilterHashTablePtr filterparams,
                          virNWFilterDriverStatePtr driver,
                          enum howDetect howDetect) {
    int rc;
    virNWFilterIPAddrLearnReqPtr req = NULL;
    virNWFilterHashTablePtr ht = NULL;

    if (howDetect == 0)
690
        return -1;
691

692
    if (!techdriver->canApplyBasicRules()) {
693 694 695 696
        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                       _("IP parameter must be provided since "
                         "snooping the IP address does not work "
                         "possibly due to missing tools"));
697
        return -1;
S
Stefan Berger 已提交
698 699
    }

700
    if (VIR_ALLOC(req) < 0)
701 702 703
        goto err_no_req;

    ht = virNWFilterHashTableCreate(0);
704
    if (ht == NULL)
S
Stefan Berger 已提交
705
        goto err_free_req;
706

707
    if (virNWFilterHashTablePutAll(filterparams, ht) < 0)
708 709
        goto err_free_ht;

710
    if (VIR_STRDUP(req->filtername, filtername) < 0)
711 712 713
        goto err_free_ht;

    if (virStrcpyStatic(req->ifname, ifname) == NULL) {
714 715 716
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("Destination buffer for ifname ('%s') "
                         "not large enough"), ifname);
717 718 719 720 721
        goto err_free_ht;
    }

    if (linkdev) {
        if (virStrcpyStatic(req->linkdev, linkdev) == NULL) {
722 723 724
            virReportError(VIR_ERR_INTERNAL_ERROR,
                           _("Destination buffer for linkdev ('%s') "
                             "not large enough"), linkdev);
725 726 727
            goto err_free_ht;
        }
    }
S
Stefan Berger 已提交
728 729

    req->ifindex = ifindex;
730
    req->nettype = nettype;
731
    virMacAddrSet(&req->macaddr, macaddr);
732 733 734 735
    req->driver = driver;
    req->filterparams = ht;
    ht = NULL;
    req->howDetect = howDetect;
736
    req->techdriver = techdriver;
737 738 739

    rc = virNWFilterRegisterLearnReq(req);

740
    if (rc < 0)
S
Stefan Berger 已提交
741
        goto err_free_req;
742 743 744 745 746

    if (pthread_create(&req->thread,
                       NULL,
                       learnIPAddressThread,
                       req) != 0)
S
Stefan Berger 已提交
747
        goto err_dereg_req;
748 749 750

    return 0;

S
Stefan Berger 已提交
751 752
err_dereg_req:
    virNWFilterDeregisterLearnReq(ifindex);
753 754
err_free_ht:
    virNWFilterHashTableFree(ht);
S
Stefan Berger 已提交
755
err_free_req:
756 757
    virNWFilterIPAddrLearnReqFree(req);
err_no_req:
758
    return -1;
759 760 761 762 763
}

#else

int
764 765
virNWFilterLearnIPAddress(virNWFilterTechDriverPtr techdriver ATTRIBUTE_UNUSED,
                          const char *ifname ATTRIBUTE_UNUSED,
S
Stefan Berger 已提交
766
                          int ifindex ATTRIBUTE_UNUSED,
767 768
                          const char *linkdev ATTRIBUTE_UNUSED,
                          enum virDomainNetType nettype ATTRIBUTE_UNUSED,
769
                          const virMacAddrPtr macaddr ATTRIBUTE_UNUSED,
770 771 772 773
                          const char *filtername ATTRIBUTE_UNUSED,
                          virNWFilterHashTablePtr filterparams ATTRIBUTE_UNUSED,
                          virNWFilterDriverStatePtr driver ATTRIBUTE_UNUSED,
                          enum howDetect howDetect ATTRIBUTE_UNUSED) {
774 775 776 777
    virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                   _("IP parameter must be given since libvirt "
                     "was not compiled with IP address learning "
                     "support"));
778
    return -1;
779 780 781 782 783 784 785 786 787 788
}
#endif /* HAVE_LIBPCAP */


/**
 * virNWFilterLearnInit
 * Initialization of this layer
 */
int
virNWFilterLearnInit(void) {
S
Stefan Berger 已提交
789 790 791 792

    if (pendingLearnReq)
        return 0;

793
    VIR_DEBUG("Initializing IP address learning");
S
Stefan Berger 已提交
794 795
    threadsTerminate = false;

796
    pendingLearnReq = virHashCreate(0, freeLearnReqEntry);
797
    if (!pendingLearnReq) {
798
        return -1;
799 800
    }

801
    if (virMutexInit(&pendingLearnReqLock) < 0) {
802
        virNWFilterLearnShutdown();
803
        return -1;
804 805
    }

806
    ifaceLockMap = virHashCreate(0, freeIfaceLock);
S
Stefan Berger 已提交
807 808
    if (!ifaceLockMap) {
        virNWFilterLearnShutdown();
809
        return -1;
S
Stefan Berger 已提交
810 811
    }

812
    if (virMutexInit(&ifaceMapLock) < 0) {
S
Stefan Berger 已提交
813
        virNWFilterLearnShutdown();
814
        return -1;
S
Stefan Berger 已提交
815 816
    }

817 818 819 820
    return 0;
}


821 822 823 824 825 826 827 828 829 830 831
void
virNWFilterLearnThreadsTerminate(bool allowNewThreads) {
    threadsTerminate = true;

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

    if (allowNewThreads)
        threadsTerminate = false;
}

832 833 834 835 836
/**
 * virNWFilterLearnShutdown
 * Shutdown of this layer
 */
void
837 838 839 840
virNWFilterLearnShutdown(void)
{
    if (!pendingLearnReq)
        return;
S
Stefan Berger 已提交
841

842
    virNWFilterLearnThreadsTerminate(false);
S
Stefan Berger 已提交
843

844
    virHashFree(pendingLearnReq);
845 846
    pendingLearnReq = NULL;

847
    virHashFree(ifaceLockMap);
S
Stefan Berger 已提交
848
    ifaceLockMap = NULL;
849
}