Thomas Schäfer <tschae...@t-online.de> writes: > Am Samstag, 26. Oktober 2013, 16:01:26 schrieben Sie: >> Thomas Schäfer <tschae...@t-online.de> writes: >> > Am Donnerstag, 24. Oktober 2013, 11:45:39 schrieben Sie: >> >> Did you try any of the ND tuning controls on Linux? Turning on ARP >> >> >> >> before bringing up the interface might work on IPv6 (but fail for IPv4): >> >> ifconfig wwan0 arp >> > >> > This solved the problem. (IPv6 positive , IPv4 at the moment untested) >> >> Great! >> >> Then I believe we have positive confirmation that this firmware really >> needs neigbour discovery. Now we only have to figure out how to support >> that without breaking IPv4. > > That's your part :-) > > Now I tested IPv4 with arp, and you are right. It doesn't work. > Ping is answered with "Destination Host Unreachable". > > If I switch off arp again with ifconfig wwan0 -arp, ping works.
Yes, that's expected. I am sorry, but I cannot come up with a single non-ugly solution to this problem. The only options I see are: a) split IFF_NOARP into multiple flags, allowing the driver to specify the wanted behaviour (no ARP, but still respond to IPv6 NS) b) let the driver intercept IPv6 NS from the modem, sending direct replies c) let the driver intercept ARP from the host, sending direct replies Alternative a) may seem cleanest, but changes a lot of core code for a single driver/device. The arp and ndisc code looks very magic to me already, and I am therefore reluctant to attempt to modify it. Alternative b) will have to deal with the different "is IPv6 available?" possibilities. This has been solved for vxlan recently, but it's a bit more complex than alternative c). So I made a shot at that one. You could try the attached patch, which works for me on IPv4. Not tested on IPv6, but should work as it removes the IFF_NOARP flag. But I still find this so ugly that I am not sure I am going to propose it on netdev... Bjørn
>From 8d3ddbb24f2e55dc73a7e32172ed4d5930b484c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Mork?= <bj...@mork.no> Date: Sat, 26 Oct 2013 18:55:08 +0200 Subject: [PATCH] cdc_mbim: add IPv6 ND support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some buggy devices require IPv6 Neighbor Discovery support. This is incompatible with the IFF_NOARP flag. But ARP is impossible on MBIM, because the protocol only supports IPv4 or IPv6 packets. Work around this by faking ARP replies. Signed-off-by: Bjørn Mork <bj...@mork.no> --- drivers/net/usb/cdc_mbim.c | 39 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/drivers/net/usb/cdc_mbim.c b/drivers/net/usb/cdc_mbim.c index 25ba7ec..a1aa8c9 100644 --- a/drivers/net/usb/cdc_mbim.c +++ b/drivers/net/usb/cdc_mbim.c @@ -13,6 +13,7 @@ #include <linux/module.h> #include <linux/netdevice.h> #include <linux/ethtool.h> +#include <linux/if_arp.h> #include <linux/if_vlan.h> #include <linux/ip.h> #include <linux/mii.h> @@ -21,6 +22,7 @@ #include <linux/usb/usbnet.h> #include <linux/usb/cdc-wdm.h> #include <linux/usb/cdc_ncm.h> +#include <net/arp.h> /* driver specific data - must match cdc_ncm usage */ struct cdc_mbim_state { @@ -97,9 +99,6 @@ static int cdc_mbim_bind(struct usbnet *dev, struct usb_interface *intf) dev->status = NULL; info->subdriver = subdriver; - /* MBIM cannot do ARP */ - dev->net->flags |= IFF_NOARP; - /* no need to put the VLAN tci in the packet headers */ dev->net->features |= NETIF_F_HW_VLAN_CTAG_TX; err: @@ -120,6 +119,37 @@ static void cdc_mbim_unbind(struct usbnet *dev, struct usb_interface *intf) cdc_ncm_unbind(dev, intf); } +/* Create fake ARP replies. Some devices require ND support for IPv6, + * so we cannot set the IFF_NOARP flag like we would want to. Faking + * ARP replies works as well + */ +static void mbim_handle_arp(struct usbnet *dev, struct sk_buff *skb, u16 tci) +{ + struct sk_buff *skb_reply; + unsigned char *arp_ptr; + struct arphdr *arp; + __be32 sip, tip; + + if (skb->len < sizeof(struct arphdr) + 2 * 4 + 2 * ETH_ALEN) + return; + + arp = arp_hdr(skb); + if (arp->ar_hln != ETH_ALEN || arp->ar_pln != 4 || + arp->ar_pro != htons(ETH_P_IP) || arp->ar_hrd != htons(ARPHRD_ETHER)) + return; + + arp_ptr = (unsigned char *)(arp + 1); + memcpy(&sip, arp_ptr + ETH_ALEN, 4); + memcpy(&tip, arp_ptr + 4 + 2 * ETH_ALEN, 4); + skb_reply = arp_create(ARPOP_REPLY, ETH_P_ARP, sip, dev->net, tip, + dev->net->dev_addr, dev->net->dev_addr, + dev->net->dev_addr); + if (!skb_reply) + return; + if (tci && !vlan_put_tag(skb_reply, htons(ETH_P_8021Q), tci)) + return; + usbnet_skb_return(dev, skb_reply); +} static struct sk_buff *cdc_mbim_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags) { @@ -153,6 +183,9 @@ static struct sk_buff *cdc_mbim_tx_fixup(struct usbnet *dev, struct sk_buff *skb case htons(ETH_P_IP): case htons(ETH_P_IPV6): break; + case htons(ETH_P_ARP): + mbim_handle_arp(dev, skb, tci); + /* fallthrough */ default: goto error; } -- 1.7.10.4