aboutsummaryrefslogtreecommitdiff
path: root/debian-container/lib/network.sh
blob: efe0d4722036895d9a3aeef53d0b9aaadd3210bd (plain)
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
#!/usr/bin/env bash
# SPDX-License-Identifier: GPL-3.0-or-later
# Copyright (C) 2026 by Richard Qin

DC_NETWORK_MODE=${DEBIAN_NETWORK:-none}
DC_NETWORK_UNSHARE_ARGS=()
DC_NAT_BRIDGE=${DEBIAN_NAT_BRIDGE:-rutils0}
DC_NAT_NETWORK=${DEBIAN_NAT_NETWORK:-10.88.0.0/24}
DC_NAT_PREFIX=${DC_NAT_NETWORK#*/}
DC_NAT_GATEWAY=${DEBIAN_NAT_GATEWAY:-10.88.0.1}
DC_NAT_ADDRESS=${DEBIAN_NAT_ADDRESS:-10.88.0.2}
DC_NAT_DNS=${DEBIAN_NAT_DNS:-1.1.1.1}

dc_network_prepare() {
  local mode=${1:-$DC_NETWORK_MODE}

  case "$mode" in
    none)
      DC_NETWORK_UNSHARE_ARGS=(--net)
      ;;
    host)
      DC_NETWORK_UNSHARE_ARGS=()
      ;;
    nat)
      DC_NETWORK_UNSHARE_ARGS=(--net)
      ;;
    *)
      dc_die "网络模式无效:$mode(可选:none、host、nat)"
      ;;
  esac
}

dc_network_configure_rootfs() {
  [[ "$DC_NETWORK_MODE" == nat ]] || return 0
  rm -f "$DC_ROOT/etc/resolv.conf"
  printf 'nameserver %s\n' "$DC_NAT_DNS" > "$DC_ROOT/etc/resolv.conf"
}

dc_network_nat_setup_firewall() {
  dc_require_commands grep nft sysctl

  sysctl -w net.ipv4.ip_forward=1 >/dev/null
  if ! nft list table ip rutils_nat >/dev/null 2>&1; then
    nft add table ip rutils_nat
  fi
  if ! nft list chain ip rutils_nat postrouting >/dev/null 2>&1; then
    nft 'add chain ip rutils_nat postrouting { type nat hook postrouting priority 100; policy accept; }'
  fi
  if ! nft list chain ip rutils_nat postrouting | grep -Fq "ip saddr $DC_NAT_NETWORK"; then
    nft add rule ip rutils_nat postrouting ip saddr "$DC_NAT_NETWORK" oifname != "$DC_NAT_BRIDGE" masquerade
  fi
}

dc_network_nat_setup() {
  local netns=$1
  local host_interface=$2
  local guest_interface=$3

  dc_require_commands grep ip
  if ! ip link show "$DC_NAT_BRIDGE" >/dev/null 2>&1; then
    ip link add "$DC_NAT_BRIDGE" type bridge
  fi
  ip link set "$DC_NAT_BRIDGE" up
  if ! ip -4 addr show dev "$DC_NAT_BRIDGE" | grep -Fq "inet $DC_NAT_GATEWAY/"; then
    ip addr add "$DC_NAT_GATEWAY/$DC_NAT_PREFIX" dev "$DC_NAT_BRIDGE"
  fi

  ip netns add "$netns"
  ip link add "$host_interface" type veth peer name "$guest_interface"
  ip link set "$guest_interface" netns "$netns"
  ip link set "$host_interface" master "$DC_NAT_BRIDGE"
  ip link set "$host_interface" up

  ip netns exec "$netns" ip link set lo up
  ip netns exec "$netns" ip link set "$guest_interface" name eth0
  ip netns exec "$netns" ip addr add "$DC_NAT_ADDRESS/$DC_NAT_PREFIX" dev eth0
  ip netns exec "$netns" ip link set eth0 up
  ip netns exec "$netns" ip route add default via "$DC_NAT_GATEWAY"
  dc_network_nat_setup_firewall
}

dc_network_nat_cleanup() {
  local netns=$1
  local host_interface=$2

  ip link delete "$host_interface" 2>/dev/null || true
  ip netns delete "$netns" 2>/dev/null || true
}

dc_network_nat_run() {
  local child_action=$1
  local netns="rutils-${DC_INSTANCE}-$$"
  local host_interface="rvh$$"
  local guest_interface="rvg$$"

  shift
  trap "dc_network_nat_cleanup '$netns' '$host_interface'" EXIT
  dc_network_nat_setup "$netns" "$host_interface" "$guest_interface"
  env DEBIAN_NETWORK=nat ip netns exec "$netns" \
    unshare --mount --uts --ipc --pid --fork --propagation private -- \
    bash "$DC_SCRIPT" "$child_action" "$DC_INSTANCE" "$@"
}

dc_network_nat_namespaces() {
  ip netns list | awk '$1 ~ /^rutils-/ { print $1 }'
}

dc_network_status() {
  local namespace
  local link

  dc_require_commands awk ip nft
  echo "网桥:$DC_NAT_BRIDGE"
  if ip link show "$DC_NAT_BRIDGE" >/dev/null 2>&1; then
    ip -brief addr show dev "$DC_NAT_BRIDGE"
  else
    echo '状态:不存在'
  fi

  echo 'network namespace:'
  while read -r namespace; do
    [[ -n "$namespace" ]] || continue
    echo "  $namespace"
    ip netns pids "$namespace" | awk '{ print "    pid: " $0 }'
  done < <(dc_network_nat_namespaces)

  echo 'veth:'
  while read -r link; do
    [[ -n "$link" ]] || continue
    echo "  $link"
  done < <(ip -o link show | awk -F': ' '$2 ~ /^rvh[0-9]+@/ { sub(/@.*/, "", $2); print $2 }')

  echo 'nftables:'
  if nft list table ip rutils_nat >/dev/null 2>&1; then
    nft list table ip rutils_nat
  else
    echo '  rutils_nat 不存在'
  fi
}

dc_network_clean() {
  local namespace
  local link
  local active=0

  dc_require_commands awk ip nft
  while read -r namespace; do
    [[ -n "$namespace" ]] || continue
    if [[ -n "$(ip netns pids "$namespace")" ]]; then
      echo "保留活动 namespace:$namespace"
      active=1
    else
      ip netns delete "$namespace"
      echo "已删除 namespace:$namespace"
    fi
  done < <(dc_network_nat_namespaces)

  if ((active == 0)); then
    while read -r link; do
      [[ -n "$link" ]] || continue
      ip link delete "$link" 2>/dev/null || true
      echo "已删除 veth:$link"
    done < <(ip -o link show | awk -F': ' '$2 ~ /^rvh[0-9]+@/ { sub(/@.*/, "", $2); print $2 }')

    nft delete table ip rutils_nat 2>/dev/null || true
    if ip link show "$DC_NAT_BRIDGE" >/dev/null 2>&1; then
      ip link set "$DC_NAT_BRIDGE" down
      ip link delete "$DC_NAT_BRIDGE" type bridge
    fi
    echo '已清理 NAT 网桥和 nftables 规则。'
  else
    echo '存在活动 namespace,保留 veth。'
    echo '存在活动 namespace,保留 NAT 网桥和 nftables 规则。'
  fi
}