-
Notifications
You must be signed in to change notification settings - Fork 2.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
IP fragmentation send path: added TCP/UDP packet splitting to fit within MTU #2380
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The approach seems to look fine for TCP.
However, for UDP, it will only work if the user calls mg_print*() functions that call mg_io_send(). Otherwise, calls to mg_send() will bypass your strategy and be sent without being fragmented. Please write a test that proves or refutes my point and, if I'm right, then solve it.
Lines 1075 to 1088 in d23664a
bool mg_send(struct mg_connection *c, const void *buf, size_t len) { | |
struct mg_tcpip_if *ifp = (struct mg_tcpip_if *) c->mgr->priv; | |
bool res = false; | |
uint32_t rem_ip; | |
memcpy(&rem_ip, c->rem.ip, sizeof(uint32_t)); | |
if (ifp->ip == 0 || ifp->state != MG_TCPIP_STATE_READY) { | |
mg_error(c, "net down"); | |
} else if (c->is_udp) { | |
struct connstate *s = (struct connstate *) (c + 1); | |
tx_udp(ifp, s->mac, ifp->ip, c->loc.port, rem_ip, c->rem.port, buf, len); | |
res = true; | |
} else { | |
res = mg_iobuf_add(&c->send, c->send.len, buf, len); | |
} |
In case we would need to mg_io_add for UDP, I wouldn't want to delay UDP packets that would fit the MTU, so we should not just buffer, as buffered datagrams would be sent on next poll, not now. For UDP, whatever fits the MTU we should try to send it now. (*)
If I'm right, this fragmentation stuff will have the side effect that we'll now be able to "send UDP datagrams larger than an IP datagram", which will cause them to the splitted into several datagrams and that IMO wouldn't be correct. Please check that, too.
(*): How would this interact with an ARP lookup ? Now our first UDP that requires an ARP lookup is lost, can we make use of this in advance and save the datagram for later ? (If the ISO police OSI enforcement unit comes for you, just tell them I told you to do it)
And now that I think of it again... We are not actually sending fragmented IP, are we ? So if a user requests a 1500-byte UDP datagram and our MTU is 750, we'll actually send two UDP datagrams breaking the very logic foundation of UDP datagrams. I think we should either reject this (force the users to break their messages) and (or at least) log it. Otherwise, we should send IP fragments but we will not receive our own data and that looks bad...
Our current logic is to follow down the path and the Ethernet layer will drop too-long packets.
I think this is turning into a long rambling, if that is the case, let's please follow up at the chat.
9506aea
to
608ef14
Compare
src/net.h
Outdated
@@ -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 | |||
#define MTU_DEFAULT_VALUE 1500 | |||
uint16_t mtu; // MTU for this connection |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this should be moved to the tcp/ip stack implementation. Specifically, to the struct connstate
-
Lines 16 to 17 in d23664a
struct connstate { | |
uint32_t seq, ack; // TCP seq/ack counters |
MTU_DEFAULT_VALUE
I'd rename to MG_DEFAULT_MTU
, to keep our defines prefixed with MG_
in order to avoid conflicts with customer code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I second, missed that, that is ifp-> stuff not c-> ...
src/net_builtin.c
Outdated
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) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, mtu
must not be too small - it should be bigger that the size of headers. Shall we check that mtu
is not less than a minimum possible here, or somewhere else?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please move mtu
setting to the struct connstate
08bbd07
to
6410d85
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please address those nitty comments of mine.
src/net_builtin.c
Outdated
size_t max_udp_header_len = 32; | ||
if (s->mtu < max_udp_header_len) { | ||
s->mtu = MIP_MTU_DEFAULT; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See above (RFC-791)
src/net_builtin.c
Outdated
if (s->mtu < max_headers_len - eth_h_len) { | ||
s->mtu = MIP_MTU_DEFAULT; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This doesn't seem correct. The user is trying to lower the MTU and we go full throttle to 1500.
I think we should limit to the smallest allowed, IIUC it is 68 bytes per RFC-791: "Every internet module must be able to forward a datagram of 68 octets without further fragmentation"
However, for TCP, we should perhaps limit to the max header value.
src/net_builtin.c
Outdated
if (len + max_headers_len - eth_h_len > s->mtu) { | ||
len = s->mtu - max_headers_len + eth_h_len; | ||
if (c->is_udp) | ||
MG_ERROR(("UDP datagram exceeds MTU")); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please tell the datagram has been truncated, something like
"Truncated UDP datagram exceeding MTU"
src/net_builtin.c
Outdated
} | ||
if (len + max_udp_header_len > s->mtu) { | ||
len = s->mtu - max_udp_header_len; | ||
MG_ERROR(("UDP datagram exceeds MTU")); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same comment as above (truncation)
6410d85
to
220c524
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changes bring more changes...
src/net_builtin.c
Outdated
struct connstate *s = (struct connstate *) (c + 1); | ||
size_t min_mtu = 68 /* RFC-791 */; | ||
if (ifp->mtu < min_mtu) { | ||
MG_ERROR(("MTU is lower than minimum possible value. Setting it to %d.", min_mtu)); | ||
ifp->mtu = (uint16_t) min_mtu; | ||
} | ||
if (len + max_udp_header_len > ifp->mtu) { | ||
len = ifp->mtu - max_udp_header_len; | ||
MG_ERROR(("UDP datagram exceeds MTU. Truncating it.")); | ||
} | ||
tx_udp(ifp, s->mac, ifp->ip, c->loc.port, rem_ip, c->rem.port, buf, len); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now that the MTU is checked against ifp->mtu
I think it makes sense moving the UDP case into tx_udp()
. This also avoids duplicated code and texts.
src/net_builtin.c
Outdated
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); | ||
size_t min_mtu = c->is_udp ? 68 /* RFC-791 */ : max_headers_len - eth_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 (ifp->mtu < min_mtu) { | ||
MG_ERROR(("MTU is lower than minimum possible value. Setting it to %d.", min_mtu)); | ||
ifp->mtu = (uint16_t) min_mtu; | ||
} | ||
if (len + max_headers_len - eth_h_len > ifp->mtu) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same comment (mg_send())
220c524
to
264026a
Compare
No description provided.