5

I'm trying to make ARP request on python. My code:

import socket
from struct import pack
from uuid import getnode as get_mac

def main():
    dest_ip = [10, 7, 31, 99]
    local_mac = [int(("%x" % get_mac())[i:i+2], 16) for i in range(0, 12, 2)]
    local_ip = [int(x) for x in socket.gethostbyname(socket.gethostname()).split('.')]

    sock = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.SOCK_RAW)
    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
    sock.bind(('', 0))

    ARP_FRAME = [
        pack('!H', 0x0001), # HRD
        pack('!H', 0x0800), # PRO
        pack('!B', 0x06), # HLN
        pack('!B', 0x04), # PLN 
        pack('!H', 0x0001), # OP
        pack('!6B', *local_mac), # SHA
        pack('!4B', *local_ip), # SPA
        pack('!6B', *(0x00,)*6), # THA
        pack('!4B', *dest_ip), # TPA
    ]
    print(ARP_FRAME)
    sock.sendto(b''.join(ARP_FRAME), ('255.255.255.255', 0))
    sock.close()

if __name__ == "__main__":
    main()

When I execute this code, Wireshark does not catch any packets. I think problem in socket. When I do socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.SOCK_RAW) I get AttributeError: module 'socket' has no attribute 'AF_PACKET'. What am I doing wrong and how fix it?

OS X 10.11.3

0x1337
  • 1,074
  • 1
  • 14
  • 33
  • Out of curiosity, is there any reason you're not using scapy? – Goodies Feb 11 '16 at 19:17
  • The layer-2 frame is a standard ethernet frame with a broadcast destination MAC address (FFFF.FFFF.FFFF) and the ARP ethertype (0x0806). The layer-3 packet with be an ARP request. See http://www.tcpipguide.com/free/t_ARPMessageFormat.htm for the ARP packet format. – Ron Maupin Feb 11 '16 at 19:21
  • @Goodies This is home assignment for "Computer Networks" course. – 0x1337 Feb 11 '16 at 19:22
  • If you take a look at an ARP 'packet,' you'll see that it doesn't have a destination IP address because it is a Layer 2 Frame, not a packet. Ex: https://i.gyazo.com/e33183815d544a7eadb86da3e14373a4.png Your packets are being sent, but are NOT formatted correctly. https://i.gyazo.com/8b03adeb2db7fed565ca8f9b945cedcb.png – Goodies Feb 11 '16 at 19:31
  • @Goodies I change packet structure according to http://www.tcpipguide.com/free/t_ARPMessageFormat.htm. Here new structure https://gist.github.com/yutkin/1781aa574126ddb2ff65, but it does not work. – 0x1337 Feb 11 '16 at 20:02
  • Why are you sending anything to `255.255.255.255`? That makes no sense since this is not an IP packet. The frame is sent to the broadcast MAC address, and the ARP (not IP) packet contains the destination IP address to be resolved. – Ron Maupin Feb 11 '16 at 20:14
  • @RonMaupin I should set some address for `sendto` method. Is there another ways for sending? – 0x1337 Feb 11 '16 at 20:22
  • I will explain the way it works, and let you decide how to implement it since it is your homework. You need to create an ARP packet (not an IP packet) for the ARP request which includes the source and destination IP addresses, and the source MAC address. You need to encapsulate the packet in an ethernet frame which has the destination MAC address as the broadcast (FFFF.FFFF.FFFF) address, the source MAC address, and the ethertype set to ARP (0x0806). The frame then gets serialized and put on the wire. You should refer to [RFC 826](https://tools.ietf.org/html/rfc826) which has the algorithms. – Ron Maupin Feb 11 '16 at 21:07
  • @RonMaupin I understand about arp frame! I can't understand how to send it in Python! I can't use raw socket on OS X. – 0x1337 Feb 11 '16 at 21:39

2 Answers2

7

socket.AF_INET is a socket for IP packets. Since ARP is Ethernet, it won't work this way, you need access to the Ethernet layer.

On Linux, you can use:

s = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.htons(3))

with s being any variable you use to assign the socket to.

To send the packet, you should bind the interface with

s.bind(("eth0", 0))

Where "eth0" is the network-interface name.

After that, you can send the packet with

s.send(packet)

with packet being your payload as bytestring.

The

AttributeError: module 'socket' has no attribute 'AF_PACKET'

is saying, that AF_PACKET is not implemented on your platform. Unfortunately Some platforms don't provide access to the low-level network layer, this is true for windows, and may be true for OS X. At least, even on linux, you need root privileges to access the low-level networking, otherwise you'll get an access denied error.

An Idea to get access to the low-level networking via Python is to use a Virtual Machine, like Virtualbox, and run a linux guest with it.

L.S.
  • 476
  • 4
  • 10
  • 1
    Confirming "AttributeError: module 'socket' has no attribute 'AF_PACKET'" on MacOS. Actually AF_PACKET is Linux specific, on BSD-like systems it works a bit different: https://stackoverflow.com/questions/17169298/af-packet-on-osx – Mikhail Zakharov Aug 13 '18 at 14:42
2

FTR, to work around this issue you can use Scapy. It will use:

  • AF_PACKET or libpcap on unix
  • BPF on BSD
  • Winpcap/Npcap on Windows

to access the raw layer in each case, which makes it cross platform.

Cukic0d
  • 5,111
  • 2
  • 19
  • 48