收起左侧

飞牛的另外一种上网方式。VLAN+DHCP

0
回复
55
查看
[ 复制链接 ]

5

主题

9

回帖

0

牛值

江湖小虾

我之前以为PPOE上网,网线直插DHCP,或者手动指定IP上网。滚出来了才知道还有另外一种上网方式DHCP+VLAN上网。

飞牛并没有这种上网方式,所以只能手动了。

现在代码贴出来了。你们自己看,自己研究。

#!/usr/bin/env bash
set -euo pipefail

SERVICE_NAME="fnnas-vlan-dhcp.service"
CONFIG_FILE="/etc/fnnas-vlan-dhcp.conf"
HELPER_FILE="/usr/local/sbin/fnnas-vlan-dhcp"
SYSTEMD_FILE="/etc/systemd/system/${SERVICE_NAME}"

need_root() {
  if [[ "${EUID}" -ne 0 ]]; then
    echo "Please run as root: sudo bash $0"
    exit 1
  fi
}

has_cmd() {
  command -v "$1" >/dev/null 2>&1
}

show_current_status() {
  echo "===== systemd status ====="
  systemctl is-enabled "${SERVICE_NAME}" 2>/dev/null || true
  systemctl status "${SERVICE_NAME}" --no-pager 2>/dev/null || true

  echo
  echo "===== saved config ====="
  if [[ -f "${CONFIG_FILE}" ]]; then
    cat "${CONFIG_FILE}"
  else
    echo "${CONFIG_FILE} not found"
  fi

  echo
  echo "===== current VLAN interface ====="
  if [[ -f "${CONFIG_FILE}" ]]; then
    # shellcheck disable=SC1090
    source "${CONFIG_FILE}"
    ip -d link show "${VLAN_IFACE}" 2>/dev/null || true
    ip addr show "${VLAN_IFACE}" 2>/dev/null || true
    ip route show dev "${VLAN_IFACE}" 2>/dev/null || true
  fi

  echo
  echo "===== boot logs ====="
  journalctl -u "${SERVICE_NAME}" -b --no-pager 2>/dev/null || true
}

remove_installation() {
  echo "Stopping and removing ${SERVICE_NAME} ..."
  systemctl disable --now "${SERVICE_NAME}" >/dev/null 2>&1 || true

  if [[ -x "${HELPER_FILE}" ]]; then
    "${HELPER_FILE}" stop >/dev/null 2>&1 || true
  fi

  rm -f "${SYSTEMD_FILE}" "${HELPER_FILE}" "${CONFIG_FILE}"
  systemctl daemon-reload
  echo "Removed."
}

collect_interfaces() {
  local iface path
  IFACES=()

  for path in /sys/class/net/*; do
    iface="${path##*/}"
    [[ "${iface}" == "lo" ]] && continue
    [[ "${iface}" == *.* ]] && continue
    [[ "${iface}" == docker* ]] && continue
    [[ "${iface}" == br-* ]] && continue
    [[ "${iface}" == veth* ]] && continue
    [[ "${iface}" == virbr* ]] && continue
    [[ "${iface}" == tailscale* ]] && continue

    if [[ -e "${path}/device" ]]; then
      IFACES+=("${iface}")
    fi
  done

  if [[ "${#IFACES[@]}" -eq 0 ]]; then
    for path in /sys/class/net/*; do
      iface="${path##*/}"
      [[ "${iface}" == "lo" ]] && continue
      [[ "${iface}" == *.* ]] && continue
      IFACES+=("${iface}")
    done
  fi

  IFS=$'\n' IFACES=($(sort <<<"${IFACES[*]}"))
  unset IFS
}

print_interfaces() {
  local i iface mac state carrier ipv4

  echo "Detected ${#IFACES[@]} selectable network interface(s):"
  echo

  for i in "${!IFACES[@]}"; do
    iface="${IFACES[$i]}"
    mac="$(cat "/sys/class/net/${iface}/address" 2>/dev/null || echo unknown)"
    state="$(cat "/sys/class/net/${iface}/operstate" 2>/dev/null || echo unknown)"
    carrier="$(cat "/sys/class/net/${iface}/carrier" 2>/dev/null || echo unknown)"
    ipv4="$(ip -4 -o addr show dev "${iface}" 2>/dev/null | awk '{print $4}' | paste -sd ',' -)"
    [[ -n "${ipv4}" ]] || ipv4="-"

    printf "%2d) %-18s MAC %-17s state %-8s carrier %-7s IPv4 %s\n" \
      "$((i + 1))" "${iface}" "${mac}" "${state}" "${carrier}" "${ipv4}"
  done

  echo
}

