RSS 管理后台
09/7
19:12
internet

Configuring UCG-Fiber Dual WAN via DAC and CRS305 with a Single PON Stick (Shanghai Unicom)

Port Media / Speed Connected Device Role / Service Description
SFP+ 1 Optical (PON Stick) Shanghai Unicom ODN Uplink (WAN) Carries Internet & IPTV traffic from ISP
SFP+ 2 DAC (SFP+) UCG-Fiber (WAN 1) Downlink (WAN 1) Primary WAN link (PPPoE / DHCP)
SFP+ 3 DAC (SFP+) UCG-Fiber (WAN 2) Downlink (WAN 2) Secondary WAN link (Dual WAN / Load Balance)
SFP+ 4 DAC / Optical Core Switch / LAN Management (MGMT) Out-of-band management access to CRS305
Ether1 RJ45 (1G) IPTV STB / Local LAN IPTV Access Dedicated untagged IPTV access port
# ============================================================
# CRS305-1G-4S+ 自动化部署脚本 (setup.rsc)
# 适用: RouterOS v7.x
# 执行重置指令:
# /system reset-configuration no-defaults=yes run-after-reset=setup.rsc
#
# 口型:
#   sfp-sfpplus1  PONStick   tagged 1268/44/40 + untagged 99 (Stick 管理 192.168.0.1)
#   sfp-sfpplus2  UCG-WAN1   untagged 1268  PPPoE
#   sfp-sfpplus3  UCG-WAN2   untagged 1268  PPPoE + tagged 99 (访问 192.168.0.1/.2)
#   sfp-sfpplus4  Local-MGMT untagged 99
#   ether1        IPTV       untagged 44 + tagged 40
# ============================================================

:delay 15s;

/ipv6 settings
set disable-ipv6=yes

/interface list
add name=MGMT

/interface bridge
add name=br-pon \
    protocol-mode=none \
    vlan-filtering=no \
    igmp-snooping=yes

/interface bridge port
add bridge=br-pon \
    interface=sfp-sfpplus1 \
    pvid=99 \
    ingress-filtering=yes \
    frame-types=admit-all \
    hw=yes \
    multicast-router=permanent \
    comment="PONStick"

add bridge=br-pon \
    interface=sfp-sfpplus2 \
    pvid=1268 \
    ingress-filtering=yes \
    frame-types=admit-only-untagged-and-priority-tagged \
    hw=yes \
    unknown-multicast-flood=no \
    multicast-router=disabled \
    comment="UCG-WAN1"

add bridge=br-pon \
    interface=sfp-sfpplus3 \
    pvid=1268 \
    ingress-filtering=yes \
    frame-types=admit-all \
    hw=yes \
    unknown-multicast-flood=no \
    multicast-router=disabled \
    comment="UCG-WAN2"

add bridge=br-pon \
    interface=sfp-sfpplus4 \
    pvid=99 \
    ingress-filtering=yes \
    frame-types=admit-only-untagged-and-priority-tagged \
    hw=yes \
    unknown-multicast-flood=no \
    multicast-router=disabled \
    comment="Local-MGMT"

add bridge=br-pon \
    interface=ether1 \
    pvid=44 \
    ingress-filtering=yes \
    frame-types=admit-all \
    hw=yes \
    fast-leave=yes \
    multicast-router=disabled \
    comment="IPTV"

/interface bridge vlan
add bridge=br-pon \
    vlan-ids=99 \
    tagged=br-pon,sfp-sfpplus3 \
    untagged=sfp-sfpplus1,sfp-sfpplus4

add bridge=br-pon \
    vlan-ids=1268 \
    tagged=sfp-sfpplus1 \
    untagged=sfp-sfpplus2,sfp-sfpplus3

add bridge=br-pon \
    vlan-ids=44 \
    tagged=sfp-sfpplus1 \
    untagged=ether1

add bridge=br-pon \
    vlan-ids=40 \
    tagged=sfp-sfpplus1,ether1

/interface vlan
add name=vlan99-mgmt \
    interface=br-pon \
    vlan-id=99

/ip address
add address=192.168.0.2/24 \
    interface=vlan99-mgmt

/ip route
add dst-address=0.0.0.0/0 \
    gateway=192.168.0.3 \
    comment="Default Gateway via UCG"

/ip dns
set servers=192.168.0.3,223.5.5.5

/interface list member
add interface=vlan99-mgmt \
    list=MGMT \
    comment="VLAN99 IP Management"

