Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Packet fun with Python and C

2 views
Skip to first unread message

Will Ware

unread,
Aug 30, 2002, 2:03:34 AM8/30/02
to
"""
/*
gcc -Wall -O2 -c -I/usr/include/python1.5 rawsocket.c
gcc -shared -o rawsocket.so rawsocket.o
*/

#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <netdb.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <linux/if_ether.h>

#include \"Python.h\"

#define DEFAULT_DEVICE \"eth0\"


static PyObject *
rawsocket_socket (PyObject * self, PyObject * args)
{
int sock;
sock = socket (AF_INET, SOCK_PACKET, htons (ETH_P_RARP));
if (sock < 0)
{
PyErr_SetString(PyExc_IOError,
\"can't allocate a socket\");
return NULL;
}
return PyInt_FromLong(sock);
}

static PyObject *
rawsocket_sendto (PyObject * self, PyObject * args)
{
int sock;
char *packet;
int packetlen;
char *device;
struct sockaddr sa;
device = DEFAULT_DEVICE;
if (!PyArg_ParseTuple (args, \"is#|s\",
&sock,
&packet, &packetlen,
&device))
return NULL;
strcpy (sa.sa_data, device);
if (sendto (sock,
packet, packetlen, 0, &sa, sizeof (sa)) < 0)
{
PyErr_SetString(PyExc_IOError,
\"socket sendto unsuccessful\");
return NULL;
}
Py_INCREF (Py_None);
return Py_None;
}

/* List of functions defined in the module */

static PyMethodDef rawsocket_methods[] = {
{\"socket\", rawsocket_socket, 1},
{\"sendto\", rawsocket_sendto, 1},
{NULL, NULL} /* sentinel */
};


/* Initialization function for the module (*must* be called
initrawsocket) */

DL_EXPORT (void)
initrawsocket ()
{
Py_InitModule (\"rawsocket\", rawsocket_methods);
}
"""

# It will be necessary to run this script as root in order to get a
# raw socket. Combined with the Ethereal packet sniffer, this script
# is highly instructive.


def bigEndian(n, x):
str = ""
shift = (n - 1) * 8
for i in range(n):
str = str + chr((x >> shift) & 0xFF)
shift = shift - 8
return str

def ethernet_header(src_mac, dest_mac, type):
return (bigEndian(6, dest_mac) +
bigEndian(6, src_mac) +
bigEndian(2, type))

def ip_address(a, b, c, d):
return ((long(a) << 24) +
(long(b) << 16) +
(long(c) << 8) +
long(d))

my_ipaddr = ip_address(192, 168, 1, 101)
my_macaddr = 0x00C04F4D09E7L
bd_ipaddr = ip_address(192, 168, 1, 150)
bd_macaddr = 0xFFFFFFFFFFFFL

# /usr/include/net/ethernet.h
ETHERTYPE_PUP = 0x0200 # Xerox PUP
ETHERTYPE_IP = 0x0800 # IP
ETHERTYPE_ARP = 0x0806 # Address resolution
ETHERTYPE_REVARP = 0x8035 # Reverse ARP

# ARP protocol HARDWARE identifiers, /usr/include/net/if_arp.h
ARPHRD_NETROM = 0 # From KA9Q: NET/ROM pseudo.
ARPHRD_ETHER = 1 # Ethernet 10/100Mbps.
ARPHRD_EETHER = 2 # Experimental Ethernet.
ARPHRD_AX25 = 3 # AX.25 Level 2.
ARPHRD_PRONET = 4 # PROnet token ring.
ARPHRD_CHAOS = 5 # Chaosnet.
ARPHRD_IEEE802 = 6 # IEEE 802.2 Ethernet/TR/TB.
ARPHRD_ARCNET = 7 # ARCnet.
ARPHRD_APPLETLK = 8 # APPLEtalk.
ARPHRD_DLCI = 15 # Frame Relay DLCI.
ARPHRD_ATM = 19 # ATM.
ARPHRD_METRICOM = 23 # Metricom STRIP (new IANA id).

# ARP opcodes, /usr/include/net/if_arp.h
ARPOP_REQUEST = 1 # ARP request.
ARPOP_REPLY = 2 # ARP reply.
ARPOP_RREQUEST = 3 # RARP request.
ARPOP_RREPLY = 4 # RARP reply.
ARPOP_InREQUEST = 8 # InARP request.
ARPOP_InREPLY = 9 # InARP reply.
ARPOP_NAK = 10 # (ATM)ARP NAK.

ethernet_header = (bigEndian(6, bd_macaddr) +
bigEndian(6, my_macaddr) +
bigEndian(2, ETHERTYPE_ARP))

arp_packet = (bigEndian(2, ARPHRD_ETHER) +
bigEndian(2, ETHERTYPE_IP) +
bigEndian(1, 6) + # length of MAC addr in bytes
bigEndian(1, 4) + # length of IP addr in bytes
bigEndian(2, ARPOP_REQUEST) +
bigEndian(6, my_macaddr) +
bigEndian(4, my_ipaddr) +
bigEndian(6, bd_macaddr) +
bigEndian(4, bd_ipaddr))

import os, sys

try:
import rawsocket
except ImportError:
outf = open("rawsocket.c", "w")
outf.write(__doc__)
outf.close()
os.system("gcc -Wall -O2 -c -I/usr/include/python1.5 rawsocket.c")
os.system("gcc -shared -o rawsocket.so rawsocket.o")
import rawsocket

try:
ipsock = rawsocket.socket()
except IOError:
print "Login as root, or you won't be able to make a raw socket"
raise IOError

rawsocket.sendto(ipsock, ethernet_header + arp_packet)

Will Ware

unread,
Aug 31, 2002, 1:48:25 AM8/31/02
to
#!/bin/sh
# This is a shell archive (produced by GNU sharutils 4.2.1).
# To extract the files from this archive, save it to some FILE, remove
# everything before the `!/bin/sh' line above, then type `sh FILE'.
#
# Made on 2002-08-31 01:46 EDT by <ww...@localhost.localdomain>.
# Source directory was `/home/wware/prot-docs'.
#
# Existing files will *not* be overwritten unless `-c' is specified.
#
# This shar contains:
# length mode name
# ------ ---------- ------------------------------------------
# 1942 -r--r--r-- rawsocket.c
# 5159 -r-xr-xr-x packetfun.py
#
save_IFS="${IFS}"
IFS="${IFS}:"
gettext_dir=FAILED
locale_dir=FAILED
first_param="$1"
for dir in $PATH
do
if test "$gettext_dir" = FAILED && test -f $dir/gettext \
&& ($dir/gettext --version >/dev/null 2>&1)
then
set `$dir/gettext --version 2>&1`
if test "$3" = GNU
then
gettext_dir=$dir
fi
fi
if test "$locale_dir" = FAILED && test -f $dir/shar \
&& ($dir/shar --print-text-domain-dir >/dev/null 2>&1)
then
locale_dir=`$dir/shar --print-text-domain-dir`
fi
done
IFS="$save_IFS"
if test "$locale_dir" = FAILED || test "$gettext_dir" = FAILED
then
echo=echo
else
TEXTDOMAINDIR=$locale_dir
export TEXTDOMAINDIR
TEXTDOMAIN=sharutils
export TEXTDOMAIN
echo="$gettext_dir/gettext -s"
fi
if touch -am -t 200112312359.59 $$.touch >/dev/null 2>&1 && test ! -f
200112312359.59 -a -f $$.touch; then
shar_touch='touch -am -t $1$2$3$4$5$6.$7 "$8"'
elif touch -am 123123592001.59 $$.touch >/dev/null 2>&1 && test ! -f
123123592001.59 -a ! -f 123123592001.5 -a -f $$.touch; then
shar_touch='touch -am $3$4$5$6$1$2.$7 "$8"'
elif touch -am 1231235901 $$.touch >/dev/null 2>&1 && test ! -f
1231235901 -a -f $$.touch; then
shar_touch='touch -am $3$4$5$6$2 "$8"'
else
shar_touch=:
echo
$echo 'WARNING: not restoring timestamps. Consider getting and'
$echo "installing GNU \`touch', distributed in GNU File Utilities..."
echo
fi
rm -f 200112312359.59 123123592001.59 123123592001.5 1231235901 $$.touch
#
if mkdir _sh19003; then
$echo 'x -' 'creating lock directory'
else
$echo 'failed to create lock directory'
exit 1
fi
# ============= rawsocket.c ==============
if test -f 'rawsocket.c' && test "$first_param" != -c; then
$echo 'x -' SKIPPING 'rawsocket.c' '(file already exists)'
else
$echo 'x -' extracting 'rawsocket.c' '(text)'
sed 's/^X//' << 'SHAR_EOF' > 'rawsocket.c' &&
/*
X * gcc -Wall -O2 -c -I/usr/include/python1.5 rawsocket.c
X * gcc -shared -o rawsocket.so rawsocket.o
X */
X

