mirror of
https://github.com/zeek/zeek.git
synced 2025-10-02 14:48:21 +00:00
Implement EDNS Client Subnet Option
This commit is contained in:
parent
e891c310fb
commit
e7146c2a6b
11 changed files with 177 additions and 9 deletions
|
@ -3676,6 +3676,16 @@ type dns_edns_additional: record {
|
|||
is_query: count; ##< TODO.
|
||||
};
|
||||
|
||||
## An DNS EDNS Client Subnet (ECS) record.
|
||||
##
|
||||
## .. zeek:see:: dns_EDNS_ecs
|
||||
type dns_edns_ecs: record {
|
||||
family: string; ##< IP Family
|
||||
source_prefix_len: count; ##< Source Prefix Length.
|
||||
scope_prefix_len: count; ##< Scope Prefix Length.
|
||||
address: string; ##< Client Subnet Address.
|
||||
};
|
||||
|
||||
## An additional DNS TSIG record.
|
||||
##
|
||||
## .. zeek:see:: dns_TSIG_addl
|
||||
|
|
|
@ -527,6 +527,10 @@ event dns_SRV_reply(c: connection, msg: dns_msg, ans: dns_answer, target: string
|
|||
# {
|
||||
#
|
||||
# }
|
||||
# event dns_EDNS_ecs(c: connection, msg: dns_msg, opt: dns_edns_ecs)
|
||||
# {
|
||||
#
|
||||
# }
|
||||
#
|
||||
#event dns_TSIG_addl(c: connection, msg: dns_msg, ans: dns_tsig_additional)
|
||||
# {
|
||||
|
|
|
@ -107,6 +107,7 @@ zeek::RecordType* dns_msg;
|
|||
zeek::RecordType* dns_answer;
|
||||
zeek::RecordType* dns_soa;
|
||||
zeek::RecordType* dns_edns_additional;
|
||||
zeek::RecordType* dns_edns_ecs;
|
||||
zeek::RecordType* dns_tsig_additional;
|
||||
zeek::RecordType* dns_rrsig_rr;
|
||||
zeek::RecordType* dns_dnskey_rr;
|
||||
|
|
|
@ -149,6 +149,8 @@ extern zeek::RecordType* dns_soa;
|
|||
[[deprecated("Remove in v4.1. Perform your own lookup.")]]
|
||||
extern zeek::RecordType* dns_edns_additional;
|
||||
[[deprecated("Remove in v4.1. Perform your own lookup.")]]
|
||||
extern zeek::RecordType* dns_edns_ecs;
|
||||
[[deprecated("Remove in v4.1. Perform your own lookup.")]]
|
||||
extern zeek::RecordType* dns_tsig_additional;
|
||||
[[deprecated("Remove in v4.1. Perform your own lookup.")]]
|
||||
extern zeek::RecordType* dns_rrsig_rr;
|
||||
|
|
|
@ -700,8 +700,6 @@ bool DNS_Interpreter::ParseRR_EDNS(DNS_MsgInfo* msg,
|
|||
const u_char*& data, int& len, int rdlength,
|
||||
const u_char* msg_start)
|
||||
{
|
||||
// We need a pair-value set mechanism here to dump useful information
|
||||
// out to the policy side of the house if rdlength > 0.
|
||||
|
||||
if ( dns_EDNS_addl && ! msg->skip_event )
|
||||
analyzer->EnqueueConnEvent(dns_EDNS_addl,
|
||||
|
@ -710,13 +708,79 @@ bool DNS_Interpreter::ParseRR_EDNS(DNS_MsgInfo* msg,
|
|||
msg->BuildEDNS_Val()
|
||||
);
|
||||
|
||||
// Currently EDNS supports the movement of type:data pairs
|
||||
// in the RR_DATA section. Here's where we should put together
|
||||
// a corresponding mechanism.
|
||||
if ( rdlength > 0 )
|
||||
{ // deal with data
|
||||
data += rdlength;
|
||||
len -= rdlength;
|
||||
// parse EDNS options
|
||||
while ( len > 0 )
|
||||
{
|
||||
uint16_t option_code = ExtractShort(data, len);
|
||||
int option_len = ExtractShort(data, len);
|
||||
len -= option_len;
|
||||
|
||||
// TODO: Implement additional option codes
|
||||
switch ( option_code )
|
||||
{
|
||||
case TYPE_ECS:
|
||||
{
|
||||
struct EDNS_ECS opt;
|
||||
uint16_t ecs_family = ExtractShort(data, option_len);
|
||||
uint16_t source_scope = ExtractShort(data, option_len);
|
||||
opt.ecs_src_pfx_len = (source_scope >> 8) & 0xff;
|
||||
opt.ecs_scp_pfx_len = source_scope & 0xff;
|
||||
|
||||
// ADDRESS, variable number of octets, contains either an IPv4 or
|
||||
// IPv6 address, depending on FAMILY, which MUST be truncated to the
|
||||
// number of bits indicated by the SOURCE PREFIX-LENGTH field,
|
||||
// padding with 0 bits to pad to the end of the last octet needed.
|
||||
if ( ecs_family == L3_IPV4)
|
||||
{
|
||||
opt.ecs_family = make_intrusive<StringVal>("v4");
|
||||
uint32_t addr = 0;
|
||||
for (uint16_t shift_factor = 3; option_len > 0; option_len--)
|
||||
{
|
||||
addr |= data[0] << (shift_factor * 8);
|
||||
data++;
|
||||
shift_factor--;
|
||||
}
|
||||
addr = htonl(addr);
|
||||
opt.ecs_addr = make_intrusive<AddrVal>(addr);
|
||||
}
|
||||
else if ( ecs_family == L3_IPV6 )
|
||||
{
|
||||
opt.ecs_family = make_intrusive<StringVal>("v6");
|
||||
uint32_t addr[4] = { 0 };
|
||||
for (uint16_t i = 0, shift_factor = 15; option_len > 0; option_len--)
|
||||
{
|
||||
addr[i / 4] |= data[0] << ((shift_factor % 4) * 8);
|
||||
data++;
|
||||
i++;
|
||||
shift_factor--;
|
||||
}
|
||||
|
||||
for (uint8_t i = 0; i < 4; i++)
|
||||
{
|
||||
addr[i] = htonl(addr[i]);
|
||||
}
|
||||
opt.ecs_addr = make_intrusive<AddrVal>(addr);
|
||||
}
|
||||
else
|
||||
{
|
||||
// non ipv4/ipv6 family address
|
||||
data += option_len;
|
||||
break;
|
||||
}
|
||||
|
||||
analyzer->EnqueueConnEvent(dns_EDNS_ecs,
|
||||
analyzer->ConnVal(),
|
||||
msg->BuildHdrVal(),
|
||||
msg->BuildEDNS_ECS_Val(&opt)
|
||||
);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
data += option_len;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -1518,6 +1582,19 @@ zeek::RecordValPtr DNS_MsgInfo::BuildEDNS_Val()
|
|||
return r;
|
||||
}
|
||||
|
||||
zeek::RecordValPtr DNS_MsgInfo::BuildEDNS_ECS_Val(struct EDNS_ECS* opt)
|
||||
{
|
||||
static auto dns_edns_ecs = zeek::id::find_type<zeek::RecordType>("dns_edns_ecs");
|
||||
auto r = make_intrusive<RecordVal>(dns_edns_ecs);
|
||||
|
||||
r->Assign(0, opt->ecs_family);
|
||||
r->Assign(1, val_mgr->Count(opt->ecs_src_pfx_len));
|
||||
r->Assign(2, val_mgr->Count(opt->ecs_scp_pfx_len));
|
||||
r->Assign(3, opt->ecs_addr);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
zeek::RecordValPtr DNS_MsgInfo::BuildTSIG_Val(struct TSIG_DATA* tsig)
|
||||
{
|
||||
static auto dns_tsig_additional = zeek::id::find_type<zeek::RecordType>("dns_tsig_additional");
|
||||
|
|
|
@ -81,6 +81,27 @@ typedef enum {
|
|||
DNS_ADDITIONAL,
|
||||
} DNS_AnswerType;
|
||||
|
||||
// https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml
|
||||
// DNS EDNS0 Option Codes (OPT)
|
||||
typedef enum {
|
||||
TYPE_LLQ = 1, ///< https://www.iana.org/go/draft-sekar-dns-llq-06
|
||||
TYPE_UL = 2, ///< http://files.dns-sd.org/draft-sekar-dns-ul.txt
|
||||
TYPE_NSID = 3, ///< RFC5001
|
||||
TYPE_DAU = 5, ///< RFC6975
|
||||
TYPE_DHU = 6, ///< RFC6975
|
||||
TYPE_N3U = 7, ///< RFC6975
|
||||
TYPE_ECS = 8, ///< RFC7871
|
||||
TYPE_EXPIRE = 9, ///< RFC7314
|
||||
TYPE_TCP_KA = 11, ///< RFC7828
|
||||
TYPE_PAD = 12, ///< RFC7830
|
||||
TYPE_CHAIN = 13, ///< RFC7901
|
||||
TYPE_KEY_TAG = 14, ///< RFC8145
|
||||
TYPE_ERROR = 15, ///< https://www.iana.org/go/draft-ietf-dnsop-extended-error-16
|
||||
TYPE_CLIENT_TAG = 16, ///< https://www.iana.org/go/draft-bellis-dnsop-edns-tags
|
||||
TYPE_SERVER_TAG = 17, ///< https://www.iana.org/go/draft-bellis-dnsop-edns-tags
|
||||
TYPE_DEVICE_ID = 26946 ///< https://docs.umbrella.com/developer/networkdevices-api/identifying-dns-traffic2
|
||||
} EDNS_OPT_Type;
|
||||
|
||||
typedef enum {
|
||||
reserved0 = 0,
|
||||
RSA_MD5 = 1, ///< [RFC2537] NOT RECOMMENDED
|
||||
|
@ -128,6 +149,13 @@ struct EDNS_ADDITIONAL { // size
|
|||
unsigned short rdata_len; // 16
|
||||
};
|
||||
|
||||
struct EDNS_ECS {
|
||||
IntrusivePtr<StringVal> ecs_family; ///< EDNS client subnet address family
|
||||
uint16_t ecs_src_pfx_len; ///< EDNS client subnet source prefix length
|
||||
uint16_t ecs_scp_pfx_len; ///< EDNS client subnet scope prefix length
|
||||
IntrusivePtr<AddrVal> ecs_addr; ///< EDNS client subnet address
|
||||
};
|
||||
|
||||
struct TSIG_DATA {
|
||||
zeek::String* alg_name;
|
||||
unsigned long time_s;
|
||||
|
@ -182,6 +210,7 @@ public:
|
|||
zeek::RecordValPtr BuildHdrVal();
|
||||
zeek::RecordValPtr BuildAnswerVal();
|
||||
zeek::RecordValPtr BuildEDNS_Val();
|
||||
zeek::RecordValPtr BuildEDNS_ECS_Val(struct EDNS_ECS*);
|
||||
zeek::RecordValPtr BuildTSIG_Val(struct TSIG_DATA*);
|
||||
zeek::RecordValPtr BuildRRSIG_Val(struct RRSIG_DATA*);
|
||||
zeek::RecordValPtr BuildDNSKEY_Val(struct DNSKEY_DATA*);
|
||||
|
@ -271,6 +300,9 @@ protected:
|
|||
bool ParseRR_EDNS(DNS_MsgInfo* msg,
|
||||
const u_char*& data, int& len, int rdlength,
|
||||
const u_char* msg_start);
|
||||
bool ParseRR_EDNS_ECS(DNS_MsgInfo* msg,
|
||||
const u_char*& data, int& len, int rdlength,
|
||||
const u_char* msg_start);
|
||||
bool ParseRR_A(DNS_MsgInfo* msg,
|
||||
const u_char*& data, int& len, int rdlength);
|
||||
bool ParseRR_AAAA(DNS_MsgInfo* msg,
|
||||
|
|
|
@ -505,6 +505,29 @@ event dns_unknown_reply%(c: connection, msg: dns_msg, ans: dns_answer%);
|
|||
## dns_skip_all_addl dns_skip_all_auth dns_skip_auth
|
||||
event dns_EDNS_addl%(c: connection, msg: dns_msg, ans: dns_edns_additional%);
|
||||
|
||||
## Generated for DNS replies of type *EDNS*. For replies with multiple options,
|
||||
## an individual event is raised for each.
|
||||
##
|
||||
## See `Wikipedia <http://en.wikipedia.org/wiki/Domain_Name_System>`__ for more
|
||||
## information about the DNS protocol. Zeek analyzes both UDP and TCP DNS
|
||||
## sessions.
|
||||
##
|
||||
## c: The connection, which may be UDP or TCP depending on the type of the
|
||||
## transport-layer session being analyzed.
|
||||
##
|
||||
## msg: The parsed DNS message header.
|
||||
##
|
||||
## opt: The parsed EDNS option.
|
||||
##
|
||||
## .. zeek:see:: dns_AAAA_reply dns_A_reply dns_CNAME_reply 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_SPF_reply dns_WKS_reply dns_end 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
|
||||
## dns_max_queries dns_session_timeout dns_skip_addl
|
||||
## dns_skip_all_addl dns_skip_all_auth dns_skip_auth
|
||||
event dns_EDNS_ecs%(c: connection, msg: dns_msg, opt: dns_edns_ecs%);
|
||||
|
||||
## Generated for DNS replies of type *TSIG*. For replies with multiple answers,
|
||||
## an individual event of the corresponding type is raised for each.
|
||||
##
|
||||
|
|
|
@ -42,6 +42,7 @@ void zeek_legacy_netvar_init()
|
|||
::dns_answer = zeek::id::find_type("dns_answer")->AsRecordType();
|
||||
::dns_soa = zeek::id::find_type("dns_soa")->AsRecordType();
|
||||
::dns_edns_additional = zeek::id::find_type("dns_edns_additional")->AsRecordType();
|
||||
::dns_edns_ecs = zeek::id::find_type("dns_edns_ecs")->AsRecordType();
|
||||
::dns_tsig_additional = zeek::id::find_type("dns_tsig_additional")->AsRecordType();
|
||||
::dns_rrsig_rr = zeek::id::find_type("dns_rrsig_rr")->AsRecordType();
|
||||
::dns_dnskey_rr = zeek::id::find_type("dns_dnskey_rr")->AsRecordType();
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
[family=v4, source_prefix_len=24, scope_prefix_len=0, address=213.61.29.0]
|
||||
[family=v4, source_prefix_len=24, scope_prefix_len=0, address=213.61.29.0]
|
||||
[family=v6, source_prefix_len=56, scope_prefix_len=0, address=2001:470:1f0b:1600::]
|
||||
[family=v6, source_prefix_len=56, scope_prefix_len=0, address=2001:470:1f0b:1600::]
|
||||
[family=v6, source_prefix_len=56, scope_prefix_len=0, address=2001:470:1f0b:1600::]
|
||||
[family=v6, source_prefix_len=56, scope_prefix_len=0, address=2001:470:1f0b:1600::]
|
||||
[family=v6, source_prefix_len=56, scope_prefix_len=0, address=2001:470:1f0b:1600::]
|
||||
[family=v6, source_prefix_len=56, scope_prefix_len=0, address=2001:470:1f0b:1600::]
|
||||
[family=v6, source_prefix_len=56, scope_prefix_len=0, address=2001:470:1f0b:1600::]
|
||||
[family=v6, source_prefix_len=56, scope_prefix_len=0, address=2001:470:1f0b:1600::]
|
BIN
testing/btest/Traces/dns-edns-ecs.pcap
Normal file
BIN
testing/btest/Traces/dns-edns-ecs.pcap
Normal file
Binary file not shown.
|
@ -0,0 +1,8 @@
|
|||
# @TEST-EXEC: zeek -C -r $TRACES/dns-edns-ecs.pcap %INPUT > output
|
||||
# @TEST-EXEC: btest-diff output
|
||||
|
||||
@load policy/protocols/dns/auth-addl
|
||||
|
||||
event dns_EDNS_ecs(c: connection, msg: dns_msg, opt: dns_edns_ecs) {
|
||||
print opt;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue