Python Network Packet Sniffer
This guide demonstrates how to build a network packet sniffer using raw sockets. It captures traffic flowing through the network interface and unpacks the binary data to reveal Ethernet, IP, TCP, UDP, and ICMP headers.
Note: This script requires Administrator (Windows) or Root (Linux/macOS) privileges.
Modules Used:
- socket: To create raw sockets.
struct: To unpack binary data structures (headers).os: To detect the operating system (Windows handles raw sockets differently).
The Code
Save this as sniffer.py.
import socket
import struct
import textwrap
import os
import sys
def get_mac_addr(bytes_addr):
"""Converts bytes to MAC address string (e.g., AA:BB:CC...)"""
bytes_str = map('{:02x}'.format, bytes_addr)
return ':'.join(bytes_str).upper()
def get_ipv4(addr):
"""Converts raw IP bytes to string (e.g., 192.168.1.1)"""
return '.'.join(map(str, addr))
def ethernet_frame(data):
"""Unpacks Ethernet Frame"""
dest_mac, src_mac, proto = struct.unpack('! 6s 6s H', data[:14])
return get_mac_addr(dest_mac), get_mac_addr(src_mac), socket.htons(proto), data[14:]
def ipv4_packet(data):
"""Unpacks IPv4 Packet"""
version_header_length = data[0]
version = version_header_length >> 4
header_length = (version_header_length & 15) * 4
ttl, proto, src, target = struct.unpack('! 8x B B 2x 4s 4s', data[:20])
return version, header_length, ttl, proto, get_ipv4(src), get_ipv4(target), data[header_length:]
def tcp_segment(data):
"""Unpacks TCP Segment"""
(src_port, dest_port, sequence, acknowledgment, offset_reserved_flags) = struct.unpack('! H H L L H', data[:14])
offset = (offset_reserved_flags >> 12) * 4
return src_port, dest_port, sequence, acknowledgment, data[offset:]
def udp_segment(data):
"""Unpacks UDP Segment"""
src_port, dest_port, size = struct.unpack('! H H 2x H', data[:8])
return src_port, dest_port, size, data[8:]
def main():
conn = None
# 1. Setup Socket
try:
if os.name == 'nt':
# Windows: Captures IP packets (no Ethernet header)
host = socket.gethostbyname(socket.gethostname())
conn = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_IP)
conn.bind((host, 0))
conn.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1)
conn.ioctl(socket.SIO_RCVALL, socket.RCVALL_ON)
print(f"Sniffing on {host} (Windows mode - IP only)...")
else:
# Linux/Unix: Captures full Ethernet frames
# socket.ntohs(0x0003) = ETH_P_ALL (Capture all protocols)
conn = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.ntohs(3))
print("Sniffing on interface (Linux mode - Full Ethernet)...")
except PermissionError:
print("Error: Access denied. Run as Administrator/Root.")
sys.exit(1)
except Exception as e:
print(f"Error setting up socket: {e}")
sys.exit(1)
# 2. Capture Loop
try:
while True:
raw_data, addr = conn.recvfrom(65535)
ip_data = raw_data
# If Linux, parse Ethernet header first
if os.name != 'nt':
dest_mac, src_mac, eth_proto, data = ethernet_frame(raw_data)
print(f'\n[Ethernet] {src_mac} -> {dest_mac} | Proto: {eth_proto}')
# 8 is IPv4
if eth_proto == 8:
ip_data = data
else:
continue # Skip non-IPv4 for this example
# Parse IPv4
version, header_length, ttl, proto, src, target, data = ipv4_packet(ip_data)
print(f'\t[IPv4] {src} -> {target} | Proto: {proto}')
# Parse Transport Layer
# TCP
if proto == 6:
src_port, dest_port, seq, ack, payload = tcp_segment(data)
print(f'\t\t[TCP] Port {src_port} -> {dest_port}')
# UDP
elif proto == 17:
src_port, dest_port, length, payload = udp_segment(data)
print(f'\t\t[UDP] Port {src_port} -> {dest_port} | Len: {length}')
# ICMP
elif proto == 1:
print(f'\t\t[ICMP] Packet')
except KeyboardInterrupt:
# Windows: Turn off promiscuous mode
if os.name == 'nt':
conn.ioctl(socket.SIO_RCVALL, socket.RCVALL_OFF)
print("\nSniffer stopped.")
if __name__ == "__main__":
main()
Usage
-
Run with Privileges:
- Windows: Open Command Prompt as Administrator.
- Linux/macOS: Use
sudo.
# Linux sudo python sniffer.py # Windows (Admin CMD) python sniffer.py -
Generate Traffic: Open a web browser or ping a website to see packets appearing in the terminal.