Skip to content

Commit 889120e

Browse files
authored
Merge pull request #1251 from kernelkit/dhcpv6-client
Add DHCPv6 client support
2 parents 8ea9644 + 548e0c4 commit 889120e

35 files changed

+1466
-287
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/usr/share/udhcpc/default.script
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/usr/share/udhcpc/default6.script

board/common/rootfs/usr/share/udhcpc/default.script

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,6 @@ case "$ACTION" in
104104
deconfig)
105105
clr_dhcp_addresses
106106
clr_dhcp_routes
107-
/bin/ip link set dev $interface up
108107

109108
# drop info from this interface
110109
rm -f "$RESOLV_CONF"
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
#!/bin/sh
2+
# This script expects a system with resolvconf (openresolv) and iproute2
3+
4+
[ -z "$1" ] && echo "Error: should be called from udhcpc6" && exit 1
5+
6+
ACTION="$1"
7+
RESOLV_CONF="/run/resolvconf/interfaces/${interface}-ipv6.conf"
8+
NTPFILE="/run/chrony/dhcp-sources.d/${interface}-ipv6.sources"
9+
10+
[ -n "$metric" ] || metric=5
11+
12+
if [ -z "${IF_WAIT_DELAY}" ]; then
13+
IF_WAIT_DELAY=10
14+
fi
15+
16+
log()
17+
{
18+
logger -I $$ -t udhcpc6 -p user.notice "${interface}: $*"
19+
}
20+
21+
dbg()
22+
{
23+
logger -I $$ -t udhcpc6 -p user.debug "${interface}: $*"
24+
}
25+
26+
err()
27+
{
28+
logger -I $$ -t udhcpc6 -p user.err "${interface}: $*"
29+
}
30+
31+
clr_dhcpv6_addresses()
32+
{
33+
addrs=$(ip -j addr show dev $interface \
34+
| jq -c '.[0].addr_info[] | select(.family == "inet6") | select(.protocol == "dhcp")')
35+
36+
for addr in $addrs; do
37+
ip="$(echo "$addr" | jq -r '."local"')"
38+
prefix="$(echo "$addr" | jq -r '."prefixlen"')"
39+
log "removing $ip/$prefix"
40+
ip addr del "$ip/$prefix" dev "$interface"
41+
done
42+
}
43+
44+
log "action $ACTION"
45+
case "$ACTION" in
46+
deconfig)
47+
clr_dhcpv6_addresses
48+
49+
# drop info from this interface
50+
rm -f "$RESOLV_CONF"
51+
rm -f "$NTPFILE"
52+
;;
53+
54+
leasefail|nak)
55+
# DHCPv6 lease failed - log it
56+
err "DHCPv6 lease failed"
57+
;;
58+
59+
renew|bound)
60+
# Add IPv6 address if provided (stateful DHCPv6)
61+
if [ -n "$ipv6" ]; then
62+
if /bin/ip addr add dev $interface $ipv6 proto dhcp; then
63+
log "assigned address $ipv6"
64+
fi
65+
fi
66+
67+
# Handle delegated prefix (prefix delegation)
68+
if [ -n "$ipv6prefix" ]; then
69+
log "received delegated prefix $ipv6prefix"
70+
# Prefix delegation handling could be added here
71+
# For now, just log it - actual routing/distribution
72+
# would need additional configuration
73+
fi
74+
75+
# drop info from this interface
76+
truncate -s 0 "$RESOLV_CONF"
77+
78+
# DHCPv6 domain search list (option 24)
79+
if [ -n "$search" ]; then
80+
dbg "adding search $search"
81+
echo "search $search # $interface" >> $RESOLV_CONF
82+
fi
83+
84+
# DHCPv6 DNS servers (option 23)
85+
if [ -n "$dns" ]; then
86+
for i in $dns ; do
87+
dbg "adding dns $i"
88+
echo "nameserver $i # $interface" >> $RESOLV_CONF
89+
done
90+
resolvconf -u
91+
fi
92+
93+
# NTP servers (option 56)
94+
if [ -n "$ntpsrv" ]; then
95+
truncate -s 0 "$NTPFILE"
96+
for srv in $ntpsrv; do
97+
dbg "got NTP server $srv"
98+
echo "server $srv iburst" >> "$NTPFILE"
99+
done
100+
chronyc reload sources >/dev/null
101+
fi
102+
;;
103+
esac
104+
105+
HOOK_DIR="$0.d"
106+
for hook in "${HOOK_DIR}/"*; do
107+
[ -f "${hook}" -a -x "${hook}" ] || continue
108+
"${hook}" "$ACTION"
109+
done
110+
111+
exit 0

