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

可以使用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
SEED实验室网站提供了创建TUN的代码,修改后如下:
//vpnclient.c
#include
#include
#include
#include
#include
#include
#include
#include
#include
#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
#include
#include
#include
#include
#include
#include
#include
#include
#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:


这里实验室官网同样提供了配置文件,但是其中的证书早已过期,应使用其中附带的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
#include
#include
#include
#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
#include
#include
#include
#include
#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"
""
"Hello World "
""
"Hello, world!