Virtual P****** Network (V*N) Lab

Virtual Private Network (V*N) Lab

我的(old)代码应要求放出来了,结果如本篇一修,现在2022年8月30日02:28:53应该已经过审了。
(new)代码思路见二修,希望各位自力更生,C源码放GitHub了,有缘公开/doge

task1 环境设置

在这里插入图片描述
可以使用docker容器。配置文件如下,如要模拟多客户端需要增加公网设备,直接复制修改以下client部分地址即可:

version: "3"

services:
    VPN_Client:
        image: handsonsecurity/seed-ubuntu:large
        container_name: client-10.0.2.7
        tty: true
        cap_add:
                - ALL
        devices:
                - "/dev/net/tun:/dev/net/tun"
        volumes:
                - ./volumes:/volumes
        networks:
            net-10.0.2.0:
                ipv4_address: 10.0.2.7
        command: bash -c "
                     tail -f /dev/null
                 "

    Host1:
        image: handsonsecurity/seed-ubuntu:large
        container_name: host-192.168.60.101
        tty: true
        cap_add:
                - ALL
        networks:
            net-192.168.60.0:
                ipv4_address: 192.168.60.101
        command: bash -c "
                      ip route del default  &&
                      ip route add default via 192.168.60.2  &&
                      /etc/init.d/openbsd-inetd start &&
                      tail -f /dev/null
                 "
                  

    Router:
        image: handsonsecurity/seed-ubuntu:large
        container_name: server-router
        tty: true
        cap_add:
                - ALL
        devices:
                - "/dev/net/tun:/dev/net/tun"
        sysctls:
                - net.ipv4.ip_forward=1
        volumes:
                - ./volumes:/volumes
        networks:
            net-10.0.2.0:
                ipv4_address: 10.0.2.8
            net-192.168.60.0:
                ipv4_address: 192.168.60.2
        command: bash -c "
                      ip route del default  &&
                      ip route add default via 10.0.2.1 &&
                      tail -f /dev/null
                 "

networks:
    net-192.168.60.0:
        name: net-192.168.60.0
        ipam:
            config:
                - subnet: 192.168.60.0/24

    net-10.0.2.0:
        name: net-10.0.2.0
        ipam:
            config:
                - subnet: 10.0.2.0/24

task2 使用TUN/TAP创建一个VPN隧道

SEED实验室网站提供了创建TUN的代码,修改后如下:

//vpnclient.c
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>
#include <linux/if.h>
#include <linux/if_tun.h>
#include <sys/ioctl.h>
#include <stdlib.h>

#define BUFF_SIZE 2000
#define PORT_NUMBER 55555
#define SERVER_IP "10.0.2.8" 
struct sockaddr_in peerAddr;

int createTunDevice() {
	int tunfd;
	struct ifreq ifr;
	memset(&ifr, 0, sizeof(ifr));
	
	ifr.ifr_flags = IFF_TUN | IFF_NO_PI;  
	
	tunfd = open("/dev/net/tun", O_RDWR);
	ioctl(tunfd, TUNSETIFF, &ifr);       
	
	return tunfd;
}

int connectToUDPServer(){
    int sockfd;
    char *hello="Hello";

    memset(&peerAddr, 0, sizeof(peerAddr));
    peerAddr.sin_family = AF_INET;
    peerAddr.sin_port = htons(PORT_NUMBER);
    peerAddr.sin_addr.s_addr = inet_addr(SERVER_IP);

    sockfd = socket(AF_INET, SOCK_DGRAM, 0);

    // Send a hello message to "connect" with the VPN server
    sendto(sockfd, hello, strlen(hello), 0,
                (struct sockaddr *) &peerAddr, sizeof(peerAddr));

    return sockfd;
}



void tunSelected(int tunfd, int sockfd){
    int  len;
    char buff[BUFF_SIZE];

    printf("Client: Got a packet from TUN\n");

    bzero(buff, BUFF_SIZE);
    len = read(tunfd, buff, BUFF_SIZE);
    sendto(sockfd, buff, len, 0, (struct sockaddr *) &peerAddr,
                    sizeof(peerAddr));
}

void socketSelected (int tunfd, int sockfd){
    int  len;
    char buff[BUFF_SIZE];

    printf("Client: Got a packet from the server tunnel\n");

    bzero(buff, BUFF_SIZE);
    len = recvfrom(sockfd, buff, BUFF_SIZE, 0, NULL, NULL);
    write(tunfd, buff, len);

}
int main (int argc, char * argv[]) {
	int tunfd, sockfd;

	tunfd  = createTunDevice();
	sockfd = connectToUDPServer();
	system("ip addr add 192.168.53.99/24 dev tun0");
	system("ip link set dev tun0 up ");
	system("ip route add 192.168.60.0/24 dev tun0 via 192.168.53.99");
	system("ip route add 192.168.78.0/24 dev tun0 via 192.168.53.99");

	// Enter the main loop
	while (1) {
		fd_set readFDSet;
		
		FD_ZERO(&readFDSet);
		FD_SET(sockfd, &readFDSet);
		FD_SET(tunfd, &readFDSet);
		select(FD_SETSIZE, &readFDSet, NULL, NULL, NULL);
		
		if (FD_ISSET(tunfd,  &readFDSet)) tunSelected(tunfd, sockfd);
		if (FD_ISSET(sockfd, &readFDSet)) socketSelected(tunfd, sockfd);
	}
}
//vpnserver.c
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>
#include <linux/if.h>
#include <linux/if_tun.h>
#include <sys/ioctl.h>
#include <stdlib.h>

#define PORT_NUMBER 55555
#define BUFF_SIZE 2000

struct sockaddr_in peerAddr;

int createTunDevice() {
	int tunfd;
	struct ifreq ifr;
	memset(&ifr, 0, sizeof(ifr));
	
	ifr.ifr_flags = IFF_TUN | IFF_NO_PI;  
	
	tunfd = open("/dev/net/tun", O_RDWR);
	ioctl(tunfd, TUNSETIFF, &ifr);       
	
	return tunfd;
}

int initUDPServer() {
    int sockfd;
    struct sockaddr_in server;
    char buff[100];

    memset(&server, 0, sizeof(server));
    server.sin_family = AF_INET;                 
    server.sin_addr.s_addr = htonl(INADDR_ANY);
    server.sin_port = htons(PORT_NUMBER);        

    sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    bind(sockfd, (struct sockaddr*) &server, sizeof(server)); 

    // Wait for the VPN client to "connect".
    bzero(buff, 100);
    int peerAddrLen = sizeof(struct sockaddr_in);
    int len = recvfrom(sockfd, buff, 100, 0,                  
                (struct sockaddr *) &peerAddr, &peerAddrLen);

    printf("Connected with the client: %s\n", buff);
    return sockfd;
}

void tunSelected(int tunfd, int sockfd){
    int  len;
    char buff[BUFF_SIZE];

    printf("Server: Got a packet from TUN\n");

    bzero(buff, BUFF_SIZE);
    len = read(tunfd, buff, BUFF_SIZE);
    sendto(sockfd, buff, len, 0, (struct sockaddr *) &peerAddr,
                    sizeof(peerAddr));
}

