-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathexptrack.lua
157 lines (114 loc) · 3.83 KB
/
exptrack.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
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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
#!/usr/bin/env lua5.1
--[[
A userspace connection tracker for an example RPC-like echo service.
Assumes iptables rules have been setup appropriately.
See ECHO.txt for usage.
]]
-- FIXME remove assert, its too strong a failure
local function optional(package)
local ok, m = pcall(require, package)
if not ok then
m = nil
end
return m
end
local arg = assert(arg)
local assert = assert
local tonumber = tonumber
local tostring = tostring
local nfct = optional"nfct"
local nfq = optional"nfq"
module(...)
-- You can set these to make things print for debug purposes.
function debug() end
function verbose() end
--[[-
-- qhandle = exptrack.open(decodefn, queuenum)
Opens a netfilter queuenum and sets it up to catch packets.
The queue must previously have been setup using iptables rules. The default queue is 0.
]]
function open(decode, queuenum)
queuenum = assert(tonumber(queuenum or 0))
local qhandle = assert(nfq.open())
nfq.unbind_pf(qhandle, "inet")
nfq.bind_pf(qhandle, "inet")
local queue = assert(nfq.create_queue(qhandle, queuenum))
assert(nfq.set_mode(queue, "packet"))
return qhandle
end
--[[-
-- exptrack.catch(qhandle, decode)
The decode function will be passed an IPv4 packet to analyze. It can create
expectations by calling exptrack.expect().
]]
function catch(qhandle, decode)
return nfq.catch(qhandle, function (nfqdata)
debug("CB nfq")
local inip = assert(nfq.get_payload(nfqdata))
decode(inip)
return "accept"
end)
end
-- nfct helpers
function ctprint(ct, name, ...)
verbose("ct="..nfct.tostring(ct).." -- "..name, ...)
end
function expprint(exp, name, ...)
verbose("exp="..nfct.exp_tostring(exp).." -- "..name, ...)
end
function check(...)
if (...) then
return ...
end
local _, emsg, eno = ...
local emsg = "["..tostring(eno).."] "..tostring(emsg)
return assert(_, emsg)
end
function tuple(name, src, dst, sport, dport)
local ct = assert(nfct.new())
nfct.set_attr_pf(ct, "l3proto", "inet")
nfct.set_attr_ipv4(ct, "ipv4-src", src)
nfct.set_attr_ipv4(ct, "ipv4-dst", dst)
nfct.set_attr_ipproto(ct, "l4proto", "tcp")
if sport then
nfct.set_attr_port(ct, "port-src", sport)
end
nfct.set_attr_port(ct, "port-dst", dport)
ctprint(ct, name)
return ct
end
--[[-
-- exptrack.expect(src, dst, sport, dport, expectport, timeout, flags)
- src, dst are addresses of master connection, and will be used for expected
- sport, dport are ports of master connection
- expectport is destination port of expected connection
- timeout is how long the expectation will wait for a matching connection, in seconds
- flags defaults to nil, but can be "permanent"
FIXME assumes TCP! Would need a protocol argument to work with UDP.
]]
function expect(src, dst, sport, dport, expectport, timeout, flag)
if arg.expector then
return arg.expector(src, dst, sport, dport, expectport, timeout, flag)
end
-- identify the master to which this expectation is related
local master = tuple("master", src, dst, sport, dport)
local expected = tuple("expected", src, dst, nil, expectport)
local mask = tuple("mask", 0xffffffff, 0xffffffff, nil, expectport)
local timeout = timeout or 10
local exp = assert(nfct.exp_new(master, expected, mask, timeout, flag))
nfct.destroy(master)
nfct.destroy(expected)
nfct.destroy(mask)
expprint(exp, "expectation")
local h = assert(nfct.open("expect"))
-- FIXME this can fail if conntrack hasn't tracked the master... but is
-- that possible? we just got data from nfq, the connection must exist
local _, emsg, eno = nfct.exp_query(h, "create", exp)
if eno == 17 then
-- expectation already exists... normal?
else
check(_, emsg, eno)
end
nfct.exp_destroy(exp)
nfct.close(h)
end