-
Notifications
You must be signed in to change notification settings - Fork 5
/
convert.lua
202 lines (173 loc) · 8.03 KB
/
convert.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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
VERSION = 1 -- draft-ietf-tcpm-converters-06
HDR_LEN = 4
DEFAULT_PORT = 5124
TLV_TYPE_INFO = 1
TLV_TYPE_CONNECT = 10
TLV_TYPE_EXT_TCP_HDR = 20
TLV_TYPE_SUP_TCP_EXT = 21
TLV_TYPE_COOKIE = 22
TLV_TYPE_ERROR = 30
convert_prot = Proto('Convert', '0-RTT TCP Convert Protocol')
convert_prot.prefs.port = Pref.uint('port', DEFAULT_PORT, 'Converter Port')
version_f = ProtoField.uint8( 'convert.version', 'Version')
total_length_f = ProtoField.uint8( 'convert.total_length', 'Total Length')
unassigned_f = ProtoField.uint16('convert.unassigned', 'Unassigned')
tlv_f = ProtoField.bytes( 'convert.tlv', 'TLV')
tlv_type_f = ProtoField.uint8( 'convert.tlv_type', 'Type')
tlv_length_f = ProtoField.uint8( 'convert.tlv_length', 'Length')
tlv_value_f = ProtoField.bytes( 'convert.tlv_value', 'Value')
connect_port_f = ProtoField.uint16('convert.connect.port', 'Port')
connect_addr_f = ProtoField.ipv6( 'convert.connect.addr', 'Address')
connect_tcp_opts_f = ProtoField.bytes( 'convert.connect.tcp_optons', 'TCP Options')
ext_tcp_hdr_una_f = ProtoField.uint16('convert.ext_tcp_hdr.unassigned', 'Unassigned')
ext_tcp_hdr_hdr_f = ProtoField.bytes( 'convert.ext_tcp_hdr.tcp_header', 'TCP Header')
error_code_f = ProtoField.uint8( 'convert.error.code', 'Error Code')
error_value_f = ProtoField.bytes( 'convert.error.value', 'Value')
convert_prot.fields = {
version_f, total_length_f, unassigned_f,
tlv_f, tlv_type_f, tlv_length_f, tlv_value_f,
connect_port_f, connect_addr_f, connect_tcp_opts_f,
ext_tcp_hdr_una_f, ext_tcp_hdr_hdr_f,
error_code_f, error_value_f
}
tcp_stream_f = Field.new('tcp.stream')
ip_src_f = Field.new('ip.src')
ip_dst_f = Field.new('ip.dst')
ipv6_src_f = Field.new('ipv6.src')
ipv6_dst_f = Field.new('ipv6.dst')
tcp_srcport_f = Field.new('tcp.srcport')
tcp_dstport_f = Field.new('tcp.dstport')
tcp_syn_f = Field.new('tcp.flags.syn')
tcp_ack_f = Field.new('tcp.flags.ack')
tcp_len_f = Field.new('tcp.len')
-- For packet for both ipv6 and ipv4, we need to check its availability here
function get_ip_src()
local ip_src = ip_src_f()
local ipv6_src = ipv6_src_f()
if ip_src then
return ip_src
elseif ipv6_src then
return ipv6_src
end
end
-- For packet for both ipv6 and ipv4, we need to check its availability here
function get_ip_dst()
local ip_dst = ip_dst_f()
local ipv6_dst = ipv6_dst_f()
if ip_dst then
return ip_dst
elseif ipv6_dst then
return ipv6_dst
end
end
function get_tlv_name(tlv_type)
local tlv_name = 'Unknown TLV Type'
if tlv_type == TLV_TYPE_INFO then tlv_name = 'Info TLV'
elseif tlv_type == TLV_TYPE_CONNECT then tlv_name = 'Connect TLV'
elseif tlv_type == TLV_TYPE_EXT_TCP_HDR then tlv_name = 'Extended TCP Header TLV'
elseif tlv_type == TLV_TYPE_SUP_TCP_EXT then tlv_name = 'Supported TCP Extensions TLV'
elseif tlv_type == TLV_TYPE_COOKIE then tlv_name = 'Cookie TLV'
elseif tlv_type == TLV_TYPE_ERROR then tlv_name = 'Error TLV'
end
return tlv_name
end
function get_error_name(error_code)
local error_name = 'Unknown Error Code'
if error_code == 0 then error_name = 'Unsupported Version'
elseif error_code == 1 then error_name = 'Malformed Message'
elseif error_code == 2 then error_name = 'Unsupported Message'
elseif error_code == 3 then error_name = 'Missing Cookie'
elseif error_code == 32 then error_name = 'Not Authorized'
elseif error_code == 33 then error_name = 'Unsupported TCP Option'
elseif error_code == 64 then error_name = 'Resource Exceeded'
elseif error_code == 65 then error_name = 'Network Failure'
elseif error_code == 96 then error_name = 'Connection Reset'
elseif error_code == 97 then error_name = 'Destination Unreachable'
end
return error_name
end
function get_stream_dir_key()
return tostring(tcp_stream_f()) ..
tostring(get_ip_src()) ..
tostring(get_ip_dst()) ..
tostring(tcp_srcport_f()) ..
tostring(tcp_dstport_f())
end
function is_past_convert_msg(key, pkt_num)
return convert_end_pkt_num[key] and
pkt_num > convert_end_pkt_num[key]
end
function parse_tlv_value(tlv_tree, tlv_type, buffer, val_offset, val_length)
if tlv_type == TLV_TYPE_CONNECT then
tlv_tree:add(connect_port_f, buffer(val_offset, 2))
tlv_tree:add(connect_addr_f, buffer(val_offset + 2 ,16))
tlv_tree:add(connect_tcp_opts_f, buffer(val_offset + 18, val_length - 18))
elseif tlv_type == TLV_TYPE_EXT_TCP_HDR then
tlv_tree:add(ext_tcp_hdr_una_f, buffer(val_offset, 2))
tlv_tree:add(ext_tcp_hdr_hdr_f, buffer(val_offset + 2, val_length - 2))
elseif tlv_type == TLV_TYPE_ERROR then
local error_code = buffer(offset, 1):uint()
local error_name = get_error_name(error_code)
tlv_tree:add(error_code_f, buffer(val_offset, 1)):append_text(' (' .. error_name .. ')')
tlv_tree:add(error_value_f, buffer(val_offset + 1, val_length - 1))
else
tlv_tree:add(tlv_value_f, buffer(val_offset, val_length))
end
end
-- For a given TCP connection, we need to remember when we finished parsing the
-- Convert message in each direction. After that point the dissector is a NOOP.
-- We thus store the last pkt number that contained Convert protocol data in
-- each direction for each tcp.stream. For the moment, we assume the first pkt
-- with Convert data contains the full Convert message.
convert_end_pkt_num = {}
function convert_prot.dissector(buffer, pinfo, tree)
-- Empty TCP packets, cannot be Convert. Ignore.
local msg_length = buffer:len()
if msg_length == 0 then
return
end
-- Past the end of the Convert message. Ignore.
local stream_dir_key = get_stream_dir_key()
if is_past_convert_msg(stream_dir_key, pinfo.number) then
return
end
-- We are now parsing a Convert message.
pinfo.cols.protocol = convert_prot.name
local subtree = tree:add(convert_prot, buffer(), '0-RTT TCP Convert Protocol Data')
local version = buffer(0, 1):uint()
local total_length = buffer(1, 1):uint() * 4
-- Different version. Stop parsing this stream direction.
if version ~= VERSION then
convert_end_pkt_num[stream_dir_key] = pinfo.number
return
end
-- Parse Header.
subtree:add(version_f, buffer(0, 1))
subtree:add(total_length_f, buffer(1, 1)):append_text(' (' .. total_length .. ' Bytes)')
subtree:add(unassigned_f, buffer(2, 2))
-- Parse TLVs.
local offset = HDR_LEN
while offset < msg_length do
local tlv_type = buffer(offset, 1):uint()
local tlv_length = buffer(offset + 1, 1):uint()
local tlv_name = get_tlv_name(tlv_type)
local tlv_bytes = tlv_length * 4
tlv_tree = subtree:add(tlv_f, buffer(offset, tlv_bytes))
tlv_tree:set_text(tlv_name)
tlv_tree:add(tlv_type_f, buffer(offset, 1)):append_text(' (' .. tlv_name .. ')')
tlv_tree:add(tlv_length_f, buffer(offset + 1, 1)):append_text(' (' .. tlv_bytes .. ' Bytes)')
parse_tlv_value(tlv_tree, tlv_type, buffer, offset + 2, tlv_bytes - 2)
offset = offset + tlv_bytes
end
-- Mark end of Convert for stream direction.
convert_end_pkt_num[stream_dir_key] = pinfo.number
end
function convert_prot.prefs_changed()
tcp_port:remove(registered_port, convert_prot)
registered_port = convert_prot.prefs.port
tcp_port:add(registered_port, convert_prot)
end
-- Initial registration
tcp_port = DissectorTable.get('tcp.port')
registered_port = convert_prot.prefs.port
tcp_port:add(registered_port, convert_prot)