在Python中查找多播UDP消息发送者的MAC地址?

发布于 2024-08-11 10:28:34 字数 887 浏览 8 评论 0原文

我有一些代码通过 UDP 多播监听“公告”。我可以获得发件人的 IP 地址,但我真正需要的是发件人的 MAC 地址(因为 IP 地址可能并且将会改变)。

有没有一种简单的方法可以在Python中做到这一点?

包含一个代码片段供参考,但可能没有必要。

sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)

# Allow multiple sockets to use the same PORT number
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

# Bind to the port that we know will receive multicast data
sock.bind((self.interface, MCAST_PORT))

# Tell API we are a multicast socket
sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 255)

# Tell API we want to add ourselves to a multicast group
# The address for the multicast group is the third param

status = sock.setsockopt(socket.IPPROTO_IP, 
          socket.IP_ADD_MEMBERSHIP, 
          socket.inet_aton(MCAST_ADDR) + socket.inet_aton(self.interface));

data, addr = sock.recvfrom(1024)

...

I have some code that listens for "announcements" via UDP multicast. I can get the IP address of the sender, but what I really need is the MAC address of the sender (since the IP address can and will change).

Is there an easy way to do this in Python?

A code snippet is included for reference, but likely unnecessary.

sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)

# Allow multiple sockets to use the same PORT number
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

# Bind to the port that we know will receive multicast data
sock.bind((self.interface, MCAST_PORT))

# Tell API we are a multicast socket
sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 255)

# Tell API we want to add ourselves to a multicast group
# The address for the multicast group is the third param

status = sock.setsockopt(socket.IPPROTO_IP, 
          socket.IP_ADD_MEMBERSHIP, 
          socket.inet_aton(MCAST_ADDR) + socket.inet_aton(self.interface));

data, addr = sock.recvfrom(1024)

...

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

合久必婚 2024-08-18 10:28:34

一般来说,您无法获取 MAC 地址。在 LAN 上使用 ARP 可能会成功,但在 Internet 上则不可能。

考虑这样一种情况:您收到的数据包具有发送者的 NATting 路由器的 IP 地址。数据包沿途可能经过任意数量的中间机器,每个中间机器也有 MAC 地址。支持您所追求的查找类型应该由谁负责?对于沿途的所有机器,发送者的 MAC 地址完全没有用,那么为什么还要支持这种查找呢?

而且,顺便说一句,更改 mac 地址在许多网卡上都很简单,因此将其用作某些网卡那种唯一的ID并不是一个明智的想法。

You cannot, in general, get the mac address. You might succeed using ARP on a LAN, but across the Internet it's not possible.

Consider the case where the packet you receive has the IP address of the sender's NATting router. The packet may have traversed any number of intermediate machines along the way, each of which have mac addresses, too. Whose responsibility should it be to support the kind of lookup you're after? For all the machines along the way, the sender's mac address is completely useless, so why bother supporting that kind of lookup?

And, btw, changing the mac address is trivial on many network cards, so using it as some kind of unique ID is not a wise idea.

快乐很简单 2024-08-18 10:28:34

为此,您需要捕获原始以太网帧,而不仅仅是 UDP 数据包。

import socket

ETH_P_ALL=3
sock = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.htons(ETH_P_ALL))
sock.bind((interface_name, 0))
data = sock.recv(2000)
dmac = data[:6]
smac = data[6:12]
udp_offset = 14
ethertype = data[12:14]
if ethertype == [0x81, 0x00]: # 802.1Q VLAN-tagged
    udp_offset += 4
udp_pkt = data[udp_offset:]

一些注意事项:

  • Ethertype 值 0x88a8 表示 802.1ad QiQ 或“堆叠 VLAN”,需要进行适当处理才能正确查找 UDP 数据。
  • 在大多数 Linux 系统上,您需要成为 root 用户(或具有 CAP_NET_RAW 功能)才能执行此操作。不确定您在 Windows 上需要什么,但假设有类似的情况。
  • 实际上,这将是一个消防水带,因为它将接收所有 UDP 数据包。您可以自己解析 UDP 标头以将其范围缩小到您感兴趣的范围,或者(在 Linux / BSD 等上)研究 Berkeley 数据包过滤器以让内核为您完成此操作。后者的 CPU 效率更高,但实现起来相当困难。
  • 建议 ARP 或类似内容的答案可能会满足您的要求,但也可能不会。他们会告诉您操作系统在 IP 地址和 MAC 之间有什么关联;这对于第 2 层协议、多播、广播等或响应 ARP 请求的某些内容没有帮助。

