OpenWrt Forum Archive

Topic: IPv6 SLAAC just works

The content of this topic has been archived on 24 Apr 2018. There are no obvious gaps in this topic, but there may still be some posts missing at the end.

I have started playing with IPv6.
From the wiki (wiki.openwrt.org/doc/uci/network#protocol_dhcpv6), I thought I would have to install the odhcp6c package in order to get a (global scope) IPv6 address.
But it turns out I don't, because SLAAC (Stateless Address Autoconfiguration) just works! Can someone help me understand why?

In /etc/config/network I have the following section:

config interface 'wan0'
    option ifname 'eth1'
    option dns '8.8.8.8'
    option netmask '255.255.255.0'
    option gateway '172.24.20.1'
    option hostname 'SmartSwarm6202731'
    option ipaddr '172.24.20.13'
    option proto 'static'

This is my default config for a static IPv4 address.

As soon as I connect eth1 to my upstream router, my device sends a Router Solicitation message, and the upstream router replies with a Router Advertisement message (with M=0, O=0, and a Prefix Information Option with a valid 64-bit prefix, with L=1, A=1).

ifconfig confirms that my device now has a globally-routable IPv6 address (2001:...), using the link-local address (fe80::...) of the upstream router as the default gateway.

This is all great, but I'm confused as to what daemon is managing all this.
If I run ps, there is no dhcp client running, IPv6 or otherwise.

(Last edited by bkinsell on 24 Oct 2016, 12:40)

odhcpd

bolvan wrote:

odhcpd

I don't have odhcpd!
I do have dnsmasq though - could that be it? (I thought dnsmasq and odhcpd were DHCP servers, not clients?)

How would I disable dnsmasq as a test? I have tried removing the /etc/config/dhcp file, but I'm still seeing a dnsmasq process running.

bkinsell wrote:

This is all great, but I'm confused as to what daemon is managing all this.

You don't need a daemon to use SLAAC for configuring an IPv6 address on the OpenWRT device. But you do if you want to use OpenWRT as an IPv6-router. (Unless you use static IPv6 addresses on the LAN which is not common.)

(Last edited by mikma on 25 Oct 2016, 11:12)

Openwrt in default conf will not accept RAs because net.ipv6.conf.<if>.forwarding=1 and net.ipv6.conf.<if>.accept_ra=1.

accept_ra
Possible values are:

    0 Do not accept Router Advertisements.
    1 Accept Router Advertisements if forwarding is disabled.
    2 Overrule forwarding behaviour. Accept Router Advertisements even if forwarding is enabled.

Try issuing "ip -6 route" on normal linux system and openwrt.
In openwrt you see "proto static", in normal linux "proto ra".
This is because on desktop linux forwarding is disabled and kernel accepts RAs.
In openwrt routes look like someone manually added them.

try stop dhcpd and do
ifdown wan ; ifup wan
System will not receive ipv6 slaac ips.
start odhcpd and do
ifdown wan ; ifup wan
System will receive ipv6 slaac ips.

Its because odhcpd is not only dhcp server, it serves all RA management from artificially accepting RAs in user mode daemon instead of kernel  to acting as RA server or RA relay

bkinsell wrote:

I don't have odhcpd!

Which openwrt version are  you on ?
In old versions its not present.

(Last edited by bolvan on 25 Oct 2016, 12:01)

mikma wrote:
bkinsell wrote:

This is all great, but I'm confused as to what daemon is managing all this.

You don't need a daemon to use SLAAC for configuring an IPv6 address on the OpenWRT device. But you do if you want to use OpenWRT as an IPv6-router. (Unless you use static IPv6 addresses on the LAN which is not common.)

Thanks! Wow - I was not aware that the Linux kernel handles SLAAC directly, using /proc/sys/net/ipv6
This blog post explains some of the options: http://strugglers.net/~andy/blog/2011/0 … orwarding/


Why then does odhcp6c support "RA only"? From the README:

1. IPv6 bootstrap from different environments with autodetection
    a) RA only
    b) RA + stateless DHCPv6
    c) RA + stateful DHCPv6 (either IA_NA or IA_PD or both)

This is _not_ correct.

odhcpd is the daemon which is sending RAs into your LAN, the kernel does not send RAs. If you do a 'ps' I think you will find odhcpd. It is part of the 15.05.1 distro. The OpenWRT folks have tried to make it work for IPv6 right out of the box with Chaos Calmer (15.05). And they have done a pretty good job, IMHO.

Good to hear you have IPv6 working.

So, I confirmed that in my original setup, the device was configured with /proc/sys/net/ipv6/conf/all/forwarding = 0, which meant that the kernel was sending Router Solicitations and autoconfiguring an address from Router Advertisements.
If I set /proc/sys/net/ipv6/conf/all/forwarding to 1, the device is configured as a router (which it should have been all along), and SLAAC no longer works.
Thanks bolvan, mikma.

Now I want to use odhcp6c for SLAAC. So I change /etc/config/network as follows:

config interface 'wan0'
        option ifname 'eth1'
        option proto 'dhcpv6'
        option reqaddress 'none'
        option reqprefix 'no'
        option defaultroute '1'

Is this the correct config for "RA only" in odhcp6c?

I'm seeing that SLAAC works, in that I get a globally-routable IPv6 address with the advertised prefix.
But for some reason there's no default route, so I have no internet access.
Using Wireshark, I have confirmed that the RA message has the L (On-Link) flag set.
Any ideas?

By the way, I've confirmed that odhcpd isn't on the system.
This is a custom device with OpenWrt-based networking (procd, uci, ubus). I also have packages like odhcp6c and dnsmasq. But I don't have opkg. Is there any way to establish what other OpenWrt packages and versions are installed?

(Last edited by bkinsell on 27 Oct 2016, 11:54)

Sorry about all the questions, but I have been looking at the usage options for odhcp6c:

# odhcp6c
Usage: odhcp6c [options] <interface>

Feature options:
        -S <time>       Wait at least <time> sec for a DHCP-server (0)
        -N <mode>       Mode for requesting addresses [try|force|none]
        -P <length>     Request IPv6-Prefix (0 = auto)
        -F              Force IPv6-Prefix
        -V <class>      Set vendor-class option (base-16 encoded)
        -u <user-class> Set user-class option string
        -c <clientid>   Override client-ID (base-16 encoded 16-bit type + value)
        -i <iface-id>   Use a custom interface identifier for RA handling
        -r <options>    Options to be requested (comma-separated)
        -R              Do not request any options except those specified with -r
        -s <script>     Status update script (/usr/sbin/odhcp6c-update)
        -a              Don't send Accept Reconfigure option
        -f              Don't send Client FQDN option
        -k              Don't send a RELEASE when stopping
        -t <seconds>    Maximum timeout for DHCPv6-SOLICIT (3600)
        -m <seconds>    Minimum time between accepting updates (30)

Invocation options:
        -p <pidfile>    Set pidfile (/var/run/odhcp6c.pid)
        -d              Daemonize
        -e              Write logmessages to stderr
        -v              Increase logging verbosity
        -h              Show this help

Where is the option defaultroute?
Or is this handled by something other than odhcp6c?
In general, how does one trace a UCI option from /etc/config/X to the corresponding daemon(s)?

bkinsell,

I think there is a mis-understanding. Perhaps a network diagram would be better. If this is your "gateway" router, e.g. the one that talks to your ISP, then you want DHCPv6 to get a prefix. Therefore you want to set reqpreifx to 1, not 0. You don't want the WAN interface to get an IPv6 address via SLAAC, but via DHCPv6 (assuming your provider supports this).

SLAAC is for the endstations, not routers, generally. So you will want to create a wan6 interface like this:
https://wiki.openwrt.org/doc/uci/networ … connection

and that _should_ do it (if your ISP supports DHCPv6-PD).

HTH

cvmiller wrote:

bkinsell,

I think there is a mis-understanding. Perhaps a network diagram would be better. If this is your "gateway" router, e.g. the one that talks to your ISP, then you want DHCPv6 to get a prefix. Therefore you want to set reqpreifx to 1, not 0. You don't want the WAN interface to get an IPv6 address via SLAAC, but via DHCPv6 (assuming your provider supports this).

SLAAC is for the endstations, not routers, generally. So you will want to create a wan6 interface like this:
https://wiki.openwrt.org/doc/uci/networ … connection

and that _should_ do it (if your ISP supports DHCPv6-PD).

HTH

@cvmiller: I understand. I have a couple of reasons for wanting to use SLAAC-only.

  • Cellular ISPs don't support DHCPv6 prefix delegation;

  • I want to allow SLAAC-only on my Ethernet WAN interface, just as a learning exercise.

In general, I'm trying to understand the whole OpenWrt / IPv6 system, not just get it working.

So getting back to my previous questions:

  • How should odhcp6c be configured for "RA only"?

  • What daemon handles the defaultroute option for proto dhcpv6?

