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

这篇具有很好参考价值的文章主要介绍了Virtual P****** Network (V*N) Lab。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

Virtual Private Network (V*N) Lab

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

task1 环境设置

seedlab:virtual private network lab,网安,个人用,服务器,docker,网络,linux
可以使用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:
seedlab:virtual private network lab,网安,个人用,服务器,docker,网络,linux
seedlab:virtual private network lab,网安,个人用,服务器,docker,网络,linux

  • 本章在实验手册中共分为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发送了加密的访问请求,服务器返回的加密信息:
seedlab:virtual private network lab,网安,个人用,服务器,docker,网络,linux

完成TLS解密后就可以收到一份html文件,拷贝后用浏览器打开:
seedlab:virtual private network lab,网安,个人用,服务器,docker,网络,linux
提供的代码:

//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一下。
可以成功访问:
seedlab:virtual private network lab,网安,个人用,服务器,docker,网络,linux

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:
seedlab:virtual private network lab,网安,个人用,服务器,docker,网络,linux

Task 6支持多客户端

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

指导

指导1.2略
指导3参见Task 5

指导4 管道间通信

通信结果:
seedlab:virtual private network lab,网安,个人用,服务器,docker,网络,linux

参考代码:

#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位。下图为证明:

seedlab:virtual private network lab,网安,个人用,服务器,docker,网络,linux
seedlab:virtual private network lab,网安,个人用,服务器,docker,网络,linux
seedlab:virtual private network lab,网安,个人用,服务器,docker,网络,linux

  • 从指定接口读取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)
seedlab:virtual private network lab,网安,个人用,服务器,docker,网络,linux文章来源地址https://www.toymoban.com/news/detail-708322.html

到了这里,关于Virtual P****** Network (V*N) Lab的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • rabbitmq 开启 virtual host

    由于我的rabbitmq架设在测试服务期。 导致我本地测试的mq消息,经常被服务器消费掉。 所以通过添加v-host,可以创建专属v-host域下的消息进行生产和消费。 点击Admin,点击右边Users 输入Username Password ,并且Tags给与Admin权限 点击virtual host,新增host 我的vhost叫做local_test,在上图点

    2024年02月15日
    浏览(26)
  • 虚拟相机 Cinemachine Virtual Camera

    本质上,虚拟相机应该是 相机行为 的配置文件,虚拟相机之间的切换实际上就是在进行相机行为之间的切换; 虚拟相机并不会创建任何摄像机,他只会创建虚拟节点,实际上操作的是 Cinemachine Brain 虚拟相机属性设置完毕后,应尽量避免在游戏中对齐进行修改, 如有需要可以多创建几

    2024年02月11日
    浏览(25)
  • Python虚拟环境(Virtual Environment)

    Python虚拟环境是一种用于隔离和管理项目所需的Python解释器及其依赖库的工具。它可以让我们在同一台机器上同时运行多个项目,并且每个项目都可以有不同的依赖库和Python版本。虚拟环境可以为每个项目提供独立的运行环境,避免各项目之间的依赖冲突。 在开发项目时,我

    2024年01月20日
    浏览(64)
  • LVS (Linux Virtual Server)

    LVS (Linux Virtual Server) 是一个基于 Linux 操作系统的负载均衡器。它通过将请求分发到多个服务器来实现负载均衡,从而提高系统的可扩展性和可靠性。LVS 提供了多种负载均衡算法,可以根据需要选择适合的算法,例如轮询、加权轮询、最少连接和源地址散列等。 使用 LVS 可以

    2024年02月15日
    浏览(22)
  • 【Azure】Virtual Hub & vWAN

    虚拟 WAN 文档 Azure 虚拟 WAN 是一个网络服务,其中整合了多种网络、安全和路由功能,提供单一操作界面。 我们主要讨论两种连接情况: 通过一个 vWAN 来连接不通的 vNET 和本地网络。以下是一个扩展的拓扑 结合 vhub,可以把两个中心连接起来 路由 Azure 虚拟 WAN 支持以下全局

    2024年02月11日
    浏览(23)
  • JVM(Java Virtual Machine)

    哥几个来学 JVM 啦~~     目录 🌲一、JVM 执行流程( JVM 是如何运行的?) 🌳二、JVM 运行时数据区 🍦1. 堆(线程共享) 🍧2. Java 虚拟机栈(线程私有) 🍨3. 本地方法栈(线程私有) 🍩4. 程序计数器(线程私有) 🍪5. 方法区(线程共享) 🌴三、JVM 类加载(Class Loading)

    2024年02月15日
    浏览(30)
  • C#关键字Virtual用法详解

    本篇讲解C#Virtual用法 目录 定义 特性 实例 virtual 用于修饰方法、属性、索引器或事件声明,并且允许在派生类中重写这些对象。 虚拟成员的实现可由派生类中的重写成员更改

    2024年02月15日
    浏览(40)
  • Virtual box安装Ubuntu1804乱码

    运行以下命令打开locale配置文件: 接着进入设置,搜索language,进入设置语言部分 依次点击manage installed languages(管理已安装的语言), Install Remove Languages…(添加或删除语言), 下翻找到中文简体(Chinese simplified), 点击应用(截图为已经配置好后的界面,选项位置是一

    2024年02月08日
    浏览(33)
  • C++基础之关键字——virtual详解

    修饰父类中的普通函数 被修饰的函数称为虚函数, 是C++中多态的一种实现(多说一句,多态分编译时多态-通过重载实现和运行时多态-通过虚函数实现)。 也就是说用父类的指针或者引用指向其派生类的对象,当使用指针或引用调用函数的时候会根据具体的对象类型调用对应对

    2024年02月08日
    浏览(27)
  • C++ 虚函数virtual的引入和应用

            来回顾一下使用引用或指针调用方法的过程。请看下面的代码: BrassPlus ophelia;        // 子类对象 Brass * bp;                // 基类指针 bp = ophelia;            // 让基类指针指向子类对象 bp-ViewAcct();            // ViewAcct() 如果基类和子类都有这个函数,

    2024年02月02日
    浏览(14)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包