aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBjørn Mork <bjorn@mork.no>2014-05-11 04:47:12 -0400
committerDavid S. Miller <davem@davemloft.net>2014-05-13 17:46:09 -0400
commit146a08d2d646983ebd2e510405e7554364675e39 (patch)
treefc58c54a651c6524a8cf31008af738dd36894124
parent7ad24ea4bf620a32631d7b3069c3e30c078b0c3e (diff)
net: cdc_mbim: optionally use VLAN ID 4094 for IP session 0
The cdc_mbim driver maps 802.1q VLANs to MBIM IP and DSS sessions. MBIM IP session 0 is handled as an exception and is mapped to untagged frames. This patch adds optional support for remapping MBIM IP session 0 to 802.1q VLAN ID 4094 instead. The default behaviour is not changed. The new behaviour is triggered by adding a link for this previously unsupported VLAN. The untagged mapping was chosen initially to support the assumed most common use case: Most current MBIM devices only support a single IP session (i.e. session 0 only), and using untagged frames lets the users completely ignore the additonal complexity of the multiplexing layer. But when the multiplexing features of MBIM are used, then this netdev gets a double meaning: It becomes the master interface for all the VLAN subdevs the additional sessions are mapped to, while still serving as the untagged IP interface for session 0. This can be problematic, especially when using Device Service Streams (DSS), as have become apparent recently with the availability of devices with real DSS support. Some use cases need to e.g set a MTU which is higher than allowed for IP Session 0. The dual role also leads to the situation where the IP Session 0 interface cannot be taken down without breaking unrelated IP or DSS sessions - a devastating side effect which applications managing a simple IP session cannot be expected to be aware of. A typical DHCP client will assume that it should bring the interface down after releasing the IP lease. These problems can be avoided by tagging IP session 0 packets too, making this session similar to all other multiplexed sessions. This redefines the main netdev as an upper master interface only. Cc: Greg Suarez <gsuarez@smithmicro.com> Signed-off-by: Bjørn Mork <bjorn@mork.no> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--drivers/net/usb/cdc_mbim.c74
1 files changed, 68 insertions, 6 deletions
diff --git a/drivers/net/usb/cdc_mbim.c b/drivers/net/usb/cdc_mbim.c
index 0ab79fca822c..694a8790f695 100644
--- a/drivers/net/usb/cdc_mbim.c
+++ b/drivers/net/usb/cdc_mbim.c
@@ -24,13 +24,21 @@
24#include <net/ipv6.h> 24#include <net/ipv6.h>
25#include <net/addrconf.h> 25#include <net/addrconf.h>
26 26
27/* alternative VLAN for IP session 0 if not untagged */
28#define MBIM_IPS0_VID 4094
29
27/* driver specific data - must match cdc_ncm usage */ 30/* driver specific data - must match cdc_ncm usage */
28struct cdc_mbim_state { 31struct cdc_mbim_state {
29 struct cdc_ncm_ctx *ctx; 32 struct cdc_ncm_ctx *ctx;
30 atomic_t pmcount; 33 atomic_t pmcount;
31 struct usb_driver *subdriver; 34 struct usb_driver *subdriver;
32 struct usb_interface *control; 35 unsigned long _unused;
33 struct usb_interface *data; 36 unsigned long flags;
37};
38
39/* flags for the cdc_mbim_state.flags field */
40enum cdc_mbim_flags {
41 FLAG_IPS0_VLAN = 1 << 0, /* IP session 0 is tagged */
34}; 42};
35 43
36/* using a counter to merge subdriver requests with our own into a combined state */ 44/* using a counter to merge subdriver requests with our own into a combined state */
@@ -62,6 +70,42 @@ static int cdc_mbim_wdm_manage_power(struct usb_interface *intf, int status)
62 return cdc_mbim_manage_power(dev, status); 70 return cdc_mbim_manage_power(dev, status);
63} 71}
64 72
73static int cdc_mbim_rx_add_vid(struct net_device *netdev, __be16 proto, u16 vid)
74{
75 struct usbnet *dev = netdev_priv(netdev);
76 struct cdc_mbim_state *info = (void *)&dev->data;
77
78 /* creation of this VLAN is a request to tag IP session 0 */
79 if (vid == MBIM_IPS0_VID)
80 info->flags |= FLAG_IPS0_VLAN;
81 else
82 if (vid >= 512) /* we don't map these to MBIM session */
83 return -EINVAL;
84 return 0;
85}
86
87static int cdc_mbim_rx_kill_vid(struct net_device *netdev, __be16 proto, u16 vid)
88{
89 struct usbnet *dev = netdev_priv(netdev);
90 struct cdc_mbim_state *info = (void *)&dev->data;
91
92 /* this is a request for an untagged IP session 0 */
93 if (vid == MBIM_IPS0_VID)
94 info->flags &= ~FLAG_IPS0_VLAN;
95 return 0;
96}
97
98static const struct net_device_ops cdc_mbim_netdev_ops = {
99 .ndo_open = usbnet_open,
100 .ndo_stop = usbnet_stop,
101 .ndo_start_xmit = usbnet_start_xmit,
102 .ndo_tx_timeout = usbnet_tx_timeout,
103 .ndo_change_mtu = usbnet_change_mtu,
104 .ndo_set_mac_address = eth_mac_addr,
105 .ndo_validate_addr = eth_validate_addr,
106 .ndo_vlan_rx_add_vid = cdc_mbim_rx_add_vid,
107 .ndo_vlan_rx_kill_vid = cdc_mbim_rx_kill_vid,
108};
65 109
66static int cdc_mbim_bind(struct usbnet *dev, struct usb_interface *intf) 110static int cdc_mbim_bind(struct usbnet *dev, struct usb_interface *intf)
67{ 111{
@@ -101,7 +145,10 @@ static int cdc_mbim_bind(struct usbnet *dev, struct usb_interface *intf)
101 dev->net->flags |= IFF_NOARP; 145 dev->net->flags |= IFF_NOARP;
102 146
103 /* no need to put the VLAN tci in the packet headers */ 147 /* no need to put the VLAN tci in the packet headers */
104 dev->net->features |= NETIF_F_HW_VLAN_CTAG_TX; 148 dev->net->features |= NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_FILTER;
149
150 /* monitor VLAN additions and removals */
151 dev->net->netdev_ops = &cdc_mbim_netdev_ops;
105err: 152err:
106 return ret; 153 return ret;
107} 154}
@@ -164,12 +211,24 @@ static struct sk_buff *cdc_mbim_tx_fixup(struct usbnet *dev, struct sk_buff *skb
164 skb_pull(skb, ETH_HLEN); 211 skb_pull(skb, ETH_HLEN);
165 } 212 }
166 213
214 /* Is IP session <0> tagged too? */
215 if (info->flags & FLAG_IPS0_VLAN) {
216 /* drop all untagged packets */
217 if (!tci)
218 goto error;
219 /* map MBIM_IPS0_VID to IPS<0> */
220 if (tci == MBIM_IPS0_VID)
221 tci = 0;
222 }
223
167 /* mapping VLANs to MBIM sessions: 224 /* mapping VLANs to MBIM sessions:
168 * no tag => IPS session <0> 225 * no tag => IPS session <0> if !FLAG_IPS0_VLAN
169 * 1 - 255 => IPS session <vlanid> 226 * 1 - 255 => IPS session <vlanid>
170 * 256 - 511 => DSS session <vlanid - 256> 227 * 256 - 511 => DSS session <vlanid - 256>
171 * 512 - 4095 => unsupported, drop 228 * 512 - 4093 => unsupported, drop
229 * 4094 => IPS session <0> if FLAG_IPS0_VLAN
172 */ 230 */
231
173 switch (tci & 0x0f00) { 232 switch (tci & 0x0f00) {
174 case 0x0000: /* VLAN ID 0 - 255 */ 233 case 0x0000: /* VLAN ID 0 - 255 */
175 if (!is_ip) 234 if (!is_ip)
@@ -268,7 +327,7 @@ static struct sk_buff *cdc_mbim_process_dgram(struct usbnet *dev, u8 *buf, size_
268 __be16 proto = htons(ETH_P_802_3); 327 __be16 proto = htons(ETH_P_802_3);
269 struct sk_buff *skb = NULL; 328 struct sk_buff *skb = NULL;
270 329
271 if (tci < 256) { /* IPS session? */ 330 if (tci < 256 || tci == MBIM_IPS0_VID) { /* IPS session? */
272 if (len < sizeof(struct iphdr)) 331 if (len < sizeof(struct iphdr))
273 goto err; 332 goto err;
274 333
@@ -338,6 +397,9 @@ next_ndp:
338 case cpu_to_le32(USB_CDC_MBIM_NDP16_IPS_SIGN): 397 case cpu_to_le32(USB_CDC_MBIM_NDP16_IPS_SIGN):
339 c = (u8 *)&ndp16->dwSignature; 398 c = (u8 *)&ndp16->dwSignature;
340 tci = c[3]; 399 tci = c[3];
400 /* tag IPS<0> packets too if MBIM_IPS0_VID exists */
401 if (!tci && info->flags & FLAG_IPS0_VLAN)
402 tci = MBIM_IPS0_VID;
341 break; 403 break;
342 case cpu_to_le32(USB_CDC_MBIM_NDP16_DSS_SIGN): 404 case cpu_to_le32(USB_CDC_MBIM_NDP16_DSS_SIGN):
343 c = (u8 *)&ndp16->dwSignature; 405 c = (u8 *)&ndp16->dwSignature;