Skip to content

Commit 46c1605

Browse files
committed
no-pad: add toplevel -p option to disable frame padding
This enables sending runt frames. This is useful for testing on NPI ports and frame injection where IFH is prepended, so the wire length is above 60 bytes, while the frame data may be below.
1 parent dec564d commit 46c1605

5 files changed

Lines changed: 110 additions & 3 deletions

File tree

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ add_executable(ef-tests
9696
test/ef-tests.cxx
9797
test/test-ef-parse-bytes.cxx
9898
test/ifh-ignore.cxx
99+
test/test-padding.cxx
99100
)
100101

101102
target_link_libraries(ef-tests libef)

src/ef-args.c

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,8 @@ void print_help() {
9090
po("Options:\n");
9191
po(" -v Print version.\n");
9292
po(" -h Top level help message.\n");
93+
po(" -p No pad. Skip padding frames to 60 bytes,\n");
94+
po(" allowing runt frames to be sent or matched as-is.\n");
9395
po(" -t <timeout-in-ms> When listening on an interface (rx),\n");
9496
po(" When listening on an interface (rx), the tool will always\n");
9597
po(" listen during the entire timeout period. This is needed,\n");
@@ -320,13 +322,18 @@ int argc_cmds(int argc, const char *argv[]) {
320322
return res;
321323
}
322324

325+
int NO_PAD = 0;
323326
int TIME_OUT_MS = 100;
324327

325328
int main_(int argc, const char *argv[]) {
326329
int opt;
327330

328-
while ((opt = getopt(argc, (char * const*)argv, "vht:c:")) != -1) {
331+
while ((opt = getopt(argc, (char * const*)argv, "pvht:c:")) != -1) {
329332
switch (opt) {
333+
case 'p':
334+
NO_PAD = 1;
335+
break;
336+
330337
case 'v':
331338
print_version();
332339
return 0;

src/ef.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -477,7 +477,7 @@ buf_t *frame_to_buf(frame_t *f) {
477477
frame_size += f->stack[i]->size;
478478
}
479479

480-
if (frame_size < 60)
480+
if (frame_size < 60 && !NO_PAD)
481481
frame_size = 60;
482482

483483
buf = balloc(frame_size);
@@ -503,7 +503,7 @@ buf_t *frame_mask_to_buf(frame_t *f) {
503503
}
504504

505505
frame_size_no_padding = frame_size;
506-
if (frame_size < 60)
506+
if (frame_size < 60 && !NO_PAD)
507507
frame_size = 60;
508508

509509
buf = balloc(frame_size);

src/ef.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ extern "C" {
1616
#define DIV_ROUND(a, b) (1 + ((a - 1) / b))
1717
#define BIT_TO_BYTE(x) (DIV_ROUND(x, 8))
1818

19+
extern int NO_PAD;
1920
extern int TIME_OUT_MS;
2021

2122
///////////////////////////////////////////////////////////////////////////////

test/test-padding.cxx

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
#include "ef.h"
2+
#include "ef-test.h"
3+
#include "catch_single_include.hxx"
4+
5+
#include <cstring>
6+
7+
// RAII guard to set NO_PAD for the duration of a scope
8+
struct NoPadGuard {
9+
NoPadGuard() { NO_PAD = 1; }
10+
~NoPadGuard() { NO_PAD = 0; }
11+
};
12+
13+
// Build a frame and its expected buf/mask from a frame spec
14+
static void build_frame(std::vector<const char *> spec,
15+
buf_t **out_buf, buf_t **out_mask,
16+
frame_t **out_frame)
17+
{
18+
frame_t *f = parse_frame_wrap(spec);
19+
REQUIRE(f != NULL);
20+
*out_buf = frame_to_buf(f);
21+
REQUIRE(*out_buf != NULL);
22+
*out_mask = f->has_mask ? frame_mask_to_buf(f) : NULL;
23+
*out_frame = f;
24+
}
25+
26+
TEST_CASE("no-pad: eth-only frame is 14 bytes", "[nopad]") {
27+
NoPadGuard g;
28+
buf_t *buf, *mask;
29+
frame_t *f;
30+
build_frame({"eth", "dmac", "::1", "smac", "::2"}, &buf, &mask, &f);
31+
32+
// eth header = 14 bytes, no padding to 60
33+
CHECK(buf->size == 14);
34+
35+
bfree(buf);
36+
frame_free(f);
37+
}
38+
39+
TEST_CASE("no-pad: default pads to 60 bytes", "[nopad]") {
40+
CHECK(NO_PAD == 0);
41+
buf_t *buf, *mask;
42+
frame_t *f;
43+
build_frame({"eth", "dmac", "::1", "smac", "::2"}, &buf, &mask, &f);
44+
45+
CHECK(buf->size == 60);
46+
47+
bfree(buf);
48+
frame_free(f);
49+
}
50+
51+
TEST_CASE("no-pad: mask buf also skips padding", "[nopad]") {
52+
NoPadGuard g;
53+
54+
// Use 'ign' on smac to force mask generation
55+
frame_t *f = parse_frame_wrap({"eth", "dmac", "::1", "smac", "ign"});
56+
REQUIRE(f != NULL);
57+
buf_t *buf = frame_to_buf(f);
58+
buf_t *mask = frame_mask_to_buf(f);
59+
REQUIRE(buf != NULL);
60+
REQUIRE(mask != NULL);
61+
62+
CHECK(buf->size == 14);
63+
CHECK(mask->size == 14);
64+
65+
bfree(buf);
66+
bfree(mask);
67+
frame_free(f);
68+
}
69+
70+
TEST_CASE("no-pad: frame with payload stays exact size", "[nopad]") {
71+
NoPadGuard g;
72+
buf_t *buf, *mask;
73+
frame_t *f;
74+
75+
// eth(14) + data pattern cnt 4 = 18 bytes, well under 60
76+
build_frame({"eth", "dmac", "::1", "smac", "::2",
77+
"data", "pattern", "cnt", "4"}, &buf, &mask, &f);
78+
79+
CHECK(buf->size == 18);
80+
81+
bfree(buf);
82+
frame_free(f);
83+
}
84+
85+
TEST_CASE("no-pad: large frame unaffected", "[nopad]") {
86+
NoPadGuard g;
87+
buf_t *buf, *mask;
88+
frame_t *f;
89+
90+
// eth(14) + data pattern cnt 100 = 114 bytes, already > 60
91+
build_frame({"eth", "dmac", "::1", "smac", "::2",
92+
"data", "pattern", "cnt", "100"}, &buf, &mask, &f);
93+
94+
CHECK(buf->size == 114);
95+
96+
bfree(buf);
97+
frame_free(f);
98+
}

0 commit comments

Comments
 (0)