diff mbox series

[ovs-dev,v2,2/3] actions: Add new actions check_in_port_sec and check_out_port_sec.

Message ID 20220519151755.987255-1-numans@ovn.org
State Accepted
Headers show
Series Adding generic port security flows. | expand

Checks

Context Check Description
ovsrobot/apply-robot success apply and check: success
ovsrobot/github-robot-_Build_and_Test fail github build: failed
ovsrobot/github-robot-_ovn-kubernetes success github build: passed

Commit Message

Numan Siddique May 19, 2022, 3:17 p.m. UTC
From: Numan Siddique <numans@ovn.org>

The action - check_in_port_sec runs the port security checks for the
incoming packet from the logical port (inport) and stores the result in a
destination register bit.  If the port security fails, 1 is stored
in the destination register bit.

The action - check_out_port_sec runs the port security checks for the
logical output port (outport) and stores the result in a destination
register bit.  If the port security fails, 1 is stored in the
destination register bit.

Usage:
reg0[1] = check_in_port_sec();
reg1[1] = check_out_port_sec();

The previous commit added the OF rules for the port security in openflow
tables - 73 and 74 for in port security checks and table 75 for out
port security.  These logical actions just translate to resubmitting
to these tables.

Upcoming patch will make use of these logical actions in the logical
switch pipeline.

Reported-at: https://bugzilla.redhat.com/show_bug.cgi?id=2078927
Suggested-by: Dumitru Ceara <dceara@redhat.com>
Acked-by: Mark Michelson <mmichels@redhat.com>
Signed-off-by: Numan Siddique <numans@ovn.org>
---
 include/ovn/actions.h |   2 +
 lib/actions.c         |  75 ++++++++--
 ovn-sb.xml            |  39 ++++++
 tests/ovn.at          |  32 +++++
 tests/test-ovn.c      |   2 +
 utilities/ovn-trace.c | 313 ++++++++++++++++++++++++++++++++++++++++++
 6 files changed, 455 insertions(+), 8 deletions(-)
diff mbox series

Patch

diff --git a/include/ovn/actions.h b/include/ovn/actions.h
index 49a2e26124..1ae496960e 100644
--- a/include/ovn/actions.h
+++ b/include/ovn/actions.h
@@ -116,6 +116,8 @@  struct ovn_extend_table;
     OVNACT(PUT_FDB,           ovnact_put_fdb)         \
     OVNACT(GET_FDB,           ovnact_get_fdb)         \
     OVNACT(LOOKUP_FDB,        ovnact_lookup_fdb)      \
+    OVNACT(CHECK_IN_PORT_SEC,  ovnact_result)         \
+    OVNACT(CHECK_OUT_PORT_SEC, ovnact_result)         \
 
 /* enum ovnact_type, with a member OVNACT_<ENUM> for each action. */
 enum OVS_PACKED_ENUM ovnact_type {
diff --git a/lib/actions.c b/lib/actions.c
index a9c27600c7..36d4a33ae5 100644
--- a/lib/actions.c
+++ b/lib/actions.c
@@ -4004,19 +4004,20 @@  format_CHK_LB_HAIRPIN_REPLY(const struct ovnact_result *res, struct ds *s)
 }
 
 static void
-encode_chk_lb_hairpin__(const struct ovnact_result *res,
-                        uint8_t hairpin_table,
-                        struct ofpbuf *ofpacts)
+encode_result_action__(const struct ovnact_result *res,
+                       uint8_t resubmit_table,
+                       int log_flags_result_bit,
+                       struct ofpbuf *ofpacts)
 {
     struct mf_subfield dst = expr_resolve_field(&res->dst);
     ovs_assert(dst.field);
-    put_load(0, MFF_LOG_FLAGS, MLF_LOOKUP_LB_HAIRPIN_BIT, 1, ofpacts);
-    emit_resubmit(ofpacts, hairpin_table);
+    put_load(0, MFF_LOG_FLAGS, log_flags_result_bit, 1, ofpacts);
+    emit_resubmit(ofpacts, resubmit_table);
 
     struct ofpact_reg_move *orm = ofpact_put_REG_MOVE(ofpacts);
     orm->dst = dst;
     orm->src.field = mf_from_id(MFF_LOG_FLAGS);
-    orm->src.ofs = MLF_LOOKUP_LB_HAIRPIN_BIT;
+    orm->src.ofs = log_flags_result_bit;
     orm->src.n_bits = 1;
 }
 
@@ -4025,7 +4026,8 @@  encode_CHK_LB_HAIRPIN(const struct ovnact_result *res,
                       const struct ovnact_encode_params *ep,
                       struct ofpbuf *ofpacts)
 {
-    encode_chk_lb_hairpin__(res, ep->lb_hairpin_ptable, ofpacts);
+    encode_result_action__(res, ep->lb_hairpin_ptable,
+                           MLF_LOOKUP_LB_HAIRPIN_BIT, ofpacts);
 }
 
 static void
@@ -4033,7 +4035,8 @@  encode_CHK_LB_HAIRPIN_REPLY(const struct ovnact_result *res,
                             const struct ovnact_encode_params *ep,
                             struct ofpbuf *ofpacts)
 {
-    encode_chk_lb_hairpin__(res, ep->lb_hairpin_reply_ptable, ofpacts);
+    encode_result_action__(res, ep->lb_hairpin_reply_ptable,
+                           MLF_LOOKUP_LB_HAIRPIN_BIT, ofpacts);
 }
 
 static void
@@ -4216,6 +4219,54 @@  ovnact_lookup_fdb_free(struct ovnact_lookup_fdb *get_fdb OVS_UNUSED)
 {
 }
 
+static void
+parse_check_in_port_sec(struct action_context *ctx,
+                        const struct expr_field *dst,
+                        struct ovnact_result *dl)
+{
+    parse_ovnact_result(ctx, "check_in_port_sec", NULL, dst, dl);
+}
+
+static void
+format_CHECK_IN_PORT_SEC(const struct ovnact_result *dl, struct ds *s)
+{
+    expr_field_format(&dl->dst, s);
+    ds_put_cstr(s, " = check_in_port_sec();");
+}
+
+static void
+encode_CHECK_IN_PORT_SEC(const struct ovnact_result *dl,
+                         const struct ovnact_encode_params *ep,
+                         struct ofpbuf *ofpacts)
+{
+    encode_result_action__(dl, ep->in_port_sec_ptable,
+                           MLF_CHECK_PORT_SEC_BIT, ofpacts);
+}
+
+static void
+parse_check_out_port_sec(struct action_context *ctx,
+                         const struct expr_field *dst,
+                         struct ovnact_result *dl)
+{
+    parse_ovnact_result(ctx, "check_out_port_sec", NULL, dst, dl);
+}
+
+static void
+format_CHECK_OUT_PORT_SEC(const struct ovnact_result *dl, struct ds *s)
+{
+    expr_field_format(&dl->dst, s);
+    ds_put_cstr(s, " = check_out_port_sec();");
+}
+
+static void
+encode_CHECK_OUT_PORT_SEC(const struct ovnact_result *dl,
+                         const struct ovnact_encode_params *ep,
+                         struct ofpbuf *ofpacts)
+{
+    encode_result_action__(dl, ep->out_port_sec_ptable,
+                           MLF_CHECK_PORT_SEC_BIT, ofpacts);
+}
+
 /* Parses an assignment or exchange or put_dhcp_opts action. */
 static void
 parse_set_action(struct action_context *ctx)
@@ -4284,6 +4335,14 @@  parse_set_action(struct action_context *ctx)
                    && lexer_lookahead(ctx->lexer) == LEX_T_LPAREN) {
             parse_lookup_fdb(
                 ctx, &lhs, ovnact_put_LOOKUP_FDB(ctx->ovnacts));
+        } else if (!strcmp(ctx->lexer->token.s, "check_in_port_sec")
+                   && lexer_lookahead(ctx->lexer) == LEX_T_LPAREN) {
+            parse_check_in_port_sec(
+                ctx, &lhs, ovnact_put_CHECK_IN_PORT_SEC(ctx->ovnacts));
+        } else if (!strcmp(ctx->lexer->token.s, "check_out_port_sec")
+                   && lexer_lookahead(ctx->lexer) == LEX_T_LPAREN) {
+            parse_check_out_port_sec(
+                ctx, &lhs, ovnact_put_CHECK_OUT_PORT_SEC(ctx->ovnacts));
         } else {
             parse_assignment_action(ctx, false, &lhs);
         }
diff --git a/ovn-sb.xml b/ovn-sb.xml
index 086edddef4..4c35dda366 100644
--- a/ovn-sb.xml
+++ b/ovn-sb.xml
@@ -2514,6 +2514,45 @@  tcp.flags = RST;
             action to advance the packet to the next stage.
           </p>
         </dd>
+
+        <dt><code><var>R</var> = check_in_port_sec();</code></dt>
+        <dd>
+          <p>
+            This action checks if the packet under consideration passes the
+            inport port security checks.  If the packet fails the port security
+            checks, then <code>1</code> is stored in the destination register
+            <var>R</var>.  Else 0 is stored.  The port security values to check
+            are retrieved from the the <code>inport</code> logical port.
+          </p>
+
+          <p>
+            This action should be used in the ingress logical switch pipeline.
+
+          </p>
+          <p>
+            <b>Example:</b> <code>reg8[0..7] = check_in_port_sec();</code>
+          </p>
+        </dd>
+
+        <dt><code><var>R</var> = check_out_port_sec();</code></dt>
+        <dd>
+          <p>
+            This action checks if the packet under consideration passes the
+            outport port security checks.  If the packet fails the port
+            security checks, then <code>1</code> is stored in the destination
+            register <var>R</var>.  Else 0 is stored.  The port security
+            values to check are retrieved from the the <code>outport</code>
+            logical port.
+          </p>
+
+          <p>
+            This action should be used in the egress logical switch pipeline.
+
+          </p>
+          <p>
+            <b>Example:</b> <code>reg8[0..7] = check_out_port_sec();</code>
+          </p>
+        </dd>
       </dl>
     </column>
 
diff --git a/tests/ovn.at b/tests/ovn.at
index e3d65b1cce..45cc6d5483 100644
--- a/tests/ovn.at
+++ b/tests/ovn.at
@@ -2037,6 +2037,38 @@  reg1[1] = lookup_fdb(outport, ip4.src);
 reg1[1] = lookup_fdb(ip4.src, eth.src);
     Cannot use numeric field ip4.src where string field is required.
 
+# check_in_port_sec
+reg0[0] = check_in_port_sec();
+    encodes as set_field:0/0x1000->reg10,resubmit(,73),move:NXM_NX_REG10[12]->NXM_NX_XXREG0[96]
+
+reg2[2] = check_in_port_sec();
+    encodes as set_field:0/0x1000->reg10,resubmit(,73),move:NXM_NX_REG10[12]->NXM_NX_XXREG0[34]
+
+reg0 = check_in_port_sec();
+    Cannot use 32-bit field reg0[0..31] where 1-bit field is required.
+
+reg0[0] = check_in_port_sec(foo);
+    check_in_port_sec doesn't take any parameters
+
+check_in_port_sec;
+    Syntax error at `check_in_port_sec' expecting action.
+
+# check_out_port_sec
+reg0[0] = check_out_port_sec();
+    encodes as set_field:0/0x1000->reg10,resubmit(,75),move:NXM_NX_REG10[12]->NXM_NX_XXREG0[96]
+
+reg2[2..3] = check_out_port_sec();
+    Cannot use 2-bit field reg2[2..3] where 1-bit field is required.
+
+reg0 = check_out_port_sec();
+    Cannot use 32-bit field reg0[0..31] where 1-bit field is required.
+
+reg0[0] = check_out_port_sec(foo);
+    check_out_port_sec doesn't take any parameters
+
+check_out_port_sec;
+    Syntax error at `check_out_port_sec' expecting action.
+
 # push/pop
 push(xxreg0);push(xxreg1[10..20]);push(eth.src);pop(xxreg0[0..47]);pop(xxreg0[48..57]);pop(xxreg1);
     formats as push(xxreg0); push(xxreg1[10..20]); push(eth.src); pop(xxreg0[0..47]); pop(xxreg0[48..57]); pop(xxreg1);
diff --git a/tests/test-ovn.c b/tests/test-ovn.c
index 6704f612bf..2a84f79ede 100644
--- a/tests/test-ovn.c
+++ b/tests/test-ovn.c
@@ -1352,6 +1352,8 @@  test_parse_actions(struct ovs_cmdl_context *ctx OVS_UNUSED)
                 .fdb_ptable = OFTABLE_GET_FDB,
                 .fdb_lookup_ptable = OFTABLE_LOOKUP_FDB,
                 .common_nat_ct_zone = MFF_LOG_DNAT_ZONE,
+                .in_port_sec_ptable = OFTABLE_CHK_IN_PORT_SEC,
+                .out_port_sec_ptable = OFTABLE_CHK_OUT_PORT_SEC,
             };
             struct ofpbuf ofpacts;
             ofpbuf_init(&ofpacts, 0);
