surban / usb-gadget

Expose standard or fully custom USB peripherals (gadgets) through a USB device controller (UDC) on Linux using Rust.
https://crates.io/crates/usb-gadget
Apache License 2.0
51 stars 4 forks source link

Dual Ethernet: same host_addr assigned to both device #7

Open RaubCamaioni opened 2 months ago

RaubCamaioni commented 2 months ago

Tests preformed on pi4 model B kernel: 6.6.20+rpt-rpi-v8

Composite configurations with two net devices, with the same drivers, assign the same host mac address to both devices. I believe this is an error with the Linux gadget module / kernel and not an issue with this repo.

When reviewing the files generated by rust, the host_addr variables are properly assigned, unique to each function. I have the same issue when manually creating the configuration without this repo.

Is this the proper way, using this library, to configure a composite Linux device with two functions with the same driver? Both devices enumerate on a windows machine. They have identical mac addresses. Raising as an issue here to track the limitation in Linux gadget kernel capabilities.

I have been looking through the Linux kernel gadgets implementation and have not found any obvious culprits. Working from the "gether_setup_name" function that assigns the host and dev addresses. https://github.com/torvalds/linux/blob/20cb38a7af88dc40095da7c2c9094da3873fea23/drivers/usb/gadget/function/u_ether.c

Post on stack overflow tracking this error: https://stackoverflow.com/questions/78270075/linux-gadget-universal-device-controller-dual-cdc-ecm-or-dual-rndis-ethernet

Code showing roughly how the rust library is being used.

let dev_addr1 = MacAddr6::new(0x66, 0xf9, 0x7d, 0xf2, 0x3e, 0x3b);
let host_addr1 = MacAddr6::new(0x7e, 0x21, 0xb2, 0xcb, 0xd4, 0x62);
let mut builder1 = Net::builder(NetClass::Ncm);
builder1.dev_addr = Some(dev_addr1);
builder1.host_addr = Some(host_addr1);
builder1.qmult = Some(10);
let (net1, func1) = builder1.build();

let dev_addr2 = MacAddr6::new(0x66, 0xf9, 0x7d, 0xf2, 0x3e, 0x4b);
let host_addr2 = MacAddr6::new(0x7e, 0x21, 0xb2, 0xcb, 0xd4, 0x71);
let mut builder2 = Net::builder(NetClass::Ncm);
builder2.dev_addr = Some(dev_addr2);
builder2.host_addr = Some(host_addr2);
builder2.qmult = Some(10);
let (net2, func2) = builder2.build();

let reg =
  Gadget::new(Class::new(0xEF, 0x02, 0x01), Id::new(0x5523, 0x2355), Strings::new("MyManufacturer", "MyProduct", "123456"))
      .with_config(Config::new("config").with_function(func1).with_function(func2))
      .bind(&udc)
      .expect("cannot bind to UDC");
surban commented 2 months ago

https://github.com/surban/usb-gadget/blob/5ebe622dd8dbf789a3298db4632ea4c96b4d13cd/src/function/net.rs#L94-L100

If dev_addr or host_addr are None they are not written to functionfs and should be assigned automatically by the kernel, hopefully unique.

RaubCamaioni commented 2 months ago

I tried setting the host_addr to None, allowing the kernel to assign a random mac address. Both interfaces get a random mac address in their configuration files. cat /sys/kernel/config/usb_gadget/usb-gadget0/functions/ncm.usb-gadget0-0/host_addr: f2:b8:41:90:dd:10 cat /sys/kernel/config/usb_gadget/usb-gadget0/functions/ncm.usb-gadget0-1/host_addr: 4a:04:0a:2a:3d:04

Unfortunately, the client side enumerates both gadgets with the same mac address: Both interfaces received function1 mac address 4a:04:0a:2a:3d:04. I have seem function2 mac address assigned to both before too.

The client side getting the same mac addresses happens on windows and linux. I believe this is a linux kernel or linux gadgets module bug, the configuration files generated by Rust are all correct.