Skip to content

Instantly share code, notes, and snippets.

@cslarsen
Created April 27, 2014 07:14
Show Gist options
  • Star 31 You must be signed in to star a gist
  • Fork 9 You must be signed in to fork a gist
  • Save cslarsen/11339448 to your computer and use it in GitHub Desktop.
Save cslarsen/11339448 to your computer and use it in GitHub Desktop.
One way of sending raw Ethernet packets in Python
"""Demonstrates how to construct and send raw Ethernet packets on the
network.
You probably need root privs to be able to bind to the network interface,
e.g.:
$ sudo python sendeth.py
"""
from socket import *
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(AF_PACKET, SOCK_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"))
@cslarsen
Copy link
Author

... and here is a full example IPv4 + ICMP PING REQ payload

"""Demonstrates how to construct and send raw Ethernet packets on the
network.

You probably need root privs to be able to bind to the network interface,
e.g.:

    $ sudo python sendeth.py
"""

from socket import *

def sendeth(ethernet_packet, payload, interface = "eth0"):
  """Send raw Ethernet packet on interface."""
  s = socket(AF_PACKET, SOCK_RAW)

  # From the docs: "For raw packet
  # sockets the address is a tuple (ifname, proto [,pkttype [,hatype]])"
  s.bind((interface, 0))
  return s.send(ethernet_packet + payload)

def pack(byte_sequence):
  """Convert list of bytes to byte string."""
  return b"".join(map(chr, byte_sequence))

if __name__ == "__main__":
      # Note that this example contains HARDCODED packets, meaning that
      # it will ONLY work on the system it was designed for.

      # I got these values by sending a ping while running Wireshark.
      # You can do so yourself.  Another way to construct these manually is to use
      # the impacket library (sudo pip install impacket)

      # src=fe:ed:fa:ce:be:ef, dst=52:54:00:12:35:02, type=0x0800 (IP)
      ethernet_packet = [0x52, 0x54, 0x00, 0x12, 0x35, 0x02, 0xfe, 0xed, 0xfa,
                         0xce, 0xbe, 0xef, 0x08, 0x00]

      # src=10.0.2.15, dst=195.88.54.16 (vg.no), checksum, etc.
      ipv4_header = [0x45, 0x00, 0x00, 0x54, 0x05, 0x9f, 0x40, 0x00, 0x40, 0x01,
                     0x2f, 0x93, 0x0a, 0x00, 0x02, 0x0f, 0xc3, 0x58, 0x36, 0x10]

      # echo (ping) request, checksum 2b45, etc
      icmp_ping = [0x08, 0x00, 0x2b, 0x45, 0x11, 0x22, 0x00, 0x02, 0xa9, 0xf4, 0x5c,
                   0x53, 0x00, 0x00, 0x00, 0x00, 0xf5, 0x7b, 0x01, 0x00, 0x00, 0x00,
                   0x00, 0x00, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
                   0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23,
                   0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e,
                   0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37]

      payload = "".join(map(chr, ipv4_header + icmp_ping))

      # Construct Ethernet packet with an IPv4 ICMP PING request as payload
      r = sendeth(pack(ethernet_packet),
                  pack(ipv4_header + icmp_ping))
      print("Sent Ethernet w/IPv4 ICMP PING payload of length %d bytes" % r)

@Winterflower
Copy link

Thank you very much @cslarsen! I've been looking around for an example using AF_PACKET.

@suchith92
Copy link

src and dst should be reversed in order!

@cslarsen
Copy link
Author

cslarsen commented Jun 6, 2016

To see an updated version, I've posted about this on https://csl.name/post/raw-ethernet-frames/

@phoy
Copy link

phoy commented Oct 4, 2016

Running it i get:
Traceback (most recent call last):
File "sendeth.py", line 52, in
r = sendeth(pack(ethernet_packet),
File "sendeth.py", line 23, in pack
return b"".join(map(chr, byte_sequence))
TypeError: sequence item 0: expected a bytes-like object, str found

which object do i need to convert from a str to bytes ?

@netzeng
Copy link

netzeng commented Jan 8, 2017

def pack(byte_sequence):
"""Convert list of bytes to byte string."""
return b"".join(map(chr, byte_sequence))

ethernet_packet = [0x52, 0x54, 0x00, 0x12, 0x35, 0x02, 0xfe, 0xed, 0xfa,
0xce, 0xbe, 0xef, 0x08, 0x00]

print(pack(ethernet_packet))

Traceback (most recent call last):
File "convert.py", line 9, in
print(pack(ethernet_packet))
File convert.py", line 3, in pack
return b"".join(map(chr, byte_sequence))
TypeError: sequence item 0: expected a bytes-like object, str found

#python env 3.4
this code not run windwos ENV,

@nasaWelder
Copy link

@cslarsen Could I use this to talk to the internet from an air gapped computer via QR code? I have the QR two way comm figured out...

@geajack
Copy link

geajack commented Oct 19, 2018

Using code based on this I encountered this problem - when viewed in Wireshark, it seems Python is tacking some extra bytes onto the end of this "raw" data. Anyone else experiencing this?

@mpkopec
Copy link

mpkopec commented Apr 1, 2020

@geajack If you send less than Ethernet's minimum frame size it will be padded. The number is (as I remember) 60 bytes. It is actually a good sign if your packet is padded.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment