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 socke开发者_JAVA技巧ts 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)
...
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.
The protocol you need is ARP. Check this question/answer for details
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.
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 theCAP_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.
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
精彩评论