-
Notifications
You must be signed in to change notification settings - Fork 0
/
bpf.py
138 lines (105 loc) · 4.08 KB
/
bpf.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
import os
import fcntl
import struct
import select
BPF_FORMAT = "/dev/bpf%d"
def u_int2int(u_int):
return struct.unpack('i', struct.pack('I', u_int))[0]
BIOCIMMEDIATE = -2147204496
BIOCSETIF = -2145369492
BIOCGBLEN = 1074020966
BIOCPROMISC = 536887913
BPF_ALIGNMENT = 4
BPF_WORDALIGN = lambda x: (((x)+(BPF_ALIGNMENT-1))&~(BPF_ALIGNMENT-1))
class BPFPacket(object):
def __init__(self, header, data):
self.header = header
self.data = data
class BPFTimeout(Exception):
pass
def read_packet(fd, timeout=None):
blen = bpf_get_blen(fd)
if timeout is not None:
iwtd, owtd, ewtd = select.select([fd], [], [], timeout)
if not iwtd:
raise BPFTimeout()
buffer = os.read(fd, blen)
if len(buffer) > 0:
packet_index = 0
while packet_index < len(buffer):
header = get_header(buffer[packet_index:packet_index + 18])
if header.bh_caplen != header.bh_datalen:
print 'Packet fraction at BPF level. - skipped'
packet_index += BPF_WORDALIGN(header.bh_hdrlen + header.bh_caplen)
continue
data = buffer[packet_index + header.bh_hdrlen: \
packet_index + header.bh_caplen + header.bh_hdrlen]
return BPFPacket(header, data)
packet_index += BPF_WORDALIGN(header.bh_hdrlen + header.bh_caplen)
def packet_reader(fd, timeout=None):
blen = bpf_get_blen(fd)
while True:
if timeout is not None:
iwtd, owtd, ewtd = select.select([fd], [], [], timeout)
if not iwtd:
yield BPFTimeout()
continue
buffer = os.read(fd, blen)
if len(buffer) > 0:
packet_index = 0
while packet_index < len(buffer):
header = get_header(buffer[packet_index:packet_index + 18])
if header.bh_caplen != header.bh_datalen:
print 'Packet fraction at BPF level. - skipped'
packet_index += BPF_WORDALIGN(header.bh_hdrlen + header.bh_caplen)
continue
data = buffer[packet_index + header.bh_hdrlen: \
packet_index + header.bh_caplen + header.bh_hdrlen]
yield BPFPacket(header, data)
packet_index += BPF_WORDALIGN(header.bh_hdrlen + header.bh_caplen)
def get_bpf_fg(device, nonblocking=False, promiscuous=False):
fd = bpf_new()
bpf_set_immediate(fd, 1)
bpf_setif(fd, device)
if promiscuous:
bpf_set_promiscuous(fd)
if nonblocking:
fcntl.fcntl(fd, fcntl.F_SETFL, os.O_NONBLOCK) # nonblocking reading - slows down CPU
return fd
def bpf_new():
for i in xrange(10):
bpfdev = BPF_FORMAT % i
try:
os.stat(bpfdev)
except:
raise Exception('''Can't open BPF.''')
try:
fd = os.open(bpfdev, os.O_RDWR, 0)
return fd
except:
pass
raise Exception('''Can't open any BPF.''')
def bpf_set_promiscuous(fd):
return fcntl.ioctl(fd, BIOCPROMISC, '\x00' * 4)
def bpf_set_immediate(fd, value):
return fcntl.ioctl(fd, BIOCIMMEDIATE, struct.pack('I', value))
def bpf_dispose(bpf_fd):
return os.close(bpf_fd)
def bpf_setif(fd, en_name):
ifreq = en_name + '\0x0' * (32 - len(en_name));
return fcntl.ioctl(fd, BIOCSETIF, ifreq)
def bpf_get_blen(fd):
return struct.unpack('I', fcntl.ioctl(fd, BIOCGBLEN, " "))[0]
class BPFHeader(object):
def __init__(self, unpacked):
self._unpacked = unpacked
self.tv_sec = unpacked[0]
self.tv_usec = unpacked[1]
self.bh_caplen = unpacked[2]
self.bh_datalen = unpacked[3]
self.bh_hdrlen = unpacked[4]
def __str__(self):
return '''<BPFHeader tv_sec=%u tv_usec=%u bh_caplen=%u bh_datalen=%u bh_hdrlen=%u>''' % \
self._unpacked
def get_header(buffer):
return BPFHeader(struct.unpack('IIIIH', buffer))