mirror of
https://github.com/zeek/zeek.git
synced 2025-10-06 00:28:21 +00:00
dhcpv6: WIP
This commit is contained in:
parent
c8098fd7fa
commit
8e8eb7eccd
5 changed files with 514 additions and 22 deletions
|
@ -1,4 +1,7 @@
|
||||||
|
@if ( have_spicy_analyzers() ) # prototypes below must not be used with legacy analyzer
|
||||||
@load ./consts
|
@load ./consts
|
||||||
|
@load ./spicy-events
|
||||||
@load ./main
|
@load ./main
|
||||||
|
|
||||||
@load-sigs ./dpd.sig
|
@load-sigs ./dpd.sig
|
||||||
|
@endif
|
||||||
|
|
|
@ -2,11 +2,206 @@ module DHCPv6;
|
||||||
|
|
||||||
export {
|
export {
|
||||||
const message_types = {
|
const message_types = {
|
||||||
[1] = "SOLICIT",
|
[1] = "SOLICIT",
|
||||||
} &default = function(n: count): string { return fmt("unknown-message-type-%d", n); };
|
[2] = "ADVERTISE",
|
||||||
|
[3] = "REQUEST",
|
||||||
|
[4] = "CONFIRM",
|
||||||
|
[5] = "RENEW",
|
||||||
|
[6] = "REBIND",
|
||||||
|
[7] = "REPLY",
|
||||||
|
[8] = "RELEASE",
|
||||||
|
[9] = "DECLINE",
|
||||||
|
[10] = "RECONFIGURE",
|
||||||
|
[11] = "INFORMATION_REQUEST",
|
||||||
|
} &default = function(n: count): string { return fmt("unk-%d", n); };
|
||||||
|
|
||||||
|
## DUID types
|
||||||
|
const duid_types = {
|
||||||
|
[1] = "LLT",
|
||||||
|
[2] = "EN",
|
||||||
|
[3] = "LL",
|
||||||
|
[4] = "UUID",
|
||||||
|
} &default = function(n: count): string { return fmt("unk-%d", n); };
|
||||||
|
|
||||||
|
## Status codes
|
||||||
|
# Full list https://www.iana.org/assignments/dhcpv6-parameters/dhcpv6-parameters.xhtml#dhcpv6-parameters-5
|
||||||
|
const status_codes = {
|
||||||
|
[0] = "Success",
|
||||||
|
[1] = "UnspecFail",
|
||||||
|
[2] = "NoAddrsAvail",
|
||||||
|
[3] = "NoBinding",
|
||||||
|
[4] = "NotOnLink",
|
||||||
|
[5] = "UseMulticast",
|
||||||
|
[6] = "NoPrefixAvail",
|
||||||
|
[7] = "UnknownQueryType",
|
||||||
|
[8] = "MalformedQuery",
|
||||||
|
[9] = "NotConfigured",
|
||||||
|
[10] = "NotAllowed",
|
||||||
|
[11] = "QueryTerminated",
|
||||||
|
[12] = "DataMissing",
|
||||||
|
[13] = "CatchUpComplete",
|
||||||
|
[14] = "NotSupported",
|
||||||
|
[15] = "TLSConnectionRefused",
|
||||||
|
[16] = "AddressInUse",
|
||||||
|
[17] = "ConfigurationConflict",
|
||||||
|
[18] = "MissingBindingInformation",
|
||||||
|
[19] = "OutdatedBindingInformation",
|
||||||
|
[20] = "ServerShuttingDown",
|
||||||
|
[21] = "DNSUpdateNotSupported",
|
||||||
|
[22] = "ExcessiveTimeSkew",
|
||||||
|
} &default = function(n: count): string { return fmt("unk-%d", n); };
|
||||||
|
|
||||||
|
|
||||||
## Option types mapped to their names.
|
## Option types mapped to their names.
|
||||||
const option_types = {
|
const option_types = {
|
||||||
[1] = "???",
|
[0] = "Reserved",
|
||||||
} &default = function(n: count): string { return fmt("unknown-option-type-%d", n); };
|
[1] = "CLIENTID",
|
||||||
|
[2] = "SERVERID",
|
||||||
|
[3] = "IA_NA",
|
||||||
|
[4] = "IA_TA",
|
||||||
|
[5] = "IAADDR",
|
||||||
|
[6] = "ORO",
|
||||||
|
[7] = "PREFERENCE",
|
||||||
|
[8] = "ELAPSED_TIME",
|
||||||
|
[9] = "RELAY_MSG",
|
||||||
|
[10] = "Unassigned",
|
||||||
|
[11] = "AUTH",
|
||||||
|
[12] = "UNICAST",
|
||||||
|
[13] = "STATUS_CODE",
|
||||||
|
[14] = "RAPID_COMMIT",
|
||||||
|
[15] = "USER_CLASS",
|
||||||
|
[16] = "VENDOR_CLASS",
|
||||||
|
[17] = "VENDOR_OPTS",
|
||||||
|
[18] = "INTERFACE_ID",
|
||||||
|
[19] = "RECONF_MSG",
|
||||||
|
[20] = "RECONF_ACCEPT",
|
||||||
|
[21] = "SIP_SERVER_D",
|
||||||
|
[22] = "SIP_SERVER_A",
|
||||||
|
[23] = "DNS_SERVERS",
|
||||||
|
[24] = "DOMAIN_LIST",
|
||||||
|
[25] = "IA_PD",
|
||||||
|
[26] = "IAPREFIX",
|
||||||
|
[27] = "NIS_SERVERS",
|
||||||
|
[28] = "NISP_SERVERS",
|
||||||
|
[29] = "NIS_DOMAIN_NAME",
|
||||||
|
[30] = "NISP_DOMAIN_NAME",
|
||||||
|
[31] = "SNTP_SERVERS",
|
||||||
|
[32] = "INFORMATION_REFRESH_TIME",
|
||||||
|
[33] = "BCMCS_SERVER_D",
|
||||||
|
[34] = "BCMCS_SERVER_A",
|
||||||
|
[35] = "Unassigned",
|
||||||
|
[36] = "GEOCONF_CIVIC",
|
||||||
|
[37] = "REMOTE_ID",
|
||||||
|
[38] = "SUBSCRIBER_ID",
|
||||||
|
[39] = "CLIENT_FQDN",
|
||||||
|
[40] = "PANA_AGENT",
|
||||||
|
[41] = "NEW_POSIX_TIMEZONE",
|
||||||
|
[42] = "NEW_TZDB_TIMEZONE",
|
||||||
|
[43] = "ERO",
|
||||||
|
[44] = "LQ_QUERY",
|
||||||
|
[45] = "CLIENT_DATA",
|
||||||
|
[46] = "CLT_TIME",
|
||||||
|
[47] = "LQ_RELAY_DATA",
|
||||||
|
[48] = "LQ_CLIENT_LINK",
|
||||||
|
[49] = "MIP6_HNIDF",
|
||||||
|
[50] = "MIP6_VDINF",
|
||||||
|
[51] = "V6_LOST",
|
||||||
|
[52] = "CAPWAP_AC_V6",
|
||||||
|
[53] = "RELAY_ID",
|
||||||
|
[54] = "IPv6_Address-MoS",
|
||||||
|
[55] = "IPv6_FQDN-MoS",
|
||||||
|
[56] = "NTP_SERVER",
|
||||||
|
[57] = "V6_ACCESS_DOMAIN",
|
||||||
|
[58] = "SIP_UA_CS_LIST",
|
||||||
|
[59] = "OPT_BOOTFILE_URL",
|
||||||
|
[60] = "OPT_BOOTFILE_PARAM",
|
||||||
|
[61] = "CLIENT_ARCH_TYPE",
|
||||||
|
[62] = "NII",
|
||||||
|
[63] = "GEOLOCATION",
|
||||||
|
[64] = "AFTR_NAME",
|
||||||
|
[65] = "ERP_LOCAL_DOMAIN_NAME",
|
||||||
|
[66] = "RSOO",
|
||||||
|
[67] = "PD_EXCLUDE",
|
||||||
|
[68] = "VSS",
|
||||||
|
[69] = "MIP6_IDINF",
|
||||||
|
[70] = "MIP6_UDINF",
|
||||||
|
[71] = "MIP6_HNP",
|
||||||
|
[72] = "MIP6_HAA",
|
||||||
|
[73] = "MIP6_HAF",
|
||||||
|
[74] = "RDNSS_SELECTION",
|
||||||
|
[75] = "KRB_PRINCIPAL_NAME",
|
||||||
|
[76] = "KRB_REALM_NAME",
|
||||||
|
[77] = "KRB_DEFAULT_REALM_NAME",
|
||||||
|
[78] = "KRB_KDC",
|
||||||
|
[79] = "CLIENT_LINKLAYER_ADDR",
|
||||||
|
[80] = "LINK_ADDRESS",
|
||||||
|
[81] = "RADIUS",
|
||||||
|
[82] = "SOL_MAX_RT",
|
||||||
|
[83] = "INF_MAX_RT",
|
||||||
|
[84] = "ADDRSEL",
|
||||||
|
[85] = "ADDRSEL_TABLE",
|
||||||
|
[86] = "V6_PCP_SERVER",
|
||||||
|
[87] = "DHCPV4_MSG",
|
||||||
|
[88] = "DHCP4_O_DHCP6_SERVER",
|
||||||
|
[89] = "S46_RULE",
|
||||||
|
[90] = "S46_BR",
|
||||||
|
[91] = "S46_DMR",
|
||||||
|
[92] = "S46_V4V6BIND",
|
||||||
|
[93] = "S46_PORTPARAMS",
|
||||||
|
[94] = "S46_CONT_MAPE",
|
||||||
|
[95] = "S46_CONT_MAPT",
|
||||||
|
[96] = "S46_CONT_LW",
|
||||||
|
[97] = "4RD",
|
||||||
|
[98] = "4RD_MAP_RULE",
|
||||||
|
[99] = "4RD_NON_MAP_RULE",
|
||||||
|
[100] = "LQ_BASE_TIME",
|
||||||
|
[101] = "LQ_START_TIME",
|
||||||
|
[102] = "LQ_END_TIME",
|
||||||
|
[103] = "DHCP_Captive_Portal",
|
||||||
|
[104] = "MPL_PARAMETERS",
|
||||||
|
[105] = "ANI_ATT",
|
||||||
|
[106] = "ANI_NETWORK_NAME",
|
||||||
|
[107] = "ANI_AP_NAME",
|
||||||
|
[108] = "ANI_AP_BSSID",
|
||||||
|
[109] = "ANI_OPERATOR_ID",
|
||||||
|
[110] = "ANI_OPERATOR_REALM",
|
||||||
|
[111] = "S46_PRIORITY",
|
||||||
|
[112] = "MUD_URL_V6",
|
||||||
|
[113] = "V6_PREFIX64",
|
||||||
|
[114] = "F_BINDING_STATUS",
|
||||||
|
[115] = "F_CONNECT_FLAGS",
|
||||||
|
[116] = "F_DNS_REMOVAL_INFO",
|
||||||
|
[117] = "F_DNS_HOST_NAME",
|
||||||
|
[118] = "F_DNS_ZONE_NAME",
|
||||||
|
[119] = "F_DNS_FLAGS",
|
||||||
|
[120] = "F_EXPIRATION_TIME",
|
||||||
|
[121] = "F_MAX_UNACKED_BNDUPD",
|
||||||
|
[122] = "F_MCLT",
|
||||||
|
[123] = "F_PARTNER_LIFETIME",
|
||||||
|
[124] = "F_PARTNER_LIFETIME_SENT",
|
||||||
|
[125] = "F_PARTNER_DOWN_TIME",
|
||||||
|
[126] = "F_PARTNER_RAW_CLT_TIME",
|
||||||
|
[127] = "F_PROTOCOL_VERSION",
|
||||||
|
[128] = "F_KEEPALIVE_TIME",
|
||||||
|
[129] = "F_RECONFIGURE_DATA",
|
||||||
|
[130] = "F_RELATIONSHIP_NAME",
|
||||||
|
[131] = "F_SERVER_FLAGS",
|
||||||
|
[132] = "F_SERVER_STATE",
|
||||||
|
[133] = "F_START_TIME_OF_STATE",
|
||||||
|
[134] = "F_STATE_EXPIRATION_TIME",
|
||||||
|
[135] = "RELAY_PORT",
|
||||||
|
[136] = "V6_SZTP_REDIRECT",
|
||||||
|
[137] = "S46_BIND_IPV6_PREFIX",
|
||||||
|
[138] = "IA_LL",
|
||||||
|
[139] = "LLADDR",
|
||||||
|
[140] = "SLAP_QUAD",
|
||||||
|
[141] = "V6_DOTS_RI",
|
||||||
|
[142] = "V6_DOTS_ADDRESS",
|
||||||
|
[143] = "IPv6_Address-ANDSF",
|
||||||
|
[144] = "V6_DNR",
|
||||||
|
[145] = "REGISTERED_DOMAIN",
|
||||||
|
[146] = "FORWARD_DIST_MANAGER",
|
||||||
|
[147] = "REVERSE_DIST_MANAGER",
|
||||||
|
[148] = "ADDR_REG_ENABLE",
|
||||||
|
} &default = function(n: count): string { return fmt("unk-%d", n); };
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,21 +8,79 @@ export {
|
||||||
|
|
||||||
global log_policy: Log::PolicyHook;
|
global log_policy: Log::PolicyHook;
|
||||||
|
|
||||||
|
type DUID: record {
|
||||||
|
## Type of DUID in string format.
|
||||||
|
typ: string;
|
||||||
|
## DUID in hex format.
|
||||||
|
data: string;
|
||||||
|
} &log;
|
||||||
|
|
||||||
|
type Status: record {
|
||||||
|
code: string;
|
||||||
|
message: string;
|
||||||
|
} &log;
|
||||||
|
|
||||||
|
type IA_NA: record {
|
||||||
|
iaid: count &log &optional;
|
||||||
|
t1: interval &log &optional;
|
||||||
|
t2: interval &log &optional;
|
||||||
|
iaaddr: addr &optional; # This could be more than just one.
|
||||||
|
} &log;
|
||||||
|
|
||||||
## The record type which contains the column fields of the DHCP log.
|
## The record type which contains the column fields of the DHCP log.
|
||||||
type Info: record {
|
type Info: record {
|
||||||
## The earliest time at which a DHCP message over the
|
## The earliest time at which a DHCP message over the
|
||||||
## associated connection is observed.
|
## associated connection is observed.
|
||||||
ts: time &log;
|
ts: time &log;
|
||||||
|
|
||||||
|
## Transaction ID.
|
||||||
|
transaction_id: count &log;
|
||||||
|
|
||||||
|
client_msg_type: string &log &optional;
|
||||||
|
server_msg_type: string &log &optional;
|
||||||
|
|
||||||
|
client_duid: DUID &log &optional;
|
||||||
|
server_duid: DUID &log &optional;
|
||||||
|
|
||||||
|
client_options: vector of string &log;
|
||||||
|
client_requested_options: vector of string &log &optional;
|
||||||
|
server_options: vector of string &log;
|
||||||
|
|
||||||
|
|
||||||
|
## Involved connection uids for this transaction.
|
||||||
|
uids: set[string] &log;
|
||||||
|
|
||||||
|
## Information of the *first* ia_na option given
|
||||||
|
## by the server.
|
||||||
|
ia_na: IA_NA &log &optional;
|
||||||
|
|
||||||
|
status: Status &log &optional;
|
||||||
|
|
||||||
|
## If the server provided a jj
|
||||||
|
client_fqdn: string &log &optional;
|
||||||
|
|
||||||
|
logged: bool &default=F;
|
||||||
|
};
|
||||||
|
|
||||||
|
type State: record {
|
||||||
|
info: Info;
|
||||||
|
cid: conn_id;
|
||||||
|
uid: string;
|
||||||
|
is_client: bool;
|
||||||
};
|
};
|
||||||
|
|
||||||
## Event that can be handled to access the DHCP
|
## Event that can be handled to access the DHCP
|
||||||
## record as it is sent on to the logging framework.
|
## record as it is sent on to the logging framework.
|
||||||
global log_dhcpv6: event(rec: Info);
|
global log_dhcpv6: event(rec: Info);
|
||||||
|
|
||||||
|
option transaction_timeout = 5sec;
|
||||||
|
|
||||||
|
global aggregate_msgs: event(c: State);
|
||||||
}
|
}
|
||||||
|
|
||||||
# Add the dhcp info to the connection record.
|
# Add the dhcp info to the connection record.
|
||||||
redef record connection += {
|
redef record connection += {
|
||||||
dhcpv6: Info &optional;
|
dhcpv6_state: State &optional;
|
||||||
};
|
};
|
||||||
|
|
||||||
const ports = { 546/udp, 547/udp };
|
const ports = { 546/udp, 547/udp };
|
||||||
|
@ -30,15 +88,110 @@ redef likely_server_ports += { 547/udp };
|
||||||
|
|
||||||
event zeek_init() &priority=5
|
event zeek_init() &priority=5
|
||||||
{
|
{
|
||||||
Log::create_stream(DHCP::LOG, [$columns=Info, $ev=log_dhcpv6, $path="dhcpv6", $policy=log_policy]);
|
Log::create_stream(LOG, [$columns=Info, $ev=log_dhcpv6, $path="dhcpv6", $policy=log_policy]);
|
||||||
Analyzer::register_for_ports(Analyzer::ANALYZER_DHCPV6, ports);
|
Analyzer::register_for_ports(Analyzer::ANALYZER_DHCPV6, ports);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function do_log(tbl: table[count] of State, transaction_id: count): interval
|
||||||
|
{
|
||||||
|
local rec = tbl[transaction_id]$info;
|
||||||
|
print "expire_func", rec;
|
||||||
|
if ( ! rec$logged )
|
||||||
|
{
|
||||||
|
Log::write(LOG, rec);
|
||||||
|
rec$logged = T;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0sec;
|
||||||
|
}
|
||||||
|
|
||||||
|
## On manager, globally?
|
||||||
|
global transactions: table[count] of State &write_expire=transaction_timeout &expire_func=do_log;
|
||||||
|
|
||||||
|
function merge(into_state: State, from_state: State): Info
|
||||||
|
{
|
||||||
|
local into = into_state$info;
|
||||||
|
local from = from_state$info;
|
||||||
|
local from_is_client = from_state$is_client;
|
||||||
|
local from_is_server = ! from_is_client;
|
||||||
|
|
||||||
|
add into$uids[from_state$uid];
|
||||||
|
|
||||||
|
if ( from_is_server )
|
||||||
|
{
|
||||||
|
if ( from?$status )
|
||||||
|
into$status = from$status;
|
||||||
|
|
||||||
|
if ( from?$server_msg_type )
|
||||||
|
into$server_msg_type = from$server_msg_type;
|
||||||
|
|
||||||
|
into$server_options = from$server_options;
|
||||||
|
|
||||||
|
if ( from?$server_duid )
|
||||||
|
into$server_duid = from$server_duid;
|
||||||
|
|
||||||
|
if ( from?$ia_na )
|
||||||
|
into$ia_na = from$ia_na;
|
||||||
|
}
|
||||||
|
|
||||||
|
return into;
|
||||||
|
}
|
||||||
|
|
||||||
|
event aggregate_msgs(state: State)
|
||||||
|
{
|
||||||
|
local txid = state$info$transaction_id;
|
||||||
|
print "aggregate", state$is_client;
|
||||||
|
|
||||||
|
# First time we see this transaction, just store it.
|
||||||
|
if ( txid !in transactions )
|
||||||
|
{
|
||||||
|
transactions[txid] = state;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
local into_state = transactions[txid];
|
||||||
|
if ( into_state$is_client == state$is_client )
|
||||||
|
{
|
||||||
|
# Repeated send from client or server. Is this weird?
|
||||||
|
Weird::weird([$ts=network_time(), $uid=state$uid, $name="dhcpv6_resend"]);
|
||||||
|
Log::write(LOG, into_state$info);
|
||||||
|
transactions[txid] = state;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if ( ! into_state$is_client )
|
||||||
|
Weird::weird([$ts=network_time(), $uid=into_state$uid, $name="dhcpv6_server_before_client"]);
|
||||||
|
|
||||||
|
local info = merge(into_state, state);
|
||||||
|
Log::write(LOG, info);
|
||||||
|
|
||||||
|
# We do not delete the record immediately so that for a
|
||||||
|
# single client request that doesn't use SERVERID, we
|
||||||
|
# might process further replies.
|
||||||
|
info$logged = T;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function set_state(c: connection, is_orig: bool, transaction_id: count): State
|
||||||
|
{
|
||||||
|
print "set_state", c$id, is_orig;
|
||||||
|
c$dhcpv6_state = State($cid=c$id, $uid=c$uid, $is_client=is_orig);
|
||||||
|
c$dhcpv6_state$info = Info($ts=network_time(), $transaction_id=transaction_id);
|
||||||
|
add c$dhcpv6_state$info$uids[c$uid];
|
||||||
|
|
||||||
|
return c$dhcpv6_state;
|
||||||
|
}
|
||||||
|
|
||||||
# Aggregate DHCP messages to the manager.
|
# Aggregate DHCP messages to the manager.
|
||||||
event dhcpv6_message(c: connection, is_orig: bool)
|
event dhcpv6_message(c: connection, is_orig: bool, msg_type: count, transaction_id: count)
|
||||||
{
|
{
|
||||||
print c$uid, c$id, is_orig;
|
print "XXX dhcpv6_message", c$uid, c$id, is_orig, message_types[msg_type];
|
||||||
|
local state = set_state(c, is_orig, transaction_id);
|
||||||
|
if ( state$is_client )
|
||||||
|
state$info$client_msg_type = message_types[msg_type];
|
||||||
|
else
|
||||||
|
state$info$server_msg_type = message_types[msg_type];
|
||||||
|
# print "dhcpv6_message", c$uid, c$id, is_orig;
|
||||||
# if ( Cluster::is_enabled() && Cluster::local_node_type() != Cluster::MANAGER )
|
# if ( Cluster::is_enabled() && Cluster::local_node_type() != Cluster::MANAGER )
|
||||||
# Broker::publish(Cluster::manager_topic, DHCP::aggregate_msgs,
|
# Broker::publish(Cluster::manager_topic, DHCP::aggregate_msgs,
|
||||||
# network_time(), c$id, c$uid, is_orig, msg, options);
|
# network_time(), c$id, c$uid, is_orig, msg, options);
|
||||||
|
@ -46,9 +199,95 @@ event dhcpv6_message(c: connection, is_orig: bool)
|
||||||
# event DHCP::aggregate_msgs(network_time(), c$id, c$uid, is_orig, msg, options);
|
# event DHCP::aggregate_msgs(network_time(), c$id, c$uid, is_orig, msg, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
event dhcpv6_option(c: connection, is_orig: bool, code: count)
|
||||||
|
{
|
||||||
|
print "option", option_types[code];
|
||||||
|
local info = c$dhcpv6_state$info;
|
||||||
|
local opts = c$dhcpv6_state$is_client ? info$client_options : info$server_options;
|
||||||
|
opts += option_types[code];
|
||||||
|
}
|
||||||
|
|
||||||
|
event dhcpv6_option_clientid(c: connection, is_orig: bool, duid_type: count, data: string)
|
||||||
|
{
|
||||||
|
local info = c$dhcpv6_state$info;
|
||||||
|
info$client_duid = [$typ=duid_types[duid_type], $data=bytestring_to_hexstr(data)];
|
||||||
|
}
|
||||||
|
|
||||||
|
event dhcpv6_option_serverid(c: connection, is_orig: bool, duid_type: count, data: string)
|
||||||
|
{
|
||||||
|
local info = c$dhcpv6_state$info;
|
||||||
|
info$server_duid = [$typ=duid_types[duid_type], $data=bytestring_to_hexstr(data)];
|
||||||
|
}
|
||||||
|
|
||||||
|
event dhcpv6_option_status_code(c: connection, is_orig: bool, code: count, message: string)
|
||||||
|
{
|
||||||
|
local info = c$dhcpv6_state$info;
|
||||||
|
info$status = [$code=status_codes[code], $message=message];
|
||||||
|
}
|
||||||
|
|
||||||
|
event dhcpv6_option_requested_options(c: connection, is_orig: bool, options: vector of count)
|
||||||
|
{
|
||||||
|
print "requested options", options;
|
||||||
|
local vec: vector of string;
|
||||||
|
for ( _, o in options )
|
||||||
|
vec += option_types[o];
|
||||||
|
|
||||||
|
c$dhcpv6_state$info$client_requested_options = vec;
|
||||||
|
}
|
||||||
|
|
||||||
|
event dhcpv6_option_ia_na(c: connection, is_orig: bool, iaid: count, t1: interval, t2: interval)
|
||||||
|
{
|
||||||
|
local state = c$dhcpv6_state;
|
||||||
|
local info = state$info;
|
||||||
|
|
||||||
|
# Weird?
|
||||||
|
if ( ! info?$ia_na )
|
||||||
|
info$ia_na = IA_NA();
|
||||||
|
|
||||||
|
local ia_na = state$info$ia_na;
|
||||||
|
ia_na$iaid = iaid;
|
||||||
|
ia_na$t1 = t1;
|
||||||
|
ia_na$t2 = t2;
|
||||||
|
}
|
||||||
|
|
||||||
|
event dhcpv6_option_ipaddr(c: connection, is_orig: bool, addr6: addr, preferred_lifetime: interval, valid_lifetime: interval)
|
||||||
|
{
|
||||||
|
local state = c$dhcpv6_state;
|
||||||
|
if ( c$dhcpv6_state$is_client )
|
||||||
|
{
|
||||||
|
Weird::weird([$ts=network_time(), $uid=state$uid, $name="dhcpv6_ipaddr_option_from_client"]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
local info = state$info;
|
||||||
|
info$ia_na = IA_NA($iaaddr=addr6);
|
||||||
|
}
|
||||||
|
|
||||||
|
event dhcpv6_option_client_fqdn(c: connection, is_orig: bool, n: bool, o: bool, s: bool,
|
||||||
|
domain_name: string)
|
||||||
|
{
|
||||||
|
print "GGGGGRR client fqdn", "n", n, "o", o, "s", s, domain_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
event dhcpv6_message_end(c: connection, is_orig: bool, msg_type: count, transaction_id: count)
|
||||||
|
{
|
||||||
|
print "dhcpv6_message_end", c$uid, c$id, is_orig;
|
||||||
|
|
||||||
|
# TODO: Cluster, publish to manager
|
||||||
|
event aggregate_msgs(c$dhcpv6_state);
|
||||||
|
|
||||||
|
delete c$dhcpv6_state;
|
||||||
|
}
|
||||||
|
|
||||||
event zeek_done() &priority=-5
|
event zeek_done() &priority=-5
|
||||||
{
|
{
|
||||||
# Log any remaining data that hasn't already been logged!
|
# Log any remaining data that hasn't already been logged!
|
||||||
# for ( i in DHCP::join_data )
|
# for ( i in DHCP::join_data )
|
||||||
# join_data_expiration(DHCP::join_data, i);
|
# join_data_expiration(DHCP::join_data, i);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
hook log_policy(rec: Info, id: Log::ID, filter: Log::Filter)
|
||||||
|
{
|
||||||
|
print "log_policy", rec;
|
||||||
|
}
|
||||||
|
|
|
@ -8,4 +8,20 @@ protocol analyzer DHCPv6 over UDP:
|
||||||
|
|
||||||
import DHCPv6;
|
import DHCPv6;
|
||||||
|
|
||||||
on DHCPv6::Message -> event dhcpv6_message($conn, $is_orig);
|
on DHCPv6::Message::transaction_id -> event dhcpv6_message($conn, $is_orig, cast<uint32>(self.msg_type), self.transaction_id);
|
||||||
|
|
||||||
|
on DHCPv6::Option if (self.top_level) -> event dhcpv6_option($conn, $is_orig, cast<uint32>(self.code));
|
||||||
|
on DHCPv6::Option::client_id if (self.top_level) -> event dhcpv6_option_clientid($conn, $is_orig, cast<uint32>(self.client_id.duid_type), self.client_id.data);
|
||||||
|
on DHCPv6::Option::server_id if (self.top_level) -> event dhcpv6_option_serverid($conn, $is_orig, cast<uint32>(self.server_id.duid_type), self.server_id.data);
|
||||||
|
|
||||||
|
on DHCPv6::Option::status_code if (self.top_level) -> event dhcpv6_option_status_code($conn, $is_orig, cast<uint32>(self.status_code.code), self.status_code.message);
|
||||||
|
|
||||||
|
on DHCPv6::Option::request_option if (self.top_level) -> event dhcpv6_option_requested_options($conn, $is_orig, self.request_option.requested_options);
|
||||||
|
|
||||||
|
|
||||||
|
on DHCPv6::IA_NAOption -> event dhcpv6_option_ia_na($conn, $is_orig, self.iaid, interval(self.t1), interval(self.t2));
|
||||||
|
on DHCPv6::IAADDROption -> event dhcpv6_option_ipaddr($conn, $is_orig, self.addr6, interval(self.preferred_lifetime), interval(self.valid_lifetime));
|
||||||
|
on DHCPv6::ClientFQDNOption -> event dhcpv6_option_client_fqdn($conn, $is_orig, self.flags.n, self.flags.o, self.flags.s, self.domain_name);
|
||||||
|
|
||||||
|
|
||||||
|
on DHCPv6::Message::done -> event dhcpv6_message_end($conn, $is_orig, cast<uint32>(self.msg_type), self.transaction_id);
|
||||||
|
|
|
@ -26,7 +26,7 @@ type MessageType = enum {
|
||||||
INFORMATION_REQUEST = 11,
|
INFORMATION_REQUEST = 11,
|
||||||
};
|
};
|
||||||
|
|
||||||
type DDUIDType = enum {
|
type DUIDType = enum {
|
||||||
LLT = 1, # Link-Layer Address Plus Time https://datatracker.ietf.org/doc/html/rfc8415#section-11.2
|
LLT = 1, # Link-Layer Address Plus Time https://datatracker.ietf.org/doc/html/rfc8415#section-11.2
|
||||||
EN = 2, # Enterprise Number https://datatracker.ietf.org/doc/html/rfc8415#section-11.3
|
EN = 2, # Enterprise Number https://datatracker.ietf.org/doc/html/rfc8415#section-11.3
|
||||||
LL = 3, # Link-Layer Address https://datatracker.ietf.org/doc/html/rfc8415#section-11.4
|
LL = 3, # Link-Layer Address https://datatracker.ietf.org/doc/html/rfc8415#section-11.4
|
||||||
|
@ -54,13 +54,15 @@ type DUIDOption_UUID = unit {
|
||||||
};
|
};
|
||||||
|
|
||||||
type DUIDOption = unit {
|
type DUIDOption = unit {
|
||||||
duid_type: uint16 &convert=DDUIDType($$);
|
duid_type: uint16 &convert=DUIDType($$);
|
||||||
|
data: bytes &eod;
|
||||||
|
|
||||||
switch (self.duid_type) {
|
switch (self.duid_type) {
|
||||||
DDUIDType::LLT -> llt: DUIDOption_LLT;
|
DUIDType::LLT -> llt: DUIDOption_LLT;
|
||||||
DDUIDType::EN -> en: DUIDOption_EN;
|
DUIDType::EN -> en: DUIDOption_EN;
|
||||||
DDUIDType::LL -> ll: DUIDOption_LL;
|
DUIDType::LL -> ll: DUIDOption_LL;
|
||||||
DDUIDType::UUID -> uuid: DUIDOption_UUID;
|
DUIDType::UUID -> uuid: DUIDOption_UUID;
|
||||||
};
|
} &parse-from=self.data;
|
||||||
};
|
};
|
||||||
|
|
||||||
type StatusCode = enum {
|
type StatusCode = enum {
|
||||||
|
@ -82,14 +84,44 @@ type IA_NAOption = unit {
|
||||||
iaid: uint32;
|
iaid: uint32;
|
||||||
t1: uint32; # seconds
|
t1: uint32; # seconds
|
||||||
t2: uint32; # seconds
|
t2: uint32; # seconds
|
||||||
options: Option[] &eod;
|
options: Option(False)[] &eod;
|
||||||
};
|
};
|
||||||
|
|
||||||
type IAADDROption = unit {
|
type IAADDROption = unit {
|
||||||
addr_: addr &ipv6;
|
addr6: addr &ipv6;
|
||||||
preferred_lifetime: uint32;
|
preferred_lifetime: uint32;
|
||||||
valid_lifetime: uint32;
|
valid_lifetime: uint32;
|
||||||
options: Option[] &eod;
|
options: Option(False)[] &eod;
|
||||||
|
};
|
||||||
|
|
||||||
|
type RequestOption = unit {
|
||||||
|
requested_options: uint16[] &eod;
|
||||||
|
};
|
||||||
|
|
||||||
|
# DNS Label without compression.
|
||||||
|
# https://datatracker.ietf.org/doc/html/rfc1035#section-3.1
|
||||||
|
type Label = unit {
|
||||||
|
len: uint8 &requires=(($$ & 0xC0) == 0);
|
||||||
|
label: bytes &size=self.len;
|
||||||
|
} &convert=self.label;
|
||||||
|
|
||||||
|
# https://datatracker.ietf.org/doc/html/rfc4704#section-4
|
||||||
|
type ClientFQDNOption = unit {
|
||||||
|
var domain_name: bytes;
|
||||||
|
flags: bitfield(8) {
|
||||||
|
mbz: 0..4;
|
||||||
|
n: 5 &convert=($$ == 1);
|
||||||
|
o: 6 &convert=($$ == 1);
|
||||||
|
s: 7 &convert=($$ == 1);
|
||||||
|
} &bit-order=spicy::BitOrder::MSB0;
|
||||||
|
|
||||||
|
labels: Label[] &eod {
|
||||||
|
self.domain_name = b".".join($$);
|
||||||
|
}
|
||||||
|
|
||||||
|
on %done {
|
||||||
|
print "YYYYYYY", self.flags, self.domain_name;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
type OptionCode = enum {
|
type OptionCode = enum {
|
||||||
|
@ -98,10 +130,13 @@ type OptionCode = enum {
|
||||||
IA_NA = 3,
|
IA_NA = 3,
|
||||||
IA_TA = 4,
|
IA_TA = 4,
|
||||||
IAADDR = 5,
|
IAADDR = 5,
|
||||||
|
REQUEST = 6,
|
||||||
STATUS_CODE = 13,
|
STATUS_CODE = 13,
|
||||||
|
CLIENT_FQDN = 39,
|
||||||
};
|
};
|
||||||
|
|
||||||
type Option = unit {
|
type Option = unit(top_level: bool) {
|
||||||
|
var top_level: bool = top_level;
|
||||||
code: uint16 &convert=OptionCode($$);
|
code: uint16 &convert=OptionCode($$);
|
||||||
len: uint16;
|
len: uint16;
|
||||||
|
|
||||||
|
@ -111,7 +146,9 @@ type Option = unit {
|
||||||
OptionCode::IA_NA -> ia_na: IA_NAOption;
|
OptionCode::IA_NA -> ia_na: IA_NAOption;
|
||||||
# IA_TA
|
# IA_TA
|
||||||
OptionCode::IAADDR -> iaaddr: IAADDROption;
|
OptionCode::IAADDR -> iaaddr: IAADDROption;
|
||||||
|
OptionCode::REQUEST -> request_option: RequestOption;
|
||||||
OptionCode::STATUS_CODE -> status_code: StatusCodeOption;
|
OptionCode::STATUS_CODE -> status_code: StatusCodeOption;
|
||||||
|
OptionCode::CLIENT_FQDN -> client_fqdn: ClientFQDNOption;
|
||||||
* -> unknown: bytes &eod;
|
* -> unknown: bytes &eod;
|
||||||
} &size=self.len;
|
} &size=self.len;
|
||||||
};
|
};
|
||||||
|
@ -127,13 +164,15 @@ public type Message = unit {
|
||||||
|
|
||||||
transaction_id: uint8[3] &convert=uint32((uint32($$[0]) << 16) + (uint32($$[1]) << 8) + uint32($$[2]));
|
transaction_id: uint8[3] &convert=uint32((uint32($$[0]) << 16) + (uint32($$[1]) << 8) + uint32($$[2]));
|
||||||
|
|
||||||
options: Option[] &eod;
|
options: Option(True)[] &eod;
|
||||||
|
|
||||||
# Once the options are parsed, what do we actually want to
|
# Once the options are parsed, what do we actually want to
|
||||||
# send to script land? Maybe a vector with options that have
|
# send to script land? Maybe a vector with options that have
|
||||||
# a lot of optionals?
|
# a lot of optionals?
|
||||||
|
done: void;
|
||||||
|
|
||||||
on %done {
|
on %done {
|
||||||
|
return;
|
||||||
print self.msg_type, self.transaction_id;
|
print self.msg_type, self.transaction_id;
|
||||||
for (o in self.options) {
|
for (o in self.options) {
|
||||||
print o;
|
print o;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue