diff --git a/CMakeLists.txt b/CMakeLists.txt index e489537..e81ea57 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -96,6 +96,7 @@ add_executable(ef-tests test/ef-tests.cxx test/test-ef-parse-bytes.cxx test/ifh-ignore.cxx + test/test-padding.cxx ) target_link_libraries(ef-tests libef) diff --git a/src/ef-args.c b/src/ef-args.c index d42bae6..abfeff4 100644 --- a/src/ef-args.c +++ b/src/ef-args.c @@ -90,6 +90,8 @@ void print_help() { po("Options:\n"); po(" -v Print version.\n"); po(" -h Top level help message.\n"); + po(" -p No pad. Skip padding frames to 60 bytes,\n"); + po(" allowing runt frames to be sent or matched as-is.\n"); po(" -t When listening on an interface (rx),\n"); po(" When listening on an interface (rx), the tool will always\n"); po(" listen during the entire timeout period. This is needed,\n"); @@ -320,13 +322,18 @@ int argc_cmds(int argc, const char *argv[]) { return res; } +int NO_PAD = 0; int TIME_OUT_MS = 100; int main_(int argc, const char *argv[]) { int opt; - while ((opt = getopt(argc, (char * const*)argv, "vht:c:")) != -1) { + while ((opt = getopt(argc, (char * const*)argv, "pvht:c:")) != -1) { switch (opt) { + case 'p': + NO_PAD = 1; + break; + case 'v': print_version(); return 0; diff --git a/src/ef.c b/src/ef.c index 5a1d5d9..ae3c147 100644 --- a/src/ef.c +++ b/src/ef.c @@ -477,7 +477,7 @@ buf_t *frame_to_buf(frame_t *f) { frame_size += f->stack[i]->size; } - if (frame_size < 60) + if (frame_size < 60 && !NO_PAD) frame_size = 60; buf = balloc(frame_size); @@ -503,7 +503,7 @@ buf_t *frame_mask_to_buf(frame_t *f) { } frame_size_no_padding = frame_size; - if (frame_size < 60) + if (frame_size < 60 && !NO_PAD) frame_size = 60; buf = balloc(frame_size); diff --git a/src/ef.h b/src/ef.h index 10a3863..af31deb 100644 --- a/src/ef.h +++ b/src/ef.h @@ -16,6 +16,7 @@ extern "C" { #define DIV_ROUND(a, b) (1 + ((a - 1) / b)) #define BIT_TO_BYTE(x) (DIV_ROUND(x, 8)) +extern int NO_PAD; extern int TIME_OUT_MS; /////////////////////////////////////////////////////////////////////////////// diff --git a/test/test-padding.cxx b/test/test-padding.cxx new file mode 100644 index 0000000..f24a3b5 --- /dev/null +++ b/test/test-padding.cxx @@ -0,0 +1,98 @@ +#include "ef.h" +#include "ef-test.h" +#include "catch_single_include.hxx" + +#include + +// RAII guard to set NO_PAD for the duration of a scope +struct NoPadGuard { + NoPadGuard() { NO_PAD = 1; } + ~NoPadGuard() { NO_PAD = 0; } +}; + +// Build a frame and its expected buf/mask from a frame spec +static void build_frame(std::vector spec, + buf_t **out_buf, buf_t **out_mask, + frame_t **out_frame) +{ + frame_t *f = parse_frame_wrap(spec); + REQUIRE(f != NULL); + *out_buf = frame_to_buf(f); + REQUIRE(*out_buf != NULL); + *out_mask = f->has_mask ? frame_mask_to_buf(f) : NULL; + *out_frame = f; +} + +TEST_CASE("no-pad: eth-only frame is 14 bytes", "[nopad]") { + NoPadGuard g; + buf_t *buf, *mask; + frame_t *f; + build_frame({"eth", "dmac", "::1", "smac", "::2"}, &buf, &mask, &f); + + // eth header = 14 bytes, no padding to 60 + CHECK(buf->size == 14); + + bfree(buf); + frame_free(f); +} + +TEST_CASE("no-pad: default pads to 60 bytes", "[nopad]") { + CHECK(NO_PAD == 0); + buf_t *buf, *mask; + frame_t *f; + build_frame({"eth", "dmac", "::1", "smac", "::2"}, &buf, &mask, &f); + + CHECK(buf->size == 60); + + bfree(buf); + frame_free(f); +} + +TEST_CASE("no-pad: mask buf also skips padding", "[nopad]") { + NoPadGuard g; + + // Use 'ign' on smac to force mask generation + frame_t *f = parse_frame_wrap({"eth", "dmac", "::1", "smac", "ign"}); + REQUIRE(f != NULL); + buf_t *buf = frame_to_buf(f); + buf_t *mask = frame_mask_to_buf(f); + REQUIRE(buf != NULL); + REQUIRE(mask != NULL); + + CHECK(buf->size == 14); + CHECK(mask->size == 14); + + bfree(buf); + bfree(mask); + frame_free(f); +} + +TEST_CASE("no-pad: frame with payload stays exact size", "[nopad]") { + NoPadGuard g; + buf_t *buf, *mask; + frame_t *f; + + // eth(14) + data pattern cnt 4 = 18 bytes, well under 60 + build_frame({"eth", "dmac", "::1", "smac", "::2", + "data", "pattern", "cnt", "4"}, &buf, &mask, &f); + + CHECK(buf->size == 18); + + bfree(buf); + frame_free(f); +} + +TEST_CASE("no-pad: large frame unaffected", "[nopad]") { + NoPadGuard g; + buf_t *buf, *mask; + frame_t *f; + + // eth(14) + data pattern cnt 100 = 114 bytes, already > 60 + build_frame({"eth", "dmac", "::1", "smac", "::2", + "data", "pattern", "cnt", "100"}, &buf, &mask, &f); + + CHECK(buf->size == 114); + + bfree(buf); + frame_free(f); +}