Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 51 additions & 35 deletions tools/tcpaccept.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,42 +117,8 @@

// check this is TCP
u16 protocol = 0;
// workaround for reading the sk_protocol bitfield:

// Following comments add by Joe Yin:
// Unfortunately,it can not work since Linux 4.10,
// because the sk_wmem_queued is not following the bitfield of sk_protocol.
// And the following member is sk_gso_max_segs.
// So, we can use this:
// bpf_probe_read_kernel(&protocol, 1, (void *)((u64)&newsk->sk_gso_max_segs) - 3);
// In order to diff the pre-4.10 and 4.10+ ,introduce the variables gso_max_segs_offset,sk_lingertime,
// sk_lingertime is closed to the gso_max_segs_offset,and
// the offset between the two members is 4

int gso_max_segs_offset = offsetof(struct sock, sk_gso_max_segs);
int sk_lingertime_offset = offsetof(struct sock, sk_lingertime);


// Since kernel v5.6 sk_protocol is its own u16 field and gso_max_segs
// precedes sk_lingertime.
if (sk_lingertime_offset - gso_max_segs_offset == 2)
protocol = newsk->sk_protocol;
else if (sk_lingertime_offset - gso_max_segs_offset == 4)
// 4.10+ with little endian
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
protocol = *(u8 *)((u64)&newsk->sk_gso_max_segs - 3);
else
// pre-4.10 with little endian
protocol = *(u8 *)((u64)&newsk->sk_wmem_queued - 3);
#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
// 4.10+ with big endian
protocol = *(u8 *)((u64)&newsk->sk_gso_max_segs - 1);
else
// pre-4.10 with big endian
protocol = *(u8 *)((u64)&newsk->sk_wmem_queued - 1);
#else
# error "Fix your compiler's __BYTE_ORDER__?!"
#endif
##GET_SK_PROTOCOL##

if (protocol != IPPROTO_TCP)
return 0;
Expand Down Expand Up @@ -196,6 +162,51 @@
}
"""

get_sk_protocol_field = """
protocol = newsk->sk_protocol;
"""

get_sk_protocol_bitfield = """
// workaround for reading the sk_protocol bitfield:

// Following comments add by Joe Yin:
// Unfortunately,it can not work since Linux 4.10,
// because the sk_wmem_queued is not following the bitfield of sk_protocol.
// And the following member is sk_gso_max_segs.
// So, we can use this:
// bpf_probe_read_kernel(&protocol, 1, (void *)((u64)&newsk->sk_gso_max_segs) - 3);
// In order to diff the pre-4.10 and 4.10+ ,introduce the variables gso_max_segs_offset,sk_lingertime,
// sk_lingertime is closed to the gso_max_segs_offset,and
// the offset between the two members is 4

int gso_max_segs_offset = offsetof(struct sock, sk_gso_max_segs);
int sk_lingertime_offset = offsetof(struct sock, sk_lingertime);


// Since kernel v5.6 sk_protocol is its own u16 field and gso_max_segs
// precedes sk_lingertime.
// We keep this workaround in case BTF is unavailable
if (sk_lingertime_offset - gso_max_segs_offset == 2)
protocol = newsk->sk_protocol;
else if (sk_lingertime_offset - gso_max_segs_offset == 4)
// 4.10+ with little endian
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
protocol = *(u8 *)((u64)&newsk->sk_gso_max_segs - 3);
else
// pre-4.10 with little endian
protocol = *(u8 *)((u64)&newsk->sk_wmem_queued - 3);
#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
// 4.10+ with big endian
protocol = *(u8 *)((u64)&newsk->sk_gso_max_segs - 1);
else
// pre-4.10 with big endian
protocol = *(u8 *)((u64)&newsk->sk_wmem_queued - 1);
#else
# error "Fix your compiler's __BYTE_ORDER__?!"
#endif
"""


bpf_text += bpf_text_kprobe

# code substitutions
Expand All @@ -216,6 +227,11 @@
bpf_text = bpf_text.replace('##FILTER_FAMILY##',
'if (family != AF_INET6) { return 0; }')

if BPF.kernel_struct_has_field("sock", "sk_protocol") == 1:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the existing offset == 2 fallback still necessary for cases where the sk_protocol field exists, but the absence of BTF information causes the kernel_struct_has_field() check to fail?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. I didn't think of that.

bpf_text = bpf_text.replace('##GET_SK_PROTOCOL##', get_sk_protocol_field)
else:
bpf_text = bpf_text.replace('##GET_SK_PROTOCOL##', get_sk_protocol_bitfield)

bpf_text = filter_by_containers(args) + bpf_text
if debug or args.ebpf:
print(bpf_text)
Expand Down
Loading