void socketSelected (int tunfd, int sockfd){
    int  len;
    char buff[BUFF_SIZE];

    printf("Server: Got a packet from the client tunnel\n");

    bzero(buff, BUFF_SIZE);
    len = recvfrom(sockfd, buff, BUFF_SIZE, 0, NULL, NULL);
    write(tunfd, buff, len);

}
int main (int argc, char * argv[]) {
	int tunfd, sockfd;

	tunfd  = createTunDevice();
	sockfd = initUDPServer();
	system("ip addr add 192.168.78.100/24 dev tun0");
	system("ip link set dev tun0 up ");
	system("ip route add 192.168.53.0/24 dev tun0 via 192.168.78.100");

	// Enter the main loop
	while (1) {
		fd_set readFDSet;
		
		FD_ZERO(&readFDSet);
		FD_SET(sockfd, &readFDSet);
		FD_SET(tunfd, &readFDSet);
		select(FD_SETSIZE, &readFDSet, NULL, NULL, NULL);
		
		if (FD_ISSET(tunfd,  &readFDSet)) tunSelected(tunfd, sockfd);
		if (FD_ISSET(sockfd, &readFDSet)) socketSelected(tunfd, sockfd);
	}
}

实现互ping:
在这里插入图片描述
在这里插入图片描述

  • 本章在实验手册中共分为6部分进行实验,已经在另一个实验中完成,本节不予展示。

Task 3 加密隧道

这里实验室官网同样提供了配置文件,但是其中的证书早已过期,应使用其中附带的ca文件重新生成证书,密码为seed

openssl ca -config openssl.cnf -policy policy_anything -md sha256 -days 3650 -in server-csr.pem -out server.crt -batch -cert ./cacert.pem -keyfile ./cakey.pem 

同时要在client的/etc/hosts中添加域名配置,因为tls的证书是颁发给该域名的。查看证书信息可以参考我之前写过的博客

应当按照手册中的README进行操作:

*************************************************
* tlsserver.c tls server program 
* tlsclient.c tls client program 
* cert_server server certificate folder 
* ca_client   client CA folder
************************************************

---------------------------
To compile the compile: 
---------------------------
$ make 


---------------------------
To run the server:
---------------------------
$ ./tlsserver

Note: The server's certificate is ./cert_server/server-cert.pem, 
and the common name of the certificate is 'vpnlabserver.com'. 


---------------------------
To run the client:
---------------------------

Since the common name of the server's certificate is 'vpnlabserver.com', 
the hostname used by the client should match with this common name, or
the TLS connection will fail (this checking is implemented in the client 
program).  

First, add the following line to /etc/hosts, so the name 'vpnlabserver.com'
is mapped to your server's IP address (server_ip should be replaced by a 
real IP address):
 
server_ip     vpnlabserver.com
 
Now, we can run the client program (our server listens to port 4433):
$ ./tlsclient vpnlabserver.com  4433

We have put two CA certificates in the ./ca_client folder: one is the 
CA that signs our server certificate, and the other is the CA that 
signs Google's certificate. Therefore, we can also use our client to
talk to Google's HTTPS server:

$ ./tlsclient www.google.com 443

See the instructions in the lab description if you want to use this client
program to talk to other HTTPS servers. 

如图,完成tls通信至结束会话的所有报文,可以看到client向server发送了加密的访问请求,服务器返回的加密信息:
在这里插入图片描述

完成TLS解密后就可以收到一份html文件,拷贝后用浏览器打开:
在这里插入图片描述
提供的代码:

//tlsclient.c
#include <arpa/inet.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <netdb.h>


#define CHK_SSL(err) if ((err) < 1) { ERR_print_errors_fp(stderr); exit(2); }
#define CA_DIR "./ca_client" 

int verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx)
{
    char  buf[300];

    X509* cert = X509_STORE_CTX_get_current_cert(x509_ctx);
    X509_NAME_oneline(X509_get_subject_name(cert), buf, 300);
    printf("subject= %s\n", buf);

    if (preverify_ok == 1) {
       printf("Verification passed.\n");
    } else {
       int err = X509_STORE_CTX_get_error(x509_ctx);
       printf("Verification failed: %s.\n",
                    X509_verify_cert_error_string(err));
    }
}

