Skip to content

Commit

Permalink
added TCP/UDP packet splitting to fit within MTU
Browse files Browse the repository at this point in the history
  • Loading branch information
robert committed Sep 6, 2023
1 parent fbc56f4 commit 1274ec4
Show file tree
Hide file tree
Showing 6 changed files with 84 additions and 27 deletions.
29 changes: 17 additions & 12 deletions mongoose.c
Original file line number Diff line number Diff line change
Expand Up @@ -3694,6 +3694,7 @@ struct mg_connection *mg_alloc_conn(struct mg_mgr *mgr) {
c->mgr = mgr;
c->send.align = c->recv.align = MG_IO_SIZE;
c->id = ++mgr->nextid;
c->mtu = MTU_DEFAULT_VALUE;
}
return c;
}
Expand Down Expand Up @@ -4383,17 +4384,19 @@ long mg_io_send(struct mg_connection *c, const void *buf, size_t len) {
struct mg_tcpip_if *ifp = (struct mg_tcpip_if *) c->mgr->priv;
struct connstate *s = (struct connstate *) (c + 1);
uint32_t rem_ip;
size_t eth_h_len = 14, ip_max_h_len = 24, tcp_max_h_len = 60, udp_h_len = 8;
size_t max_headers_len = eth_h_len + ip_max_h_len +
(c->is_udp ? udp_h_len : tcp_max_h_len);
memcpy(&rem_ip, c->rem.ip, sizeof(uint32_t));
if (len + max_headers_len > ifp->tx.len) {
len = ifp->tx.len - max_headers_len;
}
if (len + max_headers_len - eth_h_len > c->mtu) {
len = c->mtu - max_headers_len + eth_h_len;
}
if (c->is_udp) {
size_t max_headers_len = 14 + 24 /* max IP */ + 8 /* UDP */;
if (len + max_headers_len > ifp->tx.len) {
len = ifp->tx.len - max_headers_len;
}
tx_udp(ifp, s->mac, ifp->ip, c->loc.port, rem_ip, c->rem.port, buf, len);
} else {
size_t max_headers_len = 14 + 24 /* max IP */ + 60 /* max TCP */;
if (len + max_headers_len > ifp->tx.len)
len = ifp->tx.len - max_headers_len;
if (tx_tcp(ifp, s->mac, rem_ip, TH_PUSH | TH_ACK, c->loc.port, c->rem.port,
mg_htonl(s->seq), mg_htonl(s->ack), buf, len) > 0) {
s->seq += (uint32_t) len;
Expand Down Expand Up @@ -4849,11 +4852,13 @@ bool mg_open_listener(struct mg_connection *c, const char *url) {
}

static void write_conn(struct mg_connection *c) {
long len = c->is_tls ? mg_tls_send(c, c->send.buf, c->send.len)
: mg_io_send(c, c->send.buf, c->send.len);
if (len > 0) {
mg_iobuf_del(&c->send, 0, (size_t) len);
mg_call(c, MG_EV_WRITE, &len);
while (c->send.len) {
long len = c->is_tls ? mg_tls_send(c, c->send.buf, c->send.len)
: mg_io_send(c, c->send.buf, c->send.len);
if (len > 0) {
mg_iobuf_del(&c->send, 0, (size_t) len);
mg_call(c, MG_EV_WRITE, &len);
}
}
}

Expand Down
2 changes: 2 additions & 0 deletions mongoose.h
Original file line number Diff line number Diff line change
Expand Up @@ -1201,6 +1201,8 @@ struct mg_connection {
void *pfn_data; // Protocol-specific function parameter
char data[MG_DATA_SIZE]; // Arbitrary connection data
void *tls; // TLS specific data
uint16_t mtu; // MTU for this connection
#define MTU_DEFAULT_VALUE 1500
unsigned is_listening : 1; // Listening connection
unsigned is_client : 1; // Outbound (client) connection
unsigned is_accepted : 1; // Accepted (server) connection
Expand Down
1 change: 1 addition & 0 deletions src/net.c
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ struct mg_connection *mg_alloc_conn(struct mg_mgr *mgr) {
c->mgr = mgr;
c->send.align = c->recv.align = MG_IO_SIZE;
c->id = ++mgr->nextid;
c->mtu = MTU_DEFAULT_VALUE;
}
return c;
}
Expand Down
2 changes: 2 additions & 0 deletions src/net.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ struct mg_connection {
void *pfn_data; // Protocol-specific function parameter
char data[MG_DATA_SIZE]; // Arbitrary connection data
void *tls; // TLS specific data
uint16_t mtu; // MTU for this connection
#define MTU_DEFAULT_VALUE 1500
unsigned is_listening : 1; // Listening connection
unsigned is_client : 1; // Outbound (client) connection
unsigned is_accepted : 1; // Accepted (server) connection
Expand Down
28 changes: 16 additions & 12 deletions src/net_builtin.c
Original file line number Diff line number Diff line change
Expand Up @@ -552,17 +552,19 @@ long mg_io_send(struct mg_connection *c, const void *buf, size_t len) {
struct mg_tcpip_if *ifp = (struct mg_tcpip_if *) c->mgr->priv;
struct connstate *s = (struct connstate *) (c + 1);
uint32_t rem_ip;
size_t eth_h_len = 14, ip_max_h_len = 24, tcp_max_h_len = 60, udp_h_len = 8;
size_t max_headers_len = eth_h_len + ip_max_h_len +
(c->is_udp ? udp_h_len : tcp_max_h_len);
memcpy(&rem_ip, c->rem.ip, sizeof(uint32_t));
if (len + max_headers_len > ifp->tx.len) {
len = ifp->tx.len - max_headers_len;
}
if (len + max_headers_len - eth_h_len > c->mtu) {
len = c->mtu - max_headers_len + eth_h_len;
}
if (c->is_udp) {
size_t max_headers_len = 14 + 24 /* max IP */ + 8 /* UDP */;
if (len + max_headers_len > ifp->tx.len) {
len = ifp->tx.len - max_headers_len;
}
tx_udp(ifp, s->mac, ifp->ip, c->loc.port, rem_ip, c->rem.port, buf, len);
} else {
size_t max_headers_len = 14 + 24 /* max IP */ + 60 /* max TCP */;
if (len + max_headers_len > ifp->tx.len)
len = ifp->tx.len - max_headers_len;
if (tx_tcp(ifp, s->mac, rem_ip, TH_PUSH | TH_ACK, c->loc.port, c->rem.port,
mg_htonl(s->seq), mg_htonl(s->ack), buf, len) > 0) {
s->seq += (uint32_t) len;
Expand Down Expand Up @@ -1018,11 +1020,13 @@ bool mg_open_listener(struct mg_connection *c, const char *url) {
}

static void write_conn(struct mg_connection *c) {
long len = c->is_tls ? mg_tls_send(c, c->send.buf, c->send.len)
: mg_io_send(c, c->send.buf, c->send.len);
if (len > 0) {
mg_iobuf_del(&c->send, 0, (size_t) len);
mg_call(c, MG_EV_WRITE, &len);
while (c->send.len) {
long len = c->is_tls ? mg_tls_send(c, c->send.buf, c->send.len)
: mg_io_send(c, c->send.buf, c->send.len);
if (len > 0) {
mg_iobuf_del(&c->send, 0, (size_t) len);
mg_call(c, MG_EV_WRITE, &len);
}
}
}

Expand Down
49 changes: 46 additions & 3 deletions test/mip_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

static int s_num_tests = 0;
static bool s_sent_fragment = 0;
static int s_seg_sent = 0;

#define ASSERT(expr) \
do { \
Expand Down Expand Up @@ -35,6 +36,10 @@ static void ph(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
}

static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
(void) c, (void) ev, (void) ev_data, (void) fn_data;
}

static void frag_recv_fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
if (ev == MG_EV_ERROR) {
if (s_sent_fragment) {
ASSERT(strcmp((char*) ev_data, "Received fragmented packet") == 0);
Expand All @@ -43,6 +48,22 @@ static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
(void) c, (void) ev_data, (void) fn_data;
}

static void frag_send_fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
static bool s_sent;
static int s_seg_sizes[] = {416, 416, 368};
if (ev == MG_EV_POLL) {
if (!s_sent) {
c->mtu = 500;
c->send.len = 1200; // setting TCP payload size
s_sent = true;
}
} else if (ev == MG_EV_WRITE) {
// Checking TCP segment sizes (ev_data points to the TCP payload length)
ASSERT(*(int *) ev_data == s_seg_sizes[s_seg_sent++]);
}
(void) c, (void) ev_data, (void) fn_data;
}

static void test_poll(void) {
int count = 0, i;
struct mg_mgr mgr;
Expand Down Expand Up @@ -217,7 +238,7 @@ static void test_retransmit(void) {
mg_tcpip_free(&mif);
}

static void test_fragmentation(void) {
static void test_frag_recv_path(void) {
struct mg_mgr mgr;
struct eth e;
struct ip ip;
Expand All @@ -232,7 +253,7 @@ static void test_fragmentation(void) {
mif.driver = &driver;
mif.driver_data = &s_driver_data;
mg_tcpip_init(&mgr, &mif);
mg_http_listen(&mgr, "http://0.0.0.0:0", fn, NULL);
mg_http_listen(&mgr, "http://0.0.0.0:0", frag_recv_fn, NULL);
mgr.conns->pfn = NULL;
mg_mgr_poll(&mgr, 0);

Expand Down Expand Up @@ -261,11 +282,33 @@ static void test_fragmentation(void) {
mg_tcpip_free(&mif);
}

static void test_frag_send_path(void) {
struct mg_mgr mgr;
struct mg_tcpip_driver driver;
struct mg_tcpip_if mif;

mg_mgr_init(&mgr);
memset(&mif, 0, sizeof(mif));
memset(&s_driver_data, 0, sizeof(struct driver_data));
driver.init = NULL, driver.tx = if_tx, driver.up = if_up, driver.rx = if_rx;
mif.driver = &driver;
mif.driver_data = &s_driver_data;
mg_tcpip_init(&mgr, &mif);
mg_http_listen(&mgr, "http://0.0.0.0:0", frag_send_fn, NULL);
mgr.conns->pfn = NULL;
mg_mgr_poll(&mgr, 0);
ASSERT(s_seg_sent == 3);
s_driver_data.len = 0;
mg_mgr_free(&mgr);
mg_tcpip_free(&mif);
}

int main(void) {
test_statechange();
test_poll();
test_retransmit();
test_fragmentation();
test_frag_recv_path();
test_frag_send_path();
printf("SUCCESS. Total tests: %d\n", s_num_tests);
return 0;
}

0 comments on commit 1274ec4

Please sign in to comment.