@@ -4205,7 +4205,7 @@ struct eth {
42054205struct 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
42204220struct 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
42304230struct 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+
42364242struct 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
42874301struct 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
43014317static 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
43074323static 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
43124328static 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
46304646static 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
46914706static 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
50835097static 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
51305182static 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
51465237static 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