mirror of https://github.com/OISF/suricata
rust/dhcp: Rust based DHCP decoder and logger.
This is a DHCP decoder and logger written in Rust. Unlike most parsers, this one is stateless so responses are not matched up to requests by Suricata. However, the output does contain enough fields to match them up in post-processing. Rules are included to alert of malformed or truncated options.pull/3395/head
parent
1b0b74dc16
commit
9210d8743b
@ -0,0 +1,4 @@
|
||||
These test pcap files are individual packets broken out of a pcap
|
||||
containing 4 DHCP messages. The original source of the PCAP file is
|
||||
|
||||
https://wiki.wireshark.org/SampleCaptures?action=AttachFile&do=get&target=dhcp.pcap
|
||||
Binary file not shown.
@ -0,0 +1,420 @@
|
||||
/* Copyright (C) 2018 Open Information Security Foundation
|
||||
*
|
||||
* You can copy, redistribute or modify this Program under the terms of
|
||||
* the GNU General Public License version 2 as published by the Free
|
||||
* Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* version 2 along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*/
|
||||
|
||||
use applayer;
|
||||
use core;
|
||||
use core::{ALPROTO_UNKNOWN, AppProto, Flow};
|
||||
use dhcp::parser::*;
|
||||
use libc;
|
||||
use log::*;
|
||||
use nom;
|
||||
use parser::*;
|
||||
use std;
|
||||
use std::ffi::{CStr,CString};
|
||||
use std::mem::transmute;
|
||||
|
||||
static mut ALPROTO_DHCP: AppProto = ALPROTO_UNKNOWN;
|
||||
|
||||
static DHCP_MIN_FRAME_LEN: u32 = 232;
|
||||
|
||||
pub const BOOTP_REQUEST: u8 = 1;
|
||||
pub const BOOTP_REPLY: u8 = 2;
|
||||
|
||||
// DHCP option types. Names based on IANA naming:
|
||||
// https://www.iana.org/assignments/bootp-dhcp-parameters/bootp-dhcp-parameters.xhtml
|
||||
pub const DHCP_OPT_SUBNET_MASK: u8 = 1;
|
||||
pub const DHCP_OPT_ROUTERS: u8 = 3;
|
||||
pub const DHCP_OPT_DNS_SERVER: u8 = 6;
|
||||
pub const DHCP_OPT_HOSTNAME: u8 = 12;
|
||||
pub const DHCP_OPT_REQUESTED_IP: u8 = 50;
|
||||
pub const DHCP_OPT_ADDRESS_TIME: u8 = 51;
|
||||
pub const DHCP_OPT_TYPE: u8 = 53;
|
||||
pub const DHCP_OPT_SERVER_ID: u8 = 54;
|
||||
pub const DHCP_OPT_PARAMETER_LIST: u8 = 55;
|
||||
pub const DHCP_OPT_RENEWAL_TIME: u8 = 58;
|
||||
pub const DHCP_OPT_REBINDING_TIME: u8 = 59;
|
||||
pub const DHCP_OPT_CLIENT_ID: u8 = 61;
|
||||
pub const DHCP_OPT_END: u8 = 255;
|
||||
|
||||
/// DHCP message types.
|
||||
pub const DHCP_TYPE_DISCOVER: u8 = 1;
|
||||
pub const DHCP_TYPE_OFFER: u8 = 2;
|
||||
pub const DHCP_TYPE_REQUEST: u8 = 3;
|
||||
pub const DHCP_TYPE_DECLINE: u8 = 4;
|
||||
pub const DHCP_TYPE_ACK: u8 = 5;
|
||||
pub const DHCP_TYPE_NAK: u8 = 6;
|
||||
pub const DHCP_TYPE_RELEASE: u8 = 7;
|
||||
pub const DHCP_TYPE_INFORM: u8 = 8;
|
||||
|
||||
/// DHCP parameter types.
|
||||
/// https://www.iana.org/assignments/bootp-dhcp-parameters/bootp-dhcp-parameters.txt
|
||||
pub const DHCP_PARAM_SUBNET_MASK: u8 = 1;
|
||||
pub const DHCP_PARAM_ROUTER: u8 = 3;
|
||||
pub const DHCP_PARAM_DNS_SERVER: u8 = 6;
|
||||
pub const DHCP_PARAM_DOMAIN: u8 = 15;
|
||||
pub const DHCP_PARAM_ARP_TIMEOUT: u8 = 35;
|
||||
pub const DHCP_PARAM_NTP_SERVER: u8 = 42;
|
||||
pub const DHCP_PARAM_TFTP_SERVER_NAME: u8 = 66;
|
||||
pub const DHCP_PARAM_TFTP_SERVER_IP: u8 = 150;
|
||||
|
||||
#[repr(u32)]
|
||||
pub enum DHCPEvent {
|
||||
TruncatedOptions = 0,
|
||||
MalformedOptions,
|
||||
}
|
||||
|
||||
/// The concept of a transaction is more to satisfy the Suricata
|
||||
/// app-layer. This DHCP parser is actually stateless where each
|
||||
/// message is its own transaction.
|
||||
pub struct DHCPTransaction {
|
||||
tx_id: u64,
|
||||
pub message: DHCPMessage,
|
||||
logged: applayer::LoggerFlags,
|
||||
de_state: Option<*mut core::DetectEngineState>,
|
||||
events: *mut core::AppLayerDecoderEvents,
|
||||
}
|
||||
|
||||
impl DHCPTransaction {
|
||||
pub fn new(id: u64, message: DHCPMessage) -> DHCPTransaction {
|
||||
DHCPTransaction {
|
||||
tx_id: id,
|
||||
message: message,
|
||||
logged: applayer::LoggerFlags::new(),
|
||||
de_state: None,
|
||||
events: std::ptr::null_mut(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export_tx_get_detect_state!(rs_dhcp_tx_get_detect_state, DHCPTransaction);
|
||||
export_tx_set_detect_state!(rs_dhcp_tx_set_detect_state, DHCPTransaction);
|
||||
|
||||
pub struct DHCPState {
|
||||
// Internal transaction ID.
|
||||
tx_id: u64,
|
||||
|
||||
// List of transactions.
|
||||
transactions: Vec<DHCPTransaction>,
|
||||
|
||||
events: u16,
|
||||
}
|
||||
|
||||
impl DHCPState {
|
||||
pub fn new() -> DHCPState {
|
||||
return DHCPState {
|
||||
tx_id: 0,
|
||||
transactions: Vec::new(),
|
||||
events: 0,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn parse(&mut self, input: &[u8]) -> bool {
|
||||
match dhcp_parse(input) {
|
||||
nom::IResult::Done(_, message) => {
|
||||
let malformed_options = message.malformed_options;
|
||||
let truncated_options = message.truncated_options;
|
||||
self.tx_id += 1;
|
||||
let transaction = DHCPTransaction::new(self.tx_id, message);
|
||||
self.transactions.push(transaction);
|
||||
if malformed_options {
|
||||
self.set_event(DHCPEvent::MalformedOptions);
|
||||
}
|
||||
if truncated_options {
|
||||
self.set_event(DHCPEvent::TruncatedOptions);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
_ => {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_tx(&mut self, tx_id: u64) -> Option<&DHCPTransaction> {
|
||||
for tx in &mut self.transactions {
|
||||
if tx.tx_id == tx_id + 1 {
|
||||
return Some(tx);
|
||||
}
|
||||
}
|
||||
return None;
|
||||
}
|
||||
|
||||
fn free_tx(&mut self, tx_id: u64) {
|
||||
let len = self.transactions.len();
|
||||
let mut found = false;
|
||||
let mut index = 0;
|
||||
for i in 0..len {
|
||||
let tx = &self.transactions[i];
|
||||
if tx.tx_id == tx_id + 1 {
|
||||
found = true;
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if found {
|
||||
self.transactions.remove(index);
|
||||
}
|
||||
}
|
||||
|
||||
fn set_event(&mut self, event: DHCPEvent) {
|
||||
if let Some(tx) = self.transactions.last_mut() {
|
||||
core::sc_app_layer_decoder_events_set_event_raw(
|
||||
&mut tx.events, event as u8);
|
||||
self.events += 1;
|
||||
}
|
||||
}
|
||||
|
||||
fn get_tx_iterator(&mut self, min_tx_id: u64, state: &mut u64) ->
|
||||
Option<(&DHCPTransaction, u64, bool)>
|
||||
{
|
||||
let mut index = *state as usize;
|
||||
let len = self.transactions.len();
|
||||
|
||||
while index < len {
|
||||
let tx = &self.transactions[index];
|
||||
if tx.tx_id < min_tx_id + 1 {
|
||||
index += 1;
|
||||
continue;
|
||||
}
|
||||
*state = index as u64 + 1;
|
||||
return Some((tx, tx.tx_id - 1, (len - index) > 1));
|
||||
}
|
||||
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn rs_dhcp_probing_parser(_flow: *const Flow,
|
||||
input: *const libc::uint8_t,
|
||||
input_len: u32,
|
||||
_offset: *const u32) -> AppProto {
|
||||
if input_len < DHCP_MIN_FRAME_LEN {
|
||||
return ALPROTO_UNKNOWN;
|
||||
}
|
||||
|
||||
let slice = build_slice!(input, input_len as usize);
|
||||
match parse_header(slice) {
|
||||
nom::IResult::Done(_, _) => {
|
||||
return unsafe { ALPROTO_DHCP };
|
||||
}
|
||||
_ => {
|
||||
return ALPROTO_UNKNOWN;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn rs_dhcp_tx_get_alstate_progress(_tx: *mut libc::c_void,
|
||||
_direction: libc::uint8_t) -> libc::c_int {
|
||||
// As this is a stateless parser, simply use 1.
|
||||
return 1;
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn rs_dhcp_state_progress_completion_status(
|
||||
_direction: libc::uint8_t) -> libc::c_int {
|
||||
// The presence of a transaction means we are complete.
|
||||
return 1;
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn rs_dhcp_state_get_tx(state: *mut libc::c_void,
|
||||
tx_id: libc::uint64_t) -> *mut libc::c_void {
|
||||
let state = cast_pointer!(state, DHCPState);
|
||||
match state.get_tx(tx_id) {
|
||||
Some(tx) => {
|
||||
return unsafe { transmute(tx) };
|
||||
}
|
||||
None => {
|
||||
return std::ptr::null_mut();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn rs_dhcp_state_get_tx_count(state: *mut libc::c_void) -> libc::uint64_t {
|
||||
let state = cast_pointer!(state, DHCPState);
|
||||
return state.tx_id;
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn rs_dhcp_parse(_flow: *const core::Flow,
|
||||
state: *mut libc::c_void,
|
||||
_pstate: *mut libc::c_void,
|
||||
input: *const libc::uint8_t,
|
||||
input_len: u32,
|
||||
_data: *const libc::c_void) -> i8 {
|
||||
let state = cast_pointer!(state, DHCPState);
|
||||
let buf = build_slice!(input, input_len as usize);
|
||||
if state.parse(buf) {
|
||||
return 1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn rs_dhcp_state_tx_free(
|
||||
state: *mut libc::c_void,
|
||||
tx_id: libc::uint64_t)
|
||||
{
|
||||
let state = cast_pointer!(state, DHCPState);
|
||||
state.free_tx(tx_id);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn rs_dhcp_state_new() -> *mut libc::c_void {
|
||||
let state = DHCPState::new();
|
||||
let boxed = Box::new(state);
|
||||
return unsafe {
|
||||
transmute(boxed)
|
||||
};
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn rs_dhcp_state_free(state: *mut libc::c_void) {
|
||||
// Just unbox...
|
||||
let _drop: Box<DHCPState> = unsafe { transmute(state) };
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn rs_dhcp_tx_get_logged(_state: *mut libc::c_void, tx: *mut libc::c_void) -> u32 {
|
||||
let tx = cast_pointer!(tx, DHCPTransaction);
|
||||
return tx.logged.get();
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn rs_dhcp_tx_set_logged(_state: *mut libc::c_void,
|
||||
tx: *mut libc::c_void,
|
||||
logged: libc::uint32_t) {
|
||||
let tx = cast_pointer!(tx, DHCPTransaction);
|
||||
tx.logged.set(logged);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn rs_dhcp_state_get_events(state: *mut libc::c_void,
|
||||
tx_id: libc::uint64_t)
|
||||
-> *mut core::AppLayerDecoderEvents
|
||||
{
|
||||
let state = cast_pointer!(state, DHCPState);
|
||||
match state.get_tx(tx_id) {
|
||||
Some(tx) => tx.events,
|
||||
_ => std::ptr::null_mut(),
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn rs_dhcp_state_get_event_info(
|
||||
event_name: *const libc::c_char,
|
||||
event_id: *mut libc::c_int,
|
||||
event_type: *mut core::AppLayerEventType)
|
||||
-> libc::c_int
|
||||
{
|
||||
if event_name == std::ptr::null() {
|
||||
return -1;
|
||||
}
|
||||
let c_event_name: &CStr = unsafe { CStr::from_ptr(event_name) };
|
||||
let event = match c_event_name.to_str() {
|
||||
Ok(s) => {
|
||||
match s {
|
||||
"malformed_options" => DHCPEvent::MalformedOptions as i32,
|
||||
"truncated_options" => DHCPEvent::TruncatedOptions as i32,
|
||||
_ => -1, // unknown event
|
||||
}
|
||||
},
|
||||
Err(_) => -1, // UTF-8 conversion failed
|
||||
};
|
||||
unsafe{
|
||||
*event_type = core::APP_LAYER_EVENT_TYPE_TRANSACTION;
|
||||
*event_id = event as libc::c_int;
|
||||
};
|
||||
0
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn rs_dhcp_state_get_tx_iterator(
|
||||
_ipproto: libc::uint8_t,
|
||||
_alproto: AppProto,
|
||||
state: *mut libc::c_void,
|
||||
min_tx_id: libc::uint64_t,
|
||||
_max_tx_id: libc::uint64_t,
|
||||
istate: &mut libc::uint64_t)
|
||||
-> applayer::AppLayerGetTxIterTuple
|
||||
{
|
||||
let state = cast_pointer!(state, DHCPState);
|
||||
match state.get_tx_iterator(min_tx_id, istate) {
|
||||
Some((tx, out_tx_id, has_next)) => {
|
||||
let c_tx = unsafe { transmute(tx) };
|
||||
let ires = applayer::AppLayerGetTxIterTuple::with_values(
|
||||
c_tx, out_tx_id, has_next);
|
||||
return ires;
|
||||
}
|
||||
None => {
|
||||
return applayer::AppLayerGetTxIterTuple::not_found();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const PARSER_NAME: &'static [u8] = b"dhcp\0";
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn rs_dhcp_register_parser() {
|
||||
SCLogNotice!("Registering DHCP parser.");
|
||||
let ports = CString::new("[67,68]").unwrap();
|
||||
let parser = RustParser {
|
||||
name: PARSER_NAME.as_ptr() as *const libc::c_char,
|
||||
default_port: ports.as_ptr(),
|
||||
ipproto: libc::IPPROTO_UDP,
|
||||
probe_ts: rs_dhcp_probing_parser,
|
||||
probe_tc: rs_dhcp_probing_parser,
|
||||
min_depth: 0,
|
||||
max_depth: 16,
|
||||
state_new: rs_dhcp_state_new,
|
||||
state_free: rs_dhcp_state_free,
|
||||
tx_free: rs_dhcp_state_tx_free,
|
||||
parse_ts: rs_dhcp_parse,
|
||||
parse_tc: rs_dhcp_parse,
|
||||
get_tx_count: rs_dhcp_state_get_tx_count,
|
||||
get_tx: rs_dhcp_state_get_tx,
|
||||
tx_get_comp_st: rs_dhcp_state_progress_completion_status,
|
||||
tx_get_progress: rs_dhcp_tx_get_alstate_progress,
|
||||
get_tx_logged: Some(rs_dhcp_tx_get_logged),
|
||||
set_tx_logged: Some(rs_dhcp_tx_set_logged),
|
||||
get_de_state: rs_dhcp_tx_get_detect_state,
|
||||
set_de_state: rs_dhcp_tx_set_detect_state,
|
||||
get_events: Some(rs_dhcp_state_get_events),
|
||||
get_eventinfo: Some(rs_dhcp_state_get_event_info),
|
||||
localstorage_new: None,
|
||||
localstorage_free: None,
|
||||
get_tx_mpm_id: None,
|
||||
set_tx_mpm_id: None,
|
||||
get_files: None,
|
||||
get_tx_iterator: Some(rs_dhcp_state_get_tx_iterator),
|
||||
};
|
||||
|
||||
let ip_proto_str = CString::new("udp").unwrap();
|
||||
|
||||
if AppLayerProtoDetectConfProtoDetectionEnabled(ip_proto_str.as_ptr(), parser.name) != 0 {
|
||||
let alproto = AppLayerRegisterProtocolDetection(&parser, 1);
|
||||
ALPROTO_DHCP = alproto;
|
||||
if AppLayerParserConfParserEnabled(ip_proto_str.as_ptr(), parser.name) != 0 {
|
||||
let _ = AppLayerRegisterParser(&parser, alproto);
|
||||
}
|
||||
} else {
|
||||
SCLogDebug!("Protocol detector and parser disabled for DHCP.");
|
||||
}
|
||||
}
|
||||
Binary file not shown.
@ -0,0 +1,280 @@
|
||||
/* Copyright (C) 2018 Open Information Security Foundation
|
||||
*
|
||||
* You can copy, redistribute or modify this Program under the terms of
|
||||
* the GNU General Public License version 2 as published by the Free
|
||||
* Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* version 2 along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*/
|
||||
|
||||
extern crate libc;
|
||||
|
||||
use std;
|
||||
use std::os::raw::c_void;
|
||||
|
||||
use dhcp::dhcp::*;
|
||||
use dhcp::parser::{DHCPOptionWrapper,DHCPOptGeneric};
|
||||
use dns::log::dns_print_addr;
|
||||
use json::*;
|
||||
use conf::ConfNode;
|
||||
|
||||
pub struct DHCPLogger {
|
||||
extended: bool,
|
||||
}
|
||||
|
||||
impl DHCPLogger {
|
||||
|
||||
pub fn new(conf: ConfNode) -> DHCPLogger {
|
||||
return DHCPLogger{
|
||||
extended: conf.get_child_bool("extended"),
|
||||
};
|
||||
}
|
||||
|
||||
fn get_type(&self, tx: &DHCPTransaction) -> Option<u8> {
|
||||
let options = &tx.message.options;
|
||||
for option in options {
|
||||
let code = option.code;
|
||||
match &option.option {
|
||||
&DHCPOptionWrapper::Generic(ref option) => {
|
||||
match code {
|
||||
DHCP_OPT_TYPE => {
|
||||
if option.data.len() > 0 {
|
||||
return Some(option.data[0]);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
return None;
|
||||
}
|
||||
|
||||
fn do_log(&self, tx: &DHCPTransaction) -> bool {
|
||||
if !self.extended {
|
||||
match self.get_type(tx) {
|
||||
Some(t) => {
|
||||
match t {
|
||||
DHCP_TYPE_ACK => {
|
||||
return true;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
pub fn log(&self, tx: &DHCPTransaction) -> Option<Json> {
|
||||
if !self.do_log(tx) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let header = &tx.message.header;
|
||||
let options = &tx.message.options;
|
||||
let js = Json::object();
|
||||
|
||||
match header.opcode {
|
||||
BOOTP_REQUEST => {
|
||||
js.set_string("type", "request");
|
||||
}
|
||||
BOOTP_REPLY => {
|
||||
js.set_string("type", "reply");
|
||||
}
|
||||
_ => {
|
||||
js.set_string("type", "<unknown>");
|
||||
}
|
||||
}
|
||||
|
||||
js.set_integer("id", header.txid as u64);
|
||||
js.set_string("client_mac",
|
||||
&format_addr_hex(&header.clienthw.to_vec()));
|
||||
js.set_string("assigned_ip", &dns_print_addr(&header.yourip));
|
||||
|
||||
if self.extended {
|
||||
js.set_string("client_ip", &dns_print_addr(&header.clientip));
|
||||
if header.opcode == BOOTP_REPLY {
|
||||
js.set_string("relay_ip",
|
||||
&dns_print_addr(&header.giaddr));
|
||||
js.set_string("next_server_ip",
|
||||
&dns_print_addr(&header.serverip));
|
||||
}
|
||||
}
|
||||
|
||||
for option in options {
|
||||
let code = option.code;
|
||||
match &option.option {
|
||||
&DHCPOptionWrapper::ClientId(ref clientid) => {
|
||||
js.set_string("client_id",
|
||||
&format_addr_hex(&clientid.data));
|
||||
}
|
||||
&DHCPOptionWrapper::TimeValue(ref time_value) => {
|
||||
match code {
|
||||
DHCP_OPT_ADDRESS_TIME => {
|
||||
if self.extended {
|
||||
js.set_integer("lease_time",
|
||||
time_value.seconds as u64);
|
||||
}
|
||||
}
|
||||
DHCP_OPT_REBINDING_TIME => {
|
||||
if self.extended {
|
||||
js.set_integer("rebinding_time",
|
||||
time_value.seconds as u64);
|
||||
}
|
||||
}
|
||||
DHCP_OPT_RENEWAL_TIME => {
|
||||
js.set_integer("renewal_time",
|
||||
time_value.seconds as u64);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
&DHCPOptionWrapper::Generic(ref option) => {
|
||||
match code {
|
||||
DHCP_OPT_SUBNET_MASK => {
|
||||
if self.extended {
|
||||
js.set_string("subnet_mask",
|
||||
&dns_print_addr(&option.data));
|
||||
}
|
||||
}
|
||||
DHCP_OPT_HOSTNAME => {
|
||||
if option.data.len() > 0 {
|
||||
js.set_string_from_bytes("hostname",
|
||||
&option.data);
|
||||
}
|
||||
}
|
||||
DHCP_OPT_TYPE => {
|
||||
self.log_opt_type(&js, option);
|
||||
}
|
||||
DHCP_OPT_REQUESTED_IP => {
|
||||
if self.extended {
|
||||
js.set_string("requested_ip",
|
||||
&dns_print_addr(&option.data));
|
||||
}
|
||||
}
|
||||
DHCP_OPT_PARAMETER_LIST => {
|
||||
if self.extended {
|
||||
self.log_opt_parameters(&js, option);
|
||||
}
|
||||
}
|
||||
DHCP_OPT_DNS_SERVER => {
|
||||
if self.extended {
|
||||
self.log_opt_dns_server(&js, option);
|
||||
}
|
||||
}
|
||||
DHCP_OPT_ROUTERS => {
|
||||
if self.extended {
|
||||
self.log_opt_routers(&js, option);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
return Some(js);
|
||||
}
|
||||
|
||||
fn log_opt_type(&self, js: &Json, option: &DHCPOptGeneric) {
|
||||
let dhcp_type = match option.data[0] {
|
||||
DHCP_TYPE_DISCOVER => "discover",
|
||||
DHCP_TYPE_OFFER => "offer",
|
||||
DHCP_TYPE_REQUEST => "request",
|
||||
DHCP_TYPE_DECLINE => "decline",
|
||||
DHCP_TYPE_ACK => "ack",
|
||||
DHCP_TYPE_NAK => "nak",
|
||||
DHCP_TYPE_RELEASE => "release",
|
||||
DHCP_TYPE_INFORM => "inform",
|
||||
_ => "unknown"
|
||||
};
|
||||
js.set_string("dhcp_type", dhcp_type);
|
||||
}
|
||||
|
||||
fn log_opt_parameters(&self, js: &Json, option: &DHCPOptGeneric) {
|
||||
let params = Json::array();
|
||||
for i in &option.data {
|
||||
let param = match *i {
|
||||
DHCP_PARAM_SUBNET_MASK => "subnet_mask",
|
||||
DHCP_PARAM_ROUTER => "router",
|
||||
DHCP_PARAM_DNS_SERVER => "dns_server",
|
||||
DHCP_PARAM_DOMAIN => "domain",
|
||||
DHCP_PARAM_ARP_TIMEOUT => "arp_timeout",
|
||||
DHCP_PARAM_NTP_SERVER => "ntp_server",
|
||||
DHCP_PARAM_TFTP_SERVER_NAME => "tftp_server_name",
|
||||
DHCP_PARAM_TFTP_SERVER_IP => "tftp_server_ip",
|
||||
_ => ""
|
||||
};
|
||||
if param.len() > 0 {
|
||||
params.array_append_string(param);
|
||||
}
|
||||
}
|
||||
js.set("params", params);
|
||||
}
|
||||
|
||||
fn log_opt_dns_server(&self, js: &Json, option: &DHCPOptGeneric) {
|
||||
let servers = Json::array();
|
||||
for i in 0..(option.data.len() / 4) {
|
||||
servers.array_append_string(&dns_print_addr(
|
||||
&option.data[(i * 4)..(i * 4) + 4].to_vec()))
|
||||
}
|
||||
js.set("dns_servers", servers);
|
||||
}
|
||||
|
||||
fn log_opt_routers(&self, js: &Json, option: &DHCPOptGeneric) {
|
||||
let routers = Json::array();
|
||||
for i in 0..(option.data.len() / 4) {
|
||||
routers.array_append_string(&dns_print_addr(
|
||||
&option.data[(i * 4)..(i * 4) + 4].to_vec()))
|
||||
}
|
||||
js.set("routers", routers);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fn format_addr_hex(input: &Vec<u8>) -> String {
|
||||
let parts: Vec<String> = input.iter()
|
||||
.map(|b| format!("{:02x}", b))
|
||||
.collect();
|
||||
return parts.join(":");
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn rs_dhcp_logger_new(conf: *const c_void) -> *mut libc::c_void {
|
||||
let conf = ConfNode::wrap(conf);
|
||||
let boxed = Box::new(DHCPLogger::new(conf));
|
||||
return unsafe{std::mem::transmute(boxed)};
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn rs_dhcp_logger_free(logger: *mut libc::c_void) {
|
||||
let _: Box<DHCPLogger> = unsafe{std::mem::transmute(logger)};
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn rs_dhcp_logger_log(logger: *mut libc::c_void,
|
||||
tx: *mut libc::c_void) -> *mut JsonT {
|
||||
let logger = cast_pointer!(logger, DHCPLogger);
|
||||
let tx = cast_pointer!(tx, DHCPTransaction);
|
||||
match logger.log(tx) {
|
||||
Some(js) => {
|
||||
return js.unwrap();
|
||||
}
|
||||
_ => {
|
||||
return std::ptr::null_mut();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,20 @@
|
||||
/* Copyright (C) 2018 Open Information Security Foundation
|
||||
*
|
||||
* You can copy, redistribute or modify this Program under the terms of
|
||||
* the GNU General Public License version 2 as published by the Free
|
||||
* Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* version 2 along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*/
|
||||
|
||||
pub mod dhcp;
|
||||
pub mod parser;
|
||||
pub mod logger;
|
||||
Binary file not shown.
@ -0,0 +1,288 @@
|
||||
/* Copyright (C) 2018 Open Information Security Foundation
|
||||
*
|
||||
* You can copy, redistribute or modify this Program under the terms of
|
||||
* the GNU General Public License version 2 as published by the Free
|
||||
* Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* version 2 along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*/
|
||||
|
||||
use std::cmp::min;
|
||||
|
||||
use dhcp::dhcp::*;
|
||||
use nom::*;
|
||||
|
||||
pub struct DHCPMessage {
|
||||
pub header: DHCPHeader,
|
||||
|
||||
pub options: Vec<DHCPOption>,
|
||||
|
||||
// Set to true if the options were found to be malformed. That is
|
||||
// failing to parse with enough data.
|
||||
pub malformed_options: bool,
|
||||
|
||||
// Set to true if the options failed to parse due to not enough
|
||||
// data.
|
||||
pub truncated_options: bool,
|
||||
}
|
||||
|
||||
pub struct DHCPHeader {
|
||||
pub opcode: u8,
|
||||
pub htype: u8,
|
||||
pub hlen: u8,
|
||||
pub hops: u8,
|
||||
pub txid: u32,
|
||||
pub seconds: u16,
|
||||
pub flags: u16,
|
||||
pub clientip: Vec<u8>,
|
||||
pub yourip: Vec<u8>,
|
||||
pub serverip: Vec<u8>,
|
||||
pub giaddr: Vec<u8>,
|
||||
pub clienthw: Vec<u8>,
|
||||
pub servername: Vec<u8>,
|
||||
pub bootfilename: Vec<u8>,
|
||||
pub magic: Vec<u8>,
|
||||
}
|
||||
|
||||
pub struct DHCPOptClientId {
|
||||
pub htype: u8,
|
||||
pub data: Vec<u8>,
|
||||
}
|
||||
|
||||
/// Option type for time values.
|
||||
pub struct DHCPOptTimeValue {
|
||||
pub seconds: u32,
|
||||
}
|
||||
|
||||
pub struct DHCPOptGeneric {
|
||||
pub data: Vec<u8>,
|
||||
}
|
||||
|
||||
pub enum DHCPOptionWrapper {
|
||||
ClientId(DHCPOptClientId),
|
||||
TimeValue(DHCPOptTimeValue),
|
||||
Generic(DHCPOptGeneric),
|
||||
End,
|
||||
}
|
||||
|
||||
pub struct DHCPOption {
|
||||
pub code: u8,
|
||||
pub data: Option<Vec<u8>>,
|
||||
pub option: DHCPOptionWrapper,
|
||||
}
|
||||
|
||||
named!(pub parse_header<DHCPHeader>,
|
||||
do_parse!(
|
||||
opcode: be_u8
|
||||
>> htype: be_u8
|
||||
>> hlen: be_u8
|
||||
>> hops: be_u8
|
||||
>> txid: be_u32
|
||||
>> seconds: be_u16
|
||||
>> flags: be_u16
|
||||
>> clientip: take!(4)
|
||||
>> yourip: take!(4)
|
||||
>> serverip: take!(4)
|
||||
>> giaddr: take!(4)
|
||||
>> clienthw: take!(16)
|
||||
>> servername: take!(64)
|
||||
>> bootfilename: take!(128)
|
||||
>> magic: take!(4)
|
||||
>> (
|
||||
DHCPHeader{
|
||||
opcode: opcode,
|
||||
htype: htype,
|
||||
hlen: hlen,
|
||||
hops: hops,
|
||||
txid: txid,
|
||||
seconds: seconds,
|
||||
flags: flags,
|
||||
clientip: clientip.to_vec(),
|
||||
yourip: yourip.to_vec(),
|
||||
serverip: serverip.to_vec(),
|
||||
giaddr: giaddr.to_vec(),
|
||||
clienthw: clienthw[0..min(hlen as usize, 16)].to_vec(),
|
||||
servername: servername.to_vec(),
|
||||
bootfilename: bootfilename.to_vec(),
|
||||
magic: magic.to_vec(),
|
||||
}
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
named!(pub parse_clientid_option<DHCPOption>,
|
||||
do_parse!(
|
||||
code: be_u8 >>
|
||||
len: be_u8 >>
|
||||
htype: be_u8 >>
|
||||
data: take!(len - 1) >>
|
||||
(
|
||||
DHCPOption{
|
||||
code: code,
|
||||
data: None,
|
||||
option: DHCPOptionWrapper::ClientId(DHCPOptClientId{
|
||||
htype: 1,
|
||||
data: data.to_vec(),
|
||||
}),
|
||||
}
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
named!(pub parse_address_time_option<DHCPOption>,
|
||||
do_parse!(
|
||||
code: be_u8 >>
|
||||
len: be_u8 >>
|
||||
seconds: be_u32 >>
|
||||
(
|
||||
DHCPOption{
|
||||
code: code,
|
||||
data: None,
|
||||
option: DHCPOptionWrapper::TimeValue(DHCPOptTimeValue{
|
||||
seconds: seconds,
|
||||
}),
|
||||
}
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
named!(pub parse_generic_option<DHCPOption>,
|
||||
do_parse!(
|
||||
code: be_u8 >>
|
||||
len: be_u8 >>
|
||||
data: take!(len) >> (
|
||||
DHCPOption{
|
||||
code: code,
|
||||
data: None,
|
||||
option: DHCPOptionWrapper::Generic(DHCPOptGeneric{
|
||||
data: data.to_vec(),
|
||||
}),
|
||||
}
|
||||
))
|
||||
);
|
||||
|
||||
/// Parse a single DHCP option. When option 255 (END) is parsed, the remaining
|
||||
/// data will be consumed.
|
||||
named!(pub parse_option<DHCPOption>,
|
||||
switch!(peek!(be_u8),
|
||||
// End of options case. We consume the rest of the data
|
||||
// so the parse is not called again. But is there a
|
||||
// better way to "break"?
|
||||
DHCP_OPT_END => do_parse!(
|
||||
code: be_u8 >>
|
||||
data: rest >> (DHCPOption{
|
||||
code: code,
|
||||
data: Some(data.to_vec()),
|
||||
option: DHCPOptionWrapper::End,
|
||||
})) |
|
||||
DHCP_OPT_CLIENT_ID => call!(parse_clientid_option) |
|
||||
DHCP_OPT_ADDRESS_TIME => call!(parse_address_time_option) |
|
||||
DHCP_OPT_RENEWAL_TIME => call!(parse_address_time_option) |
|
||||
DHCP_OPT_REBINDING_TIME => call!(parse_address_time_option) |
|
||||
_ => call!(parse_generic_option)
|
||||
));
|
||||
|
||||
/// Parse and return all the options. Upon the end of option indicator
|
||||
/// all the data will be consumed.
|
||||
named!(pub parse_all_options<Vec<DHCPOption>>, many0!(call!(parse_option)));
|
||||
|
||||
pub fn dhcp_parse(input: &[u8]) -> IResult<&[u8], DHCPMessage> {
|
||||
match parse_header(input) {
|
||||
IResult::Done(rem, header) => {
|
||||
let mut options = Vec::new();
|
||||
let mut next = rem;
|
||||
let mut malformed_options = false;
|
||||
let mut truncated_options = false;
|
||||
loop {
|
||||
match parse_option(next) {
|
||||
IResult::Done(rem, option) => {
|
||||
let done = option.code == DHCP_OPT_END;
|
||||
options.push(option);
|
||||
next = rem;
|
||||
if done {
|
||||
break;
|
||||
}
|
||||
}
|
||||
IResult::Incomplete(_) => {
|
||||
println!("incomplete parsing options");
|
||||
truncated_options = true;
|
||||
break;
|
||||
}
|
||||
IResult::Error(_) => {
|
||||
malformed_options = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
let message = DHCPMessage {
|
||||
header: header,
|
||||
options: options,
|
||||
malformed_options: malformed_options,
|
||||
truncated_options: truncated_options,
|
||||
};
|
||||
return IResult::Done(next, message);
|
||||
}
|
||||
IResult::Error(err) => {
|
||||
return IResult::Error(err);
|
||||
}
|
||||
IResult::Incomplete(incomplete) => {
|
||||
return IResult::Incomplete(incomplete);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use dhcp::dhcp::*;
|
||||
use dhcp::parser::*;
|
||||
|
||||
#[test]
|
||||
fn test_parse_discover() {
|
||||
let pcap = include_bytes!("discover.pcap");
|
||||
let payload = &pcap[24 + 16 + 42..];
|
||||
|
||||
match dhcp_parse(payload) {
|
||||
IResult::Done(_rem, message) => {
|
||||
let header = message.header;
|
||||
assert_eq!(header.opcode, BOOTP_REQUEST);
|
||||
assert_eq!(header.htype, 1);
|
||||
assert_eq!(header.hlen, 6);
|
||||
assert_eq!(header.hops, 0);
|
||||
assert_eq!(header.txid, 0x00003d1d);
|
||||
assert_eq!(header.seconds, 0);
|
||||
assert_eq!(header.flags, 0);
|
||||
assert_eq!(header.clientip, &[0, 0, 0, 0]);
|
||||
assert_eq!(header.yourip, &[0, 0, 0, 0]);
|
||||
assert_eq!(header.serverip, &[0, 0, 0, 0]);
|
||||
assert_eq!(header.giaddr, &[0, 0, 0, 0]);
|
||||
assert_eq!(&header.clienthw[..(header.hlen as usize)],
|
||||
&[0x00, 0x0b, 0x82, 0x01, 0xfc, 0x42]);
|
||||
assert!(header.servername.iter().all(|&x| x == 0));
|
||||
assert!(header.bootfilename.iter().all(|&x| x == 0));
|
||||
assert_eq!(header.magic, &[0x63, 0x82, 0x53, 0x63]);
|
||||
|
||||
assert!(!message.malformed_options);
|
||||
assert!(!message.truncated_options);
|
||||
|
||||
assert_eq!(message.options.len(), 5);
|
||||
assert_eq!(message.options[0].code, DHCP_OPT_TYPE);
|
||||
assert_eq!(message.options[1].code, DHCP_OPT_CLIENT_ID);
|
||||
assert_eq!(message.options[2].code, DHCP_OPT_REQUESTED_IP);
|
||||
assert_eq!(message.options[3].code, DHCP_OPT_PARAMETER_LIST);
|
||||
assert_eq!(message.options[4].code, DHCP_OPT_END);
|
||||
}
|
||||
_ => {
|
||||
assert!(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Binary file not shown.
@ -0,0 +1,51 @@
|
||||
/* Copyright (C) 2015 Open Information Security Foundation
|
||||
*
|
||||
* You can copy, redistribute or modify this Program under the terms of
|
||||
* the GNU General Public License version 2 as published by the Free
|
||||
* Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* version 2 along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* \author Jason Ish <jason.ish@oisf.net>
|
||||
*/
|
||||
|
||||
#include "suricata-common.h"
|
||||
#include "util-unittest.h"
|
||||
#include "app-layer-parser.h"
|
||||
#include "app-layer-dhcp.h"
|
||||
|
||||
#ifdef HAVE_RUST
|
||||
#include "rust-dhcp-dhcp-gen.h"
|
||||
#endif /* HAVE_RUST */
|
||||
|
||||
void RegisterDHCPParsers(void)
|
||||
{
|
||||
#ifdef HAVE_RUST
|
||||
rs_dhcp_register_parser();
|
||||
#endif /* HAVE_RUST */
|
||||
#ifdef UNITTESTS
|
||||
AppLayerParserRegisterProtocolUnittests(IPPROTO_TCP, ALPROTO_DHCP,
|
||||
DHCPParserRegisterTests);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef UNITTESTS
|
||||
#endif
|
||||
|
||||
void DHCPParserRegisterTests(void)
|
||||
{
|
||||
#ifdef UNITTESTS
|
||||
#endif
|
||||
}
|
||||
@ -0,0 +1,30 @@
|
||||
/* Copyright (C) 2015 Open Information Security Foundation
|
||||
*
|
||||
* You can copy, redistribute or modify this Program under the terms of
|
||||
* the GNU General Public License version 2 as published by the Free
|
||||
* Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* version 2 along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* \author Jason Ish <jason.ish@oisf.net>
|
||||
*/
|
||||
|
||||
#ifndef __APP_LAYER_DHCP_H__
|
||||
#define __APP_LAYER_DHCP_H__
|
||||
|
||||
void RegisterDHCPParsers(void);
|
||||
void DHCPParserRegisterTests(void);
|
||||
|
||||
#endif /* __APP_LAYER_DHCP_H__ */
|
||||
@ -0,0 +1,190 @@
|
||||
/* Copyright (C) 2015 Open Information Security Foundation
|
||||
*
|
||||
* You can copy, redistribute or modify this Program under the terms of
|
||||
* the GNU General Public License version 2 as published by the Free
|
||||
* Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* version 2 along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*/
|
||||
|
||||
/*
|
||||
* TODO: Update \author in this file and in output-json-dhcp.h.
|
||||
* TODO: Remove SCLogNotice statements, or convert to debug.
|
||||
* TODO: Implement your app-layers logging.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* \author Jason Ish <jason.ish@oisf.net>
|
||||
*/
|
||||
|
||||
#include "suricata-common.h"
|
||||
#include "debug.h"
|
||||
#include "detect.h"
|
||||
#include "pkt-var.h"
|
||||
#include "conf.h"
|
||||
|
||||
#include "threads.h"
|
||||
#include "threadvars.h"
|
||||
#include "tm-threads.h"
|
||||
|
||||
#include "util-unittest.h"
|
||||
#include "util-buffer.h"
|
||||
#include "util-debug.h"
|
||||
#include "util-byte.h"
|
||||
|
||||
#include "output.h"
|
||||
#include "output-json.h"
|
||||
|
||||
#include "app-layer.h"
|
||||
#include "app-layer-parser.h"
|
||||
|
||||
#include "app-layer-dhcp.h"
|
||||
#include "output-json-dhcp.h"
|
||||
|
||||
#if defined(HAVE_LIBJANSSON) && defined(HAVE_RUST)
|
||||
|
||||
#include "rust-dhcp-logger-gen.h"
|
||||
|
||||
typedef struct LogDHCPFileCtx_ {
|
||||
LogFileCtx *file_ctx;
|
||||
uint32_t flags;
|
||||
void *rs_logger;
|
||||
} LogDHCPFileCtx;
|
||||
|
||||
typedef struct LogDHCPLogThread_ {
|
||||
LogDHCPFileCtx *dhcplog_ctx;
|
||||
uint32_t count;
|
||||
MemBuffer *buffer;
|
||||
} LogDHCPLogThread;
|
||||
|
||||
static int JsonDHCPLogger(ThreadVars *tv, void *thread_data,
|
||||
const Packet *p, Flow *f, void *state, void *tx, uint64_t tx_id)
|
||||
{
|
||||
LogDHCPLogThread *thread = thread_data;
|
||||
LogDHCPFileCtx *ctx = thread->dhcplog_ctx;
|
||||
|
||||
json_t *js = CreateJSONHeader((Packet *)p, 0, "dhcp");
|
||||
if (unlikely(js == NULL)) {
|
||||
return TM_ECODE_FAILED;
|
||||
}
|
||||
|
||||
json_t *dhcp_js = rs_dhcp_logger_log(ctx->rs_logger, tx);
|
||||
if (unlikely(dhcp_js == NULL)) {
|
||||
goto skip;
|
||||
}
|
||||
json_object_set_new(js, "dhcp", dhcp_js);
|
||||
|
||||
MemBufferReset(thread->buffer);
|
||||
OutputJSONBuffer(js, thread->dhcplog_ctx->file_ctx, &thread->buffer);
|
||||
json_decref(js);
|
||||
|
||||
return TM_ECODE_OK;
|
||||
|
||||
skip:
|
||||
json_decref(js);
|
||||
return TM_ECODE_OK;
|
||||
}
|
||||
|
||||
static void OutputDHCPLogDeInitCtxSub(OutputCtx *output_ctx)
|
||||
{
|
||||
LogDHCPFileCtx *dhcplog_ctx = (LogDHCPFileCtx *)output_ctx->data;
|
||||
rs_dhcp_logger_free(dhcplog_ctx->rs_logger);
|
||||
SCFree(dhcplog_ctx);
|
||||
SCFree(output_ctx);
|
||||
}
|
||||
|
||||
static OutputInitResult OutputDHCPLogInitSub(ConfNode *conf,
|
||||
OutputCtx *parent_ctx)
|
||||
{
|
||||
OutputInitResult result = { NULL, false };
|
||||
OutputJsonCtx *ajt = parent_ctx->data;
|
||||
|
||||
LogDHCPFileCtx *dhcplog_ctx = SCCalloc(1, sizeof(*dhcplog_ctx));
|
||||
if (unlikely(dhcplog_ctx == NULL)) {
|
||||
return result;
|
||||
}
|
||||
dhcplog_ctx->file_ctx = ajt->file_ctx;
|
||||
|
||||
OutputCtx *output_ctx = SCCalloc(1, sizeof(*output_ctx));
|
||||
if (unlikely(output_ctx == NULL)) {
|
||||
SCFree(dhcplog_ctx);
|
||||
return result;
|
||||
}
|
||||
output_ctx->data = dhcplog_ctx;
|
||||
output_ctx->DeInit = OutputDHCPLogDeInitCtxSub;
|
||||
|
||||
dhcplog_ctx->rs_logger = rs_dhcp_logger_new(conf);
|
||||
|
||||
AppLayerParserRegisterLogger(IPPROTO_UDP, ALPROTO_DHCP);
|
||||
|
||||
result.ctx = output_ctx;
|
||||
result.ok = true;
|
||||
return result;
|
||||
}
|
||||
|
||||
#define OUTPUT_BUFFER_SIZE 65535
|
||||
|
||||
static TmEcode JsonDHCPLogThreadInit(ThreadVars *t, const void *initdata, void **data)
|
||||
{
|
||||
LogDHCPLogThread *thread = SCCalloc(1, sizeof(*thread));
|
||||
if (unlikely(thread == NULL)) {
|
||||
return TM_ECODE_FAILED;
|
||||
}
|
||||
|
||||
if (initdata == NULL) {
|
||||
SCLogDebug("Error getting context for EveLogDHCP. \"initdata\" is NULL.");
|
||||
SCFree(thread);
|
||||
return TM_ECODE_FAILED;
|
||||
}
|
||||
|
||||
thread->buffer = MemBufferCreateNew(OUTPUT_BUFFER_SIZE);
|
||||
if (unlikely(thread->buffer == NULL)) {
|
||||
SCFree(thread);
|
||||
return TM_ECODE_FAILED;
|
||||
}
|
||||
|
||||
thread->dhcplog_ctx = ((OutputCtx *)initdata)->data;
|
||||
*data = (void *)thread;
|
||||
|
||||
return TM_ECODE_OK;
|
||||
}
|
||||
|
||||
static TmEcode JsonDHCPLogThreadDeinit(ThreadVars *t, void *data)
|
||||
{
|
||||
LogDHCPLogThread *thread = (LogDHCPLogThread *)data;
|
||||
if (thread == NULL) {
|
||||
return TM_ECODE_OK;
|
||||
}
|
||||
if (thread->buffer != NULL) {
|
||||
MemBufferFree(thread->buffer);
|
||||
}
|
||||
SCFree(thread);
|
||||
return TM_ECODE_OK;
|
||||
}
|
||||
|
||||
void JsonDHCPLogRegister(void)
|
||||
{
|
||||
/* Register as an eve sub-module. */
|
||||
OutputRegisterTxSubModule(LOGGER_JSON_DHCP, "eve-log", "JsonDHCPLog",
|
||||
"eve-log.dhcp", OutputDHCPLogInitSub, ALPROTO_DHCP,
|
||||
JsonDHCPLogger, JsonDHCPLogThreadInit,
|
||||
JsonDHCPLogThreadDeinit, NULL);
|
||||
}
|
||||
|
||||
#else /* No JSON support. */
|
||||
|
||||
void JsonDHCPLogRegister(void)
|
||||
{
|
||||
}
|
||||
|
||||
#endif /* HAVE_LIBJANSSON */
|
||||
@ -0,0 +1,29 @@
|
||||
/* Copyright (C) 2015 Open Information Security Foundation
|
||||
*
|
||||
* You can copy, redistribute or modify this Program under the terms of
|
||||
* the GNU General Public License version 2 as published by the Free
|
||||
* Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* version 2 along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* \author FirstName LastName <name@domain>
|
||||
*/
|
||||
|
||||
#ifndef __OUTPUT_JSON_DHCP_H__
|
||||
#define __OUTPUT_JSON_DHCP_H__
|
||||
|
||||
void JsonDHCPLogRegister(void);
|
||||
|
||||
#endif /* __OUTPUT_JSON_DHCP_H__ */
|
||||
Loading…
Reference in New Issue