Created
July 23, 2016 07:45
-
-
Save RecursiveG/96ae19ff9ed2940001e218458f722e6f to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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