[ovs-discuss] [ACL 2/3] vswitchd: Implement local ACL functionality.

Jesse Gross jesse at nicira.com
Mon Aug 3 18:21:34 UTC 2009


Allows ACL's to be locally configured and applied when the switch is
not connected to a controller.  ACL's may be added to the configuration
file and will applied to specified switch ports.  Ingress rules generate
OpenFlow entries, while egress rules utilize a new filter to remove output
ports after normal processing has taken place.
---
 lib/acl.c            |  629 ++++++++++++++++++++++++++++++++++++++++++++++++++
 lib/acl.h            |   36 +++
 lib/automake.mk      |    2 +
 lib/vlog-modules.def |    1 +
 vswitchd/bridge.c    |   41 +++-
 5 files changed, 703 insertions(+), 6 deletions(-)
 create mode 100644 lib/acl.c
 create mode 100644 lib/acl.h

diff --git a/lib/acl.c b/lib/acl.c
new file mode 100644
index 0000000..bc204f3
--- /dev/null
+++ b/lib/acl.c
@@ -0,0 +1,629 @@
+/*
+ * Copyright (c) 2009 Nicira Networks.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <config.h>
+#include "acl.h"
+
+#include <arpa/inet.h>
+#include <ctype.h>
+#include <fnmatch.h>
+#include <netinet/in.h>
+
+#include "cfg.h"
+#include "classifier.h"
+#include "flow.h"
+#include "hmap.h"
+#include "packets.h"
+#include "shash.h"
+#include "svec.h"
+#include "util.h"
+#include "vlog.h"
+
+#define THIS_MODULE VLM_acl
+
+struct acl {
+    const char *port_name;      /* Name of port */
+    struct ofproto *ofproto;    /* Bridge OpenFlow switch */
+    uint16_t cur_ofp_port;      /* The port in rules are installed on */
+    struct classifier out_rules; /* Lookup table for applying egress ACL's */
+    bool has_in_rules;          /* Have in rules been applied to this port? */
+    bool has_out_rules;         /* Have out rules been applied to this port? */
+};
+
+struct acl_rule {
+    flow_t flow;                /* Flow entry for this rule */
+    uint32_t wildcards;         /* Wildcards for this rule */
+    unsigned int priority;      /* Rule priority */
+    struct cls_rule cr;         /* Classifier rule for out ACL's */
+    bool permit;                /* Allow further processing or drop packets */
+};
+
+struct acl_group {
+    int size;                   /* Size of group */
+    struct acl_rule **rules;    /* Rules in group */
+};
+
+/* Whether ACL's are currently enabled. */
+static bool acls_active = true;
+
+/* Mapping of group name to struct acl_group *. */
+static struct shash acls = SHASH_INITIALIZER(&acls);
+
+/* Old configuration, used to check if we need to update. */
+static struct svec old_cfg = SVEC_EMPTY_INITIALIZER;
+
+/* The action to be taken if a packet is permitted. */
+static union ofp_action permit_action;
+
+static void install_rules(const char *group_name, uint16_t ofp_port,
+                          bool in_rule, struct acl *info);
+static void update_rules(void);
+static bool parse_acl(const char *acl, unsigned int priority,
+                          struct acl_rule **rule);
+static bool extract_ip_mask(const char *mask, int wildcard_shift,
+                            int of_wildcard_mask, uint32_t *address,
+                            uint32_t *wildcards);
+static bool extract_port(const char *str, int wildcard_val, uint16_t *port,
+                         uint32_t *wildcards);
+static void add_default_acl(struct acl_rule **rule);
+
+void
+acl_init(void)
+{
+    memset(&permit_action, 0, sizeof permit_action);
+    permit_action.type = htons(OFPAT_OUTPUT);
+    permit_action.output.len = htons(sizeof permit_action);
+    permit_action.output.port = htons(OFPP_NORMAL);
+
+    acl_reconfigure();
+}
+
+void
+acl_reconfigure(void)
+{
+    struct svec new_cfg = SVEC_EMPTY_INITIALIZER;
+    struct shash_node *old_node;
+    struct acl_group *old_rule_set;
+    int i;
+
+    /* ACL's are switch local, so disable if a controller is configured. */
+    if (cfg_has_section("acl")) {
+        if (cfg_has("mgmt.controller")) {
+            if (acls_active) {
+                VLOG_WARN("CONTROLLER CONFIGURED, DISABLING ACL'S");
+                acls_active = false;
+            }
+        } else {
+            acls_active = true;
+        }
+    } else {
+        acls_active = false;
+    }
+
+    /* Only reconfigure if something actually changed. */
+    if (acls_active) {
+        cfg_get_section(&new_cfg, "acl");
+        if (svec_equal(&old_cfg, &new_cfg)) {
+            svec_destroy(&new_cfg);
+            return;
+        } else {
+            svec_swap(&old_cfg, &new_cfg);
+            svec_destroy(&new_cfg);
+        }
+    }
+
+    /* Remove old entries. */
+    HMAP_FOR_EACH(old_node, struct shash_node, node, &acls.map) {
+        old_rule_set = old_node->data;
+        for (i = 0; i < old_rule_set->size; i++) {
+            free(old_rule_set->rules[i]);
+        }
+        free(old_rule_set);
+    }
+    shash_clear(&acls);
+
+    if (!acls_active) {
+        return;
+    }
+
+    /* Add new entries. */
+    update_rules();
+}
+
+bool
+acl_is_active(void)
+{
+    return acls_active;
+}
+
+void
+acl_iface_init(const char *port_name, struct ofproto *ofproto,
+               struct acl **info)
+{
+    *info = xmalloc(sizeof **info);
+    (*info)->port_name = port_name;
+    (*info)->ofproto = ofproto;
+    classifier_init(&(*info)->out_rules);
+    (*info)->has_in_rules = false;
+    (*info)->has_out_rules = false;
+}
+
+void
+acl_iface_reconfigure(struct acl *info, uint16_t ofp_port)
+{
+    flow_t flow;
+    uint32_t wildcards;
+    const char *acl_group_name;
+    struct svec priorities = SVEC_EMPTY_INITIALIZER;
+    int i;
+    const char *glob;
+
+    memset(&flow, 0, sizeof flow);
+    flow.in_port = ofp_port;
+    wildcards = OFPFW_ALL & ~OFPFW_IN_PORT;
+
+    /* Clear out old flows associated with ACL's.  Since ACL's are only active
+     * when there is no controller, only our flows should be in here. */
+    if (info->has_in_rules) {
+        ofproto_delete_flows_wildcarded(info->ofproto, &flow, wildcards);
+        info->has_in_rules = false;
+    }
+
+    if (info->has_out_rules) {
+        classifier_destroy(&info->out_rules);
+        classifier_init(&info->out_rules);
+        info->has_out_rules = false;
+    }
+
+    if (!acls_active) {
+        return;
+    }
+
+    /* Add flows for ingress rules. */
+    if (!info->has_in_rules) {
+        acl_group_name = cfg_get_string(0, "acl.port.%s.in", info->port_name);
+        if (acl_group_name) {
+            install_rules(acl_group_name, ofp_port, true, info);
+        }
+    }
+
+    /* Add to classifier for egress rules. */
+    if (!info->has_out_rules) {
+        acl_group_name = cfg_get_string(0, "acl.port.%s.out", info->port_name);
+        if (acl_group_name) {
+            install_rules(acl_group_name, ofp_port, false, info);
+        }
+    }
+
+    /* Wildcarded rules. */
+    if (!info->has_in_rules || !info->has_out_rules) {
+        cfg_get_subsections(&priorities, "acl.default");
+        svec_sort(&priorities);
+
+        for (i = 0; i < priorities.n; i++) {
+            glob = cfg_get_string(0, "acl.default.%s.match",
+                                  priorities.names[i]);
+            if (glob) {
+                if (fnmatch(glob, info->port_name, 0) == 0) {
+                    if (!info->has_in_rules) {
+                        acl_group_name = cfg_get_string(0,
+                                                        "acl.default.%s.in",
+                                                        priorities.names[i]);
+                        if (acl_group_name) {
+                            install_rules(acl_group_name, ofp_port, true, info);
+                        }
+                    }
+                    if (!info->has_out_rules) {
+                        acl_group_name = cfg_get_string(0,
+                                                        "acl.default.%s.out",
+                                                        priorities.names[i]);
+                        if (acl_group_name) {
+                            install_rules(acl_group_name, ofp_port, false,
+                                          info);
+                        }
+                    }
+                    if (info->has_in_rules && info->has_out_rules) {
+                        break;
+                    }
+                }
+            } else {
+                VLOG_WARN("invalid glob when processing default acl's,"
+                          " skipping");
+            }
+        }
+        svec_destroy(&priorities);
+    }
+
+    /* No match, install a default ingress flow. */
+    if (!info->has_in_rules) {
+        ofproto_add_flow(info->ofproto, &flow, wildcards, 0, &permit_action,
+                         1, 0);
+        info->has_in_rules = true;
+    }
+
+    info->cur_ofp_port = ofp_port;
+}
+
+static void
+install_rules(const char *group_name, uint16_t ofp_port, bool in_rule,
+              struct acl *info)
+{
+    struct acl_group *access_rules;
+    int i;
+    flow_t flow;
+    uint32_t wildcards;
+
+    access_rules = shash_find_data(&acls, group_name);
+    if (access_rules) {
+        for (i = 0; i < access_rules->size; i++) {
+            if (access_rules->rules[i]) {
+                if (in_rule) {
+                    flow = access_rules->rules[i]->flow;
+                    wildcards = access_rules->rules[i]->wildcards;
+
+                    flow.in_port = ofp_port;
+                    wildcards &= ~OFPFW_IN_PORT;
+
+                    ofproto_add_flow(info->ofproto, &flow, wildcards,
+                                     access_rules->rules[i]->priority,
+                                     access_rules->rules[i]->permit ?
+                                     &permit_action : NULL,
+                                     access_rules->rules[i]->permit ? 1 : 0, 0);
+
+                    info->has_in_rules = true;
+                } else {
+                    classifier_insert(&info->out_rules,
+                                      &access_rules->rules[i]->cr);
+                    info->has_out_rules = true;
+                }
+            }
+        }
+    } else {
+        VLOG_WARN("acl group '%s' not found", group_name);
+    }
+}
+
+void
+acl_iface_destroy(struct acl *info)
+{
+    flow_t flow;
+
+    if (info->has_in_rules) {
+        memset(&flow, 0, sizeof flow);
+        flow.in_port = info->cur_ofp_port;
+        ofproto_delete_flows_wildcarded(info->ofproto, &flow,
+                                        OFPFW_ALL & ~OFPFW_IN_PORT);
+    }
+    classifier_destroy(&info->out_rules);
+    free(info);
+}
+
+bool
+acl_iface_output_filter(struct acl *info, const flow_t *flow) {
+
+    struct cls_rule *cls_rule;
+    struct acl_rule *rule;
+
+    if (!info->has_out_rules) {
+        return true;
+    }
+
+    cls_rule = classifier_lookup(&info->out_rules, flow);
+    assert(cls_rule);   /* All rule sets should end with a wildcarded entry. */
+
+    rule = CONTAINER_OF(cls_rule, struct acl_rule, cr);
+
+    return rule->permit;
+}
+
+static void
+update_rules(void)
+{
+    struct svec groups = SVEC_EMPTY_INITIALIZER;
+    struct svec priorities = SVEC_EMPTY_INITIALIZER;
+    int i, j;
+    const char *acl;
+    struct acl_group *rule_set;
+    bool final_rule;
+
+    /* Create parsed flow entries for each ACL. */
+    cfg_get_subsections(&groups, "acl.group");
+
+    for (i = 0; i < groups.n; i++) {
+        svec_clear(&priorities);
+
+        cfg_get_subsections(&priorities, "acl.group.%s", groups.names[i]);
+        svec_sort(&priorities);
+
+        rule_set = xmalloc(sizeof *rule_set);
+        rule_set->size = priorities.n + 1;
+        rule_set->rules = xcalloc(rule_set->size, sizeof *rule_set->rules);
+
+        for (j = 0; j < priorities.n; j++) {
+            acl = cfg_get_string(0, "acl.group.%s.%s", groups.names[i],
+                                 priorities.names[j]);
+            if (acl) {
+                final_rule = parse_acl(acl, priorities.n - j,
+                                           &rule_set->rules[priorities.n - j]);
+
+                if (final_rule) {
+                    break;
+                }
+            } else {
+                VLOG_WARN("invalid rule when processing acl's, skipping");
+            }
+        }
+
+        if (!final_rule) {
+            add_default_acl(&rule_set->rules[0]);
+        }
+
+        shash_add(&acls, groups.names[i], rule_set);
+    }
+
+    svec_destroy(&priorities);
+    svec_destroy(&groups);
+}
+
+/* Returns true if this is a completely wildcarded rule. */
+static bool
+parse_acl(const char *acl_, unsigned int priority, struct acl_rule **rule)
+{
+    char *acl;
+    char *c;
+    int num_args;
+    char action[7];
+    char proto_str[5];
+    char srcip_str[19];
+    char dstip_str[19];
+    char srcport_str[6];
+    char dstport_str[6];
+    flow_t *flow;
+    uint32_t *wildcards;
+
+    *rule = xcalloc(1, sizeof **rule);
+    flow = &(*rule)->flow;
+    wildcards = &(*rule)->wildcards;
+    (*rule)->priority = priority;
+    *wildcards = OFPFW_ALL; /* Wildcard all fields unless overridden. */
+
+    /* ACL's are case insensitive. */
+    acl = xstrdup(acl_);
+    c = acl;
+    while (*c) {
+        *c = tolower(*c);
+        c++;
+    }
+
+    num_args = sscanf(acl, "%6s %4s %18s %18s %5s %5s", action, proto_str,
+                      srcip_str, dstip_str, srcport_str, dstport_str);
+
+    if (!num_args || num_args == EOF) {
+        VLOG_WARN("unparsable string when processing acl, skipping; "
+                  "bad rule: %s", acl);
+        goto error;
+    }
+
+    /* Rule action */
+
+    if (strcmp(action, "permit") == 0) {
+        (*rule)->permit = true;
+    } else if (strcmp(action, "deny") == 0) {
+        (*rule)->permit = false;
+    } else {
+        VLOG_WARN("unknown action when processing acl, skipping; "
+                  "bad rule: %s", acl);
+        goto error;
+    }
+    num_args--;
+    if (!num_args) {
+        goto finished;
+    }
+
+    /* Protocol type */
+
+    if (strcmp(proto_str, "any") == 0) {
+        if (num_args > 1) {
+            VLOG_WARN("protocol 'any' specified, skipping remainder; "
+                      "rule: %s", acl);
+        }
+        goto finished;
+    } else if (strcmp(proto_str, "arp") == 0) {
+        flow->dl_type = htons(ETH_TYPE_ARP);
+        *wildcards &= ~OFPFW_DL_TYPE;
+        if (num_args > 1) {
+            VLOG_WARN("protocol 'arp' specified, skipping remainder; "
+                      "rule: %s", acl);
+        }
+        goto finished;
+    }
+
+    /* All the following protocols are IP-based. */
+    flow->dl_type = htons(ETH_TYPE_IP);
+    *wildcards &= ~OFPFW_DL_TYPE;
+
+    if (strcmp(proto_str, "tcp") == 0) {
+        flow->nw_proto = IP_TYPE_TCP;
+        *wildcards &= ~OFPFW_NW_PROTO;
+    } else if (strcmp(proto_str, "udp") == 0) {
+        flow->nw_proto = IP_TYPE_UDP;
+        *wildcards &= ~OFPFW_NW_PROTO;
+    } else if (strcmp(proto_str, "icmp") == 0) {
+        flow->nw_proto = IP_TYPE_ICMP;
+        *wildcards &= ~OFPFW_NW_PROTO;
+    } else if (strcmp(proto_str, "ip") == 0) {
+        if (num_args > 1) {
+            VLOG_WARN("protocol 'ip' specified, skipping remainder; "
+                      "rule: %s", acl);
+        }
+        goto finished;
+    } else {
+        VLOG_WARN("unknown protocol when processing acl, skipping; "
+                  "bad rule: %s", acl);
+        goto error;
+    }
+    num_args--;
+    if (!num_args) {
+        goto finished;
+    }
+
+    /* IP addresses */
+
+    if (!extract_ip_mask(srcip_str, OFPFW_NW_SRC_SHIFT, OFPFW_NW_SRC_MASK,
+                         &flow->nw_src, wildcards)) {
+        goto error;
+    }
+    num_args--;
+    if (!num_args) {
+        goto finished;
+    }
+
+    if (!extract_ip_mask(dstip_str, OFPFW_NW_DST_SHIFT, OFPFW_NW_DST_MASK,
+                         &flow->nw_dst, wildcards)) {
+        goto error;
+    }
+    num_args--;
+    if (!num_args) {
+        goto finished;
+    }
+
+    /* Ports */
+
+    if (flow->nw_proto == 0) {
+        VLOG_WARN("ip protocol specified, skipping port specifications; "
+                  "rule: %s", acl);
+        goto finished;
+    }
+
+    if (!extract_port(srcport_str, OFPFW_TP_SRC, &flow->tp_src, wildcards)) {
+        goto error;
+    }
+    num_args--;
+    if (!num_args) {
+        goto finished;
+    }
+
+    if (!extract_port(dstport_str, OFPFW_TP_DST, &flow->tp_dst, wildcards)) {
+        goto error;
+    }
+    num_args--;
+    if (!num_args) {
+        goto finished;
+    }
+
+error:
+    free(*rule);
+    *rule = NULL;
+
+finished:
+    free(acl);
+
+    if (*rule) {
+        cls_rule_from_flow(&(*rule)->cr, flow, *wildcards, priority);
+        return (*wildcards == OFPFW_ALL);
+    } else {
+        return false;
+    }
+}
+
+/* Returns true if the string parsed correctly. */
+static bool
+extract_ip_mask(const char *mask, int wildcard_shift, int of_wildcard_mask,
+                uint32_t *address, uint32_t *wildcards)
+{
+    char *separator;
+    bool ret = true;
+    char *ip;
+    char *cidr;
+    struct in_addr addr;
+    int wildcard_mask;
+
+    if (strcmp(mask, "any") == 0) {
+        return true;
+    }
+
+    ip = xstrdup(mask);
+
+    separator = strchr(ip, '/');
+    if (separator) {
+        *separator = '\0';
+    }
+
+    if (inet_aton(ip, &addr)) {
+        *address = addr.s_addr;
+    } else {
+        VLOG_WARN("invalid address when processing acl, skipping rule; "
+                  "bad address: %s", mask);
+        ret = false;
+    }
+
+    /* Clear the existing wildcard bits. */
+    *wildcards &= ~of_wildcard_mask;
+
+    if (separator && *(separator + 1)) {
+        cidr = separator + 1;
+        wildcard_mask = atoi(cidr);
+        if (wildcard_mask) {
+            /* Wildcards are opposite of CIDR. */
+            wildcard_mask = 32 - wildcard_mask;
+            *wildcards |= wildcard_mask << wildcard_shift;
+        } else {
+            VLOG_WARN("invalid netmask when processing acl, skipping rule; "
+                      "bad address: %s", mask);
+            ret = false;
+        }
+    }
+
+    free(ip);
+    return ret;
+}
+
+/* Returns true if the string parsed correctly. */
+static bool
+extract_port(const char *str, int wildcard_val, uint16_t *port,
+             uint32_t *wildcards)
+{
+    unsigned short int port_val;
+
+    if (strcmp(str, "any") != 0) {
+        port_val = atoi(str);
+        if (port_val > 0 && port_val <= USHRT_MAX) {
+            *port = htons(port_val);
+            *wildcards &= ~wildcard_val;
+        } else {
+            VLOG_WARN("invalid port specified, skipping rule; port: %s", str);
+            return false;
+        }
+    }
+
+    return true;
+}
+
+static void
+add_default_acl(struct acl_rule **rule)
+{
+    struct acl_rule *new_rule;
+
+    /* Create a lowest priority default rule that denies all traffic. */
+    new_rule = *rule = xcalloc(1, sizeof **rule);
+
+    new_rule->permit = false;
+    new_rule->priority = 0;
+    new_rule->wildcards = OFPFW_ALL;
+
+    cls_rule_from_flow(&new_rule->cr, &new_rule->flow, new_rule->wildcards,
+                       new_rule->priority);
+}
diff --git a/lib/acl.h b/lib/acl.h
new file mode 100644
index 0000000..220d87f
--- /dev/null
+++ b/lib/acl.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2009 Nicira Networks.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ACL_H
+#define ACL_H 1
+
+#include "ofproto/ofproto.h"
+
+struct acl;
+
+void acl_init(void);
+void acl_reconfigure(void);
+
+bool acl_is_active(void);
+
+void acl_iface_init(const char *port_name, struct ofproto *ofproto,
+                   struct acl **info);
+void acl_iface_reconfigure(struct acl *info, uint16_t ofp_port);
+void acl_iface_destroy(struct acl *info);
+
+bool acl_iface_output_filter(struct acl *info, const flow_t *flow);
+
+#endif /* acl.h */
diff --git a/lib/automake.mk b/lib/automake.mk
index b0d10fd..c7a7b70 100644
--- a/lib/automake.mk
+++ b/lib/automake.mk
@@ -8,6 +8,8 @@
 noinst_LIBRARIES += lib/libopenvswitch.a
 
 lib_libopenvswitch_a_SOURCES = \
+	lib/acl.c \
+	lib/acl.h \
 	lib/backtrace.c \
 	lib/backtrace.h \
 	lib/bitmap.c \
diff --git a/lib/vlog-modules.def b/lib/vlog-modules.def
index 849c867..7a8ced5 100644
--- a/lib/vlog-modules.def
+++ b/lib/vlog-modules.def
@@ -15,6 +15,7 @@
  */
 
 /* Modules that can emit log messages. */
+VLOG_MODULE(acl)
 VLOG_MODULE(backtrace)
 VLOG_MODULE(brcompatd)
 VLOG_MODULE(bridge)
diff --git a/vswitchd/bridge.c b/vswitchd/bridge.c
index 6b05d13..e89b468 100644
--- a/vswitchd/bridge.c
+++ b/vswitchd/bridge.c
@@ -29,6 +29,7 @@
 #include <sys/socket.h>
 #include <sys/types.h>
 #include <unistd.h>
+#include "acl.h"
 #include "bitmap.h"
 #include "cfg.h"
 #include "coverage.h"
@@ -77,6 +78,7 @@ struct iface {
     char *name;                 /* Host network device name. */
     tag_type tag;               /* Tag associated with this interface. */
     long long delay_expires;    /* Time after which 'enabled' may change. */
+    struct acl *acl;            /* ACL state information for this interface. */
 
     /* These members are valid only after bridge_reconfigure() causes them to
      * be initialized.*/
@@ -310,6 +312,7 @@ bridge_init(void)
         }
     }
 
