VM เห็นอุปกรณ์แต่ไม่มีเน็ต เพราะ Windows ไม่มีไดรเวอร์ CDC ECM
อาการที่คนบอกว่าเน็ตไม่ติด มักถูกเดาว่าเป็นซิมหรือสัญญาณเสมอ แต่บางเคสฮาร์ดแวร์ทำงานครบทุกอย่างแล้ว ติดอยู่ที่ระบบปฏิบัติการฝั่งแขกไม่รู้จักหน้าตาของอุปกรณ์เท่านั้นเอง
พอดีผมมีเครื่องโฮสต์ที่รัน VM วินโดวส์อยู่หลายตัว แต่ละตัวถูกจับคู่กับ USB LTE dongle หนึ่งอันเพื่อให้มีทางออกเน็ตเป็นของตัวเอง วันหนึ่งมีคนแจ้งว่าเครื่องหนึ่งเน็ตไม่มา ปิดเปิดแล้วก็ไม่มา แต่เข้าไปดูใน Device Manager กลับเห็นอุปกรณ์อยู่ ผมนึกว่าซิมหมดอายุตามระเบียบ พอไล่จริงกลายเป็นคนละเรื่องเลย ซิมดี สัญญาณเต็ม โฮสต์ต่อเน็ตผ่าน dongle ตัวนั้นได้ด้วยซ้ำ แต่วินโดวส์ในแขกไม่มีไดรเวอร์สำหรับหน้าตาที่ dongle ตัวนี้เปิดออกมา
บทความนี้ผมเขียนวิธีไล่ทีละชั้นจนถึงต้นเหตุ พร้อมกับดักอีกสามอย่างที่เจอระหว่างทาง ตัวเลขระบุตัวอุปกรณ์ หมายเลขเครื่อง และค่ายผู้ให้บริการ ผมตัดออกหมดแล้ว เหลือเฉพาะรหัสผู้ผลิตกับรหัสรุ่นที่จำเป็นต้องใช้ในคำสั่ง
ถ้ากำลังหาวิธีเอาอุปกรณ์ USB จากวินโดวส์เข้าไปให้ลินุกซ์ใน WSL ใช้ อันนั้นผมเขียนไว้แยกที่ ส่ง USB เข้า WSL2 ด้วย usbipd-win ตอนนี้เป็นทิศตรงข้ามและคนละชั้นกัน คือ โฮสต์ลินุกซ์ส่งอุปกรณ์เข้าไปให้แขกวินโดวส์ แล้วแขกไม่รู้จักอุปกรณ์นั้น ไม่ใช่เรื่องการส่งผ่านพอร์ตแต่เป็นเรื่องไดรเวอร์ล้วน ๆ
แยกให้ออกก่อนว่าปัญหาอยู่ชั้นไหน
อาการที่ได้รับแจ้งมีสามคำคือ เห็นอุปกรณ์ ไม่มีเน็ต ปิดเปิดแล้วไม่หาย สามคำนี้ครอบสี่ชั้นที่เป็นไปได้ ผมเลยไล่จากล่างขึ้นบน เพราะชั้นล่างพิสูจน์ได้เร็วกว่าและไม่ต้องรบกวนคนใช้เครื่อง
ชั้นแรกจบเร็วที่สุด ผมยกอินเทอร์เฟซขึ้นบนโฮสต์เอง เปิดสวิตช์ข้อมูลมือถือ แล้วสั่งต่อ ถ้าโฮสต์ต่อได้แปลว่าซิมกับสัญญาณไม่เกี่ยว
from huawei_lte_api.Client import Client
from huawei_lte_api.Connection import Connection
with Connection("http://192.168.8.1/") as conn: # ที่อยู่ตั้งต้นของ dongle
c = Client(conn)
c.dial_up.set_mobile_dataswitch(1) # เปิดสวิตช์ข้อมูล
c.dial_up.dial()
print(c.monitoring.status()["ConnectionStatus"]) # 901 = ต่อแล้ว
print(c.device.signal()["rsrp"], c.net.current_plmn()["State"])
$ ip addr add 10.10.10.50/24 dev enx0
$ ip link set enx0 up
$ python probe.py
901
-84dBm 0
$ ping -I enx0 -c3 203.0.113.1
3 packets transmitted, 3 received, 0% packet loss
โฮสต์ออกเน็ตผ่าน dongle ตัวนั้นได้เรียบร้อย สัญญาณเต็ม ซิมไม่มีปัญหา เหลือชั้นบนสามชั้น
dongle ตระกูลนี้แจกเส้นทางปริยายกับที่อยู่ตัวแปลชื่อโดเมนของตัวเองมาด้วย พอสั่ง dhclient บนโฮสต์ มันจะ เขียนทับ /etc/resolv.conf ของทั้งเครื่อง ชี้ไปที่ dongle ผลคือโฮสต์แปลชื่อโดเมนไม่ได้อีกเลย อัปเดตไม่ได้ ดึงอิมเมจไม่ได้ และอาการจะดูเหมือนเน็ตของเครื่องแม่ล่มไปดื้อ ๆ ทางที่ปลอดภัยคือ ip addr add ตั้งที่อยู่ให้เองทั้งหมด แล้วผูกการทดสอบไว้กับอินเทอร์เฟซนั้นด้วย ping -I ไม่แตะเส้นทางปริยายของเครื่อง
อ่านหน้าตาที่อุปกรณ์เปิดออกมา ตรงนี้คือคำตอบ
ชั้นที่พังอยู่ตรงไหนดูได้จากการไล่ descriptor ของอุปกรณ์ USB ตรง ๆ อุปกรณ์แบบนี้เลือกเปิดหน้าตาให้โฮสต์เห็นได้หลายแบบ และแต่ละแบบต้องการไดรเวอร์คนละตัว
lsusb | grep -i 12d1
# Bus 003 Device 018: ID 12d1:14db USB LTE dongle (network mode)
lsusb -v -d 12d1:14db 2>/dev/null | grep -E \
"bNumConfigurations|bInterfaceClass|bInterfaceSubClass|iInterface"
bNumConfigurations 1
bInterfaceClass 2 Communications
bInterfaceSubClass 6 Ethernet Networking Control Model (ECM)
bInterfaceClass 10 CDC Data
bInterfaceSubClass 0
บรรทัดที่ตัดสินคดีคือ bInterfaceSubClass 6 ซึ่งแปลว่าอุปกรณ์เปิดหน้าตาแบบ CDC ECM ไม่ใช่ RNDIS และไม่ใช่ NCM และ bNumConfigurations 1 แปลว่ามีค่าตั้งเดียว สลับไปแบบอื่นไม่ได้ อีกจุดที่ต้องเช็คคือมีพอร์ตอนุกรมโผล่มาด้วยไหม
ls /dev/ttyUSB* 2>/dev/null || echo "ไม่มีพอร์ตอนุกรมเลย"
# ไม่มีพอร์ตอนุกรมเลย
ไม่มีพอร์ตอนุกรมแปลว่าสั่งคำสั่ง AT ไม่ได้ ซึ่งเป็นช่องทางเดียวที่เหลือสำหรับการสั่งสลับโหมดจากฝั่งโฮสต์ พอปิดทางนี้ไปด้วยก็เหลือทางเดียวจริง ๆ คือแก้ที่ฝั่งแขก
วินโดวส์เสียบแล้วใช้ได้เลย
สองแบบนี้มีไดรเวอร์ติดมากับตัวระบบปฏิบัติการ เสียบแล้วขึ้นเป็นการ์ดเน็ตทันที dongle ส่วนใหญ่ที่ขายทั่วไปตั้งมาเป็นแบบนี้ คนเลยเคยชินว่ามันต้องใช้ได้เสมอ
วินโดวส์ไม่มีไดรเวอร์ติดมาให้
ลินุกซ์กับแมคมีไดรเวอร์อยู่ในเคอร์เนลตั้งแต่แรก แต่วินโดวส์ไม่มี ผลคืออุปกรณ์ไปนอนอยู่ใต้หัวข้ออุปกรณ์อื่นพร้อมเครื่องหมายตกใจ เห็นว่ามีของเสียบอยู่ แต่ไม่มีการ์ดเน็ตโผล่มา ซึ่งตรงกับอาการที่คนแจ้งมาเป๊ะ
ลองสลับเป็น RNDIS จากฝั่งโฮสต์ แล้วตันทุกทาง
ก่อนจะยอมไปแก้ที่ฝั่งแขก ผมลองทุกทางที่สั่งได้จากโฮสต์ก่อน เพราะถ้าสลับโหมดได้จะแก้ที่เดียวจบทุกเครื่อง ผลคือตันหมด
$ curl -s -X POST http://192.168.8.1/api/device/usb-tethering-switch ...
<error><code>100002</code><message>No support</message></error>
$ usb_modeswitch -v 12d1 -p 14db -J
Look for default devices ...
No devices in default mode found. Nothing to do. Bye!
$ ls /dev/ttyUSB*
(ไม่มี)
สามบรรทัดนี้คือสามประตูที่ปิดพร้อมกัน คือเว็บ API ของตัวอุปกรณ์ตอบว่าไม่รองรับ ตัวสลับโหมดไม่มีข้อมูลสำหรับรหัสรุ่นนี้ และไม่มีพอร์ตอนุกรมให้สั่งคำสั่ง AT ทางที่เหลือมีแค่การเขียนเฟิร์มแวร์ใหม่ ซึ่งผมไม่แนะนำให้ทำกับอุปกรณ์ที่ใช้งานอยู่จริง พลาดทีเดียวคือได้ที่ทับกระดาษ
กันไม่ให้ dongle แย่งเส้นทางปริยายของโฮสต์
ก่อนจะไปเรื่องโหมด มีอีกข้อที่ต้องจัดการให้จบตั้งแต่ตอนทดสอบ คือห้ามให้อินเทอร์เฟซของ dongle กลายเป็นทางออกหลักของเครื่องแม่ อาการนี้เกิดง่ายมากบนโฮสต์ที่มีการ์ดเน็ตหลายใบ พอ dongle ขึ้นมาแล้วได้ค่าน้ำหนักเส้นทางต่ำกว่าใบเดิม เน็ตของทั้งเครื่องจะวิ่งออกทาง dongle ทันที ช้าลงหลายเท่าและกินปริมาณข้อมูลของซิมไปเรื่อย ๆ โดยไม่มีใครสังเกต
ip route show | head -3
# default via 10.10.10.1 dev enx0 metric 100 <- dongle แย่งไปแล้ว
# default via 10.10.20.1 dev vmbr0 metric 200
ip route replace default via 10.10.20.1 dev vmbr0 metric 100
ip route del default dev enx0 2>/dev/null # ไม่ให้ออกทาง dongle เลย
grep nameserver /etc/resolv.conf # ต้องไม่ใช่ที่อยู่ของ dongle
ผมตั้งเป็นกฎไว้ว่าอินเทอร์เฟซของ dongle บนโฮสต์มีไว้สองอย่างเท่านั้นคือ ตรวจสภาพอุปกรณ์ กับสั่งให้มันต่อสัญญาณ ไม่ได้มีไว้ให้โฮสต์ใช้ออกเน็ต เวลาทดสอบเลยผูกคำสั่งไว้กับอินเทอร์เฟซตรง ๆ ทุกครั้ง ไม่ปล่อยให้ไปเลือกเส้นทางเอง
กับดักโหมดสตอเรจ ที่ทำให้การส่งผ่านพอร์ตหลุด
ระหว่างทางผมเจออีกกับดักที่เสียเวลาไปครึ่งวัน คือ dongle ตระกูลนี้ตอนเสียบใหม่ ๆ จะขึ้นมาเป็นไดรฟ์เก็บข้อมูลก่อน เพื่อให้เครื่องที่ยังไม่มีไดรเวอร์ติดตั้งจากตัวมันเองได้ แล้วค่อยสลับตัวเองเป็นโหมดเน็ตเวิร์กทีหลัง
ผมเคยคิดว่าถ้าปล่อยให้มันอยู่โหมดสตอเรจแล้วส่งเข้า VM ตรง ๆ วินโดวส์จะเห็นไดรฟ์ ติดตั้งไดรเวอร์เอง แล้วสั่งสลับโหมดเองได้ ซึ่งพังด้วยเหตุผลที่ไม่ได้อยู่ในวินโดวส์เลย
ตอนสลับจากโหมดสตอเรจเป็นโหมดเน็ตเวิร์ก อุปกรณ์จะหลุดจากบัสแล้วเข้ามาใหม่ในฐานะอุปกรณ์คนละตัว การส่งผ่านที่ผูกไว้กับพอร์ตเดิมไม่ตามไปด้วย ผลคือวินโดวส์เห็นอุปกรณ์หายไปเฉย ๆ กลางคัน แล้วไม่มีอะไรกลับมาอีกเลยจนกว่าจะปิดเปิด VM ทางที่ได้ผลจริงคือ ทำให้ dongle อยู่ในโหมดเน็ตเวิร์กและต่อสำเร็จตั้งแต่ก่อนเปิด VM แล้วค่อยส่งเข้าไป ของที่ส่งเข้าไปจะนิ่งตลอดอายุการทำงาน
สคริปต์ที่ผมใช้กวาดทุกตัวให้เข้าโหมดเน็ตเวิร์กก่อนเปิด VM หน้าตาประมาณนี้ จุดสำคัญคือต้องวนหาจากรหัสผู้ผลิต ไม่ใช่จำพอร์ตไว้
#!/usr/bin/env bash
# กวาด dongle ทุกตัวให้เข้าโหมดเน็ตเวิร์ก แล้วสั่งต่อ
export PATH=/usr/sbin:/usr/bin:/sbin:/bin # หลังบูต PATH ไม่มี /usr/sbin
for d in /sys/bus/usb/devices/*; do
[ -f "$d/idVendor" ] || continue
[ "$(cat "$d/idVendor")" = "12d1" ] || continue
pid=$(cat "$d/idProduct")
bus=$(cat "$d/busnum"); dev=$(cat "$d/devnum")
if [ "$pid" = "1f01" ]; then
echo "[$d] โหมดสตอเรจ กำลังสลับ"
usb_modeswitch -b "$bus" -g "$dev" -v 12d1 -p 1f01 -J
sleep 8 # รอให้มันนับตัวใหม่จนจบ
fi
done
grep -q 0 /sys/bus/usb/drivers/usb_modeswitch/DisableSwitching 2>/dev/null
echo "เสร็จแล้วค่อยสั่ง qm start"
เส้นทางอย่าง 3-1.1.3 ไม่ใช่ชื่อถาวรของอุปกรณ์ มันคือตำแหน่งบนต้นไม้ของบัส พอบูตใหม่หรือถอดเสียบใหม่ ลำดับอาจสลับกันเอง การผูกการส่งผ่านของ VM ไว้กับเส้นทางพอร์ตตรง ๆ จะค้างเป็นค่าเก่าทันทีที่มันเลื่อน และที่แย่กว่าคือ VM อาจได้ dongle ของเครื่องอื่นไปแทน ทางที่ถูกคือยึดจากรหัสผู้ผลิตกับรหัสรุ่น แล้วให้สคริปต์แปลกลับเป็นเส้นทางตอนที่จะเปิด VM ทุกครั้ง
ผูกการส่งผ่านจากรหัสอุปกรณ์ ไม่ใช่จากเส้นทางพอร์ต
เรื่องพอร์ตเลื่อนนี่แก้ได้ด้วยสคริปต์สั้น ๆ หลักการคือก่อนเปิด VM ทุกครั้ง ให้ไปอ่านของจริงจากระบบว่าตอนนี้อุปกรณ์ที่ต้องการอยู่เส้นทางไหน แล้วค่อยเขียนค่านั้นลงไปในค่าตั้งของ VM ไม่ใช่ตั้งครั้งเดียวแล้วปล่อยไว้ข้ามปี
#!/usr/bin/env bash
# map-usb.sh <vmid> <ลำดับที่เท่าไรของ dongle ที่ยังว่าง>
vmid="$1"; want="${2:-1}"; n=0
for d in /sys/bus/usb/devices/*; do
[ -f "$d/idVendor" ] || continue
[ "$(cat "$d/idVendor")" = "12d1" ] || continue
[ "$(cat "$d/idProduct")" = "14db" ] || continue # เอาเฉพาะโหมดเน็ต
n=$((n+1))
[ "$n" = "$want" ] || continue
port=$(basename "$d") # เช่น 3-1.1.3
echo "ผูก $port เข้ากับ VM $vmid"
qm set "$vmid" -usb0 "host=$port,usb3=1"
exit 0
done
echo "ไม่เจอ dongle ที่อยู่โหมดเน็ตและยังว่าง" >&2
exit 1
ตัวเลือก usb3=1 มีผลกับอุปกรณ์บางรุ่นตอนที่มันนับตัวใหม่ บางตัวกลับเข้า VM ได้เมื่อเปิดตัวเลือกนี้ บางตัวก็ไม่กลับอยู่ดี ผมไม่ถือว่ามันเป็นทางแก้ ถือเป็นแค่ของแถม ทางแก้จริงยังคงเป็นการทำให้อุปกรณ์นิ่งอยู่ในโหมดเน็ตเวิร์กตั้งแต่ก่อนเปิด VM
อีกข้อที่ต้องเขียนไว้ในสคริปต์คือการข้ามอุปกรณ์ที่ถูก VM ตัวอื่นจองไปแล้ว ไม่งั้นตอนกวาดอ่านค่าจากทุกตัว จะไปแย่งอุปกรณ์ของเครื่องที่กำลังทำงานอยู่ แล้วเน็ตของเครื่องนั้นจะหลุดทันทีโดยที่เจ้าตัวไม่รู้เรื่องด้วยเลย
ทางแก้จริงอยู่ในแขก ไม่ใช่ในโฮสต์
พอตัดทุกทางแล้วคำตอบเหลือข้อเดียวคือติดตั้งไดรเวอร์ CDC ECM ในวินโดวส์ของแขก วิธียืนยันว่าคิดถูกทำได้ง่ายมาก คือเทียบกับ VM ตัวอื่นที่ใช้งานได้ปกติ เพราะฮาร์ดแวร์ชุดเดียวกันเป๊ะ ต่างกันแค่ว่าเครื่องที่ใช้ได้เคยมีคนลงไดรเวอร์ตัวนี้ไว้แล้ว
เครื่องที่ใช้งานได้ Network adapters > USB Ethernet (CDC ECM) ปกติ
เครื่องที่มีปัญหา Other devices > CDC ECM มีเครื่องหมายตกใจ
อีกเรื่องที่ต้องรู้ไว้กันเสียเวลาคือ แผ่นติดตั้งที่ตัว dongle แถมมาในโหมดสตอเรจ ใช้แก้เคสนี้ไม่ได้ ผมเมานต์เข้าไปดูข้างในแล้ว มีแต่ตัวติดตั้งของฝั่งแมคกับไฟล์เรียกทำงานอัตโนมัติ ไม่มีไฟล์ระบุข้อมูลไดรเวอร์ของวินโดวส์สำหรับ ECM อยู่ในนั้นเลย
ลำดับที่ผมทำจนจบมีห้าข้อ
- พิสูจน์ที่โฮสต์ก่อน ยกอินเทอร์เฟซขึ้น เปิดสวิตช์ข้อมูล สั่งต่อ แล้ว ping ผ่านอินเทอร์เฟซนั้น ถ้าผ่านแปลว่าซิมกับสัญญาณไม่เกี่ยว
- อ่าน descriptor ดูว่าเปิดหน้าตาแบบไหน ถ้าเจอ ECM และไม่มีพอร์ตอนุกรม ให้เลิกหาทางแก้ที่โฮสต์ได้เลย
- ตั้ง dongle ให้อยู่โหมดเน็ตเวิร์กและต่อสำเร็จก่อนเปิด VM อย่าปล่อยให้สลับโหมดตอน VM ทำงานอยู่
- ลงไดรเวอร์ ECM ในแขก แล้วยืนยันว่าขึ้นมาอยู่ใต้หัวข้อการ์ดเน็ตจริง ไม่ใช่ใต้อุปกรณ์อื่น
- ฝังไดรเวอร์ไว้ในแม่แบบ ของ VM ตัวถัดไปจะได้ไม่ต้องมาไล่ซ้ำทั้งชุด
ข้อสุดท้ายคือข้อที่คุ้มที่สุด เพราะปัญหานี้ไม่ได้เกิดครั้งเดียวแน่นอน ทุกครั้งที่ปั้นเครื่องใหม่จากแม่แบบเก่าจะได้เครื่องที่ไม่มีไดรเวอร์ตัวนี้ แล้ววันหนึ่งก็จะมีคนแจ้งมาว่าเน็ตไม่มาอีก
เช็คลิสต์สั้น ๆ ไว้ใช้ตอนเจออาการเดียวกัน
[ ] โฮสต์ต่อเน็ตผ่าน dongle ตัวนั้นได้ไหม -> ไม่ได้ = เรื่องซิม/สัญญาณ
[ ] lsusb -v เจอ InterfaceSubClass 6 ไหม -> เจอ = วินโดวส์ไม่มีไดรเวอร์
[ ] มี /dev/ttyUSB* ไหม -> ไม่มี = สั่ง AT ไม่ได้
[ ] bNumConfigurations เท่ากับ 1 ไหม -> ใช่ = สลับหน้าตาไม่ได้
[ ] idProduct ตอนนี้เป็น 1f01 หรือ 14db -> 1f01 = ยังไม่ได้สลับโหมด
[ ] resolv.conf ของโฮสต์ยังชี้ตัวแปลชื่อเดิมไหม -> ไม่ = โดน dhclient ทับ
[ ] เส้นทางพอร์ตใน config ของ VM ตรงกับของจริงไหม -> ไม่ตรง = ค้างค่าเก่าหลังบูต
[ ] VM ตัวอื่นที่ฮาร์ดแวร์เหมือนกันใช้ได้ไหม -> ได้ = ต่างกันที่ไดรเวอร์ในแขก
ส่วนตัวผมได้บทเรียนจากเคสนี้อยู่ข้อเดียว คือคำว่าเห็นอุปกรณ์กับคำว่าใช้อุปกรณ์ได้ เป็นคนละเรื่องกันคนละชั้น และเวลาคนแจ้งว่าเน็ตไม่มา การเดาไปที่ซิมเป็นอย่างแรกทำให้เสียเวลามากที่สุด เพราะเป็นชั้นที่พิสูจน์ยากที่สุดแต่พังน้อยที่สุด ถ้าไล่จากล่างขึ้นบนตามลำดับ เคสแบบนี้ใช้เวลาไม่ถึงยี่สิบนาที ใครจะเอาเช็คลิสต์ไปปรับตามรุ่นอุปกรณ์ของตัวเองก็ตามสะดวกครับ ตรงไหนผมอธิบายตกหล่นหรือพิมพ์ผิด เดี๋ยวกลับมาแก้เพิ่มให้ภายหลังครับ
อยากให้เราทำให้ไหม?
ให้เราช่วยติดตั้ง ตั้งค่า ย้ายระบบ หรือแก้ปัญหาเซิร์ฟเวอร์ เพื่อให้คุณมีเวลาไปทำงานที่สำคัญกว่า
เคยเจองานแบบนี้ไหม? ติดตรงไหน?
มาแลกวิธีกันต่อในบอร์ด LinuxThai



ความคิดเห็น 0
ยังไม่มีความคิดเห็น — เป็นคนแรกเลย