You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
289 lines
9.4 KiB
289 lines
9.4 KiB
/*
|
|
* 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);
|
|
}
|