#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <netdb.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <linux/if_ether.h>
X
#include "Python.h"
X
#define DEFAULT_DEVICE "eth0"
X
static PyObject *
rawsocket_socket(PyObject * self, PyObject * args)
{
X int sock;
X int protocol;
X if (!PyArg_ParseTuple(args, "i", &protocol))
X return NULL;
X sock = socket(AF_INET, SOCK_PACKET, htons(protocol));
X if (sock < 0) {
X PyErr_SetString(PyExc_IOError,
X "can't allocate a socket");
X return NULL;
X }
X return PyInt_FromLong(sock);
}
X
static PyObject *
rawsocket_sendto(PyObject * self, PyObject * args)
{
X int sock;
X char *packet;
X int packetlen;
X char *device;
X struct sockaddr sa;
X device = DEFAULT_DEVICE;
X if (!PyArg_ParseTuple(args, "is#|s",
X &sock, &packet, &packetlen, &device))
X return NULL;
X strcpy(sa.sa_data, device);
X if (sendto(sock, packet, packetlen, 0, &sa, sizeof(sa)) < 0) {
X PyErr_SetString(PyExc_IOError,
X "socket sendto unsuccessful");
X return NULL;
X }
X Py_INCREF(Py_None);
X return Py_None;
}
X
static PyObject *
rawsocket_recvfrom(PyObject * self, PyObject * args)
{
X int sock;
X const int MAX_RECEIVE_PACKET = 1000;
X char packet[MAX_RECEIVE_PACKET];
X int packetlen;
X if (!PyArg_ParseTuple(args, "i", &sock))
X return NULL;
X packetlen = recvfrom(sock, packet, MAX_RECEIVE_PACKET, 0,
X NULL, NULL);
X if (packetlen < 0) {
X PyErr_SetString(PyExc_IOError,
X "socket recvfrom unsuccessful");
X return NULL;
X }
X return PyString_FromStringAndSize(packet, packetlen);
}
X
static PyMethodDef rawsocket_methods[] = {
X {"socket", rawsocket_socket, 1},
X {"sendto", rawsocket_sendto, 1},
X {"recvfrom", rawsocket_recvfrom, 1},
X {NULL, NULL} /* sentinel */
};
X
DL_EXPORT(void)
X initrawsocket()
{
X Py_InitModule("rawsocket", rawsocket_methods);
}
SHAR_EOF
(set 20 02 08 31 01 44 08 'rawsocket.c'; eval "$shar_touch") &&
chmod 0444 'rawsocket.c' ||
$echo 'restore of' 'rawsocket.c' 'failed'
if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \
&& ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null;
then
md5sum -c << SHAR_EOF >/dev/null 2>&1 \
|| $echo 'rawsocket.c:' 'MD5 check failed'
85a5d67e2ee08a7dffa4da7a2cab99c0 rawsocket.c
SHAR_EOF
else
shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'rawsocket.c'`"
test 1942 -eq "$shar_count" ||
$echo 'rawsocket.c:' 'original size' '1942,' 'current size'
"$shar_count!"
fi
fi
# ============= packetfun.py ==============
if test -f 'packetfun.py' && test "$first_param" != -c; then
$echo 'x -' SKIPPING 'packetfun.py' '(file already exists)'
else
$echo 'x -' extracting 'packetfun.py' '(text)'
sed 's/^X//' << 'SHAR_EOF' > 'packetfun.py' &&
#!/usr/bin/python
X

# It will be necessary to run this script as root in order to get a
# raw socket. Combined with the Ethereal packet sniffer, this script
# is highly instructive.
X
# N.B. This is shamelessly Linux-centric and likely to blow up on
# other platforms.
X
import sys, os, string, re
X
def bigEndian(n, x):
X """Used to build packets, converts ints or longs to a
X string of characters suitable for socket sending"""
X str = ""
X shift = (n - 1) * 8
X for i in range(n):
X str = str + chr((x >> shift) & 0xFF)
X shift = shift - 8
X return str
X

def ip_address(a, b, c, d):
X """Given four bytes for a dotted quad, build a long"""
X return ((long(a) << 24) +
X (long(b) << 16) +
X (long(c) << 8) +
X long(d))
X
def extract(x, len):
X """Pop a string of characters off the front of a string
X and interpret them as a big-endian long integer, used to
X parse packets."""
X sum = ""
X for c in x[:len]:
X sum = sum + ("%02X" % ord(c))
X sum = eval("0x" + sum + "L")
X try: sum = int(sum)
X except OverflowError: pass
X return sum, x[len:]
X
def dottedQuad(x):
X """Pop four characters off the front of a string and
X interpret them as a dotted quad, return a string"""
X sum = ""
X for c in x[:4]:
X sum = sum + "." + repr(ord(c))
X return sum[1:], x[4:]
X
# First get my own MAC and IP addresses
ifcfg_lines = os.popen("/sbin/ifconfig eth0").readlines()
X
x = string.split(ifcfg_lines[0])[4]
my_macaddr = eval("0x" + re.sub(":", "", x) + "L")
X
x = string.split(ifcfg_lines[1])[1]
x = string.split(re.sub(".*:", "", x), ".")
my_ipaddr = apply(ip_address,
X map(eval, x))
X
# Now we'll go on a Snipe Hunt for the Etherio board, but
# we'll start out ignorant of its MAC address. The purpose of
# this exercise is to use the network to ask it for its MAC
# address.

bd_ipaddr = ip_address(192, 168, 1, 150)
bd_macaddr = 0xFFFFFFFFFFFFL
X
# Ethernet protocol IDs, /usr/include/net/ethernet.h

ETHERTYPE_PUP = 0x0200 # Xerox PUP
ETHERTYPE_IP = 0x0800 # IP
ETHERTYPE_ARP = 0x0806 # Address resolution
ETHERTYPE_REVARP = 0x8035 # Reverse ARP
X
# ARP protocol hardware identifiers, /usr/include/net/if_arp.h

ARPHRD_NETROM = 0 # From KA9Q: NET/ROM pseudo.
ARPHRD_ETHER = 1 # Ethernet 10/100Mbps.
ARPHRD_EETHER = 2 # Experimental Ethernet.
ARPHRD_AX25 = 3 # AX.25 Level 2.
ARPHRD_PRONET = 4 # PROnet token ring.
ARPHRD_CHAOS = 5 # Chaosnet.
ARPHRD_IEEE802 = 6 # IEEE 802.2 Ethernet/TR/TB.
ARPHRD_ARCNET = 7 # ARCnet.
ARPHRD_APPLETLK = 8 # APPLEtalk.
ARPHRD_DLCI = 15 # Frame Relay DLCI.
ARPHRD_ATM = 19 # ATM.
ARPHRD_METRICOM = 23 # Metricom STRIP (new IANA id).
X

# ARP opcodes, /usr/include/net/if_arp.h
ARPOP_REQUEST = 1 # ARP request.
ARPOP_REPLY = 2 # ARP reply.
ARPOP_RREQUEST = 3 # RARP request.
ARPOP_RREPLY = 4 # RARP reply.
ARPOP_InREQUEST = 8 # InARP request.
ARPOP_InREPLY = 9 # InARP reply.
ARPOP_NAK = 10 # (ATM)ARP NAK.
X

ethernet_header = (bigEndian(6, bd_macaddr) +
X bigEndian(6, my_macaddr) +
X bigEndian(2, ETHERTYPE_ARP))
X

arp_packet = (bigEndian(2, ARPHRD_ETHER) +
X bigEndian(2, ETHERTYPE_IP) +
X bigEndian(1, 6) + # length of MAC addr in bytes
X bigEndian(1, 4) + # length of IP addr in bytes
X bigEndian(2, ARPOP_REQUEST) +
X bigEndian(6, my_macaddr) + # src mac, ip address
X bigEndian(4, my_ipaddr) +
X bigEndian(6, bd_macaddr) + # dst mac, ip address
X bigEndian(4, bd_ipaddr))
X
import os, sys
X
try:
X import rawsocket
except ImportError:
X os.system("gcc -Wall -O2 -c -I/usr/include/python1.5 rawsocket.c")
X os.system("gcc -shared -o rawsocket.so rawsocket.o")
X import rawsocket
X
try:
X sock = rawsocket.socket(ETHERTYPE_ARP)
except IOError:
X print "Login as root, or you won't be able to make a raw socket"
X raise IOError
X
rawsocket.sendto(sock, ethernet_header + arp_packet)
x = rawsocket.recvfrom(sock)
X
# x is an ARP reply, so let's parse it
X
print "Ethernet header"
y, x = extract(x, 6)
print " dst mac addr", hex(y)
y, x = extract(x, 6)
print " src mac addr", hex(y)
y, x = extract(x, 2)
print " eth type", hex(y)
assert y == ETHERTYPE_ARP
X
print "ARP packet"
y, x = extract(x, 2)
print " arp hardware type", y
y, x = extract(x, 2)
print " ethernet type", hex(y)
y, x = extract(x, 1)
print " mac addr len", y
y, x = extract(x, 1)
print " ip addr len", y
y, x = extract(x, 2)
print " arp opcode", y
assert y == ARPOP_REPLY
y, x = extract(x, 6)
print " src mac addr", hex(y)
y, x = dottedQuad(x)
print " src ip addr", y
y, x = extract(x, 6)
print " dst mac addr", hex(y)
y, x = dottedQuad(x)
print " dst ip addr", y
X
if x:
X print len(x), "bytes left over"
SHAR_EOF
(set 20 02 08 31 01 46 27 'packetfun.py'; eval "$shar_touch") &&
chmod 0555 'packetfun.py' ||
$echo 'restore of' 'packetfun.py' 'failed'
if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \
&& ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null;
then
md5sum -c << SHAR_EOF >/dev/null 2>&1 \
|| $echo 'packetfun.py:' 'MD5 check failed'
1f6fb7b83653562ee6be469cefdd2888 packetfun.py
SHAR_EOF
else
shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'packetfun.py'`"
test 5159 -eq "$shar_count" ||
$echo 'packetfun.py:' 'original size' '5159,' 'current size'
"$shar_count!"
fi
fi
rm -fr _sh19003
exit 0

