diff mbox series

[ovs-dev,v3,13/27] ovn-northd-ddlog: Remove `lr` field from `Router`.

Message ID 20210507040659.26830-14-blp@ovn.org
State Accepted
Headers show
Series ddlog 5x performance improvement | expand

Commit Message

Ben Pfaff May 7, 2021, 4:06 a.m. UTC
From: Leonid Ryzhyk <lryzhyk@vmware.com>

`relation Router` stores the internal representation of a logical
router, consisting of values from the `nb::Logical_Router` table
augmented with some additional fields.  We used to do this by
copying the entire `Logical_Router` record inside `Router`.  This
proved highly inefficient in scenarios where the set of router ports
changes frequently.  Every such change modifies the `ports` array
inside `Logical_Router`, which triggers an update of the `Router`
object, which can cause a bunch of rules to update their outputs.  This
recomputation is unnecessary as none of these rules look at the `ports`
field (`ports` is a slightly backwards way to maintain the relationship
between ports and routers by storing the array of ports in the router
instead of having each port point to the router).

As a workaround, we no longer store the entire `Logical_Router` object
in the `Router` table, and instead only copy its relevant fields.

Signed-off-by: Leonid Ryzhyk <lryzhyk@vmware.com>
Signed-off-by: Ben Pfaff <blp@ovn.org>
---
 northd/lrouter.dl    |  47 ++++--
 northd/ovn_northd.dl | 370 +++++++++++++++++++++----------------------
 2 files changed, 218 insertions(+), 199 deletions(-)

Comments

0-day Robot May 7, 2021, 5:09 a.m. UTC | #1
Bleep bloop.  Greetings Ben Pfaff, I am a robot and I have tried out your patch.
Thanks for your contribution.

I encountered some error that I wasn't expecting.  See the details below.


checkpatch:
WARNING: Unexpected sign-offs from developers who are not authors or co-authors or committers: Ben Pfaff <blp@ovn.org>
Lines checked: 1596, Warnings: 1, Errors: 0


Please check this out.  If you feel there has been an error, please email aconole@redhat.com

Thanks,
0-day Robot
diff mbox series

Patch

diff --git a/northd/lrouter.dl b/northd/lrouter.dl
index f5738b2cc81d..07579fb45902 100644
--- a/northd/lrouter.dl
+++ b/northd/lrouter.dl
@@ -329,10 +329,10 @@  LogicalRouterNATs(lr, vec_empty()) :-
     nb::Logical_Router(._uuid = lr),
     not LogicalRouterNAT(lr, _).
 
