Commit a82c7ce5 authored by Bjørn Mork's avatar Bjørn Mork Committed by David S. Miller

net: cdc_ncm: map MBIM IPS SessionID to VLAN ID

MBIM devices can support up to 256 independent IP Streams.
The main network device will only handle SessionID 0. Mapping
SessionIDs 1 to 255 to VLANs using the SessionID as VLAN ID
allow userspace to use these streams with traditional tools
like vconfig.
Signed-off-by: default avatarBjørn Mork <bjorn@mork.no>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent bd329e12
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/netdevice.h> #include <linux/netdevice.h>
#include <linux/ethtool.h> #include <linux/ethtool.h>
#include <linux/if_vlan.h>
#include <linux/ip.h> #include <linux/ip.h>
#include <linux/mii.h> #include <linux/mii.h>
#include <linux/usb.h> #include <linux/usb.h>
...@@ -107,6 +108,9 @@ static int cdc_mbim_bind(struct usbnet *dev, struct usb_interface *intf) ...@@ -107,6 +108,9 @@ static int cdc_mbim_bind(struct usbnet *dev, struct usb_interface *intf)
/* MBIM cannot do ARP */ /* MBIM cannot do ARP */
dev->net->flags |= IFF_NOARP; dev->net->flags |= IFF_NOARP;
/* no need to put the VLAN tci in the packet headers */
dev->net->features |= NETIF_F_HW_VLAN_TX;
err: err:
return ret; return ret;
} }
...@@ -131,6 +135,9 @@ static struct sk_buff *cdc_mbim_tx_fixup(struct usbnet *dev, struct sk_buff *skb ...@@ -131,6 +135,9 @@ static struct sk_buff *cdc_mbim_tx_fixup(struct usbnet *dev, struct sk_buff *skb
struct sk_buff *skb_out; struct sk_buff *skb_out;
struct cdc_mbim_state *info = (void *)&dev->data; struct cdc_mbim_state *info = (void *)&dev->data;
struct cdc_ncm_ctx *ctx = info->ctx; struct cdc_ncm_ctx *ctx = info->ctx;
__le32 sign = cpu_to_le32(USB_CDC_MBIM_NDP16_IPS_SIGN);
u16 tci = 0;
u8 *c;
if (!ctx) if (!ctx)
goto error; goto error;
...@@ -139,6 +146,24 @@ static struct sk_buff *cdc_mbim_tx_fixup(struct usbnet *dev, struct sk_buff *skb ...@@ -139,6 +146,24 @@ static struct sk_buff *cdc_mbim_tx_fixup(struct usbnet *dev, struct sk_buff *skb
if (skb->len <= sizeof(ETH_HLEN)) if (skb->len <= sizeof(ETH_HLEN))
goto error; goto error;
/* mapping VLANs to MBIM sessions:
* no tag => IPS session <0>
* 1 - 255 => IPS session <vlanid>
* 256 - 4095 => unsupported, drop
*/
vlan_get_tag(skb, &tci);
switch (tci & 0x0f00) {
case 0x0000: /* VLAN ID 0 - 255 */
c = (u8 *)&sign;
c[3] = tci;
break;
default:
netif_err(dev, tx_err, dev->net,
"unsupported tci=0x%04x\n", tci);
goto error;
}
skb_reset_mac_header(skb); skb_reset_mac_header(skb);
switch (eth_hdr(skb)->h_proto) { switch (eth_hdr(skb)->h_proto) {
case htons(ETH_P_IP): case htons(ETH_P_IP):
...@@ -151,7 +176,7 @@ static struct sk_buff *cdc_mbim_tx_fixup(struct usbnet *dev, struct sk_buff *skb ...@@ -151,7 +176,7 @@ static struct sk_buff *cdc_mbim_tx_fixup(struct usbnet *dev, struct sk_buff *skb
} }
spin_lock_bh(&ctx->mtx); spin_lock_bh(&ctx->mtx);
skb_out = cdc_ncm_fill_tx_frame(ctx, skb, cpu_to_le32(USB_CDC_MBIM_NDP16_IPS_SIGN)); skb_out = cdc_ncm_fill_tx_frame(ctx, skb, sign);
spin_unlock_bh(&ctx->mtx); spin_unlock_bh(&ctx->mtx);
return skb_out; return skb_out;
...@@ -162,11 +187,14 @@ static struct sk_buff *cdc_mbim_tx_fixup(struct usbnet *dev, struct sk_buff *skb ...@@ -162,11 +187,14 @@ static struct sk_buff *cdc_mbim_tx_fixup(struct usbnet *dev, struct sk_buff *skb
return NULL; return NULL;
} }
static struct sk_buff *cdc_mbim_process_dgram(struct usbnet *dev, u8 *buf, size_t len) static struct sk_buff *cdc_mbim_process_dgram(struct usbnet *dev, u8 *buf, size_t len, u16 tci)
{ {
__be16 proto; __be16 proto;
struct sk_buff *skb = NULL; struct sk_buff *skb = NULL;
if (len < sizeof(struct iphdr))
goto err;
switch (*buf & 0xf0) { switch (*buf & 0xf0) {
case 0x40: case 0x40:
proto = htons(ETH_P_IP); proto = htons(ETH_P_IP);
...@@ -191,6 +219,10 @@ static struct sk_buff *cdc_mbim_process_dgram(struct usbnet *dev, u8 *buf, size_ ...@@ -191,6 +219,10 @@ static struct sk_buff *cdc_mbim_process_dgram(struct usbnet *dev, u8 *buf, size_
/* add datagram */ /* add datagram */
memcpy(skb_put(skb, len), buf, len); memcpy(skb_put(skb, len), buf, len);
/* map MBIM session to VLAN */
if (tci)
vlan_put_tag(skb, tci);
err: err:
return skb; return skb;
} }
...@@ -198,7 +230,8 @@ static struct sk_buff *cdc_mbim_process_dgram(struct usbnet *dev, u8 *buf, size_ ...@@ -198,7 +230,8 @@ static struct sk_buff *cdc_mbim_process_dgram(struct usbnet *dev, u8 *buf, size_
static int cdc_mbim_rx_fixup(struct usbnet *dev, struct sk_buff *skb_in) static int cdc_mbim_rx_fixup(struct usbnet *dev, struct sk_buff *skb_in)
{ {
struct sk_buff *skb; struct sk_buff *skb;
struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0]; struct cdc_mbim_state *info = (void *)&dev->data;
struct cdc_ncm_ctx *ctx = info->ctx;
int len; int len;
int nframes; int nframes;
int x; int x;
...@@ -207,6 +240,8 @@ static int cdc_mbim_rx_fixup(struct usbnet *dev, struct sk_buff *skb_in) ...@@ -207,6 +240,8 @@ static int cdc_mbim_rx_fixup(struct usbnet *dev, struct sk_buff *skb_in)
struct usb_cdc_ncm_dpe16 *dpe16; struct usb_cdc_ncm_dpe16 *dpe16;
int ndpoffset; int ndpoffset;
int loopcount = 50; /* arbitrary max preventing infinite loop */ int loopcount = 50; /* arbitrary max preventing infinite loop */
u8 *c;
u16 tci;
ndpoffset = cdc_ncm_rx_verify_nth16(ctx, skb_in); ndpoffset = cdc_ncm_rx_verify_nth16(ctx, skb_in);
if (ndpoffset < 0) if (ndpoffset < 0)
...@@ -219,9 +254,10 @@ static int cdc_mbim_rx_fixup(struct usbnet *dev, struct sk_buff *skb_in) ...@@ -219,9 +254,10 @@ static int cdc_mbim_rx_fixup(struct usbnet *dev, struct sk_buff *skb_in)
ndp16 = (struct usb_cdc_ncm_ndp16 *)(skb_in->data + ndpoffset); ndp16 = (struct usb_cdc_ncm_ndp16 *)(skb_in->data + ndpoffset);
/* only supporting IPS Session #0 for now */ switch (ndp16->dwSignature & cpu_to_le32(0x00ffffff)) {
switch (ndp16->dwSignature) {
case cpu_to_le32(USB_CDC_MBIM_NDP16_IPS_SIGN): case cpu_to_le32(USB_CDC_MBIM_NDP16_IPS_SIGN):
c = (u8 *)&ndp16->dwSignature;
tci = c[3];
break; break;
default: default:
netif_dbg(dev, rx_err, dev->net, netif_dbg(dev, rx_err, dev->net,
...@@ -247,8 +283,7 @@ static int cdc_mbim_rx_fixup(struct usbnet *dev, struct sk_buff *skb_in) ...@@ -247,8 +283,7 @@ static int cdc_mbim_rx_fixup(struct usbnet *dev, struct sk_buff *skb_in)
} }
/* sanity checking */ /* sanity checking */
if (((offset + len) > skb_in->len) || (len > ctx->rx_max) || if (((offset + len) > skb_in->len) || (len > ctx->rx_max)) {
(len < sizeof(struct iphdr))) {
netif_dbg(dev, rx_err, dev->net, netif_dbg(dev, rx_err, dev->net,
"invalid frame detected (ignored) offset[%u]=%u, length=%u, skb=%p\n", "invalid frame detected (ignored) offset[%u]=%u, length=%u, skb=%p\n",
x, offset, len, skb_in); x, offset, len, skb_in);
...@@ -256,7 +291,7 @@ static int cdc_mbim_rx_fixup(struct usbnet *dev, struct sk_buff *skb_in) ...@@ -256,7 +291,7 @@ static int cdc_mbim_rx_fixup(struct usbnet *dev, struct sk_buff *skb_in)
goto err_ndp; goto err_ndp;
break; break;
} else { } else {
skb = cdc_mbim_process_dgram(dev, skb_in->data + offset, len); skb = cdc_mbim_process_dgram(dev, skb_in->data + offset, len, tci);
if (!skb) if (!skb)
goto error; goto error;
usbnet_skb_return(dev, skb); usbnet_skb_return(dev, skb);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment