Skip to content

Commit 96d85f8

Browse files
authored
fix: convert kernel monotonic times to wall time (#268)
# Description Adjusts timestamps from eBPF that use the kernel monotonic timer to UTC during Flow ingestion. This is done on a best-effort basis, because it is impossible to sample from the monotonic timer and the wall-clock at the same instant. The difference in the time it takes to execute these instructions should be small enough for our purposes in practice. ## Related Issue Fixes #204 ## Checklist - [x] I have read the [contributing documentation](https://retina.sh/docs/contributing). - [x] I signed and signed-off the commits (`git commit -S -s ...`). See [this documentation](https://docs.github.com/en/authentication/managing-commit-signature-verification/about-commit-signature-verification) on signing commits. - [x] I have correctly attributed the author(s) of the code. - [ ] I have tested the changes locally. - [x] I have followed the project's style guidelines. - [x] I have updated the documentation, if necessary. - [x] I have added tests, if applicable. ## Screenshots (if applicable) or Testing Completed Please add any relevant screenshots or GIFs to showcase the changes made. ## Additional Notes This is effectively only implemented for Unix builds as I don't know how Windows ktime behaves (and we're only compiling these plugins for Linux at the moment). It can be implemented on !unix in the future as necessary. --- Please refer to the [CONTRIBUTING.md](../CONTRIBUTING.md) file for more information on how to contribute to this project. Signed-off-by: Evan Baker <[email protected]>
1 parent 5dcaaad commit 96d85f8

File tree

7 files changed

+57
-15
lines changed

7 files changed

+57
-15
lines changed

internal/ktime/ktime.go

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package ktime
2+
3+
// MonotonicOffset is the calculated skew between the kernel's
4+
// monotonic timer and UTC.
5+
//
6+
// If clock readings were instantaneous, this would mean that
7+
// MonotonicTimer - MonotonicOFfset = the UTC Boot Time, but
8+
// that is idealized and there will be some small errror.
9+
var MonotonicOffset = calculateMonotonicOffset()

internal/ktime/ktime_other.go

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
//go:build !unix
2+
3+
package ktime
4+
5+
import "time"
6+
7+
func calculateMonotonicOffset() time.Duration {
8+
return 0 * time.Nanosecond
9+
}

internal/ktime/ktime_unix.go

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
//go:build unix
2+
3+
package ktime
4+
5+
import (
6+
"time"
7+
8+
"golang.org/x/sys/unix"
9+
)
10+
11+
// calculateMonotonicOffset tries to determine the offset of the kernel's
12+
// monotonic clock from UTC so that measurements from eBPF using the
13+
// monotonic clock timestamp may be adjusted to wall-time.
14+
//
15+
// These instructions do not execute instantaneously so it will always be
16+
// impossible to sample both clocks at exactly the same time.
17+
// This means that for any single process there will be constant error in
18+
// the accuracy of this measurement despite the nanosecond-level precision
19+
// of the individual clocks.
20+
func calculateMonotonicOffset() time.Duration {
21+
mono := &unix.Timespec{}
22+
now := time.Now()
23+
_ = unix.ClockGettime(unix.CLOCK_BOOTTIME, mono)
24+
return time.Duration(now.UnixNano() - unix.TimespecToNsec(*mono))
25+
}

pkg/plugin/dropreason/_cprog/drop_reason.c

+2-2
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,7 @@ static void get_packet_from_skb(struct packet *p, struct sk_buff *skb)
184184
p->src_ip = iphdr.saddr;
185185
p->dst_ip = iphdr.daddr;
186186
// get current timestamp in ns
187-
p->ts = bpf_ktime_get_ns();
187+
p->ts = bpf_ktime_get_boot_ns();
188188

189189
if (iphdr.protocol == IPPROTO_TCP)
190190
{
@@ -229,7 +229,7 @@ static void get_packet_from_sock(struct packet *p, struct sock *sk)
229229
#endif
230230

231231
// get current timestamp in ns
232-
p->ts = bpf_ktime_get_ns();
232+
p->ts = bpf_ktime_get_boot_ns();
233233
p->in_filtermap = true;
234234
p->src_ip = saddr;
235235
p->dst_ip = daddr;

pkg/plugin/dropreason/dropreason_linux.go

+5-6
Original file line numberDiff line numberDiff line change
@@ -14,24 +14,23 @@ import (
1414
"time"
1515
"unsafe"
1616

17-
kcfg "github.com/microsoft/retina/pkg/config"
18-
"github.com/microsoft/retina/pkg/utils"
19-
2017
"github.com/cilium/cilium/api/v1/flow"
2118
hubblev1 "github.com/cilium/cilium/pkg/hubble/api/v1"
2219
"github.com/cilium/ebpf"
2320
"github.com/cilium/ebpf/link"
2421
"github.com/cilium/ebpf/perf"
2522
"github.com/cilium/ebpf/rlimit"
23+
"github.com/microsoft/retina/internal/ktime"
24+
kcfg "github.com/microsoft/retina/pkg/config"
2625
"github.com/microsoft/retina/pkg/enricher"
2726
"github.com/microsoft/retina/pkg/loader"
2827
"github.com/microsoft/retina/pkg/log"
2928
"github.com/microsoft/retina/pkg/metrics"
3029
"github.com/microsoft/retina/pkg/plugin/api"
3130
plugincommon "github.com/microsoft/retina/pkg/plugin/common"
32-
"go.uber.org/zap"
33-
3431
_ "github.com/microsoft/retina/pkg/plugin/dropreason/_cprog" // nolint
32+
"github.com/microsoft/retina/pkg/utils"
33+
"go.uber.org/zap"
3534
)
3635

3736
//go:generate go run github.com/cilium/ebpf/cmd/bpf2go@master -cc clang-14 -cflags "-g -O2 -Wall -D__TARGET_ARCH_${GOARCH} -Wall" -target ${GOARCH} -type metrics_map_value -type drop_reason_t -type packet kprobe ./_cprog/drop_reason.c -- -I../lib/_${GOARCH} -I../lib/common/libbpf/_src -I../filter/_cprog/
@@ -356,7 +355,7 @@ func (dr *dropReason) processRecord(ctx context.Context, id int) {
356355
dropKey := (dropMetricKey)(bpfEvent.Key)
357356

358357
fl := utils.ToFlow(
359-
int64(bpfEvent.Ts),
358+
ktime.MonotonicOffset.Nanoseconds()+int64(bpfEvent.Ts),
360359
utils.Int2ip(bpfEvent.SrcIp).To4(), // Precautionary To4() call.
361360
utils.Int2ip(bpfEvent.DstIp).To4(), // Precautionary To4() call.
362361
sourcePortShort,

pkg/plugin/packetparser/_cprog/packetparser.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ static void parse(struct __sk_buff *skb, direction d)
147147
__builtin_memset(&p, 0, sizeof(p));
148148

149149
// Get current time in nanoseconds.
150-
p.ts = bpf_ktime_get_ns();
150+
p.ts = bpf_ktime_get_boot_ns();
151151

152152
p.dir = d;
153153
p.bytes = skb->len;

pkg/plugin/packetparser/packetparser_linux.go

+6-6
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
"github.com/cilium/ebpf/rlimit"
2222
tc "github.com/florianl/go-tc"
2323
helper "github.com/florianl/go-tc/core"
24+
"github.com/microsoft/retina/internal/ktime"
2425
"github.com/microsoft/retina/pkg/common"
2526
kcfg "github.com/microsoft/retina/pkg/config"
2627
"github.com/microsoft/retina/pkg/enricher"
@@ -29,17 +30,16 @@ import (
2930
"github.com/microsoft/retina/pkg/metrics"
3031
"github.com/microsoft/retina/pkg/plugin/api"
3132
plugincommon "github.com/microsoft/retina/pkg/plugin/common"
33+
_ "github.com/microsoft/retina/pkg/plugin/lib/_amd64" // nolint
34+
_ "github.com/microsoft/retina/pkg/plugin/lib/_arm64" // nolint
35+
_ "github.com/microsoft/retina/pkg/plugin/lib/common/libbpf/_src" // nolint
36+
_ "github.com/microsoft/retina/pkg/plugin/packetparser/_cprog" // nolint
3237
"github.com/microsoft/retina/pkg/pubsub"
3338
"github.com/microsoft/retina/pkg/utils"
3439
"github.com/microsoft/retina/pkg/watchers/endpoint"
3540
"github.com/vishvananda/netlink"
3641
"go.uber.org/zap"
3742
"golang.org/x/sys/unix"
38-
39-
_ "github.com/microsoft/retina/pkg/plugin/lib/_amd64" // nolint
40-
_ "github.com/microsoft/retina/pkg/plugin/lib/_arm64" // nolint
41-
_ "github.com/microsoft/retina/pkg/plugin/lib/common/libbpf/_src" // nolint
42-
_ "github.com/microsoft/retina/pkg/plugin/packetparser/_cprog" // nolint
4343
)
4444

4545
//go:generate go run github.com/cilium/ebpf/cmd/bpf2go@master -cc clang-14 -cflags "-g -O2 -Wall -D__TARGET_ARCH_${GOARCH} -Wall" -target ${GOARCH} -type packet packetparser ./_cprog/packetparser.c -- -I../lib/_${GOARCH} -I../lib/common/libbpf/_src -I../filter/_cprog/
@@ -534,7 +534,7 @@ func (p *packetParser) processRecord(ctx context.Context, id int) {
534534
destinationPortShort := uint32(utils.HostToNetShort(bpfEvent.DstPort))
535535

536536
fl := utils.ToFlow(
537-
int64(bpfEvent.Ts),
537+
ktime.MonotonicOffset.Nanoseconds()+int64(bpfEvent.Ts),
538538
utils.Int2ip(bpfEvent.SrcIp).To4(), // Precautionary To4() call.
539539
utils.Int2ip(bpfEvent.DstIp).To4(), // Precautionary To4() call.
540540
sourcePortShort,

0 commit comments

Comments
 (0)