add interface=sfp-sfpplus4 \
    list=MGMT \
    comment="Emergency MAC Winbox"

/ip neighbor discovery-settings
set discover-interface-list=MGMT

/tool mac-server
set allowed-interface-list=MGMT

/tool mac-server mac-winbox
set allowed-interface-list=MGMT

/ip service
set telnet disabled=yes
set ftp disabled=yes
set api disabled=yes
set api-ssl disabled=yes


/interface bridge
set [find name=br-pon] frame-types=admit-only-vlan-tagged ingress-filtering=yes vlan-filtering=yes

ucg 7号sfp+口 接入wan

ip link add link eth6 name eth6.99 type vlan id 99
ip link set eth6.99 up
ip addr add 192.168.0.3/24 dev eth6.99

UCG-Fiber VLAN99 一键脚本

直接 SSH 到 UCG-Fiber,以 root 身份整段复制执行:

bash <<'UCG_VLAN99_INSTALL'
set -Eeuo pipefail

# ============================================================
# UCG-Fiber VLAN99 完整一键部署
#
# 已验证环境:
#   UniFi Cloud Gateway Fiber
#   UniFi OS / Firmware v6.0.9
#   Linux 5.4.213-ui-ipq9574
#
# 配置:
#   Parent : eth6
#   VLAN   : 99
#   IP     : 192.168.0.3/24
#   MTU    : 1500
#
# 功能:
#   1. 自动检测/安装 unifi-common / udm-boot
#   2. 自动备份旧 VLAN99 配置
#   3. 部署 VLAN99 maintainer
#   4. 创建 systemd service
#   5. 创建 /data/on_boot.d 固件升级恢复脚本
#   6. 自动启用并启动
#   7. 自动健康检查
# ============================================================

PARENT="eth6"
VLAN_IF="eth6.99"
VLAN_ID="99"

IP_ADDR="192.168.0.3/24"
SRC_IP="192.168.0.3"
SUBNET="192.168.0.0/24"

BASE="/data/ucg-vlan99"
SCRIPT="$BASE/vlan99-maintain.sh"
BOOT="/data/on_boot.d/50-ucg-vlan99.sh"
UNIT="/etc/systemd/system/ucg-vlan99.service"

UDM_UNIT="/etc/systemd/system/udm-boot.service"
UDM_INSTALL_URL="https://raw.githubusercontent.com/unifi-utilities/unifi-common/HEAD/remote_install.sh"

STAMP="$(date +%Y%m%d-%H%M%S)"
BACKUP="$BASE/backup/$STAMP"


# ============================================================
# 辅助函数
# ============================================================

section() {
    echo
    echo "============================================================"
    echo "$1"
    echo "============================================================"
}

die() {
    echo
    echo "ERROR: $*" >&2
    exit 1
}


# ============================================================
# 0. 基础检查
# ============================================================

section "[0/8] 环境检查"

[ "$(id -u)" -eq 0 ] || die "请使用 root 执行"

command -v systemctl >/dev/null || die "systemctl 不存在"
command -v ip >/dev/null || die "ip 命令不存在"
command -v curl >/dev/null || die "curl 不存在"

if command -v ubnt-device-info >/dev/null 2>&1; then
    MODEL="$(ubnt-device-info model 2>/dev/null || true)"
    FW="$(ubnt-device-info firmware 2>/dev/null || true)"

    echo "Model    : ${MODEL:-unknown}"
    echo "Firmware : ${FW:-unknown}"

    if [ "${MODEL:-}" != "UniFi Cloud Gateway Fiber" ]; then
        echo "WARNING: 当前型号不是 UniFi Cloud Gateway Fiber"
        echo "         脚本仍继续,但 eth6 映射需要你自行确认。"
    fi
else
    echo "ubnt-device-info 不存在,跳过型号检查"
fi

echo "Kernel   : $(uname -r)"
echo "Arch     : $(uname -m)"


# ============================================================
# 1. 检测 / 安装 on_boot 框架
# ============================================================

section "[1/8] 检查 udm-boot / on_boot"

NEED_UDM_INSTALL=0

if [ ! -f "$UDM_UNIT" ]; then
    echo "未发现 $UDM_UNIT"
    NEED_UDM_INSTALL=1
else
    # 必须确认这是使用 /data/on_boot.d 的当前结构
    if ! grep -q '/data/on_boot.d' "$UDM_UNIT" 2>/dev/null; then
        echo "发现 udm-boot,但不是当前 /data/on_boot.d 结构"
        NEED_UDM_INSTALL=1
    else
        echo "已发现兼容的 udm-boot.service"
    fi
