From Fedora Project Wiki

< QA‎ | Networking

 
(35 intermediate revisions by 3 users not shown)
Line 26: Line 26:
Connection procedure can start after or during the name resolution phase and uses the name resolution results. The connection procedure can also be fully sequential (client attempts addresses in a given order until a successful connection is established) or using parallelization. The most prominent reason for parallel connection is to avoid waiting for a defunct protocol version by attempting an IPv4 address at the same time as an IPv6 address.
Connection procedure can start after or during the name resolution phase and uses the name resolution results. The connection procedure can also be fully sequential (client attempts addresses in a given order until a successful connection is established) or using parallelization. The most prominent reason for parallel connection is to avoid waiting for a defunct protocol version by attempting an IPv4 address at the same time as an IPv6 address.


== Test cases ==
=== IPv6 address preference ===


=== Dual-stack to dual-stack ===
Client software should generally prefer IPv6 addresses over IPv4 by default. It is possible to tweak address preference for applications using the libc resolver <code>/etc/gai.conf</code> but many applications will simply ignore it. Also IPv6 addresses in the list of recursive DNS servers should be preferred over IPv4.
 
== Checking tools ==
 
You can use ''strace'', ''tcpdump'' or other tools to check application behavior depending on your needs. While strace is great in getting information about all network communication attempts, tcpdump can show you the contents of actual communication.
 
== Test scenarios ==
 
The following test cases are meant to test the behavior of client programs. Those
can be tested manually or automatically.
 
