get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

GET /api/patches/787923/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 787923,
    "url": "http://patchwork.ozlabs.org/api/patches/787923/?format=api",
    "web_url": "http://patchwork.ozlabs.org/project/openvswitch/patch/1499965361-32021-4-git-send-email-majopela@redhat.com/",
    "project": {
        "id": 47,
        "url": "http://patchwork.ozlabs.org/api/projects/47/?format=api",
        "name": "Open vSwitch",
        "link_name": "openvswitch",
        "list_id": "ovs-dev.openvswitch.org",
        "list_email": "ovs-dev@openvswitch.org",
        "web_url": "http://openvswitch.org/",
        "scm_url": "git@github.com:openvswitch/ovs.git",
        "webscm_url": "https://github.com/openvswitch/ovs",
        "list_archive_url": "",
        "list_archive_url_format": "",
        "commit_url_format": ""
    },
    "msgid": "<1499965361-32021-4-git-send-email-majopela@redhat.com>",
    "list_archive_url": null,
    "date": "2017-07-13T17:02:37",
    "name": "[ovs-dev,v5,4/8] ovn: l3ha, handling of multiple gateway chassis",
    "commit_ref": null,
    "pull_url": null,
    "state": "accepted",
    "archived": false,
    "hash": "c35542e0cbabbb11b8dc8c0d482592174499d951",
    "submitter": {
        "id": 67170,
        "url": "http://patchwork.ozlabs.org/api/people/67170/?format=api",
        "name": "Miguel Angel Ajo",
        "email": "majopela@redhat.com"
    },
    "delegate": {
        "id": 55721,
        "url": "http://patchwork.ozlabs.org/api/users/55721/?format=api",
        "username": "russellb",
        "first_name": "Russell",
        "last_name": "Bryant",
        "email": "rbryant@redhat.com"
    },
    "mbox": "http://patchwork.ozlabs.org/project/openvswitch/patch/1499965361-32021-4-git-send-email-majopela@redhat.com/mbox/",
    "series": [],
    "comments": "http://patchwork.ozlabs.org/api/patches/787923/comments/",
    "check": "pending",
    "checks": "http://patchwork.ozlabs.org/api/patches/787923/checks/",
    "tags": {},
    "related": [],
    "headers": {
        "Return-Path": "<ovs-dev-bounces@openvswitch.org>",
        "X-Original-To": [
            "incoming@patchwork.ozlabs.org",
            "dev@openvswitch.org"
        ],
        "Delivered-To": [
            "patchwork-incoming@bilbo.ozlabs.org",
            "ovs-dev@mail.linuxfoundation.org"
        ],
        "Received": [
            "from mail.linuxfoundation.org (mail.linuxfoundation.org\n\t[140.211.169.12])\n\t(using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256\n\tbits)) (No client certificate requested)\n\tby ozlabs.org (Postfix) with ESMTPS id 3x7hyN3GDbz9t2c\n\tfor <incoming@patchwork.ozlabs.org>;\n\tFri, 14 Jul 2017 03:05:00 +1000 (AEST)",
            "from mail.linux-foundation.org (localhost [127.0.0.1])\n\tby mail.linuxfoundation.org (Postfix) with ESMTP id A87B5B88;\n\tThu, 13 Jul 2017 17:02:57 +0000 (UTC)",
            "from smtp1.linuxfoundation.org (smtp1.linux-foundation.org\n\t[172.17.192.35])\n\tby mail.linuxfoundation.org (Postfix) with ESMTPS id AD204B6E\n\tfor <dev@openvswitch.org>; Thu, 13 Jul 2017 17:02:56 +0000 (UTC)",
            "from mail-wr0-f180.google.com (mail-wr0-f180.google.com\n\t[209.85.128.180])\n\tby smtp1.linuxfoundation.org (Postfix) with ESMTPS id 875D6175\n\tfor <dev@openvswitch.org>; Thu, 13 Jul 2017 17:02:53 +0000 (UTC)",
            "by mail-wr0-f180.google.com with SMTP id r103so56238974wrb.0\n\tfor <dev@openvswitch.org>; Thu, 13 Jul 2017 10:02:53 -0700 (PDT)",
            "from ctl.localdomain (111.148.134.37.dynamic.jazztel.es.\n\t[37.134.148.111]) by smtp.gmail.com with ESMTPSA id\n\t46sm7134176wrz.8.2017.07.13.10.02.49\n\t(version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128);\n\tThu, 13 Jul 2017 10:02:49 -0700 (PDT)"
        ],
        "X-Greylist": "whitelisted by SQLgrey-1.7.6",
        "X-Google-DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20161025;\n\th=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to\n\t:references;\n\tbh=EKGDp/RiVf7iWxR9Bd2A+1MFcqSFL8wwzMNilQ9xo7g=;\n\tb=mc8fBLQqoKEsPLnbtNxUhngNqP5u9U31102gXl7+GlPfKtyx/HLpZm44cN8jaIvBjb\n\tsPOTSFIsnQ4qc2iD8TwZ62KELC9Pq9mTfgXaRNxxvVsNAwbgX/rQYxWGwIn923v8xGPs\n\tdjRSRBYMRxCE+Z39FtxyFW3Y/tbracKMSpEsg2CiwFRQN70YAn4lJnUThc6FuQtaejEu\n\t5ynOKSKYtJ9Gf2BXQKmscC5/hRV/UiZvqlPW6V4iZUsMMLo/LPA1nPXmD3CsRv23ceyA\n\tQlG1105U1Gb1E/JcuzUT9Sb2SZLEp2fC99AxtEFuBI8IB+kKjkpoeSd1q9vmKPZeAK/S\n\t/e6A==",
        "X-Gm-Message-State": "AIVw111VvQUrUUTKczeG30w0a/Q639aYOA5Rld4T1WbQ1QJ3jnldKXbu\n\t19pxAie3XE4a76RY+Lckig==",
        "X-Received": "by 10.223.161.149 with SMTP id u21mr2567466wru.70.1499965371091; \n\tThu, 13 Jul 2017 10:02:51 -0700 (PDT)",
        "From": "Miguel Angel Ajo <majopela@redhat.com>",
        "To": "dev@openvswitch.org",
        "Date": "Thu, 13 Jul 2017 17:02:37 +0000",
        "Message-Id": "<1499965361-32021-4-git-send-email-majopela@redhat.com>",
        "X-Mailer": "git-send-email 1.8.3.1",
        "In-Reply-To": "<1499965361-32021-1-git-send-email-majopela@redhat.com>",
        "References": "<1496930708-15441-1-git-send-email-majopela@redhat.com>\n\t<1499965361-32021-1-git-send-email-majopela@redhat.com>",
        "X-Spam-Status": "No, score=-1.9 required=5.0 tests=BAYES_00,LOTS_OF_MONEY,\n\tRCVD_IN_DNSWL_NONE autolearn=ham version=3.3.1",
        "X-Spam-Checker-Version": "SpamAssassin 3.3.1 (2010-03-16) on\n\tsmtp1.linux-foundation.org",
        "Cc": "\"majopela@redhat.com\" <majopela@redhat.com>",
        "Subject": "[ovs-dev] [PATCH v5 4/8] ovn: l3ha,\n\thandling of multiple gateway chassis",
        "X-BeenThere": "ovs-dev@openvswitch.org",
        "X-Mailman-Version": "2.1.12",
        "Precedence": "list",
        "List-Id": "<ovs-dev.openvswitch.org>",
        "List-Unsubscribe": "<https://mail.openvswitch.org/mailman/options/ovs-dev>,\n\t<mailto:ovs-dev-request@openvswitch.org?subject=unsubscribe>",
        "List-Archive": "<http://mail.openvswitch.org/pipermail/ovs-dev/>",
        "List-Post": "<mailto:ovs-dev@openvswitch.org>",
        "List-Help": "<mailto:ovs-dev-request@openvswitch.org?subject=help>",
        "List-Subscribe": "<https://mail.openvswitch.org/mailman/listinfo/ovs-dev>,\n\t<mailto:ovs-dev-request@openvswitch.org?subject=subscribe>",
        "MIME-Version": "1.0",
        "Content-Type": "text/plain; charset=\"us-ascii\"",
        "Content-Transfer-Encoding": "7bit",
        "Sender": "ovs-dev-bounces@openvswitch.org",
        "Errors-To": "ovs-dev-bounces@openvswitch.org"
    },
    "content": "From: \"majopela@redhat.com\" <majopela@redhat.com>\n\nThis patch handles multiple gateway_chassis within chassisredirect\nports. All the gateway_chassis within chassisredirect port\nwill implement the rules to de-encapsulate incoming packets\nfor such port (please note that later patches in the series\nwill make is_chassis_redirect conditionals aware of the\nMASTER/BACKUP status of the chassis).\n\nHosts targeting a remote chassisredirect port will setup a\nbundle(active_backup, ..) action to each tunnel port, in the given\npriority order. Following patches will enable BFD to detect\nwhen a remote gateway chassis is no longer reachable.\n\nCo-authored-by: Venkata Anil Kommaddi <vkommadi@redhat.com>\nSigned-off-by: Miguel Angel Ajo <majopela@redhat.com>\nSigned-off-by: Venkata Anil Kommaddi <vkommadi@redhat.com>\n---\n ovn/controller/automake.mk      |   2 +\n ovn/controller/binding.c        |  13 +-\n ovn/controller/binding.h        |   1 +\n ovn/controller/gchassis.c       | 176 +++++++++++++++++++++\n ovn/controller/gchassis.h       |  63 ++++++++\n ovn/controller/lflow.c          |   7 +-\n ovn/controller/lport.h          |   4 +\n ovn/controller/ovn-controller.c |  16 +-\n ovn/controller/physical.c       | 130 +++++++++++++---\n ovn/controller/physical.h       |   4 +-\n ovn/northd/ovn-northd.c         |   8 +-\n tests/ovn-northd.at             |   2 -\n tests/ovn.at                    | 329 +++++++++++++++++++++++++++++++++++++++-\n 13 files changed, 718 insertions(+), 37 deletions(-)\n create mode 100644 ovn/controller/gchassis.c\n create mode 100644 ovn/controller/gchassis.h",
    "diff": "diff --git a/ovn/controller/automake.mk b/ovn/controller/automake.mk\nindex 8c6a787..d3828f5 100644\n--- a/ovn/controller/automake.mk\n+++ b/ovn/controller/automake.mk\n@@ -6,6 +6,8 @@ ovn_controller_ovn_controller_SOURCES = \\\n \tovn/controller/chassis.h \\\n \tovn/controller/encaps.c \\\n \tovn/controller/encaps.h \\\n+\tovn/controller/gchassis.c \\\n+\tovn/controller/gchassis.h \\\n \tovn/controller/lflow.c \\\n \tovn/controller/lflow.h \\\n \tovn/controller/lport.c \\\ndiff --git a/ovn/controller/binding.c b/ovn/controller/binding.c\nindex 11145dd..cc4563d 100644\n--- a/ovn/controller/binding.c\n+++ b/ovn/controller/binding.c\n@@ -15,6 +15,7 @@\n \n #include <config.h>\n #include \"binding.h\"\n+#include \"gchassis.h\"\n #include \"lflow.h\"\n #include \"lport.h\"\n \n@@ -26,6 +27,7 @@\n #include \"lib/vswitch-idl.h\"\n #include \"openvswitch/hmap.h\"\n #include \"openvswitch/vlog.h\"\n+#include \"ovn/lib/chassis-index.h\"\n #include \"ovn/lib/ovn-sb-idl.h\"\n #include \"ovn-controller.h\"\n \n@@ -394,12 +396,15 @@ consider_local_datapath(struct controller_ctx *ctx,\n                                false, local_datapaths);\n         }\n     } else if (!strcmp(binding_rec->type, \"chassisredirect\")) {\n-        const char *chassis_id = smap_get(&binding_rec->options,\n-                                          \"redirect-chassis\");\n-        our_chassis = chassis_id && !strcmp(chassis_id, chassis_rec->name);\n-        if (our_chassis) {\n+        if (gateway_chassis_in_pb_contains(binding_rec, chassis_rec)) {\n             add_local_datapath(ldatapaths, lports, binding_rec->datapath,\n                                false, local_datapaths);\n+            /* XXX this should only be set to true if our chassis\n+             * (chassis_rec) is the master for this chassisredirect port\n+             * but for now we'll bind it only when not bound, this is\n+             * handled in subsequent patches */\n+            our_chassis = !binding_rec->chassis ||\n+                chassis_rec == binding_rec->chassis;\n         }\n     } else if (!strcmp(binding_rec->type, \"l3gateway\")) {\n         const char *chassis_id = smap_get(&binding_rec->options,\ndiff --git a/ovn/controller/binding.h b/ovn/controller/binding.h\nindex 3bfa7d1..136b3a7 100644\n--- a/ovn/controller/binding.h\n+++ b/ovn/controller/binding.h\n@@ -20,6 +20,7 @@\n #include <stdbool.h>\n \n struct controller_ctx;\n+struct chassis_index;\n struct hmap;\n struct ldatapath_index;\n struct lport_index;\ndiff --git a/ovn/controller/gchassis.c b/ovn/controller/gchassis.c\nnew file mode 100644\nindex 0000000..f165f59\n--- /dev/null\n+++ b/ovn/controller/gchassis.c\n@@ -0,0 +1,176 @@\n+/* Copyright (c) 2017, Red Hat, Inc.\n+ *\n+ * Licensed under the Apache License, Version 2.0 (the \"License\");\n+ * you may not use this file except in compliance with the License.\n+ * You may obtain a copy of the License at:\n+ *\n+ *     http://www.apache.org/licenses/LICENSE-2.0\n+ *\n+ * Unless required by applicable law or agreed to in writing, software\n+ * distributed under the License is distributed on an \"AS IS\" BASIS,\n+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n+ * See the License for the specific language governing permissions and\n+ * limitations under the License.\n+ */\n+\n+#include <config.h>\n+\n+#include \"gchassis.h\"\n+#include \"lport.h\"\n+#include \"openvswitch/vlog.h\"\n+#include \"ovn/lib/chassis-index.h\"\n+#include \"ovn/lib/ovn-sb-idl.h\"\n+\n+VLOG_DEFINE_THIS_MODULE(gchassis);\n+\n+/* gateway_chassis ordering\n+ */\n+static int\n+compare_chassis_prio_(const void *a_, const void *b_)\n+{\n+    const struct gateway_chassis *gc_a = a_;\n+    const struct gateway_chassis *gc_b = b_;\n+    int prio_diff = gc_b->db->priority - gc_a->db->priority;\n+    if (!prio_diff) {\n+        return strcmp(gc_b->db->name, gc_a->db->name);\n+    }\n+    return prio_diff;\n+}\n+\n+struct ovs_list*\n+gateway_chassis_get_ordered(const struct sbrec_port_binding *binding,\n+                            const struct chassis_index *chassis_index)\n+{\n+    const char *redir_chassis_str;\n+    const struct sbrec_chassis *redirect_chassis = NULL;\n+\n+    /* XXX: redirect-chassis SBDB option handling is supported for backwards\n+     * compatibility with N-1 version of ovn-northd. This support can\n+     * be removed in OVS 2.9 where Gateway_Chassis list on the port binding\n+     * will always be populated by northd */\n+    redir_chassis_str = smap_get(&binding->options, \"redirect-chassis\");\n+\n+    if (redir_chassis_str) {\n+        redirect_chassis = chassis_lookup_by_name(chassis_index,\n+                                                  redir_chassis_str);\n+        if (!redirect_chassis) {\n+            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);\n+            VLOG_WARN_RL(&rl, \"chassis name (%s) in redirect-chassis option \"\n+                              \"of logical port %s not known\",\n+                              redir_chassis_str, binding->logical_port);\n+        }\n+    }\n+\n+    if (!redirect_chassis && binding->n_gateway_chassis == 0) {\n+        return NULL;\n+    }\n+\n+    struct gateway_chassis *gateway_chassis = NULL;\n+    int n = 0;\n+\n+    if (binding->n_gateway_chassis) {\n+        gateway_chassis = xmalloc(sizeof *gateway_chassis *\n+                                  binding->n_gateway_chassis);\n+        for (n = 0; n < binding->n_gateway_chassis; n++) {\n+            gateway_chassis[n].db = binding->gateway_chassis[n];\n+            gateway_chassis[n].virtual_gwc = false;\n+        }\n+        qsort(gateway_chassis, n, sizeof *gateway_chassis,\n+              compare_chassis_prio_);\n+    } else if (redirect_chassis) {\n+        /* When only redirect_chassis is available, return a single\n+         * virtual entry that it's not on OVSDB, this way the code\n+         * handling the returned list will be uniform, regardless\n+         * of gateway_chassis being populated or redirect-chassis option\n+         * being used */\n+        gateway_chassis = xmalloc(sizeof *gateway_chassis);\n+        struct sbrec_gateway_chassis *gwc =\n+            xzalloc(sizeof *gateway_chassis->db);\n+        sbrec_gateway_chassis_init(gwc);\n+        gwc->name = xasprintf(\"%s_%s\", binding->logical_port,\n+                                       redirect_chassis->name);\n+        gwc->chassis = CONST_CAST(struct sbrec_chassis *, redirect_chassis);\n+        gateway_chassis->db = gwc;\n+        gateway_chassis->virtual_gwc = true;\n+        n++;\n+    }\n+\n+    struct ovs_list *list = NULL;\n+    if (n) {\n+        list = xmalloc(sizeof *list);\n+        ovs_list_init(list);\n+\n+        int i;\n+        for (i = 0; i < n; i++) {\n+            ovs_list_push_back(list, &gateway_chassis[i].node);\n+        }\n+    }\n+\n+    return list;\n+}\n+\n+bool\n+gateway_chassis_contains(struct ovs_list *gateway_chassis,\n+                         const struct sbrec_chassis *chassis) {\n+    struct gateway_chassis *chassis_item;\n+    if (gateway_chassis) {\n+        LIST_FOR_EACH (chassis_item, node, gateway_chassis) {\n+            if (chassis_item->db->chassis\n+                && !strcmp(chassis_item->db->chassis->name, chassis->name)) {\n+                return true;\n+            }\n+        }\n+    }\n+    return false;\n+}\n+\n+void\n+gateway_chassis_destroy(struct ovs_list *list)\n+{\n+    if (!list) {\n+        return;\n+    }\n+\n+    /* XXX: This loop is for backwards compatibility with redirect-chassis\n+     * which we insert as a single virtual Gateway_Chassis on the ordered\n+     * list */\n+    struct gateway_chassis *chassis_item;\n+    LIST_FOR_EACH (chassis_item, node, list) {\n+        if (chassis_item->virtual_gwc) {\n+            free(chassis_item->db->name);\n+            free(CONST_CAST(struct sbrec_gateway_chassis *, chassis_item->db));\n+        }\n+    }\n+\n+    free(ovs_list_front(list));\n+    free(list);\n+}\n+\n+bool\n+gateway_chassis_in_pb_contains(const struct sbrec_port_binding *binding,\n+                               const struct sbrec_chassis *chassis)\n+{\n+    if (!binding || !chassis) {\n+        return false;\n+    }\n+\n+    /* XXX: redirect-chassis handling for backwards compatibility,\n+     * with older ovs-northd during upgrade phase, can be removed\n+     * for OVS 2.9 */\n+    const char *redirect_chassis = smap_get(&binding->options,\n+                                            \"redirect-chassis\");\n+    if (binding->n_gateway_chassis) {\n+        int n;\n+        for (n=0; n < binding->n_gateway_chassis; n++) {\n+            if (binding->gateway_chassis[n]->chassis\n+                && !strcmp(binding->gateway_chassis[n]->chassis->name,\n+                           chassis->name)) {\n+                return true;\n+            }\n+        }\n+    } else if (redirect_chassis) {\n+        return !strcmp(redirect_chassis, chassis->name);\n+    }\n+\n+    return false;\n+}\ndiff --git a/ovn/controller/gchassis.h b/ovn/controller/gchassis.h\nnew file mode 100644\nindex 0000000..46d5e42\n--- /dev/null\n+++ b/ovn/controller/gchassis.h\n@@ -0,0 +1,63 @@\n+/* Copyright (c) 2017 Red Hat, Inc.\n+ *\n+ * Licensed under the Apache License, Version 2.0 (the \"License\");\n+ * you may not use this file except in compliance with the License.\n+ * You may obtain a copy of the License at:\n+ *\n+ *     http://www.apache.org/licenses/LICENSE-2.0\n+ *\n+ * Unless required by applicable law or agreed to in writing, software\n+ * distributed under the License is distributed on an \"AS IS\" BASIS,\n+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n+ * See the License for the specific language governing permissions and\n+ * limitations under the License.\n+ */\n+\n+#ifndef OVN_GCHASSIS_H\n+#define OVN_GCHASSIS_H 1\n+\n+#include <stdint.h>\n+#include \"lib/uuid.h\"\n+#include \"openvswitch/hmap.h\"\n+#include \"openvswitch/list.h\"\n+\n+struct chassis_index;\n+struct ovsdb_idl;\n+struct sbrec_chassis;\n+struct sbrec_gateway_chassis;\n+struct sbrec_port_binding;\n+\n+\f\n+/* Gateway_Chassis management\n+ * ==========================\n+ *\n+ * The following structure and methods handle ordering of Gateway_Chassis\n+ * entries in a chassisredirect port. And parsing redirect-chassis option\n+ * for backwards compatibility with older (N-1 version of ovn-northd).\n+ */\n+struct gateway_chassis {\n+    struct ovs_list node;\n+    const struct sbrec_gateway_chassis *db; /* sbrec row for the gwc */\n+    bool virtual_gwc; /* db entry not from SBDB, but from redirect-chassis */\n+};\n+\n+/* Gets, and orders by priority/name the list of Gateway_Chassis */\n+struct ovs_list *gateway_chassis_get_ordered(\n+        const struct sbrec_port_binding *binding,\n+        const struct chassis_index *chassis_index);\n+\n+/* Checks if an specific chassis is contained in the gateway_chassis\n+ * list */\n+bool gateway_chassis_contains(struct ovs_list *gateway_chassis,\n+                              const struct sbrec_chassis *chassis);\n+\n+/* Destroy a gateway_chassis list from memory */\n+void gateway_chassis_destroy(struct ovs_list *list);\n+\n+/* Checks if a chassis is referenced in the port_binding gateway_chassis\n+ * list or redirect-chassis option (backwards compatibility) */\n+bool gateway_chassis_in_pb_contains(\n+        const struct sbrec_port_binding *binding,\n+        const struct sbrec_chassis *chassis);\n+\n+#endif /* ovn/controller/gchassis.h */\ndiff --git a/ovn/controller/lflow.c b/ovn/controller/lflow.c\nindex b1b4b23..8cc5e7e 100644\n--- a/ovn/controller/lflow.c\n+++ b/ovn/controller/lflow.c\n@@ -14,6 +14,7 @@\n  */\n \n #include <config.h>\n+#include \"gchassis.h\"\n #include \"lflow.h\"\n #include \"lport.h\"\n #include \"ofctrl.h\"\n@@ -96,7 +97,11 @@ is_chassis_resident_cb(const void *c_aux_, const char *port_name)\n \n     const struct sbrec_port_binding *pb\n         = lport_lookup_by_name(c_aux->lports, port_name);\n-    return pb && pb->chassis && pb->chassis == c_aux->chassis;\n+    if (pb && pb->chassis && pb->chassis == c_aux->chassis) {\n+        return true;\n+    } else {\n+        return gateway_chassis_in_pb_contains(pb, c_aux->chassis);\n+    }\n }\n \n static bool\ndiff --git a/ovn/controller/lport.h b/ovn/controller/lport.h\nindex fe0e430..8937ecb 100644\n--- a/ovn/controller/lport.h\n+++ b/ovn/controller/lport.h\n@@ -17,10 +17,14 @@\n #define OVN_LPORT_H 1\n \n #include <stdint.h>\n+#include \"lib/uuid.h\"\n #include \"openvswitch/hmap.h\"\n+#include \"openvswitch/list.h\"\n \n struct ovsdb_idl;\n+struct sbrec_chassis;\n struct sbrec_datapath_binding;\n+struct sbrec_port_binding;\n \n /* Database indexes.\n  * =================\ndiff --git a/ovn/controller/ovn-controller.c b/ovn/controller/ovn-controller.c\nindex 45a670b..9fceeca 100644\n--- a/ovn/controller/ovn-controller.c\n+++ b/ovn/controller/ovn-controller.c\n@@ -40,6 +40,7 @@\n #include \"openvswitch/vconn.h\"\n #include \"openvswitch/vlog.h\"\n #include \"ovn/actions.h\"\n+#include \"ovn/lib/chassis-index.h\"\n #include \"ovn/lib/ovn-sb-idl.h\"\n #include \"ovn/lib/ovn-util.h\"\n #include \"patch.h\"\n@@ -148,6 +149,10 @@ update_sb_monitors(struct ovsdb_idl *ovnsb_idl,\n     struct ovsdb_idl_condition mg = OVSDB_IDL_CONDITION_INIT(&mg);\n     struct ovsdb_idl_condition dns = OVSDB_IDL_CONDITION_INIT(&dns);\n     sbrec_port_binding_add_clause_type(&pb, OVSDB_F_EQ, \"patch\");\n+    /* XXX: We can optimize this, if we find a way to only monitor\n+     * ports that have a Gateway_Chassis that point's to our own\n+     * chassis */\n+    sbrec_port_binding_add_clause_type(&pb, OVSDB_F_EQ, \"chassisredirect\");\n     if (chassis) {\n         /* This should be mostly redundant with the other clauses for port\n          * bindings, but it allows us to catch any ports that are assigned to\n@@ -165,10 +170,6 @@ update_sb_monitors(struct ovsdb_idl *ovnsb_idl,\n         sbrec_port_binding_add_clause_options(&pb, OVSDB_F_INCLUDES, &l2);\n         const struct smap l3 = SMAP_CONST1(&l3, \"l3gateway-chassis\", id);\n         sbrec_port_binding_add_clause_options(&pb, OVSDB_F_INCLUDES, &l3);\n-        const struct smap redirect = SMAP_CONST1(&redirect,\n-                                                 \"redirect-chassis\", id);\n-        sbrec_port_binding_add_clause_options(&pb, OVSDB_F_INCLUDES,\n-                                              &redirect);\n     }\n     if (local_ifaces) {\n         const char *name;\n@@ -627,9 +628,12 @@ main(int argc, char *argv[])\n         struct ldatapath_index ldatapaths;\n         struct lport_index lports;\n         struct mcgroup_index mcgroups;\n+        struct chassis_index chassis_index;\n+\n         ldatapath_index_init(&ldatapaths, ctx.ovnsb_idl);\n         lport_index_init(&lports, ctx.ovnsb_idl);\n         mcgroup_index_init(&mcgroups, ctx.ovnsb_idl);\n+        chassis_index_init(&chassis_index, ctx.ovnsb_idl);\n \n         const struct sbrec_chassis *chassis = NULL;\n         if (chassis_id) {\n@@ -662,7 +666,8 @@ main(int argc, char *argv[])\n \n                     physical_run(&ctx, mff_ovn_geneve,\n                                  br_int, chassis, &ct_zones, &lports,\n-                                 &flow_table, &local_datapaths, &local_lports);\n+                                 &flow_table, &local_datapaths, &local_lports,\n+                                 &chassis_index);\n \n                     ofctrl_put(&flow_table, &pending_ct_zones,\n                                get_nb_cfg(ctx.ovnsb_idl));\n@@ -709,6 +714,7 @@ main(int argc, char *argv[])\n         mcgroup_index_destroy(&mcgroups);\n         lport_index_destroy(&lports);\n         ldatapath_index_destroy(&ldatapaths);\n+        chassis_index_destroy(&chassis_index);\n \n         sset_destroy(&local_lports);\n \ndiff --git a/ovn/controller/physical.c b/ovn/controller/physical.c\nindex 4f0011f..0588d00 100644\n--- a/ovn/controller/physical.c\n+++ b/ovn/controller/physical.c\n@@ -17,16 +17,21 @@\n #include \"binding.h\"\n #include \"byte-order.h\"\n #include \"flow.h\"\n+#include \"gchassis.h\"\n #include \"lflow.h\"\n #include \"lport.h\"\n+#include \"lib/bundle.h\"\n #include \"lib/poll-loop.h\"\n+#include \"lib/uuid.h\"\n #include \"ofctrl.h\"\n+#include \"openvswitch/list.h\"\n #include \"openvswitch/hmap.h\"\n #include \"openvswitch/match.h\"\n #include \"openvswitch/ofp-actions.h\"\n #include \"openvswitch/ofpbuf.h\"\n #include \"openvswitch/vlog.h\"\n #include \"ovn-controller.h\"\n+#include \"ovn/lib/chassis-index.h\"\n #include \"ovn/lib/ovn-sb-idl.h\"\n #include \"ovn/lib/ovn-util.h\"\n #include \"physical.h\"\n@@ -289,6 +294,7 @@ static void\n consider_port_binding(enum mf_field_id mff_ovn_geneve,\n                       const struct simap *ct_zones,\n                       const struct lport_index *lports,\n+                      const struct chassis_index *chassis_index,\n                       struct hmap *local_datapaths,\n                       const struct sbrec_port_binding *binding,\n                       const struct sbrec_chassis *chassis,\n@@ -353,8 +359,18 @@ consider_port_binding(enum mf_field_id mff_ovn_geneve,\n         return;\n     }\n \n+    struct ovs_list *gateway_chassis\n+        = gateway_chassis_get_ordered(binding, chassis_index);\n+\n+    /* XXX: later in the series we should insert the next flows only\n+     * on the active chassis, and not on all of them. This is useful to\n+     * check that the BFD implementation on following patches has\n+     * an effect and routes packet by the chassis which is responding,\n+     * but later on we should not create those flows on all the\n+     * chassis of the gateway_chassis list */\n     if (!strcmp(binding->type, \"chassisredirect\")\n-        && binding->chassis == chassis) {\n+        && (binding->chassis == chassis\n+            || gateway_chassis_contains(gateway_chassis, chassis))) {\n \n         /* Table 33, priority 100.\n          * =======================\n@@ -413,7 +429,8 @@ consider_port_binding(enum mf_field_id mff_ovn_geneve,\n \n         ofctrl_add_flow(flow_table, OFTABLE_LOCAL_OUTPUT, 100, 0,\n                         &match, ofpacts_p);\n-        return;\n+\n+        goto out;\n     }\n \n     /* Find the OpenFlow port for the logical port, as 'ofport'.  This is\n@@ -442,7 +459,7 @@ consider_port_binding(enum mf_field_id mff_ovn_geneve,\n     bool is_remote = false;\n     if (binding->parent_port && *binding->parent_port) {\n         if (!binding->tag) {\n-            return;\n+            goto out;\n         }\n         ofport = u16_to_ofp(simap_get(&localvif_to_ofport,\n                                       binding->parent_port));\n@@ -460,27 +477,34 @@ consider_port_binding(enum mf_field_id mff_ovn_geneve,\n         }\n     }\n \n+    bool is_ha_remote = false;\n     const struct chassis_tunnel *tun = NULL;\n     const struct sbrec_port_binding *localnet_port =\n         get_localnet_port(local_datapaths, dp_key);\n     if (!ofport) {\n         /* It is remote port, may be reached by tunnel or localnet port */\n         is_remote = true;\n-        if (!binding->chassis) {\n-            return;\n-        }\n         if (localnet_port) {\n             ofport = u16_to_ofp(simap_get(&localvif_to_ofport,\n                                           localnet_port->logical_port));\n             if (!ofport) {\n-                return;\n+                goto out;\n             }\n         } else {\n-            tun = chassis_tunnel_find(binding->chassis->name);\n-            if (!tun) {\n-                return;\n+            if (!gateway_chassis || ovs_list_is_short(gateway_chassis)) {\n+                /* It's on a single remote chassis */\n+                if (!binding->chassis) {\n+                    goto out;\n+                }\n+                tun = chassis_tunnel_find(binding->chassis->name);\n+                if (!tun) {\n+                    goto out;\n+                }\n+                ofport = tun->ofport;\n+            } else {\n+                /* It's distributed across the \"gateway_chassis\" list */\n+                is_ha_remote = true;\n             }\n-            ofport = tun->ofport;\n         }\n     }\n \n@@ -575,7 +599,7 @@ consider_port_binding(enum mf_field_id mff_ovn_geneve,\n         }\n         ofctrl_add_flow(flow_table, OFTABLE_LOG_TO_PHY, 100, 0,\n                         &match, ofpacts_p);\n-    } else if (!tun) {\n+    } else if (!tun && !is_ha_remote) {\n         /* Remote port connected by localnet port */\n         /* Table 33, priority 100.\n          * =======================\n@@ -615,14 +639,84 @@ consider_port_binding(enum mf_field_id mff_ovn_geneve,\n         match_set_metadata(&match, htonll(dp_key));\n         match_set_reg(&match, MFF_LOG_OUTPORT - MFF_REG0, port_key);\n \n-        put_encapsulation(mff_ovn_geneve, tun, binding->datapath,\n-                          port_key, ofpacts_p);\n+        if (!is_ha_remote) {\n+            /* Setup encapsulation */\n+            put_encapsulation(mff_ovn_geneve, tun, binding->datapath,\n+                              port_key, ofpacts_p);\n+            /* Output to tunnel. */\n+            ofpact_put_OUTPUT(ofpacts_p)->port = ofport;\n+        } else {\n+            struct gateway_chassis *gwc;\n+            /* Make sure all tunnel endpoints use the same encapsulation,\n+             * and set it up */\n+            LIST_FOR_EACH (gwc, node, gateway_chassis) {\n+                if (gwc->db->chassis) {\n+                    if (!tun) {\n+                        tun = chassis_tunnel_find(gwc->db->chassis->name);\n+                    } else {\n+                        struct chassis_tunnel *chassis_tunnel =\n+                            chassis_tunnel_find(gwc->db->chassis->name);\n+                        if (chassis_tunnel &&\n+                            tun->type != chassis_tunnel->type) {\n+                            static struct vlog_rate_limit rl =\n+                                VLOG_RATE_LIMIT_INIT(1, 1);\n+                            VLOG_ERR_RL(&rl, \"Port %s has Gateway_Chassis \"\n+                                             \"with mixed encapsulations, only \"\n+                                             \"uniform encapsulations are \"\n+                                             \"supported.\",\n+                                        binding->logical_port);\n+                            goto out;\n+                        }\n+                    }\n+                }\n+            }\n+            if (!tun) {\n+                static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);\n+                VLOG_ERR_RL(&rl, \"No tunnel endpoint found for gateways in \"\n+                                 \"Gateway_Chassis of port %s\",\n+                            binding->logical_port);\n+                goto out;\n+            }\n \n-        /* Output to tunnel. */\n-        ofpact_put_OUTPUT(ofpacts_p)->port = ofport;\n+            put_encapsulation(mff_ovn_geneve, tun, binding->datapath,\n+                              port_key, ofpacts_p);\n+\n+            /* Output to tunnels with active/backup */\n+            struct ofpact_bundle *bundle = ofpact_put_BUNDLE(ofpacts_p);\n+\n+            LIST_FOR_EACH (gwc, node, gateway_chassis) {\n+                if (gwc->db->chassis) {\n+                    tun = chassis_tunnel_find(gwc->db->chassis->name);\n+                    if (!tun) {\n+                        continue;\n+                    }\n+                    if (bundle->n_slaves >= BUNDLE_MAX_SLAVES) {\n+                        static struct vlog_rate_limit rl =\n+                                VLOG_RATE_LIMIT_INIT(1, 1);\n+                        VLOG_WARN_RL(&rl, \"Remote endpoints for port beyond \"\n+                                          \"BUNDLE_MAX_SLAVES\");\n+                        break;\n+                    }\n+                    ofpbuf_put(ofpacts_p, &tun->ofport,\n+                               sizeof tun->ofport);\n+                    bundle->n_slaves++;\n+                }\n+            }\n+\n+            ofpact_finish_BUNDLE(ofpacts_p, &bundle);\n+            bundle->algorithm = NX_BD_ALG_ACTIVE_BACKUP;\n+            /* Although ACTIVE_BACKUP bundle algorithm seems to ignore\n+             * the next two fields, those are always set */\n+            bundle->basis = 0;\n+            bundle->fields = NX_HASH_FIELDS_ETH_SRC;\n+        }\n         ofctrl_add_flow(flow_table, OFTABLE_REMOTE_OUTPUT, 100, 0,\n                         &match, ofpacts_p);\n     }\n+out:\n+    if (gateway_chassis) {\n+        gateway_chassis_destroy(gateway_chassis);\n+    }\n }\n \n static void\n@@ -770,7 +864,8 @@ physical_run(struct controller_ctx *ctx, enum mf_field_id mff_ovn_geneve,\n              const struct sbrec_chassis *chassis,\n              const struct simap *ct_zones, struct lport_index *lports,\n              struct hmap *flow_table, struct hmap *local_datapaths,\n-             const struct sset *local_lports)\n+             const struct sset *local_lports,\n+             struct chassis_index *chassis_index)\n {\n \n     /* This bool tracks physical mapping changes. */\n@@ -892,6 +987,7 @@ physical_run(struct controller_ctx *ctx, enum mf_field_id mff_ovn_geneve,\n     const struct sbrec_port_binding *binding;\n     SBREC_PORT_BINDING_FOR_EACH (binding, ctx->ovnsb_idl) {\n         consider_port_binding(mff_ovn_geneve, ct_zones, lports,\n+                              chassis_index,\n                               local_datapaths, binding, chassis,\n                               &ofpacts, flow_table);\n     }\ndiff --git a/ovn/controller/physical.h b/ovn/controller/physical.h\nindex 66aa80e..9019621 100644\n--- a/ovn/controller/physical.h\n+++ b/ovn/controller/physical.h\n@@ -33,6 +33,7 @@ struct ovsdb_idl;\n struct ovsrec_bridge;\n struct simap;\n struct sset;\n+struct chassis_index;\n \n /* OVN Geneve option information.\n  *\n@@ -47,6 +48,7 @@ void physical_run(struct controller_ctx *, enum mf_field_id mff_ovn_geneve,\n                   const struct sbrec_chassis *chassis,\n                   const struct simap *ct_zones, struct lport_index *,\n                   struct hmap *flow_table, struct hmap *local_datapaths,\n-                  const struct sset *local_lports);\n+                  const struct sset *local_lports,\n+                  struct chassis_index *chassis_index);\n \n #endif /* ovn/physical.h */\ndiff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c\nindex 990d268..9a1e6c1 100644\n--- a/ovn/northd/ovn-northd.c\n+++ b/ovn/northd/ovn-northd.c\n@@ -1869,13 +1869,9 @@ ovn_port_update_sbrec(struct northd_context *ctx,\n                 }\n \n             } else if (redirect_chassis) {\n-                /* XXX: Keep the \"redirect-chassis\" option on the Port_Binding\n-                 * for compatibility purposes until ovn-controller implements\n-                 * Gateway_Chassis handling */\n-                smap_add(&new, \"redirect-chassis\", redirect_chassis);\n-\n                 /* Handle ports that had redirect-chassis option attached\n-                 * to them for backwards compatibility */\n+                 * to them, and for backwards compatibility convert them\n+                 * to a single Gateway_Chassis entry */\n                 const struct sbrec_chassis *chassis =\n                     chassis_lookup_by_name(chassis_index, redirect_chassis);\n                 if (chassis) {\ndiff --git a/tests/ovn-northd.at b/tests/ovn-northd.at\nindex d0e8bcd..fc9eda8 100644\n--- a/tests/ovn-northd.at\n+++ b/tests/ovn-northd.at\n@@ -73,8 +73,6 @@ ovn-nbctl --wait=sb lrp-add R1 bob 00:00:02:01:02:03 172.16.1.1/24 \\\n \n gwc1_uuid=`ovn-sbctl --bare --columns _uuid find Gateway_Chassis name=\"bob_gw1\"`\n \n-AT_CHECK([ovn-sbctl --bare --columns options find port_binding logical_port=\"cr-bob\" | grep 'redirect-chassis=gw1' | wc -l], [0], [1\n-])\n AT_CHECK([ovn-sbctl --bare --columns gateway_chassis find port_binding logical_port=\"cr-bob\" | grep $gwc1_uuid | wc -l], [0], [1\n ])\n \ndiff --git a/tests/ovn.at b/tests/ovn.at\nindex f3e6b4b..0cbbc27 100644\n--- a/tests/ovn.at\n+++ b/tests/ovn.at\n@@ -6812,6 +6812,189 @@ OVS_APP_EXIT_AND_WAIT([ovs-vswitchd])\n OVS_APP_EXIT_AND_WAIT([ovsdb-server])\n AT_CLEANUP\n \n+AT_SETUP([ovn -- packet test with HA distributed router gateway port])\n+AT_SKIP_IF([test $HAVE_PYTHON = no])\n+ovn_start\n+\n+net_add n1\n+\n+sim_add hv1\n+as hv1\n+ovs-vsctl add-br br-phys\n+ovn_attach n1 br-phys 192.168.0.1\n+ovs-vsctl -- add-port br-int hv1-vif1 -- \\\n+    set interface hv1-vif1 external-ids:iface-id=foo1 \\\n+    options:tx_pcap=hv1/vif1-tx.pcap \\\n+    options:rxq_pcap=hv1/vif1-rx.pcap \\\n+    ofport-request=1\n+\n+sim_add gw1\n+as gw1\n+ovs-vsctl add-br br-phys\n+ovn_attach n1 br-phys 192.168.0.2\n+\n+sim_add gw2\n+as gw2\n+ovs-vsctl add-br br-phys\n+ovn_attach n1 br-phys 192.168.0.4\n+\n+sim_add ext1\n+as ext1\n+ovs-vsctl add-br br-phys\n+ovn_attach n1 br-phys 192.168.0.3\n+ovs-vsctl -- add-port br-int ext1-vif1 -- \\\n+    set interface ext1-vif1 external-ids:iface-id=outside1 \\\n+    options:tx_pcap=ext1/vif1-tx.pcap \\\n+    options:rxq_pcap=ext1/vif1-rx.pcap \\\n+    ofport-request=1\n+\n+# Pre-populate the hypervisors' ARP tables so that we don't lose any\n+# packets for ARP resolution (native tunneling doesn't queue packets\n+# for ARP resolution).\n+ovn_populate_arp\n+\n+ovn-nbctl create Logical_Router name=R1\n+\n+ovn-nbctl ls-add foo\n+ovn-nbctl ls-add alice\n+ovn-nbctl ls-add outside\n+\n+# Connect foo to R1\n+ovn-nbctl lrp-add R1 foo 00:00:01:01:02:03 192.168.1.1/24\n+ovn-nbctl lsp-add foo rp-foo -- set Logical_Switch_Port rp-foo \\\n+    type=router options:router-port=foo \\\n+    -- lsp-set-addresses rp-foo router\n+\n+# Connect alice to R1 as distributed router gateway port on gw1\n+ovn-nbctl lrp-add R1 alice 00:00:02:01:02:03 172.16.1.1/24\n+\n+ovn-nbctl \\\n+    --id=@gc0 create Gateway_Chassis name=alice_gw1 \\\n+                                     chassis_name=gw1 \\\n+                                     priority=20 -- \\\n+    --id=@gc1 create Gateway_Chassis name=alice_gw2 \\\n+                                     chassis_name=gw2 \\\n+                                     priority=10 -- \\\n+    set Logical_Router_Port alice 'gateway_chassis=[@gc0,@gc1]'\n+\n+ovn-nbctl lsp-add alice rp-alice -- set Logical_Switch_Port rp-alice \\\n+    type=router options:router-port=alice \\\n+    -- lsp-set-addresses rp-alice router\n+\n+# Create logical port foo1 in foo\n+ovn-nbctl lsp-add foo foo1 \\\n+-- lsp-set-addresses foo1 \"f0:00:00:01:02:03 192.168.1.2\"\n+\n+# Create logical port outside1 in outside\n+ovn-nbctl lsp-add outside outside1 \\\n+-- lsp-set-addresses outside1 \"f0:00:00:01:02:04 172.16.1.3\"\n+\n+# Create localnet port in alice\n+ovn-nbctl lsp-add alice ln-alice\n+ovn-nbctl lsp-set-addresses ln-alice unknown\n+ovn-nbctl lsp-set-type ln-alice localnet\n+ovn-nbctl lsp-set-options ln-alice network_name=phys\n+\n+# Create localnet port in outside\n+ovn-nbctl lsp-add outside ln-outside\n+ovn-nbctl lsp-set-addresses ln-outside unknown\n+ovn-nbctl lsp-set-type ln-outside localnet\n+ovn-nbctl lsp-set-options ln-outside network_name=phys\n+\n+# Create bridge-mappings on gw1, gw2 and ext1, hv1 doesn't need\n+# mapping to the external network, is the one generating packets\n+as gw1 ovs-vsctl set open . external-ids:ovn-bridge-mappings=phys:br-phys\n+as gw2 ovs-vsctl set open . external-ids:ovn-bridge-mappings=phys:br-phys\n+as ext1 ovs-vsctl set open . external-ids:ovn-bridge-mappings=phys:br-phys\n+\n+AT_CHECK([ovn-nbctl --timeout=3 --wait=sb sync], [0], [ignore])\n+\n+# Allow some time for ovn-northd and ovn-controller to catch up.\n+# XXX This should be more systematic.\n+sleep 2\n+\n+ip_to_hex() {\n+    printf \"%02x%02x%02x%02x\" \"$@\"\n+}\n+\n+reset_pcap_file() {\n+    local iface=$1\n+    local pcap_file=$2\n+    ovs-vsctl -- set Interface $iface options:tx_pcap=dummy-tx.pcap \\\n+options:rxq_pcap=dummy-rx.pcap\n+    rm -f ${pcap_file}*.pcap\n+    ovs-vsctl -- set Interface $iface options:tx_pcap=${pcap_file}-tx.pcap \\\n+options:rxq_pcap=${pcap_file}-rx.pcap\n+}\n+\n+test_ip_packet()\n+{\n+    local active_gw=$1\n+    local backup_gw=$2\n+\n+    # Send ip packet between foo1 and outside1\n+    src_mac=\"f00000010203\" # foo1 mac\n+    dst_mac=\"000001010203\" # rp-foo mac (internal router leg)\n+    src_ip=`ip_to_hex 192 168 1 2`\n+    dst_ip=`ip_to_hex 172 16 1 3`\n+    packet=${dst_mac}${src_mac}08004500001c0000000040110000${src_ip}${dst_ip}0035111100080000\n+\n+    # ARP request packet to expect at outside1\n+    #arp_request=ffffffffffff${src_mac}08060001080006040001${src_mac}${src_ip}000000000000${dst_ip}\n+\n+    as hv1 ovs-appctl netdev-dummy/receive hv1-vif1 $packet\n+\n+    # Send ARP reply from outside1 back to the router\n+    # XXX: note, we could avoid this if we plug this port into a netns\n+    # and setup the IP address into the port, so the kernel would simply reply\n+    src_mac=\"000002010203\"\n+    reply_mac=\"f00000010204\"\n+    dst_ip=`ip_to_hex 172 16 1 3`\n+    src_ip=`ip_to_hex 172 16 1 1`\n+    arp_reply=${src_mac}${reply_mac}08060001080006040002${reply_mac}${dst_ip}${src_mac}${src_ip}\n+\n+    as ext1 ovs-appctl netdev-dummy/receive ext1-vif1 $arp_reply\n+\n+    # Packet to Expect at ext1 chassis, outside1 port\n+    src_mac=\"000002010203\"\n+    dst_mac=\"f00000010204\"\n+    src_ip=`ip_to_hex 192 168 1 2`\n+    dst_ip=`ip_to_hex 172 16 1 3`\n+    expected=${dst_mac}${src_mac}08004500001c000000003f110100${src_ip}${dst_ip}0035111100080000\n+    echo $expected > ext1-vif1.expected\n+\n+    as $active_gw reset_pcap_file br-phys_n1 $active_gw/br-phys_n1\n+    as $backup_gw reset_pcap_file br-phys_n1 $backup_gw/br-phys_n1\n+    as ext1 reset_pcap_file ext1-vif1 ext1/vif1\n+\n+    # Resend packet from foo1 to outside1\n+    as hv1 ovs-appctl netdev-dummy/receive hv1-vif1 $packet\n+\n+    sleep 1\n+\n+    OVN_CHECK_PACKETS([ext1/vif1-tx.pcap], [ext1-vif1.expected])\n+    $PYTHON \"$top_srcdir/utilities/ovs-pcap.in\" $active_gw/br-phys_n1-tx.pcap  > packets\n+    AT_CHECK([grep $expected packets | sort], [0], [expout])\n+    $PYTHON \"$top_srcdir/utilities/ovs-pcap.in\" $backup_gw/br-phys_n1-tx.pcap  > packets\n+    AT_CHECK([grep $expected packets | sort], [0], [])\n+}\n+\n+test_ip_packet gw1 gw2\n+\n+ovn-nbctl --wait=hv \\\n+    --id=@gc0 create Gateway_Chassis name=alice_gw1 \\\n+                                     chassis_name=gw1 \\\n+                                     priority=10 -- \\\n+    --id=@gc1 create Gateway_Chassis name=alice_gw2 \\\n+                                     chassis_name=gw2 \\\n+                                     priority=20 -- \\\n+    set Logical_Router_Port alice 'gateway_chassis=[@gc0,@gc1]'\n+\n+test_ip_packet gw2 gw1\n+\n+OVN_CLEANUP([hv1],[gw1],[gw2],[ext1])\n+AT_CLEANUP\n+\n AT_SETUP([ovn -- 1 LR with distributed router gateway port])\n AT_SKIP_IF([test $HAVE_PYTHON = no])\n ovn_start\n@@ -6937,7 +7120,11 @@ ovn-sbctl dump-flows\n echo \"---------------------\"\n ovn-sbctl list chassis\n ovn-sbctl list encap\n-echo \"---------------------\"\n+echo \"------ Gateway_Chassis dump (SBDB) -------\"\n+ovn-sbctl list Gateway_Chassis\n+echo \"------ Port_Binding chassisredirect -------\"\n+ovn-sbctl find Port_Binding type=chassisredirect\n+echo \"-------------------------------------------\"\n \n echo \"------ hv1 dump ----------\"\n as hv1 ovs-ofctl show br-int\n@@ -6950,6 +7137,7 @@ as hv3 ovs-ofctl show br-int\n as hv3 ovs-ofctl dump-flows br-int\n echo \"--------------------------\"\n \n+\n # Check that redirect mapping is programmed only on hv2\n AT_CHECK([as hv1 ovs-ofctl dump-flows br-int table=33 | grep =0x3,metadata=0x1 | wc -l], [0], [0\n ])\n@@ -7504,3 +7692,142 @@ done\n OVN_CLEANUP([hv1],[hv2])\n \n AT_CLEANUP\n+\n+AT_SETUP([ovn -- 1 LR with HA distributed router gateway port])\n+AT_SKIP_IF([test $HAVE_PYTHON = no])\n+ovn_start\n+\n+net_add n1\n+\n+# create gateways with external network connectivity\n+\n+for i in 1 2; do\n+    sim_add gw$i\n+    as gw$i\n+    ovs-vsctl add-br br-phys\n+    ovn_attach n1 br-phys 192.168.0.$i\n+    ovs-vsctl set open . external-ids:ovn-bridge-mappings=phys:br-phys\n+done\n+\n+ovn-nbctl ls-add inside\n+ovn-nbctl ls-add outside\n+\n+# create hypervisors with a vif port each to an internal network\n+\n+for i in 1 2; do\n+    sim_add hv$i\n+    as hv$i\n+    ovs-vsctl add-br br-phys\n+    ovn_attach n1 br-phys 192.168.0.1$i\n+    ovs-vsctl -- add-port br-int hv$i-vif1 -- \\\n+        set interface hv$i-vif1 external-ids:iface-id=inside$i \\\n+        options:tx_pcap=hv$i/vif1-tx.pcap \\\n+        options:rxq_pcap=hv$i/vif1-rx.pcap \\\n+        ofport-request=1\n+\n+        ovn-nbctl lsp-add inside inside$i \\\n+            -- lsp-set-addresses inside$i \"f0:00:00:01:22:$i 192.168.1.10$i\"\n+\n+done\n+\n+ovn_populate_arp\n+\n+ovn-nbctl create Logical_Router name=R1\n+\n+# Connect inside to R1\n+ovn-nbctl lrp-add R1 inside 00:00:01:01:02:03 192.168.1.1/24\n+ovn-nbctl lsp-add inside rp-inside -- set Logical_Switch_Port rp-inside \\\n+    type=router options:router-port=inside \\\n+    -- lsp-set-addresses rp-inside router\n+\n+# Connect outside to R1 as distributed router gateway port on gw1+gw2\n+ovn-nbctl lrp-add R1 outside 00:00:02:01:02:04 192.168.0.101/24\n+\n+ovn-nbctl --id=@gc0 create Gateway_Chassis \\\n+                    name=outside_gw1 chassis_name=gw1 priority=20 -- \\\n+          --id=@gc1 create Gateway_Chassis \\\n+                    name=outside_gw2 chassis_name=gw2 priority=10 -- \\\n+          set Logical_Router_Port outside 'gateway_chassis=[@gc0,@gc1]'\n+\n+ovn-nbctl lsp-add outside rp-outside -- set Logical_Switch_Port rp-outside \\\n+    type=router options:router-port=outside \\\n+    -- lsp-set-addresses rp-outside router\n+\n+# Create localnet port in outside\n+ovn-nbctl lsp-add outside ln-outside\n+ovn-nbctl lsp-set-addresses ln-outside unknown\n+ovn-nbctl lsp-set-type ln-outside localnet\n+ovn-nbctl lsp-set-options ln-outside network_name=phys\n+\n+# Allow some time for ovn-northd and ovn-controller to catch up.\n+# XXX This should be more systematic.\n+ovn-nbctl --wait=hv sync\n+\n+echo \"---------NB dump-----\"\n+ovn-nbctl show\n+echo \"---------------------\"\n+ovn-nbctl list logical_router\n+echo \"---------------------\"\n+ovn-nbctl list logical_router_port\n+echo \"---------------------\"\n+\n+echo \"---------SB dump-----\"\n+ovn-sbctl list datapath_binding\n+echo \"---------------------\"\n+ovn-sbctl list port_binding\n+echo \"---------------------\"\n+ovn-sbctl dump-flows\n+echo \"---------------------\"\n+ovn-sbctl list chassis\n+ovn-sbctl list encap\n+echo \"---------------------\"\n+echo \"------ Gateway_Chassis dump (SBDB) -------\"\n+ovn-sbctl list Gateway_Chassis\n+echo \"------ Port_Binding chassisredirect -------\"\n+ovn-sbctl find Port_Binding type=chassisredirect\n+echo \"-------------------------------------------\"\n+\n+\n+hv1_gw1_ofport=$(as hv1 ovs-vsctl --bare --columns ofport find Interface name=ovn-gw1-0)\n+hv1_gw2_ofport=$(as hv1 ovs-vsctl --bare --columns ofport find Interface name=ovn-gw2-0)\n+hv2_gw1_ofport=$(as hv2 ovs-vsctl --bare --columns ofport find Interface name=ovn-gw1-0)\n+hv2_gw2_ofport=$(as hv2 ovs-vsctl --bare --columns ofport find Interface name=ovn-gw2-0)\n+\n+echo $hv1_gw1_ofport\n+echo $hv1_gw2_ofport\n+echo $hv2_gw1_ofport\n+echo $hv2_gw2_ofport\n+\n+echo \"--- hv1 ---\"\n+as hv1 ovs-ofctl dump-flows br-int table=32\n+\n+echo \"--- hv2 ---\"\n+as hv2 ovs-ofctl dump-flows br-int table=32\n+\n+AT_CHECK([as hv1 ovs-ofctl dump-flows br-int table=32 | grep active_backup | grep slaves:$hv1_gw1_ofport,$hv1_gw2_ofport | wc -l], [0], [1\n+])\n+\n+AT_CHECK([as hv2 ovs-ofctl dump-flows br-int table=32 | grep active_backup | grep slaves:$hv2_gw1_ofport,$hv2_gw2_ofport | wc -l], [0], [1\n+])\n+\n+# set higher priority to gw2 instead of gw1, and check for changes\n+\n+ovn-nbctl --id=@gc0 create Gateway_Chassis \\\n+                    name=outside_gw1 chassis_name=gw1 priority=10 -- \\\n+          --id=@gc1 create Gateway_Chassis \\\n+                    name=outside_gw2 chassis_name=gw2 priority=20 -- \\\n+          set Logical_Router_Port outside 'gateway_chassis=[@gc0,@gc1]'\n+\n+# XXX: Let the change propagate down to the ovn-controllers\n+ovn-nbctl --wait=hv sync\n+\n+AT_CHECK([as hv1 ovs-ofctl dump-flows br-int table=32 | grep active_backup | grep slaves:$hv1_gw2_ofport,$hv1_gw1_ofport | wc -l], [0], [1\n+])\n+\n+AT_CHECK([as hv2 ovs-ofctl dump-flows br-int table=32 | grep active_backup | grep slaves:$hv2_gw2_ofport,$hv2_gw1_ofport | wc -l], [0], [1\n+])\n+\n+\n+OVN_CLEANUP([gw1],[gw2],[hv1],[hv2])\n+\n+AT_CLEANUP\n",
    "prefixes": [
        "ovs-dev",
        "v5",
        "4/8"
    ]
}