Skip to content

Commit 5b8f15f

Browse files
bmorkdavem330
authored andcommitted
net: cdc_mbim: handle IPv6 Neigbor Solicitations
MBIM is a point-to-point protocol transporting raw IP packets with no L2 headers. Only IPv4 and IPv6 are supported. ARP in particular is not, which is quite logical given the lack of L2 headers. The driver still emulates an ethernet interface, dropping all unsupported protocols, and avoiding neigbour resolving by setting the IFF_NOARP flag. The MBIM specification does not explicitly forbid IPv6 Neighbor Discovery, and it seems the other OS support will respond to Neighbor Solicitations on MBIM links. There are therefore buggy devices out there, which despite the pointlessness, still require Neighbor Discovery for IPv6 over MBIM. This is incompatible with the IFF_NOARP flag which disables both ARP and ND. We cannot support ARP in any case, so we have to keep that flag. This patch implements a workaround for the buggy devices, letting the driver respond directly to Neighbor Solicitations from the device. This is not optimal, but will have minimal effect on any sane device. Cc: Greg Suarez <gsuarez@smithmicro.com> Reported-and-tested-by: Thomas Schäfer <tschaefer@t-online.de> Signed-off-by: Bjørn Mork <bjorn@mork.no> Signed-off-by: David S. Miller <davem@davemloft.net>
1 parent 4800599 commit 5b8f15f

File tree

1 file changed

+58
-0
lines changed

1 file changed

+58
-0
lines changed

drivers/net/usb/cdc_mbim.c

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
#include <linux/usb/usbnet.h>
2222
#include <linux/usb/cdc-wdm.h>
2323
#include <linux/usb/cdc_ncm.h>
24+
#include <net/ipv6.h>
25+
#include <net/addrconf.h>
2426

2527
/* driver specific data - must match cdc_ncm usage */
2628
struct cdc_mbim_state {
@@ -184,6 +186,60 @@ static struct sk_buff *cdc_mbim_tx_fixup(struct usbnet *dev, struct sk_buff *skb
184186
return NULL;
185187
}
186188

189+
/* Some devices are known to send Neigbor Solicitation messages and
190+
* require Neigbor Advertisement replies. The IPv6 core will not
191+
* respond since IFF_NOARP is set, so we must handle them ourselves.
192+
*/
193+
static void do_neigh_solicit(struct usbnet *dev, u8 *buf, u16 tci)
194+
{
195+
struct ipv6hdr *iph = (void *)buf;
196+
struct nd_msg *msg = (void *)(iph + 1);
197+
struct net_device *netdev;
198+
struct inet6_dev *in6_dev;
199+
bool is_router;
200+
201+
/* we'll only respond to requests from unicast addresses to
202+
* our solicited node addresses.
203+
*/
204+
if (!ipv6_addr_is_solict_mult(&iph->daddr) ||
205+
!(ipv6_addr_type(&iph->saddr) & IPV6_ADDR_UNICAST))
206+
return;
207+
208+
/* need to send the NA on the VLAN dev, if any */
209+
if (tci)
210+
netdev = __vlan_find_dev_deep(dev->net, htons(ETH_P_8021Q),
211+
tci);
212+
else
213+
netdev = dev->net;
214+
if (!netdev)
215+
return;
216+
217+
in6_dev = in6_dev_get(netdev);
218+
if (!in6_dev)
219+
return;
220+
is_router = !!in6_dev->cnf.forwarding;
221+
in6_dev_put(in6_dev);
222+
223+
/* ipv6_stub != NULL if in6_dev_get returned an inet6_dev */
224+
ipv6_stub->ndisc_send_na(netdev, NULL, &iph->saddr, &msg->target,
225+
is_router /* router */,
226+
true /* solicited */,
227+
false /* override */,
228+
true /* inc_opt */);
229+
}
230+
231+
static bool is_neigh_solicit(u8 *buf, size_t len)
232+
{
233+
struct ipv6hdr *iph = (void *)buf;
234+
struct nd_msg *msg = (void *)(iph + 1);
235+
236+
return (len >= sizeof(struct ipv6hdr) + sizeof(struct nd_msg) &&
237+
iph->nexthdr == IPPROTO_ICMPV6 &&
238+
msg->icmph.icmp6_code == 0 &&
239+
msg->icmph.icmp6_type == NDISC_NEIGHBOUR_SOLICITATION);
240+
}
241+
242+
187243
static struct sk_buff *cdc_mbim_process_dgram(struct usbnet *dev, u8 *buf, size_t len, u16 tci)
188244
{
189245
__be16 proto = htons(ETH_P_802_3);
@@ -198,6 +254,8 @@ static struct sk_buff *cdc_mbim_process_dgram(struct usbnet *dev, u8 *buf, size_
198254
proto = htons(ETH_P_IP);
199255
break;
200256
case 0x60:
257+
if (is_neigh_solicit(buf, len))
258+
do_neigh_solicit(dev, buf, tci);
201259
proto = htons(ETH_P_IPV6);
202260
break;
203261
default:

0 commit comments

Comments
 (0)