This one is For when your ISP/Datacentre decides to give you a /64 (or whatever) on your uplink and relies on NDP to route rather than just handing you a routed IPv6 block.

That’s a right pain in the backside if you want to use it on the other side of your (Virtual?) firewall or for a VPN

It may not be the best way to do it, but it worked for me, as usual following the below is at your own risk yada yada.

The container I used is centos 7, this is made possible by ndppd ( )

This assumes the device you want to route a IPv6 subnet to is already connected to the Network and can communicate with the machine running the NDP proxy


  • Create a VM/Container for the router, in my case I used 2 network interfaces eth0, which was bridged to a private network on the inside of a virtual firewall (It was opnsense not that that really matters) this is because I wanted the machine to have IPv4 but it didn’t need a routable IP, Nat was fine (It’s an IPv6 router not V4, the V4 was just for management updates.etc).  The second interface is then bridged to the uplink.
  • Install a basic Linux system, I used Centos7 and the steps posted here assume this but there isn’t really a reason it can’t be another distro as long as the libs are available.
  • Assign an IPv6 address on the external interface
  • Install development tools ( yum groupinstall “Development tools)
  • Install git ( yum install git )
  • Install some dependencies ( yum install glib2-devel libnl3-devel)
  • clone the ndppd repo ( git glone
  • change into the ndppd dir ( cd ndppd)
  • compile and install ndppd (make && make install)
  • copy the sample config (cp ndppd.conf-dist /etc/ndppd.conf)


Now you need to edit the sample config ( edit /etc/ndppd.conf in your favorite text editor)

find the line that starts proxy eth0 {

and change it to proxy XXXX {   (XXXX is the name of your interface that needs to answer the RA’s, the one connected to the upstream ISP/Provider)

then find the line that states  rule 1111:: { and replace 1111:: with the subnet you want to proxy for instance 2001:xxxx:xxxx:xxxx/72 (Use a subnet calculator to see what you can divide your ISP allocated block into)

If your device you want to route to doesn’t send RA’s (my VPN server doesn’t) then you need to change auto to static

Here’s my config for an example.

# route-ttl (NEW)
# This tells ‘ndppd’ how often to reload the route file /proc/net/ipv6_route.
# Default value is ‘30000’ (30 seconds).

route-ttl 30000

# proxy
# This sets up a listener, that will listen for any Neighbor Solicitation
# messages, and respond to them according to a set of rules (see below).
# is required. You may have several ‘proxy’ sections.

proxy net1 {

# router
# This option turns on or off the router flag for Neighbor Advertisement
# messages. Default value is ‘true’.

router yes

# timeout
# Controls how long to wait for a Neighbor Advertisment message before
# invalidating the entry, in milliseconds. Default value is ‘500’.

timeout 500

# ttl
# Controls how long a valid or invalid entry remains in the cache, in
# milliseconds. Default value is ‘30000’ (30 seconds).

ttl 30000

# rule [/]
# This is a rule that the target address is to match against. If no netmask
# is provided, /128 is assumed. You may have several rule sections, and the
# addresses may or may not overlap.

rule 2001:xxxx:xxxx:xx:xxxx::/72 {
# Only one of ‘static’, ‘auto’ and ‘interface’ may be specified. Please
# read ‘ndppd.conf’ manpage for details about the methods below.

# ‘auto’ should work in most cases.

# static (NEW)
# ‘ndppd’ will immediately answer any Neighbor Solicitation Messages
# (if they match the IP rule).

# iface
# ‘ndppd’ will forward the Neighbor Solicitation Message through the
# specified interface – and only respond if a matching Neighbor
# Advertisement Message is received.

# auto (NEW)
# Same as above, but instead of manually specifying the outgoing
# interface, ‘ndppd’ will check for a matching route in /proc/net/ipv6_route.


# Note that before version 0.2.2 of ‘ndppd’, if you didn’t choose a
# method, it defaulted to ‘static’. For compatibility reasons we choose
# to keep this behavior – for now (it may be removed in a future version).

Next we’ll probably want some firewall rules (It’s more to protect the admin interface than anything, this example allows access from private IP’s in 2 rather large ranges)

I call a script called /etc/firewall.script

/usr/sbin/iptables -F
/usr/sbin/iptables -A INPUT -m state –state ESTABLISHED,RELATED -j ACCEPT
/usr/sbin/iptables -A INPUT -p tcp -s -j ACCEPT
/usr/sbin/iptables -A INPUT -p tcp -s -j ACCEPT
/usr/sbin/iptables -P INPUT DROP

/usr/sbin/ip6tables -F
/usr/sbin/ip6tables -P INPUT DROP
/usr/sbin/ip6tables -A INPUT -m state –state ESTABLISHED,RELATED -j ACCEPT
/usr/sbin/ip6tables -A INPUT -i eth0 -j DROP
/usr/sbin/ip6tables -A INPUT -p icmpv6 -j ACCEPT


Now we need to enable ipv6 forwarding and add the routes, as a quick and dirty hack I used /etc/rc.local the sleep command was because it was firing to quick and the route wasn’t getting added.


bash /etc/firewall.script
/usr/bin/sleep 15
/usr/sbin/ip -6 route add 2001:xxx:xxxx:xx:xxxx::/72 via 2001:xxxx:xxxx:yy::2
sysctl -w net.ipv6.conf.all.forwarding=1
/usr/local/sbin/ndppd -d

On the other device that you want to route to add this machines IPv6 as the default gateway and it routes (hopefully)