doc/ChangeLog.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ All notable changes to the project are documented in this file.
3131
now enabled using a presence container instead of a separate `enabled` leaf
3232
- The `enabled` nore for IPv4 autoconf (ZeroConf) has been dropped, `autoconf`
3333
is now a presence container. Configuration automatically migrated on upgrade
34+
- Add DHCPv6 client support for per-interface IPv6 configuration, augmenting
35+
`/interfaces/interface[name]/ipv6/infix-dhcpv6-client:dhcp`, issue #1110
36+
- Fix namespace for DHCPv4 client YANG module from `urn:ietf:params:xml:ns:yang`
37+
to `urn:infix:params:xml:ns:yang` to properly reflect custom implementation
3438
- Improvements to `sdcard.img` generation, useful for developers mostly:
3539
- The NanoPi R2S bootloader is now automatically built and uploaded to
3640
the [`latest-boot` release][lastest-boot] tag

doc/networking.md

Lines changed: 101 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -959,16 +959,55 @@ Client Configuration](system.md#ntp-client-configuration) section.
959959

960960
Multiple address assignment methods are available:
961961

962-
| **Type** | **Yang Model** | **Description** |
963-
|:---------------- |:-------------- |:------------------------------------------------------------------------------------------------------------------------------------------------- |
964-
| static | ietf-ip | Static assignment of IPv6 address, e.g., *2001:db8:0:1::1/64* |
965-
| link-local | ietf-ip[^2] | (RFC4862) Auto-configured link-local IPv6 address (*fe80::0* prefix + interface identifier, e.g., *fe80::ccd2:82ff:fe52:728b/64*) |
966-
| global auto-conf | ietf-ip | (RFC4862) Auto-configured (stateless) global IPv6 address (prefix from router + interface identifier, e.g., *2001:db8:0:1:ccd2:82ff:fe52:728b/64* |
962+
| **Type** | **Yang Model** | **Description** |
963+
|:---------------- |:-------------------- |:------------------------------------------------------------------------------------------------------------------------------------------------- |
964+
| static | ietf-ip | Static assignment of IPv6 address, e.g., *2001:db8:0:1::1/64* |
965+
| link-local | ietf-ip[^2] | (RFC4862) Auto-configured link-local IPv6 address (*fe80::0* prefix + interface identifier, e.g., *fe80::ccd2:82ff:fe52:728b/64*) |
966+
| global auto-conf | ietf-ip | (RFC4862) Auto-configured (stateless) global IPv6 address (prefix from router + interface identifier, e.g., *2001:db8:0:1:ccd2:82ff:fe52:728b/64* |
967+
| dhcp | infix-dhcpv6-client | Assignment of IPv6 address by DHCPv6 server, e.g., *2001:db8::42/128* |
967968

968969
Both for *link-local* and *global auto-configuration*, it is possible
969970
to auto-configure using a random suffix instead of the interface
970971
identifier.
971972

973+
> [!NOTE]
974+
> The DHCPv6 address method is only available for *LAN* interfaces
975+
> (Ethernet, virtual Ethernet (veth), bridge, link aggregates, etc.)
976+
977+
Supported DHCPv6 (request) options, configurability (Cfg) and defaults,
978+
are listed below. Configurable options can be disabled on a per client
979+
interface basis, some options, like `client-id` and `client-fqdn`, are
980+
possible to set the value of as well.
981+
982+
| **Opt** | **Name** | **Cfg** | **Description** |
983+
|---------|----------------------------|---------|--------------------------------------------------------|
984+
| 1 | `client-id` | Yes | Client identifier (DUID), auto-generated by default |
985+
| 2 | `server-id` | Yes | Server identifier (DUID) |
986+
| 23 | `dns-server` | Yes | DNS recursive name servers, static ones take precedence|
987+
| 24 | `domain-search` | Yes | Domain search list |
988+
| 25 | `ia-pd` | Yes | Prefix delegation for downstream networks |
989+
| 31 | `sntp-server` | Yes | Simple Network Time Protocol servers |
990+
| 32 | `information-refresh-time` | Yes | Refresh time for stateless DHCPv6 |
991+
| 39 | `client-fqdn` | Yes | Client FQDN, request DNS update from server |
992+
| 56 | `ntp-server` | Yes | NTP time servers, static ones take precedence |
993+
| | | | |
994+
995+
**Default:** `dns-server`, `domain-search`, `ntp-server`
996+
997+
DHCPv6 supports both **stateful** (address assignment) and **stateless**
998+
(information-only) modes:
999+
1000+
- **Stateful DHCPv6**: The server assigns IPv6 addresses to clients. This is
1001+
the default mode when enabling the DHCPv6 client.
1002+
- **Stateless DHCPv6**: Used with SLAAC (Stateless Address Autoconfiguration)
1003+
when only configuration information (DNS, NTP, etc.) is needed. Enable with
1004+
the `information-only` setting.
1005+
1006+
When configuring a DHCPv6 client, ensure that the NTP client is enabled
1007+
for the `ntp-server` DHCPv6 option to be processed correctly. If the
1008+
NTP client is not enabled, any NTP servers provided by the DHCPv6 server
1009+
will be ignored. For details on how to enable the NTP client, see the
1010+
[NTP Client Configuration](system.md#ntp-client-configuration) section.
9721011

9731012
### Examples
9741013

@@ -1080,6 +1119,63 @@ Other useful DHCP options include:
10801119

10811120
For advanced usage with vendor-specific options, see the YANG model.
10821121

1122+
#### Use of DHCPv6 for IPv6 address assignment
1123+
1124+
![Using DHCPv6 for IPv6 address assignment](img/ip-address-example-ipv6-dhcp.svg)
1125+
1126+
admin@example:/> configure
1127+
admin@example:/config/> edit interface eth0 ipv6
1128+
admin@example:/config/interface/eth0/ipv6/> set dhcp
1129+
admin@example:/config/interface/eth0/ipv6/> leave
1130+
admin@example:/> show interface
1131+
INTERFACE PROTOCOL STATE DATA
1132+
eth0 ethernet UP 02:00:00:00:00:00
1133+
ipv6 2001:db8::42/128 (dhcp)
1134+
ipv6 fe80::ff:fe00:0/64 (link-layer)
1135+
lo ethernet UP 00:00:00:00:00:00
1136+
ipv4 127.0.0.1/8 (static)
1137+
ipv6 ::1/128 (static)
1138+
admin@example:/>
1139+
1140+
The resulting address (2001:db8::42/128) is of type *dhcp*.
1141+
1142+
To configure DHCPv6 client options, such as requesting prefix delegation
1143+
for downstream networks, you can specify options:
1144+
1145+
```
1146+
admin@example:/> configure
1147+
admin@example:/config/> edit interface eth0 ipv6 dhcp
1148+
admin@example:/config/interface/eth0/ipv6/dhcp/> set option ia-pd
1149+
admin@example:/config/interface/eth0/ipv6/dhcp/> set option dns-server
1150+
admin@example:/config/interface/eth0/ipv6/dhcp/> show
1151+
option dns-server;
1152+
option ia-pd;
1153+
admin@example:/config/interface/eth0/ipv6/dhcp/> leave
1154+
admin@example:/>
1155+
```
1156+
1157+
For stateless DHCPv6 (used with SLAAC to get only configuration information):
1158+
1159+
```
1160+
admin@example:/> configure
1161+
admin@example:/config/> edit interface eth0 ipv6 dhcp
1162+
admin@example:/config/interface/eth0/ipv6/dhcp/> set information-only true
1163+
admin@example:/config/interface/eth0/ipv6/dhcp/> show
1164+
information-only true;
1165+
option dns-server;
1166+
option domain-search;
1167+
admin@example:/config/interface/eth0/ipv6/dhcp/> leave
1168+
admin@example:/>
1169+
```
1170+
1171+
Other useful DHCPv6 options include:
1172+
1173+
- `duid` - Set a specific DHCPv6 Unique Identifier (auto-generated by default)
1174+
- `client-fqdn` - Request the server to update DNS records with client's FQDN
1175+
- `route-preference` - Set the administrative distance for DHCPv6-learned routes (default: 5)
1176+
1177+
For advanced usage with vendor-specific options, see the YANG model.
1178+
10831179
#### Disabling IPv6 link-local address(es)
10841180

10851181
The (only) way to disable IPv6 link-local addresses is by disabling IPv6

src/confd/src/Makefile.am

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ confd_plugin_la_SOURCES = \
4545
ietf-routing.c \
4646
infix-dhcp-common.c \
4747
infix-dhcp-client.c \
48+
infix-dhcpv6-client.c \
4849
infix-dhcp-server.c \
4950
infix-factory.c \
5051
infix-firewall.c \

src/confd/src/core.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,10 @@ static int change_cb(sr_session_ctx_t *session, uint32_t sub_id, const char *mod
198198
if ((rc = infix_dhcp_client_change(session, config, diff, event, confd)))
199199
goto free_diff;
200200

201+
/* infix-dhcpv6-client*/
202+
if ((rc = infix_dhcpv6_client_change(session, config, diff, event, confd)))
203+
goto free_diff;
204+
201205
/* ietf-keystore */
202206
if ((rc = ietf_keystore_change(session, config, diff, event, confd)))
203207
goto free_diff;
@@ -428,10 +432,6 @@ int sr_plugin_init_cb(sr_session_ctx_t *session, void **priv)
428432
goto err;
429433

430434
rc = infix_dhcp_server_candidate_init(&confd);
431-
if (rc)
432-
goto err;
433-
434-
rc = infix_dhcp_client_candidate_init(&confd);
435435
if (rc)
436436
goto err;
437437
/* YOUR_INIT GOES HERE */

src/confd/src/core.h

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -204,12 +204,11 @@ static inline int infix_containers_rpc_init(struct confd *confd) { return 0; }
204204
static inline int infix_containers_change(sr_session_ctx_t *session, struct lyd_node *config, struct lyd_node *diff, sr_event_t event, struct confd *confd) { return 0; }
205205
#endif
206206

207-
/* infix-dhcp-common.c */
208-
int dhcp_option_lookup(const struct lyd_node *id);
209-
210207
/* infix-dhcp-client.c */
211208
int infix_dhcp_client_change(sr_session_ctx_t *session, struct lyd_node *config, struct lyd_node *diff, sr_event_t event, struct confd *confd);
212-
int infix_dhcp_client_candidate_init(struct confd *confd);
209+
210+
/* infix-dhcpv6-client.c */
211+
int infix_dhcpv6_client_change(sr_session_ctx_t *session, struct lyd_node *config, struct lyd_node *diff, sr_event_t event, struct confd *confd);
213212

214213
/* infix-dhcp-server.c */
215214
int infix_dhcp_server_candidate_init(struct confd *confd);

src/confd/src/ietf-interfaces.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,10 @@ static int ifchange_cand(sr_session_ctx_t *session, uint32_t sub_id, const char
160160
if (err)
161161
break;
162162

163+
err = ifchange_cand_infer_dhcp(session, new->xpath);
164+
if (err)
165+
break;
166+
163167
err = cni_ifchange_cand_infer_type(session, new->xpath);
164168
if (err)
165169
break;

0 commit comments

Comments
 (0)