As we all know, I usually use the ArchLinux operating system, which is a rolling release version, meaning that various software packages are updated quickly.
However, I suddenly discovered that the USB tethering feature on Android phones has a bug.
After some troubleshooting, I found that it was a bug caused by the Linux kernel. Alas, there was no choice but to modify the kernel code myself to fix the bug.
This article will introduce this relatively fresh Linux kernel bug!
This is Poor Little Water Drop, focusing on low-cost technology that is friendly to the poor. (This is work number 69.)
Related articles:
- • “Wandering Linux: Installing ArchLinux on External USB SSD”
- • “Wandering ArchLinux Follow-up: Fixing fstrim on USB SSD”
- • “Low Power Low Cost PC (Replaceable Memory) Recommendations (Laptops, Mini PCs)”
References:
- • https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/drivers/net/usb/rndis_host.c?h=v6.12.23
- • https://patchwork.kernel.org/project/netdevbpf/patch/[email protected]/
- • https://bbs.archlinux.org/viewtopic.php?id=304892
- • https://wiki.archlinux.org/title/Kernel/Arch_build_system
Table of Contents
- • 1 Bug Analysis (Fault Description)
- • 1.1 Android udev Rules
- • 1.2 Kernel rndis_host Module
- • 2 Solution: Compile Linux Kernel Yourself (ArchLinux)
- • 3 Test Results
- • 4 Summary and Outlook
1 Bug Analysis (Fault Description)
Connect the Android phone to the PC host using a USB data cable, and then enable the USB Tethering feature on the phone:
Then a USB network card will appear on the PC host:
> ip link
省略
6: wwp5s0f3u2: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/ether f2:a2:ef:01:02:03 brd ff:ff:ff:ff:ff:ff
altname wwxf2a2ef010203
Hmm? What is this situation?? <span>wwp</span>
starting network card is what?
When it was working normally before, the network card that appeared started with <span>enp</span>
, like:
2: enp1s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP mode DEFAULT group default qlen 1000
link/ether 84:47:09:01:02:03 brd ff:ff:ff:ff:ff:ff
altname enx844709010203
Kernel version:
> uname -a
Linux SC202501C7LA 6.14.2-zen1-1-zen #1 ZEN SMP PREEMPT_DYNAMIC Thu, 10 Apr 2025 18:43:47 +0000 x86_64 GNU/Linux
1.1 Android udev Rules
In the Linux system, <span>udev</span>
is used to handle hardware hot-plugging. Could it be a problem here?
> pacman -Ss android-udev
extra/android-udev 20250314-1 [installed]
Udev rules to connect Android devices to your linux box
> pacman -Ql android-udev
android-udev /usr/
android-udev /usr/lib/
android-udev /usr/lib/sysusers.d/
android-udev /usr/lib/sysusers.d/android-udev.conf
android-udev /usr/lib/udev/
android-udev /usr/lib/udev/rules.d/
android-udev /usr/lib/udev/rules.d/51-android.rules
The <span>android-udev</span>
package is about the handling rules for Android devices. Let’s take a look at the <span>/usr/lib/udev/rules.d/51-android.rules</span>
file:
> cat /usr/lib/udev/rules.d/51-android.rules
省略
# XiaoMi
ATTR{idVendor}!=\"2717\", GOTO=\"not_XiaoMi\"
省略
# Redmi Note 3 (ff08=adb)
# Mi/Redmi (ff10=ptp ff18=ptp,adb ff40=mtp ff48=mtp,adb ff80=rndis ff88=rndis,adb)
# Mi Mix / A1 (ff18=ptp,adb ff28=storage,adb ff48=mtp,adb ff88=rndis,adb)
ATTR{idProduct}==\"ff08\", GOTO=\"adb\"
ATTR{idProduct}==\"ff18\", GOTO=\"adbptp\"
ATTR{idProduct}==\"ff28\", GOTO=\"adbmass\"
ATTR{idProduct}==\"ff40\", GOTO=\"mtp\"
ATTR{idProduct}==\"ff48\", GOTO=\"adbmtp\"
ATTR{idProduct}==\"ff88\", GOTO=\"adbrndis\"
省略
# ADB Debug and Tether mode
LABEL=\"adbrndis\", ENV{adb_adb}=\"yes\"
LABEL=\"rndis\", ENV{adb_user}=\"yes\", GOTO=\"android_usb_rule_match\"
省略
# Symlink common code to reduce steps above
LABEL=\"android_usb_rule_match\"
ENV{adb_adbcdc}==\"yes\", ENV{adb_adb}=\"yes\", SYMLINK+=\"android_cdc\", SYMLINK+=\"android_cdc%n\"
ENV{adb_adbfast}==\"yes\", ENV{adb_adb}=\"yes\", ENV{adb_fast}=\"yes\"
ENV{adb_adbmass}==\"yes\", ENV{adb_mass}=\"yes\"
ENV{adb_adbmtp}==\"yes\", ENV{adb_adb}=\"yes\", ENV{adb_mtp}=\"yes\"
ENV{adb_adbptp}==\"yes\", ENV{adb_adb}=\"yes\", ENV{adb_ptp}=\"yes\"
ENV{adb_adbmidi}==\"yes\", ENV{adb_adb}=\"yes\", SYMLINK+=\"android_midi\", SYMLINK+=\"android_midi0%n\"
ENV{adb_adbuvc}==\"yes\", ENV{adb_adb}=\"yes\", ENV{adb_uvc}=\"yes\"
ENV{adb_adb}==\"yes\", ENV{adb_user}=\"yes\", SYMLINK+=\"android_adb\"
ENV{adb_fast}==\"yes\", SYMLINK+=\"android_fastboot\"
ENV{adb_mass}==\"yes\", ENV{adb_mtp}=\"yes\"
ENV{adb_ptp}==\"yes\", ENV{adb_user}=\"yes\", ATTR{bDeviceClass}==\"00|02|06|ef|ff\", ENV{adb_mtp}=\"yes\"
ENV{adb_mtp}==\"yes\", ENV{adb_user}=\"yes\", SYMLINK+=\"libmtp-%k\", ENV{ID_MTP_DEVICE}=\"1\", ENV{ID_MEDIA_PLAYER}=\"1\"
It can be seen that the function handling USB tethering is called <span>rndis</span>
.
Listen to udev events while operating on the phone to enable the USB tethering feature:
> udevadm monitor --udev
monitor will print the received events for:
UDEV - the event which udev sends out after rule processing
UDEV [18943.738720] unbind /devices/pci0000:00/0000:00:08.1/0000:05:00.3/usb1/1-2/1-2:1.0 (usb)
UDEV [18943.739269] remove /devices/pci0000:00/0000:00:08.1/0000:05:00.3/usb1/1-2/1-2:1.0 (usb)
UDEV [18943.859917] unbind /devices/pci0000:00/0000:00:08.1/0000:05:00.3/usb1/1-2 (usb)
UDEV [18943.860471] remove /devices/pci0000:00/0000:00:08.1/0000:05:00.3/usb1/1-2 (usb)
UDEV [18944.383830] add /devices/pci0000:00/0000:00:08.1/0000:05:00.3/usb1/1-2 (usb)
UDEV [18944.384734] add /devices/pci0000:00/0000:00:08.1/0000:05:00.3/usb1/1-2/1-2:1.0 (usb)
UDEV [18944.385587] add /devices/pci0000:00/0000:00:08.1/0000:05:00.3/usb1/1-2/1-2:1.1 (usb)
UDEV [18944.386854] bind /devices/pci0000:00/0000:00:08.1/0000:05:00.3/usb1/1-2/1-2:1.1 (usb)
UDEV [18944.391621] add /devices/pci0000:00/0000:00:08.1/0000:05:00.3/usb1/1-2/1-2:1.0/net/wwp5s0f3u2 (net)
UDEV [18944.392288] add /devices/pci0000:00/0000:00:08.1/0000:05:00.3/usb1/1-2/1-2:1.0/net/wwan0/queues/rx-0 (queues)
UDEV [18944.392434] add /devices/pci0000:00/0000:00:08.1/0000:05:00.3/usb1/1-2/1-2:1.0/net/wwan0/queues/tx-0 (queues)
UDEV [18944.393351] bind /devices/pci0000:00/0000:00:08.1/0000:05:00.3/usb1/1-2/1-2:1.0 (usb)
UDEV [18944.396446] bind /devices/pci0000:00/0000:00:08.1/0000:05:00.3/usb1/1-2 (usb)
UDEV [18944.398125] move /devices/pci0000:00/0000:00:08.1/0000:05:00.3/usb1/1-2/1-2:1.0/net/wwp5s0f3u2 (net)
Key event:
add /devices/pci0000:00/0000:00:08.1/0000:05:00.3/usb1/1-2/1-2:1.0/net/wwp5s0f3u2 (net)
This means that the kernel notified that a new device <span>wwp5s0f3u2</span>
was added.
1.2 Kernel rndis_host Module
It seems that udev is not the problem, so the issue might be in the kernel.
> lsmod | grep rndis
rndis_host 24576 0
cdc_ether 28672 1 rndis_host
usbnet 61440 2 rndis_host,cdc_ether
> modinfo rndis_host
filename: /lib/modules/6.14.2-zen1-1-zen/kernel/drivers/net/usb/rndis_host.ko.zst
license: GPL
description: USB Host side RNDIS driver
author: David Brownell
srcversion: 4DE15943675DBCB96800C5E
alias: usb:v*p*d*dc*dsc*dp*icEFisc04ip01in*
alias: usb:v*p*d*dc*dsc*dp*icE0isc01ip03in*
alias: usb:v*p*d*dc*dsc*dp*icEFisc01ip01in*
alias: usb:v*p*d*dc*dsc*dp*ic02isc02ipFFin*
alias: usb:v19D2p*d*dc*dsc*dp*ic02isc02ipFFin*
alias: usb:v19D2p*d*dc*dsc*dp*icE0isc01ip03in*
alias: usb:v238Bp*d*dc*dsc*dp*ic02isc02ipFFin*
alias: usb:v1630p0042d*dc*dsc*dp*ic02isc02ipFFin*
depends: cdc_ether,usbnet
intree: Y
name: rndis_host
retpoline: Y
vermagic: 6.14.2-zen1-1-zen SMP preempt mod_unload
sig_id: PKCS#7
signer: Build time autogenerated kernel key
sig_key: 09:05:FF:B2:1C:68:6D:FC:6A:6A:6E:E5:3E:B3:9E:4D:F3:4F:07:32
sig_hashalgo: sha512
signature: 30:65:02:31:00:D4:F7:6C:DD:03:F0:33:8C:82:39:BC:BD:FB:95:A8:
8A:3B:71:1D:CB:06:97:34:5C:22:54:76:13:2C:04:49:0C:1D:11:F6:
8B:C6:F5:81:3E:76:1E:F0:E1:AF:84:F8:2C:02:30:5E:28:6A:A4:90:
18:65:24:7E:74:C4:70:C8:FA:25:1C:79:0A:8F:8E:69:17:CA:2A:EB:
D7:08:23:DA:14:63:56:90:43:B3:03:3E:00:15:22:69:36:EF:58:F7:
6B:95:3D
The kernel module <span>rndis_host.ko</span>
is the corresponding driver.
Let’s take a look at the corresponding <span>dmesg</span>
log:
> sudo dmesg
省略
[ 57.742480] usb 1-2: new high-speed USB device number 4 using xhci_hcd
[ 57.871721] usb 1-2: New USB device found, idVendor=2717, idProduct=ff80, bcdDevice= 4.19
[ 57.871727] usb 1-2: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[ 57.871730] usb 1-2: Product: Redmi Note 9 Pro
[ 57.871732] usb 1-2: Manufacturer: Xiaomi
[ 57.871734] usb 1-2: SerialNumber: 87420001
[ 57.933653] usbcore: registered new interface driver cdc_ether
[ 57.941628] rndis_host 1-2:1.0 wwan0: register 'rndis_host' at usb-0000:05:00.3-2, Mobile Broadband RNDIS device, 16:f0:8c:01:02:03
[ 57.941663] usbcore: registered new interface driver rndis_host
[ 57.947399] rndis_host 1-2:1.0 wwp5s0f3u2: renamed from wwan0
Hmm, it seems that the kernel directly recognized the device as <span>wwan0</span>
, which led to a series of subsequent faults.
Searching for related information online, there were also some good findings:
- • https://patchwork.kernel.org/project/netdevbpf/patch/[email protected]/
rndis_host: Flag RNDIS modems as WWAN devices
This is a kernel code change from last month, so it is still fresh.
- • https://bbs.archlinux.org/viewtopic.php?id=304892
USB tethering not working after updating to latest kernel
There are also users on the ArchLinux forum who raised the same issue.
So, let’s look at the kernel source code: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/drivers/net/usb/rndis_host.c?h=v6.12.23
The kernel code file <span>drivers/net/usb/rndis_host.c</span>
: (excerpt)
static const struct usb_device_id products [] = {
{
/* 2Wire HomePortal 1000SW */
USB_DEVICE_AND_INTERFACE_INFO(0x1630, 0x0042,
USB_CLASS_COMM, 2 /* ACM */, 0x0ff),
.driver_info = (unsigned long) &rndis_poll_status_info,
}, {
/* Hytera Communications DMR radios' "Radio to PC Network" */
USB_VENDOR_AND_INTERFACE_INFO(0x238b,
USB_CLASS_COMM, 2 /* ACM */, 0x0ff),
.driver_info = (unsigned long)&rndis_info,
}, {
/* ZTE WWAN modules */
USB_VENDOR_AND_INTERFACE_INFO(0x19d2,
USB_CLASS_WIRELESS_CONTROLLER, 1, 3),
.driver_info = (unsigned long)&zte_rndis_info,
}, {
/* ZTE WWAN modules, ACM flavour */
USB_VENDOR_AND_INTERFACE_INFO(0x19d2,
USB_CLASS_COMM, 2 /* ACM */, 0x0ff),
.driver_info = (unsigned long)&zte_rndis_info,
}, {
/* RNDIS is MSFT's un-official variant of CDC ACM */
USB_INTERFACE_INFO(USB_CLASS_COMM, 2 /* ACM */, 0x0ff),
.driver_info = (unsigned long) &rndis_info,
}, {
/* "ActiveSync" is an undocumented variant of RNDIS, used in WM5 */
USB_INTERFACE_INFO(USB_CLASS_MISC, 1, 1),
.driver_info = (unsigned long) &rndis_poll_status_info,
}, {
/* RNDIS for tethering */
USB_INTERFACE_INFO(USB_CLASS_WIRELESS_CONTROLLER, 1, 3),
.driver_info = (unsigned long) &rndis_info,
}, {
/* Mobile Broadband Modem, seen in Novatel Verizon USB730L and
* Telit FN990A (RNDIS)
*/
USB_INTERFACE_INFO(USB_CLASS_MISC, 4, 1),
.driver_info = (unsigned long)&wwan_rndis_info,
},
{ }, // END
};
MODULE_DEVICE_TABLE(usb, products);
The key code in this segment is:
USB_INTERFACE_INFO(USB_CLASS_MISC, 4, 1),
.driver_info = (unsigned long)&wwan_rndis_info,
Now let’s compare:
static const struct driver_info rndis_info = {
.description = "RNDIS device",
.flags = FLAG_ETHER | FLAG_POINTTOPOINT | FLAG_FRAMING_RN | FLAG_NO_SETINT,
.bind = rndis_bind,
.unbind = rndis_unbind,
.status = rndis_status,
.rx_fixup = rndis_rx_fixup,
.tx_fixup = rndis_tx_fixup,
};
// 省略
static const struct driver_info wwan_rndis_info = {
.description = "Mobile Broadband RNDIS device",
.flags = FLAG_WWAN | FLAG_POINTTOPOINT | FLAG_FRAMING_RN | FLAG_NO_SETINT,
.bind = rndis_bind,
.unbind = rndis_unbind,
.status = rndis_status,
.rx_fixup = rndis_rx_fixup,
.tx_fixup = rndis_tx_fixup,
};
Under normal circumstances, the Android USB tethering device should be recognized as <span>rndis_info</span>
. However, in the above kernel driver code, it was incorrectly set to <span>wwan_rndis_info</span>
, leading to a series of subsequent faults.
Comparing these two structure data, the flags (<span>.flags</span>
) one is <span>FLAG_ETHER</span>
, the other is <span>FLAG_WWAN</span>
.
The correct flag should use <span>FLAG_ETHER</span>
to make USB tethering work properly.
2 Solution: Compile Linux Kernel Yourself (ArchLinux)
Alright, the bug analysis is complete. Due to an upstream error in the kernel code change, a series of subsequent faults occurred.
If we wait for the Linux kernel developers to fix it, who knows how long it will take. Therefore, we can only manually modify the kernel code and compile and install a custom kernel.
Reference documentation for compiling the kernel on ArchLinux: https://wiki.archlinux.org/title/Kernel/Arch_build_system
- • (1) Install the required packages for compiling the kernel:
sudo pacman -S devtools base-devel
- • (2) Create a directory and download the kernel package related code:
> mkdir build > cd build > pkgctl repo clone --protocol=https linux-zen
- • (3) After downloading, modify the
<span>PKGBUILD</span>
file like this (for reference):
> git diff -- PKGBUILD
diff --git a/PKGBUILD b/PKGBUILD
index 8e53813..a3ae8ed 100644
--- a/PKGBUILD
+++ b/PKGBUILD
@@ -1,9 +1,9 @@
# Maintainer: Jan Alexander Steffens (heftig) <[email protected]>
-pkgbase=linux-zen
+pkgbase=linux-s2
pkgver=6.14.2.zen1
pkgrel=1
-pkgdesc='Linux ZEN'
+pkgdesc='Linux ZEN (s2)'
url='https://github.com/zen-kernel/zen-kernel'
arch=(x86_64)
license=(GPL-2.0-only)
@@ -35,26 +35,20 @@ options=(
_srcname=linux-${pkgver%.*}
_srctag=v${pkgver%.*}-${pkgver##*.}
source=(
- https://cdn.kernel.org/pub/linux/kernel/v${pkgver%%.*}.x/${_srcname}.tar.{xz,sign}
- $url/releases/download/$_srctag/linux-$_srctag.patch.zst{,.sig}
+ https://cdn.kernel.org/pub/linux/kernel/v${pkgver%%.*}.x/${_srcname}.tar.xz
+ $url/releases/download/$_srctag/linux-$_srctag.patch.zst
config # the main kernel config file
-)
-validpgpkeys=(
- ABAF11C65A2970B130ABE3C479BE3E4300411886 # Linus Torvalds
- 647F28654894E3BD457199BE38DBBDC86092693E # Greg Kroah-Hartman
- 83BC8889351B5DEBBB68416EB8AC08600F108CDF # Jan Alexander Steffens (heftig)
+ fix_rndis_host_1.patch
)
# https://www.kernel.org/pub/linux/kernel/v6.x/sha256sums.asc
sha256sums=('c5c682a354ea3190139357a57d34a79e5c37221ace823a938e10116b577a2e1b'
- 'SKIP'
'433091be5e04cb49a1c1a8d8b6a3220413fea3b897d998fe10973c14933abd27'
- 'SKIP'
- 'f33a574662f1d4f266a2faa96404240e71e84438dba05528a66449db1e23ec85')
+ 'f33a574662f1d4f266a2faa96404240e71e84438dba05528a66449db1e23ec85'
+ 'SKIP')
export KBUILD_BUILD_HOST=archlinux
export KBUILD_BUILD_USER=$pkgbase
@@ -90,7 +84,6 @@ build() {
cd $_srcname
make all
make -C tools/bpf/bpftool vmlinux.h feature-clang-bpf-co-re=1
- make htmldocs
}
_package() {
@@ -248,7 +241,6 @@ _package-docs() {
pkgname=(
"$pkgbase"
"$pkgbase-headers"
- "$pkgbase-docs"
)
for _p in "${pkgname[@]}"; do
eval "package_$_p() {
Hmm, rename the new kernel to <span>linux-s2</span>
.
- • (4) Add a patch file
<span>build/linux-zen/fix_rndis_host_1.patch</span>
:
diff --git a/drivers/net/usb/rndis_host.c b/drivers/net/usb/rndis_host.c
index bb0bf14..dbbab3d 100644
--- a/drivers/net/usb/rndis_host.c
+++ b/drivers/net/usb/rndis_host.c
@@ -680,7 +680,7 @@ static const struct usb_device_id products [] = {
* Telit FN990A (RNDIS)
*/
USB_INTERFACE_INFO(USB_CLASS_MISC, 4, 1),
- .driver_info = (unsigned long)&wwan_rndis_info,
+ .driver_info = (unsigned long) &rndis_info,
},
{ }, // END
};
That’s right, just modify one line of code!
- • (5) Compile the kernel:
makepkg -s
After compilation, two new packages are obtained:
> ls -l
总计 360736
-rw-r--r-- 1 s2 s2 282735 4月13日 09:31 config
-rw-r--r-- 1 s2 s2 438 4月13日 15:07 fix_rndis_host_1.patch
-rw-r--r-- 1 s2 s2 149412128 4月13日 09:44 linux-6.14.2.tar.xz
-rw-r--r-- 1 s2 s2 151359465 4月13日 16:10 linux-s2-6.14.2.zen1-1-x86_64.pkg.tar.zst
-rw-r--r-- 1 s2 s2 68156035 4月13日 16:10 linux-s2-headers-6.14.2.zen1-1-x86_64.pkg.tar.zst
-rw-r--r-- 1 s2 s2 157702 4月13日 10:21 linux-v6.14.2-zen1.patch.zst
drwxr-xr-x 1 s2 s2 48 4月13日 16:10 pkg/
-rw-r--r-- 1 s2 s2 7500 4月13日 15:13 PKGBUILD
drwxr-xr-x 1 s2 s2 222 4月13日 15:19 src/
Install the packages:
sudo pacman -U linux-s2-6.14.2.zen1-1-x86_64.pkg.tar.zst linux-s2-headers-6.14.2.zen1-1-x86_64.pkg.tar.zst
Update the GRUB boot menu:
sudo grub-mkconfig -o /boot/grub/grub.cfg
Restart, and select the new kernel.
3 Test Results
After reboot:
> uname -a
Linux SC202501C7LA 6.14.2-zen1-1-s2 #1 ZEN SMP PREEMPT_DYNAMIC Sun, 13 Apr 2025 07:18:48 +0000 x86_64 GNU/Linux
Alright, the new kernel is in use.
> ip link
省略
5: enp5s0f3u2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UNKNOWN mode DEFAULT group default qlen 1000
link/ether ee:81:70:01:02:03 brd ff:ff:ff:ff:ff:ff
altname enxee8170010203
Good! Successfully recognized the network card for the phone’s USB sharing.
Let’s check <span>dmesg</span>
:
> sudo dmesg
省略
[ 6782.970347] usb 1-2: new high-speed USB device number 4 using xhci_hcd
[ 6783.101881] usb 1-2: New USB device found, idVendor=2717, idProduct=ff80, bcdDevice= 4.19
[ 6783.101891] usb 1-2: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[ 6783.101894] usb 1-2: Product: Redmi Note 9 Pro
[ 6783.101897] usb 1-2: Manufacturer: Xiaomi
[ 6783.101899] usb 1-2: SerialNumber: 87420001
[ 6783.177283] usbcore: registered new interface driver cdc_ether
[ 6783.184703] rndis_host 1-2:1.0 usb0: register 'rndis_host' at usb-0000:05:00.3-2, RNDIS device, ee:81:70:01:02:03
[ 6783.184738] usbcore: registered new interface driver rndis_host
[ 6783.191161] rndis_host 1-2:1.0 enp5s0f3u2: renamed from usb0
Good, everything is working normally.
NetworkManager can also recognize and configure the network normally.
Bug fix successful.
4 Summary and Outlook
Although the Linux system may have many bugs, because there is source code, anyone (like Poor Little Water Drop) can fix bugs anytime and anywhere.
This is the benefit of open source!
This article is published under the CC-BY-SA 4.0 license.
This article is formatted and published using WeChat Markdown Editor: https://github.com/doocs/md