@@ -209,20 +209,75 @@ configure_network() {
209209 # For lxc the network is configured from outside
210210 return
211211 fi
212+
212213 # Gather all non-lo, non-docker0 interfaces and sort them alphabetically
213214 shopt -s nullglob
214- local interfaces=()
215+ declare -a interfaces=()
216+ declare -A interfaces_mac=()
217+
215218 # only ethernet interfaces, so not lo, docker0, wlan, bonds, br, veth, etc.
216219 for interface in /sys/class/net/e* ; do
217220 local name
218221 name=" $( basename " $interface " ) "
219222 interfaces+=(" $name " )
223+ if [[ -r " $interface /address" ]]; then
224+ mac=$( < " $interface /address" )
225+ interfaces_mac[" $name " ]=$( echo " $mac " | tr ' [:upper:]' ' [:lower:]' )
226+ else
227+ interfaces_mac[" $name " ]=" "
228+ fi
220229 done
221230
222231 # Read network config array from system.json using jq_config
223232 local configs_length
224233 configs_length=" $( jq_config ' .network | length' ) "
225234
235+ declare -A used_json=() # json_index → 1 when already assigned
236+ declare -A iface_to_json=() # interface name → json_index
237+ declare -a remaining_json=() # indices that are still free (preserve order)
238+ declare -a pending_ifaces=() # interfaces that did NOT match name/MAC
239+
240+ for (( i= 0 ; i< ${# interfaces[@]} ; i++ )) ; do
241+ local iface=" ${interfaces[$i]} "
242+ local match_idx=-1
243+
244+ for (( j= 0 ; j< configs_length; j++ )) ; do
245+ (( used_json[$j ] )) && continue # already taken
246+
247+ local entry entry_name entry_mac
248+ entry=$( jq_config -r " .network[$j ]" )
249+ entry_name=$( echo " $entry " | jq -r ' .name // empty' )
250+ entry_mac=$( echo " $entry " | jq -r ' ."mac-address" // empty' | tr ' [:upper:]' ' [:lower:]' )
251+
252+ if [[ -n " $entry_name " && " $entry_name " == " $iface " ]]; then
253+ match_idx=$j ; break
254+ elif [[ -n " $entry_mac " && " $entry_mac " == " ${interfaces_mac[$iface]} " ]]; then
255+ match_idx=$j ; break
256+ fi
257+ done
258+
259+ if (( match_idx != - 1 )) ; then
260+ used_json[$match_idx ]=1
261+ # store the association for later stanza generation
262+ iface_to_json[" $iface " ]=$match_idx
263+ else
264+ # remember that this iface still needs a config (positional fallback)
265+ pending_ifaces+=(" $iface " )
266+ fi
267+ done
268+
269+ # collect the indices that were not used
270+ for (( j= 0 ; j< configs_length; j++ )) ; do
271+ (( used_json[$j ] )) || remaining_json+=(" $j " )
272+ done
273+
274+ # assign them in order
275+ for (( k= 0 ; k< ${# pending_ifaces[@]} ; k++ )) ; do
276+ local iface=" ${pending_ifaces[$k]} "
277+ local idx=" ${remaining_json[$k]:- } " # may be empty if JSON shorter
278+ (( idx != "" )) && iface_to_json[" $iface " ]=$idx
279+ done
280+
226281 {
227282 echo " auto lo"
228283 echo " iface lo inet loopback"
@@ -231,88 +286,92 @@ configure_network() {
231286 # Configure each interface with corresponding config if present
232287 for (( i= 0 ; i< ${# interfaces[@]} ; i++ )) ; do
233288 local IFACE=" ${interfaces[$i]} "
234- local METRIC
235- METRIC=$(( 100 * (i+ 1 )) )
236- if [ " $i " -lt " $configs_length " ]; then
237- # Extract config for this interface
238- local config
239- config=$( jq_config -r " .network[$i ]" )
240- local DHCP
241- DHCP=$( echo " $config " | jq -r ' .dhcp // empty' )
242- local IP
243- IP=$( echo " $config " | jq -r ' ."ip-address" // empty' )
244- local MASK
245- MASK=$( echo " $config " | jq -r ' ."network-mask" // empty' )
246- local GW
247- GW=$( echo " $config " | jq -r ' .gateway // empty' )
248- local DNS
249- DNS=$( echo " $config " | jq -r '
250- if (."dns-server" | type == "array") then
251- ."dns-server" | join(" ")
252- else
253- ."dns-server" // empty
254- end' )
255- local NTP
256- NTP=$( echo " $config " | jq -r '
257- if (."ntp-server" | type == "array") then
258- ."ntp-server" | join(" ")
259- else
260- ."ntp-server" // empty
261- end' )
262- if [ -z " $DHCP " ] && [ -z " $IP " ] && [ -z " $MASK " ]; then
263- # No config for this interface, skip it
264- echo " iface $IFACE inet manual"
265- echo
266- continue
267- fi
268- if [ " $DHCP " = " true" ]; then
269- # DHCP configuration with metric
289+ local METRIC=$(( 100 * (i+ 1 )) )
290+
291+ local json_idx=" ${iface_to_json[$iface]:- } "
292+
293+ # No JSON entry at all → original defaults (first iface DHCP, rest manual)
294+ if [[ -z " $json_idx " ]]; then
295+ if [[ " $IFACE " == " ${interfaces[0]} " ]]; then
270296 echo " auto $IFACE "
271297 echo " iface $IFACE inet dhcp"
272298 echo " metric $METRIC "
273299 echo
274300 else
275- # Static configuration
276- echo " auto $IFACE "
277- echo " iface $IFACE inet static"
278- [ -n " $IP " ] && echo " address $IP "
279- [ -n " $MASK " ] && echo " netmask $MASK "
280- [ -n " $GW " ] && echo " gateway $GW "
281- [ -n " $DNS " ] && echo " dns-nameservers $DNS "
282- [ -n " $NTP " ] && echo " ntp-servers $NTP "
301+ echo " iface $IFACE inet manual"
283302 echo
284303 fi
285- # Static routes
286- if echo " $config " | jq -e ' has("routes")' > /dev/null 2>&1 ; then
287- echo " $config " | jq -r '
288- .routes // empty |
289- (if type=="array" then .[] else . end) |
290- (.network // "") as $d |
291- (.gateway // "") as $g |
292- (.netmask // "") as $m |
293- [$d, $g, $m] | @tsv' |
294- while IFS=$' \t ' read -r RDEST RGW RMASK; do
295- if [ -z " $RDEST " ] || [ -z " $RGW " ]; then
296- echo " IGNORE: invalid route with empty destination or gateway" >&2
297- continue
298- fi
299- PREFIX=$( mask_to_prefix " $RMASK " )
300- RDEST=" $RDEST ${PREFIX: +" /$PREFIX " } "
301- echo " up ip route add $RDEST via $RGW dev $IFACE "
302- echo " down ip route del $RDEST via $RGW dev $IFACE "
303- done
304- fi
305- elif [ " $i " -eq 0 ]; then
306- # No config for the first interface, set to DHCP with metric
304+ continue
305+ fi
306+
307+ # Extract config for this interface
308+ local config
309+ config=$( jq_config -r " .network[$json_idx ]" )
310+ local DHCP
311+ DHCP=$( echo " $config " | jq -r ' .dhcp // empty' )
312+ local IP
313+ IP=$( echo " $config " | jq -r ' ."ip-address" // empty' )
314+ local MASK
315+ MASK=$( echo " $config " | jq -r ' ."network-mask" // empty' )
316+ local GW
317+ GW=$( echo " $config " | jq -r ' .gateway // empty' )
318+ local DNS
319+ DNS=$( echo " $config " | jq -r '
320+ if (."dns-server" | type == "array") then
321+ ."dns-server" | join(" ")
322+ else
323+ ."dns-server" // empty
324+ end' )
325+ local NTP
326+ NTP=$( echo " $config " | jq -r '
327+ if (."ntp-server" | type == "array") then
328+ ."ntp-server" | join(" ")
329+ else
330+ ."ntp-server" // empty
331+ end' )
332+ if [ -z " $DHCP " ] && [ -z " $IP " ] && [ -z " $MASK " ]; then
333+ # No config for this interface, skip it
334+ echo " iface $IFACE inet manual"
335+ echo
336+ continue
337+ fi
338+ if [ " $DHCP " = " true" ]; then
339+ # DHCP configuration with metric
307340 echo " auto $IFACE "
308341 echo " iface $IFACE inet dhcp"
309342 echo " metric $METRIC "
310343 echo
311344 else
312- # No config for this interface, set to manual
313- echo " iface $IFACE inet manual"
345+ # Static configuration
346+ echo " auto $IFACE "
347+ echo " iface $IFACE inet static"
348+ [ -n " $IP " ] && echo " address $IP "
349+ [ -n " $MASK " ] && echo " netmask $MASK "
350+ [ -n " $GW " ] && echo " gateway $GW "
351+ [ -n " $DNS " ] && echo " dns-nameservers $DNS "
352+ [ -n " $NTP " ] && echo " ntp-servers $NTP "
314353 echo
315354 fi
355+ # Static routes
356+ if echo " $config " | jq -e ' has("routes")' > /dev/null 2>&1 ; then
357+ echo " $config " | jq -r '
358+ .routes // empty |
359+ (if type=="array" then .[] else . end) |
360+ (.network // "") as $d |
361+ (.gateway // "") as $g |
362+ (.netmask // "") as $m |
363+ [$d, $g, $m] | @tsv' |
364+ while IFS=$' \t ' read -r RDEST RGW RMASK; do
365+ if [ -z " $RDEST " ] || [ -z " $RGW " ]; then
366+ echo " IGNORE: invalid route with empty destination or gateway" >&2
367+ continue
368+ fi
369+ PREFIX=$( mask_to_prefix " $RMASK " )
370+ RDEST=" $RDEST ${PREFIX: +" /$PREFIX " } "
371+ echo " up ip route add $RDEST via $RGW dev $IFACE "
372+ echo " down ip route del $RDEST via $RGW dev $IFACE "
373+ done
374+ fi
316375 done
317376 } > " ${interfaces_file} .new"
318377
0 commit comments