fi

if [ "$NEED_UDM_INSTALL" -eq 1 ]; then
    echo "安装当前 unifi-common / udm-boot..."

    TMP_INSTALL="/tmp/unifi-common-remote-install.sh"

    curl -fL \
        "$UDM_INSTALL_URL" \
        -o "$TMP_INSTALL"

    chmod 700 "$TMP_INSTALL"

    /bin/bash "$TMP_INSTALL"

    rm -f "$TMP_INSTALL"
fi

mkdir -p /data/on_boot.d

systemctl daemon-reload

systemctl enable udm-boot.service >/dev/null 2>&1 || \
    die "无法 enable udm-boot.service"

# 已经 active 就不重复启动
if ! systemctl is-active --quiet udm-boot.service; then
    systemctl start udm-boot.service || \
        die "无法启动 udm-boot.service"
fi

echo
echo "udm-boot enabled : $(systemctl is-enabled udm-boot.service 2>/dev/null || true)"
echo "udm-boot active  : $(systemctl is-active udm-boot.service 2>/dev/null || true)"
echo "on_boot path     : /data/on_boot.d"


# ============================================================
# 2. 备份已有配置
# ============================================================

section "[2/8] 备份现有 VLAN99 配置"

mkdir -p "$BASE"
mkdir -p "$BACKUP"

[ -f "$SCRIPT" ] && \
    cp -a "$SCRIPT" "$BACKUP/vlan99-maintain.sh"

[ -f "$BOOT" ] && \
    cp -a "$BOOT" "$BACKUP/50-ucg-vlan99.sh"

[ -f "$UNIT" ] && \
    cp -a "$UNIT" "$BACKUP/ucg-vlan99.service"

echo "Backup directory:"
echo "$BACKUP"


# ============================================================
# 3. 清理错误/旧 watchdog 名称
# ============================================================

section "[3/8] 清理旧服务"

systemctl disable --now ucg-vlan99-watchdog.service \
    >/dev/null 2>&1 || true

rm -f \
    /etc/systemd/system/ucg-vlan99-watchdog.service \
    /etc/systemd/system/multi-user.target.wants/ucg-vlan99-watchdog.service

# 替换正式服务前先停止
systemctl stop ucg-vlan99.service \
    >/dev/null 2>&1 || true

echo "旧 watchdog 名称已清理"


# ============================================================
# 4. 写入最终 VLAN99 Maintainer
# ============================================================

section "[4/8] 写入 VLAN99 Maintainer"

cat >"$SCRIPT" <<'MAINTAIN_EOF'
#!/bin/bash

# ============================================================
# UCG-Fiber VLAN99 Maintainer
#
# Parent : eth6
# VLAN   : 99
# IP     : 192.168.0.3/24
# MTU    : 1500
#
# 特性:
# - 等待父接口存在
# - 等待父接口 carrier=1
# - VLAN 不存在才创建
# - MTU 错误才修改
# - 接口 DOWN 才 set up
# - IP 丢失才恢复
# - 不 flush 其他地址
# - UniFi 删除 VLAN 后最多约一个检查周期恢复
# - 正常运行时只检查,不反复修改 netlink
# ============================================================

PARENT="eth6"
VLAN_IF="eth6.99"
VLAN_ID="99"

IP_ADDR="192.168.0.3/24"
SRC_IP="192.168.0.3"
SUBNET="192.168.0.0/24"

INTERVAL=5


log() {
    logger -t ucg-vlan99 -- "$*"
}


ensure_vlan() {

    # --------------------------------------------------------
    # 1. 等待父接口出现
    # --------------------------------------------------------

    if ! ip link show dev "$PARENT" >/dev/null 2>&1; then
        return 0
    fi


    # --------------------------------------------------------
    # 2. 等待物理链路真正建立
    #
    # 避免启动阶段:
    # RTNETLINK answers: Network is down
    # --------------------------------------------------------

    CARRIER="$(cat /sys/class/net/"$PARENT"/carrier 2>/dev/null || echo 0)"

    if [ "$CARRIER" != "1" ]; then
        return 0
    fi


    # --------------------------------------------------------
    # 3. VLAN99 不存在才创建
    # --------------------------------------------------------

    if ! ip link show dev "$VLAN_IF" >/dev/null 2>&1; then

        if ip link add \
            link "$PARENT" \
            name "$VLAN_IF" \
            type vlan id "$VLAN_ID"
        then
            log "Created $VLAN_IF VLAN $VLAN_ID on $PARENT"
        else
            log "ERROR: failed to create $VLAN_IF"
            return 0
        fi
    fi


    # UniFi 可能恰好在此时重构父接口
    if ! ip link show dev "$VLAN_IF" >/dev/null 2>&1; then
        return 0
    fi


    # --------------------------------------------------------
    # 4. MTU 不正确时才修改
    # --------------------------------------------------------

    CURRENT_MTU="$(cat /sys/class/net/"$VLAN_IF"/mtu 2>/dev/null || true)"

    if [ "$CURRENT_MTU" != "1500" ]; then

        if ip link set dev "$VLAN_IF" mtu 1500; then
            log "Set $VLAN_IF MTU to 1500"
        else
            return 0
        fi
    fi


    # --------------------------------------------------------
    # 5. Admin state DOWN 时才执行 set up
    #
    # /sys/.../flags bit 0 = IFF_UP
    # --------------------------------------------------------

    FLAGS="$(cat /sys/class/net/"$VLAN_IF"/flags 2>/dev/null || true)"

    if [ -n "$FLAGS" ]; then

        FLAGS_DEC=$((FLAGS))

        if (( (FLAGS_DEC & 1) == 0 )); then

            if ip link set dev "$VLAN_IF" up; then
                log "Brought $VLAN_IF up"
            else
                return 0
            fi
        fi
    fi


    # --------------------------------------------------------
    # 6. IPv4 地址不存在才添加
    #
    # 不执行 addr flush,避免删除其他地址。
    # --------------------------------------------------------

    if ! ip -4 -o addr show dev "$VLAN_IF" 2>/dev/null |
        awk '{print $4}' |
        grep -Fxq "$IP_ADDR"
    then

        if ip addr add "$IP_ADDR" dev "$VLAN_IF"; then
            log "Assigned $IP_ADDR to $VLAN_IF"
        else
            return 0
        fi
    fi


    # --------------------------------------------------------
    # 7. Connected route 检查
    #
    # 正常情况下 IP 添加后 Linux 自动建立:
    #
    # 192.168.0.0/24 dev eth6.99 proto kernel ...
    #
    # 只有 route 异常消失时才恢复。
    # --------------------------------------------------------

    if ! ip -4 route show "$SUBNET" dev "$VLAN_IF" 2>/dev/null |
        grep -q "src $SRC_IP"
    then

        if ip route replace "$SUBNET" \
            dev "$VLAN_IF" \
            src "$SRC_IP" 2>/dev/null
        then
            log "Restored route $SUBNET via $VLAN_IF"
        fi
    fi
}


trap 'exit 0' TERM INT

log "VLAN99 maintainer started"


while true; do

    ensure_vlan

    sleep "$INTERVAL" &
    wait $!

done
MAINTAIN_EOF

chmod 755 "$SCRIPT"

bash -n "$SCRIPT" || \
    die "vlan99-maintain.sh 语法检查失败"

echo "$SCRIPT : syntax OK"


# ============================================================
# 5. 创建正式 systemd service
# ============================================================

section "[5/8] 创建 ucg-vlan99.service"

cat >"$UNIT" <<'UNIT_EOF'
[Unit]
Description=UCG-Fiber VLAN99 Maintainer
After=network-pre.target
Wants=network-pre.target

[Service]
Type=simple

ExecStart=/data/ucg-vlan99/vlan99-maintain.sh

Restart=always
RestartSec=2

TimeoutStopSec=2
Nice=10

[Install]
WantedBy=multi-user.target
UNIT_EOF

chmod 644 "$UNIT"

echo "$UNIT created"


# ============================================================
# 6. 创建固件升级恢复脚本
# ============================================================

section "[6/8] 创建 on_boot 固件升级恢复脚本"

cat >"$BOOT" <<'BOOT_EOF'
#!/bin/bash

SCRIPT="/data/ucg-vlan99/vlan99-maintain.sh"
UNIT="/etc/systemd/system/ucg-vlan99.service"


# ------------------------------------------------------------
# 核心维护脚本不存在则不继续
# ------------------------------------------------------------

[ -x "$SCRIPT" ] || exit 0


# ------------------------------------------------------------
# 如果 UniFi OS 升级清除了 /etc 下的 unit,则重新创建
# ------------------------------------------------------------

if [ ! -f "$UNIT" ]; then

    cat >"$UNIT" <<'UNIT_EOF'
[Unit]
Description=UCG-Fiber VLAN99 Maintainer
After=network-pre.target
Wants=network-pre.target

[Service]
Type=simple

ExecStart=/data/ucg-vlan99/vlan99-maintain.sh

Restart=always
RestartSec=2

TimeoutStopSec=2
Nice=10

[Install]
WantedBy=multi-user.target
UNIT_EOF

    systemctl daemon-reload
fi


# ------------------------------------------------------------
# 如果 enable 状态丢失,自动恢复
# ------------------------------------------------------------

if ! systemctl is-enabled --quiet ucg-vlan99.service 2>/dev/null; then
    systemctl enable ucg-vlan99.service >/dev/null 2>&1 || true
fi


# ------------------------------------------------------------
# 如果 service 没运行,自动启动
# ------------------------------------------------------------

if ! systemctl is-active --quiet ucg-vlan99.service 2>/dev/null; then
    systemctl start --no-block ucg-vlan99.service || true
fi

exit 0
BOOT_EOF

chmod 755 "$BOOT"

bash -n "$BOOT" || \
    die "50-ucg-vlan99.sh 语法检查失败"

echo "$BOOT : syntax OK"


# ============================================================
# 7. 启用并启动正式服务
# ============================================================

section "[7/8] 启动 VLAN99"

systemctl daemon-reload

systemctl enable ucg-vlan99.service \
    >/dev/null 2>&1

systemctl restart ucg-vlan99.service


# 给 maintainer 若干检查周期
for i in 1 2 3 4 5 6; do

    if ip -4 -o addr show dev "$VLAN_IF" 2>/dev/null |
        awk '{print $4}' |
        grep -Fxq "$IP_ADDR"
    then
        break
    fi

    sleep 5

done


# ============================================================
# 8. 健康检查
# ============================================================

section "[8/8] 最终健康检查"


echo
echo "----- udm-boot -----"

echo "enabled : $(systemctl is-enabled udm-boot.service 2>/dev/null || true)"
echo "active  : $(systemctl is-active udm-boot.service 2>/dev/null || true)"

systemctl status udm-boot.service \
    --no-pager -l || true


echo
echo "----- on_boot directory -----"

ls -la /data/on_boot.d || true


echo
echo "----- VLAN99 service -----"

systemctl status ucg-vlan99.service \
    --no-pager -l || true


echo
echo "----- eth6 -----"

ip -d link show "$PARENT" || true

printf "carrier : "
cat /sys/class/net/"$PARENT"/carrier \
    2>/dev/null || echo "unknown"


echo
echo "----- eth6.99 -----"

ip -d link show "$VLAN_IF" || true


echo
echo "----- IPv4 -----"

ip -4 addr show dev "$VLAN_IF" || true


echo
echo "----- Route -----"

ip -4 route show "$SUBNET" || true


echo
echo "----- VLAN99 Journal -----"

journalctl \
    -b \
    -u ucg-vlan99.service \
    --since "-5 min" \
    --no-pager || true


echo
echo "----- Relevant dmesg -----"

dmesg |
    grep -E 'eth6\.99|link change request' |
    tail -30 || true


echo
echo "----- Error check -----"

WARNINGS="$(
    dmesg |
    grep -Ec \
        'link change request failed|Interface eth6\.99 may have been left' \
        || true
)"

NETWORK_DOWN="$(
    journalctl -b -u ucg-vlan99.service --no-pager 2>/dev/null |
    grep -c 'RTNETLINK answers: Network is down' \
        || true
)"

echo "link-change warnings : $WARNINGS"
echo "Network-is-down      : $NETWORK_DOWN"


echo
echo "============================================================"
echo " UCG-Fiber VLAN99 部署完成"
echo "============================================================"
echo
echo "Parent      : $PARENT"
echo "VLAN        : $VLAN_ID"
echo "Interface   : $VLAN_IF"
echo "IP          : $IP_ADDR"
echo
echo "Maintainer  : $SCRIPT"
echo "Service     : $UNIT"
echo "on_boot     : $BOOT"
echo "Backup      : $BACKUP"
echo
echo "udm-boot    : $(systemctl is-enabled udm-boot.service 2>/dev/null || true)"
echo "VLAN service: $(systemctl is-active ucg-vlan99.service 2>/dev/null || true)"
echo
echo "============================================================"

UCG_VLAN99_INSTALL

Configuring UCG-Fiber Dual WAN via DAC and CRS305 with a Single PON Stick (Shanghai Unicom)