choose_interface() {
  local index

  while true; do
    read -r -p "Choose interface number [1-${#IFACES[@]}]: " index
    if [[ "${index}" =~ ^[0-9]+$ ]] && ((index >= 1 && index <= ${#IFACES[@]})); then
      SELECTED_IFACE="${IFACES[$((index - 1))]}"
      return
    fi
    echo "Invalid input. Please try again."
  done
}

choose_vlan_id() {
  local vlan

  while true; do
    read -r -p "Enter VLAN ID [1-4094], for example 832: " vlan
    if [[ "${vlan}" =~ ^[0-9]+$ ]] && ((vlan >= 1 && vlan <= 4094)); then
      SELECTED_VLAN="${vlan}"
      return
    fi
    echo "Invalid VLAN ID. Please enter a number from 1 to 4094."
  done
}

write_config() {
  local iface="$1"
  local vlan="$2"
  local vlan_iface="${iface}.${vlan}"

  cat >"${CONFIG_FILE}" <<EOF_CONF
IFACE="${iface}"
VLAN_ID="${vlan}"
VLAN_IFACE="${vlan_iface}"
EOF_CONF

  chmod 0644 "${CONFIG_FILE}"
}

write_helper() {
  cat >"${HELPER_FILE}" <<'EOF_HELPER'
#!/usr/bin/env bash
set -euo pipefail

CONFIG_FILE="/etc/fnnas-vlan-dhcp.conf"

log() {
  echo "[fnnas-vlan-dhcp] $*"
}

has_cmd() {
  command -v "$1" >/dev/null 2>&1
}

load_config() {
  if [[ ! -f "${CONFIG_FILE}" ]]; then
    log "Config file not found: ${CONFIG_FILE}"
    exit 1
  fi

  # shellcheck disable=SC1090
  source "${CONFIG_FILE}"

  if [[ -z "${IFACE:-}" || -z "${VLAN_ID:-}" || -z "${VLAN_IFACE:-}" ]]; then
    log "Config file is incomplete."
    exit 1
  fi
}

wait_for_interface() {
  local i

  for i in $(seq 1 30); do
    if [[ -e "/sys/class/net/${IFACE}" ]]; then
      return 0
    fi
    sleep 1
  done

  log "Timed out waiting for interface ${IFACE}."
  exit 1
}

start_vlan() {
  load_config
  wait_for_interface

  log "Loading 802.1Q module."
  modprobe 8021q >/dev/null 2>&1 || true

  log "Bringing up physical interface ${IFACE}."
  ip link set "${IFACE}" up

  if ! ip link show "${VLAN_IFACE}" >/dev/null 2>&1; then
    log "Creating VLAN interface ${VLAN_IFACE} with VLAN ID ${VLAN_ID}."
    ip link add link "${IFACE}" name "${VLAN_IFACE}" type vlan id "${VLAN_ID}"
  else
    log "VLAN interface ${VLAN_IFACE} already exists."
  fi

  ip link set "${VLAN_IFACE}" up

  if has_cmd dhclient; then
    log "Getting DHCP address with dhclient."
    dhclient -r "${VLAN_IFACE}" >/dev/null 2>&1 || true

    if has_cmd timeout; then
      timeout 60 dhclient -4 -v -1 "${VLAN_IFACE}"
    else
      dhclient -4 -v -1 "${VLAN_IFACE}"
    fi
  elif has_cmd udhcpc; then
    log "Getting DHCP address with udhcpc."
    pkill -f "udhcpc.*${VLAN_IFACE}" >/dev/null 2>&1 || true
    nohup udhcpc -i "${VLAN_IFACE}" -p "/run/udhcpc-${VLAN_IFACE}.pid" \
      >>"/var/log/fnnas-vlan-dhcp.log" 2>&1 &
    sleep 5
  else
    log "No DHCP client found. Please install dhclient or udhcpc."
    exit 1
  fi

  log "Current address:"
  ip addr show "${VLAN_IFACE}" || true
  log "Current routes:"
  ip route show dev "${VLAN_IFACE}" || true
}

stop_vlan() {
  load_config

  log "Releasing DHCP address."
  if has_cmd dhclient; then
    dhclient -r "${VLAN_IFACE}" >/dev/null 2>&1 || true
  fi

  if [[ -f "/run/udhcpc-${VLAN_IFACE}.pid" ]]; then
    kill "$(cat "/run/udhcpc-${VLAN_IFACE}.pid")" >/dev/null 2>&1 || true
    rm -f "/run/udhcpc-${VLAN_IFACE}.pid"
  fi

  if ip link show "${VLAN_IFACE}" >/dev/null 2>&1; then
    log "Deleting VLAN interface ${VLAN_IFACE}."
    ip link delete "${VLAN_IFACE}" || true
  fi
}

status_vlan() {
  load_config

  ip -d link show "${VLAN_IFACE}" 2>/dev/null || true
  ip addr show "${VLAN_IFACE}" 2>/dev/null || true
  ip route show dev "${VLAN_IFACE}" 2>/dev/null || true
}

case "${1:-start}" in
  start)
    start_vlan
    ;;
  stop)
    stop_vlan
    ;;
  restart)
    stop_vlan
    start_vlan
    ;;
  status)
    status_vlan
    ;;
  *)
    echo "Usage: $0 {start|stop|restart|status}"
    exit 2
    ;;
