Blame view

net/phonet/pep.c 30.3 KB
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
  /*
   * File: pep.c
   *
   * Phonet pipe protocol end point socket
   *
   * Copyright (C) 2008 Nokia Corporation.
   *
   * Author: Rémi Denis-Courmont <remi.denis-courmont@nokia.com>
   *
   * This program is free software; you can redistribute it and/or
   * modify it under the terms of the GNU General Public License
   * version 2 as published by the Free Software Foundation.
   *
   * This program is distributed in the hope that it will be useful, but
   * WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   * General Public License for more details.
   *
   * You should have received a copy of the GNU General Public License
   * along with this program; if not, write to the Free Software
   * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
   * 02110-1301 USA
   */
  
  #include <linux/kernel.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
26
  #include <linux/slab.h>
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
27
28
29
30
31
32
  #include <linux/socket.h>
  #include <net/sock.h>
  #include <net/tcp_states.h>
  #include <asm/ioctls.h>
  
  #include <linux/phonet.h>
3a9a231d9   Paul Gortmaker   net: Fix files ex...
33
  #include <linux/module.h>
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
34
35
  #include <net/phonet/phonet.h>
  #include <net/phonet/pep.h>
02a47617c   Rémi Denis-Courmont   Phonet: implement...
36
  #include <net/phonet/gprs.h>
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
37
38
39
40
41
42
43
44
45
  
  /* sk_state values:
   * TCP_CLOSE		sock not in use yet
   * TCP_CLOSE_WAIT	disconnected pipe
   * TCP_LISTEN		listening pipe endpoint
   * TCP_SYN_RECV		connected pipe in disabled state
   * TCP_ESTABLISHED	connected pipe in enabled state
   *
   * pep_sock locking:
f7ae8d59f   Rémi Denis-Courmont   Phonet: allocate ...
46
   *  - sk_state, hlist: sock lock needed
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
47
48
49
50
51
52
   *  - listener: read only
   *  - pipe_handle: read only
   */
  
  #define CREDITS_MAX	10
  #define CREDITS_THR	7
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
  #define pep_sb_size(s) (((s) + 5) & ~3) /* 2-bytes head, 32-bits aligned */
  
  /* Get the next TLV sub-block. */
  static unsigned char *pep_get_sb(struct sk_buff *skb, u8 *ptype, u8 *plen,
  					void *buf)
  {
  	void *data = NULL;
  	struct {
  		u8 sb_type;
  		u8 sb_len;
  	} *ph, h;
  	int buflen = *plen;
  
  	ph = skb_header_pointer(skb, 0, 2, &h);
  	if (ph == NULL || ph->sb_len < 2 || !pskb_may_pull(skb, ph->sb_len))
  		return NULL;
  	ph->sb_len -= 2;
  	*ptype = ph->sb_type;
  	*plen = ph->sb_len;
  
  	if (buflen > ph->sb_len)
  		buflen = ph->sb_len;
  	data = skb_header_pointer(skb, 2, buflen, buf);
  	__skb_pull(skb, 2 + ph->sb_len);
  	return data;
  }
44c9ab16d   Rémi Denis-Courmont   Phonet: factor co...
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
  static struct sk_buff *pep_alloc_skb(struct sock *sk, const void *payload,
  					int len, gfp_t priority)
  {
  	struct sk_buff *skb = alloc_skb(MAX_PNPIPE_HEADER + len, priority);
  	if (!skb)
  		return NULL;
  	skb_set_owner_w(skb, sk);
  
  	skb_reserve(skb, MAX_PNPIPE_HEADER);
  	__skb_put(skb, len);
  	skb_copy_to_linear_data(skb, payload, len);
  	__skb_push(skb, sizeof(struct pnpipehdr));
  	skb_reset_transport_header(skb);
  	return skb;
  }
  
  static int pep_reply(struct sock *sk, struct sk_buff *oskb, u8 code,
  			const void *data, int len, gfp_t priority)
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
97
98
99
100
  {
  	const struct pnpipehdr *oph = pnp_hdr(oskb);
  	struct pnpipehdr *ph;
  	struct sk_buff *skb;
14ba8faeb   Rémi Denis-Courmont   Phonet: use socke...
101
  	struct sockaddr_pn peer;
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
102

44c9ab16d   Rémi Denis-Courmont   Phonet: factor co...
103
  	skb = pep_alloc_skb(sk, data, len, priority);
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
104
105
  	if (!skb)
  		return -ENOMEM;
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
106

9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
107
108
109
110
111
  	ph = pnp_hdr(skb);
  	ph->utid = oph->utid;
  	ph->message_id = oph->message_id + 1; /* REQ -> RESP */
  	ph->pipe_handle = oph->pipe_handle;
  	ph->error_code = code;
14ba8faeb   Rémi Denis-Courmont   Phonet: use socke...
112
113
  	pn_skb_get_src_sockaddr(oskb, &peer);
  	return pn_skb_send(sk, skb, &peer);
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
114
  }
44c9ab16d   Rémi Denis-Courmont   Phonet: factor co...
115
116
  static int pep_indicate(struct sock *sk, u8 id, u8 code,
  			const void *data, int len, gfp_t priority)
8d98efa84   Kumar Sanghvi   Phonet: Implement...
117
  {
44c9ab16d   Rémi Denis-Courmont   Phonet: factor co...
118
  	struct pep_sock *pn = pep_sk(sk);
8d98efa84   Kumar Sanghvi   Phonet: Implement...
119
120
  	struct pnpipehdr *ph;
  	struct sk_buff *skb;
8d98efa84   Kumar Sanghvi   Phonet: Implement...
121

44c9ab16d   Rémi Denis-Courmont   Phonet: factor co...
122
  	skb = pep_alloc_skb(sk, data, len, priority);
8d98efa84   Kumar Sanghvi   Phonet: Implement...
123
124
  	if (!skb)
  		return -ENOMEM;
8d98efa84   Kumar Sanghvi   Phonet: Implement...
125

8d98efa84   Kumar Sanghvi   Phonet: Implement...
126
  	ph = pnp_hdr(skb);
44c9ab16d   Rémi Denis-Courmont   Phonet: factor co...
127
128
  	ph->utid = 0;
  	ph->message_id = id;
b3d625538   Kumar Sanghvi   Phonet: 'connect'...
129
  	ph->pipe_handle = pn->pipe_handle;
44c9ab16d   Rémi Denis-Courmont   Phonet: factor co...
130
  	ph->data[0] = code;
14ba8faeb   Rémi Denis-Courmont   Phonet: use socke...
131
  	return pn_skb_send(sk, skb, NULL);
8d98efa84   Kumar Sanghvi   Phonet: Implement...
132
  }
44c9ab16d   Rémi Denis-Courmont   Phonet: factor co...
133
  #define PAD 0x00
44c9ab16d   Rémi Denis-Courmont   Phonet: factor co...
134
135
  static int pipe_handler_request(struct sock *sk, u8 id, u8 code,
  				const void *data, int len)
8d98efa84   Kumar Sanghvi   Phonet: Implement...
136
  {
44c9ab16d   Rémi Denis-Courmont   Phonet: factor co...
137
  	struct pep_sock *pn = pep_sk(sk);
8d98efa84   Kumar Sanghvi   Phonet: Implement...
138
139
  	struct pnpipehdr *ph;
  	struct sk_buff *skb;
8d98efa84   Kumar Sanghvi   Phonet: Implement...
140

44c9ab16d   Rémi Denis-Courmont   Phonet: factor co...
141
  	skb = pep_alloc_skb(sk, data, len, GFP_KERNEL);
8d98efa84   Kumar Sanghvi   Phonet: Implement...
142
143
  	if (!skb)
  		return -ENOMEM;
8d98efa84   Kumar Sanghvi   Phonet: Implement...
144

8d98efa84   Kumar Sanghvi   Phonet: Implement...
145
  	ph = pnp_hdr(skb);
44c9ab16d   Rémi Denis-Courmont   Phonet: factor co...
146
147
  	ph->utid = id; /* whatever */
  	ph->message_id = id;
b3d625538   Kumar Sanghvi   Phonet: 'connect'...
148
  	ph->pipe_handle = pn->pipe_handle;
44c9ab16d   Rémi Denis-Courmont   Phonet: factor co...
149
  	ph->data[0] = code;
14ba8faeb   Rémi Denis-Courmont   Phonet: use socke...
150
  	return pn_skb_send(sk, skb, NULL);
8d98efa84   Kumar Sanghvi   Phonet: Implement...
151
  }
