|
1 | 1 | // Copyright SCI Semiconductor and CHERIoT Contributors. |
2 | 2 | // SPDX-License-Identifier: MIT |
3 | 3 |
|
4 | | -#include "firewall.h" |
| 4 | +#include "firewall.hh" |
5 | 5 | #include <atomic> |
6 | 6 | #include <compartment-macros.h> |
7 | 7 | #include <debug.hh> |
@@ -69,6 +69,11 @@ namespace |
69 | 69 |
|
70 | 70 | std::atomic<uint32_t> barrier; |
71 | 71 |
|
| 72 | + /** |
| 73 | + * This is used to synchronize with the TCP/IP stack during resets. |
| 74 | + */ |
| 75 | + std::atomic<uint8_t> *tcpipRestartState = nullptr; |
| 76 | + |
72 | 77 | auto &lazy_network_interface() |
73 | 78 | { |
74 | 79 | static EthernetDevice interface; |
@@ -240,6 +245,13 @@ namespace |
240 | 245 | return table; |
241 | 246 | } |
242 | 247 |
|
| 248 | + void clear(IPProtocolNumber protocol) |
| 249 | + { |
| 250 | + auto guardedTable = permitted_endpoints(protocol); |
| 251 | + auto &[g, table] = guardedTable; |
| 252 | + table.clear(); |
| 253 | + } |
| 254 | + |
243 | 255 | void remove_endpoint(IPProtocolNumber protocol, |
244 | 256 | Address endpoint, |
245 | 257 | uint16_t localPort, |
@@ -492,6 +504,16 @@ namespace |
492 | 504 |
|
493 | 505 | bool packet_filter_ingress(const uint8_t *data, size_t length) |
494 | 506 | { |
| 507 | + uint32_t stateSnapshot = tcpipRestartState->load(); |
| 508 | + if (stateSnapshot != 0 && |
| 509 | + ((stateSnapshot & RestartStateDriverKickedBit) == 0)) |
| 510 | + { |
| 511 | + // We are in a reset and the driver has not yet been |
| 512 | + // restarted. |
| 513 | + Debug::log("Dropping packet due to network stack restart."); |
| 514 | + return false; |
| 515 | + } |
| 516 | + |
495 | 517 | // Not a valid Ethernet frame (64 bytes including four-byte FCS, which |
496 | 518 | // is stripped by this point). |
497 | 519 | if (length < 60) |
@@ -527,26 +549,6 @@ namespace |
527 | 549 |
|
528 | 550 | } // namespace |
529 | 551 |
|
530 | | -bool ethernet_driver_start() |
531 | | -{ |
532 | | - // Protect against double entry. If the barrier state is 0, no |
533 | | - // initialisation has happened and we should proceed. If it's 1, we're in |
534 | | - // the middle of initialisation, if it's 2 then initialisation is done. In |
535 | | - // any non-zero case, we should not try to do anything. |
536 | | - uint32_t expected = 0; |
537 | | - if (!barrier.compare_exchange_strong(expected, 1)) |
538 | | - { |
539 | | - return false; |
540 | | - } |
541 | | - Debug::log("Initialising network interface"); |
542 | | - auto ðernet = lazy_network_interface(); |
543 | | - ethernet.mac_address_set(); |
544 | | - // Poke the barrier and make the driver thread start. |
545 | | - barrier = 2; |
546 | | - barrier.notify_one(); |
547 | | - return true; |
548 | | -} |
549 | | - |
550 | 552 | bool ethernet_send_frame(uint8_t *frame, size_t length) |
551 | 553 | { |
552 | 554 | // We do not check the frame and length here, because we do not use it |
@@ -748,3 +750,45 @@ void firewall_remove_udpipv6_remote_endpoint(uint8_t *remoteAddress, |
748 | 750 | IPProtocolNumber::UDP, *copy, localPort, remotePort); |
749 | 751 | } |
750 | 752 | } |
| 753 | + |
| 754 | +bool ethernet_driver_start(std::atomic<uint8_t> *state) |
| 755 | +{ |
| 756 | + if (tcpipRestartState == nullptr) |
| 757 | + { |
| 758 | + if (!CHERI::check_pointer<CHERI::PermissionSet{ |
| 759 | + CHERI::Permission::Load, CHERI::Permission::Global}>( |
| 760 | + state, sizeof(*state))) |
| 761 | + { |
| 762 | + Debug::log("Invalid TCP/IP state pointer {}", state); |
| 763 | + return false; |
| 764 | + } |
| 765 | + tcpipRestartState = state; |
| 766 | + } |
| 767 | + if (tcpipRestartState->load() != 0) |
| 768 | + { |
| 769 | + // This is a restart, no need to actually reset the driver. |
| 770 | + // Instead, just remove all firewall entries. |
| 771 | + Debug::log("Network stack restart: clearing all entries."); |
| 772 | + EndpointsTable<IPv6Address>::instance().clear(IPProtocolNumber::UDP); |
| 773 | + EndpointsTable<IPv6Address>::instance().clear(IPProtocolNumber::TCP); |
| 774 | + EndpointsTable<uint32_t>::instance().clear(IPProtocolNumber::UDP); |
| 775 | + EndpointsTable<uint32_t>::instance().clear(IPProtocolNumber::TCP); |
| 776 | + return true; |
| 777 | + } |
| 778 | + // Protect against double entry. If the barrier state is 0, no |
| 779 | + // initialisation has happened and we should proceed. If it's 1, we're in |
| 780 | + // the middle of initialisation, if it's 2 then initialisation is done. In |
| 781 | + // any non-zero case, we should not try to do anything. |
| 782 | + uint32_t expected = 0; |
| 783 | + if (!barrier.compare_exchange_strong(expected, 1)) |
| 784 | + { |
| 785 | + return false; |
| 786 | + } |
| 787 | + Debug::log("Initialising network interface"); |
| 788 | + auto ðernet = lazy_network_interface(); |
| 789 | + ethernet.mac_address_set(); |
| 790 | + // Poke the barrier and make the driver thread start. |
| 791 | + barrier = 2; |
| 792 | + barrier.notify_one(); |
| 793 | + return true; |
| 794 | +} |
0 commit comments