Commit 977bc146 authored by Eduard Zingerman's avatar Eduard Zingerman Committed by Alexei Starovoitov

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: default avatarAndrii Nakryiko <andrii@kernel.org>
Signed-off-by: default avatarEduard Zingerman <eddyz87@gmail.com>
Link: https://lore.kernel.org/r/20231121020701.26440-2-eddyz87@gmail.comSigned-off-by: default avatarAlexei Starovoitov <ast@kernel.org>
parent fcb905d8
...@@ -53,6 +53,8 @@ ...@@ -53,6 +53,8 @@
#define DEFAULT_TTL 64 #define DEFAULT_TTL 64
#define MAX_ALLOWED_PORTS 8 #define MAX_ALLOWED_PORTS 8
#define MAX_PACKET_OFF 0xffff
#define swap(a, b) \ #define swap(a, b) \
do { typeof(a) __tmp = (a); (a) = (b); (b) = __tmp; } while (0) do { typeof(a) __tmp = (a); (a) = (b); (b) = __tmp; } while (0)
...@@ -183,63 +185,76 @@ static __always_inline __u32 tcp_clock_ms(void) ...@@ -183,63 +185,76 @@ static __always_inline __u32 tcp_clock_ms(void)
} }
struct tcpopt_context { struct tcpopt_context {
__u8 *ptr; void *data;
__u8 *end;
void *data_end; void *data_end;
__be32 *tsecr; __be32 *tsecr;
__u8 wscale; __u8 wscale;
bool option_timestamp; bool option_timestamp;
bool option_sack; bool option_sack;
__u32 off;
}; };
static int tscookie_tcpopt_parse(struct tcpopt_context *ctx) static __always_inline u8 *next(struct tcpopt_context *ctx, __u32 sz)
{ {
__u8 opcode, opsize; __u64 off = ctx->off;
__u8 *data;
if (ctx->ptr >= ctx->end) /* Verifier forbids access to packet when offset exceeds MAX_PACKET_OFF */
return 1; if (off > MAX_PACKET_OFF - sz)
if (ctx->ptr >= ctx->data_end) return NULL;
return 1;
opcode = ctx->ptr[0]; data = ctx->data + off;
barrier_var(data);
if (data + sz >= ctx->data_end)
return NULL;
if (opcode == TCPOPT_EOL) ctx->off += sz;
return 1; return data;
if (opcode == TCPOPT_NOP) { }
++ctx->ptr;
return 0;
}
if (ctx->ptr + 1 >= ctx->end) static int tscookie_tcpopt_parse(struct tcpopt_context *ctx)
return 1; {
if (ctx->ptr + 1 >= ctx->data_end) __u8 *opcode, *opsize, *wscale, *tsecr;
__u32 off = ctx->off;
opcode = next(ctx, 1);
if (!opcode)
return 1; return 1;
opsize = ctx->ptr[1];
if (opsize < 2) if (*opcode == TCPOPT_EOL)
return 1; return 1;
if (*opcode == TCPOPT_NOP)
return 0;
if (ctx->ptr + opsize > ctx->end) opsize = next(ctx, 1);
if (!opsize || *opsize < 2)
return 1; return 1;
switch (opcode) { switch (*opcode) {
case TCPOPT_WINDOW: case TCPOPT_WINDOW:
if (opsize == TCPOLEN_WINDOW && ctx->ptr + TCPOLEN_WINDOW <= ctx->data_end) wscale = next(ctx, 1);
ctx->wscale = ctx->ptr[2] < TCP_MAX_WSCALE ? ctx->ptr[2] : TCP_MAX_WSCALE; if (!wscale)
return 1;
if (*opsize == TCPOLEN_WINDOW)
ctx->wscale = *wscale < TCP_MAX_WSCALE ? *wscale : TCP_MAX_WSCALE;
break; break;
case TCPOPT_TIMESTAMP: case TCPOPT_TIMESTAMP:
if (opsize == TCPOLEN_TIMESTAMP && ctx->ptr + TCPOLEN_TIMESTAMP <= ctx->data_end) { tsecr = next(ctx, 4);
if (!tsecr)
return 1;
if (*opsize == TCPOLEN_TIMESTAMP) {
ctx->option_timestamp = true; ctx->option_timestamp = true;
/* Client's tsval becomes our tsecr. */ /* Client's tsval becomes our tsecr. */
*ctx->tsecr = get_unaligned((__be32 *)(ctx->ptr + 2)); *ctx->tsecr = get_unaligned((__be32 *)tsecr);
} }
break; break;
case TCPOPT_SACK_PERM: case TCPOPT_SACK_PERM:
if (opsize == TCPOLEN_SACK_PERM) if (*opsize == TCPOLEN_SACK_PERM)
ctx->option_sack = true; ctx->option_sack = true;
break; break;
} }
ctx->ptr += opsize; ctx->off = off + *opsize;
return 0; return 0;
} }
...@@ -256,16 +271,21 @@ static int tscookie_tcpopt_parse_batch(__u32 index, void *context) ...@@ -256,16 +271,21 @@ static int tscookie_tcpopt_parse_batch(__u32 index, void *context)
static __always_inline bool tscookie_init(struct tcphdr *tcp_header, static __always_inline bool tscookie_init(struct tcphdr *tcp_header,
__u16 tcp_len, __be32 *tsval, __u16 tcp_len, __be32 *tsval,
__be32 *tsecr, void *data_end) __be32 *tsecr, void *data, void *data_end)
{ {
struct tcpopt_context loop_ctx = { struct tcpopt_context loop_ctx = {
.ptr = (__u8 *)(tcp_header + 1), .data = data,
.end = (__u8 *)tcp_header + tcp_len,
.data_end = data_end, .data_end = data_end,
.tsecr = tsecr, .tsecr = tsecr,
.wscale = TS_OPT_WSCALE_MASK, .wscale = TS_OPT_WSCALE_MASK,
.option_timestamp = false, .option_timestamp = false,
.option_sack = false, .option_sack = false,
/* Note: currently verifier would track .off as unbound scalar.
* In case if verifier would at some point get smarter and
* compute bounded value for this var, beware that it might
* hinder bpf_loop() convergence validation.
*/
.off = (__u8 *)(tcp_header + 1) - (__u8 *)data,
}; };
u32 cookie; u32 cookie;
...@@ -635,7 +655,7 @@ static __always_inline int syncookie_handle_syn(struct header_pointers *hdr, ...@@ -635,7 +655,7 @@ static __always_inline int syncookie_handle_syn(struct header_pointers *hdr,
cookie = (__u32)value; cookie = (__u32)value;
if (tscookie_init((void *)hdr->tcp, hdr->tcp_len, if (tscookie_init((void *)hdr->tcp, hdr->tcp_len,
&tsopt_buf[0], &tsopt_buf[1], data_end)) &tsopt_buf[0], &tsopt_buf[1], data, data_end))
tsopt = tsopt_buf; tsopt = tsopt_buf;
/* Check that there is enough space for a SYNACK. It also covers /* Check that there is enough space for a SYNACK. It also covers
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment