Chapter 6: Network Address Translation with Ox

In this exercise, we will build a Network Address Translator (NAT) by first writing and testing a translator function that first translates IP addresses and then extending it so that it translates port numbers as well.

Exercise 1: The Network Address Translating Function

Solution

The explosive growth of the Internet in the early 1990s created a demand for IP addresses, which are limited to 32 bits. NAT allows private IP addresses to be reused in multiple networks by multiplexing several private addresses onto a single public address. More specifically, a NAT device works as follows:

Topology

You will work with the following network topology:

images

It has two private hosts and a public host connected to a single switch. This topology can be easily created using Mininet:

$ sudo mn --controller=remote --topo=single,3 --mac --arp

Programming Task

Use the template below to get started. Save it in a file called Nat1.ml and place it in the directory ~/ox-tutorial-solutions/Nat1.ml. In our case, we will use 10.0.0.99 as the public IP, and 00:00:00:00:00:99 as the public MAC address.

(* ~/ox-tutorial-solutions/Nat1.ml *)

open Frenetic_Ox
open Frenetic_OpenFlow0x01
open Frenetic_Packet

module MyApplication = struct
  include DefaultHandlers
  open Platform

  let mappings = Hashtbl.create 50

  let publicIP = ip_of_string "10.0.0.99"
  let publicMAC = mac_of_string "00:00:00:00:00:99"

  let privateIP1 = ip_of_string "10.0.0.1"

  let privateIP2 = ip_of_string "10.0.0.2"

  let switch_connected (sw:switchId) feats : unit =
     Printf.printf "Switch Connected %Ld\n%!" sw

  let packet_in (sw: switchId) (xid : xid) (pktIn : packetIn) : unit =
    let pk = parse_payload pktIn.input_payload in
      (* If the packet is of type TCP and came in through port 1 or 2 *)
      if (pktIn.port = 1 || pktIn.port = 2)
        && dlTyp pk = 0x800
        && nwProto pk = 0x06
      then
	(* [FILL] Add packet info into hashtable and install rules to
           forward packet out of correct port *)
        ...
      else (* For packets arriving on port 3 *)
	try (* If a mapping is found in the hashtable *)
          Printf.printf "Non TCP or incoming flow %s \n" (packetIn_to_string pktIn);
          (* [FILL] Install reverse rules to forward packet back to correct host *)
          ...
        with Not_found ->
	  (* [FILL] If no mapping is found in hashtable then drop the packet *)
          ...
end

let _ =
  let module C = Make (MyApplication) in
  C.start ();

Building and Testing

To ensure that TCP packets are being sent and received to the correct hosts and addresses are translated correctly, perform the following steps.

Exercise 2: Port Translatation

Solution

The scheme we have implemented so far works as long as the private hosts never initiate simultaneous flows with the same TCP source port. To relax this assumption, we can use the controller to rewrite the TCP source ports to unique values and prevent conflicts.

Programming Task

Copy Nat1.ml to Nat2.ml, and change the program to translate port numbers as well.

More specifically,

Building and Testing

Compile and test the controller the same way as before.