From patchwork Fri Feb 5 06:59:12 2021
Content-Type: text/plain; charset="utf-8"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
X-Patchwork-Submitter: Numan Siddique
+ This table is primarily used to learn the MACs observed on a VIF
+ which belongs to a
+
From patchwork Fri Feb 5 06:59:50 2021
Content-Type: text/plain; charset="utf-8"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
X-Patchwork-Submitter: Numan Siddique Logical_Switch_Port
record in
+ OVN_Northbound
whose port security is disabled
+ and 'unknown' address set. If port security is disabled on a
+ Logical_Switch_Port
record, OVN should allow traffic
+ with any source mac from the VIF. This table will be used to deliver
+ a packet to the VIF, If a packet's eth.dst
is learnt.
+
P = get_fdb(A);
+ Parameters:48-bit MAC address field A. +
+ +
+ Looks up A in fdb table. If an entry is found, stores
+ the logical port key to the out parameter P
.
+
Example: outport = get_fdb(eth.src);
put_fdb(P, A);
+ + Parameters: logical port string field P, 48-bit + MAC address field A. +
+ ++ Adds or updates the entry for Ethernet address A in + fdb table, setting its logical port key to P. +
+ +Example: put_fdb(inport, arp.spa);
R = lookup_fdb(P, A);
+ + Parameters: 48-bit MAC address field M, + logical port string field P. +
+ ++ Result: stored to a 1-bit subfield R. +
+ +
+ Looks up A in fdb table. If an entry is found
+ and the the logical port key is P, P
,
+ stores 1
in the 1-bit subfield
+ R, else 0.
+
+ Example:
+
+ reg0[0] = lookup_fdb(inport, eth.src);
+
+
nd_ns { action;
... };
diff --git a/tests/ovn.at b/tests/ovn.at
index 80c9fe138a..8dbd3bf932 100644
--- a/tests/ovn.at
+++ b/tests/ovn.at
@@ -1822,6 +1822,64 @@ ct_snat_to_vip(foo);
handle_bfd_msg();
encodes as controller(userdata=00.00.00.17.00.00.00.00)
+# put_fdb
+put_fdb(inport, arp.sha);
+ encodes as push:NXM_OF_ETH_SRC[],push:NXM_NX_ARP_SHA[],pop:NXM_OF_ETH_SRC[],controller(userdata=00.00.00.19.00.00.00.00),pop:NXM_OF_ETH_SRC[]
+ has prereqs eth.type == 0x806
+
+put_fdb(inport, eth.src);
+ encodes as controller(userdata=00.00.00.19.00.00.00.00)
+
+put_fdb(inport, ip4.src);
+ Cannot use 32-bit field ip4.src[0..31] where 48-bit field is required.
+
+# get_fdb
+outport = get_fdb(eth.dst);
+ encodes as set_field:0->reg15,resubmit(,71)
+
+outport = get_fdb(eth.src);
+ encodes as push:NXM_OF_ETH_DST[],push:NXM_OF_ETH_SRC[],pop:NXM_OF_ETH_DST[],set_field:0->reg15,resubmit(,71),pop:NXM_OF_ETH_DST[]
+
+inport = get_fdb(arp.sha);
+ encodes as push:NXM_OF_ETH_DST[],push:NXM_NX_ARP_SHA[],pop:NXM_OF_ETH_DST[],set_field:0->reg15,resubmit(,71),pop:NXM_OF_ETH_DST[],move:NXM_NX_REG15[]->NXM_NX_REG14[]
+ has prereqs eth.type == 0x806
+
+reg0 = get_fdb(arp.tha);
+ encodes as push:NXM_OF_ETH_DST[],push:NXM_NX_ARP_THA[],pop:NXM_OF_ETH_DST[],set_field:0->reg15,resubmit(,71),pop:NXM_OF_ETH_DST[],move:NXM_NX_REG15[]->NXM_NX_XXREG0[96..127]
+ has prereqs eth.type == 0x806
+
+reg0[1..3] = get_fdb(eth.src);
+ Cannot use 3-bit field reg0[1..3] where 32-bit field is required.
+
+reg15 = get_fdb(eth.dst);
+ Syntax error at `reg15' expecting field name.
+
+outport = get_fdb(ip4.dst);
+ Cannot use 32-bit field ip4.dst[0..31] where 48-bit field is required.
+
+# lookup_fdb
+reg0[0] = lookup_fdb(inport, eth.src);
+ encodes as set_field:0/0x100->reg10,resubmit(,72),move:NXM_NX_REG10[8]->NXM_NX_XXREG0[96]
+
+reg1[4] = lookup_fdb(outport, eth.dst);
+ encodes as push:NXM_NX_REG14[],push:NXM_OF_ETH_SRC[],push:NXM_OF_ETH_DST[],push:NXM_NX_REG15[],pop:NXM_NX_REG14[],pop:NXM_OF_ETH_SRC[],set_field:0/0x100->reg10,resubmit(,72),pop:NXM_OF_ETH_SRC[],pop:NXM_NX_REG14[],move:NXM_NX_REG10[8]->NXM_NX_XXREG0[68]
+
+reg0[0] = lookup_fdb(outport, arp.sha);
+ encodes as push:NXM_NX_REG14[],push:NXM_OF_ETH_SRC[],push:NXM_NX_ARP_SHA[],push:NXM_NX_REG15[],pop:NXM_NX_REG14[],pop:NXM_OF_ETH_SRC[],set_field:0/0x100->reg10,resubmit(,72),pop:NXM_OF_ETH_SRC[],pop:NXM_NX_REG14[],move:NXM_NX_REG10[8]->NXM_NX_XXREG0[96]
+ has prereqs eth.type == 0x806
+
+reg0 = lookup_fdb(outport, arp.sha);
+ Cannot use 32-bit field reg0[0..31] where 1-bit field is required.
+
+outport = lookup_fdb(outport, arp.sha);
+ Cannot use string field outport where numeric field is required.
+
+reg1[1] = lookup_fdb(outport, ip4.src);
+ Cannot use 32-bit field ip4.src[0..31] where 48-bit field is required.
+
+reg1[1] = lookup_fdb(ip4.src, eth.src);
+ Cannot use numeric field ip4.src where string field is required.
+
# Miscellaneous negative tests.
;
Syntax error at `;'.
diff --git a/tests/test-ovn.c b/tests/test-ovn.c
index 9bd13a037f..202a96c5dd 100644
--- a/tests/test-ovn.c
+++ b/tests/test-ovn.c
@@ -1348,6 +1348,8 @@ test_parse_actions(struct ovs_cmdl_context *ctx OVS_UNUSED)
.lb_hairpin_ptable = OFTABLE_CHK_LB_HAIRPIN,
.lb_hairpin_reply_ptable = OFTABLE_CHK_LB_HAIRPIN_REPLY,
.ct_snat_vip_ptable = OFTABLE_CT_SNAT_FOR_VIP,
+ .fdb_ptable = OFTABLE_GET_FDB,
+ .fdb_lookup_ptable = OFTABLE_LOOKUP_FDB,
};
struct ofpbuf ofpacts;
ofpbuf_init(&ofpacts, 0);
diff --git a/utilities/ovn-trace.c b/utilities/ovn-trace.c
index c0a0edc0a3..fead759b49 100644
--- a/utilities/ovn-trace.c
+++ b/utilities/ovn-trace.c
@@ -405,6 +405,7 @@ struct ovntrace_datapath {
size_t n_flows, allocated_flows;
struct hmap mac_bindings; /* Contains "struct ovntrace_mac_binding"s. */
+ struct hmap fdbs; /* Contains "struct ovntrace_fdb"s. */
bool has_local_l3gateway;
};
@@ -453,12 +454,24 @@ struct ovntrace_mac_binding {
struct eth_addr mac;
};
+struct ovntrace_fdb {
+ struct hmap_node node;
+ uint16_t port_key;
+ struct eth_addr mac;
+};
+
static inline uint32_t
hash_mac_binding(uint16_t port_key, const struct in6_addr *ip)
{
return hash_bytes(ip, sizeof *ip, port_key);
}
+static inline uint32_t
+hash_fdb(const struct eth_addr *mac)
+{
+ return hash_bytes(mac, sizeof *mac, 0);
+}
+
/* Every ovntrace_datapath, by southbound Datapath_Binding record UUID. */
static struct hmap datapaths;
@@ -518,6 +531,18 @@ ovntrace_datapath_find_by_name(const char *name)
return match;
}
+static struct ovntrace_datapath *
+ovntrace_datapath_find_by_key(uint32_t tunnel_key)
+{
+ struct ovntrace_datapath *dp;
+ HMAP_FOR_EACH (dp, sb_uuid_node, &datapaths) {
+ if (dp->tunnel_key == tunnel_key) {
+ return dp;
+ }
+ }
+ return NULL;
+}
+
static const struct ovntrace_port *
ovntrace_port_find_by_key(const struct ovntrace_datapath *dp,
uint16_t tunnel_key)
@@ -598,6 +623,20 @@ ovntrace_mac_binding_find_mac_ip(const struct ovntrace_datapath *dp,
return NULL;
}
+static const struct ovntrace_fdb *
+ovntrace_fdb_find(const struct ovntrace_datapath *dp,
+ const struct eth_addr *mac)
+{
+ const struct ovntrace_fdb *fdb;
+ HMAP_FOR_EACH_WITH_HASH (fdb, node, hash_fdb(mac),
+ &dp->fdbs) {
+ if (eth_addr_equals(fdb->mac, *mac)) {
+ return fdb;
+ }
+ }
+ return NULL;
+}
+
/* If 's' ends with a UUID, returns a copy of it with the UUID truncated to
* just the first 6 characters; otherwise, returns a copy of 's'. */
static char *
@@ -638,7 +677,7 @@ read_datapaths(void)
ovs_list_init(&dp->mcgroups);
hmap_init(&dp->mac_bindings);
-
+ hmap_init(&dp->fdbs);
hmap_insert(&datapaths, &dp->sb_uuid_node, uuid_hash(&dp->sb_uuid));
}
}
@@ -1053,6 +1092,30 @@ read_mac_bindings(void)
}
}
+static void
+read_fdbs(void)
+{
+ const struct sbrec_fdb *fdb;
+ SBREC_FDB_FOR_EACH (fdb, ovnsb_idl) {
+ struct eth_addr mac;
+ if (!eth_addr_from_string(fdb->mac, &mac)) {
+ VLOG_WARN("%s: bad Ethernet address", fdb->mac);
+ continue;
+ }
+
+ struct ovntrace_datapath *dp =
+ ovntrace_datapath_find_by_key(fdb->dp_key);
+ if (!dp) {
+ continue;
+ }
+
+ struct ovntrace_fdb *fdb_t = xmalloc(sizeof *fdb_t);
+ fdb_t->mac = mac;
+ fdb_t->port_key = fdb->port_key;
+ hmap_insert(&dp->fdbs, &fdb_t->node, hash_fdb(&mac));
+ }
+}
+
static void
read_db(void)
{
@@ -1064,6 +1127,7 @@ read_db(void)
read_gen_opts();
read_flows();
read_mac_bindings();
+ read_fdbs();
}
static const struct ovntrace_port *
@@ -2029,6 +2093,66 @@ execute_lookup_mac_bind_ip(const struct ovnact_lookup_mac_bind_ip *bind,
mf_write_subfield_flow(&dst, &sv, uflow);
}
+static void
+execute_lookup_fdb(const struct ovnact_lookup_fdb *lookup_fdb,
+ const struct ovntrace_datapath *dp,
+ struct flow *uflow,
+ struct ovs_list *super)
+{
+ /* Get logical port number.*/
+ struct mf_subfield port_sf = expr_resolve_field(&lookup_fdb->port);
+ ovs_assert(port_sf.n_bits == 32);
+ uint32_t port_key = mf_get_subfield(&port_sf, uflow);
+
+ /* Get MAC. */
+ struct mf_subfield mac_sf = expr_resolve_field(&lookup_fdb->mac);
+ ovs_assert(mac_sf.n_bits == 48);
+ union mf_subvalue mac_sv;
+ mf_read_subfield(&mac_sf, uflow, &mac_sv);
+
+ const struct ovntrace_fdb *fdb_t
+ = ovntrace_fdb_find(dp, &mac_sv.mac);
+
+ struct mf_subfield dst = expr_resolve_field(&lookup_fdb->dst);
+ uint8_t val = 0;
+
+ if (fdb_t && fdb_t->port_key == port_key) {
+ val = 1;
+ ovntrace_node_append(super, OVNTRACE_NODE_ACTION,
+ "/* MAC lookup for "ETH_ADDR_FMT" found in "
+ "FDB. */", ETH_ADDR_ARGS(uflow->dl_dst));
+ } else {
+ ovntrace_node_append(super, OVNTRACE_NODE_ACTION,
+ "/* lookup mac failed in mac learning table. */");
+ }
+ union mf_subvalue sv = { .u8_val = val };
+ mf_write_subfield_flow(&dst, &sv, uflow);
+}
+
+static void
+execute_get_fdb(const struct ovnact_get_fdb *get_fdb,
+ const struct ovntrace_datapath *dp,
+ struct flow *uflow)
+{
+ /* Get MAC. */
+ struct mf_subfield mac_sf = expr_resolve_field(&get_fdb->mac);
+ ovs_assert(mac_sf.n_bits == 48);
+ union mf_subvalue mac_sv;
+ mf_read_subfield(&mac_sf, uflow, &mac_sv);
+
+ const struct ovntrace_fdb *fdb_t
+ = ovntrace_fdb_find(dp, &mac_sv.mac);
+
+ struct mf_subfield dst = expr_resolve_field(&get_fdb->dst);
+ uint32_t val = 0;
+ if (fdb_t) {
+ val = fdb_t->port_key;
+ }
+
+ union mf_subvalue sv = { .be32_int = htonl(val) };
+ mf_write_subfield_flow(&dst, &sv, uflow);
+}
+
static void
execute_put_opts(const struct ovnact_put_opts *po,
const char *name, struct flow *uflow,
@@ -2638,6 +2762,18 @@ trace_actions(const struct ovnact *ovnacts, size_t ovnacts_len,
break;
case OVNACT_BFD_MSG:
break;
+
+ case OVNACT_PUT_FDB:
+ /* Nothing to do for tracing. */
+ break;
+
+ case OVNACT_GET_FDB:
+ execute_get_fdb(ovnact_get_GET_FDB(a), dp, uflow);
+ break;
+
+ case OVNACT_LOOKUP_FDB:
+ execute_lookup_fdb(ovnact_get_LOOKUP_FDB(a), dp, uflow, super);
+ break;
}
}
ds_destroy(&s);
From patchwork Fri Feb 5 07:00:04 2021
Content-Type: text/plain; charset="utf-8"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
X-Patchwork-Submitter: Numan Siddique
+ This table looks up the MAC learning table of the logical switch
+ datapath to check if the
+ For each such logical port p whose port security
+ is disabled and 'unknown' address set following flow
+ is added.
+
+ This table learns the MAC addresses seen on the logical ports
+ whose port security is disabled and 'unknown' address set
+ if the
+ For each such logical port p whose port security
+ is disabled and 'unknown' address set following flow
+ is added.
+
This table prepares flows for possible stateful ACL processing in
@@ -332,7 +398,7 @@
db="OVN_Northbound"/> table.
This table prepares flows for possible stateful load balancing processing
@@ -399,7 +465,7 @@
logical router datapath to logical switch datapath.
This table prepares flows for all possible stateful processing
@@ -410,7 +476,7 @@
This table consists of logical flows that set hints
@@ -489,7 +555,7 @@
-
Logical flows in this table closely reproduce those in the
@@ -598,7 +664,7 @@
-
Logical flows in this table closely reproduce those in the
@@ -620,7 +686,7 @@
-
Logical flows in this table closely reproduce those in the
@@ -642,7 +708,7 @@
-
It contains a priority-0 flow that simply moves traffic to the next
@@ -668,7 +734,7 @@
connection.)
This table implements ARP/ND responder in a logical switch for known
@@ -1080,7 +1146,7 @@ output;
-
This table adds the DHCPv4 options to a DHCPv4 packet from the
@@ -1141,7 +1207,7 @@ next;
-
This table implements DHCP responder for the DHCP replies generated by
@@ -1222,7 +1288,7 @@ output;
-
This table looks up and resolves the DNS names to the corresponding
@@ -1251,7 +1317,7 @@ reg0[4] = dns_lookup(); next;
-
This table implements DNS responder for the DNS replies generated by
@@ -1286,7 +1352,7 @@ output;
-
Traffic from the
This table implements switching behavior. It contains these logical
@@ -1492,12 +1558,58 @@ output;
+ This table handles the packets whose destination was not found or
+ and looked up in the MAC learning table of the logical switch
+ datapath. It contains the following flows.
+
+ If the logical switch has logical ports with 'unknown' addresses set,
+ then the below logical flow is added
+
+ If the logical switch has no logical ports with 'unknown' address
+ set, then the below logical flow is added
+ Ingress Table 3:
+ from-lport
Pre-ACLsIngress Table 3: Lookup MAC address learning table
+
+ port-mac
pair is present
+ or not. MAC is learnt only for logical switch VIF ports whose
+ port security is disabled and 'unknown' address set.
+
+
+
+
+
+ inport == p
and action
+ reg0[11] = lookup_fdb(inport, eth.src); next;
+ Ingress Table 4: Learn MAC of 'unknown' ports.
+
+ lookup_fdb
action returned false in the
+ previous table.
+
+
+
+
+
+ inport == p && reg0[11] == 0
and
+ action put_fdb(inport, eth.src); next;
which stores
+ the port-mac
in the mac learning table of the
+ logical switch datapath and advances the packet to the next table.
+ Ingress Table 5:
from-lport
Pre-ACLsIngress Table 4: Pre-LB
+ Ingress Table 6: Pre-LB
Ingress Table 5: Pre-stateful
+ Ingress Table 7: Pre-stateful
ct_next;
action.
Ingress Table 6:
+ from-lport
ACL hintsIngress Table 8:
from-lport
ACL hintsIngress table 7:
+ from-lport
ACLsIngress table 9:
from-lport
ACLsIngress Table 8:
+ from-lport
QoS MarkingIngress Table 10:
from-lport
QoS MarkingIngress Table 9:
+ from-lport
QoS MeterIngress Table 11:
from-lport
QoS MeterIngress Table 10: LB
+ Ingress Table 12: LB
Ingress Table 11: Stateful
+ Ingress Table 13: Stateful
- Ingress Table 12: Pre-Hairpin
+ Ingress Table 14: Pre-Hairpin
- Ingress Table 13: Nat-Hairpin
+ Ingress Table 15: Nat-Hairpin
- Ingress Table 14: Hairpin
+ Ingress Table 16: Hairpin
- Ingress Table 15: ARP/ND responder
+ Ingress Table 17: ARP/ND responder
Ingress Table 16: DHCP option processing
+ Ingress Table 18: DHCP option processing
Ingress Table 17: DHCP responses
+ Ingress Table 19: DHCP responses
Ingress Table 18 DNS Lookup
+ Ingress Table 20 DNS Lookup
Ingress Table 19 DNS Responses
+ Ingress Table 21 DNS Responses
Ingress table 20 External ports
+ Ingress table 22 External ports
external
logical ports enter the ingress
@@ -1329,7 +1395,7 @@ output;
- Ingress Table 21 Destination Lookup
+ Ingress Table 23 Destination Lookup
MC_UNKNOWN
multicast group, which
- ovn-northd
populates with all enabled logical ports that
- accept unknown destination packets. As a small optimization, if no
- logical ports accept unknown destination packets,
- ovn-northd
omits this multicast group and logical flow.
+ One priority-0 fallback flow that matches all packets with the
+ action outport = get_fdb(eth.dst); next;
. The action
+ get_fdb
gets the port for the eth.dst
+ in the MAC learning table of the logical switch datapath. If there
+ is no entry for eth.dst
in the MAC learning table,
+ then it stores none
in the outport
.
+ Ingress Table 23 Destination unknown
+
+
+
diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c
index 12a24a694f..23c26de36f 100644
--- a/northd/ovn-northd.c
+++ b/northd/ovn-northd.c
@@ -138,25 +138,28 @@ enum ovn_stage {
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, PRE_ACL, 3, "ls_in_pre_acl") \
- PIPELINE_STAGE(SWITCH, IN, PRE_LB, 4, "ls_in_pre_lb") \
- PIPELINE_STAGE(SWITCH, IN, PRE_STATEFUL, 5, "ls_in_pre_stateful") \
- PIPELINE_STAGE(SWITCH, IN, ACL_HINT, 6, "ls_in_acl_hint") \
- PIPELINE_STAGE(SWITCH, IN, ACL, 7, "ls_in_acl") \
- PIPELINE_STAGE(SWITCH, IN, QOS_MARK, 8, "ls_in_qos_mark") \
- PIPELINE_STAGE(SWITCH, IN, QOS_METER, 9, "ls_in_qos_meter") \
- PIPELINE_STAGE(SWITCH, IN, LB, 10, "ls_in_lb") \
- PIPELINE_STAGE(SWITCH, IN, STATEFUL, 11, "ls_in_stateful") \
- PIPELINE_STAGE(SWITCH, IN, PRE_HAIRPIN, 12, "ls_in_pre_hairpin") \
- PIPELINE_STAGE(SWITCH, IN, NAT_HAIRPIN, 13, "ls_in_nat_hairpin") \
- PIPELINE_STAGE(SWITCH, IN, HAIRPIN, 14, "ls_in_hairpin") \
- PIPELINE_STAGE(SWITCH, IN, ARP_ND_RSP, 15, "ls_in_arp_rsp") \
- PIPELINE_STAGE(SWITCH, IN, DHCP_OPTIONS, 16, "ls_in_dhcp_options") \
- PIPELINE_STAGE(SWITCH, IN, DHCP_RESPONSE, 17, "ls_in_dhcp_response") \
- PIPELINE_STAGE(SWITCH, IN, DNS_LOOKUP, 18, "ls_in_dns_lookup") \
- PIPELINE_STAGE(SWITCH, IN, DNS_RESPONSE, 19, "ls_in_dns_response") \
- PIPELINE_STAGE(SWITCH, IN, EXTERNAL_PORT, 20, "ls_in_external_port") \
- PIPELINE_STAGE(SWITCH, IN, L2_LKUP, 21, "ls_in_l2_lkup") \
+ 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, 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") \
@@ -223,6 +226,7 @@ enum ovn_stage {
#define REGBIT_ACL_HINT_ALLOW "reg0[8]"
#define REGBIT_ACL_HINT_DROP "reg0[9]"
#define REGBIT_ACL_HINT_BLOCK "reg0[10]"
+#define REGBIT_LKUP_FDB "reg0[11]"
/* Register definitions for switches and routers. */
@@ -4717,6 +4721,42 @@ build_lswitch_input_port_sec_od(
}
}
+static void
+build_lswitch_learn_fdb_op(
+ struct ovn_port *op, struct hmap *lflows,
+ struct ds *actions, struct ds *match)
+{
+ if (op->nbsp && !op->n_ps_addrs && !strcmp(op->nbsp->type, "") &&
+ op->has_unknown) {
+ ds_clear(match);
+ ds_clear(actions);
+ ds_put_format(match, "inport == %s", op->json_key);
+ ds_put_format(actions, REGBIT_LKUP_FDB
+ " = lookup_fdb(inport, eth.src); next;");
+ ovn_lflow_add_with_hint(lflows, op->od, S_SWITCH_IN_LOOKUP_FDB, 100,
+ ds_cstr(match), ds_cstr(actions),
+ &op->nbsp->header_);
+
+ ds_put_cstr(match, " && "REGBIT_LKUP_FDB" == 0");
+ ds_clear(actions);
+ ds_put_cstr(actions, "put_fdb(inport, eth.src); next;");
+ ovn_lflow_add_with_hint(lflows, op->od, S_SWITCH_IN_PUT_FDB, 100,
+ ds_cstr(match), ds_cstr(actions),
+ &op->nbsp->header_);
+ }
+}
+
+static void
+build_lswitch_learn_fdb_od(
+ struct ovn_datapath *od, struct hmap *lflows)
+{
+
+ if (od->nbs) {
+ ovn_lflow_add(lflows, od, S_SWITCH_IN_LOOKUP_FDB, 0, "1", "next;");
+ ovn_lflow_add(lflows, od, S_SWITCH_IN_PUT_FDB, 0, "1", "next;");
+ }
+}
+
/* Egress table 8: Egress port security - IP (priorities 90 and 80)
* if port security enabled.
*
@@ -6572,16 +6612,25 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *lflows)
struct ds actions = DS_EMPTY_INITIALIZER;
struct ovn_datapath *od;
- /* Ingress table 19: Destination lookup for unknown MACs (priority 0). */
+ /* Ingress table 24: Destination lookup for unknown MACs (priority 0). */
HMAP_FOR_EACH (od, key_node, datapaths) {
if (!od->nbs) {
continue;
}
+ ovn_lflow_add(lflows, od, S_SWITCH_IN_L2_LKUP, 0, "1",
+ "outport = get_fdb(eth.dst); next;");
+
if (od->has_unknown) {
- ovn_lflow_add_unique(lflows, od, S_SWITCH_IN_L2_LKUP, 0, "1",
- "outport = \""MC_UNKNOWN"\"; output;");
+ ovn_lflow_add(lflows, od, S_SWITCH_IN_L2_UNKNOWN, 50,
+ "outport == \"none\"",
+ "outport = \""MC_UNKNOWN"\"; output;");
+ } else {
+ ovn_lflow_add(lflows, od, S_SWITCH_IN_L2_UNKNOWN, 50,
+ "outport == \"none\"", "drop;");
}
+ ovn_lflow_add(lflows, od, S_SWITCH_IN_L2_UNKNOWN, 0, "1",
+ "output;");
}
ds_destroy(&match);
@@ -11421,6 +11470,7 @@ 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);
build_lswitch_dhcp_and_dns_defaults(od, lsi->lflows);
@@ -11460,6 +11510,8 @@ build_lswitch_and_lrouter_iterate_by_op(struct ovn_port *op,
/* Build Logical Switch Flows. */
build_lswitch_input_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,
&lsi->match);
build_lswitch_arp_nd_responder_known_ips(op, lsi->lflows,
diff --git a/ovn-sb.ovsschema b/ovn-sb.ovsschema
index 246e3908a0..b5d3338f46 100644
--- a/ovn-sb.ovsschema
+++ b/ovn-sb.ovsschema
@@ -1,7 +1,7 @@
{
"name": "OVN_Southbound",
- "version": "20.16.0",
- "cksum": "1219580357 26536",
+ "version": "20.16.1",
+ "cksum": "4243908307 26536",
"tables": {
"SB_Global": {
"columns": {
@@ -103,7 +103,7 @@
"egress"]]}}},
"table_id": {"type": {"key": {"type": "integer",
"minInteger": 0,
- "maxInteger": 23}}},
+ "maxInteger": 32}}},
"priority": {"type": {"key": {"type": "integer",
"minInteger": 0,
"maxInteger": 65535}}},
diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
index 7240e22baf..1b4160cfa0 100644
--- a/tests/ovn-northd.at
+++ b/tests/ovn-northd.at
@@ -1753,10 +1753,10 @@ AT_CAPTURE_FILE([sw1flows])
AT_CHECK(
[grep -E 'ls_(in|out)_acl' sw0flows sw1flows | grep pg0 | sort], [0], [dnl
-sw0flows: table=5 (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=21); };)
-sw0flows: table=7 (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=6); };)
-sw1flows: table=5 (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=21); };)
-sw1flows: table=7 (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=6); };)
+sw0flows: table=5 (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=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=6); };)
+sw1flows: table=5 (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=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=6); };)
])
AS_BOX([2])
@@ -1769,10 +1769,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=5 (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=21); };)
-sw0flows2: table=5 (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=21); };)
-sw1flows2: table=5 (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=21); };)
-sw1flows2: table=5 (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=21); };)
+sw0flows2: table=5 (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=5 (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=5 (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=5 (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])
@@ -1787,16 +1787,16 @@ AT_CAPTURE_FILE([sw1flows3])
AT_CHECK([grep "ls_out_acl" sw0flows3 sw1flows3 | grep pg0 | sort], [0], [dnl
sw0flows3: table=5 (ls_out_acl ), priority=2001 , match=(reg0[[7]] == 1 && (outport == @pg0 && ip)), action=(reg0[[1]] = 1; next;)
sw0flows3: table=5 (ls_out_acl ), priority=2001 , match=(reg0[[8]] == 1 && (outport == @pg0 && ip)), action=(next;)
-sw0flows3: table=5 (ls_out_acl ), priority=2002 , match=((reg0[[10]] == 1) && outport == @pg0 && ip4 && udp), action=(ct_commit { ct_label.blocked = 1; }; reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=21); };)
-sw0flows3: table=5 (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=21); };)
-sw0flows3: table=5 (ls_out_acl ), priority=2003 , match=((reg0[[10]] == 1) && outport == @pg0 && ip6 && udp), action=(ct_commit { ct_label.blocked = 1; }; reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=21); };)
-sw0flows3: table=5 (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=21); };)
+sw0flows3: table=5 (ls_out_acl ), priority=2002 , match=((reg0[[10]] == 1) && outport == @pg0 && ip4 && udp), action=(ct_commit { ct_label.blocked = 1; }; reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=23); };)
+sw0flows3: table=5 (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=5 (ls_out_acl ), priority=2003 , match=((reg0[[10]] == 1) && outport == @pg0 && ip6 && udp), action=(ct_commit { ct_label.blocked = 1; }; reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=23); };)
+sw0flows3: table=5 (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=5 (ls_out_acl ), priority=2001 , match=(reg0[[7]] == 1 && (outport == @pg0 && ip)), action=(reg0[[1]] = 1; next;)
sw1flows3: table=5 (ls_out_acl ), priority=2001 , match=(reg0[[8]] == 1 && (outport == @pg0 && ip)), action=(next;)
-sw1flows3: table=5 (ls_out_acl ), priority=2002 , match=((reg0[[10]] == 1) && outport == @pg0 && ip4 && udp), action=(ct_commit { ct_label.blocked = 1; }; reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=21); };)
-sw1flows3: table=5 (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=21); };)
-sw1flows3: table=5 (ls_out_acl ), priority=2003 , match=((reg0[[10]] == 1) && outport == @pg0 && ip6 && udp), action=(ct_commit { ct_label.blocked = 1; }; reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=21); };)
-sw1flows3: table=5 (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=21); };)
+sw1flows3: table=5 (ls_out_acl ), priority=2002 , match=((reg0[[10]] == 1) && outport == @pg0 && ip4 && udp), action=(ct_commit { ct_label.blocked = 1; }; reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=23); };)
+sw1flows3: table=5 (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=5 (ls_out_acl ), priority=2003 , match=((reg0[[10]] == 1) && outport == @pg0 && ip6 && udp), action=(ct_commit { ct_label.blocked = 1; }; reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=23); };)
+sw1flows3: table=5 (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
diff --git a/tests/ovn.at b/tests/ovn.at
index 8dbd3bf932..eee77a5dc4 100644
--- a/tests/ovn.at
+++ b/tests/ovn.at
@@ -8247,18 +8247,18 @@ as hv1
AT_CHECK([ovs-vsctl add-port br-int localvif1 -- set Interface localvif1 external_ids:iface-id=localvif1])
# On hv1, check that there are no flows outputting bcast to tunnel
-OVS_WAIT_UNTIL([test `ovs-ofctl dump-flows br-int table=32 | ofctl_strip | grep output | wc -l` -eq 0])
+OVS_WAIT_UNTIL([test `ovs-ofctl dump-flows br-int table=37 | ofctl_strip | grep output | wc -l` -eq 0])
# On hv2, check that no flow outputs bcast to tunnel to hv1.
as hv2
-OVS_WAIT_UNTIL([test `ovs-ofctl dump-flows br-int table=32 | ofctl_strip | grep output | wc -l` -eq 0])
+OVS_WAIT_UNTIL([test `ovs-ofctl dump-flows br-int table=37 | ofctl_strip | grep output | wc -l` -eq 0])
# Now bind vif2 on hv2.
AT_CHECK([ovs-vsctl add-port br-int localvif2 -- set Interface localvif2 external_ids:iface-id=localvif2])
# At this point, the broadcast flow on vif2 should be deleted.
-# because, there is now a localnet vif bound (table=32 programming logic)
-OVS_WAIT_UNTIL([test `ovs-ofctl dump-flows br-int table=32 | ofctl_strip | grep output | wc -l` -eq 0])
+# because, there is now a localnet vif bound (table=37 programming logic)
+OVS_WAIT_UNTIL([test `ovs-ofctl dump-flows br-int table=37 | ofctl_strip | grep output | wc -l` -eq 0])
# Verify that the local net patch port exists on hv2.
OVS_WAIT_UNTIL([test `ovs-vsctl show | grep "Port patch-br-int-to-ln_port" | wc -l` -eq 1])
@@ -10388,12 +10388,12 @@ AT_CAPTURE_FILE([hv2flows])
AT_CHECK(
[# Check that redirect mapping is programmed only on hv2
- grep table=33 hv1flows | grep =0x3,metadata=0x1 | wc -l
- grep table=33 hv2flows | grep =0x3,metadata=0x1 | grep load:0x2- | wc -l
+ grep table=38 hv1flows | grep =0x3,metadata=0x1 | wc -l
+ grep table=38 hv2flows | grep =0x3,metadata=0x1 | grep load:0x2- | wc -l
# Check that hv1 sends chassisredirect port traffic to hv2
- grep table=32 hv1flows | grep =0x3,metadata=0x1 | grep output | wc -l
- grep table=32 hv2flows | grep =0x3,metadata=0x1 | wc -l
+ grep table=37 hv1flows | grep =0x3,metadata=0x1 | grep output | wc -l
+ grep table=37 hv2flows | grep =0x3,metadata=0x1 | wc -l
# Check that arp reply on distributed gateway port is only programmed on hv2
grep arp hv1flows | grep load:0x2- | grep =0x2,metadata=0x1 | wc -l
@@ -10943,8 +10943,8 @@ as hv3 reset_pcap_file hv3-vif1 hv3/vif1
as hv1 ovs-appctl netdev-dummy/receive hv1-vif1 $packet
sleep 2
-# On hv1, table 32 check that no packet goes via the tunnel port
-AT_CHECK([as hv1 ovs-ofctl dump-flows br-int table=32 \
+# On hv1, table 37 check that no packet goes via the tunnel port
+AT_CHECK([as hv1 ovs-ofctl dump-flows br-int table=37 \
| grep "NXM_NX_TUN_ID" | grep -v n_packets=0 | wc -l], [0], [[0
]])
@@ -11634,20 +11634,20 @@ echo $hv2_gw1_ofport
echo $hv2_gw2_ofport
echo "--- hv1 ---"
-as hv1 ovs-ofctl dump-flows br-int table=32
+as hv1 ovs-ofctl dump-flows br-int table=37
echo "--- hv2 ---"
-as hv2 ovs-ofctl dump-flows br-int table=32
+as hv2 ovs-ofctl dump-flows br-int table=37
gw1_chassis=$(fetch_column Chassis _uuid name=gw1)
gw2_chassis=$(fetch_column Chassis _uuid name=gw2)
-OVS_WAIT_UNTIL([as hv1 ovs-ofctl dump-flows br-int table=32 | \
+OVS_WAIT_UNTIL([as hv1 ovs-ofctl dump-flows br-int table=37 | \
grep active_backup | grep slaves:$hv1_gw1_ofport,$hv1_gw2_ofport \
| wc -l], [0], [1
])
-OVS_WAIT_UNTIL([as hv2 ovs-ofctl dump-flows br-int table=32 | \
+OVS_WAIT_UNTIL([as hv2 ovs-ofctl dump-flows br-int table=37 | \
grep active_backup | grep slaves:$hv2_gw1_ofport,$hv2_gw2_ofport \
| wc -l], [0], [1
])
@@ -11688,12 +11688,12 @@ wait_for_ports_up
check ovn-nbctl --wait=hv sync
# we make sure that the hypervisors noticed, and inverted the slave ports
-OVS_WAIT_UNTIL([as hv1 ovs-ofctl dump-flows br-int table=32 | \
+OVS_WAIT_UNTIL([as hv1 ovs-ofctl dump-flows br-int table=37 | \
grep active_backup | grep slaves:$hv1_gw2_ofport,$hv1_gw1_ofport \
| wc -l], [0], [1
])
-OVS_WAIT_UNTIL([as hv2 ovs-ofctl dump-flows br-int table=32 | \
+OVS_WAIT_UNTIL([as hv2 ovs-ofctl dump-flows br-int table=37 | \
grep active_backup | grep slaves:$hv2_gw2_ofport,$hv2_gw1_ofport \
| wc -l], [0], [1
])
@@ -11846,12 +11846,12 @@ ovn-nbctl set Logical_Router_Port outside ha_chassis_group=$hagrp1_uuid
wait_row_count HA_Chassis_Group 1
wait_row_count HA_Chassis 2
-OVS_WAIT_UNTIL([as hv1 ovs-ofctl dump-flows br-int table=32 | \
+OVS_WAIT_UNTIL([as hv1 ovs-ofctl dump-flows br-int table=37 | \
grep active_backup | grep slaves:$hv1_gw1_ofport,$hv1_gw2_ofport \
| wc -l], [0], [1
])
-OVS_WAIT_UNTIL([as hv2 ovs-ofctl dump-flows br-int table=32 | \
+OVS_WAIT_UNTIL([as hv2 ovs-ofctl dump-flows br-int table=37 | \
grep active_backup | grep slaves:$hv2_gw1_ofport,$hv2_gw2_ofport \
| wc -l], [0], [1
])
@@ -11903,12 +11903,12 @@ wait_column "$exp_ref_ch_list" HA_Chassis_Group ref_chassis
# Increase the priority of gw2
ovn-nbctl --wait=sb ha-chassis-group-add-chassis hagrp1 gw2 40
-OVS_WAIT_UNTIL([as hv1 ovs-ofctl dump-flows br-int table=32 | \
+OVS_WAIT_UNTIL([as hv1 ovs-ofctl dump-flows br-int table=37 | \
grep active_backup | grep slaves:$hv1_gw2_ofport,$hv1_gw1_ofport \
| wc -l], [0], [1
])
-OVS_WAIT_UNTIL([as hv2 ovs-ofctl dump-flows br-int table=32 | \
+OVS_WAIT_UNTIL([as hv2 ovs-ofctl dump-flows br-int table=37 | \
grep active_backup | grep slaves:$hv2_gw2_ofport,$hv2_gw1_ofport \
| wc -l], [0], [1
])
@@ -15352,7 +15352,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=28,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
])
@@ -15950,6 +15950,7 @@ ovn-nbctl lsp-add sw1 rp-sw1 -- set Logical_Switch_Port rp-sw1 \
ovn-nbctl lsp-add sw0 sw0-p0 \
-- lsp-set-addresses sw0-p0 "f0:00:00:01:02:03 192.168.1.2 2001::2"
+
ovn-nbctl lsp-add sw0 sw0-p1 \
-- lsp-set-addresses sw0-p1 "f0:00:00:11:02:03 192.168.1.3 2001::3"
@@ -16015,7 +16016,7 @@ policy=$(fetch_column nb:Logical_Router_Policy _uuid priority=2000)
check ovn-nbctl set logical_router_policy $policy options:pkt_mark=100
as hv2
# add a flow in egress pipeline to check pkt marking
-ovs-ofctl --protocols=OpenFlow13 add-flow br-int "table=32,priority=200,ip,nw_src=172.16.1.2,pkt_mark=0x64 actions=resubmit(,33)"
+ovs-ofctl --protocols=OpenFlow13 add-flow br-int "table=37,priority=200,ip,nw_src=172.16.1.2,pkt_mark=0x64 actions=resubmit(,38)"
dst_ip=$(ip_to_hex 172 16 2 10)
fip_ip=$(ip_to_hex 172 16 1 2)
@@ -16027,7 +16028,7 @@ echo $(get_arp_req f00000010204 $fip_ip $gw_router_ip) >> expected
send_arp_reply 2 1 $gw_router_mac f00000010204 $gw_router_ip $fip_ip
echo "${gw_router_mac}f0000001020408004500001c00004000fe0121b4${fip_ip}${dst_ip}${data}" >> expected
-AT_CHECK([as hv2 ovs-ofctl dump-flows br-int table=32 | grep pkt_mark=0x64 | grep -q n_packets=1],[0])
+AT_CHECK([as hv2 ovs-ofctl dump-flows br-int table=37 | grep pkt_mark=0x64 | grep -q n_packets=1],[0])
OVN_CHECK_PACKETS([hv2/vif1-tx.pcap], [expected])
@@ -19016,7 +19017,7 @@ m4_define([DVR_N_S_PING],
OVN_CHECK_PACKETS_REMOVE_BROADCAST([hv4/vif-north-tx.pcap], [vif-north.expected])
# Confirm that packets did not go out via tunnel port.
- AT_CHECK([as hv1 ovs-ofctl dump-flows br-int | grep table=33 | grep NXM_NX_TUN_METADATA0 | grep n_packets=0 | wc -l], [0], [[0
+ AT_CHECK([as hv1 ovs-ofctl dump-flows br-int | grep table=38 | grep NXM_NX_TUN_METADATA0 | grep n_packets=0 | wc -l], [0], [[0
]])
# Confirm that packet went out via localnet port
@@ -19146,7 +19147,8 @@ list mac_binding], [0], [lr0-sw0
50:54:00:00:00:03
])
-AT_CHECK([test 1 = `cat hv1/ovn-controller.log | grep NXT_PACKET_IN2 | wc -l`])
+AT_CHECK([test 1 = `cat hv1/ovn-controller.log | grep NXT_PACKET_IN2 | \
+grep table_id=10 | wc -l`])
AT_CHECK([test 1 = `as hv1 ovs-ofctl dump-flows br-int table=10 | grep arp | \
grep controller | grep -v n_packets=0 | wc -l`])
@@ -19163,7 +19165,8 @@ OVS_WAIT_UNTIL([test 1 = `as hv1 ovs-ofctl dump-flows br-int table=67 | grep n_p
# The packet should not be sent to ovn-controller. The packet
# count should be 1 only.
-AT_CHECK([test 1 = `cat hv1/ovn-controller.log | grep NXT_PACKET_IN2 | wc -l`])
+AT_CHECK([test 1 = `cat hv1/ovn-controller.log | grep NXT_PACKET_IN2 | \
+grep table_id=10 | wc -l`])
AT_CHECK([test 1 = `as hv1 ovs-ofctl dump-flows br-int table=10 | grep arp | \
grep controller | grep -v n_packets=0 | wc -l`])
@@ -19176,7 +19179,8 @@ send_garp 1 1 $eth_src $eth_dst $spa $tpa
# The garp packet should be sent to ovn-controller and the mac_binding entry
# should be updated.
-OVS_WAIT_UNTIL([test 2 = `cat hv1/ovn-controller.log | grep NXT_PACKET_IN2 | wc -l`])
+OVS_WAIT_UNTIL([test 2 = `cat hv1/ovn-controller.log | grep NXT_PACKET_IN2 | \
+grep table_id=10 | wc -l`])
check_row_count MAC_Binding 1
@@ -19201,7 +19205,8 @@ send_garp 1 1 $eth_src $eth_dst $spa $tpa
# The garp packet should be sent to ovn-controller and the mac_binding entry
# should be updated.
-OVS_WAIT_UNTIL([test 3 = `cat hv1/ovn-controller.log | grep NXT_PACKET_IN2 | wc -l`])
+OVS_WAIT_UNTIL([test 3 = `cat hv1/ovn-controller.log | grep NXT_PACKET_IN2 | \
+grep table_id=10 | wc -l`])
OVS_WAIT_UNTIL(
[test 1 = `as hv1 ovs-ofctl dump-flows br-int table=67 | grep dl_src=50:54:00:00:00:33 \
@@ -19222,7 +19227,8 @@ OVS_WAIT_UNTIL(
| grep n_packets=1 | wc -l`]
)
-AT_CHECK([test 3 = `cat hv1/ovn-controller.log | grep NXT_PACKET_IN2 | wc -l`])
+AT_CHECK([test 3 = `cat hv1/ovn-controller.log | grep NXT_PACKET_IN2 | \
+grep table_id=10 | wc -l`])
# Now send ARP reply packet with IP - 10.0.0.40 and mac 505400000023
eth_src=505400000023
@@ -19239,7 +19245,8 @@ send_arp_reply 1 1 $eth_src $eth_dst $spa $tpa
# The garp packet should be sent to ovn-controller and the mac_binding entry
# should be updated.
-OVS_WAIT_UNTIL([test 4 = `cat hv1/ovn-controller.log | grep NXT_PACKET_IN2 | wc -l`])
+OVS_WAIT_UNTIL([test 4 = `cat hv1/ovn-controller.log | grep NXT_PACKET_IN2 | \
+grep table_id=10 | wc -l`])
# Wait for an entry in table=67 for the learnt mac_binding entry.
@@ -19255,7 +19262,8 @@ OVS_WAIT_UNTIL(
| grep n_packets=1 | wc -l`]
)
-AT_CHECK([test 4 = `cat hv1/ovn-controller.log | grep NXT_PACKET_IN2 | wc -l`])
+AT_CHECK([test 4 = `cat hv1/ovn-controller.log | grep NXT_PACKET_IN2 | \
+grep table_id=10 | wc -l`])
send_arp_reply 1 1 $eth_src $eth_dst $spa $tpa
OVS_WAIT_UNTIL(
@@ -19263,7 +19271,8 @@ OVS_WAIT_UNTIL(
| grep n_packets=2 | wc -l`]
)
-AT_CHECK([test 4 = `cat hv1/ovn-controller.log | grep NXT_PACKET_IN2 | wc -l`])
+AT_CHECK([test 4 = `cat hv1/ovn-controller.log | grep NXT_PACKET_IN2 | \
+grep table_id=10 | wc -l`])
OVN_CLEANUP([hv1], [hv2])
AT_CLEANUP
@@ -21882,22 +21891,22 @@ AT_CHECK([test ! -z $p1_zoneid])
p2_zoneid=$(as hv1 ovs-vsctl get bridge br-int external_ids:ct-zone-sw0-p2 | sed 's/"//g')
AT_CHECK([test ! -z $p2_zoneid])
-AT_CHECK([test $(ovs-ofctl dump-flows br-int table=33,metadata=${sw0_dpkey},\
+AT_CHECK([test $(ovs-ofctl dump-flows br-int table=38,metadata=${sw0_dpkey},\
reg15=0x${p1_dpkey} | grep REG13 | wc -l) -eq 1])
-AT_CHECK([test $(ovs-ofctl dump-flows br-int table=33,metadata=${sw0_dpkey},\
+AT_CHECK([test $(ovs-ofctl dump-flows br-int table=38,metadata=${sw0_dpkey},\
reg15=0x${p1_dpkey} | grep "load:0x${p1_zoneid}->NXM_NX_REG13" | wc -l) -eq 1])
-AT_CHECK([test $(ovs-ofctl dump-flows br-int table=33,metadata=${sw1_dpkey},\
+AT_CHECK([test $(ovs-ofctl dump-flows br-int table=38,metadata=${sw1_dpkey},\
reg15=0x${p2_dpkey} | grep REG13 | wc -l) -eq 1])
-AT_CHECK([test $(ovs-ofctl dump-flows br-int table=33,metadata=${sw1_dpkey},\
+AT_CHECK([test $(ovs-ofctl dump-flows br-int table=38,metadata=${sw1_dpkey},\
reg15=0x${p2_dpkey} | grep "load:0x${p2_zoneid}->NXM_NX_REG13" | wc -l) -eq 1])
ovs-vsctl set interface hv1-vif1 external_ids:iface-id=foo
OVS_WAIT_UNTIL([test x$(ovn-nbctl lsp-get-up sw0-p1) = xdown])
-AT_CHECK([test $(ovs-ofctl dump-flows br-int table=33,metadata=${sw0_dpkey},\
+AT_CHECK([test $(ovs-ofctl dump-flows br-int table=38,metadata=${sw0_dpkey},\
reg15=0x${p1_dpkey} | grep REG13 | wc -l) -eq 0])
p1_zoneid=$(as hv1 ovs-vsctl get bridge br-int external_ids:ct-zone-sw0-p1 | sed 's/"//g')
@@ -21909,16 +21918,16 @@ OVS_WAIT_UNTIL([test x$(ovn-nbctl lsp-get-up sw0-p1) = xup])
p1_zoneid=$(as hv1 ovs-vsctl get bridge br-int external_ids:ct-zone-sw0-p1 | sed 's/"//g')
AT_CHECK([test ! -z $p1_zoneid])
-AT_CHECK([test $(ovs-ofctl dump-flows br-int table=33,metadata=${sw0_dpkey},\
+AT_CHECK([test $(ovs-ofctl dump-flows br-int table=38,metadata=${sw0_dpkey},\
reg15=0x${p1_dpkey} | grep REG13 | wc -l) -eq 1])
-AT_CHECK([test $(ovs-ofctl dump-flows br-int table=33,metadata=${sw0_dpkey},\
+AT_CHECK([test $(ovs-ofctl dump-flows br-int table=38,metadata=${sw0_dpkey},\
reg15=0x${p1_dpkey} | grep "load:0x${p1_zoneid}->NXM_NX_REG13" | wc -l) -eq 1])
ovs-vsctl del-port hv1-vif2
OVS_WAIT_UNTIL([test x$(ovn-nbctl lsp-get-up sw0-p2) = xdown])
-AT_CHECK([test $(ovs-ofctl dump-flows br-int table=33,metadata=${sw0_dpkey},\
+AT_CHECK([test $(ovs-ofctl dump-flows br-int table=38,metadata=${sw0_dpkey},\
reg15=0x${p2_dpkey} | grep REG13 | wc -l) -eq 0])
p2_zoneid=$(as hv1 ovs-vsctl get bridge br-int external_ids:ct-zone-sw0-p2 | sed 's/"//g')
@@ -21926,7 +21935,7 @@ AT_CHECK([test -z $p2_zoneid])
ovn-nbctl lsp-del sw0-p1
-OVS_WAIT_UNTIL([test $(ovs-ofctl dump-flows br-int table=33,metadata=${sw0_dpkey},\
+OVS_WAIT_UNTIL([test $(ovs-ofctl dump-flows br-int table=38,metadata=${sw0_dpkey},\
reg15=0x${p1_dpkey} | grep REG13 | wc -l) -eq 0])
p1_zoneid=$(as hv1 ovs-vsctl get bridge br-int external_ids:ct-zone-sw0-p1 | sed 's/"//g')
@@ -22510,7 +22519,8 @@ ovn-nbctl lsp-add s1 lsp-s1-r1 -- set Logical_Switch_Port lsp-s1-r1 type=router
# Create logical port p1 in s1
ovn-nbctl lsp-add s1 p1 \
--- lsp-set-addresses p1 "f0:00:00:00:01:02 10.0.1.2"
+-- lsp-set-addresses p1 "f0:00:00:00:01:02 10.0.1.2" \
+-- lsp-set-port-security p1 "f0:00:00:00:01:02 10.0.1.2"
# Create two hypervisor and create OVS ports corresponding to logical ports.
net_add n1
@@ -24003,3 +24013,426 @@ wait_column "true" nb:Logical_Switch_Port up name=lsp1
OVN_CLEANUP([hv1])
AT_CLEANUP
+
+AT_SETUP([ovn -- OVN FDB (MAC learning) - 2 HVs, 2 LS, 1 LR ])
+ovn_start
+
+# Create the first logical switch with one port
+check ovn-nbctl ls-add sw0
+check ovn-nbctl lsp-add sw0 sw0-p1
+check ovn-nbctl lsp-set-addresses sw0-p1 "50:54:00:00:00:03 10.0.0.3" unknown
+
+check ovn-nbctl lsp-add sw0 sw0-p2
+check ovn-nbctl lsp-set-addresses sw0-p2 "50:54:00:00:00:04 10.0.0.4"
+# Port security is set for sw0-p2
+check ovn-nbctl lsp-set-port-security sw0-p2 "50:54:00:00:00:04 10.0.0.4"
+
+# sw0-p1 and sw0-p3 have unknown address and no port security.
+# FDB should be enabled for these lports.
+check ovn-nbctl lsp-add sw0 sw0-p3
+check ovn-nbctl lsp-set-addresses sw0-p3 unknown
+
+# Create the second logical switch with one port
+check ovn-nbctl ls-add sw1
+check ovn-nbctl lsp-add sw1 sw1-p1
+check ovn-nbctl lsp-set-addresses sw1-p1 "40:54:00:00:00:03 11.0.0.3" unknown
+
+check ovn-nbctl lsp-add sw1 sw1-p2
+check ovn-nbctl lsp-set-addresses sw1-p2 "40:54:00:00:00:04 11.0.0.4"
+check ovn-nbctl lsp-set-port-security sw1-p2 "40:54:00:00:00:04 11.0.0.4"
+
+# Create a logical router and attach both logical switches
+check ovn-nbctl lr-add lr0
+check ovn-nbctl lrp-add lr0 lr0-sw0 00:00:00:00:ff:01 10.0.0.1/24
+check ovn-nbctl lsp-add sw0 sw0-lr0
+check ovn-nbctl lsp-set-type sw0-lr0 router
+check ovn-nbctl lsp-set-addresses sw0-lr0 router
+check ovn-nbctl lsp-set-options sw0-lr0 router-port=lr0-sw0
+
+check ovn-nbctl lrp-add lr0 lr0-sw1 00:00:00:00:ff:02 11.0.0.1/24
+check ovn-nbctl lsp-add sw1 sw1-lr0
+check ovn-nbctl lsp-set-type sw1-lr0 router
+check ovn-nbctl lsp-set-addresses sw1-lr0 router
+check ovn-nbctl lsp-set-options sw1-lr0 router-port=lr0-sw1
+ovn-nbctl --wait=hv sync
+
+net_add n1
+
+sim_add hv1
+as hv1
+ovs-vsctl add-br br-phys
+ovn_attach n1 br-phys 192.168.0.1
+ovs-vsctl -- add-port br-int hv1-vif1 -- \
+ set interface hv1-vif1 external-ids:iface-id=sw0-p1 \
+ options:tx_pcap=hv1/vif1-tx.pcap \
+ options:rxq_pcap=hv1/vif1-rx.pcap \
+ ofport-request=1
+ovs-vsctl -- add-port br-int hv1-vif2 -- \
+ set interface hv1-vif2 external-ids:iface-id=sw1-p2 \
+ options:tx_pcap=hv1/vif2-tx.pcap \
+ options:rxq_pcap=hv1/vif2-rx.pcap \
+ ofport-request=2
+ovs-vsctl -- add-port br-int hv1-vif3 -- \
+ set interface hv1-vif3 external-ids:iface-id=sw0-p3 \
+ options:tx_pcap=hv1/vif3-tx.pcap \
+ options:rxq_pcap=hv1/vif3-rx.pcap \
+ ofport-request=3
+
+sim_add hv2
+as hv2
+ovs-vsctl add-br br-phys
+ovn_attach n1 br-phys 192.168.0.2
+ovs-vsctl -- add-port br-int hv2-vif1 -- \
+ set interface hv2-vif1 external-ids:iface-id=sw0-p2 \
+ options:tx_pcap=hv2/vif1-tx.pcap \
+ options:rxq_pcap=hv2/vif1-rx.pcap \
+ ofport-request=1
+ovs-vsctl -- add-port br-int hv2-vif2 -- \
+ set interface hv2-vif2 external-ids:iface-id=sw1-p1 \
+ options:tx_pcap=hv2/vif2-tx.pcap \
+ options:rxq_pcap=hv2/vif2-rx.pcap \
+ ofport-request=2
+
+OVN_POPULATE_ARP
+
+ip_to_hex() {
+ printf "%02x%02x%02x%02x" "$@"
+}
+
+send_icmp_packet() {
+ local inport=$1 hv=$2 eth_src=$3 eth_dst=$4 ipv4_src=$5 ipv4_dst=$6 ip_chksum=$7 data=$8
+ shift 8
+
+ local ip_ttl=ff
+ local ip_len=001c
+ local packet=${eth_dst}${eth_src}08004500${ip_len}00004000${ip_ttl}01${ip_chksum}${ipv4_src}${ipv4_dst}${data}
+ echo $packet > expected
+ as hv$hv ovs-appctl netdev-dummy/receive hv$hv-vif$inport $packet
+}
+
+reset_pcap_file() {
+ local iface=$1
+ local pcap_file=$2
+ ovs-vsctl -- set Interface $iface options:tx_pcap=dummy-tx.pcap \
+options:rxq_pcap=dummy-rx.pcap
+ rm -f ${pcap_file}*.pcap
+ ovs-vsctl -- set Interface $iface options:tx_pcap=${pcap_file}-tx.pcap \
+options:rxq_pcap=${pcap_file}-rx.pcap
+}
+
+trim_zeros() {
+ sed 's/\(00\)\{1,\}$//'
+}
+
+AS_BOX([Wait for all ports to be up])
+wait_for_ports_up
+
+# Check that there is put_fdb() flow added by ovn-northd for sw0-p1
+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
+match=(1), action=(next;)
+ table=3 (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
+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
+match=(1), action=(next;)
+ table=4 (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
+match=(inport == "sw0-p3" && reg0[[11]] == 0), action=(put_fdb(inport, eth.src); next;)
+])
+
+# Send a packet from sw0-p1 with a different mac not present
+# in it's addresses.
+AS_BOX([Send a pkt from sw0-p1 with a different mac address])
+
+# Use the src mac 50:54:00:00:00:13 instead of 50:54:00:00:00:03
+src_mac=505400000013
+src_ip=$(ip_to_hex 10 0 0 13)
+
+# send the packet to sw0-p2
+dst_mac=505400000004
+dst_ip=$(ip_to_hex 10 0 0 4)
+
+data=0800bee4391a0001
+send_icmp_packet 1 1 $src_mac $dst_mac $src_ip $dst_ip 0000 $data
+OVN_CHECK_PACKETS([hv2/vif1-tx.pcap], [expected])
+
+# There should be one row in fdb
+AS_BOX([Check that the FDB entry is created])
+wait_row_count FDB 1
+
+sw0_dpkey=$(fetch_column datapath_binding tunnel_key external_ids:name=sw0)
+sw0p1_dpkey=$(fetch_column port_binding tunnel_key logical_port=sw0-p1)
+sw0p3_dpkey=$(fetch_column port_binding tunnel_key logical_port=sw0-p3)
+
+check_column '50:54:00:00:00:13' fdb mac
+check_column $sw0_dpkey fdb dp_key
+check_column $sw0p1_dpkey fdb port_key
+
+# Make sure that OVS tables 71 and 72 are populated on both hv1 and hv2.
+AS_BOX([Check that ovn-controller programs the flows for FDB])
+as hv1 ovs-ofctl dump-flows br-int table=71 > hv1_offlows_table71.txt
+as hv2 ovs-ofctl dump-flows br-int table=71 > hv2_offlows_table71.txt
+
+AT_CAPTURE_FILE([hv1_offlows_table71.txt])
+AT_CAPTURE_FILE([hv2_offlows_table71.txt])
+AT_CHECK([cat hv1_offlows_table71.txt | grep -v NXST | cut -d ' ' -f8- | sort], [0], [dnl
+priority=100,metadata=0x1,dl_dst=50:54:00:00:00:13 actions=load:0x1->NXM_NX_REG15[[]]
+])
+
+AT_CHECK([cat hv2_offlows_table71.txt | grep -v NXST | cut -d ' ' -f8- | sort], [0], [dnl
+priority=100,metadata=0x1,dl_dst=50:54:00:00:00:13 actions=load:0x1->NXM_NX_REG15[[]]
+])
+
+as hv1 ovs-ofctl dump-flows br-int table=72 > hv1_offlows_table72.txt
+as hv2 ovs-ofctl dump-flows br-int table=72 > hv2_offlows_table72.txt
+
+AT_CAPTURE_FILE([hv1_offlows_table72.txt])
+AT_CAPTURE_FILE([hv2_offlows_table72.txt])
+AT_CHECK([cat hv1_offlows_table72.txt | grep -v NXST | cut -d ' ' -f8- | sort], [0], [dnl
+priority=100,reg14=0x1,metadata=0x1,dl_src=50:54:00:00:00:13 actions=load:0x1->NXM_NX_REG10[[8]]
+])
+
+AT_CHECK([cat hv2_offlows_table72.txt | grep -v NXST | cut -d ' ' -f8- | sort], [0], [dnl
+priority=100,reg14=0x1,metadata=0x1,dl_src=50:54:00:00:00:13 actions=load:0x1->NXM_NX_REG10[[8]]
+])
+
+# Use the src mac 50:54:00:00:00:14 instead of 50:54:00:00:00:03
+src_mac=505400000014
+src_ip=$(ip_to_hex 10 0 0 14)
+
+as hv2 reset_pcap_file hv2-vif1 hv2/vif1
+
+send_icmp_packet 1 1 $src_mac $dst_mac $src_ip $dst_ip 0000 $data
+OVN_CHECK_PACKETS([hv2/vif1-tx.pcap], [expected])
+
+# There should be two rows in fdb
+wait_row_count FDB 2
+
+check_column "50:54:00:00:00:13 50:54:00:00:00:14" fdb mac
+check_column "$sw0_dpkey $sw0_dpkey" fdb dp_key
+check_column "$sw0p1_dpkey $sw0p1_dpkey" fdb port_key
+
+# Make sure that OVS tables 71 and 72 are populated on both hv1 and hv2.
+as hv1 ovs-ofctl dump-flows br-int table=71 > hv1_offlows_table71.txt
+as hv2 ovs-ofctl dump-flows br-int table=71 > hv2_offlows_table71.txt
+
+AT_CAPTURE_FILE([hv1_offlows_table71.txt])
+AT_CAPTURE_FILE([hv2_offlows_table71.txt])
+AT_CHECK([cat hv1_offlows_table71.txt | grep -v NXST | cut -d ' ' -f8- | sort], [0], [dnl
+priority=100,metadata=0x1,dl_dst=50:54:00:00:00:13 actions=load:0x1->NXM_NX_REG15[[]]
+priority=100,metadata=0x1,dl_dst=50:54:00:00:00:14 actions=load:0x1->NXM_NX_REG15[[]]
+])
+
+AT_CHECK([cat hv2_offlows_table71.txt | grep -v NXST | cut -d ' ' -f8- | sort], [0], [dnl
+priority=100,metadata=0x1,dl_dst=50:54:00:00:00:13 actions=load:0x1->NXM_NX_REG15[[]]
+priority=100,metadata=0x1,dl_dst=50:54:00:00:00:14 actions=load:0x1->NXM_NX_REG15[[]]
+])
+
+as hv1 ovs-ofctl dump-flows br-int table=72 > hv1_offlows_table72.txt
+as hv2 ovs-ofctl dump-flows br-int table=72 > hv2_offlows_table72.txt
+
+AT_CAPTURE_FILE([hv1_offlows_table72.txt])
+AT_CAPTURE_FILE([hv2_offlows_table72.txt])
+AT_CHECK([cat hv1_offlows_table72.txt | grep -v NXST | cut -d ' ' -f8- | sort], [0], [dnl
+priority=100,reg14=0x1,metadata=0x1,dl_src=50:54:00:00:00:13 actions=load:0x1->NXM_NX_REG10[[8]]
+priority=100,reg14=0x1,metadata=0x1,dl_src=50:54:00:00:00:14 actions=load:0x1->NXM_NX_REG10[[8]]
+])
+
+AT_CHECK([cat hv2_offlows_table72.txt | grep -v NXST | cut -d ' ' -f8- | sort], [0], [dnl
+priority=100,reg14=0x1,metadata=0x1,dl_src=50:54:00:00:00:13 actions=load:0x1->NXM_NX_REG10[[8]]
+priority=100,reg14=0x1,metadata=0x1,dl_src=50:54:00:00:00:14 actions=load:0x1->NXM_NX_REG10[[8]]
+])
+
+as hv1 reset_pcap_file hv1-vif1 hv1/vif1
+as hv2 reset_pcap_file hv2-vif1 hv2/vif1
+
+# Send the packet from sw0-p2 to sw0-p1 with the dst mac 50:54:00:00:00:13
+src_mac=505400000004
+src_ip=$(ip_to_hex 10 0 0 4)
+
+dst_mac=505400000013
+dst_ip=$(ip_to_hex 10 0 0 13)
+
+send_icmp_packet 1 2 $src_mac $dst_mac $src_ip $dst_ip 0000 $data
+OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [expected])
+
+as hv1 reset_pcap_file hv1-vif1 hv1/vif1
+dst_mac=505400000014
+dst_ip=$(ip_to_hex 10 0 0 14)
+
+send_icmp_packet 1 2 $src_mac $dst_mac $src_ip $dst_ip 0000 $data
+OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [expected])
+
+as hv1 reset_pcap_file hv1-vif1 hv1/vif1
+as hv1 reset_pcap_file hv1-vif3 hv1/vif3
+
+# Send a packet from sw0-p2 to an unknown mac. Should be received
+# by both sw0-p1 and sw0-p3 (as unknown is set).
+AS_BOX([Send pkt from sw0-p2 to an unknown mac])
+
+src_mac=505400000004
+src_ip=$(ip_to_hex 10 0 0 4)
+
+dst_mac=505400000023
+dst_ip=$(ip_to_hex 10 0 0 23)
+
+send_icmp_packet 1 2 $src_mac $dst_mac $src_ip $dst_ip 0000 $data
+
+OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [expected])
+OVN_CHECK_PACKETS([hv1/vif3-tx.pcap], [expected])
+
+AS_BOX([Flip the mac - 50:54:00:00:00:13 from sw0-p1 to sw0-p3])
+
+# Use the src mac 50:54:00:00:00:13
+src_mac=505400000013
+src_ip=$(ip_to_hex 10 0 0 23)
+
+# send the packet to sw0-p2
+dst_mac=505400000004
+dst_ip=$(ip_to_hex 10 0 0 4)
+
+data=0800bee4391a0001
+
+as hv2 reset_pcap_file hv2-vif1 hv2/vif1
+as hv1 reset_pcap_file hv1-vif3 hv1/vif3
+
+# Send the pkt from sw0-p3 to sw0-p2.
+send_icmp_packet 3 1 $src_mac $dst_mac $src_ip $dst_ip 0000 $data
+OVN_CHECK_PACKETS([hv2/vif1-tx.pcap], [expected])
+
+# fdb row count should be still 2. But the mac 50:54:00:00:00:13
+# should be learnt on sw0-p3.
+
+wait_row_count FDB 2
+
+check_column "50:54:00:00:00:13 50:54:00:00:00:14" fdb mac
+check_column "$sw0_dpkey $sw0_dpkey" fdb dp_key
+check_column "$sw0p1_dpkey $sw0p3_dpkey" fdb port_key
+
+check_column "$sw0p3_dpkey" fdb port_key mac="50\:54\:00\:00\:00\:13"
+
+as hv1 reset_pcap_file hv1-vif1 hv1/vif1
+as hv1 reset_pcap_file hv1-vif3 hv1/vif3
+
+# Send the packet from sw0-p2 to sw0-p3 with the dst mac 50:54:00:00:00:13
+src_mac=505400000004
+src_ip=$(ip_to_hex 10 0 0 4)
+
+dst_mac=505400000013
+dst_ip=$(ip_to_hex 10 0 0 13)
+
+send_icmp_packet 1 2 $src_mac $dst_mac $src_ip $dst_ip 0000 $data
+OVN_CHECK_PACKETS([hv1/vif3-tx.pcap], [expected])
+
+# sw0-p1 should not receive the packet.
+: > expected
+OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [expected])
+
+AS_BOX([Test routing])
+
+# Test the routing.
+# Send the packet from sw1-p2 (hv1) to sw0-p1 (hv1) with dst ip 10.0.0.14
+# The packet should be delivered to sw0-p1 with dst mac 50:54:00:00:00:14
+# Before sending add mac_binding entry for 10.0.0.14
+
+lr0_dp_uuid=$(fetch_column datapath_binding _uuid external_ids:name=lr0)
+
+ovn-sbctl create mac_binding ip=10.0.0.14 logical_port=lr0-sw0 \
+mac="50\:54\:00\:00\:00\:14" datapath=$lr0_dp_uuid
+
+# Wait till the mac_binding flows appear in hv1
+OVS_WAIT_UNTIL([test 1 = $(as hv1 ovs-ofctl dump-flows br-int table=66 \
+| grep -c reg0=0xa00000e)])
+
+src_mac=405400000004
+src_ip=$(ip_to_hex 11 0 0 4)
+
+dst_mac=00000000ff02 # lr0-sw1 mac
+dst_ip=$(ip_to_hex 10 0 0 14)
+
+as hv1 reset_pcap_file hv1-vif1 hv1/vif1
+as hv1 reset_pcap_file hv1-vif3 hv1/vif3
+
+send_icmp_packet 2 1 $src_mac $dst_mac $src_ip $dst_ip 0000 $data
+
+exp_packet=50540000001400000000ff0108004500001c00004000fe010100${src_ip}${dst_ip}${data}
+echo $exp_packet > expected
+OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [expected])
+
+# sw0-p3 should not receive the packet.
+: > expected
+OVN_CHECK_PACKETS([hv1/vif3-tx.pcap], [expected])
+
+# Now the send the packet from sw1-p1 (hv2) to sw0-p1 (hv1) with dst ip 10.0.0.14
+# The acket should be delivered to sw0-p1 with dst mac 50:54:00:00:00:14
+
+src_mac=405400000003
+src_ip=$(ip_to_hex 11 0 0 3)
+
+dst_mac=00000000ff02 # lr0-sw1 mac
+dst_ip=$(ip_to_hex 10 0 0 14)
+
+as hv1 reset_pcap_file hv1-vif1 hv1/vif1
+send_icmp_packet 2 2 $src_mac $dst_mac $src_ip $dst_ip 0000 $data
+
+exp_packet=50540000001400000000ff0108004500001c00004000fe010100${src_ip}${dst_ip}${data}
+echo $exp_packet > expected
+OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [expected])
+
+AS_BOX([Clear the FDB rows])
+
+# Clear the fdb rows.
+check ovn-sbctl --all destroy fdb
+ovn-sbctl list fdb
+
+as hv1 reset_pcap_file hv1-vif1 hv1/vif1
+as hv1 reset_pcap_file hv1-vif3 hv1/vif3
+
+# Send the packet from sw0-p2 to sw0-p1 with the dst mac 50:54:00:00:00:14
+# It should be delivered to both sw0-p1 and sw0-p3 since we have cleared the
+# FDB table.
+src_mac=505400000004
+src_ip=$(ip_to_hex 10 0 0 4)
+
+dst_mac=505400000014
+dst_ip=$(ip_to_hex 10 0 0 13)
+
+send_icmp_packet 1 2 $src_mac $dst_mac $src_ip $dst_ip 0000 $data
+
+OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [expected])
+OVN_CHECK_PACKETS([hv1/vif3-tx.pcap], [expected])
+
+# Make sure that OVS tables 71 and 72 are empty.
+as hv1 ovs-ofctl dump-flows br-int table=71 > hv1_offlows_table71.txt
+as hv2 ovs-ofctl dump-flows br-int table=71 > hv2_offlows_table71.txt
+
+AT_CAPTURE_FILE([hv1_offlows_table71.txt])
+AT_CAPTURE_FILE([hv2_offlows_table71.txt])
+AT_CHECK([cat hv1_offlows_table71.txt | grep -v NXST], [1], [dnl
+])
+
+AT_CHECK([cat hv2_offlows_table71.txt | grep -v NXST], [1], [dnl
+])
+
+as hv1 ovs-ofctl dump-flows br-int table=72 > hv1_offlows_table72.txt
+as hv2 ovs-ofctl dump-flows br-int table=72 > hv2_offlows_table72.txt
+
+AT_CAPTURE_FILE([hv1_offlows_table72.txt])
+AT_CAPTURE_FILE([hv2_offlows_table72.txt])
+AT_CHECK([cat hv1_offlows_table72.txt | grep -v NXST], [1], [dnl
+])
+
+AT_CHECK([cat hv2_offlows_table72.txt | grep -v NXST], [1], [dnl
+])
+
+OVN_CLEANUP([hv1], [hv2])
+AT_CLEANUP
diff --git a/utilities/ovn-trace.c b/utilities/ovn-trace.c
index fead759b49..6b883886fb 100644
--- a/utilities/ovn-trace.c
+++ b/utilities/ovn-trace.c
@@ -945,7 +945,7 @@ parse_lflow_for_datapath(const struct sbrec_logical_flow *sblf,
.pipeline = (!strcmp(sblf->pipeline, "ingress")
? OVNACT_P_INGRESS
: OVNACT_P_EGRESS),
- .n_tables = 24,
+ .n_tables = LOG_PIPELINE_LEN,
.cur_ltable = sblf->table_id,
};
uint64_t stub[1024 / 8];
@@ -1184,6 +1184,11 @@ ovntrace_lookup_port(const void *dp_, const char *port_name,
return true;
}
+ if (!strcmp(port_name, "none")) {
+ *portp = 0;
+ return true;
+ }
+
const struct ovntrace_port *port = ovntrace_port_lookup_by_name(port_name);
if (port) {
if (port->dp == dp) {
From patchwork Fri Feb 5 07:00:33 2021
Content-Type: text/plain; charset="utf-8"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
X-Patchwork-Submitter: Numan Siddique
+
+
+ outport == none
then
+ outputs them to the MC_UNKNOWN
multicast group, which
+ ovn-northd
populates with all enabled logical ports
+ that accept unknown destination packets. As a small optimization,
+ if no logical ports accept unknown destination packets,
+ ovn-northd
omits this multicast group and logical
+ flow.
+
+
+ outport == none
+ and drops the packets.
+ get_fdb
action.