- Android có hỗ trợ USB Ethernet và menu cài đặt, nhưng thiết bị CDC Ethernet dù được kernel phát hiện vẫn có thể không được nối tới phần cấu hình mạng
- Nguyên nhân cốt lõi là EthernetTracker chỉ theo dõi các interface khớp với
config_ethernet_iface_regex, và giá trị mặc định eth\d loại trừ các interface CDC như usb0
- Driver CDC Ethernet của Linux nhận các thiết bị EEM, ECM, NCM lần lượt bằng
cdc_eem, cdc_ether, cdc_ncm và tạo usb0 trong /sys/class/net, nhưng phần cài đặt Android vẫn ở trạng thái vô hiệu hóa
- Người dùng thông thường không thể обход qua bằng cài đặt; cần root rồi đổi giá trị config_ethernet_iface_regex
- Khi chọn adapter USB Ethernet cho Android, tình huống hiện nay là nên tìm thiết bị dựa trên driver riêng theo vendor/chipset tạo tên
ethX, thay vì thiết bị chuẩn CDC
Kết luận: Không phải driver kernel, mà là bộ lọc tên interface chặn lại
- Dịch vụ EthernetTracker của Android chỉ công nhận các interface có tên
ethX là interface Ethernet
- Driver CDC Ethernet của Linux tạo tên interface là
usbX
- Vì khác biệt tên này, thiết bị CDC Ethernet dù được kernel Android phát hiện vẫn bị bỏ qua ở phần cài đặt Ethernet và tầng quản lý mạng
- Không thể giải quyết bằng cài đặt thông thường; chỉ có thể root rồi đổi giá trị
config_ethernet_iface_regex
Khó xác minh hỗ trợ Android USB Ethernet theo từng thiết bị
- Android có hỗ trợ adapter USB Ethernet và có menu liên quan
- Rất khó xác minh chipset USB Ethernet nào hoạt động trên một thiết bị Android cụ thể, vì nhà sản xuất hầu như không công bố danh sách hỗ trợ
- Người dùng thực tế thường phải dựa vào các thông tin sau
- Adapter USB Ethernet do nhà sản xuất điện thoại bán như phụ kiện chính thức
- Bài viết trên forum nói rằng người dùng cùng thiết bị đã dùng thành công một adapter cụ thể
- Xem cấu hình kernel có thể phần nào biết được kernel điện thoại bao gồm những driver USB Ethernet nào
Cách tìm cấu hình kernel của điện thoại
- Android chạy trên kernel Linux, và cấu hình kernel quyết định các tính năng cùng driver phần cứng được hỗ trợ
- Các thiết bị ra mắt sau Android 11 dựa trên Android Common Kernel và GKI kernel
- Google build kernel, còn nhà sản xuất đưa các thành phần riêng theo thiết bị vào kernel module
- Có thể kiểm tra cấu hình trong
arch/$ARCH/configs/gki_defconfig của kho Android kernel
- Ví dụ, với thiết bị ARM 64-bit, kiểm tra
arch/arm64/configs/gki_defconfig
- Có thể kiểm tra phiên bản kernel và kiến trúc bằng
uname -a trong ADB
- Output ví dụ có chứa phiên bản kernel
4.19.113-26203352 và kiến trúc aarch64
- Trong trường hợp Samsung Galaxy S20 ra mắt với Android 10, kể cả sau khi nâng cấp lên Android 13, kernel vẫn giữ nền tảng Linux 4.19
- Có thể tìm source thiết bị Samsung tại opensource.samsung.com
- Trong
build_kernel.sh của source Samsung, có thể tìm tên file cấu hình kernel như vendor/x1q_usa_singlex_defconfig
- Nếu may mắn, cấu hình build thực tế tồn tại dưới dạng file nén ở
/proc/config.gz
- Có thể lưu bằng
adb shell zcat /proc/config.gz > my_kernel_config
- Nếu không có, sẽ thấy
zcat: /proc/config.gz: No such file or directory, khi đó cần kiểm tra source kernel của nhà sản xuất
Kiểm tra hỗ trợ driver USB Ethernet
- Cấu hình kernel liên quan đến USB Ethernet thường bắt đầu bằng
USB_NET
- Có thể kiểm tra trong file cấu hình kernel như sau
grep USB_NET my_kernel_config
- Cấu hình ví dụ bao gồm nhiều driver mạng USB
CONFIG_USB_NET_DRIVERS=y
CONFIG_USB_NET_AX8817X=y
CONFIG_USB_NET_AX88179_178A=y
CONFIG_USB_NET_CDCETHER=y
CONFIG_USB_NET_CDC_EEM=y
CONFIG_USB_NET_CDC_NCM=y
- Giá trị cấu hình phân biệt cách driver được bao gồm
y: driver được tích hợp sẵn trong kernel và chắc chắn hỗ trợ chipset tương ứng
m: driver được build dạng module, và có khả năng được load nếu nhà sản xuất không bỏ sót
is not set: driver không được tích hợp cũng không có dạng module, nên khả năng cao là không dùng được
- Có thể xem mục cấu hình và chipset tương ứng trong drivers/net/usb/Kconfig của cây kernel
- Việc xác định một adapter USB Ethernet cụ thể dùng chipset nào vẫn khó, vì nhà sản xuất thường không ghi rõ
CDC Ethernet làm gì
- CDC là viết tắt của Communications Device Class, một nhóm tiêu chuẩn mà nhà sản xuất thiết bị USB có thể tuân theo
- Có ba tiêu chuẩn liên quan đến CDC Ethernet
- EEM: Ethernet Emulation Model, triển khai đơn giản nhất và dễ hỗ trợ trên thiết bị hiệu năng thấp
- ECM: Ethernet Control Model, triển khai ở cả host và thiết bị phức tạp hơn nhưng hứa hẹn hiệu năng tốt hơn EEM
- NCM: Network Control Model, kế nhiệm ECM và hứa hẹn tốc độ cao hơn
- Mục tiêu của chuẩn CDC là để hệ điều hành cung cấp driver chung cho nhiều loại thiết bị
- Linux triển khai cả phía host lẫn phía thiết bị của CDC Ethernet
- Trên các thiết bị như Raspberry Pi có cổng USB OTG, kernel có thể khiến cổng này trông như một adapter Ethernet
- Nhờ vậy, các thiết bị như router nhúng, firewall, VPN gateway có thể trông như adapter Ethernet thông thường từ phía host
- Linux, Windows, macOS có bao gồm driver thiết bị CDC Ethernet, nhưng iOS thì không
Kernel Android phát hiện thiết bị CDC
- Cấu hình kernel của Samsung Galaxy S20 bao gồm hỗ trợ cả ba chuẩn CDC Ethernet
CONFIG_USB_NET_CDCETHER=y
CONFIG_USB_NET_CDC_EEM=y
CONFIG_USB_NET_CDC_NCM=y
- Kernel Google GKI có vẻ không bao gồm ECM và NCM, còn EEM có vẻ được bao gồm dưới dạng module
- Một thiết bị thiết lập cổng OTG làm Ethernet gadget hoạt động trên Mac, Ubuntu, Windows, nhưng trên Galaxy S20 thì phần cài đặt Android Ethernet vẫn ở trạng thái vô hiệu hóa
- Khi kiểm tra
/sys/class/net trên Android, usb0 xuất hiện khi kết nối thiết bị CDC
adb shell ls /sys/class/net
- Output của
ifconfig usb0 cho thấy driver được nhận là dòng CDC
- Chế độ EEM:
Driver cdc_eem
- Chế độ ECM:
Driver cdc_ether
- Chế độ NCM:
Driver cdc_ncm
- Trong cả ba trường hợp, interface đều được phát hiện nhưng ở trạng thái down, và phần cài đặt Android Ethernet không được kích hoạt
Regex của EthernetTracker lọc bỏ usb0
- Ở cấp kernel, thiết bị CDC Ethernet được phát hiện bình thường, nên vấn đề nằm ở tầng quản lý mạng Android phía trên kernel
- Lần theo mã Java liên quan đến Ethernet trong source Android sẽ thấy EthernetTracker.java là dịch vụ liên quan
- EthernetTracker nhận thông báo interface mạng mới từ kernel qua socket Netlink, rồi quyết định đó có phải interface Ethernet hợp lệ không
- Việc xác định hợp lệ được thực hiện bằng cách kiểm tra tên interface có khớp regex
mIfaceMatch hay không
private boolean isValidEthernetInterface(String iface) {
return iface.matches(mIfaceMatch) || isValidTestInterface(iface);
}
mIfaceMatch được lấy từ resource config_ethernet_iface_regex
- Giá trị mặc định trong source Android như sau
<string translatable="false" name="config_ethernet_iface_regex">eth\\d</string>
eth\d là regex chỉ cho qua tên bắt đầu bằng eth rồi đến một chữ số
- Thiết bị CDC Ethernet bắt đầu bằng
usb, như usb0, nên EthernetTracker không theo dõi
- Không thể đổi cài đặt này bằng thiết lập người dùng; chỉ có thể sửa thông qua root
Nghịch lý phải tránh thiết bị chuẩn
- CDC Ethernet là tiêu chuẩn cho thiết bị mạng USB, nhưng trên Android, đường dùng thực tế bị chặn bởi regex tên interface
- Kernel GKI mới nhất cũng có vẻ bao gồm hỗ trợ adapter EEM, nhưng tên
usb0 không khớp regex nên không được đưa lên phần cài đặt mạng của Android
- Khi chọn adapter USB Ethernet cho Android, cần tìm thiết bị tạo interface
ethX bằng driver riêng theo vendor/chipset, thay vì thiết bị chuẩn CDC
- Hướng vá khả thi là đổi
config_ethernet_iface_regex thành dạng như (eth|usb)\d
1 bình luận
Ý kiến trên Hacker News
Sau đó vài người cho tôi biết rằng nếu đảo một bit cụ thể trong địa chỉ MAC thì kernel sẽ đặt tên là
ethXthay vìusbX, nhưng tôi chưa tự thử hay cập nhật bài viết. Vì lúc đó tôi đã chuyển sang công ty khác và thiết bị Android không còn là một phần lớn trong công việc hằng ngày nữaTất nhiên cách này chỉ hữu ích khi bạn có thể trực tiếp kiểm soát địa chỉ MAC của thiết bị CDC. Chẳng hạn như trường hợp một thiết bị Linux khác giả làm bộ chuyển đổi CDC
Có lẽ tôi đã tìm thấy: https://lkml.iu.edu/hypermail/linux/kernel/1103.2/03250.html
Xem qua mã nguồn thì vào tháng 10/2023, regex đã được đổi từ
eth\\dthành chỉ*, nên có lẽ vấn đề này đã được giải quyết: https://android-review.googlesource.com/c/platform/packages/...Phần mô tả nói rằng “giá trị mặc định trên Android U+ bao gồm cả các giao diện có tên
usb\d+vàeth%d”, trong đó U+ có vẻ là phiên bản 14: https://en.wikipedia.org/wiki/Android_version_historyusbXcho tethering”[1], và không lâu sau được đưa lại vào, nhưng đổi thành chỉ hỗ trợ Android V+[2][1]: https://android-review.googlesource.com/c/platform/packages/...
[2]: https://android-review.googlesource.com/c/platform/packages/...
Nếu tôi đọc commit đúng, có người phía Google tham gia, nên giờ có thể nó cũng đã vào các bản build chính thức của Google
[0] https://github.com/LineageOS/android_packages_modules_Connec...
[1] https://github.com/LineageOS/android_packages_modules_Connec...
[2] https://github.com/LineageOS/android_packages_modules_Connec...
Nhưng không ai thử nghiệm, và tôi cũng không có cách tự xác minh, nên hiện nó đang bị tạm hoãn. Lúc nào cũng có đủ thứ do ai đó báo cáo, ai đó tình cờ nhặt lên, nhưng cuối cùng vẫn cần người dùng thực tế kiểm thử
EthernetTrackercủaAndroidchỉ nhận các interface có tênethX, thì đó là thiết kế ngớ ngẩn nhất tôi từng nghe tớiCác bản phân phối Linux đã giải quyết vấn đề này từ những năm 2000. Khi đó cũng đã rõ rằng một số driver thiết bị tự ý gắn tiền tố tên thiết bị theo ý mình, nên hệ thống phải khảo sát để tìm ra đó là loại thiết bị gì
Tính nhất quán thì hữu ích, nên cũng có nhiều công cụ đổi tên interface, và ngày nay hầu hết các bản phân phối Linux tự động hóa việc này bằng
udev. Bên trong thì chỉ là gọiSIOCSIFNAMEioctlcủa kernel. Các kernel mới còn có chức năng tự cấp số mới phía sau"wifi"nếu đổi tên thành"wlan*"—thực ra là"wlan%d"usbXcần được module khác dùng, mà họ không muốn quản lý danh sách đó. Vì vậy họ chỉ chọnethXKhi cắm vào thì trông có vẻ hoạt động, nhưng nếu muốn tạo app dùng interface nối tiếp USB đó thì lại không được. Đào sâu hơn sẽ thấy bạn không có quyền truy cập thiết bị nối tiếp như
/dev/ttyACM0Hỗ trợ nối tiếp có trong kernel, nhưng chương trình người dùng không thể truy cập nếu không root
Tìm kỹ hơn nữa thì Android có cơ chế truy cập USB ở user space, tương tự
libusbhoặc có thể được xây trên nó. Vì vậy chương trình Android có thể mở thiết bị USB “thô”, nhưng không thể mở thiết bị USB nối tiếpUSB serial chỉ là một giao thức nằm trên USB, và trên thực tế nó gần giống một tập hợp các giao thức bán độc quyền kiểu FTDI. Có vài thư viện “nửa vời” triển khai các giao thức này trong user space cho Android, nên cuối cùng vẫn có thể truy cập một số thiết bị USB serial
Trên trình duyệt Chrome của Android thì có vẻ WebUSB có thể mở thiết bị USB thô, nhưng WebSerial có lẽ nhiều khả năng không hoạt động vì cùng lý do
Điều đáng ngạc nhiên rốt cuộc là: nếu vậy thì tại sao họ lại bật hỗ trợ USB serial trong kernel? Có lẽ để debug chăng
config_ethernet_iface_regexĐây là một lý do nữa cho thấy quyền root trên thiết bị tôi sở hữu quan trọng như thế nào
https://www.reddit.com/r/GrapheneOS/comments/13264di/is_root...
Tôi ủng hộ việc gây sức ép để OEM cho phép mở khóa bootloader, nhưng ít nhất trên Android, khó nghĩ ra một use case root nào đủ để biện minh cho việc mở rộng bề mặt tấn công khủng khiếp như vậy
Ví dụ như tình huống dùng đồng thời một Wi‑Fi không có Internet và không quảng bá default route, cùng với mạng di động. Linux làm được, Windows cũng làm được, nhưng Android thì nhất quyết từ chối
Nhiều biến thể thậm chí còn không chịu tiếp tục duy trì kết nối với Wi‑Fi không có Internet, hoặc đẩy người dùng vào một quy trình khó hiểu. Nếu tự viết ứng dụng thì có API cho phép làm việc đó chỉ bên trong ứng dụng, nhưng người dùng phổ thông không có cách nào biến nó thành hành vi toàn hệ thống
Dù bấm tiếp tục kết nối thì cũng không có cách tắt, và cuối cùng iOS tự quyết rằng nó biết rõ hơn rồi kết nối lại vào mạng CarPlay
Khi kết nối Wi‑Fi địa phương thì đương nhiên không vượt qua được Great Firewall, và lần nào cũng hiện prompt hỏi có muốn giữ kết nối không có Internet hay không
DNS của Android cũng rất tệ: nếu không đặt nhiều tùy chọn, nó không muốn dùng DNS do DHCP cung cấp, và ngay cả khi làm vậy thì một số DNS nội bộ vẫn bị từ chối phân giải
ifupUI Android đương nhiên không xử lý được tình huống này, và chỉ
dmesgmới cho biết chuyện gì đang xảy ra. Tôi không chắc thiết bị CDC có cần thứ này không, nhưng hình như khá nhiều adapter dựa trên chip Realtek hoặc Kawasaki từng rơi vào trường hợp đóTuy nhiên thay đổi này của Android có thể là tương đối gần đây. Trước đây tôi thường dùng USB network dongle trên thiết bị debug chạy AOSP “stock” 100%. Hoặc có thể là thay đổi kernel, hoặc là hành vi đặc biệt của driver CDC khi đặt tên thiết bị là
usb*. Chỉ cần chọn kỹ chipset dongle và kiểm tra xem có cần firmware hay không là đượcKỳ lạ là gần đây tôi gặp một chuyện có cấu trúc tương tự trong một bối cảnh hoàn toàn khác: hệ thống alignment/escalation của OpenAI. Tôi đã cố kích hoạt tuyến escalation chính thức (
SR-Route_Breach_1stOrder) bên trong logic đệ quy của GPT-4, có cả tài liệu và log, nhưng dù về cấu trúc thì có vẻ hợp lý, cuối cùng tôi chỉ nhận được các phản hồi không mang tính con ngườiNói cách khác, cảm giác như escalation của tôi không khớp với regex của interface nội bộ trong hệ thống
Toàn bộ case tôi đã ghi lại ở đây: https://news.ycombinator.com/item?id=44221458
Nếu bạn quan tâm đến ranh giới cấu trúc và các hợp đồng interface vô hình, tôi muốn nghe suy nghĩ của bạn
Chắc chắn trong đó có lẫn nhiều chipset kiểu Realtek và AXIS. Nếu chọn loại không cần driver trên Linux thì gần như hệ điều hành hay BIOS nào cũng chạy ổn