Bidirectionnal UDP tunnel (in TCP) as a temporary work-around

Why IPv6

I started conferencing about IPv6 in 1995 (at that time it used to be called IPng, for IP Next Generation, like in Star Trek). I set up my first IPv6 link in 1999 or so. Then I lost interest for the subject, the progresses made were, in my opinion, too slow. Things have changed lately for various reasons (emerging countries and eastern countries not getting their share of IPv4 space, thus switching to IPv6; 6Bone getting a reality, for example).

How to connect to the IPv6 world

Basically two ways

The IPv6 on IPv4 tunnel way

To be able to make a SIXXS IPv6-on-IPv4 tunnel, we need to be able to establish a IPv6 in IPv4 UDP datagram exchange:

However, in our case, we have a few more problems to care about, because the client will be in an OpenVZ virtual machine, with private IP address 192.168.99.105, NATed. No problem, because the above tunnelling protocol does support NAT/PAT traversal (aka it doesn't hide tunnel end-point IP addresses or port numbers in the payload!):

Note that we did not specify the IPv6 addresses that the Client and the Server must possess above. From the Internet (IPv4) point of view, the IPv6 over IPv4 tunnel traffic is simply a set of UDP IPv4 datagrams. Only the Client and Server interpret the payload.

Additional problem: POP unreachable

Unfortunately, 194.1.163.40 is not reachable from either of my providers (Cablecom (net2000) CATV, Sunrise ADSL). As a temporary measure, I can create another tunnel from 80.83.54.2 to a green host where I have shell access, so that the actual data exchange is done from there: but the datagram payload is exchanged through a TCP tunnel (a TCP tunnel is easier for firewall reasons; however of course it violates the UDP quality of service: we don't care in this application)

This will only work if 194.1.163.40 does NOT verify the from UDP address is the same as the AYIYA login, and/or this address is not specified in the packet payload. It seems to be the case, so it works.

Of course, without changing the software on the client, you need to have a few DNAT/SNAT tricks on the NAT system, so to make think the client that the real server is talking directly, where in reality it is the entry point into the TCP tunnel.

Oh, and what I did hide in the above is that Relay has a private address: it connects the server through another NAT.

Existing tunnelling software

Unfortunately, the udptunnel software available in Debian lenny doesn't support bidirectionnal phantom/fix tunnelling, because its main use is tunnelling RTP (e.g. audio telephony) conversations, which happens generally between two well-known ports. Thus, the easiest is probably to either reuse my C virtual_client (TCP) I wrote in 1996 or so, or, to be a bit more modern and actually learn something new, so do it in Perl. I chose the latter option.

My new implementation

  • A quick and dirty Perl program
    • protocol is quite simple: two bytes indicates the UDP payload in bytes, network order (big endian), then comes the UDP payload raw.
    • we count on TCP to not desynchronize
    • we have special care for partial data
    • we didn't disable NAGLE

  • a few firewall rules
    # Open TCP port on server
    iptables -I INPUT -i eth1 -d 80.83.54.2 -p tcp --dport 9999 -j ACCEPT
    
    # Allow our faked source address (this won't leave the virtual machine anyway!)
    iptables -I OUTPUT -o venet0 -s 194.1.163.40 -j ACCEPT
    
    # Redirect 194.1.163.40:5072 to our server's 5073
    iptables -t nat -I PREROUTING -i venet0 -s 192.168.99.105 -d 194.1.163.40 -p udp --dport 5072 -j DNAT --to-destination 192.168.1.11:5073
    
    # Make sure the replies are rewritten correctly (correct source IP/port)
    iptables -t nat -I POSTROUTING -o venet0 -d 192.168.99.105 -s 192.168.1.11 -p udp --sport 5073 -j SNAT --to 194.1.163.40:5072
    

  • instanciation (the number 34559 is obtained at aiccu startup, it's the phantom port)
    server% /tmp/udprelay.pl 5073 192.168.99.105 34559 9999
    client% /tmp/udprelay.pl 34559 194.1.163.40 5072 80.83.54.2:9999
    

The real picture of what is happening

In short, this is what happens:

  1. we start the aiccu client on the client, it starts sending IPv6 as payload of UDPv4 frames, from its private IP 192.168.99.105 and a phantom port (dynamically allocated), to 194.1.163.40 port 5072 (this comes from a negociation that aiccu does at start, with tic.sixxs.net)
  2. our DNAT makes so the destination (194.1.163.40:5072) gets rewritten to the private side of the NAT, port 5073 -- there are reasons for this special port that I won't detail here
  3. we start the TCP relay server on NAT, listening to 80.83.54.2:9999 (TCP), and relaying private-NAT:5073 to the tunnel and back
  4. we start the TCP relay client on Relay: it TCP connects to 80.83.54.2:9999 (happens through another NAT, but that has no impact), it relays any datagram received on the TCP tunnel to 194.1.163.40:5072 and the other way round too. It uses the same port number as the client for the UDP side, even if we don't think it matters.
  5. for datagrams coming from the POP, a special SNAT rule on the NAT system makes sure the client isn't confused by the return direction having another address.

We could have done it simpler

If the whole virtual system where the client runs was IPv6-compatible, all this address rewriting could have been avoided: the IPv6-over-IPv4 tunnel would have simply be run from the real machine.

However, my goal was to run the whole IPv6 subsystem in a virtual machine, not to migrate the whole virtual system to IPv6, yet!

It works!

This gives more than 100 ms ping6 to the other end of the tunnel, but it works:

vz6:~# ping6 -c 3 2001:41e0:ff00:30::1
PING 2001:41e0:ff00:30::1(2001:41e0:ff00:30::1) 56 data bytes
64 bytes from 2001:41e0:ff00:30::1: icmp_seq=1 ttl=64 time=136 ms
64 bytes from 2001:41e0:ff00:30::1: icmp_seq=2 ttl=64 time=129 ms
64 bytes from 2001:41e0:ff00:30::1: icmp_seq=3 ttl=64 time=127 ms

--- 2001:41e0:ff00:30::1 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2011ms
rtt min/avg/max/mdev = 127.894/131.089/136.159/3.625 ms

vz6:~# traceroute6 www.switch.ch
traceroute to www.switch.ch (2001:620:0:1b::b), 30 hops max, 40 byte packets
 1  gw-49.zrh-01.ch.sixxs.net (2001:41e0:ff00:30::1)  97.959 ms  97.716 ms  97.622 ms
 2  xen-gw.zrh.sixxs.net (2001:41e0:ff00::1)  97.528 ms  97.441 ms  97.361 ms
 3  ipman.zrh.sixxs.net (2001:41e0:fe00:1::1)  97.274 ms  107.240 ms  107.643 ms
 4  ge0-3.br01.zrh254.ipv6.ip-man.net (2001:41e0:100::1)  107.983 ms  109.035 ms  109.416 ms
 5  2001:41e0:4::39 (2001:41e0:4::39)  150.873 ms  151.315 ms  151.998 ms
 6  2001:41e0::1122 (2001:41e0::1122)  152.356 ms  84.435 ms  84.791 ms
 7  switch.ipv6.cixp.ch (2001:7f8:1c:24a::22f:1)  117.851 ms  118.271 ms  118.956 ms
 8  swiLS2-10GE-1-3.switch.ch (2001:620:0:c006::2)  119.312 ms  119.675 ms  112.880 ms
 9  swiAM1-10GE-1-4.switch.ch (2001:620:0:c048::1)  112.866 ms  112.291 ms  111.807 ms
10  oreius.switch.ch (2001:620:0:1b::b)  143.820 ms  101.705 ms  101.427 ms

-- MarcSCHAEFER - 05 Jan 2010
Topic revision: r6 - 08 Feb 2024, MarcSCHAEFER
This site is powered by FoswikiCopyright © by the contributing authors. All material on this collaboration platform is the property of the contributing authors.
Ideas, requests, problems regarding Foswiki? Send feedback