Skip to content

Commit 76e65fb

Browse files
committed
[CWS] Register connected flows from __inet_hash_connect
security_sk_classify_flow runs in tcp_{v4,v6}_connect before the ephemeral source port and the source address have been picked, so the flow it reports for a connecting socket is incomplete. Until now the transmit path called the hook again once both were known; since Linux 7.0 inet6_csk_xmit only routes on a dst cache miss, so that second call never happens and IPv6 TCP client flows are either registered under a wildcard address or, when the socket was never bound, not registered at all. Register the flow on the return of __inet_hash_connect instead, reading the final port and source address from the socket, and drop the entry the earlier classification left behind. Both address families funnel into that function, so a single hook point covers them.
1 parent a0335db commit 76e65fb

6 files changed

Lines changed: 63 additions & 0 deletions

File tree

pkg/security/ebpf/c/include/hooks/network/flow.h

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -695,6 +695,41 @@ __attribute__((always_inline)) int register_connected_flow(struct sock *sk, u64
695695
return 0;
696696
}
697697

698+
// __inet_hash_connect is what assigns the source port for both IP v4 and v6, so its
699+
// return is the first point where the full flow is known. security_sk_classify_flow can't be relied
700+
// on for connected sockets because tcp_{v4,v6}_connect call it before the port and the source address are
701+
// picked, and since Linux 7.0 the transmit path no longer calls it.
702+
HOOK_ENTRY("__inet_hash_connect")
703+
int hook_inet_hash_connect(ctx_t *ctx) {
704+
struct inet_hash_connect_args_t args = {
705+
.sk = (struct sock *)CTX_PARM2(ctx),
706+
};
707+
u64 pid_tgid = bpf_get_current_pid_tgid();
708+
bpf_map_update_elem(&inet_hash_connect_args, &pid_tgid, &args, BPF_ANY);
709+
return 0;
710+
}
711+
712+
HOOK_EXIT("__inet_hash_connect")
713+
int rethook_inet_hash_connect(ctx_t *ctx) {
714+
u64 pid_tgid = bpf_get_current_pid_tgid();
715+
struct inet_hash_connect_args_t *args = bpf_map_lookup_elem(&inet_hash_connect_args, &pid_tgid);
716+
if (args == NULL) {
717+
// should never happen, ignore
718+
return 0;
719+
}
720+
struct sock *sk = args->sk;
721+
722+
// delete the entry in inet_hash_connect_args to make sure we don't leak entries
723+
bpf_map_delete_elem(&inet_hash_connect_args, &pid_tgid);
724+
725+
if ((int)CTX_PARMRET(ctx) < 0 || sk == NULL) {
726+
// we only care about successful connect operations
727+
return 0;
728+
}
729+
730+
return register_connected_flow(sk, pid_tgid);
731+
}
732+
698733
// The socket returned by accept() holds the concrete local address the connection landed on, which
699734
// the BIND_ENTRY of a wildcard listener doesn't cover. Before Linux 7.0 that IPv6 socket was classified
700735
// on its first transmit, causing security_sk_classify_flow to be called and classify the flow.

pkg/security/ebpf/c/include/maps.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ BPF_HASH_MAP(cgroup_mount_id, u32, u32, 1)
6262

6363
BPF_HASH_MAP_FLAGS(active_flows, u32, struct active_flows_t, 1, BPF_F_NO_PREALLOC) // max entry will be overridden at runtime
6464
BPF_HASH_MAP_FLAGS(inet_bind_args, u64, struct inet_bind_args_t, 1, BPF_F_NO_PREALLOC) // max entries will be overridden at runtime
65+
BPF_HASH_MAP_FLAGS(inet_hash_connect_args, u64, struct inet_hash_connect_args_t, 1, BPF_F_NO_PREALLOC) // max entries will be overridden at runtime
6566

6667
BPF_LRU_MAP(activity_dumps_config, u64, struct activity_dump_config, 1) // max entries will be overridden at runtime
6768
BPF_LRU_MAP(cgroup_wait_list, u64, u64, 1) // max entries will be overridden at runtime

pkg/security/ebpf/c/include/structs/network.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,10 @@ struct inet_bind_args_t {
7575
struct socket *sock;
7676
};
7777

78+
struct inet_hash_connect_args_t {
79+
struct sock *sk;
80+
};
81+
7882
struct device_t {
7983
char name[16];
8084
u32 netns;

pkg/security/ebpf/probes/all.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -407,6 +407,11 @@ func AllMapSpecEditors(numCPU int, opts MapSpecEditorOpts, kv *kernel.Version) m
407407
Flags: unix.BPF_ANY,
408408
EditorFlag: manager.EditMaxEntries | manager.EditFlags,
409409
}
410+
editors["inet_hash_connect_args"] = manager.MapSpecEditor{
411+
MaxEntries: superReducedProcPidCacheSize,
412+
Flags: unix.BPF_ANY,
413+
EditorFlag: manager.EditMaxEntries | manager.EditFlags,
414+
}
410415
} else {
411416
editors["active_flows"] = manager.MapSpecEditor{
412417
MaxEntries: activeFlowsMaxEntries,
@@ -416,6 +421,10 @@ func AllMapSpecEditors(numCPU int, opts MapSpecEditorOpts, kv *kernel.Version) m
416421
MaxEntries: superReducedProcPidCacheSize,
417422
EditorFlag: manager.EditMaxEntries,
418423
}
424+
editors["inet_hash_connect_args"] = manager.MapSpecEditor{
425+
MaxEntries: superReducedProcPidCacheSize,
426+
EditorFlag: manager.EditMaxEntries,
427+
}
419428
}
420429

421430
return editors

pkg/security/ebpf/probes/event_types.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ func NetworkSelectors(hasCgroupSocket bool) []manager.ProbesSelector {
6060
&manager.BestEffort{Selectors: []manager.ProbesSelector{
6161
hookFunc("hook_inet6_bind"),
6262
hookFunc("rethook_inet6_bind"),
63+
hookFunc("hook_inet_hash_connect"),
64+
hookFunc("rethook_inet_hash_connect"),
6365
}},
6466

6567
// network device probes

pkg/security/ebpf/probes/flow.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,18 @@ func getFlowProbes() []*manager.Probe {
7878
EBPFFuncName: "rethook_inet6_bind",
7979
},
8080
},
81+
{
82+
ProbeIdentificationPair: manager.ProbeIdentificationPair{
83+
UID: SecurityAgentUID,
84+
EBPFFuncName: "hook_inet_hash_connect",
85+
},
86+
},
87+
{
88+
ProbeIdentificationPair: manager.ProbeIdentificationPair{
89+
UID: SecurityAgentUID,
90+
EBPFFuncName: "rethook_inet_hash_connect",
91+
},
92+
},
8193
{
8294
ProbeIdentificationPair: manager.ProbeIdentificationPair{
8395
UID: SecurityAgentUID,

0 commit comments

Comments
 (0)