A repository with automated tests is
[https://github.com/pavlix/fedora-networking/tree/master/netns-tests/testcases available on GitHub].
The automated tests use ''netresolve'' to filter out name resolution issues found in glibc and other
name resolution libraries. Those need to be tested [[../Name resolution|separately]].
Therefore the results may be different (more positive) than your manual tests.
 
In general, the destination (server) system can always be a fully working dual-stack server with
both IPv4 and IPv6 addresses announced in DNS or <code>/etc/hosts</code>. The source (client)
system will vary across test scenarios. For practical purposes, firewalling can be done on the client
side.
 
Example <code>/etc/hosts</code>:
 
<pre>
# Notes:
#
#  * Always put IPv4 records records first so that it's clear they're
#    reordered after IPv6 internally.
#  * The localhost must be dual-stack, i.e. 127.0.0.1 and ::1. Names like
#    localhost4 and localhost6 should not be included for testing because
#    they are completely optional.
#  * The server and client FQDN must be used to better mimic Internet
#    communication and both must be announced as dual-stack unless a test
#    requires specific configuration. Address ranges for documentation and
#    testing should be used to avoid any conflicts.
#
 
127.0.0.1 localhost
::1 localhost
 
192.0.2.1 server.example.net
2001:DB8::2:1 server.example.net
 
192.0.2.2 client.example.net
2001:DB8::2:2 client.example.net
</pre>
 
How to replace system name resolution functions with those from netresolve (netcat):
 
<pre>
# When in the parent directory of our special hosts file, configure netresolve
# to use that directory instead of the standard `/etc`.
export NETRESOLVE_SYSCONFDIR=`pwd`
 
# Run an ordinary client program.
wrapresolve nc server.example.net 80
 
# Run a client program that needs privileges (and normally gets them using suid).
sudo --preserve-env wrapresolve ping server.example.net 80
</pre>
 
{{admon/tip|How to prepare test environment?|For information about how to setup the testing environment using network namespaces, please read the [[QA/Networking/Test_environment#How_to_test_network_scenarios_using_Linux_network_namespaces|documentation on the Test Environment page]].}}
 
How to setup IPv4 and IPv6 addresses on the client and server?
 
<pre>
# Based on the test case content, you may need to omit some of the commands for
# setting IPv4 or IPv6 on the interface.
 
# assumption is that you already set up the veth device with network namespaces
clientns="test-client"
clientif=$clientns
serverns="test-server"
serverif="$serverns
 
# Server IP addresses
serverip4="192.0.2.1/24"
serverip6="2001:DB8::2:1/64"
 
# Client IP addresses
clientip4="192.0.2.2/24"
clientip6="2001:DB8::2:2/64"
 
# configure the static IP addresses for server
ip netns exec $serverns ip address add $serverip4 dev $serverif
ip netns exec $serverns ip address add $serverip6 dev $serverif
 
# configure the static IP addresses for client
ip netns exec $clientns ip address add $clientip4 dev $clientif
ip netns exec $clientns ip address add $clientip6 dev $clientif
 
# activate the interfaces
ip netns exec $serverns ip link set $serverif up
ip netns exec $clientns ip link set $clientif up
</pre>
 
 
=== Loopback ===


{|
{|
|IPv4 connectivity||Global or masqueraded
!rowspan=2| Connectivity
|-
! IPv4
|IPv6 connectivity||Global
| Any (loopback address wanted)
|-
|-
|Target DNS||Dual-stack
! IPv6
| Any except disabled (loopback address required)
|}
|}


==== What is tested ====
==== What is tested ====


* Whether IPv6 is preferred over IPv4.
* Whether IPv6 loopback is preferred over IPv4 loopback.
* Whether the component is capable of working over IPv6.
* Whether the component is capable of working over IPv6.
* Whether sequential or parallel DNS resolution is in use.


==== Test flow ====
==== Steps to reproduce ====
'''TBD'''
# step
# another step


==== Expected result (sequential, IPv6 preferred) ====
# Let the client connect to a localhost server.
# Check the communcation (using strace, netstat/ss or tcpdump).


* Host requests AAAA record and receives a reply.
==== Expected result ====
* Host connects via IPv6.
* No delays, no A query.


==== Expected result (parallel, IPv6 preferred) ====
* Client connects to server using <code>::1</code> IPv6 address.


* Host requests A and AAAA records simultaneously and receives the AAAA reply.
Note: If the client also connects using <code>127.0.0.1</code>
* Host connects via IPv6.
due to parallel IPv4/IPv6 connection, it drops the connection
* No delays, A reply is ignored if received.
immediately.


==== Alternative result (parallel, first result wins, no protocol preference) ====
==== Bad result ====


* Host requests A and AAAA records simultaneously and receives the first reply.
* Client doesn't attempt IPv6 loopback connection.
* Host connects via IPv4 or IPv6.
* No delays.


=== Dual-stack to dual-stack with lost AAAA answer ===
=== Dual-stack to dual-stack ===
 
Same as above, except that the AAAA answer is lost by a broken DNS server.


{|
{|
|IPv4 connectivity||Global or masqueraded
!rowspan=2| Source connectivity
! IPv4
| Global or masqueraded
|-
|-
|IPv6 connectivity||Global
! IPv6
| Global
|-
|-
|Target DNS||Lost IPv6
!rowspan=2| Target connectivity
! IPv4
| Global
|-
! IPv6
| Global
|}
|}


==== What is tested ====
==== What is tested ====


* Wheter the component reverts to IPv4 in reasonable time when AAAA answer gets lost.
* Whether IPv6 is preferred over IPv4.
* Whether the component is capable of working over IPv6.
* Whether sequential or parallel DNS resolution is in use.
 
==== Steps to reproduce ====


==== Test flow ====
# Let the client connect to a dual-stack server.
'''TBD'''
# Check all tested properties.
# step
# another step


==== Expected result (sequential, IPv6 preferred) ====
==== Expected result (sequential, IPv6 preferred) ====


* Host requests AAAA record and gives up after a delay (e.g. 15 seconds).
* (resolver) Host requests AAAA record and receives a reply.
* Host requests A record and receives reply.
* Host connects via IPv6.
* Host connects via IPv4.
* No delays.
* (resolver) No A query.


==== Expected result (parallel, IPv6 preferred) ====
==== Expected result (parallel, IPv6 preferred) ====


* Host requests A and AAAA records simultaneously and receives the A reply.
* Host requests A and AAAA records simultaneously and receives the AAAA reply.
* Host gives up waiting for AAAA record after a short delay (e.g. 300 milliseconds).
* Host connects via IPv6.
* Host connects via IPv4.
* No delays, A reply is ignored if received.
 
==== Alternative result (parallel, first result wins) ====


* Host requests A and AAAA records simultaneously and receives the A reply.
==== Bad result ====
* Host connects via IPv4.
* No delay.


=== IPv4 to dual-stack with lost AAAA answer ===
Not connecting to IPv6 address or attempting an IPv4 connection.


Same as above, except that the host doesn't have any IPv6 address except link-local and loopback.
=== IPv6 to dual-stack ===


{|
{|
|IPv4 connectivity||Global or masqueraded
!rowspan=2| Source connectivity
! IPv4
| None
|-
|-
|IPv6 connectivity||Link-local
! IPv6
| Global
|-
|-
|Target DNS||Lost IPv6
!rowspan=2| Target connectivity
! IPv4
| Global
|-
! IPv6
| Global
|}
|}
Note: It turned out that we can also test the related characteristics
using syscall analysis from other scenarios.


==== What is tested ====
==== What is tested ====


* Wheter the component suppresses AAAA queries when lacking global IPv6 connectivity.
* Whether the component works well on IPv6 only host
 
==== Steps to reproduce ====


==== Test flow ====
# Let the client connect to a dual-stack server.
'''TBD'''
# Check all tested properties.
# step
# another step


==== Expected result ====
==== Expected result ====


* Host requests A record and receives reply.
* Host requests AAAA record and receives a reply.
* Host connects via IPv4.
* Host connects via IPv6.
* No delay, no AAAA query.
* No delays, no A query.
 
==== Bad result ====
 
Failure to connect to target via IPv6.
 
==== Bad result (only a minor issue) ====
 
Sending a redundant DNS A query.


=== Dual-stack to dual-stack with lost communication ===
=== Dual-stack to dual-stack with lost IPv6 communication ===


{|
{|
|IPv4 connectivity||Global or masqueraded
!rowspan=2| Source connectivity
! IPv4
| Global or masqueraded
|-
|-
|IPv6 connectivity||Global but packets to destination get dropped
! IPv6
| Global
|-
|-
|Target DNS||Dual-stack, DNS server accessed via IPv4
!rowspan=2| Target connectivity
! IPv4
| Global
|-
! IPv6
| Global
|-
!colspan=2| Other
| Lost client-server IPv6 communication
|}
|}


Line 147: Line 274:
* Whether the component reverts to IPv4 in reasonable time when local or remote network drops all IPv6 communication.
* Whether the component reverts to IPv4 in reasonable time when local or remote network drops all IPv6 communication.


==== Test flow ====
==== Steps to reproduce ====
'''TBD'''
 
# step
# Let the client connect to a dual-stack server with lost IPv6 communication.
# another step
# Check all tested properties including the delay.


==== Expected result (sequential, IPv6 preferred) ====
==== Expected result (sequential, IPv6 preferred) ====
Line 167: Line 294:
==== Expected result (parallel DNS, parallel TCP, IPv6 preferred) ====
==== Expected result (parallel DNS, parallel TCP, IPv6 preferred) ====


* Host requests A and AAAA records simultaneously
* Host requests A and AAAA records simultaneously.
* Host attempts IPv4 and IPv6 connections upon receiving the respective records
* Host attempts IPv4 and IPv6 connections upon receiving the respective records.
* IPv4 connection is established
* IPv4 connection is established.
* IPv6 connection is given up after a short delay (e.g. 300 milliseconds)
* IPv6 connection is given up after a short delay (e.g. 300 milliseconds).
* IPv4 connection is used
* IPv4 connection is used.
 
==== Notes ====
 
There is no common bad result. But the sequential connection is highly suboptimal and therefore is not suitable for interactive applications.
 
=== Disabled IPv6 to dual-stack ===
 
{|
!rowspan=2| Source connectivity
! IPv4
| Global or masqueraded
|-
! IPv6
| Disabled
|-
!rowspan=2| Target connectivity
! IPv4
| Global
|-
! IPv6
| Global
|-
!colspan=2| Other
| Lost client-server IPv6 communication
|}
 
Note: This requires a kernel run with the respective boot option
and cannot be fully tested otherwise. It can be partially tested
with all IP addresses removed.
 
==== What is tested ====
 
* Whether the component works on a system with IPv6 disabled in the kernel.
 
==== Steps to reproduce ====
 
# Start the system with kernel command line option <code>ipv6.disable=1</code>.
# Connect to a service.
 
==== Expected result ====
 
* Client connects to a service successfully.
 
==== Bad result ====
 
* Client fails to connect.
 
Note: You will probably see an error message coming from the <code>socket()</code> libc call.

Latest revision as of 10:14, 17 March 2016

Operations

A networking client typically connects to a server identified by connection information specified in its configuration or by the user. The general client procedure is to use name resolution service (often through the system C library) and use the result to negotiate a connection through the operating system kernel.

Name resolution phase

Client software retrieves a domain name from the user or from its configuration either as a verbatim value or by parsing a more complex configuration item like an URL or an e-mail address. Client software can often handle special values like literal IP addresses or empty host names. Most often this is the only configuration that is needed. Other name resolution input is built-in but can often also be tweaked by configuration or URL.

Name resolution typically consists of multiple actions. The most typical combination of actions is host name resolution using DNS where A and AAAA are requested separately in order to get IPv4 and IPv6 addresses. Sequential or parallel queries can be used to handle that situation.

See Name resolution for more details.

Example: Jabber (XMPP client connection)

Identifier: user@example.net

Client extracts domain name example.net from the configuration option above, resolves SRV record _xmpp-client._tcp.example.net to get the list of host names and then attempts to resolve A and AAAA records of those host names to get the list of IPv4 and IPv6 addresses to be attempted for connection.

You can use netresolve to mimic the above procedure:

netresolve --node example.net --service xmpp-client --srv --protocol tcp

Connection phase

Connection procedure can start after or during the name resolution phase and uses the name resolution results. The connection procedure can also be fully sequential (client attempts addresses in a given order until a successful connection is established) or using parallelization. The most prominent reason for parallel connection is to avoid waiting for a defunct protocol version by attempting an IPv4 address at the same time as an IPv6 address.

IPv6 address preference

Client software should generally prefer IPv6 addresses over IPv4 by default. It is possible to tweak address preference for applications using the libc resolver /etc/gai.conf but many applications will simply ignore it. Also IPv6 addresses in the list of recursive DNS servers should be preferred over IPv4.

Checking tools

You can use strace, tcpdump or other tools to check application behavior depending on your needs. While strace is great in getting information about all network communication attempts, tcpdump can show you the contents of actual communication.

Test scenarios

The following test cases are meant to test the behavior of client programs. Those can be tested manually or automatically.

A repository with automated tests is available on GitHub. The automated tests use netresolve to filter out name resolution issues found in glibc and other name resolution libraries. Those need to be tested separately. Therefore the results may be different (more positive) than your manual tests.

In general, the destination (server) system can always be a fully working dual-stack server with both IPv4 and IPv6 addresses announced in DNS or /etc/hosts. The source (client) system will vary across test scenarios. For practical purposes, firewalling can be done on the client side.

Example /etc/hosts:

# Notes:
#
#  * Always put IPv4 records records first so that it's clear they're
#    reordered after IPv6 internally.
#  * The localhost must be dual-stack, i.e. 127.0.0.1 and ::1. Names like
#    localhost4 and localhost6 should not be included for testing because
#    they are completely optional.
#  * The server and client FQDN must be used to better mimic Internet
#    communication and both must be announced as dual-stack unless a test
#    requires specific configuration. Address ranges for documentation and
#    testing should be used to avoid any conflicts.
#

127.0.0.1 localhost
::1 localhost

192.0.2.1 server.example.net
2001:DB8::2:1 server.example.net

192.0.2.2 client.example.net
2001:DB8::2:2 client.example.net

How to replace system name resolution functions with those from netresolve (netcat):

# When in the parent directory of our special hosts file, configure netresolve
# to use that directory instead of the standard `/etc`.
export NETRESOLVE_SYSCONFDIR=`pwd`

# Run an ordinary client program.
wrapresolve nc server.example.net 80

# Run a client program that needs privileges (and normally gets them using suid).
sudo --preserve-env wrapresolve ping server.example.net 80
Idea.png
How to prepare test environment?
For information about how to setup the testing environment using network namespaces, please read the documentation on the Test Environment page.

How to setup IPv4 and IPv6 addresses on the client and server?

# Based on the test case content, you may need to omit some of the commands for
# setting IPv4 or IPv6 on the interface.

# assumption is that you already set up the veth device with network namespaces
clientns="test-client"
clientif=$clientns
serverns="test-server"
serverif="$serverns

# Server IP addresses
serverip4="192.0.2.1/24"
serverip6="2001:DB8::2:1/64"

# Client IP addresses
clientip4="192.0.2.2/24"
clientip6="2001:DB8::2:2/64"

# configure the static IP addresses for server
ip netns exec $serverns ip address add $serverip4 dev $serverif
ip netns exec $serverns ip address add $serverip6 dev $serverif

# configure the static IP addresses for client
ip netns exec $clientns ip address add $clientip4 dev $clientif
ip netns exec $clientns ip address add $clientip6 dev $clientif

# activate the interfaces
ip netns exec $serverns ip link set $serverif up
ip netns exec $clientns ip link set $clientif up


Loopback

Connectivity IPv4 Any (loopback address wanted)
IPv6 Any except disabled (loopback address required)

What is tested

  • Whether IPv6 loopback is preferred over IPv4 loopback.
  • Whether the component is capable of working over IPv6.

Steps to reproduce

  1. Let the client connect to a localhost server.
  2. Check the communcation (using strace, netstat/ss or tcpdump).

Expected result

  • Client connects to server using ::1 IPv6 address.

Note: If the client also connects using 127.0.0.1 due to parallel IPv4/IPv6 connection, it drops the connection immediately.

Bad result

  • Client doesn't attempt IPv6 loopback connection.

Dual-stack to dual-stack

Source connectivity IPv4 Global or masqueraded
IPv6 Global
Target connectivity IPv4 Global
IPv6 Global

What is tested

  • Whether IPv6 is preferred over IPv4.
  • Whether the component is capable of working over IPv6.
  • Whether sequential or parallel DNS resolution is in use.

Steps to reproduce

  1. Let the client connect to a dual-stack server.
  2. Check all tested properties.

Expected result (sequential, IPv6 preferred)

  • (resolver) Host requests AAAA record and receives a reply.
  • Host connects via IPv6.
  • No delays.
  • (resolver) No A query.

Expected result (parallel, IPv6 preferred)

  • Host requests A and AAAA records simultaneously and receives the AAAA reply.
  • Host connects via IPv6.
  • No delays, A reply is ignored if received.

Bad result

Not connecting to IPv6 address or attempting an IPv4 connection.

IPv6 to dual-stack

Source connectivity IPv4 None
IPv6 Global
Target connectivity IPv4 Global
IPv6 Global

Note: It turned out that we can also test the related characteristics using syscall analysis from other scenarios.

What is tested

  • Whether the component works well on IPv6 only host

Steps to reproduce

  1. Let the client connect to a dual-stack server.
  2. Check all tested properties.

Expected result

  • Host requests AAAA record and receives a reply.
  • Host connects via IPv6.
  • No delays, no A query.

Bad result

Failure to connect to target via IPv6.

Bad result (only a minor issue)

Sending a redundant DNS A query.

Dual-stack to dual-stack with lost IPv6 communication

Source connectivity IPv4 Global or masqueraded
IPv6 Global
Target connectivity IPv4 Global
IPv6 Global
Other Lost client-server IPv6 communication

What is tested

  • Whether the component reverts to IPv4 in reasonable time when local or remote network drops all IPv6 communication.

Steps to reproduce

  1. Let the client connect to a dual-stack server with lost IPv6 communication.
  2. Check all tested properties including the delay.

Expected result (sequential, IPv6 preferred)

  • Host requests AAAA record and receives reply.
  • Host attempts connecting to IPv6 and times out.
  • Host requests A record and receives reply.
  • Host connects via IPv4

Expected result (parallel DNS, IPv6 preferred)

  • Host requests A and AAAA records simultaneously and waits for AAAA reply.
  • Host attempts connecting to IPv6 and times out, receiving A reply in the meantime.
  • Host connects via IPv4.

Expected result (parallel DNS, parallel TCP, IPv6 preferred)

  • Host requests A and AAAA records simultaneously.
  • Host attempts IPv4 and IPv6 connections upon receiving the respective records.
  • IPv4 connection is established.
  • IPv6 connection is given up after a short delay (e.g. 300 milliseconds).
  • IPv4 connection is used.

Notes

There is no common bad result. But the sequential connection is highly suboptimal and therefore is not suitable for interactive applications.

Disabled IPv6 to dual-stack

Source connectivity IPv4 Global or masqueraded
IPv6 Disabled
Target connectivity IPv4 Global
IPv6 Global
Other Lost client-server IPv6 communication

Note: This requires a kernel run with the respective boot option and cannot be fully tested otherwise. It can be partially tested with all IP addresses removed.

What is tested

  • Whether the component works on a system with IPv6 disabled in the kernel.

Steps to reproduce

  1. Start the system with kernel command line option ipv6.disable=1.
  2. Connect to a service.

Expected result

  • Client connects to a service successfully.

Bad result

  • Client fails to connect.

Note: You will probably see an error message coming from the socket() libc call.