Line | Count | Source |
1 | | // See the file "COPYING" in the main distribution directory for copyright. |
2 | | |
3 | | #include "zeek/Conn.h" |
4 | | |
5 | | #include <binpac.h> |
6 | | #include <cctype> |
7 | | #include <memory> |
8 | | |
9 | | #include "zeek/Desc.h" |
10 | | #include "zeek/ID.h" |
11 | | #include "zeek/NetVar.h" |
12 | | #include "zeek/Reporter.h" |
13 | | #include "zeek/RunState.h" |
14 | | #include "zeek/Timer.h" |
15 | | #include "zeek/TunnelEncapsulation.h" |
16 | | #include "zeek/WeirdState.h" |
17 | | #include "zeek/analyzer/Analyzer.h" |
18 | | #include "zeek/analyzer/Manager.h" |
19 | | #include "zeek/analyzer/protocol/pia/PIA.h" |
20 | | #include "zeek/iosource/Packet.h" |
21 | | #include "zeek/packet_analysis/protocol/ip/SessionAdapter.h" |
22 | | #include "zeek/packet_analysis/protocol/tcp/TCP.h" |
23 | | #include "zeek/session/Manager.h" |
24 | | |
25 | | namespace zeek { |
26 | | |
27 | | uint64_t Connection::total_connections = 0; |
28 | | uint64_t Connection::current_connections = 0; |
29 | | zeek::RecordValPtr Connection::conn_id_ctx_singleton; |
30 | | |
31 | 66 | void Connection::InitPostScript() { |
32 | 66 | if ( id::conn_id_ctx->NumFields() == 0 ) |
33 | 66 | conn_id_ctx_singleton = zeek::make_intrusive<zeek::RecordVal>(id::conn_id_ctx); |
34 | | |
35 | | // Double-check that the maximum tunnel changes script variable doesn't exceed the |
36 | | // maximum value allowed due to it being a uint8_t. |
37 | 66 | if ( zeek::detail::tunnel_max_changes_per_connection > 255 ) |
38 | 0 | reporter->Error("Tunnel::max_changes_per_connection is too large: %" PRIu64, |
39 | 0 | zeek::detail::tunnel_max_changes_per_connection); |
40 | 66 | } |
41 | | |
42 | | Connection::Connection(zeek::IPBasedConnKeyPtr k, double t, uint32_t flow, const Packet* pkt) |
43 | 399k | : Session(t, connection_timeout, connection_status_update, detail::connection_status_update_interval), |
44 | 399k | key(std::move(k)) { |
45 | 399k | orig_addr = key->SrcAddr(); |
46 | 399k | resp_addr = key->DstAddr(); |
47 | 399k | orig_port = key->SrcPort(); |
48 | 399k | resp_port = key->DstPort(); |
49 | 399k | proto = key->GetTransportProto(); |
50 | | |
51 | 399k | orig_flow_label = flow; |
52 | 399k | resp_flow_label = 0; |
53 | 399k | saw_first_orig_packet = true; |
54 | 399k | saw_first_resp_packet = false; |
55 | | |
56 | 399k | if ( pkt->l2_src ) |
57 | 1.69k | memcpy(orig_l2_addr, pkt->l2_src, sizeof(orig_l2_addr)); |
58 | 397k | else |
59 | 397k | memset(orig_l2_addr, 0, sizeof(orig_l2_addr)); |
60 | | |
61 | 399k | if ( pkt->l2_dst ) |
62 | 1.69k | memcpy(resp_l2_addr, pkt->l2_dst, sizeof(resp_l2_addr)); |
63 | 397k | else |
64 | 397k | memset(resp_l2_addr, 0, sizeof(resp_l2_addr)); |
65 | | |
66 | 399k | if ( pkt->vlan ) |
67 | 14 | vlan = pkt->vlan->id; |
68 | | |
69 | 399k | if ( pkt->inner_vlan ) |
70 | 0 | inner_vlan = pkt->inner_vlan->id; |
71 | | |
72 | 399k | weird = false; |
73 | | |
74 | 399k | suppress_event = 0; |
75 | | |
76 | 399k | finished = false; |
77 | | |
78 | 399k | adapter = nullptr; |
79 | 399k | primary_PIA = nullptr; |
80 | | |
81 | 399k | ++current_connections; |
82 | 399k | ++total_connections; |
83 | | |
84 | 399k | encapsulation = pkt->encap; |
85 | 399k | } |
86 | | |
87 | 399k | Connection::~Connection() { |
88 | 399k | if ( ! finished ) |
89 | 0 | reporter->InternalError("Done() not called before destruction of Connection"); |
90 | | |
91 | 399k | CancelTimers(); |
92 | | |
93 | 399k | if ( conn_val ) |
94 | 399k | conn_val->SetOrigin(nullptr); |
95 | | |
96 | 399k | delete adapter; |
97 | | |
98 | 399k | --current_connections; |
99 | 399k | } |
100 | | |
101 | 99.9k | void Connection::CheckEncapsulation(const std::shared_ptr<EncapsulationStack>& arg_encap) { |
102 | 99.9k | auto enqueue_tunnel_changed = [&](const auto& val) { |
103 | 2.86k | if ( ! tunnel_changed ) |
104 | 0 | return; |
105 | | |
106 | 2.86k | if ( zeek::detail::tunnel_max_changes_per_connection != 0 && |
107 | 2.86k | tunnel_changes >= zeek::detail::tunnel_max_changes_per_connection ) { |
108 | 1.22k | Weird("tunnel_max_changes_per_connection_exceeded", |
109 | 1.22k | util::fmt("%" PRIu64, zeek::detail::tunnel_max_changes_per_connection)); |
110 | 1.22k | CheckHistory(zeek::session::detail::HIST_ANALYZER_LIMIT_REACHED, 'X'); |
111 | 1.22k | return; |
112 | 1.22k | } |
113 | | |
114 | 1.63k | tunnel_changes++; |
115 | 1.63k | EnqueueEvent(tunnel_changed, nullptr, GetVal(), val); |
116 | 1.63k | }; |
117 | | |
118 | 99.9k | if ( encapsulation && arg_encap ) { |
119 | 4.27k | if ( *encapsulation != *arg_encap ) { |
120 | 2.19k | enqueue_tunnel_changed(arg_encap->ToVal()); |
121 | 2.19k | encapsulation = std::make_shared<EncapsulationStack>(*arg_encap); |
122 | 2.19k | } |
123 | 4.27k | } |
124 | | |
125 | 95.7k | else if ( encapsulation ) { |
126 | 314 | EncapsulationStack empty; |
127 | 314 | enqueue_tunnel_changed(empty.ToVal()); |
128 | 314 | encapsulation = nullptr; |
129 | 314 | } |
130 | | |
131 | 95.4k | else if ( arg_encap ) { |
132 | 353 | enqueue_tunnel_changed(arg_encap->ToVal()); |
133 | 353 | encapsulation = std::make_shared<EncapsulationStack>(*arg_encap); |
134 | 353 | } |
135 | 99.9k | } |
136 | | |
137 | 399k | void Connection::Done() { |
138 | 399k | finished = true; |
139 | | |
140 | 399k | if ( adapter ) { |
141 | 399k | if ( ConnTransport() == TRANSPORT_TCP ) { |
142 | 323k | auto* ta = static_cast<packet_analysis::TCP::TCPSessionAdapter*>(adapter); |
143 | 323k | assert(ta->IsAnalyzer("TCP")); |
144 | 323k | analyzer::tcp::TCP_Endpoint* to = ta->Orig(); |
145 | 323k | analyzer::tcp::TCP_Endpoint* tr = ta->Resp(); |
146 | | |
147 | 323k | packet_analysis::TCP::TCPAnalyzer::GetStats().StateLeft(to->state, tr->state); |
148 | 323k | } |
149 | | |
150 | 399k | if ( ! adapter->IsFinished() ) |
151 | 399k | adapter->Done(); |
152 | 399k | } |
153 | 399k | } |
154 | | |
155 | | void Connection::NextPacket(double t, bool is_orig, const IP_Hdr* ip, int len, int caplen, const u_char*& data, |
156 | | int& record_packet, int& record_content, |
157 | | // arguments for reproducing packets |
158 | 0 | const Packet* pkt) { |
159 | 0 | run_state::current_timestamp = t; |
160 | 0 | run_state::current_pkt = pkt; |
161 | |
|
162 | 0 | if ( adapter ) { |
163 | 0 | if ( adapter->Skipping() ) |
164 | 0 | return; |
165 | | |
166 | 0 | record_current_packet = record_packet; |
167 | 0 | record_current_content = record_content; |
168 | 0 | adapter->NextPacket(len, data, is_orig, -1, ip, caplen); |
169 | 0 | record_packet = record_current_packet; |
170 | 0 | record_content = record_current_content; |
171 | 0 | } |
172 | 0 | else |
173 | 0 | last_time = t; |
174 | | |
175 | 0 | run_state::current_timestamp = 0; |
176 | 0 | run_state::current_pkt = nullptr; |
177 | 0 | } |
178 | | |
179 | | |
180 | 15.2k | const ConnKey& Connection::Key() const { return *key; } |
181 | | |
182 | 511k | session::detail::Key Connection::SessionKey(bool copy) const { return key->SessionKey(); } |
183 | | |
184 | 1.02M | uint8_t Connection::KeyProto() const { return key->PackedTuple().proto; } |
185 | | |
186 | 208k | bool Connection::IsReuse(double t, const u_char* pkt) { return adapter && adapter->IsReuse(t, pkt); } |
187 | | |
188 | 302M | const RecordValPtr& Connection::GetVal() { |
189 | 302M | if ( ! conn_val ) { |
190 | 399k | conn_val = make_intrusive<RecordVal>(id::connection); |
191 | | |
192 | 399k | auto id_val = make_intrusive<RecordVal>(id::conn_id); |
193 | | |
194 | 399k | constexpr int ctx_offset = 5; |
195 | | |
196 | | // If the conn_id_ctx type has no fields at all, set it to the singleton instance, |
197 | | // otherwise the instance is initialized on first access through GetField() below. |
198 | 399k | if ( conn_id_ctx_singleton ) { |
199 | 399k | assert(id::conn_id_ctx->NumFields() == 0); |
200 | 399k | id_val->Assign(ctx_offset, conn_id_ctx_singleton); |
201 | 399k | } |
202 | | |
203 | 399k | auto ctx = id_val->GetField<zeek::RecordVal>(ctx_offset); |
204 | | |
205 | | // Allow customized ConnKeys to augment conn_id and ctx. |
206 | 399k | key->PopulateConnIdVal(*id_val, *ctx); |
207 | | |
208 | 399k | auto orig_endp = make_intrusive<RecordVal>(id::endpoint); |
209 | 399k | orig_endp->Assign(0, 0); |
210 | 399k | orig_endp->Assign(1, 0); |
211 | 399k | orig_endp->Assign(4, orig_flow_label); |
212 | | |
213 | 399k | const int l2_len = sizeof(orig_l2_addr); |
214 | 399k | char null[l2_len]{}; |
215 | | |
216 | 399k | if ( memcmp(&orig_l2_addr, &null, l2_len) != 0 ) { |
217 | 965 | auto [mac_bytes, mac_len] = fmt_mac_bytes(orig_l2_addr, l2_len); |
218 | 965 | orig_endp->Assign(5, new String(true, mac_bytes.release(), mac_len)); |
219 | 965 | } |
220 | | |
221 | 399k | auto resp_endp = make_intrusive<RecordVal>(id::endpoint); |
222 | 399k | resp_endp->Assign(0, 0); |
223 | 399k | resp_endp->Assign(1, 0); |
224 | 399k | resp_endp->Assign(4, resp_flow_label); |
225 | | |
226 | 399k | if ( memcmp(&resp_l2_addr, &null, l2_len) != 0 ) { |
227 | 971 | auto [mac_bytes, mac_len] = fmt_mac_bytes(resp_l2_addr, l2_len); |
228 | 971 | resp_endp->Assign(5, new String(true, mac_bytes.release(), mac_len)); |
229 | 971 | } |
230 | | |
231 | 399k | conn_val->Assign(0, std::move(id_val)); |
232 | 399k | conn_val->Assign(1, std::move(orig_endp)); |
233 | 399k | conn_val->Assign(2, std::move(resp_endp)); |
234 | | // 3 and 4 are set below. |
235 | | // Do not assign to 5 (service). It is a non-optional set, which will be default-initialized |
236 | | // using the script-level settings; this easily applies the &ordered attribute to it. |
237 | | // conn_val->Assign(5, make_intrusive<TableVal>(id::ordered_string_set)); // service |
238 | 399k | conn_val->Assign(6, val_mgr->EmptyString()); // history |
239 | | |
240 | 399k | if ( ! uid ) |
241 | 399k | uid.Set(zeek::detail::bits_per_uid); |
242 | | |
243 | 399k | conn_val->Assign(7, uid.Base62("C")); |
244 | | |
245 | 399k | if ( encapsulation && encapsulation->Depth() > 0 ) |
246 | 1.81k | conn_val->Assign(8, encapsulation->ToVal()); |
247 | | |
248 | 399k | if ( vlan ) |
249 | 14 | conn_val->Assign(9, vlan.value()); |
250 | | |
251 | 399k | if ( inner_vlan ) |
252 | 0 | conn_val->Assign(10, inner_vlan.value()); |
253 | 399k | } |
254 | | |
255 | 302M | if ( adapter ) |
256 | 302M | adapter->UpdateConnVal(conn_val.get()); |
257 | | |
258 | 302M | conn_val->AssignTime(3, start_time); // ### |
259 | 302M | conn_val->AssignInterval(4, last_time - start_time); |
260 | | |
261 | 302M | if ( ! history.empty() ) { |
262 | 33.2M | auto v = conn_val->GetFieldAs<StringVal>(6); |
263 | 33.2M | if ( *v != history ) |
264 | 431k | conn_val->Assign(6, history); |
265 | 33.2M | } |
266 | | |
267 | 302M | conn_val->SetOrigin(this); |
268 | | |
269 | 302M | return conn_val; |
270 | 302M | } |
271 | | |
272 | 344k | analyzer::Analyzer* Connection::FindAnalyzer(analyzer::ID id) { return adapter ? adapter->FindChild(id) : nullptr; } |
273 | | |
274 | 2.49M | analyzer::Analyzer* Connection::FindAnalyzer(const zeek::Tag& tag) { |
275 | 2.49M | return adapter ? adapter->FindChild(tag) : nullptr; |
276 | 2.49M | } |
277 | | |
278 | 4.42M | analyzer::Analyzer* Connection::FindAnalyzer(const char* name) { return adapter->FindChild(name); } |
279 | | |
280 | | void Connection::Match(detail::Rule::PatternType type, const u_char* data, int len, bool is_orig, bool bol, bool eol, |
281 | 40.9M | bool clear_state) { |
282 | 40.9M | if ( primary_PIA ) |
283 | 40.9M | primary_PIA->Match(type, data, len, is_orig, bol, eol, clear_state); |
284 | 40.9M | } |
285 | | |
286 | 399k | void Connection::RemovalEvent() { |
287 | 399k | if ( connection_state_remove ) |
288 | 399k | EnqueueEvent(connection_state_remove, nullptr, GetVal()); |
289 | 399k | } |
290 | | |
291 | 24.4M | void Connection::Weird(const char* name, const char* addl, const char* source) { |
292 | 24.4M | weird = true; |
293 | 24.4M | reporter->Weird(this, name, addl ? addl : "", source ? source : ""); |
294 | 24.4M | } |
295 | | |
296 | 55.8k | void Connection::LimitReachedWeird(const char* name, size_t limit, size_t limit_max, const char* source) { |
297 | 55.8k | const char* addl = limit_max > 0 ? util::fmt("%zu > %zu", limit, limit_max) : util::fmt("%zu", limit); |
298 | 55.8k | Weird(name, addl, source); |
299 | 55.8k | CheckHistory(zeek::session::detail::HIST_ANALYZER_LIMIT_REACHED, 'X'); |
300 | 55.8k | } |
301 | | |
302 | 81.1k | void Connection::FlipRoles() { |
303 | 81.1k | IPAddr tmp_addr = resp_addr; |
304 | 81.1k | resp_addr = orig_addr; |
305 | 81.1k | orig_addr = tmp_addr; |
306 | | |
307 | 81.1k | uint32_t tmp_port = resp_port; |
308 | 81.1k | resp_port = orig_port; |
309 | 81.1k | orig_port = tmp_port; |
310 | | |
311 | 81.1k | const int l2_len = sizeof(orig_l2_addr); |
312 | 81.1k | u_char tmp_l2_addr[l2_len]; |
313 | 81.1k | memcpy(tmp_l2_addr, resp_l2_addr, l2_len); |
314 | 81.1k | memcpy(resp_l2_addr, orig_l2_addr, l2_len); |
315 | 81.1k | memcpy(orig_l2_addr, tmp_l2_addr, l2_len); |
316 | | |
317 | 81.1k | bool tmp_bool = saw_first_resp_packet; |
318 | 81.1k | saw_first_resp_packet = saw_first_orig_packet; |
319 | 81.1k | saw_first_orig_packet = tmp_bool; |
320 | | |
321 | 81.1k | uint32_t tmp_flow = resp_flow_label; |
322 | 81.1k | resp_flow_label = orig_flow_label; |
323 | 81.1k | orig_flow_label = tmp_flow; |
324 | | |
325 | 81.1k | if ( conn_val ) { |
326 | | // Delegate flipping of conn_id and ctx records to the key instance. |
327 | 4.43k | auto id_val = conn_val->GetField<zeek::RecordVal>(0); |
328 | 4.43k | auto* ctx = id_val->GetFieldAs<zeek::RecordVal>(5); |
329 | 4.43k | key->FlipRoles(*id_val, *ctx); |
330 | | |
331 | | // Flip the connection's endpoints |
332 | 4.43k | const auto& tmp_endp = conn_val->GetField<zeek::RecordVal>(1); |
333 | 4.43k | conn_val->Assign(1, conn_val->GetField(2)); |
334 | 4.43k | conn_val->Assign(2, tmp_endp); |
335 | 4.43k | } |
336 | 76.6k | else { |
337 | | // Even we haven't yet allocated a connection value, still need to flip the key's |
338 | | // idea of originator and responder |
339 | 76.6k | key->FlipRoles(); |
340 | 76.6k | } |
341 | | |
342 | 81.1k | if ( adapter ) |
343 | 4.43k | adapter->FlipRoles(); |
344 | | |
345 | 81.1k | analyzer_mgr->ApplyScheduledAnalyzers(this); |
346 | | |
347 | 81.1k | AddHistory('^'); |
348 | | |
349 | 81.1k | if ( connection_flipped ) |
350 | 81.1k | EnqueueEvent(connection_flipped, nullptr, GetVal()); |
351 | 81.1k | } |
352 | | |
353 | 0 | void Connection::Describe(ODesc* d) const { |
354 | 0 | session::Session::Describe(d); |
355 | |
|
356 | 0 | switch ( proto ) { |
357 | 0 | case TRANSPORT_TCP: d->Add("TCP"); break; |
358 | | |
359 | 0 | case TRANSPORT_UDP: d->Add("UDP"); break; |
360 | | |
361 | 0 | case TRANSPORT_ICMP: d->Add("ICMP"); break; |
362 | | |
363 | 0 | case TRANSPORT_UNKNOWN: |
364 | 0 | d->Add("unknown"); |
365 | 0 | reporter->InternalWarning("unknown transport in Connection::Describe()"); |
366 | |
|
367 | 0 | break; |
368 | | |
369 | 0 | default: reporter->InternalError("unhandled transport type in Connection::Describe"); |
370 | 0 | } |
371 | | |
372 | 0 | d->SP(); |
373 | 0 | d->Add(orig_addr); |
374 | 0 | d->Add(":"); |
375 | 0 | d->Add(ntohs(orig_port)); |
376 | |
|
377 | 0 | d->SP(); |
378 | 0 | d->AddSP("->"); |
379 | |
|
380 | 0 | d->Add(resp_addr); |
381 | 0 | d->Add(":"); |
382 | 0 | d->Add(ntohs(resp_port)); |
383 | |
|
384 | 0 | d->NL(); |
385 | 0 | } |
386 | | |
387 | 143k | void Connection::IDString(ODesc* d) const { |
388 | 143k | d->Add(orig_addr); |
389 | 143k | d->AddRaw(":", 1); |
390 | 143k | d->Add(ntohs(orig_port)); |
391 | 143k | d->AddRaw(" > ", 3); |
392 | 143k | d->Add(resp_addr); |
393 | 143k | d->AddRaw(":", 1); |
394 | 143k | d->Add(ntohs(resp_port)); |
395 | 143k | } |
396 | | |
397 | 399k | void Connection::SetSessionAdapter(packet_analysis::IP::SessionAdapter* aa, analyzer::pia::PIA* pia) { |
398 | 399k | adapter = aa; |
399 | 399k | primary_PIA = pia; |
400 | 399k | } |
401 | | |
402 | 294k | void Connection::CheckFlowLabel(bool is_orig, uint32_t flow_label) { |
403 | 294k | uint32_t& my_flow_label = is_orig ? orig_flow_label : resp_flow_label; |
404 | | |
405 | 294k | if ( my_flow_label != flow_label ) { |
406 | 3.37k | if ( conn_val ) { |
407 | 3.37k | RecordVal* endp = conn_val->GetFieldAs<RecordVal>(is_orig ? 1 : 2); |
408 | 3.37k | endp->Assign(4, flow_label); |
409 | 3.37k | } |
410 | | |
411 | 3.37k | if ( connection_flow_label_changed && (is_orig ? saw_first_orig_packet : saw_first_resp_packet) ) { |
412 | 1.49k | EnqueueEvent(connection_flow_label_changed, nullptr, GetVal(), val_mgr->Bool(is_orig), |
413 | 1.49k | val_mgr->Count(my_flow_label), val_mgr->Count(flow_label)); |
414 | 1.49k | } |
415 | | |
416 | 3.37k | my_flow_label = flow_label; |
417 | 3.37k | } |
418 | | |
419 | 294k | if ( is_orig ) |
420 | 174k | saw_first_orig_packet = true; |
421 | 119k | else |
422 | 119k | saw_first_resp_packet = true; |
423 | 294k | } |
424 | | |
425 | 30.1M | bool Connection::PermitWeird(const char* name, uint64_t threshold, uint64_t rate, double duration) { |
426 | 30.1M | if ( ! weird_state ) |
427 | 274k | weird_state = std::make_unique<detail::WeirdStateMap>(); |
428 | | |
429 | 30.1M | return detail::PermitWeird(*weird_state, name, threshold, rate, duration); |
430 | 30.1M | } |
431 | | |
432 | | } // namespace zeek |