44c9ab16d   Rémi Denis-Courmont   Phonet: factor co...
152
  static int pipe_handler_send_created_ind(struct sock *sk)
8d98efa84   Kumar Sanghvi   Phonet: Implement...
153
  {
b3d625538   Kumar Sanghvi   Phonet: 'connect'...
154
  	struct pep_sock *pn = pep_sk(sk);
44c9ab16d   Rémi Denis-Courmont   Phonet: factor co...
155
156
157
158
  	u8 data[4] = {
  		PN_PIPE_SB_NEGOTIATED_FC, pep_sb_size(2),
  		pn->tx_fc, pn->rx_fc,
  	};
8d98efa84   Kumar Sanghvi   Phonet: Implement...
159

44c9ab16d   Rémi Denis-Courmont   Phonet: factor co...
160
161
162
  	return pep_indicate(sk, PNS_PIPE_CREATED_IND, 1 /* sub-blocks */,
  				data, 4, GFP_ATOMIC);
  }
8d98efa84   Kumar Sanghvi   Phonet: Implement...
163

9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
  static int pep_accept_conn(struct sock *sk, struct sk_buff *skb)
  {
  	static const u8 data[20] = {
  		PAD, PAD, PAD, 2 /* sub-blocks */,
  		PN_PIPE_SB_REQUIRED_FC_TX, pep_sb_size(5), 3, PAD,
  			PN_MULTI_CREDIT_FLOW_CONTROL,
  			PN_ONE_CREDIT_FLOW_CONTROL,
  			PN_LEGACY_FLOW_CONTROL,
  			PAD,
  		PN_PIPE_SB_PREFERRED_FC_RX, pep_sb_size(5), 3, PAD,
  			PN_MULTI_CREDIT_FLOW_CONTROL,
  			PN_ONE_CREDIT_FLOW_CONTROL,
  			PN_LEGACY_FLOW_CONTROL,
  			PAD,
  	};
  
  	might_sleep();
  	return pep_reply(sk, skb, PN_PIPE_NO_ERROR, data, sizeof(data),
  				GFP_KERNEL);
  }
f7ae8d59f   Rémi Denis-Courmont   Phonet: allocate ...
184
185
  static int pep_reject_conn(struct sock *sk, struct sk_buff *skb, u8 code,
  				gfp_t priority)
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
186
187
188
  {
  	static const u8 data[4] = { PAD, PAD, PAD, 0 /* sub-blocks */ };
  	WARN_ON(code == PN_PIPE_NO_ERROR);
f7ae8d59f   Rémi Denis-Courmont   Phonet: allocate ...
189
  	return pep_reply(sk, skb, code, data, sizeof(data), priority);
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
190
191
192
193
  }
  
  /* Control requests are not sent by the pipe service and have a specific
   * message format. */
c41bd97f8   Rémi Denis-Courmont   Phonet: receive p...
194
195
  static int pep_ctrlreq_error(struct sock *sk, struct sk_buff *oskb, u8 code,
  				gfp_t priority)
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
196
197
198
199
200
  {
  	const struct pnpipehdr *oph = pnp_hdr(oskb);
  	struct sk_buff *skb;
  	struct pnpipehdr *ph;
  	struct sockaddr_pn dst;
44c9ab16d   Rémi Denis-Courmont   Phonet: factor co...
201
202
203
204
205
  	u8 data[4] = {
  		oph->data[0], /* PEP type */
  		code, /* error code, at an unusual offset */
  		PAD, PAD,
  	};
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
206

44c9ab16d   Rémi Denis-Courmont   Phonet: factor co...
207
  	skb = pep_alloc_skb(sk, data, 4, priority);
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
208
209
  	if (!skb)
  		return -ENOMEM;
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
210

44c9ab16d   Rémi Denis-Courmont   Phonet: factor co...
211
  	ph = pnp_hdr(skb);
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
212
213
214
215
  	ph->utid = oph->utid;
  	ph->message_id = PNS_PEP_CTRL_RESP;
  	ph->pipe_handle = oph->pipe_handle;
  	ph->data[0] = oph->data[1]; /* CTRL id */
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
216
217
218
219
220
221
222
  
  	pn_skb_get_src_sockaddr(oskb, &dst);
  	return pn_skb_send(sk, skb, &dst);
  }
  
  static int pipe_snd_status(struct sock *sk, u8 type, u8 status, gfp_t priority)
  {
44c9ab16d   Rémi Denis-Courmont   Phonet: factor co...
223
  	u8 data[4] = { type, PAD, PAD, status };
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
224

44c9ab16d   Rémi Denis-Courmont   Phonet: factor co...
225
226
  	return pep_indicate(sk, PNS_PEP_STATUS_IND, PN_PEP_TYPE_COMMON,
  				data, 4, priority);
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
227
228
229
230
  }
  
  /* Send our RX flow control information to the sender.
   * Socket must be locked. */