Will Ware

unread,
Aug 31, 2002, 11:37:40 PM8/31/02
to
#!/usr/bin/python
# This uses the rawsocket.c Python module defined previously:
# http://groups.google.com/groups?selm=3D7058A9.29732F5%40alum.mit.edu

# N.B. This is shamelessly Linux-centric and likely to blow up on
# other platforms.

import sys, os, string, re, types

try:
import rawsocket
except ImportError:

os.system("gcc -Wall -O2 -c -I/usr/include/python1.5 rawsocket.c")

os.system("gcc -shared -o rawsocket.so rawsocket.o")

import rawsocket

#########################################################

def ip_address(a, b, c, d):

"""Given four bytes for a dotted quad, build a long"""

return ((long(a) << 24) + (long(b) << 16) +
(long(c) << 8) + long(d))

# First get my own MAC and IP addresses


ifcfg_lines = os.popen("/sbin/ifconfig eth0").readlines()

x = string.split(ifcfg_lines[0])[4]


my_macaddr = eval("0x" + re.sub(":", "", x) + "L")

x = string.split(ifcfg_lines[1])[1]


x = string.split(re.sub(".*:", "", x), ".")

my_ipaddr = apply(ip_address, map(eval, x))

# Now we'll go on a Snipe Hunt for the Etherio board, but
# we'll start out ignorant of its MAC address. The purpose of
# this exercise is to use the network to ask it for its MAC
# address.
bd_ipaddr = ip_address(192, 168, 1, 150)
bd_macaddr = 0xFFFFFFFFFFFFL

