How to limit size of delegated downstream IPv6 prefixes

I'm experimenting with IPv6 and delegating from my upstream to a downstream VM.
Here's the configuration I'm trying to do.
Hurricane Electric delegates me a /48
I have several downstream interfaces (lan, guest, a few others)
I want to allow delegation of no more than a /60 to any downstream router. Where do I put such a limit: in the HE tunnel network configuration? In the downstream interface (lan, guest, etc.) through which the router connects?

Conceptually, I'm thinking I would have something like this:

tunnelwan6:  /48
  br-guest: /56
    <up to 15 clients of guest>: /60

If I set up the guest network with ip6assign=56, then any client can ask for up to a /57 and get it. I want to limit each to a /60 (to leave room for more clients).

I'm using an OpenWrt x86-64 virtual machine as a test bed, I have it set up with a virtual WAN interface bridged onto the guest network, and it will get a prefix delegation, but I haven't succeeded in limiting its ability to grab a larger prefix than I want to permit.

Here's the network config for the HE tunnel and the guest network on the main router:

network.tunnelwan6=interface
network.tunnelwan6.proto='6in4'
network.tunnelwan6.peeraddr='209.51.161.14'
network.tunnelwan6.ip6addr='2001:470:REDACTED::2/64'
network.tunnelwan6.tunnelid='REDACTED'
network.tunnelwan6.username='REDACTED'
network.tunnelwan6.password='REDACTED'
network.tunnelwan6.mtu='1480'
network.tunnelwan6.ip6assign='64'
network.tunnelwan6.ip6hint='2'
network.tunnelwan6.ip6prefix='2001:470:REDACTED::/48'


network.guest=interface
network.guest.proto='static'
network.guest.device='br-guest'
network.guest.ipaddr='192.168.8.1'
network.guest.netmask='255.255.255.0'
network.guest.ip6class='local' 'tunnelwan6'
network.guest.ip6assign='56'
network.guest.ip6hint='ff00'
network.guest.delegate='0'

