[Libvirt-cim] [PATCH] (#2) Add DCN ACL classes & providers

Chip Vincent cvincent at linux.vnet.ibm.com
Fri May 27 05:08:43 UTC 2011


# HG changeset patch
# User Chip Vincent <cvincent at us.ibm.com>
# Date 1305221061 14400
# Node ID 68e37b55a0687ca55554ca84c79c3358c05aee90
# Parent  8e7c9a8ad1f9e5bf6765c89a36ccca43f0564934
(#2) Add DCN ACL classes & providers

Changes in this version:
#2 - Scoped code to primary use case: link network interfaces to
existing fitlers.
- Updated copyright year in a few places.
- Fixed octets_to_cmpi() to work when called from an associattion provider

This patch adds several new classes and providers to add support for filter
rules and filter lists.

The patch is rather large since all aspects are intertwined.
Here's an overview of the implementation:

libxkutil:
The instrumentation is implemented in acl_parsing.c/.h since this function
is essentially independent of the device and/or pool parsing. ACLs are
associated to devices (via filterref) so there are some minor changes in
device_parsing.c & xmlgen.c to support this.

+libxkutil/acl_parsing.h & .c
Adds several new structures to represent filters and rules. Since rules can
only exist within the context of a filter, the main structure used in the APIs
is acl_fitler.

Also adds several new APIs to libxkutil to allow providers (see below) to
convert acl_filter and acl_rule structures to CIM objects, and to support
associations between themselves and existing resource (host system & network
port).

schema & providers:
The ACL function does not map to an exiting profile so a subset of the
classes defined in the CIM Network schema are used.

+schema/FilterEntry.mof &.registration
+src/FilterEntry.h & .c
Adds two new classes to represent IP-level rules (IPHeadersFilter) and
MAC-level (Hdr8021Filter). These classes include two new properties to
surface libvirt features not defined by the schema (Direction & Priority).

+schema/FilterList.mof & .registration
+src/FilterList.h & .c
Adds a class to represent ACL filters (FilterList)

NOTE: Read-write. Requires Name property only.

+schema/EntriesInFilterList.mof & .registration
+src/EntriesInFilterList.c
Adds a class to represent the relationship (association) between a filter
(FilterList) and the rules it contains (IPHeadersFilter or Hdr8021Filter).

+schema/NestedFilterList.mof & .registration
+src/NestedFilterList.c
Adds a class to represent filters that have references (filterref) to
other pre-defined filters. This is not covered by the current schema so
a new class was introduced to a) allow filters to reference each other, and
b) do it in a way that allows the client to determine if a filter
reference_s_ a filter or is reference_d_ by a filter.

+schema/HostedFilterList.mof & .registration
+src/HostedFilterList.c
Adds a class to represent the relationship (association) between a filter
and the computer system that hosts them. In the case of ACL filters, which
are essentially defined once but available to all VMs, they are always linked
to the host system (hypervisor) rather than the individual computer
systems (VMs).

+schema/AppliedFilterList.mof & .registration
+src/AppliedFilterList.c
Adds a class to represent the relationship (association) between a filter
and a network port referencing it. This is not covered by the current schema
so a new class was introduced.

NOTE: New associations are created via CIM intrinsic methods (create/modify/
delete). It requires the client to provide fully qualified object paths to
the filter and network port so the providers can a) validate their existance,
and b) apply the necessary links in the XML so new associations appear when
queried.

Signed-off-by: Chip Vincent <cvincent at us.ibm.com>

diff --git a/Makefile.am b/Makefile.am
--- a/Makefile.am
+++ b/Makefile.am
@@ -57,7 +57,13 @@
 	schema/InputPool.mof \
 	schema/HostedAccessPoint.mof \
 	schema/ServiceAccessBySAP.mof \
-	schema/SAPAvailableForElement.mof
+	schema/SAPAvailableForElement.mof \
+	schema/FilterEntry.mof \
+	schema/FilterList.mof \
+	schema/EntriesInFilterList.mof \
+	schema/NestedFilterList.mof \
+	schema/AppliedFilterList.mof \
+	schema/HostedFilterList.mof
 
 INTEROP_MOFS = \
 	schema/ComputerSystem.mof \
@@ -138,7 +144,13 @@
 	schema/InputPool.registration \
 	schema/HostedAccessPoint.registration \
 	schema/ServiceAccessBySAP.registration \
-	schema/SAPAvailableForElement.registration
+	schema/SAPAvailableForElement.registration \
+	schema/FilterEntry.registration \
+	schema/FilterList.registration \
+	schema/EntriesInFilterList.registration \
+	schema/NestedFilterList.registration \
+	schema/AppliedFilterList.registration \
+	schema/HostedFilterList.registration
 
 INTEROP_REGS = \
 	schema/RegisteredProfile.registration \
diff --git a/libxkutil/Makefile.am b/libxkutil/Makefile.am
--- a/libxkutil/Makefile.am
+++ b/libxkutil/Makefile.am
@@ -4,12 +4,12 @@
 CFLAGS += $(CFLAGS_STRICT)
 
 noinst_HEADERS = cs_util.h misc_util.h device_parsing.h xmlgen.h infostore.h \
-                 pool_parsing.h
+                 pool_parsing.h acl_parsing.h
 
 lib_LTLIBRARIES = libxkutil.la
 
 libxkutil_la_SOURCES = cs_util_instance.c misc_util.c device_parsing.c \
-                       xmlgen.c infostore.c pool_parsing.c
+                       xmlgen.c infostore.c pool_parsing.c acl_parsing.c
 
 libxkutil_la_LIBADD = @LIBVIRT_LIBS@ \
 		      @LIBUUID_LIBS@
