On Mon, Jul 27, 2009 at 6:59 AM, Marcel Holtmann<marcel@xxxxxxxxxxxx> wrote: > Hi Gustavo, > >> +static struct sk_buff *l2cap_create_pdu(struct sock *sk, struct msghdr *msg, size_t len, u16 *control) > > Why does control have to be a pointer here. You are not modifying its > content anyway. Because control could be 0 too. So I pass the control = NULL when we don't want control field. > >> +{ >> + struct l2cap_conn *conn = l2cap_pi(sk)->conn; >> + struct sk_buff *skb; >> + int err, count, hlen = L2CAP_HDR_SIZE; >> + struct l2cap_hdr *lh; >> + >> + BT_DBG("sk %p len %d", sk, (int)len); >> + >> + if (control) >> + hlen += 2; >> + else if (sk->sk_type == SOCK_DGRAM) >> + hlen += 2; > > An if (control || sk->sk_type == ...) would do the same here. I know > wanna make it the same as below, but it is confusing. > >> + >> + count = min_t(unsigned int, (conn->mtu - hlen), len); >> + skb = bt_skb_send_alloc(sk, count + hlen, >> + msg->msg_flags & MSG_DONTWAIT, &err); >> + if (!skb) >> + return ERR_PTR(-ENOMEM); >> + >> + /* Create L2CAP header */ >> + lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE); >> + lh->cid = cpu_to_le16(l2cap_pi(sk)->dcid); >> + lh->len = cpu_to_le16(len + (hlen - L2CAP_HDR_SIZE)); >> + >> + if (control) >> + put_unaligned_le16(*control, skb_put(skb, 2)); >> + else if (sk->sk_type == SOCK_DGRAM) >> + put_unaligned_le16(l2cap_pi(sk)->psm, skb_put(skb, 2)); >> + >> + err = l2cap_skbuff_fromiovec(sk, msg, len, count, skb); >> + if (unlikely(err < 0)) { >> + kfree_skb(skb); >> + return ERR_PTR(err); >> + } >> + >> + return skb; >> } >> >> static int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t len) >> { >> struct sock *sk = sock->sk; >> - int err = 0; >> + struct l2cap_pinfo *pi = l2cap_pi(sk); >> + struct sk_buff *skb; >> + u16 control; >> + int err; >> >> BT_DBG("sock %p, sk %p", sock, sk); >> >> @@ -1238,16 +1339,61 @@ static int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock, struct ms >> return -EOPNOTSUPP; >> >> /* Check outgoing MTU */ >> - if (sk->sk_type != SOCK_RAW && len > l2cap_pi(sk)->omtu) >> + if ((sk->sk_type != SOCK_RAW || pi->mode == L2CAP_MODE_BASIC) >> + && len > pi->omtu) >> return -EINVAL; > > I am confused now. Wouldn't it be better to check for == SOCK_SEQPACKET > and that it is basic mode and that the len in smaller than the MTU. So do we need to add a new check? One for SOCK_RAW (the old) and for the other you told? > >> lock_sock(sk); >> >> - if (sk->sk_state == BT_CONNECTED) >> - err = l2cap_do_send(sk, msg, len); >> - else >> + if (sk->sk_state != BT_CONNECTED) { >> err = -ENOTCONN; >> + goto done; >> + } >> + >> + switch (pi->mode) { >> + case L2CAP_MODE_BASIC: >> + /* Create a basic PDU */ >> + skb = l2cap_create_pdu(sk, msg, len, NULL); >> + if (IS_ERR(skb)) { >> + err = PTR_ERR(skb); >> + goto done; >> + } >> + >> + err = l2cap_do_send(sk, skb); >> + if (!err) >> + err = len; >> + break; >> + >> + case L2CAP_MODE_ERTM: >> + /* Entire SDU fits into one PDU */ >> + if (len <= pi->omtu) { >> + control = L2CAP_SDU_UNSEGMENTED; >> + skb = l2cap_create_pdu(sk, msg, len, &control); >> + if (IS_ERR(skb)) { >> + err = PTR_ERR(skb); >> + goto done; >> + } >> + } >> + else { >> + /* FIXME: Segmentation will be added later */ >> + err = -EINVAL; >> + goto done; >> + } >> + __skb_queue_tail(TX_QUEUE(sk), skb); >> + if (sk->sk_send_head == NULL) >> + sk->sk_send_head = skb; >> >> + err = l2cap_ertm_send(sk); >> + if (!err) >> + err = len; >> + break; >> + >> + default: >> + BT_DBG("bad state %1.1x", pi->mode); >> + err = -EINVAL; >> + } >> + >> +done: >> release_sock(sk); >> return err; >> } >> @@ -2302,6 +2448,10 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr >> >> if (l2cap_pi(sk)->conf_state & L2CAP_CONF_INPUT_DONE) { >> sk->sk_state = BT_CONNECTED; >> + l2cap_pi(sk)->next_tx_seq = 0; >> + l2cap_pi(sk)->expected_ack_seq = 0; >> + l2cap_pi(sk)->unacked_frames = 0; >> + __skb_queue_head_init(TX_QUEUE(sk)); >> l2cap_chan_ready(sk); >> goto unlock; >> } >> @@ -2376,6 +2526,9 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr >> >> if (l2cap_pi(sk)->conf_state & L2CAP_CONF_OUTPUT_DONE) { >> sk->sk_state = BT_CONNECTED; >> + l2cap_pi(sk)->expected_tx_seq = 0; >> + l2cap_pi(sk)->num_to_ack = 0; >> + __skb_queue_head_init(TX_QUEUE(sk)); >> l2cap_chan_ready(sk); >> } >> >> @@ -2406,6 +2559,8 @@ static inline int l2cap_disconnect_req(struct l2cap_conn *conn, struct l2cap_cmd >> >> sk->sk_shutdown = SHUTDOWN_MASK; >> >> + skb_queue_purge(TX_QUEUE(sk)); >> + >> l2cap_chan_del(sk, ECONNRESET); >> bh_unlock_sock(sk); >> >> @@ -2428,6 +2583,8 @@ static inline int l2cap_disconnect_rsp(struct l2cap_conn *conn, struct l2cap_cmd >> if (!sk) >> return 0; >> >> + skb_queue_purge(TX_QUEUE(sk)); >> + >> l2cap_chan_del(sk, 0); >> bh_unlock_sock(sk); >> >> @@ -2603,9 +2760,63 @@ static inline void l2cap_sig_channel(struct l2cap_conn *conn, struct sk_buff *sk >> kfree_skb(skb); >> } >> >> +static inline int l2cap_data_channel_iframe(struct sock *sk, u16 rx_control, struct sk_buff *skb) >> +{ >> + struct l2cap_pinfo *pi = l2cap_pi(sk); >> + u8 tx_seq = __get_txseq(rx_control); >> + u16 tx_control = 0; >> + int err; >> + >> + BT_DBG("sk %p rx_control 0x%4.4x len %d", sk, rx_control, skb->len); >> + >> + if (tx_seq != pi->expected_tx_seq) >> + return -EINVAL; >> + >> + L2CAP_SEQ_NUM_INC(pi->expected_tx_seq); >> + err = sock_queue_rcv_skb(sk, skb); >> + if (err) >> + return err; >> + >> + L2CAP_NUM_TO_ACK_INC(pi->num_to_ack); >> + if (pi->num_to_ack == L2CAP_DEFAULT_NUM_TO_ACK - 1) { >> + tx_control |= L2CAP_CTRL_FRAME_TYPE; >> + tx_control |= L2CAP_SUPER_RCV_READY; >> + tx_control |= pi->expected_tx_seq << L2CAP_CTRL_REQSEQ_SHIFT; >> + >> + err = l2cap_send_sframe(pi, tx_control); >> + if (unlikely(err)) >> + return err; > > Drop this unlikely, too. Just return err. Actually at this point in the > flow there is no need to check it at all. > >> + } >> + return 0; >> +} >> + >> +static inline int l2cap_data_channel_sframe(struct sock *sk, u16 rx_control, struct sk_buff *skb) >> +{ >> + struct l2cap_pinfo *pi = l2cap_pi(sk); >> + >> + BT_DBG("sk %p rx_control 0x%4.4x len %d", sk, rx_control, skb->len); >> + >> + switch (rx_control & L2CAP_CTRL_SUPERVISE) { >> + case L2CAP_SUPER_RCV_READY: >> + pi->expected_ack_seq = __get_reqseq(rx_control); >> + l2cap_drop_acked_frames(sk); >> + l2cap_ertm_send(sk); >> + break; >> + >> + case L2CAP_SUPER_RCV_NOT_READY: >> + case L2CAP_SUPER_REJECT: >> + case L2CAP_SUPER_SELECT_REJECT: >> + break; >> + } >> + >> + return 0; >> +} >> + >> static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk_buff *skb) >> { >> struct sock *sk; >> + u16 control; >> + int err; >> >> sk = l2cap_get_chan_by_scid(&conn->chan_list, cid); >> if (!sk) { >> @@ -2618,16 +2829,40 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk >> if (sk->sk_state != BT_CONNECTED) >> goto drop; >> >> - if (l2cap_pi(sk)->imtu < skb->len) >> - goto drop; >> + switch (l2cap_pi(sk)->mode) { >> + case L2CAP_MODE_BASIC: >> + /* If socket recv buffers overflows we drop data here >> + * which is *bad* because L2CAP has to be reliable. >> + * But we don't have any other choice. L2CAP doesn't >> + * provide flow control mechanism. */ >> >> - /* If socket recv buffers overflows we drop data here >> - * which is *bad* because L2CAP has to be reliable. >> - * But we don't have any other choice. L2CAP doesn't >> - * provide flow control mechanism. */ >> + if (l2cap_pi(sk)->imtu < skb->len) >> + goto drop; >> >> - if (!sock_queue_rcv_skb(sk, skb)) >> - goto done; >> + if (!sock_queue_rcv_skb(sk, skb)) >> + goto done; >> + break; >> + >> + case L2CAP_MODE_ERTM: >> + control = get_unaligned_le16(skb->data); >> + skb_pull(skb, 2); >> + >> + if (l2cap_pi(sk)->imtu < skb->len) >> + goto drop; >> + >> + if (__is_iframe(control)) >> + err = l2cap_data_channel_iframe(sk, control, skb); >> + else >> + err = l2cap_data_channel_sframe(sk, control, skb); >> + >> + if (!err) >> + goto done; >> + break; >> + >> + default: >> + BT_DBG("sk %p: bad mode 0x%2.2x", sk, l2cap_pi(sk)->mode); >> + break; >> + } >> >> drop: >> kfree_skb(skb); >> @@ -2677,6 +2912,9 @@ static void l2cap_recv_frame(struct l2cap_conn *conn, struct sk_buff *skb) >> cid = __le16_to_cpu(lh->cid); >> len = __le16_to_cpu(lh->len); >> >> + if (len != skb->len) >> + goto drop; >> + >> BT_DBG("len %d, cid 0x%4.4x", len, cid); >> >> switch (cid) { >> @@ -2694,6 +2932,10 @@ static void l2cap_recv_frame(struct l2cap_conn *conn, struct sk_buff *skb) >> l2cap_data_channel(conn, cid, skb); >> break; >> } >> + return; >> + >> +drop: >> + kfree_skb(skb); > > So this change is pointless. Just free the skb and return from the > actual length check. There is no benefit to go to the end of the > function here. > > Regards > > Marcel > > > -- Gustavo F. Padovan http://padovan.org -- To unsubscribe from this list: send the line "unsubscribe linux-bluetooth" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html