Thanks.

bkinsell,

I understand better now. Since your ISP doesn't support PD, you will need to "bridge" or "relay" the ISPs prefix over to your LAN segment. The easiest way to do this is to use odhcpd's "relay" feature. Look here for how to configure (just below the dhcpv6 config)
https://wiki.openwrt.org/doc/uci/networ … ent_dhcpv6

The default route in IPv6 is a function of the RA itself. Because the router is sending RAs, it becomes the default route. It happens automagically. You don't configure it.

You can put odhcpd into RA mode only, but since you don't have PD, there won't be anything to advertise in the RA, relay will work better for your situation. You might find the documentation on odhcpd helpful. There you can see there is a ra_management parameter that controls the M & O bits. What isn't said is that the O bit is always on (which is harmless).Use the value of '0' for SLAAC only.
https://wiki.openwrt.org/doc/techref/odhcpd

Because cell providers typically don't do PD, the IETF has created a standard to deal with this, essentially extending the providers /64. It is a good read.
https://tools.ietf.org/html/rfc7278

HTH

I realise now that I wan't clear at the very start. I'm not worried about the need for relaying prefixes from WAN- to LAN-side. (At least not yet!)
My first problem is in using SLAAC to get an address for the WAN interface itself.
As described above, using odhcp6c I am getting an address, but no default route.
Why?

Is bolvan correct to say that I require odhcpD?
I am working on getting odhcpd installed in my system, but find it strange that this feature would be part of odhcpd and not odhcp6c.

odhcp6c only receives and interprets RAs. It does not make any changes to system, ips, routes,... For this it calls /lib/netifd/dhcpv6.script. Debug this script to understand what it does. Modern openwrt versions push ips/routes to netifd using shell helper functions. They construct json query and send it to netifd thru ubus.
Netifd is responsible to making actual changes.
ifstatus <ifname> shows internal netifd state of a given logical interface

(Last edited by bolvan on 4 Nov 2016, 17:57)

bkinsell,

Thanks to bolvan for that explanation. But if your upstream connection isn't supporting DHCPv6, then odhcp6c will not return anything useful (such as an address). Are you quite sure they don't support DHCPv6?

If they don't, then you will probably have to enable the following on your wan interface
accept_ra                         
accept_ra_defrtr

Not sure what the UCI config is, but you can poke 1s into the /proc/sys/net/ipv6/conf/eth0/ (assuming eth0 is your WAN) just to see if it works.

bolvan wrote:

odhcp6c only receives and interprets RAs. It does not make any changes to system, ips, routes,... For this it calls /lib/netifd/dhcpv6.script. Debug this script to understand what it does. Modern openwrt versions push ips/routes to netifd using shell helper functions. They construct json query and send it to netifd thru ubus.
Netifd is responsible to making actual changes.
ifstatus <ifname> shows internal netifd state of a given logical interface

I added some logging to the /lib/netifd/dhcpv6.script.
When the RA message is received, the $RA_ROUTES variable seems to have the correct info:

::/0,fe80::Y:Y:Y:Y,1800,512
2001:470:6db5::/64,,2592000,256