diff --git a/utilities/ovn-trace.c b/utilities/ovn-trace.c
index 6abd9a83e4..c4110de0ac 100644
--- a/utilities/ovn-trace.c
+++ b/utilities/ovn-trace.c
@@ -431,6 +431,8 @@  struct ovntrace_port {
     uint16_t tunnel_key;
     struct ovntrace_port *peer; /* Patch ports only. */
     struct ovntrace_port *distributed_port; /* chassisredirect ports only. */
+    struct lport_addresses *ps_addrs;   /* Port security addresses. */
+    size_t n_ps_addrs;
 };
 
 struct ovntrace_mcgroup {
@@ -747,6 +749,22 @@  read_ports(void)
                 }
             }
         }
+
+        port->n_ps_addrs = 0;
+        port->ps_addrs =
+            xmalloc(sizeof *port->ps_addrs * sbpb->n_port_security);
+        for (size_t i = 0; i < sbpb->n_port_security; i++) {
+            if (!extract_lsp_addresses(sbpb->port_security[i],
+                                        &port->ps_addrs[port->n_ps_addrs])) {
+                static struct vlog_rate_limit rl
+                    = VLOG_RATE_LIMIT_INIT(1, 1);
+                VLOG_INFO_RL(&rl, "invalid syntax '%s' in port "
+                            "security. No MAC address found",
+                            sbpb->port_security[i]);
+                continue;
+            }
+            port->n_ps_addrs++;
+        }
     }
 
     SBREC_PORT_BINDING_FOR_EACH (sbpb, ovnsb_idl) {
@@ -2621,6 +2639,291 @@  execute_ct_snat_to_vip(struct flow *uflow OVS_UNUSED, struct ovs_list *super)
                          "*** ct_snat_to_vip action not implemented");
 }
 
