Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Merge branch 'rndis_host-handle-bogus-mac-addresses-in-zte-rndis-devices'

Lech Perczak says:

====================
rndis_host: handle bogus MAC addresses in ZTE RNDIS devices

When porting support of ZTE MF286R to OpenWrt [1], it was discovered,
that its built-in LTE modem fails to adjust its target MAC address,
when a random MAC address is assigned to the interface, due to detection of
"locally-administered address" bit. This leads to dropping of ingress
trafficat the host. The modem uses RNDIS as its primary interface,
with some variants exposing both of them simultaneously.

Then it was discovered, that cdc_ether driver contains a fixup for that
exact issue, also appearing on CDC ECM interfaces.
I discussed how to proceed with that with Bjørn Mork at OpenWrt forum [3],
with the first approach would be to trust the locally-administered MAC
again, and add a quirk for the problematic ZTE devices, as suggested by
Kristian Evensen. before [4], but reusing the fixup from cdc_ether looks
like a safer and more generic solution.

Finally, according to Bjørn's suggestion. limit the scope of bogus MAC
addressdetection to ZTE devices, the same way as it is done in cdc_ether,
as this trait wasn't really observed outside of ZTE devices.
Do that for both flavours of RNDIS devices, with interface classes
02/02/ff and e0/01/03, as both types are reported by different modems.

[1] https://git.openwrt.org/?p=openwrt/openwrt.git;a=commit;h=7ac8da00609f42b8aba74b7efc6b0d055b7cef3e
[2] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=bfe9b9d2df669a57a95d641ed46eb018e204c6ce
[3] https://forum.openwrt.org/t/problem-with-modem-in-zte-mf286r/120988
[4] https://lore.kernel.org/all/CAKfDRXhDp3heiD75Lat7cr1JmY-kaJ-MS0tt7QXX=s8RFjbpUQ@mail.gmail.com/T/

Cc: Bjørn Mork <bjorn@mork.no>
Cc: Kristian Evensen <kristian.evensen@gmail.com>
Cc: Oliver Neukum <oliver@neukum.org>

v3: Fixed wrong identifier commit description and whitespace in patch 2.

v2: ensure that MAC fixup is applied to all Ethernet frames in RNDIS
batch, by introducing a driver flag, and integrating the fixup inside
rndis_rx_fixup().
====================

Link: https://lore.kernel.org/r/20220413014416.2306843-1-lech.perczak@gmail.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>

