Make mip-pcap work on Mac

This commit is contained in:
cpq 2022-09-07 13:35:05 +01:00
parent 9dea02e578
commit b2624f6055
5 changed files with 220 additions and 148 deletions

View File

@ -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
```

View File

@ -6,45 +6,22 @@
#include <pcap.h>
#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);

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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++;