/* * Copyright (C) 2011 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <arpa/inet.h> #include <netinet/in.h> #include <sys/ioctl.h> #include <sys/socket.h> #include <sys/stat.h> #include <sys/types.h> #include <errno.h> #include <fcntl.h> #ifdef __linux__ // There are several ways to play with this program. Here we just give an // example for the simplest scenario. Let us say that a Linux box has a // public IPv4 address on eth0. Please try the following steps and adjust // the parameters when necessary. // // # Enable IP forwarding // echo 1 > /proc/sys/net/ipv4/ip_forward // // # Pick a range of private addresses and perform NAT over eth0. // iptables -t nat -A POSTROUTING -s 10.0.0.0/8 -o eth0 -j MASQUERADE // // # Create a TUN interface. // ip tuntap add dev tun0 mode tun // // # Set the addresses and bring up the interface. // ifconfig tun0 10.0.0.1 dstaddr 10.0.0.2 up // // # Create a server on port 8000 with shared secret "test". // ./ToyVpnServer tun0 8000 test -m 1400 -a 10.0.0.2 32 -d 8.8.8.8 -r 0.0.0.0 0 // // This program only handles a session at a time. To allow multiple sessions, // multiple servers can be created on the same port, but each of them requires // its own TUN interface. A short shell script will be sufficient. Since this // program is designed for demonstration purpose, it performs neither strong // authentication nor encryption. DO NOT USE IT IN PRODUCTION! #include <net/if.h> #include <linux/if_tun.h> static int get_interface(char *name) { int interface = open("/dev/net/tun", O_RDWR | O_NONBLOCK); ifreq ifr; memset(&ifr, 0, sizeof(ifr)); ifr.ifr_flags = IFF_TUN | IFF_NO_PI; strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); if (ioctl(interface, TUNSETIFF, &ifr)) { perror("Cannot get TUN interface"); exit(1); } return interface; } #else #error Sorry, you have to implement this part by yourself. #endif static int get_tunnel(char *port, char *secret) { // We use an IPv6 socket to cover both IPv4 and IPv6. int tunnel = socket(AF_INET6, SOCK_DGRAM, 0); int flag = 1; setsockopt(tunnel, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag)); flag = 0; setsockopt(tunnel, IPPROTO_IPV6, IPV6_V6ONLY, &flag, sizeof(flag)); // Accept packets received on any local address. sockaddr_in6 addr; memset(&addr, 0, sizeof(addr)); addr.sin6_family = AF_INET6; addr.sin6_port = htons(atoi(port)); // Call bind(2) in a loop since Linux does not have SO_REUSEPORT. while (bind(tunnel, (sockaddr *)&addr, sizeof(addr))) { if (errno != EADDRINUSE) { return -1; } usleep(100000); } // Receive packets till the secret matches. char packet[1024]; socklen_t addrlen; do { addrlen = sizeof(addr); int n = recvfrom(tunnel, packet, sizeof(packet), 0, (sockaddr *)&addr, &addrlen); if (n <= 0) { return -1; } packet[n] = 0; } while (packet[0] != 0 || strcmp(secret, &packet[1])); // Connect to the client as we only handle one client at a time. connect(tunnel, (sockaddr *)&addr, addrlen); return tunnel; } static void build_parameters(char *parameters, int size, int argc, char **argv) { // Well, for simplicity, we just concatenate them (almost) blindly. int offset = 0; for (int i = 4; i < argc; ++i) { char *parameter = argv[i]; int length = strlen(parameter); char delimiter = ','; // If it looks like an option, prepend a space instead of a comma. if (length == 2 && parameter[0] == '-') { ++parameter; --length; delimiter = ' '; } // This is just a demo app, really. if (offset + length >= size) { puts("Parameters are too large"); exit(1); } // Append the delimiter and the parameter. parameters[offset] = delimiter; memcpy(¶meters[offset + 1], parameter, length); offset += 1 + length; } // Fill the rest of the space with spaces. memset(¶meters[offset], ' ', size - offset); // Control messages always start with zero. parameters[0] = 0; } //----------------------------------------------------------------------------- int main(int argc, char **argv) { if (argc < 5) { printf("Usage: %s <tunN> <port> <secret> options...\n" "\n" "Options:\n" " -m <MTU> for the maximum transmission unit\n" " -a <address> <prefix-length> for the private address\n" " -r <address> <prefix-length> for the forwarding route\n" " -d <address> for the domain name server\n" " -s <domain> for the search domain\n" "\n" "Note that TUN interface needs to be configured properly\n" "BEFORE running this program. For more information, please\n" "read the comments in the source code.\n\n", argv[0]); exit(1); } // Parse the arguments and set the parameters. char parameters[1024]; build_parameters(parameters, sizeof(parameters), argc, argv); // Get TUN interface. int interface = get_interface(argv[1]); // Wait for a tunnel. int tunnel; while ((tunnel = get_tunnel(argv[2], argv[3])) != -1) { printf("%s: Here comes a new tunnel\n", argv[1]); // On UN*X, there are many ways to deal with multiple file // descriptors, such as poll(2), select(2), epoll(7) on Linux, // kqueue(2) on FreeBSD, pthread(3), or even fork(2). Here we // mimic everything from the client, so their source code can // be easily compared side by side. // Put the tunnel into non-blocking mode. fcntl(tunnel, F_SETFL, O_NONBLOCK); // Send the parameters several times in case of packet loss. for (int i = 0; i < 3; ++i) { send(tunnel, parameters, sizeof(parameters), MSG_NOSIGNAL); } // Allocate the buffer for a single packet. char packet[32767]; // We use a timer to determine the status of the tunnel. It // works on both sides. A positive value means sending, and // any other means receiving. We start with receiving. int timer = 0; // We keep forwarding packets till something goes wrong. while (true) { // Assume that we did not make any progress in this iteration. bool idle = true; // Read the outgoing packet from the input stream. int length = read(interface, packet, sizeof(packet)); if (length > 0) { // Write the outgoing packet to the tunnel. send(tunnel, packet, length, MSG_NOSIGNAL); // There might be more outgoing packets. idle = false; // If we were receiving, switch to sending. if (timer < 1) { timer = 1; } } // Read the incoming packet from the tunnel. length = recv(tunnel, packet, sizeof(packet), 0); if (length == 0) { break; } if (length > 0) { // Ignore control messages, which start with zero. if (packet[0] != 0) { // Write the incoming packet to the output stream. write(interface, packet, length); } // There might be more incoming packets. idle = false; // If we were sending, switch to receiving. if (timer > 0) { timer = 0; } } // If we are idle or waiting for the network, sleep for a // fraction of time to avoid busy looping. if (idle) { usleep(100000); // Increase the timer. This is inaccurate but good enough, // since everything is operated in non-blocking mode. timer += (timer > 0) ? 100 : -100; // We are receiving for a long time but not sending. // Can you figure out why we use a different value? :) if (timer < -16000) { // Send empty control messages. packet[0] = 0; for (int i = 0; i < 3; ++i) { send(tunnel, packet, 1, MSG_NOSIGNAL); } // Switch to sending. timer = 1; } // We are sending for a long time but not receiving. if (timer > 20000) { break; } } } printf("%s: The tunnel is broken\n", argv[1]); close(tunnel); } perror("Cannot create tunnels"); exit(1); }