aos/usr/drivers/enet/enet_proto.c
2022-05-27 19:11:22 +02:00

707 lines
24 KiB
C

#include <devif/queue_interface_backend.h>
#include <devif/backends/net/enet_devif.h>
#include <aos/aos.h>
#include <driverkit/driverkit.h>
#include <dev/imx8x/enet_dev.h>
#include <aos/ump_binding.h>
#include <aos/ump_chan.h>
#include <aos/ump_net.h>
#include <netutil/etharp.h>
#include <netutil/ip.h>
#include <netutil/icmp.h>
#include <netutil/udp.h>
#include <netutil/checksum.h>
#include <netutil/types.h>
#include "enet.h"
// https://datatracker.ietf.org/doc/html/rfc6335#section-6
#define EPHEMERAL_PORT_START 49152
#define EPHEMERAL_PORT_END 65535
extern struct enet_driver_state *st;
static void write_eth_ip_header (
struct devq_buf *buf, void *vaddr,
uint64_t eth_dst, uint32_t ip_dest, uint8_t proto, size_t payload_len
);
static void tx_alloc (struct tx_alloc_queue_entry *entry, tx_alloc_callback_t cb, void *cb_arg) {
errval_t err = DEVQ_ERR_QUEUE_EMPTY;
struct devq_buf buf;
// If the queue is not empty, always append to the queue, so that FIFO order is maintained
if (st->tx_alloc_queue_head == NULL) {
err = devq_dequeue((struct devq*) st->txq, &buf.rid, &buf.offset,
&buf.length, &buf.valid_data, &buf.valid_length,
&buf.flags);
}
if (err_is_fail(err)) {
if (st->tx_init_i < st->txq->size) {
buf.rid = st->tx_rid;
buf.offset = st->tx_init_i * 2048;
buf.length = 2048;
buf.valid_data = 0;
buf.valid_length = 2048;
buf.flags = 0;
st->tx_init_i++;
} else {
entry->cb = cb;
entry->cb_arg = cb_arg;
entry->next = NULL;
if (st->tx_alloc_queue_tail == NULL) {
st->tx_alloc_queue_head = entry;
} else {
st->tx_alloc_queue_tail->next = entry;
}
st->tx_alloc_queue_tail = entry;
return;
}
}
struct region_entry *region_entry = enet_get_region(st->txq, buf.rid);
assert(region_entry != NULL);
void *vaddr = (void*)region_entry->mem.vbase + buf.offset;
cb(cb_arg, &buf, vaddr);
}
static void tx_send (struct devq_buf *buf) {
errval_t err;
err = devq_enqueue((struct devq*) st->txq, buf->rid, buf->offset,
buf->length, buf->valid_data, buf->valid_length,
buf->flags);
assert(err_is_ok(err));
}
static void rx_release (struct devq_buf *buf) {
errval_t err;
err = devq_enqueue((struct devq*) st->rxq, buf->rid, buf->offset,
buf->length, buf->valid_data, buf->valid_length,
buf->flags);
assert(err_is_ok(err));
}
// ARP: https://datatracker.ietf.org/doc/html/rfc826
static void arp_send (
struct devq_buf *buf, void *vaddr,
uint64_t eth_dst, uint32_t ip_dest,
uint16_t op
) {
buf->valid_data = 0;
buf->valid_length = ETH_HLEN + sizeof(struct arp_hdr);
struct eth_hdr *eth_hdr = vaddr;
eth_hdr->dst = eth_addr_wr(eth_dst);
eth_hdr->src = eth_addr_wr(st->mac);
eth_hdr->type = uint16_wr(ETH_TYPE_ARP);
struct arp_hdr *arp_hdr = vaddr + ETH_HLEN;
arp_hdr->hwtype = uint16_wr(ARP_HW_TYPE_ETH);
arp_hdr->proto = uint16_wr(ETH_TYPE_IP);
arp_hdr->hwlen = ETH_ADDR_LEN;
arp_hdr->protolen = IP_ADDR_LEN;
arp_hdr->opcode = uint16_wr(op);
arp_hdr->eth_src = eth_addr_wr(st->mac);
arp_hdr->ip_src = uint32_wr(st->my_ip);
arp_hdr->eth_dst = eth_addr_wr(eth_dst);
arp_hdr->ip_dst = uint32_wr(ip_dest);
tx_send(buf);
}
static void arp_reply (void *cb_arg, struct devq_buf *buf, void *vaddr) {
struct arp_tx_queue_entry *entry = &st->arp.tx_queue[st->arp.tx_queue_tail];
assert(cb_arg == entry);
arp_send(buf, vaddr, entry->eth_dst, entry->ip_dst, ARP_OP_REP);
st->arp.tx_queue_tail = (st->arp.tx_queue_tail + 1) & (ARP_TX_QUEUE_LEN - 1);
st->arp.tx_queue_len--;
}
static void arp_handle (struct devq_buf *buf, void *vaddr, struct eth_hdr *eth_hdr) {
ENET_DEBUG("Received ARP packet\n");
if (buf->valid_length < sizeof(struct arp_hdr)) {
ENET_WARN("Received ARP packet too small\n");
rx_release(buf);
return;
}
struct arp_hdr *arp_hdr = vaddr + buf->valid_data;
uint16_t opcode = uint16_rd(arp_hdr->opcode);
if (
uint16_rd(arp_hdr->hwtype) != ARP_HW_TYPE_ETH ||
uint16_rd(arp_hdr->proto) != ETH_TYPE_IP ||
arp_hdr->hwlen != ETH_ADDR_LEN ||
arp_hdr->protolen != IP_ADDR_LEN ||
(opcode != ARP_OP_REQ && opcode != ARP_OP_REP)
) {
ENET_WARN("Unknown ARP packet type\n");
rx_release(buf);
return;
}
uint32_t ip_src = uint32_rd(arp_hdr->ip_src);
if ((st->my_ip & st->net_mask) != (ip_src & st->net_mask)) {
ENET_WARN("Received ARP packet for address outside my network\n");
rx_release(buf);
return;
}
uint64_t eth_src = eth_addr_rd(arp_hdr->eth_src);
uint64_t eth_old = (uintptr_t)collections_hash_find(st->arp.table, ip_src);
// check if sender protocol address is in translation table, if so update entry
if (eth_old != 0 && eth_src != eth_old) {
ENET_WARN("MAC address of %d.%d.%d.%d has changed\n",
arp_hdr->ip_src.val[0],
arp_hdr->ip_src.val[1],
arp_hdr->ip_src.val[2],
arp_hdr->ip_src.val[3]
);
collections_hash_delete(st->arp.table, ip_src);
collections_hash_insert(st->arp.table, ip_src, (void*)eth_src);
}
if (uint32_rd(arp_hdr->ip_dst) == st->my_ip) {
ENET_DEBUG("Received ARP packet addressed to me\n");
// if sender protocol address was not in translation table, add entry
if (eth_old == 0) {
collections_hash_insert(st->arp.table, ip_src, (void*)eth_src);
}
if (opcode == ARP_OP_REQ) {
if (st->arp.tx_queue_len >= ARP_TX_QUEUE_LEN) {
ENET_WARN("Too many queued ARP replies, not sending reply\n");
} else {
struct arp_tx_queue_entry *entry = &st->arp.tx_queue[(st->arp.tx_queue_tail + st->arp.tx_queue_len) & (ARP_TX_QUEUE_LEN - 1)];
st->arp.tx_queue_len++;
entry->eth_dst = eth_src;
entry->ip_dst = ip_src;
tx_alloc(&entry->tx_entry, arp_reply, entry);
}
}
}
rx_release(buf);
}
// ICMP: https://datatracker.ietf.org/doc/html/rfc792
static void icmp_reply (void *cb_arg, struct devq_buf *buf, void *vaddr) {
struct icmp_echo_reply_meta *entry = cb_arg;
write_eth_ip_header(
buf, vaddr,
eth_addr_rd(entry->eth_hdr->src),
uint32_rd(entry->ip_hdr->src),
IP_PROTO_ICMP,
entry->rx_buf.valid_length
);
struct icmp_echo_hdr *icmp_echo_hdr = vaddr + ETH_HLEN + sizeof(struct ip_hdr);
memcpy(icmp_echo_hdr, entry->icmp_echo_hdr, entry->rx_buf.valid_length);
icmp_echo_hdr->type = ICMP_ER;
// Incremental update: https://datatracker.ietf.org/doc/html/rfc1071
uint32_t chksum = uint16_rd(icmp_echo_hdr->chksum);
chksum += (ICMP_ECHO << 8) + ((ICMP_ER << 8) ^ 0x0000ffffUL);
chksum = (chksum >> 16) + (chksum & 0x0000ffffUL);
icmp_echo_hdr->chksum = uint16_wr(chksum);
rx_release(&entry->rx_buf);
simpleslab_free(&st->rx_meta_slab, entry);
tx_send(buf);
}
static void icmp_handle (struct devq_buf *buf, void *vaddr, struct eth_hdr *eth_hdr, struct ip_hdr *ip_hdr) {
if (buf->valid_length < 4) {
ENET_WARN("Received ICMP packet too small\n");
rx_release(buf);
return;
}
if (inet_checksum(vaddr + buf->valid_data, buf->valid_length) != 0) {
ENET_WARN("Received ICMP packet with bad checksum\n");
rx_release(buf);
return;
}
uint8_t type = *(uint8_t*)(vaddr + buf->valid_data);
if (type == ICMP_ECHO) {
if (buf->valid_length < sizeof(struct icmp_echo_hdr)) {
ENET_WARN("Received ICMP packet too small\n");
rx_release(buf);
return;
}
struct icmp_echo_hdr *icmp_echo_hdr = vaddr + buf->valid_data;
ENET_DEBUG("Received ICMP echo, seq=%d\n", uint16_rd(icmp_echo_hdr->seqno));
struct icmp_echo_reply_meta *meta = simpleslab_alloc(&st->rx_meta_slab);
meta->rx_buf = *buf;
meta->eth_hdr = eth_hdr;
meta->ip_hdr = ip_hdr;
meta->icmp_echo_hdr = icmp_echo_hdr;
tx_alloc(&meta->tx_entry, icmp_reply, meta);
return;
} else {
ENET_WARN("Received ICMP packet with unknown type %d\n", type);
}
rx_release(buf);
}
// UDP: https://datatracker.ietf.org/doc/html/rfc768
static void udp_recv_ump_callback (void *arg, struct ump_send_queue_entry *entry) {
struct udp_recv *meta = arg;
rx_release(&meta->rx_buf);
simpleslab_free(&st->rx_meta_slab, meta);
}
static uint16_t udp_checksum (struct ip_hdr *ip_hdr, struct udp_hdr *udp_hdr) {
uint16_t udp_len = uint16_rd(udp_hdr->len);
uint32_t chksum = inet_checksum(udp_hdr, udp_len) ^ 0x0000ffffUL;
// add pseudo header
chksum += (ip_hdr->src.val[0] << 8) | ip_hdr->src.val[1];
chksum += (ip_hdr->src.val[2] << 8) | ip_hdr->src.val[3];
chksum += (ip_hdr->dest.val[0] << 8) | ip_hdr->dest.val[1];
chksum += (ip_hdr->dest.val[2] << 8) | ip_hdr->dest.val[3];
chksum += IP_PROTO_UDP;
chksum += udp_len;
chksum = (chksum >> 16) + (chksum & 0x0000ffffUL);
chksum = (chksum >> 16) + (chksum & 0x0000ffffUL);
return ~(uint16_t)chksum;
}
static void udp_handle (struct devq_buf *buf, void *vaddr, struct eth_hdr *eth_hdr, struct ip_hdr *ip_hdr) {
if (buf->valid_length < UDP_HLEN) {
ENET_WARN("Received UDP packet too small\n");
rx_release(buf);
return;
}
struct udp_hdr *udp_hdr = vaddr + buf->valid_data;
uint16_t udp_len = uint16_rd(udp_hdr->len);
if (udp_len < UDP_HLEN || buf->valid_length < udp_len) {
ENET_WARN("Received UDP packet too small\n");
rx_release(buf);
return;
}
if (
uint16_rd(udp_hdr->chksum) != 0 && // 0 means no checksum
udp_checksum(ip_hdr, udp_hdr) != 0
) {
ENET_WARN("Received UDP packet with bad checksum\n");
rx_release(buf);
return;
}
uint16_t src_port = uint16_rd(udp_hdr->src);
uint16_t dest_port = uint16_rd(udp_hdr->dest);
void *payload = (void*)udp_hdr + UDP_HLEN;
uint16_t payload_len = udp_len - UDP_HLEN;
ENET_DEBUG("Received UDP packet from %d to %d, len %d\n", src_port, dest_port, payload_len);
struct ump_client *client = collections_hash_find(st->udp.listen_table, dest_port);
if (client == NULL) {
// TODO: send ICMP unreachable
rx_release(buf);
} else {
struct udp_recv *meta = simpleslab_alloc(&st->rx_meta_slab);
meta->rx_buf = *buf;
meta->ump_header = (struct ump_net_in_header){
.op = UMP_NET_EV_UDP_RECV,
.d = { .udp_recv = {
.src_ip = uint32_rd(ip_hdr->src),
.dest_ip = uint32_rd(ip_hdr->dest),
.src_port = src_port,
.dest_port = dest_port,
} }
};
ump_send(client->send_chan, &meta->ump_entry,
sizeof(struct ump_net_in_header), &meta->ump_header,
payload_len, payload,
udp_recv_ump_callback, meta);
return;
}
}
// IP: https://datatracker.ietf.org/doc/html/rfc791#section-3.1
static void ip_handle (struct devq_buf *buf, void *vaddr, struct eth_hdr *eth_hdr) {
if (buf->valid_length < sizeof(struct ip_hdr)) {
ENET_WARN("Received IP packet too small\n");
rx_release(buf);
return;
}
struct ip_hdr *ip_hdr = vaddr + buf->valid_data;
uint16_t ip_len = uint16_rd(ip_hdr->len);
uint16_t header_len = IPH_HL(ip_hdr) * 4;
if (
IPH_V(ip_hdr) != 4 ||
header_len < 20 ||
header_len > ip_len ||
ip_len > buf->valid_length
) {
ENET_WARN("Received bad IP packet\n");
rx_release(buf);
return;
}
if (inet_checksum(ip_hdr, header_len) != 0) {
ENET_WARN("Received IP packet with bad checksum\n");
rx_release(buf);
return;
}
uint16_t offset_flags = uint16_rd(ip_hdr->offset);
if (
(offset_flags & IP_MF) != 0 ||
(offset_flags & IP_OFFMASK) != 0
) {
ENET_WARN("Received IP fragment, dropping\n");
rx_release(buf);
return;
}
ENET_DEBUG("Received IP packet from %d.%d.%d.%d to %d.%d.%d.%d, proto %d\n",
ip_hdr->src.val[0],
ip_hdr->src.val[1],
ip_hdr->src.val[2],
ip_hdr->src.val[3],
ip_hdr->dest.val[0],
ip_hdr->dest.val[1],
ip_hdr->dest.val[2],
ip_hdr->dest.val[3],
ip_hdr->proto
);
buf->valid_data += header_len;
buf->valid_length = ip_len - header_len;
if (uint32_rd(ip_hdr->dest) == st->my_ip) {
if (ip_hdr->proto == IP_PROTO_ICMP) {
icmp_handle(buf, vaddr, eth_hdr, ip_hdr);
} else if (ip_hdr->proto == IP_PROTO_UDP) {
udp_handle(buf, vaddr, eth_hdr, ip_hdr);
} else {
ENET_WARN("Received IP packet with unknown protocol %d\n", ip_hdr->proto);
rx_release(buf);
}
} else {
ENET_WARN("Received IP packet for someone else\n");
rx_release(buf);
}
}
static void write_eth_ip_header (
struct devq_buf *buf, void *vaddr,
uint64_t eth_dst, uint32_t ip_dest, uint8_t proto, size_t payload_len
) {
buf->valid_data = 0;
buf->valid_length = ETH_HLEN + sizeof(struct ip_hdr) + payload_len;
assert(buf->valid_length <= ETH_HLEN + 1500);
struct eth_hdr *eth_hdr = vaddr;
eth_hdr->dst = eth_addr_wr(eth_dst);
eth_hdr->src = eth_addr_wr(st->mac);
eth_hdr->type = uint16_wr(ETH_TYPE_IP);
struct ip_hdr *ip_hdr = vaddr + ETH_HLEN;
IPH_VHL_SET(ip_hdr, 4, 5);
ip_hdr->tos = 0;
ip_hdr->len = uint16_wr(sizeof(struct ip_hdr) + payload_len);
ip_hdr->id = uint16_wr(st->ip.next_id++);
ip_hdr->offset = uint16_wr(IP_DF);
ip_hdr->ttl = 64;
ip_hdr->proto = proto;
ip_hdr->chksum = uint16_wr(0);
ip_hdr->src = uint32_wr(st->my_ip);
ip_hdr->dest = uint32_wr(ip_dest);
ip_hdr->chksum = uint16_wr(inet_checksum(ip_hdr, 20));
}
// Ethernet
static void rx_handle (struct devq_buf *buf) {
struct region_entry *entry = enet_get_region(st->rxq, buf->rid);
assert(entry != NULL);
void *vaddr = (void*)entry->mem.vbase + buf->offset;
void *eth_vaddr = vaddr + buf->valid_data;
#if defined(ENET_DEBUG_OPTION)
debug_printf("Received Packet of size %lu:", buf->valid_length);
for (size_t i = 0; i < buf->valid_length; i++) {
printf(" %02x", ((uint8_t*)eth_vaddr)[i]);
}
printf("\n");
#endif
if (buf->valid_length < ETH_HLEN) {
ENET_WARN("Ethernet packet too small\n");
rx_release(buf);
return;
}
struct eth_hdr *eth_hdr = eth_vaddr;
uint16_t type = uint16_rd(eth_hdr->type);
buf->valid_data += ETH_HLEN;
buf->valid_length -= ETH_HLEN;
if (type == ETH_TYPE_ARP) {
arp_handle(buf, vaddr, eth_hdr);
} else if (type == ETH_TYPE_IP) {
ip_handle(buf, vaddr, eth_hdr);
} else {
ENET_WARN("Received packet of unknown type: %04x\n", type);
rx_release(buf);
}
}
// UMP
static void netump_header_handle (void *arg, size_t header_size, void *header_raw, size_t payload_size);
static void netump_next (struct ump_client *client) {
if (client->ump_reply_slab.free != 0) {
ump_recv_header(client->recv_chan, netump_header_handle, client);
} else {
client->blocked_on_reply_slab = true;
}
}
static void netump_reply_callback (void *arg, struct ump_send_queue_entry *entry) {
struct ump_reply_buf *reply = arg;
struct ump_client *client = reply->client;
simpleslab_free(&client->ump_reply_slab, reply);
if (client->blocked_on_reply_slab) {
client->blocked_on_reply_slab = false;
netump_next(client);
}
}
static void netump_payload_ignore (void *arg, size_t payload_size, void *payload) {
netump_next(arg);
}
static void netump_send_reply (struct ump_reply_buf *reply, errval_t err) {
reply->ump_header.ret_err = err;
ump_send(reply->client->send_chan, &reply->ump_entry,
sizeof(struct ump_net_in_header), &reply->ump_header,
0, NULL,
netump_reply_callback, reply);
}
static void netump_udp_tx_payload (void *arg, size_t payload_size, void *payload) {
struct ump_client *client = arg;
struct udp_hdr *udp_hdr = (void*)client->tx_ip_hdr + sizeof(struct ip_hdr);
uint16_t checksum = udp_checksum(client->tx_ip_hdr, udp_hdr);
if (checksum == 0) checksum = 0xffff;
udp_hdr->chksum = uint16_wr(checksum);
tx_send(&client->tx_buf);
netump_next(client);
}
static void netump_udp_tx_allocated (void *arg, struct devq_buf *buf, void *vaddr) {
struct ump_reply_buf *reply = arg;
struct ump_client *client = reply->client;
struct ump_net_out_header *header = client->recv_chan->header;
size_t payload_size = client->recv_chan->next_payload_size;
uint64_t eth_dest = (uintptr_t)collections_hash_find(st->arp.table, header->d.udp_send.dest_ip);
if (eth_dest == 0) {
ENET_WARN("IP not in ARP table, dropping packet and sending ARP request\n");
arp_send(buf, vaddr, 0xffffffffffff,
header->d.udp_send.dest_ip, ARP_OP_REQ);
netump_send_reply(reply, LIB_ERR_NET_ARP_MISS);
ump_recv_payload(client->recv_chan, NULL, netump_payload_ignore, client);
return;
}
write_eth_ip_header(
buf, vaddr,
eth_dest,
header->d.udp_send.dest_ip,
IP_PROTO_UDP,
UDP_HLEN + payload_size
);
struct udp_hdr *udp_hdr = vaddr + ETH_HLEN + sizeof(struct ip_hdr);
udp_hdr->src = uint16_wr(header->d.udp_send.src_port);
udp_hdr->dest = uint16_wr(header->d.udp_send.dest_port);
udp_hdr->len = uint16_wr(UDP_HLEN + payload_size);
udp_hdr->chksum = uint16_wr(0);
void *payload = (void*)udp_hdr + UDP_HLEN;
client->tx_buf = *buf;
client->tx_ip_hdr = vaddr + ETH_HLEN;
netump_send_reply(reply, SYS_ERR_OK);
ump_recv_payload(client->recv_chan, payload, netump_udp_tx_payload, client);
}
static void netump_header_handle (void *arg, size_t header_size, void *header_raw, size_t payload_size) {
struct ump_client *client = arg;
struct ump_net_out_header *header = header_raw;
errval_t err;
struct ump_reply_buf *reply = simpleslab_alloc(&client->ump_reply_slab);
assert(reply != NULL);
reply->client = client;
reply->ump_header.op = header->op;
if (header->op == UMP_NET_OP_UDP_SEND) {
if (payload_size > 1500 - sizeof(struct ip_hdr) - UDP_HLEN) {
err = LIB_ERR_NET_PACKET_TOO_BIG;
} else {
tx_alloc(&client->tx_entry, netump_udp_tx_allocated, reply);
return;
}
} else if (header->op == UMP_NET_OP_UDP_LISTEN) {
uint16_t port = header->d.udp_listen.dest_port;
if (port == 0) {
// allocate an ephemeral port
uint16_t first = st->udp.next_ephemeral_port;
port = first;
while (true) {
uint16_t next_port;
if (port == EPHEMERAL_PORT_END) {
next_port = EPHEMERAL_PORT_START;
} else {
next_port = port + 1;
}
if (collections_hash_find(st->udp.listen_table, port) == NULL) {
st->udp.next_ephemeral_port = next_port;
break;
}
port = next_port;
if (port == first) {
port = 0;
break;
}
}
}
if (port == 0) {
err = LIB_ERR_NET_ALLOC_PORT;
} else if (collections_hash_find(st->udp.listen_table, port) != NULL) {
err = LIB_ERR_NET_PORT_IN_USE;
} else {
ENET_DEBUG("Listening on UDP port %d\n", port);
reply->ump_header.d.udp_listen.dest_port = port;
collections_hash_insert(st->udp.listen_table, port, client);
err = SYS_ERR_OK;
}
} else if (header->op == UMP_NET_OP_UDP_LISTEN_STOP) {
uint16_t port = header->d.udp_listen_stop.dest_port;
if (collections_hash_find(st->udp.listen_table, port) != client) {
err = LIB_ERR_NET_NOT_LISTENING;
} else {
ENET_DEBUG("Stopped listening on UDP port %d\n", port);
collections_hash_delete(st->udp.listen_table, port);
err = SYS_ERR_OK;
}
} else {
debug_printf("Error: unknown UMP op\n");
simpleslab_free(&client->ump_reply_slab, reply);
ump_recv_payload(client->recv_chan, NULL, netump_payload_ignore, client);
return;
}
netump_send_reply(reply, err);
ump_recv_payload(client->recv_chan, NULL, netump_payload_ignore, client);
}
static errval_t netump_connect (void *arg, struct capref cap) {
errval_t err;
ENET_DEBUG("Incoming UMP connection\n");
struct ump_client *client = malloc(sizeof(struct ump_client));
if (client == NULL) return LIB_ERR_MALLOC_FAIL;
err = ump_chan_init(UMP_ROLE_SERVER, &client->send_chan, &client->recv_chan,
sizeof(struct ump_net_out_header), cap, get_default_waitset());
if (err_is_fail(err)) return err;
simpleslab_init(&client->ump_reply_slab, sizeof(struct ump_reply_buf),
&client->ump_reply_slab_buf, sizeof(client->ump_reply_slab_buf));
client->blocked_on_reply_slab = false;
netump_next(client);
return SYS_ERR_OK;
}
void enet_loop (void) {
errval_t err;
st->arp.tx_queue_len = 0;
st->arp.tx_queue_tail = 0;
collections_hash_create(&st->arp.table, NULL);
st->ip.next_id = 0;
collections_hash_create_with_buckets(&st->udp.listen_table, 100, NULL);
st->udp.next_ephemeral_port = EPHEMERAL_PORT_START;
simpleslab_init(&st->rx_meta_slab, sizeof(union rx_meta_slab_data),
&st->rx_meta_slab_buf, sizeof(st->rx_meta_slab_buf));
struct ump_binding_server server;
err = ump_binding_register(&server, UMP_SERVER_NET, netump_connect, NULL);
if (err_is_fail(err)) USER_PANIC_ERR(err, "Failed to register UMP server");
struct devq_buf buf;
struct waitset *default_ws = get_default_waitset();
while (true) {
bool made_progress = false;
err = event_dispatch_non_block(default_ws);
if (err_is_fail(err) && err != LIB_ERR_NO_EVENT) {
DEBUG_ERR(err, "in event_dispatch");
abort();
}
// TODO: set made_progress if we handled an event. Currently,
// UMP always registers a callback, so we would always see an event.
// See TODO in poll_channels_disabled.
err = devq_dequeue((struct devq*) st->rxq, &buf.rid, &buf.offset,
&buf.length, &buf.valid_data, &buf.valid_length,
&buf.flags);
if (err_is_ok(err)) {
made_progress = true;
rx_handle(&buf);
}
if (st->tx_alloc_queue_head != NULL) {
err = devq_dequeue((struct devq*) st->txq, &buf.rid, &buf.offset,
&buf.length, &buf.valid_data, &buf.valid_length,
&buf.flags);
if (err_is_ok(err)) {
made_progress = true;
struct tx_alloc_queue_entry *entry = st->tx_alloc_queue_head;
st->tx_alloc_queue_head = entry->next;
if (entry->next == NULL) {
st->tx_alloc_queue_tail = NULL;
}
struct region_entry *region_entry = enet_get_region(st->txq, buf.rid);
assert(region_entry != NULL);
void *vaddr = (void*)region_entry->mem.vbase + buf.offset;
entry->cb(entry->cb_arg, &buf, vaddr);
}
}
if (!made_progress) {
thread_yield();
}
}
}