【问题标题】:Python Raw Socket to Ethernet Interface (Windows)Python 原始套接字到以太网接口 (Windows)
【发布时间】:2017-02-16 21:14:39
【问题描述】:

我正在尝试创建 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

有人知道为什么会这样吗?

【问题讨论】:

  • 你在为接口传递什么? “eth0”在 Windows 上不起作用。
  • 确实,根据我对 Windows 套接字的经验,接口名称通常是难以理解的。甚至还有一个第三方模块来处理它们

标签: python windows python-2.7 sockets dhcp


【解决方案1】:

如上所述,ETH_P_ALL未在 Windows 上实现,因为 Win32 的限制。 另一种方法称为 Winpcap(最近是 Npcap),它设置 Windows 来访问这些低级的东西(它添加了一个额外的驱动程序)

然后您可以做的是使用 基于 Winpcap/Npcap 的库(例如 Scapy)来访问原始低级套接字。这需要在计算机上安装 Npcap(或 Winpcap)。

然后您可以按原样使用该库(它有很多处理数据包的能力),或者如果您想访问原始数据

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

【讨论】:

    【解决方案2】:

    从另一个方向解决您的问题:您为什么需要使用以太网? DHCP 通常通过 UDP 实现。

    1. DHCP 服务器总是有一些 IP 地址(以及它可以租用的地址池)。
    2. 如果客户端需要 IP(DHCP 实际上可以做的不仅仅是分配 IP,但现在让我们停止这个用例),它会发送一个带有源 IP 0.0.0.0 和目标 IP 255.255.255.255 的广播 DHCPREQUEST。服务器也通过 UDP 广播回复他,但使用他自己的源 IP。

    如果您想创建 DHCP 的实现,从 OSI Level 2(以太网)开始只会让您头疼维护 Level 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)。

    【讨论】:

      【解决方案3】:

      DHCP 是一种 UDP 协议。您不需要原始套接字来实现 DHCP 服务器。

      使用 AF_INET/SOCK_DGRAM 套接字,并绑定到地址 255.255.255.255 以实现您的服务器。

      【讨论】:

      • 谢谢。这让我的生活轻松了很多。在绑定到 255.255.255.255 的套接字上接收到消息后,我必须处理消息并将信息发回给客户端以设置其 IP。稍微超出了这个线程的范围,你知道描述这种交互的地方吗?还是我必须深入研究 RFC2131 文档?
      【解决方案4】:

      文档中的这个例子似乎很有启发性。 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())。 Windows 不支持您的示例中使用的“eth0”。

      【讨论】:

      • 如果我有多个接口会怎样?
      • hmmm... gethostbyname 返回一个 IP 地址。也许尝试与分配给您想要的物理接口的 IP 地址绑定?
      【解决方案5】:

      您似乎无法使用此套接字访问以太网:

      s = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_RAW)
      

      socket.IPPROTO_RAW 让您可以访问第 3 级协议 (IP),而以太网位于第 1 级和第 2 级。在第 3 级,已经分析了以太网帧并丢弃了其标头。您需要达到第 2 级,ETH_P_ALL 协议似乎是一个不错的起点。我不相信 python socket 模块在那个低级别上实现它,但是您可以通过 ctypes 模块与 WinAPI 进行交互。

      【讨论】:

      • 当我尝试使用 ETH_P_ALL 时出现错误:AttributeError: 'module' object has no attribute 'ETH_P_ALL'
      • @Sam 是的,socket 中没有实现 ETH_P_ALL。您必须直接使用 WinAPI。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-02-21
      • 1970-01-01
      • 1970-01-01
      • 2018-12-13
      • 2014-11-30
      • 1970-01-01
      相关资源
      最近更新 更多