@@ -1,6 +1,9 @@
Post-v21.06.0
-------------------------
- Added Control Plane Protection support (control plane traffic metering).
+ - Introduced a new "label" field for "allow" and "allow-related" ACLs
+ which helps in debugging/backtracking the ACL which allowed a particular
+ connection.
OVN v21.06.0 - 18 Jun 2021
-------------------------
@@ -146,6 +146,8 @@ ovn_init_symtab(struct shash *symtab)
"ct_label[32..79]", WR_CT_COMMIT);
expr_symtab_add_subfield_scoped(symtab, "ct_label.ecmp_reply_port", NULL,
"ct_label[80..95]", WR_CT_COMMIT);
+ expr_symtab_add_subfield_scoped(symtab, "ct_label.label", NULL,
+ "ct_label[96..127]", WR_CT_COMMIT);
expr_symtab_add_field(symtab, "ct_state", MFF_CT_STATE, NULL, false);
@@ -670,13 +670,19 @@
the <code>next;</code> action. If there are any stateful ACLs
on this datapath, then <code>allow</code> ACLs translate to
<code>ct_commit; next;</code> (which acts as a hint for the next tables
- to commit the connection to conntrack),
+ to commit the connection to conntrack). In case the <code>ACL</code>
+ has a label then <code>reg3</code> is loaded with the label value and
+ <code>reg0[13]</code> bit is set to 1 (which acts as a hint for the
+ next tables to commit the label to conntrack).
</li>
<li>
<code>allow-related</code> ACLs translate into logical
flows with the <code>ct_commit(ct_label=0/1); next;</code> actions
for new connections and <code>reg0[1] = 1; next;</code> for existing
- connections.
+ connections. In case the <code>ACL</code> has a label then
+ <code>reg3</code> is loaded with the label value and
+ <code>reg0[13]</code> bit is set to 1 (which acts as a hint for the
+ next tables to commit the label to conntrack).
</li>
<li>
<code>allow-stateless</code> ACLs translate into logical
@@ -876,9 +882,19 @@
</li>
<li>
- A priority-100 flow commits packets to connection tracker using
- <code>ct_commit; next;</code> action based on a hint provided by
- the previous tables (with a match for <code>reg0[1] == 1</code>).
+ A priority 100 flow is added which commits the packet to the conntrack
+ and sets the most significant 32-bits of <code>ct_label</code> with the
+ <code>reg3</code> value based on the hint provided by previous tables
+ (with a match for <code>reg0[1] == 1 && reg0[13] == 1</code>).
+ This is used by the <code>ACLs</code> with label to commit the label
+ value to conntrack.
+ </li>
+
+ <li>
+ For <code>ACLs</code> without label, a second priority-100 flow commits
+ packets to connection tracker using <code>ct_commit; next;</code>
+ action based on a hint provided by the previous tables (with a match
+ for <code>reg0[1] == 1 && reg0[13] == 0</code>).
</li>
<li>
A priority-0 flow that simply moves traffic to the next table.
@@ -241,6 +241,7 @@ enum ovn_stage {
#define REGBIT_ACL_HINT_BLOCK "reg0[10]"
#define REGBIT_LKUP_FDB "reg0[11]"
#define REGBIT_HAIRPIN_REPLY "reg0[12]"
+#define REGBIT_ACL_LABEL "reg0[13]"
#define REG_ORIG_DIP_IPV4 "reg1"
#define REG_ORIG_DIP_IPV6 "xxreg1"
@@ -273,6 +274,9 @@ enum ovn_stage {
#define REG_SRC_IPV4 "reg1"
#define REG_SRC_IPV6 "xxreg1"
+/* Register used for setting a label for ACLs in a Logical Switch. */
+#define REG_LABEL "reg3"
+
#define FLAGBIT_NOT_VXLAN "flags[1] == 0"
/*
@@ -281,14 +285,15 @@ enum ovn_stage {
* Logical Switch pipeline:
* +----+----------------------------------------------+---+------------------+
* | R0 | REGBIT_{CONNTRACK/DHCP/DNS} | | |
- * | | REGBIT_{HAIRPIN/HAIRPIN_REPLY} | X | |
- * | | REGBIT_ACL_HINT_{ALLOW_NEW/ALLOW/DROP/BLOCK} | X | |
+ * | | REGBIT_{HAIRPIN/HAIRPIN_REPLY} | | |
+ * | | REGBIT_ACL_HINT_{ALLOW_NEW/ALLOW/DROP/BLOCK} | | |
+ * | | REGBIT_ACL_LABEL | X | |
* +----+----------------------------------------------+ X | |
* | R1 | ORIG_DIP_IPV4 (>= IN_STATEFUL) | R | |
* +----+----------------------------------------------+ E | |
* | R2 | ORIG_TP_DPORT (>= IN_STATEFUL) | G | |
* +----+----------------------------------------------+ 0 | |
- * | R3 | UNUSED | | |
+ * | R3 | ACL LABEL | | |
* +----+----------------------------------------------+---+------------------+
* | R4 | UNUSED | | |
* +----+----------------------------------------------+ X | ORIG_DIP_IPV6 |
@@ -5781,7 +5786,12 @@ consider_acl(struct hmap *lflows, struct ovn_datapath *od,
ds_clear(actions);
ds_put_format(match, REGBIT_ACL_HINT_ALLOW_NEW " == 1 && (%s)",
acl->match);
+
ds_put_cstr(actions, REGBIT_CONNTRACK_COMMIT" = 1; ");
+ if (acl->label) {
+ ds_put_format(actions, REGBIT_ACL_LABEL" = 1; "
+ REG_LABEL" = %"PRId64"; ", acl->label);
+ }
build_acl_log(actions, acl, meter_groups);
ds_put_cstr(actions, "next;");
ovn_lflow_add_with_hint(lflows, od, stage,
@@ -5792,15 +5802,21 @@ consider_acl(struct hmap *lflows, struct ovn_datapath *od,
/* Match on traffic in the request direction for an established
* connection tracking entry that has not been marked for
- * deletion. There is no need to commit here, so we can just
- * proceed to the next table. We use this to ensure that this
+ * deletion. We use this to ensure that this
* connection is still allowed by the currently defined
- * policy. Match untracked packets too. */
+ * policy. Match untracked packets too.
+ * Commit the connection only if the ACL has a label. This is done
+ * to update the connection tracking entry label in case the ACL
+ * allowing the connection changes. */
ds_clear(match);
ds_clear(actions);
ds_put_format(match, REGBIT_ACL_HINT_ALLOW " == 1 && (%s)",
acl->match);
-
+ if (acl->label) {
+ ds_put_cstr(actions, REGBIT_CONNTRACK_COMMIT" = 1; ");
+ ds_put_format(actions, REGBIT_ACL_LABEL" = 1; "
+ REG_LABEL" = %"PRId64"; ", acl->label);
+ }
build_acl_log(actions, acl, meter_groups);
ds_put_cstr(actions, "next;");
ovn_lflow_add_with_hint(lflows, od, stage,
@@ -6299,15 +6315,34 @@ build_stateful(struct ovn_datapath *od, struct hmap *lflows)
ovn_lflow_add(lflows, od, S_SWITCH_IN_STATEFUL, 0, "1", "next;");
ovn_lflow_add(lflows, od, S_SWITCH_OUT_STATEFUL, 0, "1", "next;");
+ /* If REGBIT_CONNTRACK_COMMIT is set as 1 and
+ * REGBIT_CONNTRACK_SET_LABEL is set to 1, then the packets should be
+ * committed to conntrack.
+ * We always set ct_mark.blocked to 0 here as
+ * any packet that makes it this far is part of a connection we
+ * want to allow to continue. */
+ ovn_lflow_add(lflows, od, S_SWITCH_IN_STATEFUL, 100,
+ REGBIT_CONNTRACK_COMMIT" == 1 && "
+ REGBIT_ACL_LABEL" == 1",
+ "ct_commit { ct_label.blocked = 0; "
+ "ct_label.label = " REG_LABEL "; }; next;");
+ ovn_lflow_add(lflows, od, S_SWITCH_OUT_STATEFUL, 100,
+ REGBIT_CONNTRACK_COMMIT" == 1 && "
+ REGBIT_ACL_LABEL" == 1",
+ "ct_commit { ct_label.blocked = 0; "
+ "ct_label.label = " REG_LABEL "; }; next;");
+
/* If REGBIT_CONNTRACK_COMMIT is set as 1, then the packets should be
* committed to conntrack. We always set ct_label.blocked to 0 here as
* any packet that makes it this far is part of a connection we
* want to allow to continue. */
ovn_lflow_add(lflows, od, S_SWITCH_IN_STATEFUL, 100,
- REGBIT_CONNTRACK_COMMIT" == 1",
+ REGBIT_CONNTRACK_COMMIT" == 1 && "
+ REGBIT_ACL_LABEL" == 0",
"ct_commit { ct_label.blocked = 0; }; next;");
ovn_lflow_add(lflows, od, S_SWITCH_OUT_STATEFUL, 100,
- REGBIT_CONNTRACK_COMMIT" == 1",
+ REGBIT_CONNTRACK_COMMIT" == 1 && "
+ REGBIT_ACL_LABEL" == 0",
"ct_commit { ct_label.blocked = 0; }; next;");
}
@@ -1503,13 +1503,14 @@ function s_ROUTER_OUT_DELIVERY(): Stage { Stage{ Egress, 4, "lr_out_deliv
* Logical Switch pipeline:
* +----+----------------------------------------------+---+------------------+
* | R0 | REGBIT_{CONNTRACK/DHCP/DNS} | | |
- * | | REGBIT_{HAIRPIN/HAIRPIN_REPLY} | X | |
+ * | | REGBIT_{HAIRPIN/HAIRPIN_REPLY} | | |
+ * | | REGBIT_ACL_LABEL | X | |
* +----+----------------------------------------------+ X | |
* | R1 | ORIG_DIP_IPV4 (>= IN_STATEFUL) | R | |
* +----+----------------------------------------------+ E | |
* | R2 | ORIG_TP_DPORT (>= IN_STATEFUL) | G | |
* +----+----------------------------------------------+ 0 | |
- * | R3 | UNUSED | | |
+ * | R3 | ACL_LABEL | | |
* +----+----------------------------------------------+---+------------------+
* | R4 | UNUSED | | |
* +----+----------------------------------------------+ X | ORIG_DIP_IPV6 |
@@ -1582,6 +1583,7 @@ function rEGBIT_ACL_HINT_DROP() : string = "reg0[9]"
function rEGBIT_ACL_HINT_BLOCK() : string = "reg0[10]"
function rEGBIT_LKUP_FDB() : string = "reg0[11]"
function rEGBIT_HAIRPIN_REPLY() : string = "reg0[12]"
+function rEGBIT_ACL_LABEL() : string = "reg0[13]"
function rEG_ORIG_DIP_IPV4() : string = "reg1"
function rEG_ORIG_DIP_IPV6() : string = "xxreg1"
@@ -1608,6 +1610,9 @@ function rEG_INPORT_ETH_ADDR() : string = "xreg0[0..47]"
function rEG_ECMP_GROUP_ID() : string = "reg8[0..15]"
function rEG_ECMP_MEMBER_ID() : string = "reg8[16..31]"
+/* Register used for setting a label for ACLs in a Logical Switch. */
+function rEG_LABEL() : string = "reg3"
+
function fLAGBIT_NOT_VXLAN() : string = "flags[1] == 0"
function mFF_N_LOG_REGS() : bit<32> = 10
@@ -2696,26 +2701,41 @@ for (&SwitchACL(.sw = sw, .acl = acl, .has_fair_meter = fair_meter)) {
* that case here and un-set ct_label.blocked (which will be done
* by ct_commit in the "stateful" stage) to indicate that the
* connection should be allowed to resume.
+ * If the ACL has a label, then load REG_LABEL with the label and
+ * set the REGBIT_ACL_LABEL field.
*/
- Flow(.logical_datapath = sw._uuid,
- .stage = stage,
- .priority = acl.priority + oVN_ACL_PRI_OFFSET(),
- .__match = "${rEGBIT_ACL_HINT_ALLOW_NEW()} == 1 && (${acl.__match})",
- .actions = "${rEGBIT_CONNTRACK_COMMIT()} = 1; ${acl_log}next;",
- .external_ids = stage_hint);
+ var __action = if (acl.label != 0) {
+ "${rEGBIT_CONNTRACK_COMMIT()} = 1; ${rEGBIT_ACL_LABEL()} = 1;\
+ \ ${rEG_LABEL()} = ${acl.label}; ${acl_log}next;"
+ } else {
+ "${rEGBIT_CONNTRACK_COMMIT()} = 1; ${acl_log}next;"
+ } in Flow(.logical_datapath = sw._uuid,
+ .stage = stage,
+ .priority = acl.priority + oVN_ACL_PRI_OFFSET(),
+ .__match = "${rEGBIT_ACL_HINT_ALLOW_NEW()} == 1 && (${acl.__match})",
+ .actions = __action,
+ .external_ids = stage_hint);
/* Match on traffic in the request direction for an established
* connection tracking entry that has not been marked for
- * deletion. There is no need to commit here, so we can just
- * proceed to the next table. We use this to ensure that this
+ * deletion. We use this to ensure that this
* connection is still allowed by the currently defined
- * policy. Match untracked packets too. */
- Flow(.logical_datapath = sw._uuid,
- .stage = stage,
- .priority = acl.priority + oVN_ACL_PRI_OFFSET(),
- .__match = "${rEGBIT_ACL_HINT_ALLOW()} == 1 && (${acl.__match})",
- .actions = "${acl_log}next;",
- .external_ids = stage_hint)
+ * policy. Match untracked packets too.
+ * Commit the connection only if the ACL has a label. This is done to
+ * update the connection tracking entry label in case the ACL
+ * allowing the connection changes.
+ */
+ var __action = if (acl.label != 0) {
+ "${rEGBIT_CONNTRACK_COMMIT()} = 1; ${rEGBIT_ACL_LABEL()} = 1;\
+ \ ${rEG_LABEL()} = ${acl.label}; ${acl_log}next;"
+ } else {
+ "${acl_log}next;"
+ } in Flow(.logical_datapath = sw._uuid,
+ .stage = stage,
+ .priority = acl.priority + oVN_ACL_PRI_OFFSET(),
+ .__match = "${rEGBIT_ACL_HINT_ALLOW()} == 1 && (${acl.__match})",
+ .actions = __action,
+ .external_ids = stage_hint)
}
} else if (acl.action == "allow-stateless") {
Flow(.logical_datapath = sw._uuid,
@@ -2932,6 +2952,24 @@ for (&Switch(._uuid = ls_uuid)) {
.actions = "next;",
.external_ids = map_empty());
+ /* If REGBIT_CONNTRACK_COMMIT is set as 1 and REGBIT_CONNTRACK_SET_LABEL
+ * is set to 1, then the packets should be
+ * committed to conntrack. We always set ct_label.blocked to 0 here as
+ * any packet that makes it this far is part of a connection we
+ * want to allow to continue. */
+ Flow(.logical_datapath = ls_uuid,
+ .stage = s_SWITCH_IN_STATEFUL(),
+ .priority = 100,
+ .__match = "${rEGBIT_CONNTRACK_COMMIT()} == 1 && ${rEGBIT_ACL_LABEL()} == 1",
+ .actions = "ct_commit { ct_label.blocked = 0; ct_label.label = ${rEG_LABEL()}; }; next;",
+ .external_ids = map_empty());
+ Flow(.logical_datapath = ls_uuid,
+ .stage = s_SWITCH_OUT_STATEFUL(),
+ .priority = 100,
+ .__match = "${rEGBIT_CONNTRACK_COMMIT()} == 1 && ${rEGBIT_ACL_LABEL()} == 1",
+ .actions = "ct_commit { ct_label.blocked = 0; ct_label.label = ${rEG_LABEL()}; }; next;",
+ .external_ids = map_empty());
+
/* If REGBIT_CONNTRACK_COMMIT is set as 1, then the packets should be
* committed to conntrack. We always set ct_label.blocked to 0 here as
* any packet that makes it this far is part of a connection we
@@ -2939,13 +2977,13 @@ for (&Switch(._uuid = ls_uuid)) {
Flow(.logical_datapath = ls_uuid,
.stage = s_SWITCH_IN_STATEFUL(),
.priority = 100,
- .__match = "${rEGBIT_CONNTRACK_COMMIT()} == 1",
+ .__match = "${rEGBIT_CONNTRACK_COMMIT()} == 1 && ${rEGBIT_ACL_LABEL()} == 0",
.actions = "ct_commit { ct_label.blocked = 0; }; next;",
.external_ids = map_empty());
Flow(.logical_datapath = ls_uuid,
.stage = s_SWITCH_OUT_STATEFUL(),
.priority = 100,
- .__match = "${rEGBIT_CONNTRACK_COMMIT()} == 1",
+ .__match = "${rEGBIT_CONNTRACK_COMMIT()} == 1 && ${rEGBIT_ACL_LABEL()} == 0",
.actions = "ct_commit { ct_label.blocked = 0; }; next;",
.external_ids = map_empty())
}
@@ -1,7 +1,7 @@
{
"name": "OVN_Northbound",
- "version": "5.32.0",
- "cksum": "2501921026 29540",
+ "version": "5.32.1",
+ "cksum": "2805328215 29734",
"tables": {
"NB_Global": {
"columns": {
@@ -244,6 +244,9 @@
"debug"]]},
"min": 0, "max": 1}},
"meter": {"type": {"key": "string", "min": 0, "max": 1}},
+ "label": {"type": {"key": {"type": "integer",
+ "minInteger": 0,
+ "maxInteger": 4294967295}}},
"external_ids": {
"type": {"key": "string", "value": "string",
"min": 0, "max": "unlimited"}}},
@@ -1853,6 +1853,18 @@
and <code>deny</code> as <ref column="action"/>.)
</p>
+ <column name="label">
+ <p>
+ Associates an identifier with the ACL.
+ The same value will be written to corresponding connection
+ tracker entry. The value should be a valid 32-bit unsigned integer.
+ This value can help in debugging from connection tracker side.
+ For example, through this "label" we can backtrack to the ACL rule
+ which is causing a "leaked" connection. Connection tracker entries are
+ created only for allowed connections so the label is valid only
+ for allow and allow-related actions.
+ </p>
+ </column>
<column name="priority">
<p>
The ACL rule's priority. Rules with numerically higher priority
@@ -211,18 +211,36 @@ ovn_nbctl_test_acl() {
AT_CHECK([ovn-nbctl $2 acl-add $1 to-lport 300 tcp drop])
AT_CHECK([ovn-nbctl $2 acl-add $1 from-lport 200 ip drop])
AT_CHECK([ovn-nbctl $2 acl-add $1 to-lport 100 ip drop])
+ AT_CHECK([ovn-nbctl $2 --label=1234 acl-add $1 from-lport 70 icmp allow-related])
+ AT_CHECK([ovn-nbctl $2 --label=1235 acl-add $1 to-lport 70 icmp allow-related])
+
dnl Add duplicated ACL
AT_CHECK([ovn-nbctl $2 acl-add $1 to-lport 100 ip drop], [1], [], [stderr])
AT_CHECK([grep 'already existed' stderr], [0], [ignore])
AT_CHECK([ovn-nbctl $2 --may-exist acl-add $1 to-lport 100 ip drop])
+ dnl Add invalid ACL label
+ AT_CHECK([ovn-nbctl $2 --label=1234 acl-add $1 to-lport 50 ip drop], [1], [], [stderr])
+ AT_CHECK([grep 'can only be set with actions' stderr], [0], [ignore])
+
+ AT_CHECK([ovn-nbctl $2 --label=abcd acl-add $1 to-lport 50 ip allow-related], [1], [], [stderr])
+ AT_CHECK([grep 'label must in range 0...4294967295' stderr], [0], [ignore])
+
+ AT_CHECK([ovn-nbctl $2 --label=-1 acl-add $1 to-lport 50 ip allow-related], [1], [], [stderr])
+ AT_CHECK([grep 'label must in range 0...4294967295' stderr], [0], [ignore])
+
+ AT_CHECK([ovn-nbctl $2 --label=4294967296 acl-add $1 to-lport 50 ip allow-related], [1], [], [stderr])
+ AT_CHECK([grep 'label must in range 0...4294967295' stderr], [0], [ignore])
+
AT_CHECK([ovn-nbctl $2 acl-list $1], [0], [dnl
from-lport 600 (udp) drop log()
from-lport 400 (tcp) drop
from-lport 200 (ip) drop
+from-lport 70 (icmp) allow-related label=1234
to-lport 500 (udp) drop log(name=test,severity=info)
to-lport 300 (tcp) drop
to-lport 100 (ip) drop
+ to-lport 70 (icmp) allow-related label=1235
])
dnl Delete in one direction.
@@ -231,6 +249,7 @@ from-lport 200 (ip) drop
from-lport 600 (udp) drop log()
from-lport 400 (tcp) drop
from-lport 200 (ip) drop
+from-lport 70 (icmp) allow-related label=1234
])
dnl Delete all ACLs.
@@ -3608,7 +3608,8 @@ check_stateful_flows() {
AT_CHECK([grep "ls_in_stateful" sw0flows | sort], [0], [dnl
table=12(ls_in_stateful ), priority=0 , match=(1), action=(next;)
- table=12(ls_in_stateful ), priority=100 , match=(reg0[[1]] == 1), action=(ct_commit { ct_label.blocked = 0; }; next;)
+ table=12(ls_in_stateful ), priority=100 , match=(reg0[[1]] == 1 && reg0[[13]] == 0), action=(ct_commit { ct_label.blocked = 0; }; next;)
+ table=12(ls_in_stateful ), priority=100 , match=(reg0[[1]] == 1 && reg0[[13]] == 1), action=(ct_commit { ct_label.blocked = 0; ct_label.label = reg3; }; next;)
table=12(ls_in_stateful ), priority=120 , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(reg1 = 10.0.0.10; reg2[[0..15]] = 80; ct_lb(backends=10.0.0.4:8080);)
])
@@ -3630,7 +3631,8 @@ check_stateful_flows() {
AT_CHECK([grep "ls_out_stateful" sw0flows | sort], [0], [dnl
table=7 (ls_out_stateful ), priority=0 , match=(1), action=(next;)
- table=7 (ls_out_stateful ), priority=100 , match=(reg0[[1]] == 1), action=(ct_commit { ct_label.blocked = 0; }; next;)
+ table=7 (ls_out_stateful ), priority=100 , match=(reg0[[1]] == 1 && reg0[[13]] == 0), action=(ct_commit { ct_label.blocked = 0; }; next;)
+ table=7 (ls_out_stateful ), priority=100 , match=(reg0[[1]] == 1 && reg0[[13]] == 1), action=(ct_commit { ct_label.blocked = 0; ct_label.label = reg3; }; next;)
])
}
@@ -3669,7 +3671,8 @@ AT_CHECK([grep "ls_in_pre_stateful" sw0flows | sort], [0], [dnl
AT_CHECK([grep "ls_in_stateful" sw0flows | sort], [0], [dnl
table=12(ls_in_stateful ), priority=0 , match=(1), action=(next;)
- table=12(ls_in_stateful ), priority=100 , match=(reg0[[1]] == 1), action=(ct_commit { ct_label.blocked = 0; }; next;)
+ table=12(ls_in_stateful ), priority=100 , match=(reg0[[1]] == 1 && reg0[[13]] == 0), action=(ct_commit { ct_label.blocked = 0; }; next;)
+ table=12(ls_in_stateful ), priority=100 , match=(reg0[[1]] == 1 && reg0[[13]] == 1), action=(ct_commit { ct_label.blocked = 0; ct_label.label = reg3; }; next;)
])
AT_CHECK([grep "ls_out_pre_lb" sw0flows | sort], [0], [dnl
@@ -3687,14 +3690,108 @@ AT_CHECK([grep "ls_out_pre_stateful" sw0flows | sort], [0], [dnl
AT_CHECK([grep "ls_out_stateful" sw0flows | sort], [0], [dnl
table=7 (ls_out_stateful ), priority=0 , match=(1), action=(next;)
- table=7 (ls_out_stateful ), priority=100 , match=(reg0[[1]] == 1), action=(ct_commit { ct_label.blocked = 0; }; next;)
+ table=7 (ls_out_stateful ), priority=100 , match=(reg0[[1]] == 1 && reg0[[13]] == 0), action=(ct_commit { ct_label.blocked = 0; }; next;)
+ table=7 (ls_out_stateful ), priority=100 , match=(reg0[[1]] == 1 && reg0[[13]] == 1), action=(ct_commit { ct_label.blocked = 0; ct_label.label = reg3; }; next;)
])
AT_CLEANUP
])
OVN_FOR_EACH_NORTHD([
-AT_SETUP([ct.inv usage])
+AT_SETUP([ovn -- ACL label usage])
+ovn_start
+
+check ovn-nbctl ls-add sw0
+check ovn-nbctl lsp-add sw0 sw0p1
+
+check ovn-nbctl --wait=sb --label=1234 acl-add sw0 to-lport 1002 tcp allow-related
+check ovn-nbctl --wait=sb --label=1234 acl-add sw0 from-lport 1002 tcp allow-related
+
+ovn-sbctl dump-flows sw0 > sw0flows
+AT_CAPTURE_FILE([sw0flows])
+
+AT_CHECK([grep -w "ls_in_acl" sw0flows | grep 2002 | sort], [0], [dnl
+ table=9 (ls_in_acl ), priority=2002 , match=(reg0[[7]] == 1 && (tcp)), action=(reg0[[1]] = 1; reg0[[13]] = 1; reg3 = 1234; next;)
+ table=9 (ls_in_acl ), priority=2002 , match=(reg0[[8]] == 1 && (tcp)), action=(reg0[[1]] = 1; reg0[[13]] = 1; reg3 = 1234; next;)
+])
+AT_CHECK([grep "ls_in_stateful" sw0flows | sort], [0], [dnl
+ table=12(ls_in_stateful ), priority=0 , match=(1), action=(next;)
+ table=12(ls_in_stateful ), priority=100 , match=(reg0[[1]] == 1 && reg0[[13]] == 0), action=(ct_commit { ct_label.blocked = 0; }; next;)
+ table=12(ls_in_stateful ), priority=100 , match=(reg0[[1]] == 1 && reg0[[13]] == 1), action=(ct_commit { ct_label.blocked = 0; ct_label.label = reg3; }; next;)
+])
+
+AT_CHECK([grep -w "ls_out_acl" sw0flows | grep 2002 | sort], [0], [dnl
+ table=4 (ls_out_acl ), priority=2002 , match=(reg0[[7]] == 1 && (tcp)), action=(reg0[[1]] = 1; reg0[[13]] = 1; reg3 = 1234; next;)
+ table=4 (ls_out_acl ), priority=2002 , match=(reg0[[8]] == 1 && (tcp)), action=(reg0[[1]] = 1; reg0[[13]] = 1; reg3 = 1234; next;)
+])
+AT_CHECK([grep "ls_out_stateful" sw0flows | sort], [0], [dnl
+ table=7 (ls_out_stateful ), priority=0 , match=(1), action=(next;)
+ table=7 (ls_out_stateful ), priority=100 , match=(reg0[[1]] == 1 && reg0[[13]] == 0), action=(ct_commit { ct_label.blocked = 0; }; next;)
+ table=7 (ls_out_stateful ), priority=100 , match=(reg0[[1]] == 1 && reg0[[13]] == 1), action=(ct_commit { ct_label.blocked = 0; ct_label.label = reg3; }; next;)
+])
+
+# Add new ACL without label
+check ovn-nbctl --wait=sb acl-add sw0 to-lport 1002 udp allow-related
+check ovn-nbctl --wait=sb acl-add sw0 from-lport 1002 udp allow-related
+
+ovn-sbctl dump-flows sw0 > sw0flows
+AT_CAPTURE_FILE([sw0flows])
+
+AT_CHECK([grep -w "ls_in_acl" sw0flows | grep 2002 | sort], [0], [dnl
+ table=9 (ls_in_acl ), priority=2002 , match=(reg0[[7]] == 1 && (tcp)), action=(reg0[[1]] = 1; reg0[[13]] = 1; reg3 = 1234; next;)
+ table=9 (ls_in_acl ), priority=2002 , match=(reg0[[7]] == 1 && (udp)), action=(reg0[[1]] = 1; next;)
+ table=9 (ls_in_acl ), priority=2002 , match=(reg0[[8]] == 1 && (tcp)), action=(reg0[[1]] = 1; reg0[[13]] = 1; reg3 = 1234; next;)
+ table=9 (ls_in_acl ), priority=2002 , match=(reg0[[8]] == 1 && (udp)), action=(next;)
+])
+AT_CHECK([grep "ls_in_stateful" sw0flows | sort], [0], [dnl
+ table=12(ls_in_stateful ), priority=0 , match=(1), action=(next;)
+ table=12(ls_in_stateful ), priority=100 , match=(reg0[[1]] == 1 && reg0[[13]] == 0), action=(ct_commit { ct_label.blocked = 0; }; next;)
+ table=12(ls_in_stateful ), priority=100 , match=(reg0[[1]] == 1 && reg0[[13]] == 1), action=(ct_commit { ct_label.blocked = 0; ct_label.label = reg3; }; next;)
+])
+
+AT_CHECK([grep -w "ls_out_acl" sw0flows | grep 2002 | sort], [0], [dnl
+ table=4 (ls_out_acl ), priority=2002 , match=(reg0[[7]] == 1 && (tcp)), action=(reg0[[1]] = 1; reg0[[13]] = 1; reg3 = 1234; next;)
+ table=4 (ls_out_acl ), priority=2002 , match=(reg0[[7]] == 1 && (udp)), action=(reg0[[1]] = 1; next;)
+ table=4 (ls_out_acl ), priority=2002 , match=(reg0[[8]] == 1 && (tcp)), action=(reg0[[1]] = 1; reg0[[13]] = 1; reg3 = 1234; next;)
+ table=4 (ls_out_acl ), priority=2002 , match=(reg0[[8]] == 1 && (udp)), action=(next;)
+])
+AT_CHECK([grep "ls_out_stateful" sw0flows | sort], [0], [dnl
+ table=7 (ls_out_stateful ), priority=0 , match=(1), action=(next;)
+ table=7 (ls_out_stateful ), priority=100 , match=(reg0[[1]] == 1 && reg0[[13]] == 0), action=(ct_commit { ct_label.blocked = 0; }; next;)
+ table=7 (ls_out_stateful ), priority=100 , match=(reg0[[1]] == 1 && reg0[[13]] == 1), action=(ct_commit { ct_label.blocked = 0; ct_label.label = reg3; }; next;)
+])
+
+# Delete new ACL with label
+check ovn-nbctl --wait=sb acl-del sw0 to-lport 1002 tcp
+check ovn-nbctl --wait=sb acl-del sw0 from-lport 1002 tcp
+
+ovn-sbctl dump-flows sw0 > sw0flows
+AT_CAPTURE_FILE([sw0flows])
+
+AT_CHECK([grep -w "ls_in_acl" sw0flows | grep 2002 | sort], [0], [dnl
+ table=9 (ls_in_acl ), priority=2002 , match=(reg0[[7]] == 1 && (udp)), action=(reg0[[1]] = 1; next;)
+ table=9 (ls_in_acl ), priority=2002 , match=(reg0[[8]] == 1 && (udp)), action=(next;)
+])
+AT_CHECK([grep "ls_in_stateful" sw0flows | sort], [0], [dnl
+ table=12(ls_in_stateful ), priority=0 , match=(1), action=(next;)
+ table=12(ls_in_stateful ), priority=100 , match=(reg0[[1]] == 1 && reg0[[13]] == 0), action=(ct_commit { ct_label.blocked = 0; }; next;)
+ table=12(ls_in_stateful ), priority=100 , match=(reg0[[1]] == 1 && reg0[[13]] == 1), action=(ct_commit { ct_label.blocked = 0; ct_label.label = reg3; }; next;)
+])
+
+AT_CHECK([grep -w "ls_out_acl" sw0flows | grep 2002 | sort], [0], [dnl
+ table=4 (ls_out_acl ), priority=2002 , match=(reg0[[7]] == 1 && (udp)), action=(reg0[[1]] = 1; next;)
+ table=4 (ls_out_acl ), priority=2002 , match=(reg0[[8]] == 1 && (udp)), action=(next;)
+])
+AT_CHECK([grep "ls_out_stateful" sw0flows | sort], [0], [dnl
+ table=7 (ls_out_stateful ), priority=0 , match=(1), action=(next;)
+ table=7 (ls_out_stateful ), priority=100 , match=(reg0[[1]] == 1 && reg0[[13]] == 0), action=(ct_commit { ct_label.blocked = 0; }; next;)
+ table=7 (ls_out_stateful ), priority=100 , match=(reg0[[1]] == 1 && reg0[[13]] == 1), action=(ct_commit { ct_label.blocked = 0; ct_label.label = reg3; }; next;)
+])
+AT_CLEANUP
+])
+
+OVN_FOR_EACH_NORTHD([
+AT_SETUP([ovn -- ct.inv usage])
ovn_start
check ovn-nbctl ls-add sw0
@@ -197,6 +197,7 @@ ct_label = NXM_NX_CT_LABEL
ct_label.blocked = ct_label[0]
ct_label.ecmp_reply_eth = ct_label[32..79]
ct_label.ecmp_reply_port = ct_label[80..95]
+ct_label.label = ct_label[96..127]
ct_label.natted = ct_label[1]
ct_mark = NXM_NX_CT_MARK
ct_state = NXM_NX_CT_STATE
@@ -6745,5 +6745,249 @@ OVS_APP_EXIT_AND_WAIT([NORTHD_TYPE])
as
OVS_TRAFFIC_VSWITCHD_STOP(["/.*error receiving.*/d
/.*terminating with signal 15.*/d"])
+
+AT_CLEANUP
+])
+
+OVN_FOR_EACH_NORTHD([
+AT_SETUP([ACL label - conntrack ct_label])
+AT_KEYWORDS([acl label ct_commit])
+
+CHECK_CONNTRACK()
+ovn_start
+
+OVS_TRAFFIC_VSWITCHD_START()
+ADD_BR([br-int])
+
+# Set external-ids in br-int needed for ovn-controller
+ovs-vsctl \
+ -- set Open_vSwitch . external-ids:system-id=hv1 \
+ -- set Open_vSwitch . external-ids:ovn-remote=unix:$ovs_base/ovn-sb/ovn-sb.sock \
+ -- set Open_vSwitch . external-ids:ovn-encap-type=geneve \
+ -- set Open_vSwitch . external-ids:ovn-encap-ip=169.0.0.1 \
+ -- set bridge br-int fail-mode=secure other-config:disable-in-band=true
+
+# Start ovn-controller
+start_daemon ovn-controller
+
+check ovn-nbctl ls-add sw0
+
+check ovn-nbctl lsp-add sw0 sw0-p1
+check ovn-nbctl lsp-set-addresses sw0-p1 "50:54:00:00:00:02 10.0.0.2"
+check ovn-nbctl lsp-set-port-security sw0-p1 "50:54:00:00:00:02 10.0.0.2"
+
+check ovn-nbctl lsp-add sw0 sw0-p2
+check ovn-nbctl lsp-set-addresses sw0-p2 "50:54:00:00:00:03 10.0.0.3"
+check ovn-nbctl lsp-set-port-security sw0-p2 "50:54:00:00:00:03 10.0.0.3"
+
+check ovn-nbctl lsp-add sw0 sw0-p3
+check ovn-nbctl lsp-set-addresses sw0-p3 "50:54:00:00:00:04 10.0.0.4"
+check ovn-nbctl lsp-set-port-security sw0-p3 "50:54:00:00:00:04 10.0.0.4"
+
+# ACLs
+# Case 1: sw0-p1 ---> sw0-p3 allowed, label=1234
+# Case 2: sw0-p3 ---> sw0-p1 allowed, label=1235
+# Case 3: sw0-p1 ---> sw0-p2 allowed, no label
+# Case 4: sw0-p2 ---> sw0-p1 allowed, no label
+
+check ovn-nbctl --label=1234 acl-add sw0 from-lport 1002 'ip4 && inport == "sw0-p1" && ip4.dst == 10.0.0.4' allow-related
+check ovn-nbctl --label=1235 acl-add sw0 to-lport 1002 'ip4 && outport == "sw0-p1" && ip4.src == 10.0.0.4' allow-related
+check ovn-nbctl acl-add sw0 from-lport 1001 "ip" allow-related
+check ovn-nbctl acl-add sw0 to-lport 1001 "ip" allow-related
+
+
+ADD_NAMESPACES(sw0-p1)
+ADD_VETH(sw0-p1, sw0-p1, br-int, "10.0.0.2/24", "50:54:00:00:00:02", \
+ "10.0.0.1")
+ADD_NAMESPACES(sw0-p2)
+ADD_VETH(sw0-p2, sw0-p2, br-int, "10.0.0.3/24", "50:54:00:00:00:03", \
+ "10.0.0.1")
+ADD_NAMESPACES(sw0-p3)
+ADD_VETH(sw0-p3, sw0-p3, br-int, "10.0.0.4/24", "50:54:00:00:00:04", \
+ "10.0.0.1")
+
+# Ensure ovn-controller is caught up
+ovn-nbctl --wait=hv sync
+
+on_exit 'ovn-nbctl acl-list sw0'
+on_exit 'ovn-sbctl lflow-list'
+on_exit 'ovs-ofctl dump-flows br-int'
+
+wait_for_ports_up
+
+AT_CHECK([ovs-appctl dpctl/flush-conntrack])
+# 'sw0-p1' should be able to ping 'sw0-p3'.
+NS_CHECK_EXEC([sw0-p1], [ping -q -c 10 -i 0.3 -w 15 10.0.0.4 | FORMAT_PING], \
+[0], [dnl
+10 packets transmitted, 10 received, 0% packet loss, time 0ms
+])
+
+# Ensure conntrack entry is present and ct_label is set.
+AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.0.0.4) | \
+sed -e 's/zone=[[0-9]]*/zone=<cleared>/' | \
+sed -e 's/labels=0x4d2[[0-9a-f]]*/labels=0x4d2000000000000000000000000/'], [0], [dnl
+icmp,orig=(src=10.0.0.2,dst=10.0.0.4,id=<cleared>,type=8,code=0),reply=(src=10.0.0.4,dst=10.0.0.2,id=<cleared>,type=0,code=0),zone=<cleared>,labels=0x4d2000000000000000000000000
+icmp,orig=(src=10.0.0.2,dst=10.0.0.4,id=<cleared>,type=8,code=0),reply=(src=10.0.0.4,dst=10.0.0.2,id=<cleared>,type=0,code=0),zone=<cleared>
+])
+
+AT_CHECK([ovs-appctl dpctl/flush-conntrack])
+# 'sw0-p3' should be able to ping 'sw0-p1'.
+NS_CHECK_EXEC([sw0-p3], [ping -q -c 10 -i 0.3 -w 15 10.0.0.2 | FORMAT_PING], \
+[0], [dnl
+10 packets transmitted, 10 received, 0% packet loss, time 0ms
+])
+
+# Ensure conntrack entry is present and ct_label is set.
+AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.0.0.2) | \
+sed -e 's/zone=[[0-9]]*/zone=<cleared>/' | \
+sed -e 's/labels=0x4d3[[0-9a-f]]*/labels=0x4d3000000000000000000000000/'], [0], [dnl
+icmp,orig=(src=10.0.0.4,dst=10.0.0.2,id=<cleared>,type=8,code=0),reply=(src=10.0.0.2,dst=10.0.0.4,id=<cleared>,type=0,code=0),zone=<cleared>,labels=0x4d3000000000000000000000000
+icmp,orig=(src=10.0.0.4,dst=10.0.0.2,id=<cleared>,type=8,code=0),reply=(src=10.0.0.2,dst=10.0.0.4,id=<cleared>,type=0,code=0),zone=<cleared>
+])
+
+AT_CHECK([ovs-appctl dpctl/flush-conntrack])
+# 'sw0-p1' should be able to ping 'sw0-p2'.
+NS_CHECK_EXEC([sw0-p1], [ping -q -c 10 -i 0.3 -w 15 10.0.0.3 | FORMAT_PING], \
+[0], [dnl
+10 packets transmitted, 10 received, 0% packet loss, time 0ms
+])
+
+# Ensure conntrack entry is present and ct_label is not set.
+AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.0.0.3) | \
+sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
+icmp,orig=(src=10.0.0.2,dst=10.0.0.3,id=<cleared>,type=8,code=0),reply=(src=10.0.0.3,dst=10.0.0.2,id=<cleared>,type=0,code=0),zone=<cleared>
+icmp,orig=(src=10.0.0.2,dst=10.0.0.3,id=<cleared>,type=8,code=0),reply=(src=10.0.0.3,dst=10.0.0.2,id=<cleared>,type=0,code=0),zone=<cleared>
+])
+
+AT_CHECK([ovs-appctl dpctl/flush-conntrack])
+# 'sw0-p2' should be able to ping 'sw0-p1'.
+NS_CHECK_EXEC([sw0-p2], [ping -q -c 10 -i 0.3 -w 15 10.0.0.2 | FORMAT_PING], \
+[0], [dnl
+10 packets transmitted, 10 received, 0% packet loss, time 0ms
+])
+
+# Ensure conntrack entry is present and ct_label is not set.
+AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.0.0.2) | \
+sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
+icmp,orig=(src=10.0.0.3,dst=10.0.0.2,id=<cleared>,type=8,code=0),reply=(src=10.0.0.2,dst=10.0.0.3,id=<cleared>,type=0,code=0),zone=<cleared>
+icmp,orig=(src=10.0.0.3,dst=10.0.0.2,id=<cleared>,type=8,code=0),reply=(src=10.0.0.2,dst=10.0.0.3,id=<cleared>,type=0,code=0),zone=<cleared>
+])
+
+OVS_APP_EXIT_AND_WAIT([ovn-controller])
+
+as ovn-sb
+OVS_APP_EXIT_AND_WAIT([ovsdb-server])
+
+as ovn-nb
+OVS_APP_EXIT_AND_WAIT([ovsdb-server])
+
+as northd
+OVS_APP_EXIT_AND_WAIT([NORTHD_TYPE])
+
+as
+OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d
+/connection dropped.*/d"])
+
+AT_CLEANUP
+])
+
+OVN_FOR_EACH_NORTHD([
+AT_SETUP([ACL label - conntrack label change])
+AT_KEYWORDS([acl label ct_commit label change])
+
+CHECK_CONNTRACK()
+ovn_start
+
+OVS_TRAFFIC_VSWITCHD_START()
+ADD_BR([br-int])
+
+# Set external-ids in br-int needed for ovn-controller
+ovs-vsctl \
+ -- set Open_vSwitch . external-ids:system-id=hv1 \
+ -- set Open_vSwitch . external-ids:ovn-remote=unix:$ovs_base/ovn-sb/ovn-sb.sock \
+ -- set Open_vSwitch . external-ids:ovn-encap-type=geneve \
+ -- set Open_vSwitch . external-ids:ovn-encap-ip=169.0.0.1 \
+ -- set bridge br-int fail-mode=secure other-config:disable-in-band=true
+
+# Start ovn-controller
+start_daemon ovn-controller
+
+check ovn-nbctl ls-add sw0
+
+check ovn-nbctl lsp-add sw0 sw0-p1
+check ovn-nbctl lsp-set-addresses sw0-p1 "50:54:00:00:00:02 10.0.0.2"
+check ovn-nbctl lsp-set-port-security sw0-p1 "50:54:00:00:00:02 10.0.0.2"
+
+check ovn-nbctl lsp-add sw0 sw0-p2
+check ovn-nbctl lsp-set-addresses sw0-p2 "50:54:00:00:00:03 10.0.0.3"
+check ovn-nbctl lsp-set-port-security sw0-p2 "50:54:00:00:00:03 10.0.0.3"
+
+# ACLs
+# sw0-p1 ---> sw0-p2 allowed, label=1234
+
+check ovn-nbctl --label=1234 acl-add sw0 from-lport 1002 'ip4 && inport == "sw0-p1" && ip4.dst == 10.0.0.3' allow-related
+
+ADD_NAMESPACES(sw0-p1)
+ADD_VETH(sw0-p1, sw0-p1, br-int, "10.0.0.2/24", "50:54:00:00:00:02", \
+ "10.0.0.1")
+ADD_NAMESPACES(sw0-p2)
+ADD_VETH(sw0-p2, sw0-p2, br-int, "10.0.0.3/24", "50:54:00:00:00:03", \
+ "10.0.0.1")
+
+# Ensure ovn-controller is caught up
+ovn-nbctl --wait=hv sync
+
+on_exit 'ovn-nbctl acl-list sw0'
+on_exit 'ovn-sbctl lflow-list'
+on_exit 'ovs-ofctl dump-flows br-int'
+
+wait_for_ports_up
+
+AT_CHECK([ovs-appctl dpctl/flush-conntrack])
+
+# start a background ping for ~30 secs.
+NETNS_DAEMONIZE([sw0-p1], [[ping -q -c 100 -i 0.3 -w 15 10.0.0.3]], [ns-sw0-p1.pid])
+
+sleep 3s
+
+# Ensure conntrack entry is present and ct_label is set.
+AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.0.0.3) | \
+sed -e 's/zone=[[0-9]]*/zone=<cleared>/' | \
+sed -e 's/labels=0x4d2[[0-9a-f]]*/labels=0x4d2000000000000000000000000/'], [0], [dnl
+icmp,orig=(src=10.0.0.2,dst=10.0.0.3,id=<cleared>,type=8,code=0),reply=(src=10.0.0.3,dst=10.0.0.2,id=<cleared>,type=0,code=0),zone=<cleared>,labels=0x4d2000000000000000000000000
+icmp,orig=(src=10.0.0.2,dst=10.0.0.3,id=<cleared>,type=8,code=0),reply=(src=10.0.0.3,dst=10.0.0.2,id=<cleared>,type=0,code=0),zone=<cleared>
+])
+
+# Add a higher priority ACL with different label.
+# This ACL also allows the ping running in background.
+
+check ovn-nbctl --label=1235 acl-add sw0 from-lport 1003 'ip4 && inport == "sw0-p1" && ip4.dst == 10.0.0.3' allow-related
+ovn-nbctl --wait=hv sync
+
+sleep 3s
+
+# Ensure conntrack entry is updated with new ct_label is set.
+AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.0.0.3) | \
+sed -e 's/zone=[[0-9]]*/zone=<cleared>/' | \
+sed -e 's/labels=0x4d3[[0-9a-f]]*/labels=0x4d3000000000000000000000000/'], [0], [dnl
+icmp,orig=(src=10.0.0.2,dst=10.0.0.3,id=<cleared>,type=8,code=0),reply=(src=10.0.0.3,dst=10.0.0.2,id=<cleared>,type=0,code=0),zone=<cleared>,labels=0x4d3000000000000000000000000
+icmp,orig=(src=10.0.0.2,dst=10.0.0.3,id=<cleared>,type=8,code=0),reply=(src=10.0.0.3,dst=10.0.0.2,id=<cleared>,type=0,code=0),zone=<cleared>
+])
+
+OVS_APP_EXIT_AND_WAIT([ovn-controller])
+
+as ovn-sb
+OVS_APP_EXIT_AND_WAIT([ovsdb-server])
+
+as ovn-nb
+OVS_APP_EXIT_AND_WAIT([ovsdb-server])
+
+as northd
+OVS_APP_EXIT_AND_WAIT([NORTHD_TYPE])
+
+as
+OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d
+/connection dropped.*/d"])
+
AT_CLEANUP
])
@@ -399,7 +399,7 @@
must be either <code>switch</code> or <code>port-group</code>.
</p>
<dl>
- <dt>[<code>--type=</code>{<code>switch</code> | <code>port-group</code>}] [<code>--log</code>] [<code>--meter=</code><var>meter</var>] [<code>--severity=</code><var>severity</var>] [<code>--name=</code><var>name</var>] [<code>--may-exist</code>] <code>acl-add</code> <var>entity</var> <var>direction</var> <var>priority</var> <var>match</var> <var>verdict</var></dt>
+ <dt>[<code>--type=</code>{<code>switch</code> | <code>port-group</code>}] [<code>--log</code>] [<code>--meter=</code><var>meter</var>] [<code>--severity=</code><var>severity</var>] [<code>--name=</code><var>name</var>] [<code>--label=</code><var>label</var>] [<code>--may-exist</code>] <code>acl-add</code> <var>entity</var> <var>direction</var> <var>priority</var> <var>match</var> <var>verdict</var></dt>
<dd>
<p>
Adds the specified ACL to <var>entity</var>. <var>direction</var>
@@ -2011,6 +2011,9 @@ nbctl_acl_list(struct ctl_context *ctx)
ds_chomp(&ctx->output, ',');
ds_put_cstr(&ctx->output, ")");
}
+ if (acl->label) {
+ ds_put_format(&ctx->output, " label=%"PRId64, acl->label);
+ }
ds_put_cstr(&ctx->output, "\n");
}
@@ -2069,6 +2072,19 @@ parse_priority(const char *arg, int64_t *priority_p)
return NULL;
}
+static char * OVS_WARN_UNUSED_RESULT
+parse_acl_label(const char *arg, int64_t *label_p)
+{
+ /* Validate label. */
+ int64_t label;
+ if (!ovs_scan(arg, "%"SCNd64, &label)
+ || label < 0 || label > UINT32_MAX) {
+ return xasprintf("%s: label must in range 0...4294967295", arg);
+ }
+ *label_p = label;
+ return NULL;
+}
+
static void
nbctl_pre_acl(struct ctl_context *ctx)
{
@@ -2093,6 +2109,7 @@ nbctl_pre_acl_list(struct ctl_context *ctx)
ovsdb_idl_add_column(ctx->idl, &nbrec_acl_col_name);
ovsdb_idl_add_column(ctx->idl, &nbrec_acl_col_severity);
ovsdb_idl_add_column(ctx->idl, &nbrec_acl_col_meter);
+ ovsdb_idl_add_column(ctx->idl, &nbrec_acl_col_label);
}
static void
@@ -2160,6 +2177,25 @@ nbctl_acl_add(struct ctl_context *ctx)
nbrec_acl_set_meter(acl, meter);
}
+ /* Set the ACL label */
+ const char *label = shash_find_data(&ctx->options, "--label");
+ if (label) {
+ /* Ensure that the action is either allow or allow-related */
+ if (strcmp(action, "allow") && strcmp(action, "allow-related")) {
+ ctl_error(ctx, "label can only be set with actions \"allow\" or "
+ "\"allow-related\"");
+ return;
+ }
+
+ int64_t label_value = 0;
+ error = parse_acl_label(label, &label_value);
+ if (error) {
+ ctx->error = error;
+ return;
+ }
+ nbrec_acl_set_label(acl, label_value);
+ }
+
/* Check if same acl already exists for the ls/portgroup */
size_t n_acls = pg ? pg->n_acls : ls->n_acls;
struct nbrec_acl **acls = pg ? pg->acls : ls->acls;
@@ -6757,7 +6793,7 @@ static const struct ctl_command_syntax nbctl_commands[] = {
/* acl commands. */
{ "acl-add", 5, 6, "{SWITCH | PORTGROUP} DIRECTION PRIORITY MATCH ACTION",
nbctl_pre_acl, nbctl_acl_add, NULL,
- "--log,--may-exist,--type=,--name=,--severity=,--meter=", RW },
+ "--log,--may-exist,--type=,--name=,--severity=,--meter=,--label=", RW },
{ "acl-del", 1, 4, "{SWITCH | PORTGROUP} [DIRECTION [PRIORITY MATCH]]",
nbctl_pre_acl, nbctl_acl_del, NULL, "--type=", RW },
{ "acl-list", 1, 1, "{SWITCH | PORTGROUP}",