-function get_force_snat_ip(lr: nb::Logical_Router, key_type: string): Set<v46_ip> =
+function get_force_snat_ip(options: Map<string,string>, key_type: string): Set<v46_ip> =
 {
     var ips = set_empty();
-    match (lr.options.get(key_type ++ "_force_snat_ip")) {
+    match (options.get(key_type ++ "_force_snat_ip")) {
         None -> (),
         Some{s} -> {
             for (token in s.split(" ")) {
@@ -346,8 +346,8 @@  function get_force_snat_ip(lr: nb::Logical_Router, key_type: string): Set<v46_ip
     ips
 }
 
-function has_force_snat_ip(lr: nb::Logical_Router, key_type: string): bool {
-    not get_force_snat_ip(lr, key_type).is_empty()
+function has_force_snat_ip(options: Map<string, string>, key_type: string): bool {
+    not get_force_snat_ip(options, key_type).is_empty()
 }
 
 function lb_force_snat_router_ip(lr_options: Map<string, string>): bool {
@@ -359,11 +359,11 @@  typedef LBForceSNAT = NoForceSNAT
                     | ForceSNAT
                     | SkipSNAT
 
-function snat_for_lb(lr: nb::Logical_Router, lb: Ref<nb::Load_Balancer>): LBForceSNAT {
+function snat_for_lb(lr_options: Map<string, string>, lb: Ref<nb::Load_Balancer>): LBForceSNAT {
     if (lb.options.get_bool_def("skip_snat", false)) {
         return SkipSNAT
     };
-    if (not get_force_snat_ip(lr, "lb").is_empty() or lb_force_snat_router_ip(lr.options)) {
+    if (not get_force_snat_ip(lr_options, "lb").is_empty() or lb_force_snat_router_ip(lr_options)) {
         return ForceSNAT
     };
     return NoForceSNAT
@@ -380,11 +380,11 @@  function snat_for_lb(lr: nb::Logical_Router, lb: Ref<nb::Load_Balancer>): LBForc
 relation LogicalRouterSnatIP(lr: uuid, snat_ip: v46_ip, nat: Option<NAT>)
 LogicalRouterSnatIP(lr._uuid, force_snat_ip, None) :-
     lr in nb::Logical_Router(),
-    var dnat_force_snat_ips = get_force_snat_ip(lr, "dnat"),
+    var dnat_force_snat_ips = get_force_snat_ip(lr.options, "dnat"),
     var lb_force_snat_ips = if (lb_force_snat_router_ip(lr.options)) {
         set_empty()
     } else {
-        get_force_snat_ip(lr, "lb")
+        get_force_snat_ip(lr.options, "lb")
     },
     var force_snat_ip = FlatMap(dnat_force_snat_ips.union(lb_force_snat_ips)).
 LogicalRouterSnatIP(lr, snat_ip, Some{nat}) :-
@@ -428,7 +428,6 @@  LogicalRouterLBs(lr, vec_empty()) :-
 
 /* Router relation collects all attributes of a logical router.
  *
- * `lr` - Logical_Router record from the NB database
  * `l3dgw_port` - optional redirect port (see `DistributedGatewayPort`)
  * `redirect_port_name` - derived redirect port name (or empty string if
  *      router does not have a redirect port)
@@ -442,7 +441,18 @@  LogicalRouterLBs(lr, vec_empty()) :-
 function chassis_redirect_name(port_name: string): string = "cr-${port_name}"
 
 relation &Router(
-    lr:                 nb::Logical_Router,
+    /* Fields copied from nb::Logical_Router. */
+    _uuid:              uuid,
+    name:               string,
+    static_routes:      Set<uuid>,
+    policies:           Set<uuid>,
+    enabled:            Option<bool>,
+    nat:                Set<uuid>,
+    load_balancer:      Set<uuid>,
+    options:            Map<string,string>,
+    external_ids:       Map<string,string>,
+
+    /* Additional computed fields. */
     l3dgw_port:         Option<nb::Logical_Router_Port>,
     redirect_port_name: string,
     is_gateway:         bool,
@@ -454,7 +464,16 @@  relation &Router(
     force_lb_snat: bool,
 )
 
-&Router(.lr = lr,
+&Router(._uuid         =    lr._uuid,
+        .name          =    lr.name,
+        .static_routes =    lr.static_routes,
+        .policies      =    lr.policies,
+        .enabled       =    lr.enabled,
+        .nat           =    lr.nat,
+        .load_balancer =    lr.load_balancer,
+        .options       =    lr.options,
+        .external_ids  =    lr.external_ids,
+
         .l3dgw_port = l3dgw_port,
         .redirect_port_name =
             match (l3dgw_port) {
@@ -586,7 +605,7 @@  relation &RouterPort(
     nb::Logical_Router_Port[lrp],
     Some{var networks} = extract_lrp_networks(lrp.mac, lrp.networks),
     LogicalRouterPort(lrp._uuid, lrouter_uuid),
-    router in &Router(.lr = nb::Logical_Router{._uuid = lrouter_uuid}),
+    router in &Router(._uuid = lrouter_uuid),
     RouterPortIsRedirect(lrp._uuid, is_redirect),
     RouterPortPeer(lrp._uuid, peer),
     mcast_cfg in &McastPortCfg(.port = lrp._uuid, .router_port = true),
@@ -713,7 +732,7 @@  RouterStaticRoute_(.router = router,
                    .nexthop = route.nexthop,
                    .output_port = route.output_port,
                    .ecmp_symmetric_reply = route.ecmp_symmetric_reply) :-
-    router in &Router(.lr = nb::Logical_Router{.static_routes = routes}),
+    router in &Router(.static_routes = routes),
     var route_id = FlatMap(routes),
     route in &StaticRoute(.lrsr = nb::Logical_Router_Static_Route{._uuid = route_id}).
 
@@ -734,7 +753,7 @@  RouterStaticRoute(router, key, dsts) :-
     rsr in RouterStaticRoute_(.router = router, .output_port = None),
     /* output_port is not specified, find the
      * router port matching the next hop. */
-    port in &RouterPort(.router = &Router{.lr = nb::Logical_Router{._uuid = router.lr._uuid}},
+    port in &RouterPort(.router = &Router{._uuid = router._uuid},
                         .networks = networks),
     Some{var src_ip} = find_lrp_member_ip(networks, rsr.nexthop),
     var dst = RouteDst{rsr.nexthop, src_ip, port, rsr.ecmp_symmetric_reply},
diff --git a/northd/ovn_northd.dl b/northd/ovn_northd.dl
index 6b45df739c67..fad633b1870e 100644
--- a/northd/ovn_northd.dl
+++ b/northd/ovn_northd.dl
@@ -180,8 +180,8 @@  OutProxy_Port_Binding(._uuid              = lsp._uuid,
         eids
     },
     Some{var router_port} = lsp.options.get("router-port"),
-    var opt_chassis = peer.and_then(|p| p.router.lr.options.get("chassis")),
-    var l3dgw_port = peer.and_then(|p| p.router.l3dgw_port),    
+    var opt_chassis = peer.and_then(|p| p.router.options.get("chassis")),
+    var l3dgw_port = peer.and_then(|p| p.router.l3dgw_port),
     (var __type, var options) = {
         var options = ["peer" -> router_port];
         match (opt_chassis) {
@@ -238,7 +238,7 @@  OutProxy_Port_Binding(._uuid              = lsp._uuid,
             (rport.lrp.options.get_bool_def("reside-on-redirect-chassis", false)
              and l3dgw_port.is_some()) or
             Some{rport.lrp} == l3dgw_port or
-            (rport.router.lr.options.contains_key("chassis") and
+            (rport.router.options.contains_key("chassis") and
              not sw.localnet_ports.is_empty())) {
             false -> set_empty(),
             true -> set_singleton(get_garp_nat_addresses(deref(rport)))
@@ -254,7 +254,7 @@  OutProxy_Port_Binding(._uuid              = lrp._uuid,
                       .gateway_chassis    = set_empty(),
                       .ha_chassis_group   = None,
                       .options            = options,
-                      .datapath           = router.lr._uuid,
+                      .datapath           = router._uuid,
                       .parent_port        = None,
                       .tag                = None, // always empty for router ports
                       .mac                = set_singleton("${lrp.mac} ${lrp.networks.join(\" \")}"),
@@ -262,7 +262,7 @@  OutProxy_Port_Binding(._uuid              = lrp._uuid,
                       .external_ids       = lrp.external_ids) :-
     rp in &RouterPort(.lrp = lrp, .router = &router, .peer = peer),
     RouterPortRAOptionsComplete(lrp._uuid, options0),
-    (var __type, var options1) = match (router.lr.options.get("chassis")) {
+    (var __type, var options1) = match (router.options.get("chassis")) {
         /* TODO: derived ports */
         None -> ("patch", map_empty()),
         Some{lrchassis} -> ("l3gateway", ["l3gateway-chassis" -> lrchassis])
@@ -910,8 +910,8 @@  sb::Out_Multicast_Group (._uuid    = hash128((sw.ls._uuid,name)),
 /* Create a multicast group to flood traffic and reports to router ports with
  * multicast flood enabled.
  */
-sb::Out_Multicast_Group (._uuid    = hash128((rtr.lr._uuid,name)),
-                        .datapath = rtr.lr._uuid,
+sb::Out_Multicast_Group (._uuid    = hash128((rtr._uuid,name)),
+                        .datapath = rtr._uuid,
                         .name = name,
                         .tunnel_key = tunnel_key,
                         .ports = port_ids) :-
@@ -930,7 +930,7 @@  OutProxy_Multicast_Group (.datapath   = switch.ls._uuid,
 /* Create a multicast group for each IGMP group learned by a Router.
  * 'tunnel_key' == 0 triggers an ID allocation later.
  */
-OutProxy_Multicast_Group (.datapath   = router.lr._uuid,
+OutProxy_Multicast_Group (.datapath   = router._uuid,
                           .name       = address,
                           .ports      = port_ids) :-
     IgmpRouterMulticastGroup(address, &router, port_ids).
@@ -4156,7 +4156,7 @@  for (&SwitchPort(.lsp = lsp,
                  .sw = &sw,
                  .peer = Some{&RouterPort{.lrp = lrp,
                                           .is_redirect = is_redirect,
-                                          .router = &Router{.lr = lr,
+                                          .router = &Router{._uuid = lr_uuid,
                                                             .redirect_port_name = redirect_port_name}}})
      if (lsp.addresses.contains("router") and lsp.__type != "external"))
 {
@@ -4194,7 +4194,7 @@  for (&SwitchPort(.lsp = lsp,
         /* Add ethernet addresses specified in NAT rules on
          * distributed logical routers. */
         if (is_redirect) {
-            for (LogicalRouterNAT(.lr = lr._uuid, .nat = nat)) {
+            for (LogicalRouterNAT(.lr = lr_uuid, .nat = nat)) {
                 if (nat.nat.__type == "dnat_and_snat") {
                     Some{var lport} = nat.nat.logical_port in
                     Some{var emac} = nat.nat.external_mac in
@@ -4387,10 +4387,10 @@  for (SwitchPortPSAddresses(.port = &SwitchPort{.lsp = lsp, .json_name = json_nam
 }
 
 /* Logical router ingress table ADMISSION: Admission control framework. */
-for (&Router(.lr = lr)) {
+for (&Router(._uuid = lr_uuid)) {
     /* Logical VLANs not supported.
      * Broadcast/multicast source address is invalid. */
-    Flow(.logical_datapath = lr._uuid,
+    Flow(.logical_datapath = lr_uuid,
          .stage            = s_ROUTER_IN_ADMISSION(),
          .priority         = 100,
          .__match          = "vlan.present || eth.src[40]",
@@ -4419,7 +4419,7 @@  for (&RouterPort(.lrp = lrp,
      * the pipeline.
      */
     var actions = "${rEG_INPORT_ETH_ADDR()} = ${lrp_networks.ea}; next;" in {
-        Flow(.logical_datapath = router.lr._uuid,
+        Flow(.logical_datapath = router._uuid,
              .stage            = s_ROUTER_IN_ADMISSION(),
              .priority         = 50,
              .__match          = "eth.mcast && inport == ${json_name}",
@@ -4433,7 +4433,7 @@  for (&RouterPort(.lrp = lrp,
                  * should only be received on the "redirect-chassis". */
                 " && is_chassis_resident(${json_string_escape(chassis_redirect_name(lrp.name))})"
             } else { "" } in
-        Flow(.logical_datapath = router.lr._uuid,
+        Flow(.logical_datapath = router._uuid,
              .stage            = s_ROUTER_IN_ADMISSION(),
              .priority         = 50,
              .__match          = __match,
@@ -4480,11 +4480,11 @@  for (&RouterPort(.lrp = lrp,
  * */
 
 /* Flows for LOOKUP_NEIGHBOR. */
-for (&Router(.lr = lr, .learn_from_arp_request = learn_from_arp_request))
+for (&Router(._uuid = lr_uuid, .learn_from_arp_request = learn_from_arp_request))
 var rLNR = rEGBIT_LOOKUP_NEIGHBOR_RESULT() in
 var rLNIR = rEGBIT_LOOKUP_NEIGHBOR_IP_RESULT() in
 {
-    Flow(.logical_datapath = lr._uuid,
+    Flow(.logical_datapath = lr_uuid,
          .stage            = s_ROUTER_IN_LOOKUP_NEIGHBOR(),
          .priority         = 100,
          .__match          = "arp.op == 2",
@@ -4493,7 +4493,7 @@  var rLNIR = rEGBIT_LOOKUP_NEIGHBOR_IP_RESULT() in
              { if (learn_from_arp_request) "" else "${rLNIR} = 1; " } ++
              "next;",
          .external_ids     = map_empty());
-    Flow(.logical_datapath = lr._uuid,
+    Flow(.logical_datapath = lr_uuid,
          .stage            = s_ROUTER_IN_LOOKUP_NEIGHBOR(),
          .priority         = 100,
          .__match          = "nd_na",
@@ -4502,7 +4502,7 @@  var rLNIR = rEGBIT_LOOKUP_NEIGHBOR_IP_RESULT() in
              { if (learn_from_arp_request) "" else "${rLNIR} = 1; " } ++
              "next;",
          .external_ids     = map_empty());
-    Flow(.logical_datapath = lr._uuid,
+    Flow(.logical_datapath = lr_uuid,
          .stage            = s_ROUTER_IN_LOOKUP_NEIGHBOR(),
          .priority         = 100,
          .__match          = "nd_ns",
@@ -4515,7 +4515,7 @@  var rLNIR = rEGBIT_LOOKUP_NEIGHBOR_IP_RESULT() in
 
     /* For other packet types, we can skip neighbor learning.
      * So set REGBIT_LOOKUP_NEIGHBOR_RESULT to 1. */
-    Flow(.logical_datapath = lr._uuid,
+    Flow(.logical_datapath = lr_uuid,
          .stage            = s_ROUTER_IN_LOOKUP_NEIGHBOR(),
          .priority         = 0,
          .__match          = "1",
@@ -4524,7 +4524,7 @@  var rLNIR = rEGBIT_LOOKUP_NEIGHBOR_IP_RESULT() in
 
     /* Flows for LEARN_NEIGHBOR. */
     /* Skip Neighbor learning if not required. */
-    Flow(.logical_datapath = lr._uuid,
+    Flow(.logical_datapath = lr_uuid,
          .stage            = s_ROUTER_IN_LEARN_NEIGHBOR(),
          .priority         = 100,
          .__match          =
@@ -4532,25 +4532,25 @@  var rLNIR = rEGBIT_LOOKUP_NEIGHBOR_IP_RESULT() in
              { if (learn_from_arp_request) "" else " || ${rLNIR} == 0" },
          .actions          = "next;",
          .external_ids     = map_empty());
-    Flow(.logical_datapath = lr._uuid,
+    Flow(.logical_datapath = lr_uuid,
          .stage            = s_ROUTER_IN_LEARN_NEIGHBOR(),
          .priority         = 90,
          .__match          = "arp",
          .actions          = "put_arp(inport, arp.spa, arp.sha); next;",
          .external_ids     = map_empty());
-    Flow(.logical_datapath = lr._uuid,
+    Flow(.logical_datapath = lr_uuid,
          .stage            = s_ROUTER_IN_LEARN_NEIGHBOR(),
          .priority         = 90,
          .__match          = "arp",
          .actions          = "put_arp(inport, arp.spa, arp.sha); next;",
          .external_ids     = map_empty());
-    Flow(.logical_datapath = lr._uuid,
+    Flow(.logical_datapath = lr_uuid,
          .stage            = s_ROUTER_IN_LEARN_NEIGHBOR(),
          .priority         = 90,
          .__match          = "nd_na",
          .actions          = "put_nd(inport, nd.target, nd.tll); next;",
          .external_ids     = map_empty());
-    Flow(.logical_datapath = lr._uuid,
+    Flow(.logical_datapath = lr_uuid,
          .stage            = s_ROUTER_IN_LEARN_NEIGHBOR(),
          .priority         = 90,
          .__match          = "nd_ns",
@@ -4583,7 +4583,7 @@  for (RouterPortNetworksIPv4Addr(rp@&RouterPort{.router = router}, addr)) {
             var actions = "${rLNR} = lookup_arp(inport, arp.spa, arp.sha); "
                           "${rLNIR} = 1; "
                           "next;" in
-            Flow(.logical_datapath = router.lr._uuid,
+            Flow(.logical_datapath = router._uuid,
                  .stage            = s_ROUTER_IN_LOOKUP_NEIGHBOR(),
                  .priority         = 110,
                  .__match          = __match.join(" && "),
@@ -4595,7 +4595,7 @@  for (RouterPortNetworksIPv4Addr(rp@&RouterPort{.router = router}, addr)) {
                       { if (learn_from_arp_request) "" else
                         "${rLNIR} = lookup_arp_ip(inport, arp.spa); " } ++
                       "next;" in
-        Flow(.logical_datapath = router.lr._uuid,
+        Flow(.logical_datapath = router._uuid,
              .stage            = s_ROUTER_IN_LOOKUP_NEIGHBOR(),
              .priority         = 100,
              .__match          = "${match0} && ${match1}",
@@ -4606,11 +4606,11 @@  for (RouterPortNetworksIPv4Addr(rp@&RouterPort{.router = router}, addr)) {
 
 
 /* Logical router ingress table IP_INPUT: IP Input. */
-for (router in &Router(.lr = lr, .mcast_cfg = &mcast_cfg)) {
+for (router in &Router(._uuid = lr_uuid, .mcast_cfg = &mcast_cfg)) {
     /* L3 admission control: drop multicast and broadcast source, localhost
      * source or destination, and zero network source or destination
      * (priority 100). */
-    Flow(.logical_datapath = lr._uuid,
+    Flow(.logical_datapath = lr_uuid,
          .stage            = s_ROUTER_IN_IP_INPUT(),
          .priority         = 100,
          .__match          = "ip4.src_mcast ||"
@@ -4627,7 +4627,7 @@  for (router in &Router(.lr = lr, .mcast_cfg = &mcast_cfg)) {
     * Drop IPv6 ND packets (priority 85). ND NA packets for router's own
     * IPs are handled with priority-90 flows.
     */
-    Flow(.logical_datapath = lr._uuid,
+    Flow(.logical_datapath = lr_uuid,
          .stage            = s_ROUTER_IN_IP_INPUT(),
          .priority         = 85,
          .__match          = "arp || nd",
@@ -4637,7 +4637,7 @@  for (router in &Router(.lr = lr, .mcast_cfg = &mcast_cfg)) {
     /* Allow IPv6 multicast traffic that's supposed to reach the
      * router pipeline (e.g., router solicitations).
      */
-    Flow(.logical_datapath = lr._uuid,
+    Flow(.logical_datapath = lr_uuid,
          .stage            = s_ROUTER_IN_IP_INPUT(),
          .priority         = 84,
          .__match          = "nd_rs || nd_ra",
@@ -4645,7 +4645,7 @@  for (router in &Router(.lr = lr, .mcast_cfg = &mcast_cfg)) {
          .external_ids     = map_empty());
 
     /* Drop other reserved multicast. */
-    Flow(.logical_datapath = lr._uuid,
+    Flow(.logical_datapath = lr_uuid,
          .stage            = s_ROUTER_IN_IP_INPUT(),
          .priority         = 83,
          .__match          = "ip6.mcast_rsvd",
@@ -4654,7 +4654,7 @@  for (router in &Router(.lr = lr, .mcast_cfg = &mcast_cfg)) {
 
     /* Allow other multicast if relay enabled (priority 82). */
     var mcast_action = { if (mcast_cfg.relay) { "next;" } else { "drop;" } } in
-    Flow(.logical_datapath = lr._uuid,
+    Flow(.logical_datapath = lr_uuid,
          .stage            = s_ROUTER_IN_IP_INPUT(),
          .priority         = 82,
          .__match          = "ip4.mcast || ip6.mcast",
@@ -4663,7 +4663,7 @@  for (router in &Router(.lr = lr, .mcast_cfg = &mcast_cfg)) {
 
     /* Drop Ethernet local broadcast.  By definition this traffic should
      * not be forwarded.*/
-    Flow(.logical_datapath = lr._uuid,
+    Flow(.logical_datapath = lr_uuid,
          .stage            = s_ROUTER_IN_IP_INPUT(),
          .priority         = 50,
          .__match          = "eth.bcast",
@@ -4672,7 +4672,7 @@  for (router in &Router(.lr = lr, .mcast_cfg = &mcast_cfg)) {
 
     /* TTL discard */
     Flow(
-        .logical_datapath = lr._uuid,
+        .logical_datapath = lr_uuid,
         .stage            = s_ROUTER_IN_IP_INPUT(),
         .priority         = 30,
         .__match          = "ip4 && ip.ttl == {0, 1}",
@@ -4681,7 +4681,7 @@  for (router in &Router(.lr = lr, .mcast_cfg = &mcast_cfg)) {
 
     /* Pass other traffic not already handled to the next table for
      * routing. */
-    Flow(.logical_datapath = lr._uuid,
+    Flow(.logical_datapath = lr_uuid,
          .stage            = s_ROUTER_IN_IP_INPUT(),
          .priority         = 0,
          .__match          = "1",
@@ -4768,7 +4768,7 @@  for (&RouterPort(.router = &router, .networks = networks, .lrp = lrp)
     var __match = "ip4.src == "                                  ++
                    format_v4_networks(networks, true)            ++
                    " && ${rEGBIT_EGRESS_LOOPBACK()} == 0" in
-    Flow(.logical_datapath = router.lr._uuid,
+    Flow(.logical_datapath = router._uuid,
          .stage            = s_ROUTER_IN_IP_INPUT(),
          .priority         = 100,
          .__match          = __match,
@@ -4783,7 +4783,7 @@  for (&RouterPort(.router = &router, .networks = networks, .lrp = lrp)
     var __match = "ip4.dst == "                                  ++
                   format_v4_networks(networks, false)            ++
                   " && icmp4.type == 8 && icmp4.code == 0" in
-    Flow(.logical_datapath = router.lr._uuid,
+    Flow(.logical_datapath = router._uuid,
          .stage            = s_ROUTER_IN_IP_INPUT(),
          .priority         = 90,
          .__match          = __match,
@@ -4804,7 +4804,7 @@  for (&RouterPort(.router = &router, .networks = networks, .lrp = lrp)
  * on a regular port, just reply with the port's ETH address.
  */
 LogicalRouterNatArpNdFlow(router, nat) :-
-    router in &Router(.lr = nb::Logical_Router{._uuid = lr}),
+    router in &Router(._uuid = lr),
     LogicalRouterNAT(.lr = lr, .nat = nat@NAT{.nat = &nb::NAT{.__type = __type}}),
     /* Skip SNAT entries for now, we handle unique SNAT IPs separately
      * below.
@@ -4831,8 +4831,8 @@  LogicalRouterArpNdFlow(router, nat, None, rEG_INPORT_ETH_ADDR(), None, false, 90
  * different ETH address.
  */
 LogicalRouterPortNatArpNdFlow(router, nat, l3dgw_port) :-
-    router in &Router(.lr = lr, .l3dgw_port = Some{l3dgw_port}),
-    LogicalRouterNAT(lr._uuid, nat),
+    router in &Router(._uuid = lr_uuid, .l3dgw_port = Some{l3dgw_port}),
+    LogicalRouterNAT(lr_uuid, nat),
     /* Skip SNAT entries for now, we handle unique SNAT IPs separately
      * below.
      */
@@ -4903,7 +4903,7 @@  relation LogicalRouterArpFlow(
     drop: bool,
     priority: integer,
     external_ids: Map<string,string>)
-Flow(.logical_datapath = lr.lr._uuid,
+Flow(.logical_datapath = lr._uuid,
      .stage = s_ROUTER_IN_IP_INPUT(),
      .priority = priority,
      .__match = __match,
@@ -4948,7 +4948,7 @@  relation LogicalRouterNdFlow(
     drop: bool,
     priority: integer,
     external_ids: Map<string,string>)
-Flow(.logical_datapath = lr.lr._uuid,
+Flow(.logical_datapath = lr._uuid,
      .stage = s_ROUTER_IN_IP_INPUT(),
      .priority = priority,
      .__match = __match,
@@ -4993,7 +4993,7 @@  for (RouterPortNetworksIPv4Addr(.port = &RouterPort{.lrp = lrp,
                                                     .is_redirect = is_redirect},
                                 .addr = addr))
 {
-    Flow(.logical_datapath = router.lr._uuid,
+    Flow(.logical_datapath = router._uuid,
          .stage            = s_ROUTER_IN_IP_INPUT(),
          .priority         = 40,
          .__match          = "inport == ${json_name} && ip4 && "
@@ -5028,7 +5028,7 @@  for (RouterPortNetworksIPv4Addr(.port = &RouterPort{.lrp = lrp,
 }
 
 for (&RouterPort(.lrp = lrp,
-                 .router = router@&Router{.lr = lr},
+                 .router = router@&Router{._uuid = lr_uuid},
                  .json_name = json_name,
                  .networks = networks,
                  .is_redirect = is_redirect))
@@ -5036,7 +5036,7 @@  var residence_check = match (is_redirect) {
     true -> Some{"is_chassis_resident(${router.redirect_port_name})"},
     false -> None
 } in {
-    for (RouterLBVIP(.router = &Router{.lr = nb::Logical_Router{._uuid= lr._uuid}}, .vip = vip)) {
+    for (RouterLBVIP(.router = &Router{._uuid= lr_uuid}, .vip = vip)) {
         Some{(var ip_address, _)} = ip_address_and_port_from_lb_key(vip) in {
             IPv4{var ipv4} = ip_address in
             LogicalRouterArpFlow(.lr = router,
@@ -5078,7 +5078,7 @@  Flow(.logical_datapath = lr_uuid,
     &RouterPort(.lrp = nb::Logical_Router_Port{._uuid = lrp_uuid},
                 .router = &Router{.snat_ips = snat_ips,
                                   .force_lb_snat = false,
-                                  .lr = nb::Logical_Router{._uuid = lr_uuid}},
+                                  ._uuid = lr_uuid},
                 .networks = networks),
     var addr = FlatMap(networks.ipv4_addrs),
     not snat_ips.contains_key(IPv4{addr.addr}),
@@ -5092,7 +5092,7 @@  Flow(.logical_datapath = lr_uuid,
     &RouterPort(.lrp = nb::Logical_Router_Port{._uuid = lrp_uuid},
                 .router = &Router{.snat_ips = snat_ips,
                                   .force_lb_snat = false,
-                                  .lr = nb::Logical_Router{._uuid = lr_uuid}},
+                                  ._uuid = lr_uuid},
                 .networks = networks),
     var addr = FlatMap(networks.ipv6_addrs),
     not snat_ips.contains_key(IPv6{addr.addr}),
@@ -5100,7 +5100,7 @@  Flow(.logical_datapath = lr_uuid,
 
 for (RouterPortNetworksIPv4Addr(
         .port = &RouterPort{
-            .router = &Router{.lr = lr,
+            .router = &Router{._uuid = lr_uuid,
                               .l3dgw_port = None,
                               .is_gateway = false},
             .lrp = lrp},
@@ -5108,7 +5108,7 @@  for (RouterPortNetworksIPv4Addr(
 {
     /* UDP/TCP/SCTP port unreachable. */
     var __match = "ip4 && ip4.dst == ${addr.addr} && !ip.later_frag && udp" in
-    Flow(.logical_datapath = lr._uuid,
+    Flow(.logical_datapath = lr_uuid,
          .stage            = s_ROUTER_IN_IP_INPUT(),
          .priority         = 80,
          .__match          = __match,
@@ -5122,7 +5122,7 @@  for (RouterPortNetworksIPv4Addr(
          .external_ids     = stage_hint(lrp._uuid));
 
     var __match = "ip4 && ip4.dst == ${addr.addr} && !ip.later_frag && tcp" in
-    Flow(.logical_datapath = lr._uuid,
+    Flow(.logical_datapath = lr_uuid,
          .stage            = s_ROUTER_IN_IP_INPUT(),
          .priority         = 80,
          .__match          = __match,
@@ -5133,7 +5133,7 @@  for (RouterPortNetworksIPv4Addr(
          .external_ids     = stage_hint(lrp._uuid));
 
     var __match = "ip4 && ip4.dst == ${addr.addr} && !ip.later_frag && sctp" in
-    Flow(.logical_datapath = lr._uuid,
+    Flow(.logical_datapath = lr_uuid,
          .stage            = s_ROUTER_IN_IP_INPUT(),
          .priority         = 80,
          .__match          = __match,
@@ -5144,7 +5144,7 @@  for (RouterPortNetworksIPv4Addr(
          .external_ids     = stage_hint(lrp._uuid));
 
     var __match = "ip4 && ip4.dst == ${addr.addr} && !ip.later_frag" in
-    Flow(.logical_datapath = lr._uuid,
+    Flow(.logical_datapath = lr_uuid,
          .stage            = s_ROUTER_IN_IP_INPUT(),
          .priority         = 70,
          .__match          = __match,
@@ -5159,7 +5159,7 @@  for (RouterPortNetworksIPv4Addr(
 }
 
 /* DHCPv6 reply handling */
-Flow(.logical_datapath = rp.router.lr._uuid,
+Flow(.logical_datapath = rp.router._uuid,
      .stage            = s_ROUTER_IN_IP_INPUT(),
      .priority         = 100,
      .__match          = "ip6.dst == ${ipv6_addr.addr} "
@@ -5184,7 +5184,7 @@  for (&RouterPort(.router = &router, .networks = networks, .lrp = lrp)
     var __match = "ip6.dst == "                   ++
                   format_v6_networks(networks)    ++
                   " && icmp6.type == 128 && icmp6.code == 0" in
-    Flow(.logical_datapath = router.lr._uuid,
+    Flow(.logical_datapath = router._uuid,
          .stage            = s_ROUTER_IN_IP_INPUT(),
          .priority         = 90,
          .__match          = __match,
@@ -5227,7 +5227,7 @@  for (RouterPortNetworksIPv6Addr(.port = &RouterPort{.lrp = lrp,
 
 /* UDP/TCP/SCTP port unreachable */
 for (RouterPortNetworksIPv6Addr(
-        .port = &RouterPort{.router = &Router{.lr = lr,
+        .port = &RouterPort{.router = &Router{._uuid = lr_uuid,
                                               .l3dgw_port = None,
                                               .is_gateway = false},
                             .lrp = lrp,
@@ -5235,7 +5235,7 @@  for (RouterPortNetworksIPv6Addr(
         .addr = addr))
 {
     var __match = "ip6 && ip6.dst == ${addr.addr} && !ip.later_frag && tcp" in
-    Flow(.logical_datapath = lr._uuid,
+    Flow(.logical_datapath = lr_uuid,
          .stage            = s_ROUTER_IN_IP_INPUT(),
          .priority         = 80,
          .__match          = __match,
@@ -5246,7 +5246,7 @@  for (RouterPortNetworksIPv6Addr(
          .external_ids     = stage_hint(lrp._uuid));
 
     var __match = "ip6 && ip6.dst == ${addr.addr} && !ip.later_frag && sctp" in
-    Flow(.logical_datapath = lr._uuid,
+    Flow(.logical_datapath = lr_uuid,
          .stage            = s_ROUTER_IN_IP_INPUT(),
          .priority         = 80,
          .__match          = __match,
@@ -5257,7 +5257,7 @@  for (RouterPortNetworksIPv6Addr(
          .external_ids     = stage_hint(lrp._uuid));
 
     var __match = "ip6 && ip6.dst == ${addr.addr} && !ip.later_frag && udp" in
-    Flow(.logical_datapath = lr._uuid,
+    Flow(.logical_datapath = lr_uuid,
          .stage            = s_ROUTER_IN_IP_INPUT(),
          .priority         = 80,
          .__match          = __match,
@@ -5271,7 +5271,7 @@  for (RouterPortNetworksIPv6Addr(
          .external_ids     = stage_hint(lrp._uuid));
 
     var __match = "ip6 && ip6.dst == ${addr.addr} && !ip.later_frag" in
-    Flow(.logical_datapath = lr._uuid,
+    Flow(.logical_datapath = lr_uuid,
          .stage            = s_ROUTER_IN_IP_INPUT(),
          .priority         = 70,
          .__match          = __match,
@@ -5304,7 +5304,7 @@  for (RouterPortNetworksIPv6Addr(.port = &RouterPort{.router = &router,
                   "icmp6.type = 3; /* Time exceeded */ "
                   "icmp6.code = 0; /* TTL exceeded in transit */ "
                   "next; };" in
-    Flow(.logical_datapath = router.lr._uuid,
+    Flow(.logical_datapath = router._uuid,
          .stage            = s_ROUTER_IN_IP_INPUT(),
          .priority         = 40,
          .__match          = __match,
@@ -5322,20 +5322,20 @@  function default_allow_flow(datapath: uuid, stage: Stage): Flow {
          .actions          = "next;",
          .external_ids     = map_empty()}
 }
-for (&Router(.lr = lr)) {
+for (&Router(._uuid = lr_uuid)) {
     /* Packets are allowed by default. */
-    Flow[default_allow_flow(lr._uuid, s_ROUTER_IN_DEFRAG())];
-    Flow[default_allow_flow(lr._uuid, s_ROUTER_IN_UNSNAT())];
-    Flow[default_allow_flow(lr._uuid, s_ROUTER_OUT_SNAT())];
-    Flow[default_allow_flow(lr._uuid, s_ROUTER_IN_DNAT())];
-    Flow[default_allow_flow(lr._uuid, s_ROUTER_OUT_UNDNAT())];
-    Flow[default_allow_flow(lr._uuid, s_ROUTER_OUT_EGR_LOOP())];
-    Flow[default_allow_flow(lr._uuid, s_ROUTER_IN_ECMP_STATEFUL())];
+    Flow[default_allow_flow(lr_uuid, s_ROUTER_IN_DEFRAG())];
+    Flow[default_allow_flow(lr_uuid, s_ROUTER_IN_UNSNAT())];
+    Flow[default_allow_flow(lr_uuid, s_ROUTER_OUT_SNAT())];
+    Flow[default_allow_flow(lr_uuid, s_ROUTER_IN_DNAT())];
+    Flow[default_allow_flow(lr_uuid, s_ROUTER_OUT_UNDNAT())];
+    Flow[default_allow_flow(lr_uuid, s_ROUTER_OUT_EGR_LOOP())];
+    Flow[default_allow_flow(lr_uuid, s_ROUTER_IN_ECMP_STATEFUL())];
 
     /* Send the IPv6 NS packets to next table. When ovn-controller
      * generates IPv6 NS (for the action - nd_ns{}), the injected
      * packet would go through conntrack - which is not required. */
-    Flow(.logical_datapath = lr._uuid,
+    Flow(.logical_datapath = lr_uuid,
          .stage            = s_ROUTER_OUT_SNAT(),
          .priority         = 120,
          .__match          = "nd_ns",
@@ -5408,7 +5408,7 @@  function lrouter_nat_add_ext_ip_match(
             };
 
             ("",
-             Some{Flow{.logical_datapath = router.lr._uuid,
+             Some{Flow{.logical_datapath = router._uuid,
                        .stage = if (is_src) { s_ROUTER_IN_DNAT() } else { s_ROUTER_OUT_SNAT() },
                        .priority = priority,
                        .__match = "${__match} && ${ipX}.${dir} == $${__as.name}",
@@ -5446,17 +5446,17 @@  Flow(.logical_datapath = logical_router,
 /* Higher priority rules to force SNAT with the router port ip.
  * This only takes effect when the packet has already been
  * load balanced once. */
-for (rp in &RouterPort(.router = &Router{.lr = lr}, .lrp = lrp)) {
-    if (lb_force_snat_router_ip(lr.options) and rp.peer != PeerNone) {
+for (rp in &RouterPort(.router = &Router{._uuid = lr_uuid, .options = lr_options}, .lrp = lrp)) {
+    if (lb_force_snat_router_ip(lr_options) and rp.peer != PeerNone) {
         Some{var ipv4} = rp.networks.ipv4_addrs.nth(0) in {
-            Flow(.logical_datapath = lr._uuid,
+            Flow(.logical_datapath = lr_uuid,
                  .stage = s_ROUTER_IN_UNSNAT(),
                  .priority = 110,
                  .__match = "inport == ${rp.json_name} && ip4.dst == ${ipv4.addr}",
                  .actions = "ct_snat;",
                  .external_ids = map_empty());
 
-            Flow(.logical_datapath = lr._uuid,
+            Flow(.logical_datapath = lr_uuid,
                  .stage = s_ROUTER_OUT_SNAT(),
                  .priority = 110,
                  .__match = "flags.force_snat_for_lb == 1 && ip4 && outport == ${rp.json_name}",
@@ -5474,14 +5474,14 @@  for (rp in &RouterPort(.router = &Router{.lr = lr}, .lrp = lrp)) {
          * last in the list. So add the flows only if n_ipv6_addrs > 1. */
         if (rp.networks.ipv6_addrs.len() > 1) {
             Some{var ipv6} = rp.networks.ipv6_addrs.nth(0) in {
-                Flow(.logical_datapath = lr._uuid,
+                Flow(.logical_datapath = lr_uuid,
                      .stage = s_ROUTER_IN_UNSNAT(),
                      .priority = 110,
                      .__match = "inport == ${rp.json_name} && ip6.dst == ${ipv6.addr}",
                      .actions = "ct_snat;",
                      .external_ids = map_empty());
 
-                Flow(.logical_datapath = lr._uuid,
+                Flow(.logical_datapath = lr_uuid,
                      .stage = s_ROUTER_OUT_SNAT(),
                      .priority = 110,
                      .__match = "flags.force_snat_for_lb == 1 && ip6 && outport == ${rp.json_name}",
@@ -5501,13 +5501,13 @@  for (rp in &RouterPort(.router = &Router{.lr = lr}, .lrp = lrp)) {
 /* NAT rules are only valid on Gateway routers and routers with
  * l3dgw_port (router has a port with "redirect-chassis"
  * specified). */
-for (r in &Router(.lr = lr,
+for (r in &Router(._uuid = lr_uuid,
                   .l3dgw_port = l3dgw_port,
                   .redirect_port_name = redirect_port_name,
                   .is_gateway = is_gateway)
      if l3dgw_port.is_some() or is_gateway)
 {
-    for (LogicalRouterNAT(.lr = lr._uuid, .nat = nat)) {
+    for (LogicalRouterNAT(.lr = lr_uuid, .nat = nat)) {
         var ipX = nat.external_ip.ipX() in
         var xx = nat.external_ip.xxreg() in
         /* Check the validity of nat->logical_ip. 'logical_ip' can
@@ -5516,7 +5516,7 @@  for (r in &Router(.lr = lr,
         true == match ((mask.is_all_ones(), nat.nat.__type)) {
             (_, "snat") -> true,
             (false, _) -> {
-                warn("bad ip ${nat.nat.logical_ip} for dnat in router ${uuid2str(lr._uuid)}");
+                warn("bad ip ${nat.nat.logical_ip} for dnat in router ${uuid2str(lr_uuid)}");
                 false
             },
             _ -> true
@@ -5548,7 +5548,7 @@  for (r in &Router(.lr = lr,
                     } else {
                         "ct_snat;"
                     } in
-                    Flow(.logical_datapath = lr._uuid,
+                    Flow(.logical_datapath = lr_uuid,
                          .stage            = s_ROUTER_IN_UNSNAT(),
                          .priority         = 90,
                          .__match          = "ip && ${ipX}.dst == ${nat.nat.external_ip}",
@@ -5572,7 +5572,7 @@  for (r in &Router(.lr = lr,
                     } else {
                         "ct_snat;"
                     } in
-                    Flow(.logical_datapath = lr._uuid,
+                    Flow(.logical_datapath = lr_uuid,
                          .stage            = s_ROUTER_IN_UNSNAT(),
                          .priority         = 100,
                          .__match          = __match,
@@ -5603,7 +5603,7 @@  for (r in &Router(.lr = lr,
                     Some{var f} = ext_flow in Flow[f];
 
                     var flag_action =
-                        if (has_force_snat_ip(lr, "dnat")) {
+                        if (has_force_snat_ip(r.options, "dnat")) {
                             /* Indicate to the future tables that a DNAT has taken
                              * place and a force SNAT needs to be done in the
                              * Egress SNAT table. */
@@ -5615,7 +5615,7 @@  for (r in &Router(.lr = lr,
                         "flags.loopback = 1; "
                         "ct_dnat(${ip_and_ports});"
                     } in
-                    Flow(.logical_datapath = lr._uuid,
+                    Flow(.logical_datapath = lr_uuid,
                          .stage            = s_ROUTER_IN_DNAT(),
                          .priority         = 100,
                          .__match          = __match ++ ext_ip_match,
@@ -5644,7 +5644,7 @@  for (r in &Router(.lr = lr,
                     } else {
                         "ct_dnat(${ip_and_ports});"
                     } in
-                    Flow(.logical_datapath = lr._uuid,
+                    Flow(.logical_datapath = lr_uuid,
                          .stage            = s_ROUTER_IN_DNAT(),
                          .priority         = 100,
                          .__match          = __match ++ ext_ip_match,
@@ -5659,7 +5659,7 @@  for (r in &Router(.lr = lr,
                 if (nat.nat.__type == "snat") {
                     var __match = "inport == ${gwport_name} && "
                                   "${ipX}.src == ${nat.nat.external_ip}" in
-                    Flow(.logical_datapath = lr._uuid,
+                    Flow(.logical_datapath = lr_uuid,
                          .stage            = s_ROUTER_IN_IP_INPUT(),
                          .priority         = 120,
                          .__match          = __match,
@@ -5674,7 +5674,7 @@  for (r in &Router(.lr = lr,
                     Some{value} -> "${value}",
                     None -> gwport.mac
                 } in
-                Flow(.logical_datapath = lr._uuid,
+                Flow(.logical_datapath = lr_uuid,
                      .stage            = s_ROUTER_IN_ARP_RESOLVE(),
                      .priority         = 100,
                      .__match          = __match,
@@ -5711,7 +5711,7 @@  for (r in &Router(.lr = lr,
                     } else {
                         "ct_dnat;"
                     } in
-                Flow(.logical_datapath = lr._uuid,
+                Flow(.logical_datapath = lr_uuid,
                      .stage            = s_ROUTER_OUT_UNDNAT(),
                      .priority         = 100,
                      .__match          = __match,
@@ -5746,7 +5746,7 @@  for (r in &Router(.lr = lr,
                         "ct_snat(${ip_and_ports});"
                     } in
                     Some{var plen} = mask.cidr_bits() in
-                    Flow(.logical_datapath = lr._uuid,
+                    Flow(.logical_datapath = lr_uuid,
                          .stage            = s_ROUTER_OUT_SNAT(),
                          .priority         = plen as bit<64> + 1,
                          .__match          = __match ++ ext_ip_match,
@@ -5784,7 +5784,7 @@  for (r in &Router(.lr = lr,
                     Some{var plen} = mask.cidr_bits() in
                     var priority = (plen as bit<64>) + 1 in
                     var centralized_boost = if (mac == None) 128 else 0 in
-                    Flow(.logical_datapath = lr._uuid,
+                    Flow(.logical_datapath = lr_uuid,
                          .stage            = s_ROUTER_OUT_SNAT(),
                          .priority         = priority + centralized_boost,
                          .__match          = __match ++ ext_ip_match,
@@ -5809,7 +5809,7 @@  for (r in &Router(.lr = lr,
              * down in the pipeline.
              */
             var actions = "${rEG_INPORT_ETH_ADDR()} = ${gwport.mac}; next;" in
-            Flow(.logical_datapath = lr._uuid,
+            Flow(.logical_datapath = lr_uuid,
                  .stage            = s_ROUTER_IN_ADMISSION(),
                  .priority         = 50,
                  .__match          = __match,
@@ -5836,7 +5836,7 @@  for (r in &Router(.lr = lr,
                 "eth.src = ${external_mac}; "
                 "${xx}${rEG_SRC()} = ${nat.nat.external_ip}; "
                 "next;" in
-            Flow(.logical_datapath = lr._uuid,
+            Flow(.logical_datapath = lr_uuid,
                  .stage            = s_ROUTER_IN_GW_REDIRECT(),
                  .priority         = 100,
                  .__match          = __match,
@@ -5872,7 +5872,7 @@  for (r in &Router(.lr = lr,
                 regs.join("") ++
                 "${rEGBIT_EGRESS_LOOPBACK()} = 1; "
                 "next(pipeline=ingress, table=0); };" in
-            Flow(.logical_datapath = lr._uuid,
+            Flow(.logical_datapath = lr_uuid,
                  .stage            = s_ROUTER_OUT_EGR_LOOP(),
                  .priority         = 100,
                  .__match          = __match,
@@ -5883,15 +5883,15 @@  for (r in &Router(.lr = lr,
 
     /* Handle force SNAT options set in the gateway router. */
     if (l3dgw_port == None) {
-        var dnat_force_snat_ips = get_force_snat_ip(lr, "dnat") in
+        var dnat_force_snat_ips = get_force_snat_ip(r.options, "dnat") in
         if (not dnat_force_snat_ips.is_empty())
-        LogicalRouterForceSnatFlows(.logical_router = lr._uuid,
+        LogicalRouterForceSnatFlows(.logical_router = lr_uuid,
                                     .ips = dnat_force_snat_ips,
                                     .context = "dnat");
 
-        var lb_force_snat_ips = get_force_snat_ip(lr, "lb") in
+        var lb_force_snat_ips = get_force_snat_ip(r.options, "lb") in
         if (not lb_force_snat_ips.is_empty())
-        LogicalRouterForceSnatFlows(.logical_router = lr._uuid,
+        LogicalRouterForceSnatFlows(.logical_router = lr_uuid,
                                     .ips = lb_force_snat_ips,
                                     .context = "lb");
 
@@ -5904,7 +5904,7 @@  for (r in &Router(.lr = lr,
         * does not have any feature that depends on the source
         * ip address being external IP address for IP routing,
         * we can do it here, saving a future re-circulation. */
-        Flow(.logical_datapath = lr._uuid,
+        Flow(.logical_datapath = lr_uuid,
              .stage            = s_ROUTER_IN_DNAT(),
              .priority         = 50,
              .__match          = "ip",
@@ -5925,11 +5925,11 @@  function nats_contain_vip(nats: Vec<NAT>, vip: v46_ip): bool {
 /* Load balancing and packet defrag are only valid on
  * Gateway routers or router with gateway port. */
 for (RouterLBVIP(
-        .router = &Router{.lr = lr,
-                          .l3dgw_port = l3dgw_port,
-                          .redirect_port_name = redirect_port_name,
-                          .is_gateway = is_gateway,
-                          .nats = nats},
+        .router = r@&Router{._uuid = lr_uuid,
+                            .l3dgw_port = l3dgw_port,
+                            .redirect_port_name = redirect_port_name,
+                            .is_gateway = is_gateway,
+                            .nats = nats},
         .lb = lb,
         .vip = vip,
         .backends = backends)
@@ -5940,7 +5940,7 @@  for (RouterLBVIP(
             for (HasEventElbMeter(has_elb_meter)) {
                 Some {(var __match, var __action)} =
                     build_empty_lb_event_flow(vip, lb, has_elb_meter) in
-                Flow(.logical_datapath = lr._uuid,
+                Flow(.logical_datapath = lr_uuid,
                      .stage            = s_ROUTER_IN_DNAT(),
                      .priority         = 130,
                      .__match          = __match,
@@ -5972,7 +5972,7 @@  for (RouterLBVIP(
          * We create one for each VIP:port pair; flows with the same IP and
          * different port numbers will produce identical flows that will
          * get merged by DDlog. */
-        Flow(.logical_datapath = lr._uuid,
+        Flow(.logical_datapath = lr_uuid,
              .stage            = s_ROUTER_IN_DEFRAG(),
              .priority         = 100,
              .__match          = __match,
@@ -5996,7 +5996,7 @@  for (RouterLBVIP(
                 (Some{gwport}, true) -> " && is_chassis_resident(${redirect_port_name})",
                 _ -> ""
             } in
-        var snat_for_lb = snat_for_lb(lr, lb) in
+        var snat_for_lb = snat_for_lb(r.options, lb) in
         {
             /* A match and actions for established connections. */
             var est_match = "ct.est && " ++ __match in
@@ -6006,7 +6006,7 @@  for (RouterLBVIP(
                     ForceSNAT -> "flags.force_snat_for_lb = 1; ct_dnat;",
                     _ -> "ct_dnat;"
                 } in
-            Flow(.logical_datapath = lr._uuid,
+            Flow(.logical_datapath = lr_uuid,
                  .stage            = s_ROUTER_IN_DNAT(),
                  .priority         = prio,
                  .__match          = est_match,
@@ -6028,7 +6028,7 @@  for (RouterLBVIP(
                 var match3 = "${ipX} && ${ipX}.dst == ${ip_address} && ${proto}" ++
                              if (port != 0) { " && ${proto}.dst == ${port}" }
                              else { "" } in
-                Flow(.logical_datapath = lr._uuid,
+                Flow(.logical_datapath = lr_uuid,
                      .stage            = s_ROUTER_IN_UNSNAT(),
                      .priority         = 120,
                      .__match          = match3,
@@ -6068,7 +6068,7 @@  for (RouterLBVIP(
                     ForceSNAT -> "flags.force_snat_for_lb = 1; ct_dnat;",
                     _ -> "ct_dnat;"
                 } in
-            Flow(.logical_datapath = lr._uuid,
+            Flow(.logical_datapath = lr_uuid,
                  .stage            = s_ROUTER_OUT_UNDNAT(),
                  .priority         = 120,
                  .__match          = undnat_match,
@@ -6083,7 +6083,7 @@  for (RouterLBVIP(
  * via add_router_lb_flow().  One flow is for specific matching
  * on ct.new with an action of "ct_lb($targets);".  The other
  * flow is for ct.est with an action of "ct_dnat;". */
-Flow(.logical_datapath = r.lr._uuid,
+Flow(.logical_datapath = r._uuid,
      .stage            = s_ROUTER_IN_DNAT(),
      .priority         = priority,
      .__match          = __match,
@@ -6092,7 +6092,7 @@  Flow(.logical_datapath = r.lr._uuid,
     r in &Router(),
     r.l3dgw_port.is_some() or r.is_gateway,
     LBVIPWithStatus[lbvip@&LBVIPWithStatus{.lb = lb}],
-    r.lr.load_balancer.contains(lb._uuid),
+    r.load_balancer.contains(lb._uuid),
     var __match
         = "ct.new && " ++
           get_match_for_lb_key(lbvip.vip_addr, lbvip.vip_port, lb.protocol, true) ++
@@ -6101,7 +6101,7 @@  Flow(.logical_datapath = r.lr._uuid,
               _ -> ""
           },
     var priority = if (lbvip.vip_port != 0) 120 else 110,
-    var force_snat = match (snat_for_lb(r.lr, lb)) {
+    var force_snat = match (snat_for_lb(r.options, lb)) {
         SkipSNAT -> "flags.skip_snat_for_lb = 1; ",
         ForceSNAT -> "flags.force_snat_for_lb = 1; ",
         _ -> ""
@@ -6247,7 +6247,7 @@  for (&RouterPort[port@RouterPort{.lrp = lrp@nb::Logical_Router_Port{.peer = None
                     Some{prf} -> ", router_preference = \"${prf}\""
                 } in
             var actions = actions0 ++ router_preference ++ prefix ++ "); next;" in
-            Flow(.logical_datapath = router.lr._uuid,
+            Flow(.logical_datapath = router._uuid,
                  .stage            = s_ROUTER_IN_ND_RA_OPTIONS(),
                  .priority         = 50,
                  .__match          = __match,
@@ -6261,7 +6261,7 @@  for (&RouterPort[port@RouterPort{.lrp = lrp@nb::Logical_Router_Port{.peer = None
                           "ip6.dst = ip6.src; ip6.src = ${ip6_str}; "
                           "outport = inport; flags.loopback = 1; "
                           "output;" in
-            Flow(.logical_datapath = router.lr._uuid,
+            Flow(.logical_datapath = router._uuid,
                  .stage            = s_ROUTER_IN_ND_RA_RESPONSE(),
                  .priority         = 50,
                  .__match          = __match,
@@ -6274,15 +6274,15 @@  for (&RouterPort[port@RouterPort{.lrp = lrp@nb::Logical_Router_Port{.peer = None
 
 /* Logical router ingress table ND_RA_OPTIONS, ND_RA_RESPONSE: RS responder, by
  * default goto next.  (priority 0)*/
-for (&Router(.lr = lr))
+for (&Router(._uuid = lr_uuid))
 {
-    Flow(.logical_datapath = lr._uuid,
+    Flow(.logical_datapath = lr_uuid,
          .stage            = s_ROUTER_IN_ND_RA_OPTIONS(),
          .priority         = 0,
          .__match          = "1",
          .actions          = "next;",
          .external_ids     = map_empty());
-    Flow(.logical_datapath = lr._uuid,
+    Flow(.logical_datapath = lr_uuid,
          .stage            = s_ROUTER_IN_ND_RA_RESPONSE(),
          .priority         = 0,
          .__match          = "1",
@@ -6344,7 +6344,7 @@  for (Route(.port        = port,
         "flags.loopback = 1; "
         "next;" in
     {
-        Flow(.logical_datapath = port.router.lr._uuid,
+        Flow(.logical_datapath = port.router._uuid,
              .stage            = s_ROUTER_IN_IP_ROUTING(),
              .priority         = priority as integer,
              .__match          = __match,
@@ -6352,7 +6352,7 @@  for (Route(.port        = port,
              .external_ids     = stage_hint(port.lrp._uuid));
 
         if (port.has_bfd) {
-            Flow(.logical_datapath = port.router.lr._uuid,
+            Flow(.logical_datapath = port.router._uuid,
                  .stage            = s_ROUTER_IN_IP_ROUTING(),
                  .priority         = priority as integer + 1,
                  .__match          = "${__match} && udp.dst == 3784",
@@ -6387,7 +6387,7 @@  Route(key, port, src_ip, None) :-
     var key = RouteKey{DstIp, IPv6{addr.addr}, addr.plen},
     var src_ip = IPv6{addr.addr}.
 
-Flow(.logical_datapath = r.lr._uuid,
+Flow(.logical_datapath = r._uuid,
      .stage            = s_ROUTER_IN_IP_ROUTING_ECMP(),
      .priority         = 150,
      .__match          = "${rEG_ECMP_GROUP_ID()} == 0",
@@ -6429,7 +6429,7 @@  EcmpGroup(group_id, router, key, dsts, route_match, route_priority) :-
     (var route_match, var route_priority0) = build_route_match(key),
     var route_priority = route_priority0 as integer.
 
-Flow(.logical_datapath = router.lr._uuid,
+Flow(.logical_datapath = router._uuid,
      .stage            = s_ROUTER_IN_IP_ROUTING(),
      .priority         = route_priority,
      .__match          = route_match,
@@ -6449,7 +6449,7 @@  Flow(.logical_datapath = router.lr._uuid,
         "${rEG_ECMP_GROUP_ID()} = ${group_id}; " /* XXX */
         "${rEG_ECMP_MEMBER_ID()} = select(${all_member_ids});".
 
-Flow(.logical_datapath = router.lr._uuid,
+Flow(.logical_datapath = router._uuid,
      .stage            = s_ROUTER_IN_IP_ROUTING_ECMP(),
      .priority         = 100,
      .__match          = __match,
@@ -6482,7 +6482,7 @@  EcmpSymmetricReply(router, dst, route_match, tunkey) :-
     dst.ecmp_symmetric_reply,
     PortTunKeyAllocation(.port = dst.port.lrp._uuid, .tunkey = tunkey).
 
-Flow(.logical_datapath = router.lr._uuid,
+Flow(.logical_datapath = router._uuid,
      .stage = s_ROUTER_IN_DEFRAG(),
      .priority = 100,
      .__match = __match,
@@ -6497,7 +6497,7 @@  Flow(.logical_datapath = router.lr._uuid,
 /* Save src eth and inport in ct_label for packets that arrive over
  * an ECMP route.
  */
-Flow(.logical_datapath = router.lr._uuid,
+Flow(.logical_datapath = router._uuid,
      .stage = s_ROUTER_IN_ECMP_STATEFUL(),
      .priority = 100,
      .__match = __match,
@@ -6512,7 +6512,7 @@  Flow(.logical_datapath = router.lr._uuid,
 /* Bypass ECMP selection if we already have ct_label information
  * for where to route the packet.
  */
-Flow(.logical_datapath = router.lr._uuid,
+Flow(.logical_datapath = router._uuid,
      .stage = s_ROUTER_IN_IP_ROUTING(),
      .priority = 100,
      .__match = "${ecmp_reply} && ${route_match}",
@@ -6524,13 +6524,13 @@  Flow(.logical_datapath = router.lr._uuid,
                 "next;",
      .external_ids = map_empty()),
 /* Egress reply traffic for symmetric ECMP routes skips router policies. */
-Flow(.logical_datapath = router.lr._uuid,
+Flow(.logical_datapath = router._uuid,
      .stage = s_ROUTER_IN_POLICY(),
      .priority = 65535,
      .__match = ecmp_reply,
      .actions = "next;",
      .external_ids = map_empty()),
-Flow(.logical_datapath = router.lr._uuid,
+Flow(.logical_datapath = router._uuid,
      .stage = s_ROUTER_IN_ARP_RESOLVE(),
      .priority = 200,
      .__match = ecmp_reply,
@@ -6547,7 +6547,7 @@  Flow(.logical_datapath = router.lr._uuid,
 /* Drop IPv6 multicast traffic that shouldn't be forwarded,
  * i.e., router solicitation and router advertisement.
  */
-Flow(.logical_datapath = router.lr._uuid,
+Flow(.logical_datapath = router._uuid,
      .stage            = s_ROUTER_IN_IP_ROUTING(),
      .priority         = 550,
      .__match          = "nd_rs || nd_ra",
@@ -6572,7 +6572,7 @@  for (IgmpRouterMulticastGroup(address, &rtr, ports)) {
         } in
         Some{var ip} = ip46_parse(address) in
         var ipX = ip.ipX() in
-        UniqueFlow[Flow{.logical_datapath = rtr.lr._uuid,
+        UniqueFlow[Flow{.logical_datapath = rtr._uuid,
                         .stage            = s_ROUTER_IN_IP_ROUTING(),
                         .priority         = 500,
                         .__match          = "${ipX} && ${ipX}.dst == ${address} ",
@@ -6598,7 +6598,7 @@  for (RouterMcastFloodPorts(&rtr, flood_ports) if rtr.mcast_cfg.relay) {
     } else {
         "drop;"
     } in
-    AnnotatedFlow(.f = Flow{.logical_datapath = rtr.lr._uuid,
+    AnnotatedFlow(.f = Flow{.logical_datapath = rtr._uuid,
                             .stage            = s_ROUTER_IN_IP_ROUTING(),
                             .priority         = 450,
                             .__match          = "ip4.mcast || ip6.mcast",
@@ -6616,17 +6616,17 @@  for (RouterMcastFloodPorts(&rtr, flood_ports) if rtr.mcast_cfg.relay) {
  * the appropriate register to the next-hop IP address (leaving
  * 'ip[46].dst', the packet’s final destination, unchanged), and
  * advances to the next table for ARP/ND resolution. */
-for (&Router(.lr = lr)) {
+for (&Router(._uuid = lr_uuid)) {
     /* This is a catch-all rule. It has the lowest priority (0)
      * does a match-all("1") and pass-through (next) */
-    Flow(.logical_datapath = lr._uuid,
+    Flow(.logical_datapath = lr_uuid,
          .stage            = s_ROUTER_IN_POLICY(),
          .priority         = 0,
          .__match          = "1",
          .actions          = "${rEG_ECMP_GROUP_ID()} = 0; next;",
          .external_ids     = map_empty());
 
-    Flow(.logical_datapath = lr._uuid,
+    Flow(.logical_datapath = lr_uuid,
          .stage            = s_ROUTER_IN_POLICY_ECMP(),
          .priority         = 150,
          .__match          = "${rEG_ECMP_GROUP_ID()} == 0",
@@ -6648,14 +6648,14 @@  function pkt_mark_policy(options: Map<string,string>): string {
         ""
     }
 }
-Flow(.logical_datapath = r.lr._uuid,
+Flow(.logical_datapath = r._uuid,
      .stage            = s_ROUTER_IN_POLICY(),
      .priority         = policy.priority,
      .__match          = policy.__match,
      .actions          = actions,
      .external_ids     = stage_hint(policy._uuid)) :-
     r in &Router(),
-    var policy_uuid = FlatMap(r.lr.policies),
+    var policy_uuid = FlatMap(r.policies),
     policy in nb::Logical_Router_Policy(._uuid = policy_uuid),
     policy.action == "reroute",
     Some{var nexthop_s} = match (policy.nexthops.size()) {
@@ -6699,7 +6699,7 @@  relation EcmpReroutePolicy(
 )
 EcmpReroutePolicy(r, policy, ecmp_group_id) :-
     r in &Router(),
-    var policy_uuid = FlatMap(r.lr.policies),
+    var policy_uuid = FlatMap(r.policies),
     policy in nb::Logical_Router_Policy(._uuid = policy_uuid),
     policy.action == "reroute",
     policy.nexthops.size() > 1,
@@ -6709,7 +6709,7 @@  EcmpReroutePolicy(r, policy, ecmp_group_id) :-
     var pair = FlatMap(numbered_policies),
     (var policy, var ecmp_group_id) = pair,
     all_same_addr_family(policy.nexthops).
-Flow(.logical_datapath = r.lr._uuid,
+Flow(.logical_datapath = r._uuid,
      .stage            = s_ROUTER_IN_POLICY_ECMP(),
      .priority         = 100,
      .__match          = __match,
@@ -6733,7 +6733,7 @@  Flow(.logical_datapath = r.lr._uuid,
                    "next;"),
     var __match = ("${rEG_ECMP_GROUP_ID()} == ${ecmp_group_id} && "
                    "${rEG_ECMP_MEMBER_ID()} == ${member_id}").
-Flow(.logical_datapath = r.lr._uuid,
+Flow(.logical_datapath = r._uuid,
      .stage            = s_ROUTER_IN_POLICY(),
      .priority         = policy.priority,
      .__match          = policy.__match,
@@ -6750,25 +6750,25 @@  Flow(.logical_datapath = r.lr._uuid,
     },
     var actions = ("${rEG_ECMP_GROUP_ID()} = ${ecmp_group_id}; "
                    "${rEG_ECMP_MEMBER_ID()} = select(${member_ids});").
-    
-Flow(.logical_datapath = r.lr._uuid,
+
+Flow(.logical_datapath = r._uuid,
      .stage            = s_ROUTER_IN_POLICY(),
      .priority         = policy.priority,
      .__match          = policy.__match,
      .actions          = "drop;",
      .external_ids     = stage_hint(policy._uuid)) :-
     r in &Router(),
-    var policy_uuid = FlatMap(r.lr.policies),
+    var policy_uuid = FlatMap(r.policies),
     policy in nb::Logical_Router_Policy(._uuid = policy_uuid),
     policy.action == "drop".
-Flow(.logical_datapath = r.lr._uuid,
+Flow(.logical_datapath = r._uuid,
      .stage            = s_ROUTER_IN_POLICY(),
      .priority         = policy.priority,
      .__match          = policy.__match,
      .actions          = pkt_mark_policy(policy.options) ++ "${rEG_ECMP_GROUP_ID()} = 0; next;",
      .external_ids     = stage_hint(policy._uuid)) :-
     r in &Router(),
-    var policy_uuid = FlatMap(r.lr.policies),
+    var policy_uuid = FlatMap(r.policies),
     policy in nb::Logical_Router_Policy(._uuid = policy_uuid),
     policy.action == "allow".
 
@@ -6780,8 +6780,8 @@  Flow(.logical_datapath = r.lr._uuid,
  * Multicast packets already have the outport set so just advance to next
  * table (priority 500).
  */
-for (&Router(.lr = lr)) {
-    Flow(.logical_datapath = lr._uuid,
+for (&Router(._uuid = lr_uuid)) {
+    Flow(.logical_datapath = lr_uuid,
          .stage            = s_ROUTER_IN_ARP_RESOLVE(),
          .priority         = 500,
          .__match          = "ip4.mcast || ip6.mcast",
@@ -6816,7 +6816,7 @@  for (rp in &RouterPort(.peer = PeerRouter{peer_port, _},
             var __match = "outport == ${peer_json_name} && "
                           "${rEG_NEXT_HOP()} == " ++
                           format_v4_networks(networks, false) in
-            Flow(.logical_datapath = peer_router.lr._uuid,
+            Flow(.logical_datapath = peer_router._uuid,
                  .stage            = s_ROUTER_IN_ARP_RESOLVE(),
                  .priority         = 100,
                  .__match          = __match,
@@ -6828,7 +6828,7 @@  for (rp in &RouterPort(.peer = PeerRouter{peer_port, _},
             var __match = "outport == ${peer_json_name} && "
                           "xx${rEG_NEXT_HOP()} == " ++
                           format_v6_networks(networks) in
-            Flow(.logical_datapath = peer_router.lr._uuid,
+            Flow(.logical_datapath = peer_router._uuid,
                  .stage            = s_ROUTER_IN_ARP_RESOLVE(),
                  .priority         = 100,
                  .__match          = __match,
@@ -6844,7 +6844,7 @@  for (rp in &RouterPort(.peer = PeerRouter{peer_port, _},
  * is "bridged", instead of calling "get_arp"
  * on this node, we will redirect the packet to gateway
  * chassis, by setting destination mac router port mac.*/
-Flow(.logical_datapath = router.lr._uuid,
+Flow(.logical_datapath = router._uuid,
      .stage            = s_ROUTER_IN_ARP_RESOLVE(),
      .priority         = 50,
      .__match          = "outport == ${rp.json_name} && "
@@ -6870,7 +6870,7 @@  Flow(.logical_datapath = lr_uuid,
      .external_ids = stage_hint(lrp_uuid)) :-
     &RouterPort(.lrp = nb::Logical_Router_Port{._uuid = lrp_uuid},
                 .router = &Router{.snat_ips = snat_ips,
-                                  .lr = nb::Logical_Router{._uuid = lr_uuid}},
+                                  ._uuid = lr_uuid},
                 .networks = networks),
     var addr = FlatMap(networks.ipv4_addrs),
     snat_ips.contains_key(IPv4{addr.addr}),
@@ -6883,7 +6883,7 @@  Flow(.logical_datapath = lr_uuid,
      .external_ids = stage_hint(lrp_uuid)) :-
     &RouterPort(.lrp = nb::Logical_Router_Port{._uuid = lrp_uuid},
                 .router = &Router{.snat_ips = snat_ips,
-                                  .lr = nb::Logical_Router{._uuid = lr_uuid}},
+                                  ._uuid = lr_uuid},
                 .networks = networks),
     var addr = FlatMap(networks.ipv6_addrs),
     snat_ips.contains_key(IPv6{addr.addr}),
@@ -6904,7 +6904,7 @@  for (SwitchPortIPv4Address(
                      .peer = Some{&peer@RouterPort{.router = &peer_router}}))
     {
         Some{_} = find_lrp_member_ip(peer.networks, IPv4{addr.addr}) in
-        Flow(.logical_datapath = peer_router.lr._uuid,
+        Flow(.logical_datapath = peer_router._uuid,
              .stage            = s_ROUTER_IN_ARP_RESOLVE(),
              .priority         = 100,
              .__match          = "outport == ${peer.json_name} && "
@@ -6924,7 +6924,7 @@  for (SwitchPortIPv6Address(
                      .peer = Some{&peer@RouterPort{.router = &peer_router}}))
     {
         Some{_} = find_lrp_member_ip(peer.networks, IPv6{addr.addr}) in
-        Flow(.logical_datapath = peer_router.lr._uuid,
+        Flow(.logical_datapath = peer_router._uuid,
              .stage            = s_ROUTER_IN_ARP_RESOLVE(),
              .priority         = 100,
              .__match          = "outport == ${peer.json_name} && "
@@ -6954,7 +6954,7 @@  function is_empty_set_or_string(s: Option<string>): bool = {
  * 00:00:00:00:00:00 and advance to next table so that ARP is
  * resolved by router pipeline using the arp{} action.
  * The MAC_Binding entry for the virtual ip might be invalid. */
-Flow(.logical_datapath = peer.router.lr._uuid,
+Flow(.logical_datapath = peer.router._uuid,
      .stage            = s_ROUTER_IN_ARP_RESOLVE(),
      .priority         = 100,
      .__match          = "outport == ${peer.json_name} && "
@@ -6969,7 +6969,7 @@  Flow(.logical_datapath = peer.router.lr._uuid,
     is_empty_set_or_string(pb.virtual_parent) or pb.chassis == None,
     sp2 in &SwitchPort(.sw = sp.sw, .peer = Some{peer}),
     Some{_} = find_lrp_member_ip(peer.networks, IPv4{virtual_ip}).
-Flow(.logical_datapath = peer.router.lr._uuid,
+Flow(.logical_datapath = peer.router._uuid,
      .stage            = s_ROUTER_IN_ARP_RESOLVE(),
      .priority         = 100,
      .__match          = "outport == ${peer.json_name} && "
@@ -6998,7 +6998,7 @@  for (&SwitchPort(.lsp = lsp1,
                  .peer = Some{&peer1@RouterPort{.router = &peer_router}},
                  .sw = &sw)
      if lsp1.is_enabled() and
-        not peer_router.lr.options.get_bool_def("dynamic_neigh_routers", false))
+        not peer_router.options.get_bool_def("dynamic_neigh_routers", false))
 {
     for (&SwitchPort(.lsp = lsp2, .peer = Some{&peer2},
                      .sw = &Switch{.ls = nb::Logical_Switch{._uuid = sw.ls._uuid}})
@@ -7006,7 +7006,7 @@  for (&SwitchPort(.lsp = lsp1,
          if peer2.lrp._uuid != peer1.lrp._uuid)
     {
         if (not peer2.networks.ipv4_addrs.is_empty()) {
-            Flow(.logical_datapath = peer_router.lr._uuid,
+            Flow(.logical_datapath = peer_router._uuid,
                  .stage            = s_ROUTER_IN_ARP_RESOLVE(),
                  .priority         = 100,
                  .__match          = "outport == ${peer1.json_name} && "
@@ -7016,7 +7016,7 @@  for (&SwitchPort(.lsp = lsp1,
         };
 
         if (not peer2.networks.ipv6_addrs.is_empty()) {
-            Flow(.logical_datapath = peer_router.lr._uuid,
+            Flow(.logical_datapath = peer_router._uuid,
                  .stage            = s_ROUTER_IN_ARP_RESOLVE(),
                  .priority         = 100,
                  .__match          = "outport == ${peer1.json_name} && "
@@ -7027,15 +7027,15 @@  for (&SwitchPort(.lsp = lsp1,
     }
 }
 
-for (&Router(.lr = lr))
+for (&Router(._uuid = lr_uuid))
 {
-    Flow(.logical_datapath = lr._uuid,
+    Flow(.logical_datapath = lr_uuid,
          .stage            = s_ROUTER_IN_ARP_RESOLVE(),
          .priority         = 0,
          .__match          = "ip4",
          .actions          = "get_arp(outport, ${rEG_NEXT_HOP()}); next;",
          .external_ids     = map_empty());
-    Flow(.logical_datapath = lr._uuid,
+    Flow(.logical_datapath = lr_uuid,
          .stage            = s_ROUTER_IN_ARP_RESOLVE(),
          .priority         = 0,
          .__match          = "ip6",
@@ -7056,35 +7056,35 @@  for (&Router(.lr = lr))
  * generate ICMPv4 packet with type 3 (Destination Unreachable) and
  * code 4 (Fragmentation needed).
  * */
-Flow(.logical_datapath = lr._uuid,
+Flow(.logical_datapath = lr_uuid,
      .stage            = s_ROUTER_IN_CHK_PKT_LEN(),
      .priority         = 0,
      .__match          = "1",
      .actions          = "next;",
      .external_ids     = map_empty()) :-
-    &Router(.lr = lr).
-Flow(.logical_datapath = lr._uuid,
+    &Router(._uuid = lr_uuid).
+Flow(.logical_datapath = lr_uuid,
      .stage            = s_ROUTER_IN_LARGER_PKTS(),
      .priority         = 0,
      .__match          = "1",
      .actions          = "next;",
      .external_ids     = map_empty()) :-
-    &Router(.lr = lr).
-Flow(.logical_datapath = lr._uuid,
+    &Router(._uuid = lr_uuid).
+Flow(.logical_datapath = lr_uuid,
      .stage            = s_ROUTER_IN_CHK_PKT_LEN(),
      .priority         = 50,
      .__match          = "outport == ${l3dgw_port_json_name}",
      .actions          = "${rEGBIT_PKT_LARGER()} = check_pkt_larger(${mtu}); "
                          "next;",
      .external_ids     = stage_hint(l3dgw_port._uuid)) :-
-    r in &Router(.lr = lr),
+    r in &Router(._uuid = lr_uuid),
     Some{var l3dgw_port} = r.l3dgw_port,
     var l3dgw_port_json_name = json_string_escape(l3dgw_port.name),
     r.redirect_port_name != "",
     var gw_mtu = l3dgw_port.options.get_int_def("gateway_mtu", 0),
     gw_mtu > 0,
     var mtu = gw_mtu + vLAN_ETH_HEADER_LEN().
-Flow(.logical_datapath = lr._uuid,
+Flow(.logical_datapath = lr_uuid,
      .stage            = s_ROUTER_IN_LARGER_PKTS(),
      .priority         = 50,
      .__match          = "inport == ${rp.json_name} && outport == ${l3dgw_port_json_name} && "
@@ -7102,7 +7102,7 @@  Flow(.logical_datapath = lr._uuid,
                          "next(pipeline=ingress, table=0); "
                          "};",
      .external_ids     = stage_hint(rp.lrp._uuid)) :-
-    r in &Router(.lr = lr),
+    r in &Router(._uuid = lr_uuid),
     Some{var l3dgw_port} = r.l3dgw_port,
     var l3dgw_port_json_name = json_string_escape(l3dgw_port.name),
     r.redirect_port_name != "",
@@ -7111,7 +7111,7 @@  Flow(.logical_datapath = lr._uuid,
     rp in &RouterPort(.router = r),
     rp.lrp != l3dgw_port,
     Some{var first_ipv4} = rp.networks.ipv4_addrs.nth(0).
-Flow(.logical_datapath = lr._uuid,
+Flow(.logical_datapath = lr_uuid,
      .stage            = s_ROUTER_IN_LARGER_PKTS(),
      .priority         = 50,
      .__match          = "inport == ${rp.json_name} && outport == ${l3dgw_port_json_name} && "
@@ -7129,7 +7129,7 @@  Flow(.logical_datapath = lr._uuid,
                          "next(pipeline=ingress, table=0); "
                          "};",
      .external_ids     = stage_hint(rp.lrp._uuid)) :-
-    r in &Router(.lr = lr),
+    r in &Router(._uuid = lr_uuid),
     Some{var l3dgw_port} = r.l3dgw_port,
     var l3dgw_port_json_name = json_string_escape(l3dgw_port.name),
     r.redirect_port_name != "",
@@ -7146,7 +7146,7 @@  Flow(.logical_datapath = lr._uuid,
  * of the traffic to the l3redirect_port which represents
  * the central instance of the l3dgw_port.
  */
-for (&Router(.lr = lr,
+for (&Router(._uuid = lr_uuid,
              .l3dgw_port = l3dgw_port,
              .redirect_port_name = redirect_port_name))
 {
@@ -7155,7 +7155,7 @@  for (&Router(.lr = lr,
      * rule, then the traffic is redirected to the central
      * instance of the l3dgw_port. */
     Some{var gwport} = l3dgw_port in
-    Flow(.logical_datapath = lr._uuid,
+    Flow(.logical_datapath = lr_uuid,
          .stage            = s_ROUTER_IN_GW_REDIRECT(),
          .priority         = 50,
          .__match          = "outport == ${json_string_escape(gwport.name)}",
@@ -7163,7 +7163,7 @@  for (&Router(.lr = lr,
          .external_ids     = stage_hint(gwport._uuid));
 
     /* Packets are allowed by default. */
-    Flow(.logical_datapath = lr._uuid,
+    Flow(.logical_datapath = lr_uuid,
          .stage            = s_ROUTER_IN_GW_REDIRECT(),
          .priority         = 0,
          .__match          = "1",
@@ -7176,7 +7176,7 @@  for (&Router(.lr = lr,
  * In the common case where the Ethernet destination has been resolved,
  * this table outputs the packet (priority 0).  Otherwise, it composes
  * and sends an ARP/IPv6 NA request (priority 100). */
-Flow(.logical_datapath = router.lr._uuid,
+Flow(.logical_datapath = router._uuid,
      .stage            = s_ROUTER_IN_ARP_REQUEST(),
      .priority         = 200,
      .__match          = __match,
@@ -7197,9 +7197,9 @@  Flow(.logical_datapath = router.lr._uuid,
                   "output; "
                   "};".
 
-for (&Router(.lr = lr))
+for (&Router(._uuid = lr_uuid))
 {
-    Flow(.logical_datapath = lr._uuid,
+    Flow(.logical_datapath = lr_uuid,
          .stage            = s_ROUTER_IN_ARP_REQUEST(),
          .priority         = 100,
          .__match          = "eth.dst == 00:00:00:00:00:00 && ip4",
@@ -7212,7 +7212,7 @@  for (&Router(.lr = lr))
                              "};",
          .external_ids     = map_empty());
 
-    Flow(.logical_datapath = lr._uuid,
+    Flow(.logical_datapath = lr_uuid,
          .stage            = s_ROUTER_IN_ARP_REQUEST(),
          .priority         = 100,
          .__match          = "eth.dst == 00:00:00:00:00:00 && ip6",
@@ -7222,7 +7222,7 @@  for (&Router(.lr = lr))
                              "};",
          .external_ids     = map_empty());
 
-    Flow(.logical_datapath = lr._uuid,
+    Flow(.logical_datapath = lr_uuid,
          .stage            = s_ROUTER_IN_ARP_REQUEST(),
          .priority         = 0,
          .__match          = "1",
@@ -7237,7 +7237,7 @@  for (&Router(.lr = lr))
 for (&RouterPort(.lrp = lrp,
                  .json_name = json_name,
                  .networks = lrp_networks,
-                 .router = &Router{.lr = lr, .mcast_cfg = &mcast_cfg})
+                 .router = &Router{._uuid = lr_uuid, .mcast_cfg = &mcast_cfg})
      /* Drop packets to disabled logical ports (since logical flow
       * tables are default-drop). */
      if lrp.is_enabled())
@@ -7246,7 +7246,7 @@  for (&RouterPort(.lrp = lrp,
      * multicast traffic.
      */
     if (mcast_cfg.relay) {
-        Flow(.logical_datapath = lr._uuid,
+        Flow(.logical_datapath = lr_uuid,
              .stage            = s_ROUTER_OUT_DELIVERY(),
              .priority         = 110,
              .__match          = "(ip4.mcast || ip6.mcast) && "
@@ -7259,7 +7259,7 @@  for (&RouterPort(.lrp = lrp,
      * be replaced by the l3dgw port in the local output
      * pipeline stage before egress processing. */
 
-    Flow(.logical_datapath = lr._uuid,
+    Flow(.logical_datapath = lr_uuid,
          .stage            = s_ROUTER_OUT_DELIVERY(),
          .priority         = 100,
          .__match          = "outport == ${json_name}",
@@ -7405,7 +7405,7 @@  RequestedPortTunKey(datapath, port, tunkey) :-
     Some{var tunkey} = get_port_tunkey(sp.lsp.options, "requested-tnl-key").
 RequestedPortTunKey(datapath, port, tunkey) :-
     rp in &RouterPort(),
-    var datapath = rp.router.lr._uuid,
+    var datapath = rp.router._uuid,
     var port = rp.lrp._uuid,
     Some{var tunkey} = get_port_tunkey(rp.lrp.options, "requested-tnl-key").
 Warning[message] :-
@@ -7909,7 +7909,7 @@  function lrouter_bfd_flows(lr_uuid: uuid, lrp_uuid: uuid, ipX: string, networks:
 }
 for (&RouterPort(.router = &router, .networks = networks, .lrp = lrp, .has_bfd = true)) {
     if (not networks.ipv4_addrs.is_empty()) {
-        (var a, var b) = lrouter_bfd_flows(router.lr._uuid, lrp._uuid, "ip4",
+        (var a, var b) = lrouter_bfd_flows(router._uuid, lrp._uuid, "ip4",
                                            format_v4_networks(networks, false)) in {
             Flow[a];
             Flow[b]
@@ -7917,14 +7917,14 @@  for (&RouterPort(.router = &router, .networks = networks, .lrp = lrp, .has_bfd =
     };
 
     if (not networks.ipv6_addrs.is_empty()) {
-        (var a, var b) = lrouter_bfd_flows(router.lr._uuid, lrp._uuid, "ip6",
+        (var a, var b) = lrouter_bfd_flows(router._uuid, lrp._uuid, "ip6",
                                            format_v6_networks(networks)) in {
             Flow[a];
             Flow[b]
         }
     }
-}    
-                                          
+}
+
 /* Clean up stale FDB entries. */
 sb::Out_FDB(_uuid, mac, dp_key, port_key) :-
     sb::FDB(_uuid, mac, dp_key, port_key),