# But actually I do know the MAC address for the board, and
# I'll want it later for my ARP table
bd_macaddr_really = 0x45445450

#########################################################

class Packet:

def __init__(self):
self.previous = None

def bytes(self):
r = ""
if self.previous:
r = self.previous.bytes()
return r + self.thesebytes()

def thesebytes(self):
r = ""
for (field, size) in self.format:
size = self.eval(size)
r = r + self.bigEndian(size, getattr(self, field))
return r

def compute_checksum(self, str=None):
if str == None:
str = self.thesebytes()
if len(str) & 1:
str = str + "\000"
sum = 0L
while str:
highbyte = ord(str[0])
lowbyte = ord(str[1])
str = str[2:]
x = (highbyte << 8) + lowbyte
sum = sum + x
while (sum >> 16):
sum = (sum & 0xffff) + (sum >> 16)
return int((~sum) & 0xffff)

def bigEndian(self, n, x):


"""Used to build packets, converts ints or longs to a

string of characters suitable for socket sending"""

str = ""


shift = (n - 1) * 8

for i in range(n):


str = str + chr((x >> shift) & 0xFF)

shift = shift - 8

return str

def extract(self, x):
if self.previous:
x = self.previous.extract(x)
for (field, size) in self.format:
size = self.eval(size)
y, x = self.extractField(x, size)
setattr(self, field, y)
return x

