162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * File: pep.c
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Phonet pipe protocol end point socket
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Copyright (C) 2008 Nokia Corporation.
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * Author: Rémi Denis-Courmont
1062306a36Sopenharmony_ci */
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include <linux/kernel.h>
1362306a36Sopenharmony_ci#include <linux/sched/signal.h>
1462306a36Sopenharmony_ci#include <linux/slab.h>
1562306a36Sopenharmony_ci#include <linux/socket.h>
1662306a36Sopenharmony_ci#include <net/sock.h>
1762306a36Sopenharmony_ci#include <net/tcp_states.h>
1862306a36Sopenharmony_ci#include <asm/ioctls.h>
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#include <linux/phonet.h>
2162306a36Sopenharmony_ci#include <linux/module.h>
2262306a36Sopenharmony_ci#include <net/phonet/phonet.h>
2362306a36Sopenharmony_ci#include <net/phonet/pep.h>
2462306a36Sopenharmony_ci#include <net/phonet/gprs.h>
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci/* sk_state values:
2762306a36Sopenharmony_ci * TCP_CLOSE		sock not in use yet
2862306a36Sopenharmony_ci * TCP_CLOSE_WAIT	disconnected pipe
2962306a36Sopenharmony_ci * TCP_LISTEN		listening pipe endpoint
3062306a36Sopenharmony_ci * TCP_SYN_RECV		connected pipe in disabled state
3162306a36Sopenharmony_ci * TCP_ESTABLISHED	connected pipe in enabled state
3262306a36Sopenharmony_ci *
3362306a36Sopenharmony_ci * pep_sock locking:
3462306a36Sopenharmony_ci *  - sk_state, hlist: sock lock needed
3562306a36Sopenharmony_ci *  - listener: read only
3662306a36Sopenharmony_ci *  - pipe_handle: read only
3762306a36Sopenharmony_ci */
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci#define CREDITS_MAX	10
4062306a36Sopenharmony_ci#define CREDITS_THR	7
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci#define pep_sb_size(s) (((s) + 5) & ~3) /* 2-bytes head, 32-bits aligned */
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci/* Get the next TLV sub-block. */
4562306a36Sopenharmony_cistatic unsigned char *pep_get_sb(struct sk_buff *skb, u8 *ptype, u8 *plen,
4662306a36Sopenharmony_ci					void *buf)
4762306a36Sopenharmony_ci{
4862306a36Sopenharmony_ci	void *data = NULL;
4962306a36Sopenharmony_ci	struct {
5062306a36Sopenharmony_ci		u8 sb_type;
5162306a36Sopenharmony_ci		u8 sb_len;
5262306a36Sopenharmony_ci	} *ph, h;
5362306a36Sopenharmony_ci	int buflen = *plen;
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	ph = skb_header_pointer(skb, 0, 2, &h);
5662306a36Sopenharmony_ci	if (ph == NULL || ph->sb_len < 2 || !pskb_may_pull(skb, ph->sb_len))
5762306a36Sopenharmony_ci		return NULL;
5862306a36Sopenharmony_ci	ph->sb_len -= 2;
5962306a36Sopenharmony_ci	*ptype = ph->sb_type;
6062306a36Sopenharmony_ci	*plen = ph->sb_len;
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	if (buflen > ph->sb_len)
6362306a36Sopenharmony_ci		buflen = ph->sb_len;
6462306a36Sopenharmony_ci	data = skb_header_pointer(skb, 2, buflen, buf);
6562306a36Sopenharmony_ci	__skb_pull(skb, 2 + ph->sb_len);
6662306a36Sopenharmony_ci	return data;
6762306a36Sopenharmony_ci}
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_cistatic struct sk_buff *pep_alloc_skb(struct sock *sk, const void *payload,
7062306a36Sopenharmony_ci					int len, gfp_t priority)
7162306a36Sopenharmony_ci{
7262306a36Sopenharmony_ci	struct sk_buff *skb = alloc_skb(MAX_PNPIPE_HEADER + len, priority);
7362306a36Sopenharmony_ci	if (!skb)
7462306a36Sopenharmony_ci		return NULL;
7562306a36Sopenharmony_ci	skb_set_owner_w(skb, sk);
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	skb_reserve(skb, MAX_PNPIPE_HEADER);
7862306a36Sopenharmony_ci	__skb_put(skb, len);
7962306a36Sopenharmony_ci	skb_copy_to_linear_data(skb, payload, len);
8062306a36Sopenharmony_ci	__skb_push(skb, sizeof(struct pnpipehdr));
8162306a36Sopenharmony_ci	skb_reset_transport_header(skb);
8262306a36Sopenharmony_ci	return skb;
8362306a36Sopenharmony_ci}
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_cistatic int pep_reply(struct sock *sk, struct sk_buff *oskb, u8 code,
8662306a36Sopenharmony_ci			const void *data, int len, gfp_t priority)
8762306a36Sopenharmony_ci{
8862306a36Sopenharmony_ci	const struct pnpipehdr *oph = pnp_hdr(oskb);
8962306a36Sopenharmony_ci	struct pnpipehdr *ph;
9062306a36Sopenharmony_ci	struct sk_buff *skb;
9162306a36Sopenharmony_ci	struct sockaddr_pn peer;
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	skb = pep_alloc_skb(sk, data, len, priority);
9462306a36Sopenharmony_ci	if (!skb)
9562306a36Sopenharmony_ci		return -ENOMEM;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	ph = pnp_hdr(skb);
9862306a36Sopenharmony_ci	ph->utid = oph->utid;
9962306a36Sopenharmony_ci	ph->message_id = oph->message_id + 1; /* REQ -> RESP */
10062306a36Sopenharmony_ci	ph->pipe_handle = oph->pipe_handle;
10162306a36Sopenharmony_ci	ph->error_code = code;
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	pn_skb_get_src_sockaddr(oskb, &peer);
10462306a36Sopenharmony_ci	return pn_skb_send(sk, skb, &peer);
10562306a36Sopenharmony_ci}
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_cistatic int pep_indicate(struct sock *sk, u8 id, u8 code,
10862306a36Sopenharmony_ci			const void *data, int len, gfp_t priority)
10962306a36Sopenharmony_ci{
11062306a36Sopenharmony_ci	struct pep_sock *pn = pep_sk(sk);
11162306a36Sopenharmony_ci	struct pnpipehdr *ph;
11262306a36Sopenharmony_ci	struct sk_buff *skb;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	skb = pep_alloc_skb(sk, data, len, priority);
11562306a36Sopenharmony_ci	if (!skb)
11662306a36Sopenharmony_ci		return -ENOMEM;
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	ph = pnp_hdr(skb);
11962306a36Sopenharmony_ci	ph->utid = 0;
12062306a36Sopenharmony_ci	ph->message_id = id;
12162306a36Sopenharmony_ci	ph->pipe_handle = pn->pipe_handle;
12262306a36Sopenharmony_ci	ph->error_code = code;
12362306a36Sopenharmony_ci	return pn_skb_send(sk, skb, NULL);
12462306a36Sopenharmony_ci}
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci#define PAD 0x00
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_cistatic int pipe_handler_request(struct sock *sk, u8 id, u8 code,
12962306a36Sopenharmony_ci				const void *data, int len)
13062306a36Sopenharmony_ci{
13162306a36Sopenharmony_ci	struct pep_sock *pn = pep_sk(sk);
13262306a36Sopenharmony_ci	struct pnpipehdr *ph;
13362306a36Sopenharmony_ci	struct sk_buff *skb;
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	skb = pep_alloc_skb(sk, data, len, GFP_KERNEL);
13662306a36Sopenharmony_ci	if (!skb)
13762306a36Sopenharmony_ci		return -ENOMEM;
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	ph = pnp_hdr(skb);
14062306a36Sopenharmony_ci	ph->utid = id; /* whatever */
14162306a36Sopenharmony_ci	ph->message_id = id;
14262306a36Sopenharmony_ci	ph->pipe_handle = pn->pipe_handle;
14362306a36Sopenharmony_ci	ph->error_code = code;
14462306a36Sopenharmony_ci	return pn_skb_send(sk, skb, NULL);
14562306a36Sopenharmony_ci}
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_cistatic int pipe_handler_send_created_ind(struct sock *sk)
14862306a36Sopenharmony_ci{
14962306a36Sopenharmony_ci	struct pep_sock *pn = pep_sk(sk);
15062306a36Sopenharmony_ci	u8 data[4] = {
15162306a36Sopenharmony_ci		PN_PIPE_SB_NEGOTIATED_FC, pep_sb_size(2),
15262306a36Sopenharmony_ci		pn->tx_fc, pn->rx_fc,
15362306a36Sopenharmony_ci	};
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	return pep_indicate(sk, PNS_PIPE_CREATED_IND, 1 /* sub-blocks */,
15662306a36Sopenharmony_ci				data, 4, GFP_ATOMIC);
15762306a36Sopenharmony_ci}
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_cistatic int pep_accept_conn(struct sock *sk, struct sk_buff *skb)
16062306a36Sopenharmony_ci{
16162306a36Sopenharmony_ci	static const u8 data[20] = {
16262306a36Sopenharmony_ci		PAD, PAD, PAD, 2 /* sub-blocks */,
16362306a36Sopenharmony_ci		PN_PIPE_SB_REQUIRED_FC_TX, pep_sb_size(5), 3, PAD,
16462306a36Sopenharmony_ci			PN_MULTI_CREDIT_FLOW_CONTROL,
16562306a36Sopenharmony_ci			PN_ONE_CREDIT_FLOW_CONTROL,
16662306a36Sopenharmony_ci			PN_LEGACY_FLOW_CONTROL,
16762306a36Sopenharmony_ci			PAD,
16862306a36Sopenharmony_ci		PN_PIPE_SB_PREFERRED_FC_RX, pep_sb_size(5), 3, PAD,
16962306a36Sopenharmony_ci			PN_MULTI_CREDIT_FLOW_CONTROL,
17062306a36Sopenharmony_ci			PN_ONE_CREDIT_FLOW_CONTROL,
17162306a36Sopenharmony_ci			PN_LEGACY_FLOW_CONTROL,
17262306a36Sopenharmony_ci			PAD,
17362306a36Sopenharmony_ci	};
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	might_sleep();
17662306a36Sopenharmony_ci	return pep_reply(sk, skb, PN_PIPE_NO_ERROR, data, sizeof(data),
17762306a36Sopenharmony_ci				GFP_KERNEL);
17862306a36Sopenharmony_ci}
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_cistatic int pep_reject_conn(struct sock *sk, struct sk_buff *skb, u8 code,
18162306a36Sopenharmony_ci				gfp_t priority)
18262306a36Sopenharmony_ci{
18362306a36Sopenharmony_ci	static const u8 data[4] = { PAD, PAD, PAD, 0 /* sub-blocks */ };
18462306a36Sopenharmony_ci	WARN_ON(code == PN_PIPE_NO_ERROR);
18562306a36Sopenharmony_ci	return pep_reply(sk, skb, code, data, sizeof(data), priority);
18662306a36Sopenharmony_ci}
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci/* Control requests are not sent by the pipe service and have a specific
18962306a36Sopenharmony_ci * message format. */
19062306a36Sopenharmony_cistatic int pep_ctrlreq_error(struct sock *sk, struct sk_buff *oskb, u8 code,
19162306a36Sopenharmony_ci				gfp_t priority)
19262306a36Sopenharmony_ci{
19362306a36Sopenharmony_ci	const struct pnpipehdr *oph = pnp_hdr(oskb);
19462306a36Sopenharmony_ci	struct sk_buff *skb;
19562306a36Sopenharmony_ci	struct pnpipehdr *ph;
19662306a36Sopenharmony_ci	struct sockaddr_pn dst;
19762306a36Sopenharmony_ci	u8 data[4] = {
19862306a36Sopenharmony_ci		oph->pep_type, /* PEP type */
19962306a36Sopenharmony_ci		code, /* error code, at an unusual offset */
20062306a36Sopenharmony_ci		PAD, PAD,
20162306a36Sopenharmony_ci	};
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	skb = pep_alloc_skb(sk, data, 4, priority);
20462306a36Sopenharmony_ci	if (!skb)
20562306a36Sopenharmony_ci		return -ENOMEM;
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	ph = pnp_hdr(skb);
20862306a36Sopenharmony_ci	ph->utid = oph->utid;
20962306a36Sopenharmony_ci	ph->message_id = PNS_PEP_CTRL_RESP;
21062306a36Sopenharmony_ci	ph->pipe_handle = oph->pipe_handle;
21162306a36Sopenharmony_ci	ph->data0 = oph->data[0]; /* CTRL id */
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	pn_skb_get_src_sockaddr(oskb, &dst);
21462306a36Sopenharmony_ci	return pn_skb_send(sk, skb, &dst);
21562306a36Sopenharmony_ci}
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_cistatic int pipe_snd_status(struct sock *sk, u8 type, u8 status, gfp_t priority)
21862306a36Sopenharmony_ci{
21962306a36Sopenharmony_ci	u8 data[4] = { type, PAD, PAD, status };
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	return pep_indicate(sk, PNS_PEP_STATUS_IND, PN_PEP_TYPE_COMMON,
22262306a36Sopenharmony_ci				data, 4, priority);
22362306a36Sopenharmony_ci}
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci/* Send our RX flow control information to the sender.
22662306a36Sopenharmony_ci * Socket must be locked. */
22762306a36Sopenharmony_cistatic void pipe_grant_credits(struct sock *sk, gfp_t priority)
22862306a36Sopenharmony_ci{
22962306a36Sopenharmony_ci	struct pep_sock *pn = pep_sk(sk);
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	BUG_ON(sk->sk_state != TCP_ESTABLISHED);
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	switch (pn->rx_fc) {
23462306a36Sopenharmony_ci	case PN_LEGACY_FLOW_CONTROL: /* TODO */
23562306a36Sopenharmony_ci		break;
23662306a36Sopenharmony_ci	case PN_ONE_CREDIT_FLOW_CONTROL:
23762306a36Sopenharmony_ci		if (pipe_snd_status(sk, PN_PEP_IND_FLOW_CONTROL,
23862306a36Sopenharmony_ci					PEP_IND_READY, priority) == 0)
23962306a36Sopenharmony_ci			pn->rx_credits = 1;
24062306a36Sopenharmony_ci		break;
24162306a36Sopenharmony_ci	case PN_MULTI_CREDIT_FLOW_CONTROL:
24262306a36Sopenharmony_ci		if ((pn->rx_credits + CREDITS_THR) > CREDITS_MAX)
24362306a36Sopenharmony_ci			break;
24462306a36Sopenharmony_ci		if (pipe_snd_status(sk, PN_PEP_IND_ID_MCFC_GRANT_CREDITS,
24562306a36Sopenharmony_ci					CREDITS_MAX - pn->rx_credits,
24662306a36Sopenharmony_ci					priority) == 0)
24762306a36Sopenharmony_ci			pn->rx_credits = CREDITS_MAX;
24862306a36Sopenharmony_ci		break;
24962306a36Sopenharmony_ci	}
25062306a36Sopenharmony_ci}
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_cistatic int pipe_rcv_status(struct sock *sk, struct sk_buff *skb)
25362306a36Sopenharmony_ci{
25462306a36Sopenharmony_ci	struct pep_sock *pn = pep_sk(sk);
25562306a36Sopenharmony_ci	struct pnpipehdr *hdr;
25662306a36Sopenharmony_ci	int wake = 0;
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	if (!pskb_may_pull(skb, sizeof(*hdr) + 4))
25962306a36Sopenharmony_ci		return -EINVAL;
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	hdr = pnp_hdr(skb);
26262306a36Sopenharmony_ci	if (hdr->pep_type != PN_PEP_TYPE_COMMON) {
26362306a36Sopenharmony_ci		net_dbg_ratelimited("Phonet unknown PEP type: %u\n",
26462306a36Sopenharmony_ci				    (unsigned int)hdr->pep_type);
26562306a36Sopenharmony_ci		return -EOPNOTSUPP;
26662306a36Sopenharmony_ci	}
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	switch (hdr->data[0]) {
26962306a36Sopenharmony_ci	case PN_PEP_IND_FLOW_CONTROL:
27062306a36Sopenharmony_ci		switch (pn->tx_fc) {
27162306a36Sopenharmony_ci		case PN_LEGACY_FLOW_CONTROL:
27262306a36Sopenharmony_ci			switch (hdr->data[3]) {
27362306a36Sopenharmony_ci			case PEP_IND_BUSY:
27462306a36Sopenharmony_ci				atomic_set(&pn->tx_credits, 0);
27562306a36Sopenharmony_ci				break;
27662306a36Sopenharmony_ci			case PEP_IND_READY:
27762306a36Sopenharmony_ci				atomic_set(&pn->tx_credits, wake = 1);
27862306a36Sopenharmony_ci				break;
27962306a36Sopenharmony_ci			}
28062306a36Sopenharmony_ci			break;
28162306a36Sopenharmony_ci		case PN_ONE_CREDIT_FLOW_CONTROL:
28262306a36Sopenharmony_ci			if (hdr->data[3] == PEP_IND_READY)
28362306a36Sopenharmony_ci				atomic_set(&pn->tx_credits, wake = 1);
28462306a36Sopenharmony_ci			break;
28562306a36Sopenharmony_ci		}
28662306a36Sopenharmony_ci		break;
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	case PN_PEP_IND_ID_MCFC_GRANT_CREDITS:
28962306a36Sopenharmony_ci		if (pn->tx_fc != PN_MULTI_CREDIT_FLOW_CONTROL)
29062306a36Sopenharmony_ci			break;
29162306a36Sopenharmony_ci		atomic_add(wake = hdr->data[3], &pn->tx_credits);
29262306a36Sopenharmony_ci		break;
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	default:
29562306a36Sopenharmony_ci		net_dbg_ratelimited("Phonet unknown PEP indication: %u\n",
29662306a36Sopenharmony_ci				    (unsigned int)hdr->data[0]);
29762306a36Sopenharmony_ci		return -EOPNOTSUPP;
29862306a36Sopenharmony_ci	}
29962306a36Sopenharmony_ci	if (wake)
30062306a36Sopenharmony_ci		sk->sk_write_space(sk);
30162306a36Sopenharmony_ci	return 0;
30262306a36Sopenharmony_ci}
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_cistatic int pipe_rcv_created(struct sock *sk, struct sk_buff *skb)
30562306a36Sopenharmony_ci{
30662306a36Sopenharmony_ci	struct pep_sock *pn = pep_sk(sk);
30762306a36Sopenharmony_ci	struct pnpipehdr *hdr = pnp_hdr(skb);
30862306a36Sopenharmony_ci	u8 n_sb = hdr->data0;
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci	pn->rx_fc = pn->tx_fc = PN_LEGACY_FLOW_CONTROL;
31162306a36Sopenharmony_ci	__skb_pull(skb, sizeof(*hdr));
31262306a36Sopenharmony_ci	while (n_sb > 0) {
31362306a36Sopenharmony_ci		u8 type, buf[2], len = sizeof(buf);
31462306a36Sopenharmony_ci		u8 *data = pep_get_sb(skb, &type, &len, buf);
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci		if (data == NULL)
31762306a36Sopenharmony_ci			return -EINVAL;
31862306a36Sopenharmony_ci		switch (type) {
31962306a36Sopenharmony_ci		case PN_PIPE_SB_NEGOTIATED_FC:
32062306a36Sopenharmony_ci			if (len < 2 || (data[0] | data[1]) > 3)
32162306a36Sopenharmony_ci				break;
32262306a36Sopenharmony_ci			pn->tx_fc = data[0] & 3;
32362306a36Sopenharmony_ci			pn->rx_fc = data[1] & 3;
32462306a36Sopenharmony_ci			break;
32562306a36Sopenharmony_ci		}
32662306a36Sopenharmony_ci		n_sb--;
32762306a36Sopenharmony_ci	}
32862306a36Sopenharmony_ci	return 0;
32962306a36Sopenharmony_ci}
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci/* Queue an skb to a connected sock.
33262306a36Sopenharmony_ci * Socket lock must be held. */
33362306a36Sopenharmony_cistatic int pipe_do_rcv(struct sock *sk, struct sk_buff *skb)
33462306a36Sopenharmony_ci{
33562306a36Sopenharmony_ci	struct pep_sock *pn = pep_sk(sk);
33662306a36Sopenharmony_ci	struct pnpipehdr *hdr = pnp_hdr(skb);
33762306a36Sopenharmony_ci	struct sk_buff_head *queue;
33862306a36Sopenharmony_ci	int err = 0;
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	BUG_ON(sk->sk_state == TCP_CLOSE_WAIT);
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	switch (hdr->message_id) {
34362306a36Sopenharmony_ci	case PNS_PEP_CONNECT_REQ:
34462306a36Sopenharmony_ci		pep_reject_conn(sk, skb, PN_PIPE_ERR_PEP_IN_USE, GFP_ATOMIC);
34562306a36Sopenharmony_ci		break;
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci	case PNS_PEP_DISCONNECT_REQ:
34862306a36Sopenharmony_ci		pep_reply(sk, skb, PN_PIPE_NO_ERROR, NULL, 0, GFP_ATOMIC);
34962306a36Sopenharmony_ci		sk->sk_state = TCP_CLOSE_WAIT;
35062306a36Sopenharmony_ci		if (!sock_flag(sk, SOCK_DEAD))
35162306a36Sopenharmony_ci			sk->sk_state_change(sk);
35262306a36Sopenharmony_ci		break;
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci	case PNS_PEP_ENABLE_REQ:
35562306a36Sopenharmony_ci		/* Wait for PNS_PIPE_(ENABLED|REDIRECTED)_IND */
35662306a36Sopenharmony_ci		pep_reply(sk, skb, PN_PIPE_NO_ERROR, NULL, 0, GFP_ATOMIC);
35762306a36Sopenharmony_ci		break;
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	case PNS_PEP_RESET_REQ:
36062306a36Sopenharmony_ci		switch (hdr->state_after_reset) {
36162306a36Sopenharmony_ci		case PN_PIPE_DISABLE:
36262306a36Sopenharmony_ci			pn->init_enable = 0;
36362306a36Sopenharmony_ci			break;
36462306a36Sopenharmony_ci		case PN_PIPE_ENABLE:
36562306a36Sopenharmony_ci			pn->init_enable = 1;
36662306a36Sopenharmony_ci			break;
36762306a36Sopenharmony_ci		default: /* not allowed to send an error here!? */
36862306a36Sopenharmony_ci			err = -EINVAL;
36962306a36Sopenharmony_ci			goto out;
37062306a36Sopenharmony_ci		}
37162306a36Sopenharmony_ci		fallthrough;
37262306a36Sopenharmony_ci	case PNS_PEP_DISABLE_REQ:
37362306a36Sopenharmony_ci		atomic_set(&pn->tx_credits, 0);
37462306a36Sopenharmony_ci		pep_reply(sk, skb, PN_PIPE_NO_ERROR, NULL, 0, GFP_ATOMIC);
37562306a36Sopenharmony_ci		break;
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	case PNS_PEP_CTRL_REQ:
37862306a36Sopenharmony_ci		if (skb_queue_len(&pn->ctrlreq_queue) >= PNPIPE_CTRLREQ_MAX) {
37962306a36Sopenharmony_ci			atomic_inc(&sk->sk_drops);
38062306a36Sopenharmony_ci			break;
38162306a36Sopenharmony_ci		}
38262306a36Sopenharmony_ci		__skb_pull(skb, 4);
38362306a36Sopenharmony_ci		queue = &pn->ctrlreq_queue;
38462306a36Sopenharmony_ci		goto queue;
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	case PNS_PIPE_ALIGNED_DATA:
38762306a36Sopenharmony_ci		__skb_pull(skb, 1);
38862306a36Sopenharmony_ci		fallthrough;
38962306a36Sopenharmony_ci	case PNS_PIPE_DATA:
39062306a36Sopenharmony_ci		__skb_pull(skb, 3); /* Pipe data header */
39162306a36Sopenharmony_ci		if (!pn_flow_safe(pn->rx_fc)) {
39262306a36Sopenharmony_ci			err = sock_queue_rcv_skb(sk, skb);
39362306a36Sopenharmony_ci			if (!err)
39462306a36Sopenharmony_ci				return NET_RX_SUCCESS;
39562306a36Sopenharmony_ci			err = -ENOBUFS;
39662306a36Sopenharmony_ci			break;
39762306a36Sopenharmony_ci		}
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci		if (pn->rx_credits == 0) {
40062306a36Sopenharmony_ci			atomic_inc(&sk->sk_drops);
40162306a36Sopenharmony_ci			err = -ENOBUFS;
40262306a36Sopenharmony_ci			break;
40362306a36Sopenharmony_ci		}
40462306a36Sopenharmony_ci		pn->rx_credits--;
40562306a36Sopenharmony_ci		queue = &sk->sk_receive_queue;
40662306a36Sopenharmony_ci		goto queue;
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci	case PNS_PEP_STATUS_IND:
40962306a36Sopenharmony_ci		pipe_rcv_status(sk, skb);
41062306a36Sopenharmony_ci		break;
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci	case PNS_PIPE_REDIRECTED_IND:
41362306a36Sopenharmony_ci		err = pipe_rcv_created(sk, skb);
41462306a36Sopenharmony_ci		break;
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci	case PNS_PIPE_CREATED_IND:
41762306a36Sopenharmony_ci		err = pipe_rcv_created(sk, skb);
41862306a36Sopenharmony_ci		if (err)
41962306a36Sopenharmony_ci			break;
42062306a36Sopenharmony_ci		fallthrough;
42162306a36Sopenharmony_ci	case PNS_PIPE_RESET_IND:
42262306a36Sopenharmony_ci		if (!pn->init_enable)
42362306a36Sopenharmony_ci			break;
42462306a36Sopenharmony_ci		fallthrough;
42562306a36Sopenharmony_ci	case PNS_PIPE_ENABLED_IND:
42662306a36Sopenharmony_ci		if (!pn_flow_safe(pn->tx_fc)) {
42762306a36Sopenharmony_ci			atomic_set(&pn->tx_credits, 1);
42862306a36Sopenharmony_ci			sk->sk_write_space(sk);
42962306a36Sopenharmony_ci		}
43062306a36Sopenharmony_ci		if (sk->sk_state == TCP_ESTABLISHED)
43162306a36Sopenharmony_ci			break; /* Nothing to do */
43262306a36Sopenharmony_ci		sk->sk_state = TCP_ESTABLISHED;
43362306a36Sopenharmony_ci		pipe_grant_credits(sk, GFP_ATOMIC);
43462306a36Sopenharmony_ci		break;
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	case PNS_PIPE_DISABLED_IND:
43762306a36Sopenharmony_ci		sk->sk_state = TCP_SYN_RECV;
43862306a36Sopenharmony_ci		pn->rx_credits = 0;
43962306a36Sopenharmony_ci		break;
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci	default:
44262306a36Sopenharmony_ci		net_dbg_ratelimited("Phonet unknown PEP message: %u\n",
44362306a36Sopenharmony_ci				    hdr->message_id);
44462306a36Sopenharmony_ci		err = -EINVAL;
44562306a36Sopenharmony_ci	}
44662306a36Sopenharmony_ciout:
44762306a36Sopenharmony_ci	kfree_skb(skb);
44862306a36Sopenharmony_ci	return (err == -ENOBUFS) ? NET_RX_DROP : NET_RX_SUCCESS;
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ciqueue:
45162306a36Sopenharmony_ci	skb->dev = NULL;
45262306a36Sopenharmony_ci	skb_set_owner_r(skb, sk);
45362306a36Sopenharmony_ci	skb_queue_tail(queue, skb);
45462306a36Sopenharmony_ci	if (!sock_flag(sk, SOCK_DEAD))
45562306a36Sopenharmony_ci		sk->sk_data_ready(sk);
45662306a36Sopenharmony_ci	return NET_RX_SUCCESS;
45762306a36Sopenharmony_ci}
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci/* Destroy connected sock. */
46062306a36Sopenharmony_cistatic void pipe_destruct(struct sock *sk)
46162306a36Sopenharmony_ci{
46262306a36Sopenharmony_ci	struct pep_sock *pn = pep_sk(sk);
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci	skb_queue_purge(&sk->sk_receive_queue);
46562306a36Sopenharmony_ci	skb_queue_purge(&pn->ctrlreq_queue);
46662306a36Sopenharmony_ci}
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_cistatic u8 pipe_negotiate_fc(const u8 *fcs, unsigned int n)
46962306a36Sopenharmony_ci{
47062306a36Sopenharmony_ci	unsigned int i;
47162306a36Sopenharmony_ci	u8 final_fc = PN_NO_FLOW_CONTROL;
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci	for (i = 0; i < n; i++) {
47462306a36Sopenharmony_ci		u8 fc = fcs[i];
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci		if (fc > final_fc && fc < PN_MAX_FLOW_CONTROL)
47762306a36Sopenharmony_ci			final_fc = fc;
47862306a36Sopenharmony_ci	}
47962306a36Sopenharmony_ci	return final_fc;
48062306a36Sopenharmony_ci}
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_cistatic int pep_connresp_rcv(struct sock *sk, struct sk_buff *skb)
48362306a36Sopenharmony_ci{
48462306a36Sopenharmony_ci	struct pep_sock *pn = pep_sk(sk);
48562306a36Sopenharmony_ci	struct pnpipehdr *hdr;
48662306a36Sopenharmony_ci	u8 n_sb;
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci	if (!pskb_pull(skb, sizeof(*hdr) + 4))
48962306a36Sopenharmony_ci		return -EINVAL;
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci	hdr = pnp_hdr(skb);
49262306a36Sopenharmony_ci	if (hdr->error_code != PN_PIPE_NO_ERROR)
49362306a36Sopenharmony_ci		return -ECONNREFUSED;
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci	/* Parse sub-blocks */
49662306a36Sopenharmony_ci	n_sb = hdr->data[3];
49762306a36Sopenharmony_ci	while (n_sb > 0) {
49862306a36Sopenharmony_ci		u8 type, buf[6], len = sizeof(buf);
49962306a36Sopenharmony_ci		const u8 *data = pep_get_sb(skb, &type, &len, buf);
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci		if (data == NULL)
50262306a36Sopenharmony_ci			return -EINVAL;
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci		switch (type) {
50562306a36Sopenharmony_ci		case PN_PIPE_SB_REQUIRED_FC_TX:
50662306a36Sopenharmony_ci			if (len < 2 || len < data[0])
50762306a36Sopenharmony_ci				break;
50862306a36Sopenharmony_ci			pn->tx_fc = pipe_negotiate_fc(data + 2, len - 2);
50962306a36Sopenharmony_ci			break;
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci		case PN_PIPE_SB_PREFERRED_FC_RX:
51262306a36Sopenharmony_ci			if (len < 2 || len < data[0])
51362306a36Sopenharmony_ci				break;
51462306a36Sopenharmony_ci			pn->rx_fc = pipe_negotiate_fc(data + 2, len - 2);
51562306a36Sopenharmony_ci			break;
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci		}
51862306a36Sopenharmony_ci		n_sb--;
51962306a36Sopenharmony_ci	}
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci	return pipe_handler_send_created_ind(sk);
52262306a36Sopenharmony_ci}
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_cistatic int pep_enableresp_rcv(struct sock *sk, struct sk_buff *skb)
52562306a36Sopenharmony_ci{
52662306a36Sopenharmony_ci	struct pnpipehdr *hdr = pnp_hdr(skb);
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci	if (hdr->error_code != PN_PIPE_NO_ERROR)
52962306a36Sopenharmony_ci		return -ECONNREFUSED;
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci	return pep_indicate(sk, PNS_PIPE_ENABLED_IND, 0 /* sub-blocks */,
53262306a36Sopenharmony_ci		NULL, 0, GFP_ATOMIC);
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci}
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_cistatic void pipe_start_flow_control(struct sock *sk)
53762306a36Sopenharmony_ci{
53862306a36Sopenharmony_ci	struct pep_sock *pn = pep_sk(sk);
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci	if (!pn_flow_safe(pn->tx_fc)) {
54162306a36Sopenharmony_ci		atomic_set(&pn->tx_credits, 1);
54262306a36Sopenharmony_ci		sk->sk_write_space(sk);
54362306a36Sopenharmony_ci	}
54462306a36Sopenharmony_ci	pipe_grant_credits(sk, GFP_ATOMIC);
54562306a36Sopenharmony_ci}
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci/* Queue an skb to an actively connected sock.
54862306a36Sopenharmony_ci * Socket lock must be held. */
54962306a36Sopenharmony_cistatic int pipe_handler_do_rcv(struct sock *sk, struct sk_buff *skb)
55062306a36Sopenharmony_ci{
55162306a36Sopenharmony_ci	struct pep_sock *pn = pep_sk(sk);
55262306a36Sopenharmony_ci	struct pnpipehdr *hdr = pnp_hdr(skb);
55362306a36Sopenharmony_ci	int err = NET_RX_SUCCESS;
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci	switch (hdr->message_id) {
55662306a36Sopenharmony_ci	case PNS_PIPE_ALIGNED_DATA:
55762306a36Sopenharmony_ci		__skb_pull(skb, 1);
55862306a36Sopenharmony_ci		fallthrough;
55962306a36Sopenharmony_ci	case PNS_PIPE_DATA:
56062306a36Sopenharmony_ci		__skb_pull(skb, 3); /* Pipe data header */
56162306a36Sopenharmony_ci		if (!pn_flow_safe(pn->rx_fc)) {
56262306a36Sopenharmony_ci			err = sock_queue_rcv_skb(sk, skb);
56362306a36Sopenharmony_ci			if (!err)
56462306a36Sopenharmony_ci				return NET_RX_SUCCESS;
56562306a36Sopenharmony_ci			err = NET_RX_DROP;
56662306a36Sopenharmony_ci			break;
56762306a36Sopenharmony_ci		}
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci		if (pn->rx_credits == 0) {
57062306a36Sopenharmony_ci			atomic_inc(&sk->sk_drops);
57162306a36Sopenharmony_ci			err = NET_RX_DROP;
57262306a36Sopenharmony_ci			break;
57362306a36Sopenharmony_ci		}
57462306a36Sopenharmony_ci		pn->rx_credits--;
57562306a36Sopenharmony_ci		skb->dev = NULL;
57662306a36Sopenharmony_ci		skb_set_owner_r(skb, sk);
57762306a36Sopenharmony_ci		skb_queue_tail(&sk->sk_receive_queue, skb);
57862306a36Sopenharmony_ci		if (!sock_flag(sk, SOCK_DEAD))
57962306a36Sopenharmony_ci			sk->sk_data_ready(sk);
58062306a36Sopenharmony_ci		return NET_RX_SUCCESS;
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_ci	case PNS_PEP_CONNECT_RESP:
58362306a36Sopenharmony_ci		if (sk->sk_state != TCP_SYN_SENT)
58462306a36Sopenharmony_ci			break;
58562306a36Sopenharmony_ci		if (!sock_flag(sk, SOCK_DEAD))
58662306a36Sopenharmony_ci			sk->sk_state_change(sk);
58762306a36Sopenharmony_ci		if (pep_connresp_rcv(sk, skb)) {
58862306a36Sopenharmony_ci			sk->sk_state = TCP_CLOSE_WAIT;
58962306a36Sopenharmony_ci			break;
59062306a36Sopenharmony_ci		}
59162306a36Sopenharmony_ci		if (pn->init_enable == PN_PIPE_DISABLE)
59262306a36Sopenharmony_ci			sk->sk_state = TCP_SYN_RECV;
59362306a36Sopenharmony_ci		else {
59462306a36Sopenharmony_ci			sk->sk_state = TCP_ESTABLISHED;
59562306a36Sopenharmony_ci			pipe_start_flow_control(sk);
59662306a36Sopenharmony_ci		}
59762306a36Sopenharmony_ci		break;
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_ci	case PNS_PEP_ENABLE_RESP:
60062306a36Sopenharmony_ci		if (sk->sk_state != TCP_SYN_SENT)
60162306a36Sopenharmony_ci			break;
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_ci		if (pep_enableresp_rcv(sk, skb)) {
60462306a36Sopenharmony_ci			sk->sk_state = TCP_CLOSE_WAIT;
60562306a36Sopenharmony_ci			break;
60662306a36Sopenharmony_ci		}
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_ci		sk->sk_state = TCP_ESTABLISHED;
60962306a36Sopenharmony_ci		pipe_start_flow_control(sk);
61062306a36Sopenharmony_ci		break;
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci	case PNS_PEP_DISCONNECT_RESP:
61362306a36Sopenharmony_ci		/* sock should already be dead, nothing to do */
61462306a36Sopenharmony_ci		break;
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_ci	case PNS_PEP_STATUS_IND:
61762306a36Sopenharmony_ci		pipe_rcv_status(sk, skb);
61862306a36Sopenharmony_ci		break;
61962306a36Sopenharmony_ci	}
62062306a36Sopenharmony_ci	kfree_skb(skb);
62162306a36Sopenharmony_ci	return err;
62262306a36Sopenharmony_ci}
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_ci/* Listening sock must be locked */
62562306a36Sopenharmony_cistatic struct sock *pep_find_pipe(const struct hlist_head *hlist,
62662306a36Sopenharmony_ci					const struct sockaddr_pn *dst,
62762306a36Sopenharmony_ci					u8 pipe_handle)
62862306a36Sopenharmony_ci{
62962306a36Sopenharmony_ci	struct sock *sknode;
63062306a36Sopenharmony_ci	u16 dobj = pn_sockaddr_get_object(dst);
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci	sk_for_each(sknode, hlist) {
63362306a36Sopenharmony_ci		struct pep_sock *pnnode = pep_sk(sknode);
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_ci		/* Ports match, but addresses might not: */
63662306a36Sopenharmony_ci		if (pnnode->pn_sk.sobject != dobj)
63762306a36Sopenharmony_ci			continue;
63862306a36Sopenharmony_ci		if (pnnode->pipe_handle != pipe_handle)
63962306a36Sopenharmony_ci			continue;
64062306a36Sopenharmony_ci		if (sknode->sk_state == TCP_CLOSE_WAIT)
64162306a36Sopenharmony_ci			continue;
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_ci		sock_hold(sknode);
64462306a36Sopenharmony_ci		return sknode;
64562306a36Sopenharmony_ci	}
64662306a36Sopenharmony_ci	return NULL;
64762306a36Sopenharmony_ci}
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_ci/*
65062306a36Sopenharmony_ci * Deliver an skb to a listening sock.
65162306a36Sopenharmony_ci * Socket lock must be held.
65262306a36Sopenharmony_ci * We then queue the skb to the right connected sock (if any).
65362306a36Sopenharmony_ci */
65462306a36Sopenharmony_cistatic int pep_do_rcv(struct sock *sk, struct sk_buff *skb)
65562306a36Sopenharmony_ci{
65662306a36Sopenharmony_ci	struct pep_sock *pn = pep_sk(sk);
65762306a36Sopenharmony_ci	struct sock *sknode;
65862306a36Sopenharmony_ci	struct pnpipehdr *hdr;
65962306a36Sopenharmony_ci	struct sockaddr_pn dst;
66062306a36Sopenharmony_ci	u8 pipe_handle;
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci	if (!pskb_may_pull(skb, sizeof(*hdr)))
66362306a36Sopenharmony_ci		goto drop;
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_ci	hdr = pnp_hdr(skb);
66662306a36Sopenharmony_ci	pipe_handle = hdr->pipe_handle;
66762306a36Sopenharmony_ci	if (pipe_handle == PN_PIPE_INVALID_HANDLE)
66862306a36Sopenharmony_ci		goto drop;
66962306a36Sopenharmony_ci
67062306a36Sopenharmony_ci	pn_skb_get_dst_sockaddr(skb, &dst);
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_ci	/* Look for an existing pipe handle */
67362306a36Sopenharmony_ci	sknode = pep_find_pipe(&pn->hlist, &dst, pipe_handle);
67462306a36Sopenharmony_ci	if (sknode)
67562306a36Sopenharmony_ci		return sk_receive_skb(sknode, skb, 1);
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_ci	switch (hdr->message_id) {
67862306a36Sopenharmony_ci	case PNS_PEP_CONNECT_REQ:
67962306a36Sopenharmony_ci		if (sk->sk_state != TCP_LISTEN || sk_acceptq_is_full(sk)) {
68062306a36Sopenharmony_ci			pep_reject_conn(sk, skb, PN_PIPE_ERR_PEP_IN_USE,
68162306a36Sopenharmony_ci					GFP_ATOMIC);
68262306a36Sopenharmony_ci			break;
68362306a36Sopenharmony_ci		}
68462306a36Sopenharmony_ci		skb_queue_head(&sk->sk_receive_queue, skb);
68562306a36Sopenharmony_ci		sk_acceptq_added(sk);
68662306a36Sopenharmony_ci		if (!sock_flag(sk, SOCK_DEAD))
68762306a36Sopenharmony_ci			sk->sk_data_ready(sk);
68862306a36Sopenharmony_ci		return NET_RX_SUCCESS;
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_ci	case PNS_PEP_DISCONNECT_REQ:
69162306a36Sopenharmony_ci		pep_reply(sk, skb, PN_PIPE_NO_ERROR, NULL, 0, GFP_ATOMIC);
69262306a36Sopenharmony_ci		break;
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_ci	case PNS_PEP_CTRL_REQ:
69562306a36Sopenharmony_ci		pep_ctrlreq_error(sk, skb, PN_PIPE_INVALID_HANDLE, GFP_ATOMIC);
69662306a36Sopenharmony_ci		break;
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ci	case PNS_PEP_RESET_REQ:
69962306a36Sopenharmony_ci	case PNS_PEP_ENABLE_REQ:
70062306a36Sopenharmony_ci	case PNS_PEP_DISABLE_REQ:
70162306a36Sopenharmony_ci		/* invalid handle is not even allowed here! */
70262306a36Sopenharmony_ci		break;
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ci	default:
70562306a36Sopenharmony_ci		if ((1 << sk->sk_state)
70662306a36Sopenharmony_ci				& ~(TCPF_CLOSE|TCPF_LISTEN|TCPF_CLOSE_WAIT))
70762306a36Sopenharmony_ci			/* actively connected socket */
70862306a36Sopenharmony_ci			return pipe_handler_do_rcv(sk, skb);
70962306a36Sopenharmony_ci	}
71062306a36Sopenharmony_cidrop:
71162306a36Sopenharmony_ci	kfree_skb(skb);
71262306a36Sopenharmony_ci	return NET_RX_SUCCESS;
71362306a36Sopenharmony_ci}
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_cistatic int pipe_do_remove(struct sock *sk)
71662306a36Sopenharmony_ci{
71762306a36Sopenharmony_ci	struct pep_sock *pn = pep_sk(sk);
71862306a36Sopenharmony_ci	struct pnpipehdr *ph;
71962306a36Sopenharmony_ci	struct sk_buff *skb;
72062306a36Sopenharmony_ci
72162306a36Sopenharmony_ci	skb = pep_alloc_skb(sk, NULL, 0, GFP_KERNEL);
72262306a36Sopenharmony_ci	if (!skb)
72362306a36Sopenharmony_ci		return -ENOMEM;
72462306a36Sopenharmony_ci
72562306a36Sopenharmony_ci	ph = pnp_hdr(skb);
72662306a36Sopenharmony_ci	ph->utid = 0;
72762306a36Sopenharmony_ci	ph->message_id = PNS_PIPE_REMOVE_REQ;
72862306a36Sopenharmony_ci	ph->pipe_handle = pn->pipe_handle;
72962306a36Sopenharmony_ci	ph->data0 = PAD;
73062306a36Sopenharmony_ci	return pn_skb_send(sk, skb, NULL);
73162306a36Sopenharmony_ci}
73262306a36Sopenharmony_ci
73362306a36Sopenharmony_ci/* associated socket ceases to exist */
73462306a36Sopenharmony_cistatic void pep_sock_close(struct sock *sk, long timeout)
73562306a36Sopenharmony_ci{
73662306a36Sopenharmony_ci	struct pep_sock *pn = pep_sk(sk);
73762306a36Sopenharmony_ci	int ifindex = 0;
73862306a36Sopenharmony_ci
73962306a36Sopenharmony_ci	sock_hold(sk); /* keep a reference after sk_common_release() */
74062306a36Sopenharmony_ci	sk_common_release(sk);
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_ci	lock_sock(sk);
74362306a36Sopenharmony_ci	if ((1 << sk->sk_state) & (TCPF_SYN_RECV|TCPF_ESTABLISHED)) {
74462306a36Sopenharmony_ci		if (sk->sk_backlog_rcv == pipe_do_rcv)
74562306a36Sopenharmony_ci			/* Forcefully remove dangling Phonet pipe */
74662306a36Sopenharmony_ci			pipe_do_remove(sk);
74762306a36Sopenharmony_ci		else
74862306a36Sopenharmony_ci			pipe_handler_request(sk, PNS_PEP_DISCONNECT_REQ, PAD,
74962306a36Sopenharmony_ci						NULL, 0);
75062306a36Sopenharmony_ci	}
75162306a36Sopenharmony_ci	sk->sk_state = TCP_CLOSE;
75262306a36Sopenharmony_ci
75362306a36Sopenharmony_ci	ifindex = pn->ifindex;
75462306a36Sopenharmony_ci	pn->ifindex = 0;
75562306a36Sopenharmony_ci	release_sock(sk);
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_ci	if (ifindex)
75862306a36Sopenharmony_ci		gprs_detach(sk);
75962306a36Sopenharmony_ci	sock_put(sk);
76062306a36Sopenharmony_ci}
76162306a36Sopenharmony_ci
76262306a36Sopenharmony_cistatic struct sock *pep_sock_accept(struct sock *sk, int flags, int *errp,
76362306a36Sopenharmony_ci				    bool kern)
76462306a36Sopenharmony_ci{
76562306a36Sopenharmony_ci	struct pep_sock *pn = pep_sk(sk), *newpn;
76662306a36Sopenharmony_ci	struct sock *newsk = NULL;
76762306a36Sopenharmony_ci	struct sk_buff *skb;
76862306a36Sopenharmony_ci	struct pnpipehdr *hdr;
76962306a36Sopenharmony_ci	struct sockaddr_pn dst, src;
77062306a36Sopenharmony_ci	int err;
77162306a36Sopenharmony_ci	u16 peer_type;
77262306a36Sopenharmony_ci	u8 pipe_handle, enabled, n_sb;
77362306a36Sopenharmony_ci	u8 aligned = 0;
77462306a36Sopenharmony_ci
77562306a36Sopenharmony_ci	skb = skb_recv_datagram(sk, (flags & O_NONBLOCK) ? MSG_DONTWAIT : 0,
77662306a36Sopenharmony_ci				errp);
77762306a36Sopenharmony_ci	if (!skb)
77862306a36Sopenharmony_ci		return NULL;
77962306a36Sopenharmony_ci
78062306a36Sopenharmony_ci	lock_sock(sk);
78162306a36Sopenharmony_ci	if (sk->sk_state != TCP_LISTEN) {
78262306a36Sopenharmony_ci		err = -EINVAL;
78362306a36Sopenharmony_ci		goto drop;
78462306a36Sopenharmony_ci	}
78562306a36Sopenharmony_ci	sk_acceptq_removed(sk);
78662306a36Sopenharmony_ci
78762306a36Sopenharmony_ci	err = -EPROTO;
78862306a36Sopenharmony_ci	if (!pskb_may_pull(skb, sizeof(*hdr) + 4))
78962306a36Sopenharmony_ci		goto drop;
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_ci	hdr = pnp_hdr(skb);
79262306a36Sopenharmony_ci	pipe_handle = hdr->pipe_handle;
79362306a36Sopenharmony_ci	switch (hdr->state_after_connect) {
79462306a36Sopenharmony_ci	case PN_PIPE_DISABLE:
79562306a36Sopenharmony_ci		enabled = 0;
79662306a36Sopenharmony_ci		break;
79762306a36Sopenharmony_ci	case PN_PIPE_ENABLE:
79862306a36Sopenharmony_ci		enabled = 1;
79962306a36Sopenharmony_ci		break;
80062306a36Sopenharmony_ci	default:
80162306a36Sopenharmony_ci		pep_reject_conn(sk, skb, PN_PIPE_ERR_INVALID_PARAM,
80262306a36Sopenharmony_ci				GFP_KERNEL);
80362306a36Sopenharmony_ci		goto drop;
80462306a36Sopenharmony_ci	}
80562306a36Sopenharmony_ci	peer_type = hdr->other_pep_type << 8;
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_ci	/* Parse sub-blocks (options) */
80862306a36Sopenharmony_ci	n_sb = hdr->data[3];
80962306a36Sopenharmony_ci	while (n_sb > 0) {
81062306a36Sopenharmony_ci		u8 type, buf[1], len = sizeof(buf);
81162306a36Sopenharmony_ci		const u8 *data = pep_get_sb(skb, &type, &len, buf);
81262306a36Sopenharmony_ci
81362306a36Sopenharmony_ci		if (data == NULL)
81462306a36Sopenharmony_ci			goto drop;
81562306a36Sopenharmony_ci		switch (type) {
81662306a36Sopenharmony_ci		case PN_PIPE_SB_CONNECT_REQ_PEP_SUB_TYPE:
81762306a36Sopenharmony_ci			if (len < 1)
81862306a36Sopenharmony_ci				goto drop;
81962306a36Sopenharmony_ci			peer_type = (peer_type & 0xff00) | data[0];
82062306a36Sopenharmony_ci			break;
82162306a36Sopenharmony_ci		case PN_PIPE_SB_ALIGNED_DATA:
82262306a36Sopenharmony_ci			aligned = data[0] != 0;
82362306a36Sopenharmony_ci			break;
82462306a36Sopenharmony_ci		}
82562306a36Sopenharmony_ci		n_sb--;
82662306a36Sopenharmony_ci	}
82762306a36Sopenharmony_ci
82862306a36Sopenharmony_ci	/* Check for duplicate pipe handle */
82962306a36Sopenharmony_ci	newsk = pep_find_pipe(&pn->hlist, &dst, pipe_handle);
83062306a36Sopenharmony_ci	if (unlikely(newsk)) {
83162306a36Sopenharmony_ci		__sock_put(newsk);
83262306a36Sopenharmony_ci		newsk = NULL;
83362306a36Sopenharmony_ci		pep_reject_conn(sk, skb, PN_PIPE_ERR_PEP_IN_USE, GFP_KERNEL);
83462306a36Sopenharmony_ci		goto drop;
83562306a36Sopenharmony_ci	}
83662306a36Sopenharmony_ci
83762306a36Sopenharmony_ci	/* Create a new to-be-accepted sock */
83862306a36Sopenharmony_ci	newsk = sk_alloc(sock_net(sk), PF_PHONET, GFP_KERNEL, sk->sk_prot,
83962306a36Sopenharmony_ci			 kern);
84062306a36Sopenharmony_ci	if (!newsk) {
84162306a36Sopenharmony_ci		pep_reject_conn(sk, skb, PN_PIPE_ERR_OVERLOAD, GFP_KERNEL);
84262306a36Sopenharmony_ci		err = -ENOBUFS;
84362306a36Sopenharmony_ci		goto drop;
84462306a36Sopenharmony_ci	}
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_ci	sock_init_data(NULL, newsk);
84762306a36Sopenharmony_ci	newsk->sk_state = TCP_SYN_RECV;
84862306a36Sopenharmony_ci	newsk->sk_backlog_rcv = pipe_do_rcv;
84962306a36Sopenharmony_ci	newsk->sk_protocol = sk->sk_protocol;
85062306a36Sopenharmony_ci	newsk->sk_destruct = pipe_destruct;
85162306a36Sopenharmony_ci
85262306a36Sopenharmony_ci	newpn = pep_sk(newsk);
85362306a36Sopenharmony_ci	pn_skb_get_dst_sockaddr(skb, &dst);
85462306a36Sopenharmony_ci	pn_skb_get_src_sockaddr(skb, &src);
85562306a36Sopenharmony_ci	newpn->pn_sk.sobject = pn_sockaddr_get_object(&dst);
85662306a36Sopenharmony_ci	newpn->pn_sk.dobject = pn_sockaddr_get_object(&src);
85762306a36Sopenharmony_ci	newpn->pn_sk.resource = pn_sockaddr_get_resource(&dst);
85862306a36Sopenharmony_ci	sock_hold(sk);
85962306a36Sopenharmony_ci	newpn->listener = sk;
86062306a36Sopenharmony_ci	skb_queue_head_init(&newpn->ctrlreq_queue);
86162306a36Sopenharmony_ci	newpn->pipe_handle = pipe_handle;
86262306a36Sopenharmony_ci	atomic_set(&newpn->tx_credits, 0);
86362306a36Sopenharmony_ci	newpn->ifindex = 0;
86462306a36Sopenharmony_ci	newpn->peer_type = peer_type;
86562306a36Sopenharmony_ci	newpn->rx_credits = 0;
86662306a36Sopenharmony_ci	newpn->rx_fc = newpn->tx_fc = PN_LEGACY_FLOW_CONTROL;
86762306a36Sopenharmony_ci	newpn->init_enable = enabled;
86862306a36Sopenharmony_ci	newpn->aligned = aligned;
86962306a36Sopenharmony_ci
87062306a36Sopenharmony_ci	err = pep_accept_conn(newsk, skb);
87162306a36Sopenharmony_ci	if (err) {
87262306a36Sopenharmony_ci		__sock_put(sk);
87362306a36Sopenharmony_ci		sock_put(newsk);
87462306a36Sopenharmony_ci		newsk = NULL;
87562306a36Sopenharmony_ci		goto drop;
87662306a36Sopenharmony_ci	}
87762306a36Sopenharmony_ci	sk_add_node(newsk, &pn->hlist);
87862306a36Sopenharmony_cidrop:
87962306a36Sopenharmony_ci	release_sock(sk);
88062306a36Sopenharmony_ci	kfree_skb(skb);
88162306a36Sopenharmony_ci	*errp = err;
88262306a36Sopenharmony_ci	return newsk;
88362306a36Sopenharmony_ci}
88462306a36Sopenharmony_ci
88562306a36Sopenharmony_cistatic int pep_sock_connect(struct sock *sk, struct sockaddr *addr, int len)
88662306a36Sopenharmony_ci{
88762306a36Sopenharmony_ci	struct pep_sock *pn = pep_sk(sk);
88862306a36Sopenharmony_ci	int err;
88962306a36Sopenharmony_ci	u8 data[4] = { 0 /* sub-blocks */, PAD, PAD, PAD };
89062306a36Sopenharmony_ci
89162306a36Sopenharmony_ci	if (pn->pipe_handle == PN_PIPE_INVALID_HANDLE)
89262306a36Sopenharmony_ci		pn->pipe_handle = 1; /* anything but INVALID_HANDLE */
89362306a36Sopenharmony_ci
89462306a36Sopenharmony_ci	err = pipe_handler_request(sk, PNS_PEP_CONNECT_REQ,
89562306a36Sopenharmony_ci				pn->init_enable, data, 4);
89662306a36Sopenharmony_ci	if (err) {
89762306a36Sopenharmony_ci		pn->pipe_handle = PN_PIPE_INVALID_HANDLE;
89862306a36Sopenharmony_ci		return err;
89962306a36Sopenharmony_ci	}
90062306a36Sopenharmony_ci
90162306a36Sopenharmony_ci	sk->sk_state = TCP_SYN_SENT;
90262306a36Sopenharmony_ci
90362306a36Sopenharmony_ci	return 0;
90462306a36Sopenharmony_ci}
90562306a36Sopenharmony_ci
90662306a36Sopenharmony_cistatic int pep_sock_enable(struct sock *sk, struct sockaddr *addr, int len)
90762306a36Sopenharmony_ci{
90862306a36Sopenharmony_ci	int err;
90962306a36Sopenharmony_ci
91062306a36Sopenharmony_ci	err = pipe_handler_request(sk, PNS_PEP_ENABLE_REQ, PAD,
91162306a36Sopenharmony_ci				NULL, 0);
91262306a36Sopenharmony_ci	if (err)
91362306a36Sopenharmony_ci		return err;
91462306a36Sopenharmony_ci
91562306a36Sopenharmony_ci	sk->sk_state = TCP_SYN_SENT;
91662306a36Sopenharmony_ci
91762306a36Sopenharmony_ci	return 0;
91862306a36Sopenharmony_ci}
91962306a36Sopenharmony_ci
92062306a36Sopenharmony_cistatic unsigned int pep_first_packet_length(struct sock *sk)
92162306a36Sopenharmony_ci{
92262306a36Sopenharmony_ci	struct pep_sock *pn = pep_sk(sk);
92362306a36Sopenharmony_ci	struct sk_buff_head *q;
92462306a36Sopenharmony_ci	struct sk_buff *skb;
92562306a36Sopenharmony_ci	unsigned int len = 0;
92662306a36Sopenharmony_ci	bool found = false;
92762306a36Sopenharmony_ci
92862306a36Sopenharmony_ci	if (sock_flag(sk, SOCK_URGINLINE)) {
92962306a36Sopenharmony_ci		q = &pn->ctrlreq_queue;
93062306a36Sopenharmony_ci		spin_lock_bh(&q->lock);
93162306a36Sopenharmony_ci		skb = skb_peek(q);
93262306a36Sopenharmony_ci		if (skb) {
93362306a36Sopenharmony_ci			len = skb->len;
93462306a36Sopenharmony_ci			found = true;
93562306a36Sopenharmony_ci		}
93662306a36Sopenharmony_ci		spin_unlock_bh(&q->lock);
93762306a36Sopenharmony_ci	}
93862306a36Sopenharmony_ci
93962306a36Sopenharmony_ci	if (likely(!found)) {
94062306a36Sopenharmony_ci		q = &sk->sk_receive_queue;
94162306a36Sopenharmony_ci		spin_lock_bh(&q->lock);
94262306a36Sopenharmony_ci		skb = skb_peek(q);
94362306a36Sopenharmony_ci		if (skb)
94462306a36Sopenharmony_ci			len = skb->len;
94562306a36Sopenharmony_ci		spin_unlock_bh(&q->lock);
94662306a36Sopenharmony_ci	}
94762306a36Sopenharmony_ci
94862306a36Sopenharmony_ci	return len;
94962306a36Sopenharmony_ci}
95062306a36Sopenharmony_ci
95162306a36Sopenharmony_cistatic int pep_ioctl(struct sock *sk, int cmd, int *karg)
95262306a36Sopenharmony_ci{
95362306a36Sopenharmony_ci	struct pep_sock *pn = pep_sk(sk);
95462306a36Sopenharmony_ci	int ret = -ENOIOCTLCMD;
95562306a36Sopenharmony_ci
95662306a36Sopenharmony_ci	switch (cmd) {
95762306a36Sopenharmony_ci	case SIOCINQ:
95862306a36Sopenharmony_ci		if (sk->sk_state == TCP_LISTEN) {
95962306a36Sopenharmony_ci			ret = -EINVAL;
96062306a36Sopenharmony_ci			break;
96162306a36Sopenharmony_ci		}
96262306a36Sopenharmony_ci
96362306a36Sopenharmony_ci		*karg = pep_first_packet_length(sk);
96462306a36Sopenharmony_ci		ret = 0;
96562306a36Sopenharmony_ci		break;
96662306a36Sopenharmony_ci
96762306a36Sopenharmony_ci	case SIOCPNENABLEPIPE:
96862306a36Sopenharmony_ci		lock_sock(sk);
96962306a36Sopenharmony_ci		if (sk->sk_state == TCP_SYN_SENT)
97062306a36Sopenharmony_ci			ret =  -EBUSY;
97162306a36Sopenharmony_ci		else if (sk->sk_state == TCP_ESTABLISHED)
97262306a36Sopenharmony_ci			ret = -EISCONN;
97362306a36Sopenharmony_ci		else if (!pn->pn_sk.sobject)
97462306a36Sopenharmony_ci			ret = -EADDRNOTAVAIL;
97562306a36Sopenharmony_ci		else
97662306a36Sopenharmony_ci			ret = pep_sock_enable(sk, NULL, 0);
97762306a36Sopenharmony_ci		release_sock(sk);
97862306a36Sopenharmony_ci		break;
97962306a36Sopenharmony_ci	}
98062306a36Sopenharmony_ci
98162306a36Sopenharmony_ci	return ret;
98262306a36Sopenharmony_ci}
98362306a36Sopenharmony_ci
98462306a36Sopenharmony_cistatic int pep_init(struct sock *sk)
98562306a36Sopenharmony_ci{
98662306a36Sopenharmony_ci	struct pep_sock *pn = pep_sk(sk);
98762306a36Sopenharmony_ci
98862306a36Sopenharmony_ci	sk->sk_destruct = pipe_destruct;
98962306a36Sopenharmony_ci	INIT_HLIST_HEAD(&pn->hlist);
99062306a36Sopenharmony_ci	pn->listener = NULL;
99162306a36Sopenharmony_ci	skb_queue_head_init(&pn->ctrlreq_queue);
99262306a36Sopenharmony_ci	atomic_set(&pn->tx_credits, 0);
99362306a36Sopenharmony_ci	pn->ifindex = 0;
99462306a36Sopenharmony_ci	pn->peer_type = 0;
99562306a36Sopenharmony_ci	pn->pipe_handle = PN_PIPE_INVALID_HANDLE;
99662306a36Sopenharmony_ci	pn->rx_credits = 0;
99762306a36Sopenharmony_ci	pn->rx_fc = pn->tx_fc = PN_LEGACY_FLOW_CONTROL;
99862306a36Sopenharmony_ci	pn->init_enable = 1;
99962306a36Sopenharmony_ci	pn->aligned = 0;
100062306a36Sopenharmony_ci	return 0;
100162306a36Sopenharmony_ci}
100262306a36Sopenharmony_ci
100362306a36Sopenharmony_cistatic int pep_setsockopt(struct sock *sk, int level, int optname,
100462306a36Sopenharmony_ci			  sockptr_t optval, unsigned int optlen)
100562306a36Sopenharmony_ci{
100662306a36Sopenharmony_ci	struct pep_sock *pn = pep_sk(sk);
100762306a36Sopenharmony_ci	int val = 0, err = 0;
100862306a36Sopenharmony_ci
100962306a36Sopenharmony_ci	if (level != SOL_PNPIPE)
101062306a36Sopenharmony_ci		return -ENOPROTOOPT;
101162306a36Sopenharmony_ci	if (optlen >= sizeof(int)) {
101262306a36Sopenharmony_ci		if (copy_from_sockptr(&val, optval, sizeof(int)))
101362306a36Sopenharmony_ci			return -EFAULT;
101462306a36Sopenharmony_ci	}
101562306a36Sopenharmony_ci
101662306a36Sopenharmony_ci	lock_sock(sk);
101762306a36Sopenharmony_ci	switch (optname) {
101862306a36Sopenharmony_ci	case PNPIPE_ENCAP:
101962306a36Sopenharmony_ci		if (val && val != PNPIPE_ENCAP_IP) {
102062306a36Sopenharmony_ci			err = -EINVAL;
102162306a36Sopenharmony_ci			break;
102262306a36Sopenharmony_ci		}
102362306a36Sopenharmony_ci		if (!pn->ifindex == !val)
102462306a36Sopenharmony_ci			break; /* Nothing to do! */
102562306a36Sopenharmony_ci		if (!capable(CAP_NET_ADMIN)) {
102662306a36Sopenharmony_ci			err = -EPERM;
102762306a36Sopenharmony_ci			break;
102862306a36Sopenharmony_ci		}
102962306a36Sopenharmony_ci		if (val) {
103062306a36Sopenharmony_ci			release_sock(sk);
103162306a36Sopenharmony_ci			err = gprs_attach(sk);
103262306a36Sopenharmony_ci			if (err > 0) {
103362306a36Sopenharmony_ci				pn->ifindex = err;
103462306a36Sopenharmony_ci				err = 0;
103562306a36Sopenharmony_ci			}
103662306a36Sopenharmony_ci		} else {
103762306a36Sopenharmony_ci			pn->ifindex = 0;
103862306a36Sopenharmony_ci			release_sock(sk);
103962306a36Sopenharmony_ci			gprs_detach(sk);
104062306a36Sopenharmony_ci			err = 0;
104162306a36Sopenharmony_ci		}
104262306a36Sopenharmony_ci		goto out_norel;
104362306a36Sopenharmony_ci
104462306a36Sopenharmony_ci	case PNPIPE_HANDLE:
104562306a36Sopenharmony_ci		if ((sk->sk_state == TCP_CLOSE) &&
104662306a36Sopenharmony_ci			(val >= 0) && (val < PN_PIPE_INVALID_HANDLE))
104762306a36Sopenharmony_ci			pn->pipe_handle = val;
104862306a36Sopenharmony_ci		else
104962306a36Sopenharmony_ci			err = -EINVAL;
105062306a36Sopenharmony_ci		break;
105162306a36Sopenharmony_ci
105262306a36Sopenharmony_ci	case PNPIPE_INITSTATE:
105362306a36Sopenharmony_ci		pn->init_enable = !!val;
105462306a36Sopenharmony_ci		break;
105562306a36Sopenharmony_ci
105662306a36Sopenharmony_ci	default:
105762306a36Sopenharmony_ci		err = -ENOPROTOOPT;
105862306a36Sopenharmony_ci	}
105962306a36Sopenharmony_ci	release_sock(sk);
106062306a36Sopenharmony_ci
106162306a36Sopenharmony_ciout_norel:
106262306a36Sopenharmony_ci	return err;
106362306a36Sopenharmony_ci}
106462306a36Sopenharmony_ci
106562306a36Sopenharmony_cistatic int pep_getsockopt(struct sock *sk, int level, int optname,
106662306a36Sopenharmony_ci				char __user *optval, int __user *optlen)
106762306a36Sopenharmony_ci{
106862306a36Sopenharmony_ci	struct pep_sock *pn = pep_sk(sk);
106962306a36Sopenharmony_ci	int len, val;
107062306a36Sopenharmony_ci
107162306a36Sopenharmony_ci	if (level != SOL_PNPIPE)
107262306a36Sopenharmony_ci		return -ENOPROTOOPT;
107362306a36Sopenharmony_ci	if (get_user(len, optlen))
107462306a36Sopenharmony_ci		return -EFAULT;
107562306a36Sopenharmony_ci
107662306a36Sopenharmony_ci	switch (optname) {
107762306a36Sopenharmony_ci	case PNPIPE_ENCAP:
107862306a36Sopenharmony_ci		val = pn->ifindex ? PNPIPE_ENCAP_IP : PNPIPE_ENCAP_NONE;
107962306a36Sopenharmony_ci		break;
108062306a36Sopenharmony_ci
108162306a36Sopenharmony_ci	case PNPIPE_IFINDEX:
108262306a36Sopenharmony_ci		val = pn->ifindex;
108362306a36Sopenharmony_ci		break;
108462306a36Sopenharmony_ci
108562306a36Sopenharmony_ci	case PNPIPE_HANDLE:
108662306a36Sopenharmony_ci		val = pn->pipe_handle;
108762306a36Sopenharmony_ci		if (val == PN_PIPE_INVALID_HANDLE)
108862306a36Sopenharmony_ci			return -EINVAL;
108962306a36Sopenharmony_ci		break;
109062306a36Sopenharmony_ci
109162306a36Sopenharmony_ci	case PNPIPE_INITSTATE:
109262306a36Sopenharmony_ci		val = pn->init_enable;
109362306a36Sopenharmony_ci		break;
109462306a36Sopenharmony_ci
109562306a36Sopenharmony_ci	default:
109662306a36Sopenharmony_ci		return -ENOPROTOOPT;
109762306a36Sopenharmony_ci	}
109862306a36Sopenharmony_ci
109962306a36Sopenharmony_ci	len = min_t(unsigned int, sizeof(int), len);
110062306a36Sopenharmony_ci	if (put_user(len, optlen))
110162306a36Sopenharmony_ci		return -EFAULT;
110262306a36Sopenharmony_ci	if (put_user(val, (int __user *) optval))
110362306a36Sopenharmony_ci		return -EFAULT;
110462306a36Sopenharmony_ci	return 0;
110562306a36Sopenharmony_ci}
110662306a36Sopenharmony_ci
110762306a36Sopenharmony_cistatic int pipe_skb_send(struct sock *sk, struct sk_buff *skb)
110862306a36Sopenharmony_ci{
110962306a36Sopenharmony_ci	struct pep_sock *pn = pep_sk(sk);
111062306a36Sopenharmony_ci	struct pnpipehdr *ph;
111162306a36Sopenharmony_ci	int err;
111262306a36Sopenharmony_ci
111362306a36Sopenharmony_ci	if (pn_flow_safe(pn->tx_fc) &&
111462306a36Sopenharmony_ci	    !atomic_add_unless(&pn->tx_credits, -1, 0)) {
111562306a36Sopenharmony_ci		kfree_skb(skb);
111662306a36Sopenharmony_ci		return -ENOBUFS;
111762306a36Sopenharmony_ci	}
111862306a36Sopenharmony_ci
111962306a36Sopenharmony_ci	skb_push(skb, 3 + pn->aligned);
112062306a36Sopenharmony_ci	skb_reset_transport_header(skb);
112162306a36Sopenharmony_ci	ph = pnp_hdr(skb);
112262306a36Sopenharmony_ci	ph->utid = 0;
112362306a36Sopenharmony_ci	if (pn->aligned) {
112462306a36Sopenharmony_ci		ph->message_id = PNS_PIPE_ALIGNED_DATA;
112562306a36Sopenharmony_ci		ph->data0 = 0; /* padding */
112662306a36Sopenharmony_ci	} else
112762306a36Sopenharmony_ci		ph->message_id = PNS_PIPE_DATA;
112862306a36Sopenharmony_ci	ph->pipe_handle = pn->pipe_handle;
112962306a36Sopenharmony_ci	err = pn_skb_send(sk, skb, NULL);
113062306a36Sopenharmony_ci
113162306a36Sopenharmony_ci	if (err && pn_flow_safe(pn->tx_fc))
113262306a36Sopenharmony_ci		atomic_inc(&pn->tx_credits);
113362306a36Sopenharmony_ci	return err;
113462306a36Sopenharmony_ci
113562306a36Sopenharmony_ci}
113662306a36Sopenharmony_ci
113762306a36Sopenharmony_cistatic int pep_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
113862306a36Sopenharmony_ci{
113962306a36Sopenharmony_ci	struct pep_sock *pn = pep_sk(sk);
114062306a36Sopenharmony_ci	struct sk_buff *skb;
114162306a36Sopenharmony_ci	long timeo;
114262306a36Sopenharmony_ci	int flags = msg->msg_flags;
114362306a36Sopenharmony_ci	int err, done;
114462306a36Sopenharmony_ci
114562306a36Sopenharmony_ci	if (len > USHRT_MAX)
114662306a36Sopenharmony_ci		return -EMSGSIZE;
114762306a36Sopenharmony_ci
114862306a36Sopenharmony_ci	if ((msg->msg_flags & ~(MSG_DONTWAIT|MSG_EOR|MSG_NOSIGNAL|
114962306a36Sopenharmony_ci				MSG_CMSG_COMPAT)) ||
115062306a36Sopenharmony_ci			!(msg->msg_flags & MSG_EOR))
115162306a36Sopenharmony_ci		return -EOPNOTSUPP;
115262306a36Sopenharmony_ci
115362306a36Sopenharmony_ci	skb = sock_alloc_send_skb(sk, MAX_PNPIPE_HEADER + len,
115462306a36Sopenharmony_ci					flags & MSG_DONTWAIT, &err);
115562306a36Sopenharmony_ci	if (!skb)
115662306a36Sopenharmony_ci		return err;
115762306a36Sopenharmony_ci
115862306a36Sopenharmony_ci	skb_reserve(skb, MAX_PHONET_HEADER + 3 + pn->aligned);
115962306a36Sopenharmony_ci	err = memcpy_from_msg(skb_put(skb, len), msg, len);
116062306a36Sopenharmony_ci	if (err < 0)
116162306a36Sopenharmony_ci		goto outfree;
116262306a36Sopenharmony_ci
116362306a36Sopenharmony_ci	lock_sock(sk);
116462306a36Sopenharmony_ci	timeo = sock_sndtimeo(sk, flags & MSG_DONTWAIT);
116562306a36Sopenharmony_ci	if ((1 << sk->sk_state) & (TCPF_LISTEN|TCPF_CLOSE)) {
116662306a36Sopenharmony_ci		err = -ENOTCONN;
116762306a36Sopenharmony_ci		goto out;
116862306a36Sopenharmony_ci	}
116962306a36Sopenharmony_ci	if (sk->sk_state != TCP_ESTABLISHED) {
117062306a36Sopenharmony_ci		/* Wait until the pipe gets to enabled state */
117162306a36Sopenharmony_cidisabled:
117262306a36Sopenharmony_ci		err = sk_stream_wait_connect(sk, &timeo);
117362306a36Sopenharmony_ci		if (err)
117462306a36Sopenharmony_ci			goto out;
117562306a36Sopenharmony_ci
117662306a36Sopenharmony_ci		if (sk->sk_state == TCP_CLOSE_WAIT) {
117762306a36Sopenharmony_ci			err = -ECONNRESET;
117862306a36Sopenharmony_ci			goto out;
117962306a36Sopenharmony_ci		}
118062306a36Sopenharmony_ci	}
118162306a36Sopenharmony_ci	BUG_ON(sk->sk_state != TCP_ESTABLISHED);
118262306a36Sopenharmony_ci
118362306a36Sopenharmony_ci	/* Wait until flow control allows TX */
118462306a36Sopenharmony_ci	done = atomic_read(&pn->tx_credits);
118562306a36Sopenharmony_ci	while (!done) {
118662306a36Sopenharmony_ci		DEFINE_WAIT_FUNC(wait, woken_wake_function);
118762306a36Sopenharmony_ci
118862306a36Sopenharmony_ci		if (!timeo) {
118962306a36Sopenharmony_ci			err = -EAGAIN;
119062306a36Sopenharmony_ci			goto out;
119162306a36Sopenharmony_ci		}
119262306a36Sopenharmony_ci		if (signal_pending(current)) {
119362306a36Sopenharmony_ci			err = sock_intr_errno(timeo);
119462306a36Sopenharmony_ci			goto out;
119562306a36Sopenharmony_ci		}
119662306a36Sopenharmony_ci
119762306a36Sopenharmony_ci		add_wait_queue(sk_sleep(sk), &wait);
119862306a36Sopenharmony_ci		done = sk_wait_event(sk, &timeo, atomic_read(&pn->tx_credits), &wait);
119962306a36Sopenharmony_ci		remove_wait_queue(sk_sleep(sk), &wait);
120062306a36Sopenharmony_ci
120162306a36Sopenharmony_ci		if (sk->sk_state != TCP_ESTABLISHED)
120262306a36Sopenharmony_ci			goto disabled;
120362306a36Sopenharmony_ci	}
120462306a36Sopenharmony_ci
120562306a36Sopenharmony_ci	err = pipe_skb_send(sk, skb);
120662306a36Sopenharmony_ci	if (err >= 0)
120762306a36Sopenharmony_ci		err = len; /* success! */
120862306a36Sopenharmony_ci	skb = NULL;
120962306a36Sopenharmony_ciout:
121062306a36Sopenharmony_ci	release_sock(sk);
121162306a36Sopenharmony_cioutfree:
121262306a36Sopenharmony_ci	kfree_skb(skb);
121362306a36Sopenharmony_ci	return err;
121462306a36Sopenharmony_ci}
121562306a36Sopenharmony_ci
121662306a36Sopenharmony_ciint pep_writeable(struct sock *sk)
121762306a36Sopenharmony_ci{
121862306a36Sopenharmony_ci	struct pep_sock *pn = pep_sk(sk);
121962306a36Sopenharmony_ci
122062306a36Sopenharmony_ci	return atomic_read(&pn->tx_credits);
122162306a36Sopenharmony_ci}
122262306a36Sopenharmony_ci
122362306a36Sopenharmony_ciint pep_write(struct sock *sk, struct sk_buff *skb)
122462306a36Sopenharmony_ci{
122562306a36Sopenharmony_ci	struct sk_buff *rskb, *fs;
122662306a36Sopenharmony_ci	int flen = 0;
122762306a36Sopenharmony_ci
122862306a36Sopenharmony_ci	if (pep_sk(sk)->aligned)
122962306a36Sopenharmony_ci		return pipe_skb_send(sk, skb);
123062306a36Sopenharmony_ci
123162306a36Sopenharmony_ci	rskb = alloc_skb(MAX_PNPIPE_HEADER, GFP_ATOMIC);
123262306a36Sopenharmony_ci	if (!rskb) {
123362306a36Sopenharmony_ci		kfree_skb(skb);
123462306a36Sopenharmony_ci		return -ENOMEM;
123562306a36Sopenharmony_ci	}
123662306a36Sopenharmony_ci	skb_shinfo(rskb)->frag_list = skb;
123762306a36Sopenharmony_ci	rskb->len += skb->len;
123862306a36Sopenharmony_ci	rskb->data_len += rskb->len;
123962306a36Sopenharmony_ci	rskb->truesize += rskb->len;
124062306a36Sopenharmony_ci
124162306a36Sopenharmony_ci	/* Avoid nested fragments */
124262306a36Sopenharmony_ci	skb_walk_frags(skb, fs)
124362306a36Sopenharmony_ci		flen += fs->len;
124462306a36Sopenharmony_ci	skb->next = skb_shinfo(skb)->frag_list;
124562306a36Sopenharmony_ci	skb_frag_list_init(skb);
124662306a36Sopenharmony_ci	skb->len -= flen;
124762306a36Sopenharmony_ci	skb->data_len -= flen;
124862306a36Sopenharmony_ci	skb->truesize -= flen;
124962306a36Sopenharmony_ci
125062306a36Sopenharmony_ci	skb_reserve(rskb, MAX_PHONET_HEADER + 3);
125162306a36Sopenharmony_ci	return pipe_skb_send(sk, rskb);
125262306a36Sopenharmony_ci}
125362306a36Sopenharmony_ci
125462306a36Sopenharmony_cistruct sk_buff *pep_read(struct sock *sk)
125562306a36Sopenharmony_ci{
125662306a36Sopenharmony_ci	struct sk_buff *skb = skb_dequeue(&sk->sk_receive_queue);
125762306a36Sopenharmony_ci
125862306a36Sopenharmony_ci	if (sk->sk_state == TCP_ESTABLISHED)
125962306a36Sopenharmony_ci		pipe_grant_credits(sk, GFP_ATOMIC);
126062306a36Sopenharmony_ci	return skb;
126162306a36Sopenharmony_ci}
126262306a36Sopenharmony_ci
126362306a36Sopenharmony_cistatic int pep_recvmsg(struct sock *sk, struct msghdr *msg, size_t len,
126462306a36Sopenharmony_ci		       int flags, int *addr_len)
126562306a36Sopenharmony_ci{
126662306a36Sopenharmony_ci	struct sk_buff *skb;
126762306a36Sopenharmony_ci	int err;
126862306a36Sopenharmony_ci
126962306a36Sopenharmony_ci	if (flags & ~(MSG_OOB|MSG_PEEK|MSG_TRUNC|MSG_DONTWAIT|MSG_WAITALL|
127062306a36Sopenharmony_ci			MSG_NOSIGNAL|MSG_CMSG_COMPAT))
127162306a36Sopenharmony_ci		return -EOPNOTSUPP;
127262306a36Sopenharmony_ci
127362306a36Sopenharmony_ci	if (unlikely(1 << sk->sk_state & (TCPF_LISTEN | TCPF_CLOSE)))
127462306a36Sopenharmony_ci		return -ENOTCONN;
127562306a36Sopenharmony_ci
127662306a36Sopenharmony_ci	if ((flags & MSG_OOB) || sock_flag(sk, SOCK_URGINLINE)) {
127762306a36Sopenharmony_ci		/* Dequeue and acknowledge control request */
127862306a36Sopenharmony_ci		struct pep_sock *pn = pep_sk(sk);
127962306a36Sopenharmony_ci
128062306a36Sopenharmony_ci		if (flags & MSG_PEEK)
128162306a36Sopenharmony_ci			return -EOPNOTSUPP;
128262306a36Sopenharmony_ci		skb = skb_dequeue(&pn->ctrlreq_queue);
128362306a36Sopenharmony_ci		if (skb) {
128462306a36Sopenharmony_ci			pep_ctrlreq_error(sk, skb, PN_PIPE_NO_ERROR,
128562306a36Sopenharmony_ci						GFP_KERNEL);
128662306a36Sopenharmony_ci			msg->msg_flags |= MSG_OOB;
128762306a36Sopenharmony_ci			goto copy;
128862306a36Sopenharmony_ci		}
128962306a36Sopenharmony_ci		if (flags & MSG_OOB)
129062306a36Sopenharmony_ci			return -EINVAL;
129162306a36Sopenharmony_ci	}
129262306a36Sopenharmony_ci
129362306a36Sopenharmony_ci	skb = skb_recv_datagram(sk, flags, &err);
129462306a36Sopenharmony_ci	lock_sock(sk);
129562306a36Sopenharmony_ci	if (skb == NULL) {
129662306a36Sopenharmony_ci		if (err == -ENOTCONN && sk->sk_state == TCP_CLOSE_WAIT)
129762306a36Sopenharmony_ci			err = -ECONNRESET;
129862306a36Sopenharmony_ci		release_sock(sk);
129962306a36Sopenharmony_ci		return err;
130062306a36Sopenharmony_ci	}
130162306a36Sopenharmony_ci
130262306a36Sopenharmony_ci	if (sk->sk_state == TCP_ESTABLISHED)
130362306a36Sopenharmony_ci		pipe_grant_credits(sk, GFP_KERNEL);
130462306a36Sopenharmony_ci	release_sock(sk);
130562306a36Sopenharmony_cicopy:
130662306a36Sopenharmony_ci	msg->msg_flags |= MSG_EOR;
130762306a36Sopenharmony_ci	if (skb->len > len)
130862306a36Sopenharmony_ci		msg->msg_flags |= MSG_TRUNC;
130962306a36Sopenharmony_ci	else
131062306a36Sopenharmony_ci		len = skb->len;
131162306a36Sopenharmony_ci
131262306a36Sopenharmony_ci	err = skb_copy_datagram_msg(skb, 0, msg, len);
131362306a36Sopenharmony_ci	if (!err)
131462306a36Sopenharmony_ci		err = (flags & MSG_TRUNC) ? skb->len : len;
131562306a36Sopenharmony_ci
131662306a36Sopenharmony_ci	skb_free_datagram(sk, skb);
131762306a36Sopenharmony_ci	return err;
131862306a36Sopenharmony_ci}
131962306a36Sopenharmony_ci
132062306a36Sopenharmony_cistatic void pep_sock_unhash(struct sock *sk)
132162306a36Sopenharmony_ci{
132262306a36Sopenharmony_ci	struct pep_sock *pn = pep_sk(sk);
132362306a36Sopenharmony_ci	struct sock *skparent = NULL;
132462306a36Sopenharmony_ci
132562306a36Sopenharmony_ci	lock_sock(sk);
132662306a36Sopenharmony_ci
132762306a36Sopenharmony_ci	if (pn->listener != NULL) {
132862306a36Sopenharmony_ci		skparent = pn->listener;
132962306a36Sopenharmony_ci		pn->listener = NULL;
133062306a36Sopenharmony_ci		release_sock(sk);
133162306a36Sopenharmony_ci
133262306a36Sopenharmony_ci		pn = pep_sk(skparent);
133362306a36Sopenharmony_ci		lock_sock(skparent);
133462306a36Sopenharmony_ci		sk_del_node_init(sk);
133562306a36Sopenharmony_ci		sk = skparent;
133662306a36Sopenharmony_ci	}
133762306a36Sopenharmony_ci
133862306a36Sopenharmony_ci	/* Unhash a listening sock only when it is closed
133962306a36Sopenharmony_ci	 * and all of its active connected pipes are closed. */
134062306a36Sopenharmony_ci	if (hlist_empty(&pn->hlist))
134162306a36Sopenharmony_ci		pn_sock_unhash(&pn->pn_sk.sk);
134262306a36Sopenharmony_ci	release_sock(sk);
134362306a36Sopenharmony_ci
134462306a36Sopenharmony_ci	if (skparent)
134562306a36Sopenharmony_ci		sock_put(skparent);
134662306a36Sopenharmony_ci}
134762306a36Sopenharmony_ci
134862306a36Sopenharmony_cistatic struct proto pep_proto = {
134962306a36Sopenharmony_ci	.close		= pep_sock_close,
135062306a36Sopenharmony_ci	.accept		= pep_sock_accept,
135162306a36Sopenharmony_ci	.connect	= pep_sock_connect,
135262306a36Sopenharmony_ci	.ioctl		= pep_ioctl,
135362306a36Sopenharmony_ci	.init		= pep_init,
135462306a36Sopenharmony_ci	.setsockopt	= pep_setsockopt,
135562306a36Sopenharmony_ci	.getsockopt	= pep_getsockopt,
135662306a36Sopenharmony_ci	.sendmsg	= pep_sendmsg,
135762306a36Sopenharmony_ci	.recvmsg	= pep_recvmsg,
135862306a36Sopenharmony_ci	.backlog_rcv	= pep_do_rcv,
135962306a36Sopenharmony_ci	.hash		= pn_sock_hash,
136062306a36Sopenharmony_ci	.unhash		= pep_sock_unhash,
136162306a36Sopenharmony_ci	.get_port	= pn_sock_get_port,
136262306a36Sopenharmony_ci	.obj_size	= sizeof(struct pep_sock),
136362306a36Sopenharmony_ci	.owner		= THIS_MODULE,
136462306a36Sopenharmony_ci	.name		= "PNPIPE",
136562306a36Sopenharmony_ci};
136662306a36Sopenharmony_ci
136762306a36Sopenharmony_cistatic const struct phonet_protocol pep_pn_proto = {
136862306a36Sopenharmony_ci	.ops		= &phonet_stream_ops,
136962306a36Sopenharmony_ci	.prot		= &pep_proto,
137062306a36Sopenharmony_ci	.sock_type	= SOCK_SEQPACKET,
137162306a36Sopenharmony_ci};
137262306a36Sopenharmony_ci
137362306a36Sopenharmony_cistatic int __init pep_register(void)
137462306a36Sopenharmony_ci{
137562306a36Sopenharmony_ci	return phonet_proto_register(PN_PROTO_PIPE, &pep_pn_proto);
137662306a36Sopenharmony_ci}
137762306a36Sopenharmony_ci
137862306a36Sopenharmony_cistatic void __exit pep_unregister(void)
137962306a36Sopenharmony_ci{
138062306a36Sopenharmony_ci	phonet_proto_unregister(PN_PROTO_PIPE, &pep_pn_proto);
138162306a36Sopenharmony_ci}
138262306a36Sopenharmony_ci
138362306a36Sopenharmony_cimodule_init(pep_register);
138462306a36Sopenharmony_cimodule_exit(pep_unregister);
138562306a36Sopenharmony_ciMODULE_AUTHOR("Remi Denis-Courmont, Nokia");
138662306a36Sopenharmony_ciMODULE_DESCRIPTION("Phonet pipe protocol");
138762306a36Sopenharmony_ciMODULE_LICENSE("GPL");
138862306a36Sopenharmony_ciMODULE_ALIAS_NET_PF_PROTO(PF_PHONET, PN_PROTO_PIPE);
1389