Skip to content

Commit d1cc8a2

Browse files
vincentmlitohojo
authored andcommitted
port track tcp payload offset as scalar in xdp_synproxy
commit 977bc146d4eb7070118d8a974919b33bb52732b4 Author: Eduard Zingerman <[email protected]> Date: Tue Nov 21 04:06:51 2023 +0200 selftests/bpf: track tcp payload offset as scalar in xdp_synproxy This change prepares syncookie_{tc,xdp} for update in callbakcs verification logic. To allow bpf_loop() verification converge when multiple callback itreations are considered: - track offset inside TCP payload explicitly, not as a part of the pointer; - make sure that offset does not exceed MAX_PACKET_OFF enforced by verifier; - make sure that offset is tracked as unbound scalar between iterations, otherwise verifier won't be able infer that bpf_loop callback reaches identical states. Acked-by: Andrii Nakryiko <[email protected]> Signed-off-by: Eduard Zingerman <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Alexei Starovoitov <[email protected]> without above commit, syncookie_xdp program failed on kernel 6.7 with verifier error: "BPF program is too large. Processed 1000001 insn" Signed-off-by: Vincent Li <[email protected]>
1 parent 86ec1b7 commit d1cc8a2

File tree

1 file changed

+55
-35
lines changed

1 file changed

+55
-35
lines changed

xdp-synproxy/xdp_synproxy_kern.c

Lines changed: 55 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@
6161
#define DEFAULT_TTL 64
6262
#define MAX_ALLOWED_PORTS 8
6363

64+
#define MAX_PACKET_OFF 0xffff
65+
6466
#define swap(a, b) \
6567
do { typeof(a) __tmp = (a); (a) = (b); (b) = __tmp; } while (0)
6668

@@ -179,69 +181,82 @@ static __always_inline __u32 tcp_ns_to_ts(__u64 ns)
179181
return ns / (NSEC_PER_SEC / TCP_TS_HZ);
180182
}
181183

182-
static __always_inline __u32 tcp_time_stamp_raw(void)
184+
static __always_inline __u32 tcp_clock_ms(void)
183185
{
184186
return tcp_ns_to_ts(tcp_clock_ns());
185187
}
186188

187189
struct tcpopt_context {
188-
__u8 *ptr;
189-
__u8 *end;
190+
void *data;
190191
void *data_end;
191192
__be32 *tsecr;
192193
__u8 wscale;
193194
bool option_timestamp;
194195
bool option_sack;
196+
__u32 off;
195197
};
196198

197-
static int tscookie_tcpopt_parse(struct tcpopt_context *ctx)
199+
static __always_inline u8 *next(struct tcpopt_context *ctx, __u32 sz)
198200
{
199-
__u8 opcode, opsize;
201+
__u64 off = ctx->off;
202+
__u8 *data;
200203

201-
if (ctx->ptr >= ctx->end)
202-
return 1;
203-
if (ctx->ptr >= ctx->data_end)
204-
return 1;
204+
/* Verifier forbids access to packet when offset exceeds MAX_PACKET_OFF */
205+
if (off > MAX_PACKET_OFF - sz)
206+
return NULL;
205207

206-
opcode = ctx->ptr[0];
208+
data = ctx->data + off;
209+
barrier_var(data);
210+
if (data + sz >= ctx->data_end)
211+
return NULL;
207212

208-
if (opcode == TCPOPT_EOL)
209-
return 1;
210-
if (opcode == TCPOPT_NOP) {
211-
++ctx->ptr;
212-
return 0;
213-
}
213+
ctx->off += sz;
214+
return data;
215+
}
214216