(Also, I'm not seeing that network.guest.delegate='0' changes any behavior...should it?)

and ifstatus for guest & tunnelwan6:

# ifstatus guest
{
	"up": true,
	"pending": false,
	"available": true,
	"autostart": true,
	"dynamic": false,
	"uptime": 2294,
	"l3_device": "br-guest",
	"proto": "static",
	"device": "br-guest",
	"updated": [
		"addresses"
	],
	"metric": 0,
	"dns_metric": 0,
	"delegation": false,
	"ipv4-address": [
		{
			"address": "192.168.8.1",
			"mask": 24
		}
	],
	"ipv6-address": [
		
	],
	"ipv6-prefix": [
		
	],
	"ipv6-prefix-assignment": [
		{
			"address": "2001:470:REDACTED:ff00::",
			"mask": 56,
			"local-address": {
				"address": "2001:470:REDACTED:ff00::1",
				"mask": 56
			}
		},
		{
			"address": "ULAREDACTED:ff00::",
			"mask": 56,
			"local-address": {
				"address": "ULAREDACTED:ff00::1",
				"mask": 56
			}
		}
	],
	"route": [
		
	],
	"dns-server": [
		
	],
	"dns-search": [
		
	],
	"neighbors": [
		
	],
	"inactive": {
		"ipv4-address": [
			
		],
		"ipv6-address": [
			
		],
		"route": [
			
		],
		"dns-server": [
			
		],
		"dns-search": [
			
		],
		"neighbors": [
			
		]
	},
	"data": {
		
	}
}

# ifstatus tunnelwan6
{
	"up": true,
	"pending": false,
	"available": true,
	"autostart": true,
	"dynamic": false,
	"uptime": 119045,
	"l3_device": "6in4-tunnelwan6",
	"proto": "6in4",
	"updated": [
		"addresses",
		"routes",
		"prefixes"
	],
	"metric": 0,
	"dns_metric": 0,
	"delegation": true,
	"ipv4-address": [
		
	],
	"ipv6-address": [
		{
			"address": "2001:470:REDACTED::2",
			"mask": 64
		}
	],
	"ipv6-prefix": [
		{
			"address": "2001:470:REDACTED::",
			"mask": 48,
			"class": "tunnelwan6",
			"assigned": {
				"lan": {
					"address": "2001:470:REDACTED:1::",
					"mask": 64
				},
				"tunnelwan6": {
					"address": "2001:470:REDACTED:2::",
					"mask": 64
				},
				"guest": {
					"address": "2001:470:REDACTED:ff00::",
					"mask": 56
				}
			}
		}
	],
	"ipv6-prefix-assignment": [
		{
			"address": "2001:470:REDACTED:2::",
			"mask": 64,
			"local-address": {
				"address": "2001:470:REDACTED:2::1",
				"mask": 64
			}
		},
		{
			"address": "ULAREDACTED:2::",
			"mask": 64,
			"local-address": {
				"address": "ULAREDACTED:2::1",
				"mask": 64
			}
		}
	],
	"route": [
		{
			"target": "::",
			"mask": 0,
			"nexthop": "::",
			"table": 100,
			"source": "::/0"
		},
		{
			"target": "::",
			"mask": 0,
			"nexthop": "::",
			"source": "2001:470:REDACTED::/48"
		},
		{
			"target": "::",
			"mask": 0,
			"nexthop": "::",
			"source": "2001:470:REDACTED::2/64"
		}
	],
	"dns-server": [
		
	],
	"dns-search": [
		
	],
	"neighbors": [
		
	],
	"inactive": {
		"ipv4-address": [
			
		],
		"ipv6-address": [
			
		],
		"route": [
			
		],
		"dns-server": [
			
		],
		"dns-search": [
			
		],
		"neighbors": [
			
		]
	},
	"data": {
		
	}
}

One more possibly relevant config section for DHCP on the guest interface:

# uci show dhcp.guest
dhcp.guest=dhcp
dhcp.guest.interface='guest'
dhcp.guest.start='100'
dhcp.guest.limit='150'
dhcp.guest.leasetime='12h'
dhcp.guest.ra='server'
dhcp.guest.dhcpv6='server'
dhcp.guest.ra_flags='managed-config'

This is not needed.

There is no prefix delegated from upstream of guest interface, so you won't see any difference.

Regarding the main question, I could not find any relevant option. It seems that for so complicated networks it is better to configure them manually.

The delegated network from upstream appears on pppoe-wan in my setting:

34: pppoe-wan: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1492 qdisc fq_codel state UNKNOWN group default qlen 3
    link/ppp 
    inet 80.151.XX.XXX peer 62.156.XXX.XX/32 scope global pppoe-wan
       valid_lft forever preferred_lft forever
    inet6 2003:a:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX/64 scope global dynamic noprefixroute 
       valid_lft 14305sec preferred_lft 1705sec
    inet6 fe80::XXXX:XXXX:XXXX:XXXX/128 scope link 
       valid_lft forever preferred_lft forever

OpenWRT then sets a /64 address on br-lan. I have not other networks, but I'd assume they would work the same. Here are the two lines that configure that /64 static address:

option ip6assign '64'
option ip6ifaceid '::1'
1 Like

I think the relevant program is odhcpd, I'm willing to give it a go to add a limitation option to it to restrict delegated prefix lengths. But, I need to find the right information on how to build it from local sources.

https://openwrt.org/docs/guide-developer/packages is pretty close, but I can't tell from there if there's an option to build from an unpacked directory, rather than say a .tar.gz file? I would then edit package/network/services/odhcpd/Makefile
to use my local sources (checked-out copy of odhcpd repository)

I found the hint I needed in https://openwrt.org/docs/guide-developer/package-policies#the_src_directory

I can make package/network/services/odhcpd/src a symbolic link to my checked-out sources, comment out all the PKG_SOURCE_* variables in package/network/services/odhcpd/Makefile, then use make package/odhcpd/{clean,compile} as my build step in the edit-build-debug cycle.

I've put up a PR to add this to odhcpd, https://github.com/openwrt/odhcpd/pull/184
@dedeckeh would you consider this?