Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Finding the MAC address of the sender of a multicast UDP message in Python?

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)

...

like image 516
ZebZiggle Avatar asked Oct 28 '25 18:10

ZebZiggle


2 Answers

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.

like image 64
unwind Avatar answered Oct 31 '25 08:10

unwind


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.
like image 45
Tom Avatar answered Oct 31 '25 07:10

Tom