[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]

[libvirt] [RFC PATCHv2 8/9] add DHCP snooping



This patch adds DHCP Snooping support to libvirt.

Signed-off-by: David L Stevens <dlstevens us ibm com>
---
 examples/xml/nwfilter/no-ip-spoofing.xml |    5 +
 src/Makefile.am                          |    2 +
 src/nwfilter/nwfilter_dhcpsnoop.c        |  602 ++++++++++++++++++++++++++++++
 src/nwfilter/nwfilter_dhcpsnoop.h        |   36 ++
 src/nwfilter/nwfilter_driver.c           |    5 +
 src/nwfilter/nwfilter_gentech_driver.c   |   83 +++--
 6 files changed, 708 insertions(+), 25 deletions(-)
 create mode 100644 src/nwfilter/nwfilter_dhcpsnoop.c
 create mode 100644 src/nwfilter/nwfilter_dhcpsnoop.h

diff --git a/examples/xml/nwfilter/no-ip-spoofing.xml b/examples/xml/nwfilter/no-ip-spoofing.xml
index 2fccd12..2ae9500 100644
--- a/examples/xml/nwfilter/no-ip-spoofing.xml
+++ b/examples/xml/nwfilter/no-ip-spoofing.xml
@@ -4,4 +4,9 @@
     <rule action='return' direction='out'>
         <ip match='yes' srcipaddr='$IP' />
     </rule>
+    <!-- allow DHCP requests -->
+    <rule action='return' direction='out'>
+        <ip match='yes' srcipaddr='0.0.0.0' protocol='udp' srcportstart='68'
+                  srcportend='68' />
+    </rule>
 </filter>
diff --git a/src/Makefile.am b/src/Makefile.am
index 738ee91..f6d3fdd 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -473,6 +473,8 @@ NWFILTER_DRIVER_SOURCES =					\
 		nwfilter/nwfilter_driver.h nwfilter/nwfilter_driver.c	\
 		nwfilter/nwfilter_gentech_driver.c			\
 		nwfilter/nwfilter_gentech_driver.h			\
+		nwfilter/nwfilter_dhcpsnoop.c				\
+		nwfilter/nwfilter_dhcpsnoop.h				\
 		nwfilter/nwfilter_ebiptables_driver.c			\
 		nwfilter/nwfilter_ebiptables_driver.h			\
 		nwfilter/nwfilter_learnipaddr.c				\
