diff options
author | Gustavo F. Padovan <gustavo@las.ic.unicamp.br> | 2009-08-20 21:26:02 -0400 |
---|---|---|
committer | Marcel Holtmann <marcel@holtmann.org> | 2009-08-22 17:59:49 -0400 |
commit | fcc203c30d72dde82692f6b761a80e5ca5fdd8fa (patch) | |
tree | 04eb154db3be85574efe74542f528efb39abf5bb /net/bluetooth | |
parent | 6840ed0770d79b9bb0800e5e026a067040ef18f5 (diff) |
Bluetooth: Add support for FCS option to L2CAP
Implement CRC16 check for L2CAP packets. FCS is used by Streaming Mode and
Enhanced Retransmission Mode and is a extra check for the packet content.
Using CRC16 is the default, L2CAP won't use FCS only when both side send
a "No FCS" request.
Initially based on a patch from Nathan Holstein <nathan@lampreynetworks.com>
Signed-off-by: Gustavo F. Padovan <gustavo@las.ic.unicamp.br>
Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
Diffstat (limited to 'net/bluetooth')
-rw-r--r-- | net/bluetooth/l2cap.c | 101 |
1 files changed, 95 insertions, 6 deletions
diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 7f835e761822..4c319003c290 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c | |||
@@ -41,6 +41,7 @@ | |||
41 | #include <linux/list.h> | 41 | #include <linux/list.h> |
42 | #include <linux/device.h> | 42 | #include <linux/device.h> |
43 | #include <linux/uaccess.h> | 43 | #include <linux/uaccess.h> |
44 | #include <linux/crc16.h> | ||
44 | #include <net/sock.h> | 45 | #include <net/sock.h> |
45 | 46 | ||
46 | #include <asm/system.h> | 47 | #include <asm/system.h> |
@@ -338,11 +339,14 @@ static inline int l2cap_send_sframe(struct l2cap_pinfo *pi, u16 control) | |||
338 | struct sk_buff *skb; | 339 | struct sk_buff *skb; |
339 | struct l2cap_hdr *lh; | 340 | struct l2cap_hdr *lh; |
340 | struct l2cap_conn *conn = pi->conn; | 341 | struct l2cap_conn *conn = pi->conn; |
341 | int count; | 342 | int count, hlen = L2CAP_HDR_SIZE + 2; |
343 | |||
344 | if (pi->fcs == L2CAP_FCS_CRC16) | ||
345 | hlen += 2; | ||
342 | 346 | ||
343 | BT_DBG("pi %p, control 0x%2.2x", pi, control); | 347 | BT_DBG("pi %p, control 0x%2.2x", pi, control); |
344 | 348 | ||
345 | count = min_t(unsigned int, conn->mtu, L2CAP_HDR_SIZE + 2); | 349 | count = min_t(unsigned int, conn->mtu, hlen); |
346 | control |= L2CAP_CTRL_FRAME_TYPE; | 350 | control |= L2CAP_CTRL_FRAME_TYPE; |
347 | 351 | ||
348 | skb = bt_skb_alloc(count, GFP_ATOMIC); | 352 | skb = bt_skb_alloc(count, GFP_ATOMIC); |
@@ -350,10 +354,15 @@ static inline int l2cap_send_sframe(struct l2cap_pinfo *pi, u16 control) | |||
350 | return -ENOMEM; | 354 | return -ENOMEM; |
351 | 355 | ||
352 | lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE); | 356 | lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE); |
353 | lh->len = cpu_to_le16(2); | 357 | lh->len = cpu_to_le16(hlen - L2CAP_HDR_SIZE); |
354 | lh->cid = cpu_to_le16(pi->dcid); | 358 | lh->cid = cpu_to_le16(pi->dcid); |
355 | put_unaligned_le16(control, skb_put(skb, 2)); | 359 | put_unaligned_le16(control, skb_put(skb, 2)); |
356 | 360 | ||
361 | if (pi->fcs == L2CAP_FCS_CRC16) { | ||
362 | u16 fcs = crc16(0, (u8 *)lh, count - 2); | ||
363 | put_unaligned_le16(fcs, skb_put(skb, 2)); | ||
364 | } | ||
365 | |||
357 | return hci_send_acl(pi->conn->hcon, skb, 0); | 366 | return hci_send_acl(pi->conn->hcon, skb, 0); |
358 | } | 367 | } |
359 | 368 | ||
@@ -1249,7 +1258,7 @@ static int l2cap_streaming_send(struct sock *sk) | |||
1249 | { | 1258 | { |
1250 | struct sk_buff *skb, *tx_skb; | 1259 | struct sk_buff *skb, *tx_skb; |
1251 | struct l2cap_pinfo *pi = l2cap_pi(sk); | 1260 | struct l2cap_pinfo *pi = l2cap_pi(sk); |
1252 | u16 control; | 1261 | u16 control, fcs; |
1253 | int err; | 1262 | int err; |
1254 | 1263 | ||
1255 | while ((skb = sk->sk_send_head)) { | 1264 | while ((skb = sk->sk_send_head)) { |
@@ -1259,6 +1268,11 @@ static int l2cap_streaming_send(struct sock *sk) | |||
1259 | control |= pi->next_tx_seq << L2CAP_CTRL_TXSEQ_SHIFT; | 1268 | control |= pi->next_tx_seq << L2CAP_CTRL_TXSEQ_SHIFT; |
1260 | put_unaligned_le16(control, tx_skb->data + L2CAP_HDR_SIZE); | 1269 | put_unaligned_le16(control, tx_skb->data + L2CAP_HDR_SIZE); |
1261 | 1270 | ||
1271 | if (l2cap_pi(sk)->fcs == L2CAP_FCS_CRC16) { | ||
1272 | fcs = crc16(0, (u8 *)tx_skb->data, tx_skb->len - 2); | ||
1273 | put_unaligned_le16(fcs, tx_skb->data + tx_skb->len - 2); | ||
1274 | } | ||
1275 | |||
1262 | err = l2cap_do_send(sk, tx_skb); | 1276 | err = l2cap_do_send(sk, tx_skb); |
1263 | if (err < 0) { | 1277 | if (err < 0) { |
1264 | l2cap_send_disconn_req(pi->conn, sk); | 1278 | l2cap_send_disconn_req(pi->conn, sk); |
@@ -1282,7 +1296,7 @@ static int l2cap_ertm_send(struct sock *sk) | |||
1282 | { | 1296 | { |
1283 | struct sk_buff *skb, *tx_skb; | 1297 | struct sk_buff *skb, *tx_skb; |
1284 | struct l2cap_pinfo *pi = l2cap_pi(sk); | 1298 | struct l2cap_pinfo *pi = l2cap_pi(sk); |
1285 | u16 control; | 1299 | u16 control, fcs; |
1286 | int err; | 1300 | int err; |
1287 | 1301 | ||
1288 | if (pi->conn_state & L2CAP_CONN_WAIT_F) | 1302 | if (pi->conn_state & L2CAP_CONN_WAIT_F) |
@@ -1305,6 +1319,11 @@ static int l2cap_ertm_send(struct sock *sk) | |||
1305 | put_unaligned_le16(control, tx_skb->data + L2CAP_HDR_SIZE); | 1319 | put_unaligned_le16(control, tx_skb->data + L2CAP_HDR_SIZE); |
1306 | 1320 | ||
1307 | 1321 | ||
1322 | if (l2cap_pi(sk)->fcs == L2CAP_FCS_CRC16) { | ||
1323 | fcs = crc16(0, (u8 *)skb->data, tx_skb->len - 2); | ||
1324 | put_unaligned_le16(fcs, skb->data + tx_skb->len - 2); | ||
1325 | } | ||
1326 | |||
1308 | err = l2cap_do_send(sk, tx_skb); | 1327 | err = l2cap_do_send(sk, tx_skb); |
1309 | if (err < 0) { | 1328 | if (err < 0) { |
1310 | l2cap_send_disconn_req(pi->conn, sk); | 1329 | l2cap_send_disconn_req(pi->conn, sk); |
@@ -1428,6 +1447,9 @@ static struct sk_buff *l2cap_create_iframe_pdu(struct sock *sk, struct msghdr *m | |||
1428 | if (sdulen) | 1447 | if (sdulen) |
1429 | hlen += 2; | 1448 | hlen += 2; |
1430 | 1449 | ||
1450 | if (l2cap_pi(sk)->fcs == L2CAP_FCS_CRC16) | ||
1451 | hlen += 2; | ||
1452 | |||
1431 | count = min_t(unsigned int, (conn->mtu - hlen), len); | 1453 | count = min_t(unsigned int, (conn->mtu - hlen), len); |
1432 | skb = bt_skb_send_alloc(sk, count + hlen, | 1454 | skb = bt_skb_send_alloc(sk, count + hlen, |
1433 | msg->msg_flags & MSG_DONTWAIT, &err); | 1455 | msg->msg_flags & MSG_DONTWAIT, &err); |
@@ -1448,6 +1470,9 @@ static struct sk_buff *l2cap_create_iframe_pdu(struct sock *sk, struct msghdr *m | |||
1448 | return ERR_PTR(err); | 1470 | return ERR_PTR(err); |
1449 | } | 1471 | } |
1450 | 1472 | ||
1473 | if (l2cap_pi(sk)->fcs == L2CAP_FCS_CRC16) | ||
1474 | put_unaligned_le16(0, skb_put(skb, 2)); | ||
1475 | |||
1451 | bt_cb(skb)->retries = 0; | 1476 | bt_cb(skb)->retries = 0; |
1452 | return skb; | 1477 | return skb; |
1453 | } | 1478 | } |
@@ -1633,6 +1658,7 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __us | |||
1633 | opts.omtu = l2cap_pi(sk)->omtu; | 1658 | opts.omtu = l2cap_pi(sk)->omtu; |
1634 | opts.flush_to = l2cap_pi(sk)->flush_to; | 1659 | opts.flush_to = l2cap_pi(sk)->flush_to; |
1635 | opts.mode = l2cap_pi(sk)->mode; | 1660 | opts.mode = l2cap_pi(sk)->mode; |
1661 | opts.fcs = l2cap_pi(sk)->fcs; | ||
1636 | 1662 | ||
1637 | len = min_t(unsigned int, sizeof(opts), optlen); | 1663 | len = min_t(unsigned int, sizeof(opts), optlen); |
1638 | if (copy_from_user((char *) &opts, optval, len)) { | 1664 | if (copy_from_user((char *) &opts, optval, len)) { |
@@ -1643,6 +1669,7 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __us | |||
1643 | l2cap_pi(sk)->imtu = opts.imtu; | 1669 | l2cap_pi(sk)->imtu = opts.imtu; |
1644 | l2cap_pi(sk)->omtu = opts.omtu; | 1670 | l2cap_pi(sk)->omtu = opts.omtu; |
1645 | l2cap_pi(sk)->mode = opts.mode; | 1671 | l2cap_pi(sk)->mode = opts.mode; |
1672 | l2cap_pi(sk)->fcs = opts.fcs; | ||
1646 | break; | 1673 | break; |
1647 | 1674 | ||
1648 | case L2CAP_LM: | 1675 | case L2CAP_LM: |
@@ -1756,6 +1783,7 @@ static int l2cap_sock_getsockopt_old(struct socket *sock, int optname, char __us | |||
1756 | opts.omtu = l2cap_pi(sk)->omtu; | 1783 | opts.omtu = l2cap_pi(sk)->omtu; |
1757 | opts.flush_to = l2cap_pi(sk)->flush_to; | 1784 | opts.flush_to = l2cap_pi(sk)->flush_to; |
1758 | opts.mode = l2cap_pi(sk)->mode; | 1785 | opts.mode = l2cap_pi(sk)->mode; |
1786 | opts.fcs = l2cap_pi(sk)->fcs; | ||
1759 | 1787 | ||
1760 | len = min_t(unsigned int, len, sizeof(opts)); | 1788 | len = min_t(unsigned int, len, sizeof(opts)); |
1761 | if (copy_to_user(optval, (char *) &opts, len)) | 1789 | if (copy_to_user(optval, (char *) &opts, len)) |
@@ -2154,6 +2182,15 @@ done: | |||
2154 | 2182 | ||
2155 | l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, | 2183 | l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, |
2156 | sizeof(rfc), (unsigned long) &rfc); | 2184 | sizeof(rfc), (unsigned long) &rfc); |
2185 | |||
2186 | if (!(pi->conn->feat_mask & L2CAP_FEAT_FCS)) | ||
2187 | break; | ||
2188 | |||
2189 | if (pi->fcs == L2CAP_FCS_NONE || | ||
2190 | pi->conf_state & L2CAP_CONF_NO_FCS_RECV) { | ||
2191 | pi->fcs = L2CAP_FCS_NONE; | ||
2192 | l2cap_add_conf_opt(&ptr, L2CAP_CONF_FCS, 1, pi->fcs); | ||
2193 | } | ||
2157 | break; | 2194 | break; |
2158 | 2195 | ||
2159 | case L2CAP_MODE_STREAMING: | 2196 | case L2CAP_MODE_STREAMING: |
@@ -2166,6 +2203,15 @@ done: | |||
2166 | 2203 | ||
2167 | l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, | 2204 | l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, |
2168 | sizeof(rfc), (unsigned long) &rfc); | 2205 | sizeof(rfc), (unsigned long) &rfc); |
2206 | |||
2207 | if (!(pi->conn->feat_mask & L2CAP_FEAT_FCS)) | ||
2208 | break; | ||
2209 | |||
2210 | if (pi->fcs == L2CAP_FCS_NONE || | ||
2211 | pi->conf_state & L2CAP_CONF_NO_FCS_RECV) { | ||
2212 | pi->fcs = L2CAP_FCS_NONE; | ||
2213 | l2cap_add_conf_opt(&ptr, L2CAP_CONF_FCS, 1, pi->fcs); | ||
2214 | } | ||
2169 | break; | 2215 | break; |
2170 | } | 2216 | } |
2171 | 2217 | ||
@@ -2217,6 +2263,12 @@ static int l2cap_parse_conf_req(struct sock *sk, void *data) | |||
2217 | memcpy(&rfc, (void *) val, olen); | 2263 | memcpy(&rfc, (void *) val, olen); |
2218 | break; | 2264 | break; |
2219 | 2265 | ||
2266 | case L2CAP_CONF_FCS: | ||
2267 | if (val == L2CAP_FCS_NONE) | ||
2268 | pi->conf_state |= L2CAP_CONF_NO_FCS_RECV; | ||
2269 | |||
2270 | break; | ||
2271 | |||
2220 | default: | 2272 | default: |
2221 | if (hint) | 2273 | if (hint) |
2222 | break; | 2274 | break; |
@@ -2638,6 +2690,10 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr | |||
2638 | goto unlock; | 2690 | goto unlock; |
2639 | 2691 | ||
2640 | if (l2cap_pi(sk)->conf_state & L2CAP_CONF_INPUT_DONE) { | 2692 | if (l2cap_pi(sk)->conf_state & L2CAP_CONF_INPUT_DONE) { |
2693 | if (!(l2cap_pi(sk)->conf_state & L2CAP_CONF_NO_FCS_RECV) | ||
2694 | || l2cap_pi(sk)->fcs != L2CAP_FCS_NONE) | ||
2695 | l2cap_pi(sk)->fcs = L2CAP_FCS_CRC16; | ||
2696 | |||
2641 | sk->sk_state = BT_CONNECTED; | 2697 | sk->sk_state = BT_CONNECTED; |
2642 | l2cap_pi(sk)->next_tx_seq = 0; | 2698 | l2cap_pi(sk)->next_tx_seq = 0; |
2643 | l2cap_pi(sk)->expected_ack_seq = 0; | 2699 | l2cap_pi(sk)->expected_ack_seq = 0; |
@@ -2722,6 +2778,10 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr | |||
2722 | l2cap_pi(sk)->conf_state |= L2CAP_CONF_INPUT_DONE; | 2778 | l2cap_pi(sk)->conf_state |= L2CAP_CONF_INPUT_DONE; |
2723 | 2779 | ||
2724 | if (l2cap_pi(sk)->conf_state & L2CAP_CONF_OUTPUT_DONE) { | 2780 | if (l2cap_pi(sk)->conf_state & L2CAP_CONF_OUTPUT_DONE) { |
2781 | if (!(l2cap_pi(sk)->conf_state & L2CAP_CONF_NO_FCS_RECV) | ||
2782 | || l2cap_pi(sk)->fcs != L2CAP_FCS_NONE) | ||
2783 | l2cap_pi(sk)->fcs = L2CAP_FCS_CRC16; | ||
2784 | |||
2725 | sk->sk_state = BT_CONNECTED; | 2785 | sk->sk_state = BT_CONNECTED; |
2726 | l2cap_pi(sk)->expected_tx_seq = 0; | 2786 | l2cap_pi(sk)->expected_tx_seq = 0; |
2727 | l2cap_pi(sk)->num_to_ack = 0; | 2787 | l2cap_pi(sk)->num_to_ack = 0; |
@@ -2809,7 +2869,8 @@ static inline int l2cap_information_req(struct l2cap_conn *conn, struct l2cap_cm | |||
2809 | rsp->type = cpu_to_le16(L2CAP_IT_FEAT_MASK); | 2869 | rsp->type = cpu_to_le16(L2CAP_IT_FEAT_MASK); |
2810 | rsp->result = cpu_to_le16(L2CAP_IR_SUCCESS); | 2870 | rsp->result = cpu_to_le16(L2CAP_IR_SUCCESS); |
2811 | if (enable_ertm) | 2871 | if (enable_ertm) |
2812 | feat_mask |= L2CAP_FEAT_ERTM | L2CAP_FEAT_STREAMING; | 2872 | feat_mask |= L2CAP_FEAT_ERTM | L2CAP_FEAT_STREAMING |
2873 | | L2CAP_FEAT_FCS; | ||
2813 | put_unaligned(cpu_to_le32(feat_mask), (__le32 *) rsp->data); | 2874 | put_unaligned(cpu_to_le32(feat_mask), (__le32 *) rsp->data); |
2814 | l2cap_send_cmd(conn, cmd->ident, | 2875 | l2cap_send_cmd(conn, cmd->ident, |
2815 | L2CAP_INFO_RSP, sizeof(buf), buf); | 2876 | L2CAP_INFO_RSP, sizeof(buf), buf); |
@@ -2961,6 +3022,22 @@ static inline void l2cap_sig_channel(struct l2cap_conn *conn, struct sk_buff *sk | |||
2961 | kfree_skb(skb); | 3022 | kfree_skb(skb); |
2962 | } | 3023 | } |
2963 | 3024 | ||
3025 | static int l2cap_check_fcs(struct l2cap_pinfo *pi, struct sk_buff *skb) | ||
3026 | { | ||
3027 | u16 our_fcs, rcv_fcs; | ||
3028 | int hdr_size = L2CAP_HDR_SIZE + 2; | ||
3029 | |||
3030 | if (pi->fcs == L2CAP_FCS_CRC16) { | ||
3031 | skb_trim(skb, skb->len - 2); | ||
3032 | rcv_fcs = get_unaligned_le16(skb->data + skb->len); | ||
3033 | our_fcs = crc16(0, skb->data - hdr_size, skb->len + hdr_size); | ||
3034 | |||
3035 | if (our_fcs != rcv_fcs) | ||
3036 | return -EINVAL; | ||
3037 | } | ||
3038 | return 0; | ||
3039 | } | ||
3040 | |||
2964 | static int l2cap_sar_reassembly_sdu(struct sock *sk, struct sk_buff *skb, u16 control) | 3041 | static int l2cap_sar_reassembly_sdu(struct sock *sk, struct sk_buff *skb, u16 control) |
2965 | { | 3042 | { |
2966 | struct l2cap_pinfo *pi = l2cap_pi(sk); | 3043 | struct l2cap_pinfo *pi = l2cap_pi(sk); |
@@ -3174,6 +3251,9 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk | |||
3174 | if (__is_sar_start(control)) | 3251 | if (__is_sar_start(control)) |
3175 | len -= 2; | 3252 | len -= 2; |
3176 | 3253 | ||
3254 | if (pi->fcs == L2CAP_FCS_CRC16) | ||
3255 | len -= 2; | ||
3256 | |||
3177 | /* | 3257 | /* |
3178 | * We can just drop the corrupted I-frame here. | 3258 | * We can just drop the corrupted I-frame here. |
3179 | * Receiver will miss it and start proper recovery | 3259 | * Receiver will miss it and start proper recovery |
@@ -3182,6 +3262,9 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk | |||
3182 | if (len > L2CAP_DEFAULT_MAX_PDU_SIZE) | 3262 | if (len > L2CAP_DEFAULT_MAX_PDU_SIZE) |
3183 | goto drop; | 3263 | goto drop; |
3184 | 3264 | ||
3265 | if (l2cap_check_fcs(pi, skb)) | ||
3266 | goto drop; | ||
3267 | |||
3185 | if (__is_iframe(control)) | 3268 | if (__is_iframe(control)) |
3186 | err = l2cap_data_channel_iframe(sk, control, skb); | 3269 | err = l2cap_data_channel_iframe(sk, control, skb); |
3187 | else | 3270 | else |
@@ -3199,9 +3282,15 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk | |||
3199 | if (__is_sar_start(control)) | 3282 | if (__is_sar_start(control)) |
3200 | len -= 2; | 3283 | len -= 2; |
3201 | 3284 | ||
3285 | if (pi->fcs == L2CAP_FCS_CRC16) | ||
3286 | len -= 2; | ||
3287 | |||
3202 | if (len > L2CAP_DEFAULT_MAX_PDU_SIZE || __is_sframe(control)) | 3288 | if (len > L2CAP_DEFAULT_MAX_PDU_SIZE || __is_sframe(control)) |
3203 | goto drop; | 3289 | goto drop; |
3204 | 3290 | ||
3291 | if (l2cap_check_fcs(pi, skb)) | ||
3292 | goto drop; | ||
3293 | |||
3205 | tx_seq = __get_txseq(control); | 3294 | tx_seq = __get_txseq(control); |
3206 | 3295 | ||
3207 | if (pi->expected_tx_seq == tx_seq) | 3296 | if (pi->expected_tx_seq == tx_seq) |