]> git.ipfire.org Git - people/ms/suricata.git/blame - rust/src/dhcp/dhcp.rs
app-layer: include decoder events in app-layer tx data
[people/ms/suricata.git] / rust / src / dhcp / dhcp.rs
CommitLineData
2f5834cd 1/* Copyright (C) 2018-2020 Open Information Security Foundation
9210d874
JI
2 *
3 * You can copy, redistribute or modify this Program under the terms of
4 * the GNU General Public License version 2 as published by the Free
5 * Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * version 2 along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
15 * 02110-1301, USA.
16 */
17
2f5834cd 18use crate::applayer::{self, *};
42e5065a
JI
19use crate::core;
20use crate::core::{ALPROTO_UNKNOWN, AppProto, Flow, IPPROTO_UDP};
42e5065a 21use crate::dhcp::parser::*;
9210d874 22use std;
9c3f06d9 23use std::ffi::CString;
9210d874
JI
24
25static mut ALPROTO_DHCP: AppProto = ALPROTO_UNKNOWN;
26
27static DHCP_MIN_FRAME_LEN: u32 = 232;
28
29pub const BOOTP_REQUEST: u8 = 1;
30pub const BOOTP_REPLY: u8 = 2;
31
32// DHCP option types. Names based on IANA naming:
33// https://www.iana.org/assignments/bootp-dhcp-parameters/bootp-dhcp-parameters.xhtml
34pub const DHCP_OPT_SUBNET_MASK: u8 = 1;
35pub const DHCP_OPT_ROUTERS: u8 = 3;
36pub const DHCP_OPT_DNS_SERVER: u8 = 6;
37pub const DHCP_OPT_HOSTNAME: u8 = 12;
38pub const DHCP_OPT_REQUESTED_IP: u8 = 50;
39pub const DHCP_OPT_ADDRESS_TIME: u8 = 51;
40pub const DHCP_OPT_TYPE: u8 = 53;
41pub const DHCP_OPT_SERVER_ID: u8 = 54;
42pub const DHCP_OPT_PARAMETER_LIST: u8 = 55;
43pub const DHCP_OPT_RENEWAL_TIME: u8 = 58;
44pub const DHCP_OPT_REBINDING_TIME: u8 = 59;
45pub const DHCP_OPT_CLIENT_ID: u8 = 61;
46pub const DHCP_OPT_END: u8 = 255;
47
48/// DHCP message types.
49pub const DHCP_TYPE_DISCOVER: u8 = 1;
50pub const DHCP_TYPE_OFFER: u8 = 2;
51pub const DHCP_TYPE_REQUEST: u8 = 3;
52pub const DHCP_TYPE_DECLINE: u8 = 4;
53pub const DHCP_TYPE_ACK: u8 = 5;
54pub const DHCP_TYPE_NAK: u8 = 6;
55pub const DHCP_TYPE_RELEASE: u8 = 7;
56pub const DHCP_TYPE_INFORM: u8 = 8;
57
eb6cc629
JI
58// DHCP parameter types.
59// https://www.iana.org/assignments/bootp-dhcp-parameters/bootp-dhcp-parameters.txt
9210d874
JI
60pub const DHCP_PARAM_SUBNET_MASK: u8 = 1;
61pub const DHCP_PARAM_ROUTER: u8 = 3;
62pub const DHCP_PARAM_DNS_SERVER: u8 = 6;
63pub const DHCP_PARAM_DOMAIN: u8 = 15;
64pub const DHCP_PARAM_ARP_TIMEOUT: u8 = 35;
65pub const DHCP_PARAM_NTP_SERVER: u8 = 42;
66pub const DHCP_PARAM_TFTP_SERVER_NAME: u8 = 66;
67pub const DHCP_PARAM_TFTP_SERVER_IP: u8 = 150;
68
9c3f06d9 69#[derive(AppLayerEvent)]
9210d874 70pub enum DHCPEvent {
9c3f06d9 71 TruncatedOptions,
9210d874
JI
72 MalformedOptions,
73}
74
75/// The concept of a transaction is more to satisfy the Suricata
76/// app-layer. This DHCP parser is actually stateless where each
77/// message is its own transaction.
78pub struct DHCPTransaction {
79 tx_id: u64,
80 pub message: DHCPMessage,
a484bbbe 81 tx_data: applayer::AppLayerTxData,
9210d874
JI
82}
83
84impl DHCPTransaction {
85 pub fn new(id: u64, message: DHCPMessage) -> DHCPTransaction {
86 DHCPTransaction {
87 tx_id: id,
88 message: message,
a484bbbe 89 tx_data: applayer::AppLayerTxData::new(),
9210d874
JI
90 }
91 }
a337908c
VJ
92}
93
ac4c5ada
JI
94impl Transaction for DHCPTransaction {
95 fn id(&self) -> u64 {
96 self.tx_id
97 }
98}
99
aafb0a60 100#[derive(Default)]
9210d874
JI
101pub struct DHCPState {
102 // Internal transaction ID.
103 tx_id: u64,
104
105 // List of transactions.
106 transactions: Vec<DHCPTransaction>,
107
108 events: u16,
109}
110
ac4c5ada
JI
111impl State<DHCPTransaction> for DHCPState {
112 fn get_transactions(&self) -> &[DHCPTransaction] {
113 &self.transactions
114 }
115}
116
9210d874 117impl DHCPState {
aafb0a60
JL
118 pub fn new() -> Self {
119 Default::default()
9210d874
JI
120 }
121
122 pub fn parse(&mut self, input: &[u8]) -> bool {
123 match dhcp_parse(input) {
13b73997 124 Ok((_, message)) => {
9210d874
JI
125 let malformed_options = message.malformed_options;
126 let truncated_options = message.truncated_options;
127 self.tx_id += 1;
128 let transaction = DHCPTransaction::new(self.tx_id, message);
129 self.transactions.push(transaction);
130 if malformed_options {
131 self.set_event(DHCPEvent::MalformedOptions);
132 }
133 if truncated_options {
134 self.set_event(DHCPEvent::TruncatedOptions);
135 }
136 return true;
137 }
138 _ => {
139 return false;
140 }
141 }
142 }
143
144 pub fn get_tx(&mut self, tx_id: u64) -> Option<&DHCPTransaction> {
145 for tx in &mut self.transactions {
146 if tx.tx_id == tx_id + 1 {
147 return Some(tx);
148 }
149 }
150 return None;
151 }
152
153 fn free_tx(&mut self, tx_id: u64) {
154 let len = self.transactions.len();
155 let mut found = false;
156 let mut index = 0;
157 for i in 0..len {
158 let tx = &self.transactions[i];
159 if tx.tx_id == tx_id + 1 {
160 found = true;
161 index = i;
162 break;
163 }
164 }
165 if found {
166 self.transactions.remove(index);
167 }
168 }
169
170 fn set_event(&mut self, event: DHCPEvent) {
171 if let Some(tx) = self.transactions.last_mut() {
7732efbe 172 tx.tx_data.set_event(event as u8);
9210d874
JI
173 self.events += 1;
174 }
175 }
9210d874
JI
176}
177
178#[no_mangle]
363b5f99 179pub unsafe extern "C" fn rs_dhcp_probing_parser(_flow: *const Flow,
422e4892 180 _direction: u8,
bf1bd407 181 input: *const u8,
422e4892
VJ
182 input_len: u32,
183 _rdir: *mut u8) -> AppProto
184{
9210d874
JI
185 if input_len < DHCP_MIN_FRAME_LEN {
186 return ALPROTO_UNKNOWN;
187 }
188
189 let slice = build_slice!(input, input_len as usize);
190 match parse_header(slice) {
13b73997 191 Ok((_, _)) => {
363b5f99 192 return ALPROTO_DHCP;
9210d874
JI
193 }
194 _ => {
195 return ALPROTO_UNKNOWN;
196 }
197 }
198}
199
200#[no_mangle]
3f6624bf
VJ
201pub extern "C" fn rs_dhcp_tx_get_alstate_progress(_tx: *mut std::os::raw::c_void,
202 _direction: u8) -> std::os::raw::c_int {
9210d874
JI
203 // As this is a stateless parser, simply use 1.
204 return 1;
205}
206
9210d874 207#[no_mangle]
363b5f99 208pub unsafe extern "C" fn rs_dhcp_state_get_tx(state: *mut std::os::raw::c_void,
3f6624bf 209 tx_id: u64) -> *mut std::os::raw::c_void {
9210d874
JI
210 let state = cast_pointer!(state, DHCPState);
211 match state.get_tx(tx_id) {
212 Some(tx) => {
53413f2d 213 return tx as *const _ as *mut _;
9210d874
JI
214 }
215 None => {
216 return std::ptr::null_mut();
217 }
218 }
219}
220
221#[no_mangle]
363b5f99 222pub unsafe extern "C" fn rs_dhcp_state_get_tx_count(state: *mut std::os::raw::c_void) -> u64 {
9210d874
JI
223 let state = cast_pointer!(state, DHCPState);
224 return state.tx_id;
225}
226
227#[no_mangle]
363b5f99 228pub unsafe extern "C" fn rs_dhcp_parse(_flow: *const core::Flow,
3f6624bf
VJ
229 state: *mut std::os::raw::c_void,
230 _pstate: *mut std::os::raw::c_void,
bf1bd407 231 input: *const u8,
9210d874 232 input_len: u32,
3f6624bf 233 _data: *const std::os::raw::c_void,
44d3f264 234 _flags: u8) -> AppLayerResult {
9210d874
JI
235 let state = cast_pointer!(state, DHCPState);
236 let buf = build_slice!(input, input_len as usize);
237 if state.parse(buf) {
44d3f264 238 return AppLayerResult::ok();
9210d874 239 }
44d3f264 240 return AppLayerResult::err();
9210d874
JI
241}
242
243#[no_mangle]
363b5f99 244pub unsafe extern "C" fn rs_dhcp_state_tx_free(
3f6624bf 245 state: *mut std::os::raw::c_void,
bf1bd407 246 tx_id: u64)
9210d874
JI
247{
248 let state = cast_pointer!(state, DHCPState);
249 state.free_tx(tx_id);
250}
251
252#[no_mangle]
547d6c2d 253pub extern "C" fn rs_dhcp_state_new(_orig_state: *mut std::os::raw::c_void, _orig_proto: AppProto) -> *mut std::os::raw::c_void {
9210d874
JI
254 let state = DHCPState::new();
255 let boxed = Box::new(state);
53413f2d 256 return Box::into_raw(boxed) as *mut _;
9210d874
JI
257}
258
259#[no_mangle]
363b5f99
JI
260pub unsafe extern "C" fn rs_dhcp_state_free(state: *mut std::os::raw::c_void) {
261 std::mem::drop(Box::from_raw(state as *mut DHCPState));
9210d874
JI
262}
263
a484bbbe
VJ
264export_tx_data_get!(rs_dhcp_get_tx_data, DHCPTransaction);
265
9210d874
JI
266const PARSER_NAME: &'static [u8] = b"dhcp\0";
267
268#[no_mangle]
269pub unsafe extern "C" fn rs_dhcp_register_parser() {
b5bc5098 270 SCLogDebug!("Registering DHCP parser.");
9210d874
JI
271 let ports = CString::new("[67,68]").unwrap();
272 let parser = RustParser {
3f6624bf 273 name: PARSER_NAME.as_ptr() as *const std::os::raw::c_char,
e3ca6b43
JL
274 default_port : ports.as_ptr(),
275 ipproto : IPPROTO_UDP,
66632465
PA
276 probe_ts : Some(rs_dhcp_probing_parser),
277 probe_tc : Some(rs_dhcp_probing_parser),
e3ca6b43
JL
278 min_depth : 0,
279 max_depth : 16,
280 state_new : rs_dhcp_state_new,
281 state_free : rs_dhcp_state_free,
282 tx_free : rs_dhcp_state_tx_free,
283 parse_ts : rs_dhcp_parse,
284 parse_tc : rs_dhcp_parse,
285 get_tx_count : rs_dhcp_state_get_tx_count,
286 get_tx : rs_dhcp_state_get_tx,
efc9a7a3
VJ
287 tx_comp_st_ts : 1,
288 tx_comp_st_tc : 1,
e3ca6b43 289 tx_get_progress : rs_dhcp_tx_get_alstate_progress,
9c3f06d9
JI
290 get_eventinfo : Some(DHCPEvent::get_event_info),
291 get_eventinfo_byid : Some(DHCPEvent::get_event_info_by_id),
e3ca6b43
JL
292 localstorage_new : None,
293 localstorage_free : None,
e3ca6b43 294 get_files : None,
ac4c5ada 295 get_tx_iterator : Some(applayer::state_get_tx_iterator::<DHCPState, DHCPTransaction>),
c94a5e63 296 get_tx_data : rs_dhcp_get_tx_data,
5665fc83 297 apply_tx_config : None,
0529a00f 298 flags : APP_LAYER_PARSER_OPT_UNIDIR_TXS,
4da0d9bd 299 truncate : None,
9210d874
JI
300 };
301
302 let ip_proto_str = CString::new("udp").unwrap();
303
304 if AppLayerProtoDetectConfProtoDetectionEnabled(ip_proto_str.as_ptr(), parser.name) != 0 {
305 let alproto = AppLayerRegisterProtocolDetection(&parser, 1);
306 ALPROTO_DHCP = alproto;
307 if AppLayerParserConfParserEnabled(ip_proto_str.as_ptr(), parser.name) != 0 {
308 let _ = AppLayerRegisterParser(&parser, alproto);
309 }
310 } else {
311 SCLogDebug!("Protocol detector and parser disabled for DHCP.");
312 }
313}