Skip to content

Commit 66dcca9

Browse files
committed
Add safety nets for lousy networks
1 parent 0f1a3cf commit 66dcca9

File tree

5 files changed

+182
-46
lines changed

5 files changed

+182
-46
lines changed

mongoose.c

Lines changed: 88 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -4872,6 +4872,8 @@ static struct mg_connection *getpeer(struct mg_mgr *mgr, struct pkt *pkt,
48724872
}
48734873

48744874
static void mac_resolved(struct mg_connection *c);
4875+
static uint8_t *get_return_mac(struct mg_tcpip_if *ifp, struct mg_addr *rem,
4876+
bool is_udp, struct pkt *pkt);
48754877

48764878
static void rx_arp(struct mg_tcpip_if *ifp, struct pkt *pkt) {
48774879
if (pkt->arp->op == mg_htons(1) && pkt->arp->tpa == ifp->ip) {
@@ -4897,6 +4899,7 @@ static void rx_arp(struct mg_tcpip_if *ifp, struct pkt *pkt) {
48974899
if (pkt->arp->spa == ifp->gw) {
48984900
// Got response for the GW ARP request. Set ifp->gwmac and IP -> READY
48994901
memcpy(ifp->gwmac, pkt->arp->sha, sizeof(ifp->gwmac));
4902+
ifp->gw_ready = true;
49004903
if (ifp->state == MG_TCPIP_STATE_IP) {
49014904
ifp->state = MG_TCPIP_STATE_READY;
49024905
onstatechange(ifp);
@@ -4921,6 +4924,11 @@ static void rx_icmp(struct mg_tcpip_if *ifp, struct pkt *pkt) {
49214924
size_t space = ifp->tx.len - hlen, plen = pkt->pay.len;
49224925
struct ip *ip;
49234926
struct icmp *icmp;
4927+
struct mg_addr ips;
4928+
ips.ip4 = pkt->ip->src;
4929+
ips.is_ip6 = false;
4930+
if (get_return_mac(ifp, &ips, false, pkt) == NULL)
4931+
return; // safety net for lousy networks
49244932
if (plen > space) plen = space;
49254933
ip = tx_ip(ifp, pkt->eth->src, 1, ifp->ip, pkt->ip->src,
49264934
sizeof(*icmp) + plen);
@@ -4940,7 +4948,7 @@ static void rx_dhcp_client(struct mg_tcpip_if *ifp, struct pkt *pkt) {
49404948
// perform size check first, then access fields
49414949
uint8_t *p = pkt->dhcp->options,
49424950
*end = (uint8_t *) &pkt->pay.buf[pkt->pay.len];
4943-
if (end < p) return; // options are optional, check min header length
4951+
if (end < p) return; // options are optional, check min header length
49444952
if (memcmp(&pkt->dhcp->xid, ifp->mac + 2, sizeof(pkt->dhcp->xid))) return;
49454953
while (p + 1 < end && p[0] != 255) { // Parse options RFC-1533 #9
49464954
if (p[0] == 1 && p[1] == sizeof(ifp->mask) && p + 6 < end) { // Mask
@@ -4978,6 +4986,7 @@ static void rx_dhcp_client(struct mg_tcpip_if *ifp, struct pkt *pkt) {
49784986
MG_INFO(("Lease: %u sec (%lld)", lease, ifp->lease_expire / 1000));
49794987
// assume DHCP server = router until ARP resolves
49804988
memcpy(ifp->gwmac, pkt->eth->src, sizeof(ifp->gwmac));
4989+
ifp->gw_ready = true; // NOTE(): actual gw ARP won't retry now
49814990
ifp->ip = ip, ifp->gw = gw, ifp->mask = mask;
49824991
ifp->state = MG_TCPIP_STATE_IP; // BOUND state
49834992
mg_random(&rand, sizeof(rand));
@@ -5002,7 +5011,7 @@ static void rx_dhcp_server(struct mg_tcpip_if *ifp, struct pkt *pkt) {
50025011
*end = (uint8_t *) &pkt->pay.buf[pkt->pay.len];
50035012
// struct dhcp *req = pkt->dhcp;
50045013
struct dhcp res = {2, 1, 6, 0, 0, 0, 0, 0, 0, 0, 0, {0}, 0, {0}};
5005-
if (end < p) return; // options are optional, check min header length
5014+
if (end < p) return; // options are optional, check min header length
50065015
res.yiaddr = ifp->ip;
50075016
((uint8_t *) (&res.yiaddr))[3]++; // Offer our IP + 1
50085017
while (p + 1 < end && p[0] != 255) { // Parse options
@@ -5106,6 +5115,7 @@ static void rx_ndp_na(struct mg_tcpip_if *ifp, struct pkt *pkt) {
51065115
if (MG_IP6MATCH(na->addr, ifp->gw6)) {
51075116
// Got response for the GW NS request. Set ifp->gw6mac and IP6 -> READY
51085117
memcpy(ifp->gw6mac, opts, sizeof(ifp->gw6mac));
5118+
ifp->gw6_ready = true;
51095119
if (ifp->state6 == MG_TCPIP_STATE_IP) {
51105120
ifp->state6 = MG_TCPIP_STATE_READY;
51115121
onstate6change(ifp);
@@ -5226,6 +5236,7 @@ static void rx_ndp_ra(struct mg_tcpip_if *ifp, struct pkt *pkt) {
52265236
// Received router's MAC address
52275237
ifp->state6 = MG_TCPIP_STATE_READY;
52285238
memcpy(ifp->gw6mac, opts + 2, 6);
5239+
ifp->gw6_ready = true;
52295240
} else if (type == 5 && length >= 8) {
52305241
// process MTU if available
52315242
uint32_t mtu = mg_ntohl(*(uint32_t *) (opts + 4));
@@ -5272,6 +5283,11 @@ static void rx_icmp6(struct mg_tcpip_if *ifp, struct pkt *pkt) {
52725283
size_t hlen =
52735284
sizeof(struct eth) + sizeof(struct ip6) + sizeof(struct icmp6);
52745285
size_t space = ifp->tx.len - hlen, plen = pkt->pay.len;
5286+
struct mg_addr ips;
5287+
ips.ip6[0] = pkt->ip6->src[0], ips.ip6[1] = pkt->ip6->src[1];
5288+
ips.is_ip6 = true;
5289+
if (get_return_mac(ifp, &ips, false, pkt) == NULL)
5290+
return; // safety net for lousy networks
52755291
if (plen > space) plen = space; // Copy (truncated) RX payload to TX
52765292
// Echo Reply, 4.2
52775293
tx_icmp6(ifp, pkt->eth->src, pkt->ip6->dst, pkt->ip6->src, 129, 0,
@@ -5296,7 +5312,7 @@ static void onstate6change(struct mg_tcpip_if *ifp) {
52965312
MG_INFO((" GW: %M", mg_print_ip6, &ifp->gw6));
52975313
MG_INFO((" MAC: %M", mg_print_mac, &ifp->mac));
52985314
} else if (ifp->state6 == MG_TCPIP_STATE_IP) {
5299-
if (ifp->gw6[0] != 0 || ifp->gw6[1] != 0)
5315+
if (ifp->gw6[0] != 0 || ifp->gw6[1] != 0)
53005316
tx_ndp_ns(ifp, ifp->gw6, ifp->gw6mac); // unsolicited GW MAC resolution
53015317
} else if (ifp->state6 == MG_TCPIP_STATE_UP) {
53025318
MG_INFO(("IP: %M", mg_print_ip6, &ifp->ip6ll));
@@ -5306,9 +5322,43 @@ static void onstate6change(struct mg_tcpip_if *ifp) {
53065322
}
53075323
#endif
53085324

5325+
static uint8_t *get_return_mac(struct mg_tcpip_if *ifp, struct mg_addr *rem,
5326+
bool is_udp, struct pkt *pkt) {
5327+
#if MG_ENABLE_IPV6
5328+
if (rem->is_ip6) {
5329+
if (is_udp && MG_IP6MATCH(rem->ip6, ip6_allnodes.u)) // local broadcast
5330+
return (uint8_t *) ip6mac_allnodes;
5331+
if (rem->ip6[0] == ifp->ip6[0]) // TODO(): HANDLE PREFIX ***
5332+
return pkt->eth->src; // we're on the same LAN, get MAC from frame
5333+
if (is_udp && *((uint8_t *) rem->ip6) == 0xFF) // multicast
5334+
{
5335+
} // TODO(): ip6_mcastmac(s->mac, c->rem.ip6), l2 PR handles this better
5336+
if (ifp->gw6_ready) // use the router
5337+
return ifp->gw6mac; // ignore source MAC in frame
5338+
} else
5339+
#endif
5340+
{
5341+
uint32_t rem_ip = rem->ip4;
5342+
if (is_udp && (rem_ip == 0xffffffff || rem_ip == (ifp->ip | ~ifp->mask)))
5343+
return (uint8_t *) broadcast; // global or local broadcast
5344+
if (ifp->ip != 0 && ((rem_ip & ifp->mask) == (ifp->ip & ifp->mask)))
5345+
return pkt->eth->src; // we're on the same LAN, get MAC from frame
5346+
if (is_udp &&
5347+
(*((uint8_t *) &rem_ip) & 0xE0) == 0xE0) // 224 to 239, E0 to EF
5348+
{
5349+
} // TODO(): ip4_mcastmac(s->mac, &rem_ip); // multicast group, l2 PR
5350+
if (ifp->gw_ready) // use the router, ignore source MAC
5351+
return ifp->gwmac;
5352+
}
5353+
MG_ERROR(("%M %s: No way back, can't respond", mg_print_ip_port, rem,
5354+
is_udp ? "UDP" : "TCP"));
5355+
return NULL;
5356+
}
5357+
53095358
static bool rx_udp(struct mg_tcpip_if *ifp, struct pkt *pkt) {
53105359
struct mg_connection *c = getpeer(ifp->mgr, pkt, true);
53115360
struct connstate *s;
5361+
uint8_t *mac;
53125362
if (c == NULL) return false; // No UDP listener on this port
53135363
s = (struct connstate *) (c + 1);
53145364
c->rem.port = pkt->udp->sport;
@@ -5321,7 +5371,9 @@ static bool rx_udp(struct mg_tcpip_if *ifp, struct pkt *pkt) {
53215371
{
53225372
c->rem.ip4 = pkt->ip->src;
53235373
}
5324-
memcpy(s->mac, pkt->eth->src, sizeof(s->mac));
5374+
if ((mac = get_return_mac(ifp, &c->rem, true, pkt)) == NULL)
5375+
return false; // safety net for lousy networks
5376+
memcpy(s->mac, mac, sizeof(s->mac));
53255377
if (c->recv.len >= MG_MAX_RECV_SIZE) {
53265378
mg_error(c, "max_recv_buf_size reached");
53275379
} else if (c->recv.size - c->recv.len < pkt->pay.len &&
@@ -5410,6 +5462,7 @@ static size_t tx_tcp_ctrlresp(struct mg_tcpip_if *ifp, struct pkt *pkt,
54105462
uint32_t ackno = mg_htonl(mg_ntohl(pkt->tcp->seq) + (uint32_t) pkt->pay.len +
54115463
((pkt->tcp->flags & (TH_SYN | TH_FIN)) ? 1 : 0));
54125464
struct mg_addr ips, ipd;
5465+
uint8_t *mac;
54135466
memset(&ips, 0, sizeof(ips));
54145467
memset(&ipd, 0, sizeof(ipd));
54155468
if (pkt->ip != NULL) {
@@ -5423,7 +5476,9 @@ static size_t tx_tcp_ctrlresp(struct mg_tcpip_if *ifp, struct pkt *pkt,
54235476
}
54245477
ips.port = pkt->tcp->dport;
54255478
ipd.port = pkt->tcp->sport;
5426-
return tx_tcp(ifp, pkt->eth->src, &ips, &ipd, flags, seqno, ackno, NULL, 0);
5479+
if ((mac = get_return_mac(ifp, &ipd, false, pkt)) == NULL)
5480+
return 0; // safety net for lousy networks
5481+
return tx_tcp(ifp, mac, &ips, &ipd, flags, seqno, ackno, NULL, 0);
54275482
}
54285483

54295484
static size_t tx_tcp_rst(struct mg_tcpip_if *ifp, struct pkt *pkt, bool toack) {
@@ -5435,15 +5490,14 @@ static struct mg_connection *accept_conn(struct mg_connection *lsn,
54355490
struct pkt *pkt, uint16_t mss) {
54365491
struct mg_connection *c = mg_alloc_conn(lsn->mgr);
54375492
struct connstate *s;
5493+
uint8_t *mac;
54385494
if (c == NULL) {
54395495
MG_ERROR(("OOM"));
54405496
return NULL;
54415497
}
54425498
s = (struct connstate *) (c + 1);
54435499
s->dmss = mss; // from options in client SYN
54445500
s->seq = mg_ntohl(pkt->tcp->ack), s->ack = mg_ntohl(pkt->tcp->seq);
5445-
memcpy(s->mac, pkt->eth->src, sizeof(s->mac));
5446-
settmout(c, MIP_TTYPE_KEEPALIVE);
54475501
#if MG_ENABLE_IPV6
54485502
if (lsn->loc.is_ip6) {
54495503
c->rem.ip6[0] = pkt->ip6->src[0], c->rem.ip6[1] = pkt->ip6->src[1],
@@ -5459,6 +5513,12 @@ static struct mg_connection *accept_conn(struct mg_connection *lsn,
54595513
}
54605514
c->rem.port = pkt->tcp->sport;
54615515
c->loc.port = lsn->loc.port;
5516+
if ((mac = get_return_mac(lsn->mgr->ifp, &c->rem, false, pkt)) == NULL) {
5517+
free(c); // safety net for lousy networks, not actually needed
5518+
return NULL; // as path has already been checked at SYN (sending SYN+ACK)
5519+
}
5520+
memcpy(s->mac, mac, sizeof(s->mac));
5521+
settmout(c, MIP_TTYPE_KEEPALIVE);
54625522
MG_DEBUG(("%lu accepted %M", c->id, mg_print_ip_port, &c->rem));
54635523
LIST_ADD_HEAD(struct mg_connection, &lsn->mgr->conns, c);
54645524
c->is_accepted = 1;
@@ -5770,7 +5830,8 @@ static void rx_tcp(struct mg_tcpip_if *ifp, struct pkt *pkt) {
57705830
// Use peer's src port and bl key as ISN, to later identify the
57715831
// handshake
57725832
isn = (mg_htonl(((uint32_t) key << 16) | mg_ntohs(pkt->tcp->sport)));
5773-
tx_tcp_ctrlresp(ifp, pkt, TH_SYN | TH_ACK, isn);
5833+
if (tx_tcp_ctrlresp(ifp, pkt, TH_SYN | TH_ACK, isn) == 0)
5834+
backlog_remove(c, (uint16_t) key); // safety net for lousy networks
57745835
} // what should we do when port=0 ? Linux takes port 0 as any other
57755836
// port
57765837
} else if (pkt->tcp->flags == TH_ACK) {
@@ -5804,10 +5865,10 @@ static void rx_ip(struct mg_tcpip_if *ifp, struct pkt *pkt) {
58045865
if (ihl < 5) return; // bad IHL
58055866
if (pkt->pay.len < (uint16_t) (ihl * 4)) return; // Truncated / malformed
58065867
// There can be link padding, take length from IP header
5807-
len = mg_ntohs(pkt->ip->len); // IP datagram length
5868+
len = mg_ntohs(pkt->ip->len); // IP datagram length
58085869
if (len < (uint16_t) (ihl * 4) || len > pkt->pay.len) return; // malformed
5809-
pkt->pay.len = len; // strip padding
5810-
mkpay(pkt, (uint32_t *) pkt->ip + ihl); // account for opts
5870+
pkt->pay.len = len; // strip padding
5871+
mkpay(pkt, (uint32_t *) pkt->ip + ihl); // account for opts
58115872
frag = mg_ntohs(pkt->ip->frag);
58125873
if (frag & IP_MORE_FRAGS_MSK || frag & IP_FRAG_OFFSET_MSK) {
58135874
struct mg_connection *c;
@@ -6074,15 +6135,21 @@ static void mg_tcpip_poll(struct mg_tcpip_if *ifp, uint64_t now) {
60746135
}
60756136
// Handle gw ARP request timeout, order is important
60766137
if (expired_1000ms && ifp->state == MG_TCPIP_STATE_IP) {
6077-
ifp->state = MG_TCPIP_STATE_READY; // keep best-effort MAC
6138+
ifp->state = MG_TCPIP_STATE_READY; // keep best-effort MAC or poison mark
60786139
onstatechange(ifp);
60796140
}
6141+
if (expired_1000ms && ifp->state == MG_TCPIP_STATE_READY && !ifp->gw_ready &&
6142+
ifp->gw != 0)
6143+
mg_tcpip_arp_request(ifp, ifp->gw, NULL); // retry GW ARP request
60806144
#if MG_ENABLE_IPV6
60816145
// Handle gw NS/NA req/resp timeout, order is important
60826146
if (expired_1000ms && ifp->state6 == MG_TCPIP_STATE_IP) {
6083-
ifp->state6 = MG_TCPIP_STATE_READY; // keep best-effort MAC
6147+
ifp->state6 = MG_TCPIP_STATE_READY; // keep best-effort MAC or poison mark
60846148
onstate6change(ifp);
60856149
}
6150+
if (expired_1000ms && ifp->state == MG_TCPIP_STATE_READY && !ifp->gw6_ready &&
6151+
(ifp->gw6[0] != 0 || ifp->gw6[1] != 0))
6152+
tx_ndp_ns(ifp, ifp->gw6, ifp->gw6mac); // retry GW MAC resolution
60866153
#endif
60876154

60886155
// poll driver
@@ -6196,9 +6263,8 @@ void mg_tcpip_init(struct mg_mgr *mgr, struct mg_tcpip_if *ifp) {
61966263
ifp->mtu = MG_TCPIP_MTU_DEFAULT;
61976264
mgr->extraconnsize = sizeof(struct connstate);
61986265
if (ifp->ip == 0) ifp->enable_dhcp_client = true;
6199-
memset(ifp->gwmac, 255, sizeof(ifp->gwmac)); // Set best-effort to bcast
6200-
mg_random(&ifp->eport, sizeof(ifp->eport)); // Random from 0 to 65535
6201-
ifp->eport |= MG_EPHEMERAL_PORT_BASE; // Random from
6266+
mg_random(&ifp->eport, sizeof(ifp->eport)); // Random from 0 to 65535
6267+
ifp->eport |= MG_EPHEMERAL_PORT_BASE; // Random from
62026268
// MG_EPHEMERAL_PORT_BASE to 65535
62036269
if (ifp->tx.buf == NULL || ifp->recv_queue.buf == NULL) MG_ERROR(("OOM"));
62046270
#if MG_ENABLE_IPV6
@@ -6208,7 +6274,6 @@ void mg_tcpip_init(struct mg_mgr *mgr, struct mg_tcpip_if *ifp) {
62086274
ifp->enable_slaac = true;
62096275
ip6genll((uint8_t *) ifp->ip6ll, ifp->mac); // build link-local address
62106276
}
6211-
memset(ifp->gw6mac, 255, sizeof(ifp->gw6mac)); // Set best-effort to bcast
62126277
#endif
62136278
}
62146279
}
@@ -6267,19 +6332,19 @@ void mg_connect_resolved(struct mg_connection *c) {
62676332
struct connstate *s = (struct connstate *) (c + 1);
62686333
memcpy(s->mac, ip6mac_allnodes, sizeof(s->mac));
62696334
mac_resolved(c);
6270-
} else if (c->rem.ip6[0] == ifp->ip6[0] &&
6335+
} else if (c->rem.ip6[0] == ifp->ip6[0] && // TODO(): HANDLE PREFIX ***
62716336
!MG_IP6MATCH(c->rem.ip6,
62726337
ifp->gw6)) { // skip if gw (onstate6change -> NS)
62736338
// If we're in the same LAN, fire a Neighbor Solicitation
62746339
MG_DEBUG(("%lu NS lookup...", c->id));
62756340
tx_ndp_ns(ifp, c->rem.ip6, ifp->mac);
62766341
settmout(c, MIP_TTYPE_ARP);
62776342
c->is_arplooking = 1;
6278-
} else if (*((uint8_t *) c->rem.ip6) == 0xFF) { // multicast
6343+
} else if (c->is_udp && *((uint8_t *) c->rem.ip6) == 0xFF) { // multicast
62796344
struct connstate *s = (struct connstate *) (c + 1);
62806345
ip6_mcastmac(s->mac, c->rem.ip6);
62816346
mac_resolved(c);
6282-
} else if (ifp->gw6[0] != 0 || ifp->gw6[1] != 0) {
6347+
} else if (ifp->gw6_ready) {
62836348
struct connstate *s = (struct connstate *) (c + 1);
62846349
memcpy(s->mac, ifp->gw6mac, sizeof(s->mac));
62856350
mac_resolved(c);
@@ -6302,14 +6367,14 @@ void mg_connect_resolved(struct mg_connection *c) {
63026367
mg_tcpip_arp_request(ifp, rem_ip, NULL);
63036368
settmout(c, MIP_TTYPE_ARP);
63046369
c->is_arplooking = 1;
6305-
} else if ((*((uint8_t *) &rem_ip) & 0xE0) == 0xE0) {
6370+
} else if (c->is_udp && (*((uint8_t *) &rem_ip) & 0xE0) == 0xE0) {
63066371
struct connstate *s =
63076372
(struct connstate *) (c + 1); // 224 to 239, E0 to EF
63086373
ip4_mcastmac(s->mac, &rem_ip); // multicast group
63096374
mac_resolved(c);
6310-
} else if (ifp->gw != 0) {
6375+
} else if (ifp->gw_ready) {
63116376
struct connstate *s = (struct connstate *) (c + 1);
6312-
memcpy(s->mac, ifp->gwmac, sizeof(ifp->gwmac));
6377+
memcpy(s->mac, ifp->gwmac, sizeof(s->mac));
63136378
mac_resolved(c);
63146379
} else {
63156380
MG_ERROR(("No gateway, can't connect"));

mongoose.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3219,9 +3219,11 @@ struct mg_tcpip_if {
32193219
#define MG_TCPIP_STATE_REQ 2 // Interface is up, DHCP REQUESTING state
32203220
#define MG_TCPIP_STATE_IP 3 // Interface is up and has an IP assigned
32213221
#define MG_TCPIP_STATE_READY 4 // Interface has fully come up, ready to work
3222+
bool gw_ready; // We've got a hw address for the router
32223223
#if MG_ENABLE_IPV6
32233224
uint8_t gw6mac[6]; // IPv6 Router's MAC
32243225
uint8_t state6; // Current IPv6 state
3226+
bool gw6_ready; // We've got a hw address for the IPv6 router
32253227
#endif
32263228
};
32273229
void mg_tcpip_init(struct mg_mgr *, struct mg_tcpip_if *);

0 commit comments

Comments
 (0)