Skip to content

Commit c208d0e

Browse files
jeromemarchandekyooo
authored andcommitted
tools/tcpaccept: Fix tcpaccept on recent kernel
The tcpaccept use the relative offset of gso_max_segs and sk_lingertime_offset to check whether sk_protocol is its own field of part of a bitfield and find it's location. This is not very robust. Use BPF.kernel_struct_has_field() to find out whether it's its own field and revert to the old workaround if it's part of a bitfield or BTF is unavailable. Closes: #5316 Signed-off-by: Jerome Marchand <[email protected]>
1 parent 1cc15c3 commit c208d0e

File tree

1 file changed

+51
-35
lines changed

1 file changed

+51
-35
lines changed

tools/tcpaccept.py

Lines changed: 51 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -117,42 +117,8 @@
117117
118118
// check this is TCP
119119
u16 protocol = 0;
120-
// workaround for reading the sk_protocol bitfield:
121-
122-
// Following comments add by Joe Yin:
123-
// Unfortunately,it can not work since Linux 4.10,
124-
// because the sk_wmem_queued is not following the bitfield of sk_protocol.
125-
// And the following member is sk_gso_max_segs.
126-
// So, we can use this:
127-
// bpf_probe_read_kernel(&protocol, 1, (void *)((u64)&newsk->sk_gso_max_segs) - 3);
128-
// In order to diff the pre-4.10 and 4.10+ ,introduce the variables gso_max_segs_offset,sk_lingertime,
129-
// sk_lingertime is closed to the gso_max_segs_offset,and
130-
// the offset between the two members is 4
131120
132-
int gso_max_segs_offset = offsetof(struct sock, sk_gso_max_segs);
133-
int sk_lingertime_offset = offsetof(struct sock, sk_lingertime);
134-
135-
136-
// Since kernel v5.6 sk_protocol is its own u16 field and gso_max_segs
137-
// precedes sk_lingertime.
138-
if (sk_lingertime_offset - gso_max_segs_offset == 2)
139-
protocol = newsk->sk_protocol;
140-
else if (sk_lingertime_offset - gso_max_segs_offset == 4)
141-
// 4.10+ with little endian
142-
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
143-
protocol = *(u8 *)((u64)&newsk->sk_gso_max_segs - 3);
144-
else
145-
// pre-4.10 with little endian
146-
protocol = *(u8 *)((u64)&newsk->sk_wmem_queued - 3);
147-
#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
148-
// 4.10+ with big endian
149-
protocol = *(u8 *)((u64)&newsk->sk_gso_max_segs - 1);
150-
else
151-
// pre-4.10 with big endian
152-
protocol = *(u8 *)((u64)&newsk->sk_wmem_queued - 1);
153-
#else
154-
# error "Fix your compiler's __BYTE_ORDER__?!"
155-
#endif
121+
##GET_SK_PROTOCOL##
156122
157123
if (protocol != IPPROTO_TCP)
158124
return 0;
@@ -196,6 +162,51 @@
196162
}
197163
"""
198164

165+
get_sk_protocol_field = """
166+
protocol = newsk->sk_protocol;
167+
"""
168+
169+
get_sk_protocol_bitfield = """
170+
// workaround for reading the sk_protocol bitfield:
171+
172+
// Following comments add by Joe Yin:
173+
// Unfortunately,it can not work since Linux 4.10,
174+
// because the sk_wmem_queued is not following the bitfield of sk_protocol.
175+
// And the following member is sk_gso_max_segs.
176+
// So, we can use this:
177+
// bpf_probe_read_kernel(&protocol, 1, (void *)((u64)&newsk->sk_gso_max_segs) - 3);
178+
// In order to diff the pre-4.10 and 4.10+ ,introduce the variables gso_max_segs_offset,sk_lingertime,
179+
// sk_lingertime is closed to the gso_max_segs_offset,and
180+
// the offset between the two members is 4
181+
182+
int gso_max_segs_offset = offsetof(struct sock, sk_gso_max_segs);
183+
int sk_lingertime_offset = offsetof(struct sock, sk_lingertime);
184+
185+
186+
// Since kernel v5.6 sk_protocol is its own u16 field and gso_max_segs
187+
// precedes sk_lingertime.
188+
// We keep this workaround in case BTF is unavailable
189+
if (sk_lingertime_offset - gso_max_segs_offset == 2)
190+
protocol = newsk->sk_protocol;
191+
else if (sk_lingertime_offset - gso_max_segs_offset == 4)
192+
// 4.10+ with little endian
193+
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
194+
protocol = *(u8 *)((u64)&newsk->sk_gso_max_segs - 3);
195+
else
196+
// pre-4.10 with little endian
197+
protocol = *(u8 *)((u64)&newsk->sk_wmem_queued - 3);
198+
#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
199+
// 4.10+ with big endian
200+
protocol = *(u8 *)((u64)&newsk->sk_gso_max_segs - 1);
201+
else
202+
// pre-4.10 with big endian
203+
protocol = *(u8 *)((u64)&newsk->sk_wmem_queued - 1);
204+
#else
205+
# error "Fix your compiler's __BYTE_ORDER__?!"
206+
#endif
207+
"""
208+
209+
199210
bpf_text += bpf_text_kprobe
200211

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

230+
if BPF.kernel_struct_has_field("sock", "sk_protocol") == 1:
231+
bpf_text = bpf_text.replace('##GET_SK_PROTOCOL##', get_sk_protocol_field)
232+
else:
233+
bpf_text = bpf_text.replace('##GET_SK_PROTOCOL##', get_sk_protocol_bitfield)
234+
219235
bpf_text = filter_by_containers(args) + bpf_text
220236
if debug or args.ebpf:
221237
print(bpf_text)

0 commit comments

Comments
 (0)