tun interface inside docker not able to read packets from host
Asked Answered
O

0

1

I am trying this tutorial for creating and using tun interface. In my case, I want to use it inside a docker container. My host and docker container both are Linux with mknod support. I am trying to get tun interface inside the container to read packets from host but without using --network=host.

Based on the docker run doc, I am mapping host /dev/net/tun inside docker using --device. Then adding capabilities NET_ADMIN (also tried adding MKNOD, NET_RAW but does not seem to help). Then using the example tun reader below; which is example from above tutorial; with code added to create interface; tried to read the packets.

It can read packets from ping on 10.0.0.2 from within the container. But not able to read from ping on host. With --network=host flag to docker run, the tunx interface is created on host and therefore ping works.

My question is, is there a to get it to work without using host network? Do I need any explicit iptables rules to forward traffic between container and host?

tun_alloc function is from this tutorial

docker command

docker run -it --device=/dev/net/tun:/dev/net/tun -cap-add=NET_ADMIN <image>

reader code

int main(void) {
  const char *ifname = "tunx";
  const char *ifaddr = "10.0.0.1/24";
  char tun_name[IFNAMSIZ];
  char buffer[2096];
  int tun_fd = -1;
  char cmd[1024] = "";
  int err = -1;

  strcpy(tun_name, ifname);
  tun_fd = tun_alloc(tun_name, IFF_TUN);  /* tun interface */
  if (tun_fd < 0) {
    fprintf(stderr, "failed to alloc_tun. tun_fd: %d\n", tun_fd);
    exit(1);
  }
  printf("tun_fd: %d\n", tun_fd);

  printf("ip commands \n");

  snprintf(cmd, sizeof(cmd), "/bin/ip addr add dev %s %s", ifname, ifaddr);
  err = system(cmd);
  fprintf(stdout, "Running: %s, err: %d\n", cmd, err);
  if (err < 0) {
          fprintf(stderr, "failed system. Cmd:%s, err: %d (%s)\n", cmd, err, strerror(errno));
          exit(1);
  }

  snprintf(cmd, sizeof(cmd), "/bin/ip link set %s up", ifname);
  err = system(cmd);
  fprintf(stdout, "Running: %s, err: %d\n", cmd, err);
  if (err < 0) {
          fprintf(stderr, "failed system. Cmd:%s, err: %d (%s)\n", cmd, err, strerror(errno));
          exit(1);
  }

  while (1) {
    /* Note that "buffer" should be at least the MTU size of the interface, eg 1500 bytes */
    int nread = read(tun_fd,buffer,sizeof(buffer));
    if(nread < 0) {
      perror("Reading from interface");
      close(tun_fd);
      exit(1);
    }

    /* Do whatever with the data */
    printf("Read %d bytes from device %s\n", nread, tun_name);
  }
  return 0;
}
Oto answered 3/1, 2021 at 22:23 Comment(2)
A specific goal of Docker is that container processes can't do host-global things like modify the network environment. If you don't use --network host (and you generally shouldn't) a container will run in its own network namespace and the tunnel interface will be local to the container. I'd run this process directly on the host and not in a container.Recurrent
@DavidMaze Is there a way to limit container access to specific interface? Then maybe I can create tunx interface on host and limit my container access to that interface. I do not really need full access to host network, but just single tun interface.Oto

© 2022 - 2024 — McMap. All rights reserved.