+47 -5
+2 -1
drivers/net/usb/cdc_ether.c
··· 479 479 * device MAC address has been updated). Always set MAC address to that of the 480 480 * device. 481 481 */ 482 - static int usbnet_cdc_zte_rx_fixup(struct usbnet *dev, struct sk_buff *skb) 482 + int usbnet_cdc_zte_rx_fixup(struct usbnet *dev, struct sk_buff *skb) 483 483 { 484 484 if (skb->len < ETH_HLEN || !(skb->data[0] & 0x02)) 485 485 return 1; ··· 489 489 490 490 return 1; 491 491 } 492 + EXPORT_SYMBOL_GPL(usbnet_cdc_zte_rx_fixup); 492 493 493 494 /* Ensure correct link state 494 495 *
+43 -4
drivers/net/usb/rndis_host.c
··· 418 418 goto halt_fail_and_release; 419 419 } 420 420 421 - if (bp[0] & 0x02) 422 - eth_hw_addr_random(net); 423 - else 424 - eth_hw_addr_set(net, bp); 421 + eth_hw_addr_set(net, bp); 425 422 426 423 /* set a nonzero filter to enable data transfers */ 427 424 memset(u.set, 0, sizeof *u.set); ··· 460 463 return generic_rndis_bind(dev, intf, FLAG_RNDIS_PHYM_NOT_WIRELESS); 461 464 } 462 465 466 + static int zte_rndis_bind(struct usbnet *dev, struct usb_interface *intf) 467 + { 468 + int status = rndis_bind(dev, intf); 469 + 470 + if (!status && (dev->net->dev_addr[0] & 0x02)) 471 + eth_hw_addr_random(dev->net); 472 + 473 + return status; 474 + } 475 + 463 476 void rndis_unbind(struct usbnet *dev, struct usb_interface *intf) 464 477 { 465 478 struct rndis_halt *halt; ··· 492 485 */ 493 486 int rndis_rx_fixup(struct usbnet *dev, struct sk_buff *skb) 494 487 { 488 + bool dst_mac_fixup; 489 + 495 490 /* This check is no longer done by usbnet */ 496 491 if (skb->len < dev->net->hard_header_len) 497 492 return 0; 493 + 494 + dst_mac_fixup = !!(dev->driver_info->data & RNDIS_DRIVER_DATA_DST_MAC_FIXUP); 498 495 499 496 /* peripheral may have batched packets to us... */ 500 497 while (likely(skb->len)) { ··· 534 523 break; 535 524 skb_pull(skb, msg_len - sizeof *hdr); 536 525 skb_trim(skb2, data_len); 526 + 527 + if (unlikely(dst_mac_fixup)) 528 + usbnet_cdc_zte_rx_fixup(dev, skb2); 529 + 537 530 usbnet_skb_return(dev, skb2); 538 531 } 539 532 540 533 /* caller will usbnet_skb_return the remaining packet */ 534 + if (unlikely(dst_mac_fixup)) 535 + usbnet_cdc_zte_rx_fixup(dev, skb); 536 + 541 537 return 1; 542 538 } 543 539 EXPORT_SYMBOL_GPL(rndis_rx_fixup); ··· 618 600 .tx_fixup = rndis_tx_fixup, 619 601 }; 620 602 603 + static const struct driver_info zte_rndis_info = { 604 + .description = "ZTE RNDIS device", 605 + .flags = FLAG_ETHER | FLAG_POINTTOPOINT | FLAG_FRAMING_RN | FLAG_NO_SETINT, 606 + .data = RNDIS_DRIVER_DATA_DST_MAC_FIXUP, 607 + .bind = zte_rndis_bind, 608 + .unbind = rndis_unbind, 609 + .status = rndis_status, 610 + .rx_fixup = rndis_rx_fixup, 611 + .tx_fixup = rndis_tx_fixup, 612 + }; 613 + 621 614 /*-------------------------------------------------------------------------*/ 622 615 623 616 static const struct usb_device_id products [] = { ··· 642 613 USB_VENDOR_AND_INTERFACE_INFO(0x238b, 643 614 USB_CLASS_COMM, 2 /* ACM */, 0x0ff), 644 615 .driver_info = (unsigned long)&rndis_info, 616 + }, { 617 + /* ZTE WWAN modules */ 618 + USB_VENDOR_AND_INTERFACE_INFO(0x19d2, 619 + USB_CLASS_WIRELESS_CONTROLLER, 1, 3), 620 + .driver_info = (unsigned long)&zte_rndis_info, 621 + }, { 622 + /* ZTE WWAN modules, ACM flavour */ 623 + USB_VENDOR_AND_INTERFACE_INFO(0x19d2, 624 + USB_CLASS_COMM, 2 /* ACM */, 0x0ff), 625 + .driver_info = (unsigned long)&zte_rndis_info, 645 626 }, { 646 627 /* RNDIS is MSFT's un-official variant of CDC ACM */ 647 628 USB_INTERFACE_INFO(USB_CLASS_COMM, 2 /* ACM */, 0x0ff),
+1
include/linux/usb/rndis_host.h
··· 197 197 198 198 /* Flags for driver_info::data */ 199 199 #define RNDIS_DRIVER_DATA_POLL_STATUS 1 /* poll status before control */ 200 + #define RNDIS_DRIVER_DATA_DST_MAC_FIXUP 2 /* device ignores configured MAC address */ 200 201 201 202 extern void rndis_status(struct usbnet *dev, struct urb *urb); 202 203 extern int
+1
include/linux/usb/usbnet.h
··· 214 214 extern int usbnet_cdc_bind(struct usbnet *, struct usb_interface *); 215 215 extern void usbnet_cdc_unbind(struct usbnet *, struct usb_interface *); 216 216 extern void usbnet_cdc_status(struct usbnet *, struct urb *); 217 + extern int usbnet_cdc_zte_rx_fixup(struct usbnet *dev, struct sk_buff *skb); 217 218 218 219 /* CDC and RNDIS support the same host-chosen packet filters for IN transfers */ 219 220 #define DEFAULT_FILTER (USB_CDC_PACKET_TYPE_BROADCAST \