← Back to all posts
[Python]

Packet Sniffing with Python: A Beginner's Guide

Sep 29, 20243 min read

Hi folks. Today I'll demonstrate network traffic analysis using a simple Python program, and explain why having a keen eye for small details is crucial in networking.

How packets are sniffed over a network

Understanding this process — and being able to categorize it — is foundational for more advanced techniques like Man-in-the-Middle attacks and ARP poisoning. Before we get there, we need a solid grasp of the tools, how they work, and the specific arguments and functions involved.

Network visualization

Client/Server side

In a previous article, I covered the socket library and how to create a simple client and server. This tutorial builds directly on that, so if you haven't read it, I'd recommend checking it out first.

Link

Let's get started


import socket
import os

# This script only sniffs IPv4 packets
# To test: ping -4 google.com
# Host to listen on
Host = '192.168.1.10'

def main():
    if os.name == 'nt':
        socket_protocol = socket.IPPROTO_IP
    else:
        socket_protocol = socket.IPPROTO_ICMP

    sniffer = socket.socket(socket.AF_INET,
        socket.SOCK_RAW, socket_protocol)

    sniffer.bind((Host, 0))

    # Include the IP header in the capture
    sniffer.setsockopt(socket.IPPROTO_IP,
        socket.IP_HDRINCL, 1)

    if os.name == 'nt':
        sniffer.ioctl(socket.SIO_RCVALL,
            socket.RCVALL_ON)

    print(sniffer.recvfrom(65565))

    if os.name == 'nt':
        sniffer.ioctl(socket.SIO_RCVALL,
            socket.RCVALL_OFF)

if __name__ == '__main__':
    main()

socket — this library lets us create low-level network connections to other devices.

os — stands for Operating System. It provides functions for interacting with the underlying system — file handling, environment variables, and system-level operations. Here we use it to detect whether we're running on Windows or Linux, since the two platforms handle raw sockets differently.

The conditional statement checks the OS type. If os.name returns 'nt' (Windows), the protocol is set to IPPROTO_IP. On Linux, we use IPPROTO_ICMP instead — because Linux raw sockets require a specific protocol rather than the wildcard IP protocol. ICMP (Internet Control Message Protocol) is what ping uses — a protocol for communicating network-level errors and diagnostics between devices.

socket.AF_INET specifies IPv4 addressing. socket.SOCK_RAW is the critical argument — it creates a raw socket that captures data directly from the network without any processing or filtering by the OS. This is what gives us access to the raw packet bytes, including headers.


sniffer.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1)

By default, when capturing in RAW mode the IP header is stripped out. This line tells the socket to include it in every captured packet — which we need to properly parse the source and destination addresses later.


if os.name == 'nt':
    sniffer.ioctl(socket.SIO_RCVALL, socket.RCVALL_ON)

print(sniffer.recvfrom(65565))

if os.name == 'nt':
    sniffer.ioctl(socket.SIO_RCVALL, socket.RCVALL_OFF)

On Windows, SIO_RCVALL_ON enables promiscuous mode — capturing all packets on the interface, not just those addressed to us. The socket then captures one packet up to 65,565 bytes and prints it. The final block turns promiscuous mode back off.

sniffed packet output

The output is the raw packet data — bytes, headers, and all. In a follow-up article, we'll cover how to parse and decode this into something readable.


Thank you for reading. I hope it was useful. Have a great day!

buy me a coffee: ko-fi.com/ghostman77506