aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--net/bluetooth/l2cap.c119
1 files changed, 112 insertions, 7 deletions
diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c
index 1c35c328181d..cfd672419315 100644
--- a/net/bluetooth/l2cap.c
+++ b/net/bluetooth/l2cap.c
@@ -3329,12 +3329,111 @@ static void l2cap_add_to_srej_queue(struct sock *sk, struct sk_buff *skb, u8 tx_
3329 __skb_queue_tail(SREJ_QUEUE(sk), skb); 3329 __skb_queue_tail(SREJ_QUEUE(sk), skb);
3330} 3330}
3331 3331
3332static int l2cap_sar_reassembly_sdu(struct sock *sk, struct sk_buff *skb, u16 control) 3332static int l2cap_ertm_reassembly_sdu(struct sock *sk, struct sk_buff *skb, u16 control)
3333{
3334 struct l2cap_pinfo *pi = l2cap_pi(sk);
3335 struct sk_buff *_skb;
3336 int err = 0;
3337
3338 switch (control & L2CAP_CTRL_SAR) {
3339 case L2CAP_SDU_UNSEGMENTED:
3340 if (pi->conn_state & L2CAP_CONN_SAR_SDU)
3341 goto drop;
3342
3343 err = sock_queue_rcv_skb(sk, skb);
3344 if (!err)
3345 return err;
3346
3347 break;
3348
3349 case L2CAP_SDU_START:
3350 if (pi->conn_state & L2CAP_CONN_SAR_SDU)
3351 goto drop;
3352
3353 pi->sdu_len = get_unaligned_le16(skb->data);
3354 skb_pull(skb, 2);
3355
3356 if (pi->sdu_len > pi->imtu)
3357 goto disconnect;
3358
3359 pi->sdu = bt_skb_alloc(pi->sdu_len, GFP_ATOMIC);
3360 if (!pi->sdu) {
3361 err = -ENOMEM;
3362 break;
3363 }
3364
3365 memcpy(skb_put(pi->sdu, skb->len), skb->data, skb->len);
3366
3367 pi->conn_state |= L2CAP_CONN_SAR_SDU;
3368 pi->partial_sdu_len = skb->len;
3369 break;
3370
3371 case L2CAP_SDU_CONTINUE:
3372 if (!(pi->conn_state & L2CAP_CONN_SAR_SDU))
3373 goto disconnect;
3374
3375 if (!pi->sdu)
3376 goto disconnect;
3377
3378 memcpy(skb_put(pi->sdu, skb->len), skb->data, skb->len);
3379
3380 pi->partial_sdu_len += skb->len;
3381 if (pi->partial_sdu_len > pi->sdu_len)
3382 goto drop;
3383
3384 break;
3385
3386 case L2CAP_SDU_END:
3387 if (!(pi->conn_state & L2CAP_CONN_SAR_SDU))
3388 goto disconnect;
3389
3390 if (!pi->sdu)
3391 goto disconnect;
3392
3393 memcpy(skb_put(pi->sdu, skb->len), skb->data, skb->len);
3394
3395 pi->conn_state &= ~L2CAP_CONN_SAR_SDU;
3396 pi->partial_sdu_len += skb->len;
3397
3398 if (pi->partial_sdu_len > pi->imtu)
3399 goto drop;
3400
3401 if (pi->partial_sdu_len != pi->sdu_len)
3402 goto drop;
3403
3404 _skb = skb_clone(pi->sdu, GFP_ATOMIC);
3405 err = sock_queue_rcv_skb(sk, _skb);
3406 if (err < 0)
3407 kfree_skb(_skb);
3408
3409 kfree_skb(pi->sdu);
3410 break;
3411 }
3412
3413 kfree_skb(skb);
3414 return err;
3415
3416drop:
3417 kfree_skb(pi->sdu);
3418 pi->sdu = NULL;
3419
3420disconnect:
3421 l2cap_send_disconn_req(pi->conn, sk);
3422 kfree_skb(skb);
3423 return 0;
3424}
3425
3426static int l2cap_streaming_reassembly_sdu(struct sock *sk, struct sk_buff *skb, u16 control)
3333{ 3427{
3334 struct l2cap_pinfo *pi = l2cap_pi(sk); 3428 struct l2cap_pinfo *pi = l2cap_pi(sk);
3335 struct sk_buff *_skb; 3429 struct sk_buff *_skb;
3336 int err = -EINVAL; 3430 int err = -EINVAL;
3337 3431
3432 /*
3433 * TODO: We have to notify the userland if some data is lost with the
3434 * Streaming Mode.
3435 */
3436
3338 switch (control & L2CAP_CTRL_SAR) { 3437 switch (control & L2CAP_CTRL_SAR) {
3339 case L2CAP_SDU_UNSEGMENTED: 3438 case L2CAP_SDU_UNSEGMENTED:
3340 if (pi->conn_state & L2CAP_CONN_SAR_SDU) { 3439 if (pi->conn_state & L2CAP_CONN_SAR_SDU) {
@@ -3429,7 +3528,7 @@ static void l2cap_check_srej_gap(struct sock *sk, u8 tx_seq)
3429 3528
3430 skb = skb_dequeue(SREJ_QUEUE(sk)); 3529 skb = skb_dequeue(SREJ_QUEUE(sk));
3431 control = bt_cb(skb)->sar << L2CAP_CTRL_SAR_SHIFT; 3530 control = bt_cb(skb)->sar << L2CAP_CTRL_SAR_SHIFT;
3432 l2cap_sar_reassembly_sdu(sk, skb, control); 3531 l2cap_ertm_reassembly_sdu(sk, skb, control);
3433 l2cap_pi(sk)->buffer_seq_srej = 3532 l2cap_pi(sk)->buffer_seq_srej =
3434 (l2cap_pi(sk)->buffer_seq_srej + 1) % 64; 3533 (l2cap_pi(sk)->buffer_seq_srej + 1) % 64;
3435 tx_seq++; 3534 tx_seq++;
@@ -3566,7 +3665,7 @@ expected:
3566 3665
3567 pi->buffer_seq = (pi->buffer_seq + 1) % 64; 3666 pi->buffer_seq = (pi->buffer_seq + 1) % 64;
3568 3667
3569 err = l2cap_sar_reassembly_sdu(sk, skb, rx_control); 3668 err = l2cap_ertm_reassembly_sdu(sk, skb, rx_control);
3570 if (err < 0) 3669 if (err < 0)
3571 return err; 3670 return err;
3572 3671
@@ -3790,8 +3889,10 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk
3790 * Receiver will miss it and start proper recovery 3889 * Receiver will miss it and start proper recovery
3791 * procedures and ask retransmission. 3890 * procedures and ask retransmission.
3792 */ 3891 */
3793 if (len > pi->mps) 3892 if (len > pi->mps) {
3893 l2cap_send_disconn_req(pi->conn, sk);
3794 goto drop; 3894 goto drop;
3895 }
3795 3896
3796 if (l2cap_check_fcs(pi, skb)) 3897 if (l2cap_check_fcs(pi, skb))
3797 goto drop; 3898 goto drop;
@@ -3813,13 +3914,17 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk
3813 } 3914 }
3814 3915
3815 if (__is_iframe(control)) { 3916 if (__is_iframe(control)) {
3816 if (len < 4) 3917 if (len < 4) {
3918 l2cap_send_disconn_req(pi->conn, sk);
3817 goto drop; 3919 goto drop;
3920 }
3818 3921
3819 l2cap_data_channel_iframe(sk, control, skb); 3922 l2cap_data_channel_iframe(sk, control, skb);
3820 } else { 3923 } else {
3821 if (len != 0) 3924 if (len != 0) {
3925 l2cap_send_disconn_req(pi->conn, sk);
3822 goto drop; 3926 goto drop;
3927 }
3823 3928
3824 l2cap_data_channel_sframe(sk, control, skb); 3929 l2cap_data_channel_sframe(sk, control, skb);
3825 } 3930 }
@@ -3850,7 +3955,7 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk
3850 else 3955 else
3851 pi->expected_tx_seq = (tx_seq + 1) % 64; 3956 pi->expected_tx_seq = (tx_seq + 1) % 64;
3852 3957
3853 l2cap_sar_reassembly_sdu(sk, skb, control); 3958 l2cap_streaming_reassembly_sdu(sk, skb, control);
3854 3959
3855 goto done; 3960 goto done;
3856 3961