get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

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

{
    "id": 2225962,
    "url": "http://patchwork.ozlabs.org/api/1.2/patches/2225962/?format=api",
    "web_url": "http://patchwork.ozlabs.org/project/ovn/patch/20260421211209.1974247-1-lucas.vdias@luizalabs.com/",
    "project": {
        "id": 68,
        "url": "http://patchwork.ozlabs.org/api/1.2/projects/68/?format=api",
        "name": "Open Virtual Network development",
        "link_name": "ovn",
        "list_id": "ovs-dev.openvswitch.org",
        "list_email": "ovs-dev@openvswitch.org",
        "web_url": "http://openvswitch.org/",
        "scm_url": "",
        "webscm_url": "",
        "list_archive_url": "",
        "list_archive_url_format": "",
        "commit_url_format": ""
    },
    "msgid": "<20260421211209.1974247-1-lucas.vdias@luizalabs.com>",
    "list_archive_url": null,
    "date": "2026-04-21T21:12:09",
    "name": "[ovs-dev,v2,2/2] northd: Incremental processing for static routes.",
    "commit_ref": null,
    "pull_url": null,
    "state": "new",
    "archived": false,
    "hash": "3d45d1b4e6e1c271646716170355e8cd02081e3e",
    "submitter": {
        "id": 90169,
        "url": "http://patchwork.ozlabs.org/api/1.2/people/90169/?format=api",
        "name": "Lucas Vargas Dias",
        "email": "lucas.vdias@luizalabs.com"
    },
    "delegate": null,
    "mbox": "http://patchwork.ozlabs.org/project/ovn/patch/20260421211209.1974247-1-lucas.vdias@luizalabs.com/mbox/",
    "series": [
        {
            "id": 500896,
            "url": "http://patchwork.ozlabs.org/api/1.2/series/500896/?format=api",
            "web_url": "http://patchwork.ozlabs.org/project/ovn/list/?series=500896",
            "date": "2026-04-21T21:11:55",
            "name": "[ovs-dev,v2,1/2] northd: Use uuid hash from source of parsed route.",
            "version": 2,
            "mbox": "http://patchwork.ozlabs.org/series/500896/mbox/"
        }
    ],
    "comments": "http://patchwork.ozlabs.org/api/patches/2225962/comments/",
    "check": "fail",
    "checks": "http://patchwork.ozlabs.org/api/patches/2225962/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@legolas.ozlabs.org",
            "ovs-dev@lists.linuxfoundation.org"
        ],
        "Authentication-Results": [
            "legolas.ozlabs.org;\n\tdkim=fail reason=\"signature verification failed\" (1024-bit key;\n unprotected) header.d=luizalabs.com header.i=@luizalabs.com\n header.a=rsa-sha256 header.s=google header.b=OhPC8Qfo;\n\tdkim-atps=neutral",
            "legolas.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=openvswitch.org\n (client-ip=2605:bc80:3010::133; helo=smtp2.osuosl.org;\n envelope-from=ovs-dev-bounces@openvswitch.org; receiver=patchwork.ozlabs.org)",
            "smtp2.osuosl.org;\n\tdkim=fail reason=\"signature verification failed\" (1024-bit key)\n header.d=luizalabs.com header.i=@luizalabs.com header.a=rsa-sha256\n header.s=google header.b=OhPC8Qfo",
            "smtp3.osuosl.org; dmarc=pass (p=quarantine dis=none)\n header.from=luizalabs.com",
            "smtp3.osuosl.org;\n dkim=pass (1024-bit key) header.d=luizalabs.com header.i=@luizalabs.com\n header.a=rsa-sha256 header.s=google header.b=OhPC8Qfo"
        ],
        "Received": [
            "from smtp2.osuosl.org (smtp2.osuosl.org [IPv6:2605:bc80:3010::133])\n\t(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n\t key-exchange x25519 server-signature ECDSA (secp384r1) server-digest SHA384)\n\t(No client certificate requested)\n\tby legolas.ozlabs.org (Postfix) with ESMTPS id 4g0ZnF4Q3kz1yGs\n\tfor <incoming@patchwork.ozlabs.org>; Wed, 22 Apr 2026 07:12:33 +1000 (AEST)",
            "from localhost (localhost [127.0.0.1])\n\tby smtp2.osuosl.org (Postfix) with ESMTP id D2C25420E5;\n\tTue, 21 Apr 2026 21:12:31 +0000 (UTC)",
            "from smtp2.osuosl.org ([127.0.0.1])\n by localhost (smtp2.osuosl.org [127.0.0.1]) (amavis, port 10024) with ESMTP\n id s57NDLNWxyBi; Tue, 21 Apr 2026 21:12:29 +0000 (UTC)",
            "from lists.linuxfoundation.org (lf-lists.osuosl.org\n [IPv6:2605:bc80:3010:104::8cd3:938])\n\tby smtp2.osuosl.org (Postfix) with ESMTPS id 0D44D420E4;\n\tTue, 21 Apr 2026 21:12:29 +0000 (UTC)",
            "from lf-lists.osuosl.org (localhost [127.0.0.1])\n\tby lists.linuxfoundation.org (Postfix) with ESMTP id D4EAAC058E;\n\tTue, 21 Apr 2026 21:12:28 +0000 (UTC)",
            "from smtp3.osuosl.org (smtp3.osuosl.org [IPv6:2605:bc80:3010::136])\n by lists.linuxfoundation.org (Postfix) with ESMTP id A77A6C058D\n for <dev@openvswitch.org>; Tue, 21 Apr 2026 21:12:27 +0000 (UTC)",
            "from localhost (localhost [127.0.0.1])\n by smtp3.osuosl.org (Postfix) with ESMTP id 12A8A61564\n for <dev@openvswitch.org>; Tue, 21 Apr 2026 21:12:20 +0000 (UTC)",
            "from smtp3.osuosl.org ([127.0.0.1])\n by localhost (smtp3.osuosl.org [127.0.0.1]) (amavis, port 10024) with ESMTP\n id nUuEvtB8H3TG for <dev@openvswitch.org>;\n Tue, 21 Apr 2026 21:12:18 +0000 (UTC)",
            "from mail-dy1-x132d.google.com (mail-dy1-x132d.google.com\n [IPv6:2607:f8b0:4864:20::132d])\n by smtp3.osuosl.org (Postfix) with ESMTPS id 1E8F661562\n for <dev@openvswitch.org>; Tue, 21 Apr 2026 21:12:17 +0000 (UTC)",
            "by mail-dy1-x132d.google.com with SMTP id\n 5a478bee46e88-2b4520f6b32so6141716eec.0\n for <dev@openvswitch.org>; Tue, 21 Apr 2026 14:12:17 -0700 (PDT)",
            "from WNEC-73GS814.. ([186.237.124.211])\n by smtp.gmail.com with ESMTPSA id\n 5a478bee46e88-2e79c2954f6sm18455763eec.30.2026.04.21.14.12.14\n (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n Tue, 21 Apr 2026 14:12:16 -0700 (PDT)"
        ],
        "X-Virus-Scanned": [
            "amavis at osuosl.org",
            "amavis at osuosl.org"
        ],
        "X-Comment": "SPF check N/A for local connections -\n client-ip=2605:bc80:3010:104::8cd3:938; helo=lists.linuxfoundation.org;\n envelope-from=ovs-dev-bounces@openvswitch.org; receiver=<UNKNOWN> ",
        "DKIM-Filter": [
            "OpenDKIM Filter v2.11.0 smtp2.osuosl.org 0D44D420E4",
            "OpenDKIM Filter v2.11.0 smtp3.osuosl.org 1E8F661562"
        ],
        "Received-SPF": "Pass (mailfrom) identity=mailfrom;\n client-ip=2607:f8b0:4864:20::132d; helo=mail-dy1-x132d.google.com;\n envelope-from=lucas.vdias@luizalabs.com; receiver=<UNKNOWN>",
        "DMARC-Filter": "OpenDMARC Filter v1.4.2 smtp3.osuosl.org 1E8F661562",
        "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n d=luizalabs.com; s=google; t=1776805937; x=1777410737; darn=openvswitch.org;\n h=content-transfer-encoding:mime-version:message-id:date:subject:cc\n :to:from:from:to:cc:subject:date:message-id:reply-to;\n bh=W7WnPWWPbWInQRHFhHdYb/cTd4DXts6Oc26GrK/C5cA=;\n b=OhPC8QfoA9oeOb/yCCOxqLHk/dx46qg1Qmpy9Evy8w5SgiPO4nOPZcUVRZ0OBkK8gX\n JUA109goTdJe9FwkBBYLKeRMMzuGKyM6dQBprfXe5zN0UjgsCS3xWuayY3UifiSFrQ1z\n mSjwbaS3yaCK+NEK2ohmylTbs7qli+Uj1YY5E=",
        "X-Google-DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n d=1e100.net; s=20251104; t=1776805937; x=1777410737;\n h=content-transfer-encoding:mime-version:message-id:date:subject:cc\n :to:from:x-gm-gg:x-gm-message-state:from:to:cc:subject:date\n :message-id:reply-to;\n bh=W7WnPWWPbWInQRHFhHdYb/cTd4DXts6Oc26GrK/C5cA=;\n b=Gjv/hk1TBqpiecb/oiX0TtA8kIhO3BoTYBNk1gYSdGuW//YqO/qpWO36UXQ35V/D6P\n B6mqQZK1/XRlzsVhXYQZ82H3x/r73T9wWCnwU6jGnO9ylMIlu1YxFJ4ShcACCZvfKPcd\n 9OydvHCwT+yBGiBEN8oM+S1MbrvbTYQMNzf9z1RR3PKOrCruZEK/ZHy3AgE3vbCnKEot\n wtPz2JFdNCkEnuSb9FpOTyvsEBNsyD/x/tNywfiNbOhm24cuGqFh68WICvoVW8FVuI1S\n MSlMBzyBKrQFktA9AjnqVtUunFcKyK3ZE4qDDQvMhN2s+6bBOEC+mwXtUH5G1I29QYT4\n 3JaA==",
        "X-Gm-Message-State": "AOJu0YxVclcyUOMOYdz+Ye6igQMxBKbH/dgd16pUHRZn5gpnDht6nwph\n E/YxBSsmukqQ6PJncTr6sRU1FE2ib/S6QOrVNlgIQBHcJuiFdzkvxbZYqIAnd/KHDO65QaCOXBQ\n GbbTnPLSsP7/5bcwA9GSA/0UsD1S9V+bJbY0MKRyHlxsRVWWnrxkDkKFXg6j6",
        "X-Gm-Gg": "AeBDievOK9hq0bu5geE3FvJUDBD9B/wcVBEuNsLxD/WQFem1HGEkZP8jbCcel2fxfMu\n B6XEX4zTR0/K+X+rRpucnP0v1DAUrmk0wejgFbNBmk5lXGnnTvZAiQp7dJvIp1NlEnAHDxz7Jow\n J/EUsaN2JfltHaV4xJjZZvulbGdElLRlA1XozRFpLTE2f5xn+iQY6WQK2uB7Dj5ipHdep03+UcX\n +MHkfsJxp+QN5uqdmKIbTYC2s7bWacyDs9i96GRRbxJvOmu6U+snGo8vIrPgo59ziG7X9c6jCy+\n sNbSTRI+7seA1vRcOtiTY3IpcOOyGDz2ca7RrRe1ZyvvnRPrdJoN4WTb3ChVQ62a7e9mRA7lLpU\n sTXSxVbKnavztf5+JK+n4RRWLuqLHKAoucv8fbPwGk/KNMQricshhy7unv4i5Fv+Bh/h/Yjeqdo\n s6o2pdTUuDdvD85hnrBRiwVdgaYJzEmyyQbujr3ABgT5ewQ7k=",
        "X-Received": "by 2002:a05:7300:3211:b0:2dd:db16:478e with SMTP id\n 5a478bee46e88-2e4654880bemr9729916eec.10.1776805936376;\n Tue, 21 Apr 2026 14:12:16 -0700 (PDT)",
        "To": "dev@openvswitch.org",
        "Date": "Tue, 21 Apr 2026 18:12:09 -0300",
        "Message-ID": "<20260421211209.1974247-1-lucas.vdias@luizalabs.com>",
        "X-Mailer": "git-send-email 2.43.0",
        "MIME-Version": "1.0",
        "Subject": "[ovs-dev] [PATCH ovn v2 2/2] northd: Incremental processing for\n static routes.",
        "X-BeenThere": "ovs-dev@openvswitch.org",
        "X-Mailman-Version": "2.1.30",
        "Precedence": "list",
        "List-Id": "<ovs-dev.openvswitch.org>",
        "List-Unsubscribe": "<https://mail.openvswitch.org/mailman/options/ovs-dev>,\n <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 <mailto:ovs-dev-request@openvswitch.org?subject=subscribe>",
        "From": "Lucas Vargas Dias via dev <ovs-dev@openvswitch.org>",
        "Reply-To": "Lucas Vargas Dias <lucas.vdias@luizalabs.com>",
        "Content-Type": "text/plain; charset=\"iso-8859-1\"",
        "Content-Transfer-Encoding": "quoted-printable",
        "Errors-To": "ovs-dev-bounces@openvswitch.org",
        "Sender": "\"dev\" <ovs-dev-bounces@openvswitch.org>"
    },
    "content": "Create a handler for deleted and updated static routes,\nand change the handler from engine route which check if\nit has a new static route created.\nTest with 2000 static routes created in the same logical router\nand add a new one:\nWithout the incremental processing:\novn-nbctl --print-wait-time --wait=sb lr-route-add lr1-2 10.0.0.1/32 192.168.20.2\nTime spent on processing nb_cfg 4:\n\tovn-northd delay before processing:\t4ms\n\tovn-northd completion:\t\t\t62ms\n\nWith the incremental processing:\novn-nbctl --print-wait-time --wait=sb lr-route-add lr1-2 10.0.0.1/32 192.168.20.2\nTime spent on processing nb_cfg 6:\n\tovn-northd delay before processing:\t4ms\n\tovn-northd completion:\t\t\t21ms\n\nTest with 2000 static routes created in the same logical router\nand delete one:\nWithout the incremental processing:\novn-nbctl --print-wait-time --wait=sb lr-route-del lr1-2 10.0.0.1/32 192.168.20.2\nTime spent on processing nb_cfg 5:\n\tovn-northd delay before processing:\t3ms\n\tovn-northd completion:\t\t\t62ms\n\nWith the incremental processing:\novn-nbctl --print-wait-time --wait=sb lr-route-del lr1-2 10.0.0.1/32 192.168.20.2\nTime spent on processing nb_cfg 9:\n\tovn-northd delay before processing:\t2ms\n\tovn-northd completion:\t\t\t32ms\n\nSigned-off-by: Lucas Vargas Dias <lucas.vdias@luizalabs.com>\n---\n northd/en-group-ecmp-route.c     |  57 ++++++++++\n northd/en-group-ecmp-route.h     |   4 +\n northd/en-lflow.c                |  20 ++++\n northd/en-lflow.h                |   2 +\n northd/en-northd.c               | 186 +++++++++++++++++++++++++++----\n northd/en-northd.h               |   5 +-\n northd/inc-proc-northd.c         |  13 ++-\n northd/northd.c                  | 107 +++++++++++++-----\n northd/northd.h                  |  38 ++++++-\n tests/ovn-inc-proc-graph-dump.at |   6 +-\n tests/ovn-northd.at              |  98 +++++++++++++---\n 11 files changed, 466 insertions(+), 70 deletions(-)",
    "diff": "diff --git a/northd/en-group-ecmp-route.c b/northd/en-group-ecmp-route.c\nindex c4c93fd84..fcc76b076 100644\n--- a/northd/en-group-ecmp-route.c\n+++ b/northd/en-group-ecmp-route.c\n@@ -519,3 +519,60 @@ group_ecmp_route_learned_route_change_handler(struct engine_node *eng_node,\n     }\n     return EN_HANDLED_UNCHANGED;\n }\n+\n+enum engine_input_handler_result\n+group_ecmp_static_route_change_handler(struct engine_node *eng_node,\n+                                       void *_data)\n+{\n+    struct routes_data *routes_data\n+        = engine_get_input_data(\"routes\", eng_node);\n+    struct group_ecmp_route_data *data = _data;\n+    if (!routes_data->tracked) {\n+        data->tracked = false;\n+        return EN_UNHANDLED;\n+    }\n+\n+    struct parsed_route *pr;\n+    struct hmapx updated_routes = HMAPX_INITIALIZER(&updated_routes);\n+\n+    const struct hmapx_node *hmapx_node;\n+    HMAPX_FOR_EACH (hmapx_node,\n+                    &routes_data->trk_data.trk_deleted_parsed_route) {\n+        pr = hmapx_node->data;\n+        if (!handle_deleted_route(data, pr, &updated_routes)) {\n+            hmapx_destroy(&updated_routes);\n+            return EN_UNHANDLED;\n+        }\n+\n+        if (pr->is_in_parsed_routes) {\n+            hmap_remove(&routes_data->parsed_routes, &pr->key_node);\n+        }\n+        parsed_route_free(pr);\n+    }\n+\n+    HMAPX_FOR_EACH (hmapx_node,\n+                    &routes_data->trk_data.trk_crupdated_parsed_route) {\n+        pr = hmapx_node->data;\n+        handle_added_route(data, pr, &updated_routes);\n+    }\n+\n+    HMAPX_FOR_EACH (hmapx_node, &updated_routes) {\n+        struct group_ecmp_datapath *node = hmapx_node->data;\n+        if (hmap_is_empty(&node->unique_routes) &&\n+                hmap_is_empty(&node->ecmp_groups)) {\n+            hmapx_add(&data->trk_data.deleted_datapath_routes, node);\n+            hmap_remove(&data->datapaths, &node->hmap_node);\n+        } else {\n+            hmapx_add(&data->trk_data.crupdated_datapath_routes, node);\n+        }\n+    }\n+\n+    hmapx_destroy(&updated_routes);\n+\n+    if (!hmapx_is_empty(&data->trk_data.crupdated_datapath_routes) ||\n+        !hmapx_is_empty(&data->trk_data.deleted_datapath_routes)) {\n+        data->tracked = true;\n+        return EN_HANDLED_UPDATED;\n+    }\n+    return EN_HANDLED_UNCHANGED;\n+}\ndiff --git a/northd/en-group-ecmp-route.h b/northd/en-group-ecmp-route.h\nindex d4a3248d0..246ca06bf 100644\n--- a/northd/en-group-ecmp-route.h\n+++ b/northd/en-group-ecmp-route.h\n@@ -98,6 +98,10 @@ enum engine_input_handler_result\n group_ecmp_route_learned_route_change_handler(struct engine_node *,\n                                               void *data);\n \n+enum engine_input_handler_result\n+group_ecmp_static_route_change_handler(struct engine_node *,\n+                                       void *data);\n+\n struct group_ecmp_datapath *group_ecmp_datapath_lookup(\n     const struct group_ecmp_route_data *data,\n     const struct ovn_datapath *od);\ndiff --git a/northd/en-lflow.c b/northd/en-lflow.c\nindex d4351edb9..22cd8fe91 100644\n--- a/northd/en-lflow.c\n+++ b/northd/en-lflow.c\n@@ -297,6 +297,21 @@ lflow_multicast_igmp_handler(struct engine_node *node, void *data)\n     return EN_HANDLED_UPDATED;\n }\n \n+enum engine_input_handler_result\n+lflow_group_route_change_handler(struct engine_node *node,\n+                                      void *data OVS_UNUSED)\n+{\n+    struct routes_data *route_data =\n+        engine_get_input_data(\"routes\", node);\n+\n+    /* If we do not have tracked data we need to recompute. */\n+    if (!route_data->tracked) {\n+        return EN_UNHANDLED;\n+    }\n+\n+    return EN_HANDLED_UNCHANGED;\n+}\n+\n enum engine_input_handler_result\n lflow_group_ecmp_route_change_handler(struct engine_node *node,\n                                       void *data OVS_UNUSED)\n@@ -346,6 +361,11 @@ lflow_group_ecmp_route_change_handler(struct engine_node *node,\n             route_node->od, lflow_data->lflow_table,\n             route_node, lflow_input.bfd_ports);\n \n+        build_arp_request_flows_for_lrouter(route_node->od,\n+                                            lflow_data->lflow_table,\n+                                            lflow_input.meter_groups,\n+                                            route_node->lflow_ref);\n+\n         bool handled = lflow_ref_sync_lflows(\n             route_node->lflow_ref, lflow_data->lflow_table,\n             eng_ctx->ovnsb_idl_txn, lflow_input.dps,\ndiff --git a/northd/en-lflow.h b/northd/en-lflow.h\nindex d2a92e49f..aa320615f 100644\n--- a/northd/en-lflow.h\n+++ b/northd/en-lflow.h\n@@ -31,5 +31,7 @@ lflow_multicast_igmp_handler(struct engine_node *node, void *data);\n enum engine_input_handler_result\n lflow_group_ecmp_route_change_handler(struct engine_node *node, void *data);\n enum engine_input_handler_result\n+lflow_group_route_change_handler(struct engine_node *node, void *data);\n+enum engine_input_handler_result\n lflow_ic_learned_svc_mons_handler(struct engine_node *node, void *data);\n #endif /* EN_LFLOW_H */\ndiff --git a/northd/en-northd.c b/northd/en-northd.c\nindex c34818dba..c05939a1d 100644\n--- a/northd/en-northd.c\n+++ b/northd/en-northd.c\n@@ -207,7 +207,8 @@ northd_nb_logical_router_handler(struct engine_node *node,\n     }\n \n     if (northd_has_lr_nats_in_tracked_data(&nd->trk_data) ||\n-        northd_has_lrouters_in_tracked_data(&nd->trk_data)) {\n+        northd_has_lrouters_in_tracked_data(&nd->trk_data) ||\n+        northd_has_lr_route_in_tracked_data(&nd->trk_data)) {\n         return EN_HANDLED_UPDATED;\n     }\n \n@@ -329,32 +330,174 @@ en_route_policies_run(struct engine_node *node, void *data)\n \n enum engine_input_handler_result\n routes_northd_change_handler(struct engine_node *node,\n-                                    void *data OVS_UNUSED)\n+                                    void *data)\n {\n     struct northd_data *northd_data = engine_get_input_data(\"northd\", node);\n     if (!northd_has_tracked_data(&northd_data->trk_data)) {\n         return EN_UNHANDLED;\n     }\n \n-    /* This node uses the below data from the en_northd engine node.\n-     * See (lr_stateful_get_input_data())\n-     *   1. northd_data->lr_datapaths\n-     *   2. northd_data->lr_ports\n-     *      This data gets updated when a logical router or logical router port\n-     *      is created or deleted.\n-     *      Northd engine node presently falls back to full recompute when\n-     *      this happens and so does this node.\n-     *      Note: When we add I-P to the created/deleted logical routers or\n-     *      logical router ports, we need to revisit this handler.\n-     *\n-     *      This node also accesses the static routes of the logical router.\n-     *      When these static routes gets updated, en_northd engine recomputes\n-     *      and so does this node.\n-     *      Note: When we add I-P to handle static routes changes, we need\n-     *      to revisit this handler.\n-     */\n+    if (!northd_has_lr_route_in_tracked_data(&northd_data->trk_data)) {\n+        return EN_HANDLED_UNCHANGED;\n+    }\n+\n+    struct bfd_data *bfd_data = engine_get_input_data(\"bfd\", node);\n+    struct routes_data *routes_data = data;\n+    struct hmapx_node *hmapx_node;\n+    struct ovn_datapath *od;\n+    HMAPX_FOR_EACH (hmapx_node, &northd_data->trk_data.trk_lrs_routes) {\n+        od = hmapx_node->data;\n+        struct parsed_route *pr;\n+\n+        for (int i = 0; i < od->nbr->n_static_routes; i++) {\n+            struct nbrec_logical_router_static_route *static_route =\n+                od->nbr->static_routes[i];\n+            pr = parsed_route_lookup_by_source(ROUTE_SOURCE_STATIC,\n+                                               &static_route->header_,\n+                                               &routes_data->parsed_routes);\n+            if (pr) {\n+                pr->stale = false;\n+                continue;\n+            }\n+            pr = parsed_routes_add_static(od, &northd_data->lr_ports,\n+                                        static_route,\n+                                        &bfd_data->bfd_connections,\n+                                        &routes_data->parsed_routes,\n+                                        &routes_data->route_tables,\n+                                        &routes_data->bfd_active_connections);\n+            if (!pr) {\n+                continue;\n+            }\n+            hmapx_add(&routes_data->trk_data.trk_crupdated_parsed_route,\n+                      pr);\n+        }\n+    }\n+\n+    if (!hmapx_is_empty(&routes_data->trk_data.trk_crupdated_parsed_route)) {\n+        routes_data->tracked = true;\n+        return EN_HANDLED_UPDATED;\n+    }\n+\n     return EN_HANDLED_UNCHANGED;\n }\n+enum engine_input_handler_result\n+routes_static_route_change_handler(struct engine_node *node,\n+                                   void *data)\n+{\n+    struct routes_data *routes_data = data;\n+    struct hmapx created_trk_parsed_route =\n+        HMAPX_INITIALIZER(&created_trk_parsed_route);\n+    const struct nbrec_logical_router_static_route_table *\n+      nb_lr_static_route_table =\n+    EN_OVSDB_GET(engine_get_input(\"NB_logical_router_static_route\", node));\n+\n+    struct northd_data *northd_data = engine_get_input_data(\"northd\", node);\n+    struct bfd_data *bfd_data = engine_get_input_data(\"bfd\", node);\n+\n+    const struct nbrec_logical_router_static_route *changed_static_route;\n+    NBREC_LOGICAL_ROUTER_STATIC_ROUTE_TABLE_FOR_EACH_TRACKED (\n+                            changed_static_route, nb_lr_static_route_table) {\n+\n+        bool is_deleted = nbrec_logical_router_static_route_is_deleted(\n+                                                        changed_static_route);\n+        bool is_new = nbrec_logical_router_static_route_is_new(\n+                                                        changed_static_route);\n+\n+        if (is_new && is_deleted) {\n+            continue;\n+        }\n+\n+        if (is_new) {\n+            hmapx_add(&created_trk_parsed_route, &changed_static_route);\n+            continue;\n+        }\n+\n+        if (is_deleted) {\n+            struct parsed_route *pr = parsed_route_lookup_by_source(\n+                                            ROUTE_SOURCE_STATIC,\n+                                            &changed_static_route->header_,\n+                                            &routes_data->parsed_routes);\n+            if (!pr) {\n+                pr = parsed_route_lookup_by_source(ROUTE_SOURCE_IC_DYNAMIC,\n+                                            &changed_static_route->header_,\n+                                            &routes_data->parsed_routes);\n+            }\n+            if (pr) {\n+                pr->stale = true;\n+                hmapx_add(&routes_data->trk_data.trk_deleted_parsed_route, pr);\n+            }\n+            continue;\n+        }\n+\n+        if (nbrec_logical_router_static_route_is_updated(\n+                    changed_static_route,\n+                    NBREC_LOGICAL_ROUTER_STATIC_ROUTE_COL_NEXTHOP)\n+            || nbrec_logical_router_static_route_is_updated(\n+                    changed_static_route,\n+                    NBREC_LOGICAL_ROUTER_STATIC_ROUTE_COL_IP_PREFIX)\n+            || nbrec_logical_router_static_route_is_updated(\n+                    changed_static_route,\n+                    NBREC_LOGICAL_ROUTER_STATIC_ROUTE_COL_OUTPUT_PORT)\n+            || nbrec_logical_router_static_route_is_updated(\n+                    changed_static_route,\n+                    NBREC_LOGICAL_ROUTER_STATIC_ROUTE_COL_POLICY)\n+            || nbrec_logical_router_static_route_is_updated(\n+                    changed_static_route,\n+                    NBREC_LOGICAL_ROUTER_STATIC_ROUTE_COL_ROUTE_TABLE)\n+            || nbrec_logical_router_static_route_is_updated(\n+                    changed_static_route,\n+                    NBREC_LOGICAL_ROUTER_STATIC_ROUTE_COL_SELECTION_FIELDS)\n+            ||  nbrec_logical_router_static_route_is_updated(\n+                    changed_static_route,\n+                    NBREC_LOGICAL_ROUTER_STATIC_ROUTE_COL_OPTIONS)) {\n+            struct parsed_route *pr = parsed_route_lookup_by_source(\n+                                            ROUTE_SOURCE_STATIC,\n+                                            &changed_static_route->header_,\n+                                            &routes_data->parsed_routes);\n+            if (!pr) {\n+                pr = parsed_route_lookup_by_source(\n+                                                ROUTE_SOURCE_IC_DYNAMIC,\n+                                                &changed_static_route->header_,\n+                                                &routes_data->parsed_routes);\n+            }\n+\n+            if (!pr || !pr->od || !northd_data || !bfd_data) {\n+                continue;\n+            }\n+            struct parsed_route *old_pr = pr;\n+            hmap_remove(&routes_data->parsed_routes, &old_pr->key_node);\n+            pr = parsed_routes_add_static(old_pr->od, &northd_data->lr_ports,\n+                                        changed_static_route,\n+                                        &bfd_data->bfd_connections,\n+                                        &routes_data->parsed_routes,\n+                                        &routes_data->route_tables,\n+                                        &routes_data->bfd_active_connections);\n+            old_pr->is_in_parsed_routes = false;\n+            if (!pr) {\n+                continue;\n+            }\n+\n+            hmapx_add(&routes_data->trk_data.trk_crupdated_parsed_route,\n+                       pr);\n+            hmapx_add(&routes_data->trk_data.trk_deleted_parsed_route,\n+                      old_pr);\n+        }\n+    }\n+    if (!hmapx_is_empty(&routes_data->trk_data.trk_crupdated_parsed_route) ||\n+        !hmapx_is_empty(&routes_data->trk_data.trk_deleted_parsed_route)) {\n+        hmapx_destroy(&created_trk_parsed_route);\n+        routes_data->tracked = true;\n+        return EN_HANDLED_UPDATED;\n+    }\n+\n+    if (!hmapx_is_empty(&created_trk_parsed_route)) {\n+        hmapx_destroy(&created_trk_parsed_route);\n+        return EN_HANDLED_UPDATED;\n+    }\n+\n+    hmapx_destroy(&created_trk_parsed_route);\n+    return EN_UNHANDLED;\n+}\n \n enum engine_node_state\n en_routes_run(struct engine_node *node, void *data)\n@@ -590,6 +733,11 @@ en_routes_cleanup(void *data)\n     routes_destroy(data);\n }\n \n+void\n+en_routes_clear_tracked_data(void *data)\n+{\n+    routes_clear_tracked(data);\n+}\n void\n en_bfd_cleanup(void *data)\n {\ndiff --git a/northd/en-northd.h b/northd/en-northd.h\nindex 7794739b9..5247f3e11 100644\n--- a/northd/en-northd.h\n+++ b/northd/en-northd.h\n@@ -39,9 +39,12 @@ enum engine_node_state en_route_policies_run(struct engine_node *node,\n                                              void *data);\n void *en_route_policies_init(struct engine_node *node OVS_UNUSED,\n                              struct engine_arg *arg OVS_UNUSED);\n+void en_routes_clear_tracked_data(void *data);\n void en_routes_cleanup(void *data);\n enum engine_input_handler_result\n-routes_northd_change_handler(struct engine_node *node, void *data OVS_UNUSED);\n+routes_northd_change_handler(struct engine_node *node, void *data);\n+enum engine_input_handler_result\n+routes_static_route_change_handler(struct engine_node *node, void *data);\n enum engine_node_state en_routes_run(struct engine_node *node, void *data);\n void *en_bfd_init(struct engine_node *node OVS_UNUSED,\n                   struct engine_arg *arg OVS_UNUSED);\ndiff --git a/northd/inc-proc-northd.c b/northd/inc-proc-northd.c\nindex ece388ce7..52f5dc57f 100644\n--- a/northd/inc-proc-northd.c\n+++ b/northd/inc-proc-northd.c\n@@ -76,7 +76,9 @@ static unixctl_cb_func chassis_features_list;\n     NB_NODE(sampling_app) \\\n     NB_NODE(network_function) \\\n     NB_NODE(network_function_group) \\\n-    NB_NODE(logical_switch_port_health_check)\n+    NB_NODE(logical_switch_port_health_check) \\\n+    NB_NODE(logical_router_static_route)\n+\n \n     enum nb_engine_node {\n #define NB_NODE(NAME) NB_##NAME,\n@@ -179,7 +181,7 @@ static ENGINE_NODE(lr_stateful, CLEAR_TRACKED_DATA);\n static ENGINE_NODE(ls_stateful, CLEAR_TRACKED_DATA);\n static ENGINE_NODE(ls_arp, CLEAR_TRACKED_DATA);\n static ENGINE_NODE(route_policies);\n-static ENGINE_NODE(routes);\n+static ENGINE_NODE(routes, CLEAR_TRACKED_DATA);\n static ENGINE_NODE(bfd);\n static ENGINE_NODE(bfd_sync, SB_WRITE);\n static ENGINE_NODE(ecmp_nexthop, SB_WRITE);\n@@ -341,6 +343,8 @@ void inc_proc_northd_init(struct ovsdb_idl_loop *nb,\n     engine_add_input(&en_routes, &en_bfd, NULL);\n     engine_add_input(&en_routes, &en_northd,\n                      routes_northd_change_handler);\n+    engine_add_input(&en_routes, &en_nb_logical_router_static_route,\n+                     routes_static_route_change_handler);\n \n     engine_add_input(&en_bfd_sync, &en_bfd, NULL);\n     engine_add_input(&en_bfd_sync, &en_nb_bfd, NULL);\n@@ -380,7 +384,8 @@ void inc_proc_northd_init(struct ovsdb_idl_loop *nb,\n     engine_add_input(&en_learned_route_sync, &en_northd,\n                      learned_route_sync_northd_change_handler);\n \n-    engine_add_input(&en_group_ecmp_route, &en_routes, NULL);\n+    engine_add_input(&en_group_ecmp_route, &en_routes,\n+                     group_ecmp_static_route_change_handler);\n     engine_add_input(&en_group_ecmp_route, &en_learned_route_sync,\n                      group_ecmp_route_learned_route_change_handler);\n \n@@ -399,7 +404,7 @@ void inc_proc_northd_init(struct ovsdb_idl_loop *nb,\n     engine_add_input(&en_lflow, &en_sb_logical_dp_group, NULL);\n     engine_add_input(&en_lflow, &en_bfd_sync, NULL);\n     engine_add_input(&en_lflow, &en_route_policies, NULL);\n-    engine_add_input(&en_lflow, &en_routes, NULL);\n+    engine_add_input(&en_lflow, &en_routes, lflow_group_route_change_handler);\n     /* XXX: The incremental processing only supports changes to learned routes.\n      * All other changes trigger a full recompute. */\n     engine_add_input(&en_lflow, &en_group_ecmp_route,\ndiff --git a/northd/northd.c b/northd/northd.c\nindex 4fd4b9de9..ea48bc442 100644\n--- a/northd/northd.c\n+++ b/northd/northd.c\n@@ -4517,6 +4517,7 @@ destroy_northd_data_tracked_changes(struct northd_data *nd)\n     destroy_tracked_ovn_ports(&trk_changes->trk_lsps);\n     destroy_tracked_lbs(&trk_changes->trk_lbs);\n     hmapx_clear(&trk_changes->trk_nat_lrs);\n+    hmapx_clear(&trk_changes->trk_lrs_routes);\n     hmapx_clear(&trk_changes->ls_with_changed_lbs);\n     hmapx_clear(&trk_changes->ls_with_changed_acls);\n     hmapx_clear(&trk_changes->ls_with_changed_ipam);\n@@ -4540,6 +4541,7 @@ init_northd_tracked_data(struct northd_data *nd)\n     hmapx_init(&trk_data->trk_lbs.crupdated);\n     hmapx_init(&trk_data->trk_lbs.deleted);\n     hmapx_init(&trk_data->trk_nat_lrs);\n+    hmapx_init(&trk_data->trk_lrs_routes);\n     hmapx_init(&trk_data->ls_with_changed_lbs);\n     hmapx_init(&trk_data->ls_with_changed_acls);\n     hmapx_init(&trk_data->ls_with_changed_ipam);\n@@ -4558,6 +4560,7 @@ destroy_northd_tracked_data(struct northd_data *nd)\n     hmapx_destroy(&trk_data->trk_lbs.crupdated);\n     hmapx_destroy(&trk_data->trk_lbs.deleted);\n     hmapx_destroy(&trk_data->trk_nat_lrs);\n+    hmapx_destroy(&trk_data->trk_lrs_routes);\n     hmapx_destroy(&trk_data->ls_with_changed_lbs);\n     hmapx_destroy(&trk_data->ls_with_changed_acls);\n     hmapx_destroy(&trk_data->ls_with_changed_ipam);\n@@ -5379,7 +5382,8 @@ lr_changes_can_be_handled(const struct nbrec_logical_router *lr)\n         if (nbrec_logical_router_is_updated(lr, col)) {\n             if (col == NBREC_LOGICAL_ROUTER_COL_LOAD_BALANCER\n                 || col == NBREC_LOGICAL_ROUTER_COL_LOAD_BALANCER_GROUP\n-                || col == NBREC_LOGICAL_ROUTER_COL_NAT) {\n+                || col == NBREC_LOGICAL_ROUTER_COL_NAT\n+                || col == NBREC_LOGICAL_ROUTER_COL_STATIC_ROUTES) {\n                 continue;\n             }\n             return false;\n@@ -5404,12 +5408,7 @@ lr_changes_can_be_handled(const struct nbrec_logical_router *lr)\n             return false;\n         }\n     }\n-    for (size_t i = 0; i < lr->n_static_routes; i++) {\n-        if (nbrec_logical_router_static_route_row_get_seqno(\n-            lr->static_routes[i], OVSDB_IDL_CHANGE_MODIFY) > 0) {\n-            return false;\n-        }\n-    }\n+\n     return true;\n }\n \n@@ -5435,6 +5434,13 @@ is_lr_nats_changed(const struct nbrec_logical_router *nbr) {\n             || is_lr_nats_seqno_changed(nbr));\n }\n \n+static bool\n+is_lr_static_routes_changed(const struct nbrec_logical_router *nbr) {\n+    return nbrec_logical_router_is_updated(nbr,\n+                                   NBREC_LOGICAL_ROUTER_COL_STATIC_ROUTES);\n+}\n+\n+\n /* Return true if changes are handled incrementally, false otherwise.\n  *\n  * Note: Changes to load balancer and load balancer groups associated with\n@@ -5503,6 +5509,22 @@ northd_handle_lr_changes(const struct northd_input *ni,\n \n             hmapx_add(&nd->trk_data.trk_nat_lrs, od);\n         }\n+\n+        /* Static Route was added or deleted. */\n+        if (is_lr_static_routes_changed(changed_lr)) {\n+            struct ovn_datapath *od = ovn_datapath_find_(\n+                                    &nd->lr_datapaths.datapaths,\n+                                    &changed_lr->header_.uuid);\n+\n+            if (!od) {\n+                static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);\n+                VLOG_WARN_RL(&rl, \"Internal error: a tracked updated LR \"\n+                            \"doesn't exist in lr_datapaths: \"UUID_FMT,\n+                            UUID_ARGS(&changed_lr->header_.uuid));\n+                goto fail;\n+            }\n+            hmapx_add(&nd->trk_data.trk_lrs_routes, od);\n+        }\n     }\n \n     HMAPX_FOR_EACH (node, &ni->synced_lrs->deleted) {\n@@ -5543,6 +5565,9 @@ northd_handle_lr_changes(const struct northd_input *ni,\n     if (!hmapx_is_empty(&nd->trk_data.trk_nat_lrs)) {\n         nd->trk_data.type |= NORTHD_TRACKED_LR_NATS;\n     }\n+    if (!hmapx_is_empty(&nd->trk_data.trk_lrs_routes)) {\n+        nd->trk_data.type |= NORTHD_TRACKED_LR_ROUTES;\n+    }\n     if (!hmapx_is_empty(&nd->trk_data.trk_routers.crupdated) ||\n         !hmapx_is_empty(&nd->trk_data.trk_routers.deleted)) {\n         nd->trk_data.type |= NORTHD_TRACKED_ROUTERS;\n@@ -12189,6 +12214,7 @@ parsed_route_init(const struct ovn_datapath *od,\n     new_pr->route_table_id = route_table_id;\n     new_pr->is_src_route = is_src_route;\n     new_pr->od = od;\n+    new_pr->is_in_parsed_routes = false;\n     new_pr->ecmp_symmetric_reply = ecmp_symmetric_reply;\n     new_pr->is_discard_route = is_discard_route;\n     new_pr->lrp_addr_s = nullable_xstrdup(lrp_addr_s);\n@@ -12296,6 +12322,7 @@ parsed_route_add(const struct ovn_datapath *od,\n     struct parsed_route *pr = parsed_route_lookup(routes, hash, new_pr);\n     if (!pr) {\n         hmap_insert(routes, &new_pr->key_node, hash);\n+        new_pr->is_in_parsed_routes = true;\n         return new_pr;\n     } else {\n         pr->stale = false;\n@@ -12304,7 +12331,7 @@ parsed_route_add(const struct ovn_datapath *od,\n     }\n }\n \n-static void\n+struct parsed_route *\n parsed_routes_add_static(const struct ovn_datapath *od,\n                          const struct hmap *lr_ports,\n                          const struct nbrec_logical_router_static_route *route,\n@@ -12325,8 +12352,9 @@ parsed_routes_add_static(const struct ovn_datapath *od,\n                          UUID_FMT, route->nexthop,\n                          UUID_ARGS(&route->header_.uuid));\n             free(nexthop);\n-            return;\n+            return NULL;\n         }\n+\n         if ((IN6_IS_ADDR_V4MAPPED(nexthop) && plen != 32) ||\n             (!IN6_IS_ADDR_V4MAPPED(nexthop) && plen != 128)) {\n             static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);\n@@ -12334,7 +12362,7 @@ parsed_routes_add_static(const struct ovn_datapath *od,\n                          UUID_FMT, route->nexthop,\n                          UUID_ARGS(&route->header_.uuid));\n             free(nexthop);\n-            return;\n+            return NULL;\n         }\n     }\n \n@@ -12346,7 +12374,7 @@ parsed_routes_add_static(const struct ovn_datapath *od,\n                      UUID_FMT, route->ip_prefix,\n                      UUID_ARGS(&route->header_.uuid));\n         free(nexthop);\n-        return;\n+        return NULL;\n     }\n \n     /* Verify that ip_prefix and nexthop are on the same network. */\n@@ -12358,7 +12386,7 @@ parsed_routes_add_static(const struct ovn_datapath *od,\n                                    : IN6_IS_ADDR_V4MAPPED(&prefix),\n                                    &lrp_addr_s, &out_port)) {\n         free(nexthop);\n-        return;\n+        return NULL;\n     }\n \n     const struct nbrec_bfd *nb_bt = route->bfd;\n@@ -12368,7 +12396,7 @@ parsed_routes_add_static(const struct ovn_datapath *od,\n                                                   nb_bt->dst_ip);\n         if (!bfd_e) {\n             free(nexthop);\n-            return;\n+            return NULL;\n         }\n \n         /* This static route is linked to an active bfd session. */\n@@ -12385,10 +12413,9 @@ parsed_routes_add_static(const struct ovn_datapath *od,\n             bfd_set_status(bfd_sr, \"down\");\n         }\n \n-\n         if (!strcmp(bfd_sr->status, \"down\")) {\n-            free(nexthop);\n-            return;\n+           free(nexthop);\n+           return NULL;\n         }\n     }\n \n@@ -12427,11 +12454,15 @@ parsed_routes_add_static(const struct ovn_datapath *od,\n         source = ROUTE_SOURCE_STATIC;\n     }\n \n-    parsed_route_add(od, nexthop, &prefix, plen, is_discard_route, lrp_addr_s,\n-                     out_port, route_table_id, is_src_route,\n-                     ecmp_symmetric_reply, &ecmp_selection_fields, source,\n-                     &route->header_, NULL, routes);\n+    struct parsed_route *pr = parsed_route_add(od, nexthop, &prefix, plen,\n+                                               is_discard_route, lrp_addr_s,\n+                                               out_port, route_table_id,\n+                                               is_src_route,\n+                                               ecmp_symmetric_reply,\n+                                               &ecmp_selection_fields, source,\n+                                               &route->header_, NULL, routes);\n     sset_destroy(&ecmp_selection_fields);\n+    return pr;\n }\n \n static void\n@@ -16309,13 +16340,14 @@ build_lr_gateway_redirect_flows_for_nats(\n  * In the common case where the Ethernet destination has been resolved,\n  * this table outputs the packet (priority 0).  Otherwise, it composes\n  * and sends an ARP/IPv6 NA request (priority 100). */\n-static void\n+void\n build_arp_request_flows_for_lrouter(\n-        struct ovn_datapath *od, struct lflow_table *lflows,\n-        struct ds *match, struct ds *actions,\n+        const struct ovn_datapath *od, struct lflow_table *lflows,\n         const struct shash *meter_groups,\n         struct lflow_ref *lflow_ref)\n {\n+    struct ds match =  DS_EMPTY_INITIALIZER;\n+    struct ds actions = DS_EMPTY_INITIALIZER;\n     ovs_assert(od->nbr);\n     for (int i = 0; i < od->nbr->n_static_routes; i++) {\n         const struct nbrec_logical_router_static_route *route;\n@@ -16329,8 +16361,8 @@ build_arp_request_flows_for_lrouter(\n             continue;\n         }\n \n-        ds_clear(match);\n-        ds_put_format(match, \"eth.dst == 00:00:00:00:00:00 && \"\n+        ds_clear(&match);\n+        ds_put_format(&match, \"eth.dst == 00:00:00:00:00:00 && \"\n                       REGBIT_NEXTHOP_IS_IPV4\" == 0 && \"\n                       REG_NEXT_HOP_IPV6 \" == %s\",\n                       route->nexthop);\n@@ -16342,8 +16374,8 @@ build_arp_request_flows_for_lrouter(\n         char sn_addr_s[INET6_ADDRSTRLEN + 1];\n         ipv6_string_mapped(sn_addr_s, &sn_addr);\n \n-        ds_clear(actions);\n-        ds_put_format(actions,\n+        ds_clear(&actions);\n+        ds_put_format(&actions,\n                       \"nd_ns { \"\n                       \"eth.dst = \"ETH_ADDR_FMT\"; \"\n                       \"ip6.dst = %s; \"\n@@ -16353,7 +16385,7 @@ build_arp_request_flows_for_lrouter(\n                       route->nexthop);\n \n         ovn_lflow_add(lflows, od, S_ROUTER_IN_ARP_REQUEST, 200,\n-                      ds_cstr(match), ds_cstr(actions), lflow_ref,\n+                      ds_cstr(&match), ds_cstr(&actions), lflow_ref,\n                       WITH_CTRL_METER(copp_meter_get(COPP_ND_NS_RESOLVE,\n                                                      od->nbr->copp,\n                                                      meter_groups)),\n@@ -16385,6 +16417,8 @@ build_arp_request_flows_for_lrouter(\n                                                             meter_groups)));\n     ovn_lflow_add(lflows, od, S_ROUTER_IN_ARP_REQUEST, 0, \"1\", \"next;\",\n                   lflow_ref);\n+    ds_destroy(&match);\n+    ds_destroy(&actions);\n }\n \n static void\n@@ -19508,8 +19542,7 @@ build_lswitch_and_lrouter_iterate_by_lr(struct ovn_datapath *od,\n     build_gateway_redirect_flows_for_lrouter(od, lsi->lflows, &lsi->match,\n                                              &lsi->actions,\n                                              od->datapath_lflows);\n-    build_arp_request_flows_for_lrouter(od, lsi->lflows, &lsi->match,\n-                                        &lsi->actions,\n+    build_arp_request_flows_for_lrouter(od, lsi->lflows,\n                                         lsi->meter_groups,\n                                         od->datapath_lflows);\n     build_ecmp_stateful_egr_flows_for_lrouter(od, lsi->lflows,\n@@ -21067,6 +21100,9 @@ routes_init(struct routes_data *data)\n     hmap_init(&data->parsed_routes);\n     simap_init(&data->route_tables);\n     hmap_init(&data->bfd_active_connections);\n+    data->tracked = false;\n+    hmapx_init(&data->trk_data.trk_deleted_parsed_route);\n+    hmapx_init(&data->trk_data.trk_crupdated_parsed_route);\n }\n \n void\n@@ -21197,6 +21233,17 @@ routes_destroy(struct routes_data *data)\n \n     simap_destroy(&data->route_tables);\n     __bfd_destroy(&data->bfd_active_connections);\n+    data->tracked = false;\n+    hmapx_destroy(&data->trk_data.trk_crupdated_parsed_route);\n+    hmapx_destroy(&data->trk_data.trk_deleted_parsed_route);\n+}\n+\n+void\n+routes_clear_tracked(struct routes_data *data)\n+{\n+    data->tracked = false;\n+    hmapx_clear(&data->trk_data.trk_crupdated_parsed_route);\n+    hmapx_clear(&data->trk_data.trk_deleted_parsed_route);\n }\n \n void\ndiff --git a/northd/northd.h b/northd/northd.h\nindex a9070d6f6..5b3461840 100644\n--- a/northd/northd.h\n+++ b/northd/northd.h\n@@ -160,6 +160,7 @@ enum northd_tracked_data_type {\n     NORTHD_TRACKED_LS_ACLS  = (1 << 4),\n     NORTHD_TRACKED_SWITCHES = (1 << 5),\n     NORTHD_TRACKED_ROUTERS  = (1 << 6),\n+    NORTHD_TRACKED_LR_ROUTES  = (1 << 7),\n };\n \n /* Track what's changed in the northd engine node.\n@@ -177,6 +178,10 @@ struct northd_tracked_data {\n      * hmapx node is 'struct ovn_datapath *'. */\n     struct hmapx trk_nat_lrs;\n \n+    /* Tracked logical routers whose static routes have changed.\n+     * hmapx node is 'struct ovn_datapath *'. */\n+    struct hmapx trk_lrs_routes;\n+\n     /* Tracked logical switches whose load balancers have changed.\n      * hmapx node is 'struct ovn_datapath *'. */\n     struct hmapx ls_with_changed_lbs;\n@@ -217,10 +222,23 @@ struct route_policy {\n     uint32_t jump_chain_id;\n };\n \n+struct route_tracked_data {\n+    /* Contains references to group_ecmp_route_node. Each of the referenced\n+     * datapaths contains at least one route. */\n+    struct hmapx trk_crupdated_parsed_route;\n+\n+    /* Contains references to group_ecmp_route_node. Each of the referenced\n+     * datapath previously had some routes. The datapath now no longer\n+     * contains any route.*/\n+    struct hmapx trk_deleted_parsed_route;\n+};\n+\n struct routes_data {\n     struct hmap parsed_routes; /* Stores struct parsed_route. */\n     struct simap route_tables;\n     struct hmap bfd_active_connections;\n+    bool tracked;\n+    struct route_tracked_data trk_data;\n };\n \n struct route_policies_data {\n@@ -855,6 +873,7 @@ struct parsed_route {\n     char *lrp_addr_s;\n     const struct ovn_port *out_port;\n     const struct ovn_port *tracked_port; /* May be NULL. */\n+    bool is_in_parsed_routes;\n };\n \n struct parsed_route *parsed_route_clone(const struct parsed_route *);\n@@ -881,6 +900,14 @@ struct parsed_route *parsed_route_add(\n     const struct ovn_port *tracked_port,\n     struct hmap *routes);\n \n+struct  parsed_route * parsed_routes_add_static(\n+    const struct ovn_datapath *od,\n+    const struct hmap *lr_ports,\n+    const struct nbrec_logical_router_static_route *route,\n+    const struct hmap *bfd_connections,\n+    struct hmap *routes, struct simap *route_tables,\n+    struct hmap *bfd_active_connections);\n+\n struct svc_monitors_map_data {\n     const struct hmap *local_svc_monitors_map;\n     const struct hmap *ic_learned_svc_monitors_map;\n@@ -925,7 +952,7 @@ void build_parsed_routes(const struct ovn_datapath *, const struct hmap *,\n uint32_t get_route_table_id(struct simap *, const char *);\n void routes_init(struct routes_data *);\n void routes_destroy(struct routes_data *);\n-\n+void routes_clear_tracked(struct routes_data *);\n void bfd_init(struct bfd_data *);\n void bfd_destroy(struct bfd_data *);\n \n@@ -951,6 +978,10 @@ void build_route_data_flows_for_lrouter(\n     const struct ovn_datapath *od, struct lflow_table *lflows,\n     const struct group_ecmp_datapath *route_node,\n     const struct sset *bfd_ports);\n+void build_arp_request_flows_for_lrouter(\n+    const struct ovn_datapath *od, struct lflow_table *lflows,\n+    const struct shash *meter_groups,\n+    struct lflow_ref *lflow_ref);\n \n bool lflow_handle_northd_lr_changes(struct ovsdb_idl_txn *ovnsh_txn,\n                                      struct tracked_dps *,\n@@ -1041,6 +1072,11 @@ northd_has_lr_nats_in_tracked_data(struct northd_tracked_data *trk_nd_changes)\n {\n     return trk_nd_changes->type & NORTHD_TRACKED_LR_NATS;\n }\n+static inline bool\n+northd_has_lr_route_in_tracked_data(struct northd_tracked_data *trk_nd_changes)\n+{\n+    return trk_nd_changes->type & NORTHD_TRACKED_LR_ROUTES;\n+}\n \n static inline bool\n northd_has_ls_lbs_in_tracked_data(struct northd_tracked_data *trk_nd_changes)\ndiff --git a/tests/ovn-inc-proc-graph-dump.at b/tests/ovn-inc-proc-graph-dump.at\nindex 178310978..fd05c20dc 100644\n--- a/tests/ovn-inc-proc-graph-dump.at\n+++ b/tests/ovn-inc-proc-graph-dump.at\n@@ -151,9 +151,11 @@ digraph \"Incremental-Processing-Engine\" {\n \tbfd [[style=filled, shape=box, fillcolor=white, label=\"bfd\"]];\n \tNB_bfd -> bfd [[label=\"\"]];\n \tSB_bfd -> bfd [[label=\"\"]];\n+\tNB_logical_router_static_route [[style=filled, shape=box, fillcolor=white, label=\"NB_logical_router_static_route\"]];\n \troutes [[style=filled, shape=box, fillcolor=white, label=\"routes\"]];\n \tbfd -> routes [[label=\"\"]];\n \tnorthd -> routes [[label=\"routes_northd_change_handler\"]];\n+\tNB_logical_router_static_route -> routes [[label=\"routes_static_route_change_handler\"]];\n \troute_policies [[style=filled, shape=box, fillcolor=white, label=\"route_policies\"]];\n \tbfd -> route_policies [[label=\"\"]];\n \tnorthd -> route_policies [[label=\"route_policies_northd_change_handler\"]];\n@@ -168,7 +170,7 @@ digraph \"Incremental-Processing-Engine\" {\n \tSB_learned_route -> learned_route_sync [[label=\"learned_route_sync_sb_learned_route_change_handler\"]];\n \tnorthd -> learned_route_sync [[label=\"learned_route_sync_northd_change_handler\"]];\n \tgroup_ecmp_route [[style=filled, shape=box, fillcolor=white, label=\"group_ecmp_route\"]];\n-\troutes -> group_ecmp_route [[label=\"\"]];\n+\troutes -> group_ecmp_route [[label=\"group_ecmp_static_route_change_handler\"]];\n \tlearned_route_sync -> group_ecmp_route [[label=\"group_ecmp_route_learned_route_change_handler\"]];\n \tls_stateful [[style=filled, shape=box, fillcolor=white, label=\"ls_stateful\"]];\n \tnorthd -> ls_stateful [[label=\"ls_stateful_northd_handler\"]];\n@@ -189,7 +191,7 @@ digraph \"Incremental-Processing-Engine\" {\n \tSB_logical_dp_group -> lflow [[label=\"\"]];\n \tbfd_sync -> lflow [[label=\"\"]];\n \troute_policies -> lflow [[label=\"\"]];\n-\troutes -> lflow [[label=\"\"]];\n+\troutes -> lflow [[label=\"lflow_group_route_change_handler\"]];\n \tgroup_ecmp_route -> lflow [[label=\"lflow_group_ecmp_route_change_handler\"]];\n \tglobal_config -> lflow [[label=\"node_global_config_handler\"]];\n \tsampling_app -> lflow [[label=\"\"]];\ndiff --git a/tests/ovn-northd.at b/tests/ovn-northd.at\nindex 1d7bd6c28..eacccaf20 100644\n--- a/tests/ovn-northd.at\n+++ b/tests/ovn-northd.at\n@@ -4272,9 +4272,9 @@ check ovn-nbctl --bfd=$uuid lr-route-add r0 100.0.0.0/8 192.168.1.2\n wait_column down bfd status logical_port=r0-sw1\n AT_CHECK([ovn-nbctl lr-route-list r0 | grep 192.168.1.2 | grep -q bfd], [0], [], [ignore])\n \n-check_engine_stats northd recompute nocompute\n+check_engine_stats northd norecompute compute\n check_engine_stats bfd recompute nocompute\n-check_engine_stats routes recompute nocompute\n+check_engine_stats routes recompute incremental\n check_engine_stats lflow recompute nocompute\n check_engine_stats northd_output norecompute compute\n CHECK_NO_CHANGE_AFTER_RECOMPUTE\n@@ -4288,9 +4288,9 @@ check ovn-nbctl --bfd lr-route-add r0 240.0.0.0/8 192.168.5.2 r0-sw5\n wait_column down bfd status logical_port=r0-sw5\n AT_CHECK([ovn-nbctl lr-route-list r0 | grep 192.168.5.2 | grep -q bfd], [0], [], [ignore])\n \n-check_engine_stats northd recompute nocompute\n+check_engine_stats northd norecompute compute\n check_engine_stats bfd recompute nocompute\n-check_engine_stats routes recompute nocompute\n+check_engine_stats routes recompute incremental\n check_engine_stats lflow recompute nocompute\n check_engine_stats northd_output norecompute compute\n CHECK_NO_CHANGE_AFTER_RECOMPUTE\n@@ -4300,7 +4300,7 @@ check ovn-nbctl --bfd --policy=src-ip lr-route-add r0 192.168.6.1/32 192.168.10.\n wait_column down bfd status logical_port=r0-sw6\n AT_CHECK([ovn-nbctl lr-route-list r0 | grep 192.168.6.1 | grep -q bfd], [0], [], [ignore])\n \n-check_engine_stats northd recompute nocompute\n+check_engine_stats northd norecompute compute\n check_engine_stats bfd recompute nocompute\n check_engine_stats route_policies recompute nocompute\n check_engine_stats lflow recompute nocompute\n@@ -4335,10 +4335,10 @@ wait_column down bfd status logical_port=r0-sw8\n bfd_route_policy_uuid=$(fetch_column nb:bfd _uuid logical_port=r0-sw8)\n AT_CHECK([ovn-nbctl list logical_router_policy | grep -q $bfd_route_policy_uuid])\n \n-check_engine_stats northd recompute nocompute\n+check_engine_stats northd recompute incremental\n check_engine_stats bfd recompute nocompute\n-check_engine_stats routes recompute nocompute\n-check_engine_stats lflow recompute nocompute\n+check_engine_stats routes recompute incremental\n+check_engine_stats lflow recompute incremental\n check_engine_stats northd_output norecompute compute\n CHECK_NO_CHANGE_AFTER_RECOMPUTE\n check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats\n@@ -16437,12 +16437,12 @@ CHECK_NO_CHANGE_AFTER_RECOMPUTE\n \n check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats\n check ovn-nbctl --wait=sb lr-route-add lr0 192.168.0.0/24 10.0.0.10\n-check_engine_compute northd recompute\n-check_engine_compute routes recompute\n+check_engine_compute northd incremental\n+check_engine_compute routes incremental\n check_engine_compute advertised_route_sync recompute\n-check_engine_compute learned_route_sync recompute\n-check_engine_compute group_ecmp_route recompute\n-check_engine_compute lflow recompute\n+check_engine_compute learned_route_sync incremental\n+check_engine_compute group_ecmp_route incremental\n+check_engine_compute lflow incremental\n CHECK_NO_CHANGE_AFTER_RECOMPUTE\n \n check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats\n@@ -20672,3 +20672,75 @@ check_column \"$global_svc_mon_mac\" sb:Service_Monitor src_mac port=2\n OVN_CLEANUP_NORTHD\n AT_CLEANUP\n ])\n+\n+OVN_FOR_EACH_NORTHD_NO_HV([\n+AT_SETUP([Static Route incremental processing])\n+ovn_start\n+\n+check ovn-nbctl lr-add r0\n+\n+check ovn-nbctl --wait=sb lrp-add r0 r0-lrp1 00:00:00:00:00:01 192.168.1.1/24\n+\n+check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats\n+check ovn-nbctl --wait=sb lr-route-add r0 10.0.0.0/24 192.168.1.2\n+\n+check_engine_compute northd incremental\n+check_engine_compute routes incremental\n+check_engine_compute group_ecmp_route incremental\n+check_engine_compute lflow incremental\n+\n+static_route_uuid=`ovn-nbctl --bare --columns _uuid find Logical_Router_Static_Route nexthop=192.168.1.2`\n+\n+check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats\n+check ovn-nbctl --wait=sb set logical_router_static_route $static_route_uuid nexthop=192.168.1.3\n+check_engine_compute northd incremental\n+check_engine_compute routes incremental\n+check_engine_compute group_ecmp_route incremental\n+check_engine_compute lflow incremental\n+\n+check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats\n+check ovn-nbctl --wait=sb set logical_router_static_route $static_route_uuid ip_prefix=10.0.1.0/24\n+check_engine_compute northd incremental\n+check_engine_compute routes incremental\n+check_engine_compute group_ecmp_route incremental\n+check_engine_compute lflow incremental\n+\n+check ovn-nbctl --wait=sb lrp-add r0 r0-lrp2 00:00:00:00:00:02 192.168.1.10/24\n+check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats\n+check ovn-nbctl --wait=sb set logical_router_static_route $static_route_uuid output_port=r0-lrp2\n+check_engine_compute northd incremental\n+check_engine_compute routes incremental\n+check_engine_compute group_ecmp_route incremental\n+check_engine_compute lflow incremental\n+\n+check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats\n+check ovn-nbctl --wait=sb set logical_router_static_route $static_route_uuid policy=src-ip\n+check_engine_compute northd incremental\n+check_engine_compute routes incremental\n+check_engine_compute group_ecmp_route incremental\n+check_engine_compute lflow incremental\n+\n+check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats\n+check ovn-nbctl --wait=sb set logical_router_static_route $static_route_uuid selection_fields=\"ip_proto,ip_src,ip_dst\"\n+check_engine_compute northd incremental\n+check_engine_compute routes incremental\n+check_engine_compute group_ecmp_route incremental\n+check_engine_compute lflow incremental\n+\n+check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats\n+check ovn-nbctl --wait=sb set logical_router_static_route $static_route_uuid options:ecmp_symmetric_reply=true\n+check_engine_compute northd incremental\n+check_engine_compute routes incremental\n+check_engine_compute group_ecmp_route incremental\n+check_engine_compute lflow incremental\n+\n+check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats\n+check ovn-nbctl remove logical_router r0 static_routes $static_route_uuid\n+check_engine_compute northd incremental\n+check_engine_compute routes incremental\n+check_engine_compute group_ecmp_route incremental\n+check_engine_compute lflow incremental\n+\n+OVN_CLEANUP_NORTHD\n+AT_CLEANUP\n+])\n",
    "prefixes": [
        "ovs-dev",
        "v2",
        "2/2"
    ]
}