To do this, you need to capture the raw Ethernet frame, not just the UDP packet.

import socket

ETH_P_ALL=3
sock = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.htons(ETH_P_ALL))
sock.bind((interface_name, 0))
data = sock.recv(2000)
dmac = data[:6]
smac = data[6:12]
udp_offset = 14
ethertype = data[12:14]
if ethertype == [0x81, 0x00]: # 802.1Q VLAN-tagged
    udp_offset += 4
udp_pkt = data[udp_offset:]

Some notes:

  • An Ethertype value of 0x88a8 would indicate 802.1ad QinQ or "stacked VLAN" and would need to be handled appropriately to correctly find the UDP data.
  • On most Linux systems you need to be root (or have the CAP_NET_RAW capability) to do this. Not sure what you need on Windows, but assume something similar.
  • In practice this will be something of a firehose as it will receive all UDP packets. You can either parse the UDP header yourself to narrow it down to the ones you are interested in, or (on Linux / BSD etc) investigate Berkeley Packet Filter to get the kernel to do it for you. The latter is much more CPU efficient but is rather a pain to implement.
  • Answers suggesting ARP or similar might do what you want but they might not. They will tell you what association your OS has between IP addresses and MACs; this won't help for layer 2 protocols, multicast, broadcast etc or if something is lying in response to ARP requests.
毁梦 2024-08-18 10:28:34

您需要的协议是 ARP。查看此问题/答案了解详细信息

The protocol you need is ARP. Check this question/answer for details

独行侠 2024-08-18 10:28:34

我不确定是否可以获取发送者的 MAC 地址,因为 MAC 地址是链路级地址,而不是像 IP 那样的网络级地址。当包含 UDP 消息的数据包从发送方路由到接收方时,MAC 地址将在网络中的每一跳发生变化。

I'm not sure that it is possible to get the sender's MAC address since the MAC address is a link level address and not a network level address like IP. The MAC address will change at each hop in the network as the packet containing the UDP message is routed from the sender to the receiver.

卷耳 2024-08-18 10:28:34

我不知道如何在 python 中执行此操作,但可以获取 MAC 地址。例如,通过使用 tcpdump 我将所有数据包放入文件中:

sudo tcpdump -i enp0s31f6 -w file_name port 6665

然后在 python 中读取它:

       packetlist = rdpcap("./file_name")
       for pkt in packetlist:  
       print pkt.src, pkt.load

您可以看到 mac 地址

编辑:
我找到了一种方法来做到这一点:
借助 sniff 函数,使用 scapy 嗅探所有包,然后过滤包以仅获取您需要的包。在那里你可以使用mac地址
例如我的项目:

sniff(prn=self._pkt_callback, store=0)

def _pkt_callback(self, pkt):
    if not self.sniffer_on:
         return
    if Ether not in pkt or pkt[Ether].type != 0x800:
        return
    if pkt[IP].proto != 17: # 17 means UDP package
        return
    if pkt[UDP].dport != 6665:
        return

    print pkt.src, pkt.load    #src is mac address

I don't know how to do it in python but it is possible to get MAC address. For example by using tcpdump I put all packets into file:

sudo tcpdump -i enp0s31f6 -w file_name port 6665

then in python read it with:

       packetlist = rdpcap("./file_name")
       for pkt in packetlist:  
       print pkt.src, pkt.load

you can see the mac address

edit:
I found one way to do this:
sniff all packages with scapy with the help of function sniff, then filter the packages to get only what you need. There you can use mac address
for example from my project:

sniff(prn=self._pkt_callback, store=0)

def _pkt_callback(self, pkt):
    if not self.sniffer_on:
         return
    if Ether not in pkt or pkt[Ether].type != 0x800:
        return
    if pkt[IP].proto != 17: # 17 means UDP package
        return
    if pkt[UDP].dport != 6665:
        return

    print pkt.src, pkt.load    #src is mac address
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文