-
Notifications
You must be signed in to change notification settings - Fork 39
/
Copy pathpf.lua
100 lines (92 loc) · 3.48 KB
/
pf.lua
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
module("pf",package.seeall)
local savefile = require("pf.savefile")
local types = require("pf.types")
local libpcap = require("pf.libpcap")
local bpf = require("pf.bpf")
local parse = require('pf.parse')
local expand = require('pf.expand')
local optimize = require('pf.optimize')
local anf = require('pf.anf')
local ssa = require('pf.ssa')
local backend = require('pf.backend')
local codegen = require('pf.codegen')
local utils = require('pf.utils')
-- TODO: rename the 'libpcap' option to reduce terminology overload
local compile_defaults = {
optimize=true, libpcap=false, bpf=false, source=false, native=false
}
function compile_filter(filter_str, opts)
local opts = utils.parse_opts(opts or {}, compile_defaults)
local dlt = opts.dlt or "EN10MB"
if opts.libpcap then
local bytecode = libpcap.compile(filter_str, dlt, opts.optimize)
if opts.source then return bpf.disassemble(bytecode) end
local header = types.pcap_pkthdr(0, 0, 0, 0)
return function(P, len)
header.incl_len = len
header.orig_len = len
return libpcap.offline_filter(bytecode, header, P) ~= 0
end
elseif opts.bpf then
local bytecode = libpcap.compile(filter_str, dlt, opts.optimize)
if opts.source then return bpf.compile_lua(bytecode) end
return bpf.compile(bytecode)
else -- pflua (to lua or native)
local expr = parse.parse(filter_str)
expr = expand.expand(expr, dlt)
if opts.optimize then expr = optimize.optimize(expr) end
expr = anf.convert_anf(expr)
expr = ssa.convert_ssa(expr)
if opts.native then
return codegen.load(expr, opts.source or false)
elseif opts.source then
return backend.emit_lua(expr)
else
return backend.emit_and_load(expr, filter_str)
end
end
end
function selftest ()
print("selftest: pf")
local function test_null(str)
local f1 = compile_filter(str, { libpcap = true })
local f2 = compile_filter(str, { bpf = true })
local f3 = compile_filter(str, {})
assert(f1(str, 0) == false, "null packet should be rejected (libpcap)")
assert(f2(str, 0) == false, "null packet should be rejected (bpf)")
assert(f3(str, 0) == false, "null packet should be rejected (pflua)")
end
test_null("icmp")
test_null("tcp port 80 and (((ip[2:2] - ((ip[0]&0xf)<<2)) - ((tcp[12]&0xf0)>>2)) != 0)")
local function assert_count(filter, packets, expected)
function count_matched(pred)
local matched = 0
for i=1,#packets do
if pred(packets[i].packet, packets[i].len) then
matched = matched + 1
end
end
return matched
end
local f1 = compile_filter(filter, { libpcap = true })
local f2 = compile_filter(filter, { bpf = true })
local f3 = compile_filter(filter, {})
local actual
actual = count_matched(f1)
assert(actual == expected,
'libpcap: got ' .. actual .. ', expected ' .. expected)
actual = count_matched(f2)
assert(actual == expected,
'bpf: got ' .. actual .. ', expected ' .. expected)
actual = count_matched(f3)
assert(actual == expected,
'pflua: got ' .. actual .. ', expected ' .. expected)
end
local v4 = savefile.load_packets("../tests/data/v4.pcap")
assert_count('', v4, 43)
assert_count('ip', v4, 43)
assert_count('tcp', v4, 41)
assert_count('tcp port 80', v4, 41)
compile_filter("ip[0] * ip[1] = 4", { bpf=true })
print("OK")
end