+static bool
+check_in_port_sec_arp(const struct flow *uflow,
+                      struct lport_addresses *ps_addr)
+{
+    /* arp.sha should match the ps_addr's ea. */
+    if (!eth_addr_equals(uflow->arp_sha, ps_addr->ea)) {
+        return false;
+    }
+
+    if (!ps_addr->n_ipv4_addrs) {
+        return true;
+    }
+
+    /* arp.spa should match the allowed IPv4 addresses. */
+    for (size_t i = 0; i < ps_addr->n_ipv4_addrs; i++) {
+        if (uflow->nw_src == ps_addr->ipv4_addrs[i].addr) {
+            return true;
+        }
+    }
+
+    return false;
+}
+
+static bool
+check_in_port_sec_ip4(const struct flow *uflow,
+                      struct lport_addresses *ps_addr)
+{
+    /* No IPs present in ps_addr.  Allow all IPs. */
+    if (!ps_addr->n_ipv4_addrs && !ps_addr->n_ipv6_addrs) {
+        return true;
+    }
+
+    /* ip4.src should match the allowed IPv4 addresses. */
+    for (size_t i = 0; i < ps_addr->n_ipv4_addrs; i++) {
+        if (uflow->nw_src == ps_addr->ipv4_addrs[i].addr) {
+            return true;
+        }
+    }
+
+    /* If its dhcp packet, then the ip4.src == 0.0.0.0 is allowed if
+     * ip4.dst == 255.255.255.255. */
+    if (uflow->nw_proto == IPPROTO_UDP && uflow->tp_src == htons(68) &&
+            uflow->tp_dst == htons(67) && uflow->nw_src == htonl(0) &&
+            uflow->nw_dst == htonl(0xffffffff)) {
+        return true;
+    }
+
+    return false;
+}
+
+static bool
+check_in_port_sec_ip6(const struct flow *uflow,
+                      struct lport_addresses *ps_addr)
+{
+    /* No IPs present in ps_addr.  Allow all IPs. */
+    if (!ps_addr->n_ipv4_addrs && !ps_addr->n_ipv6_addrs) {
+        return true;
+    }
+
+    /* ip6.src should match the allowed IPv6 addresses. */
+    bool passed = false;
+    for (size_t i = 0; i < ps_addr->n_ipv6_addrs; i++) {
+        if (ipv6_addr_equals(&uflow->ipv6_src, &ps_addr->ipv6_addrs[i].addr)) {
+            passed = true;
+            break;
+        }
+    }
+
+    struct in6_addr lla;
+    in6_generate_lla(ps_addr->ea, &lla);
+
+    if (!passed && !ipv6_addr_equals(&uflow->ipv6_src, &lla)) {
+        return false;
+    }
+
+    if (uflow->nw_proto == IPPROTO_ICMPV6) {
+        if (ntohs(uflow->tp_src) == 135) {
+            if (!eth_addr_equals(uflow->arp_sha, eth_addr_zero) &&
+                 !eth_addr_equals(uflow->arp_sha, ps_addr->ea)) {
+                return false;
+            }
+        }
+
+        if (ntohs(uflow->tp_src) == 136) {
+            if (!eth_addr_equals(uflow->arp_tha, eth_addr_zero) &&
+                 !eth_addr_equals(uflow->arp_tha, ps_addr->ea)) {
+                return false;
+            }
+
+            if (ps_addr->n_ipv6_addrs) {
+                passed = false;
+                for (size_t i = 0; i < ps_addr->n_ipv6_addrs; i++) {
+                    if (ipv6_addr_equals(&uflow->nd_target,
+                                         &ps_addr->ipv6_addrs[i].addr)) {
+                        passed = true;
+                        break;
+                    }
+                }
+
+                if (!passed && !ipv6_addr_equals(&uflow->nd_target, &lla)) {
+                    return false;
+                }
+            }
+        }
+    }
+
+    return true;
+}
+
+static void
+execute_check_in_port_sec(const struct ovnact_result *dl,
+                          const struct ovntrace_datapath *dp,
+                          struct flow *uflow)
+{
+    struct mf_subfield sf = expr_resolve_field(&dl->dst);
+    union mf_subvalue sv = { .u8_val = 1 };
+
+    /* Get the input port .*/
+    uint32_t in_key = uflow->regs[MFF_LOG_INPORT - MFF_REG0];
+    ovs_assert(in_key);
+    const struct ovntrace_port *inport = ovntrace_port_find_by_key(dp, in_key);
+    ovs_assert(inport);
+
+    /* No port security is enabled on the input port.
+     * Set result bit 'sv' to 0. */
+    if (!inport->n_ps_addrs) {
+        sv.u8_val = 0;
+        mf_write_subfield_flow(&sf, &sv, uflow);
+        return;
+    }
+
+    bool allow = false;
+    for (size_t i = 0; i < inport->n_ps_addrs; i++) {
+        struct lport_addresses *ps_addr = &inport->ps_addrs[i];
+
+        /* Check L2 first. */
+        if (!eth_addr_equals(uflow->dl_src, ps_addr->ea)) {
+            continue;
+        }
+
+        if (uflow->dl_type == htons(ETH_TYPE_IP)) {
+            allow = check_in_port_sec_ip4(uflow, ps_addr);
+        } else if (uflow->dl_type == htons(ETH_TYPE_ARP)) {
+            allow = check_in_port_sec_arp(uflow, ps_addr);
+        } else if (uflow->dl_type == htons(ETH_TYPE_IPV6)) {
+            allow = check_in_port_sec_ip6(uflow, ps_addr);
+        } else {
+            allow = true;
+        }
+        break;
+    }
+
+    if (allow) {
+        sv.u8_val = 0;
+    }
+
+    mf_write_subfield_flow(&sf, &sv, uflow);
+}
+
+static bool
+check_out_port_sec_ip4(const struct flow *uflow,
+                       struct lport_addresses *ps_addr)
+{
+    /* No IPs present in ps_addr.  Allow all IPs. */
+    if (!ps_addr->n_ipv4_addrs && !ps_addr->n_ipv6_addrs) {
+        return true;
+    }
+
+    if (!ps_addr->n_ipv4_addrs) {
+        /* Only IPv6 is allowed. */
+        return false;
+    }
+
+    /* ip4.dst should match from the allowed IPv4 addresses. */
+    for (size_t i = 0; i < ps_addr->n_ipv4_addrs; i++) {
+        if (uflow->nw_dst == ps_addr->ipv4_addrs[i].addr) {
+            return true;
+        }
+    }
+
+    if (uflow->nw_dst == htonl(0xffffffff)) {
+        return true;
+    }
+
+    ovs_be32 mcast_network, mcast_mask;
+    mcast_network = htonl(0xe0000000);
+    mcast_mask = htonl(0xf0000000);
+    if (!((mcast_network ^ uflow->nw_dst) & mcast_mask)) {
+        return true;
+    }
+
+    return false;
+}
+
+static bool
+check_out_port_sec_ip6(const struct flow *uflow,
+                       struct lport_addresses *ps_addr)
+{
+    /* No IPs present in ps_addr.  Allow all IPs. */
+    if (!ps_addr->n_ipv4_addrs && !ps_addr->n_ipv6_addrs) {
+        return true;
+    }
+
+    if (!ps_addr->n_ipv6_addrs) {
+        /* Only IPv4 is allowed. */
+        return false;
+    }
+
+    /* ip6.dst should match from the allowed IPv6 addresses. */
+    for (size_t i = 0; i < ps_addr->n_ipv6_addrs; i++) {
+        if (ipv6_addr_equals(&uflow->ipv6_dst, &ps_addr->ipv6_addrs[i].addr)) {
+            return true;
+        }
+    }
+
+    struct in6_addr lla;
+    in6_generate_lla(ps_addr->ea, &lla);
+
+    if (ipv6_addr_equals(&uflow->ipv6_dst, &lla)) {
+        return true;
+    }
+
+    struct in6_addr mcast6_network, mcast6_mask;
+    char *error = ipv6_parse_masked("ff00::/8", &mcast6_network, &mcast6_mask);
+    ovs_assert(!error);
+
+    struct in6_addr ip6_mask = ipv6_addr_bitxor(&uflow->ipv6_dst,
+                                                &mcast6_network);
+    ip6_mask = ipv6_addr_bitand(&ip6_mask, &mcast6_mask);
+    if (ipv6_mask_is_any(&ip6_mask)) {
+        return true;
+    }
+
+    return false;
+}
+
+static void
+execute_check_out_port_sec(const struct ovnact_result *dl,
+                           const struct ovntrace_datapath *dp,
+                           struct flow *uflow)
+{
+    struct mf_subfield sf = expr_resolve_field(&dl->dst);
+    union mf_subvalue sv = { .u8_val = 1 };
+
+    /* Get the output port .*/
+    uint32_t out_key = uflow->regs[MFF_LOG_OUTPORT - MFF_REG0];
+    ovs_assert(out_key);
+    const struct ovntrace_port *outport =
+        ovntrace_port_find_by_key(dp, out_key);
+    ovs_assert(outport);
+
+    /* No port security is enabled on the output port.
+     * Set result bit 'sv' to 0. */
+    if (!outport->n_ps_addrs) {
+        sv.u8_val = 0;
+        mf_write_subfield_flow(&sf, &sv, uflow);
+        return;
+    }
+
+    bool allow = false;
+    for (size_t i = 0; i < outport->n_ps_addrs; i++) {
+        struct lport_addresses *ps_addr = &outport->ps_addrs[i];
+
+        /* Check L2 first. */
+        if (!eth_addr_equals(uflow->dl_dst, ps_addr->ea)) {
+            continue;
+        }
+
+        if (uflow->dl_type == htons(ETH_TYPE_IP)) {
+            allow = check_out_port_sec_ip4(uflow, ps_addr);
+        } else if (uflow->dl_type == htons(ETH_TYPE_IPV6)) {
+            allow = check_out_port_sec_ip6(uflow, ps_addr);
+        } else {
+            allow = true;
+        }
+        break;
+    }
+
+    if (allow) {
+        sv.u8_val = 0;
+    }
+
+    mf_write_subfield_flow(&sf, &sv, uflow);
+}
+
 static void
 trace_actions(const struct ovnact *ovnacts, size_t ovnacts_len,
               const struct ovntrace_datapath *dp, struct flow *uflow,
@@ -2911,6 +3214,16 @@  trace_actions(const struct ovnact *ovnacts, size_t ovnacts_len,
         case OVNACT_LOOKUP_FDB:
             execute_lookup_fdb(ovnact_get_LOOKUP_FDB(a), dp, uflow, super);
             break;
+
+        case OVNACT_CHECK_IN_PORT_SEC:
+            execute_check_in_port_sec(ovnact_get_CHECK_IN_PORT_SEC(a),
+                                      dp, uflow);
+            break;
+
+        case OVNACT_CHECK_OUT_PORT_SEC:
+            execute_check_out_port_sec(ovnact_get_CHECK_OUT_PORT_SEC(a),
+                                       dp, uflow);
+            break;
         }
     }
     ofpbuf_uninit(&stack);