diff --git a/libxkutil/acl_parsing.c b/libxkutil/acl_parsing.c
new file mode 100644
--- /dev/null
+++ b/libxkutil/acl_parsing.c
@@ -0,0 +1,639 @@
+/*
+ * Copyright IBM Corp. 2011
+ *
+ * Authors:
+ *  Chip Vincent <cvincent at us.ibm.com>
+ *
+ * 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
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <inttypes.h>
+#include <sys/stat.h>
+
+#include <libcmpiutil/libcmpiutil.h>
+
+#include "acl_parsing.h"
+#include "device_parsing.h"
+#include "xmlgen.h"
+#include "../src/svpc_types.h"
+
+void cleanup_rule(struct acl_rule *rule)
+{
+        if(rule == NULL)
+                return;
+
+        free(rule->name);
+        free(rule->protocol_id);
+        free(rule->action);
+        free(rule->direction);
+        free(rule->priority);
+
+        switch(rule->type) {
+        case MAC_RULE:
+                free(rule->var.mac.srcmacaddr);
+                free(rule->var.mac.srcmacmask);
+                free(rule->var.mac.dstmacaddr);
+                free(rule->var.mac.dstmacmask);
+                free(rule->var.mac.protocol_id);
+                free(rule->var.mac.comment);
+                break;
+        case ARP_RULE:
+                free(rule->var.arp.srcmacaddr);
+                free(rule->var.arp.srcmacmask);
+                free(rule->var.arp.dstmacaddr);
+                free(rule->var.arp.dstmacmask);
+                free(rule->var.arp.hw_type);
+                free(rule->var.arp.protocol_type);
+                free(rule->var.arp.opcode);
+                free(rule->var.arp.arpsrcmacaddr);
+                free(rule->var.arp.arpdstmacaddr);
+                free(rule->var.arp.arpsrcipaddr);
+                free(rule->var.arp.arpdstipaddr);
+                free(rule->var.arp.comment);
+                break;
+        case IP_RULE:
+                free(rule->var.ip.srcmacaddr);
+                free(rule->var.ip.srcmacmask);
+                free(rule->var.ip.dstmacaddr);
+                free(rule->var.ip.dstmacmask);
+                free(rule->var.ip.srcipaddr);
+                free(rule->var.ip.srcipmask);
+                free(rule->var.ip.dstipaddr);
+                free(rule->var.ip.dstipmask);
+                free(rule->var.ip.srcportstart);
+                free(rule->var.ip.srcportend);
+                free(rule->var.ip.dstportstart);
+                free(rule->var.ip.dstportend);
+                free(rule->var.ip.comment);
+                break;
+        case TCP_RULE:
+                free(rule->var.tcp.srcmacaddr);
+                free(rule->var.tcp.srcipaddr);
+                free(rule->var.tcp.srcipmask);
+                free(rule->var.tcp.dstipaddr);
+                free(rule->var.tcp.srcipfrom);
+                free(rule->var.tcp.srcipto);
+                free(rule->var.tcp.dstipfrom);
+                free(rule->var.tcp.dstipto);
+                free(rule->var.tcp.srcportstart);
+                free(rule->var.tcp.srcportend);
+                free(rule->var.tcp.dstportstart);
+                free(rule->var.tcp.dstportend);
+                free(rule->var.tcp.comment);
+                free(rule->var.tcp.state);
+                break;
+        case IGMP_RULE:
+                free(rule->var.igmp.srcmacaddr);
+                free(rule->var.igmp.srcmacmask);
+                free(rule->var.igmp.dstmacaddr);
+                free(rule->var.igmp.dstmacmask);
+                free(rule->var.igmp.srcipaddr);
+                free(rule->var.igmp.srcipmask);
+                free(rule->var.igmp.dstipaddr);
+                free(rule->var.igmp.dstipmask);
+                free(rule->var.igmp.srcipfrom);
+                free(rule->var.igmp.srcipto);
+                free(rule->var.igmp.dstipfrom);
+                free(rule->var.igmp.dstipto);
+                free(rule->var.igmp.type);
+                free(rule->var.igmp.code);
+                free(rule->var.igmp.comment);
+                free(rule->var.igmp.state);
+                break;
+        case ICMP_RULE:
+                free(rule->var.icmp.srcmacaddr);
+                free(rule->var.icmp.srcmacmask);
+                free(rule->var.icmp.dstmacaddr);
+                free(rule->var.icmp.dstmacmask);
+                free(rule->var.icmp.srcipaddr);
+                free(rule->var.icmp.srcipmask);
+                free(rule->var.icmp.dstipaddr);
+                free(rule->var.icmp.dstipmask);
+                free(rule->var.icmp.srcipfrom);
+                free(rule->var.icmp.srcipto);
+                free(rule->var.icmp.dstipfrom);
+                free(rule->var.icmp.dstipto);
+                free(rule->var.icmp.type);
+                free(rule->var.icmp.code);
+                free(rule->var.icmp.comment);
+                free(rule->var.icmp.state);
+                break;
+        case UNKNOWN_RULE:
+        default:
+                break;
+        };
+
+        rule->type = UNKNOWN_RULE;
+}
+
+void cleanup_filter(struct acl_filter *filter)
+{
+        int i;
+
+        if(filter == NULL)
+                return;
+
+        free(filter->uuid);
+        free(filter->name);
+        free(filter->chain);
+
+        for (i = 0; i < filter->rule_ct; i++) {
+                cleanup_rule(filter->rules[i]);
+                free(filter->rules[i]);
+        }
+
+        free(filter->rules);
+        filter->rule_ct = 0;
+
+        for (i = 0; i < filter->ref_ct; i++)
+                free(filter->refs[i]);
+
+        free(filter->refs);
+        filter->ref_ct = 0;
+}
+
+void cleanup_filters(struct acl_filter **filters, int count)
+{
+        int i;
+        struct acl_filter *_filters = *filters;
+
+        if((filters == NULL) || (*filters == NULL) || (count == 0))
+                return;
+
+        for (i = 0; i < count; i++)
+                cleanup_filter(&_filters[i]);
+
+        free(_filters);
+        *filters = NULL;
+}
+
+
+static int parse_acl_mac_rule(xmlNode *rnode, struct acl_rule *rule)
+{
+        CU_DEBUG("ACL mac rule %s", rnode->name);
+
+        rule->type = MAC_RULE;
+        rule->var.mac.protocol_id = get_attr_value(rnode, "protocolid");
+        rule->var.mac.srcmacaddr = get_attr_value(rnode, "srcmacaddr");
+        rule->var.mac.srcmacmask = get_attr_value(rnode, "srcmacmask");
+        rule->var.mac.dstmacaddr = get_attr_value(rnode, "dstmacaddr");
+        rule->var.mac.dstmacmask = get_attr_value(rnode, "dstmacmask");
+        rule->var.mac.comment = get_attr_value(rnode, "comment");
+
+        return 1;
+}
+
+static int parse_acl_arp_rule(xmlNode *rnode, struct acl_rule *rule)
+{
+        CU_DEBUG("ACL arp rule %s", rnode->name);
+
+        rule->type = ARP_RULE;
+        rule->var.arp.srcmacaddr = get_attr_value(rnode, "srcmacaddr");
+        rule->var.arp.srcmacmask = get_attr_value(rnode, "srcmacmask");
+        rule->var.arp.dstmacaddr = get_attr_value(rnode, "dstmacaddr");
+        rule->var.arp.dstmacmask = get_attr_value(rnode, "dstmacmask");
+        rule->var.arp.hw_type = get_attr_value(rnode, "hwtype");
+        rule->var.arp.protocol_type = get_attr_value(rnode, "protocoltype");
+        rule->var.arp.opcode = get_attr_value(rnode, "opcode");
+        rule->var.arp.arpsrcmacaddr = get_attr_value(rnode, "arpsrcmacaddr");
+        rule->var.arp.arpdstmacaddr = get_attr_value(rnode, "arpdstmacaddr");
+        rule->var.arp.arpsrcipaddr = get_attr_value(rnode, "arpsrcipaddr");
+        rule->var.arp.arpdstipaddr = get_attr_value(rnode, "arpdstipaddr");
+        rule->var.arp.comment = get_attr_value(rnode, "comment");
+
+        return 1;
+}
+
+static int parse_acl_ip_rule(xmlNode *rnode, struct acl_rule *rule)
+{
+        CU_DEBUG("ACP ip rule %s", rnode->name);
+
+        rule->type = IP_RULE;
+        rule->var.ip.srcmacaddr = get_attr_value(rnode, "srcmacaddr");
+        rule->var.ip.srcmacmask = get_attr_value(rnode, "srcmacmask");
+        rule->var.ip.dstmacaddr = get_attr_value(rnode, "dstmacaddr");
+        rule->var.ip.dstmacmask = get_attr_value(rnode, "dstmacmaks");
+        rule->var.ip.srcipaddr = get_attr_value(rnode, "srcipaddr");
+        rule->var.ip.srcipmask = get_attr_value(rnode, "srcipmask");
+        rule->var.ip.dstipaddr = get_attr_value(rnode, "dstipaddr");
+        rule->var.ip.dstipmask = get_attr_value(rnode, "dstipmask");
+        rule->var.ip.protocol = get_attr_value(rnode, "protocol");
+        rule->var.ip.srcportstart = get_attr_value(rnode, "srcportstart");
+        rule->var.ip.srcportend = get_attr_value(rnode, "srcportend");
+        rule->var.ip.dstportstart = get_attr_value(rnode, "dstportstart");
+        rule->var.ip.dstportend = get_attr_value(rnode, "dstportend");
+        rule->var.ip.comment = get_attr_value(rnode, "comment");
+
+        return 1;
+}
+
+static int parse_acl_tcp_rule(xmlNode *rnode, struct acl_rule *rule)
+{
+        CU_DEBUG("ACL tcp rule %s", rnode->name);
+
+        rule->type = TCP_RULE;
+        rule->var.tcp.srcmacaddr = get_attr_value(rnode, "srcmacaddr");
+        rule->var.tcp.srcipaddr = get_attr_value(rnode, "srcipaddr");
+        rule->var.tcp.srcipmask = get_attr_value(rnode, "srcipmask");
+        rule->var.tcp.dstipaddr = get_attr_value(rnode, "dstipaddr");
+        rule->var.tcp.dstipmask = get_attr_value(rnode, "dstipmask");
+        rule->var.tcp.srcipfrom = get_attr_value(rnode, "srcipfrom");
+        rule->var.tcp.srcipto = get_attr_value(rnode, "srcipto");
+        rule->var.tcp.dstipfrom = get_attr_value(rnode, "dstipfrom");
+        rule->var.tcp.dstipto = get_attr_value(rnode, "dstipto");
+        rule->var.tcp.srcportstart = get_attr_value(rnode, "srcportstart");
+        rule->var.tcp.srcportend = get_attr_value(rnode, "srcportend");
+        rule->var.tcp.dstportstart = get_attr_value(rnode, "dstportstart");
+        rule->var.tcp.dstportend = get_attr_value(rnode, "dstportend");
+        rule->var.tcp.comment = get_attr_value(rnode, "comment");
+        rule->var.tcp.state = get_attr_value(rnode, "state");
+
+        return 1;
+}
+
+static int parse_acl_icmp_rule(xmlNode *rnode, struct acl_rule *rule)
+{
+        CU_DEBUG("ACL icmp rule %s", rnode->name);
+
+        rule->type = ICMP_RULE;
+        rule->var.icmp.srcmacaddr = get_attr_value(rnode, "srcmacaddr");
+        rule->var.icmp.srcmacmask = get_attr_value(rnode, "srcmacmask");
+        rule->var.icmp.dstmacaddr = get_attr_value(rnode, "dstmacaddr");
+        rule->var.icmp.dstmacmask = get_attr_value(rnode, "dstmacmask");
+        rule->var.icmp.srcipaddr = get_attr_value(rnode, "srcipaddr");
+        rule->var.icmp.srcipmask = get_attr_value(rnode, "srcipmask");
+        rule->var.icmp.dstipaddr = get_attr_value(rnode, "dstipaddr");
+        rule->var.icmp.dstipmask = get_attr_value(rnode, "dstipmask");
+        rule->var.icmp.srcipfrom = get_attr_value(rnode, "srcipfrom");
+        rule->var.icmp.srcipto = get_attr_value(rnode, "srcipto");
+        rule->var.icmp.dstipfrom = get_attr_value(rnode, "dstipfrom");
+        rule->var.icmp.dstipto = get_attr_value(rnode, "dstipto");
+        rule->var.icmp.comment = get_attr_value(rnode, "comment");
+        rule->var.icmp.state = get_attr_value(rnode, "state");
+
+        return 1;
+}
+
+static int parse_acl_igmp_rule(xmlNode *rnode, struct acl_rule *rule)
+{
+        CU_DEBUG("ACL igmp rule %s", rnode->name);
+
+        rule->type = IGMP_RULE;
+        rule->var.igmp.srcmacaddr = get_attr_value(rnode, "srcmacaddr");
+        rule->var.igmp.srcmacmask = get_attr_value(rnode, "srcmacmask");
+        rule->var.igmp.dstmacaddr = get_attr_value(rnode, "dstmacaddr");
+        rule->var.igmp.dstmacmask = get_attr_value(rnode, "dstmacmask");
+        rule->var.igmp.srcipaddr = get_attr_value(rnode, "srcipaddr");
+        rule->var.igmp.srcipmask = get_attr_value(rnode, "srcipmask");
+        rule->var.igmp.dstipaddr = get_attr_value(rnode, "dstipaddr");
+        rule->var.igmp.dstipmask = get_attr_value(rnode, "dstipmask");
+        rule->var.igmp.srcipfrom = get_attr_value(rnode, "srcipfrom");
+        rule->var.igmp.srcipto = get_attr_value(rnode, "srcipto");
+        rule->var.igmp.dstipfrom = get_attr_value(rnode, "dstipfrom");
+        rule->var.igmp.dstipto = get_attr_value(rnode, "dstipto");
+        rule->var.igmp.type = get_attr_value(rnode, "type");
+        rule->var.igmp.code = get_attr_value(rnode, "code");
+        rule->var.igmp.comment = get_attr_value(rnode, "comment");
+        rule->var.igmp.state = get_attr_value(rnode, "state");
+
+        return 1;
+}
+
+static int parse_acl_rule(xmlNode *rnode, struct acl_rule *rule)
+{
+        xmlNode *child = NULL;
+
+        memset(rule, 0, sizeof(*rule));
+
+        rule->action = get_attr_value(rnode, "action");
+        if (rule->action == NULL)
+                goto err;
+
+        rule->direction = get_attr_value(rnode, "direction");
+        if (rule->direction == NULL)
+                goto err;
+
+        rule->priority = get_attr_value(rnode, "priority");
+        rule->statematch = get_attr_value(rnode, "statematch");
+
+        for (child = rnode->children; child != NULL; child = child->next) {
+                if (XSTREQ(child->name, "mac")) {
+                        rule->protocol_id = strdup((char *)child->name);
+                        parse_acl_mac_rule(child, rule);
+                } else if (XSTREQ(child->name, "arp") ||
+                        XSTREQ(child->name, "rarp")) {
+                        rule->protocol_id = strdup((char *)child->name);
+                        parse_acl_arp_rule(child, rule);
+                } else if (XSTREQ(child->name, "ip") ||
+                        XSTREQ(child->name, "ipv6")) {
+                        rule->protocol_id = strdup((char *)child->name);
+                        parse_acl_ip_rule(child, rule);
+                } else if (XSTREQ(child->name, "tcp") ||
+                        XSTREQ(child->name, "tcp-ipv6") ||
+                        XSTREQ(child->name, "udp") ||
+                        XSTREQ(child->name, "udp-ipv6") ||
+                        XSTREQ(child->name, "sctp") ||
+                        XSTREQ(child->name, "sctp-ipv6")) {
+                        rule->protocol_id = strdup((char *)child->name);
+                        parse_acl_tcp_rule(child, rule);
+                } else if (XSTREQ(child->name, "icmp") ||
+                        XSTREQ(child->name, "icmpv6")) {
+                        rule->protocol_id = strdup((char *)child->name);
+                        parse_acl_icmp_rule(child, rule);
+                } else if (XSTREQ(child->name, "igmp") ||
+                        XSTREQ(child->name, "igmp-ipv6") ||
+                        XSTREQ(child->name, "esp") ||
+                        XSTREQ(child->name, "esp-ipv6") ||
+                        XSTREQ(child->name, "ah") ||
+                        XSTREQ(child->name, "ah-ipv6") ||
+                        XSTREQ(child->name, "udplite") ||
+                        XSTREQ(child->name, "udplite-ipv6") ||
+                        XSTREQ(child->name, "all") ||
+                        XSTREQ(child->name, "all-ipv6")) {
+                        rule->protocol_id = strdup((char *)child->name);
+                        parse_acl_igmp_rule(child, rule);
+                }
+        }
+
+        return 1;
+
+ err:
+        cleanup_rule(rule);
+
+        return 0;
+}
+
+static int parse_acl_filter(xmlNode *fnode, struct acl_filter *filter)
+{
+        struct acl_rule *rule = NULL;
+        char *filter_ref = NULL;
+        xmlNode *child = NULL;
+
+        filter->name = get_attr_value(fnode, "name");
+        if (filter->name == NULL)
+                goto err;
+
+        filter->chain = get_attr_value(fnode, "chain");
+
+        for (child = fnode->children; child != NULL; child = child->next) {
+                if (XSTREQ(child->name, "uuid")) {
+                        STRPROP(filter, uuid, child);
+                } else if (XSTREQ(child->name, "rule")) {
+                        rule = malloc(sizeof(*rule));
+                        if (rule == NULL)
+                                goto err;
+
+                        if (parse_acl_rule(child, rule) == 0)
+                                goto err;
+
+                        append_filter_rule(filter, rule);
+                }
+                else if (XSTREQ(child->name, "filterref")) {
+                        filter_ref = get_attr_value(child, "filter");
+                        if (filter_ref == NULL)
+                                goto err;
+
+                        append_filter_ref(filter, filter_ref);
+                }
+        }
+
+        return 1;
+
+ err:
+        cleanup_filter(filter);
+
+        return 0;
+}
+
+/* Dummy function to suppress error message from libxml2 */
+static void swallow_err_msg(void *ctx, const char *msg, ...)
+{
+        /* do nothing, just swallow the message. */
+}
+
+int get_filter_from_xml(const char *xml, struct acl_filter **filter)
+{
+        xmlDoc *xmldoc = NULL;
+
+        if (xml == NULL || filter == NULL)
+                return 0;
+
+        xmlSetGenericErrorFunc(NULL, swallow_err_msg);
+
+        xmldoc = xmlParseMemory(xml, strlen(xml) + 1);
+        if (xmldoc == NULL)
+                goto err;
+
+        *filter = malloc(sizeof(**filter));
+
+        memset(*filter, 0, sizeof(**filter));
+        parse_acl_filter(xmldoc->children, *filter);
+
+ err:
+        xmlSetGenericErrorFunc(NULL, NULL);
+        xmlFreeDoc(xmldoc);
+
+        return 1;
+}
+
+int get_filter_by_name(
+        virConnectPtr conn,
+        const char *name,
+        struct acl_filter **filter)
+{
+        virNWFilterPtr vfilter = NULL;
+        char *xml = NULL;
+
+        if (name == NULL || filter == NULL)
+                return 0;
+
+        vfilter = virNWFilterLookupByName(conn, name);
+
+        xml = virNWFilterGetXMLDesc(vfilter, 0);
+
+        virNWFilterFree(vfilter);
+
+        if (xml == NULL)
+                return 0;
+
+        get_filter_from_xml(xml, filter);
+
+        return 1;
+}
+
+int get_filter_by_uuid(
+        virConnectPtr conn,
+        const char *uuid,
+        struct acl_filter **filter)
+{
+        virNWFilterPtr vfilter = NULL;
+        char *xml = NULL;
+
+        if (uuid == NULL || filter == NULL)
+                return 0;
+
+        vfilter = virNWFilterLookupByUUIDString(conn, uuid);
+
+        xml = virNWFilterGetXMLDesc(vfilter, 0);
+
+        virNWFilterFree(vfilter);
+
+        if (xml == NULL)
+                return 0;
+
+        get_filter_from_xml(xml, filter);
+
+        return 1;
+}
+
+int get_filters(
+        virConnectPtr conn,
+        struct acl_filter **list)
+{
+        int count = 0;
+        char **names = NULL;
+        struct acl_filter *filters = NULL;
+        int i = 0;
+
+        count = virConnectNumOfNWFilters(conn);
+
+        names = calloc(count, sizeof(char *));
+        if (names == NULL)
+                goto err;
+
+        virConnectListNWFilters(conn, names, count);
+
+        filters = malloc(count * sizeof(struct acl_filter));
+        if (filters == NULL)
+                goto err;
+
+        for(i = 0; i < count; i++)
+        {
+                struct acl_filter *filter = NULL;
+
+                get_filter_by_name(conn, names[i], &filter);
+
+                memcpy(&filters[i], filter, sizeof(*filter));
+        }
+
+        *list = filters;
+
+ err:
+        free(names);
+
+        return i;
+}
+
+
+int append_filter_rule(struct acl_filter *filter, struct acl_rule *rule)
+{
+        struct acl_rule **old_rules = NULL;
+
+        if ((filter == NULL) || (rule == NULL))
+                return 0;
+
+        rule->name = make_rule_id(filter->name, filter->rule_ct);
+        if (rule->name == NULL)
+                return 0;
+
+        old_rules = filter->rules;
+
+        filter->rules =
+                malloc((filter->rule_ct + 1) * sizeof(struct acl_rule *));
+
+        memcpy(filter->rules,
+                old_rules,
+                filter->rule_ct * sizeof(struct acl_rule *));
+
+        filter->rules[filter->rule_ct] = rule;
+        filter->rule_ct++;
+
+        free(old_rules);
+
+        return 1;
+}
+
+int append_filter_ref(struct acl_filter *filter, char *name)
+{
+        int i;
+        char **old_refs = NULL;
+
+        if ((filter == NULL) || (name == NULL))
+                return 0;
+
+        for (i = 0; i < filter->ref_ct; i++)
+                if (STREQC(filter->refs[i], name))
+                        return 0; /* already exists */
+
+        old_refs = filter->refs;
+
+        filter->refs = malloc((filter->ref_ct + 1) * sizeof(char *));
+        memcpy(filter->refs, old_refs, filter->ref_ct * sizeof(char *));
+
+        filter->refs[filter->ref_ct] = name;
+        filter->ref_ct++;
+
+        free(old_refs);
+
+        return 1;
+}
+
+char *make_rule_id(const char *filter, int index)
+{
+        int ret;
+        char *rule_id = NULL;
+
+        if (filter == NULL)
+                return NULL;
+
+        ret = asprintf(&rule_id, "%s:%u", filter, index);
+        if (ret == -1) {
+                free(rule_id);
+                rule_id = NULL;
+        }
+
+
+        return rule_id;
+}
+
+int parse_rule_id(const char *rule_id, char **filter, int *index)
+{
+        int ret;
+
+        if ((filter == NULL) || (index == NULL))
+                return 0;
+        ret = sscanf(rule_id, "%as[^:]:%u", filter, index);
+        if (ret != 2) {
+                free(*filter);
+                *filter = NULL;
+
+                return 0;
+        }
+
+        return 1;
+}
+
+/*
+ * Local Variables:
+ * mode: C
+ * c-set-style: "K&R"
+ * tab-width: 8
+ * c-basic-offset: 8
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/libxkutil/acl_parsing.h b/libxkutil/acl_parsing.h
new file mode 100644
--- /dev/null
+++ b/libxkutil/acl_parsing.h
@@ -0,0 +1,212 @@
+/*
+ * Copyright IBM Corp. 2011
+ *
+ * Authors:
+ *  Chip Vincent <cvincent at us.ibm.com>
+ *
+ * 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
+ */
+#ifndef __ACL_PARSING_H
+#define __ACL_PARSING_H
+
+#include <libvirt/libvirt.h>
+#include <libxml/tree.h>
+#include <libxml/parser.h>
+#include <libxml/xpath.h>
+
+struct acl_mac_rule {
+        char *srcmacaddr;
+        char *srcmacmask;
+        char *dstmacaddr;
+        char *dstmacmask;
+
+        char *protocol_id;
+        char *comment;
+};
+
+struct acl_arp_rule {
+        char *srcmacaddr;
+        char *srcmacmask;
+        char *dstmacaddr;
+        char *dstmacmask;
+
+        char *hw_type;
+        char *protocol_type;
+        char *opcode;
+        char *arpsrcmacaddr;
+        char *arpdstmacaddr;
+        char *arpsrcipaddr;
+        char *arpdstipaddr;
+        char *comment;
+};
+
+struct acl_ip_rule {
+        char *srcmacaddr;
+        char *srcmacmask;
+        char *dstmacaddr;
+        char *dstmacmask;
+
+        char *srcipaddr;
+        char *srcipmask;
+        char *dstipaddr;
+        char *dstipmask;
+
+        char *protocol;
+
+        char *srcportstart;
+        char *srcportend;
+        char *dstportstart;
+        char *dstportend;
+
+        char *comment;
+};
+
+struct acl_tcp_rule {
+        char *srcmacaddr;
+
+        char *srcipaddr;
+        char *srcipmask;
+        char *dstipaddr;
+        char *dstipmask;
+
+        char *srcipfrom;
+        char *srcipto;
+        char *dstipfrom;
+        char *dstipto;
+
+        char *srcportstart;
+        char *srcportend;
+        char *dstportstart;
+        char *dstportend;
+
+        char *comment;
+        char *state;
+};
+
+struct acl_igmp_rule {
+        char *srcmacaddr;
+        char *srcmacmask;
+        char *dstmacaddr;
+        char *dstmacmask;
+
+        char *srcipaddr;
+        char *srcipmask;
+        char *dstipaddr;
+        char *dstipmask;
+
+        char *srcipfrom;
+        char *srcipto;
+        char *dstipfrom;
+        char *dstipto;
+
+        char *type;
+        char *code;
+        char *comment;
+        char *state;
+};
+
+struct acl_icmp_rule {
+        char *srcmacaddr;
+        char *srcmacmask;
+        char *dstmacaddr;
+        char *dstmacmask;
+
+        char *srcipaddr;
+        char *srcipmask;
+        char *dstipaddr;
+        char *dstipmask;
+
+        char *srcipfrom;
+        char *srcipto;
+        char *dstipfrom;
+        char *dstipto;
+
+        char *type;
+        char *code;
+        char *comment;
+        char *state;
+};
+
+struct acl_rule {
+        char *name;
+        char *protocol_id;
+        char *action;
+        char *direction;
+        char *priority;
+        char *statematch;
+
+        enum {
+                UNKNOWN_RULE,
+                MAC_RULE,
+                ARP_RULE,
+                IP_RULE,
+                TCP_RULE,
+                ICMP_RULE,
+                IGMP_RULE
+        } type;
+
+        union {
+                struct acl_mac_rule mac;
+                struct acl_arp_rule arp;
+                struct acl_ip_rule ip;
+                struct acl_tcp_rule tcp;
+                struct acl_icmp_rule icmp;
+                struct acl_igmp_rule igmp;
+        } var;
+};
+
+struct acl_filter {
+        char *uuid;
+        char *name;
+        char *chain;
+
+        struct acl_rule **rules;
+        int rule_ct;
+
+        char **refs;
+        int ref_ct;
+};
+
+void cleanup_rule(struct acl_rule *rule);
+void cleanup_filter(struct acl_filter *filter);
+void cleanup_filters(struct acl_filter **filters, int count);
+
+int get_filters(virConnectPtr conn, struct acl_filter **list);
+
+int get_filter_from_xml(const char *xml, struct acl_filter **filter);
+int get_filter_by_uuid(virConnectPtr conn, const char *uuid, 
+        struct acl_filter **filter);
+int get_filter_by_name(virConnectPtr conn, const char *name,
+        struct acl_filter **filter);
+
+char *make_rule_id(const char *filter, int index);
+int parse_rule_id(const char *rule_id, char **filter, int *index);
+
+/** NOTE: Both append functions take parameters allocated by caller and
+ *  freed by cleanup_filter(s)
+ */
+int append_filter_rule(struct acl_filter *filter, struct acl_rule *rule);
+int append_filter_ref(struct acl_filter *filter, char *name);
+#endif
+
+/*
+ * Local Variables:
+ * mode: C
+ * c-set-style: "K&R"
+ * tab-width: 8
+ * c-basic-offset: 8
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/libxkutil/device_parsing.c b/libxkutil/device_parsing.c
--- a/libxkutil/device_parsing.c
+++ b/libxkutil/device_parsing.c
@@ -80,6 +80,7 @@
         free(dev->model);
         free(dev->device);
         free(dev->net_mode);
+        free(dev->filter_ref);
 }
 
 static void cleanup_emu_device(struct emu_device *dev)
@@ -386,9 +387,12 @@
                         ndev->model = get_attr_value(child, "type");
                         if (ndev->model == NULL)
                                 goto err;
+                } else if (XSTREQ(child->name, "filterref")) {
+                        ndev->filter_ref = get_attr_value(child, "filter");
                 } else if (XSTREQ(child->name, "virtualport")) {
                         parse_vsi_device(child, ndev);
                 }
+
         }
 
         if (ndev->source == NULL)
@@ -764,6 +768,7 @@
                 DUP_FIELD(dev, _dev, dev.net.model);
                 DUP_FIELD(dev, _dev, dev.net.device);
                 DUP_FIELD(dev, _dev, dev.net.net_mode);
+                DUP_FIELD(dev, _dev, dev.net.filter_ref);
                 DUP_FIELD(dev, _dev, dev.net.vsi.vsi_type);
                 DUP_FIELD(dev, _dev, dev.net.vsi.manager_id);
                 DUP_FIELD(dev, _dev, dev.net.vsi.type_id);
diff --git a/libxkutil/device_parsing.h b/libxkutil/device_parsing.h
--- a/libxkutil/device_parsing.h
+++ b/libxkutil/device_parsing.h
@@ -64,6 +64,7 @@
         char *model;
         char *device;
         char *net_mode;
+        char *filter_ref;
         struct vsi_device vsi;
 };
 
diff --git a/libxkutil/xmlgen.c b/libxkutil/xmlgen.c
--- a/libxkutil/xmlgen.c
+++ b/libxkutil/xmlgen.c
@@ -301,6 +301,15 @@
                         xmlNewProp(tmp, BAD_CAST "type", BAD_CAST net->model);
                 }
 
+                if (net->filter_ref != NULL) {
+                        tmp = xmlNewChild(nic, NULL, 
+                                BAD_CAST "filterref", NULL);
+                        if (tmp == NULL)
+                                return XML_ERROR;
+                        xmlNewProp(tmp, BAD_CAST "filter", 
+                                BAD_CAST net->filter_ref);
+                }
+
                 if (STREQ(dev->dev.net.type, "network"))
                         msg = set_net_source(nic, net, "network");
                 else if (STREQ(dev->dev.net.type, "bridge"))
diff --git a/libxkutil/xmlgen.h b/libxkutil/xmlgen.h
--- a/libxkutil/xmlgen.h
+++ b/libxkutil/xmlgen.h
@@ -23,6 +23,7 @@
 
 #include "device_parsing.h"
 #include "pool_parsing.h"
+#include "acl_parsing.h"
 
 #include "cmpidt.h"
 
@@ -38,4 +39,6 @@
 
 char *res_to_xml(struct virt_pool_res *res);
 
+char *filter_to_xml(struct acl_filter *filter);
+
 #endif
diff --git a/schema/AppliedFilterList.mof b/schema/AppliedFilterList.mof
new file mode 100644
--- /dev/null
+++ b/schema/AppliedFilterList.mof
@@ -0,0 +1,8 @@
+// Copyright IBM Corp. 2011
+[Association,
+ Provider("cmpi::Virt_AppliedFilterList")]
+class KVM_AppliedFilterList : CIM_ConcreteDependency
+{
+        CIM_NetworkPort ref Antecedent;
+        CIM_FilterList ref Dependent;
+};
diff --git a/schema/AppliedFilterList.registration b/schema/AppliedFilterList.registration
new file mode 100644
--- /dev/null
+++ b/schema/AppliedFilterList.registration
@@ -0,0 +1,3 @@
+# Copyright IBM Corp. 2011
+# Classname Namespace ProviderName ProviderModule ProviderTypes
+KVM_AppliedFilterList root/virt Virt_AppliedFilterList Virt_AppliedFilterList association
diff --git a/schema/EntriesInFilterList.mof b/schema/EntriesInFilterList.mof
new file mode 100644
--- /dev/null
+++ b/schema/EntriesInFilterList.mof
@@ -0,0 +1,6 @@
+// Copyright IBM Corp. 2011
+[Association,
+ Provider("cmpi::Virt_EntriesInFilterList")]
+class KVM_EntriesInFilterList : CIM_EntriesInFilterList
+{
+};
diff --git a/schema/EntriesInFilterList.registration b/schema/EntriesInFilterList.registration
new file mode 100644
--- /dev/null
+++ b/schema/EntriesInFilterList.registration
@@ -0,0 +1,3 @@
+# Copyright IBM Corp. 2011
+# Classname Namespace ProviderName ProviderModule ProviderTypes
+KVM_EntriesInFilterList root/virt Virt_EntriesInFilterList Virt_EntriesInFilterList association
diff --git a/schema/FilterEntry.mof b/schema/FilterEntry.mof
new file mode 100644
--- /dev/null
+++ b/schema/FilterEntry.mof
@@ -0,0 +1,60 @@
+// Copyright IBM Corp. 2011
+[Provider("cmpi::Virt_FilterEntry")]
+class KVM_Hdr8021Filter : CIM_Hdr8021Filter
+{
+        [Description("This defines whether the Filter is used for input, "
+	        "output, or both input and output filtering.  All values are "
+                "used with respect to the interface for which the Filter "
+                "applies. \"Not Applicable\" (0) is used when there is no "
+                "direction applicable to the Filter. \"Input\" (1) is "
+                "used when the Filter applies to packets that are inbound "
+                "on the related interface. \"Output\" (2) is used when the "
+                "Filter applies to packets that are outbound on the "
+                "related interface. \"Both\" (3) is used to indicate that "
+                "the direction is immaterial, e.g., to filter on a source "
+                "subnet regardless of whether the flow is inbound or "
+                "outbound."),
+        ValueMap { "0", "1", "2", "3", "4" },
+        Values { "Not Applicable", "Input, Output", "Both", "Mirrored" }]
+        uint16 Direction;
+
+        [Description("The priority of the rule controls the order in which "
+                "the rule will be, instantiated relative to other rules. "
+                "Rules with lower value will be instantiated and therefore "
+                "evaluated before rules with higher value. Valid values are "
+                "in the range of 0 to 1000. If this attribute is not "
+                "provided, the value 500 will automatically be assigned."),
+        MinValue(0),
+        MaxValue(1000)]
+        uint16 Priority = 500;
+};
+
+[Provider("cmpi::Virt_FilterEntry")]
+class KVM_IPHeadersFilter : CIM_IPHeadersFilter
+{
+        [Description("This defines whether the Filter is used for input, "
+	        "output, or both input and output filtering.  All values are "
+                "used with respect to the interface for which the Filter "
+                "applies. \"Not Applicable\" (0) is used when there is no "
+                "direction applicable to the Filter. \"Input\" (1) is "
+                "used when the Filter applies to packets that are inbound "
+                "on the related interface. \"Output\" (2) is used when the "
+                "Filter applies to packets that are outbound on the "
+                "related interface. \"Both\" (3) is used to indicate that "
+                "the direction is immaterial, e.g., to filter on a source "
+                "subnet regardless of whether the flow is inbound or "
+                "outbound."),
+        ValueMap { "0", "1", "2", "3", "4" },
+        Values { "Not Applicable", "Input, Output", "Both", "Mirrored" }]
+        uint16 Direction;
+
+        [Description("The priority of the rule controls the order in which "
+                "the rule will be, instantiated relative to other rules. "
+                "Rules with lower value will be instantiated and therefore "
+                "evaluated before rules with higher value. Valid values are "
+                "in the range of 0 to 1000. If this attribute is not "
+                "provided, the value 500 will automatically be assigned."),
+        MinValue(0),
+        MaxValue(1000)]
+        uint16 Priority = 500;
+};
diff --git a/schema/FilterEntry.registration b/schema/FilterEntry.registration
new file mode 100644
--- /dev/null
+++ b/schema/FilterEntry.registration
@@ -0,0 +1,4 @@
+# Copyright IBM Corp. 2011
+# Classname Namespace ProviderName ProviderModule ProviderTypes
+KVM_Hdr8021Filter root/virt Virt_FilterEntry Virt_FilterEntry instance
+KVM_IPHeadersFilter root/virt Virt_FilterEntry Virt_FilterEntry instance
diff --git a/schema/FilterList.mof b/schema/FilterList.mof
new file mode 100644
--- /dev/null
+++ b/schema/FilterList.mof
@@ -0,0 +1,4 @@
+// Copyright IBM Corp. 2011
+class KVM_FilterList : CIM_FilterList
+{
+};
diff --git a/schema/FilterList.registration b/schema/FilterList.registration
new file mode 100755
--- /dev/null
+++ b/schema/FilterList.registration
@@ -0,0 +1,3 @@
+# Copyright IBM Corp. 2011
+# Classname Namespace ProviderName ProviderModule ProviderTypes
+KVM_FilterList root/virt Virt_FilterList Virt_FilterList instance
diff --git a/schema/HostedFilterList.mof b/schema/HostedFilterList.mof
new file mode 100644
--- /dev/null
+++ b/schema/HostedFilterList.mof
@@ -0,0 +1,6 @@
+// Copyright IBM Corp. 2011
+[Association,
+ Provider("cmpi::Virt_HostedFilterList")]
+class KVM_HostedFilterList : CIM_HostedFilterList
+{
+};
diff --git a/schema/HostedFilterList.registration b/schema/HostedFilterList.registration
new file mode 100644
--- /dev/null
+++ b/schema/HostedFilterList.registration
@@ -0,0 +1,3 @@
+# Copyright IBM Corp. 2011
+# Classname Namespace ProviderName ProviderModule ProviderTypes
+KVM_HostedFilterList root/virt Virt_HostedFilterList Virt_HostedFilterList association
diff --git a/schema/NestedFilterList.mof b/schema/NestedFilterList.mof
new file mode 100644
--- /dev/null
+++ b/schema/NestedFilterList.mof
@@ -0,0 +1,8 @@
+// Copyright IBM Corp. 2011
+[Association,
+ Provider("cmpi::Virt_NestedFilterList")]
+class KVM_NestedFilterList : CIM_ConcreteDependency
+{
+        CIM_FilterList ref Antecedent;
+        CIM_FilterList ref Dependent;
+};
diff --git a/schema/NestedFilterList.registration b/schema/NestedFilterList.registration
new file mode 100644
--- /dev/null
+++ b/schema/NestedFilterList.registration
@@ -0,0 +1,3 @@
+# Copyright IBM Corp. 2011
+# Classname Namespace ProviderName ProviderModule ProviderTypes
+KVM_NestedFilterList root/virt Virt_NestedFilterList Virt_NestedFilterList association
diff --git a/src/Makefile.am b/src/Makefile.am
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -23,7 +23,9 @@
     Virt_VSMigrationSettingData.h \
     Virt_ConsoleRedirectionService.h \
     Virt_ConsoleRedirectionServiceCapabilities.h \
-    Virt_KVMRedirectionSAP.h
+    Virt_KVMRedirectionSAP.h \
+    Virt_FilterList.h \
+    Virt_FilterEntry.h
 
 XKUADD = $(top_builddir)/libxkutil/libxkutil.la
 
@@ -77,7 +79,13 @@
                        libVirt_ServiceAffectsElement.la \
                        libVirt_HostedAccessPoint.la \
                        libVirt_ServiceAccessBySAP.la \
-                       libVirt_SAPAvailableForElement.la
+                       libVirt_SAPAvailableForElement.la \
+                       libVirt_FilterList.la \
+                       libVirt_FilterEntry.la \
+                       libVirt_EntriesInFilterList.la \
+                       libVirt_NestedFilterList.la \
+                       libVirt_HostedFilterList.la \
+                       libVirt_AppliedFilterList.la
 
 libVirt_ComputerSystem_la_SOURCES = Virt_ComputerSystem.c
 libVirt_ComputerSystem_la_DEPENDENCIES = libVirt_VirtualSystemSnapshotService.la
@@ -243,3 +251,26 @@
 libVirt_SAPAvailableForElement_la_SOURCES = Virt_SAPAvailableForElement.c
 libVirt_SAPAvailableForElement_la_LIBADD = -lVirt_ComputerSystem -lVirt_KVMRedirectionSAP
 
+libVirt_FilterEntry_la_DEPENDENCIES = libVirt_HostSystem.la
+libVirt_FilterEntry_la_SOURCES = Virt_FilterEntry.c
+libVirt_FilterEntry_la_LIBADD = -lVirt_HostSystem
+
+libVirt_FilterList_la_DEPENDENCIES = libVirt_HostSystem.la
+libVirt_FilterList_la_SOURCES = Virt_FilterList.c
+libVirt_FilterList_la_LIBADD = -lVirt_HostSystem
+
+libVirt_EntriesInFilterList_la_DEPENDENCIES = libVirt_FilterEntry.la libVirt_FilterList.la 
+libVirt_EntriesInFilterList_la_SOURCES = Virt_EntriesInFilterList.c
+libVirt_EntriesInFilterList_la_LIBADD = -lVirt_FilterEntry -lVirt_FilterList
+
+libVirt_NestedFilterList_la_DEPENDENCIES = libVirt_FilterList.la 
+libVirt_NestedFilterList_la_SOURCES = Virt_NestedFilterList.c
+libVirt_NestedFilterList_la_LIBADD = -lVirt_FilterList
+
+libVirt_HostedFilterList_la_DEPENDENCIES = libVirt_HostSystem.la libVirt_FilterList.la 
+libVirt_HostedFilterList_la_SOURCES = Virt_HostedFilterList.c
+libVirt_HostedFilterList_la_LIBADD = -lVirt_HostSystem -lVirt_FilterList
+
+libVirt_AppliedFilterList_la_DEPENDENCIES = libVirt_Device.la libVirt_FilterList.la
+libVirt_AppliedFilterList_la_SOURCES = Virt_AppliedFilterList.c
+libVirt_AppliedFilterList_la_LIBADD = -lVirt_Device -lVirt_FilterList
diff --git a/src/Virt_AppliedFilterList.c b/src/Virt_AppliedFilterList.c
new file mode 100644
--- /dev/null
+++ b/src/Virt_AppliedFilterList.c
@@ -0,0 +1,634 @@
+/*
+ * Copyright IBM Corp. 2011
+ *
+ * Authors:
+ *  Chip Vincent <cvincent at us.ibm.com>
+ *
+ * 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
+ */
+#include <cmpidt.h>
+#include <cmpift.h>
+#include <cmpimacs.h>
+
+#include <libcmpiutil/libcmpiutil.h>
+#include <libcmpiutil/std_association.h>
+#include <libcmpiutil/std_instance.h>
+
+#include "device_parsing.h"
+#include "acl_parsing.h"
+#include "misc_util.h"
+#include "cs_util.h"
+
+#include "Virt_Device.h"
+#include "Virt_FilterList.h"
+
+static const CMPIBroker *_BROKER;
+
+/* TODO: Port to libcmpiutil/args_util.c */
+/**
+ * Get a reference property of an instance
+ *
+ * @param inst The instance
+ * @param prop The property name
+ * @param reference A pointer to a CMPIObjectPath* that will be set
+ *                  if successful
+ * @returns
+ *      - CMPI_RC_OK on success
+ *      - CMPI_RC_ERR_NO_SUCH_PROPERTY if prop is not present
+ *      - CMPI_RC_ERR_TYPE_MISMATCH if prop is not a reference
+ *      - CMPI_RC_OK otherwise
+ */
+static CMPIrc cu_get_ref_prop(const CMPIInstance *instance,
+                              const char *prop,
+                              CMPIObjectPath **reference)
+{
+        CMPIStatus s = {CMPI_RC_OK, NULL};
+        CMPIData value;
+
+        /* REQUIRE_PROPERY_DEFINED(instance, prop, value, &s); */
+        value = CMGetProperty(instance, prop, &s);
+        if ((s.rc != CMPI_RC_OK) || CMIsNullValue(value))
+                return CMPI_RC_ERR_NO_SUCH_PROPERTY;
+
+        if ((value.type != CMPI_ref) ||  CMIsNullObject(value.value.ref))
+                return CMPI_RC_ERR_TYPE_MISMATCH;
+
+        *reference = value.value.ref;
+
+        return CMPI_RC_OK;
+}
+
+/* TODO: Port to libcmpiutil/args_util.c */
+/**
+ * Get a reference component of an object path
+ *
+ * @param _reference The reference
+ * @param key The key name
+ * @param reference A pointer to a CMPIObjectPath* that will be set
+ *                  if successful
+ * @returns
+ *      - CMPI_RC_OK on success
+ *      - CMPI_RC_ERR_NO_SUCH_PROPERTY if prop is not present
+ *      - CMPI_RC_ERR_TYPE_MISMATCH if prop is not a reference
+ *      - CMPI_RC_OK otherwise
+ */
+static CMPIrc cu_get_ref_path(const CMPIObjectPath *reference,
+                              const char *key,
+                              CMPIObjectPath **_reference)
+{
+        CMPIStatus s = {CMPI_RC_OK, NULL};
+        CMPIData value;
+
+        /* REQUIRE_PROPERY_DEFINED(instance, prop, value, &s); */
+        value = CMGetKey(reference, key, &s);
+        if ((s.rc != CMPI_RC_OK) || CMIsNullValue(value))
+                return CMPI_RC_ERR_NO_SUCH_PROPERTY;
+
+        /* how to parse and object path? */
+
+        return CMPI_RC_OK;
+}
+
+/* TODO: Port to libxkutil/device_parsing.c */
+static int update_device(virDomainPtr dom,
+                         struct virt_device *dev)
+{
+        char *xml = NULL;
+        int flags = VIR_DOMAIN_DEVICE_MODIFY_CURRENT |
+                    VIR_DOMAIN_DEVICE_MODIFY_CONFIG;
+        int ret = 0;
+
+        /** device_to_xml() is not exported, so this function needs
+         * to be moved
+         */
+
+        /* xml = device_to_xml(dev); */
+
+        if (xml == NULL) {
+                CU_DEBUG("Failed to get XML for device '%s'", dev->id);
+                goto out;
+        }
+
+        if (virDomainUpdateDeviceFlags(dom, xml, flags) != 0) {
+                CU_DEBUG("Failed to dynamically update device:");
+                CU_DEBUG("%s", xml);
+                goto out;
+        }
+
+        ret = 1;
+ out:
+        free(xml);
+
+        return ret;
+}
+
+/* TODO: Port to libxkutil/device_parsing.c */
+static int get_device_by_devid(virDomainPtr dom,
+                               const char *devid,
+                               int type,
+                               struct virt_device **dev)
+{
+        int i, ret = 0;
+        struct virt_device *devices = NULL;
+        int count = get_devices(dom, &devices, type);
+
+        for (i = 0; i < count; i++) {
+                if (STREQC(devid, devices[i].id)) {
+                        CU_DEBUG("Found '%s'", devices[i].id);
+
+                        *dev = virt_device_dup(&devices[i]);
+                        if (*dev != NULL)
+                                ret = 1;
+
+                        break;
+                }
+        }
+
+        cleanup_virt_devices(&devices, count);
+
+        return ret;
+}
+
+/**
+ *  given a filter, get the network interface
+ */
+static CMPIStatus list_to_net(
+        const CMPIObjectPath *reference,
+        struct std_assoc_info *info,
+        struct inst_list *list)
+{
+        CMPIStatus s = {CMPI_RC_OK, NULL};
+        const char *name = NULL;
+        struct acl_filter *filter = NULL;
+        virDomainPtr *doms = NULL;
+        virConnectPtr conn = NULL;
+        int i, j, dcount, ncount;
+
+        CU_DEBUG("Reference = %s", REF2STR(reference));
+
+        if (cu_get_str_path(reference, "Name", &name) != CMPI_RC_OK) {
+                cu_statusf(_BROKER, &s,
+                        CMPI_RC_ERR_NOT_FOUND,
+                        "Unable to get Name from reference");
+                goto out;
+        }
+
+        conn = connect_by_classname(_BROKER, CLASSNAME(reference), &s);
+        if (conn == NULL)
+                goto out;
+
+        /* validate filter */
+        get_filter_by_name(conn, name, &filter);
+        if (filter == NULL)
+                goto out;
+
+        cleanup_filter(filter);
+
+        /* get domains */
+        dcount = get_domain_list(conn, &doms);
+        if (dcount < 0) {
+                cu_statusf(_BROKER, &s,
+                        CMPI_RC_ERR_FAILED,
+                        "Failed to get domain list");
+                goto out;
+        }
+
+        for (i = 0; i < dcount; i++) {
+                /* get domain's network devices */
+                struct virt_device *devices = NULL;
+                ncount = get_devices(doms[i], &devices, CIM_RES_TYPE_NET);
+
+                CU_DEBUG("Found %u network devices", ncount);
+
+                for (j = 0; j < ncount; j++) {
+                        struct net_device *ndev = &(devices[j].dev.net);
+
+                        CU_DEBUG("filterref = %s", ndev->filter_ref);
+
+                        if ((ndev->filter_ref != NULL) &&
+                        STREQC(name, ndev->filter_ref)) {
+                                CU_DEBUG("Getting network device instance");
+
+                                CMPIInstance *instance = NULL;
+                                char *device_id =
+                                        get_fq_devid(
+                                        (char *)virDomainGetName(doms[i]),
+                                        devices[j].id);
+
+                                CU_DEBUG("Processing %s", device_id);
+
+                                s = get_device_by_name(_BROKER,
+                                                        reference,
+                                                        device_id,
+                                                        CIM_RES_TYPE_NET,
+                                                        &instance);
+
+                                if (instance != NULL) {
+                                        CU_DEBUG("adding instance to list");
+                                        inst_list_add(list, instance);
+                                }
+                        }
+                }
+
+                cleanup_virt_devices(&devices, ncount);
+                virDomainFree(doms[i]);
+        }
+
+ out:
+        virConnectClose(conn);
+
+        return s;
+}
+
+/**
+ * given a network interface, find the filter lists
+ */
+static CMPIStatus net_to_list(
+        const CMPIObjectPath *reference,
+        struct std_assoc_info *info,
+        struct inst_list *list)
+{
+        CMPIStatus s = {CMPI_RC_OK, NULL};
+        CMPIInstance *instance = NULL;
+        const char *device_name = NULL;
+        char *domain_name = NULL;
+        char *net_name = NULL;
+        virConnectPtr conn = NULL;
+        virDomainPtr dom = NULL;
+        int i;
+        struct acl_filter *filter = NULL;
+
+        CU_DEBUG("Reference %s", REF2STR(reference));
+
+        /* validate device
+         * TODO: This may be redundant since it's necessary to get
+         * the device structure in order to determine the filter_ref
+         */
+        if (!STREQC(CLASSNAME(reference), "KVM_NetworkPort"))
+                goto out;
+
+        s = get_device_by_ref(_BROKER, reference, &instance);
+        if ((s.rc != CMPI_RC_OK) || (instance == NULL))
+                goto out;
+
+        if (cu_get_str_path(reference, "DeviceID",
+                &device_name) != CMPI_RC_OK) {
+                CU_DEBUG("Failed to get DeviceID");
+                goto out;
+        }
+
+        if (parse_fq_devid(device_name, &domain_name, &net_name) == 0) {
+                CU_DEBUG("Failed to parse devid");
+                goto out;
+        }
+
+        conn = connect_by_classname(_BROKER, CLASSNAME(reference), &s);
+        if (conn == NULL)
+                goto out;
+
+        /* connect to domain */
+        dom = virDomainLookupByName(conn, domain_name);
+        if (dom == NULL) {
+                CU_DEBUG("Failed to connect to Domain '%s'", domain_name);
+                goto out;
+        }
+
+        /* get domain's network devices */
+        struct virt_device *devices = NULL;
+        int count = get_devices(dom, &devices, CIM_RES_TYPE_NET);
+
+        CU_DEBUG("Found %u net devices on dom '%s'", count, domain_name);
+
+        for (i = 0; i < count; i++) {
+                struct net_device *ndev = &(devices[i].dev.net);
+
+                CU_DEBUG("Checking net device '%s' for filterref",
+                        devices[i].id);
+
+                if (STREQC(device_name, devices[i].id)) {
+                        CMPIInstance *instance = NULL;
+
+                        CU_DEBUG("Processing %s", ndev->filter_ref);
+
+                        get_filter_by_name(conn, ndev->filter_ref, &filter);
+                        if (filter == NULL)
+                                continue;
+
+                        s = instance_from_filter(_BROKER,
+                                                info->context,
+                                                reference,
+                                                filter,
+                                                &instance);
+
+                        if (instance != NULL)
+                                inst_list_add(list, instance);
+
+                }
+
+        }
+
+        cleanup_virt_devices(&devices, count);
+ out:
+
+        free(domain_name);
+        free((char *)device_name);
+        free(net_name);
+
+        virDomainFree(dom);
+        virConnectClose(conn);
+
+        return s;
+}
+
+LIBVIRT_CIM_DEFAULT_MAKEREF()
+
+static char *antecedent[] = {
+        "KVM_FilterList",
+        NULL
+};
+
+static char *dependent[] = {
+        "KVM_NetworkPort",
+        NULL
+};
+
+static char *assoc_class_name[] = {
+        "KVM_AppliedFilterList",
+        NULL
+};
+
+static struct std_assoc _list_to_net = {
+        .source_class = (char **)&antecedent,
+        .source_prop = "Antecedent",
+
+        .target_class = (char **)&dependent,
+        .target_prop = "Dependent",
+
+        .assoc_class = (char **)&assoc_class_name,
+
+        .handler = list_to_net,
+        .make_ref = make_ref
+};
+
+static struct std_assoc _net_to_list = {
+        .source_class = (char **)&dependent,
+        .source_prop = "Dependent",
+
+        .target_class = (char **)&antecedent,
+        .target_prop = "Antecedent",
+
+        .assoc_class = (char **)&assoc_class_name,
+
+        .handler = net_to_list,
+        .make_ref = make_ref
+};
+
+static struct std_assoc *handlers[] = {
+        &_list_to_net,
+        &_net_to_list,
+        NULL
+};
+
+STDA_AssocMIStub(,
+        Virt_AppliedFilterList,
+        _BROKER,
+        libvirt_cim_init(),
+        handlers);
+
+DEFAULT_GI();
+DEFAULT_EIN();
+DEFAULT_EI();
+
+static CMPIStatus CreateInstance(
+        CMPIInstanceMI *self,
+        const CMPIContext *context,
+        const CMPIResult *results,
+        const CMPIObjectPath *reference,
+        const CMPIInstance *instance)
+{
+        CMPIStatus s = {CMPI_RC_OK, NULL};
+        CMPIObjectPath *antecedent = NULL;
+        const char *filter_name = NULL;
+        struct acl_filter *filter = NULL;
+        CMPIObjectPath *dependent = NULL;
+        char *domain_name = NULL;
+        const char *device_name = NULL;
+        char *net_name = NULL;
+        struct virt_device *device = NULL;
+        virConnectPtr conn = NULL;
+        virDomainPtr dom = NULL;
+
+        if (cu_get_ref_prop(instance, "Antecedent",
+                &antecedent) != CMPI_RC_OK) {
+                cu_statusf(_BROKER, &s,
+                        CMPI_RC_ERR_FAILED,
+                        "Unable to get Antecedent property");
+                goto out;
+        }
+
+        if (cu_get_str_path(reference, "Name", &filter_name) != CMPI_RC_OK) {
+                cu_statusf(_BROKER, &s,
+                        CMPI_RC_ERR_FAILED,
+                        "Unable to get Antecedent.Name property");
+                goto out;
+        }
+
+        get_filter_by_name(conn, filter_name, &filter);
+        if (filter == NULL) {
+                cu_statusf(_BROKER, &s,
+                        CMPI_RC_ERR_FAILED,
+                        "Antecedent.Name object does not exist");
+                goto out;
+        }
+
+        if (cu_get_ref_prop(instance, "Dependent",
+                &dependent) != CMPI_RC_OK) {
+                cu_statusf(_BROKER, &s,
+                        CMPI_RC_ERR_FAILED,
+                        "Unable to get Dependent property");
+                goto out;
+        }
+
+        if (cu_get_str_path(reference, "DeviceID",
+                &device_name) != CMPI_RC_OK) {
+                cu_statusf(_BROKER, &s,
+                        CMPI_RC_ERR_FAILED,
+                        "Unable to get Dependent.DeviceID property");
+                goto out;
+        }
+
+        if (parse_fq_devid(device_name, &domain_name, &net_name) == 0) {
+                CU_DEBUG("Failed to parse devid");
+                goto out;
+        }
+
+        dom = virDomainLookupByName(conn, domain_name);
+        if (dom == NULL) {
+                CU_DEBUG("Failed to connect to Domain '%s'", domain_name);
+                goto out;
+        }
+
+        get_device_by_devid(dom, net_name, CIM_RES_TYPE_NET, &device);
+        if (device == NULL) {
+                cu_statusf(_BROKER, &s,
+                        CMPI_RC_ERR_FAILED,
+                        "Dependent.Name object does not exist");
+                goto out;
+        }
+
+        free(device->dev.net.filter_ref);
+        device->dev.net.filter_ref = strdup(filter_name);
+
+        if (update_device(dom, device) == 0) {
+                cu_statusf(_BROKER, &s,
+                        CMPI_RC_ERR_FAILED,
+                        "Failed to update device");
+                goto out;
+        }
+
+ out:
+        free((char *)filter_name);
+        free(domain_name);
+        free((char *)device_name);
+        free(net_name);
+
+        cleanup_filter(filter);
+        cleanup_virt_device(device);
+
+        virDomainFree(dom);
+        virConnectClose(conn);
+
+        return s;
+}
+
+static CMPIStatus DeleteInstance(
+        CMPIInstanceMI *self,
+        const CMPIContext *context,
+        const CMPIResult *results,
+        const CMPIObjectPath *reference)
+{
+        CMPIStatus s = {CMPI_RC_OK, NULL};
+        CMPIObjectPath *antecedent = NULL;
+        const char *filter_name = NULL;
+        struct acl_filter *filter = NULL;
+        CMPIObjectPath *dependent = NULL;
+        char *domain_name = NULL;
+        const char *device_name = NULL;
+        char *net_name = NULL;
+        struct virt_device *device = NULL;
+        virConnectPtr conn = NULL;
+        virDomainPtr dom = NULL;
+
+        if (cu_get_ref_path(reference, "Antecedent",
+                &antecedent) != CMPI_RC_OK) {
+                cu_statusf(_BROKER, &s,
+                        CMPI_RC_ERR_FAILED,
+                        "Unable to get Antecedent property");
+                goto out;
+        }
+
+        if (cu_get_str_path(reference, "Name", &filter_name) != CMPI_RC_OK) {
+                cu_statusf(_BROKER, &s,
+                        CMPI_RC_ERR_FAILED,
+                        "Unable to get Antecedent.Name property");
+                goto out;
+        }
+
+        get_filter_by_name(conn, filter_name, &filter);
+        if (filter == NULL) {
+                cu_statusf(_BROKER, &s,
+                        CMPI_RC_ERR_FAILED,
+                        "Antecedent.Name object does not exist");
+                goto out;
+        }
+
+        if (cu_get_ref_path(reference, "Dependent",
+                &dependent) != CMPI_RC_OK) {
+                cu_statusf(_BROKER, &s,
+                        CMPI_RC_ERR_FAILED,
+                        "Unable to get Dependent property");
+                goto out;
+        }
+
+        if (cu_get_str_path(reference, "DeviceID",
+                &device_name) != CMPI_RC_OK) {
+                cu_statusf(_BROKER, &s,
+                        CMPI_RC_ERR_FAILED,
+                        "Unable to get Dependent.DeviceID property");
+                goto out;
+        }
+
+        if (parse_fq_devid(device_name, &domain_name, &net_name) == 0) {
+                CU_DEBUG("Failed to parse devid");
+                goto out;
+        }
+
+        dom = virDomainLookupByName(conn, domain_name);
+        if (dom == NULL) {
+                CU_DEBUG("Failed to connect to Domain '%s'", domain_name);
+                goto out;
+        }
+
+        get_device_by_devid(dom, net_name, CIM_RES_TYPE_NET, &device);
+        if (device == NULL) {
+                cu_statusf(_BROKER, &s,
+                        CMPI_RC_ERR_FAILED,
+                        "Dependent.Name object does not exist");
+                goto out;
+        }
+
+        free(device->dev.net.filter_ref);
+        device->dev.net.filter_ref = NULL;
+
+        if (update_device(dom, device) == 0) {
+                cu_statusf(_BROKER, &s,
+                        CMPI_RC_ERR_FAILED,
+                        "Failed to update device");
+                goto out;
+        }
+
+ out:
+        free((char *)filter_name);
+        free(domain_name);
+        free((char *)device_name);
+        free(net_name);
+
+        cleanup_filter(filter);
+        cleanup_virt_device(device);
+
+        virDomainFree(dom);
+        virConnectClose(conn);
+
+        return s;
+}
+
+DEFAULT_MI();
+DEFAULT_EQ();
+DEFAULT_INST_CLEANUP();
+
+STD_InstanceMIStub(,
+        Virt_AppliedFilterEntry,
+        _BROKER,
+        libvirt_cim_init());
+
+/*
+ * Local Variables:
+ * mode: C
+ * c-set-style: "K&R"
+ * tab-width: 8
+ * c-basic-offset: 8
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/src/Virt_EntriesInFilterList.c b/src/Virt_EntriesInFilterList.c
new file mode 100644
--- /dev/null
+++ b/src/Virt_EntriesInFilterList.c
@@ -0,0 +1,219 @@
+/*
+ * Copyright IBM Corp. 2011
+ *
+ * Authors:
+ *  Chip Vincent <cvincent at us.ibm.com>
+ *
+ * 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
+ */
+#include <cmpidt.h>
+#include <cmpift.h>
+#include <cmpimacs.h>
+
+#include <libcmpiutil/libcmpiutil.h>
+#include <libcmpiutil/std_association.h>
+
+#include <strings.h>
+
+#include "acl_parsing.h"
+#include "misc_util.h"
+#include "Virt_FilterList.h"
+#include "Virt_FilterEntry.h"
+
+static const CMPIBroker *_BROKER;
+
+/**
+ *  given a filter, find all *direct* children
+ */
+static CMPIStatus list_to_rule(
+        const CMPIObjectPath *reference,
+        struct std_assoc_info *info,
+        struct inst_list *list)
+{
+        CMPIStatus s = {CMPI_RC_OK, NULL};
+        CMPIInstance *instance = NULL;
+        struct acl_filter *filter = NULL;
+        const char *name = NULL;
+        virConnectPtr conn = NULL;
+        int i;
+
+        CU_DEBUG("Reference = %s", REF2STR(reference));
+
+        if (cu_get_str_path(reference, "Name", &name) != CMPI_RC_OK) {
+                cu_statusf(_BROKER, &s,
+                        CMPI_RC_ERR_NOT_FOUND,
+                        "Unable to get Name from reference");
+                goto out;
+        }
+
+        conn = connect_by_classname(_BROKER, CLASSNAME(reference), &s);
+        if (conn == NULL)
+                goto out;
+
+        get_filter_by_name(conn, name, &filter);
+        if (filter == NULL) {
+                CU_DEBUG("Filter '%s' does not exist", name);
+                goto out;
+        }
+
+        for (i = 0; i < filter->rule_ct; i++) {
+                CU_DEBUG("Processing %s", filter->rules[i]->name);
+
+                s = instance_from_rule(_BROKER,
+                                        info->context,
+                                        reference,
+                                        filter->rules[i],
+                                        &instance);
+
+                if (instance != NULL) {
+                        inst_list_add(list, instance);
+                        instance = NULL;
+                }
+        }
+
+        cleanup_filter(filter);
+
+ out:
+        virConnectClose(conn);
+
+        return s;
+}
+
+/**
+ * given a rule, fine the parent filter
+ */
+static CMPIStatus rule_to_list(
+        const CMPIObjectPath *reference,
+        struct std_assoc_info *info,
+        struct inst_list *list)
+{
+        CMPIStatus s = {CMPI_RC_OK, NULL};
+        struct acl_filter *filters = NULL;
+        CMPIInstance *instance = NULL;
+        const char *name = NULL;
+        virConnectPtr conn = NULL;
+        int count = 0;
+        int i, j = 0;
+
+        CU_DEBUG("Reference = %s", REF2STR(reference));
+
+        if (cu_get_str_path(reference, "Name", &name) != CMPI_RC_OK) {
+                cu_statusf(_BROKER, &s,
+                        CMPI_RC_ERR_NOT_FOUND,
+                        "Unable to get Name from reference");
+                goto out;
+        }
+
+        conn = connect_by_classname(_BROKER, CLASSNAME(reference), &s);
+        if (conn == NULL)
+                goto out;
+
+        count = get_filters(conn, &filters);
+        if (filters == NULL)
+                goto out;
+
+        /* return the filter that contains the rule */
+        for (i = 0; i < count; i++) {
+                for (j = 0; j < filters[i].rule_ct; j++) {
+                        if (STREQC(name, filters[i].rules[j]->name)) {
+                                CU_DEBUG("Processing %s,",filters[i].name);
+
+                                s = instance_from_filter(_BROKER,
+                                                        info->context,
+                                                        reference,
+                                                        &filters[i],
+                                                        &instance);
+
+                                if (instance != NULL) {
+                                        inst_list_add(list, instance);
+                                        instance = NULL;
+                                }
+
+                        }
+                }
+        }
+
+ out:
+        cleanup_filters(&filters, count);
+        virConnectClose(conn);
+
+        return s;
+}
+
+LIBVIRT_CIM_DEFAULT_MAKEREF()
+
+static char *group_component[] = {
+        "KVM_FilterList",
+        NULL
+};
+
+static char *part_component[] = {
+        "KVM_Hdr8021Filter",
+        "KVM_IPHeadersFilter",
+        NULL
+};
+
+static char *assoc_class_name[] = {
+        "KVM_EntriesInFilterList",
+        NULL
+};
+
+static struct std_assoc _list_to_rule = {
+        .source_class = (char **)&group_component,
+        .source_prop = "GroupComponent",
+
+        .target_class = (char **)&part_component,
+        .target_prop = "PartComponent",
+
+        .assoc_class = (char **)&assoc_class_name,
+
+        .handler = list_to_rule,
+        .make_ref = make_ref
+};
+
+static struct std_assoc _rule_to_list = {
+        .source_class = (char **)&part_component,
+        .source_prop = "PartComponent",
+
+        .target_class = (char **)&group_component,
+        .target_prop = "GroupComponent",
+
+        .assoc_class = (char **)&assoc_class_name,
+
+        .handler = rule_to_list,
+        .make_ref = make_ref
+};
+
+static struct std_assoc *handlers[] = {
+        &_list_to_rule,
+        &_rule_to_list,
+        NULL
+};
+
+STDA_AssocMIStub(,
+        Virt_EntriesInFilterList,
+        _BROKER,
+        libvirt_cim_init(),
+        handlers);
+
+/*
+ * Local Variables:
+ * mode: C
+ * c-set-style: "K&R"
+ * tab-width: 8
+ * c-basic-offset: 8
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/src/Virt_FilterEntry.c b/src/Virt_FilterEntry.c
new file mode 100644
--- /dev/null
+++ b/src/Virt_FilterEntry.c
@@ -0,0 +1,678 @@
+/*
+ * Copyright IBM Corp. 2011
+ *
+ * Authors:
+ *  Chip Vincent <cvincent at us.ibm.com>
+ *
+ * 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
+ */
+#include <cmpidt.h>
+#include <cmpift.h>
+#include <cmpimacs.h>
+
+#include <libcmpiutil/libcmpiutil.h>
+#include <libcmpiutil/std_instance.h>
+
+#include <arpa/inet.h>
+
+#include "acl_parsing.h"
+#include "misc_util.h"
+#include "xmlgen.h"
+
+#include "Virt_FilterEntry.h"
+#include "Virt_HostSystem.h"
+
+const static CMPIBroker *_BROKER;
+
+static bool is_mac_rule(int type)
+{
+        if (type == MAC_RULE || type == ARP_RULE)
+                return 1;
+
+        return 0;
+}
+
+static bool is_ip_rule(int type)
+{
+        if (type == IP_RULE || type == TCP_RULE || type == ICMP_RULE ||
+                type == IGMP_RULE)
+                return 1;
+
+        return 0;
+}
+
+static int octets_from_mac(const char * s, unsigned int *buffer,
+                                unsigned int size)
+{
+        unsigned int _buffer[6];
+        unsigned int i, n = 0;
+
+        if ((s == 0) || (s[0] == '\0') || (buffer == NULL) || (size < 6))
+                return 0;
+
+        if (s[0] == '$') {
+                for (i = 0; (s[i] != '\0') && (i < size); i++)
+                        buffer[i] = s[i];
+
+                n = i;
+        }
+        else {
+                n = sscanf(s, "%x:%x:%x:%x:%x:%x",
+                        &_buffer[0], &_buffer[1], &_buffer[2],
+                        &_buffer[3], &_buffer[4], &_buffer[5]);
+
+                for (i = 0; (i < n) && (i < size); i++)
+                        buffer[i] = _buffer[i];
+        }
+
+        return n;
+}
+
+static int octets_from_ip(const char * s, unsigned int *buffer,
+                                unsigned int size)
+{
+        struct in6_addr addr;
+        unsigned int family = 0;
+        unsigned int i, n = 0;
+
+        if ((s == 0) || (s[0] == '\0') || (buffer == NULL) || (size < 4))
+                return 0;
+
+        if (s[0] == '$') {
+                for (i = 0; (s[i] != '\0') && (i < size); i++)
+                        buffer[i] = s[i];
+
+                n = i;
+        }
+        else {
+                family = strstr(s, ":") ? AF_INET6 : AF_INET;
+                n  = family == AF_INET6 ? 16 : 4;
+
+                if (size < n)
+                        return 0;
+
+                if (inet_pton(family, s, &addr)) {
+                        n = n <= size ? n : size;
+                        for (i = 0; i < n; i++)
+                                buffer[i] = addr.s6_addr[i];
+                }
+        }
+
+        return n;
+}
+
+static CMPIArray *octets_to_cmpi(const CMPIBroker *broker, unsigned int *bytes, int size)
+{
+        CMPIStatus s = {CMPI_RC_OK, NULL};
+        CMPIArray *array = NULL;
+        int i;
+
+        if (bytes == 0 || size == 0)
+                return array;
+
+        array = CMNewArray(broker, size, CMPI_uint8, &s);
+
+        for (i = 0; i < size; i++) {
+                s = CMSetArrayElementAt(array, i,
+                        (CMPIValue*)&bytes[i], CMPI_uint8);
+        }
+
+        return array;
+}
+
+static int convert_direction(const char *s)
+{
+        enum {NOT_APPLICABLE, INPUT, OUTPUT, BOTH} direction = NOT_APPLICABLE;
+
+        if (s != NULL) {
+                if (STREQC(s, "in"))
+                        direction = INPUT;
+                else if (STREQC(s, "out"))
+                        direction = OUTPUT;
+                else if (STREQC(s, "inout"))
+                        direction = BOTH;
+        }
+
+        return direction;
+}
+
+static int convert_priority(const char *s)
+{
+        int priority = 0;
+
+        if (s != NULL) {
+                priority = atoi(s);
+        }
+
+        return priority;
+}
+
+static CMPIInstance *convert_mac_rule_to_instance(
+        struct acl_rule *rule,
+        const CMPIBroker *broker,
+        const CMPIContext *context,
+        const CMPIObjectPath *reference,
+        CMPIStatus *s)
+{
+        CMPIInstance *inst = NULL;
+        const char *sys_name = NULL;
+        const char *sys_ccname = NULL;
+        int direction, priority = 0;
+        unsigned int bytes[48];
+        unsigned int size = 0;
+        CMPIArray *array = NULL;
+
+        inst = get_typed_instance(broker,
+                                  CLASSNAME(reference),
+                                  "Hdr8021Filter",
+                                  NAMESPACE(reference));
+        if (inst == NULL) {
+                cu_statusf(broker, s,
+                           CMPI_RC_ERR_FAILED,
+                           "Unable to get 8021 filter instance");
+
+                goto out;
+        }
+
+        *s = get_host_system_properties(&sys_name,
+                                       &sys_ccname,
+                                       reference,
+                                       broker,
+                                       context);
+
+        if (s->rc != CMPI_RC_OK) {
+                cu_statusf(broker, s,
+                           CMPI_RC_ERR_FAILED,
+                           "Unable to get host attributes");
+                goto out;
+        }
+
+        CMSetProperty(inst, "SystemName", sys_name, CMPI_chars);
+        CMSetProperty(inst, "SystemCreationClassName", sys_ccname, CMPI_chars);
+        CMSetProperty(inst, "Name", (CMPIValue *)rule->name, CMPI_chars);
+
+        direction = convert_direction(rule->direction);
+        CMSetProperty(inst, "Direction", (CMPIValue *)&direction, CMPI_uint16);
+
+        priority = convert_priority(rule->priority);
+        CMSetProperty(inst, "Priority", (CMPIValue *)&priority, CMPI_uint16);
+
+        memset(bytes, 0, sizeof(bytes));
+        size = octets_from_mac(rule->var.mac.srcmacaddr,
+                bytes, sizeof(bytes));
+
+        array = octets_to_cmpi(broker, bytes, size);
+        if (array != NULL)
+                CMSetProperty(inst, "HdrSrcMACAddr8021",
+                        (CMPIValue *)&array, CMPI_uint8A);
+
+        memset(bytes, 0, sizeof(bytes));
+        size = octets_from_mac(rule->var.mac.srcmacmask,
+                bytes, sizeof(bytes));
+
+        array = octets_to_cmpi(broker, bytes, size);
+        if (array != NULL)
+                CMSetProperty(inst, "HdrSrcMACMask8021",
+                        (CMPIValue *)&array, CMPI_uint8A);
+
+        memset(bytes, 0, sizeof(bytes));
+        size = octets_from_mac(rule->var.mac.dstmacaddr,
+                bytes, sizeof(bytes));
+
+        array = octets_to_cmpi(broker, bytes, size);
+        if (array != NULL)
+                CMSetProperty(inst, "HdrDestMACAddr8021", (CMPIValue *)
+                        (CMPIValue *)&array, CMPI_uint8A);
+
+        memset(bytes, 0, sizeof(bytes));
+        size = octets_from_mac(rule->var.mac.dstmacmask,
+                bytes, sizeof(bytes));
+
+        array = octets_to_cmpi(broker, bytes, size);
+        if (array != NULL)
+                CMSetProperty(inst, "HdrDestMACMask8021", (CMPIValue *)
+                        (CMPIValue *)&array, CMPI_uint8A);
+
+ out:
+        return inst;
+}
+
+static CMPIInstance *convert_ip_rule_to_instance(
+        struct acl_rule *rule,
+        const CMPIBroker *broker,
+        const CMPIContext *context,
+        const CMPIObjectPath *reference,
+        CMPIStatus *s)
+{
+        CMPIInstance *inst = NULL;
+        const char *sys_name = NULL;
+        const char *sys_ccname = NULL;
+        int direction, priority = 0;
+        unsigned int bytes[48];
+        unsigned int size = 0;
+        unsigned int n = 0;
+        CMPIArray *array = NULL;
+
+        inst = get_typed_instance(broker,
+                                  CLASSNAME(reference),
+                                  "IPHeadersFilter",
+                                  NAMESPACE(reference));
+        if (inst == NULL) {
+                cu_statusf(broker, s,
+                           CMPI_RC_ERR_FAILED,
+                           "Unable to get ip headers filter instance");
+                goto out;
+        }
+
+        *s = get_host_system_properties(&sys_name,
+                                       &sys_ccname,
+                                       reference,
+                                       broker,
+                                       context);
+
+        if (s->rc != CMPI_RC_OK) {
+                cu_statusf(broker, s,
+                           CMPI_RC_ERR_FAILED,
+                           "Unable to get host attributes");
+                goto out;
+        }
+
+        CMSetProperty(inst, "SystemName", sys_name, CMPI_chars);
+        CMSetProperty(inst, "SystemCreationClassName", sys_ccname, CMPI_chars);
+        CMSetProperty(inst, "Name", (CMPIValue *)rule->name, CMPI_chars);
+
+        direction = convert_direction(rule->direction);
+        CMSetProperty(inst, "Direction", (CMPIValue *)&direction, CMPI_uint16);
+
+        priority = convert_priority(rule->priority);
+        CMSetProperty(inst, "Priority", (CMPIValue *)&priority, CMPI_uint16);
+
+        if (strstr(rule->protocol_id, "v6"))
+                n = 6;
+        else
+                n = 4;
+
+        CMSetProperty(inst, "HdrIPVersion",(CMPIValue *)&n, CMPI_uint8);
+
+        if (rule->var.tcp.srcipfrom && rule->var.tcp.srcipto) {
+                memset(bytes, 0, sizeof(bytes));
+                size = octets_from_ip(rule->var.tcp.srcipfrom,
+                        bytes, sizeof(bytes));
+
+                array = octets_to_cmpi(broker, bytes, size);
+                if (array != NULL)
+                        CMSetProperty(inst, "HdrSrcAddress",
+                                (CMPIValue *)&array, CMPI_uint8A);
+
+                memset(bytes, 0, sizeof(bytes));
+                size = octets_from_ip(rule->var.tcp.srcipto,
+                        bytes, sizeof(bytes));
+
+                array = octets_to_cmpi(broker, bytes, size);
+                if (array != NULL)
+                        CMSetProperty(inst, "HdrSrcAddressEndOfRange",
+                                (CMPIValue *)&array, CMPI_uint8A);
+        } else {
+                memset(bytes, 0, sizeof(bytes));
+                size = octets_from_ip(rule->var.tcp.srcmacaddr,
+                        bytes, sizeof(bytes));
+
+                array = octets_to_cmpi(broker, bytes, size);
+                if (array != NULL)
+                        CMSetProperty(inst, "HdrSrcAddress",
+                                (CMPIValue *)&array, CMPI_uint8A);
+
+                memset(bytes, 0, sizeof(bytes));
+                size = octets_from_ip(rule->var.tcp.srcipmask,
+                        bytes, sizeof(bytes));
+
+                array = octets_to_cmpi(broker, bytes, size);
+                if (array != NULL)
+                        CMSetProperty(inst, "HdrSrcMask",
+                                (CMPIValue *)&array, CMPI_uint8A);
+        }
+
+        if (rule->var.tcp.dstipfrom && rule->var.tcp.dstipto) {
+                memset(bytes, 0, sizeof(bytes));
+                size = octets_from_ip(rule->var.tcp.dstipfrom,
+                        bytes, sizeof(bytes));
+
+                array = octets_to_cmpi(broker, bytes, size);
+                if (array != NULL)
+                        CMSetProperty(inst, "HdrDestAddress",
+                                (CMPIValue *)&array, CMPI_uint8A);
+
+                memset(bytes, 0, sizeof(bytes));
+                size = octets_from_ip(rule->var.tcp.dstipto,
+                        bytes, sizeof(bytes));
+
+                array = octets_to_cmpi(broker, bytes, size);
+                if (array != NULL)
+                        CMSetProperty(inst, "HdrDestAddressEndOfRange",
+                                (CMPIValue *)&array, CMPI_uint8A);
+        } else {
+                memset(bytes, 0, sizeof(bytes));
+                size = octets_from_ip(rule->var.tcp.dstipaddr,
+                        bytes, sizeof(bytes));
+
+                array = octets_to_cmpi(broker, bytes, size);
+                if (array != NULL)
+                        CMSetProperty(inst, "HdrDestAddress",
+                                (CMPIValue *)&array, CMPI_uint8A);
+
+                memset(bytes, 0, sizeof(bytes));
+                size = octets_from_ip(rule->var.tcp.dstipmask,
+                        bytes, sizeof(bytes));
+
+                array = octets_to_cmpi(broker, bytes, size);
+                if (array != NULL)
+                        CMSetProperty(inst, "HdrDestMask",
+                                (CMPIValue *)&array, CMPI_uint8A);
+        }
+
+        if ((rule->type == IP_RULE) || (rule->type == TCP_RULE)) {
+                if (rule->var.tcp.srcportstart) {
+                        n = atoi(rule->var.tcp.srcportstart);
+                        CMSetProperty(inst, "HdrSrcPortStart",
+                                (CMPIValue *)&n, CMPI_uint16);
+                }
+
+                if (rule->var.tcp.srcportend) {
+                        n = atoi(rule->var.tcp.srcportend);
+                        CMSetProperty(inst, "HdrSrcPortEnd",
+                                (CMPIValue *)&n, CMPI_uint16);
+                }
+
+                if (rule->var.tcp.dstportstart) {
+                        n = atoi(rule->var.tcp.dstportstart);
+                        CMSetProperty(inst, "HdrDestPortStart",
+                                (CMPIValue *)&n, CMPI_uint16);
+                }
+
+                if (rule->var.tcp.dstportend) {
+                        n = atoi(rule->var.tcp.dstportend);
+                        CMSetProperty(inst, "HdrDestPortEnd",
+                                (CMPIValue *)&n, CMPI_uint16);
+                }
+        }
+
+ out:
+        return inst;
+}
+
+static CMPIInstance *convert_rule_to_instance(
+        struct acl_rule *rule,
+        const CMPIBroker *broker,
+        const CMPIContext *context,
+        const CMPIObjectPath *reference,
+        CMPIStatus *s)
+{
+        CMPIInstance *instance = NULL;
+
+        if (rule == NULL)
+                return NULL;
+
+        if(is_mac_rule(rule->type)) {
+                instance = convert_mac_rule_to_instance(rule,
+                                                broker,
+                                                context,
+                                                reference,
+                                                s);
+        }
+        else if(is_ip_rule(rule->type)) {
+                instance = convert_ip_rule_to_instance(rule,
+                                                broker,
+                                                context,
+                                                reference,
+                                                s);
+        }
+
+        return instance;
+}
+
+CMPIStatus enum_filter_rules(
+        const CMPIBroker *broker,
+        const CMPIContext *context,
+        const CMPIObjectPath *reference,
+        struct inst_list *list)
+{
+        virConnectPtr conn = NULL;
+        struct acl_filter *filters = NULL;
+        int i, j, count = 0;
+        CMPIStatus s = {CMPI_RC_OK, NULL};
+        enum {NONE, MAC, IP} class_type = NONE;
+
+        CU_DEBUG("Reference = %s", REF2STR(reference));
+
+        if (STREQC(CLASSNAME(reference), "KVM_Hdr8021Filter"))
+                class_type = MAC;
+        else if (STREQC(CLASSNAME(reference), "KVM_IPHeadersFilter"))
+                class_type = IP;
+
+        conn = connect_by_classname(_BROKER, CLASSNAME(reference), &s);
+        if (conn == NULL)
+                goto out;
+
+        count = get_filters(conn, &filters);
+
+        for (i = 0; i < count; i++) {
+                for (j = 0; j < filters[i].rule_ct; j++) {
+                        CMPIInstance *instance = NULL;
+
+                        if (((class_type == NONE) ||
+                                (class_type == MAC)) &&
+                                is_mac_rule(filters[i].rules[j]->type)) {
+                                instance = convert_mac_rule_to_instance(
+                                                filters[i].rules[j],
+                                                broker,
+                                                context,
+                                                reference,
+                                                &s);
+                        }
+                        else if (((class_type == NONE) ||
+                                (class_type == IP)) &&
+                                is_ip_rule(filters[i].rules[j]->type)) {
+                                instance = convert_ip_rule_to_instance(
+                                                filters[i].rules[j],
+                                                broker,
+                                                context,
+                                                reference,
+                                                &s);
+                        }
+                        else
+                                CU_DEBUG("Unrecognized rule type %u",
+                                        filters[i].rules[j]->type);
+
+                        if (instance != NULL)
+                                inst_list_add(list, instance);
+                }
+
+        }
+
+ out:
+        cleanup_filters(&filters, count);
+        virConnectClose(conn);
+
+        return s;
+}
+
+CMPIStatus get_rule_by_ref(
+        const CMPIBroker *broker,
+        const CMPIContext *context,
+        const CMPIObjectPath *reference,
+        CMPIInstance **instance)
+{
+        CMPIStatus s = {CMPI_RC_OK, NULL};
+        struct acl_filter *filter = NULL;
+        struct acl_rule *rule = NULL;
+        const char *name = NULL;
+        char *filter_name = NULL;
+        int rule_index;
+        virConnectPtr conn = NULL;
+        int i;
+
+        CU_DEBUG("Reference = %s", REF2STR(reference));
+
+        if (cu_get_str_path(reference, "Name", &name) != CMPI_RC_OK) {
+                cu_statusf(_BROKER, &s,
+                        CMPI_RC_ERR_NOT_FOUND,
+                        "Unable to get Name from reference");
+                goto out;
+        }
+
+        if (parse_rule_id(name,  &filter_name, &rule_index) == 0) {
+                cu_statusf(_BROKER, &s,
+                        CMPI_RC_ERR_NOT_FOUND,
+                        "Could not parse filter name");
+                goto out;
+        }
+
+        CU_DEBUG("Filter name = %s, rule index = %u", filter_name, rule_index);
+
+        conn = connect_by_classname(_BROKER, CLASSNAME(reference), &s);
+        if (conn == NULL)
+                goto out;
+
+        get_filter_by_name(conn, filter_name, &filter);
+        if (filter == NULL) {
+                cu_statusf(_BROKER, &s,
+                        CMPI_RC_ERR_NOT_FOUND,
+                        "Could not retrieve filter");
+                goto out;
+        }
+
+        for (i = 0; i < filter->rule_ct; i++) {
+                if (rule_index == i) {
+                        rule = filter->rules[i];
+                        break;
+                }
+        }
+
+        if (rule == NULL) {
+                cu_statusf(_BROKER, &s,
+                        CMPI_RC_ERR_NOT_FOUND,
+                        "Could not retrieve rule");
+                goto out;
+        }
+
+        *instance = convert_rule_to_instance(rule,
+                                        broker,
+                                        context,
+                                        reference,
+                                        &s);
+ out:
+        free(filter_name);
+        cleanup_filter(filter);
+        virConnectClose(conn);
+
+        return s;
+}
+
+CMPIStatus instance_from_rule(
+        const CMPIBroker *broker,
+        const CMPIContext *context,
+        const CMPIObjectPath *reference,
+        struct acl_rule *rule,
+        CMPIInstance **instance)
+{
+        CMPIStatus s = {CMPI_RC_OK, NULL};
+
+        *instance = convert_rule_to_instance(rule,
+                                        broker,
+                                        context,
+                                        reference,
+                                        &s);
+
+        return s;
+
+}
+
+static CMPIStatus GetInstance(
+        CMPIInstanceMI *self,
+        const CMPIContext *context,
+        const CMPIResult *results,
+        const CMPIObjectPath *reference,
+        const char **properties)
+{
+        CMPIStatus s = {CMPI_RC_OK, NULL};
+        CMPIInstance *instance = NULL;
+
+        s = get_rule_by_ref(_BROKER, context, reference, &instance);
+
+        if (instance != NULL)
+                CMReturnInstance(results, instance);
+
+        return s;
+}
+
+static CMPIStatus EnumInstanceNames(
+        CMPIInstanceMI *self,
+        const CMPIContext *context,
+        const CMPIResult *results,
+        const CMPIObjectPath *reference)
+{
+        CMPIStatus s = {CMPI_RC_OK, NULL};
+        struct inst_list list;
+
+        inst_list_init(&list);
+
+        s = enum_filter_rules(_BROKER, context, reference, &list);
+
+        cu_return_instance_names(results, &list);
+
+        inst_list_free(&list);
+
+        return s;
+}
+
+static CMPIStatus EnumInstances(
+        CMPIInstanceMI *self,
+        const CMPIContext *context,
+        const CMPIResult *results,
+        const CMPIObjectPath *reference,
+        const char **properties)
+{
+        CMPIStatus s = {CMPI_RC_OK, NULL};
+        struct inst_list list;
+
+        inst_list_init(&list);
+
+        s = enum_filter_rules(_BROKER, context, reference, &list);
+
+        cu_return_instances(results, &list);
+
+        return s;
+}
+
+DEFAULT_CI();
+DEFAULT_MI();
+DEFAULT_DI();
+DEFAULT_EQ();
+DEFAULT_INST_CLEANUP();
+
+STD_InstanceMIStub(,
+        Virt_FilterEntry,
+        _BROKER,
+        libvirt_cim_init());
+
+/*
+ * Local Variables:
+ * mode: C
+ * c-set-style: "K&R"
+ * tab-width: 8
+ * c-basic-offset: 8
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/src/Virt_FilterEntry.h b/src/Virt_FilterEntry.h
new file mode 100644
--- /dev/null
+++ b/src/Virt_FilterEntry.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright IBM Corp. 2011
+ *
+ * Authors:
+ *  Chip Vincent <cvincent at us.ibm.com>
+ *
+ * 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
+ */
+#ifndef __VIRT_FILTERENTRY_H
+#define __VIRT_FILTERENTRY_H
+
+/**
+ * Return a list of filter instances
+ *
+ * @param broker A pointer to the CIM broker
+ * @param context A pointer to an operation context
+ * @param reference Defines the libvirt connection to use (via class name
+ *             prefix), but can also be used to scope the results by
+ *             specifying the parent filter
+ * @param list A pointer to an array of CMPIInstance objects
+ *             (called inits before and frees after)
+ */
+CMPIStatus enum_filter_rules(
+                const CMPIBroker *broker,
+                const CMPIContext *context,
+                const CMPIObjectPath *reference,
+                struct inst_list *list);
+
+/**
+ * Return a single filter instance by reference
+ *
+ * @param broker A pointer to the CIM broker
+ * @param context A pointer to an operation context
+ * @param reference Defines the libvirt connection to use (via class name
+ *             prefix), but can also be used to scope the results by
+ *             specifying the parent filter
+ * @param list A pointer to a CMPIInstance *
+ */
+CMPIStatus get_rule_by_ref(
+                const CMPIBroker *broker,
+                const CMPIContext *context,
+                const CMPIObjectPath *reference,
+                CMPIInstance **instance);
+
+/**
+ * Get an instance representing a filter rule
+ *
+ * @param broker A pointer to the CIM broker
+ * @param context A pointer to an operation context
+ * @param reference Defines the libvirt connection to use (via class name
+ *             prefix), but can also be used to scope the results by
+ *             specifying the parent filter
+ * @param rule A pointer to a filter rule
+ * @param instance A pointer to a CMPIInstance *
+ */
+CMPIStatus instance_from_rule(
+                const CMPIBroker *broker,
+                const CMPIContext *context,
+                const CMPIObjectPath *reference,
+                struct acl_rule *rule,
+                CMPIInstance **instance);
+
+#endif
+
+/*
+ * Local Variables:
+ * mode: C
+ * c-set-style: "K&R"
+ * tab-width: 8
+ * c-basic-offset: 8
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/src/Virt_FilterList.c b/src/Virt_FilterList.c
new file mode 100644
--- /dev/null
+++ b/src/Virt_FilterList.c
@@ -0,0 +1,252 @@
+/*
+ * Copyright IBM Corp. 2011
+ *
+ * Authors:
+ *  Chip Vincent <cvincent at us.ibm.com>
+ *
+ * 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
+ */
+#include <cmpidt.h>
+#include <cmpift.h>
+#include <cmpimacs.h>
+
+#include <libcmpiutil/libcmpiutil.h>
+#include <libcmpiutil/std_instance.h>
+
+#include "acl_parsing.h"
+#include "misc_util.h"
+#include "xmlgen.h"
+
+#include "Virt_FilterList.h"
+#include "Virt_HostSystem.h"
+
+const static CMPIBroker *_BROKER;
+
+static CMPIInstance *convert_filter_to_instance(
+        struct acl_filter *filter,
+        const CMPIBroker *broker,
+        const CMPIContext *context,
+        const CMPIObjectPath *reference,
+        CMPIStatus *s)
+{
+        CMPIInstance *inst = NULL;
+        const char *sys_name = NULL;
+        const char *sys_ccname = NULL;
+        int direction = 0;
+
+        inst = get_typed_instance(broker,
+                                  CLASSNAME(reference),
+                                  "FilterList",
+                                  NAMESPACE(reference));
+        if (inst == NULL) {
+                cu_statusf(broker, s,
+                           CMPI_RC_ERR_FAILED,
+                           "Unable to get filter list instance");
+                goto out;
+        }
+
+        *s = get_host_system_properties(&sys_name,
+                                       &sys_ccname,
+                                       reference,
+                                       broker,
+                                       context);
+
+        if (s->rc != CMPI_RC_OK) {
+                cu_statusf(broker, s,
+                           CMPI_RC_ERR_FAILED,
+                           "Unable to get host attributes");
+                goto out;
+        }
+
+        CMSetProperty(inst, "SystemName", sys_name, CMPI_chars);
+        CMSetProperty(inst, "SystemCreationClassName", sys_ccname, CMPI_chars);
+        CMSetProperty(inst, "Name", (CMPIValue *)filter->name, CMPI_chars);
+        CMSetProperty(inst, "InstanceID", (CMPIValue *)filter->uuid,
+                        CMPI_chars);
+        CMSetProperty(inst, "Direction", (CMPIValue *)&direction, CMPI_uint16);
+
+ out:
+        return inst;
+}
+
+CMPIStatus enum_filter_lists(const CMPIBroker *broker,
+                        const CMPIContext *context,
+                        const CMPIObjectPath *reference,
+                        struct inst_list *list)
+{
+        virConnectPtr conn = NULL;
+        struct acl_filter *filters = NULL;
+        int i, count = 0;
+        CMPIStatus s = {CMPI_RC_OK, NULL};
+        CMPIInstance *instance = NULL;
+
+        conn = connect_by_classname(broker, CLASSNAME(reference), &s);
+        if (conn == NULL)
+                goto out;
+
+        count = get_filters(conn, &filters);
+
+        CU_DEBUG("found %d filters", count);
+
+        for (i = 0; i < count; i++) {
+                instance = convert_filter_to_instance(&filters[i],
+                                                broker,
+                                                context,
+                                                reference,
+                                                &s);
+
+                if (instance != NULL)
+                        inst_list_add(list, instance);
+        }
+
+ out:
+        cleanup_filters(&filters, count);
+        virConnectClose(conn);
+
+        return s;
+}
+
+CMPIStatus get_filter_by_ref(const CMPIBroker *broker,
+                        const CMPIContext *context,
+                        const CMPIObjectPath *reference,
+                        CMPIInstance **instance)
+{
+        virConnectPtr conn = NULL;
+        struct acl_filter *filter = NULL;
+
+        CMPIStatus s = {CMPI_RC_OK, NULL};
+        const char *name = NULL;
+
+        if (cu_get_str_path(reference, "Name", &name) != CMPI_RC_OK) {
+                cu_statusf(_BROKER, &s,
+                        CMPI_RC_ERR_NOT_FOUND,
+                        "Unable to get Name from reference");
+                goto out;
+        }
+
+        conn = connect_by_classname(broker, CLASSNAME(reference), &s);
+        if (conn == NULL)
+                goto out;
+
+        get_filter_by_name(conn, name, &filter);
+        if (filter == NULL) {
+                cu_statusf(_BROKER, &s,
+                        CMPI_RC_ERR_NOT_FOUND,
+                        "No such instance (Name)");
+                goto out;
+        }
+
+        s = instance_from_filter(broker, context, reference, filter, instance);
+
+ out:
+        cleanup_filter(filter);
+        virConnectClose(conn);
+
+        return s;
+}
+
+CMPIStatus instance_from_filter(const CMPIBroker *broker,
+                        const CMPIContext *context,
+                        const CMPIObjectPath *reference,
+                        struct acl_filter *filter,
+                        CMPIInstance **instance)
+{
+        CMPIStatus s = {CMPI_RC_OK, NULL};
+
+        *instance = convert_filter_to_instance(filter, broker, context,
+                                                reference, &s);
+
+        return s;
+}
+
+static CMPIStatus GetInstance(
+        CMPIInstanceMI *self,
+        const CMPIContext *context,
+        const CMPIResult *results,
+        const CMPIObjectPath *reference,
+        const char **properties)
+{
+        CMPIStatus s = {CMPI_RC_OK, NULL};
+        CMPIInstance *instance = NULL;
+
+        s = get_filter_by_ref(_BROKER, context, reference, &instance);
+
+        if (instance != NULL)
+                CMReturnInstance(results, instance);
+
+        return s;
+}
+
+static CMPIStatus EnumInstanceNames(
+        CMPIInstanceMI *self,
+        const CMPIContext *context,
+        const CMPIResult *results,
+        const CMPIObjectPath *reference)
+{
+        CMPIStatus s = {CMPI_RC_OK, NULL};
+        struct inst_list list;
+
+        inst_list_init(&list);
+
+        s = enum_filter_lists(_BROKER, context, reference, &list);
+
+        cu_return_instance_names(results, &list);
+
+        inst_list_free(&list);
+
+        return s;
+}
+
+static CMPIStatus EnumInstances(
+        CMPIInstanceMI *self,
+        const CMPIContext *context,
+        const CMPIResult *results,
+        const CMPIObjectPath *reference,
+        const char **properties)
+{
+        CMPIStatus s = {CMPI_RC_OK, NULL};
+        struct inst_list list;
+
+        inst_list_init(&list);
+
+        s = enum_filter_lists(_BROKER, context, reference, &list);
+
+        cu_return_instances(results, &list);
+
+        inst_list_free(&list);
+
+        return s;
+}
+
+DEFAULT_CI();
+DEFAULT_MI();
+DEFAULT_DI();
+DEFAULT_EQ();
+DEFAULT_INST_CLEANUP();
+
+STD_InstanceMIStub(,
+        Virt_FilterList,
+        _BROKER,
+        libvirt_cim_init());
+
+/*
+ * Local Variables:
+ * mode: C
+ * c-set-style: "K&R"
+ * tab-width: 8
+ * c-basic-offset: 8
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/src/Virt_FilterList.h b/src/Virt_FilterList.h
new file mode 100644
--- /dev/null
+++ b/src/Virt_FilterList.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright IBM Corp. 2011
+ *
+ * Authors:
+ *  Chip Vincent <cvincent at us.ibm.com>
+ *
+ * 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
+ */
+#ifndef __VIRT_FILTERLIST_H
+#define __VIRT_FILTERLIST_H
+
+#include "acl_parsing.h"
+
+/**
+ * Return a list of filter lists
+ *
+ * @param broker A pointer to the CIM broker
+ * @param context A pointer to an operation context
+ * @param reference Defines the libvirt connection to use (via class name
+ *             prefix), but can also be used to scope the results by
+ *             specifying a specific filter
+ * @param list A pointer to an array of CMPIInstance objects
+ *             (caller inits before and frees after)
+ */
+CMPIStatus enum_filter_lists(
+                const CMPIBroker *broker,
+                const CMPIContext *context,
+                const CMPIObjectPath *reference,
+                struct inst_list *list);
+
+/**
+ * Return a filter instance by reference
+ *
+ * @param broker A pointer to the CIM broker
+ * @param context A pointer to an operation context
+ * @param reference Defines the libvirt connection to use (via class name
+ *             prefix), but can also be used to scope the results by
+ *             specifying a specific filter
+ * @param instance A pointer to a CMPIInstance * to place the new instance
+ */
+CMPIStatus get_filter_by_ref(
+                const CMPIBroker *broker,
+                const CMPIContext *contest,
+                const CMPIObjectPath *reference,
+                CMPIInstance **instance);
+
+/**
+ * Return a list of filter lists
+ *
+ * @param broker A pointer to the CIM broker
+ * @param context A pointer to an operation context
+ * @param reference Defines the libvirt connection to use (via class name
+ *             prefix), but can also be used to scope the results by
+ *             specifying a specific filter
+ * @param filter A pointer to a acl_filter
+ * @param instance A pointer to a CMPIInstance * to place the new instance
+ */
+CMPIStatus instance_from_filter(
+                const CMPIBroker *broker,
+                const CMPIContext *context,
+                const CMPIObjectPath *reference,
+                struct acl_filter *filter,
+                CMPIInstance **instance);
+
+#endif
+
+/*
+ * Local Variables:
+ * mode: C
+ * c-set-style: "K&R"
+ * tab-width: 8
+ * c-basic-offset: 8
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/src/Virt_HostedFilterList.c b/src/Virt_HostedFilterList.c
new file mode 100644
--- /dev/null
+++ b/src/Virt_HostedFilterList.c
@@ -0,0 +1,144 @@
+/*
+ * Copyright IBM Corp. 2011
+ *
+ * Authors:
+ *  Chip Vincent <cvincent at us.ibm.com>
+ *
+ * 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
+ */
+#include <cmpidt.h>
+#include <cmpift.h>
+#include <cmpimacs.h>
+
+#include <libcmpiutil/libcmpiutil.h>
+#include <libcmpiutil/std_association.h>
+
+#include "misc_util.h"
+#include "Virt_HostSystem.h"
+#include "Virt_FilterList.h"
+
+static const CMPIBroker *_BROKER;
+
+static CMPIStatus host_to_list(
+        const CMPIObjectPath *reference,
+        struct std_assoc_info *info,
+        struct inst_list *list)
+{
+        CMPIStatus s = {CMPI_RC_OK, NULL};
+        CMPIInstance *instance = NULL;
+
+        /* validate host reference */
+        s = get_host(_BROKER, info->context, reference, &instance, false);
+        if (s.rc != CMPI_RC_OK)
+                goto out;
+
+        s = cu_validate_ref(_BROKER, reference, instance);
+        if (s.rc != CMPI_RC_OK)
+                goto out;
+
+        s = enum_filter_lists(_BROKER, info->context, reference, list);
+
+ out:
+        return s;
+}
+
+static CMPIStatus list_to_host(
+        const CMPIObjectPath *reference,
+        struct std_assoc_info *info,
+        struct inst_list *list)
+{
+        CMPIStatus s = {CMPI_RC_OK, NULL};
+        CMPIInstance *instance = NULL;
+
+        /* validate filter reference */
+        s = get_filter_by_ref(_BROKER, info->context, reference, &instance);
+        if (s.rc != CMPI_RC_OK)
+                goto out;
+
+        s = get_host(_BROKER, info->context, reference, &instance, false);
+        if (s.rc != CMPI_RC_OK)
+                goto out;
+
+        if (instance != NULL)
+                inst_list_add(list, instance);
+
+ out:
+        return s;
+}
+
+LIBVIRT_CIM_DEFAULT_MAKEREF()
+
+static char *antecedent[] = {
+        "KVM_HostSystem",
+        NULL
+};
+
+static char *dependent[] = {
+        "KVM_FilterList",
+        NULL
+};
+
+static char *assoc_class_name[] = {
+        "KVM_HostedFilterList",
+        NULL
+};
+
+static struct std_assoc _host_to_list = {
+        .source_class = (char **)&antecedent,
+        .source_prop = "Antecedent",
+
+        .target_class = (char **)&dependent,
+        .target_prop = "Dependent",
+
+        .assoc_class = (char **)&assoc_class_name,
+
+        .handler = host_to_list,
+        .make_ref = make_ref
+};
+
+static struct std_assoc _list_to_host = {
+        .source_class = (char **)&dependent,
+        .source_prop = "Dependent",
+
+        .target_class = (char **)&antecedent,
+        .target_prop = "Antecedent",
+
+        .assoc_class = (char **)&assoc_class_name,
+
+        .handler = list_to_host,
+        .make_ref = make_ref
+};
+
+static struct std_assoc *handlers[] = {
+        &_host_to_list,
+        &_list_to_host,
+        NULL
+};
+
+STDA_AssocMIStub(,
+        Virt_HostedFilterList,
+        _BROKER,
+        libvirt_cim_init(),
+        handlers);
+
+/*
+ * Local Variables:
+ * mode: C
+ * c-set-style: "K&R"
+ * tab-width: 8
+ * c-basic-offset: 8
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/src/Virt_NestedFilterList.c b/src/Virt_NestedFilterList.c
new file mode 100644
--- /dev/null
+++ b/src/Virt_NestedFilterList.c
@@ -0,0 +1,235 @@
+/*
+ * Copyright IBM Corp. 2011
+ *
+ * Authors:
+ *  Chip Vincent <cvincent at us.ibm.com>
+ *
+ * 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
+ */
+#include <cmpidt.h>
+#include <cmpift.h>
+#include <cmpimacs.h>
+
+#include <libcmpiutil/libcmpiutil.h>
+#include <libcmpiutil/std_association.h>
+#include <libcmpiutil/std_instance.h>
+
+/* FIXME: This seems to be needed to compile STREQC, which suggests
+ * libcmpiutil.h needs to add the include since the marco is defined there.
+ */
+#include <string.h>
+#include <strings.h>
+
+#include "acl_parsing.h"
+#include "misc_util.h"
+#include "Virt_FilterList.h"
+
+static const CMPIBroker *_BROKER;
+
+/**
+ *  given a filter, find all *direct* filter_refs
+ */
+static CMPIStatus parent_to_child(
+        const CMPIObjectPath *reference,
+        struct std_assoc_info *info,
+        struct inst_list *list)
+{
+        CMPIStatus s = {CMPI_RC_OK, NULL};
+        struct acl_filter *parent_filter = NULL;
+        struct acl_filter *child_filter = NULL;
+        CMPIInstance *instance = NULL;
+        const char * name = NULL;
+        virConnectPtr conn = NULL;
+        int i;
+
+        CU_DEBUG("Reference = %s", REF2STR(reference));
+
+        if (cu_get_str_path(reference, "Name", &name) != CMPI_RC_OK) {
+                cu_statusf(_BROKER, &s,
+                        CMPI_RC_ERR_NOT_FOUND,
+                        "Unable to get Name from reference");
+                goto out;
+        }
+
+        conn = connect_by_classname(_BROKER, CLASSNAME(reference), &s);
+        if (conn == NULL)
+                goto out;
+
+        get_filter_by_name(conn, name, &parent_filter);
+        if (parent_filter == NULL)
+                goto out;
+
+        for (i = 0; i < parent_filter->ref_ct; i++) {
+                get_filter_by_name(conn, parent_filter->refs[i],
+                                        &child_filter);
+                if (child_filter == NULL)
+                        continue;
+
+                CU_DEBUG("Processing %s,", child_filter->name);
+
+                s = instance_from_filter(_BROKER,
+                                        info->context,
+                                        reference,
+                                        child_filter,
+                                        &instance);
+
+                if (instance != NULL) {
+                        CU_DEBUG("Adding instance to inst_list");
+                        inst_list_add(list, instance);
+                }
+
+                cleanup_filter(child_filter);
+
+                child_filter = NULL;
+                instance = NULL;
+        }
+
+        cleanup_filter(parent_filter);
+
+ out:
+        virConnectClose(conn);
+
+        return s;
+}
+
+/**
+ *  given a filter, find all the other filters that reference it
+ */
+static CMPIStatus child_to_parent(
+        const CMPIObjectPath *reference,
+        struct std_assoc_info *info,
+        struct inst_list *list)
+{
+        CMPIStatus s = {CMPI_RC_OK, NULL};
+        struct acl_filter *_list = NULL;
+        CMPIInstance *instance = NULL;
+        const char *name = NULL;
+        virConnectPtr conn = NULL;
+        int count, i, j;
+
+        CU_DEBUG("Reference = %s", REF2STR(reference));
+
+        if (cu_get_str_path(reference, "Name", &name) != CMPI_RC_OK) {
+                cu_statusf(_BROKER, &s,
+                        CMPI_RC_ERR_NOT_FOUND,
+                        "Unable to get Name from reference");
+                goto out;
+        }
+
+        conn = connect_by_classname(_BROKER, CLASSNAME(reference), &s);
+        if (conn == NULL)
+                goto out;
+
+        /* TODO: Ensure the referenced filter exists */
+
+        count = get_filters(conn, &_list);
+        if (_list == NULL)
+                goto out;
+
+        /* return any filter that has name in refs */
+        for (i = 0; i < count; i++) {
+                for (j = 0; j < _list[i].ref_ct; j++) {
+                        if (STREQC(name, _list[i].refs[j])) {
+                                CU_DEBUG("Processing %s,", _list[i].name);
+
+                                s = instance_from_filter(_BROKER,
+                                                        info->context,
+                                                        reference,
+                                                        &_list[i],
+                                                        &instance);
+
+                                if (instance != NULL)
+                                        inst_list_add(list, instance);
+
+                                instance = NULL;
+                        }
+
+                }
+
+                cleanup_filter(&_list[i]);
+        }
+
+        free(_list);
+
+ out:
+        virConnectClose(conn);
+
+        return s;
+}
+
+LIBVIRT_CIM_DEFAULT_MAKEREF()
+
+static char *antecedent[] = {
+        "KVM_FilterList",
+        NULL
+};
+
+static char *dependent[] = {
+        "KVM_FilterList",
+        NULL
+};
+
+static char *assoc_class_name[] = {
+        "KVM_NestedFilterList",
+        NULL
+};
+
+static struct std_assoc _list_to_filter_ref = {
+        .source_class = (char **)&antecedent,
+        .source_prop = "Antecedent",
+
+        .target_class = (char **)&dependent,
+        .target_prop = "Dependent",
+
+        .assoc_class = (char **)&assoc_class_name,
+
+        .handler = parent_to_child,
+        .make_ref = make_ref
+};
+
+static struct std_assoc _filter_ref_to_list = {
+        .source_class = (char **)&dependent,
+        .source_prop = "Dependent",
+
+        .target_class = (char **)&antecedent,
+        .target_prop = "Antecedent",
+
+        .assoc_class = (char **)&assoc_class_name,
+
+        .handler = child_to_parent,
+        .make_ref = make_ref
+};
+
+static struct std_assoc *handlers[] = {
+        &_list_to_filter_ref,
+        &_filter_ref_to_list,
+        NULL
+};
+
+STDA_AssocMIStub(,
+        Virt_NestedFilterList,
+        _BROKER,
+        libvirt_cim_init(),
+        handlers);
+
+/*
+ * Local Variables:
+ * mode: C
+ * c-set-style: "K&R"
+ * tab-width: 8
+ * c-basic-offset: 8
+ * indent-tabs-mode: nil
+ * End:
+ */




More information about the Libvirt-cim mailing list