Skip to content

Commit 4ae0d97

Browse files
committed
dns name
1 parent e9ebab7 commit 4ae0d97

File tree

15 files changed

+216
-42
lines changed

15 files changed

+216
-42
lines changed

bpf/dns_tracker.h

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,23 @@
88

99
#define DNS_DEFAULT_PORT 53
1010
#define DNS_QR_FLAG 0x8000
11+
#define DNS_OPCODE_MASK 0x7800
12+
#define DNS_RCODE_MASK 0x0F
1113
#define UDP_MAXMSG 512
14+
// Reduced to keep BPF program size under limits
15+
#define MAX_DNS_LABELS 8
16+
17+
// Per-CPU buffer for DNS name parsing to avoid stack overflow
18+
struct dns_parse_buffer {
19+
char name[DNS_NAME_MAX];
20+
};
21+
22+
struct {
23+
__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
24+
__uint(max_entries, 1);
25+
__type(key, u32);
26+
__type(value, struct dns_parse_buffer);
27+
} dns_parse_buffer_map SEC(".maps");
1228

1329
// See https://www.rfc-editor.org/rfc/rfc1035 4.1.1. Header section format
1430
struct dns_header {
@@ -65,7 +81,7 @@ static __always_inline u8 calc_dns_header_offset(pkt_info *pkt, void *data_end)
6581
return len;
6682
}
6783

