@@ -66,6 +66,34 @@
</dd>
</dl>
+ <h1>ACL Commands</h1>
+ <dl>
+ <dt><code>acl-add</code> <var>lswitch</var> <var>direction</var> <var>priority</var> <var>match</var> <var>action</var> [<code>log</code>]</dt>
+ <dd>
+ Adds the specified ACL to <var>lswitch</var>.
+ <var>direction</var> must be either <code>from-lport</code> or
+ <code>to-lport</code>. <var>priority</var> must be between
+ <code>1</code> and <code>65534</code>, inclusive. If
+ <code>log</code> is supplied, packet logging is enabled for the
+ ACL. A full description of the fields are in <code>ovn-nb</code>(5).
+ </dd>
+
+ <dt><code>acl-del</code> <var>lswitch</var> [<var>direction</var> [<var>priority</var> <var>match</var>]]</dt>
+ <dd>
+ Deletes ACLs from <var>lswitch</var>. If only
+ <var>lswitch</var> is supplied, all the ACLs from the logical
+ switch are deleted. If <var>direction</var> is also specified,
+ then all the flows in that direction will be deleted from the
+ logical switch. If all the fields are given, then a single flow
+ that matches all the fields will be deleted.
+ </dd>
+
+ <dt><code>acl-list</code> <var>lswitch</var></dt>
+ <dd>
+ Lists the ACLs on <var>lswitch</var>.
+ </dd>
+ </dl>
+
<h1>Logical Port Commands</h1>
<dl>
<dt><code>lport-add</code> <var>lswitch</var> <var>lport</var></dt>
@@ -63,6 +63,13 @@ Logical switch commands:\n\
lswitch-get-external-id LSWITCH [KEY]\n\
list one or all external-ids on LSWITCH\n\
\n\
+ACL commands:\n\
+ acl-add LSWITCH DIRECTION PRIORITY MATCH ACTION [log]\n\
+ add an ACL to LSWITCH\n\
+ acl-del LSWITCH [DIRECTION [PRIORITY MATCH]]\n\
+ remove ACLs from LSWITCH\n\
+ acl-list LSWITCH print ACLs for LSWITCH\n\
+\n\
Logical port commands:\n\
lport-add LSWITCH LPORT add logical port LPORT on LSWITCH\n\
lport-add LSWITCH LPORT PARENT TAG\n\
@@ -747,6 +754,220 @@ do_lport_get_options(struct ovs_cmdl_context *ctx)
printf("%s=%s\n", node->key, node->value);
}
}
+
+enum {
+ DIR_FROM_LPORT,
+ DIR_TO_LPORT
+};
+
+static int
+dir_encode(const char *dir)
+{
+ if (!strcmp(dir, "from-lport")) {
+ return DIR_FROM_LPORT;
+ } else if (!strcmp(dir, "to-lport")) {
+ return DIR_TO_LPORT;
+ }
+
+ OVS_NOT_REACHED();
+}
+
+static int
+acl_cmp(const void *acl1_, const void *acl2_)
+{
+ const struct nbrec_acl *acl1, *acl2;
+
+ acl1 = *((struct nbrec_acl **) acl1_);
+ acl2 = *((struct nbrec_acl **) acl2_);
+
+ int dir1 = dir_encode(acl1->direction);
+ int dir2 = dir_encode(acl2->direction);
+
+#define CMP(expr) \
+ do { \
+ int res; \
+ res = (expr); \
+ if (res) { \
+ return res; \
+ } \
+ } while (0)
+
+ CMP(dir1 - dir2);
+ CMP(acl1->priority > acl2->priority ? -1 :
+ (acl1->priority < acl2->priority ? 1 : 0));
+ CMP(strcmp(acl1->match, acl2->match));
+
+#undef CMP
+
+ return 0;
+}
+
+static void
+do_acl_list(struct ovs_cmdl_context *ctx)
+{
+ const struct nbrec_logical_switch *lswitch;
+ struct nbctl_context *nb_ctx = ctx->pvt;
+ const struct nbrec_acl **acls;
+ size_t i;
+
+ lswitch = lswitch_by_name_or_uuid(nb_ctx, ctx->argv[1]);
+ if (!lswitch) {
+ return;
+ }
+
+ acls = xmalloc(sizeof *acls * lswitch->n_acls);
+ for (i = 0; i < lswitch->n_acls; i++) {
+ acls[i] = lswitch->acls[i];
+ }
+
+ qsort(acls, lswitch->n_acls, sizeof *acls, acl_cmp);
+
+ for (i = 0; i < lswitch->n_acls; i++) {
+ const struct nbrec_acl *acl = acls[i];
+ printf("%10s %5ld (%s) %s%s\n", acl->direction, acl->priority,
+ acl->match, acl->action, acl->log ? " log" : "");
+ }
+
+ free(acls);
+}
+
+static void
+do_acl_add(struct ovs_cmdl_context *ctx)
+{
+ const struct nbrec_logical_switch *lswitch;
+ struct nbctl_context *nb_ctx = ctx->pvt;
+ const char *action = ctx->argv[5];
+ const char *direction;
+ int64_t priority;
+
+ lswitch = lswitch_by_name_or_uuid(nb_ctx, ctx->argv[1]);
+ if (!lswitch) {
+ return;
+ }
+
+ /* Validate direction. Only require the first letter. */
+ if (ctx->argv[2][0] == 't') {
+ direction = "to-lport";
+ } else if (ctx->argv[2][0] == 'f') {
+ direction = "from-lport";
+ } else {
+ VLOG_WARN("Invalid direction '%s'", ctx->argv[2]);
+ return;
+ }
+
+ /* Validate priority. */
+ if (!ovs_scan(ctx->argv[3], "%"SCNd64, &priority) || priority < 1
+ || priority > 65535) {
+ VLOG_WARN("Invalid priority '%s'", ctx->argv[3]);
+ return;
+ }
+
+ /* Validate action. */
+ if (strcmp(action, "allow") && strcmp(action, "allow-related")
+ && strcmp(action, "drop") && strcmp(action, "reject")) {
+ VLOG_WARN("Invalid action '%s'", action);
+ return;
+ }
+
+ /* Create the acl. */
+ struct nbrec_acl *acl = nbrec_acl_insert(nb_ctx->txn);
+ nbrec_acl_set_priority(acl, priority);
+ nbrec_acl_set_direction(acl, direction);
+ nbrec_acl_set_match(acl, ctx->argv[4]);
+ nbrec_acl_set_action(acl, action);
+ if (ctx->argc == 7 && ctx->argv[6][0] == 'l') {
+ nbrec_acl_set_log(acl, true);
+ }
+
+ /* Insert the acl into the logical switch. */
+ nbrec_logical_switch_verify_acls(lswitch);
+ struct nbrec_acl **new_acls = xmalloc(sizeof *new_acls *
+ (lswitch->n_acls + 1));
+ memcpy(new_acls, lswitch->acls, sizeof *new_acls * lswitch->n_acls);
+ new_acls[lswitch->n_acls] = acl;
+ nbrec_logical_switch_set_acls(lswitch, new_acls, lswitch->n_acls + 1);
+ free(new_acls);
+}
+
+static void
+do_acl_del(struct ovs_cmdl_context *ctx)
+{
+ const struct nbrec_logical_switch *lswitch;
+ struct nbctl_context *nb_ctx = ctx->pvt;
+ const char *direction;
+ int64_t priority = 0;
+
+ lswitch = lswitch_by_name_or_uuid(nb_ctx, ctx->argv[1]);
+ if (!lswitch) {
+ return;
+ }
+
+ if (ctx->argc != 2 && ctx->argc != 3 && ctx->argc != 5) {
+ VLOG_WARN("Invalid number of arguments");
+ return;
+ }
+
+ if (ctx->argc == 2) {
+ /* If direction, priority, and match are not specified, delete
+ * all ACLs. */
+ nbrec_logical_switch_verify_acls(lswitch);
+ nbrec_logical_switch_set_acls(lswitch, NULL, 0);
+ return;
+ }
+
+ /* Validate direction. Only require first letter. */
+ if (ctx->argv[2][0] == 't') {
+ direction = "to-lport";
+ } else if (ctx->argv[2][0] == 'f') {
+ direction = "from-lport";
+ } else {
+ VLOG_WARN("Invalid direction '%s'", ctx->argv[2]);
+ return;
+ }
+
+ /* If priority and match are not specified, delete all ACLs with the
+ * specified direction. */
+ if (ctx->argc == 3) {
+ struct nbrec_acl **new_acls
+ = xmalloc(sizeof *new_acls * lswitch->n_acls);
+
+ int n_acls = 0;
+ for (size_t i = 0; i < lswitch->n_acls; i++) {
+ if (strcmp(direction, lswitch->acls[i]->direction)) {
+ new_acls[n_acls++] = lswitch->acls[i];
+ }
+ }
+
+ nbrec_logical_switch_verify_acls(lswitch);
+ nbrec_logical_switch_set_acls(lswitch, new_acls, n_acls);
+ free(new_acls);
+ return;
+ }
+
+ /* Validate priority. */
+ if (!ovs_scan(ctx->argv[3], "%"SCNd64, &priority) || priority < 1
+ || priority > 65535) {
+ VLOG_WARN("Invalid priority '%s'", ctx->argv[3]);
+ return;
+ }
+
+ /* Remove the matching rule. */
+ for (size_t i = 0; i < lswitch->n_acls; i++) {
+ struct nbrec_acl *acl = lswitch->acls[i];
+
+ if (priority == acl->priority && !strcmp(ctx->argv[4], acl->match) &&
+ !strcmp(direction, acl->direction)) {
+ struct nbrec_acl **new_acls
+ = xmemdup(lswitch->acls, sizeof *new_acls * lswitch->n_acls);
+ new_acls[i] = lswitch->acls[lswitch->n_acls - 1];
+ nbrec_logical_switch_verify_acls(lswitch);
+ nbrec_logical_switch_set_acls(lswitch, new_acls,
+ lswitch->n_acls - 1);
+ free(new_acls);
+ return;
+ }
+ }
+}
static void
parse_options(int argc, char *argv[])
@@ -849,6 +1070,27 @@ static const struct ovs_cmdl_command all_commands[] = {
.handler = do_lswitch_get_external_id,
},
{
+ .name = "acl-add",
+ .usage = "LSWITCH DIRECTION PRIORITY MATCH ACTION [log]",
+ .min_args = 5,
+ .max_args = 6,
+ .handler = do_acl_add,
+ },
+ {
+ .name = "acl-del",
+ .usage = "LSWITCH [DIRECTION [PRIORITY MATCH]]",
+ .min_args = 1,
+ .max_args = 4,
+ .handler = do_acl_del,
+ },
+ {
+ .name = "acl-list",
+ .usage = "LSWITCH",
+ .min_args = 1,
+ .max_args = 1,
+ .handler = do_acl_list,
+ },
+ {
.name = "lport-add",
.usage = "LSWITCH LPORT [PARENT] [TAG]",
.min_args = 2,
@@ -117,3 +117,52 @@ AT_CHECK([ovn-nbctl lport-get-port-security lp0], [0], [dnl
OVN_NBCTL_TEST_STOP
AT_CLEANUP
+
+dnl ---------------------------------------------------------------------
+
+AT_SETUP([ovn-nbctl - ACLs])
+OVN_NBCTL_TEST_START
+
+AT_CHECK([ovn-nbctl lswitch-add ls0])
+AT_CHECK([ovn-nbctl acl-add ls0 from-lport 600 udp drop log])
+AT_CHECK([ovn-nbctl acl-add ls0 to-lport 500 udp drop log])
+AT_CHECK([ovn-nbctl acl-add ls0 from-lport 400 tcp drop])
+AT_CHECK([ovn-nbctl acl-add ls0 to-lport 300 tcp drop])
+AT_CHECK([ovn-nbctl acl-add ls0 from-lport 200 ip drop])
+AT_CHECK([ovn-nbctl acl-add ls0 to-lport 100 ip drop])
+
+AT_CHECK([ovn-nbctl acl-list ls0], [0], [dnl
+from-lport 600 (udp) drop log
+from-lport 400 (tcp) drop
+from-lport 200 (ip) drop
+ to-lport 500 (udp) drop log
+ to-lport 300 (tcp) drop
+ to-lport 100 (ip) drop
+])
+
+dnl Delete in one direction.
+AT_CHECK([ovn-nbctl acl-del ls0 to-lport])
+AT_CHECK([ovn-nbctl acl-list ls0], [0], [dnl
+from-lport 600 (udp) drop log
+from-lport 400 (tcp) drop
+from-lport 200 (ip) drop
+])
+
+dnl Delete all ACLs.
+AT_CHECK([ovn-nbctl acl-del ls0])
+AT_CHECK([ovn-nbctl acl-list ls0], [0], [dnl
+])
+
+AT_CHECK([ovn-nbctl acl-add ls0 from-lport 600 udp drop])
+AT_CHECK([ovn-nbctl acl-add ls0 from-lport 400 tcp drop])
+AT_CHECK([ovn-nbctl acl-add ls0 from-lport 200 ip drop])
+
+dnl Delete a single flow.
+AT_CHECK([ovn-nbctl acl-del ls0 from-lport 400 tcp])
+AT_CHECK([ovn-nbctl acl-list ls0], [0], [dnl
+from-lport 600 (udp) drop
+from-lport 200 (ip) drop
+])
+
+OVN_NBCTL_TEST_STOP
+AT_CLEANUP
Signed-off-by: Justin Pettit <jpettit@nicira.com> --- ovn/utilities/ovn-nbctl.8.xml | 28 +++++ ovn/utilities/ovn-nbctl.c | 242 +++++++++++++++++++++++++++++++++++++++++ tests/ovn-nbctl.at | 49 ++++++++ 3 files changed, 319 insertions(+), 0 deletions(-)