V*N Lab: The Container Version(SEED Labs)

V*N Lab: The Container Version(SEED Labs)

本实验本身并不是最终大作业(Virtual Private Network (V*N) Lab)的必做前置内容,在完成了PKI实验与TLS实验后即可完成大作业。不过依旧按照教学大纲继续完成本项实验T_T
本次实验仅涉及V*N实现过程中管道传送的实现,而并不设计加密保护的内容。

The lab covers the following topics:
• Virtual Private Network
• The TUN/TAP virtual interface
• IP tunneling
• Routing

Task1 网络设置

本次实验的Task1~7模拟的网络环境配置如下:
V*N Lab: The Container Version(SEED Labs)_第1张图片

本来client与V*N服务器应该通过网络连接,不过为了简化实验,这里将其安置在同一局域网下(即虚机可与容器直接相连)。但即使这样,为了实验准确性,不应使主机U与主机V直接相连。

实验设置

本次实验依旧是VM20.04上推荐进行的实验,在官网上下载[LabSetup]并放入虚机,按照PKI与TLS中的方法进行运作。默认使用的是 docker-compose.yml而不是其2,2是用于task 8的双私网环境的服务器搭建。
注意运行时某个过去用过的容器可能占用了本次实验的ip网段,可以使用docker rm containerName将其删除。

包嗅探指令

本实验中包嗅探将是一个常用的调试方式,有以下几种方式可供使用:

  1. wireshark
  2. tcpdump
tcpdump -i eth0 -n

端口名应做适当调整,选择自己希望探查的端口。tcpdump在容器中运行时由于实验设置,仅能嗅探自身的网络报,在主虚机中理论上可以嗅探每一个容器及自身。wireshark同理。

测试

完成以下来证明完成了实验设置:

• Host U can communicate with VN Server.
• V
N Server can communicate with Host V.
• Host U should not be able to communicate with Host V.
• Run tcpdump on the router, and sniff the traffic on each of the network. Show that you can capture packets.

  1. 可以ping通即可
    V*N Lab: The Container Version(SEED Labs)_第2张图片
  2. 可以ping通即可
  3. 不应ping通
  4. 首先在eth0监听,听到了ICMP与ARP报文,这期间client向服务器与内网设备均发送过ping,但只有服务器回应:
    V*N Lab: The Container Version(SEED Labs)_第3张图片
    其次监听eth1,第一次ping错了对象到192.168.60.1,结果只看到了了ARP报文,之后再ping192.168.60.11才找到了ICMP报文:
    V*N Lab: The Container Version(SEED Labs)_第4张图片

至此完成了实验环境的配置与确认。

Task 2 生成&配置一个TUN接口

V*N管道基于TUN/TAP技术,TAP负责仿真一个接口设备并操作2层数据帧,TUN负责仿真一个网络层设备并操作3层IP报文。
本节实验基于以下代码进行修改:

#!/usr/bin/env python3

import fcntl
import struct
import os
import time
from scapy.all import *

TUNSETIFF = 0x400454ca
IFF_TUN   = 0x0001
IFF_TAP   = 0x0002
IFF_NO_PI = 0x1000

# Create the tun interface
tun = os.open("/dev/net/tun", os.O_RDWR)
ifr = struct.pack('16sH', b'tun%d', IFF_TUN | IFF_NO_PI)
ifname_bytes  = fcntl.ioctl(tun, TUNSETIFF, ifr)

# Get the interface name
ifname = ifname_bytes.decode('UTF-8')[:16].strip("\x00")
print("Interface Name: {}".format(ifname))

while True:
   time.sleep(10)

2.a 接口名称

在主机U上直接运行该程序,由于root与seed不是同一个用户也不是同一个用户组,所以添加权限后执行:
V*N Lab: The Container Version(SEED Labs)_第5张图片
该程序会创建一个接口tun0,但当程序结束时该接口也会被撤销,因此最后使用循环锁住该程序。我们当然可以使其隐式执行,但这里选择另开一个窗口,使用ip address查看当前各个接口状态:V*N Lab: The Container Version(SEED Labs)_第6张图片
文档还要求我们将该接口前缀改为姓,所幸py中比较好找,在一片生成接口的函数中找到并修改即可:
V*N Lab: The Container Version(SEED Labs)_第7张图片

2.b 设置TUN接口

使用以下指令进行接口的地址分配与状态修改

// Assign IP address to the interface
# ip addr add 192.168.53.99/24 dev tun0
// Bring up the interface
# ip link set dev tun0 up

同时我们也可以在py代码中添加以下,来使每次打开接口自动设置:

os.system("ip addr add 192.168.53.99/24 dev {}".format(ifname))
os.system("ip link set dev {} up".format(ifname))

在加入配置命令后再次打开,使用指令查看接口,可以发现为接口分配的新接口编号递增,在之后的实验中我们将能发现其上限是很高的。

2.c 从TUN接口中读取

使用以下代码替换源码中的while循环以实现读取:

while True:
	# Get a packet from the tun interface
	packet = os.read(tun, 2048)
	if packet:
		ip = IP(packet)
		print(ip.summary())

替换后执行,发现并没有报文被读取,这是因为接口并没有收到任意一份数据。完成以下任务:

• On Host U, ping a host in the 192.168.53.0/24 network. What are printed out by the tun.py program? What has happened? Why?
• On Host U, ping a host in the internal network 192.168.60.0/24, Does tun.py print out anything? Why?

  1. 当尝试ping192.168.53.1192.168.53.2时收到以下报文,说明系统会将该网段的报文自动发向该接口:
    V*N Lab: The Container Version(SEED Labs)_第8张图片
    值得注意的是当ping192.168.53.99即我们的管道接口时并不会在该接口收到报文,而发出ping请求的终端却能收到报文。猜测是因为系统发现是发送给自己地址,所以替换为环回地址了。使用tcpdump监听一下环回地址,果然如此:
  2. 向192.168.60.0/24下任一地址发送ping请求,在我们的隧道接口上均无反应,因为并没有从该接口发送或接收到任一数据。因为没有收到数据,所以这里不展示截图。

2.d 向TUN接口写入

实验手册给出了以下范例代码,【但实际上屁用没有,要去网上查:

# Send out a spoof packet using the tun interface
newip = IP(src='1.2.3.4', dst=ip.src)
newpkt = newip/ip.payload
os.write(tun, bytes(newpkt))

本节要求完成:

• After getting a packet from the TUN interface, if this packet is an ICMP echo request packet, construct a corresponding echo reply packet and write it to the TUN interface. Please provide evidence to show that the code works as expected.
• Instead of writing an IP packet to the interface, write some arbitrary data to the interface, and report your observation.

  1. 使用以下代码写入一个报文以模拟收到报文:

    # Receive a packet
    newip = IP(src='1.2.3.4', dst='192.168.53.99')
    newpkt = newip/ICMP()/b'123'
    os.write(tun, bytes(newpkt))
    

    由于任何ip起点与终点是本机的报文都会被内核接收(由task 4得知),不符合网段要求的报文也会被拒绝,所以这里发送与接收的报文仅用于展示代码可以生成对应报文。写入一条报文并生成对应的报文:
    V*N Lab: The Container Version(SEED Labs)_第9张图片
    部分代码:

    ......
    
    # Simulate receiving a packet
    newip = IP(src='1.2.3.4', dst='192.168.53.12')
    newpkt = newip/ICMP()/b'123'
    os.write(tun, bytes(newpkt))
    
    while True:
    	# Get a packet from the tun interface
    	packet = os.read(tun, 2048)
    	if packet:
    		ip = IP(packet)
    		print(ip.summary())
    		print(ip.payload)
    		if ICMP in ip:
    			# Send out a packet using the tun interface
    			newip = IP(src=ip.dst, dst=ip.src)
    			newpkt = newip/ip.payload
    			os.write(tun, bytes(newpkt))
    			print('Send out:')
    			print(IP(bytes(newpkt)).summary())
    
  2. 随意写点东西进去:

    os.write(tun,bytes('123123'))
    

    返回错误信息表示拒绝未经编码的数据:

    Traceback (most recent call last):
      File "./tun.py", line 31, in 
        os.write(tun,bytes('123123'))
    TypeError: string argument without an encoding
    

Task 3 通过管道向V*N服务器发送IP报文

将一个完整的IP报文放入TCP或UDP报文,并将其发送给目标,这就是通过管道发送报文。我们这一次task使用的是UDP。

服务器程序

tun_server.py始终监听9090端口并输出展示一切接收到的报文,并且拆解报文展示内部的报文。

#!/usr/bin/env python3
from scapy.all import *
IP_A = "0.0.0.0"
PORT = 9090
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind((IP_A, PORT))
while True:
	data, (ip, port) = sock.recvfrom(2048)
	print("{}:{} --> {}:{}".format(ip, port, IP_A, PORT))
	pkt = IP(data)
	print(" Inside: {} --> {}".format(pkt.src, pkt.dst))

修改客户端程序

客户端部分代码替换为:

# Create UDP socket
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

while True:
	# Get a packet from the tun interface
	packet = os.read(tun, 2048)
	if packet:
		# Send the packet via the tunnel
		sock.sendto(packet, (10.9.0.11, 9090))

最后服务器ip与端口都要修改为服务器对应的内容。

测试

分别运行上述程序,并在客户端ping192.168.53.2,则能在服务器上接收到以下信息:
V*N Lab: The Container Version(SEED Labs)_第10张图片

理由如下:客户机会自动将192.168.53.0/24转到我们之前设定的sun0接口,然而其并不是真实(虚拟)的接口,并不能真正发送报文。但是我们的程序却能读取并将其打包发送给我们设定的服务器。服务器将其解包,并读取到我们发送的报文实际上是从53.99发向53.2

但是这样并不能使我们ping到我们的Host V,所以我们应该在客户机上添加ip导向,将报文导向我们的sun0。使用以下指令添加:

# ip route add  dev  via 

同时我们也可以直接在代码中添加以下以永久执行:

# Send Host-V package to sun0
os.system("ip route add {} dev {} via 192.168.53.99".format(Host-V, ifname))

这样我们的服务器就可以接收到了。

Task 4 设置服务器

一个linux系统默认自己会是一个主机而并非一个网关,但是docker已经帮我们设置好了,因此其收到的报文如果满足转发条件则一定会转发:

sysctls:
	- net.ipv4.ip_forward=1

设置服务器以使其能够同样实现隧道,满足以下功能:

• Create a TUN interface and configure it.
• Get the data from the socket interface; treat the received data as an IP packet.
• Write the packet to the TUN interface.

修改保留的tun.py与之前的while代码为新的tun_server.py:

#!/usr/bin/env python3

import fcntl
import struct
import os
import time
from scapy.all import *

TUNSETIFF = 0x400454ca
IFF_TUN   = 0x0001
IFF_TAP   = 0x0002
IFF_NO_PI = 0x1000
Host_U    = '192.168.53.0/24'

# Create the tun interface
tun = os.open("/dev/net/tun", os.O_RDWR)
ifr = struct.pack('16sH', b'sun_s%d', IFF_TUN | IFF_NO_PI)
ifname_bytes  = fcntl.ioctl(tun, TUNSETIFF, ifr)

# Get the interface name
ifname = ifname_bytes.decode('UTF-8')[:16].strip("\x00")
print("Interface Name: {}".format(ifname))

# Assign an IP address & bring up the interface
os.system("ip addr add 192.168.78.100/24 dev {}".format(ifname))
os.system("ip link set dev {} up".format(ifname))

# Send Host-V package to sun0
os.system("ip route add {} dev {} via 192.168.78.100".format(Host_U, ifname))

# Simulate receiving a packet
#newip = IP(src='192.168.53.99', dst='192.168.53.12')
#newpkt = newip/ICMP()/b'123'
#os.write(tun, bytes(newpkt))

IP_A = "0.0.0.0"
PORT = 9090
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind((IP_A, PORT))
while True:
	data, (ip, port) = sock.recvfrom(2048)
	print("{}:{} --> {}:{}".format(ip, port, IP_A, PORT))
	packet = IP(data)
	print(" Inside: {} --> {}".format(packet.src, packet.dst))
	if packet:
		ip = packet
		if ICMP in ip:
			# Send out a packet using the tun interface
			os.write(tun, bytes(ip))
			print('Send out:')
			print(ip.summary())
			print()

通过输出我们可以看到代码正确运行发送了隧道中传送的ICMP报文:
V*N Lab: The Container Version(SEED Labs)_第11张图片
通过wireshark我们可以更清楚地看到,我们的Host-V已经做出了它的回应,但很显然客户机并没有收到,这将在接下来的task中得到完善:

Task 5 处理双向流量

使用以下代码可以实现监视多个接口,使用select.select实现通信监控,相关内容:

# We assume that sock and tun file descriptors have already been created.
while True:
	# this will block until at least one interface is ready
	ready, _, _ = select.select([sock, tun], [], [])
	for fd in ready:
		if fd is sock:
			data, (ip, port) = sock.recvfrom(2048)
			pkt = IP(data)
			print("From socket <==: {} --> {}".format(pkt.src, pkt.dst))
			... (code needs to be added by students) ...
		if fd is tun:
			packet = os.read(tun, 2048)
			pkt = IP(packet)
			print("From tun ==>: {} --> {}".format(pkt.src, pkt.dst))
			... (code needs to be added by students) ...

省流版,代码完成,客户机与服务器代码相同,仅发送的IP地址不同,连监听端口都一致(因为实际上这部分的客户机负责了本应是服务器该负责的工作,类似于当前流行的(魔fan法qiang)工具),这部分展示的客户机的部分代码是负责接收与转发的关键部分:


# Create UDP socket
IP_A = "0.0.0.0"
PORT = 9090
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind((IP_A, PORT))

while True:
	# this will block until at least one interface is ready
	ready, _, _ = select.select([sock, tun], [], [])
	for fd in ready:
		if fd is sock:
			# IP 0.0.0.0 Port 9090: Has receive a VPN package!!!
			data, (ip, port) = sock.recvfrom(2048)
			print("{}:{} --> {}:{}".format(ip, port, IP_A, PORT))
			packet = IP(data)
			print(" Inside: {} --> {}".format(packet.src, packet.dst))
			if packet:
				ip = packet
				# Send out a packet using the tun interface
				os.write(tun, bytes(ip))
				print('Send out:')
				print(ip.summary())
				print()
					
		if fd is tun:
			# Get a packet from the tun interface
			packet = os.read(tun, 2048)
			pkt = IP(packet)
			print("From tun ==>: {} --> {}".format(pkt.src, pkt.dst))
			if packet:
				# Send the packet via the tunnel
				sock.sendto(packet, ('10.9.0.11', 9090))

完成修改后,客户机Host-U已经能够正常ping到Host-V:

与此同时,也可以完成远程登录:
V*N Lab: The Container Version(SEED Labs)_第12张图片

Task 6 隧道破坏实验

在保持远程登录在线状态的同时,打破客户端或服务器的隧道服务,都出现了同样的情况,即无法在远程登录界面输入任何文字,并且不再会有新的输出。当短时间重新连回服务时,积压在TUN文件的报文缓存就会被逐个释放。如图:
V*N Lab: The Container Version(SEED Labs)_第13张图片

Task 7 在Host-V上的路由实验

本次实验简化了网络环境,在私网机上设置的除了192.168.60.0/24的流量外均发往VpN服务器。但实际情况远非如此。因此可以在私网机上进行设置,将发往所有处在隧道另一端的网段的报文全都默认导向VpN服务器,即相当于Task 3最后一部分的工作。

// Delete the default entry
# ip route del default
// Add an entry
# ip route add 192.168.53.0/24 via 192.168.60.11 # 服务器网址

在这里插入图片描述

Task 8 在两个私网间架设V*N

本节的网络拓扑结构如图所示。要求通过架设隧道以实现两端互通并证明:
V*N Lab: The Container Version(SEED Labs)_第14张图片
可以通过观察docker配置文件发现,2仅在1的基础上为所谓的客户机增加了两台子机。
通过以下指令运行容器:

$ docker-compose -f docker-compose2.yml build
$ docker-compose -f docker-compose2.yml up
$ docker-compose -f docker-compose2.yml down
  • 首先验证无法互通,这里仅展示左侧无法ping到右侧:
    V*N Lab: The Container Version(SEED Labs)_第15张图片
    然后在客户机与服务器同时打开程序(注意要修改服务器程序的发送IP地址)。此时左侧已经可以与右侧通信:
    V*N Lab: The Container Version(SEED Labs)_第16张图片

Task 9 通过TAP接口进行实验

之前的所有实验都是基于TUN技术,TAP是与TUN十分类似的技术,以至于其代码也极其相似。TUN的接口时建立在第三层网络层的,而TAP则是建立在第二层MAC层的。因此,TAP上的报文应包括MAC报文头。
我们依然使用之前的容器就可以进行实验。唯一需要修改的就是我们设置接口时设置为TAP,如图:V*N Lab: The Container Version(SEED Labs)_第17张图片
我们之前将隧道收到的报文都视为IP报文,但是改为TAP后实际收到的是MAC报文,应在原来的代码中将收到的二进制数据解析为Ether,这样就能正确解析,但仍然无法完成通信:
V*N Lab: The Container Version(SEED Labs)_第18张图片
要完成以上通信,必须要先完善ARP的通信过程。这里附上ARP协议格式:

V*N Lab: The Container Version(SEED Labs)_第19张图片

# scapy官方文档的ARP格式
0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|             HWTYPE            |             PTYPE             |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|     HWLEN     |      PLEN     |               OP              |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|             HWSRC             |              PSRC             |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|             HWDST             |              PDST             |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

具体含义参考本篇。其中,每个字段的含义如下。

  • 硬件类型:指明了发送方想知道的硬件接口类型,以太网的值为 1。
  • 协议类型:表示要映射的协议地址类型。它的值为 0x0800,表示 IP 地址。
  • 硬件地址长度和协议长度:分别指出硬件地址和协议的长度,以字节为单位。对于以- 太网上 IP 地址的ARP请求或应答来说,它们的值分别为 6 和 4。
  • 操作类型:用来表示这个报文的类型,ARP 请求为 1,ARP 响应为 2,RARP 请求为 3,RARP 响应为 4。
  • 发送方 MAC 地址:发送方设备的硬件地址。
  • 发送方 IP 地址:发送方设备的 IP 地址。
  • 目标 MAC 地址:接收方设备的硬件地址。
  • 目标 IP 地址:接收方设备的IP地址。

通过修改代码,可以实现arping

#!/usr/bin/env python3
# tap_client.py
import fcntl
import struct
import os
import time
from scapy.all import *

TUNSETIFF = 0x400454ca
IFF_TUN   = 0x0001
IFF_TAP   = 0x0002
IFF_NO_PI = 0x1000
Host_V    = '192.168.60.0/24'
LOCAL_IP  = '192.168.53.99'

# Create the tap interface
tap = os.open("/dev/net/tun", os.O_RDWR)
ifr = struct.pack('16sH', b'sun_c%d', IFF_TAP | IFF_NO_PI)
ifname_bytes  = fcntl.ioctl(tap, TUNSETIFF, ifr)

# Get the interface name
ifname = ifname_bytes.decode('UTF-8')[:16].strip("\x00")
print("Interface Name: {}".format(ifname))

# Assign an IP address & bring up the interface
os.system("ip addr add {}/24 dev {}".format(LOCAL_IP, ifname))
os.system("ip link set dev {} up".format(ifname))

# Send Host-V package to sun0
os.system("ip route add {} dev {} via {}".format(Host_V, ifname, LOCAL_IP))

# Simulate receiving a packet
#newip = IP(src='192.168.53.99', dst='192.168.53.12')
#newpkt = newip/ICMP()/b'123'
#os.write(tap, bytes(newpkt))

# Create UDP socket
IP_A = "0.0.0.0"
PORT = 9090
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind((IP_A, PORT))

