Skip to content

Commit 9ccb6e5

Browse files
authored
chore: change parse ts func in packetparser (#140)
Build and deploy in a test cluster and verified that TSVal and TSErc are parsed correctly. --------- Signed-off-by: Quang Nguyen <[email protected]>
1 parent b7ac4fd commit 9ccb6e5

6 files changed

+81
-45
lines changed

pkg/plugin/linuxutil/linuxutil_mock_generated.go

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/plugin/packetparser/_cprog/packetparser.c

+78-43
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
char __license[] SEC("license") = "Dual MIT/GPL";
1212

1313

14+
1415
struct tcpmetadata {
1516
__u32 seq; // TCP sequence number
1617
__u32 ack_num; // TCP ack number
@@ -49,59 +50,93 @@ struct
4950
const struct packet *unused __attribute__((unused));
5051

5152
/*
52-
* Full credit to authors of pping_kern.c for the parse_tcp_ts function.
53-
* https://github.com/xdp-project/bpf-examples/blob/bc9df640cb9e5a541a7425ca2e66174ae22a18e3/pping/pping_kern.c#L316
54-
*
5553
* Parses the TSval and TSecr values from the TCP options field. If sucessful
5654
* the TSval and TSecr values will be stored at tsval and tsecr (in network
5755
* byte order).
5856
* Returns 0 if sucessful and -1 on failure
59-
*/
60-
static int parse_tcp_ts(struct tcphdr *tcph, void *data_end, __u32 *tsval,
61-
__u32 *tsecr)
62-
{
63-
int len = tcph->doff << 2;
64-
void *opt_end = (void *)tcph + len;
65-
__u8 *pos = (__u8 *)(tcph + 1); //Current pos in TCP options
66-
__u8 i, opt;
67-
volatile __u8
68-
opt_size; // Seems to ensure it's always read of from stack as u8
69-
70-
if (tcph + 1 > data_end || len <= sizeof(struct tcphdr))
57+
*
58+
+-------+-------+---------------------+---------------------+
59+
|Kind=8 | 10 | TS Value (TSval) |TS Echo Reply (TSecr)|
60+
+-------+-------+---------------------+---------------------+
61+
1 1 4 4
62+
* Reference:
63+
* - https://github.com/xdp-project/bpf-examples
64+
* - https://www.ietf.org/rfc/rfc9293.html
65+
* - https://www.rfc-editor.org/rfc/pdfrfc/rfc7323.txt.pdf
66+
* May explore using bpf_loop() in the future (kernel 5.17+)
67+
*/
68+
static int parse_tcp_ts(struct tcphdr *tcph, void *data_end, __u32 *tsval, __u32 *tsecr) {
69+
// Get the length of the TCP header.
70+
// The length is in 4-byte words, so we need to multiply it by 4 (bit shift left 2) to get the length in bytes.
71+
__u8 tcp_header_len = tcph->doff << 2;
72+
volatile __u8 opt_len;
73+
__u8 opt_kind, i;
74+
75+
// Verify that the options field is present.
76+
// If the header length is less than or equal to the default size of the TCP header, then there are no options.
77+
if (tcp_header_len <= sizeof(struct tcphdr)) {
7178
return -1;
72-
#pragma unroll //temporary solution until we can identify why the non-unrolled loop gets stuck in an infinite loop
73-
for (i = 0; i < MAX_TCP_OPTIONS; i++) {
74-
if (pos + 1 > opt_end || pos + 1 > data_end)
75-
return -1;
79+
}
7680

77-
opt = *pos;
78-
if (opt == 0) // Reached end of TCP options
79-
return -1;
81+
// Get the pointer to the end of the TCP header options field.
82+
__u8 *tcp_opt_end_ptr = (__u8 *)tcph + tcp_header_len;
8083

81-
if (opt == 1) { // TCP NOP option - advance one byte
82-
pos++;
83-
continue;
84-
}
84+
// Check that adding 1 to the start of the TCP header will not go past the end of the packet.
85+
// We need this to get to the start of the options field.
86+
if ((__u8 *)tcph + 1 > (__u8 *)data_end) {
87+
return -1;
88+
}
8589

86-
// Option > 1, should have option size
87-
if (pos + 2 > opt_end || pos + 2 > data_end)
88-
return -1;
89-
opt_size = *(pos + 1);
90-
if (opt_size < 2) // Stop parsing options if opt_size has an invalid value
91-
return -1;
90+
// Get the pointer to the start of the options field.
91+
__u8 *tcp_options_cur_ptr = (__u8 *)(tcph + 1);
9292

93-
// Option-kind is TCP timestap (yey!)
94-
if (opt == 8 && opt_size == 10) {
95-
if (pos + 10 > opt_end || pos + 10 > data_end)
96-
return -1;
97-
*tsval = bpf_ntohl(*(__u32 *)(pos + 2));
98-
*tsecr = bpf_ntohl(*(__u32 *)(pos + 6));
99-
return 0;
93+
// Loop through the options field to find the TSval and TSecr values.
94+
// MAX_TCP_OPTIONS_LEN is used to prevent infinite loops and the fact that the options field is at most 40 bytes long.
95+
#pragma unroll
96+
for (i = 0; i < MAX_TCP_OPTIONS_LEN; i++) {
97+
// Verify that adding 1 to the current pointer will not go past the end of the packet.
98+
if (tcp_options_cur_ptr + 1 > (__u8 *)tcp_opt_end_ptr || tcp_options_cur_ptr + 1 > (__u8 *)data_end) {
99+
return -1;
100100
}
101-
102-
// Some other TCP option - advance option-length bytes
103-
pos += opt_size;
104-
}
101+
// Dereference the pointer to get the option kind.
102+
opt_kind = *tcp_options_cur_ptr;
103+
// switch case to check the option kind.
104+
switch (opt_kind) {
105+
case 0:
106+
// End of options list.
107+
return -1;
108+
case 1:
109+
// No operation.
110+
tcp_options_cur_ptr++;
111+
continue;
112+
default:
113+
// Some kind of option.
114+
// Since each option is at least 2 bytes long, we need to check that adding 2 to the pointer will not go past the end of the packet.
115+
if (tcp_options_cur_ptr + 2 > tcp_opt_end_ptr || tcp_options_cur_ptr + 2 > (__u8 *)data_end) {
116+
return -1;
117+
}
118+
// Get the length of the option.
119+
opt_len = *(tcp_options_cur_ptr + 1);
120+
// Check that the option length is valid. It should be at least 2 bytes long.
121+
if (opt_len < 2) {
122+
return -1;
123+
}
124+
// Check if the option is the timestamp option. The timestamp option has a kind of 8 and a length of 10 bytes.
125+
if (opt_kind == 8 && opt_len == 10) {
126+
// Verify that adding the option's length to the pointer will not go past the end of the packet.
127+
if (tcp_options_cur_ptr + 10 > tcp_opt_end_ptr || tcp_options_cur_ptr + 10 > (__u8 *)data_end) {
128+
return -1;
129+
}
130+
// Found the TSval and TSecr values. Store them in the tsval and tsecr pointers.
131+
*tsval = bpf_ntohl(*(__u32 *)(tcp_options_cur_ptr + 2));
132+
*tsecr = bpf_ntohl(*(__u32 *)(tcp_options_cur_ptr + 6));
133+
134+
return 0;
135+
}
136+
// Move the pointer to the next option.
137+
tcp_options_cur_ptr += opt_len;
138+
}
139+
}
105140
return -1;
106141
}
107142

pkg/plugin/packetparser/_cprog/packetparser.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
// Licensed under the MIT license.
33

44
#define ETH_P_IP 0x0800
5-
#define MAX_TCP_OPTIONS 10
5+
// The maximum length of the TCP options field.
6+
#define MAX_TCP_OPTIONS_LEN 40
67

78
typedef enum
89
{
35.7 KB
Binary file not shown.
Binary file not shown.
1.04 KB
Binary file not shown.

0 commit comments

Comments
 (0)