Hi! Thanks for the comments. I added fragmentation flag to the default match to only respond to the first fragmented packet. I didn't actually find any specific info in the RFC regarding the need to respond to every fragmented packet, but it does say the following (for IPv4): If fragment zero is not available, then no time exceeded need be sent at all. So I think it would be better to keep the old behavior and only respond to the first packet.
On 13.01.2026 00:55, Mark Michelson wrote: > Hi Alexandra, thanks a bunch for the patch. > > Overall, I think this is a great improvement. We should definitely be > matching on the source address for IPv4 packets, and the way you have > rearranged the code is much better than how it used to be. > > I have one concern, which I bring up below. > > On Mon, Jan 12, 2026 at 11:24 AM 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]> >> --- >> northd/northd.c | 223 +++++++++++++++++++++++++++++--------------- >> tests/ovn-northd.at | 35 ++++--- >> tests/ovn.at | 32 ++++--- >> 3 files changed, 193 insertions(+), 97 deletions(-) >> >> diff --git a/northd/northd.c b/northd/northd.c >> index 9c3fa8735..843c28216 100644 >> --- a/northd/northd.c >> +++ b/northd/northd.c >> @@ -16030,11 +16030,6 @@ build_misc_local_traffic_drop_flows_for_lrouter( >> "(ip4.mcast || ip6.mcast)", debug_drop_action(), >> lflow_ref); >> >> - /* TTL discard */ >> - ovn_lflow_add(lflows, od, S_ROUTER_IN_IP_INPUT, 30, >> - "ip.ttl == {0, 1}", debug_drop_action(), >> - lflow_ref); >> - >> /* Pass other traffic not already handled to the next table for >> * routing. */ >> ovn_lflow_add(lflows, od, S_ROUTER_IN_IP_INPUT, 0, "1", "next;", >> @@ -16225,6 +16220,150 @@ 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_with_hint__(lflows, op->od, S_ROUTER_IN_IP_INPUT, >> + 31, ds_cstr(match), ds_cstr(actions), NULL, >> + copp_meter_get(COPP_ICMP4_ERR, op->od->nbr->copp, >> + meter_groups), >> + &op->nbrp->header_, lflow_ref); >> + >> + } >> + 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}", >> + op->json_key); > The priority-31 flow that we add a few lines above contains > "!ip.later_frag" as part of its match. My understanding is that this > is to ensure that for fragmented IP traffic, we only send the ICMP > reply to the first fragment. Later fragments do not get the ICMP > response. Before this patch, the later fragments would be dropped by > the priority-30 flow that was installed by > build_misc_local_traffic_drop_flows_for_lrouter(). With the match used > here, later fragments will now get an ICMP reply from the first > configured IP address on the router. This could be a different address > than was used to reply to the first fragment. This seems incorrect to > me. > > If the proper thing to do is *not* to respond to later fragments and > drop them, then you should do the following: > * Change this match to also include "!ip.later_frag". > * Also add another priority 30 flow that matches on "ip.later_frag" > and drops the packet if ttl is 0 or 1. > > If the proper thing to do is to respond to all fragments with the same > ICMP reply, then : > * Remove the "!ip.later_frag" from the priority-31 flow. > * Leave the rest of your submission as-is. > >> + 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_with_hint__(lflows, op->od, S_ROUTER_IN_IP_INPUT, >> + 30, ds_cstr(match), ds_cstr(actions), NULL, >> + copp_meter_get(COPP_ICMP4_ERR, >> + op->od->nbr->copp, >> + meter_groups), >> + &op->nbrp->header_, lflow_ref); >> +} >> + >> +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_with_hint__(lflows, op->od, S_ROUTER_IN_IP_INPUT, >> + 31, ds_cstr(match), ds_cstr(actions), NULL, >> + copp_meter_get(COPP_ICMP6_ERR, op->od->nbr->copp, >> + meter_groups), >> + &op->nbrp->header_, lflow_ref); >> + } >> + 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}", >> + 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_with_hint__(lflows, op->od, S_ROUTER_IN_IP_INPUT, >> + 30, ds_cstr(match), ds_cstr(actions), NULL, >> + copp_meter_get(COPP_ICMP6_ERR, >> + op->od->nbr->copp, >> + meter_groups), >> + &op->nbrp->header_, lflow_ref); >> +} >> + >> static void >> build_ipv6_input_flows_for_lrouter_port( >> struct ovn_port *op, struct lflow_table *lflows, >> @@ -16359,44 +16498,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_with_hint__(lflows, op->od, S_ROUTER_IN_IP_INPUT, >> - 31, ds_cstr(match), ds_cstr(actions), NULL, >> - copp_meter_get(COPP_ICMP6_ERR, op->od->nbr->copp, >> - meter_groups), >> - &op->nbrp->header_, lflow_ref); >> - } >> - ds_destroy(&ip_ds); >> + build_lrouter_ipv6_default_ttl_expired_flows(op, lflows, >> + match, actions, >> + meter_groups, lflow_ref); >> } >> >> static void >> @@ -16505,36 +16609,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_with_hint__(lflows, op->od, S_ROUTER_IN_IP_INPUT, >> - 31, ds_cstr(match), ds_cstr(actions), NULL, >> - copp_meter_get(COPP_ICMP4_ERR, op->od->nbr->copp, >> - meter_groups), >> - &op->nbrp->header_, lflow_ref); >> - >> - } >> - 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..2b2e1d4ad 100644 >> --- a/tests/ovn-northd.at >> +++ b/tests/ovn-northd.at >> @@ -8185,10 +8185,13 @@ 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=30 , match=(inport == "lrp1" && >> ip4 && ip.ttl == {0, 1}), 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}), 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}), 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}), 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 +13928,15 @@ 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=30 , match=(inport == >> "lr0-public" && ip4 && ip.ttl == {0, 1}), 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}), 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}), 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}), 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}), 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}), 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 +14110,15 @@ 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=30 , match=(inport == >> "lr0-public" && ip4 && ip.ttl == {0, 1}), 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}), 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}), 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}), 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}), 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}), 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 58127f0d3..a06ee04b0 100644 >> --- a/tests/ovn.at >> +++ b/tests/ovn.at >> @@ -20498,7 +20498,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. >> @@ -20508,10 +20508,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 >> @@ -20524,28 +20524,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 >> >> @@ -20576,10 +20576,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 >> @@ -20587,20 +20585,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 >> -- regards, Alexandra. _______________________________________________ dev mailing list [email protected] https://mail.openvswitch.org/mailman/listinfo/ovs-dev
