@@ -4729,13 +4729,17 @@ static size_t tx_tcp(struct mg_tcpip_if *ifp, uint8_t *dst_mac, uint32_t dst_ip,
4729
4729
return ether_output(ifp, PDIFF(ifp->tx.buf, tcp + 1) + len);
4730
4730
}
4731
4731
4732
- static size_t tx_tcp_pkt (struct mg_tcpip_if *ifp, struct pkt *pkt,
4733
- uint8_t flags, uint32_t seq, const void *buf,
4734
- size_t len) {
4735
- uint32_t delta = ( pkt->tcp->flags & (TH_SYN | TH_FIN)) ? 1 : 0;
4732
+ static size_t tx_tcp_ctrlresp (struct mg_tcpip_if *ifp, struct pkt *pkt,
4733
+ uint8_t flags, uint32_t seqno) {
4734
+ uint32_t ackno = mg_htonl(mg_ntohl(pkt->tcp->seq) + (uint32_t) pkt->pay. len +
4735
+ (( pkt->tcp->flags & (TH_SYN | TH_FIN)) ? 1 : 0)) ;
4736
4736
return tx_tcp(ifp, pkt->eth->src, pkt->ip->src, flags, pkt->tcp->dport,
4737
- pkt->tcp->sport, seq, mg_htonl(mg_ntohl(pkt->tcp->seq) + delta),
4738
- buf, len);
4737
+ pkt->tcp->sport, seqno, ackno, NULL, 0);
4738
+ }
4739
+
4740
+ static size_t tx_tcp_rst(struct mg_tcpip_if *ifp, struct pkt *pkt, bool toack) {
4741
+ return tx_tcp_ctrlresp(ifp, pkt, toack ? TH_RST : (TH_RST | TH_ACK),
4742
+ toack ? pkt->tcp->ack : 0);
4739
4743
}
4740
4744
4741
4745
static struct mg_connection *accept_conn(struct mg_connection *lsn,
@@ -4934,10 +4938,9 @@ static void read_conn(struct mg_connection *c, struct pkt *pkt) {
4934
4938
}
4935
4939
}
4936
4940
4937
-
4938
4941
// TCP backlog
4939
4942
struct mg_backlog {
4940
- uint16_t port, mss; // use port=0 for available entries
4943
+ uint16_t port, mss; // use port=0 for available entries
4941
4944
uint8_t age;
4942
4945
};
4943
4946
@@ -4947,7 +4950,7 @@ static int backlog_insert(struct mg_connection *c, uint16_t port,
4947
4950
size_t i;
4948
4951
for (i = 0; i < sizeof(c->data) / sizeof(*p); i++) {
4949
4952
if (p[i].port != 0) continue;
4950
- p[i].age = 2; // remove after two calls, average 1.5 call rate
4953
+ p[i].age = 2; // remove after two calls, average 1.5 call rate
4951
4954
p[i].port = port, p[i].mss = mss;
4952
4955
return (int) i;
4953
4956
}
@@ -4970,7 +4973,7 @@ static void backlog_remove(struct mg_connection *c, uint16_t key) {
4970
4973
4971
4974
static void backlog_maintain(struct mg_connection *c) {
4972
4975
struct mg_backlog *p = (struct mg_backlog *) c->data;
4973
- size_t i; // dec age and remove those where it reaches 0
4976
+ size_t i; // dec age and remove those where it reaches 0
4974
4977
for (i = 0; i < sizeof(c->data) / sizeof(*p); i++) {
4975
4978
if (p[i].port == 0) continue;
4976
4979
if (p[i].age != 0) --p[i].age;
@@ -5007,70 +5010,74 @@ static void handle_opt(struct connstate *s, struct tcp *tcp) {
5007
5010
static void rx_tcp(struct mg_tcpip_if *ifp, struct pkt *pkt) {
5008
5011
struct mg_connection *c = getpeer(ifp->mgr, pkt, false);
5009
5012
struct connstate *s = c == NULL ? NULL : (struct connstate *) (c + 1);
5010
- #if 0
5011
- MG_INFO(("%lu %hhu %d", c ? c->id : 0, pkt->tcp->flags, (int) pkt->pay.len));
5012
- #endif
5013
+ // Order is VERY important; RFC-9293 3.5.2
5014
+ // - check clients (Group 1) and established connections (Group 3)
5013
5015
if (c != NULL && c->is_connecting && pkt->tcp->flags == (TH_SYN | TH_ACK)) {
5016
+ // client got a server connection accept
5014
5017
handle_opt(s, pkt->tcp); // process options (MSS)
5015
5018
s->seq = mg_ntohl(pkt->tcp->ack), s->ack = mg_ntohl(pkt->tcp->seq) + 1;
5016
- tx_tcp_pkt (ifp, pkt, TH_ACK, pkt->tcp->ack, NULL, 0 );
5019
+ tx_tcp_ctrlresp (ifp, pkt, TH_ACK, pkt->tcp->ack);
5017
5020
c->is_connecting = 0; // Client connected
5018
5021
settmout(c, MIP_TTYPE_KEEPALIVE);
5019
5022
mg_call(c, MG_EV_CONNECT, NULL); // Let user know
5020
5023
if (c->is_tls_hs) mg_tls_handshake(c);
5021
5024
if (!c->is_tls_hs) c->is_tls = 0; // user did not call mg_tls_init()
5022
5025
} else if (c != NULL && c->is_connecting && pkt->tcp->flags != TH_ACK) {
5023
- // mg_hexdump(pkt->raw.buf, pkt->raw.len);
5024
- tx_tcp_pkt(ifp, pkt, TH_RST | TH_ACK, pkt->tcp->ack, NULL, 0);
5026
+ mg_error(c, "connection refused");
5025
5027
} else if (c != NULL && pkt->tcp->flags & TH_RST) {
5028
+ // TODO(): validate RST is within window (and optional with proper ACK)
5026
5029
mg_error(c, "peer RST"); // RFC-1122 4.2.2.13
5027
5030
} else if (c != NULL) {
5028
- #if 0
5029
- MG_DEBUG(("%lu %d %M:%hu -> %M:%hu", c->id, (int) pkt->raw.len,
5030
- mg_print_ip4, &pkt->ip->src, mg_ntohs(pkt->tcp->sport),
5031
- mg_print_ip4, &pkt->ip->dst, mg_ntohs(pkt->tcp->dport)));
5032
- mg_hexdump(pkt->pay.buf, pkt->pay.len);
5033
- #endif
5031
+ // process segment
5034
5032
s->tmiss = 0; // Reset missed keep-alive counter
5035
5033
if (s->ttype == MIP_TTYPE_KEEPALIVE) // Advance keep-alive timer
5036
5034
settmout(c,
5037
5035
MIP_TTYPE_KEEPALIVE); // unless a former ACK timeout is pending
5038
5036
read_conn(c, pkt); // Override timer with ACK timeout if needed
5039
- } else if ((c = getpeer(ifp->mgr, pkt, true)) == NULL) {
5040
- tx_tcp_pkt(ifp, pkt, TH_RST | TH_ACK, pkt->tcp->ack, NULL, 0);
5041
- } else if (pkt->tcp->flags & TH_RST) {
5042
- if (c->is_accepted) mg_error(c, "peer RST"); // RFC-1122 4.2.2.13
5043
- // ignore RST if not connected
5044
- } else if (pkt->tcp->flags & TH_SYN) {
5045
- struct connstate cs; // At this point, s = NULL, there is no connection
5046
- int key;
5047
- uint32_t isn;
5048
- if (pkt->tcp->sport != 0) {
5049
- handle_opt(&cs, pkt->tcp); // process options (MSS)
5050
- key = backlog_insert(c, pkt->tcp->sport, cs.dmss); // backlog options (MSS)
5051
- if (key < 0) return; // no room in backlog, discard SYN, client retries
5052
- // Use peer's src port and bl key as ISN, to later identify the handshake
5053
- isn = (mg_htonl(((uint32_t)key << 16) | mg_ntohs(pkt->tcp->sport)));
5054
- tx_tcp_pkt(ifp, pkt, TH_SYN | TH_ACK, isn, NULL, 0);
5055
- } // what should we do when port=0 ? Linux takes port 0 as any other port
5056
- } else if (pkt->tcp->flags & TH_FIN) {
5057
- tx_tcp_pkt(ifp, pkt, TH_FIN | TH_ACK, pkt->tcp->ack, NULL, 0);
5058
- } else if ((uint16_t) (mg_htonl(pkt->tcp->ack) - 1) ==
5059
- mg_htons(pkt->tcp->sport)) {
5060
- uint16_t key = (uint16_t) ((mg_htonl(pkt->tcp->ack) - 1) >> 16);
5061
- struct mg_backlog *b = backlog_retrieve(c, key, pkt->tcp->sport);
5062
- if (b != NULL) {
5063
- accept_conn(c, pkt, b->mss); // pass options
5064
- backlog_remove(c, key);
5065
- } else if (!c->is_accepted) { // not an actual match, reset
5066
- tx_tcp_pkt(ifp, pkt, TH_RST | TH_ACK, pkt->tcp->ack, NULL, 0);
5067
- // TODO(scaprile): revisit this and below, weird scenarios
5068
- }
5069
- } else if (!c->is_accepted) { // no peer
5070
- tx_tcp_pkt(ifp, pkt, TH_RST | TH_ACK, pkt->tcp->ack, NULL, 0);
5071
- } else {
5072
- // MG_VERBOSE(("dropped silently.."));
5073
- }
5037
+ } else
5038
+ // - we don't listen on that port; RFC-9293 3.5.2 Group 1
5039
+ // - check listening connections; RFC-9293 3.5.2 Group 2
5040
+ if ((c = getpeer(ifp->mgr, pkt, true)) == NULL) {
5041
+ // not listening on that port
5042
+ if (!(pkt->tcp->flags & TH_RST)) {
5043
+ tx_tcp_rst(ifp, pkt, pkt->tcp->flags & TH_ACK);
5044
+ } // else silently discard
5045
+ } else if (pkt->tcp->flags == TH_SYN) {
5046
+ // listener receives a connection request
5047
+ struct connstate cs; // At this point, s = NULL, there is no connection
5048
+ int key;
5049
+ uint32_t isn;
5050
+ if (pkt->tcp->sport != 0) {
5051
+ handle_opt(&cs, pkt->tcp); // process options (MSS)
5052
+ key = backlog_insert(c, pkt->tcp->sport,
5053
+ cs.dmss); // backlog options (MSS)
5054
+ if (key < 0) return; // no room in backlog, discard SYN, client retries
5055
+ // Use peer's src port and bl key as ISN, to later identify the
5056
+ // handshake
5057
+ isn = (mg_htonl(((uint32_t) key << 16) | mg_ntohs(pkt->tcp->sport)));
5058
+ tx_tcp_ctrlresp(ifp, pkt, TH_SYN | TH_ACK, isn);
5059
+ } // what should we do when port=0 ? Linux takes port 0 as any other
5060
+ // port
5061
+ } else if (pkt->tcp->flags == TH_ACK) {
5062
+ // listener receives an ACK
5063
+ struct mg_backlog *b = NULL;
5064
+ if ((uint16_t) (mg_htonl(pkt->tcp->ack) - 1) ==
5065
+ mg_htons(pkt->tcp->sport)) {
5066
+ uint16_t key = (uint16_t) ((mg_htonl(pkt->tcp->ack) - 1) >> 16);
5067
+ b = backlog_retrieve(c, key, pkt->tcp->sport);
5068
+ if (b != NULL) { // ACK is a response to a SYN+ACK
5069
+ accept_conn(c, pkt, b->mss); // pass options
5070
+ backlog_remove(c, key);
5071
+ } // else not an actual match, reset
5072
+ }
5073
+ if (b == NULL) tx_tcp_rst(ifp, pkt, true);
5074
+ } else if (pkt->tcp->flags & TH_RST) {
5075
+ // silently discard
5076
+ } else if (pkt->tcp->flags & TH_ACK) { // ACK + something else != RST
5077
+ tx_tcp_rst(ifp, pkt, true);
5078
+ } else if (pkt->tcp->flags & TH_SYN) { // SYN + something else != ACK
5079
+ tx_tcp_rst(ifp, pkt, false);
5080
+ } // else silently discard
5074
5081
}
5075
5082
5076
5083
static void rx_ip(struct mg_tcpip_if *ifp, struct pkt *pkt) {
@@ -5090,6 +5097,7 @@ static void rx_ip(struct mg_tcpip_if *ifp, struct pkt *pkt) {
5090
5097
pkt->udp = (struct udp *) (pkt->ip + 1);
5091
5098
if (pkt->pay.len < sizeof(*pkt->udp)) return;
5092
5099
mkpay(pkt, pkt->udp + 1);
5100
+ if (pkt->udp->len < pkt->pay.len) pkt->pay.len = pkt->udp->len;
5093
5101
MG_VERBOSE(("UDP %M:%hu -> %M:%hu len %u", mg_print_ip4, &pkt->ip->src,
5094
5102
mg_ntohs(pkt->udp->sport), mg_print_ip4, &pkt->ip->dst,
5095
5103
mg_ntohs(pkt->udp->dport), (int) pkt->pay.len));
@@ -5108,7 +5116,7 @@ static void rx_ip(struct mg_tcpip_if *ifp, struct pkt *pkt) {
5108
5116
uint16_t iplen, off;
5109
5117
pkt->tcp = (struct tcp *) (pkt->ip + 1);
5110
5118
if (pkt->pay.len < sizeof(*pkt->tcp)) return;
5111
- mkpay(pkt, pkt->tcp + 1);
5119
+ mkpay(pkt, (uint32_t *) pkt->tcp + (pkt->tcp->off >> 4)); // may have opts
5112
5120
iplen = mg_ntohs(pkt->ip->len);
5113
5121
off = (uint16_t) (sizeof(*pkt->ip) + ((pkt->tcp->off >> 4) * 4U));
5114
5122
if (iplen >= off) pkt->pay.len = (size_t) (iplen - off);
0 commit comments