L2 socket

L2 socket即链路层socket,用来手动构造frame。我们可以通过这个实验来理解以太网的数据结构。

首先用VMware构造一个虚拟的网络,将3台主机用LINK连接起来。

在其中的主机h1上执行ifconfig找到我们的虚拟链接:

engineer@netlab1:~$ ifconfigens33: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 inet 192.168.40.131 netmask 255.255.255.0 broadcast 192.168.40.255 inet6 fe80::8156:8ce5:6bbf:4579 prefixlen 64 scopeid 0x20<link> ether 00:0c:29:79:b4:8a txqueuelen 1000 (Ethernet) RX packets 4563 bytes 5822993 (5.8 MB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 1279 bytes 153480 (153.4 KB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
ens37: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 ether 00:0c:29:79:b4:94 txqueuelen 1000 (Ethernet) RX packets 209 bytes 41829 (41.8 KB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 261 bytes 41788 (41.7 KB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536 inet 127.0.0.1 netmask 255.0.0.0 inet6 ::1 prefixlen 128 scopeid 0x10<host> loop txqueuelen 1000 (Local Loopback) RX packets 514 bytes 52844 (52.8 KB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 514 bytes 52844 (52.8 KB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0

找到对应的MAC地址00:0c:29:79:b4:94

主机1上运行此代码来构造帧:

import socketimport sys
s = socket.socket(socket.AF_PACKET, socket.SOCK_RAW)s.bind(('ens37', 0))data_size = int(sys.argv[1])s.send(b'\x31' * data_size)

通过命令行指定传送的数据大小(字节数)。

engineer@netlab1:~$ sudo python3 l2socket.py 13Traceback (most recent call last): File "/home/engineer/l2socket.py", line 7, in <module> s.send(b'\x31' * data_size)OSError: [Errno 22] Invalid argument
engineer@netlab1:~$ sudo python3 l2socket.py 1515Traceback (most recent call last): File "/home/engineer/l2socket.py", line 7, in <module> s.send(b'\x31' * data_size)OSError: [Errno 90] Message too long

我们不难看出,合法的长度是14到1514个字节。即需要至少提供6字节的目标MAC,6字节的源MAC,2字节的类型,以及0-1500个字节的数据。


主机2上运行代码来监听:

import socket
s = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.htons(3))s.bind(('ens37', 0))while True: data = s.recv(4096) print(f'get [{len(data)} bytes]: {data}')
注意要先打开对应网卡的混杂模式,否则我们构造的帧其实是不合格的,会被过滤掉。engineer@netlab2:~$ sudo ifconfig ens37 promiscengineer@netlab2:~/Desktop$ sudo python3 l2socket_listen.py

现在我们在主机1上运行代码,试图发送一个长度14字节的帧(不包含帧尾的4字节CRC)。

engineer@netlab1:~$ sudo python3 l2socket.py 14

来看看主机2上面能收到什么内容:

engineer@netlab2:~/Desktop$ sudo python3 l2socket_listen.pyget [60 bytes]: b'11111111111111\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'

收到了60个字节的内容!前面14个字节是我们提供的,后面补全的部分是帧的填充内容,以确保帧的数据部分至少有46字节。这样加上14个字节的头部,一共60字节。

如果我们发送1514个字节的帧呢:

engineer@netlab1:~$ sudo python3 l2socket.py 1514

主机2会收到1514个字节:14字节头+1500字节数据

get [1514 bytes]: b'11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111'

通过wireshark也可以看到相同的结论。