desktop-linux icon indicating copy to clipboard operation
desktop-linux copied to clipboard

UDP socket in host network: UDP reply fails.

Open gabilm opened this issue 1 year ago • 0 comments

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

gabilm avatar Jan 03 '25 09:01 gabilm