2

I'm trying to develop a docker image to test SocketCAN (with vcan) that would work on MacOS and Windows and everything is fine until I run candump vcan0 which returns: "socket: Address family not supported by protocol"

After some time I've managed to get it to work on Linux by starting the container with "--network=host --privileged". Still, this doesn't work on a container started on Mac, which returns:

socket: Address family not supported by protocol

A strace of "candump vcan0" shows it stopping on this call:

socket(AF_CAN, SOCK_RAW, 1)             = -1 EAFNOSUPPORT (Address family not supported by protocol)

What's strange is that even though "--network=host" shouldn't be supported on Mac (https://docs.docker.com/network/host/), the hostname of the container is showing the LinuxKit image (the VM running the containers):

root@linuxkit-025000000001:~#

The vcan module is of course loaded:

root@linuxkit-025000000001:~# lsmod
Module                  Size  Used by
vcan                   16384  0
xfrm_user              32768  1
xfrm_algo              16384  1 xfrm_user

I don't know what else to try ;)

  • If the feature isn't supported, you shouldn't rely on it even if it seems that a few stuff are done. You have to define the bridging rules yourself or maybe there is a networking module that could help you. – Benoît Jul 12 '19 at 07:05
  • 1
    @Benoît, the problem is just for development, nothing live would run on this. The question arose because under MacOS (or Windows) Docker first starts a Linux VM in which it starts the Docker containers, so normally everything should be supported as it's virtualised by the Linux VM. So probably the Linux VM has to be somewhat changed. Maybe that would be solution... to somehow recompile the Linux VM running inside Mac OS. – Andrei Kovacs Jul 25 '19 at 20:07

1 Answers1

3

I recently went through the same pain and finally got this to work.

Here is what I did:

1) Clone https://github.com/linuxkit/linuxkit.git

2) Follow the instructions here to modify the kernel as you want: https://github.com/linuxkit/linuxkit/blob/master/docs/kernels.md#modifying-the-kernel-config

Specifically you want to add the CAN network as a module and the CAN vcan driver as a module.

3) Copy your modified config (for example config-4.9.x-x86_64) to linuxkit/kernel to override the one that is already there.

4) From linuxkit/kernel run make build_4.9.x HASH=mods HASH_COMMIT=HEAD

5) Using the simple Dockerfile below (call it 'Dockerfile.kernel') run docker build . -f Dockerfile.kernel -t kernel_extract (there is probably a more elegant way to do this, but I'm somewhat of a docker n00b).

6) Run the image with docker run -it --entrypoint=/bin/sh kernel_extract

7) In the image extract the modules by doing:

cd /ksrc
tar -xvf kernel.tar
find lib/modules/ -name "*.ko" | grep can > /tmp/files
tar cpzf can.tar.gz -T /tmp/files

8) While the container is still running copy the tarball out with:

id=$(docker container ls | grep kernel_extract | awk '{print $1}')
docker cp $id:/ksrc/can.tar.gz .

9) Install the can.tar.gz files in your docker image.

10) Run your docker image with --net=host and --cap-add=ALL to make sure you can load the kernel modules and setup the interface.

11) When you startup your docker image you need to run some code like this (inside your container, I used a shell script on the entrypoint to do this and then exec the thing I really want to do):

for module in "can" "can-raw" "can-gw" "can-bcm" "can-dev" "vcan"; do
  module_name=$(echo $module | tr "-" "_")
  lsmod | grep $module_name >/dev/null
  if [ $? -ne 0 ]; then
    insmod $(find /lib/modules -name "${module}.ko")
  fi
done

for iface in "can0" "can1"; do
  ip link show $iface >/dev/null 2>&1
  if [ $? -ne 0 ]; then
    ip link add $iface type vcan
    ip link set $iface up
  fi
done

Because you are messing with the host kernel (in this case the hyperkit vm that is running on mac), it will persist beyond each individual container run, which is why there is care to check that it doesn't try to insmod twice.

It is unfortunately kind of convoluted, and perhaps someone with more docker nuance could describe a more elegant way, but that does work!

Dockerfile.kernel:

FROM linuxkit/kernel:4.9.184-mods-amd64 AS ksrc
FROM linuxkit/alpine:3fdc49366257e53276c6f363956a4353f95d9a81 AS build
RUN apk add build-base

COPY --from=ksrc / /ksrc

ENTRYPOINT ["/bin/sh"]
Andrew
  • 31
  • 2
  • Thanks @Andrew, I will try this sometime in the future and I'll let you know if that fixed my problem. Meanwhile my solution to allow cross platform compatibility was to create a CentOS VM in which I'm running docker ;) – Andrei Kovacs Sep 22 '19 at 15:51