diff --git a/.ovn.metadata b/.ovn.metadata index d14e5a4..4bcfed1 100644 --- a/.ovn.metadata +++ b/.ovn.metadata @@ -1,5 +1,5 @@ 002450621b33c5690060345b0aac25bc2426d675 SOURCES/docutils-0.12.tar.gz -1a15e20ba189049e6bb9f6dde87ff04483351623 SOURCES/openvswitch-91e1ff5.tar.gz +838279b54706cbb447491f422d829569c1c90b73 SOURCES/openvswitch-498cedc.tar.gz 347346dae160f28d6e56b1dee8fa8b701a50748e SOURCES/ovn-21.12.0.tar.gz d34f96421a86004aa5d26ecf975edefd09f948b1 SOURCES/Pygments-1.4.tar.gz 6beb30f18ffac3de7689b7fd63e9a8a7d9c8df3a SOURCES/Sphinx-1.1.3.tar.gz diff --git a/SOURCES/ovn-2021.patch b/SOURCES/ovn-2021.patch index adf62ad..9f922c1 100644 --- a/SOURCES/ovn-2021.patch +++ b/SOURCES/ovn-2021.patch @@ -1,16 +1,132 @@ +diff --git a/.ci/linux-prepare.sh b/.ci/linux-prepare.sh +index 37e8d4250..e0c528479 100755 +--- a/.ci/linux-prepare.sh ++++ b/.ci/linux-prepare.sh +@@ -12,4 +12,10 @@ set -ev + git clone git://git.kernel.org/pub/scm/devel/sparse/sparse.git + cd sparse && make -j4 HAVE_LLVM= HAVE_SQLITE= install && cd .. + +-pip3 install --disable-pip-version-check --user flake8 hacking sphinx pyOpenSSL ++# Installing wheel separately because it may be needed to build some ++# of the packages during dependency backtracking and pip >= 22.0 will ++# abort backtracking on build failures: ++# https://github.com/pypa/pip/issues/10655 ++pip3 install --disable-pip-version-check --user wheel ++pip3 install --disable-pip-version-check --user \ ++ flake8 'hacking>=3.0' sphinx setuptools pyelftools pyOpenSSL +diff --git a/.ci/ovn-kubernetes/Dockerfile b/.ci/ovn-kubernetes/Dockerfile +index 9cfc32f62..495ffc8be 100644 +--- a/.ci/ovn-kubernetes/Dockerfile ++++ b/.ci/ovn-kubernetes/Dockerfile +@@ -1,4 +1,5 @@ + ARG OVNKUBE_COMMIT=master ++ARG LIBOVSDB_COMMIT=8081fe24e48f + + FROM fedora:33 AS ovnbuilder + +@@ -38,11 +39,21 @@ RUN rm rpm/rpmbuild/RPMS/x86_64/*docker* + # Build ovn-kubernetes + FROM golang:1.16 as ovnkubebuilder + ARG OVNKUBE_COMMIT ++ARG LIBOVSDB_COMMIT ++ ++# Get a working version of libovsdb (for modelgen). ++RUN GO111MODULE=on go install github.com/ovn-org/libovsdb/cmd/modelgen@${LIBOVSDB_COMMIT} ++ + # Clone OVN Kubernetes and build the binary based on the commit passed as argument + WORKDIR /root + RUN git clone https://github.com/ovn-org/ovn-kubernetes.git + WORKDIR /root/ovn-kubernetes/go-controller +-RUN git checkout ${OVNKUBE_COMMIT} && git log -n 1 && make ++RUN git checkout ${OVNKUBE_COMMIT} && git log -n 1 ++ ++# Make sure we use the OVN NB/SB schema from the local code. ++COPY --from=ovnbuilder /tmp/ovn/ovn-nb.ovsschema pkg/nbdb/ovn-nb.ovsschema ++COPY --from=ovnbuilder /tmp/ovn/ovn-sb.ovsschema pkg/sbdb/ovn-sb.ovsschema ++RUN go generate ./pkg/nbdb && go generate ./pkg/sbdb && make + + # Build the final image + FROM fedora:33 +diff --git a/.github/workflows/ovn-kubernetes.yml b/.github/workflows/ovn-kubernetes.yml +index 60c585a24..c05bbd3f9 100644 +--- a/.github/workflows/ovn-kubernetes.yml ++++ b/.github/workflows/ovn-kubernetes.yml +@@ -9,9 +9,10 @@ on: + - cron: '0 0 * * 0' + + env: +- GO_VERSION: "1.16.3" +- K8S_VERSION: v1.20.2 ++ GO_VERSION: "1.17.6" ++ K8S_VERSION: v1.23.3 + OVNKUBE_COMMIT: "master" ++ LIBOVSDB_COMMIT: "8081fe24e48f" + KIND_CLUSTER_NAME: ovn + KIND_INSTALL_INGRESS: true + KIND_ALLOW_SYSTEM_WRITES: true +@@ -31,7 +32,9 @@ jobs: + + - name: Build ovn-kubernetes container + run: | +- docker build --build-arg OVNKUBE_COMMIT=${{ env.OVNKUBE_COMMIT }} -t ovn-daemonset-f:dev -f .ci/ovn-kubernetes/Dockerfile . ++ docker build --build-arg OVNKUBE_COMMIT=${{ env.OVNKUBE_COMMIT }} \ ++ --build-arg LIBOVSDB_COMMIT=${{ env.LIBOVSDB_COMMIT }} \ ++ -t ovn-daemonset-f:dev -f .ci/ovn-kubernetes/Dockerfile . + mkdir /tmp/_output + docker save ovn-daemonset-f:dev > /tmp/_output/image.tar + +diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml +index 280f0ffea..7b3648116 100644 +--- a/.github/workflows/test.yml ++++ b/.github/workflows/test.yml +@@ -13,7 +13,7 @@ jobs: + dependencies: | + automake libtool gcc bc libjemalloc2 libjemalloc-dev \ + libssl-dev llvm-dev libelf-dev libnuma-dev libpcap-dev \ +- selinux-policy-dev ncat python3-scapy ++ selinux-policy-dev ncat python3-scapy isc-dhcp-server + m32_dependecies: gcc-multilib + CC: ${{ matrix.compiler }} + LIBS: ${{ matrix.libs }} +diff --git a/AUTHORS.rst b/AUTHORS.rst +index 064725f68..40f36d815 100644 +--- a/AUTHORS.rst ++++ b/AUTHORS.rst +@@ -147,6 +147,7 @@ Fabrizio D'Angelo fdangelo@redhat.com + Flavio Fernandes flavio@flaviof.com + Flavio Leitner fbl@redhat.com + Francesco Fusco ffusco@redhat.com ++François Rigault frigo@amadeus.com + Frank Wang wangpeihuixyz@126.com + Frédéric Tobias Christ fchrist@live.de + Frode Nordahl frode.nordahl@gmail.com +@@ -364,6 +365,7 @@ Steve Ruan ruansx@cn.ibm.com + Stuart Cardall developer@it-offshore.co.uk + Sugesh Chandran sugesh.chandran@intel.com + SUGYO Kazushi sugyo.org@gmail.com ++Sven Haardiek sven.haardiek@uni-muenster.de + Tadaaki Nagao nagao@stratosphere.co.jp + Terry Wilson twilson@redhat.com + Tetsuo NAKAGAWA nakagawa@mxc.nes.nec.co.jp diff --git a/NEWS b/NEWS -index 75f26ddb7..4043ecf20 100644 +index 75f26ddb7..3b3104c2f 100644 --- a/NEWS +++ b/NEWS -@@ -1,3 +1,6 @@ -+OVN v21.12.1 - xx xxx xxxx +@@ -1,3 +1,12 @@ ++OVN v21.12.2 - xx xxx xxxx ++-------------------------- ++ - When configured to log packets matching ACLs, log the direction (logical ++ pipeline) too. ++ ++OVN v21.12.1 - 11 Mar 2022 +-------------------------- ++ - Bug fixes + OVN v21.12.0 - 22 Dec 2021 -------------------------- - Set ignore_lsp_down to true as default, so that ARP responder flows are diff --git a/configure.ac b/configure.ac -index 48b4662f0..e44c86c74 100644 +index 48b4662f0..c4bf08db7 100644 --- a/configure.ac +++ b/configure.ac @@ -13,7 +13,7 @@ @@ -18,10 +134,828 @@ index 48b4662f0..e44c86c74 100644 AC_PREREQ(2.63) -AC_INIT(ovn, 21.12.0, bugs@openvswitch.org) -+AC_INIT(ovn, 21.12.1, bugs@openvswitch.org) ++AC_INIT(ovn, 21.12.2, bugs@openvswitch.org) AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_AUX_DIR([build-aux]) AC_CONFIG_HEADERS([config.h]) +diff --git a/controller-vtep/binding.c b/controller-vtep/binding.c +index 01d5a16d2..1ee52b592 100644 +--- a/controller-vtep/binding.c ++++ b/controller-vtep/binding.c +@@ -109,12 +109,10 @@ update_pb_chassis(const struct sbrec_port_binding *port_binding_rec, + port_binding_rec->chassis->name, + chassis_rec->name); + } +- + sbrec_port_binding_set_chassis(port_binding_rec, chassis_rec); +- if (port_binding_rec->n_up) { +- bool up = true; +- sbrec_port_binding_set_up(port_binding_rec, &up, 1); +- } ++ } else if (port_binding_rec->n_up) { ++ bool up = true; ++ sbrec_port_binding_set_up(port_binding_rec, &up, 1); + } + } + +diff --git a/controller-vtep/gateway.c b/controller-vtep/gateway.c +index 288772dc4..9fbfc0337 100644 +--- a/controller-vtep/gateway.c ++++ b/controller-vtep/gateway.c +@@ -22,6 +22,7 @@ + #include "lib/util.h" + #include "openvswitch/vlog.h" + #include "lib/ovn-sb-idl.h" ++#include "smap.h" + #include "vtep/vtep-idl.h" + #include "ovn-controller-vtep.h" + +@@ -117,6 +118,10 @@ revalidate_gateway(struct controller_vtep_ctx *ctx) + "false"); + sbrec_encap_set_options(chassis_rec->encaps[0], &options); + } ++ if (!smap_get_bool(&chassis_rec->other_config, "is-vtep", false)) { ++ const struct smap oc = SMAP_CONST1(&oc, "is-vtep", "true"); ++ sbrec_chassis_set_other_config(chassis_rec, &oc); ++ } + } else { + if (gw_node) { + VLOG_WARN("Chassis for VTEP physical switch (%s) disappears, " +diff --git a/controller/binding.c b/controller/binding.c +index 4d62b0858..1259e6b3b 100644 +--- a/controller/binding.c ++++ b/controller/binding.c +@@ -481,6 +481,16 @@ remove_related_lport(const struct sbrec_port_binding *pb, + } + } + ++static void ++delete_active_pb_ras_pd(const struct sbrec_port_binding *pb, ++ struct shash *ras_pd_map) ++{ ++ struct pb_ld_binding *ras_pd = ++ shash_find_and_delete(ras_pd_map, pb->logical_port); ++ ++ free(ras_pd); ++} ++ + static void + update_active_pb_ras_pd(const struct sbrec_port_binding *pb, + struct hmap *local_datapaths, +@@ -2251,6 +2261,9 @@ binding_handle_port_binding_changes(struct binding_ctx_in *b_ctx_in, + continue; + } + ++ delete_active_pb_ras_pd(pb, b_ctx_out->local_active_ports_ipv6_pd); ++ delete_active_pb_ras_pd(pb, b_ctx_out->local_active_ports_ras); ++ + enum en_lport_type lport_type = get_lport_type(pb); + + struct binding_lport *b_lport = +diff --git a/controller/lflow-conj-ids.c b/controller/lflow-conj-ids.c +index bfe63862a..6d3601237 100644 +--- a/controller/lflow-conj-ids.c ++++ b/controller/lflow-conj-ids.c +@@ -17,6 +17,8 @@ + #include "coverage.h" + #include "lflow-conj-ids.h" + #include "util.h" ++#include "hash.h" ++#include "openvswitch/list.h" + + COVERAGE_DEFINE(lflow_conj_conflict); + COVERAGE_DEFINE(lflow_conj_alloc); +@@ -30,33 +32,65 @@ struct conj_id_node { + uint32_t conj_id; + }; + +-/* Node in struct conj_ids.lflow_conj_ids. */ + struct lflow_conj_node { +- struct hmap_node hmap_node; ++ struct hmap_node hmap_node; /* Node in struct conj_ids.lflow_conj_ids. */ ++ struct ovs_list list_node; /* Node in struct lflow_to_dps_node.dps. */ + struct uuid lflow_uuid; ++ struct uuid dp_uuid; + uint32_t start_conj_id; + uint32_t n_conjs; + }; + ++struct lflow_to_dps_node { ++ struct hmap_node hmap_node; /* Node in struct conj_ids.lflow_to_dps. */ ++ struct uuid lflow_uuid; ++ struct ovs_list dps; /* List of DPs the lflow belongs to. Each node is ++ struct lflow_conj_node.list_node. */ ++}; ++ ++/* XXX: Figure out a way to avoid test_mode. */ ++static bool test_mode = false; ++ + static void lflow_conj_ids_insert_(struct conj_ids *, + const struct uuid *lflow_uuid, ++ const struct uuid *dp_uuid, + uint32_t start_conj_id, uint32_t n_conjs); +-static void lflow_conj_ids_free_(struct conj_ids *, +- const struct uuid *lflow_uuid, bool expected); ++static void lflow_conj_ids_free_(struct conj_ids *, struct lflow_conj_node *); ++static void lflow_conj_ids_free_for_lflow_dp(struct conj_ids *, ++ const struct uuid *lflow_uuid, ++ const struct uuid *dp_uuid); ++static struct lflow_to_dps_node *lflow_to_dps_find(struct conj_ids *, ++ const struct uuid *); ++static inline uint32_t ++hash_lflow_dp(const struct uuid *lflow_uuid, const struct uuid *dp_uuid) ++{ ++ if (test_mode) { ++ return lflow_uuid->parts[0]; ++ } ++ ++ return hash_int(uuid_hash(lflow_uuid), uuid_hash(dp_uuid)); ++} ++ ++/* For test purpose only. */ ++void ++lflow_conj_ids_set_test_mode(bool mode) ++{ ++ test_mode = mode; ++} + + /* Allocate n_conjs continuous conjuction ids from the conj_ids for the given +- * lflow_uuid. (0 is never included in an allocated range) ++ * lflow_uuid and dp_uuid. (0 is never included in an allocated range) + * + * The first conjunction id is returned. If no conjunction ids available, or if + * the input is invalid (n_conjs == 0), then 0 is returned. + * +- * The algorithm tries to allocate the parts[0] of the input uuid as the first +- * conjunction id. If it is unavailable, or any of the subsequent n_conjs - 1 +- * ids are unavailable, iterate until the next available n_conjs ids are found. +- * Given that n_conjs is very small (in most cases will be 1), the algorithm +- * should be efficient enough and in most cases just return the lflow_uuid's +- * part[0], which ensures conjunction ids are consistent for the same logical +- * flow in most cases. ++ * The algorithm tries to allocate the hash result of the combination of the ++ * lflow_uuid and dp_uuid as the first conjunction id. If it is unavailable, or ++ * any of the subsequent n_conjs - 1 ids are unavailable, iterate until the ++ * next available n_conjs ids are found. Given that n_conjs is very small (in ++ * most cases will be 1), the algorithm should be efficient enough and in most ++ * cases just return the hash value, which ensures conjunction ids are ++ * consistent for the same logical flow + DP in most cases. + * + * The performance will degrade if most of the uint32_t are allocated because + * conflicts will happen a lot. In practice this is not expected to happen in +@@ -66,16 +100,19 @@ static void lflow_conj_ids_free_(struct conj_ids *, + * smaller than this. */ + uint32_t + lflow_conj_ids_alloc(struct conj_ids *conj_ids, const struct uuid *lflow_uuid, +- uint32_t n_conjs) ++ const struct uuid *dp_uuid, uint32_t n_conjs) + { + if (!n_conjs) { + return 0; + } +- lflow_conj_ids_free_(conj_ids, lflow_uuid, false); ++ lflow_conj_ids_free_for_lflow_dp(conj_ids, lflow_uuid, dp_uuid); + + COVERAGE_INC(lflow_conj_alloc); + +- uint32_t start_conj_id = lflow_uuid->parts[0]; ++ uint32_t start_conj_id = hash_lflow_dp(lflow_uuid, dp_uuid); ++ if (start_conj_id == 0) { ++ start_conj_id++; ++ } + uint32_t initial_id = start_conj_id; + bool initial = true; + while (true) { +@@ -118,7 +155,8 @@ lflow_conj_ids_alloc(struct conj_ids *conj_ids, const struct uuid *lflow_uuid, + } + start_conj_id = conj_id + 1; + } +- lflow_conj_ids_insert_(conj_ids, lflow_uuid, start_conj_id, n_conjs); ++ lflow_conj_ids_insert_(conj_ids, lflow_uuid, dp_uuid, start_conj_id, ++ n_conjs); + return start_conj_id; + } + +@@ -130,12 +168,13 @@ lflow_conj_ids_alloc(struct conj_ids *conj_ids, const struct uuid *lflow_uuid, + bool + lflow_conj_ids_alloc_specified(struct conj_ids *conj_ids, + const struct uuid *lflow_uuid, ++ const struct uuid *dp_uuid, + uint32_t start_conj_id, uint32_t n_conjs) + { + if (!n_conjs) { + return false; + } +- lflow_conj_ids_free_(conj_ids, lflow_uuid, false); ++ lflow_conj_ids_free_for_lflow_dp(conj_ids, lflow_uuid, dp_uuid); + + uint32_t conj_id = start_conj_id; + for (uint32_t i = 0; i < n_conjs; i++) { +@@ -151,7 +190,8 @@ lflow_conj_ids_alloc_specified(struct conj_ids *conj_ids, + } + conj_id++; + } +- lflow_conj_ids_insert_(conj_ids, lflow_uuid, start_conj_id, n_conjs); ++ lflow_conj_ids_insert_(conj_ids, lflow_uuid, dp_uuid, start_conj_id, ++ n_conjs); + COVERAGE_INC(lflow_conj_alloc_specified); + + return true; +@@ -161,7 +201,16 @@ lflow_conj_ids_alloc_specified(struct conj_ids *conj_ids, + void + lflow_conj_ids_free(struct conj_ids *conj_ids, const struct uuid *lflow_uuid) + { +- lflow_conj_ids_free_(conj_ids, lflow_uuid, true); ++ struct lflow_to_dps_node *ltd = lflow_to_dps_find(conj_ids, lflow_uuid); ++ if (!ltd) { ++ return; ++ } ++ struct lflow_conj_node *lflow_conj, *next; ++ LIST_FOR_EACH_SAFE (lflow_conj, next, list_node, <d->dps) { ++ lflow_conj_ids_free_(conj_ids, lflow_conj); ++ } ++ hmap_remove(&conj_ids->lflow_to_dps, <d->hmap_node); ++ free(ltd); + } + + void +@@ -169,6 +218,7 @@ lflow_conj_ids_init(struct conj_ids *conj_ids) + { + hmap_init(&conj_ids->conj_id_allocations); + hmap_init(&conj_ids->lflow_conj_ids); ++ hmap_init(&conj_ids->lflow_to_dps); + } + + void +@@ -185,9 +235,18 @@ lflow_conj_ids_destroy(struct conj_ids *conj_ids) { + HMAP_FOR_EACH_SAFE (lflow_conj, l_c_next, hmap_node, + &conj_ids->lflow_conj_ids) { + hmap_remove(&conj_ids->lflow_conj_ids, &lflow_conj->hmap_node); ++ ovs_list_remove(&lflow_conj->list_node); + free(lflow_conj); + } + hmap_destroy(&conj_ids->lflow_conj_ids); ++ ++ struct lflow_to_dps_node *ltd, *ltd_next; ++ HMAP_FOR_EACH_SAFE (ltd, ltd_next, hmap_node, ++ &conj_ids->lflow_to_dps) { ++ hmap_remove(&conj_ids->lflow_to_dps, <d->hmap_node); ++ free(ltd); ++ } ++ hmap_destroy(&conj_ids->lflow_to_dps); + } + + void lflow_conj_ids_clear(struct conj_ids *conj_ids) { +@@ -204,10 +263,11 @@ lflow_conj_ids_dump(struct conj_ids *conj_ids, struct ds *out_data) + ds_put_cstr(out_data, "Conjunction IDs allocations:\n"); + HMAP_FOR_EACH (lflow_conj, hmap_node, &conj_ids->lflow_conj_ids) { + bool has_conflict = +- (lflow_conj->start_conj_id != lflow_conj->lflow_uuid.parts[0]); +- ds_put_format(out_data, "lflow: "UUID_FMT", start: %"PRIu32 +- ", n: %"PRIu32"%s\n", ++ (lflow_conj->start_conj_id != lflow_conj->hmap_node.hash); ++ ds_put_format(out_data, "lflow: "UUID_FMT", dp: "UUID_FMT", start: %" ++ PRIu32", n: %"PRIu32"%s\n", + UUID_ARGS(&lflow_conj->lflow_uuid), ++ UUID_ARGS(&lflow_conj->dp_uuid), + lflow_conj->start_conj_id, + lflow_conj->n_conjs, + has_conflict ? " (*)" : ""); +@@ -224,11 +284,25 @@ lflow_conj_ids_dump(struct conj_ids *conj_ids, struct ds *out_data) + } + } + ++static struct lflow_to_dps_node * ++lflow_to_dps_find(struct conj_ids *conj_ids, const struct uuid *lflow_uuid) ++{ ++ struct lflow_to_dps_node *ltd; ++ HMAP_FOR_EACH_WITH_HASH (ltd, hmap_node, uuid_hash(lflow_uuid), ++ &conj_ids->lflow_to_dps) { ++ if (uuid_equals(<d->lflow_uuid, lflow_uuid)) { ++ return ltd; ++ } ++ } ++ return NULL; ++} ++ + /* Insert n_conjs conjuntion ids starting from start_conj_id into the conj_ids, + * assuming the ids are confirmed to be available. */ + static void + lflow_conj_ids_insert_(struct conj_ids *conj_ids, + const struct uuid *lflow_uuid, ++ const struct uuid *dp_uuid, + uint32_t start_conj_id, uint32_t n_conjs) + { + ovs_assert(n_conjs); +@@ -243,36 +317,46 @@ lflow_conj_ids_insert_(struct conj_ids *conj_ids, + + struct lflow_conj_node *lflow_conj = xzalloc(sizeof *lflow_conj); + lflow_conj->lflow_uuid = *lflow_uuid; ++ lflow_conj->dp_uuid = *dp_uuid; + lflow_conj->start_conj_id = start_conj_id; + lflow_conj->n_conjs = n_conjs; + hmap_insert(&conj_ids->lflow_conj_ids, &lflow_conj->hmap_node, +- uuid_hash(lflow_uuid)); ++ hash_lflow_dp(lflow_uuid, dp_uuid)); ++ ++ struct lflow_to_dps_node *ltd = lflow_to_dps_find(conj_ids, lflow_uuid); ++ if (!ltd) { ++ ltd = xmalloc(sizeof *ltd); ++ ltd->lflow_uuid = *lflow_uuid; ++ ovs_list_init(<d->dps); ++ hmap_insert(&conj_ids->lflow_to_dps, <d->hmap_node, ++ uuid_hash(lflow_uuid)); ++ } ++ ovs_list_insert(<d->dps, &lflow_conj->list_node); + } + +-static void +-lflow_conj_ids_free_(struct conj_ids *conj_ids, const struct uuid *lflow_uuid, +- bool expected) ++static struct lflow_conj_node * ++lflow_conj_ids_find_(struct conj_ids *conj_ids, ++ const struct uuid *lflow_uuid, ++ const struct uuid *dp_uuid) + { + struct lflow_conj_node *lflow_conj; +- bool found = false; +- HMAP_FOR_EACH_WITH_HASH (lflow_conj, hmap_node, uuid_hash(lflow_uuid), ++ HMAP_FOR_EACH_WITH_HASH (lflow_conj, hmap_node, ++ hash_lflow_dp(lflow_uuid, dp_uuid), + &conj_ids->lflow_conj_ids) { +- if (uuid_equals(&lflow_conj->lflow_uuid, lflow_uuid)) { +- found = true; +- break; ++ if (uuid_equals(&lflow_conj->lflow_uuid, lflow_uuid) && ++ uuid_equals(&lflow_conj->dp_uuid, dp_uuid)) { ++ return lflow_conj; + } + } +- if (!found) { +- return; +- } ++ return NULL; ++} ++ ++static void ++lflow_conj_ids_free_(struct conj_ids *conj_ids, ++ struct lflow_conj_node *lflow_conj) ++{ + ovs_assert(lflow_conj->n_conjs); + COVERAGE_INC(lflow_conj_free); +- if (!expected) { +- /* It is unexpected that an entry is found when calling from +- * alloc/alloc_specified. Something may be wrong in the lflow module. +- */ +- COVERAGE_INC(lflow_conj_free_unexpected); +- } + uint32_t conj_id = lflow_conj->start_conj_id; + for (uint32_t i = 0; i < lflow_conj->n_conjs; i++) { + ovs_assert(conj_id); +@@ -290,5 +374,25 @@ lflow_conj_ids_free_(struct conj_ids *conj_ids, const struct uuid *lflow_uuid, + } + + hmap_remove(&conj_ids->lflow_conj_ids, &lflow_conj->hmap_node); ++ ovs_list_remove(&lflow_conj->list_node); + free(lflow_conj); + } ++ ++static void ++lflow_conj_ids_free_for_lflow_dp(struct conj_ids *conj_ids, ++ const struct uuid *lflow_uuid, ++ const struct uuid *dp_uuid) ++{ ++ struct lflow_conj_node *lflow_conj = lflow_conj_ids_find_(conj_ids, ++ lflow_uuid, ++ dp_uuid); ++ if (!lflow_conj) { ++ return; ++ } ++ ++ /* It is unexpected that an entry is found because this is called only by ++ * alloc/alloc_specified. Something may be wrong in the lflow module. ++ */ ++ COVERAGE_INC(lflow_conj_free_unexpected); ++ lflow_conj_ids_free_(conj_ids, lflow_conj); ++} +diff --git a/controller/lflow-conj-ids.h b/controller/lflow-conj-ids.h +index 6da0a612c..b53e570f2 100644 +--- a/controller/lflow-conj-ids.h ++++ b/controller/lflow-conj-ids.h +@@ -24,20 +24,25 @@ + struct conj_ids { + /* Allocated conjunction ids. Contains struct conj_id_node. */ + struct hmap conj_id_allocations; +- /* A map from lflows to the conjunction ids used. Contains struct ++ /* A map from lflow + DP to the conjunction ids used. Contains struct + * lflow_conj_node. */ + struct hmap lflow_conj_ids; ++ /* A map from lflow to the list of DPs this lflow belongs to. Contains ++ * struct lflow_to_dps_node. */ ++ struct hmap lflow_to_dps; + }; + + uint32_t lflow_conj_ids_alloc(struct conj_ids *, const struct uuid *lflow_uuid, +- uint32_t n_conjs); ++ const struct uuid *dp_uuid, uint32_t n_conjs); + bool lflow_conj_ids_alloc_specified(struct conj_ids *, + const struct uuid *lflow_uuid, ++ const struct uuid *dp_uuid, + uint32_t start_conj_id, uint32_t n_conjs); + void lflow_conj_ids_free(struct conj_ids *, const struct uuid *lflow_uuid); + void lflow_conj_ids_init(struct conj_ids *); + void lflow_conj_ids_destroy(struct conj_ids *); + void lflow_conj_ids_clear(struct conj_ids *); + void lflow_conj_ids_dump(struct conj_ids *, struct ds *out_data); ++void lflow_conj_ids_set_test_mode(bool); + + #endif /* controller/lflow-conj-ids.h */ +diff --git a/controller/lflow.c b/controller/lflow.c +index 933e2f3cc..489347dae 100644 +--- a/controller/lflow.c ++++ b/controller/lflow.c +@@ -852,6 +852,7 @@ consider_logical_flow__(const struct sbrec_logical_flow *lflow, + && lcv->n_conjs + && !lflow_conj_ids_alloc_specified(l_ctx_out->conj_ids, + &lflow->header_.uuid, ++ &dp->header_.uuid, + lcv->conj_id_ofs, lcv->n_conjs)) { + /* This should happen very rarely. */ + VLOG_DBG("lflow "UUID_FMT" match cached with conjunctions, but the" +@@ -915,6 +916,7 @@ consider_logical_flow__(const struct sbrec_logical_flow *lflow, + if (n_conjs) { + start_conj_id = lflow_conj_ids_alloc(l_ctx_out->conj_ids, + &lflow->header_.uuid, ++ &dp->header_.uuid, + n_conjs); + if (!start_conj_id) { + VLOG_ERR("32-bit conjunction ids exhausted!"); +diff --git a/controller/ofctrl.c b/controller/ofctrl.c +index 08fcfed8b..8d958faf1 100644 +--- a/controller/ofctrl.c ++++ b/controller/ofctrl.c +@@ -335,6 +335,22 @@ static struct ovn_extend_table *groups; + /* A reference to the meter_table. */ + static struct ovn_extend_table *meters; + ++/* Installed meter bands. */ ++struct meter_band_data { ++ int64_t burst_size; ++ int64_t rate; ++}; ++ ++struct meter_band_entry { ++ struct meter_band_data *bands; ++ size_t n_bands; ++}; ++ ++static struct shash meter_bands; ++ ++static void ofctrl_meter_bands_destroy(void); ++static void ofctrl_meter_bands_clear(void); ++ + /* MFF_* field ID for our Geneve option. In S_TLV_TABLE_MOD_SENT, this is + * the option we requested (we don't know whether we obtained it yet). In + * S_CLEAR_FLOWS or S_UPDATE_FLOWS, this is really the option we have. */ +@@ -372,6 +388,7 @@ ofctrl_init(struct ovn_extend_table *group_table, + ovn_init_symtab(&symtab); + groups = group_table; + meters = meter_table; ++ shash_init(&meter_bands); + } + + /* S_NEW, for a new connection. +@@ -615,6 +632,7 @@ run_S_CLEAR_FLOWS(void) + /* Clear existing meters, to match the state of the switch. */ + if (meters) { + ovn_extend_table_clear(meters, true); ++ ofctrl_meter_bands_clear(); + } + + /* All flow updates are irrelevant now. */ +@@ -789,6 +807,7 @@ ofctrl_destroy(void) + rconn_packet_counter_destroy(tx_counter); + expr_symtab_destroy(&symtab); + shash_destroy(&symtab); ++ ofctrl_meter_bands_destroy(); + } + + uint64_t +@@ -1802,26 +1821,14 @@ add_meter_string(struct ovn_extend_table_info *m_desired, + } + + static void +-add_meter(struct ovn_extend_table_info *m_desired, +- const struct sbrec_meter_table *meter_table, +- struct ovs_list *msgs) ++update_ovs_meter(struct ovn_extend_table_info *entry, ++ const struct sbrec_meter *sb_meter, int cmd, ++ struct ovs_list *msgs) + { +- const struct sbrec_meter *sb_meter; +- SBREC_METER_TABLE_FOR_EACH (sb_meter, meter_table) { +- if (!strcmp(m_desired->name, sb_meter->name)) { +- break; +- } +- } +- +- if (!sb_meter) { +- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); +- VLOG_ERR_RL(&rl, "could not find meter named \"%s\"", m_desired->name); +- return; +- } + + struct ofputil_meter_mod mm; +- mm.command = OFPMC13_ADD; +- mm.meter.meter_id = m_desired->table_id; ++ mm.command = cmd; ++ mm.meter.meter_id = entry->table_id; + mm.meter.flags = OFPMF13_STATS; + + if (!strcmp(sb_meter->unit, "pktps")) { +@@ -1854,6 +1861,152 @@ add_meter(struct ovn_extend_table_info *m_desired, + free(mm.meter.bands); + } + ++static void ++ofctrl_meter_bands_clear(void) ++{ ++ struct shash_node *node, *next; ++ SHASH_FOR_EACH_SAFE (node, next, &meter_bands) { ++ struct meter_band_entry *mb = node->data; ++ shash_delete(&meter_bands, node); ++ free(mb->bands); ++ free(mb); ++ } ++} ++ ++static void ++ofctrl_meter_bands_destroy(void) ++{ ++ ofctrl_meter_bands_clear(); ++ shash_destroy(&meter_bands); ++} ++ ++static bool ++ofctrl_meter_bands_is_equal(const struct sbrec_meter *sb_meter, ++ struct meter_band_entry *mb) ++{ ++ if (mb->n_bands != sb_meter->n_bands) { ++ return false; ++ } ++ ++ for (int i = 0; i < sb_meter->n_bands; i++) { ++ int j; ++ for (j = 0; j < mb->n_bands; j++) { ++ if (sb_meter->bands[i]->rate == mb->bands[j].rate && ++ sb_meter->bands[i]->burst_size == mb->bands[j].burst_size) { ++ break; ++ } ++ } ++ if (j == mb->n_bands) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++static void ++ofctrl_meter_bands_alloc(const struct sbrec_meter *sb_meter, ++ struct ovn_extend_table_info *entry, ++ struct ovs_list *msgs) ++{ ++ struct meter_band_entry *mb = mb = xzalloc(sizeof *mb); ++ mb->n_bands = sb_meter->n_bands; ++ mb->bands = xcalloc(mb->n_bands, sizeof *mb->bands); ++ for (int i = 0; i < sb_meter->n_bands; i++) { ++ mb->bands[i].rate = sb_meter->bands[i]->rate; ++ mb->bands[i].burst_size = sb_meter->bands[i]->burst_size; ++ } ++ shash_add(&meter_bands, entry->name, mb); ++ update_ovs_meter(entry, sb_meter, OFPMC13_ADD, msgs); ++} ++ ++static void ++ofctrl_meter_bands_update(const struct sbrec_meter *sb_meter, ++ struct ovn_extend_table_info *entry, ++ struct ovs_list *msgs) ++{ ++ struct meter_band_entry *mb = ++ shash_find_data(&meter_bands, entry->name); ++ if (!mb) { ++ ofctrl_meter_bands_alloc(sb_meter, entry, msgs); ++ return; ++ } ++ ++ if (ofctrl_meter_bands_is_equal(sb_meter, mb)) { ++ return; ++ } ++ ++ free(mb->bands); ++ mb->n_bands = sb_meter->n_bands; ++ mb->bands = xcalloc(mb->n_bands, sizeof *mb->bands); ++ for (int i = 0; i < sb_meter->n_bands; i++) { ++ mb->bands[i].rate = sb_meter->bands[i]->rate; ++ mb->bands[i].burst_size = sb_meter->bands[i]->burst_size; ++ } ++ ++ update_ovs_meter(entry, sb_meter, OFPMC13_MODIFY, msgs); ++} ++ ++static void ++ofctrl_meter_bands_erase(struct ovn_extend_table_info *entry, ++ struct ovs_list *msgs) ++{ ++ struct meter_band_entry *mb = ++ shash_find_and_delete(&meter_bands, entry->name); ++ if (mb) { ++ /* Delete the meter. */ ++ struct ofputil_meter_mod mm = { ++ .command = OFPMC13_DELETE, ++ .meter = { .meter_id = entry->table_id }, ++ }; ++ add_meter_mod(&mm, msgs); ++ ++ free(mb->bands); ++ free(mb); ++ } ++} ++ ++static void ++ofctrl_meter_bands_sync(struct ovn_extend_table_info *m_existing, ++ const struct sbrec_meter_table *meter_table, ++ struct ovs_list *msgs) ++{ ++ const struct sbrec_meter *sb_meter; ++ SBREC_METER_TABLE_FOR_EACH (sb_meter, meter_table) { ++ if (!strcmp(m_existing->name, sb_meter->name)) { ++ break; ++ } ++ } ++ ++ if (sb_meter) { ++ /* OFPMC13_ADD or OFPMC13_MODIFY */ ++ ofctrl_meter_bands_update(sb_meter, m_existing, msgs); ++ } else { ++ /* OFPMC13_DELETE */ ++ ofctrl_meter_bands_erase(m_existing, msgs); ++ } ++} ++ ++static void ++add_meter(struct ovn_extend_table_info *m_desired, ++ const struct sbrec_meter_table *meter_table, ++ struct ovs_list *msgs) ++{ ++ const struct sbrec_meter *sb_meter; ++ SBREC_METER_TABLE_FOR_EACH (sb_meter, meter_table) { ++ if (!strcmp(m_desired->name, sb_meter->name)) { ++ break; ++ } ++ } ++ ++ if (!sb_meter) { ++ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); ++ VLOG_ERR_RL(&rl, "could not find meter named \"%s\"", m_desired->name); ++ return; ++ } ++ ++ ofctrl_meter_bands_alloc(sb_meter, m_desired, msgs); ++} ++ + static void + installed_flow_add(struct ovn_flow *d, + struct ofputil_bundle_ctrl_msg *bc, +@@ -1994,7 +2147,20 @@ deleted_flow_lookup(struct hmap *deleted_flows, struct ovn_flow *target) + && f->cookie == target->cookie + && ofpacts_equal(f->ofpacts, f->ofpacts_len, target->ofpacts, + target->ofpacts_len)) { +- return d; ++ /* del_f must have been installed, otherwise it should have ++ * been removed during track_flow_del. */ ++ ovs_assert(d->installed_flow); ++ ++ /* Now we also need to make sure the desired flow being ++ * added/updated has exact same action and cookie as the installed ++ * flow of d. Otherwise, don't merge them, so that the ++ * installed flow can be updated later. */ ++ struct ovn_flow *f_i = &d->installed_flow->flow; ++ if (f_i->cookie == target->cookie ++ && ofpacts_equal(f_i->ofpacts, f_i->ofpacts_len, ++ target->ofpacts, target->ofpacts_len)) { ++ return d; ++ } + } + } + return NULL; +@@ -2023,10 +2189,6 @@ merge_tracked_flows(struct ovn_desired_flow_table *flow_table) + continue; + } + +- /* del_f must have been installed, otherwise it should have been +- * removed during track_flow_add_or_modify. */ +- ovs_assert(del_f->installed_flow); +- + if (!f->installed_flow) { + /* f is not installed yet. */ + replace_installed_to_desired(del_f->installed_flow, del_f, f); +@@ -2232,13 +2394,19 @@ ofctrl_put(struct ovn_desired_flow_table *lflow_table, + /* Iterate through all the desired meters. If there are new ones, + * add them to the switch. */ + struct ovn_extend_table_info *m_desired; +- EXTEND_TABLE_FOR_EACH_UNINSTALLED (m_desired, meters) { +- if (!strncmp(m_desired->name, "__string: ", 10)) { +- /* The "set-meter" action creates a meter entry name that +- * describes the meter itself. */ +- add_meter_string(m_desired, &msgs); ++ HMAP_FOR_EACH (m_desired, hmap_node, &meters->desired) { ++ struct ovn_extend_table_info *m_existing = ++ ovn_extend_table_lookup(&meters->existing, m_desired); ++ if (!m_existing) { ++ if (!strncmp(m_desired->name, "__string: ", 10)) { ++ /* The "set-meter" action creates a meter entry name that ++ * describes the meter itself. */ ++ add_meter_string(m_desired, &msgs); ++ } else { ++ add_meter(m_desired, meter_table, &msgs); ++ } + } else { +- add_meter(m_desired, meter_table, &msgs); ++ ofctrl_meter_bands_sync(m_existing, meter_table, &msgs); + } + } + +@@ -2328,12 +2496,14 @@ ofctrl_put(struct ovn_desired_flow_table *lflow_table, + struct ovn_extend_table_info *m_installed, *next_meter; + EXTEND_TABLE_FOR_EACH_INSTALLED (m_installed, next_meter, meters) { + /* Delete the meter. */ +- struct ofputil_meter_mod mm = { +- .command = OFPMC13_DELETE, +- .meter = { .meter_id = m_installed->table_id }, +- }; +- add_meter_mod(&mm, &msgs); +- ++ ofctrl_meter_bands_erase(m_installed, &msgs); ++ if (!strncmp(m_installed->name, "__string: ", 10)) { ++ struct ofputil_meter_mod mm = { ++ .command = OFPMC13_DELETE, ++ .meter = { .meter_id = m_installed->table_id }, ++ }; ++ add_meter_mod(&mm, &msgs); ++ } + ovn_extend_table_remove_existing(meters, m_installed); + } + +diff --git a/controller/ovn-controller.c b/controller/ovn-controller.c +index 5069aedfc..f85af9353 100644 +--- a/controller/ovn-controller.c ++++ b/controller/ovn-controller.c +@@ -962,7 +962,8 @@ ctrl_register_ovs_idl(struct ovsdb_idl *ovs_idl) + SB_NODE(dhcpv6_options, "dhcpv6_options") \ + SB_NODE(dns, "dns") \ + SB_NODE(load_balancer, "load_balancer") \ +- SB_NODE(fdb, "fdb") ++ SB_NODE(fdb, "fdb") \ ++ SB_NODE(meter, "meter") + + enum sb_engine_node { + #define SB_NODE(NAME, NAME_STR) SB_##NAME, +@@ -2713,6 +2714,26 @@ lflow_output_sb_fdb_handler(struct engine_node *node, void *data) + return handled; + } + ++static bool ++lflow_output_sb_meter_handler(struct engine_node *node, void *data) ++{ ++ struct ed_type_lflow_output *fo = data; ++ struct sbrec_meter_table *meter_table = ++ (struct sbrec_meter_table *)EN_OVSDB_GET( ++ engine_get_input("SB_meter", node)); ++ ++ const struct sbrec_meter *iter; ++ SBREC_METER_TABLE_FOR_EACH_TRACKED (iter, meter_table) { ++ if (ovn_extend_table_desired_lookup_by_name(&fo->meter_table, ++ iter->name)) { ++ engine_set_node_state(node, EN_UPDATED); ++ break; ++ } ++ } ++ ++ return true; ++} ++ + struct ed_type_pflow_output { + /* Desired physical flows. */ + struct ovn_desired_flow_table flow_table; +@@ -3303,6 +3324,8 @@ main(int argc, char *argv[]) + lflow_output_sb_load_balancer_handler); + engine_add_input(&en_lflow_output, &en_sb_fdb, + lflow_output_sb_fdb_handler); ++ engine_add_input(&en_lflow_output, &en_sb_meter, ++ lflow_output_sb_meter_handler); + + engine_add_input(&en_ct_zones, &en_ovs_open_vswitch, NULL); + engine_add_input(&en_ct_zones, &en_ovs_bridge, NULL); diff --git a/controller/physical.c b/controller/physical.c index 836fc769a..aa651b876 100644 --- a/controller/physical.c @@ -44,7 +978,7 @@ index 836fc769a..aa651b876 100644 put_resubmit(OFTABLE_CHECK_LOOPBACK, &ofpacts); } else if (!strcmp(port->type, "chassisredirect") diff --git a/controller/pinctrl.c b/controller/pinctrl.c -index f0667807e..d2bb7f441 100644 +index f0667807e..2dd1bc7bd 100644 --- a/controller/pinctrl.c +++ b/controller/pinctrl.c @@ -1624,12 +1624,8 @@ pinctrl_handle_icmp(struct rconn *swconn, const struct flow *ip_flow, @@ -90,7 +1024,7 @@ index f0667807e..d2bb7f441 100644 /* RFC 1122: 3.2.2 MUST send at least the IP header and 8 bytes * of header. MAY send more. -@@ -1671,51 +1648,40 @@ pinctrl_handle_icmp(struct rconn *swconn, const struct flow *ip_flow, +@@ -1671,51 +1648,46 @@ pinctrl_handle_icmp(struct rconn *swconn, const struct flow *ip_flow, * So, lets return as much as we can. */ /* Calculate available room to include the original IP + data. */ @@ -121,6 +1055,12 @@ index f0667807e..d2bb7f441 100644 + } + + struct icmp_header *ih = dp_packet_l4(&packet); ++ ++ /* The packet's L4 data was allocated and will never be NULL, inform ++ * the compiler about that. ++ */ ++ ovs_assert(ih); ++ + packet_set_icmp(&packet, ICMP4_DST_UNREACH, icmp_code); + + /* Include original IP + data. */ @@ -165,7 +1105,7 @@ index f0667807e..d2bb7f441 100644 /* RFC 4443: 3.1. * -@@ -1730,20 +1696,33 @@ pinctrl_handle_icmp(struct rconn *swconn, const struct flow *ip_flow, +@@ -1730,20 +1702,33 @@ pinctrl_handle_icmp(struct rconn *swconn, const struct flow *ip_flow, * | exceeding the minimum IPv6 MTU [IPv6] | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ @@ -189,9 +1129,7 @@ index f0667807e..d2bb7f441 100644 + CONST_CAST(struct in6_addr *, ip6_src), + CONST_CAST(struct in6_addr *, ip6_dst), + IPPROTO_ICMPV6, 255, ip_total_len); - -- icmpv6_csum = packet_csum_pseudoheader6(dp_packet_l3(&packet)); -- ih = dp_packet_l4(&packet); ++ + struct icmp6_data_header *ih = dp_packet_l4(&packet); + ih->icmp6_base.icmp6_type = ICMP6_DST_UNREACH; + ih->icmp6_base.icmp6_code = 1; @@ -200,7 +1138,9 @@ index f0667807e..d2bb7f441 100644 + ih->icmp6_base.icmp6_code = ICMP6_DST_UNREACH_NOPORT; + } + ih->icmp6_base.icmp6_cksum = 0; -+ + +- icmpv6_csum = packet_csum_pseudoheader6(dp_packet_l3(&packet)); +- ih = dp_packet_l4(&packet); + void *data = ih + 1; + memcpy(data, in_ip, in_ip_len); + uint32_t icmpv6_csum = @@ -208,7 +1148,7 @@ index f0667807e..d2bb7f441 100644 ih->icmp6_base.icmp6_cksum = csum_finish( csum_continue(icmpv6_csum, ih, in_ip_len + ICMP6_DATA_HEADER_LEN)); -@@ -1777,7 +1756,6 @@ pinctrl_handle_tcp_reset(struct rconn *swconn, const struct flow *ip_flow, +@@ -1777,7 +1762,6 @@ pinctrl_handle_tcp_reset(struct rconn *swconn, const struct flow *ip_flow, struct dp_packet packet; dp_packet_use_stub(&packet, packet_stub, sizeof packet_stub); @@ -216,7 +1156,7 @@ index f0667807e..d2bb7f441 100644 struct eth_addr eth_src = loopback ? ip_flow->dl_dst : ip_flow->dl_src; struct eth_addr eth_dst = loopback ? ip_flow->dl_src : ip_flow->dl_dst; -@@ -1798,8 +1776,8 @@ pinctrl_handle_tcp_reset(struct rconn *swconn, const struct flow *ip_flow, +@@ -1798,8 +1782,9 @@ pinctrl_handle_tcp_reset(struct rconn *swconn, const struct flow *ip_flow, IPPROTO_TCP, 63, TCP_HEADER_LEN); } @@ -224,10 +1164,11 @@ index f0667807e..d2bb7f441 100644 - dp_packet_set_l4(&packet, th); + struct tcp_header *th = dp_packet_l4(&packet); + ++ th->tcp_csum = 0; th->tcp_dst = ip_flow->tp_src; th->tcp_src = ip_flow->tp_dst; -@@ -1825,18 +1803,6 @@ pinctrl_handle_tcp_reset(struct rconn *swconn, const struct flow *ip_flow, +@@ -1825,18 +1810,6 @@ pinctrl_handle_tcp_reset(struct rconn *swconn, const struct flow *ip_flow, dp_packet_uninit(&packet); } @@ -246,7 +1187,7 @@ index f0667807e..d2bb7f441 100644 static void pinctrl_handle_sctp_abort(struct rconn *swconn, const struct flow *ip_flow, struct dp_packet *pkt_in, -@@ -1870,7 +1836,7 @@ pinctrl_handle_sctp_abort(struct rconn *swconn, const struct flow *ip_flow, +@@ -1870,7 +1843,7 @@ pinctrl_handle_sctp_abort(struct rconn *swconn, const struct flow *ip_flow, return; } @@ -255,7 +1196,7 @@ index f0667807e..d2bb7f441 100644 if (sh_in_chunk->sctp_chunk_type == SCTP_CHUNK_TYPE_INIT) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); sh_in_init = dp_packet_at(pkt_in, pkt_in->l4_ofs + -@@ -1909,8 +1875,7 @@ pinctrl_handle_sctp_abort(struct rconn *swconn, const struct flow *ip_flow, +@@ -1909,8 +1882,7 @@ pinctrl_handle_sctp_abort(struct rconn *swconn, const struct flow *ip_flow, SCTP_CHUNK_HEADER_LEN); } @@ -265,7 +1206,7 @@ index f0667807e..d2bb7f441 100644 sh->sctp_dst = ip_flow->tp_src; sh->sctp_src = ip_flow->tp_dst; put_16aligned_be32(&sh->sctp_csum, 0); -@@ -1918,7 +1883,7 @@ pinctrl_handle_sctp_abort(struct rconn *swconn, const struct flow *ip_flow, +@@ -1918,7 +1890,7 @@ pinctrl_handle_sctp_abort(struct rconn *swconn, const struct flow *ip_flow, bool tag_reflected; if (get_16aligned_be32(&sh_in->sctp_vtag) == 0 && sh_in_init) { /* See RFC 4960 Section 8.4, item 3. */ @@ -274,7 +1215,7 @@ index f0667807e..d2bb7f441 100644 tag_reflected = false; } else { /* See RFC 4960 Section 8.4, item 8. */ -@@ -1926,7 +1891,11 @@ pinctrl_handle_sctp_abort(struct rconn *swconn, const struct flow *ip_flow, +@@ -1926,7 +1898,11 @@ pinctrl_handle_sctp_abort(struct rconn *swconn, const struct flow *ip_flow, tag_reflected = true; } @@ -287,7 +1228,7 @@ index f0667807e..d2bb7f441 100644 put_16aligned_be32(&sh->sctp_csum, crc32c((void *) sh, dp_packet_l4_size(&packet))); -@@ -2942,7 +2911,7 @@ pinctrl_handle_dns_lookup( +@@ -2942,7 +2918,7 @@ pinctrl_handle_dns_lookup( goto exit; } @@ -296,18 +1237,92 @@ index f0667807e..d2bb7f441 100644 /* Supported query types - A, AAAA, ANY and PTR */ if (!(query_type == DNS_QUERY_TYPE_A || query_type == DNS_QUERY_TYPE_AAAA || query_type == DNS_QUERY_TYPE_ANY -@@ -4564,16 +4533,15 @@ pinctrl_compose_ipv4(struct dp_packet *packet, struct eth_addr eth_src, +@@ -3047,8 +3023,9 @@ pinctrl_handle_dns_lookup( + /* Set the response bit to 1 in the flags. */ + out_dns_header->lo_flag |= 0x80; + +- /* Set the answer RR. */ ++ /* Set the answer RRs. */ + out_dns_header->ancount = htons(ancount); ++ out_dns_header->arcount = 0; + + /* Copy the Query section. */ + dp_packet_put(&pkt_out, dp_packet_data(pkt_in), dp_packet_size(pkt_in)); +@@ -3189,7 +3166,9 @@ process_packet_in(struct rconn *swconn, const struct ofp_header *msg) + break; + + case ACTION_OPCODE_LOG: +- handle_acl_log(&headers, &userdata); ++ handle_acl_log(&headers, &userdata, ++ pin.table_id < OFTABLE_LOG_EGRESS_PIPELINE ++ ? "from-lport" : "to-lport"); + break; + + case ACTION_OPCODE_PUT_ND_RA_OPTS: +@@ -3736,37 +3715,14 @@ static void + packet_put_ra_dnssl_opt(struct dp_packet *b, ovs_be32 lifetime, + char *dnssl_data) + { +- char *dnssl_list, *t0, *r0 = NULL, dnssl[255] = {}; + size_t prev_l4_size = dp_packet_l4_size(b); +- size_t size = sizeof(struct ovs_nd_dnssl); +- int i = 0; +- +- dnssl_list = xstrdup(dnssl_data); +- +- /* Multiple DNS Search List must be 'comma' separated +- * (e.g. "a.b.c, d.e.f"). Domain names must be encoded +- * as described in Section 3.1 of RFC1035. +- * (e.g if dns list is a.b.c,www.ovn.org, it will be encoded as: +- * 01 61 01 62 01 63 00 03 77 77 77 03 6f 76 63 03 6f 72 67 00 +- */ +- for (t0 = strtok_r(dnssl_list, ",", &r0); t0; +- t0 = strtok_r(NULL, ",", &r0)) { +- char *t1, *r1 = NULL; +- +- size += strlen(t0) + 2; +- if (size > sizeof(dnssl)) { +- goto out; +- } ++ char dnssl[255] = {}; ++ int size; + +- for (t1 = strtok_r(t0, ".", &r1); t1; +- t1 = strtok_r(NULL, ".", &r1)) { +- dnssl[i++] = strlen(t1); +- memcpy(&dnssl[i], t1, strlen(t1)); +- i += strlen(t1); +- } +- dnssl[i++] = 0; ++ size = encode_ra_dnssl_opt(dnssl_data, dnssl, sizeof(dnssl)); ++ if (size < 0) { ++ return; + } +- size = ROUND_UP(size, 8); + + struct ip6_hdr *nh = dp_packet_l3(b); + nh->ip6_plen = htons(prev_l4_size + size); +@@ -3784,8 +3740,6 @@ packet_put_ra_dnssl_opt(struct dp_packet *b, ovs_be32 lifetime, + uint32_t icmp_csum = packet_csum_pseudoheader6(dp_packet_l3(b)); + ra->icmph.icmp6_cksum = csum_finish(csum_continue(icmp_csum, ra, + prev_l4_size + size)); +-out: +- free(dnssl_list); + } + + static void +@@ -4564,16 +4518,15 @@ pinctrl_compose_ipv4(struct dp_packet *packet, struct eth_addr eth_src, ovs_be32 ipv4_dst, uint8_t ip_proto, uint8_t ttl, uint16_t ip_payload_len) { - dp_packet_clear(packet); - packet->packet_type = htonl(PT_ETH); -- -- struct eth_header *eh = dp_packet_put_zeros(packet, sizeof *eh); -- struct ip_header *nh = dp_packet_put_zeros(packet, sizeof *nh); + struct ip_header *nh; + size_t ip_tot_len = sizeof *nh + ip_payload_len; +- struct eth_header *eh = dp_packet_put_zeros(packet, sizeof *eh); +- struct ip_header *nh = dp_packet_put_zeros(packet, sizeof *nh); +- - eh->eth_dst = eth_dst; - eh->eth_src = eth_src; - eh->eth_type = htons(ETH_TYPE_IP); @@ -321,7 +1336,7 @@ index f0667807e..d2bb7f441 100644 nh->ip_ihl_ver = IP_IHL_VER(5, 4); nh->ip_tot_len = htons(sizeof *nh + ip_payload_len); nh->ip_tos = IP_DSCP_CS6; -@@ -4584,6 +4552,7 @@ pinctrl_compose_ipv4(struct dp_packet *packet, struct eth_addr eth_src, +@@ -4584,6 +4537,7 @@ pinctrl_compose_ipv4(struct dp_packet *packet, struct eth_addr eth_src, nh->ip_csum = 0; nh->ip_csum = csum(nh, sizeof *nh); @@ -329,7 +1344,7 @@ index f0667807e..d2bb7f441 100644 } static void -@@ -4592,23 +4561,21 @@ pinctrl_compose_ipv6(struct dp_packet *packet, struct eth_addr eth_src, +@@ -4592,22 +4546,20 @@ pinctrl_compose_ipv6(struct dp_packet *packet, struct eth_addr eth_src, struct in6_addr *ipv6_dst, uint8_t ip_proto, uint8_t ttl, uint16_t ip_payload_len) { @@ -357,12 +1372,11 @@ index f0667807e..d2bb7f441 100644 nh->ip6_nxt = ip_proto; nh->ip6_plen = htons(ip_payload_len); - packet_set_ipv6(packet, ipv6_src, ipv6_dst, 0, 0, ttl); + dp_packet_set_l4(packet, nh + 1); + packet_set_ipv6(packet, ipv6_src, ipv6_dst, 0, 0, ttl); } - /* -@@ -5400,10 +5367,6 @@ ip_mcast_querier_send_igmp(struct rconn *swconn, struct ip_mcast_snoop *ip_ms) +@@ -5400,10 +5352,6 @@ ip_mcast_querier_send_igmp(struct rconn *swconn, struct ip_mcast_snoop *ip_ms) ip_ms->cfg.query_ipv4_dst, IPPROTO_IGMP, 1, sizeof(struct igmpv3_query_header)); @@ -373,7 +1387,7 @@ index f0667807e..d2bb7f441 100644 /* IGMP query max-response in tenths of seconds. */ uint8_t max_response = ip_ms->cfg.query_max_resp_s * 10; uint8_t qqic = max_response; -@@ -5449,15 +5412,10 @@ ip_mcast_querier_send_mld(struct rconn *swconn, struct ip_mcast_snoop *ip_ms) +@@ -5449,15 +5397,10 @@ ip_mcast_querier_send_mld(struct rconn *swconn, struct ip_mcast_snoop *ip_ms) IPPROTO_HOPOPTS, 1, IPV6_EXT_HEADER_LEN + MLD_QUERY_HEADER_LEN); @@ -390,7 +1404,7 @@ index f0667807e..d2bb7f441 100644 /* MLD query max-response in milliseconds. */ uint16_t max_response = ip_ms->cfg.query_max_resp_s * 1000; uint8_t qqic = ip_ms->cfg.query_max_resp_s; -@@ -6033,6 +5991,8 @@ pinctrl_handle_put_nd_ra_opts( +@@ -6033,6 +5976,8 @@ pinctrl_handle_put_nd_ra_opts( struct dp_packet pkt_out; dp_packet_init(&pkt_out, new_packet_size); dp_packet_clear(&pkt_out); @@ -399,7 +1413,7 @@ index f0667807e..d2bb7f441 100644 dp_packet_prealloc_tailroom(&pkt_out, new_packet_size); pkt_out_ptr = &pkt_out; -@@ -6155,23 +6115,26 @@ wait_controller_event(struct ovsdb_idl_txn *ovnsb_idl_txn) +@@ -6155,23 +6100,26 @@ wait_controller_event(struct ovsdb_idl_txn *ovnsb_idl_txn) static bool pinctrl_handle_empty_lb_backends_opts(struct ofpbuf *userdata) { @@ -430,7 +1444,7 @@ index f0667807e..d2bb7f441 100644 case EMPTY_LB_VIP: vip = xmemdup0(userdata_opt_data, size); break; -@@ -6820,8 +6783,6 @@ bfd_monitor_put_bfd_msg(struct bfd_entry *entry, struct dp_packet *packet, +@@ -6820,8 +6768,6 @@ bfd_monitor_put_bfd_msg(struct bfd_entry *entry, struct dp_packet *packet, { int payload_len = sizeof(struct udp_header) + sizeof(struct bfd_msg); @@ -439,7 +1453,7 @@ index f0667807e..d2bb7f441 100644 if (IN6_IS_ADDR_V4MAPPED(&entry->ip_src)) { ovs_be32 ip_src = in6_addr_get_mapped_ipv4(&entry->ip_src); ovs_be32 ip_dst = in6_addr_get_mapped_ipv4(&entry->ip_dst); -@@ -6833,13 +6794,13 @@ bfd_monitor_put_bfd_msg(struct bfd_entry *entry, struct dp_packet *packet, +@@ -6833,13 +6779,13 @@ bfd_monitor_put_bfd_msg(struct bfd_entry *entry, struct dp_packet *packet, MAXTTL, payload_len); } @@ -455,7 +1469,7 @@ index f0667807e..d2bb7f441 100644 msg->vers_diag = (BFD_VERSION << 5); msg->mult = entry->local_mult; msg->length = BFD_PACKET_LEN; -@@ -7383,7 +7344,7 @@ svc_monitor_send_tcp_health_check__(struct rconn *swconn, +@@ -7383,7 +7329,7 @@ svc_monitor_send_tcp_health_check__(struct rconn *swconn, ip4_src, in6_addr_get_mapped_ipv4(&svc_mon->ip), IPPROTO_TCP, 63, TCP_HEADER_LEN); @@ -464,7 +1478,7 @@ index f0667807e..d2bb7f441 100644 dp_packet_set_l4(&packet, th); th->tcp_dst = htons(svc_mon->proto_port); th->tcp_src = tcp_src; -@@ -7446,13 +7407,12 @@ svc_monitor_send_udp_health_check(struct rconn *swconn, +@@ -7446,13 +7392,12 @@ svc_monitor_send_udp_health_check(struct rconn *swconn, ip4_src, in6_addr_get_mapped_ipv4(&svc_mon->ip), IPPROTO_UDP, 63, UDP_HEADER_LEN + 8); @@ -479,25 +1493,123 @@ index f0667807e..d2bb7f441 100644 uint64_t ofpacts_stub[4096 / 8]; struct ofpbuf ofpacts = OFPBUF_STUB_INITIALIZER(ofpacts_stub); +diff --git a/controller/test-lflow-conj-ids.c b/controller/test-lflow-conj-ids.c +index 55eb3c7b6..5d692f1d1 100644 +--- a/controller/test-lflow-conj-ids.c ++++ b/controller/test-lflow-conj-ids.c +@@ -44,7 +44,9 @@ test_conj_ids_operations(struct ovs_cmdl_context *ctx) + unsigned int shift = 1; + unsigned int n_ops; + struct conj_ids conj_ids; ++ struct uuid dp_uuid = UUID_ZERO; + lflow_conj_ids_init(&conj_ids); ++ lflow_conj_ids_set_test_mode(true); + + if (!test_read_uint_value(ctx, shift++, "n_ops", &n_ops)) { + goto done; +@@ -69,7 +71,7 @@ test_conj_ids_operations(struct ovs_cmdl_context *ctx) + } + + uint32_t start_conj_id = lflow_conj_ids_alloc(&conj_ids, &uuid, +- n_conjs); ++ &dp_uuid, n_conjs); + printf("alloc("UUID_FMT", %"PRIu32"): 0x%"PRIx32"\n", + UUID_ARGS(&uuid), n_conjs, start_conj_id); + } else if (!strcmp(op, "alloc-specified")) { +@@ -90,7 +92,8 @@ test_conj_ids_operations(struct ovs_cmdl_context *ctx) + } + + bool ret = lflow_conj_ids_alloc_specified(&conj_ids, &uuid, +- start_conj_id, n_conjs); ++ &dp_uuid, start_conj_id, ++ n_conjs); + printf("alloc_specified("UUID_FMT", 0x%"PRIx32", %"PRIu32"): %s\n", + UUID_ARGS(&uuid), start_conj_id, n_conjs, + ret ? "true" : "false"); diff --git a/debian/changelog b/debian/changelog -index 0cc5f14ac..0d8593083 100644 +index 0cc5f14ac..1a1c7364e 100644 --- a/debian/changelog +++ b/debian/changelog -@@ -1,3 +1,9 @@ +@@ -1,3 +1,15 @@ ++OVN (21.12.2-1) unstable; urgency=low ++ [ OVN team ] ++ * New upstream version ++ ++ -- OVN team Fri, 11 Mar 2022 13:22:24 -0500 ++ +OVN (21.12.1-1) unstable; urgency=low + [ OVN team ] + * New upstream version + -+ -- OVN team Wed, 22 Dec 2021 09:45:52 -0500 ++ -- OVN team Fri, 11 Mar 2022 13:22:24 -0500 + ovn (21.12.0-1) unstable; urgency=low * New upstream version +diff --git a/include/ovn/actions.h b/include/ovn/actions.h +index cdef5fb03..0641b927e 100644 +--- a/include/ovn/actions.h ++++ b/include/ovn/actions.h +@@ -807,5 +807,6 @@ void ovnacts_encode(const struct ovnact[], size_t ovnacts_len, + + void ovnacts_free(struct ovnact[], size_t ovnacts_len); + char *ovnact_op_to_string(uint32_t); ++int encode_ra_dnssl_opt(char *data, char *buf, int buf_len); + + #endif /* ovn/actions.h */ +diff --git a/lib/acl-log.c b/lib/acl-log.c +index 220b6dc30..9530dd763 100644 +--- a/lib/acl-log.c ++++ b/lib/acl-log.c +@@ -76,7 +76,8 @@ log_severity_from_string(const char *name) + } + + void +-handle_acl_log(const struct flow *headers, struct ofpbuf *userdata) ++handle_acl_log(const struct flow *headers, struct ofpbuf *userdata, ++ const char *direction) + { + if (!VLOG_IS_INFO_ENABLED()) { + return; +@@ -94,9 +95,10 @@ handle_acl_log(const struct flow *headers, struct ofpbuf *userdata) + struct ds ds = DS_EMPTY_INITIALIZER; + ds_put_cstr(&ds, "name="); + json_string_escape(name_len ? name : "", &ds); +- ds_put_format(&ds, ", verdict=%s, severity=%s: ", ++ ds_put_format(&ds, ", verdict=%s, severity=%s, direction=%s: ", + log_verdict_to_string(lph->verdict), +- log_severity_to_string(lph->severity)); ++ log_severity_to_string(lph->severity), ++ direction); + flow_format(&ds, headers, NULL); + + VLOG_INFO("%s", ds_cstr(&ds)); +diff --git a/lib/acl-log.h b/lib/acl-log.h +index 4f23f790d..da7fa2f02 100644 +--- a/lib/acl-log.h ++++ b/lib/acl-log.h +@@ -49,6 +49,7 @@ const char *log_verdict_to_string(uint8_t verdict); + const char *log_severity_to_string(uint8_t severity); + uint8_t log_severity_from_string(const char *name); + +-void handle_acl_log(const struct flow *headers, struct ofpbuf *userdata); ++void handle_acl_log(const struct flow *headers, struct ofpbuf *userdata, ++ const char *direction); + + #endif /* lib/acl-log.h */ diff --git a/lib/actions.c b/lib/actions.c -index da00ee349..a78d01198 100644 +index da00ee349..c46772e78 100644 --- a/lib/actions.c +++ b/lib/actions.c -@@ -1842,19 +1842,20 @@ encode_event_empty_lb_backends_opts(struct ofpbuf *ofpacts, +@@ -40,6 +40,7 @@ + #include "simap.h" + #include "uuid.h" + #include "socket-util.h" ++#include "lib/ovn-util.h" + + VLOG_DEFINE_THIS_MODULE(actions); + +@@ -1842,19 +1843,20 @@ encode_event_empty_lb_backends_opts(struct ofpbuf *ofpacts, { for (const struct ovnact_gen_option *o = event->options; o < &event->options[event->n_options]; o++) { @@ -530,11 +1642,254 @@ index da00ee349..a78d01198 100644 } } +@@ -2987,6 +2989,15 @@ parse_put_nd_ra_opts(struct action_context *ctx, const struct expr_field *dst, + case ND_OPT_MTU: + ok = c->format == LEX_F_DECIMAL; + break; ++ ++ case ND_OPT_RDNSS: ++ ok = c->format == LEX_F_IPV6; ++ break; ++ ++ case ND_OPT_ROUTE_INFO_TYPE: ++ case ND_OPT_DNSSL: ++ ok = !!c->string; ++ break; + } + + if (!ok) { +@@ -3017,6 +3028,109 @@ format_PUT_ND_RA_OPTS(const struct ovnact_put_opts *po, + format_put_opts("put_nd_ra_opts", po, s); + } + ++int encode_ra_dnssl_opt(char *data, char *buf, int buf_len) ++{ ++ size_t size = sizeof(struct ovs_nd_dnssl); ++ char *dnssl = xstrdup(data); ++ char *t0, *r0 = NULL; ++ int i = 0; ++ ++ /* Multiple DNS Search List must be 'comma' separated ++ * (e.g. "a.b.c, d.e.f"). Domain names must be encoded ++ * as described in Section 3.1 of RFC1035. ++ * (e.g if dns list is a.b.c,www.ovn.org, it will be encoded as: ++ * 01 61 01 62 01 63 00 03 77 77 77 03 6f 76 63 03 6f 72 67 00 ++ */ ++ for (t0 = strtok_r(dnssl, ",", &r0); t0; ++ t0 = strtok_r(NULL, ",", &r0)) { ++ char *t1, *r1 = NULL; ++ ++ size += strlen(t0) + 2; ++ if (size > buf_len) { ++ free(dnssl); ++ return -EINVAL; ++ } ++ ++ for (t1 = strtok_r(t0, ".", &r1); t1; ++ t1 = strtok_r(NULL, ".", &r1)) { ++ int len = strlen(t1); ++ if (len > UINT8_MAX) { ++ len = UINT8_MAX; ++ } ++ ++ buf[i++] = len; ++ memcpy(&buf[i], t1, len); ++ i += len; ++ } ++ buf[i++] = 0; ++ } ++ free(dnssl); ++ ++ return ROUND_UP(size, 8); ++} ++ ++static void ++encode_ra_route_info_opt(struct ofpbuf *ofpacts, char *route_data) ++{ ++ char *route_list = xstrdup(route_data); ++ char *t0, *r0 = NULL; ++ ++ for (t0 = strtok_r(route_list, ",", &r0); t0; ++ t0 = strtok_r(NULL, ",", &r0)) { ++ struct ovs_nd_route_info nd_rinfo; ++ char *t1, *r1 = NULL; ++ int index; ++ ++ for (t1 = strtok_r(t0, "-", &r1), index = 0; t1; ++ t1 = strtok_r(NULL, "-", &r1), index++) { ++ ++ nd_rinfo.type = ND_OPT_ROUTE_INFO_TYPE; ++ nd_rinfo.route_lifetime = htonl(0xffffffff); ++ ++ switch (index) { ++ case 0: ++ if (!strcmp(t1, "HIGH")) { ++ nd_rinfo.flags = IPV6_ND_RA_OPT_PRF_HIGH; ++ } else if (!strcmp(t1, "LOW")) { ++ nd_rinfo.flags = IPV6_ND_RA_OPT_PRF_LOW; ++ } else { ++ nd_rinfo.flags = IPV6_ND_RA_OPT_PRF_NORMAL; ++ } ++ break; ++ case 1: { ++ struct lport_addresses route; ++ uint8_t plen; ++ ++ if (!extract_ip_addresses(t1, &route)) { ++ goto out; ++ } ++ if (!route.n_ipv6_addrs) { ++ destroy_lport_addresses(&route); ++ goto out; ++ } ++ ++ nd_rinfo.prefix_len = route.ipv6_addrs->plen; ++ plen = DIV_ROUND_UP(nd_rinfo.prefix_len, 64); ++ nd_rinfo.len = 1 + plen; ++ struct ovs_nd_route_info *rinfo_opt = ++ ofpbuf_put_uninit(ofpacts, sizeof *rinfo_opt); ++ memcpy(rinfo_opt, &nd_rinfo, sizeof *rinfo_opt); ++ void *data = ofpbuf_put_uninit(ofpacts, plen * 8); ++ memcpy(data, &route.ipv6_addrs->network, plen * 8); ++ ++ destroy_lport_addresses(&route); ++ index = 0; ++ break; ++ } ++ default: ++ break; ++ } ++ } ++ } ++out: ++ free(route_list); ++} ++ + static void + encode_put_nd_ra_option(const struct ovnact_gen_option *o, + struct ofpbuf *ofpacts, ptrdiff_t ra_offset) +@@ -3091,6 +3205,46 @@ encode_put_nd_ra_option(const struct ovnact_gen_option *o, + sizeof(ovs_be32[4])); + break; + } ++ ++ case ND_OPT_RDNSS: ++ { ++ struct nd_rdnss_opt *rdnss_opt = ++ ofpbuf_put_uninit(ofpacts, sizeof *rdnss_opt); ++ rdnss_opt->type = ND_OPT_RDNSS; ++ rdnss_opt->len = 3; ++ rdnss_opt->reserved = htons(0); ++ put_16aligned_be32(&rdnss_opt->lifetime, htonl(0xffffffff)); ++ void *dns = ofpbuf_put_uninit(ofpacts, sizeof(ovs_be32[4])); ++ memcpy(dns, &c->value.be128[7].be32, sizeof(ovs_be32[4])); ++ break; ++ } ++ ++ case ND_OPT_DNSSL: ++ { ++ char dnssl[255] = {}; ++ int size; ++ ++ size = encode_ra_dnssl_opt(c->string, dnssl, sizeof(dnssl)); ++ if (size < 0) { ++ break; ++ } ++ ++ struct ovs_nd_dnssl *nd_dnssl = ++ ofpbuf_put_uninit(ofpacts, sizeof *nd_dnssl); ++ nd_dnssl->type = ND_OPT_DNSSL; ++ nd_dnssl->len = size / 8; ++ nd_dnssl->reserved = 0; ++ put_16aligned_be32(&nd_dnssl->lifetime, htonl(0xffffffff)); ++ void *p = ofpbuf_put_uninit(ofpacts, size - sizeof *nd_dnssl); ++ memcpy(p, dnssl, size - sizeof *nd_dnssl); ++ break; ++ } ++ ++ case ND_OPT_ROUTE_INFO_TYPE: ++ { ++ encode_ra_route_info_opt(ofpacts, c->string); ++ break; ++ } + } + } + +diff --git a/lib/extend-table.c b/lib/extend-table.c +index c708e24b9..32d541b55 100644 +--- a/lib/extend-table.c ++++ b/lib/extend-table.c +@@ -337,3 +337,17 @@ ovn_extend_table_assign_id(struct ovn_extend_table *table, const char *name, + + return table_id; + } ++ ++struct ovn_extend_table_info * ++ovn_extend_table_desired_lookup_by_name(struct ovn_extend_table * table, ++ const char *name) ++{ ++ uint32_t hash = hash_string(name, 0); ++ struct ovn_extend_table_info *m_desired; ++ HMAP_FOR_EACH_WITH_HASH (m_desired, hmap_node, hash, &table->desired) { ++ if (!strcmp(m_desired->name, name)) { ++ return m_desired; ++ } ++ } ++ return NULL; ++} +diff --git a/lib/extend-table.h b/lib/extend-table.h +index 4d80cfd80..6240b946e 100644 +--- a/lib/extend-table.h ++++ b/lib/extend-table.h +@@ -94,6 +94,10 @@ uint32_t ovn_extend_table_assign_id(struct ovn_extend_table *, + const char *name, + struct uuid lflow_uuid); + ++struct ovn_extend_table_info * ++ovn_extend_table_desired_lookup_by_name(struct ovn_extend_table * table, ++ const char *name); ++ + /* Iterates 'DESIRED' through all of the 'ovn_extend_table_info's in + * 'TABLE'->desired that are not in 'TABLE'->existing. (The loop body + * presumably adds them.) */ +diff --git a/lib/inc-proc-eng.c b/lib/inc-proc-eng.c +index 2958a55e3..b098c5089 100644 +--- a/lib/inc-proc-eng.c ++++ b/lib/inc-proc-eng.c +@@ -336,14 +336,11 @@ engine_recompute(struct engine_node *node, bool allowed, + const char *reason_fmt, ...) + { + char *reason = NULL; ++ va_list reason_args; + +- if (VLOG_IS_DBG_ENABLED()) { +- va_list reason_args; +- +- va_start(reason_args, reason_fmt); +- reason = xvasprintf(reason_fmt, reason_args); +- va_end(reason_args); +- } ++ va_start(reason_args, reason_fmt); ++ reason = xvasprintf(reason_fmt, reason_args); ++ va_end(reason_args); + + if (!allowed) { + VLOG_DBG("node: %s, recompute (%s) aborted", node->name, reason); diff --git a/lib/ovn-l7.h b/lib/ovn-l7.h -index 9a33f5cda..256e963d9 100644 +index 9a33f5cda..49ecea81f 100644 --- a/lib/ovn-l7.h +++ b/lib/ovn-l7.h -@@ -502,7 +502,7 @@ struct mld_query_header { +@@ -409,6 +409,9 @@ nd_ra_opts_init(struct hmap *nd_ra_opts) + nd_ra_opt_add(nd_ra_opts, "slla", ND_OPT_SOURCE_LINKADDR, "mac"); + nd_ra_opt_add(nd_ra_opts, "prefix", ND_OPT_PREFIX_INFORMATION, "ipv6"); + nd_ra_opt_add(nd_ra_opts, "mtu", ND_OPT_MTU, "uint32"); ++ nd_ra_opt_add(nd_ra_opts, "rdnss", ND_OPT_RDNSS, "ipv6"); ++ nd_ra_opt_add(nd_ra_opts, "dnssl", ND_OPT_DNSSL, "str"); ++ nd_ra_opt_add(nd_ra_opts, "route_info", ND_OPT_ROUTE_INFO_TYPE, "str"); + } + + #define EMPTY_LB_VIP 1 +@@ -502,7 +505,7 @@ struct mld_query_header { ovs_be16 csum; ovs_be16 max_resp; ovs_be16 rsvd; @@ -543,7 +1898,7 @@ index 9a33f5cda..256e963d9 100644 uint8_t srs_qrv; uint8_t qqic; ovs_be16 nsrcs; -@@ -518,7 +518,9 @@ packet_set_mld_query(struct dp_packet *packet, uint16_t max_resp, +@@ -518,7 +521,9 @@ packet_set_mld_query(struct dp_packet *packet, uint16_t max_resp, const struct in6_addr *group, bool srs, uint8_t qrv, uint8_t qqic) { @@ -554,6 +1909,18 @@ index 9a33f5cda..256e963d9 100644 mqh->type = MLD_QUERY; mqh->code = 0; mqh->max_resp = htons(max_resp); +diff --git a/lib/ovn-parallel-hmap.c b/lib/ovn-parallel-hmap.c +index 56ceed8e8..7edc4c0b6 100644 +--- a/lib/ovn-parallel-hmap.c ++++ b/lib/ovn-parallel-hmap.c +@@ -404,6 +404,7 @@ static void + setup_worker_pools(bool force) { + int cores, nodes; + ++ ovs_numa_init(); + nodes = ovs_numa_get_n_numas(); + if (nodes == OVS_NUMA_UNSPEC || nodes <= 0) { + nodes = 1; diff --git a/lib/ovn-util.h b/lib/ovn-util.h index a923c3b65..b212c64b7 100644 --- a/lib/ovn-util.h @@ -580,13 +1947,2293 @@ index a923c3b65..b212c64b7 100644 /* These are the only SCTP chunk types that OVN cares about. * There is no need to define the other chunk types until they are -diff --git a/tests/ovn.at b/tests/ovn.at -index 9ec62e321..92e284e8a 100644 ---- a/tests/ovn.at -+++ b/tests/ovn.at -@@ -9658,8 +9658,8 @@ as hv1 ovs-appctl netdev-dummy/receive vm1 $packet +diff --git a/northd/northd.c b/northd/northd.c +index c714227b2..fbc29b554 100644 +--- a/northd/northd.c ++++ b/northd/northd.c +@@ -112,18 +112,20 @@ enum ovn_stage { + PIPELINE_STAGE(SWITCH, IN, ACL, 9, "ls_in_acl") \ + PIPELINE_STAGE(SWITCH, IN, QOS_MARK, 10, "ls_in_qos_mark") \ + PIPELINE_STAGE(SWITCH, IN, QOS_METER, 11, "ls_in_qos_meter") \ +- PIPELINE_STAGE(SWITCH, IN, STATEFUL, 12, "ls_in_stateful") \ +- PIPELINE_STAGE(SWITCH, IN, PRE_HAIRPIN, 13, "ls_in_pre_hairpin") \ +- PIPELINE_STAGE(SWITCH, IN, NAT_HAIRPIN, 14, "ls_in_nat_hairpin") \ +- PIPELINE_STAGE(SWITCH, IN, HAIRPIN, 15, "ls_in_hairpin") \ +- PIPELINE_STAGE(SWITCH, IN, ARP_ND_RSP, 16, "ls_in_arp_rsp") \ +- PIPELINE_STAGE(SWITCH, IN, DHCP_OPTIONS, 17, "ls_in_dhcp_options") \ +- PIPELINE_STAGE(SWITCH, IN, DHCP_RESPONSE, 18, "ls_in_dhcp_response") \ +- PIPELINE_STAGE(SWITCH, IN, DNS_LOOKUP, 19, "ls_in_dns_lookup") \ +- PIPELINE_STAGE(SWITCH, IN, DNS_RESPONSE, 20, "ls_in_dns_response") \ +- PIPELINE_STAGE(SWITCH, IN, EXTERNAL_PORT, 21, "ls_in_external_port") \ +- PIPELINE_STAGE(SWITCH, IN, L2_LKUP, 22, "ls_in_l2_lkup") \ +- PIPELINE_STAGE(SWITCH, IN, L2_UNKNOWN, 23, "ls_in_l2_unknown") \ ++ PIPELINE_STAGE(SWITCH, IN, LB, 12, "ls_in_lb") \ ++ PIPELINE_STAGE(SWITCH, IN, ACL_AFTER_LB, 13, "ls_in_acl_after_lb") \ ++ PIPELINE_STAGE(SWITCH, IN, STATEFUL, 14, "ls_in_stateful") \ ++ PIPELINE_STAGE(SWITCH, IN, PRE_HAIRPIN, 15, "ls_in_pre_hairpin") \ ++ PIPELINE_STAGE(SWITCH, IN, NAT_HAIRPIN, 16, "ls_in_nat_hairpin") \ ++ PIPELINE_STAGE(SWITCH, IN, HAIRPIN, 17, "ls_in_hairpin") \ ++ PIPELINE_STAGE(SWITCH, IN, ARP_ND_RSP, 18, "ls_in_arp_rsp") \ ++ PIPELINE_STAGE(SWITCH, IN, DHCP_OPTIONS, 19, "ls_in_dhcp_options") \ ++ PIPELINE_STAGE(SWITCH, IN, DHCP_RESPONSE, 20, "ls_in_dhcp_response") \ ++ PIPELINE_STAGE(SWITCH, IN, DNS_LOOKUP, 21, "ls_in_dns_lookup") \ ++ PIPELINE_STAGE(SWITCH, IN, DNS_RESPONSE, 22, "ls_in_dns_response") \ ++ PIPELINE_STAGE(SWITCH, IN, EXTERNAL_PORT, 23, "ls_in_external_port") \ ++ PIPELINE_STAGE(SWITCH, IN, L2_LKUP, 24, "ls_in_l2_lkup") \ ++ PIPELINE_STAGE(SWITCH, IN, L2_UNKNOWN, 25, "ls_in_l2_unknown") \ + \ + /* Logical switch egress stages. */ \ + PIPELINE_STAGE(SWITCH, OUT, PRE_LB, 0, "ls_out_pre_lb") \ +@@ -761,16 +763,6 @@ init_nat_entries(struct ovn_datapath *od) + return; + } - # expected packet at foo2 +- if (od->n_l3dgw_ports > 1) { +- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); +- VLOG_WARN_RL(&rl, "NAT is configured on logical router %s, which has %" +- PRIuSIZE" distributed gateway ports. NAT is not supported" +- " yet when there is more than one distributed gateway " +- "port on the router.", +- od->nbr->name, od->n_l3dgw_ports); +- return; +- } +- + od->nat_entries = xmalloc(od->nbr->n_nat * sizeof *od->nat_entries); + + for (size_t i = 0; i < od->nbr->n_nat; i++) { +@@ -1641,13 +1633,13 @@ destroy_routable_addresses(struct ovn_port_routable_addresses *ra) + } + + static char **get_nat_addresses(const struct ovn_port *op, size_t *n, +- bool routable_only); ++ bool routable_only, bool include_lb_ips); + + static void + assign_routable_addresses(struct ovn_port *op) + { + size_t n; +- char **nats = get_nat_addresses(op, &n, true); ++ char **nats = get_nat_addresses(op, &n, true, true); + + if (!nats) { + return; +@@ -2711,7 +2703,8 @@ join_logical_ports(struct northd_input *input_data, + * The caller must free each of the n returned strings with free(), + * and must free the returned array when it is no longer needed. */ + static char ** +-get_nat_addresses(const struct ovn_port *op, size_t *n, bool routable_only) ++get_nat_addresses(const struct ovn_port *op, size_t *n, bool routable_only, ++ bool include_lb_ips) + { + size_t n_nats = 0; + struct eth_addr mac; +@@ -2791,24 +2784,26 @@ get_nat_addresses(const struct ovn_port *op, size_t *n, bool routable_only) + } + } + +- const char *ip_address; +- if (routable_only) { +- SSET_FOR_EACH (ip_address, &op->od->lb_ips_v4_routable) { +- ds_put_format(&c_addresses, " %s", ip_address); +- central_ip_address = true; +- } +- SSET_FOR_EACH (ip_address, &op->od->lb_ips_v6_routable) { +- ds_put_format(&c_addresses, " %s", ip_address); +- central_ip_address = true; +- } +- } else { +- SSET_FOR_EACH (ip_address, &op->od->lb_ips_v4) { +- ds_put_format(&c_addresses, " %s", ip_address); +- central_ip_address = true; +- } +- SSET_FOR_EACH (ip_address, &op->od->lb_ips_v6) { +- ds_put_format(&c_addresses, " %s", ip_address); +- central_ip_address = true; ++ if (include_lb_ips) { ++ const char *ip_address; ++ if (routable_only) { ++ SSET_FOR_EACH (ip_address, &op->od->lb_ips_v4_routable) { ++ ds_put_format(&c_addresses, " %s", ip_address); ++ central_ip_address = true; ++ } ++ SSET_FOR_EACH (ip_address, &op->od->lb_ips_v6_routable) { ++ ds_put_format(&c_addresses, " %s", ip_address); ++ central_ip_address = true; ++ } ++ } else { ++ SSET_FOR_EACH (ip_address, &op->od->lb_ips_v4) { ++ ds_put_format(&c_addresses, " %s", ip_address); ++ central_ip_address = true; ++ } ++ SSET_FOR_EACH (ip_address, &op->od->lb_ips_v6) { ++ ds_put_format(&c_addresses, " %s", ip_address); ++ central_ip_address = true; ++ } + } + } + +@@ -3376,7 +3371,10 @@ ovn_port_update_sbrec(struct northd_input *input_data, + if (nat_addresses && !strcmp(nat_addresses, "router")) { + if (op->peer && op->peer->od + && (chassis || op->peer->od->n_l3dgw_ports)) { +- nats = get_nat_addresses(op->peer, &n_nats, false); ++ bool exclude_lb_vips = smap_get_bool(&op->nbsp->options, ++ "exclude-lb-vips-from-garp", false); ++ nats = get_nat_addresses(op->peer, &n_nats, false, ++ !exclude_lb_vips); + } + /* Only accept manual specification of ethernet address + * followed by IPv4 addresses on type "l3gateway" ports. */ +@@ -3803,6 +3801,7 @@ build_ovn_lbs(struct northd_input *input_data, + } + + /* Delete any stale SB load balancer rows. */ ++ struct hmapx existing_lbs = HMAPX_INITIALIZER(&existing_lbs); + const struct sbrec_load_balancer *sbrec_lb, *next; + SBREC_LOAD_BALANCER_TABLE_FOR_EACH_SAFE (sbrec_lb, next, + input_data->sbrec_load_balancer_table) { +@@ -3813,13 +3812,22 @@ build_ovn_lbs(struct northd_input *input_data, + continue; + } + ++ /* Delete any SB load balancer entries that refer to NB load balancers ++ * that don't exist anymore or are not applied to switches anymore. ++ * ++ * There is also a special case in which duplicate LBs might be created ++ * in the SB, e.g., due to the fact that OVSDB only ensures ++ * "at-least-once" consistency for clustered database tables that ++ * are not indexed in any way. ++ */ + lb = ovn_northd_lb_find(lbs, &lb_uuid); +- if (lb && lb->n_nb_ls) { +- lb->slb = sbrec_lb; +- } else { ++ if (!lb || !lb->n_nb_ls || !hmapx_add(&existing_lbs, lb)) { + sbrec_load_balancer_delete(sbrec_lb); ++ } else { ++ lb->slb = sbrec_lb; + } + } ++ hmapx_destroy(&existing_lbs); + + /* Create SB Load balancer records if not present and sync + * the SB load balancer columns. */ +@@ -6122,7 +6130,7 @@ build_reject_acl_rules(struct ovn_datapath *od, struct hmap *lflows, + { + struct ds match = DS_EMPTY_INITIALIZER; + struct ds actions = DS_EMPTY_INITIALIZER; +- bool ingress = (stage == S_SWITCH_IN_ACL); ++ bool ingress = (ovn_stage_get_pipeline(stage) == P_IN); + + char *next_action = + xasprintf("next(pipeline=%s,table=%d);", +@@ -6163,7 +6171,15 @@ consider_acl(struct hmap *lflows, struct ovn_datapath *od, + struct ds *actions) + { + bool ingress = !strcmp(acl->direction, "from-lport") ? true :false; +- enum ovn_stage stage = ingress ? S_SWITCH_IN_ACL : S_SWITCH_OUT_ACL; ++ enum ovn_stage stage; ++ ++ if (ingress && smap_get_bool(&acl->options, "apply-after-lb", false)) { ++ stage = S_SWITCH_IN_ACL_AFTER_LB; ++ } else if (ingress) { ++ stage = S_SWITCH_IN_ACL; ++ } else { ++ stage = S_SWITCH_OUT_ACL; ++ } + + if (!strcmp(acl->action, "allow-stateless")) { + ds_clear(actions); +@@ -6342,6 +6358,72 @@ ovn_port_group_destroy(struct hmap *pgs, struct ovn_port_group *pg) + } + } + ++static void ++copy_ra_to_sb(struct ovn_port *op, const char *address_mode); ++ ++static void ++ovn_update_ipv6_options(struct hmap *ports) ++{ ++ struct ovn_port *op; ++ HMAP_FOR_EACH (op, key_node, ports) { ++ if (!op->nbrp || op->nbrp->peer || !op->peer) { ++ continue; ++ } ++ ++ if (!op->lrp_networks.n_ipv6_addrs) { ++ continue; ++ } ++ ++ struct smap options; ++ smap_clone(&options, &op->sb->options); ++ ++ /* enable IPv6 prefix delegation */ ++ bool prefix_delegation = smap_get_bool(&op->nbrp->options, ++ "prefix_delegation", false); ++ if (!lrport_is_enabled(op->nbrp)) { ++ prefix_delegation = false; ++ } ++ if (smap_get_bool(&options, "ipv6_prefix_delegation", ++ false) != prefix_delegation) { ++ smap_add(&options, "ipv6_prefix_delegation", ++ prefix_delegation ? "true" : "false"); ++ } ++ ++ bool ipv6_prefix = smap_get_bool(&op->nbrp->options, ++ "prefix", false); ++ if (!lrport_is_enabled(op->nbrp)) { ++ ipv6_prefix = false; ++ } ++ if (smap_get_bool(&options, "ipv6_prefix", false) != ipv6_prefix) { ++ smap_add(&options, "ipv6_prefix", ++ ipv6_prefix ? "true" : "false"); ++ } ++ sbrec_port_binding_set_options(op->sb, &options); ++ ++ smap_destroy(&options); ++ ++ const char *address_mode = smap_get( ++ &op->nbrp->ipv6_ra_configs, "address_mode"); ++ ++ if (!address_mode) { ++ continue; ++ } ++ if (strcmp(address_mode, "slaac") && ++ strcmp(address_mode, "dhcpv6_stateful") && ++ strcmp(address_mode, "dhcpv6_stateless")) { ++ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); ++ VLOG_WARN_RL(&rl, "Invalid address mode [%s] defined", ++ address_mode); ++ continue; ++ } ++ ++ if (smap_get_bool(&op->nbrp->ipv6_ra_configs, "send_periodic", ++ false)) { ++ copy_ra_to_sb(op, address_mode); ++ } ++ } ++} ++ + static void + build_port_group_lswitches(struct northd_input *input_data, + struct hmap *pgs, +@@ -6405,6 +6487,8 @@ build_acls(struct ovn_datapath *od, struct hmap *lflows, + ovn_lflow_add(lflows, od, S_SWITCH_OUT_ACL, 0, "1", "next;"); + } + ++ ovn_lflow_add(lflows, od, S_SWITCH_IN_ACL_AFTER_LB, 0, "1", "next;"); ++ + if (has_stateful) { + /* Ingress and Egress ACL Table (Priority 1). + * +@@ -6463,7 +6547,8 @@ build_acls(struct ovn_datapath *od, struct hmap *lflows, + "ct.rpl && ct_label.blocked == 0", + use_ct_inv_match ? " && !ct.inv" : ""); + ovn_lflow_add(lflows, od, S_SWITCH_IN_ACL, UINT16_MAX - 3, +- ds_cstr(&match), "next;"); ++ ds_cstr(&match), REGBIT_ACL_HINT_DROP" = 0; " ++ REGBIT_ACL_HINT_BLOCK" = 0; next;"); + ovn_lflow_add(lflows, od, S_SWITCH_OUT_ACL, UINT16_MAX - 3, + ds_cstr(&match), "next;"); + +@@ -6670,6 +6755,10 @@ build_lb_rules(struct hmap *lflows, struct ovn_northd_lb *lb, + ds_clear(action); + ds_clear(match); + ++ /* Make sure that we clear the REGBIT_CONNTRACK_COMMIT flag. Otherwise ++ * the load balanced packet will be committed again in ++ * S_SWITCH_IN_STATEFUL. */ ++ ds_put_format(action, REGBIT_CONNTRACK_COMMIT" = 0; "); + /* Store the original destination IP to be used when generating + * hairpin flows. + */ +@@ -6716,8 +6805,8 @@ build_lb_rules(struct hmap *lflows, struct ovn_northd_lb *lb, + + struct ovn_lflow *lflow_ref = NULL; + uint32_t hash = ovn_logical_flow_hash( +- ovn_stage_get_table(S_SWITCH_IN_STATEFUL), +- ovn_stage_get_pipeline(S_SWITCH_IN_STATEFUL), priority, ++ ovn_stage_get_table(S_SWITCH_IN_LB), ++ ovn_stage_get_pipeline(S_SWITCH_IN_LB), priority, + ds_cstr(match), ds_cstr(action)); + + for (size_t j = 0; j < lb->n_nb_ls; j++) { +@@ -6730,7 +6819,7 @@ build_lb_rules(struct hmap *lflows, struct ovn_northd_lb *lb, + continue; + } + lflow_ref = ovn_lflow_add_at_with_hash(lflows, od, +- S_SWITCH_IN_STATEFUL, priority, ++ S_SWITCH_IN_LB, priority, + ds_cstr(match), ds_cstr(action), + NULL, meter, &lb->nlb->header_, + OVS_SOURCE_LOCATOR, hash); +@@ -6741,8 +6830,9 @@ build_lb_rules(struct hmap *lflows, struct ovn_northd_lb *lb, + static void + build_stateful(struct ovn_datapath *od, struct hmap *lflows) + { +- /* Ingress and Egress stateful Table (Priority 0): Packets are ++ /* Ingress LB, Ingress and Egress stateful Table (Priority 0): Packets are + * allowed by default. */ ++ ovn_lflow_add(lflows, od, S_SWITCH_IN_LB, 0, "1", "next;"); + ovn_lflow_add(lflows, od, S_SWITCH_IN_STATEFUL, 0, "1", "next;"); + ovn_lflow_add(lflows, od, S_SWITCH_OUT_STATEFUL, 0, "1", "next;"); + +@@ -7006,7 +7096,7 @@ build_lrouter_groups(struct hmap *ports, struct ovs_list *lr_list) + } + + /* +- * Ingress table 22: Flows that flood self originated ARP/ND packets in the ++ * Ingress table 24: Flows that flood self originated ARP/ND packets in the + * switching domain. + */ + static void +@@ -7119,7 +7209,7 @@ lrouter_port_ipv6_reachable(const struct ovn_port *op, + } + + /* +- * Ingress table 22: Flows that forward ARP/ND requests only to the routers ++ * Ingress table 24: Flows that forward ARP/ND requests only to the routers + * that own the addresses. Other ARP/ND packets are still flooded in the + * switching domain as regular broadcast. + */ +@@ -7156,7 +7246,7 @@ build_lswitch_rport_arp_req_flow(const char *ips, + } + + /* +- * Ingress table 22: Flows that forward ARP/ND requests only to the routers ++ * Ingress table 24: Flows that forward ARP/ND requests only to the routers + * that own the addresses. + * Priorities: + * - 80: self originated GARPs that need to follow regular processing. +@@ -7484,7 +7574,7 @@ build_lswitch_flows(const struct hmap *datapaths, + + struct ovn_datapath *od; + +- /* Ingress table 23: Destination lookup for unknown MACs (priority 0). */ ++ /* Ingress table 25: Destination lookup for unknown MACs (priority 0). */ + HMAP_FOR_EACH (od, key_node, datapaths) { + if (!od->nbs) { + continue; +@@ -7553,7 +7643,7 @@ build_lswitch_lflows_admission_control(struct ovn_datapath *od, + } + } + +-/* Ingress table 16: ARP/ND responder, skip requests coming from localnet ++/* Ingress table 18: ARP/ND responder, skip requests coming from localnet + * and vtep ports. (priority 100); see ovn-northd.8.xml for the + * rationale. */ + +@@ -7575,7 +7665,7 @@ build_lswitch_arp_nd_responder_skip_local(struct ovn_port *op, + } + } + +-/* Ingress table 16: ARP/ND responder, reply for known IPs. ++/* Ingress table 18: ARP/ND responder, reply for known IPs. + * (priority 50). */ + static void + build_lswitch_arp_nd_responder_known_ips(struct ovn_port *op, +@@ -7835,7 +7925,7 @@ build_lswitch_arp_nd_responder_known_ips(struct ovn_port *op, + } + } + +-/* Ingress table 16: ARP/ND responder, by default goto next. ++/* Ingress table 18: ARP/ND responder, by default goto next. + * (priority 0)*/ + static void + build_lswitch_arp_nd_responder_default(struct ovn_datapath *od, +@@ -7846,7 +7936,7 @@ build_lswitch_arp_nd_responder_default(struct ovn_datapath *od, + } + } + +-/* Ingress table 16: ARP/ND responder for service monitor source ip. ++/* Ingress table 18: ARP/ND responder for service monitor source ip. + * (priority 110)*/ + static void + build_lswitch_arp_nd_service_monitor(struct ovn_northd_lb *lb, +@@ -7894,7 +7984,7 @@ build_lswitch_arp_nd_service_monitor(struct ovn_northd_lb *lb, + } + + +-/* Logical switch ingress table 14 and 15: DHCP options and response ++/* Logical switch ingress table 19 and 20: DHCP options and response + * priority 100 flows. */ + static void + build_lswitch_dhcp_options_and_response(struct ovn_port *op, +@@ -7946,11 +8036,11 @@ build_lswitch_dhcp_options_and_response(struct ovn_port *op, + } + } + +-/* Ingress table 14 and 15: DHCP options and response, by default goto ++/* Ingress table 19 and 20: DHCP options and response, by default goto + * next. (priority 0). +- * Ingress table 16 and 17: DNS lookup and response, by default goto next. ++ * Ingress table 21 and 22: DNS lookup and response, by default goto next. + * (priority 0). +- * Ingress table 18 - External port handling, by default goto next. ++ * Ingress table 23 - External port handling, by default goto next. + * (priority 0). */ + static void + build_lswitch_dhcp_and_dns_defaults(struct ovn_datapath *od, +@@ -7965,7 +8055,7 @@ build_lswitch_dhcp_and_dns_defaults(struct ovn_datapath *od, + } + } + +-/* Logical switch ingress table 17 and 18: DNS lookup and response ++/* Logical switch ingress table 21 and 22: DNS lookup and response + * priority 100 flows. + */ + static void +@@ -7993,7 +8083,7 @@ build_lswitch_dns_lookup_and_response(struct ovn_datapath *od, + } + } + +-/* Table 18: External port. Drop ARP request for router ips from ++/* Table 23: External port. Drop ARP request for router ips from + * external ports on chassis not binding those ports. + * This makes the router pipeline to be run only on the chassis + * binding the external ports. */ +@@ -8010,7 +8100,7 @@ build_lswitch_external_port(struct ovn_port *op, + } + } + +-/* Ingress table 22: Destination lookup, broadcast and multicast handling ++/* Ingress table 24: Destination lookup, broadcast and multicast handling + * (priority 70 - 100). */ + static void + build_lswitch_destination_lookup_bmcast(struct ovn_datapath *od, +@@ -8102,7 +8192,7 @@ build_lswitch_destination_lookup_bmcast(struct ovn_datapath *od, + } + + +-/* Ingress table 22: Add IP multicast flows learnt from IGMP/MLD ++/* Ingress table 24: Add IP multicast flows learnt from IGMP/MLD + * (priority 90). */ + static void + build_lswitch_ip_mcast_igmp_mld(struct ovn_igmp_group *igmp_group, +@@ -8180,7 +8270,7 @@ build_lswitch_ip_mcast_igmp_mld(struct ovn_igmp_group *igmp_group, + + static struct ovs_mutex mcgroup_mutex = OVS_MUTEX_INITIALIZER; + +-/* Ingress table 22: Destination lookup, unicast handling (priority 50), */ ++/* Ingress table 24: Destination lookup, unicast handling (priority 50), */ + static void + build_lswitch_ip_unicast_lookup(struct ovn_port *op, + struct hmap *lflows, +@@ -10675,6 +10765,12 @@ build_neigh_learning_flows_for_lrouter( + copp_meter_get(COPP_ARP, od->nbr->copp, + meter_groups)); + ++ ovn_lflow_metered(lflows, od, S_ROUTER_IN_LEARN_NEIGHBOR, 95, ++ "nd_na && nd.tll == 0", ++ "put_nd(inport, nd.target, eth.src); next;", ++ copp_meter_get(COPP_ND_NA, od->nbr->copp, ++ meter_groups)); ++ + ovn_lflow_metered(lflows, od, S_ROUTER_IN_LEARN_NEIGHBOR, 90, + "nd_na", "put_nd(inport, nd.target, nd.tll); next;", + copp_meter_get(COPP_ND_NA, od->nbr->copp, +@@ -10767,34 +10863,6 @@ build_ND_RA_flows_for_lrouter_port( + return; + } + +- struct smap options; +- smap_clone(&options, &op->sb->options); +- +- /* enable IPv6 prefix delegation */ +- bool prefix_delegation = smap_get_bool(&op->nbrp->options, +- "prefix_delegation", false); +- if (!lrport_is_enabled(op->nbrp)) { +- prefix_delegation = false; +- } +- if (smap_get_bool(&options, "ipv6_prefix_delegation", +- false) != prefix_delegation) { +- smap_add(&options, "ipv6_prefix_delegation", +- prefix_delegation ? "true" : "false"); +- } +- +- bool ipv6_prefix = smap_get_bool(&op->nbrp->options, +- "prefix", false); +- if (!lrport_is_enabled(op->nbrp)) { +- ipv6_prefix = false; +- } +- if (smap_get_bool(&options, "ipv6_prefix", false) != ipv6_prefix) { +- smap_add(&options, "ipv6_prefix", +- ipv6_prefix ? "true" : "false"); +- } +- sbrec_port_binding_set_options(op->sb, &options); +- +- smap_destroy(&options); +- + const char *address_mode = smap_get( + &op->nbrp->ipv6_ra_configs, "address_mode"); + +@@ -10810,11 +10878,6 @@ build_ND_RA_flows_for_lrouter_port( + return; + } + +- if (smap_get_bool(&op->nbrp->ipv6_ra_configs, "send_periodic", +- false)) { +- copy_ra_to_sb(op, address_mode); +- } +- + ds_clear(match); + ds_put_format(match, "inport == %s && ip6.dst == ff02::2 && nd_rs", + op->json_key); +@@ -10839,6 +10902,22 @@ build_ND_RA_flows_for_lrouter_port( + ds_put_format(actions, ", router_preference = \"%s\"", prf); + } + ++ const char *ra_rdnss = smap_get(&op->nbrp->ipv6_ra_configs, "rdnss"); ++ if (ra_rdnss) { ++ ds_put_format(actions, ", rdnss = %s", ra_rdnss); ++ } ++ ++ const char *ra_dnssl = smap_get(&op->nbrp->ipv6_ra_configs, "dnssl"); ++ if (ra_dnssl) { ++ ds_put_format(actions, ", dnssl = \"%s\"", ra_dnssl); ++ } ++ ++ const char *route_info = smap_get(&op->nbrp->ipv6_ra_configs, ++ "route_info"); ++ if (route_info) { ++ ds_put_format(actions, ", route_info = \"%s\"", route_info); ++ } ++ + bool add_rs_response_flow = false; + + for (size_t i = 0; i < op->lrp_networks.n_ipv6_addrs; i++) { +@@ -13055,6 +13134,18 @@ build_lrouter_nat_defrag_and_lb(struct ovn_datapath *od, struct hmap *lflows, + return; + } + ++ /* NAT rules are not currently supported on logical routers with multiple ++ * distributed gateway ports. */ ++ if (od->n_l3dgw_ports > 1) { ++ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); ++ VLOG_WARN_RL(&rl, "NAT is configured on logical router %s, which has %" ++ PRIuSIZE" distributed gateway ports. NAT is not supported" ++ " yet when there is more than one distributed gateway " ++ "port on the router.", ++ od->nbr->name, od->n_l3dgw_ports); ++ return; ++ } ++ + struct sset nat_entries = SSET_INITIALIZER(&nat_entries); + + bool dnat_force_snat_ip = +@@ -14948,6 +15039,7 @@ ovnnb_db_run(struct northd_input *input_data, + build_meter_groups(input_data, &data->meter_groups); + stopwatch_stop(BUILD_LFLOWS_CTX_STOPWATCH_NAME, time_msec()); + stopwatch_start(CLEAR_LFLOWS_CTX_STOPWATCH_NAME, time_msec()); ++ ovn_update_ipv6_options(&data->ports); + ovn_update_ipv6_prefix(&data->ports); + + sync_address_sets(input_data, ovnsb_txn, &data->datapaths); +diff --git a/northd/ovn-northd.8.xml b/northd/ovn-northd.8.xml +index 79f35bc16..ad79a32d2 100644 +--- a/northd/ovn-northd.8.xml ++++ b/northd/ovn-northd.8.xml +@@ -663,15 +663,16 @@ + + + +-