215-
if (ctx->ptr + 1 >= ctx->end)
216-
return 1;
217-
if (ctx->ptr + 1 >= ctx->data_end)
217+
static int tscookie_tcpopt_parse(struct tcpopt_context *ctx)
218+
{
219+
__u8 *opcode, *opsize, *wscale, *tsecr;
220+
__u32 off = ctx->off;
221+
222+
opcode = next(ctx, 1);
223+
if (!opcode)
218224
return 1;
219-
opsize = ctx->ptr[1];
220-
if (opsize < 2)
225+
226+
if (*opcode == TCPOPT_EOL)
221227
return 1;
228+
if (*opcode == TCPOPT_NOP)
229+
return 0;
222230

223-
if (ctx->ptr + opsize > ctx->end)
231+
opsize = next(ctx, 1);
232+
if (!opsize || *opsize < 2)
224233
return 1;
225234

226-
switch (opcode) {
235+
switch (*opcode) {
227236
case TCPOPT_WINDOW:
228-
if (opsize == TCPOLEN_WINDOW && ctx->ptr + TCPOLEN_WINDOW <= ctx->data_end)
229-
ctx->wscale = ctx->ptr[2] < TCP_MAX_WSCALE ? ctx->ptr[2] : TCP_MAX_WSCALE;
237+
wscale = next(ctx, 1);
238+
if (!wscale)
239+
return 1;
240+
if (*opsize == TCPOLEN_WINDOW)
241+
ctx->wscale = *wscale < TCP_MAX_WSCALE ? *wscale : TCP_MAX_WSCALE;
230242
break;
231243
case TCPOPT_TIMESTAMP:
232-
if (opsize == TCPOLEN_TIMESTAMP && ctx->ptr + TCPOLEN_TIMESTAMP <= ctx->data_end) {
244+
tsecr = next(ctx, 4);
245+
if (!tsecr)
246+
return 1;
247+
if (*opsize == TCPOLEN_TIMESTAMP) {
233248
ctx->option_timestamp = true;
234249
/* Client's tsval becomes our tsecr. */
235-
*ctx->tsecr = get_unaligned((__be32 *)(ctx->ptr + 2));
250+
*ctx->tsecr = get_unaligned((__be32 *)tsecr);
236251
}
237252
break;
238253
case TCPOPT_SACK_PERM:
239-
if (opsize == TCPOLEN_SACK_PERM)
254+
if (*opsize == TCPOLEN_SACK_PERM)
240255
ctx->option_sack = true;
241256
break;
242257
}
243258

244-
ctx->ptr += opsize;
259+
ctx->off = off + *opsize;
245260

246261
return 0;
247262
}
@@ -258,25 +273,30 @@ static int tscookie_tcpopt_parse_batch(__u32 index, void *context)
258273

259274
static __always_inline bool tscookie_init(struct tcphdr *tcp_header,
260275
__u16 tcp_len, __be32 *tsval,
261-
__be32 *tsecr, void *data_end)
276+
__be32 *tsecr, void *data, void *data_end)
262277
{
263278
struct tcpopt_context loop_ctx = {
264-
.ptr = (__u8 *)(tcp_header + 1),
265-
.end = (__u8 *)tcp_header + tcp_len,
279+
.data = data,
266280
.data_end = data_end,
267281
.tsecr = tsecr,
268282
.wscale = TS_OPT_WSCALE_MASK,
269283
.option_timestamp = false,
270284
.option_sack = false,
285+
/* Note: currently verifier would track .off as unbound scalar.
286+
* In case if verifier would at some point get smarter and
287+
* compute bounded value for this var, beware that it might
288+
* hinder bpf_loop() convergence validation.
289+
*/
290+
.off = (__u8 *)(tcp_header + 1) - (__u8 *)data,
271291
};
272-
__u32 cookie;
292+
u32 cookie;
273293

274294
bpf_loop(6, tscookie_tcpopt_parse_batch, &loop_ctx, 0);
275295

276296
if (!loop_ctx.option_timestamp)
277297
return false;
278298

279-
cookie = tcp_time_stamp_raw() & ~TSMASK;
299+
cookie = tcp_clock_ms() & ~TSMASK;
280300
cookie |= loop_ctx.wscale & TS_OPT_WSCALE_MASK;
281301
if (loop_ctx.option_sack)
282302
cookie |= TS_OPT_SACK;
@@ -628,7 +648,7 @@ static __always_inline int syncookie_handle_syn(struct header_pointers *hdr,
628648
cookie = (__u32)value;
629649

630650
if (tscookie_init((void *)hdr->tcp, hdr->tcp_len,
631-
&tsopt_buf[0], &tsopt_buf[1], data_end))
651+
&tsopt_buf[0], &tsopt_buf[1], data, data_end))
632652
tsopt = tsopt_buf;
633653

634654
/* Check that there is enough space for a SYNACK. It also covers

0 commit comments

Comments
 (0)