Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions doc/run.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

The DPDK based PMD requires VFIO (IOMMU) and huge pages to run, but it also supports non-root run, making it easy to deploy within Docker/Kubernetes environments.

## Network Security

Deploy RTP/UDP receivers on trusted media networks and restrict ingress with VLANs or firewall rules. MTL validates received packet fields, but network isolation limits exposure to malformed or unauthorized traffic.

## 1. IOMMU Setup

### 1.1. [IOMMU Setup](chunks/_iommu_setup.md)
Expand Down
9 changes: 9 additions & 0 deletions lib/src/st2110/st_rx_video_session.c
Original file line number Diff line number Diff line change
Expand Up @@ -1679,6 +1679,15 @@ static int rv_handle_frame_pkt(struct st_rx_video_session_impl* s, struct rte_mb
s->port_user_stats.stat_pkts_offset_dropped++;
return -EIO;
}
if (extra_rtp && s->st20_linesize > s->st20_bytes_in_line) {
size_t continuation_row = (size_t)line1_number + 1;
size_t continuation_length = payload_length - line1_length;
if ((continuation_row >= ops->height) ||
(continuation_length > s->st20_fb_size - continuation_row * s->st20_linesize)) {
s->port_user_stats.stat_pkts_offset_dropped++;
return -EIO;
}
}

/* check if valid pkt len */
size_t pkt_payload_len = mbuf->pkt_len - sizeof(struct st_rfc4175_video_hdr);
Expand Down
23 changes: 23 additions & 0 deletions tests/unit/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,29 @@ Filter to a single suite or test:
./build_unit/tests/unit/UnitTest --gtest_list_tests
```

### ST20 continuation bounds regression

`St20RxHeaderValidationTest.FinalRowContinuationPastFrameDropped` feeds the
production RFC 4175 frame handler a continuation SRD whose second row begins
past its frame buffer. It needs no NIC, root, hugepages, or packet injector.

```bash
meson setup build_unit -Denable_unit_tests=true
ninja -C build_unit tests/unit/UnitTest
./build_unit/tests/unit/UnitTest \
--gtest_filter='St20RxHeaderValidationTest.FinalRowContinuationPastFrameDropped'
```

The test geometry is two 40-byte rows with a padded 48-byte stride, making an
80-byte frame. It sends row 1 with a 12-byte first SRD and a 12-byte
continuation. The original offset check accepts the first copy, while the
second copy would begin at byte 96. Correct behavior is a negative return and
one `stat_pkts_offset_dropped` increment. Before the continuation bound check,
this test fails with `rc` equal to `0` and the drop counter equal to `0`.

`St20RxHeaderValidationTest.PaddedLineContinuationAccepted` is the companion
positive test. It verifies a valid padded continuation remains accepted.

> **Why `LD_PRELOAD=libasan.so`?** `libmtl.so` is built with AddressSanitizer
> when `enable_unit_tests=true`. Preloading the runtime first prevents
> init-order interposition issues between gtest, libstdc++, and libasan.
Expand Down
18 changes: 18 additions & 0 deletions tests/unit/session/st20/header_validation_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,3 +73,21 @@ TEST_F(St20RxHeaderValidationTest, WrongInterlaceDropped) {
EXPECT_EQ(rc, -EINVAL);
EXPECT_EQ(wrong_interlace(), 1u);
}

TEST_F(St20RxHeaderValidationTest, FinalRowContinuationPastFrameDropped) {
ut20_ctx_set_linesize(ctx_, 48);

int rc = ut20_feed_continuation_pkt(ctx_, 0, 1000, 1, 0, 12, 12, MTL_SESSION_PORT_P);

EXPECT_LT(rc, 0);
EXPECT_EQ(ut20_stat_offset_dropped(ctx_), 1u);
}

TEST_F(St20RxHeaderValidationTest, PaddedLineContinuationAccepted) {
ut20_ctx_set_linesize(ctx_, 48);

int rc = ut20_feed_continuation_pkt(ctx_, 0, 1000, 0, 0, 12, 12, MTL_SESSION_PORT_P);

EXPECT_EQ(rc, 0);
EXPECT_EQ(ut20_stat_offset_dropped(ctx_), 0u);
}
58 changes: 58 additions & 0 deletions tests/unit/session/st20_harness.c
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,46 @@ static struct rte_mbuf* make_video_mbuf(uint32_t seq, uint32_t ts, uint16_t line
return make_video_mbuf_full(seq, ts, line_num, line_offset, line_length, 0, 0);
}

static struct rte_mbuf* make_video_continuation_mbuf(uint32_t seq, uint32_t ts,
uint16_t line_num,
uint16_t line_offset,
uint16_t line1_length,
uint16_t line2_length) {
struct rte_mbuf* m = rte_pktmbuf_alloc(ut_pool());
if (!m) return NULL;

size_t total = sizeof(struct st_rfc4175_video_hdr) +
sizeof(struct st20_rfc4175_extra_rtp_hdr) + line1_length + line2_length;
if (rte_pktmbuf_tailroom(m) < total) {
rte_pktmbuf_free(m);
return NULL;
}

uint8_t* buf = rte_pktmbuf_mtod(m, uint8_t*);
memset(buf, 0, total);
size_t hdr_offset =
sizeof(struct st_rfc4175_video_hdr) - sizeof(struct st20_rfc4175_rtp_hdr);
struct st20_rfc4175_rtp_hdr* rtp = (struct st20_rfc4175_rtp_hdr*)(buf + hdr_offset);
struct st20_rfc4175_extra_rtp_hdr* extra_rtp =
(struct st20_rfc4175_extra_rtp_hdr*)&rtp[1];

rtp->base.version = 2;
rtp->base.seq_number = htons((uint16_t)(seq & 0xFFFF));
rtp->base.tmstamp = htonl(ts);
rtp->seq_number_ext = htons((uint16_t)(seq >> 16));
rtp->row_number = htons(line_num);
rtp->row_offset = htons(line_offset | ST20_SRD_OFFSET_CONTINUATION);
rtp->row_length = htons(line1_length);
extra_rtp->row_length = htons(line2_length);
extra_rtp->row_offset = 0;
extra_rtp->row_number = htons(line_num + 1);

m->data_len = total;
m->pkt_len = total;
m->next = NULL;
return m;
}

/* ── pkt_idx → line/offset mapping ────────────────────────────────────── */

static void pkt_idx_to_line(int pkt_idx, uint16_t* line_num, uint16_t* line_offset,
Expand All @@ -272,6 +312,18 @@ int ut20_feed_pkt(ut20_test_ctx* ctx, uint32_t seq, uint32_t ts, uint16_t line_n
return rc;
}

int ut20_feed_continuation_pkt(ut20_test_ctx* ctx, uint32_t seq, uint32_t ts,
uint16_t line_num, uint16_t line_offset,
uint16_t line1_length, uint16_t line2_length,
enum mtl_session_port port) {
struct rte_mbuf* m = make_video_continuation_mbuf(seq, ts, line_num, line_offset,
line1_length, line2_length);
if (!m) return -1;
int rc = rv_handle_frame_pkt(&ctx->session, m, port, true);
rte_pktmbuf_free(m);
return rc;
}

int ut20_feed_frame_pkt(ut20_test_ctx* ctx, int pkt_idx, uint32_t ts,
enum mtl_session_port port) {
uint32_t seq = ts * (uint32_t)ctx->session.ops.height + (uint32_t)pkt_idx;
Expand Down Expand Up @@ -393,6 +445,12 @@ void ut20_ctx_set_ssrc(ut20_test_ctx* ctx, uint32_t ssrc) {
ctx->session.ops.ssrc = ssrc;
}

void ut20_ctx_set_linesize(ut20_test_ctx* ctx, uint32_t linesize) {
ctx->session.ops.linesize = linesize;
ctx->session.st20_linesize = linesize;
ctx->session.st20_fb_size = linesize * ctx->session.ops.height;
}

void ut20_set_port_down(ut20_test_ctx* ctx, enum mtl_session_port port, bool down) {
enum mtl_port phy = mt_port_logic2phy(ctx->session.port_maps, port);
if (down)
Expand Down
6 changes: 6 additions & 0 deletions tests/unit/session/st20_harness.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,11 @@ void ut20_ctx_destroy(ut20_test_ctx* ctx);
int ut20_feed_pkt(ut20_test_ctx* ctx, uint32_t seq, uint32_t ts, uint16_t line_num,
uint16_t line_offset, uint16_t line_length, enum mtl_session_port port);

int ut20_feed_continuation_pkt(ut20_test_ctx* ctx, uint32_t seq, uint32_t ts,
uint16_t line_num, uint16_t line_offset,
uint16_t line1_length, uint16_t line2_length,
enum mtl_session_port port);

/* Convenience: feed packet `pkt_idx` (0 .. UT20_PKTS_PER_FRAME-1) of a
* frame at timestamp `ts`. Auto-derives line_num/offset/length and uses
* `pkt_idx` as the RTP sequence number (no per-port sequence tracking). */
Expand Down Expand Up @@ -109,6 +114,7 @@ int ut20_feed_pkt_ssrc(ut20_test_ctx* ctx, uint32_t seq, uint32_t ts, uint16_t l
* ut20_ctx_create() and before feeding any packet. */
void ut20_ctx_set_pt(ut20_test_ctx* ctx, uint8_t pt);
void ut20_ctx_set_ssrc(ut20_test_ctx* ctx, uint32_t ssrc);
void ut20_ctx_set_linesize(ut20_test_ctx* ctx, uint32_t linesize);

/* Force a session port's physical link up/down. A link-down port is a dead
* wire: it never receives data and is not charged per-port loss at frame
Expand Down
Loading