Ingress table 9: from-lport ACLs

++

Ingress table 9: from-lport ACLs before LB

+ +

+ Logical flows in this table closely reproduce those in the + ACL table in the OVN_Northbound database +- for the from-lport direction. The priority +- values from the ACL table have a limited range and have +- 1000 added to them to leave room for OVN default flows at both +- higher and lower priorities. ++ for the from-lport direction without the option ++ apply-after-lb set or set to false. ++ The priority values from the ACL table have a ++ limited range and have 1000 added to them to leave room for OVN default ++ flows at both higher and lower priorities. +

+
    +
  • +@@ -746,7 +747,9 @@ + go through the flows that implement the currently defined + policy based on ACLs. If a connection is no longer allowed by + policy, ct_label.blocked will get set and packets in the +- reply direction will no longer be allowed, either. ++ reply direction will no longer be allowed, either. This flow also ++ clears the register bits reg0[9] and ++ reg0[10]. +
  • + +
  • +@@ -838,7 +841,7 @@ +
  • +
+ +-

Ingress Table 12: Stateful

++

Ingress Table 12: LB

+ +
    +
  • +@@ -889,7 +892,73 @@ + Please note using --reject option will disable + empty_lb SB controller event for this load balancer. +
  • ++
++ ++

Ingress table 13: from-lport ACLs after LB

