mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-04-04 14:29:45 +00:00
Merge branch 'bugfix/alpha.20' of github.com:Start9Labs/start-os into bugfix/alpha.20
This commit is contained in:
@@ -1,7 +1,6 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
MAX_IMG_LEN=$((4 * 1024 * 1024 * 1024)) # 4GB
|
|
||||||
|
|
||||||
echo "==== StartOS Image Build ===="
|
echo "==== StartOS Image Build ===="
|
||||||
|
|
||||||
@@ -332,10 +331,10 @@ fi
|
|||||||
|
|
||||||
if [ "${IB_TARGET_PLATFORM}" = "raspberrypi" ]; then
|
if [ "${IB_TARGET_PLATFORM}" = "raspberrypi" ]; then
|
||||||
ln -sf /usr/bin/pi-beep /usr/local/bin/beep
|
ln -sf /usr/bin/pi-beep /usr/local/bin/beep
|
||||||
sh /boot/config.sh > /boot/config.txt
|
sh /boot/firmware/config.sh > /boot/firmware/config.txt
|
||||||
mkinitramfs -c gzip -o /boot/initrd.img-${RPI_KERNEL_VERSION}-rpi-v8 ${RPI_KERNEL_VERSION}-rpi-v8
|
mkinitramfs -c gzip -o /boot/initrd.img-${RPI_KERNEL_VERSION}-rpi-v8 ${RPI_KERNEL_VERSION}-rpi-v8
|
||||||
mkinitramfs -c gzip -o /boot/initrd.img-${RPI_KERNEL_VERSION}-rpi-2712 ${RPI_KERNEL_VERSION}-rpi-2712
|
mkinitramfs -c gzip -o /boot/initrd.img-${RPI_KERNEL_VERSION}-rpi-2712 ${RPI_KERNEL_VERSION}-rpi-2712
|
||||||
cp /usr/lib/u-boot/rpi_arm64/u-boot.bin /boot/u-boot.bin
|
cp /usr/lib/u-boot/rpi_arm64/u-boot.bin /boot/firmware/u-boot.bin
|
||||||
fi
|
fi
|
||||||
|
|
||||||
useradd --shell /bin/bash -G startos -m start9
|
useradd --shell /bin/bash -G startos -m start9
|
||||||
@@ -411,7 +410,16 @@ elif [ "${IMAGE_TYPE}" = img ]; then
|
|||||||
BOOT_LEN=$((2 * 1024 * 1024 * 1024))
|
BOOT_LEN=$((2 * 1024 * 1024 * 1024))
|
||||||
BOOT_END=$((BOOT_START + BOOT_LEN - 1))
|
BOOT_END=$((BOOT_START + BOOT_LEN - 1))
|
||||||
ROOT_START=$((BOOT_END + 1))
|
ROOT_START=$((BOOT_END + 1))
|
||||||
ROOT_LEN=$((MAX_IMG_LEN - ROOT_START))
|
|
||||||
|
# Size root partition to fit the squashfs + 256MB overhead for btrfs
|
||||||
|
# metadata and config overlay, avoiding the need for btrfs resize
|
||||||
|
SQUASHFS_SIZE=$(stat -c %s $prep_results_dir/binary/live/filesystem.squashfs)
|
||||||
|
ROOT_LEN=$(( SQUASHFS_SIZE + 256 * 1024 * 1024 ))
|
||||||
|
# Align to sector boundary
|
||||||
|
ROOT_LEN=$(( (ROOT_LEN + SECTOR_LEN - 1) / SECTOR_LEN * SECTOR_LEN ))
|
||||||
|
|
||||||
|
# Total image: partitions + GPT backup header (34 sectors)
|
||||||
|
IMG_LEN=$((ROOT_START + ROOT_LEN + 34 * SECTOR_LEN))
|
||||||
|
|
||||||
# Fixed GPT partition UUIDs (deterministic, based on old MBR disk ID cb15ae4d)
|
# Fixed GPT partition UUIDs (deterministic, based on old MBR disk ID cb15ae4d)
|
||||||
FW_UUID=cb15ae4d-0001-4000-8000-000000000001
|
FW_UUID=cb15ae4d-0001-4000-8000-000000000001
|
||||||
@@ -420,7 +428,7 @@ elif [ "${IMAGE_TYPE}" = img ]; then
|
|||||||
ROOT_UUID=cb15ae4d-0004-4000-8000-000000000004
|
ROOT_UUID=cb15ae4d-0004-4000-8000-000000000004
|
||||||
|
|
||||||
TARGET_NAME=$prep_results_dir/${IMAGE_BASENAME}.img
|
TARGET_NAME=$prep_results_dir/${IMAGE_BASENAME}.img
|
||||||
truncate -s $MAX_IMG_LEN $TARGET_NAME
|
truncate -s $IMG_LEN $TARGET_NAME
|
||||||
|
|
||||||
sfdisk $TARGET_NAME <<-EOF
|
sfdisk $TARGET_NAME <<-EOF
|
||||||
label: gpt
|
label: gpt
|
||||||
@@ -431,10 +439,23 @@ elif [ "${IMAGE_TYPE}" = img ]; then
|
|||||||
${TARGET_NAME}4 : start=$((ROOT_START / SECTOR_LEN)), size=$((ROOT_LEN / SECTOR_LEN)), type=B921B045-1DF0-41C3-AF44-4C6F280D3FAE, uuid=${ROOT_UUID}, name="root"
|
${TARGET_NAME}4 : start=$((ROOT_START / SECTOR_LEN)), size=$((ROOT_LEN / SECTOR_LEN)), type=B921B045-1DF0-41C3-AF44-4C6F280D3FAE, uuid=${ROOT_UUID}, name="root"
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
FW_DEV=$(losetup --show -f --offset $FW_START --sizelimit $FW_LEN $TARGET_NAME)
|
# Create named loop device nodes (high minor numbers to avoid conflicts)
|
||||||
ESP_DEV=$(losetup --show -f --offset $ESP_START --sizelimit $ESP_LEN $TARGET_NAME)
|
# and detach any stale ones from previous failed builds
|
||||||
BOOT_DEV=$(losetup --show -f --offset $BOOT_START --sizelimit $BOOT_LEN $TARGET_NAME)
|
FW_DEV=/dev/startos-loop-fw
|
||||||
ROOT_DEV=$(losetup --show -f --offset $ROOT_START --sizelimit $ROOT_LEN $TARGET_NAME)
|
ESP_DEV=/dev/startos-loop-esp
|
||||||
|
BOOT_DEV=/dev/startos-loop-boot
|
||||||
|
ROOT_DEV=/dev/startos-loop-root
|
||||||
|
for dev in $FW_DEV:200 $ESP_DEV:201 $BOOT_DEV:202 $ROOT_DEV:203; do
|
||||||
|
name=${dev%:*}
|
||||||
|
minor=${dev#*:}
|
||||||
|
[ -e $name ] || mknod $name b 7 $minor
|
||||||
|
losetup -d $name 2>/dev/null || true
|
||||||
|
done
|
||||||
|
|
||||||
|
losetup $FW_DEV --offset $FW_START --sizelimit $FW_LEN $TARGET_NAME
|
||||||
|
losetup $ESP_DEV --offset $ESP_START --sizelimit $ESP_LEN $TARGET_NAME
|
||||||
|
losetup $BOOT_DEV --offset $BOOT_START --sizelimit $BOOT_LEN $TARGET_NAME
|
||||||
|
losetup $ROOT_DEV --offset $ROOT_START --sizelimit $ROOT_LEN $TARGET_NAME
|
||||||
|
|
||||||
mkfs.vfat -F32 -n firmware $FW_DEV
|
mkfs.vfat -F32 -n firmware $FW_DEV
|
||||||
mkfs.vfat -F32 -n efi $ESP_DEV
|
mkfs.vfat -F32 -n efi $ESP_DEV
|
||||||
@@ -447,18 +468,16 @@ elif [ "${IMAGE_TYPE}" = img ]; then
|
|||||||
BOOT_STAGING=$(mktemp -d)
|
BOOT_STAGING=$(mktemp -d)
|
||||||
unsquashfs -n -f -d $BOOT_STAGING $prep_results_dir/binary/live/filesystem.squashfs boot
|
unsquashfs -n -f -d $BOOT_STAGING $prep_results_dir/binary/live/filesystem.squashfs boot
|
||||||
|
|
||||||
# Mount partitions
|
# Mount partitions (nested: firmware and efi inside boot)
|
||||||
mkdir -p $TMPDIR/firmware $TMPDIR/efi $TMPDIR/boot $TMPDIR/root
|
mkdir -p $TMPDIR/boot $TMPDIR/root
|
||||||
mount $FW_DEV $TMPDIR/firmware
|
|
||||||
mount $ESP_DEV $TMPDIR/efi
|
|
||||||
mount $BOOT_DEV $TMPDIR/boot
|
mount $BOOT_DEV $TMPDIR/boot
|
||||||
|
mkdir -p $TMPDIR/boot/firmware $TMPDIR/boot/efi
|
||||||
|
mount $FW_DEV $TMPDIR/boot/firmware
|
||||||
|
mount $ESP_DEV $TMPDIR/boot/efi
|
||||||
mount $ROOT_DEV $TMPDIR/root
|
mount $ROOT_DEV $TMPDIR/root
|
||||||
|
|
||||||
# Split boot files: firmware to Part 1, kernels/initramfs to Part 3 (/boot)
|
# Copy boot files — nested mounts route firmware/* to the firmware partition
|
||||||
cp -a $BOOT_STAGING/boot/. $TMPDIR/firmware/
|
cp -a $BOOT_STAGING/boot/. $TMPDIR/boot/
|
||||||
for f in $TMPDIR/firmware/vmlinuz-* $TMPDIR/firmware/initrd.img-* $TMPDIR/firmware/System.map-* $TMPDIR/firmware/config-*; do
|
|
||||||
[ -e "$f" ] && mv "$f" $TMPDIR/boot/
|
|
||||||
done
|
|
||||||
rm -rf $BOOT_STAGING
|
rm -rf $BOOT_STAGING
|
||||||
|
|
||||||
mkdir $TMPDIR/root/images $TMPDIR/root/config
|
mkdir $TMPDIR/root/images $TMPDIR/root/config
|
||||||
@@ -475,11 +494,9 @@ elif [ "${IMAGE_TYPE}" = img ]; then
|
|||||||
rsync -a $SOURCE_DIR/raspberrypi/img/ $TMPDIR/next/
|
rsync -a $SOURCE_DIR/raspberrypi/img/ $TMPDIR/next/
|
||||||
|
|
||||||
# Install GRUB: ESP at /boot/efi (Part 2), /boot (Part 3)
|
# Install GRUB: ESP at /boot/efi (Part 2), /boot (Part 3)
|
||||||
mkdir -p $TMPDIR/next/boot $TMPDIR/next/boot/efi $TMPDIR/next/boot/firmware \
|
mkdir -p $TMPDIR/next/boot \
|
||||||
$TMPDIR/next/dev $TMPDIR/next/proc $TMPDIR/next/sys $TMPDIR/next/media/startos/root
|
$TMPDIR/next/dev $TMPDIR/next/proc $TMPDIR/next/sys $TMPDIR/next/media/startos/root
|
||||||
mount --bind $TMPDIR/boot $TMPDIR/next/boot
|
mount --rbind $TMPDIR/boot $TMPDIR/next/boot
|
||||||
mount --bind $TMPDIR/efi $TMPDIR/next/boot/efi
|
|
||||||
mount --bind $TMPDIR/firmware $TMPDIR/next/boot/firmware
|
|
||||||
mount --bind /dev $TMPDIR/next/dev
|
mount --bind /dev $TMPDIR/next/dev
|
||||||
mount --bind /proc $TMPDIR/next/proc
|
mount --bind /proc $TMPDIR/next/proc
|
||||||
mount --bind /sys $TMPDIR/next/sys
|
mount --bind /sys $TMPDIR/next/sys
|
||||||
@@ -492,9 +509,7 @@ elif [ "${IMAGE_TYPE}" = img ]; then
|
|||||||
umount $TMPDIR/next/sys
|
umount $TMPDIR/next/sys
|
||||||
umount $TMPDIR/next/proc
|
umount $TMPDIR/next/proc
|
||||||
umount $TMPDIR/next/dev
|
umount $TMPDIR/next/dev
|
||||||
umount $TMPDIR/next/boot/firmware
|
umount -l $TMPDIR/next/boot
|
||||||
umount $TMPDIR/next/boot/efi
|
|
||||||
umount $TMPDIR/next/boot
|
|
||||||
|
|
||||||
# Fix root= in grub.cfg: update-grub sees loop devices, but the
|
# Fix root= in grub.cfg: update-grub sees loop devices, but the
|
||||||
# real device uses a fixed GPT PARTUUID for root (Part 4).
|
# real device uses a fixed GPT PARTUUID for root (Part 4).
|
||||||
@@ -507,39 +522,16 @@ elif [ "${IMAGE_TYPE}" = img ]; then
|
|||||||
umount $TMPDIR/next
|
umount $TMPDIR/next
|
||||||
umount $TMPDIR/lower
|
umount $TMPDIR/lower
|
||||||
|
|
||||||
umount $TMPDIR/firmware
|
umount $TMPDIR/boot/firmware
|
||||||
umount $TMPDIR/efi
|
umount $TMPDIR/boot/efi
|
||||||
umount $TMPDIR/boot
|
umount $TMPDIR/boot
|
||||||
umount $TMPDIR/root
|
umount $TMPDIR/root
|
||||||
|
|
||||||
# Shrink btrfs to minimum size
|
|
||||||
SHRINK_MNT=$(mktemp -d)
|
|
||||||
mount $ROOT_DEV $SHRINK_MNT
|
|
||||||
btrfs filesystem resize min $SHRINK_MNT
|
|
||||||
umount $SHRINK_MNT
|
|
||||||
rmdir $SHRINK_MNT
|
|
||||||
ROOT_LEN=$(btrfs inspect-internal dump-super $ROOT_DEV | awk '/^total_bytes/ {print $2}')
|
|
||||||
|
|
||||||
losetup -d $ROOT_DEV
|
losetup -d $ROOT_DEV
|
||||||
losetup -d $BOOT_DEV
|
losetup -d $BOOT_DEV
|
||||||
losetup -d $ESP_DEV
|
losetup -d $ESP_DEV
|
||||||
losetup -d $FW_DEV
|
losetup -d $FW_DEV
|
||||||
|
|
||||||
# Recreate partition table with shrunk root
|
|
||||||
sfdisk $TARGET_NAME <<-EOF
|
|
||||||
label: gpt
|
|
||||||
|
|
||||||
${TARGET_NAME}1 : start=$((FW_START / SECTOR_LEN)), size=$((FW_LEN / SECTOR_LEN)), type=EBD0A0A2-B9E5-4433-87C0-68B6B72699C7, uuid=${FW_UUID}, name="firmware"
|
|
||||||
${TARGET_NAME}2 : start=$((ESP_START / SECTOR_LEN)), size=$((ESP_LEN / SECTOR_LEN)), type=C12A7328-F81F-11D2-BA4B-00A0C93EC93B, uuid=${ESP_UUID}, name="efi"
|
|
||||||
${TARGET_NAME}3 : start=$((BOOT_START / SECTOR_LEN)), size=$((BOOT_LEN / SECTOR_LEN)), type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=${BOOT_UUID}, name="boot"
|
|
||||||
${TARGET_NAME}4 : start=$((ROOT_START / SECTOR_LEN)), size=$((ROOT_LEN / SECTOR_LEN)), type=B921B045-1DF0-41C3-AF44-4C6F280D3FAE, uuid=${ROOT_UUID}, name="root"
|
|
||||||
EOF
|
|
||||||
|
|
||||||
TARGET_SIZE=$((ROOT_START + ROOT_LEN))
|
|
||||||
truncate -s $TARGET_SIZE $TARGET_NAME
|
|
||||||
# Move backup GPT to new end of disk after truncation
|
|
||||||
sgdisk -e $TARGET_NAME
|
|
||||||
|
|
||||||
mv $TARGET_NAME $RESULTS_DIR/$IMAGE_BASENAME.img
|
mv $TARGET_NAME $RESULTS_DIR/$IMAGE_BASENAME.img
|
||||||
|
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ set -- "${POSITIONAL_ARGS[@]}" # restore positional parameters
|
|||||||
|
|
||||||
if [ -z "$NO_SYNC" ]; then
|
if [ -z "$NO_SYNC" ]; then
|
||||||
echo 'Syncing...'
|
echo 'Syncing...'
|
||||||
umount -R /media/startos/next 2> /dev/null
|
umount -l /media/startos/next 2> /dev/null
|
||||||
umount /media/startos/upper 2> /dev/null
|
umount /media/startos/upper 2> /dev/null
|
||||||
rm -rf /media/startos/upper /media/startos/next
|
rm -rf /media/startos/upper /media/startos/next
|
||||||
mkdir /media/startos/upper
|
mkdir /media/startos/upper
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ fi
|
|||||||
|
|
||||||
unsquashfs -f -d / $1 boot
|
unsquashfs -f -d / $1 boot
|
||||||
|
|
||||||
umount -R /media/startos/next 2> /dev/null || true
|
umount -l /media/startos/next 2> /dev/null || true
|
||||||
umount /media/startos/upper 2> /dev/null || true
|
umount /media/startos/upper 2> /dev/null || true
|
||||||
umount /media/startos/lower 2> /dev/null || true
|
umount /media/startos/lower 2> /dev/null || true
|
||||||
|
|
||||||
@@ -47,14 +47,9 @@ mount --bind /tmp /media/startos/next/tmp
|
|||||||
mount --bind /dev /media/startos/next/dev
|
mount --bind /dev /media/startos/next/dev
|
||||||
mount --bind /sys /media/startos/next/sys
|
mount --bind /sys /media/startos/next/sys
|
||||||
mount --bind /proc /media/startos/next/proc
|
mount --bind /proc /media/startos/next/proc
|
||||||
mount --bind /boot /media/startos/next/boot
|
mount --rbind /boot /media/startos/next/boot
|
||||||
mount --bind /media/startos/root /media/startos/next/media/startos/root
|
mount --bind /media/startos/root /media/startos/next/media/startos/root
|
||||||
|
|
||||||
if mountpoint /boot/efi 2>&1 > /dev/null; then
|
|
||||||
mkdir -p /media/startos/next/boot/efi
|
|
||||||
mount --bind /boot/efi /media/startos/next/boot/efi
|
|
||||||
fi
|
|
||||||
|
|
||||||
if mountpoint /sys/firmware/efi/efivars 2>&1 > /dev/null; then
|
if mountpoint /sys/firmware/efi/efivars 2>&1 > /dev/null; then
|
||||||
mount --bind /sys/firmware/efi/efivars /media/startos/next/sys/firmware/efi/efivars
|
mount --bind /sys/firmware/efi/efivars /media/startos/next/sys/firmware/efi/efivars
|
||||||
fi
|
fi
|
||||||
@@ -79,7 +74,7 @@ SIGN_FILE="$(ls -1 /media/startos/next/usr/lib/linux-kbuild-*/scripts/sign-file
|
|||||||
|
|
||||||
sync
|
sync
|
||||||
|
|
||||||
umount -Rl /media/startos/next
|
umount -l /media/startos/next
|
||||||
umount /media/startos/upper
|
umount /media/startos/upper
|
||||||
umount /media/startos/lower
|
umount /media/startos/lower
|
||||||
|
|
||||||
|
|||||||
@@ -1379,6 +1379,21 @@ net.tor.client-error:
|
|||||||
fr_FR: "Erreur du client Tor : %{error}"
|
fr_FR: "Erreur du client Tor : %{error}"
|
||||||
pl_PL: "Błąd klienta Tor: %{error}"
|
pl_PL: "Błąd klienta Tor: %{error}"
|
||||||
|
|
||||||
|
# net/tunnel.rs
|
||||||
|
net.tunnel.timeout-waiting-for-add:
|
||||||
|
en_US: "timed out waiting for gateway %{gateway} to appear in database"
|
||||||
|
de_DE: "Zeitüberschreitung beim Warten auf das Erscheinen von Gateway %{gateway} in der Datenbank"
|
||||||
|
es_ES: "se agotó el tiempo esperando que la puerta de enlace %{gateway} aparezca en la base de datos"
|
||||||
|
fr_FR: "délai d'attente dépassé pour l'apparition de la passerelle %{gateway} dans la base de données"
|
||||||
|
pl_PL: "upłynął limit czasu oczekiwania na pojawienie się bramy %{gateway} w bazie danych"
|
||||||
|
|
||||||
|
net.tunnel.timeout-waiting-for-remove:
|
||||||
|
en_US: "timed out waiting for gateway %{gateway} to be removed from database"
|
||||||
|
de_DE: "Zeitüberschreitung beim Warten auf das Entfernen von Gateway %{gateway} aus der Datenbank"
|
||||||
|
es_ES: "se agotó el tiempo esperando que la puerta de enlace %{gateway} sea eliminada de la base de datos"
|
||||||
|
fr_FR: "délai d'attente dépassé pour la suppression de la passerelle %{gateway} de la base de données"
|
||||||
|
pl_PL: "upłynął limit czasu oczekiwania na usunięcie bramy %{gateway} z bazy danych"
|
||||||
|
|
||||||
# net/wifi.rs
|
# net/wifi.rs
|
||||||
net.wifi.ssid-no-special-characters:
|
net.wifi.ssid-no-special-characters:
|
||||||
en_US: "SSID may not have special characters"
|
en_US: "SSID may not have special characters"
|
||||||
|
|||||||
@@ -1018,18 +1018,16 @@ async fn apply_policy_routing(
|
|||||||
})
|
})
|
||||||
.copied();
|
.copied();
|
||||||
|
|
||||||
// Flush and rebuild per-interface routing table.
|
// Rebuild per-interface routing table using `ip route replace` to avoid
|
||||||
// Clone all non-default routes from the main table so that LAN IPs on
|
// the connectivity gap that a flush+add cycle would create. We replace
|
||||||
// other subnets remain reachable when the priority-75 catch-all overrides
|
// every desired route in-place (each replace is atomic in the kernel),
|
||||||
// default routing, then replace the default route with this interface's.
|
// then delete any stale routes that are no longer in the desired set.
|
||||||
Command::new("ip")
|
|
||||||
.arg("route")
|
// Collect the set of desired non-default route prefixes (the first
|
||||||
.arg("flush")
|
// whitespace-delimited token of each `ip route show` line is the
|
||||||
.arg("table")
|
// destination prefix, e.g. "192.168.1.0/24" or "10.0.0.0/8").
|
||||||
.arg(&table_str)
|
let mut desired_prefixes = BTreeSet::<String>::new();
|
||||||
.invoke(ErrorKind::Network)
|
|
||||||
.await
|
|
||||||
.log_err();
|
|
||||||
if let Ok(main_routes) = Command::new("ip")
|
if let Ok(main_routes) = Command::new("ip")
|
||||||
.arg("route")
|
.arg("route")
|
||||||
.arg("show")
|
.arg("show")
|
||||||
@@ -1044,11 +1042,14 @@ async fn apply_policy_routing(
|
|||||||
if line.is_empty() || line.starts_with("default") {
|
if line.is_empty() || line.starts_with("default") {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if let Some(prefix) = line.split_whitespace().next() {
|
||||||
|
desired_prefixes.insert(prefix.to_owned());
|
||||||
|
}
|
||||||
let mut cmd = Command::new("ip");
|
let mut cmd = Command::new("ip");
|
||||||
cmd.arg("route").arg("add");
|
cmd.arg("route").arg("replace");
|
||||||
for part in line.split_whitespace() {
|
for part in line.split_whitespace() {
|
||||||
// Skip status flags that appear in route output but
|
// Skip status flags that appear in route output but
|
||||||
// are not valid for `ip route add`.
|
// are not valid for `ip route replace`.
|
||||||
if part == "linkdown" || part == "dead" {
|
if part == "linkdown" || part == "dead" {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -1058,10 +1059,11 @@ async fn apply_policy_routing(
|
|||||||
cmd.invoke(ErrorKind::Network).await.log_err();
|
cmd.invoke(ErrorKind::Network).await.log_err();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Add default route via this interface's gateway
|
|
||||||
|
// Replace the default route via this interface's gateway.
|
||||||
{
|
{
|
||||||
let mut cmd = Command::new("ip");
|
let mut cmd = Command::new("ip");
|
||||||
cmd.arg("route").arg("add").arg("default");
|
cmd.arg("route").arg("replace").arg("default");
|
||||||
if let Some(gw) = ipv4_gateway {
|
if let Some(gw) = ipv4_gateway {
|
||||||
cmd.arg("via").arg(gw.to_string());
|
cmd.arg("via").arg(gw.to_string());
|
||||||
}
|
}
|
||||||
@@ -1075,6 +1077,40 @@ async fn apply_policy_routing(
|
|||||||
cmd.invoke(ErrorKind::Network).await.log_err();
|
cmd.invoke(ErrorKind::Network).await.log_err();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Delete stale routes: any non-default route in the per-interface table
|
||||||
|
// whose prefix is not in the desired set.
|
||||||
|
if let Ok(existing_routes) = Command::new("ip")
|
||||||
|
.arg("route")
|
||||||
|
.arg("show")
|
||||||
|
.arg("table")
|
||||||
|
.arg(&table_str)
|
||||||
|
.invoke(ErrorKind::Network)
|
||||||
|
.await
|
||||||
|
.and_then(|b| String::from_utf8(b).with_kind(ErrorKind::Utf8))
|
||||||
|
{
|
||||||
|
for line in existing_routes.lines() {
|
||||||
|
let line = line.trim();
|
||||||
|
if line.is_empty() || line.starts_with("default") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let Some(prefix) = line.split_whitespace().next() else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
if desired_prefixes.contains(prefix) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Command::new("ip")
|
||||||
|
.arg("route")
|
||||||
|
.arg("del")
|
||||||
|
.arg(prefix)
|
||||||
|
.arg("table")
|
||||||
|
.arg(&table_str)
|
||||||
|
.invoke(ErrorKind::Network)
|
||||||
|
.await
|
||||||
|
.log_err();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Ensure global CONNMARK restore rules in mangle PREROUTING (forwarded
|
// Ensure global CONNMARK restore rules in mangle PREROUTING (forwarded
|
||||||
// packets) and OUTPUT (locally-generated replies). Both are needed:
|
// packets) and OUTPUT (locally-generated replies). Both are needed:
|
||||||
// PREROUTING handles DNAT-forwarded traffic, OUTPUT handles replies from
|
// PREROUTING handles DNAT-forwarded traffic, OUTPUT handles replies from
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use imbl_value::InternedString;
|
use imbl_value::InternedString;
|
||||||
use patch_db::json_ptr::JsonPointer;
|
use patch_db::json_ptr::JsonPointer;
|
||||||
@@ -8,7 +10,9 @@ use ts_rs::TS;
|
|||||||
|
|
||||||
use crate::GatewayId;
|
use crate::GatewayId;
|
||||||
use crate::context::{CliContext, RpcContext};
|
use crate::context::{CliContext, RpcContext};
|
||||||
use crate::db::model::public::{GatewayType, NetworkInterfaceInfo, NetworkInterfaceType};
|
use crate::db::model::public::{
|
||||||
|
GatewayType, NetworkInfo, NetworkInterfaceInfo, NetworkInterfaceType,
|
||||||
|
};
|
||||||
use crate::net::host::all_hosts;
|
use crate::net::host::all_hosts;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::util::Invoke;
|
use crate::util::Invoke;
|
||||||
@@ -139,6 +143,34 @@ pub async fn add_tunnel(
|
|||||||
.result?;
|
.result?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Wait for the sync loop to fully commit gateway state (addresses, hosts)
|
||||||
|
// to the database, with a 15-second timeout.
|
||||||
|
if tokio::time::timeout(Duration::from_secs(15), async {
|
||||||
|
let mut watch = ctx
|
||||||
|
.db
|
||||||
|
.watch("/public/serverInfo/network".parse::<JsonPointer>().unwrap())
|
||||||
|
.await
|
||||||
|
.typed::<NetworkInfo>();
|
||||||
|
loop {
|
||||||
|
if watch
|
||||||
|
.peek()?
|
||||||
|
.as_gateways()
|
||||||
|
.as_idx(&iface)
|
||||||
|
.and_then(|g| g.as_ip_info().transpose_ref())
|
||||||
|
.is_some()
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
watch.changed().await?;
|
||||||
|
}
|
||||||
|
Ok::<_, Error>(())
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.is_err()
|
||||||
|
{
|
||||||
|
tracing::warn!("{}", t!("net.tunnel.timeout-waiting-for-add", gateway = iface.as_str()));
|
||||||
|
}
|
||||||
|
|
||||||
Ok(iface)
|
Ok(iface)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -224,5 +256,27 @@ pub async fn remove_tunnel(
|
|||||||
.await
|
.await
|
||||||
.result?;
|
.result?;
|
||||||
|
|
||||||
|
// Wait for the sync loop to fully commit gateway removal to the database,
|
||||||
|
// with a 15-second timeout.
|
||||||
|
if tokio::time::timeout(Duration::from_secs(15), async {
|
||||||
|
let mut watch = ctx
|
||||||
|
.db
|
||||||
|
.watch("/public/serverInfo/network".parse::<JsonPointer>().unwrap())
|
||||||
|
.await
|
||||||
|
.typed::<NetworkInfo>();
|
||||||
|
loop {
|
||||||
|
if watch.peek()?.as_gateways().as_idx(&id).is_none() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
watch.changed().await?;
|
||||||
|
}
|
||||||
|
Ok::<_, Error>(())
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.is_err()
|
||||||
|
{
|
||||||
|
tracing::warn!("{}", t!("net.tunnel.timeout-waiting-for-remove", gateway = id.as_str()));
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user