100 lines
3.7 KiB
C
100 lines
3.7 KiB
C
// Replays recorded air traffic through gn_unwrap_its, one pcap record at a time and exactly as the
|
|||
|
|
// promiscuous RX callback hands each frame over (the recordings come through the same API), and
|
||
|
|
// writes what came out to a TSV that check_replay.py verifies independently. See README.md.
|
||
|
|
//
|
||
|
|
// Usage: test_replay out.tsv capture.pcap [capture.pcap ...]
|
||
|
|
//
|
||
|
|
// One row per pcap record:
|
||
|
|
// file, record, accepted, port, signed, truncated, area, area_lat, area_lon, area_dist, payload
|
||
|
|
// with everything after `accepted` empty for rejected frames and the payload in hex. Each frame
|
||
|
|
// is placed against the guard page, and an accepted payload that is not inside its frame is a
|
||
|
|
// failure here already.
|
||
|
|
|
||
|
|
#include <stdint.h>
|
||
|
|
#include <stdio.h>
|
||
|
|
#include <stdlib.h>
|
||
|
|
|
||
|
|
#include "gn_unwrap.h"
|
||
|
|
#include "test_util.h"
|
||
|
|
|
||
|
|
// main.c's RX_FRAME_MAX_LEN: the promiscuous callback copies at most this many bytes of a frame,
|
||
|
|
// so it is the most gn_unwrap_its ever sees on the board. Keep in step with main.c.
|
||
|
|
#define RX_FRAME_MAX_LEN 800
|
||
|
|
|
||
|
|
typedef struct {
|
||
|
|
FILE *out;
|
||
|
|
const char *path;
|
||
|
|
long records, capped, accepted, cam, denm, spatem, signed_frames, truncated, bad;
|
||
|
|
} replay_t;
|
||
|
|
|
||
|
|
static void on_frame(const uint8_t *frame, int len, int index, void *ctx)
|
||
|
|
{
|
||
|
|
replay_t *r = ctx;
|
||
|
|
r->records++;
|
||
|
|
if (len > RX_FRAME_MAX_LEN) {
|
||
|
|
len = RX_FRAME_MAX_LEN;
|
||
|
|
r->capped++;
|
||
|
|
}
|
||
|
|
tu_set_context("replay %s record %d (%d bytes)", r->path, index, len);
|
||
|
|
const uint8_t *g = tu_guarded(frame, len);
|
||
|
|
gn_rx_t rx;
|
||
|
|
const bool ok = gn_unwrap_its(g, len, &rx);
|
||
|
|
fprintf(r->out, "%s\t%d\t%d", r->path, index, ok);
|
||
|
|
if (ok) {
|
||
|
|
const uintptr_t lo = (uintptr_t)g;
|
||
|
|
const uintptr_t p = (uintptr_t)rx.payload;
|
||
|
|
if (p < lo || rx.payload_len <= 0 || p + (uintptr_t)rx.payload_len > lo + (uintptr_t)len) {
|
||
|
|
fprintf(stderr, "FAIL %s: payload outside the frame\n", tu_context());
|
||
|
|
r->bad++;
|
||
|
|
} else {
|
||
|
|
fprintf(r->out, "\t%u\t%d\t%d\t%d\t%ld\t%ld\t%u\t", rx.btp_dest_port, rx.signed_unverified,
|
||
|
|
rx.truncated, rx.has_geo_area, (long)rx.geo_area_lat_tenmicrodeg,
|
||
|
|
(long)rx.geo_area_lon_tenmicrodeg, rx.geo_area_distance_a_m);
|
||
|
|
for (int i = 0; i < rx.payload_len; i++) {
|
||
|
|
fprintf(r->out, "%02x", rx.payload[i]);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
r->accepted++;
|
||
|
|
r->cam += rx.btp_dest_port == 2001;
|
||
|
|
r->denm += rx.btp_dest_port == 2002;
|
||
|
|
r->spatem += rx.btp_dest_port == 2004;
|
||
|
|
r->signed_frames += rx.signed_unverified;
|
||
|
|
r->truncated += rx.truncated;
|
||
|
|
}
|
||
|
|
fputc('\n', r->out);
|
||
|
|
}
|
||
|
|
|
||
|
|
int main(int argc, char **argv)
|
||
|
|
{
|
||
|
|
if (argc < 3) {
|
||
|
|
fprintf(stderr, "usage: test_replay out.tsv capture.pcap [capture.pcap ...]\n");
|
||
|
|
return 2;
|
||
|
|
}
|
||
|
|
tu_install_crash_handler();
|
||
|
|
tu_guard_init();
|
||
|
|
|
||
|
|
replay_t r = {0};
|
||
|
|
r.out = fopen(argv[1], "w");
|
||
|
|
if (!r.out) {
|
||
|
|
fprintf(stderr, "cannot write %s\n", argv[1]);
|
||
|
|
return 2;
|
||
|
|
}
|
||
|
|
fprintf(r.out, "file\trecord\taccepted\tport\tsigned\ttruncated\tarea\tarea_lat\tarea_lon\tarea_dist\tpayload\n");
|
||
|
|
|
||
|
|
int unreadable = 0;
|
||
|
|
for (int i = 2; i < argc; i++) {
|
||
|
|
r.path = argv[i];
|
||
|
|
if (tu_pcap_foreach(argv[i], on_frame, &r) < 0) {
|
||
|
|
fprintf(stderr, "cannot read %s as a pcap\n", argv[i]);
|
||
|
|
unreadable++;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
fclose(r.out);
|
||
|
|
|
||
|
|
printf("test_replay: %ld records (%ld cut to %d bytes as main.c does), %ld accepted (CAM %ld, "
|
||
|
|
"DENM %ld, SPATEM %ld; %ld signed, %ld truncated), %ld bad\n",
|
||
|
|
r.records, r.capped, RX_FRAME_MAX_LEN, r.accepted, r.cam, r.denm, r.spatem,
|
||
|
|
r.signed_frames, r.truncated, r.bad);
|
||
|
|
return (r.bad || unreadable) ? 1 : 0;
|
||
|
|
}
|