+    acl_init();
     bond_init();
     bridge_reconfigure();
 }
@@ -440,7 +443,7 @@ bridge_reconfigure(void)
 {
     struct svec old_br, new_br;
     struct bridge *br, *next;
-    size_t i;
+    size_t i,j;
 
     COVERAGE_INC(bridge_reconfigure);
 
@@ -476,6 +479,8 @@ bridge_reconfigure(void)
     bridge_configure_ssl();
 #endif
 
+    acl_reconfigure();
+
     /* Reconfigure all bridges. */
     LIST_FOR_EACH (br, struct bridge, node, &all_bridges) {
         bridge_reconfigure_one(br);
@@ -624,6 +629,18 @@ bridge_reconfigure(void)
         brstp_reconfigure(br);
         iterate_and_prune_ifaces(br, set_iface_policing, NULL);
     }
+    LIST_FOR_EACH (br, struct bridge, node, &all_bridges) {
+        for (i = 0; i < br->n_ports; i++) {
+            struct port *port = br->ports[i];
+            if (!port->is_mirror_output_port) {
+                for (j = 0; j < port->n_ifaces; j++) {
+                    struct iface *iface = port->ifaces[j];
+                    acl_iface_reconfigure(iface->acl,
+                                         odp_port_to_ofp_port(iface->dp_ifidx));
+                }
+            }
+        }
+    }
 }
 
 static void