diff --git a/src/nwfilter/nwfilter_dhcpsnoop.c b/src/nwfilter/nwfilter_dhcpsnoop.c
new file mode 100644
index 0000000..f784a29
--- /dev/null
+++ b/src/nwfilter/nwfilter_dhcpsnoop.c
@@ -0,0 +1,602 @@
+
+/*
+ * nwfilter_dhcpsnoop.c: support for DHCP snooping used by a VM
+ *                         on an interface
+ *
+ * Copyright (C) 2011 IBM Corp.
+ * Copyright (C) 2011 David L Stevens
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
+ *
+ * Author: David L Stevens <dlstevens us ibm com>
+ * Based in part on work by Stefan Berger <stefanb us ibm com>
+ */
+
+#include <config.h>
+
+#ifdef HAVE_LIBPCAP
+#include <pcap.h>
+#endif
+
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <signal.h>
+
+#include <arpa/inet.h>
+#include <net/ethernet.h>
+#include <netinet/ip.h>
+#include <netinet/udp.h>
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <intprops.h>
+
+#include "internal.h"
+
+#include "buf.h"
+#include "memory.h"
+#include "logging.h"
+#include "datatypes.h"
+#include "interface.h"
+#include "virterror_internal.h"
+#include "threads.h"
+#include "conf/nwfilter_params.h"
+#include "conf/domain_conf.h"
+#include "nwfilter_gentech_driver.h"
+#include "nwfilter_ebiptables_driver.h"
+#include "nwfilter_dhcpsnoop.h"
+
+#define VIR_FROM_THIS VIR_FROM_NWFILTER
+
+static virHashTablePtr SnoopReqs;
+
+struct virNWFilterSnoopReq {
+    virConnectPtr             conn;
+    virNWFilterTechDriverPtr  techdriver;
+    enum virDomainNetType     nettype;
+    virNWFilterDefPtr         filter;
+    const char               *ifname;
+    virNWFilterHashTablePtr   vars;
+    virNWFilterDriverStatePtr driver;
+    pthread_t                 thread;
+    /* start and end of lease list, ordered by lease time */
+    struct iplease           *start;
+    struct iplease           *end;
+    bool                      die;
+};
+
+#define POLL_INTERVAL    10*1000 /* 10 secs */
+
+struct iplease {
+    uint32_t                    ipl_ipaddr;
+    uint32_t                    ipl_server;
+    struct virNWFilterSnoopReq *ipl_req;
+    unsigned int                ipl_timeout;
+    /* timer list */
+    struct iplease             *ipl_prev;
+    struct iplease             *ipl_next;
+};
+
+static struct iplease *ipl_getbyip(struct iplease *start, uint32_t ipaddr);
+static void ipl_update(struct iplease *pl, uint32_t timeout);
+
+
+/*
+ * ipl_ladd - add an IP lease to a list
+ */
+static void
+ipl_ladd(struct iplease *plnew, struct iplease **start, struct iplease **end)
+{
+    struct iplease             *pl;
+
+    plnew->ipl_next = plnew->ipl_prev = 0;
+    if (!*start) {
+        plnew->ipl_prev = plnew->ipl_next = 0;
+        *start = *end = plnew;
+        return;
+    }
+    for (pl = *end; pl && plnew->ipl_timeout < pl->ipl_timeout;
+         pl = pl->ipl_prev)
+        /* empty */ ;
+    if (!pl) {
+        plnew->ipl_next = *start;
+        *start = plnew;
+    } else {
+        plnew->ipl_next = pl->ipl_next;
+        pl->ipl_next = plnew;
+    }
+    plnew->ipl_prev = pl;
+    if (plnew->ipl_next)
+        plnew->ipl_next->ipl_prev = plnew;
+    else
+        *end = plnew;
+}
+
+/*
+ * ipl_tadd - add an IP lease to the timer list
+ */
+static void
+ipl_tadd(struct iplease *plnew)
+{
+    struct virNWFilterSnoopReq *req = plnew->ipl_req;
+
+    ipl_ladd(plnew, &req->start, &req->end);
+}
+
+/*
+ * ipl_add - create or update an IP lease
+ */
+static void
+ipl_add(struct iplease *plnew)
+{
+    struct iplease *pl;
+    int             rc;
+    char            ipbuf[20];             /* dotted decimal IP addr string */
+    char           *ipstr;
+
+    pl = ipl_getbyip(plnew->ipl_req->start, plnew->ipl_ipaddr);
+    if (pl) {
+        ipl_update(pl, plnew->ipl_timeout);
+        return;
+    }
+    if (VIR_ALLOC(pl) < 0) {
+        virReportOOMError();
+        return;
+    }
+    *pl = *plnew;
+
+    if (!inet_ntop(AF_INET, &pl->ipl_ipaddr, ipbuf, sizeof(ipbuf))) {
+        virNWFilterReportError(VIR_ERR_INTERNAL_ERROR,
+                               _("ipl_add inet_ntop " "failed (0x%08X)"),
+                               pl->ipl_ipaddr);
+        VIR_FREE(pl);
+        return;
+
+    }
+
+    ipstr = strdup(ipbuf);
+    if (!ipstr) {
+        VIR_FREE(pl);
+        virReportOOMError();
+        return;
+    }
+    rc = virNWFilterChangeVar(pl->ipl_req->conn,
+                              pl->ipl_req->techdriver,
+                              pl->ipl_req->nettype,
+                              pl->ipl_req->filter,
+                              pl->ipl_req->ifname,
+                              pl->ipl_req->vars,
+                              pl->ipl_req->driver, "IP", ipstr, 0);
+    if (rc) {
+        VIR_FREE(ipstr);
+        VIR_FREE(pl);
+        return;
+    }
+    ipl_tadd(pl);
+}
+
+/*
+ * ipl_tdel - remove an IP lease from a list
+ */
+static void
+ipl_ldel(struct iplease *ipl, struct iplease **start, struct iplease **end)
+{
+    if (ipl->ipl_prev)
+        ipl->ipl_prev->ipl_next = ipl->ipl_next;
+    else
+        *start = ipl->ipl_next;
+    if (ipl->ipl_next)
+        ipl->ipl_next->ipl_prev = ipl->ipl_prev;
+    else
+        *end = ipl->ipl_prev;
+    ipl->ipl_next = ipl->ipl_prev = 0;
+}
+
+/*
+ * ipl_tdel - remove an IP lease from the timer list
+ */
+static void
+ipl_tdel(struct iplease *ipl)
+{
+    struct virNWFilterSnoopReq *req = ipl->ipl_req;
+
+    ipl_ldel(ipl, &req->start, &req->end);
+    ipl->ipl_timeout = 0;
+}
+
+/*
+ * ipl_del - delete an IP lease
+ */
+static void
+ipl_del(struct iplease *ipl)
+{
+    struct virNWFilterSnoopReq *req;
+    char                        ipbuf[20];  /* dotted decimal IP addr string */
+    char                       *ipstr;
+
+    if (!ipl)
+        return;
+
+    req = ipl->ipl_req;
+
+    ipl_tdel(ipl);
+
+    if (inet_ntop(AF_INET, &ipl->ipl_ipaddr, ipbuf, sizeof(ipbuf))) {
+        ipstr = strdup(ipbuf);
+        if (virNWFilterChangeVar(req->conn,
+                                 req->techdriver,
+                                 req->nettype,
+                                 req->filter,
+                                 req->ifname,
+                                 req->vars, req->driver, "IP", ipstr, 1))
+            virNWFilterReportError(VIR_ERR_INTERNAL_ERROR,
+                                   _("ipl_del virNWFilterChangeVar failed; "
+                                     "filter not deleted"));
+    } else
+        virNWFilterReportError(VIR_ERR_INTERNAL_ERROR,
+                               _("ipl_del inet_ntop " "failed (0x%08X)"),
+                               ipl->ipl_ipaddr);
+    VIR_FREE(ipl);
+}
+
+/*
+ * ipl_update - update the timeout on an IP lease
+ */
+static void
+ipl_update(struct iplease *ipl, uint32_t timeout)
+{
+    ipl_tdel(ipl);
+    ipl->ipl_timeout = timeout;
+    ipl_tadd(ipl);
+    return;
+}
+
+/*
+ * ipl_getbyip - lookup IP lease by IP address
+ */
+static struct iplease *
+ipl_getbyip(struct iplease *start, uint32_t ipaddr)
+{
+    struct iplease *pl;
+
+    for (pl = start; pl && pl->ipl_ipaddr != ipaddr; pl = pl->ipl_next)
+        /* empty */ ;
+    return pl;
+}
+
+#define GRACE   5
+
+/*
+ * ipl_trun - run the IP lease timeout list
+ */
+static unsigned int
+ipl_trun(struct virNWFilterSnoopReq *req)
+{
+    uint32_t now;
+
+    now = time(0);
+    while (req->start && req->start->ipl_timeout <= now)
+        ipl_del(req->start);
+    return 0;
+}
+
+typedef unsigned char Eaddr[6];
+
+struct eth {
+    Eaddr eh_dst;
+    Eaddr eh_src;
+    unsigned short eh_type;
+    unsigned char eh_data[0];
+};
+
+struct dhcp {
+    unsigned char  d_op;
+    unsigned char  d_htype;
+    unsigned char  d_hlen;
+    unsigned char  d_hops;
+    unsigned int   d_xid;
+    unsigned short d_secs;
+    unsigned short d_flags;
+    unsigned int   d_ciaddr;
+    unsigned int   d_yiaddr;
+    unsigned int   d_siaddr;
+    unsigned int   d_giaddr;
+    unsigned char  d_chaddr[16];
+    char           d_sname[64];
+    char           d_file[128];
+    unsigned char  d_opts[0];
+};
+
+/* DHCP options */
+
+#define DHCPO_PAD         0
+#define DHCPO_LEASE      51     /* lease time in secs */
+#define DHCPO_MTYPE      53     /* message type */
+#define DHCPO_END       255     /* end of options */
+
+/* DHCP message types */
+#define DHCPDECLINE     4
+#define DHCPACK         5
+#define DHCPRELEASE     7
+
+unsigned char dhcp_magic[4] = { 99, 130, 83, 99 };
+
+static int
+dhcp_getopt(struct dhcp *pd, int len, int *pmtype, int *pleasetime)
+{
+    int oind, olen;
+    int oend;
+
+    olen = len - sizeof *pd;
+    oind = 0;
+
+    if (olen < 4)               /* bad magic */
+        return -1;
+    for (oind = 0; oind < sizeof dhcp_magic; ++oind)
+        if (pd->d_opts[oind] != dhcp_magic[oind])
+            return -1;          /* bad magic */
+
+    oend = 0;
+
+    *pmtype = *pleasetime = 0;
+
+    while (oind < olen) {
+        switch (pd->d_opts[oind]) {
+            case DHCPO_LEASE:
+                if (olen - oind < 6)
+                    goto malformed;
+                if (*pleasetime)
+                    return -1;  /* duplicate lease time */
+                *pleasetime =
+                    ntohl(*(unsigned int *) (pd->d_opts + oind + 2));
+                break;
+            case DHCPO_MTYPE:
+                if (olen - oind < 3)
+                    goto malformed;
+                if (*pmtype)
+                    return -1;  /* duplicate message type */
+                *pmtype = pd->d_opts[oind + 2];
+                break;
+            case DHCPO_PAD:
+                oind++;
+                continue;
+
+            case DHCPO_END:
+                oend = 1;
+                break;
+            default:
+                if (olen - oind < 2)
+                    goto malformed;
+        }
+        if (oend)
+            break;
+        oind += pd->d_opts[oind + 1] + 2;
+    }
+    return 0;
+  malformed:
+    VIR_WARN("got lost in the options!");
+    return -1;
+}
+
+static void
+dhcpdecode(struct virNWFilterSnoopReq *req, struct eth *pep, int len)
+{
+    struct iphdr   *pip;
+    struct udphdr  *pup;
+    struct dhcp    *pd;
+    struct iplease  ipl, *pipl;
+    int             mtype, leasetime;
+
+    /* go through the protocol headers */
+    pip = (struct iphdr *) pep->eh_data;
+    len -= sizeof(*pep);
+    pup = (struct udphdr *) ((char *) pip + (pip->ihl << 2));
+    len -= pip->ihl << 2;
+    pd = (struct dhcp *) ((char *) pup + sizeof(*pup));
+    len -= sizeof(*pup);
+    if (len < 0)
+        return;                 /* dhcpdecode: invalid packet length */
+    if (dhcp_getopt(pd, len, &mtype, &leasetime) < 0)
+        return;
+
+    memset(&ipl, 0, sizeof(ipl));
+    ipl.ipl_ipaddr = pd->d_yiaddr;
+    ipl.ipl_server = pd->d_siaddr;
+    if (leasetime == ~0)
+        ipl.ipl_timeout = ~0;
+    else
+        ipl.ipl_timeout = time(0) + leasetime;
+    ipl.ipl_req = req;
+
+    switch (mtype) {
+        case DHCPACK:
+            ipl_add(&ipl);
+            break;
+        case DHCPDECLINE:
+        case DHCPRELEASE:
+            pipl = ipl_getbyip(req->start, ipl.ipl_ipaddr);
+            ipl_del(pipl);
+            break;
+        default:
+            break;
+    }
+}
+
+#define PBUFSIZE        576     /* >= IP/TCP/DHCP headers */
+
+static pcap_t *
+dhcpopen(const char *intf)
+{
+    pcap_t             *handle;
+    struct bpf_program  fp;
+    char                filter[64];
+    char                pcap_errbuf[PCAP_ERRBUF_SIZE];
+
+    handle = pcap_open_live(intf, PBUFSIZE, 0, POLL_INTERVAL, pcap_errbuf);
+    if (handle == NULL) {
+        virNWFilterReportError(VIR_ERR_INTERNAL_ERROR,
+                               _("pcap_open_live: %s"), pcap_errbuf);
+        return 0;
+    }
+
+    sprintf(filter, "port 67 or dst port 68");
+    if (pcap_compile(handle, &fp, filter, 1, PCAP_NETMASK_UNKNOWN) != 0) {
+        virNWFilterReportError(VIR_ERR_INTERNAL_ERROR,
+                               _("pcap_compile: %s"), pcap_geterr(handle));
+        return 0;
+    }
+    if (pcap_setfilter(handle, &fp) != 0) {
+        virNWFilterReportError(VIR_ERR_INTERNAL_ERROR,
+                               _("pcap_setfilter: %s"), pcap_geterr(handle));
+        return 0;
+    }
+    pcap_freecode(&fp);
+    return handle;
+}
+
+static void *
+virNWFilterDHCPSnoop(void *req0)
+{
+    struct virNWFilterSnoopReq *req = req0;
+    pcap_t                     *handle;
+    struct pcap_pkthdr          hdr;
+    struct eth                 *packet;
+    struct iplease             *ipl;
+    int                         ifindex;
+
+    handle = dhcpopen(req->ifname);
+    if (!handle)
+        return 0;
+
+    ifindex = if_nametoindex(req->ifname);
+
+    while (1) {
+        if (req->die)
+            break;
+        ipl_trun(req);
+
+        packet = (struct eth *) pcap_next(handle, &hdr);
+
+        if (!packet) {
+            if (ifaceCheck(false, req->ifname, NULL, ifindex))
+                virNWFilterDHCPSnoopEnd(req->ifname);
+            continue;
+        }
+
+        dhcpdecode(req, packet, hdr.caplen);
+    }
+    /* free all leases */
+    for (ipl = req->start; ipl; ipl = req->start)
+        ipl_del(ipl);
+
+    /* free all req data */
+    VIR_FREE(req->ifname);
+    virNWFilterHashTableFree(req->vars);
+    VIR_FREE(req);
+    return 0;
+}
+
+int
+virNWFilterDHCPSnoopReq(virConnectPtr conn,
+                        virNWFilterTechDriverPtr techdriver ATTRIBUTE_UNUSED,
+                        enum virDomainNetType nettype ATTRIBUTE_UNUSED,
+                        virNWFilterDefPtr filter ATTRIBUTE_UNUSED,
+                        const char *ifname ATTRIBUTE_UNUSED,
+                        virNWFilterHashTablePtr vars ATTRIBUTE_UNUSED,
+                        virNWFilterDriverStatePtr driver ATTRIBUTE_UNUSED)
+{
+    struct virNWFilterSnoopReq *req;
+
+    req = virHashLookup(SnoopReqs, ifname);
+    if (req)
+        return 0;
+    if (VIR_ALLOC(req) < 0) {
+        virReportOOMError();
+        return 1;
+    }
+
+    req->conn = conn;
+    req->techdriver = techdriver;
+    req->nettype = nettype;
+    req->filter = filter;
+    req->ifname = strdup(ifname);
+    req->vars = virNWFilterHashTableCreate(0);
+    if (!req->vars) {
+        virReportOOMError();
+        return 1;
+    }
+    if (virNWFilterHashTablePutAll(vars, req->vars)) {
+        virNWFilterReportError(VIR_ERR_INTERNAL_ERROR,
+                               _("virNWFilterDHCPSnoopReq: can't copy variables"
+                                " on if %s"), ifname);
+        return 1;
+    }
+    req->driver = driver;
+    req->start = req->end = 0;
+
+    if (virHashAddEntry(SnoopReqs, ifname, req)) {
+        VIR_FREE(req);
+        virNWFilterReportError(VIR_ERR_INTERNAL_ERROR,
+                               _("virNWFilterDHCPSnoopReq req add failed on"
+                                "interface \"%s\""), ifname);
+        return 1;
+    }
+    if (pthread_create(&req->thread, NULL, virNWFilterDHCPSnoop, req) != 0) {
+        (void) virHashRemoveEntry(SnoopReqs, ifname);
+        VIR_FREE(req);
+        virNWFilterReportError(VIR_ERR_INTERNAL_ERROR,
+                               _("virNWFilterDHCPSnoopReq pthread_create failed"
+                                " oninterface \"%s\""), ifname);
+        return 1;
+    }
+    return 0;
+}
+
+/*
+ * freeReq - hash table free function to kill a request
+ */
+static void
+freeReq(void *req0, const void *name ATTRIBUTE_UNUSED)
+{
+    struct virNWFilterSnoopReq *req = (struct virNWFilterSnoopReq *) req0;
+
+    if (!req)
+        return;
+
+    req->die = 1;
+}
+
+int
+virNWFilterDHCPSnoopInit(void)
+{
+    if (SnoopReqs)
+        return 0;
+    SnoopReqs = virHashCreate(0, freeReq);
+    if (!SnoopReqs) {
+        virReportOOMError();
+        return -1;
+    }
+    return 0;
+}
+
+void
+virNWFilterDHCPSnoopEnd(const char *ifname)
+{
+    if (!SnoopReqs)
+        return;
+    if (ifname)
+        virHashRemoveEntry(SnoopReqs, ifname);
+    else                        /* free all of them */
+        virHashFree(SnoopReqs);
+}
diff --git a/src/nwfilter/nwfilter_dhcpsnoop.h b/src/nwfilter/nwfilter_dhcpsnoop.h
new file mode 100644
index 0000000..248b1a0
--- /dev/null
+++ b/src/nwfilter/nwfilter_dhcpsnoop.h
@@ -0,0 +1,36 @@
+/*
+ * nwfilter_dhcpsnoop.h: support DHCP snooping for a VM on an interface
+ *
+ * Copyright (C) 2010 IBM Corp.
+ * Copyright (C) 2010 David L Stevens
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
+ *
+ * Author: David L Stevens <dlstevens us ibm com>
+ */
+
+#ifndef __NWFILTER_DHCPSNOOP_H
+# define __NWFILTER_DHCPSNOOP_H
+
+int virNWFilterDHCPSnoopInit(void);
+int virNWFilterDHCPSnoopReq(virConnectPtr conn,
+                            virNWFilterTechDriverPtr techdriver,
+                            enum virDomainNetType,
+                            virNWFilterDefPtr filter,
+                            const char *ifname,
+                            virNWFilterHashTablePtr vars,
+                            virNWFilterDriverStatePtr driver);
+void virNWFilterDHCPSnoopEnd(const char *ifname);
+#endif /* __NWFILTER_DHCPSNOOP_H */
diff --git a/src/nwfilter/nwfilter_driver.c b/src/nwfilter/nwfilter_driver.c
index a735059..57eb52c 100644
--- a/src/nwfilter/nwfilter_driver.c
+++ b/src/nwfilter/nwfilter_driver.c
@@ -39,6 +39,7 @@
 #include "nwfilter_gentech_driver.h"
 #include "configmake.h"
 
+#include "nwfilter_dhcpsnoop.h"
 #include "nwfilter_learnipaddr.h"
 
 #define VIR_FROM_THIS VIR_FROM_NWFILTER
@@ -66,6 +67,8 @@ static int
 nwfilterDriverStartup(int privileged) {
     char *base = NULL;
 
+    if (virNWFilterDHCPSnoopInit() < 0)
+        return -1;
     if (virNWFilterLearnInit() < 0)
         return -1;
 
@@ -127,6 +130,7 @@ alloc_err_exit:
 
 conf_init_err:
     virNWFilterTechDriversShutdown();
+    virNWFilterDHCPSnoopEnd(0);
     virNWFilterLearnShutdown();
 
     return -1;
@@ -201,6 +205,7 @@ nwfilterDriverShutdown(void) {
 
     virNWFilterConfLayerShutdown();
     virNWFilterTechDriversShutdown();
+    virNWFilterDHCPSnoopEnd(0);
     virNWFilterLearnShutdown();
 
     nwfilterDriverLock(driverState);
diff --git a/src/nwfilter/nwfilter_gentech_driver.c b/src/nwfilter/nwfilter_gentech_driver.c
index 563a1f3..577b48d 100644
--- a/src/nwfilter/nwfilter_gentech_driver.c
+++ b/src/nwfilter/nwfilter_gentech_driver.c
@@ -33,6 +33,7 @@
 #include "virterror_internal.h"
 #include "nwfilter_gentech_driver.h"
 #include "nwfilter_ebiptables_driver.h"
+#include "nwfilter_dhcpsnoop.h"
 #include "nwfilter_learnipaddr.h"
 
 
@@ -42,6 +43,8 @@
 #define NWFILTER_STD_VAR_MAC "MAC"
 #define NWFILTER_STD_VAR_IP  "IP"
 
+#define NWFILTER_DFLT_LEARN  "DHCP"
+
 static int _virNWFilterTeardownFilter(const char *ifname);
 
 
@@ -747,6 +750,8 @@ virNWFilterInstantiate(virConnectPtr conn,
     void **ptrs = NULL;
     int instantiate = 1;
     char *buf;
+    const char *learning = NWFILTER_DFLT_LEARN;
+    bool reportIP = false;
 
     virNWFilterHashTablePtr missing_vars = virNWFilterHashTableCreate(0);
     if (!missing_vars) {
@@ -764,39 +769,65 @@ virNWFilterInstantiate(virConnectPtr conn,
     if (rc)
         goto err_exit;
 
+    learning = virHashLookup(vars->hashTable, "ip_learning");
+
     if (virHashSize(missing_vars->hashTable) == 1) {
         if (virHashLookup(missing_vars->hashTable,
                           NWFILTER_STD_VAR_IP) != NULL) {
-            if (virNWFilterLookupLearnReq(ifindex) == NULL) {
-                rc = virNWFilterLearnIPAddress(techdriver,
-                                               ifname,
-                                               ifindex,
-                                               linkdev,
-                                               nettype, macaddr,
-                                               filter->name,
-                                               vars, driver,
-                                               DETECT_DHCP|DETECT_STATIC);
+            if (strcasecmp(learning, "none") == 0) {    /* no learning */
+                reportIP = true;
+                goto err_unresolvable_vars;
             }
-            goto err_exit;
-        }
-        goto err_unresolvable_vars;
+            if (strcasecmp(learning, "dhcp") == 0) {
+                rc = _virNWFilterInstantiateRec(conn,
+                                                techdriver,
+                                                nettype,
+                                                filter,
+                                                ifname,
+                                                vars,
+                                                NWFILTER_STD_VAR_IP, 1,
+                                                &nEntries, &insts,
+                                                useNewFilter, foundNewFilter,
+                                                driver);
+                if (!rc)
+                    rc = virNWFilterDHCPSnoopReq(conn, techdriver, nettype,
+                                                 filter, ifname, vars, driver);
+            } else if (strcasecmp(learning, "any") == 0) {
+                if (virNWFilterLookupLearnReq(ifindex) == NULL) {
+                    rc = virNWFilterLearnIPAddress(techdriver,
+                                                   ifname,
+                                                   ifindex,
+                                                   linkdev,
+                                                   nettype, macaddr,
+                                                   filter->name,
+                                                   vars, driver,
+                                                   DETECT_DHCP|DETECT_STATIC);
+                }
+                goto err_exit;
+            } else {
+                rc = 1;
+                virNWFilterReportError(VIR_ERR_PARSE_FAILED, _("filter '%s' "
+                                       "learning value '%s' invalid."),
+                                       filter->name, learning);
+            }
+        } else
+            goto err_unresolvable_vars;
     } else if (virHashSize(missing_vars->hashTable) > 1) {
         goto err_unresolvable_vars;
     } else if (!forceWithPendingReq &&
                virNWFilterLookupLearnReq(ifindex) != NULL) {
         goto err_exit;
-    }
-
-    rc = _virNWFilterInstantiateRec(conn,
-                                    techdriver,
-                                    nettype,
-                                    filter,
-                                    ifname,
-                                    vars,
-                                    0, 0,
-                                    &nEntries, &insts,
-                                    useNewFilter, foundNewFilter,
-                                    driver);
+    } else
+        rc = _virNWFilterInstantiateRec(conn,
+                                        techdriver,
+                                        nettype,
+                                        filter,
+                                        ifname,
+                                        vars,
+                                        0, 0,
+                                        &nEntries, &insts,
+                                        useNewFilter, foundNewFilter,
+                                        driver);
 
     if (rc)
         goto err_exit;
@@ -848,7 +879,7 @@ err_exit:
 
 err_unresolvable_vars:
 
-    buf = virNWFilterPrintVars(missing_vars->hashTable, ", ", false, false);
+    buf = virNWFilterPrintVars(missing_vars->hashTable, ", ", false, reportIP);
     if (buf) {
         virNWFilterReportError(VIR_ERR_INTERNAL_ERROR,
                    _("Cannot instantiate filter due to unresolvable "
@@ -1180,6 +1211,8 @@ _virNWFilterTeardownFilter(const char *ifname)
         return 1;
     }
 
+    virNWFilterDHCPSnoopEnd(ifname);
+
     virNWFilterTerminateLearnReq(ifname);
 
     if (virNWFilterLockIface(ifname))
-- 
1.7.6.4


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]