The DTCP design would work (I think) if only there wasn't also a firewall at Imperial which stops incoming UDP packets (even if a NAT would let then in). The only other solution would be to tunnel everything in DNS packets (which do seem to work) or to find another place to develop from.
(P.S. they need to be real DNS packets - just using the port numbers isn't enough).
So, here's the next idea - Assisted TCP. The idea being to have a userland program linked to libnet and libpcap at each end (A and B) and a 3rd party (C) unfirewalled. The ends can talk both ways to C via TCP and can fabricate packets to the NAT and the local kernel. C can fabricate packets with the source address of A and B to the other side of the NATs.
Skipping the details, A & B both send SYN packets to each other (both die at the oppsite NAT) then C fabricates SYN+ACK packets from A and B to make the NATs think it's a normal outgoing connection.
That leaves out how to make the local kernel think it's a normal connection too, but I think it can be done without patching it directly.
Ingress and Egress filters might stop C from sending the SYN+ACK packets and it's more messy than doing it via UDP, but it should work. (I've already checked that A can't send a SYN+ACK thru the NAT).
I would hope, in the end, to probe each technique to pick the best that works, in the mean time it's a question of comming up with a decent toolkit.