From 765bb237b74fc4832177bac9a2d9d5dd9e7e305d Mon Sep 17 00:00:00 2001 From: "Sergio R. Caprile" Date: Thu, 30 Jul 2026 17:16:43 -0300 Subject: [PATCH] Add support for DSCP --- mongoose.c | 171 +++++++++++++++++++++++++++------------------- mongoose.h | 9 +++ src/net.c | 22 ++++++ src/net.h | 9 +++ src/net_builtin.c | 149 +++++++++++++++++++++------------------- test/mip_test.c | 14 +++- 6 files changed, 234 insertions(+), 140 deletions(-) diff --git a/mongoose.c b/mongoose.c index 76d1e4fa..fc9e97b8 100644 --- a/mongoose.c +++ b/mongoose.c @@ -7817,6 +7817,28 @@ size_t mg_printf(struct mg_connection *c, const char *fmt, ...) { return len; } +bool mg_dscp(struct mg_connection *c, uint8_t dscp) { +#if MG_ENABLE_SOCKET +#if MG_ENABLE_FREERTOS_TCP || MG_ARCH == MG_ARCH_THREADX || \ + MG_ARCH == MG_ARCH_TIRTOS || MG_ARCH == MG_ARCH_WIN32 + (void) c; + (void) dscp; +#else + int tos = dscp << 2, level = IPPROTO_IP, optname = IP_TOS; + c->dscp = (unsigned) (dscp & 63); +#if MG_ENABLE_IPV6 && !MG_ENABLE_LWIP + if (c->loc.is_ip6) + level = IPPROTO_IPV6, optname = IPV6_TCLASS; +#endif + return setsockopt((MG_SOCKET_TYPE) (size_t) c->fd, level, optname, (char *) &tos, sizeof(tos)) == 0; +#endif +#elif MG_ENABLE_TCPIP + c->dscp = (unsigned) (dscp & 63); + return true; +#endif + return false; +} + static bool mg_atonl(struct mg_str str, struct mg_addr *addr) { uint32_t localhost = mg_htonl(0x7f000001); if (mg_strcasecmp(str, mg_str("localhost")) != 0) return false; @@ -8176,7 +8198,7 @@ struct connstate { struct ip { uint8_t ver; // Version - uint8_t tos; // Unused + uint8_t tos; // DS field uint16_t len; // Datagram length uint16_t id; // Unused uint16_t frag; // Fragmentation @@ -8542,13 +8564,15 @@ static void onstatechange(struct mg_tcpip_if *ifp) { } static struct ip *tx_ip(struct mg_tcpip_if *ifp, uint8_t *l2_dst, uint8_t proto, - uint32_t ip_src, uint32_t ip_dst, size_t plen) { + uint8_t dscp, uint32_t ip_src, uint32_t ip_dst, + size_t plen) { // ifp->tx.buf is 8-bit aligned, keep other headers as pointers, see pkt uint8_t *l2p = (uint8_t *) ifp->tx.buf; struct ip *ip = (struct ip *) mg_l2_header(ifp, MG_TCPIP_L2PROTO_IPV4, ifp->mac, l2_dst, l2p); memset(ip, 0, sizeof(*ip)); - ip->ver = 0x45; // Version 4, header length 5 words + ip->ver = 0x45; // Version 4, header length 5 words + ip->tos = (uint8_t) (dscp << 2); ip->frag = mg_htons(0x4000); // Don't fragment ip->len = mg_htons((uint16_t) (sizeof(*ip) + plen)); ip->ttl = 64; @@ -8561,12 +8585,12 @@ static struct ip *tx_ip(struct mg_tcpip_if *ifp, uint8_t *l2_dst, uint8_t proto, #if MG_ENABLE_IPV6 static struct ip6 *tx_ip6(struct mg_tcpip_if *ifp, uint8_t *l2_dst, - uint8_t next, uint64_t *ip_src, uint64_t *ip_dst, - size_t plen); + uint8_t next, uint8_t dscp, uint64_t *ip_src, + uint64_t *ip_dst, size_t plen); #endif static bool tx_udp(struct mg_tcpip_if *ifp, uint8_t *l2_dst, - struct mg_addr *ip_src, struct mg_addr *ip_dst, + struct mg_addr *ip_src, struct mg_addr *ip_dst, uint8_t dscp, const void *buf, size_t len) { uint8_t *l3p; size_t l2_len; @@ -8575,7 +8599,7 @@ static bool tx_udp(struct mg_tcpip_if *ifp, uint8_t *l2_dst, #if MG_ENABLE_IPV6 struct ip6 *ip6 = NULL; if (ip_dst->is_ip6) { - ip6 = tx_ip6(ifp, l2_dst, 17, ip_src->addr.ip6, ip_dst->addr.ip6, + ip6 = tx_ip6(ifp, l2_dst, 17, dscp, ip_src->addr.ip6, ip_dst->addr.ip6, len + sizeof(struct udp)); udp = (struct udp *) (ip6 + 1); l2_len = sizeof(*ip6) + sizeof(*udp) + len; @@ -8583,7 +8607,7 @@ static bool tx_udp(struct mg_tcpip_if *ifp, uint8_t *l2_dst, } else #endif { - ip = tx_ip(ifp, l2_dst, 17, ip_src->addr.ip4, ip_dst->addr.ip4, + ip = tx_ip(ifp, l2_dst, 17, dscp, ip_src->addr.ip4, ip_dst->addr.ip4, len + sizeof(struct udp)); udp = (struct udp *) (ip + 1); l2_len = sizeof(*ip) + sizeof(*udp) + len; @@ -8616,7 +8640,7 @@ static bool tx_udp4(struct mg_tcpip_if *ifp, uint8_t *l2_dst, uint32_t ip_src, memset(&ipd, 0, sizeof(ipd)); ipd.addr.ip4 = ip_dst; ipd.port = dport; - return tx_udp(ifp, l2_dst, &ips, &ipd, buf, len); + return tx_udp(ifp, l2_dst, &ips, &ipd, 0, buf, len); } static void tx_dhcp(struct mg_tcpip_if *ifp, uint8_t *l2_dst, uint32_t ip_src, @@ -8772,7 +8796,7 @@ static void rx_icmp(struct mg_tcpip_if *ifp, struct pkt *pkt) { if ((l2addr = get_return_l2addr(ifp, &ips, false, pkt)) == NULL) return; // safety net for lousy networks if (plen > room) plen = room; - ip = tx_ip(ifp, l2addr, 1, ifp->ip, pkt->ip->src, sizeof(*icmp) + plen); + ip = tx_ip(ifp, l2addr, 1, 0, ifp->ip, pkt->ip->src, sizeof(*icmp) + plen); icmp = (struct icmp *) (ip + 1); memset(icmp, 0, sizeof(*icmp)); // Set csum, type, code to 0 memcpy(icmp + 1, pkt->pay.buf, plen); // Copy RX payload to TX @@ -8913,14 +8937,15 @@ static void rx_dhcp_server(struct mg_tcpip_if *ifp, struct pkt *pkt) { #if MG_ENABLE_IPV6 static struct ip6 *tx_ip6(struct mg_tcpip_if *ifp, uint8_t *l2_dst, - uint8_t next, uint64_t *ip_src, uint64_t *ip_dst, - size_t plen) { + uint8_t next, uint8_t dscp, uint64_t *ip_src, + uint64_t *ip_dst, size_t plen) { // ifp->tx.buf is 8-bit aligned, keep other headers as pointers, see pkt uint8_t *l2p = (uint8_t *) ifp->tx.buf; struct ip6 *ip6 = (struct ip6 *) mg_l2_header(ifp, MG_TCPIP_L2PROTO_IPV6, ifp->mac, l2_dst, l2p); memset(ip6, 0, sizeof(*ip6)); - ip6->ver = 0x60; // Version 6, traffic class 0 + ip6->ver = (uint8_t) (0x60 | (dscp >> 2)); // Version 6, traffic class + ip6->label[0] = (uint8_t) (dscp << 6); ip6->plen = mg_htons((uint16_t) plen); ip6->next = next; ip6->hops = 255; // NDP requires max @@ -8936,7 +8961,7 @@ static void tx_icmp6(struct mg_tcpip_if *ifp, uint8_t *l2_dst, uint64_t *ip_src, const void *buf, size_t len) { struct ip6 *ip6; struct icmp6 *icmp6; - ip6 = tx_ip6(ifp, l2_dst, 58, ip_src, ip_dst, sizeof(*icmp6) + len); + ip6 = tx_ip6(ifp, l2_dst, 58, 0, ip_src, ip_dst, sizeof(*icmp6) + len); icmp6 = (struct icmp6 *) (ip6 + 1); memset(icmp6, 0, sizeof(*icmp6)); // Set csum to 0 icmp6->type = type; @@ -8968,15 +8993,16 @@ static void onstate6change(struct mg_tcpip_if *ifp); static void rx_ndp_na(struct mg_tcpip_if *ifp, struct pkt *pkt) { struct ndp_na *na = (struct ndp_na *) pkt->pay.buf; - uint8_t *opts = (uint8_t *) (na + 1), *endp = opts + pkt->pay.len - sizeof(*na); - if (pkt->pay.len < (sizeof(*na) + 2)) return; // first 2 bytes in opts - if ((na->res[0] & 0x40) == 0) return; // not "solicited" - if (*opts++ != 2) return; // no target hwaddr, must have + uint8_t *opts = (uint8_t *) (na + 1), + *endp = opts + pkt->pay.len - sizeof(*na); + if (pkt->pay.len < (sizeof(*na) + 2)) return; // first 2 bytes in opts + if ((na->res[0] & 0x40) == 0) return; // not "solicited" + if (*opts++ != 2) return; // no target hwaddr, must have MG_VERBOSE(("NDP NA resp from %M", mg_print_ip6, (char *) &na->addr)); if (MG_IP6MATCH(na->addr, ifp->gw6)) { // Got response for the GW NS request. Set ifp->gw6mac and IP6 -> READY - uint8_t len = *opts++; // check valid hwaddr and get it - if ((opts + 8 * len - 2) > endp) return; // truncated + uint8_t len = *opts++; // check valid hwaddr and get it + if ((opts + 8 * len - 2) > endp) return; // truncated if (!mg_l2_ip6get(ifp->l2type, ifp->gw6mac, opts, len)) return; ifp->gw6_ready = true; if (ifp->state6 == MG_TCPIP_STATE_IP) { @@ -8987,8 +9013,8 @@ static void rx_ndp_na(struct mg_tcpip_if *ifp, struct pkt *pkt) { struct mg_connection *c = getpeer(ifp->mgr, pkt, false); if (c != NULL && c->is_arplooking) { struct connstate *s = (struct connstate *) (c + 1); - uint8_t len = *opts++; // check valid hwaddr and get it - if ((opts + 8 * len - 2) > endp) return; // truncated + uint8_t len = *opts++; // check valid hwaddr and get it + if ((opts + 8 * len - 2) > endp) return; // truncated if (!mg_l2_ip6get(ifp->l2type, s->mac, opts, len)) return; MG_DEBUG(("%lu NDP resolved %M -> %M", c->id, mg_print_ip6, &c->rem.addr.ip6, mg_print_l2addr, ifp->l2type, s->mac)); @@ -9000,17 +9026,18 @@ static void rx_ndp_na(struct mg_tcpip_if *ifp, struct pkt *pkt) { // Neighbor Solicitation, 4.3 static void rx_ndp_ns(struct mg_tcpip_if *ifp, struct pkt *pkt) { - struct ndp_na *ns = (struct ndp_na *) pkt->pay.buf; // struct ndp_ns = ndp_na + struct ndp_na *ns = (struct ndp_na *) pkt->pay.buf; // struct ndp_ns = ndp_na uint64_t target[2]; - if (pkt->pay.len < (sizeof(*ns) + 2)) return; // first 2 bytes in opts + if (pkt->pay.len < (sizeof(*ns) + 2)) return; // first 2 bytes in opts memcpy(target, ns->addr, sizeof(target)); if (MG_IP6MATCH(target, ifp->ip6ll) || MG_IP6MATCH(target, ifp->ip6)) { uint64_t req[2]; // requester address uint8_t l2[sizeof(struct mg_l2addr)]; - uint8_t len, *opts = (uint8_t *) (ns + 1), *endp = opts + pkt->pay.len - sizeof(*ns); - if (*opts++ != 1) return; // no requester hwaddr (source) - len = *opts++; // check valid hwaddr and get it - if ((opts + 8 * len - 2) > endp) return; // truncated + uint8_t len, *opts = (uint8_t *) (ns + 1), + *endp = opts + pkt->pay.len - sizeof(*ns); + if (*opts++ != 1) return; // no requester hwaddr (source) + len = *opts++; // check valid hwaddr and get it + if ((opts + 8 * len - 2) > endp) return; // truncated if (!mg_l2_ip6get(ifp->l2type, l2, opts, len)) return; req[0] = pkt->ip6->src[0], req[1] = pkt->ip6->src[1]; // align to 64-bit tx_ndp_na(ifp, l2, target, req, true, ifp->mac); @@ -9129,7 +9156,7 @@ static void rx_ndp_ra(struct mg_tcpip_if *ifp, struct pkt *pkt) { } else if (type == 5 && length >= 8) { // process MTU if available, ignore if it smells mtu = MG_LOAD_BE32(opts + 4); - if (mtu < 1280 || mtu > ifp->l2mtu) mtu = 0; // RFC-8200, minimum MTU + if (mtu < 1280 || mtu > ifp->l2mtu) mtu = 0; // RFC-8200, minimum MTU } else if (type == 3 && length >= 32) { // process prefix, 4.6.2, ignore if it smells uint8_t pfx_flags = opts[3]; // L=0x80, A=0x40 @@ -9145,7 +9172,8 @@ static void rx_ndp_ra(struct mg_tcpip_if *ifp, struct pkt *pkt) { (void) pref_lifetime; gotprefix = true; - if (prefix_len != ifp->prefix_len || !match_prefix(prefix, ifp->prefix, ifp->prefix_len)) + if (prefix_len != ifp->prefix_len || + !match_prefix(prefix, ifp->prefix, ifp->prefix_len)) changed = true; } opts += length; @@ -9157,7 +9185,8 @@ static void rx_ndp_ra(struct mg_tcpip_if *ifp, struct pkt *pkt) { if (changed) ifp->is_ip6_changed = true; ifp->gw6[0] = pkt->ip6->src[0], ifp->gw6[1] = pkt->ip6->src[1]; if (gotl2addr) memcpy(ifp->gw6mac, l2, sizeof(ifp->gw6mac)); - if (gotl2addr || ifp->l2type == MG_TCPIP_L2_PPP || ifp->l2type == MG_TCPIP_L2_PPPoE) { + if (gotl2addr || ifp->l2type == MG_TCPIP_L2_PPP || + ifp->l2type == MG_TCPIP_L2_PPPoE) { ifp->state6 = MG_TCPIP_STATE_READY; ifp->gw6_ready = true; } @@ -9242,7 +9271,8 @@ static void onstate6change(struct mg_tcpip_if *ifp) { ifp->is_ip6_changed = false; } } else if (ifp->state6 == MG_TCPIP_STATE_IP) { - if ((ifp->gw6[0] != 0 || ifp->gw6[1] != 0) && (ifp->l2type != MG_TCPIP_L2_PPP && ifp->l2type != MG_TCPIP_L2_PPPoE)) + if ((ifp->gw6[0] != 0 || ifp->gw6[1] != 0) && + (ifp->l2type != MG_TCPIP_L2_PPP && ifp->l2type != MG_TCPIP_L2_PPPoE)) tx_ndp_ns(ifp, ifp->gw6, NULL); // unsolicited GW hwaddr resolution } else if (ifp->state6 == MG_TCPIP_STATE_UP) { MG_INFO(("IP: %M", mg_print_ip6, &ifp->ip6ll)); @@ -9332,8 +9362,8 @@ static bool rx_udp(struct mg_tcpip_if *ifp, struct pkt *pkt) { static size_t tx_tcp(struct mg_tcpip_if *ifp, uint8_t *l2_dst, struct mg_addr *ip_src, struct mg_addr *ip_dst, - uint8_t flags, uint32_t seq, uint32_t ack, const void *buf, - size_t len) { + uint8_t dscp, uint8_t flags, uint32_t seq, uint32_t ack, + const void *buf, size_t len) { uint8_t *l3p; struct ip *ip = NULL; struct tcp *tcp; @@ -9358,14 +9388,15 @@ static size_t tx_tcp(struct mg_tcpip_if *ifp, uint8_t *l2_dst, #if MG_ENABLE_IPV6 if (ip_dst->is_ip6) { - ip6 = - tx_ip6(ifp, l2_dst, 6, ip_src->addr.ip6, ip_dst->addr.ip6, hlen + len); + ip6 = tx_ip6(ifp, l2_dst, 6, dscp, ip_src->addr.ip6, ip_dst->addr.ip6, + hlen + len); tcp = (struct tcp *) (ip6 + 1); l3p = (uint8_t *) ip6; } else #endif { - ip = tx_ip(ifp, l2_dst, 6, ip_src->addr.ip4, ip_dst->addr.ip4, hlen + len); + ip = tx_ip(ifp, l2_dst, 6, dscp, ip_src->addr.ip4, ip_dst->addr.ip4, + hlen + len); tcp = (struct tcp *) (ip + 1); l3p = (uint8_t *) ip; } @@ -9415,11 +9446,12 @@ static size_t tx_tcp_ctrlresp(struct mg_tcpip_if *ifp, struct pkt *pkt, ipd.port = pkt->tcp->sport; if ((l2addr = get_return_l2addr(ifp, &ipd, false, pkt)) == NULL) return 0; // safety net for lousy networks - return tx_tcp(ifp, l2addr, &ips, &ipd, flags, seqno, ackno, NULL, 0); + return tx_tcp(ifp, l2addr, &ips, &ipd, 0, flags, seqno, ackno, NULL, 0); } static size_t tx_tcp_rst(struct mg_tcpip_if *ifp, struct pkt *pkt, bool toack) { - return tx_tcp_ctrlresp(ifp, pkt, (uint8_t)(toack ? TH_RST : (TH_RST | TH_ACK)), + return tx_tcp_ctrlresp(ifp, pkt, + (uint8_t) (toack ? TH_RST : (TH_RST | TH_ACK)), toack ? pkt->tcp->ack : 0); } @@ -9435,7 +9467,7 @@ static struct mg_connection *accept_conn(struct mg_connection *lsn, s = (struct connstate *) (c + 1); s->dmss = mss; // from options in client SYN s->seq = mg_ntohl(pkt->tcp->ack), s->ack = mg_ntohl(pkt->tcp->seq); - s->win = mg_ntohs(pkt->tcp->win), s->maxseq = (uint32_t)(s->seq + s->win); + s->win = mg_ntohs(pkt->tcp->win), s->maxseq = (uint32_t) (s->seq + s->win); #if MG_ENABLE_IPV6 if (lsn->loc.is_ip6) { c->rem.addr.ip6[0] = pkt->ip6->src[0], @@ -9520,7 +9552,7 @@ static bool udp_send(struct mg_connection *c, const void *buf, size_t len) { ips.addr.ip4 = ifp->ip; } ips.port = c->loc.port; - return tx_udp(ifp, s->mac, &ips, &c->rem, buf, len); + return tx_udp(ifp, s->mac, &ips, &c->rem, c->dscp, buf, len); } long mg_io_send(struct mg_connection *c, const void *buf, size_t len) { @@ -9535,7 +9567,7 @@ long mg_io_send(struct mg_connection *c, const void *buf, size_t len) { if (room == 0) return MG_IO_WAIT; if (len > s->dmss) len = s->dmss; // RFC-6691: reduce if sending opts if ((uint32_t) len > room) len = room; - sent = tx_tcp(ifp, s->mac, &c->loc, &c->rem, TH_PUSH | TH_ACK, + sent = tx_tcp(ifp, s->mac, &c->loc, &c->rem, c->dscp, TH_PUSH | TH_ACK, mg_htonl(s->seq), mg_htonl(s->ack), buf, len); if (sent == 0) { return MG_IO_WAIT; @@ -9550,16 +9582,16 @@ long mg_io_send(struct mg_connection *c, const void *buf, size_t len) { } static void handle_tls_recv(struct mg_connection *c) { - size_t avail = mg_tls_pending(c); // will change after mg_tls_recv() + size_t avail = mg_tls_pending(c); // will change after mg_tls_recv() size_t min = avail > MG_MAX_RECV_SIZE ? MG_MAX_RECV_SIZE : avail; - struct mg_iobuf *io = &c->recv; // allocated on first avail > 0 + struct mg_iobuf *io = &c->recv; // allocated on first avail > 0 if (io->size - io->len < min && !mg_iobuf_resize(io, io->len + min)) { mg_error(c, "oom"); } else { // Decrypt data directly into c->recv. If io->buf = NULL or // io->len = io->size (no room), there can be outstanding data that can't be - // moved (mg_tls_pending() > 0 after mg_tls_recv() returns). So mg_tls_recv() - // takes care of returning 0 in this case (commented as "MIP") + // moved (mg_tls_pending() > 0 after mg_tls_recv() returns). So + // mg_tls_recv() takes care of returning 0 in this case (commented as "MIP") long n = mg_tls_recv(c, io->buf != NULL ? &io->buf[io->len] : io->buf, io->size - io->len); if (n == MG_IO_ERR) { @@ -9574,7 +9606,7 @@ static void handle_tls_recv(struct mg_connection *c) { static void handle_ack(struct connstate *s, uint32_t ackno, uint16_t win) { if (ackno < (s->seq - s->win) || ackno > s->seq) return; - s->maxseq = (uint32_t)(ackno + win); + s->maxseq = (uint32_t) (ackno + win); s->win = win; } @@ -9586,8 +9618,8 @@ static void read_conn(struct mg_connection *c, struct pkt *pkt) { uint8_t flags = TH_ACK; if (mg_ntohl(pkt->tcp->seq) != s->ack) { MG_VERBOSE(("ignoring FIN, %x != %x", mg_ntohl(pkt->tcp->seq), s->ack)); - tx_tcp(c->mgr->ifp, s->mac, &c->loc, &c->rem, TH_ACK, mg_htonl(s->seq), - mg_htonl(s->ack), "", 0); + tx_tcp(c->mgr->ifp, s->mac, &c->loc, &c->rem, c->dscp, TH_ACK, + mg_htonl(s->seq), mg_htonl(s->ack), "", 0); return; } // If we initiated the closure, we reply with ACK upon receiving FIN @@ -9608,14 +9640,14 @@ static void read_conn(struct mg_connection *c, struct pkt *pkt) { // is flushed, then send our FIN. c->is_draining = 1; } - tx_tcp(c->mgr->ifp, s->mac, &c->loc, &c->rem, flags, mg_htonl(s->seq), - mg_htonl(s->ack), "", 0); + tx_tcp(c->mgr->ifp, s->mac, &c->loc, &c->rem, c->dscp, flags, + mg_htonl(s->seq), mg_htonl(s->ack), "", 0); if (pkt->pay.len == 0) return; // if no data, we're done } else if (pkt->pay.len <= 1 && mg_ntohl(pkt->tcp->seq) == s->ack - 1) { // Keep-Alive (RFC-9293 3.8.4, allow erroneous implementations) MG_VERBOSE(("%lu keepalive ACK", c->id)); - tx_tcp(c->mgr->ifp, s->mac, &c->loc, &c->rem, TH_ACK, mg_htonl(s->seq), - mg_htonl(s->ack), NULL, 0); + tx_tcp(c->mgr->ifp, s->mac, &c->loc, &c->rem, c->dscp, TH_ACK, + mg_htonl(s->seq), mg_htonl(s->ack), NULL, 0); return; // no data to process } else if (pkt->pay.len == 0) { // this is an ACK if (pkt->tcp->flags & TH_ACK) @@ -9628,8 +9660,8 @@ static void read_conn(struct mg_connection *c, struct pkt *pkt) { MG_VERBOSE(("ignoring duplicate pkt")); } else { MG_VERBOSE(("SEQ != ACK: %x %x %x", seq, s->ack, ack)); - tx_tcp(c->mgr->ifp, s->mac, &c->loc, &c->rem, TH_ACK, mg_htonl(s->seq), - mg_htonl(s->ack), "", 0); + tx_tcp(c->mgr->ifp, s->mac, &c->loc, &c->rem, c->dscp, TH_ACK, + mg_htonl(s->seq), mg_htonl(s->ack), "", 0); } return; // drop it } else if (io->size - io->len < pkt->pay.len && @@ -9637,8 +9669,8 @@ static void read_conn(struct mg_connection *c, struct pkt *pkt) { mg_error(c, "oom"); return; // drop it } - if (pkt->tcp->flags & TH_ACK) - handle_ack(s, mg_ntohl(pkt->tcp->ack), mg_ntohs(pkt->tcp->win)); + if (pkt->tcp->flags & TH_ACK) + handle_ack(s, mg_ntohl(pkt->tcp->ack), mg_ntohs(pkt->tcp->win)); // Copy TCP payload into the IO buffer. If the connection is plain text, // we copy to c->recv. If the connection is TLS, this data is encrypted, // therefore we copy that encrypted data to the c->rtls iobuffer instead, @@ -9654,8 +9686,8 @@ static void read_conn(struct mg_connection *c, struct pkt *pkt) { if (s->unacked > MG_TCPIP_WIN / 2 && s->acked != s->ack) { // Send ACK immediately MG_VERBOSE(("%lu imm ACK %lu", c->id, s->acked)); - tx_tcp(c->mgr->ifp, s->mac, &c->loc, &c->rem, TH_ACK, mg_htonl(s->seq), - mg_htonl(s->ack), NULL, 0); + tx_tcp(c->mgr->ifp, s->mac, &c->loc, &c->rem, c->dscp, TH_ACK, + mg_htonl(s->seq), mg_htonl(s->ack), NULL, 0); s->unacked = 0; s->acked = s->ack; if (s->ttype != MIP_TTYPE_KEEPALIVE) settmout(c, MIP_TTYPE_KEEPALIVE); @@ -9757,7 +9789,7 @@ static void rx_tcp(struct mg_tcpip_if *ifp, struct pkt *pkt) { if (!handle_opt(s, pkt->tcp, pkt->ip6 != NULL)) return; // process options (MSS) s->seq = mg_ntohl(pkt->tcp->ack), s->ack = mg_ntohl(pkt->tcp->seq) + 1; - s->win = mg_ntohs(pkt->tcp->win), s->maxseq = (uint32_t)(s->seq + s->win); + s->win = mg_ntohs(pkt->tcp->win), s->maxseq = (uint32_t) (s->seq + s->win); tx_tcp_ctrlresp(ifp, pkt, TH_ACK, pkt->tcp->ack); c->is_connecting = 0; // Client connected settmout(c, MIP_TTYPE_KEEPALIVE); @@ -10143,8 +10175,8 @@ static void mg_tcpip_poll(struct mg_tcpip_if *ifp, uint64_t now) { drv_up = ifp->driver->poll ? ifp->driver->poll(ifp, expired_1000ms) : true; l2_up = mg_l2_poll(ifp, expired_1000ms); // Handle L2 up/down link status; if (expired_1000ms) { // ifp->state rules over state6 - mg_ip_link(ifp, drv_up, l2_up); // Handle IPv4 - mg_ip6_link(ifp, drv_up, l2_up); // Handle IPv6 + mg_ip_link(ifp, drv_up, l2_up); // Handle IPv4 + mg_ip6_link(ifp, drv_up, l2_up); // Handle IPv6 if (ifp->state < MG_TCPIP_STATE_UP) MG_ERROR(("Network is down")); mg_tcpip_call(ifp, MG_TCPIP_EV_TIMER_1S, NULL); } @@ -10183,7 +10215,7 @@ static void mg_tcpip_poll(struct mg_tcpip_if *ifp, uint64_t now) { continue; } else if (s->ttype == MIP_TTYPE_ACK && s->acked != s->ack) { MG_VERBOSE(("%lu ack %x %x", c->id, s->seq, s->ack)); - tx_tcp(ifp, s->mac, &c->loc, &c->rem, TH_ACK, mg_htonl(s->seq), + tx_tcp(ifp, s->mac, &c->loc, &c->rem, c->dscp, TH_ACK, mg_htonl(s->seq), mg_htonl(s->ack), NULL, 0); s->acked = s->ack; } else if (s->ttype == MIP_TTYPE_SYN) { @@ -10196,8 +10228,8 @@ static void mg_tcpip_poll(struct mg_tcpip_if *ifp, uint64_t now) { mg_error(c, "keepalive"); } else { MG_VERBOSE(("%lu keepalive", c->id)); - tx_tcp(ifp, s->mac, &c->loc, &c->rem, TH_ACK, mg_htonl(s->seq - 1), - mg_htonl(s->ack), NULL, 0); + tx_tcp(ifp, s->mac, &c->loc, &c->rem, c->dscp, TH_ACK, + mg_htonl(s->seq - 1), mg_htonl(s->ack), NULL, 0); } } @@ -10260,7 +10292,8 @@ void mg_tcpip_free(struct mg_tcpip_if *ifp) { static void send_syn(struct mg_connection *c) { struct connstate *s = (struct connstate *) (c + 1); uint32_t isn = mg_htonl((uint32_t) mg_ntohs(c->loc.port)); - tx_tcp(c->mgr->ifp, s->mac, &c->loc, &c->rem, TH_SYN, isn, 0, NULL, 0); + tx_tcp(c->mgr->ifp, s->mac, &c->loc, &c->rem, c->dscp, TH_SYN, isn, 0, NULL, + 0); } static void l2addr_resolved(struct mg_connection *c) { @@ -10281,9 +10314,9 @@ void mg_connect_resolved(struct mg_connection *c) { c->loc.port = mg_htons(ifp->eport++); #if MG_ENABLE_IPV6 if (c->rem.is_ip6) { - if(c->rem.addr.ip6[0] == ifp->ip6ll[0]) { // same local LAN, use ll + if (c->rem.addr.ip6[0] == ifp->ip6ll[0]) { // same local LAN, use ll c->loc.addr.ip6[0] = ifp->ip6ll[0], c->loc.addr.ip6[1] = ifp->ip6ll[1]; - } else { // use global address + } else { // use global address c->loc.addr.ip6[0] = ifp->ip6[0], c->loc.addr.ip6[1] = ifp->ip6[1]; } c->loc.is_ip6 = true; @@ -10363,7 +10396,7 @@ static void init_closure(struct mg_connection *c) { struct connstate *s = (struct connstate *) (c + 1); if (c->is_udp == false && c->is_listening == false && c->is_connecting == false) { // For TCP conns, - tx_tcp(c->mgr->ifp, s->mac, &c->loc, &c->rem, TH_FIN | TH_ACK, + tx_tcp(c->mgr->ifp, s->mac, &c->loc, &c->rem, c->dscp, TH_FIN | TH_ACK, mg_htonl(s->seq), mg_htonl(s->ack), NULL, 0); settmout(c, MIP_TTYPE_FIN); } diff --git a/mongoose.h b/mongoose.h index e4af7fa2..d3f9856c 100644 --- a/mongoose.h +++ b/mongoose.h @@ -2382,6 +2382,10 @@ struct mg_connection { void *pfn_data; // Protocol-level handler argument char data[MG_DATA_SIZE]; // Scratch space for protocol state; freely readable void *tls; // TLS state (internal) + unsigned dscp : 6; // Differentiated Services Code Point +#define MG_DSCP_CS(class_) ((class_) << 3) +#define MG_DSCP_AF(class_, drop_) (((class_) << 3) | ((drop_) << 1)) +#define MG_DSCP_EF 46 unsigned is_listening : 1; // Listening connection; accepts inbound connections unsigned is_client : 1; // Outbound connection created by mg_connect() unsigned is_accepted : 1; // Inbound connection accepted from a listener @@ -2462,6 +2466,11 @@ struct mg_connection *mg_connect(struct mg_mgr *, const char *url, struct mg_connection *mg_wrapfd(struct mg_mgr *mgr, int fd, mg_event_handler_t fn, void *fn_data); +// Sets the 6-bit Differentiated Services Code Point for outgoing datagrams. +// Call on MG_EV_OPEN for UDP listeners, MG_EV_CONNECT for clients, or +// MG_EV_ACCEPT for TCP servers, before sending application data. +bool mg_dscp(struct mg_connection *, uint8_t dscp); + // Called internally after DNS resolution completes to create the OS socket // and initiate the TCP/IP connect. Normally not called by user code. void mg_connect_resolved(struct mg_connection *); diff --git a/src/net.c b/src/net.c index 4211d4b3..3e262ce1 100644 --- a/src/net.c +++ b/src/net.c @@ -29,6 +29,28 @@ size_t mg_printf(struct mg_connection *c, const char *fmt, ...) { return len; } +bool mg_dscp(struct mg_connection *c, uint8_t dscp) { +#if MG_ENABLE_SOCKET +#if MG_ENABLE_FREERTOS_TCP || MG_ARCH == MG_ARCH_THREADX || \ + MG_ARCH == MG_ARCH_TIRTOS || MG_ARCH == MG_ARCH_WIN32 + (void) c; + (void) dscp; +#else + int tos = dscp << 2, level = IPPROTO_IP, optname = IP_TOS; + c->dscp = (unsigned) (dscp & 63); +#if MG_ENABLE_IPV6 && !MG_ENABLE_LWIP + if (c->loc.is_ip6) + level = IPPROTO_IPV6, optname = IPV6_TCLASS; +#endif + return setsockopt((MG_SOCKET_TYPE) (size_t) c->fd, level, optname, (char *) &tos, sizeof(tos)) == 0; +#endif +#elif MG_ENABLE_TCPIP + c->dscp = (unsigned) (dscp & 63); + return true; +#endif + return false; +} + static bool mg_atonl(struct mg_str str, struct mg_addr *addr) { uint32_t localhost = mg_htonl(0x7f000001); if (mg_strcasecmp(str, mg_str("localhost")) != 0) return false; diff --git a/src/net.h b/src/net.h index 6061afb6..10356cb5 100644 --- a/src/net.h +++ b/src/net.h @@ -74,6 +74,10 @@ struct mg_connection { void *pfn_data; // Protocol-level handler argument char data[MG_DATA_SIZE]; // Scratch space for protocol state; freely readable void *tls; // TLS state (internal) + unsigned dscp : 6; // Differentiated Services Code Point +#define MG_DSCP_CS(class_) ((class_) << 3) +#define MG_DSCP_AF(class_, drop_) (((class_) << 3) | ((drop_) << 1)) +#define MG_DSCP_EF 46 unsigned is_listening : 1; // Listening connection; accepts inbound connections unsigned is_client : 1; // Outbound connection created by mg_connect() unsigned is_accepted : 1; // Inbound connection accepted from a listener @@ -154,6 +158,11 @@ struct mg_connection *mg_connect(struct mg_mgr *, const char *url, struct mg_connection *mg_wrapfd(struct mg_mgr *mgr, int fd, mg_event_handler_t fn, void *fn_data); +// Sets the 6-bit Differentiated Services Code Point for outgoing datagrams. +// Call on MG_EV_OPEN for UDP listeners, MG_EV_CONNECT for clients, or +// MG_EV_ACCEPT for TCP servers, before sending application data. +bool mg_dscp(struct mg_connection *, uint8_t dscp); + // Called internally after DNS resolution completes to create the OS socket // and initiate the TCP/IP connect. Normally not called by user code. void mg_connect_resolved(struct mg_connection *); diff --git a/src/net_builtin.c b/src/net_builtin.c index fb0769bd..6c723090 100644 --- a/src/net_builtin.c +++ b/src/net_builtin.c @@ -46,7 +46,7 @@ struct connstate { struct ip { uint8_t ver; // Version - uint8_t tos; // Unused + uint8_t tos; // DS field uint16_t len; // Datagram length uint16_t id; // Unused uint16_t frag; // Fragmentation @@ -412,13 +412,15 @@ static void onstatechange(struct mg_tcpip_if *ifp) { } static struct ip *tx_ip(struct mg_tcpip_if *ifp, uint8_t *l2_dst, uint8_t proto, - uint32_t ip_src, uint32_t ip_dst, size_t plen) { + uint8_t dscp, uint32_t ip_src, uint32_t ip_dst, + size_t plen) { // ifp->tx.buf is 8-bit aligned, keep other headers as pointers, see pkt uint8_t *l2p = (uint8_t *) ifp->tx.buf; struct ip *ip = (struct ip *) mg_l2_header(ifp, MG_TCPIP_L2PROTO_IPV4, ifp->mac, l2_dst, l2p); memset(ip, 0, sizeof(*ip)); - ip->ver = 0x45; // Version 4, header length 5 words + ip->ver = 0x45; // Version 4, header length 5 words + ip->tos = (uint8_t) (dscp << 2); ip->frag = mg_htons(0x4000); // Don't fragment ip->len = mg_htons((uint16_t) (sizeof(*ip) + plen)); ip->ttl = 64; @@ -431,12 +433,12 @@ static struct ip *tx_ip(struct mg_tcpip_if *ifp, uint8_t *l2_dst, uint8_t proto, #if MG_ENABLE_IPV6 static struct ip6 *tx_ip6(struct mg_tcpip_if *ifp, uint8_t *l2_dst, - uint8_t next, uint64_t *ip_src, uint64_t *ip_dst, - size_t plen); + uint8_t next, uint8_t dscp, uint64_t *ip_src, + uint64_t *ip_dst, size_t plen); #endif static bool tx_udp(struct mg_tcpip_if *ifp, uint8_t *l2_dst, - struct mg_addr *ip_src, struct mg_addr *ip_dst, + struct mg_addr *ip_src, struct mg_addr *ip_dst, uint8_t dscp, const void *buf, size_t len) { uint8_t *l3p; size_t l2_len; @@ -445,7 +447,7 @@ static bool tx_udp(struct mg_tcpip_if *ifp, uint8_t *l2_dst, #if MG_ENABLE_IPV6 struct ip6 *ip6 = NULL; if (ip_dst->is_ip6) { - ip6 = tx_ip6(ifp, l2_dst, 17, ip_src->addr.ip6, ip_dst->addr.ip6, + ip6 = tx_ip6(ifp, l2_dst, 17, dscp, ip_src->addr.ip6, ip_dst->addr.ip6, len + sizeof(struct udp)); udp = (struct udp *) (ip6 + 1); l2_len = sizeof(*ip6) + sizeof(*udp) + len; @@ -453,7 +455,7 @@ static bool tx_udp(struct mg_tcpip_if *ifp, uint8_t *l2_dst, } else #endif { - ip = tx_ip(ifp, l2_dst, 17, ip_src->addr.ip4, ip_dst->addr.ip4, + ip = tx_ip(ifp, l2_dst, 17, dscp, ip_src->addr.ip4, ip_dst->addr.ip4, len + sizeof(struct udp)); udp = (struct udp *) (ip + 1); l2_len = sizeof(*ip) + sizeof(*udp) + len; @@ -486,7 +488,7 @@ static bool tx_udp4(struct mg_tcpip_if *ifp, uint8_t *l2_dst, uint32_t ip_src, memset(&ipd, 0, sizeof(ipd)); ipd.addr.ip4 = ip_dst; ipd.port = dport; - return tx_udp(ifp, l2_dst, &ips, &ipd, buf, len); + return tx_udp(ifp, l2_dst, &ips, &ipd, 0, buf, len); } static void tx_dhcp(struct mg_tcpip_if *ifp, uint8_t *l2_dst, uint32_t ip_src, @@ -642,7 +644,7 @@ static void rx_icmp(struct mg_tcpip_if *ifp, struct pkt *pkt) { if ((l2addr = get_return_l2addr(ifp, &ips, false, pkt)) == NULL) return; // safety net for lousy networks if (plen > room) plen = room; - ip = tx_ip(ifp, l2addr, 1, ifp->ip, pkt->ip->src, sizeof(*icmp) + plen); + ip = tx_ip(ifp, l2addr, 1, 0, ifp->ip, pkt->ip->src, sizeof(*icmp) + plen); icmp = (struct icmp *) (ip + 1); memset(icmp, 0, sizeof(*icmp)); // Set csum, type, code to 0 memcpy(icmp + 1, pkt->pay.buf, plen); // Copy RX payload to TX @@ -783,14 +785,15 @@ static void rx_dhcp_server(struct mg_tcpip_if *ifp, struct pkt *pkt) { #if MG_ENABLE_IPV6 static struct ip6 *tx_ip6(struct mg_tcpip_if *ifp, uint8_t *l2_dst, - uint8_t next, uint64_t *ip_src, uint64_t *ip_dst, - size_t plen) { + uint8_t next, uint8_t dscp, uint64_t *ip_src, + uint64_t *ip_dst, size_t plen) { // ifp->tx.buf is 8-bit aligned, keep other headers as pointers, see pkt uint8_t *l2p = (uint8_t *) ifp->tx.buf; struct ip6 *ip6 = (struct ip6 *) mg_l2_header(ifp, MG_TCPIP_L2PROTO_IPV6, ifp->mac, l2_dst, l2p); memset(ip6, 0, sizeof(*ip6)); - ip6->ver = 0x60; // Version 6, traffic class 0 + ip6->ver = (uint8_t) (0x60 | (dscp >> 2)); // Version 6, traffic class + ip6->label[0] = (uint8_t) (dscp << 6); ip6->plen = mg_htons((uint16_t) plen); ip6->next = next; ip6->hops = 255; // NDP requires max @@ -806,7 +809,7 @@ static void tx_icmp6(struct mg_tcpip_if *ifp, uint8_t *l2_dst, uint64_t *ip_src, const void *buf, size_t len) { struct ip6 *ip6; struct icmp6 *icmp6; - ip6 = tx_ip6(ifp, l2_dst, 58, ip_src, ip_dst, sizeof(*icmp6) + len); + ip6 = tx_ip6(ifp, l2_dst, 58, 0, ip_src, ip_dst, sizeof(*icmp6) + len); icmp6 = (struct icmp6 *) (ip6 + 1); memset(icmp6, 0, sizeof(*icmp6)); // Set csum to 0 icmp6->type = type; @@ -838,15 +841,16 @@ static void onstate6change(struct mg_tcpip_if *ifp); static void rx_ndp_na(struct mg_tcpip_if *ifp, struct pkt *pkt) { struct ndp_na *na = (struct ndp_na *) pkt->pay.buf; - uint8_t *opts = (uint8_t *) (na + 1), *endp = opts + pkt->pay.len - sizeof(*na); - if (pkt->pay.len < (sizeof(*na) + 2)) return; // first 2 bytes in opts - if ((na->res[0] & 0x40) == 0) return; // not "solicited" - if (*opts++ != 2) return; // no target hwaddr, must have + uint8_t *opts = (uint8_t *) (na + 1), + *endp = opts + pkt->pay.len - sizeof(*na); + if (pkt->pay.len < (sizeof(*na) + 2)) return; // first 2 bytes in opts + if ((na->res[0] & 0x40) == 0) return; // not "solicited" + if (*opts++ != 2) return; // no target hwaddr, must have MG_VERBOSE(("NDP NA resp from %M", mg_print_ip6, (char *) &na->addr)); if (MG_IP6MATCH(na->addr, ifp->gw6)) { // Got response for the GW NS request. Set ifp->gw6mac and IP6 -> READY - uint8_t len = *opts++; // check valid hwaddr and get it - if ((opts + 8 * len - 2) > endp) return; // truncated + uint8_t len = *opts++; // check valid hwaddr and get it + if ((opts + 8 * len - 2) > endp) return; // truncated if (!mg_l2_ip6get(ifp->l2type, ifp->gw6mac, opts, len)) return; ifp->gw6_ready = true; if (ifp->state6 == MG_TCPIP_STATE_IP) { @@ -857,8 +861,8 @@ static void rx_ndp_na(struct mg_tcpip_if *ifp, struct pkt *pkt) { struct mg_connection *c = getpeer(ifp->mgr, pkt, false); if (c != NULL && c->is_arplooking) { struct connstate *s = (struct connstate *) (c + 1); - uint8_t len = *opts++; // check valid hwaddr and get it - if ((opts + 8 * len - 2) > endp) return; // truncated + uint8_t len = *opts++; // check valid hwaddr and get it + if ((opts + 8 * len - 2) > endp) return; // truncated if (!mg_l2_ip6get(ifp->l2type, s->mac, opts, len)) return; MG_DEBUG(("%lu NDP resolved %M -> %M", c->id, mg_print_ip6, &c->rem.addr.ip6, mg_print_l2addr, ifp->l2type, s->mac)); @@ -870,17 +874,18 @@ static void rx_ndp_na(struct mg_tcpip_if *ifp, struct pkt *pkt) { // Neighbor Solicitation, 4.3 static void rx_ndp_ns(struct mg_tcpip_if *ifp, struct pkt *pkt) { - struct ndp_na *ns = (struct ndp_na *) pkt->pay.buf; // struct ndp_ns = ndp_na + struct ndp_na *ns = (struct ndp_na *) pkt->pay.buf; // struct ndp_ns = ndp_na uint64_t target[2]; - if (pkt->pay.len < (sizeof(*ns) + 2)) return; // first 2 bytes in opts + if (pkt->pay.len < (sizeof(*ns) + 2)) return; // first 2 bytes in opts memcpy(target, ns->addr, sizeof(target)); if (MG_IP6MATCH(target, ifp->ip6ll) || MG_IP6MATCH(target, ifp->ip6)) { uint64_t req[2]; // requester address uint8_t l2[sizeof(struct mg_l2addr)]; - uint8_t len, *opts = (uint8_t *) (ns + 1), *endp = opts + pkt->pay.len - sizeof(*ns); - if (*opts++ != 1) return; // no requester hwaddr (source) - len = *opts++; // check valid hwaddr and get it - if ((opts + 8 * len - 2) > endp) return; // truncated + uint8_t len, *opts = (uint8_t *) (ns + 1), + *endp = opts + pkt->pay.len - sizeof(*ns); + if (*opts++ != 1) return; // no requester hwaddr (source) + len = *opts++; // check valid hwaddr and get it + if ((opts + 8 * len - 2) > endp) return; // truncated if (!mg_l2_ip6get(ifp->l2type, l2, opts, len)) return; req[0] = pkt->ip6->src[0], req[1] = pkt->ip6->src[1]; // align to 64-bit tx_ndp_na(ifp, l2, target, req, true, ifp->mac); @@ -999,7 +1004,7 @@ static void rx_ndp_ra(struct mg_tcpip_if *ifp, struct pkt *pkt) { } else if (type == 5 && length >= 8) { // process MTU if available, ignore if it smells mtu = MG_LOAD_BE32(opts + 4); - if (mtu < 1280 || mtu > ifp->l2mtu) mtu = 0; // RFC-8200, minimum MTU + if (mtu < 1280 || mtu > ifp->l2mtu) mtu = 0; // RFC-8200, minimum MTU } else if (type == 3 && length >= 32) { // process prefix, 4.6.2, ignore if it smells uint8_t pfx_flags = opts[3]; // L=0x80, A=0x40 @@ -1015,7 +1020,8 @@ static void rx_ndp_ra(struct mg_tcpip_if *ifp, struct pkt *pkt) { (void) pref_lifetime; gotprefix = true; - if (prefix_len != ifp->prefix_len || !match_prefix(prefix, ifp->prefix, ifp->prefix_len)) + if (prefix_len != ifp->prefix_len || + !match_prefix(prefix, ifp->prefix, ifp->prefix_len)) changed = true; } opts += length; @@ -1027,7 +1033,8 @@ static void rx_ndp_ra(struct mg_tcpip_if *ifp, struct pkt *pkt) { if (changed) ifp->is_ip6_changed = true; ifp->gw6[0] = pkt->ip6->src[0], ifp->gw6[1] = pkt->ip6->src[1]; if (gotl2addr) memcpy(ifp->gw6mac, l2, sizeof(ifp->gw6mac)); - if (gotl2addr || ifp->l2type == MG_TCPIP_L2_PPP || ifp->l2type == MG_TCPIP_L2_PPPoE) { + if (gotl2addr || ifp->l2type == MG_TCPIP_L2_PPP || + ifp->l2type == MG_TCPIP_L2_PPPoE) { ifp->state6 = MG_TCPIP_STATE_READY; ifp->gw6_ready = true; } @@ -1112,7 +1119,8 @@ static void onstate6change(struct mg_tcpip_if *ifp) { ifp->is_ip6_changed = false; } } else if (ifp->state6 == MG_TCPIP_STATE_IP) { - if ((ifp->gw6[0] != 0 || ifp->gw6[1] != 0) && (ifp->l2type != MG_TCPIP_L2_PPP && ifp->l2type != MG_TCPIP_L2_PPPoE)) + if ((ifp->gw6[0] != 0 || ifp->gw6[1] != 0) && + (ifp->l2type != MG_TCPIP_L2_PPP && ifp->l2type != MG_TCPIP_L2_PPPoE)) tx_ndp_ns(ifp, ifp->gw6, NULL); // unsolicited GW hwaddr resolution } else if (ifp->state6 == MG_TCPIP_STATE_UP) { MG_INFO(("IP: %M", mg_print_ip6, &ifp->ip6ll)); @@ -1202,8 +1210,8 @@ static bool rx_udp(struct mg_tcpip_if *ifp, struct pkt *pkt) { static size_t tx_tcp(struct mg_tcpip_if *ifp, uint8_t *l2_dst, struct mg_addr *ip_src, struct mg_addr *ip_dst, - uint8_t flags, uint32_t seq, uint32_t ack, const void *buf, - size_t len) { + uint8_t dscp, uint8_t flags, uint32_t seq, uint32_t ack, + const void *buf, size_t len) { uint8_t *l3p; struct ip *ip = NULL; struct tcp *tcp; @@ -1228,14 +1236,15 @@ static size_t tx_tcp(struct mg_tcpip_if *ifp, uint8_t *l2_dst, #if MG_ENABLE_IPV6 if (ip_dst->is_ip6) { - ip6 = - tx_ip6(ifp, l2_dst, 6, ip_src->addr.ip6, ip_dst->addr.ip6, hlen + len); + ip6 = tx_ip6(ifp, l2_dst, 6, dscp, ip_src->addr.ip6, ip_dst->addr.ip6, + hlen + len); tcp = (struct tcp *) (ip6 + 1); l3p = (uint8_t *) ip6; } else #endif { - ip = tx_ip(ifp, l2_dst, 6, ip_src->addr.ip4, ip_dst->addr.ip4, hlen + len); + ip = tx_ip(ifp, l2_dst, 6, dscp, ip_src->addr.ip4, ip_dst->addr.ip4, + hlen + len); tcp = (struct tcp *) (ip + 1); l3p = (uint8_t *) ip; } @@ -1285,11 +1294,12 @@ static size_t tx_tcp_ctrlresp(struct mg_tcpip_if *ifp, struct pkt *pkt, ipd.port = pkt->tcp->sport; if ((l2addr = get_return_l2addr(ifp, &ipd, false, pkt)) == NULL) return 0; // safety net for lousy networks - return tx_tcp(ifp, l2addr, &ips, &ipd, flags, seqno, ackno, NULL, 0); + return tx_tcp(ifp, l2addr, &ips, &ipd, 0, flags, seqno, ackno, NULL, 0); } static size_t tx_tcp_rst(struct mg_tcpip_if *ifp, struct pkt *pkt, bool toack) { - return tx_tcp_ctrlresp(ifp, pkt, (uint8_t)(toack ? TH_RST : (TH_RST | TH_ACK)), + return tx_tcp_ctrlresp(ifp, pkt, + (uint8_t) (toack ? TH_RST : (TH_RST | TH_ACK)), toack ? pkt->tcp->ack : 0); } @@ -1305,7 +1315,7 @@ static struct mg_connection *accept_conn(struct mg_connection *lsn, s = (struct connstate *) (c + 1); s->dmss = mss; // from options in client SYN s->seq = mg_ntohl(pkt->tcp->ack), s->ack = mg_ntohl(pkt->tcp->seq); - s->win = mg_ntohs(pkt->tcp->win), s->maxseq = (uint32_t)(s->seq + s->win); + s->win = mg_ntohs(pkt->tcp->win), s->maxseq = (uint32_t) (s->seq + s->win); #if MG_ENABLE_IPV6 if (lsn->loc.is_ip6) { c->rem.addr.ip6[0] = pkt->ip6->src[0], @@ -1390,7 +1400,7 @@ static bool udp_send(struct mg_connection *c, const void *buf, size_t len) { ips.addr.ip4 = ifp->ip; } ips.port = c->loc.port; - return tx_udp(ifp, s->mac, &ips, &c->rem, buf, len); + return tx_udp(ifp, s->mac, &ips, &c->rem, c->dscp, buf, len); } long mg_io_send(struct mg_connection *c, const void *buf, size_t len) { @@ -1405,7 +1415,7 @@ long mg_io_send(struct mg_connection *c, const void *buf, size_t len) { if (room == 0) return MG_IO_WAIT; if (len > s->dmss) len = s->dmss; // RFC-6691: reduce if sending opts if ((uint32_t) len > room) len = room; - sent = tx_tcp(ifp, s->mac, &c->loc, &c->rem, TH_PUSH | TH_ACK, + sent = tx_tcp(ifp, s->mac, &c->loc, &c->rem, c->dscp, TH_PUSH | TH_ACK, mg_htonl(s->seq), mg_htonl(s->ack), buf, len); if (sent == 0) { return MG_IO_WAIT; @@ -1420,16 +1430,16 @@ long mg_io_send(struct mg_connection *c, const void *buf, size_t len) { } static void handle_tls_recv(struct mg_connection *c) { - size_t avail = mg_tls_pending(c); // will change after mg_tls_recv() + size_t avail = mg_tls_pending(c); // will change after mg_tls_recv() size_t min = avail > MG_MAX_RECV_SIZE ? MG_MAX_RECV_SIZE : avail; - struct mg_iobuf *io = &c->recv; // allocated on first avail > 0 + struct mg_iobuf *io = &c->recv; // allocated on first avail > 0 if (io->size - io->len < min && !mg_iobuf_resize(io, io->len + min)) { mg_error(c, "oom"); } else { // Decrypt data directly into c->recv. If io->buf = NULL or // io->len = io->size (no room), there can be outstanding data that can't be - // moved (mg_tls_pending() > 0 after mg_tls_recv() returns). So mg_tls_recv() - // takes care of returning 0 in this case (commented as "MIP") + // moved (mg_tls_pending() > 0 after mg_tls_recv() returns). So + // mg_tls_recv() takes care of returning 0 in this case (commented as "MIP") long n = mg_tls_recv(c, io->buf != NULL ? &io->buf[io->len] : io->buf, io->size - io->len); if (n == MG_IO_ERR) { @@ -1444,7 +1454,7 @@ static void handle_tls_recv(struct mg_connection *c) { static void handle_ack(struct connstate *s, uint32_t ackno, uint16_t win) { if (ackno < (s->seq - s->win) || ackno > s->seq) return; - s->maxseq = (uint32_t)(ackno + win); + s->maxseq = (uint32_t) (ackno + win); s->win = win; } @@ -1456,8 +1466,8 @@ static void read_conn(struct mg_connection *c, struct pkt *pkt) { uint8_t flags = TH_ACK; if (mg_ntohl(pkt->tcp->seq) != s->ack) { MG_VERBOSE(("ignoring FIN, %x != %x", mg_ntohl(pkt->tcp->seq), s->ack)); - tx_tcp(c->mgr->ifp, s->mac, &c->loc, &c->rem, TH_ACK, mg_htonl(s->seq), - mg_htonl(s->ack), "", 0); + tx_tcp(c->mgr->ifp, s->mac, &c->loc, &c->rem, c->dscp, TH_ACK, + mg_htonl(s->seq), mg_htonl(s->ack), "", 0); return; } // If we initiated the closure, we reply with ACK upon receiving FIN @@ -1478,14 +1488,14 @@ static void read_conn(struct mg_connection *c, struct pkt *pkt) { // is flushed, then send our FIN. c->is_draining = 1; } - tx_tcp(c->mgr->ifp, s->mac, &c->loc, &c->rem, flags, mg_htonl(s->seq), - mg_htonl(s->ack), "", 0); + tx_tcp(c->mgr->ifp, s->mac, &c->loc, &c->rem, c->dscp, flags, + mg_htonl(s->seq), mg_htonl(s->ack), "", 0); if (pkt->pay.len == 0) return; // if no data, we're done } else if (pkt->pay.len <= 1 && mg_ntohl(pkt->tcp->seq) == s->ack - 1) { // Keep-Alive (RFC-9293 3.8.4, allow erroneous implementations) MG_VERBOSE(("%lu keepalive ACK", c->id)); - tx_tcp(c->mgr->ifp, s->mac, &c->loc, &c->rem, TH_ACK, mg_htonl(s->seq), - mg_htonl(s->ack), NULL, 0); + tx_tcp(c->mgr->ifp, s->mac, &c->loc, &c->rem, c->dscp, TH_ACK, + mg_htonl(s->seq), mg_htonl(s->ack), NULL, 0); return; // no data to process } else if (pkt->pay.len == 0) { // this is an ACK if (pkt->tcp->flags & TH_ACK) @@ -1498,8 +1508,8 @@ static void read_conn(struct mg_connection *c, struct pkt *pkt) { MG_VERBOSE(("ignoring duplicate pkt")); } else { MG_VERBOSE(("SEQ != ACK: %x %x %x", seq, s->ack, ack)); - tx_tcp(c->mgr->ifp, s->mac, &c->loc, &c->rem, TH_ACK, mg_htonl(s->seq), - mg_htonl(s->ack), "", 0); + tx_tcp(c->mgr->ifp, s->mac, &c->loc, &c->rem, c->dscp, TH_ACK, + mg_htonl(s->seq), mg_htonl(s->ack), "", 0); } return; // drop it } else if (io->size - io->len < pkt->pay.len && @@ -1507,8 +1517,8 @@ static void read_conn(struct mg_connection *c, struct pkt *pkt) { mg_error(c, "oom"); return; // drop it } - if (pkt->tcp->flags & TH_ACK) - handle_ack(s, mg_ntohl(pkt->tcp->ack), mg_ntohs(pkt->tcp->win)); + if (pkt->tcp->flags & TH_ACK) + handle_ack(s, mg_ntohl(pkt->tcp->ack), mg_ntohs(pkt->tcp->win)); // Copy TCP payload into the IO buffer. If the connection is plain text, // we copy to c->recv. If the connection is TLS, this data is encrypted, // therefore we copy that encrypted data to the c->rtls iobuffer instead, @@ -1524,8 +1534,8 @@ static void read_conn(struct mg_connection *c, struct pkt *pkt) { if (s->unacked > MG_TCPIP_WIN / 2 && s->acked != s->ack) { // Send ACK immediately MG_VERBOSE(("%lu imm ACK %lu", c->id, s->acked)); - tx_tcp(c->mgr->ifp, s->mac, &c->loc, &c->rem, TH_ACK, mg_htonl(s->seq), - mg_htonl(s->ack), NULL, 0); + tx_tcp(c->mgr->ifp, s->mac, &c->loc, &c->rem, c->dscp, TH_ACK, + mg_htonl(s->seq), mg_htonl(s->ack), NULL, 0); s->unacked = 0; s->acked = s->ack; if (s->ttype != MIP_TTYPE_KEEPALIVE) settmout(c, MIP_TTYPE_KEEPALIVE); @@ -1627,7 +1637,7 @@ static void rx_tcp(struct mg_tcpip_if *ifp, struct pkt *pkt) { if (!handle_opt(s, pkt->tcp, pkt->ip6 != NULL)) return; // process options (MSS) s->seq = mg_ntohl(pkt->tcp->ack), s->ack = mg_ntohl(pkt->tcp->seq) + 1; - s->win = mg_ntohs(pkt->tcp->win), s->maxseq = (uint32_t)(s->seq + s->win); + s->win = mg_ntohs(pkt->tcp->win), s->maxseq = (uint32_t) (s->seq + s->win); tx_tcp_ctrlresp(ifp, pkt, TH_ACK, pkt->tcp->ack); c->is_connecting = 0; // Client connected settmout(c, MIP_TTYPE_KEEPALIVE); @@ -2013,8 +2023,8 @@ static void mg_tcpip_poll(struct mg_tcpip_if *ifp, uint64_t now) { drv_up = ifp->driver->poll ? ifp->driver->poll(ifp, expired_1000ms) : true; l2_up = mg_l2_poll(ifp, expired_1000ms); // Handle L2 up/down link status; if (expired_1000ms) { // ifp->state rules over state6 - mg_ip_link(ifp, drv_up, l2_up); // Handle IPv4 - mg_ip6_link(ifp, drv_up, l2_up); // Handle IPv6 + mg_ip_link(ifp, drv_up, l2_up); // Handle IPv4 + mg_ip6_link(ifp, drv_up, l2_up); // Handle IPv6 if (ifp->state < MG_TCPIP_STATE_UP) MG_ERROR(("Network is down")); mg_tcpip_call(ifp, MG_TCPIP_EV_TIMER_1S, NULL); } @@ -2053,7 +2063,7 @@ static void mg_tcpip_poll(struct mg_tcpip_if *ifp, uint64_t now) { continue; } else if (s->ttype == MIP_TTYPE_ACK && s->acked != s->ack) { MG_VERBOSE(("%lu ack %x %x", c->id, s->seq, s->ack)); - tx_tcp(ifp, s->mac, &c->loc, &c->rem, TH_ACK, mg_htonl(s->seq), + tx_tcp(ifp, s->mac, &c->loc, &c->rem, c->dscp, TH_ACK, mg_htonl(s->seq), mg_htonl(s->ack), NULL, 0); s->acked = s->ack; } else if (s->ttype == MIP_TTYPE_SYN) { @@ -2066,8 +2076,8 @@ static void mg_tcpip_poll(struct mg_tcpip_if *ifp, uint64_t now) { mg_error(c, "keepalive"); } else { MG_VERBOSE(("%lu keepalive", c->id)); - tx_tcp(ifp, s->mac, &c->loc, &c->rem, TH_ACK, mg_htonl(s->seq - 1), - mg_htonl(s->ack), NULL, 0); + tx_tcp(ifp, s->mac, &c->loc, &c->rem, c->dscp, TH_ACK, + mg_htonl(s->seq - 1), mg_htonl(s->ack), NULL, 0); } } @@ -2130,7 +2140,8 @@ void mg_tcpip_free(struct mg_tcpip_if *ifp) { static void send_syn(struct mg_connection *c) { struct connstate *s = (struct connstate *) (c + 1); uint32_t isn = mg_htonl((uint32_t) mg_ntohs(c->loc.port)); - tx_tcp(c->mgr->ifp, s->mac, &c->loc, &c->rem, TH_SYN, isn, 0, NULL, 0); + tx_tcp(c->mgr->ifp, s->mac, &c->loc, &c->rem, c->dscp, TH_SYN, isn, 0, NULL, + 0); } static void l2addr_resolved(struct mg_connection *c) { @@ -2151,9 +2162,9 @@ void mg_connect_resolved(struct mg_connection *c) { c->loc.port = mg_htons(ifp->eport++); #if MG_ENABLE_IPV6 if (c->rem.is_ip6) { - if(c->rem.addr.ip6[0] == ifp->ip6ll[0]) { // same local LAN, use ll + if (c->rem.addr.ip6[0] == ifp->ip6ll[0]) { // same local LAN, use ll c->loc.addr.ip6[0] = ifp->ip6ll[0], c->loc.addr.ip6[1] = ifp->ip6ll[1]; - } else { // use global address + } else { // use global address c->loc.addr.ip6[0] = ifp->ip6[0], c->loc.addr.ip6[1] = ifp->ip6[1]; } c->loc.is_ip6 = true; @@ -2233,7 +2244,7 @@ static void init_closure(struct mg_connection *c) { struct connstate *s = (struct connstate *) (c + 1); if (c->is_udp == false && c->is_listening == false && c->is_connecting == false) { // For TCP conns, - tx_tcp(c->mgr->ifp, s->mac, &c->loc, &c->rem, TH_FIN | TH_ACK, + tx_tcp(c->mgr->ifp, s->mac, &c->loc, &c->rem, c->dscp, TH_FIN | TH_ACK, mg_htonl(s->seq), mg_htonl(s->ack), NULL, 0); settmout(c, MIP_TTYPE_FIN); } diff --git a/test/mip_test.c b/test/mip_test.c index ba67b044..077eac41 100644 --- a/test/mip_test.c +++ b/test/mip_test.c @@ -110,7 +110,8 @@ static void ph(struct mg_connection *c, int ev, void *ev_data) { } static void fn(struct mg_connection *c, int ev, void *ev_data) { - (void) c, (void) ev, (void) ev_data; + if (ev == MG_EV_ACCEPT) ASSERT(mg_dscp(c, MG_DSCP_AF(4, 1))); + (void) ev_data; } static void tcpclosure_fn(struct mg_connection *c, int ev, void *ev_data) { @@ -499,8 +500,11 @@ static void test_tcp_basics(bool ipv6) { if (ipv6) { ASSERT(i6->src[0] == mg_htonll(0x100000000000000) && i6->src[1] == 0 && i6->dst[0] == mg_htonll(0x200000000000000) && i6->dst[1] == 0); + ASSERT((uint8_t) (((i6->ver & 15) << 2) | (i6->label[0] >> 6)) == + MG_DSCP_AF(4, 1)); } else { ASSERT(i->src == mg_htonl(0x1000000) && i->dst == mg_htonl(0x2000000)); + ASSERT(i->tos == (MG_DSCP_AF(4, 1) << 2)); } // segment with seq_no way out of window @@ -1125,8 +1129,11 @@ static void test_tcp(bool ipv6) { } static void udp_fn(struct mg_connection *c, int ev, void *ev_data) { - if (ev == MG_EV_READ && c->recv.len == 2 && c->recv.buf[0] == 'p') + if (ev == MG_EV_OPEN) { + ASSERT(mg_dscp(c, MG_DSCP_AF(4, 1))); + } else if (ev == MG_EV_READ && c->recv.len == 2 && c->recv.buf[0] == 'p') { mg_send(c, "P90", 3); + } (void) ev_data; } @@ -1221,9 +1228,12 @@ static void test_udp(bool ipv6) { if (ipv6) { ASSERT(i6->src[0] == mg_htonll(0x100000000000000) && i6->src[1] == 0 && i6->dst[0] == mg_htonll(0x200000000000000) && i6->dst[1] == 0); + ASSERT((uint8_t) (((i6->ver & 15) << 2) | (i6->label[0] >> 6)) == + MG_DSCP_AF(4, 1)); ASSERT(udp6csum_ok(i6, u)); } else { ASSERT(i->src == mg_htonl(0x1000000) && i->dst == mg_htonl(0x2000000)); + ASSERT(i->tos == (MG_DSCP_AF(4, 1) << 2)); ASSERT(udpcsum_ok(i, u)); }