|
@@ -42,7 +42,7 @@
|
|
|
* TCP_ESTABLISHED connected pipe in enabled state
|
|
|
*
|
|
|
* pep_sock locking:
|
|
|
- * - sk_state, ackq, hlist: sock lock needed
|
|
|
+ * - sk_state, hlist: sock lock needed
|
|
|
* - listener: read only
|
|
|
* - pipe_handle: read only
|
|
|
*/
|
|
@@ -202,11 +202,12 @@ static int pep_accept_conn(struct sock *sk, struct sk_buff *skb)
|
|
|
GFP_KERNEL);
|
|
|
}
|
|
|
|
|
|
-static int pep_reject_conn(struct sock *sk, struct sk_buff *skb, u8 code)
|
|
|
+static int pep_reject_conn(struct sock *sk, struct sk_buff *skb, u8 code,
|
|
|
+ gfp_t priority)
|
|
|
{
|
|
|
static const u8 data[4] = { PAD, PAD, PAD, 0 /* sub-blocks */ };
|
|
|
WARN_ON(code == PN_PIPE_NO_ERROR);
|
|
|
- return pep_reply(sk, skb, code, data, sizeof(data), GFP_ATOMIC);
|
|
|
+ return pep_reply(sk, skb, code, data, sizeof(data), priority);
|
|
|
}
|
|
|
|
|
|
/* Control requests are not sent by the pipe service and have a specific
|
|
@@ -365,7 +366,7 @@ static int pipe_do_rcv(struct sock *sk, struct sk_buff *skb)
|
|
|
|
|
|
switch (hdr->message_id) {
|
|
|
case PNS_PEP_CONNECT_REQ:
|
|
|
- pep_reject_conn(sk, skb, PN_PIPE_ERR_PEP_IN_USE);
|
|
|
+ pep_reject_conn(sk, skb, PN_PIPE_ERR_PEP_IN_USE, GFP_ATOMIC);
|
|
|
break;
|
|
|
|
|
|
case PNS_PEP_DISCONNECT_REQ:
|
|
@@ -574,7 +575,6 @@ static int pep_connresp_rcv(struct sock *sk, struct sk_buff *skb)
|
|
|
|
|
|
sk->sk_state = TCP_SYN_RECV;
|
|
|
sk->sk_backlog_rcv = pipe_do_rcv;
|
|
|
- sk->sk_destruct = pipe_destruct;
|
|
|
pn->rx_credits = 0;
|
|
|
sk->sk_state_change(sk);
|
|
|
|
|
@@ -582,96 +582,6 @@ static int pep_connresp_rcv(struct sock *sk, struct sk_buff *skb)
|
|
|
}
|
|
|
#endif
|
|
|
|
|
|
-static int pep_connreq_rcv(struct sock *sk, struct sk_buff *skb)
|
|
|
-{
|
|
|
- struct sock *newsk;
|
|
|
- struct pep_sock *newpn, *pn = pep_sk(sk);
|
|
|
- struct pnpipehdr *hdr;
|
|
|
- struct sockaddr_pn dst, src;
|
|
|
- u16 peer_type;
|
|
|
- u8 pipe_handle, enabled, n_sb;
|
|
|
- u8 aligned = 0;
|
|
|
-
|
|
|
- if (!pskb_pull(skb, sizeof(*hdr) + 4))
|
|
|
- return -EINVAL;
|
|
|
-
|
|
|
- hdr = pnp_hdr(skb);
|
|
|
- pipe_handle = hdr->pipe_handle;
|
|
|
- switch (hdr->state_after_connect) {
|
|
|
- case PN_PIPE_DISABLE:
|
|
|
- enabled = 0;
|
|
|
- break;
|
|
|
- case PN_PIPE_ENABLE:
|
|
|
- enabled = 1;
|
|
|
- break;
|
|
|
- default:
|
|
|
- pep_reject_conn(sk, skb, PN_PIPE_ERR_INVALID_PARAM);
|
|
|
- return -EINVAL;
|
|
|
- }
|
|
|
- peer_type = hdr->other_pep_type << 8;
|
|
|
-
|
|
|
- /* Parse sub-blocks (options) */
|
|
|
- n_sb = hdr->data[4];
|
|
|
- while (n_sb > 0) {
|
|
|
- u8 type, buf[1], len = sizeof(buf);
|
|
|
- const u8 *data = pep_get_sb(skb, &type, &len, buf);
|
|
|
-
|
|
|
- if (data == NULL)
|
|
|
- return -EINVAL;
|
|
|
- switch (type) {
|
|
|
- case PN_PIPE_SB_CONNECT_REQ_PEP_SUB_TYPE:
|
|
|
- if (len < 1)
|
|
|
- return -EINVAL;
|
|
|
- peer_type = (peer_type & 0xff00) | data[0];
|
|
|
- break;
|
|
|
- case PN_PIPE_SB_ALIGNED_DATA:
|
|
|
- aligned = data[0] != 0;
|
|
|
- break;
|
|
|
- }
|
|
|
- n_sb--;
|
|
|
- }
|
|
|
-
|
|
|
- skb = skb_clone(skb, GFP_ATOMIC);
|
|
|
- if (!skb)
|
|
|
- return -ENOMEM;
|
|
|
-
|
|
|
- /* Create a new to-be-accepted sock */
|
|
|
- newsk = sk_alloc(sock_net(sk), PF_PHONET, GFP_ATOMIC, sk->sk_prot);
|
|
|
- if (!newsk) {
|
|
|
- kfree_skb(skb);
|
|
|
- return -ENOMEM;
|
|
|
- }
|
|
|
- sock_init_data(NULL, newsk);
|
|
|
- newsk->sk_state = TCP_SYN_RECV;
|
|
|
- newsk->sk_backlog_rcv = pipe_do_rcv;
|
|
|
- newsk->sk_protocol = sk->sk_protocol;
|
|
|
- newsk->sk_destruct = pipe_destruct;
|
|
|
-
|
|
|
- newpn = pep_sk(newsk);
|
|
|
- pn_skb_get_dst_sockaddr(skb, &dst);
|
|
|
- pn_skb_get_src_sockaddr(skb, &src);
|
|
|
- newpn->pn_sk.sobject = pn_sockaddr_get_object(&dst);
|
|
|
- newpn->pn_sk.dobject = pn_sockaddr_get_object(&src);
|
|
|
- newpn->pn_sk.resource = pn_sockaddr_get_resource(&dst);
|
|
|
- skb_queue_head_init(&newpn->ctrlreq_queue);
|
|
|
- newpn->pipe_handle = pipe_handle;
|
|
|
- atomic_set(&newpn->tx_credits, 0);
|
|
|
- newpn->peer_type = peer_type;
|
|
|
- newpn->rx_credits = 0;
|
|
|
- newpn->rx_fc = newpn->tx_fc = PN_LEGACY_FLOW_CONTROL;
|
|
|
- newpn->init_enable = enabled;
|
|
|
- newpn->aligned = aligned;
|
|
|
-
|
|
|
- BUG_ON(!skb_queue_empty(&newsk->sk_receive_queue));
|
|
|
- skb_queue_head(&newsk->sk_receive_queue, skb);
|
|
|
- if (!sock_flag(sk, SOCK_DEAD))
|
|
|
- sk->sk_data_ready(sk, 0);
|
|
|
-
|
|
|
- sk_acceptq_added(sk);
|
|
|
- sk_add_node(newsk, &pn->ackq);
|
|
|
- return 0;
|
|
|
-}
|
|
|
-
|
|
|
/* Listening sock must be locked */
|
|
|
static struct sock *pep_find_pipe(const struct hlist_head *hlist,
|
|
|
const struct sockaddr_pn *dst,
|
|
@@ -726,22 +636,18 @@ static int pep_do_rcv(struct sock *sk, struct sk_buff *skb)
|
|
|
if (sknode)
|
|
|
return sk_receive_skb(sknode, skb, 1);
|
|
|
|
|
|
- /* Look for a pipe handle pending accept */
|
|
|
- sknode = pep_find_pipe(&pn->ackq, &dst, pipe_handle);
|
|
|
- if (sknode) {
|
|
|
- sock_put(sknode);
|
|
|
- if (net_ratelimit())
|
|
|
- printk(KERN_WARNING"Phonet unconnected PEP ignored");
|
|
|
- goto drop;
|
|
|
- }
|
|
|
-
|
|
|
switch (hdr->message_id) {
|
|
|
case PNS_PEP_CONNECT_REQ:
|
|
|
- if (sk->sk_state == TCP_LISTEN && !sk_acceptq_is_full(sk))
|
|
|
- pep_connreq_rcv(sk, skb);
|
|
|
- else
|
|
|
- pep_reject_conn(sk, skb, PN_PIPE_ERR_PEP_IN_USE);
|
|
|
- break;
|
|
|
+ if (sk->sk_state != TCP_LISTEN || sk_acceptq_is_full(sk)) {
|
|
|
+ pep_reject_conn(sk, skb, PN_PIPE_ERR_PEP_IN_USE,
|
|
|
+ GFP_ATOMIC);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ skb_queue_head(&sk->sk_receive_queue, skb);
|
|
|
+ sk_acceptq_added(sk);
|
|
|
+ if (!sock_flag(sk, SOCK_DEAD))
|
|
|
+ sk->sk_data_ready(sk, 0);
|
|
|
+ return NET_RX_SUCCESS;
|
|
|
|
|
|
#ifdef CONFIG_PHONET_PIPECTRLR
|
|
|
case PNS_PEP_CONNECT_RESP:
|
|
@@ -799,24 +705,16 @@ static void pep_sock_close(struct sock *sk, long timeout)
|
|
|
sk_common_release(sk);
|
|
|
|
|
|
lock_sock(sk);
|
|
|
- if (sk->sk_state == TCP_LISTEN) {
|
|
|
- /* Destroy the listen queue */
|
|
|
- struct sock *sknode;
|
|
|
- struct hlist_node *p, *n;
|
|
|
-
|
|
|
- sk_for_each_safe(sknode, p, n, &pn->ackq)
|
|
|
- sk_del_node_init(sknode);
|
|
|
- sk->sk_state = TCP_CLOSE;
|
|
|
- } else if ((1 << sk->sk_state) & (TCPF_SYN_RECV|TCPF_ESTABLISHED)) {
|
|
|
+ if ((1 << sk->sk_state) & (TCPF_SYN_RECV|TCPF_ESTABLISHED)) {
|
|
|
#ifndef CONFIG_PHONET_PIPECTRLR
|
|
|
/* Forcefully remove dangling Phonet pipe */
|
|
|
pipe_do_remove(sk);
|
|
|
#else
|
|
|
/* send pep disconnect request */
|
|
|
pipe_handler_request(sk, PNS_PEP_DISCONNECT_REQ, PAD, NULL, 0);
|
|
|
- sk->sk_state = TCP_CLOSE;
|
|
|
#endif
|
|
|
}
|
|
|
+ sk->sk_state = TCP_CLOSE;
|
|
|
|
|
|
ifindex = pn->ifindex;
|
|
|
pn->ifindex = 0;
|
|
@@ -827,69 +725,121 @@ static void pep_sock_close(struct sock *sk, long timeout)
|
|
|
sock_put(sk);
|
|
|
}
|
|
|
|
|
|
-static int pep_wait_connreq(struct sock *sk, int noblock)
|
|
|
+static struct sock *pep_sock_accept(struct sock *sk, int flags, int *errp)
|
|
|
{
|
|
|
- struct task_struct *tsk = current;
|
|
|
- struct pep_sock *pn = pep_sk(sk);
|
|
|
- long timeo = sock_rcvtimeo(sk, noblock);
|
|
|
-
|
|
|
- for (;;) {
|
|
|
- DEFINE_WAIT(wait);
|
|
|
+ struct pep_sock *pn = pep_sk(sk), *newpn;
|
|
|
+ struct sock *newsk = NULL;
|
|
|
+ struct sk_buff *skb;
|
|
|
+ struct pnpipehdr *hdr;
|
|
|
+ struct sockaddr_pn dst, src;
|
|
|
+ int err;
|
|
|
+ u16 peer_type;
|
|
|
+ u8 pipe_handle, enabled, n_sb;
|
|
|
+ u8 aligned = 0;
|
|
|
|
|
|
- if (sk->sk_state != TCP_LISTEN)
|
|
|
- return -EINVAL;
|
|
|
- if (!hlist_empty(&pn->ackq))
|
|
|
- break;
|
|
|
- if (!timeo)
|
|
|
- return -EWOULDBLOCK;
|
|
|
- if (signal_pending(tsk))
|
|
|
- return sock_intr_errno(timeo);
|
|
|
+ skb = skb_recv_datagram(sk, 0, flags & O_NONBLOCK, errp);
|
|
|
+ if (!skb)
|
|
|
+ return NULL;
|
|
|
|
|
|
- prepare_to_wait_exclusive(sk_sleep(sk), &wait,
|
|
|
- TASK_INTERRUPTIBLE);
|
|
|
- release_sock(sk);
|
|
|
- timeo = schedule_timeout(timeo);
|
|
|
- lock_sock(sk);
|
|
|
- finish_wait(sk_sleep(sk), &wait);
|
|
|
+ lock_sock(sk);
|
|
|
+ if (sk->sk_state != TCP_LISTEN) {
|
|
|
+ err = -EINVAL;
|
|
|
+ goto drop;
|
|
|
}
|
|
|
+ sk_acceptq_removed(sk);
|
|
|
|
|
|
- return 0;
|
|
|
-}
|
|
|
+ err = -EPROTO;
|
|
|
+ if (!pskb_may_pull(skb, sizeof(*hdr) + 4))
|
|
|
+ goto drop;
|
|
|
|
|
|
-static struct sock *pep_sock_accept(struct sock *sk, int flags, int *errp)
|
|
|
-{
|
|
|
- struct pep_sock *pn = pep_sk(sk);
|
|
|
- struct sock *newsk = NULL;
|
|
|
- struct sk_buff *oskb;
|
|
|
- int err;
|
|
|
+ hdr = pnp_hdr(skb);
|
|
|
+ pipe_handle = hdr->pipe_handle;
|
|
|
+ switch (hdr->state_after_connect) {
|
|
|
+ case PN_PIPE_DISABLE:
|
|
|
+ enabled = 0;
|
|
|
+ break;
|
|
|
+ case PN_PIPE_ENABLE:
|
|
|
+ enabled = 1;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ pep_reject_conn(sk, skb, PN_PIPE_ERR_INVALID_PARAM,
|
|
|
+ GFP_KERNEL);
|
|
|
+ goto drop;
|
|
|
+ }
|
|
|
+ peer_type = hdr->other_pep_type << 8;
|
|
|
|
|
|
- lock_sock(sk);
|
|
|
- err = pep_wait_connreq(sk, flags & O_NONBLOCK);
|
|
|
- if (err)
|
|
|
- goto out;
|
|
|
+ /* Parse sub-blocks (options) */
|
|
|
+ n_sb = hdr->data[4];
|
|
|
+ while (n_sb > 0) {
|
|
|
+ u8 type, buf[1], len = sizeof(buf);
|
|
|
+ const u8 *data = pep_get_sb(skb, &type, &len, buf);
|
|
|
|
|
|
- newsk = __sk_head(&pn->ackq);
|
|
|
+ if (data == NULL)
|
|
|
+ goto drop;
|
|
|
+ switch (type) {
|
|
|
+ case PN_PIPE_SB_CONNECT_REQ_PEP_SUB_TYPE:
|
|
|
+ if (len < 1)
|
|
|
+ goto drop;
|
|
|
+ peer_type = (peer_type & 0xff00) | data[0];
|
|
|
+ break;
|
|
|
+ case PN_PIPE_SB_ALIGNED_DATA:
|
|
|
+ aligned = data[0] != 0;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ n_sb--;
|
|
|
+ }
|
|
|
|
|
|
- oskb = skb_dequeue(&newsk->sk_receive_queue);
|
|
|
- err = pep_accept_conn(newsk, oskb);
|
|
|
- if (err) {
|
|
|
- skb_queue_head(&newsk->sk_receive_queue, oskb);
|
|
|
+ /* Check for duplicate pipe handle */
|
|
|
+ newsk = pep_find_pipe(&pn->hlist, &dst, pipe_handle);
|
|
|
+ if (unlikely(newsk)) {
|
|
|
+ __sock_put(newsk);
|
|
|
newsk = NULL;
|
|
|
- goto out;
|
|
|
+ pep_reject_conn(sk, skb, PN_PIPE_ERR_PEP_IN_USE, GFP_KERNEL);
|
|
|
+ goto drop;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Create a new to-be-accepted sock */
|
|
|
+ newsk = sk_alloc(sock_net(sk), PF_PHONET, GFP_KERNEL, sk->sk_prot);
|
|
|
+ if (!newsk) {
|
|
|
+ pep_reject_conn(sk, skb, PN_PIPE_ERR_OVERLOAD, GFP_KERNEL);
|
|
|
+ err = -ENOBUFS;
|
|
|
+ goto drop;
|
|
|
}
|
|
|
- kfree_skb(oskb);
|
|
|
|
|
|
+ sock_init_data(NULL, newsk);
|
|
|
+ newsk->sk_state = TCP_SYN_RECV;
|
|
|
+ newsk->sk_backlog_rcv = pipe_do_rcv;
|
|
|
+ newsk->sk_protocol = sk->sk_protocol;
|
|
|
+ newsk->sk_destruct = pipe_destruct;
|
|
|
+
|
|
|
+ newpn = pep_sk(newsk);
|
|
|
+ pn_skb_get_dst_sockaddr(skb, &dst);
|
|
|
+ pn_skb_get_src_sockaddr(skb, &src);
|
|
|
+ newpn->pn_sk.sobject = pn_sockaddr_get_object(&dst);
|
|
|
+ newpn->pn_sk.dobject = pn_sockaddr_get_object(&src);
|
|
|
+ newpn->pn_sk.resource = pn_sockaddr_get_resource(&dst);
|
|
|
sock_hold(sk);
|
|
|
- pep_sk(newsk)->listener = sk;
|
|
|
+ newpn->listener = sk;
|
|
|
+ skb_queue_head_init(&newpn->ctrlreq_queue);
|
|
|
+ newpn->pipe_handle = pipe_handle;
|
|
|
+ atomic_set(&newpn->tx_credits, 0);
|
|
|
+ newpn->ifindex = 0;
|
|
|
+ newpn->peer_type = peer_type;
|
|
|
+ newpn->rx_credits = 0;
|
|
|
+ newpn->rx_fc = newpn->tx_fc = PN_LEGACY_FLOW_CONTROL;
|
|
|
+ newpn->init_enable = enabled;
|
|
|
+ newpn->aligned = aligned;
|
|
|
|
|
|
- sock_hold(newsk);
|
|
|
- sk_del_node_init(newsk);
|
|
|
- sk_acceptq_removed(sk);
|
|
|
+ err = pep_accept_conn(newsk, skb);
|
|
|
+ if (err) {
|
|
|
+ sock_put(newsk);
|
|
|
+ newsk = NULL;
|
|
|
+ goto drop;
|
|
|
+ }
|
|
|
sk_add_node(newsk, &pn->hlist);
|
|
|
- __sock_put(newsk);
|
|
|
-
|
|
|
-out:
|
|
|
+drop:
|
|
|
release_sock(sk);
|
|
|
+ kfree_skb(skb);
|
|
|
*errp = err;
|
|
|
return newsk;
|
|
|
}
|
|
@@ -937,7 +887,7 @@ static int pep_init(struct sock *sk)
|
|
|
{
|
|
|
struct pep_sock *pn = pep_sk(sk);
|
|
|
|
|
|
- INIT_HLIST_HEAD(&pn->ackq);
|
|
|
+ sk->sk_destruct = pipe_destruct;
|
|
|
INIT_HLIST_HEAD(&pn->hlist);
|
|
|
skb_queue_head_init(&pn->ctrlreq_queue);
|
|
|
pn->pipe_handle = PN_PIPE_INVALID_HANDLE;
|