On Mon, 2025-09-29 at 09:19 +0200, Ales Musil wrote:
>
>
> On Thu, Sep 25, 2025 at 6:03 PM Martin Kalcok
> <[email protected]> wrote:
> > Macro NS_CHECK_CONNECTIVITY is introduced to provide a more
> > comprehensive
> > way to check connectivity between network namespaces. It takes
> > source
> > namespace, destination namespace and destination IP as arguments,
> > and
> > it verifies that source NS can reach destination NS on the provided
> > IP.
> > Executed checks include:
> > * simple ping
> > * Exchange of multiple requests and replies over TCP
> > * Exchange of multiple requests and replies over UDP
> >
> > First usage of this macro is in
> > 'DNAT and SNAT on distributed router - N/S' tests that were
> > currently not
> > equipped to detect NAT failures after the initial connection was
> > established.
> >
> > Signed-off-by: Martin Kalcok <[email protected]>
> > ---
> >
>
>
> Hi Martin,
>
> thank you for the v3. I have some small comments down below.
Thanks for the review Ales.
>
> > tests/system-common-macros.at | 47 ++++++++++++
> > tests/system-ovn.at | 136 +++++++++---------------------
> > ----
> > 2 files changed, 81 insertions(+), 102 deletions(-)
> >
> > diff --git a/tests/system-common-macros.at b/tests/system-common-
> > macros.at
> > index 251a4c0b8..1c6cefcd9 100644
> > --- a/tests/system-common-macros.at
> > +++ b/tests/system-common-macros.at
> > @@ -614,3 +614,50 @@ m4_define([OVN_NEIGH_EQUAL],
> > m4_define([OVN_NEIGH_V6_EQUAL],
> > [OVS_WAIT_UNTIL_EQUAL([ip -6 neigh show dev $1 $2 | sed -e
> > 's|[[[[:space:]]]]*$||g' | grep $3 | sort], [$4])
> > ])
> > +
> > +# NS_CHECK_CONNECTIVITY([NS_SRC], [NS_DST], [IP_ADDR])
> > +#
> > +# Ensures that network namespace NS_SRC can reach NS_DST on
> > IP_ADDR. The check performs:
> > +# * Ping
> > +# * Exchange of multiple messages over TCP
> > +# * Exchange of multiple messages over UDP
> > +m4_define([NS_CHECK_CONNECTIVITY],
> > +[
> > + ns_src="$1"
> > + ns_dst="$2"
> > + ip="$3"
> > + # Start a simple TCP and UDP echo server that replies with
> > "ack <received_msg>".
> > + NETNS_DAEMONIZE($ns_dst, [nc -l -k -p 1235 -d 0.1 -c 'while
> > read msg; do echo "ack $msg"; done'], [nc-$ns_dst-$ip-tcp.pid])
> > + NETNS_DAEMONIZE($ns_dst, [nc -l -u -k -p 1234 -d 0.1 -c 'read
> > msg; echo "ack $msg"'], [nc-$ns_dst-$ip-udp.pid])
> >
>
>
> I think it would be slightly simpler having just
> "-e /bin/cat" instead of the whole loop WDYT?
Simplicity is the king. I agree that repeating the value back is
enough.
> We wouldn't have the "ack" prefix but that's still
> fine IMO. The "-k" is not needed, it should be a single
> connection from the clients. And last thing here,
> the most important one, newer nc require explicit
> "-6" for IPv6 otherwise it fails to bind the server.
ack, I went with splitting the macro into V4 and V6 versions for better
user experience.
>
> > +
> > + # Ensure that the destination NS can be pinged on the
> > specified IP
> > + NS_CHECK_EXEC([$ns_src], [ping -q -c 3 -i 0.3 -w 2 $ip |
> > FORMAT_PING], \
> > + [0], [dnl
> > +3 packets transmitted, 3 received, 0% packet loss, time 0ms
> > +])
> > +
> > + # Exchange multiple messages over TCP and UDP to verify
> > connectivity
> > + # Note(mkalcok): Server replies are printed to file, because
> > STDOUT is captured by the nc.
> > + truncate --size 0 ./tcp_data
> > + truncate --size 0 ./udp_data
> >
>
>
> nit: ": > ./tcp_data" would do the same trick.
ack. Though I must say that I'm no fan of these cryptic bash
expressions :D
>
> > +
> > + NETNS_DAEMONIZE($ns_src, [nc $ip 1235 -d 0.1 -c 'for i in
> > $(seq 1 3); do echo "tcp_data $i"; read msg; echo "$msg"
> > >>./tcp_data; done'], [nc_$ns_src-$ip-tcp.pid])
> > + NETNS_DAEMONIZE($ns_src, [nc -u $ip 1234 -d 0.1 -c 'for i in
> > $(seq 1 3); do echo "udp_data $i"; read msg; echo "$msg"
> > >>./udp_data; done'], [nc_$ns_src-$ip-udp.pid])
> > +
> > + OVS_WAIT_FOR_OUTPUT([cat ./tcp_data], [0], [dnl
> > +ack tcp_data 1
> > +ack tcp_data 2
> > +ack tcp_data 3
> > +])
> > + OVS_WAIT_FOR_OUTPUT([cat ./udp_data], [0], [dnl
> > +ack udp_data 1
> > +ack udp_data 2
> > +ack udp_data 3
> > +])
> > +
> > + # cleanup echo processes
> > + kill $(cat nc-$ns_dst-$ip-tcp.pid)
> > + kill $(cat nc-$ns_dst-$ip-udp.pid)
> > + kill $(cat nc_$ns_src-$ip-tcp.pid)
> > + kill $(cat nc_$ns_src-$ip-udp.pid)
> > +])
> > diff --git a/tests/system-ovn.at b/tests/system-ovn.at
> > index 8e356df6f..e6688c381 100644
> > --- a/tests/system-ovn.at
> > +++ b/tests/system-ovn.at
> > @@ -3777,46 +3777,49 @@ AT_CHECK([ovn-nbctl lr-route-add R1
> > 10.0.0.0/24 172.16.1.2])
> > check ovn-nbctl --wait=hv sync
> > OVS_WAIT_UNTIL([ovs-ofctl dump-flows br-int | grep
> > 'nat(src=172.16.1.1)'])
> >
> > -# North-South DNAT: 'alice1' pings 'foo1' using 172.16.1.3.
> > -NS_CHECK_EXEC([alice1], [ping -q -c 3 -i 0.3 -w 2 172.16.1.3 |
> > FORMAT_PING], \
> > -[0], [dnl
> > -3 packets transmitted, 3 received, 0% packet loss, time 0ms
> > -])
> > +# North-South DNAT: 'alice1' reaches 'foo1' on 172.16.1.3.
> > +NS_CHECK_CONNECTIVITY([alice1], [foo1], [172.16.1.3])
> >
> > # We verify that DNAT indeed happened via 'dump-conntrack'
> > command.
> > AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(172.16.1.3)
> > | \
> > sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
> > icmp,orig=(src=172.16.1.2,dst=172.16.1.3,id=<cleared>,type=8,code=
> > 0),reply=(src=192.168.1.2,dst=172.16.1.2,id=<cleared>,type=0,code=0
> > ),zone=<cleared>
> > +tcp,orig=(src=172.16.1.2,dst=172.16.1.3,sport=<cleared>,dport=<cle
> > ared>),reply=(src=192.168.1.2,dst=172.16.1.2,sport=<cleared>,dport=
> > <cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
> > +udp,orig=(src=172.16.1.2,dst=172.16.1.3,sport=<cleared>,dport=<cle
> > ared>),reply=(src=192.168.1.2,dst=172.16.1.2,sport=<cleared>,dport=
> > <cleared>),zone=<cleared>
> > ])
> >
> > -# South-North SNAT: 'foo2' pings 'alice1'. But 'alice1' receives
> > traffic
> > +# South-North SNAT: 'foo2' reaches 'alice1'. But 'alice1' receives
> > traffic
> > # from 172.16.1.4
> > -NS_CHECK_EXEC([foo2], [ping -q -c 3 -i 0.3 -w 2 172.16.1.2 |
> > FORMAT_PING], \
> > -[0], [dnl
> > -3 packets transmitted, 3 received, 0% packet loss, time 0ms
> > -])
> > +NS_CHECK_CONNECTIVITY([foo2], [alice1], 172.16.1.2)
> >
> > # We verify that SNAT indeed happened via 'dump-conntrack'
> > command.
> > AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(172.16.1.4)
> > | \
> > sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
> > icmp,orig=(src=192.168.1.3,dst=172.16.1.2,id=<cleared>,type=8,code
> > =0),reply=(src=172.16.1.2,dst=172.16.1.4,id=<cleared>,type=0,code=0
> > ),zone=<cleared>
> > +tcp,orig=(src=192.168.1.3,dst=172.16.1.2,sport=<cleared>,dport=<cl
> > eared>),reply=(src=172.16.1.2,dst=172.16.1.4,sport=<cleared>,dport=
> > <cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
> > +udp,orig=(src=192.168.1.3,dst=172.16.1.2,sport=<cleared>,dport=<cl
> > eared>),reply=(src=172.16.1.2,dst=172.16.1.4,sport=<cleared>,dport=
> > <cleared>),zone=<cleared>
> > ])
> >
> > AT_CHECK([ovs-appctl dpctl/flush-conntrack])
> >
> > -# South-North SNAT: 'bar1' pings 'alice1'. But 'alice1' receives
> > traffic
> > +# South-North SNAT: 'bar1' reaches 'alice1'. But 'alice1' receives
> > traffic
> > # from 172.16.1.1
> > -NS_CHECK_EXEC([bar1], [ping -q -c 3 -i 0.3 -w 2 172.16.1.2 |
> > FORMAT_PING], \
> > -[0], [dnl
> > -3 packets transmitted, 3 received, 0% packet loss, time 0ms
> > -])
> > +NS_CHECK_CONNECTIVITY([bar1], [alice1], 172.16.1.2)
> >
> > # We verify that SNAT indeed happened via 'dump-conntrack'
> > command.
> > AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(172.16.1.1)
> > | \
> > sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
> > icmp,orig=(src=192.168.2.2,dst=172.16.1.2,id=<cleared>,type=8,code
> > =0),reply=(src=172.16.1.2,dst=172.16.1.1,id=<cleared>,type=0,code=0
> > ),zone=<cleared>
> > +tcp,orig=(src=192.168.2.2,dst=172.16.1.2,sport=<cleared>,dport=<cl
> > eared>),reply=(src=172.16.1.2,dst=172.16.1.1,sport=<cleared>,dport=
> > <cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
> > +udp,orig=(src=192.168.2.2,dst=172.16.1.2,sport=<cleared>,dport=<cl
> > eared>),reply=(src=172.16.1.2,dst=172.16.1.1,sport=<cleared>,dport=
> > <cleared>),zone=<cleared>
> > ])
> >
> > +# North-South Direct (Bypassing DNAT): 'alice1' reaches 'foo1'
> > using 192.168.1.2
> > +NS_CHECK_CONNECTIVITY(alice1, foo1, 192.168.1.2)
> >
>
>
> nit: Missing "[]" around arguments.
ack.
>
> > +
> > +# North-South Direct (Bypassing SNAT): 'alice1' reaches 'bar1'
> > using 192.168.2.2
> > +NS_CHECK_CONNECTIVITY(alice1, bar1, 192.168.2.2)
> >
>
>
> nit: Same here.
ack.
Thanks again for the review.
Martin.
>
> > +
> > # Try to ping external network
> > NETNS_START_TCPDUMP([ext-net], [-n -c 3 -i ext-veth dst 172.16.1.3
> > and icmp], [ext-net])
> > AT_CHECK([ovn-nbctl lr-nat-del R1 snat])
> > @@ -3825,43 +3828,6 @@ NS_CHECK_EXEC([foo1], [ping -q -c 3 -i 0.3 -
> > w 2 10.0.0.1 | FORMAT_PING], \
> > 3 packets transmitted, 3 received, 0% packet loss, time 0ms
> > ])
> >
> > -# test_connectivity_from_ext takes parameters 'vm' and 'ip'. It
> > tests
> > -# icmp, udp and tcp connectivity from external network to the 'vm'
> > on
> > -# the specified 'ip'.
> > -test_connectivity_from_ext() {
> > - local vm=$1; shift
> > - local ip=$1; shift
> > -
> > - # Start listening daemon TCP connections.
> > - NETNS_DAEMONIZE($vm, [nc -l -k 1235], [nc-$vm-$ip-tcp.pid])
> > -
> > - # Ensure that vm can be pinged on the specified IP
> > - NS_CHECK_EXEC([alice1], [ping -q -c 3 -i 0.3 -w 2 $ip |
> > FORMAT_PING], \
> > - [0], [dnl
> > -3 packets transmitted, 3 received, 0% packet loss, time 0ms
> > -])
> > -
> > - # Perform two consecutive UDP connections to the specified IP
> > - NETNS_DAEMONIZE($vm, [nc -l -u 1234], [nc-$vm-$ip-udp.pid])
> > - NS_CHECK_EXEC([alice1], [nc -u $ip 1234 -p 2000 -z])
> > - kill $(cat nc-$vm-$ip-udp.pid)
> > -
> > - NETNS_DAEMONIZE($vm, [nc -l -u 1234], [nc-$vm-$ip-udp.pid])
> > - NS_CHECK_EXEC([alice1], [nc -u $ip 1234 -p 2000 -z])
> > - kill $(cat nc-$vm-$ip-udp.pid)
> > -
> > - # Send data over TCP connection to the specified IP
> > - NS_CHECK_EXEC([alice1], [echo "TCP test" | nc --send-only $ip
> > 1235])
> > -}
> > -
> > -# Test access from external network to the internal IP of a VM
> > that
> > -# has also configured DNAT
> > -test_connectivity_from_ext foo1 192.168.1.2
> > -
> > -# Test access from external network to the internal IP of a VM
> > that
> > -# does not have DNAT
> > -test_connectivity_from_ext bar1 192.168.2.2
> > -
> > OVS_WAIT_UNTIL([
> > total_pkts=$(cat ext-net.tcpdump | wc -l)
> > test "${total_pkts}" = "3"
> > @@ -3980,26 +3946,22 @@ AT_CHECK([ovn-nbctl lr-nat-add R1 snat
> > fd20::1 ::/0])
> > check ovn-nbctl --wait=hv sync
> > OVS_WAIT_UNTIL([ovs-ofctl dump-flows br-int | grep
> > 'nat(src=fd20::1)'])
> >
> > -# North-South DNAT: 'alice1' pings 'foo1' using fd20::3
> > -NS_CHECK_EXEC([alice1], [ping6 -q -c 3 -i 0.3 -w 2 fd20::3 |
> > FORMAT_PING], \
> > -[0], [dnl
> > -3 packets transmitted, 3 received, 0% packet loss, time 0ms
> > -])
> > +# North-South DNAT: 'alice1' reaches 'foo1' using fd20::3
> > +NS_CHECK_CONNECTIVITY([alice1], [foo1], [fd20::3])
> >
> > # We verify that DNAT indeed happened via 'dump-conntrack'
> > command.
> > AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd20::3) | \
> > sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
> > icmpv6,orig=(src=fd20::2,dst=fd20::3,id=<cleared>,type=128,code=0)
> > ,reply=(src=fd11::2,dst=fd20::2,id=<cleared>,type=129,code=0),zone=
> > <cleared>
> > +tcp,orig=(src=fd20::2,dst=fd20::3,sport=<cleared>,dport=<cleared>)
> > ,reply=(src=fd11::2,dst=fd20::2,sport=<cleared>,dport=<cleared>),zo
> > ne=<cleared>,protoinfo=(state=<cleared>)
> > +udp,orig=(src=fd20::2,dst=fd20::3,sport=<cleared>,dport=<cleared>)
> > ,reply=(src=fd11::2,dst=fd20::2,sport=<cleared>,dport=<cleared>),zo
> > ne=<cleared>
> > ])
> >
> > AT_CHECK([ovs-appctl dpctl/flush-conntrack])
> >
> > -# South-North SNAT: 'foo2' pings 'alice1'. But 'alice1' receives
> > traffic
> > +# South-North SNAT: 'foo2' reaches 'alice1'. But 'alice1' receives
> > traffic
> > # from fd20::4
> > -NS_CHECK_EXEC([foo2], [ping6 -q -c 3 -i 0.3 -w 2 fd20::2 |
> > FORMAT_PING], \
> > -[0], [dnl
> > -3 packets transmitted, 3 received, 0% packet loss, time 0ms
> > -])
> > +NS_CHECK_CONNECTIVITY([foo2], [alice1], fd20::2)
> >
> > ovs-appctl dpctl/dump-conntrack | grep icmpv6
> > AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd11::3) | \
> > @@ -4010,59 +3972,29 @@ sed -e 's/zone=[[0-9]]*/zone=<cleared>/'],
> > [0], [dnl
> > AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd20::4) | \
> > sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
> > icmpv6,orig=(src=fd11::3,dst=fd20::2,id=<cleared>,type=128,code=0)
> > ,reply=(src=fd20::2,dst=fd20::4,id=<cleared>,type=129,code=0),zone=
> > <cleared>
> > +tcp,orig=(src=fd11::3,dst=fd20::2,sport=<cleared>,dport=<cleared>)
> > ,reply=(src=fd20::2,dst=fd20::4,sport=<cleared>,dport=<cleared>),zo
> > ne=<cleared>,protoinfo=(state=<cleared>)
> > +udp,orig=(src=fd11::3,dst=fd20::2,sport=<cleared>,dport=<cleared>)
> > ,reply=(src=fd20::2,dst=fd20::4,sport=<cleared>,dport=<cleared>),zo
> > ne=<cleared>
> > ])
> >
> > AT_CHECK([ovs-appctl dpctl/flush-conntrack])
> >
> > -# South-North SNAT: 'bar1' pings 'alice1'. But 'alice1' receives
> > traffic
> > +# South-North SNAT: 'bar1' reaches 'alice1'. But 'alice1' receives
> > traffic
> > # from fd20::1
> > -NS_CHECK_EXEC([bar1], [ping6 -q -c 3 -i 0.3 -w 2 fd20::2 |
> > FORMAT_PING], \
> > -[0], [dnl
> > -3 packets transmitted, 3 received, 0% packet loss, time 0ms
> > -])
> > +NS_CHECK_CONNECTIVITY([bar1], [alice1], fd20::2)
> >
> > # We verify that SNAT indeed happened via 'dump-conntrack'
> > command.
> > AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd20::1) | \
> > sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
> > icmpv6,orig=(src=fd12::2,dst=fd20::2,id=<cleared>,type=128,code=0)
> > ,reply=(src=fd20::2,dst=fd20::1,id=<cleared>,type=129,code=0),zone=
> > <cleared>
> > +tcp,orig=(src=fd12::2,dst=fd20::2,sport=<cleared>,dport=<cleared>)
> > ,reply=(src=fd20::2,dst=fd20::1,sport=<cleared>,dport=<cleared>),zo
> > ne=<cleared>,protoinfo=(state=<cleared>)
> > +udp,orig=(src=fd12::2,dst=fd20::2,sport=<cleared>,dport=<cleared>)
> > ,reply=(src=fd20::2,dst=fd20::1,sport=<cleared>,dport=<cleared>),zo
> > ne=<cleared>
> > ])
> >
> > -# test_connectivity_from_ext takes parameters 'vm' and 'ip'. It
> > tests
> > -# icmp, udp and tcp connectivity from external network to the 'vm'
> > on
> > -# the specified 'ip'.
> > -test_connectivity_from_ext() {
> > - local vm=$1; shift
> > - local ip=$1; shift
> > -
> > - # Start listening daemon for TCP connections.
> > - NETNS_DAEMONIZE($vm, [nc -6 -l -k 1235], [nc-$vm-$ip-tcp.pid])
> > -
> > - # Ensure that vm can be pinged on the specified IP
> > - NS_CHECK_EXEC([alice1], [ping -q -c 3 -i 0.3 -w 2 $ip |
> > FORMAT_PING], \
> > - [0], [dnl
> > -3 packets transmitted, 3 received, 0% packet loss, time 0ms
> > -])
> > -
> > - # Perform two consecutive UDP connections to the specified IP
> > - NETNS_DAEMONIZE($vm, [nc -6 -l -u 1234], [nc-$vm-$ip-udp.pid])
> > - NS_CHECK_EXEC([alice1], [nc -u $ip 1234 -p 2000 -z])
> > - kill $(cat nc-$vm-$ip-udp.pid)
> > -
> > - NETNS_DAEMONIZE($vm, [nc -6 -l -u 1234], [nc-$vm-$ip-udp.pid])
> > - NS_CHECK_EXEC([alice1], [nc -u $ip 1234 -p 2000 -z])
> > - kill $(cat nc-$vm-$ip-udp.pid)
> > -
> > - # Send data over TCP connection to the specified IP
> > - NS_CHECK_EXEC([alice1], [echo "TCP test" | nc --send-only $ip
> > 1235])
> > -}
> > -
> > -# Test access from external network to the internal IP of a VM
> > that
> > -# has also configured DNAT
> > -test_connectivity_from_ext foo1 fd11::2
> > +# North-South Direct (Bypassing DNAT): 'alice1' reaches 'foo1'
> > using fd11::2
> > +NS_CHECK_CONNECTIVITY([alice1], [foo1], [fd11::2])
> >
> > -# Test access from external network to the internal IP of a VM
> > that
> > -# does not have DNAT
> > -test_connectivity_from_ext bar1 fd12::2
> > +# North-South Direct (Bypassing SNAT): 'alice1' reaches 'bar1'
> > using fd12::2
> > +NS_CHECK_CONNECTIVITY([alice1], [bar1], [fd12::2])
> >
> > OVN_CLEANUP_CONTROLLER([hv1])
> >
_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev