diff options
Diffstat (limited to 'net')
-rw-r--r-- | net/bluetooth/l2cap.c | 132 |
1 files changed, 79 insertions, 53 deletions
diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index dc8601fc2404..cf4481f7f566 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c | |||
@@ -77,6 +77,8 @@ static void l2cap_sock_kill(struct sock *sk); | |||
77 | static struct sk_buff *l2cap_build_cmd(struct l2cap_conn *conn, | 77 | static struct sk_buff *l2cap_build_cmd(struct l2cap_conn *conn, |
78 | u8 code, u8 ident, u16 dlen, void *data); | 78 | u8 code, u8 ident, u16 dlen, void *data); |
79 | 79 | ||
80 | static int l2cap_ertm_data_rcv(struct sock *sk, struct sk_buff *skb); | ||
81 | |||
80 | /* ---- L2CAP timers ---- */ | 82 | /* ---- L2CAP timers ---- */ |
81 | static void l2cap_sock_timeout(unsigned long arg) | 83 | static void l2cap_sock_timeout(unsigned long arg) |
82 | { | 84 | { |
@@ -2447,6 +2449,8 @@ static inline void l2cap_ertm_init(struct sock *sk) | |||
2447 | __skb_queue_head_init(BUSY_QUEUE(sk)); | 2449 | __skb_queue_head_init(BUSY_QUEUE(sk)); |
2448 | 2450 | ||
2449 | INIT_WORK(&l2cap_pi(sk)->busy_work, l2cap_busy_work); | 2451 | INIT_WORK(&l2cap_pi(sk)->busy_work, l2cap_busy_work); |
2452 | |||
2453 | sk->sk_backlog_rcv = l2cap_ertm_data_rcv; | ||
2450 | } | 2454 | } |
2451 | 2455 | ||
2452 | static inline __u8 l2cap_select_mode(__u8 mode, __u16 remote_feat_mask) | 2456 | static inline __u8 l2cap_select_mode(__u8 mode, __u16 remote_feat_mask) |
@@ -4171,13 +4175,83 @@ static inline int l2cap_data_channel_sframe(struct sock *sk, u16 rx_control, str | |||
4171 | return 0; | 4175 | return 0; |
4172 | } | 4176 | } |
4173 | 4177 | ||
4178 | static int l2cap_ertm_data_rcv(struct sock *sk, struct sk_buff *skb) | ||
4179 | { | ||
4180 | struct l2cap_pinfo *pi = l2cap_pi(sk); | ||
4181 | u16 control; | ||
4182 | u8 req_seq; | ||
4183 | int len, next_tx_seq_offset, req_seq_offset; | ||
4184 | |||
4185 | control = get_unaligned_le16(skb->data); | ||
4186 | skb_pull(skb, 2); | ||
4187 | len = skb->len; | ||
4188 | |||
4189 | /* | ||
4190 | * We can just drop the corrupted I-frame here. | ||
4191 | * Receiver will miss it and start proper recovery | ||
4192 | * procedures and ask retransmission. | ||
4193 | */ | ||
4194 | if (l2cap_check_fcs(pi, skb)) | ||
4195 | goto drop; | ||
4196 | |||
4197 | if (__is_sar_start(control) && __is_iframe(control)) | ||
4198 | len -= 2; | ||
4199 | |||
4200 | if (pi->fcs == L2CAP_FCS_CRC16) | ||
4201 | len -= 2; | ||
4202 | |||
4203 | if (len > pi->mps) { | ||
4204 | l2cap_send_disconn_req(pi->conn, sk, ECONNRESET); | ||
4205 | goto drop; | ||
4206 | } | ||
4207 | |||
4208 | req_seq = __get_reqseq(control); | ||
4209 | req_seq_offset = (req_seq - pi->expected_ack_seq) % 64; | ||
4210 | if (req_seq_offset < 0) | ||
4211 | req_seq_offset += 64; | ||
4212 | |||
4213 | next_tx_seq_offset = | ||
4214 | (pi->next_tx_seq - pi->expected_ack_seq) % 64; | ||
4215 | if (next_tx_seq_offset < 0) | ||
4216 | next_tx_seq_offset += 64; | ||
4217 | |||
4218 | /* check for invalid req-seq */ | ||
4219 | if (req_seq_offset > next_tx_seq_offset) { | ||
4220 | l2cap_send_disconn_req(pi->conn, sk, ECONNRESET); | ||
4221 | goto drop; | ||
4222 | } | ||
4223 | |||
4224 | if (__is_iframe(control)) { | ||
4225 | if (len < 0) { | ||
4226 | l2cap_send_disconn_req(pi->conn, sk, ECONNRESET); | ||
4227 | goto drop; | ||
4228 | } | ||
4229 | |||
4230 | l2cap_data_channel_iframe(sk, control, skb); | ||
4231 | } else { | ||
4232 | if (len != 0) { | ||
4233 | BT_ERR("%d", len); | ||
4234 | l2cap_send_disconn_req(pi->conn, sk, ECONNRESET); | ||
4235 | goto drop; | ||
4236 | } | ||
4237 | |||
4238 | l2cap_data_channel_sframe(sk, control, skb); | ||
4239 | } | ||
4240 | |||
4241 | return 0; | ||
4242 | |||
4243 | drop: | ||
4244 | kfree_skb(skb); | ||
4245 | return 0; | ||
4246 | } | ||
4247 | |||
4174 | static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk_buff *skb) | 4248 | static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk_buff *skb) |
4175 | { | 4249 | { |
4176 | struct sock *sk; | 4250 | struct sock *sk; |
4177 | struct l2cap_pinfo *pi; | 4251 | struct l2cap_pinfo *pi; |
4178 | u16 control; | 4252 | u16 control; |
4179 | u8 tx_seq, req_seq; | 4253 | u8 tx_seq; |
4180 | int len, next_tx_seq_offset, req_seq_offset; | 4254 | int len; |
4181 | 4255 | ||
4182 | sk = l2cap_get_chan_by_scid(&conn->chan_list, cid); | 4256 | sk = l2cap_get_chan_by_scid(&conn->chan_list, cid); |
4183 | if (!sk) { | 4257 | if (!sk) { |
@@ -4207,59 +4281,11 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk | |||
4207 | break; | 4281 | break; |
4208 | 4282 | ||
4209 | case L2CAP_MODE_ERTM: | 4283 | case L2CAP_MODE_ERTM: |
4210 | control = get_unaligned_le16(skb->data); | 4284 | if (!sock_owned_by_user(sk)) { |
4211 | skb_pull(skb, 2); | 4285 | l2cap_ertm_data_rcv(sk, skb); |
4212 | len = skb->len; | ||
4213 | |||
4214 | /* | ||
4215 | * We can just drop the corrupted I-frame here. | ||
4216 | * Receiver will miss it and start proper recovery | ||
4217 | * procedures and ask retransmission. | ||
4218 | */ | ||
4219 | if (l2cap_check_fcs(pi, skb)) | ||
4220 | goto drop; | ||
4221 | |||
4222 | if (__is_sar_start(control) && __is_iframe(control)) | ||
4223 | len -= 2; | ||
4224 | |||
4225 | if (pi->fcs == L2CAP_FCS_CRC16) | ||
4226 | len -= 2; | ||
4227 | |||
4228 | if (len > pi->mps) { | ||
4229 | l2cap_send_disconn_req(pi->conn, sk, ECONNRESET); | ||
4230 | goto drop; | ||
4231 | } | ||
4232 | |||
4233 | req_seq = __get_reqseq(control); | ||
4234 | req_seq_offset = (req_seq - pi->expected_ack_seq) % 64; | ||
4235 | if (req_seq_offset < 0) | ||
4236 | req_seq_offset += 64; | ||
4237 | |||
4238 | next_tx_seq_offset = | ||
4239 | (pi->next_tx_seq - pi->expected_ack_seq) % 64; | ||
4240 | if (next_tx_seq_offset < 0) | ||
4241 | next_tx_seq_offset += 64; | ||
4242 | |||
4243 | /* check for invalid req-seq */ | ||
4244 | if (req_seq_offset > next_tx_seq_offset) { | ||
4245 | l2cap_send_disconn_req(pi->conn, sk, ECONNRESET); | ||
4246 | goto drop; | ||
4247 | } | ||
4248 | |||
4249 | if (__is_iframe(control)) { | ||
4250 | if (len < 0) { | ||
4251 | l2cap_send_disconn_req(pi->conn, sk, ECONNRESET); | ||
4252 | goto drop; | ||
4253 | } | ||
4254 | |||
4255 | l2cap_data_channel_iframe(sk, control, skb); | ||
4256 | } else { | 4286 | } else { |
4257 | if (len != 0) { | 4287 | if (sk_add_backlog(sk, skb)) |
4258 | l2cap_send_disconn_req(pi->conn, sk, ECONNRESET); | ||
4259 | goto drop; | 4288 | goto drop; |
4260 | } | ||
4261 | |||
4262 | l2cap_data_channel_sframe(sk, control, skb); | ||
4263 | } | 4289 | } |
4264 | 4290 | ||
4265 | goto done; | 4291 | goto done; |