Thanks for the rebase, Alexandra!

Acked-by: Mark Michelson <[email protected]>

On Thu, Jan 15, 2026 at 4:51 PM Alexandra Rukomoinikova
<[email protected]> wrote:
>
> When a logical router port has multiple IP addresses from different networks,
> northd generates multiple TTL exceeded flows. Previously, these flows had
> identical match conditions but different actions (using different ICMP reply
> source IPs), leading to non-deterministic behavior where replies could use
> an incorrect source IP not belonging to the original packet's destination 
> network.
>
> The fix adds source IP network matching to flow, ensuring that ICMP TTL 
> exceeded
> replies always originate from an IP in the same network as the source of the 
> original packet.
>
> Additionally, the default TTL exceeded flow behavior has been unified for IPv4
> and IPv6: previously, packets that didn't match any configured subnet were
> dropped; now we trigger a reply using the first IP address configured on the
> router port.
>
> Fixes: c0321040c703 ("OVN: add ICMPv6 time exceeded support to OVN logical 
> router")
> Fixes: 7f19374c5933 ("OVN: add ICMP time exceeded support to OVN logical 
> router")
> Reported-at: https://issues.redhat.com/browse/FDP-2870
> Signed-off-by: Alexandra Rukomoinikova <[email protected]>
> ---
> v4 --> v5: rebased to use new lflow addition API: changed 
> ovn_lflow_add_with_hint__ to ovn_lflow_add
> ---
>  northd/northd.c     | 226 ++++++++++++++++++++++++++++++--------------
>  tests/ovn-northd.at |  38 +++++---
>  tests/ovn.at        |  32 ++++---
>  3 files changed, 201 insertions(+), 95 deletions(-)
>
> diff --git a/northd/northd.c b/northd/northd.c
> index afecbabc1..b13e24221 100644
> --- a/northd/northd.c
> +++ b/northd/northd.c
> @@ -15866,7 +15866,7 @@ build_misc_local_traffic_drop_flows_for_lrouter(
>                    lflow_ref);
>
>      /* TTL discard */
> -    ovn_lflow_add(lflows, od, S_ROUTER_IN_IP_INPUT, 30,
> +    ovn_lflow_add(lflows, od, S_ROUTER_IN_IP_INPUT, 29,
>                    "ip.ttl == {0, 1}", debug_drop_action(),
>                    lflow_ref);
>
> @@ -16059,6 +16059,154 @@ build_dhcp_relay_flows_for_lrouter_port(struct 
> ovn_port *op,
>      free(server_ip_str);
>  }
>
> +static void
> +build_lrouter_ipv4_default_ttl_expired_flows(
> +    struct ovn_port *op, struct lflow_table *lflows,
> +    struct ds *match, struct ds *actions,
> +    const struct shash *meter_groups,
> +    struct lflow_ref *lflow_ref)
> +{
> +    if (!op->lrp_networks.n_ipv4_addrs) {
> +        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");
> +        } 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 && "
> +                      "ip.ttl == {0, 1} && !ip.later_frag",
> +                      op->json_key,
> +                      op->lrp_networks.ipv4_addrs[i].network_s,
> +                      op->lrp_networks.ipv4_addrs[i].plen);
> +        ds_put_format(actions,
> +                      "icmp4 {"
> +                      "eth.dst <-> eth.src; "
> +                      "icmp4.type = 11; /* Time exceeded */ "
> +                      "icmp4.code = 0; /* TTL exceeded in transit */ "
> +                      "%s ; ip.ttl = 254; "
> +                      "outport = %s; flags.loopback = 1; output; };",
> +                      ds_cstr(&ip_ds), 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);
> +
> +    /* Default flow for IPv4 packets with expired TTL (0 or 1).
> +     * Generate ICMPv4 Time Exceeded reply using the first IPv4 address
> +     * of the logical router port as source address. */
> +    ds_put_format(match,
> +                  "inport == %s && ip4 && "
> +                  "ip.ttl == {0, 1} && !ip.later_frag",
> +                  op->json_key);
> +    ds_put_format(actions,
> +                  "icmp4 {"
> +                  "eth.dst <-> eth.src; "
> +                  "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);
> +    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,
> +                                                 op->od->nbr->copp,
> +                                                 meter_groups)),
> +                  WITH_HINT(&op->nbrp->header_));
> +}
> +
> +static void
> +build_lrouter_ipv6_default_ttl_expired_flows(
> +    struct ovn_port *op, struct lflow_table *lflows,
> +    struct ds *match, struct ds *actions,
> +    const struct shash *meter_groups,
> +    struct lflow_ref *lflow_ref)
> +{
> +    /* Early return if no IPv6 addresses are configured.
> +     * Note: op->lrp_networks.ipv6_addrs will always have LLA and that
> +     * will be last in the list. */
> +    if (op->lrp_networks.n_ipv6_addrs < 1) {
> +        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");
> +        } 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 && "
> +                      "ip.ttl == {0, 1} && !ip.later_frag",
> +                      op->json_key,
> +                      op->lrp_networks.ipv6_addrs[i].network_s,
> +                      op->lrp_networks.ipv6_addrs[i].plen);
> +        ds_put_format(actions,
> +                      "icmp6 {"
> +                      "eth.dst <-> eth.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);
> +        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,
> +                                                     op->od->nbr->copp,
> +                                                     meter_groups)),
> +                      WITH_HINT(&op->nbrp->header_));
> +    }
> +    ds_destroy(&ip_ds);
> +    ds_clear(match);
> +    ds_clear(actions);
> +
> +    /* Default flow for IPv6 packets with expired TTL (0 or 1).
> +     * Generate ICMPv6 Time Exceeded reply using the first IPv6 address
> +     * of the logical router port as source address. */
> +    ds_put_format(match,
> +                  "inport == %s && ip6 && "
> +                  "ip.ttl == {0, 1} && !ip.later_frag",
> +                  op->json_key);
> +    ds_put_format(actions,
> +                  "icmp6 {"
> +                  "eth.dst <-> eth.src; "
> +                  "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->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_ICMP6_ERR,
> +                                                 op->od->nbr->copp,
> +                                                 meter_groups)),
> +                  WITH_HINT(&op->nbrp->header_));
> +}
> +
>  static void
>  build_ipv6_input_flows_for_lrouter_port(
>          struct ovn_port *op, struct lflow_table *lflows,
> @@ -16185,45 +16333,9 @@ build_ipv6_input_flows_for_lrouter_port(
>      }
>
>      /* ICMPv6 time exceeded */
> -    struct ds ip_ds = DS_EMPTY_INITIALIZER;
> -    for (int i = 0; i < op->lrp_networks.n_ipv6_addrs; i++) {
> -        /* skip link-local address */
> -        if (in6_is_lla(&op->lrp_networks.ipv6_addrs[i].network)) {
> -            continue;
> -        }
> -
> -        ds_clear(match);
> -        ds_clear(actions);
> -        ds_clear(&ip_ds);
> -        if (lrp_is_l3dgw(op)) {
> -            ds_put_cstr(&ip_ds, "ip6.dst <-> ip6.src");
> -        } 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 && "
> -                      "ip.ttl == {0, 1} && !ip.later_frag",
> -                      op->json_key,
> -                      op->lrp_networks.ipv6_addrs[i].network_s,
> -                      op->lrp_networks.ipv6_addrs[i].plen);
> -        ds_put_format(actions,
> -                      "icmp6 {"
> -                      "eth.dst <-> eth.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);
> -        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,
> -                                                     op->od->nbr->copp,
> -                                                     meter_groups)),
> -                      WITH_HINT(&op->nbrp->header_));
> -    }
> -    ds_destroy(&ip_ds);
> +    build_lrouter_ipv6_default_ttl_expired_flows(op, lflows,
> +                                                 match, actions,
> +                                                 meter_groups, lflow_ref);
>  }
>
>  static void
> @@ -16331,37 +16443,9 @@ build_lrouter_ipv4_ip_input(struct ovn_port *op,
>      build_lrouter_bfd_flows(lflows, op, meter_groups, bfd_ports, lflow_ref);
>
>      /* ICMP time exceeded */
> -    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");
> -        } 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 && "
> -                      "ip.ttl == {0, 1} && !ip.later_frag", op->json_key);
> -        ds_put_format(actions,
> -                      "icmp4 {"
> -                      "eth.dst <-> eth.src; "
> -                      "icmp4.type = 11; /* Time exceeded */ "
> -                      "icmp4.code = 0; /* TTL exceeded in transit */ "
> -                      "%s ; ip.ttl = 254; "
> -                      "outport = %s; flags.loopback = 1; output; };",
> -                      ds_cstr(&ip_ds), 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);
> +    build_lrouter_ipv4_default_ttl_expired_flows(op, lflows,
> +                                                 match, actions,
> +                                                 meter_groups, lflow_ref);
>
>      /* ARP reply.  These flows reply to ARP requests for the router's own
>       * IP address. */
> diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
> index e3df8c9fc..53fef9c61 100644
> --- a/tests/ovn-northd.at
> +++ b/tests/ovn-northd.at
> @@ -8185,10 +8185,14 @@ dnl Flows to skip TTL == {0, 1} check for IGMP and 
> MLD packets.
>  AT_CHECK([grep -e 'lr_in_ip_input    ' lrflows | grep -e 'igmp' -e 'mld' -e 
> 'ip.ttl == {0, 1}' | ovn_strip_lflows], [0], [dnl
>    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=30   , match=(ip.ttl == {0, 1}), 
> action=(drop;)
> -  table=??(lr_in_ip_input     ), priority=31   , 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=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 && 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" && 
> 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=32   , match=(ip.ttl == {0, 1} && 
> !ip.later_frag && (ip4.mcast || ip6.mcast)), action=(drop;)
>  ])
> @@ -13925,10 +13929,16 @@ AT_CHECK([grep "lr_in_ip_input" lr0flows | 
> ovn_strip_lflows], [0], [dnl
>    table=??(lr_in_ip_input     ), priority=100  , match=(ip4.src == 
> {20.0.0.1, 20.0.0.255} && reg9[[0]] == 0), action=(drop;)
>    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=30   , match=(ip.ttl == {0, 1}), 
> action=(drop;)
> -  table=??(lr_in_ip_input     ), priority=31   , 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 ; ip.ttl = 254; outport = 
> "lr0-public"; flags.loopback = 1; output; };)
> -  table=??(lr_in_ip_input     ), priority=31   , 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=31   , 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=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-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=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=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;)
> @@ -14102,10 +14112,16 @@ AT_CHECK([grep "lr_in_ip_input" lr0flows | 
> ovn_strip_lflows], [0], [dnl
>    table=??(lr_in_ip_input     ), priority=100  , match=(ip4.src == 
> {20.0.0.1, 20.0.0.255} && reg9[[0]] == 0), action=(drop;)
>    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=30   , match=(ip.ttl == {0, 1}), 
> action=(drop;)
> -  table=??(lr_in_ip_input     ), priority=31   , 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 ; ip.ttl = 254; outport = 
> "lr0-public"; flags.loopback = 1; output; };)
> -  table=??(lr_in_ip_input     ), priority=31   , 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=31   , 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=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-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=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=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;)
> diff --git a/tests/ovn.at b/tests/ovn.at
> index 445a74ce5..c74a1aad1 100644
> --- a/tests/ovn.at
> +++ b/tests/ovn.at
> @@ -20503,7 +20503,7 @@ AT_SETUP([TTL exceeded])
>  AT_KEYWORDS([ttl-exceeded])
>  ovn_start
>
> -# test_ip_packet INPORT HV ETH_SRC ETH_DST IPV4_SRC IPV4_DST IPV4_ROUTER 
> IP_CHKSUM EXP_IP_CHKSUM EXP_ICMP_CHKSUM SHOULD_REPLY
> +# test_ip_packet INPORT HV ETH_SRC ETH_DST IPV4_SRC IPV4_DST IP_SRC_REPLY 
> IP_CHKSUM EXP_IP_CHKSUM EXP_ICMP_CHKSUM SHOULD_REPLY
>  #
>  # Causes a packet to be received on INPORT of the hypervisor HV. The packet 
> is an IPv4 packet with
>  # ETH_SRC, ETH_DST, IPV4_SRC, IPV4_DST, IP_CHKSUM as specified and TTL set 
> to 1.
> @@ -20513,10 +20513,10 @@ ovn_start
>  # INPORT is a lport number, e.g. 11 for vif11.
>  # HV is a hypervisor number
>  # ETH_SRC and ETH_DST are each 12 hex digits.
> -# IPV4_SRC, IPV4_DST and IPV4_ROUTER are each 8 hex digits.
> +# IPV4_SRC, IPV4_DST and IP_SRC_REPLY are each 8 hex digits.
>  # IP_CHKSUM, EXP_IP_CHSUM and EXP_ICMP_CHKSUM are each 4 hex digits
>  test_ip_packet() {
> -    local inport=$1 hv=$2 eth_src=$3 eth_dst=$4 ipv4_src=$5 ipv4_dst=$6 
> ip_router=$7 ip_chksum=$8
> +    local inport=$1 hv=$2 eth_src=$3 eth_dst=$4 ipv4_src=$5 ipv4_dst=$6 
> ip_src_reply=$7 ip_chksum=$8
>      local exp_ip_chksum=$9 exp_icmp_chksum=${10}
>      shift 10
>      local should_reply=$1
> @@ -20529,28 +20529,28 @@ test_ip_packet() {
>      local icmp_data=00000000
>      local 
> reply_icmp_payload=${icmp_type_code_response}${exp_icmp_chksum}${icmp_data}
>      if test $should_reply == yes; then
> -        local 
> reply=${eth_src}${eth_dst}08004500003000004000${reply_icmp_ttl}01${exp_ip_chksum}${ip_router}${ipv4_src}${reply_icmp_payload}
> +        local 
> reply=${eth_src}${eth_dst}08004500003000004000${reply_icmp_ttl}01${exp_ip_chksum}${ip_src_reply}${ipv4_src}${reply_icmp_payload}
>          echo $reply$orig_pkt_in_reply >> vif$inport.expected
>      fi
>
>      as hv$hv ovs-appctl netdev-dummy/receive vif$inport $packet
>  }
>
> -# test_ip6_packet INPORT HV ETH_SRC ETH_DST IPV6_SRC IPV6_DST IPV6_ROUTER 
> EXP_ICMP_CHKSUM SHOULD_REPLY
> +# test_ip6_packet INPORT HV ETH_SRC ETH_DST IPV6_SRC IPV6_DST IPV6_SRC_REPLY 
> EXP_ICMP_CHKSUM SHOULD_REPLY
>  #
>  # Causes a packet to be received on INPORT of the hypervisor HV. The packet 
> is an IPv6
>  # packet with ETH_SRC, ETH_DST, IPV6_SRC and IPV6_DST as specified.
>  # IPV6_ROUTER and EXP_ICMP_CHKSUM are the source IP and checksum of the 
> icmpv6 ttl exceeded
>  # packet sent by OVN logical router
>  test_ip6_packet() {
> -    local inport=$1 hv=$2 eth_src=$3 eth_dst=$4 ipv6_src=$5 ipv6_dst=$6 
> ipv6_router=$7 exp_icmp_chksum=$8 should_reply=$9
> +    local inport=$1 hv=$2 eth_src=$3 eth_dst=$4 ipv6_src=$5 ipv6_dst=$6 
> ipv6_src_reply=$7 exp_icmp_chksum=$8 should_reply=$9
>      shift 8
>
>      local ip6_hdr=6000000000151101${ipv6_src}${ipv6_dst}
>      local 
> packet=${eth_dst}${eth_src}86dd${ip6_hdr}dbb8303900155bac6b646f65206676676e6d66720a
>
>      if test $should_reply == yes; then
> -        local 
> reply=${eth_src}${eth_dst}86dd6000000000453afe${ipv6_router}${ipv6_src}0300${exp_icmp_chksum}00000000${ip6_hdr}dbb8303900155bac6b646f65206676676e6d66720a
> +        local 
> reply=${eth_src}${eth_dst}86dd6000000000453afe${ipv6_src_reply}${ipv6_src}0300${exp_icmp_chksum}00000000${ip6_hdr}dbb8303900155bac6b646f65206676676e6d66720a
>          echo $reply >> vif$inport.expected
>      fi
>
> @@ -20581,10 +20581,8 @@ done
>
>  check ovn-nbctl lr-add lr0
>  for i in 1 2; do
> -    check ovn-nbctl lrp-add lr0 lrp$i 00:00:00:00:ff:0$i 192.168.$i.254/24 
> 2001:db8:$i::1/64
> -    check ovn-nbctl -- lsp-add sw$i lrp$i-attachment \
> -              -- set Logical_Switch_Port lrp$i-attachment type=router \
> -                options:router-port=lrp$i addresses='"00:00:00:00:ff:0'$i' 
> 192.168.'$i'.254 2001:db8:'$i'::1"'
> +    check ovn-nbctl lrp-add lr0 lrp$i 00:00:00:00:ff:0$i 172.31.$i.254/24 
> 192.168.$i.254/24 2001:db8:$i::1/64 2002:db8:$i::1/64
> +    check ovn-nbctl lsp-add-router-port sw$i lrp$i-attachment lrp$i
>  done
>
>  OVN_POPULATE_ARP
> @@ -20592,20 +20590,28 @@ OVN_POPULATE_ARP
>  wait_for_ports_up
>  check ovn-nbctl --wait=hv sync
>
> +# Test packet with source IP from router's networks
>  test_ip_packet 1 1 000000000001 00000000ff01 $(ip_to_hex 192 168 1 1) 
> $(ip_to_hex 192 168 2 1) $(ip_to_hex 192 168 1 254) 0000 f87c ea96 yes
>  test_ip6_packet 1 1 000000000001 00000000ff01 
> 20010db8000100000000000000000011 20010db8000200000000000000000011 
> 20010db8000100000000000000000001 1c22 yes
>
> +test_ip_packet 1 1 000000000001 00000000ff01 $(ip_to_hex 172 31 1 5) 
> $(ip_to_hex 192 168 2 1) $(ip_to_hex 172 31 1 254) 0000 218b ff1b yes
> +test_ip6_packet 1 1 000000000001 00000000ff01 
> 20020db8000100000000000000000011 20010db8000200000000000000000011 
> 20020db8000100000000000000000001 1c1f yes
> +
> +# Test packet with source IP from external network - expect reply from first 
> router's IP
> +test_ip_packet 1 1 000000000001 00000000ff01 $(ip_to_hex 1 1 1 1) 
> $(ip_to_hex 192 168 2 1) $(ip_to_hex 172 31 1 254) 0000 ccad aa3e yes
> +test_ip6_packet 1 1 000000000001 00000000ff01 
> 20030db8000100000000000000000011 20010db8000200000000000000000011 
> 20010db8000100000000000000000001 1c1e yes
> +
>  # Should not send ICMP for multicast
>  test_ip_packet 1 1 000000000001 01005e7f0001 $(ip_to_hex 192 168 1 1) 
> $(ip_to_hex 239 255 0 1) $(ip_to_hex 192 168 1 254) 0000 000000000 no
>  test_ip6_packet 1 1 000000000001 333300000001 
> 20010db8000100000000000000000011 ff020000000000000000000000000001 
> 20010db8000100000000000000000001 0000 no
>
>  OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [vif1.expected])
>
> -# Confirm from debug log that we only see 2 packet-ins (no packet-ins for
> +# Confirm from debug log that we only see 6 packet-ins (no packet-ins for
>  # multicasts). This is necessary because not seeing ICMP messages doesn't
>  # necessarily mean the packet-in didn't happen. It is possible that packet-in
>  # is processed but the ICMP message got dropped.
> -AT_CHECK([grep -c packet-in hv1/ovn-controller.log], [0], [2
> +AT_CHECK([grep -c packet-in hv1/ovn-controller.log], [0], [6
>  ])
>
>  OVN_CLEANUP([hv1], [hv2])
> --
> 2.48.1
>
> _______________________________________________
> dev mailing list
> [email protected]
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
>

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

Reply via email to