From patchwork Thu May 19 15:17:55 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Numan Siddique X-Patchwork-Id: 1633324 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=openvswitch.org (client-ip=2605:bc80:3010::138; helo=smtp1.osuosl.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=) Received: from smtp1.osuosl.org (smtp1.osuosl.org [IPv6:2605:bc80:3010::138]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by bilbo.ozlabs.org (Postfix) with ESMTPS id 4L3tlt28WSz9t6h for ; Fri, 20 May 2022 01:18:18 +1000 (AEST) Received: from localhost (localhost [127.0.0.1]) by smtp1.osuosl.org (Postfix) with ESMTP id 94912841CB; Thu, 19 May 2022 15:18:12 +0000 (UTC) X-Virus-Scanned: amavisd-new at osuosl.org Received: from smtp1.osuosl.org ([127.0.0.1]) by localhost (smtp1.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id 4XQk0mL0_lCA; Thu, 19 May 2022 15:18:10 +0000 (UTC) Received: from lists.linuxfoundation.org (lf-lists.osuosl.org [IPv6:2605:bc80:3010:104::8cd3:938]) by smtp1.osuosl.org (Postfix) with ESMTPS id D580B8442B; Thu, 19 May 2022 15:18:08 +0000 (UTC) Received: from lf-lists.osuosl.org (localhost [127.0.0.1]) by lists.linuxfoundation.org (Postfix) with ESMTP id A4252C0032; Thu, 19 May 2022 15:18:08 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@lists.linuxfoundation.org Received: from smtp1.osuosl.org (smtp1.osuosl.org [IPv6:2605:bc80:3010::138]) by lists.linuxfoundation.org (Postfix) with ESMTP id 7126BC002D for ; Thu, 19 May 2022 15:18:07 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by smtp1.osuosl.org (Postfix) with ESMTP id 518B3843B0 for ; Thu, 19 May 2022 15:18:04 +0000 (UTC) X-Virus-Scanned: amavisd-new at osuosl.org Received: from smtp1.osuosl.org ([127.0.0.1]) by localhost (smtp1.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id qBPyjg4ZWsKt for ; Thu, 19 May 2022 15:18:03 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.8.0 Received: from relay7-d.mail.gandi.net (relay7-d.mail.gandi.net [IPv6:2001:4b98:dc4:8::227]) by smtp1.osuosl.org (Postfix) with ESMTPS id 86AF684276 for ; Thu, 19 May 2022 15:18:02 +0000 (UTC) Received: (Authenticated sender: numans@ovn.org) by mail.gandi.net (Postfix) with ESMTPSA id E7D2920007; Thu, 19 May 2022 15:17:58 +0000 (UTC) From: numans@ovn.org To: dev@openvswitch.org Date: Thu, 19 May 2022 11:17:55 -0400 Message-Id: <20220519151755.987255-1-numans@ovn.org> X-Mailer: git-send-email 2.35.3 In-Reply-To: <20220519151706.987120-1-numans@ovn.org> References: <20220519151706.987120-1-numans@ovn.org> MIME-Version: 1.0 Cc: Dumitru Ceara Subject: [ovs-dev] [PATCH ovn v2 2/3] actions: Add new actions check_in_port_sec and check_out_port_sec. X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: ovs-dev-bounces@openvswitch.org Sender: "dev" From: Numan Siddique 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 Acked-by: Mark Michelson Signed-off-by: Numan Siddique --- 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 --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_ 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.

+ +
R = check_in_port_sec();
+
+

+ This action checks if the packet under consideration passes the + inport port security checks. If the packet fails the port security + checks, then 1 is stored in the destination register + R. Else 0 is stored. The port security values to check + are retrieved from the the inport logical port. +

+ +

+ This action should be used in the ingress logical switch pipeline. + +

+

+ Example: reg8[0..7] = check_in_port_sec(); +

+
+ +
R = check_out_port_sec();
+
+

+ This action checks if the packet under consideration passes the + outport port security checks. If the packet fails the port + security checks, then 1 is stored in the destination + register R. Else 0 is stored. The port security + values to check are retrieved from the the outport + logical port. +

+ +

+ This action should be used in the egress logical switch pipeline. + +

+

+ Example: reg8[0..7] = check_out_port_sec(); +

+
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);