banger/internal/guestnet/assets/bootstrap.sh
Thales Maciel 14d8563f3c
Stop using kernel IP autoconfig for runtime VMs
Avoid the Alpine boot stall caused by kernel ip= autoconfig running before
virtio_net is available.

Split runtime and image-build boot args so managed VMs boot without kernel
network autoconfig, inject a static guest network config plus bootstrap
script into the runtime overlay, and keep image builds on the old path for
compatibility with existing base images.

Preserve executable bits when patching guest files into ext4 images and add
coverage for the new boot-arg split and guest network config generation.

Validated with go test ./..., a rebuilt Alpine image, and a fresh alp-fast
create/ssh check that brought vm.start down to about 2.7s.
2026-03-21 21:54:18 -03:00

163 lines
2.9 KiB
Bash

#!/bin/sh
set -eu
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
CONFIG_PATH="/etc/banger-network.conf"
if ! command -v ip >/dev/null 2>&1; then
exit 0
fi
guest_ip=""
gateway_ip=""
netmask=""
iface_hint=""
dns1=""
dns2=""
load_file_config() {
if [ ! -r "$CONFIG_PATH" ]; then
return 1
fi
# shellcheck disable=SC1090
. "$CONFIG_PATH"
guest_ip="${GUEST_IP:-}"
gateway_ip="${GATEWAY_IP:-}"
netmask="${NETMASK:-}"
iface_hint="${INTERFACE:-}"
dns1="${DNS1:-}"
dns2="${DNS2:-}"
[ -n "$guest_ip" ]
}
load_cmdline_config() {
cmdline="$(cat /proc/cmdline 2>/dev/null || true)"
ip_arg=""
for arg in $cmdline; do
case "$arg" in
ip=*)
ip_arg="${arg#ip=}"
break
;;
esac
done
if [ -z "$ip_arg" ]; then
return 1
fi
guest_ip="$(field 1)"
gateway_ip="$(field 3)"
netmask="$(field 4)"
iface_hint="$(field 6)"
dns1="$(field 8)"
dns2="$(field 9)"
[ -n "$guest_ip" ]
}
field() {
printf '%s' "$ip_arg" | cut -d: -f"$1"
}
mask_to_prefix() {
case "$1" in
[0-9]|[1-2][0-9]|3[0-2])
printf '%s\n' "$1"
return 0
;;
esac
prefix=0
old_ifs=$IFS
IFS=.
set -- $1
IFS=$old_ifs
if [ "$#" -ne 4 ]; then
return 1
fi
for octet in "$@"; do
case "$octet" in
255) prefix=$((prefix + 8)) ;;
254) prefix=$((prefix + 7)) ;;
252) prefix=$((prefix + 6)) ;;
248) prefix=$((prefix + 5)) ;;
240) prefix=$((prefix + 4)) ;;
224) prefix=$((prefix + 3)) ;;
192) prefix=$((prefix + 2)) ;;
128) prefix=$((prefix + 1)) ;;
0) ;;
*) return 1 ;;
esac
done
printf '%s\n' "$prefix"
}
find_iface() {
hint="$1"
if [ -n "$hint" ] && [ -d "/sys/class/net/$hint" ]; then
printf '%s\n' "$hint"
return 0
fi
for path in /sys/class/net/*; do
[ -e "$path" ] || continue
iface="${path##*/}"
if [ "$iface" = "lo" ]; then
continue
fi
printf '%s\n' "$iface"
return 0
done
return 1
}
if ! load_file_config; then
load_cmdline_config || exit 0
fi
if [ -z "$guest_ip" ]; then
exit 0
fi
iface=""
attempt=0
while [ "$attempt" -lt 50 ]; do
iface="$(find_iface "$iface_hint" || true)"
if [ -n "$iface" ]; then
break
fi
attempt=$((attempt + 1))
sleep 0.2
done
if [ -z "$iface" ]; then
exit 0
fi
prefix="$(mask_to_prefix "$netmask" || printf '24\n')"
ip link set "$iface" up
ip addr replace "$guest_ip/$prefix" dev "$iface"
if [ -n "$gateway_ip" ]; then
ip route replace default via "$gateway_ip" dev "$iface"
fi
if [ -n "$dns1" ] || [ -n "$dns2" ]; then
tmp_resolv="/tmp/.banger-resolv.conf.$$"
: > "$tmp_resolv"
if [ -n "$dns1" ]; then
printf 'nameserver %s\n' "$dns1" >> "$tmp_resolv"
fi
if [ -n "$dns2" ]; then
printf 'nameserver %s\n' "$dns2" >> "$tmp_resolv"
fi
if [ -s "$tmp_resolv" ]; then
cat "$tmp_resolv" > /etc/resolv.conf
fi
rm -f "$tmp_resolv"
fi