1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
|
# network — Bring up loopback, physical interfaces, routes (BSD-pivot v2)
#
# Reads /etc/hostname.<if> for each physical interface (line-oriented
# format documented in start.sh) and /etc/defaultrouter for static
# default routes. DHCP is handled via `ifconfig <if> dhcp start`, which
# forks /sbin/dhcpagent directly.
#
# Requires both dlmgmtd and ipmgmtd: modern illumos `ifconfig <link> plumb`
# resolves link names through dlmgmtd, and `ifconfig <link> inet ADDR up`
# goes through libipadm → ipmgmtd. `dladm init-phys` (in start.sh)
# registers physical links with dlmgmtd at boot.
#
# Replaces the v1 SMF stack (network/loopback, network/physical,
# network/initial, network/netmask, network/service, network/routing-setup)
# with a single oneshot. inetd / fmd / rpcbind remain dropped.
[service]
name = "network"
description = "Bring up network interfaces and routes (BSD-style)"
type = "oneshot"
[exec]
start = "/etc/zyginit/services/network/start.sh"
stop = "/etc/zyginit/services/network/stop.sh"
[dependencies]
requires = ["identity", "dlmgmtd", "ipmgmtd"]
[restart]
on = "never"
#!/bin/zsh
# network/start.sh — BSD-style interface/route bring-up (v2)
#
# No ipmgmtd, no inetd. Uses /sbin/ifconfig directly for plumbing,
# /sbin/dhcpagent (forked by ifconfig dhcp start) for DHCP, and
# /usr/sbin/route for static routes from /etc/defaultrouter. dlmgmtd is
# required: modern illumos `ifconfig <link> plumb` resolves names through
# it, so we keep it but skip the rest of the SMF networking stack.
#
# Walks /etc/hostname.<if> files (one per interface to configure). Each
# file is line-oriented; see the case statement below for supported syntax.
set -e
set -u
# Enable [[:space:]]## "one or more" repetition in pattern matching.
setopt EXTENDED_GLOB
IFCONFIG=/sbin/ifconfig
DLADM=/sbin/dladm
ROUTE=/usr/sbin/route
# --- Force-load IP STREAMS modules ---
# zyginit's tier ordering runs network/start.sh before SMF's traditional
# autoload triggers fire, so socket(PF_INET) returns EAFNOSUPPORT until
# the ip / ip6 drivers are explicitly loaded. modload is idempotent:
# returns success on already-loaded modules.
/sbin/modload /kernel/drv/ip || true
/sbin/modload /kernel/drv/ip6 || true
# --- Datalink registration ---
# Tell dlmgmtd about every physical link the kernel attached after dlmgmtd
# started (e.g. virtio NICs that came up via devfsadm). Without this,
# `ifconfig vioif0 plumb` returns "no such interface" because dlmgmtd
# can't translate the link name to a DLPI device. Mirrors what SMF's
# network/physical method runs.
$DLADM init-phys || true
# --- Loopback ---
# Plumb lo0 explicitly (the kernel auto-attaches it but we want predictable
# state). `ifconfig lo0 plumb` is idempotent — repeats just succeed.
$IFCONFIG lo0 plumb || true
$IFCONFIG lo0 inet 127.0.0.1 netmask 255.0.0.0 up || true
$IFCONFIG lo0 inet6 ::1/128 up || true
# --- Physical interfaces ---
# Each /etc/hostname.<if> file marks an interface to configure (BSD style).
# The configure_interface() helper plumbs the named link and applies the
# directives in the file.
configure_interface() {
local iface="$1"
local cfgfile="/etc/hostname.${iface}"
if [[ ! -e "$cfgfile" ]]; then
# No config — leave the interface untouched. (Operator can plumb
# by-hand or add a config file and re-run; this matches OpenBSD.)
return 0
fi
# Plumb first; downstream lines may add addresses to it.
$IFCONFIG "$iface" plumb || true
# If the config file has no non-comment content, just bring it up.
if ! grep -Eq '^[[:space:]]*[^#[:space:]]' "$cfgfile" 2>/dev/null; then
$IFCONFIG "$iface" up
return 0
fi
# Walk the file line by line.
while IFS= read -r line || [[ -n "$line" ]]; do
# Strip comments, then leading and trailing whitespace.
# `[[:space:]]##` is zsh extended glob for "one or more whitespace
# chars" — covers tab-indented lines and multi-space margins that
# an editor might insert. EXTENDED_GLOB is enabled at script top;
# the `\#` escape is needed because EXTENDED_GLOB makes bare `#` a
# pattern operator.
line="${line%%\#*}"
line="${line##[[:space:]]##}"
line="${line%%[[:space:]]##}"
[[ -z "$line" ]] && continue
# Split on first whitespace into verb + rest.
local verb="${line%%[[:space:]]*}"
local rest=""
if [[ "$line" == *[[:space:]]* ]]; then
rest="${line#*[[:space:]]}"
fi
# ${=rest} forces word-splitting on $rest — necessary because
# ifconfig args like `192.168.122.50/24 up` need to arrive as
# separate arguments, not one quoted string.
case "$verb" in
dhcp)
$IFCONFIG "$iface" dhcp start
;;
inet)
# `inet <addr>/<prefix>` or `inet <addr> netmask <mask>`
$IFCONFIG "$iface" inet ${=rest} up
;;
addif)
$IFCONFIG "$iface" addif ${=rest} up
;;
inet6)
$IFCONFIG "$iface" inet6 ${=rest} up
;;
up|down)
$IFCONFIG "$iface" "$verb"
;;
*)
print -u2 "network: ignoring unknown directive in $cfgfile: $line"
;;
esac
done < "$cfgfile"
}
# Iterate /etc/hostname.<if> files — the BSD-style marker for "configure
# this interface." Iter 7 boot showed /dev/net/ enumeration silently
# skipped vioif0 on Hammerhead (the directory either doesn't exist or
# doesn't contain DLPI link nodes for virtio NICs); switching to
# hostname.* lookup is both more portable and closer to OpenBSD's
# original semantics.
for cfg in /etc/hostname.*(N); do
fname="${cfg:t}"
# ${cfg:t} returns "hostname.vioif0"; strip the prefix.
iface="${fname#hostname.}"
# Defensively skip loopback (we already handled lo0 above).
[[ "$iface" == "lo0" ]] && continue
configure_interface "$iface"
done
# --- Static default routes ---
# /etc/defaultrouter is one IPv4 address per line. DHCP-acquired routes
# don't need this file; only set one when the operator opts in.
if [[ -r /etc/defaultrouter ]]; then
while IFS= read -r line || [[ -n "$line" ]]; do
# Same trim treatment as the hostname.<if> parser. `\#` is
# escaped because EXTENDED_GLOB is set script-wide.
line="${line%%\#*}"
line="${line##[[:space:]]##}"
line="${line%%[[:space:]]##}"
[[ -z "$line" ]] && continue
$ROUTE -n add default "$line" || true
done < /etc/defaultrouter
fi
exit 0
#!/bin/zsh
# network/stop.sh — reverse of start.sh (BSD-pivot v2)
#
# Releases DHCP leases and unplumbs all configured non-loopback interfaces.
# Best-effort: each command may fail (e.g., interface already torn down,
# dhcpagent already gone) and we continue regardless. ZFS sync happens
# in main.reef after this runs.
set -u
# NOTE: no `set -e` — every step is best-effort during shutdown.
IFCONFIG=/sbin/ifconfig
# Release any DHCP leases first so the server marks them free.
if [[ -d /dev/net ]]; then
for dev in /dev/net/*(N); do
link="${dev:t}"
[[ "$link" == "lo0" ]] && continue
# `dhcp release` is idempotent — silently no-op if no lease.
$IFCONFIG "$link" dhcp release 2>/dev/null || true
done
fi
# Kill the dhcpagent itself. It manages all leases globally, so one kill
# tears down every interface's state at once.
pkill -TERM -x dhcpagent 2>/dev/null || true
# Unplumb. inet6 first (Hammerhead requires this ordering for clean teardown).
if [[ -d /dev/net ]]; then
for dev in /dev/net/*(N); do
link="${dev:t}"
[[ "$link" == "lo0" ]] && continue
$IFCONFIG "$link" inet6 unplumb 2>/dev/null || true
$IFCONFIG "$link" unplumb 2>/dev/null || true
done
fi
exit 0
|