IPv6 in containers

Hi all,

I would like to (re)open discussion of IPv6 in containers. In my view, as of now (stable v7.10.2) the problem has at least two sides:

  1. No address. It is not possible to assign one or more IPv6 addresses to a container’s virtual ethernet interface (veth) since /interface/veth explicitly expects only one IPv4. Workarounds:
    → IPv6 SLAAC: add a veth to a bridge, assign an IPv6 address to a bridge with advertise=yes. Pros: easy, persistent. Cons: not suitable for any setup where prefixes longer than /64 are used.
    → Run /sbin/ip -6 addr add [IPV6_ADDRESS] dev eth0 inside a container. Pros: universal. Cons: complex, goes away on reboot.

  2. No gateway. Similarly, it is not possible to assign an IPv6 gateway for a container since /interface/veth explicitly expects IPv4 as a gateway. Workarounds:
    → IPv6 SLAAC: see above, but this is where a problem (probably, a bug) occurs. Normally, a container has to add default route with a gateway set to the IPv6 link-local address of the host’s bridge, but it won’t do that - IPv6 default route is missing in containers. Pros: easy, persistent. Cons: doesn’t work as expected.
    → Run /sbin/ip -6 route add default via [IPV6_GATEWAY] dev eth0 inside a container. Pros: universal. Cons: complex, goes away on reboot.

Other relevant ideas:
→ I understand one may run a custom script to assign IPv6 addresses and gateways to containers on startup but, in my view, it’s an overkill for such a basic functionality.
→ While testing, I was surprised to find that a container can have multiple DNS addresses including IPv6, and they are correctly mirrored to /etc/resolv.conf inside a container. So, there is at least one small step towards a proper dual-stack for containers already made.
→ There is another issue (probably, a bug) with a virtual interface (veth) itself. When you restart a container, its veth and routing table are not reconfigured. In other words, if you run some ip addr add/del or ip route add/del commands inside a container and then reboot this container, it will keep its network configuration. It is an unwanted behaviour, I would say. But if you reboot your host, no changes to your container’s network configuration persist, as expected.

Community, am I the only one suffering from this lack of IPv6 in containers? Or do you know other workarounds that are better in any way? Please share your experience with IPv6 in containers.

MikroTik engineers, I appreciate your hard work and hope this post will help you implement enhancements to the code.

Thanks

I added IPv6 to my CHR container running a nextdns client. I had to add support for both options 1 and 2 into my docker container (these are using ULA):

Dockerfile

FROM debian:bullseye-slim
RUN apt-get update && apt-get install -y apt-transport-https curl && \ 
  curl -o /usr/share/keyrings/nextdns.gpg https://repo.nextdns.io/nextdns.gpg && \
  echo "deb [signed-by=/usr/share/keyrings/nextdns.gpg] https://repo.nextdns.io/deb stable main" | tee /etc/apt/sources.list.d/nextdns.list && \
  apt-get update && apt-get install -y nextdns iproute2 
EXPOSE 53/tcp 53/udp
CMD /bin/ip -6 addr add ${NEXTDNS_IPV6ADDR} dev eth0; /bin/ip -6 route add default via ${NEXTDNS_IPV6GW} dev eth0 ;/bin/ip -6 addr sh ; /bin/ip -6 route; /usr/bin/nextdns run ${NEXTDNS_ARGUMENTS} -profile ${NEXTDNS_ID}

I add the container with the correct envs. This container will basically have static addresses, so no RA/ND involved.

IPv6 envs for reference:

/container envs
add key=NEXTDNS_IPV6ADDR name=nextdns value=fd00:ffff::2/64
add key=NEXTDNS_IPV6GW name=nextdns value=fd00:ffff::1

I then add the masquerade/dst-nat to the CHR:

/ipv6 address
add address=fd00:70::40 advertise=no interface=ether1
add address=fd00:ffff::1 advertise=no interface=dockers
/ipv6 firewall nat
add action=masquerade chain=srcnat src-address=fd00:ffff::/64
add action=dst-nat chain=dstnat dst-address=fd00:70::40/128 dst-port=53 protocol=tcp to-address=\
    fd00:ffff::2/128 to-ports=53
add action=dst-nat chain=dstnat dst-address=fd00:70::40/128 dst-port=53 protocol=udp to-address=\
    fd00:ffff::2/128 to-ports=53
/ipv6 route
add disabled=no dst-address=::/0 gateway=fd00:70::1

I agree that the current status is not IPv6 friendly and does require work on the container to get things to work properly.

@biomesh, thanks for sharing your solution. It is universal and works pretty fine. However, it may be a little too complex to recreate and recompile multiple containers, track new versions and support all relevant platforms, at least for me.

Based on your idea I can see another possible workaround for both address and gateway assignment: override an original CMD of your docker container with a custom string:

/container set 0 cmd="sh -c \"ip -6 addr add [IPV6_ADDRESS] dev eth0; ip -6 route add default via [IPV6_GATEWAY] dev eth0; [ORIGINAL_CMD]\""

Pros: universal, persistent. Cons: works well only on the first launch of the container. When a container is restarted, an error occurs (one such line per each ip command): ip: RTNETLINK answers: File exists. This error message confirms what I mentioned in my first post as a veth bug. Nevertheless, the container continues running after such errors.

When you reboot your nextdns client container, do you get similar error messages?

next beta will have ipv6 support for veth:

add address=172.17.0.3/16,fd8d:5ad2:24:2::2/64 gateway=172.17.0.1 gateway6=fd8d:5ad2:24:2::1

as well as multiple addresses

Wow, it was quick, thank you, @antonsb!

Could you please also check/confirm a veth bug I mentioned above?

does not seem like veth bug to me - this is container that answers, that such entry is already in place.

I do get that error message on a container restart but my container works and the address assignment is valid (I added commands in the container CMD to print the ipv6 addresses and routes)

The upcoming changes to ROS mean I can revert my changes to my container soon.

@biomesh, thank you for confirmation.

@antonsb, well, you are technically right, it is a container that answers that an address/route is already in place. I mean that no addresses/routes assigned inside a running container should be persistent. Take a bare docker setup: every time you restart a container you will get a basic set of IPs assigned in the config, nothing more, no manual changes are kept. I guess, docker may recreate veth on container restart for this purpose, but I’m not sure about that.

Please, follow the steps below, then restart a container to reproduce error log messages we get.

/interface veth
    add address=172.16.0.2/24 gateway=172.16.0.1 name=veth2
/bridge
    add name=test
/bridge port
    add bridge=test interface=veth2
/ip address
    add address=172.16.0.1/24 interface=test network=172.16.0.0
/ipv6 address
    add address=fd00::1/64 interface=test advertise=no
/container 
    add cmd="sh -c \"ip -6 addr add fd00::2/64 dev eth0; ip -6 route add default via fd00::1 dev eth0;  tail -f /dev/null\"" \
    dns=172.16.0.1,fd00::1 hostname=alpine interface=veth2 logging=yes root-dir=/container/alpine remote-image=arm64v8/alpine

Thanks antonsb. That is great news. :smiley:

I just hope it makes it into 7.11.

Thank you, this is good news.

Thanks, we will look into this.

I get this error “Error: either “local” is duplicate, or “ip” is a garbage.” when CMD is set to ip -6 addr add {myipv6}/64 dev eth0; ip -6 route add default via {ipv6gateay} dev eth0;

Any idea?