SSL* setupTLSClient(const char* hostname)
{
    // Step 0: OpenSSL library initialization 
   // This step is no longer needed as of version 1.1.0.
   SSL_library_init();
   SSL_load_error_strings();
   SSLeay_add_ssl_algorithms();

   SSL_METHOD *meth;
   SSL_CTX* ctx;
   SSL* ssl;

   meth = (SSL_METHOD *)TLSv1_2_method();
   ctx = SSL_CTX_new(meth);

   SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
   if(SSL_CTX_load_verify_locations(ctx,NULL, CA_DIR) < 1){
	printf("Error setting the verify locations. \n");
	exit(0);
   }
   ssl = SSL_new (ctx);

   X509_VERIFY_PARAM *vpm = SSL_get0_param(ssl); 
   X509_VERIFY_PARAM_set1_host(vpm, hostname, 0);

   return ssl;
}


int setupTCPClient(const char* hostname, int port)
{
   struct sockaddr_in server_addr;

   // Get the IP address from hostname
   struct hostent* hp = gethostbyname(hostname);

   // Create a TCP socket
   int sockfd= socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

   // Fill in the destination information (IP, port #, and family)
   memset (&server_addr, '\0', sizeof(server_addr));
   memcpy(&(server_addr.sin_addr.s_addr), hp->h_addr, hp->h_length);
//   server_addr.sin_addr.s_addr = inet_addr ("10.0.2.14"); 
   server_addr.sin_port   = htons (port);
   server_addr.sin_family = AF_INET;

   // Connect to the destination
   connect(sockfd, (struct sockaddr*) &server_addr,
           sizeof(server_addr));

   return sockfd;
}


int main(int argc, char *argv[])
{
   char *hostname = "yahoo.com";
   int port = 443;

   if (argc > 1) hostname = argv[1];
   if (argc > 2) port = atoi(argv[2]);
   printf("%s\n",hostname);

   /*----------------TLS initialization ----------------*/
   SSL *ssl   = setupTLSClient(hostname);printf("abc");

   /*----------------Create a TCP connection ---------------*/
   int sockfd = setupTCPClient(hostname, port);printf("abc");

   /*----------------TLS handshake ---------------------*/
   SSL_set_fd(ssl, sockfd);printf("***");
   int err = SSL_connect(ssl);printf("###"); CHK_SSL(err);printf("!!!");
   printf("SSL connection is successful\n");
   printf ("SSL connection using %s\n", SSL_get_cipher(ssl));printf("abc");

   /*----------------Send/Receive data --------------------*/
   char buf[9000];
   char sendBuf[200];
   sprintf(sendBuf, "GET / HTTP/1.1\nHost: %s\n\n", hostname);
   SSL_write(ssl, sendBuf, strlen(sendBuf));

   int len;
   do {
     len = SSL_read (ssl, buf, sizeof(buf) - 1);
     buf[len] = '\0';
     printf("%s\n",buf);
   } while (len > 0);
}

//tlsserver.c
#include <arpa/inet.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <netdb.h>
#include <unistd.h>

#define CHK_SSL(err) if ((err) < 1) { ERR_print_errors_fp(stderr); exit(2); }
#define CHK_ERR(err,s) if ((err)==-1) { perror(s); exit(1); }

int  setupTCPServer();                   // Defined in Listing 19.10
void processRequest(SSL* ssl, int sock); // Defined in Listing 19.12

int main(){

  SSL_METHOD *meth;
  SSL_CTX* ctx;
  SSL *ssl;
  int err;

  // Step 0: OpenSSL library initialization 
  // This step is no longer needed as of version 1.1.0.
  SSL_library_init();
  SSL_load_error_strings();
  SSLeay_add_ssl_algorithms();

  // Step 1: SSL context initialization
  meth = (SSL_METHOD *)TLSv1_2_method();
  ctx = SSL_CTX_new(meth);
  SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, NULL);
  // Step 2: Set up the server certificate and private key
  SSL_CTX_use_certificate_file(ctx, "./cert_server/server-cert.pem", SSL_FILETYPE_PEM);
  SSL_CTX_use_PrivateKey_file(ctx, "./cert_server/server-key.pem", SSL_FILETYPE_PEM);
  // Step 3: Create a new SSL structure for a connection
  ssl = SSL_new (ctx);

  struct sockaddr_in sa_client;
  size_t client_len;
  int listen_sock = setupTCPServer();

  while(1){
    int sock = accept(listen_sock, (struct sockaddr*)&sa_client, &client_len);
    if (fork() == 0) { // The child process
       close (listen_sock);

       SSL_set_fd (ssl, sock);
       int err = SSL_accept (ssl);
       CHK_SSL(err);
       printf ("SSL connection established!\n");

       processRequest(ssl, sock);
       close(sock);
       return 0;
    } else { // The parent process
        close(sock);
    }
  }
}