44c9ab16d   Rémi Denis-Courmont   Phonet: factor co...
231
  static void pipe_grant_credits(struct sock *sk, gfp_t priority)
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
232
233
234
235
236
237
238
239
240
  {
  	struct pep_sock *pn = pep_sk(sk);
  
  	BUG_ON(sk->sk_state != TCP_ESTABLISHED);
  
  	switch (pn->rx_fc) {
  	case PN_LEGACY_FLOW_CONTROL: /* TODO */
  		break;
  	case PN_ONE_CREDIT_FLOW_CONTROL:
44c9ab16d   Rémi Denis-Courmont   Phonet: factor co...
241
242
243
  		if (pipe_snd_status(sk, PN_PEP_IND_FLOW_CONTROL,
  					PEP_IND_READY, priority) == 0)
  			pn->rx_credits = 1;
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
244
245
246
247
248
249
  		break;
  	case PN_MULTI_CREDIT_FLOW_CONTROL:
  		if ((pn->rx_credits + CREDITS_THR) > CREDITS_MAX)
  			break;
  		if (pipe_snd_status(sk, PN_PEP_IND_ID_MCFC_GRANT_CREDITS,
  					CREDITS_MAX - pn->rx_credits,
44c9ab16d   Rémi Denis-Courmont   Phonet: factor co...
250
  					priority) == 0)
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
251
252
253
254
255
256
257
258
  			pn->rx_credits = CREDITS_MAX;
  		break;
  	}
  }
  
  static int pipe_rcv_status(struct sock *sk, struct sk_buff *skb)
  {
  	struct pep_sock *pn = pep_sk(sk);
a91e7d471   Kumar Sanghvi   Phonet: Correct h...
259
  	struct pnpipehdr *hdr;
be677730a   Rémi Denis-Courmont   Phonet: use atomi...
260
  	int wake = 0;
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
261
262
263
  
  	if (!pskb_may_pull(skb, sizeof(*hdr) + 4))
  		return -EINVAL;
a91e7d471   Kumar Sanghvi   Phonet: Correct h...
264
  	hdr = pnp_hdr(skb);
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
265
266
267
268
269
270
271
272
273
274
275
276
277
  	if (hdr->data[0] != PN_PEP_TYPE_COMMON) {
  		LIMIT_NETDEBUG(KERN_DEBUG"Phonet unknown PEP type: %u
  ",
  				(unsigned)hdr->data[0]);
  		return -EOPNOTSUPP;
  	}
  
  	switch (hdr->data[1]) {
  	case PN_PEP_IND_FLOW_CONTROL:
  		switch (pn->tx_fc) {
  		case PN_LEGACY_FLOW_CONTROL:
  			switch (hdr->data[4]) {
  			case PEP_IND_BUSY:
be677730a   Rémi Denis-Courmont   Phonet: use atomi...
278
  				atomic_set(&pn->tx_credits, 0);
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
279
280
  				break;
  			case PEP_IND_READY:
be677730a   Rémi Denis-Courmont   Phonet: use atomi...
281
  				atomic_set(&pn->tx_credits, wake = 1);
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
282
283
284
285
286
  				break;
  			}
  			break;
  		case PN_ONE_CREDIT_FLOW_CONTROL:
  			if (hdr->data[4] == PEP_IND_READY)
be677730a   Rémi Denis-Courmont   Phonet: use atomi...
287
  				atomic_set(&pn->tx_credits, wake = 1);
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
288
289
290
291
292
293
294
  			break;
  		}
  		break;
  
  	case PN_PEP_IND_ID_MCFC_GRANT_CREDITS:
  		if (pn->tx_fc != PN_MULTI_CREDIT_FLOW_CONTROL)
  			break;
be677730a   Rémi Denis-Courmont   Phonet: use atomi...
295
  		atomic_add(wake = hdr->data[4], &pn->tx_credits);
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
296
297
298
299
300
301
302
303
  		break;
  
  	default:
  		LIMIT_NETDEBUG(KERN_DEBUG"Phonet unknown PEP indication: %u
  ",
  				(unsigned)hdr->data[1]);
  		return -EOPNOTSUPP;
  	}
be677730a   Rémi Denis-Courmont   Phonet: use atomi...
304
  	if (wake)
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
  		sk->sk_write_space(sk);
  	return 0;
  }
  
  static int pipe_rcv_created(struct sock *sk, struct sk_buff *skb)
  {
  	struct pep_sock *pn = pep_sk(sk);
  	struct pnpipehdr *hdr = pnp_hdr(skb);
  	u8 n_sb = hdr->data[0];
  
  	pn->rx_fc = pn->tx_fc = PN_LEGACY_FLOW_CONTROL;
  	__skb_pull(skb, sizeof(*hdr));
  	while (n_sb > 0) {
  		u8 type, buf[2], len = sizeof(buf);
  		u8 *data = pep_get_sb(skb, &type, &len, buf);
  
  		if (data == NULL)
  			return -EINVAL;
  		switch (type) {
  		case PN_PIPE_SB_NEGOTIATED_FC:
  			if (len < 2 || (data[0] | data[1]) > 3)
  				break;
  			pn->tx_fc = data[0] & 3;
  			pn->rx_fc = data[1] & 3;
  			break;
  		}
  		n_sb--;
  	}
  	return 0;
  }
  
  /* Queue an skb to a connected sock.
   * Socket lock must be held. */
  static int pipe_do_rcv(struct sock *sk, struct sk_buff *skb)
  {
  	struct pep_sock *pn = pep_sk(sk);
  	struct pnpipehdr *hdr = pnp_hdr(skb);
c41bd97f8   Rémi Denis-Courmont   Phonet: receive p...
342
  	struct sk_buff_head *queue;
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
343
344
345
346
347
348
  	int err = 0;
  
  	BUG_ON(sk->sk_state == TCP_CLOSE_WAIT);
  
  	switch (hdr->message_id) {
  	case PNS_PEP_CONNECT_REQ:
f7ae8d59f   Rémi Denis-Courmont   Phonet: allocate ...
349
  		pep_reject_conn(sk, skb, PN_PIPE_ERR_PEP_IN_USE, GFP_ATOMIC);
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
  		break;
  
  	case PNS_PEP_DISCONNECT_REQ:
  		pep_reply(sk, skb, PN_PIPE_NO_ERROR, NULL, 0, GFP_ATOMIC);
  		sk->sk_state = TCP_CLOSE_WAIT;
  		if (!sock_flag(sk, SOCK_DEAD))
  			sk->sk_state_change(sk);
  		break;
  
  	case PNS_PEP_ENABLE_REQ:
  		/* Wait for PNS_PIPE_(ENABLED|REDIRECTED)_IND */
  		pep_reply(sk, skb, PN_PIPE_NO_ERROR, NULL, 0, GFP_ATOMIC);
  		break;
  
  	case PNS_PEP_RESET_REQ:
  		switch (hdr->state_after_reset) {
  		case PN_PIPE_DISABLE:
  			pn->init_enable = 0;
  			break;
  		case PN_PIPE_ENABLE:
  			pn->init_enable = 1;
  			break;
  		default: /* not allowed to send an error here!? */
  			err = -EINVAL;
  			goto out;
  		}
  		/* fall through */
  	case PNS_PEP_DISABLE_REQ:
be677730a   Rémi Denis-Courmont   Phonet: use atomi...
378
  		atomic_set(&pn->tx_credits, 0);
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
379
380
381
382
  		pep_reply(sk, skb, PN_PIPE_NO_ERROR, NULL, 0, GFP_ATOMIC);
  		break;
  
  	case PNS_PEP_CTRL_REQ:
2e2fb4b33   Rémi Denis-Courmont   Phonet: account f...
383
384
  		if (skb_queue_len(&pn->ctrlreq_queue) >= PNPIPE_CTRLREQ_MAX) {
  			atomic_inc(&sk->sk_drops);
c41bd97f8   Rémi Denis-Courmont   Phonet: receive p...
385
  			break;
2e2fb4b33   Rémi Denis-Courmont   Phonet: account f...
386
  		}
c41bd97f8   Rémi Denis-Courmont   Phonet: receive p...
387
388
389
  		__skb_pull(skb, 4);
  		queue = &pn->ctrlreq_queue;
  		goto queue;
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
390

fc6a11075   Rémi Denis-Courmont   Phonet: zero-copy...
391
392
393
  	case PNS_PIPE_ALIGNED_DATA:
  		__skb_pull(skb, 1);
  		/* fall through */
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
394
395
396
397
398
  	case PNS_PIPE_DATA:
  		__skb_pull(skb, 3); /* Pipe data header */
  		if (!pn_flow_safe(pn->rx_fc)) {
  			err = sock_queue_rcv_skb(sk, skb);
  			if (!err)
0ebbf3186   Rémi Denis-Courmont   Phonet: correct p...
399
400
  				return NET_RX_SUCCESS;
  			err = -ENOBUFS;
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
401
402
403
404
  			break;
  		}
  
  		if (pn->rx_credits == 0) {
2e2fb4b33   Rémi Denis-Courmont   Phonet: account f...
405
  			atomic_inc(&sk->sk_drops);
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
406
407
408
409
  			err = -ENOBUFS;
  			break;
  		}
  		pn->rx_credits--;
c41bd97f8   Rémi Denis-Courmont   Phonet: receive p...
410
411
  		queue = &sk->sk_receive_queue;
  		goto queue;
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
  
  	case PNS_PEP_STATUS_IND:
  		pipe_rcv_status(sk, skb);
  		break;
  
  	case PNS_PIPE_REDIRECTED_IND:
  		err = pipe_rcv_created(sk, skb);
  		break;
  
  	case PNS_PIPE_CREATED_IND:
  		err = pipe_rcv_created(sk, skb);
  		if (err)
  			break;
  		/* fall through */
  	case PNS_PIPE_RESET_IND:
  		if (!pn->init_enable)
  			break;
  		/* fall through */
  	case PNS_PIPE_ENABLED_IND:
  		if (!pn_flow_safe(pn->tx_fc)) {
be677730a   Rémi Denis-Courmont   Phonet: use atomi...
432
  			atomic_set(&pn->tx_credits, 1);
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
433
434
435
436
437
  			sk->sk_write_space(sk);
  		}
  		if (sk->sk_state == TCP_ESTABLISHED)
  			break; /* Nothing to do */
  		sk->sk_state = TCP_ESTABLISHED;
44c9ab16d   Rémi Denis-Courmont   Phonet: factor co...
438
  		pipe_grant_credits(sk, GFP_ATOMIC);
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
  		break;
  
  	case PNS_PIPE_DISABLED_IND:
  		sk->sk_state = TCP_SYN_RECV;
  		pn->rx_credits = 0;
  		break;
  
  	default:
  		LIMIT_NETDEBUG(KERN_DEBUG"Phonet unknown PEP message: %u
  ",
  				hdr->message_id);
  		err = -EINVAL;
  	}
  out:
  	kfree_skb(skb);
0ebbf3186   Rémi Denis-Courmont   Phonet: correct p...
454
  	return (err == -ENOBUFS) ? NET_RX_DROP : NET_RX_SUCCESS;
c41bd97f8   Rémi Denis-Courmont   Phonet: receive p...
455
456
457
458
459
460
461
462
  
  queue:
  	skb->dev = NULL;
  	skb_set_owner_r(skb, sk);
  	err = skb->len;
  	skb_queue_tail(queue, skb);
  	if (!sock_flag(sk, SOCK_DEAD))
  		sk->sk_data_ready(sk, err);
0ebbf3186   Rémi Denis-Courmont   Phonet: correct p...
463
  	return NET_RX_SUCCESS;
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
464
465
466
467
468
  }
  
  /* Destroy connected sock. */
  static void pipe_destruct(struct sock *sk)
  {
c41bd97f8   Rémi Denis-Courmont   Phonet: receive p...
469
  	struct pep_sock *pn = pep_sk(sk);
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
470
  	skb_queue_purge(&sk->sk_receive_queue);
c41bd97f8   Rémi Denis-Courmont   Phonet: receive p...
471
  	skb_queue_purge(&pn->ctrlreq_queue);
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
472
  }
