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实现。

  1. DHCP 服务器总是有一些 IP 地址(以及它可以租用的地址池)。
  2. 如果客户端需要 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