From dddd63bb72012f8b779e7935d3178cecf70620d0 Mon Sep 17 00:00:00 2001 From: Alfredo Moralejo Date: Nov 04 2020 12:27:58 +0000 Subject: Import ovn2.13-20.09.0-2 from Fast Datapath --- diff --git a/SOURCES/0001-Fix-ovn-controller-crash-when-a-lport-of-type-virtua.patch b/SOURCES/0001-Fix-ovn-controller-crash-when-a-lport-of-type-virtua.patch deleted file mode 100644 index d73b70f..0000000 --- a/SOURCES/0001-Fix-ovn-controller-crash-when-a-lport-of-type-virtua.patch +++ /dev/null @@ -1,86 +0,0 @@ -From e1cd90a8ac7ede76dabc3714358c32076f9557e7 Mon Sep 17 00:00:00 2001 -From: Numan Siddique -Date: Wed, 26 Aug 2020 16:48:26 +0530 -Subject: [PATCH] Fix ovn-controller crash when a lport of type 'virtual' is - deleted. - -The below bt is seen when a lport of type 'virtual' is deleted. - -(gdb) bt -0x00001470c0708655 in __strlen_avx2 () from /lib64/libc.so.6 -0x0000563340037449 in hash_string (basis=0, s=s@entry=0x0) at lib/hash.h:342 -hash_name (name=name@entry=0x0) at lib/shash.c:28 -0x0000563340037a76 in shash_find (sh=0x5633407bb260, name=0x0) at lib/shash.c:231 -0x0000563340037b7d in shash_find_data (sh=, name=) at lib/shash.c:245 -0x000056333ff71151 in local_binding_find (name=, local_bindings=) at controller/binding.h:108 -get_lbinding_for_lport (b_ctx_out=0x7fff616745b0, lport_type=, pb=0x56334314d630) at controller/binding.c:1960 -handle_deleted_vif_lport (b_ctx_in=0x7fff61674600, b_ctx_in=0x7fff61674600, b_ctx_out=0x7fff616745b0, lport_type=, pb=0x56334314d630) at controller/binding.c:1979 -binding_handle_port_binding_changes (b_ctx_in=b_ctx_in@entry=0x7fff61674600, b_ctx_out=b_ctx_out@entry=0x7fff616745b0) at controller/binding.c:2087 -0x000056333ff8e208 in runtime_data_sb_port_binding_handler (node=0x7fff616759f0, data=0x5633407bb240) at controller/ovn-controller.c:1325 -0x000056333ffa6de3 in engine_compute (recompute_allowed=, node=) at lib/inc-proc-eng.c:306 -... -... - -Fixes: 354bdba51ab("ovn-controller: I-P for SB port binding and OVS interface in runtime_data.") -Acked-by: Mark Michelson -Signed-off-by: Numan Siddique - -(cherry-picked from master commit 51fea73f0e09a1e670b8b7ca52819963bfa29c7e) - -(cherry-picked from upstream branch-20.06 commit edc8b8ffdfd4d0250ea8650b1501e49fb4a82b6f) - -Change-Id: I926ee596800a5cc105e481845bd328acda578dd2 ---- - controller/binding.c | 12 ++++++++---- - tests/ovn.at | 11 +++++++++++ - 2 files changed, 19 insertions(+), 4 deletions(-) - -diff --git a/controller/binding.c b/controller/binding.c -index 880fbb13b..3c102dc7f 100644 ---- a/controller/binding.c -+++ b/controller/binding.c -@@ -1957,11 +1957,15 @@ get_lbinding_for_lport(const struct sbrec_port_binding *pb, - struct local_binding *parent_lbinding = NULL; - - if (lport_type == LP_VIRTUAL) { -- parent_lbinding = local_binding_find(b_ctx_out->local_bindings, -- pb->virtual_parent); -+ if (pb->virtual_parent) { -+ parent_lbinding = local_binding_find(b_ctx_out->local_bindings, -+ pb->virtual_parent); -+ } - } else { -- parent_lbinding = local_binding_find(b_ctx_out->local_bindings, -- pb->parent_port); -+ if (pb->parent_port) { -+ parent_lbinding = local_binding_find(b_ctx_out->local_bindings, -+ pb->parent_port); -+ } - } - - return parent_lbinding -diff --git a/tests/ovn.at b/tests/ovn.at -index 0d99adf3f..1216bc50f 100644 ---- a/tests/ovn.at -+++ b/tests/ovn.at -@@ -16115,6 +16115,17 @@ ovn-nbctl lsp-set-addresses sw1-lr0 00:00:00:00:ff:02 - ovn-nbctl lsp-set-options sw1-lr0 router-port=lr0-sw1 - - OVN_POPULATE_ARP -+ -+# Delete sw0-vir and add again. -+ovn-nbctl lsp-del sw0-vir -+ -+ovn-nbctl lsp-add sw0 sw0-vir -+ovn-nbctl lsp-set-addresses sw0-vir "50:54:00:00:00:10 10.0.0.10" -+ovn-nbctl lsp-set-port-security sw0-vir "50:54:00:00:00:10 10.0.0.10" -+ovn-nbctl lsp-set-type sw0-vir virtual -+ovn-nbctl set logical_switch_port sw0-vir options:virtual-ip=10.0.0.10 -+ovn-nbctl set logical_switch_port sw0-vir options:virtual-parents=sw0-p1,sw0-p2,sw0-p3 -+ - ovn-nbctl --wait=hv sync - - # Check that logical flows are added for sw0-vir in lsp_in_arp_rsp pipeline --- -2.26.2 - diff --git a/SOURCES/0001-Fix-the-data-type-for-DHCP-option-tftp_server-66.patch b/SOURCES/0001-Fix-the-data-type-for-DHCP-option-tftp_server-66.patch deleted file mode 100644 index 36246f3..0000000 --- a/SOURCES/0001-Fix-the-data-type-for-DHCP-option-tftp_server-66.patch +++ /dev/null @@ -1,279 +0,0 @@ -From fe0be84c42c1b304bfbe49e59aa11eea100e16b1 Mon Sep 17 00:00:00 2001 -From: Dhathri Purohith -Date: Thu, 11 Jun 2020 13:44:40 -0700 -Subject: [PATCH 01/22] Fix the data type for DHCP option tftp_server (66) - -Currently, DHCP option is of type ipv4. But, according to RFC 2132, -option 66 can be a hostname i.e, we should also accept string type. -In order to be backward compatible, a new type called "host_id" is -introduced, which accepts both ipv4 address and string. Type for DHCP -option 66 is changed to "host_id" instead of ipv4. -OVN northd code that updates the OVN southbound database is enhanced to -consider the change in the type and code for DHCP option, so that the -change in datatype is reflected. - -Signed-off-by: Dhathri Purohith -Signed-off-by: Ankur Sharma -Signed-off-by: Numan Siddique - -(cherry-picked from upstream master commit b06319993debbeb6d116901afa511d627543c10d) - -Change-Id: I45b4d783bfc8849a69d7c8c8584429c2740e668c ---- - lib/actions.c | 12 ++++++++ - lib/ovn-l7.h | 2 +- - northd/ovn-northd.c | 7 ++++- - ovn-nb.xml | 18 ++++++++---- - ovn-sb.ovsschema | 7 +++-- - ovn-sb.xml | 13 +++++++++ - tests/ovn.at | 68 +++++++++++++++++++++++++++++++++++++++++++++ - tests/test-ovn.c | 2 +- - 8 files changed, 117 insertions(+), 12 deletions(-) - -diff --git a/lib/actions.c b/lib/actions.c -index 6d0d687b3..616c93e8a 100644 ---- a/lib/actions.c -+++ b/lib/actions.c -@@ -2083,6 +2083,10 @@ parse_gen_opt(struct action_context *ctx, struct ovnact_gen_option *o, - return; - } - -+ if (!strcmp(o->option->type, "host_id")) { -+ return; -+ } -+ - if (!strcmp(o->option->type, "str")) { - if (o->value.type != EXPR_C_STRING) { - lexer_error(ctx->lexer, "%s option %s requires string value.", -@@ -2410,6 +2414,14 @@ encode_put_dhcpv4_option(const struct ovnact_gen_option *o, - } else if (!strcmp(o->option->type, "str")) { - opt_header[1] = strlen(c->string); - ofpbuf_put(ofpacts, c->string, opt_header[1]); -+ } else if (!strcmp(o->option->type, "host_id")) { -+ if (o->value.type == EXPR_C_STRING) { -+ opt_header[1] = strlen(c->string); -+ ofpbuf_put(ofpacts, c->string, opt_header[1]); -+ } else { -+ opt_header[1] = sizeof(ovs_be32); -+ ofpbuf_put(ofpacts, &c->value.ipv4, sizeof(ovs_be32)); -+ } - } - } - -diff --git a/lib/ovn-l7.h b/lib/ovn-l7.h -index d5c6feaeb..22a2153de 100644 ---- a/lib/ovn-l7.h -+++ b/lib/ovn-l7.h -@@ -57,7 +57,7 @@ struct gen_opts_map { - #define DHCP_OPT_NIS_SERVER DHCP_OPTION("nis_server", 41, "ipv4") - #define DHCP_OPT_NTP_SERVER DHCP_OPTION("ntp_server", 42, "ipv4") - #define DHCP_OPT_SERVER_ID DHCP_OPTION("server_id", 54, "ipv4") --#define DHCP_OPT_TFTP_SERVER DHCP_OPTION("tftp_server", 66, "ipv4") -+#define DHCP_OPT_TFTP_SERVER DHCP_OPTION("tftp_server", 66, "host_id") - - #define DHCP_OPT_CLASSLESS_STATIC_ROUTE \ - DHCP_OPTION("classless_static_route", 121, "static_routes") -diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c -index 6858bf8fd..14be87435 100644 ---- a/northd/ovn-northd.c -+++ b/northd/ovn-northd.c -@@ -11785,7 +11785,12 @@ check_and_add_supported_dhcp_opts_to_sb_db(struct northd_context *ctx) - struct gen_opts_map *dhcp_opt = - dhcp_opts_find(&dhcp_opts_to_add, opt_row->name); - if (dhcp_opt) { -- hmap_remove(&dhcp_opts_to_add, &dhcp_opt->hmap_node); -+ if (!strcmp(dhcp_opt->type, opt_row->type) && -+ dhcp_opt->code == opt_row->code) { -+ hmap_remove(&dhcp_opts_to_add, &dhcp_opt->hmap_node); -+ } else { -+ sbrec_dhcp_options_delete(opt_row); -+ } - } else { - sbrec_dhcp_options_delete(opt_row); - } -diff --git a/ovn-nb.xml b/ovn-nb.xml -index e947c440d..8d04d3d3b 100644 ---- a/ovn-nb.xml -+++ b/ovn-nb.xml -@@ -2830,12 +2830,6 @@ -

- - -- --

-- The DHCPv4 option code for this option is 66. --

--
-- - -

- The DHCPv4 option code for this option is 121. -@@ -2984,6 +2978,18 @@ -

-
- -+ -+ -+

-+ These options accept either an IPv4 address or a string value. -+

-+ -+ -+

-+ The DHCPv4 option code for this option is 66. -+

-+
-+
- - - -diff --git a/ovn-sb.ovsschema b/ovn-sb.ovsschema -index c196ddaf3..2ec729b77 100644 ---- a/ovn-sb.ovsschema -+++ b/ovn-sb.ovsschema -@@ -1,7 +1,7 @@ - { - "name": "OVN_Southbound", -- "version": "2.8.0", -- "cksum": "1643994484 21853", -+ "version": "2.8.1", -+ "cksum": "236203406 21905", - "tables": { - "SB_Global": { - "columns": { -@@ -217,7 +217,8 @@ - "type": {"key": { - "type": "string", - "enum": ["set", ["bool", "uint8", "uint16", "uint32", -- "ipv4", "static_routes", "str"]]}}}}, -+ "ipv4", "static_routes", "str", -+ "host_id"]]}}}}, - "isRoot": true}, - "DHCPv6_Options": { - "columns": { -diff --git a/ovn-sb.xml b/ovn-sb.xml -index 0641e4942..2edafd48f 100644 ---- a/ovn-sb.xml -+++ b/ovn-sb.xml -@@ -3274,6 +3274,19 @@ tcp.flags = RST; - Example. "name=host_name", "code=12", "type=str". -

- -+ -+
value: host_id
-+
-+

-+ This indicates that the value of the DHCP option is a host_id. -+ It can either be a host_name or an IP address. -+

-+ -+

-+ Example. "name=tftp_server", "code=66", "type=host_id". -+

-+
-+ - - - -diff --git a/tests/ovn.at b/tests/ovn.at -index f6adbb7a3..4e98790af 100644 ---- a/tests/ovn.at -+++ b/tests/ovn.at -@@ -1249,6 +1249,12 @@ reg2[5] = put_dhcp_opts(offerip=10.0.0.4,router=10.0.0.1,netmask=255.255.254.0,m - reg0[15] = put_dhcp_opts(offerip=10.0.0.4,router=10.0.0.1,netmask=255.255.255.0,mtu=1400,ip_forward_enable=1,default_ttl=121,dns_server={8.8.8.8,7.7.7.7},classless_static_route={30.0.0.0/24,10.0.0.4,40.0.0.0/16,10.0.0.6,0.0.0.0/0,10.0.0.1},ethernet_encap=1,router_discovery=0,tftp_server_address={10.0.0.4,10.0.0.5},arp_cache_timeout=10,tcp_keepalive_interval=10); - formats as reg0[15] = put_dhcp_opts(offerip = 10.0.0.4, router = 10.0.0.1, netmask = 255.255.255.0, mtu = 1400, ip_forward_enable = 1, default_ttl = 121, dns_server = {8.8.8.8, 7.7.7.7}, classless_static_route = {30.0.0.0/24, 10.0.0.4, 40.0.0.0/16, 10.0.0.6, 0.0.0.0/0, 10.0.0.1}, ethernet_encap = 1, router_discovery = 0, tftp_server_address = {10.0.0.4, 10.0.0.5}, arp_cache_timeout = 10, tcp_keepalive_interval = 10); - encodes as controller(userdata=00.00.00.02.00.00.00.00.00.01.de.10.00.00.00.6f.0a.00.00.04.03.04.0a.00.00.01.01.04.ff.ff.ff.00.1a.02.05.78.13.01.01.17.01.79.06.08.08.08.08.08.07.07.07.07.79.14.18.1e.00.00.0a.00.00.04.10.28.00.0a.00.00.06.00.0a.00.00.01.24.01.01.1f.01.00.96.08.0a.00.00.04.0a.00.00.05.23.04.00.00.00.0a.26.04.00.00.00.0a,pause) -+reg0[15] = put_dhcp_opts(offerip=10.0.0.4,router=10.0.0.1,netmask=255.255.255.0,mtu=1400,ip_forward_enable=1,default_ttl=121,dns_server={8.8.8.8,7.7.7.7},classless_static_route={30.0.0.0/24,10.0.0.4,40.0.0.0/16,10.0.0.6,0.0.0.0/0,10.0.0.1},ethernet_encap=1,router_discovery=0,tftp_server=10.0.0.10); -+ formats as reg0[15] = put_dhcp_opts(offerip = 10.0.0.4, router = 10.0.0.1, netmask = 255.255.255.0, mtu = 1400, ip_forward_enable = 1, default_ttl = 121, dns_server = {8.8.8.8, 7.7.7.7}, classless_static_route = {30.0.0.0/24, 10.0.0.4, 40.0.0.0/16, 10.0.0.6, 0.0.0.0/0, 10.0.0.1}, ethernet_encap = 1, router_discovery = 0, tftp_server = 10.0.0.10); -+ encodes as controller(userdata=00.00.00.02.00.00.00.00.00.01.de.10.00.00.00.6f.0a.00.00.04.03.04.0a.00.00.01.01.04.ff.ff.ff.00.1a.02.05.78.13.01.01.17.01.79.06.08.08.08.08.08.07.07.07.07.79.14.18.1e.00.00.0a.00.00.04.10.28.00.0a.00.00.06.00.0a.00.00.01.24.01.01.1f.01.00.42.04.0a.00.00.0a,pause) -+reg0[15] = put_dhcp_opts(offerip=10.0.0.4,router=10.0.0.1,netmask=255.255.255.0,mtu=1400,ip_forward_enable=1,default_ttl=121,dns_server={8.8.8.8,7.7.7.7},classless_static_route={30.0.0.0/24,10.0.0.4,40.0.0.0/16,10.0.0.6,0.0.0.0/0,10.0.0.1},ethernet_encap=1,router_discovery=0,tftp_server="tftp_server_test"); -+ formats as reg0[15] = put_dhcp_opts(offerip = 10.0.0.4, router = 10.0.0.1, netmask = 255.255.255.0, mtu = 1400, ip_forward_enable = 1, default_ttl = 121, dns_server = {8.8.8.8, 7.7.7.7}, classless_static_route = {30.0.0.0/24, 10.0.0.4, 40.0.0.0/16, 10.0.0.6, 0.0.0.0/0, 10.0.0.1}, ethernet_encap = 1, router_discovery = 0, tftp_server = "tftp_server_test"); -+ encodes as controller(userdata=00.00.00.02.00.00.00.00.00.01.de.10.00.00.00.6f.0a.00.00.04.03.04.0a.00.00.01.01.04.ff.ff.ff.00.1a.02.05.78.13.01.01.17.01.79.06.08.08.08.08.08.07.07.07.07.79.14.18.1e.00.00.0a.00.00.04.10.28.00.0a.00.00.06.00.0a.00.00.01.24.01.01.1f.01.00.42.10.74.66.74.70.5f.73.65.72.76.65.72.5f.74.65.73.74,pause) - - reg1[0..1] = put_dhcp_opts(offerip = 1.2.3.4, router = 10.0.0.1); - Cannot use 2-bit field reg1[0..1] where 1-bit field is required. -@@ -5624,6 +5630,68 @@ AT_CHECK([cat 1.packets | cut -c -48], [0], [expout]) - cat 1.expected | cut -c 53- > expout - AT_CHECK([cat 1.packets | cut -c 53-], [0], [expout]) - -+reset_pcap_file hv1-vif1 hv1/vif1 -+reset_pcap_file hv1-vif2 hv1/vif2 -+rm -f 1.expected -+rm -f 2.expected -+ -+# Set tftp server option (IPv4 address) for ls1 -+echo "------ Set tftp server (IPv4 address) --------" -+ovn-nbctl dhcp-options-set-options $d1 server_id=10.0.0.1 \ -+server_mac=ff:10:00:00:00:01 lease_time=3600 router=10.0.0.1 \ -+tftp_server=10.10.10.10 -+echo "----------------------------------------------" -+ -+# Send DHCPREQUEST in the SELECTING/INIT-REBOOT state with the offered IP -+# address in the Requested IP Address option. -+offer_ip=`ip_to_hex 10 0 0 6` -+server_ip=`ip_to_hex 10 0 0 1` -+ciaddr=`ip_to_hex 0 0 0 0` -+request_ip=$offer_ip -+expected_dhcp_opts=330400000e100104ffffff0003040a00000136040a00000142040a0a0a0a -+test_dhcp 2 f00000000002 03 0 $ciaddr $offer_ip $request_ip 0 ff1000000001 $server_ip 05 $expected_dhcp_opts -+ -+# NXT_RESUMEs should be 10. -+OVS_WAIT_UNTIL([test 10 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`]) -+ -+$PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > 2.packets -+cat 2.expected | cut -c -48 > expout -+AT_CHECK([cat 2.packets | cut -c -48], [0], [expout]) -+# Skipping the IPv4 checksum. -+cat 2.expected | cut -c 53- > expout -+AT_CHECK([cat 2.packets | cut -c 53-], [0], [expout]) -+ -+reset_pcap_file hv1-vif1 hv1/vif1 -+reset_pcap_file hv1-vif2 hv1/vif2 -+rm -f 1.expected -+rm -f 2.expected -+ -+# Set tftp server option (Hostname) for ls1 -+echo "------ Set tftp server (hostname) --------" -+ovn-nbctl dhcp-options-set-options $d1 server_id=10.0.0.1 \ -+server_mac=ff:10:00:00:00:01 lease_time=3600 router=10.0.0.1 \ -+tftp_server=\"test_tftp_server\" -+echo "------------------------------------------" -+ -+# Send DHCPREQUEST in the SELECTING/INIT-REBOOT state with the offered IP -+# address in the Requested IP Address option. -+offer_ip=`ip_to_hex 10 0 0 6` -+server_ip=`ip_to_hex 10 0 0 1` -+ciaddr=`ip_to_hex 0 0 0 0` -+request_ip=$offer_ip -+expected_dhcp_opts=330400000e100104ffffff0003040a00000136040a0000014210746573745f746674705f736572766572 -+test_dhcp 2 f00000000002 03 0 $ciaddr $offer_ip $request_ip 0 ff1000000001 $server_ip 05 $expected_dhcp_opts -+ -+# NXT_RESUMEs should be 11. -+OVS_WAIT_UNTIL([test 11 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`]) -+ -+$PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > 2.packets -+cat 2.expected | cut -c -48 > expout -+AT_CHECK([cat 2.packets | cut -c -48], [0], [expout]) -+# Skipping the IPv4 checksum. -+cat 2.expected | cut -c 53- > expout -+AT_CHECK([cat 2.packets | cut -c 53-], [0], [expout]) -+ - OVN_CLEANUP([hv1]) - - AT_CLEANUP -diff --git a/tests/test-ovn.c b/tests/test-ovn.c -index 8f1bb7e01..29d343b60 100644 ---- a/tests/test-ovn.c -+++ b/tests/test-ovn.c -@@ -175,7 +175,7 @@ create_gen_opts(struct hmap *dhcp_opts, struct hmap *dhcpv6_opts, - dhcp_opt_add(dhcp_opts, "nis_server", 41, "ipv4"); - dhcp_opt_add(dhcp_opts, "ntp_server", 42, "ipv4"); - dhcp_opt_add(dhcp_opts, "server_id", 54, "ipv4"); -- dhcp_opt_add(dhcp_opts, "tftp_server", 66, "ipv4"); -+ dhcp_opt_add(dhcp_opts, "tftp_server", 66, "host_id"); - dhcp_opt_add(dhcp_opts, "classless_static_route", 121, "static_routes"); - dhcp_opt_add(dhcp_opts, "ip_forward_enable", 19, "bool"); - dhcp_opt_add(dhcp_opts, "router_discovery", 31, "bool"); --- -2.26.2 - diff --git a/SOURCES/0001-I-P-engine-Provide-the-option-to-store-client-data-i.patch b/SOURCES/0001-I-P-engine-Provide-the-option-to-store-client-data-i.patch deleted file mode 100644 index a4614fa..0000000 --- a/SOURCES/0001-I-P-engine-Provide-the-option-to-store-client-data-i.patch +++ /dev/null @@ -1,33 +0,0 @@ -From 22ae217041eee8b7d655cc489797ac88432495d6 Mon Sep 17 00:00:00 2001 -From: Numan Siddique -Date: Mon, 24 Aug 2020 16:42:48 +0530 -Subject: [PATCH 1/2] I-P engine: Provide the option to store client data in - engine ctx. - -There can be some client specific data which could change from one engine run -to another. Adding a 'void *' data in 'struct engine_ctx' will be useful. -One such usecase is to provide a config option in ovn-controller to turn on or -off logical flow expr caching. And this config knob can be stored in the engine_ctx. -An upcoming patch will make use of this. - -Acked-by: Mark Michelson -Signed-off-by: Numan Siddique ---- - lib/inc-proc-eng.h | 1 + - 1 file changed, 1 insertion(+) - -diff --git a/lib/inc-proc-eng.h b/lib/inc-proc-eng.h -index e25bcb29c..80de75029 100644 ---- a/lib/inc-proc-eng.h -+++ b/lib/inc-proc-eng.h -@@ -66,6 +66,7 @@ - struct engine_context { - struct ovsdb_idl_txn *ovs_idl_txn; - struct ovsdb_idl_txn *ovnsb_idl_txn; -+ void *client_ctx; - }; - - /* Arguments to be passed to the engine at engine_init(). */ --- -2.26.2 - diff --git a/SOURCES/0001-Introduce-DHCPDECLINE-msg-support-to-OVN-DHCP-server.patch b/SOURCES/0001-Introduce-DHCPDECLINE-msg-support-to-OVN-DHCP-server.patch deleted file mode 100644 index 8607655..0000000 --- a/SOURCES/0001-Introduce-DHCPDECLINE-msg-support-to-OVN-DHCP-server.patch +++ /dev/null @@ -1,74 +0,0 @@ -From 9ad199184959deac21b3dbf0efbbb3d23f6baed1 Mon Sep 17 00:00:00 2001 -Message-Id: <9ad199184959deac21b3dbf0efbbb3d23f6baed1.1599568829.git.lorenzo.bianconi@redhat.com> -From: Lorenzo Bianconi -Date: Wed, 26 Aug 2020 12:15:46 +0200 -Subject: [PATCH] Introduce DHCPDECLINE msg support to OVN DHCP server - -According to the RFC2131 (https://tools.ietf.org/html/rfc2131), if the -server server receives a DHCPDECLINE message, the client has discovered -through some other means that the suggested network address is already -in use. The server SHOULD notify the local system administrator of a -possible configuration problem. - -Signed-off-by: Lorenzo Bianconi -Signed-off-by: Numan Siddique ---- - controller/pinctrl.c | 6 ++++++ - lib/ovn-l7.h | 1 + - tests/ovn.at | 13 +++++++++++++ - 3 files changed, 20 insertions(+) - ---- a/controller/pinctrl.c -+++ b/controller/pinctrl.c -@@ -1889,6 +1889,12 @@ pinctrl_handle_put_dhcp_opts( - - break; - } -+ case OVN_DHCP_MSG_DECLINE: -+ if (request_ip == *offer_ip) { -+ VLOG_INFO("DHCPDECLINE from "ETH_ADDR_FMT ", "IP_FMT" duplicated", -+ ETH_ADDR_ARGS(in_flow->dl_src), IP_ARGS(*offer_ip)); -+ } -+ goto exit; - default: { - static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); - VLOG_WARN_RL(&rl, "Invalid DHCP message type: %d", *in_dhcp_msg_type); ---- a/lib/ovn-l7.h -+++ b/lib/ovn-l7.h -@@ -180,6 +180,7 @@ struct dhcp_opt6_header { - }; - - /* These are not defined in ovs/lib/dhcp.h, hence defining here. */ -+#define OVN_DHCP_MSG_DECLINE 4 - #define OVN_DHCP_MSG_RELEASE 7 - #define OVN_DHCP_MSG_INFORM 8 - ---- a/tests/ovn.at -+++ b/tests/ovn.at -@@ -5332,6 +5332,10 @@ test_dhcp() { - reply_dst_ip=${offer_ip} - fi - -+ if test "$dhcp_type" == "04"; then -+ ciaddr=$offer_ip -+ fi -+ - local request=ffffffffffff${src_mac}08004510${ip_len}0000000080110000${src_ip}${dst_ip} - # udp header and dhcp header - request=${request}00440043${udp_len}0000 -@@ -5897,6 +5901,15 @@ AT_CHECK([cat 2.packets | cut -c -48], [ - cat 2.expected | cut -c 53- > expout - AT_CHECK([cat 2.packets | cut -c 53-], [0], [expout]) - -+# test DHCPDECLINE -+offer_ip=`ip_to_hex 10 0 0 4` -+server_ip=`ip_to_hex 10 0 0 1` -+ciaddr=`ip_to_hex 0 0 0 0` -+request_ip=0 -+expected_dhcp_opts="" -+test_dhcp 1 f00000000001 04 0 $ciaddr $offer_ip $request_ip 0 ff1000000001 $server_ip 02 $expected_dhcp_opts -+AT_CHECK([fgrep -iq 'DHCPDECLINE from f0:00:00:00:00:01, 10.0.0.4 duplicated' hv1/ovn-controller.log], [0], []) -+ - OVN_CLEANUP([hv1]) - - AT_CLEANUP diff --git a/SOURCES/0001-Revert-ovsdb-idl-Avoid-sending-redundant-conditional.patch b/SOURCES/0001-Revert-ovsdb-idl-Avoid-sending-redundant-conditional.patch index 8cb059a..9f9f93d 100644 --- a/SOURCES/0001-Revert-ovsdb-idl-Avoid-sending-redundant-conditional.patch +++ b/SOURCES/0001-Revert-ovsdb-idl-Avoid-sending-redundant-conditional.patch @@ -1,7 +1,7 @@ -From 7d3fe4b24896304bc1d832f95a425fa62c48f7f8 Mon Sep 17 00:00:00 2001 +From e8922fefa62184cdd0ff808f4e52de74e8d9bbe5 Mon Sep 17 00:00:00 2001 From: Dumitru Ceara Date: Wed, 25 Mar 2020 21:15:23 +0100 -Subject: [PATCH] Revert "ovsdb-idl: Avoid sending redundant conditional +Subject: [PATCH 1/4] Revert "ovsdb-idl: Avoid sending redundant conditional monitoring updates" This reverts commit 5351980b047f4dd40be7a59a1e4b910df21eca0a. @@ -43,7 +43,7 @@ Change-Id: Iaa24bc949d648e8fa29abea1fe8fb5878ba45864 1 file changed, 2 deletions(-) diff --git a/openvswitch-2.13.0/lib/ovsdb-idl.c b/openvswitch-2.13.0/lib/ovsdb-idl.c -index 190143f..1535ad7 100644 +index 190143f36..1535ad7b5 100644 --- a/openvswitch-2.13.0/lib/ovsdb-idl.c +++ b/openvswitch-2.13.0/lib/ovsdb-idl.c @@ -610,7 +610,6 @@ ovsdb_idl_db_clear(struct ovsdb_idl_db *db) @@ -63,5 +63,5 @@ index 190143f..1535ad7 100644 ovsdb_idl_db_track_clear(db); -- -1.8.3.1 +2.26.2 diff --git a/SOURCES/0001-chassis-Fix-the-way-encaps-are-updated-for-a-chassis.patch b/SOURCES/0001-chassis-Fix-the-way-encaps-are-updated-for-a-chassis.patch deleted file mode 100644 index ee4a429..0000000 --- a/SOURCES/0001-chassis-Fix-the-way-encaps-are-updated-for-a-chassis.patch +++ /dev/null @@ -1,175 +0,0 @@ -From 536d6aa32497ab17e12767446f294fc8467cfc7c Mon Sep 17 00:00:00 2001 -From: Dumitru Ceara -Date: Thu, 3 Sep 2020 17:03:45 +0200 -Subject: [PATCH 1/2] chassis: Fix the way encaps are updated for a chassis - record. - -ovn-controller always stores the last configured system-id/chassis-id in -memory regardless if the connection to the SB is up or down. This is OK -as long as the change can be committed successfully when the SB DB -connection comes back up. - -Without this change, if the chassis-id changes while the SB connection is -down, ovn-controller will fail to create the new record but nevertheless -update its in-memory chassis-id. When the SB connection is restored -ovn-controller tries to find the record corresponding to the chassis-id -it stored in memory. This fails causing ovn-controller to try to insert -a new record. But at this point a constraint violation is hit in the SB -because the Encap records of the "stale" chassis still exist in the DB, -along with the old chassis record. - -This commit changes the way we search for a "stale" chassis record in the -SB to cover the above mentioned case. Also, in such cases there's no need -to recreate the Encaps, it's sufficient to update the chassis_name field. - -Fixes: 5344f24ecb1a ("ovn-controller: Refactor chassis.c to abstract the string parsing") -Signed-off-by: Dumitru Ceara -Signed-off-by: Numan Siddique - -(cherry-picked from master commit 94a32fca2d2b825fece0ef5b1873459bd9857dd3) - -(cherry picked from upstream commit 0716c4f4cf2d97f03c1f2e5099fece92f3183d43) - -Change-Id: I20f0c61fddffba599b16f43d15b110414a6a6e6b ---- - controller/chassis.c | 60 +++++++++++++++++++++++++++++++------------------ - tests/ovn-controller.at | 17 ++++++++++++++ - 2 files changed, 55 insertions(+), 22 deletions(-) - -diff --git a/controller/chassis.c b/controller/chassis.c -index 6ac591e..773d966 100644 ---- a/controller/chassis.c -+++ b/controller/chassis.c -@@ -397,10 +397,7 @@ chassis_tunnels_changed(const struct sset *encap_type_set, - { - size_t encap_type_count = 0; - -- for (int i = 0; i < chassis_rec->n_encaps; i++) { -- if (strcmp(chassis_rec->name, chassis_rec->encaps[i]->chassis_name)) { -- return true; -- } -+ for (size_t i = 0; i < chassis_rec->n_encaps; i++) { - - if (!sset_contains(encap_type_set, chassis_rec->encaps[i]->type)) { - return true; -@@ -473,6 +470,19 @@ chassis_build_encaps(struct ovsdb_idl_txn *ovnsb_idl_txn, - } - - /* -+ * Updates encaps for a given chassis. This can happen when the chassis -+ * name has changed. Also, the only thing we support updating is the -+ * chassis_name. For other changes the encaps will be recreated. -+ */ -+static void -+chassis_update_encaps(const struct sbrec_chassis *chassis) -+{ -+ for (size_t i = 0; i < chassis->n_encaps; i++) { -+ sbrec_encap_set_chassis_name(chassis->encaps[i], chassis->name); -+ } -+} -+ -+/* - * Returns a pointer to a chassis record from 'chassis_table' that - * matches at least one tunnel config. - */ -@@ -503,9 +513,10 @@ chassis_get_stale_record(const struct sbrec_chassis_table *chassis_table, - /* If this is a chassis config update after we initialized the record once - * then we should always be able to find it with the ID we saved in - * chassis_state. -- * Otherwise (i.e., first time we create the record) then we check if there's -- * a stale record from a previous controller run that didn't end gracefully -- * and reuse it. If not then we create a new record. -+ * Otherwise (i.e., first time we create the record or if the system-id -+ * changed) then we check if there's a stale record from a previous -+ * controller run that didn't end gracefully and reuse it. If not then we -+ * create a new record. - * - * Sets '*chassis_rec' to point to the local chassis record. - * Returns true if this record was already in the database, false if it was -@@ -519,28 +530,32 @@ chassis_get_record(struct ovsdb_idl_txn *ovnsb_idl_txn, - const char *chassis_id, - const struct sbrec_chassis **chassis_rec) - { -+ const struct sbrec_chassis *chassis = NULL; -+ - if (chassis_info_id_inited(&chassis_state)) { -- *chassis_rec = chassis_lookup_by_name(sbrec_chassis_by_name, -- chassis_info_id(&chassis_state)); -- if (!(*chassis_rec)) { -- VLOG_DBG("Could not find Chassis, will create it" -- ": stored (%s) ovs (%s)", -+ chassis = chassis_lookup_by_name(sbrec_chassis_by_name, -+ chassis_info_id(&chassis_state)); -+ if (!chassis) { -+ VLOG_DBG("Could not find Chassis, will check if the id changed: " -+ "stored (%s) ovs (%s)", - chassis_info_id(&chassis_state), chassis_id); -- if (ovnsb_idl_txn) { -- /* Recreate the chassis record. */ -- *chassis_rec = sbrec_chassis_insert(ovnsb_idl_txn); -- return false; -- } - } -- } else { -- *chassis_rec = -- chassis_get_stale_record(chassis_table, ovs_cfg, chassis_id); -+ } - -- if (!(*chassis_rec) && ovnsb_idl_txn) { -+ if (!chassis) { -+ chassis = chassis_get_stale_record(chassis_table, ovs_cfg, chassis_id); -+ } -+ -+ if (!chassis) { -+ /* Recreate the chassis record. */ -+ VLOG_DBG("Could not find Chassis, will create it: %s", chassis_id); -+ if (ovnsb_idl_txn) { - *chassis_rec = sbrec_chassis_insert(ovnsb_idl_txn); -- return false; - } -+ return false; - } -+ -+ *chassis_rec = chassis; - return true; - } - -@@ -602,6 +617,7 @@ chassis_update(const struct sbrec_chassis *chassis_rec, - &ovs_cfg->encap_ip_set, ovs_cfg->encap_csum, - chassis_rec); - if (!tunnels_changed) { -+ chassis_update_encaps(chassis_rec); - return updated; - } - -diff --git a/tests/ovn-controller.at b/tests/ovn-controller.at -index 1b96934..f2faf1f 100644 ---- a/tests/ovn-controller.at -+++ b/tests/ovn-controller.at -@@ -196,6 +196,23 @@ OVS_WAIT_UNTIL([ - test "${sysid}" = "${chassis_id}" - ]) - -+# Simulate system-id changing while ovn-controller is disconnected from the -+# SB. -+valid_remote=$(ovs-vsctl get Open_vSwitch . external_ids:ovn-remote) -+invalid_remote=tcp:0.0.0.0:4242 -+ovs-vsctl set Open_vSwitch . external_ids:ovn-remote=${invalid_remote} -+expected_state="not connected" -+OVS_WAIT_UNTIL([ -+ test "${expected_state}" = "$(ovn-appctl -t ovn-controller connection-status)" -+]) -+sysid=${sysid}-bar -+ovs-vsctl set Open_vSwitch . external-ids:system-id="${sysid}" -+ovs-vsctl set Open_vSwitch . external_ids:ovn-remote=${valid_remote} -+OVS_WAIT_UNTIL([ -+ chassis_id=$(ovn-sbctl get Chassis "${sysid}" name) -+ test "${sysid}" = "${chassis_id}" -+]) -+ - # Gracefully terminate daemons - OVN_CLEANUP_SBOX([hv]) - OVN_CLEANUP_VSWITCH([main]) --- -1.8.3.1 - diff --git a/SOURCES/0001-lex-Allow-unmasked-bits-in-value-mask-tokens.patch b/SOURCES/0001-lex-Allow-unmasked-bits-in-value-mask-tokens.patch deleted file mode 100644 index e1d5078..0000000 --- a/SOURCES/0001-lex-Allow-unmasked-bits-in-value-mask-tokens.patch +++ /dev/null @@ -1,95 +0,0 @@ -From 714a097ba82ad53b90cfff921ea3749cd1130f3e Mon Sep 17 00:00:00 2001 -From: Dumitru Ceara -Date: Tue, 23 Jun 2020 10:17:50 +0200 -Subject: [PATCH] lex: Allow unmasked bits in value/mask tokens. - -It's quite restrictive to not accept ACLs/policies that match on a CIDR -that has non-zero host bits. Right now this generates a lexer error that -can only be detected in the logs. - -There's no real harm in automatically zero-ing the unmasked bits. - -Reported-at: https://bugzilla.redhat.com/1812820 -Reported-by: Ying Xu -Signed-off-by: Dumitru Ceara -Acked-by: Mark Michelson -Signed-off-by: Numan Siddique -(cherry picked from upstream commit 2104f67aacd62f62a31f4e23a6720aeeaa751154) - -Change-Id: I90c57fe51170d63fcd08d1a57d6d9555755a43be ---- - lib/lex.c | 10 ++-------- - tests/ovn.at | 11 +++++++---- - 2 files changed, 9 insertions(+), 12 deletions(-) - -diff --git a/lib/lex.c b/lib/lex.c -index 94f6c77..4d92199 100644 ---- a/lib/lex.c -+++ b/lib/lex.c -@@ -485,16 +485,10 @@ lex_parse_mask(const char *p, struct lex_token *token) - return p; - } - -- /* Check invariant that a 1-bit in the value corresponds to a 1-bit in the -+ /* Apply invariant that a 1-bit in the value corresponds to a 1-bit in the - * mask. */ - for (int i = 0; i < ARRAY_SIZE(token->mask.be32); i++) { -- ovs_be32 v = token->value.be32[i]; -- ovs_be32 m = token->mask.be32[i]; -- -- if (v & ~m) { -- lex_error(token, "Value contains unmasked 1-bits."); -- break; -- } -+ token->value.be32[i] &= token->mask.be32[i]; - } - - /* Done! */ -diff --git a/tests/ovn.at b/tests/ovn.at -index cf521af..e7e0439 100644 ---- a/tests/ovn.at -+++ b/tests/ovn.at -@@ -79,7 +79,7 @@ a/b => a error("`/' is only valid as part of `//' or `/*'.") b - - 0/0 - 0/1 --1/0 => error("Value contains unmasked 1-bits.") -+1/0 => 0/0 - 1/1 - 128/384 - 1/3 -@@ -99,7 +99,7 @@ a/b => a error("`/' is only valid as part of `//' or `/*'.") b - 0X => error("Hex digits expected following 0X.") - 0x0/0x0 => 0/0 - 0x0/0x1 => 0/0x1 --0x1/0x0 => error("Value contains unmasked 1-bits.") -+0x1/0x0 => 0/0 - 0xffff/0x1ffff - 0x. => error("Invalid syntax in hexadecimal constant.") - -@@ -109,9 +109,12 @@ a/b => a error("`/' is only valid as part of `//' or `/*'.") b - 192.168.0.0/255.255.0.0 => 192.168.0.0/16 - 192.168.0.0/255.255.255.0 => 192.168.0.0/24 - 192.168.0.0/255.255.0.255 --192.168.0.0/255.0.0.0 => error("Value contains unmasked 1-bits.") -+192.168.0.0/255.0.0.0 => 192.0.0.0/8 - 192.168.0.0/32 - 192.168.0.0/255.255.255.255 => 192.168.0.0/32 -+192.168.0.2/32 -+192.168.0.2/30 => 192.168.0.0/30 -+192.168.0.2/24 => 192.168.0.0/24 - 1.2.3.4:5 => 1.2.3.4 : 5 - - :: -@@ -135,7 +138,7 @@ FE:DC:ba:98:76:54 => fe:dc:ba:98:76:54 - 01:00:00:00:00:00/01:00:00:00:00:00 - ff:ff:ff:ff:ff:ff/ff:ff:ff:ff:ff:ff - fe:ff:ff:ff:ff:ff/ff:ff:ff:ff:ff:ff --ff:ff:ff:ff:ff:ff/fe:ff:ff:ff:ff:ff => error("Value contains unmasked 1-bits.") -+ff:ff:ff:ff:ff:ff/fe:ff:ff:ff:ff:ff => fe:ff:ff:ff:ff:ff/fe:ff:ff:ff:ff:ff - fe:x => error("Invalid numeric constant.") - 00:01:02:03:04:x => error("Invalid numeric constant.") - --- -1.8.3.1 - diff --git a/SOURCES/0001-northd-fix-empty_lb_backends-controller_event-for-IP.patch b/SOURCES/0001-northd-fix-empty_lb_backends-controller_event-for-IP.patch deleted file mode 100644 index 51d2497..0000000 --- a/SOURCES/0001-northd-fix-empty_lb_backends-controller_event-for-IP.patch +++ /dev/null @@ -1,115 +0,0 @@ -From 940fff5c75ccc1ef0d66f37ce167f89f8ae6d098 Mon Sep 17 00:00:00 2001 -Message-Id: <940fff5c75ccc1ef0d66f37ce167f89f8ae6d098.1599499391.git.lorenzo.bianconi@redhat.com> -From: Lorenzo Bianconi -Date: Fri, 4 Sep 2020 15:44:49 +0200 -Subject: [PATCH] northd: fix empty_lb_backends controller_event for IPv6 - -Introduce missing square brackets defining IPv6 empty_lb_backends -controller_event logical flows in order to properly extract vip ip -and port from the related string - -Fixes: bb9f2b9ce56c ("ovn-northd: Consider load balancer active backends in router pipeline") -Fixes: 821e1e54abcb ("OVN: use trigger_event action to report 'empty_lb_rule' events") -Acked-by: Dumitru Ceara -Signed-off-by: Lorenzo Bianconi -Signed-off-by: Numan Siddique ---- - northd/ovn-northd.c | 7 ++++--- - tests/ovn.at | 30 +++++++++++++++++++++++++++--- - 2 files changed, 31 insertions(+), 6 deletions(-) - ---- a/northd/ovn-northd.c -+++ b/northd/ovn-northd.c -@@ -4995,6 +4995,7 @@ build_empty_lb_event_flow(struct ovn_dat - return; - } - -+ bool ipv4 = (lb_vip->addr_family == AF_INET); - struct ds match = DS_EMPTY_INITIALIZER; - char *meter = "", *action; - -@@ -5003,14 +5004,14 @@ build_empty_lb_event_flow(struct ovn_dat - } - - ds_put_format(&match, "ip%s.dst == %s && %s", -- lb_vip->addr_family == AF_INET ? "4": "6", -- lb_vip->vip, lb->protocol); -+ ipv4 ? "4": "6", lb_vip->vip, lb->protocol); - - char *vip = lb_vip->vip; - if (lb_vip->vip_port) { - ds_put_format(&match, " && %s.dst == %u", lb->protocol, - lb_vip->vip_port); -- vip = xasprintf("%s:%u", lb_vip->vip, lb_vip->vip_port); -+ vip = xasprintf("%s%s%s:%u", ipv4 ? "" : "[", lb_vip->vip, -+ ipv4 ? "" : "]", lb_vip->vip_port); - } - - action = xasprintf("trigger_event(event = \"%s\", " ---- a/tests/ovn.at -+++ b/tests/ovn.at -@@ -16404,7 +16404,7 @@ ovn-nbctl create Logical_Router name=lr0 - for i in 0 1; do - idx=$((i+1)) - ovn-nbctl ls-add sw$i -- ovn-nbctl lrp-add lr0 lrp$i 00:00:00:00:ff:0$idx 192.168.$idx.254/24 -+ ovn-nbctl lrp-add lr0 lrp$i 00:00:00:00:ff:0$idx 192.168.$idx.254/24 200$idx::a/64 - ovn-nbctl \ - -- lsp-add sw$i lrp$i-attachment \ - -- set Logical_Switch_Port lrp$i-attachment type=router \ -@@ -16420,7 +16420,7 @@ for i in 1 2; do - - for j in 1 2; do - ovn-nbctl lsp-add sw0 sw0-p$i$j -- \ -- lsp-set-addresses sw0-p$i$j "00:00:00:00:00:$i$j 192.168.1.$i$j" -+ lsp-set-addresses sw0-p$i$j "00:00:00:00:00:$i$j 192.168.1.$i$j 2001::$i$j" - - ovs-vsctl -- add-port br-int vif$i$j -- \ - set interface vif$i$j \ -@@ -16433,7 +16433,7 @@ done - - as hv1 - ovn-nbctl lsp-add sw1 sw1-p0 \ -- -- lsp-set-addresses sw1-p0 "00:00:00:00:00:33 192.168.2.11" -+ -- lsp-set-addresses sw1-p0 "00:00:00:00:00:33 192.168.2.11 2002::1" - ovs-vsctl -- add-port br-int vif33 -- \ - set interface vif33 \ - external-ids:iface-id=sw1-p0 \ -@@ -16449,6 +16449,11 @@ uuid_lb0=$(ovn-nbctl --bare --columns=_u - ovn-nbctl lb-add lb1 192.168.2.100:80 "" - ovn-nbctl lr-lb-add lr0 lb1 - uuid_lb1=$(ovn-nbctl --bare --columns=_uuid find load_balancer name=lb1) -+ -+ovn-nbctl lb-add lb2 [[2001::10]]:50051 "" -+ovn-nbctl ls-lb-add sw0 lb2 -+uuid_lb2=$(ovn-nbctl --bare --columns=_uuid find load_balancer name=lb2) -+ - ovn-nbctl --wait=hv meter-add event-elb drop 100 pktps 10 - - OVN_POPULATE_ARP -@@ -16493,6 +16498,25 @@ empty_lb_backends - AT_CHECK([ovn-sbctl get controller_event $uuid event_info:vip], [0], [dnl - "192.168.2.100:80" - ]) -+ovn-sbctl destroy controller_event $uuid -+ -+packet2="inport==\"sw0-p11\" && eth.src==00:00:00:00:00:11 && eth.dst==00:00:00:00:00:21 && -+ ip6 && ip.ttl==64 && ip6.src==2001::11 && ip6.dst==2001::10 && -+ tcp && tcp.src==10000 && tcp.dst==50051" -+ -+as hv1 ovs-appctl -t ovn-controller inject-pkt "$packet2" -+ovn-sbctl list controller_event -+uuid=$(ovn-sbctl list controller_event | awk '/_uuid/{print $3}') -+ -+AT_CHECK([ovn-sbctl get controller_event $uuid event_type], [0], [dnl -+empty_lb_backends -+]) -+AT_CHECK([ovn-sbctl get controller_event $uuid event_info:vip], [0], [dnl -+"[[2001::10]]:50051" -+]) -+AT_CHECK_UNQUOTED([ovn-sbctl get controller_event $uuid event_info:load_balancer], [0], [dnl -+"$uuid_lb2" -+]) - - OVN_CLEANUP([hv1], [hv2]) - AT_CLEANUP diff --git a/SOURCES/0001-ovn-controller-Fix-incremental-processing-of-Port_Bi.patch b/SOURCES/0001-ovn-controller-Fix-incremental-processing-of-Port_Bi.patch deleted file mode 100644 index e525ef5..0000000 --- a/SOURCES/0001-ovn-controller-Fix-incremental-processing-of-Port_Bi.patch +++ /dev/null @@ -1,207 +0,0 @@ -From 499546979fcf98c8423fb18263261005f747b228 Mon Sep 17 00:00:00 2001 -From: Dumitru Ceara -Date: Mon, 31 Aug 2020 14:14:52 +0200 -Subject: [PATCH] ovn-controller: Fix incremental processing of Port_Binding - deletes. - -If a Port_Binding is deleted from the Southbound DB and the -corresponding OVS interface is also deleted from the OVS DB, and -if both updates are received and processed by ovn-controller in -the same iteration, ovn-controller should process port_binding -delete operations first. - -This commit also adds three new unixctl debug commands for -ovn-controller: -- debug/pause: pause ovn-controller processing, except unixctl commands. -- debug/resume: resume ovn-controller processing. -- debug/status: return the status of the ovn-controller processing. - -These new commands are needed by the test for this scenario as without -them we have no way of ensuring predictable results. Users should not -use these commands in production. This is also why the commands are not -documented. - -CC: Numan Siddique -Fixes: 6b0f01116bab ("ovn-controller: Handle runtime data changes in flow output engine") -Reported-by: Tim Rozet -Reported-at: https://bugzilla.redhat.com/1871961 -Signed-off-by: Dumitru Ceara -Signed-off-by: Numan Siddique -(cherry picked from upstream commit d2de07a627721ef443d8929fe04eb888c5086e98) - -Change-Id: I889235d31cf9b7fd6a9dde6e0903038c87106b13 ---- - controller/ovn-controller.c | 71 ++++++++++++++++++++++++++++++++++++++++++--- - tests/ovn.at | 38 ++++++++++++++++++++++++ - 2 files changed, 105 insertions(+), 4 deletions(-) - -diff --git a/controller/ovn-controller.c b/controller/ovn-controller.c -index 933acf6..28ca7a8 100644 ---- a/controller/ovn-controller.c -+++ b/controller/ovn-controller.c -@@ -73,6 +73,9 @@ static unixctl_cb_func extend_table_list; - static unixctl_cb_func inject_pkt; - static unixctl_cb_func engine_recompute_cmd; - static unixctl_cb_func cluster_state_reset_cmd; -+static unixctl_cb_func debug_pause_execution; -+static unixctl_cb_func debug_resume_execution; -+static unixctl_cb_func debug_status_execution; - - #define DEFAULT_BRIDGE_NAME "br-int" - #define DEFAULT_PROBE_INTERVAL_MSEC 5000 -@@ -2253,10 +2256,6 @@ main(int argc, char *argv[]) - - engine_add_input(&en_runtime_data, &en_ovs_open_vswitch, NULL); - engine_add_input(&en_runtime_data, &en_ovs_bridge, NULL); -- engine_add_input(&en_runtime_data, &en_ovs_port, -- engine_noop_handler); -- engine_add_input(&en_runtime_data, &en_ovs_interface, -- runtime_data_ovs_interface_handler); - engine_add_input(&en_runtime_data, &en_ovs_qos, NULL); - - engine_add_input(&en_runtime_data, &en_sb_chassis, NULL); -@@ -2265,6 +2264,15 @@ main(int argc, char *argv[]) - engine_add_input(&en_runtime_data, &en_sb_port_binding, - runtime_data_sb_port_binding_handler); - -+ /* The OVS interface handler for runtime_data changes MUST be executed -+ * after the sb_port_binding_handler as port_binding deletes must be -+ * processed first. -+ */ -+ engine_add_input(&en_runtime_data, &en_ovs_port, -+ engine_noop_handler); -+ engine_add_input(&en_runtime_data, &en_ovs_interface, -+ runtime_data_ovs_interface_handler); -+ - struct engine_arg engine_arg = { - .sb_idl = ovnsb_idl_loop.idl, - .ovs_idl = ovs_idl_loop.idl, -@@ -2319,6 +2327,14 @@ main(int argc, char *argv[]) - cluster_state_reset_cmd, - &reset_ovnsb_idl_min_index); - -+ bool paused = false; -+ unixctl_command_register("debug/pause", "", 0, 0, debug_pause_execution, -+ &paused); -+ unixctl_command_register("debug/resume", "", 0, 0, debug_resume_execution, -+ &paused); -+ unixctl_command_register("debug/status", "", 0, 0, debug_status_execution, -+ &paused); -+ - unsigned int ovs_cond_seqno = UINT_MAX; - unsigned int ovnsb_cond_seqno = UINT_MAX; - -@@ -2327,6 +2343,15 @@ main(int argc, char *argv[]) - restart = false; - bool sb_monitor_all = false; - while (!exiting) { -+ /* If we're paused just run the unixctl server and skip most of the -+ * processing loop. -+ */ -+ if (paused) { -+ unixctl_server_run(unixctl); -+ unixctl_server_wait(unixctl); -+ goto loop_done; -+ } -+ - engine_init_run(); - - struct ovsdb_idl_txn *ovs_idl_txn = ovsdb_idl_loop_run(&ovs_idl_loop); -@@ -2581,6 +2606,8 @@ main(int argc, char *argv[]) - - ovsdb_idl_track_clear(ovnsb_idl_loop.idl); - ovsdb_idl_track_clear(ovs_idl_loop.idl); -+ -+loop_done: - poll_block(); - if (should_service_stop()) { - exiting = true; -@@ -2834,3 +2861,39 @@ cluster_state_reset_cmd(struct unixctl_conn *conn, int argc OVS_UNUSED, - poll_immediate_wake(); - unixctl_command_reply(conn, NULL); - } -+ -+static void -+debug_pause_execution(struct unixctl_conn *conn, int argc OVS_UNUSED, -+ const char *argv[] OVS_UNUSED, void *paused_) -+{ -+ bool *paused = paused_; -+ -+ VLOG_INFO("User triggered execution pause."); -+ *paused = true; -+ unixctl_command_reply(conn, NULL); -+} -+ -+static void -+debug_resume_execution(struct unixctl_conn *conn, int argc OVS_UNUSED, -+ const char *argv[] OVS_UNUSED, void *paused_) -+{ -+ bool *paused = paused_; -+ -+ VLOG_INFO("User triggered execution resume."); -+ *paused = false; -+ poll_immediate_wake(); -+ unixctl_command_reply(conn, NULL); -+} -+ -+static void -+debug_status_execution(struct unixctl_conn *conn, int argc OVS_UNUSED, -+ const char *argv[] OVS_UNUSED, void *paused_) -+{ -+ bool *paused = paused_; -+ -+ if (*paused) { -+ unixctl_command_reply(conn, "paused"); -+ } else { -+ unixctl_command_reply(conn, "running"); -+ } -+} -diff --git a/tests/ovn.at b/tests/ovn.at -index 1216bc5..cf521af 100644 ---- a/tests/ovn.at -+++ b/tests/ovn.at -@@ -21361,3 +21361,41 @@ AT_CHECK([ovn-sbctl find mac ip=10.0.0.2 mac='"00:00:00:00:03:02"' logical_port= - OVN_CLEANUP([hv1],[hv2]) - - AT_CLEANUP -+ -+AT_SETUP([ovn -- Delete Port_Binding and OVS port Incremental Processing]) -+ovn_start -+ -+net_add n1 -+sim_add hv1 -+as hv1 -+ovs-vsctl add-br br-phys -+ovn_attach n1 br-phys 192.168.0.10 -+ -+ovn-nbctl ls-add ls -+ovn-nbctl lsp-add ls lsp -+ -+as hv1 ovs-vsctl \ -+ -- add-port br-int vif1 \ -+ -- set Interface vif1 external_ids:iface-id=lsp -+ -+# Wait for port to be bound. -+OVS_WAIT_UNTIL([test $(ovn-sbctl --columns _uuid --bare list chassis hv1 | wc -l) -eq 1]) -+ch=$(ovn-sbctl --columns _uuid --bare list chassis hv1) -+OVS_WAIT_UNTIL([test $(ovn-sbctl --columns chassis --bare list port_binding lsp | grep $ch -c) -eq 1]) -+ -+# Pause ovn-controller. -+as hv1 ovn-appctl -t ovn-controller debug/pause -+ -+# Delete port binding and OVS port. The updates will be processed in the same -+# loop in ovn-controller when it resumes. -+ovn-nbctl --wait=sb lsp-del lsp -+as hv1 ovs-vsctl del-port vif1 -+ -+# Resume ovn-controller. -+as hv1 ovn-appctl -t ovn-controller debug/resume -+ -+# Make sure ovn-controller runs fine. -+OVS_WAIT_UNTIL([test x$(as hv1 ovn-appctl -t ovn-controller debug/status) = "xrunning"]) -+ -+OVN_CLEANUP([hv1]) -+AT_CLEANUP --- -1.8.3.1 - diff --git a/SOURCES/0001-ovn-ctl-introduce-ovsdb-n-s-b-wrapper-options.patch b/SOURCES/0001-ovn-ctl-introduce-ovsdb-n-s-b-wrapper-options.patch deleted file mode 100644 index c2fe3b1..0000000 --- a/SOURCES/0001-ovn-ctl-introduce-ovsdb-n-s-b-wrapper-options.patch +++ /dev/null @@ -1,175 +0,0 @@ -From 1060d922a0fee3c9795eb58d5799b235ae406bc6 Mon Sep 17 00:00:00 2001 -Message-Id: <1060d922a0fee3c9795eb58d5799b235ae406bc6.1599568836.git.lorenzo.bianconi@redhat.com> -From: Lorenzo Bianconi -Date: Fri, 21 Aug 2020 15:40:13 +0200 -Subject: [PATCH] ovn-ctl: introduce ovsdb-{n, s}b-wrapper options - -ovn-ctl has the following options to run ovn-northd, ovn-controller or -ovn-ic under strace or valgrind wrappers. - - --ovn-northd-wrapper - --ovn-controller-wrapper - --ovn-ic-wrapper - -Introduce --ovsdb-nb-wrapper and --ovsdb-sb-wrapper to do the same for -ovsdb processes for ovn-{nb,sb} dbs - -Tested-by: Dumitru Ceara -Signed-off-by: Lorenzo Bianconi -Acked-by: Dumitru Ceara -Signed-off-by: Numan Siddique ---- - utilities/ovn-ctl | 18 ++++++++++----- - utilities/ovn-ctl.8.xml | 2 ++ - utilities/ovn-lib.in | 49 ++++++++++++++++++++++++----------------- - 3 files changed, 43 insertions(+), 26 deletions(-) - ---- a/utilities/ovn-ctl -+++ b/utilities/ovn-ctl -@@ -145,7 +145,7 @@ promote_ic_sb() { - } - - start_ovsdb__() { -- local DB=$1 db=$2 schema_name=$3 table_name=$4 -+ local DB=$1 db=$2 schema_name=$3 table_name=$4 wrapper=$5 - local db_pid_file - local cluster_local_addr - local cluster_local_port -@@ -288,7 +288,7 @@ $cluster_remote_port - set "$@" --sync-from=`cat $active_conf_file` - fi - -- "$@" "$file" -+ start_wrapped_daemon "$wrapper" ovsdb-$db "" "$@" "$file" - - # Initialize the database if it's NOT joining a cluster. - if test -z "$cluster_remote_addr"; then -@@ -301,7 +301,7 @@ $cluster_remote_port - } - - start_nb_ovsdb() { -- start_ovsdb__ NB nb OVN_Northbound NB_Global -+ start_ovsdb__ NB nb OVN_Northbound NB_Global "$OVSDB_NB_WRAPPER" - } - - start_sb_ovsdb() { -@@ -313,7 +313,7 @@ start_sb_ovsdb() { - ulimit -n $MAXFD - fi - -- start_ovsdb__ SB sb OVN_Southbound SB_Global -+ start_ovsdb__ SB sb OVN_Southbound SB_Global "$OVSDB_SB_WRAPPER" - } - - start_ovsdb () { -@@ -322,11 +322,13 @@ start_ovsdb () { - } - - start_ic_nb_ovsdb() { -- start_ovsdb__ IC_NB ic_nb OVN_IC_Northbound IC_NB_Global -+ start_ovsdb__ IC_NB ic_nb OVN_IC_Northbound IC_NB_Global \ -+ "$OVSDB_NB_WRAPPER" - } - - start_ic_sb_ovsdb() { -- start_ovsdb__ IC_SB ic_sb OVN_IC_Southbound IC_SB_Global -+ start_ovsdb__ IC_SB ic_sb OVN_IC_Southbound IC_SB_Global \ -+ "$OVSDB_SB_WRAPPER" - } - - start_ic_ovsdb () { -@@ -692,6 +694,8 @@ set_defaults () { - OVN_IC_WRAPPER= - OVN_CONTROLLER_PRIORITY=-10 - OVN_CONTROLLER_WRAPPER= -+ OVSDB_NB_WRAPPER= -+ OVSDB_SB_WRAPPER= - - OVN_USER= - -@@ -908,6 +912,8 @@ Options: - --ovn-ic-sb-db-ssl-ca-cert=CERT OVN IC Southbound DB SSL CA certificate file - --ovn-user="user[:group]" pass the --user flag to the ovn daemons - --ovs-user="user[:group]" pass the --user flag to ovs daemons -+ --ovsdb-nb-wrapper=WRAPPER run with a wrapper like valgrind for debugging -+ --ovsdb-sb-wrapper=WRAPPER run with a wrapper like valgrind for debugging - -h, --help display this help message - - File location options: ---- a/utilities/ovn-ctl.8.xml -+++ b/utilities/ovn-ctl.8.xml -@@ -64,6 +64,8 @@ -

--ovn-controller-wrapper=WRAPPER

-

--ovn-ic-priority=NICE

-

--ovn-ic-wrapper=WRAPPER

-+

--ovsdb-nb-wrapper=WRAPPER

-+

--ovsdb-sb-wrapper=WRAPPER

-

--ovn-user=USER:GROUP

-

--ovs-user=USER:GROUP

-

-h | --help

---- a/utilities/ovn-lib.in -+++ b/utilities/ovn-lib.in -@@ -59,27 +59,12 @@ ovn_install_dir () { - fi - } - --start_ovn_daemon () { -- priority=$1 -- wrapper=$2 -- shift; shift -- daemon=$1 -+start_wrapped_daemon() { -+ wrapper=$1 -+ daemon=$2 -+ priority=$3 - strace="" -- -- # drop core files in a sensible place -- ovn_install_dir "$DAEMON_CWD" -- set "$@" --no-chdir -- cd "$DAEMON_CWD" -- -- # log file -- ovn_install_dir "$ovn_logdir" "750" -- set "$@" --log-file="$ovn_logdir/$daemon.log" -- -- # pidfile and monitoring -- ovn_install_dir "$ovn_rundir" -- set "$@" --pidfile="$ovn_rundir/$daemon.pid" -- set "$@" --detach -- test X"$MONITOR" = Xno || set "$@" --monitor -+ shift ; shift ; shift ; - - # wrapper - case $wrapper in -@@ -127,6 +112,30 @@ start_ovn_daemon () { - fi - } - -+start_ovn_daemon () { -+ priority=$1 -+ wrapper=$2 -+ shift; shift -+ daemon=$1 -+ -+ # drop core files in a sensible place -+ ovn_install_dir "$DAEMON_CWD" -+ set "$@" --no-chdir -+ cd "$DAEMON_CWD" -+ -+ # log file -+ ovn_install_dir "$ovn_logdir" "750" -+ set "$@" --log-file="$ovn_logdir/$daemon.log" -+ -+ # pidfile and monitoring -+ ovn_install_dir "$ovn_rundir" -+ set "$@" --pidfile="$ovn_rundir/$daemon.pid" -+ set "$@" --detach -+ test X"$MONITOR" = Xno || set "$@" --monitor -+ -+ start_wrapped_daemon "$wrapper" $daemon "$priority" "$@" -+} -+ - stop_ovn_daemon () { - if test -e "$ovn_rundir/$1.pid"; then - if pid=`cat "$ovn_rundir/$1.pid"`; then diff --git a/SOURCES/0001-ovn-nbctl-Deal-with-nb_cfg-overflows.patch b/SOURCES/0001-ovn-nbctl-Deal-with-nb_cfg-overflows.patch deleted file mode 100644 index c1a1dae..0000000 --- a/SOURCES/0001-ovn-nbctl-Deal-with-nb_cfg-overflows.patch +++ /dev/null @@ -1,38 +0,0 @@ -From 9cb2f23877fec13693f27986e4d075152f318d0f Mon Sep 17 00:00:00 2001 -From: Dumitru Ceara -Date: Fri, 28 Aug 2020 14:10:54 +0200 -Subject: [PATCH] ovn-nbctl: Deal with nb_cfg overflows. - -Reported-at: https://bugzilla.redhat.com/id=1873455 -Reported-by: Ying Xu -Signed-off-by: Dumitru Ceara -Signed-off-by: Numan Siddique - -(cherry-picked from master commit be3a60f8e6a3a01e08ae6fe760279dc4f274562a) - -(cherry picked from upstream commit 49f322806785cf1195dfa8cfb63531cd1c119e5b) - -Change-Id: Iba7df83c1d8b88a70fff3c69b985ef46db8d18c4 ---- - utilities/ovn-nbctl.c | 5 +++++ - 1 file changed, 5 insertions(+) - -diff --git a/utilities/ovn-nbctl.c b/utilities/ovn-nbctl.c -index e6d8dbe..bd66ee6 100644 ---- a/utilities/ovn-nbctl.c -+++ b/utilities/ovn-nbctl.c -@@ -6080,6 +6080,11 @@ do_nbctl(const char *args, struct ctl_command *commands, size_t n_commands, - nb = nbrec_nb_global_insert(txn); - } - -+ /* Deal with potential overflows. */ -+ if (nb->nb_cfg == LLONG_MAX) { -+ nbrec_nb_global_set_nb_cfg(nb, 0); -+ } -+ - if (wait_type != NBCTL_WAIT_NONE) { - ovsdb_idl_txn_increment(txn, &nb->header_, &nbrec_nb_global_col_nb_cfg, - force_wait); --- -1.8.3.1 - diff --git a/SOURCES/0001-ovn-nbctl-add-may-exist-if-exists-options-for-policy.patch b/SOURCES/0001-ovn-nbctl-add-may-exist-if-exists-options-for-policy.patch new file mode 100644 index 0000000..76d59a0 --- /dev/null +++ b/SOURCES/0001-ovn-nbctl-add-may-exist-if-exists-options-for-policy.patch @@ -0,0 +1,157 @@ +From d811e1027f74de0f1eee1af9af8dd3338eadb61d Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Fri, 25 Sep 2020 13:21:58 +0200 +Subject: [PATCH] ovn-nbctl: add --may-exist/--if-exists options for policy + routing + +Introduce the following options to avoid error reporting for policy +routing: +1) --may-exist: the lr-policy-add does not result in an error if a policy + with the same priority and match string is already present +2) --if-exists: the lr-policy-del does not result in an error if a policy + with the specified uuid is not present in the db + +Signed-off-by: Lorenzo Bianconi +Signed-off-by: Han Zhou +--- + tests/ovn-nbctl.at | 7 ++++++- + utilities/ovn-nbctl.8.xml | 20 +++++++++++++++----- + utilities/ovn-nbctl.c | 16 ++++++++++------ + 3 files changed, 31 insertions(+), 12 deletions(-) + +diff --git a/tests/ovn-nbctl.at b/tests/ovn-nbctl.at +index baf7a87f5..3dbedc843 100644 +--- a/tests/ovn-nbctl.at ++++ b/tests/ovn-nbctl.at +@@ -1651,6 +1651,8 @@ AT_CHECK([ovn-nbctl lr-policy-add lr0 100 "ip4.src == 1.1.1.0/24" drop], [1], [] + [ovn-nbctl: Same routing policy already existed on the logical router lr0. + ]) + ++AT_CHECK([ovn-nbctl --may-exist lr-policy-add lr0 100 "ip4.src == 1.1.1.0/24" drop]) ++ + dnl Add duplicated policy + AT_CHECK([ovn-nbctl lr-policy-add lr0 103 "ip4.src == 1.1.1.0/24" deny], [1], [], + [ovn-nbctl: deny: action must be one of "allow", "drop", and "reroute" +@@ -1675,10 +1677,13 @@ Routing Policies + + + dnl Delete policy by specified uuid +-AT_CHECK([ovn-nbctl lr-policy-del lr0 $(ovn-nbctl --bare --column _uuid list logical_router_policy)]) ++uuid=$(ovn-nbctl --bare --column _uuid list logical_router_policy) ++AT_CHECK([ovn-nbctl lr-policy-del lr0 $uuid]) + AT_CHECK([ovn-nbctl list logical-router-policy], [0], [dnl + ]) + ++AT_CHECK([ovn-nbctl --if-exists lr-policy-del lr0 $uuid]) ++ + dnl Add policy with reroute action + AT_CHECK([ovn-nbctl lr-policy-add lr0 102 "ip4.src == 3.1.2.0/24" reroute 3.3.3.3]) + +diff --git a/utilities/ovn-nbctl.8.xml b/utilities/ovn-nbctl.8.xml +index fcc4312dd..59302296b 100644 +--- a/utilities/ovn-nbctl.8.xml ++++ b/utilities/ovn-nbctl.8.xml +@@ -737,8 +737,9 @@ +

Logical Router Policy Commands

+ +
+-
lr-policy-add router priority +- match action [nexthop] ++
[--may-exist]lr-policy-add ++ router priority match ++ action [nexthop] + [options key=value]]
+
+

+@@ -754,6 +755,13 @@ + The supported option is : pkt_mark. +

+ ++

++ If --may-exist is specified, adding a duplicated ++ routing policy with the same priority and match string is not ++ really created. Without --may-exist, adding a ++ duplicated routing policy results in error. ++

++ +

+ The following example shows a policy to lr1, which will drop packets + from192.168.100.0/24. +@@ -771,8 +779,8 @@ +

+
+ +-
lr-policy-del router [{priority | uuid} +- [match]]
++
[--if-exists] lr-policy-del ++ router [{priority | uuid} [match]]
+
+

+ Deletes polices from router. If only router +@@ -784,7 +792,9 @@ + +

+ If router and uuid are supplied, then the +- policy with sepcified uuid is deleted. ++ policy with sepcified uuid is deleted. It is an error if ++ uuid does not exist, unless --if-exists ++ is specified. +

+
+ +diff --git a/utilities/ovn-nbctl.c b/utilities/ovn-nbctl.c +index c54e63937..caf99dfeb 100644 +--- a/utilities/ovn-nbctl.c ++++ b/utilities/ovn-nbctl.c +@@ -3648,12 +3648,15 @@ nbctl_lr_policy_add(struct ctl_context *ctx) + + /* Check if same routing policy already exists. + * A policy is uniquely identified by priority and match */ ++ bool may_exist = !!shash_find(&ctx->options, "--may-exist"); + for (int i = 0; i < lr->n_policies; i++) { + const struct nbrec_logical_router_policy *policy = lr->policies[i]; + if (policy->priority == priority && + !strcmp(policy->match, ctx->argv[3])) { +- ctl_error(ctx, "Same routing policy already existed on the " +- "logical router %s.", ctx->argv[1]); ++ if (!may_exist) { ++ ctl_error(ctx, "Same routing policy already existed on the " ++ "logical router %s.", ctx->argv[1]); ++ } + return; + } + } +@@ -3733,7 +3736,6 @@ nbctl_lr_policy_del(struct ctl_context *ctx) + ctx->error = error; + return; + } +- + } + /* If uuid was specified, delete routing policy with the + * specified uuid. */ +@@ -3751,7 +3753,9 @@ nbctl_lr_policy_del(struct ctl_context *ctx) + } + } + if (n_policies == lr->n_policies) { +- ctl_error(ctx, "Logical router policy uuid is not found."); ++ if (!shash_find(&ctx->options, "--if-exists")) { ++ ctl_error(ctx, "Logical router policy uuid is not found."); ++ } + return; + } + +@@ -6529,9 +6533,9 @@ static const struct ctl_command_syntax nbctl_commands[] = { + /* Policy commands */ + { "lr-policy-add", 4, INT_MAX, + "ROUTER PRIORITY MATCH ACTION [NEXTHOP] [OPTIONS - KEY=VALUE ...]", +- NULL, nbctl_lr_policy_add, NULL, "", RW }, ++ NULL, nbctl_lr_policy_add, NULL, "--may-exist", RW }, + { "lr-policy-del", 1, 3, "ROUTER [{PRIORITY | UUID} [MATCH]]", NULL, +- nbctl_lr_policy_del, NULL, "", RW }, ++ nbctl_lr_policy_del, NULL, "--if-exists", RW }, + { "lr-policy-list", 1, 1, "ROUTER", NULL, nbctl_lr_policy_list, NULL, + "", RO }, + +-- +2.26.2 + diff --git a/SOURCES/0001-ovn-northd-Add-localnet-ports-to-Multicast_Groups-cr.patch b/SOURCES/0001-ovn-northd-Add-localnet-ports-to-Multicast_Groups-cr.patch new file mode 100644 index 0000000..28fe93c --- /dev/null +++ b/SOURCES/0001-ovn-northd-Add-localnet-ports-to-Multicast_Groups-cr.patch @@ -0,0 +1,183 @@ +From 89502a3b33b1bf407debe5edc6578b2a72afdd5b Mon Sep 17 00:00:00 2001 +From: Dumitru Ceara +Date: Thu, 8 Oct 2020 15:48:07 +0200 +Subject: [PATCH] ovn-northd: Add localnet ports to Multicast_Groups created by + IGMP_Group. + +In case a logical switch is a "bridged logical switch", i.e., it has a +localnet port, add the localnet port to all multicast groups generated +from IGMP_Groups. This is needed because multicast packets are never +tunneled between hypervisors. + +Note: There still exists an issue regarding IP Multicast Relay (routing) +when traffic is received on bridged logical switches connected to +logical routers with mcast_relay=true. That issue is not addressed +yet but an item is added to TODO.rst to track it. + +Reported-at: https://bugzilla.redhat.com/1886103 +Fixes: 5d1527b11e94 ("ovn-northd: Add IGMP Relay support") +Signed-off-by: Dumitru Ceara +Signed-off-by: Numan Siddique + +(cherry-picked from master commit 97778ab3e422ac071faa67f9f477fd54977e9c04) + +(cherry picked from upstream commit a2563f623f2c88f78134928a4f40bdd06cc89b02) + +Change-Id: I3f251a19317bfb8a3334357604605ccbb054a0f9 +--- + TODO.rst | 8 ++++++++ + northd/ovn-northd.c | 7 +++++++ + tests/ovn.at | 40 ++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 55 insertions(+) + +diff --git a/TODO.rst b/TODO.rst +index 4321630..c158155 100644 +--- a/TODO.rst ++++ b/TODO.rst +@@ -152,3 +152,11 @@ OVN To-do List + . This causes an additional + hashtable lookup in parse_port_group() which can be avoided when we are sure + that the Southbound DB uses the new format. ++ ++* IP Multicast Relay ++ ++ * When connecting bridged logical switches (localnet) to logical routers ++ with IP Multicast Relay enabled packets might get duplicated. We need ++ to find a way of determining if routing has already been executed (on a ++ different hypervisor) for the IP multicast packet being processed locally ++ in the router pipeline. +diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c +index 3a00c8a..c7ca0f8 100644 +--- a/northd/ovn-northd.c ++++ b/northd/ovn-northd.c +@@ -4095,6 +4095,13 @@ ovn_igmp_group_aggregate_ports(struct ovn_igmp_group *igmp_group, + ovn_igmp_group_destroy_entry(entry); + free(entry); + } ++ ++ if (igmp_group->datapath->n_localnet_ports) { ++ ovn_multicast_add_ports(mcast_groups, igmp_group->datapath, ++ &igmp_group->mcgroup, ++ igmp_group->datapath->localnet_ports, ++ igmp_group->datapath->n_localnet_ports); ++ } + } + + static void +diff --git a/tests/ovn.at b/tests/ovn.at +index 7769b69..4c6adf2 100644 +--- a/tests/ovn.at ++++ b/tests/ovn.at +@@ -16650,6 +16650,7 @@ ovn_start + # - subnet 30.0.0.0/8 + # - 1 port bound on hv1 (sw3-p1) + # - 1 port bound on hv2 (sw3-p2) ++# - 1 localnet port (sw3-ln) + + reset_pcap_file() { + local iface=$1 +@@ -16776,6 +16777,9 @@ ovn-nbctl lsp-add sw2 sw2-p1 + ovn-nbctl lsp-add sw2 sw2-p2 + ovn-nbctl lsp-add sw3 sw3-p1 + ovn-nbctl lsp-add sw3 sw3-p2 ++ovn-nbctl lsp-add sw3 sw3-ln \ ++ -- lsp-set-type sw3-ln localnet \ ++ -- lsp-set-options sw3-ln network_name=phys + + ovn-nbctl lr-add rtr + ovn-nbctl lrp-add rtr rtr-sw1 00:00:00:00:01:00 10.0.0.254/24 +@@ -16820,6 +16824,7 @@ ovs-vsctl -- add-port br-int hv1-vif4 -- \ + options:tx_pcap=hv1/vif4-tx.pcap \ + options:rxq_pcap=hv1/vif4-rx.pcap \ + ofport-request=1 ++ovs-vsctl set open . external-ids:ovn-bridge-mappings=phys:br-phys + + sim_add hv2 + as hv2 +@@ -16845,6 +16850,7 @@ ovs-vsctl -- add-port br-int hv2-vif4 -- \ + options:tx_pcap=hv2/vif4-tx.pcap \ + options:rxq_pcap=hv2/vif4-rx.pcap \ + ofport-request=1 ++ovs-vsctl set open . external-ids:ovn-bridge-mappings=phys:br-phys + + OVN_POPULATE_ARP + +@@ -17128,6 +17134,18 @@ store_ip_multicast_pkt \ + $(ip_to_hex 10 0 0 42) $(ip_to_hex 239 0 1 68) 1e 20 ca70 11 \ + e518e518000a3b3a0000 expected_switched + ++# TODO: IGMP Relay duplicates IP multicast packets in some conditions, for ++# more details see TODO.rst, section "IP Multicast Relay". Once that issue is ++# fixed the duplicated packets should not appear anymore. ++store_ip_multicast_pkt \ ++ 000000000100 01005e000144 \ ++ $(ip_to_hex 10 0 0 42) $(ip_to_hex 239 0 1 68) 1e 1f cb70 11 \ ++ e518e518000a3b3a0000 expected_routed_sw1 ++store_ip_multicast_pkt \ ++ 000000000200 01005e000144 \ ++ $(ip_to_hex 10 0 0 42) $(ip_to_hex 239 0 1 68) 1e 1f cb70 11 \ ++ e518e518000a3b3a0000 expected_routed_sw2 ++ + OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [expected_routed_sw1]) + OVN_CHECK_PACKETS([hv2/vif3-tx.pcap], [expected_routed_sw2]) + OVN_CHECK_PACKETS([hv1/vif4-tx.pcap], [expected_switched]) +@@ -17275,6 +17293,7 @@ ovn_start + # - subnet 30::/64 + # - 1 port bound on hv1 (sw3-p1) + # - 1 port bound on hv2 (sw3-p2) ++# - 1 localnet port (sw3-ln) + + reset_pcap_file() { + local iface=$1 +@@ -17400,6 +17419,9 @@ ovn-nbctl lsp-add sw2 sw2-p1 + ovn-nbctl lsp-add sw2 sw2-p2 + ovn-nbctl lsp-add sw3 sw3-p1 + ovn-nbctl lsp-add sw3 sw3-p2 ++ovn-nbctl lsp-add sw3 sw3-ln \ ++ -- lsp-set-type sw3-ln localnet \ ++ -- lsp-set-options sw3-ln network_name=phys + + ovn-nbctl lr-add rtr + ovn-nbctl lrp-add rtr rtr-sw1 00:00:00:00:01:00 10::fe/64 +@@ -17459,6 +17481,7 @@ ovs-vsctl -- add-port br-int hv1-vif4 -- \ + options:tx_pcap=hv1/vif4-tx.pcap \ + options:rxq_pcap=hv1/vif4-rx.pcap \ + ofport-request=1 ++ovs-vsctl set open . external-ids:ovn-bridge-mappings=phys:br-phys + + sim_add hv2 + as hv2 +@@ -17484,6 +17507,7 @@ ovs-vsctl -- add-port br-int hv2-vif4 -- \ + options:tx_pcap=hv2/vif4-tx.pcap \ + options:rxq_pcap=hv2/vif4-rx.pcap \ + ofport-request=1 ++ovs-vsctl set open . external-ids:ovn-bridge-mappings=phys:br-phys + + OVN_POPULATE_ARP + +@@ -17794,6 +17818,22 @@ store_ip_multicast_pkt \ + 93407a69000e1b5e61736461640a \ + expected_switched + ++# TODO: MLD Relay duplicates IP multicast packets in some conditions, for ++# more details see TODO.rst, section "IP Multicast Relay". Once that issue is ++# fixed the duplicated packets should not appear anymore. ++store_ip_multicast_pkt \ ++ 000000000100 333300000001 \ ++ 10000000000000000000000000000042 ff0adeadbeef00000000000000000001 \ ++ 000e 01 11 \ ++ 93407a69000e1b5e61736461640a \ ++ expected_routed_sw1 ++store_ip_multicast_pkt \ ++ 000000000200 333300000001 \ ++ 10000000000000000000000000000042 ff0adeadbeef00000000000000000001 \ ++ 000e 01 11 \ ++ 93407a69000e1b5e61736461640a \ ++ expected_routed_sw2 ++ + OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [expected_routed_sw1]) + OVN_CHECK_PACKETS([hv2/vif3-tx.pcap], [expected_routed_sw2]) + OVN_CHECK_PACKETS([hv1/vif4-tx.pcap], [expected_switched]) +-- +1.8.3.1 + diff --git a/SOURCES/0001-ovn-northd-Fix-multiple-ARP-replies-for-SNAT-entries.patch b/SOURCES/0001-ovn-northd-Fix-multiple-ARP-replies-for-SNAT-entries.patch deleted file mode 100644 index f370ad9..0000000 --- a/SOURCES/0001-ovn-northd-Fix-multiple-ARP-replies-for-SNAT-entries.patch +++ /dev/null @@ -1,393 +0,0 @@ -From eec06481841c053a00f04849a305b66b2d274f2a Mon Sep 17 00:00:00 2001 -From: Numan Siddique -Date: Sat, 12 Sep 2020 18:25:53 +0530 -Subject: [PATCH] ovn-northd: Fix multiple ARP replies for SNAT entries - configured on a distributed router. - -The commit in the Fixes tag, while addressing the issue to send ARP replies for -the SNAT entries, didn't take into account the gateway router port scenario. -Because of this, all the chassis which have bridge mappings configured reply -to ARP request for SNAT entries. This patch fixes the issue by adding -"is_chassis_resident()" condition for such flows so that only the gateway chassis -which claims the gateway router port responds to the ARP request. - -Note: This patch doesn't require any changes to ovn-northd.8.xml as it was already -documented with the desired flows for SNAT entries. - -Reported-at: https://mail.openvswitch.org/pipermail/ovs-discuss/2020-September/050679.html -Reported-by: Chris -Fixes: e2aa124ff7c2("ovn-northd: Add ARP responder flows for SNAT entries.") -Acked-by: Dumitru Ceara -Signed-off-by: Numan Siddique ---- - TODO.rst | 4 + - northd/ovn-northd.c | 14 ++- - tests/ovn-northd.at | 5 + - tests/ovn.at | 260 ++++++++++++++++++++++++++++++++++++++++++++ - 4 files changed, 279 insertions(+), 4 deletions(-) - -diff --git a/TODO.rst b/TODO.rst -index 98b86cef8..53ca2057e 100644 ---- a/TODO.rst -+++ b/TODO.rst -@@ -25,6 +25,10 @@ - OVN To-do List - ============== - -+* Refactor ovn-northd code to have separate functions to add logical flows -+ for gateway logical routers and logical routers with distributed gateway -+ port. -+ - * Get incremental updates in ovn-controller and ovn-northd in some - sensible way. - -diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c -index 63c58ca6a..e5e524ec3 100644 ---- a/northd/ovn-northd.c -+++ b/northd/ovn-northd.c -@@ -8678,10 +8678,9 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, - ext_addrs->ipv4_addrs[0].addr_s; - - if (!strcmp(nat->type, "snat")) { -- if (sset_contains(&snat_ips, ext_addr)) { -+ if (!sset_add(&snat_ips, ext_addr)) { - continue; - } -- sset_add(&snat_ips, ext_addr); - } - - /* Priority 91 and 92 flows are added for each gateway router -@@ -9063,6 +9062,7 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, - continue; - } - -+ struct sset sset_snat_ips = SSET_INITIALIZER(&sset_snat_ips); - for (size_t i = 0; i < op->od->nbr->n_nat; i++) { - struct ovn_nat *nat_entry = &op->od->nat_entries[i]; - const struct nbrec_nat *nat = nat_entry->nb; -@@ -9072,8 +9072,14 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, - continue; - } - -+ struct lport_addresses *ext_addrs = &nat_entry->ext_addrs; -+ char *ext_addr = (nat_entry_is_v6(nat_entry) -+ ? ext_addrs->ipv6_addrs[0].addr_s -+ : ext_addrs->ipv4_addrs[0].addr_s); - if (!strcmp(nat->type, "snat")) { -- continue; -+ if (!sset_add(&sset_snat_ips, ext_addr)) { -+ continue; -+ } - } - - /* Mac address to use when replying to ARP/NS. */ -@@ -9115,7 +9121,6 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, - /* Respond to ARP/NS requests on the chassis that binds the gw - * port. Drop the ARP/NS requests on other chassis. - */ -- struct lport_addresses *ext_addrs = &nat_entry->ext_addrs; - if (nat_entry_is_v6(nat_entry)) { - build_lrouter_nd_flow(op->od, op, "nd_na", - ext_addrs->ipv6_addrs[0].addr_s, -@@ -9138,6 +9143,7 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, - &nat->header_, lflows); - } - } -+ sset_destroy(&sset_snat_ips); - } - - /* DHCPv6 reply handling */ -diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at -index 86eee3542..70ae8b92b 100644 ---- a/tests/ovn-northd.at -+++ b/tests/ovn-northd.at -@@ -1754,6 +1754,8 @@ action=(nd_na_router { eth.src = xreg0[[0..47]]; ip6.src = fe80::200:ff:fe00:100 - # Priority 91 drop flows (per distributed gw port), if port is not resident. - AT_CHECK([ovn-sbctl lflow-list | grep -E "lr_in_ip_input.*priority=91" | grep "arp\|nd" | sort], [0], [dnl - table=3 (lr_in_ip_input ), priority=91 , dnl -+match=(inport == "lrp-public" && arp.op == 1 && arp.tpa == 43.43.43.150), action=(drop;) -+ table=3 (lr_in_ip_input ), priority=91 , dnl - match=(inport == "lrp-public" && arp.op == 1 && arp.tpa == 43.43.43.2), dnl - action=(drop;) - table=3 (lr_in_ip_input ), priority=91 , dnl -@@ -1767,6 +1769,9 @@ action=(drop;) - # Priority 92 ARP/NS responders (per distributed gw port), if port is resident. - AT_CHECK([ovn-sbctl lflow-list | grep -E "lr_in_ip_input.*priority=92" | grep "arp\|nd" | sort], [0], [dnl - table=3 (lr_in_ip_input ), priority=92 , dnl -+match=(inport == "lrp-public" && arp.op == 1 && arp.tpa == 43.43.43.150 && is_chassis_resident("cr-lrp-public")), dnl -+action=(eth.dst = eth.src; eth.src = xreg0[[0..47]]; arp.op = 2; /* ARP reply */ arp.tha = arp.sha; arp.sha = xreg0[[0..47]]; arp.tpa = arp.spa; arp.spa = 43.43.43.150; outport = inport; flags.loopback = 1; output;) -+ table=3 (lr_in_ip_input ), priority=92 , dnl - match=(inport == "lrp-public" && arp.op == 1 && arp.tpa == 43.43.43.2 && is_chassis_resident("cr-lrp-public")), dnl - action=(eth.dst = eth.src; eth.src = xreg0[[0..47]]; arp.op = 2; /* ARP reply */ arp.tha = arp.sha; arp.sha = xreg0[[0..47]]; arp.tpa = arp.spa; arp.spa = 43.43.43.2; outport = inport; flags.loopback = 1; output;) - table=3 (lr_in_ip_input ), priority=92 , dnl -diff --git a/tests/ovn.at b/tests/ovn.at -index 2bef87b40..7cc1756e1 100644 ---- a/tests/ovn.at -+++ b/tests/ovn.at -@@ -21439,3 +21439,263 @@ OVS_WAIT_UNTIL([test x$(as hv1 ovn-appctl -t ovn-controller debug/status) = "xru - - OVN_CLEANUP([hv1]) - AT_CLEANUP -+ -+AT_SETUP([ovn -- ARP replies for SNAT external ips]) -+ovn_start -+ -+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-port1 \ -+ 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=sw3-port1 \ -+ options:tx_pcap=hv1/vif2-tx.pcap \ -+ options:rxq_pcap=hv1/vif2-rx.pcap \ -+ ofport-request=2 -+AT_CHECK([ovs-vsctl set Open_vSwitch . external-ids:ovn-bridge-mappings=physnet1:br-phys]) -+ovs-vsctl set open . external_ids:ovn-enable-lflow-cache=false -+ -+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-port2 \ -+ options:tx_pcap=hv2/vif1-tx.pcap \ -+ options:rxq_pcap=hv2/vif1-rx.pcap \ -+ ofport-request=1 -+AT_CHECK([ovs-vsctl set Open_vSwitch . external-ids:ovn-bridge-mappings=physnet1:br-phys]) -+ovs-vsctl set open . external_ids:ovn-enable-lflow-cache=false -+ -+sim_add hv3 -+as hv3 -+ovs-vsctl add-br br-phys -+ovn_attach n1 br-phys 192.168.0.3 -+ovs-vsctl -- add-port br-int hv3-vif1 -- \ -+ set interface hv3-vif1 external-ids:iface-id=sw1-port1 \ -+ options:tx_pcap=hv3/vif1-tx.pcap \ -+ options:rxq_pcap=hv3/vif1-rx.pcap \ -+ ofport-request=1 -+AT_CHECK([ovs-vsctl set Open_vSwitch . external-ids:ovn-bridge-mappings=physnet1:br-phys]) -+ovs-vsctl set open . external_ids:ovn-enable-lflow-cache=false -+ -+ovn-nbctl ls-add sw0 -+ovn-nbctl lsp-add sw0 sw0-port1 -+ovn-nbctl lsp-set-addresses sw0-port1 "10:54:00:00:00:03 10.0.0.3 1000::3" -+ovn-nbctl lsp-add sw0 sw0-port2 -+ovn-nbctl lsp-set-addresses sw0-port2 "10:54:00:00:00:04 10.0.0.4 1000::4" -+ -+ovn-nbctl ls-add sw1 -+ovn-nbctl lsp-add sw1 sw1-port1 -+ovn-nbctl lsp-set-addresses sw1-port1 "40:54:00:00:00:03 20.0.0.3 2000::3" -+ -+ovn-nbctl lr-add lr0 -+ovn-nbctl lrp-add lr0 lr0-sw0 00:00:00:00:ff:01 10.0.0.1/24 1000::a/64 -+ovn-nbctl lsp-add sw0 sw0-lr0 -+ovn-nbctl lsp-set-type sw0-lr0 router -+ovn-nbctl lsp-set-addresses sw0-lr0 router -+ovn-nbctl lsp-set-options sw0-lr0 router-port=lr0-sw0 -+ -+ovn-nbctl lrp-add lr0 lr0-sw1 00:00:00:00:ff:02 20.0.0.1/24 2000::a/64 -+ovn-nbctl lsp-add sw1 sw1-lr0 -+ovn-nbctl lsp-set-type sw1-lr0 router -+ovn-nbctl lsp-set-addresses sw1-lr0 router -+ovn-nbctl lsp-set-options sw1-lr0 router-port=lr0-sw1 -+ -+ovn-nbctl ls-add public -+ovn-nbctl lrp-add lr0 lr0-public 00:00:20:20:12:13 172.16.0.100/24 3000::a/64 -+ovn-nbctl lsp-add public public-lr0 -+ovn-nbctl lsp-set-type public-lr0 router -+ovn-nbctl lsp-set-addresses public-lr0 router -+ovn-nbctl lsp-set-options public-lr0 router-port=lr0-public -+ -+# localnet port -+ovn-nbctl lsp-add public ln-public -+ovn-nbctl lsp-set-type ln-public localnet -+ovn-nbctl lsp-set-addresses ln-public unknown -+ovn-nbctl lsp-set-options ln-public network_name=physnet1 -+ -+# schedule the gw router port to a chassis. -+ovn-nbctl lrp-set-gateway-chassis lr0-public hv1 20 -+ -+# Create NAT entries for the ports -+ -+# sw0-port1 -+ovn-nbctl lr-nat-add lr0 dnat_and_snat 172.16.0.110 10.0.0.3 sw0-port1 30:54:00:00:00:03 -+ovn-nbctl lr-nat-add lr0 dnat_and_snat 3000::c 1000::3 sw0-port1 40:54:00:00:00:03 -+# sw1-port1 -+ovn-nbctl lr-nat-add lr0 dnat_and_snat 172.16.0.120 20.0.0.3 sw1-port1 30:54:00:00:00:04 -+ovn-nbctl lr-nat-add lr0 dnat_and_snat 3000::d 2000::3 sw1-port1 40:54:00:00:00:04 -+ -+# Add snat entriess -+ovn-nbctl lr-nat-add lr0 snat 172.16.0.100 10.0.0.0/24 -+ovn-nbctl lr-nat-add lr0 snat 172.16.0.101 10.0.0.10 -+ovn-nbctl lr-nat-add lr0 snat 172.16.0.102 10.0.0.11 -+ovn-nbctl lr-nat-add lr0 snat 172.16.0.100 20.0.0.0/24 -+ -+ovn-nbctl ls-add sw3 -+ovn-nbctl lsp-add sw3 sw3-port1 -+ovn-nbctl lsp-set-addresses sw3-port1 "20:14:00:00:00:03 30.0.0.3 3000::3" -+ -+ovn-nbctl lr-add lr1 -+ovn-nbctl lrp-add lr1 lr1-sw3 00:00:00:10:ff:03 30.0.0.1/24 3000::a/64 -+ovn-nbctl lsp-add sw3 sw3-lr1 -+ovn-nbctl lsp-set-type sw3-lr1 router -+ovn-nbctl lsp-set-addresses sw3-lr1 router -+ovn-nbctl lsp-set-options sw3-lr1 router-port=lr1-sw3 -+ -+ovn-nbctl ls-add join -+ -+# Connect lr1 to join -+ovn-nbctl lrp-add lr1 lr1-join 00:00:04:01:02:03 170.0.0.1/24 -+ovn-nbctl lsp-add join join-lr1 -+ovn-nbctl lsp-set-type join-lr1 router -+ovn-nbctl lsp-set-addresses join-lr1 router -+ovn-nbctl lsp-set-options join-lr1 router-port=lr1-join -+ -+# Create GW router -+ovn-nbctl lr-add gw_router -+# connect gw_router to join -+ovn-nbctl lrp-add gw_router gw_router-join 00:00:03:11:12:13 170.0.0.2/24 -+ovn-nbctl lsp-add join join-gw_router -+ovn-nbctl lsp-set-type join-gw_router router -+ovn-nbctl lsp-set-addresses join-gw_router router -+ovn-nbctl lsp-set-options join-gw_router router-port=gw_router-join -+ -+# Connect gw_router to public -+ovn-nbctl lrp-add gw_router gw_router-public 00:00:30:30:32:33 172.16.0.200/24 3000::b/64 -+ovn-nbctl lsp-add public public-gw_router -+ovn-nbctl lsp-set-type public-gw_router router -+ovn-nbctl lsp-set-addresses public-gw_router router -+ovn-nbctl lsp-set-options public-gw_router router-port=gw_router-public -+ -+# Pin gw_router to hv3 -+ovn-nbctl set logical_router gw_router options:chassis=hv3 -+ -+# Add snat entries on gw_router -+ovn-nbctl lr-nat-add gw_router snat 172.16.0.200 30.0.0.0/24 -+ovn-nbctl lr-nat-add gw_router snat 172.16.0.201 30.0.0.3 -+ -+ovn-nbctl --wait=hv sync -+ -+# Create an interface in br-phys in hv2 and send ARP request for 172.16.0.100 -+as hv2 -+ovs-vsctl -- add-port br-phys hv2-phys1 -- \ -+ set interface hv2-phys1 options:tx_pcap=hv2/phys1-tx.pcap \ -+ options:rxq_pcap=hv2/phys1-rx.pcap \ -+ ofport-request=1 -+ -+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 -+} -+ -+ip_to_hex() { -+ printf "%02x%02x%02x%02x" "$@" -+} -+ -+send_arp_request() { -+ local eth_src=$1 spa=$2 tpa=$3 -+ local eth_dst=ffffffffffff -+ local eth_type=0806 -+ local eth=${eth_dst}${eth_src}${eth_type} -+ -+ local arp=0001080006040001${eth_src}${spa}${eth_dst}${tpa} -+ -+ local request=${eth}${arp} -+ as hv2 ovs-appctl netdev-dummy/receive hv2-phys1 $request -+} -+ -+test_arp_response () { -+ local router_mac=$1 router_ip=$2 gw=$3 nongw1=$4 nongw2=$5 -+ -+ echo "Checking arp reply for IP - $router_ip" -+ as hv1 reset_pcap_file br-phys_n1 hv1/br-phys_n1 -+ as hv2 reset_pcap_file br-phys_n1 hv2/br-phys_n1 -+ as hv3 reset_pcap_file br-phys_n1 hv3/br-phys_n1 -+ as hv2 reset_pcap_file hv2-phys1 hv2/phys1 -+ -+ local src_mac=000200100011 -+ src_ip=$(ip_to_hex 172 16 0 40) -+ send_arp_request ${src_mac} ${src_ip} ${router_ip} -+ arp_reply=${src_mac}${router_mac}08060001080006040002${router_mac} -+ arp_reply=${arp_reply}${router_ip}${src_mac}${src_ip} -+ -+ OVS_WAIT_UNTIL([ -+ test $($PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv2/phys1-tx.pcap | wc -l) -ge 1 -+ ]) -+ -+ AT_CHECK([$PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv2/phys1-tx.pcap | \ -+ grep -c $arp_reply], [0], [1 -+]) -+ -+ # $gw phys1-n1 should see the response because $gw ovn-controller responds -+ # to arp request. -+ AT_CHECK([$PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" $gw/br-phys_n1-tx.pcap | \ -+ grep -c $arp_reply], [0], [1 -+]) -+ -+ # $nongw1 and $nongw1 phys1-n1 should not see the response. -+ $PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" $nongw1/br-phys_n1-tx.pcap -+ AT_CHECK([$PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" $nongw1/br-phys_n1-tx.pcap | \ -+ grep -c $arp_reply], [1], [0 -+]) -+ -+ AT_CHECK([$PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" $nongw2/br-phys_n1-tx.pcap | \ -+ grep -c $arp_reply], [1], [0 -+]) -+} -+ -+# Send ARP request for the IPs which belongs to lr0 having -+# distributed gw router port - lr0-public. -+test_arp_response 000020201213 $(ip_to_hex 172 16 0 100) hv1 hv2 hv3 -+test_arp_response 000020201213 $(ip_to_hex 172 16 0 101) hv1 hv2 hv3 -+test_arp_response 000020201213 $(ip_to_hex 172 16 0 102) hv1 hv2 hv3 -+ -+# Send ARP request for the IP which belongs to gw_router -+test_arp_response 000030303233 $(ip_to_hex 172 16 0 200) hv3 hv1 hv2 -+test_arp_response 000030303233 $(ip_to_hex 172 16 0 201) hv3 hv1 hv2 -+ -+# Make hv3 claim the cr-lr0-public -+ovn-nbctl lrp-set-gateway-chassis lr0-public hv1 20 -+ovn-nbctl lrp-set-gateway-chassis lr0-public hv2 30 -+ovn-nbctl lrp-set-gateway-chassis lr0-public hv3 40 -+ -+hv3_uuid=$(ovn-sbctl --bare --columns _uuid list chassis hv3) -+ -+OVS_WAIT_UNTIL([ -+ cr_lr0_public_ch=$(ovn-sbctl --bare --columns chassis list port_binding cr-lr0-public) -+ test $cr_lr0_public_ch = $hv3_uuid -+]) -+ -+test_arp_response 000020201213 $(ip_to_hex 172 16 0 100) hv3 hv1 hv2 -+test_arp_response 000020201213 $(ip_to_hex 172 16 0 101) hv3 hv1 hv2 -+test_arp_response 000020201213 $(ip_to_hex 172 16 0 102) hv3 hv1 hv2 -+ -+# Schedule gw_router on hv1. -+ovn-nbctl set logical_router gw_router options:chassis=hv1 -+hv1_uuid=$(ovn-sbctl --bare --columns _uuid list chassis hv1) -+ -+OVS_WAIT_UNTIL([ -+ gw_router_ch=$(ovn-sbctl --bare --columns chassis list port_binding gw_router-public) -+ test $gw_router_ch = $hv1_uuid -+]) -+ -+# Send ARP request for the IP which belongs to gw_router -+test_arp_response 000030303233 $(ip_to_hex 172 16 0 200) hv1 hv2 hv3 -+test_arp_response 000030303233 $(ip_to_hex 172 16 0 201) hv1 hv2 hv3 -+ -+OVN_CLEANUP([hv1],[hv2],[hv3]) -+AT_CLEANUP --- -2.26.2 - diff --git a/SOURCES/0001-ovn-northd-Rate-limit-missing-chassis-log.patch b/SOURCES/0001-ovn-northd-Rate-limit-missing-chassis-log.patch deleted file mode 100644 index 5eb3010..0000000 --- a/SOURCES/0001-ovn-northd-Rate-limit-missing-chassis-log.patch +++ /dev/null @@ -1,39 +0,0 @@ -From 4fe045290891859c14e19d6bb00b2c857a0d4021 Mon Sep 17 00:00:00 2001 -From: Dumitru Ceara -Date: Tue, 1 Sep 2020 21:20:04 +0200 -Subject: [PATCH] ovn-northd: Rate limit missing chassis log. - -This log does indicate a problem so it's useful to log it at WARN level -but there's not much use to spam at every iteration of ovn-northd. - -CC: Han Zhou -Fixes: 4adc10f58127 ("Avoid nb_cfg update notification flooding") -Signed-off-by: Dumitru Ceara -Signed-off-by: Han Zhou -(cherry picked from upstream commit 81e88122985caf35b6c60b8d04e16cd0b53aa2b7) - -Change-Id: I946b74b44149cefec09cd1a19de3ffb8d7c01647 ---- - northd/ovn-northd.c | 6 ++++-- - 1 file changed, 4 insertions(+), 2 deletions(-) - -diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c -index c83f9d5..8f2cd43 100644 ---- a/northd/ovn-northd.c -+++ b/northd/ovn-northd.c -@@ -12254,8 +12254,10 @@ update_northbound_cfg(struct northd_context *ctx, - continue; - } - } else { -- VLOG_WARN("Chassis not exist for Chassis_Private record, " -- "name: %s", chassis_priv->name); -+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); -+ VLOG_WARN_RL(&rl, "Chassis does not exist for " -+ "Chassis_Private record, name: %s", -+ chassis_priv->name); - } - - if (chassis_priv->nb_cfg < hv_cfg) { --- -1.8.3.1 - diff --git a/SOURCES/0001-ovsdb-idl-Avoid-inconsistent-IDL-state-with-OVSDB_MO.patch b/SOURCES/0001-ovsdb-idl-Avoid-inconsistent-IDL-state-with-OVSDB_MO.patch deleted file mode 100644 index 5df4118..0000000 --- a/SOURCES/0001-ovsdb-idl-Avoid-inconsistent-IDL-state-with-OVSDB_MO.patch +++ /dev/null @@ -1,460 +0,0 @@ -From 966dd7bc7fb7931d7616ca886dc24ecb1f34cced Mon Sep 17 00:00:00 2001 -From: Dumitru Ceara -Date: Thu, 28 May 2020 14:32:31 +0200 -Subject: [PATCH] ovsdb-idl: Avoid inconsistent IDL state with - OVSDB_MONITOR_V3. - -Assuming an ovsdb client connected to a database using OVSDB_MONITOR_V3 -(i.e., "monitor_cond_since" method) with the initial monitor condition -MC1. - -Assuming the following two transactions are executed on the -ovsdb-server: -TXN1: "insert record R1 in table T1" -TXN2: "insert record R2 in table T2" - -If the client's monitor condition MC1 for table T2 matches R2 then the -client will receive the following update3 message: -method="update3", "insert record R2 in table T2", last-txn-id=TXN2 - -At this point, if the presence of the new record R2 in the IDL triggers -the client to update its monitor condition to MC2 and add a clause for -table T1 which matches R1, a monitor_cond_change message is sent to the -server: -method="monitor_cond_change", "clauses from MC2" - -In normal operation the ovsdb-server will reply with a new update3 -message of the form: -method="update3", "insert record R1 in table T1", last-txn-id=TXN2 - -However, if the connection drops in the meantime, this last update might -get lost. - -It might happen that during the reconnect a new transaction happens -that modifies the original record R1: -TXN3: "modify record R1 in table T1" - -When the client reconnects, it will try to perform a fast resync by -sending: -method="monitor_cond_since", "clauses from MC2", last-txn-id=TXN2 - -Because TXN2 is still in the ovsdb-server transaction history, the -server replies with the changes from the most recent transactions only, -i.e., TXN3: -result="true", last-txbb-id=TXN3, "modify record R1 in table T1" - -This causes the IDL on the client in to end up in an inconsistent -state because it has never seen the update that created R1. - -Such a scenario is described in: -https://bugzilla.redhat.com/show_bug.cgi?id=1808580#c22 - -To avoid this issue, the IDL will now maintain (up to) 3 different -types of conditions for each DB table: -- new_cond: condition that has been set by the IDL client but has - not yet been sent to the server through monitor_cond_change. -- req_cond: condition that has been sent to the server but the reply - acknowledging the change hasn't been received yet. -- ack_cond: condition that has been acknowledged by the server. - -Whenever the IDL FSM is restarted (e.g., voluntary or involuntary -disconnect): -- if there is a known last_id txn-id the code ensures that new_cond - will contain the most recent condition set by the IDL client - (either req_cond if there was a request in flight, or new_cond - if the IDL client set a condition while the IDL was disconnected) -- if there is no known last_id txn-id the code ensures that ack_cond will - contain the most recent conditions set by the IDL client regardless - whether they were acked by the server or not. - -When monitor_cond_since/monitor_cond requests are sent they will -always include ack_cond and if new_cond is not NULL a follow up -monitor_cond_change will be generated afterwards. - -On the other hand ovsdb_idl_db_set_condition() will always modify new_cond. - -This ensures that updates of type "insert" that happened before the last -transaction known by the IDL but didn't match old monitor conditions are -sent upon reconnect if the monitor condition has changed to include them -in the meantime. - -Fixes: 403a6a0cb003 ("ovsdb-idl: Fast resync from server when connection reset.") -Signed-off-by: Dumitru Ceara -Acked-by: Han Zhou -Signed-off-by: Ilya Maximets -(cherry picked from upstream OVS commit ae25f8c8fff80a58cd0a15e2d3ae7ab1b4994e48) - -Change-Id: I4f3cd43cf69dfe76eb65c9709b759e5062c29e89 ---- - openvswitch-2.13.0/lib/ovsdb-idl-provider.h | 8 +- - openvswitch-2.13.0/lib/ovsdb-idl.c | 167 ++++++++++++++++++++++++---- - openvswitch-2.13.0/tests/ovsdb-idl.at | 56 ++++++++++ - 3 files changed, 206 insertions(+), 25 deletions(-) - -diff --git a/openvswitch-2.13.0/lib/ovsdb-idl-provider.h b/openvswitch-2.13.0/lib/ovsdb-idl-provider.h -index 30d1d08..00497d9 100644 ---- a/openvswitch-2.13.0/lib/ovsdb-idl-provider.h -+++ b/openvswitch-2.13.0/lib/ovsdb-idl-provider.h -@@ -122,8 +122,12 @@ struct ovsdb_idl_table { - unsigned int change_seqno[OVSDB_IDL_CHANGE_MAX]; - struct ovs_list indexes; /* Contains "struct ovsdb_idl_index"s */ - struct ovs_list track_list; /* Tracked rows (ovsdb_idl_row.track_node). */ -- struct ovsdb_idl_condition condition; -- bool cond_changed; -+ struct ovsdb_idl_condition *ack_cond; /* Last condition acked by the -+ * server. */ -+ struct ovsdb_idl_condition *req_cond; /* Last condition requested to the -+ * server. */ -+ struct ovsdb_idl_condition *new_cond; /* Latest condition set by the IDL -+ * client. */ - }; - - struct ovsdb_idl_class { -diff --git a/openvswitch-2.13.0/lib/ovsdb-idl.c b/openvswitch-2.13.0/lib/ovsdb-idl.c -index 2d35179..8eb4213 100644 ---- a/openvswitch-2.13.0/lib/ovsdb-idl.c -+++ b/openvswitch-2.13.0/lib/ovsdb-idl.c -@@ -240,6 +240,10 @@ static void ovsdb_idl_send_monitor_request(struct ovsdb_idl *, - struct ovsdb_idl_db *, - enum ovsdb_idl_monitor_method); - static void ovsdb_idl_db_clear(struct ovsdb_idl_db *db); -+static void ovsdb_idl_db_ack_condition(struct ovsdb_idl_db *db); -+static void ovsdb_idl_db_sync_condition(struct ovsdb_idl_db *db); -+static void ovsdb_idl_condition_move(struct ovsdb_idl_condition **dst, -+ struct ovsdb_idl_condition **src); - - struct ovsdb_idl { - struct ovsdb_idl_db server; -@@ -424,9 +428,11 @@ ovsdb_idl_db_init(struct ovsdb_idl_db *db, const struct ovsdb_idl_class *class, - = table->change_seqno[OVSDB_IDL_CHANGE_MODIFY] - = table->change_seqno[OVSDB_IDL_CHANGE_DELETE] = 0; - table->db = db; -- ovsdb_idl_condition_init(&table->condition); -- ovsdb_idl_condition_add_clause_true(&table->condition); -- table->cond_changed = false; -+ table->ack_cond = NULL; -+ table->req_cond = NULL; -+ table->new_cond = xmalloc(sizeof *table->new_cond); -+ ovsdb_idl_condition_init(table->new_cond); -+ ovsdb_idl_condition_add_clause_true(table->new_cond); - } - db->monitor_id = json_array_create_2(json_string_create("monid"), - json_string_create(class->database)); -@@ -558,12 +564,15 @@ ovsdb_idl_set_shuffle_remotes(struct ovsdb_idl *idl, bool shuffle) - static void - ovsdb_idl_db_destroy(struct ovsdb_idl_db *db) - { -+ struct ovsdb_idl_condition *null_cond = NULL; - ovs_assert(!db->txn); - ovsdb_idl_db_txn_abort_all(db); - ovsdb_idl_db_clear(db); - for (size_t i = 0; i < db->class_->n_tables; i++) { - struct ovsdb_idl_table *table = &db->tables[i]; -- ovsdb_idl_condition_destroy(&table->condition); -+ ovsdb_idl_condition_move(&table->ack_cond, &null_cond); -+ ovsdb_idl_condition_move(&table->req_cond, &null_cond); -+ ovsdb_idl_condition_move(&table->new_cond, &null_cond); - ovsdb_idl_destroy_indexes(table); - shash_destroy(&table->columns); - hmap_destroy(&table->rows); -@@ -692,6 +701,12 @@ ovsdb_idl_send_request(struct ovsdb_idl *idl, struct jsonrpc_msg *request) - static void - ovsdb_idl_restart_fsm(struct ovsdb_idl *idl) - { -+ /* Resync data DB table conditions to avoid missing updates due to -+ * conditions that were in flight or changed locally while the connection -+ * was down. -+ */ -+ ovsdb_idl_db_sync_condition(&idl->data); -+ - ovsdb_idl_send_schema_request(idl, &idl->server); - ovsdb_idl_transition(idl, IDL_S_SERVER_SCHEMA_REQUESTED); - idl->data.monitoring = OVSDB_IDL_NOT_MONITORING; -@@ -799,7 +814,9 @@ ovsdb_idl_process_response(struct ovsdb_idl *idl, struct jsonrpc_msg *msg) - * do, it's a "monitor_cond_change", which means that the conditional - * monitor clauses were updated. - * -- * If further condition changes were pending, send them now. */ -+ * Mark the last requested conditions as acked and if further -+ * condition changes were pending, send them now. */ -+ ovsdb_idl_db_ack_condition(&idl->data); - ovsdb_idl_send_cond_change(idl); - idl->data.cond_seqno++; - break; -@@ -1495,30 +1512,60 @@ ovsdb_idl_condition_equals(const struct ovsdb_idl_condition *a, - } - - static void --ovsdb_idl_condition_clone(struct ovsdb_idl_condition *dst, -+ovsdb_idl_condition_clone(struct ovsdb_idl_condition **dst, - const struct ovsdb_idl_condition *src) - { -- ovsdb_idl_condition_init(dst); -+ if (*dst) { -+ ovsdb_idl_condition_destroy(*dst); -+ } else { -+ *dst = xmalloc(sizeof **dst); -+ } -+ ovsdb_idl_condition_init(*dst); - -- dst->is_true = src->is_true; -+ (*dst)->is_true = src->is_true; - - const struct ovsdb_idl_clause *clause; - HMAP_FOR_EACH (clause, hmap_node, &src->clauses) { -- ovsdb_idl_condition_add_clause__(dst, clause, clause->hmap_node.hash); -+ ovsdb_idl_condition_add_clause__(*dst, clause, clause->hmap_node.hash); - } - } - -+static void -+ovsdb_idl_condition_move(struct ovsdb_idl_condition **dst, -+ struct ovsdb_idl_condition **src) -+{ -+ if (*dst) { -+ ovsdb_idl_condition_destroy(*dst); -+ free(*dst); -+ } -+ *dst = *src; -+ *src = NULL; -+} -+ - static unsigned int - ovsdb_idl_db_set_condition(struct ovsdb_idl_db *db, - const struct ovsdb_idl_table_class *tc, - const struct ovsdb_idl_condition *condition) - { -+ struct ovsdb_idl_condition *table_cond; - struct ovsdb_idl_table *table = ovsdb_idl_db_table_from_class(db, tc); - unsigned int seqno = db->cond_seqno; -- if (!ovsdb_idl_condition_equals(condition, &table->condition)) { -- ovsdb_idl_condition_destroy(&table->condition); -- ovsdb_idl_condition_clone(&table->condition, condition); -- db->cond_changed = table->cond_changed = true; -+ -+ /* Compare the new condition to the last known condition which can be -+ * either "new" (not sent yet), "requested" or "acked", in this order. -+ */ -+ if (table->new_cond) { -+ table_cond = table->new_cond; -+ } else if (table->req_cond) { -+ table_cond = table->req_cond; -+ } else { -+ table_cond = table->ack_cond; -+ } -+ ovs_assert(table_cond); -+ -+ if (!ovsdb_idl_condition_equals(condition, table_cond)) { -+ ovsdb_idl_condition_clone(&table->new_cond, condition); -+ db->cond_changed = true; - poll_immediate_wake(); - return seqno + 1; - } -@@ -1563,9 +1610,8 @@ ovsdb_idl_condition_to_json(const struct ovsdb_idl_condition *cnd) - } - - static struct json * --ovsdb_idl_create_cond_change_req(struct ovsdb_idl_table *table) -+ovsdb_idl_create_cond_change_req(const struct ovsdb_idl_condition *cond) - { -- const struct ovsdb_idl_condition *cond = &table->condition; - struct json *monitor_cond_change_request = json_object_create(); - struct json *cond_json = ovsdb_idl_condition_to_json(cond); - -@@ -1585,8 +1631,12 @@ ovsdb_idl_db_compose_cond_change(struct ovsdb_idl_db *db) - for (size_t i = 0; i < db->class_->n_tables; i++) { - struct ovsdb_idl_table *table = &db->tables[i]; - -- if (table->cond_changed) { -- struct json *req = ovsdb_idl_create_cond_change_req(table); -+ /* Always use the most recent conditions set by the IDL client when -+ * requesting monitor_cond_change, i.e., table->new_cond. -+ */ -+ if (table->new_cond) { -+ struct json *req = -+ ovsdb_idl_create_cond_change_req(table->new_cond); - if (req) { - if (!monitor_cond_change_requests) { - monitor_cond_change_requests = json_object_create(); -@@ -1595,7 +1645,11 @@ ovsdb_idl_db_compose_cond_change(struct ovsdb_idl_db *db) - table->class_->name, - json_array_create_1(req)); - } -- table->cond_changed = false; -+ /* Mark the new condition as requested by moving it to req_cond. -+ * If there's already requested condition that's a bug. -+ */ -+ ovs_assert(table->req_cond == NULL); -+ ovsdb_idl_condition_move(&table->req_cond, &table->new_cond); - } - } - -@@ -1610,6 +1664,73 @@ ovsdb_idl_db_compose_cond_change(struct ovsdb_idl_db *db) - return jsonrpc_create_request("monitor_cond_change", params, NULL); - } - -+/* Marks all requested table conditions in 'db' as acked by the server. -+ * It should be called when the server replies to monitor_cond_change -+ * requests. -+ */ -+static void -+ovsdb_idl_db_ack_condition(struct ovsdb_idl_db *db) -+{ -+ for (size_t i = 0; i < db->class_->n_tables; i++) { -+ struct ovsdb_idl_table *table = &db->tables[i]; -+ -+ if (table->req_cond) { -+ ovsdb_idl_condition_move(&table->ack_cond, &table->req_cond); -+ } -+ } -+} -+ -+/* Should be called when the IDL fsm is restarted and resyncs table conditions -+ * based on the state the DB is in: -+ * - if a non-zero last_id is available for the DB then upon reconnect -+ * the IDL should first request acked conditions to avoid missing updates -+ * about records that were added before the transaction with -+ * txn-id == last_id. If there were requested condition changes in flight -+ * (i.e., req_cond not NULL) and the IDL client didn't set new conditions -+ * (i.e., new_cond is NULL) then move req_cond to new_cond to trigger a -+ * follow up monitor_cond_change request. -+ * - if there's no last_id available for the DB then it's safe to use the -+ * latest conditions set by the IDL client even if they weren't acked yet. -+ */ -+static void -+ovsdb_idl_db_sync_condition(struct ovsdb_idl_db *db) -+{ -+ bool ack_all = uuid_is_zero(&db->last_id); -+ -+ db->cond_changed = false; -+ for (size_t i = 0; i < db->class_->n_tables; i++) { -+ struct ovsdb_idl_table *table = &db->tables[i]; -+ -+ /* When monitor_cond_since requests will be issued, the -+ * table->ack_cond condition will be added to the "where" clause". -+ * Follow up monitor_cond_change requests will use table->new_cond. -+ */ -+ if (ack_all) { -+ if (table->new_cond) { -+ ovsdb_idl_condition_move(&table->req_cond, &table->new_cond); -+ } -+ -+ if (table->req_cond) { -+ ovsdb_idl_condition_move(&table->ack_cond, &table->req_cond); -+ } -+ } else { -+ /* If there was no "unsent" condition but instead a -+ * monitor_cond_change request was in flight, move table->req_cond -+ * to table->new_cond and set db->cond_changed to trigger a new -+ * monitor_cond_change request. -+ * -+ * However, if a new condition has been set by the IDL client, -+ * monitor_cond_change will be sent anyway and will use the most -+ * recent table->new_cond so there's no need to update it here. -+ */ -+ if (table->req_cond && !table->new_cond) { -+ ovsdb_idl_condition_move(&table->new_cond, &table->req_cond); -+ db->cond_changed = true; -+ } -+ } -+ } -+} -+ - static void - ovsdb_idl_send_cond_change(struct ovsdb_idl *idl) - { -@@ -2064,13 +2185,15 @@ ovsdb_idl_send_monitor_request(struct ovsdb_idl *idl, struct ovsdb_idl_db *db, - monitor_request = json_object_create(); - json_object_put(monitor_request, "columns", columns); - -- const struct ovsdb_idl_condition *cond = &table->condition; -+ /* Always use acked conditions when requesting -+ * monitor_cond/monitor_cond_since. -+ */ -+ const struct ovsdb_idl_condition *cond = table->ack_cond; - if ((monitor_method == OVSDB_IDL_MM_MONITOR_COND || - monitor_method == OVSDB_IDL_MM_MONITOR_COND_SINCE) && -- !ovsdb_idl_condition_is_true(cond)) { -+ cond && !ovsdb_idl_condition_is_true(cond)) { - json_object_put(monitor_request, "where", - ovsdb_idl_condition_to_json(cond)); -- table->cond_changed = false; - } - json_object_put(monitor_requests, tc->name, - json_array_create_1(monitor_request)); -@@ -2078,8 +2201,6 @@ ovsdb_idl_send_monitor_request(struct ovsdb_idl *idl, struct ovsdb_idl_db *db, - } - free_schema(schema); - -- db->cond_changed = false; -- - struct json *params = json_array_create_3( - json_string_create(db->class_->database), - json_clone(db->monitor_id), -diff --git a/openvswitch-2.13.0/tests/ovsdb-idl.at b/openvswitch-2.13.0/tests/ovsdb-idl.at -index cc38d69..a5ca966 100644 ---- a/openvswitch-2.13.0/tests/ovsdb-idl.at -+++ b/openvswitch-2.13.0/tests/ovsdb-idl.at -@@ -1814,3 +1814,59 @@ m4_define([OVSDB_CHECK_IDL_LEADER_ONLY_PY], - - OVSDB_CHECK_IDL_LEADER_ONLY_PY([Check Python IDL connects to leader], 3, ['remote']) - OVSDB_CHECK_IDL_LEADER_ONLY_PY([Check Python IDL reconnects to leader], 3, ['remote' '+remotestop' 'remote']) -+ -+# same as OVSDB_CHECK_IDL but uses C IDL implementation with tcp -+# with multiple remotes. -+m4_define([OVSDB_CHECK_CLUSTER_IDL_C], -+ [AT_SETUP([$1 - C - tcp]) -+ AT_KEYWORDS([ovsdb server idl positive tcp socket $5]) -+ m4_define([LPBK],[127.0.0.1]) -+ AT_CHECK([ovsdb_cluster_start_idltest $2 "ptcp:0:"LPBK]) -+ PARSE_LISTENING_PORT([s1.log], [TCP_PORT_1]) -+ PARSE_LISTENING_PORT([s2.log], [TCP_PORT_2]) -+ PARSE_LISTENING_PORT([s3.log], [TCP_PORT_3]) -+ remotes=tcp:LPBK:$TCP_PORT_1,tcp:LPBK:$TCP_PORT_2,tcp:LPBK:$TCP_PORT_3 -+ -+ m4_if([$3], [], [], -+ [AT_CHECK([ovsdb-client transact $remotes $3], [0], [ignore], [ignore])]) -+ AT_CHECK([test-ovsdb '-vPATTERN:console:test-ovsdb|%c|%m' -vjsonrpc -t10 idl tcp:LPBK:$TCP_PORT_1 $4], -+ [0], [stdout], [ignore]) -+ AT_CHECK([sort stdout | uuidfilt]m4_if([$7],,, [[| $7]]), -+ [0], [$5]) -+ AT_CLEANUP]) -+ -+# Checks that monitor_cond_since works fine when disconnects happen -+# with cond_change requests in flight (i.e., IDL is properly updated). -+OVSDB_CHECK_CLUSTER_IDL_C([simple idl, monitor_cond_since, cluster disconnect], -+ 3, -+ [['["idltest", -+ {"op": "insert", -+ "table": "simple", -+ "row": {"i": 1, -+ "r": 1.0, -+ "b": true}}, -+ {"op": "insert", -+ "table": "simple", -+ "row": {"i": 2, -+ "r": 1.0, -+ "b": true}}]']], -+ [['condition simple []' \ -+ 'condition simple [["i","==",2]]' \ -+ 'condition simple [["i","==",1]]' \ -+ '+reconnect' \ -+ '["idltest", -+ {"op": "update", -+ "table": "simple", -+ "where": [["i", "==", 1]], -+ "row": {"r": 2.0 }}]']], -+ [[000: change conditions -+001: empty -+002: change conditions -+003: i=2 r=1 b=true s= u=<0> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<1> -+004: change conditions -+005: reconnect -+006: i=2 r=1 b=true s= u=<0> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<1> -+007: {"error":null,"result":[{"count":1}]} -+008: i=1 r=2 b=true s= u=<0> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<2> -+009: done -+]]) --- -1.8.3.1 - diff --git a/SOURCES/0001-pinctrl-Fix-incorrect-warning-message-for-multicast-.patch b/SOURCES/0001-pinctrl-Fix-incorrect-warning-message-for-multicast-.patch deleted file mode 100644 index 522da2d..0000000 --- a/SOURCES/0001-pinctrl-Fix-incorrect-warning-message-for-multicast-.patch +++ /dev/null @@ -1,89 +0,0 @@ -From 6a3006116945234a5ad8e88f9cf86a4c4212b1f2 Mon Sep 17 00:00:00 2001 -From: Dumitru Ceara -Date: Thu, 27 Aug 2020 16:47:33 +0200 -Subject: [PATCH] pinctrl: Fix incorrect warning message for multicast querier. - -It's quite common that IP Multicast is enabled only for IPv4 or only for -IPv6. In such cases ovn-controller should not generate warnings. - -Fixes: 677a3ba4d66b ("ovn: Add MLD support.") -Signed-off-by: Dumitru Ceara -Signed-off-by: Numan Siddique - -(cherry-picked from master commit d6d59911412edc20c40c026a1bb8c48ef01800fb) - -(cherry picked from upstream commit be614f0743abe605c4305003c9de6859dd224fe9) - -Change-Id: I9fbd75723fdd163e4129106b378f7fb71a0e6877 ---- - controller/pinctrl.c | 38 +++++++++++++++++++++++--------------- - 1 file changed, 23 insertions(+), 15 deletions(-) - -diff --git a/controller/pinctrl.c b/controller/pinctrl.c -index f72ab70..c8fbd37 100644 ---- a/controller/pinctrl.c -+++ b/controller/pinctrl.c -@@ -4183,21 +4183,11 @@ ip_mcast_snoop_cfg_load(struct ip_mcast_snoop_cfg *cfg, - cfg->seq_no = ip_mcast->seq_no; - - if (querier_enabled) { -- /* Try to parse the source ETH address. */ -- if (!ip_mcast->eth_src || -- !eth_addr_from_string(ip_mcast->eth_src, -- &cfg->query_eth_src)) { -- VLOG_WARN_RL(&rl, -- "IGMP Querier enabled with invalid ETH src address"); -- /* Failed to parse the ETH source address. Disable the querier. */ -- cfg->querier_v4_enabled = false; -- cfg->querier_v6_enabled = false; -- } -- - /* Try to parse the source IPv4 address. */ - if (cfg->querier_v4_enabled) { -- if (!ip_mcast->ip4_src || -- !ip_parse(ip_mcast->ip4_src, &cfg->query_ipv4_src)) { -+ if (!ip_mcast->ip4_src || !ip_mcast->ip4_src[0]) { -+ cfg->querier_v4_enabled = false; -+ } else if (!ip_parse(ip_mcast->ip4_src, &cfg->query_ipv4_src)) { - VLOG_WARN_RL(&rl, - "IGMP Querier enabled with invalid IPv4 " - "src address"); -@@ -4215,8 +4205,9 @@ ip_mcast_snoop_cfg_load(struct ip_mcast_snoop_cfg *cfg, - - /* Try to parse the source IPv6 address. */ - if (cfg->querier_v6_enabled) { -- if (!ip_mcast->ip6_src || -- !ipv6_parse(ip_mcast->ip6_src, &cfg->query_ipv6_src)) { -+ if (!ip_mcast->ip6_src || !ip_mcast->ip6_src[0]) { -+ cfg->querier_v6_enabled = false; -+ } else if (!ipv6_parse(ip_mcast->ip6_src, &cfg->query_ipv6_src)) { - VLOG_WARN_RL(&rl, - "MLD Querier enabled with invalid IPv6 " - "src address"); -@@ -4232,6 +4223,23 @@ ip_mcast_snoop_cfg_load(struct ip_mcast_snoop_cfg *cfg, - cfg->query_ipv6_dst = - (struct in6_addr)IN6ADDR_ALL_HOSTS_INIT; - } -+ -+ if (!cfg->querier_v4_enabled && !cfg->querier_v6_enabled) { -+ VLOG_WARN_RL(&rl, -+ "IGMP Querier enabled without a valid IPv4 or IPv6 " -+ "address"); -+ } -+ -+ /* Try to parse the source ETH address. */ -+ if (!ip_mcast->eth_src || -+ !eth_addr_from_string(ip_mcast->eth_src, -+ &cfg->query_eth_src)) { -+ VLOG_WARN_RL(&rl, -+ "IGMP Querier enabled with invalid ETH src address"); -+ /* Failed to parse the ETH source address. Disable the querier. */ -+ cfg->querier_v4_enabled = false; -+ cfg->querier_v6_enabled = false; -+ } - } - } - --- -1.8.3.1 - diff --git a/SOURCES/0002-Add-support-for-DHCP-domain-search-option-119.patch b/SOURCES/0002-Add-support-for-DHCP-domain-search-option-119.patch deleted file mode 100644 index 594f6f7..0000000 --- a/SOURCES/0002-Add-support-for-DHCP-domain-search-option-119.patch +++ /dev/null @@ -1,309 +0,0 @@ -From a0f09fcde4c1ef4f2971cc4c504d939298aa7ec5 Mon Sep 17 00:00:00 2001 -From: Dhathri Purohith -Date: Wed, 24 Jun 2020 19:16:24 -0700 -Subject: [PATCH 02/22] Add support for DHCP domain search option (119) - -Domain search list is encoded according to the specifications in RFC 1035, -section 4.1.4. - -Change-Id: I43d8e10f90fc64c5d8463f3d41fbaac5a2e1a929 -Signed-off-by: Dhathri Purohith -Signed-off-by: Ankur Sharma -Signed-off-by: Numan Siddique - -(cherry-picked from upmstream master commit d79bb92c4b4938da89aa16a6795c7bcb4e128374) ---- - lib/actions.c | 85 ++++++++++++++++++++++++++++++++++++++++++++- - lib/ovn-l7.h | 3 ++ - northd/ovn-northd.c | 1 + - ovn-nb.xml | 13 +++++++ - ovn-sb.ovsschema | 6 ++-- - ovn-sb.xml | 11 ++++++ - tests/ovn.at | 39 +++++++++++++++++++++ - tests/test-ovn.c | 1 + - 8 files changed, 155 insertions(+), 4 deletions(-) - -diff --git a/lib/actions.c b/lib/actions.c -index 616c93e8a..e4de97c23 100644 ---- a/lib/actions.c -+++ b/lib/actions.c -@@ -2087,7 +2087,8 @@ parse_gen_opt(struct action_context *ctx, struct ovnact_gen_option *o, - return; - } - -- if (!strcmp(o->option->type, "str")) { -+ if (!strcmp(o->option->type, "str") || -+ !strcmp(o->option->type, "domains")) { - if (o->value.type != EXPR_C_STRING) { - lexer_error(ctx->lexer, "%s option %s requires string value.", - opts_type, o->option->name); -@@ -2422,6 +2423,88 @@ encode_put_dhcpv4_option(const struct ovnact_gen_option *o, - opt_header[1] = sizeof(ovs_be32); - ofpbuf_put(ofpacts, &c->value.ipv4, sizeof(ovs_be32)); - } -+ } else if (!strcmp(o->option->type, "domains")) { -+ /* Please refer to RFC 1035, section 4.1.4 for the format of encoding -+ * domain names. Below is an example for encoding a search list -+ * consisting of the "abc.com" and "xyz.abc.com". -+ * -+ * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ -+ * |119|14 | 3 |'a'|'b'|'c'| 3 |'c'|'o'|'m'| 0 |'x'|'y'|'z'|xC0|x00| -+ * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ -+ * -+ * The encoding of "abc.com" ends with 0 to mark the end of the -+ * domain name as required by RFC 1035. -+ * -+ * The encoding of "xyz" (for "xyz.abc.com") ends with the two-octet -+ * compression pointer C000 (hex), which points to offset 0 where -+ * another validly encoded domain name can be found to complete -+ * the name ("abc.com"). -+ * -+ * Encoding adds 2 bytes (one for length and one for delimiter) for -+ * every domain name that is unique. If all the domain names are unique -+ * (which probably never happens in real world), then encoded string -+ * could be longer than the original string. Just to be on the safer -+ * side, allocate the (approx.) worst case length here. -+ */ -+ uint8_t *dns_encoded = xzalloc(2 * strlen(c->string)); -+ uint16_t encode_offset = 0; -+ struct shash label_offset_map = SHASH_INITIALIZER(&label_offset_map); -+ char *domain_list = xstrdup(c->string), *dom_ptr = NULL; -+ char *suffix = xzalloc(strlen(domain_list)); -+ for (char *domain = strtok_r(domain_list, ",", &dom_ptr); -+ domain != NULL; -+ domain = strtok_r(NULL, ",", &dom_ptr)) { -+ if (strlen(domain) > DOMAIN_NAME_MAX_LEN) { -+ VLOG_WARN("Domain names longer than 255 characters are not" -+ "supported"); -+ goto out; -+ } -+ ovs_strlcpy(suffix, domain, strlen(domain)); -+ char *label; -+ for (label = strtok_r(domain, ".", &domain); -+ label != NULL; -+ label = strtok_r(NULL, ".", &domain)) { -+ /* Check if we have already encoded this suffix. -+ * If yes, fill in the reference and break. */ -+ uint16_t *get_offset; -+ get_offset = shash_find_data(&label_offset_map, suffix); -+ if (get_offset != NULL) { -+ ovs_be16 temp = htons(0xc000) | htons(*get_offset); -+ memcpy(dns_encoded + encode_offset, &temp, -+ sizeof(temp)); -+ encode_offset += sizeof(temp); -+ break; -+ } else { -+ /* The suffix was not encoded before, encode it now -+ * and add the offset to the label_offset_map. */ -+ uint16_t *set_offset = xzalloc(sizeof(uint16_t)); -+ *set_offset = encode_offset; -+ shash_add_once(&label_offset_map, suffix, set_offset); -+ -+ uint8_t len = strlen(label); -+ memcpy(dns_encoded + encode_offset, &len, sizeof(uint8_t)); -+ encode_offset += sizeof(uint8_t); -+ memcpy(dns_encoded + encode_offset, label, len); -+ encode_offset += len; -+ } -+ ovs_strlcpy(suffix, domain, strlen(domain)); -+ } -+ /* Add the end marker (0 byte) to determine the end of the -+ * domain. */ -+ if (label == NULL) { -+ uint8_t end = 0; -+ memcpy(dns_encoded + encode_offset, &end, sizeof(uint8_t)); -+ encode_offset += sizeof(uint8_t); -+ } -+ } -+ opt_header[1] = encode_offset; -+ ofpbuf_put(ofpacts, dns_encoded, encode_offset); -+ -+ out: -+ free(suffix); -+ free(domain_list); -+ free(dns_encoded); -+ shash_destroy_free_data(&label_offset_map); - } - } - -diff --git a/lib/ovn-l7.h b/lib/ovn-l7.h -index 22a2153de..cea97b9ce 100644 ---- a/lib/ovn-l7.h -+++ b/lib/ovn-l7.h -@@ -34,6 +34,7 @@ struct gen_opts_map { - size_t code; - }; - -+#define DOMAIN_NAME_MAX_LEN 255 - #define DHCP_BROADCAST_FLAG 0x8000 - - #define DHCP_OPTION(NAME, CODE, TYPE) \ -@@ -81,6 +82,8 @@ struct gen_opts_map { - #define DHCP_OPT_PATH_PREFIX DHCP_OPTION("path_prefix", 210, "str") - #define DHCP_OPT_TFTP_SERVER_ADDRESS \ - DHCP_OPTION("tftp_server_address", 150, "ipv4") -+#define DHCP_OPT_DOMAIN_SEARCH_LIST \ -+ DHCP_OPTION("domain_search_list", 119, "domains") - - #define DHCP_OPT_ARP_CACHE_TIMEOUT \ - DHCP_OPTION("arp_cache_timeout", 35, "uint32") -diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c -index 14be87435..a665d52e9 100644 ---- a/northd/ovn-northd.c -+++ b/northd/ovn-northd.c -@@ -11761,6 +11761,7 @@ static struct gen_opts_map supported_dhcp_opts[] = { - DHCP_OPT_DOMAIN_NAME, - DHCP_OPT_ARP_CACHE_TIMEOUT, - DHCP_OPT_TCP_KEEPALIVE_INTERVAL, -+ DHCP_OPT_DOMAIN_SEARCH_LIST, - }; - - static struct gen_opts_map supported_dhcpv6_opts[] = { -diff --git a/ovn-nb.xml b/ovn-nb.xml -index 8d04d3d3b..0fdc1592b 100644 ---- a/ovn-nb.xml -+++ b/ovn-nb.xml -@@ -2990,6 +2990,19 @@ -

- - -+ -+ -+

-+ These options accept string value which is a comma separated -+ list of domain names. The domain names are encoded based on RFC 1035. -+

-+ -+ -+

-+ The DHCPv4 option code for this option is 119. -+

-+
-+
- - - -diff --git a/ovn-sb.ovsschema b/ovn-sb.ovsschema -index 2ec729b77..99c5de822 100644 ---- a/ovn-sb.ovsschema -+++ b/ovn-sb.ovsschema -@@ -1,7 +1,7 @@ - { - "name": "OVN_Southbound", -- "version": "2.8.1", -- "cksum": "236203406 21905", -+ "version": "2.8.2", -+ "cksum": "464326363 21916", - "tables": { - "SB_Global": { - "columns": { -@@ -218,7 +218,7 @@ - "type": "string", - "enum": ["set", ["bool", "uint8", "uint16", "uint32", - "ipv4", "static_routes", "str", -- "host_id"]]}}}}, -+ "host_id", "domains"]]}}}}, - "isRoot": true}, - "DHCPv6_Options": { - "columns": { -diff --git a/ovn-sb.xml b/ovn-sb.xml -index 2edafd48f..293b0920c 100644 ---- a/ovn-sb.xml -+++ b/ovn-sb.xml -@@ -3287,6 +3287,17 @@ tcp.flags = RST; -

- - -+
value: domains
-+
-+

-+ This indicates that the value of the DHCP option is a domain name -+ or a comma separated list of domain names. -+

-+ -+

-+ Example. "name=domain_search_list", "code=119", "type=domains". -+

-+
-
- - -diff --git a/tests/ovn.at b/tests/ovn.at -index 4e98790af..be677b663 100644 ---- a/tests/ovn.at -+++ b/tests/ovn.at -@@ -1255,6 +1255,12 @@ reg0[15] = put_dhcp_opts(offerip=10.0.0.4,router=10.0.0.1,netmask=255.255.255.0, - reg0[15] = put_dhcp_opts(offerip=10.0.0.4,router=10.0.0.1,netmask=255.255.255.0,mtu=1400,ip_forward_enable=1,default_ttl=121,dns_server={8.8.8.8,7.7.7.7},classless_static_route={30.0.0.0/24,10.0.0.4,40.0.0.0/16,10.0.0.6,0.0.0.0/0,10.0.0.1},ethernet_encap=1,router_discovery=0,tftp_server="tftp_server_test"); - formats as reg0[15] = put_dhcp_opts(offerip = 10.0.0.4, router = 10.0.0.1, netmask = 255.255.255.0, mtu = 1400, ip_forward_enable = 1, default_ttl = 121, dns_server = {8.8.8.8, 7.7.7.7}, classless_static_route = {30.0.0.0/24, 10.0.0.4, 40.0.0.0/16, 10.0.0.6, 0.0.0.0/0, 10.0.0.1}, ethernet_encap = 1, router_discovery = 0, tftp_server = "tftp_server_test"); - encodes as controller(userdata=00.00.00.02.00.00.00.00.00.01.de.10.00.00.00.6f.0a.00.00.04.03.04.0a.00.00.01.01.04.ff.ff.ff.00.1a.02.05.78.13.01.01.17.01.79.06.08.08.08.08.08.07.07.07.07.79.14.18.1e.00.00.0a.00.00.04.10.28.00.0a.00.00.06.00.0a.00.00.01.24.01.01.1f.01.00.42.10.74.66.74.70.5f.73.65.72.76.65.72.5f.74.65.73.74,pause) -+reg2[5] = put_dhcp_opts(offerip=10.0.0.4,router=10.0.0.1,netmask=255.255.254.0,mtu=1400,domain_name="ovn.org",wpad="https://example.org",bootfile_name="https://127.0.0.1/boot.ipxe",path_prefix="/tftpboot",domain_search_list="ovn.org,abc.ovn.org"); -+ formats as reg2[5] = put_dhcp_opts(offerip = 10.0.0.4, router = 10.0.0.1, netmask = 255.255.254.0, mtu = 1400, domain_name = "ovn.org", wpad = "https://example.org", bootfile_name = "https://127.0.0.1/boot.ipxe", path_prefix = "/tftpboot", domain_search_list = "ovn.org,abc.ovn.org"); -+ encodes as controller(userdata=00.00.00.02.00.00.00.00.00.01.de.10.00.00.00.25.0a.00.00.04.03.04.0a.00.00.01.01.04.ff.ff.fe.00.1a.02.05.78.0f.07.6f.76.6e.2e.6f.72.67.fc.13.68.74.74.70.73.3a.2f.2f.65.78.61.6d.70.6c.65.2e.6f.72.67.43.1b.68.74.74.70.73.3a.2f.2f.31.32.37.2e.30.2e.30.2e.31.2f.62.6f.6f.74.2e.69.70.78.65.d2.09.2f.74.66.74.70.62.6f.6f.74.77.0f.03.6f.76.6e.03.6f.72.67.00.03.61.62.63.c0.00,pause) -+reg2[5] = put_dhcp_opts(offerip=10.0.0.4,router=10.0.0.1,netmask=255.255.254.0,mtu=1400,domain_name="ovn.org",wpad="https://example.org",bootfile_name="https://127.0.0.1/boot.ipxe",path_prefix="/tftpboot",domain_search_list="ovn.org,abc.ovn.org,def.ovn.org,ovn.test,def.ovn.test,test.org,abc.com"); -+ formats as reg2[5] = put_dhcp_opts(offerip = 10.0.0.4, router = 10.0.0.1, netmask = 255.255.254.0, mtu = 1400, domain_name = "ovn.org", wpad = "https://example.org", bootfile_name = "https://127.0.0.1/boot.ipxe", path_prefix = "/tftpboot", domain_search_list = "ovn.org,abc.ovn.org,def.ovn.org,ovn.test,def.ovn.test,test.org,abc.com"); -+ encodes as controller(userdata=00.00.00.02.00.00.00.00.00.01.de.10.00.00.00.25.0a.00.00.04.03.04.0a.00.00.01.01.04.ff.ff.fe.00.1a.02.05.78.0f.07.6f.76.6e.2e.6f.72.67.fc.13.68.74.74.70.73.3a.2f.2f.65.78.61.6d.70.6c.65.2e.6f.72.67.43.1b.68.74.74.70.73.3a.2f.2f.31.32.37.2e.30.2e.30.2e.31.2f.62.6f.6f.74.2e.69.70.78.65.d2.09.2f.74.66.74.70.62.6f.6f.74.77.35.03.6f.76.6e.03.6f.72.67.00.03.61.62.63.c0.00.03.64.65.66.c0.00.03.6f.76.6e.04.74.65.73.74.00.03.64.65.66.c0.15.04.74.65.73.74.c0.04.03.61.62.63.03.63.6f.6d.00,pause) - - reg1[0..1] = put_dhcp_opts(offerip = 1.2.3.4, router = 10.0.0.1); - Cannot use 2-bit field reg1[0..1] where 1-bit field is required. -@@ -1272,6 +1278,8 @@ reg1[0] = put_dhcp_opts(offerip="xyzzy"); - DHCPv4 option offerip requires numeric value. - reg1[0] = put_dhcp_opts(offerip=1.2.3.4, domain_name=1.2.3.4); - DHCPv4 option domain_name requires string value. -+reg1[0] = put_dhcp_opts(offerip=1.2.3.4, domain_search_list=1.2.3.4); -+ DHCPv4 option domain_search_list requires string value. - - # nd_ns - nd_ns { nd.target = xxreg0; output; }; -@@ -5692,6 +5700,37 @@ AT_CHECK([cat 2.packets | cut -c -48], [0], [expout]) - cat 2.expected | cut -c 53- > expout - AT_CHECK([cat 2.packets | cut -c 53-], [0], [expout]) - -+reset_pcap_file hv1-vif1 hv1/vif1 -+reset_pcap_file hv1-vif2 hv1/vif2 -+rm -f 1.expected -+rm -f 2.expected -+ -+# Set domain search list option for ls1 -+echo "------ Set domain search list --------" -+ovn-nbctl dhcp-options-set-options $d1 server_id=10.0.0.1 \ -+server_mac=ff:10:00:00:00:01 lease_time=3600 router=10.0.0.1 \ -+domain_search_list=\"test1.com,test2.com\" -+echo "------------------------------------------" -+ -+# Send DHCPREQUEST in the SELECTING/INIT-REBOOT state with the offered IP -+# address in the Requested IP Address option. -+offer_ip=`ip_to_hex 10 0 0 6` -+server_ip=`ip_to_hex 10 0 0 1` -+ciaddr=`ip_to_hex 0 0 0 0` -+request_ip=$offer_ip -+expected_dhcp_opts=771305746573743103636f6d00057465737432c006330400000e100104ffffff0003040a00000136040a000001 -+test_dhcp 2 f00000000002 03 0 $ciaddr $offer_ip $request_ip 0 ff1000000001 $server_ip 05 $expected_dhcp_opts -+ -+# NXT_RESUMEs should be 16. -+OVS_WAIT_UNTIL([test 16 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`]) -+ -+$PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > 2.packets -+cat 2.expected | cut -c -48 > expout -+AT_CHECK([cat 2.packets | cut -c -48], [0], [expout]) -+# Skipping the IPv4 checksum. -+cat 2.expected | cut -c 53- > expout -+AT_CHECK([cat 2.packets | cut -c 53-], [0], [expout]) -+ - OVN_CLEANUP([hv1]) - - AT_CLEANUP -diff --git a/tests/test-ovn.c b/tests/test-ovn.c -index 29d343b60..ba628288b 100644 ---- a/tests/test-ovn.c -+++ b/tests/test-ovn.c -@@ -190,6 +190,7 @@ create_gen_opts(struct hmap *dhcp_opts, struct hmap *dhcpv6_opts, - dhcp_opt_add(dhcp_opts, "tftp_server_address", 150, "ipv4"); - dhcp_opt_add(dhcp_opts, "arp_cache_timeout", 35, "uint32"); - dhcp_opt_add(dhcp_opts, "tcp_keepalive_interval", 38, "uint32"); -+ dhcp_opt_add(dhcp_opts, "domain_search_list", 119, "domains"); - - /* DHCPv6 options. */ - hmap_init(dhcpv6_opts); --- -2.26.2 - diff --git a/SOURCES/0002-chassis-Fix-chassis_private-record-updates-when-the-.patch b/SOURCES/0002-chassis-Fix-chassis_private-record-updates-when-the-.patch deleted file mode 100644 index b9e4333..0000000 --- a/SOURCES/0002-chassis-Fix-chassis_private-record-updates-when-the-.patch +++ /dev/null @@ -1,274 +0,0 @@ -From 59103ff841797ad399e1679bfecfb6256bb6a0c4 Mon Sep 17 00:00:00 2001 -From: Dumitru Ceara -Date: Thu, 3 Sep 2020 17:04:01 +0200 -Subject: [PATCH 2/2] chassis: Fix chassis_private record updates when the - system-id changes. - -Also: -- Change conditional monitoring for Chassis_Private to use the chassis uuid - instead of chassis name. Using the chassis->name field does not work - because this is the old value of the field and would cause ovsdb-server - to inform ovn-controller that the updated Chassis_Private record was - "deleted" because it doesn't match the monitor condition anymore. -- Allow ovn-sbctl to access Chassis_Private records by name. - -Reported-at: https://bugzilla.redhat.com/1873032 -Reported-by: Ying Xu -CC: Han Zhou -Fixes: 4adc10f58127 ("Avoid nb_cfg update notification flooding") -Signed-off-by: Dumitru Ceara -Signed-off-by: Numan Siddique -(cherry picked from upstream commit dce1af31b550a9fb57b01cbe0b4139b6768f2521) - -Change-Id: Ic5f7f0b820b43715e1f1cf68b215374daf237fd5 ---- - controller/chassis.c | 92 +++++++++++++++++++++++++++++++++++++++------ - controller/chassis.h | 2 + - controller/ovn-controller.c | 13 ++++--- - tests/ovn-controller.at | 18 +++++++++ - utilities/ovn-sbctl.c | 3 ++ - 5 files changed, 112 insertions(+), 16 deletions(-) - -diff --git a/controller/chassis.c b/controller/chassis.c -index 773d966..8231169 100644 ---- a/controller/chassis.c -+++ b/controller/chassis.c -@@ -633,6 +633,77 @@ chassis_update(const struct sbrec_chassis *chassis_rec, - return true; - } - -+/* -+ * Returns a pointer to a chassis_private record from 'chassis_pvt_table' that -+ * matches the chassis record. -+ */ -+static const struct sbrec_chassis_private * -+chassis_private_get_stale_record( -+ const struct sbrec_chassis_private_table *chassis_pvt_table, -+ const struct sbrec_chassis *chassis) -+{ -+ const struct sbrec_chassis_private *chassis_pvt_rec; -+ -+ SBREC_CHASSIS_PRIVATE_TABLE_FOR_EACH (chassis_pvt_rec, chassis_pvt_table) { -+ if (chassis_pvt_rec->chassis == chassis) { -+ return chassis_pvt_rec; -+ } -+ } -+ -+ return NULL; -+} -+ -+/* If this is a chassis_private config update after we initialized the record -+ * once then we should always be able to find it with the ID we saved in -+ * chassis_state. -+ * Otherwise (i.e., first time we created the chassis record or if the -+ * system-id changed) then we check if there's a stale record from a previous -+ * controller run that didn't end gracefully and reuse it. If not then we -+ * create a new record. -+ * -+ * Returns the local chassis record. -+ */ -+static const struct sbrec_chassis_private * -+chassis_private_get_record( -+ struct ovsdb_idl_txn *ovnsb_idl_txn, -+ struct ovsdb_idl_index *sbrec_chassis_pvt_by_name, -+ const struct sbrec_chassis_private_table *chassis_pvt_table, -+ const struct sbrec_chassis *chassis) -+{ -+ const struct sbrec_chassis_private *chassis_p = NULL; -+ -+ if (chassis_info_id_inited(&chassis_state)) { -+ chassis_p = -+ chassis_private_lookup_by_name(sbrec_chassis_pvt_by_name, -+ chassis_info_id(&chassis_state)); -+ } -+ -+ if (!chassis_p) { -+ chassis_p = chassis_private_get_stale_record(chassis_pvt_table, -+ chassis); -+ } -+ -+ if (!chassis_p && ovnsb_idl_txn) { -+ return sbrec_chassis_private_insert(ovnsb_idl_txn); -+ } -+ -+ return chassis_p; -+} -+ -+static void -+chassis_private_update(const struct sbrec_chassis_private *chassis_pvt, -+ const struct sbrec_chassis *chassis, -+ const char *chassis_id) -+{ -+ if (!chassis_pvt->name || strcmp(chassis_pvt->name, chassis_id)) { -+ sbrec_chassis_private_set_name(chassis_pvt, chassis_id); -+ } -+ -+ if (chassis_pvt->chassis != chassis) { -+ sbrec_chassis_private_set_chassis(chassis_pvt, chassis); -+ } -+} -+ - /* Returns this chassis's Chassis record, if it is available. */ - const struct sbrec_chassis * - chassis_run(struct ovsdb_idl_txn *ovnsb_idl_txn, -@@ -640,6 +711,7 @@ chassis_run(struct ovsdb_idl_txn *ovnsb_idl_txn, - struct ovsdb_idl_index *sbrec_chassis_private_by_name, - const struct ovsrec_open_vswitch_table *ovs_table, - const struct sbrec_chassis_table *chassis_table, -+ const struct sbrec_chassis_private_table *chassis_pvt_table, - const char *chassis_id, - const struct ovsrec_bridge *br_int, - const struct sset *transport_zones, -@@ -668,7 +740,6 @@ chassis_run(struct ovsdb_idl_txn *ovnsb_idl_txn, - bool updated = chassis_update(chassis_rec, ovnsb_idl_txn, &ovs_cfg, - chassis_id, transport_zones); - -- chassis_info_set_id(&chassis_state, chassis_id); - if (!existed || updated) { - ovsdb_idl_txn_add_comment(ovnsb_idl_txn, - "ovn-controller: %s chassis '%s'", -@@ -676,17 +747,16 @@ chassis_run(struct ovsdb_idl_txn *ovnsb_idl_txn, - chassis_id); - } - -- const struct sbrec_chassis_private *chassis_private_rec = -- chassis_private_lookup_by_name(sbrec_chassis_private_by_name, -- chassis_id); -- if (!chassis_private_rec && ovnsb_idl_txn) { -- chassis_private_rec = sbrec_chassis_private_insert(ovnsb_idl_txn); -- sbrec_chassis_private_set_name(chassis_private_rec, -- chassis_id); -- sbrec_chassis_private_set_chassis(chassis_private_rec, -- chassis_rec); -+ *chassis_private = -+ chassis_private_get_record(ovnsb_idl_txn, -+ sbrec_chassis_private_by_name, -+ chassis_pvt_table, chassis_rec); -+ -+ if (*chassis_private) { -+ chassis_private_update(*chassis_private, chassis_rec, chassis_id); - } -- *chassis_private = chassis_private_rec; -+ -+ chassis_info_set_id(&chassis_state, chassis_id); - } - - ovs_chassis_cfg_destroy(&ovs_cfg); -diff --git a/controller/chassis.h b/controller/chassis.h -index 81055b4..220f726 100644 ---- a/controller/chassis.h -+++ b/controller/chassis.h -@@ -26,6 +26,7 @@ struct ovsrec_bridge; - struct ovsrec_open_vswitch_table; - struct sbrec_chassis; - struct sbrec_chassis_table; -+struct sbrec_chassis_private_table; - struct sset; - struct eth_addr; - struct smap; -@@ -37,6 +38,7 @@ const struct sbrec_chassis *chassis_run( - struct ovsdb_idl_index *sbrec_chassis_private_by_name, - const struct ovsrec_open_vswitch_table *, - const struct sbrec_chassis_table *, -+ const struct sbrec_chassis_private_table *, - const char *chassis_id, const struct ovsrec_bridge *br_int, - const struct sset *transport_zones, - const struct sbrec_chassis_private **chassis_private); -diff --git a/controller/ovn-controller.c b/controller/ovn-controller.c -index 28ca7a8..6aeeb15 100644 ---- a/controller/ovn-controller.c -+++ b/controller/ovn-controller.c -@@ -179,7 +179,7 @@ update_sb_monitors(struct ovsdb_idl *ovnsb_idl, - * chassis */ - sbrec_port_binding_add_clause_type(&pb, OVSDB_F_EQ, "chassisredirect"); - sbrec_port_binding_add_clause_type(&pb, OVSDB_F_EQ, "external"); -- if (chassis) { -+ if (chassis && !sbrec_chassis_is_new(chassis)) { - /* This should be mostly redundant with the other clauses for port - * bindings, but it allows us to catch any ports that are assigned to - * us but should not be. That way, we can clear their chassis -@@ -202,9 +202,9 @@ update_sb_monitors(struct ovsdb_idl *ovnsb_idl, - sbrec_igmp_group_add_clause_chassis(&igmp, OVSDB_F_EQ, - &chassis->header_.uuid); - -- /* Monitors Chassis_Private record for current chassis only */ -- sbrec_chassis_private_add_clause_name(&chprv, OVSDB_F_EQ, -- chassis->name); -+ /* Monitors Chassis_Private record for current chassis only. */ -+ sbrec_chassis_private_add_clause_chassis(&chprv, OVSDB_F_EQ, -+ &chassis->header_.uuid); - } else { - /* During initialization, we monitor all records in Chassis_Private so - * that we don't try to recreate existing ones. */ -@@ -2402,6 +2402,8 @@ main(int argc, char *argv[]) - ovsrec_open_vswitch_table_get(ovs_idl_loop.idl); - const struct sbrec_chassis_table *chassis_table = - sbrec_chassis_table_get(ovnsb_idl_loop.idl); -+ const struct sbrec_chassis_private_table *chassis_pvt_table = -+ sbrec_chassis_private_table_get(ovnsb_idl_loop.idl); - const struct ovsrec_bridge *br_int = - process_br_int(ovs_idl_txn, bridge_table, ovs_table); - const char *chassis_id = get_ovs_chassis_id(ovs_table); -@@ -2410,7 +2412,8 @@ main(int argc, char *argv[]) - if (chassis_id) { - chassis = chassis_run(ovnsb_idl_txn, sbrec_chassis_by_name, - sbrec_chassis_private_by_name, -- ovs_table, chassis_table, chassis_id, -+ ovs_table, chassis_table, -+ chassis_pvt_table, chassis_id, - br_int, &transport_zones, - &chassis_private); - } -diff --git a/tests/ovn-controller.at b/tests/ovn-controller.at -index f2faf1f..812946b 100644 ---- a/tests/ovn-controller.at -+++ b/tests/ovn-controller.at -@@ -195,6 +195,15 @@ OVS_WAIT_UNTIL([ - chassis_id=$(ovn-sbctl get Chassis "${sysid}" name) - test "${sysid}" = "${chassis_id}" - ]) -+OVS_WAIT_UNTIL([ -+ chassis_id=$(ovn-sbctl get Chassis_Private "${sysid}" name) -+ test "${sysid}" = "${chassis_id}" -+]) -+ -+# Only one Chassis_Private record should exist. -+OVS_WAIT_UNTIL([ -+ test $(ovn-sbctl --columns _uuid list chassis_private | wc -l) -eq 1 -+]) - - # Simulate system-id changing while ovn-controller is disconnected from the - # SB. -@@ -212,6 +221,15 @@ OVS_WAIT_UNTIL([ - chassis_id=$(ovn-sbctl get Chassis "${sysid}" name) - test "${sysid}" = "${chassis_id}" - ]) -+OVS_WAIT_UNTIL([ -+ chassis_id=$(ovn-sbctl get Chassis_Private "${sysid}" name) -+ test "${sysid}" = "${chassis_id}" -+]) -+ -+# Only one Chassis_Private record should exist. -+OVS_WAIT_UNTIL([ -+ test $(ovn-sbctl --columns _uuid list chassis_private | wc -l) -eq 1 -+]) - - # Gracefully terminate daemons - OVN_CLEANUP_SBOX([hv]) -diff --git a/utilities/ovn-sbctl.c b/utilities/ovn-sbctl.c -index 04e082c..d3eec91 100644 ---- a/utilities/ovn-sbctl.c -+++ b/utilities/ovn-sbctl.c -@@ -1391,6 +1391,9 @@ cmd_set_ssl(struct ctl_context *ctx) - static const struct ctl_table_class tables[SBREC_N_TABLES] = { - [SBREC_TABLE_CHASSIS].row_ids[0] = {&sbrec_chassis_col_name, NULL, NULL}, - -+ [SBREC_TABLE_CHASSIS_PRIVATE].row_ids[0] -+ = {&sbrec_chassis_private_col_name, NULL, NULL}, -+ - [SBREC_TABLE_DATAPATH_BINDING].row_ids - = {{&sbrec_datapath_binding_col_external_ids, "name", NULL}, - {&sbrec_datapath_binding_col_external_ids, "name2", NULL}, --- -1.8.3.1 - diff --git a/SOURCES/0002-ovn-controller-Persist-the-conjunction-ids-allocated.patch b/SOURCES/0002-ovn-controller-Persist-the-conjunction-ids-allocated.patch deleted file mode 100644 index 6410d70..0000000 --- a/SOURCES/0002-ovn-controller-Persist-the-conjunction-ids-allocated.patch +++ /dev/null @@ -1,745 +0,0 @@ -From dc36d5ee7b510e9b258037be454460dea53c00a5 Mon Sep 17 00:00:00 2001 -From: Numan Siddique -Date: Tue, 18 Aug 2020 20:16:07 +0530 -Subject: [PATCH 2/2] ovn-controller: Persist the conjunction ids allocated for - conjuctive matches. - -For a logical flow which results in conjuctive OF matches, we are not persisting -the allocated conjunction ids for it. There are few side effects because of this. - - When a port group or address set gets modified, the logical flows which references - these port groups/address sets gets reprocessed again and the resulting OpenvSwitch flows - with conjunctive matches gets modified in the vswitchd if the conjunction id changes. - - - And because of this there is small probability of a packet getting dropped when the - OF flows gets updated with different conjunction ids. - -This patch fixes this issue by persisting the conjunction ids. Earlier, logical flow caching -support was added [1] to ovn-controller and then reverted [2] later due to some issues. - -This patch takes the lflow caching approach to persist the conjunction ids. But it only -creates the cache for logical flows which result in conjunctive matches. And it doesn't -cache the expr tree. - -The lflow caching is made configurable and enabled by default. Any user can disable caching -by setting 'ovn-enable-lflow-cache' to 'false' in the OVS db. - - ovs-vsctl set open . external_ids:ovn-enable-lflow-cache=false - -Note: Changing the option 'ovn-enable-lflow-cache' doesn't result in full recompute of -I-P engine. But ovn-controller updates the chassis.other_config column. And when it -receives the update2/update3 message, this results in full recompute (as any chassis -changes results in full recompute). This is the case with all the config options in OVS db. - -An upcoming patch series, will attempt to add back the expr caching (addressing all the issues.) - -[1] - 8795bec737b("ovn-controller: Cache logical flow expr tree for each lflow.) -[2] - 065bcf46218("ovn-controller: Revert lflow expr caching") - -Reported-at: https://bugzilla.redhat.com/show_bug.cgi?id=1858878 -Acked-by: Mark Michelson -Signed-off-by: Numan Siddique - -(cherry-picked from upstream master commit 2662498bfd13ccb13defd1c800f449be7e271abe) -Conflicts: - controller/lflow.c - -Change-Id: I8a8de80e6bc3f7d9935266fbf466c2aab3e30c8c ---- - controller/chassis.c | 22 +++++- - controller/lflow.c | 118 ++++++++++++++++++++++++++++++- - controller/lflow.h | 8 ++- - controller/ovn-controller.c | 97 +++++++++++++++++++++++--- - tests/ovn.at | 135 ++++++++++++++++++++++++++++++++++++ - 5 files changed, 363 insertions(+), 17 deletions(-) - -diff --git a/controller/chassis.c b/controller/chassis.c -index 823116926..e77fb0849 100644 ---- a/controller/chassis.c -+++ b/controller/chassis.c -@@ -86,6 +86,7 @@ struct ovs_chassis_cfg { - const char *cms_options; - const char *monitor_all; - const char *chassis_macs; -+ const char *enable_lflow_cache; - - /* Set of encap types parsed from the 'ovn-encap-type' external-id. */ - struct sset encap_type_set; -@@ -165,6 +166,12 @@ get_monitor_all(const struct smap *ext_ids) - return smap_get_def(ext_ids, "ovn-monitor-all", "false"); - } - -+static const char * -+get_enable_lflow_cache(const struct smap *ext_ids) -+{ -+ return smap_get_def(ext_ids, "ovn-enable-lflow-cache", "true"); -+} -+ - static const char * - get_encap_csum(const struct smap *ext_ids) - { -@@ -284,6 +291,7 @@ chassis_parse_ovs_config(const struct ovsrec_open_vswitch_table *ovs_table, - ovs_cfg->cms_options = get_cms_options(&cfg->external_ids); - ovs_cfg->monitor_all = get_monitor_all(&cfg->external_ids); - ovs_cfg->chassis_macs = get_chassis_mac_mappings(&cfg->external_ids); -+ ovs_cfg->enable_lflow_cache = get_enable_lflow_cache(&cfg->external_ids); - - if (!chassis_parse_ovs_encap_type(encap_type, &ovs_cfg->encap_type_set)) { - return false; -@@ -310,12 +318,14 @@ static void - chassis_build_other_config(struct smap *config, const char *bridge_mappings, - const char *datapath_type, const char *cms_options, - const char *monitor_all, const char *chassis_macs, -- const char *iface_types, bool is_interconn) -+ const char *iface_types, -+ const char *enable_lflow_cache, bool is_interconn) - { - smap_replace(config, "ovn-bridge-mappings", bridge_mappings); - smap_replace(config, "datapath-type", datapath_type); - smap_replace(config, "ovn-cms-options", cms_options); - smap_replace(config, "ovn-monitor-all", monitor_all); -+ smap_replace(config, "ovn-enable-lflow-cache", enable_lflow_cache); - smap_replace(config, "iface-types", iface_types); - smap_replace(config, "ovn-chassis-mac-mappings", chassis_macs); - smap_replace(config, "is-interconn", is_interconn ? "true" : "false"); -@@ -330,6 +340,7 @@ chassis_other_config_changed(const char *bridge_mappings, - const char *cms_options, - const char *monitor_all, - const char *chassis_macs, -+ const char *enable_lflow_cache, - const struct ds *iface_types, - bool is_interconn, - const struct sbrec_chassis *chassis_rec) -@@ -362,6 +373,13 @@ chassis_other_config_changed(const char *bridge_mappings, - return true; - } - -+ const char *chassis_enable_lflow_cache = -+ get_enable_lflow_cache(&chassis_rec->other_config); -+ -+ if (strcmp(enable_lflow_cache, chassis_enable_lflow_cache)) { -+ return true; -+ } -+ - const char *chassis_mac_mappings = - get_chassis_mac_mappings(&chassis_rec->other_config); - if (strcmp(chassis_macs, chassis_mac_mappings)) { -@@ -586,6 +604,7 @@ chassis_update(const struct sbrec_chassis *chassis_rec, - ovs_cfg->cms_options, - ovs_cfg->monitor_all, - ovs_cfg->chassis_macs, -+ ovs_cfg->enable_lflow_cache, - &ovs_cfg->iface_types, - ovs_cfg->is_interconn, - chassis_rec)) { -@@ -598,6 +617,7 @@ chassis_update(const struct sbrec_chassis *chassis_rec, - ovs_cfg->monitor_all, - ovs_cfg->chassis_macs, - ds_cstr_ro(&ovs_cfg->iface_types), -+ ovs_cfg->enable_lflow_cache, - ovs_cfg->is_interconn); - sbrec_chassis_verify_other_config(chassis_rec); - sbrec_chassis_set_other_config(chassis_rec, &other_config); -diff --git a/controller/lflow.c b/controller/lflow.c -index 151561210..c2f939f90 100644 ---- a/controller/lflow.c -+++ b/controller/lflow.c -@@ -269,6 +269,70 @@ lflow_resource_destroy_lflow(struct lflow_resource_ref *lfrr, - free(lfrn); - } - -+/* Represents an lflow cache which -+ * - stores the conjunction id offset if the lflow matches -+ * results in conjunctive OpenvSwitch flows. -+ */ -+struct lflow_cache { -+ struct hmap_node node; -+ struct uuid lflow_uuid; /* key */ -+ uint32_t conj_id_ofs; -+}; -+ -+static struct lflow_cache * -+lflow_cache_add(struct hmap *lflow_cache_map, -+ const struct sbrec_logical_flow *lflow) -+{ -+ struct lflow_cache *lc = xmalloc(sizeof *lc); -+ lc->lflow_uuid = lflow->header_.uuid; -+ lc->conj_id_ofs = 0; -+ hmap_insert(lflow_cache_map, &lc->node, uuid_hash(&lc->lflow_uuid)); -+ return lc; -+} -+ -+static struct lflow_cache * -+lflow_cache_get(struct hmap *lflow_cache_map, -+ const struct sbrec_logical_flow *lflow) -+{ -+ struct lflow_cache *lc; -+ size_t hash = uuid_hash(&lflow->header_.uuid); -+ HMAP_FOR_EACH_WITH_HASH (lc, node, hash, lflow_cache_map) { -+ if (uuid_equals(&lc->lflow_uuid, &lflow->header_.uuid)) { -+ return lc; -+ } -+ } -+ -+ return NULL; -+} -+ -+static void -+lflow_cache_delete(struct hmap *lflow_cache_map, -+ const struct sbrec_logical_flow *lflow) -+{ -+ struct lflow_cache *lc = lflow_cache_get(lflow_cache_map, lflow); -+ if (lc) { -+ hmap_remove(lflow_cache_map, &lc->node); -+ free(lc); -+ } -+} -+ -+void -+lflow_cache_init(struct hmap *lflow_cache_map) -+{ -+ hmap_init(lflow_cache_map); -+} -+ -+void -+lflow_cache_destroy(struct hmap *lflow_cache_map) -+{ -+ struct lflow_cache *lc; -+ HMAP_FOR_EACH_POP (lc, node, lflow_cache_map) { -+ free(lc); -+ } -+ -+ hmap_destroy(lflow_cache_map); -+} -+ - /* Adds the logical flows from the Logical_Flow table to flow tables. */ - static void - add_logical_flows(struct lflow_ctx_in *l_ctx_in, -@@ -306,6 +370,7 @@ add_logical_flows(struct lflow_ctx_in *l_ctx_in, - static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 5); - VLOG_ERR_RL(&rl, "Conjunction id overflow when processing lflow " - UUID_FMT, UUID_ARGS(&lflow->header_.uuid)); -+ l_ctx_out->conj_id_overflow = true; - } - } - -@@ -355,6 +420,9 @@ lflow_handle_changed_flows(struct lflow_ctx_in *l_ctx_in, - /* Delete entries from lflow resource reference. */ - lflow_resource_destroy_lflow(l_ctx_out->lfrr, - &lflow->header_.uuid); -+ if (l_ctx_out->lflow_cache_map) { -+ lflow_cache_delete(l_ctx_out->lflow_cache_map, lflow); -+ } - } - } - -@@ -382,6 +450,7 @@ lflow_handle_changed_flows(struct lflow_ctx_in *l_ctx_in, - &nd_ra_opts, &controller_event_opts, - l_ctx_in, l_ctx_out)) { - ret = false; -+ l_ctx_out->conj_id_overflow = true; - break; - } - } -@@ -467,6 +536,7 @@ lflow_handle_changed_ref(enum ref_type ref_type, const char *ref_name, - &nd_ra_opts, &controller_event_opts, - l_ctx_in, l_ctx_out)) { - ret = false; -+ l_ctx_out->conj_id_overflow = true; - break; - } - *changed = true; -@@ -652,13 +722,41 @@ consider_logical_flow(const struct sbrec_logical_flow *lflow, - ovnacts_free(ovnacts.data, ovnacts.size); - ofpbuf_uninit(&ovnacts); - -+ uint32_t conj_id_ofs = *l_ctx_out->conj_id_ofs; -+ if (n_conjs) { -+ if (l_ctx_out->lflow_cache_map) { -+ struct lflow_cache *lc = -+ lflow_cache_get(l_ctx_out->lflow_cache_map, lflow); -+ if (!lc) { -+ lc = lflow_cache_add(l_ctx_out->lflow_cache_map, lflow); -+ } -+ -+ if (!lc->conj_id_ofs) { -+ lc->conj_id_ofs = *l_ctx_out->conj_id_ofs; -+ if (!update_conj_id_ofs(l_ctx_out->conj_id_ofs, n_conjs)) { -+ lc->conj_id_ofs = 0; -+ expr_matches_destroy(&matches); -+ return false; -+ } -+ } -+ -+ conj_id_ofs = lc->conj_id_ofs; -+ } else { -+ /* lflow caching is disabled. */ -+ if (!update_conj_id_ofs(l_ctx_out->conj_id_ofs, n_conjs)) { -+ expr_matches_destroy(&matches); -+ return false; -+ } -+ } -+ } -+ - /* Prepare the OpenFlow matches for adding to the flow table. */ - struct expr_match *m; - HMAP_FOR_EACH (m, hmap_node, &matches) { - match_set_metadata(&m->match, - htonll(lflow->logical_datapath->tunnel_key)); - if (m->match.wc.masks.conj_id) { -- m->match.flow.conj_id += *l_ctx_out->conj_id_ofs; -+ m->match.flow.conj_id += conj_id_ofs; - } - if (datapath_is_switch(ldp)) { - unsigned int reg_index -@@ -693,7 +791,7 @@ consider_logical_flow(const struct sbrec_logical_flow *lflow, - struct ofpact_conjunction *dst; - - dst = ofpact_put_CONJUNCTION(&conj); -- dst->id = src->id + *l_ctx_out->conj_id_ofs; -+ dst->id = src->id + conj_id_ofs; - dst->clause = src->clause; - dst->n_clauses = src->n_clauses; - } -@@ -708,7 +806,7 @@ consider_logical_flow(const struct sbrec_logical_flow *lflow, - /* Clean up. */ - expr_matches_destroy(&matches); - ofpbuf_uninit(&ofpacts); -- return update_conj_id_ofs(l_ctx_out->conj_id_ofs, n_conjs); -+ return true; - } - - static void -@@ -858,6 +956,19 @@ lflow_run(struct lflow_ctx_in *l_ctx_in, struct lflow_ctx_out *l_ctx_out) - { - COVERAGE_INC(lflow_run); - -+ /* when lflow_run is called, it's possible that some of the logical flows -+ * are deleted. We need to delete the lflow cache for these -+ * lflows (if present), otherwise, they will not be deleted at all. */ -+ if (l_ctx_out->lflow_cache_map) { -+ const struct sbrec_logical_flow *lflow; -+ SBREC_LOGICAL_FLOW_TABLE_FOR_EACH_TRACKED (lflow, -+ l_ctx_in->logical_flow_table) { -+ if (sbrec_logical_flow_is_deleted(lflow)) { -+ lflow_cache_delete(l_ctx_out->lflow_cache_map, lflow); -+ } -+ } -+ } -+ - add_logical_flows(l_ctx_in, l_ctx_out); - add_neighbor_flows(l_ctx_in->sbrec_port_binding_by_name, - l_ctx_in->mac_binding_table, l_ctx_in->local_datapaths, -@@ -914,6 +1025,7 @@ lflow_add_flows_for_datapath(const struct sbrec_datapath_binding *dp, - &nd_ra_opts, &controller_event_opts, - l_ctx_in, l_ctx_out)) { - handled = false; -+ l_ctx_out->conj_id_overflow = true; - break; - } - } -diff --git a/controller/lflow.h b/controller/lflow.h -index ae02eaf5e..c66b318e9 100644 ---- a/controller/lflow.h -+++ b/controller/lflow.h -@@ -141,12 +141,13 @@ struct lflow_ctx_out { - struct ovn_extend_table *group_table; - struct ovn_extend_table *meter_table; - struct lflow_resource_ref *lfrr; -- struct hmap *lflow_expr_cache; -+ struct hmap *lflow_cache_map; - uint32_t *conj_id_ofs; -+ bool conj_id_overflow; - }; - - void lflow_init(void); --void lflow_run(struct lflow_ctx_in *, struct lflow_ctx_out *); -+void lflow_run(struct lflow_ctx_in *, struct lflow_ctx_out *); - bool lflow_handle_changed_flows(struct lflow_ctx_in *, struct lflow_ctx_out *); - bool lflow_handle_changed_ref(enum ref_type, const char *ref_name, - struct lflow_ctx_in *, struct lflow_ctx_out *, -@@ -159,7 +160,8 @@ void lflow_handle_changed_neighbors( - - void lflow_destroy(void); - --void lflow_expr_destroy(struct hmap *lflow_expr_cache); -+void lflow_cache_init(struct hmap *); -+void lflow_cache_destroy(struct hmap *); - - bool lflow_add_flows_for_datapath(const struct sbrec_datapath_binding *, - struct lflow_ctx_in *, -diff --git a/controller/ovn-controller.c b/controller/ovn-controller.c -index 6aeeb15f4..3531b187f 100644 ---- a/controller/ovn-controller.c -+++ b/controller/ovn-controller.c -@@ -76,6 +76,7 @@ static unixctl_cb_func cluster_state_reset_cmd; - static unixctl_cb_func debug_pause_execution; - static unixctl_cb_func debug_resume_execution; - static unixctl_cb_func debug_status_execution; -+static unixctl_cb_func flush_lflow_cache; - - #define DEFAULT_BRIDGE_NAME "br-int" - #define DEFAULT_PROBE_INTERVAL_MSEC 5000 -@@ -462,7 +463,8 @@ get_ofctrl_probe_interval(struct ovsdb_idl *ovs_idl) - * updates 'sbdb_idl' with that pointer. */ - static void - update_sb_db(struct ovsdb_idl *ovs_idl, struct ovsdb_idl *ovnsb_idl, -- bool *monitor_all_p, bool *reset_ovnsb_idl_min_index) -+ bool *monitor_all_p, bool *reset_ovnsb_idl_min_index, -+ bool *enable_lflow_cache) - { - const struct ovsrec_open_vswitch *cfg = ovsrec_open_vswitch_first(ovs_idl); - if (!cfg) { -@@ -498,6 +500,11 @@ update_sb_db(struct ovsdb_idl *ovs_idl, struct ovsdb_idl *ovnsb_idl, - ovsdb_idl_reset_min_index(ovnsb_idl); - *reset_ovnsb_idl_min_index = false; - } -+ -+ if (enable_lflow_cache != NULL) { -+ *enable_lflow_cache = -+ smap_get_bool(&cfg->external_ids, "ovn-enable-lflow-cache", true); -+ } - } - - static void -@@ -788,6 +795,10 @@ enum ovs_engine_node { - OVS_NODES - #undef OVS_NODE - -+struct controller_engine_ctx { -+ bool enable_lflow_cache; -+}; -+ - struct ed_type_ofctrl_is_connected { - bool connected; - }; -@@ -1519,6 +1530,12 @@ physical_flow_changes_ovs_iface_handler(struct engine_node *node, void *data) - return true; - } - -+struct flow_output_persistent_data { -+ uint32_t conj_id_ofs; -+ struct hmap lflow_cache_map; -+ bool lflow_cache_enabled; -+}; -+ - struct ed_type_flow_output { - /* desired flows */ - struct ovn_desired_flow_table flow_table; -@@ -1526,10 +1543,12 @@ struct ed_type_flow_output { - struct ovn_extend_table group_table; - /* meter ids for QoS */ - struct ovn_extend_table meter_table; -- /* conjunction id offset */ -- uint32_t conj_id_ofs; - /* lflow resource cross reference */ - struct lflow_resource_ref lflow_resource_ref; -+ -+ /* Data which is persistent and not cleared during -+ * full recompute. */ -+ struct flow_output_persistent_data pd; - }; - - static void init_physical_ctx(struct engine_node *node, -@@ -1680,7 +1699,13 @@ static void init_lflow_ctx(struct engine_node *node, - l_ctx_out->group_table = &fo->group_table; - l_ctx_out->meter_table = &fo->meter_table; - l_ctx_out->lfrr = &fo->lflow_resource_ref; -- l_ctx_out->conj_id_ofs = &fo->conj_id_ofs; -+ l_ctx_out->conj_id_ofs = &fo->pd.conj_id_ofs; -+ if (fo->pd.lflow_cache_enabled) { -+ l_ctx_out->lflow_cache_map = &fo->pd.lflow_cache_map; -+ } else { -+ l_ctx_out->lflow_cache_map = NULL; -+ } -+ l_ctx_out->conj_id_overflow = false; - } - - static void * -@@ -1692,8 +1717,10 @@ en_flow_output_init(struct engine_node *node OVS_UNUSED, - ovn_desired_flow_table_init(&data->flow_table); - ovn_extend_table_init(&data->group_table); - ovn_extend_table_init(&data->meter_table); -- data->conj_id_ofs = 1; -+ data->pd.conj_id_ofs = 1; - lflow_resource_init(&data->lflow_resource_ref); -+ lflow_cache_init(&data->pd.lflow_cache_map); -+ data->pd.lflow_cache_enabled = true; - return data; - } - -@@ -1705,6 +1732,7 @@ en_flow_output_cleanup(void *data) - ovn_extend_table_destroy(&flow_output_data->group_table); - ovn_extend_table_destroy(&flow_output_data->meter_table); - lflow_resource_destroy(&flow_output_data->lflow_resource_ref); -+ lflow_cache_destroy(&flow_output_data->pd.lflow_cache_map); - } - - static void -@@ -1738,7 +1766,6 @@ en_flow_output_run(struct engine_node *node, void *data) - struct ovn_desired_flow_table *flow_table = &fo->flow_table; - struct ovn_extend_table *group_table = &fo->group_table; - struct ovn_extend_table *meter_table = &fo->meter_table; -- uint32_t *conj_id_ofs = &fo->conj_id_ofs; - struct lflow_resource_ref *lfrr = &fo->lflow_resource_ref; - - static bool first_run = true; -@@ -1751,12 +1778,39 @@ en_flow_output_run(struct engine_node *node, void *data) - lflow_resource_clear(lfrr); - } - -- *conj_id_ofs = 1; -+ struct controller_engine_ctx *ctrl_ctx = engine_get_context()->client_ctx; -+ if (fo->pd.lflow_cache_enabled && !ctrl_ctx->enable_lflow_cache) { -+ lflow_cache_destroy(&fo->pd.lflow_cache_map); -+ lflow_cache_init(&fo->pd.lflow_cache_map); -+ } -+ fo->pd.lflow_cache_enabled = ctrl_ctx->enable_lflow_cache; -+ -+ if (!fo->pd.lflow_cache_enabled) { -+ fo->pd.conj_id_ofs = 1; -+ } -+ - struct lflow_ctx_in l_ctx_in; - struct lflow_ctx_out l_ctx_out; - init_lflow_ctx(node, rt_data, fo, &l_ctx_in, &l_ctx_out); - lflow_run(&l_ctx_in, &l_ctx_out); - -+ if (l_ctx_out.conj_id_overflow) { -+ /* Conjunction ids overflow. There can be many holes in between. -+ * Destroy lflow cache and call lflow_run() again. */ -+ ovn_desired_flow_table_clear(flow_table); -+ ovn_extend_table_clear(group_table, false /* desired */); -+ ovn_extend_table_clear(meter_table, false /* desired */); -+ lflow_resource_clear(lfrr); -+ fo->pd.conj_id_ofs = 1; -+ lflow_cache_destroy(&fo->pd.lflow_cache_map); -+ lflow_cache_init(&fo->pd.lflow_cache_map); -+ l_ctx_out.conj_id_overflow = false; -+ lflow_run(&l_ctx_in, &l_ctx_out); -+ if (l_ctx_out.conj_id_overflow) { -+ VLOG_WARN("Conjunction id overflow."); -+ } -+ } -+ - struct physical_ctx p_ctx; - init_physical_ctx(node, rt_data, &p_ctx); - -@@ -2321,6 +2375,8 @@ main(int argc, char *argv[]) - - unixctl_command_register("recompute", "", 0, 0, engine_recompute_cmd, - NULL); -+ unixctl_command_register("flush-lflow-cache", "", 0, 0, flush_lflow_cache, -+ &flow_output_data->pd); - - bool reset_ovnsb_idl_min_index = false; - unixctl_command_register("sb-cluster-state-reset", "", 0, 0, -@@ -2338,6 +2394,10 @@ main(int argc, char *argv[]) - unsigned int ovs_cond_seqno = UINT_MAX; - unsigned int ovnsb_cond_seqno = UINT_MAX; - -+ struct controller_engine_ctx ctrl_engine_ctx = { -+ .enable_lflow_cache = true -+ }; -+ - /* Main loop. */ - exiting = false; - restart = false; -@@ -2366,7 +2426,8 @@ main(int argc, char *argv[]) - } - - update_sb_db(ovs_idl_loop.idl, ovnsb_idl_loop.idl, &sb_monitor_all, -- &reset_ovnsb_idl_min_index); -+ &reset_ovnsb_idl_min_index, -+ &ctrl_engine_ctx.enable_lflow_cache); - update_ssl_config(ovsrec_ssl_table_get(ovs_idl_loop.idl)); - ofctrl_set_probe_interval(get_ofctrl_probe_interval(ovs_idl_loop.idl)); - -@@ -2384,7 +2445,8 @@ main(int argc, char *argv[]) - - struct engine_context eng_ctx = { - .ovs_idl_txn = ovs_idl_txn, -- .ovnsb_idl_txn = ovnsb_idl_txn -+ .ovnsb_idl_txn = ovnsb_idl_txn, -+ .client_ctx = &ctrl_engine_ctx - }; - - engine_set_context(&eng_ctx); -@@ -2624,7 +2686,8 @@ loop_done: - if (!restart) { - bool done = !ovsdb_idl_has_ever_connected(ovnsb_idl_loop.idl); - while (!done) { -- update_sb_db(ovs_idl_loop.idl, ovnsb_idl_loop.idl, NULL, NULL); -+ update_sb_db(ovs_idl_loop.idl, ovnsb_idl_loop.idl, -+ NULL, NULL, NULL); - update_ssl_config(ovsrec_ssl_table_get(ovs_idl_loop.idl)); - - struct ovsdb_idl_txn *ovs_idl_txn -@@ -2854,6 +2917,20 @@ engine_recompute_cmd(struct unixctl_conn *conn OVS_UNUSED, int argc OVS_UNUSED, - unixctl_command_reply(conn, NULL); - } - -+static void -+flush_lflow_cache(struct unixctl_conn *conn OVS_UNUSED, int argc OVS_UNUSED, -+ const char *argv[] OVS_UNUSED, void *arg_) -+{ -+ VLOG_INFO("User triggered lflow cache flush."); -+ struct flow_output_persistent_data *fo_pd = arg_; -+ lflow_cache_destroy(&fo_pd->lflow_cache_map); -+ lflow_cache_init(&fo_pd->lflow_cache_map); -+ fo_pd->conj_id_ofs = 1; -+ engine_set_force_recompute(true); -+ poll_immediate_wake(); -+ unixctl_command_reply(conn, NULL); -+} -+ - static void - cluster_state_reset_cmd(struct unixctl_conn *conn, int argc OVS_UNUSED, - const char *argv[] OVS_UNUSED, void *idl_reset_) -diff --git a/tests/ovn.at b/tests/ovn.at -index 7cc1756e1..5a1e59df5 100644 ---- a/tests/ovn.at -+++ b/tests/ovn.at -@@ -21402,6 +21402,141 @@ OVN_CLEANUP([hv1],[hv2]) - - AT_CLEANUP - -+AT_SETUP([ovn -- lflow cache for conjunctions]) -+ovn_start -+net_add n1 -+sim_add hv1 -+ -+as hv1 -+ovs-vsctl add-br br-phys -+ovn_attach n1 br-phys 192.168.0.1 -+ -+ovn-nbctl ls-add sw0 -+ovn-nbctl lsp-add sw0 sw0-p1 -+ovn-nbctl lsp-set-addresses sw0-p1 "10:14:00:00:00:03 10.0.0.3" -+ovn-nbctl lsp-set-port-security sw0-p1 "10:14:00:00:00:03 10.0.0.3" -+ -+ovn-nbctl lsp-add sw0 sw0-p2 -+ovn-nbctl lsp-set-addresses sw0-p2 "10:14:00:00:00:04 10.0.0.4" -+ovn-nbctl lsp-set-port-security sw0-p2 "10:14:00:00:00:04 10.0.0.4" -+ -+ovn-nbctl lsp-add sw0 sw0-p3 -+ovn-nbctl lsp-set-addresses sw0-p3 "10:14:00:00:00:05 10.0.0.5" -+ovn-nbctl lsp-set-port-security sw0-p3 "10:14:00:00:00:05 10.0.0.5" -+ -+ovn-nbctl lsp-add sw0 sw0-p4 -+ovn-nbctl lsp-set-addresses sw0-p4 "10:14:00:00:00:06 10.0.0.6" -+ovn-nbctl lsp-set-port-security sw0-p4 "10:14:00:00:00:06 10.0.0.6" -+ -+as hv1 -+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=sw0-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 -+ovs-vsctl -- add-port br-int hv1-vif4 -- \ -+ set interface hv1-vif4 external-ids:iface-id=sw0-p4 \ -+ options:tx_pcap=hv1/vif4-tx.pcap \ -+ options:rxq_pcap=hv1/vif4-rx.pcap \ -+ ofport-request=4 -+ -+OVS_WAIT_UNTIL([test x$(ovn-nbctl lsp-get-up sw0-p1) = xup]) -+OVS_WAIT_UNTIL([test x$(ovn-nbctl lsp-get-up sw0-p2) = xup]) -+OVS_WAIT_UNTIL([test x$(ovn-nbctl lsp-get-up sw0-p3) = xup]) -+OVS_WAIT_UNTIL([test x$(ovn-nbctl lsp-get-up sw0-p4) = xup]) -+ -+ovn-nbctl pg-add pg0 sw0-p1 sw0-p2 -+ovn-nbctl acl-add pg0 to-lport 1002 "outport == @pg0 && ip4 && tcp.dst >= 80 && tcp.dst <= 82" allow -+ovn-nbctl --wait=hv sync -+ -+OVS_WAIT_UNTIL([test 1 = $(as hv1 ovs-ofctl dump-flows br-int table=44 | grep -c "conj_id=2")]) -+ -+# Add sw0-p3 to the port group pg0. The conj_id should be 2. -+ovn-nbctl pg-set-ports pg0 sw0-p1 sw0-p2 sw0-p3 -+OVS_WAIT_UNTIL([test 1 = $(as hv1 ovs-ofctl dump-flows br-int table=44 | grep -c "conj_id")]) -+AT_CHECK([test 1 = $(as hv1 ovs-ofctl dump-flows br-int table=44 | grep -c "conj_id=2")]) -+ -+# Add sw0p4 to the port group pg0. The conj_id should be 2. -+ovn-nbctl pg-set-ports pg0 sw0-p1 sw0-p2 sw0-p3 sw0-p4 -+OVS_WAIT_UNTIL([test 1 = $(as hv1 ovs-ofctl dump-flows br-int table=44 | grep -c "conj_id")]) -+AT_CHECK([test 1 = $(as hv1 ovs-ofctl dump-flows br-int table=44 | grep -c "conj_id=2")]) -+ -+# Add another ACL with conjunction. -+ovn-nbctl acl-add pg0 to-lport 1002 "outport == @pg0 && ip4 && udp.dst >= 80 && udp.dst <= 82" allow -+OVS_WAIT_UNTIL([test 2 = $(as hv1 ovs-ofctl dump-flows br-int table=44 | grep -c "conj_id")]) -+AT_CHECK([test 1 = $(as hv1 ovs-ofctl dump-flows br-int table=44 | grep tcp | grep -c "conj_id=2")]) -+AT_CHECK([test 1 = $(as hv1 ovs-ofctl dump-flows br-int table=44 | grep udp | grep -c "conj_id=3")]) -+ -+# Delete tcp ACL. -+ovn-nbctl acl-del pg0 to-lport 1002 "outport == @pg0 && ip4 && tcp.dst >= 80 && tcp.dst <= 82" -+OVS_WAIT_UNTIL([test 1 = $(as hv1 ovs-ofctl dump-flows br-int table=44 | grep -c "conj_id")]) -+AT_CHECK([test 1 = $(as hv1 ovs-ofctl dump-flows br-int table=44 | grep udp | grep -c "conj_id=3")]) -+ -+# Add back the tcp ACL. -+ovn-nbctl acl-add pg0 to-lport 1002 "outport == @pg0 && ip4 && tcp.dst >= 80 && tcp.dst <= 82" allow -+OVS_WAIT_UNTIL([test 2 = $(as hv1 ovs-ofctl dump-flows br-int table=44 | grep -c "conj_id")]) -+AT_CHECK([test 1 = $(as hv1 ovs-ofctl dump-flows br-int table=44 | grep udp | grep -c "conj_id=3")]) -+AT_CHECK([test 1 = $(as hv1 ovs-ofctl dump-flows br-int table=44 | grep tcp | grep -c "conj_id=4")]) -+ -+ovn-nbctl acl-add pg0 to-lport 1002 "outport == @pg0 && inport == @pg0 && ip4 && tcp.dst >= 84 && tcp.dst <= 86" allow -+OVS_WAIT_UNTIL([test 3 = $(as hv1 ovs-ofctl dump-flows br-int table=44 | grep -c "conj_id")]) -+AT_CHECK([test 1 = $(as hv1 ovs-ofctl dump-flows br-int table=44 | grep udp | grep -c "conj_id=3")]) -+AT_CHECK([test 1 = $(as hv1 ovs-ofctl dump-flows br-int table=44 | grep tcp | grep -c "conj_id=4")]) -+AT_CHECK([test 1 = $(as hv1 ovs-ofctl dump-flows br-int table=44 | grep tcp | grep -c "conj_id=5")]) -+ -+ovn-nbctl clear port_group pg0 acls -+OVS_WAIT_UNTIL([test 0 = $(as hv1 ovs-ofctl dump-flows br-int table=44 | grep -c "conj_id")]) -+ -+ovn-nbctl --wait=hv acl-add pg0 to-lport 1002 "outport == @pg0 && ip4 && tcp.dst >= 80 && tcp.dst <= 82" allow -+ovn-nbctl --wait=hv acl-add pg0 to-lport 1002 "outport == @pg0 && ip4 && udp.dst >= 80 && udp.dst <= 82" allow -+OVS_WAIT_UNTIL([test 2 = $(as hv1 ovs-ofctl dump-flows br-int table=44 | grep -c "conj_id")]) -+AT_CHECK([test 1 = $(as hv1 ovs-ofctl dump-flows br-int table=44 | grep tcp | grep -c "conj_id=6")]) -+AT_CHECK([test 1 = $(as hv1 ovs-ofctl dump-flows br-int table=44 | grep udp | grep -c "conj_id=7")]) -+ -+# Flush the lflow cache. -+as hv1 ovn-appctl -t ovn-controller flush-lflow-cache -+OVS_WAIT_UNTIL([test 2 = $(as hv1 ovs-ofctl dump-flows br-int table=44 | grep -c "conj_id")]) -+AT_CHECK([test 1 = $(as hv1 ovs-ofctl dump-flows br-int table=44 | grep -c "conj_id=2")]) -+AT_CHECK([test 1 = $(as hv1 ovs-ofctl dump-flows br-int table=44 | grep -c "conj_id=3")]) -+ -+# Disable lflow caching. -+ -+as hv1 ovs-vsctl set open . external_ids:ovn-enable-lflow-cache=false -+ -+# Wait until ovn-enble-lflow-cache is processed by ovn-controller. -+OVS_WAIT_UNTIL([ -+ test $(ovn-sbctl get chassis hv1 other_config:ovn-enable-lflow-cache) = '"false"' -+]) -+ -+AT_CHECK([test 1 = $(as hv1 ovs-ofctl dump-flows br-int table=44 | grep -c "conj_id=2")]) -+AT_CHECK([test 1 = $(as hv1 ovs-ofctl dump-flows br-int table=44 | grep -c "conj_id=3")]) -+ -+# Remove port sw0-p4 from port group. -+ovn-nbctl pg-set-ports pg0 sw0-p1 sw0-p2 sw0-p3 -+OVS_WAIT_UNTIL([test 2 = $(as hv1 ovs-ofctl dump-flows br-int table=44 | grep -c "conj_id")]) -+AT_CHECK([test 1 = $(as hv1 ovs-ofctl dump-flows br-int table=44 | grep -c "conj_id=4")]) -+AT_CHECK([test 1 = $(as hv1 ovs-ofctl dump-flows br-int table=44 | grep -c "conj_id=5")]) -+ -+as hv1 ovn-appctl -t ovn-controller recompute -+ -+OVS_WAIT_UNTIL([test 2 = $(as hv1 ovs-ofctl dump-flows br-int table=44 | grep -c "conj_id")]) -+OVS_WAIT_UNTIL([test 1 = $(as hv1 ovs-ofctl dump-flows br-int table=44 | grep -c "conj_id=2")]) -+AT_CHECK([test 1 = $(as hv1 ovs-ofctl dump-flows br-int table=44 | grep -c "conj_id=3")]) -+ -+OVN_CLEANUP([hv1]) -+ -+AT_CLEANUP -+ - AT_SETUP([ovn -- Delete Port_Binding and OVS port Incremental Processing]) - ovn_start - --- -2.26.2 - diff --git a/SOURCES/0002-ovsdb-idl-Try-committing-the-pending-txn-in-ovsdb_id.patch b/SOURCES/0002-ovsdb-idl-Try-committing-the-pending-txn-in-ovsdb_id.patch new file mode 100644 index 0000000..7f4c1c4 --- /dev/null +++ b/SOURCES/0002-ovsdb-idl-Try-committing-the-pending-txn-in-ovsdb_id.patch @@ -0,0 +1,217 @@ +From a2271039d49a12390ca3118fb3a90a057577d360 Mon Sep 17 00:00:00 2001 +From: Numan Siddique +Date: Fri, 5 Jun 2020 14:00:29 +0530 +Subject: [PATCH 2/4] ovsdb idl: Try committing the pending txn in + ovsdb_idl_loop_run. + +The function ovsdb_idl_loop_run(), after calling ovsdb_idl_run(), +returns a transaction object (of type 'struct ovsdb_idl_txn'). +The returned transaction object can be NULL if there is a pending +transaction (loop->committing_txn) in the idl loop object. + +Normally the clients of idl library, first call ovsdb_idl_loop_run(), +then do their own processing and create any idl transactions during +this processing and then finally call ovsdb_idl_loop_commit_and_wait(). + +If ovsdb_idl_loop_run() returns NULL transaction object, then much +of the processing done by the client gets wasted as in the case +of ovn-controller. + +The client (in this case ovn-controller), can skip the processing +and instead call ovsdb_idl_loop_commit_and_wait() if the transaction +oject is NULL. But ovn-controller uses IDL tracking and it may +loose the tracked changes in that run. + +This patch tries to improve this scenario, by checking if the +pending transaction can be committed in the ovsdb_idl_loop_run() +itself and if the pending transaction is cleared (because of the +response messages from ovsdb-server due to a transaction message +in the previous run), ovsdb_idl_loop_run() can return a valid +transaction object. + +CC: Han Zhou +Signed-off-by: Numan Siddique +Signed-off-by: Ben Pfaff +--- + openvswitch-2.13.0/lib/ovsdb-idl.c | 143 +++++++++++++++++++---------- + 1 file changed, 93 insertions(+), 50 deletions(-) + +diff --git a/openvswitch-2.13.0/lib/ovsdb-idl.c b/openvswitch-2.13.0/lib/ovsdb-idl.c +index 1535ad7b5..2d351791f 100644 +--- a/openvswitch-2.13.0/lib/ovsdb-idl.c ++++ b/openvswitch-2.13.0/lib/ovsdb-idl.c +@@ -385,6 +385,8 @@ static void ovsdb_idl_send_cond_change(struct ovsdb_idl *idl); + static void ovsdb_idl_destroy_indexes(struct ovsdb_idl_table *); + static void ovsdb_idl_add_to_indexes(const struct ovsdb_idl_row *); + static void ovsdb_idl_remove_from_indexes(const struct ovsdb_idl_row *); ++static int ovsdb_idl_try_commit_loop_txn(struct ovsdb_idl_loop *loop, ++ bool *may_need_wakeup); + + static void + ovsdb_idl_db_init(struct ovsdb_idl_db *db, const struct ovsdb_idl_class *class, +@@ -5329,6 +5331,12 @@ struct ovsdb_idl_txn * + ovsdb_idl_loop_run(struct ovsdb_idl_loop *loop) + { + ovsdb_idl_run(loop->idl); ++ ++ /* See if we can commit the loop->committing_txn. */ ++ if (loop->committing_txn) { ++ ovsdb_idl_try_commit_loop_txn(loop, NULL); ++ } ++ + loop->open_txn = (loop->committing_txn + || ovsdb_idl_get_seqno(loop->idl) == loop->skip_seqno + ? NULL +@@ -5336,6 +5344,87 @@ ovsdb_idl_loop_run(struct ovsdb_idl_loop *loop) + return loop->open_txn; + } + ++/* Attempts to commit the current transaction, if one is open. ++ * ++ * If a transaction was open, in this or a previous iteration of the main loop, ++ * and had not before finished committing (successfully or unsuccessfully), the ++ * return value is one of: ++ * ++ * 1: The transaction committed successfully (or it did not change anything in ++ * the database). ++ * 0: The transaction failed. ++ * -1: The commit is still in progress. ++ * ++ * Thus, the return value is -1 if the transaction is in progress and otherwise ++ * true for success, false for failure. ++ * ++ * (In the corner case where the IDL sends a transaction to the database and ++ * the database commits it, and the connection between the IDL and the database ++ * drops before the IDL receives the message confirming the commit, this ++ * function can return 0 even though the transaction succeeded.) ++ */ ++static int ++ovsdb_idl_try_commit_loop_txn(struct ovsdb_idl_loop *loop, ++ bool *may_need_wakeup) ++{ ++ if (!loop->committing_txn) { ++ /* Not a meaningful return value: no transaction was in progress. */ ++ return 1; ++ } ++ ++ int retval; ++ struct ovsdb_idl_txn *txn = loop->committing_txn; ++ ++ enum ovsdb_idl_txn_status status = ovsdb_idl_txn_commit(txn); ++ if (status != TXN_INCOMPLETE) { ++ switch (status) { ++ case TXN_TRY_AGAIN: ++ /* We want to re-evaluate the database when it's changed from ++ * the contents that it had when we started the commit. (That ++ * might have already happened.) */ ++ loop->skip_seqno = loop->precommit_seqno; ++ if (ovsdb_idl_get_seqno(loop->idl) != loop->skip_seqno ++ && may_need_wakeup) { ++ *may_need_wakeup = true; ++ } ++ retval = 0; ++ break; ++ ++ case TXN_SUCCESS: ++ /* Possibly some work on the database was deferred because no ++ * further transaction could proceed. Wake up again. */ ++ retval = 1; ++ loop->cur_cfg = loop->next_cfg; ++ if (may_need_wakeup) { ++ *may_need_wakeup = true; ++ } ++ break; ++ ++ case TXN_UNCHANGED: ++ retval = 1; ++ loop->cur_cfg = loop->next_cfg; ++ break; ++ ++ case TXN_ABORTED: ++ case TXN_NOT_LOCKED: ++ case TXN_ERROR: ++ retval = 0; ++ break; ++ ++ case TXN_UNCOMMITTED: ++ case TXN_INCOMPLETE: ++ default: ++ OVS_NOT_REACHED(); ++ } ++ ovsdb_idl_txn_destroy(txn); ++ loop->committing_txn = NULL; ++ } else { ++ retval = -1; ++ } ++ ++ return retval; ++} ++ + /* Attempts to commit the current transaction, if one is open, and sets up the + * poll loop to wake up when some more work might be needed. + * +@@ -5366,57 +5455,11 @@ ovsdb_idl_loop_commit_and_wait(struct ovsdb_idl_loop *loop) + loop->precommit_seqno = ovsdb_idl_get_seqno(loop->idl); + } + +- struct ovsdb_idl_txn *txn = loop->committing_txn; +- int retval; +- if (txn) { +- enum ovsdb_idl_txn_status status = ovsdb_idl_txn_commit(txn); +- if (status != TXN_INCOMPLETE) { +- switch (status) { +- case TXN_TRY_AGAIN: +- /* We want to re-evaluate the database when it's changed from +- * the contents that it had when we started the commit. (That +- * might have already happened.) */ +- loop->skip_seqno = loop->precommit_seqno; +- if (ovsdb_idl_get_seqno(loop->idl) != loop->skip_seqno) { +- poll_immediate_wake(); +- } +- retval = 0; +- break; +- +- case TXN_SUCCESS: +- /* Possibly some work on the database was deferred because no +- * further transaction could proceed. Wake up again. */ +- retval = 1; +- loop->cur_cfg = loop->next_cfg; +- poll_immediate_wake(); +- break; +- +- case TXN_UNCHANGED: +- retval = 1; +- loop->cur_cfg = loop->next_cfg; +- break; +- +- case TXN_ABORTED: +- case TXN_NOT_LOCKED: +- case TXN_ERROR: +- retval = 0; +- break; +- +- case TXN_UNCOMMITTED: +- case TXN_INCOMPLETE: +- default: +- OVS_NOT_REACHED(); +- } +- ovsdb_idl_txn_destroy(txn); +- loop->committing_txn = NULL; +- } else { +- retval = -1; +- } +- } else { +- /* Not a meaningful return value: no transaction was in progress. */ +- retval = 1; ++ bool may_need_wakeup = false; ++ int retval = ovsdb_idl_try_commit_loop_txn(loop, &may_need_wakeup); ++ if (may_need_wakeup) { ++ poll_immediate_wake(); + } +- + ovsdb_idl_wait(loop->idl); + + return retval; +-- +2.26.2 + diff --git a/SOURCES/0003-Support-packet-metadata-marking-for-logical-router-p.patch b/SOURCES/0003-Support-packet-metadata-marking-for-logical-router-p.patch deleted file mode 100644 index 8cfd40b..0000000 --- a/SOURCES/0003-Support-packet-metadata-marking-for-logical-router-p.patch +++ /dev/null @@ -1,455 +0,0 @@ -From 74b88cd58dcfdb5e80fa2d64a909170b5a24f76a Mon Sep 17 00:00:00 2001 -From: Numan Siddique -Date: Wed, 17 Jun 2020 23:39:29 +0530 -Subject: [PATCH 03/22] Support packet metadata marking for logical router - policies. - -This patch adds a new column 'options' of type smap in the -Logical_Router_Policy table in the NB DB and supports the key 'pkt_mark'. -CMS can set a desired value for this key in the 'options' column. When this -router policy is applied, the packet metadata is marked with the specified -value (to the NXM_NX_PKT_MARK OVS field). - -In the case of Linux, this corresponds to struct sk_buff's "skb_mark" -member and this mark can be seen by the linux networking subsystem. -CMS can inspect this value (as an iptables rule or adding an OF flow -in another ovs bridge) and take appropriate action when the marked packet -leaves the integration bridge via the patch port. - -Change-Id: I05efa5e1d77bb02bf59d28b174151ba33779b8c3 -Requested-at: https://bugzilla.redhat.com/show_bug.cgi?id=1828933 -Requested-by: Alexander Constantinescu -Acked-by: Dumitru Ceara -Signed-off-by: Numan Siddique - -(cherry-picked from upstream master commit a123ef0fb8fd03cc586f924cb511fb24a661743a) ---- - NEWS | 2 + - lib/logical-fields.c | 2 + - northd/ovn-northd.c | 8 ++ - ovn-nb.ovsschema | 7 +- - ovn-nb.xml | 9 ++ - ovn-sb.xml | 1 + - tests/ovn.at | 291 +++++++++++++++++++++++++++++++++++++++++++ - 7 files changed, 318 insertions(+), 2 deletions(-) - -diff --git a/NEWS b/NEWS -index 30b24e5ad..8abdc95b9 100644 ---- a/NEWS -+++ b/NEWS -@@ -7,6 +7,8 @@ OVN v20.06.2 - 21 Aug 2020 - - OVN v20.06.1 - 08 Jul 2020 - -------------------------- -+ - Added packet marking support for traffic routed with -+ a routing policy. - - OVN v20.06.0 - 08 Jun 2020 - -------------------------- -diff --git a/lib/logical-fields.c b/lib/logical-fields.c -index a007085b3..8ad56aa53 100644 ---- a/lib/logical-fields.c -+++ b/lib/logical-fields.c -@@ -254,6 +254,8 @@ ovn_init_symtab(struct shash *symtab) - expr_symtab_add_field(symtab, "sctp.src", MFF_SCTP_SRC, "sctp", false); - expr_symtab_add_field(symtab, "sctp.dst", MFF_SCTP_DST, "sctp", false); - -+ expr_symtab_add_field(symtab, "pkt.mark", MFF_PKT_MARK, NULL, false); -+ - expr_symtab_add_ovn_field(symtab, "icmp4.frag_mtu", OVN_ICMP4_FRAG_MTU); - } - -diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c -index a665d52e9..2b1257114 100644 ---- a/northd/ovn-northd.c -+++ b/northd/ovn-northd.c -@@ -7386,6 +7386,10 @@ build_routing_policy_flow(struct hmap *lflows, struct ovn_datapath *od, - rule->priority, rule->nexthop); - return; - } -+ uint32_t pkt_mark = smap_get_int(&rule->options, "pkt_mark", 0); -+ if (pkt_mark) { -+ ds_put_format(&actions, "pkt.mark = %u; ", pkt_mark); -+ } - bool is_ipv4 = strchr(rule->nexthop, '.') ? true : false; - ds_put_format(&actions, "%s = %s; " - "%s = %s; " -@@ -7403,6 +7407,10 @@ build_routing_policy_flow(struct hmap *lflows, struct ovn_datapath *od, - } else if (!strcmp(rule->action, "drop")) { - ds_put_cstr(&actions, "drop;"); - } else if (!strcmp(rule->action, "allow")) { -+ uint32_t pkt_mark = smap_get_int(&rule->options, "pkt_mark", 0); -+ if (pkt_mark) { -+ ds_put_format(&actions, "pkt.mark = %u; ", pkt_mark); -+ } - ds_put_cstr(&actions, "next;"); - } - ds_put_format(&match, "%s", rule->match); -diff --git a/ovn-nb.ovsschema b/ovn-nb.ovsschema -index a06972aa0..da9af7157 100644 ---- a/ovn-nb.ovsschema -+++ b/ovn-nb.ovsschema -@@ -1,7 +1,7 @@ - { - "name": "OVN_Northbound", -- "version": "5.23.0", -- "cksum": "111023208 25806", -+ "version": "5.24.0", -+ "cksum": "1092394564 25961", - "tables": { - "NB_Global": { - "columns": { -@@ -379,6 +379,9 @@ - "key": {"type": "string", - "enum": ["set", ["allow", "drop", "reroute"]]}}}, - "nexthop": {"type": {"key": "string", "min": 0, "max": 1}}, -+ "options": { -+ "type": {"key": "string", "value": "string", -+ "min": 0, "max": "unlimited"}}, - "external_ids": { - "type": {"key": "string", "value": "string", - "min": 0, "max": "unlimited"}}}, -diff --git a/ovn-nb.xml b/ovn-nb.xml -index 0fdc1592b..02161372a 100644 ---- a/ovn-nb.xml -+++ b/ovn-nb.xml -@@ -2581,6 +2581,15 @@ -

- - -+ -+

-+ Marks the packet with the value specified when the router policy -+ is applied. CMS can inspect this packet marker and take some decisions -+ if desired. This value is not preserved when the packet goes out on the -+ wire. -+

-+
-+ - - - See External IDs at the beginning of this document. -diff --git a/ovn-sb.xml b/ovn-sb.xml -index 293b0920c..709cb4c48 100644 ---- a/ovn-sb.xml -+++ b/ovn-sb.xml -@@ -975,6 +975,7 @@ -
  • xxreg0 xxreg1
  • -
  • inport outport
  • -
  • flags.loopback
  • -+
  • pkt.mark
  • -
  • eth.src eth.dst eth.type
  • -
  • vlan.tci vlan.vid vlan.pcp vlan.present
  • -
  • ip.proto ip.dscp ip.ecn ip.ttl ip.frag
  • -diff --git a/tests/ovn.at b/tests/ovn.at -index be677b663..8ce45823f 100644 ---- a/tests/ovn.at -+++ b/tests/ovn.at -@@ -965,6 +965,23 @@ ip.ttl--; - ip.ttl - Syntax error at end of input expecting `--'. - -+# Packet mark. -+pkt.mark=1; -+ formats as pkt.mark = 1; -+ encodes as set_field:0x1->pkt_mark -+ -+pkt.mark = 1000; -+ encodes as set_field:0x3e8->pkt_mark -+ -+pkt.mark; -+ Syntax error at `pkt.mark' expecting action. -+ -+pkt.mark = foo; -+ Syntax error at `foo' expecting field name. -+ -+pkt.mark = "foo"; -+ Integer field pkt.mark is not compatible with string constant. -+ - # load balancing. - ct_lb; - encodes as ct(table=19,zone=NXM_NX_REG13[0..15],nat) -@@ -20199,6 +20216,280 @@ AT_CHECK([ovn-nbctl --if-exists lr-nat-del r1 snat beef:0000::0/ffff:ffff:ffff:f - AT_CHECK([ovn-nbctl --if-exists lr-nat-del r1 dnat aef0:0000:00::1]) - AT_CLEANUP - -+AT_SETUP([ovn -- Logical router policy packet marking]) -+ovn_start -+ -+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-port1 \ -+ 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=sw0-port2 \ -+ options:tx_pcap=hv1/vif2-tx.pcap \ -+ options:rxq_pcap=hv1/vif2-rx.pcap \ -+ ofport-request=2 -+ -+as hv1 ovs-vsctl set Open_vSwitch . external-ids:ovn-bridge-mappings=public:br-phys -+ -+ovn-nbctl ls-add sw0 -+ovn-nbctl lsp-add sw0 sw0-port1 -+ovn-nbctl lsp-set-addresses sw0-port1 "50:54:00:00:00:03 10.0.0.3 10.0.0.5" -+ovn-nbctl lsp-set-port-security sw0-port1 "50:54:00:00:00:03 10.0.0.3 10.0.0.5" -+ -+ovn-nbctl lsp-add sw0 sw0-port2 -+ovn-nbctl lsp-set-addresses sw0-port2 "50:54:00:00:00:04 10.0.0.4 aef0::4" -+ -+ovn-nbctl lr-add lr0 -+ovn-nbctl lrp-add lr0 lr0-sw0 00:00:00:00:ff:01 10.0.0.1/24 aef0::1/64 -+ovn-nbctl lsp-add sw0 sw0-lr0 -+ovn-nbctl lsp-set-type sw0-lr0 router -+ovn-nbctl lsp-set-addresses sw0-lr0 router -+ovn-nbctl lsp-set-options sw0-lr0 router-port=lr0-sw0 -+ -+ovn-nbctl ls-add public -+ovn-nbctl lrp-add lr0 lr0-public 00:00:20:20:12:13 172.168.0.100/24 bef0::1/64 -+ovn-nbctl lsp-add public public-lr0 -+ovn-nbctl lsp-set-type public-lr0 router -+ovn-nbctl lsp-set-addresses public-lr0 router -+ovn-nbctl lsp-set-options public-lr0 router-port=lr0-public -+ -+# localnet port -+ovn-nbctl lsp-add public ln-public -+ovn-nbctl lsp-set-type ln-public localnet -+ovn-nbctl lsp-set-addresses ln-public unknown -+ovn-nbctl lsp-set-options ln-public network_name=public -+ -+ovn-nbctl lrp-set-gateway-chassis lr0-public hv1 20 -+ovn-nbctl lr-nat-add lr0 snat 172.168.0.100 10.0.0.0/24 -+lr0_dp_uuid=$(ovn-sbctl --bare --columns _uuid list datapath_binding lr0) -+ovn-sbctl create mac_binding datapath=$lr0_dp_uuid ip=172.168.0.120 \ -+logical_port=lr0-public mac="10\:54\:00\:00\:00\:03" -+ovn-sbctl create mac_binding datapath=$lr0_dp_uuid ip=172.168.0.200 \ -+logical_port=lr0-public mac="10\:54\:00\:00\:00\:04" -+ovn-sbctl create mac_binding datapath=$lr0_dp_uuid ip="bef0\:\:4" \ -+logical_port=lr0-public mac="10\:54\:00\:00\:00\:05" -+ovn-sbctl create mac_binding datapath=$lr0_dp_uuid ip="bef0\:\:5" \ -+logical_port=lr0-public mac="10\:54\:00\:00\:00\:06" -+ovn-sbctl create mac_binding datapath=$lr0_dp_uuid ip="bef0\:\:6" \ -+logical_port=lr0-public mac="10\:54\:00\:00\:00\:07" -+ -+ovn-nbctl -- --id=@lrt create Logical_Router_Static_Route \ -+ip_prefix="\:\:/64" nexthop="bef0\:\:4" -- add Logical_Router lr0 \ -+static_routes @lrt -+ -+ovn-nbctl --wait=hv sync -+ -+# Add logical router policy and set pkt_mark on it. -+ovn-nbctl lr-policy-add lr0 2000 "ip4.src == 10.0.0.3" allow -+ovn-nbctl lr-policy-add lr0 1000 "ip4.src == 10.0.0.4" allow -+ovn-nbctl lr-policy-add lr0 900 "ip4.src == 10.0.0.5" reroute 172.168.0.200 -+ovn-nbctl lr-policy-add lr0 2001 "ip6.dst == bef0::5" reroute bef0::6 -+ovn-nbctl lr-policy-add lr0 1001 "ip6" allow -+ -+ -+pol1=$(ovn-nbctl --bare --columns _uuid find logical_router_policy priority=2000) -+pol3=$(ovn-nbctl --bare --columns _uuid find logical_router_policy priority=900) -+pol4=$(ovn-nbctl --bare --columns _uuid find logical_router_policy priority=2001) -+pol5=$(ovn-nbctl --bare --columns _uuid find logical_router_policy priority=1001) -+ -+ovn-nbctl set logical_router_policy $pol1 options:pkt_mark=100 -+ovn-nbctl set logical_router_policy $pol3 options:pkt_mark=3 -+ovn-nbctl set logical_router_policy $pol4 options:pkt_mark=4 -+ovn-nbctl set logical_router_policy $pol5 options:pkt_mark=5 -+ovn-nbctl --wait=hv sync -+ -+OVS_WAIT_UNTIL([ -+ test 1 -eq $(as hv1 ovs-ofctl dump-flows br-int table=19 | \ -+ grep "load:0x64->NXM_NX_PKT_MARK" -c) -+]) -+ -+OVS_WAIT_UNTIL([ -+ test 1 -eq $(as hv1 ovs-ofctl dump-flows br-int table=19 | \ -+ grep "load:0x3->NXM_NX_PKT_MARK" -c) -+]) -+ -+OVS_WAIT_UNTIL([ -+ test 1 -eq $(as hv1 ovs-ofctl dump-flows br-int table=19 | \ -+ grep "load:0x4->NXM_NX_PKT_MARK" -c) -+]) -+ -+OVS_WAIT_UNTIL([ -+ test 1 -eq $(as hv1 ovs-ofctl dump-flows br-int table=19 | \ -+ grep "load:0x5->NXM_NX_PKT_MARK" -c) -+]) -+ -+AT_CHECK([as hv1 ovs-ofctl del-flows br-phys]) -+AT_DATA([flows.txt], [dnl -+table=0, priority=0 actions=NORMAL -+table=0, priority=200 arp,actions=drop -+table=0, priority=100, pkt_mark=0x64 actions=drop -+table=0, priority=100, pkt_mark=0x2 actions=drop -+table=0, priority=100, pkt_mark=0x3 actions=drop -+table=0, priority=100, pkt_mark=0x4 actions=drop -+table=0, priority=100, pkt_mark=0x5 actions=drop -+]) -+ -+AT_CHECK([as hv1 ovs-ofctl --protocols=OpenFlow13 add-flows br-phys flows.txt]) -+ -+ip_to_hex() { -+ printf "%02x%02x%02x%02x" "$@" -+} -+ -+send_ipv4_pkt() { -+ local hv=$1 inport=$2 eth_src=$3 eth_dst=$4 -+ local ip_src=$5 ip_dst=$6 -+ packet=${eth_dst}${eth_src}08004500001c0000000040110000${ip_src}${ip_dst}0035111100080000 -+ as $hv ovs-appctl netdev-dummy/receive ${inport} ${packet} -+} -+ -+send_icmp6_packet() { -+ local hv=$1 inport=$2 eth_src=$3 eth_dst=$4 ipv6_src=$5 ipv6_dst=$6 -+ -+ local ip6_hdr=6000000000083aff${ipv6_src}${ipv6_dst} -+ local packet=${eth_dst}${eth_src}86dd${ip6_hdr}8000dcb662f00001 -+ -+ as $hv ovs-appctl netdev-dummy/receive ${inport} ${packet} -+} -+ -+send_ipv4_pkt hv1 hv1-vif1 505400000003 00000000ff01 \ -+ $(ip_to_hex 10 0 0 3) $(ip_to_hex 172 168 0 120) -+ -+OVS_WAIT_UNTIL([ -+ test 1 -eq $(as hv1 ovs-ofctl dump-flows br-phys table=0 | \ -+ grep "priority=100,pkt_mark=0x64" | \ -+ grep "n_packets=1" -c) -+]) -+ -+AT_CHECK([ -+ test 1 -eq $(as hv1 ovs-ofctl dump-flows br-phys table=0 | \ -+ grep priority=0 | \ -+ grep "n_packets=0" -c) -+]) -+ -+# Send the pkt from sw0-port2. Packet should not be marked. -+send_ipv4_pkt hv1 hv1-vif2 505400000004 00000000ff01 \ -+ $(ip_to_hex 10 0 0 4) $(ip_to_hex 172 168 0 120) -+ -+AT_CHECK([ -+ test 1 -eq $(as hv1 ovs-ofctl dump-flows br-phys table=0 | \ -+ grep priority=0 | \ -+ grep "n_packets=1" -c) -+]) -+ -+AT_CHECK([ -+ test 1 -eq $(as hv1 ovs-ofctl dump-flows br-phys table=0 | \ -+ grep "priority=100,pkt_mark=0x64" | \ -+ grep "n_packets=1" -c) -+]) -+ -+AT_CHECK([ -+ test 1 -eq $(as hv1 ovs-ofctl dump-flows br-phys table=0 | \ -+ grep "priority=100,pkt_mark=0x3" | \ -+ grep "n_packets=0" -c) -+]) -+ -+AT_CHECK([ -+ test 1 -eq $(as hv1 ovs-ofctl dump-flows br-phys table=0 | \ -+ grep "priority=100,pkt_mark=0x4" | \ -+ grep "n_packets=0" -c) -+]) -+ -+AT_CHECK([ -+ test 1 -eq $(as hv1 ovs-ofctl dump-flows br-phys table=0 | \ -+ grep "priority=100,pkt_mark=0x5" | \ -+ grep "n_packets=0" -c) -+]) -+ -+ovn-nbctl set logical_router_policy $pol1 options:pkt_mark=2 -+send_ipv4_pkt hv1 hv1-vif1 505400000003 00000000ff01 \ -+ $(ip_to_hex 10 0 0 3) $(ip_to_hex 172 168 0 120) -+ -+OVS_WAIT_UNTIL([ -+ test 1 -eq $(as hv1 ovs-ofctl dump-flows br-int table=19 | \ -+ grep "load:0x2->NXM_NX_PKT_MARK" -c) -+]) -+ -+AT_CHECK([ -+ test 0 -eq $(as hv1 ovs-ofctl dump-flows br-int table=19 | \ -+ grep "load:0x64->NXM_NX_PKT_MARK" -c) -+]) -+ -+AT_CHECK([ -+ test 1 -eq $(as hv1 ovs-ofctl dump-flows br-phys table=0 | \ -+ grep "priority=100,pkt_mark=0x2" | \ -+ grep "n_packets=1" -c) -+]) -+ -+AT_CHECK([ -+ test 1 -eq $(as hv1 ovs-ofctl dump-flows br-phys table=0 | \ -+ grep priority=0 | \ -+ grep "n_packets=1" -c) -+]) -+ -+AT_CHECK([ -+ test 1 -eq $(as hv1 ovs-ofctl dump-flows br-phys table=0 | \ -+ grep "priority=100,pkt_mark=0x3" | \ -+ grep "n_packets=0" -c) -+]) -+ -+# Send with src ip 10.0.0.5. The reroute policy should be hit -+# and the packet should be marked with 5. -+send_ipv4_pkt hv1 hv1-vif1 505400000003 00000000ff01 \ -+ $(ip_to_hex 10 0 0 5) $(ip_to_hex 172 168 0 120) -+ -+OVS_WAIT_UNTIL([ -+ test 1 -eq $(as hv1 ovs-ofctl dump-flows br-phys table=0 | \ -+ grep "priority=100,pkt_mark=0x3" | \ -+ grep "n_packets=1" -c) -+]) -+ -+# Send IPv6 traffic. -+src_ip6=aef00000000000000000000000000004 -+dst_ip6=bef00000000000000000000000000004 -+ -+send_icmp6_packet hv1 hv1-vif2 505400000004 00000000ff01 ${src_ip6} ${dst_ip6} -+ -+OVS_WAIT_UNTIL([ -+ test 1 -eq $(as hv1 ovs-ofctl dump-flows br-phys table=0 | \ -+ grep "priority=100,pkt_mark=0x5" | \ -+ grep "n_packets=1" -c) -+]) -+ -+AT_CHECK([ -+ test 1 -eq $(as hv1 ovs-ofctl dump-flows br-phys table=0 | \ -+ grep "priority=100,pkt_mark=0x4" | \ -+ grep "n_packets=0" -c) -+]) -+ -+# Send IPv6 packet which hits the reroute policy. Packet should be marked -+# with 4. -+ -+src_ip6=aef00000000000000000000000000004 -+dst_ip6=bef00000000000000000000000000005 -+ -+send_icmp6_packet hv1 hv1-vif2 505400000004 00000000ff01 ${src_ip6} ${dst_ip6} -+ -+OVS_WAIT_UNTIL([ -+ test 1 -eq $(as hv1 ovs-ofctl dump-flows br-phys table=0 | \ -+ grep "priority=100,pkt_mark=0x4" | \ -+ grep "n_packets=1" -c) -+]) -+ -+AT_CHECK([ -+ test 1 -eq $(as hv1 ovs-ofctl dump-flows br-phys table=0 | \ -+ grep "priority=100,pkt_mark=0x5" | \ -+ grep "n_packets=1" -c) -+]) -+ -+OVN_CLEANUP([hv1]) -+AT_CLEANUP -+ - AT_SETUP([ovn -- Load balancer selection fields]) - AT_KEYWORDS([lb]) - ovn_start --- -2.26.2 - diff --git a/SOURCES/0003-ovsdb-idl-Avoid-inconsistent-IDL-state-with-OVSDB_MO.patch b/SOURCES/0003-ovsdb-idl-Avoid-inconsistent-IDL-state-with-OVSDB_MO.patch new file mode 100644 index 0000000..c2856ef --- /dev/null +++ b/SOURCES/0003-ovsdb-idl-Avoid-inconsistent-IDL-state-with-OVSDB_MO.patch @@ -0,0 +1,460 @@ +From 2b29d852dc13843c72c3820fb82c1b123f3f77d7 Mon Sep 17 00:00:00 2001 +From: Dumitru Ceara +Date: Thu, 28 May 2020 14:32:31 +0200 +Subject: [PATCH 3/4] ovsdb-idl: Avoid inconsistent IDL state with + OVSDB_MONITOR_V3. + +Assuming an ovsdb client connected to a database using OVSDB_MONITOR_V3 +(i.e., "monitor_cond_since" method) with the initial monitor condition +MC1. + +Assuming the following two transactions are executed on the +ovsdb-server: +TXN1: "insert record R1 in table T1" +TXN2: "insert record R2 in table T2" + +If the client's monitor condition MC1 for table T2 matches R2 then the +client will receive the following update3 message: +method="update3", "insert record R2 in table T2", last-txn-id=TXN2 + +At this point, if the presence of the new record R2 in the IDL triggers +the client to update its monitor condition to MC2 and add a clause for +table T1 which matches R1, a monitor_cond_change message is sent to the +server: +method="monitor_cond_change", "clauses from MC2" + +In normal operation the ovsdb-server will reply with a new update3 +message of the form: +method="update3", "insert record R1 in table T1", last-txn-id=TXN2 + +However, if the connection drops in the meantime, this last update might +get lost. + +It might happen that during the reconnect a new transaction happens +that modifies the original record R1: +TXN3: "modify record R1 in table T1" + +When the client reconnects, it will try to perform a fast resync by +sending: +method="monitor_cond_since", "clauses from MC2", last-txn-id=TXN2 + +Because TXN2 is still in the ovsdb-server transaction history, the +server replies with the changes from the most recent transactions only, +i.e., TXN3: +result="true", last-txbb-id=TXN3, "modify record R1 in table T1" + +This causes the IDL on the client in to end up in an inconsistent +state because it has never seen the update that created R1. + +Such a scenario is described in: +https://bugzilla.redhat.com/show_bug.cgi?id=1808580#c22 + +To avoid this issue, the IDL will now maintain (up to) 3 different +types of conditions for each DB table: +- new_cond: condition that has been set by the IDL client but has + not yet been sent to the server through monitor_cond_change. +- req_cond: condition that has been sent to the server but the reply + acknowledging the change hasn't been received yet. +- ack_cond: condition that has been acknowledged by the server. + +Whenever the IDL FSM is restarted (e.g., voluntary or involuntary +disconnect): +- if there is a known last_id txn-id the code ensures that new_cond + will contain the most recent condition set by the IDL client + (either req_cond if there was a request in flight, or new_cond + if the IDL client set a condition while the IDL was disconnected) +- if there is no known last_id txn-id the code ensures that ack_cond will + contain the most recent conditions set by the IDL client regardless + whether they were acked by the server or not. + +When monitor_cond_since/monitor_cond requests are sent they will +always include ack_cond and if new_cond is not NULL a follow up +monitor_cond_change will be generated afterwards. + +On the other hand ovsdb_idl_db_set_condition() will always modify new_cond. + +This ensures that updates of type "insert" that happened before the last +transaction known by the IDL but didn't match old monitor conditions are +sent upon reconnect if the monitor condition has changed to include them +in the meantime. + +Fixes: 403a6a0cb003 ("ovsdb-idl: Fast resync from server when connection reset.") +Signed-off-by: Dumitru Ceara +Acked-by: Han Zhou +Signed-off-by: Ilya Maximets +(cherry picked from upstream OVS commit ae25f8c8fff80a58cd0a15e2d3ae7ab1b4994e48) + +Change-Id: I4f3cd43cf69dfe76eb65c9709b759e5062c29e89 +--- + openvswitch-2.13.0/lib/ovsdb-idl-provider.h | 8 +- + openvswitch-2.13.0/lib/ovsdb-idl.c | 167 +++++++++++++++++--- + openvswitch-2.13.0/tests/ovsdb-idl.at | 56 +++++++ + 3 files changed, 206 insertions(+), 25 deletions(-) + +diff --git a/openvswitch-2.13.0/lib/ovsdb-idl-provider.h b/openvswitch-2.13.0/lib/ovsdb-idl-provider.h +index 30d1d08eb..00497d940 100644 +--- a/openvswitch-2.13.0/lib/ovsdb-idl-provider.h ++++ b/openvswitch-2.13.0/lib/ovsdb-idl-provider.h +@@ -122,8 +122,12 @@ struct ovsdb_idl_table { + unsigned int change_seqno[OVSDB_IDL_CHANGE_MAX]; + struct ovs_list indexes; /* Contains "struct ovsdb_idl_index"s */ + struct ovs_list track_list; /* Tracked rows (ovsdb_idl_row.track_node). */ +- struct ovsdb_idl_condition condition; +- bool cond_changed; ++ struct ovsdb_idl_condition *ack_cond; /* Last condition acked by the ++ * server. */ ++ struct ovsdb_idl_condition *req_cond; /* Last condition requested to the ++ * server. */ ++ struct ovsdb_idl_condition *new_cond; /* Latest condition set by the IDL ++ * client. */ + }; + + struct ovsdb_idl_class { +diff --git a/openvswitch-2.13.0/lib/ovsdb-idl.c b/openvswitch-2.13.0/lib/ovsdb-idl.c +index 2d351791f..8eb421366 100644 +--- a/openvswitch-2.13.0/lib/ovsdb-idl.c ++++ b/openvswitch-2.13.0/lib/ovsdb-idl.c +@@ -240,6 +240,10 @@ static void ovsdb_idl_send_monitor_request(struct ovsdb_idl *, + struct ovsdb_idl_db *, + enum ovsdb_idl_monitor_method); + static void ovsdb_idl_db_clear(struct ovsdb_idl_db *db); ++static void ovsdb_idl_db_ack_condition(struct ovsdb_idl_db *db); ++static void ovsdb_idl_db_sync_condition(struct ovsdb_idl_db *db); ++static void ovsdb_idl_condition_move(struct ovsdb_idl_condition **dst, ++ struct ovsdb_idl_condition **src); + + struct ovsdb_idl { + struct ovsdb_idl_db server; +@@ -424,9 +428,11 @@ ovsdb_idl_db_init(struct ovsdb_idl_db *db, const struct ovsdb_idl_class *class, + = table->change_seqno[OVSDB_IDL_CHANGE_MODIFY] + = table->change_seqno[OVSDB_IDL_CHANGE_DELETE] = 0; + table->db = db; +- ovsdb_idl_condition_init(&table->condition); +- ovsdb_idl_condition_add_clause_true(&table->condition); +- table->cond_changed = false; ++ table->ack_cond = NULL; ++ table->req_cond = NULL; ++ table->new_cond = xmalloc(sizeof *table->new_cond); ++ ovsdb_idl_condition_init(table->new_cond); ++ ovsdb_idl_condition_add_clause_true(table->new_cond); + } + db->monitor_id = json_array_create_2(json_string_create("monid"), + json_string_create(class->database)); +@@ -558,12 +564,15 @@ ovsdb_idl_set_shuffle_remotes(struct ovsdb_idl *idl, bool shuffle) + static void + ovsdb_idl_db_destroy(struct ovsdb_idl_db *db) + { ++ struct ovsdb_idl_condition *null_cond = NULL; + ovs_assert(!db->txn); + ovsdb_idl_db_txn_abort_all(db); + ovsdb_idl_db_clear(db); + for (size_t i = 0; i < db->class_->n_tables; i++) { + struct ovsdb_idl_table *table = &db->tables[i]; +- ovsdb_idl_condition_destroy(&table->condition); ++ ovsdb_idl_condition_move(&table->ack_cond, &null_cond); ++ ovsdb_idl_condition_move(&table->req_cond, &null_cond); ++ ovsdb_idl_condition_move(&table->new_cond, &null_cond); + ovsdb_idl_destroy_indexes(table); + shash_destroy(&table->columns); + hmap_destroy(&table->rows); +@@ -692,6 +701,12 @@ ovsdb_idl_send_request(struct ovsdb_idl *idl, struct jsonrpc_msg *request) + static void + ovsdb_idl_restart_fsm(struct ovsdb_idl *idl) + { ++ /* Resync data DB table conditions to avoid missing updates due to ++ * conditions that were in flight or changed locally while the connection ++ * was down. ++ */ ++ ovsdb_idl_db_sync_condition(&idl->data); ++ + ovsdb_idl_send_schema_request(idl, &idl->server); + ovsdb_idl_transition(idl, IDL_S_SERVER_SCHEMA_REQUESTED); + idl->data.monitoring = OVSDB_IDL_NOT_MONITORING; +@@ -799,7 +814,9 @@ ovsdb_idl_process_response(struct ovsdb_idl *idl, struct jsonrpc_msg *msg) + * do, it's a "monitor_cond_change", which means that the conditional + * monitor clauses were updated. + * +- * If further condition changes were pending, send them now. */ ++ * Mark the last requested conditions as acked and if further ++ * condition changes were pending, send them now. */ ++ ovsdb_idl_db_ack_condition(&idl->data); + ovsdb_idl_send_cond_change(idl); + idl->data.cond_seqno++; + break; +@@ -1495,30 +1512,60 @@ ovsdb_idl_condition_equals(const struct ovsdb_idl_condition *a, + } + + static void +-ovsdb_idl_condition_clone(struct ovsdb_idl_condition *dst, ++ovsdb_idl_condition_clone(struct ovsdb_idl_condition **dst, + const struct ovsdb_idl_condition *src) + { +- ovsdb_idl_condition_init(dst); ++ if (*dst) { ++ ovsdb_idl_condition_destroy(*dst); ++ } else { ++ *dst = xmalloc(sizeof **dst); ++ } ++ ovsdb_idl_condition_init(*dst); + +- dst->is_true = src->is_true; ++ (*dst)->is_true = src->is_true; + + const struct ovsdb_idl_clause *clause; + HMAP_FOR_EACH (clause, hmap_node, &src->clauses) { +- ovsdb_idl_condition_add_clause__(dst, clause, clause->hmap_node.hash); ++ ovsdb_idl_condition_add_clause__(*dst, clause, clause->hmap_node.hash); + } + } + ++static void ++ovsdb_idl_condition_move(struct ovsdb_idl_condition **dst, ++ struct ovsdb_idl_condition **src) ++{ ++ if (*dst) { ++ ovsdb_idl_condition_destroy(*dst); ++ free(*dst); ++ } ++ *dst = *src; ++ *src = NULL; ++} ++ + static unsigned int + ovsdb_idl_db_set_condition(struct ovsdb_idl_db *db, + const struct ovsdb_idl_table_class *tc, + const struct ovsdb_idl_condition *condition) + { ++ struct ovsdb_idl_condition *table_cond; + struct ovsdb_idl_table *table = ovsdb_idl_db_table_from_class(db, tc); + unsigned int seqno = db->cond_seqno; +- if (!ovsdb_idl_condition_equals(condition, &table->condition)) { +- ovsdb_idl_condition_destroy(&table->condition); +- ovsdb_idl_condition_clone(&table->condition, condition); +- db->cond_changed = table->cond_changed = true; ++ ++ /* Compare the new condition to the last known condition which can be ++ * either "new" (not sent yet), "requested" or "acked", in this order. ++ */ ++ if (table->new_cond) { ++ table_cond = table->new_cond; ++ } else if (table->req_cond) { ++ table_cond = table->req_cond; ++ } else { ++ table_cond = table->ack_cond; ++ } ++ ovs_assert(table_cond); ++ ++ if (!ovsdb_idl_condition_equals(condition, table_cond)) { ++ ovsdb_idl_condition_clone(&table->new_cond, condition); ++ db->cond_changed = true; + poll_immediate_wake(); + return seqno + 1; + } +@@ -1563,9 +1610,8 @@ ovsdb_idl_condition_to_json(const struct ovsdb_idl_condition *cnd) + } + + static struct json * +-ovsdb_idl_create_cond_change_req(struct ovsdb_idl_table *table) ++ovsdb_idl_create_cond_change_req(const struct ovsdb_idl_condition *cond) + { +- const struct ovsdb_idl_condition *cond = &table->condition; + struct json *monitor_cond_change_request = json_object_create(); + struct json *cond_json = ovsdb_idl_condition_to_json(cond); + +@@ -1585,8 +1631,12 @@ ovsdb_idl_db_compose_cond_change(struct ovsdb_idl_db *db) + for (size_t i = 0; i < db->class_->n_tables; i++) { + struct ovsdb_idl_table *table = &db->tables[i]; + +- if (table->cond_changed) { +- struct json *req = ovsdb_idl_create_cond_change_req(table); ++ /* Always use the most recent conditions set by the IDL client when ++ * requesting monitor_cond_change, i.e., table->new_cond. ++ */ ++ if (table->new_cond) { ++ struct json *req = ++ ovsdb_idl_create_cond_change_req(table->new_cond); + if (req) { + if (!monitor_cond_change_requests) { + monitor_cond_change_requests = json_object_create(); +@@ -1595,7 +1645,11 @@ ovsdb_idl_db_compose_cond_change(struct ovsdb_idl_db *db) + table->class_->name, + json_array_create_1(req)); + } +- table->cond_changed = false; ++ /* Mark the new condition as requested by moving it to req_cond. ++ * If there's already requested condition that's a bug. ++ */ ++ ovs_assert(table->req_cond == NULL); ++ ovsdb_idl_condition_move(&table->req_cond, &table->new_cond); + } + } + +@@ -1610,6 +1664,73 @@ ovsdb_idl_db_compose_cond_change(struct ovsdb_idl_db *db) + return jsonrpc_create_request("monitor_cond_change", params, NULL); + } + ++/* Marks all requested table conditions in 'db' as acked by the server. ++ * It should be called when the server replies to monitor_cond_change ++ * requests. ++ */ ++static void ++ovsdb_idl_db_ack_condition(struct ovsdb_idl_db *db) ++{ ++ for (size_t i = 0; i < db->class_->n_tables; i++) { ++ struct ovsdb_idl_table *table = &db->tables[i]; ++ ++ if (table->req_cond) { ++ ovsdb_idl_condition_move(&table->ack_cond, &table->req_cond); ++ } ++ } ++} ++ ++/* Should be called when the IDL fsm is restarted and resyncs table conditions ++ * based on the state the DB is in: ++ * - if a non-zero last_id is available for the DB then upon reconnect ++ * the IDL should first request acked conditions to avoid missing updates ++ * about records that were added before the transaction with ++ * txn-id == last_id. If there were requested condition changes in flight ++ * (i.e., req_cond not NULL) and the IDL client didn't set new conditions ++ * (i.e., new_cond is NULL) then move req_cond to new_cond to trigger a ++ * follow up monitor_cond_change request. ++ * - if there's no last_id available for the DB then it's safe to use the ++ * latest conditions set by the IDL client even if they weren't acked yet. ++ */ ++static void ++ovsdb_idl_db_sync_condition(struct ovsdb_idl_db *db) ++{ ++ bool ack_all = uuid_is_zero(&db->last_id); ++ ++ db->cond_changed = false; ++ for (size_t i = 0; i < db->class_->n_tables; i++) { ++ struct ovsdb_idl_table *table = &db->tables[i]; ++ ++ /* When monitor_cond_since requests will be issued, the ++ * table->ack_cond condition will be added to the "where" clause". ++ * Follow up monitor_cond_change requests will use table->new_cond. ++ */ ++ if (ack_all) { ++ if (table->new_cond) { ++ ovsdb_idl_condition_move(&table->req_cond, &table->new_cond); ++ } ++ ++ if (table->req_cond) { ++ ovsdb_idl_condition_move(&table->ack_cond, &table->req_cond); ++ } ++ } else { ++ /* If there was no "unsent" condition but instead a ++ * monitor_cond_change request was in flight, move table->req_cond ++ * to table->new_cond and set db->cond_changed to trigger a new ++ * monitor_cond_change request. ++ * ++ * However, if a new condition has been set by the IDL client, ++ * monitor_cond_change will be sent anyway and will use the most ++ * recent table->new_cond so there's no need to update it here. ++ */ ++ if (table->req_cond && !table->new_cond) { ++ ovsdb_idl_condition_move(&table->new_cond, &table->req_cond); ++ db->cond_changed = true; ++ } ++ } ++ } ++} ++ + static void + ovsdb_idl_send_cond_change(struct ovsdb_idl *idl) + { +@@ -2064,13 +2185,15 @@ ovsdb_idl_send_monitor_request(struct ovsdb_idl *idl, struct ovsdb_idl_db *db, + monitor_request = json_object_create(); + json_object_put(monitor_request, "columns", columns); + +- const struct ovsdb_idl_condition *cond = &table->condition; ++ /* Always use acked conditions when requesting ++ * monitor_cond/monitor_cond_since. ++ */ ++ const struct ovsdb_idl_condition *cond = table->ack_cond; + if ((monitor_method == OVSDB_IDL_MM_MONITOR_COND || + monitor_method == OVSDB_IDL_MM_MONITOR_COND_SINCE) && +- !ovsdb_idl_condition_is_true(cond)) { ++ cond && !ovsdb_idl_condition_is_true(cond)) { + json_object_put(monitor_request, "where", + ovsdb_idl_condition_to_json(cond)); +- table->cond_changed = false; + } + json_object_put(monitor_requests, tc->name, + json_array_create_1(monitor_request)); +@@ -2078,8 +2201,6 @@ ovsdb_idl_send_monitor_request(struct ovsdb_idl *idl, struct ovsdb_idl_db *db, + } + free_schema(schema); + +- db->cond_changed = false; +- + struct json *params = json_array_create_3( + json_string_create(db->class_->database), + json_clone(db->monitor_id), +diff --git a/openvswitch-2.13.0/tests/ovsdb-idl.at b/openvswitch-2.13.0/tests/ovsdb-idl.at +index cc38d69c1..a5ca96646 100644 +--- a/openvswitch-2.13.0/tests/ovsdb-idl.at ++++ b/openvswitch-2.13.0/tests/ovsdb-idl.at +@@ -1814,3 +1814,59 @@ m4_define([OVSDB_CHECK_IDL_LEADER_ONLY_PY], + + OVSDB_CHECK_IDL_LEADER_ONLY_PY([Check Python IDL connects to leader], 3, ['remote']) + OVSDB_CHECK_IDL_LEADER_ONLY_PY([Check Python IDL reconnects to leader], 3, ['remote' '+remotestop' 'remote']) ++ ++# same as OVSDB_CHECK_IDL but uses C IDL implementation with tcp ++# with multiple remotes. ++m4_define([OVSDB_CHECK_CLUSTER_IDL_C], ++ [AT_SETUP([$1 - C - tcp]) ++ AT_KEYWORDS([ovsdb server idl positive tcp socket $5]) ++ m4_define([LPBK],[127.0.0.1]) ++ AT_CHECK([ovsdb_cluster_start_idltest $2 "ptcp:0:"LPBK]) ++ PARSE_LISTENING_PORT([s1.log], [TCP_PORT_1]) ++ PARSE_LISTENING_PORT([s2.log], [TCP_PORT_2]) ++ PARSE_LISTENING_PORT([s3.log], [TCP_PORT_3]) ++ remotes=tcp:LPBK:$TCP_PORT_1,tcp:LPBK:$TCP_PORT_2,tcp:LPBK:$TCP_PORT_3 ++ ++ m4_if([$3], [], [], ++ [AT_CHECK([ovsdb-client transact $remotes $3], [0], [ignore], [ignore])]) ++ AT_CHECK([test-ovsdb '-vPATTERN:console:test-ovsdb|%c|%m' -vjsonrpc -t10 idl tcp:LPBK:$TCP_PORT_1 $4], ++ [0], [stdout], [ignore]) ++ AT_CHECK([sort stdout | uuidfilt]m4_if([$7],,, [[| $7]]), ++ [0], [$5]) ++ AT_CLEANUP]) ++ ++# Checks that monitor_cond_since works fine when disconnects happen ++# with cond_change requests in flight (i.e., IDL is properly updated). ++OVSDB_CHECK_CLUSTER_IDL_C([simple idl, monitor_cond_since, cluster disconnect], ++ 3, ++ [['["idltest", ++ {"op": "insert", ++ "table": "simple", ++ "row": {"i": 1, ++ "r": 1.0, ++ "b": true}}, ++ {"op": "insert", ++ "table": "simple", ++ "row": {"i": 2, ++ "r": 1.0, ++ "b": true}}]']], ++ [['condition simple []' \ ++ 'condition simple [["i","==",2]]' \ ++ 'condition simple [["i","==",1]]' \ ++ '+reconnect' \ ++ '["idltest", ++ {"op": "update", ++ "table": "simple", ++ "where": [["i", "==", 1]], ++ "row": {"r": 2.0 }}]']], ++ [[000: change conditions ++001: empty ++002: change conditions ++003: i=2 r=1 b=true s= u=<0> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<1> ++004: change conditions ++005: reconnect ++006: i=2 r=1 b=true s= u=<0> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<1> ++007: {"error":null,"result":[{"count":1}]} ++008: i=1 r=2 b=true s= u=<0> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<2> ++009: done ++]]) +-- +2.26.2 + diff --git a/SOURCES/0003-ovsdb-idl-Try-committing-the-pending-txn-in-ovsdb_id.patch b/SOURCES/0003-ovsdb-idl-Try-committing-the-pending-txn-in-ovsdb_id.patch deleted file mode 100644 index 64e808e..0000000 --- a/SOURCES/0003-ovsdb-idl-Try-committing-the-pending-txn-in-ovsdb_id.patch +++ /dev/null @@ -1,217 +0,0 @@ -From 22a9d1b20218d2467f30c9a87205344ff787bcf8 Mon Sep 17 00:00:00 2001 -From: Numan Siddique -Date: Fri, 5 Jun 2020 14:00:29 +0530 -Subject: [PATCH 3/3] ovsdb idl: Try committing the pending txn in - ovsdb_idl_loop_run. - -The function ovsdb_idl_loop_run(), after calling ovsdb_idl_run(), -returns a transaction object (of type 'struct ovsdb_idl_txn'). -The returned transaction object can be NULL if there is a pending -transaction (loop->committing_txn) in the idl loop object. - -Normally the clients of idl library, first call ovsdb_idl_loop_run(), -then do their own processing and create any idl transactions during -this processing and then finally call ovsdb_idl_loop_commit_and_wait(). - -If ovsdb_idl_loop_run() returns NULL transaction object, then much -of the processing done by the client gets wasted as in the case -of ovn-controller. - -The client (in this case ovn-controller), can skip the processing -and instead call ovsdb_idl_loop_commit_and_wait() if the transaction -oject is NULL. But ovn-controller uses IDL tracking and it may -loose the tracked changes in that run. - -This patch tries to improve this scenario, by checking if the -pending transaction can be committed in the ovsdb_idl_loop_run() -itself and if the pending transaction is cleared (because of the -response messages from ovsdb-server due to a transaction message -in the previous run), ovsdb_idl_loop_run() can return a valid -transaction object. - -CC: Han Zhou -Signed-off-by: Numan Siddique -Signed-off-by: Ben Pfaff ---- - openvswitch-2.13.0/lib/ovsdb-idl.c | 143 +++++++++++++++++++---------- - 1 file changed, 93 insertions(+), 50 deletions(-) - -diff --git a/openvswitch-2.13.0/lib/ovsdb-idl.c b/openvswitch-2.13.0/lib/ovsdb-idl.c -index 1535ad7b5..2d351791f 100644 ---- a/openvswitch-2.13.0/lib/ovsdb-idl.c -+++ b/openvswitch-2.13.0/lib/ovsdb-idl.c -@@ -385,6 +385,8 @@ static void ovsdb_idl_send_cond_change(struct ovsdb_idl *idl); - static void ovsdb_idl_destroy_indexes(struct ovsdb_idl_table *); - static void ovsdb_idl_add_to_indexes(const struct ovsdb_idl_row *); - static void ovsdb_idl_remove_from_indexes(const struct ovsdb_idl_row *); -+static int ovsdb_idl_try_commit_loop_txn(struct ovsdb_idl_loop *loop, -+ bool *may_need_wakeup); - - static void - ovsdb_idl_db_init(struct ovsdb_idl_db *db, const struct ovsdb_idl_class *class, -@@ -5329,6 +5331,12 @@ struct ovsdb_idl_txn * - ovsdb_idl_loop_run(struct ovsdb_idl_loop *loop) - { - ovsdb_idl_run(loop->idl); -+ -+ /* See if we can commit the loop->committing_txn. */ -+ if (loop->committing_txn) { -+ ovsdb_idl_try_commit_loop_txn(loop, NULL); -+ } -+ - loop->open_txn = (loop->committing_txn - || ovsdb_idl_get_seqno(loop->idl) == loop->skip_seqno - ? NULL -@@ -5336,6 +5344,87 @@ ovsdb_idl_loop_run(struct ovsdb_idl_loop *loop) - return loop->open_txn; - } - -+/* Attempts to commit the current transaction, if one is open. -+ * -+ * If a transaction was open, in this or a previous iteration of the main loop, -+ * and had not before finished committing (successfully or unsuccessfully), the -+ * return value is one of: -+ * -+ * 1: The transaction committed successfully (or it did not change anything in -+ * the database). -+ * 0: The transaction failed. -+ * -1: The commit is still in progress. -+ * -+ * Thus, the return value is -1 if the transaction is in progress and otherwise -+ * true for success, false for failure. -+ * -+ * (In the corner case where the IDL sends a transaction to the database and -+ * the database commits it, and the connection between the IDL and the database -+ * drops before the IDL receives the message confirming the commit, this -+ * function can return 0 even though the transaction succeeded.) -+ */ -+static int -+ovsdb_idl_try_commit_loop_txn(struct ovsdb_idl_loop *loop, -+ bool *may_need_wakeup) -+{ -+ if (!loop->committing_txn) { -+ /* Not a meaningful return value: no transaction was in progress. */ -+ return 1; -+ } -+ -+ int retval; -+ struct ovsdb_idl_txn *txn = loop->committing_txn; -+ -+ enum ovsdb_idl_txn_status status = ovsdb_idl_txn_commit(txn); -+ if (status != TXN_INCOMPLETE) { -+ switch (status) { -+ case TXN_TRY_AGAIN: -+ /* We want to re-evaluate the database when it's changed from -+ * the contents that it had when we started the commit. (That -+ * might have already happened.) */ -+ loop->skip_seqno = loop->precommit_seqno; -+ if (ovsdb_idl_get_seqno(loop->idl) != loop->skip_seqno -+ && may_need_wakeup) { -+ *may_need_wakeup = true; -+ } -+ retval = 0; -+ break; -+ -+ case TXN_SUCCESS: -+ /* Possibly some work on the database was deferred because no -+ * further transaction could proceed. Wake up again. */ -+ retval = 1; -+ loop->cur_cfg = loop->next_cfg; -+ if (may_need_wakeup) { -+ *may_need_wakeup = true; -+ } -+ break; -+ -+ case TXN_UNCHANGED: -+ retval = 1; -+ loop->cur_cfg = loop->next_cfg; -+ break; -+ -+ case TXN_ABORTED: -+ case TXN_NOT_LOCKED: -+ case TXN_ERROR: -+ retval = 0; -+ break; -+ -+ case TXN_UNCOMMITTED: -+ case TXN_INCOMPLETE: -+ default: -+ OVS_NOT_REACHED(); -+ } -+ ovsdb_idl_txn_destroy(txn); -+ loop->committing_txn = NULL; -+ } else { -+ retval = -1; -+ } -+ -+ return retval; -+} -+ - /* Attempts to commit the current transaction, if one is open, and sets up the - * poll loop to wake up when some more work might be needed. - * -@@ -5366,57 +5455,11 @@ ovsdb_idl_loop_commit_and_wait(struct ovsdb_idl_loop *loop) - loop->precommit_seqno = ovsdb_idl_get_seqno(loop->idl); - } - -- struct ovsdb_idl_txn *txn = loop->committing_txn; -- int retval; -- if (txn) { -- enum ovsdb_idl_txn_status status = ovsdb_idl_txn_commit(txn); -- if (status != TXN_INCOMPLETE) { -- switch (status) { -- case TXN_TRY_AGAIN: -- /* We want to re-evaluate the database when it's changed from -- * the contents that it had when we started the commit. (That -- * might have already happened.) */ -- loop->skip_seqno = loop->precommit_seqno; -- if (ovsdb_idl_get_seqno(loop->idl) != loop->skip_seqno) { -- poll_immediate_wake(); -- } -- retval = 0; -- break; -- -- case TXN_SUCCESS: -- /* Possibly some work on the database was deferred because no -- * further transaction could proceed. Wake up again. */ -- retval = 1; -- loop->cur_cfg = loop->next_cfg; -- poll_immediate_wake(); -- break; -- -- case TXN_UNCHANGED: -- retval = 1; -- loop->cur_cfg = loop->next_cfg; -- break; -- -- case TXN_ABORTED: -- case TXN_NOT_LOCKED: -- case TXN_ERROR: -- retval = 0; -- break; -- -- case TXN_UNCOMMITTED: -- case TXN_INCOMPLETE: -- default: -- OVS_NOT_REACHED(); -- } -- ovsdb_idl_txn_destroy(txn); -- loop->committing_txn = NULL; -- } else { -- retval = -1; -- } -- } else { -- /* Not a meaningful return value: no transaction was in progress. */ -- retval = 1; -+ bool may_need_wakeup = false; -+ int retval = ovsdb_idl_try_commit_loop_txn(loop, &may_need_wakeup); -+ if (may_need_wakeup) { -+ poll_immediate_wake(); - } -- - ovsdb_idl_wait(loop->idl); - - return retval; --- -2.26.2 - diff --git a/SOURCES/0004-ovn-nbctl-Enhance-lr-policy-add-to-set-the-options.patch b/SOURCES/0004-ovn-nbctl-Enhance-lr-policy-add-to-set-the-options.patch deleted file mode 100644 index 379c3df..0000000 --- a/SOURCES/0004-ovn-nbctl-Enhance-lr-policy-add-to-set-the-options.patch +++ /dev/null @@ -1,254 +0,0 @@ -From 025fec42cebe9efc7c6a2d94816b173748e7e4f6 Mon Sep 17 00:00:00 2001 -From: Numan Siddique -Date: Tue, 23 Jun 2020 17:07:04 +0530 -Subject: [PATCH 04/22] ovn-nbctl: Enhance lr-policy-add to set the options. - -The commit [1] added a new column - 'options' to Logical_Router_Policy NB DB -table. This patch enhances the lr-policy-add command to set the options -as key=value pairs. - -For nbctl_lr_policy_add(), this patch now returns after ctl_error() as there is no -point continuing further and the comments in the ctl_error() implementation says so. - -[1] - a123ef0fb8fd("Support packet metadata marking for logical router policies.") - -Acked-by: Mark Michelson -Signed-off-by: Numan Siddique - -(cherry-picked from upstream master commit 742474bad730fbdc9705b4c2784a2b4acca327cf) - -Change-Id: I64c786ff4c5244b643a57bff270a14d85d5204f1 ---- - tests/ovn-nbctl.at | 15 +++++++++--- - tests/ovn.at | 8 ++----- - utilities/ovn-nbctl.8.xml | 11 ++++++++- - utilities/ovn-nbctl.c | 48 ++++++++++++++++++++++++++++++++++----- - 4 files changed, 66 insertions(+), 16 deletions(-) - -diff --git a/tests/ovn-nbctl.at b/tests/ovn-nbctl.at -index dc9d9d76a..6d6608729 100644 ---- a/tests/ovn-nbctl.at -+++ b/tests/ovn-nbctl.at -@@ -1590,11 +1590,20 @@ AT_CHECK([ovn-nbctl lr-add lr0]) - - dnl Add policies with allow and drop actions - AT_CHECK([ovn-nbctl lr-policy-add lr0 100 "ip4.src == 1.1.1.0/24" drop]) --AT_CHECK([ovn-nbctl lr-policy-add lr0 100 "ip4.src == 1.1.2.0/24" allow]) -+AT_CHECK([ovn-nbctl lr-policy-add lr0 100 "ip4.src == 1.1.2.0/24" allow pkt_mark=100,foo=bar]) - AT_CHECK([ovn-nbctl lr-policy-add lr0 101 "ip4.src == 2.1.1.0/24" allow]) - AT_CHECK([ovn-nbctl lr-policy-add lr0 101 "ip4.src == 2.1.2.0/24" drop]) - AT_CHECK([ovn-nbctl lr-policy-add lr0 101 "ip6.src == 2002::/64" drop]) - -+dnl Incomplete option set. -+AT_CHECK([ovn-nbctl lr-policy-add lr0 200 "ip4.src == 1.1.4.0/24" reroute 192.168.0.10 foo], [1], [], -+ [ovn-nbctl: No value specified for the option : foo -+]) -+ -+AT_CHECK([ovn-nbctl lr-policy-add lr0 200 "ip4.src == 1.1.4.0/24" allow bar=], [1], [], -+ [ovn-nbctl: No value specified for the option : bar -+]) -+ - dnl Add duplicated policy - AT_CHECK([ovn-nbctl lr-policy-add lr0 100 "ip4.src == 1.1.1.0/24" drop], [1], [], - [ovn-nbctl: Same routing policy already existed on the logical router lr0. -@@ -1612,14 +1621,14 @@ Routing Policies - 101 ip4.src == 2.1.1.0/24 allow - 101 ip4.src == 2.1.2.0/24 drop - 101 ip6.src == 2002::/64 drop -- 100 ip4.src == 1.1.2.0/24 allow -+ 100 ip4.src == 1.1.2.0/24 allow pkt_mark=100,foo=bar - ]) - - dnl Delete all policies for given priority - AT_CHECK([ovn-nbctl lr-policy-del lr0 101]) - AT_CHECK([ovn-nbctl lr-policy-list lr0], [0], [dnl - Routing Policies -- 100 ip4.src == 1.1.2.0/24 allow -+ 100 ip4.src == 1.1.2.0/24 allow pkt_mark=100,foo=bar - ]) - - -diff --git a/tests/ovn.at b/tests/ovn.at -index 8ce45823f..b84cf75fd 100644 ---- a/tests/ovn.at -+++ b/tests/ovn.at -@@ -20286,20 +20286,16 @@ static_routes @lrt - ovn-nbctl --wait=hv sync - - # Add logical router policy and set pkt_mark on it. --ovn-nbctl lr-policy-add lr0 2000 "ip4.src == 10.0.0.3" allow -+ovn-nbctl lr-policy-add lr0 2000 "ip4.src == 10.0.0.3" allow pkt_mark=100 - ovn-nbctl lr-policy-add lr0 1000 "ip4.src == 10.0.0.4" allow --ovn-nbctl lr-policy-add lr0 900 "ip4.src == 10.0.0.5" reroute 172.168.0.200 -+ovn-nbctl lr-policy-add lr0 900 "ip4.src == 10.0.0.5" reroute 172.168.0.200 pkt_mark=3 - ovn-nbctl lr-policy-add lr0 2001 "ip6.dst == bef0::5" reroute bef0::6 - ovn-nbctl lr-policy-add lr0 1001 "ip6" allow - -- - pol1=$(ovn-nbctl --bare --columns _uuid find logical_router_policy priority=2000) --pol3=$(ovn-nbctl --bare --columns _uuid find logical_router_policy priority=900) - pol4=$(ovn-nbctl --bare --columns _uuid find logical_router_policy priority=2001) - pol5=$(ovn-nbctl --bare --columns _uuid find logical_router_policy priority=1001) - --ovn-nbctl set logical_router_policy $pol1 options:pkt_mark=100 --ovn-nbctl set logical_router_policy $pol3 options:pkt_mark=3 - ovn-nbctl set logical_router_policy $pol4 options:pkt_mark=4 - ovn-nbctl set logical_router_policy $pol5 options:pkt_mark=5 - ovn-nbctl --wait=hv sync -diff --git a/utilities/ovn-nbctl.8.xml b/utilities/ovn-nbctl.8.xml -index d265c7fcc..de86b70e6 100644 ---- a/utilities/ovn-nbctl.8.xml -+++ b/utilities/ovn-nbctl.8.xml -@@ -721,7 +721,8 @@ - -
    -
    lr-policy-add router priority -- match action [nexthop]
    -+ match action [nexthop] -+ [options key=value]] -
    -

    - Add Policy to router which provides a way to configure -@@ -732,6 +733,8 @@ - only when action is reroute. A policy is - uniquely identified by priority and match. - Multiple policies can have the same priority. -+ options sets the router policy options as key-value pair. -+ The supported option is : pkt_mark. -

    - -

    -@@ -743,6 +746,12 @@ - lr-policy-add lr1 100 ip4.src == 192.168.100.0/24 drop. -

    - -+

    -+ -+ lr-policy-add lr1 100 ip4.src == 192.168.100.0/24 allow -+ pkt_mark=100 -+ . -+

    -
    - -
    lr-policy-del router [{priority | uuid} -diff --git a/utilities/ovn-nbctl.c b/utilities/ovn-nbctl.c -index 159a44960..7578b9928 100644 ---- a/utilities/ovn-nbctl.c -+++ b/utilities/ovn-nbctl.c -@@ -694,7 +694,8 @@ Route commands:\n\ - lr-route-list ROUTER print routes for ROUTER\n\ - \n\ - Policy commands:\n\ -- lr-policy-add ROUTER PRIORITY MATCH ACTION [NEXTHOP]\n\ -+ lr-policy-add ROUTER PRIORITY MATCH ACTION [NEXTHOP] \ -+[OPTIONS KEY=VALUE ...] \n\ - add a policy to router\n\ - lr-policy-del ROUTER [{PRIORITY | UUID} [MATCH]]\n\ - remove policies from ROUTER\n\ -@@ -3609,16 +3610,19 @@ nbctl_lr_policy_add(struct ctl_context *ctx) - const char *action = ctx->argv[4]; - char *next_hop = NULL; - -+ bool reroute = false; - /* Validate action. */ - if (strcmp(action, "allow") && strcmp(action, "drop") - && strcmp(action, "reroute")) { - ctl_error(ctx, "%s: action must be one of \"allow\", \"drop\", " - "and \"reroute\"", action); -+ return; - } - if (!strcmp(action, "reroute")) { - if (ctx->argc < 6) { - ctl_error(ctx, "Nexthop is required when action is reroute."); - } -+ reroute = true; - } - - /* Check if same routing policy already exists. -@@ -3629,12 +3633,14 @@ nbctl_lr_policy_add(struct ctl_context *ctx) - !strcmp(policy->match, ctx->argv[3])) { - ctl_error(ctx, "Same routing policy already existed on the " - "logical router %s.", ctx->argv[1]); -+ return; - } - } -- if (ctx->argc == 6) { -+ if (reroute) { - next_hop = normalize_prefix_str(ctx->argv[5]); - if (!next_hop) { - ctl_error(ctx, "bad next hop argument: %s", ctx->argv[5]); -+ return; - } - } - -@@ -3643,9 +3649,28 @@ nbctl_lr_policy_add(struct ctl_context *ctx) - nbrec_logical_router_policy_set_priority(policy, priority); - nbrec_logical_router_policy_set_match(policy, ctx->argv[3]); - nbrec_logical_router_policy_set_action(policy, action); -- if (ctx->argc == 6) { -+ if (reroute) { - nbrec_logical_router_policy_set_nexthop(policy, next_hop); - } -+ -+ /* Parse the options. */ -+ struct smap options = SMAP_INITIALIZER(&options); -+ for (size_t i = reroute ? 6 : 5; i < ctx->argc; i++) { -+ char *key, *value; -+ value = xstrdup(ctx->argv[i]); -+ key = strsep(&value, "="); -+ if (value && value[0]) { -+ smap_add(&options, key, value); -+ } else { -+ ctl_error(ctx, "No value specified for the option : %s", key); -+ free(key); -+ return; -+ } -+ free(key); -+ } -+ nbrec_logical_router_policy_set_options(policy, &options); -+ smap_destroy(&options); -+ - nbrec_logical_router_verify_policies(lr); - struct nbrec_logical_router_policy **new_policies - = xmalloc(sizeof *new_policies * (lr->n_policies + 1)); -@@ -3773,6 +3798,16 @@ print_routing_policy(const struct nbrec_logical_router_policy *policy, - ds_put_format(s, "%10"PRId64" %50s %15s", policy->priority, - policy->match, policy->action); - } -+ -+ if (!smap_is_empty(&policy->options)) { -+ ds_put_format(s, "%15s", ""); -+ struct smap_node *node; -+ SMAP_FOR_EACH (node, &policy->options) { -+ ds_put_format(s, "%s=%s,", node->key, node->value); -+ } -+ ds_chomp(s, ','); -+ } -+ - ds_put_char(s, '\n'); - } - -@@ -3788,7 +3823,7 @@ nbctl_lr_policy_list(struct ctl_context *ctx) - return; - } - policies = xmalloc(sizeof *policies * lr->n_policies); -- for (int i = 0; i < lr->n_policies; i++) { -+ for (int i = 0; i < lr->n_policies; i++) { - const struct nbrec_logical_router_policy *policy - = lr->policies[i]; - policies[n_policies].priority = policy->priority; -@@ -6362,8 +6397,9 @@ static const struct ctl_command_syntax nbctl_commands[] = { - "", RO }, - - /* Policy commands */ -- { "lr-policy-add", 4, 5, "ROUTER PRIORITY MATCH ACTION [NEXTHOP]", NULL, -- nbctl_lr_policy_add, NULL, "", RW }, -+ { "lr-policy-add", 4, INT_MAX, -+ "ROUTER PRIORITY MATCH ACTION [NEXTHOP] [OPTIONS - KEY=VALUE ...]", -+ NULL, nbctl_lr_policy_add, NULL, "", RW }, - { "lr-policy-del", 1, 3, "ROUTER [{PRIORITY | UUID} [MATCH]]", NULL, - nbctl_lr_policy_del, NULL, "", RW }, - { "lr-policy-list", 1, 1, "ROUTER", NULL, nbctl_lr_policy_list, NULL, --- -2.26.2 - diff --git a/SOURCES/0004-ovsdb-idl-Add-function-to-reset-min_index.patch b/SOURCES/0004-ovsdb-idl-Add-function-to-reset-min_index.patch new file mode 100644 index 0000000..656edca --- /dev/null +++ b/SOURCES/0004-ovsdb-idl-Add-function-to-reset-min_index.patch @@ -0,0 +1,68 @@ +From 1de31c3a531f5db6793819fa18f6e69304db929c Mon Sep 17 00:00:00 2001 +From: Mark Michelson +Date: Fri, 1 May 2020 15:13:08 -0400 +Subject: [PATCH 4/4] ovsdb-idl: Add function to reset min_index. + +If an administrator removes all of the databases in a cluster from +disk, then ovsdb IDL clients will have a problem. The databases will all +reset their stored indexes to 0, so The IDL client's min_index will be +higher than the indexes of all databases in the cluster. This results in +the client constantly connecting to databases, detecting the data as +"stale", and then attempting to connect to another. + +This function provides a way to reset the IDL to an initial state with +min_index of 0. This way, the client will not wrongly detect the +database data as stale and will recover properly. + +Notice that this function is not actually used anywhere in this patch. +This will be used by OVN, though, since OVN is the primary user of +clustered OVSDB. + +Signed-off-by: Mark Michelson +Acked-by: Han Zhou +Signed-off-by: Ilya Maximets + +(cherry-picked from upstream ovs commit 89b522aee379f7ebd21ec67ffb622118af7e9db1) + +Change-Id: I943ece9a07566a34b11248455cc1abbe7892d4e8 +--- + openvswitch-2.13.0/lib/ovsdb-idl.c | 10 ++++++++++ + openvswitch-2.13.0/lib/ovsdb-idl.h | 1 + + 2 files changed, 11 insertions(+) + +diff --git a/openvswitch-2.13.0/lib/ovsdb-idl.c b/openvswitch-2.13.0/lib/ovsdb-idl.c +index 8eb421366..648c227d6 100644 +--- a/openvswitch-2.13.0/lib/ovsdb-idl.c ++++ b/openvswitch-2.13.0/lib/ovsdb-idl.c +@@ -561,6 +561,16 @@ ovsdb_idl_set_shuffle_remotes(struct ovsdb_idl *idl, bool shuffle) + idl->shuffle_remotes = shuffle; + } + ++/* Reset min_index to 0. This prevents a situation where the client ++ * thinks all databases have stale data, when they actually have all ++ * been destroyed and rebuilt from scratch. ++ */ ++void ++ovsdb_idl_reset_min_index(struct ovsdb_idl *idl) ++{ ++ idl->min_index = 0; ++} ++ + static void + ovsdb_idl_db_destroy(struct ovsdb_idl_db *db) + { +diff --git a/openvswitch-2.13.0/lib/ovsdb-idl.h b/openvswitch-2.13.0/lib/ovsdb-idl.h +index 9f12ce320..c56cd19b1 100644 +--- a/openvswitch-2.13.0/lib/ovsdb-idl.h ++++ b/openvswitch-2.13.0/lib/ovsdb-idl.h +@@ -64,6 +64,7 @@ struct ovsdb_idl *ovsdb_idl_create_unconnected( + const struct ovsdb_idl_class *, bool monitor_everything_by_default); + void ovsdb_idl_set_remote(struct ovsdb_idl *, const char *, bool); + void ovsdb_idl_set_shuffle_remotes(struct ovsdb_idl *, bool); ++void ovsdb_idl_reset_min_index(struct ovsdb_idl *); + void ovsdb_idl_destroy(struct ovsdb_idl *); + + void ovsdb_idl_set_leader_only(struct ovsdb_idl *, bool leader_only); +-- +2.26.2 + diff --git a/SOURCES/0005-pinctrl-Support-DHCPRELEASE-and-DHCPINFORM-in-native.patch b/SOURCES/0005-pinctrl-Support-DHCPRELEASE-and-DHCPINFORM-in-native.patch deleted file mode 100644 index b4d6b18..0000000 --- a/SOURCES/0005-pinctrl-Support-DHCPRELEASE-and-DHCPINFORM-in-native.patch +++ /dev/null @@ -1,438 +0,0 @@ -From d41a77411d154b9bdfab1a7c78deb63c7c5b990b Mon Sep 17 00:00:00 2001 -From: Numan Siddique -Date: Fri, 29 May 2020 23:29:37 +0530 -Subject: [PATCH 05/22] pinctrl: Support DHCPRELEASE and DHCPINFORM in native - OVN dhcp responder. - -Right now we ignore these dhcp packets. This patch adds the support -as per RFC 2131. - -Acked-by: Lorenzo Bianconi -Acked-by: Mark Michelson -Signed-off-by: Numan Siddique - -(cherry-picked from upstream master commit e008a4d46020a778b8f1f85b9dfd7c9e9b6fde21) - -Change-Id: I1363f99d241f661957147a7c124ac5b1c093ce00 ---- - NEWS | 2 + - controller/pinctrl.c | 125 ++++++++++++++++++++++++++++++++++--------- - lib/ovn-l7.h | 12 +++++ - tests/ovn.at | 125 ++++++++++++++++++++++++++++++++++++++++--- - 4 files changed, 233 insertions(+), 31 deletions(-) - -diff --git a/NEWS b/NEWS -index 8abdc95b9..2ed3d480a 100644 ---- a/NEWS -+++ b/NEWS -@@ -9,6 +9,8 @@ OVN v20.06.1 - 08 Jul 2020 - -------------------------- - - Added packet marking support for traffic routed with - a routing policy. -+ - Added DHCPINFORM and DHCPRELEASE support in native -+ OVN DHCPv4 responder. - - OVN v20.06.0 - 08 Jun 2020 - -------------------------- -diff --git a/controller/pinctrl.c b/controller/pinctrl.c -index dab1782f6..9231efbe3 100644 ---- a/controller/pinctrl.c -+++ b/controller/pinctrl.c -@@ -1682,11 +1682,13 @@ static void - pinctrl_handle_put_dhcp_opts( - struct rconn *swconn, - struct dp_packet *pkt_in, struct ofputil_packet_in *pin, -- struct ofpbuf *userdata, struct ofpbuf *continuation) -+ struct flow *in_flow, struct ofpbuf *userdata, -+ struct ofpbuf *continuation) - { - enum ofp_version version = rconn_get_version(swconn); - enum ofputil_protocol proto = ofputil_protocol_from_ofp_version(version); - struct dp_packet *pkt_out_ptr = NULL; -+ struct ofpbuf *dhcp_inform_reply_buf = NULL; - uint32_t success = 0; - - /* Parse result field. */ -@@ -1810,22 +1812,15 @@ pinctrl_handle_put_dhcp_opts( - VLOG_WARN_RL(&rl, "Missing DHCP message type"); - goto exit; - } -- if (*in_dhcp_msg_type != DHCP_MSG_DISCOVER && -- *in_dhcp_msg_type != DHCP_MSG_REQUEST) { -- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); -- VLOG_WARN_RL(&rl, "Invalid DHCP message type: %d", *in_dhcp_msg_type); -- goto exit; -- } - -- uint8_t msg_type; -- if (*in_dhcp_msg_type == DHCP_MSG_DISCOVER) { -+ struct ofpbuf *reply_dhcp_opts_ptr = userdata; -+ uint8_t msg_type = 0; -+ -+ switch (*in_dhcp_msg_type) { -+ case DHCP_MSG_DISCOVER: - msg_type = DHCP_MSG_OFFER; -- } else { -- /* This is a DHCPREQUEST. If the client has requested an IP that -- * does not match the offered IP address, reply with a NAK. The -- * requested IP address may be supplied either via Requested IP Address -- * (opt 50) or via ciaddr, depending on the client's state. -- */ -+ break; -+ case DHCP_MSG_REQUEST: { - msg_type = DHCP_MSG_ACK; - if (request_ip != *offer_ip) { - static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); -@@ -1834,12 +1829,81 @@ pinctrl_handle_put_dhcp_opts( - IP_ARGS(*offer_ip)); - msg_type = DHCP_MSG_NAK; - } -+ break; -+ } -+ case OVN_DHCP_MSG_RELEASE: { -+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(20, 40); -+ const struct eth_header *l2 = dp_packet_eth(pkt_in); -+ VLOG_INFO_RL(&rl, "DHCPRELEASE "ETH_ADDR_FMT " "IP_FMT"", -+ ETH_ADDR_ARGS(l2->eth_src), -+ IP_ARGS(in_dhcp_data->ciaddr)); -+ break; -+ } -+ case OVN_DHCP_MSG_INFORM: { -+ /* RFC 2131 section 3.4. -+ * Remove all the offer ip related dhcp options and -+ * all the time related dhcp options. -+ * Loop through the dhcp option defined in the userdata buffer -+ * and copy all the options into dhcp_inform_reply_buf skipping -+ * the not required ones. -+ * */ -+ msg_type = DHCP_MSG_ACK; -+ in_dhcp_ptr = userdata->data; -+ end = (const char *)userdata->data + userdata->size; -+ -+ /* The buf size cannot be greater > userdata->size. */ -+ dhcp_inform_reply_buf = ofpbuf_new(userdata->size); -+ -+ reply_dhcp_opts_ptr = dhcp_inform_reply_buf; -+ while (in_dhcp_ptr < end) { -+ const struct dhcp_opt_header *in_dhcp_opt = -+ (const struct dhcp_opt_header *)in_dhcp_ptr; -+ -+ switch (in_dhcp_opt->code) { -+ case OVN_DHCP_OPT_CODE_NETMASK: -+ case OVN_DHCP_OPT_CODE_LEASE_TIME: -+ case OVN_DHCP_OPT_CODE_T1: -+ case OVN_DHCP_OPT_CODE_T2: -+ break; -+ default: -+ /* Copy the dhcp option to reply_dhcp_opts_ptr. */ -+ ofpbuf_put(reply_dhcp_opts_ptr, in_dhcp_opt, -+ in_dhcp_opt->len + sizeof *in_dhcp_opt); -+ break; -+ } -+ -+ in_dhcp_ptr += sizeof *in_dhcp_opt; -+ if (in_dhcp_ptr > end) { -+ break; -+ } -+ in_dhcp_ptr += in_dhcp_opt->len; -+ if (in_dhcp_ptr > end) { -+ break; -+ } -+ } -+ -+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(20, 40); -+ VLOG_INFO_RL(&rl, "DHCPINFORM from "ETH_ADDR_FMT " "IP_FMT"", -+ ETH_ADDR_ARGS(in_flow->dl_src), -+ IP_ARGS(in_flow->nw_src)); -+ -+ break; -+ } -+ default: { -+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); -+ VLOG_WARN_RL(&rl, "Invalid DHCP message type: %d", *in_dhcp_msg_type); -+ goto exit; -+ } -+ } -+ -+ if (!msg_type) { -+ goto exit; - } - - /* Frame the DHCP reply packet -- * Total DHCP options length will be options stored in the userdata + -- * 16 bytes. Note that the DHCP options stored in userdata are not included -- * in DHCPNAK messages. -+ * Total DHCP options length will be options stored in the -+ * reply_dhcp_opts_ptr + 16 bytes. Note that the DHCP options stored in -+ * reply_dhcp_opts_ptr are not included in DHCPNAK messages. - * - * -------------------------------------------------------------- - *| 4 Bytes (dhcp cookie) | 3 Bytes (option type) | DHCP options | -@@ -1849,7 +1913,7 @@ pinctrl_handle_put_dhcp_opts( - */ - uint16_t new_l4_size = UDP_HEADER_LEN + DHCP_HEADER_LEN + 16; - if (msg_type != DHCP_MSG_NAK) { -- new_l4_size += userdata->size; -+ new_l4_size += reply_dhcp_opts_ptr->size; - } - size_t new_packet_size = pkt_in->l4_ofs + new_l4_size; - -@@ -1874,12 +1938,18 @@ pinctrl_handle_put_dhcp_opts( - struct dhcp_header *dhcp_data = dp_packet_put( - &pkt_out, dp_packet_pull(pkt_in, DHCP_HEADER_LEN), DHCP_HEADER_LEN); - dhcp_data->op = DHCP_OP_REPLY; -- dhcp_data->yiaddr = (msg_type == DHCP_MSG_NAK) ? 0 : *offer_ip; -+ -+ if (*in_dhcp_msg_type != OVN_DHCP_MSG_INFORM) { -+ dhcp_data->yiaddr = (msg_type == DHCP_MSG_NAK) ? 0 : *offer_ip; -+ } else { -+ dhcp_data->yiaddr = 0; -+ } -+ - dp_packet_put(&pkt_out, &magic_cookie, sizeof(ovs_be32)); - - uint16_t out_dhcp_opts_size = 12; - if (msg_type != DHCP_MSG_NAK) { -- out_dhcp_opts_size += userdata->size; -+ out_dhcp_opts_size += reply_dhcp_opts_ptr->size; - } - uint8_t *out_dhcp_opts = dp_packet_put_zeros(&pkt_out, - out_dhcp_opts_size); -@@ -1890,8 +1960,9 @@ pinctrl_handle_put_dhcp_opts( - out_dhcp_opts += 3; - - if (msg_type != DHCP_MSG_NAK) { -- memcpy(out_dhcp_opts, userdata->data, userdata->size); -- out_dhcp_opts += userdata->size; -+ memcpy(out_dhcp_opts, reply_dhcp_opts_ptr->data, -+ reply_dhcp_opts_ptr->size); -+ out_dhcp_opts += reply_dhcp_opts_ptr->size; - } - - /* Padding */ -@@ -1939,6 +2010,10 @@ exit: - if (pkt_out_ptr) { - dp_packet_uninit(pkt_out_ptr); - } -+ -+ if (dhcp_inform_reply_buf) { -+ ofpbuf_delete(dhcp_inform_reply_buf); -+ } - } - - static bool -@@ -2644,8 +2719,8 @@ process_packet_in(struct rconn *swconn, const struct ofp_header *msg) - break; - - case ACTION_OPCODE_PUT_DHCP_OPTS: -- pinctrl_handle_put_dhcp_opts(swconn, &packet, &pin, &userdata, -- &continuation); -+ pinctrl_handle_put_dhcp_opts(swconn, &packet, &pin, &headers, -+ &userdata, &continuation); - break; - - case ACTION_OPCODE_ND_NA: -diff --git a/lib/ovn-l7.h b/lib/ovn-l7.h -index cea97b9ce..9acfbe075 100644 ---- a/lib/ovn-l7.h -+++ b/lib/ovn-l7.h -@@ -37,6 +37,14 @@ struct gen_opts_map { - #define DOMAIN_NAME_MAX_LEN 255 - #define DHCP_BROADCAST_FLAG 0x8000 - -+/* These are not defined in ovs/lib/dhcp.h and hence defined here with -+ * OVN_DHCP_OPT_CODE_. -+ */ -+#define OVN_DHCP_OPT_CODE_NETMASK 1 -+#define OVN_DHCP_OPT_CODE_LEASE_TIME 51 -+#define OVN_DHCP_OPT_CODE_T1 58 -+#define OVN_DHCP_OPT_CODE_T2 59 -+ - #define DHCP_OPTION(NAME, CODE, TYPE) \ - {.name = NAME, .code = CODE, .type = TYPE} - -@@ -171,6 +179,10 @@ struct dhcp_opt6_header { - ovs_be16 size; - }; - -+/* These are not defined in ovs/lib/dhcp.h, hence defining here. */ -+#define OVN_DHCP_MSG_RELEASE 7 -+#define OVN_DHCP_MSG_INFORM 8 -+ - /* Supported DHCPv6 Message Types */ - #define DHCPV6_MSG_TYPE_SOLICIT 1 - #define DHCPV6_MSG_TYPE_ADVT 2 -diff --git a/tests/ovn.at b/tests/ovn.at -index b84cf75fd..6866a58da 100644 ---- a/tests/ovn.at -+++ b/tests/ovn.at -@@ -5330,6 +5330,12 @@ test_dhcp() { - done - if test $offer_ip != 0; then - local srv_mac=$1 srv_ip=$2 dhcp_reply_type=$3 expected_dhcp_opts=$4 -+ local offered_ip=$offer_ip -+ if [[ "$dhcp_type" == "08" ]]; then -+ # DHCP ACK for DHCP INFORM should not have any offer ip. -+ offered_ip=00000000 -+ fi -+ - # total IP length will be the IP length of the request packet - # (which is 272 in our case) + 8 (padding bytes) + (expected_dhcp_opts / 2) - ip_len=`expr 280 + ${#expected_dhcp_opts} / 2` -@@ -5345,7 +5351,7 @@ test_dhcp() { - if test $dhcp_reply_type = 06; then - reply=${reply}00000000 - else -- reply=${reply}${offer_ip} -+ reply=${reply}${offered_ip} - fi - # next server ip address, relay agent ip address, client mac address - reply=${reply}0000000000000000${src_mac} -@@ -5485,7 +5491,7 @@ rm -f 2.expected - ciaddr=`ip_to_hex 0 0 0 0` - offer_ip=0 - request_ip=0 --test_dhcp 2 f00000000002 08 0 $ciaddr $offer_ip $request_ip 0 1 1 -+test_dhcp 2 f00000000002 09 0 $ciaddr $offer_ip $request_ip 0 1 1 - - # NXT_RESUMEs should be 4. - OVS_WAIT_UNTIL([test 4 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`]) -@@ -5660,6 +5666,113 @@ reset_pcap_file hv1-vif2 hv1/vif2 - rm -f 1.expected - rm -f 2.expected - -+# Send DHCPRELEASE. -+offer_ip=0 -+server_ip=`ip_to_hex 10 0 0 1` -+ciaddr=`ip_to_hex 10 0 0 6` -+request_ip=0 -+expected_dhcp_opts=0 -+test_dhcp 2 f00000000002 07 0 $ciaddr $offer_ip $request_ip 0 ff1000000001 -+ -+# NXT_RESUMEs should be 10. -+OVS_WAIT_UNTIL([test 10 = $(cat ofctl_monitor*.log | grep -c NXT_RESUME)]) -+ -+# There is no reply for this. Check for the INFO log in ovn-controller.log -+AT_CHECK([test 1 = $(cat hv1/ovn-controller.log | \ -+grep "DHCPRELEASE f0:00:00:00:00:02 10.0.0.6" -c)]) -+ -+$PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > 2.packets -+AT_CHECK([cat 2.packets], [0], []) -+ -+reset_pcap_file hv1-vif1 hv1/vif1 -+reset_pcap_file hv1-vif2 hv1/vif2 -+rm -f 1.expected -+rm -f 2.expected -+ -+# Send DHCPINFORM -+offer_ip=`ip_to_hex 10 0 0 6` -+server_ip=`ip_to_hex 10 0 0 1` -+ciaddr=$offer_ip -+request_ip=0 -+src_ip=$offer_ip -+dst_ip=$server_ip -+# In the expected_dhcp_opts we should not see 330400000e10 which is -+# dhcp lease time option and 0104ffffff00 which is subnet mask option. -+expected_dhcp_opts=03040a00000136040a000001 -+test_dhcp 2 f00000000002 08 0 $ciaddr $offer_ip $request_ip 1 $src_ip $dst_ip ff1000000001 $server_ip 05 $expected_dhcp_opts -+ -+# NXT_RESUMEs should be 11. -+OVS_WAIT_UNTIL([test 11 = $(cat ofctl_monitor*.log | grep -c NXT_RESUME)]) -+ -+$PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > 2.packets -+cat 2.expected | cut -c -48 > expout -+AT_CHECK([cat 2.packets | cut -c -48], [0], [expout]) -+# Skipping the IPv4 checksum. -+cat 2.expected | cut -c 53- > expout -+AT_CHECK([cat 2.packets | cut -c 53-], [0], [expout]) -+ -+# Now add the dhcp option T1 to the dhcp options. -+ovn-nbctl set dhcp_options ${d1} options:T1=4000 -+ -+reset_pcap_file hv1-vif1 hv1/vif1 -+reset_pcap_file hv1-vif2 hv1/vif2 -+rm -f 1.expected -+rm -f 2.expected -+ -+# Send DHCPREQUEST to make sure that T1 is in the reply dhcp options. -+offer_ip=`ip_to_hex 10 0 0 6` -+server_ip=`ip_to_hex 10 0 0 1` -+ciaddr=$offer_ip -+request_ip=0 -+src_ip=$offer_ip -+dst_ip=$server_ip -+# In the expected_dhcp_opts we should not see 330400000e10 which is -+# dhcp lease time option. -+expected_dhcp_opts=3a0400000fa0330400000e100104ffffff0003040a00000136040a000001 -+test_dhcp 2 f00000000002 03 0 $ciaddr $offer_ip $request_ip 1 $src_ip $dst_ip ff1000000001 $server_ip 05 $expected_dhcp_opts -+ -+# NXT_RESUMEs should be 12. -+OVS_WAIT_UNTIL([test 12 = $(cat ofctl_monitor*.log | grep -c NXT_RESUME)]) -+ -+$PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > 2.packets -+cat 2.expected | cut -c -48 > expout -+AT_CHECK([cat 2.packets | cut -c -48], [0], [expout]) -+# Skipping the IPv4 checksum. -+cat 2.expected | cut -c 53- > expout -+AT_CHECK([cat 2.packets | cut -c 53-], [0], [expout]) -+ -+reset_pcap_file hv1-vif1 hv1/vif1 -+reset_pcap_file hv1-vif2 hv1/vif2 -+rm -f 1.expected -+rm -f 2.expected -+ -+# Now send DHCPINFORM again. -+offer_ip=`ip_to_hex 10 0 0 6` -+server_ip=`ip_to_hex 10 0 0 1` -+ciaddr=00000000 -+request_ip=0 -+src_ip=$offer_ip -+dst_ip=$server_ip -+# In the expected_dhcp_opts we should not see 330400000e10 which is -+# dhcp lease time option and 0104ffffff00 which is subnet mask option. -+expected_dhcp_opts=03040a00000136040a000001 -+test_dhcp 2 f00000000002 08 0 $ciaddr $offer_ip $request_ip 1 $src_ip $dst_ip ff1000000001 $server_ip 05 $expected_dhcp_opts -+ -+# NXT_RESUMEs should be 13. -+OVS_WAIT_UNTIL([test 13 = $(cat ofctl_monitor*.log | grep -c NXT_RESUME)]) -+ -+$PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > 2.packets -+cat 2.expected | cut -c -48 > expout -+AT_CHECK([cat 2.packets | cut -c -48], [0], [expout]) -+# Skipping the IPv4 checksum. -+cat 2.expected | cut -c 53- > expout -+AT_CHECK([cat 2.packets | cut -c 53-], [0], [expout]) -+ -+reset_pcap_file hv1-vif1 hv1/vif1 -+reset_pcap_file hv1-vif2 hv1/vif2 -+rm -f 1.expected -+rm -f 2.expected -+ - # Set tftp server option (IPv4 address) for ls1 - echo "------ Set tftp server (IPv4 address) --------" - ovn-nbctl dhcp-options-set-options $d1 server_id=10.0.0.1 \ -@@ -5676,8 +5789,8 @@ request_ip=$offer_ip - expected_dhcp_opts=330400000e100104ffffff0003040a00000136040a00000142040a0a0a0a - test_dhcp 2 f00000000002 03 0 $ciaddr $offer_ip $request_ip 0 ff1000000001 $server_ip 05 $expected_dhcp_opts - --# NXT_RESUMEs should be 10. --OVS_WAIT_UNTIL([test 10 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`]) -+# NXT_RESUMEs should be 14. -+OVS_WAIT_UNTIL([test 14 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`]) - - $PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > 2.packets - cat 2.expected | cut -c -48 > expout -@@ -5707,8 +5820,8 @@ request_ip=$offer_ip - expected_dhcp_opts=330400000e100104ffffff0003040a00000136040a0000014210746573745f746674705f736572766572 - test_dhcp 2 f00000000002 03 0 $ciaddr $offer_ip $request_ip 0 ff1000000001 $server_ip 05 $expected_dhcp_opts - --# NXT_RESUMEs should be 11. --OVS_WAIT_UNTIL([test 11 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`]) -+# NXT_RESUMEs should be 15. -+OVS_WAIT_UNTIL([test 15 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`]) - - $PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > 2.packets - cat 2.expected | cut -c -48 > expout --- -2.26.2 - diff --git a/SOURCES/0006-Introduce-icmp6_error-action.patch b/SOURCES/0006-Introduce-icmp6_error-action.patch deleted file mode 100644 index e03f1d6..0000000 --- a/SOURCES/0006-Introduce-icmp6_error-action.patch +++ /dev/null @@ -1,178 +0,0 @@ -From 13e1aade24b29ce4a6425a355f482b149848928a Mon Sep 17 00:00:00 2001 -From: Lorenzo Bianconi -Date: Tue, 7 Jul 2020 17:18:26 +0200 -Subject: [PATCH 06/22] Introduce icmp6_error action - -Introduce icmp6_error action in order to generate an ICMPv6 packet in -response to an error in original IPv6 packet - -Signed-off-by: Lorenzo Bianconi -Signed-off-by: Numan Siddique ---- - controller/pinctrl.c | 1 + - include/ovn/actions.h | 9 ++++++++- - lib/actions.c | 22 ++++++++++++++++++++++ - ovn-sb.xml | 8 ++++++++ - tests/ovn.at | 10 ++++++++++ - utilities/ovn-trace.c | 5 +++++ - 6 files changed, 54 insertions(+), 1 deletion(-) - -diff --git a/controller/pinctrl.c b/controller/pinctrl.c -index 9231efbe3..f8be22db0 100644 ---- a/controller/pinctrl.c -+++ b/controller/pinctrl.c -@@ -2772,6 +2772,7 @@ process_packet_in(struct rconn *swconn, const struct ofp_header *msg) - break; - - case ACTION_OPCODE_ICMP4_ERROR: -+ case ACTION_OPCODE_ICMP6_ERROR: - pinctrl_handle_icmp(swconn, &headers, &packet, &pin.flow_metadata, - &userdata, false); - break; -diff --git a/include/ovn/actions.h b/include/ovn/actions.h -index 2b5a63a74..5abf61a40 100644 ---- a/include/ovn/actions.h -+++ b/include/ovn/actions.h -@@ -94,7 +94,8 @@ struct ovn_extend_table; - OVNACT(BIND_VPORT, ovnact_bind_vport) \ - OVNACT(HANDLE_SVC_CHECK, ovnact_handle_svc_check) \ - OVNACT(FWD_GROUP, ovnact_fwd_group) \ -- OVNACT(DHCP6_REPLY, ovnact_null) -+ OVNACT(DHCP6_REPLY, ovnact_null) \ -+ OVNACT(ICMP6_ERROR, ovnact_nest) - - /* enum ovnact_type, with a member OVNACT_ for each action. */ - enum OVS_PACKED_ENUM ovnact_type { -@@ -601,6 +602,12 @@ enum action_opcode { - * The actions, in OpenFlow 1.3 format, follow the action_header. - */ - ACTION_OPCODE_DHCP6_SERVER, -+ -+ /* "icmp6_error { ...actions... }". -+ * -+ * The actions, in OpenFlow 1.3 format, follow the action_header. -+ */ -+ ACTION_OPCODE_ICMP6_ERROR, - }; - - /* Header. */ -diff --git a/lib/actions.c b/lib/actions.c -index e4de97c23..515dbd2db 100644 ---- a/lib/actions.c -+++ b/lib/actions.c -@@ -1408,6 +1408,12 @@ parse_ICMP6(struct action_context *ctx) - parse_nested_action(ctx, OVNACT_ICMP6, "ip6"); - } - -+static void -+parse_ICMP6_ERROR(struct action_context *ctx) -+{ -+ parse_nested_action(ctx, OVNACT_ICMP6_ERROR, "ip6"); -+} -+ - static void - parse_TCP_RESET(struct action_context *ctx) - { -@@ -1471,6 +1477,12 @@ format_ICMP6(const struct ovnact_nest *nest, struct ds *s) - format_nested_action(nest, "icmp6", s); - } - -+static void -+format_ICMP6_ERROR(const struct ovnact_nest *nest, struct ds *s) -+{ -+ format_nested_action(nest, "icmp6_error", s); -+} -+ - static void - format_IGMP(const struct ovnact_null *a OVS_UNUSED, struct ds *s) - { -@@ -1582,6 +1594,14 @@ encode_ICMP6(const struct ovnact_nest *on, - encode_nested_actions(on, ep, ACTION_OPCODE_ICMP, ofpacts); - } - -+static void -+encode_ICMP6_ERROR(const struct ovnact_nest *on, -+ const struct ovnact_encode_params *ep, -+ struct ofpbuf *ofpacts) -+{ -+ encode_nested_actions(on, ep, ACTION_OPCODE_ICMP6_ERROR, ofpacts); -+} -+ - static void - encode_IGMP(const struct ovnact_null *a OVS_UNUSED, - const struct ovnact_encode_params *ep OVS_UNUSED, -@@ -3558,6 +3578,8 @@ parse_action(struct action_context *ctx) - parse_ICMP4_ERROR(ctx); - } else if (lexer_match_id(ctx->lexer, "icmp6")) { - parse_ICMP6(ctx); -+ } else if (lexer_match_id(ctx->lexer, "icmp6_error")) { -+ parse_ICMP6_ERROR(ctx); - } else if (lexer_match_id(ctx->lexer, "igmp")) { - ovnact_put_IGMP(ctx->ovnacts); - } else if (lexer_match_id(ctx->lexer, "tcp_reset")) { -diff --git a/ovn-sb.xml b/ovn-sb.xml -index 709cb4c48..6d6775a45 100644 ---- a/ovn-sb.xml -+++ b/ovn-sb.xml -@@ -2090,6 +2090,9 @@ - - -
    icmp6 { action; ... };
    -+
    -+ icmp6_error { action; ... }; -+
    -
    -

    - Temporarily replaces the IPv6 packet being processed by an ICMPv6 -@@ -2112,6 +2115,11 @@ -

  • icmp6.code = 1 (administratively prohibited)
  • - - -+

    -+ icmp6_error action is expected to be used to -+ generate an ICMPv6 packet in response to an error in original -+ IPv6 packet. -+

    -

    Prerequisite: ip6

    -
    - -diff --git a/tests/ovn.at b/tests/ovn.at -index 6866a58da..cfcfa0915 100644 ---- a/tests/ovn.at -+++ b/tests/ovn.at -@@ -1547,6 +1547,16 @@ icmp6 { }; - encodes as controller(userdata=00.00.00.0a.00.00.00.00) - has prereqs ip6 - -+# icmp6_error -+icmp6_error { eth.dst = ff:ff:ff:ff:ff:ff; output; }; output; -+ encodes as controller(userdata=00.00.00.14.00.00.00.00.00.19.00.10.80.00.06.06.ff.ff.ff.ff.ff.ff.00.00.ff.ff.00.10.00.00.23.20.00.0e.ff.f8.40.00.00.00),resubmit(,64) -+ has prereqs ip6 -+ -+icmp6_error { }; -+ formats as icmp6_error { drop; }; -+ encodes as controller(userdata=00.00.00.14.00.00.00.00) -+ has prereqs ip6 -+ - # tcp_reset - tcp_reset { eth.dst = ff:ff:ff:ff:ff:ff; output; }; output; - encodes as controller(userdata=00.00.00.0b.00.00.00.00.00.19.00.10.80.00.06.06.ff.ff.ff.ff.ff.ff.00.00.ff.ff.00.10.00.00.23.20.00.0e.ff.f8.40.00.00.00),resubmit(,64) -diff --git a/utilities/ovn-trace.c b/utilities/ovn-trace.c -index 647cf3075..c34517aaa 100644 ---- a/utilities/ovn-trace.c -+++ b/utilities/ovn-trace.c -@@ -2319,6 +2319,11 @@ trace_actions(const struct ovnact *ovnacts, size_t ovnacts_len, - super); - break; - -+ case OVNACT_ICMP6_ERROR: -+ execute_icmp6(ovnact_get_ICMP6_ERROR(a), dp, uflow, table_id, -+ pipeline, super); -+ break; -+ - case OVNACT_IGMP: - /* Nothing to do for tracing. */ - break; --- -2.26.2 - diff --git a/SOURCES/0007-Introduce-icmp6.frag_mtu-action.patch b/SOURCES/0007-Introduce-icmp6.frag_mtu-action.patch deleted file mode 100644 index 7f39cba..0000000 --- a/SOURCES/0007-Introduce-icmp6.frag_mtu-action.patch +++ /dev/null @@ -1,287 +0,0 @@ -From c659c6ae95e5ffa94e795a0f104c27c3f5523eae Mon Sep 17 00:00:00 2001 -From: Lorenzo Bianconi -Date: Tue, 7 Jul 2020 17:18:27 +0200 -Subject: [PATCH 07/22] Introduce icmp6.frag_mtu action - -Similar to what have been already done for IPv4, introduce -icmp6.frag_mtu action in order to set correct mtu in ICMPv6 "packet too -big" error message - -Signed-off-by: Lorenzo Bianconi -Signed-off-by: Numan Siddique ---- - controller/pinctrl.c | 77 ++++++++++++++++++++++-------------- - include/ovn/actions.h | 4 ++ - include/ovn/logical-fields.h | 7 ++++ - lib/actions.c | 13 +++++- - lib/logical-fields.c | 5 +++ - ovn-sb.xml | 7 ++-- - tests/ovn.at | 8 ++++ - utilities/ovn-trace.c | 7 +++- - 8 files changed, 93 insertions(+), 35 deletions(-) - -diff --git a/controller/pinctrl.c b/controller/pinctrl.c -index f8be22db0..f72ab70e1 100644 ---- a/controller/pinctrl.c -+++ b/controller/pinctrl.c -@@ -228,12 +228,12 @@ static void pinctrl_handle_nd_ns(struct rconn *swconn, - struct dp_packet *pkt_in, - const struct match *md, - struct ofpbuf *userdata); --static void pinctrl_handle_put_icmp4_frag_mtu(struct rconn *swconn, -- const struct flow *in_flow, -- struct dp_packet *pkt_in, -- struct ofputil_packet_in *pin, -- struct ofpbuf *userdata, -- struct ofpbuf *continuation); -+static void pinctrl_handle_put_icmp_frag_mtu(struct rconn *swconn, -+ const struct flow *in_flow, -+ struct dp_packet *pkt_in, -+ struct ofputil_packet_in *pin, -+ struct ofpbuf *userdata, -+ struct ofpbuf *continuation); - static void - pinctrl_handle_event(struct ofpbuf *userdata) - OVS_REQUIRES(pinctrl_mutex); -@@ -2783,8 +2783,9 @@ process_packet_in(struct rconn *swconn, const struct ofp_header *msg) - break; - - case ACTION_OPCODE_PUT_ICMP4_FRAG_MTU: -- pinctrl_handle_put_icmp4_frag_mtu(swconn, &headers, &packet, -- &pin, &userdata, &continuation); -+ case ACTION_OPCODE_PUT_ICMP6_FRAG_MTU: -+ pinctrl_handle_put_icmp_frag_mtu(swconn, &headers, &packet, &pin, -+ &userdata, &continuation); - break; - - case ACTION_OPCODE_EVENT: -@@ -5475,26 +5476,22 @@ exit: - - /* Called with in the pinctrl_handler thread context. */ - static void --pinctrl_handle_put_icmp4_frag_mtu(struct rconn *swconn, -- const struct flow *in_flow, -- struct dp_packet *pkt_in, -- struct ofputil_packet_in *pin, -- struct ofpbuf *userdata, -- struct ofpbuf *continuation) -+pinctrl_handle_put_icmp_frag_mtu(struct rconn *swconn, -+ const struct flow *in_flow, -+ struct dp_packet *pkt_in, -+ struct ofputil_packet_in *pin, -+ struct ofpbuf *userdata, -+ struct ofpbuf *continuation) - { - enum ofp_version version = rconn_get_version(swconn); - enum ofputil_protocol proto = ofputil_protocol_from_ofp_version(version); - struct dp_packet *pkt_out = NULL; - -- /* This action only works for ICMPv4 packets. */ -- if (!is_icmpv4(in_flow, NULL)) { -+ /* This action only works for ICMPv4/v6 packets. */ -+ if (!is_icmpv4(in_flow, NULL) && !is_icmpv6(in_flow, NULL)) { - static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); -- VLOG_WARN_RL(&rl, "put_icmp4_frag_mtu action on non-ICMPv4 packet"); -- goto exit; -- } -- -- ovs_be16 *mtu = ofpbuf_try_pull(userdata, sizeof *mtu); -- if (!mtu) { -+ VLOG_WARN_RL(&rl, -+ "put_icmp(4/6)_frag_mtu action on non-ICMPv4/v6 packet"); - goto exit; - } - -@@ -5504,13 +5501,35 @@ pinctrl_handle_put_icmp4_frag_mtu(struct rconn *swconn, - pkt_out->l3_ofs = pkt_in->l3_ofs; - pkt_out->l4_ofs = pkt_in->l4_ofs; - -- struct ip_header *nh = dp_packet_l3(pkt_out); -- struct icmp_header *ih = dp_packet_l4(pkt_out); -- ovs_be16 old_frag_mtu = ih->icmp_fields.frag.mtu; -- ih->icmp_fields.frag.mtu = *mtu; -- ih->icmp_csum = recalc_csum16(ih->icmp_csum, old_frag_mtu, *mtu); -- nh->ip_csum = 0; -- nh->ip_csum = csum(nh, sizeof *nh); -+ if (is_icmpv4(in_flow, NULL)) { -+ ovs_be16 *mtu = ofpbuf_try_pull(userdata, sizeof *mtu); -+ if (!mtu) { -+ goto exit; -+ } -+ -+ struct ip_header *nh = dp_packet_l3(pkt_out); -+ struct icmp_header *ih = dp_packet_l4(pkt_out); -+ ovs_be16 old_frag_mtu = ih->icmp_fields.frag.mtu; -+ ih->icmp_fields.frag.mtu = *mtu; -+ ih->icmp_csum = recalc_csum16(ih->icmp_csum, old_frag_mtu, *mtu); -+ nh->ip_csum = 0; -+ nh->ip_csum = csum(nh, sizeof *nh); -+ } else { -+ ovs_be32 *mtu = ofpbuf_try_pull(userdata, sizeof *mtu); -+ if (!mtu) { -+ goto exit; -+ } -+ -+ struct icmp6_data_header *ih = dp_packet_l4(pkt_out); -+ put_16aligned_be32(ih->icmp6_data.be32, *mtu); -+ -+ /* compute checksum and set correct mtu */ -+ ih->icmp6_base.icmp6_cksum = 0; -+ uint32_t csum = packet_csum_pseudoheader6(dp_packet_l3(pkt_out)); -+ uint32_t size = (uint8_t *)dp_packet_tail(pkt_out) - (uint8_t *)ih; -+ ih->icmp6_base.icmp6_cksum = csum_finish( -+ csum_continue(csum, ih, size)); -+ } - - pin->packet = dp_packet_data(pkt_out); - pin->packet_len = dp_packet_size(pkt_out); -diff --git a/include/ovn/actions.h b/include/ovn/actions.h -index 5abf61a40..34ba0d880 100644 ---- a/include/ovn/actions.h -+++ b/include/ovn/actions.h -@@ -608,6 +608,10 @@ enum action_opcode { - * The actions, in OpenFlow 1.3 format, follow the action_header. - */ - ACTION_OPCODE_ICMP6_ERROR, -+ -+ /* MTU value (to put in the icmp6 header field - frag_mtu) follow the -+ * action header. */ -+ ACTION_OPCODE_PUT_ICMP6_FRAG_MTU, - }; - - /* Header. */ -diff --git a/include/ovn/logical-fields.h b/include/ovn/logical-fields.h -index c7bd2dba9..61d17d14f 100644 ---- a/include/ovn/logical-fields.h -+++ b/include/ovn/logical-fields.h -@@ -108,6 +108,13 @@ enum ovn_field_id { - * packet as per the RFC 1191. - */ - OVN_ICMP4_FRAG_MTU, -+ /* -+ * Name: "icmp6.frag_mtu" - -+ * Type: be32 -+ * Description: Sets the first 32 bits of the ICMPv6 body to the MTU of -+ * next-hop link (RFC 4443) -+ */ -+ OVN_ICMP6_FRAG_MTU, - - OVN_FIELD_N_IDS - }; -diff --git a/lib/actions.c b/lib/actions.c -index 515dbd2db..1deeef799 100644 ---- a/lib/actions.c -+++ b/lib/actions.c -@@ -3159,6 +3159,7 @@ format_OVNFIELD_LOAD(const struct ovnact_load *load , struct ds *s) - const struct ovn_field *f = ovn_field_from_name(load->dst.symbol->name); - switch (f->id) { - case OVN_ICMP4_FRAG_MTU: -+ case OVN_ICMP6_FRAG_MTU: - ds_put_format(s, "%s = %u;", f->name, - ntohs(load->imm.value.be16_int)); - break; -@@ -3178,12 +3179,20 @@ encode_OVNFIELD_LOAD(const struct ovnact_load *load, - switch (f->id) { - case OVN_ICMP4_FRAG_MTU: { - size_t oc_offset = encode_start_controller_op( -- ACTION_OPCODE_PUT_ICMP4_FRAG_MTU, true, NX_CTLR_NO_METER, -- ofpacts); -+ ACTION_OPCODE_PUT_ICMP4_FRAG_MTU, true, -+ NX_CTLR_NO_METER, ofpacts); - ofpbuf_put(ofpacts, &load->imm.value.be16_int, sizeof(ovs_be16)); - encode_finish_controller_op(oc_offset, ofpacts); - break; - } -+ case OVN_ICMP6_FRAG_MTU: { -+ size_t oc_offset = encode_start_controller_op( -+ ACTION_OPCODE_PUT_ICMP6_FRAG_MTU, true, -+ NX_CTLR_NO_METER, ofpacts); -+ ofpbuf_put(ofpacts, &load->imm.value.be32_int, sizeof(ovs_be32)); -+ encode_finish_controller_op(oc_offset, ofpacts); -+ break; -+ } - case OVN_FIELD_N_IDS: - default: - OVS_NOT_REACHED(); -diff --git a/lib/logical-fields.c b/lib/logical-fields.c -index 8ad56aa53..8639523ea 100644 ---- a/lib/logical-fields.c -+++ b/lib/logical-fields.c -@@ -29,6 +29,10 @@ const struct ovn_field ovn_fields[OVN_FIELD_N_IDS] = { - OVN_ICMP4_FRAG_MTU, - "icmp4.frag_mtu", - 2, 16, -+ }, { -+ OVN_ICMP6_FRAG_MTU, -+ "icmp6.frag_mtu", -+ 4, 32, - }, - }; - -@@ -257,6 +261,7 @@ ovn_init_symtab(struct shash *symtab) - expr_symtab_add_field(symtab, "pkt.mark", MFF_PKT_MARK, NULL, false); - - expr_symtab_add_ovn_field(symtab, "icmp4.frag_mtu", OVN_ICMP4_FRAG_MTU); -+ expr_symtab_add_ovn_field(symtab, "icmp6.frag_mtu", OVN_ICMP6_FRAG_MTU); - } - - const char * -diff --git a/ovn-sb.xml b/ovn-sb.xml -index 6d6775a45..fc39b2d03 100644 ---- a/ovn-sb.xml -+++ b/ovn-sb.xml -@@ -1170,10 +1170,11 @@ -
      -
    • - icmp4.frag_mtu -+ icmp6.frag_mtu -

      -- This field sets the low-order 16 bits of the ICMP4 header field -- that is labelled "unused" in the ICMP specification as defined -- in the RFC 1191 with the value specified in -+ This field sets the low-order 16 bits of the ICMP{4,6} header -+ field that is labelled "unused" in the ICMP specification as -+ defined in the RFC 1191 with the value specified in - constant. -

      - -diff --git a/tests/ovn.at b/tests/ovn.at -index cfcfa0915..80cd62c49 100644 ---- a/tests/ovn.at -+++ b/tests/ovn.at -@@ -1557,6 +1557,14 @@ icmp6_error { }; - encodes as controller(userdata=00.00.00.14.00.00.00.00) - has prereqs ip6 - -+# icmp6_error with icmp6.frag_mtu -+icmp6_error { eth.dst = ff:ff:ff:ff:ff:ff; icmp6.frag_mtu = 1500; output; }; output; -+ encodes as controller(userdata=00.00.00.14.00.00.00.00.00.19.00.10.80.00.06.06.ff.ff.ff.ff.ff.ff.00.00.ff.ff.00.28.00.00.23.20.00.25.00.00.00.00.00.00.00.03.00.10.00.00.00.15.00.00.00.00.00.00.05.dc.00.04.00.04.00.00.00.00.ff.ff.00.10.00.00.23.20.00.0e.ff.f8.40.00.00.00),resubmit(,64) -+ has prereqs ip6 -+ -+icmp6.frag_mtu = 1500; -+ encodes as controller(userdata=00.00.00.15.00.00.00.00.00.00.05.dc,pause) -+ - # tcp_reset - tcp_reset { eth.dst = ff:ff:ff:ff:ff:ff; output; }; output; - encodes as controller(userdata=00.00.00.0b.00.00.00.00.00.19.00.10.80.00.06.06.ff.ff.ff.ff.ff.ff.00.00.ff.ff.00.10.00.00.23.20.00.0e.ff.f8.40.00.00.00),resubmit(,64) -diff --git a/utilities/ovn-trace.c b/utilities/ovn-trace.c -index c34517aaa..50a32b714 100644 ---- a/utilities/ovn-trace.c -+++ b/utilities/ovn-trace.c -@@ -2119,7 +2119,12 @@ execute_ovnfield_load(const struct ovnact_load *load, - ntohs(load->imm.value.be16_int)); - break; - } -- -+ case OVN_ICMP6_FRAG_MTU: { -+ ovntrace_node_append(super, OVNTRACE_NODE_MODIFY, -+ "icmp6.frag_mtu = %u", -+ ntohs(load->imm.value.be16_int)); -+ break; -+ } - case OVN_FIELD_N_IDS: - default: - OVS_NOT_REACHED(); --- -2.26.2 - diff --git a/SOURCES/0008-northd-introduce-icmp6_error-logical-flows-in-router.patch b/SOURCES/0008-northd-introduce-icmp6_error-logical-flows-in-router.patch deleted file mode 100644 index 246780e..0000000 --- a/SOURCES/0008-northd-introduce-icmp6_error-logical-flows-in-router.patch +++ /dev/null @@ -1,263 +0,0 @@ -From a1a0c7061850d78edb74a7977d0241121575be0e Mon Sep 17 00:00:00 2001 -From: Lorenzo Bianconi -Date: Tue, 7 Jul 2020 17:18:28 +0200 -Subject: [PATCH 08/22] northd: introduce icmp6_error logical flows in router - pipeline - -Introduce icmp6_error logical flows in router pipeline if gateway_mtu -has been added to logical router port option column in order to perform -IPv6 PMTU discovery - -Signed-off-by: Lorenzo Bianconi -Signed-off-by: Numan Siddique ---- - northd/ovn-northd.8.xml | 24 +++++++++--- - northd/ovn-northd.c | 84 ++++++++++++++++++++++++++++------------- - tests/ovn.at | 48 +++++++++++++++++++++-- - 3 files changed, 120 insertions(+), 36 deletions(-) - -diff --git a/northd/ovn-northd.8.xml b/northd/ovn-northd.8.xml -index 67481f458..623768988 100644 ---- a/northd/ovn-northd.8.xml -+++ b/northd/ovn-northd.8.xml -@@ -2922,11 +2922,11 @@ REGBIT_PKT_LARGER = check_pkt_larger(L); next; - For distributed logical routers with distributed gateway port configured - with options:gateway_mtu to a valid integer value, this - table adds the following priority-50 logical flow for each -- logical router port with the match ip4 && -- inport == LRP && outport == GW_PORT -- && REGBIT_PKT_LARGER, where LRP is the logical -- router port and GW_PORT is the distributed gateway router port -- and applies the following action -+ logical router port with the match inport == LRP -+ && outport == GW_PORT && -+ REGBIT_PKT_LARGER, where LRP is the logical -+ router port and GW_PORT is the distributed gateway router -+ port and applies the following action for ipv4 and ipv6 respectively: -

      - -
      -@@ -2941,6 +2941,18 @@ icmp4 {
      -     REGBIT_EGRESS_LOOPBACK = 1;
      -     next(pipeline=ingress, table=0);
      - };
      -+
      -+icmp6 {
      -+    icmp6.type = 2;
      -+    icmp6.code = 0;
      -+    icmp6.frag_mtu = M;
      -+    eth.dst = E;
      -+    ip6.dst = ip6.src;
      -+    ip6.src = I;
      -+    ip.ttl = 255;
      -+    REGBIT_EGRESS_LOOPBACK = 1;
      -+    next(pipeline=ingress, table=0);
      -+};
      -     
      - -
        -@@ -2956,7 +2968,7 @@ icmp4 { - - -
      • -- I is the IPv4 address of the logical router port. -+ I is the IPv4/IPv6 address of the logical router port. -
      • -
      - -diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c -index 2b1257114..6375aee8d 100644 ---- a/northd/ovn-northd.c -+++ b/northd/ovn-northd.c -@@ -10495,8 +10495,7 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, - } - - ds_clear(&match); -- ds_put_format(&match, "outport == %s && ip4", -- od->l3dgw_port->json_key); -+ ds_put_format(&match, "outport == %s", od->l3dgw_port->json_key); - - ds_clear(&actions); - ds_put_format(&actions, -@@ -10509,34 +10508,65 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, - for (size_t i = 0; i < od->nbr->n_ports; i++) { - struct ovn_port *rp = ovn_port_find(ports, - od->nbr->ports[i]->name); -- if (!rp || rp == od->l3dgw_port || -- !rp->lrp_networks.ipv4_addrs) { -+ if (!rp || rp == od->l3dgw_port) { - continue; - } -- ds_clear(&match); -- ds_put_format(&match, "inport == %s && outport == %s && ip4 " -- "&& "REGBIT_PKT_LARGER, -- rp->json_key, od->l3dgw_port->json_key); - -- ds_clear(&actions); -- /* Set icmp4.frag_mtu to gw_mtu */ -- ds_put_format(&actions, -- "icmp4_error {" -- REGBIT_EGRESS_LOOPBACK" = 1; " -- "eth.dst = %s; " -- "ip4.dst = ip4.src; " -- "ip4.src = %s; " -- "ip.ttl = 255; " -- "icmp4.type = 3; /* Destination Unreachable. */ " -- "icmp4.code = 4; /* Frag Needed and DF was Set. */ " -- "icmp4.frag_mtu = %d; " -- "next(pipeline=ingress, table=0); };", -- rp->lrp_networks.ea_s, -- rp->lrp_networks.ipv4_addrs[0].addr_s, -- gw_mtu); -- ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_LARGER_PKTS, -- 50, ds_cstr(&match), ds_cstr(&actions), -- &rp->nbrp->header_); -+ if (rp->lrp_networks.ipv4_addrs) { -+ ds_clear(&match); -+ ds_put_format(&match, "inport == %s && outport == %s" -+ " && ip4 && "REGBIT_PKT_LARGER, -+ rp->json_key, od->l3dgw_port->json_key); -+ -+ ds_clear(&actions); -+ /* Set icmp4.frag_mtu to gw_mtu */ -+ ds_put_format(&actions, -+ "icmp4_error {" -+ REGBIT_EGRESS_LOOPBACK" = 1; " -+ "eth.dst = %s; " -+ "ip4.dst = ip4.src; " -+ "ip4.src = %s; " -+ "ip.ttl = 255; " -+ "icmp4.type = 3; /* Destination Unreachable. */ " -+ "icmp4.code = 4; /* Frag Needed and DF was Set. */ " -+ "icmp4.frag_mtu = %d; " -+ "next(pipeline=ingress, table=0); };", -+ rp->lrp_networks.ea_s, -+ rp->lrp_networks.ipv4_addrs[0].addr_s, -+ gw_mtu); -+ ovn_lflow_add_with_hint(lflows, od, -+ S_ROUTER_IN_LARGER_PKTS, 50, -+ ds_cstr(&match), ds_cstr(&actions), -+ &rp->nbrp->header_); -+ } -+ -+ if (rp->lrp_networks.ipv6_addrs) { -+ ds_clear(&match); -+ ds_put_format(&match, "inport == %s && outport == %s" -+ " && ip6 && "REGBIT_PKT_LARGER, -+ rp->json_key, od->l3dgw_port->json_key); -+ -+ ds_clear(&actions); -+ /* Set icmp6.frag_mtu to gw_mtu */ -+ ds_put_format(&actions, -+ "icmp6_error {" -+ REGBIT_EGRESS_LOOPBACK" = 1; " -+ "eth.dst = %s; " -+ "ip6.dst = ip6.src; " -+ "ip6.src = %s; " -+ "ip.ttl = 255; " -+ "icmp6.type = 2; /* Packet Too Big. */ " -+ "icmp6.code = 0; " -+ "icmp6.frag_mtu = %d; " -+ "next(pipeline=ingress, table=0); };", -+ rp->lrp_networks.ea_s, -+ rp->lrp_networks.ipv6_addrs[0].addr_s, -+ gw_mtu); -+ ovn_lflow_add_with_hint(lflows, od, -+ S_ROUTER_IN_LARGER_PKTS, 50, -+ ds_cstr(&match), ds_cstr(&actions), -+ &rp->nbrp->header_); -+ } - } - } - } -diff --git a/tests/ovn.at b/tests/ovn.at -index 80cd62c49..905112a8d 100644 ---- a/tests/ovn.at -+++ b/tests/ovn.at -@@ -15115,17 +15115,17 @@ ovn_start - - ovn-nbctl ls-add sw0 - ovn-nbctl lsp-add sw0 sw0-port1 --ovn-nbctl lsp-set-addresses sw0-port1 "50:54:00:00:00:01 10.0.0.3" -+ovn-nbctl lsp-set-addresses sw0-port1 "50:54:00:00:00:01 10.0.0.3 1000::3" - - ovn-nbctl lr-add lr0 --ovn-nbctl lrp-add lr0 lr0-sw0 00:00:00:00:ff:01 10.0.0.1/24 -+ovn-nbctl lrp-add lr0 lr0-sw0 00:00:00:00:ff:01 10.0.0.1/24 1000::1/64 - ovn-nbctl lsp-add sw0 sw0-lr0 - ovn-nbctl lsp-set-type sw0-lr0 router - ovn-nbctl lsp-set-addresses sw0-lr0 router - ovn-nbctl lsp-set-options sw0-lr0 router-port=lr0-sw0 - - ovn-nbctl ls-add public --ovn-nbctl lrp-add lr0 lr0-public 00:00:20:20:12:13 172.168.0.100/24 -+ovn-nbctl lrp-add lr0 lr0-public 00:00:20:20:12:13 172.168.0.100/24 2000::1/64 - ovn-nbctl lsp-add public public-lr0 - ovn-nbctl lsp-set-type public-lr0 router - ovn-nbctl lsp-set-addresses public-lr0 router -@@ -15139,6 +15139,7 @@ ovn-nbctl lsp-set-options ln-public network_name=phys - - ovn-nbctl lrp-set-gateway-chassis lr0-public hv1 20 - ovn-nbctl lr-nat-add lr0 snat 172.168.0.100 10.0.0.0/24 -+ovn-nbctl lr-nat-add lr0 snat 2000::1 1000::/64 - - net_add n1 - -@@ -15249,6 +15250,41 @@ test_ip_packet_larger() { - fi - } - -+test_ip6_packet_larger() { -+ local icmp_pmtu_reply_expected=$1 -+ -+ local eth_src=505400000001 -+ local eth_dst=00000000ff01 -+ -+ local ipv6_src=10000000000000000000000000000003 -+ local ipv6_dst=20000000000000000000000000000002 -+ local ipv6_rt=10000000000000000000000000000001 -+ -+ local payload=0000000000000000000000000000000000000000 -+ local payload=${payload}0000000000000000000000000000000000000000 -+ local payload=${payload}0000000000000000000000000000000000000000 -+ local payload=${payload}0000000000000000000000000000000000000000 -+ -+ local ip6_hdr=6000000000583aff${ipv6_src}${ipv6_dst} -+ local packet=${eth_dst}${eth_src}86dd${ip6_hdr}8000ec7662f00001${payload} -+ -+ as hv1 reset_pcap_file br-phys_n1 hv1/br-phys_n1 -+ as hv1 reset_pcap_file hv1-vif1 hv1/vif1 -+ -+ # Send packet from sw0-port1 to outside -+ as hv1 ovs-appctl netdev-dummy/receive hv1-vif1 $packet -+ -+ if test $icmp_pmtu_reply_expected = 1; then -+ icmp6_reply=${eth_src}${eth_dst}86dd6000000000883afe -+ icmp6_reply=${icmp6_reply}${ipv6_rt}${ipv6_src}020041ff00000076 -+ icmp6_reply=${icmp6_reply}6000000000583afe${ipv6_src}${ipv6_dst} -+ icmp6_reply=${icmp6_reply}8000ec7662f00001${payload} -+ echo $icmp6_reply > hv1-vif1.expected -+ -+ OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [hv1-vif1.expected]) -+ fi -+} -+ - ovn-nbctl show - ovn-sbctl show - -@@ -15283,6 +15319,12 @@ OVS_WAIT_UNTIL([ - # Now the packet should be sent via the localnet port to br-phys. - icmp_reply_expected=0 - test_ip_packet_larger $icmp_reply_expected -+ -+# Set the gateway mtu to 118 -+ovn-nbctl --wait=hv set logical_router_port lr0-public options:gateway_mtu=118 -+icmp_reply_expected=1 -+test_ip6_packet_larger $icmp_reply_expected -+ - OVN_CLEANUP([hv1]) - AT_CLEANUP - --- -2.26.2 - diff --git a/SOURCES/0009-Add-IP-address-normalization-to-ovn-utils.patch b/SOURCES/0009-Add-IP-address-normalization-to-ovn-utils.patch deleted file mode 100644 index e7a69ce..0000000 --- a/SOURCES/0009-Add-IP-address-normalization-to-ovn-utils.patch +++ /dev/null @@ -1,126 +0,0 @@ -From 6d0628f34e7462cbe7c580774b19644609757b85 Mon Sep 17 00:00:00 2001 -From: Mark Michelson -Date: Mon, 20 Jul 2020 14:53:15 -0400 -Subject: [PATCH 09/22] Add IP address normalization to ovn-utils. - -This moves a couple of existing IP address normalization routines from -ovn-nbctl.c to ovn-util.c. It also adds a new normalization function for -a v46_address. This new function is not used in this commit but will be -used in a future commit in this series. - -Signed-off-by: Mark Michelson -Acked-by: Numan Siddique ---- - lib/ovn-util.c | 39 +++++++++++++++++++++++++++++++++++++++ - lib/ovn-util.h | 4 ++++ - utilities/ovn-nbctl.c | 29 ----------------------------- - 3 files changed, 43 insertions(+), 29 deletions(-) - -diff --git a/lib/ovn-util.c b/lib/ovn-util.c -index f09fdaffe..cdb5e18fb 100644 ---- a/lib/ovn-util.c -+++ b/lib/ovn-util.c -@@ -589,6 +589,45 @@ ip46_equals(const struct v46_ip *addr1, const struct v46_ip *addr2) - IN6_ARE_ADDR_EQUAL(&addr1->ipv6, &addr2->ipv6))); - } - -+/* The caller must free the returned string. */ -+char * -+normalize_ipv4_prefix(ovs_be32 ipv4, unsigned int plen) -+{ -+ ovs_be32 network = ipv4 & be32_prefix_mask(plen); -+ if (plen == 32) { -+ return xasprintf(IP_FMT, IP_ARGS(network)); -+ } else { -+ return xasprintf(IP_FMT "/%d", IP_ARGS(network), plen); -+ } -+} -+ -+/* The caller must free the returned string. */ -+char * -+normalize_ipv6_prefix(struct in6_addr ipv6, unsigned int plen) -+{ -+ char network_s[INET6_ADDRSTRLEN]; -+ -+ struct in6_addr mask = ipv6_create_mask(plen); -+ struct in6_addr network = ipv6_addr_bitand(&ipv6, &mask); -+ -+ inet_ntop(AF_INET6, &network, network_s, INET6_ADDRSTRLEN); -+ if (plen == 128) { -+ return xasprintf("%s", network_s); -+ } else { -+ return xasprintf("%s/%d", network_s, plen); -+ } -+} -+ -+char * -+normalize_v46_prefix(const struct v46_ip *prefix, unsigned int plen) -+{ -+ if (prefix->family == AF_INET) { -+ return normalize_ipv4_prefix(prefix->ipv4, plen); -+ } else { -+ return normalize_ipv6_prefix(prefix->ipv6, plen); -+ } -+} -+ - char * - str_tolower(const char *orig) - { -diff --git a/lib/ovn-util.h b/lib/ovn-util.h -index 4e08ee01e..0f7b501f1 100644 ---- a/lib/ovn-util.h -+++ b/lib/ovn-util.h -@@ -144,6 +144,10 @@ bool ip46_parse_cidr(const char *str, struct v46_ip *prefix, - unsigned int *plen); - bool ip46_equals(const struct v46_ip *addr1, const struct v46_ip *addr2); - -+char *normalize_ipv4_prefix(ovs_be32 ipv4, unsigned int plen); -+char *normalize_ipv6_prefix(struct in6_addr ipv6, unsigned int plen); -+char *normalize_v46_prefix(const struct v46_ip *prefix, unsigned int plen); -+ - /* Returns a lowercase copy of orig. - * Caller must free the returned string. - */ -diff --git a/utilities/ovn-nbctl.c b/utilities/ovn-nbctl.c -index 7578b9928..0079ad5a6 100644 ---- a/utilities/ovn-nbctl.c -+++ b/utilities/ovn-nbctl.c -@@ -3482,35 +3482,6 @@ nbctl_dhcp_options_list(struct ctl_context *ctx) - free(nodes); - } - --/* The caller must free the returned string. */ --static char * --normalize_ipv4_prefix(ovs_be32 ipv4, unsigned int plen) --{ -- ovs_be32 network = ipv4 & be32_prefix_mask(plen); -- if (plen == 32) { -- return xasprintf(IP_FMT, IP_ARGS(network)); -- } else { -- return xasprintf(IP_FMT"/%d", IP_ARGS(network), plen); -- } --} -- --/* The caller must free the returned string. */ --static char * --normalize_ipv6_prefix(struct in6_addr ipv6, unsigned int plen) --{ -- char network_s[INET6_ADDRSTRLEN]; -- -- struct in6_addr mask = ipv6_create_mask(plen); -- struct in6_addr network = ipv6_addr_bitand(&ipv6, &mask); -- -- inet_ntop(AF_INET6, &network, network_s, INET6_ADDRSTRLEN); -- if (plen == 128) { -- return xasprintf("%s", network_s); -- } else { -- return xasprintf("%s/%d", network_s, plen); -- } --} -- - static char * - normalize_ipv4_prefix_str(const char *orig_prefix) - { --- -2.26.2 - diff --git a/SOURCES/0010-Don-t-check-for-writeability-of-rhs-during-assignmen.patch b/SOURCES/0010-Don-t-check-for-writeability-of-rhs-during-assignmen.patch deleted file mode 100644 index 90d5f06..0000000 --- a/SOURCES/0010-Don-t-check-for-writeability-of-rhs-during-assignmen.patch +++ /dev/null @@ -1,31 +0,0 @@ -From 5b5b130408eebf999edacfe298c2fedc40d6e603 Mon Sep 17 00:00:00 2001 -From: Mark Michelson -Date: Mon, 27 Jul 2020 16:04:06 -0400 -Subject: [PATCH 10/22] Don't check for writeability of rhs during assignment. - -The only condition under which the right-hand side of an assignment -needs to be checked for writeability is if it is an exchange (<->) -operation. - -Signed-off-by: Mark Michelson -Acked-by: Numan Siddique ---- - lib/actions.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/lib/actions.c b/lib/actions.c -index 1deeef799..23e334404 100644 ---- a/lib/actions.c -+++ b/lib/actions.c -@@ -515,7 +515,7 @@ parse_assignment_action(struct action_context *ctx, bool exchange, - - char *error = expr_type_check(lhs, lhs->n_bits, true); - if (!error) { -- error = expr_type_check(&rhs, rhs.n_bits, true); -+ error = expr_type_check(&rhs, rhs.n_bits, exchange); - } - if (error) { - lexer_error(ctx->lexer, "%s", error); --- -2.26.2 - diff --git a/SOURCES/0011-Add-expression-writeability-scopes.patch b/SOURCES/0011-Add-expression-writeability-scopes.patch deleted file mode 100644 index c5d0f1c..0000000 --- a/SOURCES/0011-Add-expression-writeability-scopes.patch +++ /dev/null @@ -1,423 +0,0 @@ -From 2bbba1279ae7b197d1b21dfb2ef7e45d0bcd8000 Mon Sep 17 00:00:00 2001 -From: Mark Michelson -Date: Mon, 27 Jul 2020 16:08:21 -0400 -Subject: [PATCH 11/22] Add expression writeability scopes. - -Logical fields are defined as either being writeable or read-only. There -is no way to make fields writeable only in specific scenarios. - -This commit changes the boolean writeability field to a field of flags -indicating contexts where a field is writeable. Any time that nested -actions are used (i.e. actions enclosed in curly braces), a new scope -may be set for the nested action. For this particular commit, no -functionality is changed, and only a "default" scope is added -that mirrors the current setup. A future commit will make use of this -feature. - -Change-Id: Id7a8dbedb862e8274c70597251233eeb35f81af6 -Signed-off-by: Mark Michelson -Acked-by: Numan Siddique ---- - include/ovn/expr.h | 52 ++++++++++++++++++++++++++++++++++------------ - lib/actions.c | 46 +++++++++++++++++++++------------------- - lib/expr.c | 35 ++++++++++++++++++------------- - 3 files changed, 84 insertions(+), 49 deletions(-) - -diff --git a/include/ovn/expr.h b/include/ovn/expr.h -index 9838251c1..11bfdad5b 100644 ---- a/include/ovn/expr.h -+++ b/include/ovn/expr.h -@@ -83,6 +83,10 @@ enum expr_level { - EXPR_L_ORDINAL - }; - -+enum expr_write_scope { -+ WR_DEFAULT = (1 << 0), /* Writeable at "global" level */ -+}; -+ - const char *expr_level_to_string(enum expr_level); - - /* A symbol. -@@ -255,7 +259,8 @@ struct expr_symbol { - - char *prereqs; - bool must_crossproduct; -- bool rw; -+ enum expr_write_scope rw; /* Bit map indicating in which nested contexts -+ * the symbol is writeable */ - }; - - void expr_symbol_format(const struct expr_symbol *, struct ds *); -@@ -273,20 +278,40 @@ bool expr_field_parse(struct lexer *, const struct shash *symtab, - struct expr_field *, struct expr **prereqsp); - void expr_field_format(const struct expr_field *, struct ds *); - --struct expr_symbol *expr_symtab_add_field(struct shash *symtab, -- const char *name, enum mf_field_id, -- const char *prereqs, -- bool must_crossproduct); --struct expr_symbol *expr_symtab_add_subfield(struct shash *symtab, -- const char *name, -- const char *prereqs, -- const char *subfield); --struct expr_symbol *expr_symtab_add_string(struct shash *symtab, -- const char *name, enum mf_field_id, -- const char *prereqs); -+struct expr_symbol *expr_symtab_add_field_scoped(struct shash *symtab, -+ const char *name, -+ enum mf_field_id, -+ const char *prereqs, -+ bool must_crossproduct, -+ enum expr_write_scope scope); -+ -+#define expr_symtab_add_field(SYMTAB, NAME, MF_FIELD_ID, PREREQS, \ -+ MUST_CROSSPRODUCT) \ -+ expr_symtab_add_field_scoped((SYMTAB), (NAME), (MF_FIELD_ID), (PREREQS), \ -+ (MUST_CROSSPRODUCT), WR_DEFAULT) -+ -+struct expr_symbol *expr_symtab_add_subfield_scoped(struct shash *symtab, -+ const char *name, const char *prereqs, const char *subfield, -+ enum expr_write_scope scope); -+ -+#define expr_symtab_add_subfield(SYMTAB, NAME, PREREQS, SUBFIELD) \ -+ expr_symtab_add_subfield_scoped((SYMTAB), (NAME), (PREREQS), \ -+ (SUBFIELD), WR_DEFAULT) -+ -+struct expr_symbol *expr_symtab_add_string_scoped(struct shash *symtab, -+ const char *name, -+ enum mf_field_id, -+ const char *prereqs, -+ enum expr_write_scope scope); -+ -+#define expr_symtab_add_string(SYMTAB, NAME, MF_FIELD_ID, PREREQS) \ -+ expr_symtab_add_string_scoped((SYMTAB), (NAME), (MF_FIELD_ID), (PREREQS), \ -+ WR_DEFAULT) -+ - struct expr_symbol *expr_symtab_add_predicate(struct shash *symtab, - const char *name, - const char *expansion); -+ - struct expr_symbol *expr_symtab_add_ovn_field(struct shash *symtab, - const char *name, - enum ovn_field_id id); -@@ -452,7 +477,8 @@ void expr_matches_print(const struct hmap *matches, FILE *); - - /* Action parsing helper. */ - --char *expr_type_check(const struct expr_field *, int n_bits, bool rw) -+char *expr_type_check(const struct expr_field *, int n_bits, bool rw, -+ enum expr_write_scope scope) - OVS_WARN_UNUSED_RESULT; - struct mf_subfield expr_resolve_field(const struct expr_field *); - -diff --git a/lib/actions.c b/lib/actions.c -index 23e334404..460ab0cf5 100644 ---- a/lib/actions.c -+++ b/lib/actions.c -@@ -195,6 +195,7 @@ struct action_context { - struct ofpbuf *ovnacts; /* Actions. */ - struct expr *prereqs; /* Prerequisites to apply to match. */ - int depth; /* Current nested action depth. */ -+ enum expr_write_scope scope; /* Current writeability scope */ - }; - - static void parse_actions(struct action_context *, enum lex_type sentinel); -@@ -207,7 +208,7 @@ action_parse_field(struct action_context *ctx, - return false; - } - -- char *error = expr_type_check(f, n_bits, rw); -+ char *error = expr_type_check(f, n_bits, rw, ctx->scope); - if (error) { - lexer_error(ctx->lexer, "%s", error); - free(error); -@@ -374,7 +375,7 @@ parse_LOAD(struct action_context *ctx, const struct expr_field *lhs) - - load->dst = *lhs; - -- char *error = expr_type_check(lhs, lhs->n_bits, true); -+ char *error = expr_type_check(lhs, lhs->n_bits, true, ctx->scope); - if (error) { - ctx->ovnacts->size = ofs; - lexer_error(ctx->lexer, "%s", error); -@@ -513,9 +514,9 @@ parse_assignment_action(struct action_context *ctx, bool exchange, - return; - } - -- char *error = expr_type_check(lhs, lhs->n_bits, true); -+ char *error = expr_type_check(lhs, lhs->n_bits, true, ctx->scope); - if (!error) { -- error = expr_type_check(&rhs, rhs.n_bits, exchange); -+ error = expr_type_check(&rhs, rhs.n_bits, exchange, ctx->scope); - } - if (error) { - lexer_error(ctx->lexer, "%s", error); -@@ -1186,7 +1187,8 @@ static void - parse_select_action(struct action_context *ctx, struct expr_field *res_field) - { - /* Check if the result field is modifiable. */ -- char *error = expr_type_check(res_field, res_field->n_bits, true); -+ char *error = expr_type_check(res_field, res_field->n_bits, true, -+ ctx->scope); - if (error) { - lexer_error(ctx->lexer, "%s", error); - free(error); -@@ -1337,7 +1339,7 @@ encode_CT_CLEAR(const struct ovnact_null *null OVS_UNUSED, - * actions on a packet derived from the one being processed. */ - static void - parse_nested_action(struct action_context *ctx, enum ovnact_type type, -- const char *prereq) -+ const char *prereq, enum expr_write_scope scope) - { - if (!lexer_force_match(ctx->lexer, LEX_T_LCURLY)) { - return; -@@ -1357,6 +1359,7 @@ parse_nested_action(struct action_context *ctx, enum ovnact_type type, - .ovnacts = &nested, - .prereqs = NULL, - .depth = ctx->depth + 1, -+ .scope = scope, - }; - parse_actions(&inner_ctx, LEX_T_RCURLY); - -@@ -1387,61 +1390,61 @@ parse_nested_action(struct action_context *ctx, enum ovnact_type type, - static void - parse_ARP(struct action_context *ctx) - { -- parse_nested_action(ctx, OVNACT_ARP, "ip4"); -+ parse_nested_action(ctx, OVNACT_ARP, "ip4", ctx->scope); - } - - static void - parse_ICMP4(struct action_context *ctx) - { -- parse_nested_action(ctx, OVNACT_ICMP4, "ip4"); -+ parse_nested_action(ctx, OVNACT_ICMP4, "ip4", ctx->scope); - } - - static void - parse_ICMP4_ERROR(struct action_context *ctx) - { -- parse_nested_action(ctx, OVNACT_ICMP4_ERROR, "ip4"); -+ parse_nested_action(ctx, OVNACT_ICMP4_ERROR, "ip4", ctx->scope); - } - - static void - parse_ICMP6(struct action_context *ctx) - { -- parse_nested_action(ctx, OVNACT_ICMP6, "ip6"); -+ parse_nested_action(ctx, OVNACT_ICMP6, "ip6", ctx->scope); - } - - static void - parse_ICMP6_ERROR(struct action_context *ctx) - { -- parse_nested_action(ctx, OVNACT_ICMP6_ERROR, "ip6"); -+ parse_nested_action(ctx, OVNACT_ICMP6_ERROR, "ip6", ctx->scope); - } - - static void - parse_TCP_RESET(struct action_context *ctx) - { -- parse_nested_action(ctx, OVNACT_TCP_RESET, "tcp"); -+ parse_nested_action(ctx, OVNACT_TCP_RESET, "tcp", ctx->scope); - } - - static void - parse_ND_NA(struct action_context *ctx) - { -- parse_nested_action(ctx, OVNACT_ND_NA, "nd_ns"); -+ parse_nested_action(ctx, OVNACT_ND_NA, "nd_ns", ctx->scope); - } - - static void - parse_ND_NA_ROUTER(struct action_context *ctx) - { -- parse_nested_action(ctx, OVNACT_ND_NA_ROUTER, "nd_ns"); -+ parse_nested_action(ctx, OVNACT_ND_NA_ROUTER, "nd_ns", ctx->scope); - } - - static void - parse_ND_NS(struct action_context *ctx) - { -- parse_nested_action(ctx, OVNACT_ND_NS, "ip6"); -+ parse_nested_action(ctx, OVNACT_ND_NS, "ip6", ctx->scope); - } - - static void - parse_CLONE(struct action_context *ctx) - { -- parse_nested_action(ctx, OVNACT_CLONE, NULL); -+ parse_nested_action(ctx, OVNACT_CLONE, NULL, WR_DEFAULT); - } - - static void -@@ -1947,7 +1950,7 @@ parse_lookup_mac_bind(struct action_context *ctx, - struct ovnact_lookup_mac_bind *lookup_mac) - { - /* Validate that the destination is a 1-bit, modifiable field. */ -- char *error = expr_type_check(dst, 1, true); -+ char *error = expr_type_check(dst, 1, true, ctx->scope); - if (error) { - lexer_error(ctx->lexer, "%s", error); - free(error); -@@ -2053,7 +2056,7 @@ parse_lookup_mac_bind_ip(struct action_context *ctx, - struct ovnact_lookup_mac_bind_ip *lookup_mac) - { - /* Validate that the destination is a 1-bit, modifiable field. */ -- char *error = expr_type_check(dst, 1, true); -+ char *error = expr_type_check(dst, 1, true, ctx->scope); - if (error) { - lexer_error(ctx->lexer, "%s", error); - free(error); -@@ -2283,7 +2286,7 @@ parse_put_opts(struct action_context *ctx, const struct expr_field *dst, - lexer_get(ctx->lexer); /* Skip '('. */ - - /* Validate that the destination is a 1-bit, modifiable field. */ -- char *error = expr_type_check(dst, 1, true); -+ char *error = expr_type_check(dst, 1, true, ctx->scope); - if (error) { - lexer_error(ctx->lexer, "%s", error); - free(error); -@@ -2680,7 +2683,7 @@ parse_dns_lookup(struct action_context *ctx, const struct expr_field *dst, - return; - } - /* Validate that the destination is a 1-bit, modifiable field. */ -- char *error = expr_type_check(dst, 1, true); -+ char *error = expr_type_check(dst, 1, true, ctx->scope); - if (error) { - lexer_error(ctx->lexer, "%s", error); - free(error); -@@ -3205,7 +3208,7 @@ parse_check_pkt_larger(struct action_context *ctx, - struct ovnact_check_pkt_larger *cipl) - { - /* Validate that the destination is a 1-bit, modifiable field. */ -- char *error = expr_type_check(dst, 1, true); -+ char *error = expr_type_check(dst, 1, true, ctx->scope); - if (error) { - lexer_error(ctx->lexer, "%s", error); - free(error); -@@ -3677,6 +3680,7 @@ ovnacts_parse(struct lexer *lexer, const struct ovnact_parse_params *pp, - .lexer = lexer, - .ovnacts = ovnacts, - .prereqs = NULL, -+ .scope = WR_DEFAULT, - }; - if (!lexer->error) { - parse_actions(&ctx, LEX_T_END); -diff --git a/lib/expr.c b/lib/expr.c -index 497b2accc..c07e7dd4d 100644 ---- a/lib/expr.c -+++ b/lib/expr.c -@@ -1447,7 +1447,7 @@ expr_symbol_format(const struct expr_symbol *symbol, struct ds *s) - static struct expr_symbol * - add_symbol(struct shash *symtab, const char *name, int width, - const char *prereqs, enum expr_level level, -- bool must_crossproduct, bool rw) -+ bool must_crossproduct, enum expr_write_scope rw) - { - struct expr_symbol *symbol = xzalloc(sizeof *symbol); - symbol->name = xstrdup(name); -@@ -1471,9 +1471,10 @@ add_symbol(struct shash *symtab, const char *name, int width, - * Use subfields to duplicate or subset a field (you can even make a subfield - * include all the bits of the "parent" field if you like). */ - struct expr_symbol * --expr_symtab_add_field(struct shash *symtab, const char *name, -- enum mf_field_id id, const char *prereqs, -- bool must_crossproduct) -+expr_symtab_add_field_scoped(struct shash *symtab, const char *name, -+ enum mf_field_id id, const char *prereqs, -+ bool must_crossproduct, -+ enum expr_write_scope scope) - { - const struct mf_field *field = mf_from_id(id); - struct expr_symbol *symbol; -@@ -1482,7 +1483,8 @@ expr_symtab_add_field(struct shash *symtab, const char *name, - (field->maskable == MFM_FULLY - ? EXPR_L_ORDINAL - : EXPR_L_NOMINAL), -- must_crossproduct, field->writable); -+ must_crossproduct, -+ field->writable ? scope : 0); - symbol->field = field; - return symbol; - } -@@ -1511,8 +1513,9 @@ parse_field_from_string(const char *s, const struct shash *symtab, - * 'subfield' must describe the subfield as a string, e.g. "vlan.tci[0..11]" - * for the low 12 bits of a larger field named "vlan.tci". */ - struct expr_symbol * --expr_symtab_add_subfield(struct shash *symtab, const char *name, -- const char *prereqs, const char *subfield) -+expr_symtab_add_subfield_scoped(struct shash *symtab, const char *name, -+ const char *prereqs, const char *subfield, -+ enum expr_write_scope scope) - { - struct expr_symbol *symbol; - struct expr_field f; -@@ -1531,7 +1534,7 @@ expr_symtab_add_subfield(struct shash *symtab, const char *name, - } - - symbol = add_symbol(symtab, name, f.n_bits, prereqs, level, false, -- f.symbol->rw); -+ f.symbol->rw ? scope : 0); - symbol->parent = f.symbol; - symbol->parent_ofs = f.ofs; - return symbol; -@@ -1540,14 +1543,15 @@ expr_symtab_add_subfield(struct shash *symtab, const char *name, - /* Adds a string-valued symbol named 'name' to 'symtab' with the specified - * 'prereqs'. */ - struct expr_symbol * --expr_symtab_add_string(struct shash *symtab, const char *name, -- enum mf_field_id id, const char *prereqs) -+expr_symtab_add_string_scoped(struct shash *symtab, const char *name, -+ enum mf_field_id id, const char *prereqs, -+ enum expr_write_scope scope) - { - const struct mf_field *field = mf_from_id(id); - struct expr_symbol *symbol; - - symbol = add_symbol(symtab, name, 0, prereqs, EXPR_L_NOMINAL, false, -- field->writable); -+ field->writable ? scope : 0); - symbol->field = field; - return symbol; - } -@@ -1610,7 +1614,7 @@ expr_symtab_add_predicate(struct shash *symtab, const char *name, - return NULL; - } - -- symbol = add_symbol(symtab, name, 1, NULL, level, false, false); -+ symbol = add_symbol(symtab, name, 1, NULL, level, false, 0); - symbol->predicate = xstrdup(expansion); - return symbol; - } -@@ -1623,7 +1627,7 @@ expr_symtab_add_ovn_field(struct shash *symtab, const char *name, - struct expr_symbol *symbol; - - symbol = add_symbol(symtab, name, ovn_field->n_bits, NULL, -- EXPR_L_NOMINAL, false, true); -+ EXPR_L_NOMINAL, false, UINT32_MAX); - symbol->ovn_field = ovn_field; - return symbol; - } -@@ -3322,7 +3326,8 @@ expr_evaluate(const struct expr *e, const struct flow *uflow, - * if 'f' is acceptable, otherwise a malloc()'d error message that the caller - * must free(). */ - char * OVS_WARN_UNUSED_RESULT --expr_type_check(const struct expr_field *f, int n_bits, bool rw) -+expr_type_check(const struct expr_field *f, int n_bits, bool rw, -+ uint32_t write_scope) - { - if (n_bits != f->n_bits) { - if (n_bits && f->n_bits) { -@@ -3340,7 +3345,7 @@ expr_type_check(const struct expr_field *f, int n_bits, bool rw) - } - } - -- if (rw && !f->symbol->rw) { -+ if (rw && !(f->symbol->rw & write_scope)) { - return xasprintf("Field %s is not modifiable.", f->symbol->name); - } - --- -2.26.2 - diff --git a/SOURCES/0012-Used-nested-actions-in-ct_commit.patch b/SOURCES/0012-Used-nested-actions-in-ct_commit.patch deleted file mode 100644 index 4c8db8e..0000000 --- a/SOURCES/0012-Used-nested-actions-in-ct_commit.patch +++ /dev/null @@ -1,401 +0,0 @@ -From b74a558d0a5e4c217f966d9615611e3dca1d6c23 Mon Sep 17 00:00:00 2001 -From: Mark Michelson -Date: Mon, 27 Jul 2020 16:11:56 -0400 -Subject: [PATCH 12/22] Used nested actions in ct_commit - -ct_commit allows for ct_label and ct_mark to be set within. However, -there are some restrictions with the current implementation: - -* It is not possible to address the indiviual bits within the ct_mark or - ct_label. -* It is not possible to set these to the value of a register. Only - explicit integer setting can be used. - -With this change, ct_commit now can have arbitrary nested actions -inside. This makes it similar to how the "exec" option works in OVS's -ct() action. - -ct_commit now also sets a writeability scope so that ct_mark and -ct_label are the only symbols that are writeable. The positive side -effect is that ct_mark and ct_label are no longer writeable except for -inside ct_commit. - -In this commit, the only noticeable effect is that it allows for -slightly more expressive setting of ct_label.blocked. A future commit -will take further advantage of this. - -Signed-off-by: Mark Michelson ---- - include/ovn/actions.h | 9 +--- - include/ovn/expr.h | 1 + - lib/actions.c | 110 +++++++----------------------------------- - lib/logical-fields.c | 9 ++-- - northd/ovn-northd.c | 8 +-- - ovn-sb.xml | 11 +++-- - tests/ovn.at | 59 ++++++++++++---------- - 7 files changed, 72 insertions(+), 135 deletions(-) - -diff --git a/include/ovn/actions.h b/include/ovn/actions.h -index 34ba0d880..636cb4bc1 100644 ---- a/include/ovn/actions.h -+++ b/include/ovn/actions.h -@@ -57,7 +57,7 @@ struct ovn_extend_table; - OVNACT(EXCHANGE, ovnact_move) \ - OVNACT(DEC_TTL, ovnact_null) \ - OVNACT(CT_NEXT, ovnact_ct_next) \ -- OVNACT(CT_COMMIT, ovnact_ct_commit) \ -+ OVNACT(CT_COMMIT, ovnact_nest) \ - OVNACT(CT_DNAT, ovnact_ct_nat) \ - OVNACT(CT_SNAT, ovnact_ct_nat) \ - OVNACT(CT_LB, ovnact_ct_lb) \ -@@ -222,13 +222,6 @@ struct ovnact_ct_next { - uint8_t ltable; /* Logical table ID of next table. */ - }; - --/* OVNACT_CT_COMMIT. */ --struct ovnact_ct_commit { -- struct ovnact ovnact; -- uint32_t ct_mark, ct_mark_mask; -- ovs_be128 ct_label, ct_label_mask; --}; -- - /* OVNACT_CT_DNAT, OVNACT_CT_SNAT. */ - struct ovnact_ct_nat { - struct ovnact ovnact; -diff --git a/include/ovn/expr.h b/include/ovn/expr.h -index 11bfdad5b..b34fb0e81 100644 ---- a/include/ovn/expr.h -+++ b/include/ovn/expr.h -@@ -85,6 +85,7 @@ enum expr_level { - - enum expr_write_scope { - WR_DEFAULT = (1 << 0), /* Writeable at "global" level */ -+ WR_CT_COMMIT = (1 << 1), /* Writeable in "ct_commit" action */ - }; - - const char *expr_level_to_string(enum expr_level); -diff --git a/lib/actions.c b/lib/actions.c -index 460ab0cf5..79ac79a95 100644 ---- a/lib/actions.c -+++ b/lib/actions.c -@@ -200,6 +200,15 @@ struct action_context { - - static void parse_actions(struct action_context *, enum lex_type sentinel); - -+static void parse_nested_action(struct action_context *ctx, -+ enum ovnact_type type, -+ const char *prereq, -+ enum expr_write_scope scope); -+ -+static void format_nested_action(const struct ovnact_nest *on, -+ const char *name, -+ struct ds *s); -+ - static bool - action_parse_field(struct action_context *ctx, - int n_bits, bool rw, struct expr_field *f) -@@ -618,125 +627,42 @@ ovnact_ct_next_free(struct ovnact_ct_next *a OVS_UNUSED) - { - } - --static void --parse_ct_commit_arg(struct action_context *ctx, -- struct ovnact_ct_commit *cc) --{ -- if (lexer_match_id(ctx->lexer, "ct_mark")) { -- if (!lexer_force_match(ctx->lexer, LEX_T_EQUALS)) { -- return; -- } -- if (ctx->lexer->token.type == LEX_T_INTEGER) { -- cc->ct_mark = ntohll(ctx->lexer->token.value.integer); -- cc->ct_mark_mask = UINT32_MAX; -- } else if (ctx->lexer->token.type == LEX_T_MASKED_INTEGER) { -- cc->ct_mark = ntohll(ctx->lexer->token.value.integer); -- cc->ct_mark_mask = ntohll(ctx->lexer->token.mask.integer); -- } else { -- lexer_syntax_error(ctx->lexer, "expecting integer"); -- return; -- } -- lexer_get(ctx->lexer); -- } else if (lexer_match_id(ctx->lexer, "ct_label")) { -- if (!lexer_force_match(ctx->lexer, LEX_T_EQUALS)) { -- return; -- } -- if (ctx->lexer->token.type == LEX_T_INTEGER) { -- cc->ct_label = ctx->lexer->token.value.be128_int; -- cc->ct_label_mask = OVS_BE128_MAX; -- } else if (ctx->lexer->token.type == LEX_T_MASKED_INTEGER) { -- cc->ct_label = ctx->lexer->token.value.be128_int; -- cc->ct_label_mask = ctx->lexer->token.mask.be128_int; -- } else { -- lexer_syntax_error(ctx->lexer, "expecting integer"); -- return; -- } -- lexer_get(ctx->lexer); -- } else { -- lexer_syntax_error(ctx->lexer, NULL); -- } --} -- - static void - parse_CT_COMMIT(struct action_context *ctx) - { -- add_prerequisite(ctx, "ip"); - -- struct ovnact_ct_commit *ct_commit = ovnact_put_CT_COMMIT(ctx->ovnacts); -- if (lexer_match(ctx->lexer, LEX_T_LPAREN)) { -- while (!lexer_match(ctx->lexer, LEX_T_RPAREN)) { -- parse_ct_commit_arg(ctx, ct_commit); -- if (ctx->lexer->error) { -- return; -- } -- lexer_match(ctx->lexer, LEX_T_COMMA); -- } -- } -+ parse_nested_action(ctx, OVNACT_CT_COMMIT, "ip", -+ WR_CT_COMMIT); - } - - static void --format_CT_COMMIT(const struct ovnact_ct_commit *cc, struct ds *s) -+format_CT_COMMIT(const struct ovnact_nest *on, struct ds *s) - { -- ds_put_cstr(s, "ct_commit("); -- if (cc->ct_mark_mask) { -- ds_put_format(s, "ct_mark=%#"PRIx32, cc->ct_mark); -- if (cc->ct_mark_mask != UINT32_MAX) { -- ds_put_format(s, "/%#"PRIx32, cc->ct_mark_mask); -- } -- } -- if (!ovs_be128_is_zero(cc->ct_label_mask)) { -- if (ds_last(s) != '(') { -- ds_put_cstr(s, ", "); -- } -- -- ds_put_format(s, "ct_label="); -- ds_put_hex(s, &cc->ct_label, sizeof cc->ct_label); -- if (!ovs_be128_equals(cc->ct_label_mask, OVS_BE128_MAX)) { -- ds_put_char(s, '/'); -- ds_put_hex(s, &cc->ct_label_mask, sizeof cc->ct_label_mask); -- } -- } -- if (!ds_chomp(s, '(')) { -- ds_put_char(s, ')'); -- } -- ds_put_char(s, ';'); -+ format_nested_action(on, "ct_commit", s); - } - - static void --encode_CT_COMMIT(const struct ovnact_ct_commit *cc, -+encode_CT_COMMIT(const struct ovnact_nest *on, - const struct ovnact_encode_params *ep OVS_UNUSED, - struct ofpbuf *ofpacts) - { - struct ofpact_conntrack *ct = ofpact_put_CT(ofpacts); - ct->flags = NX_CT_F_COMMIT; - ct->recirc_table = NX_CT_RECIRC_NONE; -- ct->zone_src.field = mf_from_id(MFF_LOG_CT_ZONE); -+ ct->zone_src.field = ep->is_switch -+ ? mf_from_id(MFF_LOG_CT_ZONE) -+ : mf_from_id(MFF_LOG_DNAT_ZONE); - ct->zone_src.ofs = 0; - ct->zone_src.n_bits = 16; - - size_t set_field_offset = ofpacts->size; - ofpbuf_pull(ofpacts, set_field_offset); - -- if (cc->ct_mark_mask) { -- const ovs_be32 value = htonl(cc->ct_mark); -- const ovs_be32 mask = htonl(cc->ct_mark_mask); -- ofpact_put_set_field(ofpacts, mf_from_id(MFF_CT_MARK), &value, &mask); -- } -- -- if (!ovs_be128_is_zero(cc->ct_label_mask)) { -- ofpact_put_set_field(ofpacts, mf_from_id(MFF_CT_LABEL), &cc->ct_label, -- &cc->ct_label_mask); -- } -- -+ ovnacts_encode(on->nested, on->nested_len, ep, ofpacts); - ofpacts->header = ofpbuf_push_uninit(ofpacts, set_field_offset); - ct = ofpacts->header; - ofpact_finish(ofpacts, &ct->ofpact); - } -- --static void --ovnact_ct_commit_free(struct ovnact_ct_commit *cc OVS_UNUSED) --{ --} - - static void - parse_ct_nat(struct action_context *ctx, const char *name, -diff --git a/lib/logical-fields.c b/lib/logical-fields.c -index 8639523ea..fde53a47e 100644 ---- a/lib/logical-fields.c -+++ b/lib/logical-fields.c -@@ -123,10 +123,13 @@ ovn_init_symtab(struct shash *symtab) - flags_str); - - /* Connection tracking state. */ -- expr_symtab_add_field(symtab, "ct_mark", MFF_CT_MARK, NULL, false); -+ expr_symtab_add_field_scoped(symtab, "ct_mark", MFF_CT_MARK, NULL, false, -+ WR_CT_COMMIT); - -- expr_symtab_add_field(symtab, "ct_label", MFF_CT_LABEL, NULL, false); -- expr_symtab_add_subfield(symtab, "ct_label.blocked", NULL, "ct_label[0]"); -+ expr_symtab_add_field_scoped(symtab, "ct_label", MFF_CT_LABEL, NULL, false, -+ WR_CT_COMMIT); -+ expr_symtab_add_subfield_scoped(symtab, "ct_label.blocked", NULL, -+ "ct_label[0]", WR_CT_COMMIT); - - expr_symtab_add_field(symtab, "ct_state", MFF_CT_STATE, NULL, false); - -diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c -index 6375aee8d..44e7d9365 100644 ---- a/northd/ovn-northd.c -+++ b/northd/ovn-northd.c -@@ -5356,7 +5356,7 @@ consider_acl(struct hmap *lflows, struct ovn_datapath *od, - ds_clear(&match); - ds_clear(&actions); - ds_put_cstr(&match, "ct.est && ct_label.blocked == 0"); -- ds_put_cstr(&actions, "ct_commit(ct_label=1/1); "); -+ ds_put_cstr(&actions, "ct_commit { ct_label.blocked = 1; }; "); - if (!strcmp(acl->action, "reject")) { - build_reject_acl_rules(od, lflows, stage, acl, &match, - &actions, &acl->header_); -@@ -5880,9 +5880,11 @@ build_stateful(struct ovn_datapath *od, struct hmap *lflows, struct hmap *lbs) - * any packet that makes it this far is part of a connection we - * want to allow to continue. */ - ovn_lflow_add(lflows, od, S_SWITCH_IN_STATEFUL, 100, -- REGBIT_CONNTRACK_COMMIT" == 1", "ct_commit(ct_label=0/1); next;"); -+ REGBIT_CONNTRACK_COMMIT" == 1", -+ "ct_commit { ct_label.blocked = 0; }; next;"); - ovn_lflow_add(lflows, od, S_SWITCH_OUT_STATEFUL, 100, -- REGBIT_CONNTRACK_COMMIT" == 1", "ct_commit(ct_label=0/1); next;"); -+ REGBIT_CONNTRACK_COMMIT" == 1", -+ "ct_commit { ct_label.blocked = 0; }; next;"); - - /* If REGBIT_CONNTRACK_NAT is set as 1, then packets should just be sent - * through nat (without committing). -diff --git a/ovn-sb.xml b/ovn-sb.xml -index fc39b2d03..a74d9c3ea 100644 ---- a/ovn-sb.xml -+++ b/ovn-sb.xml -@@ -1261,10 +1261,10 @@ -

      - - --
      ct_commit;
      --
      ct_commit(ct_mark=value[/mask]);
      --
      ct_commit(ct_label=value[/mask]);
      --
      ct_commit(ct_mark=value[/mask], ct_label=value[/mask]);
      -+
      ct_commit { };
      -+
      ct_commit { ct_mark=value[/mask]; };
      -+
      ct_commit { ct_label=value[/mask]; };
      -+
      ct_commit { ct_mark=value[/mask]; ct_label=value[/mask]; };
      -
      -

      - Commit the flow to the connection tracking entry associated with it -@@ -1276,6 +1276,9 @@ - tracking entry. ct_mark is a 32-bit field. - ct_label is a 128-bit field. The value[/mask] - should be specified in hex string if more than 64bits are to be used. -+ Registers and other named fields can be used for value. -+ ct_mark and ct_label may be sub-addressed -+ in order to have specific bits set. -

      - -

      -diff --git a/tests/ovn.at b/tests/ovn.at -index 905112a8d..4c68b77d8 100644 ---- a/tests/ovn.at -+++ b/tests/ovn.at -@@ -1045,51 +1045,60 @@ ct_next; - has prereqs ip - - # ct_commit --ct_commit; -+ct_commit { }; -+ formats as ct_commit { drop; }; - encodes as ct(commit,zone=NXM_NX_REG13[0..15]) - has prereqs ip --ct_commit(); -- formats as ct_commit; -- encodes as ct(commit,zone=NXM_NX_REG13[0..15]) -- has prereqs ip --ct_commit(ct_mark=1); -- formats as ct_commit(ct_mark=0x1); -+ct_commit { ct_mark=1; }; -+ formats as ct_commit { ct_mark = 1; }; - encodes as ct(commit,zone=NXM_NX_REG13[0..15],exec(set_field:0x1->ct_mark)) - has prereqs ip --ct_commit(ct_mark=1/1); -- formats as ct_commit(ct_mark=0x1/0x1); -+ct_commit { ct_mark=1/1; }; -+ formats as ct_commit { ct_mark = 1/1; }; - encodes as ct(commit,zone=NXM_NX_REG13[0..15],exec(set_field:0x1/0x1->ct_mark)) - has prereqs ip --ct_commit(ct_label=1); -- formats as ct_commit(ct_label=0x1); -+ct_commit { ct_label=1; }; -+ formats as ct_commit { ct_label = 1; }; - encodes as ct(commit,zone=NXM_NX_REG13[0..15],exec(set_field:0x1->ct_label)) - has prereqs ip --ct_commit(ct_label=1/1); -- formats as ct_commit(ct_label=0x1/0x1); -+ct_commit { ct_label=1/1; }; -+ formats as ct_commit { ct_label = 1/1; }; - encodes as ct(commit,zone=NXM_NX_REG13[0..15],exec(set_field:0x1/0x1->ct_label)) - has prereqs ip --ct_commit(ct_mark=1, ct_label=2); -- formats as ct_commit(ct_mark=0x1, ct_label=0x2); -+ct_commit { ct_mark=1; ct_label=2; }; -+ formats as ct_commit { ct_mark = 1; ct_label = 2; }; - encodes as ct(commit,zone=NXM_NX_REG13[0..15],exec(set_field:0x1->ct_mark,set_field:0x2->ct_label)) - has prereqs ip - --ct_commit(ct_label=0x01020304050607080910111213141516); -- formats as ct_commit(ct_label=0x1020304050607080910111213141516); -+ct_commit { ct_label=0x01020304050607080910111213141516; }; -+ formats as ct_commit { ct_label = 0x1020304050607080910111213141516; }; - encodes as ct(commit,zone=NXM_NX_REG13[0..15],exec(set_field:0x1020304050607080910111213141516->ct_label)) - has prereqs ip --ct_commit(ct_label=0x181716151413121110090807060504030201); -- formats as ct_commit(ct_label=0x16151413121110090807060504030201); -- encodes as ct(commit,zone=NXM_NX_REG13[0..15],exec(set_field:0x16151413121110090807060504030201->ct_label)) -- has prereqs ip --ct_commit(ct_label=0x1000000000000000000000000000000/0x1000000000000000000000000000000); -+ct_commit { ct_label=0x1000000000000000000000000000000/0x1000000000000000000000000000000; }; -+ formats as ct_commit { ct_label = 0x1000000000000000000000000000000/0x1000000000000000000000000000000; }; - encodes as ct(commit,zone=NXM_NX_REG13[0..15],exec(set_field:0x1000000000000000000000000000000/0x1000000000000000000000000000000->ct_label)) - has prereqs ip --ct_commit(ct_label=18446744073709551615); -- formats as ct_commit(ct_label=0xffffffffffffffff); -+ct_commit { ct_label=18446744073709551615; }; -+ formats as ct_commit { ct_label = 18446744073709551615; }; - encodes as ct(commit,zone=NXM_NX_REG13[0..15],exec(set_field:0xffffffffffffffff->ct_label)) - has prereqs ip --ct_commit(ct_label=18446744073709551616); -+ct_commit { ct_label[0..47] = 0x00000f040201; ct_label[48..63] = 0x0002; }; -+ formats as ct_commit { ct_label[0..47] = 0xf040201; ct_label[48..63] = 0x2; }; -+ encodes as ct(commit,zone=NXM_NX_REG13[0..15],exec(set_field:0xf040201/0xffffffffffff->ct_label,set_field:0x2000000000000/0xffff000000000000->ct_label)) -+ has prereqs ip -+ct_commit { ct_label=18446744073709551616; }; - Decimal constants must be less than 2**64. -+ct_commit { ct_label=0x181716151413121110090807060504030201; }; -+ 141-bit constant is not compatible with 128-bit field ct_label. -+ct_commit { ip4.dst = 192.168.0.1; }; -+ Field ip4.dst is not modifiable. -+ -+ct_mark = 12345 -+ Field ct_mark is not modifiable. -+ct_label = 0xcafe -+ Field ct_label is not modifiable. -+ct_label.blocked = 1/1 -+ Field ct_label.blocked is not modifiable. - - # ct_dnat - ct_dnat; --- -2.26.2 - diff --git a/SOURCES/0013-Add-ECMP-symmetric-replies.patch b/SOURCES/0013-Add-ECMP-symmetric-replies.patch deleted file mode 100644 index 6a0a858..0000000 --- a/SOURCES/0013-Add-ECMP-symmetric-replies.patch +++ /dev/null @@ -1,922 +0,0 @@ -From 750e47ec508977af7bb37e9d0c98dd13984e9002 Mon Sep 17 00:00:00 2001 -From: Mark Michelson -Date: Mon, 20 Jul 2020 15:01:32 -0400 -Subject: [PATCH 13/22] Add ECMP symmetric replies. - -When traffic arrives over an ECMP route, there is no guarantee that the -reply traffic will egress over the same route. Sometimes, the nature of -the traffic (or the intervening equipment) means that it is important -for reply traffic to go out the same route it came in. - -This commit introduces optional ECMP symmetric reply behavior. If -configured, then traffic to or from the ECMP route will be sent to -conntrack. New incoming traffic over the route will have the source MAC -address and incoming port saved in the ct_label. Reply traffic then uses -this saved information to send the packet back out the same way it came -in. - -To facilitate this, a new table was added to the ingress logical router -pipeline. The ECMP_STATEFUL table is responsible for committing to -conntrack and setting the ct_label when it detects new incoming traffic -from the route. - -Since ingress pipeline logic on the logical router depends on ct state -of a particular hypervisor, this feature is only usable on gateway -routers. - -Change-Id: I6e5177a6de2258286869114ab6b4028667fee009 -Signed-off-by: Mark Michelson -Reported-at: https://bugzilla.redhat.com/show_bug.cgi?id=1849683 -Acked-by: Numan Siddique ---- - lib/logical-fields.c | 4 + - northd/ovn-northd.8.xml | 49 +++++++++--- - northd/ovn-northd.c | 123 ++++++++++++++++++++++++++---- - ovn-architecture.7.xml | 7 +- - ovn-nb.ovsschema | 7 +- - ovn-nb.xml | 16 ++++ - tests/ovn.at | 152 ++++++++++++++++++++++++++++++++++---- - tests/system-ovn.at | 143 +++++++++++++++++++++++++++++++++++ - utilities/ovn-nbctl.8.xml | 31 ++++++-- - utilities/ovn-nbctl.c | 18 ++++- - 10 files changed, 496 insertions(+), 54 deletions(-) - -diff --git a/lib/logical-fields.c b/lib/logical-fields.c -index fde53a47e..15342dded 100644 ---- a/lib/logical-fields.c -+++ b/lib/logical-fields.c -@@ -130,6 +130,10 @@ ovn_init_symtab(struct shash *symtab) - WR_CT_COMMIT); - expr_symtab_add_subfield_scoped(symtab, "ct_label.blocked", NULL, - "ct_label[0]", WR_CT_COMMIT); -+ expr_symtab_add_subfield_scoped(symtab, "ct_label.ecmp_reply_eth", NULL, -+ "ct_label[32..79]", WR_CT_COMMIT); -+ expr_symtab_add_subfield_scoped(symtab, "ct_label.ecmp_reply_port", NULL, -+ "ct_label[80..95]", WR_CT_COMMIT); - - expr_symtab_add_field(symtab, "ct_state", MFF_CT_STATE, NULL, false); - -diff --git a/northd/ovn-northd.8.xml b/northd/ovn-northd.8.xml -index 623768988..f35a035fd 100644 ---- a/northd/ovn-northd.8.xml -+++ b/northd/ovn-northd.8.xml -@@ -2175,15 +2175,31 @@ icmp6 { -

      - This is to send packets to connection tracker for tracking and - defragmentation. It contains a priority-0 flow that simply moves traffic -- to the next table. If load balancing rules with virtual IP addresses -- (and ports) are configured in OVN_Northbound database for a -- Gateway router, a priority-100 flow is added for each configured virtual -- IP address VIP. For IPv4 VIPs the flow matches -- ip && ip4.dst == VIP. For IPv6 -- VIPs, the flow matches ip && ip6.dst == -- VIP. The flow uses the action ct_next; -- to send IP packets to the connection tracker for packet de-fragmentation -- and tracking before sending it to the next table. -+ to the next table. -+

      -+ -+

      -+ If load balancing rules with virtual IP addresses (and ports) are -+ configured in OVN_Northbound database for a Gateway router, -+ a priority-100 flow is added for each configured virtual IP address -+ VIP. For IPv4 VIPs the flow matches ip -+ && ip4.dst == VIP. For IPv6 VIPs, -+ the flow matches ip && ip6.dst == VIP. -+ The flow uses the action ct_next; to send IP packets to the -+ connection tracker for packet de-fragmentation and tracking before -+ sending it to the next table. -+

      -+ -+

      -+ If ECMP routes with symmetric reply are configured in the -+ OVN_Northbound database for a gateway router, a priority-100 -+ flow is added for each router port on which symmetric replies are -+ configured. The matching logic for these ports essentially reverses the -+ configured logic of the ECMP route. So for instance, a route with a -+ destination routing policy will instead match if the source IP address -+ matches the static route's prefix. The flow uses the action -+ ct_next to send IP packets to the connection tracker for -+ packet de-fragmentation and tracking before sending it to the next table. -

      - -

      Ingress Table 5: UNSNAT

      -@@ -2544,7 +2560,15 @@ output; - table. This table, instead, is responsible for determine the ECMP - group id and select a member id within the group based on 5-tuple - hashing. It stores group id in reg8[0..15] and member id in -- reg8[16..31]. -+ reg8[16..31]. This step is skipped if the traffic going -+ out the ECMP route is reply traffic, and the ECMP route was configured -+ to use symmetric replies. Instead, the stored ct_label value -+ is used to choose the destination. The least significant 48 bits of the -+ ct_label tell the destination MAC address to which the -+ packet should be sent. The next 16 bits tell the logical router port on -+ which the packet should be sent. These values in the -+ ct_label are set when the initial ingress traffic is -+ received over the ECMP route. -

      - -

      -@@ -2694,6 +2718,11 @@ select(reg8[16..31], MID1, MID2, ...); - address and reg1 as the source protocol address). -

      - -+

      -+ This processing is skipped for reply traffic being sent out of an ECMP -+ route if the route was configured to use symmetric replies. -+

      -+ -

      - This table contains the following logical flows: -

      -diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c -index 44e7d9365..cb8e25bdf 100644 ---- a/northd/ovn-northd.c -+++ b/northd/ovn-northd.c -@@ -171,16 +171,17 @@ enum ovn_stage { - PIPELINE_STAGE(ROUTER, IN, DEFRAG, 4, "lr_in_defrag") \ - PIPELINE_STAGE(ROUTER, IN, UNSNAT, 5, "lr_in_unsnat") \ - PIPELINE_STAGE(ROUTER, IN, DNAT, 6, "lr_in_dnat") \ -- PIPELINE_STAGE(ROUTER, IN, ND_RA_OPTIONS, 7, "lr_in_nd_ra_options") \ -- PIPELINE_STAGE(ROUTER, IN, ND_RA_RESPONSE, 8, "lr_in_nd_ra_response") \ -- PIPELINE_STAGE(ROUTER, IN, IP_ROUTING, 9, "lr_in_ip_routing") \ -- PIPELINE_STAGE(ROUTER, IN, IP_ROUTING_ECMP, 10, "lr_in_ip_routing_ecmp") \ -- PIPELINE_STAGE(ROUTER, IN, POLICY, 11, "lr_in_policy") \ -- PIPELINE_STAGE(ROUTER, IN, ARP_RESOLVE, 12, "lr_in_arp_resolve") \ -- PIPELINE_STAGE(ROUTER, IN, CHK_PKT_LEN , 13, "lr_in_chk_pkt_len") \ -- PIPELINE_STAGE(ROUTER, IN, LARGER_PKTS, 14,"lr_in_larger_pkts") \ -- PIPELINE_STAGE(ROUTER, IN, GW_REDIRECT, 15, "lr_in_gw_redirect") \ -- PIPELINE_STAGE(ROUTER, IN, ARP_REQUEST, 16, "lr_in_arp_request") \ -+ PIPELINE_STAGE(ROUTER, IN, ECMP_STATEFUL, 7, "lr_in_ecmp_stateful") \ -+ PIPELINE_STAGE(ROUTER, IN, ND_RA_OPTIONS, 8, "lr_in_nd_ra_options") \ -+ PIPELINE_STAGE(ROUTER, IN, ND_RA_RESPONSE, 9, "lr_in_nd_ra_response") \ -+ PIPELINE_STAGE(ROUTER, IN, IP_ROUTING, 10, "lr_in_ip_routing") \ -+ PIPELINE_STAGE(ROUTER, IN, IP_ROUTING_ECMP, 11, "lr_in_ip_routing_ecmp") \ -+ PIPELINE_STAGE(ROUTER, IN, POLICY, 12, "lr_in_policy") \ -+ PIPELINE_STAGE(ROUTER, IN, ARP_RESOLVE, 13, "lr_in_arp_resolve") \ -+ PIPELINE_STAGE(ROUTER, IN, CHK_PKT_LEN , 14, "lr_in_chk_pkt_len") \ -+ PIPELINE_STAGE(ROUTER, IN, LARGER_PKTS, 15,"lr_in_larger_pkts") \ -+ PIPELINE_STAGE(ROUTER, IN, GW_REDIRECT, 16, "lr_in_gw_redirect") \ -+ PIPELINE_STAGE(ROUTER, IN, ARP_REQUEST, 17, "lr_in_arp_request") \ - \ - /* Logical router egress stages. */ \ - PIPELINE_STAGE(ROUTER, OUT, UNDNAT, 0, "lr_out_undnat") \ -@@ -7430,6 +7431,7 @@ struct parsed_route { - bool is_src_route; - uint32_t hash; - const struct nbrec_logical_router_static_route *route; -+ bool ecmp_symmetric_reply; - }; - - static uint32_t -@@ -7491,6 +7493,8 @@ parsed_routes_add(struct ovs_list *routes, - "src-ip")); - pr->hash = route_hash(pr); - pr->route = route; -+ pr->ecmp_symmetric_reply = smap_get_bool(&route->options, -+ "ecmp_symmetric_reply", false); - ovs_list_insert(routes, &pr->list_node); - return pr; - } -@@ -7739,18 +7743,95 @@ find_static_route_outport(struct ovn_datapath *od, struct hmap *ports, - return true; - } - -+static void -+add_ecmp_symmetric_reply_flows(struct hmap *lflows, -+ struct ovn_datapath *od, -+ const char *port_ip, -+ struct ovn_port *out_port, -+ const struct parsed_route *route, -+ struct ds *route_match) -+{ -+ const struct nbrec_logical_router_static_route *st_route = route->route; -+ struct ds match = DS_EMPTY_INITIALIZER; -+ struct ds actions = DS_EMPTY_INITIALIZER; -+ struct ds ecmp_reply = DS_EMPTY_INITIALIZER; -+ char *cidr = normalize_v46_prefix(&route->prefix, route->plen); -+ -+ /* If symmetric ECMP replies are enabled, then packets that arrive over -+ * an ECMP route need to go through conntrack. -+ */ -+ ds_put_format(&match, "inport == %s && ip%s.%s == %s", -+ out_port->json_key, -+ route->prefix.family == AF_INET ? "4" : "6", -+ route->is_src_route ? "dst" : "src", -+ cidr); -+ ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_DEFRAG, 100, -+ ds_cstr(&match), "ct_next;", -+ &st_route->header_); -+ -+ /* And packets that go out over an ECMP route need conntrack */ -+ ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_DEFRAG, 100, -+ ds_cstr(route_match), "ct_next;", -+ &st_route->header_); -+ -+ /* Save src eth and inport in ct_label for packets that arrive over -+ * an ECMP route. -+ * -+ * NOTE: we purposely are not clearing match before this -+ * ds_put_cstr() call. The previous contents are needed. -+ */ -+ ds_put_cstr(&match, " && (ct.new && !ct.est)"); -+ -+ ds_put_format(&actions, "ct_commit { ct_label.ecmp_reply_eth = eth.src;" -+ " ct_label.ecmp_reply_port = %" PRId64 ";}; next;", -+ out_port->sb->tunnel_key); -+ ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_ECMP_STATEFUL, 100, -+ ds_cstr(&match), ds_cstr(&actions), -+ &st_route->header_); -+ -+ /* Bypass ECMP selection if we already have ct_label information -+ * for where to route the packet. -+ */ -+ ds_put_format(&ecmp_reply, "ct.rpl && ct_label.ecmp_reply_port == %" -+ PRId64, out_port->sb->tunnel_key); -+ ds_clear(&match); -+ ds_put_format(&match, "%s && %s", ds_cstr(&ecmp_reply), -+ ds_cstr(route_match)); -+ ds_clear(&actions); -+ ds_put_format(&actions, "ip.ttl--; flags.loopback = 1; " -+ "eth.src = %s; %sreg1 = %s; outport = %s; next;", -+ out_port->lrp_networks.ea_s, -+ route->prefix.family == AF_INET ? "" : "xx", -+ port_ip, out_port->json_key); -+ ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_IP_ROUTING, 100, -+ ds_cstr(&match), ds_cstr(&actions), -+ &st_route->header_); -+ -+ /* Egress reply traffic for symmetric ECMP routes skips router policies. */ -+ ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_POLICY, 65535, -+ ds_cstr(&ecmp_reply), "next;", -+ &st_route->header_); -+ -+ ds_clear(&actions); -+ ds_put_cstr(&actions, "eth.dst = ct_label.ecmp_reply_eth; next;"); -+ ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_ARP_RESOLVE, -+ 200, ds_cstr(&ecmp_reply), -+ ds_cstr(&actions), &st_route->header_); -+} -+ - static void - build_ecmp_route_flow(struct hmap *lflows, struct ovn_datapath *od, - struct hmap *ports, struct ecmp_groups_node *eg) - - { - bool is_ipv4 = (eg->prefix.family == AF_INET); -- struct ds match = DS_EMPTY_INITIALIZER; - uint16_t priority; -+ struct ecmp_route_list_node *er; -+ struct ds route_match = DS_EMPTY_INITIALIZER; - - char *prefix_s = build_route_prefix_s(&eg->prefix, eg->plen); - build_route_match(NULL, prefix_s, eg->plen, eg->is_src_route, is_ipv4, -- &match, &priority); -+ &route_match, &priority); - free(prefix_s); - - struct ds actions = DS_EMPTY_INITIALIZER; -@@ -7758,7 +7839,6 @@ build_ecmp_route_flow(struct hmap *lflows, struct ovn_datapath *od, - "; %s = select(", REG_ECMP_GROUP_ID, eg->id, - REG_ECMP_MEMBER_ID); - -- struct ecmp_route_list_node *er; - bool is_first = true; - LIST_FOR_EACH (er, list_node, &eg->route_list) { - if (is_first) { -@@ -7772,11 +7852,12 @@ build_ecmp_route_flow(struct hmap *lflows, struct ovn_datapath *od, - ds_put_cstr(&actions, ");"); - - ovn_lflow_add(lflows, od, S_ROUTER_IN_IP_ROUTING, priority, -- ds_cstr(&match), ds_cstr(&actions)); -+ ds_cstr(&route_match), ds_cstr(&actions)); - - /* Add per member flow */ -+ struct ds match = DS_EMPTY_INITIALIZER; -+ struct sset visited_ports = SSET_INITIALIZER(&visited_ports); - LIST_FOR_EACH (er, list_node, &eg->route_list) { -- - const struct parsed_route *route_ = er->route; - const struct nbrec_logical_router_static_route *route = route_->route; - /* Find the outgoing port. */ -@@ -7786,6 +7867,15 @@ build_ecmp_route_flow(struct hmap *lflows, struct ovn_datapath *od, - &out_port)) { - continue; - } -+ /* Symmetric ECMP reply is only usable on gateway routers. -+ * It is NOT usable on distributed routers with a gateway port. -+ */ -+ if (smap_get(&od->nbr->options, "chassis") && -+ route_->ecmp_symmetric_reply && sset_add(&visited_ports, -+ out_port->key)) { -+ add_ecmp_symmetric_reply_flows(lflows, od, lrp_addr_s, out_port, -+ route_, &route_match); -+ } - ds_clear(&match); - ds_put_format(&match, REG_ECMP_GROUP_ID" == %"PRIu16" && " - REG_ECMP_MEMBER_ID" == %"PRIu16, -@@ -7806,7 +7896,9 @@ build_ecmp_route_flow(struct hmap *lflows, struct ovn_datapath *od, - ds_cstr(&match), ds_cstr(&actions), - &route->header_); - } -+ sset_destroy(&visited_ports); - ds_destroy(&match); -+ ds_destroy(&route_match); - ds_destroy(&actions); - } - -@@ -9161,6 +9253,7 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, - ovn_lflow_add(lflows, od, S_ROUTER_IN_DNAT, 0, "1", "next;"); - ovn_lflow_add(lflows, od, S_ROUTER_OUT_UNDNAT, 0, "1", "next;"); - ovn_lflow_add(lflows, od, S_ROUTER_OUT_EGR_LOOP, 0, "1", "next;"); -+ ovn_lflow_add(lflows, od, S_ROUTER_IN_ECMP_STATEFUL, 0, "1", "next;"); - - /* Send the IPv6 NS packets to next table. When ovn-controller - * generates IPv6 NS (for the action - nd_ns{}), the injected -diff --git a/ovn-architecture.7.xml b/ovn-architecture.7.xml -index 246cebc19..b1a462933 100644 ---- a/ovn-architecture.7.xml -+++ b/ovn-architecture.7.xml -@@ -1210,11 +1210,12 @@ -
      - Fields that denote the connection tracking zones for routers. These - values only have local significance and are not meaningful between -- chassis. OVN stores the zone information for DNATting in Open vSwitch -+ chassis. OVN stores the zone information for north to south traffic -+ (for DNATting or ECMP symmetric replies) in Open vSwitch - -- extension register number 11 and zone information for SNATing in -- Open vSwitch extension register number 12. -+ extension register number 11 and zone information for south to north -+ traffic (for SNATing) in Open vSwitch extension register number 12. -
      - -
      logical flow flags
      -diff --git a/ovn-nb.ovsschema b/ovn-nb.ovsschema -index da9af7157..0c939b715 100644 ---- a/ovn-nb.ovsschema -+++ b/ovn-nb.ovsschema -@@ -1,7 +1,7 @@ - { - "name": "OVN_Northbound", -- "version": "5.24.0", -- "cksum": "1092394564 25961", -+ "version": "5.25.0", -+ "cksum": "1354137211 26116", - "tables": { - "NB_Global": { - "columns": { -@@ -365,6 +365,9 @@ - "min": 0, "max": 1}}, - "nexthop": {"type": "string"}, - "output_port": {"type": {"key": "string", "min": 0, "max": 1}}, -+ "options": { -+ "type": {"key": "string", "value": "string", -+ "min": 0, "max": "unlimited"}}, - "external_ids": { - "type": {"key": "string", "value": "string", - "min": 0, "max": "unlimited"}}}, -diff --git a/ovn-nb.xml b/ovn-nb.xml -index 02161372a..98d36b270 100644 ---- a/ovn-nb.xml -+++ b/ovn-nb.xml -@@ -2520,6 +2520,22 @@ - - - -+ -+ -+ This column provides general key/value settings. The supported -+ options are described individually below. -+ -+ -+ -+ It true, then new traffic that arrives over this route will have -+ its reply traffic bypass ECMP route selection and will be sent out -+ this route instead. Note that this option overrides any rules set -+ in the table. This option -+ only works on gateway routers (routers that have -+ set). -+ -+ -+ - - - -diff --git a/tests/ovn.at b/tests/ovn.at -index 4c68b77d8..b626bcfcc 100644 ---- a/tests/ovn.at -+++ b/tests/ovn.at -@@ -192,6 +192,8 @@ ct.snat = ct_state[6] - ct.trk = ct_state[5] - ct_label = NXM_NX_CT_LABEL - ct_label.blocked = ct_label[0] -+ct_label.ecmp_reply_eth = ct_label[32..79] -+ct_label.ecmp_reply_port = ct_label[80..95] - ct_mark = NXM_NX_CT_MARK - ct_state = NXM_NX_CT_STATE - ]]) -@@ -16128,7 +16130,7 @@ ovn-sbctl dump-flows lr0 | grep lr_in_arp_resolve | grep "reg0 == 10.0.0.10" \ - # Since the sw0-vir is not claimed by any chassis, eth.dst should be set to - # zero if the ip4.dst is the virtual ip in the router pipeline. - AT_CHECK([cat lflows.txt], [0], [dnl -- table=12(lr_in_arp_resolve ), priority=100 , match=(outport == "lr0-sw0" && reg0 == 10.0.0.10), action=(eth.dst = 00:00:00:00:00:00; next;) -+ table=13(lr_in_arp_resolve ), priority=100 , match=(outport == "lr0-sw0" && reg0 == 10.0.0.10), action=(eth.dst = 00:00:00:00:00:00; next;) - ]) - - ip_to_hex() { -@@ -16179,7 +16181,7 @@ ovn-sbctl dump-flows lr0 | grep lr_in_arp_resolve | grep "reg0 == 10.0.0.10" \ - # There should be an arp resolve flow to resolve the virtual_ip with the - # sw0-p1's MAC. - AT_CHECK([cat lflows.txt], [0], [dnl -- table=12(lr_in_arp_resolve ), priority=100 , match=(outport == "lr0-sw0" && reg0 == 10.0.0.10), action=(eth.dst = 50:54:00:00:00:03; next;) -+ table=13(lr_in_arp_resolve ), priority=100 , match=(outport == "lr0-sw0" && reg0 == 10.0.0.10), action=(eth.dst = 50:54:00:00:00:03; next;) - ]) - - # Forcibly clear virtual_parent. ovn-controller should release the binding -@@ -16220,7 +16222,7 @@ ovn-sbctl dump-flows lr0 | grep lr_in_arp_resolve | grep "reg0 == 10.0.0.10" \ - # There should be an arp resolve flow to resolve the virtual_ip with the - # sw0-p2's MAC. - AT_CHECK([cat lflows.txt], [0], [dnl -- table=12(lr_in_arp_resolve ), priority=100 , match=(outport == "lr0-sw0" && reg0 == 10.0.0.10), action=(eth.dst = 50:54:00:00:00:05; next;) -+ table=13(lr_in_arp_resolve ), priority=100 , match=(outport == "lr0-sw0" && reg0 == 10.0.0.10), action=(eth.dst = 50:54:00:00:00:05; next;) - ]) - - # send the garp from sw0-p2 (in hv2). hv2 should claim sw0-vir -@@ -16243,7 +16245,7 @@ ovn-sbctl dump-flows lr0 | grep lr_in_arp_resolve | grep "reg0 == 10.0.0.10" \ - # There should be an arp resolve flow to resolve the virtual_ip with the - # sw0-p3's MAC. - AT_CHECK([cat lflows.txt], [0], [dnl -- table=12(lr_in_arp_resolve ), priority=100 , match=(outport == "lr0-sw0" && reg0 == 10.0.0.10), action=(eth.dst = 50:54:00:00:00:04; next;) -+ table=13(lr_in_arp_resolve ), priority=100 , match=(outport == "lr0-sw0" && reg0 == 10.0.0.10), action=(eth.dst = 50:54:00:00:00:04; next;) - ]) - - # Now send arp reply from sw0-p1. hv1 should claim sw0-vir -@@ -16264,7 +16266,7 @@ ovn-sbctl dump-flows lr0 | grep lr_in_arp_resolve | grep "reg0 == 10.0.0.10" \ - > lflows.txt - - AT_CHECK([cat lflows.txt], [0], [dnl -- table=12(lr_in_arp_resolve ), priority=100 , match=(outport == "lr0-sw0" && reg0 == 10.0.0.10), action=(eth.dst = 50:54:00:00:00:03; next;) -+ table=13(lr_in_arp_resolve ), priority=100 , match=(outport == "lr0-sw0" && reg0 == 10.0.0.10), action=(eth.dst = 50:54:00:00:00:03; next;) - ]) - - # Delete hv1-vif1 port. hv1 should release sw0-vir -@@ -16282,7 +16284,7 @@ ovn-sbctl dump-flows lr0 | grep lr_in_arp_resolve | grep "reg0 == 10.0.0.10" \ - > lflows.txt - - AT_CHECK([cat lflows.txt], [0], [dnl -- table=12(lr_in_arp_resolve ), priority=100 , match=(outport == "lr0-sw0" && reg0 == 10.0.0.10), action=(eth.dst = 00:00:00:00:00:00; next;) -+ table=13(lr_in_arp_resolve ), priority=100 , match=(outport == "lr0-sw0" && reg0 == 10.0.0.10), action=(eth.dst = 00:00:00:00:00:00; next;) - ]) - - # Now send arp reply from sw0-p2. hv2 should claim sw0-vir -@@ -16303,7 +16305,7 @@ ovn-sbctl dump-flows lr0 | grep lr_in_arp_resolve | grep "reg0 == 10.0.0.10" \ - > lflows.txt - - AT_CHECK([cat lflows.txt], [0], [dnl -- table=12(lr_in_arp_resolve ), priority=100 , match=(outport == "lr0-sw0" && reg0 == 10.0.0.10), action=(eth.dst = 50:54:00:00:00:04; next;) -+ table=13(lr_in_arp_resolve ), priority=100 , match=(outport == "lr0-sw0" && reg0 == 10.0.0.10), action=(eth.dst = 50:54:00:00:00:04; next;) - ]) - - # Delete sw0-p2 logical port -@@ -20483,22 +20485,22 @@ ovn-nbctl set logical_router_policy $pol5 options:pkt_mark=5 - ovn-nbctl --wait=hv sync - - OVS_WAIT_UNTIL([ -- test 1 -eq $(as hv1 ovs-ofctl dump-flows br-int table=19 | \ -+ test 1 -eq $(as hv1 ovs-ofctl dump-flows br-int table=20 | \ - grep "load:0x64->NXM_NX_PKT_MARK" -c) - ]) - - OVS_WAIT_UNTIL([ -- test 1 -eq $(as hv1 ovs-ofctl dump-flows br-int table=19 | \ -+ test 1 -eq $(as hv1 ovs-ofctl dump-flows br-int table=20 | \ - grep "load:0x3->NXM_NX_PKT_MARK" -c) - ]) - - OVS_WAIT_UNTIL([ -- test 1 -eq $(as hv1 ovs-ofctl dump-flows br-int table=19 | \ -+ test 1 -eq $(as hv1 ovs-ofctl dump-flows br-int table=20 | \ - grep "load:0x4->NXM_NX_PKT_MARK" -c) - ]) - - OVS_WAIT_UNTIL([ -- test 1 -eq $(as hv1 ovs-ofctl dump-flows br-int table=19 | \ -+ test 1 -eq $(as hv1 ovs-ofctl dump-flows br-int table=20 | \ - grep "load:0x5->NXM_NX_PKT_MARK" -c) - ]) - -@@ -20589,12 +20591,12 @@ send_ipv4_pkt hv1 hv1-vif1 505400000003 00000000ff01 \ - $(ip_to_hex 10 0 0 3) $(ip_to_hex 172 168 0 120) - - OVS_WAIT_UNTIL([ -- test 1 -eq $(as hv1 ovs-ofctl dump-flows br-int table=19 | \ -+ test 1 -eq $(as hv1 ovs-ofctl dump-flows br-int table=20 | \ - grep "load:0x2->NXM_NX_PKT_MARK" -c) - ]) - - AT_CHECK([ -- test 0 -eq $(as hv1 ovs-ofctl dump-flows br-int table=19 | \ -+ test 0 -eq $(as hv1 ovs-ofctl dump-flows br-int table=20 | \ - grep "load:0x64->NXM_NX_PKT_MARK" -c) - ]) - -@@ -20927,7 +20929,6 @@ AT_CHECK([test "$hv2_offlows" = "$hv2_offlows_mon"]) - OVN_CLEANUP([hv1], [hv2]) - AT_CLEANUP - -- - AT_SETUP([ovn -- controller I-P handling when lrp added last]) - - ovn_start -@@ -21111,6 +21112,129 @@ AT_CHECK([test ! -z $p1_zoneid]) - OVN_CLEANUP([hv1]) - AT_CLEANUP - -+AT_SETUP([ovn -- Symmetric ECMP reply flows]) -+ovn_start -+ -+net_add n1 -+sim_add hv1 -+as hv1 -+ovs-vsctl add-br br-phys -+ovn_attach n1 br-phys 192.168.0.1 -+ -+sim_add hv2 -+as hv2 -+ovs-vsctl add-br br-phys -+ovn_attach n1 br-phys 192.168.0.2 -+ -+# Logical network -+# -+# ls1 \ -+# \ -+# DR -- join -- GW -- ext -+# / -+# ls2 / -+# -+# ls1 and ls2 are internal switches connected to distributed router -+# DR. DR is then connected via a join switch to gateway router GW. -+# GW is then connected to external switch ext. In real life, this -+# would likely have a localnet port, but for the purposes of this test -+# it is unnecessary. -+ -+ovn-nbctl create Logical_Router name=DR -+gw_uuid=$(ovn-nbctl create Logical_Router name=GW) -+ -+ovn-nbctl ls-add ls1 -+ovn-nbctl ls-add ls2 -+ovn-nbctl ls-add join -+ovn-nbctl ls-add ext -+ -+# Connect ls1 to DR -+ovn-nbctl lrp-add DR dr-ls1 00:00:01:01:02:03 10.0.0.1/24 -+ovn-nbctl lsp-add ls1 ls1-dr -- set Logical_Switch_Port ls1-dr \ -+ type=router options:router-port=dr-ls1 addresses='"00:00:01:01:02:03"' -+ -+# Connect ls2 to DR -+ovn-nbctl lrp-add DR dr-ls2 00:00:01:01:02:04 10.0.0.2/24 -+ovn-nbctl lsp-add ls2 ls2-dr -- set Logical_Switch_Port ls2-dr \ -+ type=router options:router-port=dr-ls2 addresses='"00:00:01:01:02:04"' -+ -+# Connect join to DR -+ovn-nbctl lrp-add DR dr-join 00:00:02:01:02:03 20.0.0.1/24 -+ovn-nbctl lsp-add join join-dr -- set Logical_Switch_Port join-dr \ -+ type=router options:router-port=dr-join addresses='"00:00:02:01:02:03"' -+ -+# Connect join to GW -+ovn-nbctl lrp-add GW gw-join 00:00:02:01:02:04 20.0.0.2/24 -+ovn-nbctl lsp-add join join-gw -- set Logical_Switch_Port join-gw \ -+ type=router options:router-port=gw-join addresses='"00:00:02:01:02:04"' -+ -+# Connect ext to GW -+ovn-nbctl lrp-add GW gw-ext 00:00:03:01:02:03 172.16.0.1/16 -+ovn-nbctl lsp-add ext ext-gw -- set Logical_Switch_Port ext-gw \ -+ type=router options:router-port=gw-ext addresses='"00:00:03:01:02:03"' -+ -+ovn-nbctl lr-route-add GW 10.0.0.0/24 20.0.0.1 -+ovn-nbctl --policy="src-ip" lr-route-add DR 10.0.0.0/24 20.0.0.2 -+ -+# Now add some ECMP routes to the GW router. -+ovn-nbctl --ecmp-symmetric-reply --policy="src-ip" lr-route-add GW 10.0.0.0/24 172.16.0.2 -+ovn-nbctl --ecmp-symmetric-reply --policy="src-ip" lr-route-add GW 10.0.0.0/24 172.16.0.3 -+ -+ovn-nbctl --wait=hv sync -+ -+# Ensure ECMP symmetric reply flows are not present on any hypervisor. -+AT_CHECK([ -+ test 0 -eq $(as hv1 ovs-ofctl dump-flows br-int table=15 | \ -+ grep "priority=100" | \ -+ grep "ct(commit,zone=NXM_NX_REG11\\[[0..15\\]],exec(move:NXM_OF_ETH_SRC\\[[\\]]->NXM_NX_CT_LABEL\\[[32..79\\]],load:0x[[0-9]]->NXM_NX_CT_LABEL\\[[80..95\\]]))" -c) -+]) -+AT_CHECK([ -+ test 0 -eq $(as hv1 ovs-ofctl dump-flows br-int table=21 | \ -+ grep "priority=200" | \ -+ grep "actions=move:NXM_NX_CT_LABEL\\[[32..79\\]]->NXM_OF_ETH_DST\\[[\\]]" -c) -+]) -+ -+AT_CHECK([ -+ test 0 -eq $(as hv2 ovs-ofctl dump-flows br-int table=15 | \ -+ grep "priority=100" | \ -+ grep "ct(commit,zone=NXM_NX_REG11\\[[0..15\\]],exec(move:NXM_OF_ETH_SRC\\[[\\]]->NXM_NX_CT_LABEL\\[[32..79\\]],load:0x[[0-9]]->NXM_NX_CT_LABEL\\[[80..95\\]]))" -c) -+]) -+AT_CHECK([ -+ test 0 -eq $(as hv2 ovs-ofctl dump-flows br-int table=21 | \ -+ grep "priority=200" | \ -+ grep "actions=move:NXM_NX_CT_LABEL\\[[32..79\\]]->NXM_OF_ETH_DST\\[[\\]]" -c) -+]) -+ -+# Now make GW a gateway router on hv1 -+ovn-nbctl set Logical_Router $gw_uuid options:chassis=hv1 -+ovn-nbctl --wait=hv sync -+ -+# And ensure that ECMP symmetric reply flows are present only on hv1 -+AT_CHECK([ -+ test 1 -eq $(as hv1 ovs-ofctl dump-flows br-int table=15 | \ -+ grep "priority=100" | \ -+ grep "ct(commit,zone=NXM_NX_REG11\\[[0..15\\]],exec(move:NXM_OF_ETH_SRC\\[[\\]]->NXM_NX_CT_LABEL\\[[32..79\\]],load:0x[[0-9]]->NXM_NX_CT_LABEL\\[[80..95\\]]))" -c) -+]) -+AT_CHECK([ -+ test 1 -eq $(as hv1 ovs-ofctl dump-flows br-int table=21 | \ -+ grep "priority=200" | \ -+ grep "actions=move:NXM_NX_CT_LABEL\\[[32..79\\]]->NXM_OF_ETH_DST\\[[\\]]" -c) -+]) -+ -+AT_CHECK([ -+ test 0 -eq $(as hv2 ovs-ofctl dump-flows br-int table=15 | \ -+ grep "priority=100" | \ -+ grep "ct(commit,zone=NXM_NX_REG11\\[[0..15\\]],exec(move:NXM_OF_ETH_SRC\\[[\\]]->NXM_NX_CT_LABEL\\[[32..79\\]],load:0x[[0-9]]->NXM_NX_CT_LABEL\\[[80..95\\]]))" -c) -+]) -+AT_CHECK([ -+ test 0 -eq $(as hv2 ovs-ofctl dump-flows br-int table=21 | \ -+ grep "priority=200" | \ -+ grep "actions=move:NXM_NX_CT_LABEL\\[[32..79\\]]->NXM_OF_ETH_DST\\[[\\]]" -c) -+]) -+ -+OVN_CLEANUP([hv1], [hv2]) -+AT_CLEANUP -+ - # Test option:dynamic_neigh_routers. No static neighbor flows when enabled, and - # traffic should still work, with the help of dynamic mac_bindings. - AT_SETUP([ovn -- Dynamic neighbor between LRs]) -diff --git a/tests/system-ovn.at b/tests/system-ovn.at -index 94e3964e5..bce097b17 100644 ---- a/tests/system-ovn.at -+++ b/tests/system-ovn.at -@@ -4555,6 +4555,149 @@ NS_CHECK_EXEC([sw0-p1-f], [ping -q -c 3 -i 0.3 -w 2 10.0.0.5 | FORMAT_PING], \ - 3 packets transmitted, 3 received, 0% packet loss, time 0ms - ]) - -+OVS_APP_EXIT_AND_WAIT([ovn-controller]) -+ -+as ovn-sb -+OVS_APP_EXIT_AND_WAIT([ovsdb-server]) -+ -+as ovn-nb -+OVS_APP_EXIT_AND_WAIT([ovsdb-server]) -+ -+as northd -+OVS_APP_EXIT_AND_WAIT([ovn-northd]) -+ -+as -+OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d -+/connection dropped.*/d"]) -+ -+AT_CLEANUP -+ -+AT_SETUP([ovn -- ECMP symmetric reply]) -+AT_KEYWORDS([ecmp]) -+ -+CHECK_CONNTRACK() -+ovn_start -+ -+OVS_TRAFFIC_VSWITCHD_START() -+ADD_BR([br-int]) -+ -+# Set external-ids in br-int needed for ovn-controller -+ovs-vsctl \ -+ -- set Open_vSwitch . external-ids:system-id=hv1 \ -+ -- set Open_vSwitch . external-ids:ovn-remote=unix:$ovs_base/ovn-sb/ovn-sb.sock \ -+ -- set Open_vSwitch . external-ids:ovn-encap-type=geneve \ -+ -- set Open_vSwitch . external-ids:ovn-encap-ip=169.0.0.1 \ -+ -- set bridge br-int fail-mode=secure other-config:disable-in-band=true -+ -+# Start ovn-controller -+start_daemon ovn-controller -+ -+# Logical network: -+# Alice is connected to gateway router R1. R1 is connected to two "external" -+# routers, R2 and R3 via an "ext" switch. -+# Bob is connected to both R2 and R3. R1 contains two ECMP routes, one through R2 -+# and one through R3, to Bob. -+# -+# alice -- R1 -- ext ---- R2 -+# | \ -+# | bob -+# | / -+# + ----- R3 -+# -+# For this test, Bob sends request traffic through R2 to Alice. We want to ensure that -+# all response traffic from Alice is routed through R2 as well. -+ -+ovn-nbctl create Logical_Router name=R1 options:chassis=hv1 -+ovn-nbctl create Logical_Router name=R2 -+ovn-nbctl create Logical_Router name=R3 -+ -+ovn-nbctl ls-add alice -+ovn-nbctl ls-add bob -+ovn-nbctl ls-add ext -+ -+# connect alice to R1 -+ovn-nbctl lrp-add R1 alice 00:00:01:01:02:03 10.0.0.1/24 -+ovn-nbctl lsp-add alice rp-alice -- set Logical_Switch_Port rp-alice \ -+ type=router options:router-port=alice addresses='"00:00:01:01:02:03"' -+ -+# connect bob to R2 -+ovn-nbctl lrp-add R2 R2_bob 00:00:02:01:02:03 172.16.0.2/16 -+ovn-nbctl lsp-add bob rp2-bob -- set Logical_Switch_Port rp2-bob \ -+ type=router options:router-port=R2_bob addresses='"00:00:02:01:02:03"' -+ -+# connect bob to R3 -+ovn-nbctl lrp-add R3 R3_bob 00:00:02:01:02:04 172.16.0.3/16 -+ovn-nbctl lsp-add bob rp3-bob -- set Logical_Switch_Port rp3-bob \ -+ type=router options:router-port=R3_bob addresses='"00:00:02:01:02:04"' -+ -+# Connect R1 to ext -+ovn-nbctl lrp-add R1 R1_ext 00:00:04:01:02:03 20.0.0.1/24 -+ovn-nbctl lsp-add ext r1-ext -- set Logical_Switch_Port r1-ext \ -+ type=router options:router-port=R1_ext addresses='"00:00:04:01:02:03"' -+ -+# Connect R2 to ext -+ovn-nbctl lrp-add R2 R2_ext 00:00:04:01:02:04 20.0.0.2/24 -+ovn-nbctl lsp-add ext r2-ext -- set Logical_Switch_Port r2-ext \ -+ type=router options:router-port=R2_ext addresses='"00:00:04:01:02:04"' -+ -+# Connect R3 to ext -+ovn-nbctl lrp-add R3 R3_ext 00:00:04:01:02:05 20.0.0.3/24 -+ovn-nbctl lsp-add ext r3-ext -- set Logical_Switch_Port r3-ext \ -+ type=router options:router-port=R3_ext addresses='"00:00:04:01:02:05"' -+ -+# Install ECMP routes for alice. -+ovn-nbctl --ecmp-symmetric-reply --policy="src-ip" lr-route-add R1 10.0.0.0/24 20.0.0.2 -+ovn-nbctl --ecmp-symmetric-reply --policy="src-ip" lr-route-add R1 10.0.0.0/24 20.0.0.3 -+ -+# Static Routes -+ovn-nbctl lr-route-add R2 10.0.0.0/24 20.0.0.1 -+ovn-nbctl lr-route-add R3 10.0.0.0/24 20.0.0.1 -+ -+# Logical port 'alice1' in switch 'alice'. -+ADD_NAMESPACES(alice1) -+ADD_VETH(alice1, alice1, br-int, "10.0.0.2/24", "f0:00:00:01:02:04", \ -+ "10.0.0.1") -+ovn-nbctl lsp-add alice alice1 \ -+-- lsp-set-addresses alice1 "f0:00:00:01:02:04 10.0.0.2" -+ -+# Logical port 'bob1' in switch 'bob'. -+ADD_NAMESPACES(bob1) -+ADD_VETH(bob1, bob1, br-int, "172.16.0.1/16", "f0:00:00:01:02:06", \ -+ "172.16.0.2") -+ovn-nbctl lsp-add bob bob1 \ -+-- lsp-set-addresses bob1 "f0:00:00:01:02:06 172.16.0.1" -+ -+# Ensure ovn-controller is caught up -+ovn-nbctl --wait=hv sync -+ -+on_exit 'ovs-ofctl dump-flows br-int' -+ -+# 'bob1' should be able to ping 'alice1' directly. -+NS_CHECK_EXEC([bob1], [ping -q -c 20 -i 0.3 -w 15 10.0.0.2 | FORMAT_PING], \ -+[0], [dnl -+20 packets transmitted, 20 received, 0% packet loss, time 0ms -+]) -+ -+# Ensure conntrack entry is present. We should not try to predict -+# the tunnel key for the output port, so we strip it from the labels -+# and just ensure that the known ethernet address is present. -+AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(172.16.0.1) | \ -+sed -e 's/zone=[[0-9]]*/zone=/' | -+sed -e 's/labels=0x[[0-9a-f]]*00000401020400000000/labels=0x00000401020400000000/'], [0], [dnl -+icmp,orig=(src=172.16.0.1,dst=10.0.0.2,id=,type=8,code=0),reply=(src=10.0.0.2,dst=172.16.0.1,id=,type=0,code=0),zone=,labels=0x00000401020400000000 -+]) -+ -+# Ensure datapaths show conntrack states as expected -+# Like with conntrack entries, we shouldn't try to predict -+# port binding tunnel keys. So omit them from expected labels. -+AT_CHECK([ovs-appctl dpctl/dump-flows | grep 'ct_state(+new-est-rpl+trk).*ct(.*label=0x.*00000401020400000000/0xffffffffffffffff00000000)' -c], [0], [dnl -+1 -+]) -+AT_CHECK([ovs-appctl dpctl/dump-flows | grep 'ct_state(-new+est+rpl+trk).*ct_label(0x.*00000401020400000000/0xffffffffffffffff00000000)' -c], [0], [dnl -+1 -+]) -+ -+ovs-ofctl dump-flows br-int - - OVS_APP_EXIT_AND_WAIT([ovn-controller]) - -diff --git a/utilities/ovn-nbctl.8.xml b/utilities/ovn-nbctl.8.xml -index de86b70e6..18bf90e08 100644 ---- a/utilities/ovn-nbctl.8.xml -+++ b/utilities/ovn-nbctl.8.xml -@@ -658,7 +658,8 @@ - -
      -
      [--may-exist] [--policy=POLICY] -- [--ecmp] lr-route-add router -+ [--ecmp] [--ecmp-symmetric-reply] -+ lr-route-add router - prefix nexthop [port]
      -
      -

      -@@ -680,15 +681,31 @@ - specified, the default is "dst-ip". -

      - -+

      -+ The --ecmp option allows for multiple routes with the -+ same prefix POLICY but different -+ nexthop and port to be added. -+

      -+ -+

      -+ The --ecmp-symmetric-reply option makes it so that -+ traffic that arrives over an ECMP route will have its reply traffic -+ sent out over that same route. Setting -+ --ecmp-symmetric-reply implies --ecmp so -+ it is not necessary to set both. -+

      -+ -

      - It is an error if a route with prefix and -- POLICY already exists, unless --may-exist or -- --ecmp is specified. If --may-exist is -- specified but not --ecmp, the existed route will be -- updated with the new nexthop and port. If --ecmp is -+ POLICY already exists, unless --may-exist, -+ --ecmp, or --ecmp-symmetric-reply is -+ specified. If --may-exist is specified but not -+ --ecmp or --ecmp-symmetric-reply, the -+ existed route will be updated with the new nexthop and port. If -+ --ecmp or --ecmp-symmetric-reply is - specified, a new route will be added, regardless of the existed -- route, which is useful when adding ECMP routes, i.e. routes with same -- POLICY and prefix but different -+ route., which is useful when adding ECMP routes, i.e. routes with -+ same POLICY and prefix but different - nexthop and port. -

      -
      -diff --git a/utilities/ovn-nbctl.c b/utilities/ovn-nbctl.c -index 0079ad5a6..e6d8dbe63 100644 ---- a/utilities/ovn-nbctl.c -+++ b/utilities/ovn-nbctl.c -@@ -687,7 +687,8 @@ Logical router port commands:\n\ - ('overlay' or 'bridged')\n\ - \n\ - Route commands:\n\ -- [--policy=POLICY] [--ecmp] lr-route-add ROUTER PREFIX NEXTHOP [PORT]\n\ -+ [--policy=POLICY] [--ecmp] [--ecmp-symmetric-reply] lr-route-add ROUTER \n\ -+ PREFIX NEXTHOP [PORT]\n\ - add a route to ROUTER\n\ - [--policy=POLICY] lr-route-del ROUTER [PREFIX [NEXTHOP [PORT]]]\n\ - remove routes from ROUTER\n\ -@@ -3855,7 +3856,10 @@ nbctl_lr_route_add(struct ctl_context *ctx) - } - - bool may_exist = shash_find(&ctx->options, "--may-exist") != NULL; -- bool ecmp = shash_find(&ctx->options, "--ecmp") != NULL; -+ bool ecmp_symmetric_reply = shash_find(&ctx->options, -+ "--ecmp-symmetric-reply") != NULL; -+ bool ecmp = shash_find(&ctx->options, "--ecmp") != NULL || -+ ecmp_symmetric_reply; - if (!ecmp) { - for (int i = 0; i < lr->n_static_routes; i++) { - const struct nbrec_logical_router_static_route *route -@@ -3920,6 +3924,13 @@ nbctl_lr_route_add(struct ctl_context *ctx) - nbrec_logical_router_static_route_set_policy(route, policy); - } - -+ if (ecmp_symmetric_reply) { -+ const struct smap options = SMAP_CONST1(&options, -+ "ecmp_symmetric_reply", -+ "true"); -+ nbrec_logical_router_static_route_set_options(route, &options); -+ } -+ - nbrec_logical_router_verify_static_routes(lr); - struct nbrec_logical_router_static_route **new_routes - = xmalloc(sizeof *new_routes * (lr->n_static_routes + 1)); -@@ -6361,7 +6372,8 @@ static const struct ctl_command_syntax nbctl_commands[] = { - - /* logical router route commands. */ - { "lr-route-add", 3, 4, "ROUTER PREFIX NEXTHOP [PORT]", NULL, -- nbctl_lr_route_add, NULL, "--may-exist,--ecmp,--policy=", RW }, -+ nbctl_lr_route_add, NULL, "--may-exist,--ecmp,--ecmp-symmetric-reply," -+ "--policy=", RW }, - { "lr-route-del", 1, 4, "ROUTER [PREFIX [NEXTHOP [PORT]]]", NULL, - nbctl_lr_route_del, NULL, "--if-exists,--policy=", RW }, - { "lr-route-list", 1, 1, "ROUTER", NULL, nbctl_lr_route_list, NULL, --- -2.26.2 - diff --git a/SOURCES/0014-expr.c-Fix-argument-type-of-expr_write_scope.patch b/SOURCES/0014-expr.c-Fix-argument-type-of-expr_write_scope.patch deleted file mode 100644 index 5e9b04a..0000000 --- a/SOURCES/0014-expr.c-Fix-argument-type-of-expr_write_scope.patch +++ /dev/null @@ -1,39 +0,0 @@ -From 677e51935f9030a779956cf3c6fb0f4d2fb19c10 Mon Sep 17 00:00:00 2001 -From: Han Zhou -Date: Wed, 29 Jul 2020 18:36:38 -0700 -Subject: [PATCH 14/22] expr.c: Fix argument type of expr_write_scope(). - -There is compile error introduced by the commit 2054d01247. -*** -../lib/expr.c:3328:6: error: symbol 'expr_type_check' redeclared with different -type (originally declared at ../include/ovn/expr.h:481) - incompatible argument -4 (different signedness) -Makefile:1971: recipe for target 'lib/expr.lo' failed -*** - -Fixes: 2054d01247 ("Add expression writeability scopes.") -Acked-by: Ankur Sharma -Signed-off-by: Han Zhou -(cherry picked from upstream commit c64ed2a9bb175539ed9493a08e31cc29d10aa8df) - -Change-Id: I1de528787319b21a50bdcebbe60894026b32ab06 ---- - lib/expr.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/lib/expr.c b/lib/expr.c -index c07e7dd4d..6fb96757a 100644 ---- a/lib/expr.c -+++ b/lib/expr.c -@@ -3327,7 +3327,7 @@ expr_evaluate(const struct expr *e, const struct flow *uflow, - * must free(). */ - char * OVS_WARN_UNUSED_RESULT - expr_type_check(const struct expr_field *f, int n_bits, bool rw, -- uint32_t write_scope) -+ enum expr_write_scope write_scope) - { - if (n_bits != f->n_bits) { - if (n_bits && f->n_bits) { --- -2.26.2 - diff --git a/SOURCES/0015-Allow-bare-ct_commits-when-no-nested-actions-are-req.patch b/SOURCES/0015-Allow-bare-ct_commits-when-no-nested-actions-are-req.patch deleted file mode 100644 index e0e34c9..0000000 --- a/SOURCES/0015-Allow-bare-ct_commits-when-no-nested-actions-are-req.patch +++ /dev/null @@ -1,75 +0,0 @@ -From bc0e773fa3620fa3c4fef817d3b2256542be6b11 Mon Sep 17 00:00:00 2001 -From: Mark Michelson -Date: Thu, 6 Aug 2020 10:48:12 -0400 -Subject: [PATCH 15/22] Allow bare ct_commits when no nested actions are - required. - -In the fixes commit below, ct_commit was changed to use nested actions. -This requires that curly braces be present for all ct_commits. When -adjusting ovn-northd, some ct_commits were not updated to have them. -This commit changes the behavior of the ct_commit action not to require -curly braces if there are no nested actions required. - -Fixes: 6cfb44a76c61("Used nested actions in ct_commit") -Signed-off-by: Mark Michelson ---- - lib/actions.c | 20 ++++++++++++++++---- - tests/ovn.at | 5 ++++- - 2 files changed, 20 insertions(+), 5 deletions(-) - -diff --git a/lib/actions.c b/lib/actions.c -index 79ac79a95..245486b0a 100644 ---- a/lib/actions.c -+++ b/lib/actions.c -@@ -630,15 +630,27 @@ ovnact_ct_next_free(struct ovnact_ct_next *a OVS_UNUSED) - static void - parse_CT_COMMIT(struct action_context *ctx) - { -- -- parse_nested_action(ctx, OVNACT_CT_COMMIT, "ip", -- WR_CT_COMMIT); -+ if (ctx->lexer->token.type == LEX_T_LCURLY) { -+ parse_nested_action(ctx, OVNACT_CT_COMMIT, "ip", -+ WR_CT_COMMIT); -+ } else { -+ /* Add an empty nested action to allow for "ct_commit;" syntax */ -+ add_prerequisite(ctx, "ip"); -+ struct ovnact_nest *on = ovnact_put(ctx->ovnacts, OVNACT_CT_COMMIT, -+ OVNACT_ALIGN(sizeof *on)); -+ on->nested_len = 0; -+ on->nested = NULL; -+ } - } - - static void - format_CT_COMMIT(const struct ovnact_nest *on, struct ds *s) - { -- format_nested_action(on, "ct_commit", s); -+ if (on->nested_len) { -+ format_nested_action(on, "ct_commit", s); -+ } else { -+ ds_put_cstr(s, "ct_commit;"); -+ } - } - - static void -diff --git a/tests/ovn.at b/tests/ovn.at -index b626bcfcc..2651b3eac 100644 ---- a/tests/ovn.at -+++ b/tests/ovn.at -@@ -1047,8 +1047,11 @@ ct_next; - has prereqs ip - - # ct_commit -+ct_commit; -+ encodes as ct(commit,zone=NXM_NX_REG13[0..15]) -+ has prereqs ip - ct_commit { }; -- formats as ct_commit { drop; }; -+ formats as ct_commit; - encodes as ct(commit,zone=NXM_NX_REG13[0..15]) - has prereqs ip - ct_commit { ct_mark=1; }; --- -2.26.2 - diff --git a/SOURCES/0016-Allow-force_snat-options-to-work-for-dual-stack-rout.patch b/SOURCES/0016-Allow-force_snat-options-to-work-for-dual-stack-rout.patch deleted file mode 100644 index f9cf520..0000000 --- a/SOURCES/0016-Allow-force_snat-options-to-work-for-dual-stack-rout.patch +++ /dev/null @@ -1,887 +0,0 @@ -From 3aeb8b73e0947010945aea8bf8fa6df1b7a558a7 Mon Sep 17 00:00:00 2001 -From: Mark Michelson -Date: Thu, 6 Aug 2020 15:04:15 -0400 -Subject: [PATCH 16/22] Allow force_snat options to work for dual-stack - routers. - -The lb_force_snat and dnat_force_snat options could accept only a single -IP address. For routers that only route traffic of a single IP address -family, this is fine. However, if a router routes both IPv4 and IPv6 -traffic, then this limitation is a problem. - -This patch addresses this problem by allowing for these options to -specify both an IPv4 and IPv6 address. - -Signed-off-by: Mark Michelson -Acked-by: Dumitru Ceara -Signed-off-by: Numan Siddique - -(cherry-picked from upstream master commit 474821c55608cbad5bdb8deee468827ab489c02b) - -Change-Id: I42266af72622d1f15ec94d68f106954cc49979bd ---- - northd/ovn-northd.c | 187 ++++++++------- - ovn-nb.xml | 24 +- - tests/system-ovn.at | 541 ++++++++++++++++++++++++++++++++++++++++++++ - 3 files changed, 657 insertions(+), 95 deletions(-) - -diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c -index cb8e25bdf..1f5433d9d 100644 ---- a/northd/ovn-northd.c -+++ b/northd/ovn-northd.c -@@ -8013,44 +8013,37 @@ op_put_v6_networks(struct ds *ds, const struct ovn_port *op) - ds_put_cstr(ds, "}"); - } - --static const char * -+static bool - get_force_snat_ip(struct ovn_datapath *od, const char *key_type, -- struct v46_ip *ip) -+ struct lport_addresses *laddrs) - { - char *key = xasprintf("%s_force_snat_ip", key_type); -- const char *ip_address = smap_get(&od->nbr->options, key); -+ const char *addresses = smap_get(&od->nbr->options, key); - free(key); - -- if (ip_address) { -- ovs_be32 mask; -- ip->family = AF_INET; -- char *error = ip_parse_masked(ip_address, &ip->ipv4, &mask); -- if (error || mask != OVS_BE32_MAX) { -- free(error); -- struct in6_addr mask_v6, v6_exact = IN6ADDR_EXACT_INIT; -- ip->family = AF_INET6; -- error = ipv6_parse_masked(ip_address, &ip->ipv6, &mask_v6); -- if (error || memcmp(&mask_v6, &v6_exact, sizeof(mask_v6))) { -- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); -- VLOG_WARN_RL(&rl, "bad ip %s in options of router "UUID_FMT"", -- ip_address, UUID_ARGS(&od->key)); -- memset(ip, 0, sizeof *ip); -- ip->family = AF_UNSPEC; -- return NULL; -- } -- } -- return ip_address; -+ if (!addresses) { -+ return false; - } - -- memset(ip, 0, sizeof *ip); -- ip->family = AF_UNSPEC; -- return NULL; -+ if (!extract_ip_addresses(addresses, laddrs) || -+ laddrs->n_ipv4_addrs > 1 || -+ laddrs->n_ipv6_addrs > 1 || -+ (laddrs->n_ipv4_addrs && laddrs->ipv4_addrs[0].plen != 32) || -+ (laddrs->n_ipv6_addrs && laddrs->ipv6_addrs[0].plen != 128)) { -+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); -+ VLOG_WARN_RL(&rl, "bad ip %s in options of router "UUID_FMT"", -+ addresses, UUID_ARGS(&od->key)); -+ destroy_lport_addresses(laddrs); -+ return false; -+ } -+ -+ return true; - } - - static void - add_router_lb_flow(struct hmap *lflows, struct ovn_datapath *od, - struct ds *match, struct ds *actions, int priority, -- const char *lb_force_snat_ip, struct lb_vip *lb_vip, -+ bool lb_force_snat_ip, struct lb_vip *lb_vip, - const char *proto, struct nbrec_load_balancer *lb, - struct shash *meter_groups, struct sset *nat_entries) - { -@@ -8371,6 +8364,32 @@ build_lrouter_nd_flow(struct ovn_datapath *od, struct ovn_port *op, - ds_destroy(&actions); - } - -+static void -+build_lrouter_force_snat_flows(struct hmap *lflows, struct ovn_datapath *od, -+ const char *ip_version, const char *ip_addr, -+ const char *context) -+{ -+ struct ds match = DS_EMPTY_INITIALIZER; -+ struct ds actions = DS_EMPTY_INITIALIZER; -+ ds_put_format(&match, "ip%s && ip%s.dst == %s", -+ ip_version, ip_version, ip_addr); -+ ovn_lflow_add(lflows, od, S_ROUTER_IN_UNSNAT, 110, -+ ds_cstr(&match), "ct_snat;"); -+ -+ /* Higher priority rules to force SNAT with the IP addresses -+ * configured in the Gateway router. This only takes effect -+ * when the packet has already been DNATed or load balanced once. */ -+ ds_clear(&match); -+ ds_put_format(&match, "flags.force_snat_for_%s == 1 && ip%s", -+ context, ip_version); -+ ds_put_format(&actions, "ct_snat(%s);", ip_addr); -+ ovn_lflow_add(lflows, od, S_ROUTER_OUT_SNAT, 100, -+ ds_cstr(&match), ds_cstr(&actions)); -+ -+ ds_destroy(&match); -+ ds_destroy(&actions); -+} -+ - static void - build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, - struct hmap *lflows, struct shash *meter_groups, -@@ -8892,24 +8911,37 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, - } - } - -- /* A gateway router can have 2 SNAT IP addresses to force DNATed and -+ /* A gateway router can have 4 SNAT IP addresses to force DNATed and - * LBed traffic respectively to be SNATed. In addition, there can be - * a number of SNAT rules in the NAT table. */ - struct v46_ip *snat_ips = xmalloc(sizeof *snat_ips -- * (op->od->nbr->n_nat + 2)); -+ * (op->od->nbr->n_nat + 4)); - size_t n_snat_ips = 0; -+ struct lport_addresses snat_addrs; - -- struct v46_ip snat_ip; -- const char *dnat_force_snat_ip = get_force_snat_ip(op->od, "dnat", -- &snat_ip); -- if (dnat_force_snat_ip) { -- snat_ips[n_snat_ips++] = snat_ip; -+ if (get_force_snat_ip(op->od, "dnat", &snat_addrs)) { -+ if (snat_addrs.n_ipv4_addrs) { -+ snat_ips[n_snat_ips].family = AF_INET; -+ snat_ips[n_snat_ips++].ipv4 = snat_addrs.ipv4_addrs[0].addr; -+ } -+ if (snat_addrs.n_ipv6_addrs) { -+ snat_ips[n_snat_ips].family = AF_INET6; -+ snat_ips[n_snat_ips++].ipv6 = snat_addrs.ipv6_addrs[0].addr; -+ } -+ destroy_lport_addresses(&snat_addrs); - } - -- const char *lb_force_snat_ip = get_force_snat_ip(op->od, "lb", -- &snat_ip); -- if (lb_force_snat_ip) { -- snat_ips[n_snat_ips++] = snat_ip; -+ memset(&snat_addrs, 0, sizeof(snat_addrs)); -+ if (get_force_snat_ip(op->od, "lb", &snat_addrs)) { -+ if (snat_addrs.n_ipv4_addrs) { -+ snat_ips[n_snat_ips].family = AF_INET; -+ snat_ips[n_snat_ips++].ipv4 = snat_addrs.ipv4_addrs[0].addr; -+ } -+ if (snat_addrs.n_ipv6_addrs) { -+ snat_ips[n_snat_ips].family = AF_INET6; -+ snat_ips[n_snat_ips++].ipv6 = snat_addrs.ipv6_addrs[0].addr; -+ } -+ destroy_lport_addresses(&snat_addrs); - } - - for (size_t i = 0; i < op->od->nbr->n_nat; i++) { -@@ -9269,11 +9301,12 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, - - struct sset nat_entries = SSET_INITIALIZER(&nat_entries); - -- struct v46_ip snat_ip, lb_snat_ip; -- const char *dnat_force_snat_ip = get_force_snat_ip(od, "dnat", -- &snat_ip); -- const char *lb_force_snat_ip = get_force_snat_ip(od, "lb", -- &lb_snat_ip); -+ struct lport_addresses dnat_force_snat_addrs; -+ struct lport_addresses lb_force_snat_addrs; -+ bool dnat_force_snat_ip = get_force_snat_ip(od, "dnat", -+ &dnat_force_snat_addrs); -+ bool lb_force_snat_ip = get_force_snat_ip(od, "lb", -+ &lb_force_snat_addrs); - - for (int i = 0; i < od->nbr->n_nat; i++) { - const struct nbrec_nat *nat; -@@ -9739,49 +9772,28 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, - } - - /* Handle force SNAT options set in the gateway router. */ -- if (dnat_force_snat_ip && !od->l3dgw_port) { -- /* If a packet with destination IP address as that of the -- * gateway router (as set in options:dnat_force_snat_ip) is seen, -- * UNSNAT it. */ -- ds_clear(&match); -- ds_put_format(&match, "ip && ip%s.dst == %s", -- snat_ip.family == AF_INET ? "4" : "6", -- dnat_force_snat_ip); -- ovn_lflow_add(lflows, od, S_ROUTER_IN_UNSNAT, 110, -- ds_cstr(&match), "ct_snat;"); -- -- /* Higher priority rules to force SNAT with the IP addresses -- * configured in the Gateway router. This only takes effect -- * when the packet has already been DNATed once. */ -- ds_clear(&match); -- ds_put_format(&match, "flags.force_snat_for_dnat == 1 && ip"); -- ds_clear(&actions); -- ds_put_format(&actions, "ct_snat(%s);", dnat_force_snat_ip); -- ovn_lflow_add(lflows, od, S_ROUTER_OUT_SNAT, 100, -- ds_cstr(&match), ds_cstr(&actions)); -- } -- if (lb_force_snat_ip && !od->l3dgw_port) { -- /* If a packet with destination IP address as that of the -- * gateway router (as set in options:lb_force_snat_ip) is seen, -- * UNSNAT it. */ -- ds_clear(&match); -- ds_put_format(&match, "ip && ip%s.dst == %s", -- lb_snat_ip.family == AF_INET ? "4" : "6", -- lb_force_snat_ip); -- ovn_lflow_add(lflows, od, S_ROUTER_IN_UNSNAT, 100, -- ds_cstr(&match), "ct_snat;"); -- -- /* Load balanced traffic will have flags.force_snat_for_lb set. -- * Force SNAT it. */ -- ds_clear(&match); -- ds_put_format(&match, "flags.force_snat_for_lb == 1 && ip"); -- ds_clear(&actions); -- ds_put_format(&actions, "ct_snat(%s);", lb_force_snat_ip); -- ovn_lflow_add(lflows, od, S_ROUTER_OUT_SNAT, 100, -- ds_cstr(&match), ds_cstr(&actions)); -- } -- - if (!od->l3dgw_port) { -+ if (dnat_force_snat_ip) { -+ if (dnat_force_snat_addrs.n_ipv4_addrs) { -+ build_lrouter_force_snat_flows(lflows, od, "4", -+ dnat_force_snat_addrs.ipv4_addrs[0].addr_s, "dnat"); -+ } -+ if (dnat_force_snat_addrs.n_ipv6_addrs) { -+ build_lrouter_force_snat_flows(lflows, od, "6", -+ dnat_force_snat_addrs.ipv6_addrs[0].addr_s, "dnat"); -+ } -+ } -+ if (lb_force_snat_ip) { -+ if (lb_force_snat_addrs.n_ipv4_addrs) { -+ build_lrouter_force_snat_flows(lflows, od, "4", -+ lb_force_snat_addrs.ipv4_addrs[0].addr_s, "lb"); -+ } -+ if (lb_force_snat_addrs.n_ipv6_addrs) { -+ build_lrouter_force_snat_flows(lflows, od, "6", -+ lb_force_snat_addrs.ipv6_addrs[0].addr_s, "lb"); -+ } -+ } -+ - /* For gateway router, re-circulate every packet through - * the DNAT zone. This helps with the following. - * -@@ -9795,6 +9807,13 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, - "ip", "flags.loopback = 1; ct_dnat;"); - } - -+ if (dnat_force_snat_ip) { -+ destroy_lport_addresses(&dnat_force_snat_addrs); -+ } -+ if (lb_force_snat_ip) { -+ destroy_lport_addresses(&lb_force_snat_addrs); -+ } -+ - /* Load balancing and packet defrag are only valid on - * Gateway routers or router with gateway port. */ - if (!smap_get(&od->nbr->options, "chassis") && !od->l3dgw_port) { -diff --git a/ovn-nb.xml b/ovn-nb.xml -index 98d36b270..5f4291559 100644 ---- a/ovn-nb.xml -+++ b/ovn-nb.xml -@@ -1816,27 +1816,29 @@ - - -

      -- If set, indicates the IP address to use to force SNAT a packet -- that has already been DNATed in the gateway router. When multiple -- gateway routers are configured, a packet can potentially enter any -- of the gateway router, get DNATted and eventually reach the logical -- switch port. For the return traffic to go back to the same gateway -- router (for unDNATing), the packet needs a SNAT in the first place. -- This can be achieved by setting the above option with a gateway -- specific IP address. -+ If set, indicates a set of IP addresses to use to force SNAT a -+ packet that has already been DNATed in the gateway router. When -+ multiple gateway routers are configured, a packet can potentially -+ enter any of the gateway router, get DNATted and eventually reach the -+ logical switch port. For the return traffic to go back to the same -+ gateway router (for unDNATing), the packet needs a SNAT in the first -+ place. This can be achieved by setting the above option with a -+ gateway specific set of IP addresses. This option may have exactly -+ one IPv4 and/or one IPv6 address on it, separated by a a space. -

      -
      - -

      -- If set, indicates the IP address to use to force SNAT a packet -+ If set, indicates a set of IP addresses to use to force SNAT a packet - that has already been load-balanced in the gateway router. When - multiple gateway routers are configured, a packet can potentially - enter any of the gateway routers, get DNATted as part of the load- - balancing and eventually reach the logical switch port. - For the return traffic to go back to the same gateway router (for - unDNATing), the packet needs a SNAT in the first place. This can be -- achieved by setting the above option with a gateway specific IP -- address. -+ achieved by setting the above option with a gateway specific set of -+ IP addresses. This option may have exactly one IPv4 and/or one IPv6 -+ address on it, separated by a space character. -

      -
      - -diff --git a/tests/system-ovn.at b/tests/system-ovn.at -index bce097b17..0d478b4aa 100644 ---- a/tests/system-ovn.at -+++ b/tests/system-ovn.at -@@ -1026,6 +1026,323 @@ OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d - /connection dropped.*/d"]) - AT_CLEANUP - -+AT_SETUP([ovn -- multiple gateway routers, SNAT and DNAT - Dual Stack]) -+AT_KEYWORDS([ovnnat]) -+ -+CHECK_CONNTRACK() -+CHECK_CONNTRACK_NAT() -+ovn_start -+OVS_TRAFFIC_VSWITCHD_START() -+ADD_BR([br-int]) -+ -+# Set external-ids in br-int needed for ovn-controller -+ovs-vsctl \ -+ -- set Open_vSwitch . external-ids:system-id=hv1 \ -+ -- set Open_vSwitch . external-ids:ovn-remote=unix:$ovs_base/ovn-sb/ovn-sb.sock \ -+ -- set Open_vSwitch . external-ids:ovn-encap-type=geneve \ -+ -- set Open_vSwitch . external-ids:ovn-encap-ip=169.0.0.1 \ -+ -- set bridge br-int fail-mode=secure other-config:disable-in-band=true -+ -+# Start ovn-controller -+start_daemon ovn-controller -+ -+# Logical network: -+# Three LRs - R1, R2 and R3 that are connected to each other via LS "join" -+# in 20.0.0.0/24 and fd20::/64 networks. R1 has switches foo (192.168.1.0/24 -+# and fd11::/64) and bar (192.168.2.0/24 and fd12::/64) connected to it. R2 -+# has alice (172.16.1.0/24 and fd30::/64) connected to it. R3 has bob -+# (172.16.1.0/24 andfd30::/64) connected to it. Note how both alice and bob -+# have the same subnets behind them. We are trying to simulate external network -+# via those 2 switches. In real world the switch ports of these switches will -+# have addresses set as "unknown" to make them learning switches. Or those -+# switches will be "localnet" ones. -+# -+# foo -- R1 -- join - R2 -- alice -+# | | -+# bar ---- - R3 --- bob -+ -+ovn-nbctl create Logical_Router name=R1 -+ovn-nbctl create Logical_Router name=R2 options:chassis=hv1 -+ovn-nbctl create Logical_Router name=R3 options:chassis=hv1 -+ -+ovn-nbctl ls-add foo -+ovn-nbctl ls-add bar -+ovn-nbctl ls-add alice -+ovn-nbctl ls-add bob -+ovn-nbctl ls-add join -+ -+# Connect foo to R1 -+ovn-nbctl lrp-add R1 foo 00:00:01:01:02:03 192.168.1.1/24 fd11::1/64 -+ovn-nbctl lsp-add foo rp-foo -- set Logical_Switch_Port rp-foo \ -+ type=router options:router-port=foo addresses=\"00:00:01:01:02:03\" -+ -+# Connect bar to R1 -+ovn-nbctl lrp-add R1 bar 00:00:01:01:02:04 192.168.2.1/24 fd12::1/64 -+ovn-nbctl lsp-add bar rp-bar -- set Logical_Switch_Port rp-bar \ -+ type=router options:router-port=bar addresses=\"00:00:01:01:02:04\" -+ -+# Connect alice to R2 -+ovn-nbctl lrp-add R2 alice 00:00:02:01:02:03 172.16.1.1/24 fd30::1/64 -+ovn-nbctl lsp-add alice rp-alice -- set Logical_Switch_Port rp-alice \ -+ type=router options:router-port=alice addresses=\"00:00:02:01:02:03\" -+ -+# Connect bob to R3 -+ovn-nbctl lrp-add R3 bob 00:00:03:01:02:03 172.16.1.2/24 fd30::2/64 -+ovn-nbctl lsp-add bob rp-bob -- set Logical_Switch_Port rp-bob \ -+ type=router options:router-port=bob addresses=\"00:00:03:01:02:03\" -+ -+# Connect R1 to join -+ovn-nbctl lrp-add R1 R1_join 00:00:04:01:02:03 20.0.0.1/24 fd20::1/64 -+ovn-nbctl lsp-add join r1-join -- set Logical_Switch_Port r1-join \ -+ type=router options:router-port=R1_join addresses='"00:00:04:01:02:03"' -+ -+# Connect R2 to join -+ovn-nbctl lrp-add R2 R2_join 00:00:04:01:02:04 20.0.0.2/24 fd20::2/64 -+ovn-nbctl lsp-add join r2-join -- set Logical_Switch_Port r2-join \ -+ type=router options:router-port=R2_join addresses='"00:00:04:01:02:04"' -+ -+# Connect R3 to join -+ovn-nbctl lrp-add R3 R3_join 00:00:04:01:02:05 20.0.0.3/24 fd20::3/64 -+ovn-nbctl lsp-add join r3-join -- set Logical_Switch_Port r3-join \ -+ type=router options:router-port=R3_join addresses='"00:00:04:01:02:05"' -+ -+# Install static routes with source ip address as the policy for routing. -+# We want traffic from 'foo' to go via R2 and traffic of 'bar' to go via R3. -+ovn-nbctl --policy="src-ip" lr-route-add R1 fd11::/64 fd20::2 -+ovn-nbctl --policy="src-ip" lr-route-add R1 fd12::/64 fd20::3 -+ovn-nbctl --policy="src-ip" lr-route-add R1 192.168.1.0/24 20.0.0.2 -+ovn-nbctl --policy="src-ip" lr-route-add R1 192.168.2.0/24 20.0.0.3 -+ -+# Static routes. -+ovn-nbctl lr-route-add R2 fd11::/64 fd20::1 -+ovn-nbctl lr-route-add R2 fd12::/64 fd20::1 -+ovn-nbctl lr-route-add R2 192.168.0.0/16 20.0.0.1 -+ovn-nbctl lr-route-add R3 fd11::/64 fd20::1 -+ovn-nbctl lr-route-add R3 fd12::/64 fd20::1 -+ovn-nbctl lr-route-add R3 192.168.0.0/16 20.0.0.1 -+ -+# For gateway routers R2 and R3, set a force SNAT rule. -+ovn-nbctl set logical_router R2 options:dnat_force_snat_ip="20.0.0.2 fd20::2" -+ovn-nbctl set logical_router R3 options:dnat_force_snat_ip="20.0.0.3 fd20::3" -+ -+# Logical port 'foo1' in switch 'foo'. -+ADD_NAMESPACES(foo1) -+ADD_VETH(foo1, foo1, br-int, "192.168.1.2/24", "f0:00:00:01:02:03", \ -+ "192.168.1.1") -+ovn-nbctl lsp-add foo foo1 \ -+-- lsp-set-addresses foo1 "f0:00:00:01:02:03 192.168.1.2" -+ -+ADD_NAMESPACES(foo16) -+ADD_VETH(foo16, foo16, br-int, "fd11::2/64", "f0:00:00:02:02:03", \ -+ "fd11::1") -+OVS_WAIT_UNTIL([test "$(ip netns exec foo16 ip a | grep fd11::2 | grep tentative)" = ""]) -+ovn-nbctl lsp-add foo foo16 \ -+-- lsp-set-addresses foo16 "f0:00:00:02:02:03 fd11::2" -+ -+# Logical port 'alice1' in switch 'alice'. -+ADD_NAMESPACES(alice1) -+ADD_VETH(alice1, alice1, br-int, "172.16.1.3/24", "f0:00:00:01:02:04", \ -+ "172.16.1.1") -+ovn-nbctl lsp-add alice alice1 \ -+-- lsp-set-addresses alice1 "f0:00:00:01:02:04 172.16.1.3" -+ -+ADD_NAMESPACES(alice16) -+ADD_VETH(alice16, alice16, br-int, "fd30::3/64", "f0:00:00:02:02:04", \ -+ "fd30::1") -+OVS_WAIT_UNTIL([test "$(ip netns exec alice16 ip a | grep fd30::3 | grep tentative)" = ""]) -+ovn-nbctl lsp-add alice alice16 \ -+-- lsp-set-addresses alice16 "f0:00:00:02:02:04 fd30::3" -+ -+# Logical port 'bar1' in switch 'bar'. -+ADD_NAMESPACES(bar1) -+ADD_VETH(bar1, bar1, br-int, "192.168.2.2/24", "f0:00:00:01:02:05", \ -+"192.168.2.1") -+ovn-nbctl lsp-add bar bar1 \ -+-- lsp-set-addresses bar1 "f0:00:00:01:02:05 192.168.2.2" -+ -+ADD_NAMESPACES(bar16) -+ADD_VETH(bar16, bar16, br-int, "fd12::2/64", "f0:00:00:02:02:05", \ -+ "fd12::1") -+OVS_WAIT_UNTIL([test "$(ip netns exec bar16 ip a | grep fd12::2 | grep tentative)" = ""]) -+ovn-nbctl lsp-add bar bar16 \ -+-- lsp-set-addresses bar16 "f0:00:00:02:02:05 fd12::2" -+ -+# Logical port 'bob1' in switch 'bob'. -+ADD_NAMESPACES(bob1) -+ADD_VETH(bob1, bob1, br-int, "172.16.1.4/24", "f0:00:00:01:02:06", \ -+ "172.16.1.2") -+ovn-nbctl lsp-add bob bob1 \ -+-- lsp-set-addresses bob1 "f0:00:00:01:02:06 172.16.1.4" -+ -+ADD_NAMESPACES(bob16) -+ADD_VETH(bob16, bob16, br-int, "fd30::4/64", "f0:00:00:02:02:06", \ -+ "fd30::2") -+OVS_WAIT_UNTIL([test "$(ip netns exec bob16 ip a | grep fd30::4 | grep tentative)" = ""]) -+ovn-nbctl lsp-add bob bob16 \ -+-- lsp-set-addresses bob16 "f0:00:00:02:02:06 fd30::4" -+ -+# Router R2 -+# Add a DNAT rule. -+ovn-nbctl -- --id=@nat create nat type="dnat" logical_ip=192.168.1.2 \ -+ external_ip=30.0.0.2 -- add logical_router R2 nat @nat -+ovn-nbctl -- --id=@nat create nat type="dnat" logical_ip='"fd11::2"' \ -+ external_ip='"fd40::2"' -- add logical_router R2 nat @nat -+ -+# Add a SNAT rule -+ovn-nbctl -- --id=@nat create nat type="snat" logical_ip=192.168.1.2 \ -+ external_ip=30.0.0.1 -- add logical_router R2 nat @nat -+ovn-nbctl -- --id=@nat create nat type="snat" logical_ip='"fd11::2"' \ -+ external_ip='"fd40::1"' -- add logical_router R2 nat @nat -+ -+# Router R3 -+# Add a DNAT rule. -+ovn-nbctl -- --id=@nat create nat type="dnat" logical_ip=192.168.1.2 \ -+ external_ip=30.0.0.3 -- add logical_router R3 nat @nat -+ovn-nbctl -- --id=@nat create nat type="dnat" logical_ip='"fd11::2"' \ -+ external_ip='"fd40::3"' -- add logical_router R3 nat @nat -+ -+# Add a SNAT rule -+ovn-nbctl -- --id=@nat create nat type="snat" logical_ip=192.168.2.2 \ -+ external_ip=30.0.0.4 -- add logical_router R3 nat @nat -+ovn-nbctl -- --id=@nat create nat type="snat" logical_ip='"fd12::2"' \ -+ external_ip='"fd40::4"' -- add logical_router R3 nat @nat -+ -+# wait for ovn-controller to catch up. -+ovn-nbctl --wait=hv sync -+OVS_WAIT_UNTIL([ovs-ofctl dump-flows br-int | grep 'nat(src=fd40::4)']) -+OVS_WAIT_UNTIL([ovs-ofctl dump-flows br-int | grep 'nat(src=30.0.0.4)']) -+ -+# North-South DNAT: 'alice1' should be able to ping 'foo1' via 30.0.0.2 -+NS_CHECK_EXEC([alice1], [ping -q -c 3 -i 0.3 -w 2 30.0.0.2 | FORMAT_PING], \ -+[0], [dnl -+3 packets transmitted, 3 received, 0% packet loss, time 0ms -+]) -+ -+# North-South DNAT: 'alice16' should be able to ping 'foo16' via fd30::2 -+NS_CHECK_EXEC([alice16], [ping -6 -q -c 3 -i 0.3 -w 2 fd40::2 | FORMAT_PING], \ -+[0], [dnl -+3 packets transmitted, 3 received, 0% packet loss, time 0ms -+]) -+ -+# Check conntrack entries. -+AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(172.16.1.3) | \ -+sed -e 's/zone=[[0-9]]*/zone=/'], [0], [dnl -+icmp,orig=(src=172.16.1.3,dst=30.0.0.2,id=,type=8,code=0),reply=(src=192.168.1.2,dst=172.16.1.3,id=,type=0,code=0),zone= -+]) -+AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd30::3) | \ -+sed -e 's/zone=[[0-9]]*/zone=/'], [0], [dnl -+icmpv6,orig=(src=fd30::3,dst=fd40::2,id=,type=128,code=0),reply=(src=fd11::2,dst=fd30::3,id=,type=129,code=0),zone= -+]) -+ -+# But foo1 should receive traffic from 20.0.0.2 -+AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(20.0.0.2) | \ -+sed -e 's/zone=[[0-9]]*/zone=/'], [0], [dnl -+icmp,orig=(src=172.16.1.3,dst=192.168.1.2,id=,type=8,code=0),reply=(src=192.168.1.2,dst=20.0.0.2,id=,type=0,code=0),zone= -+]) -+# But foo16 should receive traffic from fd20::2 -+AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd20::2) | \ -+sed -e 's/zone=[[0-9]]*/zone=/'], [0], [dnl -+icmpv6,orig=(src=fd30::3,dst=fd11::2,id=,type=128,code=0),reply=(src=fd11::2,dst=fd20::2,id=,type=129,code=0),zone= -+]) -+ -+# North-South DNAT: 'bob1' should be able to ping 'foo1' via 30.0.0.3 -+NS_CHECK_EXEC([bob1], [ping -q -c 3 -i 0.3 -w 2 30.0.0.3 | FORMAT_PING], \ -+[0], [dnl -+3 packets transmitted, 3 received, 0% packet loss, time 0ms -+]) -+ -+# North-South DNAT: 'bob16' should be able to ping 'foo16' via fd40::3 -+NS_CHECK_EXEC([bob16], [ping -6 -q -c 3 -i 0.3 -w 2 fd40::3 | FORMAT_PING], \ -+[0], [dnl -+3 packets transmitted, 3 received, 0% packet loss, time 0ms -+]) -+ -+# Check conntrack entries. -+AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(172.16.1.4) | \ -+sed -e 's/zone=[[0-9]]*/zone=/'], [0], [dnl -+icmp,orig=(src=172.16.1.4,dst=30.0.0.3,id=,type=8,code=0),reply=(src=192.168.1.2,dst=172.16.1.4,id=,type=0,code=0),zone= -+]) -+AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd30::4) | \ -+sed -e 's/zone=[[0-9]]*/zone=/'], [0], [dnl -+icmpv6,orig=(src=fd30::4,dst=fd40::3,id=,type=128,code=0),reply=(src=fd11::2,dst=fd30::4,id=,type=129,code=0),zone= -+]) -+ -+# But foo1 should receive traffic from 20.0.0.3 -+AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(20.0.0.3) | \ -+sed -e 's/zone=[[0-9]]*/zone=/'], [0], [dnl -+icmp,orig=(src=172.16.1.4,dst=192.168.1.2,id=,type=8,code=0),reply=(src=192.168.1.2,dst=20.0.0.3,id=,type=0,code=0),zone= -+]) -+ -+# But foo16 should receive traffic from fd20::3 -+AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd20::3) | \ -+sed -e 's/zone=[[0-9]]*/zone=/'], [0], [dnl -+icmpv6,orig=(src=fd30::4,dst=fd11::2,id=,type=128,code=0),reply=(src=fd11::2,dst=fd20::3,id=,type=129,code=0),zone= -+]) -+ -+# South-North SNAT: 'bar1' pings 'bob1'. But 'bob1' receives traffic -+# from 30.0.0.4 -+NS_CHECK_EXEC([bar1], [ping -q -c 3 -i 0.3 -w 2 172.16.1.4 | FORMAT_PING], \ -+[0], [dnl -+3 packets transmitted, 3 received, 0% packet loss, time 0ms -+]) -+# South-North SNAT: 'bar16' pings 'bob16'. But 'bob16' receives traffic -+# from fd40::4 -+NS_CHECK_EXEC([bar16], [ping -6 -q -c 3 -i 0.3 -w 2 fd30::4 | FORMAT_PING], \ -+[0], [dnl -+3 packets transmitted, 3 received, 0% packet loss, time 0ms -+]) -+ -+# We verify that SNAT indeed happened via 'dump-conntrack' command. -+AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(30.0.0.4) | \ -+sed -e 's/zone=[[0-9]]*/zone=/'], [0], [dnl -+icmp,orig=(src=192.168.2.2,dst=172.16.1.4,id=,type=8,code=0),reply=(src=172.16.1.4,dst=30.0.0.4,id=,type=0,code=0),zone= -+]) -+AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd40::4) | \ -+sed -e 's/zone=[[0-9]]*/zone=/'], [0], [dnl -+icmpv6,orig=(src=fd12::2,dst=fd30::4,id=,type=128,code=0),reply=(src=fd30::4,dst=fd40::4,id=,type=129,code=0),zone= -+]) -+ -+# South-North SNAT: 'foo1' pings 'alice1'. But 'alice1' receives traffic -+# from 30.0.0.1 -+NS_CHECK_EXEC([foo1], [ping -q -c 3 -i 0.3 -w 2 172.16.1.3 | FORMAT_PING], \ -+[0], [dnl -+3 packets transmitted, 3 received, 0% packet loss, time 0ms -+]) -+ -+# South-North SNAT: 'foo16' pings 'alice16'. But 'alice16' receives traffic -+# from fd40::1 -+NS_CHECK_EXEC([foo16], [ping -6 -q -c 3 -i 0.3 -w 2 fd30::3 | FORMAT_PING], \ -+[0], [dnl -+3 packets transmitted, 3 received, 0% packet loss, time 0ms -+]) -+ -+# We verify that SNAT indeed happened via 'dump-conntrack' command. -+AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(30.0.0.1) | \ -+sed -e 's/zone=[[0-9]]*/zone=/'], [0], [dnl -+icmp,orig=(src=192.168.1.2,dst=172.16.1.3,id=,type=8,code=0),reply=(src=172.16.1.3,dst=30.0.0.1,id=,type=0,code=0),zone= -+]) -+AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd40::1) | \ -+sed -e 's/zone=[[0-9]]*/zone=/'], [0], [dnl -+icmpv6,orig=(src=fd11::2,dst=fd30::3,id=,type=128,code=0),reply=(src=fd30::3,dst=fd40::1,id=,type=129,code=0),zone= -+]) -+ -+OVS_APP_EXIT_AND_WAIT([ovn-controller]) -+ -+as ovn-sb -+OVS_APP_EXIT_AND_WAIT([ovsdb-server]) -+ -+as ovn-nb -+OVS_APP_EXIT_AND_WAIT([ovsdb-server]) -+ -+as northd -+OVS_APP_EXIT_AND_WAIT([ovn-northd]) -+ -+as -+OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d -+/connection dropped.*/d"]) -+AT_CLEANUP -+ -+ - AT_SETUP([ovn -- load-balancing]) - AT_KEYWORDS([ovnlb]) - -@@ -2405,6 +2722,230 @@ OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d - /connection dropped.*/d"]) - AT_CLEANUP - -+AT_SETUP([ovn -- multiple gateway routers, load-balancing - Dual Stack]) -+AT_KEYWORDS([ovnlb]) -+ -+CHECK_CONNTRACK() -+CHECK_CONNTRACK_NAT() -+ovn_start -+OVS_TRAFFIC_VSWITCHD_START() -+ADD_BR([br-int]) -+ -+# Set external-ids in br-int needed for ovn-controller -+ovs-vsctl \ -+ -- set Open_vSwitch . external-ids:system-id=hv1 \ -+ -- set Open_vSwitch . external-ids:ovn-remote=unix:$ovs_base/ovn-sb/ovn-sb.sock \ -+ -- set Open_vSwitch . external-ids:ovn-encap-type=geneve \ -+ -- set Open_vSwitch . external-ids:ovn-encap-ip=169.0.0.1 \ -+ -- set bridge br-int fail-mode=secure other-config:disable-in-band=true -+ -+# Start ovn-controller -+start_daemon ovn-controller -+ -+# Logical network: -+# Three LRs - R1, R2 and R3 that are connected to each other via LS "join" -+# in 20.0.0.0/24 and fd20::/64 networks. R1 has switches foo (192.168.1.0/24 -+# and fd11::/64) and bar (192.168.2.0/24 and fd12::/64) connected to it. R2 -+# has alice (172.16.1.0/24 and fd72::/64) connected to it. R3 has bob -+# (172.16.1.0/24 and fd72::/64) connected to it. Note how both alice and -+# bob have the same subnets behind them. We are trying to simulate external -+# network via those 2 switches. In real world the switch ports of these -+# switches will have addresses set as "unknown" to make them learning switches. -+# Or those switches will be "localnet" ones. -+# -+# foo -- R1 -- join - R2 -- alice -+# | | -+# bar ---- - R3 --- bob -+ -+ovn-nbctl create Logical_Router name=R1 -+ovn-nbctl create Logical_Router name=R2 options:chassis=hv1 -+ovn-nbctl create Logical_Router name=R3 options:chassis=hv1 -+ -+ovn-nbctl ls-add foo -+ovn-nbctl ls-add bar -+ovn-nbctl ls-add alice -+ovn-nbctl ls-add bob -+ovn-nbctl ls-add join -+ -+# Connect foo to R1 -+ovn-nbctl lrp-add R1 foo 00:00:01:01:02:03 192.168.1.1/24 fd11::1/64 -+ovn-nbctl lsp-add foo rp-foo -- set Logical_Switch_Port rp-foo \ -+ type=router options:router-port=foo addresses=\"00:00:01:01:02:03\" -+ -+# Connect bar to R1 -+ovn-nbctl lrp-add R1 bar 00:00:01:01:02:04 192.168.2.1/24 fd12::1/64 -+ovn-nbctl lsp-add bar rp-bar -- set Logical_Switch_Port rp-bar \ -+ type=router options:router-port=bar addresses=\"00:00:01:01:02:04\" -+ -+# Connect alice to R2 -+ovn-nbctl lrp-add R2 alice 00:00:02:01:02:03 172.16.1.1/24 fd72::1/64 -+ovn-nbctl lsp-add alice rp-alice -- set Logical_Switch_Port rp-alice \ -+ type=router options:router-port=alice addresses=\"00:00:02:01:02:03\" -+ -+# Connect bob to R3 -+ovn-nbctl lrp-add R3 bob 00:00:03:01:02:03 172.16.1.2/24 fd72::2/64 -+ovn-nbctl lsp-add bob rp-bob -- set Logical_Switch_Port rp-bob \ -+ type=router options:router-port=bob addresses=\"00:00:03:01:02:03\" -+ -+# Connect R1 to join -+ovn-nbctl lrp-add R1 R1_join 00:00:04:01:02:03 20.0.0.1/24 fd20::1/64 -+ovn-nbctl lsp-add join r1-join -- set Logical_Switch_Port r1-join \ -+ type=router options:router-port=R1_join addresses='"00:00:04:01:02:03"' -+ -+# Connect R2 to join -+ovn-nbctl lrp-add R2 R2_join 00:00:04:01:02:04 20.0.0.2/24 fd20::2/64 -+ovn-nbctl lsp-add join r2-join -- set Logical_Switch_Port r2-join \ -+ type=router options:router-port=R2_join addresses='"00:00:04:01:02:04"' -+ -+# Connect R3 to join -+ovn-nbctl lrp-add R3 R3_join 00:00:04:01:02:05 20.0.0.3/24 fd20::3/64 -+ovn-nbctl lsp-add join r3-join -- set Logical_Switch_Port r3-join \ -+ type=router options:router-port=R3_join addresses='"00:00:04:01:02:05"' -+ -+# Install static routes with source ip address as the policy for routing. -+# We want traffic from 'foo' to go via R2 and traffic of 'bar' to go via R3. -+ovn-nbctl --policy="src-ip" lr-route-add R1 192.168.1.0/24 20.0.0.2 -+ovn-nbctl --policy="src-ip" lr-route-add R1 192.168.2.0/24 20.0.0.3 -+ovn-nbctl --policy="src-ip" lr-route-add R1 fd11::/64 fd20::2 -+ovn-nbctl --policy="src-ip" lr-route-add R1 fd12::/64 fd20::3 -+ -+# Static routes. -+ovn-nbctl lr-route-add R2 192.168.0.0/16 20.0.0.1 -+ovn-nbctl lr-route-add R3 192.168.0.0/16 20.0.0.1 -+ovn-nbctl lr-route-add R2 fd11::/64 fd20::1 -+ovn-nbctl lr-route-add R2 fd12::/64 fd20::1 -+ovn-nbctl lr-route-add R3 fd11::/64 fd20::1 -+ovn-nbctl lr-route-add R3 fd12::/64 fd20::1 -+ -+# For gateway routers R2 and R3, set a force SNAT rule. -+ovn-nbctl set logical_router R2 options:lb_force_snat_ip="20.0.0.2 fd20::2" -+ovn-nbctl set logical_router R3 options:lb_force_snat_ip="20.0.0.3 fd20::3" -+ -+# Logical port 'foo1' in switch 'foo'. -+ADD_NAMESPACES(foo1) -+ADD_VETH(foo1, foo1, br-int, "192.168.1.2/24", "f0:00:00:01:02:03", \ -+ "192.168.1.1") -+ovn-nbctl lsp-add foo foo1 \ -+-- lsp-set-addresses foo1 "f0:00:00:01:02:03 192.168.1.2" -+ -+# Logical port 'foo16' in switch 'foo'. -+ADD_NAMESPACES(foo16) -+ADD_VETH(foo16, foo16, br-int, "fd11::2/64", "f0:00:06:01:02:03", \ -+ "fd11::1") -+ovn-nbctl lsp-add foo foo16 \ -+-- lsp-set-addresses foo16 "f0:00:06:01:02:03 fd11::2" -+ -+# Logical port 'alice1' in switch 'alice'. -+ADD_NAMESPACES(alice1) -+ADD_VETH(alice1, alice1, br-int, "172.16.1.3/24", "f0:00:00:01:02:04", \ -+ "172.16.1.1") -+ovn-nbctl lsp-add alice alice1 \ -+-- lsp-set-addresses alice1 "f0:00:00:01:02:04 172.16.1.3" -+ -+# Logical port 'alice16' in switch 'alice'. -+ADD_NAMESPACES(alice16) -+ADD_VETH(alice16, alice16, br-int, "fd72::3/64", "f0:00:06:01:02:04", \ -+ "fd72::1") -+ovn-nbctl lsp-add alice alice16 \ -+-- lsp-set-addresses alice16 "f0:00:06:01:02:04 fd72::3" -+ -+# Logical port 'bar1' in switch 'bar'. -+ADD_NAMESPACES(bar1) -+ADD_VETH(bar1, bar1, br-int, "192.168.2.2/24", "f0:00:00:01:02:05", \ -+"192.168.2.1") -+ovn-nbctl lsp-add bar bar1 \ -+-- lsp-set-addresses bar1 "f0:00:00:01:02:05 192.168.2.2" -+ -+# Logical port 'bar16' in switch 'bar'. -+ADD_NAMESPACES(bar16) -+ADD_VETH(bar16, bar16, br-int, "fd12::2/64", "f0:00:06:01:02:05", \ -+"fd12::1") -+ovn-nbctl lsp-add bar bar16 \ -+-- lsp-set-addresses bar16 "f0:00:06:01:02:05 fd12::2" -+ -+# Logical port 'bob1' in switch 'bob'. -+ADD_NAMESPACES(bob1) -+ADD_VETH(bob1, bob1, br-int, "172.16.1.4/24", "f0:00:00:01:02:06", \ -+ "172.16.1.2") -+ovn-nbctl lsp-add bob bob1 \ -+-- lsp-set-addresses bob1 "f0:00:00:01:02:06 172.16.1.4" -+ -+# Logical port 'bob16' in switch 'bob'. -+ADD_NAMESPACES(bob16) -+ADD_VETH(bob16, bob16, br-int, "fd72::4/64", "f0:00:06:01:02:06", \ -+ "fd72::2") -+ovn-nbctl lsp-add bob bob16 \ -+-- lsp-set-addresses bob16 "f0:00:06:01:02:06 fd72::4" -+ -+# Config OVN load-balancer with a VIP. -+uuid=`ovn-nbctl create load_balancer vips:30.0.0.1="192.168.1.2,192.168.2.2" \ -+vips:\"fd30::1\"=\"fd11::2,fd12::2\"` -+ovn-nbctl set logical_router R2 load_balancer=$uuid -+ovn-nbctl set logical_router R3 load_balancer=$uuid -+ -+# Wait for ovn-controller to catch up. -+ovn-nbctl --wait=hv sync -+OVS_WAIT_UNTIL([ovs-ofctl -O OpenFlow13 dump-groups br-int | \ -+grep 'nat(dst=192.168.2.2)']) -+OVS_WAIT_UNTIL([ovs-ofctl -O OpenFlow13 dump-groups br-int | \ -+grep 'nat(dst=fd12::2)']) -+ -+# Start webservers in 'foo1', 'foo16, 'bar1', and 'bar16'. -+OVS_START_L7([foo1], [http]) -+OVS_START_L7([bar1], [http]) -+OVS_START_L7([foo16], [http6]) -+OVS_START_L7([bar16], [http6]) -+ -+dnl Should work with the virtual IP address through NAT -+for i in `seq 1 20`; do -+ echo Request $i -+ NS_CHECK_EXEC([alice1], [wget 30.0.0.1 -t 5 -T 1 --retry-connrefused -v -o wget$i.log]) -+done -+ -+for i in `seq 1 20`; do -+ echo Request ${i}_6 -+ NS_CHECK_EXEC([alice16], [wget http://[[fd30::1]] -t 5 -T 1 --retry-connrefused -v -o wget${i}_6.log]) -+done -+ -+dnl Each server should have at least one connection. -+AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(30.0.0.1) | -+sed -e 's/zone=[[0-9]]*/zone=/'], [0], [dnl -+tcp,orig=(src=172.16.1.3,dst=30.0.0.1,sport=,dport=),reply=(src=192.168.1.2,dst=172.16.1.3,sport=,dport=),zone=,protoinfo=(state=) -+tcp,orig=(src=172.16.1.3,dst=30.0.0.1,sport=,dport=),reply=(src=192.168.2.2,dst=172.16.1.3,sport=,dport=),zone=,protoinfo=(state=) -+]) -+AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd30::1) | grep -v fe80 | -+sed -e 's/zone=[[0-9]]*/zone=/'], [0], [dnl -+tcp,orig=(src=fd72::3,dst=fd30::1,sport=,dport=),reply=(src=fd11::2,dst=fd72::3,sport=,dport=),zone=,protoinfo=(state=) -+tcp,orig=(src=fd72::3,dst=fd30::1,sport=,dport=),reply=(src=fd12::2,dst=fd72::3,sport=,dport=),zone=,protoinfo=(state=) -+]) -+ -+dnl Force SNAT should have worked. -+AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(20.0.0) | -+sed -e 's/zone=[[0-9]]*/zone=/'], [0], [dnl -+tcp,orig=(src=172.16.1.3,dst=192.168.1.2,sport=,dport=),reply=(src=192.168.1.2,dst=20.0.0.2,sport=,dport=),zone=,protoinfo=(state=) -+tcp,orig=(src=172.16.1.3,dst=192.168.2.2,sport=,dport=),reply=(src=192.168.2.2,dst=20.0.0.2,sport=,dport=),zone=,protoinfo=(state=) -+]) -+AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd20::2) | grep -v fe80 | -+sed -e 's/zone=[[0-9]]*/zone=/'], [0], [dnl -+tcp,orig=(src=fd72::3,dst=fd11::2,sport=,dport=),reply=(src=fd11::2,dst=fd20::2,sport=,dport=),zone=,protoinfo=(state=) -+tcp,orig=(src=fd72::3,dst=fd12::2,sport=,dport=),reply=(src=fd12::2,dst=fd20::2,sport=,dport=),zone=,protoinfo=(state=) -+]) -+OVS_APP_EXIT_AND_WAIT([ovn-controller]) -+ -+as ovn-sb -+OVS_APP_EXIT_AND_WAIT([ovsdb-server]) -+ -+as ovn-nb -+OVS_APP_EXIT_AND_WAIT([ovsdb-server]) -+ -+as northd -+OVS_APP_EXIT_AND_WAIT([ovn-northd]) -+ -+as -+OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d -+/connection dropped.*/d"]) -+AT_CLEANUP -+ - AT_SETUP([ovn -- load balancing in router with gateway router port]) - AT_KEYWORDS([ovnlb]) - --- -2.26.2 - diff --git a/SOURCES/0017-ovn-northd-Don-t-send-the-pkt-to-conntrack-if-it-is-.patch b/SOURCES/0017-ovn-northd-Don-t-send-the-pkt-to-conntrack-if-it-is-.patch deleted file mode 100644 index 8be01b7..0000000 --- a/SOURCES/0017-ovn-northd-Don-t-send-the-pkt-to-conntrack-if-it-is-.patch +++ /dev/null @@ -1,187 +0,0 @@ -From ac218b6e00ca97ddb54362d71435c4ea2d47cfb7 Mon Sep 17 00:00:00 2001 -From: Numan Siddique -Date: Tue, 4 Aug 2020 12:49:34 +0530 -Subject: [PATCH 17/22] ovn-northd: Don't send the pkt to conntrack if it is to - be routed in egress stage. - -If there is a logical port 'P1' with the IP - 10.0.0.3 and a logical port 'P2' with -the IP 20.0.0.3 and if the logical switch of 'P1' has atleast one load balancer -associated with it and atleast one ACL with allow-related action associated with it. -Then for every packet from 'P1' to 'P2' after the TCP connection -is established we see a total of 4 recirculations in the datapath on the chassis -claiming 'P1'. This is because, - -In the ingress logical switch pipeline, below logical flows are hit - - table=9 (ls_in_lb ), priority=65535, match=(ct.est && !ct.rel && !ct.new && !ct.inv), action=(reg0[2] = 1; next;) - - table=10(ls_in_stateful ), priority=100 , match=(reg0[2] == 1), action=(ct_lb;) - -And in the egress logical switch pipeline, below logical flows are hit - - table=0 (ls_out_pre_lb ), priority=100 , match=(ip), action=(reg0[0] = 1; next;) - - table=2 (ls_out_pre_stateful), priority=100 , match=(reg0[0] == 1), action=(ct_next;) - - table=3 (ls_out_lb ), priority=65535, match=(ct.est && !ct.rel && !ct.new && !ct.inv), action=(reg0[2] = 1; next;) - - table=7 (ls_out_stateful ), priority=100 , match=(reg0[2] == 1), action=(ct_lb;) - -In the above example, when the packet enters the egress pipeline and since it needs to -enter the router pipeline, we can skip setting reg0[0] if outport is peer port of -logical router port. There is no need to send the packet to conntrack in this case. - -This patch handles this case for router ports. Next patch in the series avoids sending to -conntrack with the action - ct_lb if the packet is not destined to the LB VIP. - -With the present master for the above example, we see total of 4 recirculations on the -chassis claiming the lport 'P1'. With this patch we see only 2 recirculations. - -Acked-by: Dumitru Ceara -Signed-off-by: Numan Siddique ---- - northd/ovn-northd.8.xml | 33 ++++++++++++++++++++++++++++++++- - northd/ovn-northd.c | 39 ++++++++++++++++++++++++++++++--------- - 2 files changed, 62 insertions(+), 10 deletions(-) - -diff --git a/northd/ovn-northd.8.xml b/northd/ovn-northd.8.xml -index f35a035fd..e45d494e8 100644 ---- a/northd/ovn-northd.8.xml -+++ b/northd/ovn-northd.8.xml -@@ -338,6 +338,15 @@ - db="OVN_Northbound"/> table. -

      - -+

      -+ This table also has a priority-110 flow with the match -+ inport == I for all logical switch -+ datapaths to move traffic to the next table. Where I -+ is the peer of a logical router port. This flow is added to -+ skip the connection tracking of packets which enter from -+ logical router datapath to logical switch datapath. -+

      -+ -

      Ingress Table 5: Pre-stateful

      - -

      -@@ -505,7 +514,20 @@ - -

      - It contains a priority-0 flow that simply moves traffic to the next -- table. For established connections a priority 100 flow matches on -+ table. -+

      -+ -+

      -+ A priority-65535 flow with the match -+ inport == I for all logical switch -+ datapaths to move traffic to the next table. Where I -+ is the peer of a logical router port. This flow is added to -+ skip the connection tracking of packets which enter from -+ logical router datapath to logical switch datapath. -+

      -+ -+

      -+ For established connections a priority 65534 flow matches on - ct.est && !ct.rel && !ct.new && - !ct.inv and sets an action reg0[2] = 1; next; to act - as a hint for table Stateful to send packets through -@@ -1342,6 +1364,15 @@ output; - db="OVN_Northbound"/> table. -

      - -+

      -+ This table also has a priority-110 flow with the match -+ outport == I for all logical switch -+ datapaths to move traffic to the next table. Where I -+ is the peer of a logical router port. This flow is added to -+ skip the connection tracking of packets which will be entering -+ logical router datapath from logical switch datapath for routing. -+

      -+ -

      Egress Table 2: Pre-stateful

      - -

      -diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c -index 1f5433d9d..7b534ce3c 100644 ---- a/northd/ovn-northd.c -+++ b/northd/ovn-northd.c -@@ -4850,8 +4850,9 @@ build_lswitch_output_port_sec(struct hmap *ports, struct hmap *datapaths, - } - - static void --build_pre_acl_flows(struct ovn_datapath *od, struct ovn_port *op, -- struct hmap *lflows) -+skip_port_from_conntrack(struct ovn_datapath *od, struct ovn_port *op, -+ enum ovn_stage in_stage, enum ovn_stage out_stage, -+ uint16_t priority, struct hmap *lflows) - { - /* Can't use ct() for router ports. Consider the following configuration: - * lp1(10.0.0.2) on hostA--ls1--lr0--ls2--lp2(10.0.1.2) on hostB, For a -@@ -4867,10 +4868,10 @@ build_pre_acl_flows(struct ovn_datapath *od, struct ovn_port *op, - - ds_put_format(&match_in, "ip && inport == %s", op->json_key); - ds_put_format(&match_out, "ip && outport == %s", op->json_key); -- ovn_lflow_add_with_hint(lflows, od, S_SWITCH_IN_PRE_ACL, 110, -+ ovn_lflow_add_with_hint(lflows, od, in_stage, priority, - ds_cstr(&match_in), "next;", - &op->nbsp->header_); -- ovn_lflow_add_with_hint(lflows, od, S_SWITCH_OUT_PRE_ACL, 110, -+ ovn_lflow_add_with_hint(lflows, od, out_stage, priority, - ds_cstr(&match_out), "next;", - &op->nbsp->header_); - -@@ -4903,10 +4904,14 @@ build_pre_acls(struct ovn_datapath *od, struct hmap *lflows) - * defragmentation, in order to match L4 headers. */ - if (has_stateful) { - for (size_t i = 0; i < od->n_router_ports; i++) { -- build_pre_acl_flows(od, od->router_ports[i], lflows); -+ skip_port_from_conntrack(od, od->router_ports[i], -+ S_SWITCH_IN_PRE_ACL, S_SWITCH_OUT_PRE_ACL, -+ 110, lflows); - } - for (size_t i = 0; i < od->n_localnet_ports; i++) { -- build_pre_acl_flows(od, od->localnet_ports[i], lflows); -+ skip_port_from_conntrack(od, od->localnet_ports[i], -+ S_SWITCH_IN_PRE_ACL, S_SWITCH_OUT_PRE_ACL, -+ 110, lflows); - } - - /* Ingress and Egress Pre-ACL Table (Priority 110). -@@ -5050,6 +5055,17 @@ build_pre_lb(struct ovn_datapath *od, struct hmap *lflows, - ovn_lflow_add(lflows, od, S_SWITCH_IN_PRE_LB, 0, "1", "next;"); - ovn_lflow_add(lflows, od, S_SWITCH_OUT_PRE_LB, 0, "1", "next;"); - -+ for (size_t i = 0; i < od->n_router_ports; i++) { -+ skip_port_from_conntrack(od, od->router_ports[i], -+ S_SWITCH_IN_PRE_LB, S_SWITCH_OUT_PRE_LB, -+ 110, lflows); -+ } -+ for (size_t i = 0; i < od->n_localnet_ports; i++) { -+ skip_port_from_conntrack(od, od->localnet_ports[i], -+ S_SWITCH_IN_PRE_LB, S_SWITCH_OUT_PRE_LB, -+ 110, lflows); -+ } -+ - struct sset all_ips_v4 = SSET_INITIALIZER(&all_ips_v4); - struct sset all_ips_v6 = SSET_INITIALIZER(&all_ips_v6); - bool vip_configured = false; -@@ -5725,13 +5741,18 @@ build_lb(struct ovn_datapath *od, struct hmap *lflows) - ovn_lflow_add(lflows, od, S_SWITCH_OUT_LB, 0, "1", "next;"); - - if (od->nbs->load_balancer) { -- /* Ingress and Egress LB Table (Priority 65535). -+ for (size_t i = 0; i < od->n_router_ports; i++) { -+ skip_port_from_conntrack(od, od->router_ports[i], -+ S_SWITCH_IN_LB, S_SWITCH_OUT_LB, -+ UINT16_MAX, lflows); -+ } -+ /* Ingress and Egress LB Table (Priority 65534). - * - * Send established traffic through conntrack for just NAT. */ -- ovn_lflow_add(lflows, od, S_SWITCH_IN_LB, UINT16_MAX, -+ ovn_lflow_add(lflows, od, S_SWITCH_IN_LB, UINT16_MAX - 1, - "ct.est && !ct.rel && !ct.new && !ct.inv", - REGBIT_CONNTRACK_NAT" = 1; next;"); -- ovn_lflow_add(lflows, od, S_SWITCH_OUT_LB, UINT16_MAX, -+ ovn_lflow_add(lflows, od, S_SWITCH_OUT_LB, UINT16_MAX - 1, - "ct.est && !ct.rel && !ct.new && !ct.inv", - REGBIT_CONNTRACK_NAT" = 1; next;"); - } --- -2.26.2 - diff --git a/SOURCES/0018-ovn-northd-Don-t-send-the-pkt-to-conntrack-for-NAT-i.patch b/SOURCES/0018-ovn-northd-Don-t-send-the-pkt-to-conntrack-for-NAT-i.patch deleted file mode 100644 index 17307a3..0000000 --- a/SOURCES/0018-ovn-northd-Don-t-send-the-pkt-to-conntrack-for-NAT-i.patch +++ /dev/null @@ -1,475 +0,0 @@ -From ad27f022b9bda5364f34c611ba116e0e18269f2c Mon Sep 17 00:00:00 2001 -From: Numan Siddique -Date: Tue, 4 Aug 2020 12:49:48 +0530 -Subject: [PATCH 18/22] ovn-northd: Don't send the pkt to conntrack for NAT if - its not destined for LB VIP. - -Presently when a logical switch has load balancer(s) associated to it, then the -packet is still sent to conntrack with the action ct_lb on both the ingress -and egress logical switch pipeline even if the destination IP is not LB VIP. - -This is because below logical flows are hit: - -In the ingress logical switch pipeline: - - table=9 (ls_in_lb ), priority=65535, match=(ct.est && !ct.rel && !ct.new && !ct.inv), action=(reg0[2] = 1; next;) - - table=10(ls_in_stateful ), priority=100 , match=(reg0[2] == 1), action=(ct_lb;) - -In the egress logical switch pipeline: - - table=3 (ls_out_lb ), priority=65535, match=(ct.est && !ct.rel && !ct.new && !ct.inv), action=(reg0[2] = 1; next;) - - table=7 (ls_out_stateful), priority=100 , match=(reg0[2] == 1), action=(ct_lb;) - -This patch avoid unnecessary ct actions by setting the ct_label.natted to 1 when the ct_lb(backends=...) action -is applied for NEW connections and updating the above logical flows to check for this mark: - - - table=9 (ls_in_lb), priority=65535, match=(ct.est && !ct.rel && !ct.new && !ct.inv && ct_label.natted == 1), - action=(reg0[2] = 1; next;) - - - table=3 (ls_out_lb), priority=65535, match=(ct.est && !ct.rel && !ct.new && !ct.inv && ct_label.natted == 1), - action=(reg0[2] = 1; next;) - -Acked-by: Dumitru Ceara -Signed-off-by: Numan Siddique ---- - lib/actions.c | 3 +- - lib/logical-fields.c | 6 +- - northd/ovn-northd.c | 6 +- - tests/ovn.at | 17 +++--- - tests/system-ovn.at | 130 +++++++++++++++++++++---------------------- - 5 files changed, 84 insertions(+), 78 deletions(-) - -diff --git a/lib/actions.c b/lib/actions.c -index 245486b0a..8c11b7b3f 100644 ---- a/lib/actions.c -+++ b/lib/actions.c -@@ -1098,7 +1098,8 @@ encode_CT_LB(const struct ovnact_ct_lb *cl, - if (dst->port) { - ds_put_format(&ds, ":%"PRIu16, dst->port); - } -- ds_put_format(&ds, "),commit,table=%d,zone=NXM_NX_REG%d[0..15])", -+ ds_put_format(&ds, "),commit,table=%d,zone=NXM_NX_REG%d[0..15]," -+ "exec(set_field:2/2->ct_label))", - recirc_table, zone_reg); - } - -diff --git a/lib/logical-fields.c b/lib/logical-fields.c -index 15342dded..bf61df771 100644 ---- a/lib/logical-fields.c -+++ b/lib/logical-fields.c -@@ -126,10 +126,12 @@ ovn_init_symtab(struct shash *symtab) - expr_symtab_add_field_scoped(symtab, "ct_mark", MFF_CT_MARK, NULL, false, - WR_CT_COMMIT); - -- expr_symtab_add_field_scoped(symtab, "ct_label", MFF_CT_LABEL, NULL, false, -- WR_CT_COMMIT); -+ expr_symtab_add_field_scoped(symtab, "ct_label", MFF_CT_LABEL, NULL, -+ false, WR_CT_COMMIT); - expr_symtab_add_subfield_scoped(symtab, "ct_label.blocked", NULL, - "ct_label[0]", WR_CT_COMMIT); -+ expr_symtab_add_subfield_scoped(symtab, "ct_label.natted", NULL, -+ "ct_label[1]", WR_CT_COMMIT); - expr_symtab_add_subfield_scoped(symtab, "ct_label.ecmp_reply_eth", NULL, - "ct_label[32..79]", WR_CT_COMMIT); - expr_symtab_add_subfield_scoped(symtab, "ct_label.ecmp_reply_port", NULL, -diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c -index 7b534ce3c..5f0abeee1 100644 ---- a/northd/ovn-northd.c -+++ b/northd/ovn-northd.c -@@ -5750,10 +5750,12 @@ build_lb(struct ovn_datapath *od, struct hmap *lflows) - * - * Send established traffic through conntrack for just NAT. */ - ovn_lflow_add(lflows, od, S_SWITCH_IN_LB, UINT16_MAX - 1, -- "ct.est && !ct.rel && !ct.new && !ct.inv", -+ "ct.est && !ct.rel && !ct.new && !ct.inv && " -+ "ct_label.natted == 1", - REGBIT_CONNTRACK_NAT" = 1; next;"); - ovn_lflow_add(lflows, od, S_SWITCH_OUT_LB, UINT16_MAX - 1, -- "ct.est && !ct.rel && !ct.new && !ct.inv", -+ "ct.est && !ct.rel && !ct.new && !ct.inv && " -+ "ct_label.natted == 1", - REGBIT_CONNTRACK_NAT" = 1; next;"); - } - } -diff --git a/tests/ovn.at b/tests/ovn.at -index 2651b3eac..0d99adf3f 100644 ---- a/tests/ovn.at -+++ b/tests/ovn.at -@@ -194,6 +194,7 @@ ct_label = NXM_NX_CT_LABEL - ct_label.blocked = ct_label[0] - ct_label.ecmp_reply_eth = ct_label[32..79] - ct_label.ecmp_reply_port = ct_label[80..95] -+ct_label.natted = ct_label[1] - ct_mark = NXM_NX_CT_MARK - ct_state = NXM_NX_CT_STATE - ]]) -@@ -996,17 +997,17 @@ ct_lb(192.168.1.2:80, 192.168.1.3:80); - Syntax error at `192.168.1.2' expecting backends. - ct_lb(backends=192.168.1.2:80,192.168.1.3:80); - encodes as group:1 -- uses group: id(1), name(type=select,selection_method=dp_hash,bucket=bucket_id=0,weight:100,actions=ct(nat(dst=192.168.1.2:80),commit,table=19,zone=NXM_NX_REG13[0..15]),bucket=bucket_id=1,weight:100,actions=ct(nat(dst=192.168.1.3:80),commit,table=19,zone=NXM_NX_REG13[0..15])) -+ uses group: id(1), name(type=select,selection_method=dp_hash,bucket=bucket_id=0,weight:100,actions=ct(nat(dst=192.168.1.2:80),commit,table=19,zone=NXM_NX_REG13[0..15],exec(set_field:2/2->ct_label)),bucket=bucket_id=1,weight:100,actions=ct(nat(dst=192.168.1.3:80),commit,table=19,zone=NXM_NX_REG13[0..15],exec(set_field:2/2->ct_label))) - has prereqs ip - ct_lb(backends=192.168.1.2, 192.168.1.3, ); - formats as ct_lb(backends=192.168.1.2,192.168.1.3); - encodes as group:2 -- uses group: id(2), name(type=select,selection_method=dp_hash,bucket=bucket_id=0,weight:100,actions=ct(nat(dst=192.168.1.2),commit,table=19,zone=NXM_NX_REG13[0..15]),bucket=bucket_id=1,weight:100,actions=ct(nat(dst=192.168.1.3),commit,table=19,zone=NXM_NX_REG13[0..15])) -+ uses group: id(2), name(type=select,selection_method=dp_hash,bucket=bucket_id=0,weight:100,actions=ct(nat(dst=192.168.1.2),commit,table=19,zone=NXM_NX_REG13[0..15],exec(set_field:2/2->ct_label)),bucket=bucket_id=1,weight:100,actions=ct(nat(dst=192.168.1.3),commit,table=19,zone=NXM_NX_REG13[0..15],exec(set_field:2/2->ct_label))) - has prereqs ip - ct_lb(backends=fd0f::2, fd0f::3, ); - formats as ct_lb(backends=fd0f::2,fd0f::3); - encodes as group:3 -- uses group: id(3), name(type=select,selection_method=dp_hash,bucket=bucket_id=0,weight:100,actions=ct(nat(dst=fd0f::2),commit,table=19,zone=NXM_NX_REG13[0..15]),bucket=bucket_id=1,weight:100,actions=ct(nat(dst=fd0f::3),commit,table=19,zone=NXM_NX_REG13[0..15])) -+ uses group: id(3), name(type=select,selection_method=dp_hash,bucket=bucket_id=0,weight:100,actions=ct(nat(dst=fd0f::2),commit,table=19,zone=NXM_NX_REG13[0..15],exec(set_field:2/2->ct_label)),bucket=bucket_id=1,weight:100,actions=ct(nat(dst=fd0f::3),commit,table=19,zone=NXM_NX_REG13[0..15],exec(set_field:2/2->ct_label))) - has prereqs ip - - ct_lb(backends=192.168.1.2:); -@@ -1022,23 +1023,23 @@ ct_lb(backends=192.168.1.2:80,192.168.1.3:80; hash_fields=eth_src,eth_dst,ip_src - Syntax error at `eth_src' invalid hash_fields. - ct_lb(backends=192.168.1.2:80,192.168.1.3:80; hash_fields="eth_src,eth_dst,ip_src"); - encodes as group:4 -- uses group: id(4), name(type=select,selection_method=hash,fields(eth_src,eth_dst,ip_src),bucket=bucket_id=0,weight:100,actions=ct(nat(dst=192.168.1.2:80),commit,table=19,zone=NXM_NX_REG13[0..15]),bucket=bucket_id=1,weight:100,actions=ct(nat(dst=192.168.1.3:80),commit,table=19,zone=NXM_NX_REG13[0..15])) -+ uses group: id(4), name(type=select,selection_method=hash,fields(eth_src,eth_dst,ip_src),bucket=bucket_id=0,weight:100,actions=ct(nat(dst=192.168.1.2:80),commit,table=19,zone=NXM_NX_REG13[0..15],exec(set_field:2/2->ct_label)),bucket=bucket_id=1,weight:100,actions=ct(nat(dst=192.168.1.3:80),commit,table=19,zone=NXM_NX_REG13[0..15],exec(set_field:2/2->ct_label))) - has prereqs ip - ct_lb(backends=fd0f::2,fd0f::3; hash_fields="eth_src,eth_dst,ip_src,ip_dst,tp_src,tp_dst"); - encodes as group:5 -- uses group: id(5), name(type=select,selection_method=hash,fields(eth_src,eth_dst,ip_src,ip_dst,tp_src,tp_dst),bucket=bucket_id=0,weight:100,actions=ct(nat(dst=fd0f::2),commit,table=19,zone=NXM_NX_REG13[0..15]),bucket=bucket_id=1,weight:100,actions=ct(nat(dst=fd0f::3),commit,table=19,zone=NXM_NX_REG13[0..15])) -+ uses group: id(5), name(type=select,selection_method=hash,fields(eth_src,eth_dst,ip_src,ip_dst,tp_src,tp_dst),bucket=bucket_id=0,weight:100,actions=ct(nat(dst=fd0f::2),commit,table=19,zone=NXM_NX_REG13[0..15],exec(set_field:2/2->ct_label)),bucket=bucket_id=1,weight:100,actions=ct(nat(dst=fd0f::3),commit,table=19,zone=NXM_NX_REG13[0..15],exec(set_field:2/2->ct_label))) - has prereqs ip - ct_lb(backends=fd0f::2,fd0f::3; hash_fields="eth_src,eth_dst,ip_src,ip_dst,tcp_src,tcp_dst"); - encodes as group:6 -- uses group: id(6), name(type=select,selection_method=hash,fields(eth_src,eth_dst,ip_src,ip_dst,tcp_src,tcp_dst),bucket=bucket_id=0,weight:100,actions=ct(nat(dst=fd0f::2),commit,table=19,zone=NXM_NX_REG13[0..15]),bucket=bucket_id=1,weight:100,actions=ct(nat(dst=fd0f::3),commit,table=19,zone=NXM_NX_REG13[0..15])) -+ uses group: id(6), name(type=select,selection_method=hash,fields(eth_src,eth_dst,ip_src,ip_dst,tcp_src,tcp_dst),bucket=bucket_id=0,weight:100,actions=ct(nat(dst=fd0f::2),commit,table=19,zone=NXM_NX_REG13[0..15],exec(set_field:2/2->ct_label)),bucket=bucket_id=1,weight:100,actions=ct(nat(dst=fd0f::3),commit,table=19,zone=NXM_NX_REG13[0..15],exec(set_field:2/2->ct_label))) - has prereqs ip - ct_lb(backends=fd0f::2,fd0f::3; hash_fields="eth_src,eth_dst,ip_src,ip_dst,udp_src,udp_dst"); - encodes as group:7 -- uses group: id(7), name(type=select,selection_method=hash,fields(eth_src,eth_dst,ip_src,ip_dst,udp_src,udp_dst),bucket=bucket_id=0,weight:100,actions=ct(nat(dst=fd0f::2),commit,table=19,zone=NXM_NX_REG13[0..15]),bucket=bucket_id=1,weight:100,actions=ct(nat(dst=fd0f::3),commit,table=19,zone=NXM_NX_REG13[0..15])) -+ uses group: id(7), name(type=select,selection_method=hash,fields(eth_src,eth_dst,ip_src,ip_dst,udp_src,udp_dst),bucket=bucket_id=0,weight:100,actions=ct(nat(dst=fd0f::2),commit,table=19,zone=NXM_NX_REG13[0..15],exec(set_field:2/2->ct_label)),bucket=bucket_id=1,weight:100,actions=ct(nat(dst=fd0f::3),commit,table=19,zone=NXM_NX_REG13[0..15],exec(set_field:2/2->ct_label))) - has prereqs ip - ct_lb(backends=fd0f::2,fd0f::3; hash_fields="eth_src,eth_dst,ip_src,ip_dst,sctp_src,sctp_dst"); - encodes as group:8 -- uses group: id(8), name(type=select,selection_method=hash,fields(eth_src,eth_dst,ip_src,ip_dst,sctp_src,sctp_dst),bucket=bucket_id=0,weight:100,actions=ct(nat(dst=fd0f::2),commit,table=19,zone=NXM_NX_REG13[0..15]),bucket=bucket_id=1,weight:100,actions=ct(nat(dst=fd0f::3),commit,table=19,zone=NXM_NX_REG13[0..15])) -+ uses group: id(8), name(type=select,selection_method=hash,fields(eth_src,eth_dst,ip_src,ip_dst,sctp_src,sctp_dst),bucket=bucket_id=0,weight:100,actions=ct(nat(dst=fd0f::2),commit,table=19,zone=NXM_NX_REG13[0..15],exec(set_field:2/2->ct_label)),bucket=bucket_id=1,weight:100,actions=ct(nat(dst=fd0f::3),commit,table=19,zone=NXM_NX_REG13[0..15],exec(set_field:2/2->ct_label))) - has prereqs ip - - # ct_next -diff --git a/tests/system-ovn.at b/tests/system-ovn.at -index 0d478b4aa..d0ffb5c1a 100644 ---- a/tests/system-ovn.at -+++ b/tests/system-ovn.at -@@ -1441,9 +1441,9 @@ done - dnl Each server should have at least one connection. - AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(30.0.0.1) | \ - sed -e 's/zone=[[0-9]]*/zone=/'], [0], [dnl --tcp,orig=(src=192.168.1.2,dst=30.0.0.1,sport=,dport=),reply=(src=172.16.1.2,dst=192.168.1.2,sport=,dport=),zone=,protoinfo=(state=) --tcp,orig=(src=192.168.1.2,dst=30.0.0.1,sport=,dport=),reply=(src=172.16.1.3,dst=192.168.1.2,sport=,dport=),zone=,protoinfo=(state=) --tcp,orig=(src=192.168.1.2,dst=30.0.0.1,sport=,dport=),reply=(src=172.16.1.4,dst=192.168.1.2,sport=,dport=),zone=,protoinfo=(state=) -+tcp,orig=(src=192.168.1.2,dst=30.0.0.1,sport=,dport=),reply=(src=172.16.1.2,dst=192.168.1.2,sport=,dport=),zone=,labels=0x2,protoinfo=(state=) -+tcp,orig=(src=192.168.1.2,dst=30.0.0.1,sport=,dport=),reply=(src=172.16.1.3,dst=192.168.1.2,sport=,dport=),zone=,labels=0x2,protoinfo=(state=) -+tcp,orig=(src=192.168.1.2,dst=30.0.0.1,sport=,dport=),reply=(src=172.16.1.4,dst=192.168.1.2,sport=,dport=),zone=,labels=0x2,protoinfo=(state=) - ]) - - dnl Should work with the virtual IP 30.0.0.3 address through NAT -@@ -1455,9 +1455,9 @@ done - dnl Each server should have at least one connection. - AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(30.0.0.3) | \ - sed -e 's/zone=[[0-9]]*/zone=/'], [0], [dnl --tcp,orig=(src=192.168.1.2,dst=30.0.0.3,sport=,dport=),reply=(src=172.16.1.2,dst=192.168.1.2,sport=,dport=),zone=,protoinfo=(state=) --tcp,orig=(src=192.168.1.2,dst=30.0.0.3,sport=,dport=),reply=(src=172.16.1.3,dst=192.168.1.2,sport=,dport=),zone=,protoinfo=(state=) --tcp,orig=(src=192.168.1.2,dst=30.0.0.3,sport=,dport=),reply=(src=172.16.1.4,dst=192.168.1.2,sport=,dport=),zone=,protoinfo=(state=) -+tcp,orig=(src=192.168.1.2,dst=30.0.0.3,sport=,dport=),reply=(src=172.16.1.2,dst=192.168.1.2,sport=,dport=),zone=,labels=0x2,protoinfo=(state=) -+tcp,orig=(src=192.168.1.2,dst=30.0.0.3,sport=,dport=),reply=(src=172.16.1.3,dst=192.168.1.2,sport=,dport=),zone=,labels=0x2,protoinfo=(state=) -+tcp,orig=(src=192.168.1.2,dst=30.0.0.3,sport=,dport=),reply=(src=172.16.1.4,dst=192.168.1.2,sport=,dport=),zone=,labels=0x2,protoinfo=(state=) - ]) - - dnl Test load-balancing that includes L4 ports in NAT. -@@ -1469,9 +1469,9 @@ done - dnl Each server should have at least one connection. - AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(30.0.0.2) | \ - sed -e 's/zone=[[0-9]]*/zone=/'], [0], [dnl --tcp,orig=(src=192.168.1.2,dst=30.0.0.2,sport=,dport=),reply=(src=172.16.1.2,dst=192.168.1.2,sport=,dport=),zone=,protoinfo=(state=) --tcp,orig=(src=192.168.1.2,dst=30.0.0.2,sport=,dport=),reply=(src=172.16.1.3,dst=192.168.1.2,sport=,dport=),zone=,protoinfo=(state=) --tcp,orig=(src=192.168.1.2,dst=30.0.0.2,sport=,dport=),reply=(src=172.16.1.4,dst=192.168.1.2,sport=,dport=),zone=,protoinfo=(state=) -+tcp,orig=(src=192.168.1.2,dst=30.0.0.2,sport=,dport=),reply=(src=172.16.1.2,dst=192.168.1.2,sport=,dport=),zone=,labels=0x2,protoinfo=(state=) -+tcp,orig=(src=192.168.1.2,dst=30.0.0.2,sport=,dport=),reply=(src=172.16.1.3,dst=192.168.1.2,sport=,dport=),zone=,labels=0x2,protoinfo=(state=) -+tcp,orig=(src=192.168.1.2,dst=30.0.0.2,sport=,dport=),reply=(src=172.16.1.4,dst=192.168.1.2,sport=,dport=),zone=,labels=0x2,protoinfo=(state=) - ]) - - # Configure selection_fields. -@@ -1492,9 +1492,9 @@ done - dnl Each server should have at least one connection. - AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(30.0.0.2) | \ - sed -e 's/zone=[[0-9]]*/zone=/'], [0], [dnl --tcp,orig=(src=192.168.1.2,dst=30.0.0.2,sport=,dport=),reply=(src=172.16.1.2,dst=192.168.1.2,sport=,dport=),zone=,protoinfo=(state=) --tcp,orig=(src=192.168.1.2,dst=30.0.0.2,sport=,dport=),reply=(src=172.16.1.3,dst=192.168.1.2,sport=,dport=),zone=,protoinfo=(state=) --tcp,orig=(src=192.168.1.2,dst=30.0.0.2,sport=,dport=),reply=(src=172.16.1.4,dst=192.168.1.2,sport=,dport=),zone=,protoinfo=(state=) -+tcp,orig=(src=192.168.1.2,dst=30.0.0.2,sport=,dport=),reply=(src=172.16.1.2,dst=192.168.1.2,sport=,dport=),zone=,labels=0x2,protoinfo=(state=) -+tcp,orig=(src=192.168.1.2,dst=30.0.0.2,sport=,dport=),reply=(src=172.16.1.3,dst=192.168.1.2,sport=,dport=),zone=,labels=0x2,protoinfo=(state=) -+tcp,orig=(src=192.168.1.2,dst=30.0.0.2,sport=,dport=),reply=(src=172.16.1.4,dst=192.168.1.2,sport=,dport=),zone=,labels=0x2,protoinfo=(state=) - ]) - - AT_CHECK([ovs-appctl dpctl/flush-conntrack]) -@@ -1687,9 +1687,9 @@ done - dnl Each server should have at least one connection. - AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd03::1) | grep -v fe80 | \ - sed -e 's/zone=[[0-9]]*/zone=/'], [0], [dnl --tcp,orig=(src=fd01::2,dst=fd03::1,sport=,dport=),reply=(src=fd02::2,dst=fd01::2,sport=,dport=),zone=,protoinfo=(state=) --tcp,orig=(src=fd01::2,dst=fd03::1,sport=,dport=),reply=(src=fd02::3,dst=fd01::2,sport=,dport=),zone=,protoinfo=(state=) --tcp,orig=(src=fd01::2,dst=fd03::1,sport=,dport=),reply=(src=fd02::4,dst=fd01::2,sport=,dport=),zone=,protoinfo=(state=) -+tcp,orig=(src=fd01::2,dst=fd03::1,sport=,dport=),reply=(src=fd02::2,dst=fd01::2,sport=,dport=),zone=,labels=0x2,protoinfo=(state=) -+tcp,orig=(src=fd01::2,dst=fd03::1,sport=,dport=),reply=(src=fd02::3,dst=fd01::2,sport=,dport=),zone=,labels=0x2,protoinfo=(state=) -+tcp,orig=(src=fd01::2,dst=fd03::1,sport=,dport=),reply=(src=fd02::4,dst=fd01::2,sport=,dport=),zone=,labels=0x2,protoinfo=(state=) - ]) - - dnl Should work with the virtual IP fd03::3 address through NAT -@@ -1701,9 +1701,9 @@ done - dnl Each server should have at least one connection. - AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd03::3) | grep -v fe80 | \ - sed -e 's/zone=[[0-9]]*/zone=/'], [0], [dnl --tcp,orig=(src=fd01::2,dst=fd03::3,sport=,dport=),reply=(src=fd02::2,dst=fd01::2,sport=,dport=),zone=,protoinfo=(state=) --tcp,orig=(src=fd01::2,dst=fd03::3,sport=,dport=),reply=(src=fd02::3,dst=fd01::2,sport=,dport=),zone=,protoinfo=(state=) --tcp,orig=(src=fd01::2,dst=fd03::3,sport=,dport=),reply=(src=fd02::4,dst=fd01::2,sport=,dport=),zone=,protoinfo=(state=) -+tcp,orig=(src=fd01::2,dst=fd03::3,sport=,dport=),reply=(src=fd02::2,dst=fd01::2,sport=,dport=),zone=,labels=0x2,protoinfo=(state=) -+tcp,orig=(src=fd01::2,dst=fd03::3,sport=,dport=),reply=(src=fd02::3,dst=fd01::2,sport=,dport=),zone=,labels=0x2,protoinfo=(state=) -+tcp,orig=(src=fd01::2,dst=fd03::3,sport=,dport=),reply=(src=fd02::4,dst=fd01::2,sport=,dport=),zone=,labels=0x2,protoinfo=(state=) - ]) - - dnl Test load-balancing that includes L4 ports in NAT. -@@ -1715,9 +1715,9 @@ done - dnl Each server should have at least one connection. - AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd03::2) | grep -v fe80 | \ - sed -e 's/zone=[[0-9]]*/zone=/'], [0], [dnl --tcp,orig=(src=fd01::2,dst=fd03::2,sport=,dport=),reply=(src=fd02::2,dst=fd01::2,sport=,dport=),zone=,protoinfo=(state=) --tcp,orig=(src=fd01::2,dst=fd03::2,sport=,dport=),reply=(src=fd02::3,dst=fd01::2,sport=,dport=),zone=,protoinfo=(state=) --tcp,orig=(src=fd01::2,dst=fd03::2,sport=,dport=),reply=(src=fd02::4,dst=fd01::2,sport=,dport=),zone=,protoinfo=(state=) -+tcp,orig=(src=fd01::2,dst=fd03::2,sport=,dport=),reply=(src=fd02::2,dst=fd01::2,sport=,dport=),zone=,labels=0x2,protoinfo=(state=) -+tcp,orig=(src=fd01::2,dst=fd03::2,sport=,dport=),reply=(src=fd02::3,dst=fd01::2,sport=,dport=),zone=,labels=0x2,protoinfo=(state=) -+tcp,orig=(src=fd01::2,dst=fd03::2,sport=,dport=),reply=(src=fd02::4,dst=fd01::2,sport=,dport=),zone=,labels=0x2,protoinfo=(state=) - ]) - - # Configure selection_fields. -@@ -1738,9 +1738,9 @@ done - dnl Each server should have at least one connection. - AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd03::2) | grep -v fe80 | \ - sed -e 's/zone=[[0-9]]*/zone=/'], [0], [dnl --tcp,orig=(src=fd01::2,dst=fd03::2,sport=,dport=),reply=(src=fd02::2,dst=fd01::2,sport=,dport=),zone=,protoinfo=(state=) --tcp,orig=(src=fd01::2,dst=fd03::2,sport=,dport=),reply=(src=fd02::3,dst=fd01::2,sport=,dport=),zone=,protoinfo=(state=) --tcp,orig=(src=fd01::2,dst=fd03::2,sport=,dport=),reply=(src=fd02::4,dst=fd01::2,sport=,dport=),zone=,protoinfo=(state=) -+tcp,orig=(src=fd01::2,dst=fd03::2,sport=,dport=),reply=(src=fd02::2,dst=fd01::2,sport=,dport=),zone=,labels=0x2,protoinfo=(state=) -+tcp,orig=(src=fd01::2,dst=fd03::2,sport=,dport=),reply=(src=fd02::3,dst=fd01::2,sport=,dport=),zone=,labels=0x2,protoinfo=(state=) -+tcp,orig=(src=fd01::2,dst=fd03::2,sport=,dport=),reply=(src=fd02::4,dst=fd01::2,sport=,dport=),zone=,labels=0x2,protoinfo=(state=) - ]) - - AT_CHECK([ovs-appctl dpctl/flush-conntrack]) -@@ -1884,9 +1884,9 @@ done - dnl Each server should have at least one connection. - AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(30.0.0.1) | \ - sed -e 's/zone=[[0-9]]*/zone=/'], [0], [dnl --tcp,orig=(src=192.168.1.2,dst=30.0.0.1,sport=,dport=),reply=(src=192.168.1.3,dst=192.168.1.2,sport=,dport=),zone=,protoinfo=(state=) --tcp,orig=(src=192.168.1.2,dst=30.0.0.1,sport=,dport=),reply=(src=192.168.1.4,dst=192.168.1.2,sport=,dport=),zone=,protoinfo=(state=) --tcp,orig=(src=192.168.1.2,dst=30.0.0.1,sport=,dport=),reply=(src=192.168.1.5,dst=192.168.1.2,sport=,dport=),zone=,protoinfo=(state=) -+tcp,orig=(src=192.168.1.2,dst=30.0.0.1,sport=,dport=),reply=(src=192.168.1.3,dst=192.168.1.2,sport=,dport=),zone=,labels=0x2,protoinfo=(state=) -+tcp,orig=(src=192.168.1.2,dst=30.0.0.1,sport=,dport=),reply=(src=192.168.1.4,dst=192.168.1.2,sport=,dport=),zone=,labels=0x2,protoinfo=(state=) -+tcp,orig=(src=192.168.1.2,dst=30.0.0.1,sport=,dport=),reply=(src=192.168.1.5,dst=192.168.1.2,sport=,dport=),zone=,labels=0x2,protoinfo=(state=) - ]) - - dnl Test load-balancing that includes L4 ports in NAT. -@@ -1898,9 +1898,9 @@ done - dnl Each server should have at least one connection. - AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(30.0.0.2) | \ - sed -e 's/zone=[[0-9]]*/zone=/'], [0], [dnl --tcp,orig=(src=192.168.1.2,dst=30.0.0.2,sport=,dport=),reply=(src=192.168.1.3,dst=192.168.1.2,sport=,dport=),zone=,protoinfo=(state=) --tcp,orig=(src=192.168.1.2,dst=30.0.0.2,sport=,dport=),reply=(src=192.168.1.4,dst=192.168.1.2,sport=,dport=),zone=,protoinfo=(state=) --tcp,orig=(src=192.168.1.2,dst=30.0.0.2,sport=,dport=),reply=(src=192.168.1.5,dst=192.168.1.2,sport=,dport=),zone=,protoinfo=(state=) -+tcp,orig=(src=192.168.1.2,dst=30.0.0.2,sport=,dport=),reply=(src=192.168.1.3,dst=192.168.1.2,sport=,dport=),zone=,labels=0x2,protoinfo=(state=) -+tcp,orig=(src=192.168.1.2,dst=30.0.0.2,sport=,dport=),reply=(src=192.168.1.4,dst=192.168.1.2,sport=,dport=),zone=,labels=0x2,protoinfo=(state=) -+tcp,orig=(src=192.168.1.2,dst=30.0.0.2,sport=,dport=),reply=(src=192.168.1.5,dst=192.168.1.2,sport=,dport=),zone=,labels=0x2,protoinfo=(state=) - ]) - - -@@ -1993,9 +1993,9 @@ done - dnl Each server should have at least one connection. - AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd03::1) | grep -v fe80 | \ - sed -e 's/zone=[[0-9]]*/zone=/'], [0], [dnl --tcp,orig=(src=fd01::2,dst=fd03::1,sport=,dport=),reply=(src=fd01::3,dst=fd01::2,sport=,dport=),zone=,protoinfo=(state=) --tcp,orig=(src=fd01::2,dst=fd03::1,sport=,dport=),reply=(src=fd01::4,dst=fd01::2,sport=,dport=),zone=,protoinfo=(state=) --tcp,orig=(src=fd01::2,dst=fd03::1,sport=,dport=),reply=(src=fd01::5,dst=fd01::2,sport=,dport=),zone=,protoinfo=(state=) -+tcp,orig=(src=fd01::2,dst=fd03::1,sport=,dport=),reply=(src=fd01::3,dst=fd01::2,sport=,dport=),zone=,labels=0x2,protoinfo=(state=) -+tcp,orig=(src=fd01::2,dst=fd03::1,sport=,dport=),reply=(src=fd01::4,dst=fd01::2,sport=,dport=),zone=,labels=0x2,protoinfo=(state=) -+tcp,orig=(src=fd01::2,dst=fd03::1,sport=,dport=),reply=(src=fd01::5,dst=fd01::2,sport=,dport=),zone=,labels=0x2,protoinfo=(state=) - ]) - - dnl Test load-balancing that includes L4 ports in NAT. -@@ -2007,9 +2007,9 @@ done - dnl Each server should have at least one connection. - AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd03::2) | grep -v fe80 | \ - sed -e 's/zone=[[0-9]]*/zone=/'], [0], [dnl --tcp,orig=(src=fd01::2,dst=fd03::2,sport=,dport=),reply=(src=fd01::3,dst=fd01::2,sport=,dport=),zone=,protoinfo=(state=) --tcp,orig=(src=fd01::2,dst=fd03::2,sport=,dport=),reply=(src=fd01::4,dst=fd01::2,sport=,dport=),zone=,protoinfo=(state=) --tcp,orig=(src=fd01::2,dst=fd03::2,sport=,dport=),reply=(src=fd01::5,dst=fd01::2,sport=,dport=),zone=,protoinfo=(state=) -+tcp,orig=(src=fd01::2,dst=fd03::2,sport=,dport=),reply=(src=fd01::3,dst=fd01::2,sport=,dport=),zone=,labels=0x2,protoinfo=(state=) -+tcp,orig=(src=fd01::2,dst=fd03::2,sport=,dport=),reply=(src=fd01::4,dst=fd01::2,sport=,dport=),zone=,labels=0x2,protoinfo=(state=) -+tcp,orig=(src=fd01::2,dst=fd03::2,sport=,dport=),reply=(src=fd01::5,dst=fd01::2,sport=,dport=),zone=,labels=0x2,protoinfo=(state=) - ]) - - -@@ -2145,8 +2145,8 @@ done - dnl Each server should have at least one connection. - AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(30.0.0.1) | - sed -e 's/zone=[[0-9]]*/zone=/'], [0], [dnl --tcp,orig=(src=172.16.1.2,dst=30.0.0.1,sport=,dport=),reply=(src=192.168.1.2,dst=172.16.1.2,sport=,dport=),zone=,protoinfo=(state=) --tcp,orig=(src=172.16.1.2,dst=30.0.0.1,sport=,dport=),reply=(src=192.168.2.2,dst=172.16.1.2,sport=,dport=),zone=,protoinfo=(state=) -+tcp,orig=(src=172.16.1.2,dst=30.0.0.1,sport=,dport=),reply=(src=192.168.1.2,dst=172.16.1.2,sport=,dport=),zone=,labels=0x2,protoinfo=(state=) -+tcp,orig=(src=172.16.1.2,dst=30.0.0.1,sport=,dport=),reply=(src=192.168.2.2,dst=172.16.1.2,sport=,dport=),zone=,labels=0x2,protoinfo=(state=) - ]) - - dnl Test load-balancing that includes L4 ports in NAT. -@@ -2158,8 +2158,8 @@ done - dnl Each server should have at least one connection. - AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(30.0.0.2) | - sed -e 's/zone=[[0-9]]*/zone=/'], [0], [dnl --tcp,orig=(src=172.16.1.2,dst=30.0.0.2,sport=,dport=),reply=(src=192.168.1.2,dst=172.16.1.2,sport=,dport=),zone=,protoinfo=(state=) --tcp,orig=(src=172.16.1.2,dst=30.0.0.2,sport=,dport=),reply=(src=192.168.2.2,dst=172.16.1.2,sport=,dport=),zone=,protoinfo=(state=) -+tcp,orig=(src=172.16.1.2,dst=30.0.0.2,sport=,dport=),reply=(src=192.168.1.2,dst=172.16.1.2,sport=,dport=),zone=,labels=0x2,protoinfo=(state=) -+tcp,orig=(src=172.16.1.2,dst=30.0.0.2,sport=,dport=),reply=(src=192.168.2.2,dst=172.16.1.2,sport=,dport=),zone=,labels=0x2,protoinfo=(state=) - ]) - - check_est_flows () { -@@ -2200,8 +2200,8 @@ done - dnl Each server should have at least one connection. - AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(30.0.0.2) | - sed -e 's/zone=[[0-9]]*/zone=/'], [0], [dnl --tcp,orig=(src=172.16.1.2,dst=30.0.0.2,sport=,dport=),reply=(src=192.168.1.2,dst=172.16.1.2,sport=,dport=),zone=,protoinfo=(state=) --tcp,orig=(src=172.16.1.2,dst=30.0.0.2,sport=,dport=),reply=(src=192.168.2.2,dst=172.16.1.2,sport=,dport=),zone=,protoinfo=(state=) -+tcp,orig=(src=172.16.1.2,dst=30.0.0.2,sport=,dport=),reply=(src=192.168.1.2,dst=172.16.1.2,sport=,dport=),zone=,labels=0x2,protoinfo=(state=) -+tcp,orig=(src=172.16.1.2,dst=30.0.0.2,sport=,dport=),reply=(src=192.168.2.2,dst=172.16.1.2,sport=,dport=),zone=,labels=0x2,protoinfo=(state=) - ]) - - AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(20.0.0.2) | -@@ -2349,8 +2349,8 @@ done - dnl Each server should have at least one connection. - AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd30::1) | grep -v fe80 | - sed -e 's/zone=[[0-9]]*/zone=/'], [0], [dnl --tcp,orig=(src=fd72::2,dst=fd30::1,sport=,dport=),reply=(src=fd11::2,dst=fd72::2,sport=,dport=),zone=,protoinfo=(state=) --tcp,orig=(src=fd72::2,dst=fd30::1,sport=,dport=),reply=(src=fd12::2,dst=fd72::2,sport=,dport=),zone=,protoinfo=(state=) -+tcp,orig=(src=fd72::2,dst=fd30::1,sport=,dport=),reply=(src=fd11::2,dst=fd72::2,sport=,dport=),zone=,labels=0x2,protoinfo=(state=) -+tcp,orig=(src=fd72::2,dst=fd30::1,sport=,dport=),reply=(src=fd12::2,dst=fd72::2,sport=,dport=),zone=,labels=0x2,protoinfo=(state=) - ]) - - dnl Test load-balancing that includes L4 ports in NAT. -@@ -2362,8 +2362,8 @@ done - dnl Each server should have at least one connection. - AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd30::2) | grep -v fe80 | - sed -e 's/zone=[[0-9]]*/zone=/'], [0], [dnl --tcp,orig=(src=fd72::2,dst=fd30::2,sport=,dport=),reply=(src=fd11::2,dst=fd72::2,sport=,dport=),zone=,protoinfo=(state=) --tcp,orig=(src=fd72::2,dst=fd30::2,sport=,dport=),reply=(src=fd12::2,dst=fd72::2,sport=,dport=),zone=,protoinfo=(state=) -+tcp,orig=(src=fd72::2,dst=fd30::2,sport=,dport=),reply=(src=fd11::2,dst=fd72::2,sport=,dport=),zone=,labels=0x2,protoinfo=(state=) -+tcp,orig=(src=fd72::2,dst=fd30::2,sport=,dport=),reply=(src=fd12::2,dst=fd72::2,sport=,dport=),zone=,labels=0x2,protoinfo=(state=) - ]) - - OVS_APP_EXIT_AND_WAIT([ovn-controller]) -@@ -2525,8 +2525,8 @@ done - dnl Each server should have at least one connection. - AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(30.0.0.1) | - sed -e 's/zone=[[0-9]]*/zone=/'], [0], [dnl --tcp,orig=(src=172.16.1.3,dst=30.0.0.1,sport=,dport=),reply=(src=192.168.1.2,dst=172.16.1.3,sport=,dport=),zone=,protoinfo=(state=) --tcp,orig=(src=172.16.1.3,dst=30.0.0.1,sport=,dport=),reply=(src=192.168.2.2,dst=172.16.1.3,sport=,dport=),zone=,protoinfo=(state=) -+tcp,orig=(src=172.16.1.3,dst=30.0.0.1,sport=,dport=),reply=(src=192.168.1.2,dst=172.16.1.3,sport=,dport=),zone=,labels=0x2,protoinfo=(state=) -+tcp,orig=(src=172.16.1.3,dst=30.0.0.1,sport=,dport=),reply=(src=192.168.2.2,dst=172.16.1.3,sport=,dport=),zone=,labels=0x2,protoinfo=(state=) - ]) - - dnl Force SNAT should have worked. -@@ -2696,8 +2696,8 @@ done - dnl Each server should have at least one connection. - AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd30::1) | grep -v fe80 | - sed -e 's/zone=[[0-9]]*/zone=/'], [0], [dnl --tcp,orig=(src=fd72::3,dst=fd30::1,sport=,dport=),reply=(src=fd11::2,dst=fd72::3,sport=,dport=),zone=,protoinfo=(state=) --tcp,orig=(src=fd72::3,dst=fd30::1,sport=,dport=),reply=(src=fd12::2,dst=fd72::3,sport=,dport=),zone=,protoinfo=(state=) -+tcp,orig=(src=fd72::3,dst=fd30::1,sport=,dport=),reply=(src=fd11::2,dst=fd72::3,sport=,dport=),zone=,labels=0x2,protoinfo=(state=) -+tcp,orig=(src=fd72::3,dst=fd30::1,sport=,dport=),reply=(src=fd12::2,dst=fd72::3,sport=,dport=),zone=,labels=0x2,protoinfo=(state=) - ]) - - dnl Force SNAT should have worked. -@@ -2910,13 +2910,13 @@ done - dnl Each server should have at least one connection. - AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(30.0.0.1) | - sed -e 's/zone=[[0-9]]*/zone=/'], [0], [dnl --tcp,orig=(src=172.16.1.3,dst=30.0.0.1,sport=,dport=),reply=(src=192.168.1.2,dst=172.16.1.3,sport=,dport=),zone=,protoinfo=(state=) --tcp,orig=(src=172.16.1.3,dst=30.0.0.1,sport=,dport=),reply=(src=192.168.2.2,dst=172.16.1.3,sport=,dport=),zone=,protoinfo=(state=) -+tcp,orig=(src=172.16.1.3,dst=30.0.0.1,sport=,dport=),reply=(src=192.168.1.2,dst=172.16.1.3,sport=,dport=),zone=,labels=0x2,protoinfo=(state=) -+tcp,orig=(src=172.16.1.3,dst=30.0.0.1,sport=,dport=),reply=(src=192.168.2.2,dst=172.16.1.3,sport=,dport=),zone=,labels=0x2,protoinfo=(state=) - ]) - AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd30::1) | grep -v fe80 | - sed -e 's/zone=[[0-9]]*/zone=/'], [0], [dnl --tcp,orig=(src=fd72::3,dst=fd30::1,sport=,dport=),reply=(src=fd11::2,dst=fd72::3,sport=,dport=),zone=,protoinfo=(state=) --tcp,orig=(src=fd72::3,dst=fd30::1,sport=,dport=),reply=(src=fd12::2,dst=fd72::3,sport=,dport=),zone=,protoinfo=(state=) -+tcp,orig=(src=fd72::3,dst=fd30::1,sport=,dport=),reply=(src=fd11::2,dst=fd72::3,sport=,dport=),zone=,labels=0x2,protoinfo=(state=) -+tcp,orig=(src=fd72::3,dst=fd30::1,sport=,dport=),reply=(src=fd12::2,dst=fd72::3,sport=,dport=),zone=,labels=0x2,protoinfo=(state=) - ]) - - dnl Force SNAT should have worked. -@@ -3054,8 +3054,8 @@ done - dnl Each server should have at least one connection. - AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(172.16.1.10) | - sed -e 's/zone=[[0-9]]*/zone=/'], [0], [dnl --tcp,orig=(src=172.16.1.2,dst=172.16.1.10,sport=,dport=),reply=(src=192.168.1.2,dst=172.16.1.2,sport=,dport=),zone=,protoinfo=(state=) --tcp,orig=(src=172.16.1.2,dst=172.16.1.10,sport=,dport=),reply=(src=192.168.2.2,dst=172.16.1.2,sport=,dport=),zone=,protoinfo=(state=) -+tcp,orig=(src=172.16.1.2,dst=172.16.1.10,sport=,dport=),reply=(src=192.168.1.2,dst=172.16.1.2,sport=,dport=),zone=,labels=0x2,protoinfo=(state=) -+tcp,orig=(src=172.16.1.2,dst=172.16.1.10,sport=,dport=),reply=(src=192.168.2.2,dst=172.16.1.2,sport=,dport=),zone=,labels=0x2,protoinfo=(state=) - ]) - - dnl Test load-balancing that includes L4 ports in NAT. -@@ -3067,8 +3067,8 @@ done - dnl Each server should have at least one connection. - AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(172.16.1.11) | - sed -e 's/zone=[[0-9]]*/zone=/'], [0], [dnl --tcp,orig=(src=172.16.1.2,dst=172.16.1.11,sport=,dport=),reply=(src=192.168.1.2,dst=172.16.1.2,sport=,dport=),zone=,protoinfo=(state=) --tcp,orig=(src=172.16.1.2,dst=172.16.1.11,sport=,dport=),reply=(src=192.168.2.2,dst=172.16.1.2,sport=,dport=),zone=,protoinfo=(state=) -+tcp,orig=(src=172.16.1.2,dst=172.16.1.11,sport=,dport=),reply=(src=192.168.1.2,dst=172.16.1.2,sport=,dport=),zone=,labels=0x2,protoinfo=(state=) -+tcp,orig=(src=172.16.1.2,dst=172.16.1.11,sport=,dport=),reply=(src=192.168.2.2,dst=172.16.1.2,sport=,dport=),zone=,labels=0x2,protoinfo=(state=) - ]) - - OVS_APP_EXIT_AND_WAIT([ovn-controller]) -@@ -3195,8 +3195,8 @@ done - dnl Each server should have at least one connection. - AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd72::10) | grep -v fe80 | - sed -e 's/zone=[[0-9]]*/zone=/'], [0], [dnl --tcp,orig=(src=fd72::2,dst=fd72::10,sport=,dport=),reply=(src=fd01::2,dst=fd72::2,sport=,dport=),zone=,protoinfo=(state=) --tcp,orig=(src=fd72::2,dst=fd72::10,sport=,dport=),reply=(src=fd02::2,dst=fd72::2,sport=,dport=),zone=,protoinfo=(state=) -+tcp,orig=(src=fd72::2,dst=fd72::10,sport=,dport=),reply=(src=fd01::2,dst=fd72::2,sport=,dport=),zone=,labels=0x2,protoinfo=(state=) -+tcp,orig=(src=fd72::2,dst=fd72::10,sport=,dport=),reply=(src=fd02::2,dst=fd72::2,sport=,dport=),zone=,labels=0x2,protoinfo=(state=) - ]) - - dnl Test load-balancing that includes L4 ports in NAT. -@@ -3208,8 +3208,8 @@ done - dnl Each server should have at least one connection. - AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd72::11) | grep -v fe80 | - sed -e 's/zone=[[0-9]]*/zone=/'], [0], [dnl --tcp,orig=(src=fd72::2,dst=fd72::11,sport=,dport=),reply=(src=fd01::2,dst=fd72::2,sport=,dport=),zone=,protoinfo=(state=) --tcp,orig=(src=fd72::2,dst=fd72::11,sport=,dport=),reply=(src=fd02::2,dst=fd72::2,sport=,dport=),zone=,protoinfo=(state=) -+tcp,orig=(src=fd72::2,dst=fd72::11,sport=,dport=),reply=(src=fd01::2,dst=fd72::2,sport=,dport=),zone=,labels=0x2,protoinfo=(state=) -+tcp,orig=(src=fd72::2,dst=fd72::11,sport=,dport=),reply=(src=fd02::2,dst=fd72::2,sport=,dport=),zone=,labels=0x2,protoinfo=(state=) - ]) - - OVS_APP_EXIT_AND_WAIT([ovn-controller]) -@@ -4207,8 +4207,8 @@ done - dnl Each server should have at least one connection. - AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.0.0.10) | \ - sed -e 's/zone=[[0-9]]*/zone=/'], [0], [dnl --tcp,orig=(src=10.0.0.4,dst=10.0.0.10,sport=,dport=),reply=(src=10.0.0.3,dst=10.0.0.4,sport=,dport=),zone=,protoinfo=(state=) --tcp,orig=(src=10.0.0.4,dst=10.0.0.10,sport=,dport=),reply=(src=20.0.0.3,dst=10.0.0.4,sport=,dport=),zone=,protoinfo=(state=) -+tcp,orig=(src=10.0.0.4,dst=10.0.0.10,sport=,dport=),reply=(src=10.0.0.3,dst=10.0.0.4,sport=,dport=),zone=,labels=0x2,protoinfo=(state=) -+tcp,orig=(src=10.0.0.4,dst=10.0.0.10,sport=,dport=),reply=(src=20.0.0.3,dst=10.0.0.4,sport=,dport=),zone=,labels=0x2,protoinfo=(state=) - ]) - - # Stop webserer in sw0-p1 -@@ -4232,7 +4232,7 @@ done - - AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.0.0.10) | \ - sed -e 's/zone=[[0-9]]*/zone=/'], [0], [dnl --tcp,orig=(src=10.0.0.4,dst=10.0.0.10,sport=,dport=),reply=(src=20.0.0.3,dst=10.0.0.4,sport=,dport=),zone=,protoinfo=(state=) -+tcp,orig=(src=10.0.0.4,dst=10.0.0.10,sport=,dport=),reply=(src=20.0.0.3,dst=10.0.0.4,sport=,dport=),zone=,labels=0x2,protoinfo=(state=) - ]) - - # Create udp load balancer. --- -2.26.2 - diff --git a/SOURCES/0019-ovsdb-idl-Add-function-to-reset-min_index.patch b/SOURCES/0019-ovsdb-idl-Add-function-to-reset-min_index.patch deleted file mode 100644 index e85d3ac..0000000 --- a/SOURCES/0019-ovsdb-idl-Add-function-to-reset-min_index.patch +++ /dev/null @@ -1,68 +0,0 @@ -From 3ad242eef272a8cc31a18a1b047ba7d54e861b0d Mon Sep 17 00:00:00 2001 -From: Mark Michelson -Date: Fri, 1 May 2020 15:13:08 -0400 -Subject: [PATCH 19/22] ovsdb-idl: Add function to reset min_index. - -If an administrator removes all of the databases in a cluster from -disk, then ovsdb IDL clients will have a problem. The databases will all -reset their stored indexes to 0, so The IDL client's min_index will be -higher than the indexes of all databases in the cluster. This results in -the client constantly connecting to databases, detecting the data as -"stale", and then attempting to connect to another. - -This function provides a way to reset the IDL to an initial state with -min_index of 0. This way, the client will not wrongly detect the -database data as stale and will recover properly. - -Notice that this function is not actually used anywhere in this patch. -This will be used by OVN, though, since OVN is the primary user of -clustered OVSDB. - -Signed-off-by: Mark Michelson -Acked-by: Han Zhou -Signed-off-by: Ilya Maximets - -(cherry-picked from upstream ovs commit 89b522aee379f7ebd21ec67ffb622118af7e9db1) - -Change-Id: I943ece9a07566a34b11248455cc1abbe7892d4e8 ---- - openvswitch-2.13.0/lib/ovsdb-idl.c | 10 ++++++++++ - openvswitch-2.13.0/lib/ovsdb-idl.h | 1 + - 2 files changed, 11 insertions(+) - -diff --git a/openvswitch-2.13.0/lib/ovsdb-idl.c b/openvswitch-2.13.0/lib/ovsdb-idl.c -index 8eb421366..648c227d6 100644 ---- a/openvswitch-2.13.0/lib/ovsdb-idl.c -+++ b/openvswitch-2.13.0/lib/ovsdb-idl.c -@@ -561,6 +561,16 @@ ovsdb_idl_set_shuffle_remotes(struct ovsdb_idl *idl, bool shuffle) - idl->shuffle_remotes = shuffle; - } - -+/* Reset min_index to 0. This prevents a situation where the client -+ * thinks all databases have stale data, when they actually have all -+ * been destroyed and rebuilt from scratch. -+ */ -+void -+ovsdb_idl_reset_min_index(struct ovsdb_idl *idl) -+{ -+ idl->min_index = 0; -+} -+ - static void - ovsdb_idl_db_destroy(struct ovsdb_idl_db *db) - { -diff --git a/openvswitch-2.13.0/lib/ovsdb-idl.h b/openvswitch-2.13.0/lib/ovsdb-idl.h -index 9f12ce320..c56cd19b1 100644 ---- a/openvswitch-2.13.0/lib/ovsdb-idl.h -+++ b/openvswitch-2.13.0/lib/ovsdb-idl.h -@@ -64,6 +64,7 @@ struct ovsdb_idl *ovsdb_idl_create_unconnected( - const struct ovsdb_idl_class *, bool monitor_everything_by_default); - void ovsdb_idl_set_remote(struct ovsdb_idl *, const char *, bool); - void ovsdb_idl_set_shuffle_remotes(struct ovsdb_idl *, bool); -+void ovsdb_idl_reset_min_index(struct ovsdb_idl *); - void ovsdb_idl_destroy(struct ovsdb_idl *); - - void ovsdb_idl_set_leader_only(struct ovsdb_idl *, bool leader_only); --- -2.26.2 - diff --git a/SOURCES/0020-Add-northd-and-ovn-controller-cluster-status-reset-c.patch b/SOURCES/0020-Add-northd-and-ovn-controller-cluster-status-reset-c.patch deleted file mode 100644 index 73d6138..0000000 --- a/SOURCES/0020-Add-northd-and-ovn-controller-cluster-status-reset-c.patch +++ /dev/null @@ -1,246 +0,0 @@ -From 8e629246fead7a5dfee78ca45627a0cfd7fef4b1 Mon Sep 17 00:00:00 2001 -From: Mark Michelson -Date: Wed, 10 Jun 2020 14:50:06 -0400 -Subject: [PATCH 20/22] Add northd and ovn-controller cluster status reset - commands. - -During the course of debugging a clustered DB environment, all members -of the southbound database cluster were destroyed (i.e. the .db files -were removed from disk) and then restarted. Once this happened, -ovn-northd and ovn-controller could not interact with the southbound -database because they both detected all members of the cluster as having -"stale" data. The only course of action was to reset ovn-northd and all -ovn-controllers. It is possible to have this happen with the northbound -database as well if it is clustered. - -This patch offers new ovn-appctl commands for ovn-northd and -ovn-controller that allows for it to reset its clustered status. This -allows for it to interact with the database successfully after a cluster -teardown and restart. - -Signed-off-by: Mark Michelson -Acked-by: Han Zhou -Reported-at: https://bugzilla.redhat.com/show_bug.cgi?id=1829109 - -(cherry-picked from upstream ovn master commit 512b884dea3f85791eca44fd1d92956e8282be6d) - -Change-Id: I3e7d844d6f79552fd53a018c74b80def6069edcb ---- - controller/ovn-controller.8.xml | 16 ++++++++++++++++ - controller/ovn-controller.c | 30 ++++++++++++++++++++++++++--- - northd/ovn-northd.8.xml | 28 +++++++++++++++++++++++++++ - northd/ovn-northd.c | 34 +++++++++++++++++++++++++++++++++ - 4 files changed, 105 insertions(+), 3 deletions(-) - -diff --git a/controller/ovn-controller.8.xml b/controller/ovn-controller.8.xml -index 92e0a6e43..66877314c 100644 ---- a/controller/ovn-controller.8.xml -+++ b/controller/ovn-controller.8.xml -@@ -491,6 +491,22 @@ - recomputes are cpu intensive. -

      - -+ -+
      sb-cluster-state-reset
      -+
      -+

      -+ Reset southbound database cluster status when databases are destroyed -+ and rebuilt. -+

      -+

      -+ If all databases in a clustered southbound database are removed from -+ disk, then the stored index of all databases will be reset to zero. -+ This will cause ovn-controller to be unable to read or write to the -+ southbound database, because it will always detect the data as stale. -+ In such a case, run this command so that ovn-controller will reset its -+ local index so that it can interact with the southbound database again. -+

      -+
      -
      -

      - -diff --git a/controller/ovn-controller.c b/controller/ovn-controller.c -index 85e58d04f..fe6048153 100644 ---- a/controller/ovn-controller.c -+++ b/controller/ovn-controller.c -@@ -72,6 +72,7 @@ static unixctl_cb_func ct_zone_list; - static unixctl_cb_func extend_table_list; - static unixctl_cb_func inject_pkt; - static unixctl_cb_func engine_recompute_cmd; -+static unixctl_cb_func cluster_state_reset_cmd; - - #define DEFAULT_BRIDGE_NAME "br-int" - #define DEFAULT_PROBE_INTERVAL_MSEC 5000 -@@ -445,7 +446,7 @@ get_ofctrl_probe_interval(struct ovsdb_idl *ovs_idl) - * updates 'sbdb_idl' with that pointer. */ - static void - update_sb_db(struct ovsdb_idl *ovs_idl, struct ovsdb_idl *ovnsb_idl, -- bool *monitor_all_p) -+ bool *monitor_all_p, bool *reset_ovnsb_idl_min_index) - { - const struct ovsrec_open_vswitch *cfg = ovsrec_open_vswitch_first(ovs_idl); - if (!cfg) { -@@ -475,6 +476,12 @@ update_sb_db(struct ovsdb_idl *ovs_idl, struct ovsdb_idl *ovnsb_idl, - if (monitor_all_p) { - *monitor_all_p = monitor_all; - } -+ if (*reset_ovnsb_idl_min_index) { -+ VLOG_INFO("Resetting southbound database cluster state"); -+ engine_set_force_recompute(true); -+ ovsdb_idl_reset_min_index(ovnsb_idl); -+ *reset_ovnsb_idl_min_index = false; -+ } - } - - static void -@@ -2287,6 +2294,11 @@ main(int argc, char *argv[]) - unixctl_command_register("recompute", "", 0, 0, engine_recompute_cmd, - NULL); - -+ bool reset_ovnsb_idl_min_index = false; -+ unixctl_command_register("sb-cluster-state-reset", "", 0, 0, -+ cluster_state_reset_cmd, -+ &reset_ovnsb_idl_min_index); -+ - unsigned int ovs_cond_seqno = UINT_MAX; - unsigned int ovnsb_cond_seqno = UINT_MAX; - -@@ -2308,7 +2320,8 @@ main(int argc, char *argv[]) - ovs_cond_seqno = new_ovs_cond_seqno; - } - -- update_sb_db(ovs_idl_loop.idl, ovnsb_idl_loop.idl, &sb_monitor_all); -+ update_sb_db(ovs_idl_loop.idl, ovnsb_idl_loop.idl, &sb_monitor_all, -+ &reset_ovnsb_idl_min_index); - update_ssl_config(ovsrec_ssl_table_get(ovs_idl_loop.idl)); - ofctrl_set_probe_interval(get_ofctrl_probe_interval(ovs_idl_loop.idl)); - -@@ -2558,7 +2571,7 @@ main(int argc, char *argv[]) - if (!restart) { - bool done = !ovsdb_idl_has_ever_connected(ovnsb_idl_loop.idl); - while (!done) { -- update_sb_db(ovs_idl_loop.idl, ovnsb_idl_loop.idl, NULL); -+ update_sb_db(ovs_idl_loop.idl, ovnsb_idl_loop.idl, NULL, false); - update_ssl_config(ovsrec_ssl_table_get(ovs_idl_loop.idl)); - - struct ovsdb_idl_txn *ovs_idl_txn -@@ -2780,3 +2793,14 @@ engine_recompute_cmd(struct unixctl_conn *conn OVS_UNUSED, int argc OVS_UNUSED, - poll_immediate_wake(); - unixctl_command_reply(conn, NULL); - } -+ -+static void -+cluster_state_reset_cmd(struct unixctl_conn *conn, int argc OVS_UNUSED, -+ const char *argv[] OVS_UNUSED, void *idl_reset_) -+{ -+ bool *idl_reset = idl_reset_; -+ -+ *idl_reset = true; -+ poll_immediate_wake(); -+ unixctl_command_reply(conn, NULL); -+} -diff --git a/northd/ovn-northd.8.xml b/northd/ovn-northd.8.xml -index e45d494e8..989e3643b 100644 ---- a/northd/ovn-northd.8.xml -+++ b/northd/ovn-northd.8.xml -@@ -96,6 +96,34 @@ - acquired OVSDB lock on SB DB, "standby" if it has not or "paused" if - this instance is paused. - -+ -+
      sb-cluster-state-reset
      -+
      -+

      -+ Reset southbound database cluster status when databases are destroyed -+ and rebuilt. -+

      -+

      -+ If all databases in a clustered southbound database are removed from -+ disk, then the stored index of all databases will be reset to zero. -+ This will cause ovn-northd to be unable to read or write to the -+ southbound database, because it will always detect the data as stale. -+ In such a case, run this command so that ovn-northd will reset its -+ local index so that it can interact with the southbound database again. -+

      -+
      -+ -+
      nb-cluster-state-reset
      -+
      -+

      -+ Reset northbound database cluster status when databases are destroyed -+ and rebuilt. -+

      -+

      -+ This performs the same task as sb-cluster-state-reset -+ except for the northbound database client. -+

      -+
      - -

      - -diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c -index 5f0abeee1..fc05accde 100644 ---- a/northd/ovn-northd.c -+++ b/northd/ovn-northd.c -@@ -56,6 +56,7 @@ static unixctl_cb_func ovn_northd_pause; - static unixctl_cb_func ovn_northd_resume; - static unixctl_cb_func ovn_northd_is_paused; - static unixctl_cb_func ovn_northd_status; -+static unixctl_cb_func cluster_state_reset_cmd; - - struct northd_context { - struct ovsdb_idl *ovnnb_idl; -@@ -12393,6 +12394,16 @@ main(int argc, char *argv[]) - &state); - unixctl_command_register("status", "", 0, 0, ovn_northd_status, &state); - -+ bool reset_ovnsb_idl_min_index = false; -+ unixctl_command_register("sb-cluster-state-reset", "", 0, 0, -+ cluster_state_reset_cmd, -+ &reset_ovnsb_idl_min_index); -+ -+ bool reset_ovnnb_idl_min_index = false; -+ unixctl_command_register("nb-cluster-state-reset", "", 0, 0, -+ cluster_state_reset_cmd, -+ &reset_ovnnb_idl_min_index); -+ - daemonize_complete(); - - /* We want to detect (almost) all changes to the ovn-nb db. */ -@@ -12684,6 +12695,18 @@ main(int argc, char *argv[]) - ovsdb_idl_set_probe_interval(ovnnb_idl_loop.idl, northd_probe_interval); - ovsdb_idl_set_probe_interval(ovnsb_idl_loop.idl, northd_probe_interval); - -+ if (reset_ovnsb_idl_min_index) { -+ VLOG_INFO("Resetting southbound database cluster state"); -+ ovsdb_idl_reset_min_index(ovnsb_idl_loop.idl); -+ reset_ovnsb_idl_min_index = false; -+ } -+ -+ if (reset_ovnnb_idl_min_index) { -+ VLOG_INFO("Resetting northbound database cluster state"); -+ ovsdb_idl_reset_min_index(ovnnb_idl_loop.idl); -+ reset_ovnnb_idl_min_index = false; -+ } -+ - poll_block(); - if (should_service_stop()) { - exiting = true; -@@ -12762,3 +12785,14 @@ ovn_northd_status(struct unixctl_conn *conn, int argc OVS_UNUSED, - unixctl_command_reply(conn, ds_cstr(&s)); - ds_destroy(&s); - } -+ -+static void -+cluster_state_reset_cmd(struct unixctl_conn *conn, int argc OVS_UNUSED, -+ const char *argv[] OVS_UNUSED, void *idl_reset_) -+{ -+ bool *idl_reset = idl_reset_; -+ -+ *idl_reset = true; -+ poll_immediate_wake(); -+ unixctl_command_reply(conn, NULL); -+} --- -2.26.2 - diff --git a/SOURCES/0021-Fix-compilation-error-when-configured-with-enable-sp.patch b/SOURCES/0021-Fix-compilation-error-when-configured-with-enable-sp.patch deleted file mode 100644 index 17b4266..0000000 --- a/SOURCES/0021-Fix-compilation-error-when-configured-with-enable-sp.patch +++ /dev/null @@ -1,49 +0,0 @@ -From a426e7292fc6a8c9d37e60abd0ff58eac983b18e Mon Sep 17 00:00:00 2001 -From: Numan Siddique -Date: Thu, 11 Jun 2020 12:47:08 +0530 -Subject: [PATCH 21/22] Fix compilation error when configured with - --enable-sparse. - -The below error is seen. - -../controller/ovn-controller.c:2305:70: error: Using plain integer as NULL pointer -make[1]: *** [Makefile:2000: controller/ovn-controller.o] Error 1 -make[1]: *** Waiting for unfinished jobs.... - -Fixes: 512b884dea3f("Add northd and ovn-controller cluster status reset commands.") -CC: Mark Michelson -Acked-by: Dumitru Ceara -Signed-off-by: Numan Siddique - -(cherry-picked from upstream ovn master commit 10deb869fff9f0b6736523cf3bdf0d856035895b) - -Change-Id: Iaff476538e85a9d68a1269cd55963edb5275dbe3 ---- - controller/ovn-controller.c | 4 ++-- - 1 file changed, 2 insertions(+), 2 deletions(-) - -diff --git a/controller/ovn-controller.c b/controller/ovn-controller.c -index fe6048153..67b3cd989 100644 ---- a/controller/ovn-controller.c -+++ b/controller/ovn-controller.c -@@ -476,7 +476,7 @@ update_sb_db(struct ovsdb_idl *ovs_idl, struct ovsdb_idl *ovnsb_idl, - if (monitor_all_p) { - *monitor_all_p = monitor_all; - } -- if (*reset_ovnsb_idl_min_index) { -+ if (reset_ovnsb_idl_min_index && *reset_ovnsb_idl_min_index) { - VLOG_INFO("Resetting southbound database cluster state"); - engine_set_force_recompute(true); - ovsdb_idl_reset_min_index(ovnsb_idl); -@@ -2571,7 +2571,7 @@ main(int argc, char *argv[]) - if (!restart) { - bool done = !ovsdb_idl_has_ever_connected(ovnsb_idl_loop.idl); - while (!done) { -- update_sb_db(ovs_idl_loop.idl, ovnsb_idl_loop.idl, NULL, false); -+ update_sb_db(ovs_idl_loop.idl, ovnsb_idl_loop.idl, NULL, NULL); - update_ssl_config(ovsrec_ssl_table_get(ovs_idl_loop.idl)); - - struct ovsdb_idl_txn *ovs_idl_txn --- -2.26.2 - diff --git a/SOURCES/0022-Avoid-nb_cfg-update-notification-flooding.patch b/SOURCES/0022-Avoid-nb_cfg-update-notification-flooding.patch deleted file mode 100644 index 184e0b3..0000000 --- a/SOURCES/0022-Avoid-nb_cfg-update-notification-flooding.patch +++ /dev/null @@ -1,565 +0,0 @@ -From ddbdb15a4b6050d9c667ca2bc546a118e208a342 Mon Sep 17 00:00:00 2001 -From: Han Zhou -Date: Thu, 30 Jul 2020 23:18:58 -0700 -Subject: [PATCH 22/22] Avoid nb_cfg update notification flooding - -nb_cfg as a mechanism to "ping" OVN control plane is very useful -in many ways. However, the current implementation will trigger -update notifications flooding in the whole control plane. Each -HV updates to SB the nb_cfg number and all these updates are -notified to all the other HVs, which is O(n^2). Although updates -are batched in fewers notifications than n^2, it still generates -significant load on SB DB and ovn-controllers. - -To solve this problem and make the mechanism more useful in large -scale producation deployment, this patch separates the per HV -*private* data (write only by the owning chassis and not -interesting to any other HVs) from the Chassis table to a separate -table, so that each HV can conditionally monitor and get updates -only for its own record. - -Test result shows great improvement: -In a test environment with 1200 sandbox HVs, and 12K ports created -on 80 lswitches and 1 lrouter, do the sync test when the system -is idle, with command: - - time ovn-nbctl --wait=hv sync - -Original result: -real 0m13.724s -user 0m0.295s -sys 0m0.012s - -With this patch: -real 0m3.255s -user 0m0.248s -sys 0m0.020s - -Also, regarding backwards compatibility note that the nb_cfg from the -Chassis table is no longer updated. If any system is relying on this -mechanism they should start using the nb_cfg from the Chassis_Private -table from now on. - -Change-Id: I9be2449f3317ff6b91d9afc8f53a9caa8e14c062 -Co-authored-by: Lucas Alvares Gomes -Signed-off-by: Lucas Alvares Gomes -Signed-off-by: Han Zhou -Acked-by: Dumitru Ceara - -(cherry-picked from upstream master commit 4adc10f58127e45b5883f2e7cb1c702720b95043) ---- - controller/chassis.c | 30 ++++++++++++++++++++---- - controller/chassis.h | 8 +++++-- - controller/ovn-controller.c | 42 ++++++++++++++++++++++++++++----- - lib/chassis-index.c | 26 +++++++++++++++++++++ - lib/chassis-index.h | 6 +++++ - northd/ovn-northd.c | 46 +++++++++++++++++++++++++++++++------ - ovn-sb.ovsschema | 17 ++++++++++++-- - ovn-sb.xml | 42 +++++++++++++++++++++++++++++---- - tests/ovn-controller.at | 26 +++++++++++++++++++++ - 9 files changed, 218 insertions(+), 25 deletions(-) - -diff --git a/controller/chassis.c b/controller/chassis.c -index bdf3fb950..6ac591e02 100644 ---- a/controller/chassis.c -+++ b/controller/chassis.c -@@ -621,14 +621,18 @@ chassis_update(const struct sbrec_chassis *chassis_rec, - const struct sbrec_chassis * - chassis_run(struct ovsdb_idl_txn *ovnsb_idl_txn, - struct ovsdb_idl_index *sbrec_chassis_by_name, -+ struct ovsdb_idl_index *sbrec_chassis_private_by_name, - const struct ovsrec_open_vswitch_table *ovs_table, - const struct sbrec_chassis_table *chassis_table, - const char *chassis_id, - const struct ovsrec_bridge *br_int, -- const struct sset *transport_zones) -+ const struct sset *transport_zones, -+ const struct sbrec_chassis_private **chassis_private) - { - struct ovs_chassis_cfg ovs_cfg; - -+ *chassis_private = NULL; -+ - /* Get the chassis config from the ovs table. */ - ovs_chassis_cfg_init(&ovs_cfg); - if (!chassis_parse_ovs_config(ovs_table, br_int, &ovs_cfg)) { -@@ -655,6 +659,18 @@ chassis_run(struct ovsdb_idl_txn *ovnsb_idl_txn, - !existed ? "registering" : "updating", - chassis_id); - } -+ -+ const struct sbrec_chassis_private *chassis_private_rec = -+ chassis_private_lookup_by_name(sbrec_chassis_private_by_name, -+ chassis_id); -+ if (!chassis_private_rec && ovnsb_idl_txn) { -+ chassis_private_rec = sbrec_chassis_private_insert(ovnsb_idl_txn); -+ sbrec_chassis_private_set_name(chassis_private_rec, -+ chassis_id); -+ sbrec_chassis_private_set_chassis(chassis_private_rec, -+ chassis_rec); -+ } -+ *chassis_private = chassis_private_rec; - } - - ovs_chassis_cfg_destroy(&ovs_cfg); -@@ -710,16 +726,22 @@ chassis_get_mac(const struct sbrec_chassis *chassis_rec, - * required. */ - bool - chassis_cleanup(struct ovsdb_idl_txn *ovnsb_idl_txn, -- const struct sbrec_chassis *chassis_rec) -+ const struct sbrec_chassis *chassis_rec, -+ const struct sbrec_chassis_private *chassis_private_rec) - { -- if (!chassis_rec) { -+ if (!chassis_rec && !chassis_private_rec) { - return true; - } - if (ovnsb_idl_txn) { - ovsdb_idl_txn_add_comment(ovnsb_idl_txn, - "ovn-controller: unregistering chassis '%s'", - chassis_rec->name); -- sbrec_chassis_delete(chassis_rec); -+ if (chassis_rec) { -+ sbrec_chassis_delete(chassis_rec); -+ } -+ if (chassis_private_rec) { -+ sbrec_chassis_private_delete(chassis_private_rec); -+ } - } - return false; - } -diff --git a/controller/chassis.h b/controller/chassis.h -index 178d2957e..81055b403 100644 ---- a/controller/chassis.h -+++ b/controller/chassis.h -@@ -17,6 +17,7 @@ - #define OVN_CHASSIS_H 1 - - #include -+#include "lib/ovn-sb-idl.h" - - struct ovsdb_idl; - struct ovsdb_idl_index; -@@ -33,12 +34,15 @@ void chassis_register_ovs_idl(struct ovsdb_idl *); - const struct sbrec_chassis *chassis_run( - struct ovsdb_idl_txn *ovnsb_idl_txn, - struct ovsdb_idl_index *sbrec_chassis_by_name, -+ struct ovsdb_idl_index *sbrec_chassis_private_by_name, - const struct ovsrec_open_vswitch_table *, - const struct sbrec_chassis_table *, - const char *chassis_id, const struct ovsrec_bridge *br_int, -- const struct sset *transport_zones); -+ const struct sset *transport_zones, -+ const struct sbrec_chassis_private **chassis_private); - bool chassis_cleanup(struct ovsdb_idl_txn *ovnsb_idl_txn, -- const struct sbrec_chassis *); -+ const struct sbrec_chassis *, -+ const struct sbrec_chassis_private *); - bool chassis_get_mac(const struct sbrec_chassis *chassis, - const char *bridge_mapping, - struct eth_addr *chassis_mac); -diff --git a/controller/ovn-controller.c b/controller/ovn-controller.c -index 67b3cd989..933acf676 100644 ---- a/controller/ovn-controller.c -+++ b/controller/ovn-controller.c -@@ -155,6 +155,7 @@ update_sb_monitors(struct ovsdb_idl *ovnsb_idl, - struct ovsdb_idl_condition ce = OVSDB_IDL_CONDITION_INIT(&ce); - struct ovsdb_idl_condition ip_mcast = OVSDB_IDL_CONDITION_INIT(&ip_mcast); - struct ovsdb_idl_condition igmp = OVSDB_IDL_CONDITION_INIT(&igmp); -+ struct ovsdb_idl_condition chprv = OVSDB_IDL_CONDITION_INIT(&chprv); - - if (monitor_all) { - ovsdb_idl_condition_add_clause_true(&pb); -@@ -165,6 +166,7 @@ update_sb_monitors(struct ovsdb_idl *ovnsb_idl, - ovsdb_idl_condition_add_clause_true(&ce); - ovsdb_idl_condition_add_clause_true(&ip_mcast); - ovsdb_idl_condition_add_clause_true(&igmp); -+ ovsdb_idl_condition_add_clause_true(&chprv); - goto out; - } - -@@ -196,7 +198,16 @@ update_sb_monitors(struct ovsdb_idl *ovnsb_idl, - &chassis->header_.uuid); - sbrec_igmp_group_add_clause_chassis(&igmp, OVSDB_F_EQ, - &chassis->header_.uuid); -+ -+ /* Monitors Chassis_Private record for current chassis only */ -+ sbrec_chassis_private_add_clause_name(&chprv, OVSDB_F_EQ, -+ chassis->name); -+ } else { -+ /* During initialization, we monitor all records in Chassis_Private so -+ * that we don't try to recreate existing ones. */ -+ ovsdb_idl_condition_add_clause_true(&chprv); - } -+ - if (local_ifaces) { - const char *name; - SSET_FOR_EACH (name, local_ifaces) { -@@ -229,6 +240,7 @@ out: - sbrec_controller_event_set_condition(ovnsb_idl, &ce); - sbrec_ip_multicast_set_condition(ovnsb_idl, &ip_mcast); - sbrec_igmp_group_set_condition(ovnsb_idl, &igmp); -+ sbrec_chassis_private_set_condition(ovnsb_idl, &chprv); - ovsdb_idl_condition_destroy(&pb); - ovsdb_idl_condition_destroy(&lf); - ovsdb_idl_condition_destroy(&mb); -@@ -237,6 +249,7 @@ out: - ovsdb_idl_condition_destroy(&ce); - ovsdb_idl_condition_destroy(&ip_mcast); - ovsdb_idl_condition_destroy(&igmp); -+ ovsdb_idl_condition_destroy(&chprv); - } - - static const char * -@@ -2090,6 +2103,8 @@ main(int argc, char *argv[]) - - struct ovsdb_idl_index *sbrec_chassis_by_name - = chassis_index_create(ovnsb_idl_loop.idl); -+ struct ovsdb_idl_index *sbrec_chassis_private_by_name -+ = chassis_private_index_create(ovnsb_idl_loop.idl); - struct ovsdb_idl_index *sbrec_multicast_group_by_name_datapath - = mcast_group_index_create(ovnsb_idl_loop.idl); - struct ovsdb_idl_index *sbrec_logical_flow_by_logical_datapath -@@ -2118,7 +2133,8 @@ main(int argc, char *argv[]) - = igmp_group_index_create(ovnsb_idl_loop.idl); - - ovsdb_idl_track_add_all(ovnsb_idl_loop.idl); -- ovsdb_idl_omit_alert(ovnsb_idl_loop.idl, &sbrec_chassis_col_nb_cfg); -+ ovsdb_idl_omit_alert(ovnsb_idl_loop.idl, -+ &sbrec_chassis_private_col_nb_cfg); - - /* Omit the external_ids column of all the tables except for - - * - DNS. pinctrl.c uses the external_ids column of DNS, -@@ -2155,6 +2171,10 @@ main(int argc, char *argv[]) - * other_config column so we no longer need to monitor it */ - ovsdb_idl_omit_alert(ovnsb_idl_loop.idl, &sbrec_chassis_col_external_ids); - -+ /* Do not monitor Chassis_Private external_ids */ -+ ovsdb_idl_omit(ovnsb_idl_loop.idl, -+ &sbrec_chassis_private_col_external_ids); -+ - update_sb_monitors(ovnsb_idl_loop.idl, NULL, NULL, NULL, false); - - stopwatch_create(CONTROLLER_LOOP_STOPWATCH_NAME, SW_MS); -@@ -2361,10 +2381,13 @@ main(int argc, char *argv[]) - process_br_int(ovs_idl_txn, bridge_table, ovs_table); - const char *chassis_id = get_ovs_chassis_id(ovs_table); - const struct sbrec_chassis *chassis = NULL; -+ const struct sbrec_chassis_private *chassis_private = NULL; - if (chassis_id) { - chassis = chassis_run(ovnsb_idl_txn, sbrec_chassis_by_name, -+ sbrec_chassis_private_by_name, - ovs_table, chassis_table, chassis_id, -- br_int, &transport_zones); -+ br_int, &transport_zones, -+ &chassis_private); - } - - if (br_int) { -@@ -2489,10 +2512,10 @@ main(int argc, char *argv[]) - engine_set_force_recompute(false); - } - -- if (ovnsb_idl_txn && chassis) { -+ if (ovnsb_idl_txn && chassis_private) { - int64_t cur_cfg = ofctrl_get_cur_cfg(); -- if (cur_cfg && cur_cfg != chassis->nb_cfg) { -- sbrec_chassis_set_nb_cfg(chassis, cur_cfg); -+ if (cur_cfg && cur_cfg != chassis_private->nb_cfg) { -+ sbrec_chassis_private_set_nb_cfg(chassis_private, cur_cfg); - } - } - -@@ -2595,10 +2618,17 @@ main(int argc, char *argv[]) - ? chassis_lookup_by_name(sbrec_chassis_by_name, chassis_id) - : NULL); - -+ const struct sbrec_chassis_private *chassis_private -+ = (chassis_id -+ ? chassis_private_lookup_by_name( -+ sbrec_chassis_private_by_name, chassis_id) -+ : NULL); -+ - /* Run all of the cleanup functions, even if one of them returns - * false. We're done if all of them return true. */ - done = binding_cleanup(ovnsb_idl_txn, port_binding_table, chassis); -- done = chassis_cleanup(ovnsb_idl_txn, chassis) && done; -+ done = chassis_cleanup(ovnsb_idl_txn, -+ chassis, chassis_private) && done; - done = encaps_cleanup(ovs_idl_txn, br_int) && done; - done = igmp_group_cleanup(ovnsb_idl_txn, sbrec_igmp_group) && done; - if (done) { -diff --git a/lib/chassis-index.c b/lib/chassis-index.c -index 39066f4cc..13120fe3e 100644 ---- a/lib/chassis-index.c -+++ b/lib/chassis-index.c -@@ -40,6 +40,32 @@ chassis_lookup_by_name(struct ovsdb_idl_index *sbrec_chassis_by_name, - return retval; - } - -+struct ovsdb_idl_index * -+chassis_private_index_create(struct ovsdb_idl *idl) -+{ -+ return ovsdb_idl_index_create1(idl, -+ &sbrec_chassis_private_col_name); -+} -+ -+/* Finds and returns the chassis with the given 'name', or NULL if no such -+ * chassis exists. */ -+const struct sbrec_chassis_private * -+chassis_private_lookup_by_name( -+ struct ovsdb_idl_index *sbrec_chassis_private_by_name, -+ const char *name) -+{ -+ struct sbrec_chassis_private *target = -+ sbrec_chassis_private_index_init_row(sbrec_chassis_private_by_name); -+ sbrec_chassis_private_index_set_name(target, name); -+ -+ struct sbrec_chassis_private *retval = sbrec_chassis_private_index_find( -+ sbrec_chassis_private_by_name, target); -+ -+ sbrec_chassis_private_index_destroy_row(target); -+ -+ return retval; -+} -+ - struct ovsdb_idl_index * - ha_chassis_group_index_create(struct ovsdb_idl *idl) - { -diff --git a/lib/chassis-index.h b/lib/chassis-index.h -index 302e5f0fd..b9b331f34 100644 ---- a/lib/chassis-index.h -+++ b/lib/chassis-index.h -@@ -23,6 +23,12 @@ struct ovsdb_idl_index *chassis_index_create(struct ovsdb_idl *); - const struct sbrec_chassis *chassis_lookup_by_name( - struct ovsdb_idl_index *sbrec_chassis_by_name, const char *name); - -+struct ovsdb_idl_index *chassis_private_index_create(struct ovsdb_idl *); -+ -+const struct sbrec_chassis_private * -+chassis_private_lookup_by_name( -+ struct ovsdb_idl_index *sbrec_chassis_private_by_name, const char *name); -+ - struct ovsdb_idl_index *ha_chassis_group_index_create(struct ovsdb_idl *idl); - const struct sbrec_ha_chassis_group *ha_chassis_group_lookup_by_name( - struct ovsdb_idl_index *sbrec_ha_chassis_grp_by_name, const char *name); -diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c -index fc05accde..c83f9d5c2 100644 ---- a/northd/ovn-northd.c -+++ b/northd/ovn-northd.c -@@ -12024,6 +12024,11 @@ static const char *rbac_chassis_update[] = - {"nb_cfg", "external_ids", "encaps", "vtep_logical_switches", - "other_config"}; - -+static const char *rbac_chassis_private_auth[] = -+ {"name"}; -+static const char *rbac_chassis_private_update[] = -+ {"nb_cfg", "chassis"}; -+ - static const char *rbac_encap_auth[] = - {"chassis_name"}; - static const char *rbac_encap_update[] = -@@ -12061,6 +12066,14 @@ static struct rbac_perm_cfg { - .update = rbac_chassis_update, - .n_update = ARRAY_SIZE(rbac_chassis_update), - .row = NULL -+ },{ -+ .table = "Chassis_Private", -+ .auth = rbac_chassis_private_auth, -+ .n_auth = ARRAY_SIZE(rbac_chassis_private_auth), -+ .insdel = true, -+ .update = rbac_chassis_private_update, -+ .n_update = ARRAY_SIZE(rbac_chassis_private_update), -+ .row = NULL - },{ - .table = "Encap", - .auth = rbac_encap_auth, -@@ -12230,12 +12243,23 @@ update_northbound_cfg(struct northd_context *ctx, - /* Update northbound hv_cfg if appropriate. */ - if (nbg) { - /* Find minimum nb_cfg among all chassis. */ -- const struct sbrec_chassis *chassis; -+ const struct sbrec_chassis_private *chassis_priv; - int64_t hv_cfg = nbg->nb_cfg; -- SBREC_CHASSIS_FOR_EACH (chassis, ctx->ovnsb_idl) { -- if (!smap_get_bool(&chassis->other_config, "is-remote", false) && -- chassis->nb_cfg < hv_cfg) { -- hv_cfg = chassis->nb_cfg; -+ SBREC_CHASSIS_PRIVATE_FOR_EACH (chassis_priv, ctx->ovnsb_idl) { -+ const struct sbrec_chassis *chassis = chassis_priv->chassis; -+ if (chassis) { -+ if (smap_get_bool(&chassis->other_config, -+ "is-remote", false)) { -+ /* Skip remote chassises. */ -+ continue; -+ } -+ } else { -+ VLOG_WARN("Chassis not exist for Chassis_Private record, " -+ "name: %s", chassis_priv->name); -+ } -+ -+ if (chassis_priv->nb_cfg < hv_cfg) { -+ hv_cfg = chassis_priv->nb_cfg; - } - } - -@@ -12248,7 +12272,8 @@ update_northbound_cfg(struct northd_context *ctx, - - /* Handle a fairly small set of changes in the southbound database. */ - static void --ovnsb_db_run(struct northd_context *ctx, struct ovsdb_idl_loop *sb_loop, -+ovnsb_db_run(struct northd_context *ctx, -+ struct ovsdb_idl_loop *sb_loop, - struct hmap *ports) - { - if (!ctx->ovnnb_txn || !ovsdb_idl_has_ever_connected(ctx->ovnsb_idl)) { -@@ -12529,10 +12554,17 @@ main(int argc, char *argv[]) - ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_meter_band_col_burst_size); - - ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_chassis); -- ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_chassis_col_nb_cfg); - ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_chassis_col_name); - ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_chassis_col_other_config); - -+ ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_chassis_private); -+ ovsdb_idl_add_column(ovnsb_idl_loop.idl, -+ &sbrec_chassis_private_col_name); -+ ovsdb_idl_add_column(ovnsb_idl_loop.idl, -+ &sbrec_chassis_private_col_chassis); -+ ovsdb_idl_add_column(ovnsb_idl_loop.idl, -+ &sbrec_chassis_private_col_nb_cfg); -+ - ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_ha_chassis); - add_column_noalert(ovnsb_idl_loop.idl, - &sbrec_ha_chassis_col_chassis); -diff --git a/ovn-sb.ovsschema b/ovn-sb.ovsschema -index 99c5de822..3af76540a 100644 ---- a/ovn-sb.ovsschema -+++ b/ovn-sb.ovsschema -@@ -1,7 +1,7 @@ - { - "name": "OVN_Southbound", -- "version": "2.8.2", -- "cksum": "464326363 21916", -+ "version": "2.9.0", -+ "cksum": "223619766 22548", - "tables": { - "SB_Global": { - "columns": { -@@ -46,6 +46,19 @@ - "max": "unlimited"}}}, - "isRoot": true, - "indexes": [["name"]]}, -+ "Chassis_Private": { -+ "columns": { -+ "name": {"type": "string"}, -+ "chassis": {"type": {"key": {"type": "uuid", -+ "refTable": "Chassis", -+ "refType": "weak"}, -+ "min": 0, "max": 1}}, -+ "nb_cfg": {"type": {"key": "integer"}}, -+ "external_ids": { -+ "type": {"key": "string", "value": "string", -+ "min": 0, "max": "unlimited"}}}, -+ "isRoot": true, -+ "indexes": [["name"]]}, - "Encap": { - "columns": { - "type": {"type": {"key": { -diff --git a/ovn-sb.xml b/ovn-sb.xml -index a74d9c3ea..59b21711b 100644 ---- a/ovn-sb.xml -+++ b/ovn-sb.xml -@@ -256,10 +256,8 @@ - - - -- Sequence number for the configuration. When ovn-controller -- updates the configuration of a chassis from the contents of the -- southbound database, it copies -- from the table into this column. -+ Deprecated. This column is replaced by the column of the table. - - - -@@ -366,6 +364,42 @@ - -
      - -+ -+

      -+ Each row in this table maintains per chassis private data that are -+ accessed only by the owning chassis (write only) and ovn-northd, not by -+ any other chassis. These data are stored in this separate table instead -+ of the table for performance considerations: -+ the rows in this table can be conditionally monitored by chassises so -+ that each chassis only get update notifications for its own row, to avoid -+ unnecessary chassis private data update flooding in a large scale -+ deployment. -+

      -+ -+ -+ The name of the chassis that owns these chassis-private data. -+ -+ -+ -+ The reference to table for the chassis that owns -+ these chassis-private data. -+ -+ -+ -+ Sequence number for the configuration. When ovn-controller -+ updates the configuration of a chassis from the contents of the -+ southbound database, it copies -+ from the table into this column. -+ -+ -+ -+ The overall purpose of these columns is described under Common -+ Columns at the beginning of this document. -+ -+ -+ -+
      -+ - -

      - The column in the - 20.06.2-11 +* Fri Oct 9 2020 Dumitru Ceara - 20.09.0-2 +- Backport "ovn-northd: Add localnet ports to Multicast_Groups created by IGMP_Group." (#1886314) + +* Tue Sep 29 2020 Numan Siddique - 20.09.0-1 +- Rebase to upstream v20.09.0. + +* Mon Sep 28 2020 Lorenzo Bianconi - 20.06.2-15 +- Backport "binding: Rely on qos_map for consider_localnet_lport" (#1878943) +- Backport "binding: fix localnet QoS configuration after I-P" (#1878943) + +* Mon Sep 28 2020 Lorenzo Bianconi - 20.06.2-14 +- Backport "ovn-nbctl: add --may-exist/--if-exists options for policy routing" (#1845109) + +* Mon Sep 21 2020 Numan Siddique - 20.06.2-13 +- Backport "ovn-ctl: Handle cluster db upgrades for run_(nb/sb)_ovsdb" (#1868392) +- Backport "northd: Add lflows to send all pkts to conntrack if LB is configured on a lswitch." (#1870359) +- Backport "ovn-northd: Fix router policy pkt mark over flow if the value is greater than signed int." (#1878248) + +* Tue Sep 15 2020 Numan Siddique - 20.06.2-12 +- Fixed the date typo in changelog. + +* Tue Sep 15 2020 Numan Siddique - 20.06.2-11 - Backport "ovn-controller: Persist the conjunction ids allocated for conjuctive matches." (#1858878) - Backport "I-P engine: Provide the option to store client data in engine ctx." (#1858878) -* Mon Sep 15 2020 Numan Siddique - 20.06.2-10 +* Mon Sep 14 2020 Numan Siddique - 20.06.2-10 - Backport "ovn-northd: Fix multiple ARP replies for SNAT entries configured on a distributed router." (#1878451) * Tue Sep 8 2020 Lorenzo Bianconi - 20.06.2-9