Implementing Data Diodes Using Commodity Hardware and Open Source Software
arXiv:2609.35256v1 [cs.CR] 28 Sep 2026
Peter Story Clark University Worcester, MA, USA [email protected]
Gert-Jan den Besten Dutch Ministry of Defence Netherlands [email protected]
Abstract
cure [15, 36, 54, 16]. One-way network devices, known as data diodes, offer a more secure and usable alternative [18, 48]. Data diodes are physically limited to only transfer data in one direction. Today, data diodes are mostly deployed in nuclear power plants and within the government for handling classified information. Commercially available data diodes are expensive, with some costs exceeding one hundred thousand dollars [10]. Their high prices may discourage more widespread adoption. A data diode’s hardware can be assembled from commodity fiber-optic network equipment [9, 55, 49]. However, specialized software is needed to send data through a data diode reliably: the receiving program cannot request retransmission of dropped packets, so packet loss must be minimized and mitigated. We answer three research questions about data diodes:
One-way network devices, known as data diodes, are used to defend against sophisticated cyberattacks. Partly due to their high cost, data diodes are mostly deployed in nuclear power plants and within the government for handling classified information. Although commercially available data diodes are expensive, a data diode’s hardware can assembled from commodity fiberoptic network equipment. However, specialized software is needed to send data through a data diode reliably: the receiving program cannot request retransmission of dropped packets, so packet loss must be minimized and mitigated. First, we developed a minimal program to measure packet loss. We found that most packet loss was caused by the receiving program processing incoming packets too slowly, and that packet loss often occurs in clusters. Also, we discovered ways to minimize packet loss on Linux and macOS without using superuser privileges. Next, we tested three existing open source programs for one-way data transfers: netcat, UDPcast, and lidi. Although these programs were unreliable in their default configurations, we identified reliable configurations for UDPcast and lidi. Finally, we incorporated our findings into pydiode, our cross-platform program for reliable one-way data transfers.
1
1. What causes packet loss in data diodes? 2. How does existing open source software for one-way data transfers perform? In particular, how long do transfers take, and how often do transfers fail? 3. Can our own software send data reliably in less time than existing software? First, we developed a minimal program to measure packet loss under different conditions on Linux and macOS (§ 3.3). We collected statistics on packet loss while transferring terabytes of data through a data diode built using commodity network hardware (§ 4.1 and § 4.2). In our testing, 99.99% of packet loss was due to RcvbufErrors, caused by the receiving program processing incoming packets too slowly. We discovered several ways to minimize packet loss without using superuser privileges. Some results were expected: packet loss can be minimized by using a separate helper thread to write out received data, programs on Linux can send packets with larger payloads, and programs on macOS can request a larger receive buffer size. However, we also made several counterintuitive discoveries. First, on both Linux and macOS, we observed cases where transferring data more slowly decreased transfer reliability.
Introduction
It is challenging to defend against technically sophisticated cyberattacks. Militaries, intelligence agencies, and other powerful entities use unpatched software vulnerabilities to launch zero-day attacks against highvalue targets. Nearly any internet-connected device can be hacked by zero-day attacks [3, 22]. Softwarebased solutions offer limited protection, since they can be hacked themselves. One approach to defend against zero-day attacks is to physically isolate devices from the internet using an air gap. However, air gaps are often impractical, since it is necessary to exchange data with most devices. Manually transferring data across an air gap using a USB drive is cumbersome and inse1
Second, we found that although sending larger packets increased reliability on Linux, sending larger packets on macOS could decrease reliability. Next, we performed one-way data transfers using three existing open source programs: netcat, UDPcast, and lidi. We found that only 93.89% of netcat’s transfers succeeded (§ 4.3). Transfers were also unreliable when using UDPcast (§ 4.4) and lidi (§ 4.5) in their default configurations. However, we discovered configurations for UDPcast and lidi that enabled reliable transfers using these programs. We also incorporated our findings into the development of pydiode, a crossplatform program for reliably transferring information through data diodes (§ 3.3). pydiode uses redundancy to mitigate packet loss and can automatically detect transfer errors. Unlike UDPcast and lidi, our minimal program and pydiode do not employ forward error correction (FEC). Nevertheless, both minimal and pydiode offer faster reliable transfers than UDPcast, and they are only 52% and 46% slower than lidi, respectively (Table 7). When properly configured, the choice between pydiode, UDPcast, and lidi may depend on factors beyond transfer speed (§ 6.2). For example, lidi’s FEC implementation may infringe on RaptorQ patents held by Qualcomm, whereas pydiode and UDPcast’s underlying technology is public domain. Finally, we offer advice for software developers (§ 6.3), and describe opportunities for future work (§ 7).
2
The economics of cybercrime have protected most users from technically sophisticated attacks. Although software vulnerabilities are widespread in modern software, discovering vulnerabilities takes time and expertise. If a software vulnerability is widely exploited, software vendors will patch their software to close the vulnerability. Thus, at a given time there is a limited supply of unpatched vulnerabilities, also known as zerodays [1, 22]. Because zero-days are valuable [59, 22], attackers save them for high-value targets. However, artificial intelligence is accelerating the discovery of zeroday exploits [6, 45], potentially exposing more organizations to zero-day attacks.
2.2
Defending against zero-day attacks is incredibly challenging [3, 1]. One way high-value targets defend against zero-day attacks is by physically isolating devices using an air gap. In theory, if an attacker cannot communicate with a device, they cannot hack it. In practice, many air-gapped devices are not completely isolated, as there is a need to exchange some data with the device. However, if the direction of the flow of information is controlled, guarantees can be made about confidentiality [12, 11] and integrity [29]. In particular, the Bell–LaPadula model (BLP) implies that if information cannot leave a system containing confidential information, then confidentiality is guaranteed even if that system is compromised [12, 11]. Similarly, the Biba Integrity Model implies that if a high integrity system cannot receive information from lower integrity systems, then lower integrity systems cannot compromise the high integrity system [29]. The direction of information flow can be enforced using devices which are physically limited to only transfer data in one direction, known as data diodes [18, 48]. A data diode is a network device which is only physically capable of transferring data in one direction. A network firewall is not a data diode, since a software vulnerability could compromise the firewall and modify its rules. In contrast, a data diode should include hardware that only allows data to be transferred in one direction. For example, using a modified fiber-optic link [9, 55, 49]. These physical properties allow for guarantees about the direction of data flow [18, 48].
Related Work
First, we describe the threat posed by zero-day attacks, which are used by powerful entities against high-value targets (§ 2.1). Next, we explain how air gaps and one-way communication (i.e., data diodes) can defend against zero-day attacks (§ 2.2). Currently, data diodes are mainly used by the military and in nuclear power plants, in part due to their high cost (§ 2.3). Finally, we survey existing open source software for data diodes (§ 2.4).
2.1
Air Gaps and Data Diodes
Zero-Day Attacks
Cybersecurity threats vary in technical sophistication. The most advanced attacks can succeed without user interaction, even if the target device has all software updates installed. Commercial spyware exploits software vulnerabilities to take over targets’ devices, and is available to governments through products like the NSO Group’s Pegasus [14] and Cytrox’s Predator [23]. Software-based solutions such as firewalls, sandboxing, code signing, and executable-space protection increase the cost of attacks, but cannot completely prevent attacks; software-based solutions can be evaded by combining vulnerabilities, or can be hacked themselves.
2.3
Existing Diodes
Deployments
of
Data
Data diodes are primarily deployed in two different contexts to protect confidentiality and integrity, respectively. First, data diodes are used by military and intelligence agencies as a cross-domain solution (CDS), to limit exchange of data between different security classification levels [9]. For example, a data diode can ensure 2
that data only flows from a lower classification network to a higher classification network. Second, data diodes are deployed in certain safety-critical public sector industries [28]. For example, data diodes are mentioned frequently in the U.S. Nuclear Regulatory Commission’s regulations for nuclear power plants [2, 13, 19, 38]. Literature from data diode vendors describe deployments in other industries as well [20, 40, 47]. However, a recent industry report found that data diodes had lower adoption than other cybersecurity technologies, and suggested this may be due to their “reputation for complexity and cost” [26]. Indeed, proprietary data diodes can have prices exceeding one hundred thousand dollars [21, 34, 10], and each product offers different features. Alternatively, a data diode’s hardware can be assembled from commodity fiber-optic network equipment for less than $100 [9, 55, 49], as shown in Figure 1a. Still, specialized software is needed to send data through a data diode reliably.
2.4
how we connected two workstations for testing (§ 3.1). Next, we explain our methodology for testing netcat, UDPcast, and lidi (§ 3.2). Finally, we describe our implementation of a minimal test program (“minimal”) and a more full-featured program (“pydiode”) (§ 3.3).
3.1
Data Diode Assembly A data diode can be built using fiber-optic network equipment. We assembled our data diode according to the OSDD project’s instructions [55]. We used two Gigabit Ethernet copper to single mode fiber media converters (TP-Link MC210CS) connected by an SC/UPC single mode fiber-optic splitter. As of spring 2025, the components cost about $70 on Amazon. Figure 1a illustrates how the components are connected. The splitter’s input is connected to the TX port of the sender. One of the splitter’s two outputs should be connected to the sender’s RX port, and the other to the receiver’s RX port. Without the loop from the sender’s TX to its RX, Ethernet autonegotiation will fail, which will prevent data transfer [57, 25]. Finally, we covered the TX port of the receiver with electrical tape.
Open Source Software for Data Diodes
Data diodes ensure that data only flows in one direction, necessitating the use of a connectionless protocol like UDP. Furthermore, the receiver cannot request retransmission of dropped packets, so packet loss must be minimized and mitigated. As shown in Table 1, a variety of open source software is capable of transferring data through data diodes using UDP. We identified this software by searching on Google and GitHub; although the list is not necessarily exhaustive, it includes the most commonly used programs, along with some lesser-known software. The software’s documentation varies in maturity, so we reviewed the documentation and source code to determine each software’s functionality and supported input formats. When documentation listed supported platforms or when precompiled binaries were available, we list those platforms; otherwise, we list the platforms on which we were able to compile and run the software. Note that stream-based input formats are the most flexible, since they allow sending individual files via file redirection, directories of files using tar, or network traffic using a proxy program. We chose to evaluate netcat, UDPcast, and lidi because all support transferring data streams. We considered testing hairgap, but we could not compile it, and the project is not actively maintained.
3
Hardware Configuration
Workstation Configuration For Linux testing, we used two Intel NUC 12 mini PCs (NUC12WSHi5). Each PC had 64 GB of DDR4 RAM and 1 TB of PCIe storage. Figure 1b shows our PCs’ network configurations. We connected each PC to one of the media converters, designating one PC the sender and the other the receiver. To automate data collection, we also connected the PCs directly using an Ethernet cable, allowing the sender to control the receiver over SSH without traversing our campus network. For remote management, both PCs were also connected to our campus network using an additional wired connection. Our software configuration ensured that data transfers used the data diode, which we confirmed by monitoring the activity lights on the media converters. For macOS testing, we used two M1 Mac minis. Each Mac mini had 16 GB of RAM and 512 GB of storage. We connected the Mac minis the same way we connected the PCs (Figure 1b).
3.2
Software Configuration
We performed tests on Linux and macOS. Linux can be used in industrial deployments of data diodes. Data diodes can also be used to protect mobile devices against malware [50]. Mobile devices overwhelmingly run Android and iOS, which share network stacks with Linux and macOS, respectively. Thus, our findings can inform future work testing with mobile devices.
Method
Data diodes include hardware for enforcing one-way network traffic, and software for transferring data reliably. First, we describe how we assembled a data diode, and 3
Remote Access via Campus Network
(a) Our data diode is built using fiber-optic media converters. The converter on the left sends data to the converter on the right. The converter on the right physically cannot transfer data in the reverse direction, since its transmit port is taped over.
Sending Workstation
Automation
Receiving Workstation
Sending Converter
Data Transfers
Receiving Converter
(b) Our workstations used separate network connections for remote access, experiment automation, and data transfers. Bidirectional arrows depict standard Ethernet connections. The one-way arrow depicts a one-way connection, enforced by the data diode.
Figure 1: Network configuration for our experiments
Program netcat [58]
UDPcast [32]
lidi [4]
hairgap [17]
godiode [30] BlindFTP [33] Sven Seeberg’s data diode [46]
Functionality to Increase the Reliability of Transfers No extra functionality to increase the reliability of transfers.
Input Format
Platforms
Standard input stream (or file via redirection)
Rate limiting and forward error correction based on Vandermonde matrices [44]. Forward error correction based on RaptorQ codes [37, 42].
Standard input stream or file
Different implementations for Linux, macOS, and Windows Linux, Windows
Rate limiting, redundancy, and forward error correction based on wirehair [51]. Rate limiting, checksums, and redundancy. Rate limiting, checksums, and redundancy. Rate limiting, checksums, and redundancy.
Standard input stream, TCP stream, UNIX stream, file Standard input stream (or file via redirection)
Linux
Directory of files
Linux, macOS
Directory of files
Linux, macOS, Windows
UDP packets, directory of files
Linux, OpenBSD
Linux
Table 1: A summary of command-line programs for transferring data over a data diode using UDP.
4
For Linux testing, we installed Ubuntu Desktop 24.04.3 LTS on both PCs. After installing all available updates in December 2025, we disabled automatic updates. Python 3.12.3 was installed by default, so we used this version to run our Python code. Both PCs ran Linux kernel 6.14.0-35-generic. We moved the interfaces connected to the data diode into network namespaces and assigned IP addresses in the 10.0.1.0/24 range. Using network namespaces ensured that each data transfer program used the data diode. Network namespaces also prevented other programs on the system from sending traffic through the data diode, allowing us to collect accurate counts of packets sent and received. We also added a manual ARP entry (Address Resolution Protocol) for the receiver’s IP address, since ARP cannot resolve without bidirectional communication. We installed macOS Sequoia 15.7.4 on both Mac minis. After installing all available updates in March 2026, we disabled automatic updates. Python 3.9.6 was installed by default, but this version of Python does not include all the standard library features we needed. For consistency with Ubuntu, we used MacPorts to install Python 3.12.12, which we used to run our Python code. macOS does not support network namespaces, but similar to Linux we assigned IP addresses in the 10.0.1.0/24 range to the interfaces connected to the data diode. Also, we increased the maximum UDP packet size to allow sending packets of the same size as on Linux, and we added a manual ARP entry for the receiver’s IP address. Finally, we excluded our experiment logs from Spotlight search indexing and we disabled video wallpapers. Prior work suggests that increasing kernel network buffer sizes and process priority can increase the reliability of transfers [48, 41, 35]. However, these changes can require superuser privileges, making them incompatible with certain contexts. Linux and macOS default to 213 kB and 787 kB UDP receive buffers, respectively. On Linux, UDP buffers cannot be increased beyond this default without superuser privileges, whereas programs on macOS can request buffers up to 8.4 MB. Increasing the kernel’s network buffer size beyond these limits requires superuser privileges on Linux and macOS, and is impossible on Android and iOS. Process priority can be configured directly using superuser privileges on Linux and macOS. Without superuser privileges, process priority can only be adjusted within limits on Android [24], iOS, and macOS [8]. To increase the generalizability of our findings, we focused on the configuration options available without superuser privileges. We did use superuser privileges for three purposes. First, we used superuser privileges to use network namespaces on Linux. To confirm that using network namespaces did not affect the reliability of transfers, we reran some experiments without them, and we verified that the results were substantially the same. Second, we used supe-
ruser privileges to add a manual ARP entry on Linux and macOS. UDPcast and lidi fail to send data without a manual ARP entry, whereas netcat and our own programs support sending to a broadcast address (e.g., 10.0.1.255). However, in principle it should be possible to modify UDPcast and lidi to support sending to broadcast addresses. Third, when testing minimal on Linux and macOS, we used superuser privileges to increase the receive buffer size and maximum UDP payload size, respectively. On Linux, we increased the receive buffer size to 8.4 MB, for comparison to macOS. On macOS, we increased the maximum UDP payload size to 65,507 bytes, for comparison to Linux. By default, macOS only allows maximum payload sizes of 1,472 and 9,216 bytes for broadcast and unicast packets, respectively.1 Our results show how to achieve reliable transfers even without superuser privileges. Our goal was to determine the optimal configuration for each data transfer program. Specifically, the configuration with minimum data transfer duration for which all transfers would succeed. We tested the software by repeatedly transferring data through the data diode, and comparing the checksums of the sent and received data. We chose to transfer 1 Gbit (109 bits) of randomly generated data in each trial. This value was large enough to exceed the default network buffer size, and small enough to allow testing many transfers in a reasonable amount of time. Figure 3 in Appendix A describes our experimental design in pseudocode. As part of each transfer, we also recorded network statistics (e.g., the number of packets sent and received), which we used to diagnose the reasons for packet loss. We power cycled the workstations between each experiment. Next, we explain the configurations we tested for netcat, UDPcast, and lidi. Configuring netcat We tested the OpenBSD rewrite of netcat, available in the netcat-openbsd package, since it is installed by default in Ubuntu. netcat does not offer any options to increase the reliability of transfers. Figure 4 in Appendix A shows the commands we used to test netcat. Configuring UDPcast We tested UDPcast version 20120424-2build1. Although newer releases are available on UDPcast’s website [32], we tested the latest version available from Ubuntu’s package manager. Figure 5 in Appendix A shows the commands we used to test UDPcast. In our testing, we varied the transfer rate and forward error 1 UDP packets with payloads exceeding 1,472 bytes will be fragmented across multiple standard Ethernet frames, which have a 1,500 byte maximum transmission unit (MTU). Network hardware often supports Ethernet jumbo frame MTUs up to 9,216 bytes. IPv4 limits UDP payloads to 65,507 bytes.
5
correction (FEC) settings. FEC is implemented using an erasure code based on Vandermonde matrices [44]. The FEC setting specifies how many “stripes” each chunk of data is split into, the number of FEC packets included with each stripe, and the number of data packets in each stripe [31]. For example, with --fec 8x16/128, each chunk of data will be sent in 8 stripes, and each stripe will have 16 FEC packets and 128 data packets.
include configuration options to limit the maximum bitrate, adjust the maximum size of packets, request a different receive buffer size, and to change how data is written to standard output by the receiver. We observed that netcat is single-threaded, whereas UDPcast and lidi are multithreaded, and we wanted to measure the impact of multithreading on performance. Thus, we included options to control how the receiver writes to standard output: after the transfer completes, using a helper thread, using a thread orchestrated by asyncio, and simply writing using the main thread. Finally, we included a feature which gave us detailed insight into exactly which packets are dropped in each transfer. When the packet details feature is enabled, both the sender and receiver save each packet in memory until the transfer completes. Afterwards, they write CSV files containing the SHA-256 digests of each packet. By comparing the CSV files from the sender and receiver, the indices of each dropped packet can be determined. Figure 7 in Appendix A shows the core of our program. Not counting comments, whitespace, argument parsing, and packet details logging, the core of our program includes just 44 lines of code.
Configuring lidi We tested lidi v2.1.0. Figure 6 in Appendix A shows the commands we used to test lidi. Although lidi’s transfer rate is not configurable, we varied lidi’s FEC settings. lidi implements FEC using RaptorQ codes [37, 42]. lidi’s --repair argument controls the amount of repair data, specified as a percent of the original data [5]. By default, lidi sends repair data equal to 2% of the original data. Error correction data is associated with a block of original data, with a default size of 734,928 bytes. We observed that when lidi’s transfers fail, the receiving program never exits. Thus, we used to the timeout command to terminate the receiving program after 60 seconds. When analyzing the results, we counted these transfers as failures with undefined durations.
3.3
Developing pydiode We used the findings from our minimal program (§ 4.1 and § 4.2) to develop pydiode. pydiode is designed for reliable one-way transfers on Linux and macOS, without needing superuser privileges. pydiode uses redundancy to mitigate the patterns of packet loss we observed when testing minimal. pydiode sends chunks of data a configurable number of times, where each chunk is composed of a configurable number of packets. The receiver uses each packet’s sequence number to reassemble the original chunk of data, even if packets arrive out of order. The receiver waits to output data until all of a chunk’s packets have been received. To distinguish between chunks, pydiode alternates the color of the chunk between “red” and “blue.” When the data transfer is complete, a “black” chunk is sent, indicating the end-offile. Each “black” packet also contains a SHA-256 digest of the transmitted data, which the receiver compares to its received data to detect transfer failures. Each packet consists of a seven byte header, the payload, and possibly padding to ensure uniform packet sizes. The header includes the chunk color, the number of packets in the chunk, the sequence number of the packet, and the payload length. Figure 2 depicts a series of packets sent by pydiode. On Linux and macOS, we observed rare, short clusters of packet loss (up to 80 packets) at unpredictable locations within transfers. To mitigate packet loss, pydiode defaults to a chunk length of 100 packets, and sends each chunk twice: this makes pydiode resilient to packet loss of up to 100 sequential packets. Also, on both
Software Development
First, we developed a minimal program for one-way transfers (“minimal”). We used minimal to establish a performance baseline for comparison with the other programs we evaluated. We also used minimal to record exactly which packets were dropped in each transfer. Next, we developed a more full-featured program for one-way file transfers (“pydiode”). pydiode includes mitigations for packet loss and can detect transfer errors. Developing a Minimal Program To establish a performance baseline, we developed a minimal program for one-way transfers. Our nonfunctional design requirements were that the software: be cross-platform, be understandable by the widest audience possible, minimize the number of lines of code, and avoid dependencies. Thus, we developed the software in Python. When sending, minimal reads data from standard input, and sends the data in UDP packets to the receiver. When receiving, minimal listens for UDP packets, writes their payloads to standard output, and ends the transfer after 200 ms elapse without receiving another packet. We initially tested using a 100 ms timeout, but this caused some transfers to exit prematurely on macOS due to timer coalescing [7]. We 6
2
0
1
2
0
1
2
0
1
2
0
0
0
0
0
0
D E
F
D E
F
G H
I
G H
I
SHA
A B C
1
SHA
2
SHA
1
0
Black Chunk SHA
Payload A B C
0
Red Chunk
SHA
Sequence Number 0 1 2
Blue Chunk
SHA
Red Chunk
Figure 2: pydiode sends packets in “chunks,” which alternate between “red” and “blue.” For resilience against packet loss, each chunk is transmitted a configurable number of times. The end-of-file is signalled by a “black” chunk, which contains a SHA-256 digest of the transmitted data. This diagram shows three distinct chunks of data. Each chunk of data consists of three packets, with sequence numbers 0, 1, and 2. Linux and macOS we identified configurations for which lower bitrates were less reliable than higher bitrates. Thus, pydiode uses retransmission and padding to send data at the configured bitrate, even if pydiode’s input stream supplies data at a lower rate. pydiode enforces a maximum bitrate by sleeping after sending each packet, and pydiode uses a helper thread to output received data. On Linux, pydiode sends 65,507 byte payloads. On macOS, pydiode sends 1,472 byte payloads, since macOS does not allow sending larger broadcast packets. pydiode requests an 8.4 MB receive buffer size, which is allowed on macOS, but is ignored on Linux.
4
privileges. However, we identified multiple reliable configurations which do not require superuser privileges. The fastest of these reliable configurations used a helper thread to write to standard output, 65,507 byte payloads, and limited the bitrate to 500 Mbit/sec. Notably, we did not identify any reliable configurations when the main thread was used for writing. Inspecting our instrumentation data, we see that almost all transfer failures are accompanied by a nonzero number of RcvbufErrors, indicating that the receiving UDP buffer overflowed. Across 400,000 trials and 18,484 failures, only eight of the failures were not accompanied by RcvbufErrors. If a program uses a single thread, each write to standard output blocks the program from reading from the UDP buffer for a short period of time, during which time the buffer may overflow, causing incoming packets to be discarded. Thus, the receiving program should use separate threads for reading from the network and writing to standard output. Next, observe that transfers were more reliable when larger packets were sent. When using larger packets, fewer packets are needed. Since minimal handles each packet as it arrives, fewer packets results in fewer system calls and greater responsiveness. This pattern is clearest when using an unlimited bitrate: when ratelimiting while sending smaller packets, more sleeps are performed, and due to operating system scheduling data is sent even more slowly than requested. Finally, notice that for the helper, defer, and asyncio write methods, transfers are more reliable at lower bitrates. In contrast, the main write method performs much worse at 500 Mbit/sec and 750 Mbit/sec than at higher bitrates. Thus, lower-bitrate transfers are not necessarily more reliable than higher-bitrate transfers.
Results
First, we use our minimal program to characterize packet loss on Linux (§ 4.1) and macOS (§ 4.2). Next, we evaluate netcat (§ 4.3), UDPcast (§ 4.4), and lidi (§ 4.5) on Linux. Finally, we evaluate pydiode on Linux (§ 4.6) and macOS (§ 4.7).
4.1
Performance Baseline on Linux
We conducted several experiments with our minimal program on Linux. First, we varied the maximum UDP payload size, using two different receive buffer sizes (Table 2a and Table 2b). Next, we varied the receiver’s write method (Table 3). In all experiments, we varied the sender’s rate limit, and we used minimal’s packet details feature to determine exactly which packets were dropped. Since the packet details feature takes extra time to write CSV files to disk, the durations in our tables are from 1,000 trials run without packet details. Our minimal program does not include any forward error correction or redundancy, so transfers will fail if even a single packet is dropped. Note that Linux blocks outgoing datagrams until it is ready to send them, so minimal’s unlimited configuration is limited by the bandwidth of the underlying network hardware. Our results show that increasing the receive buffer size from 213 kB to 8.4 MB made transfers significantly more reliable. With an 8.4 MB receive buffer, only one transfer failed for four different configurations. Unfortunately, increasing the receive buffer requires superuser
Next, we analyzed the packet details CSV files from the sender and receiver. By comparing the CSV files, we determined the indices of each dropped packet. First, we compared the number of RcvbufErrors to the number of packets that didn’t reach the receiving program. In all but eight trials, the number of RcvbufErrors exactly matched the number of dropped packets. Considering all trials, 99.99% of packet loss was due to RcvbufErrors, confirming that most packet loss is due 7
minimal on Linux, 213 kB Receive Buffer
minimal on Linux, 8.4 MB Receive Buffer
Max Bitrate
Max Payload
Succeeded
Duration
Max Bitrate
Max Payload
Succeeded
Duration
500 Mbit/s 750 Mbit/s 1 Gbit/s unlimited 500 Mbit/s 750 Mbit/s 1 Gbit/s unlimited 500 Mbit/s 750 Mbit/s 1 Gbit/s unlimited
1472 1472 1472 1472 9216 9216 9216 9216 65507 65507 65507 65507
100.00% 100.00% 99.99% 9.85% 100.00% 99.98% 99.97% 99.36% 100.00% 99.99% 99.94% 99.95%
7.33 s 6.39 s 5.70 s 1.32 s 3.03 s 2.39 s 2.03 s 1.32 s 2.45 s 1.74 s 1.40 s 1.31 s
500 Mbit/s 750 Mbit/s 1 Gbit/s unlimited 500 Mbit/s 750 Mbit/s 1 Gbit/s unlimited 500 Mbit/s 750 Mbit/s 1 Gbit/s unlimited
1472 1472 1472 1472 9216 9216 9216 9216 65507 65507 65507 65507
100.00% 100.00% 100.00% 99.99% 100.00% 100.00% 99.99% 99.99% 100.00% 100.00% 100.00% 99.99%
7.33 s 6.39 s 5.69 s 1.32 s 3.03 s 2.39 s 2.03 s 1.32 s 2.45 s 1.74 s 1.40 s 1.31 s
(a) On Linux, programs are limited to a 213 kB receive buffer by default.
(b) For comparison with macOS, we used superuser privileges to request an 8.4 MB receive buffer.
minimal on macOS, 213 kB Receive Buffer
minimal on macOS, 8.4 MB Receive Buffer
Max Bitrate
Max Payload
Succeeded
Duration
Max Bitrate
Max Payload
Succeeded
Duration
500 Mbit/s 750 Mbit/s 1 Gbit/s 500 Mbit/s 750 Mbit/s 1 Gbit/s 500 Mbit/s 750 Mbit/s 1 Gbit/s
1472 1472 1472 9216 9216 9216 65507 65507 65507
0.35% 0.00% 0.00% 97.99% 99.97% 99.98% 98.22% 98.50% 98.90%
3.38 s 2.35 s 1.84 s 3.11 s 2.17 s 1.86 s 3.20 s 2.20 s 1.66 s
500 Mbit/s 750 Mbit/s 1 Gbit/s 500 Mbit/s 750 Mbit/s 1 Gbit/s 500 Mbit/s 750 Mbit/s 1 Gbit/s
1472 1472 1472 9216 9216 9216 65507 65507 65507
100.00% 100.00% 100.00% 100.00% 100.00% 100.00% 100.00% 100.00% 99.97%
3.38 s 2.35 s 1.84 s 3.11 s 2.17 s 1.86 s 3.20 s 2.20 s 1.66 s
(c) For comparison with Linux, we requested a 213 kB receive buffer.
(d) On macOS, programs can request up to an 8.4 MB receive buffer without using superuser privileges.
Table 2: minimal results on Linux and macOS, showing the effect of payload size and receive buffer size. We always used the helper write method. For each configuration, we performed 10,000 trials transferring 1 Gbit of data. The fastest reliable configurations which do not require superuser privileges are colored green. Configurations which require superuser privileges are colored yellow.
8
minimal on Linux, Comparing Write Method Max Bitrate
Write Method
Succeeded
Duration
500 Mbit/s 750 Mbit/s 1 Gbit/s unlimited 500 Mbit/s 750 Mbit/s 1 Gbit/s unlimited 500 Mbit/s 750 Mbit/s 1 Gbit/s unlimited 500 Mbit/s 750 Mbit/s 1 Gbit/s unlimited
helper helper helper helper defer defer defer defer asyncio asyncio asyncio asyncio main main main main
100.00% 100.00% 99.92% 99.81% 100.00% 100.00% 99.99% 99.94% 100.00% 99.99% 99.98% 99.91% 32.62% 75.29% 99.11% 99.61%
2.45 s 1.74 s 1.40 s 1.31 s 2.49 s 1.78 s 1.44 s 1.35 s 2.52 s 1.89 s 1.52 s 1.39 s 2.44 s 1.74 s 1.40 s 1.31 s
4.2
Performance Baseline on macOS
Our preliminary testing on macOS showed that with an unlimited transfer rate, all transfers failed, and transfers finished far too quickly. This is because the sendto function doesn’t block on macOS, a behavior it inherits from FreeBSD [52, 53]. Instead, outgoing datagrams are discarded if the network stack is not ready to send them. Thus, our code must rate-limit calls to sendto. Another challenge is that although macOS can receive datagrams of any size, macOS limits the size of outgoing datagrams. By default, outgoing broadcast payloads are limited to 1,472 bytes. Packets directed to unicast addresses can have payloads of up to 9,216 bytes, but this requires creating a manual ARP entry using superuser privileges. Using superuser privileges, it is also possible to enable unicast packets with payloads up to 65,507 bytes, the maximum possible for IPv4 UDP packets. Table 2 compares the results from our minimal program on Linux and macOS. Programs on macOS can request receive buffers up to 8.4 MB, whereas on Linux superuser priviledges are required to exceed the limit of 213 kB. For comparison with Linux, we tested using both 213 kB and 8.4 MB receive buffers (Table 2c and Table 2d, respectively). We varied the maximum UDP payload size and bitrate, and all configurations used the helper write method. Consistent with our Linux results, we see a strong association between RcvbufErrors and transfer failures: only one transfer failed without an accompanying RcvbufError, and only one packet was dropped in that transfer. Since macOS does not support network namespaces, our network statistics are affected by other traffic on the device, making it difficult to count exactly how much packet loss was due to factors other than RcvbufErrors. However, all the transfers which succeeded had zero RcvbufErrors, and the transfers which failed had an average of 1,379 RcvbufErrors. As on Linux, lower bitrates are sometimes less reliable than faster bitrates. Different than our Linux results, 65,507 byte payloads can be less reliable than smaller payloads. Only three transfers failed when using an 8.4 MB buffer, and all had 65,507 byte payloads and a 1 Gbit/sec bitrate. Examining exactly which packets were dropped in these transfers, we saw one cluster of packet loss in each failed transfer, with between 13 and 80 immediately adjacent packets dropped. The number of dropped packets exactly matches the number of RcvbufErrors recorded for those transfers. Our results also show that increasing the receive buffer size significantly improves reliability. This effect is especially pronounced when using 1,472 byte payloads and a 1 Gbit/sec bitrate: all transfers succeeded with an 8.4 MB buffer, whereas all transfers failed with a 213 kB buffer. Despite macOS limiting the size of outgoing broadcast packets to 1472 bytes, reliable
Table 3: On Linux, we varied the receiving program’s write method. We used 65,507 byte UDP payloads and a 213 kB receive buffer, the maximum without using superuser priviledges. For each configuration, we performed 10,000 trials transferring 1 Gbit of data.
to the receiving program being insufficiently responsive. The unexplained packet loss occurs lower in the network stack, possibly due to the Linux kernel itself dropping packets, or hardware-level errors. Unexplained packet loss occurred at a rate of approximately one in 5 × 107 packets. However, the packet loss was not randomly distributed: in the trials with unexplained packet loss, between one and 109 packets were dropped, and when multiple packets were dropped, they were either adjacent or in close proximity. The fastest reliable configurations we identified used the helper write method, 65,507 byte payloads, and a bitrate limit of 500 Mbit/sec. Faster transfers could be acheived if packet loss was mitigated using redundancy or forward error correction, rather than a rate limit. Thus, we analyzed exactly which packets were dropped in the corresponding unlimited bitrate transfers. In total, 25 of 30,000 trials failed. Of these failed trials, 24 trials had packet loss due to RcvbufErrors, and one trial had packet loss due to unexplained reasons. Between one and six packets were dropped, and when multiple packets were dropped, they were either adjacent or in close proximity. The maximum difference between the indices of the first and last dropped packets was eight. Thus, both the receiving UDP buffer overflowing and issues lower in the network stack can cause multiple packets to be dropped in succession. Since packet loss occurs in clusters, error correction should include redundant information some distance from the data it is protecting. 9
UDPcast on Linux
transfers can be achieved by requesting a larger receive buffer.
4.3
Evaluating netcat on Linux
netcat does not offer any configurable options which would increase the reliability of transfers, so we simply performed 10,000 trials transferring 1 Gbit of data. 93.89% of trials completed successfully, in an average of 2.07 seconds. Since netcat does not exit until one second has elapsed since it last received a packet, the data itself was transferred in 1.07 seconds. Transfers failed exactly when there were a nonzero number of RcvbufErrors, indicating that the receiving UDP buffer overflowed. netcat is written in C, which could offer an advantage relative to minimal’s Python code. However, netcat often performed worse than our minimal program. Since netcat is single-threaded, it is particularly interesting that netcat performed worse than minimal’s single-threaded (i.e., main write method), unlimited bitrate, 65,507 byte payload configuration: 93.89% vs 99.61% of transfers succeeded for netcat and minimal, respectively (Table 3). Whereas minimal was configured to send packets with 65,507 byte payloads, tcpdump shows that netcat sends packets with 16,384 byte payloads. Furthermore, strace shows that both netcat and minimal trigger write syscalls for each packet they receive. These findings are consistent with our minimal results showing that using larger payloads improves reliability on Linux (Table 2a). minimal’s superior performance relative to netcat is likely due to processing fewer packets, or writing to standard output fewer times.
4.4
Max Bitrate
FEC
Succeeded
Duration
500 Mbit/s 750 Mbit/s 1 Gbit/s unlimited 500 Mbit/s 750 Mbit/s 1 Gbit/s unlimited 500 Mbit/s 750 Mbit/s 1 Gbit/s unlimited 500 Mbit/s 750 Mbit/s 1 Gbit/s unlimited
None None None None 8x8/128 8x8/128 8x8/128 8x8/128 8x16/128 8x16/128 8x16/128 8x16/128 8x32/128 8x32/128 8x32/128 8x32/128
82.62% 76.99% 77.97% 78.79% 99.78% 99.05% 96.67% 96.63% 99.99% 99.87% 99.61% 99.63% 100.00% 100.00% 99.88% 99.91%
2.61 s 1.90 s 1.60 s 1.61 s 4.03 s 3.19 s 2.78 s 2.79 s 4.45 s 3.41 s 2.98 s 2.99 s 5.09 s 3.98 s 3.52 s 3.52 s
Table 4: We tested transferring 1 Gbit of data using UDPcast on Linux. For each configuration, we performed 10,000 trials. The fastest reliable configuration we identified is colored green. The default, unreliable configuration is colored red.
UDPcast is written in C and is multithreaded, so it is suprising that our minimal program outperforms UDPcast. However, UDPcast sends packets with 1,472 byte payloads, and it does not support sending packets with larger payloads [31]. The results from our minimal program suggest that UDPcast would be more reliable if it was modified to send larger packets. Finally, in one instance the receiving program failed to exit after running for multiple hours. This trial was configured with a 750 Mbit/sec bitrate and 8x8/128 FEC. In Table 4, this trial is represented as a failure with an undefined duration. After we manually reran the sending program, the receiving program exited normally (i.e., with zero as its exit code). Thus, we infer that the receiving program did not receive the hello packet from the first run of the sending program [31]. We attempted an experiment in which UDPcast was configured to use an additional transmission of the hello packet, and we encountered the same problem, so it is unclear how to solve the issue.
Evaluating UDPcast on Linux
Table 4 shows the results from testing UDPcast. We measured the effect of rate limiting and forward error correction (FEC) on transfer reliability. First, notice that without FEC, transfers have high failure rates. Second, note that some FEC configurations were more reliable than others. In particular, when using the 8x8/128 and 8x16/128 FEC configurations, none of the tested bitrates were reliable. In contrast, when using the 8x32/128 configuration at 500 Mbit/sec or 750 Mbit/sec bitrates, all transfers succeeded. These configurations used 8, 16, and 32 FEC packets per stripe, respectively. When there were no RcvbufErrors, all transfers succeeded, but transfers sometimes failed when there were RcvbufErrors. For example, when using the 8x8/128 FEC configuration, 49% of transfers had nonzero RcvbufErrors. Of those transfers with nonzero RcvbufErrors, 4% failed and 96% succeeded, with a median of 238 and 7 RcvbufErrors, respectively. As expected, FEC has difficulty mitigating large numbers of dropped packets.
4.5
Evaluating lidi on Linux
Table 5 shows the results from testing lidi. lidi’s transfer rate is not configurable, so we only measured the effect of FEC on reliability. We observed that when lidi’s transfers failed, the receiving program never exited. Thus, we used to the timeout command to terminate the receiving program after 60 seconds, and we counted these transfers as failures with undefined du10
lidi on Linux Max Bitrate
FEC
Succeeded
Duration
unlimited unlimited unlimited unlimited unlimited
2% 10% 25% 50% 100%
99.29% 99.53% 99.95% 100.00% 100.00%
1.11 s 1.19 s 1.35 s 1.61 s 2.14 s
whereas minimal waits until 200 ms elapse without receiving another packet.
4.7
On macOS, we tested pydiode using 1,472 byte payloads sent to a broadcast IP address, a configuration which does not require superuser privileges. Table 6b shows the effect of rate limiting and redundancy on transfer reliability. Consistent with our minimal results, all redundant and non-redundant transfers completed successfully.
Table 5: We tested transferring 1 Gbit of data using lidi on Linux. For each configuration, we performed 10,000 trials. The fastest reliable configuration we identified is colored green. The default, unreliable configuration is colored red.
5
rations. Transfers were reliable when we increased the amount of FEC data from the default of 2% to at least 50%. Similar to UDPcast, when there were no RcvbufErrors, all transfers succeeded, but transfers sometimes failed when there were RcvbufErrors. For example, when using the 2% FEC setting, 79 transfers had nonzero RcvbufErrors. Of those 2% FEC transfers with nonzero RcvbufErrors, 71 failed and only eight succeeded, with a median of 57 and 2.5 RcvbufErrors, respectively. When using the 50% FEC setting, 93 transfers had nonzero RcvbufErrors. Of those 50% FEC transfers with nonzero RcvbufErrors, all succeeded, and there were a median of 52 RcvbufErrors. Based on our minimal program’s behavior, we expect that lidi’s packet loss occurs in clusters, meaning that many packets are lost from the same block of data. Thus, a large amount of FEC data is required to mitigate large numbers of dropped packets. Our minimal program showed that transfers encounter more RcvbufErrors when small payloads are sent. Using tcpdump, we observe that lidi sends packets with a maximum payload of 1,468 bytes. Similar to our minimal program, lidi might encounter fewer RcvbufErrors if it sent larger payloads.
4.6
Evaluating pydiode on macOS
Limitations
There are several limitations to consider when interpreting our results. First, when testing netcat, UDPcast, lidi, and pydiode, we only tested configurations which did not require superuser privileges. In particular, we did not increase network buffer sizes beyond the limits imposed by macOS and Linux, and we did not modify process priority. Configurations which require superuser privileges are incompatible with certain contexts (e.g., mobile devices). To increase the generalizability of our findings, we focused on configurations available without superuser privileges. Consistent with prior work, our minimal results (Table 2) show that larger network buffer sizes reduce packet loss [48, 41, 35]. Second, we only tested using two platforms: Intel NUC 12 mini PCs running Ubuntu Desktop 24.04.3 LTS, and M1 Mac minis running macOS Sequoia 15.7.4. Our results show that transfer reliability varies by platform, and we did not test using mobile devices and Microsoft Windows. However, mobile devices overwhelmingly run Android and iOS, which share network stacks with Linux and macOS, respectively. Thus, our results will serve as a useful point of comparison when testing on other platforms. Our results show that commodity hardware and open source software can be used to implement reliable oneway data transfers. However, real-world deployments will also depend on the reliability of other components. During our testing, we discovered rare but persistent reliability issues with the underlying platforms. When testing with the PCs, the SSH connections between the machines which we used to automate testing occasionally terminated unexpectedly. The issue was isolated to the secondary PCI Ethernet port, and never occurred after we switched to a USB-C Ethernet port. We encountered no issues using the PCs’ primary built-in Ethernet ports for one-way traffic. When testing using the M1 Mac minis, we encountered repeated kernel panics, suggesting that we triggered a kernel bug. Most panics referenced the built-in Ethernet port (AppleT810xPCIePort). We encountered the panics on
Evaluating pydiode on Linux
We applied the findings from our minimal program to develop a more robust program, pydiode (§ 3.3). On Linux, we tested pydiode using 65,507 byte payloads sent to a broadcast IP address, a configuration which does not require superuser privileges. Table 6a shows the effect of rate limiting and redundancy on transfer reliability. No transfers failed when redundant transmissions were enabled, showing that redundancy mitigates the clusters of packet loss we measured using our minimal program. Also, notice that pydiode’s single-redundancy unlimited transfers completed faster than similarly configured minimal transfers: this is because pydiode exits after receiving an end-of-file packet, 11
pydiode on Linux Max Bitrate
Redundancy
Succeeded
Duration
500 Mbit/s 750 Mbit/s 1 Gbit/s unlimited 500 Mbit/s 750 Mbit/s 1 Gbit/s unlimited
1 1 1 1 2 2 2 2
100.00% 99.89% 99.74% 99.62% 100.00% 100.00% 100.00% 100.00%
2.54 s 1.74 s 1.35 s 1.21 s 5.02 s 3.40 s 2.64 s 2.35 s
pydiode on macOS
(a) On Linux, pydiode uses 65,507 byte payloads and is limited to a 213 kB receive buffer.
Max Bitrate
Redundancy
Succeeded
Duration
500 Mbit/s 750 Mbit/s 1 Gbit/s 500 Mbit/s 750 Mbit/s 1 Gbit/s
1 1 1 2 2 2
100.00% 100.00% 100.00% 100.00% 100.00% 100.00%
3.21 s 2.18 s 1.65 s 6.32 s 4.25 s 3.18 s
(b) On macOS, pydiode uses 1,472 byte payloads and an 8.4 MB receive buffer.
Table 6: pydiode results on Linux and macOS. For each configuration, we performed 10,000 trials transferring 1 Gbit of data. On each platform, the fastest reliable configuration we identified is colored green. The default configurations are also reliable but slower, and are colored blue.
6.1
three different M1 Mac minis, on both the sender and receiver, and when using both macOS Tahoe 26.2 and macOS Sequoia 15.7.4. Panics even occurred when no superuser commands had been used. When we encountered a kernel panic, we restarted the experiment we were running from the beginning. Although there is an element of randomness, we can reproduce the kernel panics: when repeatedly sending 1 Tbit streams of data using pydiode, we encountered kernel panics after 35, 48, and 71 hours of testing. We anticipate these kernel panics would be an obstacle to deploying Mac minis in industrial contexts, but would not be an issue for endusers.
6
Packet Loss Is Caused by an Insufficiently Responsive Receiving Program
Packet loss between the sending and receiving programs interferes with reliable transmission of data. But why is there packet loss between two devices which have a direct physical connection? We transferred terabytes of data through our data diode using our minimal program, and we found that 99.99% of packet loss was caused by receiving program unresponsiveness (§ 4.1). When the receiving program processes incoming packets too slowly, its UDP buffer overflows, causing clusters of packet loss. Rare packet loss from lower in the network stack also occurred in clusters. To mitigate clusters of packet loss, redundant information should not be sent in adjacent packets. We discovered several ways to minimize packet loss without using superuser privileges. First, packet loss can be minimized by using a separate helper thread to write out received data. Second, programs on Linux can send packets with larger payloads. Third, programs on macOS can request a larger receive buffer size. We also made several counterintuitive discoveries. First, on both Linux and macOS, we observed cases where transferring data more slowly decreased transfer reliability. This contradicts recommendations from prior work [41, 35]. If the sending program reads from a stream of data, its input bandwidth may vary over time. This suggests that the sending program should use retransmission and padding to maintain a consistent output bandwidth for more predictable performance. Second, although we found that sending larger packets increased reliability on Linux, sending larger packets on macOS decreased reliability in some cases. Thus, the optimal payload size is platform-dependent. We incorporated our findings into the development of pydiode, a cross-platform program for reliably transferring information through
Discussion
Data diodes are physically limited to only transfer data in one direction, thereby offering a physical defense against sophisticated malware. Although commercially available data diodes are expensive, data diodes can also be constructed from commodity network hardware (§ 3.1). However, specialized software is needed to send data through a data diode reliably: the receiving program cannot request retransmission of dropped packets, so packet loss must be minimized and mitigated. In this section, we summarize our results to answer our research questions. First, we explain the cause of packet loss in data diodes, and ways to mitigate packet loss (§ 6.1). Next, we describe the relative performance of the data transfer software we evaluated (§ 6.2). Finally, we offer advice to software developers based on our own experience (§ 6.3). 12
Fastest Reliable Configuration Platform Program Duration Linux minimal 2.45 sec Linux pydiode 2.35 sec Linux UDPcast 3.98 sec Linux lidi 1.61 sec macOS minimal 1.84 sec macOS pydiode 1.65 sec
sidering. First, some programs may not be available for your operating system. UDPcast, lidi, and pydiode all run on Linux. Only pydiode supports macOS. Currently, only UDPcast supports Windows, though we are working on Windows support for pydiode. Second, only pydiode supports sending to broadcast addresses. UDPcast and lidi can only send to unicast addresses, which requires using superuser privileges to create a manual ARP entry. Third, the codebases have different sizes, which has implications for auditing the code for security vulnerabilities. pydiode has 1,887 lines of Python code, lidi has 3,592 lines of Rust code, and UDPcast has 7,709 lines of C code. UDPcast does not require dependencies and pydiode only depends on the Python standard library. However, lidi requires many direct and indirect Rust dependencies, which include 887,355 additional lines of Rust code. Finally, lidi uses an open source implementation of RaptorQ codes for forward error correction, but Qualcomm owns many patents related to RaptorQ [42, 43]. In contrast, UDPcast and pydiode’s underlying technology is public domain.
Table 7: For each program, we identified the configuration with the minimum average transfer duration for which all transfers succeeded. We omitted netcat, because only 93.89% of netcat’s trials completed successfully.
data diodes (§ 3.3).
6.2
When Properly Configured, Multiple Programs Offer Reliable Transfers
We tested three existing open source programs on Linux: netcat, UDPcast, and lidi. We found that only 93.89% of netcat’s transfers succeeded (§ 4.3), and that transfers were also unreliable when using UDPcast (§ 4.4) and lidi (§ 4.5) in their default configurations. However, we discovered configurations for UDPcast and lidi for which all transfers succeeded. We also tested our own programs, minimal and pydiode, on both Linux and macOS. Similar to minimal, pydiode is written in Python, though it differs in two important ways. First, pydiode uses redundancy to mitigate the clusters of packet loss we observed on Linux and macOS. Second, the pydiode receiver detects transfer errors by calculating a SHA-256 digest of the data it receives, and comparing it to a SHA-256 digest calculated by the sending program. Table 7 shows the fastest reliable configuration for each program. Unlike UDPcast and lidi, neither of our programs employ forward error correction (FEC). Furthermore, UDPcast and lidi are written in C and Rust, respectively, which offer performance benefits relative to Python. Nevertheless, both minimal and pydiode offer faster reliable transfers than UDPcast. Compared to lidi, minimal and pydiode are only 52% and 46% slower, respectively. Our results show that although FEC can improve reliability, it is neither necessary nor sufficient. In theory, FEC is more efficient than simple retransmission of data, offering greater error correction at a given bitrate. In practice, if the receiving program does not read from its UDP network buffer frequently enough, the number of packets dropped can exceed what FEC can mitigate. When choosing between these programs, there are several factors beyond data transfer speed worth con-
6.3
Advice For Software Developers
We offer several recommendations to developers seeking to implement software for one-way file transfers. First, it is important to establish a performance baseline on your platform using a simple program, like our minimal program. As you implement more complex software with additional features, you should compare it to your performance baseline. Second, you should incorporate our findings to minimize packet loss (§ 6.1), considering factors like multithreading, payload size, UDP buffer size, and transfer bitrate. We recommend focusing on minimizing packet loss before mitigating packet loss. If you attempt to mitigate packet loss through methods like FEC, you should confirm you are not increasing packet loss by making your program less responsive. Finally, you should implement your software in a modular fashion. Stream-based input formats are the most flexible, since they allow sending individual files via file redirection, directories of files using tar, or network traffic using a proxy program. For example, pydiode’s sending program reads from standard input, and its receiving program writes to standard output. pydiode supports sending directories of files using a process pipeline: Python’s tarfile module converts directories to and from streams, and pydiode simply transfers these streams through the data diode. Similarly, pydiode can transfer network traffic using protocol proxies. For example, Figure 8 in Appendix A shows a proxy for the MQTT protocol, which is widely used in IoT deployments. 13
7
Conclusions and Future Work
[3] Ross Anderson. Why information security is hard - an economic perspective. In Seventeenth Annual Computer Security Applications Conference, pages 358–365, New Orleans, LA, USA, 2001. IEEE Comput. Soc. http://ieeexplore.ieee.org/docume nt/991552/.
Data diodes offer a physical defense against cyberattacks by enforcing the direction of information flow. If information cannot enter or leave a system, integrity or confidentiality can be ensured, respectively. Although commercially available data diodes are expensive, our results show that data diodes can be assembled using commodity hardware and open source software for a fraction of the cost. Our research will unlock new opportunities. First, security practitioners, educators, and others will benefit from greater access to data diodes. Security teams can test the suitability of data diodes in their environment with minimal capital investment. We have also found that data diodes are a useful educational tool in our undergraduate Computer Networks course. Second, open source solutions support supply chain diversification and avoid vendor lock-in. Commercially available data diodes use proprietary software, and are not interoperable with each other. In contrast, open source data diodes are hardware agnostic, and are compatible with fiber-optic network equipment from different vendors. These benefits are magnified by recent uncertainty in international relations. Finally, data diodes can be deployed in novel contexts to defend against targeted cyberattacks. For example, data diodes can be used to harden messaging apps like Signal against spyware [50]. We anticipate several areas for future work. First, packet loss minimization and mitigation can be implemented more effectively to support faster reliable transfers. UDPcast and lidi use forward error correction (FEC) to mitigate packet loss, but if their receiving programs were more responsive, they could reduce packet loss significantly. Similarly, pydiode could be modified to use FEC instead of simple redundancy, allowing more efficient packet loss mitigation. Second, open source solutions could be compared against commercial products. Commercial vendors do not publish data on the reliability of their products, so their relative performance is unclear. Finally, alternative data diode hardware can be developed and evaluated [55, 56, 30, 39, 27].
[4] ANSSI. lidi, Feb 2026. https://github.com/ANS SI-FR/lidi. [5] ANSSI. lidi Command line parameters, Jan 2026. https://anssi-fr.github.io/lidi/parameter s.html. [6] Anthropic. Project Glasswing: Securing critical software for the AI era, April 2026. https: //www.anthropic.com/glasswing. [7] Apple. Power Efficiency in OS X, October 2013. https://www.apple.com/media/us/osx/2013/do cs/OSX_Power_Efficiency_Technology_Overvie w.pdf. [8] Apple. DispatchQoS, May 2026. https://develo per.apple.com/documentation/dispatch/dispa tchqos. [9] Ross D. Arnold. Strategies for Transporting Data Between Classified and Unclassified Networks. Technical report, Defense Technical Information Center, Fort Belvoir, VA, March 2016. http s://apps.dtic.mil/sti/citations/AD1005160. [10] Courtney Barry. Data Diodes for Cyber Security. NRECA Cooperative Research Network TechSurveillance Magazine, March 2012. https://we b.archive.org/web/20231220100501/http://co urtneybarry.com/Images/TS_Data_Diodes.pdf. [11] David Elliott Bell. Looking Back at the Bell-La Padula Model. In 21st Annual Computer Security Applications Conference (ACSAC’05), pages 337– 351, Tucson, AZ, USA, 2005. IEEE. http://ieee xplore.ieee.org/document/1565261/. [12] David Elliott Bell and Leonard J. LaPadula. Secure Computer Systems: Mathematical Foundations. Technical Report MTR-2547, The MITRE Corporation, March 1973. https://apps.dtic. mil/sti/tr/pdf/AD0770768.pdf.
References [1] Lillian Ablon and Andy Bogart. Zero Days, Thousands of Nights: The Life and Times of Zero-Day Vulnerabilities and Their Exploits. Technical report, RAND Corporation, 2017. http://www.ra nd.org/pubs/research_reports/RR1751.html.
[13] Brad Bergemann. Cyber Security Event Notifications. Technical Report Regulatory Guide 5.83, U.S. Nuclear Regulatory Commission, July 2015.
[2] Advisory Committee on Reactor Safeguards Digital Instrumentation and Control. Official Transcript of Proceedings Nuclear Regulatory Commission. http s://www.nrc.gov/docs/ML2132/ML21320A055.pd f, October 2021.
[14] Ronen Bergman and Mark Mazzetti. The Battle for the World’s Most Powerful Cyberweapon. The New York Times, January 2022. https://www.ny times.com/2022/01/28/magazine/nso-group-i srael-spyware.html. 14
[15] Charles Berret. Guide to SecureDrop. Technical report, Tow Center for Digital Journalism, 2016.
[28] Industrial Control Systems Cyber Emergency Response Team. Recommended Practice: Improving Industrial Control System Cybersecurity with Defense-in-Depth Strategies, September 2016.
[16] Stéphanie Blanchet. BadUSB, the threat hidden in ordinary objects. Technical report, Bertin Technologies, June 2018.
[29] Biba J. Kenneth. Integrity considerations for secure computer systems. Technical Report MTR3153, The MITRE Corporation, Bedford, MA, June 1977. https://apps.dtic.mil/sti/tr/p df/ADA039324.pdf.
[17] CEA. Hairgap, Apr 2017. https://github.com/c ea-sec/hairgap. [18] Fred Cohen. Designing provably correct information networks with digital diodes. Computers & Security, 7(3):279–286, June 1988. https://www. sciencedirect.com/science/article/abs/pii/ 016740488890034X.
[30] klockcykel. DIY Data Diode, Sep 2024. https: //github.com/klockcykel/godiode. [31] Alain Knaff. UDPcast commandline options, Jan 2012. http://www.udpcast.linux.lu/cmd.html.
[19] James Downs. Cyber Security Programs For Nuclear Fuel Cycle Facilities. Technical Report Draft Regulatory Guide DG-5062, U.S. Nuclear Regulatory Commission, January 2017.
[32] Alain Knaff. UDPcast, May 2026. http://www.ud pcast.linux.lu. [33] Philippe Lagadec. Diode réseau et ExeFilter : 2 projets pour des interconnexions sécurisées. Proceedings of SSTIC06, 2006.
[20] Fend. Industries, May 2026. https://www.fend.t ech/industries.
[34] Robert D. Larkin, Torrey J. Wagner, and Barry E. Mullins. Securing Photovoltaic System Deployments with Data Diodes. In 2020 47th IEEE Photovoltaic Specialists Conference (PVSC), pages 2525– 2531, Calgary, AB, Canada, June 2020. IEEE. http s://ieeexplore.ieee.org/document/9300863/.
[21] Fend Incorporated. Fend XE15 Data Diode, Mar 2023. https://web.archive.org/web/20230315 012223/https://www.fend.tech/fend-xe15-d ata-diode. [22] Mailyn Fidler. Zero Progress on Zero Days: How the Last Ten Years Created the Modern Spyware Market. Nebraska Law Review, 103, May 2024.
[35] Honggang Lin. Research on Packet Loss Issues in Unidirectional Transmission. Journal of Computers, 8(10):2664–2671, October 2013. https: //web.archive.org/web/20240415211830/http: //www.jcomputers.us/vol8/jcp0810-29.pdf.
[23] Dan Goodin. 3 iOS 0-days, a cellular network compromise, and HTTP used to infect an iPhone, September 2023. https://arstechnica.com/se curity/2023/09/how-the-iphone-of-a-presi dential-candidate-in-egypt-got-hacked-for -the-2nd-time/.
[36] Hongyi Lu, Yechang Wu, Shuqing Li, You Lin, Chaozu Zhang, and Fengwei Zhang. BADUSB-C: Revisiting BadUSB with Type-C. 2021 IEEE Security and Privacy Workshops (SPW), 2021. [37] Lorenz Minder, Amin Shokrollahi, Mark Watson, Michael Luby, and Thomas Stockhammer. RaptorQ Forward Error Correction Scheme for Object Delivery. Request for Comments RFC 6330, Internet Engineering Task Force, August 2011. https: //datatracker.ietf.org/doc/rfc6330.
[24] Google. Android Thread MAX PRIORITY, May 2026. https://developer.android.com/refere nce/java/lang/Thread#MAX_PRIORITY. [25] Dmitry Grigoryev. Implement send-only (one-way) Ethernet cable, Dec 2017. https://electronics. stackexchange.com/a/279277.
[38] Office of Nuclear Regulatory Research. Cyber Security Programs For Nuclear Facilities. Technical Report Regulatory Guide 5.71, U.S. Nuclear Regulatory Commission, January 2010.
[26] Derek Harp, Bengt Gregory-Brown, Walter Risi, and Andrew Ginter. The (CS)2AI-KPMG Control System Cybersecurity Annual Report. Technical report, Control System Cyber Security Association International, March 2024.
[39] Markus Ottela. Tinfoil Chat, Apr 2023. https: //github.com/maqp/tfc.
[27] Cyber Innovation Hub. The Open Source Data Diode, Nov 2022. https://github.com/Cyber InnovationHub-NLD/OpenSourceDataDiode.
[40] Owl Cyber Defense. Data Diode Cybersecurity Products, May 2026. https://owlcyberdefense. com/products/data-diode-products/. 15
[41] Ludovic Piètre-Cambacédès and Pascal Sitbon. Deconstruction of some industrial control systems cybersecurity myths. Sixth American Nuclear Society International Topical Meeting on Nuclear Plant Instrumentation, Control, and Human-Machine Interface Technologies, 2009.
[53] user2085689. sendto() dgrams do not block for ENOBUFS on OSX, May 2013. https://stac koverflow.com/questions/16555101/sendto-d grams-do-not-block-for-enobufs-on-osx. [54] Stella Vouteva, Ruud Verbij, and Jarno Roos. Feasibility and Deployment of Bad USB. System and Network Engineering Master Research Project, University of Amsterdam, February 2015.
[42] QUALCOMM. RaptorQ™ Technical Overview. Technical report, 2010. https://www.qualcomm .com/content/dam/qcomm-martech/dm-assets/ documents/RaptorQ_Technical_Overview.pdf.
[55] Vrolijk. Get started with Data Diodes, Jan 2026. https://github.com/Vrolijk/OSDD.
[43] Qualcomm Incorporated. RaptorQ Patents, May 2026. https://patents.google.com/?q=(raptor q)&assignee=Qualcomm+Incorporated&num=100.
[56] Wavestone. Do Your Own Diode, Jan 2020. https: //github.com/wavestone-cdt/dyode.
[44] Luigi Rizzo. Effective erasure codes for reliable computer communication protocols. ACM SIGCOMM Computer Communication Review, 27(2):24–36, April 1997. https://dl.acm.org /doi/10.1145/263876.263881.
[57] Wikipedia. autonegotiation, May 2026. https: //en.wikipedia.org/wiki/Autonegotiation.
[45] Bruce Schneier and Barath Raghavan. How AI Is Changing Cybersecurity, April 2026. https://sp ectrum.ieee.org/ai-cybersecurity-mythos.
[59] Zerodium. Zerodium Exploit Acquisition Program, December 2024. https://web.archive.org/web/ 20241217190417/https://zerodium.com/progr am.html.
[58] Wikipedia. netcat, May 2026. https://en.wikip edia.org/wiki/Netcat.
[46] Sven Seeberg. A Data Diode with 2 Raspberry Pi and OpenBSD, Sep 2025. https://github.com/s venseeberg/data-diode. [47] Siemens. New Siemens data diode now available: secure monitoring of your networks, Dec 2017. ht tps://www.mobility.siemens.com/global/en/p ortfolio/rail/stories/new-siemens-data-d iode-now-available-secure-monitoring-of-y our-networks.html. [48] Malcolm W Stevens. An Implication of an Optical Data Diode. Technical Report DSTO-TR-0785, Information Technology Division Electronics and Surveillance Research Laboratory, 1999. [49] Peter Story. Building an Affordable Data Diode to Protect Journalists. In Workshop on Privacy Engineering in Practice (PEP ’23), August 2023. https://peterstory.me/publications/story_p ep_2023.pdf. [50] Peter Story. Defending Messaging Apps Against Spyware Using Data Diodes. In Free and Open Communications on the Internet, number 1, 2026. https://www.petsymposium.org/foci/2026/foc i-2026-0009.pdf. [51] Christopher Taylor. Wirehair, Dec 2023. https: //github.com/catid/wirehair. [52] The Python Software Foundation. Transports and Protocols, May 2026. https://docs.python.or g/3/library/asyncio-protocol.html#asyncio .DatagramProtocol.error_received. 16
A
Supplementary Figures
for i in range(10000): generate random data for each configuration combination: start receiving program start sending program wait for programs to exit compare SHA-256 of sent and received data
Figure 3: We tested transferring data through the data diode with different combinations of configurable options. We tested each combination 10,000 times, with randomly generated data each time.
# To send data nc -u -q 0 -s 10.0.1.2 10.0.1.1 1234 < /tmp/write/random_data # To receive data nc -u -w 1 10.0.1.1 -l 1234 > /tmp/random_data
Figure 4: Commands to send and receive data using netcat. The -u option enables UDP instead of TCP. The -s option specifies the source address. The -w 1 option causes netcat to exit one second after it stops receiving data, meaning that even small transfers take at least one second.
# To send data udp-sender --interface enp100s0 --mcast-rdv-address 10.0.1.1 \ --async --rexmit-hello-interval 10 --autostart 1 \ --max-bitrate 100000000 --fec 8x8/128 < /tmp/write/random_data # To receive data udp-receiver --nosync --interface enp100s0 > /tmp/random_data
Figure 5: Commands to send and receive data using UDPcast. The --max-bitrate “is the raw bitrate, including packet headers, forward error correction, retransmissions, etc. Actual payload bitrate will be lower” [31]. The --fec argument specifies how many “stripes” each chunk of data is split into, the number of FEC packets included with each stripe, and the number of data packets in each stripe. The --async, --rexmit-hello-interval, and --autostart arguments are used to run UDPcast in asynchronous mode.
17
# To send data diode-oneshot-send --to 10.0.1.1:1234 --repair 2 < /tmp/write/random_data # To receive data timeout 60 diode-oneshot-receive --from 10.0.1.1:1234 --repair 2 > /tmp/random_data
Figure 6: Commands to send and receive data using lidi. The --repair argument controls the amount of repair data, specified as a percent of the original data [5].
1 2 3 4 5
import queue import socket import sys import threading import time
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
def send(read_ip, read_port, write_ip, max_bitrate, max_payload): # To avoid exceeding max_bitrate, take at least this many seconds to send each packet target_elapsed = max_payload / max_bitrate * BYTE if max_bitrate else 0 data = sys.stdin.buffer.read(max_payload) with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as sock: sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) sock.bind((write_ip, 0)) # The OS will choose an available port while data: start = time.monotonic() sock.sendto(data, (read_ip, read_port)) data = sys.stdin.buffer.read(max_payload) if target_elapsed: already_elapsed = time.monotonic() - start sleep_duration = target_elapsed - already_elapsed if sleep_duration > 0: time.sleep(sleep_duration)
23 24 25 26 27 28
def write(packets): data = packets.get() while data is not None: sys.stdout.buffer.write(data) data = packets.get()
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
def receive(read_ip, read_port, rcvbuf_size, timeout): packets = queue.Queue() received_packets = False # Write to STDOUT using a separate thread t = threading.Thread(target=write, args=(packets,)) t.start() # Receive packets with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as sock: sock.bind((read_ip, read_port)) sock.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, rcvbuf_size) sock.settimeout(timeout) while True: try: # IPv4 UDP packet payloads cannot exceed 65507 bytes data, _ = sock.recvfrom(65507) received_packets = True packets.put(data) # Break from the loop if we have received some packets, but # timeout seconds have elapsed since receiving the last packet except TimeoutError: if received_packets: break # Indicate there won’t be more packets packets.put(None) t.join()
Figure 7: Code from our minimal program. Argument parsing and packet loss logging are omitted. Since writing to standard output using a helper thread offered the best performance, code for the other write methods is omitted.
18
1 2 3 4
import argparse import csv import sys import paho.mqtt.client as mqtt
5 6 7 8
def write(client, writer, msg): writer.writerow({"topic": msg.topic, "payload": msg.payload}) sys.stdout.flush()
9 10
FIELDNAMES = ["topic", "payload"]
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
def main(): parser = argparse.ArgumentParser( description="Relay MQTT data via STDIN and STDOUT" ) parser.add_argument( "mode", help="Whether to subscribe or publish MQTT data", choices=("subscribe", "publish"), ) parser.add_argument( "host", help="MQTT broker hostname", ) args = parser.parse_args() client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2) if args.mode == "subscribe": writer = csv.DictWriter(sys.stdout, fieldnames=FIELDNAMES) writer.writeheader() client.user_data_set(writer) client.on_connect = lambda client, *args: client.subscribe("#") client.on_message = write client.connect(args.host) client.loop_forever() else: reader = csv.DictReader(sys.stdin, fieldnames=FIELDNAMES) client.connect(args.host) client.loop_start() for row in reader: client.publish(row["topic"], row["payload"]) client.disconnect() client.loop_stop()
Example usage: # To send data from the high-integrity network python3 relay.py subscribe mqtt.industrial.net | pydiode send 10.0.1.255 10.0.1.2 # To receive data pydiode receive 10.0.1.255 | python3 relay.py publish mqtt.business.net
Figure 8: MQTT protocol proxy. Suppose IoT devices on a high-integrity industrial network publish data to an MQTT broker. By transferring the MQTT broker’s data through a data diode, the data can be accessed on the organization’s low-integrity business network without exposing the high-integrity network to cyberattacks.
19