++ ++

++ Logical flows in this table closely reproduce those in the ++ ACL table in the OVN_Northbound database ++ for the from-lport direction with the option ++ apply-after-lb set to true. ++ The priority values from the ACL table have a ++ limited range and have 1000 added to them to leave room for OVN default ++ flows at both higher and lower priorities. ++

+ ++
    ++
  • ++ allow apply-after-lb ACLs translate into logical flows ++ with the next; action. If there are any stateful ACLs ++ (including both before-lb and after-lb ACLs) ++ on this datapath, then allow ACLs translate to ++ ct_commit; next; (which acts as a hint for the next tables ++ to commit the connection to conntrack). In case the ACL ++ has a label then reg3 is loaded with the label value and ++ reg0[13] bit is set to 1 (which acts as a hint for the ++ next tables to commit the label to conntrack). ++
  • ++
  • ++ allow-related apply-after-lb ACLs translate into logical ++ flows with the ct_commit(ct_label=0/1); next; actions ++ for new connections and reg0[1] = 1; next; for existing ++ connections. In case the ACL has a label then ++ reg3 is loaded with the label value and ++ reg0[13] bit is set to 1 (which acts as a hint for the ++ next tables to commit the label to conntrack). ++
  • ++
  • ++ allow-stateless apply-after-lb ACLs translate into logical ++ flows with the next; action. ++
  • ++
  • ++ reject apply-after-lb ACLs translate into logical ++ flows with the ++ tcp_reset { output <-> inport; ++ next(pipeline=egress,table=5);} ++ action for TCP connections,icmp4/icmp6 action ++ for UDP connections, and sctp_abort {output <-%gt; inport; ++ next(pipeline=egress,table=5);} action for SCTP associations. ++
  • ++
  • ++ Other apply-after-lb ACLs translate to drop; for new ++ or untracked connections and ct_commit(ct_label=1/1); for ++ known connections. Setting ct_label marks a connection ++ as one that was previously allowed, but should no longer be ++ allowed due to a policy change. ++
  • ++
++ ++
    ++
  • ++ One priority-0 fallback flow that matches all packets and advances to ++ the next table. ++
  • ++
++ ++

Ingress Table 14: Stateful

++ ++
    +
  • + A priority 100 flow is added which commits the packet to the conntrack + and sets the most significant 32-bits of ct_label with the +@@ -910,7 +979,7 @@ +
  • +
+ +-

Ingress Table 13: Pre-Hairpin

++

Ingress Table 15: Pre-Hairpin

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

Ingress Table 14: Nat-Hairpin

++

Ingress Table 16: Nat-Hairpin

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

Ingress Table 15: Hairpin

++

Ingress Table 17: Hairpin

+
    +
  • + A priority-1 flow that hairpins traffic matched by non-default +@@ -976,7 +1045,7 @@ +
  • +
+ +-

Ingress Table 16: ARP/ND responder

++

Ingress Table 18: ARP/ND responder

+ +

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

Ingress Table 17: DHCP option processing

++

Ingress Table 19: DHCP option processing

+ +

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

Ingress Table 18: DHCP responses

++

Ingress Table 20: DHCP responses

+ +

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

Ingress Table 19 DNS Lookup

++

Ingress Table 21 DNS Lookup

+ +

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

Ingress Table 20 DNS Responses

++

Ingress Table 22 DNS Responses

+ +

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

Ingress table 21 External ports

++

Ingress table 23 External ports

+ +

+ Traffic from the external logical ports enter the ingress +@@ -1527,7 +1596,7 @@ output; + + + +-

Ingress Table 22 Destination Lookup

++

Ingress Table 24 Destination Lookup

+ +

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

Ingress Table 24 Destination unknown

++

Ingress Table 25 Destination unknown

+ +

+ This table handles the packets whose destination was not found or +@@ -2178,6 +2247,12 @@ next; + put_arp(inport, arp.spa, arp.sha); next; + + ++

  • ++ A priority-95 flow with the match nd_na && ++ nd.tll == 0 and applies the action ++ put_nd(inport, nd.target, eth.src); next; ++
  • ++ +
  • + A priority-90 flow with the match nd_na and + applies the action +diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c +index 2b58bfcec..c56b29f2a 100644 +--- a/northd/ovn-northd.c ++++ b/northd/ovn-northd.c +@@ -513,14 +513,6 @@ update_sequence_numbers(int64_t loop_start_time, + } + + static void +-add_column_noalert(struct ovsdb_idl *idl, +- const struct ovsdb_idl_column *column) +-{ +- ovsdb_idl_add_column(idl, column); +- ovsdb_idl_omit_alert(idl, column); +-} +- +-static void + usage(void) + { + printf("\ +@@ -730,255 +722,10 @@ main(int argc, char *argv[]) + unixctl_command_register("nb-connection-status", "", 0, 0, + ovn_conn_show, ovnnb_idl_loop.idl); + +- /* We want to detect only selected changes to the ovn-sb db. */ ++ /* We want to detect all changes to the ovn-sb db. */ + struct ovsdb_idl_loop ovnsb_idl_loop = OVSDB_IDL_LOOP_INITIALIZER( + ovsdb_idl_create(ovnsb_db, &sbrec_idl_class, true, true)); +- ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_sb_global); +- add_column_noalert(ovnsb_idl_loop.idl, &sbrec_sb_global_col_nb_cfg); +- add_column_noalert(ovnsb_idl_loop.idl, &sbrec_sb_global_col_options); +- add_column_noalert(ovnsb_idl_loop.idl, &sbrec_sb_global_col_ipsec); +- ovsdb_idl_track_add_column(ovnsb_idl_loop.idl, +- &sbrec_sb_global_col_connections); +- +- ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_logical_flow); +- add_column_noalert(ovnsb_idl_loop.idl, +- &sbrec_logical_flow_col_logical_datapath); +- add_column_noalert(ovnsb_idl_loop.idl, +- &sbrec_logical_flow_col_logical_dp_group); +- add_column_noalert(ovnsb_idl_loop.idl, &sbrec_logical_flow_col_pipeline); +- add_column_noalert(ovnsb_idl_loop.idl, &sbrec_logical_flow_col_table_id); +- add_column_noalert(ovnsb_idl_loop.idl, &sbrec_logical_flow_col_priority); +- add_column_noalert(ovnsb_idl_loop.idl, &sbrec_logical_flow_col_match); +- add_column_noalert(ovnsb_idl_loop.idl, &sbrec_logical_flow_col_actions); +- add_column_noalert(ovnsb_idl_loop.idl, +- &sbrec_logical_flow_col_controller_meter); +- ovsdb_idl_add_column(ovnsb_idl_loop.idl, +- &sbrec_logical_flow_col_external_ids); +- +- ovsdb_idl_add_table(ovnsb_idl_loop.idl, +- &sbrec_table_logical_dp_group); +- add_column_noalert(ovnsb_idl_loop.idl, +- &sbrec_logical_dp_group_col_datapaths); +- +- ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_multicast_group); +- add_column_noalert(ovnsb_idl_loop.idl, +- &sbrec_multicast_group_col_datapath); +- add_column_noalert(ovnsb_idl_loop.idl, +- &sbrec_multicast_group_col_tunnel_key); +- add_column_noalert(ovnsb_idl_loop.idl, &sbrec_multicast_group_col_name); +- add_column_noalert(ovnsb_idl_loop.idl, &sbrec_multicast_group_col_ports); +- +- ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_datapath_binding); +- add_column_noalert(ovnsb_idl_loop.idl, +- &sbrec_datapath_binding_col_tunnel_key); +- add_column_noalert(ovnsb_idl_loop.idl, +- &sbrec_datapath_binding_col_load_balancers); +- ovsdb_idl_track_add_column(ovnsb_idl_loop.idl, +- &sbrec_datapath_binding_col_external_ids); +- +- ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_port_binding); +- add_column_noalert(ovnsb_idl_loop.idl, &sbrec_port_binding_col_datapath); +- add_column_noalert(ovnsb_idl_loop.idl, +- &sbrec_port_binding_col_logical_port); +- add_column_noalert(ovnsb_idl_loop.idl, +- &sbrec_port_binding_col_tunnel_key); +- add_column_noalert(ovnsb_idl_loop.idl, +- &sbrec_port_binding_col_parent_port); +- add_column_noalert(ovnsb_idl_loop.idl, &sbrec_port_binding_col_tag); +- add_column_noalert(ovnsb_idl_loop.idl, &sbrec_port_binding_col_type); +- add_column_noalert(ovnsb_idl_loop.idl, &sbrec_port_binding_col_options); +- add_column_noalert(ovnsb_idl_loop.idl, &sbrec_port_binding_col_mac); +- add_column_noalert(ovnsb_idl_loop.idl, +- &sbrec_port_binding_col_nat_addresses); +- ovsdb_idl_track_add_column(ovnsb_idl_loop.idl, +- &sbrec_port_binding_col_requested_chassis); +- ovsdb_idl_track_add_column(ovnsb_idl_loop.idl, +- &sbrec_port_binding_col_chassis); +- ovsdb_idl_track_add_column(ovnsb_idl_loop.idl, +- &sbrec_port_binding_col_gateway_chassis); +- ovsdb_idl_track_add_column(ovnsb_idl_loop.idl, +- &sbrec_port_binding_col_ha_chassis_group); +- ovsdb_idl_track_add_column(ovnsb_idl_loop.idl, +- &sbrec_port_binding_col_virtual_parent); +- ovsdb_idl_track_add_column(ovnsb_idl_loop.idl, +- &sbrec_port_binding_col_up); +- ovsdb_idl_track_add_column(ovnsb_idl_loop.idl, +- &sbrec_gateway_chassis_col_chassis); +- ovsdb_idl_track_add_column(ovnsb_idl_loop.idl, +- &sbrec_gateway_chassis_col_name); +- ovsdb_idl_track_add_column(ovnsb_idl_loop.idl, +- &sbrec_gateway_chassis_col_priority); +- ovsdb_idl_track_add_column(ovnsb_idl_loop.idl, +- &sbrec_gateway_chassis_col_external_ids); +- ovsdb_idl_track_add_column(ovnsb_idl_loop.idl, +- &sbrec_gateway_chassis_col_options); +- add_column_noalert(ovnsb_idl_loop.idl, +- &sbrec_port_binding_col_external_ids); +- ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_mac_binding); +- add_column_noalert(ovnsb_idl_loop.idl, &sbrec_mac_binding_col_datapath); +- add_column_noalert(ovnsb_idl_loop.idl, &sbrec_mac_binding_col_ip); +- add_column_noalert(ovnsb_idl_loop.idl, &sbrec_mac_binding_col_mac); +- add_column_noalert(ovnsb_idl_loop.idl, +- &sbrec_mac_binding_col_logical_port); +- ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_dhcp_options); +- add_column_noalert(ovnsb_idl_loop.idl, &sbrec_dhcp_options_col_code); +- add_column_noalert(ovnsb_idl_loop.idl, &sbrec_dhcp_options_col_type); +- add_column_noalert(ovnsb_idl_loop.idl, &sbrec_dhcp_options_col_name); +- ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_dhcpv6_options); +- add_column_noalert(ovnsb_idl_loop.idl, &sbrec_dhcpv6_options_col_code); +- add_column_noalert(ovnsb_idl_loop.idl, &sbrec_dhcpv6_options_col_type); +- add_column_noalert(ovnsb_idl_loop.idl, &sbrec_dhcpv6_options_col_name); +- ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_address_set); +- add_column_noalert(ovnsb_idl_loop.idl, &sbrec_address_set_col_name); +- add_column_noalert(ovnsb_idl_loop.idl, &sbrec_address_set_col_addresses); +- ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_port_group); +- add_column_noalert(ovnsb_idl_loop.idl, &sbrec_port_group_col_name); +- add_column_noalert(ovnsb_idl_loop.idl, &sbrec_port_group_col_ports); +- +- ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_dns); +- add_column_noalert(ovnsb_idl_loop.idl, &sbrec_dns_col_datapaths); +- add_column_noalert(ovnsb_idl_loop.idl, &sbrec_dns_col_records); +- add_column_noalert(ovnsb_idl_loop.idl, &sbrec_dns_col_external_ids); +- +- ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_rbac_role); +- add_column_noalert(ovnsb_idl_loop.idl, &sbrec_rbac_role_col_name); +- add_column_noalert(ovnsb_idl_loop.idl, &sbrec_rbac_role_col_permissions); +- +- ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_rbac_permission); +- add_column_noalert(ovnsb_idl_loop.idl, +- &sbrec_rbac_permission_col_table); +- add_column_noalert(ovnsb_idl_loop.idl, +- &sbrec_rbac_permission_col_authorization); +- add_column_noalert(ovnsb_idl_loop.idl, +- &sbrec_rbac_permission_col_insert_delete); +- add_column_noalert(ovnsb_idl_loop.idl, &sbrec_rbac_permission_col_update); +- +- ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_meter); +- ovsdb_idl_track_add_column(ovnsb_idl_loop.idl, &sbrec_meter_col_name); +- ovsdb_idl_track_add_column(ovnsb_idl_loop.idl, &sbrec_meter_col_unit); +- ovsdb_idl_track_add_column(ovnsb_idl_loop.idl, &sbrec_meter_col_bands); +- +- ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_meter_band); +- ovsdb_idl_track_add_column(ovnsb_idl_loop.idl, +- &sbrec_meter_band_col_action); +- ovsdb_idl_track_add_column(ovnsb_idl_loop.idl, &sbrec_meter_band_col_rate); +- ovsdb_idl_track_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_track_add_column(ovnsb_idl_loop.idl, &sbrec_chassis_col_name); +- ovsdb_idl_track_add_column(ovnsb_idl_loop.idl, +- &sbrec_chassis_col_hostname); +- ovsdb_idl_track_add_column(ovnsb_idl_loop.idl, +- &sbrec_chassis_col_other_config); +- ovsdb_idl_track_add_column(ovnsb_idl_loop.idl, &sbrec_chassis_col_encaps); +- +- ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_encap); +- ovsdb_idl_track_add_column(ovnsb_idl_loop.idl, &sbrec_encap_col_type); +- +- ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_chassis_private); +- ovsdb_idl_track_add_column(ovnsb_idl_loop.idl, +- &sbrec_chassis_private_col_name); +- ovsdb_idl_track_add_column(ovnsb_idl_loop.idl, +- &sbrec_chassis_private_col_chassis); +- ovsdb_idl_track_add_column(ovnsb_idl_loop.idl, +- &sbrec_chassis_private_col_nb_cfg); +- ovsdb_idl_track_add_column(ovnsb_idl_loop.idl, +- &sbrec_chassis_private_col_nb_cfg_timestamp); +- +- ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_ha_chassis); +- add_column_noalert(ovnsb_idl_loop.idl, +- &sbrec_ha_chassis_col_chassis); +- add_column_noalert(ovnsb_idl_loop.idl, +- &sbrec_ha_chassis_col_priority); +- add_column_noalert(ovnsb_idl_loop.idl, +- &sbrec_ha_chassis_col_external_ids); +- +- ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_ha_chassis_group); +- add_column_noalert(ovnsb_idl_loop.idl, +- &sbrec_ha_chassis_group_col_name); +- add_column_noalert(ovnsb_idl_loop.idl, +- &sbrec_ha_chassis_group_col_ha_chassis); +- add_column_noalert(ovnsb_idl_loop.idl, +- &sbrec_ha_chassis_group_col_external_ids); +- add_column_noalert(ovnsb_idl_loop.idl, +- &sbrec_ha_chassis_group_col_ref_chassis); +- +- ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_igmp_group); +- ovsdb_idl_track_add_column(ovnsb_idl_loop.idl, +- &sbrec_igmp_group_col_address); +- ovsdb_idl_track_add_column(ovnsb_idl_loop.idl, +- &sbrec_igmp_group_col_datapath); +- ovsdb_idl_track_add_column(ovnsb_idl_loop.idl, +- &sbrec_igmp_group_col_chassis); +- ovsdb_idl_track_add_column(ovnsb_idl_loop.idl, +- &sbrec_igmp_group_col_ports); +- +- ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_ip_multicast); +- add_column_noalert(ovnsb_idl_loop.idl, +- &sbrec_ip_multicast_col_datapath); +- add_column_noalert(ovnsb_idl_loop.idl, +- &sbrec_ip_multicast_col_enabled); +- add_column_noalert(ovnsb_idl_loop.idl, +- &sbrec_ip_multicast_col_querier); +- add_column_noalert(ovnsb_idl_loop.idl, +- &sbrec_ip_multicast_col_eth_src); +- add_column_noalert(ovnsb_idl_loop.idl, +- &sbrec_ip_multicast_col_ip4_src); +- add_column_noalert(ovnsb_idl_loop.idl, +- &sbrec_ip_multicast_col_ip6_src); +- add_column_noalert(ovnsb_idl_loop.idl, +- &sbrec_ip_multicast_col_table_size); +- add_column_noalert(ovnsb_idl_loop.idl, +- &sbrec_ip_multicast_col_idle_timeout); +- add_column_noalert(ovnsb_idl_loop.idl, +- &sbrec_ip_multicast_col_query_interval); +- add_column_noalert(ovnsb_idl_loop.idl, +- &sbrec_ip_multicast_col_query_max_resp); +- ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_service_monitor); +- add_column_noalert(ovnsb_idl_loop.idl, +- &sbrec_service_monitor_col_ip); +- add_column_noalert(ovnsb_idl_loop.idl, +- &sbrec_service_monitor_col_logical_port); +- add_column_noalert(ovnsb_idl_loop.idl, +- &sbrec_service_monitor_col_port); +- add_column_noalert(ovnsb_idl_loop.idl, +- &sbrec_service_monitor_col_options); +- ovsdb_idl_track_add_column(ovnsb_idl_loop.idl, +- &sbrec_service_monitor_col_status); +- add_column_noalert(ovnsb_idl_loop.idl, +- &sbrec_service_monitor_col_protocol); +- add_column_noalert(ovnsb_idl_loop.idl, +- &sbrec_service_monitor_col_src_mac); +- add_column_noalert(ovnsb_idl_loop.idl, +- &sbrec_service_monitor_col_src_ip); +- add_column_noalert(ovnsb_idl_loop.idl, +- &sbrec_service_monitor_col_external_ids); +- +- ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_load_balancer); +- add_column_noalert(ovnsb_idl_loop.idl, &sbrec_load_balancer_col_datapaths); +- add_column_noalert(ovnsb_idl_loop.idl, &sbrec_load_balancer_col_name); +- add_column_noalert(ovnsb_idl_loop.idl, &sbrec_load_balancer_col_vips); +- add_column_noalert(ovnsb_idl_loop.idl, &sbrec_load_balancer_col_protocol); +- add_column_noalert(ovnsb_idl_loop.idl, &sbrec_load_balancer_col_options); +- add_column_noalert(ovnsb_idl_loop.idl, +- &sbrec_load_balancer_col_external_ids); +- +- ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_bfd); +- ovsdb_idl_track_add_column(ovnsb_idl_loop.idl, +- &sbrec_bfd_col_logical_port); +- ovsdb_idl_track_add_column(ovnsb_idl_loop.idl, &sbrec_bfd_col_dst_ip); +- ovsdb_idl_track_add_column(ovnsb_idl_loop.idl, &sbrec_bfd_col_status); +- ovsdb_idl_track_add_column(ovnsb_idl_loop.idl, &sbrec_bfd_col_min_tx); +- ovsdb_idl_track_add_column(ovnsb_idl_loop.idl, &sbrec_bfd_col_min_rx); +- ovsdb_idl_track_add_column(ovnsb_idl_loop.idl, &sbrec_bfd_col_detect_mult); +- ovsdb_idl_track_add_column(ovnsb_idl_loop.idl, &sbrec_bfd_col_disc); +- ovsdb_idl_track_add_column(ovnsb_idl_loop.idl, &sbrec_bfd_col_src_port); +- +- ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_fdb); +- ovsdb_idl_track_add_column(ovnsb_idl_loop.idl, &sbrec_fdb_col_mac); +- ovsdb_idl_track_add_column(ovnsb_idl_loop.idl, &sbrec_fdb_col_dp_key); +- ovsdb_idl_track_add_column(ovnsb_idl_loop.idl, &sbrec_fdb_col_port_key); ++ ovsdb_idl_track_add_all(ovnsb_idl_loop.idl); + + unixctl_command_register("sb-connection-status", "", 0, 0, + ovn_conn_show, ovnsb_idl_loop.idl); +@@ -1069,7 +816,7 @@ main(int argc, char *argv[]) + } + + if (ovsdb_idl_has_lock(ovnsb_idl_loop.idl)) { +- int64_t loop_start_time = time_msec(); ++ int64_t loop_start_time = time_wall_msec(); + inc_proc_northd_run(ovnnb_txn, ovnsb_txn, recompute); + recompute = false; + if (ovnsb_txn) { +@@ -1088,6 +835,27 @@ main(int argc, char *argv[]) + ovnnb_txn, ovnsb_txn, + &ovnsb_idl_loop); + } ++ ++ /* If there are any errors, we force a full recompute in order ++ * to ensure we handle all changes. */ ++ if (!ovsdb_idl_loop_commit_and_wait(&ovnnb_idl_loop)) { ++ VLOG_INFO("OVNNB commit failed, " ++ "force recompute next time."); ++ recompute = true; ++ } ++ ++ if (!ovsdb_idl_loop_commit_and_wait(&ovnsb_idl_loop)) { ++ VLOG_INFO("OVNSB commit failed, " ++ "force recompute next time."); ++ recompute = true; ++ } ++ } else { ++ /* Make sure we send any pending requests, e.g., lock. */ ++ ovsdb_idl_loop_commit_and_wait(&ovnnb_idl_loop); ++ ovsdb_idl_loop_commit_and_wait(&ovnsb_idl_loop); ++ ++ /* Force a full recompute next time we become active. */ ++ recompute = true; + } + } else { + /* ovn-northd is paused +@@ -1109,17 +877,8 @@ main(int argc, char *argv[]) + ovsdb_idl_run(ovnsb_idl_loop.idl); + ovsdb_idl_wait(ovnnb_idl_loop.idl); + ovsdb_idl_wait(ovnsb_idl_loop.idl); +- } +- +- /* If there are any errors, we force a full recompute in order to +- ensure we handle all changes. */ +- if (!ovsdb_idl_loop_commit_and_wait(&ovnnb_idl_loop)) { +- VLOG_INFO("OVNNB commit failed, force recompute next time."); +- recompute = true; +- } + +- if (!ovsdb_idl_loop_commit_and_wait(&ovnsb_idl_loop)) { +- VLOG_INFO("OVNSB commit failed, force recompute next time."); ++ /* Force a full recompute next time we become active. */ + recompute = true; + } + +diff --git a/ovn-nb.ovsschema b/ovn-nb.ovsschema +index 55977339a..9dd31197e 100644 +--- a/ovn-nb.ovsschema ++++ b/ovn-nb.ovsschema +@@ -1,7 +1,7 @@ + { + "name": "OVN_Northbound", +- "version": "5.34.1", +- "cksum": "2177334725 30782", ++ "version": "5.35.1", ++ "cksum": "649108105 30999", + "tables": { + "NB_Global": { + "columns": { +@@ -262,6 +262,11 @@ + "label": {"type": {"key": {"type": "integer", + "minInteger": 0, + "maxInteger": 4294967295}}}, ++ "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 6a6972856..c705b89ea 100644 +--- a/ovn-nb.xml ++++ b/ovn-nb.xml +@@ -925,6 +925,15 @@ + + + ++ ++ If is set to ++ router, Gratuitous ARPs will be sent for all ++ SNAT and DNAT external IP addresses defined on the ++ 's logical router, ++ using the 's MAC address, ++ not cosidering configured load balancers. ++ ++ + + Optional. A list of IPv4 addresses that this + logical switch router port will reply to ARP requests. +@@ -2014,6 +2023,35 @@ + + + ++ ++

    ++ ACLs options. ++

    ++ ++

    ++ If set to true, the ACL will be applied after load balancing ++ stage. Supported only for from-lport direction. ++

    ++ ++

    ++ The main use case of this option is to support ACLs matching on ++ the destination IP address of the packet for the backend IPs ++ of load balancers. ++

    ++ ++

    ++ OVN will apply the from-lport ACLs in two ++ stages. ACLs without this option apply-after-lb ++ set, will be applied before the load balancer stage and ACLs ++ with this option set will be applied after the load balancer ++ stage. The priorities are indepedent between these stages and ++ may not be obvious to the CMS. Hence CMS should be extra careful ++ when using this option and should carefully evaluate the priorities ++ of all the ACLs and the default deny/allow ACLs if any. ++

    ++
    ++
    ++ + +

    + These columns control whether and how OVN logs packets that match an +diff --git a/rhel/ovn-fedora.spec.in b/rhel/ovn-fedora.spec.in +index 3fb854a37..821eb03cc 100644 +--- a/rhel/ovn-fedora.spec.in ++++ b/rhel/ovn-fedora.spec.in +@@ -323,7 +323,7 @@ ln -sf ovn_detrace.py %{_bindir}/ovn-detrace + %if %{with libcapng} + if [ $1 -eq 1 ]; then + sed -i 's:^#OVN_USER_ID=:OVN_USER_ID=:' %{_sysconfdir}/sysconfig/ovn +- sed -i 's:\(.*su\).*:\1 ovn ovn:' %{_sysconfdir}/logrotate.d/ovn ++ sed -i 's:\(.*su\).*:\1 openvswitch openvswitch:' %{_sysconfdir}/logrotate.d/ovn + fi + %endif + +diff --git a/tests/atlocal.in b/tests/atlocal.in +index 310fd46a5..eb80cbd1b 100644 +--- a/tests/atlocal.in ++++ b/tests/atlocal.in +@@ -181,8 +181,8 @@ else + DIFF_SUPPORTS_NORMAL_FORMAT=no + fi + +-# Set HAVE_DIBBLER-SERVER +-find_command dibbler-server ++# Set HAVE_DHCPD ++find_command dhcpd + + # Set HAVE_BFDD_BEACON + find_command bfdd-beacon +diff --git a/tests/ovn-controller-vtep.at b/tests/ovn-controller-vtep.at +index b76fa5cef..08e1d13e7 100644 +--- a/tests/ovn-controller-vtep.at ++++ b/tests/ovn-controller-vtep.at +@@ -102,6 +102,14 @@ Chassis br-vtep + options: {csum="false"} + ]) + ++# checks is-vtep option is in place, remove, check again ++AT_CHECK([ovn-sbctl get chassis br-vtep other_config:is-vtep], [0], [dnl ++"true" ++]) ++ ++check ovn-sbctl remove chassis br-vtep other_config is-vtep ++OVS_WAIT_UNTIL([ovn-sbctl get chassis br-vtep other_config:is-vtep]) ++ + # deletes the chassis via ovn-sbctl and check that it is readded back + # with the log. + AT_CHECK([ovn-sbctl chassis-del br-vtep]) +diff --git a/tests/ovn-lflow-conj-ids.at b/tests/ovn-lflow-conj-ids.at +index b5537c370..f819bf158 100644 +--- a/tests/ovn-lflow-conj-ids.at ++++ b/tests/ovn-lflow-conj-ids.at +@@ -15,9 +15,9 @@ alloc(aaaaaaaa-1111-1111-1111-111111111111, 10): 0xaaaaaaaa + alloc(bbbbbbbb-1111-1111-1111-111111111111, 10): 0xbbbbbbbb + alloc(cccccccc-1111-1111-1111-111111111111, 10): 0xcccccccc + Conjunction IDs allocations: +-lflow: cccccccc-1111-1111-1111-111111111111, start: 3435973836, n: 10 +-lflow: aaaaaaaa-1111-1111-1111-111111111111, start: 2863311530, n: 10 +-lflow: bbbbbbbb-1111-1111-1111-111111111111, start: 3149642683, n: 10 ++lflow: cccccccc-1111-1111-1111-111111111111, dp: 00000000-0000-0000-0000-000000000000, start: 3435973836, n: 10 ++lflow: aaaaaaaa-1111-1111-1111-111111111111, dp: 00000000-0000-0000-0000-000000000000, start: 2863311530, n: 10 ++lflow: bbbbbbbb-1111-1111-1111-111111111111, dp: 00000000-0000-0000-0000-000000000000, start: 3149642683, n: 10 + --- + Total 30 IDs used. + ]) +@@ -35,8 +35,8 @@ AT_CHECK( + alloc(aaaaaaaa-1111-1111-1111-111111111111, 1): 0xaaaaaaaa + alloc(aaaaaaaa-2222-1111-1111-111111111111, 1): 0xaaaaaaab + Conjunction IDs allocations: +-lflow: aaaaaaaa-1111-1111-1111-111111111111, start: 2863311530, n: 1 +-lflow: aaaaaaaa-2222-1111-1111-111111111111, start: 2863311531, n: 1 (*) ++lflow: aaaaaaaa-1111-1111-1111-111111111111, dp: 00000000-0000-0000-0000-000000000000, start: 2863311530, n: 1 ++lflow: aaaaaaaa-2222-1111-1111-111111111111, dp: 00000000-0000-0000-0000-000000000000, start: 2863311531, n: 1 (*) + --- + Total 2 IDs used. + ]) +@@ -56,8 +56,8 @@ alloc(aaaaaaab-1111-1111-1111-111111111111, 1): 0xaaaaaaba + free(aaaaaaaa-1111-1111-1111-111111111111) + alloc(aaaaaaab-2222-1111-1111-111111111111, 1): 0xaaaaaaab + Conjunction IDs allocations: +-lflow: aaaaaaab-2222-1111-1111-111111111111, start: 2863311531, n: 1 +-lflow: aaaaaaab-1111-1111-1111-111111111111, start: 2863311546, n: 1 (*) ++lflow: aaaaaaab-2222-1111-1111-111111111111, dp: 00000000-0000-0000-0000-000000000000, start: 2863311531, n: 1 ++lflow: aaaaaaab-1111-1111-1111-111111111111, dp: 00000000-0000-0000-0000-000000000000, start: 2863311546, n: 1 (*) + --- + Total 2 IDs used. + ]) +@@ -71,8 +71,8 @@ AT_CHECK( + alloc(aaaaaaaa-1111-1111-1111-111111111111, 1): 0xaaaaaaaa + alloc(aaaaaaa0-1111-1111-1111-111111111111, 11): 0xaaaaaaab + Conjunction IDs allocations: +-lflow: aaaaaaa0-1111-1111-1111-111111111111, start: 2863311531, n: 11 (*) +-lflow: aaaaaaaa-1111-1111-1111-111111111111, start: 2863311530, n: 1 ++lflow: aaaaaaa0-1111-1111-1111-111111111111, dp: 00000000-0000-0000-0000-000000000000, start: 2863311531, n: 11 (*) ++lflow: aaaaaaaa-1111-1111-1111-111111111111, dp: 00000000-0000-0000-0000-000000000000, start: 2863311530, n: 1 + --- + Total 12 IDs used. + ]) +@@ -89,8 +89,8 @@ alloc(aaaaaaaa-1111-1111-1111-111111111111, 16): 0xaaaaaaaa + alloc(aaaaaaaa-1111-1111-1111-111111111111, 1): 0xaaaaaaaa + alloc(aaaaaaab-1111-1111-1111-111111111111, 1): 0xaaaaaaab + Conjunction IDs allocations: +-lflow: aaaaaaaa-1111-1111-1111-111111111111, start: 2863311530, n: 1 +-lflow: aaaaaaab-1111-1111-1111-111111111111, start: 2863311531, n: 1 ++lflow: aaaaaaaa-1111-1111-1111-111111111111, dp: 00000000-0000-0000-0000-000000000000, start: 2863311530, n: 1 ++lflow: aaaaaaab-1111-1111-1111-111111111111, dp: 00000000-0000-0000-0000-000000000000, start: 2863311531, n: 1 + --- + Total 2 IDs used. + ]) +@@ -109,7 +109,7 @@ alloc(ffffffff-1111-1111-1111-111111111111, 2): 0x1 + free(ffffffff-1111-1111-1111-111111111111) + alloc(00000000-2222-1111-1111-111111111111, 1): 0x1 + Conjunction IDs allocations: +-lflow: 00000000-2222-1111-1111-111111111111, start: 1, n: 1 (*) ++lflow: 00000000-2222-1111-1111-111111111111, dp: 00000000-0000-0000-0000-000000000000, start: 1, n: 1 (*) + --- + Total 1 IDs used. + ]) +@@ -130,7 +130,7 @@ alloc_specified(0000000a-1111-1111-1111-111111111111, 0xa, 1): false + free(00000001-1111-1111-1111-111111111111) + alloc_specified(0000000a-1111-1111-1111-111111111111, 0xa, 1): true + Conjunction IDs allocations: +-lflow: 0000000a-1111-1111-1111-111111111111, start: 10, n: 1 ++lflow: 0000000a-1111-1111-1111-111111111111, dp: 00000000-0000-0000-0000-000000000000, start: 10, n: 1 + --- + Total 1 IDs used. + ]) +diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at +index 652903761..25bd8a217 100644 +--- a/tests/ovn-northd.at ++++ b/tests/ovn-northd.at +@@ -1214,7 +1214,7 @@ check ovn-nbctl --wait=sb ls-lb-add sw0 lb1 + AT_CAPTURE_FILE([sbflows]) + OVS_WAIT_FOR_OUTPUT( + [ovn-sbctl dump-flows sw0 | tee sbflows | grep 'priority=120.*backends' | sed 's/table=..//'], 0, [dnl +- (ls_in_stateful ), priority=120 , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(reg1 = 10.0.0.10; reg2[[0..15]] = 80; ct_lb(backends=10.0.0.3:80,20.0.0.3:80);) ++ (ls_in_lb ), priority=120 , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(reg0[[1]] = 0; reg1 = 10.0.0.10; reg2[[0..15]] = 80; ct_lb(backends=10.0.0.3:80,20.0.0.3:80);) + ]) + + AS_BOX([Delete the Load_Balancer_Health_Check]) +@@ -1224,7 +1224,7 @@ wait_row_count Service_Monitor 0 + AT_CAPTURE_FILE([sbflows2]) + OVS_WAIT_FOR_OUTPUT( + [ovn-sbctl dump-flows sw0 | tee sbflows2 | grep 'priority=120.*backends' | sed 's/table=..//'], [0], +-[ (ls_in_stateful ), priority=120 , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(reg1 = 10.0.0.10; reg2[[0..15]] = 80; ct_lb(backends=10.0.0.3:80,20.0.0.3:80);) ++[ (ls_in_lb ), priority=120 , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(reg0[[1]] = 0; reg1 = 10.0.0.10; reg2[[0..15]] = 80; ct_lb(backends=10.0.0.3:80,20.0.0.3:80);) + ]) + + AS_BOX([Create the Load_Balancer_Health_Check again.]) +@@ -1236,7 +1236,7 @@ check ovn-nbctl --wait=sb sync + + ovn-sbctl dump-flows sw0 | grep backends | grep priority=120 > lflows.txt + AT_CHECK([cat lflows.txt | sed 's/table=..//'], [0], [dnl +- (ls_in_stateful ), priority=120 , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(reg1 = 10.0.0.10; reg2[[0..15]] = 80; ct_lb(backends=10.0.0.3:80,20.0.0.3:80);) ++ (ls_in_lb ), priority=120 , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(reg0[[1]] = 0; reg1 = 10.0.0.10; reg2[[0..15]] = 80; ct_lb(backends=10.0.0.3:80,20.0.0.3:80);) + ]) + + AS_BOX([Get the uuid of both the service_monitor]) +@@ -1246,7 +1246,7 @@ sm_sw1_p1=$(fetch_column Service_Monitor _uuid logical_port=sw1-p1) + AT_CAPTURE_FILE([sbflows3]) + OVS_WAIT_FOR_OUTPUT( + [ovn-sbctl dump-flows sw0 | tee sbflows 3 | grep 'priority=120.*backends' | sed 's/table=..//'], [0], +-[ (ls_in_stateful ), priority=120 , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(reg1 = 10.0.0.10; reg2[[0..15]] = 80; ct_lb(backends=10.0.0.3:80,20.0.0.3:80);) ++[ (ls_in_lb ), priority=120 , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(reg0[[1]] = 0; reg1 = 10.0.0.10; reg2[[0..15]] = 80; ct_lb(backends=10.0.0.3:80,20.0.0.3:80);) + ]) + + AS_BOX([Set the service monitor for sw1-p1 to offline]) +@@ -1257,7 +1257,7 @@ check ovn-nbctl --wait=sb sync + AT_CAPTURE_FILE([sbflows4]) + OVS_WAIT_FOR_OUTPUT( + [ovn-sbctl dump-flows sw0 | tee sbflows4 | grep 'priority=120.*backends' | sed 's/table=..//'], [0], +-[ (ls_in_stateful ), priority=120 , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(reg1 = 10.0.0.10; reg2[[0..15]] = 80; ct_lb(backends=10.0.0.3:80);) ++[ (ls_in_lb ), priority=120 , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(reg0[[1]] = 0; reg1 = 10.0.0.10; reg2[[0..15]] = 80; ct_lb(backends=10.0.0.3:80);) + ]) + + AS_BOX([Set the service monitor for sw0-p1 to offline]) +@@ -1273,7 +1273,7 @@ OVS_WAIT_FOR_OUTPUT( + AT_CAPTURE_FILE([sbflows6]) + OVS_WAIT_FOR_OUTPUT( + [ovn-sbctl dump-flows sw0 | tee sbflows6 | grep "ip4.dst == 10.0.0.10 && tcp.dst == 80" | grep priority=120 | sed 's/table=..//'], [0], [dnl +- (ls_in_stateful ), priority=120 , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(drop;) ++ (ls_in_lb ), priority=120 , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(drop;) + ]) + + AS_BOX([Set the service monitor for sw0-p1 and sw1-p1 to online]) +@@ -1286,7 +1286,7 @@ check ovn-nbctl --wait=sb sync + AT_CAPTURE_FILE([sbflows7]) + OVS_WAIT_FOR_OUTPUT( + [ovn-sbctl dump-flows sw0 | tee sbflows7 | grep backends | grep priority=120 | sed 's/table=..//'], 0, +-[ (ls_in_stateful ), priority=120 , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(reg1 = 10.0.0.10; reg2[[0..15]] = 80; ct_lb(backends=10.0.0.3:80,20.0.0.3:80);) ++[ (ls_in_lb ), priority=120 , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(reg0[[1]] = 0; reg1 = 10.0.0.10; reg2[[0..15]] = 80; ct_lb(backends=10.0.0.3:80,20.0.0.3:80);) + ]) + + AS_BOX([Set the service monitor for sw1-p1 to error]) +@@ -1297,7 +1297,7 @@ check ovn-nbctl --wait=sb sync + ovn-sbctl dump-flows sw0 | grep "ip4.dst == 10.0.0.10 && tcp.dst == 80" \ + | grep priority=120 > lflows.txt + AT_CHECK([cat lflows.txt | sed 's/table=..//'], [0], [dnl +- (ls_in_stateful ), priority=120 , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(reg1 = 10.0.0.10; reg2[[0..15]] = 80; ct_lb(backends=10.0.0.3:80);) ++ (ls_in_lb ), priority=120 , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(reg0[[1]] = 0; reg1 = 10.0.0.10; reg2[[0..15]] = 80; ct_lb(backends=10.0.0.3:80);) + ]) + + AS_BOX([Add one more vip to lb1]) +@@ -1323,8 +1323,8 @@ AT_CAPTURE_FILE([sbflows9]) + OVS_WAIT_FOR_OUTPUT( + [ovn-sbctl dump-flows sw0 | tee sbflows9 | grep backends | grep priority=120 | sed 's/table=..//' | sort], + 0, +-[ (ls_in_stateful ), priority=120 , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(reg1 = 10.0.0.10; reg2[[0..15]] = 80; ct_lb(backends=10.0.0.3:80);) +- (ls_in_stateful ), priority=120 , match=(ct.new && ip4.dst == 10.0.0.40 && tcp.dst == 1000), action=(reg1 = 10.0.0.40; reg2[[0..15]] = 1000; ct_lb(backends=10.0.0.3:1000);) ++[ (ls_in_lb ), priority=120 , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(reg0[[1]] = 0; reg1 = 10.0.0.10; reg2[[0..15]] = 80; ct_lb(backends=10.0.0.3:80);) ++ (ls_in_lb ), priority=120 , match=(ct.new && ip4.dst == 10.0.0.40 && tcp.dst == 1000), action=(reg0[[1]] = 0; reg1 = 10.0.0.40; reg2[[0..15]] = 1000; ct_lb(backends=10.0.0.3:1000);) + ]) + + AS_BOX([Set the service monitor for sw1-p1 to online]) +@@ -1337,8 +1337,8 @@ AT_CAPTURE_FILE([sbflows10]) + OVS_WAIT_FOR_OUTPUT( + [ovn-sbctl dump-flows sw0 | tee sbflows10 | grep backends | grep priority=120 | sed 's/table=..//' | sort], + 0, +-[ (ls_in_stateful ), priority=120 , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(reg1 = 10.0.0.10; reg2[[0..15]] = 80; ct_lb(backends=10.0.0.3:80,20.0.0.3:80);) +- (ls_in_stateful ), priority=120 , match=(ct.new && ip4.dst == 10.0.0.40 && tcp.dst == 1000), action=(reg1 = 10.0.0.40; reg2[[0..15]] = 1000; ct_lb(backends=10.0.0.3:1000,20.0.0.3:80);) ++[ (ls_in_lb ), priority=120 , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(reg0[[1]] = 0; reg1 = 10.0.0.10; reg2[[0..15]] = 80; ct_lb(backends=10.0.0.3:80,20.0.0.3:80);) ++ (ls_in_lb ), priority=120 , match=(ct.new && ip4.dst == 10.0.0.40 && tcp.dst == 1000), action=(reg0[[1]] = 0; reg1 = 10.0.0.40; reg2[[0..15]] = 1000; ct_lb(backends=10.0.0.3:1000,20.0.0.3:80);) + ]) + + AS_BOX([Associate lb1 to sw1]) +@@ -1347,8 +1347,8 @@ AT_CAPTURE_FILE([sbflows11]) + OVS_WAIT_FOR_OUTPUT( + [ovn-sbctl dump-flows sw1 | tee sbflows11 | grep backends | grep priority=120 | sed 's/table=..//' | sort], + 0, [dnl +- (ls_in_stateful ), priority=120 , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(reg1 = 10.0.0.10; reg2[[0..15]] = 80; ct_lb(backends=10.0.0.3:80,20.0.0.3:80);) +- (ls_in_stateful ), priority=120 , match=(ct.new && ip4.dst == 10.0.0.40 && tcp.dst == 1000), action=(reg1 = 10.0.0.40; reg2[[0..15]] = 1000; ct_lb(backends=10.0.0.3:1000,20.0.0.3:80);) ++ (ls_in_lb ), priority=120 , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(reg0[[1]] = 0; reg1 = 10.0.0.10; reg2[[0..15]] = 80; ct_lb(backends=10.0.0.3:80,20.0.0.3:80);) ++ (ls_in_lb ), priority=120 , match=(ct.new && ip4.dst == 10.0.0.40 && tcp.dst == 1000), action=(reg0[[1]] = 0; reg1 = 10.0.0.40; reg2[[0..15]] = 1000; ct_lb(backends=10.0.0.3:1000,20.0.0.3:80);) + ]) + + AS_BOX([Now create lb2 same as lb1 but udp protocol.]) +@@ -1405,7 +1405,7 @@ ovn-sbctl set service_monitor $sm_sw1_p1 status=offline + AT_CAPTURE_FILE([sbflows12]) + OVS_WAIT_FOR_OUTPUT( + [ovn-sbctl dump-flows sw0 | tee sbflows12 | grep "ip4.dst == 10.0.0.10 && tcp.dst == 80" | grep priority=120 | sed 's/table=..//'], [0], [dnl +- (ls_in_stateful ), priority=120 , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(reg0 = 0; reject { outport <-> inport; next(pipeline=egress,table=5);};) ++ (ls_in_lb ), priority=120 , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(reg0 = 0; reject { outport <-> inport; next(pipeline=egress,table=5);};) + ]) + + AT_CLEANUP +@@ -2025,9 +2025,9 @@ AT_CAPTURE_FILE([sw1flows]) + + AT_CHECK( + [grep -E 'ls_(in|out)_acl' sw0flows sw1flows | grep pg0 | sort], [0], [dnl +-sw0flows: table=4 (ls_out_acl ), priority=2003 , match=(outport == @pg0 && ip6 && udp), action=(reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=22); };) ++sw0flows: table=4 (ls_out_acl ), priority=2003 , match=(outport == @pg0 && ip6 && udp), action=(reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=24); };) + sw0flows: table=9 (ls_in_acl ), priority=2002 , match=(inport == @pg0 && ip4 && tcp && tcp.dst == 80), action=(reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=egress,table=5); };) +-sw1flows: table=4 (ls_out_acl ), priority=2003 , match=(outport == @pg0 && ip6 && udp), action=(reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=22); };) ++sw1flows: table=4 (ls_out_acl ), priority=2003 , match=(outport == @pg0 && ip6 && udp), action=(reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=24); };) + sw1flows: table=9 (ls_in_acl ), priority=2002 , match=(inport == @pg0 && ip4 && tcp && tcp.dst == 80), action=(reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=egress,table=5); };) + ]) + +@@ -2041,10 +2041,10 @@ ovn-sbctl dump-flows sw1 > sw1flows2 + AT_CAPTURE_FILE([sw1flows2]) + + AT_CHECK([grep "ls_out_acl" sw0flows2 sw1flows2 | grep pg0 | sort], [0], [dnl +-sw0flows2: table=4 (ls_out_acl ), priority=2002 , match=(outport == @pg0 && ip4 && udp), action=(reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=22); };) +-sw0flows2: table=4 (ls_out_acl ), priority=2003 , match=(outport == @pg0 && ip6 && udp), action=(reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=22); };) +-sw1flows2: table=4 (ls_out_acl ), priority=2002 , match=(outport == @pg0 && ip4 && udp), action=(reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=22); };) +-sw1flows2: table=4 (ls_out_acl ), priority=2003 , match=(outport == @pg0 && ip6 && udp), action=(reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=22); };) ++sw0flows2: table=4 (ls_out_acl ), priority=2002 , match=(outport == @pg0 && ip4 && udp), action=(reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=24); };) ++sw0flows2: table=4 (ls_out_acl ), priority=2003 , match=(outport == @pg0 && ip6 && udp), action=(reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=24); };) ++sw1flows2: table=4 (ls_out_acl ), priority=2002 , match=(outport == @pg0 && ip4 && udp), action=(reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=24); };) ++sw1flows2: table=4 (ls_out_acl ), priority=2003 , match=(outport == @pg0 && ip6 && udp), action=(reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=24); };) + ]) + + AS_BOX([3]) +@@ -2059,16 +2059,16 @@ AT_CAPTURE_FILE([sw1flows3]) + AT_CHECK([grep "ls_out_acl" sw0flows3 sw1flows3 | grep pg0 | sort], [0], [dnl + sw0flows3: table=4 (ls_out_acl ), priority=2001 , match=(reg0[[7]] == 1 && (outport == @pg0 && ip)), action=(reg0[[1]] = 1; next;) + sw0flows3: table=4 (ls_out_acl ), priority=2001 , match=(reg0[[8]] == 1 && (outport == @pg0 && ip)), action=(next;) +-sw0flows3: table=4 (ls_out_acl ), priority=2002 , match=((reg0[[10]] == 1) && outport == @pg0 && ip4 && udp), action=(ct_commit { ct_label.blocked = 1; }; reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=22); };) +-sw0flows3: table=4 (ls_out_acl ), priority=2002 , match=((reg0[[9]] == 1) && outport == @pg0 && ip4 && udp), action=(reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=22); };) +-sw0flows3: table=4 (ls_out_acl ), priority=2003 , match=((reg0[[10]] == 1) && outport == @pg0 && ip6 && udp), action=(ct_commit { ct_label.blocked = 1; }; reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=22); };) +-sw0flows3: table=4 (ls_out_acl ), priority=2003 , match=((reg0[[9]] == 1) && outport == @pg0 && ip6 && udp), action=(reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=22); };) ++sw0flows3: table=4 (ls_out_acl ), priority=2002 , match=((reg0[[10]] == 1) && outport == @pg0 && ip4 && udp), action=(ct_commit { ct_label.blocked = 1; }; reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=24); };) ++sw0flows3: table=4 (ls_out_acl ), priority=2002 , match=((reg0[[9]] == 1) && outport == @pg0 && ip4 && udp), action=(reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=24); };) ++sw0flows3: table=4 (ls_out_acl ), priority=2003 , match=((reg0[[10]] == 1) && outport == @pg0 && ip6 && udp), action=(ct_commit { ct_label.blocked = 1; }; reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=24); };) ++sw0flows3: table=4 (ls_out_acl ), priority=2003 , match=((reg0[[9]] == 1) && outport == @pg0 && ip6 && udp), action=(reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=24); };) + sw1flows3: table=4 (ls_out_acl ), priority=2001 , match=(reg0[[7]] == 1 && (outport == @pg0 && ip)), action=(reg0[[1]] = 1; next;) + sw1flows3: table=4 (ls_out_acl ), priority=2001 , match=(reg0[[8]] == 1 && (outport == @pg0 && ip)), action=(next;) +-sw1flows3: table=4 (ls_out_acl ), priority=2002 , match=((reg0[[10]] == 1) && outport == @pg0 && ip4 && udp), action=(ct_commit { ct_label.blocked = 1; }; reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=22); };) +-sw1flows3: table=4 (ls_out_acl ), priority=2002 , match=((reg0[[9]] == 1) && outport == @pg0 && ip4 && udp), action=(reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=22); };) +-sw1flows3: table=4 (ls_out_acl ), priority=2003 , match=((reg0[[10]] == 1) && outport == @pg0 && ip6 && udp), action=(ct_commit { ct_label.blocked = 1; }; reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=22); };) +-sw1flows3: table=4 (ls_out_acl ), priority=2003 , match=((reg0[[9]] == 1) && outport == @pg0 && ip6 && udp), action=(reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=22); };) ++sw1flows3: table=4 (ls_out_acl ), priority=2002 , match=((reg0[[10]] == 1) && outport == @pg0 && ip4 && udp), action=(ct_commit { ct_label.blocked = 1; }; reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=24); };) ++sw1flows3: table=4 (ls_out_acl ), priority=2002 , match=((reg0[[9]] == 1) && outport == @pg0 && ip4 && udp), action=(reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=24); };) ++sw1flows3: table=4 (ls_out_acl ), priority=2003 , match=((reg0[[10]] == 1) && outport == @pg0 && ip6 && udp), action=(ct_commit { ct_label.blocked = 1; }; reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=24); };) ++sw1flows3: table=4 (ls_out_acl ), priority=2003 , match=((reg0[[9]] == 1) && outport == @pg0 && ip6 && udp), action=(reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=24); };) + ]) + AT_CLEANUP + ]) +@@ -2228,7 +2228,7 @@ AT_CHECK([ovn-sbctl lflow-list ls | grep -e ls_in_acl_hint -e ls_out_acl_hint -e + table=8 (ls_in_acl_hint ), priority=7 , match=(ct.new && !ct.est), action=(reg0[[7]] = 1; reg0[[9]] = 1; next;) + table=9 (ls_in_acl ), priority=1 , match=(ip && (!ct.est || (ct.est && ct_label.blocked == 1))), action=(reg0[[1]] = 1; next;) + table=9 (ls_in_acl ), priority=65532, match=(!ct.est && ct.rel && !ct.new && !ct.inv && ct_label.blocked == 0), action=(next;) +- table=9 (ls_in_acl ), priority=65532, match=(ct.est && !ct.rel && !ct.new && !ct.inv && ct.rpl && ct_label.blocked == 0), action=(next;) ++ table=9 (ls_in_acl ), priority=65532, match=(ct.est && !ct.rel && !ct.new && !ct.inv && ct.rpl && ct_label.blocked == 0), action=(reg0[[9]] = 0; reg0[[10]] = 0; next;) + table=9 (ls_in_acl ), priority=65532, match=(ct.inv || (ct.est && ct.rpl && ct_label.blocked == 1)), action=(drop;) + ]) + +@@ -2240,6 +2240,7 @@ check ovn-nbctl --wait=sb \ + -- ls-lb-add ls lb + + AT_CHECK([ovn-sbctl lflow-list ls | grep -e ls_in_acl_hint -e ls_out_acl_hint -e ls_in_acl -e ls_out_acl | sort], [0], [dnl ++ table=13(ls_in_acl_after_lb ), priority=0 , match=(1), action=(next;) + table=3 (ls_out_acl_hint ), priority=0 , match=(1), action=(next;) + table=3 (ls_out_acl_hint ), priority=1 , match=(ct.est && ct_label.blocked == 0), action=(reg0[[10]] = 1; next;) + table=3 (ls_out_acl_hint ), priority=2 , match=(ct.est && ct_label.blocked == 1), action=(reg0[[9]] = 1; next;) +@@ -2271,7 +2272,7 @@ AT_CHECK([ovn-sbctl lflow-list ls | grep -e ls_in_acl_hint -e ls_out_acl_hint -e + table=9 (ls_in_acl ), priority=1001 , match=(reg0[[8]] == 1 && (ip)), action=(next;) + table=9 (ls_in_acl ), priority=34000, match=(eth.dst == $svc_monitor_mac), action=(next;) + table=9 (ls_in_acl ), priority=65532, match=(!ct.est && ct.rel && !ct.new && !ct.inv && ct_label.blocked == 0), action=(next;) +- table=9 (ls_in_acl ), priority=65532, match=(ct.est && !ct.rel && !ct.new && !ct.inv && ct.rpl && ct_label.blocked == 0), action=(next;) ++ table=9 (ls_in_acl ), priority=65532, match=(ct.est && !ct.rel && !ct.new && !ct.inv && ct.rpl && ct_label.blocked == 0), action=(reg0[[9]] = 0; reg0[[10]] = 0; next;) + table=9 (ls_in_acl ), priority=65532, match=(ct.inv || (ct.est && ct.rpl && ct_label.blocked == 1)), action=(drop;) + table=9 (ls_in_acl ), priority=65532, match=(nd || nd_ra || nd_rs || mldv1 || mldv2), action=(next;) + ]) +@@ -2280,6 +2281,7 @@ ovn-nbctl --wait=sb clear logical_switch ls acls + ovn-nbctl --wait=sb clear logical_switch ls load_balancer + + AT_CHECK([ovn-sbctl lflow-list ls | grep -e ls_in_acl_hint -e ls_out_acl_hint -e ls_in_acl -e ls_out_acl | sort], [0], [dnl ++ table=13(ls_in_acl_after_lb ), priority=0 , match=(1), action=(next;) + table=3 (ls_out_acl_hint ), priority=65535, match=(1), action=(next;) + table=4 (ls_out_acl ), priority=65535, match=(1), action=(next;) + table=8 (ls_in_acl_hint ), priority=65535, match=(1), action=(next;) +@@ -2566,56 +2568,56 @@ check ovn-nbctl \ + -- ls-lb-add sw0 lb0 + check ovn-nbctl --wait=sb sync + +-AT_CHECK([ovn-sbctl lflow-list sw0 | grep ls_in_pre_hairpin | sort], [0], [dnl +- table=13(ls_in_pre_hairpin ), priority=0 , match=(1), action=(next;) +- table=13(ls_in_pre_hairpin ), priority=100 , match=(ip && ct.trk), action=(reg0[[6]] = chk_lb_hairpin(); reg0[[12]] = chk_lb_hairpin_reply(); next;) ++AT_CHECK([ovn-sbctl lflow-list sw0 | grep ls_in_pre_hairpin | sort | sed 's/table=../table=??/'], [0], [dnl ++ table=??(ls_in_pre_hairpin ), priority=0 , match=(1), action=(next;) ++ table=??(ls_in_pre_hairpin ), priority=100 , match=(ip && ct.trk), action=(reg0[[6]] = chk_lb_hairpin(); reg0[[12]] = chk_lb_hairpin_reply(); next;) + ]) + +-AT_CHECK([ovn-sbctl lflow-list sw0 | grep ls_in_nat_hairpin | sort], [0], [dnl +- table=14(ls_in_nat_hairpin ), priority=0 , match=(1), action=(next;) +- table=14(ls_in_nat_hairpin ), priority=100 , match=(ip && ct.est && ct.trk && reg0[[6]] == 1), action=(ct_snat;) +- table=14(ls_in_nat_hairpin ), priority=100 , match=(ip && ct.new && ct.trk && reg0[[6]] == 1), action=(ct_snat_to_vip; next;) +- table=14(ls_in_nat_hairpin ), priority=90 , match=(ip && reg0[[12]] == 1), action=(ct_snat;) ++AT_CHECK([ovn-sbctl lflow-list sw0 | grep ls_in_nat_hairpin | sort | sed 's/table=../table=??/'], [0], [dnl ++ table=??(ls_in_nat_hairpin ), priority=0 , match=(1), action=(next;) ++ table=??(ls_in_nat_hairpin ), priority=100 , match=(ip && ct.est && ct.trk && reg0[[6]] == 1), action=(ct_snat;) ++ table=??(ls_in_nat_hairpin ), priority=100 , match=(ip && ct.new && ct.trk && reg0[[6]] == 1), action=(ct_snat_to_vip; next;) ++ table=??(ls_in_nat_hairpin ), priority=90 , match=(ip && reg0[[12]] == 1), action=(ct_snat;) + ]) + +-AT_CHECK([ovn-sbctl lflow-list sw0 | grep ls_in_hairpin | sort], [0], [dnl +- table=15(ls_in_hairpin ), priority=0 , match=(1), action=(next;) +- table=15(ls_in_hairpin ), priority=1 , match=((reg0[[6]] == 1 || reg0[[12]] == 1)), action=(eth.dst <-> eth.src; outport = inport; flags.loopback = 1; output;) ++AT_CHECK([ovn-sbctl lflow-list sw0 | grep ls_in_hairpin | sort | sed 's/table=../table=??/'], [0], [dnl ++ table=??(ls_in_hairpin ), priority=0 , match=(1), action=(next;) ++ table=??(ls_in_hairpin ), priority=1 , match=((reg0[[6]] == 1 || reg0[[12]] == 1)), action=(eth.dst <-> eth.src; outport = inport; flags.loopback = 1; output;) + ]) + + check ovn-nbctl -- ls-lb-del sw0 lb0 + check ovn-nbctl --wait=sb sync + +-AT_CHECK([ovn-sbctl lflow-list sw0 | grep ls_in_pre_hairpin | sort], [0], [dnl +- table=13(ls_in_pre_hairpin ), priority=0 , match=(1), action=(next;) ++AT_CHECK([ovn-sbctl lflow-list sw0 | grep ls_in_pre_hairpin | sort | sed 's/table=../table=??/'], [0], [dnl ++ table=??(ls_in_pre_hairpin ), priority=0 , match=(1), action=(next;) + ]) + +-AT_CHECK([ovn-sbctl lflow-list sw0 | grep ls_in_nat_hairpin | sort], [0], [dnl +- table=14(ls_in_nat_hairpin ), priority=0 , match=(1), action=(next;) ++AT_CHECK([ovn-sbctl lflow-list sw0 | grep ls_in_nat_hairpin | sort | sed 's/table=../table=??/'], [0], [dnl ++ table=??(ls_in_nat_hairpin ), priority=0 , match=(1), action=(next;) + ]) + +-AT_CHECK([ovn-sbctl lflow-list sw0 | grep ls_in_hairpin | sort], [0], [dnl +- table=15(ls_in_hairpin ), priority=0 , match=(1), action=(next;) ++AT_CHECK([ovn-sbctl lflow-list sw0 | grep ls_in_hairpin | sort | sed 's/table=../table=??/'], [0], [dnl ++ table=??(ls_in_hairpin ), priority=0 , match=(1), action=(next;) + ]) + + check ovn-nbctl -- add load_balancer_group $lbg load_balancer $lb0 + check ovn-nbctl --wait=sb sync + +-AT_CHECK([ovn-sbctl lflow-list sw0 | grep ls_in_pre_hairpin | sort], [0], [dnl +- table=13(ls_in_pre_hairpin ), priority=0 , match=(1), action=(next;) +- table=13(ls_in_pre_hairpin ), priority=100 , match=(ip && ct.trk), action=(reg0[[6]] = chk_lb_hairpin(); reg0[[12]] = chk_lb_hairpin_reply(); next;) ++AT_CHECK([ovn-sbctl lflow-list sw0 | grep ls_in_pre_hairpin | sort | sed 's/table=../table=??/'], [0], [dnl ++ table=??(ls_in_pre_hairpin ), priority=0 , match=(1), action=(next;) ++ table=??(ls_in_pre_hairpin ), priority=100 , match=(ip && ct.trk), action=(reg0[[6]] = chk_lb_hairpin(); reg0[[12]] = chk_lb_hairpin_reply(); next;) + ]) + +-AT_CHECK([ovn-sbctl lflow-list sw0 | grep ls_in_nat_hairpin | sort], [0], [dnl +- table=14(ls_in_nat_hairpin ), priority=0 , match=(1), action=(next;) +- table=14(ls_in_nat_hairpin ), priority=100 , match=(ip && ct.est && ct.trk && reg0[[6]] == 1), action=(ct_snat;) +- table=14(ls_in_nat_hairpin ), priority=100 , match=(ip && ct.new && ct.trk && reg0[[6]] == 1), action=(ct_snat_to_vip; next;) +- table=14(ls_in_nat_hairpin ), priority=90 , match=(ip && reg0[[12]] == 1), action=(ct_snat;) ++AT_CHECK([ovn-sbctl lflow-list sw0 | grep ls_in_nat_hairpin | sort | sed 's/table=../table=??/'], [0], [dnl ++ table=??(ls_in_nat_hairpin ), priority=0 , match=(1), action=(next;) ++ table=??(ls_in_nat_hairpin ), priority=100 , match=(ip && ct.est && ct.trk && reg0[[6]] == 1), action=(ct_snat;) ++ table=??(ls_in_nat_hairpin ), priority=100 , match=(ip && ct.new && ct.trk && reg0[[6]] == 1), action=(ct_snat_to_vip; next;) ++ table=??(ls_in_nat_hairpin ), priority=90 , match=(ip && reg0[[12]] == 1), action=(ct_snat;) + ]) + +-AT_CHECK([ovn-sbctl lflow-list sw0 | grep ls_in_hairpin | sort], [0], [dnl +- table=15(ls_in_hairpin ), priority=0 , match=(1), action=(next;) +- table=15(ls_in_hairpin ), priority=1 , match=((reg0[[6]] == 1 || reg0[[12]] == 1)), action=(eth.dst <-> eth.src; outport = inport; flags.loopback = 1; output;) ++AT_CHECK([ovn-sbctl lflow-list sw0 | grep ls_in_hairpin | sort | sed 's/table=../table=??/'], [0], [dnl ++ table=??(ls_in_hairpin ), priority=0 , match=(1), action=(next;) ++ table=??(ls_in_hairpin ), priority=1 , match=((reg0[[6]] == 1 || reg0[[12]] == 1)), action=(eth.dst <-> eth.src; outport = inport; flags.loopback = 1; output;) + ]) + + AT_CLEANUP +@@ -3889,12 +3891,16 @@ check_stateful_flows() { + table=7 (ls_in_pre_stateful ), priority=120 , match=(reg0[[2]] == 1 && ip6 && udp), action=(xxreg1 = ip6.dst; reg2[[0..15]] = udp.dst; ct_lb;) + ]) + ++ AT_CHECK([grep "ls_in_lb" sw0flows | sort], [0], [dnl ++ table=12(ls_in_lb ), priority=0 , match=(1), action=(next;) ++ table=12(ls_in_lb ), priority=120 , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(reg0[[1]] = 0; reg1 = 10.0.0.10; reg2[[0..15]] = 80; ct_lb(backends=10.0.0.4:8080);) ++ table=12(ls_in_lb ), priority=120 , match=(ct.new && ip4.dst == 10.0.0.20 && tcp.dst == 80), action=(reg0[[1]] = 0; reg1 = 10.0.0.20; reg2[[0..15]] = 80; ct_lb(backends=10.0.0.40:8080);) ++]) ++ + AT_CHECK([grep "ls_in_stateful" sw0flows | sort], [0], [dnl +- table=12(ls_in_stateful ), priority=0 , match=(1), action=(next;) +- table=12(ls_in_stateful ), priority=100 , match=(reg0[[1]] == 1 && reg0[[13]] == 0), action=(ct_commit { ct_label.blocked = 0; }; next;) +- table=12(ls_in_stateful ), priority=100 , match=(reg0[[1]] == 1 && reg0[[13]] == 1), action=(ct_commit { ct_label.blocked = 0; ct_label.label = reg3; }; next;) +- table=12(ls_in_stateful ), priority=120 , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(reg1 = 10.0.0.10; reg2[[0..15]] = 80; ct_lb(backends=10.0.0.4:8080);) +- table=12(ls_in_stateful ), priority=120 , match=(ct.new && ip4.dst == 10.0.0.20 && tcp.dst == 80), action=(reg1 = 10.0.0.20; reg2[[0..15]] = 80; ct_lb(backends=10.0.0.40:8080);) ++ table=14(ls_in_stateful ), priority=0 , match=(1), action=(next;) ++ table=14(ls_in_stateful ), priority=100 , match=(reg0[[1]] == 1 && reg0[[13]] == 0), action=(ct_commit { ct_label.blocked = 0; }; next;) ++ table=14(ls_in_stateful ), priority=100 , match=(reg0[[1]] == 1 && reg0[[13]] == 1), action=(ct_commit { ct_label.blocked = 0; ct_label.label = reg3; }; next;) + ]) + + AT_CHECK([grep "ls_out_pre_lb" sw0flows | sort], [0], [dnl +@@ -3957,10 +3963,14 @@ AT_CHECK([grep "ls_in_pre_stateful" sw0flows | sort], [0], [dnl + table=7 (ls_in_pre_stateful ), priority=120 , match=(reg0[[2]] == 1 && ip6 && udp), action=(xxreg1 = ip6.dst; reg2[[0..15]] = udp.dst; ct_lb;) + ]) + ++AT_CHECK([grep "ls_in_lb" sw0flows | sort], [0], [dnl ++ table=12(ls_in_lb ), priority=0 , match=(1), action=(next;) ++]) ++ + AT_CHECK([grep "ls_in_stateful" sw0flows | sort], [0], [dnl +- table=12(ls_in_stateful ), priority=0 , match=(1), action=(next;) +- table=12(ls_in_stateful ), priority=100 , match=(reg0[[1]] == 1 && reg0[[13]] == 0), action=(ct_commit { ct_label.blocked = 0; }; next;) +- table=12(ls_in_stateful ), priority=100 , match=(reg0[[1]] == 1 && reg0[[13]] == 1), action=(ct_commit { ct_label.blocked = 0; ct_label.label = reg3; }; next;) ++ table=14(ls_in_stateful ), priority=0 , match=(1), action=(next;) ++ table=14(ls_in_stateful ), priority=100 , match=(reg0[[1]] == 1 && reg0[[13]] == 0), action=(ct_commit { ct_label.blocked = 0; }; next;) ++ table=14(ls_in_stateful ), priority=100 , match=(reg0[[1]] == 1 && reg0[[13]] == 1), action=(ct_commit { ct_label.blocked = 0; ct_label.label = reg3; }; next;) + ]) + + AT_CHECK([grep "ls_out_pre_lb" sw0flows | sort], [0], [dnl +@@ -4004,9 +4014,9 @@ AT_CHECK([grep -w "ls_in_acl" sw0flows | grep 2002 | sort], [0], [dnl + table=9 (ls_in_acl ), priority=2002 , match=(reg0[[8]] == 1 && (tcp)), action=(reg0[[1]] = 1; reg0[[13]] = 1; reg3 = 1234; next;) + ]) + AT_CHECK([grep "ls_in_stateful" sw0flows | sort], [0], [dnl +- table=12(ls_in_stateful ), priority=0 , match=(1), action=(next;) +- table=12(ls_in_stateful ), priority=100 , match=(reg0[[1]] == 1 && reg0[[13]] == 0), action=(ct_commit { ct_label.blocked = 0; }; next;) +- table=12(ls_in_stateful ), priority=100 , match=(reg0[[1]] == 1 && reg0[[13]] == 1), action=(ct_commit { ct_label.blocked = 0; ct_label.label = reg3; }; next;) ++ table=14(ls_in_stateful ), priority=0 , match=(1), action=(next;) ++ table=14(ls_in_stateful ), priority=100 , match=(reg0[[1]] == 1 && reg0[[13]] == 0), action=(ct_commit { ct_label.blocked = 0; }; next;) ++ table=14(ls_in_stateful ), priority=100 , match=(reg0[[1]] == 1 && reg0[[13]] == 1), action=(ct_commit { ct_label.blocked = 0; ct_label.label = reg3; }; next;) + ]) + + AT_CHECK([grep -w "ls_out_acl" sw0flows | grep 2002 | sort], [0], [dnl +@@ -4033,9 +4043,9 @@ AT_CHECK([grep -w "ls_in_acl" sw0flows | grep 2002 | sort], [0], [dnl + table=9 (ls_in_acl ), priority=2002 , match=(reg0[[8]] == 1 && (udp)), action=(next;) + ]) + AT_CHECK([grep "ls_in_stateful" sw0flows | sort], [0], [dnl +- table=12(ls_in_stateful ), priority=0 , match=(1), action=(next;) +- table=12(ls_in_stateful ), priority=100 , match=(reg0[[1]] == 1 && reg0[[13]] == 0), action=(ct_commit { ct_label.blocked = 0; }; next;) +- table=12(ls_in_stateful ), priority=100 , match=(reg0[[1]] == 1 && reg0[[13]] == 1), action=(ct_commit { ct_label.blocked = 0; ct_label.label = reg3; }; next;) ++ table=14(ls_in_stateful ), priority=0 , match=(1), action=(next;) ++ table=14(ls_in_stateful ), priority=100 , match=(reg0[[1]] == 1 && reg0[[13]] == 0), action=(ct_commit { ct_label.blocked = 0; }; next;) ++ table=14(ls_in_stateful ), priority=100 , match=(reg0[[1]] == 1 && reg0[[13]] == 1), action=(ct_commit { ct_label.blocked = 0; ct_label.label = reg3; }; next;) + ]) + + AT_CHECK([grep -w "ls_out_acl" sw0flows | grep 2002 | sort], [0], [dnl +@@ -4062,9 +4072,9 @@ AT_CHECK([grep -w "ls_in_acl" sw0flows | grep 2002 | sort], [0], [dnl + table=9 (ls_in_acl ), priority=2002 , match=(reg0[[8]] == 1 && (udp)), action=(next;) + ]) + AT_CHECK([grep "ls_in_stateful" sw0flows | sort], [0], [dnl +- table=12(ls_in_stateful ), priority=0 , match=(1), action=(next;) +- table=12(ls_in_stateful ), priority=100 , match=(reg0[[1]] == 1 && reg0[[13]] == 0), action=(ct_commit { ct_label.blocked = 0; }; next;) +- table=12(ls_in_stateful ), priority=100 , match=(reg0[[1]] == 1 && reg0[[13]] == 1), action=(ct_commit { ct_label.blocked = 0; ct_label.label = reg3; }; next;) ++ table=14(ls_in_stateful ), priority=0 , match=(1), action=(next;) ++ table=14(ls_in_stateful ), priority=100 , match=(reg0[[1]] == 1 && reg0[[13]] == 0), action=(ct_commit { ct_label.blocked = 0; }; next;) ++ table=14(ls_in_stateful ), priority=100 , match=(reg0[[1]] == 1 && reg0[[13]] == 1), action=(ct_commit { ct_label.blocked = 0; ct_label.label = reg3; }; next;) + ]) + + AT_CHECK([grep -w "ls_out_acl" sw0flows | grep 2002 | sort], [0], [dnl +@@ -4093,7 +4103,7 @@ AT_CAPTURE_FILE([sw0flows]) + + AT_CHECK([grep -w "ls_in_acl" sw0flows | grep 6553 | sort], [0], [dnl + table=9 (ls_in_acl ), priority=65532, match=(!ct.est && ct.rel && !ct.new && !ct.inv && ct_label.blocked == 0), action=(next;) +- table=9 (ls_in_acl ), priority=65532, match=(ct.est && !ct.rel && !ct.new && !ct.inv && ct.rpl && ct_label.blocked == 0), action=(next;) ++ table=9 (ls_in_acl ), priority=65532, match=(ct.est && !ct.rel && !ct.new && !ct.inv && ct.rpl && ct_label.blocked == 0), action=(reg0[[9]] = 0; reg0[[10]] = 0; next;) + table=9 (ls_in_acl ), priority=65532, match=(ct.inv || (ct.est && ct.rpl && ct_label.blocked == 1)), action=(drop;) + table=9 (ls_in_acl ), priority=65532, match=(nd || nd_ra || nd_rs || mldv1 || mldv2), action=(next;) + ]) +@@ -4114,7 +4124,7 @@ AT_CAPTURE_FILE([sw0flows]) + AT_CHECK([grep -w "ls_in_acl" sw0flows | grep 6553 | sort], [0], [dnl + table=9 (ls_in_acl ), priority=65532, match=(!ct.est && ct.rel && !ct.new && ct_label.blocked == 0), action=(next;) + table=9 (ls_in_acl ), priority=65532, match=((ct.est && ct.rpl && ct_label.blocked == 1)), action=(drop;) +- table=9 (ls_in_acl ), priority=65532, match=(ct.est && !ct.rel && !ct.new && ct.rpl && ct_label.blocked == 0), action=(next;) ++ table=9 (ls_in_acl ), priority=65532, match=(ct.est && !ct.rel && !ct.new && ct.rpl && ct_label.blocked == 0), action=(reg0[[9]] = 0; reg0[[10]] = 0; next;) + table=9 (ls_in_acl ), priority=65532, match=(nd || nd_ra || nd_rs || mldv1 || mldv2), action=(next;) + ]) + +@@ -4137,7 +4147,7 @@ AT_CAPTURE_FILE([sw0flows]) + + AT_CHECK([grep -w "ls_in_acl" sw0flows | grep 6553 | sort], [0], [dnl + table=9 (ls_in_acl ), priority=65532, match=(!ct.est && ct.rel && !ct.new && !ct.inv && ct_label.blocked == 0), action=(next;) +- table=9 (ls_in_acl ), priority=65532, match=(ct.est && !ct.rel && !ct.new && !ct.inv && ct.rpl && ct_label.blocked == 0), action=(next;) ++ table=9 (ls_in_acl ), priority=65532, match=(ct.est && !ct.rel && !ct.new && !ct.inv && ct.rpl && ct_label.blocked == 0), action=(reg0[[9]] = 0; reg0[[10]] = 0; next;) + table=9 (ls_in_acl ), priority=65532, match=(ct.inv || (ct.est && ct.rpl && ct_label.blocked == 1)), action=(drop;) + table=9 (ls_in_acl ), priority=65532, match=(nd || nd_ra || nd_rs || mldv1 || mldv2), action=(next;) + ]) +@@ -4309,20 +4319,20 @@ ovn-nbctl --wait=sb lsp-set-dhcpv4-options sw0-port1 $CIDR_UUID + ovn-sbctl dump-flows sw0 > sw0flows + AT_CAPTURE_FILE([sw0flows]) + +-AT_CHECK([grep -w "ls_in_dhcp_options" sw0flows | sort], [0], [dnl +- table=17(ls_in_dhcp_options ), priority=0 , match=(1), action=(next;) +- table=17(ls_in_dhcp_options ), priority=100 , match=(inport == "sw0-port1" && eth.src == 50:54:00:00:00:01 && ip4.src == 0.0.0.0 && ip4.dst == 255.255.255.255 && udp.src == 68 && udp.dst == 67), action=(reg0[[3]] = put_dhcp_opts(offerip = 10.0.0.2, hostname = "foo", lease_time = 3600, netmask = 255.255.255.0, router = 10.0.0.1, server_id = 10.0.0.1); next;) +- table=17(ls_in_dhcp_options ), priority=100 , match=(inport == "sw0-port1" && eth.src == 50:54:00:00:00:01 && ip4.src == 10.0.0.2 && ip4.dst == {10.0.0.1, 255.255.255.255} && udp.src == 68 && udp.dst == 67), action=(reg0[[3]] = put_dhcp_opts(offerip = 10.0.0.2, hostname = "foo", lease_time = 3600, netmask = 255.255.255.0, router = 10.0.0.1, server_id = 10.0.0.1); next;) ++AT_CHECK([grep -w "ls_in_dhcp_options" sw0flows | sort | sed 's/table=../table=??/'], [0], [dnl ++ table=??(ls_in_dhcp_options ), priority=0 , match=(1), action=(next;) ++ table=??(ls_in_dhcp_options ), priority=100 , match=(inport == "sw0-port1" && eth.src == 50:54:00:00:00:01 && ip4.src == 0.0.0.0 && ip4.dst == 255.255.255.255 && udp.src == 68 && udp.dst == 67), action=(reg0[[3]] = put_dhcp_opts(offerip = 10.0.0.2, hostname = "foo", lease_time = 3600, netmask = 255.255.255.0, router = 10.0.0.1, server_id = 10.0.0.1); next;) ++ table=??(ls_in_dhcp_options ), priority=100 , match=(inport == "sw0-port1" && eth.src == 50:54:00:00:00:01 && ip4.src == 10.0.0.2 && ip4.dst == {10.0.0.1, 255.255.255.255} && udp.src == 68 && udp.dst == 67), action=(reg0[[3]] = put_dhcp_opts(offerip = 10.0.0.2, hostname = "foo", lease_time = 3600, netmask = 255.255.255.0, router = 10.0.0.1, server_id = 10.0.0.1); next;) + ]) + + check ovn-nbctl --wait=sb lsp-set-options sw0-port1 hostname="\"port1\"" + ovn-sbctl dump-flows sw0 > sw0flows + AT_CAPTURE_FILE([sw0flows]) + +-AT_CHECK([grep -w "ls_in_dhcp_options" sw0flows | sort], [0], [dnl +- table=17(ls_in_dhcp_options ), priority=0 , match=(1), action=(next;) +- table=17(ls_in_dhcp_options ), priority=100 , match=(inport == "sw0-port1" && eth.src == 50:54:00:00:00:01 && ip4.src == 0.0.0.0 && ip4.dst == 255.255.255.255 && udp.src == 68 && udp.dst == 67), action=(reg0[[3]] = put_dhcp_opts(offerip = 10.0.0.2, hostname = "port1", lease_time = 3600, netmask = 255.255.255.0, router = 10.0.0.1, server_id = 10.0.0.1); next;) +- table=17(ls_in_dhcp_options ), priority=100 , match=(inport == "sw0-port1" && eth.src == 50:54:00:00:00:01 && ip4.src == 10.0.0.2 && ip4.dst == {10.0.0.1, 255.255.255.255} && udp.src == 68 && udp.dst == 67), action=(reg0[[3]] = put_dhcp_opts(offerip = 10.0.0.2, hostname = "port1", lease_time = 3600, netmask = 255.255.255.0, router = 10.0.0.1, server_id = 10.0.0.1); next;) ++AT_CHECK([grep -w "ls_in_dhcp_options" sw0flows | sort | sed 's/table=../table=??/'], [0], [dnl ++ table=??(ls_in_dhcp_options ), priority=0 , match=(1), action=(next;) ++ table=??(ls_in_dhcp_options ), priority=100 , match=(inport == "sw0-port1" && eth.src == 50:54:00:00:00:01 && ip4.src == 0.0.0.0 && ip4.dst == 255.255.255.255 && udp.src == 68 && udp.dst == 67), action=(reg0[[3]] = put_dhcp_opts(offerip = 10.0.0.2, hostname = "port1", lease_time = 3600, netmask = 255.255.255.0, router = 10.0.0.1, server_id = 10.0.0.1); next;) ++ table=??(ls_in_dhcp_options ), priority=100 , match=(inport == "sw0-port1" && eth.src == 50:54:00:00:00:01 && ip4.src == 10.0.0.2 && ip4.dst == {10.0.0.1, 255.255.255.255} && udp.src == 68 && udp.dst == 67), action=(reg0[[3]] = put_dhcp_opts(offerip = 10.0.0.2, hostname = "port1", lease_time = 3600, netmask = 255.255.255.0, router = 10.0.0.1, server_id = 10.0.0.1); next;) + ]) + + ovn-nbctl dhcp-options-set-options $CIDR_UUID lease_time=3600 router=10.0.0.1 server_id=10.0.0.1 server_mac=c0:ff:ee:00:00:01 +@@ -4330,10 +4340,10 @@ check ovn-nbctl --wait=sb lsp-set-options sw0-port1 hostname="\"bar\"" + ovn-sbctl dump-flows sw0 > sw0flows + AT_CAPTURE_FILE([sw0flows]) + +-AT_CHECK([grep -w "ls_in_dhcp_options" sw0flows | sort], [0], [dnl +- table=17(ls_in_dhcp_options ), priority=0 , match=(1), action=(next;) +- table=17(ls_in_dhcp_options ), priority=100 , match=(inport == "sw0-port1" && eth.src == 50:54:00:00:00:01 && ip4.src == 0.0.0.0 && ip4.dst == 255.255.255.255 && udp.src == 68 && udp.dst == 67), action=(reg0[[3]] = put_dhcp_opts(offerip = 10.0.0.2, hostname = "bar", lease_time = 3600, netmask = 255.255.255.0, router = 10.0.0.1, server_id = 10.0.0.1); next;) +- table=17(ls_in_dhcp_options ), priority=100 , match=(inport == "sw0-port1" && eth.src == 50:54:00:00:00:01 && ip4.src == 10.0.0.2 && ip4.dst == {10.0.0.1, 255.255.255.255} && udp.src == 68 && udp.dst == 67), action=(reg0[[3]] = put_dhcp_opts(offerip = 10.0.0.2, hostname = "bar", lease_time = 3600, netmask = 255.255.255.0, router = 10.0.0.1, server_id = 10.0.0.1); next;) ++AT_CHECK([grep -w "ls_in_dhcp_options" sw0flows | sort | sed 's/table=../table=??/'], [0], [dnl ++ table=??(ls_in_dhcp_options ), priority=0 , match=(1), action=(next;) ++ table=??(ls_in_dhcp_options ), priority=100 , match=(inport == "sw0-port1" && eth.src == 50:54:00:00:00:01 && ip4.src == 0.0.0.0 && ip4.dst == 255.255.255.255 && udp.src == 68 && udp.dst == 67), action=(reg0[[3]] = put_dhcp_opts(offerip = 10.0.0.2, hostname = "bar", lease_time = 3600, netmask = 255.255.255.0, router = 10.0.0.1, server_id = 10.0.0.1); next;) ++ table=??(ls_in_dhcp_options ), priority=100 , match=(inport == "sw0-port1" && eth.src == 50:54:00:00:00:01 && ip4.src == 10.0.0.2 && ip4.dst == {10.0.0.1, 255.255.255.255} && udp.src == 68 && udp.dst == 67), action=(reg0[[3]] = put_dhcp_opts(offerip = 10.0.0.2, hostname = "bar", lease_time = 3600, netmask = 255.255.255.0, router = 10.0.0.1, server_id = 10.0.0.1); next;) + ]) + + AT_CLEANUP +@@ -5336,6 +5346,23 @@ AT_CHECK([grep "lr_out_snat" lr0flows | sed 's/table=./table=?/' | sort], [0], [ + AT_CLEANUP + ]) + ++OVN_FOR_EACH_NORTHD([ ++AT_SETUP([Load Balancer SB duplicates]) ++ovn_start ++ ++check ovn-nbctl ls-add ls -- lb-add lb1 10.0.0.1:80 10.0.0.2:80 -- ls-lb-add ls lb1 ++check ovn-nbctl --wait=sb sync ++ ++dps=$(fetch_column Load_Balancer datapaths) ++nlb=$(fetch_column nb:Load_Balancer _uuid) ++AT_CHECK([ovn-sbctl create Load_Balancer name=lb1 datapaths="$dps" external_ids="lb_id=$nlb"], [0], [ignore]) ++ ++check ovn-nbctl --wait=sb sync ++check_row_count Load_Balancer 1 ++ ++AT_CLEANUP ++]) ++ + OVN_FOR_EACH_NORTHD([ + AT_SETUP([ovn -- Add tags to logical flows]) + ovn_start +@@ -5718,6 +5745,12 @@ AT_CHECK([grep lr_in_gw_redirect lrflows | grep cr-DR | sed 's/table=../table=?? + table=??(lr_in_gw_redirect ), priority=50 , match=(outport == "DR-S3"), action=(outport = "cr-DR-S3"; next;) + ]) + ++# Check that ovn-northd logs a warning when trying to configure NAT ++# on the router with multiple distributed gw ports. Such configurations are ++# not supported yet. ++check ovn-nbctl lr-nat-add DR dnat_and_snat 42.42.42.1 20.0.0.2 ++AT_CHECK([grep -q 'NAT is configured on logical router DR, which has 2 distributed gateway ports. NAT is not supported yet when there is more than one distributed gateway port on the router.' northd/ovn-northd.log], [0]) ++ + AT_CLEANUP + ]) + +@@ -5890,3 +5923,227 @@ AT_CHECK([grep -e "(lr_in_ip_routing ).*outport" lr0flows | sed 's/table=../ta + + AT_CLEANUP + ]) ++ ++OVN_FOR_EACH_NORTHD([ ++AT_SETUP([check exclude-lb-vips-from-garp option]) ++ovn_start ++ ++ovn-nbctl lr-add R1 ++ovn-nbctl set logical_router R1 options:chassis=hv1 ++ovn-nbctl lrp-add R1 R1-S1 02:ac:10:01:00:01 172.16.1.1/24 ++ ++ovn-nbctl ls-add S1 ++ovn-nbctl lsp-add S1 S1-R1 ++ovn-nbctl lsp-set-type S1-R1 router ++ovn-nbctl lsp-set-addresses S1-R1 02:ac:10:01:00:01 ++ovn-nbctl --wait=sb lsp-set-options S1-R1 router-port=R1-S1 nat-addresses="router" ++ ++ovn-nbctl lr-nat-add R1 snat 172.16.1.1 10.0.0.0/24 ++ovn-nbctl lr-nat-add R1 dnat 172.16.1.2 10.0.0.1 ++# Add load balancers ++ovn-nbctl lb-add lb0 172.16.1.10:80 10.0.0.1:80 ++ovn-nbctl lr-lb-add R1 lb0 ++ovn-nbctl lb-add lb1 172.16.1.10:8080 10.0.0.1:8080 ++ovn-nbctl lr-lb-add R1 lb1 ++ ++AT_CHECK([ovn-sbctl get Port_Binding S1-R1 nat_addresses |grep -q 172.16.1.10], [0]) ++ ++ovn-nbctl --wait=sb lsp-set-options S1-R1 router-port=R1-S1 nat-addresses="router" \ ++ exclude-lb-vips-from-garp="true" ++ ++AT_CHECK([ovn-sbctl get Port_Binding S1-R1 nat_addresses |grep -q 172.16.1.10], [1]) ++ ++AT_CLEANUP ++]) ++ ++AT_SETUP([ACLs after lb]) ++AT_KEYWORDS([acl]) ++ovn_start ++ ++check ovn-nbctl --wait=sb \ ++ -- ls-add ls \ ++ -- lsp-add ls lsp ++ ++check ovn-nbctl pg-add pg0 lsp ++ ++check ovn-nbctl acl-add pg0 from-lport 1004 "ip4 && ip4.dst == 10.0.0.2" drop ++check ovn-nbctl acl-add pg0 from-lport 1002 "ip4 && tcp" allow-related ++check ovn-nbctl acl-add pg0 from-lport 1003 "ip4 && icmp" allow-related ++check ovn-nbctl acl-add pg0 from-lport 1001 "ip4" drop ++ ++check ovn-nbctl lb-add lb0 10.0.0.2 10.0.0.10 ++check ovn-nbctl ls-lb-add ls lb0 ++check ovn-nbctl --wait=sb sync ++ ++ovn-sbctl dump-flows ls > lsflows ++AT_CAPTURE_FILE([lsflows]) ++ ++AT_CHECK([grep -e "ls_in_acl" lsflows | sed 's/table=../table=??/' | sort], [0], [dnl ++ table=??(ls_in_acl ), priority=0 , match=(1), action=(next;) ++ table=??(ls_in_acl ), priority=1 , match=(ip && (!ct.est || (ct.est && ct_label.blocked == 1))), action=(reg0[[1]] = 1; next;) ++ table=??(ls_in_acl ), priority=2001 , match=(reg0[[10]] == 1 && (ip4)), action=(ct_commit { ct_label.blocked = 1; }; /* drop */) ++ table=??(ls_in_acl ), priority=2001 , match=(reg0[[9]] == 1 && (ip4)), action=(/* drop */) ++ table=??(ls_in_acl ), priority=2002 , match=(reg0[[7]] == 1 && (ip4 && tcp)), action=(reg0[[1]] = 1; next;) ++ table=??(ls_in_acl ), priority=2002 , match=(reg0[[8]] == 1 && (ip4 && tcp)), action=(next;) ++ table=??(ls_in_acl ), priority=2003 , match=(reg0[[7]] == 1 && (ip4 && icmp)), action=(reg0[[1]] = 1; next;) ++ table=??(ls_in_acl ), priority=2003 , match=(reg0[[8]] == 1 && (ip4 && icmp)), action=(next;) ++ table=??(ls_in_acl ), priority=2004 , match=(reg0[[10]] == 1 && (ip4 && ip4.dst == 10.0.0.2)), action=(ct_commit { ct_label.blocked = 1; }; /* drop */) ++ table=??(ls_in_acl ), priority=2004 , match=(reg0[[9]] == 1 && (ip4 && ip4.dst == 10.0.0.2)), action=(/* drop */) ++ table=??(ls_in_acl ), priority=34000, match=(eth.dst == $svc_monitor_mac), action=(next;) ++ table=??(ls_in_acl ), priority=65532, match=(!ct.est && ct.rel && !ct.new && !ct.inv && ct_label.blocked == 0), action=(next;) ++ table=??(ls_in_acl ), priority=65532, match=(ct.est && !ct.rel && !ct.new && !ct.inv && ct.rpl && ct_label.blocked == 0), action=(reg0[[9]] = 0; reg0[[10]] = 0; next;) ++ table=??(ls_in_acl ), priority=65532, match=(ct.inv || (ct.est && ct.rpl && ct_label.blocked == 1)), action=(drop;) ++ table=??(ls_in_acl ), priority=65532, match=(nd || nd_ra || nd_rs || mldv1 || mldv2), action=(next;) ++ table=??(ls_in_acl_after_lb ), priority=0 , match=(1), action=(next;) ++ table=??(ls_in_acl_hint ), priority=0 , match=(1), action=(next;) ++ table=??(ls_in_acl_hint ), priority=1 , match=(ct.est && ct_label.blocked == 0), action=(reg0[[10]] = 1; next;) ++ table=??(ls_in_acl_hint ), priority=2 , match=(ct.est && ct_label.blocked == 1), action=(reg0[[9]] = 1; next;) ++ table=??(ls_in_acl_hint ), priority=3 , match=(!ct.est), action=(reg0[[9]] = 1; next;) ++ table=??(ls_in_acl_hint ), priority=4 , match=(!ct.new && ct.est && !ct.rpl && ct_label.blocked == 0), action=(reg0[[8]] = 1; reg0[[10]] = 1; next;) ++ table=??(ls_in_acl_hint ), priority=5 , match=(!ct.trk), action=(reg0[[8]] = 1; reg0[[9]] = 1; next;) ++ table=??(ls_in_acl_hint ), priority=6 , match=(!ct.new && ct.est && !ct.rpl && ct_label.blocked == 1), action=(reg0[[7]] = 1; reg0[[9]] = 1; next;) ++ table=??(ls_in_acl_hint ), priority=7 , match=(ct.new && !ct.est), action=(reg0[[7]] = 1; reg0[[9]] = 1; next;) ++]) ++ ++AT_CHECK([grep -e "ls_in_lb" lsflows | sed 's/table=../table=??/' | sort], [0], [dnl ++ table=??(ls_in_lb ), priority=0 , match=(1), action=(next;) ++ table=??(ls_in_lb ), priority=110 , match=(ct.new && ip4.dst == 10.0.0.2), action=(reg0[[1]] = 0; reg1 = 10.0.0.2; ct_lb(backends=10.0.0.10);) ++]) ++ ++AT_CHECK([grep -e "ls_in_stateful" lsflows | sed 's/table=../table=??/' | sort], [0], [dnl ++ table=??(ls_in_stateful ), priority=0 , match=(1), action=(next;) ++ table=??(ls_in_stateful ), priority=100 , match=(reg0[[1]] == 1 && reg0[[13]] == 0), action=(ct_commit { ct_label.blocked = 0; }; next;) ++ table=??(ls_in_stateful ), priority=100 , match=(reg0[[1]] == 1 && reg0[[13]] == 1), action=(ct_commit { ct_label.blocked = 0; ct_label.label = reg3; }; next;) ++]) ++ ++AS_BOX([Remove and add the ACLs back with the apply-after-lb option]) ++ ++check ovn-nbctl clear port_group . acls ++ ++check ovn-nbctl --apply-after-lb acl-add pg0 from-lport 1004 "ip4 && ip4.dst == 10.0.0.2" drop ++check ovn-nbctl --apply-after-lb acl-add pg0 from-lport 1002 "ip4 && tcp" allow-related ++check ovn-nbctl --apply-after-lb acl-add pg0 from-lport 1003 "ip4 && icmp" allow-related ++check ovn-nbctl --apply-after-lb acl-add pg0 from-lport 1001 "ip4" drop ++ ++check ovn-nbctl --wait=sb sync ++ ++ovn-sbctl dump-flows ls > lsflows ++AT_CAPTURE_FILE([lsflows]) ++ ++AT_CHECK([grep -e "ls_in_acl" lsflows | sed 's/table=../table=??/' | sort], [0], [dnl ++ table=??(ls_in_acl ), priority=0 , match=(1), action=(next;) ++ table=??(ls_in_acl ), priority=1 , match=(ip && (!ct.est || (ct.est && ct_label.blocked == 1))), action=(reg0[[1]] = 1; next;) ++ table=??(ls_in_acl ), priority=34000, match=(eth.dst == $svc_monitor_mac), action=(next;) ++ table=??(ls_in_acl ), priority=65532, match=(!ct.est && ct.rel && !ct.new && !ct.inv && ct_label.blocked == 0), action=(next;) ++ table=??(ls_in_acl ), priority=65532, match=(ct.est && !ct.rel && !ct.new && !ct.inv && ct.rpl && ct_label.blocked == 0), action=(reg0[[9]] = 0; reg0[[10]] = 0; next;) ++ table=??(ls_in_acl ), priority=65532, match=(ct.inv || (ct.est && ct.rpl && ct_label.blocked == 1)), action=(drop;) ++ table=??(ls_in_acl ), priority=65532, match=(nd || nd_ra || nd_rs || mldv1 || mldv2), action=(next;) ++ table=??(ls_in_acl_after_lb ), priority=0 , match=(1), action=(next;) ++ table=??(ls_in_acl_after_lb ), priority=2001 , match=(reg0[[10]] == 1 && (ip4)), action=(ct_commit { ct_label.blocked = 1; }; /* drop */) ++ table=??(ls_in_acl_after_lb ), priority=2001 , match=(reg0[[9]] == 1 && (ip4)), action=(/* drop */) ++ table=??(ls_in_acl_after_lb ), priority=2002 , match=(reg0[[7]] == 1 && (ip4 && tcp)), action=(reg0[[1]] = 1; next;) ++ table=??(ls_in_acl_after_lb ), priority=2002 , match=(reg0[[8]] == 1 && (ip4 && tcp)), action=(next;) ++ table=??(ls_in_acl_after_lb ), priority=2003 , match=(reg0[[7]] == 1 && (ip4 && icmp)), action=(reg0[[1]] = 1; next;) ++ table=??(ls_in_acl_after_lb ), priority=2003 , match=(reg0[[8]] == 1 && (ip4 && icmp)), action=(next;) ++ table=??(ls_in_acl_after_lb ), priority=2004 , match=(reg0[[10]] == 1 && (ip4 && ip4.dst == 10.0.0.2)), action=(ct_commit { ct_label.blocked = 1; }; /* drop */) ++ table=??(ls_in_acl_after_lb ), priority=2004 , match=(reg0[[9]] == 1 && (ip4 && ip4.dst == 10.0.0.2)), action=(/* drop */) ++ table=??(ls_in_acl_hint ), priority=0 , match=(1), action=(next;) ++ table=??(ls_in_acl_hint ), priority=1 , match=(ct.est && ct_label.blocked == 0), action=(reg0[[10]] = 1; next;) ++ table=??(ls_in_acl_hint ), priority=2 , match=(ct.est && ct_label.blocked == 1), action=(reg0[[9]] = 1; next;) ++ table=??(ls_in_acl_hint ), priority=3 , match=(!ct.est), action=(reg0[[9]] = 1; next;) ++ table=??(ls_in_acl_hint ), priority=4 , match=(!ct.new && ct.est && !ct.rpl && ct_label.blocked == 0), action=(reg0[[8]] = 1; reg0[[10]] = 1; next;) ++ table=??(ls_in_acl_hint ), priority=5 , match=(!ct.trk), action=(reg0[[8]] = 1; reg0[[9]] = 1; next;) ++ table=??(ls_in_acl_hint ), priority=6 , match=(!ct.new && ct.est && !ct.rpl && ct_label.blocked == 1), action=(reg0[[7]] = 1; reg0[[9]] = 1; next;) ++ table=??(ls_in_acl_hint ), priority=7 , match=(ct.new && !ct.est), action=(reg0[[7]] = 1; reg0[[9]] = 1; next;) ++]) ++ ++AT_CHECK([grep -e "ls_in_lb" lsflows | sed 's/table=../table=??/' | sort], [0], [dnl ++ table=??(ls_in_lb ), priority=0 , match=(1), action=(next;) ++ table=??(ls_in_lb ), priority=110 , match=(ct.new && ip4.dst == 10.0.0.2), action=(reg0[[1]] = 0; reg1 = 10.0.0.2; ct_lb(backends=10.0.0.10);) ++]) ++ ++AT_CHECK([grep -e "ls_in_stateful" lsflows | sed 's/table=../table=??/' | sort], [0], [dnl ++ table=??(ls_in_stateful ), priority=0 , match=(1), action=(next;) ++ table=??(ls_in_stateful ), priority=100 , match=(reg0[[1]] == 1 && reg0[[13]] == 0), action=(ct_commit { ct_label.blocked = 0; }; next;) ++ table=??(ls_in_stateful ), priority=100 , match=(reg0[[1]] == 1 && reg0[[13]] == 1), action=(ct_commit { ct_label.blocked = 0; ct_label.label = reg3; }; next;) ++]) ++ ++AS_BOX([Remove and add the ACLs back with a few ACLs with apply-after-lb option]) ++ ++check ovn-nbctl clear port_group . acls ++ ++check ovn-nbctl --apply-after-lb acl-add pg0 from-lport 1004 "ip4 && ip4.dst == 10.0.0.2" drop ++check ovn-nbctl acl-add pg0 from-lport 1002 "ip4 && tcp" allow-related ++check ovn-nbctl acl-add pg0 from-lport 1003 "ip4 && icmp" allow-related ++check ovn-nbctl --apply-after-lb acl-add pg0 from-lport 1001 "ip4" drop ++ ++check ovn-nbctl --wait=sb sync ++ ++ovn-sbctl dump-flows ls > lsflows ++AT_CAPTURE_FILE([lsflows]) ++ ++AT_CHECK([grep -e "ls_in_acl" lsflows | sed 's/table=../table=??/' | sort], [0], [dnl ++ table=??(ls_in_acl ), priority=0 , match=(1), action=(next;) ++ table=??(ls_in_acl ), priority=1 , match=(ip && (!ct.est || (ct.est && ct_label.blocked == 1))), action=(reg0[[1]] = 1; next;) ++ table=??(ls_in_acl ), priority=2002 , match=(reg0[[7]] == 1 && (ip4 && tcp)), action=(reg0[[1]] = 1; next;) ++ table=??(ls_in_acl ), priority=2002 , match=(reg0[[8]] == 1 && (ip4 && tcp)), action=(next;) ++ table=??(ls_in_acl ), priority=2003 , match=(reg0[[7]] == 1 && (ip4 && icmp)), action=(reg0[[1]] = 1; next;) ++ table=??(ls_in_acl ), priority=2003 , match=(reg0[[8]] == 1 && (ip4 && icmp)), action=(next;) ++ table=??(ls_in_acl ), priority=34000, match=(eth.dst == $svc_monitor_mac), action=(next;) ++ table=??(ls_in_acl ), priority=65532, match=(!ct.est && ct.rel && !ct.new && !ct.inv && ct_label.blocked == 0), action=(next;) ++ table=??(ls_in_acl ), priority=65532, match=(ct.est && !ct.rel && !ct.new && !ct.inv && ct.rpl && ct_label.blocked == 0), action=(reg0[[9]] = 0; reg0[[10]] = 0; next;) ++ table=??(ls_in_acl ), priority=65532, match=(ct.inv || (ct.est && ct.rpl && ct_label.blocked == 1)), action=(drop;) ++ table=??(ls_in_acl ), priority=65532, match=(nd || nd_ra || nd_rs || mldv1 || mldv2), action=(next;) ++ table=??(ls_in_acl_after_lb ), priority=0 , match=(1), action=(next;) ++ table=??(ls_in_acl_after_lb ), priority=2001 , match=(reg0[[10]] == 1 && (ip4)), action=(ct_commit { ct_label.blocked = 1; }; /* drop */) ++ table=??(ls_in_acl_after_lb ), priority=2001 , match=(reg0[[9]] == 1 && (ip4)), action=(/* drop */) ++ table=??(ls_in_acl_after_lb ), priority=2004 , match=(reg0[[10]] == 1 && (ip4 && ip4.dst == 10.0.0.2)), action=(ct_commit { ct_label.blocked = 1; }; /* drop */) ++ table=??(ls_in_acl_after_lb ), priority=2004 , match=(reg0[[9]] == 1 && (ip4 && ip4.dst == 10.0.0.2)), action=(/* drop */) ++ table=??(ls_in_acl_hint ), priority=0 , match=(1), action=(next;) ++ table=??(ls_in_acl_hint ), priority=1 , match=(ct.est && ct_label.blocked == 0), action=(reg0[[10]] = 1; next;) ++ table=??(ls_in_acl_hint ), priority=2 , match=(ct.est && ct_label.blocked == 1), action=(reg0[[9]] = 1; next;) ++ table=??(ls_in_acl_hint ), priority=3 , match=(!ct.est), action=(reg0[[9]] = 1; next;) ++ table=??(ls_in_acl_hint ), priority=4 , match=(!ct.new && ct.est && !ct.rpl && ct_label.blocked == 0), action=(reg0[[8]] = 1; reg0[[10]] = 1; next;) ++ table=??(ls_in_acl_hint ), priority=5 , match=(!ct.trk), action=(reg0[[8]] = 1; reg0[[9]] = 1; next;) ++ table=??(ls_in_acl_hint ), priority=6 , match=(!ct.new && ct.est && !ct.rpl && ct_label.blocked == 1), action=(reg0[[7]] = 1; reg0[[9]] = 1; next;) ++ table=??(ls_in_acl_hint ), priority=7 , match=(ct.new && !ct.est), action=(reg0[[7]] = 1; reg0[[9]] = 1; next;) ++]) ++ ++AT_CHECK([grep -e "ls_in_lb" lsflows | sed 's/table=../table=??/' | sort], [0], [dnl ++ table=??(ls_in_lb ), priority=0 , match=(1), action=(next;) ++ table=??(ls_in_lb ), priority=110 , match=(ct.new && ip4.dst == 10.0.0.2), action=(reg0[[1]] = 0; reg1 = 10.0.0.2; ct_lb(backends=10.0.0.10);) ++]) ++ ++AT_CHECK([grep -e "ls_in_stateful" lsflows | sed 's/table=../table=??/' | sort], [0], [dnl ++ table=??(ls_in_stateful ), priority=0 , match=(1), action=(next;) ++ table=??(ls_in_stateful ), priority=100 , match=(reg0[[1]] == 1 && reg0[[13]] == 0), action=(ct_commit { ct_label.blocked = 0; }; next;) ++ table=??(ls_in_stateful ), priority=100 , match=(reg0[[1]] == 1 && reg0[[13]] == 1), action=(ct_commit { ct_label.blocked = 0; ct_label.label = reg3; }; next;) ++]) ++ ++AT_CLEANUP ++]) ++ ++OVN_FOR_EACH_NORTHD([ ++AT_SETUP([LR neighbor lookup and learning flows]) ++ovn_start ++ ++# Create logical routers ++ovn-nbctl --wait=sb lr-add lr0 ++ ++ovn-sbctl dump-flows lr0 > lrflows ++AT_CAPTURE_FILE([lrflows]) ++ ++AT_CHECK([cat lrflows | grep -e lr_in_lookup_neighbor -e lr_in_learn_neighbor | sort], [0], [dnl ++ table=1 (lr_in_lookup_neighbor), priority=0 , match=(1), action=(reg9[[2]] = 1; next;) ++ table=1 (lr_in_lookup_neighbor), priority=100 , match=(arp.op == 2), action=(reg9[[2]] = lookup_arp(inport, arp.spa, arp.sha); next;) ++ table=1 (lr_in_lookup_neighbor), priority=100 , match=(nd_na), action=(reg9[[2]] = lookup_nd(inport, nd.target, nd.tll); next;) ++ table=1 (lr_in_lookup_neighbor), priority=100 , match=(nd_ns), action=(reg9[[2]] = lookup_nd(inport, ip6.src, nd.sll); next;) ++ table=2 (lr_in_learn_neighbor), priority=100 , match=(reg9[[2]] == 1), action=(next;) ++ table=2 (lr_in_learn_neighbor), priority=90 , match=(arp), action=(put_arp(inport, arp.spa, arp.sha); next;) ++ table=2 (lr_in_learn_neighbor), priority=90 , match=(nd_na), action=(put_nd(inport, nd.target, nd.tll); next;) ++ table=2 (lr_in_learn_neighbor), priority=90 , match=(nd_ns), action=(put_nd(inport, ip6.src, nd.sll); next;) ++ table=2 (lr_in_learn_neighbor), priority=95 , match=(nd_na && nd.tll == 0), action=(put_nd(inport, nd.target, eth.src); next;) ++]) ++ ++AT_CLEANUP ++]) +diff --git a/tests/ovn-performance.at b/tests/ovn-performance.at +index 10341ad72..f92fbebf1 100644 +--- a/tests/ovn-performance.at ++++ b/tests/ovn-performance.at +@@ -543,6 +543,23 @@ OVN_CONTROLLER_EXPECT_HIT( + [as hv3 ovs-vsctl set interface vgw3 external-ids:ovn-egress-iface=true] + ) + ++ovn-nbctl --wait=hv meter-add meter0 drop 100 pktps 10 ++ ++OVN_CONTROLLER_EXPECT_NO_HIT( ++ [hv1 hv2 hv3 hv4], [lflow_run], ++ [ovn-nbctl --wait=hv lr-copp-add lr1 arp meter0] ++) ++ ++OVN_CONTROLLER_EXPECT_NO_HIT( ++ [hv1 hv2 hv3 hv4], [lflow_run], ++ [ovn-nbctl --wait=hv --may-exist meter-add meter0 drop 200 pktps 10] ++) ++ ++OVN_CONTROLLER_EXPECT_NO_HIT( ++ [hv1 hv2 hv3 hv4], [lflow_run], ++ [ovn-nbctl --wait=hv meter-del meter0] ++) ++ + for i in 1 2; do + j=$((i%2 + 1)) + lp=lp$i +diff --git a/tests/ovn.at b/tests/ovn.at +index 9ec62e321..0f71219b9 100644 +--- a/tests/ovn.at ++++ b/tests/ovn.at +@@ -3369,8 +3369,8 @@ wait_for_ports_up + ovn-sbctl dump-flows ls > lsflows + AT_CAPTURE_FILE([lsflows]) + +-AT_CHECK([grep -w "ls_in_arp_rsp" lsflows | sort], [0], [dnl +- table=16(ls_in_arp_rsp ), priority=0 , match=(1), action=(next;) ++AT_CHECK([grep -w "ls_in_arp_rsp" lsflows | sed 's/table=../table=??/' | sort], [0], [dnl ++ table=??(ls_in_arp_rsp ), priority=0 , match=(1), action=(next;) + ]) + + for i in 1 2; do +@@ -3432,8 +3432,8 @@ wait_for_ports_up + ovn-sbctl dump-flows ls > lsflows + AT_CAPTURE_FILE([lsflows]) + +-AT_CHECK([grep -w "ls_in_arp_rsp" lsflows | sort], [0], [dnl +- table=16(ls_in_arp_rsp ), priority=0 , match=(1), action=(next;) ++AT_CHECK([grep -w "ls_in_arp_rsp" lsflows | sed 's/table=../table=??/' | sort], [0], [dnl ++ table=??(ls_in_arp_rsp ), priority=0 , match=(1), action=(next;) + ]) + + test_nd_na() { +@@ -8651,6 +8651,44 @@ expected="fffffffffffff0000000000108060001080006040001f00000000001c0a80003000000 + echo $expected >> expout + AT_CHECK([sort packets], [0], [expout]) + ++# Temporarily remove nat-addresses option to avoid race conditions ++# due to GARP backoff ++ovn-nbctl lsp-set-options lrp0-rp router-port=lrp0 nat-addresses="" ++ ++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 ++} ++ ++as hv1 reset_pcap_file snoopvif hv1/snoopvif ++ ++# Re-add nat-addresses option ++ovn-nbctl lsp-set-options lrp0-rp router-port=lrp0 nat-addresses="router" exclude-lb-vips-from-garp="true" ++ ++# Wait for packets to be received. ++OVS_WAIT_UNTIL([test `wc -c < "hv1/snoopvif-tx.pcap"` -ge 250]) ++trim_zeros() { ++ sed 's/\(00\)\{1,\}$//' ++} ++ ++$PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv1/snoopvif-tx.pcap | trim_zeros > packets ++g0="fffffffffffff0000000000108060001080006040001f00000000001c0a80001000000000000c0a80001" ++echo $g0 > expout ++g1="fffffffffffff0000000000108060001080006040001f00000000001c0a80002000000000000c0a80002" ++echo $g1 >> expout ++ ++grep $g0 packets | head -1 > exp ++grep $g1 packets | head -1 >> exp ++AT_CHECK([cat exp], [0], [expout]) ++ ++g3="fffffffffffff0000000000108060001080006040001f00000000001c0a80003000000000000c0a80003" ++AT_CHECK([grep -q $g3 packets], [1]) ++ + OVN_CLEANUP([hv1]) + + AT_CLEANUP +@@ -8965,33 +9003,59 @@ ovn-nbctl lsp-set-addresses lp2 $lp2_mac + ovn-nbctl --wait=sb sync + wait_for_ports_up + +-ovn-nbctl acl-add lsw0 to-lport 1000 'tcp.dst==80' drop +-ovn-nbctl --log --severity=alert --name=drop-flow acl-add lsw0 to-lport 1000 'tcp.dst==81' drop ++ovn-nbctl acl-add lsw0 from-lport 1000 'tcp.dst==80' drop ++ovn-nbctl --log --severity=alert --name=drop-flow acl-add lsw0 from-lport 1000 'tcp.dst==81' drop ++ ++ovn-nbctl acl-add lsw0 to-lport 1000 'tcp.dst==180' drop ++ovn-nbctl --log --severity=alert --name=drop-flow acl-add lsw0 to-lport 1000 'tcp.dst==181' drop ++ ++ovn-nbctl acl-add lsw0 from-lport 1000 'tcp.dst==82' allow ++ovn-nbctl --log --severity=info --name=allow-flow acl-add lsw0 from-lport 1000 'tcp.dst==83' allow + + ovn-nbctl acl-add lsw0 to-lport 1000 'tcp.dst==82' allow + ovn-nbctl --log --severity=info --name=allow-flow acl-add lsw0 to-lport 1000 'tcp.dst==83' allow + ++ovn-nbctl acl-add lsw0 from-lport 1000 'tcp.dst==84' allow-related ++ovn-nbctl --log acl-add lsw0 from-lport 1000 'tcp.dst==85' allow-related ++ + ovn-nbctl acl-add lsw0 to-lport 1000 'tcp.dst==84' allow-related + ovn-nbctl --log acl-add lsw0 to-lport 1000 'tcp.dst==85' allow-related + +-ovn-nbctl acl-add lsw0 to-lport 1000 'tcp.dst==86' reject +-ovn-nbctl --wait=hv --log --severity=alert --name=reject-flow acl-add lsw0 to-lport 1000 'tcp.dst==87' reject ++ovn-nbctl acl-add lsw0 from-lport 1000 'tcp.dst==86' reject ++ovn-nbctl --log --severity=alert --name=reject-flow acl-add lsw0 from-lport 1000 'tcp.dst==87' reject ++ ++ovn-nbctl acl-add lsw0 to-lport 1000 'tcp.dst==186' reject ++ovn-nbctl --log --severity=alert --name=reject-flow acl-add lsw0 to-lport 1000 'tcp.dst==187' reject ++ ++ovn-nbctl --wait=hv sync + + ovn-sbctl dump-flows > sbflows + AT_CAPTURE_FILE([sbflows]) + +-# Send packet that should be dropped without logging. ++# Send packet that should be dropped without logging in the ingress pipeline. + packet="inport==\"lp1\" && eth.src==$lp1_mac && eth.dst==$lp2_mac && + ip4 && ip.ttl==64 && ip4.src==$lp1_ip && ip4.dst==$lp2_ip && + tcp && tcp.flags==2 && tcp.src==4360 && tcp.dst==80" + as hv ovs-appctl -t ovn-controller inject-pkt "$packet" + +-# Send packet that should be dropped with logging. ++# Send packet that should be dropped with logging in the ingress pipeline. + packet="inport==\"lp1\" && eth.src==$lp1_mac && eth.dst==$lp2_mac && + ip4 && ip.ttl==64 && ip4.src==$lp1_ip && ip4.dst==$lp2_ip && + tcp && tcp.flags==2 && tcp.src==4361 && tcp.dst==81" + as hv ovs-appctl -t ovn-controller inject-pkt "$packet" + ++# Send packet that should be dropped without logging in the eggress pipeline. ++packet="inport==\"lp1\" && eth.src==$lp1_mac && eth.dst==$lp2_mac && ++ ip4 && ip.ttl==64 && ip4.src==$lp1_ip && ip4.dst==$lp2_ip && ++ tcp && tcp.flags==2 && tcp.src==4360 && tcp.dst==180" ++as hv ovs-appctl -t ovn-controller inject-pkt "$packet" ++ ++# Send packet that should be dropped with logging in the egress pipeline. ++packet="inport==\"lp1\" && eth.src==$lp1_mac && eth.dst==$lp2_mac && ++ ip4 && ip.ttl==64 && ip4.src==$lp1_ip && ip4.dst==$lp2_ip && ++ tcp && tcp.flags==2 && tcp.src==4361 && tcp.dst==181" ++as hv ovs-appctl -t ovn-controller inject-pkt "$packet" ++ + # Send packet that should be allowed without logging. + packet="inport==\"lp1\" && eth.src==$lp1_mac && eth.dst==$lp2_mac && + ip4 && ip.ttl==64 && ip4.src==$lp1_ip && ip4.dst==$lp2_ip && +@@ -9016,25 +9080,41 @@ packet="inport==\"lp1\" && eth.src==$lp1_mac && eth.dst==$lp2_mac && + tcp && tcp.flags==2 && tcp.src==4365 && tcp.dst==85" + as hv ovs-appctl -t ovn-controller inject-pkt "$packet" + +-# Send packet that should be rejected without logging. ++# Send packet that should be rejected without logging in the ingress pipeline. + packet="inport==\"lp1\" && eth.src==$lp1_mac && eth.dst==$lp2_mac && + ip4 && ip.ttl==64 && ip4.src==$lp1_ip && ip4.dst==$lp2_ip && + tcp && tcp.flags==2 && tcp.src==4366 && tcp.dst==86" + as hv ovs-appctl -t ovn-controller inject-pkt "$packet" + +-# Send packet that should be rejected with logging. ++# Send packet that should be rejected with logging in the ingress pipeline. + packet="inport==\"lp1\" && eth.src==$lp1_mac && eth.dst==$lp2_mac && + ip4 && ip.ttl==64 && ip4.src==$lp1_ip && ip4.dst==$lp2_ip && + tcp && tcp.flags==2 && tcp.src==4367 && tcp.dst==87" + as hv ovs-appctl -t ovn-controller inject-pkt "$packet" + +-OVS_WAIT_UNTIL([ test 4 = $(grep -c 'acl_log' hv/ovn-controller.log) ]) ++# Send packet that should be rejected without logging in the egress pipeline. ++packet="inport==\"lp1\" && eth.src==$lp1_mac && eth.dst==$lp2_mac && ++ ip4 && ip.ttl==64 && ip4.src==$lp1_ip && ip4.dst==$lp2_ip && ++ tcp && tcp.flags==2 && tcp.src==4366 && tcp.dst==186" ++as hv ovs-appctl -t ovn-controller inject-pkt "$packet" ++ ++# Send packet that should be rejected with logging in the egress pipeline. ++packet="inport==\"lp1\" && eth.src==$lp1_mac && eth.dst==$lp2_mac && ++ ip4 && ip.ttl==64 && ip4.src==$lp1_ip && ip4.dst==$lp2_ip && ++ tcp && tcp.flags==2 && tcp.src==4367 && tcp.dst==187" ++as hv ovs-appctl -t ovn-controller inject-pkt "$packet" ++ ++OVS_WAIT_UNTIL([ test 8 = $(grep -c 'acl_log' hv/ovn-controller.log) ]) + + AT_CHECK([grep 'acl_log' hv/ovn-controller.log | sed 's/.*name=/name=/'], [0], [dnl +-name="drop-flow", verdict=drop, severity=alert: tcp,vlan_tci=0x0000,dl_src=f0:00:00:00:00:01,dl_dst=f0:00:00:00:00:02,nw_src=192.168.1.2,nw_dst=192.168.1.3,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=4361,tp_dst=81,tcp_flags=syn +-name="allow-flow", verdict=allow, severity=info: tcp,vlan_tci=0x0000,dl_src=f0:00:00:00:00:01,dl_dst=f0:00:00:00:00:02,nw_src=192.168.1.2,nw_dst=192.168.1.3,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=4363,tp_dst=83,tcp_flags=syn +-name="", verdict=allow, severity=info: tcp,vlan_tci=0x0000,dl_src=f0:00:00:00:00:01,dl_dst=f0:00:00:00:00:02,nw_src=192.168.1.2,nw_dst=192.168.1.3,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=4365,tp_dst=85,tcp_flags=syn +-name="reject-flow", verdict=reject, severity=alert: tcp,vlan_tci=0x0000,dl_src=f0:00:00:00:00:01,dl_dst=f0:00:00:00:00:02,nw_src=192.168.1.2,nw_dst=192.168.1.3,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=4367,tp_dst=87,tcp_flags=syn ++name="drop-flow", verdict=drop, severity=alert, direction=from-lport: tcp,vlan_tci=0x0000,dl_src=f0:00:00:00:00:01,dl_dst=f0:00:00:00:00:02,nw_src=192.168.1.2,nw_dst=192.168.1.3,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=4361,tp_dst=81,tcp_flags=syn ++name="drop-flow", verdict=drop, severity=alert, direction=to-lport: tcp,vlan_tci=0x0000,dl_src=f0:00:00:00:00:01,dl_dst=f0:00:00:00:00:02,nw_src=192.168.1.2,nw_dst=192.168.1.3,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=4361,tp_dst=181,tcp_flags=syn ++name="allow-flow", verdict=allow, severity=info, direction=from-lport: tcp,vlan_tci=0x0000,dl_src=f0:00:00:00:00:01,dl_dst=f0:00:00:00:00:02,nw_src=192.168.1.2,nw_dst=192.168.1.3,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=4363,tp_dst=83,tcp_flags=syn ++name="allow-flow", verdict=allow, severity=info, direction=to-lport: tcp,vlan_tci=0x0000,dl_src=f0:00:00:00:00:01,dl_dst=f0:00:00:00:00:02,nw_src=192.168.1.2,nw_dst=192.168.1.3,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=4363,tp_dst=83,tcp_flags=syn ++name="", verdict=allow, severity=info, direction=from-lport: tcp,vlan_tci=0x0000,dl_src=f0:00:00:00:00:01,dl_dst=f0:00:00:00:00:02,nw_src=192.168.1.2,nw_dst=192.168.1.3,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=4365,tp_dst=85,tcp_flags=syn ++name="", verdict=allow, severity=info, direction=to-lport: tcp,vlan_tci=0x0000,dl_src=f0:00:00:00:00:01,dl_dst=f0:00:00:00:00:02,nw_src=192.168.1.2,nw_dst=192.168.1.3,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=4365,tp_dst=85,tcp_flags=syn ++name="reject-flow", verdict=reject, severity=alert, direction=from-lport: tcp,vlan_tci=0x0000,dl_src=f0:00:00:00:00:01,dl_dst=f0:00:00:00:00:02,nw_src=192.168.1.2,nw_dst=192.168.1.3,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=4367,tp_dst=87,tcp_flags=syn ++name="reject-flow", verdict=reject, severity=alert, direction=to-lport: tcp,vlan_tci=0x0000,dl_src=f0:00:00:00:00:01,dl_dst=f0:00:00:00:00:02,nw_src=192.168.1.2,nw_dst=192.168.1.3,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=4367,tp_dst=187,tcp_flags=syn + ]) + + OVN_CLEANUP([hv]) +@@ -9323,6 +9403,10 @@ check ovn-nbctl --wait=hv qos-add lsw0 to-lport 1002 'inport=="lp2" && is_chassi + AT_CHECK([as hv ovs-ofctl dump-meters br-int -O OpenFlow13 | grep meter | wc -l], [0], [4 + ]) + ++check ovn-nbctl qos-del lsw0 ++AT_CHECK([as hv ovs-ofctl dump-meters br-int -O OpenFlow13 | grep meter | wc -l], [0], [0 ++]) ++ + OVN_CLEANUP([hv]) + AT_CLEANUP + ]) +@@ -9658,8 +9742,8 @@ as hv1 ovs-appctl netdev-dummy/receive vm1 $packet + + # expected packet at foo2 packet=${dst_mac}${src_mac}8100000208004500001c0000000040110000${src_ip}${dst_ip}0035111100080000 -echo $packet > expected -OVN_CHECK_PACKETS([hv2/vm2-tx.pcap], [expected]) @@ -595,7 +4242,7 @@ index 9ec62e321..92e284e8a 100644 # Send ip packets between foo1 and bar2 (different switch, different HV) src_mac="f00000010205" -@@ -9673,8 +9673,8 @@ as hv1 ovs-appctl netdev-dummy/receive vm1 $packet +@@ -9673,8 +9757,8 @@ as hv1 ovs-appctl netdev-dummy/receive vm1 $packet src_mac="000000010204" dst_mac="f00000010208" packet=${dst_mac}${src_mac}8100000108004500001c000000003f110100${src_ip}${dst_ip}0035111100080000 @@ -606,7 +4253,7 @@ index 9ec62e321..92e284e8a 100644 # Send ip packets between foo1 and bar1 # (different switch, loopback to same vm but different tag) -@@ -9703,11 +9703,11 @@ as hv1 ovs-appctl netdev-dummy/receive vm1 $packet +@@ -9703,11 +9787,11 @@ as hv1 ovs-appctl netdev-dummy/receive vm1 $packet # expected packet at bar3 packet=${dst_mac}${src_mac}08004500001c0000000040110000${src_ip}${dst_ip}0035111100080000 @@ -621,7 +4268,7 @@ index 9ec62e321..92e284e8a 100644 src_mac="f00000010205" dst_mac="000000010203" src_ip=`ip_to_hex 192 168 1 2` -@@ -9723,7 +9723,7 @@ echo $packet >> expected1 +@@ -9723,7 +9807,7 @@ echo $packet >> expected1 OVN_CHECK_PACKETS([hv1/vm1-tx.pcap], [expected1]) # Send packets from vm1 to bar1. @@ -630,7 +4277,7 @@ index 9ec62e321..92e284e8a 100644 src_mac="f00000010203" dst_mac="000000010202" src_ip=`ip_to_hex 172 16 1 2` -@@ -9739,6 +9739,7 @@ echo $packet >> expected1 +@@ -9739,6 +9823,7 @@ echo $packet >> expected1 OVN_CHECK_PACKETS([hv1/vm1-tx.pcap], [expected1]) # Send broadcast packet from foo1. foo1 should not receive the same packet. @@ -638,7 +4285,7 @@ index 9ec62e321..92e284e8a 100644 src_mac="f00000010205" dst_mac="ffffffffffff" src_ip=`ip_to_hex 192 168 1 2` -@@ -9749,6 +9750,11 @@ as hv1 ovs-appctl netdev-dummy/receive vm1 $packet +@@ -9749,6 +9834,11 @@ as hv1 ovs-appctl netdev-dummy/receive vm1 $packet # expected packet at VM1 OVN_CHECK_PACKETS([hv1/vm1-tx.pcap], [expected1]) @@ -650,3 +4297,1230 @@ index 9ec62e321..92e284e8a 100644 # Test binding of parent and container ports. ovn-nbctl lsp-set-options vm1 requested-chassis=foo +@@ -11896,9 +11986,10 @@ options:rxq_pcap=${pcap_file}-rx.pcap + OVS_WAIT_UNTIL([test 1 = `as hv1 ovs-ofctl dump-flows br-int | grep -c "ipv6_dst=ff02::2,nw_ttl=255,icmp_type=133,icmp_code=0"`]) + + # This shell function sends a Router Solicitation packet. +-# test_ipv6_ra INPORT SRC_MAC SRC_LLA ADDR_MODE MTU RA_PREFIX_OPT ++# test_ipv6_ra INPORT SRC_MAC SRC_LLA ADDR_MODE MTU RA_PREFIX_OPT RDNSS DNSSL ROUTE_INFO + test_ipv6_ra() { + local inport=$1 src_mac=$2 src_lla=$3 addr_mode=$4 mtu=$5 prefix_opt=$6 ++ local rdnss=$7 dnssl=$8 route_info=$9 + local request=333300000002${src_mac}86dd6000000000103aff${src_lla}ff02000000000000000000000000000285000efc000000000101${src_mac} + + local len=24 +@@ -11908,6 +11999,18 @@ test_ipv6_ra() { + mtu_opt=05010000${mtu} + fi + ++ if test ${#rdnss} != 0; then ++ len=`expr $len + ${#rdnss} / 2` ++ fi ++ ++ if test ${#dnssl} != 0; then ++ len=`expr $len + ${#dnssl} / 2` ++ fi ++ ++ if test ${#route_info} != 0; then ++ len=`expr $len + ${#route_info} / 2` ++ fi ++ + if test ${#prefix_opt} != 0; then + prefix_opt=${prefix_opt}fdad1234567800000000000000000000 + len=`expr $len + ${#prefix_opt} / 2` +@@ -11916,7 +12019,7 @@ test_ipv6_ra() { + len=$(printf "%x" $len) + local lrp_mac=fa163e000001 + local lrp_lla=fe80000000000000f8163efffe000001 +- local reply=${src_mac}${lrp_mac}86dd6000000000${len}3aff${lrp_lla}${src_lla}8600XXXXff${addr_mode}ffff00000000000000000101${lrp_mac}${mtu_opt}${prefix_opt} ++ local reply=${src_mac}${lrp_mac}86dd6000000000${len}3aff${lrp_lla}${src_lla}8600XXXXff${addr_mode}ffff00000000000000000101${lrp_mac}${mtu_opt}${rdnss}${dnssl}${route_info}${prefix_opt} + echo $reply >> $inport.expected + + as hv1 ovs-appctl netdev-dummy/receive hv1-vif${inport} $request +@@ -11954,6 +12057,9 @@ reset_pcap_file hv1-vif3 hv1/vif3 + ovn-nbctl --wait=hv set Logical_Router_Port lrp0 ipv6_ra_configs:mtu=1500 + ovn-nbctl --wait=hv set Logical_Router_Port lrp0 ipv6_ra_configs:send_periodic="false" + ovn-nbctl --wait=hv set Logical_Router_Port lrp0 ipv6_ra_configs:router_preference="LOW" ++ovn-nbctl --wait=hv set Logical_Router_port lrp0 ipv6_ra_configs:rdnss=1000::11 ++ovn-nbctl --wait=hv set Logical_Router_port lrp0 ipv6_ra_configs:dnssl=aa.bb.cc ++ovn-nbctl --wait=hv set Logical_Router_port lrp0 ipv6_ra_configs:route_info=HIGH-1001::11/48,LOW-1002::11/96 + + # Make sure that ovn-controller has installed the corresponding OF Flow. + OVS_WAIT_UNTIL([test 1 = `as hv1 ovs-ofctl dump-flows br-int | grep -c "ipv6_dst=ff02::2,nw_ttl=255,icmp_type=133,icmp_code=0"`]) +@@ -11964,8 +12070,11 @@ default_prefix_option_config=030440c0ffffffffffffffff00000000 + src_mac=fa163e000003 + src_lla=fe80000000000000f8163efffe000003 + mtu=000005dc ++rdnss=19030000ffffffff10000000000000000000000000000011 ++dnssl=1f030000ffffffff02616102626202636300000000000000 ++route_info=18023008ffffffff100100000000000018036018ffffffff10020000000000000000000000000000 + +-test_ipv6_ra 2 $src_mac $src_lla $addr_mode $mtu $default_prefix_option_config ++test_ipv6_ra 2 $src_mac $src_lla $addr_mode $mtu $default_prefix_option_config $rdnss $dnssl $route_info + + # NXT_RESUME should be 2. + OVS_WAIT_UNTIL([test 2 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`]) +@@ -11987,6 +12096,8 @@ reset_pcap_file hv1-vif3 hv1/vif3 + # Set the address mode to dhcpv6_stateful, router_preference to HIGH + ovn-nbctl --wait=hv set Logical_Router_Port lrp0 ipv6_ra_configs:address_mode=dhcpv6_stateful + ovn-nbctl --wait=hv set Logical_Router_Port lrp0 ipv6_ra_configs:router_preference="HIGH" ++ovn-nbctl --wait=hv remove Logical_Router_Port lrp0 ipv6_ra_configs rdnss ++ovn-nbctl --wait=hv remove Logical_Router_Port lrp0 ipv6_ra_configs route_info + # Make sure that ovn-controller has installed the corresponding OF Flow. + OVS_WAIT_UNTIL([test 1 = `as hv1 ovs-ofctl dump-flows br-int | grep -c "ipv6_dst=ff02::2,nw_ttl=255,icmp_type=133,icmp_code=0"`]) + +@@ -11997,7 +12108,7 @@ src_mac=fa163e000004 + src_lla=fe80000000000000f8163efffe000004 + mtu=000005dc + +-test_ipv6_ra 3 $src_mac $src_lla $addr_mode $mtu $default_prefix_option_config ++test_ipv6_ra 3 $src_mac $src_lla $addr_mode $mtu $default_prefix_option_config "" $dnssl + + # NXT_RESUME should be 3. + OVS_WAIT_UNTIL([test 3 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`]) +@@ -12019,6 +12130,7 @@ reset_pcap_file hv1-vif3 hv1/vif3 + # Set the address mode to dhcpv6_stateless, reset router preference to default + ovn-nbctl --wait=hv set Logical_Router_Port lrp0 ipv6_ra_configs:address_mode=dhcpv6_stateless + ovn-nbctl --wait=hv set Logical_Router_Port lrp0 ipv6_ra_configs:router_preference="MEDIUM" ++ovn-nbctl --wait=hv remove Logical_Router_Port lrp0 ipv6_ra_configs dnssl + # Make sure that ovn-controller has installed the corresponding OF Flow. + OVS_WAIT_UNTIL([test 1 = `as hv1 ovs-ofctl dump-flows br-int | grep -c "ipv6_dst=ff02::2,nw_ttl=255,icmp_type=133,icmp_code=0"`]) + +@@ -14290,7 +14402,7 @@ ovn-sbctl dump-flows sw0 > sw0-flows + AT_CAPTURE_FILE([sw0-flows]) + + AT_CHECK([grep -E 'ls_(in|out)_acl' sw0-flows |grep reject| sed 's/table=../table=??/' | sort], [0], [dnl +- table=??(ls_out_acl ), priority=2002 , match=(ip), action=(reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=22); };) ++ table=??(ls_out_acl ), priority=2002 , match=(ip), action=(reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=24); };) + ]) + + +@@ -15135,6 +15247,92 @@ OVN_CLEANUP([hv1]) + AT_CLEANUP + ]) + ++# This test ensures that the incremental flow installation works well when ++# handling update->delete->add/update for the same OVS flow. ++OVN_FOR_EACH_NORTHD([ ++AT_SETUP([ACL conjunction append and reprocess]) ++ovn_start ++ ++net_add n1 ++sim_add hv1 ++as hv1 ++check ovs-vsctl add-br br-phys ++ovn_attach n1 br-phys 192.168.0.1 ++ ++# Setup the desired state: ++# - Two ACLs, each matches its own port-group (pg1 & pg2), and matches the same ++# set of IP addresses. ++# - pg1 includes p1, p2, p3 ++# - pg2 includes p4, p5 ++check ovn-nbctl ls-add sw ++check ovn-nbctl lsp-add sw p1 -- lsp-set-addresses p1 "00:00:00:00:00:02 192.168.0.2" ++check ovn-nbctl lsp-add sw p2 -- lsp-set-addresses p2 "00:00:00:00:00:03 192.168.0.3" ++check ovn-nbctl lsp-add sw p3 -- lsp-set-addresses p3 "00:00:00:00:00:04 192.168.0.4" ++check ovn-nbctl lsp-add sw p4 -- lsp-set-addresses p4 "00:00:00:00:00:05 192.168.0.5" ++check ovn-nbctl lsp-add sw p5 -- lsp-set-addresses p5 "00:00:00:00:00:06 192.168.0.6" ++check ovn-nbctl pg-add pg1 p1 p2 p3 ++check ovn-nbctl pg-add pg2 p4 p5 ++check ovs-vsctl add-port br-int p1 -- set Interface p1 external_ids:iface-id=p1 ++check ovs-vsctl add-port br-int p2 -- set Interface p2 external_ids:iface-id=p2 ++check ovs-vsctl add-port br-int p3 -- set Interface p3 external_ids:iface-id=p3 ++check ovs-vsctl add-port br-int p4 -- set Interface p4 external_ids:iface-id=p4 ++check ovs-vsctl add-port br-int p5 -- set Interface p5 external_ids:iface-id=p5 ++check ovn-nbctl acl-add pg1 to-lport 1000 "outport==@pg1 && ip4 && ip4.src == {10.0.0.1, 10.0.0.2}" allow ++check ovn-nbctl acl-add pg2 to-lport 1000 "outport==@pg2 && ip4 && ip4.src == {10.0.0.1, 10.0.0.2}" allow ++check ovn-nbctl --wait=hv sync ++ ++# Now we should have two flows with combined conjunctions. ++OVS_WAIT_UNTIL([test 2 = `as hv1 ovs-ofctl dump-flows br-int | \ ++grep conjunction.*conjunction | wc -l`]) ++ ++ ++# Test the scenario 10 times to give enough chance to hit the ++# "update->delete->add/update" scenario, because we can't decide the order of ++# change handling inside ovn-controller. ++for i in $(seq 10); do ++# Unbind the p3 and p5, the combined conjunctions should be gone. ++ovs-vsctl del-port br-int p3 ++ovs-vsctl del-port br-int p5 ++OVS_WAIT_UNTIL([test 0 = `as hv1 ovs-ofctl dump-flows br-int | \ ++grep conjunction.*conjunction | wc -l`]) ++ ++# Delete and re-add the ACLs, just to bring some randomness in the lflow ++# processing order, so that there is a chance that the order of adding and ++# appending are the same before & after the flow deletion, so that the ++# generated combined conjunctions are the same before & after the flow ++# deletion. (If the order is different, the combined conjunctions order is ++# different and the action comparison would fail, so won't trigger the tracked ++# flow merging. We want to make sure that we test the merging scenario) ++ovn-nbctl acl-del pg1 to-lport 1000 "outport==@pg1 && ip4 && ip4.src == {10.0.0.1, 10.0.0.2}" ++ovn-nbctl acl-del pg2 to-lport 1000 "outport==@pg2 && ip4 && ip4.src == {10.0.0.1, 10.0.0.2}" ++ovn-nbctl acl-add pg1 to-lport 1000 "outport==@pg1 && ip4 && ip4.src == {10.0.0.1, 10.0.0.2}" allow ++ovn-nbctl acl-add pg2 to-lport 1000 "outport==@pg2 && ip4 && ip4.src == {10.0.0.1, 10.0.0.2}" allow ++ovn-nbctl --wait=hv sync ++ ++# Now re-bind p3 and p5 in the same transaction, so that pg1 and pg2 update are ++# handled in the same I-P engine run. The order of pg1 and pg2 can be random. ++# If the order is pg2 -> pg1, then it should trigger the OVS flow ++# "update->delete->add/update" scenario: ++# 1) when pg2 update is handled, the ACL-2 would append conjunctions to ++# the conjunction flows of ACL-1 ++# 2) when pg1 update is handled, it would flood remove flows of both ACL-1 and ++# ACL-2, including the "appended" conjunction flows. And then reprocess ++# ACL-1 and ACL-2 would re-add and re-append the conjunction flows with ++# combined conjunctions. ++ovs-vsctl add-port br-int p3 -- set Interface p3 external_ids:iface-id=p3 -- \ ++ add-port br-int p5 -- set Interface p5 external_ids:iface-id=p5 ++ovn-nbctl --wait=hv sync ++ ++# Now making sure we end up with two combined conjunctions. ++OVS_WAIT_UNTIL([test 2 = `as hv1 ovs-ofctl dump-flows br-int | \ ++grep conjunction.*conjunction | wc -l`]) ++ ++done ++ ++OVN_CLEANUP([hv1]) ++AT_CLEANUP ++]) ++ + OVN_FOR_EACH_NORTHD([ + AT_SETUP([Superseding ACLs with conjunction]) + ovn_start +@@ -16181,17 +16379,17 @@ ovs-vsctl set open . external-ids:ovn-bridge-mappings=phys:br-phys + AT_CHECK([ovn-sbctl dump-flows ls1 | grep "offerip = 10.0.0.6" | \ + wc -l], [0], [0 + ]) +-AT_CHECK([as hv1 ovs-ofctl dump-flows br-int | grep table=23 | \ ++AT_CHECK([as hv1 ovs-ofctl dump-flows br-int | grep table=25 | \ + grep controller | grep "0a.00.00.06" | wc -l], [0], [0 + ]) +-AT_CHECK([as hv2 ovs-ofctl dump-flows br-int | grep table=23 | \ ++AT_CHECK([as hv2 ovs-ofctl dump-flows br-int | grep table=25 | \ + grep controller | grep "0a.00.00.06" | wc -l], [0], [0 + ]) +-AT_CHECK([as hv1 ovs-ofctl dump-flows br-int | grep table=23 | \ ++AT_CHECK([as hv1 ovs-ofctl dump-flows br-int | grep table=25 | \ + grep controller | grep tp_src=546 | grep \ + "ae.70.00.00.00.00.00.00.00.00.00.00.00.00.00.06" | wc -l], [0], [0 + ]) +-AT_CHECK([as hv2 ovs-ofctl dump-flows br-int | grep table=23 | \ ++AT_CHECK([as hv2 ovs-ofctl dump-flows br-int | grep table=25 | \ + grep controller | grep tp_src=546 | grep \ + "ae.70.00.00.00.00.00.00.00.00.00.00.00.00.00.06" | wc -l], [0], [0 + ]) +@@ -16799,7 +16997,7 @@ wait_for_ports_up ls1-lp_ext1 + # There should be a flow in hv2 to drop traffic from ls1-lp_ext1 destined + # to router mac. + AT_CHECK([as hv2 ovs-ofctl dump-flows br-int \ +-table=29,dl_src=f0:00:00:00:00:03,dl_dst=a0:10:00:00:00:01 | \ ++table=31,dl_src=f0:00:00:00:00:03,dl_dst=a0:10:00:00:00:01 | \ + grep -c "actions=drop"], [0], [1 + ]) + +@@ -18476,7 +18674,7 @@ check_row_count Port_Binding 1 logical_port=sw0-vir virtual_parent=sw0-p1 + wait_for_ports_up sw0-vir + check ovn-nbctl --wait=hv sync + AT_CHECK([test 2 = `cat hv1/ovn-controller.log | grep "pinctrl received packet-in" | \ +-grep opcode=BIND_VPORT | grep OF_Table_ID=24 | wc -l`]) ++grep opcode=BIND_VPORT | grep OF_Table_ID=26 | wc -l`]) + + wait_row_count Port_Binding 1 logical_port=sw0-vir6 chassis=$hv1_ch_uuid + check_row_count Port_Binding 1 logical_port=sw0-vir6 virtual_parent=sw0-p1 +@@ -18525,7 +18723,7 @@ eth_dst=00000000ff01 + ip_src=$(ip_to_hex 10 0 0 10) + ip_dst=$(ip_to_hex 172 168 0 101) + send_icmp_packet 1 1 $eth_src $eth_dst $ip_src $ip_dst c4c9 0000000000000000000000 +-AT_CHECK([as hv1 ovs-ofctl dump-flows br-int | awk '/table=26, n_packets=1, n_bytes=45/{print $7" "$8}'],[0],[dnl ++AT_CHECK([as hv1 ovs-ofctl dump-flows br-int metadata=0x$lr0_dp_key | awk '/table=26, n_packets=1, n_bytes=45/{print $7" "$8}'],[0],[dnl + priority=80,ip,reg15=0x3,metadata=0x3,nw_src=10.0.0.10 actions=drop + ]) + +@@ -21964,7 +22162,7 @@ OVS_WAIT_FOR_OUTPUT( + (ls_in_pre_stateful ), priority=120 , match=(reg0[[2]] == 1 && ip6 && sctp), action=(xxreg1 = ip6.dst; reg2[[0..15]] = sctp.dst; ct_lb;) + (ls_in_pre_stateful ), priority=120 , match=(reg0[[2]] == 1 && ip6 && tcp), action=(xxreg1 = ip6.dst; reg2[[0..15]] = tcp.dst; ct_lb;) + (ls_in_pre_stateful ), priority=120 , match=(reg0[[2]] == 1 && ip6 && udp), action=(xxreg1 = ip6.dst; reg2[[0..15]] = udp.dst; ct_lb;) +- (ls_in_stateful ), priority=120 , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(reg1 = 10.0.0.10; reg2[[0..15]] = 80; ct_lb(backends=10.0.0.3:80,20.0.0.3:80; hash_fields="ip_dst,ip_src,tcp_dst,tcp_src");) ++ (ls_in_lb ), priority=120 , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(reg0[[1]] = 0; reg1 = 10.0.0.10; reg2[[0..15]] = 80; ct_lb(backends=10.0.0.3:80,20.0.0.3:80; hash_fields="ip_dst,ip_src,tcp_dst,tcp_src");) + ]) + + AT_CAPTURE_FILE([sbflows2]) +@@ -22006,7 +22204,7 @@ ovn-sbctl dump-flows sw0 > sbflows3 + AT_CHECK( + [grep "ip4.dst == 10.0.0.10 && tcp.dst == 80" sbflows3 | grep priority=120 |\ + sed 's/table=../table=??/'], [0], +- [ table=??(ls_in_stateful ), priority=120 , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(drop;) ++ [ table=??(ls_in_lb ), priority=120 , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(drop;) + ]) + + AT_CAPTURE_FILE([sbflows4]) +@@ -28539,6 +28737,10 @@ done + # Make sure all the above was performed with I-P (no recompute) + AT_CHECK([test $(ovn-appctl -t ovn-controller coverage/read-counter lflow_run) == $lflow_run]) + ++# Make sure there is no unexpected conjunction ID free and reallocation ++AT_CHECK([ovn-appctl -t ovn-controller coverage/read-counter lflow_conj_free_unexpected], [0], [0 ++]) ++ + OVN_CLEANUP([hv1]) + AT_CLEANUP + ]) +@@ -28726,8 +28928,8 @@ options arp_proxy='"169.254.239.254 169.254.239.2"' + ovn-sbctl dump-flows > sbflows + AT_CAPTURE_FILE([sbflows]) + +-AT_CHECK([ovn-sbctl dump-flows | grep ls_in_arp_rsp | grep "169.254.239.2"], [0], [dnl +- table=16(ls_in_arp_rsp ), priority=50 , match=(arp.op == 1 && arp.tpa == {169.254.239.254,169.254.239.2}), dnl ++AT_CHECK([ovn-sbctl dump-flows | grep ls_in_arp_rsp | grep "169.254.239.2" | sed 's/table=../table=??/'], [0], [dnl ++ table=??(ls_in_arp_rsp ), priority=50 , match=(arp.op == 1 && arp.tpa == {169.254.239.254,169.254.239.2}), dnl + action=(eth.dst = eth.src; eth.src = 00:00:00:01:02:f1; arp.op = 2; /* ARP reply */ arp.tha = arp.sha; arp.sha = 00:00:00:01:02:f1; arp.tpa <-> arp.spa; outport = inport; flags.loopback = 1; output;) + ]) + +@@ -29208,26 +29410,26 @@ done + check ovn-nbctl --wait=hv sync + + # hv0 should see flows for lsp1 but not lsp2 +-AT_CHECK([as hv1 ovs-ofctl dump-flows br-int table=24 | grep 10.0.1.2], [0], [ignore]) +-AT_CHECK([as hv1 ovs-ofctl dump-flows br-int table=24 | grep 10.0.2.2], [1]) ++AT_CHECK([as hv1 ovs-ofctl dump-flows br-int table=26 | grep 10.0.1.2], [0], [ignore]) ++AT_CHECK([as hv1 ovs-ofctl dump-flows br-int table=26 | grep 10.0.2.2], [1]) + # hv2 should see flows for lsp2 but not lsp1 +-AT_CHECK([as hv2 ovs-ofctl dump-flows br-int table=24 | grep 10.0.2.2], [0], [ignore]) +-AT_CHECK([as hv2 ovs-ofctl dump-flows br-int table=24 | grep 10.0.1.2], [1]) ++AT_CHECK([as hv2 ovs-ofctl dump-flows br-int table=26 | grep 10.0.2.2], [0], [ignore]) ++AT_CHECK([as hv2 ovs-ofctl dump-flows br-int table=26 | grep 10.0.1.2], [1]) + + # Change lrp_lr_ls1 to a regular lrp, hv2 should see flows for lsp1 + check ovn-nbctl --wait=hv lrp-del-gateway-chassis lrp_lr_ls1 hv1 +-AT_CHECK([as hv2 ovs-ofctl dump-flows br-int table=24 | grep 10.0.1.2], [0], [ignore]) ++AT_CHECK([as hv2 ovs-ofctl dump-flows br-int table=26 | grep 10.0.1.2], [0], [ignore]) + + # Change it back, and trigger recompute to make sure extra flows are removed + # from hv2 (recompute is needed because currently I-P adds local datapaths but + # doesn't remove.) + check ovn-nbctl --wait=hv lrp-set-gateway-chassis lrp_lr_ls1 hv1 1 + as hv2 check ovn-appctl -t ovn-controller recompute +-AT_CHECK([as hv2 ovs-ofctl dump-flows br-int table=24 | grep 10.0.1.2], [1]) ++AT_CHECK([as hv2 ovs-ofctl dump-flows br-int table=26 | grep 10.0.1.2], [1]) + + # Enable dnat_and_snat on lr, and now hv2 should see flows for lsp1. + AT_CHECK([ovn-nbctl --wait=hv lr-nat-add lr dnat_and_snat 192.168.0.1 10.0.1.3 lsp1 f0:00:00:00:00:03]) +-AT_CHECK([as hv2 ovs-ofctl dump-flows br-int table=24 | grep 10.0.1.2], [0], [ignore]) ++AT_CHECK([as hv2 ovs-ofctl dump-flows br-int table=26 | grep 10.0.1.2], [0], [ignore]) + + OVN_CLEANUP([hv1],[hv2]) + AT_CLEANUP +@@ -29698,3 +29900,76 @@ OVS_WAIT_UNTIL([ + OVN_CLEANUP([hv1],[hv2]) + AT_CLEANUP + ]) ++ ++OVN_FOR_EACH_NORTHD([ ++AT_SETUP([ovn-controller - check meters update]) ++AT_KEYWORDS([meters-update]) ++ ++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 ++ ++check ovn-nbctl ls-add sw0 ++check ovn-nbctl lsp-add sw0 lsp ++ ++as hv1 ovs-vsctl \ ++ -- add-port br-int vif1 \ ++ -- set Interface vif1 external_ids:iface-id=lsp ++ ++# Wait for port to be bound. ++wait_row_count Chassis 1 name=hv1 ++ch=$(fetch_column Chassis _uuid name=hv1) ++wait_row_count Port_Binding 1 logical_port=lsp chassis=$ch ++ ++# Add a new meter ++check ovn-nbctl --event lb-add lb0 192.168.1.100:80 "" ++check ovn-nbctl ls-lb-add sw0 lb0 ++check ovn-nbctl meter-add meter0 drop 10 pktps ++ovn-nbctl --wait=hv ls-copp-add sw0 event-elb meter0 ++check ovn-nbctl --wait=hv sync ++ ++AT_CHECK([as hv1 ovs-ofctl -OOpenFlow15 dump-meters br-int | grep -q rate=10], [0]) ++AT_CHECK([as hv1 ovs-ofctl dump-flows br-int | grep -q meter_id=1], [0]) ++ ++# Update existing meter ++check ovn-nbctl --may-exist meter-add meter0 drop 20 pktps ++check ovn-nbctl --wait=hv sync ++ ++AT_CHECK([as hv1 ovs-ofctl -OOpenFlow15 dump-meters br-int | grep -q rate=20], [0]) ++ ++# Add a new meter ++check ovn-nbctl meter-add meter1 drop 30 pktps ++check ovn-nbctl --log --severity=alert --meter=meter1 \ ++ --name=dns acl-add sw0 to-lport 1000 'udp.dst == 53' drop ++check ovn-nbctl --wait=hv sync ++AT_CHECK([as hv1 ovs-ofctl -OOpenFlow15 dump-meters br-int | grep -q rate=30], [0]) ++AT_CHECK([as hv1 ovs-ofctl dump-flows br-int | grep -q meter_id=2], [0]) ++ ++# Remove meter0 ++check ovn-nbctl meter-del meter0 ++check ovn-nbctl --wait=hv sync ++AT_CHECK([as hv1 ovs-ofctl -OOpenFlow15 dump-meters br-int | grep -q rate=10], [1]) ++AT_CHECK([as hv1 ovs-ofctl dump-flows br-int | grep -q meter_id=1], [1]) ++ ++check ovn-nbctl meter-del meter1 ++check ovn-nbctl --wait=hv sync ++AT_CHECK([as hv1 ovs-ofctl -OOpenFlow15 dump-meters br-int | grep -q rate=30], [1]) ++ ++# create meters in the opposite order ++check ovn-nbctl --log --severity=alert --meter=meter2 \ ++ --name=dns acl-add sw0 to-lport 1000 'tcp.dst == 80' drop ++check ovn-nbctl meter-add meter2 drop 100 pktps ++check ovn-nbctl --wait=hv sync ++AT_CHECK([as hv1 ovs-ofctl -OOpenFlow15 dump-meters br-int | grep -q rate=100], [0]) ++AT_CHECK([as hv1 ovs-ofctl dump-flows br-int | grep -q meter_id=1], [0]) ++ ++check ovn-nbctl meter-del meter2 ++AT_CHECK([as hv1 ovs-ofctl -OOpenFlow15 dump-meters br-int | grep -q rate=100], [1]) ++ ++OVN_CLEANUP([hv1]) ++AT_CLEANUP ++]) +diff --git a/tests/system-ovn.at b/tests/system-ovn.at +index 7f6cb32dc..5f41722d9 100644 +--- a/tests/system-ovn.at ++++ b/tests/system-ovn.at +@@ -1448,6 +1448,32 @@ OVS_START_L7([bar1], [http]) + OVS_START_L7([bar2], [http]) + OVS_START_L7([bar3], [http]) + ++# Add ACLs (after lb) to drop the traffic if destined to backend ips. ++check ovn-nbctl --apply-after-lb acl-add foo from-lport 1002 "ip4 && ip4.dst == {172.16.1.2,172.16.1.3,172.16.1.4} && ct.new" drop ++check ovn-nbctl --wait=hv sync ++ ++AT_CHECK([ip netns exec foo1 wget 30.0.0.1 -t 3 -T 1], [4], [ignore], [ignore]) ++ ++AT_CHECK([ovs-appctl dpctl/flush-conntrack]) ++ ++# Clear the apply-after-lb option. The traffic will be allowed. ++check ovn-nbctl clear acl . options ++ovn-nbctl --wait=hv sync ++ ++OVS_WAIT_FOR_OUTPUT([ ++ for i in `seq 1 20`; do ++ ip netns exec foo1 wget 30.0.0.1 -t 5 -T 1 --retry-connrefused -v -o wget$i.log; ++ done ++ 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=,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=) ++]) ++ ++ovn-nbctl acl-del foo from-lport 1002 "ip4 && ip4.dst == {172.16.1.2,172.16.1.3,172.16.1.4} && ct.new" ++ovn-nbctl --wait=hv sync ++ + dnl Should work with the virtual IP 30.0.0.1 address through NAT + dnl Each server should have at least one connection. + dnl With 20 requests, one server might not receive any connection +@@ -4895,6 +4921,247 @@ aef0::3 udp port 90" | uniq | wc -l) + ]) + + ++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([NORTHD_TYPE]) ++ ++as ++OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d ++/connection dropped.*/d"]) ++ ++AT_CLEANUP ++]) ++ ++OVN_FOR_EACH_NORTHD([ ++AT_SETUP([ACL after lb - reject]) ++AT_SKIP_IF([test $HAVE_NC = no]) ++AT_KEYWORDS([lb]) ++ ++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 ++ ++ovn-nbctl ls-add sw0 ++ ++ovn-nbctl lsp-add sw0 sw0-p1-rej ++ovn-nbctl lsp-set-addresses sw0-p1-rej "50:54:00:00:00:03 10.0.0.3 aef0::3" ++ovn-nbctl lsp-set-port-security sw0-p1-rej "50:54:00:00:00:03 10.0.0.3 aef0::3" ++ ++ovn-nbctl lsp-add sw0 sw0-p2-rej ++ovn-nbctl lsp-set-addresses sw0-p2-rej "50:54:00:00:00:04 10.0.0.4 aef0::4" ++ovn-nbctl lsp-set-port-security sw0-p2-rej "50:54:00:00:00:04 10.0.0.4 aef0::4" ++ ++# Create port group and ACLs for sw0 ports. ++ovn-nbctl pg-add pg0_drop sw0-p1-rej sw0-p2-rej ++ovn-nbctl --apply-after-lb acl-add pg0_drop from-lport 1001 "inport == @pg0_drop && ip" drop ++ovn-nbctl acl-add pg0_drop to-lport 1001 "outport == @pg0_drop && ip" drop ++ ++ovn-nbctl pg-add pg0 sw0-p1-rej sw0-p2-rej ++ovn-nbctl --apply-after-lb acl-add pg0 from-lport 1002 "inport == @pg0 && ip" allow-related ++ovn-nbctl --log --apply-after-lb acl-add pg0 from-lport 1004 "inport == @pg0 && ip && tcp && tcp.dst == 80" reject ++ovn-nbctl --log --apply-after-lb acl-add pg0 from-lport 1004 "inport == @pg0 && ip && udp && udp.dst == 90" reject ++ ++ovn-nbctl acl-add pg0 to-lport 1002 "outport == @pg0 && ip4 && ip4.src == 0.0.0.0/0 && tcp && tcp.dst == 82" allow-related ++ovn-nbctl acl-add pg0 to-lport 1002 "outport == @pg0 && ip4 && ip4.src == 0.0.0.0/0 && udp && udp.dst == 82" allow-related ++ovn-nbctl --log acl-add pg0 to-lport 1004 "inport == @pg0 && ip && tcp && tcp.dst == 84" reject ++ovn-nbctl --log acl-add pg0 to-lport 1004 "inport == @pg0 && ip && udp && udp.dst == 94" reject ++ ++ovn-nbctl ls-add sw1 ++ovn-nbctl lsp-add sw1 sw1-p1-rej ++ovn-nbctl lsp-set-addresses sw1-p1-rej "40:54:00:00:00:03 20.0.0.3" ++ovn-nbctl lsp-set-port-security sw1-p1-rej "40:54:00:00:00:03 20.0.0.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 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 ++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_POPULATE_ARP ++ovn-nbctl --wait=hv sync ++ ++ADD_NAMESPACES(sw0-p1-rej) ++ADD_VETH(sw0-p1-rej, sw0-p1-rej, br-int, "10.0.0.3/24", "50:54:00:00:00:03", \ ++ "10.0.0.1") ++ ++ADD_NAMESPACES(sw0-p2-rej) ++ADD_VETH(sw0-p2-rej, sw0-p2-rej, br-int, "10.0.0.4/24", "50:54:00:00:00:04", \ ++ "10.0.0.1") ++ ++NS_CHECK_EXEC([sw0-p1-rej], [ip a a aef0::3/64 dev sw0-p1-rej], [0]) ++NS_CHECK_EXEC([sw0-p2-rej], [ip a a aef0::4/64 dev sw0-p2-rej], [0]) ++ ++ADD_NAMESPACES(sw1-p1-rej) ++ADD_VETH(sw1-p1-rej, sw1-p1-rej, br-int, "20.0.0.3/24", "40:54:00:00:00:03", \ ++ "20.0.0.1") ++ ++sleep 1 ++ ++# Capture packets in sw0-p1-rej. ++NS_CHECK_EXEC([sw0-p1-rej], [tcpdump -nn -c 4 -i sw0-p1-rej tcp > sw0-p1-rej-ip4.pcap &], [0]) ++ ++sleep 1 ++ ++OVS_WAIT_UNTIL([ ++ ip netns exec sw0-p1-rej nc -vz 10.0.0.4 80 2>&1 | grep -i 'connection refused' ++]) ++ ++# Now send traffic to port 84 ++OVS_WAIT_UNTIL([ ++ ip netns exec sw0-p1-rej nc -vz 10.0.0.4 84 2>&1 | grep -i 'connection refused' ++]) ++ ++OVS_WAIT_UNTIL([ ++ n_pkt=$(ovs-ofctl dump-flows br-int table=44 | grep -v n_packets=0 | \ ++grep controller | grep tp_dst=84 -c) ++ test $n_pkt -eq 1 ++]) ++ ++OVS_WAIT_UNTIL([ ++ total=`cat sw0-p1-rej-ip4.pcap | wc -l` ++ echo "total = $total" ++ test "${total}" = "4" ++]) ++ ++# Without this sleep, test case fails intermittently. ++sleep 3 ++ ++NS_CHECK_EXEC([sw0-p2-rej], [tcpdump -nn -c 2 -i sw0-p2-rej tcp port 80 > sw0-p2-rej-ip6.pcap &], [0]) ++ ++sleep 1 ++ ++OVS_WAIT_UNTIL([ ++ ip netns exec sw0-p2-rej nc -vz6 aef0::3 80 2>&1 | grep -i 'connection refused' ++]) ++ ++ ++OVS_WAIT_UNTIL([ ++ total=`cat sw0-p2-rej-ip6.pcap | wc -l` ++ echo "total = $total" ++ test "${total}" = "2" ++]) ++ ++ovn-nbctl --apply-after-lb acl-add sw1 from-lport 1004 "ip" allow-related ++ovn-nbctl acl-add sw1 to-lport 1004 "ip" allow-related ++ovn-nbctl --log acl-add pg0 to-lport 1004 "outport == @pg0 && ip && tcp && tcp.dst == 84" reject ++ ++OVS_WAIT_UNTIL([ ++ ip netns exec sw1-p1-rej nc -vz 10.0.0.4 84 2>&1 | grep -i 'connection refused' ++]) ++ ++# Now test for IPv4 UDP. ++NS_CHECK_EXEC([sw0-p1-rej], [tcpdump -nn -c 1 -i sw0-p1-rej udp port 90 > sw0-p1-rej-udp.pcap &], [0]) ++NS_CHECK_EXEC([sw0-p1-rej], [tcpdump -nn -c 1 -i sw0-p1-rej icmp > sw0-p1-rej-icmp.pcap &], [0]) ++ ++printf '.%.0s' {1..100} > foo ++OVS_WAIT_UNTIL([ ++ ip netns exec sw0-p1-rej nc -u 10.0.0.4 90 < foo ++ c=$(cat sw0-p1-rej-icmp.pcap | grep \ ++"10.0.0.4 > 10.0.0.3: ICMP 10.0.0.4 udp port 90 unreachable" | uniq | wc -l) ++ test $c -eq 1 ++]) ++ ++rm -f *.pcap ++ ++NS_CHECK_EXEC([sw0-p1-rej], [tcpdump -nn -c 1 -i sw0-p1-rej udp port 94 > sw0-p1-rej-udp.pcap &], [0]) ++NS_CHECK_EXEC([sw0-p1-rej], [tcpdump -nn -c 1 -i sw0-p1-rej icmp > sw0-p1-rej-icmp.pcap &], [0]) ++ ++OVS_WAIT_UNTIL([ ++ ip netns exec sw0-p1-rej nc -u 10.0.0.4 94 < foo ++ c=$(cat sw0-p1-rej-icmp.pcap | grep \ ++"10.0.0.4 > 10.0.0.3: ICMP 10.0.0.4 udp port 94 unreachable" | uniq | wc -l) ++ test $c -eq 1 ++]) ++ ++# Now test for IPv6 UDP. ++NS_CHECK_EXEC([sw0-p2-rej], [tcpdump -nn -c 1 -i sw0-p2-rej udp port 90 > sw0-p2-rej-ip6-udp.pcap &], [0]) ++NS_CHECK_EXEC([sw0-p2-rej], [tcpdump -nn -c 1 -i sw0-p2-rej icmp6 > sw0-p2-rej-icmp6.pcap &], [0]) ++ ++OVS_WAIT_UNTIL([ ++ ip netns exec sw0-p2-rej nc -u -6 aef0::3 90 < foo ++ c=$(cat sw0-p2-rej-icmp6.pcap | grep \ ++"IP6 aef0::3 > aef0::4: ICMP6, destination unreachable, unreachable port, \ ++aef0::3 udp port 90" | uniq | wc -l) ++ test $c -eq 1 ++]) ++ ++rm -f *.pcap ++ ++NS_CHECK_EXEC([sw0-p2-rej], [tcpdump -nn -c 1 -i sw0-p2-rej udp port 94 > sw0-p2-rej-ip6-udp.pcap &], [0]) ++NS_CHECK_EXEC([sw0-p2-rej], [tcpdump -nn -c 1 -i sw0-p2-rej icmp6 > sw0-p2-rej-icmp6.pcap &], [0]) ++ ++OVS_WAIT_UNTIL([ ++ ip netns exec sw0-p2-rej nc -u -6 aef0::3 94 < foo ++ c=$(cat sw0-p2-rej-icmp6.pcap | grep \ ++"IP6 aef0::3 > aef0::4: ICMP6, destination unreachable, unreachable port, \ ++aef0::3 udp port 94" | uniq | wc -l) ++ test $c -eq 1 ++]) ++ ++# Delete all the ACLs of pg0 and add the ACL with a generic match with reject action. ++ovn-nbctl pg-del pg0 ++ovn-nbctl pg-add pg0 sw0-p1-rej sw0-p2-rej ++ovn-nbctl --log --apply-after-lb acl-add pg0 from-lport 1004 "inport == @pg0 && ip && (tcp || udp)" reject ++ ++OVS_WAIT_UNTIL([ ++ ip netns exec sw0-p1-rej nc -vz 10.0.0.4 80 2>&1 | grep -i 'connection refused' ++]) ++ ++OVS_WAIT_UNTIL([ ++ ip netns exec sw0-p2-rej nc -vz6 aef0::3 80 2>&1 | grep -i 'connection refused' ++]) ++ ++rm -f *.pcap ++ ++NS_CHECK_EXEC([sw0-p1-rej], [tcpdump -nn -c 1 -i sw0-p1-rej icmp > sw0-p1-rej-icmp.pcap &], [0]) ++ ++printf '.%.0s' {1..100} > foo ++OVS_WAIT_UNTIL([ ++ ip netns exec sw0-p1-rej nc -u 10.0.0.4 90 < foo ++ c=$(cat sw0-p1-rej-icmp.pcap | grep \ ++"10.0.0.4 > 10.0.0.3: ICMP 10.0.0.4 udp port 90 unreachable" | uniq | wc -l) ++ test $c -eq 1 ++]) ++ ++rm -f *.pcap ++# Now test for IPv6 UDP. ++NS_CHECK_EXEC([sw0-p2-rej], [tcpdump -nn -c 1 -i sw0-p2-rej icmp6 > sw0-p2-rej-icmp6.pcap &], [0]) ++ ++OVS_WAIT_UNTIL([ ++ ip netns exec sw0-p2-rej nc -u -6 aef0::3 90 < foo ++ c=$(cat sw0-p2-rej-icmp6.pcap | grep \ ++"IP6 aef0::3 > aef0::4: ICMP6, destination unreachable, unreachable port, \ ++aef0::3 udp port 90" | uniq | wc -l) ++ test $c -eq 1 ++]) ++ ++ + OVS_APP_EXIT_AND_WAIT([ovn-controller]) + + as ovn-sb +@@ -4915,7 +5182,7 @@ AT_CLEANUP + + OVN_FOR_EACH_NORTHD([ + AT_SETUP([IPv6 prefix delegation]) +-AT_SKIP_IF([test $HAVE_DIBBLER_SERVER = no]) ++AT_SKIP_IF([test $HAVE_DHCPD = no]) + AT_SKIP_IF([test $HAVE_TCPDUMP = no]) + AT_KEYWORDS([ovn-ipv6-prefix_d]) + +@@ -4989,28 +5256,21 @@ ovn-nbctl set logical_router_port rp-public options:prefix=true + ovn-nbctl set logical_router_port rp-sw0 options:prefix=true + ovn-nbctl set logical_router_port rp-sw1 options:prefix=true + +-# reset dibbler state +-sed s/^iface.*/"iface \"s1\" {"/g -i /etc/dibbler/server.conf +-sed s/pd-pool.*/"pd-pool 2001:1db8:3333::\/80"/g -i /etc/dibbler/server.conf +-sed s/t1.*/"t1 10"/g -i /etc/dibbler/server.conf +-sed s/t2.*/"t2 15"/g -i /etc/dibbler/server.conf +-cat > /var/lib/dibbler/server-AddrMgr.xml < +- 1575481348 +- 0 +- +-EOF +-cat > /var/lib/dibbler/server-CfgMgr.xml < +- /var/lib/dibbler +- Server +- 8 +- 0 +- 0 +- ++cat > /etc/dhcp/dhcpd.conf < dibbler.log &]) ++NS_CHECK_EXEC([server], [dhcpd -6 -f s1 > dhcpd.log &]) + ovn-nbctl --wait=hv sync + + OVS_WAIT_WHILE([test "$(ovn-nbctl get logical_router_port rp-public ipv6_prefix | cut -c4-15)" = ""]) +@@ -5034,7 +5294,7 @@ ovn-nbctl set logical_router_port rp-sw1 options:prefix=false + # Renew message + NS_CHECK_EXEC([server], [tcpdump -c 1 -nni s1 ip6[[48:1]]=0x05 and ip6[[113:4]]=0x${prefix} > renew.pcap &]) + # Reply message with Status OK +-NS_CHECK_EXEC([server], [tcpdump -c 1 -nni s1 ip6[[48:1]]=0x07 and ip6[[81:4]]=0x${prefix} and ip6[[98:1]]=0x0d and ip6[[101:2]]=0x0000 > reply.pcap &]) ++NS_CHECK_EXEC([server], [tcpdump -c 1 -nni s1 ip6[[48:1]]=0x07 and ip6[[81:4]]=0x${prefix} > reply.pcap &]) + + OVS_WAIT_UNTIL([ + total_pkts=$(cat renew.pcap | wc -l) +@@ -5046,7 +5306,7 @@ OVS_WAIT_UNTIL([ + test "${total_pkts}" = "1" + ]) + +-kill $(pidof dibbler-server) ++kill $(pidof dhcpd) + kill $(pidof tcpdump) + + ovn-nbctl set logical_router_port rp-sw0 options:prefix=false +@@ -6667,8 +6927,25 @@ OVS_WAIT_UNTIL([ + test "${n_reject}" = "2" + ]) + kill $(pidof tcpdump) ++rm -f reject.pcap ++ ++# Let's update the meter ++NS_EXEC([sw01], [tcpdump -l -n -i sw01 icmp -Q in > reject.pcap &]) ++check ovn-nbctl --may-exist meter-add acl-meter drop 10 pktps 0 ++ip netns exec sw01 scapy -H <<-EOF ++p = IP(src="192.168.1.2", dst="192.168.1.1") / UDP(dport = 12345) / Raw(b"X"*64) ++send (p, iface='sw01', loop = 0, verbose = 0, count = 100) ++EOF ++ ++# 10pps + 1 burst size ++OVS_WAIT_UNTIL([ ++ n_reject=$(grep unreachable reject.pcap | wc -l) ++ test "${n_reject}" = "20" ++]) + ++kill $(pidof tcpdump) + rm -f reject.pcap ++ + NS_EXEC([sw01], [tcpdump -l -n -i sw01 icmp -Q in > reject.pcap &]) + check ovn-nbctl --wait=hv ls-copp-del sw0 reject + +@@ -6814,6 +7091,148 @@ check ovn-nbctl acl-add sw0 from-lport 1001 "ip" allow-related + check ovn-nbctl acl-add sw0 to-lport 1001 "ip" allow-related + + ++ADD_NAMESPACES(sw0-p1) ++ADD_VETH(sw0-p1, sw0-p1, br-int, "10.0.0.2/24", "50:54:00:00:00:02", \ ++ "10.0.0.1") ++ADD_NAMESPACES(sw0-p2) ++ADD_VETH(sw0-p2, sw0-p2, br-int, "10.0.0.3/24", "50:54:00:00:00:03", \ ++ "10.0.0.1") ++ADD_NAMESPACES(sw0-p3) ++ADD_VETH(sw0-p3, sw0-p3, br-int, "10.0.0.4/24", "50:54:00:00:00:04", \ ++ "10.0.0.1") ++ ++# Ensure ovn-controller is caught up ++ovn-nbctl --wait=hv sync ++ ++on_exit 'ovn-nbctl acl-list sw0' ++on_exit 'ovn-sbctl lflow-list' ++on_exit 'ovs-ofctl dump-flows br-int' ++ ++wait_for_ports_up ++ ++AT_CHECK([ovs-appctl dpctl/flush-conntrack]) ++# 'sw0-p1' should be able to ping 'sw0-p3'. ++NS_CHECK_EXEC([sw0-p1], [ping -q -c 10 -i 0.3 -w 15 10.0.0.4 | FORMAT_PING], \ ++[0], [dnl ++10 packets transmitted, 10 received, 0% packet loss, time 0ms ++]) ++ ++# Ensure conntrack entry is present and ct_label is set. ++AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.0.0.4) | \ ++sed -e 's/zone=[[0-9]]*/zone=/' | \ ++sed -e 's/labels=0x4d2[[0-9a-f]]*/labels=0x4d2000000000000000000000000/'], [0], [dnl ++icmp,orig=(src=10.0.0.2,dst=10.0.0.4,id=,type=8,code=0),reply=(src=10.0.0.4,dst=10.0.0.2,id=,type=0,code=0),zone=,labels=0x4d2000000000000000000000000 ++icmp,orig=(src=10.0.0.2,dst=10.0.0.4,id=,type=8,code=0),reply=(src=10.0.0.4,dst=10.0.0.2,id=,type=0,code=0),zone= ++]) ++ ++AT_CHECK([ovs-appctl dpctl/flush-conntrack]) ++# 'sw0-p3' should be able to ping 'sw0-p1'. ++NS_CHECK_EXEC([sw0-p3], [ping -q -c 10 -i 0.3 -w 15 10.0.0.2 | FORMAT_PING], \ ++[0], [dnl ++10 packets transmitted, 10 received, 0% packet loss, time 0ms ++]) ++ ++# Ensure conntrack entry is present and ct_label is set. ++AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.0.0.2) | \ ++sed -e 's/zone=[[0-9]]*/zone=/' | \ ++sed -e 's/labels=0x4d3[[0-9a-f]]*/labels=0x4d3000000000000000000000000/'], [0], [dnl ++icmp,orig=(src=10.0.0.4,dst=10.0.0.2,id=,type=8,code=0),reply=(src=10.0.0.2,dst=10.0.0.4,id=,type=0,code=0),zone=,labels=0x4d3000000000000000000000000 ++icmp,orig=(src=10.0.0.4,dst=10.0.0.2,id=,type=8,code=0),reply=(src=10.0.0.2,dst=10.0.0.4,id=,type=0,code=0),zone= ++]) ++ ++AT_CHECK([ovs-appctl dpctl/flush-conntrack]) ++# 'sw0-p1' should be able to ping 'sw0-p2'. ++NS_CHECK_EXEC([sw0-p1], [ping -q -c 10 -i 0.3 -w 15 10.0.0.3 | FORMAT_PING], \ ++[0], [dnl ++10 packets transmitted, 10 received, 0% packet loss, time 0ms ++]) ++ ++# Ensure conntrack entry is present and ct_label is not set. ++AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.0.0.3) | \ ++sed -e 's/zone=[[0-9]]*/zone=/'], [0], [dnl ++icmp,orig=(src=10.0.0.2,dst=10.0.0.3,id=,type=8,code=0),reply=(src=10.0.0.3,dst=10.0.0.2,id=,type=0,code=0),zone= ++icmp,orig=(src=10.0.0.2,dst=10.0.0.3,id=,type=8,code=0),reply=(src=10.0.0.3,dst=10.0.0.2,id=,type=0,code=0),zone= ++]) ++ ++AT_CHECK([ovs-appctl dpctl/flush-conntrack]) ++# 'sw0-p2' should be able to ping 'sw0-p1'. ++NS_CHECK_EXEC([sw0-p2], [ping -q -c 10 -i 0.3 -w 15 10.0.0.2 | FORMAT_PING], \ ++[0], [dnl ++10 packets transmitted, 10 received, 0% packet loss, time 0ms ++]) ++ ++# Ensure conntrack entry is present and ct_label is not set. ++AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.0.0.2) | \ ++sed -e 's/zone=[[0-9]]*/zone=/'], [0], [dnl ++icmp,orig=(src=10.0.0.3,dst=10.0.0.2,id=,type=8,code=0),reply=(src=10.0.0.2,dst=10.0.0.3,id=,type=0,code=0),zone= ++icmp,orig=(src=10.0.0.3,dst=10.0.0.2,id=,type=8,code=0),reply=(src=10.0.0.2,dst=10.0.0.3,id=,type=0,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([NORTHD_TYPE]) ++ ++as ++OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d ++/connection dropped.*/d"]) ++ ++AT_CLEANUP ++]) ++ ++OVN_FOR_EACH_NORTHD([ ++AT_SETUP([ACL label - conntrack ct_label - acl after lb]) ++AT_KEYWORDS([acl label ct_commit]) ++ ++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 ++ ++check ovn-nbctl ls-add sw0 ++ ++check ovn-nbctl lsp-add sw0 sw0-p1 ++check ovn-nbctl lsp-set-addresses sw0-p1 "50:54:00:00:00:02 10.0.0.2" ++check ovn-nbctl lsp-set-port-security sw0-p1 "50:54:00:00:00:02 10.0.0.2" ++ ++check ovn-nbctl lsp-add sw0 sw0-p2 ++check ovn-nbctl lsp-set-addresses sw0-p2 "50:54:00:00:00:03 10.0.0.3" ++check ovn-nbctl lsp-set-port-security sw0-p2 "50:54:00:00:00:03 10.0.0.3" ++ ++check ovn-nbctl lsp-add sw0 sw0-p3 ++check ovn-nbctl lsp-set-addresses sw0-p3 "50:54:00:00:00:04 10.0.0.4" ++check ovn-nbctl lsp-set-port-security sw0-p3 "50:54:00:00:00:04 10.0.0.4" ++ ++# ACLs ++# Case 1: sw0-p1 ---> sw0-p3 allowed, label=1234 ++# Case 2: sw0-p3 ---> sw0-p1 allowed, label=1235 ++# Case 3: sw0-p1 ---> sw0-p2 allowed, no label ++# Case 4: sw0-p2 ---> sw0-p1 allowed, no label ++ ++check ovn-nbctl --label=1234 --apply-after-lb acl-add sw0 from-lport 1002 'ip4 && inport == "sw0-p1" && ip4.dst == 10.0.0.4' allow-related ++check ovn-nbctl --label=1235 acl-add sw0 to-lport 1002 'ip4 && outport == "sw0-p1" && ip4.src == 10.0.0.4' allow-related ++check ovn-nbctl --apply-after-lb acl-add sw0 from-lport 1001 "ip" allow-related ++check ovn-nbctl acl-add sw0 to-lport 1001 "ip" allow-related ++ ++ + ADD_NAMESPACES(sw0-p1) + ADD_VETH(sw0-p1, sw0-p1, br-int, "10.0.0.2/24", "50:54:00:00:00:02", \ + "10.0.0.1") +@@ -7009,3 +7428,204 @@ OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d + + AT_CLEANUP + ]) ++ ++OVN_FOR_EACH_NORTHD([ ++AT_SETUP([ACL label - conntrack label change - acl after lb]) ++AT_KEYWORDS([acl label ct_commit label change]) ++ ++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 ++ ++check ovn-nbctl ls-add sw0 ++ ++check ovn-nbctl lsp-add sw0 sw0-p1 ++check ovn-nbctl lsp-set-addresses sw0-p1 "50:54:00:00:00:02 10.0.0.2" ++check ovn-nbctl lsp-set-port-security sw0-p1 "50:54:00:00:00:02 10.0.0.2" ++ ++check ovn-nbctl lsp-add sw0 sw0-p2 ++check ovn-nbctl lsp-set-addresses sw0-p2 "50:54:00:00:00:03 10.0.0.3" ++check ovn-nbctl lsp-set-port-security sw0-p2 "50:54:00:00:00:03 10.0.0.3" ++ ++# ACLs ++# sw0-p1 ---> sw0-p2 allowed, label=1234 ++ ++check ovn-nbctl --label=1234 --apply-after-lb acl-add sw0 from-lport 1002 'ip4 && inport == "sw0-p1" && ip4.dst == 10.0.0.3' allow-related ++ ++ADD_NAMESPACES(sw0-p1) ++ADD_VETH(sw0-p1, sw0-p1, br-int, "10.0.0.2/24", "50:54:00:00:00:02", \ ++ "10.0.0.1") ++ADD_NAMESPACES(sw0-p2) ++ADD_VETH(sw0-p2, sw0-p2, br-int, "10.0.0.3/24", "50:54:00:00:00:03", \ ++ "10.0.0.1") ++ ++# Ensure ovn-controller is caught up ++ovn-nbctl --wait=hv sync ++ ++on_exit 'ovn-nbctl acl-list sw0' ++on_exit 'ovn-sbctl lflow-list' ++on_exit 'ovs-ofctl dump-flows br-int' ++ ++wait_for_ports_up ++ ++AT_CHECK([ovs-appctl dpctl/flush-conntrack]) ++ ++# start a background ping for ~30 secs. ++NETNS_DAEMONIZE([sw0-p1], [[ping -q -c 100 -i 0.3 -w 15 10.0.0.3]], [ns-sw0-p1.pid]) ++ ++sleep 3s ++ ++# Ensure conntrack entry is present and ct_label is set. ++AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.0.0.3) | \ ++sed -e 's/zone=[[0-9]]*/zone=/' | \ ++sed -e 's/labels=0x4d2[[0-9a-f]]*/labels=0x4d2000000000000000000000000/'], [0], [dnl ++icmp,orig=(src=10.0.0.2,dst=10.0.0.3,id=,type=8,code=0),reply=(src=10.0.0.3,dst=10.0.0.2,id=,type=0,code=0),zone=,labels=0x4d2000000000000000000000000 ++icmp,orig=(src=10.0.0.2,dst=10.0.0.3,id=,type=8,code=0),reply=(src=10.0.0.3,dst=10.0.0.2,id=,type=0,code=0),zone= ++]) ++ ++# Add a higher priority ACL with different label. ++# This ACL also allows the ping running in background. ++ ++check ovn-nbctl --label=1235 --apply-after-lb acl-add sw0 from-lport 1003 'ip4 && inport == "sw0-p1" && ip4.dst == 10.0.0.3' allow-related ++ovn-nbctl --wait=hv sync ++ ++sleep 3s ++ ++# Ensure conntrack entry is updated with new ct_label is set. ++AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.0.0.3) | \ ++sed -e 's/zone=[[0-9]]*/zone=/' | \ ++sed -e 's/labels=0x4d3[[0-9a-f]]*/labels=0x4d3000000000000000000000000/'], [0], [dnl ++icmp,orig=(src=10.0.0.2,dst=10.0.0.3,id=,type=8,code=0),reply=(src=10.0.0.3,dst=10.0.0.2,id=,type=0,code=0),zone=,labels=0x4d3000000000000000000000000 ++icmp,orig=(src=10.0.0.2,dst=10.0.0.3,id=,type=8,code=0),reply=(src=10.0.0.3,dst=10.0.0.2,id=,type=0,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([NORTHD_TYPE]) ++ ++as ++OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d ++/connection dropped.*/d"]) ++ ++AT_CLEANUP ++]) ++ ++OVN_FOR_EACH_NORTHD([ ++AT_SETUP([ACL all drop and allow related - acl after lb]) ++AT_KEYWORDS([ACL all drop and allow related]) ++ ++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 ++ ++ ++# No ACLs in sw0. ++check ovn-nbctl ls-add sw0 ++ ++check ovn-nbctl lsp-add sw0 sw0p1 ++check ovn-nbctl lsp-set-addresses sw0p1 "50:54:00:00:00:02 10.0.0.3" ++ ++# ACLs to drop every thing and just allow-related. ++check ovn-nbctl ls-add sw1 ++ ++check ovn-nbctl lsp-add sw1 sw1p1 ++check ovn-nbctl lsp-set-addresses sw1p1 "50:54:00:00:00:03 20.0.0.3" ++ ++check ovn-nbctl --apply-after-lb acl-add sw1 from-lport 1001 'inport == "sw1p1" && ip4' drop ++ ++check ovn-nbctl acl-add sw1 to-lport 1002 'ip4 && tcp && tcp.dst == 80' allow-related ++check ovn-nbctl acl-add sw1 to-lport 1001 'ip4' drop ++ ++ADD_NAMESPACES(sw0p1) ++ADD_VETH(sw0p1, sw0p1, br-int, "10.0.0.3/24", "50:54:00:00:00:02", \ ++ "10.0.0.1") ++ADD_NAMESPACES(sw1p1) ++ADD_VETH(sw1p1, sw1p1, br-int, "20.0.0.3/24", "50:54:00:00:00:03", \ ++ "20.0.0.1") ++ ++# Create a logical router and attach both logical switches ++check ovn-nbctl lr-add lr0 ++check ovn-nbctl lrp-add lr0 lr0-sw0 00:00:00:00:ff:01 10.0.0.1/24 ++check ovn-nbctl lsp-add sw0 sw0-lr0 ++check ovn-nbctl lsp-set-type sw0-lr0 router ++check ovn-nbctl lsp-set-addresses sw0-lr0 00:00:00:00:ff:01 ++check ovn-nbctl lsp-set-options sw0-lr0 router-port=lr0-sw0 ++ ++check ovn-nbctl lrp-add lr0 lr0-sw1 00:00:00:00:ff:02 20.0.0.1/24 ++check ovn-nbctl lsp-add sw1 sw1-lr0 ++check ovn-nbctl lsp-set-type sw1-lr0 router ++check ovn-nbctl lsp-set-addresses sw1-lr0 00:00:00:00:ff:02 ++check ovn-nbctl lsp-set-options sw1-lr0 router-port=lr0-sw1 ++ ++# Ensure ovn-controller is caught up ++ovn-nbctl --wait=hv sync ++ ++on_exit 'ovn-nbctl acl-list sw0' ++on_exit 'ovn-sbctl lflow-list' ++on_exit 'ovs-ofctl dump-flows br-int' ++ ++wait_for_ports_up ++ ++# Start webservers in 'sw1-p1' ++OVS_START_L7([sw1p1], [http]) ++ ++AT_CHECK([ip netns exec sw0p1 wget 20.0.0.3 -t 3 -T 1], [0], [ignore], [ignore]) ++ ++# Clear the apply-after-lb option for the ACL ++check ovn-nbctl acl-del sw1 from-lport 1001 'inport == "sw1p1" && ip4' ++check ovn-nbctl acl-add sw1 from-lport 1001 'inport == "sw1p1" && ip4' drop ++ ++check ovn-nbctl --wait=hv sync ++ ++AT_CHECK([ip netns exec sw0p1 wget 20.0.0.3 -t 3 -T 1], [0], [ignore], [ignore]) ++ ++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([NORTHD_TYPE]) ++ ++as ++OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d ++/connection dropped.*/d"]) ++ ++AT_CLEANUP ++]) +diff --git a/utilities/ovn-nbctl.8.xml b/utilities/ovn-nbctl.8.xml +index 80a564660..6be37d21a 100644 +--- a/utilities/ovn-nbctl.8.xml ++++ b/utilities/ovn-nbctl.8.xml +@@ -399,7 +399,7 @@ + must be either switch or port-group. +

    +
    +-
    [--type={switch | port-group}] [--log] [--meter=meter] [--severity=severity] [--name=name] [--label=label] [--may-exist] acl-add entity direction priority match verdict
    ++
    [--type={switch | port-group}] [--log] [--meter=meter] [--severity=severity] [--name=name] [--label=label] [--may-exist] [--apply-after-lb] acl-add entity direction priority match verdict
    +
    +

    + Adds the specified ACL to entity. direction +@@ -423,6 +423,13 @@ + is used to rate-limit packet logging. The meter argument + names a meter configured by meter-add. +

    ++ ++

    ++ The --apply-after-lb option sets ++ apply-after-lb=true in the options column ++ of the ACL table. As the option name suggests, the ACL ++ will be applied after the logical switch load balancer stage. ++

    +
    + +
    [--type={switch | port-group}] acl-del entity [direction [priority match]]
    +diff --git a/utilities/ovn-nbctl.c b/utilities/ovn-nbctl.c +index 55b0f5124..3a41f4c61 100644 +--- a/utilities/ovn-nbctl.c ++++ b/utilities/ovn-nbctl.c +@@ -2113,6 +2113,7 @@ nbctl_pre_acl(struct ctl_context *ctx) + ovsdb_idl_add_column(ctx->idl, &nbrec_acl_col_direction); + ovsdb_idl_add_column(ctx->idl, &nbrec_acl_col_priority); + ovsdb_idl_add_column(ctx->idl, &nbrec_acl_col_match); ++ ovsdb_idl_add_column(ctx->idl, &nbrec_acl_col_options); + } + + static void +@@ -2126,6 +2127,7 @@ nbctl_pre_acl_list(struct ctl_context *ctx) + ovsdb_idl_add_column(ctx->idl, &nbrec_acl_col_severity); + ovsdb_idl_add_column(ctx->idl, &nbrec_acl_col_meter); + ovsdb_idl_add_column(ctx->idl, &nbrec_acl_col_label); ++ ovsdb_idl_add_column(ctx->idl, &nbrec_acl_col_options); + } + + static void +@@ -2212,6 +2214,13 @@ nbctl_acl_add(struct ctl_context *ctx) + nbrec_acl_set_label(acl, label_value); + } + ++ if (!strcmp(direction, "from-lport") && ++ (shash_find(&ctx->options, "--apply-after-lb") != NULL)) { ++ const struct smap options = SMAP_CONST1(&options, "apply-after-lb", ++ "true"); ++ nbrec_acl_set_options(acl, &options); ++ } ++ + /* Check if same acl already exists for the ls/portgroup */ + size_t n_acls = pg ? pg->n_acls : ls->n_acls; + struct nbrec_acl **acls = pg ? pg->acls : ls->acls; +@@ -6968,7 +6977,8 @@ static const struct ctl_command_syntax nbctl_commands[] = { + /* acl commands. */ + { "acl-add", 5, 6, "{SWITCH | PORTGROUP} DIRECTION PRIORITY MATCH ACTION", + nbctl_pre_acl, nbctl_acl_add, NULL, +- "--log,--may-exist,--type=,--name=,--severity=,--meter=,--label=", RW }, ++ "--log,--may-exist,--type=,--name=,--severity=,--meter=,--label=," ++ "--apply-after-lb", RW }, + { "acl-del", 1, 4, "{SWITCH | PORTGROUP} [DIRECTION [PRIORITY MATCH]]", + nbctl_pre_acl, nbctl_acl_del, NULL, "--type=", RW }, + { "acl-list", 1, 1, "{SWITCH | PORTGROUP}", +diff --git a/utilities/ovn-trace.c b/utilities/ovn-trace.c +index 0795913d3..ece5803f2 100644 +--- a/utilities/ovn-trace.c ++++ b/utilities/ovn-trace.c +@@ -2457,12 +2457,14 @@ execute_select(const struct ovnact_select *select, + + static void + execute_log(const struct ovnact_log *log, struct flow *uflow, +- struct ovs_list *super) ++ struct ovs_list *super, const char *direction) + { + char *packet_str = flow_to_string(uflow, NULL); + ovntrace_node_append(super, OVNTRACE_NODE_TRANSFORMATION, +- "LOG: ACL name=%s, verdict=%s, severity=%s, packet=\"%s\"", ++ "LOG: ACL name=%s, direction=%s, verdict=%s, " ++ "severity=%s, packet=\"%s\"", + log->name ? log->name : "", ++ direction, + log_verdict_to_string(log->verdict), + log_severity_to_string(log->severity), + packet_str); +@@ -2726,7 +2728,8 @@ trace_actions(const struct ovnact *ovnacts, size_t ovnacts_len, + break; + + case OVNACT_LOG: +- execute_log(ovnact_get_LOG(a), uflow, super); ++ execute_log(ovnact_get_LOG(a), uflow, super, ++ pipeline == OVNACT_P_INGRESS ? "IN" : "OUT"); + break; + + case OVNACT_SET_METER: diff --git a/SPECS/ovn-2021.spec b/SPECS/ovn-2021.spec index f2880f1..25df317 100644 --- a/SPECS/ovn-2021.spec +++ b/SPECS/ovn-2021.spec @@ -51,7 +51,7 @@ Summary: Open Virtual Network support Group: System Environment/Daemons URL: http://www.ovn.org/ Version: 21.12.0 -Release: 11%{?commit0:.%{date}git%{shortcommit0}}%{?dist} +Release: 46%{?commit0:.%{date}git%{shortcommit0}}%{?dist} Provides: openvswitch%{pkgver}-ovn-common = %{?epoch:%{epoch}:}%{version}-%{release} Obsoletes: openvswitch%{pkgver}-ovn-common < 2.11.0-1 @@ -62,8 +62,8 @@ License: ASL 2.0 and LGPLv2+ and SISSL # Always pull an upstream release, since this is what we rebase to. Source: https://github.com/ovn-org/ovn/archive/v%{version}.tar.gz#/ovn-%{version}.tar.gz -%define ovscommit 91e1ff5dde396fbcc8623ac0726066e970e6de15 -%define ovsshortcommit 91e1ff5 +%define ovscommit 498cedc483f3239c839c55b4d9f2261b61fb6ace +%define ovsshortcommit 498cedc Source10: https://github.com/openvswitch/ovs/archive/%{ovscommit}.tar.gz#/openvswitch-%{ovsshortcommit}.tar.gz %define ovsdir ovs-%{ovscommit} @@ -528,43 +528,218 @@ fi %{_unitdir}/ovn-controller-vtep.service %changelog +* Mon Apr 25 2022 Numan Siddique - 21.12.0-46 +- ovn-northd: Add flow to use eth.src if nd.tll is 0 in put_nd() action. (#2078026) +[Gerrit: 30bc0f402ef89b5383c0a0a679df51206e488f64] +[Upstream: 80187a8031b6abe01fb23657a9bed2372ae23af5] + +* Wed Apr 13 2022 Han Zhou - 21.12.0-45 +- ofctrl.c: Check installed flow when merging tracked flow changes. (#2071272) +[Gerrit: 4db73f09c5a1a2abcc468dff9c65ecb7e716102e] +[Upstream: 9c6d285ef2a65aef032cd60f9a9fe16cad7c4222] + +* Mon Apr 11 2022 Xavier Simonart - 21.12.0-44 +- northd: avoid writing to IDL in parallel when using northd parallelization +[Gerrit: e4530457b3935d9fda10fbf2d9352db921396b6c] +[Upstream: 741a135fa1db9e10b725d4b69c6da455f732b89b] + +* Thu Apr 07 2022 Mohammad Heib - 21.12.0-43 +- controller/pinctrl: avoid accessing invalid memory (#2052945) +[Gerrit: bb50b967c1778cc6469761bb10b459bdb989f7e1] +[Upstream: 6bc6002601ea161a287bcf53524e9282ab97c31b] + +* Wed Apr 06 2022 Vladislav Odintsov - 21.12.0-42 +- vtep: correctly bring vtep lport up in SBDB +[Gerrit: 4204ac1ef4b47540398a51d2db258c2d5533478d] +[Upstream: b35c98c923f4972198f741ff1d2b28671d6ff50d] + +* Wed Apr 06 2022 Lorenzo Bianconi - 21.12.0-41 +- controller: properly remove qos policy meters +[Gerrit: 09d2f91c3579a9fa1ea2a57f7cab4a2489563248] +[Upstream: 1e1d75c725a3445a853dd792b28ff02bb3ab1218] + +* Fri Apr 01 2022 Mark Michelson - 21.12.0-40 +- ovs: Revert submodule after erroneous change. +[Gerrit: 19a0032b60326e0c1be810d7d3a9b4cb55ebe069] +[Upstream: 19a0032b60326e0c1be810d7d3a9b4cb55ebe069] + +* Wed Mar 30 2022 Dumitru Ceara - 21.12.0-39 +- acl-log: Log the direction (logical pipeline) of the matching ACL. (#1992641) +[Gerrit: 05cd6245db39baf750bd58d9eefd581ccf9c6418] +[Upstream: 05cd6245db39baf750bd58d9eefd581ccf9c6418] + +* Tue Mar 29 2022 Dumitru Ceara - 21.12.0-38 +- inc-proc-eng: Properly log recompute reason. +[Gerrit: 4d43d4fdb9d5e515a9fe1d37e1620b32685a3c47] +[Upstream: 4d43d4fdb9d5e515a9fe1d37e1620b32685a3c47] + +* Thu Mar 24 2022 Vladislav Odintsov - 21.12.0-37 +- rhel: fix logrotate user config option +[Gerrit: cbb8e71dca93a9a0d2bcfb531fb234b04d0f2b58] +[Upstream: e8800ddd9e919e2ffc9d8c5c9ab27f0a5a6ec2e5] + +* Thu Mar 17 2022 Dumitru Ceara - 21.12.0-36 +- northd: Properly warn for NAT on LR with multiple gw ports. +[Gerrit: f49d842fc75f7a833157e70d97991ff7c6fb5307] +[Upstream: b8194738c99ee09ad6d4762a3c999e04d58d1a0f] + +* Fri Mar 11 2022 Mark Michelson - 21.12.0-35 +- Prepare for 21.12.2. +[Gerrit: f091025ba3a38a0d9046214d046b9f09fdeb28e5] +[Upstream: f091025ba3a38a0d9046214d046b9f09fdeb28e5] + +* Fri Mar 11 2022 Mark Michelson - 21.12.0-34 +- Set release date for 21.12.1. +[Gerrit: a1001ce71dd24da6c2412e403db4f423ce2ba2bb] +[Upstream: a1001ce71dd24da6c2412e403db4f423ce2ba2bb] + +* Thu Mar 10 2022 Lorenzo Bianconi - 21.12.0-33 +- controller: reconfigure ovs meters for ovn meters (#1939524) +[Gerrit: 2169703529f47b50e0871e64fa6921170002cd51] +[Upstream: 2169703529f47b50e0871e64fa6921170002cd51] + +* Tue Mar 08 2022 Numan Siddique - 21.12.0-32 +- branch-21.12: Fix the compilation error. +[Gerrit: 091f1b5a31e4cef212d2c7f92b6371a0c97d1cf6] +[Upstream: 091f1b5a31e4cef212d2c7f92b6371a0c97d1cf6] + +* Tue Mar 08 2022 Numan Siddique - 21.12.0-31 +- northd: Support the option to apply from-lport ACLs after load balancer. +[Gerrit: 77f7512157f863e481ba9f8f41f3feece63d43f6] +[Upstream: 74d82e296f80f38948bcb39cd610447c1f435613] + +* Fri Feb 25 2022 Numan Siddique - 21.12.0-30 +- ovs: Bump submodule to stable branch-2.17. +[Gerrit: c96bf2fa983e7c849e9f094e4e5edcc8b4312700] +[Upstream: 576491f50721748ddfb2541ad1ef793ed9cba69d] + +* Fri Feb 25 2022 Dumitru Ceara - 21.12.0-29 +- ci: ovn-kubernetes: Bump kubernetes version to v1.23.3. +[Gerrit: 11645b21b4ed8a22220b5a635b2a3217f68904bf] +[Upstream: d811222c1adb852babe2de57af063c17db9b91fe] + +* Fri Feb 25 2022 Dumitru Ceara - 21.12.0-28 +- ci: ovn-kubernetes: Generate DB model based on current OVN tree. +[Gerrit: 4331e12f0feff7c19036ab32b1680f11d00fc919] +[Upstream: 0c252b858b3a3483b5b671704312cc2acb8e99a3] + +* Fri Feb 25 2022 Lorenzo Bianconi - 21.12.0-27 +- northd: introduce exclude-lb-vips-from-garp option for lsp (#2054394 2053013) +[Gerrit: 5c6f3c6c9aae97b87f4d968251e7dfdd5f299e99] +[Upstream: 2a5219321c2fa48f3f4b24ff3b87a27b21bf1b7b] + +* Wed Feb 23 2022 Dumitru Ceara - 21.12.0-26 +- ovn-northd: Don't log transaction failures on standby instances. +[Gerrit: a136a82fcb5dc90700f2059dac1994232d62d242] +[Upstream: 71f87a61e3dbbf7850055d027c1e5bccffa9de61] + +* Mon Feb 21 2022 Han Zhou - 21.12.0-25 +- lflow: Fix conjunction ID allocation problem with DP groups. +[Gerrit: 3c727ff4d03618154fef556f40b6e5c239b67250] +[Upstream: 3c727ff4d03618154fef556f40b6e5c239b67250] + +* Mon Feb 14 2022 Lorenzo Bianconi - 21.12.0-24 +- introduce rdnss, dnssl and route_info opt in put_nd_ra_opts action (#1851788) +[Gerrit: 9038ab66fcee57bdfff4b39b318ed3f89cd35400] +[Upstream: bb15877964c036848fe0c2e13ab394c88cdf7457] + +* Wed Feb 09 2022 Sven Haardiek - 21.12.0-23 +- Set additional header in DNS message explicitly +[Gerrit: 3b78224bee7eccd4f9e3dd125d5813821069b189] +[Upstream: f167c0bcdff915d358eb0bc442e447c8944dd05a] + +* Tue Feb 08 2022 Han Zhou - 21.12.0-22 +- northd.c: Fix nbcfg timestamp - use time_wall_msec instead of time_msec. +[Gerrit: db51c2ad942af91775f5111ba4d57193ed8ddb48] +[Upstream: 06d677bf20ac07c50a785869bb566b39cbc4c7f4] + +* Tue Feb 08 2022 Mark Michelson - 21.12.0-21 +- ovn-parallel-hmap: Fix NUMA and core detection. (#1975345) +[Gerrit: ae1318c742f707f811779cd84cbe4cdc889f2362] +[Upstream: ae1318c742f707f811779cd84cbe4cdc889f2362] + +* Fri Feb 04 2022 Ilya Maximets - 21.12.0-20 +- ci: Install wheel before installing any other python packages. +[Gerrit: 59a42559e0cd83b269c214a43fed858cd87284e2] +[Upstream: d5453008c419512ba5a31dade5d394984b6161a1] + +* Tue Feb 01 2022 Dumitru Ceara - 21.12.0-19 +- pinctrl: Avoid false positive out of bounds warning. +[Gerrit: 5d05cd2d93689ea8b0e97e8178e318e043ca0257] +[Upstream: 04cfd325971719a08baaa34555908abfed4ca9b2] + +* Tue Feb 01 2022 Dumitru Ceara - 21.12.0-18 +- pinctrl: Fix potential stack overflow in pinctrl_compose_ipv6(). +[Gerrit: 17c22d4a43a493558572ff1fde728b98e0fef1bd] +[Upstream: 6fa3409ddd48979e52b9e3963b75d330eaad2d19] + +* Tue Feb 01 2022 Dumitru Ceara - 21.12.0-17 +- ovn-northd: Enable change tracking for all SB tables. +[Gerrit: 18f9509e3e2c6c319299578c70cd632faa69c02b] +[Upstream: e4d6d3455baf09c63ed610037c384855e5f64141] + +* Tue Feb 01 2022 Vladislav Odintsov - 21.12.0-16 +- vtep: set is-vtep to chassis's other_config if absent +[Gerrit: 0bce1553f4231d76b37a48642269e3e8fd3a8207] +[Upstream: 0bce1553f4231d76b37a48642269e3e8fd3a8207] + +* Wed Jan 26 2022 Dumitru Ceara - 21.12.0-15 +- northd: Remove potential duplicates in SB Load_Balancer table. (#2046274) +[Gerrit: 2bbef142105d93bd6b492d4c80a84fc1e599d66f] +[Upstream: 2bbef142105d93bd6b492d4c80a84fc1e599d66f] + +* Mon Jan 17 2022 Lorenzo Bianconi - 21.12.0-14 +- Add isc-dhcp-server to github workload +[Gerrit: 32f997b13a58f5e32fa4a65828a2cb1a113039ae] +[Upstream: 1f85302f18556fc8f2020b38008806a2f0fc501c] + +* Mon Jan 17 2022 Lorenzo Bianconi - 21.12.0-13 +- test: replace dibbler with dhcpd +[Gerrit: 59d974be1d11e137e68cbeab07f1d31b1a24d9ca] +[Upstream: 2bbd90bd5c696e4228157aa7790c54c74ae51e71] + +* Mon Jan 17 2022 Lorenzo Bianconi - 21.12.0-12 +- northd: fix IPv6-PD with northd IP rework +[Gerrit: 1eae43916edde2443578f477560ea03c0c22dec7] +[Upstream: d32a9bc5290e40dd63ef495eb4f0fcde9e446089] + * Thu Jan 06 2022 Dumitru Ceara - 21.12.0-11 - pinctrl: Avoid misaligned access to ovs_ra_msg. - [Gerrit: 290523cdfadc5cb401939cc21c1f8de66a6b79b2] - [Upstream: N/A] +[Gerrit: 290523cdfadc5cb401939cc21c1f8de66a6b79b2] +[Upstream: 290523cdfadc5cb401939cc21c1f8de66a6b79b2] * Thu Jan 06 2022 Dumitru Ceara - 21.12.0-10 - pinctrl: Avoid misaligned access to controller_event_opt_header. - [Gerrit: 2280d3a3f63e026657564f34793b9143323afaf6] - [Upstream: N/A] +[Gerrit: 2280d3a3f63e026657564f34793b9143323afaf6] +[Upstream: 2280d3a3f63e026657564f34793b9143323afaf6] * Thu Jan 06 2022 Dumitru Ceara - 21.12.0-9 - pinctrl: Ensure packet headers are properly aligned for ICMP errors. - [Gerrit: ca764d60c6cfd4e7121b15faf6ca3a026928da4e] - [Upstream: N/A] +[Gerrit: ca764d60c6cfd4e7121b15faf6ca3a026928da4e] +[Upstream: ca764d60c6cfd4e7121b15faf6ca3a026928da4e] * Thu Jan 06 2022 Dumitru Ceara - 21.12.0-8 - pinctrl: Ensure aligned accesses when processing DNS. - [Gerrit: 67829e142dcc71b1f8e4f01aab9e73333420a76f] - [Upstream: N/A] +[Gerrit: 67829e142dcc71b1f8e4f01aab9e73333420a76f] +[Upstream: 67829e142dcc71b1f8e4f01aab9e73333420a76f] * Thu Jan 06 2022 Dumitru Ceara - 21.12.0-7 - pinctrl: Ensure no misaligned accesses for SCTP packets. - [Gerrit: b5083fdb4dce839fe70ce5e1c059f11c917105f4] - [Upstream: N/A] +[Gerrit: b5083fdb4dce839fe70ce5e1c059f11c917105f4] +[Upstream: b5083fdb4dce839fe70ce5e1c059f11c917105f4] * Thu Jan 06 2022 Dumitru Ceara - 21.12.0-6 - pinctrl: Ensure proper alignment when using pinctrl_compose_ipv*(). - [Gerrit: 31e933469d862915b8c83131f72728fe32ecac51] - [Upstream: N/A] +[Gerrit: 31e933469d862915b8c83131f72728fe32ecac51] +[Upstream: 31e933469d862915b8c83131f72728fe32ecac51] * Thu Jan 06 2022 Dumitru Ceara - 21.12.0-5 - physical: Add remote parent ports to OFTABLE_REMOTE_OUTPUT flows. (#2036970) - [Gerrit: 627b25bd14085a78f1f9611f2a218ce639515e26] - [Upstream: e101e45f355a91e277630243e64897f91f13f8bc] +[Gerrit: 627b25bd14085a78f1f9611f2a218ce639515e26] +[Upstream: e101e45f355a91e277630243e64897f91f13f8bc] * Wed Dec 22 2021 Mark Michelson - 21.12.0-4 - Prepare for 21.12.1. - [Gerrit: 9c67f93b92d9864ac0e06dd3d96e8172441343c3] - [Upstream: N/A] +[Gerrit: 9c67f93b92d9864ac0e06dd3d96e8172441343c3] +[Upstream: 9c67f93b92d9864ac0e06dd3d96e8172441343c3]