From 0ec2a0252b74baf43f827d336d20ed5cd6f9c1dd Mon Sep 17 00:00:00 2001 From: "Sergio R. Caprile" Date: Tue, 10 Nov 2026 11:07:19 -0300 Subject: [PATCH] tighten IP fragment behavior --- mongoose.c | 38 +++++++++++++++++++++++--------------- src/net_builtin.c | 38 +++++++++++++++++++++++--------------- test/mip_test.c | 2 +- 3 files changed, 47 insertions(+), 31 deletions(-) diff --git a/mongoose.c b/mongoose.c index 3260d2ec..c0b42f62 100644 --- a/mongoose.c +++ b/mongoose.c @@ -11515,12 +11515,16 @@ static void rx_ip(struct mg_tcpip_if *ifp, struct pkt *pkt) { mkpay(pkt, (uint32_t *) pkt->ip + ihl); // account for opts if (!ipcsum_ok(pkt->ip)) return; frag = mg_ntohs(pkt->ip->frag); - if (frag & IP_MORE_FRAGS_MSK || frag & IP_FRAG_OFFSET_MSK) { - struct mg_connection *c; - if (pkt->ip->proto == 17) pkt->udp = (struct udp *) (pkt->pay.buf); - if (pkt->ip->proto == 6) pkt->tcp = (struct tcp *) (pkt->pay.buf); - c = getpeer(ifp->mgr, pkt, false); - if (c) proto_error(c, false, "Received fragmented packet"); + if (frag & IP_FRAG_OFFSET_MSK) { // Non-first fragment: ignore + } else if (frag & IP_MORE_FRAGS_MSK) { // First fragment: find and close + if (pkt->ip->proto == 17 && pkt->pay.len >= sizeof(*pkt->udp)) + pkt->udp = (struct udp *) pkt->pay.buf; + if (pkt->ip->proto == 6 && pkt->pay.len >= sizeof(*pkt->tcp)) + pkt->tcp = (struct tcp *) pkt->pay.buf; + if (pkt->udp || pkt->tcp) { + struct mg_connection *c = getpeer(ifp->mgr, pkt, false); + if (c) proto_error(c, false, "Received fragmented packet"); + } // else ignore // else ignore } else if (pkt->ip->proto == 1) { pkt->icmp = (struct icmp *) (pkt->pay.buf); if (pkt->pay.len < sizeof(*pkt->icmp)) return; @@ -11596,15 +11600,19 @@ static void rx_ip6(struct mg_tcpip_if *ifp, struct pkt *pkt) { len += hlen; nhdr += hlen; break; - case 44: // Fragment 4.5 - { - struct mg_connection *c; - if (((uint32_t) len + 2) > plen) return; // nhdr[0, 1]; malformed - if (nhdr[0] == 17) pkt->udp = (struct udp *) (pkt->pay.buf); - if (nhdr[0] == 6) pkt->tcp = (struct tcp *) (pkt->pay.buf); - c = getpeer(ifp->mgr, pkt, false); - if (c) proto_error(c, false, "Received fragmented packet"); - } + case 44: // Fragment 4.5; no reassembly + if ((uint32_t) len + 8 > plen) return; // Truncated (frag hdr = 8) + if ((nhdr[2] | (nhdr[3] & 0xf8)) == 0) { // First fragment + size_t tlen = (size_t)(plen - len - 8); + if (nhdr[0] == 17 && tlen >= sizeof(*pkt->udp)) + pkt->udp = (struct udp *) (nhdr + 8); + if (nhdr[0] == 6 && tlen >= sizeof(*pkt->tcp)) + pkt->tcp = (struct tcp *) (nhdr + 8); + if (pkt->udp || pkt->tcp) { + struct mg_connection *c = getpeer(ifp->mgr, pkt, false); + if (c) proto_error(c, false, "Received fragmented packet"); + } // else ignore + } // else ignore return; case 59: // No Next Header 4.7 return; diff --git a/src/net_builtin.c b/src/net_builtin.c index edae6918..9e1a6b73 100644 --- a/src/net_builtin.c +++ b/src/net_builtin.c @@ -1867,12 +1867,16 @@ static void rx_ip(struct mg_tcpip_if *ifp, struct pkt *pkt) { mkpay(pkt, (uint32_t *) pkt->ip + ihl); // account for opts if (!ipcsum_ok(pkt->ip)) return; frag = mg_ntohs(pkt->ip->frag); - if (frag & IP_MORE_FRAGS_MSK || frag & IP_FRAG_OFFSET_MSK) { - struct mg_connection *c; - if (pkt->ip->proto == 17) pkt->udp = (struct udp *) (pkt->pay.buf); - if (pkt->ip->proto == 6) pkt->tcp = (struct tcp *) (pkt->pay.buf); - c = getpeer(ifp->mgr, pkt, false); - if (c) proto_error(c, false, "Received fragmented packet"); + if (frag & IP_FRAG_OFFSET_MSK) { // Non-first fragment: ignore + } else if (frag & IP_MORE_FRAGS_MSK) { // First fragment: find and close + if (pkt->ip->proto == 17 && pkt->pay.len >= sizeof(*pkt->udp)) + pkt->udp = (struct udp *) pkt->pay.buf; + if (pkt->ip->proto == 6 && pkt->pay.len >= sizeof(*pkt->tcp)) + pkt->tcp = (struct tcp *) pkt->pay.buf; + if (pkt->udp || pkt->tcp) { + struct mg_connection *c = getpeer(ifp->mgr, pkt, false); + if (c) proto_error(c, false, "Received fragmented packet"); + } // else ignore // else ignore } else if (pkt->ip->proto == 1) { pkt->icmp = (struct icmp *) (pkt->pay.buf); if (pkt->pay.len < sizeof(*pkt->icmp)) return; @@ -1948,15 +1952,19 @@ static void rx_ip6(struct mg_tcpip_if *ifp, struct pkt *pkt) { len += hlen; nhdr += hlen; break; - case 44: // Fragment 4.5 - { - struct mg_connection *c; - if (((uint32_t) len + 2) > plen) return; // nhdr[0, 1]; malformed - if (nhdr[0] == 17) pkt->udp = (struct udp *) (pkt->pay.buf); - if (nhdr[0] == 6) pkt->tcp = (struct tcp *) (pkt->pay.buf); - c = getpeer(ifp->mgr, pkt, false); - if (c) proto_error(c, false, "Received fragmented packet"); - } + case 44: // Fragment 4.5; no reassembly + if ((uint32_t) len + 8 > plen) return; // Truncated (frag hdr = 8) + if ((nhdr[2] | (nhdr[3] & 0xf8)) == 0) { // First fragment + size_t tlen = (size_t)(plen - len - 8); + if (nhdr[0] == 17 && tlen >= sizeof(*pkt->udp)) + pkt->udp = (struct udp *) (nhdr + 8); + if (nhdr[0] == 6 && tlen >= sizeof(*pkt->tcp)) + pkt->tcp = (struct tcp *) (nhdr + 8); + if (pkt->udp || pkt->tcp) { + struct mg_connection *c = getpeer(ifp->mgr, pkt, false); + if (c) proto_error(c, false, "Received fragmented packet"); + } // else ignore + } // else ignore return; case 59: // No Next Header 4.7 return; diff --git a/test/mip_test.c b/test/mip_test.c index 75656867..1b882f99 100644 --- a/test/mip_test.c +++ b/test/mip_test.c @@ -1257,7 +1257,7 @@ static void test_frag_recv_path(void) { init_tcp_handshake(&e, &ipp, &mgr); // starts with seq_no=1000, ackno=2 // send fragmented TCP packet - ip.frag |= IP_MORE_FRAGS_MSK; // setting More Fragments bit to 1 + ip.frag = mg_htons(IP_MORE_FRAGS_MSK); // setting More Fragments bit to 1 create_tcp_simpleseg(&e, &ipp, 1001, 2, TH_PUSH | TH_ACK, 1000); s_sent_fragment = 1; // "enable" fn mg_mgr_poll(&mgr, 0); // call it (process fake frag IP)