From patchwork Thu May 19 15:17:39 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Numan Siddique X-Patchwork-Id: 1633323 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=140.211.166.137; helo=smtp4.osuosl.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=) Received: from smtp4.osuosl.org (smtp4.osuosl.org [140.211.166.137]) (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 4L3tlY4VSNz9t6h for ; Fri, 20 May 2022 01:18:01 +1000 (AEST) Received: from localhost (localhost [127.0.0.1]) by smtp4.osuosl.org (Postfix) with ESMTP id 28B0C41B80; Thu, 19 May 2022 15:17:59 +0000 (UTC) X-Virus-Scanned: amavisd-new at osuosl.org Received: from smtp4.osuosl.org ([127.0.0.1]) by localhost (smtp4.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id Kl457envltDi; Thu, 19 May 2022 15:17:56 +0000 (UTC) Received: from lists.linuxfoundation.org (lf-lists.osuosl.org [140.211.9.56]) by smtp4.osuosl.org (Postfix) with ESMTPS id C90A341781; Thu, 19 May 2022 15:17:55 +0000 (UTC) Received: from lf-lists.osuosl.org (localhost [127.0.0.1]) by lists.linuxfoundation.org (Postfix) with ESMTP id 9A31EC0032; Thu, 19 May 2022 15:17:55 +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 C4DCDC002D for ; Thu, 19 May 2022 15:17:53 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by smtp1.osuosl.org (Postfix) with ESMTP id B8B8A83F46 for ; Thu, 19 May 2022 15:17:53 +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 EJ_As1ov3ReZ for ; Thu, 19 May 2022 15:17:51 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.8.0 Received: from relay8-d.mail.gandi.net (relay8-d.mail.gandi.net [IPv6:2001:4b98:dc4:8::228]) by smtp1.osuosl.org (Postfix) with ESMTPS id A2B57841CB for ; Thu, 19 May 2022 15:17:50 +0000 (UTC) Received: (Authenticated sender: numans@ovn.org) by mail.gandi.net (Postfix) with ESMTPSA id C3CBD1BF20E; Thu, 19 May 2022 15:17:45 +0000 (UTC) From: numans@ovn.org To: dev@openvswitch.org Date: Thu, 19 May 2022 11:17:39 -0400 Message-Id: <20220519151739.987211-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 1/3] ovn-controller: Add OF rules for port security. 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 ovn-controller will now generate OF rules for in port security and out port security checks in OF tables - 73, 74 and 75. These flows will be added if a port binding has port security defined in the Port_Binding.Port_Security column which is newly added in this patch. The idea of this patch is to program these OF rules directly within the ovn-controller instead of ovn-northd generating logical flows. This helps in reducing the numnber of logical flows overall in the Southbound database. Upcoming patches will add the necessary OVN actions which ovn-northd can make use of. Reported-at: https://bugzilla.redhat.com/show_bug.cgi?id=2078927 Suggested-by: Dumitru Ceara Acked-by: Mark Michelson Signed-off-by: Numan Siddique --- controller/binding.c | 78 +++- controller/binding.h | 23 +- controller/lflow.c | 792 ++++++++++++++++++++++++++++++++++- controller/lflow.h | 4 + controller/ovn-controller.c | 21 +- include/ovn/actions.h | 4 + include/ovn/logical-fields.h | 1 + ovn-sb.ovsschema | 7 +- ovn-sb.xml | 15 + tests/ovn.at | 288 +++++++++++++ 10 files changed, 1199 insertions(+), 34 deletions(-) diff --git a/controller/binding.c b/controller/binding.c index 0b6e83a74f..92baebd296 100644 --- a/controller/binding.c +++ b/controller/binding.c @@ -524,24 +524,6 @@ update_active_pb_ras_pd(const struct sbrec_port_binding *pb, } } -/* This structure represents a logical port (or port binding) - * which is associated with 'struct local_binding'. - * - * An instance of 'struct binding_lport' is created for a logical port - * - If the OVS interface's iface-id corresponds to the logical port. - * - If it is a container or virtual logical port and its parent - * has a 'local binding'. - * - */ -struct binding_lport { - struct ovs_list list_node; /* Node in local_binding.binding_lports. */ - - char *name; - const struct sbrec_port_binding *pb; - struct local_binding *lbinding; - enum en_lport_type type; -}; - static struct local_binding *local_binding_create( const char *name, const struct ovsrec_interface *); static void local_binding_add(struct shash *local_bindings, @@ -584,6 +566,11 @@ static const struct sbrec_port_binding *binding_lport_get_parent_pb( struct binding_lport *b_lprt); static struct binding_lport *binding_lport_check_and_cleanup( struct binding_lport *, struct shash *b_lports); +static bool binding_lport_has_port_sec_changed( + struct binding_lport *, const struct sbrec_port_binding *); +static void binding_lport_clear_port_sec(struct binding_lport *); +static bool binding_lport_update_port_sec( + struct binding_lport *, const struct sbrec_port_binding *); static char *get_lport_type_str(enum en_lport_type lport_type); static bool ovs_iface_matches_lport_iface_id_ver( @@ -1107,6 +1094,11 @@ consider_vif_lport_(const struct sbrec_port_binding *pb, b_ctx_out->tracked_dp_bindings); update_related_lport(pb, b_ctx_out); update_local_lports(pb->logical_port, b_ctx_out); + if (binding_lport_update_port_sec(b_lport, pb) && + b_ctx_out->tracked_dp_bindings) { + tracked_datapath_lport_add(pb, TRACKED_RESOURCE_UPDATED, + b_ctx_out->tracked_dp_bindings); + } if (b_lport->lbinding->iface && qos_map && b_ctx_in->ovs_idl_txn) { get_qos_params(pb, qos_map); } @@ -2799,6 +2791,7 @@ binding_lport_destroy(struct binding_lport *b_lport) ovs_list_remove(&b_lport->list_node); } + binding_lport_clear_port_sec(b_lport); free(b_lport->name); free(b_lport); } @@ -2925,6 +2918,55 @@ cleanup: } +static bool +binding_lport_has_port_sec_changed(struct binding_lport *b_lport, + const struct sbrec_port_binding *pb) +{ + if (b_lport->n_port_security != pb->n_port_security) { + return true; + } + + for (size_t i = 0; i < b_lport->n_port_security; i++) { + if (strcmp(b_lport->port_security[i], pb->port_security[i])) { + return true; + } + } + + return false; +} + +static void +binding_lport_clear_port_sec(struct binding_lport *b_lport) +{ + for (size_t i = 0; i < b_lport->n_port_security; i++) { + free(b_lport->port_security[i]); + } + free(b_lport->port_security); + b_lport->n_port_security = 0; +} + +static bool +binding_lport_update_port_sec(struct binding_lport *b_lport, + const struct sbrec_port_binding *pb) +{ + if (binding_lport_has_port_sec_changed(b_lport, pb)) { + binding_lport_clear_port_sec(b_lport); + b_lport->port_security = + pb->n_port_security ? + xmalloc(pb->n_port_security * sizeof *b_lport->port_security) : + NULL; + + b_lport->n_port_security = pb->n_port_security; + for (size_t i = 0; i < pb->n_port_security; i++) { + b_lport->port_security[i] = xstrdup(pb->port_security[i]); + } + + return true; + } + + return false; +} + static bool ovs_iface_matches_lport_iface_id_ver(const struct ovsrec_interface *iface, const struct sbrec_port_binding *pb) diff --git a/controller/binding.h b/controller/binding.h index e49e1ebb60..a3858d78bc 100644 --- a/controller/binding.h +++ b/controller/binding.h @@ -135,7 +135,6 @@ struct local_binding { struct ovs_list binding_lports; }; - struct local_binding_data { struct shash bindings; struct shash lports; @@ -193,4 +192,26 @@ enum en_lport_type { enum en_lport_type get_lport_type(const struct sbrec_port_binding *); +/* This structure represents a logical port (or port binding) + * which is associated with 'struct local_binding'. + * + * An instance of 'struct binding_lport' is created for a logical port + * - If the OVS interface's iface-id corresponds to the logical port. + * - If it is a container or virtual logical port and its parent + * has a 'local binding'. + * + */ +struct binding_lport { + struct ovs_list list_node; /* Node in local_binding.binding_lports. */ + + char *name; + const struct sbrec_port_binding *pb; + struct local_binding *lbinding; + enum en_lport_type type; + + /* Cached port security. */ + char **port_security; + size_t n_port_security; +}; + #endif /* controller/binding.h */ diff --git a/controller/lflow.c b/controller/lflow.c index b19bd236f2..e968231492 100644 --- a/controller/lflow.c +++ b/controller/lflow.c @@ -14,6 +14,7 @@ */ #include +#include "binding.h" #include "lflow.h" #include "coverage.h" #include "ha-chassis.h" @@ -114,6 +115,11 @@ static void ref_lflow_node_destroy(struct ref_lflow_node *); static void lflow_resource_destroy_lflow(struct lflow_resource_ref *, const struct uuid *lflow_uuid); +static void add_port_sec_flows(const struct shash *binding_lports, + const struct sbrec_chassis *, + struct ovn_desired_flow_table *); +static void consider_port_sec_flows(const struct sbrec_port_binding *pb, + struct ovn_desired_flow_table *); static bool lookup_port_cb(const void *aux_, const char *port_name, unsigned int *portp) @@ -1169,6 +1175,8 @@ add_matches_to_flow_table(const struct sbrec_logical_flow *lflow, .ct_snat_vip_ptable = OFTABLE_CT_SNAT_HAIRPIN, .fdb_ptable = OFTABLE_GET_FDB, .fdb_lookup_ptable = OFTABLE_LOOKUP_FDB, + .in_port_sec_ptable = OFTABLE_CHK_IN_PORT_SEC, + .out_port_sec_ptable = OFTABLE_CHK_OUT_PORT_SEC, .ctrl_meter_id = ctrl_meter_id, .common_nat_ct_zone = get_common_nat_zone(ldp), }; @@ -2543,6 +2551,8 @@ lflow_run(struct lflow_ctx_in *l_ctx_in, struct lflow_ctx_out *l_ctx_out) l_ctx_out->hairpin_id_pool); add_fdb_flows(l_ctx_in->fdb_table, l_ctx_in->local_datapaths, l_ctx_out->flow_table); + add_port_sec_flows(l_ctx_in->binding_lports, l_ctx_in->chassis, + l_ctx_out->flow_table); } /* Should be called at every ovn-controller iteration before IDL tracked @@ -2716,8 +2726,19 @@ lflow_handle_flows_for_lport(const struct sbrec_port_binding *pb, { bool changed; - return lflow_handle_changed_ref(REF_TYPE_PORTBINDING, pb->logical_port, - l_ctx_in, l_ctx_out, &changed); + if (!lflow_handle_changed_ref(REF_TYPE_PORTBINDING, pb->logical_port, + l_ctx_in, l_ctx_out, &changed)) { + return false; + } + + /* Program the port security flows. */ + ofctrl_remove_flows(l_ctx_out->flow_table, &pb->header_.uuid); + + if (pb->n_port_security && shash_find(l_ctx_in->binding_lports, + pb->logical_port)) { + consider_port_sec_flows(pb, l_ctx_out->flow_table); + } + return true; } /* Handles port-binding add/deletions. */ @@ -2854,3 +2875,770 @@ lflow_handle_changed_fdbs(struct lflow_ctx_in *l_ctx_in, return true; } + +static void +add_port_sec_flows(const struct shash *binding_lports, + const struct sbrec_chassis *chassis, + struct ovn_desired_flow_table *flow_table) +{ + const struct shash_node *node; + SHASH_FOR_EACH (node, binding_lports) { + const struct binding_lport *b_lport = node->data; + if (!b_lport->pb || b_lport->pb->chassis != chassis) { + continue; + } + + consider_port_sec_flows(b_lport->pb, flow_table); + } +} + +static void +reset_match_for_port_sec_flows(const struct sbrec_port_binding *pb, + enum mf_field_id reg_id, struct match *match) +{ + match_init_catchall(match); + match_set_metadata(match, htonll(pb->datapath->tunnel_key)); + match_set_reg(match, reg_id - MFF_REG0, pb->tunnel_key); +} + +static void build_port_sec_deny_action(struct ofpbuf *ofpacts) +{ + ofpbuf_clear(ofpacts); + uint8_t value = 1; + put_load(&value, sizeof value, MFF_LOG_FLAGS, + MLF_CHECK_PORT_SEC_BIT, 1, ofpacts); +} + +static void build_port_sec_allow_action(struct ofpbuf *ofpacts) +{ + ofpbuf_clear(ofpacts); + uint8_t value = 0; + put_load(&value, sizeof value, MFF_LOG_FLAGS, + MLF_CHECK_PORT_SEC_BIT, 1, ofpacts); +} + +static void build_port_sec_adv_nd_check(struct ofpbuf *ofpacts) +{ + ofpbuf_clear(ofpacts); + struct ofpact_resubmit *resubmit = ofpact_put_RESUBMIT(ofpacts); + resubmit->in_port = OFPP_IN_PORT; + resubmit->table_id = OFTABLE_CHK_IN_PORT_SEC_ND; +} + +static void +build_in_port_sec_default_flows(const struct sbrec_port_binding *pb, + struct match *m, struct ofpbuf *ofpacts, + struct ovn_desired_flow_table *flow_table) +{ + reset_match_for_port_sec_flows(pb, MFF_LOG_INPORT, m); + build_port_sec_deny_action(ofpacts); + + /* Add the below logical flow equivalent OF rule in 'in_port_sec' table. + * priority: 80 + * match - "inport == pb->logical_port" + * action - "port_sec_failed = 1;" + * description: "Default drop all traffic from"" + */ + ofctrl_add_flow(flow_table, OFTABLE_CHK_IN_PORT_SEC, 80, + pb->header_.uuid.parts[0], m, ofpacts, + &pb->header_.uuid); + + /* ARP checking is done in the next table. So just advance + * the arp packets to the next table. + * + * Add the below logical flow equivalent OF rules in 'in_port_sec' table. + * priority: 95 + * match - "inport == pb->logical_port && arp" + * action - "resubmit(,PORT_SEC_ND_TABLE);" + */ + match_set_dl_type(m, htons(ETH_TYPE_ARP)); + build_port_sec_adv_nd_check(ofpacts); + ofctrl_add_flow(flow_table, OFTABLE_CHK_IN_PORT_SEC, 95, + pb->header_.uuid.parts[0], m, ofpacts, + &pb->header_.uuid); + + /* Add the below logical flow equivalent OF rules in 'in_port_sec_nd' table + * priority: 80 + * match - "inport == pb->logical_port && arp" + * action - "port_sec_failed = 1;" + * description: "Default drop all arp packets" + * note: "Higher priority flows are added to allow the legit ARP packets." + */ + reset_match_for_port_sec_flows(pb, MFF_LOG_INPORT, m); + build_port_sec_deny_action(ofpacts); + match_set_dl_type(m, htons(ETH_TYPE_ARP)); + ofctrl_add_flow(flow_table, OFTABLE_CHK_IN_PORT_SEC_ND, 80, + pb->header_.uuid.parts[0], m, ofpacts, + &pb->header_.uuid); + + /* Add the below logical flow equivalent OF rules in 'in_port_sec_nd' table + * priority: 80 + * match - "inport == pb->logical_port && icmp6 && icmp6.code == 136" + * action - "port_sec_failed = 1;" + * description: "Default drop all IPv6 NA packets" + * note: "Higher priority flows are added to allow the legit NA packets." + */ + match_set_dl_type(m, htons(ETH_TYPE_IPV6)); + match_set_nw_proto(m, IPPROTO_ICMPV6); + match_set_nw_ttl(m, 255); + match_set_icmp_type(m, 136); + ofctrl_add_flow(flow_table, OFTABLE_CHK_IN_PORT_SEC_ND, 80, + pb->header_.uuid.parts[0], m, ofpacts, + &pb->header_.uuid); + + /* Add the below logical flow equivalent OF rules in 'in_port_sec_nd' table + * priority: 80 + * match - "inport == pb->logical_port && icmp6 && icmp6.code == 135" + * action - "port_sec_failed = 0;" + * description: "Default allow all IPv6 NS packets" + * note: This is a hack for now. Ideally we should do default drop. + * There seems to be a bug in ovs-vswitchd which needs further + * investigation. + * + * Eg. If there are below OF rules in the same table + * (1) priority=90,icmp6,reg14=0x1,metadata=0x1,nw_ttl=225,icmp_type=135, + * icmp_code=0,nd_sll=fa:16:3e:94:05:98 + * actions=load:0->NXM_NX_REG10[12] + * (2) priority=80,icmp6,reg14=0x1,metadata=0x1,nw_ttl=225,icmp_type=135, + * icmp_code=0 actions=load:1->NXM_NX_REG10[12] + * + * An IPv6 NS packet with nd_sll = fa:16:3e:94:05:98 is matching on the + * second prio-80 flow instead of the first one. + */ + match_set_dl_type(m, htons(ETH_TYPE_IPV6)); + match_set_nw_proto(m, IPPROTO_ICMPV6); + match_set_nw_ttl(m, 255); + match_set_icmp_type(m, 135); + build_port_sec_allow_action(ofpacts); /*TODO: Change this to + * build_port_sec_deny_action(). */ + ofctrl_add_flow(flow_table, OFTABLE_CHK_IN_PORT_SEC_ND, 80, + pb->header_.uuid.parts[0], m, ofpacts, + &pb->header_.uuid); +} + +static void +build_in_port_sec_no_ip_flows(const struct sbrec_port_binding *pb, + struct lport_addresses *ps_addr, + struct match *m, struct ofpbuf *ofpacts, + struct ovn_desired_flow_table *flow_table) +{ + if (ps_addr->n_ipv4_addrs || ps_addr->n_ipv6_addrs) { + return; + } + + /* Add the below logical flow equivalent OF rules in 'in_port_sec' table. + * priority: 90 + * match - "inport == pb->logical_port && eth.src == ps_addr.ea" + * action - "next;" + * description: "Advance the packet for ARP/ND check" + */ + reset_match_for_port_sec_flows(pb, MFF_LOG_INPORT, m); + match_set_dl_src(m, ps_addr->ea); + build_port_sec_adv_nd_check(ofpacts); + ofctrl_add_flow(flow_table, OFTABLE_CHK_IN_PORT_SEC, 90, + pb->header_.uuid.parts[0], m, ofpacts, + &pb->header_.uuid); +} + +static void +build_in_port_sec_ip4_flows(const struct sbrec_port_binding *pb, + struct lport_addresses *ps_addr, + struct match *m, struct ofpbuf *ofpacts, + struct ovn_desired_flow_table *flow_table) +{ + if (!ps_addr->n_ipv4_addrs) { + /* If no IPv4 addresses, then 'pb' is not allowed to send IPv4 traffic. + * build_in_port_sec_default_flows() takes care of this scenario. */ + return; + } + + /* Advance all traffic from the port security eth address for ND check. */ + build_port_sec_allow_action(ofpacts); + + /* Add the below logical flow equivalent OF rules in in_port_sec. + * priority: 90 + * match - "inport == pb->port && eth.src == ps_addr.ea && + * ip4.src == {ps_addr.ipv4_addrs}" + * action - "port_sec_failed = 0;" + */ + for (size_t j = 0; j < ps_addr->n_ipv4_addrs; j++) { + reset_match_for_port_sec_flows(pb, MFF_LOG_INPORT, m); + match_set_dl_src(m, ps_addr->ea); + match_set_dl_type(m, htons(ETH_TYPE_IP)); + + ovs_be32 mask = ps_addr->ipv4_addrs[j].mask; + /* When the netmask is applied, if the host portion is + * non-zero, the host can only use the specified + * address. If zero, the host is allowed to use any + * address in the subnet. + */ + if (ps_addr->ipv4_addrs[j].plen == 32 || + ps_addr->ipv4_addrs[j].addr & ~mask) { + match_set_nw_src(m, ps_addr->ipv4_addrs[j].addr); + } else { + match_set_nw_src_masked(m, ps_addr->ipv4_addrs[j].addr, mask); + } + + ofctrl_add_flow(flow_table, OFTABLE_CHK_IN_PORT_SEC, 90, + pb->header_.uuid.parts[0], m, ofpacts, + &pb->header_.uuid); + } + + /* Add the below logical flow equivalent OF rules in in_port_sec. + * priority: 90 + * match - "inport == pb->port && eth.src == ps_addr.ea && + * ip4.src == 0.0.0.0 && ip4.dst == 255.255.255.255 && + * udp.src == 67 && udp.dst == 68" + * action - "port_sec_failed = 0;" + * description: "Allow the DHCP requests." + */ + reset_match_for_port_sec_flows(pb, MFF_LOG_INPORT, m); + match_set_dl_src(m, ps_addr->ea); + match_set_dl_type(m, htons(ETH_TYPE_IP)); + + ovs_be32 ip4 = htonl(0); + match_set_nw_src(m, ip4); + ip4 = htonl(0xffffffff); + match_set_nw_dst(m, ip4); + match_set_nw_proto(m, IPPROTO_UDP); + match_set_tp_src(m, htons(68)); + match_set_tp_dst(m, htons(67)); + + ofctrl_add_flow(flow_table, OFTABLE_CHK_IN_PORT_SEC, 90, + pb->header_.uuid.parts[0], m, ofpacts, + &pb->header_.uuid); +} + +/* Adds the OF rules to allow ARP packets in 'in_port_sec_nd' table. */ +static void +build_in_port_sec_arp_flows(const struct sbrec_port_binding *pb, + struct lport_addresses *ps_addr, + struct match *m, struct ofpbuf *ofpacts, + struct ovn_desired_flow_table *flow_table) +{ + if (!ps_addr->n_ipv4_addrs && ps_addr->n_ipv6_addrs) { + /* No ARP is allowed as only IPv6 addresses are configured. */ + return; + } + + build_port_sec_allow_action(ofpacts); + + if (!ps_addr->n_ipv4_addrs) { + /* No IPv4 addresses. + * Add the below logical flow equivalent OF rules in 'in_port_sec_nd' + * table. + * priority: 90 + * match - "inport == pb->port && eth.src == ps_addr.ea && + * arp && arp.sha == ps_addr.ea" + * action - "port_sec_failed = 0;" + */ + reset_match_for_port_sec_flows(pb, MFF_LOG_INPORT, m); + match_set_dl_src(m, ps_addr->ea); + match_set_dl_type(m, htons(ETH_TYPE_ARP)); + match_set_arp_sha(m, ps_addr->ea); + ofctrl_add_flow(flow_table, OFTABLE_CHK_IN_PORT_SEC_ND, 90, + pb->header_.uuid.parts[0], m, ofpacts, + &pb->header_.uuid); + } + + /* Add the below logical flow equivalent OF rules in 'in_port_sec_nd' + * table. + * priority: 90 + * match - "inport == pb->port && eth.src == ps_addr.ea && + * arp && arp.sha == ps_addr.ea && arp.spa == {ps_addr.ipv4_addrs}" + * action - "port_sec_failed = 0;" + */ + for (size_t j = 0; j < ps_addr->n_ipv4_addrs; j++) { + reset_match_for_port_sec_flows(pb, MFF_LOG_INPORT, m); + match_set_dl_src(m, ps_addr->ea); + match_set_dl_type(m, htons(ETH_TYPE_ARP)); + match_set_arp_sha(m, ps_addr->ea); + + ovs_be32 mask = ps_addr->ipv4_addrs[j].mask; + if (ps_addr->ipv4_addrs[j].plen == 32 || + ps_addr->ipv4_addrs[j].addr & ~mask) { + match_set_nw_src(m, ps_addr->ipv4_addrs[j].addr); + } else { + match_set_nw_src_masked(m, ps_addr->ipv4_addrs[j].addr, mask); + } + ofctrl_add_flow(flow_table, OFTABLE_CHK_IN_PORT_SEC_ND, 90, + pb->header_.uuid.parts[0], m, ofpacts, + &pb->header_.uuid); + } +} + +static void +build_in_port_sec_ip6_flows(const struct sbrec_port_binding *pb, + struct lport_addresses *ps_addr, + struct match *m, struct ofpbuf *ofpacts, + struct ovn_desired_flow_table *flow_table) +{ + if (!ps_addr->n_ipv6_addrs) { + /* If no IPv6 addresses, then 'pb' is not allowed to send IPv6 traffic. + * build_in_port_sec_default_flows() takes care of this scenario. */ + return; + } + + /* Add the below logical flow equivalent OF rules in 'in_port_sec_nd' + * table. + * priority: 90 + * match - "inport == pb->port && eth.src == ps_addr.ea && + * ip6.src == {ps_addr.ipv6_addrs, lla}" + * action - "next;" + * description - Advance the packet for Neighbor Solicit/Adv check. + */ + build_port_sec_adv_nd_check(ofpacts); + + for (size_t j = 0; j < ps_addr->n_ipv6_addrs; j++) { + reset_match_for_port_sec_flows(pb, MFF_LOG_INPORT, m); + match_set_dl_src(m, ps_addr->ea); + match_set_dl_type(m, htons(ETH_TYPE_IPV6)); + + if (ps_addr->ipv6_addrs[j].plen == 128 + || !ipv6_addr_is_host_zero(&ps_addr->ipv6_addrs[j].addr, + &ps_addr->ipv6_addrs[j].mask)) { + match_set_ipv6_src(m, &ps_addr->ipv6_addrs[j].addr); + } else { + match_set_ipv6_src_masked(m, &ps_addr->ipv6_addrs[j].network, + &ps_addr->ipv6_addrs[j].mask); + } + + ofctrl_add_flow(flow_table, OFTABLE_CHK_IN_PORT_SEC, 90, + pb->header_.uuid.parts[0], m, ofpacts, + &pb->header_.uuid); + } + + reset_match_for_port_sec_flows(pb, MFF_LOG_INPORT, m); + match_set_dl_src(m, ps_addr->ea); + match_set_dl_type(m, htons(ETH_TYPE_IPV6)); + + struct in6_addr lla; + in6_generate_lla(ps_addr->ea, &lla); + match_set_ipv6_src(m, &lla); + + ofctrl_add_flow(flow_table, OFTABLE_CHK_IN_PORT_SEC, 90, + pb->header_.uuid.parts[0], m, ofpacts, + &pb->header_.uuid); + + /* Add the below logical flow equivalent OF rules in 'in_port_sec_nd' + * table. + * priority: 90 + * match - "inport == pb->port && eth.src == ps_addr.ea && + * ip6.src == :: && ip6.dst == ff02::/16 && icmp6 && + * icmp6.code == 0 && icmp6.type == {131, 143}" + * action - "port_sec_failed = 0;" + */ + build_port_sec_allow_action(ofpacts); + match_set_ipv6_src(m, &in6addr_any); + struct in6_addr ip6, mask; + char *err = ipv6_parse_masked("ff02::/16", &ip6, &mask); + ovs_assert(!err); + + match_set_ipv6_dst_masked(m, &ip6, &mask); + match_set_nw_proto(m, IPPROTO_ICMPV6); + match_set_icmp_type(m, 131); + match_set_icmp_code(m, 0); + ofctrl_add_flow(flow_table, OFTABLE_CHK_IN_PORT_SEC, 90, + pb->header_.uuid.parts[0], m, ofpacts, + &pb->header_.uuid); + + match_set_icmp_type(m, 143); + ofctrl_add_flow(flow_table, OFTABLE_CHK_IN_PORT_SEC, 90, + pb->header_.uuid.parts[0], m, ofpacts, + &pb->header_.uuid); + + /* Add the below logical flow equivalent OF rules in 'in_port_sec_nd' + * table. + * priority: 90 + * match - "inport == pb->port && eth.src == ps_addr.ea && + * ip6.src == :: && ip6.dst == ff02::/16 && icmp6 && + * icmp6.code == 0 && icmp6.type == 135" + * action - "next;" + * description: "Advance the packet for Neighbor solicit check" + */ + build_port_sec_adv_nd_check(ofpacts); + match_set_icmp_type(m, 135); + ofctrl_add_flow(flow_table, OFTABLE_CHK_IN_PORT_SEC, 90, + pb->header_.uuid.parts[0], m, ofpacts, + &pb->header_.uuid); +} + +/* Adds the OF rules to allow IPv6 Neigh discovery packet in + * 'in_port_sec_nd' table. */ +static void +build_in_port_sec_nd_flows(const struct sbrec_port_binding *pb, + struct lport_addresses *ps_addr, + struct match *m, struct ofpbuf *ofpacts, + struct ovn_desired_flow_table *flow_table) +{ + build_port_sec_allow_action(ofpacts); + + /* Add the below logical flow equivalent OF rules in 'in_port_sec_nd' + * table. + * priority: 90 + * match - "inport == pb->port && eth.src == ps_addr.ea && + * icmp6 && icmp6.code == 135 && icmp6.type == 0 && + * ip6.tll == 255 && nd.sll == {00:00:00:00:00:00, ps_addr.ea}" + * action - "port_sec_failed = 0;" + */ + reset_match_for_port_sec_flows(pb, MFF_LOG_INPORT, m); + match_set_dl_type(m, htons(ETH_TYPE_IPV6)); + match_set_nw_proto(m, IPPROTO_ICMPV6); + match_set_nw_ttl(m, 225); + match_set_icmp_type(m, 135); + match_set_icmp_code(m, 0); + + match_set_arp_sha(m, eth_addr_zero); + ofctrl_add_flow(flow_table, OFTABLE_CHK_IN_PORT_SEC_ND, 90, + pb->header_.uuid.parts[0], m, ofpacts, + &pb->header_.uuid); + + match_set_arp_sha(m, ps_addr->ea); + ofctrl_add_flow(flow_table, OFTABLE_CHK_IN_PORT_SEC_ND, 90, + pb->header_.uuid.parts[0], m, ofpacts, + &pb->header_.uuid); + + match_set_icmp_type(m, 136); + match_set_icmp_code(m, 0); + if (ps_addr->n_ipv6_addrs) { + /* Add the below logical flow equivalent OF rules in 'in_port_sec_nd' + * table if IPv6 addresses are configured. + * priority: 90 + * match - "inport == pb->port && eth.src == ps_addr.ea && icmp6 && + * icmp6.code == 136 && icmp6.type == 0 && ip6.tll == 255 && + * nd.tll == {00:00:00:00:00:00, ps_addr.ea} && + * nd.target == {ps_addr.ipv6_addrs, lla}" + * action - "port_sec_failed = 0;" + */ + struct in6_addr lla; + in6_generate_lla(ps_addr->ea, &lla); + match_set_arp_tha(m, eth_addr_zero); + + match_set_nd_target(m, &lla); + ofctrl_add_flow(flow_table, OFTABLE_CHK_IN_PORT_SEC_ND, 90, + pb->header_.uuid.parts[0], m, ofpacts, + &pb->header_.uuid); + match_set_arp_tha(m, ps_addr->ea); + match_set_nd_target(m, &lla); + ofctrl_add_flow(flow_table, OFTABLE_CHK_IN_PORT_SEC_ND, 90, + pb->header_.uuid.parts[0], m, ofpacts, + &pb->header_.uuid); + + for (size_t j = 0; j < ps_addr->n_ipv6_addrs; j++) { + reset_match_for_port_sec_flows(pb, MFF_LOG_INPORT, m); + match_set_dl_src(m, ps_addr->ea); + match_set_dl_type(m, htons(ETH_TYPE_IPV6)); + match_set_nw_proto(m, IPPROTO_ICMPV6); + match_set_icmp_type(m, 136); + match_set_icmp_code(m, 0); + match_set_arp_tha(m, eth_addr_zero); + + if (ps_addr->ipv6_addrs[j].plen == 128 + || !ipv6_addr_is_host_zero(&ps_addr->ipv6_addrs[j].addr, + &ps_addr->ipv6_addrs[j].mask)) { + match_set_nd_target(m, &ps_addr->ipv6_addrs[j].addr); + } else { + match_set_nd_target_masked(m, &ps_addr->ipv6_addrs[j].network, + &ps_addr->ipv6_addrs[j].mask); + } + + ofctrl_add_flow(flow_table, OFTABLE_CHK_IN_PORT_SEC_ND, 90, + pb->header_.uuid.parts[0], m, ofpacts, + &pb->header_.uuid); + + match_set_arp_tha(m, ps_addr->ea); + ofctrl_add_flow(flow_table, OFTABLE_CHK_IN_PORT_SEC_ND, 90, + pb->header_.uuid.parts[0], m, ofpacts, + &pb->header_.uuid); + } + } else { + /* Add the below logical flow equivalent OF rules in 'in_port_sec_nd' + * table if no IPv6 addresses are configured. + * priority: 90 + * match - "inport == pb->port && eth.src == ps_addr.ea && icmp6 && + * icmp6.code == 136 && icmp6.type == 0 && ip6.tll == 255 && + * nd.tll == {00:00:00:00:00:00, ps_addr.ea}" + * action - "port_sec_failed = 0;" + */ + match_set_arp_tha(m, eth_addr_zero); + ofctrl_add_flow(flow_table, OFTABLE_CHK_IN_PORT_SEC_ND, 90, + pb->header_.uuid.parts[0], m, ofpacts, + &pb->header_.uuid); + + match_set_arp_tha(m, ps_addr->ea); + ofctrl_add_flow(flow_table, OFTABLE_CHK_IN_PORT_SEC_ND, 90, + pb->header_.uuid.parts[0], m, ofpacts, + &pb->header_.uuid); + } +} + +static void +build_out_port_sec_no_ip_flows(const struct sbrec_port_binding *pb, + struct lport_addresses *ps_addr, + struct match *m, struct ofpbuf *ofpacts, + struct ovn_desired_flow_table *flow_table) +{ + /* Add the below logical flow equivalent OF rules in 'out_port_sec' table. + * priority: 85 + * match - "outport == pb->logical_port && eth.dst == ps_addr.ea" + * action - "port_sec_failed = 0;" + * description: "Allow the packet if eth.dst matches." + */ + reset_match_for_port_sec_flows(pb, MFF_LOG_OUTPORT, m); + match_set_dl_dst(m, ps_addr->ea); + build_port_sec_allow_action(ofpacts); + ofctrl_add_flow(flow_table, OFTABLE_CHK_OUT_PORT_SEC, 85, + pb->header_.uuid.parts[0], m, ofpacts, + &pb->header_.uuid); +} + +static void +build_out_port_sec_ip4_flows(const struct sbrec_port_binding *pb, + struct lport_addresses *ps_addr, + struct match *m, struct ofpbuf *ofpacts, + struct ovn_desired_flow_table *flow_table) +{ + if (!ps_addr->n_ipv4_addrs && !ps_addr->n_ipv6_addrs) { + return; + } + + /* Add the below logical flow equivalent OF rules in 'out_port_sec' table. + * priority: 90 + * match - "outport == pb->logical_port && eth.dst == ps_addr.ea && ip4" + * action - "port_sec_failed = 1;" + * description: Default drop IPv4 packets. If IPv4 addresses are + * configured, then higher priority flows are added + * to allow specific IPv4 packets. + */ + reset_match_for_port_sec_flows(pb, MFF_LOG_OUTPORT, m); + match_set_dl_dst(m, ps_addr->ea); + match_set_dl_type(m, htons(ETH_TYPE_IP)); + build_port_sec_deny_action(ofpacts); + ofctrl_add_flow(flow_table, OFTABLE_CHK_OUT_PORT_SEC, 90, + pb->header_.uuid.parts[0], m, ofpacts, + &pb->header_.uuid); + + if (!ps_addr->n_ipv4_addrs) { + return; + } + + /* Add the below logical flow equivalent OF rules in 'out_port_sec' table. + * priority: 95 + * match - "outport == pb->logical_port && eth.dst == ps_addr.ea && + * ip4.dst == {ps_addr.ipv4_addrs, 255.255.255.255, 224.0.0.0/4}," + * action - "port_sec_failed = 0;" + */ + build_port_sec_allow_action(ofpacts); + for (size_t j = 0; j < ps_addr->n_ipv4_addrs; j++) { + reset_match_for_port_sec_flows(pb, MFF_LOG_OUTPORT, m); + match_set_dl_dst(m, ps_addr->ea); + match_set_dl_type(m, htons(ETH_TYPE_IP)); + ovs_be32 mask = ps_addr->ipv4_addrs[j].mask; + if (ps_addr->ipv4_addrs[j].plen == 32 + || ps_addr->ipv4_addrs[j].addr & ~mask) { + + if (ps_addr->ipv4_addrs[j].plen != 32) { + /* Special case to allow bcast traffic. + * Eg. If ps_addr is 10.0.0.4/24, then add the below flow + * priority: 95 + * match - "outport == pb->logical_port && + * eth.dst == ps_addr.ea && + * ip4.dst == 10.0.0.255" + * action - "port_sec_failed = 0;" + */ + ovs_be32 bcast_addr; + ovs_assert(ip_parse(ps_addr->ipv4_addrs[j].bcast_s, + &bcast_addr)); + match_set_nw_dst(m, bcast_addr); + ofctrl_add_flow(flow_table, OFTABLE_CHK_OUT_PORT_SEC, 95, + pb->header_.uuid.parts[0], m, ofpacts, + &pb->header_.uuid); + } + + match_set_nw_dst(m, ps_addr->ipv4_addrs[j].addr); + } else { + /* host portion is zero */ + match_set_nw_dst_masked(m, ps_addr->ipv4_addrs[j].addr, + mask); + } + + ofctrl_add_flow(flow_table, OFTABLE_CHK_OUT_PORT_SEC, 95, + pb->header_.uuid.parts[0], m, ofpacts, + &pb->header_.uuid); + } + + reset_match_for_port_sec_flows(pb, MFF_LOG_OUTPORT, m); + match_set_dl_dst(m, ps_addr->ea); + match_set_dl_type(m, htons(ETH_TYPE_IP)); + + ovs_be32 ip4 = htonl(0xffffffff); + match_set_nw_dst(m, ip4); + ofctrl_add_flow(flow_table, OFTABLE_CHK_OUT_PORT_SEC, 95, + pb->header_.uuid.parts[0], m, ofpacts, + &pb->header_.uuid); + + /* Allow 224.0.0.0/4 traffic. */ + ip4 = htonl(0xe0000000); + ovs_be32 mask = htonl(0xf0000000); + match_set_nw_dst_masked(m, ip4, mask); + ofctrl_add_flow(flow_table, OFTABLE_CHK_OUT_PORT_SEC, 95, + pb->header_.uuid.parts[0], m, ofpacts, + &pb->header_.uuid); +} + +static void +build_out_port_sec_ip6_flows(const struct sbrec_port_binding *pb, + struct lport_addresses *ps_addr, + struct match *m, struct ofpbuf *ofpacts, + struct ovn_desired_flow_table *flow_table) +{ + if (!ps_addr->n_ipv4_addrs && !ps_addr->n_ipv6_addrs) { + return; + } + + /* Add the below logical flow equivalent OF rules in 'out_port_sec' table. + * priority: 90 + * match - "outport == pb->logical_port && eth.dst == ps_addr.ea && ip6" + * action - "port_sec_failed = 1;" + * description: Default drop IPv6 packets. If IPv6 addresses are + * configured, then higher priority flows are added + * to allow specific IPv6 packets. + */ + reset_match_for_port_sec_flows(pb, MFF_LOG_OUTPORT, m); + match_set_dl_dst(m, ps_addr->ea); + match_set_dl_type(m, htons(ETH_TYPE_IPV6)); + build_port_sec_deny_action(ofpacts); + ofctrl_add_flow(flow_table, OFTABLE_CHK_OUT_PORT_SEC, 90, + pb->header_.uuid.parts[0], m, ofpacts, + &pb->header_.uuid); + + if (!ps_addr->n_ipv6_addrs) { + return; + } + + /* Add the below logical flow equivalent OF rules in 'out_port_sec' table. + * priority: 95 + * match - "outport == pb->logical_port && eth.dst == ps_addr.ea && + * ip6.dst == {ps_addr.ipv6_addrs, lla, ff00::/8}," + * action - "port_sec_failed = 0;" + */ + build_port_sec_allow_action(ofpacts); + for (size_t j = 0; j < ps_addr->n_ipv6_addrs; j++) { + reset_match_for_port_sec_flows(pb, MFF_LOG_OUTPORT, m); + match_set_dl_dst(m, ps_addr->ea); + match_set_dl_type(m, htons(ETH_TYPE_IPV6)); + + if (ps_addr->ipv6_addrs[j].plen == 128 + || !ipv6_addr_is_host_zero(&ps_addr->ipv6_addrs[j].addr, + &ps_addr->ipv6_addrs[j].mask)) { + match_set_ipv6_dst(m, &ps_addr->ipv6_addrs[j].addr); + } else { + match_set_ipv6_dst_masked(m, &ps_addr->ipv6_addrs[j].network, + &ps_addr->ipv6_addrs[j].mask); + } + + ofctrl_add_flow(flow_table, OFTABLE_CHK_OUT_PORT_SEC, 95, + pb->header_.uuid.parts[0], m, ofpacts, + &pb->header_.uuid); + } + + struct in6_addr lla; + in6_generate_lla(ps_addr->ea, &lla); + + reset_match_for_port_sec_flows(pb, MFF_LOG_OUTPORT, m); + match_set_dl_dst(m, ps_addr->ea); + match_set_dl_type(m, htons(ETH_TYPE_IPV6)); + match_set_ipv6_dst(m, &lla); + ofctrl_add_flow(flow_table, OFTABLE_CHK_OUT_PORT_SEC, 95, + pb->header_.uuid.parts[0], m, ofpacts, + &pb->header_.uuid); + + struct in6_addr ip6, mask; + char *err = ipv6_parse_masked("ff00::/8", &ip6, &mask); + ovs_assert(!err); + + match_set_ipv6_dst_masked(m, &ip6, &mask); + ofctrl_add_flow(flow_table, OFTABLE_CHK_OUT_PORT_SEC, 95, + pb->header_.uuid.parts[0], m, ofpacts, + &pb->header_.uuid); +} + +static void +consider_port_sec_flows(const struct sbrec_port_binding *pb, + struct ovn_desired_flow_table *flow_table) +{ + if (!pb->n_port_security) { + return; + } + + struct lport_addresses *ps_addrs; /* Port security addresses. */ + size_t n_ps_addrs = 0; + + ps_addrs = xmalloc(sizeof *ps_addrs * pb->n_port_security); + for (size_t i = 0; i < pb->n_port_security; i++) { + if (!extract_lsp_addresses(pb->port_security[i], + &ps_addrs[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", + pb->port_security[i]); + continue; + } + n_ps_addrs++; + } + + if (!n_ps_addrs) { + free(ps_addrs); + return; + } + + struct match match = MATCH_CATCHALL_INITIALIZER; + uint64_t stub[1024 / 8]; + struct ofpbuf ofpacts = OFPBUF_STUB_INITIALIZER(stub); + + build_in_port_sec_default_flows(pb, &match, &ofpacts, flow_table); + + for (size_t i = 0; i < n_ps_addrs; i++) { + build_in_port_sec_no_ip_flows(pb, &ps_addrs[i], &match, &ofpacts, + flow_table); + build_in_port_sec_ip4_flows(pb, &ps_addrs[i], &match, &ofpacts, + flow_table); + build_in_port_sec_arp_flows(pb, &ps_addrs[i], &match, &ofpacts, + flow_table); + build_in_port_sec_ip6_flows(pb, &ps_addrs[i], &match, &ofpacts, + flow_table); + build_in_port_sec_nd_flows(pb, &ps_addrs[i], &match, &ofpacts, + flow_table); + } + + /* Out port security. */ + + /* Add the below logical flow equivalent OF rules in 'out_port_sec_nd' + * table. + * priority: 80 + * match - "outport == pb->logical_port" + * action - "port_sec_failed = 1;" + * descrption: "Drop all traffic" + */ + reset_match_for_port_sec_flows(pb, MFF_LOG_OUTPORT, &match); + build_port_sec_deny_action(&ofpacts); + ofctrl_add_flow(flow_table, OFTABLE_CHK_OUT_PORT_SEC, 80, + pb->header_.uuid.parts[0], &match, &ofpacts, + &pb->header_.uuid); + + for (size_t i = 0; i < n_ps_addrs; i++) { + build_out_port_sec_no_ip_flows(pb, &ps_addrs[i], &match, &ofpacts, + flow_table); + build_out_port_sec_ip4_flows(pb, &ps_addrs[i], &match, &ofpacts, + flow_table); + build_out_port_sec_ip6_flows(pb, &ps_addrs[i], &match, &ofpacts, + flow_table); + } + + ofpbuf_uninit(&ofpacts); + for (size_t i = 0; i < n_ps_addrs; i++) { + destroy_lport_addresses(&ps_addrs[i]); + } + free(ps_addrs); +} diff --git a/controller/lflow.h b/controller/lflow.h index ba2efcebda..ad9449d3ac 100644 --- a/controller/lflow.h +++ b/controller/lflow.h @@ -76,6 +76,9 @@ struct uuid; #define OFTABLE_CT_SNAT_HAIRPIN 70 #define OFTABLE_GET_FDB 71 #define OFTABLE_LOOKUP_FDB 72 +#define OFTABLE_CHK_IN_PORT_SEC 73 +#define OFTABLE_CHK_IN_PORT_SEC_ND 74 +#define OFTABLE_CHK_OUT_PORT_SEC 75 enum ref_type { REF_TYPE_ADDRSET, @@ -155,6 +158,7 @@ struct lflow_ctx_in { const struct shash *port_groups; const struct sset *active_tunnels; const struct sset *related_lport_ids; + const struct shash *binding_lports; const struct hmap *chassis_tunnels; bool check_ct_label_for_lb_hairpin; }; diff --git a/controller/ovn-controller.c b/controller/ovn-controller.c index dfe30d1d19..f0c2e23d85 100644 --- a/controller/ovn-controller.c +++ b/controller/ovn-controller.c @@ -2050,7 +2050,8 @@ ct_zones_runtime_data_handler(struct engine_node *node, void *data) continue; } - if (t_lport->tracked_type == TRACKED_RESOURCE_NEW) { + if (t_lport->tracked_type == TRACKED_RESOURCE_NEW || + t_lport->tracked_type == TRACKED_RESOURCE_UPDATED) { if (!simap_contains(&ct_zones_data->current, t_lport->pb->logical_port)) { alloc_id_to_ct_zone(t_lport->pb->logical_port, @@ -2072,8 +2073,6 @@ ct_zones_runtime_data_handler(struct engine_node *node, void *data) simap_delete(&ct_zones_data->current, ct_zone); updated = true; } - } else { - OVS_NOT_REACHED(); } } } @@ -2528,6 +2527,7 @@ init_lflow_ctx(struct engine_node *node, l_ctx_in->port_groups = port_groups; l_ctx_in->active_tunnels = &rt_data->active_tunnels; l_ctx_in->related_lport_ids = &rt_data->related_lports.lport_ids; + l_ctx_in->binding_lports = &rt_data->lbinding_data.lports; l_ctx_in->chassis_tunnels = &non_vif_data->chassis_tunnels; l_ctx_in->check_ct_label_for_lb_hairpin = get_check_ct_label_for_lb_hairpin(n_ver->ver); @@ -2882,14 +2882,13 @@ lflow_output_runtime_data_handler(struct engine_node *node, &l_ctx_in, &l_ctx_out)) { return false; } - } else { - struct shash_node *shash_node; - SHASH_FOR_EACH (shash_node, &tdp->lports) { - struct tracked_lport *lport = shash_node->data; - if (!lflow_handle_flows_for_lport(lport->pb, &l_ctx_in, - &l_ctx_out)) { - return false; - } + } + struct shash_node *shash_node; + SHASH_FOR_EACH (shash_node, &tdp->lports) { + struct tracked_lport *lport = shash_node->data; + if (!lflow_handle_flows_for_lport(lport->pb, &l_ctx_in, + &l_ctx_out)) { + return false; } } } diff --git a/include/ovn/actions.h b/include/ovn/actions.h index 547797584b..49a2e26124 100644 --- a/include/ovn/actions.h +++ b/include/ovn/actions.h @@ -806,6 +806,10 @@ struct ovnact_encode_params { * 'get_fdb' to resubmit. */ uint8_t fdb_lookup_ptable; /* OpenFlow table for * 'lookup_fdb' to resubmit. */ + uint8_t in_port_sec_ptable; /* OpenFlow table for + * 'check_in_port_sec' to resubmit. */ + uint8_t out_port_sec_ptable; /* OpenFlow table for + * 'check_out_port_sec' to resubmit. */ uint32_t ctrl_meter_id; /* Meter to be used if the resulting flow sends packets to controller. */ uint32_t common_nat_ct_zone; /* When performing NAT in a common CT zone, diff --git a/include/ovn/logical-fields.h b/include/ovn/logical-fields.h index 18516634e7..bfb07ebeff 100644 --- a/include/ovn/logical-fields.h +++ b/include/ovn/logical-fields.h @@ -69,6 +69,7 @@ enum mff_log_flags_bits { MLF_SKIP_SNAT_FOR_LB_BIT = 9, MLF_LOCALPORT_BIT = 10, MLF_USE_SNAT_ZONE = 11, + MLF_CHECK_PORT_SEC_BIT = 12, }; /* MFF_LOG_FLAGS_REG flag assignments */ diff --git a/ovn-sb.ovsschema b/ovn-sb.ovsschema index 66664c840f..8609521944 100644 --- a/ovn-sb.ovsschema +++ b/ovn-sb.ovsschema @@ -1,7 +1,7 @@ { "name": "OVN_Southbound", - "version": "20.22.0", - "cksum": "1686121686 27471", + "version": "20.23.0", + "cksum": "2468078434 27629", "tables": { "SB_Global": { "columns": { @@ -225,6 +225,9 @@ "mac": {"type": {"key": "string", "min": 0, "max": "unlimited"}}, + "port_security": {"type": {"key": "string", + "min": 0, + "max": "unlimited"}}, "nat_addresses": {"type": {"key": "string", "min": 0, "max": "unlimited"}}, diff --git a/ovn-sb.xml b/ovn-sb.xml index b0b11b8ee3..086edddef4 100644 --- a/ovn-sb.xml +++ b/ovn-sb.xml @@ -2969,6 +2969,21 @@ tcp.flags = RST; follows the same format as that column. + +

+ This column controls the addresses from which the host attached to + the logical port (``the host'') is allowed to send packets and to + which it is allowed to receive packets. If this column is empty, + all addresses are permitted. +

+ +

+ It is copied from the port_security column in the + Logical_Switch_Port table in the Northbound database. It + follows the same format as that column. +

+
+

A type for this logical port. Logical ports can be used to model other diff --git a/tests/ovn.at b/tests/ovn.at index bb00d8e1a8..e3d65b1cce 100644 --- a/tests/ovn.at +++ b/tests/ovn.at @@ -30662,6 +30662,294 @@ OVN_CLEANUP([hv1], [hv2]) AT_CLEANUP ]) +OVN_FOR_EACH_NORTHD([ +AT_SETUP([ovn-controller port security OF flows]) +ovn_start + +net_add n1 + +# create two hypervisors, each with one vif port +sim_add hv1 +as hv1 +ovs-vsctl add-br br-phys +ovn_attach n1 br-phys 192.168.0.11 + +sim_add hv2 +as hv2 +ovs-vsctl add-br br-phys +ovn_attach n1 br-phys 192.168.0.12 + +check ovn-nbctl ls-add sw0 +check ovn-nbctl lsp-add sw0 sw0p1 -- lsp-set-addresses sw0p1 "00:00:00:00:00:03 10.0.0.3" +check ovn-nbctl lsp-add sw0 sw0p2 -- lsp-set-addresses sw0p2 "00:00:00:00:00:04 10.0.0.4" + +as hv1 +ovs-vsctl -- add-port br-int hv1-vif0 -- \ +set Interface hv1-vif0 external-ids:iface-id=sw0p1 ofport-request=1 + +wait_for_ports_up sw0p1 + +sw0_dp_key=$(printf "%x" $(fetch_column Datapath_Binding tunnel_key external_ids:name=sw0)) +sw0p1_key=$(printf "%x" $(fetch_column Port_Binding tunnel_key logical_port=sw0p1)) +sw0p2_key=$(printf "%x" $(fetch_column Port_Binding tunnel_key logical_port=sw0p2)) + +# There should be no flows in table 73, 74 and 75 in hv1 and hv2 +> hv1_t73_flows.expected +> hv1_t74_flows.expected +> hv1_t75_flows.expected + +> hv2_t73_flows.expected +> hv2_t74_flows.expected +> hv2_t75_flows.expected + +check_port_sec_offlows hv1 73 +check_port_sec_offlows hv1 74 +check_port_sec_offlows hv1 75 + +check_port_sec_offlows hv2 73 +check_port_sec_offlows hv2 74 +check_port_sec_offlows hv2 75 + +# Set port security for sw0p1 +check ovn-sbctl set port-binding sw0p1 port_security='"00:00:00:00:00:03"' +check ovn-nbctl --wait=hv sync + +check_port_sec_offlows() { + hv=$1 + t=$2 + + as $hv ovs-ofctl dump-flows br-int table=${t} | ofctl_strip_all | sort | grep -v NXST_FLOW > ${hv}_t${t}_flows.actual + AT_CHECK([diff -u ${hv}_t${t}_flows.actual ${hv}_t${t}_flows.expected]) +} + +echo " table=73, priority=80,reg14=0x$sw0p1_key,metadata=0x$sw0_dp_key actions=load:0x1->NXM_NX_REG10[[12]] + table=73, priority=90,reg14=0x$sw0p1_key,metadata=0x$sw0_dp_key,dl_src=00:00:00:00:00:03 actions=resubmit(,74) + table=73, priority=95,arp,reg14=0x1,metadata=0x$sw0_dp_key actions=resubmit(,74)" > hv1_t73_flows.expected + +check_port_sec_offlows hv1 73 + +echo " table=74, priority=80,arp,reg14=0x1,metadata=0x1 actions=load:0x1->NXM_NX_REG10[[12]] + table=74, priority=80,icmp6,reg14=0x1,metadata=0x1,nw_ttl=255,icmp_type=135 actions=load:0->NXM_NX_REG10[[12]] + table=74, priority=80,icmp6,reg14=0x1,metadata=0x1,nw_ttl=255,icmp_type=136 actions=load:0x1->NXM_NX_REG10[[12]] + table=74, priority=90,arp,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:00:03,arp_sha=00:00:00:00:00:03 actions=load:0->NXM_NX_REG10[[12]] + table=74, priority=90,icmp6,reg14=0x1,metadata=0x1,nw_ttl=225,icmp_type=135,icmp_code=0,nd_sll=00:00:00:00:00:00 actions=load:0->NXM_NX_REG10[[12]] + table=74, priority=90,icmp6,reg14=0x1,metadata=0x1,nw_ttl=225,icmp_type=135,icmp_code=0,nd_sll=00:00:00:00:00:03 actions=load:0->NXM_NX_REG10[[12]] + table=74, priority=90,icmp6,reg14=0x1,metadata=0x1,nw_ttl=225,icmp_type=136,icmp_code=0,nd_tll=00:00:00:00:00:00 actions=load:0->NXM_NX_REG10[[12]] + table=74, priority=90,icmp6,reg14=0x1,metadata=0x1,nw_ttl=225,icmp_type=136,icmp_code=0,nd_tll=00:00:00:00:00:03 actions=load:0->NXM_NX_REG10[[12]]" > hv1_t74_flows.expected + +check_port_sec_offlows hv1 74 + +echo " table=75, priority=80,reg15=0x1,metadata=0x1 actions=load:0x1->NXM_NX_REG10[[12]] + table=75, priority=85,reg15=0x1,metadata=0x1,dl_dst=00:00:00:00:00:03 actions=load:0->NXM_NX_REG10[[12]]" > hv1_t75_flows.expected + +check_port_sec_offlows hv1 75 + +> hv2_t73_flows.expected +> hv2_t74_flows.expected +> hv2_t75_flows.expected + +check_port_sec_offlows hv2 73 +check_port_sec_offlows hv2 74 +check_port_sec_offlows hv2 75 + +# Add IPv4 addresses to sw0p1 +check ovn-sbctl set port-binding sw0p1 port_security='"00:00:00:00:00:03 10.0.0.3" "00:00:00:00:00:13 10.0.0.13"' +check ovn-nbctl --wait=hv sync + +echo " table=73, priority=80,reg14=0x1,metadata=0x1 actions=load:0x1->NXM_NX_REG10[[12]] + table=73, priority=90,ip,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:00:03,nw_src=10.0.0.3 actions=load:0->NXM_NX_REG10[[12]] + table=73, priority=90,ip,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:00:13,nw_src=10.0.0.13 actions=load:0->NXM_NX_REG10[[12]] + table=73, priority=90,udp,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:00:03,nw_src=0.0.0.0,nw_dst=255.255.255.255,tp_src=68,tp_dst=67 actions=load:0->NXM_NX_REG10[[12]] + table=73, priority=90,udp,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:00:13,nw_src=0.0.0.0,nw_dst=255.255.255.255,tp_src=68,tp_dst=67 actions=load:0->NXM_NX_REG10[[12]] + table=73, priority=95,arp,reg14=0x1,metadata=0x1 actions=resubmit(,74)" > hv1_t73_flows.expected + +check_port_sec_offlows hv1 73 + +echo " table=74, priority=80,arp,reg14=0x1,metadata=0x1 actions=load:0x1->NXM_NX_REG10[[12]] + table=74, priority=80,icmp6,reg14=0x1,metadata=0x1,nw_ttl=255,icmp_type=135 actions=load:0->NXM_NX_REG10[[12]] + table=74, priority=80,icmp6,reg14=0x1,metadata=0x1,nw_ttl=255,icmp_type=136 actions=load:0x1->NXM_NX_REG10[[12]] + table=74, priority=90,arp,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:00:03,arp_spa=10.0.0.3,arp_sha=00:00:00:00:00:03 actions=load:0->NXM_NX_REG10[[12]] + table=74, priority=90,arp,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:00:13,arp_spa=10.0.0.13,arp_sha=00:00:00:00:00:13 actions=load:0->NXM_NX_REG10[[12]] + table=74, priority=90,icmp6,reg14=0x1,metadata=0x1,nw_ttl=225,icmp_type=135,icmp_code=0,nd_sll=00:00:00:00:00:00 actions=load:0->NXM_NX_REG10[[12]] + table=74, priority=90,icmp6,reg14=0x1,metadata=0x1,nw_ttl=225,icmp_type=135,icmp_code=0,nd_sll=00:00:00:00:00:03 actions=load:0->NXM_NX_REG10[[12]] + table=74, priority=90,icmp6,reg14=0x1,metadata=0x1,nw_ttl=225,icmp_type=135,icmp_code=0,nd_sll=00:00:00:00:00:13 actions=load:0->NXM_NX_REG10[[12]] + table=74, priority=90,icmp6,reg14=0x1,metadata=0x1,nw_ttl=225,icmp_type=136,icmp_code=0,nd_tll=00:00:00:00:00:00 actions=load:0->NXM_NX_REG10[[12]] + table=74, priority=90,icmp6,reg14=0x1,metadata=0x1,nw_ttl=225,icmp_type=136,icmp_code=0,nd_tll=00:00:00:00:00:03 actions=load:0->NXM_NX_REG10[[12]] + table=74, priority=90,icmp6,reg14=0x1,metadata=0x1,nw_ttl=225,icmp_type=136,icmp_code=0,nd_tll=00:00:00:00:00:13 actions=load:0->NXM_NX_REG10[[12]]" > hv1_t74_flows.expected + +check_port_sec_offlows hv1 74 + +echo " table=75, priority=80,reg15=0x1,metadata=0x1 actions=load:0x1->NXM_NX_REG10[[12]] + table=75, priority=85,reg15=0x1,metadata=0x1,dl_dst=00:00:00:00:00:03 actions=load:0->NXM_NX_REG10[[12]] + table=75, priority=85,reg15=0x1,metadata=0x1,dl_dst=00:00:00:00:00:13 actions=load:0->NXM_NX_REG10[[12]] + table=75, priority=90,ip,reg15=0x1,metadata=0x1,dl_dst=00:00:00:00:00:03 actions=load:0x1->NXM_NX_REG10[[12]] + table=75, priority=90,ip,reg15=0x1,metadata=0x1,dl_dst=00:00:00:00:00:13 actions=load:0x1->NXM_NX_REG10[[12]] + table=75, priority=90,ipv6,reg15=0x1,metadata=0x1,dl_dst=00:00:00:00:00:03 actions=load:0x1->NXM_NX_REG10[[12]] + table=75, priority=90,ipv6,reg15=0x1,metadata=0x1,dl_dst=00:00:00:00:00:13 actions=load:0x1->NXM_NX_REG10[[12]] + table=75, priority=95,ip,reg15=0x1,metadata=0x1,dl_dst=00:00:00:00:00:03,nw_dst=10.0.0.3 actions=load:0->NXM_NX_REG10[[12]] + table=75, priority=95,ip,reg15=0x1,metadata=0x1,dl_dst=00:00:00:00:00:03,nw_dst=224.0.0.0/4 actions=load:0->NXM_NX_REG10[[12]] + table=75, priority=95,ip,reg15=0x1,metadata=0x1,dl_dst=00:00:00:00:00:03,nw_dst=255.255.255.255 actions=load:0->NXM_NX_REG10[[12]] + table=75, priority=95,ip,reg15=0x1,metadata=0x1,dl_dst=00:00:00:00:00:13,nw_dst=10.0.0.13 actions=load:0->NXM_NX_REG10[[12]] + table=75, priority=95,ip,reg15=0x1,metadata=0x1,dl_dst=00:00:00:00:00:13,nw_dst=224.0.0.0/4 actions=load:0->NXM_NX_REG10[[12]] + table=75, priority=95,ip,reg15=0x1,metadata=0x1,dl_dst=00:00:00:00:00:13,nw_dst=255.255.255.255 actions=load:0->NXM_NX_REG10[[12]]" > hv1_t75_flows.expected + +check_port_sec_offlows hv1 75 + +check_port_sec_offlows hv2 73 +check_port_sec_offlows hv2 74 +check_port_sec_offlows hv2 75 + +# Configure IPv4 and IPv6 addresses in sw0p2 +check ovn-sbctl set port-binding sw0p2 port_security='"00:00:00:00:00:04 10.0.0.4 20.0.0.4/24 30.0.0.0/16 1000::4 2000::/64" "00:00:00:00:00:13 aef0::4"' + +# There should be no changes in hv1 and hv2 as sw0p2 is not claimed. +check_port_sec_offlows hv1 73 +check_port_sec_offlows hv1 74 +check_port_sec_offlows hv1 75 + +check_port_sec_offlows hv2 73 +check_port_sec_offlows hv2 74 +check_port_sec_offlows hv2 75 + +as hv2 +ovs-vsctl -- add-port br-int hv2-vif0 -- \ +set Interface hv2-vif0 external-ids:iface-id=sw0p2 ofport-request=1 + +wait_for_ports_up +# There should be no changes in hv1 +check_port_sec_offlows hv1 73 +check_port_sec_offlows hv1 74 +check_port_sec_offlows hv1 75 + +#hv2 ovn-controller should program flows. +echo " table=73, priority=80,reg14=0x2,metadata=0x1 actions=load:0x1->NXM_NX_REG10[[12]] + table=73, priority=90,icmp6,reg14=0x2,metadata=0x1,dl_src=00:00:00:00:00:04,ipv6_src=::,ipv6_dst=ff02::/16,icmp_type=131,icmp_code=0 actions=load:0->NXM_NX_REG10[[12]] + table=73, priority=90,icmp6,reg14=0x2,metadata=0x1,dl_src=00:00:00:00:00:04,ipv6_src=::,ipv6_dst=ff02::/16,icmp_type=135,icmp_code=0 actions=resubmit(,74) + table=73, priority=90,icmp6,reg14=0x2,metadata=0x1,dl_src=00:00:00:00:00:04,ipv6_src=::,ipv6_dst=ff02::/16,icmp_type=143,icmp_code=0 actions=load:0->NXM_NX_REG10[[12]] + table=73, priority=90,icmp6,reg14=0x2,metadata=0x1,dl_src=00:00:00:00:00:13,ipv6_src=::,ipv6_dst=ff02::/16,icmp_type=131,icmp_code=0 actions=load:0->NXM_NX_REG10[[12]] + table=73, priority=90,icmp6,reg14=0x2,metadata=0x1,dl_src=00:00:00:00:00:13,ipv6_src=::,ipv6_dst=ff02::/16,icmp_type=135,icmp_code=0 actions=resubmit(,74) + table=73, priority=90,icmp6,reg14=0x2,metadata=0x1,dl_src=00:00:00:00:00:13,ipv6_src=::,ipv6_dst=ff02::/16,icmp_type=143,icmp_code=0 actions=load:0->NXM_NX_REG10[[12]] + table=73, priority=90,ip,reg14=0x2,metadata=0x1,dl_src=00:00:00:00:00:04,nw_src=10.0.0.4 actions=load:0->NXM_NX_REG10[[12]] + table=73, priority=90,ip,reg14=0x2,metadata=0x1,dl_src=00:00:00:00:00:04,nw_src=20.0.0.4 actions=load:0->NXM_NX_REG10[[12]] + table=73, priority=90,ip,reg14=0x2,metadata=0x1,dl_src=00:00:00:00:00:04,nw_src=30.0.0.0/16 actions=load:0->NXM_NX_REG10[[12]] + table=73, priority=90,ipv6,reg14=0x2,metadata=0x1,dl_src=00:00:00:00:00:04,ipv6_src=1000::4 actions=resubmit(,74) + table=73, priority=90,ipv6,reg14=0x2,metadata=0x1,dl_src=00:00:00:00:00:04,ipv6_src=2000::/64 actions=resubmit(,74) + table=73, priority=90,ipv6,reg14=0x2,metadata=0x1,dl_src=00:00:00:00:00:04,ipv6_src=fe80::200:ff:fe00:4 actions=resubmit(,74) + table=73, priority=90,ipv6,reg14=0x2,metadata=0x1,dl_src=00:00:00:00:00:13,ipv6_src=aef0::4 actions=resubmit(,74) + table=73, priority=90,ipv6,reg14=0x2,metadata=0x1,dl_src=00:00:00:00:00:13,ipv6_src=fe80::200:ff:fe00:13 actions=resubmit(,74) + table=73, priority=90,udp,reg14=0x2,metadata=0x1,dl_src=00:00:00:00:00:04,nw_src=0.0.0.0,nw_dst=255.255.255.255,tp_src=68,tp_dst=67 actions=load:0->NXM_NX_REG10[[12]] + table=73, priority=95,arp,reg14=0x2,metadata=0x1 actions=resubmit(,74)" > hv2_t73_flows.expected + +check_port_sec_offlows hv2 73 + +echo " table=74, priority=80,arp,reg14=0x2,metadata=0x1 actions=load:0x1->NXM_NX_REG10[[12]] + table=74, priority=80,icmp6,reg14=0x2,metadata=0x1,nw_ttl=255,icmp_type=135 actions=load:0->NXM_NX_REG10[[12]] + table=74, priority=80,icmp6,reg14=0x2,metadata=0x1,nw_ttl=255,icmp_type=136 actions=load:0x1->NXM_NX_REG10[[12]] + table=74, priority=90,arp,reg14=0x2,metadata=0x1,dl_src=00:00:00:00:00:04,arp_spa=10.0.0.4,arp_sha=00:00:00:00:00:04 actions=load:0->NXM_NX_REG10[[12]] + table=74, priority=90,arp,reg14=0x2,metadata=0x1,dl_src=00:00:00:00:00:04,arp_spa=20.0.0.4,arp_sha=00:00:00:00:00:04 actions=load:0->NXM_NX_REG10[[12]] + table=74, priority=90,arp,reg14=0x2,metadata=0x1,dl_src=00:00:00:00:00:04,arp_spa=30.0.0.0/16,arp_sha=00:00:00:00:00:04 actions=load:0->NXM_NX_REG10[[12]] + table=74, priority=90,icmp6,reg14=0x2,metadata=0x1,dl_src=00:00:00:00:00:04,icmp_type=136,icmp_code=0,nd_target=1000::4,nd_tll=00:00:00:00:00:00 actions=load:0->NXM_NX_REG10[[12]] + table=74, priority=90,icmp6,reg14=0x2,metadata=0x1,dl_src=00:00:00:00:00:04,icmp_type=136,icmp_code=0,nd_target=1000::4,nd_tll=00:00:00:00:00:04 actions=load:0->NXM_NX_REG10[[12]] + table=74, priority=90,icmp6,reg14=0x2,metadata=0x1,dl_src=00:00:00:00:00:04,icmp_type=136,icmp_code=0,nd_target=2000::/64,nd_tll=00:00:00:00:00:00 actions=load:0->NXM_NX_REG10[[12]] + table=74, priority=90,icmp6,reg14=0x2,metadata=0x1,dl_src=00:00:00:00:00:04,icmp_type=136,icmp_code=0,nd_target=2000::/64,nd_tll=00:00:00:00:00:04 actions=load:0->NXM_NX_REG10[[12]] + table=74, priority=90,icmp6,reg14=0x2,metadata=0x1,dl_src=00:00:00:00:00:13,icmp_type=136,icmp_code=0,nd_target=aef0::4,nd_tll=00:00:00:00:00:00 actions=load:0->NXM_NX_REG10[[12]] + table=74, priority=90,icmp6,reg14=0x2,metadata=0x1,dl_src=00:00:00:00:00:13,icmp_type=136,icmp_code=0,nd_target=aef0::4,nd_tll=00:00:00:00:00:13 actions=load:0->NXM_NX_REG10[[12]] + table=74, priority=90,icmp6,reg14=0x2,metadata=0x1,nw_ttl=225,icmp_type=135,icmp_code=0,nd_sll=00:00:00:00:00:00 actions=load:0->NXM_NX_REG10[[12]] + table=74, priority=90,icmp6,reg14=0x2,metadata=0x1,nw_ttl=225,icmp_type=135,icmp_code=0,nd_sll=00:00:00:00:00:04 actions=load:0->NXM_NX_REG10[[12]] + table=74, priority=90,icmp6,reg14=0x2,metadata=0x1,nw_ttl=225,icmp_type=135,icmp_code=0,nd_sll=00:00:00:00:00:13 actions=load:0->NXM_NX_REG10[[12]] + table=74, priority=90,icmp6,reg14=0x2,metadata=0x1,nw_ttl=225,icmp_type=136,icmp_code=0,nd_target=fe80::200:ff:fe00:13,nd_tll=00:00:00:00:00:00 actions=load:0->NXM_NX_REG10[[12]] + table=74, priority=90,icmp6,reg14=0x2,metadata=0x1,nw_ttl=225,icmp_type=136,icmp_code=0,nd_target=fe80::200:ff:fe00:13,nd_tll=00:00:00:00:00:13 actions=load:0->NXM_NX_REG10[[12]] + table=74, priority=90,icmp6,reg14=0x2,metadata=0x1,nw_ttl=225,icmp_type=136,icmp_code=0,nd_target=fe80::200:ff:fe00:4,nd_tll=00:00:00:00:00:00 actions=load:0->NXM_NX_REG10[[12]] + table=74, priority=90,icmp6,reg14=0x2,metadata=0x1,nw_ttl=225,icmp_type=136,icmp_code=0,nd_target=fe80::200:ff:fe00:4,nd_tll=00:00:00:00:00:04 actions=load:0->NXM_NX_REG10[[12]]" > hv2_t74_flows.expected + +check_port_sec_offlows hv2 74 + +echo " table=75, priority=80,reg15=0x2,metadata=0x1 actions=load:0x1->NXM_NX_REG10[[12]] + table=75, priority=85,reg15=0x2,metadata=0x1,dl_dst=00:00:00:00:00:04 actions=load:0->NXM_NX_REG10[[12]] + table=75, priority=85,reg15=0x2,metadata=0x1,dl_dst=00:00:00:00:00:13 actions=load:0->NXM_NX_REG10[[12]] + table=75, priority=90,ip,reg15=0x2,metadata=0x1,dl_dst=00:00:00:00:00:04 actions=load:0x1->NXM_NX_REG10[[12]] + table=75, priority=90,ip,reg15=0x2,metadata=0x1,dl_dst=00:00:00:00:00:13 actions=load:0x1->NXM_NX_REG10[[12]] + table=75, priority=90,ipv6,reg15=0x2,metadata=0x1,dl_dst=00:00:00:00:00:04 actions=load:0x1->NXM_NX_REG10[[12]] + table=75, priority=90,ipv6,reg15=0x2,metadata=0x1,dl_dst=00:00:00:00:00:13 actions=load:0x1->NXM_NX_REG10[[12]] + table=75, priority=95,ip,reg15=0x2,metadata=0x1,dl_dst=00:00:00:00:00:04,nw_dst=10.0.0.4 actions=load:0->NXM_NX_REG10[[12]] + table=75, priority=95,ip,reg15=0x2,metadata=0x1,dl_dst=00:00:00:00:00:04,nw_dst=20.0.0.255 actions=load:0->NXM_NX_REG10[[12]] + table=75, priority=95,ip,reg15=0x2,metadata=0x1,dl_dst=00:00:00:00:00:04,nw_dst=20.0.0.4 actions=load:0->NXM_NX_REG10[[12]] + table=75, priority=95,ip,reg15=0x2,metadata=0x1,dl_dst=00:00:00:00:00:04,nw_dst=224.0.0.0/4 actions=load:0->NXM_NX_REG10[[12]] + table=75, priority=95,ip,reg15=0x2,metadata=0x1,dl_dst=00:00:00:00:00:04,nw_dst=255.255.255.255 actions=load:0->NXM_NX_REG10[[12]] + table=75, priority=95,ip,reg15=0x2,metadata=0x1,dl_dst=00:00:00:00:00:04,nw_dst=30.0.0.0/16 actions=load:0->NXM_NX_REG10[[12]] + table=75, priority=95,ipv6,reg15=0x2,metadata=0x1,dl_dst=00:00:00:00:00:04,ipv6_dst=1000::4 actions=load:0->NXM_NX_REG10[[12]] + table=75, priority=95,ipv6,reg15=0x2,metadata=0x1,dl_dst=00:00:00:00:00:04,ipv6_dst=2000::/64 actions=load:0->NXM_NX_REG10[[12]] + table=75, priority=95,ipv6,reg15=0x2,metadata=0x1,dl_dst=00:00:00:00:00:04,ipv6_dst=fe80::200:ff:fe00:4 actions=load:0->NXM_NX_REG10[[12]] + table=75, priority=95,ipv6,reg15=0x2,metadata=0x1,dl_dst=00:00:00:00:00:04,ipv6_dst=ff00::/8 actions=load:0->NXM_NX_REG10[[12]] + table=75, priority=95,ipv6,reg15=0x2,metadata=0x1,dl_dst=00:00:00:00:00:13,ipv6_dst=aef0::4 actions=load:0->NXM_NX_REG10[[12]] + table=75, priority=95,ipv6,reg15=0x2,metadata=0x1,dl_dst=00:00:00:00:00:13,ipv6_dst=fe80::200:ff:fe00:13 actions=load:0->NXM_NX_REG10[[12]] + table=75, priority=95,ipv6,reg15=0x2,metadata=0x1,dl_dst=00:00:00:00:00:13,ipv6_dst=ff00::/8 actions=load:0->NXM_NX_REG10[[12]]" > hv2_t75_flows.expected + +check_port_sec_offlows hv2 75 + +check ovn-sbctl clear port-binding sw0p2 port_security +check ovn-nbctl --wait=hv sync + +check_port_sec_offlows hv1 73 +check_port_sec_offlows hv1 74 +check_port_sec_offlows hv1 75 + +> hv2_t73_flows.expected +> hv2_t74_flows.expected +> hv2_t75_flows.expected + +check_port_sec_offlows hv2 73 +check_port_sec_offlows hv2 74 +check_port_sec_offlows hv2 75 + +check ovn-sbctl set port-binding sw0p2 port_security='"00:00:00:00:00:04"' + +check_port_sec_offlows hv1 73 +check_port_sec_offlows hv1 74 +check_port_sec_offlows hv1 75 + +echo " table=73, priority=80,reg14=0x2,metadata=0x1 actions=load:0x1->NXM_NX_REG10[[12]] + table=73, priority=90,reg14=0x2,metadata=0x1,dl_src=00:00:00:00:00:04 actions=resubmit(,74) + table=73, priority=95,arp,reg14=0x2,metadata=0x1 actions=resubmit(,74)" > hv2_t73_flows.expected + +check_port_sec_offlows hv2 73 + +# Delete sw0p2 +check ovn-nbctl --wait=hv lsp-del sw0p2 + +> hv2_t73_flows.expected +> hv2_t74_flows.expected +> hv2_t75_flows.expected + +check_port_sec_offlows hv1 73 +check_port_sec_offlows hv1 74 +check_port_sec_offlows hv1 75 + +check_port_sec_offlows hv2 73 +check_port_sec_offlows hv2 74 +check_port_sec_offlows hv2 75 + +# Release sw0p1 from hv1 +as hv1 ovs-vsctl del-port hv1-vif0 + +wait_column '' Port_Binding chassis logical_port=sw0p1 + +> hv1_t73_flows.expected +> hv1_t74_flows.expected +> hv1_t75_flows.expected + +check_port_sec_offlows hv1 73 +check_port_sec_offlows hv1 74 +check_port_sec_offlows hv1 75 + +check_port_sec_offlows hv2 73 +check_port_sec_offlows hv2 74 +check_port_sec_offlows hv2 75 + +OVN_CLEANUP([hv1], [hv2]) +AT_CLEANUP +]) + AT_SETUP([snat-ct-zone with common NAT zone]) # This test sets up a couple of simple NATs. OVN will program logical # flows for ct_snat_in_czone() and ct_dnat_in_czone() as a result. We 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); From patchwork Thu May 19 15:18:12 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Numan Siddique X-Patchwork-Id: 1633325 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=140.211.166.133; helo=smtp2.osuosl.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=) Received: from smtp2.osuosl.org (smtp2.osuosl.org [140.211.166.133]) (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 4L3tmH0bpYz9t6h for ; Fri, 20 May 2022 01:18:39 +1000 (AEST) Received: from localhost (localhost [127.0.0.1]) by smtp2.osuosl.org (Postfix) with ESMTP id 51DD340FD6; Thu, 19 May 2022 15:18:37 +0000 (UTC) X-Virus-Scanned: amavisd-new at osuosl.org Received: from smtp2.osuosl.org ([127.0.0.1]) by localhost (smtp2.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id 7ytw3YXOhYka; Thu, 19 May 2022 15:18:33 +0000 (UTC) Received: from lists.linuxfoundation.org (lf-lists.osuosl.org [IPv6:2605:bc80:3010:104::8cd3:938]) by smtp2.osuosl.org (Postfix) with ESMTPS id 0F90B40C56; Thu, 19 May 2022 15:18:32 +0000 (UTC) Received: from lf-lists.osuosl.org (localhost [127.0.0.1]) by lists.linuxfoundation.org (Postfix) with ESMTP id C83E1C0032; Thu, 19 May 2022 15:18:31 +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 52053C002D for ; Thu, 19 May 2022 15:18:30 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by smtp1.osuosl.org (Postfix) with ESMTP id 48DFD8443B for ; Thu, 19 May 2022 15:18:24 +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 NtLHOL-zRRcw for ; Thu, 19 May 2022 15:18:21 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.8.0 Received: from relay4-d.mail.gandi.net (relay4-d.mail.gandi.net [217.70.183.196]) by smtp1.osuosl.org (Postfix) with ESMTPS id 4BCF782B8C for ; Thu, 19 May 2022 15:18:20 +0000 (UTC) Received: (Authenticated sender: numans@ovn.org) by mail.gandi.net (Postfix) with ESMTPSA id 4EB84E0005; Thu, 19 May 2022 15:18:14 +0000 (UTC) From: numans@ovn.org To: dev@openvswitch.org Date: Thu, 19 May 2022 11:18:12 -0400 Message-Id: <20220519151812.987296-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 3/3] northd: Add generic port security logical flows. 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 ovn-northd will now make use of the newly added OVN actions - check_in_port_sec and check_out_port_sec to implement the port security for the logical ports. Port security rules remain the same and nothing changes from CMS's perspective. Scale results: Used a Northbound database from a deployment of 120 node cluster. Number of logical switch ports with port security configured: 13711 With vanilla ovn-northd ----------------------- Number of logical flows : 208061 Avg time taken to run build_lflows() : 1301 msec Size of Southbound database after compaction: 104M With ovn-northd using this feature --------------------------------- Number of logical flows : 83396 Avg time taken to run build_lflows() : 560 msec Size of Southbound database after compaction: 45M This patch shows significant improvements and reduces number of lflows and ovn-northd CPU usage. This would benefit in the large scale deployments. This patch also show significant improvements in ovn-controller CPU utilization as there will be lesser logical flows to process. This change is not backward compatible. As per the update/upgrade recommendations, CMS should first update ovn-controllers with the newer versions before updating ovn-northd. Reported-at: https://bugzilla.redhat.com/show_bug.cgi?id=2078927 Suggested-by: Dumitru Ceara Acked-by: Mark Michelson Signed-off-by: Numan Siddique --- northd/northd.c | 557 +++++++--------------------------------- northd/ovn-northd.8.xml | 263 +++++++++---------- tests/ovn-northd.at | 431 ++++++++++++++++++++----------- tests/ovn.at | 77 +++--- 4 files changed, 536 insertions(+), 792 deletions(-) diff --git a/northd/northd.c b/northd/northd.c index 6138d42ad9..f0c345015d 100644 --- a/northd/northd.c +++ b/northd/northd.c @@ -107,32 +107,31 @@ enum ovn_datapath_type { enum ovn_stage { #define PIPELINE_STAGES \ /* Logical switch ingress stages. */ \ - PIPELINE_STAGE(SWITCH, IN, PORT_SEC_L2, 0, "ls_in_port_sec_l2") \ - PIPELINE_STAGE(SWITCH, IN, PORT_SEC_IP, 1, "ls_in_port_sec_ip") \ - PIPELINE_STAGE(SWITCH, IN, PORT_SEC_ND, 2, "ls_in_port_sec_nd") \ - PIPELINE_STAGE(SWITCH, IN, LOOKUP_FDB , 3, "ls_in_lookup_fdb") \ - PIPELINE_STAGE(SWITCH, IN, PUT_FDB, 4, "ls_in_put_fdb") \ - PIPELINE_STAGE(SWITCH, IN, PRE_ACL, 5, "ls_in_pre_acl") \ - PIPELINE_STAGE(SWITCH, IN, PRE_LB, 6, "ls_in_pre_lb") \ - PIPELINE_STAGE(SWITCH, IN, PRE_STATEFUL, 7, "ls_in_pre_stateful") \ - PIPELINE_STAGE(SWITCH, IN, ACL_HINT, 8, "ls_in_acl_hint") \ - PIPELINE_STAGE(SWITCH, IN, ACL, 9, "ls_in_acl") \ - PIPELINE_STAGE(SWITCH, IN, QOS_MARK, 10, "ls_in_qos_mark") \ - PIPELINE_STAGE(SWITCH, IN, QOS_METER, 11, "ls_in_qos_meter") \ - PIPELINE_STAGE(SWITCH, IN, LB, 12, "ls_in_lb") \ - PIPELINE_STAGE(SWITCH, IN, ACL_AFTER_LB, 13, "ls_in_acl_after_lb") \ - PIPELINE_STAGE(SWITCH, IN, STATEFUL, 14, "ls_in_stateful") \ - PIPELINE_STAGE(SWITCH, IN, PRE_HAIRPIN, 15, "ls_in_pre_hairpin") \ - PIPELINE_STAGE(SWITCH, IN, NAT_HAIRPIN, 16, "ls_in_nat_hairpin") \ - PIPELINE_STAGE(SWITCH, IN, HAIRPIN, 17, "ls_in_hairpin") \ - PIPELINE_STAGE(SWITCH, IN, ARP_ND_RSP, 18, "ls_in_arp_rsp") \ - PIPELINE_STAGE(SWITCH, IN, DHCP_OPTIONS, 19, "ls_in_dhcp_options") \ - PIPELINE_STAGE(SWITCH, IN, DHCP_RESPONSE, 20, "ls_in_dhcp_response") \ - PIPELINE_STAGE(SWITCH, IN, DNS_LOOKUP, 21, "ls_in_dns_lookup") \ - PIPELINE_STAGE(SWITCH, IN, DNS_RESPONSE, 22, "ls_in_dns_response") \ - PIPELINE_STAGE(SWITCH, IN, EXTERNAL_PORT, 23, "ls_in_external_port") \ - PIPELINE_STAGE(SWITCH, IN, L2_LKUP, 24, "ls_in_l2_lkup") \ - PIPELINE_STAGE(SWITCH, IN, L2_UNKNOWN, 25, "ls_in_l2_unknown") \ + PIPELINE_STAGE(SWITCH, IN, CHECK_PORT_SEC, 0, "ls_in_check_port_sec") \ + PIPELINE_STAGE(SWITCH, IN, APPLY_PORT_SEC, 1, "ls_in_apply_port_sec") \ + PIPELINE_STAGE(SWITCH, IN, LOOKUP_FDB , 2, "ls_in_lookup_fdb") \ + PIPELINE_STAGE(SWITCH, IN, PUT_FDB, 3, "ls_in_put_fdb") \ + PIPELINE_STAGE(SWITCH, IN, PRE_ACL, 4, "ls_in_pre_acl") \ + PIPELINE_STAGE(SWITCH, IN, PRE_LB, 5, "ls_in_pre_lb") \ + PIPELINE_STAGE(SWITCH, IN, PRE_STATEFUL, 6, "ls_in_pre_stateful") \ + PIPELINE_STAGE(SWITCH, IN, ACL_HINT, 7, "ls_in_acl_hint") \ + PIPELINE_STAGE(SWITCH, IN, ACL, 8, "ls_in_acl") \ + PIPELINE_STAGE(SWITCH, IN, QOS_MARK, 9, "ls_in_qos_mark") \ + PIPELINE_STAGE(SWITCH, IN, QOS_METER, 10, "ls_in_qos_meter") \ + PIPELINE_STAGE(SWITCH, IN, LB, 11, "ls_in_lb") \ + PIPELINE_STAGE(SWITCH, IN, ACL_AFTER_LB, 12, "ls_in_acl_after_lb") \ + PIPELINE_STAGE(SWITCH, IN, STATEFUL, 13, "ls_in_stateful") \ + PIPELINE_STAGE(SWITCH, IN, PRE_HAIRPIN, 14, "ls_in_pre_hairpin") \ + PIPELINE_STAGE(SWITCH, IN, NAT_HAIRPIN, 15, "ls_in_nat_hairpin") \ + PIPELINE_STAGE(SWITCH, IN, HAIRPIN, 16, "ls_in_hairpin") \ + PIPELINE_STAGE(SWITCH, IN, ARP_ND_RSP, 17, "ls_in_arp_rsp") \ + PIPELINE_STAGE(SWITCH, IN, DHCP_OPTIONS, 18, "ls_in_dhcp_options") \ + PIPELINE_STAGE(SWITCH, IN, DHCP_RESPONSE, 19, "ls_in_dhcp_response") \ + PIPELINE_STAGE(SWITCH, IN, DNS_LOOKUP, 20, "ls_in_dns_lookup") \ + PIPELINE_STAGE(SWITCH, IN, DNS_RESPONSE, 21, "ls_in_dns_response") \ + PIPELINE_STAGE(SWITCH, IN, EXTERNAL_PORT, 22, "ls_in_external_port") \ + PIPELINE_STAGE(SWITCH, IN, L2_LKUP, 23, "ls_in_l2_lkup") \ + PIPELINE_STAGE(SWITCH, IN, L2_UNKNOWN, 24, "ls_in_l2_unknown") \ \ /* Logical switch egress stages. */ \ PIPELINE_STAGE(SWITCH, OUT, PRE_LB, 0, "ls_out_pre_lb") \ @@ -143,8 +142,8 @@ enum ovn_stage { PIPELINE_STAGE(SWITCH, OUT, QOS_MARK, 5, "ls_out_qos_mark") \ PIPELINE_STAGE(SWITCH, OUT, QOS_METER, 6, "ls_out_qos_meter") \ PIPELINE_STAGE(SWITCH, OUT, STATEFUL, 7, "ls_out_stateful") \ - PIPELINE_STAGE(SWITCH, OUT, PORT_SEC_IP, 8, "ls_out_port_sec_ip") \ - PIPELINE_STAGE(SWITCH, OUT, PORT_SEC_L2, 9, "ls_out_port_sec_l2") \ + PIPELINE_STAGE(SWITCH, OUT, CHECK_PORT_SEC, 8, "ls_out_check_port_sec") \ + PIPELINE_STAGE(SWITCH, OUT, APPLY_PORT_SEC, 9, "ls_out_apply_port_sec") \ \ /* Logical router ingress stages. */ \ PIPELINE_STAGE(ROUTER, IN, ADMISSION, 0, "lr_in_admission") \ @@ -207,6 +206,7 @@ enum ovn_stage { #define REGBIT_HAIRPIN_REPLY "reg0[12]" #define REGBIT_ACL_LABEL "reg0[13]" #define REGBIT_FROM_RAMP "reg0[14]" +#define REGBIT_PORT_SEC_DROP "reg0[15]" #define REG_ORIG_DIP_IPV4 "reg1" #define REG_ORIG_DIP_IPV6 "xxreg1" @@ -3606,6 +3606,9 @@ ovn_port_update_sbrec(struct northd_input *input_data, sbrec_port_binding_set_tag(op->sb, op->nbsp->tag, op->nbsp->n_tag); sbrec_port_binding_set_mac(op->sb, (const char **) op->nbsp->addresses, op->nbsp->n_addresses); + sbrec_port_binding_set_port_security( + op->sb, (const char **) op->nbsp->port_security, + op->nbsp->n_port_security); struct smap ids = SMAP_INITIALIZER(&ids); smap_clone(&ids, &op->nbsp->external_ids); @@ -5015,332 +5018,6 @@ ovn_lflow_destroy(struct hmap *lflows, struct ovn_lflow *lflow) } } -/* Appends port security constraints on L2 address field 'eth_addr_field' - * (e.g. "eth.src" or "eth.dst") to 'match'. 'ps_addrs', with 'n_ps_addrs' - * elements, is the collection of port_security constraints from an - * OVN_NB Logical_Switch_Port row generated by extract_lsp_addresses(). */ -static void -build_port_security_l2(const char *eth_addr_field, - struct lport_addresses *ps_addrs, - unsigned int n_ps_addrs, - struct ds *match) -{ - if (!n_ps_addrs) { - return; - } - - ds_put_format(match, " && %s == {", eth_addr_field); - - for (size_t i = 0; i < n_ps_addrs; i++) { - ds_put_format(match, "%s ", ps_addrs[i].ea_s); - } - ds_chomp(match, ' '); - ds_put_cstr(match, "}"); -} - -static void -build_port_security_ipv6_nd_flow( - struct ds *match, struct eth_addr ea, struct ipv6_netaddr *ipv6_addrs, - int n_ipv6_addrs) -{ - ds_put_format(match, " && ip6 && nd && ((nd.sll == "ETH_ADDR_FMT" || " - "nd.sll == "ETH_ADDR_FMT") || ((nd.tll == "ETH_ADDR_FMT" || " - "nd.tll == "ETH_ADDR_FMT")", ETH_ADDR_ARGS(eth_addr_zero), - ETH_ADDR_ARGS(ea), ETH_ADDR_ARGS(eth_addr_zero), - ETH_ADDR_ARGS(ea)); - if (!n_ipv6_addrs) { - ds_put_cstr(match, "))"); - return; - } - - char ip6_str[INET6_ADDRSTRLEN + 1]; - struct in6_addr lla; - in6_generate_lla(ea, &lla); - memset(ip6_str, 0, sizeof(ip6_str)); - ipv6_string_mapped(ip6_str, &lla); - ds_put_format(match, " && (nd.target == %s", ip6_str); - - for (size_t i = 0; i < n_ipv6_addrs; i++) { - /* When the netmask is applied, if the host portion is - * non-zero, the host can only use the specified - * address in the nd.target. If zero, the host is allowed - * to use any address in the subnet. - */ - if (ipv6_addrs[i].plen == 128 - || !ipv6_addr_is_host_zero(&ipv6_addrs[i].addr, - &ipv6_addrs[i].mask)) { - ds_put_format(match, " || nd.target == %s", ipv6_addrs[i].addr_s); - } else { - ds_put_format(match, " || nd.target == %s/%d", - ipv6_addrs[i].network_s, ipv6_addrs[i].plen); - } - } - - ds_put_format(match, ")))"); -} - -static void -build_port_security_ipv6_flow( - enum ovn_pipeline pipeline, struct ds *match, struct eth_addr ea, - struct ipv6_netaddr *ipv6_addrs, int n_ipv6_addrs) -{ - char ip6_str[INET6_ADDRSTRLEN + 1]; - - ds_put_format(match, " && %s == {", - pipeline == P_IN ? "ip6.src" : "ip6.dst"); - - /* Allow link-local address. */ - struct in6_addr lla; - in6_generate_lla(ea, &lla); - ipv6_string_mapped(ip6_str, &lla); - ds_put_format(match, "%s, ", ip6_str); - - /* Allow ip6.dst=ff00::/8 for multicast packets */ - if (pipeline == P_OUT) { - ds_put_cstr(match, "ff00::/8, "); - } - for (size_t i = 0; i < n_ipv6_addrs; i++) { - /* When the netmask is applied, if the host portion is - * non-zero, the host can only use the specified - * address. If zero, the host is allowed to use any - * address in the subnet. - */ - if (ipv6_addrs[i].plen == 128 - || !ipv6_addr_is_host_zero(&ipv6_addrs[i].addr, - &ipv6_addrs[i].mask)) { - ds_put_format(match, "%s, ", ipv6_addrs[i].addr_s); - } else { - ds_put_format(match, "%s/%d, ", ipv6_addrs[i].network_s, - ipv6_addrs[i].plen); - } - } - /* Replace ", " by "}". */ - ds_chomp(match, ' '); - ds_chomp(match, ','); - ds_put_cstr(match, "}"); -} - -/** - * Build port security constraints on ARP and IPv6 ND fields - * and add logical flows to S_SWITCH_IN_PORT_SEC_ND stage. - * - * For each port security of the logical port, following - * logical flows are added - * - If the port security has no IP (both IPv4 and IPv6) or - * if it has IPv4 address(es) - * - Priority 90 flow to allow ARP packets for known MAC addresses - * in the eth.src and arp.spa fields. If the port security - * has IPv4 addresses, allow known IPv4 addresses in the arp.tpa field. - * - * - If the port security has no IP (both IPv4 and IPv6) or - * if it has IPv6 address(es) - * - Priority 90 flow to allow IPv6 ND packets for known MAC addresses - * in the eth.src and nd.sll/nd.tll fields. If the port security - * has IPv6 addresses, allow known IPv6 addresses in the nd.target field - * for IPv6 Neighbor Advertisement packet. - * - * - Priority 80 flow to drop ARP and IPv6 ND packets. - */ -static void -build_port_security_nd(struct ovn_port *op, struct hmap *lflows, - const struct ovsdb_idl_row *stage_hint) -{ - struct ds match = DS_EMPTY_INITIALIZER; - - for (size_t i = 0; i < op->n_ps_addrs; i++) { - struct lport_addresses *ps = &op->ps_addrs[i]; - - bool no_ip = !(ps->n_ipv4_addrs || ps->n_ipv6_addrs); - - ds_clear(&match); - if (ps->n_ipv4_addrs || no_ip) { - ds_put_format(&match, - "inport == %s && eth.src == %s && arp.sha == %s", - op->json_key, ps->ea_s, ps->ea_s); - - if (ps->n_ipv4_addrs) { - ds_put_cstr(&match, " && arp.spa == {"); - for (size_t j = 0; j < ps->n_ipv4_addrs; j++) { - /* When the netmask is applied, if the host portion is - * non-zero, the host can only use the specified - * address in the arp.spa. If zero, the host is allowed - * to use any address in the subnet. */ - if (ps->ipv4_addrs[j].plen == 32 - || ps->ipv4_addrs[j].addr & ~ps->ipv4_addrs[j].mask) { - ds_put_cstr(&match, ps->ipv4_addrs[j].addr_s); - } else { - ds_put_format(&match, "%s/%d", - ps->ipv4_addrs[j].network_s, - ps->ipv4_addrs[j].plen); - } - ds_put_cstr(&match, ", "); - } - ds_chomp(&match, ' '); - ds_chomp(&match, ','); - ds_put_cstr(&match, "}"); - } - ovn_lflow_add_with_lport_and_hint(lflows, op->od, - S_SWITCH_IN_PORT_SEC_ND, 90, - ds_cstr(&match), "next;", - op->key, stage_hint); - } - - if (ps->n_ipv6_addrs || no_ip) { - ds_clear(&match); - ds_put_format(&match, "inport == %s && eth.src == %s", - op->json_key, ps->ea_s); - build_port_security_ipv6_nd_flow(&match, ps->ea, ps->ipv6_addrs, - ps->n_ipv6_addrs); - ovn_lflow_add_with_lport_and_hint(lflows, op->od, - S_SWITCH_IN_PORT_SEC_ND, 90, - ds_cstr(&match), "next;", - op->key, stage_hint); - } - } - - ds_clear(&match); - ds_put_format(&match, "inport == %s && (arp || nd)", op->json_key); - ovn_lflow_add_with_lport_and_hint(lflows, op->od, S_SWITCH_IN_PORT_SEC_ND, - 80, ds_cstr(&match), "drop;", op->key, - stage_hint); - ds_destroy(&match); -} - -/** - * Build port security constraints on IPv4 and IPv6 src and dst fields - * and add logical flows to S_SWITCH_(IN/OUT)_PORT_SEC_IP stage. - * - * For each port security of the logical port, following - * logical flows are added - * - If the port security has IPv4 addresses, - * - Priority 90 flow to allow IPv4 packets for known IPv4 addresses - * - * - If the port security has IPv6 addresses, - * - Priority 90 flow to allow IPv6 packets for known IPv6 addresses - * - * - If the port security has IPv4 addresses or IPv6 addresses or both - * - Priority 80 flow to drop all IPv4 and IPv6 traffic - */ -static void -build_port_security_ip(enum ovn_pipeline pipeline, struct ovn_port *op, - struct hmap *lflows, - const struct ovsdb_idl_row *stage_hint) -{ - char *port_direction; - enum ovn_stage stage; - if (pipeline == P_IN) { - port_direction = "inport"; - stage = S_SWITCH_IN_PORT_SEC_IP; - } else { - port_direction = "outport"; - stage = S_SWITCH_OUT_PORT_SEC_IP; - } - - for (size_t i = 0; i < op->n_ps_addrs; i++) { - struct lport_addresses *ps = &op->ps_addrs[i]; - - if (!(ps->n_ipv4_addrs || ps->n_ipv6_addrs)) { - continue; - } - - if (ps->n_ipv4_addrs) { - struct ds match = DS_EMPTY_INITIALIZER; - if (pipeline == P_IN) { - /* Permit use of the unspecified address for DHCP discovery */ - struct ds dhcp_match = DS_EMPTY_INITIALIZER; - ds_put_format(&dhcp_match, "inport == %s" - " && eth.src == %s" - " && ip4.src == 0.0.0.0" - " && ip4.dst == 255.255.255.255" - " && udp.src == 68 && udp.dst == 67", - op->json_key, ps->ea_s); - ovn_lflow_add_with_lport_and_hint(lflows, op->od, stage, 90, - ds_cstr(&dhcp_match), - "next;", op->key, - stage_hint); - ds_destroy(&dhcp_match); - ds_put_format(&match, "inport == %s && eth.src == %s" - " && ip4.src == {", op->json_key, - ps->ea_s); - } else { - ds_put_format(&match, "outport == %s && eth.dst == %s" - " && ip4.dst == {255.255.255.255, 224.0.0.0/4, ", - op->json_key, ps->ea_s); - } - - for (int j = 0; j < ps->n_ipv4_addrs; j++) { - ovs_be32 mask = ps->ipv4_addrs[j].mask; - /* When the netmask is applied, if the host portion is - * non-zero, the host can only use the specified - * address. If zero, the host is allowed to use any - * address in the subnet. - */ - if (ps->ipv4_addrs[j].plen == 32 - || ps->ipv4_addrs[j].addr & ~mask) { - ds_put_format(&match, "%s", ps->ipv4_addrs[j].addr_s); - if (pipeline == P_OUT && ps->ipv4_addrs[j].plen != 32) { - /* Host is also allowed to receive packets to the - * broadcast address in the specified subnet. */ - ds_put_format(&match, ", %s", - ps->ipv4_addrs[j].bcast_s); - } - } else { - /* host portion is zero */ - ds_put_format(&match, "%s/%d", ps->ipv4_addrs[j].network_s, - ps->ipv4_addrs[j].plen); - } - ds_put_cstr(&match, ", "); - } - - /* Replace ", " by "}". */ - ds_chomp(&match, ' '); - ds_chomp(&match, ','); - ds_put_cstr(&match, "}"); - ovn_lflow_add_with_lport_and_hint(lflows, op->od, stage, 90, - ds_cstr(&match), "next;", - op->key, stage_hint); - ds_destroy(&match); - } - - if (ps->n_ipv6_addrs) { - struct ds match = DS_EMPTY_INITIALIZER; - if (pipeline == P_IN) { - /* Permit use of unspecified address for duplicate address - * detection */ - struct ds dad_match = DS_EMPTY_INITIALIZER; - ds_put_format(&dad_match, "inport == %s" - " && eth.src == %s" - " && ip6.src == ::" - " && ip6.dst == ff02::/16" - " && icmp6.type == {131, 135, 143}", - op->json_key, ps->ea_s); - ovn_lflow_add_with_lport_and_hint(lflows, op->od, stage, 90, - ds_cstr(&dad_match), "next;", - op->key, stage_hint); - ds_destroy(&dad_match); - } - ds_put_format(&match, "%s == %s && %s == %s", - port_direction, op->json_key, - pipeline == P_IN ? "eth.src" : "eth.dst", ps->ea_s); - build_port_security_ipv6_flow(pipeline, &match, ps->ea, - ps->ipv6_addrs, ps->n_ipv6_addrs); - ovn_lflow_add_with_lport_and_hint(lflows, op->od, stage, 90, - ds_cstr(&match), "next;", - op->key, stage_hint); - ds_destroy(&match); - } - - char *match = xasprintf("%s == %s && %s == %s && ip", - port_direction, op->json_key, - pipeline == P_IN ? "eth.src" : "eth.dst", - ps->ea_s); - ovn_lflow_add_with_lport_and_hint(lflows, op->od, stage, 80, match, - "drop;", op->key, stage_hint); - free(match); - } - -} - static bool build_dhcpv4_action(struct ovn_port *op, ovs_be32 offer_ip, struct ds *options_action, struct ds *response_action, @@ -5620,27 +5297,22 @@ ls_get_acl_flags(struct ovn_datapath *od) } } -/* Logical switch ingress table 0: Ingress port security - L2 - * (priority 50). - * Ingress table 1: Ingress port security - IP (priority 90 and 80) - * Ingress table 2: Ingress port security - ND (priority 90 and 80) +/* Adds the logical flows in the (in/out) check port sec stage only if + * - the lport is disabled or + * - lport is of type vtep - to skip the ingress pipeline. + * - lport has qdisc queue id is configured. + * + * For all the other logical ports, generic flow added in + * build_lswitch_lflows_admission_control() handles the port security. */ static void -build_lswitch_input_port_sec_op( - struct ovn_port *op, struct hmap *lflows, - struct ds *actions, struct ds *match) +build_lswitch_port_sec_op(struct ovn_port *op, struct hmap *lflows, + struct ds *actions, struct ds *match) { - if (!op->nbsp) { return; } - if (!lsp_is_enabled(op->nbsp)) { - /* Drop packets from disabled logical ports (since logical flow - * tables are default-drop). */ - return; - } - if (lsp_is_external(op->nbsp)) { return; } @@ -5648,8 +5320,21 @@ build_lswitch_input_port_sec_op( ds_clear(match); ds_clear(actions); ds_put_format(match, "inport == %s", op->json_key); - build_port_security_l2("eth.src", op->ps_addrs, op->n_ps_addrs, - match); + if (!lsp_is_enabled(op->nbsp)) { + /* Drop packets from disabled logical ports. */ + ovn_lflow_add_with_lport_and_hint( + lflows, op->od, S_SWITCH_IN_CHECK_PORT_SEC, + 100, ds_cstr(match), REGBIT_PORT_SEC_DROP" = 1; next;", + op->key, &op->nbsp->header_); + + ds_clear(match); + ds_put_format(match, "outport == %s", op->json_key); + ovn_lflow_add_with_lport_and_hint( + lflows, op->od, S_SWITCH_OUT_CHECK_PORT_SEC, 150, + ds_cstr(match), REGBIT_PORT_SEC_DROP" = 1; next;", + op->key, &op->nbsp->header_); + return; + } const char *queue_id = smap_get(&op->sb->options, "qdisc_queue_id"); if (queue_id) { @@ -5660,31 +5345,28 @@ build_lswitch_input_port_sec_op( ds_put_format(actions, REGBIT_FROM_RAMP" = 1; "); ds_put_format(actions, "next(pipeline=ingress, table=%d);", ovn_stage_get_table(S_SWITCH_IN_HAIRPIN)); - } else { - ds_put_cstr(actions, "next;"); - } - - ovn_lflow_add_with_lport_and_hint(lflows, op->od, S_SWITCH_IN_PORT_SEC_L2, - 50, ds_cstr(match), ds_cstr(actions), - op->key, &op->nbsp->header_); - - if (op->nbsp->n_port_security) { - build_port_security_ip(P_IN, op, lflows, &op->nbsp->header_); - build_port_security_nd(op, lflows, &op->nbsp->header_); - } -} - -/* Ingress table 1 and 2: Port security - IP and ND, by default - * goto next. (priority 0) - */ -static void -build_lswitch_input_port_sec_od( - struct ovn_datapath *od, struct hmap *lflows) -{ + ovn_lflow_add_with_lport_and_hint(lflows, op->od, + S_SWITCH_IN_CHECK_PORT_SEC, 70, + ds_cstr(match), ds_cstr(actions), + op->key, &op->nbsp->header_); + } else if (queue_id) { + ds_put_cstr(actions, + REGBIT_PORT_SEC_DROP" = check_in_port_sec(); next;"); + ovn_lflow_add_with_lport_and_hint(lflows, op->od, + S_SWITCH_IN_CHECK_PORT_SEC, 70, + ds_cstr(match), ds_cstr(actions), + op->key, &op->nbsp->header_); - if (od->nbs) { - ovn_lflow_add(lflows, od, S_SWITCH_IN_PORT_SEC_ND, 0, "1", "next;"); - ovn_lflow_add(lflows, od, S_SWITCH_IN_PORT_SEC_IP, 0, "1", "next;"); + if (!strcmp(op->nbsp->type, "localnet")) { + ds_clear(match); + ds_clear(actions); + ds_put_format(match, "outport == %s", op->json_key); + ds_put_format(actions, "set_queue(%s); output;", queue_id); + ovn_lflow_add_with_lport_and_hint(lflows, op->od, + S_SWITCH_OUT_APPLY_PORT_SEC, 100, + ds_cstr(match), ds_cstr(actions), + op->key, &op->nbsp->header_); + } } } @@ -5726,58 +5408,6 @@ build_lswitch_learn_fdb_od( } } -/* Egress table 8: Egress port security - IP (priorities 90 and 80) - * if port security enabled. - * - * Egress table 9: Egress port security - L2 (priorities 50 and 150). - * - * Priority 50 rules implement port security for enabled logical port. - * - * Priority 150 rules drop packets to disabled logical ports, so that - * they don't even receive multicast or broadcast packets. - */ -static void -build_lswitch_output_port_sec_op(struct ovn_port *op, - struct hmap *lflows, - struct ds *match, - struct ds *actions) -{ - - if (op->nbsp && (!lsp_is_external(op->nbsp))) { - - ds_clear(actions); - ds_clear(match); - - ds_put_format(match, "outport == %s", op->json_key); - if (lsp_is_enabled(op->nbsp)) { - build_port_security_l2("eth.dst", op->ps_addrs, op->n_ps_addrs, - match); - - if (!strcmp(op->nbsp->type, "localnet")) { - const char *queue_id = smap_get(&op->sb->options, - "qdisc_queue_id"); - if (queue_id) { - ds_put_format(actions, "set_queue(%s); ", queue_id); - } - } - ds_put_cstr(actions, "output;"); - ovn_lflow_add_with_lport_and_hint(lflows, op->od, - S_SWITCH_OUT_PORT_SEC_L2, 50, - ds_cstr(match), ds_cstr(actions), - op->key, &op->nbsp->header_); - } else { - ovn_lflow_add_with_lport_and_hint(lflows, op->od, - S_SWITCH_OUT_PORT_SEC_L2, 150, - ds_cstr(match), "drop;", op->key, - &op->nbsp->header_); - } - - if (op->nbsp->n_port_security) { - build_port_security_ip(P_OUT, op, lflows, &op->nbsp->header_); - } - } -} - /* Egress tables 8: Egress port security - IP (priority 0) * Egress table 9: Egress port security L2 - multicast/broadcast * (priority 100). */ @@ -5786,9 +5416,16 @@ build_lswitch_output_port_sec_od(struct ovn_datapath *od, struct hmap *lflows) { if (od->nbs) { - ovn_lflow_add(lflows, od, S_SWITCH_OUT_PORT_SEC_IP, 0, "1", "next;"); - ovn_lflow_add(lflows, od, S_SWITCH_OUT_PORT_SEC_L2, 100, "eth.mcast", - "output;"); + ovn_lflow_add(lflows, od, S_SWITCH_OUT_CHECK_PORT_SEC, 100, + "eth.mcast", REGBIT_PORT_SEC_DROP" = 0; next;"); + ovn_lflow_add(lflows, od, S_SWITCH_OUT_CHECK_PORT_SEC, 0, "1", + REGBIT_PORT_SEC_DROP" = check_out_port_sec(); next;"); + + ovn_lflow_add(lflows, od, S_SWITCH_OUT_APPLY_PORT_SEC, 50, + REGBIT_PORT_SEC_DROP" == 1", "drop;"); + ovn_lflow_add(lflows, od, S_SWITCH_OUT_APPLY_PORT_SEC, 0, + "1", "output;"); + } } @@ -7947,17 +7584,21 @@ build_lswitch_lflows_admission_control(struct ovn_datapath *od, /* Logical VLANs not supported. */ if (!is_vlan_transparent(od)) { /* Block logical VLANs. */ - ovn_lflow_add(lflows, od, S_SWITCH_IN_PORT_SEC_L2, 100, + ovn_lflow_add(lflows, od, S_SWITCH_IN_CHECK_PORT_SEC, 100, "vlan.present", "drop;"); } /* Broadcast/multicast source address is invalid. */ - ovn_lflow_add(lflows, od, S_SWITCH_IN_PORT_SEC_L2, 100, "eth.src[40]", - "drop;"); + ovn_lflow_add(lflows, od, S_SWITCH_IN_CHECK_PORT_SEC, 100, + "eth.src[40]", "drop;"); + + ovn_lflow_add(lflows, od, S_SWITCH_IN_CHECK_PORT_SEC, 50, "1", + REGBIT_PORT_SEC_DROP" = check_in_port_sec(); next;"); + + ovn_lflow_add(lflows, od, S_SWITCH_IN_APPLY_PORT_SEC, 50, + REGBIT_PORT_SEC_DROP" == 1", "drop;"); - /* Port security flows have priority 50 - * (see build_lswitch_input_port_sec()) and will continue - * to the next table if packet source is acceptable. */ + ovn_lflow_add(lflows, od, S_SWITCH_IN_APPLY_PORT_SEC, 0, "1", "next;"); } } @@ -13863,7 +13504,6 @@ build_lswitch_and_lrouter_iterate_by_od(struct ovn_datapath *od, build_fwd_group_lflows(od, lsi->lflows); build_lswitch_lflows_admission_control(od, lsi->lflows); - build_lswitch_input_port_sec_od(od, lsi->lflows); build_lswitch_learn_fdb_od(od, lsi->lflows); build_lswitch_arp_nd_responder_default(od, lsi->lflows); build_lswitch_dns_lookup_and_response(od, lsi->lflows, lsi->meter_groups); @@ -13904,8 +13544,7 @@ build_lswitch_and_lrouter_iterate_by_op(struct ovn_port *op, struct lswitch_flow_build_info *lsi) { /* Build Logical Switch Flows. */ - build_lswitch_input_port_sec_op(op, lsi->lflows, &lsi->actions, - &lsi->match); + build_lswitch_port_sec_op(op, lsi->lflows, &lsi->actions, &lsi->match); build_lswitch_learn_fdb_op(op, lsi->lflows, &lsi->actions, &lsi->match); build_lswitch_arp_nd_responder_skip_local(op, lsi->lflows, @@ -13920,8 +13559,6 @@ build_lswitch_and_lrouter_iterate_by_op(struct ovn_port *op, build_lswitch_external_port(op, lsi->lflows); build_lswitch_ip_unicast_lookup(op, lsi->lflows, lsi->mcgroups, &lsi->actions, &lsi->match); - build_lswitch_output_port_sec_op(op, lsi->lflows, - &lsi->actions, &lsi->match); /* Build Logical Router Flows. */ build_adm_ctrl_flows_for_lrouter_port(op, lsi->lflows, &lsi->match, diff --git a/northd/ovn-northd.8.xml b/northd/ovn-northd.8.xml index 73ecfbc2d9..10fc3bcc89 100644 --- a/northd/ovn-northd.8.xml +++ b/northd/ovn-northd.8.xml @@ -291,7 +291,7 @@

Logical Switch Datapaths

-

Ingress Table 0: Admission Control and Ingress Port Security - L2

+

Ingress Table 0: Admission Control and Ingress Port Security check

Ingress table 0 contains these logical flows: @@ -304,122 +304,60 @@

  • - Priority 50 flows that implement ingress port security for each enabled - logical port. For logical ports on which port security is enabled, - these match the inport and the valid eth.src - address(es) and advance only those packets to the next flow table. For - logical ports on which port security is not enabled, these advance all - packets that match the inport. + For each disabled logical port, a priority 100 flow is added which + matches on all packets and applies the action + REGBIT_PORT_SEC_DROP" = 1; next;" so that the packets are + dropped in the next stage.
  • +
  • - For logical ports of type vtep, the above logical flow - will apply the action + For each (enabled) vtep logical port, a priority 70 flow is added which + matches on all packets and applies the action next(pipeline=ingress, table=S_SWITCH_IN_L2_LKUP) = 1; to skip most stages of ingress pipeline and go directly to ingress L2 lookup table to determine the output port. Packets from VTEP (RAMP) switch should not be subjected to any ACL checks. Egress pipeline will do the ACL checks.
  • - - -

    - There are no flows for disabled logical ports because the default-drop - behavior of logical flow tables causes packets that ingress from them to - be dropped. -

    - -

    Ingress Table 1: Ingress Port Security - IP

    - -

    - Ingress table 1 contains these logical flows: -

    -
    • -

      - For each element in the port security set having one or more IPv4 or - IPv6 addresses (or both), -

      - -
        -
      • - Priority 90 flow to allow IPv4 traffic if it has IPv4 addresses - which match the inport, valid eth.src - and valid ip4.src address(es). -
      • - -
      • - Priority 90 flow to allow IPv4 DHCP discovery traffic if it has a - valid eth.src. This is necessary since DHCP discovery - messages are sent from the unspecified IPv4 address (0.0.0.0) since - the IPv4 address has not yet been assigned. -
      • - -
      • - Priority 90 flow to allow IPv6 traffic if it has IPv6 addresses - which match the inport, valid eth.src and - valid ip6.src address(es). -
      • - -
      • - Priority 90 flow to allow IPv6 DAD (Duplicate Address Detection) - traffic if it has a valid eth.src. This is is - necessary since DAD include requires joining an multicast group and - sending neighbor solicitations for the newly assigned address. Since - no address is yet assigned, these are sent from the unspecified - IPv6 address (::). -
      • - -
      • - Priority 80 flow to drop IP (both IPv4 and IPv6) traffic which - match the inport and valid eth.src. -
      • -
      + For each enabled logical port configured with qdisc queue id in the + column of , a priority 70 flow is added which + matches on all packets and applies the action + set_queue(id); + REGBIT_PORT_SEC_DROP" = check_in_port_sec(); next;".
    • - One priority-0 fallback flow that matches all packets and advances to - the next table. + A priority 1 flow is added which matches on all packets for all the + logical ports and applies the action + REGBIT_PORT_SEC_DROP" = check_in_port_sec(); next; to + evaluate the port security. The action check_in_port_sec + applies the port security rules defined in the + column of table.
    -

    Ingress Table 2: Ingress Port Security - Neighbor discovery

    +

    Ingress Table 1: Ingress Port Security - Apply

    + +

    + This table drops the packets if the port security check failed + in the previous stage i.e the register bit + REGBIT_PORT_SEC_DROP is set to 1. +

    - Ingress table 2 contains these logical flows: + Ingress table 1 contains these logical flows:

    • -

      - For each element in the port security set, -

      - -
        -
      • - Priority 90 flow to allow ARP traffic which match the - inport and valid eth.src and - arp.sha. If the element has one or more - IPv4 addresses, then it also matches the valid - arp.spa. -
      • - -
      • - Priority 90 flow to allow IPv6 Neighbor Solicitation and - Advertisement traffic which match the inport, - valid eth.src and - nd.sll/nd.tll. - If the element has one or more IPv6 addresses, then it also - matches the valid nd.target address(es) for Neighbor - Advertisement traffic. -
      • - -
      • - Priority 80 flow to drop ARP and IPv6 Neighbor Solicitation and - Advertisement traffic which match the inport and - valid eth.src. -
      • -
      + A priority-50 fallback flow that drops the packet if the register + bit REGBIT_PORT_SEC_DROP is set to 1.
    • @@ -428,7 +366,7 @@
    -

    Ingress Table 3: Lookup MAC address learning table

    +

    Ingress Table 2: Lookup MAC address learning table

    This table looks up the MAC learning table of the logical switch @@ -460,7 +398,7 @@ -

    Ingress Table 4: Learn MAC of 'unknown' ports.

    +

    Ingress Table 3: Learn MAC of 'unknown' ports.

    This table learns the MAC addresses seen on the logical ports @@ -494,7 +432,7 @@ -

    Ingress Table 5: from-lport Pre-ACLs

    +

    Ingress Table 4: from-lport Pre-ACLs

    This table prepares flows for possible stateful ACL processing in @@ -521,7 +459,7 @@ db="OVN_Northbound"/> table.

    -

    Ingress Table 6: Pre-LB

    +

    Ingress Table 5: Pre-LB

    This table prepares flows for possible stateful load balancing processing @@ -595,7 +533,7 @@ logical router datapath to logical switch datapath.

    -

    Ingress Table 7: Pre-stateful

    +

    Ingress Table 6: Pre-stateful

    This table prepares flows for all possible stateful processing @@ -633,7 +571,7 @@ -

    Ingress Table 8: from-lport ACL hints

    +

    Ingress Table 7: from-lport ACL hints

    This table consists of logical flows that set hints @@ -718,7 +656,7 @@ -

    Ingress table 9: from-lport ACLs before LB

    +

    Ingress table 8: from-lport ACLs before LB

    Logical flows in this table closely reproduce those in the @@ -881,7 +819,7 @@ -

    Ingress Table 10: from-lport QoS Marking

    +

    Ingress Table 9: from-lport QoS Marking

    Logical flows in this table closely reproduce those in the @@ -903,7 +841,7 @@ -

    Ingress Table 11: from-lport QoS Meter

    +

    Ingress Table 10: from-lport QoS Meter

    Logical flows in this table closely reproduce those in the @@ -925,7 +863,7 @@ -

    Ingress Table 12: LB

    +

    Ingress Table 11: LB

    • @@ -978,7 +916,7 @@
    -

    Ingress table 13: from-lport ACLs after LB

    +

    Ingress table 12: from-lport ACLs after LB

    Logical flows in this table closely reproduce those in the @@ -1040,7 +978,7 @@ -

    Ingress Table 14: Stateful

    +

    Ingress Table 13: Stateful

    • @@ -1063,7 +1001,7 @@
    -

    Ingress Table 15: Pre-Hairpin

    +

    Ingress Table 14: Pre-Hairpin

    • If the logical switch has load balancer(s) configured, then a @@ -1081,7 +1019,7 @@
    -

    Ingress Table 16: Nat-Hairpin

    +

    Ingress Table 15: Nat-Hairpin

    • If the logical switch has load balancer(s) configured, then a @@ -1116,7 +1054,7 @@
    -

    Ingress Table 17: Hairpin

    +

    Ingress Table 16: Hairpin

    • @@ -1150,7 +1088,7 @@

    -

    Ingress Table 18: ARP/ND responder

    +

    Ingress Table 17: ARP/ND responder

    This table implements ARP/ND responder in a logical switch for known @@ -1452,7 +1390,7 @@ output; -

    Ingress Table 19: DHCP option processing

    +

    Ingress Table 18: DHCP option processing

    This table adds the DHCPv4 options to a DHCPv4 packet from the @@ -1513,7 +1451,7 @@ next; -

    Ingress Table 20: DHCP responses

    +

    Ingress Table 19: DHCP responses

    This table implements DHCP responder for the DHCP replies generated by @@ -1594,7 +1532,7 @@ output; -

    Ingress Table 21 DNS Lookup

    +

    Ingress Table 20 DNS Lookup

    This table looks up and resolves the DNS names to the corresponding @@ -1623,7 +1561,7 @@ reg0[4] = dns_lookup(); next; -

    Ingress Table 22 DNS Responses

    +

    Ingress Table 21 DNS Responses

    This table implements DNS responder for the DNS replies generated by @@ -1658,7 +1596,7 @@ output; -

    Ingress table 23 External ports

    +

    Ingress table 22 External ports

    Traffic from the external logical ports enter the ingress @@ -1701,7 +1639,7 @@ output; -

    Ingress Table 24 Destination Lookup

    +

    Ingress Table 23 Destination Lookup

    This table implements switching behavior. It contains these logical @@ -1873,7 +1811,7 @@ output; -

    Ingress Table 25 Destination unknown

    +

    Ingress Table 24 Destination unknown

    This table handles the packets whose destination was not found or @@ -2063,38 +2001,83 @@ output; there are no rules added for load balancing new connections.

    -

    Egress Table 8: Egress Port Security - IP

    +

    Egress Table 8: Egress Port Security - check

    This is similar to the port security logic in table - Ingress Port Security - IP except that outport, - eth.dst, ip4.dst and ip6.dst - are checked instead of inport, eth.src, - ip4.src and ip6.src + Ingress Port Security check except that action + check_out_port_sec is used to check the port security + rules. This table adds the below logical flows.

    -

    Egress Table 9: Egress Port Security - L2

    +
      +
    • + A priority 100 flow which matches on the multicast traffic and applies + the action REGBIT_PORT_SEC_DROP" = 0; next;" to skip + the out port security checks. +
    • + +
    • + For each disabled logical port, a priority 150 flow is added which + matches on all packets and applies the action + REGBIT_PORT_SEC_DROP" = 1; next;" so that the packets are + dropped in the next stage. +
    • + +
    • + A priority 0 logical flow is added which matches on all the packets + and applies the action + REGBIT_PORT_SEC_DROP" = check_out_port_sec(); next;". + The action check_out_port_sec applies the port security + rules based on the addresses defined in the + column of table before delivering the packet to the + outport. +
    • +
    + +

    Egress Table 9: Egress Port Security - Apply

    This is similar to the ingress port security logic in ingress table - Admission Control and Ingress Port Security - L2, - but with important differences. Most obviously, outport and - eth.dst are checked instead of inport and - eth.src. Second, packets directed to broadcast or multicast - eth.dst are always accepted instead of being subject to the - port security rules; this is implemented through a priority-100 flow that - matches on eth.mcast with action output;. - Moreover, to ensure that even broadcast and multicast packets are not - delivered to disabled logical ports, a priority-150 flow for each - disabled logical outport overrides the priority-100 flow - with a drop; action. - Finally if egress qos has been enabled on a localnet port, the outgoing - queue id is set through set_queue action. Please remember to - mark the corresponding physical interface with - ovn-egress-iface set to true in + A Ingress Port Security - Apply. This table drops the + packets if the port security check failed in the previous stage i.e + the register bit REGBIT_PORT_SEC_DROP is set to 1.

    +

    + The following flows are added. +

    + +
      +
    • +

      + For each localnet port configured with egress qos in the + column of , a priority 100 flow is added which + matches on the localnet outport and applies the action + set_queue(id); output;". +

      + +

      + Please remember to mark the corresponding physical interface with + ovn-egress-iface set to true in + . +

      +
    • + +
    • + A priority-50 flow that drops the packet if the register + bit REGBIT_PORT_SEC_DROP is set to 1. +
    • + +
    • + A priority-0 flow that outputs the packet to the outport. +
    • +
    +

    Logical Router Datapaths

    diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at index 1804067cbe..20ec14bed8 100644 --- a/tests/ovn-northd.at +++ b/tests/ovn-northd.at @@ -2043,10 +2043,10 @@ AT_CAPTURE_FILE([sw1flows]) AT_CHECK( [grep -E 'ls_(in|out)_acl' sw0flows sw1flows | grep pg0 | sort], [0], [dnl -sw0flows: table=4 (ls_out_acl ), priority=2003 , match=(outport == @pg0 && ip6 && udp), action=(reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=24); };) -sw0flows: table=9 (ls_in_acl ), priority=2002 , match=(inport == @pg0 && ip4 && tcp && tcp.dst == 80), action=(reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=egress,table=5); };) -sw1flows: table=4 (ls_out_acl ), priority=2003 , match=(outport == @pg0 && ip6 && udp), action=(reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=24); };) -sw1flows: table=9 (ls_in_acl ), priority=2002 , match=(inport == @pg0 && ip4 && tcp && tcp.dst == 80), action=(reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=egress,table=5); };) +sw0flows: table=4 (ls_out_acl ), priority=2003 , match=(outport == @pg0 && ip6 && udp), action=(reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=23); };) +sw0flows: table=8 (ls_in_acl ), priority=2002 , match=(inport == @pg0 && ip4 && tcp && tcp.dst == 80), action=(reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=egress,table=5); };) +sw1flows: table=4 (ls_out_acl ), priority=2003 , match=(outport == @pg0 && ip6 && udp), action=(reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=23); };) +sw1flows: table=8 (ls_in_acl ), priority=2002 , match=(inport == @pg0 && ip4 && tcp && tcp.dst == 80), action=(reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=egress,table=5); };) ]) AS_BOX([2]) @@ -2059,10 +2059,10 @@ ovn-sbctl dump-flows sw1 > sw1flows2 AT_CAPTURE_FILE([sw1flows2]) AT_CHECK([grep "ls_out_acl" sw0flows2 sw1flows2 | grep pg0 | sort], [0], [dnl -sw0flows2: table=4 (ls_out_acl ), priority=2002 , match=(outport == @pg0 && ip4 && udp), action=(reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=24); };) -sw0flows2: table=4 (ls_out_acl ), priority=2003 , match=(outport == @pg0 && ip6 && udp), action=(reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=24); };) -sw1flows2: table=4 (ls_out_acl ), priority=2002 , match=(outport == @pg0 && ip4 && udp), action=(reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=24); };) -sw1flows2: table=4 (ls_out_acl ), priority=2003 , match=(outport == @pg0 && ip6 && udp), action=(reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=24); };) +sw0flows2: table=4 (ls_out_acl ), priority=2002 , match=(outport == @pg0 && ip4 && udp), action=(reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=23); };) +sw0flows2: table=4 (ls_out_acl ), priority=2003 , match=(outport == @pg0 && ip6 && udp), action=(reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=23); };) +sw1flows2: table=4 (ls_out_acl ), priority=2002 , match=(outport == @pg0 && ip4 && udp), action=(reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=23); };) +sw1flows2: table=4 (ls_out_acl ), priority=2003 , match=(outport == @pg0 && ip6 && udp), action=(reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=23); };) ]) AS_BOX([3]) @@ -2077,16 +2077,16 @@ AT_CAPTURE_FILE([sw1flows3]) AT_CHECK([grep "ls_out_acl" sw0flows3 sw1flows3 | grep pg0 | sort], [0], [dnl sw0flows3: table=4 (ls_out_acl ), priority=2001 , match=(reg0[[7]] == 1 && (outport == @pg0 && ip)), action=(reg0[[1]] = 1; next;) sw0flows3: table=4 (ls_out_acl ), priority=2001 , match=(reg0[[8]] == 1 && (outport == @pg0 && ip)), action=(next;) -sw0flows3: table=4 (ls_out_acl ), priority=2002 , match=((reg0[[10]] == 1) && outport == @pg0 && ip4 && udp), action=(ct_commit { ct_mark.blocked = 1; }; reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=24); };) -sw0flows3: table=4 (ls_out_acl ), priority=2002 , match=((reg0[[9]] == 1) && outport == @pg0 && ip4 && udp), action=(reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=24); };) -sw0flows3: table=4 (ls_out_acl ), priority=2003 , match=((reg0[[10]] == 1) && outport == @pg0 && ip6 && udp), action=(ct_commit { ct_mark.blocked = 1; }; reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=24); };) -sw0flows3: table=4 (ls_out_acl ), priority=2003 , match=((reg0[[9]] == 1) && outport == @pg0 && ip6 && udp), action=(reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=24); };) +sw0flows3: table=4 (ls_out_acl ), priority=2002 , match=((reg0[[10]] == 1) && outport == @pg0 && ip4 && udp), action=(ct_commit { ct_mark.blocked = 1; }; reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=23); };) +sw0flows3: table=4 (ls_out_acl ), priority=2002 , match=((reg0[[9]] == 1) && outport == @pg0 && ip4 && udp), action=(reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=23); };) +sw0flows3: table=4 (ls_out_acl ), priority=2003 , match=((reg0[[10]] == 1) && outport == @pg0 && ip6 && udp), action=(ct_commit { ct_mark.blocked = 1; }; reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=23); };) +sw0flows3: table=4 (ls_out_acl ), priority=2003 , match=((reg0[[9]] == 1) && outport == @pg0 && ip6 && udp), action=(reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=23); };) sw1flows3: table=4 (ls_out_acl ), priority=2001 , match=(reg0[[7]] == 1 && (outport == @pg0 && ip)), action=(reg0[[1]] = 1; next;) sw1flows3: table=4 (ls_out_acl ), priority=2001 , match=(reg0[[8]] == 1 && (outport == @pg0 && ip)), action=(next;) -sw1flows3: table=4 (ls_out_acl ), priority=2002 , match=((reg0[[10]] == 1) && outport == @pg0 && ip4 && udp), action=(ct_commit { ct_mark.blocked = 1; }; reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=24); };) -sw1flows3: table=4 (ls_out_acl ), priority=2002 , match=((reg0[[9]] == 1) && outport == @pg0 && ip4 && udp), action=(reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=24); };) -sw1flows3: table=4 (ls_out_acl ), priority=2003 , match=((reg0[[10]] == 1) && outport == @pg0 && ip6 && udp), action=(ct_commit { ct_mark.blocked = 1; }; reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=24); };) -sw1flows3: table=4 (ls_out_acl ), priority=2003 , match=((reg0[[9]] == 1) && outport == @pg0 && ip6 && udp), action=(reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=24); };) +sw1flows3: table=4 (ls_out_acl ), priority=2002 , match=((reg0[[10]] == 1) && outport == @pg0 && ip4 && udp), action=(ct_commit { ct_mark.blocked = 1; }; reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=23); };) +sw1flows3: table=4 (ls_out_acl ), priority=2002 , match=((reg0[[9]] == 1) && outport == @pg0 && ip4 && udp), action=(reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=23); };) +sw1flows3: table=4 (ls_out_acl ), priority=2003 , match=((reg0[[10]] == 1) && outport == @pg0 && ip6 && udp), action=(ct_commit { ct_mark.blocked = 1; }; reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=23); };) +sw1flows3: table=4 (ls_out_acl ), priority=2003 , match=((reg0[[9]] == 1) && outport == @pg0 && ip6 && udp), action=(reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=23); };) ]) AT_CLEANUP ]) @@ -2238,18 +2238,18 @@ AT_CHECK([ovn-sbctl lflow-list ls | grep -e ls_in_acl_hint -e ls_out_acl_hint -e table=4 (ls_out_acl ), priority=65532, match=(!ct.est && ct.rel && !ct.new && !ct.inv && ct_mark.blocked == 0), action=(next;) table=4 (ls_out_acl ), priority=65532, match=(ct.est && !ct.rel && !ct.new && !ct.inv && ct.rpl && ct_mark.blocked == 0), action=(next;) table=4 (ls_out_acl ), priority=65532, match=(ct.inv || (ct.est && ct.rpl && ct_mark.blocked == 1)), action=(drop;) - table=8 (ls_in_acl_hint ), priority=1 , match=(ct.est && ct_mark.blocked == 0), action=(reg0[[10]] = 1; next;) - table=8 (ls_in_acl_hint ), priority=2 , match=(ct.est && ct_mark.blocked == 1), action=(reg0[[9]] = 1; next;) - table=8 (ls_in_acl_hint ), priority=3 , match=(!ct.est), action=(reg0[[9]] = 1; next;) - table=8 (ls_in_acl_hint ), priority=4 , match=(!ct.new && ct.est && !ct.rpl && ct_mark.blocked == 0), action=(reg0[[8]] = 1; reg0[[10]] = 1; next;) - table=8 (ls_in_acl_hint ), priority=5 , match=(!ct.trk), action=(reg0[[8]] = 1; reg0[[9]] = 1; next;) - table=8 (ls_in_acl_hint ), priority=6 , match=(!ct.new && ct.est && !ct.rpl && ct_mark.blocked == 1), action=(reg0[[7]] = 1; reg0[[9]] = 1; next;) - table=8 (ls_in_acl_hint ), priority=7 , match=(ct.new && !ct.est), action=(reg0[[7]] = 1; reg0[[9]] = 1; next;) - table=9 (ls_in_acl ), priority=1 , match=(ip && !ct.est), action=(reg0[[1]] = 1; next;) - table=9 (ls_in_acl ), priority=1 , match=(ip && ct.est && ct_mark.blocked == 1), action=(reg0[[1]] = 1; next;) - table=9 (ls_in_acl ), priority=65532, match=(!ct.est && ct.rel && !ct.new && !ct.inv && ct_mark.blocked == 0), action=(next;) - table=9 (ls_in_acl ), priority=65532, match=(ct.est && !ct.rel && !ct.new && !ct.inv && ct.rpl && ct_mark.blocked == 0), action=(reg0[[9]] = 0; reg0[[10]] = 0; next;) - table=9 (ls_in_acl ), priority=65532, match=(ct.inv || (ct.est && ct.rpl && ct_mark.blocked == 1)), action=(drop;) + table=7 (ls_in_acl_hint ), priority=1 , match=(ct.est && ct_mark.blocked == 0), action=(reg0[[10]] = 1; next;) + table=7 (ls_in_acl_hint ), priority=2 , match=(ct.est && ct_mark.blocked == 1), action=(reg0[[9]] = 1; next;) + table=7 (ls_in_acl_hint ), priority=3 , match=(!ct.est), action=(reg0[[9]] = 1; next;) + table=7 (ls_in_acl_hint ), priority=4 , match=(!ct.new && ct.est && !ct.rpl && ct_mark.blocked == 0), action=(reg0[[8]] = 1; reg0[[10]] = 1; next;) + table=7 (ls_in_acl_hint ), priority=5 , match=(!ct.trk), action=(reg0[[8]] = 1; reg0[[9]] = 1; next;) + table=7 (ls_in_acl_hint ), priority=6 , match=(!ct.new && ct.est && !ct.rpl && ct_mark.blocked == 1), action=(reg0[[7]] = 1; reg0[[9]] = 1; next;) + table=7 (ls_in_acl_hint ), priority=7 , match=(ct.new && !ct.est), action=(reg0[[7]] = 1; reg0[[9]] = 1; next;) + table=8 (ls_in_acl ), priority=1 , match=(ip && !ct.est), action=(reg0[[1]] = 1; next;) + table=8 (ls_in_acl ), priority=1 , match=(ip && ct.est && ct_mark.blocked == 1), action=(reg0[[1]] = 1; next;) + table=8 (ls_in_acl ), priority=65532, match=(!ct.est && ct.rel && !ct.new && !ct.inv && ct_mark.blocked == 0), action=(next;) + table=8 (ls_in_acl ), priority=65532, match=(ct.est && !ct.rel && !ct.new && !ct.inv && ct.rpl && ct_mark.blocked == 0), action=(reg0[[9]] = 0; reg0[[10]] = 0; next;) + table=8 (ls_in_acl ), priority=65532, match=(ct.inv || (ct.est && ct.rpl && ct_mark.blocked == 1)), action=(drop;) ]) AS_BOX([Check match ct_state with load balancer]) @@ -2260,7 +2260,7 @@ check ovn-nbctl --wait=sb \ -- ls-lb-add ls lb AT_CHECK([ovn-sbctl lflow-list ls | grep -e ls_in_acl_hint -e ls_out_acl_hint -e ls_in_acl -e ls_out_acl | sort], [0], [dnl - table=13(ls_in_acl_after_lb ), priority=0 , match=(1), action=(next;) + table=12(ls_in_acl_after_lb ), priority=0 , match=(1), action=(next;) table=3 (ls_out_acl_hint ), priority=0 , match=(1), action=(next;) table=3 (ls_out_acl_hint ), priority=1 , match=(ct.est && ct_mark.blocked == 0), action=(reg0[[10]] = 1; next;) table=3 (ls_out_acl_hint ), priority=2 , match=(ct.est && ct_mark.blocked == 1), action=(reg0[[9]] = 1; next;) @@ -2279,35 +2279,35 @@ AT_CHECK([ovn-sbctl lflow-list ls | grep -e ls_in_acl_hint -e ls_out_acl_hint -e table=4 (ls_out_acl ), priority=65532, match=(ct.est && !ct.rel && !ct.new && !ct.inv && ct.rpl && ct_mark.blocked == 0), action=(next;) table=4 (ls_out_acl ), priority=65532, match=(ct.inv || (ct.est && ct.rpl && ct_mark.blocked == 1)), action=(drop;) table=4 (ls_out_acl ), priority=65532, match=(nd || nd_ra || nd_rs || mldv1 || mldv2), action=(next;) - table=8 (ls_in_acl_hint ), priority=0 , match=(1), action=(next;) - table=8 (ls_in_acl_hint ), priority=1 , match=(ct.est && ct_mark.blocked == 0), action=(reg0[[10]] = 1; next;) - table=8 (ls_in_acl_hint ), priority=2 , match=(ct.est && ct_mark.blocked == 1), action=(reg0[[9]] = 1; next;) - table=8 (ls_in_acl_hint ), priority=3 , match=(!ct.est), action=(reg0[[9]] = 1; next;) - table=8 (ls_in_acl_hint ), priority=4 , match=(!ct.new && ct.est && !ct.rpl && ct_mark.blocked == 0), action=(reg0[[8]] = 1; reg0[[10]] = 1; next;) - table=8 (ls_in_acl_hint ), priority=5 , match=(!ct.trk), action=(reg0[[8]] = 1; reg0[[9]] = 1; next;) - table=8 (ls_in_acl_hint ), priority=6 , match=(!ct.new && ct.est && !ct.rpl && ct_mark.blocked == 1), action=(reg0[[7]] = 1; reg0[[9]] = 1; next;) - table=8 (ls_in_acl_hint ), priority=7 , match=(ct.new && !ct.est), action=(reg0[[7]] = 1; reg0[[9]] = 1; next;) - table=9 (ls_in_acl ), priority=0 , match=(1), action=(next;) - table=9 (ls_in_acl ), priority=1 , match=(ip && !ct.est), action=(reg0[[1]] = 1; next;) - table=9 (ls_in_acl ), priority=1 , match=(ip && ct.est && ct_mark.blocked == 1), action=(reg0[[1]] = 1; next;) - table=9 (ls_in_acl ), priority=1001 , match=(reg0[[7]] == 1 && (ip)), action=(reg0[[1]] = 1; next;) - table=9 (ls_in_acl ), priority=1001 , match=(reg0[[8]] == 1 && (ip)), action=(next;) - table=9 (ls_in_acl ), priority=34000, match=(eth.dst == $svc_monitor_mac), action=(next;) - table=9 (ls_in_acl ), priority=65532, match=(!ct.est && ct.rel && !ct.new && !ct.inv && ct_mark.blocked == 0), action=(next;) - table=9 (ls_in_acl ), priority=65532, match=(ct.est && !ct.rel && !ct.new && !ct.inv && ct.rpl && ct_mark.blocked == 0), action=(reg0[[9]] = 0; reg0[[10]] = 0; next;) - table=9 (ls_in_acl ), priority=65532, match=(ct.inv || (ct.est && ct.rpl && ct_mark.blocked == 1)), action=(drop;) - table=9 (ls_in_acl ), priority=65532, match=(nd || nd_ra || nd_rs || mldv1 || mldv2), action=(next;) + table=7 (ls_in_acl_hint ), priority=0 , match=(1), action=(next;) + table=7 (ls_in_acl_hint ), priority=1 , match=(ct.est && ct_mark.blocked == 0), action=(reg0[[10]] = 1; next;) + table=7 (ls_in_acl_hint ), priority=2 , match=(ct.est && ct_mark.blocked == 1), action=(reg0[[9]] = 1; next;) + table=7 (ls_in_acl_hint ), priority=3 , match=(!ct.est), action=(reg0[[9]] = 1; next;) + table=7 (ls_in_acl_hint ), priority=4 , match=(!ct.new && ct.est && !ct.rpl && ct_mark.blocked == 0), action=(reg0[[8]] = 1; reg0[[10]] = 1; next;) + table=7 (ls_in_acl_hint ), priority=5 , match=(!ct.trk), action=(reg0[[8]] = 1; reg0[[9]] = 1; next;) + table=7 (ls_in_acl_hint ), priority=6 , match=(!ct.new && ct.est && !ct.rpl && ct_mark.blocked == 1), action=(reg0[[7]] = 1; reg0[[9]] = 1; next;) + table=7 (ls_in_acl_hint ), priority=7 , match=(ct.new && !ct.est), action=(reg0[[7]] = 1; reg0[[9]] = 1; next;) + table=8 (ls_in_acl ), priority=0 , match=(1), action=(next;) + table=8 (ls_in_acl ), priority=1 , match=(ip && !ct.est), action=(reg0[[1]] = 1; next;) + table=8 (ls_in_acl ), priority=1 , match=(ip && ct.est && ct_mark.blocked == 1), action=(reg0[[1]] = 1; next;) + table=8 (ls_in_acl ), priority=1001 , match=(reg0[[7]] == 1 && (ip)), action=(reg0[[1]] = 1; next;) + table=8 (ls_in_acl ), priority=1001 , match=(reg0[[8]] == 1 && (ip)), action=(next;) + table=8 (ls_in_acl ), priority=34000, match=(eth.dst == $svc_monitor_mac), action=(next;) + table=8 (ls_in_acl ), priority=65532, match=(!ct.est && ct.rel && !ct.new && !ct.inv && ct_mark.blocked == 0), action=(next;) + table=8 (ls_in_acl ), priority=65532, match=(ct.est && !ct.rel && !ct.new && !ct.inv && ct.rpl && ct_mark.blocked == 0), action=(reg0[[9]] = 0; reg0[[10]] = 0; next;) + table=8 (ls_in_acl ), priority=65532, match=(ct.inv || (ct.est && ct.rpl && ct_mark.blocked == 1)), action=(drop;) + table=8 (ls_in_acl ), priority=65532, match=(nd || nd_ra || nd_rs || mldv1 || mldv2), action=(next;) ]) ovn-nbctl --wait=sb clear logical_switch ls acls ovn-nbctl --wait=sb clear logical_switch ls load_balancer AT_CHECK([ovn-sbctl lflow-list ls | grep -e ls_in_acl_hint -e ls_out_acl_hint -e ls_in_acl -e ls_out_acl | sort], [0], [dnl - table=13(ls_in_acl_after_lb ), priority=0 , match=(1), action=(next;) + table=12(ls_in_acl_after_lb ), priority=0 , match=(1), action=(next;) table=3 (ls_out_acl_hint ), priority=65535, match=(1), action=(next;) table=4 (ls_out_acl ), priority=65535, match=(1), action=(next;) - table=8 (ls_in_acl_hint ), priority=65535, match=(1), action=(next;) - table=9 (ls_in_acl ), priority=65535, match=(1), action=(next;) + table=7 (ls_in_acl_hint ), priority=65535, match=(1), action=(next;) + table=8 (ls_in_acl ), priority=65535, match=(1), action=(next;) ]) @@ -3942,37 +3942,37 @@ check_stateful_flows() { ovn-sbctl dump-flows sw0 > sw0flows AT_CAPTURE_FILE([sw0flows]) - AT_CHECK([grep "ls_in_pre_lb" sw0flows | sort], [0], [dnl - table=6 (ls_in_pre_lb ), priority=0 , match=(1), action=(next;) - table=6 (ls_in_pre_lb ), priority=100 , match=(ip), action=(reg0[[2]] = 1; next;) - table=6 (ls_in_pre_lb ), priority=110 , match=(eth.dst == $svc_monitor_mac), action=(next;) - table=6 (ls_in_pre_lb ), priority=110 , match=(eth.mcast), action=(next;) - table=6 (ls_in_pre_lb ), priority=110 , match=(ip && inport == "sw0-lr0"), action=(next;) - table=6 (ls_in_pre_lb ), priority=110 , match=(nd || nd_rs || nd_ra || mldv1 || mldv2), action=(next;) + AT_CHECK([grep "ls_in_pre_lb" sw0flows | sort | sed 's/table=./table=?/'], [0], [dnl + table=? (ls_in_pre_lb ), priority=0 , match=(1), action=(next;) + table=? (ls_in_pre_lb ), priority=100 , match=(ip), action=(reg0[[2]] = 1; next;) + table=? (ls_in_pre_lb ), priority=110 , match=(eth.dst == $svc_monitor_mac), action=(next;) + table=? (ls_in_pre_lb ), priority=110 , match=(eth.mcast), action=(next;) + table=? (ls_in_pre_lb ), priority=110 , match=(ip && inport == "sw0-lr0"), action=(next;) + table=? (ls_in_pre_lb ), priority=110 , match=(nd || nd_rs || nd_ra || mldv1 || mldv2), action=(next;) ]) - AT_CHECK([grep "ls_in_pre_stateful" sw0flows | sort], [0], [dnl - table=7 (ls_in_pre_stateful ), priority=0 , match=(1), action=(next;) - table=7 (ls_in_pre_stateful ), priority=100 , match=(reg0[[0]] == 1), action=(ct_next;) - table=7 (ls_in_pre_stateful ), priority=110 , match=(reg0[[2]] == 1), action=(ct_lb_mark;) - table=7 (ls_in_pre_stateful ), priority=120 , match=(reg0[[2]] == 1 && ip4 && sctp), action=(reg1 = ip4.dst; reg2[[0..15]] = sctp.dst; ct_lb_mark;) - table=7 (ls_in_pre_stateful ), priority=120 , match=(reg0[[2]] == 1 && ip4 && tcp), action=(reg1 = ip4.dst; reg2[[0..15]] = tcp.dst; ct_lb_mark;) - table=7 (ls_in_pre_stateful ), priority=120 , match=(reg0[[2]] == 1 && ip4 && udp), action=(reg1 = ip4.dst; reg2[[0..15]] = udp.dst; ct_lb_mark;) - table=7 (ls_in_pre_stateful ), priority=120 , match=(reg0[[2]] == 1 && ip6 && sctp), action=(xxreg1 = ip6.dst; reg2[[0..15]] = sctp.dst; ct_lb_mark;) - table=7 (ls_in_pre_stateful ), priority=120 , match=(reg0[[2]] == 1 && ip6 && tcp), action=(xxreg1 = ip6.dst; reg2[[0..15]] = tcp.dst; ct_lb_mark;) - table=7 (ls_in_pre_stateful ), priority=120 , match=(reg0[[2]] == 1 && ip6 && udp), action=(xxreg1 = ip6.dst; reg2[[0..15]] = udp.dst; ct_lb_mark;) + AT_CHECK([grep "ls_in_pre_stateful" sw0flows | sort | sed 's/table=./table=?/'], [0], [dnl + table=? (ls_in_pre_stateful ), priority=0 , match=(1), action=(next;) + table=? (ls_in_pre_stateful ), priority=100 , match=(reg0[[0]] == 1), action=(ct_next;) + table=? (ls_in_pre_stateful ), priority=110 , match=(reg0[[2]] == 1), action=(ct_lb_mark;) + table=? (ls_in_pre_stateful ), priority=120 , match=(reg0[[2]] == 1 && ip4 && sctp), action=(reg1 = ip4.dst; reg2[[0..15]] = sctp.dst; ct_lb_mark;) + table=? (ls_in_pre_stateful ), priority=120 , match=(reg0[[2]] == 1 && ip4 && tcp), action=(reg1 = ip4.dst; reg2[[0..15]] = tcp.dst; ct_lb_mark;) + table=? (ls_in_pre_stateful ), priority=120 , match=(reg0[[2]] == 1 && ip4 && udp), action=(reg1 = ip4.dst; reg2[[0..15]] = udp.dst; ct_lb_mark;) + table=? (ls_in_pre_stateful ), priority=120 , match=(reg0[[2]] == 1 && ip6 && sctp), action=(xxreg1 = ip6.dst; reg2[[0..15]] = sctp.dst; ct_lb_mark;) + table=? (ls_in_pre_stateful ), priority=120 , match=(reg0[[2]] == 1 && ip6 && tcp), action=(xxreg1 = ip6.dst; reg2[[0..15]] = tcp.dst; ct_lb_mark;) + table=? (ls_in_pre_stateful ), priority=120 , match=(reg0[[2]] == 1 && ip6 && udp), action=(xxreg1 = ip6.dst; reg2[[0..15]] = udp.dst; ct_lb_mark;) ]) - AT_CHECK([grep "ls_in_lb" sw0flows | sort], [0], [dnl - table=12(ls_in_lb ), priority=0 , match=(1), action=(next;) - table=12(ls_in_lb ), priority=120 , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(reg0[[1]] = 0; reg1 = 10.0.0.10; reg2[[0..15]] = 80; ct_lb_mark(backends=10.0.0.4:8080);) - table=12(ls_in_lb ), priority=120 , match=(ct.new && ip4.dst == 10.0.0.20 && tcp.dst == 80), action=(reg0[[1]] = 0; reg1 = 10.0.0.20; reg2[[0..15]] = 80; ct_lb_mark(backends=10.0.0.40:8080);) + AT_CHECK([grep "ls_in_lb" sw0flows | sort | sed 's/table=../table=??/'], [0], [dnl + table=??(ls_in_lb ), priority=0 , match=(1), action=(next;) + table=??(ls_in_lb ), priority=120 , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(reg0[[1]] = 0; reg1 = 10.0.0.10; reg2[[0..15]] = 80; ct_lb_mark(backends=10.0.0.4:8080);) + table=??(ls_in_lb ), priority=120 , match=(ct.new && ip4.dst == 10.0.0.20 && tcp.dst == 80), action=(reg0[[1]] = 0; reg1 = 10.0.0.20; reg2[[0..15]] = 80; ct_lb_mark(backends=10.0.0.40:8080);) ]) - AT_CHECK([grep "ls_in_stateful" sw0flows | sort], [0], [dnl - table=14(ls_in_stateful ), priority=0 , match=(1), action=(next;) - table=14(ls_in_stateful ), priority=100 , match=(reg0[[1]] == 1 && reg0[[13]] == 0), action=(ct_commit { ct_mark.blocked = 0; }; next;) - table=14(ls_in_stateful ), priority=100 , match=(reg0[[1]] == 1 && reg0[[13]] == 1), action=(ct_commit { ct_mark.blocked = 0; ct_label.label = reg3; }; next;) + AT_CHECK([grep "ls_in_stateful" sw0flows | sort | sed 's/table=../table=??/'], [0], [dnl + table=??(ls_in_stateful ), priority=0 , match=(1), action=(next;) + table=??(ls_in_stateful ), priority=100 , match=(reg0[[1]] == 1 && reg0[[13]] == 0), action=(ct_commit { ct_mark.blocked = 0; }; next;) + table=??(ls_in_stateful ), priority=100 , match=(reg0[[1]] == 1 && reg0[[13]] == 1), action=(ct_commit { ct_mark.blocked = 0; ct_label.label = reg3; }; next;) ]) AT_CHECK([grep "ls_out_pre_lb" sw0flows | sort], [0], [dnl @@ -4015,34 +4015,34 @@ check ovn-nbctl --wait=sb sync ovn-sbctl dump-flows sw0 > sw0flows AT_CAPTURE_FILE([sw0flows]) -AT_CHECK([grep "ls_in_pre_lb" sw0flows | sort], [0], [dnl - table=6 (ls_in_pre_lb ), priority=0 , match=(1), action=(next;) - table=6 (ls_in_pre_lb ), priority=110 , match=(eth.dst == $svc_monitor_mac), action=(next;) - table=6 (ls_in_pre_lb ), priority=110 , match=(eth.mcast), action=(next;) - table=6 (ls_in_pre_lb ), priority=110 , match=(ip && inport == "sw0-lr0"), action=(next;) - table=6 (ls_in_pre_lb ), priority=110 , match=(nd || nd_rs || nd_ra || mldv1 || mldv2), action=(next;) +AT_CHECK([grep "ls_in_pre_lb" sw0flows | sort | sed 's/table=./table=?/'], [0], [dnl + table=? (ls_in_pre_lb ), priority=0 , match=(1), action=(next;) + table=? (ls_in_pre_lb ), priority=110 , match=(eth.dst == $svc_monitor_mac), action=(next;) + table=? (ls_in_pre_lb ), priority=110 , match=(eth.mcast), action=(next;) + table=? (ls_in_pre_lb ), priority=110 , match=(ip && inport == "sw0-lr0"), action=(next;) + table=? (ls_in_pre_lb ), priority=110 , match=(nd || nd_rs || nd_ra || mldv1 || mldv2), action=(next;) ]) -AT_CHECK([grep "ls_in_pre_stateful" sw0flows | sort], [0], [dnl - table=7 (ls_in_pre_stateful ), priority=0 , match=(1), action=(next;) - table=7 (ls_in_pre_stateful ), priority=100 , match=(reg0[[0]] == 1), action=(ct_next;) - table=7 (ls_in_pre_stateful ), priority=110 , match=(reg0[[2]] == 1), action=(ct_lb_mark;) - table=7 (ls_in_pre_stateful ), priority=120 , match=(reg0[[2]] == 1 && ip4 && sctp), action=(reg1 = ip4.dst; reg2[[0..15]] = sctp.dst; ct_lb_mark;) - table=7 (ls_in_pre_stateful ), priority=120 , match=(reg0[[2]] == 1 && ip4 && tcp), action=(reg1 = ip4.dst; reg2[[0..15]] = tcp.dst; ct_lb_mark;) - table=7 (ls_in_pre_stateful ), priority=120 , match=(reg0[[2]] == 1 && ip4 && udp), action=(reg1 = ip4.dst; reg2[[0..15]] = udp.dst; ct_lb_mark;) - table=7 (ls_in_pre_stateful ), priority=120 , match=(reg0[[2]] == 1 && ip6 && sctp), action=(xxreg1 = ip6.dst; reg2[[0..15]] = sctp.dst; ct_lb_mark;) - table=7 (ls_in_pre_stateful ), priority=120 , match=(reg0[[2]] == 1 && ip6 && tcp), action=(xxreg1 = ip6.dst; reg2[[0..15]] = tcp.dst; ct_lb_mark;) - table=7 (ls_in_pre_stateful ), priority=120 , match=(reg0[[2]] == 1 && ip6 && udp), action=(xxreg1 = ip6.dst; reg2[[0..15]] = udp.dst; ct_lb_mark;) +AT_CHECK([grep "ls_in_pre_stateful" sw0flows | sort | sed 's/table=./table=?/'], [0], [dnl + table=? (ls_in_pre_stateful ), priority=0 , match=(1), action=(next;) + table=? (ls_in_pre_stateful ), priority=100 , match=(reg0[[0]] == 1), action=(ct_next;) + table=? (ls_in_pre_stateful ), priority=110 , match=(reg0[[2]] == 1), action=(ct_lb_mark;) + table=? (ls_in_pre_stateful ), priority=120 , match=(reg0[[2]] == 1 && ip4 && sctp), action=(reg1 = ip4.dst; reg2[[0..15]] = sctp.dst; ct_lb_mark;) + table=? (ls_in_pre_stateful ), priority=120 , match=(reg0[[2]] == 1 && ip4 && tcp), action=(reg1 = ip4.dst; reg2[[0..15]] = tcp.dst; ct_lb_mark;) + table=? (ls_in_pre_stateful ), priority=120 , match=(reg0[[2]] == 1 && ip4 && udp), action=(reg1 = ip4.dst; reg2[[0..15]] = udp.dst; ct_lb_mark;) + table=? (ls_in_pre_stateful ), priority=120 , match=(reg0[[2]] == 1 && ip6 && sctp), action=(xxreg1 = ip6.dst; reg2[[0..15]] = sctp.dst; ct_lb_mark;) + table=? (ls_in_pre_stateful ), priority=120 , match=(reg0[[2]] == 1 && ip6 && tcp), action=(xxreg1 = ip6.dst; reg2[[0..15]] = tcp.dst; ct_lb_mark;) + table=? (ls_in_pre_stateful ), priority=120 , match=(reg0[[2]] == 1 && ip6 && udp), action=(xxreg1 = ip6.dst; reg2[[0..15]] = udp.dst; ct_lb_mark;) ]) -AT_CHECK([grep "ls_in_lb" sw0flows | sort], [0], [dnl - table=12(ls_in_lb ), priority=0 , match=(1), action=(next;) +AT_CHECK([grep "ls_in_lb" sw0flows | sort | sed 's/table=../table=??/'], [0], [dnl + table=??(ls_in_lb ), priority=0 , match=(1), action=(next;) ]) -AT_CHECK([grep "ls_in_stateful" sw0flows | sort], [0], [dnl - table=14(ls_in_stateful ), priority=0 , match=(1), action=(next;) - table=14(ls_in_stateful ), priority=100 , match=(reg0[[1]] == 1 && reg0[[13]] == 0), action=(ct_commit { ct_mark.blocked = 0; }; next;) - table=14(ls_in_stateful ), priority=100 , match=(reg0[[1]] == 1 && reg0[[13]] == 1), action=(ct_commit { ct_mark.blocked = 0; ct_label.label = reg3; }; next;) +AT_CHECK([grep "ls_in_stateful" sw0flows | sort | sed 's/table=../table=??/'], [0], [dnl + table=??(ls_in_stateful ), priority=0 , match=(1), action=(next;) + table=??(ls_in_stateful ), priority=100 , match=(reg0[[1]] == 1 && reg0[[13]] == 0), action=(ct_commit { ct_mark.blocked = 0; }; next;) + table=??(ls_in_stateful ), priority=100 , match=(reg0[[1]] == 1 && reg0[[13]] == 1), action=(ct_commit { ct_mark.blocked = 0; ct_label.label = reg3; }; next;) ]) AT_CHECK([grep "ls_out_pre_lb" sw0flows | sort], [0], [dnl @@ -4081,14 +4081,14 @@ check ovn-nbctl --wait=sb --label=1234 acl-add sw0 from-lport 1002 tcp allow-rel 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 -w "ls_in_acl" sw0flows | grep 2002 | sort | sed 's/table=./table=?/'], [0], [dnl + table=? (ls_in_acl ), priority=2002 , match=(reg0[[7]] == 1 && (tcp)), action=(reg0[[1]] = 1; reg0[[13]] = 1; reg3 = 1234; next;) + table=? (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=14(ls_in_stateful ), priority=0 , match=(1), action=(next;) - table=14(ls_in_stateful ), priority=100 , match=(reg0[[1]] == 1 && reg0[[13]] == 0), action=(ct_commit { ct_mark.blocked = 0; }; next;) - table=14(ls_in_stateful ), priority=100 , match=(reg0[[1]] == 1 && reg0[[13]] == 1), action=(ct_commit { ct_mark.blocked = 0; ct_label.label = reg3; }; next;) +AT_CHECK([grep "ls_in_stateful" sw0flows | sort | sed 's/table=../table=??/'], [0], [dnl + table=??(ls_in_stateful ), priority=0 , match=(1), action=(next;) + table=??(ls_in_stateful ), priority=100 , match=(reg0[[1]] == 1 && reg0[[13]] == 0), action=(ct_commit { ct_mark.blocked = 0; }; next;) + table=??(ls_in_stateful ), priority=100 , match=(reg0[[1]] == 1 && reg0[[13]] == 1), action=(ct_commit { ct_mark.blocked = 0; ct_label.label = reg3; }; next;) ]) AT_CHECK([grep -w "ls_out_acl" sw0flows | grep 2002 | sort], [0], [dnl @@ -4108,16 +4108,16 @@ 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 -w "ls_in_acl" sw0flows | grep 2002 | sort | sed 's/table=./table=?/'], [0], [dnl + table=? (ls_in_acl ), priority=2002 , match=(reg0[[7]] == 1 && (tcp)), action=(reg0[[1]] = 1; reg0[[13]] = 1; reg3 = 1234; next;) + table=? (ls_in_acl ), priority=2002 , match=(reg0[[7]] == 1 && (udp)), action=(reg0[[1]] = 1; next;) + table=? (ls_in_acl ), priority=2002 , match=(reg0[[8]] == 1 && (tcp)), action=(reg0[[1]] = 1; reg0[[13]] = 1; reg3 = 1234; next;) + table=? (ls_in_acl ), priority=2002 , match=(reg0[[8]] == 1 && (udp)), action=(next;) ]) -AT_CHECK([grep "ls_in_stateful" sw0flows | sort], [0], [dnl - table=14(ls_in_stateful ), priority=0 , match=(1), action=(next;) - table=14(ls_in_stateful ), priority=100 , match=(reg0[[1]] == 1 && reg0[[13]] == 0), action=(ct_commit { ct_mark.blocked = 0; }; next;) - table=14(ls_in_stateful ), priority=100 , match=(reg0[[1]] == 1 && reg0[[13]] == 1), action=(ct_commit { ct_mark.blocked = 0; ct_label.label = reg3; }; next;) +AT_CHECK([grep "ls_in_stateful" sw0flows | sort | sed 's/table=../table=??/'], [0], [dnl + table=??(ls_in_stateful ), priority=0 , match=(1), action=(next;) + table=??(ls_in_stateful ), priority=100 , match=(reg0[[1]] == 1 && reg0[[13]] == 0), action=(ct_commit { ct_mark.blocked = 0; }; next;) + table=??(ls_in_stateful ), priority=100 , match=(reg0[[1]] == 1 && reg0[[13]] == 1), action=(ct_commit { ct_mark.blocked = 0; ct_label.label = reg3; }; next;) ]) AT_CHECK([grep -w "ls_out_acl" sw0flows | grep 2002 | sort], [0], [dnl @@ -4139,14 +4139,14 @@ 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 -w "ls_in_acl" sw0flows | grep 2002 | sort | sed 's/table=./table=?/'], [0], [dnl + table=? (ls_in_acl ), priority=2002 , match=(reg0[[7]] == 1 && (udp)), action=(reg0[[1]] = 1; next;) + table=? (ls_in_acl ), priority=2002 , match=(reg0[[8]] == 1 && (udp)), action=(next;) ]) -AT_CHECK([grep "ls_in_stateful" sw0flows | sort], [0], [dnl - table=14(ls_in_stateful ), priority=0 , match=(1), action=(next;) - table=14(ls_in_stateful ), priority=100 , match=(reg0[[1]] == 1 && reg0[[13]] == 0), action=(ct_commit { ct_mark.blocked = 0; }; next;) - table=14(ls_in_stateful ), priority=100 , match=(reg0[[1]] == 1 && reg0[[13]] == 1), action=(ct_commit { ct_mark.blocked = 0; ct_label.label = reg3; }; next;) +AT_CHECK([grep "ls_in_stateful" sw0flows | sort | sed 's/table=../table=??/'], [0], [dnl + table=??(ls_in_stateful ), priority=0 , match=(1), action=(next;) + table=??(ls_in_stateful ), priority=100 , match=(reg0[[1]] == 1 && reg0[[13]] == 0), action=(ct_commit { ct_mark.blocked = 0; }; next;) + table=??(ls_in_stateful ), priority=100 , match=(reg0[[1]] == 1 && reg0[[13]] == 1), action=(ct_commit { ct_mark.blocked = 0; ct_label.label = reg3; }; next;) ]) AT_CHECK([grep -w "ls_out_acl" sw0flows | grep 2002 | sort], [0], [dnl @@ -4173,18 +4173,18 @@ check ovn-nbctl --wait=sb acl-add sw0 to-lport 1002 ip allow-related ovn-sbctl dump-flows sw0 > sw0flows AT_CAPTURE_FILE([sw0flows]) -AT_CHECK([grep -w "ls_in_acl" sw0flows | grep 6553 | sort], [0], [dnl - table=9 (ls_in_acl ), priority=65532, match=(!ct.est && ct.rel && !ct.new && !ct.inv && ct_mark.blocked == 0), action=(next;) - table=9 (ls_in_acl ), priority=65532, match=(ct.est && !ct.rel && !ct.new && !ct.inv && ct.rpl && ct_mark.blocked == 0), action=(reg0[[9]] = 0; reg0[[10]] = 0; next;) - table=9 (ls_in_acl ), priority=65532, match=(ct.inv || (ct.est && ct.rpl && ct_mark.blocked == 1)), action=(drop;) - table=9 (ls_in_acl ), priority=65532, match=(nd || nd_ra || nd_rs || mldv1 || mldv2), action=(next;) +AT_CHECK([grep -w "ls_in_acl" sw0flows | grep 6553 | sort | sed 's/table=./table=?/'], [0], [dnl + table=? (ls_in_acl ), priority=65532, match=(!ct.est && ct.rel && !ct.new && !ct.inv && ct_mark.blocked == 0), action=(next;) + table=? (ls_in_acl ), priority=65532, match=(ct.est && !ct.rel && !ct.new && !ct.inv && ct.rpl && ct_mark.blocked == 0), action=(reg0[[9]] = 0; reg0[[10]] = 0; next;) + table=? (ls_in_acl ), priority=65532, match=(ct.inv || (ct.est && ct.rpl && ct_mark.blocked == 1)), action=(drop;) + table=? (ls_in_acl ), priority=65532, match=(nd || nd_ra || nd_rs || mldv1 || mldv2), action=(next;) ]) -AT_CHECK([grep -w "ls_out_acl" sw0flows | grep 6553 | sort], [0], [dnl - table=4 (ls_out_acl ), priority=65532, match=(!ct.est && ct.rel && !ct.new && !ct.inv && ct_mark.blocked == 0), action=(next;) - table=4 (ls_out_acl ), priority=65532, match=(ct.est && !ct.rel && !ct.new && !ct.inv && ct.rpl && ct_mark.blocked == 0), action=(next;) - table=4 (ls_out_acl ), priority=65532, match=(ct.inv || (ct.est && ct.rpl && ct_mark.blocked == 1)), action=(drop;) - table=4 (ls_out_acl ), priority=65532, match=(nd || nd_ra || nd_rs || mldv1 || mldv2), action=(next;) +AT_CHECK([grep -w "ls_out_acl" sw0flows | grep 6553 | sort | sed 's/table=./table=?/'], [0], [dnl + table=? (ls_out_acl ), priority=65532, match=(!ct.est && ct.rel && !ct.new && !ct.inv && ct_mark.blocked == 0), action=(next;) + table=? (ls_out_acl ), priority=65532, match=(ct.est && !ct.rel && !ct.new && !ct.inv && ct.rpl && ct_mark.blocked == 0), action=(next;) + table=? (ls_out_acl ), priority=65532, match=(ct.inv || (ct.est && ct.rpl && ct_mark.blocked == 1)), action=(drop;) + table=? (ls_out_acl ), priority=65532, match=(nd || nd_ra || nd_rs || mldv1 || mldv2), action=(next;) ]) # Disable ct.inv usage. @@ -4193,18 +4193,18 @@ check ovn-nbctl --wait=sb set NB_Global . options:use_ct_inv_match=false ovn-sbctl dump-flows sw0 > sw0flows AT_CAPTURE_FILE([sw0flows]) -AT_CHECK([grep -w "ls_in_acl" sw0flows | grep 6553 | sort], [0], [dnl - table=9 (ls_in_acl ), priority=65532, match=(!ct.est && ct.rel && !ct.new && ct_mark.blocked == 0), action=(next;) - table=9 (ls_in_acl ), priority=65532, match=((ct.est && ct.rpl && ct_mark.blocked == 1)), action=(drop;) - table=9 (ls_in_acl ), priority=65532, match=(ct.est && !ct.rel && !ct.new && ct.rpl && ct_mark.blocked == 0), action=(reg0[[9]] = 0; reg0[[10]] = 0; next;) - table=9 (ls_in_acl ), priority=65532, match=(nd || nd_ra || nd_rs || mldv1 || mldv2), action=(next;) +AT_CHECK([grep -w "ls_in_acl" sw0flows | grep 6553 | sort | sed 's/table=./table=?/'], [0], [dnl + table=? (ls_in_acl ), priority=65532, match=(!ct.est && ct.rel && !ct.new && ct_mark.blocked == 0), action=(next;) + table=? (ls_in_acl ), priority=65532, match=((ct.est && ct.rpl && ct_mark.blocked == 1)), action=(drop;) + table=? (ls_in_acl ), priority=65532, match=(ct.est && !ct.rel && !ct.new && ct.rpl && ct_mark.blocked == 0), action=(reg0[[9]] = 0; reg0[[10]] = 0; next;) + table=? (ls_in_acl ), priority=65532, match=(nd || nd_ra || nd_rs || mldv1 || mldv2), action=(next;) ]) -AT_CHECK([grep -w "ls_out_acl" sw0flows | grep 6553 | sort], [0], [dnl - table=4 (ls_out_acl ), priority=65532, match=(!ct.est && ct.rel && !ct.new && ct_mark.blocked == 0), action=(next;) - table=4 (ls_out_acl ), priority=65532, match=((ct.est && ct.rpl && ct_mark.blocked == 1)), action=(drop;) - table=4 (ls_out_acl ), priority=65532, match=(ct.est && !ct.rel && !ct.new && ct.rpl && ct_mark.blocked == 0), action=(next;) - table=4 (ls_out_acl ), priority=65532, match=(nd || nd_ra || nd_rs || mldv1 || mldv2), action=(next;) +AT_CHECK([grep -w "ls_out_acl" sw0flows | grep 6553 | sort | sed 's/table=./table=?/'], [0], [dnl + table=? (ls_out_acl ), priority=65532, match=(!ct.est && ct.rel && !ct.new && ct_mark.blocked == 0), action=(next;) + table=? (ls_out_acl ), priority=65532, match=((ct.est && ct.rpl && ct_mark.blocked == 1)), action=(drop;) + table=? (ls_out_acl ), priority=65532, match=(ct.est && !ct.rel && !ct.new && ct.rpl && ct_mark.blocked == 0), action=(next;) + table=? (ls_out_acl ), priority=65532, match=(nd || nd_ra || nd_rs || mldv1 || mldv2), action=(next;) ]) AT_CHECK([grep -c "ct.inv" sw0flows], [1], [dnl @@ -4217,18 +4217,18 @@ check ovn-nbctl --wait=sb set NB_Global . options:use_ct_inv_match=true ovn-sbctl dump-flows sw0 > sw0flows AT_CAPTURE_FILE([sw0flows]) -AT_CHECK([grep -w "ls_in_acl" sw0flows | grep 6553 | sort], [0], [dnl - table=9 (ls_in_acl ), priority=65532, match=(!ct.est && ct.rel && !ct.new && !ct.inv && ct_mark.blocked == 0), action=(next;) - table=9 (ls_in_acl ), priority=65532, match=(ct.est && !ct.rel && !ct.new && !ct.inv && ct.rpl && ct_mark.blocked == 0), action=(reg0[[9]] = 0; reg0[[10]] = 0; next;) - table=9 (ls_in_acl ), priority=65532, match=(ct.inv || (ct.est && ct.rpl && ct_mark.blocked == 1)), action=(drop;) - table=9 (ls_in_acl ), priority=65532, match=(nd || nd_ra || nd_rs || mldv1 || mldv2), action=(next;) +AT_CHECK([grep -w "ls_in_acl" sw0flows | grep 6553 | sort | sed 's/table=./table=?/'], [0], [dnl + table=? (ls_in_acl ), priority=65532, match=(!ct.est && ct.rel && !ct.new && !ct.inv && ct_mark.blocked == 0), action=(next;) + table=? (ls_in_acl ), priority=65532, match=(ct.est && !ct.rel && !ct.new && !ct.inv && ct.rpl && ct_mark.blocked == 0), action=(reg0[[9]] = 0; reg0[[10]] = 0; next;) + table=? (ls_in_acl ), priority=65532, match=(ct.inv || (ct.est && ct.rpl && ct_mark.blocked == 1)), action=(drop;) + table=? (ls_in_acl ), priority=65532, match=(nd || nd_ra || nd_rs || mldv1 || mldv2), action=(next;) ]) -AT_CHECK([grep -w "ls_out_acl" sw0flows | grep 6553 | sort], [0], [dnl - table=4 (ls_out_acl ), priority=65532, match=(!ct.est && ct.rel && !ct.new && !ct.inv && ct_mark.blocked == 0), action=(next;) - table=4 (ls_out_acl ), priority=65532, match=(ct.est && !ct.rel && !ct.new && !ct.inv && ct.rpl && ct_mark.blocked == 0), action=(next;) - table=4 (ls_out_acl ), priority=65532, match=(ct.inv || (ct.est && ct.rpl && ct_mark.blocked == 1)), action=(drop;) - table=4 (ls_out_acl ), priority=65532, match=(nd || nd_ra || nd_rs || mldv1 || mldv2), action=(next;) +AT_CHECK([grep -w "ls_out_acl" sw0flows | grep 6553 | sort | sed 's/table=./table=?/'], [0], [dnl + table=? (ls_out_acl ), priority=65532, match=(!ct.est && ct.rel && !ct.new && !ct.inv && ct_mark.blocked == 0), action=(next;) + table=? (ls_out_acl ), priority=65532, match=(ct.est && !ct.rel && !ct.new && !ct.inv && ct.rpl && ct_mark.blocked == 0), action=(next;) + table=? (ls_out_acl ), priority=65532, match=(ct.inv || (ct.est && ct.rpl && ct_mark.blocked == 1)), action=(drop;) + table=? (ls_out_acl ), priority=65532, match=(nd || nd_ra || nd_rs || mldv1 || mldv2), action=(next;) ]) AT_CHECK([grep -c "ct.inv" sw0flows], [0], [dnl @@ -5449,7 +5449,13 @@ check ovn-nbctl lsp-add sw0 lsp0 \ check ovn-nbctl --wait=sb sync AT_CHECK([ovn-sbctl --columns=tags list logical_flow | grep lsp0 -c], [0], [dnl -10 +1 +]) + +check ovn-nbctl set logical_switch_port lsp0 enabled=false +check ovn-nbctl --wait=sb sync +AT_CHECK([ovn-sbctl --columns=tags list logical_flow | grep lsp0 -c], [0], [dnl +3 ]) AT_CLEANUP @@ -7180,3 +7186,126 @@ ct_next(ct_state=new|trk); AT_CLEANUP ]) + +OVN_FOR_EACH_NORTHD([ +AT_SETUP([Port security lflows]) +ovn_start + +# Create logical routers +check ovn-nbctl --wait=sb ls-add sw0 + +ovn-sbctl dump-flows sw0 > sw0flows +AT_CAPTURE_FILE([sw0flows]) + +AT_CHECK([cat sw0flows | grep -e port_sec | sort | sed 's/table=./table=?/' ], [0], [dnl + table=? (ls_in_check_port_sec), priority=100 , match=(eth.src[[40]]), action=(drop;) + table=? (ls_in_check_port_sec), priority=100 , match=(vlan.present), action=(drop;) + table=? (ls_in_check_port_sec), priority=50 , match=(1), action=(reg0[[15]] = check_in_port_sec(); next;) + table=? (ls_in_apply_port_sec), priority=0 , match=(1), action=(next;) + table=? (ls_in_apply_port_sec), priority=50 , match=(reg0[[15]] == 1), action=(drop;) + table=? (ls_out_check_port_sec), priority=0 , match=(1), action=(reg0[[15]] = check_out_port_sec(); next;) + table=? (ls_out_check_port_sec), priority=100 , match=(eth.mcast), action=(reg0[[15]] = 0; next;) + table=? (ls_out_apply_port_sec), priority=0 , match=(1), action=(output;) + table=? (ls_out_apply_port_sec), priority=50 , match=(reg0[[15]] == 1), action=(drop;) +]) + +check ovn-nbctl lsp-add sw0 sw0p1 -- lsp-set-addresses sw0p1 "00:00:00:00:00:01" +check ovn-nbctl lsp-add sw0 sw0p2 -- lsp-set-addresses sw0p2 "00:00:00:00:00:02" +check ovn-nbctl --wait=sb lsp-add sw0 localnetport -- lsp-set-type localnetport localnet + +ovn-sbctl dump-flows sw0 > sw0flows +AT_CAPTURE_FILE([sw0flows]) + +AT_CHECK([cat sw0flows | grep -e port_sec | sort | sed 's/table=./table=?/' ], [0], [dnl + table=? (ls_in_check_port_sec), priority=100 , match=(eth.src[[40]]), action=(drop;) + table=? (ls_in_check_port_sec), priority=100 , match=(vlan.present), action=(drop;) + table=? (ls_in_check_port_sec), priority=50 , match=(1), action=(reg0[[15]] = check_in_port_sec(); next;) + table=? (ls_in_apply_port_sec), priority=0 , match=(1), action=(next;) + table=? (ls_in_apply_port_sec), priority=50 , match=(reg0[[15]] == 1), action=(drop;) + table=? (ls_out_check_port_sec), priority=0 , match=(1), action=(reg0[[15]] = check_out_port_sec(); next;) + table=? (ls_out_check_port_sec), priority=100 , match=(eth.mcast), action=(reg0[[15]] = 0; next;) + table=? (ls_out_apply_port_sec), priority=0 , match=(1), action=(output;) + table=? (ls_out_apply_port_sec), priority=50 , match=(reg0[[15]] == 1), action=(drop;) +]) + +check ovn-nbctl lsp-set-port-security sw0p1 "00:00:00:00:00:01 10.0.0.3 1000::3" +check ovn-nbctl --wait=sb lsp-set-port-security sw0p2 "00:00:00:00:00:02 10.0.0.4 1000::4" + +ovn-sbctl dump-flows sw0 > sw0flows +AT_CAPTURE_FILE([sw0flows]) + +AT_CHECK([cat sw0flows | grep -e port_sec | sort | sed 's/table=./table=?/' ], [0], [dnl + table=? (ls_in_check_port_sec), priority=100 , match=(eth.src[[40]]), action=(drop;) + table=? (ls_in_check_port_sec), priority=100 , match=(vlan.present), action=(drop;) + table=? (ls_in_check_port_sec), priority=50 , match=(1), action=(reg0[[15]] = check_in_port_sec(); next;) + table=? (ls_in_apply_port_sec), priority=0 , match=(1), action=(next;) + table=? (ls_in_apply_port_sec), priority=50 , match=(reg0[[15]] == 1), action=(drop;) + table=? (ls_out_check_port_sec), priority=0 , match=(1), action=(reg0[[15]] = check_out_port_sec(); next;) + table=? (ls_out_check_port_sec), priority=100 , match=(eth.mcast), action=(reg0[[15]] = 0; next;) + table=? (ls_out_apply_port_sec), priority=0 , match=(1), action=(output;) + table=? (ls_out_apply_port_sec), priority=50 , match=(reg0[[15]] == 1), action=(drop;) +]) + +# Disable sw0p1 +check ovn-nbctl --wait=sb set logical_switch_port sw0p1 enabled=false + +ovn-sbctl dump-flows sw0 > sw0flows +AT_CAPTURE_FILE([sw0flows]) + +AT_CHECK([cat sw0flows | grep -e port_sec | sort | sed 's/table=./table=?/' ], [0], [dnl + table=? (ls_in_check_port_sec), priority=100 , match=(eth.src[[40]]), action=(drop;) + table=? (ls_in_check_port_sec), priority=100 , match=(inport == "sw0p1"), action=(reg0[[15]] = 1; next;) + table=? (ls_in_check_port_sec), priority=100 , match=(vlan.present), action=(drop;) + table=? (ls_in_check_port_sec), priority=50 , match=(1), action=(reg0[[15]] = check_in_port_sec(); next;) + table=? (ls_in_apply_port_sec), priority=0 , match=(1), action=(next;) + table=? (ls_in_apply_port_sec), priority=50 , match=(reg0[[15]] == 1), action=(drop;) + table=? (ls_out_check_port_sec), priority=0 , match=(1), action=(reg0[[15]] = check_out_port_sec(); next;) + table=? (ls_out_check_port_sec), priority=100 , match=(eth.mcast), action=(reg0[[15]] = 0; next;) + table=? (ls_out_check_port_sec), priority=150 , match=(outport == "sw0p1"), action=(reg0[[15]] = 1; next;) + table=? (ls_out_apply_port_sec), priority=0 , match=(1), action=(output;) + table=? (ls_out_apply_port_sec), priority=50 , match=(reg0[[15]] == 1), action=(drop;) +]) + +check ovn-nbctl --wait=sb lsp-set-options sw0p2 qdisc_queue_id=10 +ovn-sbctl dump-flows sw0 > sw0flows +AT_CAPTURE_FILE([sw0flows]) + +AT_CHECK([cat sw0flows | grep -e port_sec | sort | sed 's/table=./table=?/' ], [0], [dnl + table=? (ls_in_check_port_sec), priority=100 , match=(eth.src[[40]]), action=(drop;) + table=? (ls_in_check_port_sec), priority=100 , match=(inport == "sw0p1"), action=(reg0[[15]] = 1; next;) + table=? (ls_in_check_port_sec), priority=100 , match=(vlan.present), action=(drop;) + table=? (ls_in_check_port_sec), priority=50 , match=(1), action=(reg0[[15]] = check_in_port_sec(); next;) + table=? (ls_in_check_port_sec), priority=70 , match=(inport == "sw0p2"), action=(set_queue(10); reg0[[15]] = check_in_port_sec(); next;) + table=? (ls_in_apply_port_sec), priority=0 , match=(1), action=(next;) + table=? (ls_in_apply_port_sec), priority=50 , match=(reg0[[15]] == 1), action=(drop;) + table=? (ls_out_check_port_sec), priority=0 , match=(1), action=(reg0[[15]] = check_out_port_sec(); next;) + table=? (ls_out_check_port_sec), priority=100 , match=(eth.mcast), action=(reg0[[15]] = 0; next;) + table=? (ls_out_check_port_sec), priority=150 , match=(outport == "sw0p1"), action=(reg0[[15]] = 1; next;) + table=? (ls_out_apply_port_sec), priority=0 , match=(1), action=(output;) + table=? (ls_out_apply_port_sec), priority=50 , match=(reg0[[15]] == 1), action=(drop;) +]) + +check ovn-nbctl set logical_switch_port sw0p1 enabled=true +check ovn-nbctl lsp-set-type sw0p1 vtep +check ovn-nbctl --wait=sb lsp-set-options localnetport qdisc_queue_id=10 +ovn-sbctl dump-flows sw0 > sw0flows +AT_CAPTURE_FILE([sw0flows]) + +AT_CHECK([cat sw0flows | grep -e port_sec | sort | sed 's/table=./table=?/' ], [0], [dnl + table=? (ls_in_check_port_sec), priority=100 , match=(eth.src[[40]]), action=(drop;) + table=? (ls_in_check_port_sec), priority=100 , match=(vlan.present), action=(drop;) + table=? (ls_in_check_port_sec), priority=50 , match=(1), action=(reg0[[15]] = check_in_port_sec(); next;) + table=? (ls_in_check_port_sec), priority=50 , match=(inport == "sw0p1"), action=(reg0[[14]] = 1; next(pipeline=ingress, table=16);) + table=? (ls_in_check_port_sec), priority=70 , match=(inport == "localnetport"), action=(set_queue(10); reg0[[15]] = check_in_port_sec(); next;) + table=? (ls_in_check_port_sec), priority=70 , match=(inport == "sw0p2"), action=(set_queue(10); reg0[[15]] = check_in_port_sec(); next;) + table=? (ls_in_apply_port_sec), priority=0 , match=(1), action=(next;) + table=? (ls_in_apply_port_sec), priority=50 , match=(reg0[[15]] == 1), action=(drop;) + table=? (ls_out_check_port_sec), priority=0 , match=(1), action=(reg0[[15]] = check_out_port_sec(); next;) + table=? (ls_out_check_port_sec), priority=100 , match=(eth.mcast), action=(reg0[[15]] = 0; next;) + table=? (ls_out_apply_port_sec), priority=0 , match=(1), action=(output;) + table=? (ls_out_apply_port_sec), priority=100 , match=(outport == "localnetport"), action=(set_queue(10); output;) + table=? (ls_out_apply_port_sec), priority=50 , match=(reg0[[15]] == 1), action=(drop;) +]) + +AT_CLEANUP +]) diff --git a/tests/ovn.at b/tests/ovn.at index 45cc6d5483..8d4f914462 100644 --- a/tests/ovn.at +++ b/tests/ovn.at @@ -4270,7 +4270,7 @@ response=${sha}${lrpmac}08060001080006040002${lrpmac}${tpa}${sha}${spa} echo $response >> 3.expected AT_CHECK([ovn-sbctl lflow-list lsw0 | grep 'reg0[\[14\]]' | sort | sed 's/table=../table=??/g'], [0], [dnl - table=??(ls_in_port_sec_l2 ), priority=50 , match=(inport == "lp-vtep"), action=(reg0[[14]] = 1; next(pipeline=ingress, table=??);) + table=??(ls_in_check_port_sec), priority=70 , match=(inport == "lp-vtep"), action=(reg0[[14]] = 1; next(pipeline=ingress, table=??);) table=??(ls_in_hairpin ), priority=1000 , match=(reg0[[14]] == 1), action=(next(pipeline=ingress, table=??);) table=??(ls_in_hairpin ), priority=2000 , match=(reg0[[14]] == 1 && (is_chassis_resident("cr-lrp1") || is_chassis_resident("cr-lrp2"))), action=(next;) ]) @@ -14816,7 +14816,7 @@ ovn-sbctl dump-flows sw0 > sw0-flows AT_CAPTURE_FILE([sw0-flows]) AT_CHECK([grep -E 'ls_(in|out)_acl' sw0-flows |grep reject| sed 's/table=../table=??/' | sort], [0], [dnl - table=??(ls_out_acl ), priority=2002 , match=(ip), action=(reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=24); };) + table=??(ls_out_acl ), priority=2002 , match=(ip), action=(reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=23); };) ]) @@ -16793,17 +16793,17 @@ ovs-vsctl set open . external-ids:ovn-bridge-mappings=phys:br-phys AT_CHECK([ovn-sbctl dump-flows ls1 | grep "offerip = 10.0.0.6" | \ wc -l], [0], [0 ]) -AT_CHECK([as hv1 ovs-ofctl dump-flows br-int | grep table=25 | \ +AT_CHECK([as hv1 ovs-ofctl dump-flows br-int | grep table=26 | \ grep controller | grep "0a.00.00.06" | wc -l], [0], [0 ]) -AT_CHECK([as hv2 ovs-ofctl dump-flows br-int | grep table=25 | \ +AT_CHECK([as hv2 ovs-ofctl dump-flows br-int | grep table=26 | \ grep controller | grep "0a.00.00.06" | wc -l], [0], [0 ]) -AT_CHECK([as hv1 ovs-ofctl dump-flows br-int | grep table=25 | \ +AT_CHECK([as hv1 ovs-ofctl dump-flows br-int | grep table=26 | \ grep controller | grep tp_src=546 | grep \ "ae.70.00.00.00.00.00.00.00.00.00.00.00.00.00.06" | wc -l], [0], [0 ]) -AT_CHECK([as hv2 ovs-ofctl dump-flows br-int | grep table=25 | \ +AT_CHECK([as hv2 ovs-ofctl dump-flows br-int | grep table=26 | \ grep controller | grep tp_src=546 | grep \ "ae.70.00.00.00.00.00.00.00.00.00.00.00.00.00.06" | wc -l], [0], [0 ]) @@ -17415,7 +17415,7 @@ wait_for_ports_up ls1-lp_ext1 # There should be a flow in hv2 to drop traffic from ls1-lp_ext1 destined # to router mac. AT_CHECK([as hv2 ovs-ofctl dump-flows br-int \ -table=31,dl_src=f0:00:00:00:00:03,dl_dst=a0:10:00:00:00:01 | \ +table=30,dl_src=f0:00:00:00:00:03,dl_dst=a0:10:00:00:00:01 | \ grep -c "actions=drop"], [0], [1 ]) # Stop ovn-controllers on hv1 and hv3. @@ -19091,7 +19091,7 @@ check_row_count Port_Binding 1 logical_port=sw0-vir virtual_parent=sw0-p1 wait_for_ports_up sw0-vir check ovn-nbctl --wait=hv sync AT_CHECK([test 2 = `cat hv1/ovn-controller.log | grep "pinctrl received packet-in" | \ -grep opcode=BIND_VPORT | grep OF_Table_ID=26 | wc -l`]) +grep opcode=BIND_VPORT | grep OF_Table_ID=25 | wc -l`]) wait_row_count Port_Binding 1 logical_port=sw0-vir6 chassis=$hv1_ch_uuid check_row_count Port_Binding 1 logical_port=sw0-vir6 virtual_parent=sw0-p1 @@ -27010,12 +27010,8 @@ ovs-vsctl -- add-port br-int hv1-vif1 -- \ wait_for_ports_up ovn-nbctl --wait=hv sync -# Expected conjunction flows: -# ... nd_tll=00:00:00:00:00:00 actions=conjunction(2,2/2) -# ... nd_tll=f0:00:00:00:00:01 actions=conjunction(2,2/2) -# ... nd_target=fe80::f200:ff:fe00:1 actions=conjunction(2,1/2) -# ... nd_target=2001::1 actions=conjunction(2,1/2) -OVS_WAIT_UNTIL([test 4 = `as hv1 ovs-ofctl dump-flows br-int | \ +# Expected conjunction flows: None +OVS_WAIT_UNTIL([test 0 = `as hv1 ovs-ofctl dump-flows br-int | \ grep conjunction | wc -l`]) # unbind the port @@ -27025,7 +27021,7 @@ grep conjunction | wc -l`]) # bind the port again ovs-vsctl set interface hv1-vif1 external_ids:iface-id=lsp1 -OVS_WAIT_UNTIL([test 4 = `as hv1 ovs-ofctl dump-flows br-int | \ +OVS_WAIT_UNTIL([test 0 = `as hv1 ovs-ofctl dump-flows br-int | \ grep conjunction | wc -l`]) # unbind the port again @@ -28163,20 +28159,20 @@ ovn-sbctl dump-flows sw0 > sw0flows AT_CAPTURE_FILE([sw0flows]) AT_CHECK([grep "ls_in_lookup_fdb" sw0flows | sort], [0], [dnl - table=3 (ls_in_lookup_fdb ), priority=0 , dnl + table=2 (ls_in_lookup_fdb ), priority=0 , dnl match=(1), action=(next;) - table=3 (ls_in_lookup_fdb ), priority=100 , dnl + table=2 (ls_in_lookup_fdb ), priority=100 , dnl match=(inport == "sw0-p1"), action=(reg0[[11]] = lookup_fdb(inport, eth.src); next;) - table=3 (ls_in_lookup_fdb ), priority=100 , dnl + table=2 (ls_in_lookup_fdb ), priority=100 , dnl match=(inport == "sw0-p3"), action=(reg0[[11]] = lookup_fdb(inport, eth.src); next;) ]) AT_CHECK([grep "ls_in_put_fdb" sw0flows | sort], [0], [dnl - table=4 (ls_in_put_fdb ), priority=0 , dnl + table=3 (ls_in_put_fdb ), priority=0 , dnl match=(1), action=(next;) - table=4 (ls_in_put_fdb ), priority=100 , dnl + table=3 (ls_in_put_fdb ), priority=100 , dnl match=(inport == "sw0-p1" && reg0[[11]] == 0), action=(put_fdb(inport, eth.src); next;) - table=4 (ls_in_put_fdb ), priority=100 , dnl + table=3 (ls_in_put_fdb ), priority=100 , dnl match=(inport == "sw0-p3" && reg0[[11]] == 0), action=(put_fdb(inport, eth.src); next;) ]) @@ -29319,7 +29315,7 @@ rtr_port_key=$(fetch_column Port_Binding tunnel_key logical_port=ls_lr) # Check that ovn-controller adds a flow to drop packets with dest IP # 42.42.42.42 coming from the router port. -AT_CHECK([ovs-ofctl dump-flows br-int table=17 | grep "reg14=0x${rtr_port_key},metadata=0x${dp_key},nw_dst=42.42.42.42 actions=drop" -c], [0], [dnl +AT_CHECK([ovs-ofctl dump-flows br-int table=16 | grep "reg14=0x${rtr_port_key},metadata=0x${dp_key},nw_dst=42.42.42.42 actions=drop" -c], [0], [dnl 1 ]) @@ -29948,15 +29944,15 @@ done check ovn-nbctl --wait=hv sync # hv0 should see flows for lsp1 but not lsp2 -AT_CHECK([as hv1 ovs-ofctl dump-flows br-int table=26 | grep 10.0.1.2], [0], [ignore]) -AT_CHECK([as hv1 ovs-ofctl dump-flows br-int table=26 | grep 10.0.2.2], [1]) +AT_CHECK([as hv1 ovs-ofctl dump-flows br-int table=25 | grep 10.0.1.2], [0], [ignore]) +AT_CHECK([as hv1 ovs-ofctl dump-flows br-int table=25 | grep 10.0.2.2], [1]) # hv2 should see flows for lsp2 but not lsp1 -AT_CHECK([as hv2 ovs-ofctl dump-flows br-int table=26 | grep 10.0.2.2], [0], [ignore]) -AT_CHECK([as hv2 ovs-ofctl dump-flows br-int table=26 | grep 10.0.1.2], [1]) +AT_CHECK([as hv2 ovs-ofctl dump-flows br-int table=25 | grep 10.0.2.2], [0], [ignore]) +AT_CHECK([as hv2 ovs-ofctl dump-flows br-int table=25 | grep 10.0.1.2], [1]) # Change lrp_lr_ls1 to a regular lrp, hv2 should see flows for lsp1 check ovn-nbctl --wait=hv lrp-del-gateway-chassis lrp_lr_ls1 hv1 -AT_CHECK([as hv2 ovs-ofctl dump-flows br-int table=26 | grep 10.0.1.2], [0], [ignore]) +AT_CHECK([as hv2 ovs-ofctl dump-flows br-int table=25 | grep 10.0.1.2], [0], [ignore]) # Change it back, and trigger recompute to make sure extra flows are removed # from hv2 (recompute is needed because currently I-P adds local datapaths but @@ -29964,11 +29960,11 @@ AT_CHECK([as hv2 ovs-ofctl dump-flows br-int table=26 | grep 10.0.1.2], [0], [ig check ovn-nbctl --wait=hv lrp-set-gateway-chassis lrp_lr_ls1 hv1 1 as hv2 check ovn-appctl -t ovn-controller recompute ovn-nbctl --wait=hv sync -AT_CHECK([as hv2 ovs-ofctl dump-flows br-int table=26 | grep 10.0.1.2], [1]) +AT_CHECK([as hv2 ovs-ofctl dump-flows br-int table=25 | grep 10.0.1.2], [1]) # Enable dnat_and_snat on lr, and now hv2 should see flows for lsp1. AT_CHECK([ovn-nbctl --wait=hv --gateway-port=lrp_lr_ls1 lr-nat-add lr dnat_and_snat 192.168.0.1 10.0.1.3 lsp1 f0:00:00:00:00:03]) -AT_CHECK([as hv2 ovs-ofctl dump-flows br-int table=26 | grep 10.0.1.2], [0], [ignore]) +AT_CHECK([as hv2 ovs-ofctl dump-flows br-int table=25 | grep 10.0.1.2], [0], [ignore]) OVN_CLEANUP([hv1],[hv2]) AT_CLEANUP @@ -30743,8 +30739,7 @@ check_port_sec_offlows hv2 74 check_port_sec_offlows hv2 75 # Set port security for sw0p1 -check ovn-sbctl set port-binding sw0p1 port_security='"00:00:00:00:00:03"' -check ovn-nbctl --wait=hv sync +check ovn-nbctl --wait=hv lsp-set-port-security sw0p1 "00:00:00:00:00:03" check_port_sec_offlows() { hv=$1 @@ -30785,8 +30780,7 @@ check_port_sec_offlows hv2 74 check_port_sec_offlows hv2 75 # Add IPv4 addresses to sw0p1 -check ovn-sbctl set port-binding sw0p1 port_security='"00:00:00:00:00:03 10.0.0.3" "00:00:00:00:00:13 10.0.0.13"' -check ovn-nbctl --wait=hv sync +check ovn-nbctl --wait=hv lsp-set-port-security sw0p1 "00:00:00:00:00:03 10.0.0.3" "00:00:00:00:00:13 10.0.0.13" echo " table=73, priority=80,reg14=0x1,metadata=0x1 actions=load:0x1->NXM_NX_REG10[[12]] table=73, priority=90,ip,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:00:03,nw_src=10.0.0.3 actions=load:0->NXM_NX_REG10[[12]] @@ -30832,7 +30826,7 @@ check_port_sec_offlows hv2 74 check_port_sec_offlows hv2 75 # Configure IPv4 and IPv6 addresses in sw0p2 -check ovn-sbctl set port-binding sw0p2 port_security='"00:00:00:00:00:04 10.0.0.4 20.0.0.4/24 30.0.0.0/16 1000::4 2000::/64" "00:00:00:00:00:13 aef0::4"' +check ovn-nbctl --wait=hv lsp-set-port-security sw0p2 "00:00:00:00:00:04 10.0.0.4 20.0.0.4/24 30.0.0.0/16 1000::4 2000::/64" "00:00:00:00:00:13 aef0::4" # There should be no changes in hv1 and hv2 as sw0p2 is not claimed. check_port_sec_offlows hv1 73 @@ -30919,8 +30913,7 @@ echo " table=75, priority=80,reg15=0x2,metadata=0x1 actions=load:0x1->NXM_NX_REG check_port_sec_offlows hv2 75 -check ovn-sbctl clear port-binding sw0p2 port_security -check ovn-nbctl --wait=hv sync +check ovn-nbctl --wait=hv lsp-set-port-security sw0p2 "" check_port_sec_offlows hv1 73 check_port_sec_offlows hv1 74 @@ -30934,7 +30927,7 @@ check_port_sec_offlows hv2 73 check_port_sec_offlows hv2 74 check_port_sec_offlows hv2 75 -check ovn-sbctl set port-binding sw0p2 port_security='"00:00:00:00:00:04"' +check ovn-nbctl --wait=hv lsp-set-port-security sw0p2 "00:00:00:00:00:04" check_port_sec_offlows hv1 73 check_port_sec_offlows hv1 74 @@ -31029,9 +31022,11 @@ SNAT_TABLE=43 DNAT_ZONE_REG="NXM_NX_REG11[[0..15]]" SNAT_ZONE_REG="NXM_NX_REG12[[0..15]]" -dnat_zone=$(ovs-ofctl dump-flows br-int | grep table=$DNAT_TABLE | grep -o zone=.*, | cut -d '=' -f 2) +lr0_dp_key=$(printf "%x" $(fetch_column Datapath_Binding tunnel_key external_ids:name=lr0)) + +dnat_zone=$(ovs-ofctl dump-flows br-int table=$DNAT_TABLE,metadata=0x${lr0_dp_key} | grep -o zone=.*, | cut -d '=' -f 2) dnat_zone=${dnat_zone::-1} -snat_zone=$(ovs-ofctl dump-flows br-int | grep table=$SNAT_TABLE | grep priority=153 | grep -o zone=.*, | cut -d '=' -f 2) +snat_zone=$(ovs-ofctl dump-flows br-int table=$SNAT_TABLE,metadata=0x${lr0_dp_key} | grep priority=153 | grep -o zone=.*, | cut -d '=' -f 2) snat_zone=${snat_zone::-1} # For now, we expect that the common zone is the dnat zone @@ -31041,9 +31036,9 @@ check test "$snat_zone" = "$DNAT_ZONE_REG" check ovn-nbctl --wait=hv set logical_router lr0 options:snat-ct-zone=666 -dnat_zone=$(ovs-ofctl dump-flows br-int | grep table=$DNAT_TABLE | grep -o zone=.*, | cut -d '=' -f 2) +dnat_zone=$(ovs-ofctl dump-flows br-int table=$DNAT_TABLE,metadata=0x${lr0_dp_key} | grep -o zone=.*, | cut -d '=' -f 2) dnat_zone=${dnat_zone::-1} -snat_zone=$(ovs-ofctl dump-flows br-int | grep table=$SNAT_TABLE | grep priority=153 | grep -o zone=.*, | cut -d '=' -f 2) +snat_zone=$(ovs-ofctl dump-flows br-int table=$SNAT_TABLE,metadata=0x${lr0_dp_key} | grep priority=153 | grep -o zone=.*, | cut -d '=' -f 2) snat_zone=${snat_zone::-1} # Now with an snat-ct-zone set, the common zone is the snat zone