diff options
author | Alexey Orishko <alexey.orishko@gmail.com> | 2012-03-14 07:26:12 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2012-03-16 05:08:36 -0400 |
commit | d5ddb4a59ed43b4c569b4efa8b508d50ef140cc6 (patch) | |
tree | c8fbd659c3bc62fe64f16408954e3fb66eac8a17 /drivers/net/usb | |
parent | 3f658cde9401bd45429ee720cf2c69f0bc5547b9 (diff) |
cdc_ncm: avoid discarding datagrams in rx path
Changes:
- removed a limit for amount of datagrams for IN NTB
- using pointer to traverse NTB in rx_fixup()
- renamed "temp" to "len" in rx_fixup()
- do NTB sequence number check in rx path
Tested on Intel/ARM.
Reviewed-by: Sjur Brændeland <sjur.brandeland@stericsson.com>
Tested-by: Dmitry Tarnyagin <Dmitry.Tarnyagin@stericsson.com>
Signed-off-by: Alexey Orishko <alexey.orishko@stericsson.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/usb')
-rw-r--r-- | drivers/net/usb/cdc_ncm.c | 102 |
1 files changed, 47 insertions, 55 deletions
diff --git a/drivers/net/usb/cdc_ncm.c b/drivers/net/usb/cdc_ncm.c index f8f194658412..7adc9f6b0ea1 100644 --- a/drivers/net/usb/cdc_ncm.c +++ b/drivers/net/usb/cdc_ncm.c | |||
@@ -71,13 +71,10 @@ | |||
71 | 71 | ||
72 | /* | 72 | /* |
73 | * Maximum amount of datagrams in NCM Datagram Pointer Table, not counting | 73 | * Maximum amount of datagrams in NCM Datagram Pointer Table, not counting |
74 | * the last NULL entry. Any additional datagrams in NTB would be discarded. | 74 | * the last NULL entry. |
75 | */ | 75 | */ |
76 | #define CDC_NCM_DPT_DATAGRAMS_MAX 40 | 76 | #define CDC_NCM_DPT_DATAGRAMS_MAX 40 |
77 | 77 | ||
78 | /* Maximum amount of IN datagrams in NTB */ | ||
79 | #define CDC_NCM_DPT_DATAGRAMS_IN_MAX 0 /* unlimited */ | ||
80 | |||
81 | /* Restart the timer, if amount of datagrams is less than given value */ | 78 | /* Restart the timer, if amount of datagrams is less than given value */ |
82 | #define CDC_NCM_RESTART_TIMER_DATAGRAM_CNT 3 | 79 | #define CDC_NCM_RESTART_TIMER_DATAGRAM_CNT 3 |
83 | #define CDC_NCM_TIMER_PENDING_CNT 2 | 80 | #define CDC_NCM_TIMER_PENDING_CNT 2 |
@@ -95,7 +92,6 @@ struct cdc_ncm_data { | |||
95 | }; | 92 | }; |
96 | 93 | ||
97 | struct cdc_ncm_ctx { | 94 | struct cdc_ncm_ctx { |
98 | struct cdc_ncm_data rx_ncm; | ||
99 | struct cdc_ncm_data tx_ncm; | 95 | struct cdc_ncm_data tx_ncm; |
100 | struct usb_cdc_ncm_ntb_parameters ncm_parm; | 96 | struct usb_cdc_ncm_ntb_parameters ncm_parm; |
101 | struct hrtimer tx_timer; | 97 | struct hrtimer tx_timer; |
@@ -135,6 +131,7 @@ struct cdc_ncm_ctx { | |||
135 | u16 tx_modulus; | 131 | u16 tx_modulus; |
136 | u16 tx_ndp_modulus; | 132 | u16 tx_ndp_modulus; |
137 | u16 tx_seq; | 133 | u16 tx_seq; |
134 | u16 rx_seq; | ||
138 | u16 connected; | 135 | u16 connected; |
139 | }; | 136 | }; |
140 | 137 | ||
@@ -956,108 +953,103 @@ error: | |||
956 | static int cdc_ncm_rx_fixup(struct usbnet *dev, struct sk_buff *skb_in) | 953 | static int cdc_ncm_rx_fixup(struct usbnet *dev, struct sk_buff *skb_in) |
957 | { | 954 | { |
958 | struct sk_buff *skb; | 955 | struct sk_buff *skb; |
959 | struct cdc_ncm_ctx *ctx; | 956 | struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0]; |
960 | int sumlen; | 957 | int len; |
961 | int actlen; | ||
962 | int temp; | ||
963 | int nframes; | 958 | int nframes; |
964 | int x; | 959 | int x; |
965 | int offset; | 960 | int offset; |
961 | struct usb_cdc_ncm_nth16 *nth16; | ||
962 | struct usb_cdc_ncm_ndp16 *ndp16; | ||
963 | struct usb_cdc_ncm_dpe16 *dpe16; | ||
966 | 964 | ||
967 | ctx = (struct cdc_ncm_ctx *)dev->data[0]; | ||
968 | if (ctx == NULL) | 965 | if (ctx == NULL) |
969 | goto error; | 966 | goto error; |
970 | 967 | ||
971 | actlen = skb_in->len; | 968 | if (skb_in->len < (sizeof(struct usb_cdc_ncm_nth16) + |
972 | sumlen = CDC_NCM_NTB_MAX_SIZE_RX; | 969 | sizeof(struct usb_cdc_ncm_ndp16))) { |
973 | |||
974 | if (actlen < (sizeof(ctx->rx_ncm.nth16) + sizeof(ctx->rx_ncm.ndp16))) { | ||
975 | pr_debug("frame too short\n"); | 970 | pr_debug("frame too short\n"); |
976 | goto error; | 971 | goto error; |
977 | } | 972 | } |
978 | 973 | ||
979 | memcpy(&(ctx->rx_ncm.nth16), ((u8 *)skb_in->data), | 974 | nth16 = (struct usb_cdc_ncm_nth16 *)skb_in->data; |
980 | sizeof(ctx->rx_ncm.nth16)); | ||
981 | 975 | ||
982 | if (le32_to_cpu(ctx->rx_ncm.nth16.dwSignature) != | 976 | if (le32_to_cpu(nth16->dwSignature) != USB_CDC_NCM_NTH16_SIGN) { |
983 | USB_CDC_NCM_NTH16_SIGN) { | ||
984 | pr_debug("invalid NTH16 signature <%u>\n", | 977 | pr_debug("invalid NTH16 signature <%u>\n", |
985 | le32_to_cpu(ctx->rx_ncm.nth16.dwSignature)); | 978 | le32_to_cpu(nth16->dwSignature)); |
986 | goto error; | 979 | goto error; |
987 | } | 980 | } |
988 | 981 | ||
989 | temp = le16_to_cpu(ctx->rx_ncm.nth16.wBlockLength); | 982 | len = le16_to_cpu(nth16->wBlockLength); |
990 | if (temp > sumlen) { | 983 | if (len > ctx->rx_max) { |
991 | pr_debug("unsupported NTB block length %u/%u\n", temp, sumlen); | 984 | pr_debug("unsupported NTB block length %u/%u\n", len, |
985 | ctx->rx_max); | ||
992 | goto error; | 986 | goto error; |
993 | } | 987 | } |
994 | 988 | ||
995 | temp = le16_to_cpu(ctx->rx_ncm.nth16.wNdpIndex); | 989 | if ((ctx->rx_seq + 1) != le16_to_cpu(nth16->wSequence) && |
996 | if ((temp + sizeof(ctx->rx_ncm.ndp16)) > actlen) { | 990 | (ctx->rx_seq || le16_to_cpu(nth16->wSequence)) && |
997 | pr_debug("invalid DPT16 index\n"); | 991 | !((ctx->rx_seq == 0xffff) && !le16_to_cpu(nth16->wSequence))) { |
992 | pr_debug("sequence number glitch prev=%d curr=%d\n", | ||
993 | ctx->rx_seq, le16_to_cpu(nth16->wSequence)); | ||
994 | } | ||
995 | ctx->rx_seq = le16_to_cpu(nth16->wSequence); | ||
996 | |||
997 | len = le16_to_cpu(nth16->wNdpIndex); | ||
998 | if ((len + sizeof(struct usb_cdc_ncm_ndp16)) > skb_in->len) { | ||
999 | pr_debug("invalid DPT16 index <%u>\n", | ||
1000 | le16_to_cpu(nth16->wNdpIndex)); | ||
998 | goto error; | 1001 | goto error; |
999 | } | 1002 | } |
1000 | 1003 | ||
1001 | memcpy(&(ctx->rx_ncm.ndp16), ((u8 *)skb_in->data) + temp, | 1004 | ndp16 = (struct usb_cdc_ncm_ndp16 *)(((u8 *)skb_in->data) + len); |
1002 | sizeof(ctx->rx_ncm.ndp16)); | ||
1003 | 1005 | ||
1004 | if (le32_to_cpu(ctx->rx_ncm.ndp16.dwSignature) != | 1006 | if (le32_to_cpu(ndp16->dwSignature) != USB_CDC_NCM_NDP16_NOCRC_SIGN) { |
1005 | USB_CDC_NCM_NDP16_NOCRC_SIGN) { | ||
1006 | pr_debug("invalid DPT16 signature <%u>\n", | 1007 | pr_debug("invalid DPT16 signature <%u>\n", |
1007 | le32_to_cpu(ctx->rx_ncm.ndp16.dwSignature)); | 1008 | le32_to_cpu(ndp16->dwSignature)); |
1008 | goto error; | 1009 | goto error; |
1009 | } | 1010 | } |
1010 | 1011 | ||
1011 | if (le16_to_cpu(ctx->rx_ncm.ndp16.wLength) < | 1012 | if (le16_to_cpu(ndp16->wLength) < USB_CDC_NCM_NDP16_LENGTH_MIN) { |
1012 | USB_CDC_NCM_NDP16_LENGTH_MIN) { | ||
1013 | pr_debug("invalid DPT16 length <%u>\n", | 1013 | pr_debug("invalid DPT16 length <%u>\n", |
1014 | le32_to_cpu(ctx->rx_ncm.ndp16.dwSignature)); | 1014 | le32_to_cpu(ndp16->dwSignature)); |
1015 | goto error; | 1015 | goto error; |
1016 | } | 1016 | } |
1017 | 1017 | ||
1018 | nframes = ((le16_to_cpu(ctx->rx_ncm.ndp16.wLength) - | 1018 | nframes = ((le16_to_cpu(ndp16->wLength) - |
1019 | sizeof(struct usb_cdc_ncm_ndp16)) / | 1019 | sizeof(struct usb_cdc_ncm_ndp16)) / |
1020 | sizeof(struct usb_cdc_ncm_dpe16)); | 1020 | sizeof(struct usb_cdc_ncm_dpe16)); |
1021 | nframes--; /* we process NDP entries except for the last one */ | 1021 | nframes--; /* we process NDP entries except for the last one */ |
1022 | 1022 | ||
1023 | pr_debug("nframes = %u\n", nframes); | 1023 | len += sizeof(struct usb_cdc_ncm_ndp16); |
1024 | 1024 | ||
1025 | temp += sizeof(ctx->rx_ncm.ndp16); | 1025 | if ((len + nframes * (sizeof(struct usb_cdc_ncm_dpe16))) > |
1026 | 1026 | skb_in->len) { | |
1027 | if ((temp + nframes * (sizeof(struct usb_cdc_ncm_dpe16))) > actlen) { | ||
1028 | pr_debug("Invalid nframes = %d\n", nframes); | 1027 | pr_debug("Invalid nframes = %d\n", nframes); |
1029 | goto error; | 1028 | goto error; |
1030 | } | 1029 | } |
1031 | 1030 | ||
1032 | if (nframes > CDC_NCM_DPT_DATAGRAMS_MAX) { | 1031 | dpe16 = (struct usb_cdc_ncm_dpe16 *)(((u8 *)skb_in->data) + len); |
1033 | pr_debug("Truncating number of frames from %u to %u\n", | ||
1034 | nframes, CDC_NCM_DPT_DATAGRAMS_MAX); | ||
1035 | nframes = CDC_NCM_DPT_DATAGRAMS_MAX; | ||
1036 | } | ||
1037 | |||
1038 | memcpy(&(ctx->rx_ncm.dpe16), ((u8 *)skb_in->data) + temp, | ||
1039 | nframes * (sizeof(struct usb_cdc_ncm_dpe16))); | ||
1040 | 1032 | ||
1041 | for (x = 0; x < nframes; x++) { | 1033 | for (x = 0; x < nframes; x++, dpe16++) { |
1042 | offset = le16_to_cpu(ctx->rx_ncm.dpe16[x].wDatagramIndex); | 1034 | offset = le16_to_cpu(dpe16->wDatagramIndex); |
1043 | temp = le16_to_cpu(ctx->rx_ncm.dpe16[x].wDatagramLength); | 1035 | len = le16_to_cpu(dpe16->wDatagramLength); |
1044 | 1036 | ||
1045 | /* | 1037 | /* |
1046 | * CDC NCM ch. 3.7 | 1038 | * CDC NCM ch. 3.7 |
1047 | * All entries after first NULL entry are to be ignored | 1039 | * All entries after first NULL entry are to be ignored |
1048 | */ | 1040 | */ |
1049 | if ((offset == 0) || (temp == 0)) { | 1041 | if ((offset == 0) || (len == 0)) { |
1050 | if (!x) | 1042 | if (!x) |
1051 | goto error; /* empty NTB */ | 1043 | goto error; /* empty NTB */ |
1052 | break; | 1044 | break; |
1053 | } | 1045 | } |
1054 | 1046 | ||
1055 | /* sanity checking */ | 1047 | /* sanity checking */ |
1056 | if (((offset + temp) > actlen) || | 1048 | if (((offset + len) > skb_in->len) || |
1057 | (temp > CDC_NCM_MAX_DATAGRAM_SIZE) || (temp < ETH_HLEN)) { | 1049 | (len > ctx->rx_max) || (len < ETH_HLEN)) { |
1058 | pr_debug("invalid frame detected (ignored)" | 1050 | pr_debug("invalid frame detected (ignored)" |
1059 | "offset[%u]=%u, length=%u, skb=%p\n", | 1051 | "offset[%u]=%u, length=%u, skb=%p\n", |
1060 | x, offset, temp, skb_in); | 1052 | x, offset, len, skb_in); |
1061 | if (!x) | 1053 | if (!x) |
1062 | goto error; | 1054 | goto error; |
1063 | break; | 1055 | break; |
@@ -1066,9 +1058,9 @@ static int cdc_ncm_rx_fixup(struct usbnet *dev, struct sk_buff *skb_in) | |||
1066 | skb = skb_clone(skb_in, GFP_ATOMIC); | 1058 | skb = skb_clone(skb_in, GFP_ATOMIC); |
1067 | if (!skb) | 1059 | if (!skb) |
1068 | goto error; | 1060 | goto error; |
1069 | skb->len = temp; | 1061 | skb->len = len; |
1070 | skb->data = ((u8 *)skb_in->data) + offset; | 1062 | skb->data = ((u8 *)skb_in->data) + offset; |
1071 | skb_set_tail_pointer(skb, temp); | 1063 | skb_set_tail_pointer(skb, len); |
1072 | usbnet_skb_return(dev, skb); | 1064 | usbnet_skb_return(dev, skb); |
1073 | } | 1065 | } |
1074 | } | 1066 | } |