while True:
	# this will block until at least one interface is ready
	ready, _, _ = select.select([sock, tap], [], [])
	for fd in ready:
		if fd is sock:
			# IP 0.0.0.0 Port 9090: Has receive a VPN package!!!
			data, (ip, port) = sock.recvfrom(2048)
			print("{}:{} --> {}:{}".format(ip, port, IP_A, PORT))
			ether = Ether(data)
			print(" Inside: {} --> {}".format(ether.src, ether.dst))
			if ether:
				if ARP in ether and ether[ARP].op == 1 :
					GOAL_MAC = getmacbyip(ether[ARP].pdst)
					arp = ether[ARP]
					newether = Ether(dst=ether.src, src=GOAL_MAC)
					newarp = ARP(psrc=arp.pdst, hwsrc=GOAL_MAC,pdst=arp.psrc, hwdst=ether.src, op=2)
					newpkt = newether/newarp
					print("***** Fake response: {}".format(newpkt.summary()))
					sock.sendto(bytes(newpkt), ('10.9.0.11', 9090))
				else:
					# Send out a packet using the tap interface
					ether
					os.write(tap, bytes(ether))
					print('Send out:', ether.summary())
					print()
					
		if fd is tap:
			# Get a packet from the tap interface
			packet = os.read(tap, 2048)
			ether = Ether(packet)
			print(ether.summary())
			print("From tap ==>: {} --> {}".format(ether.src, ether.dst))
			if ether:
				# Send the packet via the tunnel
				sock.sendto(bytes(ether), ('10.9.0.11', 9090))

注意这里的代码并不能实现ping,不知道理由
二编!!ping通了!!!(时隔7天,在yw的指导下,yw yyds!!!)
V*N Lab: The Container Version(SEED Labs)_第20张图片

终于ping通了的原因

这里VPN服务器在写入tap接口时应当把接口想象成目标接口dst而不是发送接口src,因为实际上这个接口毕竟是我们虚拟出来的,并不能将报文进行直接发送,而是要将其转入内核,让内核选择可以执行发送的接口进行发送。而为了让我们的虚拟接口tap执行转发,即使其属于我们的服务器主机,但也应将其视为目标接口,这样tap就会认为自己收到一个报文并将其转发,否则就会将其放入缓存直至被丢弃。
新的代码!!

#!/usr/bin/env python3
# tap_client.py
import fcntl
import struct
import os
import time
from scapy.all import *

TUNSETIFF = 0x400454ca
IFF_TUN   = 0x0001
IFF_TAP   = 0x0002
IFF_NO_PI = 0x1000
Host_V    = '192.168.60.0/24'
LOCAL_IP  = '192.168.53.99'
TAP_MAC = 'aa:aa:aa:aa:aa:aa'

# Create the tap interface
tap = os.open("/dev/net/tun", os.O_RDWR)
ifr = struct.pack('16sH', b'sun_c%d', IFF_TAP | IFF_NO_PI)
ifname_bytes  = fcntl.ioctl(tap, TUNSETIFF, ifr)

# Get the interface name
ifname = ifname_bytes.decode('UTF-8')[:16].strip("\x00")
print("Interface Name: {}".format(ifname))

# Assign an IP address & bring up the interface
os.system("ip addr add {}/24 dev {}".format(LOCAL_IP, ifname))
os.system("ip link set dev {} addr {} up".format(ifname, TAP_MAC))

# Send Host-V package to sun0
os.system("ip route add {} dev {} via {}".format(Host_V, ifname, LOCAL_IP))

# Create UDP socket
IP_A = "0.0.0.0"
PORT = 9090
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind((IP_A, PORT))