8f44fcc72   Rémi Denis-Courmont   Phonet: fix flawe...
473
474
475
476
477
478
479
480
481
482
483
484
485
  static u8 pipe_negotiate_fc(const u8 *fcs, unsigned n)
  {
  	unsigned i;
  	u8 final_fc = PN_NO_FLOW_CONTROL;
  
  	for (i = 0; i < n; i++) {
  		u8 fc = fcs[i];
  
  		if (fc > final_fc && fc < PN_MAX_FLOW_CONTROL)
  			final_fc = fc;
  	}
  	return final_fc;
  }
b3d625538   Kumar Sanghvi   Phonet: 'connect'...
486
487
488
  static int pep_connresp_rcv(struct sock *sk, struct sk_buff *skb)
  {
  	struct pep_sock *pn = pep_sk(sk);
8f44fcc72   Rémi Denis-Courmont   Phonet: fix flawe...
489
490
491
492
493
494
495
  	struct pnpipehdr *hdr;
  	u8 n_sb;
  
  	if (!pskb_pull(skb, sizeof(*hdr) + 4))
  		return -EINVAL;
  
  	hdr = pnp_hdr(skb);
297edb600   Rémi Denis-Courmont   Phonet: support a...
496
497
  	if (hdr->error_code != PN_PIPE_NO_ERROR)
  		return -ECONNREFUSED;
8f44fcc72   Rémi Denis-Courmont   Phonet: fix flawe...
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
  
  	/* Parse sub-blocks */
  	n_sb = hdr->data[4];
  	while (n_sb > 0) {
  		u8 type, buf[6], len = sizeof(buf);
  		const u8 *data = pep_get_sb(skb, &type, &len, buf);
  
  		if (data == NULL)
  			return -EINVAL;
  
  		switch (type) {
  		case PN_PIPE_SB_REQUIRED_FC_TX:
  			if (len < 2 || len < data[0])
  				break;
  			pn->tx_fc = pipe_negotiate_fc(data + 2, len - 2);
  			break;
  
  		case PN_PIPE_SB_PREFERRED_FC_RX:
  			if (len < 2 || len < data[0])
  				break;
  			pn->rx_fc = pipe_negotiate_fc(data + 2, len - 2);
  			break;
  
  		}
  		n_sb--;
  	}
b3d625538   Kumar Sanghvi   Phonet: 'connect'...
524

44c9ab16d   Rémi Denis-Courmont   Phonet: factor co...
525
  	return pipe_handler_send_created_ind(sk);
b3d625538   Kumar Sanghvi   Phonet: 'connect'...
526
  }
297edb600   Rémi Denis-Courmont   Phonet: support a...
527

bdb6e697b   Dinesh Kumar Sharma   Phonet: set the p...
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
  static int pep_enableresp_rcv(struct sock *sk, struct sk_buff *skb)
  {
  	struct pnpipehdr *hdr = pnp_hdr(skb);
  
  	if (hdr->error_code != PN_PIPE_NO_ERROR)
  		return -ECONNREFUSED;
  
  	return pep_indicate(sk, PNS_PIPE_ENABLED_IND, 0 /* sub-blocks */,
  		NULL, 0, GFP_ATOMIC);
  
  }
  
  static void pipe_start_flow_control(struct sock *sk)
  {
  	struct pep_sock *pn = pep_sk(sk);
  
  	if (!pn_flow_safe(pn->tx_fc)) {
  		atomic_set(&pn->tx_credits, 1);
  		sk->sk_write_space(sk);
  	}
  	pipe_grant_credits(sk, GFP_ATOMIC);
  }
297edb600   Rémi Denis-Courmont   Phonet: support a...
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
  /* Queue an skb to an actively connected sock.
   * Socket lock must be held. */
  static int pipe_handler_do_rcv(struct sock *sk, struct sk_buff *skb)
  {
  	struct pep_sock *pn = pep_sk(sk);
  	struct pnpipehdr *hdr = pnp_hdr(skb);
  	int err = NET_RX_SUCCESS;
  
  	switch (hdr->message_id) {
  	case PNS_PIPE_ALIGNED_DATA:
  		__skb_pull(skb, 1);
  		/* fall through */
  	case PNS_PIPE_DATA:
  		__skb_pull(skb, 3); /* Pipe data header */
  		if (!pn_flow_safe(pn->rx_fc)) {
  			err = sock_queue_rcv_skb(sk, skb);
  			if (!err)
  				return NET_RX_SUCCESS;
  			err = NET_RX_DROP;
  			break;
  		}
  
  		if (pn->rx_credits == 0) {
  			atomic_inc(&sk->sk_drops);
  			err = NET_RX_DROP;
  			break;
  		}
  		pn->rx_credits--;
  		skb->dev = NULL;
  		skb_set_owner_r(skb, sk);
  		err = skb->len;
  		skb_queue_tail(&sk->sk_receive_queue, skb);
  		if (!sock_flag(sk, SOCK_DEAD))
  			sk->sk_data_ready(sk, err);
  		return NET_RX_SUCCESS;
  
  	case PNS_PEP_CONNECT_RESP:
  		if (sk->sk_state != TCP_SYN_SENT)
  			break;
  		if (!sock_flag(sk, SOCK_DEAD))
  			sk->sk_state_change(sk);
  		if (pep_connresp_rcv(sk, skb)) {
  			sk->sk_state = TCP_CLOSE_WAIT;
  			break;
  		}
bdb6e697b   Dinesh Kumar Sharma   Phonet: set the p...
595
596
597
598
599
600
601
  		if (pn->init_enable == PN_PIPE_DISABLE)
  			sk->sk_state = TCP_SYN_RECV;
  		else {
  			sk->sk_state = TCP_ESTABLISHED;
  			pipe_start_flow_control(sk);
  		}
  		break;
297edb600   Rémi Denis-Courmont   Phonet: support a...
602

bdb6e697b   Dinesh Kumar Sharma   Phonet: set the p...
603
604
605
606
607
608
609
  	case PNS_PEP_ENABLE_RESP:
  		if (sk->sk_state != TCP_SYN_SENT)
  			break;
  
  		if (pep_enableresp_rcv(sk, skb)) {
  			sk->sk_state = TCP_CLOSE_WAIT;
  			break;
297edb600   Rémi Denis-Courmont   Phonet: support a...
610
  		}
bdb6e697b   Dinesh Kumar Sharma   Phonet: set the p...
611
612
613
  
  		sk->sk_state = TCP_ESTABLISHED;
  		pipe_start_flow_control(sk);
297edb600   Rémi Denis-Courmont   Phonet: support a...
614
615
616
617
618
619
620
621
622
623
624
625
626
  		break;
  
  	case PNS_PEP_DISCONNECT_RESP:
  		/* sock should already be dead, nothing to do */
  		break;
  
  	case PNS_PEP_STATUS_IND:
  		pipe_rcv_status(sk, skb);
  		break;
  	}
  	kfree_skb(skb);
  	return err;
  }
b3d625538   Kumar Sanghvi   Phonet: 'connect'...
627