@@ -1289,8 +1306,13 @@ bridge_reconfigure_controller(struct bridge *br)
         action.output.len = htons(sizeof action);
         action.output.port = htons(OFPP_NORMAL);
         memset(&flow, 0, sizeof flow);
-        ofproto_add_flow(br->ofproto, &flow, OFPFW_ALL, 0,
-                         &action, 1, 0);
+
+        if (!acl_is_active()) {
+            ofproto_add_flow(br->ofproto, &flow, OFPFW_ALL, 0,
+                             &action, 1, 0);
+        } else {
+            ofproto_delete_flow(br->ofproto, &flow, OFPFW_ALL, 0);
+        }
 
         ofproto_set_in_band(br->ofproto, false);
         ofproto_set_max_backoff(br->ofproto, 1);
@@ -1652,18 +1674,22 @@ compose_dsts(const struct bridge *br, const flow_t *flow, uint16_t vlan,
 
     *tags |= in_port->stp_state_tag;
     if (out_port == FLOOD_PORT) {
-        /* XXX use ODP_FLOOD if no vlans or bonding. */
+        /* XXX use ODP_FLOOD if no vlans, bonding, or ACL's. */
         /* XXX even better, define each VLAN as a datapath port group */
         for (i = 0; i < br->n_ports; i++) {
             struct port *port = br->ports[i];
             if (port != in_port && port_includes_vlan(port, vlan)
                 && !port->is_mirror_output_port
-                && set_dst(dst, flow, in_port, port, tags)) {
+                && set_dst(dst, flow, in_port, port, tags)
+                && acl_iface_output_filter(iface_from_dp_ifidx(br,
+                                           dst->dp_ifidx)->acl, flow)) {
                 mirrors |= port->dst_mirrors;
                 dst++;
             }
         }
-    } else if (out_port && set_dst(dst, flow, in_port, out_port, tags)) {
+    } else if (out_port && set_dst(dst, flow, in_port, out_port, tags)
+               && acl_iface_output_filter(iface_from_dp_ifidx(br,
+                                          dst->dp_ifidx)->acl, flow)) {
         mirrors |= out_port->dst_mirrors;
         dst++;
     }
@@ -3000,6 +3026,7 @@ iface_create(struct port *port, const char *name)
 
     VLOG_DBG("attached network device %s to port %s", iface->name, port->name);
 
+    acl_iface_init(port->name, port->bridge->ofproto, &iface->acl);
     bridge_flush(port->bridge);
 }
 
@@ -3012,6 +3039,8 @@ iface_destroy(struct iface *iface)
         bool del_active = port->active_iface == iface->port_ifidx;
         struct iface *del;
 
+        acl_iface_destroy(iface->acl);
+
         if (iface->dp_ifidx >= 0) {
             port_array_set(&br->ifaces, iface->dp_ifidx, NULL);
         }
-- 
1.6.0.4





More information about the discuss mailing list