Python 以太网接口的原始套接字 (Windows)
Python Raw Socket to Ethernet Interface (Windows)
我正在尝试创建一个 DHCP 服务器,第一步是通过我的以太网端口发送数据包。我正在尝试将数据包发送到我的以太网接口并弹出错误。
代码如下
import socket
def sendeth(src, dst, eth_type, payload, interface = "eth0"):
"""Send raw Ethernet packet on interface."""
assert(len(src) == len(dst) == 6) # 48-bit ethernet addresses
assert(len(eth_type) == 2) # 16-bit ethernet type
#s = socket.socket(socket.AF_PACKET, socket.SOCK_RAW)
s = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_RAW)
# From the docs: "For raw packet
# sockets the address is a tuple (ifname, proto [,pkttype [,hatype]])"
s.bind((interface, 0))
return s.send(src + dst + eth_type + payload)
if __name__ == "__main__":
print("Sent %d-byte Ethernet packet on eth0" %
sendeth("\xFE\xED\xFA\xCE\xBE\xEF",
"\xFE\xED\xFA\xCE\xBE\xEF",
"\x7A\x05",
"hello"))
我对创建套接字的方式有疑问。 AF_PACKET 未被识别,因此我假设它仅适用于 Linux。我将其注释掉并在其下方添加了一个新行。我再次 运行 它并开始收到如下所示的错误。
Traceback (most recent call last):
File "eth.py", line 27, in <module>
"hello"))
File "eth.py", line 19, in sendeth
s.bind((interface, 0))
File "C:\Python27\lib\socket.py", line 224, in meth
return getattr(self._sock,name)(*args)
socket.gaierror: [Errno 11001] getaddrinfo failed
有人知道为什么会这样吗?
您似乎无法使用此套接字访问以太网:
s = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_RAW)
socket.IPPROTO_RAW
允许您访问第 3 层协议 (IP),而以太网位于第 1 层和第 2 层。在第 3 层,以太网帧已经被分析并且其 headers 被丢弃。您需要达到 2 级,ETH_P_ALL
协议似乎是一个不错的起点。我不相信 python socket
模块会在那么低的级别上实现它,但您可以通过 ctypes
模块与 WinAPI 交互。
文档中的这个示例似乎很有启发性。
https://docs.python.org/2/library/socket.html
import socket
# the public network interface
HOST = socket.gethostbyname(socket.gethostname())
# create a raw socket and bind it to the public interface
s = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_IP)
s.bind((HOST, 0))
# Include IP headers
s.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1)
# receive all packages
s.ioctl(socket.SIO_RCVALL, socket.RCVALL_ON)
# receive a package
print s.recvfrom(65565)
# disabled promiscuous mode
s.ioctl(socket.SIO_RCVALL, socket.RCVALL_OFF)
我认为关键是socket.gethostbyname(socket.gethostname())。 "eth0" Windows.
不支持您示例中使用的 "eth0"
DHCP 是一种 UDP 协议。您不需要原始套接字来实现 DHCP 服务器。
使用 AF_INET/SOCK_DGRAM 套接字,并绑定到地址 255.255.255.255 以实现您的服务器。
从另一个方向探讨您的问题:您为什么需要使用以太网? DHCP通常通过UDP实现。
- DHCP 服务器总是有一些 IP 地址(以及它可以租用的地址池)。
- 如果客户端需要 IP(DHCP 实际上可以做的不仅仅是分配 IP,但让我们暂时停止这个用例),它会发送源 IP 0.0.0.0 和目标 IP 255.255.255.255 的广播 DHCPREQUEST .服务器也通过 UDP 广播回答他,但使用他自己的源 IP。
如果您想创建 DHCP 的实现,从 OSI 级别 2(以太网)开始只会让您头疼维护级别 3(IP)和 4(UDP)。我没有看到任何好处。
如果您想创建一个基于以太网的类似 DHCP 的协议,请准备好解决以下问题:路由器不会转发广播数据包,除非被要求这样做。例如,对于 Cisco 路由器,它看起来像这样:
router(config)# interface ethernet 0/0
router(config-if)# ip helper-address 10.1.23.5
router(config-if)# end
router#
因此我们配置路由器,使其知道有一些有用的东西连接到需要广播的 IP 10.1.23.5 的以太网 0/0 端口 (source)。
如前所述,由于 Win32 限制,ETH_P_ALL
未在 Windows 上实现。
另一种方法叫做 Winpcap(最近是 Npcap),它设置 Windows 来访问这样的 low-level 东西(它添加一个额外的 driver)
然后您可以做的是使用基于 Winpcap/Npcap 的 库(例如 Scapy)来访问原始 low-level 套接字。这需要在电脑上安装Npcap(或Winpcap)。
然后你可以使用库as-is(它有很多处理数据包的能力),或者如果你想访问原始数据
from scapy.all import *
IFACES.show() # let’s see what interfaces are available. Windows only
iface = <<"full iface name">> or <<IFACES.dev_from_index(12)>> or <<IFACES.dev_from_pcapname(r"\Device_stuff")>>
socket = conf.L2socket(iface=iface)
# socket is now an Ethernet socket
### RECV
packet_raw = socket.recv_raw()[0] # Raw data
packet_decoded = socket.recv() # Using the library (also contains things like sent time...)
### SEND
socket.send(b"\x00......"). # send raw data
socket.send(Ether()/IP(dst="www.google.com")/TCP()/Raw(load=b"data")) # use library
我正在尝试创建一个 DHCP 服务器,第一步是通过我的以太网端口发送数据包。我正在尝试将数据包发送到我的以太网接口并弹出错误。
代码如下
import socket
def sendeth(src, dst, eth_type, payload, interface = "eth0"):
"""Send raw Ethernet packet on interface."""
assert(len(src) == len(dst) == 6) # 48-bit ethernet addresses
assert(len(eth_type) == 2) # 16-bit ethernet type
#s = socket.socket(socket.AF_PACKET, socket.SOCK_RAW)
s = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_RAW)
# From the docs: "For raw packet
# sockets the address is a tuple (ifname, proto [,pkttype [,hatype]])"
s.bind((interface, 0))
return s.send(src + dst + eth_type + payload)
if __name__ == "__main__":
print("Sent %d-byte Ethernet packet on eth0" %
sendeth("\xFE\xED\xFA\xCE\xBE\xEF",
"\xFE\xED\xFA\xCE\xBE\xEF",
"\x7A\x05",
"hello"))
我对创建套接字的方式有疑问。 AF_PACKET 未被识别,因此我假设它仅适用于 Linux。我将其注释掉并在其下方添加了一个新行。我再次 运行 它并开始收到如下所示的错误。
Traceback (most recent call last):
File "eth.py", line 27, in <module>
"hello"))
File "eth.py", line 19, in sendeth
s.bind((interface, 0))
File "C:\Python27\lib\socket.py", line 224, in meth
return getattr(self._sock,name)(*args)
socket.gaierror: [Errno 11001] getaddrinfo failed
有人知道为什么会这样吗?
您似乎无法使用此套接字访问以太网:
s = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_RAW)
socket.IPPROTO_RAW
允许您访问第 3 层协议 (IP),而以太网位于第 1 层和第 2 层。在第 3 层,以太网帧已经被分析并且其 headers 被丢弃。您需要达到 2 级,ETH_P_ALL
协议似乎是一个不错的起点。我不相信 python socket
模块会在那么低的级别上实现它,但您可以通过 ctypes
模块与 WinAPI 交互。
文档中的这个示例似乎很有启发性。 https://docs.python.org/2/library/socket.html
import socket
# the public network interface
HOST = socket.gethostbyname(socket.gethostname())
# create a raw socket and bind it to the public interface
s = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_IP)
s.bind((HOST, 0))
# Include IP headers
s.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1)
# receive all packages
s.ioctl(socket.SIO_RCVALL, socket.RCVALL_ON)
# receive a package
print s.recvfrom(65565)
# disabled promiscuous mode
s.ioctl(socket.SIO_RCVALL, socket.RCVALL_OFF)
我认为关键是socket.gethostbyname(socket.gethostname())。 "eth0" Windows.
不支持您示例中使用的 "eth0"DHCP 是一种 UDP 协议。您不需要原始套接字来实现 DHCP 服务器。
使用 AF_INET/SOCK_DGRAM 套接字,并绑定到地址 255.255.255.255 以实现您的服务器。
从另一个方向探讨您的问题:您为什么需要使用以太网? DHCP通常通过UDP实现。
- DHCP 服务器总是有一些 IP 地址(以及它可以租用的地址池)。
- 如果客户端需要 IP(DHCP 实际上可以做的不仅仅是分配 IP,但让我们暂时停止这个用例),它会发送源 IP 0.0.0.0 和目标 IP 255.255.255.255 的广播 DHCPREQUEST .服务器也通过 UDP 广播回答他,但使用他自己的源 IP。
如果您想创建 DHCP 的实现,从 OSI 级别 2(以太网)开始只会让您头疼维护级别 3(IP)和 4(UDP)。我没有看到任何好处。
如果您想创建一个基于以太网的类似 DHCP 的协议,请准备好解决以下问题:路由器不会转发广播数据包,除非被要求这样做。例如,对于 Cisco 路由器,它看起来像这样:
router(config)# interface ethernet 0/0
router(config-if)# ip helper-address 10.1.23.5
router(config-if)# end
router#
因此我们配置路由器,使其知道有一些有用的东西连接到需要广播的 IP 10.1.23.5 的以太网 0/0 端口 (source)。
如前所述,由于 Win32 限制,ETH_P_ALL
未在 Windows 上实现。
另一种方法叫做 Winpcap(最近是 Npcap),它设置 Windows 来访问这样的 low-level 东西(它添加一个额外的 driver)
然后您可以做的是使用基于 Winpcap/Npcap 的 库(例如 Scapy)来访问原始 low-level 套接字。这需要在电脑上安装Npcap(或Winpcap)。
然后你可以使用库as-is(它有很多处理数据包的能力),或者如果你想访问原始数据
from scapy.all import *
IFACES.show() # let’s see what interfaces are available. Windows only
iface = <<"full iface name">> or <<IFACES.dev_from_index(12)>> or <<IFACES.dev_from_pcapname(r"\Device_stuff")>>
socket = conf.L2socket(iface=iface)
# socket is now an Ethernet socket
### RECV
packet_raw = socket.recv_raw()[0] # Raw data
packet_decoded = socket.recv() # Using the library (also contains things like sent time...)
### SEND
socket.send(b"\x00......"). # send raw data
socket.send(Ether()/IP(dst="www.google.com")/TCP()/Raw(load=b"data")) # use library