def extractField(self, x, len):


"""Pop a string of characters off the front of a string

and interpret them as a big-endian long integer, used to

parse packets."""
sum = ""


for c in x[:len]:

sum = sum + ("%02X" % ord(c))

sum = eval("0x" + sum + "L")

try: sum = int(sum)
except OverflowError: pass
return sum, x[len:]

def dottedQuad(self, x):


"""Pop four characters off the front of a string and

interpret them as a dotted quad, return a string"""

sum = ""
for i in range(4):
y = int((x & 0xff000000L) >> 24)
sum = sum + "." + repr(y)
x = x << 8
return sum[1:]

def eval(self, field):
orig = field
for i in range(3):
if (type(field) == types.IntType or
type(field) == types.LongType):
return field
else:
field = getattr(self, field)
raise AttributeError, "what the heck is " + orig

def dump(self, indent=0):
if self.previous:
self.previous.dump(indent)
print (indent * " ") + self.__class__.__name__
indent = indent + 1
for (field, size) in self.format:
x = getattr(self, field)
print (indent * " ") + field + " " + hex(x),
if self.eval(size) == 4:
print self.dottedQuad(x)
else:
print


class EthernetHeader(Packet):

# Ethernet protocol IDs, /usr/include/net/ethernet.h

TYPE_PUP = 0x0200 # Xerox PUP

TYPE_IP = 0x0800 # IP


TYPE_ARP = 0x0806 # Address resolution

TYPE_REVARP = 0x8035 # Reverse ARP

format = [ ("dst_mac", 6),
("src_mac", 6),
("packet_type", 2) ]


class ArpPacket(Packet):

format = [ ("arp_hw", 2),
("ethernet_type", 2),
("hw_addr_len", 1),
("prot_addr_len", 1),
("arp_opcode", 2),
# these sizes are variable
("src_hw_addr", "hw_addr_len"),
("src_prot_addr", "prot_addr_len"),
("dst_hw_addr", "hw_addr_len"),
("dst_prot_addr", "prot_addr_len") ]

# Constants found in /usr/include/net/if_arp.h
# ARP hardware IDs
HW_NETROM = 0 # From KA9Q: NET/ROM pseudo.
HW_ETHER = 1 # Ethernet 10/100Mbps.
HW_EETHER = 2 # Experimental Ethernet.
HW_AX25 = 3 # AX.25 Level 2.
HW_PRONET = 4 # PROnet token ring.
HW_CHAOS = 5 # Chaosnet.
HW_IEEE802 = 6 # IEEE 802.2 Ethernet/TR/TB.
HW_ARCNET = 7 # ARCnet.
HW_APPLETLK = 8 # APPLEtalk.
HW_DLCI = 15 # Frame Relay DLCI.
HW_ATM = 19 # ATM.
HW_METRICOM = 23 # Metricom STRIP (new IANA id).

