Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

udp and udp-listen

Messages rather than a byte stream. udp: fixes a peer to send to; udp-listen: binds a port and either peers with the first sender or, with fork, serves every sender separately. The target follows the same host:port rules as tcp-listen, defaulting to 127.0.0.1:8000.

$ tocat udp-listen:9000 tcp:backend:80
$ tocat - udp:127.0.0.1:5353
$ tocat udp-listen:5353 'tee,format=hex' udp:8.8.8.8:53
$ tocat udp-listen:5353,fork 'timeout:both,timeout=30s' udp:8.8.8.8:53
OptionDescription
bind=ADDRudp: only. Local address to bind. Defaults to an ephemeral port on the wildcard address in the peer’s family
forkudp-listen: only. Serve each sending address separately, with its own dialled peer and plugin instances
max-connections=NSessions served at once under fork. Default 1024. Datagrams from a new sender past the ceiling are dropped
name=TEXTLabel for logs and dumps. Default udp://addr

Plus reuseaddr, recv-buffer and send-buffer from the shared socket options. linger and the TCP options are refused: there is no connection to close and no Nagle’s algorithm to turn off.

A bind= address is now resolved before the socket is created, so a name with several addresses takes the first.

Multicast

Both forms take the options below, because one endpoint is often both a sender and a receiver: a relay answering on a group address sends from the socket it joined on.

OptionDescription
multicast-group=ADDRGroup to join. Aliases multicast, group
multicast-interface=Interface to join on and send from. Aliases interface, iface
multicast-ttl=NHops a datagram may take. Default 1, the local segment. Alias ttl
multicast-loop=BOOLWhether a sender hears itself. Default on. Alias loop
# Receive a group and hand it to a local service
$ tocat 'udp-listen:0.0.0.0:5353,reuseaddr,multicast-group=239.1.1.1' tcp:localhost:8080

# Send to a group, off this host, without hearing the echo
$ tocat - 'udp:239.1.1.1:5353,multicast-ttl=32,multicast-loop=false'

A receiver has to bind the wildcard or the group address. Joining a group on a socket bound to a specific unicast address will not receive the group’s traffic, and it fails by receiving nothing rather than by erroring, which is the hardest kind of failure to diagnose. udp-listen:0.0.0.0:5353 or udp-listen:239.1.1.1:5353, not udp-listen:192.168.1.5:5353.

The group decides the address family, including for multicast-ttl. IPv4 and IPv6 have separate socket options for the hop limit and for the loopback flag, so the group is what says which pair to set. Without a group, an unqualified ttl= is read as IPv4. multicast-interface follows the same split: an address for IPv4, an interface index for IPv6.

reuseaddr is usually wanted on a receiver, since more than one process on the host may be listening to the same group and port.

What is not here yet

The join happens once, when the socket is bound. There is no way to leave a group, to join more than one, or to change the membership of a running relay, and a reconnect that reopens the socket rejoins from scratch. Source-specific multicast is not supported either.

udp: resolves the peer before binding, so that the local socket lands in the same address family. Without fork, udp-listen: peeks the first datagram to learn who the peer is and then connects to it, leaving that datagram queued for the relay rather than eating it.

Forking

With fork the socket is left unconnected and datagrams are routed by source address. Each new address becomes a session: its own dialled peer, its own buffers, its own plugin instances, its own span in the log. Replies go back out of the same socket to that address, so no extra port is opened per sender.

Nothing ends a session on its own, because UDP has no close to observe. Use the timeout plugin, on both directions:

$ tocat udp-listen:9000,fork 'timeout:both,timeout=30s' tcp:backend:80

:both matters. A forward-only halt ends the path from the sender but leaves the reverse pump reading a sink that may never close, and it is the session task finishing that releases the connection permit and the map entry. Without it, sessions accumulate until max-connections is reached and every new sender is dropped from then on; the log says so the first time it happens.

A halt is a real end of stream, so a hash digest, a compress epilogue or a rate summary do arrive when the session closes. A sender that goes quiet and comes back gets a new session with fresh plugin state, the way a reconnecting TCP client would.

Because one receive loop serves every sender, a session that cannot keep up has its datagrams dropped rather than being allowed to stall the others. The first drop is a warning and the rest are at debug, so -v is where to look if a peer is losing messages.

Three consequences of the datagram shape are worth keeping in mind.

No end of stream, unless a stage makes one. A datagram source runs until it is interrupted, so anything that only happens at end of stream never happens on that path: a rate summary, a compress ratio report, the final short block. A timeout stage halting the path is the one thing that produces one.

The copy buffer is the message ceiling. One receive is one datagram, and a message longer than the buffer is truncated by the kernel. The default is 256 KiB, which is above any datagram that will survive a real network, but -b set low enough will start cutting messages.

Boundaries are data. A stage that may not preserve them draws a warning when the destination on its path is a datagram endpoint. See Datagrams.