From deeb5ec38e2f0c3dadcb7138d672cf1ff905d577 Mon Sep 17 00:00:00 2001 From: Vlad Grigorescu Date: Thu, 18 Jul 2013 21:17:06 -0400 Subject: [PATCH 1/3] Rework the DHCP analyzer to make it compatible again. --- scripts/base/init-default.bro | 1 + scripts/base/protocols/dhcp/__load__.bro | 2 + scripts/base/protocols/dhcp/consts.bro | 20 ++ scripts/base/protocols/dhcp/main.bro | 74 +++++ scripts/base/protocols/dhcp/utils.bro | 21 ++ .../protocols/dhcp/handle_extra_msg_types.bro | 256 +++++++++++++++++ src/analyzer/protocol/dhcp/DHCP.cc | 1 - src/analyzer/protocol/dhcp/DHCP.h | 3 + src/analyzer/protocol/dhcp/dhcp-analyzer.pac | 269 ++++++++++-------- src/analyzer/protocol/dhcp/dhcp-protocol.pac | 34 +-- src/analyzer/protocol/dhcp/dhcp.pac | 1 + src/analyzer/protocol/dhcp/events.bif | 195 ++++--------- src/util.cc | 20 ++ src/util.h | 1 + 14 files changed, 623 insertions(+), 275 deletions(-) create mode 100644 scripts/base/protocols/dhcp/__load__.bro create mode 100644 scripts/base/protocols/dhcp/consts.bro create mode 100644 scripts/base/protocols/dhcp/main.bro create mode 100644 scripts/base/protocols/dhcp/utils.bro create mode 100644 scripts/policy/protocols/dhcp/handle_extra_msg_types.bro diff --git a/scripts/base/init-default.bro b/scripts/base/init-default.bro index 72ba0bf115..6aaf7446b9 100644 --- a/scripts/base/init-default.bro +++ b/scripts/base/init-default.bro @@ -36,6 +36,7 @@ @load base/frameworks/tunnels @load base/protocols/conn +@load base/protocols/dhcp @load base/protocols/dns @load base/protocols/ftp @load base/protocols/http diff --git a/scripts/base/protocols/dhcp/__load__.bro b/scripts/base/protocols/dhcp/__load__.bro new file mode 100644 index 0000000000..0098b81a7a --- /dev/null +++ b/scripts/base/protocols/dhcp/__load__.bro @@ -0,0 +1,2 @@ +@load ./consts +@load ./main \ No newline at end of file diff --git a/scripts/base/protocols/dhcp/consts.bro b/scripts/base/protocols/dhcp/consts.bro new file mode 100644 index 0000000000..81914a98c3 --- /dev/null +++ b/scripts/base/protocols/dhcp/consts.bro @@ -0,0 +1,20 @@ +##! Types, errors, and fields for analyzing DHCP data. A helper file +##! for DHCP analysis scripts. + +module DHCP; + +export { + + ## Types of DHCP messages. See RFC 1533. + const message_types = { + [1] = "DHCP_DISCOVER", + [2] = "DHCP_OFFER", + [3] = "DHCP_REQUEST", + [4] = "DHCP_DECLINE", + [5] = "DHCP_ACK", + [6] = "DHCP_NAK", + [7] = "DHCP_RELEASE", + [8] = "DHCP_INFORM", + } &default = function(n: count): string { return fmt("unknown-message-type-%d", n); }; + +} \ No newline at end of file diff --git a/scripts/base/protocols/dhcp/main.bro b/scripts/base/protocols/dhcp/main.bro new file mode 100644 index 0000000000..9fc63df152 --- /dev/null +++ b/scripts/base/protocols/dhcp/main.bro @@ -0,0 +1,74 @@ +##! Analyzes DHCP traffic in order to log DHCP leases given to clients. +##! This script ignores large swaths of the protocol, since it is rather +##! noisy on most networks, and focuses on the end-result: assigned leases. +##! +##! To enable further analysis and log output for DHCP, see the optional +##! scripts in the policy/protocols/dhcp directory. + +@load ./utils.bro + +module DHCP; + +export { + redef enum Log::ID += { LOG }; + + ## The record type which contains the column fields of the DHCP log. + type Info: record { + ## The earliest time at which a DHCP message over the + ## associated connection is observed. + ts: time &log; + ## A unique identifier of the connection over which DHCP is + ## occuring. + uid: string &log; + ## The connection's 4-tuple of endpoint addresses/ports. + id: conn_id &log; + ## Client's hardware address. + mac: string &log &optional; + ## Client's actual assigned IP address. + assigned_ip: addr &log &optional; + ## IP address lease interval. + lease_time: interval &log &optional; + ## A random number choosen by the client for this transaction. + trans_id: count &log; + }; + + ## Event that can be handled to access the DHCP + ## record as it is sent on to the logging framework. + global log_dhcp: event(rec: Info); +} + +# Add the dhcp info to the connection record +redef record connection += { + dhcp: Info &optional; +}; + +const ports = { 67/udp, 68/udp }; +redef likely_server_ports += { 67/udp }; + +event bro_init() &priority=5 + { + Log::create_stream(DHCP::LOG, [$columns=Info, $ev=log_dhcp]); + Analyzer::register_for_ports(Analyzer::ANALYZER_DHCP, ports); + } + +event dhcp_ack(c: connection, msg: dhcp_msg, mask: addr, router: dhcp_router_list, lease: interval, serv_addr: addr, host_name: string) &priority=5 + { + local info: Info; + info$ts = network_time(); + info$id = c$id; + info$uid = c$uid; + info$assigned_ip = reverse_ip(msg$yiaddr); + info$lease_time = lease; + info$trans_id = msg$xid; + + if ( msg$h_addr != "" ) + info$mac = msg$h_addr; + + c$dhcp = info; + } + +# We let policy scripts add stuff too, so we run this at a lower priority +event dhcp_ack(c: connection, msg: dhcp_msg, mask: addr, router: dhcp_router_list, lease: interval, serv_addr: addr, host_name: string) &priority=1 + { + Log::write(DHCP::LOG, c$dhcp); + } diff --git a/scripts/base/protocols/dhcp/utils.bro b/scripts/base/protocols/dhcp/utils.bro new file mode 100644 index 0000000000..cb06450088 --- /dev/null +++ b/scripts/base/protocols/dhcp/utils.bro @@ -0,0 +1,21 @@ +##! Utilities specific for DHCP processing. + +@load ./main + +module DHCP; + +export { + ## Reverse the octets of an IPv4 IP. + ## + ## ip: An :bro:type:`addr` IPv4 address. + ## + ## Returns: A reversed addr. + global reverse_ip: function(ip: addr): addr; +} + +function reverse_ip(ip: addr): addr + { + local octets = split(cat(ip), /\./); + return to_addr(cat(octets[4], ".", octets[3], ".", octets[2], ".", octets[1])); + } + diff --git a/scripts/policy/protocols/dhcp/handle_extra_msg_types.bro b/scripts/policy/protocols/dhcp/handle_extra_msg_types.bro new file mode 100644 index 0000000000..e5fa713da4 --- /dev/null +++ b/scripts/policy/protocols/dhcp/handle_extra_msg_types.bro @@ -0,0 +1,256 @@ +##! Handlers for DHCP message types other than DHCPACK, which is handled in base/protocols/dhcp. +##! For networks that wish to get more details from their DHCP logs, at the expense +##! of a significantly higher log rate. + +@load base/protocols/dhcp + +module DHCP; + +export { + redef record Info += { + ## The value of the host name option, if seen + host_name: string &log &optional; + ## The IP requested by the client, if any + requested_ip: addr &log &optional; + ## The type of the DHCP message (DHCPOFFER, DHCPRELEASE, etc.) + msg_type: string &log &optional; + }; + + #### Enabled by default + + ## A boolean value to determine if DHCPREQUEST messages are logged. + ## Often useful to see client activity, and because host_name is often available. + const log_dhcprequest = T &redef; + + ## A boolean value to determine if DHCPDECLINE messages are logged. + ## A client declines a lease if it detects that the IP is already in use (usually via ARP). + const log_dhcpdecline = T &redef; + + ## A boolean value to determine if DHCPNAK messages are logged. + ## A server issues a DHCPNAK if a client DHCPREQUEST is invalid. + const log_dhcpnak = T &redef; + + ## A boolean value to determine if DHCPRELEASE messages are logged. + ## A client issues a DHCPRELEASE when it no longer needs the lease (e.g. it's shutting down). + const log_dhcprelease = T &redef; + + #### Not enabled by default + + ## A boolean value to determine if DHCPOFFER messages are logged. + ## Used to profile server -> client communication. + const log_dhcpoffer = F &redef; + + ## A boolean value to determine if DHCPDISCOVER messages are logged. + ## Used to profile broadcast client discovery requests. + const log_dhcpdiscover = F &redef; + + ## A boolean value to determine if DHCPINFORM messages are logged. + ## Used to profile clients attempting to request/renew specific IPs. + const log_dhcpinform = F &redef; + +} + +event dhcp_offer(c: connection, msg: dhcp_msg, mask: addr, router: dhcp_router_list, lease: interval, serv_addr: addr, host_name: string) &priority=5 + { + if ( ! log_dhcpoffer ) + return; + + local info: Info; + info$ts = network_time(); + info$id = c$id; + info$uid = c$uid; + info$assigned_ip = reverse_ip(msg$yiaddr); + info$lease_time = lease; + info$trans_id = msg$xid; + info$msg_type = "DHCPOFFER"; + + if ( msg$h_addr != "" ) + info$mac = msg$h_addr; + + if ( host_name != "" ) + info$host_name = host_name; + + c$dhcp = info; + } + +event dhcp_discover(c: connection, msg: dhcp_msg, req_addr: addr, host_name: string) &priority=5 + { + if ( ! log_dhcpdiscover ) + return; + + local info: Info; + info$ts = network_time(); + info$id = c$id; + info$uid = c$uid; + info$requested_ip = req_addr; + info$trans_id = msg$xid; + info$msg_type = "DHCPDISCOVER"; + + if ( msg$h_addr != "" ) + info$mac = msg$h_addr; + + if ( host_name != "" ) + info$host_name = host_name; + + c$dhcp = info; + } + +event dhcp_request(c: connection, msg: dhcp_msg, req_addr: addr, serv_addr: addr, host_name: string) &priority=5 + { + if ( ! log_dhcprequest ) + return; + + local info: Info; + info$ts = network_time(); + info$id = c$id; + info$uid = c$uid; + info$requested_ip = req_addr; + info$trans_id = msg$xid; + info$msg_type = "DHCPREQUEST"; + + if ( msg$h_addr != "" ) + info$mac = msg$h_addr; + + if ( host_name != "" ) + info$host_name = host_name; + + c$dhcp = info; + } + +event dhcp_decline(c: connection, msg: dhcp_msg, host_name: string) &priority=5 + { + if ( ! log_dhcpdecline ) + return; + + local info: Info; + info$ts = network_time(); + info$id = c$id; + info$uid = c$uid; + info$trans_id = msg$xid; + info$msg_type = "DHCPDECLINE"; + + if ( msg$h_addr != "" ) + info$mac = msg$h_addr; + + if ( host_name != "" ) + info$host_name = host_name; + + c$dhcp = info; + } + +event dhcp_nak(c: connection, msg: dhcp_msg, host_name: string) &priority=5 + { + if ( ! log_dhcpnak ) + return; + + local info: Info; + info$ts = network_time(); + info$id = c$id; + info$uid = c$uid; + info$trans_id = msg$xid; + info$msg_type = "DHCPNAK"; + + if ( msg$h_addr != "" ) + info$mac = msg$h_addr; + + if ( host_name != "" ) + info$host_name = host_name; + + c$dhcp = info; + } + +event dhcp_release(c: connection, msg: dhcp_msg, host_name: string) &priority=5 + { + if ( ! log_dhcprelease ) + return; + + local info: Info; + info$ts = network_time(); + info$id = c$id; + info$uid = c$uid; + info$trans_id = msg$xid; + info$msg_type = "DHCPRELEASE"; + + if ( msg$h_addr != "" ) + info$mac = msg$h_addr; + + if ( host_name != "" ) + info$host_name = host_name; + + c$dhcp = info; + } + +event dhcp_inform(c: connection, msg: dhcp_msg, host_name: string) &priority=5 + { + if ( ! log_dhcpinform ) + return; + + local info: Info; + info$ts = network_time(); + info$id = c$id; + info$uid = c$uid; + info$trans_id = msg$xid; + info$msg_type = "DHCPINFORM"; + + if ( msg$h_addr != "" ) + info$mac = msg$h_addr; + + if ( host_name != "" ) + info$host_name = host_name; + + c$dhcp = info; + } + +event dhcp_ack(c: connection, msg: dhcp_msg, mask: addr, router: dhcp_router_list, lease: interval, serv_addr: addr, host_name: string) &priority=4 + { + ## For the sake of consistency, let's add msg_type to DHCPACK as well. + c$dhcp$msg_type = "DHCPACK"; + ## host_name is generally not in ACKs, but let's check anyway. + if ( host_name != "" ) + c$dhcp$host_name = host_name; + } + +#### We log stuff at a lower priority, in case any other scripts would like to extend the Info record first. + +event dhcp_offer(c: connection, msg: dhcp_msg, mask: addr, router: dhcp_router_list, lease: interval, serv_addr: addr, host_name: string) &priority=1 + { + if ( log_dhcpoffer ) + Log::write(DHCP::LOG, c$dhcp); + } + +event dhcp_discover(c: connection, msg: dhcp_msg, req_addr: addr, host_name: string) &priority=1 + { + if ( log_dhcpdiscover ) + Log::write(DHCP::LOG, c$dhcp); + } + +event dhcp_request(c: connection, msg: dhcp_msg, req_addr: addr, serv_addr: addr, host_name: string) &priority=1 + { + if ( log_dhcprequest ) + Log::write(DHCP::LOG, c$dhcp); + } + +event dhcp_decline(c: connection, msg: dhcp_msg, host_name: string) &priority=1 + { + if ( log_dhcpdecline ) + Log::write(DHCP::LOG, c$dhcp); + } + +event dhcp_nak(c: connection, msg: dhcp_msg, host_name: string) &priority=1 + { + if ( log_dhcpnak ) + Log::write(DHCP::LOG, c$dhcp); + } + +event dhcp_release(c: connection, msg: dhcp_msg, host_name: string) &priority=1 + { + if ( log_dhcprelease ) + Log::write(DHCP::LOG, c$dhcp); + } + +event dhcp_inform(c: connection, msg: dhcp_msg, host_name: string) &priority=1 + { + if ( log_dhcpinform ) + Log::write(DHCP::LOG, c$dhcp); + } + diff --git a/src/analyzer/protocol/dhcp/DHCP.cc b/src/analyzer/protocol/dhcp/DHCP.cc index 8d05aef37d..1fa8759fbf 100644 --- a/src/analyzer/protocol/dhcp/DHCP.cc +++ b/src/analyzer/protocol/dhcp/DHCP.cc @@ -1,4 +1,3 @@ - #include "DHCP.h" #include "events.bif.h" diff --git a/src/analyzer/protocol/dhcp/DHCP.h b/src/analyzer/protocol/dhcp/DHCP.h index a1c06e8b85..b9784d34f1 100644 --- a/src/analyzer/protocol/dhcp/DHCP.h +++ b/src/analyzer/protocol/dhcp/DHCP.h @@ -19,6 +19,9 @@ public: static analyzer::Analyzer* InstantiateAnalyzer(Connection* conn) { return new DHCP_Analyzer(conn); } + static bool Available() + { return dhcp_discover || dhcp_offer || dhcp_request || dhcp_decline || dhcp_ack || dhcp_nak || dhcp_release || dhcp_inform; } + protected: binpac::DHCP::DHCP_Conn* interp; }; diff --git a/src/analyzer/protocol/dhcp/dhcp-analyzer.pac b/src/analyzer/protocol/dhcp/dhcp-analyzer.pac index 5267075445..df928b4be4 100644 --- a/src/analyzer/protocol/dhcp/dhcp-analyzer.pac +++ b/src/analyzer/protocol/dhcp/dhcp-analyzer.pac @@ -8,12 +8,10 @@ flow DHCP_Flow(is_orig: bool) { %member{ BroVal dhcp_msg_val_; - BroAnalyzer interp; %} %init{ dhcp_msg_val_ = 0; - interp = connection->bro_analyzer(); %} %cleanup{ @@ -45,7 +43,7 @@ flow DHCP_Flow(is_orig: bool) { } if ( type == 0 ) - interp->Weird("DHCP_no_type_option"); + connection()->bro_analyzer()->ProtocolViolation("no DHCP message type option"); return type; %} @@ -56,54 +54,63 @@ flow DHCP_Flow(is_orig: bool) { // Requested IP address to the server. ::uint32 req_addr = 0, serv_addr = 0; + StringVal* host_name = 0; - for ( ptr = options->begin(); - ptr != options->end() && ! (*ptr)->last(); ++ptr ) + for ( ptr = options->begin(); ptr != options->end() && ! (*ptr)->last(); ++ptr ) { - switch ( (*ptr)->code() ) { - case REQ_IP_OPTION: - req_addr = htonl((*ptr)->info()->req_addr()); - break; + switch ( (*ptr)->code() ) + { + case REQ_IP_OPTION: + req_addr = htonl((*ptr)->info()->req_addr()); + break; - case SERV_ID_OPTION: - serv_addr = htonl((*ptr)->info()->serv_addr()); - break; - } + case SERV_ID_OPTION: + serv_addr = htonl((*ptr)->info()->serv_addr()); + break; + + case HOST_NAME_OPTION: + host_name = new StringVal((*ptr)->info()->host_name().length(), + (const char*) (*ptr)->info()->host_name().begin()); + break; + } } + if ( host_name == 0 ) + host_name = new StringVal(""); + switch ( type ) - { - case DHCPDISCOVER: - BifEvent::generate_dhcp_discover(connection()->bro_analyzer(), - connection()->bro_analyzer()->Conn(), - dhcp_msg_val_->Ref(), new AddrVal(req_addr)); - break; + { + case DHCPDISCOVER: + BifEvent::generate_dhcp_discover(connection()->bro_analyzer(), + connection()->bro_analyzer()->Conn(), + dhcp_msg_val_->Ref(), new AddrVal(req_addr), host_name); + break; - case DHCPREQUEST: - BifEvent::generate_dhcp_request(connection()->bro_analyzer(), - connection()->bro_analyzer()->Conn(), - dhcp_msg_val_->Ref(), new AddrVal(req_addr), - new AddrVal(serv_addr)); - break; + case DHCPREQUEST: + BifEvent::generate_dhcp_request(connection()->bro_analyzer(), + connection()->bro_analyzer()->Conn(), + dhcp_msg_val_->Ref(), new AddrVal(req_addr), + new AddrVal(serv_addr), host_name); + break; - case DHCPDECLINE: - BifEvent::generate_dhcp_decline(connection()->bro_analyzer(), - connection()->bro_analyzer()->Conn(), - dhcp_msg_val_->Ref()); - break; + case DHCPDECLINE: + BifEvent::generate_dhcp_decline(connection()->bro_analyzer(), + connection()->bro_analyzer()->Conn(), + dhcp_msg_val_->Ref(), host_name); + break; - case DHCPRELEASE: - BifEvent::generate_dhcp_release(connection()->bro_analyzer(), - connection()->bro_analyzer()->Conn(), - dhcp_msg_val_->Ref()); - break; + case DHCPRELEASE: + BifEvent::generate_dhcp_release(connection()->bro_analyzer(), + connection()->bro_analyzer()->Conn(), + dhcp_msg_val_->Ref(), host_name); + break; - case DHCPINFORM: - BifEvent::generate_dhcp_inform(connection()->bro_analyzer(), - connection()->bro_analyzer()->Conn(), - dhcp_msg_val_->Ref()); - break; - } + case DHCPINFORM: + BifEvent::generate_dhcp_inform(connection()->bro_analyzer(), + connection()->bro_analyzer()->Conn(), + dhcp_msg_val_->Ref(), host_name); + break; + } return true; %} @@ -118,72 +125,83 @@ flow DHCP_Flow(is_orig: bool) { ::uint32 subnet_mask = 0, serv_addr = 0; uint32 lease = 0; + StringVal* host_name = 0; for ( ptr = options->begin(); ptr != options->end() && ! (*ptr)->last(); ++ptr ) { - switch ( (*ptr)->code() ) { - case SUBNET_OPTION: - subnet_mask = htonl((*ptr)->info()->mask()); - break; - - case ROUTER_OPTION: - // Let's hope there aren't multiple - // such options. - Unref(router_list); - router_list = new TableVal(dhcp_router_list); - + switch ( (*ptr)->code() ) { - int num_routers = - (*ptr)->info()->router_list()->size(); + case SUBNET_OPTION: + subnet_mask = htonl((*ptr)->info()->mask()); + break; - for ( int i = 0; i < num_routers; ++i ) - { - vector* rlist = - (*ptr)->info()->router_list(); - uint32 raddr = (*rlist)[i]; - ::uint32 tmp_addr; - tmp_addr = htonl(raddr); - // index starting from 1 - Val* index = new Val(i + 1, TYPE_COUNT); - router_list->Assign(index, new AddrVal(tmp_addr)); - Unref(index); - } + case ROUTER_OPTION: + // Let's hope there aren't multiple + // such options. + Unref(router_list); + router_list = new TableVal(dhcp_router_list); + + { + int num_routers = (*ptr)->info()->router_list()->size(); + + for ( int i = 0; i < num_routers; ++i ) + { + vector* rlist = (*ptr)->info()->router_list(); + + uint32 raddr = (*rlist)[i]; + ::uint32 tmp_addr; + tmp_addr = htonl(raddr); + + // index starting from 1 + Val* index = new Val(i + 1, TYPE_COUNT); + router_list->Assign(index, new AddrVal(tmp_addr)); + Unref(index); + } + } + break; + + case LEASE_OPTION: + lease = (*ptr)->info()->lease(); + break; + + case SERV_ID_OPTION: + serv_addr = htonl((*ptr)->info()->serv_addr()); + break; + + case HOST_NAME_OPTION: + host_name = new StringVal((*ptr)->info()->host_name().length(), + (const char*) (*ptr)->info()->host_name().begin()); + break; } - break; - - case LEASE_OPTION: - lease = (*ptr)->info()->lease(); - break; - - case SERV_ID_OPTION: - serv_addr = htonl((*ptr)->info()->serv_addr()); - break; - } } - switch ( type ) { - case DHCPOFFER: - BifEvent::generate_dhcp_offer(connection()->bro_analyzer(), - connection()->bro_analyzer()->Conn(), - dhcp_msg_val_->Ref(), new AddrVal(subnet_mask), - router_list, lease, new AddrVal(serv_addr)); - break; + if ( host_name == 0 ) + host_name = new StringVal(""); - case DHCPACK: - BifEvent::generate_dhcp_ack(connection()->bro_analyzer(), - connection()->bro_analyzer()->Conn(), - dhcp_msg_val_->Ref(), new AddrVal(subnet_mask), - router_list, lease, new AddrVal(serv_addr)); - break; + switch ( type ) + { + case DHCPOFFER: + BifEvent::generate_dhcp_offer(connection()->bro_analyzer(), + connection()->bro_analyzer()->Conn(), + dhcp_msg_val_->Ref(), new AddrVal(subnet_mask), + router_list, lease, new AddrVal(serv_addr), host_name); + break; - case DHCPNAK: - BifEvent::generate_dhcp_nak(connection()->bro_analyzer(), - connection()->bro_analyzer()->Conn(), - dhcp_msg_val_->Ref()); - break; + case DHCPACK: + BifEvent::generate_dhcp_ack(connection()->bro_analyzer(), + connection()->bro_analyzer()->Conn(), + dhcp_msg_val_->Ref(), new AddrVal(subnet_mask), + router_list, lease, new AddrVal(serv_addr), host_name); + break; - } + case DHCPNAK: + BifEvent::generate_dhcp_nak(connection()->bro_analyzer(), + connection()->bro_analyzer()->Conn(), + dhcp_msg_val_->Ref(), host_name); + break; + + } return true; @@ -195,7 +213,10 @@ flow DHCP_Flow(is_orig: bool) { // DHCP or BOOTP. If not, we are unable to interpret // the message options. if ( ${msg.cookie} != 0x63825363 ) + { + connection()->bro_analyzer()->ProtocolViolation(fmt("bad cookie (%d)", ${msg.cookie})); return false; + } Unref(dhcp_msg_val_); RecordVal* r = new RecordVal(dhcp_msg); @@ -203,40 +224,44 @@ flow DHCP_Flow(is_orig: bool) { r->Assign(0, new Val(${msg.op}, TYPE_COUNT)); r->Assign(1, new Val(${msg.type}, TYPE_COUNT)); r->Assign(2, new Val(${msg.xid}, TYPE_COUNT)); - - // We want only 6 bytes for Ethernet address. - r->Assign(3, new StringVal(6, (const char*) ${msg.chaddr}.begin())); - + r->Assign(3, new StringVal(format_mac(${msg.chaddr}.data()))); r->Assign(4, new AddrVal(${msg.ciaddr})); r->Assign(5, new AddrVal(${msg.yiaddr})); dhcp_msg_val_ = r; - switch ( ${msg.op} ) { - case BOOTREQUEST: // presumablye from client to server - if ( ${msg.type} == DHCPDISCOVER || - ${msg.type} == DHCPREQUEST || - ${msg.type} == DHCPDECLINE || - ${msg.type} == DHCPRELEASE || - ${msg.type} == DHCPINFORM ) - parse_request(${msg.options}, ${msg.type}); - else - interp->Weird("DHCP_wrong_msg_type"); - break; + switch ( ${msg.op} ) + { + case BOOTREQUEST: // presumably from client to server + if ( ${msg.type} == DHCPDISCOVER || + ${msg.type} == DHCPREQUEST || + ${msg.type} == DHCPDECLINE || + ${msg.type} == DHCPRELEASE || + ${msg.type} == DHCPINFORM ) + parse_request(${msg.options}, ${msg.type}); + else + connection()->bro_analyzer()->ProtocolViolation(fmt("unknown DHCP message type option for BOOTREQUEST (%d)", + ${msg.type})); + break; - case BOOTREPLY: // presumably from server to client - if ( ${msg.type} == DHCPOFFER || - ${msg.type} == DHCPACK || ${msg.type} == DHCPNAK ) - parse_reply(${msg.options}, ${msg.type}); - else - interp->Weird("DHCP_wrong_msg_type"); - break; - - default: - interp->Weird("DHCP_wrong_op_type"); - break; - } + case BOOTREPLY: // presumably from server to client + if ( ${msg.type} == DHCPOFFER || + ${msg.type} == DHCPACK || + ${msg.type} == DHCPNAK ) + parse_reply(${msg.options}, ${msg.type}); + else + connection()->bro_analyzer()->ProtocolViolation(fmt("unknown DHCP message type option for BOOTREPLY (%d)", + ${msg.type})); + + break; + default: + connection()->bro_analyzer()->ProtocolViolation(fmt("unknown DHCP message op code (%d). Known codes: 1=BOOTREQUEST, 2=BOOTREPLY", + ${msg.op})); + break; + } + + connection()->bro_analyzer()->ProtocolConfirmation(); return true; %} }; diff --git a/src/analyzer/protocol/dhcp/dhcp-protocol.pac b/src/analyzer/protocol/dhcp/dhcp-protocol.pac index d77780b1b3..1eb5b399e5 100644 --- a/src/analyzer/protocol/dhcp/dhcp-protocol.pac +++ b/src/analyzer/protocol/dhcp/dhcp-protocol.pac @@ -10,13 +10,14 @@ enum OP_type { # The option types are by no means complete. # Anyone can add a new option type in RFC 1533 to be parsed here. enum OPTION_type { - SUBNET_OPTION = 1, - ROUTER_OPTION = 3, - REQ_IP_OPTION = 50, - LEASE_OPTION = 51, - MSG_TYPE_OPTION = 53, - SERV_ID_OPTION = 54, # Server address, actually :) - END_OPTION = 255, + SUBNET_OPTION = 1, + ROUTER_OPTION = 3, + HOST_NAME_OPTION = 12, + REQ_IP_OPTION = 50, + LEASE_OPTION = 51, + MSG_TYPE_OPTION = 53, + SERV_ID_OPTION = 54, # Server address, actually :) + END_OPTION = 255, }; # Refer to RFC 1533 for message types (with option = 53). @@ -34,21 +35,22 @@ enum DHCP_message_type { type Option_Info(code: uint8) = record { length : uint8; value : case code of { - SUBNET_OPTION -> mask : uint32; - ROUTER_OPTION -> router_list: uint32[length/4]; - REQ_IP_OPTION -> req_addr : uint32; - LEASE_OPTION -> lease : uint32; - MSG_TYPE_OPTION -> msg_type : uint8; - SERV_ID_OPTION -> serv_addr: uint32; - default -> other: bytestring &length = length; + SUBNET_OPTION -> mask : uint32; + ROUTER_OPTION -> router_list : uint32[length/4]; + REQ_IP_OPTION -> req_addr : uint32; + LEASE_OPTION -> lease : uint32; + MSG_TYPE_OPTION -> msg_type : uint8; + SERV_ID_OPTION -> serv_addr : uint32; + HOST_NAME_OPTION -> host_name : bytestring &length = length; + default -> other : bytestring &length = length; }; }; type DHCP_Option = record { code : uint8; data : case code of { - 0, 255 -> none : empty; - default -> info : Option_Info(code); + 0, 255 -> none : empty; + default -> info : Option_Info(code); }; } &let { last: bool = (code == 255); # Mark the end of a list of options diff --git a/src/analyzer/protocol/dhcp/dhcp.pac b/src/analyzer/protocol/dhcp/dhcp.pac index c4a684badc..706be31e10 100644 --- a/src/analyzer/protocol/dhcp/dhcp.pac +++ b/src/analyzer/protocol/dhcp/dhcp.pac @@ -1,3 +1,4 @@ +%include binpac.pac %include bro.pac %extern{ diff --git a/src/analyzer/protocol/dhcp/events.bif b/src/analyzer/protocol/dhcp/events.bif index 741504185e..987213dad3 100644 --- a/src/analyzer/protocol/dhcp/events.bif +++ b/src/analyzer/protocol/dhcp/events.bif @@ -1,8 +1,5 @@ -## Generated for DHCP messages of type *discover*. -## -## See `Wikipedia -## `__ for -## more information about the DHCP protocol. +## Generated for DHCP messages of type *DHCPDISCOVER* (client broadcast to locate +## available servers). ## ## c: The connection record describing the underlying UDP flow. ## @@ -10,33 +7,23 @@ ## ## req_addr: The specific address requested by the client. ## -## .. bro:see:: dns_AAAA_reply dns_A_reply dns_CNAME_reply dns_EDNS_addl -## dns_HINFO_reply dns_MX_reply dns_NS_reply dns_PTR_reply dns_SOA_reply -## dns_SRV_reply dns_TSIG_addl dns_TXT_reply dns_WKS_reply dns_end -## dns_full_request dns_mapping_altered dns_mapping_lost_name dns_mapping_new_name -## dns_mapping_unverified dns_mapping_valid dns_message dns_query_reply -## dns_rejected dns_request non_dns_request dns_max_queries dns_session_timeout -## dns_skip_addl dns_skip_all_addl dns_skip_all_auth dns_skip_auth +## host_name: The value of the host name option, if specified by the client. +## +## .. bro:see:: dhcp_discover dhcp_offer dhcp_request dhcp_decline dhcp_ack dhcp_nak +## dhcp_release dhcp_inform ## ## .. note:: Bro does not support broadcast packets (as used by the DHCP ## protocol). It treats broadcast addresses just like any other and ## associates packets into transport-level flows in the same way as usual. ## -## .. todo:: Bro's current default configuration does not activate the protocol -## analyzer that generates this event; the corresponding script has not yet -## been ported to Bro 2.x. To still enable this event, one needs to -## register a port for it or add a DPD payload signature. -event dhcp_discover%(c: connection, msg: dhcp_msg, req_addr: addr%); +event dhcp_discover%(c: connection, msg: dhcp_msg, req_addr: addr, host_name: string%); -## Generated for DHCP messages of type *offer*. -## -## See `Wikipedia -## `__ for -## more information about the DHCP protocol. +## Generated for DHCP messages of type *DHCPOFFER* (server to client in response to +## DHCPDISCOVER with offer of configuration parameters). ## ## c: The connection record describing the underlying UDP flow. ## -## msg: TODO. +## msg: The parsed type-independent part of the DHCP message. ## ## mask: The subnet mask specified by the message. ## @@ -46,28 +33,21 @@ event dhcp_discover%(c: connection, msg: dhcp_msg, req_addr: addr%); ## ## serv_addr: The server address specified by the message. ## -## .. bro:see:: dns_AAAA_reply dns_A_reply dns_CNAME_reply dns_EDNS_addl -## dns_HINFO_reply dns_MX_reply dns_NS_reply dns_PTR_reply dns_SOA_reply -## dns_SRV_reply dns_TSIG_addl dns_TXT_reply dns_WKS_reply dns_end -## dns_full_request dns_mapping_altered dns_mapping_lost_name dns_mapping_new_name -## dns_mapping_unverified dns_mapping_valid dns_message dns_query_reply -## dns_rejected dns_request non_dns_request +## host_name: The value of the host name option, if specified by the client. +## +## .. bro:see:: dhcp_discover dhcp_request dhcp_decline dhcp_ack dhcp_nak +## dhcp_release dhcp_inform ## ## .. note:: Bro does not support broadcast packets (as used by the DHCP ## protocol). It treats broadcast addresses just like any other and ## associates packets into transport-level flows in the same way as usual. ## -## .. todo:: Bro's current default configuration does not activate the protocol -## analyzer that generates this event; the corresponding script has not yet -## been ported to Bro 2.x. To still enable this event, one needs to -## register a port for it or add a DPD payload signature. -event dhcp_offer%(c: connection, msg: dhcp_msg, mask: addr, router: dhcp_router_list, lease: interval, serv_addr: addr%); +event dhcp_offer%(c: connection, msg: dhcp_msg, mask: addr, router: dhcp_router_list, lease: interval, serv_addr: addr, host_name: string%); -## Generated for DHCP messages of type *request*. -## -## See `Wikipedia -## `__ for -## more information about the DHCP protocol. +## Generated for DHCP messages of type *DHCPREQUEST* (Client message to servers either +## (a) requesting offered parameters from one server and implicitly declining offers +## from all others, (b) confirming correctness of previously allocated address after, +## e.g., system reboot, or (c) extending the lease on a particular network address.) ## ## c: The connection record describing the underlying UDP flow. ## @@ -77,55 +57,37 @@ event dhcp_offer%(c: connection, msg: dhcp_msg, mask: addr, router: dhcp_router_ ## ## serv_addr: The server address specified by the message. ## -## .. bro:see:: dns_AAAA_reply dns_A_reply dns_CNAME_reply dns_EDNS_addl -## dns_HINFO_reply dns_MX_reply dns_NS_reply dns_PTR_reply dns_SOA_reply -## dns_SRV_reply dns_TSIG_addl dns_TXT_reply dns_WKS_reply dns_end -## dns_full_request dns_mapping_altered dns_mapping_lost_name dns_mapping_new_name -## dns_mapping_unverified dns_mapping_valid dns_message dns_query_reply -## dns_rejected dns_request non_dns_request +## host_name: The value of the host name option, if specified by the client. +## +## .. bro:see:: dhcp_discover dhcp_offer dhcp_decline dhcp_ack dhcp_nak +## dhcp_release dhcp_inform ## ## .. note:: Bro does not support broadcast packets (as used by the DHCP ## protocol). It treats broadcast addresses just like any other and ## associates packets into transport-level flows in the same way as usual. ## -## .. todo:: Bro's current default configuration does not activate the protocol -## analyzer that generates this event; the corresponding script has not yet -## been ported to Bro 2.x. To still enable this event, one needs to -## register a port for it or add a DPD payload signature. -event dhcp_request%(c: connection, msg: dhcp_msg, req_addr: addr, serv_addr: addr%); +event dhcp_request%(c: connection, msg: dhcp_msg, req_addr: addr, serv_addr: addr, host_name: string%); -## Generated for DHCP messages of type *decline*. -## -## See `Wikipedia -## `__ for -## more information about the DHCP protocol. +## Generated for DHCP messages of type *DHCPDECLINE* (Client to server indicating +## network address is already in use). ## ## c: The connection record describing the underlying UDP flow. ## ## msg: The parsed type-independent part of the DHCP message. ## -## .. bro:see:: dns_AAAA_reply dns_A_reply dns_CNAME_reply dns_EDNS_addl -## dns_HINFO_reply dns_MX_reply dns_NS_reply dns_PTR_reply dns_SOA_reply -## dns_SRV_reply dns_TSIG_addl dns_TXT_reply dns_WKS_reply dns_end -## dns_full_request dns_mapping_altered dns_mapping_lost_name dns_mapping_new_name -## dns_mapping_unverified dns_mapping_valid dns_message dns_query_reply -## dns_rejected dns_request non_dns_request +## host_name: The value of the host name option, if specified by the client. +## +## .. bro:see:: dhcp_discover dhcp_offer dhcp_request dhcp_ack dhcp_nak +## dhcp_release dhcp_inform ## ## .. note:: Bro does not support broadcast packets (as used by the DHCP ## protocol). It treats broadcast addresses just like any other and ## associates packets into transport-level flows in the same way as usual. ## -## .. todo:: Bro's current default configuration does not activate the protocol -## analyzer that generates this event; the corresponding script has not yet -## been ported to Bro 2.x. To still enable this event, one needs to -## register a port for it or add a DPD payload signature. -event dhcp_decline%(c: connection, msg: dhcp_msg%); +event dhcp_decline%(c: connection, msg: dhcp_msg, host_name: string%); -## Generated for DHCP messages of type *acknowledgment*. -## -## See `Wikipedia -## `__ for -## more information about the DHCP protocol. +## Generated for DHCP messages of type *DHCPACK* (Server to client with configuration +## parameters, including committed network address). ## ## c: The connection record describing the underlying UDP flow. ## @@ -139,101 +101,62 @@ event dhcp_decline%(c: connection, msg: dhcp_msg%); ## ## serv_addr: The server address specified by the message. ## -## .. bro:see:: dns_AAAA_reply dns_A_reply dns_CNAME_reply dns_EDNS_addl -## dns_HINFO_reply dns_MX_reply dns_NS_reply dns_PTR_reply dns_SOA_reply -## dns_SRV_reply dns_TSIG_addl dns_TXT_reply dns_WKS_reply dns_end -## dns_full_request dns_mapping_altered dns_mapping_lost_name dns_mapping_new_name -## dns_mapping_unverified dns_mapping_valid dns_message dns_query_reply -## dns_rejected dns_request non_dns_request +## host_name: The value of the host name option, if specified by the client. ## -## .. note:: Bro does not support broadcast packets (as used by the DHCP -## protocol). It treats broadcast addresses just like any other and -## associates packets into transport-level flows in the same way as usual. +## .. bro:see:: dhcp_discover dhcp_offer dhcp_request dhcp_decline dhcp_nak +## dhcp_release dhcp_inform ## -## .. todo:: Bro's current default configuration does not activate the protocol -## analyzer that generates this event; the corresponding script has not yet -## been ported to Bro 2.x. To still enable this event, one needs to -## register a port for it or add a DPD payload signature. -event dhcp_ack%(c: connection, msg: dhcp_msg, mask: addr, router: dhcp_router_list, lease: interval, serv_addr: addr%); +event dhcp_ack%(c: connection, msg: dhcp_msg, mask: addr, router: dhcp_router_list, lease: interval, serv_addr: addr, host_name: string%); -## Generated for DHCP messages of type *negative acknowledgment*. -## -## See `Wikipedia -## `__ for -## more information about the DHCP protocol. +## Generated for DHCP messages of type *DHCPNAK* (Server to client indicating client's +## notion of network address is incorrect (e.g., client has moved to new subnet) or +## client's lease has expired). ## ## c: The connection record describing the underlying UDP flow. ## ## msg: The parsed type-independent part of the DHCP message. ## -## .. bro:see:: dns_AAAA_reply dns_A_reply dns_CNAME_reply dns_EDNS_addl -## dns_HINFO_reply dns_MX_reply dns_NS_reply dns_PTR_reply dns_SOA_reply -## dns_SRV_reply dns_TSIG_addl dns_TXT_reply dns_WKS_reply dns_end -## dns_full_request dns_mapping_altered dns_mapping_lost_name dns_mapping_new_name -## dns_mapping_unverified dns_mapping_valid dns_message dns_query_reply -## dns_rejected dns_request non_dns_request +## host_name: The value of the host name option, if specified by the client. +## +## .. bro:see:: dhcp_discover dhcp_offer dhcp_request dhcp_decline dhcp_ack dhcp_release +## dhcp_inform ## ## .. note:: Bro does not support broadcast packets (as used by the DHCP ## protocol). It treats broadcast addresses just like any other and ## associates packets into transport-level flows in the same way as usual. ## -## .. todo:: Bro's current default configuration does not activate the protocol -## analyzer that generates this event; the corresponding script has not yet -## been ported to Bro 2.x. To still enable this event, one needs to -## register a port for it or add a DPD payload signature. -event dhcp_nak%(c: connection, msg: dhcp_msg%); +event dhcp_nak%(c: connection, msg: dhcp_msg, host_name: string%); -## Generated for DHCP messages of type *release*. -## -## See `Wikipedia -## `__ for -## more information about the DHCP protocol. +## Generated for DHCP messages of type *DHCPRELEASE* (Client to server relinquishing +## network address and cancelling remaining lease). ## ## c: The connection record describing the underlying UDP flow. ## ## msg: The parsed type-independent part of the DHCP message. ## -## .. bro:see:: dns_AAAA_reply dns_A_reply dns_CNAME_reply dns_EDNS_addl -## dns_HINFO_reply dns_MX_reply dns_NS_reply dns_PTR_reply dns_SOA_reply -## dns_SRV_reply dns_TSIG_addl dns_TXT_reply dns_WKS_reply dns_end -## dns_full_request dns_mapping_altered dns_mapping_lost_name dns_mapping_new_name -## dns_mapping_unverified dns_mapping_valid dns_message dns_query_reply -## dns_rejected dns_request non_dns_request +## host_name: The value of the host name option, if specified by the client. ## -## .. note:: Bro does not support broadcast packets (as used by the DHCP -## protocol). It treats broadcast addresses just like any other and -## associates packets into transport-level flows in the same way as usual. +## .. bro:see:: dhcp_discover dhcp_offer dhcp_request dhcp_decline dhcp_ack dhcp_nak +## dhcp_inform ## -## .. todo:: Bro's current default configuration does not activate the protocol -## analyzer that generates this event; the corresponding script has not yet -## been ported to Bro 2.x. To still enable this event, one needs to -## register a port for it or add a DPD payload signature. -event dhcp_release%(c: connection, msg: dhcp_msg%); +event dhcp_release%(c: connection, msg: dhcp_msg, host_name: string%); -## Generated for DHCP messages of type *inform*. -## -## See `Wikipedia -## `__ for -## more information about the DHCP protocol. +## Generated for DHCP messages of type *DHCPINFORM* (Client to server, asking only for +## local configuration parameters; client already has externally configured network +## address). ## ## c: The connection record describing the underlying UDP flow. ## ## msg: The parsed type-independent part of the DHCP message. ## -## .. bro:see:: dns_AAAA_reply dns_A_reply dns_CNAME_reply dns_EDNS_addl -## dns_HINFO_reply dns_MX_reply dns_NS_reply dns_PTR_reply dns_SOA_reply -## dns_SRV_reply dns_TSIG_addl dns_TXT_reply dns_WKS_reply dns_end -## dns_full_request dns_mapping_altered dns_mapping_lost_name dns_mapping_new_name -## dns_mapping_unverified dns_mapping_valid dns_message dns_query_reply -## dns_rejected dns_request non_dns_request +## host_name: The value of the host name option, if specified by the client. +## +## .. bro:see:: dhcp_discover dhcp_offer dhcp_request dhcp_decline dhcp_ack dhcp_nak +## dhcp_release ## ## .. note:: Bro does not support broadcast packets (as used by the DHCP ## protocol). It treats broadcast addresses just like any other and ## associates packets into transport-level flows in the same way as usual. ## -## .. todo:: Bro's current default configuration does not activate the protocol -## analyzer that generates this event; the corresponding script has not yet -## been ported to Bro 2.x. To still enable this event, one needs to -## register a port for it or add a DPD payload signature. -event dhcp_inform%(c: connection, msg: dhcp_msg%); +event dhcp_inform%(c: connection, msg: dhcp_msg, host_name: string%); diff --git a/src/util.cc b/src/util.cc index 5a63be22cb..0a349ccce2 100644 --- a/src/util.cc +++ b/src/util.cc @@ -78,6 +78,26 @@ std::string extract_ip_and_len(const std::string& i, int* len) return extract_ip(i.substr(0, pos)); } +/** +* Given a MAC address, formats it as 00:de:ad:be:ef +* Supports both EUI-48 and EUI-64. If it's neither, returns +* an empty string. +* +* @param m EUI-48 or EUI-64 MAC address to format, as a char array +* @return A string of the formatted MAC +*/ +char* format_mac(const unsigned char* m) +{ + char* buf = new char[24]; + if (m[6] == 0 && m[7] == 0) // EUI-48 + snprintf(buf, 18, "%02x:%02x:%02x:%02x:%02x:%02x", + m[0], m[1], m[2], m[3], m[4], m[5]); + else + snprintf(buf, 24, "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x", + m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7]); + return buf; +} + /** * Takes a string, unescapes all characters that are escaped as hex codes * (\x##) and turns them into the equivalent ascii-codes. Returns a string diff --git a/src/util.h b/src/util.h index 91ed8f2888..8201456b45 100644 --- a/src/util.h +++ b/src/util.h @@ -106,6 +106,7 @@ std::string get_escaped_string(const std::string& str, bool escape_all); extern char* copy_string(const char* s); extern int streq(const char* s1, const char* s2); +extern char* format_mac(const unsigned char* m); // Returns the character corresponding to the given escape sequence (s points // just past the '\'), and updates s to point just beyond the last character From 0f1b0e75b719c2b5755e3d20fa7e42b2f65726e7 Mon Sep 17 00:00:00 2001 From: Vlad Grigorescu Date: Wed, 31 Jul 2013 16:32:20 -0400 Subject: [PATCH 2/3] DHCP: - Added the known-devices log, and the script to log DHCP hostname per MAC address, - Added DPD sig, - Removed some scripts following a discussion with Seth. --- scripts/base/protocols/dhcp/__load__.bro | 4 +- scripts/base/protocols/dhcp/dpd.sig | 5 + scripts/base/protocols/dhcp/main.bro | 12 +- scripts/policy/misc/known-devices.bro | 37 +++ .../protocols/dhcp/handle_extra_msg_types.bro | 256 ------------------ .../dhcp/known-devices-and-hostnames.bro | 22 ++ 6 files changed, 71 insertions(+), 265 deletions(-) create mode 100644 scripts/base/protocols/dhcp/dpd.sig create mode 100644 scripts/policy/misc/known-devices.bro delete mode 100644 scripts/policy/protocols/dhcp/handle_extra_msg_types.bro create mode 100644 scripts/policy/protocols/dhcp/known-devices-and-hostnames.bro diff --git a/scripts/base/protocols/dhcp/__load__.bro b/scripts/base/protocols/dhcp/__load__.bro index 0098b81a7a..c04423a855 100644 --- a/scripts/base/protocols/dhcp/__load__.bro +++ b/scripts/base/protocols/dhcp/__load__.bro @@ -1,2 +1,4 @@ @load ./consts -@load ./main \ No newline at end of file +@load ./main + +@load-sigs ./dpd.sig diff --git a/scripts/base/protocols/dhcp/dpd.sig b/scripts/base/protocols/dhcp/dpd.sig new file mode 100644 index 0000000000..010920e2d8 --- /dev/null +++ b/scripts/base/protocols/dhcp/dpd.sig @@ -0,0 +1,5 @@ +signature dhcp_cookie { + ip-proto == udp + payload /^.*\x63\x82\x53\x63/ + enable "dhcp" +} \ No newline at end of file diff --git a/scripts/base/protocols/dhcp/main.bro b/scripts/base/protocols/dhcp/main.bro index 9fc63df152..05491361ff 100644 --- a/scripts/base/protocols/dhcp/main.bro +++ b/scripts/base/protocols/dhcp/main.bro @@ -2,8 +2,8 @@ ##! This script ignores large swaths of the protocol, since it is rather ##! noisy on most networks, and focuses on the end-result: assigned leases. ##! -##! To enable further analysis and log output for DHCP, see the optional -##! scripts in the policy/protocols/dhcp directory. +##! If you'd like to track known DHCP devices and to log the hostname +##! supplied by the client, see policy/protocols/dhcp/known-devices.bro @load ./utils.bro @@ -45,13 +45,13 @@ redef record connection += { const ports = { 67/udp, 68/udp }; redef likely_server_ports += { 67/udp }; -event bro_init() &priority=5 +event bro_init() { Log::create_stream(DHCP::LOG, [$columns=Info, $ev=log_dhcp]); Analyzer::register_for_ports(Analyzer::ANALYZER_DHCP, ports); } -event dhcp_ack(c: connection, msg: dhcp_msg, mask: addr, router: dhcp_router_list, lease: interval, serv_addr: addr, host_name: string) &priority=5 +event dhcp_ack(c: connection, msg: dhcp_msg, mask: addr, router: dhcp_router_list, lease: interval, serv_addr: addr, host_name: string) { local info: Info; info$ts = network_time(); @@ -65,10 +65,6 @@ event dhcp_ack(c: connection, msg: dhcp_msg, mask: addr, router: dhcp_router_lis info$mac = msg$h_addr; c$dhcp = info; - } -# We let policy scripts add stuff too, so we run this at a lower priority -event dhcp_ack(c: connection, msg: dhcp_msg, mask: addr, router: dhcp_router_list, lease: interval, serv_addr: addr, host_name: string) &priority=1 - { Log::write(DHCP::LOG, c$dhcp); } diff --git a/scripts/policy/misc/known-devices.bro b/scripts/policy/misc/known-devices.bro new file mode 100644 index 0000000000..4c3ed9009c --- /dev/null +++ b/scripts/policy/misc/known-devices.bro @@ -0,0 +1,37 @@ +##! This script logs devices for which Bro has been able to determine the MAC +##! address and logs the MAC address once per day (by default). The log that +##! is output provides an easy way to determine a count of the devices in use +##! on a network per day. + +##! NOTE: This script will not generate any logs. Scripts such as +##! policy/protocols/dhcp/known-devices-and-hostnames are needed. +module Known; + +export { + ## The known-hosts logging stream identifier. + redef enum Log::ID += { DEVICES_LOG }; + + ## The record type which contains the column fields of the known-devices log. + type DevicesInfo: record { + ## The timestamp at which the host was detected. + ts: time &log; + ## The MAC address that was detected. + mac: string &log; + }; + + ## The set of all known MAC addresses to store for preventing duplicate + ## logging of addresses. It can also be used from other scripts to + ## inspect if an address has been seen in use. + ## Maintain the list of known devices for 24 hours so that the existence + ## of each individual address is logged each day. + global known_devices: set[string] &create_expire=1day &synchronized &redef; + + ## An event that can be handled to access the :bro:type:`Known::DevicesInfo` + ## record as it is sent on to the logging framework. + global log_known_devices: event(rec: DevicesInfo); +} + +event bro_init() + { + Log::create_stream(Known::DEVICES_LOG, [$columns=DevicesInfo, $ev=log_known_devices]); + } diff --git a/scripts/policy/protocols/dhcp/handle_extra_msg_types.bro b/scripts/policy/protocols/dhcp/handle_extra_msg_types.bro deleted file mode 100644 index e5fa713da4..0000000000 --- a/scripts/policy/protocols/dhcp/handle_extra_msg_types.bro +++ /dev/null @@ -1,256 +0,0 @@ -##! Handlers for DHCP message types other than DHCPACK, which is handled in base/protocols/dhcp. -##! For networks that wish to get more details from their DHCP logs, at the expense -##! of a significantly higher log rate. - -@load base/protocols/dhcp - -module DHCP; - -export { - redef record Info += { - ## The value of the host name option, if seen - host_name: string &log &optional; - ## The IP requested by the client, if any - requested_ip: addr &log &optional; - ## The type of the DHCP message (DHCPOFFER, DHCPRELEASE, etc.) - msg_type: string &log &optional; - }; - - #### Enabled by default - - ## A boolean value to determine if DHCPREQUEST messages are logged. - ## Often useful to see client activity, and because host_name is often available. - const log_dhcprequest = T &redef; - - ## A boolean value to determine if DHCPDECLINE messages are logged. - ## A client declines a lease if it detects that the IP is already in use (usually via ARP). - const log_dhcpdecline = T &redef; - - ## A boolean value to determine if DHCPNAK messages are logged. - ## A server issues a DHCPNAK if a client DHCPREQUEST is invalid. - const log_dhcpnak = T &redef; - - ## A boolean value to determine if DHCPRELEASE messages are logged. - ## A client issues a DHCPRELEASE when it no longer needs the lease (e.g. it's shutting down). - const log_dhcprelease = T &redef; - - #### Not enabled by default - - ## A boolean value to determine if DHCPOFFER messages are logged. - ## Used to profile server -> client communication. - const log_dhcpoffer = F &redef; - - ## A boolean value to determine if DHCPDISCOVER messages are logged. - ## Used to profile broadcast client discovery requests. - const log_dhcpdiscover = F &redef; - - ## A boolean value to determine if DHCPINFORM messages are logged. - ## Used to profile clients attempting to request/renew specific IPs. - const log_dhcpinform = F &redef; - -} - -event dhcp_offer(c: connection, msg: dhcp_msg, mask: addr, router: dhcp_router_list, lease: interval, serv_addr: addr, host_name: string) &priority=5 - { - if ( ! log_dhcpoffer ) - return; - - local info: Info; - info$ts = network_time(); - info$id = c$id; - info$uid = c$uid; - info$assigned_ip = reverse_ip(msg$yiaddr); - info$lease_time = lease; - info$trans_id = msg$xid; - info$msg_type = "DHCPOFFER"; - - if ( msg$h_addr != "" ) - info$mac = msg$h_addr; - - if ( host_name != "" ) - info$host_name = host_name; - - c$dhcp = info; - } - -event dhcp_discover(c: connection, msg: dhcp_msg, req_addr: addr, host_name: string) &priority=5 - { - if ( ! log_dhcpdiscover ) - return; - - local info: Info; - info$ts = network_time(); - info$id = c$id; - info$uid = c$uid; - info$requested_ip = req_addr; - info$trans_id = msg$xid; - info$msg_type = "DHCPDISCOVER"; - - if ( msg$h_addr != "" ) - info$mac = msg$h_addr; - - if ( host_name != "" ) - info$host_name = host_name; - - c$dhcp = info; - } - -event dhcp_request(c: connection, msg: dhcp_msg, req_addr: addr, serv_addr: addr, host_name: string) &priority=5 - { - if ( ! log_dhcprequest ) - return; - - local info: Info; - info$ts = network_time(); - info$id = c$id; - info$uid = c$uid; - info$requested_ip = req_addr; - info$trans_id = msg$xid; - info$msg_type = "DHCPREQUEST"; - - if ( msg$h_addr != "" ) - info$mac = msg$h_addr; - - if ( host_name != "" ) - info$host_name = host_name; - - c$dhcp = info; - } - -event dhcp_decline(c: connection, msg: dhcp_msg, host_name: string) &priority=5 - { - if ( ! log_dhcpdecline ) - return; - - local info: Info; - info$ts = network_time(); - info$id = c$id; - info$uid = c$uid; - info$trans_id = msg$xid; - info$msg_type = "DHCPDECLINE"; - - if ( msg$h_addr != "" ) - info$mac = msg$h_addr; - - if ( host_name != "" ) - info$host_name = host_name; - - c$dhcp = info; - } - -event dhcp_nak(c: connection, msg: dhcp_msg, host_name: string) &priority=5 - { - if ( ! log_dhcpnak ) - return; - - local info: Info; - info$ts = network_time(); - info$id = c$id; - info$uid = c$uid; - info$trans_id = msg$xid; - info$msg_type = "DHCPNAK"; - - if ( msg$h_addr != "" ) - info$mac = msg$h_addr; - - if ( host_name != "" ) - info$host_name = host_name; - - c$dhcp = info; - } - -event dhcp_release(c: connection, msg: dhcp_msg, host_name: string) &priority=5 - { - if ( ! log_dhcprelease ) - return; - - local info: Info; - info$ts = network_time(); - info$id = c$id; - info$uid = c$uid; - info$trans_id = msg$xid; - info$msg_type = "DHCPRELEASE"; - - if ( msg$h_addr != "" ) - info$mac = msg$h_addr; - - if ( host_name != "" ) - info$host_name = host_name; - - c$dhcp = info; - } - -event dhcp_inform(c: connection, msg: dhcp_msg, host_name: string) &priority=5 - { - if ( ! log_dhcpinform ) - return; - - local info: Info; - info$ts = network_time(); - info$id = c$id; - info$uid = c$uid; - info$trans_id = msg$xid; - info$msg_type = "DHCPINFORM"; - - if ( msg$h_addr != "" ) - info$mac = msg$h_addr; - - if ( host_name != "" ) - info$host_name = host_name; - - c$dhcp = info; - } - -event dhcp_ack(c: connection, msg: dhcp_msg, mask: addr, router: dhcp_router_list, lease: interval, serv_addr: addr, host_name: string) &priority=4 - { - ## For the sake of consistency, let's add msg_type to DHCPACK as well. - c$dhcp$msg_type = "DHCPACK"; - ## host_name is generally not in ACKs, but let's check anyway. - if ( host_name != "" ) - c$dhcp$host_name = host_name; - } - -#### We log stuff at a lower priority, in case any other scripts would like to extend the Info record first. - -event dhcp_offer(c: connection, msg: dhcp_msg, mask: addr, router: dhcp_router_list, lease: interval, serv_addr: addr, host_name: string) &priority=1 - { - if ( log_dhcpoffer ) - Log::write(DHCP::LOG, c$dhcp); - } - -event dhcp_discover(c: connection, msg: dhcp_msg, req_addr: addr, host_name: string) &priority=1 - { - if ( log_dhcpdiscover ) - Log::write(DHCP::LOG, c$dhcp); - } - -event dhcp_request(c: connection, msg: dhcp_msg, req_addr: addr, serv_addr: addr, host_name: string) &priority=1 - { - if ( log_dhcprequest ) - Log::write(DHCP::LOG, c$dhcp); - } - -event dhcp_decline(c: connection, msg: dhcp_msg, host_name: string) &priority=1 - { - if ( log_dhcpdecline ) - Log::write(DHCP::LOG, c$dhcp); - } - -event dhcp_nak(c: connection, msg: dhcp_msg, host_name: string) &priority=1 - { - if ( log_dhcpnak ) - Log::write(DHCP::LOG, c$dhcp); - } - -event dhcp_release(c: connection, msg: dhcp_msg, host_name: string) &priority=1 - { - if ( log_dhcprelease ) - Log::write(DHCP::LOG, c$dhcp); - } - -event dhcp_inform(c: connection, msg: dhcp_msg, host_name: string) &priority=1 - { - if ( log_dhcpinform ) - Log::write(DHCP::LOG, c$dhcp); - } - diff --git a/scripts/policy/protocols/dhcp/known-devices-and-hostnames.bro b/scripts/policy/protocols/dhcp/known-devices-and-hostnames.bro new file mode 100644 index 0000000000..95770ce273 --- /dev/null +++ b/scripts/policy/protocols/dhcp/known-devices-and-hostnames.bro @@ -0,0 +1,22 @@ +@load policy/misc/known-devices + +module Known; + +export { + redef record DevicesInfo += { + ## The value of the DHCP host name option, if seen + dhcp_host_name: string &log &optional; + }; +} + +event dhcp_request(c: connection, msg: dhcp_msg, req_addr: addr, serv_addr: addr, host_name: string) + { + if ( msg$h_addr == "" ) + return; + + if ( msg$h_addr !in known_devices ) + { + add known_devices[msg$h_addr]; + Log::write(Known::DEVICES_LOG, [$ts=network_time(), $mac=msg$h_addr, $dhcp_host_name=host_name]); + } + } From 1786a3b92d8bd4f9929decfcf74f59ab59d65334 Mon Sep 17 00:00:00 2001 From: Vlad Grigorescu Date: Wed, 31 Jul 2013 17:30:56 -0400 Subject: [PATCH 3/3] DHCP: Adding unit tests. --- scripts/base/protocols/dhcp/main.bro | 8 ++++++-- .../dhcp/known-devices-and-hostnames.bro | 12 ++++++++++++ .../dhcp.log | 10 ++++++++++ .../scripts.base.protocols.dhcp.inform/dhcp.log | 10 ++++++++++ .../known_devices.log | 11 +++++++++++ testing/btest/Traces/dhcp/dhcp.trace | Bin 0 -> 3140 bytes testing/btest/Traces/dhcp/dhcp_inform.trace | Bin 0 -> 687 bytes .../base/protocols/dhcp/dhcp-all-msg-types.btest | 6 ++++++ .../scripts/base/protocols/dhcp/inform.test | 5 +++++ .../dhcp/known-devices-and-hostnames/basic.test | 8 ++++++++ 10 files changed, 68 insertions(+), 2 deletions(-) create mode 100644 testing/btest/Baseline/scripts.base.protocols.dhcp.dhcp-all-msg-types/dhcp.log create mode 100644 testing/btest/Baseline/scripts.base.protocols.dhcp.inform/dhcp.log create mode 100644 testing/btest/Baseline/scripts.policy.protocols.dhcp.known-devices-and-hostnames.basic/known_devices.log create mode 100644 testing/btest/Traces/dhcp/dhcp.trace create mode 100644 testing/btest/Traces/dhcp/dhcp_inform.trace create mode 100644 testing/btest/scripts/base/protocols/dhcp/dhcp-all-msg-types.btest create mode 100644 testing/btest/scripts/base/protocols/dhcp/inform.test create mode 100644 testing/btest/scripts/policy/protocols/dhcp/known-devices-and-hostnames/basic.test diff --git a/scripts/base/protocols/dhcp/main.bro b/scripts/base/protocols/dhcp/main.bro index 05491361ff..07bd437579 100644 --- a/scripts/base/protocols/dhcp/main.bro +++ b/scripts/base/protocols/dhcp/main.bro @@ -57,13 +57,17 @@ event dhcp_ack(c: connection, msg: dhcp_msg, mask: addr, router: dhcp_router_lis info$ts = network_time(); info$id = c$id; info$uid = c$uid; - info$assigned_ip = reverse_ip(msg$yiaddr); info$lease_time = lease; info$trans_id = msg$xid; if ( msg$h_addr != "" ) info$mac = msg$h_addr; - + + if ( reverse_ip(msg$yiaddr) != 0.0.0.0 ) + info$assigned_ip = reverse_ip(msg$yiaddr); + else + info$assigned_ip = c$id$orig_h; + c$dhcp = info; Log::write(DHCP::LOG, c$dhcp); diff --git a/scripts/policy/protocols/dhcp/known-devices-and-hostnames.bro b/scripts/policy/protocols/dhcp/known-devices-and-hostnames.bro index 95770ce273..ddb058f2e0 100644 --- a/scripts/policy/protocols/dhcp/known-devices-and-hostnames.bro +++ b/scripts/policy/protocols/dhcp/known-devices-and-hostnames.bro @@ -20,3 +20,15 @@ event dhcp_request(c: connection, msg: dhcp_msg, req_addr: addr, serv_addr: addr Log::write(Known::DEVICES_LOG, [$ts=network_time(), $mac=msg$h_addr, $dhcp_host_name=host_name]); } } + +event dhcp_inform(c: connection, msg: dhcp_msg, host_name: string) + { + if ( msg$h_addr == "" ) + return; + + if ( msg$h_addr !in known_devices ) + { + add known_devices[msg$h_addr]; + Log::write(Known::DEVICES_LOG, [$ts=network_time(), $mac=msg$h_addr, $dhcp_host_name=host_name]); + } + } diff --git a/testing/btest/Baseline/scripts.base.protocols.dhcp.dhcp-all-msg-types/dhcp.log b/testing/btest/Baseline/scripts.base.protocols.dhcp.dhcp-all-msg-types/dhcp.log new file mode 100644 index 0000000000..b52d455a4a --- /dev/null +++ b/testing/btest/Baseline/scripts.base.protocols.dhcp.dhcp-all-msg-types/dhcp.log @@ -0,0 +1,10 @@ +#separator \x09 +#set_separator , +#empty_field (empty) +#unset_field - +#path dhcp +#open 2013-07-31-21-00-49 +#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p mac assigned_ip lease_time trans_id +#types time string addr port addr port string addr interval count +1370200444.371332 nQcgTWjvg4c 128.2.6.189 68 128.2.6.152 67 90:b1:1c:99:49:29 128.2.6.189 900.000000 1984 +#close 2013-07-31-21-00-50 diff --git a/testing/btest/Baseline/scripts.base.protocols.dhcp.inform/dhcp.log b/testing/btest/Baseline/scripts.base.protocols.dhcp.inform/dhcp.log new file mode 100644 index 0000000000..d8f626efe3 --- /dev/null +++ b/testing/btest/Baseline/scripts.base.protocols.dhcp.inform/dhcp.log @@ -0,0 +1,10 @@ +#separator \x09 +#set_separator , +#empty_field (empty) +#unset_field - +#path dhcp +#open 2013-07-31-21-00-55 +#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p mac assigned_ip lease_time trans_id +#types time string addr port addr port string addr interval count +1374432420.191205 FrJExwHcSal 128.2.6.122 68 128.2.6.152 67 90:b1:1c:99:49:29 128.2.6.122 0.000000 2754407505 +#close 2013-07-31-21-00-55 diff --git a/testing/btest/Baseline/scripts.policy.protocols.dhcp.known-devices-and-hostnames.basic/known_devices.log b/testing/btest/Baseline/scripts.policy.protocols.dhcp.known-devices-and-hostnames.basic/known_devices.log new file mode 100644 index 0000000000..91d37f8950 --- /dev/null +++ b/testing/btest/Baseline/scripts.policy.protocols.dhcp.known-devices-and-hostnames.basic/known_devices.log @@ -0,0 +1,11 @@ +#separator \x09 +#set_separator , +#empty_field (empty) +#unset_field - +#path known_devices +#open 2013-07-31-21-27-41 +#fields ts mac dhcp_host_name +#types time string string +1370200443.344965 90:b1:1c:99:49:29 btest.is.cool +1374432420.186878 90:b1:1c:99:49:29 (empty) +#close 2013-07-31-21-27-41 diff --git a/testing/btest/Traces/dhcp/dhcp.trace b/testing/btest/Traces/dhcp/dhcp.trace new file mode 100644 index 0000000000000000000000000000000000000000..aeb00a133f1f70d493c3152f7c1f3a7ea458c2dd GIT binary patch literal 3140 zcmca|c+)~A1{MYw`2U}Qff2~5nz=eK^&Ja?F^~ho|G{9wMwywOnj8$S42-HEWe$Rs zCqOzuk_;{k&WwUa%NZHjfFkS%K;q~Cq90v!=<<`Bf|E@d8I4#DthjuLhc~GtwYWqt zvsf=VKR@R`C)gaVA+Adfc)>V!5hvCRPDLbkPwV0n?~S~)&J2kt z>?s};kZ*li8Nz^p2*QwnWI&{N4^RL$2yXffifnK|f>OMRojfANH-MCY0Z0vqF+5W| z8zdkt(kEij^luCOx5 z0ZS7Q#weNvphfdtP;|hGW`;lPp(8J?v;40EhSmpFHj+b&r4kxibI^(!Fn4&CzwH0( gfuUtz$VPIt|6~m`w2q9N(Bd#=VbnW(0DEZ)0CO2oasU7T literal 0 HcmV?d00001 diff --git a/testing/btest/Traces/dhcp/dhcp_inform.trace b/testing/btest/Traces/dhcp/dhcp_inform.trace new file mode 100644 index 0000000000000000000000000000000000000000..798ca84149dece44eaf6f1cb91874a5dba63d86a GIT binary patch literal 687 zcmca|c+)~A1{MYw`2U}Qff2}AqVp#3-%Tb4MIZ-+8F(}u_})+0C^OSjlY_yPfl-)& z!GXa+Q0GVk6I&G!&tPz2aAx@b{177}8(25UM35Yah5?9v7;os)$xXq@ri>isjEw(5 z0rvD26GIp=}0&I=&Gj>q$49@^#HDdt< P0wc?RU~&T{IN|{S|HnC| literal 0 HcmV?d00001 diff --git a/testing/btest/scripts/base/protocols/dhcp/dhcp-all-msg-types.btest b/testing/btest/scripts/base/protocols/dhcp/dhcp-all-msg-types.btest new file mode 100644 index 0000000000..752ab91780 --- /dev/null +++ b/testing/btest/scripts/base/protocols/dhcp/dhcp-all-msg-types.btest @@ -0,0 +1,6 @@ +# This tests that DHCP leases are logged in dhcp.log +# The trace has a message of each DHCP message type, +# but only one lease should show up in the logs. + +# @TEST-EXEC: bro -r $TRACES/dhcp/dhcp.trace %INPUT +# @TEST-EXEC: btest-diff dhcp.log diff --git a/testing/btest/scripts/base/protocols/dhcp/inform.test b/testing/btest/scripts/base/protocols/dhcp/inform.test new file mode 100644 index 0000000000..652fd1ae45 --- /dev/null +++ b/testing/btest/scripts/base/protocols/dhcp/inform.test @@ -0,0 +1,5 @@ +# DHCPINFORM leases are special-cased in the code. +# This tests that those leases are correctly logged. + +# @TEST-EXEC: bro -r $TRACES/dhcp/dhcp_inform.trace %INPUT +# @TEST-EXEC: btest-diff dhcp.log diff --git a/testing/btest/scripts/policy/protocols/dhcp/known-devices-and-hostnames/basic.test b/testing/btest/scripts/policy/protocols/dhcp/known-devices-and-hostnames/basic.test new file mode 100644 index 0000000000..c2fcc1397c --- /dev/null +++ b/testing/btest/scripts/policy/protocols/dhcp/known-devices-and-hostnames/basic.test @@ -0,0 +1,8 @@ +# This tests that the known_devices log is created, +# that devices are logged by MAC address, and that +# the DHCP hostname is added, if available. + +# @TEST-EXEC: bro -r $TRACES/dhcp/dhcp.trace -r $TRACES/dhcp/dhcp_inform.trace %INPUT +# @TEST-EXEC: btest-diff known_devices.log + +@load policy/protocols/dhcp/known-devices-and-hostnames \ No newline at end of file