But the default route (::/0) never makes it into the routing table:
(I'm redacting the host IDs)

# route -A inet6
Kernel IPv6 routing table
Destination                                 Next Hop                                Flags Metric Ref    Use Iface
2001:470:6db5::/64                          ::                                      U     256    0        0 eth1    
fe80::/64                                   ::                                      U     256    0        0 eth0    
fe80::/64                                   ::                                      U     256    0        0 eth1    
::1/128                                     ::                                      U     0      0        1 lo      
2001:470:6db5::/128                         ::                                      U     0      0        1 lo      
2001:470:6db5:0:X:X:X:X/128                 ::                                      U     0      0        1 lo      
fe80::/128                                  ::                                      U     0      0        1 lo      
fe80::/128                                  ::                                      U     0      0        1 lo      
fe80::X:X:X:X/128                           ::                                      U     0      0        1 lo      
fe80::X:X:X:X/128                           ::                                      U     0      5        1 lo      
ff02::1/128                                 ::                                      UC    0      75       0 eth1    
ff02::2/128                                 ::                                      UC    0      3        0 eth1    
ff02::1:2/128                               ::                                      UC    0      2        0 eth1    
ff00::/8                                    ::                                      U     256    0        0 eth0    
ff00::/8                                    ::                                      U     256    0        3 eth1    

The problem seems to be in the following code in the "for entry in $RA_ROUTES" part of dhcpv6.script:

if [ -z "$SOURCE_ROUTING" -o -z "$gw" ]; then
            logger -p user.info -t "my_dhcpv6[$$]" "if.. $addr $mask $gw $metric $valid"
            proto_add_ipv6_route "$addr" "$mask" "$gw" "$metric" "$valid"
        else
            logger -p user.info -t "my_dhcpv6[$$]" "else.. $addr $mask $gw $metric $valid"
            proto_add_ipv6_route "$addr" "$mask" "$gw" "$metric" "$valid" "::/128"
            for prefix in $PREFIXES $ADDRESSES; do
                local paddr="${prefix%%,*}"
                logger -p user.info -t "my_dhcpv6[$$]" "loop.. $addr $mask $gw $metric $valid $paddr"
                proto_add_ipv6_route "$addr" "$mask" "$gw" "$metric" "$valid" "$paddr"
            done
        fi

Of the 2 routes in the $RA_ROUTES variable, the 2001:470:6db5::/64 address gets handled by the 'if' and seems fine. The ::/0 address gets handles by the else, and again by the for loop within the else clause.
This leads to a duplicate entry in netifd:

# ifstatus wan0
{
    "up": true,
    "pending": false,
    "available": true,
    "autostart": true,
    "uptime": 356,
    "l3_device": "eth1",
    "proto": "dhcpv6",
    "device": "eth1",
    "metric": 0,
    "delegation": true,
    "ipv4-address": [
        
    ],
    "ipv6-address": [
        {
            "address": "2001:470:6db5:0:X:X:X:X",
            "mask": 64,
            "preferred": 604796,
            "valid": 2591996
        }
    ],
    "ipv6-prefix": [
        
    ],
    "ipv6-prefix-assignment": [
        
    ],
    "route": [
        {
            "target": "2001:470:6db5::",
            "mask": 64,
            "nexthop": "::",
            "metric": 256,
            "valid": 2591996,
            "source": "::\/0"
        },
        {
            "target": "::",
            "mask": 0,
            "nexthop": "fe80::Y:Y:Y:Y",
            "metric": 512,
            "valid": 1796,
            "source": "2001:470:6db5:0:X:X:X:X\/64"
        },
        {
            "target": "::",
            "mask": 0,
            "nexthop": "fe80::Y:Y:Y:Y",
            "metric": 512,
            "valid": 1796,
            "source": "::\/128"
        }
    ],
    "dns-server": [
        
    ],
    "dns-search": [
        
    ],
    "inactive": {
        "ipv4-address": [
            
        ],
        "ipv6-address": [
            
        ],
        "route": [
            
        ],
        "dns-server": [
            
        ],
        "dns-search": [
            
        ]
    },
    "data": {
        
    }
}

If I remove the whole if-else block, and replace it with a single line

proto_add_ipv6_route "$addr" "$mask" "$gw" "$metric" "$valid"

then everything works:

# route -A inet6
Kernel IPv6 routing table
Destination                                 Next Hop                                Flags Metric Ref    Use Iface
2001:470:6db5::/64                          ::                                      U     256    0        0 eth1    
fe80::/64                                   ::                                      U     256    0        0 eth0    
fe80::/64                                   ::                                      U     256    0        0 eth1    
::/0                                        fe80::Y:Y:Y:Y                           UG    512    0        0 eth1    
::1/128                                     ::                                      U     0      0        1 lo      
2001:470:6db5::/128                         ::                                      U     0      0        1 lo      
2001:470:6db5:0:X:X:X:X/128                 ::                                      U     0      0        1 lo      
fe80::/128                                  ::                                      U     0      0        1 lo      
fe80::/128                                  ::                                      U     0      0        1 lo      
fe80::X:X:X:X/128                           ::                                      U     0      0        1 lo      
fe80::X:X:X:X/128                           ::                                      U     0      8        1 lo      
ff02::1/128                                 ::                                      UC    0      4        0 eth1    
ff00::/8                                    ::                                      U     256    0        0 eth0    
ff00::/8                                    ::                                      U     256    0        1 eth1    
# ifstatus wan0
{
    "up": true,
    "pending": false,
    "available": true,
    "autostart": true,
    "uptime": 91,
    "l3_device": "eth1",
    "proto": "dhcpv6",
    "device": "eth1",
    "updated": [
        "addresses",
        "routes"
    ],
    "metric": 0,
    "delegation": true,
    "ipv4-address": [
        
    ],
    "ipv6-address": [
        {
            "address": "2001:470:6db5:0:X:X:X:X",
            "mask": 64,
            "preferred": 604699,
            "valid": 2591899
        }
    ],
    "ipv6-prefix": [
        
    ],
    "ipv6-prefix-assignment": [
        
    ],
    "route": [
        {
            "target": "2001:470:6db5::",
            "mask": 64,
            "nexthop": "::",
            "metric": 256,
            "valid": 2591899,
            "source": "::\/0"
        },
        {
            "target": "::",
            "mask": 0,
            "nexthop": "fe80::Y:Y:Y:Y",
            "metric": 512,
            "valid": 1699,
            "source": "::\/0"
        }
    ],
    "dns-server": [
        
    ],
    "dns-search": [
        
    ],
    "inactive": {
        "ipv4-address": [
            
        ],
        "ipv6-address": [
            
        ],
        "route": [
            
        ],
        "dns-server": [
            
        ],
        "dns-search": [
            
        ]
    },
    "data": {
        
    }
}

Can anyone explain what's going on?

I don't understand the significance of the 'source' option in IPv6 routes.
There is a one-line description here: https://wiki.openwrt.org/doc/uci/network#ipv6_routes. This sounds like NAT to me; what has it got to do with a route?

cvmiller wrote:

bkinsell,

Thanks to bolvan for that explanation. But if your upstream connection isn't supporting DHCPv6, then odhcp6c will not return anything useful (such as an address). Are you quite sure they don't support DHCPv6?

If they don't, then you will probably have to enable the following on your wan interface
accept_ra                         
accept_ra_defrtr

Not sure what the UCI config is, but you can poke 1s into the /proc/sys/net/ipv6/conf/eth0/ (assuming eth0 is your WAN) just to see if it works.

I am using a test environment that is completely within my own control. I have configured the upstream router for SLAAC only.

I'm confused by your comment: I thought we had already established (earlier in this topic) that if I enable forwarding, I can prevent the kernel from handling RAs. I want odhcp6c and netifd to handle them instead.

Not sure why you want this pain. Typical deployments of IPv6 will either use DHCPv6-PD or static config. I have a test environment with a downstream router which gets PD from my upstream router. It works rather well.

But that said. Forwarding, by default will turn off listening to RAs. However, you _can_ turn it on, as I meantioned earlier. As for the default route not being propagated. Again, I don't think people thought about using a SLAAC-only routed network.Best of luck.

I want this pain because I need to support cellular WAN. IPv6 SLAAC is standard in all GSM networks. See 3GPP TS 29.061. Where I work, we don't have a cellular operator offering IPv6. (The only one I know is Verizon Wireless in the US.) So, I am testing locally with IPv6 over ethernet.

I don't think people thought about using a SLAAC-only routed network.

I'm sure they did, for cellular at least. For example, the qmi.sh script that ships with the OpenWrt uqmi package references RFC 7278: "Extending an IPv6 /64 Prefix from a 3GPP Mobile Interface to a LAN Link".

I want this pain because I need to support cellular WAN. IPv6 SLAAC is standard in all GSM networks.

Fair enough. It sounds like you are close, you just need to get the default route working (which is in the RA).

I'm sure they did, for cellular at least. For example, the qmi.sh script that ships with the OpenWrt uqmi package references RFC 7278: "Extending an IPv6 /64 Prefix from a 3GPP Mobile Interface to a LAN Link".

I am well familiar with this RFC and it addresses how to create a hot spot by extending (or proxying) the Cell carrier's /64 into the hot spot. I talked to someone who has Telstra who does this.

I look forward to hearing how you solve the problem and get it working. perhaps you could update the OpenWRT wiki page with your findings.

Note that it is possible to have the kernel accept RAs even when routing. You can either set accept_ra to 2, or configure a mixed host/router.  The latter concept can be a bit confusing though.  The thing is that you can turn off "forwarding" on your wan interface with

echo 0 >/proc/sys/net/ipv6/conf/wan/forwarding

without actually affecting routing.  Packets will still be routed in and out of that interface as long as /proc/sys/net/ipv6/conf/all/forwarding is 1.  But the wan interface is now treated as a host interface wrt RAs.

When it comes to the issue of sharing a single /64 prefix received in a RA, that is usually not a problem as long as you have only a single lan interface.  Just route the /64 from wan to lan, and make soure you have a default route back (which you'll usually have anyway). You could possibly leave a /128 for the wan interface if you need or want that.  An address more or less doesn't matter much in IPv6 smile

The discussion might have continued from here.