Skip to content

Commit 26ecf48

Browse files
authored
Merge pull request #1350 from kernelkit/change-dhcpv6-client
Migrate to odhcp6c for dhpcv6 client support
2 parents f95c938 + fa6023c commit 26ecf48

23 files changed

Lines changed: 534 additions & 147 deletions

board/common/busybox_defconfig

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1016,12 +1016,7 @@ CONFIG_UDHCPC=y
10161016
CONFIG_FEATURE_UDHCPC_ARPING=y
10171017
CONFIG_FEATURE_UDHCPC_SANITIZEOPT=y
10181018
CONFIG_UDHCPC_DEFAULT_SCRIPT="/usr/share/udhcpc/default.script"
1019-
CONFIG_UDHCPC6_DEFAULT_SCRIPT="/usr/share/udhcpc/default6.script"
1020-
CONFIG_UDHCPC6=y
1021-
CONFIG_FEATURE_UDHCPC6_RFC3646=y
1022-
CONFIG_FEATURE_UDHCPC6_RFC4704=y
1023-
CONFIG_FEATURE_UDHCPC6_RFC4833=y
1024-
CONFIG_FEATURE_UDHCPC6_RFC5970=y
1019+
# CONFIG_UDHCPC6 is not set
10251020

10261021
#
10271022
# Common options for DHCP applets
Lines changed: 242 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,242 @@
1+
#!/bin/sh
2+
# DHCPv6 client state update script for odhcp6c
3+
# This script expects a system with resolvconf (openresolv) and iproute2
4+
5+
[ -z "$1" ] && echo "Error: should be called from odhcp6c" && exit 1
6+
7+
interface="$1"
8+
state="$2"
9+
RESOLV_CONF="/run/resolvconf/interfaces/${interface}-ipv6.conf"
10+
NTPFILE="/run/chrony/dhcp-sources.d/${interface}-ipv6.sources"
11+
12+
[ -n "$metric" ] || metric=5
13+
14+
log()
15+
{
16+
logger -I $$ -t odhcp6c -p user.notice "${interface}: $*"
17+
}
18+
19+
dbg()
20+
{
21+
logger -I $$ -t odhcp6c -p user.debug "${interface}: $*"
22+
}
23+
24+
err()
25+
{
26+
logger -I $$ -t odhcp6c -p user.err "${interface}: $*"
27+
}
28+
29+
teardown_interface()
30+
{
31+
ip -6 route flush dev "$interface"
32+
ip -6 address flush dev "$interface" scope global
33+
}
34+
35+
setup_interface()
36+
{
37+
# Merge RA addresses with DHCP addresses
38+
for entry in $RA_ADDRESSES; do
39+
duplicate=0
40+
addr="${entry%%/*}"
41+
for dentry in $ADDRESSES; do
42+
daddr="${dentry%%/*}"
43+
[ "$addr" = "$daddr" ] && duplicate=1
44+
done
45+
[ "$duplicate" = "0" ] && ADDRESSES="$ADDRESSES $entry"
46+
done
47+
48+
# Add addresses
49+
for entry in $ADDRESSES; do
50+
addr="${entry%%,*}"
51+
entry="${entry#*,}"
52+
preferred="${entry%%,*}"
53+
entry="${entry#*,}"
54+
valid="${entry%%,*}"
55+
56+
ip -6 address add "$addr" dev "$interface" preferred_lft "$preferred" valid_lft "$valid" proto dhcp
57+
log "assigned address $addr (preferred=$preferred, valid=$valid)"
58+
done
59+
60+
# Add routes from RA
61+
for entry in $RA_ROUTES; do
62+
addr="${entry%%,*}"
63+
entry="${entry#*,}"
64+
gw="${entry%%,*}"
65+
entry="${entry#*,}"
66+
valid="${entry%%,*}"
67+
entry="${entry#*,}"
68+
metric="${entry%%,*}"
69+
70+
if [ -n "$gw" ]; then
71+
ip -6 route add "$addr" via "$gw" metric "$metric" dev "$interface" from "::/128"
72+
else
73+
ip -6 route add "$addr" metric "$metric" dev "$interface"
74+
fi
75+
76+
# Add routes for delegated prefixes
77+
for prefix in $PREFIXES; do
78+
paddr="${prefix%%,*}"
79+
[ -n "$gw" ] && ip -6 route add "$addr" via "$gw" metric "$metric" dev "$interface" from "$paddr"
80+
done
81+
done
82+
}
83+
84+
handle_prefixes()
85+
{
86+
# $PREFIXES format: "prefix/len,preferred,valid[,class=N][,excluded=...] ..."
87+
for entry in $PREFIXES; do
88+
addr="${entry%%,*}"
89+
entry="${entry#*,}"
90+
preferred="${entry%%,*}"
91+
entry="${entry#*,}"
92+
valid="${entry%%,*}"
93+
94+
log "received delegated prefix $addr (preferred=$preferred, valid=$valid)"
95+
96+
# Add unreachable route to prevent routing loops
97+
ip -6 route add unreachable "$addr" 2>/dev/null
98+
99+
# Future: Distribute to downstream interfaces
100+
done
101+
}
102+
103+
handle_dns()
104+
{
105+
truncate -s 0 "$RESOLV_CONF"
106+
107+
# Combine DHCPv6 DNS ($RDNSS) and RA DNS ($RA_DNS), deduplicating
108+
all_dns=""
109+
for server in $RDNSS $RA_DNS; do
110+
# Simple deduplication: only add if not already in list
111+
case " $all_dns " in
112+
*" $server "*) ;;
113+
*) all_dns="$all_dns $server" ;;
114+
esac
115+
done
116+
117+
# Domain search list (DHCPv6 option 24)
118+
if [ -n "$DOMAINS" ]; then
119+
dbg "adding search domains: $DOMAINS"
120+
echo "search $DOMAINS # $interface" >> "$RESOLV_CONF"
121+
fi
122+
123+
# DNS servers
124+
for server in $all_dns; do
125+
[ -z "$server" ] && continue
126+
dbg "adding dns $server"
127+
echo "nameserver $server # $interface" >> "$RESOLV_CONF"
128+
done
129+
130+
if [ -n "$all_dns" ]; then
131+
resolvconf -u
132+
fi
133+
}
134+
135+
handle_ntp()
136+
{
137+
# DHCPv6 option 56 (NTP server) is provided as $OPTION_56 in hex format
138+
# Format: sub-option-code (2 bytes) + length (2 bytes) + data
139+
# Sub-option 1 = NTP server address (16 bytes IPv6)
140+
#
141+
# This is complex to parse in shell. For now, we attempt basic parsing
142+
# and fall back to logging a warning if the format is unexpected.
143+
144+
if [ -n "$OPTION_56" ]; then
145+
# Remove all non-hex characters (spaces, colons, etc.) and convert to lowercase
146+
hex=$(echo "$OPTION_56" | tr -d '[:space:]:-' | tr '[:upper:]' '[:lower:]')
147+
148+
truncate -s 0 "$NTPFILE"
149+
ntp_found=0
150+
151+
# Parse option 56: iterate through sub-options
152+
# Each sub-option: 2 bytes code + 2 bytes length + data
153+
pos=0
154+
while [ $pos -lt ${#hex} ]; do
155+
# Need at least 4 hex chars (2 bytes) for sub-option code
156+
[ $((${#hex} - pos)) -lt 4 ] && break
157+
158+
# Extract sub-option code (2 bytes = 4 hex chars)
159+
subopt_code=$(echo "$hex" | cut -c $((pos+1))-$((pos+4)))
160+
pos=$((pos + 4))
161+
162+
# Need 4 more hex chars for length
163+
[ $((${#hex} - pos)) -lt 4 ] && break
164+
165+
# Extract length (2 bytes = 4 hex chars)
166+
subopt_len_hex=$(echo "$hex" | cut -c $((pos+1))-$((pos+4)))
167+
subopt_len=$(printf "%d" "0x$subopt_len_hex")
168+
pos=$((pos + 4))
169+
170+
# Sub-option 1 = NTP server address (should be 16 bytes for IPv6)
171+
if [ "$subopt_code" = "0001" ] && [ "$subopt_len" -eq 16 ]; then
172+
# Extract 16 bytes (32 hex chars) for IPv6 address
173+
addr_hex=$(echo "$hex" | cut -c $((pos+1))-$((pos+32)))
174+
175+
# Convert hex to IPv6 address format
176+
# Format: 0123456789abcdef0123456789abcdef -> 0123:4567:89ab:cdef:0123:4567:89ab:cdef
177+
ipv6=$(echo "$addr_hex" | sed 's/\(....\)\(....\)\(....\)\(....\)\(....\)\(....\)\(....\)\(....\)/\1:\2:\3:\4:\5:\6:\7:\8/')
178+
179+
dbg "got NTP server $ipv6"
180+
echo "server $ipv6 iburst" >> "$NTPFILE"
181+
ntp_found=1
182+
fi
183+
184+
# Skip this sub-option's data
185+
pos=$((pos + subopt_len * 2))
186+
done
187+
188+
if [ "$ntp_found" -eq 1 ]; then
189+
chronyc reload sources >/dev/null 2>&1
190+
else
191+
dbg "option 56 received but no NTP server addresses found (consider using option 31/SNTP)"
192+
fi
193+
fi
194+
}
195+
196+
log "state: $state"
197+
198+
(
199+
flock 9
200+
case "$state" in
201+
started)
202+
# Initial state - clean up any stale config
203+
teardown_interface
204+
;;
205+
206+
bound)
207+
# Fresh lease - tear down and set up from scratch
208+
teardown_interface
209+
setup_interface
210+
handle_prefixes
211+
handle_dns
212+
handle_ntp
213+
;;
214+
215+
informed|updated|rebound|ra-updated)
216+
# Update existing configuration
217+
setup_interface
218+
[ -n "$PREFIXES" ] && handle_prefixes
219+
handle_dns
220+
handle_ntp
221+
;;
222+
223+
unbound|stopped)
224+
# Lost server or client stopped
225+
teardown_interface
226+
rm -f "$RESOLV_CONF"
227+
rm -f "$NTPFILE"
228+
resolvconf -u
229+
chronyc reload sources >/dev/null 2>&1
230+
;;
231+
esac
232+
) 9>/tmp/odhcp6c.lock.${interface}
233+
rm -f /tmp/odhcp6c.lock.${interface}
234+
235+
# Run hooks
236+
HOOK_DIR="/usr/libexec/odhcp6c.d"
237+
for hook in "${HOOK_DIR}/"*; do
238+
[ -f "${hook}" -a -x "${hook}" ] || continue
239+
"${hook}" "$interface" "$state"
240+
done
241+
242+
exit 0

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

Lines changed: 0 additions & 111 deletions
This file was deleted.

configs/aarch32_defconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ BR2_LINUX_KERNEL_CUSTOM_CONFIG_FILE="${BR2_EXTERNAL_INFIX_PATH}/board/aarch32/li
3535
BR2_LINUX_KERNEL_INSTALL_TARGET=y
3636
BR2_LINUX_KERNEL_NEEDS_HOST_OPENSSL=y
3737
BR2_PACKAGE_BUSYBOX_CONFIG="${BR2_EXTERNAL_INFIX_PATH}/board/common/busybox_defconfig"
38+
BR2_PACKAGE_ODHCP6C=y
3839
BR2_PACKAGE_STRACE=y
3940
BR2_PACKAGE_STRESS_NG=y
4041
BR2_PACKAGE_JQ=y

configs/aarch32_minimal_defconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ BR2_LINUX_KERNEL_CUSTOM_CONFIG_FILE="${BR2_EXTERNAL_INFIX_PATH}/board/aarch32/li
3535
BR2_LINUX_KERNEL_INSTALL_TARGET=y
3636
BR2_LINUX_KERNEL_NEEDS_HOST_OPENSSL=y
3737
BR2_PACKAGE_BUSYBOX_CONFIG="${BR2_EXTERNAL_INFIX_PATH}/board/common/busybox_defconfig"
38+
BR2_PACKAGE_ODHCP6C=y
3839
BR2_PACKAGE_STRACE=y
3940
BR2_PACKAGE_STRESS_NG=y
4041
BR2_PACKAGE_JQ=y

configs/aarch64_defconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ BR2_LINUX_KERNEL_USE_CUSTOM_CONFIG=y
3232
BR2_LINUX_KERNEL_CUSTOM_CONFIG_FILE="${BR2_EXTERNAL_INFIX_PATH}/board/aarch64/linux_defconfig"
3333
BR2_LINUX_KERNEL_INSTALL_TARGET=y
3434
BR2_PACKAGE_BUSYBOX_CONFIG="${BR2_EXTERNAL_INFIX_PATH}/board/common/busybox_defconfig"
35+
BR2_PACKAGE_ODHCP6C=y
3536
BR2_PACKAGE_STRACE=y
3637
BR2_PACKAGE_STRESS_NG=y
3738
BR2_PACKAGE_JQ=y

0 commit comments

Comments
 (0)