Skip to content

UDP Protocol

opencode-agent[bot] edited this page May 10, 2026 · 1 revision

UDP Protocol

UDP (User Datagram Protocol) implementation providing connectionless datagram communication over IPv4.

Overview

UDP is a transport layer protocol in JNode's network stack that provides a simple, low-overhead mechanism for sending and receiving datagrams. Unlike TCP, UDP is connectionless and unreliable — it offers no guarantees of delivery, ordering, or error recovery. Each UDP datagram is self-contained with its own addressing information (source port, destination port), making it suitable for applications where speed matters more than reliability (e.g., DNS, streaming media, real-time gaming).

JNode's UDP implementation lives under net/src/net/org/jnode/net/ipv4/udp/ and integrates with the IPv4Service for transmission and the SocketBuffer pipeline for packet handling. The UDPProtocol class acts as the central coordinator — it maintains a port-to-socket binding map, validates checksums, and dispatches received datagrams to the appropriate socket. The protocol registers itself with the IPv4 layer via protocol ID IPPROTO_UDP (17), allowing IP to route incoming UDP packets to it.

UDP sits above IP in the stack and below the java.net.DatagramSocket API. JNode bridges these layers using DatagramSocketImplFactory — when Java code creates a DatagramSocket, the factory provides a UDPDatagramSocketImpl that delegates to UDPProtocol. This means standard Java networking code can use UDP without knowing about the underlying packet processing.

Key Components

Class / File Role
net/src/net/org/jnode/net/ipv4/udp/UDPProtocol.java Main protocol handler; manages port bindings, receive/send, checksum validation, ICMP error handling
net/src/net/org/jnode/net/ipv4/udp/UDPHeader.java UDP header encoding/decoding (8 bytes: src port, dst port, length, checksum)
net/src/net/org/jnode/net/ipv4/udp/UDPDatagramSocketImpl.java Socket implementation bridging java.net.DatagramSocket to UDPProtocol
net/src/net/org/jnode/net/ipv4/udp/UDPDatagramSocketImplFactory.java Factory creating UDPDatagramSocketImpl instances for the Java socket layer
net/src/net/org/jnode/net/ipv4/udp/PlainUDPDatagramSocketImpl.java Alternate socket implementation using GNU Classpath primitives
net/src/net/org/jnode/net/ipv4/udp/UDPConstants.java Constants (e.g., UDP_HLEN = 8)
net/src/net/org/jnode/net/ipv4/udp/UDPStatistics.java Runtime statistics counters (packet counts, errors, drops)
net/src/net/org/jnode/net/ipv4/udp/UDPControlBlock.java Per-socket control/blocking state
net/src/net/org/jnode/net/ipv4/udp/UDPHeader.java UDP header handling with pseudo-header checksum support

How It Works

Port Binding and Socket Management

UDPProtocol maintains a HashMap<Integer, UDPDatagramSocketImpl> mapping local ports to socket implementations. When a socket calls bind(), UDPProtocol.bind() checks whether the requested port is already in use. If the port is 0, an ephemeral port is randomly chosen from the range 1024–65535. Port conflicts throw SocketException("Port already bound").

// UDPProtocol.java:224-244
protected synchronized void bind(UDPDatagramSocketImpl socket) throws SocketException {
    Integer lport = socket.getLocalPort();
    if (lport.compareTo(zero) != 0 && sockets.containsKey(lport)) {
        throw new SocketException("Port already bound (" + lport + ')');
    } else {
        Integer ran;
        while (lport.compareTo(zero) == 0) {
            ran = random.nextInt(stopRandom) + startRandom; // 1024..65535
            if (!sockets.containsKey(ran)) {
                lport = ran;
                socket.setLocalPort(lport);
            }
        }
        sockets.put(lport, socket);
    }
}

Receiving and Delivering Datagrams

When IP delivers a packet with protocol ID 17, UDPProtocol.receive() is called. The method parses the UDPHeader, validates the checksum, strips the 8-byte header from the SocketBuffer, and calls deliver(). The deliver() method looks up the destination port in the socket map, checks that the local address matches (or is 0.0.0.0), and either delivers to the socket or sends an ICMP port-unreachable message.

// UDPProtocol.java:199-217
private synchronized void deliver(UDPHeader hdr, SocketBuffer skbuf) throws SocketException {
    final Integer lport = hdr.getDstPort();
    final IPv4Header ipHdr = (IPv4Header) skbuf.getNetworkLayerHeader();
    final UDPDatagramSocketImpl socket = (UDPDatagramSocketImpl) sockets.get(lport);
    if (socket != null) {
        final InetAddress laddr = socket.getLocalAddress();
        if (laddr.isAnyLocalAddress() || laddr.equals(ipHdr.getDestination().toInetAddress())) {
            if (socket.deliverReceived(skbuf)) {
                return;
            }
        }
    }
    stat.noport.inc();
    if (ipHdr.getDestination().isBroadcast()) {
        stat.noportbcast.inc();
    }
    icmp.sendPortUnreachable(skbuf);
}

Checksum Calculation

UDP checksums use a 12-byte pseudo-header derived from the IPv4 header: source address (4 bytes), destination address (4 bytes), a zero byte, the protocol number (17 for UDP), and the UDP length (2 bytes). This pseudo-header is prepended to the UDP header + data, and a one's-complement checksum is computed over the entire structure.

In UDPHeader (constructor from SocketBuffer), if the checksum field is 0, validation is skipped (no checksum was sent). Otherwise, the pseudo-header is constructed in a temporary SocketBuffer, the actual packet data is appended, and IPv4Utils.calcChecksum() is called — if the result is 0, the checksum is valid.

// UDPHeader.java:67-92
public UDPHeader(SocketBuffer skbuf) {
    this.srcPort = skbuf.get16(0);
    this.dstPort = skbuf.get16(2);
    this.udpLength = skbuf.get16(4);
    final int checksum = skbuf.get16(6);
    if (checksum == 0) {
        log.debug("No checksum set");
        this.checksumOk = true;
    } else {
        // Create the pseudo header for checksum calculation
        final IPv4Header ipHdr = (IPv4Header) skbuf.getNetworkLayerHeader();
        final SocketBuffer phdr = new SocketBuffer(12);
        phdr.insert(12);
        ipHdr.getSource().writeTo(phdr, 0);
        ipHdr.getDestination().writeTo(phdr, 4);
        phdr.set(8, 0);
        phdr.set(9, ipHdr.getProtocol());
        phdr.set16(10, udpLength);
        phdr.append(skbuf);
        final int ccs2 = IPv4Utils.calcChecksum(phdr, 0, udpLength + 12);
        this.checksumOk = (ccs2 == 0);
    }
}

Sending Datagrams

When UDPDatagramSocketImpl.send() is called, it constructs an IPv4Header and UDPHeader, wraps the payload data in a SocketBuffer, and calls protocol.send(). The UDPHeader.prefixTo() method inserts 8 bytes of header space at the front of the buffer, and finalizeHeader() fills in the checksum using the same pseudo-header algorithm.

// UDPProtocol.java:263-269
protected void send(IPv4Header ipHdr, UDPHeader udpHdr, SocketBuffer skbuf)
    throws SocketException {
    skbuf.setTransportLayerHeader(udpHdr);
    udpHdr.prefixTo(skbuf);
    ipService.transmit(ipHdr, skbuf);
    stat.opackets.inc();
}

Statistics

UDPStatistics tracks counters for received packets (ipackets), sent packets (opackets), checksum errors (badsum), length errors (badlen), undelivered packets (noport, noportbcast), and dropped packets (hdrops, fullsock). These can be retrieved via UDPProtocol.getStatistics() for monitoring and debugging.

Gotchas & Non-Obvious Behavior

  • No delivery guarantee: UDP does not retransmit lost packets, acknowledge receipt, or maintain connection state. Applications must handle all reliability concerns.
  • Checksum optional: A UDP datagram with checksum field set to 0 is accepted without validation. JNode always computes checksums on send; incoming zero-checksum datagrams are accepted.
  • Port 0 binding: When DatagramSocket binds to port 0, JNode randomly selects an ephemeral port in the range 1024–65535. Concurrent bind attempts can cause contention.
  • ICMP port unreachable: When a datagram arrives for an unbound port, JNode sends an ICMP destination-unreachable (port unreachable) message back to the sender.
  • No flow control or congestion control: UDP has no sliding window, no slow start, and no congestion avoidance. All rate limiting must be handled by the application.
  • Broadcast handling: Datagrams sent to the broadcast address that arrive on ports with no bound socket increment noportbcast instead of noport, but still trigger ICMP port unreachable.
  • UDP header length = 8 bytes: Fixed 8-byte header (source port + destination port + length + checksum). The length field includes both header and data.

Related Pages

  • Network-Stack — Overall network architecture in JNode
  • Network-Layers — Protocol layering model (LinkLayer, NetworkLayer, TransportLayer, SocketBuffer)
  • TCP-Protocol — JNode's TCP implementation (connection-oriented counterpart)
  • Sockets-Implementation — Bridge between java.net API and native stack
  • ARP — IPv4-to-MAC resolution (layer below IP)

Clone this wiki locally