9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
  /* Listening sock must be locked */
  static struct sock *pep_find_pipe(const struct hlist_head *hlist,
  					const struct sockaddr_pn *dst,
  					u8 pipe_handle)
  {
  	struct hlist_node *node;
  	struct sock *sknode;
  	u16 dobj = pn_sockaddr_get_object(dst);
  
  	sk_for_each(sknode, node, hlist) {
  		struct pep_sock *pnnode = pep_sk(sknode);
  
  		/* Ports match, but addresses might not: */
  		if (pnnode->pn_sk.sobject != dobj)
  			continue;
  		if (pnnode->pipe_handle != pipe_handle)
  			continue;
  		if (sknode->sk_state == TCP_CLOSE_WAIT)
  			continue;
  
  		sock_hold(sknode);
  		return sknode;
  	}
  	return NULL;
  }
  
  /*
   * Deliver an skb to a listening sock.
   * Socket lock must be held.
   * We then queue the skb to the right connected sock (if any).
   */
  static int pep_do_rcv(struct sock *sk, struct sk_buff *skb)
  {
  	struct pep_sock *pn = pep_sk(sk);
  	struct sock *sknode;
2ddc1ac1b   Rémi Denis-Courmont   Phonet: do not co...
663
  	struct pnpipehdr *hdr;
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
664
  	struct sockaddr_pn dst;
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
  	u8 pipe_handle;
  
  	if (!pskb_may_pull(skb, sizeof(*hdr)))
  		goto drop;
  
  	hdr = pnp_hdr(skb);
  	pipe_handle = hdr->pipe_handle;
  	if (pipe_handle == PN_PIPE_INVALID_HANDLE)
  		goto drop;
  
  	pn_skb_get_dst_sockaddr(skb, &dst);
  
  	/* Look for an existing pipe handle */
  	sknode = pep_find_pipe(&pn->hlist, &dst, pipe_handle);
  	if (sknode)
  		return sk_receive_skb(sknode, skb, 1);
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
681
682
  	switch (hdr->message_id) {
  	case PNS_PEP_CONNECT_REQ:
f7ae8d59f   Rémi Denis-Courmont   Phonet: allocate ...
683
684
685
686
687
688
689
690
691
692
  		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;
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
693
694
695
696
697
698
  
  	case PNS_PEP_DISCONNECT_REQ:
  		pep_reply(sk, skb, PN_PIPE_NO_ERROR, NULL, 0, GFP_ATOMIC);
  		break;
  
  	case PNS_PEP_CTRL_REQ:
c41bd97f8   Rémi Denis-Courmont   Phonet: receive p...
699
  		pep_ctrlreq_error(sk, skb, PN_PIPE_INVALID_HANDLE, GFP_ATOMIC);
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
700
701
702
703
704
705
  		break;
  
  	case PNS_PEP_RESET_REQ:
  	case PNS_PEP_ENABLE_REQ:
  	case PNS_PEP_DISABLE_REQ:
  		/* invalid handle is not even allowed here! */
0ebbf3186   Rémi Denis-Courmont   Phonet: correct p...
706
  		break;
297edb600   Rémi Denis-Courmont   Phonet: support a...
707
708
709
710
711
712
  
  	default:
  		if ((1 << sk->sk_state)
  				& ~(TCPF_CLOSE|TCPF_LISTEN|TCPF_CLOSE_WAIT))
  			/* actively connected socket */
  			return pipe_handler_do_rcv(sk, skb);
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
713
714
715
  	}
  drop:
  	kfree_skb(skb);
0ebbf3186   Rémi Denis-Courmont   Phonet: correct p...
716
  	return NET_RX_SUCCESS;
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
717
  }
6482f554e   Rémi Denis-Courmont   Phonet: remove da...
718
719
720
721
722
  static int pipe_do_remove(struct sock *sk)
  {
  	struct pep_sock *pn = pep_sk(sk);
  	struct pnpipehdr *ph;
  	struct sk_buff *skb;
44c9ab16d   Rémi Denis-Courmont   Phonet: factor co...
723
  	skb = pep_alloc_skb(sk, NULL, 0, GFP_KERNEL);
6482f554e   Rémi Denis-Courmont   Phonet: remove da...
724
725
  	if (!skb)
  		return -ENOMEM;
6482f554e   Rémi Denis-Courmont   Phonet: remove da...
726
727
728
729
730
  	ph = pnp_hdr(skb);
  	ph->utid = 0;
  	ph->message_id = PNS_PIPE_REMOVE_REQ;
  	ph->pipe_handle = pn->pipe_handle;
  	ph->data[0] = PAD;
14ba8faeb   Rémi Denis-Courmont   Phonet: use socke...
731
  	return pn_skb_send(sk, skb, NULL);
6482f554e   Rémi Denis-Courmont   Phonet: remove da...
732
  }
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
733
734
735
736
  /* associated socket ceases to exist */
  static void pep_sock_close(struct sock *sk, long timeout)
  {
  	struct pep_sock *pn = pep_sk(sk);
02a47617c   Rémi Denis-Courmont   Phonet: implement...
737
  	int ifindex = 0;
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
738

e513480e2   Rémi Denis-Courmont   Phonet: fix poten...
739
  	sock_hold(sk); /* keep a reference after sk_common_release() */
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
740
741
742
  	sk_common_release(sk);
  
  	lock_sock(sk);
f7ae8d59f   Rémi Denis-Courmont   Phonet: allocate ...
743
  	if ((1 << sk->sk_state) & (TCPF_SYN_RECV|TCPF_ESTABLISHED)) {
297edb600   Rémi Denis-Courmont   Phonet: support a...
744
745
746
747
748
749
  		if (sk->sk_backlog_rcv == pipe_do_rcv)
  			/* Forcefully remove dangling Phonet pipe */
  			pipe_do_remove(sk);
  		else
  			pipe_handler_request(sk, PNS_PEP_DISCONNECT_REQ, PAD,
  						NULL, 0);
2feb61816   Rémi Denis-Courmont   Phonet: remove re...
750
  	}
f7ae8d59f   Rémi Denis-Courmont   Phonet: allocate ...
751
  	sk->sk_state = TCP_CLOSE;
b3d625538   Kumar Sanghvi   Phonet: 'connect'...
752

02a47617c   Rémi Denis-Courmont   Phonet: implement...
753
754
  	ifindex = pn->ifindex;
  	pn->ifindex = 0;
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
755
  	release_sock(sk);
02a47617c   Rémi Denis-Courmont   Phonet: implement...
756
757
758
  
  	if (ifindex)
  		gprs_detach(sk);
e513480e2   Rémi Denis-Courmont   Phonet: fix poten...
759
  	sock_put(sk);
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
760
  }
f7ae8d59f   Rémi Denis-Courmont   Phonet: allocate ...
761
  static struct sock *pep_sock_accept(struct sock *sk, int flags, int *errp)
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
762
  {
f7ae8d59f   Rémi Denis-Courmont   Phonet: allocate ...
763
764
765
766
767
768
769
770
771
  	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;
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
772

f7ae8d59f   Rémi Denis-Courmont   Phonet: allocate ...
773
774
775
  	skb = skb_recv_datagram(sk, 0, flags & O_NONBLOCK, errp);
  	if (!skb)
  		return NULL;
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
776

f7ae8d59f   Rémi Denis-Courmont   Phonet: allocate ...
777
778
779
780
  	lock_sock(sk);
  	if (sk->sk_state != TCP_LISTEN) {
  		err = -EINVAL;
  		goto drop;
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
781
  	}
f7ae8d59f   Rémi Denis-Courmont   Phonet: allocate ...
782
  	sk_acceptq_removed(sk);
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
783

f7ae8d59f   Rémi Denis-Courmont   Phonet: allocate ...
784
785
786
  	err = -EPROTO;
  	if (!pskb_may_pull(skb, sizeof(*hdr) + 4))
  		goto drop;
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
787

f7ae8d59f   Rémi Denis-Courmont   Phonet: allocate ...
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
  	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;
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
803

f7ae8d59f   Rémi Denis-Courmont   Phonet: allocate ...
804
805
806
807
808
  	/* 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);
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
809

f7ae8d59f   Rémi Denis-Courmont   Phonet: allocate ...
810
811
812
813
814
815
816
817
818
819
820
821
822
823
  		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--;
  	}
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
824

f7ae8d59f   Rémi Denis-Courmont   Phonet: allocate ...
825
826
827
828
  	/* Check for duplicate pipe handle */
  	newsk = pep_find_pipe(&pn->hlist, &dst, pipe_handle);
  	if (unlikely(newsk)) {
  		__sock_put(newsk);
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
829
  		newsk = NULL;
f7ae8d59f   Rémi Denis-Courmont   Phonet: allocate ...
830
831
  		pep_reject_conn(sk, skb, PN_PIPE_ERR_PEP_IN_USE, GFP_KERNEL);
  		goto drop;
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
832
  	}
f7ae8d59f   Rémi Denis-Courmont   Phonet: allocate ...
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
  	/* 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;
  	}
  
  	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);
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
853
  	sock_hold(sk);
f7ae8d59f   Rémi Denis-Courmont   Phonet: allocate ...
854
855
856
857
858
859
860
861
862
863
  	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;
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
864

f7ae8d59f   Rémi Denis-Courmont   Phonet: allocate ...
865
866
867
868
869
870
  	err = pep_accept_conn(newsk, skb);
  	if (err) {
  		sock_put(newsk);
  		newsk = NULL;
  		goto drop;
  	}
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
871
  	sk_add_node(newsk, &pn->hlist);
f7ae8d59f   Rémi Denis-Courmont   Phonet: allocate ...
872
  drop:
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
873
  	release_sock(sk);
f7ae8d59f   Rémi Denis-Courmont   Phonet: allocate ...
874
  	kfree_skb(skb);
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
875
876
877
  	*errp = err;
  	return newsk;
  }
b3d625538   Kumar Sanghvi   Phonet: 'connect'...
878
879
880
  static int pep_sock_connect(struct sock *sk, struct sockaddr *addr, int len)
  {
  	struct pep_sock *pn = pep_sk(sk);
297edb600   Rémi Denis-Courmont   Phonet: support a...
881
  	int err;
44c9ab16d   Rémi Denis-Courmont   Phonet: factor co...
882
  	u8 data[4] = { 0 /* sub-blocks */, PAD, PAD, PAD };
