When transit traffic arrives on an l3dgw port with TTL=0 or TTL=1,
northd generates an ICMP Time Exceeded reply. Previously, the action
for l3dgw ports used "ip4.dst <-> ip4.src", which swaps the source
and destination addresses. This sets the ICMP reply source to the
original packet's destination, instead of the receiving router port
address. For example, a packet from 203.0.113.50 destined to
10.0.0.5 would produce an ICMP error with source 10.0.0.5 instead
of the router port's own address.

Use "ip4.dst = ip4.src; ip4.src = <router_port_ip>" and
"eth.dst = eth.src; eth.src = %s" unconditionally
for both l3dgw and non-l3dgw ports, so the ICMP reply MAC and IP
address are always the router port's address in the matching subnet.

Fixes: 4ecc61247d86 ("northd: fix ttl exceeded with FIP")
Reported-at: https://redhat.atlassian.net/browse/FDP-4083
Signed-off-by: Mairtin O'Loingsigh <[email protected]>
---
v1 -> v2:
  - Set source MAC to router port MAC.

 NEWS                |   2 +
 northd/northd.c     |  68 ++++++++++++-------------
 tests/ovn-northd.at | 119 ++++++++++++++++++++++++++++++++++----------
 3 files changed, 128 insertions(+), 61 deletions(-)

diff --git a/NEWS b/NEWS
index 68cca917e..0b6aa4389 100644
--- a/NEWS
+++ b/NEWS
@@ -1,5 +1,7 @@
 Post v26.03.0
 -------------
+   - Fixed support for TTL expired. Use router port IP and MAC instead of dest
+     IP and MAC for ICMP Time exceeded response.
    - Added Transit Switch port support with new ovn-ic-nbctl 'tsp-add',
      'tsp-del' and 'tsp-set-addr' commands.
    - Added ability to set any "ipsec_*" NB_Global option to configure the
diff --git a/northd/northd.c b/northd/northd.c
index aecfb2ec6..3a3463011 100644
--- a/northd/northd.c
+++ b/northd/northd.c
@@ -16984,19 +16984,9 @@ build_lrouter_ipv4_default_ttl_expired_flows(
         return;
     }
 
-    struct ds ip_ds = DS_EMPTY_INITIALIZER;
     for (int i = 0; i < op->lrp_networks.n_ipv4_addrs; i++) {
         ds_clear(match);
         ds_clear(actions);
-        ds_clear(&ip_ds);
-        if (lrp_is_l3dgw(op)) {
-            ds_put_cstr(&ip_ds, "ip4.dst <-> ip4.src");
-            ds_put_format(match, "is_chassis_resident(%s) && ",
-                          op->cr_port->json_key);
-        } else {
-            ds_put_format(&ip_ds, "ip4.dst = ip4.src; ip4.src = %s",
-                          op->lrp_networks.ipv4_addrs[i].addr_s);
-        }
         ds_put_format(match,
                       "inport == %s && ip4 && "
                       "ip4.src == %s/%d && "
@@ -17004,23 +16994,28 @@ build_lrouter_ipv4_default_ttl_expired_flows(
                       op->json_key,
                       op->lrp_networks.ipv4_addrs[i].network_s,
                       op->lrp_networks.ipv4_addrs[i].plen);
+        if (lrp_is_l3dgw(op)) {
+            ds_put_format(match, " && is_chassis_resident(%s)",
+                          op->cr_port->json_key);
+        }
+
         ds_put_format(actions,
                       "icmp4 {"
-                      "eth.dst <-> eth.src; "
+                      "eth.dst = eth.src; eth.src = %s; "
                       "icmp4.type = 11; /* Time exceeded */ "
                       "icmp4.code = 0; /* TTL exceeded in transit */ "
-                      "%s ; ip.ttl = 254; "
+                      "ip4.dst = ip4.src; ip4.src = %s; ip.ttl = 254; "
                       "outport = %s; flags.loopback = 1; output; };",
-                      ds_cstr(&ip_ds), op->json_key);
+                      op->lrp_networks.ea_s,
+                      op->lrp_networks.ipv4_addrs[i].addr_s, op->json_key);
+
         ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 31,
                       ds_cstr(match), ds_cstr(actions), lflow_ref,
                       WITH_CTRL_METER(copp_meter_get(COPP_ICMP4_ERR,
                                                      op->od->nbr->copp,
                                                      meter_groups)),
                       WITH_HINT(&op->nbrp->header_));
-
     }
-    ds_destroy(&ip_ds);
     ds_clear(match);
     ds_clear(actions);
 
@@ -17031,15 +17026,19 @@ build_lrouter_ipv4_default_ttl_expired_flows(
                   "inport == %s && ip4 && "
                   "ip.ttl == {0, 1} && !ip.later_frag",
                   op->json_key);
+    if (lrp_is_l3dgw(op)) {
+        ds_put_format(match, " && is_chassis_resident(%s)",
+                      op->cr_port->json_key);
+    }
     ds_put_format(actions,
                   "icmp4 {"
-                  "eth.dst <-> eth.src; "
+                  "eth.dst = eth.src; eth.src = %s; "
                   "icmp4.type = 11; /* Time exceeded */ "
                   "icmp4.code = 0; /* TTL exceeded in transit */ "
                   "ip4.dst = ip4.src; ip4.src = %s; ip.ttl = 254; "
-                   "outport = %s; flags.loopback = 1; output; };",
-                   op->lrp_networks.ipv4_addrs[0].addr_s,
-                   op->json_key);
+                  "outport = %s; flags.loopback = 1; output; };",
+                  op->lrp_networks.ea_s, op->lrp_networks.ipv4_addrs[0].addr_s,
+                  op->json_key);
     ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 30,
                   ds_cstr(match), ds_cstr(actions), lflow_ref,
                   WITH_CTRL_METER(copp_meter_get(COPP_ICMP4_ERR,
@@ -17062,19 +17061,9 @@ build_lrouter_ipv6_default_ttl_expired_flows(
         return;
     }
 
-    struct ds ip_ds = DS_EMPTY_INITIALIZER;
     for (size_t i = 0; i < op->lrp_networks.n_ipv6_addrs - 1; i++) {
         ds_clear(match);
         ds_clear(actions);
-        ds_clear(&ip_ds);
-        if (lrp_is_l3dgw(op)) {
-            ds_put_cstr(&ip_ds, "ip6.dst <-> ip6.src");
-            ds_put_format(match, "is_chassis_resident(%s) && ",
-                          op->cr_port->json_key);
-        } else {
-            ds_put_format(&ip_ds, "ip6.dst = ip6.src; ip6.src = %s",
-                          op->lrp_networks.ipv6_addrs[i].addr_s);
-        }
         ds_put_format(match,
                       "inport == %s && ip6 && "
                       "ip6.src == %s/%d && "
@@ -17082,14 +17071,20 @@ build_lrouter_ipv6_default_ttl_expired_flows(
                       op->json_key,
                       op->lrp_networks.ipv6_addrs[i].network_s,
                       op->lrp_networks.ipv6_addrs[i].plen);
+        if (lrp_is_l3dgw(op)) {
+            ds_put_format(match, " && is_chassis_resident(%s)",
+                          op->cr_port->json_key);
+        }
         ds_put_format(actions,
                       "icmp6 {"
-                      "eth.dst <-> eth.src; "
-                      "%s ; ip.ttl = 254; "
+                      "eth.dst = eth.src; eth.src = %s; "
+                      "ip6.dst = ip6.src; ip6.src = %s; ip.ttl = 254; "
                       "icmp6.type = 3; /* Time exceeded */ "
                       "icmp6.code = 0; /* TTL exceeded in transit */ "
                       "outport = %s; flags.loopback = 1; output; };",
-                      ds_cstr(&ip_ds), op->json_key);
+                      op->lrp_networks.ea_s,
+                      op->lrp_networks.ipv6_addrs[i].addr_s, op->json_key);
+
         ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 31,
                       ds_cstr(match), ds_cstr(actions), lflow_ref,
                       WITH_CTRL_METER(copp_meter_get(COPP_ICMP6_ERR,
@@ -17097,7 +17092,6 @@ build_lrouter_ipv6_default_ttl_expired_flows(
                                                      meter_groups)),
                       WITH_HINT(&op->nbrp->header_));
     }
-    ds_destroy(&ip_ds);
     ds_clear(match);
     ds_clear(actions);
 
@@ -17108,14 +17102,18 @@ build_lrouter_ipv6_default_ttl_expired_flows(
                   "inport == %s && ip6 && "
                   "ip.ttl == {0, 1} && !ip.later_frag",
                   op->json_key);
+    if (lrp_is_l3dgw(op)) {
+        ds_put_format(match, " && is_chassis_resident(%s)",
+                      op->cr_port->json_key);
+    }
     ds_put_format(actions,
                   "icmp6 {"
-                  "eth.dst <-> eth.src; "
+                  "eth.dst = eth.src; eth.src = %s; "
                   "ip6.dst = ip6.src; ip6.src = %s; "
                   "ip.ttl = 254; icmp6.type = 3; /* Time exceeded */ "
                   "icmp6.code = 0; /* TTL exceeded in transit */ "
                   "outport = %s; flags.loopback = 1; output; };",
-                  op->lrp_networks.ipv6_addrs[0].addr_s,
+                  op->lrp_networks.ea_s, op->lrp_networks.ipv6_addrs[0].addr_s,
                   op->json_key);
     ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 30,
                   ds_cstr(match), ds_cstr(actions), lflow_ref,
diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
index 2973d1561..013d3034a 100644
--- a/tests/ovn-northd.at
+++ b/tests/ovn-northd.at
@@ -8543,14 +8543,14 @@ AT_CHECK([grep -e 'lr_in_ip_input    ' lrflows | grep 
-e 'igmp' -e 'mld' -e 'ip.
   table=??(lr_in_ip_input     ), priority=120  , match=((mldv1 || mldv2) && 
ip.ttl == 1), action=(next;)
   table=??(lr_in_ip_input     ), priority=120  , match=(igmp && ip.ttl == 1), 
action=(next;)
   table=??(lr_in_ip_input     ), priority=29   , match=(ip.ttl == {0, 1}), 
action=(drop;)
-  table=??(lr_in_ip_input     ), priority=30   , match=(inport == "lrp1" && 
ip4 && ip.ttl == {0, 1} && !ip.later_frag), action=(icmp4 {eth.dst <-> eth.src; 
icmp4.type = 11; /* Time exceeded */ icmp4.code = 0; /* TTL exceeded in transit 
*/ ip4.dst = ip4.src; ip4.src = 10.10.10.1; ip.ttl = 254; outport = "lrp1"; 
flags.loopback = 1; output; };)
-  table=??(lr_in_ip_input     ), priority=30   , match=(inport == "lrp1" && 
ip6 && ip.ttl == {0, 1} && !ip.later_frag), action=(icmp6 {eth.dst <-> eth.src; 
ip6.dst = ip6.src; ip6.src = 1010::1; ip.ttl = 254; icmp6.type = 3; /* Time 
exceeded */ icmp6.code = 0; /* TTL exceeded in transit */ outport = "lrp1"; 
flags.loopback = 1; output; };)
-  table=??(lr_in_ip_input     ), priority=30   , match=(inport == "lrp2" && 
ip4 && ip.ttl == {0, 1} && !ip.later_frag), action=(icmp4 {eth.dst <-> eth.src; 
icmp4.type = 11; /* Time exceeded */ icmp4.code = 0; /* TTL exceeded in transit 
*/ ip4.dst = ip4.src; ip4.src = 20.20.20.1; ip.ttl = 254; outport = "lrp2"; 
flags.loopback = 1; output; };)
-  table=??(lr_in_ip_input     ), priority=30   , match=(inport == "lrp2" && 
ip6 && ip.ttl == {0, 1} && !ip.later_frag), action=(icmp6 {eth.dst <-> eth.src; 
ip6.dst = ip6.src; ip6.src = 2020::1; ip.ttl = 254; icmp6.type = 3; /* Time 
exceeded */ icmp6.code = 0; /* TTL exceeded in transit */ outport = "lrp2"; 
flags.loopback = 1; output; };)
-  table=??(lr_in_ip_input     ), priority=31   , match=(inport == "lrp1" && 
ip4 && ip4.src == 10.10.10.0/24 && ip.ttl == {0, 1} && !ip.later_frag), 
action=(icmp4 {eth.dst <-> eth.src; icmp4.type = 11; /* Time exceeded */ 
icmp4.code = 0; /* TTL exceeded in transit */ ip4.dst = ip4.src; ip4.src = 
10.10.10.1 ; ip.ttl = 254; outport = "lrp1"; flags.loopback = 1; output; };)
-  table=??(lr_in_ip_input     ), priority=31   , match=(inport == "lrp1" && 
ip6 && ip6.src == 1010::/64 && ip.ttl == {0, 1} && !ip.later_frag), 
action=(icmp6 {eth.dst <-> eth.src; ip6.dst = ip6.src; ip6.src = 1010::1 ; 
ip.ttl = 254; icmp6.type = 3; /* Time exceeded */ icmp6.code = 0; /* TTL 
exceeded in transit */ outport = "lrp1"; flags.loopback = 1; output; };)
-  table=??(lr_in_ip_input     ), priority=31   , match=(inport == "lrp2" && 
ip4 && ip4.src == 20.20.20.0/24 && ip.ttl == {0, 1} && !ip.later_frag), 
action=(icmp4 {eth.dst <-> eth.src; icmp4.type = 11; /* Time exceeded */ 
icmp4.code = 0; /* TTL exceeded in transit */ ip4.dst = ip4.src; ip4.src = 
20.20.20.1 ; ip.ttl = 254; outport = "lrp2"; flags.loopback = 1; output; };)
-  table=??(lr_in_ip_input     ), priority=31   , match=(inport == "lrp2" && 
ip6 && ip6.src == 2020::/64 && ip.ttl == {0, 1} && !ip.later_frag), 
action=(icmp6 {eth.dst <-> eth.src; ip6.dst = ip6.src; ip6.src = 2020::1 ; 
ip.ttl = 254; icmp6.type = 3; /* Time exceeded */ icmp6.code = 0; /* TTL 
exceeded in transit */ outport = "lrp2"; flags.loopback = 1; output; };)
+  table=??(lr_in_ip_input     ), priority=30   , match=(inport == "lrp1" && 
ip4 && ip.ttl == {0, 1} && !ip.later_frag), action=(icmp4 {eth.dst = eth.src; 
eth.src = 00:00:00:00:00:01; icmp4.type = 11; /* Time exceeded */ icmp4.code = 
0; /* TTL exceeded in transit */ ip4.dst = ip4.src; ip4.src = 10.10.10.1; 
ip.ttl = 254; outport = "lrp1"; flags.loopback = 1; output; };)
+  table=??(lr_in_ip_input     ), priority=30   , match=(inport == "lrp1" && 
ip6 && ip.ttl == {0, 1} && !ip.later_frag), action=(icmp6 {eth.dst = eth.src; 
eth.src = 00:00:00:00:00:01; ip6.dst = ip6.src; ip6.src = 1010::1; ip.ttl = 
254; icmp6.type = 3; /* Time exceeded */ icmp6.code = 0; /* TTL exceeded in 
transit */ outport = "lrp1"; flags.loopback = 1; output; };)
+  table=??(lr_in_ip_input     ), priority=30   , match=(inport == "lrp2" && 
ip4 && ip.ttl == {0, 1} && !ip.later_frag), action=(icmp4 {eth.dst = eth.src; 
eth.src = 00:00:00:00:00:02; icmp4.type = 11; /* Time exceeded */ icmp4.code = 
0; /* TTL exceeded in transit */ ip4.dst = ip4.src; ip4.src = 20.20.20.1; 
ip.ttl = 254; outport = "lrp2"; flags.loopback = 1; output; };)
+  table=??(lr_in_ip_input     ), priority=30   , match=(inport == "lrp2" && 
ip6 && ip.ttl == {0, 1} && !ip.later_frag), action=(icmp6 {eth.dst = eth.src; 
eth.src = 00:00:00:00:00:02; ip6.dst = ip6.src; ip6.src = 2020::1; ip.ttl = 
254; icmp6.type = 3; /* Time exceeded */ icmp6.code = 0; /* TTL exceeded in 
transit */ outport = "lrp2"; flags.loopback = 1; output; };)
+  table=??(lr_in_ip_input     ), priority=31   , match=(inport == "lrp1" && 
ip4 && ip4.src == 10.10.10.0/24 && ip.ttl == {0, 1} && !ip.later_frag), 
action=(icmp4 {eth.dst = eth.src; eth.src = 00:00:00:00:00:01; icmp4.type = 11; 
/* Time exceeded */ icmp4.code = 0; /* TTL exceeded in transit */ ip4.dst = 
ip4.src; ip4.src = 10.10.10.1; ip.ttl = 254; outport = "lrp1"; flags.loopback = 
1; output; };)
+  table=??(lr_in_ip_input     ), priority=31   , match=(inport == "lrp1" && 
ip6 && ip6.src == 1010::/64 && ip.ttl == {0, 1} && !ip.later_frag), 
action=(icmp6 {eth.dst = eth.src; eth.src = 00:00:00:00:00:01; ip6.dst = 
ip6.src; ip6.src = 1010::1; ip.ttl = 254; icmp6.type = 3; /* Time exceeded */ 
icmp6.code = 0; /* TTL exceeded in transit */ outport = "lrp1"; flags.loopback 
= 1; output; };)
+  table=??(lr_in_ip_input     ), priority=31   , match=(inport == "lrp2" && 
ip4 && ip4.src == 20.20.20.0/24 && ip.ttl == {0, 1} && !ip.later_frag), 
action=(icmp4 {eth.dst = eth.src; eth.src = 00:00:00:00:00:02; icmp4.type = 11; 
/* Time exceeded */ icmp4.code = 0; /* TTL exceeded in transit */ ip4.dst = 
ip4.src; ip4.src = 20.20.20.1; ip.ttl = 254; outport = "lrp2"; flags.loopback = 
1; output; };)
+  table=??(lr_in_ip_input     ), priority=31   , match=(inport == "lrp2" && 
ip6 && ip6.src == 2020::/64 && ip.ttl == {0, 1} && !ip.later_frag), 
action=(icmp6 {eth.dst = eth.src; eth.src = 00:00:00:00:00:02; ip6.dst = 
ip6.src; ip6.src = 2020::1; ip.ttl = 254; icmp6.type = 3; /* Time exceeded */ 
icmp6.code = 0; /* TTL exceeded in transit */ outport = "lrp2"; flags.loopback 
= 1; output; };)
   table=??(lr_in_ip_input     ), priority=32   , match=(ip.ttl == {0, 1} && 
!ip.later_frag && (ip4.mcast || ip6.mcast)), action=(drop;)
 ])
 
@@ -8570,6 +8570,73 @@ OVN_CLEANUP_NORTHD
 AT_CLEANUP
 ])
 
