Skip to content

Commit 0e63510

Browse files
committed
Fix IP opts
Refactor payload handling Refactor UDP port unreachable Add UDP unit tests Introduce basic IPv6 handling
1 parent 26260a1 commit 0e63510

File tree

3 files changed

+392
-159
lines changed

3 files changed

+392
-159
lines changed

mongoose.c

Lines changed: 154 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -4205,7 +4205,7 @@ struct eth {
42054205
struct ip {
42064206
uint8_t ver; // Version
42074207
uint8_t tos; // Unused
4208-
uint16_t len; // Length
4208+
uint16_t len; // Datagram length
42094209
uint16_t id; // Unused
42104210
uint16_t frag; // Fragmentation
42114211
#define IP_FRAG_OFFSET_MSK 0x1fff
@@ -4218,13 +4218,13 @@ struct ip {
42184218
};
42194219

42204220
struct ip6 {
4221-
uint8_t ver; // Version
4222-
uint8_t opts[3]; // Options
4223-
uint16_t len; // Length
4224-
uint8_t proto; // Upper level protocol
4225-
uint8_t ttl; // Time to live
4226-
uint8_t src[16]; // Source IP
4227-
uint8_t dst[16]; // Destination IP
4221+
uint8_t ver; // Version
4222+
uint8_t label[3]; // Flow label
4223+
uint16_t plen; // Payload length
4224+
uint8_t next; // Upper level protocol
4225+
uint8_t hops; // Hop limit
4226+
uint8_t src[16]; // Source IP
4227+
uint8_t dst[16]; // Destination IP
42284228
};
42294229

42304230
struct icmp {
@@ -4233,6 +4233,12 @@ struct icmp {
42334233
uint16_t csum;
42344234
};
42354235

4236+
struct icmp6 {
4237+
uint8_t type;
4238+
uint8_t code;
4239+
uint16_t csum;
4240+
};
4241+
42364242
struct arp {
42374243
uint16_t fmt; // Format of hardware address
42384244
uint16_t pro; // Format of protocol address
@@ -4282,6 +4288,14 @@ struct dhcp {
42824288
uint8_t options[30 + sizeof(((struct mg_tcpip_if *) 0)->dhcp_name)];
42834289
};
42844290

4291+
struct dhcp6 {
4292+
union {
4293+
uint8_t type;
4294+
uint32_t xid;
4295+
};
4296+
uint8_t options[30 + sizeof(((struct mg_tcpip_if *) 0)->dhcp_name)];
4297+
};
4298+
42854299
#pragma pack(pop)
42864300

42874301
struct pkt {
@@ -4293,9 +4307,11 @@ struct pkt {
42934307
struct ip *ip;
42944308
struct ip6 *ip6;
42954309
struct icmp *icmp;
4310+
struct icmp6 *icmp6;
42964311
struct tcp *tcp;
42974312
struct udp *udp;
42984313
struct dhcp *dhcp;
4314+
struct dhcp6 *dhcp6;
42994315
};
43004316

43014317
static void mg_tcpip_call(struct mg_tcpip_if *ifp, int ev, void *ev_data) {
@@ -4306,7 +4322,7 @@ static void send_syn(struct mg_connection *c);
43064322

43074323
static void mkpay(struct pkt *pkt, void *p) {
43084324
pkt->pay =
4309-
mg_str_n((char *) p, (size_t) (&pkt->raw.buf[pkt->raw.len] - (char *) p));
4325+
mg_str_n((char *) p, (size_t) (&pkt->pay.buf[pkt->pay.len] - (char *) p));
43104326
}
43114327

43124328
static uint32_t csumup(uint32_t sum, const void *buf, size_t len) {
@@ -4569,7 +4585,7 @@ static void rx_dhcp_client(struct mg_tcpip_if *ifp, struct pkt *pkt) {
45694585
uint8_t msgtype = 0, state = ifp->state;
45704586
// perform size check first, then access fields
45714587
uint8_t *p = pkt->dhcp->options,
4572-
*end = (uint8_t *) &pkt->raw.buf[pkt->raw.len];
4588+
*end = (uint8_t *) &pkt->pay.buf[pkt->pay.len];
45734589
if (end < (uint8_t *) (pkt->dhcp + 1)) return;
45744590
if (memcmp(&pkt->dhcp->xid, ifp->mac + 2, sizeof(pkt->dhcp->xid))) return;
45754591
while (p + 1 < end && p[0] != 255) { // Parse options RFC-1533 #9
@@ -4629,7 +4645,7 @@ static void rx_dhcp_client(struct mg_tcpip_if *ifp, struct pkt *pkt) {
46294645
// Simple DHCP server that assigns a next IP address: ifp->ip + 1
46304646
static void rx_dhcp_server(struct mg_tcpip_if *ifp, struct pkt *pkt) {
46314647
uint8_t op = 0, *p = pkt->dhcp->options,
4632-
*end = (uint8_t *) &pkt->raw.buf[pkt->raw.len];
4648+
*end = (uint8_t *) &pkt->pay.buf[pkt->pay.len];
46334649
// struct dhcp *req = pkt->dhcp;
46344650
struct dhcp res = {2, 1, 6, 0, 0, 0, 0, 0, 0, 0, 0, {0}, 0, {0}};
46354651
if (end < (uint8_t *) (pkt->dhcp + 1)) return;
@@ -4666,26 +4682,25 @@ static void rx_dhcp_server(struct mg_tcpip_if *ifp, struct pkt *pkt) {
46664682
}
46674683
}
46684684

4669-
static void rx_udp(struct mg_tcpip_if *ifp, struct pkt *pkt) {
4685+
static bool rx_udp(struct mg_tcpip_if *ifp, struct pkt *pkt) {
46704686
struct mg_connection *c = getpeer(ifp->mgr, pkt, true);
4671-
if (c == NULL) {
4672-
// No UDP listener on this port. Should send ICMP, but keep silent.
4687+
struct connstate *s;
4688+
if (c == NULL) return false; // No UDP listener on this port
4689+
s = (struct connstate *) (c + 1);
4690+
c->rem.port = pkt->udp->sport;
4691+
memcpy(c->rem.ip, &pkt->ip->src, sizeof(uint32_t));
4692+
memcpy(s->mac, pkt->eth->src, sizeof(s->mac));
4693+
if (c->recv.len >= MG_MAX_RECV_SIZE) {
4694+
mg_error(c, "max_recv_buf_size reached");
4695+
} else if (c->recv.size - c->recv.len < pkt->pay.len &&
4696+
!mg_iobuf_resize(&c->recv, c->recv.len + pkt->pay.len)) {
4697+
mg_error(c, "oom");
46734698
} else {
4674-
struct connstate *s = (struct connstate *) (c + 1);
4675-
c->rem.port = pkt->udp->sport;
4676-
memcpy(c->rem.ip, &pkt->ip->src, sizeof(uint32_t));
4677-
memcpy(s->mac, pkt->eth->src, sizeof(s->mac));
4678-
if (c->recv.len >= MG_MAX_RECV_SIZE) {
4679-
mg_error(c, "max_recv_buf_size reached");
4680-
} else if (c->recv.size - c->recv.len < pkt->pay.len &&
4681-
!mg_iobuf_resize(&c->recv, c->recv.len + pkt->pay.len)) {
4682-
mg_error(c, "oom");
4683-
} else {
4684-
memcpy(&c->recv.buf[c->recv.len], pkt->pay.buf, pkt->pay.len);
4685-
c->recv.len += pkt->pay.len;
4686-
mg_call(c, MG_EV_READ, &pkt->pay.len);
4687-
}
4699+
memcpy(&c->recv.buf[c->recv.len], pkt->pay.buf, pkt->pay.len);
4700+
c->recv.len += pkt->pay.len;
4701+
mg_call(c, MG_EV_READ, &pkt->pay.len);
46884702
}
4703+
return true;
46894704
}
46904705

46914706
static size_t tx_tcp(struct mg_tcpip_if *ifp, uint8_t *dst_mac, uint32_t dst_ip,
@@ -4725,7 +4740,6 @@ static size_t tx_tcp(struct mg_tcpip_if *ifp, uint8_t *dst_mac, uint32_t dst_ip,
47254740
MG_VERBOSE(("TCP %M:%hu -> %M:%hu fl %x len %u", mg_print_ip4, &ip->src,
47264741
mg_ntohs(tcp->sport), mg_print_ip4, &ip->dst,
47274742
mg_ntohs(tcp->dport), tcp->flags, len));
4728-
// mg_hexdump(ifp->tx.buf, PDIFF(ifp->tx.buf, tcp + 1) + len);
47294743
return ether_output(ifp, PDIFF(ifp->tx.buf, tcp + 1) + len);
47304744
}
47314745

@@ -5081,23 +5095,38 @@ static void rx_tcp(struct mg_tcpip_if *ifp, struct pkt *pkt) {
50815095
}
50825096

50835097
static void rx_ip(struct mg_tcpip_if *ifp, struct pkt *pkt) {
5084-
uint16_t frag = mg_ntohs(pkt->ip->frag);
5098+
uint8_t ihl;
5099+
uint16_t frag, len;
5100+
if (pkt->pay.len < sizeof(*pkt->ip)) return; // Truncated
5101+
if ((pkt->ip->ver >> 4) != 4) return; // Not IP
5102+
ihl = pkt->ip->ver & 0x0F;
5103+
if (ihl < 5) return; // bad IHL
5104+
if (pkt->pay.len < (ihl * 4)) return; // Truncated / malformed
5105+
// There can be link padding, take length from IP header
5106+
len = mg_ntohs(pkt->ip->len); // IP datagram length
5107+
if (len < (ihl * 4) || len > pkt->pay.len) return; // malformed
5108+
pkt->pay.len = len; // strip padding
5109+
mkpay(pkt, (uint32_t *) pkt->ip + ihl); // account for opts
5110+
frag = mg_ntohs(pkt->ip->frag);
50855111
if (frag & IP_MORE_FRAGS_MSK || frag & IP_FRAG_OFFSET_MSK) {
50865112
struct mg_connection *c;
5087-
if (pkt->ip->proto == 17) pkt->udp = (struct udp *) (pkt->ip + 1);
5088-
if (pkt->ip->proto == 6) pkt->tcp = (struct tcp *) (pkt->ip + 1);
5113+
if (pkt->ip->proto == 17) pkt->udp = (struct udp *) (pkt->pay.buf);
5114+
if (pkt->ip->proto == 6) pkt->tcp = (struct tcp *) (pkt->pay.buf);
50895115
c = getpeer(ifp->mgr, pkt, false);
50905116
if (c) mg_error(c, "Received fragmented packet");
50915117
} else if (pkt->ip->proto == 1) {
5092-
pkt->icmp = (struct icmp *) (pkt->ip + 1);
5118+
pkt->icmp = (struct icmp *) (pkt->pay.buf);
50935119
if (pkt->pay.len < sizeof(*pkt->icmp)) return;
50945120
mkpay(pkt, pkt->icmp + 1);
50955121
rx_icmp(ifp, pkt);
50965122
} else if (pkt->ip->proto == 17) {
5097-
pkt->udp = (struct udp *) (pkt->ip + 1);
5098-
if (pkt->pay.len < sizeof(*pkt->udp)) return;
5123+
pkt->udp = (struct udp *) (pkt->pay.buf);
5124+
if (pkt->pay.len < sizeof(*pkt->udp)) return; // truncated
5125+
// Take length from UDP header
5126+
len = mg_ntohs(pkt->udp->len); // UDP datagram length
5127+
if (len < sizeof(*pkt->udp) || len > pkt->pay.len) return; // malformed
5128+
pkt->pay.len = len; // strip excess data
50995129
mkpay(pkt, pkt->udp + 1);
5100-
if (pkt->udp->len < pkt->pay.len) pkt->pay.len = pkt->udp->len;
51015130
MG_VERBOSE(("UDP %M:%hu -> %M:%hu len %u", mg_print_ip4, &pkt->ip->src,
51025131
mg_ntohs(pkt->udp->sport), mg_print_ip4, &pkt->ip->dst,
51035132
mg_ntohs(pkt->udp->dport), (int) pkt->pay.len));
@@ -5109,47 +5138,108 @@ static void rx_ip(struct mg_tcpip_if *ifp, struct pkt *pkt) {
51095138
pkt->dhcp = (struct dhcp *) (pkt->udp + 1);
51105139
mkpay(pkt, pkt->dhcp + 1);
51115140
rx_dhcp_server(ifp, pkt);
5112-
} else {
5113-
rx_udp(ifp, pkt);
5141+
} else if (!rx_udp(ifp, pkt)) {
5142+
// Should send ICMP Destination Unreachable for unicasts, but keep silent
51145143
}
51155144
} else if (pkt->ip->proto == 6) {
5116-
uint16_t iplen, off;
5117-
pkt->tcp = (struct tcp *) (pkt->ip + 1);
5145+
uint8_t off;
5146+
pkt->tcp = (struct tcp *) (pkt->pay.buf);
51185147
if (pkt->pay.len < sizeof(*pkt->tcp)) return;
5119-
mkpay(pkt, (uint32_t *) pkt->tcp + (pkt->tcp->off >> 4)); // may have opts
5120-
iplen = mg_ntohs(pkt->ip->len);
5121-
off = (uint16_t) (sizeof(*pkt->ip) + ((pkt->tcp->off >> 4) * 4U));
5122-
if (iplen >= off) pkt->pay.len = (size_t) (iplen - off);
5148+
off = pkt->tcp->off >> 4; // account for opts
5149+
if (pkt->pay.len < (4 * off)) return;
5150+
mkpay(pkt, (uint32_t *) pkt->tcp + off);
51235151
MG_VERBOSE(("TCP %M:%hu -> %M:%hu len %u", mg_print_ip4, &pkt->ip->src,
51245152
mg_ntohs(pkt->tcp->sport), mg_print_ip4, &pkt->ip->dst,
51255153
mg_ntohs(pkt->tcp->dport), (int) pkt->pay.len));
51265154
rx_tcp(ifp, pkt);
5155+
} else {
5156+
MG_DEBUG(("Unknown IP proto %x", mg_htons(pkt->ip->proto)));
5157+
if (mg_log_level >= MG_LL_VERBOSE) mg_hexdump(pkt->ip, pkt->pay.len >= 32 ? 32 : pkt->pay.len);
5158+
}
5159+
}
5160+
5161+
static bool ip6_handle_opt(struct ip6 *h) {
5162+
switch(h->next) {
5163+
case 0:
5164+
case 43:
5165+
case 44:
5166+
case 50:
5167+
case 51:
5168+
case 60:
5169+
case 135:
5170+
case 139:
5171+
case 140:
5172+
case 253:
5173+
case 254:
5174+
MG_INFO(("IPv6 extension header %d", (int) h->next));
5175+
break;
5176+
default:
5177+
return false;
51275178
}
5179+
return true;
51285180
}
51295181

51305182
static void rx_ip6(struct mg_tcpip_if *ifp, struct pkt *pkt) {
5131-
// MG_DEBUG(("IP %d", (int) len));
5132-
if (pkt->ip6->proto == 1 || pkt->ip6->proto == 58) {
5133-
pkt->icmp = (struct icmp *) (pkt->ip6 + 1);
5134-
if (pkt->pay.len < sizeof(*pkt->icmp)) return;
5135-
mkpay(pkt, pkt->icmp + 1);
5136-
rx_icmp(ifp, pkt);
5137-
} else if (pkt->ip6->proto == 17) {
5138-
pkt->udp = (struct udp *) (pkt->ip6 + 1);
5183+
struct ip6 *hdr;
5184+
uint16_t len;
5185+
if (pkt->pay.len < sizeof(*pkt->ip6)) return; // Truncated
5186+
if ((pkt->ip6->ver >> 4) != 0x6) return; // Not IPv6
5187+
hdr = pkt->ip6;
5188+
while (ip6_handle_opt(hdr)) ++hdr;
5189+
// There can be link padding, take payload length from IPv6 last header
5190+
len = mg_ntohs(hdr->plen); // IPv6 datagram reported payload length
5191+
if ((len + (size_t) hdr - (size_t) pkt->ip6) > pkt->pay.len) return;
5192+
pkt->pay.buf = (char *)(hdr + 1);
5193+
pkt->pay.len = len; // strip padding
5194+
if (hdr->next == 58) {
5195+
pkt->icmp6 = (struct icmp6 *) (pkt->pay.buf);
5196+
if (pkt->pay.len < sizeof(*pkt->icmp6)) return;
5197+
mkpay(pkt, pkt->icmp6 + 1);
5198+
MG_DEBUG(("ICMPv6 %M -> %M len %u", mg_print_ip6, &pkt->ip6->src,
5199+
mg_print_ip6, &pkt->ip6->dst,
5200+
(int) pkt->pay.len));
5201+
// rx_icmp6(ifp, pkt);
5202+
} else if (hdr->next == 17) {
5203+
pkt->udp = (struct udp *) (pkt->pay.buf);
51395204
if (pkt->pay.len < sizeof(*pkt->udp)) return;
5140-
// MG_DEBUG((" UDP %u %u -> %u", len, mg_htons(udp->sport),
5141-
// mg_htons(udp->dport)));
51425205
mkpay(pkt, pkt->udp + 1);
5206+
MG_DEBUG(("UDP %M:%hu -> %M:%hu len %u", mg_print_ip6, &pkt->ip6->src,
5207+
mg_ntohs(pkt->udp->sport), mg_print_ip6, &pkt->ip6->dst,
5208+
mg_ntohs(pkt->udp->dport), (int) pkt->pay.len));
5209+
if (ifp->enable_dhcp_client && pkt->udp->dport == mg_htons(546)) {
5210+
pkt->dhcp6 = (struct dhcp6 *) (pkt->udp + 1);
5211+
mkpay(pkt, pkt->dhcp6 + 1);
5212+
// rx_dhcp6_client(ifp, pkt);
5213+
} else if (ifp->enable_dhcp_server && pkt->udp->dport == mg_htons(547)) {
5214+
pkt->dhcp6 = (struct dhcp6 *) (pkt->udp + 1);
5215+
mkpay(pkt, pkt->dhcp6 + 1);
5216+
// rx_dhcp6_server(ifp, pkt);
5217+
} else if (!rx_udp(ifp, pkt)) {
5218+
// Should send ICMPv6 Destination Unreachable for unicasts, keep silent
5219+
}
5220+
} else if (hdr->next == 6) {
5221+
uint8_t off;
5222+
pkt->tcp = (struct tcp *) (pkt->pay.buf);
5223+
if (pkt->pay.len < sizeof(*pkt->tcp)) return;
5224+
off = pkt->tcp->off >> 4; // account for opts
5225+
if (pkt->pay.len < sizeof(*pkt->tcp) + 4 * off) return;
5226+
mkpay(pkt, (uint32_t *) pkt->tcp + off);
5227+
MG_DEBUG(("TCP %M:%hu -> %M:%hu len %u", mg_print_ip6, &pkt->ip6->src,
5228+
mg_ntohs(pkt->tcp->sport), mg_print_ip6, &pkt->ip6->dst,
5229+
mg_ntohs(pkt->tcp->dport), (int) pkt->pay.len));
5230+
rx_tcp(ifp, pkt);
5231+
} else {
5232+
MG_DEBUG(("Unknown IPv6 next hdr %x", mg_htons(hdr->next)));
5233+
if (mg_log_level >= MG_LL_VERBOSE) mg_hexdump(pkt->ip6, pkt->pay.len >= 32 ? 32 : pkt->pay.len);
51435234
}
51445235
}
51455236

51465237
static void mg_tcpip_rx(struct mg_tcpip_if *ifp, void *buf, size_t len) {
51475238
struct pkt pkt;
51485239
memset(&pkt, 0, sizeof(pkt));
5149-
pkt.raw.buf = (char *) buf;
5150-
pkt.raw.len = len;
5151-
pkt.eth = (struct eth *) buf;
5152-
// mg_hexdump(buf, len > 16 ? 16: len);
5240+
pkt.pay.buf = pkt.raw.buf = (char *) buf;
5241+
pkt.pay.len = pkt.raw.len = len; // payload = raw
5242+
pkt.eth = (struct eth *) buf; // Ethernet = raw
51535243
if (pkt.raw.len < sizeof(*pkt.eth)) return; // Truncated - runt?
51545244
if (ifp->enable_mac_check &&
51555245
memcmp(pkt.eth->dst, ifp->mac, sizeof(pkt.eth->dst)) != 0 &&
@@ -5160,28 +5250,19 @@ static void mg_tcpip_rx(struct mg_tcpip_if *ifp, void *buf, size_t len) {
51605250
len -= 4; // TODO(scaprile): check on bigendian
51615251
crc = mg_crc32(0, (const char *) buf, len);
51625252
if (memcmp((void *) ((size_t) buf + len), &crc, sizeof(crc))) return;
5253+
pkt.pay.len = len;
51635254
}
5255+
mkpay(&pkt, pkt.eth + 1);
51645256
if (pkt.eth->type == mg_htons(0x806)) {
5165-
pkt.arp = (struct arp *) (pkt.eth + 1);
5166-
if (sizeof(*pkt.eth) + sizeof(*pkt.arp) > pkt.raw.len) return; // Truncated
5257+
pkt.arp = (struct arp *) (pkt.pay.buf);
5258+
if (sizeof(*pkt.eth) + sizeof(*pkt.arp) > pkt.pay.len) return; // Truncated
51675259
mg_tcpip_call(ifp, MG_TCPIP_EV_ARP, &pkt.raw);
51685260
rx_arp(ifp, &pkt);
51695261
} else if (pkt.eth->type == mg_htons(0x86dd)) {
5170-
pkt.ip6 = (struct ip6 *) (pkt.eth + 1);
5171-
if (pkt.raw.len < sizeof(*pkt.eth) + sizeof(*pkt.ip6)) return; // Truncated
5172-
if ((pkt.ip6->ver >> 4) != 0x6) return; // Not IP
5173-
mkpay(&pkt, pkt.ip6 + 1);
5262+
pkt.ip6 = (struct ip6 *) (pkt.pay.buf);
51745263
rx_ip6(ifp, &pkt);
51755264
} else if (pkt.eth->type == mg_htons(0x800)) {
5176-
pkt.ip = (struct ip *) (pkt.eth + 1);
5177-
if (pkt.raw.len < sizeof(*pkt.eth) + sizeof(*pkt.ip)) return; // Truncated
5178-
// Truncate frame to what IP header tells us
5179-
if ((size_t) mg_ntohs(pkt.ip->len) + sizeof(struct eth) < pkt.raw.len) {
5180-
pkt.raw.len = (size_t) mg_ntohs(pkt.ip->len) + sizeof(struct eth);
5181-
}
5182-
if (pkt.raw.len < sizeof(*pkt.eth) + sizeof(*pkt.ip)) return; // Truncated
5183-
if ((pkt.ip->ver >> 4) != 4) return; // Not IP
5184-
mkpay(&pkt, pkt.ip + 1);
5265+
pkt.ip = (struct ip *) (pkt.pay.buf);
51855266
rx_ip(ifp, &pkt);
51865267
} else {
51875268
MG_DEBUG(("Unknown eth type %x", mg_htons(pkt.eth->type)));

0 commit comments

Comments
 (0)