HAL_REQUIRED=0.5.0
DEVMAPPER_REQUIRED=1.0.0
LIBCURL_REQUIRED="7.18.0"
+LIBPCAP_REQUIRED="1.0.0"
dnl Checks for C compiler.
AC_PROG_CC
AC_SUBST([NUMACTL_LIBS])
+dnl pcap lib
+LIBPCAP_CONFIG="pcap-config"
+LIBPCAP_CFLAGS=""
+LIBPCAP_LIBS=""
+LIBPCAP_FOUND="no"
+
+AC_ARG_WITH([libpcap], AC_HELP_STRING([--with-libpcap=@<:@PFX@:>@], [libpcap location]))
+if test "$with_qemu" = "yes"; then
+ if test "x$with_libpcap" != "xno" ; then
+ if test "x$with_libpcap" != "x" ; then
+ LIBPCAP_CONFIG=$with_libpcap/bin/$LIBPCAP_CONFIG
+ fi
+ AC_MSG_CHECKING(libpcap $LIBPCAP_CONFIG >= $LIBPCAP_REQUIRED )
+ if ! $LIBPCAP_CONFIG --libs > /dev/null 2>&1 ; then
+ AC_MSG_RESULT(no)
+ else
+ LIBPCAP_LIBS="`$LIBPCAP_CONFIG --libs`"
+ LIBPCAP_CFLAGS="`$LIBPCAP_CONFIG --cflags`"
+ LIBPCAP_FOUND="yes"
+ AC_MSG_RESULT(yes)
+ fi
+ fi
+fi
+
+if test "x$LIBPCAP_FOUND" = "xyes"; then
+ AC_DEFINE_UNQUOTED([HAVE_LIBPCAP], 1, [whether libpcap can be used])
+fi
+
+AC_SUBST([LIBPCAP_CFLAGS])
+AC_SUBST([LIBPCAP_LIBS])
+
+
+
dnl
dnl Checks for the UML driver
dnl
else
AC_MSG_NOTICE([ xmlrpc: no])
fi
+if test "$with_qemu" = "yes" ; then
+AC_MSG_NOTICE([ pcap: $LIBPCAP_CFLAGS $LIBPCAP_LIBS])
+else
+AC_MSG_NOTICE([ pcap: no])
+fi
AC_MSG_NOTICE([])
AC_MSG_NOTICE([Test suite])
AC_MSG_NOTICE([])
%define with_udev 0%{!?_without_udev:0}
%define with_hal 0%{!?_without_hal:0}
%define with_yajl 0%{!?_without_yajl:0}
+%define with_libpcap 0%{!?_without_libpcap:0}
# Non-server/HV driver defaults which are always enabled
%define with_python 0%{!?_without_python:1}
%define with_yajl 0%{!?_without_yajl:%{server_drivers}}
%endif
+# Enable libpcap library
+%if %{with_qemu}
+%define with_libpcap 0%{!?_without_libpcap:%{server_drivers}}
+%endif
+
# Force QEMU to run as non-root
%if 0%{?fedora} >= 12 || 0%{?rhel} >= 6
%define qemu_user qemu
%if %{with_yajl}
BuildRequires: yajl-devel
%endif
+%if %{with_libpcap}
+BuildRequires: libpcap-devel
+%endif
%if %{with_avahi}
BuildRequires: avahi-devel
%endif
nwfilter/nwfilter_gentech_driver.c \
nwfilter/nwfilter_gentech_driver.h \
nwfilter/nwfilter_ebiptables_driver.c \
- nwfilter/nwfilter_ebiptables_driver.h
+ nwfilter/nwfilter_ebiptables_driver.h \
+ nwfilter/nwfilter_learnipaddr.c \
+ nwfilter/nwfilter_learnipaddr.h
# Security framework and drivers for various models
libvirt_la_LIBADD += libvirt_driver_nwfilter.la
noinst_LTLIBRARIES += libvirt_driver_nwfilter.la
endif
-libvirt_driver_nwfilter_la_CFLAGS = \
+libvirt_driver_nwfilter_la_CFLAGS = $(LIBPCAP_CFLAGS) \
-I@top_srcdir@/src/conf
+libvirt_driver_nwfilter_la_LDFLAGS = $(LIBPCAP_LIBS)
if WITH_DRIVER_MODULES
-libvirt_driver_nwfilter_la_LDFLAGS = -module -avoid-version ../gnulib/lib/libgnu.la
+libvirt_driver_nwfilter_la_LDFLAGS += -module -avoid-version ../gnulib/lib/libgnu.la
endif
libvirt_driver_nwfilter_la_SOURCES = $(NWFILTER_DRIVER_SOURCES)
endif
-version-info $(LIBVIRT_VERSION_INFO) \
$(COVERAGE_CFLAGS:-f%=-Wc,-f%) \
$(LIBXML_LIBS) \
+ $(LIBPCAP_LIBS) \
$(DRIVER_MODULE_LIBS) \
$(CYGWIN_EXTRA_LDFLAGS) $(MINGW_EXTRA_LDFLAGS)
libvirt_la_CFLAGS = $(COVERAGE_CFLAGS) -DIN_LIBVIRT
$(NODE_INFO_SOURCES) \
$(ENCRYPTION_CONF_SOURCES) \
$(DOMAIN_CONF_SOURCES) \
- $(NWFILTER_PARAM_CONF_SOURCES) \
- $(CPU_CONF_SOURCES)
+ $(CPU_CONF_SOURCES) \
+ $(NWFILTER_PARAM_CONF_SOURCES)
libvirt_lxc_LDFLAGS = $(WARN_CFLAGS) $(COVERAGE_LDCFLAGS) $(CAPNG_LIBS) $(YAJL_LIBS)
libvirt_lxc_LDADD = $(LIBXML_LIBS) $(NUMACTL_LIBS) ../gnulib/lib/libgnu.la
libvirt_lxc_CFLAGS = \
*/
static virMutex updateMutex;
-static void
+void
virNWFilterLockFilterUpdates(void) {
virMutexLock(&updateMutex);
}
-static void
+void
virNWFilterUnlockFilterUpdates(void) {
virMutexUnlock(&updateMutex);
}
+
/*
* attribute names for the rules XML
*/
{
virNWFilterDomainFWUpdateCB = domUpdateCB;
- if (virMutexInit(&updateMutex))
+ if (virMutexInitRecursive(&updateMutex))
return 1;
return 0;
void virNWFilterPoolObjLock(virNWFilterPoolObjPtr obj);
void virNWFilterPoolObjUnlock(virNWFilterPoolObjPtr obj);
+void virNWFilterLockFilterUpdates(void);
+void virNWFilterUnlockFilterUpdates(void);
+
int virNWFilterConfLayerInit(virHashIterator domUpdateCB);
void virNWFilterConfLayerShutdown(void);
# endif
# endif
+/**
+ * ATTRIBUTE_PACKED
+ *
+ * force a structure to be packed, i.e. not following architecture and
+ * compiler best alignments for its sub components. It's needed for example
+ * for the network filetering code when defining the content of raw
+ * ethernet packets.
+ * Others compiler than gcc may use something different e.g. #pragma pack(1)
+ */
+# ifndef ATTRIBUTE_PACKED
+# if __GNUC_PREREQ (3, 3)
+# define ATTRIBUTE_PACKED __attribute__((packed))
+# else
+# error "Need an __attribute__((packed)) equivalent"
+# endif
+# endif
+
# ifndef ATTRIBUTE_NONNULL
# if __GNUC_PREREQ (3, 3)
# define ATTRIBUTE_NONNULL(m) __attribute__((__nonnull__(m)))
virNWFilterTestUnassignDef;
virNWFilterConfLayerInit;
virNWFilterConfLayerShutdown;
+virNWFilterLockFilterUpdates;
+virNWFilterUnlockFilterUpdates;
#nwfilter_params.h
virNWFilterTeardownFilter;
+#nwfilter_learnipaddr.h
+ipAddressMap;
+ipAddressMapLock;
+pendingLearnReq;
+pendingLearnReqLock;
+virNWFilterGetIpAddrForIfname;
+virNWFilterDelIpAddrForIfname;
+virNWFilterLookupLearnReq;
+
+
# pci.h
pciGetDevice;
pciFreeDevice;
#include "nwfilter_gentech_driver.h"
+#include "nwfilter_learnipaddr.h"
+
#define VIR_FROM_THIS VIR_FROM_NWFILTER
#define nwfilterLog(msg...) fprintf(stderr, msg)
nwfilterDriverStartup(int privileged) {
char *base = NULL;
- if (virNWFilterConfLayerInit(virNWFilterDomainFWUpdateCB) < 0)
+ if (virNWFilterLearnInit() < 0)
return -1;
+ if (virNWFilterConfLayerInit(virNWFilterDomainFWUpdateCB) < 0)
+ goto conf_init_err;
+
if (VIR_ALLOC(driverState) < 0)
goto alloc_err_exit;
alloc_err_exit:
virNWFilterConfLayerShutdown();
+conf_init_err:
+ virNWFilterLearnShutdown();
+
return -1;
}
int nwfilterRegister(void) {
virRegisterNWFilterDriver(&nwfilterDriver);
virRegisterStateDriver(&stateDriver);
+ virNWFilterLearnInit();
return 0;
}
}
+/**
+ * ebtablesApplyBasicRules
+ *
+ * @conn: virConnect object
+ * @ifname: name of the backend-interface to which to apply the rules
+ * @macaddr: MAC address the VM is using in packets sent through the
+ * interface
+ *
+ * Returns 0 on success, 1 on failure with the rules removed
+ *
+ * Apply basic filtering rules on the given interface
+ * - filtering for MAC address spoofing
+ * - allowing IPv4 & ARP traffic
+ */
+int
+ebtablesApplyBasicRules(const char *ifname,
+ const unsigned char *macaddr)
+{
+ virBuffer buf = VIR_BUFFER_INITIALIZER;
+ int cli_status;
+ char chain[MAX_CHAINNAME_LENGTH];
+ char chainPrefix = CHAINPREFIX_HOST_IN_TEMP;
+ char macaddr_str[VIR_MAC_STRING_BUFLEN];
+
+ virFormatMacAddr(macaddr, macaddr_str);
+
+ ebtablesUnlinkTmpRootChain(&buf, 1, ifname);
+ ebtablesUnlinkTmpRootChain(&buf, 0, ifname);
+ ebtablesRemoveTmpSubChains(&buf, ifname);
+ ebtablesRemoveTmpRootChain(&buf, 1, ifname);
+ ebtablesRemoveTmpRootChain(&buf, 0, ifname);
+ ebiptablesExecCLI(&buf, &cli_status);
+
+ ebtablesCreateTmpRootChain(&buf, 1, ifname, 1);
+
+ PRINT_ROOT_CHAIN(chain, chainPrefix, ifname);
+ virBufferVSprintf(&buf,
+ CMD_DEF(EBTABLES_CMD
+ " -t %s -A %s -s ! %s -j DROP") CMD_SEPARATOR
+ CMD_EXEC
+ "%s",
+
+ EBTABLES_DEFAULT_TABLE,
+ chain,
+ macaddr_str,
+ CMD_STOPONERR(1));
+
+ virBufferVSprintf(&buf,
+ CMD_DEF(EBTABLES_CMD
+ " -t %s -A %s -p IPv4 -j ACCEPT") CMD_SEPARATOR
+ CMD_EXEC
+ "%s",
+
+ EBTABLES_DEFAULT_TABLE,
+ chain,
+ CMD_STOPONERR(1));
+
+ virBufferVSprintf(&buf,
+ CMD_DEF(EBTABLES_CMD
+ " -t %s -A %s -p ARP -j ACCEPT") CMD_SEPARATOR
+ CMD_EXEC
+ "%s",
+
+ EBTABLES_DEFAULT_TABLE,
+ chain,
+ CMD_STOPONERR(1));
+
+ virBufferVSprintf(&buf,
+ CMD_DEF(EBTABLES_CMD
+ " -t %s -A %s -j DROP") CMD_SEPARATOR
+ CMD_EXEC
+ "%s",
+
+ EBTABLES_DEFAULT_TABLE,
+ chain,
+ CMD_STOPONERR(1));
+
+ ebtablesLinkTmpRootChain(&buf, 1, ifname, 1);
+
+ if (ebiptablesExecCLI(&buf, &cli_status) || cli_status != 0)
+ goto tear_down_tmpebchains;
+
+ return 0;
+
+tear_down_tmpebchains:
+ ebtablesRemoveBasicRules(ifname);
+
+ virNWFilterReportError(VIR_ERR_BUILD_FIREWALL,
+ "%s",
+ _("Some rules could not be created."));
+
+ return 1;
+}
+
+
+/**
+ * ebtablesApplyDHCPOnlyRules
+ *
+ * @ifname: name of the backend-interface to which to apply the rules
+ * @macaddr: MAC address the VM is using in packets sent through the
+ * interface
+ * @dhcpserver: The DHCP server from which the VM may receive traffic
+ * from; may be NULL
+ *
+ * Returns 0 on success, 1 on failure with the rules removed
+ *
+ * Apply filtering rules so that the VM can only send and receive
+ * DHCP traffic and nothing else.
+ */
+int
+ebtablesApplyDHCPOnlyRules(const char *ifname,
+ const unsigned char *macaddr,
+ const char *dhcpserver)
+{
+ virBuffer buf = VIR_BUFFER_INITIALIZER;
+ int cli_status;
+ char chain_in [MAX_CHAINNAME_LENGTH],
+ chain_out[MAX_CHAINNAME_LENGTH];
+ char macaddr_str[VIR_MAC_STRING_BUFLEN];
+ char *srcIPParam = NULL;
+
+ if (dhcpserver) {
+ virBufferVSprintf(&buf, " --ip-src %s", dhcpserver);
+ if (virBufferError(&buf))
+ return 1;
+ srcIPParam = virBufferContentAndReset(&buf);
+ }
+
+ virFormatMacAddr(macaddr, macaddr_str);
+
+ ebtablesUnlinkTmpRootChain(&buf, 1, ifname);
+ ebtablesUnlinkTmpRootChain(&buf, 0, ifname);
+ ebtablesRemoveTmpSubChains(&buf, ifname);
+ ebtablesRemoveTmpRootChain(&buf, 1, ifname);
+ ebtablesRemoveTmpRootChain(&buf, 0, ifname);
+ ebiptablesExecCLI(&buf, &cli_status);
+
+ ebtablesCreateTmpRootChain(&buf, 1, ifname, 1);
+ ebtablesCreateTmpRootChain(&buf, 0, ifname, 1);
+
+ PRINT_ROOT_CHAIN(chain_in , CHAINPREFIX_HOST_IN_TEMP , ifname);
+ PRINT_ROOT_CHAIN(chain_out, CHAINPREFIX_HOST_OUT_TEMP, ifname);
+
+ virBufferVSprintf(&buf,
+ CMD_DEF(EBTABLES_CMD
+ " -t %s -A %s"
+ " -s %s -d Broadcast "
+ " -p ipv4 --ip-protocol udp"
+ " --ip-src 0.0.0.0 --ip-dst 255.255.255.255"
+ " --ip-sport 68 --ip-dport 67"
+ " -j ACCEPT") CMD_SEPARATOR
+ CMD_EXEC
+ "%s",
+
+ EBTABLES_DEFAULT_TABLE,
+ chain_in,
+ macaddr_str,
+ CMD_STOPONERR(1));
+
+ virBufferVSprintf(&buf,
+ CMD_DEF(EBTABLES_CMD
+ " -t %s -A %s -j DROP") CMD_SEPARATOR
+ CMD_EXEC
+ "%s",
+
+ EBTABLES_DEFAULT_TABLE,
+ chain_in,
+ CMD_STOPONERR(1));
+
+ virBufferVSprintf(&buf,
+ CMD_DEF(EBTABLES_CMD
+ " -t %s -A %s"
+ " -d %s"
+ " -p ipv4 --ip-protocol udp"
+ " %s"
+ " --ip-sport 67 --ip-dport 68"
+ " -j ACCEPT") CMD_SEPARATOR
+ CMD_EXEC
+ "%s",
+
+ EBTABLES_DEFAULT_TABLE,
+ chain_out,
+ macaddr_str,
+ srcIPParam != NULL ? srcIPParam : "",
+ CMD_STOPONERR(1));
+
+ virBufferVSprintf(&buf,
+ CMD_DEF(EBTABLES_CMD
+ " -t %s -A %s -j DROP") CMD_SEPARATOR
+ CMD_EXEC
+ "%s",
+
+ EBTABLES_DEFAULT_TABLE,
+ chain_out,
+ CMD_STOPONERR(1));
+
+ ebtablesLinkTmpRootChain(&buf, 1, ifname, 1);
+ ebtablesLinkTmpRootChain(&buf, 0, ifname, 1);
+
+ if (ebiptablesExecCLI(&buf, &cli_status) || cli_status != 0)
+ goto tear_down_tmpebchains;
+
+ VIR_FREE(srcIPParam);
+
+ return 0;
+
+tear_down_tmpebchains:
+ ebtablesRemoveBasicRules(ifname);
+
+ virNWFilterReportError(VIR_ERR_BUILD_FIREWALL,
+ "%s",
+ _("Some rules could not be created."));
+
+ VIR_FREE(srcIPParam);
+
+ return 1;
+}
+
+
+int
+ebtablesRemoveBasicRules(const char *ifname)
+{
+ virBuffer buf = VIR_BUFFER_INITIALIZER;
+ int cli_status;
+
+ ebtablesUnlinkTmpRootChain(&buf, 1, ifname);
+ ebtablesUnlinkTmpRootChain(&buf, 0, ifname);
+ ebtablesRemoveTmpSubChains(&buf, ifname);
+ ebtablesRemoveTmpRootChain(&buf, 1, ifname);
+ ebtablesRemoveTmpRootChain(&buf, 0, ifname);
+
+ ebiptablesExecCLI(&buf, &cli_status);
+ return 0;
+}
+
+
static int
ebiptablesRuleOrderSort(const void *a, const void *b)
{
# define EBIPTABLES_DRIVER_ID "ebiptables"
+
+int ebtablesApplyBasicRules(const char *ifname,
+ const unsigned char *macaddr);
+int ebtablesApplyDHCPOnlyRules(const char *ifname,
+ const unsigned char *macaddr,
+ const char *dhcpServer);
+int ebtablesRemoveBasicRules(const char *ifname);
+
#endif
#include <config.h>
#include <stdint.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <linux/if.h>
#include "internal.h"
#include "virterror_internal.h"
#include "nwfilter_gentech_driver.h"
#include "nwfilter_ebiptables_driver.h"
+#include "nwfilter_learnipaddr.h"
#define VIR_FROM_THIS VIR_FROM_NWFILTER
#define NWFILTER_STD_VAR_MAC "MAC"
+#define NWFILTER_STD_VAR_IP "IP"
+
+static int _virNWFilterTeardownFilter(const char *ifname);
static virNWFilterTechDriverPtr filter_tech_drivers[] = {
* @tables: pointer to hash tabel to add values to
* @macaddr: The string of the MAC address to add to the hash table,
* may be NULL
+ * @ipaddr: The string of the IP address to add to the hash table;
+ * may be NULL
*
* Returns 0 in case of success, 1 in case an error happened with
* error having been reported.
*/
static int
virNWFilterVarHashmapAddStdValues(virNWFilterHashTablePtr table,
- char *macaddr)
+ char *macaddr,
+ char *ipaddr)
{
if (macaddr) {
if (virHashAddEntry(table->hashTable,
}
}
+ if (ipaddr) {
+ if (virHashAddEntry(table->hashTable,
+ NWFILTER_STD_VAR_IP,
+ ipaddr) < 0) {
+ virNWFilterReportError(VIR_ERR_INTERNAL_ERROR,
+ "%s", _("Could not add variable 'IP' to hashmap"));
+ return 1;
+ }
+ }
+
return 0;
}
/**
* virNWFilterCreateVarHashmap:
* @macaddr: pointer to string containing formatted MAC address of interface
+ * @ipaddr: pointer to string containing formatted IP address used by
+ * VM on this interface; may be NULL
*
* Create a hashmap used for evaluating the firewall rules. Initializes
- * it with the standard variable 'MAC'.
+ * it with the standard variable 'MAC' and 'IP' if provided.
*
* Returns pointer to hashmap, NULL if an error occcurred and error message
* is attached to the virConnect object.
*/
virNWFilterHashTablePtr
-virNWFilterCreateVarHashmap(char *macaddr) {
+virNWFilterCreateVarHashmap(char *macaddr, char *ipaddr) {
virNWFilterHashTablePtr table = virNWFilterHashTableCreate(0);
if (!table) {
virReportOOMError();
return NULL;
}
- if (virNWFilterVarHashmapAddStdValues(table, macaddr)) {
+ if (virNWFilterVarHashmapAddStdValues(table, macaddr, ipaddr)) {
virNWFilterHashTableFree(table);
return NULL;
}
virNWFilterHashTablePtr vars,
int *nEntries,
virNWFilterRuleInstPtr **insts,
- enum instCase useNewFilter, int *foundNewFilter)
+ enum instCase useNewFilter, int *foundNewFilter,
+ virNWFilterDriverStatePtr driver)
{
- virNWFilterDriverStatePtr driver = conn->nwfilterPrivateData;
virNWFilterPoolObjPtr obj;
int rc = 0;
int i;
tmpvars,
nEntries, insts,
useNewFilter,
- foundNewFilter);
+ foundNewFilter,
+ driver);
+
+ virNWFilterHashTableFree(tmpvars);
+
+ virNWFilterPoolObjUnlock(obj);
+ if (rc)
+ break;
+ } else {
+ virNWFilterReportError(VIR_ERR_INTERNAL_ERROR,
+ _("referenced filter '%s' is missing"),
+ inc->filterref);
+ rc = 1;
+ break;
+ }
+ }
+ }
+ return rc;
+}
+
+
+static int
+virNWFilterDetermineMissingVarsRec(virConnectPtr conn,
+ virNWFilterDefPtr filter,
+ virNWFilterHashTablePtr vars,
+ virNWFilterHashTablePtr missing_vars,
+ int useNewFilter,
+ virNWFilterDriverStatePtr driver)
+{
+ virNWFilterPoolObjPtr obj;
+ int rc = 0;
+ int i, j;
+ virNWFilterDefPtr next_filter;
+
+ for (i = 0; i < filter->nentries; i++) {
+ virNWFilterRuleDefPtr rule = filter->filterEntries[i]->rule;
+ virNWFilterIncludeDefPtr inc = filter->filterEntries[i]->include;
+ if (rule) {
+ // check all variables of this rule
+ for (j = 0; j < rule->nvars; j++) {
+ if (!virHashLookup(vars->hashTable, rule->vars[j])) {
+ virNWFilterHashTablePut(missing_vars, rule->vars[j],
+ strdup("1"), 1);
+ }
+ }
+ } else if (inc) {
+ VIR_DEBUG("Following filter %s\n", inc->filterref);
+ obj = virNWFilterPoolObjFindByName(&driver->pools,
+ inc->filterref);
+ if (obj) {
+
+ if (obj->wantRemoved) {
+ virNWFilterReportError(VIR_ERR_NO_NWFILTER,
+ _("Filter '%s' is in use."),
+ inc->filterref);
+ rc = 1;
+ virNWFilterPoolObjUnlock(obj);
+ break;
+ }
+
+ // create a temporary hashmap for depth-first tree traversal
+ virNWFilterHashTablePtr tmpvars =
+ virNWFilterCreateVarsFrom(inc->params,
+ vars);
+ if (!tmpvars) {
+ virReportOOMError();
+ rc = 1;
+ virNWFilterPoolObjUnlock(obj);
+ break;
+ }
+
+ next_filter = obj->def;
+
+ switch (useNewFilter) {
+ case INSTANTIATE_FOLLOW_NEWFILTER:
+ if (obj->newDef) {
+ next_filter = obj->newDef;
+ }
+ break;
+ case INSTANTIATE_ALWAYS:
+ break;
+ }
+
+ rc = virNWFilterDetermineMissingVarsRec(conn,
+ next_filter,
+ tmpvars,
+ missing_vars,
+ useNewFilter,
+ driver);
virNWFilterHashTableFree(tmpvars);
enum virDomainNetType nettype,
virNWFilterDefPtr filter,
const char *ifname,
+ const char *linkdev,
virNWFilterHashTablePtr vars,
enum instCase useNewFilter, int *foundNewFilter,
- bool teardownOld)
+ bool teardownOld,
+ const unsigned char *macaddr,
+ virNWFilterDriverStatePtr driver)
{
int rc;
int j, nptrs;
void **ptrs = NULL;
int instantiate = 1;
+ virNWFilterLockFilterUpdates();
+
+ virNWFilterHashTablePtr missing_vars = virNWFilterHashTableCreate(0);
+ if (!missing_vars) {
+ virReportOOMError();
+ rc = 1;
+ goto err_exit;
+ }
+
+ rc = virNWFilterDetermineMissingVarsRec(conn,
+ filter,
+ vars,
+ missing_vars,
+ useNewFilter,
+ driver);
+ if (rc)
+ goto err_exit;
+
+ if (virHashSize(missing_vars->hashTable) == 1) {
+ if (virHashLookup(missing_vars->hashTable,
+ NWFILTER_STD_VAR_IP) != NULL) {
+ if (virNWFilterLookupLearnReq(ifname) == NULL) {
+ rc = virNWFilterLearnIPAddress(ifname,
+ linkdev,
+ nettype, macaddr,
+ filter->name,
+ vars, driver,
+ DETECT_DHCP|DETECT_STATIC);
+ }
+ goto err_exit;
+ }
+ rc = 1;
+ goto err_exit;
+ } else if (virHashSize(missing_vars->hashTable) > 1) {
+ rc = 1;
+ goto err_exit;
+ }
+
rc = _virNWFilterInstantiateRec(conn,
techdriver,
nettype,
ifname,
vars,
&nEntries, &insts,
- useNewFilter, foundNewFilter);
+ useNewFilter, foundNewFilter,
+ driver);
if (rc)
goto err_exit;
err_exit:
+ virNWFilterUnlockFilterUpdates();
+
for (j = 0; j < nEntries; j++)
virNWFilterRuleInstFree(insts[j]);
VIR_FREE(insts);
+ virNWFilterHashTableFree(missing_vars);
+
return rc;
}
static int
-_virNWFilterInstantiateFilter(virConnectPtr conn,
- const virDomainNetDefPtr net,
- bool teardownOld,
- enum instCase useNewFilter)
+__virNWFilterInstantiateFilter(virConnectPtr conn,
+ bool teardownOld,
+ const char *ifname,
+ const char *linkdev,
+ enum virDomainNetType nettype,
+ const unsigned char *macaddr,
+ const char *filtername,
+ virNWFilterHashTablePtr filterparams,
+ enum instCase useNewFilter,
+ virNWFilterDriverStatePtr driver)
{
int rc;
const char *drvname = EBIPTABLES_DRIVER_ID;
- virNWFilterDriverStatePtr driver = conn->nwfilterPrivateData;
virNWFilterTechDriverPtr techdriver;
virNWFilterPoolObjPtr obj;
virNWFilterHashTablePtr vars, vars1;
char vmmacaddr[VIR_MAC_STRING_BUFLEN] = {0};
int foundNewFilter = 0;
char *str_macaddr = NULL;
+ const char *ipaddr;
+ char *str_ipaddr = NULL;
techdriver = virNWFilterTechDriverForName(drvname);
return 1;
}
- VIR_DEBUG("filter name: %s", net->filter);
+ VIR_DEBUG("filter name: %s", filtername);
- obj = virNWFilterPoolObjFindByName(&driver->pools, net->filter);
+ obj = virNWFilterPoolObjFindByName(&driver->pools, filtername);
if (!obj) {
virNWFilterReportError(VIR_ERR_NO_NWFILTER,
_("Could not find filter '%s'"),
- net->filter);
+ filtername);
return 1;
}
if (obj->wantRemoved) {
virNWFilterReportError(VIR_ERR_NO_NWFILTER,
_("Filter '%s' is in use."),
- net->filter);
+ filtername);
rc = 1;
goto err_exit;
}
- virFormatMacAddr(net->mac, vmmacaddr);
+ virFormatMacAddr(macaddr, vmmacaddr);
str_macaddr = strdup(vmmacaddr);
if (!str_macaddr) {
virReportOOMError();
goto err_exit;
}
- vars1 = virNWFilterCreateVarHashmap(str_macaddr);
+ ipaddr = virNWFilterGetIpAddrForIfname(ifname);
+ if (ipaddr) {
+ str_ipaddr = strdup(ipaddr);
+ if (!str_ipaddr) {
+ virReportOOMError();
+ rc = 1;
+ goto err_exit;
+ }
+ }
+
+ vars1 = virNWFilterCreateVarHashmap(str_macaddr, str_ipaddr);
if (!vars1) {
rc = 1;
goto err_exit;
}
str_macaddr = NULL;
+ str_ipaddr = NULL;
vars = virNWFilterCreateVarsFrom(vars1,
- net->filterparams);
+ filterparams);
if (!vars) {
rc = 1;
goto err_exit_vars1;
rc = virNWFilterInstantiate(conn,
techdriver,
- net->type,
+ nettype,
filter,
- net->ifname,
+ ifname,
+ linkdev,
vars,
useNewFilter, &foundNewFilter,
- teardownOld);
+ teardownOld,
+ macaddr,
+ driver);
virNWFilterHashTableFree(vars);
virNWFilterHashTableFree(vars1);
err_exit:
-
virNWFilterPoolObjUnlock(obj);
+ VIR_FREE(str_ipaddr);
VIR_FREE(str_macaddr);
return rc;
}
+static int
+_virNWFilterInstantiateFilter(virConnectPtr conn,
+ const virDomainNetDefPtr net,
+ bool teardownOld,
+ enum instCase useNewFilter)
+{
+ const char *linkdev = (net->type == VIR_DOMAIN_NET_TYPE_DIRECT)
+ ? net->data.direct.linkdev
+ : NULL;
+ return __virNWFilterInstantiateFilter(conn,
+ teardownOld,
+ net->ifname,
+ linkdev,
+ net->type,
+ net->mac,
+ net->filter,
+ net->filterparams,
+ useNewFilter,
+ conn->nwfilterPrivateData);
+}
+
+
+// FIXME: move chgIfFlags, ifUp, checkIf into common file & share w/ macvtap.c
+
+/*
+ * chgIfFlags: Change flags on an interface
+ * @ifname : name of the interface
+ * @flagclear : the flags to clear
+ * @flagset : the flags to set
+ *
+ * The new flags of the interface will be calculated as
+ * flagmask = (~0 ^ flagclear)
+ * newflags = (curflags & flagmask) | flagset;
+ *
+ * Returns 0 on success, errno on failure.
+ */
+static int chgIfFlags(const char *ifname, short flagclear, short flagset) {
+ struct ifreq ifr;
+ int rc = 0;
+ int flags;
+ short flagmask = (~0 ^ flagclear);
+ int fd = socket(PF_PACKET, SOCK_DGRAM, 0);
+
+ if (fd < 0)
+ return errno;
+
+ if (virStrncpy(ifr.ifr_name,
+ ifname, strlen(ifname), sizeof(ifr.ifr_name)) == NULL) {
+ rc = ENODEV;
+ goto err_exit;
+ }
+
+ if (ioctl(fd, SIOCGIFFLAGS, &ifr) < 0) {
+ rc = errno;
+ goto err_exit;
+ }
+
+ flags = (ifr.ifr_flags & flagmask) | flagset;
+
+ if (ifr.ifr_flags != flags) {
+ ifr.ifr_flags = flags;
+
+ if (ioctl(fd, SIOCSIFFLAGS, &ifr) < 0)
+ rc = errno;
+ }
+
+err_exit:
+ close(fd);
+ return rc;
+}
+
+/*
+ * ifUp
+ * @name: name of the interface
+ * @up: 1 for up, 0 for down
+ *
+ * Function to control if an interface is activated (up, 1) or not (down, 0)
+ *
+ * Returns 0 in case of success or an errno code in case of failure.
+ */
+static int
+ifUp(const char *name, int up)
+{
+ return chgIfFlags(name,
+ (up) ? 0 : IFF_UP,
+ (up) ? IFF_UP : 0);
+}
+
+
+/**
+ * checkIf
+ *
+ * @ifname: Name of the interface
+ * @macaddr: expected MAC address of the interface
+ *
+ * FIXME: the interface's index is another good parameter to check
+ *
+ * Determine whether a given interface is still available. If so,
+ * it must have the given MAC address.
+ *
+ * Returns an error code ENODEV in case the interface does not exist
+ * anymore or its MAC address is different, 0 otherwise.
+ */
+int
+checkIf(const char *ifname, const unsigned char *macaddr)
+{
+ struct ifreq ifr;
+ int fd = socket(PF_PACKET, SOCK_DGRAM, 0);
+ int rc = 0;
+
+ if (fd < 0)
+ return errno;
+
+ if (virStrncpy(ifr.ifr_name,
+ ifname, strlen(ifname), sizeof(ifr.ifr_name)) == NULL) {
+ rc = ENODEV;
+ goto err_exit;
+ }
+
+ if (ioctl(fd, SIOCGIFHWADDR, &ifr) < 0) {
+ rc = errno;
+ goto err_exit;
+ }
+
+ if (memcmp(&ifr.ifr_hwaddr.sa_data, macaddr, 6) != 0)
+ rc = ENODEV;
+
+ err_exit:
+ close(fd);
+ return rc;
+}
+
+
+int
+virNWFilterInstantiateFilterLate(virConnectPtr conn,
+ const char *ifname,
+ const char *linkdev,
+ enum virDomainNetType nettype,
+ const unsigned char *macaddr,
+ const char *filtername,
+ virNWFilterHashTablePtr filterparams,
+ virNWFilterDriverStatePtr driver)
+{
+ int rc;
+ rc = __virNWFilterInstantiateFilter(conn,
+ 1,
+ ifname,
+ linkdev,
+ nettype,
+ macaddr,
+ filtername,
+ filterparams,
+ INSTANTIATE_ALWAYS,
+ driver);
+ if (rc) {
+ //something went wrong... 'DOWN' the interface
+ if (ifUp(ifname ,0)) {
+ // assuming interface disappeared...
+ _virNWFilterTeardownFilter(ifname);
+ }
+ }
+ return rc;
+}
+
+
int
virNWFilterInstantiateFilter(virConnectPtr conn,
const virDomainNetDefPtr net)
}
-int
-virNWFilterTeardownFilter(const virDomainNetDefPtr net)
+static int
+_virNWFilterTeardownFilter(const char *ifname)
{
const char *drvname = EBIPTABLES_DRIVER_ID;
virNWFilterTechDriverPtr techdriver;
drvname);
return 1;
}
+ techdriver->allTeardown(ifname);
- techdriver->allTeardown(net->ifname);
+ virNWFilterDelIpAddrForIfname(ifname);
return 0;
}
+int
+virNWFilterTeardownFilter(const virDomainNetDefPtr net)
+{
+ return _virNWFilterTeardownFilter(net->ifname);
+}
+
+
void
virNWFilterDomainFWUpdateCB(void *payload,
const char *name ATTRIBUTE_UNUSED,
int virNWFilterTearOldFilter(virConnectPtr conn,
const virDomainNetDefPtr net);
+int virNWFilterInstantiateFilterLate(virConnectPtr conn,
+ const char *ifname,
+ const char *linkdev,
+ enum virDomainNetType nettype,
+ const unsigned char *macaddr,
+ const char *filtername,
+ virNWFilterHashTablePtr filterparams,
+ virNWFilterDriverStatePtr driver);
+
int virNWFilterTeardownFilter(const virDomainNetDefPtr net);
-virNWFilterHashTablePtr virNWFilterCreateVarHashmap(char *macaddr);
+virNWFilterHashTablePtr virNWFilterCreateVarHashmap(char *macaddr,
+ char *ipaddr);
void virNWFilterDomainFWUpdateCB(void *payload,
const char *name ATTRIBUTE_UNUSED,
void *data);
+int checkIf(const char *ifname, const unsigned char *macaddr);
+
#endif
--- /dev/null
+/*
+ * nwfilter_learnipaddr.c: support for learning IP address used by a VM
+ * on an interface
+ *
+ * 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
+ * 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: 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"
+
+#include "buf.h"
+#include "memory.h"
+#include "logging.h"
+#include "datatypes.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_learnipaddr.h"
+
+#define VIR_FROM_THIS VIR_FROM_NWFILTER
+
+
+/* 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;
+
+
+/* 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];
+ /* omitted */
+} ATTRIBUTE_PACKED;
+
+
+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;
+
+static virMutex ipAddressMapLock;
+static virNWFilterHashTablePtr ipAddressMap;
+
+
+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;
+ virMutexLock(&pendingLearnReqLock);
+
+ if (!virHashLookup(pendingLearnReq, req->ifname))
+ res = virHashAddEntry(pendingLearnReq, req->ifname, req);
+
+ virMutexUnlock(&pendingLearnReqLock);
+
+ return res;
+}
+
+#endif
+
+
+virNWFilterIPAddrLearnReqPtr
+virNWFilterLookupLearnReq(const char *ifname) {
+ void *res;
+
+ virMutexLock(&pendingLearnReqLock);
+
+ res = virHashLookup(pendingLearnReq, ifname);
+
+ virMutexUnlock(&pendingLearnReqLock);
+
+ return res;
+}
+
+
+static void
+freeLearnReqEntry(void *payload, const char *name ATTRIBUTE_UNUSED) {
+ virNWFilterIPAddrLearnReqFree(payload);
+}
+
+
+#ifdef HAVE_LIBPCAP
+
+static virNWFilterIPAddrLearnReqPtr
+virNWFilterDeregisterLearnReq(const char *ifname) {
+ virNWFilterIPAddrLearnReqPtr res;
+
+ virMutexLock(&pendingLearnReqLock);
+
+ res = virHashLookup(pendingLearnReq, ifname);
+
+ if (res)
+ virHashRemoveEntry(pendingLearnReq, ifname, NULL);
+
+ virMutexUnlock(&pendingLearnReqLock);
+
+ return res;
+}
+
+
+
+static int
+virNWFilterAddIpAddrForIfname(const char *ifname, char *addr) {
+ int ret;
+
+ virMutexLock(&ipAddressMapLock);
+
+ ret = virNWFilterHashTablePut(ipAddressMap, ifname, addr, 1);
+
+ virMutexUnlock(&ipAddressMapLock);
+
+ return ret;
+}
+#endif
+
+
+void
+virNWFilterDelIpAddrForIfname(const char *ifname) {
+
+ virMutexLock(&ipAddressMapLock);
+
+ if (virHashLookup(ipAddressMap->hashTable, ifname))
+ virNWFilterHashTableRemoveEntry(ipAddressMap, ifname);
+
+ virMutexUnlock(&ipAddressMapLock);
+}
+
+
+const char *
+virNWFilterGetIpAddrForIfname(const char *ifname) {
+ const char *res;
+
+ virMutexLock(&ipAddressMapLock);
+
+ res = virHashLookup(ipAddressMap->hashTable, ifname);
+
+ virMutexUnlock(&ipAddressMapLock);
+
+ return res;
+}
+
+
+#ifdef HAVE_LIBPCAP
+
+/**
+ * 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};
+ pcap_t *handle;
+ 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;
+ uint32_t vmaddr = 0;
+ unsigned int ethHdrSize;
+ char *listen_if = (strlen(req->linkdev) != 0) ? req->linkdev
+ : req->ifname;
+ int to_ms = (strlen(req->linkdev) != 0) ? 1000
+ : 0;
+ char macaddr[VIR_MAC_STRING_BUFLEN];
+ virBuffer buf = VIR_BUFFER_INITIALIZER;
+ char *filter= NULL;
+ uint16_t etherType;
+ enum howDetect howDetected = 0;
+
+ req->status = 0;
+
+ handle = pcap_open_live(listen_if, BUFSIZ, 0, to_ms, errbuf);
+
+ if (handle == NULL) {
+ VIR_DEBUG("Couldn't open device %s: %s\n", listen_if, errbuf);
+ req->status = ENODEV;
+ goto done;
+ }
+
+ virFormatMacAddr(req->macaddr, macaddr);
+
+ switch (req->howDetect) {
+ case DETECT_DHCP:
+ virBufferVSprintf(&buf, " ether dst %s"
+ " and src port 67 and dst port 68",
+ macaddr);
+ break;
+ default:
+ virBufferVSprintf(&buf, "ether host %s", macaddr);
+ }
+
+ if (virBufferError(&buf)) {
+ req->status = ENOMEM;
+ goto done;
+ }
+
+ filter = virBufferContentAndReset(&buf);
+
+ if (pcap_compile(handle, &fp, filter, 1, 0) != 0 ||
+ pcap_setfilter(handle, &fp) != 0) {
+ VIR_DEBUG("Couldn't compile or set filter '%s'.\n", filter);
+ req->status = EINVAL;
+ goto done;
+ }
+
+ while (req->status == 0 && vmaddr == 0) {
+ packet = pcap_next(handle, &header);
+
+ if (!packet) {
+ if (to_ms == 0) {
+ /* assuming IF disappeared */
+ req->status = ENODEV;
+ break;
+ }
+ /* listening on linkdev, check whether VM's dev is still there */
+ if (checkIf(req->ifname, req->macaddr)) {
+ req->status = ENODEV;
+ 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;
+ }
+
+ if (memcmp(ether_hdr->ether_shost,
+ req->macaddr,
+ VIR_MAC_BUFLEN) == 0) {
+ // packets from the VM
+
+ if (etherType == ETHERTYPE_IP &&
+ (header.len >= ethHdrSize +
+ sizeof(struct iphdr))) {
+ struct iphdr *iphdr = (struct iphdr*)(packet +
+ ethHdrSize);
+ vmaddr = iphdr->saddr;
+ // skip eth. bcast and mcast addresses,
+ // and zero address in DHCP Requests
+ if ((ntohl(vmaddr) & 0xc0000000) || vmaddr == 0) {
+ vmaddr = 0;
+ continue;
+ }
+
+ howDetected = DETECT_STATIC;
+ } else if (etherType == ETHERTYPE_ARP &&
+ (header.len >= ethHdrSize +
+ sizeof(struct f_arphdr))) {
+ struct f_arphdr *arphdr = (struct f_arphdr*)(packet +
+ ethHdrSize);
+ 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;
+ }
+ }
+ } else if (memcmp(ether_hdr->ether_dhost,
+ req->macaddr,
+ VIR_MAC_BUFLEN) == 0) {
+ // packets to the VM
+ if (etherType == ETHERTYPE_IP &&
+ (header.len >= ethHdrSize +
+ sizeof(struct iphdr))) {
+ struct iphdr *iphdr = (struct iphdr*)(packet +
+ ethHdrSize);
+ if ((iphdr->protocol == IPPROTO_UDP) &&
+ (header.len >= ethHdrSize +
+ iphdr->ihl * 4 +
+ sizeof(struct udphdr))) {
+ struct udphdr *udphdr= (struct udphdr *)
+ ((char *)iphdr + iphdr->ihl * 4);
+ 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));
+ if (dhcp->op == 2 /* DHCP OFFER */ &&
+ !memcmp(&dhcp->chaddr[0],
+ req->macaddr,
+ 6)) {
+ vmaddr = dhcp->yiaddr;
+ howDetected = DETECT_DHCP;
+ }
+ }
+ }
+ }
+ }
+ }
+ if (vmaddr && (req->howDetect & howDetected) == 0) {
+ vmaddr = 0;
+ howDetected = 0;
+ }
+ } /* while */
+
+ done:
+ VIR_FREE(filter);
+
+ if (handle)
+ pcap_close(handle);
+
+ ebtablesRemoveBasicRules(req->ifname);
+
+ if (req->status == 0) {
+ int ret;
+ char inetaddr[INET_ADDRSTRLEN];
+ inet_ntop(AF_INET, &vmaddr, inetaddr, sizeof(inetaddr));
+
+ virNWFilterAddIpAddrForIfname(req->ifname, strdup(inetaddr));
+
+ ret = virNWFilterInstantiateFilterLate(NULL,
+ req->ifname,
+ req->linkdev,
+ req->nettype,
+ req->macaddr,
+ 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);
+ }
+
+ memset(&req->thread, 0x0, sizeof(req->thread));
+
+ VIR_DEBUG("pcap thread terminating for interface %s\n",req->ifname);
+
+ virNWFilterDeregisterLearnReq(req->ifname);
+
+ virNWFilterIPAddrLearnReqFree(req);
+
+ return 0;
+}
+
+
+/**
+ * virNWFilterLearnIPAddress
+ * @conn: pointer to virConnect object
+ * @ifname: the name of the interface
+ * @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
+virNWFilterLearnIPAddress(const char *ifname,
+ const char *linkdev,
+ enum virDomainNetType nettype,
+ const unsigned char *macaddr,
+ const char *filtername,
+ virNWFilterHashTablePtr filterparams,
+ virNWFilterDriverStatePtr driver,
+ enum howDetect howDetect) {
+ int rc;
+ virNWFilterIPAddrLearnReqPtr req = NULL;
+ virNWFilterHashTablePtr ht = NULL;
+
+ if (howDetect == 0)
+ return 1;
+
+ if (VIR_ALLOC(req) < 0) {
+ virReportOOMError();
+ goto err_no_req;
+ }
+
+ ht = virNWFilterHashTableCreate(0);
+ if (ht == NULL) {
+ virReportOOMError();
+ goto err_no_ht;
+ }
+
+ if (virNWFilterHashTablePutAll(filterparams, ht))
+ goto err_free_ht;
+
+ req->filtername = strdup(filtername);
+ if (req->filtername == NULL) {
+ virReportOOMError();
+ goto err_free_ht;
+ }
+
+ if (virStrcpyStatic(req->ifname, ifname) == NULL) {
+ virNWFilterReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Destination buffer for ifname ('%s') "
+ "not large enough"), ifname);
+ goto err_free_ht;
+ }
+
+ if (linkdev) {
+ if (virStrcpyStatic(req->linkdev, linkdev) == NULL) {
+ virNWFilterReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Destination buffer for linkdev ('%s') "
+ "not large enough"), linkdev);
+ goto err_free_ht;
+ }
+ }
+ req->nettype = nettype;
+ memcpy(req->macaddr, macaddr, sizeof(req->macaddr));
+ req->driver = driver;
+ req->filterparams = ht;
+ ht = NULL;
+ req->howDetect = howDetect;
+
+ rc = virNWFilterRegisterLearnReq(req);
+
+ if (rc)
+ goto err_free_ht;
+
+ switch (howDetect) {
+ case DETECT_DHCP:
+ if (ebtablesApplyDHCPOnlyRules(ifname,
+ macaddr,
+ NULL))
+ goto err_free_ht;
+ break;
+ default:
+ if (ebtablesApplyBasicRules(ifname,
+ macaddr))
+ goto err_free_ht;
+ }
+
+
+ if (pthread_create(&req->thread,
+ NULL,
+ learnIPAddressThread,
+ req) != 0)
+ goto err_remove_rules;
+
+ return 0;
+
+err_remove_rules:
+ ebtablesRemoveBasicRules(ifname);
+err_free_ht:
+ virNWFilterHashTableFree(ht);
+err_no_ht:
+ virNWFilterIPAddrLearnReqFree(req);
+err_no_req:
+ return 1;
+}
+
+#else
+
+int
+virNWFilterLearnIPAddress(const char *ifname ATTRIBUTE_UNUSED,
+ const char *linkdev ATTRIBUTE_UNUSED,
+ enum virDomainNetType nettype ATTRIBUTE_UNUSED,
+ const unsigned char *macaddr ATTRIBUTE_UNUSED,
+ const char *filtername ATTRIBUTE_UNUSED,
+ virNWFilterHashTablePtr filterparams ATTRIBUTE_UNUSED,
+ virNWFilterDriverStatePtr driver ATTRIBUTE_UNUSED,
+ enum howDetect howDetect ATTRIBUTE_UNUSED) {
+ virNWFilterReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("IP parameter must be given since libvirt "
+ "was not compiled with IP address learning "
+ "support"));
+ return 1;
+}
+#endif /* HAVE_LIBPCAP */
+
+
+/**
+ * virNWFilterLearnInit
+ * Initialization of this layer
+ */
+int
+virNWFilterLearnInit(void) {
+ pendingLearnReq = virHashCreate(0);
+ if (!pendingLearnReq) {
+ virReportOOMError();
+ return 1;
+ }
+
+ if (virMutexInit(&pendingLearnReqLock)) {
+ virNWFilterLearnShutdown();
+ return 1;
+ }
+
+ ipAddressMap = virNWFilterHashTableCreate(0);
+ if (!ipAddressMap) {
+ virReportOOMError();
+ virNWFilterLearnShutdown();
+ return 1;
+ }
+
+ if (virMutexInit(&ipAddressMapLock)) {
+ virNWFilterLearnShutdown();
+ return 1;
+ }
+
+ return 0;
+}
+
+
+/**
+ * virNWFilterLearnShutdown
+ * Shutdown of this layer
+ */
+void
+virNWFilterLearnShutdown(void) {
+ virHashFree(pendingLearnReq, freeLearnReqEntry);
+ pendingLearnReq = NULL;
+
+ virNWFilterHashTableFree(ipAddressMap);
+ ipAddressMap = NULL;
+}
--- /dev/null
+/*
+ * nwfilter_learnipaddr.h: support for learning IP address used by a VM
+ * on an interface
+ *
+ * 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
+ * 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: Stefan Berger <stefanb@us.ibm.com>
+ */
+
+#ifndef __NWFILTER_LEARNIPADDR_H
+# define __NWFILTER_LEARNIPADDR_H
+
+enum howDetect {
+ DETECT_DHCP = 1,
+ DETECT_STATIC = 2,
+};
+
+typedef struct _virNWFilterIPAddrLearnReq virNWFilterIPAddrLearnReq;
+typedef virNWFilterIPAddrLearnReq *virNWFilterIPAddrLearnReqPtr;
+struct _virNWFilterIPAddrLearnReq {
+ char ifname[IF_NAMESIZE];
+ char linkdev[IF_NAMESIZE];
+ enum virDomainNetType nettype;
+ unsigned char macaddr[VIR_MAC_BUFLEN];
+ char *filtername;
+ virNWFilterHashTablePtr filterparams;
+ virNWFilterDriverStatePtr driver;
+ enum howDetect howDetect;
+
+ int status;
+ pthread_t thread;
+};
+
+int virNWFilterLearnIPAddress(const char *ifname,
+ const char *linkdev,
+ enum virDomainNetType nettype,
+ const unsigned char *macaddr,
+ const char *filtername,
+ virNWFilterHashTablePtr filterparams,
+ virNWFilterDriverStatePtr driver,
+ enum howDetect howDetect);
+
+virNWFilterIPAddrLearnReqPtr virNWFilterLookupLearnReq(const char *ifname);
+
+
+void virNWFilterDelIpAddrForIfname(const char *ifname);
+const char *virNWFilterGetIpAddrForIfname(const char *ifname);
+
+int virNWFilterLearnInit(void);
+void virNWFilterLearnShutdown(void);
+
+#endif /* __NWFILTER_LEARNIPADDR_H */