Skip to content

Commit

Permalink
net: cdc_ncm: process chained NDPs
Browse files Browse the repository at this point in the history
The NCM 1.0 spefication makes provisions for linking more than
one NDP into a single NTB. This is important for MBIM support,
where these NDPs might be of different types.

Following the chain of NDPs is also correct for NCM, and will
not change anything in the common case where there is only
one NDP

Signed-off-by: Bjørn Mork <bjorn@mork.no>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Bjørn Mork authored and David S. Miller committed Oct 23, 2012
1 parent 38396e4 commit 75d67d3
Showing 1 changed file with 21 additions and 14 deletions.
35 changes: 21 additions & 14 deletions drivers/net/usb/cdc_ncm.c
Original file line number Diff line number Diff line change
Expand Up @@ -977,6 +977,8 @@ static int cdc_ncm_rx_fixup(struct usbnet *dev, struct sk_buff *skb_in)
struct usb_cdc_ncm_nth16 *nth16;
struct usb_cdc_ncm_ndp16 *ndp16;
struct usb_cdc_ncm_dpe16 *dpe16;
int ndpoffset;
int loopcount = 50; /* arbitrary max preventing infinite loop */

if (ctx == NULL)
goto error;
Expand Down Expand Up @@ -1010,41 +1012,40 @@ static int cdc_ncm_rx_fixup(struct usbnet *dev, struct sk_buff *skb_in)
}
ctx->rx_seq = le16_to_cpu(nth16->wSequence);

len = le16_to_cpu(nth16->wNdpIndex);
if ((len + sizeof(struct usb_cdc_ncm_ndp16)) > skb_in->len) {
pr_debug("invalid DPT16 index <%u>\n",
le16_to_cpu(nth16->wNdpIndex));
ndpoffset = le16_to_cpu(nth16->wNdpIndex);
next_ndp:
if ((ndpoffset + sizeof(struct usb_cdc_ncm_ndp16)) > skb_in->len) {
pr_debug("invalid NDP offset <%u>\n", ndpoffset);
goto error;
}

ndp16 = (struct usb_cdc_ncm_ndp16 *)(((u8 *)skb_in->data) + len);
ndp16 = (struct usb_cdc_ncm_ndp16 *)(skb_in->data + ndpoffset);

if (le32_to_cpu(ndp16->dwSignature) != USB_CDC_NCM_NDP16_NOCRC_SIGN) {
pr_debug("invalid DPT16 signature <%u>\n",
le32_to_cpu(ndp16->dwSignature));
goto error;
goto err_ndp;
}

if (le16_to_cpu(ndp16->wLength) < USB_CDC_NCM_NDP16_LENGTH_MIN) {
pr_debug("invalid DPT16 length <%u>\n",
le32_to_cpu(ndp16->dwSignature));
goto error;
goto err_ndp;
}

nframes = ((le16_to_cpu(ndp16->wLength) -
sizeof(struct usb_cdc_ncm_ndp16)) /
sizeof(struct usb_cdc_ncm_dpe16));
nframes--; /* we process NDP entries except for the last one */

len += sizeof(struct usb_cdc_ncm_ndp16);
ndpoffset += sizeof(struct usb_cdc_ncm_ndp16);

if ((len + nframes * (sizeof(struct usb_cdc_ncm_dpe16))) >
if ((ndpoffset + nframes * (sizeof(struct usb_cdc_ncm_dpe16))) >
skb_in->len) {
pr_debug("Invalid nframes = %d\n", nframes);
goto error;
goto err_ndp;
}

dpe16 = (struct usb_cdc_ncm_dpe16 *)(((u8 *)skb_in->data) + len);
dpe16 = (struct usb_cdc_ncm_dpe16 *)(skb_in->data + ndpoffset);

for (x = 0; x < nframes; x++, dpe16++) {
offset = le16_to_cpu(dpe16->wDatagramIndex);
Expand All @@ -1056,7 +1057,7 @@ static int cdc_ncm_rx_fixup(struct usbnet *dev, struct sk_buff *skb_in)
*/
if ((offset == 0) || (len == 0)) {
if (!x)
goto error; /* empty NTB */
goto err_ndp; /* empty NTB */
break;
}

Expand All @@ -1067,7 +1068,7 @@ static int cdc_ncm_rx_fixup(struct usbnet *dev, struct sk_buff *skb_in)
"offset[%u]=%u, length=%u, skb=%p\n",
x, offset, len, skb_in);
if (!x)
goto error;
goto err_ndp;
break;

} else {
Expand All @@ -1080,6 +1081,12 @@ static int cdc_ncm_rx_fixup(struct usbnet *dev, struct sk_buff *skb_in)
usbnet_skb_return(dev, skb);
}
}
err_ndp:
/* are there more NDPs to process? */
ndpoffset = le16_to_cpu(ndp16->wNextNdpIndex);
if (ndpoffset && loopcount--)
goto next_ndp;

return 1;
error:
return 0;
Expand Down

0 comments on commit 75d67d3

Please sign in to comment.