Using an IPv6-only internal network in a real-world environment
Problem: Not all client applications are ipv6-aware
Writing a kernel module that overloads the networking calls (like 'connect()'), and translates them to IPv6
This is a first (failed) attempt at this approach. The socket system calls are 'muxed' together in one call, sys_socketcall. sys_socketcall
in turn calls sys_sendto et al. When I get an IPv4 sendto() call in sys_socketcall, it's quite possible to create a 'struct sockaddr_in6' to
replace the 'struct sockaddr_in' argument of the call with. Unfortunately, sys_socket expects an argument in user space memory. Since sockaddr_in6
is bigger than sockaddr_in, I can't cheat and use the memory used by sockaddr_in. Since sys_socket and the functions it calls aren't exported,
it's not practical to re-implement them.
from this point on, this will probably be quite strictly linux-specific.
relevant links:
Inventorisation of system calls that will have to be overloaded:
- connect(int sockfd, struct sockaddr *servsaddr, int addrlen) - if the sockaddr is IPv4, translate it to an IPv6 version.
- bind(int sockfd, struct sockaddr *saddr, int addrlen) - as connect()
- write()
- send(), sendto(), sendmsg()
- read()
- recv(), recvfrom(), recvmsg()
- fcntl()?
- setsockopt()?
These probably won't have to change, but some debug-code might be handy :)
- socket(int domain, int type, int protocol) - though maybe we should 'fake' the availability of AF_INET here?
- listen(int sockfd, int backlog) - we won't do IPv4 incoming connections on IPv6-only hosts
- accept(int sockfd, struct sockaddr *peeraddr, int addrlen) - we won't do IPv4 incoming connections on IPv6-only hosts
I will first focus on a relatively simple case, being outgoing UDP traffic. If that's successful I'll attempt to implement incoming UDP traffic, and then in- and outgoing TCP traffic.
Note: IPv6-aware applications will probably run into trouble when they try to bind to both ipv4 and ipv6, since the bind to ipv4 will just bind to ipv6, too, this might yield 'address already in use'.
Step 1: Test setup
In the long term, the goal is to make a kernel module that will overload every single system call above. For testing, however, how these system calls should be implemented, I'm going to write a little library, implementing 'connectv6()' and friends.
I'm going to write a series of small proof-of-concept programs, written for ipv4, but calling 'connectv6()' instead of 'connect()',
compiling them with the connectv6()-code and trying to get it to communicate over v6 transparently.
This is work in progress (hardly started yet, actually).
Step 1.1: Sending a single UDP packet
step1-1.c (in progress, 'simple case' functionality is in place.)
Step 1.2: Receiving an UDP packet
step1-2.c (in progress, 'simple case' functionality is in place.)
Step 1.3: Initiating a TCP connection
Step 1.4: Sending a TCP stream
Step 1.5: Receiving a TCP stream
Step 2: The kernel module
A neat article on page 68 of linux magazine, the february 2001 issue, explains how to create
a linux kernel module that overloads existing system calls.
Of course, things wouldn't be interesting if we could just use that: it turns out that the socket system calls are 'special', in the way that all of them (socket, bind, connect, listen, accept, getsockname, getpeername, socketpair, send, recv, sendto, recvfrom, shutdown, setsockopt, getsockopt, sendmsg and recvmsg) are all handled by system call 102 ('socketcall'). This appears to be called 'muxing'.
- overloaded asmlinkage long sys_socketcall(int call, unsigned long *args) from net/socket.c in my
module, and redirecting it to sys_socketcallv6
- pass the call on from sys_socketcallv6 to the original sys_socketcall
- made a big 'switch' for the types of packet. 'translated' the socketv6() and sendtov6() (for udp). Unfortunately it doesn't work, see above.