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模拟的网络环境配置如下:
在这里插入图片描述

本来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通即可
    在这里插入图片描述
  2. 可以ping通即可
    在这里插入图片描述
  3. 不应ping通
    在这里插入图片描述
  4. 首先在eth0监听,听到了ICMP与ARP报文,这期间client向服务器与内网设备均发送过ping,但只有服务器回应:
    在这里插入图片描述
    其次监听eth1,第一次ping错了对象到192.168.60.1,结果只看到了了ARP报文,之后再ping192.168.60.11才找到了ICMP报文:
    在这里插入图片描述

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

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不是同一个用户也不是同一个用户组,所以添加权限后执行:
在这里插入图片描述
该程序会创建一个接口tun0,但当程序结束时该接口也会被撤销,因此最后使用循环锁住该程序。我们当然可以使其隐式执行,但这里选择另开一个窗口,使用ip address查看当前各个接口状态:在这里插入图片描述
文档还要求我们将该接口前缀改为姓,所幸py中比较好找,在一片生成接口的函数中找到并修改即可:
在这里插入图片描述

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时收到以下报文,说明系统会将该网段的报文自动发向该接口:
    在这里插入图片描述
    值得注意的是当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得知),不符合网段要求的报文也会被拒绝,所以这里发送与接收的报文仅用于展示代码可以生成对应报文。写入一条报文并生成对应的报文:
    在这里插入图片描述
    部分代码:

    ......
    
    # 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 <module>
        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,则能在服务器上接收到以下信息:
在这里插入图片描述

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

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

# ip route add <network> dev <interface> via <router ip>

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

# 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报文:
在这里插入图片描述
通过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:
在这里插入图片描述
与此同时,也可以完成远程登录:
在这里插入图片描述

Task 6 隧道破坏实验

在保持远程登录在线状态的同时,打破客户端或服务器的隧道服务,都出现了同样的情况,即无法在远程登录界面输入任何文字,并且不再会有新的输出。当短时间重新连回服务时,积压在TUN文件的报文缓存就会被逐个释放。如图:
在这里插入图片描述

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

本节的网络拓扑结构如图所示。要求通过架设隧道以实现两端互通并证明:
在这里插入图片描述
可以通过观察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到右侧:
    在这里插入图片描述
    然后在客户机与服务器同时打开程序(注意要修改服务器程序的发送IP地址)。此时左侧已经可以与右侧通信:
    在这里插入图片描述

Task 9 通过TAP接口进行实验

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

在这里插入图片描述

# 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!!!)
在这里插入图片描述

终于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通之后的截图!!!!!!!!!!!!!
在这里插入图片描述
但是由于VPN的特殊性,我们的两个tap接口并不能进行ping,只有两边的HOST_U和HOST_V可以互相ping

总结

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

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