Using scapy without root privileges

2009-11-03

I've been working on some scripts that interact with SNMP devices and after much research and frustration settled on the fact that all of the SNMP libraries available in Python can do everything. The NetSNMP bindings are incomplete, the PySNMP API changes every few months, and both are poorly documented. For these reasons, I started using Scapy to create and parse SNMP packets.

It's not terribly difficult to get some useful code with scapy and if you take a close look, the scapy source often provides concrete examples of what you're trying to do. For the purposes of this article, let's try writing an SNMP walk script.

from scapy import *

def walk(ip, community, oid_prefix):
    nextoid = oid_prefix
	while nextoid.startswith(oid_prefix):
		p = IP(dst=ip)/UDP(sport=RandShort())/SNMP(community=community, PDU=SNMPnext(varbindlist=[SNMPvarbind(oid=nextoid)]))
		r = sr1(p)
		oid = r[SNMPvarbind].oid.val
		if oid.startswith(oid_prefix):
			yield (oid, r[SNMPvarbind].value)
		else:
			break
		nextoid = oid

This is mostly just a re-implementation of scapy's builtin snmpwalk() method, except that it doesn't traverse the tree infinitely. For the most part, this code works. However, it requires root privileges to run because scapy uses raw sockets to send packets and pcap to receive them. Clearly this is not ideal.

The simplest workaround I've found (with help from Jordan) is to create a regular socket object in python, then use scapy to generate and parse the payloads.

from scapy import *
from socket import socket, AF_INET, SOCK_DGRAM

def walk(ip, community, oid_prefix):
	sock = socket(AF_INET, SOCK_DGRAM)
	sock.connect((ip, 161))

	nextoid = oid_prefix
	while nextoid.startswith(oid_prefix):
		p = SNMP(community=community, PDU=SNMPnext(varbindlist=[SNMPvarbind(oid=nextoid)]))
		buf = str(p)
		while buf:
			bytes = sock.send(buf)
			buf = buf[bytes:]

		r = SNMP(sock.recv(4096))
		oid = r[SNMPvarbind].oid.val
		if oid.startswith(oid_prefix):
			yield (oid, r[SNMPvarbind].value)
		else:
			break

By using the standard BSD sockets approach, you can avoid the requirement to have root privileges by giving up control of the IP and transport layers to the OS. This should work equally well for SOCK_STREAM (TCP) and SOCK_DGRAM (UDP) socket types. This approach appears to be much faster than scapy's native pcap based method, because pcap isn't parsing every packet on the interface to find the responses.

Next post - Implementing HTTP Live Streaming