b3d625538   Kumar Sanghvi   Phonet: 'connect'...
883

bdb6e697b   Dinesh Kumar Sharma   Phonet: set the p...
884
885
  	if (pn->pipe_handle == PN_PIPE_INVALID_HANDLE)
  		pn->pipe_handle = 1; /* anything but INVALID_HANDLE */
297edb600   Rémi Denis-Courmont   Phonet: support a...
886
  	err = pipe_handler_request(sk, PNS_PEP_CONNECT_REQ,
bdb6e697b   Dinesh Kumar Sharma   Phonet: set the p...
887
  				pn->init_enable, data, 4);
297edb600   Rémi Denis-Courmont   Phonet: support a...
888
889
890
891
  	if (err) {
  		pn->pipe_handle = PN_PIPE_INVALID_HANDLE;
  		return err;
  	}
bdb6e697b   Dinesh Kumar Sharma   Phonet: set the p...
892

297edb600   Rémi Denis-Courmont   Phonet: support a...
893
  	sk->sk_state = TCP_SYN_SENT;
bdb6e697b   Dinesh Kumar Sharma   Phonet: set the p...
894
895
896
897
898
899
900
901
902
903
904
905
906
907
  
  	return 0;
  }
  
  static int pep_sock_enable(struct sock *sk, struct sockaddr *addr, int len)
  {
  	int err;
  
  	err = pipe_handler_request(sk, PNS_PEP_ENABLE_REQ, PAD,
  				NULL, 0);
  	if (err)
  		return err;
  
  	sk->sk_state = TCP_SYN_SENT;
297edb600   Rémi Denis-Courmont   Phonet: support a...
908
  	return 0;
b3d625538   Kumar Sanghvi   Phonet: 'connect'...
909
  }
b3d625538   Kumar Sanghvi   Phonet: 'connect'...
910

9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
911
912
  static int pep_ioctl(struct sock *sk, int cmd, unsigned long arg)
  {
c41bd97f8   Rémi Denis-Courmont   Phonet: receive p...
913
  	struct pep_sock *pn = pep_sk(sk);
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
914
  	int answ;
bdb6e697b   Dinesh Kumar Sharma   Phonet: set the p...
915
  	int ret = -ENOIOCTLCMD;
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
916
917
918
  
  	switch (cmd) {
  	case SIOCINQ:
bdb6e697b   Dinesh Kumar Sharma   Phonet: set the p...
919
920
921
922
  		if (sk->sk_state == TCP_LISTEN) {
  			ret = -EINVAL;
  			break;
  		}
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
923
924
  
  		lock_sock(sk);
f64f9e719   Joe Perches   net: Move && and ...
925
926
  		if (sock_flag(sk, SOCK_URGINLINE) &&
  		    !skb_queue_empty(&pn->ctrlreq_queue))
c41bd97f8   Rémi Denis-Courmont   Phonet: receive p...
927
928
  			answ = skb_peek(&pn->ctrlreq_queue)->len;
  		else if (!skb_queue_empty(&sk->sk_receive_queue))
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
929
930
931
932
  			answ = skb_peek(&sk->sk_receive_queue)->len;
  		else
  			answ = 0;
  		release_sock(sk);
bdb6e697b   Dinesh Kumar Sharma   Phonet: set the p...
933
934
935
936
937
938
939
940
941
942
943
944
945
  		ret = put_user(answ, (int __user *)arg);
  		break;
  
  	case SIOCPNENABLEPIPE:
  		lock_sock(sk);
  		if (sk->sk_state == TCP_SYN_SENT)
  			ret =  -EBUSY;
  		else if (sk->sk_state == TCP_ESTABLISHED)
  			ret = -EISCONN;
  		else
  			ret = pep_sock_enable(sk, NULL, 0);
  		release_sock(sk);
  		break;
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
946
  	}
bdb6e697b   Dinesh Kumar Sharma   Phonet: set the p...
947
  	return ret;
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
948
949
950
951
952
  }
  
  static int pep_init(struct sock *sk)
  {
  	struct pep_sock *pn = pep_sk(sk);
f7ae8d59f   Rémi Denis-Courmont   Phonet: allocate ...
953
  	sk->sk_destruct = pipe_destruct;
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
954
  	INIT_HLIST_HEAD(&pn->hlist);
297edb600   Rémi Denis-Courmont   Phonet: support a...
955
  	pn->listener = NULL;
c41bd97f8   Rémi Denis-Courmont   Phonet: receive p...
956
  	skb_queue_head_init(&pn->ctrlreq_queue);
297edb600   Rémi Denis-Courmont   Phonet: support a...
957
958
959
  	atomic_set(&pn->tx_credits, 0);
  	pn->ifindex = 0;
  	pn->peer_type = 0;
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
960
  	pn->pipe_handle = PN_PIPE_INVALID_HANDLE;
297edb600   Rémi Denis-Courmont   Phonet: support a...
961
962
963
964
  	pn->rx_credits = 0;
  	pn->rx_fc = pn->tx_fc = PN_LEGACY_FLOW_CONTROL;
  	pn->init_enable = 1;
  	pn->aligned = 0;
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
965
966
  	return 0;
  }
02a47617c   Rémi Denis-Courmont   Phonet: implement...
967
  static int pep_setsockopt(struct sock *sk, int level, int optname,
b7058842c   David S. Miller   net: Make setsock...
968
  				char __user *optval, unsigned int optlen)
02a47617c   Rémi Denis-Courmont   Phonet: implement...
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
  {
  	struct pep_sock *pn = pep_sk(sk);
  	int val = 0, err = 0;
  
  	if (level != SOL_PNPIPE)
  		return -ENOPROTOOPT;
  	if (optlen >= sizeof(int)) {
  		if (get_user(val, (int __user *) optval))
  			return -EFAULT;
  	}
  
  	lock_sock(sk);
  	switch (optname) {
  	case PNPIPE_ENCAP:
  		if (val && val != PNPIPE_ENCAP_IP) {
  			err = -EINVAL;
  			break;
  		}
  		if (!pn->ifindex == !val)
  			break; /* Nothing to do! */
  		if (!capable(CAP_NET_ADMIN)) {
  			err = -EPERM;
  			break;
  		}
  		if (val) {
  			release_sock(sk);
  			err = gprs_attach(sk);
  			if (err > 0) {
  				pn->ifindex = err;
  				err = 0;
  			}
  		} else {
  			pn->ifindex = 0;
  			release_sock(sk);
  			gprs_detach(sk);
  			err = 0;
  		}
  		goto out_norel;
03789f267   Rémi Denis-Courmont   Phonet: cleanup p...
1007

bdb6e697b   Dinesh Kumar Sharma   Phonet: set the p...
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
  	case PNPIPE_HANDLE:
  		if ((sk->sk_state == TCP_CLOSE) &&
  			(val >= 0) && (val < PN_PIPE_INVALID_HANDLE))
  			pn->pipe_handle = val;
  		else
  			err = -EINVAL;
  		break;
  
  	case PNPIPE_INITSTATE:
  		pn->init_enable = !!val;
  		break;
02a47617c   Rémi Denis-Courmont   Phonet: implement...
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
  	default:
  		err = -ENOPROTOOPT;
  	}
  	release_sock(sk);
  
  out_norel:
  	return err;
  }
  
  static int pep_getsockopt(struct sock *sk, int level, int optname,
  				char __user *optval, int __user *optlen)
  {
  	struct pep_sock *pn = pep_sk(sk);
  	int len, val;
  
  	if (level != SOL_PNPIPE)
  		return -ENOPROTOOPT;
  	if (get_user(len, optlen))
  		return -EFAULT;
  
  	switch (optname) {
  	case PNPIPE_ENCAP:
  		val = pn->ifindex ? PNPIPE_ENCAP_IP : PNPIPE_ENCAP_NONE;
  		break;
8d98efa84   Kumar Sanghvi   Phonet: Implement...
1043

03789f267   Rémi Denis-Courmont   Phonet: cleanup p...
1044
1045
1046
  	case PNPIPE_IFINDEX:
  		val = pn->ifindex;
  		break;
acaf7df61   Rémi Denis-Courmont   Phonet: provide p...
1047
1048
1049
1050
1051
  	case PNPIPE_HANDLE:
  		val = pn->pipe_handle;
  		if (val == PN_PIPE_INVALID_HANDLE)
  			return -EINVAL;
  		break;
bdb6e697b   Dinesh Kumar Sharma   Phonet: set the p...
1052
1053
1054
  	case PNPIPE_INITSTATE:
  		val = pn->init_enable;
  		break;
02a47617c   Rémi Denis-Courmont   Phonet: implement...
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
  	default:
  		return -ENOPROTOOPT;
  	}
  
  	len = min_t(unsigned int, sizeof(int), len);
  	if (put_user(len, optlen))
  		return -EFAULT;
  	if (put_user(val, (int __user *) optval))
  		return -EFAULT;
  	return 0;
  }
  
  static int pipe_skb_send(struct sock *sk, struct sk_buff *skb)
  {
  	struct pep_sock *pn = pep_sk(sk);
  	struct pnpipehdr *ph;
e1a5964f0   Rémi Denis-Courmont   Phonet: restore f...
1071
  	int err;
02a47617c   Rémi Denis-Courmont   Phonet: implement...
1072

be677730a   Rémi Denis-Courmont   Phonet: use atomi...
1073
1074
1075
1076
1077
  	if (pn_flow_safe(pn->tx_fc) &&
  	    !atomic_add_unless(&pn->tx_credits, -1, 0)) {
  		kfree_skb(skb);
  		return -ENOBUFS;
  	}
fea93ecef   Rémi Denis-Courmont   Phonet: zero-copy...
1078
  	skb_push(skb, 3 + pn->aligned);
02a47617c   Rémi Denis-Courmont   Phonet: implement...
1079
1080
1081
  	skb_reset_transport_header(skb);
  	ph = pnp_hdr(skb);
  	ph->utid = 0;
fea93ecef   Rémi Denis-Courmont   Phonet: zero-copy...
1082
1083
1084
1085
1086
  	if (pn->aligned) {
  		ph->message_id = PNS_PIPE_ALIGNED_DATA;
  		ph->data[0] = 0; /* padding */
  	} else
  		ph->message_id = PNS_PIPE_DATA;
02a47617c   Rémi Denis-Courmont   Phonet: implement...
1087
  	ph->pipe_handle = pn->pipe_handle;
14ba8faeb   Rémi Denis-Courmont   Phonet: use socke...
1088
  	err = pn_skb_send(sk, skb, NULL);
e1a5964f0   Rémi Denis-Courmont   Phonet: restore f...
1089
1090
1091
1092
  
  	if (err && pn_flow_safe(pn->tx_fc))
  		atomic_inc(&pn->tx_credits);
  	return err;
02a47617c   Rémi Denis-Courmont   Phonet: implement...
1093
  }
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
1094
1095
1096
1097
  static int pep_sendmsg(struct kiocb *iocb, struct sock *sk,
  			struct msghdr *msg, size_t len)
  {
  	struct pep_sock *pn = pep_sk(sk);
b1704374f   Rémi Denis-Courmont   Phonet: allocate ...
1098
  	struct sk_buff *skb;
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
1099
1100
1101
  	long timeo;
  	int flags = msg->msg_flags;
  	int err, done;
82ecbcb9c   Rémi Denis-Courmont   Phonet: reject un...
1102
1103
1104
  	if ((msg->msg_flags & ~(MSG_DONTWAIT|MSG_EOR|MSG_NOSIGNAL|
  				MSG_CMSG_COMPAT)) ||
  			!(msg->msg_flags & MSG_EOR))
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
1105
  		return -EOPNOTSUPP;
b1704374f   Rémi Denis-Courmont   Phonet: allocate ...
1106
1107
1108
  	skb = sock_alloc_send_skb(sk, MAX_PNPIPE_HEADER + len,
  					flags & MSG_DONTWAIT, &err);
  	if (!skb)
02ac3268a   Rémi Denis-Courmont   Phonet: correct s...
1109
  		return err;
b1704374f   Rémi Denis-Courmont   Phonet: allocate ...
1110

638be3445   Rémi Denis-Courmont   Phonet: fix align...
1111
  	skb_reserve(skb, MAX_PHONET_HEADER + 3 + pn->aligned);
b1704374f   Rémi Denis-Courmont   Phonet: allocate ...
1112
1113
1114
  	err = memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len);
  	if (err < 0)
  		goto outfree;
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
  	lock_sock(sk);
  	timeo = sock_sndtimeo(sk, flags & MSG_DONTWAIT);
  	if ((1 << sk->sk_state) & (TCPF_LISTEN|TCPF_CLOSE)) {
  		err = -ENOTCONN;
  		goto out;
  	}
  	if (sk->sk_state != TCP_ESTABLISHED) {
  		/* Wait until the pipe gets to enabled state */
  disabled:
  		err = sk_stream_wait_connect(sk, &timeo);
  		if (err)
  			goto out;
  
  		if (sk->sk_state == TCP_CLOSE_WAIT) {
  			err = -ECONNRESET;
  			goto out;
  		}
  	}
  	BUG_ON(sk->sk_state != TCP_ESTABLISHED);
  
  	/* Wait until flow control allows TX */
be677730a   Rémi Denis-Courmont   Phonet: use atomi...
1136
  	done = atomic_read(&pn->tx_credits);
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
  	while (!done) {
  		DEFINE_WAIT(wait);
  
  		if (!timeo) {
  			err = -EAGAIN;
  			goto out;
  		}
  		if (signal_pending(current)) {
  			err = sock_intr_errno(timeo);
  			goto out;
  		}
438154823   Eric Dumazet   net: sock_def_rea...
1148
  		prepare_to_wait(sk_sleep(sk), &wait,
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
1149
  				TASK_INTERRUPTIBLE);
be677730a   Rémi Denis-Courmont   Phonet: use atomi...
1150
  		done = sk_wait_event(sk, &timeo, atomic_read(&pn->tx_credits));
438154823   Eric Dumazet   net: sock_def_rea...
1151
  		finish_wait(sk_sleep(sk), &wait);
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
1152
1153
1154
1155
  
  		if (sk->sk_state != TCP_ESTABLISHED)
  			goto disabled;
  	}
02a47617c   Rémi Denis-Courmont   Phonet: implement...
1156
  	err = pipe_skb_send(sk, skb);
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
1157
1158
1159
1160
1161
  	if (err >= 0)
  		err = len; /* success! */
  	skb = NULL;
  out:
  	release_sock(sk);
b1704374f   Rémi Denis-Courmont   Phonet: allocate ...
1162
  outfree:
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
1163
1164
1165
  	kfree_skb(skb);
  	return err;
  }