68-
static __always_inline int track_dns_packet(struct __sk_buff *skb, pkt_info *pkt) {
84+
static __noinline int track_dns_packet(struct __sk_buff *skb, pkt_info *pkt) {
6985
void *data_end = (void *)(long)skb->data_end;
7086
int ret = 0;
7187
if (pkt->id->dst_port == dns_port || pkt->id->src_port == dns_port ||
@@ -108,6 +124,19 @@ static __always_inline int track_dns_packet(struct __sk_buff *skb, pkt_info *pkt
108124
}
109125
pkt->dns_id = dns_id;
110126
pkt->dns_flags = flags;
127+
128+
// Parse DNS QNAME using per-CPU buffer
129+
u32 key = 0;
130+
struct dns_parse_buffer *buf = bpf_map_lookup_elem(&dns_parse_buffer_map, &key);
131+
if (buf) {
132+
// Copy raw QNAME bytes (label-encoded) and let userspace decode to dotted form
133+
__builtin_memset(buf->name, 0, DNS_NAME_MAX);
134+
u32 qname_off = dns_offset + sizeof(struct dns_header);
135+
// Best-effort fixed-size copy; safe for verifier (constant size)
136+
(void)bpf_skb_load_bytes(skb, qname_off, buf->name, DNS_NAME_MAX - 1);
137+
// Ensure null-termination
138+
buf->name[DNS_NAME_MAX - 1] = '\0';
139+
}
111140
} // end of dns response
112141
}
113142
return ret;

bpf/flows.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,13 @@ static inline void update_dns(additional_metrics *extra_metrics, pkt_info *pkt,
118118
extra_metrics->dns_record.id = pkt->dns_id;
119119
extra_metrics->dns_record.flags = pkt->dns_flags;
120120
extra_metrics->dns_record.latency = pkt->dns_latency;
121+
122+
// Copy DNS name from per-CPU buffer if available
123+
u32 key = 0;
124+
struct dns_parse_buffer *buf = bpf_map_lookup_elem(&dns_parse_buffer_map, &key);
125+
if (buf) {
126+
__builtin_memcpy(extra_metrics->dns_record.name, buf->name, DNS_NAME_MAX);
127+
}
121128
}
122129
if (dns_errno != 0) {
123130
extra_metrics->dns_record.errno = dns_errno;
@@ -254,6 +261,13 @@ static inline int flow_monitor(struct __sk_buff *skb, u8 direction) {
254261
new_metrics.dns_record.flags = pkt.dns_flags;
255262
new_metrics.dns_record.latency = pkt.dns_latency;
256263
new_metrics.dns_record.errno = dns_errno;
264+
265+
// Copy DNS name from per-CPU buffer if available
266+
u32 key = 0;
267+
struct dns_parse_buffer *buf = bpf_map_lookup_elem(&dns_parse_buffer_map, &key);
268+
if (buf) {
269+
__builtin_memcpy(new_metrics.dns_record.name, buf->name, DNS_NAME_MAX);
270+
}
257271
long ret =
258272
bpf_map_update_elem(&additional_flow_metrics, &id, &new_metrics, BPF_NOEXIST);
259273
if (ret != 0) {

bpf/types.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@ typedef __u64 u64;
7070
#define OBSERVED_DIRECTION_BOTH 3
7171

7272
#define MAX_PAYLOAD_SIZE 256
73+
// Max DNS name length (truncated if longer) - kept small for BPF limits
74+
#define DNS_NAME_MAX 32
7375

7476
// according to field 61 in https://www.iana.org/assignments/ipfix/ipfix.xhtml
7577
typedef enum direction_t {
@@ -123,6 +125,7 @@ typedef struct additional_metrics_t {
123125
u16 id;
124126
u16 flags;
125127
u8 errno;
128+
char name[DNS_NAME_MAX];
126129
} dns_record;
127130
struct pkt_drops_t {
128131
u64 bytes;

pkg/decode/decode_protobuf.go

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,62 @@ const (
2626
type Protobuf struct {
2727
}
2828

29+
// dnsNameToString converts a null-terminated C string (char array) to Go string
30+
func dnsNameToString[T ~byte | ~int8](arr []T) string {
31+
n := 0
32+
for ; n < len(arr); n++ {
33+
if arr[n] == 0 {
34+
break
35+
}
36+
}
37+
// Convert to byte slice
38+
b := make([]byte, n)
39+
for i := 0; i < n; i++ {
40+
b[i] = byte(arr[i])
41+
}
42+
return string(b)
43+
}
44+
45+
// dnsRawNameToDotted parses a label-encoded DNS QNAME (raw bytes copied from kernel)
46+
// into a dotted string. Stops on NUL, compression pointer, or bounds.
47+
func dnsRawNameToDotted(rawI8 []int8) string {
48+
// Convert to byte slice up to first NUL
49+
b := make([]byte, 0, len(rawI8))
50+
for i := 0; i < len(rawI8); i++ {
51+
if rawI8[i] == 0 { // NUL terminator placed in kernel copy
52+
break
53+
}
54+
b = append(b, byte(rawI8[i]))
55+
}
56+
if len(b) == 0 {
57+
return ""
58+
}
59+
out := make([]byte, 0, len(b))
60+
i := 0
61+
first := true
62+
for i < len(b) {
63+
l := int(b[i])
64+
if l == 0 {
65+
break
66+
}
67+
// Stop on compression pointer (0xC0xx) since we didn't follow it in kernel
68+
if (l & 0xC0) == 0xC0 {
69+
break
70+
}
71+
i++
72+
if i+l > len(b) {
73+
break
74+
}
75+
if !first {
76+
out = append(out, '.')
77+
}
78+
first = false
79+
out = append(out, b[i:i+l]...)
80+
i += l
81+
}
82+
return string(out)
83+
}
84+
2985
func NewProtobuf() (*Protobuf, error) {
3086
log.Debugf("entering NewProtobuf")
3187
return &Protobuf{}, nil
@@ -120,6 +176,10 @@ func RecordToMap(fr *model.Record) config.GenericMap {
120176
out["DnsFlags"] = fr.Metrics.AdditionalMetrics.DnsRecord.Flags
121177
out["DnsFlagsResponseCode"] = DNSRcodeToStr(uint32(fr.Metrics.AdditionalMetrics.DnsRecord.Flags) & 0xF)
122178
out["DnsLatencyMs"] = fr.DNSLatency.Milliseconds()
179+
// Export DNS name if present (label-encoded copied from kernel)
180+
if name := dnsRawNameToDotted(fr.Metrics.AdditionalMetrics.DnsRecord.Name[:]); name != "" {
181+
out["DnsName"] = name
182+
}
123183
}
124184

125185
if fr.Metrics.AdditionalMetrics.PktDrops.LatestDropCause != 0 {

pkg/ebpf/bpf_arm64_bpfel.go

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/ebpf/bpf_arm64_bpfel.o

4.14 KB
Binary file not shown.

pkg/ebpf/bpf_powerpc_bpfel.go

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/ebpf/bpf_powerpc_bpfel.o

4.14 KB
Binary file not shown.

pkg/ebpf/bpf_s390_bpfeb.go

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/ebpf/bpf_s390_bpfeb.o

4.14 KB
Binary file not shown.

0 commit comments

Comments
 (0)