Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 314392
b: refs/heads/master
c: 4b51dae
h: refs/heads/master
v: v3
  • Loading branch information
Mat Martineau authored and Johan Hedberg committed Jun 5, 2012
1 parent 86ba0c6 commit f4c8c15
Show file tree
Hide file tree
Showing 2 changed files with 131 additions and 7 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: cec8ab6e20a7fbdc056894ff7b3fbdbc2a82a563
refs/heads/master: 4b51dae96731c9d82f5634e75ac7ffd3b9c1b060
136 changes: 130 additions & 6 deletions trunk/net/bluetooth/l2cap_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -2420,6 +2420,13 @@ static int l2cap_tx(struct l2cap_chan *chan, struct l2cap_ctrl *control,
return err;
}

static void l2cap_pass_to_tx(struct l2cap_chan *chan,
struct l2cap_ctrl *control)
{
BT_DBG("chan %p, control %p", chan, control);
l2cap_tx(chan, control, 0, L2CAP_EV_RECV_REQSEQ_AND_FBIT);
}

/* Copy frame to all raw sockets on that connection */
static void l2cap_raw_recv(struct l2cap_conn *conn, struct sk_buff *skb)
{
Expand Down Expand Up @@ -4324,11 +4331,12 @@ static void append_skb_frag(struct sk_buff *skb,
skb->truesize += new_frag->truesize;
}

static int l2cap_reassemble_sdu(struct l2cap_chan *chan, struct sk_buff *skb, u32 control)
static int l2cap_reassemble_sdu(struct l2cap_chan *chan, struct sk_buff *skb,
struct l2cap_ctrl *control)
{
int err = -EINVAL;

switch (__get_ctrl_sar(chan, control)) {
switch (control->sar) {
case L2CAP_SAR_UNSEGMENTED:
if (chan->sdu)
break;
Expand Down Expand Up @@ -4463,7 +4471,6 @@ static void l2cap_check_srej_gap(struct l2cap_chan *chan, u16 tx_seq)

skb = skb_dequeue(&chan->srej_q);
control = __set_ctrl_sar(chan, bt_cb(skb)->control.sar);
err = l2cap_reassemble_sdu(chan, skb, control);

if (err < 0) {
l2cap_send_disconn_req(chan->conn, chan, ECONNRESET);
Expand Down Expand Up @@ -4637,7 +4644,6 @@ static inline int l2cap_data_channel_iframe(struct l2cap_chan *chan, u32 rx_cont
return 0;
}

err = l2cap_reassemble_sdu(chan, skb, rx_control);
chan->buffer_seq = __next_seq(chan, chan->buffer_seq);

if (err < 0) {
Expand Down Expand Up @@ -4822,6 +4828,93 @@ static inline int l2cap_data_channel_sframe(struct l2cap_chan *chan, u32 rx_cont
return 0;
}

static u8 l2cap_classify_txseq(struct l2cap_chan *chan, u16 txseq)
{
BT_DBG("chan %p, txseq %d", chan, txseq);

BT_DBG("last_acked_seq %d, expected_tx_seq %d", chan->last_acked_seq,
chan->expected_tx_seq);

if (chan->rx_state == L2CAP_RX_STATE_SREJ_SENT) {
if (__seq_offset(chan, txseq, chan->last_acked_seq) >=
chan->tx_win) {
/* See notes below regarding "double poll" and
* invalid packets.
*/
if (chan->tx_win <= ((chan->tx_win_max + 1) >> 1)) {
BT_DBG("Invalid/Ignore - after SREJ");
return L2CAP_TXSEQ_INVALID_IGNORE;
} else {
BT_DBG("Invalid - in window after SREJ sent");
return L2CAP_TXSEQ_INVALID;
}
}

if (chan->srej_list.head == txseq) {
BT_DBG("Expected SREJ");
return L2CAP_TXSEQ_EXPECTED_SREJ;
}

if (l2cap_ertm_seq_in_queue(&chan->srej_q, txseq)) {
BT_DBG("Duplicate SREJ - txseq already stored");
return L2CAP_TXSEQ_DUPLICATE_SREJ;
}

if (l2cap_seq_list_contains(&chan->srej_list, txseq)) {
BT_DBG("Unexpected SREJ - not requested");
return L2CAP_TXSEQ_UNEXPECTED_SREJ;
}
}

if (chan->expected_tx_seq == txseq) {
if (__seq_offset(chan, txseq, chan->last_acked_seq) >=
chan->tx_win) {
BT_DBG("Invalid - txseq outside tx window");
return L2CAP_TXSEQ_INVALID;
} else {
BT_DBG("Expected");
return L2CAP_TXSEQ_EXPECTED;
}
}

if (__seq_offset(chan, txseq, chan->last_acked_seq) <
__seq_offset(chan, chan->expected_tx_seq,
chan->last_acked_seq)){
BT_DBG("Duplicate - expected_tx_seq later than txseq");
return L2CAP_TXSEQ_DUPLICATE;
}

if (__seq_offset(chan, txseq, chan->last_acked_seq) >= chan->tx_win) {
/* A source of invalid packets is a "double poll" condition,
* where delays cause us to send multiple poll packets. If
* the remote stack receives and processes both polls,
* sequence numbers can wrap around in such a way that a
* resent frame has a sequence number that looks like new data
* with a sequence gap. This would trigger an erroneous SREJ
* request.
*
* Fortunately, this is impossible with a tx window that's
* less than half of the maximum sequence number, which allows
* invalid frames to be safely ignored.
*
* With tx window sizes greater than half of the tx window
* maximum, the frame is invalid and cannot be ignored. This
* causes a disconnect.
*/

if (chan->tx_win <= ((chan->tx_win_max + 1) >> 1)) {
BT_DBG("Invalid/Ignore - txseq outside tx window");
return L2CAP_TXSEQ_INVALID_IGNORE;
} else {
BT_DBG("Invalid - txseq outside tx window");
return L2CAP_TXSEQ_INVALID;
}
} else {
BT_DBG("Unexpected - txseq indicates missing frames");
return L2CAP_TXSEQ_UNEXPECTED;
}
}

static int l2cap_rx(struct l2cap_chan *chan, struct l2cap_ctrl *control,
struct sk_buff *skb, u8 event)
{
Expand All @@ -4832,8 +4925,39 @@ static int l2cap_rx(struct l2cap_chan *chan, struct l2cap_ctrl *control,
static int l2cap_stream_rx(struct l2cap_chan *chan, struct l2cap_ctrl *control,
struct sk_buff *skb)
{
/* Placeholder */
return -ENOTSUPP;
int err = 0;

BT_DBG("chan %p, control %p, skb %p, state %d", chan, control, skb,
chan->rx_state);

if (l2cap_classify_txseq(chan, control->txseq) ==
L2CAP_TXSEQ_EXPECTED) {
l2cap_pass_to_tx(chan, control);

BT_DBG("buffer_seq %d->%d", chan->buffer_seq,
__next_seq(chan, chan->buffer_seq));

chan->buffer_seq = __next_seq(chan, chan->buffer_seq);

l2cap_reassemble_sdu(chan, skb, control);
} else {
if (chan->sdu) {
kfree_skb(chan->sdu);
chan->sdu = NULL;
}
chan->sdu_last_frag = NULL;
chan->sdu_len = 0;

if (skb) {
BT_DBG("Freeing %p", skb);
kfree_skb(skb);
}
}

chan->last_acked_seq = control->txseq;
chan->expected_tx_seq = __next_seq(chan, control->txseq);

return err;
}

static int l2cap_data_rcv(struct l2cap_chan *chan, struct sk_buff *skb)
Expand Down

0 comments on commit f4c8c15

Please sign in to comment.