02a47617c   Rémi Denis-Courmont   Phonet: implement...
1166
1167
1168
  int pep_writeable(struct sock *sk)
  {
  	struct pep_sock *pn = pep_sk(sk);
be677730a   Rémi Denis-Courmont   Phonet: use atomi...
1169
  	return atomic_read(&pn->tx_credits);
02a47617c   Rémi Denis-Courmont   Phonet: implement...
1170
1171
1172
1173
1174
1175
  }
  
  int pep_write(struct sock *sk, struct sk_buff *skb)
  {
  	struct sk_buff *rskb, *fs;
  	int flen = 0;
fea93ecef   Rémi Denis-Courmont   Phonet: zero-copy...
1176
1177
  	if (pep_sk(sk)->aligned)
  		return pipe_skb_send(sk, skb);
02a47617c   Rémi Denis-Courmont   Phonet: implement...
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
  	rskb = alloc_skb(MAX_PNPIPE_HEADER, GFP_ATOMIC);
  	if (!rskb) {
  		kfree_skb(skb);
  		return -ENOMEM;
  	}
  	skb_shinfo(rskb)->frag_list = skb;
  	rskb->len += skb->len;
  	rskb->data_len += rskb->len;
  	rskb->truesize += rskb->len;
  
  	/* Avoid nested fragments */
5c313e9a7   David S. Miller   phonet: Use frag ...
1189
  	skb_walk_frags(skb, fs)
02a47617c   Rémi Denis-Courmont   Phonet: implement...
1190
1191
  		flen += fs->len;
  	skb->next = skb_shinfo(skb)->frag_list;
5c313e9a7   David S. Miller   phonet: Use frag ...
1192
  	skb_frag_list_init(skb);
02a47617c   Rémi Denis-Courmont   Phonet: implement...
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
  	skb->len -= flen;
  	skb->data_len -= flen;
  	skb->truesize -= flen;
  
  	skb_reserve(rskb, MAX_PHONET_HEADER + 3);
  	return pipe_skb_send(sk, rskb);
  }
  
  struct sk_buff *pep_read(struct sock *sk)
  {
  	struct sk_buff *skb = skb_dequeue(&sk->sk_receive_queue);
  
  	if (sk->sk_state == TCP_ESTABLISHED)
44c9ab16d   Rémi Denis-Courmont   Phonet: factor co...
1206
  		pipe_grant_credits(sk, GFP_ATOMIC);
02a47617c   Rémi Denis-Courmont   Phonet: implement...
1207
1208
  	return skb;
  }
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
1209
1210
1211
1212
1213
1214
  static int pep_recvmsg(struct kiocb *iocb, struct sock *sk,
  			struct msghdr *msg, size_t len, int noblock,
  			int flags, int *addr_len)
  {
  	struct sk_buff *skb;
  	int err;
82ecbcb9c   Rémi Denis-Courmont   Phonet: reject un...
1215
1216
1217
  	if (flags & ~(MSG_OOB|MSG_PEEK|MSG_TRUNC|MSG_DONTWAIT|MSG_WAITALL|
  			MSG_NOSIGNAL|MSG_CMSG_COMPAT))
  		return -EOPNOTSUPP;
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
1218
1219
  	if (unlikely(1 << sk->sk_state & (TCPF_LISTEN | TCPF_CLOSE)))
  		return -ENOTCONN;
c41bd97f8   Rémi Denis-Courmont   Phonet: receive p...
1220
1221
1222
  	if ((flags & MSG_OOB) || sock_flag(sk, SOCK_URGINLINE)) {
  		/* Dequeue and acknowledge control request */
  		struct pep_sock *pn = pep_sk(sk);
82ecbcb9c   Rémi Denis-Courmont   Phonet: reject un...
1223
1224
  		if (flags & MSG_PEEK)
  			return -EOPNOTSUPP;
c41bd97f8   Rémi Denis-Courmont   Phonet: receive p...
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
  		skb = skb_dequeue(&pn->ctrlreq_queue);
  		if (skb) {
  			pep_ctrlreq_error(sk, skb, PN_PIPE_NO_ERROR,
  						GFP_KERNEL);
  			msg->msg_flags |= MSG_OOB;
  			goto copy;
  		}
  		if (flags & MSG_OOB)
  			return -EINVAL;
  	}
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
  	skb = skb_recv_datagram(sk, flags, noblock, &err);
  	lock_sock(sk);
  	if (skb == NULL) {
  		if (err == -ENOTCONN && sk->sk_state == TCP_CLOSE_WAIT)
  			err = -ECONNRESET;
  		release_sock(sk);
  		return err;
  	}
  
  	if (sk->sk_state == TCP_ESTABLISHED)
44c9ab16d   Rémi Denis-Courmont   Phonet: factor co...
1245
  		pipe_grant_credits(sk, GFP_KERNEL);
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
1246
  	release_sock(sk);
c41bd97f8   Rémi Denis-Courmont   Phonet: receive p...
1247
  copy:
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
1248
  	msg->msg_flags |= MSG_EOR;
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
  	if (skb->len > len)
  		msg->msg_flags |= MSG_TRUNC;
  	else
  		len = skb->len;
  
  	err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, len);
  	if (!err)
  		err = (flags & MSG_TRUNC) ? skb->len : len;
  
  	skb_free_datagram(sk, skb);
  	return err;
  }
  
  static void pep_sock_unhash(struct sock *sk)
  {
  	struct pep_sock *pn = pep_sk(sk);
  	struct sock *skparent = NULL;
  
  	lock_sock(sk);
b3d625538   Kumar Sanghvi   Phonet: 'connect'...
1268

297edb600   Rémi Denis-Courmont   Phonet: support a...
1269
  	if (pn->listener != NULL) {
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
1270
  		skparent = pn->listener;
297edb600   Rémi Denis-Courmont   Phonet: support a...
1271
  		pn->listener = NULL;
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
1272
  		release_sock(sk);
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
1273
  		pn = pep_sk(skparent);
7dfde179c   Rémi Denis-Courmont   Phonet: listening...
1274
1275
1276
  		lock_sock(skparent);
  		sk_del_node_init(sk);
  		sk = skparent;
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
1277
  	}
297edb600   Rémi Denis-Courmont   Phonet: support a...
1278

9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
  	/* Unhash a listening sock only when it is closed
  	 * and all of its active connected pipes are closed. */
  	if (hlist_empty(&pn->hlist))
  		pn_sock_unhash(&pn->pn_sk.sk);
  	release_sock(sk);
  
  	if (skparent)
  		sock_put(skparent);
  }
  
  static struct proto pep_proto = {
  	.close		= pep_sock_close,
  	.accept		= pep_sock_accept,
b3d625538   Kumar Sanghvi   Phonet: 'connect'...
1292
  	.connect	= pep_sock_connect,
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
1293
1294
  	.ioctl		= pep_ioctl,
  	.init		= pep_init,
02a47617c   Rémi Denis-Courmont   Phonet: implement...
1295
1296
  	.setsockopt	= pep_setsockopt,
  	.getsockopt	= pep_getsockopt,
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
  	.sendmsg	= pep_sendmsg,
  	.recvmsg	= pep_recvmsg,
  	.backlog_rcv	= pep_do_rcv,
  	.hash		= pn_sock_hash,
  	.unhash		= pep_sock_unhash,
  	.get_port	= pn_sock_get_port,
  	.obj_size	= sizeof(struct pep_sock),
  	.owner		= THIS_MODULE,
  	.name		= "PNPIPE",
  };
  
  static struct phonet_protocol pep_pn_proto = {
  	.ops		= &phonet_stream_ops,
  	.prot		= &pep_proto,
  	.sock_type	= SOCK_SEQPACKET,
  };
  
  static int __init pep_register(void)
  {
  	return phonet_proto_register(PN_PROTO_PIPE, &pep_pn_proto);
  }
  
  static void __exit pep_unregister(void)
  {
  	phonet_proto_unregister(PN_PROTO_PIPE, &pep_pn_proto);
  }
  
  module_init(pep_register);
  module_exit(pep_unregister);
  MODULE_AUTHOR("Remi Denis-Courmont, Nokia");
  MODULE_DESCRIPTION("Phonet pipe protocol");
  MODULE_LICENSE("GPL");
  MODULE_ALIAS_NET_PF_PROTO(PF_PHONET, PN_PROTO_PIPE);