summaryrefslogtreecommitdiffstats
path: root/src/vnet/tcp/tcp_pg.c
diff options
context:
space:
mode:
authorMaxime Peim <mpeim@cisco.com>2023-06-19 18:19:48 +0200
committerBeno�t Ganne <bganne@cisco.com>2023-06-22 12:39:35 +0000
commitfdf6fbe2e771d88beddb6ca8d8bbd39599beeb9c (patch)
tree0e2c781b36f8e169e5393a7aa68802dc964b32c9 /src/vnet/tcp/tcp_pg.c
parent9ba6dcf558eeb876f863e13e6250c13341a2a3f0 (diff)
tcp: options support into pg
Packet-generator does not support TCP options. Along with its support, a formatting function has been added. Further work will be needed to update header formatting functions to take into account TCP connection options. For now, TCP options are taken on a per-packet basis. Type: improvement Change-Id: Id800887853c4941d893be353ce6d8624ed8bbc5d Signed-off-by: Maxime Peim <mpeim@cisco.com>
Diffstat (limited to 'src/vnet/tcp/tcp_pg.c')
-rw-r--r--src/vnet/tcp/tcp_pg.c181
1 files changed, 149 insertions, 32 deletions
diff --git a/src/vnet/tcp/tcp_pg.c b/src/vnet/tcp/tcp_pg.c
index 07bdb113fd0..9b98e3d8ee4 100644
--- a/src/vnet/tcp/tcp_pg.c
+++ b/src/vnet/tcp/tcp_pg.c
@@ -51,6 +51,13 @@
_ (ECE) \
_ (CWR)
+#define foreach_tcp_options \
+ _ (mss, TCP_OPTION_MSS, TCP_OPTION_LEN_MSS, 1) \
+ _ (timestamp, TCP_OPTION_TIMESTAMP, TCP_OPTION_LEN_TIMESTAMP, 2) \
+ _ (winscale, TCP_OPTION_WINDOW_SCALE, TCP_OPTION_LEN_WINDOW_SCALE, 1) \
+ _ (sackperm, TCP_OPTION_SACK_PERMITTED, TCP_OPTION_LEN_SACK_PERMITTED, 0) \
+ _ (sack, TCP_OPTION_SACK_BLOCK, TCP_OPTION_LEN_SACK_BLOCK, 0)
+
static void
tcp_pg_edit_function (pg_main_t * pg,
pg_stream_t * s,
@@ -150,82 +157,192 @@ uword
unformat_pg_tcp_header (unformat_input_t * input, va_list * args)
{
pg_stream_t *s = va_arg (*args, pg_stream_t *);
- pg_tcp_header_t *p;
- u32 group_index;
+ pg_tcp_header_t *pth;
+ u32 header_group_index, opt_group_index = ~0, noop_len, opts_len = 0;
- p = pg_create_edit_group (s, sizeof (p[0]), sizeof (tcp_header_t),
- &group_index);
- pg_tcp_header_init (p);
+ pth = pg_create_edit_group (s, sizeof (pth[0]), sizeof (tcp_header_t),
+ &header_group_index);
+ pg_tcp_header_init (pth);
/* Defaults. */
- pg_edit_set_fixed (&p->seq_number, 0);
- pg_edit_set_fixed (&p->ack_number, 0);
-
- pg_edit_set_fixed (&p->data_offset_and_reserved,
- sizeof (tcp_header_t) / sizeof (u32));
+ pg_edit_set_fixed (&pth->seq_number, 0);
+ pg_edit_set_fixed (&pth->ack_number, 0);
- pg_edit_set_fixed (&p->window, 4096);
- pg_edit_set_fixed (&p->urgent_pointer, 0);
+ pg_edit_set_fixed (&pth->window, 4096);
+ pg_edit_set_fixed (&pth->urgent_pointer, 0);
-#define _(f) pg_edit_set_fixed (&p->f##_flag, 0);
+#define _(f) pg_edit_set_fixed (&pth->f##_flag, 0);
foreach_tcp_flag
#undef _
- p->checksum.type = PG_EDIT_UNSPECIFIED;
+ pth->checksum.type = PG_EDIT_UNSPECIFIED;
- if (!unformat (input, "TCP: %U -> %U",
- unformat_pg_edit,
- unformat_tcp_udp_port, &p->src,
- unformat_pg_edit, unformat_tcp_udp_port, &p->dst))
+ if (!unformat (input, "TCP: %U -> %U", unformat_pg_edit,
+ unformat_tcp_udp_port, &pth->src, unformat_pg_edit,
+ unformat_tcp_udp_port, &pth->dst))
goto error;
/* Parse options. */
while (1)
{
- if (unformat (input, "window %U",
- unformat_pg_edit, unformat_pg_number, &p->window))
+ if (unformat (input, "window %U", unformat_pg_edit, unformat_pg_number,
+ &pth->window))
;
- else if (unformat (input, "checksum %U",
- unformat_pg_edit, unformat_pg_number, &p->checksum))
+ else if (unformat (input, "checksum %U", unformat_pg_edit,
+ unformat_pg_number, &pth->checksum))
;
else if (unformat (input, "seqnum %U", unformat_pg_edit,
- unformat_pg_number, &p->seq_number))
+ unformat_pg_number, &pth->seq_number))
;
else if (unformat (input, "acknum %U", unformat_pg_edit,
- unformat_pg_number, &p->ack_number))
+ unformat_pg_number, &pth->ack_number))
;
/* Flags. */
-#define _(f) else if (unformat (input, #f)) pg_edit_set_fixed (&p->f##_flag, 1);
+#define _(f) \
+ else if (unformat (input, #f)) pg_edit_set_fixed (&pth->f##_flag, 1);
foreach_tcp_flag
#undef _
- /* Can't parse input: try next protocol level. */
+ /* Can't parse input: try TCP options and next protocol level. */
+ else break;
+ }
+
+ while (unformat (input, "opt"))
+ {
+ int i;
+ pg_edit_t *opt_header, *opt_values;
+ u8 type, opt_len, n_values;
+
+ /* first allocate a new edit group for options */
+ if (opt_group_index == ~0)
+ (void) pg_create_edit_group (s, 0, 0, &opt_group_index);
+
+ if (false)
+ {
+ }
+#define _(n, t, l, k) \
+ else if (unformat (input, #n)) \
+ { \
+ type = (t); \
+ opt_len = (l); \
+ n_values = (k); \
+ }
+ foreach_tcp_options
+#undef _
else
+ {
+ /* unknown TCP option */
break;
+ }
+
+#define pg_tcp_option_init(e, o, b) \
+ do \
+ { \
+ *(o) += (b); \
+ (e)->lsb_bit_offset = *(o) > 0 ? (*(o) -1) * BITS (u8) : 0; \
+ (e)->n_bits = (b) *BITS (u8); \
+ } \
+ while (0);
+
+ /* if we don't know how many values to read, just ask */
+ if (n_values == 0 &&
+ unformat (input, "nvalues %D", sizeof (n_values), &n_values))
+ {
+ switch (type)
+ {
+ case TCP_OPTION_SACK_BLOCK:
+ /* each sack block is composed of 2 32-bits values */
+ n_values *= 2;
+ /*
+ opt_len contains the length of a single sack block,
+ it needs to be updated to contains the final number of bytes
+ for the sack options
+ */
+ opt_len = 2 + 2 * opt_len;
+ break;
+ default:
+ /* unknown variable options */
+ continue;
+ }
+ }
+
+ opt_header = pg_add_edits (s, sizeof (pg_edit_t) * (2 + n_values),
+ opt_len, opt_group_index);
+ pg_tcp_option_init (opt_header, &opts_len, 1);
+ pg_tcp_option_init (opt_header + 1, &opts_len, 1);
+ pg_edit_set_fixed (opt_header, type);
+ pg_edit_set_fixed (opt_header + 1, opt_len);
+ opt_values = opt_header + 2;
+
+ switch (type)
+ {
+ case TCP_OPTION_MSS:
+ pg_tcp_option_init (opt_values, &opts_len, 2);
+ break;
+ case TCP_OPTION_WINDOW_SCALE:
+ pg_tcp_option_init (opt_values, &opts_len, 1);
+ break;
+ case TCP_OPTION_TIMESTAMP:
+ case TCP_OPTION_SACK_BLOCK:
+ for (i = 0; i < n_values; ++i)
+ pg_tcp_option_init (opt_values + i, &opts_len, 4);
+ break;
+ default:
+ break;
+ }
+
+ for (i = 0; i < n_values; ++i)
+ {
+ if (!unformat (input, "%U", unformat_pg_edit, unformat_pg_number,
+ opt_values + i))
+ goto error;
+ }
}
+ /* add TCP NO-OP options to fill options up to a 4-bytes boundary */
+ noop_len = (TCP_OPTS_ALIGN - opts_len % TCP_OPTS_ALIGN) % TCP_OPTS_ALIGN;
+ if (noop_len > 0)
+ {
+ pg_edit_t *noop_edit;
+ u8 *noops = 0;
+
+ vec_validate (noops, noop_len - 1);
+ clib_memset (noops, 1, noop_len);
+
+ noop_edit =
+ pg_add_edits (s, sizeof (noop_edit[0]), noop_len, opt_group_index);
+ pg_tcp_option_init (noop_edit, &opts_len, noop_len);
+ noop_edit->type = PG_EDIT_FIXED;
+ noop_edit->values[PG_EDIT_LO] = noops;
+ }
+#undef pg_tcp_option_init
+
+ /* set the data offset according to options */
+ pg_edit_set_fixed (&pth->data_offset_and_reserved,
+ (sizeof (tcp_header_t) + opts_len) / sizeof (u32));
+
{
ip_main_t *im = &ip_main;
u16 dst_port;
tcp_udp_port_info_t *pi;
pi = 0;
- if (p->dst.type == PG_EDIT_FIXED)
+ if (pth->dst.type == PG_EDIT_FIXED)
{
- dst_port = pg_edit_get_value (&p->dst, PG_EDIT_LO);
+ dst_port = pg_edit_get_value (&pth->dst, PG_EDIT_LO);
pi = ip_get_tcp_udp_port_info (im, dst_port);
}
- if (pi && pi->unformat_pg_edit
- && unformat_user (input, pi->unformat_pg_edit, s))
+ if (pi && pi->unformat_pg_edit &&
+ unformat_user (input, pi->unformat_pg_edit, s))
;
else if (!unformat_user (input, unformat_pg_payload, s))
goto error;
- if (p->checksum.type == PG_EDIT_UNSPECIFIED)
+ if (pth->checksum.type == PG_EDIT_UNSPECIFIED)
{
- pg_edit_group_t *g = pg_stream_get_group (s, group_index);
+ pg_edit_group_t *g = pg_stream_get_group (s, header_group_index);
g->edit_function = tcp_pg_edit_function;
g->edit_function_opaque = 0;
}