# ARP opcodes
OPCODE_REQUEST = 1 # ARP request.
OPCODE_REPLY = 2 # ARP reply.
OPCODE_RREQUEST = 3 # RARP request.
OPCODE_RREPLY = 4 # RARP reply.
OPCODE_InREQUEST = 8 # InARP request.
OPCODE_InREPLY = 9 # InARP reply.
OPCODE_NAK = 10 # (ATM)ARP NAK.

def __init__(self):
# For now, assume hardware address are 6-byte MAC addresses,
# and protocol addresses are 4-byte IP addresses, but this
# might be different for 802.11b or other protocols.
self.hw_addr_len = 6
self.prot_addr_len = 4
self.previous = EthernetHeader()


arptable = {
my_ipaddr: my_macaddr,
# bd_ipaddr: bd_macaddr_really
}


class IpHeader(Packet):

# Constants taken from /usr/include/netinet/in.h
PROT_IP = 0 # Dummy protocol for TCP.
PROT_HOPOPTS = 0 # IPv6 Hop-by-Hop options.
PROT_ICMP = 1 # Internet Control Message Protocol.
PROT_IGMP = 2 # Internet Group Management Protocol
PROT_IPIP = 4 # IPIP tunnels (older KA9Q tunnels use 94).
PROT_TCP = 6 # Transmission Control Protocol.
PROT_EGP = 8 # Exterior Gateway Protocol.
PROT_PUP = 12 # PUP protocol.
PROT_UDP = 17 # User Datagram Protocol.
PROT_IDP = 22 # XNS IDP protocol.
PROT_TP = 29 # SO Transport Protocol Class 4.
PROT_IPV6 = 41 # IPv6 header.
PROT_ROUTING = 43 # IPv6 routing header.
PROT_FRAGMENT = 44 # IPv6 fragmentation header.
PROT_RSVP = 46 # Reservation Protocol.
PROT_GRE = 47 # General Routing Encapsulation.
PROT_ESP = 50 # encapsulating security payload.
PROT_AH = 51 # authentication header.
PROT_ICMPV6 = 58 # ICMPv6.
PROT_NONE = 59 # IPv6 no next header.
PROT_DSTOPTS = 60 # IPv6 destination options.
PROT_MTP = 92 # Multicast Transport Protocol.
PROT_ENCAP = 98 # Encapsulation Header.
PROT_PIM = 103 # Protocol Independent Multicast.
PROT_COMP = 108 # Compression Header Protocol.
PROT_RAW = 255 # Raw IP packets.

format = [ ("version_hdr_len", 1),
("diff_services_field", 1),
("total_length", 2),
("id", 2),
("flags_fragment_offset", 2),
("ttl", 1),
("protocol", 1),
("hdr_chksum", 2),
("src_ipaddr", 4),
("dst_ipaddr", 4),
]

def __init__(self, srcip, dstip, datalength, protocol):

self.previous = eth = EthernetHeader()
eth.dst_mac = arptable[dstip]
eth.src_mac = arptable[srcip]
eth.packet_type = EthernetHeader.TYPE_IP

self.version_hdr_len = 0x45
self.diff_services_field = 0
self.total_length = datalength + 20
self.id = 0x67cb
self.flags_fragment_offset = 0x4000
self.ttl = 64
self.protocol = protocol
self.hdr_chksum = 0
self.src_ipaddr = srcip
self.dst_ipaddr = dstip

self.hdr_chksum = self.compute_checksum()


class UdpPacket(Packet):

format = [ ("src_port", 2),
("dst_port", 2),
("length", 2),
("chksum", 2),
]

def __init__(self, srcip, srcport, dstip, dstport, data):
n = len(data) + 8
self.length = n
self.src_port = srcport
self.dst_port = dstport
self.data = data
self.chksum = 0

self.chksum = self.compute_checksum()

self.previous = IpHeader(srcip, dstip,
n, IpHeader.PROT_UDP)

def thesebytes(self):
return Packet.thesebytes(self) + self.data

def extract(self, x):
x = Packet.extract(self, x)
n = self.length - 8
self.data = x[:n]
return x[n:]

def dump(self):
Packet.dump(self)
print "UDP data:",
print string.join(map(lambda x: hex(ord(x)),
list(self.data)))


########################################################################
#
# Create a raw socket. Set up an outgoing ARP packet, asking for the
# board's MAC address. An ARP reply will come back. Print dumps of
# both outgoing and incoming packets.
#


def test_arp():

try:
sock = rawsocket.socket(EthernetHeader.TYPE_ARP)
except IOError:


print "Login as root, or you won't be able to make a raw socket"

raise IOError

a = ArpPacket()

eth = a.previous
eth.dst_mac = bd_macaddr
eth.src_mac = my_macaddr
eth.packet_type = EthernetHeader.TYPE_ARP

a.arp_hw = ArpPacket.HW_ETHER
a.ethernet_type = EthernetHeader.TYPE_IP
a.arp_opcode = ArpPacket.OPCODE_REQUEST
a.src_hw_addr = my_macaddr
a.src_prot_addr = my_ipaddr
a.dst_hw_addr = bd_macaddr
a.dst_prot_addr = bd_ipaddr

print "ARP REQUEST"
a.dump()

# Send ARP request, receive ARP reply
rawsocket.sendto(sock, a.bytes())
x = rawsocket.recvfrom(sock)

print "\nARP REPLY"
x = a.extract(x)
a.dump()

if x:


print len(x), "bytes left over"

def standard_arp_request(dst_ipaddr):
try:
sock = rawsocket.socket(EthernetHeader.TYPE_ARP)
except IOError:


print "Login as root, or you won't be able to make a raw socket"

raise IOError

a = ArpPacket()

eth = a.previous
eth.dst_mac = 0xFFFFFFFFFFFFL
eth.src_mac = my_macaddr
eth.packet_type = EthernetHeader.TYPE_ARP

a.arp_hw = ArpPacket.HW_ETHER
a.ethernet_type = EthernetHeader.TYPE_IP
a.arp_opcode = ArpPacket.OPCODE_REQUEST
a.src_hw_addr = my_macaddr
a.src_prot_addr = my_ipaddr
a.dst_hw_addr = 0xFFFFFFFFFFFFL
a.dst_prot_addr = dst_ipaddr
rawsocket.sendto(sock, a.bytes())
x = rawsocket.recvfrom(sock)
x = a.extract(x)
arptable[dst_ipaddr] = a.src_hw_addr


def test_udp(byte):

if not arptable.has_key(bd_ipaddr):
standard_arp_request(bd_ipaddr)

try:
sock = rawsocket.socket(EthernetHeader.TYPE_IP)
except IOError:


print "Login as root, or you won't be able to make a raw socket"

raise IOError

cmd = "\xff" + ((byte and "\x01") or "\x00")

u = UdpPacket(my_ipaddr, 43700,
bd_ipaddr, 5000, cmd)
print "IP COMMAND TO BOARD"
u.dump()
rawsocket.sendto(sock, u.bytes())
x = rawsocket.recvfrom(sock)
oldtype = u.previous.previous.packet_type
x = u.extract(x)
assert u.previous.previous.packet_type == oldtype
print "\nIP RESPONSE FROM BOARD"
u.dump()
if x:


print len(x), "bytes left over"


if len(sys.argv) > 1 and sys.argv[1] == "arp":
test_arp()
else:
byte = 0
if len(sys.argv) > 2:
byte = eval(sys.argv[2])
test_udp(byte)

# When I run "python packetfun.py udp 1", here is what happens.
# Because I've removed the board's MAC address from arptable, an ARP
# request is issued to the board, and the board sends back an ARP
# response whose only wierdness is that it has trailing bytes because
# I'm still a little ignorant about the CS8900A chip. Then a UDP
# command goes out, and the board sends back the expected UDP
# response. So far so good.

# Then a funny thing happens. The UDP response gets picked up by the
# Linux kernel, who looks at it and says, "hey, I never issued this
# port number, what's going on?" and the Linux kernel sends out an
# ICMP message saying, "there is no process listening on the bogus
# port number you're trying to reach". Technically the message is
# a "Destination unreachable" message. The board thinks it's being
# pinged, and sends back a ping response.

# Here's info on port allocation:
# http://www.cee.hw.ac.uk/courses/3ne3/5/9.htm
# http://ou800doc.caldera.com/SDK_netapi/sockC.addr_binding.html

0 new messages