+OVN_FOR_EACH_NORTHD_NO_HV([
+AT_SETUP([TTL exceeded -- l3dgw port uses correct source IP])
+AT_KEYWORDS([ttl-exceeded l3dgw])
+ovn_start
+
+check ovn-nbctl lr-add lr0
+check ovn-nbctl ls-add sw0
+check ovn-nbctl ls-add public
+
+check ovn-nbctl lrp-add lr0 lr0-sw0 00:00:00:00:ff:01 10.0.0.1/24 
2001:db8::1/64
+check ovn-nbctl lsp-add-router-port sw0 sw0-lr0 lr0-sw0
+
+check ovn-nbctl lrp-add lr0 lr0-public 00:00:00:00:ff:02 172.168.0.10/24 
2001:db8:1::1/64
+check ovn-nbctl lsp-add-router-port public public-lr0 lr0-public
+
+check ovn-sbctl chassis-add gw1 geneve 127.0.0.1
+check ovn-nbctl lrp-set-gateway-chassis lr0-public gw1
+
+check ovn-nbctl --wait=sb sync
+
+ovn-sbctl dump-flows lr0 > lr0flows
+AT_CAPTURE_FILE([lr0flows])
+
+dnl The priority-31 TTL expired flow for the l3dgw port must use explicit
+dnl ip4.src assignment (the router port IP), not ip4.dst <-> ip4.src swap.
+AT_CHECK([grep 'priority=31.*lr0-public.*ip4.*ip.ttl' lr0flows | 
ovn_strip_lflows], [0], [dnl
+  table=??(lr_in_ip_input     ), priority=31   , match=(inport == "lr0-public" 
&& ip4 && ip4.src == 172.168.0.0/24 && ip.ttl == {0, 1} && !ip.later_frag && 
is_chassis_resident("cr-lr0-public")), action=(icmp4 {eth.dst = eth.src; 
eth.src = 00:00:00:00:ff:02; icmp4.type = 11; /* Time exceeded */ icmp4.code = 
0; /* TTL exceeded in transit */ ip4.dst = ip4.src; ip4.src = 172.168.0.10; 
ip.ttl = 254; outport = "lr0-public"; flags.loopback = 1; output; };)
+])
+
+dnl Non-l3dgw port should also use explicit assignment (unchanged behavior).
+AT_CHECK([grep 'priority=31.*lr0-sw0.*ip4.*ip.ttl' lr0flows | 
ovn_strip_lflows], [0], [dnl
+  table=??(lr_in_ip_input     ), priority=31   , match=(inport == "lr0-sw0" && 
ip4 && ip4.src == 10.0.0.0/24 && ip.ttl == {0, 1} && !ip.later_frag), 
action=(icmp4 {eth.dst = eth.src; eth.src = 00:00:00:00:ff:01; icmp4.type = 11; 
/* Time exceeded */ icmp4.code = 0; /* TTL exceeded in transit */ ip4.dst = 
ip4.src; ip4.src = 10.0.0.1; ip.ttl = 254; outport = "lr0-sw0"; flags.loopback 
= 1; output; };)
+])
+
+dnl IPv6: l3dgw port must use explicit ip6.src assignment, not swap.
+AT_CHECK([grep 'priority=31.*lr0-public.*ip6.*ip.ttl' lr0flows | 
ovn_strip_lflows], [0], [dnl
+  table=??(lr_in_ip_input     ), priority=31   , match=(inport == "lr0-public" 
&& ip6 && ip6.src == 2001:db8:1::/64 && ip.ttl == {0, 1} && !ip.later_frag && 
is_chassis_resident("cr-lr0-public")), action=(icmp6 {eth.dst = eth.src; 
eth.src = 00:00:00:00:ff:02; ip6.dst = ip6.src; ip6.src = 2001:db8:1::1; ip.ttl 
= 254; icmp6.type = 3; /* Time exceeded */ icmp6.code = 0; /* TTL exceeded in 
transit */ outport = "lr0-public"; flags.loopback = 1; output; };)
+])
+
+dnl IPv6: non-l3dgw port (unchanged behavior).
+AT_CHECK([grep 'priority=31.*lr0-sw0.*ip6.*ip.ttl' lr0flows | 
ovn_strip_lflows], [0], [dnl
+  table=??(lr_in_ip_input     ), priority=31   , match=(inport == "lr0-sw0" && 
ip6 && ip6.src == 2001:db8::/64 && ip.ttl == {0, 1} && !ip.later_frag), 
action=(icmp6 {eth.dst = eth.src; eth.src = 00:00:00:00:ff:01; ip6.dst = 
ip6.src; ip6.src = 2001:db8::1; ip.ttl = 254; icmp6.type = 3; /* Time exceeded 
*/ icmp6.code = 0; /* TTL exceeded in transit */ outport = "lr0-sw0"; 
flags.loopback = 1; output; };)
+])
+
+AT_CHECK([grep 'priority=30.*lr0-public.*ip4.*ip.ttl' lr0flows | 
ovn_strip_lflows], [0], [dnl
+  table=??(lr_in_ip_input     ), priority=30   , match=(inport == "lr0-public" 
&& ip4 && ip.ttl == {0, 1} && !ip.later_frag && 
is_chassis_resident("cr-lr0-public")), action=(icmp4 {eth.dst = eth.src; 
eth.src = 00:00:00:00:ff:02; icmp4.type = 11; /* Time exceeded */ icmp4.code = 
0; /* TTL exceeded in transit */ ip4.dst = ip4.src; ip4.src = 172.168.0.10; 
ip.ttl = 254; outport = "lr0-public"; flags.loopback = 1; output; };)
+])
+
+dnl Non-l3dgw port should also use explicit assignment (unchanged behavior).
+AT_CHECK([grep 'priority=30.*lr0-sw0.*ip4.*ip.ttl' lr0flows | 
ovn_strip_lflows], [0], [dnl
+  table=??(lr_in_ip_input     ), priority=30   , match=(inport == "lr0-sw0" && 
ip4 && ip.ttl == {0, 1} && !ip.later_frag), action=(icmp4 {eth.dst = eth.src; 
eth.src = 00:00:00:00:ff:01; icmp4.type = 11; /* Time exceeded */ icmp4.code = 
0; /* TTL exceeded in transit */ ip4.dst = ip4.src; ip4.src = 10.0.0.1; ip.ttl 
= 254; outport = "lr0-sw0"; flags.loopback = 1; output; };)
+])
+
+dnl IPv6: l3dgw port must use explicit ip6.src assignment, not swap.
+AT_CHECK([grep 'priority=30.*lr0-public.*ip6.*ip.ttl' lr0flows | 
ovn_strip_lflows], [0], [dnl
+  table=??(lr_in_ip_input     ), priority=30   , match=(inport == "lr0-public" 
&& ip6 && ip.ttl == {0, 1} && !ip.later_frag && 
is_chassis_resident("cr-lr0-public")), action=(icmp6 {eth.dst = eth.src; 
eth.src = 00:00:00:00:ff:02; ip6.dst = ip6.src; ip6.src = 2001:db8:1::1; ip.ttl 
= 254; icmp6.type = 3; /* Time exceeded */ icmp6.code = 0; /* TTL exceeded in 
transit */ outport = "lr0-public"; flags.loopback = 1; output; };)
+])
+
+dnl IPv6: non-l3dgw port (unchanged behavior).
+AT_CHECK([grep 'priority=30.*lr0-sw0.*ip6.*ip.ttl' lr0flows | 
ovn_strip_lflows], [0], [dnl
+  table=??(lr_in_ip_input     ), priority=30   , match=(inport == "lr0-sw0" && 
ip6 && ip.ttl == {0, 1} && !ip.later_frag), action=(icmp6 {eth.dst = eth.src; 
eth.src = 00:00:00:00:ff:01; ip6.dst = ip6.src; ip6.src = 2001:db8::1; ip.ttl = 
254; icmp6.type = 3; /* Time exceeded */ icmp6.code = 0; /* TTL exceeded in 
transit */ outport = "lr0-sw0"; flags.loopback = 1; output; };)
+])
+
+OVN_CLEANUP_NORTHD
+AT_CLEANUP
+])
+
 OVN_FOR_EACH_NORTHD_NO_HV([
 AT_SETUP([IP multicast flood without IGMP relay])
 AT_KEYWORDS([mcast_relay])
@@ -14569,15 +14636,15 @@ AT_CHECK([grep "lr_in_ip_input" lr0flows | 
ovn_strip_lflows], [0], [dnl
   table=??(lr_in_ip_input     ), priority=100  , match=(ip4.src_mcast 
||ip4.src == 255.255.255.255 || ip4.src == 127.0.0.0/8 || ip4.dst == 
127.0.0.0/8 || ip4.src == 0.0.0.0/8 || ip4.dst == 0.0.0.0/8), action=(drop;)
   table=??(lr_in_ip_input     ), priority=120  , match=(inport == "lr0-public" 
&& ip4.src == 172.168.0.100), action=(next;)
   table=??(lr_in_ip_input     ), priority=29   , match=(ip.ttl == {0, 1}), 
action=(drop;)
-  table=??(lr_in_ip_input     ), priority=30   , match=(inport == "lr0-public" 
&& ip4 && ip.ttl == {0, 1} && !ip.later_frag), action=(icmp4 {eth.dst <-> 
eth.src; icmp4.type = 11; /* Time exceeded */ icmp4.code = 0; /* TTL exceeded 
in transit */ ip4.dst = ip4.src; ip4.src = 172.168.0.10; ip.ttl = 254; outport 
= "lr0-public"; flags.loopback = 1; output; };)
-  table=??(lr_in_ip_input     ), priority=30   , match=(inport == "lr0-public" 
&& ip6 && ip.ttl == {0, 1} && !ip.later_frag), action=(icmp6 {eth.dst <-> 
eth.src; ip6.dst = ip6.src; ip6.src = fe80::200:ff:fe00:ff02; ip.ttl = 254; 
icmp6.type = 3; /* Time exceeded */ icmp6.code = 0; /* TTL exceeded in transit 
*/ outport = "lr0-public"; flags.loopback = 1; output; };)
-  table=??(lr_in_ip_input     ), priority=30   , match=(inport == "lr0-sw0" && 
ip4 && ip.ttl == {0, 1} && !ip.later_frag), action=(icmp4 {eth.dst <-> eth.src; 
icmp4.type = 11; /* Time exceeded */ icmp4.code = 0; /* TTL exceeded in transit 
*/ ip4.dst = ip4.src; ip4.src = 10.0.0.1; ip.ttl = 254; outport = "lr0-sw0"; 
flags.loopback = 1; output; };)
-  table=??(lr_in_ip_input     ), priority=30   , match=(inport == "lr0-sw0" && 
ip6 && ip.ttl == {0, 1} && !ip.later_frag), action=(icmp6 {eth.dst <-> eth.src; 
ip6.dst = ip6.src; ip6.src = fe80::200:ff:fe00:ff01; ip.ttl = 254; icmp6.type = 
3; /* Time exceeded */ icmp6.code = 0; /* TTL exceeded in transit */ outport = 
"lr0-sw0"; flags.loopback = 1; output; };)
-  table=??(lr_in_ip_input     ), priority=30   , match=(inport == "lr0-sw1" && 
ip4 && ip.ttl == {0, 1} && !ip.later_frag), action=(icmp4 {eth.dst <-> eth.src; 
icmp4.type = 11; /* Time exceeded */ icmp4.code = 0; /* TTL exceeded in transit 
*/ ip4.dst = ip4.src; ip4.src = 20.0.0.1; ip.ttl = 254; outport = "lr0-sw1"; 
flags.loopback = 1; output; };)
-  table=??(lr_in_ip_input     ), priority=30   , match=(inport == "lr0-sw1" && 
ip6 && ip.ttl == {0, 1} && !ip.later_frag), action=(icmp6 {eth.dst <-> eth.src; 
ip6.dst = ip6.src; ip6.src = fe80::200:ff:fe00:ff03; ip.ttl = 254; icmp6.type = 
3; /* Time exceeded */ icmp6.code = 0; /* TTL exceeded in transit */ outport = 
"lr0-sw1"; flags.loopback = 1; output; };)
-  table=??(lr_in_ip_input     ), priority=31   , match=(inport == "lr0-sw0" && 
ip4 && ip4.src == 10.0.0.0/24 && ip.ttl == {0, 1} && !ip.later_frag), 
action=(icmp4 {eth.dst <-> eth.src; icmp4.type = 11; /* Time exceeded */ 
icmp4.code = 0; /* TTL exceeded in transit */ ip4.dst = ip4.src; ip4.src = 
10.0.0.1 ; ip.ttl = 254; outport = "lr0-sw0"; flags.loopback = 1; output; };)
-  table=??(lr_in_ip_input     ), priority=31   , match=(inport == "lr0-sw1" && 
ip4 && ip4.src == 20.0.0.0/24 && ip.ttl == {0, 1} && !ip.later_frag), 
action=(icmp4 {eth.dst <-> eth.src; icmp4.type = 11; /* Time exceeded */ 
icmp4.code = 0; /* TTL exceeded in transit */ ip4.dst = ip4.src; ip4.src = 
20.0.0.1 ; ip.ttl = 254; outport = "lr0-sw1"; flags.loopback = 1; output; };)
-  table=??(lr_in_ip_input     ), priority=31   , 
match=(is_chassis_resident("cr-lr0-public") && inport == "lr0-public" && ip4 && 
ip4.src == 172.168.0.0/24 && ip.ttl == {0, 1} && !ip.later_frag), action=(icmp4 
{eth.dst <-> eth.src; icmp4.type = 11; /* Time exceeded */ icmp4.code = 0; /* 
TTL exceeded in transit */ ip4.dst <-> ip4.src ; ip.ttl = 254; outport = 
"lr0-public"; flags.loopback = 1; output; };)
+  table=??(lr_in_ip_input     ), priority=30   , match=(inport == "lr0-public" 
&& ip4 && ip.ttl == {0, 1} && !ip.later_frag && 
is_chassis_resident("cr-lr0-public")), action=(icmp4 {eth.dst = eth.src; 
eth.src = 00:00:00:00:ff:02; icmp4.type = 11; /* Time exceeded */ icmp4.code = 
0; /* TTL exceeded in transit */ ip4.dst = ip4.src; ip4.src = 172.168.0.10; 
ip.ttl = 254; outport = "lr0-public"; flags.loopback = 1; output; };)
+  table=??(lr_in_ip_input     ), priority=30   , match=(inport == "lr0-public" 
&& ip6 && ip.ttl == {0, 1} && !ip.later_frag && 
is_chassis_resident("cr-lr0-public")), action=(icmp6 {eth.dst = eth.src; 
eth.src = 00:00:00:00:ff:02; ip6.dst = ip6.src; ip6.src = 
fe80::200:ff:fe00:ff02; ip.ttl = 254; icmp6.type = 3; /* Time exceeded */ 
icmp6.code = 0; /* TTL exceeded in transit */ outport = "lr0-public"; 
flags.loopback = 1; output; };)
+  table=??(lr_in_ip_input     ), priority=30   , match=(inport == "lr0-sw0" && 
ip4 && ip.ttl == {0, 1} && !ip.later_frag), action=(icmp4 {eth.dst = eth.src; 
eth.src = 00:00:00:00:ff:01; icmp4.type = 11; /* Time exceeded */ icmp4.code = 
0; /* TTL exceeded in transit */ ip4.dst = ip4.src; ip4.src = 10.0.0.1; ip.ttl 
= 254; outport = "lr0-sw0"; flags.loopback = 1; output; };)
+  table=??(lr_in_ip_input     ), priority=30   , match=(inport == "lr0-sw0" && 
ip6 && ip.ttl == {0, 1} && !ip.later_frag), action=(icmp6 {eth.dst = eth.src; 
eth.src = 00:00:00:00:ff:01; ip6.dst = ip6.src; ip6.src = 
fe80::200:ff:fe00:ff01; ip.ttl = 254; icmp6.type = 3; /* Time exceeded */ 
icmp6.code = 0; /* TTL exceeded in transit */ outport = "lr0-sw0"; 
flags.loopback = 1; output; };)
+  table=??(lr_in_ip_input     ), priority=30   , match=(inport == "lr0-sw1" && 
ip4 && ip.ttl == {0, 1} && !ip.later_frag), action=(icmp4 {eth.dst = eth.src; 
eth.src = 00:00:00:00:ff:03; icmp4.type = 11; /* Time exceeded */ icmp4.code = 
0; /* TTL exceeded in transit */ ip4.dst = ip4.src; ip4.src = 20.0.0.1; ip.ttl 
= 254; outport = "lr0-sw1"; flags.loopback = 1; output; };)
+  table=??(lr_in_ip_input     ), priority=30   , match=(inport == "lr0-sw1" && 
ip6 && ip.ttl == {0, 1} && !ip.later_frag), action=(icmp6 {eth.dst = eth.src; 
eth.src = 00:00:00:00:ff:03; ip6.dst = ip6.src; ip6.src = 
fe80::200:ff:fe00:ff03; ip.ttl = 254; icmp6.type = 3; /* Time exceeded */ 
icmp6.code = 0; /* TTL exceeded in transit */ outport = "lr0-sw1"; 
flags.loopback = 1; output; };)
+  table=??(lr_in_ip_input     ), priority=31   , match=(inport == "lr0-public" 
&& ip4 && ip4.src == 172.168.0.0/24 && ip.ttl == {0, 1} && !ip.later_frag && 
is_chassis_resident("cr-lr0-public")), action=(icmp4 {eth.dst = eth.src; 
eth.src = 00:00:00:00:ff:02; icmp4.type = 11; /* Time exceeded */ icmp4.code = 
0; /* TTL exceeded in transit */ ip4.dst = ip4.src; ip4.src = 172.168.0.10; 
ip.ttl = 254; outport = "lr0-public"; flags.loopback = 1; output; };)
+  table=??(lr_in_ip_input     ), priority=31   , match=(inport == "lr0-sw0" && 
ip4 && ip4.src == 10.0.0.0/24 && ip.ttl == {0, 1} && !ip.later_frag), 
action=(icmp4 {eth.dst = eth.src; eth.src = 00:00:00:00:ff:01; icmp4.type = 11; 
/* Time exceeded */ icmp4.code = 0; /* TTL exceeded in transit */ ip4.dst = 
ip4.src; ip4.src = 10.0.0.1; ip.ttl = 254; outport = "lr0-sw0"; flags.loopback 
= 1; output; };)
+  table=??(lr_in_ip_input     ), priority=31   , match=(inport == "lr0-sw1" && 
ip4 && ip4.src == 20.0.0.0/24 && ip.ttl == {0, 1} && !ip.later_frag), 
action=(icmp4 {eth.dst = eth.src; eth.src = 00:00:00:00:ff:03; icmp4.type = 11; 
/* Time exceeded */ icmp4.code = 0; /* TTL exceeded in transit */ ip4.dst = 
ip4.src; ip4.src = 20.0.0.1; ip.ttl = 254; outport = "lr0-sw1"; flags.loopback 
= 1; output; };)
   table=??(lr_in_ip_input     ), priority=32   , match=(ip.ttl == {0, 1} && 
!ip.later_frag && (ip4.mcast || ip6.mcast)), action=(drop;)
   table=??(lr_in_ip_input     ), priority=50   , match=(eth.bcast), 
action=(drop;)
   table=??(lr_in_ip_input     ), priority=60   , match=(ip4.dst == 
{10.0.0.1}), action=(drop;)
@@ -14751,15 +14818,15 @@ AT_CHECK([grep "lr_in_ip_input" lr0flows | 
ovn_strip_lflows], [0], [dnl
   table=??(lr_in_ip_input     ), priority=100  , match=(ip4.src_mcast 
||ip4.src == 255.255.255.255 || ip4.src == 127.0.0.0/8 || ip4.dst == 
127.0.0.0/8 || ip4.src == 0.0.0.0/8 || ip4.dst == 0.0.0.0/8), action=(drop;)
   table=??(lr_in_ip_input     ), priority=120  , match=(inport == "lr0-public" 
&& ip4.src == 172.168.0.100), action=(next;)
   table=??(lr_in_ip_input     ), priority=29   , match=(ip.ttl == {0, 1}), 
action=(drop;)
-  table=??(lr_in_ip_input     ), priority=30   , match=(inport == "lr0-public" 
&& ip4 && ip.ttl == {0, 1} && !ip.later_frag), action=(icmp4 {eth.dst <-> 
eth.src; icmp4.type = 11; /* Time exceeded */ icmp4.code = 0; /* TTL exceeded 
in transit */ ip4.dst = ip4.src; ip4.src = 172.168.0.10; ip.ttl = 254; outport 
= "lr0-public"; flags.loopback = 1; output; };)
-  table=??(lr_in_ip_input     ), priority=30   , match=(inport == "lr0-public" 
&& ip6 && ip.ttl == {0, 1} && !ip.later_frag), action=(icmp6 {eth.dst <-> 
eth.src; ip6.dst = ip6.src; ip6.src = fe80::200:ff:fe00:ff02; ip.ttl = 254; 
icmp6.type = 3; /* Time exceeded */ icmp6.code = 0; /* TTL exceeded in transit 
*/ outport = "lr0-public"; flags.loopback = 1; output; };)
-  table=??(lr_in_ip_input     ), priority=30   , match=(inport == "lr0-sw0" && 
ip4 && ip.ttl == {0, 1} && !ip.later_frag), action=(icmp4 {eth.dst <-> eth.src; 
icmp4.type = 11; /* Time exceeded */ icmp4.code = 0; /* TTL exceeded in transit 
*/ ip4.dst = ip4.src; ip4.src = 10.0.0.1; ip.ttl = 254; outport = "lr0-sw0"; 
flags.loopback = 1; output; };)
-  table=??(lr_in_ip_input     ), priority=30   , match=(inport == "lr0-sw0" && 
ip6 && ip.ttl == {0, 1} && !ip.later_frag), action=(icmp6 {eth.dst <-> eth.src; 
ip6.dst = ip6.src; ip6.src = fe80::200:ff:fe00:ff01; ip.ttl = 254; icmp6.type = 
3; /* Time exceeded */ icmp6.code = 0; /* TTL exceeded in transit */ outport = 
"lr0-sw0"; flags.loopback = 1; output; };)
-  table=??(lr_in_ip_input     ), priority=30   , match=(inport == "lr0-sw1" && 
ip4 && ip.ttl == {0, 1} && !ip.later_frag), action=(icmp4 {eth.dst <-> eth.src; 
icmp4.type = 11; /* Time exceeded */ icmp4.code = 0; /* TTL exceeded in transit 
*/ ip4.dst = ip4.src; ip4.src = 20.0.0.1; ip.ttl = 254; outport = "lr0-sw1"; 
flags.loopback = 1; output; };)
-  table=??(lr_in_ip_input     ), priority=30   , match=(inport == "lr0-sw1" && 
ip6 && ip.ttl == {0, 1} && !ip.later_frag), action=(icmp6 {eth.dst <-> eth.src; 
ip6.dst = ip6.src; ip6.src = fe80::200:ff:fe00:ff03; ip.ttl = 254; icmp6.type = 
3; /* Time exceeded */ icmp6.code = 0; /* TTL exceeded in transit */ outport = 
"lr0-sw1"; flags.loopback = 1; output; };)
-  table=??(lr_in_ip_input     ), priority=31   , match=(inport == "lr0-sw0" && 
ip4 && ip4.src == 10.0.0.0/24 && ip.ttl == {0, 1} && !ip.later_frag), 
action=(icmp4 {eth.dst <-> eth.src; icmp4.type = 11; /* Time exceeded */ 
icmp4.code = 0; /* TTL exceeded in transit */ ip4.dst = ip4.src; ip4.src = 
10.0.0.1 ; ip.ttl = 254; outport = "lr0-sw0"; flags.loopback = 1; output; };)
-  table=??(lr_in_ip_input     ), priority=31   , match=(inport == "lr0-sw1" && 
ip4 && ip4.src == 20.0.0.0/24 && ip.ttl == {0, 1} && !ip.later_frag), 
action=(icmp4 {eth.dst <-> eth.src; icmp4.type = 11; /* Time exceeded */ 
icmp4.code = 0; /* TTL exceeded in transit */ ip4.dst = ip4.src; ip4.src = 
20.0.0.1 ; ip.ttl = 254; outport = "lr0-sw1"; flags.loopback = 1; output; };)
-  table=??(lr_in_ip_input     ), priority=31   , 
match=(is_chassis_resident("cr-lr0-public") && inport == "lr0-public" && ip4 && 
ip4.src == 172.168.0.0/24 && ip.ttl == {0, 1} && !ip.later_frag), action=(icmp4 
{eth.dst <-> eth.src; icmp4.type = 11; /* Time exceeded */ icmp4.code = 0; /* 
TTL exceeded in transit */ ip4.dst <-> ip4.src ; ip.ttl = 254; outport = 
"lr0-public"; flags.loopback = 1; output; };)
+  table=??(lr_in_ip_input     ), priority=30   , match=(inport == "lr0-public" 
&& ip4 && ip.ttl == {0, 1} && !ip.later_frag && 
is_chassis_resident("cr-lr0-public")), action=(icmp4 {eth.dst = eth.src; 
eth.src = 00:00:00:00:ff:02; icmp4.type = 11; /* Time exceeded */ icmp4.code = 
0; /* TTL exceeded in transit */ ip4.dst = ip4.src; ip4.src = 172.168.0.10; 
ip.ttl = 254; outport = "lr0-public"; flags.loopback = 1; output; };)
+  table=??(lr_in_ip_input     ), priority=30   , match=(inport == "lr0-public" 
&& ip6 && ip.ttl == {0, 1} && !ip.later_frag && 
is_chassis_resident("cr-lr0-public")), action=(icmp6 {eth.dst = eth.src; 
eth.src = 00:00:00:00:ff:02; ip6.dst = ip6.src; ip6.src = 
fe80::200:ff:fe00:ff02; ip.ttl = 254; icmp6.type = 3; /* Time exceeded */ 
icmp6.code = 0; /* TTL exceeded in transit */ outport = "lr0-public"; 
flags.loopback = 1; output; };)
+  table=??(lr_in_ip_input     ), priority=30   , match=(inport == "lr0-sw0" && 
ip4 && ip.ttl == {0, 1} && !ip.later_frag), action=(icmp4 {eth.dst = eth.src; 
eth.src = 00:00:00:00:ff:01; icmp4.type = 11; /* Time exceeded */ icmp4.code = 
0; /* TTL exceeded in transit */ ip4.dst = ip4.src; ip4.src = 10.0.0.1; ip.ttl 
= 254; outport = "lr0-sw0"; flags.loopback = 1; output; };)
+  table=??(lr_in_ip_input     ), priority=30   , match=(inport == "lr0-sw0" && 
ip6 && ip.ttl == {0, 1} && !ip.later_frag), action=(icmp6 {eth.dst = eth.src; 
eth.src = 00:00:00:00:ff:01; ip6.dst = ip6.src; ip6.src = 
fe80::200:ff:fe00:ff01; ip.ttl = 254; icmp6.type = 3; /* Time exceeded */ 
icmp6.code = 0; /* TTL exceeded in transit */ outport = "lr0-sw0"; 
flags.loopback = 1; output; };)
+  table=??(lr_in_ip_input     ), priority=30   , match=(inport == "lr0-sw1" && 
ip4 && ip.ttl == {0, 1} && !ip.later_frag), action=(icmp4 {eth.dst = eth.src; 
eth.src = 00:00:00:00:ff:03; icmp4.type = 11; /* Time exceeded */ icmp4.code = 
0; /* TTL exceeded in transit */ ip4.dst = ip4.src; ip4.src = 20.0.0.1; ip.ttl 
= 254; outport = "lr0-sw1"; flags.loopback = 1; output; };)
+  table=??(lr_in_ip_input     ), priority=30   , match=(inport == "lr0-sw1" && 
ip6 && ip.ttl == {0, 1} && !ip.later_frag), action=(icmp6 {eth.dst = eth.src; 
eth.src = 00:00:00:00:ff:03; ip6.dst = ip6.src; ip6.src = 
fe80::200:ff:fe00:ff03; ip.ttl = 254; icmp6.type = 3; /* Time exceeded */ 
icmp6.code = 0; /* TTL exceeded in transit */ outport = "lr0-sw1"; 
flags.loopback = 1; output; };)
+  table=??(lr_in_ip_input     ), priority=31   , match=(inport == "lr0-public" 
&& ip4 && ip4.src == 172.168.0.0/24 && ip.ttl == {0, 1} && !ip.later_frag && 
is_chassis_resident("cr-lr0-public")), action=(icmp4 {eth.dst = eth.src; 
eth.src = 00:00:00:00:ff:02; icmp4.type = 11; /* Time exceeded */ icmp4.code = 
0; /* TTL exceeded in transit */ ip4.dst = ip4.src; ip4.src = 172.168.0.10; 
ip.ttl = 254; outport = "lr0-public"; flags.loopback = 1; output; };)
+  table=??(lr_in_ip_input     ), priority=31   , match=(inport == "lr0-sw0" && 
ip4 && ip4.src == 10.0.0.0/24 && ip.ttl == {0, 1} && !ip.later_frag), 
action=(icmp4 {eth.dst = eth.src; eth.src = 00:00:00:00:ff:01; icmp4.type = 11; 
/* Time exceeded */ icmp4.code = 0; /* TTL exceeded in transit */ ip4.dst = 
ip4.src; ip4.src = 10.0.0.1; ip.ttl = 254; outport = "lr0-sw0"; flags.loopback 
= 1; output; };)
+  table=??(lr_in_ip_input     ), priority=31   , match=(inport == "lr0-sw1" && 
ip4 && ip4.src == 20.0.0.0/24 && ip.ttl == {0, 1} && !ip.later_frag), 
action=(icmp4 {eth.dst = eth.src; eth.src = 00:00:00:00:ff:03; icmp4.type = 11; 
/* Time exceeded */ icmp4.code = 0; /* TTL exceeded in transit */ ip4.dst = 
ip4.src; ip4.src = 20.0.0.1; ip.ttl = 254; outport = "lr0-sw1"; flags.loopback 
= 1; output; };)
   table=??(lr_in_ip_input     ), priority=32   , match=(ip.ttl == {0, 1} && 
!ip.later_frag && (ip4.mcast || ip6.mcast)), action=(drop;)
   table=??(lr_in_ip_input     ), priority=50   , match=(eth.bcast), 
action=(drop;)
   table=??(lr_in_ip_input     ), priority=60   , match=(ip4.dst == 
{10.0.0.1}), action=(drop;)
-- 
2.54.0

_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to