while True:
	# this will block until at least one interface is ready
	ready, _, _ = select.select([sock, tap], [], [])
	for fd in ready:
		if fd is sock:
			print('# IP 0.0.0.0 Port 9090: Has receive a VPN package!!!')
			data, (ip, port) = sock.recvfrom(2048)
			print("{}:{} --> {}:{}".format(ip, port, IP_A, PORT))
			ether = Ether(data)
			print(" Inside: {} --> {}".format(ether.src, ether.dst))
			if ether:
				if ARP in ether and ether[ARP].op == 1 :
					GOAL_MAC = TAP_MAC
					arp = ether[ARP]
					newether = Ether(dst=ether.src, src=GOAL_MAC)
					newarp = ARP(psrc=arp.pdst, hwsrc=GOAL_MAC,pdst=arp.psrc, hwdst=ether.src, op=2)
					newpkt = newether/newarp
					print("***** ARP fake response: {}".format(newpkt.summary()))
					sock.sendto(bytes(newpkt), ('10.9.0.11', 9090))
					print()
				else:
					# Send out a packet using the tap interface
					ether[Ether].dst = TAP_MAC
					os.write(tap, bytes(ether))
					print('Send out:', ether.summary())
					print()
					
		if fd is tap:
			# Get a packet from the tap interface
			packet = os.read(tap, 2048)
			ether = Ether(packet)
			print("From tap ==>: {} --> {}".format(ether.src, ether.dst))
			print()
			if ether:
				# Send the packet via the tunnel
				sock.sendto(bytes(ether), ('10.9.0.11', 9090))
#!/usr/bin/env python3
# tap_server.py
import fcntl
import struct
import os
import time
from scapy.all import *

TUNSETIFF = 0x400454ca
IFF_TUN   = 0x0001
IFF_TAP   = 0x0002
IFF_NO_PI = 0x1000
Host_U    = '192.168.50.0/24'
LOCAL_IP  = '192.168.78.100'
TAP_MAC = 'aa:aa:aa:aa:aa:bb'

# Create the tap interface
tap = os.open("/dev/net/tun", os.O_RDWR)
ifr = struct.pack('16sH', b'sun_s%d', IFF_TAP | IFF_NO_PI)
ifname_bytes  = fcntl.ioctl(tap, TUNSETIFF, ifr)

# Get the interface name
ifname = ifname_bytes.decode('UTF-8')[:16].strip("\x00")
print("Interface Name: {}".format(ifname))

# Assign an IP address & bring up the interface
os.system("ip addr add {}/24 dev {}".format(LOCAL_IP, ifname))
os.system("ip link set dev {} addr {} up".format(ifname, TAP_MAC))

# Send Host-V package to sun0
os.system("ip route add {} dev {} via {}".format(Host_U, ifname, LOCAL_IP))

# Create UDP socket
IP_A = "0.0.0.0"
PORT = 9090
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind((IP_A, PORT))

while True:
	# this will block until at least one interface is ready
	ready, _, _ = select.select([sock, tap], [], [])
	for fd in ready:
		if fd is sock:
			# IP 0.0.0.0 Port 9090: Has receive a VPN package!!!
			data, (ip, port) = sock.recvfrom(2048)
			print("{}:{} --> {}:{}".format(ip, port, IP_A, PORT))
			ether = Ether(data)
			print(" Inside: {} --> {}".format(ether.src, ether.dst))
			if ether:
				if ARP in ether and ether[ARP].op == 1:
					GOAL_MAC = TAP_MAC
					arp = ether[ARP]
					newether = Ether(dst=ether.src, src = GOAL_MAC)
					newarp = ARP(psrc=arp.pdst, hwsrc=GOAL_MAC,pdst=arp.psrc, hwdst=ether.src, op=2)
					newpkt = newether/newarp
					print("***** GOAL response: {}".format(newpkt.summary()))
					sock.sendto(bytes(newpkt), ('10.9.0.12', 9090))
					print()
				else:
					# Send out a packet using the tap interface
					ether[Ether].dst = TAP_MAC
					os.write(tap, bytes(ether))
					print('Send out:', ether.summary())
					print()
					
		if fd is tap:
			# Get a packet from the tap interface
			packet = os.read(tap, 2048)
			ether = Ether(packet)
			print("From tap ==>: {} --> {}".format(ether.src, ether.dst))
			print()
			if ether:
				# Send the packet via the tunnel
				sock.sendto(bytes(ether), ('10.9.0.12', 9090))

还有ping通之后的截图!!!!!!!!!!!!!
V*N Lab: The Container Version(SEED Labs)_第21张图片
但是由于VPN的特殊性,我们的两个tap接口并不能进行ping,只有两边的HOST_U和HOST_V可以互相ping

总结

没啥好总结的,task9让我心力交瘁。
二编:
yw yyds!!!

你可能感兴趣的:(网安,docker,网络,运维)