int setupTCPServer()
{
    struct sockaddr_in sa_server;
    int listen_sock;

    listen_sock= socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
    CHK_ERR(listen_sock, "socket");
    memset (&sa_server, '\0', sizeof(sa_server));
    sa_server.sin_family      = AF_INET;
    sa_server.sin_addr.s_addr = INADDR_ANY;
    sa_server.sin_port        = htons (4433);
    int err = bind(listen_sock, (struct sockaddr*)&sa_server, sizeof(sa_server));
    CHK_ERR(err, "bind");
    err = listen(listen_sock, 5);
    CHK_ERR(err, "listen");
    return listen_sock;
}

void processRequest(SSL* ssl, int sock)
{
    char buf[1024];
    int len = SSL_read (ssl, buf, sizeof(buf) - 1);
    buf[len] = '\0';
    printf("Received: %s\n",buf);

    // Construct and send the HTML page
    char *html =
	"HTTP/1.1 200 OK\r\n"
	"Content-Type: text/html\r\n\r\n"
	"<!DOCTYPE html><html>"
	"<head><title>Hello World</title></head>"
	"<style>body {background-color: black}"
	"h1 {font-size:3cm; text-align: center; color: white;"
	"text-shadow: 0 0 3mm yellow}</style></head>"
	"<body><h1>Hello, world!</h1></body></html>";
    SSL_write(ssl, html, strlen(html));
    SSL_shutdown(ssl);  SSL_free(ssl);
}

Task 4 认证服务器端身份

本节要求服务器能够认证客户端的身份。也就是用我们拥有的CA为自己的网址颁发证书。同样我已经写过该博客。注意生成过证书之后要改一下原来的服务器代码并remake一下。
可以成功访问:
在这里插入图片描述

Task 5认证客户端身份

本章在手册中提示我们可以使用系统自带的登录系统,通过模拟系统用户登录代替我们自制的登录系统。给出的登录代码案例:

#include <stdio.h>
#include <string.h>
#include <shadow.h>
#include <crypt.h>
int login(char *user, char *passwd)
{
	struct spwd *pw;
	char *epasswd;
	pw = getspnam(user);
	if (pw == NULL) {
		return -1;
	}
	printf("Login name: %s\n", pw->sp_namp);
	printf("Passwd : %s\n", pw->sp_pwdp);
	epasswd = crypt(passwd, pw->sp_pwdp);
	if (strcmp(epasswd, pw->sp_pwdp)) {
		return -1;
	}
	return 1;
}

void main(int argc, char** argv)
{
	if (argc < 3) {
		printf("Please provide a user name and a password\n");
		return;
	}
	int r = login(argv[1], argv[2]);
	printf("Result: %d\n", r);
}

登录成功会return 1:
在这里插入图片描述

Task 6支持多客户端

服务器需要决定哪个报文应该发给哪个客户端,并把报文传输给对应子进程。这涉及到多进程间通信。参考指导4

指导

指导1.2略
指导3参见Task 5

指导4 管道间通信

通信结果:
在这里插入图片描述

参考代码:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
int main(void)
{
	int fd[2], nbytes;
	pid_t pid;
	char string[] = "Hello, world!\n";
	char readbuffer[80];
	pipe(fd);
	if((pid = fork()) == -1) {
		perror("fork");
		exit(1);
	}
	if(pid>0) { //parent process
		close(fd[0]); // Close the input end of the pipe.
		// Write data to the pipe.
		write(fd[1], string, (strlen(string)+1));
		exit(0);
	}
	else { //child process
		close(fd[1]); // Close the output end of the pipe.
		// Read data from the pipe.
		nbytes = read(fd[0], readbuffer, sizeof(readbuffer));
		printf("Child process received string: %s", readbuffer);
	}
	return(0);
}

笔记

这一节没什么用,就是记录一些学习过程中找到的觉得有意思的知识点,记录一下,不感兴趣的可以直接跳到部分

  • accept()函数
      准备好了,系统调用 accept() 会有点古怪的地方的!你可以想象发生 这样的事情:有人从很远的地方通过一个你在侦听 (listen()) 的端口连接 (connect()) 到你的机器。它的连接将加入到等待接受 (accept()) 的队列 中。你调用 accept() 告诉它你有空闲的连接。它将返回一个新的套接字文 件描述符!这样你就有两个套接字了,原来的一个还在侦听你的那个端口, 新的在准备发送 (send()) 和接收 (recv()) 数据。这就是这个过程!

  • https://blog.csdn.net/petershina/article/details/7955183

  • 进程间信号通信:https://blog.csdn.net/takashi77/article/details/108110190

  • 由于是从TUN读取报文,截取的报文是去除Ether头的,所以IPsrc在12~15位。下图为证明:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 从指定接口读取https://blog.csdn.net/ternence_hsu/article/details/70142612
  • 如果要客户端与服务器端两者tun一一对应,则其网络号必须统一,例如都在192.168.53.0/24这同一个网段下。否则由于指定的是ip route add client_ip dev tun_name via server_ip两边都是主机号IP,则由于不属于同一链路,tun接口将始终处于down的状态。
  • 定义一个拥有自定义接口名的tun虚拟接口,参考
// 创建对应TUN接口
int main(){
	// ...(skip)
	int tunfd;
	char tunName[10];
	sprintf(tunName, "tun%d", sessionID);
	// tunfd = createTunDevice();
	char cmd1[40], cmd2[45], cmd3[30], cmd4[65];
	// sprintf(cmd1, "ip tuntap add dev %s mod tun", tunName);
	tunfd = tun_alloc(tunName);
	sprintf(cmd2, "ip addr add 192.168.53.%d/24 dev %s", sessionID + 128, tunName);
	sprintf(cmd3, "ip link set dev %s up", tunName);
	sprintf(cmd4, "ip route add 192.168.53.%d dev %s via 192.168.53.%d", sessionID, tunName, sessionID + 128);
	system(cmd1);
	system(cmd2);
	system(cmd3);
	system(cmd4);
	// ...(skip)
}

// 这个函数在头文件中也包含,但是我这边编译不通过
int tun_alloc(char dev[IFNAMSIZ]) // dev数组用于存储设备的名称
{
    struct ifreq ifr;
    int fd, err;

    if ((fd = open("/dev/net/tun", O_RDWR)) < 0)
    { // 打开文件
        perror("open");
        return -1;
    }

    bzero(&ifr, sizeof(ifr));

    /* Flags : IFF_TUN   - TUN设备
     *         IFF_TAP   - TAP设备
     *         IFF_NO_PI - 不需要提供包的信息
     */

    ifr.ifr_flags = IFF_TUN | IFF_NO_PI; // tun设备不包含以太网头部,而tap包含,仅此而已

    if (*dev)
    {
        strncpy(ifr.ifr_name, dev, IFNAMSIZ);
    }

    if ((err = ioctl(fd, TUNSETIFF, (void *)&ifr)) < 0)
    { // 打开设备
        perror("ioctl TUNSETIFF");
        close(fd);
        return err;
    }
    // 一旦设备开启成功,系统会给设备分配一个名称对于tun设备,一般为tunX,X为从0开始的编号,对于tap设备
    // 一般为tapX,X为从0开始的编号
    strcpy(dev, ifr.ifr_name); // 拷贝设备的名称至dev中
    return fd;
}

