diff --git a/examples/mip-pcap/README.md b/examples/mip-pcap/README.md new file mode 100644 index 00000000..e96af74b --- /dev/null +++ b/examples/mip-pcap/README.md @@ -0,0 +1,47 @@ +#MIP TCP / IP stack over pcap + +This example allows to use Mongoose's MIP built-in TCP/IP stack on systems +that support pcap API, i.e. have libpcap library installed. The application +opens an interface and uses `pcap_next_ex()` for reading packets from the +interface, and `pcap_inject()` to write packets to the interface. + +NOTE: depending on the libcap implementation, the injected packets may +or may not be looped back to the interface's TCP/IP stack. If they are +not looped back, then it is necessary to create a separate interface that +is bridged to the target interface - see MacOS example below. + +## MacOS setup + +MacOS has support for `feth` (fake ethernet) interfaces. One can create a pair +of `feth` interfaces and interlink them. Once a `feth` interface is assigned +a peer and an IP address, anything that gets injected to it, appears on a +peer interface and vice versa. + +```sh +$ sudo ifconfig feth0 create +$ sudo ifconfig feth1 create +$ sudo ifconfig feth1 peer feth 0 # Link two fake ethernet ifaces together +$ sudo ifconfig feth1 10.10 # Assign 10.0.0.10 to feth1 +$ sudo ifconfig feth0 up +$ sudo ifconfig feth1 up +``` + +Now we have two Ethernet interfaces, `feth0` and `feth1`, interlinked and active. +On your Mac, go to "System Preferences" / Sharing, enable "Internet Sharing" +and choose "Thunderbolt bridge". This enables DHCP on the `bridge0` interface. +On my system it gets `192.168.2.1` IP address, and serves `192.168.2/24` net. +which is bridge for all Thunderbolt devices, and adds necessary routes to the +WiFi interface. We should add one of our fake interfaces to this bridge: + +```sh +$ sudo ifconfig bridge0 addm feth1 +``` + +We cat start an example using the `feth0`: + +```sh +$ make -C examples/mip-pcap/ clean all ARGS="-i feth0" +... +2386718 3 mip.c:279:arp_cache_add ARP cache: added 0xc0a80201 @ 36:77:4d:be:e0:80 +2386718 2 mip.c:300:onstatechange READY, IP: 192.168.2.17 +``` diff --git a/examples/mip-pcap/main.c b/examples/mip-pcap/main.c index 6589d3f7..0b1aace2 100644 --- a/examples/mip-pcap/main.c +++ b/examples/mip-pcap/main.c @@ -6,45 +6,22 @@ #include #include "mongoose.h" -static struct mg_connection *s_sntp_conn = NULL; +#define MQTT_URL "mqtt://broker.hivemq.com:1883" // MQTT broker to connect to +#define MQTT_TOPIC "t/123" // Topic to subscribe to static int s_signo; void signal_handler(int signo) { s_signo = signo; } -// SNTP client callback -static void sfn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) { - if (ev == MG_EV_SNTP_TIME) { - int64_t t = *(int64_t *) ev_data; - MG_INFO(("Got SNTP time: %lld ms from epoch", t)); - } else if (ev == MG_EV_CLOSE) { - s_sntp_conn = NULL; - } - (void) fn_data, (void) c; -} - -// Called every 5 seconds. Increase that for production case. -static void timer_fn(void *arg) { - struct mg_mgr *mgr = (struct mg_mgr *) arg; - if (s_sntp_conn == NULL) s_sntp_conn = mg_sntp_connect(mgr, NULL, sfn, NULL); - if (s_sntp_conn != NULL) mg_sntp_request(s_sntp_conn); -} - -static int fail(const char *fmt, ...) { - va_list ap; - va_start(ap, fmt); - vfprintf(stderr, fmt, ap); - va_end(ap); - return EXIT_FAILURE; -} - -static size_t pcap_tx(const void *data, size_t len, void *userdata) { - int res = pcap_inject((pcap_t *) userdata, data, len); +static size_t pcap_tx(const void *buf, size_t len, void *userdata) { + int res = pcap_inject((pcap_t *) userdata, buf, len); if (res == PCAP_ERROR) { MG_ERROR(("pcap_inject: %d", res)); } - return len; + MG_INFO(("TX %lu", len)); + // mg_hexdump(buf, len); + return res == PCAP_ERROR ? 0 : len; } static bool pcap_up(void *userdata) { @@ -58,21 +35,43 @@ static size_t pcap_rx(void *buf, size_t len, void *userdata) { if (pcap_next_ex((pcap_t *) userdata, &hdr, &pkt) == 1) { received = hdr->len < len ? hdr->len : len; memcpy(buf, pkt, received); + MG_INFO(("RX %lu", received)); + // mg_hexdump(buf, received); } return received; } +static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) { + if (ev == MG_EV_HTTP_MSG) mg_http_reply(c, 200, NULL, "hi\n"); + (void) fn_data, (void) ev_data; +} + +// MQTT event handler function +static void mfn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) { + if (ev == MG_EV_CONNECT && mg_url_is_ssl(MQTT_URL)) { + struct mg_tls_opts opts = {.ca = "ca.pem", + .srvname = mg_url_host(MQTT_URL)}; + mg_tls_init(c, &opts); + } else if (ev == MG_EV_MQTT_OPEN) { + c->is_hexdumping = 1; + mg_mqtt_sub(c, mg_str(MQTT_TOPIC), 2); + } else if (ev == MG_EV_MQTT_MSG) { + struct mg_mqtt_message *mm = ev_data; + MG_INFO(("%.*s", (int) mm->data.len, mm->data.ptr)); + } else if (ev == MG_EV_MQTT_CMD) { + struct mg_mqtt_message *mm = (struct mg_mqtt_message *) ev_data; + MG_DEBUG(("cmd %d qos %d", mm->cmd, mm->qos)); + } else if (ev == MG_EV_CLOSE) { + } + (void) fn_data; +} + int main(int argc, char *argv[]) { const char *iface = "lo0"; // Network iface - const char *mac = "aa:bb:cc:01:02:03"; // MAC address + const char *mac = "00:00:01:02:03:77"; // MAC address const char *bpf = NULL; // "host x.x.x.x or ether host ff:ff:ff:ff:ff:ff"; char errbuf[PCAP_ERRBUF_SIZE] = ""; - struct mg_mgr mgr; // Event manager - mg_mgr_init(&mgr); // Initialise event manager - mg_log_set(MG_LL_DEBUG); // Set debug log level - mg_timer_add(&mgr, 3000, MG_TIMER_REPEAT | MG_TIMER_RUN_NOW, timer_fn, &mgr); - // Parse options for (int i = 1; i < argc; i++) { if (strcmp(argv[i], "-i") == 0 && i + 1 < argc) { @@ -82,7 +81,8 @@ int main(int argc, char *argv[]) { } else if (strcmp(argv[i], "-bpf") == 0 && i + 1 < argc) { bpf = argv[++i]; } else { - return fail("unknown option %s", argv[i]); + MG_ERROR(("unknown option %s", argv[i])); + return EXIT_FAILURE; } } @@ -97,21 +97,27 @@ int main(int argc, char *argv[]) { } pcap_freealldevs(devs); } - fail("pcap_open_live: %s\n", errbuf); + return EXIT_FAILURE; } pcap_setnonblock(ph, 1, errbuf); + pcap_setdirection(ph, PCAP_D_IN); + // Apply BPF to reduce noise. Let in only broadcasts and our own traffic if (bpf != NULL) { struct bpf_program bpfp; - if (pcap_compile(ph, &bpfp, bpf, 1, 0)) fail("compile \n"); + if (pcap_compile(ph, &bpfp, bpf, 1, 0)) MG_ERROR(("BPF compile failed\n")); pcap_setfilter(ph, &bpfp); pcap_freecode(&bpfp); } - MG_INFO(("Opened interface %s\n", iface)); + MG_INFO(("Opened interface %s", iface)); signal(SIGINT, signal_handler); signal(SIGTERM, signal_handler); + struct mg_mgr mgr; // Event manager + mg_mgr_init(&mgr); // Initialise event manager + mg_log_set(MG_LL_DEBUG); // Set debug log level + struct mip_cfg c = {.ip = 0, .mask = 0, .gw = 0}; sscanf(mac, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", &c.mac[0], &c.mac[1], &c.mac[2], &c.mac[3], &c.mac[4], &c.mac[5]); @@ -120,6 +126,11 @@ int main(int argc, char *argv[]) { mip_init(&mgr, &c, &driver, ph); MG_INFO(("Init done, starting main loop")); + mg_http_listen(&mgr, "http://0.0.0.0:8000", fn, NULL); + + struct mg_mqtt_opts opts = {0}; + // mg_mqtt_connect(&mgr, MQTT_URL, &opts, mfn, NULL); + while (s_signo == 0) mg_mgr_poll(&mgr, 1); // Infinite event loop mg_mgr_free(&mgr); diff --git a/mip/mip.c b/mip/mip.c index 2fefc28e..69996839 100644 --- a/mip/mip.c +++ b/mip/mip.c @@ -17,6 +17,12 @@ #endif #define MIP_ARP_CS (2 + 12 * MIP_ARP_ENTRIES) // ARP cache size +struct connstate { + uint32_t seq, ack; // TCP seq/ack counters + uint64_t timer; // Used to send ACKs + uint8_t mac[6]; // Peer MAC address +}; + struct str { uint8_t *buf; size_t len; @@ -238,14 +244,22 @@ static void arp_cache_init(uint8_t *p, int n, int size) { p[1] = p[3 + (n - 1) * size] = 2; } +static inline void arp_cache_dump(const uint8_t *p) { + MG_INFO(("ARP cache:")); + for (uint8_t i = 0, j = p[1]; i < MIP_ARP_ENTRIES; i++, j = p[j + 1]) { + MG_INFO((" %d.%d.%d.%d -> %x:%x:%x:%x:%x:%x", p[j + 2], p[j + 3], p[j + 4], + p[j + 5], p[j + 6], p[j + 7], p[j + 8], p[j + 9], p[j + 10], + p[j + 11])); + } +} + static uint8_t *arp_cache_find(struct mip_if *ifp, uint32_t ip) { uint8_t *p = ifp->arp_cache; - if (ip == 0) return NULL; - if (p[0] == 0 || p[1] == 0) arp_cache_init(p, MIP_ARP_ENTRIES, 12); + if (ip == 0 || ip == 0xffffffffU) return NULL; for (uint8_t i = 0, j = p[1]; i < MIP_ARP_ENTRIES; i++, j = p[j + 1]) { if (memcmp(p + j + 2, &ip, sizeof(ip)) == 0) { p[1] = j, p[0] = p[j]; // Found entry! Point list head to us - // MG_DEBUG(("ARP find: %#lx @ %x:%x:%x:%x:%x:%x\n", (long) ip, p[j + 6], + // MG_DEBUG(("ARP find: %#lx @ %x:%x:%x:%x:%x:%x", (long) ip, p[j + 6], // p[j + 7], p[j + 8], p[j + 9], p[j + 10], p[j + 11])); return p + j + 6; // And return MAC address } @@ -285,9 +299,9 @@ static void onstatechange(struct mip_if *ifp) { MG_INFO(("READY, IP: %s", mg_ntoa(&addr, buf, sizeof(buf)))); arp_ask(ifp, ifp->gw); } else if (ifp->state == MIP_STATE_UP) { - MG_ERROR(("Network up")); + MG_ERROR(("Link up")); } else if (ifp->state == MIP_STATE_DOWN) { - MG_ERROR(("Network down")); + MG_ERROR(("Link down")); } } @@ -330,7 +344,7 @@ static void tx_udp(struct mip_if *ifp, uint32_t ip_src, uint16_t sport, cs += (uint32_t) (ip->proto + sizeof(*udp) + len); udp->csum = csumfin(cs); memmove(udp + 1, buf, len); - // MG_DEBUG(("UDP LEN %d %d\n", (int) len, (int) ifp->frame_len)); + // MG_DEBUG(("UDP LEN %d %d", (int) len, (int) ifp->frame_len)); ifp->driver->tx(ifp->tx.buf, sizeof(struct eth) + sizeof(*ip) + sizeof(*udp) + len, ifp->driver_data); @@ -361,6 +375,7 @@ static void tx_dhcp_request(struct mip_if *ifp, uint32_t src, uint32_t dst) { memcpy(opts + 14, &dst, sizeof(dst)); memcpy(opts + 20, &src, sizeof(src)); tx_dhcp(ifp, src, dst, opts, sizeof(opts)); + MG_DEBUG(("DHCP request sent")); } static void tx_dhcp_discover(struct mip_if *ifp) { @@ -370,6 +385,7 @@ static void tx_dhcp_discover(struct mip_if *ifp) { 255 // End of options }; tx_dhcp(ifp, 0, 0xffffffff, opts, sizeof(opts)); + MG_DEBUG(("DHCP discover sent")); } static void rx_arp(struct mip_if *ifp, struct pkt *pkt) { @@ -397,7 +413,7 @@ static void rx_arp(struct mip_if *ifp, struct pkt *pkt) { } static void rx_icmp(struct mip_if *ifp, struct pkt *pkt) { - // MG_DEBUG(("ICMP %d\n", (int) len)); + // MG_DEBUG(("ICMP %d", (int) len)); if (pkt->icmp->type == 8 && pkt->ip->dst == ifp->ip) { struct ip *ip = tx_ip(ifp, 1, ifp->ip, pkt->ip->src, sizeof(struct icmp) + pkt->pay.len); @@ -414,21 +430,21 @@ static void rx_dhcp(struct mip_if *ifp, struct pkt *pkt) { uint32_t ip = 0, gw = 0, mask = 0; uint8_t *p = pkt->dhcp->options, *end = &pkt->raw.buf[pkt->raw.len]; if (end < (uint8_t *) (pkt->dhcp + 1)) return; - // MG_DEBUG(("DHCP %u\n", (unsigned) pkt->raw.len)); + MG_DEBUG(("DHCP %u", (unsigned) pkt->raw.len)); while (p < end && p[0] != 255) { if (p[0] == 1 && p[1] == sizeof(ifp->mask)) { memcpy(&mask, p + 2, sizeof(mask)); - // MG_DEBUG(("MASK %x\n", mask)); + // MG_DEBUG(("MASK %x", mask)); } else if (p[0] == 3 && p[1] == sizeof(ifp->gw)) { memcpy(&gw, p + 2, sizeof(gw)); ip = pkt->dhcp->yiaddr; - // MG_DEBUG(("IP %x GW %x\n", ip, gw)); + // MG_DEBUG(("IP %x GW %x", ip, gw)); } p += p[1] + 2; } if (ip && mask && gw && ifp->ip == 0) { - // MG_DEBUG(("DHCP offer ip %#08lx mask %#08lx gw %#08lx\n", - // (long) ip, (long) mask, (long) gw)); + // MG_DEBUG(("DHCP offer ip %#08lx mask %#08lx gw %#08lx", (long) ip, + // (long) mask, (long) gw)); arp_cache_add(ifp, pkt->dhcp->siaddr, ((struct eth *) pkt->raw.buf)->src); ifp->ip = ip, ifp->gw = gw, ifp->mask = mask; ifp->state = MIP_STATE_READY; @@ -470,11 +486,6 @@ static void rx_udp(struct mip_if *ifp, struct pkt *pkt) { } } -struct tcpstate { - uint32_t seq, ack; - time_t expire; -}; - static size_t tx_tcp(struct mip_if *ifp, uint32_t dst_ip, uint8_t flags, uint16_t sport, uint16_t dport, uint32_t seq, uint32_t ack, const void *buf, size_t len) { @@ -511,7 +522,7 @@ static size_t tx_tcp_pkt(struct mip_if *ifp, struct pkt *pkt, uint8_t flags, static struct mg_connection *accept_conn(struct mg_connection *lsn, struct pkt *pkt) { struct mg_connection *c = mg_alloc_conn(lsn->mgr); - struct tcpstate *s = (struct tcpstate *) (c + 1); + struct connstate *s = (struct connstate *) (c + 1); s->seq = mg_ntohl(pkt->tcp->ack), s->ack = mg_ntohl(pkt->tcp->seq); c->rem.ip = pkt->ip->src; c->rem.port = pkt->tcp->sport; @@ -530,8 +541,8 @@ static struct mg_connection *accept_conn(struct mg_connection *lsn, return c; } -static bool read_conn(struct mg_connection *c, struct pkt *pkt) { - struct tcpstate *s = (struct tcpstate *) (c + 1); +static void read_conn(struct mg_connection *c, struct pkt *pkt) { + struct connstate *s = (struct connstate *) (c + 1); if (pkt->tcp->flags & TH_FIN) { s->ack = mg_htonl(pkt->tcp->seq) + 1, s->seq = mg_htonl(pkt->tcp->ack); c->is_closing = 1; @@ -547,9 +558,7 @@ static bool read_conn(struct mg_connection *c, struct pkt *pkt) { c->recv.len += pkt->pay.len; struct mg_str evd = mg_str_n((char *) pkt->pay.buf, pkt->pay.len); mg_call(c, MG_EV_READ, &evd); - return true; } - return false; } static void rx_tcp(struct mip_if *ifp, struct pkt *pkt) { @@ -558,7 +567,7 @@ static void rx_tcp(struct mip_if *ifp, struct pkt *pkt) { MG_INFO(("%lu %hhu %d", c ? c->id : 0, pkt->tcp->flags, (int) pkt->pay.len)); #endif if (c != NULL && c->is_connecting && pkt->tcp->flags & (TH_SYN | TH_ACK)) { - struct tcpstate *s = (struct tcpstate *) (c + 1); + struct connstate *s = (struct connstate *) (c + 1); s->seq = mg_ntohl(pkt->tcp->ack), s->ack = mg_ntohl(pkt->tcp->seq) + 1; tx_tcp_pkt(ifp, pkt, TH_ACK, pkt->tcp->ack, NULL, 0); c->is_connecting = 0; // Client connected @@ -571,14 +580,7 @@ static void rx_tcp(struct mip_if *ifp, struct pkt *pkt) { mg_ntohl(pkt->ip->dst), mg_ntohs(pkt->tcp->dport))); mg_hexdump(pkt->pay.buf, pkt->pay.len); #endif - if (read_conn(c, pkt)) { - // Send ACK immediately, no piggyback yet - // TODO() Set a timer and send ACK if timer expires and no segment was - // sent ? (clear timer on segment sent) - struct tcpstate *s = (struct tcpstate *) (c + 1); - tx_tcp(ifp, c->rem.ip, TH_ACK, c->loc.port, c->rem.port, mg_htonl(s->seq), - mg_htonl(s->ack), NULL, 0); - } + read_conn(c, pkt); } else if ((c = getpeer(ifp->mgr, pkt, true)) == NULL) { tx_tcp_pkt(ifp, pkt, TH_RST | TH_ACK, pkt->tcp->ack, NULL, 0); } else if (pkt->tcp->flags & TH_SYN) { @@ -604,7 +606,7 @@ static void rx_ip(struct mip_if *ifp, struct pkt *pkt) { } else if (pkt->ip->proto == 17) { pkt->udp = (struct udp *) (pkt->ip + 1); if (pkt->pay.len < sizeof(*pkt->udp)) return; - // MG_DEBUG((" UDP %u %u -> %u\n", len, mg_htons(udp->sport), + // MG_DEBUG((" UDP %u %u -> %u", len, mg_htons(udp->sport), // mg_htons(udp->dport))); mkpay(pkt, pkt->udp + 1); if (pkt->udp->dport == mg_htons(68)) { @@ -626,7 +628,7 @@ static void rx_ip(struct mip_if *ifp, struct pkt *pkt) { } static void rx_ip6(struct mip_if *ifp, struct pkt *pkt) { - // MG_DEBUG(("IP %d\n", (int) len)); + // MG_DEBUG(("IP %d", (int) len)); if (pkt->ip6->proto == 1 || pkt->ip6->proto == 58) { pkt->icmp = (struct icmp *) (pkt->ip6 + 1); if (pkt->pay.len < sizeof(*pkt->icmp)) return; @@ -635,7 +637,7 @@ static void rx_ip6(struct mip_if *ifp, struct pkt *pkt) { } else if (pkt->ip->proto == 17) { pkt->udp = (struct udp *) (pkt->ip6 + 1); if (pkt->pay.len < sizeof(*pkt->udp)) return; - // MG_DEBUG((" UDP %u %u -> %u\n", len, mg_htons(udp->sport), + // MG_DEBUG((" UDP %u %u -> %u", len, mg_htons(udp->sport), // mg_htons(udp->dport))); mkpay(pkt, pkt->udp + 1); } @@ -666,7 +668,7 @@ static void mip_rx(struct mip_if *ifp, void *buf, size_t len) { mkpay(&pkt, pkt.ip + 1); rx_ip(ifp, &pkt); } else { - MG_DEBUG((" Unknown eth type %x\n", mg_htons(pkt.eth->type))); + MG_DEBUG((" Unknown eth type %x", mg_htons(pkt.eth->type))); } } @@ -674,13 +676,6 @@ static void mip_poll(struct mip_if *ifp, uint64_t uptime_ms) { if (ifp == NULL || ifp->driver == NULL) return; bool expired_1000ms = mg_timer_expired(&ifp->timer_1000ms, 1000, uptime_ms); - if (ifp->ip == 0 && expired_1000ms) { - tx_dhcp_discover(ifp); // If IP not configured, send DHCP - } else if (ifp->use_dhcp == false && expired_1000ms && - arp_cache_find(ifp, ifp->gw) == NULL) { - arp_ask(ifp, ifp->gw); // If GW's MAC address in not in ARP cache - } - // Handle physical interface up/down status if (expired_1000ms && ifp->driver->up) { bool up = ifp->driver->up(ifp->driver_data); @@ -693,6 +688,15 @@ static void mip_poll(struct mip_if *ifp, uint64_t uptime_ms) { onstatechange(ifp); } } + if (ifp->state == MIP_STATE_DOWN) return; + // if (expired_1000ms) arp_cache_dump(ifp->arp_cache); + + if (ifp->ip == 0 && expired_1000ms) { + tx_dhcp_discover(ifp); // If IP not configured, send DHCP + } else if (ifp->use_dhcp == false && expired_1000ms && + arp_cache_find(ifp, ifp->gw) == NULL) { + arp_ask(ifp, ifp->gw); // If GW's MAC address in not in ARP cache + } // Read data from the network for (;;) { @@ -730,9 +734,11 @@ void mip_init(struct mg_mgr *mgr, struct mip_cfg *ipcfg, ifp->mgr = mgr; ifp->queue.buf = ifp->tx.buf + maxpktsize; ifp->queue.len = qlen; + ifp->timer_1000ms = mg_millis(); + arp_cache_init(ifp->arp_cache, MIP_ARP_ENTRIES, 12); if (driver->setrx) driver->setrx(on_rx, ifp); mgr->priv = ifp; - mgr->extraconnsize = sizeof(struct tcpstate); + mgr->extraconnsize = sizeof(struct connstate); } } @@ -776,7 +782,7 @@ bool mg_open_listener(struct mg_connection *c, const char *url) { static void write_conn(struct mg_connection *c) { struct mip_if *ifp = (struct mip_if *) c->mgr->priv; - struct tcpstate *s = (struct tcpstate *) (c + 1); + struct connstate *s = (struct connstate *) (c + 1); size_t sent, n = c->send.len, hdrlen = 14 + 24 /*max IP*/ + 60 /*max TCP*/; if (n + hdrlen > ifp->tx.len) n = ifp->tx.len - hdrlen; sent = tx_tcp(ifp, c->rem.ip, TH_PUSH | TH_ACK, c->loc.port, c->rem.port, @@ -790,7 +796,8 @@ static void write_conn(struct mg_connection *c) { static void fin_conn(struct mg_connection *c) { struct mip_if *ifp = (struct mip_if *) c->mgr->priv; - struct tcpstate *s = (struct tcpstate *) (c + 1); + struct connstate *s = (struct connstate *) (c + 1); + MG_INFO(("AAA")); tx_tcp(ifp, c->rem.ip, TH_FIN | TH_ACK, c->loc.port, c->rem.port, mg_htonl(s->seq), mg_htonl(s->ack), NULL, 0); } diff --git a/mongoose.c b/mongoose.c index cbe0bc80..25bd7323 100644 --- a/mongoose.c +++ b/mongoose.c @@ -6061,15 +6061,15 @@ static bool mip_driver_stm32_init(uint8_t *mac, void *userdata) { // NOTE(cpq): we do not use extended descriptor bit 7, and do not use // hardware checksum. Therefore, descriptor size is 4, not 8 // ETH->DMABMR = BIT(13) | BIT(16) | BIT(22) | BIT(23) | BIT(25); - ETH->MACIMR = BIT(3) | BIT(9); // Mask timestamp & PMT IT - ETH->MACMIIAR = cr_guess(hclk_get()) << 2; // MDC clock - ETH->MACFCR = BIT(7); // Disable zero quarta pause - ETH->MACFFR = BIT(31); // Receive all - eth_write_phy(PHY_ADDR, PHY_BCR, BIT(15)); // Reset PHY - eth_write_phy(PHY_ADDR, PHY_BCR, BIT(12)); // Set autonegotiation - ETH->DMARDLAR = (uint32_t) (uintptr_t) s_rxdesc; // RX descriptors - ETH->DMATDLAR = (uint32_t) (uintptr_t) s_txdesc; // RX descriptors - ETH->DMAIER = BIT(6) | BIT(16); // RIE, NISE + ETH->MACIMR = BIT(3) | BIT(9); // Mask timestamp & PMT IT + ETH->MACMIIAR = cr_guess(hclk_get()) << 2; // MDC clock + ETH->MACFCR = BIT(7); // Disable zero quarta pause + ETH->MACFFR = BIT(31); // Receive all + eth_write_phy(PHY_ADDR, PHY_BCR, BIT(15)); // Reset PHY + eth_write_phy(PHY_ADDR, PHY_BCR, BIT(12)); // Set autonegotiation + ETH->DMARDLAR = (uint32_t) (uintptr_t) s_rxdesc; // RX descriptors + ETH->DMATDLAR = (uint32_t) (uintptr_t) s_txdesc; // RX descriptors + ETH->DMAIER = BIT(6) | BIT(16); // RIE, NISE ETH->MACCR = BIT(2) | BIT(3) | BIT(11) | BIT(14); // RE, TE, Duplex, Fast ETH->DMAOMR = BIT(1) | BIT(13) | BIT(21) | BIT(25); // SR, ST, TSF, RSF @@ -6134,8 +6134,8 @@ struct mip_driver mip_driver_stm32 = {.init = mip_driver_stm32_init, .setrx = mip_driver_stm32_setrx, .up = mip_driver_stm32_up}; -/* Calculate HCLK from clock settings, - valid for STM32F74xxx/75xxx (5.3) and STM32F42xxx/43xxx (6.3) */ +// Calculate HCLK from clock settings, +// valid for STM32F74xxx/75xxx (5.3) and STM32F42xxx/43xxx (6.3) static const uint8_t ahbptab[8] = {1, 2, 3, 4, 6, 7, 8, 9}; // log2(div) struct rcc { volatile uint32_t CR, PLLCFGR, CFGR; @@ -6155,8 +6155,8 @@ static uint32_t hclk_get(void) { clk = MG_STM32_CLK_HSE; else clk = MG_STM32_CLK_HSI; - vco = (uint32_t)((uint64_t)(((uint32_t) clk * (uint32_t) n)) / - ((uint32_t) m)); + vco = (uint32_t) ((uint64_t) (((uint32_t) clk * (uint32_t) n)) / + ((uint32_t) m)); clk = vco / p; } else { clk = MG_STM32_CLK_HSI; @@ -6166,14 +6166,14 @@ static uint32_t hclk_get(void) { return ((uint32_t) clk) >> ahbptab[hpre - 8]; } -/* Guess CR from HCLK: -MDC clock is generated from HCLK (AHB); as per 802.3, it must not exceed 2.5MHz -As the AHB clock can be (and usually is) derived from the HSI (internal RC), -and it can go above specs, the datasheets specify a range of frequencies and -activate one of a series of dividers to keep the MDC clock safely below 2.5MHz. -We guess a divider setting based on HCLK with a +5% drift. -If the user uses a different clock from our defaults, needs to set the macros on top -Valid for STM32F74xxx/75xxx (38.8.1) and STM32F42xxx/43xxx (33.8.1) (both 4.5% worst case drift) */ +// Guess CR from HCLK. MDC clock is generated from HCLK (AHB); as per 802.3, +// it must not exceed 2.5MHz As the AHB clock can be (and usually is) derived +// from the HSI (internal RC), and it can go above specs, the datasheets +// specify a range of frequencies and activate one of a series of dividers to +// keep the MDC clock safely below 2.5MHz. We guess a divider setting based on +// HCLK with a +5% drift. If the user uses a different clock from our +// defaults, needs to set the macros on top Valid for STM32F74xxx/75xxx +// (38.8.1) and STM32F42xxx/43xxx (33.8.1) (both 4.5% worst case drift) #define CRDTAB_LEN 6 static const uint8_t crdtab[CRDTAB_LEN][2] = { // [{setting, div ratio},...] @@ -6306,6 +6306,12 @@ struct mip_driver mip_driver_w5500 = { #endif #define MIP_ARP_CS (2 + 12 * MIP_ARP_ENTRIES) // ARP cache size +struct connstate { + uint32_t seq, ack; // TCP seq/ack counters + uint64_t timer; // Used to send ACKs + uint8_t mac[6]; // Peer MAC address +}; + struct str { uint8_t *buf; size_t len; @@ -6527,10 +6533,18 @@ static void arp_cache_init(uint8_t *p, int n, int size) { p[1] = p[3 + (n - 1) * size] = 2; } +static inline void arp_cache_dump(const uint8_t *p) { + MG_INFO(("ARP cache:")); + for (uint8_t i = 0, j = p[1]; i < MIP_ARP_ENTRIES; i++, j = p[j + 1]) { + MG_INFO((" %d.%d.%d.%d -> %x:%x:%x:%x:%x:%x", p[j + 2], p[j + 3], p[j + 4], + p[j + 5], p[j + 6], p[j + 7], p[j + 8], p[j + 9], p[j + 10], + p[j + 11])); + } +} + static uint8_t *arp_cache_find(struct mip_if *ifp, uint32_t ip) { uint8_t *p = ifp->arp_cache; - if (ip == 0) return NULL; - if (p[0] == 0 || p[1] == 0) arp_cache_init(p, MIP_ARP_ENTRIES, 12); + if (ip == 0 || ip == 0xffffffffU) return NULL; for (uint8_t i = 0, j = p[1]; i < MIP_ARP_ENTRIES; i++, j = p[j + 1]) { if (memcmp(p + j + 2, &ip, sizeof(ip)) == 0) { p[1] = j, p[0] = p[j]; // Found entry! Point list head to us @@ -6574,9 +6588,9 @@ static void onstatechange(struct mip_if *ifp) { MG_INFO(("READY, IP: %s", mg_ntoa(&addr, buf, sizeof(buf)))); arp_ask(ifp, ifp->gw); } else if (ifp->state == MIP_STATE_UP) { - MG_ERROR(("Network up")); + MG_ERROR(("Link up")); } else if (ifp->state == MIP_STATE_DOWN) { - MG_ERROR(("Network down")); + MG_ERROR(("Link down")); } } @@ -6650,6 +6664,7 @@ static void tx_dhcp_request(struct mip_if *ifp, uint32_t src, uint32_t dst) { memcpy(opts + 14, &dst, sizeof(dst)); memcpy(opts + 20, &src, sizeof(src)); tx_dhcp(ifp, src, dst, opts, sizeof(opts)); + MG_DEBUG(("DHCP request sent")); } static void tx_dhcp_discover(struct mip_if *ifp) { @@ -6659,6 +6674,7 @@ static void tx_dhcp_discover(struct mip_if *ifp) { 255 // End of options }; tx_dhcp(ifp, 0, 0xffffffff, opts, sizeof(opts)); + MG_DEBUG(("DHCP discover sent")); } static void rx_arp(struct mip_if *ifp, struct pkt *pkt) { @@ -6703,7 +6719,7 @@ static void rx_dhcp(struct mip_if *ifp, struct pkt *pkt) { uint32_t ip = 0, gw = 0, mask = 0; uint8_t *p = pkt->dhcp->options, *end = &pkt->raw.buf[pkt->raw.len]; if (end < (uint8_t *) (pkt->dhcp + 1)) return; - // MG_DEBUG(("DHCP %u\n", (unsigned) pkt->raw.len)); + MG_DEBUG(("DHCP %u\n", (unsigned) pkt->raw.len)); while (p < end && p[0] != 255) { if (p[0] == 1 && p[1] == sizeof(ifp->mask)) { memcpy(&mask, p + 2, sizeof(mask)); @@ -6716,8 +6732,8 @@ static void rx_dhcp(struct mip_if *ifp, struct pkt *pkt) { p += p[1] + 2; } if (ip && mask && gw && ifp->ip == 0) { - // MG_DEBUG(("DHCP offer ip %#08lx mask %#08lx gw %#08lx\n", - // (long) ip, (long) mask, (long) gw)); + // MG_DEBUG(("DHCP offer ip %#08lx mask %#08lx gw %#08lx\n", (long) ip, + // (long) mask, (long) gw)); arp_cache_add(ifp, pkt->dhcp->siaddr, ((struct eth *) pkt->raw.buf)->src); ifp->ip = ip, ifp->gw = gw, ifp->mask = mask; ifp->state = MIP_STATE_READY; @@ -6759,11 +6775,6 @@ static void rx_udp(struct mip_if *ifp, struct pkt *pkt) { } } -struct tcpstate { - uint32_t seq, ack; - time_t expire; -}; - static size_t tx_tcp(struct mip_if *ifp, uint32_t dst_ip, uint8_t flags, uint16_t sport, uint16_t dport, uint32_t seq, uint32_t ack, const void *buf, size_t len) { @@ -6800,7 +6811,7 @@ static size_t tx_tcp_pkt(struct mip_if *ifp, struct pkt *pkt, uint8_t flags, static struct mg_connection *accept_conn(struct mg_connection *lsn, struct pkt *pkt) { struct mg_connection *c = mg_alloc_conn(lsn->mgr); - struct tcpstate *s = (struct tcpstate *) (c + 1); + struct connstate *s = (struct connstate *) (c + 1); s->seq = mg_ntohl(pkt->tcp->ack), s->ack = mg_ntohl(pkt->tcp->seq); c->rem.ip = pkt->ip->src; c->rem.port = pkt->tcp->sport; @@ -6819,8 +6830,8 @@ static struct mg_connection *accept_conn(struct mg_connection *lsn, return c; } -static bool read_conn(struct mg_connection *c, struct pkt *pkt) { - struct tcpstate *s = (struct tcpstate *) (c + 1); +static void read_conn(struct mg_connection *c, struct pkt *pkt) { + struct connstate *s = (struct connstate *) (c + 1); if (pkt->tcp->flags & TH_FIN) { s->ack = mg_htonl(pkt->tcp->seq) + 1, s->seq = mg_htonl(pkt->tcp->ack); c->is_closing = 1; @@ -6836,9 +6847,7 @@ static bool read_conn(struct mg_connection *c, struct pkt *pkt) { c->recv.len += pkt->pay.len; struct mg_str evd = mg_str_n((char *) pkt->pay.buf, pkt->pay.len); mg_call(c, MG_EV_READ, &evd); - return true; } - return false; } static void rx_tcp(struct mip_if *ifp, struct pkt *pkt) { @@ -6847,7 +6856,7 @@ static void rx_tcp(struct mip_if *ifp, struct pkt *pkt) { MG_INFO(("%lu %hhu %d", c ? c->id : 0, pkt->tcp->flags, (int) pkt->pay.len)); #endif if (c != NULL && c->is_connecting && pkt->tcp->flags & (TH_SYN | TH_ACK)) { - struct tcpstate *s = (struct tcpstate *) (c + 1); + struct connstate *s = (struct connstate *) (c + 1); s->seq = mg_ntohl(pkt->tcp->ack), s->ack = mg_ntohl(pkt->tcp->seq) + 1; tx_tcp_pkt(ifp, pkt, TH_ACK, pkt->tcp->ack, NULL, 0); c->is_connecting = 0; // Client connected @@ -6860,14 +6869,7 @@ static void rx_tcp(struct mip_if *ifp, struct pkt *pkt) { mg_ntohl(pkt->ip->dst), mg_ntohs(pkt->tcp->dport))); mg_hexdump(pkt->pay.buf, pkt->pay.len); #endif - if (read_conn(c, pkt)) { - // Send ACK immediately, no piggyback yet - // TODO() Set a timer and send ACK if timer expires and no segment was - // sent ? (clear timer on segment sent) - struct tcpstate *s = (struct tcpstate *) (c + 1); - tx_tcp(ifp, c->rem.ip, TH_ACK, c->loc.port, c->rem.port, mg_htonl(s->seq), - mg_htonl(s->ack), NULL, 0); - } + read_conn(c, pkt); } else if ((c = getpeer(ifp->mgr, pkt, true)) == NULL) { tx_tcp_pkt(ifp, pkt, TH_RST | TH_ACK, pkt->tcp->ack, NULL, 0); } else if (pkt->tcp->flags & TH_SYN) { @@ -6963,13 +6965,6 @@ static void mip_poll(struct mip_if *ifp, uint64_t uptime_ms) { if (ifp == NULL || ifp->driver == NULL) return; bool expired_1000ms = mg_timer_expired(&ifp->timer_1000ms, 1000, uptime_ms); - if (ifp->ip == 0 && expired_1000ms) { - tx_dhcp_discover(ifp); // If IP not configured, send DHCP - } else if (ifp->use_dhcp == false && expired_1000ms && - arp_cache_find(ifp, ifp->gw) == NULL) { - arp_ask(ifp, ifp->gw); // If GW's MAC address in not in ARP cache - } - // Handle physical interface up/down status if (expired_1000ms && ifp->driver->up) { bool up = ifp->driver->up(ifp->driver_data); @@ -6982,6 +6977,15 @@ static void mip_poll(struct mip_if *ifp, uint64_t uptime_ms) { onstatechange(ifp); } } + if (ifp->state == MIP_STATE_DOWN) return; + // if (expired_1000ms) arp_cache_dump(ifp->arp_cache); + + if (ifp->ip == 0 && expired_1000ms) { + tx_dhcp_discover(ifp); // If IP not configured, send DHCP + } else if (ifp->use_dhcp == false && expired_1000ms && + arp_cache_find(ifp, ifp->gw) == NULL) { + arp_ask(ifp, ifp->gw); // If GW's MAC address in not in ARP cache + } // Read data from the network for (;;) { @@ -7019,9 +7023,11 @@ void mip_init(struct mg_mgr *mgr, struct mip_cfg *ipcfg, ifp->mgr = mgr; ifp->queue.buf = ifp->tx.buf + maxpktsize; ifp->queue.len = qlen; + ifp->timer_1000ms = mg_millis(); + arp_cache_init(ifp->arp_cache, MIP_ARP_ENTRIES, 12); if (driver->setrx) driver->setrx(on_rx, ifp); mgr->priv = ifp; - mgr->extraconnsize = sizeof(struct tcpstate); + mgr->extraconnsize = sizeof(struct connstate); } } @@ -7065,7 +7071,7 @@ bool mg_open_listener(struct mg_connection *c, const char *url) { static void write_conn(struct mg_connection *c) { struct mip_if *ifp = (struct mip_if *) c->mgr->priv; - struct tcpstate *s = (struct tcpstate *) (c + 1); + struct connstate *s = (struct connstate *) (c + 1); size_t sent, n = c->send.len, hdrlen = 14 + 24 /*max IP*/ + 60 /*max TCP*/; if (n + hdrlen > ifp->tx.len) n = ifp->tx.len - hdrlen; sent = tx_tcp(ifp, c->rem.ip, TH_PUSH | TH_ACK, c->loc.port, c->rem.port, @@ -7079,7 +7085,8 @@ static void write_conn(struct mg_connection *c) { static void fin_conn(struct mg_connection *c) { struct mip_if *ifp = (struct mip_if *) c->mgr->priv; - struct tcpstate *s = (struct tcpstate *) (c + 1); + struct connstate *s = (struct connstate *) (c + 1); + MG_INFO(("AAA")); tx_tcp(ifp, c->rem.ip, TH_FIN | TH_ACK, c->loc.port, c->rem.port, mg_htonl(s->seq), mg_htonl(s->ack), NULL, 0); } diff --git a/src/http.c b/src/http.c index fd06045e..3b5fce56 100644 --- a/src/http.c +++ b/src/http.c @@ -834,15 +834,15 @@ int mg_http_status(const struct mg_http_message *hm) { // If a server sends data to the client using chunked encoding, Mongoose strips // off the chunking prefix (hex length and \r\n) and suffix (\r\n), appends the // stripped data to the body, and fires the MG_EV_HTTP_CHUNK event. When zero -// chunk is received, it fires MG_EV_HTTP_MSG, and the body is already have all +// chunk is received, we fire MG_EV_HTTP_MSG, and the body already has all // chunking prefixes/suffixes stripped. // -// If a server sends data without chunked encoding, we also fire MG_EV_CHUNK -// and MG_EV_HTTP_MSG in the end. +// If a server sends data without chunked encoding, we also fire a series of +// MG_EV_HTTP_CHUNK events for every received piece of data, and then we fire +// MG_EV_HTTP_MSG event in the end. // -// We track the total processed body length in the c->pfn_data, -// by using void * pointer to store size_t value. - +// We track total processed length in the c->pfn_data, which is a void * +// pointer: we store a size_t value there. static bool getchunk(struct mg_str s, size_t *prefixlen, size_t *datalen) { size_t i = 0, n; while (i < s.len && s.ptr[i] != '\r' && s.ptr[i] != '\n') i++;