~ubuntu-branches/ubuntu/maverick/libvirt/maverick-proposed

« back to all changes in this revision

Viewing changes to src/nwfilter/nwfilter_learnipaddr.c

  • Committer: Bazaar Package Importer
  • Author(s): Guido Günther
  • Date: 2010-04-19 18:11:57 UTC
  • mfrom: (0.2.6 upstream) (3.8.3 sid)
  • mto: This revision was merged to the branch mainline in revision 92.
  • Revision ID: james.westby@ubuntu.com-20100419181157-p8x7wvat9ovlae6m
Tags: 0.8.0-2
* [70fbcb6] New patch 0007-nwfilter-Don-t-crash-if-driverState- NULL.patch
  nwfilter: Don't crash if driverState == NULL (Closes: #577728)
* [d7d1abd] New patch 0008-Ignore-empty-type-statement-in-disk-
  element.patch Ignore empty type statement in disk element
  (Closes: #578347)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * nwfilter_learnipaddr.c: support for learning IP address used by a VM
 
3
 *                         on an interface
 
4
 *
 
5
 * Copyright (C) 2010 IBM Corp.
 
6
 * Copyright (C) 2010 Stefan Berger
 
7
 *
 
8
 * This library is free software; you can redistribute it and/or
 
9
 * modify it under the terms of the GNU Lesser General Public
 
10
 * License as published by the Free Software Foundation; either
 
11
 * version 2.1 of the License, or (at your option) any later version.
 
12
 *
 
13
 * This library is distributed in the hope that it will be useful,
 
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
16
 * Lesser General Public License for more details.
 
17
 *
 
18
 * You should have received a copy of the GNU Lesser General Public
 
19
 * License along with this library; if not, write to the Free Software
 
20
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
 
21
 *
 
22
 * Author: Stefan Berger <stefanb@us.ibm.com>
 
23
 */
 
24
 
 
25
#include <config.h>
 
26
 
 
27
#ifdef HAVE_LIBPCAP
 
28
# include <pcap.h>
 
29
#endif
 
30
 
 
31
#include <fcntl.h>
 
32
#include <sys/ioctl.h>
 
33
 
 
34
#include <arpa/inet.h>
 
35
#include <net/ethernet.h>
 
36
#include <netinet/ip.h>
 
37
#include <netinet/udp.h>
 
38
#include <net/if_arp.h>
 
39
 
 
40
#include "internal.h"
 
41
 
 
42
#include "buf.h"
 
43
#include "memory.h"
 
44
#include "logging.h"
 
45
#include "datatypes.h"
 
46
#include "virterror_internal.h"
 
47
#include "threads.h"
 
48
#include "conf/nwfilter_params.h"
 
49
#include "conf/domain_conf.h"
 
50
#include "nwfilter_gentech_driver.h"
 
51
#include "nwfilter_ebiptables_driver.h"
 
52
#include "nwfilter_learnipaddr.h"
 
53
 
 
54
#define VIR_FROM_THIS VIR_FROM_NWFILTER
 
55
 
 
56
 
 
57
/* structure of an ARP request/reply message */
 
58
struct f_arphdr {
 
59
    struct arphdr arphdr;
 
60
    uint8_t ar_sha[ETH_ALEN];
 
61
    uint32_t ar_sip;
 
62
    uint8_t ar_tha[ETH_ALEN];
 
63
    uint32_t ar_tip;
 
64
} ATTRIBUTE_PACKED;
 
65
 
 
66
 
 
67
struct dhcp_option {
 
68
    uint8_t code;
 
69
    uint8_t len;
 
70
    uint8_t value[0]; /* length varies */
 
71
} ATTRIBUTE_PACKED;
 
72
 
 
73
 
 
74
/* structure representing DHCP message */
 
75
struct dhcp {
 
76
    uint8_t op;
 
77
    uint8_t htype;
 
78
    uint8_t hlen;
 
79
    uint8_t hops;
 
80
    uint32_t xid;
 
81
    uint16_t secs;
 
82
    uint16_t flags;
 
83
    uint32_t ciaddr;
 
84
    uint32_t yiaddr;
 
85
    uint32_t siaddr;
 
86
    uint32_t giaddr;
 
87
    uint8_t chaddr[16];
 
88
    uint8_t zeroes[192];
 
89
    uint32_t magic;
 
90
    struct dhcp_option options[0];
 
91
} ATTRIBUTE_PACKED;
 
92
 
 
93
#define DHCP_MSGT_DHCPOFFER 2
 
94
 
 
95
struct ether_vlan_header
 
96
{
 
97
    uint8_t dhost[ETH_ALEN];
 
98
    uint8_t shost[ETH_ALEN];
 
99
    uint16_t vlan_type;
 
100
    uint16_t vlan_flags;
 
101
    uint16_t ether_type;
 
102
} ATTRIBUTE_PACKED;
 
103
 
 
104
 
 
105
static virMutex pendingLearnReqLock;
 
106
static virHashTablePtr pendingLearnReq;
 
107
 
 
108
static virMutex ipAddressMapLock;
 
109
static virNWFilterHashTablePtr ipAddressMap;
 
110
 
 
111
 
 
112
static void
 
113
virNWFilterIPAddrLearnReqFree(virNWFilterIPAddrLearnReqPtr req) {
 
114
    if (!req)
 
115
        return;
 
116
 
 
117
    VIR_FREE(req->filtername);
 
118
    virNWFilterHashTableFree(req->filterparams);
 
119
 
 
120
    VIR_FREE(req);
 
121
}
 
122
 
 
123
 
 
124
#if HAVE_LIBPCAP
 
125
 
 
126
static int
 
127
virNWFilterRegisterLearnReq(virNWFilterIPAddrLearnReqPtr req) {
 
128
    int res = -1;
 
129
    virMutexLock(&pendingLearnReqLock);
 
130
 
 
131
    if (!virHashLookup(pendingLearnReq, req->ifname))
 
132
        res = virHashAddEntry(pendingLearnReq, req->ifname, req);
 
133
 
 
134
    virMutexUnlock(&pendingLearnReqLock);
 
135
 
 
136
    return res;
 
137
}
 
138
 
 
139
#endif
 
140
 
 
141
 
 
142
virNWFilterIPAddrLearnReqPtr
 
143
virNWFilterLookupLearnReq(const char *ifname) {
 
144
    void *res;
 
145
 
 
146
    virMutexLock(&pendingLearnReqLock);
 
147
 
 
148
    res = virHashLookup(pendingLearnReq, ifname);
 
149
 
 
150
    virMutexUnlock(&pendingLearnReqLock);
 
151
 
 
152
    return res;
 
153
}
 
154
 
 
155
 
 
156
static void
 
157
freeLearnReqEntry(void *payload, const char *name ATTRIBUTE_UNUSED) {
 
158
    virNWFilterIPAddrLearnReqFree(payload);
 
159
}
 
160
 
 
161
 
 
162
#ifdef HAVE_LIBPCAP
 
163
 
 
164
static virNWFilterIPAddrLearnReqPtr
 
165
virNWFilterDeregisterLearnReq(const char *ifname) {
 
166
    virNWFilterIPAddrLearnReqPtr res;
 
167
 
 
168
    virMutexLock(&pendingLearnReqLock);
 
169
 
 
170
    res = virHashLookup(pendingLearnReq, ifname);
 
171
 
 
172
    if (res)
 
173
        virHashRemoveEntry(pendingLearnReq, ifname, NULL);
 
174
 
 
175
    virMutexUnlock(&pendingLearnReqLock);
 
176
 
 
177
    return res;
 
178
}
 
179
 
 
180
 
 
181
 
 
182
static int
 
183
virNWFilterAddIpAddrForIfname(const char *ifname, char *addr) {
 
184
    int ret;
 
185
 
 
186
    virMutexLock(&ipAddressMapLock);
 
187
 
 
188
    ret = virNWFilterHashTablePut(ipAddressMap, ifname, addr, 1);
 
189
 
 
190
    virMutexUnlock(&ipAddressMapLock);
 
191
 
 
192
    return ret;
 
193
}
 
194
#endif
 
195
 
 
196
 
 
197
void
 
198
virNWFilterDelIpAddrForIfname(const char *ifname) {
 
199
 
 
200
    virMutexLock(&ipAddressMapLock);
 
201
 
 
202
    if (virHashLookup(ipAddressMap->hashTable, ifname))
 
203
        virNWFilterHashTableRemoveEntry(ipAddressMap, ifname);
 
204
 
 
205
    virMutexUnlock(&ipAddressMapLock);
 
206
}
 
207
 
 
208
 
 
209
const char *
 
210
virNWFilterGetIpAddrForIfname(const char *ifname) {
 
211
    const char *res;
 
212
 
 
213
    virMutexLock(&ipAddressMapLock);
 
214
 
 
215
    res = virHashLookup(ipAddressMap->hashTable, ifname);
 
216
 
 
217
    virMutexUnlock(&ipAddressMapLock);
 
218
 
 
219
    return res;
 
220
}
 
221
 
 
222
 
 
223
#ifdef HAVE_LIBPCAP
 
224
 
 
225
static void
 
226
procDHCPOpts(struct dhcp *dhcp, int dhcp_opts_len,
 
227
             uint32_t *vmaddr, uint32_t *bcastaddr,
 
228
             enum howDetect *howDetected) {
 
229
    struct dhcp_option *dhcpopt = &dhcp->options[0];
 
230
 
 
231
    while (dhcp_opts_len >= 2) {
 
232
 
 
233
        switch (dhcpopt->code) {
 
234
 
 
235
        case 28: /* Broadcast address */
 
236
            if (dhcp_opts_len >= 6) {
 
237
                uint32_t *tmp = (uint32_t *)&dhcpopt->value;
 
238
                (*bcastaddr) = ntohl(*tmp);
 
239
            }
 
240
        break;
 
241
 
 
242
        case 53: /* Message type */
 
243
            if (dhcp_opts_len >= 3) {
 
244
                uint8_t *val = (uint8_t *)&dhcpopt->value;
 
245
                switch (*val) {
 
246
                case DHCP_MSGT_DHCPOFFER:
 
247
                    *vmaddr = dhcp->yiaddr;
 
248
                    *howDetected = DETECT_DHCP;
 
249
                break;
 
250
                }
 
251
            }
 
252
        }
 
253
        dhcp_opts_len -= (2 + dhcpopt->len);
 
254
        dhcpopt = (struct dhcp_option*)((char *)dhcpopt + 2 + dhcpopt->len);
 
255
    }
 
256
}
 
257
 
 
258
 
 
259
/**
 
260
 * learnIPAddressThread
 
261
 * arg: pointer to virNWFilterIPAddrLearnReq structure
 
262
 *
 
263
 * Learn the IP address being used on an interface. Use ARP Request and
 
264
 * Reply messages, DHCP offers and the first IP packet being sent from
 
265
 * the VM to detect the IP address it is using. Detects only one IP address
 
266
 * per interface (IP aliasing not supported). The method on how the
 
267
 * IP address is detected can be chosen through flags. DETECT_DHCP will
 
268
 * require that the IP address is detected from a DHCP OFFER, DETECT_STATIC
 
269
 * will require that the IP address was taken from an ARP packet or an IPv4
 
270
 * packet. Both flags can be set at the same time.
 
271
 */
 
272
static void *
 
273
learnIPAddressThread(void *arg)
 
274
{
 
275
    char errbuf[PCAP_ERRBUF_SIZE] = {0};
 
276
    pcap_t *handle;
 
277
    struct bpf_program fp;
 
278
    struct pcap_pkthdr header;
 
279
    const u_char *packet;
 
280
    struct ether_header *ether_hdr;
 
281
    struct ether_vlan_header *vlan_hdr;
 
282
    virNWFilterIPAddrLearnReqPtr req = arg;
 
283
    uint32_t vmaddr = 0, bcastaddr = 0;
 
284
    unsigned int ethHdrSize;
 
285
    char *listen_if = (strlen(req->linkdev) != 0) ? req->linkdev
 
286
                                                  : req->ifname;
 
287
    int to_ms = (strlen(req->linkdev) != 0) ? 1000
 
288
                                            : 0;
 
289
    int dhcp_opts_len;
 
290
    char macaddr[VIR_MAC_STRING_BUFLEN];
 
291
    virBuffer buf = VIR_BUFFER_INITIALIZER;
 
292
    char *filter= NULL;
 
293
    uint16_t etherType;
 
294
    enum howDetect howDetected = 0;
 
295
 
 
296
    req->status = 0;
 
297
 
 
298
    handle = pcap_open_live(listen_if, BUFSIZ, 0, to_ms, errbuf);
 
299
 
 
300
    if (handle == NULL) {
 
301
        VIR_DEBUG("Couldn't open device %s: %s\n", listen_if, errbuf);
 
302
        req->status = ENODEV;
 
303
        goto done;
 
304
    }
 
305
 
 
306
    virFormatMacAddr(req->macaddr, macaddr);
 
307
 
 
308
    switch (req->howDetect) {
 
309
    case DETECT_DHCP:
 
310
        virBufferVSprintf(&buf, " ether dst %s"
 
311
                                " and src port 67 and dst port 68",
 
312
                          macaddr);
 
313
        break;
 
314
    default:
 
315
        virBufferVSprintf(&buf, "ether host %s", macaddr);
 
316
    }
 
317
 
 
318
    if (virBufferError(&buf)) {
 
319
        req->status = ENOMEM;
 
320
        goto done;
 
321
    }
 
322
 
 
323
    filter = virBufferContentAndReset(&buf);
 
324
 
 
325
    if (pcap_compile(handle, &fp, filter, 1, 0) != 0 ||
 
326
        pcap_setfilter(handle, &fp) != 0) {
 
327
        VIR_DEBUG("Couldn't compile or set filter '%s'.\n", filter);
 
328
        req->status = EINVAL;
 
329
        goto done;
 
330
    }
 
331
 
 
332
    while (req->status == 0 && vmaddr == 0) {
 
333
        packet = pcap_next(handle, &header);
 
334
 
 
335
        if (!packet) {
 
336
            if (to_ms == 0) {
 
337
                /* assuming IF disappeared */
 
338
                req->status = ENODEV;
 
339
                break;
 
340
            }
 
341
            /* listening on linkdev, check whether VM's dev is still there */
 
342
            if (checkIf(req->ifname, req->macaddr)) {
 
343
                req->status = ENODEV;
 
344
                break;
 
345
            }
 
346
            continue;
 
347
        }
 
348
 
 
349
        if (header.len >= sizeof(struct ether_header)) {
 
350
            ether_hdr = (struct ether_header*)packet;
 
351
 
 
352
            switch (ntohs(ether_hdr->ether_type)) {
 
353
 
 
354
            case ETHERTYPE_IP:
 
355
                ethHdrSize = sizeof(struct ether_header);
 
356
                etherType = ntohs(ether_hdr->ether_type);
 
357
                break;
 
358
 
 
359
            case ETHERTYPE_VLAN:
 
360
                ethHdrSize = sizeof(struct ether_vlan_header);
 
361
                vlan_hdr = (struct ether_vlan_header *)packet;
 
362
                if (ntohs(vlan_hdr->ether_type) != ETHERTYPE_IP ||
 
363
                    header.len < ethHdrSize)
 
364
                    continue;
 
365
                etherType = ntohs(vlan_hdr->ether_type);
 
366
                break;
 
367
 
 
368
            default:
 
369
                continue;
 
370
            }
 
371
 
 
372
            if (memcmp(ether_hdr->ether_shost,
 
373
                       req->macaddr,
 
374
                       VIR_MAC_BUFLEN) == 0) {
 
375
                // packets from the VM
 
376
 
 
377
                if (etherType == ETHERTYPE_IP &&
 
378
                    (header.len >= ethHdrSize +
 
379
                                   sizeof(struct iphdr))) {
 
380
                    struct iphdr *iphdr = (struct iphdr*)(packet +
 
381
                                                          ethHdrSize);
 
382
                    vmaddr = iphdr->saddr;
 
383
                    // skip eth. bcast and mcast addresses,
 
384
                    // and zero address in DHCP Requests
 
385
                    if ((ntohl(vmaddr) & 0xc0000000) || vmaddr == 0) {
 
386
                        vmaddr = 0;
 
387
                        continue;
 
388
                    }
 
389
 
 
390
                    howDetected = DETECT_STATIC;
 
391
                } else if (etherType == ETHERTYPE_ARP &&
 
392
                           (header.len >= ethHdrSize +
 
393
                                          sizeof(struct f_arphdr))) {
 
394
                    struct f_arphdr *arphdr = (struct f_arphdr*)(packet +
 
395
                                                         ethHdrSize);
 
396
                    switch (ntohs(arphdr->arphdr.ar_op)) {
 
397
                    case ARPOP_REPLY:
 
398
                        vmaddr = arphdr->ar_sip;
 
399
                        howDetected = DETECT_STATIC;
 
400
                    break;
 
401
                    case ARPOP_REQUEST:
 
402
                        vmaddr = arphdr->ar_tip;
 
403
                        howDetected = DETECT_STATIC;
 
404
                    break;
 
405
                    }
 
406
                }
 
407
            } else if (memcmp(ether_hdr->ether_dhost,
 
408
                              req->macaddr,
 
409
                              VIR_MAC_BUFLEN) == 0) {
 
410
                // packets to the VM
 
411
                if (etherType == ETHERTYPE_IP &&
 
412
                    (header.len >= ethHdrSize +
 
413
                                   sizeof(struct iphdr))) {
 
414
                    struct iphdr *iphdr = (struct iphdr*)(packet +
 
415
                                                          ethHdrSize);
 
416
                    if ((iphdr->protocol == IPPROTO_UDP) &&
 
417
                        (header.len >= ethHdrSize +
 
418
                                       iphdr->ihl * 4 +
 
419
                                       sizeof(struct udphdr))) {
 
420
                        struct udphdr *udphdr= (struct udphdr *)
 
421
                                          ((char *)iphdr + iphdr->ihl * 4);
 
422
                        if (ntohs(udphdr->source) == 67 &&
 
423
                            ntohs(udphdr->dest)   == 68 &&
 
424
                            header.len >= ethHdrSize +
 
425
                                          iphdr->ihl * 4 +
 
426
                                          sizeof(struct udphdr) +
 
427
                                          sizeof(struct dhcp)) {
 
428
                            struct dhcp *dhcp = (struct dhcp *)
 
429
                                        ((char *)udphdr + sizeof(udphdr));
 
430
                            if (dhcp->op == 2 /* BOOTREPLY */ &&
 
431
                                !memcmp(&dhcp->chaddr[0],
 
432
                                        req->macaddr,
 
433
                                        6)) {
 
434
                                dhcp_opts_len = header.len -
 
435
                                    (ethHdrSize + iphdr->ihl * 4 +
 
436
                                     sizeof(struct udphdr) +
 
437
                                     sizeof(struct dhcp));
 
438
                                procDHCPOpts(dhcp, dhcp_opts_len,
 
439
                                             &vmaddr,
 
440
                                             &bcastaddr,
 
441
                                             &howDetected);
 
442
                            }
 
443
                        }
 
444
                    }
 
445
                }
 
446
            }
 
447
        }
 
448
        if (vmaddr && (req->howDetect & howDetected) == 0) {
 
449
            vmaddr = 0;
 
450
            howDetected = 0;
 
451
        }
 
452
    } /* while */
 
453
 
 
454
 done:
 
455
    VIR_FREE(filter);
 
456
 
 
457
    if (handle)
 
458
        pcap_close(handle);
 
459
 
 
460
    ebtablesRemoveBasicRules(req->ifname);
 
461
 
 
462
    if (req->status == 0) {
 
463
        int ret;
 
464
        char inetaddr[INET_ADDRSTRLEN];
 
465
        inet_ntop(AF_INET, &vmaddr, inetaddr, sizeof(inetaddr));
 
466
 
 
467
        virNWFilterAddIpAddrForIfname(req->ifname, strdup(inetaddr));
 
468
 
 
469
        ret = virNWFilterInstantiateFilterLate(NULL,
 
470
                                               req->ifname,
 
471
                                               req->linkdev,
 
472
                                               req->nettype,
 
473
                                               req->macaddr,
 
474
                                               req->filtername,
 
475
                                               req->filterparams,
 
476
                                               req->driver);
 
477
        VIR_DEBUG("Result from applying firewall rules on "
 
478
                  "%s with IP addr %s : %d\n", req->ifname, inetaddr, ret);
 
479
    }
 
480
 
 
481
    memset(&req->thread, 0x0, sizeof(req->thread));
 
482
 
 
483
    VIR_DEBUG("pcap thread terminating for interface %s\n",req->ifname);
 
484
 
 
485
    virNWFilterDeregisterLearnReq(req->ifname);
 
486
 
 
487
    virNWFilterIPAddrLearnReqFree(req);
 
488
 
 
489
    return 0;
 
490
}
 
491
 
 
492
 
 
493
/**
 
494
 * virNWFilterLearnIPAddress
 
495
 * @conn: pointer to virConnect object
 
496
 * @ifname: the name of the interface
 
497
 * @linkdev : the name of the link device; currently only used in case of a
 
498
 *     macvtap device
 
499
 * @nettype : the type of interface
 
500
 * @macaddr : the MAC address of the interface
 
501
 * @filtername : the name of the top-level filter to apply to the interface
 
502
 *               once its IP address has been detected
 
503
 * @driver : the network filter driver
 
504
 * @howDetect : the method on how the thread is supposed to detect the
 
505
 *              IP address; must choose any of the available flags
 
506
 *
 
507
 * Instruct to learn the IP address being used on a given interface (ifname).
 
508
 * Unless there already is a thread attempting to learn the IP address
 
509
 * being used on the interface, a thread is started that will listen on
 
510
 * the traffic being sent on the interface (or link device) with the
 
511
 * MAC address that is provided. Will then launch the application of the
 
512
 * firewall rules on the interface.
 
513
 */
 
514
int
 
515
virNWFilterLearnIPAddress(const char *ifname,
 
516
                          const char *linkdev,
 
517
                          enum virDomainNetType nettype,
 
518
                          const unsigned char *macaddr,
 
519
                          const char *filtername,
 
520
                          virNWFilterHashTablePtr filterparams,
 
521
                          virNWFilterDriverStatePtr driver,
 
522
                          enum howDetect howDetect) {
 
523
    int rc;
 
524
    virNWFilterIPAddrLearnReqPtr req = NULL;
 
525
    virNWFilterHashTablePtr ht = NULL;
 
526
 
 
527
    if (howDetect == 0)
 
528
        return 1;
 
529
 
 
530
    if (VIR_ALLOC(req) < 0) {
 
531
        virReportOOMError();
 
532
        goto err_no_req;
 
533
    }
 
534
 
 
535
    ht = virNWFilterHashTableCreate(0);
 
536
    if (ht == NULL) {
 
537
        virReportOOMError();
 
538
        goto err_no_ht;
 
539
    }
 
540
 
 
541
    if (virNWFilterHashTablePutAll(filterparams, ht))
 
542
        goto err_free_ht;
 
543
 
 
544
    req->filtername = strdup(filtername);
 
545
    if (req->filtername == NULL) {
 
546
        virReportOOMError();
 
547
        goto err_free_ht;
 
548
    }
 
549
 
 
550
    if (virStrcpyStatic(req->ifname, ifname) == NULL) {
 
551
        virNWFilterReportError(VIR_ERR_INTERNAL_ERROR,
 
552
                               _("Destination buffer for ifname ('%s') "
 
553
                               "not large enough"), ifname);
 
554
        goto err_free_ht;
 
555
    }
 
556
 
 
557
    if (linkdev) {
 
558
        if (virStrcpyStatic(req->linkdev, linkdev) == NULL) {
 
559
            virNWFilterReportError(VIR_ERR_INTERNAL_ERROR,
 
560
                                   _("Destination buffer for linkdev ('%s') "
 
561
                                   "not large enough"), linkdev);
 
562
            goto err_free_ht;
 
563
        }
 
564
    }
 
565
    req->nettype = nettype;
 
566
    memcpy(req->macaddr, macaddr, sizeof(req->macaddr));
 
567
    req->driver = driver;
 
568
    req->filterparams = ht;
 
569
    ht = NULL;
 
570
    req->howDetect = howDetect;
 
571
 
 
572
    rc = virNWFilterRegisterLearnReq(req);
 
573
 
 
574
    if (rc)
 
575
        goto err_free_ht;
 
576
 
 
577
    switch (howDetect) {
 
578
    case DETECT_DHCP:
 
579
        if (ebtablesApplyDHCPOnlyRules(ifname,
 
580
                                       macaddr,
 
581
                                       NULL))
 
582
            goto err_free_ht;
 
583
        break;
 
584
    default:
 
585
        if (ebtablesApplyBasicRules(ifname,
 
586
                                    macaddr))
 
587
            goto err_free_ht;
 
588
    }
 
589
 
 
590
 
 
591
    if (pthread_create(&req->thread,
 
592
                       NULL,
 
593
                       learnIPAddressThread,
 
594
                       req) != 0)
 
595
        goto err_remove_rules;
 
596
 
 
597
    return 0;
 
598
 
 
599
err_remove_rules:
 
600
    ebtablesRemoveBasicRules(ifname);
 
601
err_free_ht:
 
602
    virNWFilterHashTableFree(ht);
 
603
err_no_ht:
 
604
    virNWFilterIPAddrLearnReqFree(req);
 
605
err_no_req:
 
606
    return 1;
 
607
}
 
608
 
 
609
#else
 
610
 
 
611
int
 
612
virNWFilterLearnIPAddress(const char *ifname ATTRIBUTE_UNUSED,
 
613
                          const char *linkdev ATTRIBUTE_UNUSED,
 
614
                          enum virDomainNetType nettype ATTRIBUTE_UNUSED,
 
615
                          const unsigned char *macaddr ATTRIBUTE_UNUSED,
 
616
                          const char *filtername ATTRIBUTE_UNUSED,
 
617
                          virNWFilterHashTablePtr filterparams ATTRIBUTE_UNUSED,
 
618
                          virNWFilterDriverStatePtr driver ATTRIBUTE_UNUSED,
 
619
                          enum howDetect howDetect ATTRIBUTE_UNUSED) {
 
620
    virNWFilterReportError(VIR_ERR_INTERNAL_ERROR, "%s",
 
621
                           _("IP parameter must be given since libvirt "
 
622
                             "was not compiled with IP address learning "
 
623
                             "support"));
 
624
    return 1;
 
625
}
 
626
#endif /* HAVE_LIBPCAP */
 
627
 
 
628
 
 
629
/**
 
630
 * virNWFilterLearnInit
 
631
 * Initialization of this layer
 
632
 */
 
633
int
 
634
virNWFilterLearnInit(void) {
 
635
    pendingLearnReq = virHashCreate(0);
 
636
    if (!pendingLearnReq) {
 
637
        virReportOOMError();
 
638
        return 1;
 
639
    }
 
640
 
 
641
    if (virMutexInit(&pendingLearnReqLock)) {
 
642
        virNWFilterLearnShutdown();
 
643
        return 1;
 
644
    }
 
645
 
 
646
    ipAddressMap = virNWFilterHashTableCreate(0);
 
647
    if (!ipAddressMap) {
 
648
        virReportOOMError();
 
649
        virNWFilterLearnShutdown();
 
650
        return 1;
 
651
    }
 
652
 
 
653
    if (virMutexInit(&ipAddressMapLock)) {
 
654
        virNWFilterLearnShutdown();
 
655
        return 1;
 
656
    }
 
657
 
 
658
    return 0;
 
659
}
 
660
 
 
661
 
 
662
/**
 
663
 * virNWFilterLearnShutdown
 
664
 * Shutdown of this layer
 
665
 */
 
666
void
 
667
virNWFilterLearnShutdown(void) {
 
668
    virHashFree(pendingLearnReq, freeLearnReqEntry);
 
669
    pendingLearnReq = NULL;
 
670
 
 
671
    virNWFilterHashTableFree(ipAddressMap);
 
672
    ipAddressMap = NULL;
 
673
}