Skip to content

Commit 4b55e38

Browse files
committed
board/aarch64: use %m modifier in default xPi hostnames
The xPi's usually don't have a VPD so the chassis mac-address probed at boot is usually null in /run/system.json. This commit adds a fallbkack mechanism to populate this field so it can be used for unique hostnames even on these boards. Ths ietf-hardware.yang model does not have a notion of physical address, so we augment one tht is generic enought to be used for other hardware components than Ethernet, similar to what ietf-interfaces.yang use. Signed-off-by: Joachim Wiberg <[email protected]>
1 parent c66c0b4 commit 4b55e38

File tree

10 files changed

+67
-11
lines changed

10 files changed

+67
-11
lines changed

board/aarch64/bananapi-bpi-r3/rootfs/usr/share/product/bananapi,bpi-r3/etc/factory-config.cfg

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@
199199
}
200200
},
201201
"ietf-system:system": {
202-
"hostname": "bpi-r3",
202+
"hostname": "bpi-%m",
203203
"ntp": {
204204
"server": [
205205
{

board/aarch64/friendlyarm-nanopi-r2s/rootfs/usr/share/product/friendlyarm,nanopi-r2s/etc/factory-config.cfg

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@
150150
}
151151
},
152152
"ietf-system:system": {
153-
"hostname": "r2s",
153+
"hostname": "r2s-%m",
154154
"ntp": {
155155
"server": [
156156
{

board/aarch64/raspberrypi-rpi64/rootfs/usr/share/product/raspberrypi,4-model-b/etc/factory-config.cfg

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@
133133
}
134134
},
135135
"ietf-system:system": {
136-
"hostname": "rpi",
136+
"hostname": "rpi-%m",
137137
"ntp": {
138138
"enabled": true,
139139
"server": [

board/common/rootfs/usr/libexec/infix/init.d/00-probe

Lines changed: 42 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -277,8 +277,12 @@ def vpd_inject(out, vpds):
277277
break
278278

279279

280-
def qemu_base_mac():
281-
"""Find MAC address of first non-loopback interface, subtract with 1"""
280+
def fallback_base_mac():
281+
"""Find MAC address of first suitable non-loopback interface.
282+
283+
Prefers real (globally unique) MACs over locally administered ones.
284+
Prioritizes interface types: eth* > wan > wifi* > others.
285+
"""
282286
base_path = '/sys/class/net'
283287
interfaces = []
284288

@@ -290,13 +294,41 @@ def qemu_base_mac():
290294
fn = os.path.join(base_path, iface, 'address')
291295
with open(fn, 'r', encoding='ascii') as f:
292296
mac = f.read().strip()
293-
interfaces.append((mac, iface))
297+
298+
# Check if locally administered (bit 1 of first octet is set)
299+
first_byte = int(mac.split(':')[0], 16)
300+
is_local = bool(first_byte & 0x02)
301+
302+
# Prefer: eth* > wan > wifi* > others, then real MACs > local MACs
303+
priority = 0
304+
if iface.startswith('eth'):
305+
priority = 400
306+
elif iface == 'wan':
307+
priority = 300
308+
elif iface.startswith('wifi'):
309+
priority = 200
310+
else:
311+
priority = 100
312+
313+
# Real MACs get +100 bonus
314+
if not is_local:
315+
priority += 100
316+
317+
interfaces.append((priority, iface, mac))
294318
except FileNotFoundError:
295319
continue
296320

297321
if interfaces:
298-
interfaces.sort()
299-
mac = interfaces[0][0]
322+
interfaces.sort(reverse=True) # Highest priority first
323+
return interfaces[0][2] # Return MAC
324+
325+
return None
326+
327+
328+
def qemu_base_mac():
329+
"""Find MAC address of first non-loopback interface, subtract with 1"""
330+
mac = fallback_base_mac()
331+
if mac:
300332
mac = int(mac.replace(':', ''), 16)
301333
mac -= 1
302334
mac %= 1 << 48
@@ -386,6 +418,11 @@ def probe_dtsystem(out):
386418
out["factory-password-hash"] = staticpw
387419

388420
vpd_inject(out, vpds)
421+
422+
# Fallback to interface MAC if VPD doesn't provide one (e.g., SBCs)
423+
if not out["mac-address"]:
424+
out["mac-address"] = fallback_base_mac()
425+
389426
return 0
390427

391428

src/confd/yang/confd.inc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ MODULES=(
1818
1919
2020
"[email protected] -e hardware-state -e hardware-sensor"
21-
"infix-hardware@2025-10-18.yang"
21+
"infix-hardware@2025-10-30.yang"
2222
2323
2424

src/confd/yang/confd/infix-hardware.yang

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ module infix-hardware {
1717
contact "[email protected]";
1818
description "Vital Product Data augmentation of ieee-hardware and deviations.";
1919

20-
revision 2025-10-18 {
21-
description "Enable sensor support, starting with hwmon temperature sensors.";
20+
revision 2025-10-30 {
21+
description "Add phys-address leaf for hardware components and enable sensor support.";
2222
reference "internal";
2323
}
2424
revision 2024-04-25 {
@@ -72,6 +72,18 @@ module infix-hardware {
7272
deviate not-supported;
7373
}
7474
augment "/iehw:hardware/iehw:component" {
75+
leaf phys-address {
76+
type yang:phys-address;
77+
config false;
78+
description
79+
"The physical address of the hardware component. For chassis components,
80+
this represents the base MAC address used for the system. This is
81+
typically sourced from VPD data on enterprise hardware, or derived from
82+
the first physical interface on single-board computers (SBCs) without
83+
VPD. May be used as the base for generating addresses for virtual
84+
interfaces. For other component types, this could represent various
85+
physical layer addresses (e.g., Fibre Channel WWN, InfiniBand GUID).";
86+
}
7587
container vpd-data {
7688
config false;
7789
leaf product-name {

src/statd/python/cli_pretty/cli_pretty.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1703,6 +1703,8 @@ def show_hardware(json):
17031703
print(f"Manufacturer : {board['mfg-name']}")
17041704
if board.get("serial-num"):
17051705
print(f"Serial Number : {board['serial-num']}")
1706+
if board.get("infix-hardware:phys-address"):
1707+
print(f"Base MAC Address : {board['infix-hardware:phys-address']}")
17061708
if board.get("hardware-rev"):
17071709
print(f"Hardware Revision : {board['hardware-rev']}")
17081710

src/statd/python/yanger/ietf_hardware.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,10 @@ def motherboard_component(systemjson):
9999
if systemjson.get("part-number"):
100100
component["hardware-rev"] = systemjson["part-number"]
101101

102+
# Add chassis physical address (MAC) if available (from VPD or interface fallback)
103+
if systemjson.get("mac-address"):
104+
component["infix-hardware:phys-address"] = systemjson["mac-address"]
105+
102106
# Set state - admin-state is "unknown" since chassis cannot be
103107
# administratively controlled (locked/unlocked)
104108
component["state"] = {

test/case/statd/system/ietf-hardware.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
"class": "iana-hardware:chassis",
77
"mfg-name": "QEMU",
88
"model-name": "VM",
9+
"infix-hardware:phys-address": "00:a0:85:00:03:00",
910
"state": {
1011
"admin-state": "unknown",
1112
"oper-state": "enabled"

0 commit comments

Comments
 (0)