The objectives are two-fold: implement IPv6 socket programming and understand its link-local addressing idiosyncrasy, and implement a tunneling service that employs both a TCP based control plane and UDP based data plane.
Read chapters 4 and 5 from Peterson & Davie (textbook).
We will re-implement the UDP ping client/server app of lab2, Problem 3, so that the IPv6 address assigned to interface eth0 on a lab machine is to used identify the source and destination IP addresses of client and server in place of IPv4.
A new feature of IPv6 compared to IPv4 on our lab machines is that every network interface is assigned a local/private IPv6 address that may be used in place of IPv4 to identify a network interface. Local IPv6 addresses, called link-local, are similar to private IPv4 addresses that are meaningful within the boundary of a private IP network.
The Ethernet interface, eth0, of a lab machine is configured with a link-local IPv6 address, hence an amber machine is not reachable from the global Internet using its IPv6 address. However, amber machines can communicate with each other by using their link-local IPv6 addresses. A complication arises because IPv6 is endowed with scope identifiers that must be correctly which is not as simple as replacing 32-bit IPv4 addresses by 128-bit IPv6 addresses.
A key role that a scope identifier associated with an IPv6 address plays is to identify the network interface of a multi-homed device. The need to specify a particular network interface --- e.g., a device may have two Ethernet interfaces, eth0 or eth1 --- exists in IPv4 which is handled by assigning a different IPv4 address to each interface. In IPv6 accommodation is provided for configuring the interfaces of a multi-homed device with the same link-local IPv6 address and distinguishing them using a separate scope identifier. That is, the scope identifier plays the role of network interface number. IPv6 socket programming, due to dependence on the scope identifier, requires correct assignment of its value through a field of the sockaddr_in6 data structure.
The four relevant fields of struct sockaddr_in6 are: sin6_family, sin6_port, sin6_addr, sin6_scope_id. sin6_family is set to AF_INET6 (or PF_INET6), sin6_port is the port number, sin6_addr the 128-bit IPv6 address, sin6_scope_id an unsigned integer. A fifth field, sin6_flowinfo, will be ignored. When re-implementing the UDP ping client/server app, the client must fill in the four fields with appropriate values before calling bind() so that the desired interface (in our case eth0) is selected to communicate IPv6 packets. The same goes for the server.
For example, suppose amber05 runs the client, udppingc, and amber06
executes the server, udppings. Running "ifconfig -a" on the two machines
shows that eth0 is configured with link-local IPv6 address
inet6 fe80::a6bb:6dff:fe44:fc43 prefixlen 64 scopeid 0x20
at amber05 and
inet6 fe80::a6bb:6dff:fe44:ddb8 prefixlen 64 scopeid 0x20
at amber06. In the case of the client machine amber05,
the colon-hexadecimal notation fe80::a6bb:6dff:fe44:fc43 is
shorthand for fe80:0000:0000:0000:a6bb:6dff:fe44:fc43, and prefixlen 64
specifies that the most significant 64 bits (i.e., prefix) of
fe80::a6bb:6dff:fe44:fc43, in CIDR notation,
fe80::a6bb:6dff:fe44:fc43/64 are used to identify an
interface using the IPv6 address. Note that the 64-bit prefixes
of amber05 and amber06 are the same.
Since the least significant 16-bits, fc43 for amber05 and ddb8 for
amber06 are different, the two interfaces belonging to two different
hosts in our lab can be distinguished. If amber05 were to possess additional
Ethernet interfaces, say, eth1 and eth2, then IPv6 allows eth1 and
eth2 to be assigned the same IPv6 address as eth0,
fe80::a6bb:6dff:fe44:fc43, but distinguished by assigning
different scope identifiers. Even though we do not utilize
this feature, IPv6 socket programming requires us to
correctly configure the socket structure where scope identifier
dependence is incorporated.
A straightforward way to configure an IPv6 socket before calling bind() is pass as second argument of inet_pton() the string "fe80::a6bb:6dff:fe44:fc43%eth0" and assign the value 0x20 (decimal 32) to the scope identifier field sin6_scope_id. The port number is assigned as before, and the address (or protocol) family is AF_INET6 (PF_INET6). Then call bind(). Before calling sendto(), specify the server's IPv6 address as fe80::a6bb:6dff:fe44:ddb8 and leave the scope identifier of the destination unspecified. The same holds at the server when it configures the socket structure to bind to interface eth0 by calling bind().
Replace IPv4 addresses in the command-line arguments of udppingc and udppings by their IPv6 counterparts in colon-hexadecimal notation that is processed as a string. In your implementation, assume that the ping client will run on amber05 and the server on amber06. Hardcode the colon-hexadecimal IPv6 link-local addresses appended by "%eth0" before passing to inet_pton(). Similarly, hardcode 0x20 as the value assigned to the scope identifier. In general, the scope identifier for a specific interface may be queried by calling if_nametoindex(). If you are able to get bind() to work on any amber machine in our lab, you will receive 10 additional bonus points.
Create a subdirectory v1/ and submit your work as in lab2, Problem 3. Test to verify correctness. There is no need to repeat performance evaluation.
Tunneling is a packet forwarding technique that allows packets sent to a final destination to make a detour to one or more intermediate forwarding nodes (e.g., routers and servers). This can help obfuscate the true identity of the sender from the receiver (i.e., final destination) as well as the true identity of the final destination from third-party observers that monitor network traffic. For example, some servers in a country may only respond the clients whose source IP address belongs to devices located in the country. The same goes for organizations, including Purdue, where some services are available only from devices on campus (i.e., source IP addresses assigned to devices at Purdue). Virtual private networks (VPNs) provide a means for by-passing such restrictions by deploying a packet forwarding node with a source IP address in a given country or organization through which packets from a client are forwarded. To the final destination device a client request will appear to originate from a machine with IP address in the same country or organization. To an observer who captures packets upstream (i.e., close to the client) the destination will appear to be the forwarding node, hence hiding the true identity of the final destination.
When employing multiple forwarding nodes to which packets are bounced around like in a pinball machine before being forwarded to the final destination, identity of the source can be anonymized so that even when some forwarding nodes are compromised the sender's true identity requires effort to uncover. As with many technologies, tunneling may be used for innocuous as well as nefarious purposes.
We will build a single-hop tunneling server where a host sends traffic to the tunneling server which forwards the packets to the final destination, making it appear to the destination that the packets originate from the tunneling server. If the final destination responds, the tunneling server forwards the packets to the host. Our tunneling server is targeted at UDP-based applications such as the ping client/server of lab2, Problem 3. The tunneling server will utilize a control plane where requests to set up and tear down tunnels are mediated using TCP. The tunneling server and UDP app will use a data plane to transmit app traffic that helps hide the identities of the true source and destination.
The tunneling server, tvpns, is executed at a lab machine, say, amber05,
% tvpns 10.168.53.14 61111 abcABC
where the first argument is the server's IPv4 address,
61111 the port number on which to accept TCP client
requests, and the third argument abcABC is
a secret key (a string of 6 ASCII upper and lower case
characters). If binding fails (e.g., the specified port number is already in use)
then tvpns outputs a suitable message to stdout and terminates. The user
reruns tvpns with a different port number (assuming the IPv4 address is
correct) until bind() succeeds. tvpns follows a concurrent client/server
design where the parent process uses a STREAM socket to accept new tunnel set-up
and existing tunnel tear-down requests from the client, tvpnc.
The parent updates a data structure, struct tunneltab forwardtab[NUMSESSIONS],
where
struct tunneltab {
unsigned long destaddr;
unsigned short destpt;
unsigned long sourceaddr;
unsigned short sourcept;
unsigned short tunnelpt; }
tvpns limits active forwarding sessions to NUMSESSIONS where NUMSESSIONS is defined
to be 8 in v2/tvpns.h.
Initialize all the fields of forwardtab[] to 0. forwardtab[i].sourceaddr
(i = 0, 1, ..., NUMSESSIONS-1) equaling 0 will indicate that the i'th table entry is
free. When a valid client request to establish a new tunnel session arrives,
the fields of the first available entry forwardtab[] (assuming a free entry
exists) are filled with values: sourceaddr is updated with the source address
specified in the request, destaddr and destpt are updated with the
destination IPv4 address and port number of the request. The two fields
sourcept and tunnelpt are populated by the child process that the
parent process forks after updating forwardtab[].
Before forking a child process, the parent stores the index of
forwardtab[] for the new tunneling session in a local variable,
unsigned int sessionindex, that the child process will use to carry out
packet forwarding.
Child code. Whereas the parent process of the concurrent server is responsible for setting up new tunneling session requests, a child process is responsible for carrying out the actual forwarding and handling termination of a tunneling session. Thus a child is involved in both the control plane and data plane, with the latter being its primary responsibility. Upon having been forked, a child process creates a UDP socket and binds it to a port starting at 55500. If bind() fails because a port number is already in use, the child increments the port number and calls bind() until it succeeds. The child uses the TCP socket set up by the parent to communicate the 2-byte port number to the client by calling write(). This is preceded by the 6-byte secret key. The client will verify the secret key and use the received port number and the tunneling server's IPv4 address to transmit its data packets to the tunneling server by calling sendto().
The child process creates a second UDP socket that will be used to forward the payload received from the client. It binds to an unused port number starting at 57500 (incrementing the port number of bind() fails) which is stored in the field tunnelpt. Responses from the final destination will arrive at port tunnelpt whose payload the child will forward to the client using the first UDP socket by calling sendto(). To monitor packets arriving on the two UDP sockets, the child may register a signal handler for SIGPOLL/SIGIO which calls recvfrom() to receive a payload to forward on the other UDP socket by calling sendto(). For the purpose of packet forwarding, a more suitable method is to block on the select() system call to monitor activity on multiple file descriptors. Since the child needs to monitor the TCP socket inherited from the parent for session termination, use select() to monitor activity on the three socket descriptors. If the child receives on the TCP socket a 6-byte message containing the secret key, it closes the TCP socket, frees up the session entry in forwardtab[], and calls exit(0) to terminate.
The client side consists of two apps, tvpnc that sets up a tunneling session
by contacting the tvpns, and the UDP app that sends its data to the tunneling
server which forwards the payload to the final destination.
The tunneling client, tvpnc, is executed at a lab machine, say, amber02,
% tvpnc 10.168.53.14 61111 abcABC 10.168.53.13 10.168.53.14 56001
where the first two arguments specify the coordinates of the tunneling server
tvpns, the third argument is a secret key (same as at tvpns), the
fourth argument is the IPv4 address of the machine where a UDP client app
will run (e.g., 10.168.53.13 for amber04), and the last two arguments
specify the coordinates of the final destination (i.e., UDP server app),
in the above example, 10.168.53.14 for amber05.
If tunneling session set-up is successful, tvpnc will receive the UDP
port number of the UDP socket of the child process executing tvpns over the
TCP control plane which it prints to stdout. With the port number in
hand, we can execute the UDP client app, udppingc, on amber04
% udppingc 10.168.53.14 60007 <secret> <portnum2>
<pcount>
where the second argument, in the above example 60007,
is the port number output by tvpnc to stdout. udppingc's payload is sent
to tvpns which forwards it to the final destination at which udppings
runs. Responses sent by udppings to the child process of tvpns are returned
to udppingc. In production systems the functionality of tvpnc may be
implemented as part of the client app's operating system such that the
client app is oblivious to tunneling being performed. That is, the
first two command-line arguments of udppingc are the IPv4 address and
port number of the UDP ping server, not the tunneling server. Since
the scope of CS 536 does not include kernel-level mods, we employ an
application layer method where tunneling is not hidden from a legacy app.
Place your code along with Makefile and README in a new subdirectory v2/. Test and verify that your single-hop tunneling server implementation works correctly. By default, run the four apps --- tvpns, tvpnc, udppings, udppingc --- on different amber machines in our lab. Other combinations may be meaningful such as running tvpnc and udppingc on the same host.
Modify your implementation of Problem 2 so that IPv6 addresses are used in place of IPv4 addresses throughout and the UDP ping app from Problem 1 of lab4 is utilized. Place your code in v3/. Test and verify that the modified app works correctly.
The Bonus Problems are completely optional. They serve to provide additional exercises to understand material. Bonus problems help more readily reach the 40% contributed by lab component to the course grade.
Electronic turn-in instructions:
i) For problems that require answering/explaining questions, submit a write-up as a pdf file called lab4.pdf. Place lab4.pdf in your directory lab4/. You can use your favorite editor subject to that it is able to export pdf files which several freeware editors do. Files submitted in any other format will not be graded.
ii) We will use turnin to manage lab assignment submissions. Please check that relevant source code including Makefile are included in the relevant subdirectories of lab4. In the parent directory of lab4, run the command
turnin -c cs536 -p lab4 lab4
You can check/list the submitted files using
turnin -c cs536 -p lab4 -v