Skip to content

Instantly share code, notes, and snippets.

@RecursiveG
Created July 23, 2016 07:45
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save RecursiveG/96ae19ff9ed2940001e218458f722e6f to your computer and use it in GitHub Desktop.
Save RecursiveG/96ae19ff9ed2940001e218458f722e6f to your computer and use it in GitHub Desktop.
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <stropts.h>
#include <signal.h>
#include <netdb.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <linux/if.h>
#include <linux/if_tun.h>
#define CLONE_DEV "/dev/net/tun"
#define BUFF_SIZE 2048
volatile sig_atomic_t flag = 0;
void on_signal_interrupt(int sig){ // can be called asynchronously
flag = 1; // set flag
}
int open_tun(char * const dev, int dev_length, int flag) {
int fd = open(CLONE_DEV, O_RDWR);
if (fd < 0) return fd;
struct ifreq * ifr = malloc(sizeof(struct ifreq));
ifr->ifr_flags = flag;
strncpy(ifr->ifr_name, dev, IFNAMSIZ);
int err = ioctl(fd, TUNSETIFF, ifr);
if (err < 0) {
close(fd);
free(ifr);
return err;
}
strncpy(dev, ifr->ifr_name, dev_length);
free(ifr);
return fd;
}
int read_exact(int fd, char *buf, int len) {
int read_len = 0, rem = len;
while(rem > 0) {
int tmp = read(fd, buf+read_len, rem);
if (tmp < 0) {
return tmp;
}
if (tmp == 0) {
return read_len;
}
read_len += tmp;
rem -= tmp;
}
return read_len;
}
int peek_exact(int fd, char *buf, int len) {
int read_len = 0, rem = len;
while(rem > 0) {
int tmp = recv(fd, buf+read_len, rem, MSG_PEEK);
if (tmp < 0) {
return tmp;
}
if (tmp == 0) {
return read_len;
}
read_len += tmp;
rem -= tmp;
}
return read_len;
}
int write_exact(int fd, char *buf, int len) {
int written = 0, rem = len;
while(rem > 0) {
int tmp = write(fd, buf + written, rem);
if (tmp < 0) {
return tmp;
}
if (tmp == 0) {
return written;
}
written += tmp;
rem -= tmp;
}
return written;
}
int read_int(int fd) {
uint32_t tmp;
read_exact(fd, (char*)(&tmp), sizeof(tmp));
return ntohl(tmp);
}
void write_int(int fd, int data) {
uint32_t tmp = htonl(data);
write_exact(fd, (char*)(&tmp), sizeof(tmp));
}
int peek_int(int fd) {
uint32_t tmp;
peek_exact(fd, (char*)(&tmp), sizeof(tmp));
return ntohl(tmp);
}
void communicate(int tun_fd, int tcp_fd[4]) {
int epoll_fd = epoll_create(1024);
struct epoll_event ev; // 记录套接字相关信息
ev.events = EPOLLIN; // 监视有数据可读事件
ev.data.fd = tun_fd; // 文件描述符数据,其实这里可以放任何数据。
epoll_ctl(epoll_fd, EPOLL_CTL_ADD, tun_fd, &ev);
for (int i = 0; i<4; i++) {
ev.data.fd = tcp_fd[i];
epoll_ctl(epoll_fd, EPOLL_CTL_ADD, tcp_fd[i], &ev);
}
struct epoll_event events_in[16];
sigset_t sigset;
sigemptyset(&sigset); //listen to all signals
char buffer[2048];
int send_idx = 0;
int read_idx = 0;
while(1) {
int event_count = epoll_pwait(epoll_fd, events_in, 16, -1, &sigset);
if (flag || event_count == 0) break;
for (int i = 0; i<event_count; i++) { // 遍历所有事件
if (events_in[i].data.fd == tun_fd) {
int read_n = read(tun_fd, buffer, 2048);
if (read_n < 0) {
perror("tun read()");
continue;
}
if (read_n == 0) {
perror("tun read0?!");
continue;
}
int selected_fd = tcp_fd[send_idx%4];
write_int(selected_fd, send_idx++);
write_int(selected_fd, read_n);
int temp = write_exact(selected_fd, buffer, read_n);
if (temp < 0) {
perror("tcp write()");
} else if (temp != read_n) {
printf("tcp write() size error");
}
} else {
int this_fd = events_in[i].data.fd;
int incoming_idx = peek_int(this_fd);
if (incoming_idx != read_idx) continue;
read_idx++;
read_int(this_fd);
int frame_size = read_int(this_fd);
int read_n = read_exact(this_fd, buffer, frame_size);
if (read_n < 0) {
perror("tcp read()");
continue;
}
if (read_n == 0) {
printf("remote closed tcp\n");
return;
}
int temp = write_exact(tun_fd, buffer, read_n);
if (temp < 0) {
perror("tun write()");
}
}
}
}
close(epoll_fd);
}
void tcp_server(int tun_fd, char *port) {
struct addrinfo *listen_addr; //存放解析结果。参见`man getaddrinfo`
if (getaddrinfo("0.0.0.0", port, NULL, &listen_addr)) {
perror("getaddrinfo");
}
int fd = socket(AF_INET, SOCK_STREAM, 0);
if (fd < 0) perror("socket()");
if (bind(fd, listen_addr->ai_addr, listen_addr->ai_addrlen)) {
perror("bind");
}
int epoll_fd = epoll_create(1024);
struct epoll_event ev; // 记录套接字相关信息
ev.events = EPOLLIN; // 监视有数据可读事件
ev.data.fd = fd; // 文件描述符数据,其实这里可以放任何数据。
epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ev);
struct epoll_event events_in[16];
sigset_t sigset;
sigemptyset(&sigset); //listen to all signals
int client_fd[4];
int client_fd_count = 0;
if (listen(fd, 128)) {
perror("listen");
}
printf("Waiting for connections...\n");
while(1) {
int event_count = epoll_pwait(epoll_fd, events_in, 16, -1, &sigset);
if (flag || event_count == 0) break;
int new_fd = accept(fd, NULL, NULL);
if (new_fd < 0) perror("accept()");
client_fd[client_fd_count++] = new_fd;
printf("Connection establishing... No.%d\n", client_fd_count);
if (client_fd_count == 4) {
printf("Connection established!\n");
communicate(tun_fd, client_fd);
close(client_fd[0]);
close(client_fd[1]);
close(client_fd[2]);
close(client_fd[3]);
client_fd_count = 0;
if (flag) break;
printf("Waiting for connections...\n");
}
}
close(epoll_fd);
close(fd);
freeaddrinfo(listen_addr);
}
int open_tcp_connection(const struct addrinfo * const server_addr, const char * const local_ip) {
int fd = socket(AF_INET, SOCK_STREAM, 0);
if (fd < 0) perror("socket()");
struct addrinfo *local_addr;
if (getaddrinfo(local_ip, "0", NULL, &local_addr)) {
perror("getaddrinfo");
}
if (bind(fd, local_addr->ai_addr, local_addr->ai_addrlen))perror("bind()");
if (connect(fd, server_addr->ai_addr, server_addr->ai_addrlen))perror("connect()");
return fd;
}
void tcp_client(int tun_fd, const char * const ip, const char * const port) {
struct addrinfo *server_addr; //存放解析结果。参见`man getaddrinfo`
if (getaddrinfo(ip, port, NULL, &server_addr)) {
perror("getaddrinfo");
}
int tcp_cons[4];
printf("opening...\n");
tcp_cons[0] = open_tcp_connection(server_addr, "192.168.88.10");
tcp_cons[1] = open_tcp_connection(server_addr, "192.168.88.22");
tcp_cons[2] = open_tcp_connection(server_addr, "192.168.88.23");
tcp_cons[3] = open_tcp_connection(server_addr, "192.168.88.24");
printf("established...\n");
communicate(tun_fd, tcp_cons);
close(tcp_cons[0]);
close(tcp_cons[1]);
close(tcp_cons[2]);
close(tcp_cons[3]);
freeaddrinfo(server_addr);
}
int main(int argc, char *argv[]) {
signal(SIGINT, on_signal_interrupt);
char tun_name[256];
strncpy(tun_name, "tun0", 256);
int tun_fd = open_tun(tun_name, 256, IFF_TUN|IFF_NO_PI);
if (tun_fd < 0) {
perror("Failed to open TUN device.");
return -1;
} else {
printf("TUN device opened, fd = %d\n", tun_fd);
}
if (2 == argc) {
tcp_server(tun_fd, argv[1]);
} else if (3 == argc) {
tcp_client(tun_fd, argv[1], argv[2]);
} else {
printf(" %1$s [listen-port]\nor %1$s [target-ip] [target-port]\n", argv[0]);
}
close(tun_fd);
printf("TUN device closed.\n");
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment