UDP socket in host network: UDP reply fails.
Description
I have an Ubuntu 22.04 host system (x86_64) with Docker Desktop 4.37.1. I start a basic Ubuntu 24.04 container using the host network. Inside the container I run a simple UDP echo service written in C on 0.0.0.0. UDP connection fails using a nc client from the host and works fine from inside the container. TCP echo service works fine in both cases.
Reproduce
Dockerfile:
FROM ubuntu:24.04
# Set environment variables to prevent interactive prompts
ENV DEBIAN_FRONTEND=noninteractive
# Update and install basic dependencies
RUN apt-get update && \
apt-get install -y --no-install-recommends \
build-essential \
curl \
wget \
vim \
git \
ca-certificates && \
rm -rf /var/lib/apt/lists/*
# Set the default shell to bash
SHELL ["/bin/bash", "-c"]
# Set a default working directory
WORKDIR /workspace
# Command to keep the container running (useful for debugging or extending)
CMD ["bash"]
Launched with docker-compose:
version: '3.9'
services:
ubuntu-service:
build:
context: base/
dockerfile: Dockerfile
network_mode: "host"
volumes:
- ./base/:/home/ubuntu/base
command: tail -f /dev/null
Up the container:
docker-compose up -d --build
Basic UDP echo service:
$ cat base/udp_server.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#define PORT 12345
#define BUFFER_SIZE 1024
int main() {
int sockfd;
struct sockaddr_in server_addr, client_addr;
char buffer[BUFFER_SIZE];
socklen_t addr_len = sizeof(client_addr);
if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
perror("Error al crear el socket");
exit(EXIT_FAILURE);
}
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY; // Escuchar en todas las interfaces
server_addr.sin_port = htons(PORT);
if (bind(sockfd, (const struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
perror("Error al hacer bind");
close(sockfd);
exit(EXIT_FAILURE);
}
printf("Server UDP listening on port %d\n", PORT);
while (1) {
ssize_t received_len = recvfrom(sockfd, buffer, BUFFER_SIZE, 0,
(struct sockaddr *)&client_addr, &addr_len);
if (received_len < 0) {
perror("Error al recibir datos");
continue;
}
buffer[received_len] = '\0'; // Asegurar el final de la cadena
printf("Received from %s:%d: %s\n",
inet_ntoa(client_addr.sin_addr),
ntohs(client_addr.sin_port),
buffer);
if (sendto(sockfd, buffer, received_len, 0,
(struct sockaddr *)&client_addr, addr_len) < 0) {
perror("Error al enviar datos");
} else {
printf("Sent back to %s:%d\n",
inet_ntoa(client_addr.sin_addr),
ntohs(client_addr.sin_port));
}
}
close(sockfd);
return 0;
}
You can just run gcc -o udp_service udp_service.c to get the bin file
Basic TCP echo service:
$ cat base/tcp_echo_server.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#define PORT 12345 // Puerto en el que escuchará el servidor
#define BUFFER_SIZE 1024 // Tamaño del buffer para datos
int main() {
int server_fd, client_fd;
struct sockaddr_in server_addr, client_addr;
char buffer[BUFFER_SIZE];
socklen_t addr_len = sizeof(client_addr);
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
perror("Error al crear el socket");
exit(EXIT_FAILURE);
}
server_addr.sin_family = AF_INET; // IPv4
server_addr.sin_addr.s_addr = INADDR_ANY; // Escuchar en todas las interfaces
server_addr.sin_port = htons(PORT); // Convertir a formato de red
if (bind(server_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) {
perror("Error al hacer bind");
close(server_fd);
exit(EXIT_FAILURE);
}
if (listen(server_fd, 5) == -1) { // Máximo 5 conexiones en cola
perror("Error al escuchar");
close(server_fd);
exit(EXIT_FAILURE);
}
printf("Servidor TCP escuchando en el puerto %d...\n", PORT);
while (1) {
if ((client_fd = accept(server_fd, (struct sockaddr *)&client_addr, &addr_len)) == -1) {
perror("Error al aceptar conexión");
continue;
}
printf("Cliente conectado: %s:%d\n",
inet_ntoa(client_addr.sin_addr),
ntohs(client_addr.sin_port));
while (1) {
ssize_t bytes_received = recv(client_fd, buffer, BUFFER_SIZE, 0);
if (bytes_received <= 0) {
printf("Cliente desconectado: %s:%d\n",
inet_ntoa(client_addr.sin_addr),
ntohs(client_addr.sin_port));
break;
}
send(client_fd, buffer, bytes_received, 0);
}
close(client_fd);
}
close(server_fd);
return 0;
}
Testing the UDP service from a client inside the container using localhost:
# ./udp_server
Server UDP listening on port 12345
Testing the client:
# echo "hola" | nc -u 127.0.0.1 12345
Service output:
./udp_server
Server UDP listening on port 12345
Received from 127.0.0.1:62905: hola
Sent back to 127.0.0.1:62905
No answer is received in client.
tcpdump output:
# tcpdump -i lo
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on lo, link-type EN10MB (Ethernet), snapshot length 262144 bytes
08:23:31.880720 IP localhost.62905 > localhost.12345: UDP, length 5
08:23:34.971354 IP 192.168.65.9 > 192.168.65.9: ICMP host 192.168.65.254 unreachable, length 41
Testing the UDP service from a client inside the container using 192.168.65.0 network:
Server side
# ./udp_server
Server UDP listening on port 12345
Received from 192.168.65.9:60709: hola
Sent back to 192.168.65.9:60709
Client side:
# echo "hola" | nc -u 192.168.65.9 12345
hola
It works fine.
Testing the UDP service from a client outside the container (host system) using localhost
Service side:
# ./udp_server
Server UDP listening on port 12345
Received from 127.0.0.1:59255: hola
Sent back to 127.0.0.1:59255
Client side: `$ echo "hola" | nc -u localhost 12345
` No answer is received in client.
tcpdump (inside the container) output:
# tcpdump -i lo
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on lo, link-type EN10MB (Ethernet), snapshot length 262144 bytes
08:50:09.536578 IP localhost.59255 > localhost.12345: UDP, length 5
08:50:12.629447 IP 192.168.65.9 > 192.168.65.9: ICMP host 192.168.65.254 unreachable, length 41
Testing the UDP service from a client outside the container (host system) using local IP address (wlo1)
$ echo "hola" | nc -u 192.168.18.14 12345
No answer is received in client.
tcpdump (inside the container) output:
# tcpdump -i lo
# tcpdump -i lo
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on lo, link-type EN10MB (Ethernet), snapshot length 262144 bytes
08:55:58.604494 IP localhost.63246 > localhost.12345: UDP, length 5
08:56:01.679488 IP 192.168.65.9 > 192.168.65.9: ICMP host 192.168.65.254 unreachable, length 41
TCP service example works fine inside and outside the container (omitted output for simplicity).
Expected behavior
If I run the same udp echo service outside the container (in the host system) and try the nc client I get the expected result:
./udp_server
Server UDP listening on port 12345
Received from 127.0.0.1:51570: hola
Sent back to 127.0.0.1:51570
$ echo "hola" | nc -u 127.0.0.1 12345
hola
$ sudo tcpdump -i lo -p udp
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on lo, link-type EN10MB (Ethernet), snapshot length 262144 bytes
09:09:02.831498 IP localhost.51570 > localhost.12345: UDP, length 5
09:09:02.831616 IP localhost.12345 > localhost.51570: UDP, length 5
docker version
$ docker version
Client: Docker Engine - Community
Version: 27.4.1
API version: 1.47
Go version: go1.22.10
Git commit: b9d17ea
Built: Tue Dec 17 15:45:46 2024
OS/Arch: linux/amd64
Context: desktop-linux
Server: Docker Desktop 4.37.1 (178610)
Engine:
Version: 27.4.0
API version: 1.47 (minimum version 1.24)
Go version: go1.22.10
Git commit: 92a8393
Built: Sat Dec 7 10:38:57 2024
OS/Arch: linux/amd64
Experimental: false
containerd:
Version: 1.7.21
GitCommit: 472731909fa34bd7bc9c087e4c27943f9835f111
runc:
Version: 1.1.13
GitCommit: v1.1.13-0-g58aa920
docker-init:
Version: 0.19.0
GitCommit: de40ad0
docker info
$ docker info
Client: Docker Engine - Community
Version: 27.4.1
Context: desktop-linux
Debug Mode: false
Plugins:
ai: Ask Gordon - Docker Agent (Docker Inc.)
Version: v0.5.1
Path: /usr/lib/docker/cli-plugins/docker-ai
buildx: Docker Buildx (Docker Inc.)
Version: v0.19.2-desktop.1
Path: /usr/lib/docker/cli-plugins/docker-buildx
compose: Docker Compose (Docker Inc.)
Version: v2.31.0-desktop.2
Path: /usr/lib/docker/cli-plugins/docker-compose
debug: Get a shell into any image or container (Docker Inc.)
Version: 0.0.37
Path: /usr/lib/docker/cli-plugins/docker-debug
desktop: Docker Desktop commands (Beta) (Docker Inc.)
Version: v0.1.0
Path: /usr/lib/docker/cli-plugins/docker-desktop
dev: Docker Dev Environments (Docker Inc.)
Version: v0.1.2
Path: /usr/lib/docker/cli-plugins/docker-dev
extension: Manages Docker extensions (Docker Inc.)
Version: v0.2.27
Path: /usr/lib/docker/cli-plugins/docker-extension
feedback: Provide feedback, right in your terminal! (Docker Inc.)
Version: v1.0.5
Path: /usr/lib/docker/cli-plugins/docker-feedback
init: Creates Docker-related starter files for your project (Docker Inc.)
Version: v1.4.0
Path: /usr/lib/docker/cli-plugins/docker-init
sbom: View the packaged-based Software Bill Of Materials (SBOM) for an image (Anchore Inc.)
Version: 0.6.0
Path: /usr/lib/docker/cli-plugins/docker-sbom
scout: Docker Scout (Docker Inc.)
Version: v1.15.1
Path: /usr/lib/docker/cli-plugins/docker-scout
Server:
Containers: 1
Running: 1
Paused: 0
Stopped: 0
Images: 2
Server Version: 27.4.0
Storage Driver: overlayfs
driver-type: io.containerd.snapshotter.v1
Logging Driver: json-file
Cgroup Driver: cgroupfs
Cgroup Version: 2
Plugins:
Volume: local
Network: bridge host ipvlan macvlan null overlay
Log: awslogs fluentd gcplogs gelf journald json-file local splunk syslog
CDI spec directories:
/etc/cdi
/var/run/cdi
Swarm: inactive
Runtimes: io.containerd.runc.v2 runc
Default Runtime: runc
Init Binary: docker-init
containerd version: 472731909fa34bd7bc9c087e4c27943f9835f111
runc version: v1.1.13-0-g58aa920
init version: de40ad0
Security Options:
seccomp
Profile: unconfined
cgroupns
Kernel Version: 6.10.14-linuxkit
Operating System: Docker Desktop
OSType: linux
Architecture: x86_64
CPUs: 16
Total Memory: 3.56GiB
Name: docker-desktop
ID: ade0739d-5238-4cc0-b9db-4f0bc61d21d9
Docker Root Dir: /var/lib/docker
Debug Mode: false
HTTP Proxy: http.docker.internal:3128
HTTPS Proxy: http.docker.internal:3128
No Proxy: hubproxy.docker.internal
Labels:
com.docker.desktop.address=unix:///home/gabilm/.docker/desktop/docker-cli.sock
Experimental: false
Insecure Registries:
hubproxy.docker.internal:5555
127.0.0.0/8
Live Restore Enabled: false
WARNING: daemon is not using the default seccomp profile
Diagnostics ID
13F7B095-FDF5-44AF-BEB5-7A4412B21103/20250103091346
Additional Info
No response