123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233 |
- namespace AstrogateTunnel {
- private static Socket socket;
- private static int tun_fd;
- private static string ifname;
- private static InetSocketAddress server_address;
- private static InetSocketAddress client_address;
- private static InetAddress vpn_network;
- private static InetAddress vpn_netmask;
- private static InetAddress vpn_ip;
- private static int mtu = 1500;
- public static int main(string[] args) {
- int client_port = 0;
- int server_port = 0;
- if(args.length < 6 || !int.try_parse(args[3], out client_port) || !int.try_parse(args[5], out server_port)) {
- print(@"USAGE: $(args[0]) interface client_ip client_port server_ip server_port\n");
- return -1;
- }
- try {
- server_address = new InetSocketAddress.from_string(args[4], server_port);
- client_address = new InetSocketAddress.from_string(args[2], client_port);
- ifname = args[1];
- socket = new Socket(server_address.family, SocketType.DATAGRAM, SocketProtocol.UDP);
- socket.bind(client_address, false);
- // Open tun device
- tun_fd = handle_posix_error("opening tun device", Posix.open("/dev/net/tun", Posix.O_RDWR));
- // Create tunnel device
- var request = generate_ifreq();
- handle_posix_error("creating tun interface", Linux.ioctl(tun_fd, IfTun.Tun.SETIFF, &request));
- // Register with server
- var registration_message = new uint8[] { 'R' };
- socket.connect(server_address);
- socket.send(registration_message);
- print(@"Waiting for $(server_address)...\n");
- var ack_message = new uint8[1024];
- socket.receive(ack_message);
- if(ack_message[0] != 'A') {
- print("Received invalid reply from server. Exiting.\n");
- return -2;
- }
- var mtu_dat = new uint8[2];
- if(ack_message[1] == '4') {
- vpn_network = new InetAddress.from_bytes(ack_message[2:6], SocketFamily.IPV4);
- vpn_netmask = new InetAddress.from_bytes(ack_message[6:10], SocketFamily.IPV4);
- vpn_ip = new InetAddress.from_bytes(ack_message[10:14], SocketFamily.IPV4);
- mtu_dat = ack_message[14:16];
- }
- else if(ack_message[1] == '6') {
- vpn_network = new InetAddress.from_bytes(ack_message[2:18], SocketFamily.IPV6);
- vpn_netmask = new InetAddress.from_bytes(ack_message[18:34], SocketFamily.IPV6);
- vpn_ip = new InetAddress.from_bytes(ack_message[34:50], SocketFamily.IPV6);
- mtu_dat = ack_message[50:52];
- }
- else {
- print("Received unsupported network type from server. Exiting.\n");
- return -3;
- }
- uint16 mtu_val = 0;
- Memory.copy(&mtu_val, mtu_dat, sizeof(uint16));
- mtu = mtu_val.to_big_endian();
- print(@"Registered with Astrogate server at $server_address:\n");
- print(@" Network: $vpn_network\n");
- print(@" Subnet mask: $vpn_netmask\n");
- print(@" Allocated IP: $vpn_ip\n");
- print(@" MTU: $mtu\n\n");
-
- request.ifr_flags = 0;
- request.ifr_mtu = mtu;
- handle_posix_error(@"setting MTU for $ifname", Linux.ioctl(socket.fd, Linux.Network.SIOCSIFMTU, &request));
- request.ifr_flags |= Linux.Network.IfFlag.UP;
- handle_posix_error(@"bringing $ifname up", Linux.ioctl(socket.fd, Linux.Network.SIOCSIFFLAGS, &request));
-
- address_to_posix(vpn_ip, &request.ifr_addr);
- handle_posix_error(@"setting IP address for $ifname", Linux.ioctl(socket.fd, Linux.Network.SIOCSIFADDR, &request));
- address_to_posix(vpn_netmask, &request.ifr_addr);
- handle_posix_error(@"setting subnet mask for $ifname", Linux.ioctl(socket.fd, Linux.Network.SIOCSIFNETMASK, &request));
- address_to_posix(vpn_network, &request.ifr_addr);
- handle_posix_error(@"setting destination network for $ifname", Linux.ioctl(socket.fd, Linux.Network.SIOCSIFDSTADDR, &request));
- }
- catch(Error e) {
- print(@"Fatal error: $(e.message)\n");
- return -999;
- }
- new Thread<int>("udp listener", listen_udp);
- return listen_tun();
- }
- private int listen_udp() {
- while(true) {
- try {
- // Receive the next datagram
- var buffer = new uint8[mtu+5];
- var size = socket.receive(buffer);
- buffer.length = (int)size;
- if(buffer[0] == 'L') {
- link_ip(buffer);
- continue;
- }
- else if(buffer[0] == 'U') {
- unlink_ip(buffer);
- continue;
- }
- else if(buffer[0] != 'D') {
- print("Ignoring invalid datagram from server\n");
- continue;
- }
- handle_posix_error(@"piping datagram to $ifname", (int)Posix.write(tun_fd, buffer[1:buffer.length], buffer.length));
- }
- catch(Error e) {
- print(@"Exception handling incoming tunnel server datagram: $(e.message)\n");
- }
- }
- }
- private int listen_tun() {
- while(true) {
- var buffer = new uint8[mtu+4];
- var size = Posix.read(tun_fd, buffer, buffer.length);
- if(size < 0) {
- print(@"Got $(size) from Posix.read.\n");
- continue;
- }
- buffer.length = (int)size;
- try {
- var message = new uint8[buffer.length + 1];
- message[0] = 'D';
- Memory.copy(message[1:], buffer, buffer.length);
- socket.send(message);
- }
- catch (Error e) {
- print(@"Error forwarding datagram to server: $(e.message)\n");
- }
- }
- }
- private void link_ip(uint8[] buffer) throws Error {
- var address = new InetAddress.from_bytes(buffer[1:buffer.length], vpn_ip.family);
- print(@"Linking IP $(address)\n");
- var route = build_rtentry(address);
- var res = Linux.ioctl(socket.fd, Linux.Network.SIOCADDRT, &route);
- if(res < 0 && errno != Posix.EEXIST) {
- handle_posix_error(@"adding route to $address", res);
- }
- else if(errno == Posix.EEXIST) {
- print(@"Already linked to $(address)\n");
- }
- }
- private void unlink_ip(uint8[] buffer) throws Error {
- var address = new InetAddress.from_bytes(buffer[1:buffer.length], vpn_ip.family);
- print(@"Uninking IP $(address)\n");
- var route = build_rtentry(address);
- handle_posix_error(@"adding route to $address", Linux.ioctl(socket.fd, Linux.Network.SIOCDELRT, &route));
- }
- private Linux.Network.RtEntry build_rtentry(InetAddress address) {
- var route = Linux.Network.RtEntry();
- route.rt_dev = ifname;
- address_to_posix(address, &route.rt_dst);
- address_to_posix(new InetAddress.from_string("255.255.255.255"), &route.rt_genmask);
- route.rt_flags = Linux.Network.RtFlag.UP;
- return route;
- }
- private int handle_posix_error(string task, int result) throws Error {
- if(result < 0) {
- var code = Posix.errno;
- var message = Posix.strerror(code);
- throw new Error.literal(Quark.from_string("posix-error"), code, @"Got result $(result) while $(task): $message");
- }
- return result;
- }
- private void address_to_posix(InetAddress address, Posix.SockAddr *addr) {
- var addr_str = address.to_string();
- switch (address.family) {
- case SocketFamily.IPV4:
- var sin4 = Posix.SockAddrIn();
- sin4.sin_family = Posix.AF_INET;
- Posix.inet_pton(Posix.AF_INET, addr_str, &sin4.sin_addr.s_addr);
- Memory.copy(addr, &sin4, sizeof(Posix.SockAddr));
- break;
- case SocketFamily.IPV6:
- var sin6 = Posix.SockAddrIn();
- sin6.sin_family = Posix.AF_INET;
- Posix.inet_pton(Posix.AF_INET, addr_str, &sin6.sin_addr.s_addr);
- Memory.copy(addr, &sin6, sizeof(Posix.SockAddr));
- break;
- default:
- assert_not_reached();
- }
- }
- private Linux.Network.IfReq generate_ifreq() throws Error {
- var request = Linux.Network.IfReq();
- request.ifr_flags = IfTun.IfFlag.TUN;
- if(ifname.length > request.ifr_name.length) {
- error(@"Max length for interface name is $(request.ifr_name.length) characters, provided name is $(ifname.length) characters.");
- }
- for(int i = 0; i < ifname.length; i++){
- request.ifr_name[i] = ifname[i];
- }
- return request;
- }
- }
|