esac
EOF_HELPER

  chmod 0755 "${HELPER_FILE}"
}

write_systemd_service() {
  cat >"${SYSTEMD_FILE}" <<EOF_SERVICE
[Unit]
Description=Create VLAN interface and get DHCP address for fnOS
Wants=systemd-udev-settle.service
After=systemd-udev-settle.service network-pre.target

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=${HELPER_FILE} start
ExecStop=${HELPER_FILE} stop

[Install]
WantedBy=multi-user.target
EOF_SERVICE

  chmod 0644 "${SYSTEMD_FILE}"
}

install_vlan_service() {
  local confirm vlan_iface

  collect_interfaces
  if [[ "${#IFACES[@]}" -eq 0 ]]; then
    echo "No usable network interfaces were detected."
    exit 1
  fi

  print_interfaces
  choose_interface
  choose_vlan_id
  vlan_iface="${SELECTED_IFACE}.${SELECTED_VLAN}"

  echo
  echo "The following config will be installed:"
  echo "Physical interface: ${SELECTED_IFACE}"
  echo "VLAN ID           : ${SELECTED_VLAN}"
  echo "VLAN interface    : ${vlan_iface}"
  echo
  read -r -p "Continue? [y/N]: " confirm
  if [[ ! "${confirm}" =~ ^[Yy]$ ]]; then
    echo "Canceled."
    exit 0
  fi

  if systemctl list-unit-files "${SERVICE_NAME}" >/dev/null 2>&1; then
    systemctl stop "${SERVICE_NAME}" >/dev/null 2>&1 || true
  fi

  write_config "${SELECTED_IFACE}" "${SELECTED_VLAN}"
  write_helper
  write_systemd_service

  systemctl daemon-reload
  systemctl enable "${SERVICE_NAME}"

  echo
  echo "Starting ${SERVICE_NAME} and requesting a DHCP address ..."
  if systemctl restart "${SERVICE_NAME}"; then
    echo
    echo "Done. Current status:"
    systemctl status "${SERVICE_NAME}" --no-pager || true
    echo
    "${HELPER_FILE}" status || true
  else
    echo
    echo "Service start failed. Logs:"
    journalctl -u "${SERVICE_NAME}" -b --no-pager || true
    exit 1
  fi
}

main() {
  need_root

  case "${1:-install}" in
    install)
      install_vlan_service
      ;;
    status)
      show_current_status
      ;;
    remove|uninstall)
      remove_installation
      ;;
    *)
      echo "Usage: sudo bash $0 [install|status|remove]"
      exit 2
      ;;
  esac
}

main "$@"

收藏
送赞
分享
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则