diff --git a/examples/firewall/net_components/firewall_network_virt_tx.c b/examples/firewall/net_components/firewall_network_virt_tx.c index 88b63a75f..39b246896 100644 --- a/examples/firewall/net_components/firewall_network_virt_tx.c +++ b/examples/firewall/net_components/firewall_network_virt_tx.c @@ -23,6 +23,10 @@ net_queue_handle_t tx_queue_clients[SDDF_NET_MAX_CLIENTS]; fw_queue_t fw_free_clients[FW_MAX_FW_CLIENTS]; fw_queue_t fw_active_clients[FW_MAX_FW_CLIENTS]; +/* Alternates which of the net/fw client groups tx_provide() will serve first, to +prevent starvation on tx_queue_drv. */ +bool fw_clients_priority = false; + static int extract_offset_net_client(uintptr_t *phys) { for (int client = 0; client < config.num_clients; client++) { @@ -48,13 +52,19 @@ static int extract_offset_fw_client(uintptr_t *phys) return -1; } -static void tx_provide(void) +/* sdfgen sizes tx_queue_drv for the net clients only, not the firewall +clients also feeding it, so fullness must be checked before every enqueue. */ +static void tx_provide_net_clients(bool *enqueued, bool *drv_full) { - bool enqueued = false; - for (int client = 0; client < config.num_clients; client++) { + for (int client = 0; client < config.num_clients && !*drv_full; client++) { bool reprocess = true; while (reprocess) { while (!net_queue_empty_active(&tx_queue_clients[client])) { + if (net_queue_full_active(&tx_queue_drv)) { + *drv_full = true; + break; + } + net_buff_desc_t buffer; int err = net_dequeue_active(&tx_queue_clients[client], &buffer); assert(!err); @@ -75,7 +85,11 @@ static void tx_provide(void) err = net_enqueue_active(&tx_queue_drv, buffer); assert(!err); - enqueued = true; + *enqueued = true; + } + + if (*drv_full) { + break; } net_request_signal_active(&tx_queue_clients[client]); @@ -87,9 +101,19 @@ static void tx_provide(void) } } } +} - for (int client = 0; client < fw_config.num_active_clients; client++) { +static void tx_provide_fw_clients(bool *enqueued, bool *drv_full) +{ + for (int client = 0; client < fw_config.num_active_clients && !*drv_full; client++) { while (!fw_queue_empty(&fw_active_clients[client])) { + if (net_queue_full_active(&tx_queue_drv)) { + /* Leave remaining buffers queued rather than dropping them; + picked up on the next tx_provide() once tx_return() frees space. */ + *drv_full = true; + break; + } + fw_buff_desc_t buffer; int err = fw_dequeue(&fw_active_clients[client], &buffer); assert(!err); @@ -105,9 +129,24 @@ static void tx_provide(void) net_buff_desc_t net_buffer = { .io_or_offset = io_addr, .len = buffer.len }; err = net_enqueue_active(&tx_queue_drv, net_buffer); assert(!err); - enqueued = true; + *enqueued = true; } } +} + +static void tx_provide(void) +{ + bool enqueued = false; + bool drv_full = false; + + if (fw_clients_priority) { + tx_provide_fw_clients(&enqueued, &drv_full); + tx_provide_net_clients(&enqueued, &drv_full); + } else { + tx_provide_net_clients(&enqueued, &drv_full); + tx_provide_fw_clients(&enqueued, &drv_full); + } + fw_clients_priority = !fw_clients_priority; if (enqueued && net_require_signal_active(&tx_queue_drv)) { net_cancel_signal_active(&tx_queue_drv); diff --git a/examples/firewall/routing/routing.c b/examples/firewall/routing/routing.c index 493bdfaef..854296d48 100644 --- a/examples/firewall/routing/routing.c +++ b/examples/firewall/routing/routing.c @@ -79,8 +79,20 @@ static bool enqueue_icmp_unreachable(fw_buff_desc_t buffer, uint32_t next_hop) return enqueued; } -static void transmit_packet(fw_buff_desc_t buffer, uint8_t *mac_addr, uint8_t out_interface) +static void drop_buffer(fw_buff_desc_t buffer) { + net_buff_desc_t net_buff = { .io_or_offset = buffer.offset, .len = buffer.len }; + int err = fw_enqueue(&rx_free[buffer.interface], &net_buff); + assert(!err); + returned[buffer.interface] = true; +} + +static bool transmit_packet(fw_buff_desc_t buffer, uint8_t *mac_addr, uint8_t out_interface) +{ + if (fw_queue_full(&tx_active[out_interface])) { + return false; + } + uintptr_t pkt_vaddr = data_vaddr[buffer.interface] + buffer.offset; eth_hdr_t *eth_hdr = (eth_hdr_t *)pkt_vaddr; ipv4_hdr_t *ip_hdr = (ipv4_hdr_t *)(pkt_vaddr + IPV4_HDR_OFFSET); @@ -103,6 +115,7 @@ static void transmit_packet(fw_buff_desc_t buffer, uint8_t *mac_addr, uint8_t ou int err = fw_enqueue(&tx_active[out_interface], &buffer); assert(!err); tx_net[out_interface] = true; + return true; } static void process_arp_waiting(uint8_t out_interface) @@ -143,7 +156,11 @@ static void process_arp_waiting(uint8_t out_interface) /* Substitute the MAC address and send packets out of the NIC */ pkt_waiting_node_t *node = root; for (uint16_t i = 0; i < root->num_children + 1; i++) { - transmit_packet(node->buffer, response.mac_addr, out_interface); + bool sent = transmit_packet(node->buffer, response.mac_addr, out_interface); + if (!sent) { + LOG_FIREWALL("ROUTING", "tx queue for interface %u full, dropping packet\n", out_interface); + drop_buffer(node->buffer); + } node = pkts_waiting_next_child(&pkt_waiting_queue[out_interface], node); } } @@ -196,6 +213,14 @@ static void route(void) /* Check for webserver traffic */ tcp_hdr_t *tcp_pkt = (tcp_hdr_t *)(pkt_vaddr + transport_layer_offset(ip_hdr)); if (ip_hdr->protocol == IPV4_PROTO_TCP && tcp_pkt->dst_port == htons(WEBSERVER_PORT)) { + if (fw_queue_full(&webserver)) { + /* Webserver queue can receive buffers from any interface */ + LOG_FIREWALL("ROUTING", "webserver queue full, dropping packet from interface %u\n", + interface); + drop_buffer(fw_buffer); + continue; + } + err = fw_enqueue(&webserver, &fw_buffer); assert(!err); tx_webserver = true; @@ -303,7 +328,11 @@ static void route(void) } /* valid arp entry found, transmit packet */ - transmit_packet(fw_buffer, arp->mac_addr, out_interface); + bool sent = transmit_packet(fw_buffer, arp->mac_addr, out_interface); + if (!sent) { + LOG_FIREWALL("ROUTING", "tx queue for interface %u full, dropping packet\n", out_interface); + drop_buffer(fw_buffer); + } } } }