How Do I Use Raw Socket in Python?

Question:

I am writing an application to test a network driver for handling corrupted data. And I thought of sending this data using raw socket, so it will not be corrected by the sending machine’s TCP-IP stack.

I am writing this application solely on Linux. I have code examples of using raw sockets in system-calls, but I would really like to keep my test as dynamic as possible, and write most if not all of it in Python.

I have googled the web a bit for explanations and examples of the usage of raw sockets in python, but haven’t found anything really enlightening. Just a a very old code example that demonstrates the idea, but in no means work.

From what I gathered, Raw Socket usage in Python is nearly identical in semantics to UNIX’s raw socket, but without the structs that define the packets structure.

I was wondering if it would even be better not to write the raw socket part of the test in Python, but in C with system-calls, and call it from the main Python code?

Asked By: Avihu Turzion

||

Answers:

The socket class should help. If not you’ll need to either write a Python module in C or just use C. See http://mail.python.org/pipermail/python-list/2001-April/077454.html.

Basic Googling found that.

I actually tried the code example which “unwind” has pointed out. AF_PACKET did work for me in python 2.7.4

Answered By:

Is this the old code you mentioned finding? It looks sensible to me, but I haven’t tested it myself (or used raw sockets much). This example from the documentation shows how to use raw sockets to sniff packets, and it looks similar enough.

Answered By: unwind

Sockets system calls (or Winsocks, on Windows), are already wrapped in the standard module socket: intro, reference.

I’ve never used raw sockets but it looks like they can be used with this module:

The last example shows how to write a
very simple network sniffer with raw
sockets on Windows. The example
requires administrator privileges to
modify the interface:

import socket

# the public network interface
HOST = socket.gethostbyname(socket.gethostname())

# create a raw socket and bind it to the public interface
s = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_IP)
s.bind((HOST, 0))

# Include IP headers
s.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1)

# receive all packages
s.ioctl(socket.SIO_RCVALL, socket.RCVALL_ON)

# receive a package
print s.recvfrom(65565)

# disabled promiscuous mode
s.ioctl(socket.SIO_RCVALL, socket.RCVALL_OFF)
Answered By: Bastien Léonard

Eventually the best solution for this case was to write the entire thing in C, because it’s not a big application, so it would’ve incurred greater penalty to write such a small thing in more than 1 language.

After much toying with both the C and python RAW sockets, I eventually preferred the C RAW sockets. RAW sockets require bit-level modifications of less than 8 bit groups for writing the packet headers. Sometimes writing only 4 bits or less. python defines no assistance to this, whereas Linux C has a full API for this.

But I definitely believe that if only this little bit of header initialization was handled conveniently in python, I would’ve never used C here.

Answered By: Avihu Turzion

You do it like this:

First you disable your network card’s automatic checksumming:

sudo ethtool -K eth1 tx off

And then send your dodgy frame from python 2 (You’ll have to convert to Python 3 yourself):

#!/usr/bin/env python
from socket import socket, AF_PACKET, SOCK_RAW
s = socket(AF_PACKET, SOCK_RAW)
s.bind(("eth1", 0))

# We're putting together an ethernet frame here, 
# but you could have anything you want instead
# Have a look at the 'struct' module for more 
# flexible packing/unpacking of binary data
# and 'binascii' for 32 bit CRC
src_addr = "x01x02x03x04x05x06"
dst_addr = "x01x02x03x04x05x06"
payload = ("["*30)+"PAYLOAD"+("]"*30)
checksum = "x1ax2bx3cx4d"
ethertype = "x08x01"

s.send(dst_addr+src_addr+ethertype+payload+checksum)

Done.

Answered By: brice
s = socket(AF_PACKET, SOCK_RAW)
s = socket(PF_PACKET, SOCK_RAW)

result:

[root@localhost python]# tcpdump -i eth0

capture size 96 bytes
11:01:46.850438 

01:02:03:04:05:06 (oui Unknown) > 01:02:03:04:05:06 (oui Unknown), ethertype Unknown (0x0801), length 85:

        0x0000:  5b5b 5b5b 5b5b 5b5b 5b5b 5b5b 5b5b 5b5b  [[[[[[[[[[[[[[[[
        0x0010:  5b5b 5b5b 5b5b 5b5b 5b5b 5b5b 5b5b 5041  [[[[[[[[[[[[[[PA
        0x0020:  594c 4f41 445d 5d5d 5d5d 5d5d 5d5d 5d5d  YLOAD]]]]]]]]]]]
        0x0030:  5d5d 5d5d 5d5d 5d5d 5d5d 5d5d 5d5d 5d5d  ]]]]]]]]]]]]]]]]
        0x0040:  5d5d 5d00 0000 00                        ]]]....
Answered By: gteng

You can use this Python library: rawsocketpy it allows using raw sockets on Layer 2 => No IP/TCP/UDP headers.

#!/usr/bin/env python
from rawsocketpy import RawSocket

sock = RawSocket("wlp2s0", 0xEEFA)
sock.send("some data")
sock.send("personal data", dest="xAAxBBxCCxDDxEExFF")

or the server form:

#!/usr/bin/env python
from rawsocketpy import RawRequestHandler, RawAsyncServerCallback
import time

def callback(handler, server):
    print("Testing")
    handler.setup()
    handler.handle()
    handler.finish()

class LongTaskTest(RawRequestHandler):
    def handle(self):
        time.sleep(1)
        print(self.packet)

    def finish(self):
        print("End")

    def setup(self):
        print("Begin") 

def main():
    rs = RawAsyncServerCallback("wlp2s0", 0xEEFA, LongTaskTest, callback)
    rs.spin()

if __name__ == '__main__':
    main()
Answered By: Alexis Paques

FTR, if you want level 2 access (Ethernet, RadioTap…), that won’t be possible natively on Windows (as of today).

If you want to access those with a cross platform method, the go-to choice is libpcap and its Python bindings (as it will use Npcap/WinPcap to work on Windows).

You have a variety of Python bindings available for libpcap, at all sorts of levels (very high or very low).

My advice would be to use the Sockets of scapy (even if you’re not using it to dissect), that implement both Native and Libpcap calls, (and let you chose against them with conf.use_pcap = True)

from scapy.all import conf
# conf.use_pcap = True (will be automatic if required)
socket = conf.L2socket(iface="eth0")
# On any platforms, you have `get_if_list()` in `scapy.all` available, to see the ifaces available. You could also ignore it to use the default one
Answered By: Cukic0d
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.