一修

本次作业的实验手册推荐采用管道通信,但奈何本人水平有限,由于父进程的accept即使是是阻塞函数,我也没能找到替代的方法,所以无法在父进程监听TUN并为收到的报文分配隧道。
写到这里很不甘心,熬夜用6个小时尝试用线程等方法解决问题。accept确实解决了,但是管道的读写并不简单,其中5个小时都在和管道做斗争。现在2022年8月28日07:23:47,我放弃了。
我现在的方法是每一个子进程检索我从同一个tun中收到的,并准备发给客户端的报文 的ip是否与当前子进程负责会话的客户端的ip匹配:如果不匹配就rewrite进tun,同时自身sleep100ms,让其他子进程尝试接发该报文;如果匹配其负责的客户端IP,则通过(建有ssl加密的)socket发出去

但是这样有个问题就是,如果登录的客户端多的话,就会导致大家都读到不匹配的报文,于是整个系统都会锁死无法动弹。
不过提交作业的话这样应该是能过关了。。。

二修

通了通了!!!
现讲解思路:
根据实验手册的方法一步一步走,实现单客户端的VPN系统并不困难,只需要稍微在客户端的用户认证方面思考一下。难是难在如何为多客户端分配报文。
由于为了能够比较方便且直观地建立会话,实验手册与我都认为应当采用多进程的方式(而并非多线程),原因只有一个:看代码的时候看得清楚,也不需要传参传一大堆。
但是这样就会有一个问题:如何确认接收报文?

。。。大半夜的不想写这么繁琐了,一句话:在服务器建立多个tun,每个tun都与一个客户端对应,并且这些tun可以共用一个/dev/net/tun文件,不需要创建新的。什么狗屁管道什么狗屎IPC,统统滚蛋!我一个子进程就有一个tun,十个子进程就10个tun,关你父进程tm什么事。创建多个tun参考代码见笔记!!

晚安!!!

三修

说了晚安但是还有一点忘说了。正如开篇放的(old)代码中已经实现了的,我利用登录过程为每个客户端通过SSL分配了一个临时的sessionID,这样客户端与服务器就会知道当前他们之间的会话号:客户端这边用该ID自定义自己的TUN的IP地址,一修时仅因为好看所以加上了,之后其用于服务器端对报文分发的判断,父进程另开线程单独负责对tun的读取,并以其D段ip决定通过pipe分发给哪个子进程,再由子进程发送给客户端,但是由于pipe“优秀”的配置属性,我没能成功实现这种做法,但在二修后我感到很庆幸,因为我认为二修的方法效率更高;二修中ID不再仅仅用于决定客户端的IP分配,还决定了服务器端开设的众多TUN接口的名称、IP地址,并实现一一对应(server_IP_D = client_IP_D + 128

从上述我们也可以看出这种分配方式还有安全威胁与可提升空间:

  • 安全威胁:如果客户2是黑客,在完成登录后可以修改自己发出报文的IP地址伪装成客户1,虽然TLS能够保护报文不受外部公网干扰,但是依然不能防范内部的攻击(突然觉得这不是废话嘛)。
  • 可提升空间:当前sessionID仅维持在1~127之间,如果完成一个循环127个会话尝试之后,ID的分配又会从1开始计数,此时应当可以通过map等,记录当前这个sessionID的使用者是否仍在维持通信,如果还在使用,则不应将这个ID分配给新的远程主机。以此可以实现ID与会话的动态分配

晚安(2022年8月30日02:51:27)
在这里插入图片描述

  • 17
    点赞
  • 36
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 10
    评论
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

winnower-sliff

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值