blob: 671effb4ea15bd0c31ce94059227b832f8217a2b [file] [log] [blame]
Rémi Denis-Courmont96414582008-10-05 11:15:13 -07001/*
2 * File: pep.c
3 *
4 * Phonet pipe protocol end point socket
5 *
6 * Copyright (C) 2008 Nokia Corporation.
7 *
8 * Author: Rémi Denis-Courmont <remi.denis-courmont@nokia.com>
9 *
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU General Public License
12 * version 2 as published by the Free Software Foundation.
13 *
14 * This program is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
22 * 02110-1301 USA
23 */
24
25#include <linux/kernel.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090026#include <linux/slab.h>
Rémi Denis-Courmont96414582008-10-05 11:15:13 -070027#include <linux/socket.h>
28#include <net/sock.h>
29#include <net/tcp_states.h>
30#include <asm/ioctls.h>
31
32#include <linux/phonet.h>
33#include <net/phonet/phonet.h>
34#include <net/phonet/pep.h>
Rémi Denis-Courmont02a47612008-10-05 11:16:16 -070035#include <net/phonet/gprs.h>
Rémi Denis-Courmont96414582008-10-05 11:15:13 -070036
37/* sk_state values:
38 * TCP_CLOSE sock not in use yet
39 * TCP_CLOSE_WAIT disconnected pipe
40 * TCP_LISTEN listening pipe endpoint
41 * TCP_SYN_RECV connected pipe in disabled state
42 * TCP_ESTABLISHED connected pipe in enabled state
43 *
44 * pep_sock locking:
Rémi Denis-Courmontf7ae8d52011-03-08 22:44:10 +000045 * - sk_state, hlist: sock lock needed
Rémi Denis-Courmont96414582008-10-05 11:15:13 -070046 * - listener: read only
47 * - pipe_handle: read only
48 */
49
50#define CREDITS_MAX 10
51#define CREDITS_THR 7
52
Rémi Denis-Courmont96414582008-10-05 11:15:13 -070053#define pep_sb_size(s) (((s) + 5) & ~3) /* 2-bytes head, 32-bits aligned */
54
55/* Get the next TLV sub-block. */
56static unsigned char *pep_get_sb(struct sk_buff *skb, u8 *ptype, u8 *plen,
57 void *buf)
58{
59 void *data = NULL;
60 struct {
61 u8 sb_type;
62 u8 sb_len;
63 } *ph, h;
64 int buflen = *plen;
65
66 ph = skb_header_pointer(skb, 0, 2, &h);
67 if (ph == NULL || ph->sb_len < 2 || !pskb_may_pull(skb, ph->sb_len))
68 return NULL;
69 ph->sb_len -= 2;
70 *ptype = ph->sb_type;
71 *plen = ph->sb_len;
72
73 if (buflen > ph->sb_len)
74 buflen = ph->sb_len;
75 data = skb_header_pointer(skb, 2, buflen, buf);
76 __skb_pull(skb, 2 + ph->sb_len);
77 return data;
78}
79
Rémi Denis-Courmont44c9ab12011-03-08 22:44:09 +000080static struct sk_buff *pep_alloc_skb(struct sock *sk, const void *payload,
81 int len, gfp_t priority)
82{
83 struct sk_buff *skb = alloc_skb(MAX_PNPIPE_HEADER + len, priority);
84 if (!skb)
85 return NULL;
86 skb_set_owner_w(skb, sk);
87
88 skb_reserve(skb, MAX_PNPIPE_HEADER);
89 __skb_put(skb, len);
90 skb_copy_to_linear_data(skb, payload, len);
91 __skb_push(skb, sizeof(struct pnpipehdr));
92 skb_reset_transport_header(skb);
93 return skb;
94}
95
96static int pep_reply(struct sock *sk, struct sk_buff *oskb, u8 code,
97 const void *data, int len, gfp_t priority)
Rémi Denis-Courmont96414582008-10-05 11:15:13 -070098{
99 const struct pnpipehdr *oph = pnp_hdr(oskb);
100 struct pnpipehdr *ph;
101 struct sk_buff *skb;
Rémi Denis-Courmont14ba8fa2011-02-24 23:14:58 +0000102 struct sockaddr_pn peer;
Rémi Denis-Courmont96414582008-10-05 11:15:13 -0700103
Rémi Denis-Courmont44c9ab12011-03-08 22:44:09 +0000104 skb = pep_alloc_skb(sk, data, len, priority);
Rémi Denis-Courmont96414582008-10-05 11:15:13 -0700105 if (!skb)
106 return -ENOMEM;
Rémi Denis-Courmont96414582008-10-05 11:15:13 -0700107
Rémi Denis-Courmont96414582008-10-05 11:15:13 -0700108 ph = pnp_hdr(skb);
109 ph->utid = oph->utid;
110 ph->message_id = oph->message_id + 1; /* REQ -> RESP */
111 ph->pipe_handle = oph->pipe_handle;
112 ph->error_code = code;
113
Rémi Denis-Courmont14ba8fa2011-02-24 23:14:58 +0000114 pn_skb_get_src_sockaddr(oskb, &peer);
115 return pn_skb_send(sk, skb, &peer);
Rémi Denis-Courmont96414582008-10-05 11:15:13 -0700116}
117
Rémi Denis-Courmont44c9ab12011-03-08 22:44:09 +0000118static int pep_indicate(struct sock *sk, u8 id, u8 code,
119 const void *data, int len, gfp_t priority)
120{
121 struct pep_sock *pn = pep_sk(sk);
122 struct pnpipehdr *ph;
123 struct sk_buff *skb;
124
125 skb = pep_alloc_skb(sk, data, len, priority);
126 if (!skb)
127 return -ENOMEM;
128
129 ph = pnp_hdr(skb);
130 ph->utid = 0;
131 ph->message_id = id;
132 ph->pipe_handle = pn->pipe_handle;
133 ph->data[0] = code;
134 return pn_skb_send(sk, skb, NULL);
135}
136
Rémi Denis-Courmont96414582008-10-05 11:15:13 -0700137#define PAD 0x00
Kumar Sanghvi8d98efa2010-09-26 19:07:59 +0000138
Rémi Denis-Courmont44c9ab12011-03-08 22:44:09 +0000139static int pipe_handler_request(struct sock *sk, u8 id, u8 code,
140 const void *data, int len)
Kumar Sanghvi8d98efa2010-09-26 19:07:59 +0000141{
Rémi Denis-Courmont44c9ab12011-03-08 22:44:09 +0000142 struct pep_sock *pn = pep_sk(sk);
Kumar Sanghvi8d98efa2010-09-26 19:07:59 +0000143 struct pnpipehdr *ph;
144 struct sk_buff *skb;
Kumar Sanghvi8d98efa2010-09-26 19:07:59 +0000145
Rémi Denis-Courmont44c9ab12011-03-08 22:44:09 +0000146 skb = pep_alloc_skb(sk, data, len, GFP_KERNEL);
Kumar Sanghvi8d98efa2010-09-26 19:07:59 +0000147 if (!skb)
148 return -ENOMEM;
Kumar Sanghvi8d98efa2010-09-26 19:07:59 +0000149
Kumar Sanghvi8d98efa2010-09-26 19:07:59 +0000150 ph = pnp_hdr(skb);
Rémi Denis-Courmont44c9ab12011-03-08 22:44:09 +0000151 ph->utid = id; /* whatever */
152 ph->message_id = id;
Kumar Sanghvib3d62552010-10-12 20:14:43 +0000153 ph->pipe_handle = pn->pipe_handle;
Rémi Denis-Courmont44c9ab12011-03-08 22:44:09 +0000154 ph->data[0] = code;
Rémi Denis-Courmont14ba8fa2011-02-24 23:14:58 +0000155 return pn_skb_send(sk, skb, NULL);
Kumar Sanghvi8d98efa2010-09-26 19:07:59 +0000156}
157
Rémi Denis-Courmont44c9ab12011-03-08 22:44:09 +0000158static int pipe_handler_send_created_ind(struct sock *sk)
Kumar Sanghvi8d98efa2010-09-26 19:07:59 +0000159{
Kumar Sanghvib3d62552010-10-12 20:14:43 +0000160 struct pep_sock *pn = pep_sk(sk);
Rémi Denis-Courmont44c9ab12011-03-08 22:44:09 +0000161 u8 data[4] = {
162 PN_PIPE_SB_NEGOTIATED_FC, pep_sb_size(2),
163 pn->tx_fc, pn->rx_fc,
Kumar Sanghvi8d98efa2010-09-26 19:07:59 +0000164 };
Kumar Sanghvi8d98efa2010-09-26 19:07:59 +0000165
Rémi Denis-Courmont44c9ab12011-03-08 22:44:09 +0000166 return pep_indicate(sk, PNS_PIPE_CREATED_IND, 1 /* sub-blocks */,
167 data, 4, GFP_ATOMIC);
Kumar Sanghvi8d98efa2010-09-26 19:07:59 +0000168}
169
Rémi Denis-Courmont297edb62011-03-08 22:44:12 +0000170#ifdef CONFIG_PHONET_PIPECTRLR
Rémi Denis-Courmont03789f22010-10-08 04:02:02 +0000171static int pipe_handler_enable_pipe(struct sock *sk, int enable)
Kumar Sanghvi8d98efa2010-09-26 19:07:59 +0000172{
Rémi Denis-Courmont0165d692011-02-24 23:15:00 +0000173 u8 id = enable ? PNS_PEP_ENABLE_REQ : PNS_PEP_DISABLE_REQ;
Kumar Sanghvi8d98efa2010-09-26 19:07:59 +0000174
Rémi Denis-Courmont44c9ab12011-03-08 22:44:09 +0000175 return pipe_handler_request(sk, id, PAD, NULL, 0);
Kumar Sanghvi8d98efa2010-09-26 19:07:59 +0000176}
177#endif
178
Rémi Denis-Courmont96414582008-10-05 11:15:13 -0700179static int pep_accept_conn(struct sock *sk, struct sk_buff *skb)
180{
181 static const u8 data[20] = {
182 PAD, PAD, PAD, 2 /* sub-blocks */,
183 PN_PIPE_SB_REQUIRED_FC_TX, pep_sb_size(5), 3, PAD,
184 PN_MULTI_CREDIT_FLOW_CONTROL,
185 PN_ONE_CREDIT_FLOW_CONTROL,
186 PN_LEGACY_FLOW_CONTROL,
187 PAD,
188 PN_PIPE_SB_PREFERRED_FC_RX, pep_sb_size(5), 3, PAD,
189 PN_MULTI_CREDIT_FLOW_CONTROL,
190 PN_ONE_CREDIT_FLOW_CONTROL,
191 PN_LEGACY_FLOW_CONTROL,
192 PAD,
193 };
194
195 might_sleep();
196 return pep_reply(sk, skb, PN_PIPE_NO_ERROR, data, sizeof(data),
197 GFP_KERNEL);
198}
199
Rémi Denis-Courmontf7ae8d52011-03-08 22:44:10 +0000200static int pep_reject_conn(struct sock *sk, struct sk_buff *skb, u8 code,
201 gfp_t priority)
Rémi Denis-Courmont96414582008-10-05 11:15:13 -0700202{
203 static const u8 data[4] = { PAD, PAD, PAD, 0 /* sub-blocks */ };
204 WARN_ON(code == PN_PIPE_NO_ERROR);
Rémi Denis-Courmontf7ae8d52011-03-08 22:44:10 +0000205 return pep_reply(sk, skb, code, data, sizeof(data), priority);
Rémi Denis-Courmont96414582008-10-05 11:15:13 -0700206}
207
208/* Control requests are not sent by the pipe service and have a specific
209 * message format. */
Rémi Denis-Courmontc41bd97f82008-10-05 11:15:43 -0700210static int pep_ctrlreq_error(struct sock *sk, struct sk_buff *oskb, u8 code,
211 gfp_t priority)
Rémi Denis-Courmont96414582008-10-05 11:15:13 -0700212{
213 const struct pnpipehdr *oph = pnp_hdr(oskb);
214 struct sk_buff *skb;
215 struct pnpipehdr *ph;
216 struct sockaddr_pn dst;
Rémi Denis-Courmont44c9ab12011-03-08 22:44:09 +0000217 u8 data[4] = {
218 oph->data[0], /* PEP type */
219 code, /* error code, at an unusual offset */
220 PAD, PAD,
221 };
Rémi Denis-Courmont96414582008-10-05 11:15:13 -0700222
Rémi Denis-Courmont44c9ab12011-03-08 22:44:09 +0000223 skb = pep_alloc_skb(sk, data, 4, priority);
Rémi Denis-Courmont96414582008-10-05 11:15:13 -0700224 if (!skb)
225 return -ENOMEM;
Rémi Denis-Courmont96414582008-10-05 11:15:13 -0700226
Rémi Denis-Courmont44c9ab12011-03-08 22:44:09 +0000227 ph = pnp_hdr(skb);
Rémi Denis-Courmont96414582008-10-05 11:15:13 -0700228 ph->utid = oph->utid;
229 ph->message_id = PNS_PEP_CTRL_RESP;
230 ph->pipe_handle = oph->pipe_handle;
231 ph->data[0] = oph->data[1]; /* CTRL id */
Rémi Denis-Courmont96414582008-10-05 11:15:13 -0700232
233 pn_skb_get_src_sockaddr(oskb, &dst);
234 return pn_skb_send(sk, skb, &dst);
235}
236
237static int pipe_snd_status(struct sock *sk, u8 type, u8 status, gfp_t priority)
238{
Rémi Denis-Courmont44c9ab12011-03-08 22:44:09 +0000239 u8 data[4] = { type, PAD, PAD, status };
Rémi Denis-Courmont96414582008-10-05 11:15:13 -0700240
Rémi Denis-Courmont44c9ab12011-03-08 22:44:09 +0000241 return pep_indicate(sk, PNS_PEP_STATUS_IND, PN_PEP_TYPE_COMMON,
242 data, 4, priority);
Rémi Denis-Courmont96414582008-10-05 11:15:13 -0700243}
244
245/* Send our RX flow control information to the sender.
246 * Socket must be locked. */
Rémi Denis-Courmont44c9ab12011-03-08 22:44:09 +0000247static void pipe_grant_credits(struct sock *sk, gfp_t priority)
Rémi Denis-Courmont96414582008-10-05 11:15:13 -0700248{
249 struct pep_sock *pn = pep_sk(sk);
250
251 BUG_ON(sk->sk_state != TCP_ESTABLISHED);
252
253 switch (pn->rx_fc) {
254 case PN_LEGACY_FLOW_CONTROL: /* TODO */
255 break;
256 case PN_ONE_CREDIT_FLOW_CONTROL:
Rémi Denis-Courmont44c9ab12011-03-08 22:44:09 +0000257 if (pipe_snd_status(sk, PN_PEP_IND_FLOW_CONTROL,
258 PEP_IND_READY, priority) == 0)
259 pn->rx_credits = 1;
Rémi Denis-Courmont96414582008-10-05 11:15:13 -0700260 break;
261 case PN_MULTI_CREDIT_FLOW_CONTROL:
262 if ((pn->rx_credits + CREDITS_THR) > CREDITS_MAX)
263 break;
264 if (pipe_snd_status(sk, PN_PEP_IND_ID_MCFC_GRANT_CREDITS,
265 CREDITS_MAX - pn->rx_credits,
Rémi Denis-Courmont44c9ab12011-03-08 22:44:09 +0000266 priority) == 0)
Rémi Denis-Courmont96414582008-10-05 11:15:13 -0700267 pn->rx_credits = CREDITS_MAX;
268 break;
269 }
270}
271
272static int pipe_rcv_status(struct sock *sk, struct sk_buff *skb)
273{
274 struct pep_sock *pn = pep_sk(sk);
Kumar Sanghvia91e7d42010-09-27 23:10:42 +0000275 struct pnpipehdr *hdr;
Rémi Denis-Courmontbe677732008-12-17 15:48:31 -0800276 int wake = 0;
Rémi Denis-Courmont96414582008-10-05 11:15:13 -0700277
278 if (!pskb_may_pull(skb, sizeof(*hdr) + 4))
279 return -EINVAL;
280
Kumar Sanghvia91e7d42010-09-27 23:10:42 +0000281 hdr = pnp_hdr(skb);
Rémi Denis-Courmont96414582008-10-05 11:15:13 -0700282 if (hdr->data[0] != PN_PEP_TYPE_COMMON) {
283 LIMIT_NETDEBUG(KERN_DEBUG"Phonet unknown PEP type: %u\n",
284 (unsigned)hdr->data[0]);
285 return -EOPNOTSUPP;
286 }
287
288 switch (hdr->data[1]) {
289 case PN_PEP_IND_FLOW_CONTROL:
290 switch (pn->tx_fc) {
291 case PN_LEGACY_FLOW_CONTROL:
292 switch (hdr->data[4]) {
293 case PEP_IND_BUSY:
Rémi Denis-Courmontbe677732008-12-17 15:48:31 -0800294 atomic_set(&pn->tx_credits, 0);
Rémi Denis-Courmont96414582008-10-05 11:15:13 -0700295 break;
296 case PEP_IND_READY:
Rémi Denis-Courmontbe677732008-12-17 15:48:31 -0800297 atomic_set(&pn->tx_credits, wake = 1);
Rémi Denis-Courmont96414582008-10-05 11:15:13 -0700298 break;
299 }
300 break;
301 case PN_ONE_CREDIT_FLOW_CONTROL:
302 if (hdr->data[4] == PEP_IND_READY)
Rémi Denis-Courmontbe677732008-12-17 15:48:31 -0800303 atomic_set(&pn->tx_credits, wake = 1);
Rémi Denis-Courmont96414582008-10-05 11:15:13 -0700304 break;
305 }
306 break;
307
308 case PN_PEP_IND_ID_MCFC_GRANT_CREDITS:
309 if (pn->tx_fc != PN_MULTI_CREDIT_FLOW_CONTROL)
310 break;
Rémi Denis-Courmontbe677732008-12-17 15:48:31 -0800311 atomic_add(wake = hdr->data[4], &pn->tx_credits);
Rémi Denis-Courmont96414582008-10-05 11:15:13 -0700312 break;
313
314 default:
315 LIMIT_NETDEBUG(KERN_DEBUG"Phonet unknown PEP indication: %u\n",
316 (unsigned)hdr->data[1]);
317 return -EOPNOTSUPP;
318 }
Rémi Denis-Courmontbe677732008-12-17 15:48:31 -0800319 if (wake)
Rémi Denis-Courmont96414582008-10-05 11:15:13 -0700320 sk->sk_write_space(sk);
321 return 0;
322}
323
324static int pipe_rcv_created(struct sock *sk, struct sk_buff *skb)
325{
326 struct pep_sock *pn = pep_sk(sk);
327 struct pnpipehdr *hdr = pnp_hdr(skb);
328 u8 n_sb = hdr->data[0];
329
330 pn->rx_fc = pn->tx_fc = PN_LEGACY_FLOW_CONTROL;
331 __skb_pull(skb, sizeof(*hdr));
332 while (n_sb > 0) {
333 u8 type, buf[2], len = sizeof(buf);
334 u8 *data = pep_get_sb(skb, &type, &len, buf);
335
336 if (data == NULL)
337 return -EINVAL;
338 switch (type) {
339 case PN_PIPE_SB_NEGOTIATED_FC:
340 if (len < 2 || (data[0] | data[1]) > 3)
341 break;
342 pn->tx_fc = data[0] & 3;
343 pn->rx_fc = data[1] & 3;
344 break;
345 }
346 n_sb--;
347 }
348 return 0;
349}
350
351/* Queue an skb to a connected sock.
352 * Socket lock must be held. */
353static int pipe_do_rcv(struct sock *sk, struct sk_buff *skb)
354{
355 struct pep_sock *pn = pep_sk(sk);
356 struct pnpipehdr *hdr = pnp_hdr(skb);
Rémi Denis-Courmontc41bd97f82008-10-05 11:15:43 -0700357 struct sk_buff_head *queue;
Rémi Denis-Courmont96414582008-10-05 11:15:13 -0700358 int err = 0;
359
360 BUG_ON(sk->sk_state == TCP_CLOSE_WAIT);
361
362 switch (hdr->message_id) {
363 case PNS_PEP_CONNECT_REQ:
Rémi Denis-Courmontf7ae8d52011-03-08 22:44:10 +0000364 pep_reject_conn(sk, skb, PN_PIPE_ERR_PEP_IN_USE, GFP_ATOMIC);
Rémi Denis-Courmont96414582008-10-05 11:15:13 -0700365 break;
366
367 case PNS_PEP_DISCONNECT_REQ:
368 pep_reply(sk, skb, PN_PIPE_NO_ERROR, NULL, 0, GFP_ATOMIC);
369 sk->sk_state = TCP_CLOSE_WAIT;
370 if (!sock_flag(sk, SOCK_DEAD))
371 sk->sk_state_change(sk);
372 break;
373
374 case PNS_PEP_ENABLE_REQ:
375 /* Wait for PNS_PIPE_(ENABLED|REDIRECTED)_IND */
376 pep_reply(sk, skb, PN_PIPE_NO_ERROR, NULL, 0, GFP_ATOMIC);
377 break;
378
379 case PNS_PEP_RESET_REQ:
380 switch (hdr->state_after_reset) {
381 case PN_PIPE_DISABLE:
382 pn->init_enable = 0;
383 break;
384 case PN_PIPE_ENABLE:
385 pn->init_enable = 1;
386 break;
387 default: /* not allowed to send an error here!? */
388 err = -EINVAL;
389 goto out;
390 }
391 /* fall through */
392 case PNS_PEP_DISABLE_REQ:
Rémi Denis-Courmontbe677732008-12-17 15:48:31 -0800393 atomic_set(&pn->tx_credits, 0);
Rémi Denis-Courmont96414582008-10-05 11:15:13 -0700394 pep_reply(sk, skb, PN_PIPE_NO_ERROR, NULL, 0, GFP_ATOMIC);
395 break;
396
397 case PNS_PEP_CTRL_REQ:
Rémi Denis-Courmont2e2fb4b2009-07-21 01:57:59 +0000398 if (skb_queue_len(&pn->ctrlreq_queue) >= PNPIPE_CTRLREQ_MAX) {
399 atomic_inc(&sk->sk_drops);
Rémi Denis-Courmontc41bd97f82008-10-05 11:15:43 -0700400 break;
Rémi Denis-Courmont2e2fb4b2009-07-21 01:57:59 +0000401 }
Rémi Denis-Courmontc41bd97f82008-10-05 11:15:43 -0700402 __skb_pull(skb, 4);
403 queue = &pn->ctrlreq_queue;
404 goto queue;
Rémi Denis-Courmont96414582008-10-05 11:15:13 -0700405
Rémi Denis-Courmontfc6a1102010-01-04 02:02:47 +0000406 case PNS_PIPE_ALIGNED_DATA:
407 __skb_pull(skb, 1);
408 /* fall through */
Rémi Denis-Courmont96414582008-10-05 11:15:13 -0700409 case PNS_PIPE_DATA:
410 __skb_pull(skb, 3); /* Pipe data header */
411 if (!pn_flow_safe(pn->rx_fc)) {
412 err = sock_queue_rcv_skb(sk, skb);
413 if (!err)
Rémi Denis-Courmont0ebbf312011-03-08 22:44:08 +0000414 return NET_RX_SUCCESS;
415 err = -ENOBUFS;
Rémi Denis-Courmont96414582008-10-05 11:15:13 -0700416 break;
417 }
418
419 if (pn->rx_credits == 0) {
Rémi Denis-Courmont2e2fb4b2009-07-21 01:57:59 +0000420 atomic_inc(&sk->sk_drops);
Rémi Denis-Courmont96414582008-10-05 11:15:13 -0700421 err = -ENOBUFS;
422 break;
423 }
424 pn->rx_credits--;
Rémi Denis-Courmontc41bd97f82008-10-05 11:15:43 -0700425 queue = &sk->sk_receive_queue;
426 goto queue;
Rémi Denis-Courmont96414582008-10-05 11:15:13 -0700427
428 case PNS_PEP_STATUS_IND:
429 pipe_rcv_status(sk, skb);
430 break;
431
432 case PNS_PIPE_REDIRECTED_IND:
433 err = pipe_rcv_created(sk, skb);
434 break;
435
436 case PNS_PIPE_CREATED_IND:
437 err = pipe_rcv_created(sk, skb);
438 if (err)
439 break;
440 /* fall through */
441 case PNS_PIPE_RESET_IND:
442 if (!pn->init_enable)
443 break;
444 /* fall through */
445 case PNS_PIPE_ENABLED_IND:
446 if (!pn_flow_safe(pn->tx_fc)) {
Rémi Denis-Courmontbe677732008-12-17 15:48:31 -0800447 atomic_set(&pn->tx_credits, 1);
Rémi Denis-Courmont96414582008-10-05 11:15:13 -0700448 sk->sk_write_space(sk);
449 }
450 if (sk->sk_state == TCP_ESTABLISHED)
451 break; /* Nothing to do */
452 sk->sk_state = TCP_ESTABLISHED;
Rémi Denis-Courmont44c9ab12011-03-08 22:44:09 +0000453 pipe_grant_credits(sk, GFP_ATOMIC);
Rémi Denis-Courmont96414582008-10-05 11:15:13 -0700454 break;
455
456 case PNS_PIPE_DISABLED_IND:
457 sk->sk_state = TCP_SYN_RECV;
458 pn->rx_credits = 0;
459 break;
460
461 default:
462 LIMIT_NETDEBUG(KERN_DEBUG"Phonet unknown PEP message: %u\n",
463 hdr->message_id);
464 err = -EINVAL;
465 }
466out:
467 kfree_skb(skb);
Rémi Denis-Courmont0ebbf312011-03-08 22:44:08 +0000468 return (err == -ENOBUFS) ? NET_RX_DROP : NET_RX_SUCCESS;
Rémi Denis-Courmontc41bd97f82008-10-05 11:15:43 -0700469
470queue:
471 skb->dev = NULL;
472 skb_set_owner_r(skb, sk);
473 err = skb->len;
474 skb_queue_tail(queue, skb);
475 if (!sock_flag(sk, SOCK_DEAD))
476 sk->sk_data_ready(sk, err);
Rémi Denis-Courmont0ebbf312011-03-08 22:44:08 +0000477 return NET_RX_SUCCESS;
Rémi Denis-Courmont96414582008-10-05 11:15:13 -0700478}
479
480/* Destroy connected sock. */
481static void pipe_destruct(struct sock *sk)
482{
Rémi Denis-Courmontc41bd97f82008-10-05 11:15:43 -0700483 struct pep_sock *pn = pep_sk(sk);
484
Rémi Denis-Courmont96414582008-10-05 11:15:13 -0700485 skb_queue_purge(&sk->sk_receive_queue);
Rémi Denis-Courmontc41bd97f82008-10-05 11:15:43 -0700486 skb_queue_purge(&pn->ctrlreq_queue);
Rémi Denis-Courmont96414582008-10-05 11:15:13 -0700487}
488
Rémi Denis-Courmont8f44fcc2011-02-24 23:15:01 +0000489static u8 pipe_negotiate_fc(const u8 *fcs, unsigned n)
490{
491 unsigned i;
492 u8 final_fc = PN_NO_FLOW_CONTROL;
493
494 for (i = 0; i < n; i++) {
495 u8 fc = fcs[i];
496
497 if (fc > final_fc && fc < PN_MAX_FLOW_CONTROL)
498 final_fc = fc;
499 }
500 return final_fc;
501}
502
Kumar Sanghvib3d62552010-10-12 20:14:43 +0000503static int pep_connresp_rcv(struct sock *sk, struct sk_buff *skb)
504{
505 struct pep_sock *pn = pep_sk(sk);
Rémi Denis-Courmont8f44fcc2011-02-24 23:15:01 +0000506 struct pnpipehdr *hdr;
507 u8 n_sb;
Kumar Sanghvib3d62552010-10-12 20:14:43 +0000508
Rémi Denis-Courmont8f44fcc2011-02-24 23:15:01 +0000509 if (!pskb_pull(skb, sizeof(*hdr) + 4))
510 return -EINVAL;
511
512 hdr = pnp_hdr(skb);
Rémi Denis-Courmont297edb62011-03-08 22:44:12 +0000513 if (hdr->error_code != PN_PIPE_NO_ERROR)
514 return -ECONNREFUSED;
Rémi Denis-Courmont8f44fcc2011-02-24 23:15:01 +0000515
516 /* Parse sub-blocks */
517 n_sb = hdr->data[4];
518 while (n_sb > 0) {
519 u8 type, buf[6], len = sizeof(buf);
520 const u8 *data = pep_get_sb(skb, &type, &len, buf);
521
522 if (data == NULL)
523 return -EINVAL;
524
525 switch (type) {
526 case PN_PIPE_SB_REQUIRED_FC_TX:
527 if (len < 2 || len < data[0])
528 break;
529 pn->tx_fc = pipe_negotiate_fc(data + 2, len - 2);
530 break;
531
532 case PN_PIPE_SB_PREFERRED_FC_RX:
533 if (len < 2 || len < data[0])
534 break;
535 pn->rx_fc = pipe_negotiate_fc(data + 2, len - 2);
536 break;
537
538 }
539 n_sb--;
540 }
Kumar Sanghvib3d62552010-10-12 20:14:43 +0000541
Rémi Denis-Courmont44c9ab12011-03-08 22:44:09 +0000542 return pipe_handler_send_created_ind(sk);
Kumar Sanghvib3d62552010-10-12 20:14:43 +0000543}
Rémi Denis-Courmont297edb62011-03-08 22:44:12 +0000544
545/* Queue an skb to an actively connected sock.
546 * Socket lock must be held. */
547static int pipe_handler_do_rcv(struct sock *sk, struct sk_buff *skb)
548{
549 struct pep_sock *pn = pep_sk(sk);
550 struct pnpipehdr *hdr = pnp_hdr(skb);
551 int err = NET_RX_SUCCESS;
552
553 switch (hdr->message_id) {
554 case PNS_PIPE_ALIGNED_DATA:
555 __skb_pull(skb, 1);
556 /* fall through */
557 case PNS_PIPE_DATA:
558 __skb_pull(skb, 3); /* Pipe data header */
559 if (!pn_flow_safe(pn->rx_fc)) {
560 err = sock_queue_rcv_skb(sk, skb);
561 if (!err)
562 return NET_RX_SUCCESS;
563 err = NET_RX_DROP;
564 break;
565 }
566
567 if (pn->rx_credits == 0) {
568 atomic_inc(&sk->sk_drops);
569 err = NET_RX_DROP;
570 break;
571 }
572 pn->rx_credits--;
573 skb->dev = NULL;
574 skb_set_owner_r(skb, sk);
575 err = skb->len;
576 skb_queue_tail(&sk->sk_receive_queue, skb);
577 if (!sock_flag(sk, SOCK_DEAD))
578 sk->sk_data_ready(sk, err);
579 return NET_RX_SUCCESS;
580
581 case PNS_PEP_CONNECT_RESP:
582 if (sk->sk_state != TCP_SYN_SENT)
583 break;
584 if (!sock_flag(sk, SOCK_DEAD))
585 sk->sk_state_change(sk);
586 if (pep_connresp_rcv(sk, skb)) {
587 sk->sk_state = TCP_CLOSE_WAIT;
588 break;
589 }
590
591 sk->sk_state = TCP_ESTABLISHED;
592 if (!pn_flow_safe(pn->tx_fc)) {
593 atomic_set(&pn->tx_credits, 1);
594 sk->sk_write_space(sk);
595 }
596 pipe_grant_credits(sk, GFP_ATOMIC);
597 break;
598
599 case PNS_PEP_DISCONNECT_RESP:
600 /* sock should already be dead, nothing to do */
601 break;
602
603 case PNS_PEP_STATUS_IND:
604 pipe_rcv_status(sk, skb);
605 break;
606 }
607 kfree_skb(skb);
608 return err;
609}
Kumar Sanghvib3d62552010-10-12 20:14:43 +0000610
Rémi Denis-Courmont96414582008-10-05 11:15:13 -0700611/* Listening sock must be locked */
612static struct sock *pep_find_pipe(const struct hlist_head *hlist,
613 const struct sockaddr_pn *dst,
614 u8 pipe_handle)
615{
616 struct hlist_node *node;
617 struct sock *sknode;
618 u16 dobj = pn_sockaddr_get_object(dst);
619
620 sk_for_each(sknode, node, hlist) {
621 struct pep_sock *pnnode = pep_sk(sknode);
622
623 /* Ports match, but addresses might not: */
624 if (pnnode->pn_sk.sobject != dobj)
625 continue;
626 if (pnnode->pipe_handle != pipe_handle)
627 continue;
628 if (sknode->sk_state == TCP_CLOSE_WAIT)
629 continue;
630
631 sock_hold(sknode);
632 return sknode;
633 }
634 return NULL;
635}
636
637/*
638 * Deliver an skb to a listening sock.
639 * Socket lock must be held.
640 * We then queue the skb to the right connected sock (if any).
641 */
642static int pep_do_rcv(struct sock *sk, struct sk_buff *skb)
643{
644 struct pep_sock *pn = pep_sk(sk);
645 struct sock *sknode;
Rémi Denis-Courmont2ddc1ac2009-02-10 17:14:50 -0800646 struct pnpipehdr *hdr;
Rémi Denis-Courmont96414582008-10-05 11:15:13 -0700647 struct sockaddr_pn dst;
Rémi Denis-Courmont96414582008-10-05 11:15:13 -0700648 u8 pipe_handle;
649
650 if (!pskb_may_pull(skb, sizeof(*hdr)))
651 goto drop;
652
653 hdr = pnp_hdr(skb);
654 pipe_handle = hdr->pipe_handle;
655 if (pipe_handle == PN_PIPE_INVALID_HANDLE)
656 goto drop;
657
658 pn_skb_get_dst_sockaddr(skb, &dst);
659
660 /* Look for an existing pipe handle */
661 sknode = pep_find_pipe(&pn->hlist, &dst, pipe_handle);
662 if (sknode)
663 return sk_receive_skb(sknode, skb, 1);
664
Rémi Denis-Courmont96414582008-10-05 11:15:13 -0700665 switch (hdr->message_id) {
666 case PNS_PEP_CONNECT_REQ:
Rémi Denis-Courmontf7ae8d52011-03-08 22:44:10 +0000667 if (sk->sk_state != TCP_LISTEN || sk_acceptq_is_full(sk)) {
668 pep_reject_conn(sk, skb, PN_PIPE_ERR_PEP_IN_USE,
669 GFP_ATOMIC);
670 break;
671 }
672 skb_queue_head(&sk->sk_receive_queue, skb);
673 sk_acceptq_added(sk);
674 if (!sock_flag(sk, SOCK_DEAD))
675 sk->sk_data_ready(sk, 0);
676 return NET_RX_SUCCESS;
Rémi Denis-Courmont96414582008-10-05 11:15:13 -0700677
678 case PNS_PEP_DISCONNECT_REQ:
679 pep_reply(sk, skb, PN_PIPE_NO_ERROR, NULL, 0, GFP_ATOMIC);
680 break;
681
682 case PNS_PEP_CTRL_REQ:
Rémi Denis-Courmontc41bd97f82008-10-05 11:15:43 -0700683 pep_ctrlreq_error(sk, skb, PN_PIPE_INVALID_HANDLE, GFP_ATOMIC);
Rémi Denis-Courmont96414582008-10-05 11:15:13 -0700684 break;
685
686 case PNS_PEP_RESET_REQ:
687 case PNS_PEP_ENABLE_REQ:
688 case PNS_PEP_DISABLE_REQ:
689 /* invalid handle is not even allowed here! */
Rémi Denis-Courmont0ebbf312011-03-08 22:44:08 +0000690 break;
Rémi Denis-Courmont297edb62011-03-08 22:44:12 +0000691
692 default:
693 if ((1 << sk->sk_state)
694 & ~(TCPF_CLOSE|TCPF_LISTEN|TCPF_CLOSE_WAIT))
695 /* actively connected socket */
696 return pipe_handler_do_rcv(sk, skb);
Rémi Denis-Courmont96414582008-10-05 11:15:13 -0700697 }
698drop:
699 kfree_skb(skb);
Rémi Denis-Courmont0ebbf312011-03-08 22:44:08 +0000700 return NET_RX_SUCCESS;
Rémi Denis-Courmont96414582008-10-05 11:15:13 -0700701}
702
Rémi Denis-Courmont6482f552010-09-15 12:19:53 +0000703static int pipe_do_remove(struct sock *sk)
704{
705 struct pep_sock *pn = pep_sk(sk);
706 struct pnpipehdr *ph;
707 struct sk_buff *skb;
708
Rémi Denis-Courmont44c9ab12011-03-08 22:44:09 +0000709 skb = pep_alloc_skb(sk, NULL, 0, GFP_KERNEL);
Rémi Denis-Courmont6482f552010-09-15 12:19:53 +0000710 if (!skb)
711 return -ENOMEM;
712
Rémi Denis-Courmont6482f552010-09-15 12:19:53 +0000713 ph = pnp_hdr(skb);
714 ph->utid = 0;
715 ph->message_id = PNS_PIPE_REMOVE_REQ;
716 ph->pipe_handle = pn->pipe_handle;
717 ph->data[0] = PAD;
Rémi Denis-Courmont14ba8fa2011-02-24 23:14:58 +0000718 return pn_skb_send(sk, skb, NULL);
Rémi Denis-Courmont6482f552010-09-15 12:19:53 +0000719}
720
Rémi Denis-Courmont96414582008-10-05 11:15:13 -0700721/* associated socket ceases to exist */
722static void pep_sock_close(struct sock *sk, long timeout)
723{
724 struct pep_sock *pn = pep_sk(sk);
Rémi Denis-Courmont02a47612008-10-05 11:16:16 -0700725 int ifindex = 0;
Rémi Denis-Courmont96414582008-10-05 11:15:13 -0700726
Rémi Denis-Courmonte5134802010-05-25 16:08:39 -0700727 sock_hold(sk); /* keep a reference after sk_common_release() */
Rémi Denis-Courmont96414582008-10-05 11:15:13 -0700728 sk_common_release(sk);
729
730 lock_sock(sk);
Rémi Denis-Courmontf7ae8d52011-03-08 22:44:10 +0000731 if ((1 << sk->sk_state) & (TCPF_SYN_RECV|TCPF_ESTABLISHED)) {
Rémi Denis-Courmont297edb62011-03-08 22:44:12 +0000732 if (sk->sk_backlog_rcv == pipe_do_rcv)
733 /* Forcefully remove dangling Phonet pipe */
734 pipe_do_remove(sk);
735 else
736 pipe_handler_request(sk, PNS_PEP_DISCONNECT_REQ, PAD,
737 NULL, 0);
Rémi Denis-Courmont2feb6182011-02-24 23:14:59 +0000738 }
Rémi Denis-Courmontf7ae8d52011-03-08 22:44:10 +0000739 sk->sk_state = TCP_CLOSE;
Kumar Sanghvib3d62552010-10-12 20:14:43 +0000740
Rémi Denis-Courmont02a47612008-10-05 11:16:16 -0700741 ifindex = pn->ifindex;
742 pn->ifindex = 0;
Rémi Denis-Courmont96414582008-10-05 11:15:13 -0700743 release_sock(sk);
Rémi Denis-Courmont02a47612008-10-05 11:16:16 -0700744
745 if (ifindex)
746 gprs_detach(sk);
Rémi Denis-Courmonte5134802010-05-25 16:08:39 -0700747 sock_put(sk);
Rémi Denis-Courmont96414582008-10-05 11:15:13 -0700748}
749
Rémi Denis-Courmont96414582008-10-05 11:15:13 -0700750static struct sock *pep_sock_accept(struct sock *sk, int flags, int *errp)
751{
Rémi Denis-Courmontf7ae8d52011-03-08 22:44:10 +0000752 struct pep_sock *pn = pep_sk(sk), *newpn;
Rémi Denis-Courmont96414582008-10-05 11:15:13 -0700753 struct sock *newsk = NULL;
Rémi Denis-Courmontf7ae8d52011-03-08 22:44:10 +0000754 struct sk_buff *skb;
755 struct pnpipehdr *hdr;
756 struct sockaddr_pn dst, src;
Rémi Denis-Courmont96414582008-10-05 11:15:13 -0700757 int err;
Rémi Denis-Courmontf7ae8d52011-03-08 22:44:10 +0000758 u16 peer_type;
759 u8 pipe_handle, enabled, n_sb;
760 u8 aligned = 0;
761
762 skb = skb_recv_datagram(sk, 0, flags & O_NONBLOCK, errp);
763 if (!skb)
764 return NULL;
Rémi Denis-Courmont96414582008-10-05 11:15:13 -0700765
766 lock_sock(sk);
Rémi Denis-Courmontf7ae8d52011-03-08 22:44:10 +0000767 if (sk->sk_state != TCP_LISTEN) {
768 err = -EINVAL;
769 goto drop;
Rémi Denis-Courmont96414582008-10-05 11:15:13 -0700770 }
Rémi Denis-Courmont96414582008-10-05 11:15:13 -0700771 sk_acceptq_removed(sk);
Rémi Denis-Courmont96414582008-10-05 11:15:13 -0700772
Rémi Denis-Courmontf7ae8d52011-03-08 22:44:10 +0000773 err = -EPROTO;
774 if (!pskb_may_pull(skb, sizeof(*hdr) + 4))
775 goto drop;
776
777 hdr = pnp_hdr(skb);
778 pipe_handle = hdr->pipe_handle;
779 switch (hdr->state_after_connect) {
780 case PN_PIPE_DISABLE:
781 enabled = 0;
782 break;
783 case PN_PIPE_ENABLE:
784 enabled = 1;
785 break;
786 default:
787 pep_reject_conn(sk, skb, PN_PIPE_ERR_INVALID_PARAM,
788 GFP_KERNEL);
789 goto drop;
790 }
791 peer_type = hdr->other_pep_type << 8;
792
793 /* Parse sub-blocks (options) */
794 n_sb = hdr->data[4];
795 while (n_sb > 0) {
796 u8 type, buf[1], len = sizeof(buf);
797 const u8 *data = pep_get_sb(skb, &type, &len, buf);
798
799 if (data == NULL)
800 goto drop;
801 switch (type) {
802 case PN_PIPE_SB_CONNECT_REQ_PEP_SUB_TYPE:
803 if (len < 1)
804 goto drop;
805 peer_type = (peer_type & 0xff00) | data[0];
806 break;
807 case PN_PIPE_SB_ALIGNED_DATA:
808 aligned = data[0] != 0;
809 break;
810 }
811 n_sb--;
812 }
813
814 /* Check for duplicate pipe handle */
815 newsk = pep_find_pipe(&pn->hlist, &dst, pipe_handle);
816 if (unlikely(newsk)) {
817 __sock_put(newsk);
818 newsk = NULL;
819 pep_reject_conn(sk, skb, PN_PIPE_ERR_PEP_IN_USE, GFP_KERNEL);
820 goto drop;
821 }
822
823 /* Create a new to-be-accepted sock */
824 newsk = sk_alloc(sock_net(sk), PF_PHONET, GFP_KERNEL, sk->sk_prot);
825 if (!newsk) {
826 pep_reject_conn(sk, skb, PN_PIPE_ERR_OVERLOAD, GFP_KERNEL);
827 err = -ENOBUFS;
828 goto drop;
829 }
830
831 sock_init_data(NULL, newsk);
832 newsk->sk_state = TCP_SYN_RECV;
833 newsk->sk_backlog_rcv = pipe_do_rcv;
834 newsk->sk_protocol = sk->sk_protocol;
835 newsk->sk_destruct = pipe_destruct;
836
837 newpn = pep_sk(newsk);
838 pn_skb_get_dst_sockaddr(skb, &dst);
839 pn_skb_get_src_sockaddr(skb, &src);
840 newpn->pn_sk.sobject = pn_sockaddr_get_object(&dst);
841 newpn->pn_sk.dobject = pn_sockaddr_get_object(&src);
842 newpn->pn_sk.resource = pn_sockaddr_get_resource(&dst);
843 sock_hold(sk);
844 newpn->listener = sk;
845 skb_queue_head_init(&newpn->ctrlreq_queue);
846 newpn->pipe_handle = pipe_handle;
847 atomic_set(&newpn->tx_credits, 0);
848 newpn->ifindex = 0;
849 newpn->peer_type = peer_type;
850 newpn->rx_credits = 0;
851 newpn->rx_fc = newpn->tx_fc = PN_LEGACY_FLOW_CONTROL;
852 newpn->init_enable = enabled;
853 newpn->aligned = aligned;
854
855 err = pep_accept_conn(newsk, skb);
856 if (err) {
857 sock_put(newsk);
858 newsk = NULL;
859 goto drop;
860 }
861 sk_add_node(newsk, &pn->hlist);
862drop:
Rémi Denis-Courmont96414582008-10-05 11:15:13 -0700863 release_sock(sk);
Rémi Denis-Courmontf7ae8d52011-03-08 22:44:10 +0000864 kfree_skb(skb);
Rémi Denis-Courmont96414582008-10-05 11:15:13 -0700865 *errp = err;
866 return newsk;
867}
868
Kumar Sanghvib3d62552010-10-12 20:14:43 +0000869static int pep_sock_connect(struct sock *sk, struct sockaddr *addr, int len)
870{
871 struct pep_sock *pn = pep_sk(sk);
Rémi Denis-Courmont297edb62011-03-08 22:44:12 +0000872 int err;
Rémi Denis-Courmont44c9ab12011-03-08 22:44:09 +0000873 u8 data[4] = { 0 /* sub-blocks */, PAD, PAD, PAD };
Kumar Sanghvib3d62552010-10-12 20:14:43 +0000874
Rémi Denis-Courmontacaf7df2011-03-08 22:44:11 +0000875 pn->pipe_handle = 1; /* anything but INVALID_HANDLE */
Rémi Denis-Courmont297edb62011-03-08 22:44:12 +0000876 err = pipe_handler_request(sk, PNS_PEP_CONNECT_REQ,
877 PN_PIPE_ENABLE, data, 4);
878 if (err) {
879 pn->pipe_handle = PN_PIPE_INVALID_HANDLE;
880 return err;
881 }
882 sk->sk_state = TCP_SYN_SENT;
883 return 0;
Kumar Sanghvib3d62552010-10-12 20:14:43 +0000884}
Kumar Sanghvib3d62552010-10-12 20:14:43 +0000885
Rémi Denis-Courmont96414582008-10-05 11:15:13 -0700886static int pep_ioctl(struct sock *sk, int cmd, unsigned long arg)
887{
Rémi Denis-Courmontc41bd97f82008-10-05 11:15:43 -0700888 struct pep_sock *pn = pep_sk(sk);
Rémi Denis-Courmont96414582008-10-05 11:15:13 -0700889 int answ;
890
891 switch (cmd) {
892 case SIOCINQ:
893 if (sk->sk_state == TCP_LISTEN)
894 return -EINVAL;
895
896 lock_sock(sk);
Joe Perchesf64f9e72009-11-29 16:55:45 -0800897 if (sock_flag(sk, SOCK_URGINLINE) &&
898 !skb_queue_empty(&pn->ctrlreq_queue))
Rémi Denis-Courmontc41bd97f82008-10-05 11:15:43 -0700899 answ = skb_peek(&pn->ctrlreq_queue)->len;
900 else if (!skb_queue_empty(&sk->sk_receive_queue))
Rémi Denis-Courmont96414582008-10-05 11:15:13 -0700901 answ = skb_peek(&sk->sk_receive_queue)->len;
902 else
903 answ = 0;
904 release_sock(sk);
905 return put_user(answ, (int __user *)arg);
906 }
907
908 return -ENOIOCTLCMD;
909}
910
911static int pep_init(struct sock *sk)
912{
913 struct pep_sock *pn = pep_sk(sk);
914
Rémi Denis-Courmontf7ae8d52011-03-08 22:44:10 +0000915 sk->sk_destruct = pipe_destruct;
Rémi Denis-Courmont96414582008-10-05 11:15:13 -0700916 INIT_HLIST_HEAD(&pn->hlist);
Rémi Denis-Courmont297edb62011-03-08 22:44:12 +0000917 pn->listener = NULL;
Rémi Denis-Courmontc41bd97f82008-10-05 11:15:43 -0700918 skb_queue_head_init(&pn->ctrlreq_queue);
Rémi Denis-Courmont297edb62011-03-08 22:44:12 +0000919 atomic_set(&pn->tx_credits, 0);
920 pn->ifindex = 0;
921 pn->peer_type = 0;
Rémi Denis-Courmont96414582008-10-05 11:15:13 -0700922 pn->pipe_handle = PN_PIPE_INVALID_HANDLE;
Rémi Denis-Courmont297edb62011-03-08 22:44:12 +0000923 pn->rx_credits = 0;
924 pn->rx_fc = pn->tx_fc = PN_LEGACY_FLOW_CONTROL;
925 pn->init_enable = 1;
926 pn->aligned = 0;
Rémi Denis-Courmont96414582008-10-05 11:15:13 -0700927 return 0;
928}
929
Rémi Denis-Courmont02a47612008-10-05 11:16:16 -0700930static int pep_setsockopt(struct sock *sk, int level, int optname,
David S. Millerb7058842009-09-30 16:12:20 -0700931 char __user *optval, unsigned int optlen)
Rémi Denis-Courmont02a47612008-10-05 11:16:16 -0700932{
933 struct pep_sock *pn = pep_sk(sk);
934 int val = 0, err = 0;
935
936 if (level != SOL_PNPIPE)
937 return -ENOPROTOOPT;
938 if (optlen >= sizeof(int)) {
939 if (get_user(val, (int __user *) optval))
940 return -EFAULT;
941 }
942
943 lock_sock(sk);
944 switch (optname) {
945 case PNPIPE_ENCAP:
946 if (val && val != PNPIPE_ENCAP_IP) {
947 err = -EINVAL;
948 break;
949 }
950 if (!pn->ifindex == !val)
951 break; /* Nothing to do! */
952 if (!capable(CAP_NET_ADMIN)) {
953 err = -EPERM;
954 break;
955 }
956 if (val) {
957 release_sock(sk);
958 err = gprs_attach(sk);
959 if (err > 0) {
960 pn->ifindex = err;
961 err = 0;
962 }
963 } else {
964 pn->ifindex = 0;
965 release_sock(sk);
966 gprs_detach(sk);
967 err = 0;
968 }
969 goto out_norel;
Rémi Denis-Courmont03789f22010-10-08 04:02:02 +0000970
971#ifdef CONFIG_PHONET_PIPECTRLR
972 case PNPIPE_ENABLE:
Rémi Denis-Courmont2feb6182011-02-24 23:14:59 +0000973 if ((1 << sk->sk_state) & ~(TCPF_SYN_RECV|TCPF_ESTABLISHED)) {
Rémi Denis-Courmont03789f22010-10-08 04:02:02 +0000974 err = -ENOTCONN;
975 break;
976 }
977 err = pipe_handler_enable_pipe(sk, val);
978 break;
979#endif
980
Rémi Denis-Courmont02a47612008-10-05 11:16:16 -0700981 default:
982 err = -ENOPROTOOPT;
983 }
984 release_sock(sk);
985
986out_norel:
987 return err;
988}
989
990static int pep_getsockopt(struct sock *sk, int level, int optname,
991 char __user *optval, int __user *optlen)
992{
993 struct pep_sock *pn = pep_sk(sk);
994 int len, val;
995
996 if (level != SOL_PNPIPE)
997 return -ENOPROTOOPT;
998 if (get_user(len, optlen))
999 return -EFAULT;
1000
1001 switch (optname) {
1002 case PNPIPE_ENCAP:
1003 val = pn->ifindex ? PNPIPE_ENCAP_IP : PNPIPE_ENCAP_NONE;
1004 break;
Kumar Sanghvi8d98efa2010-09-26 19:07:59 +00001005
Rémi Denis-Courmont02a47612008-10-05 11:16:16 -07001006 case PNPIPE_IFINDEX:
1007 val = pn->ifindex;
1008 break;
Rémi Denis-Courmont03789f22010-10-08 04:02:02 +00001009
Rémi Denis-Courmontacaf7df2011-03-08 22:44:11 +00001010 case PNPIPE_HANDLE:
1011 val = pn->pipe_handle;
1012 if (val == PN_PIPE_INVALID_HANDLE)
1013 return -EINVAL;
1014 break;
1015
Rémi Denis-Courmont03789f22010-10-08 04:02:02 +00001016#ifdef CONFIG_PHONET_PIPECTRLR
1017 case PNPIPE_ENABLE:
Rémi Denis-Courmont2feb6182011-02-24 23:14:59 +00001018 val = sk->sk_state == TCP_ESTABLISHED;
Rémi Denis-Courmont03789f22010-10-08 04:02:02 +00001019 break;
1020#endif
1021
Rémi Denis-Courmont02a47612008-10-05 11:16:16 -07001022 default:
1023 return -ENOPROTOOPT;
1024 }
1025
1026 len = min_t(unsigned int, sizeof(int), len);
1027 if (put_user(len, optlen))
1028 return -EFAULT;
1029 if (put_user(val, (int __user *) optval))
1030 return -EFAULT;
1031 return 0;
1032}
1033
1034static int pipe_skb_send(struct sock *sk, struct sk_buff *skb)
1035{
1036 struct pep_sock *pn = pep_sk(sk);
1037 struct pnpipehdr *ph;
Rémi Denis-Courmonte1a59642010-09-29 22:33:50 +00001038 int err;
Rémi Denis-Courmont02a47612008-10-05 11:16:16 -07001039
Rémi Denis-Courmontbe677732008-12-17 15:48:31 -08001040 if (pn_flow_safe(pn->tx_fc) &&
1041 !atomic_add_unless(&pn->tx_credits, -1, 0)) {
1042 kfree_skb(skb);
1043 return -ENOBUFS;
1044 }
1045
Rémi Denis-Courmontfea93ec2010-01-04 02:02:48 +00001046 skb_push(skb, 3 + pn->aligned);
Rémi Denis-Courmont02a47612008-10-05 11:16:16 -07001047 skb_reset_transport_header(skb);
1048 ph = pnp_hdr(skb);
1049 ph->utid = 0;
Rémi Denis-Courmontfea93ec2010-01-04 02:02:48 +00001050 if (pn->aligned) {
1051 ph->message_id = PNS_PIPE_ALIGNED_DATA;
1052 ph->data[0] = 0; /* padding */
1053 } else
1054 ph->message_id = PNS_PIPE_DATA;
Rémi Denis-Courmont02a47612008-10-05 11:16:16 -07001055 ph->pipe_handle = pn->pipe_handle;
Rémi Denis-Courmont14ba8fa2011-02-24 23:14:58 +00001056 err = pn_skb_send(sk, skb, NULL);
Rémi Denis-Courmonte1a59642010-09-29 22:33:50 +00001057
1058 if (err && pn_flow_safe(pn->tx_fc))
1059 atomic_inc(&pn->tx_credits);
1060 return err;
1061
Rémi Denis-Courmont02a47612008-10-05 11:16:16 -07001062}
1063
Rémi Denis-Courmont96414582008-10-05 11:15:13 -07001064static int pep_sendmsg(struct kiocb *iocb, struct sock *sk,
1065 struct msghdr *msg, size_t len)
1066{
1067 struct pep_sock *pn = pep_sk(sk);
Rémi Denis-Courmontb1704372009-11-09 04:06:40 +00001068 struct sk_buff *skb;
Rémi Denis-Courmont96414582008-10-05 11:15:13 -07001069 long timeo;
1070 int flags = msg->msg_flags;
1071 int err, done;
1072
Rémi Denis-Courmont82ecbcb2010-01-04 02:02:49 +00001073 if ((msg->msg_flags & ~(MSG_DONTWAIT|MSG_EOR|MSG_NOSIGNAL|
1074 MSG_CMSG_COMPAT)) ||
1075 !(msg->msg_flags & MSG_EOR))
Rémi Denis-Courmont96414582008-10-05 11:15:13 -07001076 return -EOPNOTSUPP;
1077
Rémi Denis-Courmontb1704372009-11-09 04:06:40 +00001078 skb = sock_alloc_send_skb(sk, MAX_PNPIPE_HEADER + len,
1079 flags & MSG_DONTWAIT, &err);
1080 if (!skb)
Rémi Denis-Courmont02ac3262010-08-30 12:57:04 +00001081 return err;
Rémi Denis-Courmontb1704372009-11-09 04:06:40 +00001082
1083 skb_reserve(skb, MAX_PHONET_HEADER + 3);
1084 err = memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len);
1085 if (err < 0)
1086 goto outfree;
1087
Rémi Denis-Courmont96414582008-10-05 11:15:13 -07001088 lock_sock(sk);
1089 timeo = sock_sndtimeo(sk, flags & MSG_DONTWAIT);
1090 if ((1 << sk->sk_state) & (TCPF_LISTEN|TCPF_CLOSE)) {
1091 err = -ENOTCONN;
1092 goto out;
1093 }
1094 if (sk->sk_state != TCP_ESTABLISHED) {
1095 /* Wait until the pipe gets to enabled state */
1096disabled:
1097 err = sk_stream_wait_connect(sk, &timeo);
1098 if (err)
1099 goto out;
1100
1101 if (sk->sk_state == TCP_CLOSE_WAIT) {
1102 err = -ECONNRESET;
1103 goto out;
1104 }
1105 }
1106 BUG_ON(sk->sk_state != TCP_ESTABLISHED);
1107
1108 /* Wait until flow control allows TX */
Rémi Denis-Courmontbe677732008-12-17 15:48:31 -08001109 done = atomic_read(&pn->tx_credits);
Rémi Denis-Courmont96414582008-10-05 11:15:13 -07001110 while (!done) {
1111 DEFINE_WAIT(wait);
1112
1113 if (!timeo) {
1114 err = -EAGAIN;
1115 goto out;
1116 }
1117 if (signal_pending(current)) {
1118 err = sock_intr_errno(timeo);
1119 goto out;
1120 }
1121
Eric Dumazet43815482010-04-29 11:01:49 +00001122 prepare_to_wait(sk_sleep(sk), &wait,
Rémi Denis-Courmont96414582008-10-05 11:15:13 -07001123 TASK_INTERRUPTIBLE);
Rémi Denis-Courmontbe677732008-12-17 15:48:31 -08001124 done = sk_wait_event(sk, &timeo, atomic_read(&pn->tx_credits));
Eric Dumazet43815482010-04-29 11:01:49 +00001125 finish_wait(sk_sleep(sk), &wait);
Rémi Denis-Courmont96414582008-10-05 11:15:13 -07001126
1127 if (sk->sk_state != TCP_ESTABLISHED)
1128 goto disabled;
1129 }
1130
Rémi Denis-Courmont02a47612008-10-05 11:16:16 -07001131 err = pipe_skb_send(sk, skb);
Rémi Denis-Courmont96414582008-10-05 11:15:13 -07001132 if (err >= 0)
1133 err = len; /* success! */
1134 skb = NULL;
1135out:
1136 release_sock(sk);
Rémi Denis-Courmontb1704372009-11-09 04:06:40 +00001137outfree:
Rémi Denis-Courmont96414582008-10-05 11:15:13 -07001138 kfree_skb(skb);
1139 return err;
1140}
1141
Rémi Denis-Courmont02a47612008-10-05 11:16:16 -07001142int pep_writeable(struct sock *sk)
1143{
1144 struct pep_sock *pn = pep_sk(sk);
1145
Rémi Denis-Courmontbe677732008-12-17 15:48:31 -08001146 return atomic_read(&pn->tx_credits);
Rémi Denis-Courmont02a47612008-10-05 11:16:16 -07001147}
1148
1149int pep_write(struct sock *sk, struct sk_buff *skb)
1150{
1151 struct sk_buff *rskb, *fs;
1152 int flen = 0;
1153
Rémi Denis-Courmontfea93ec2010-01-04 02:02:48 +00001154 if (pep_sk(sk)->aligned)
1155 return pipe_skb_send(sk, skb);
1156
Rémi Denis-Courmont02a47612008-10-05 11:16:16 -07001157 rskb = alloc_skb(MAX_PNPIPE_HEADER, GFP_ATOMIC);
1158 if (!rskb) {
1159 kfree_skb(skb);
1160 return -ENOMEM;
1161 }
1162 skb_shinfo(rskb)->frag_list = skb;
1163 rskb->len += skb->len;
1164 rskb->data_len += rskb->len;
1165 rskb->truesize += rskb->len;
1166
1167 /* Avoid nested fragments */
David S. Miller5c313e92009-06-09 00:21:58 -07001168 skb_walk_frags(skb, fs)
Rémi Denis-Courmont02a47612008-10-05 11:16:16 -07001169 flen += fs->len;
1170 skb->next = skb_shinfo(skb)->frag_list;
David S. Miller5c313e92009-06-09 00:21:58 -07001171 skb_frag_list_init(skb);
Rémi Denis-Courmont02a47612008-10-05 11:16:16 -07001172 skb->len -= flen;
1173 skb->data_len -= flen;
1174 skb->truesize -= flen;
1175
1176 skb_reserve(rskb, MAX_PHONET_HEADER + 3);
1177 return pipe_skb_send(sk, rskb);
1178}
1179
1180struct sk_buff *pep_read(struct sock *sk)
1181{
1182 struct sk_buff *skb = skb_dequeue(&sk->sk_receive_queue);
1183
1184 if (sk->sk_state == TCP_ESTABLISHED)
Rémi Denis-Courmont44c9ab12011-03-08 22:44:09 +00001185 pipe_grant_credits(sk, GFP_ATOMIC);
Rémi Denis-Courmont02a47612008-10-05 11:16:16 -07001186 return skb;
1187}
1188
Rémi Denis-Courmont96414582008-10-05 11:15:13 -07001189static int pep_recvmsg(struct kiocb *iocb, struct sock *sk,
1190 struct msghdr *msg, size_t len, int noblock,
1191 int flags, int *addr_len)
1192{
1193 struct sk_buff *skb;
1194 int err;
1195
Rémi Denis-Courmont82ecbcb2010-01-04 02:02:49 +00001196 if (flags & ~(MSG_OOB|MSG_PEEK|MSG_TRUNC|MSG_DONTWAIT|MSG_WAITALL|
1197 MSG_NOSIGNAL|MSG_CMSG_COMPAT))
1198 return -EOPNOTSUPP;
1199
Rémi Denis-Courmont96414582008-10-05 11:15:13 -07001200 if (unlikely(1 << sk->sk_state & (TCPF_LISTEN | TCPF_CLOSE)))
1201 return -ENOTCONN;
1202
Rémi Denis-Courmontc41bd97f82008-10-05 11:15:43 -07001203 if ((flags & MSG_OOB) || sock_flag(sk, SOCK_URGINLINE)) {
1204 /* Dequeue and acknowledge control request */
1205 struct pep_sock *pn = pep_sk(sk);
1206
Rémi Denis-Courmont82ecbcb2010-01-04 02:02:49 +00001207 if (flags & MSG_PEEK)
1208 return -EOPNOTSUPP;
Rémi Denis-Courmontc41bd97f82008-10-05 11:15:43 -07001209 skb = skb_dequeue(&pn->ctrlreq_queue);
1210 if (skb) {
1211 pep_ctrlreq_error(sk, skb, PN_PIPE_NO_ERROR,
1212 GFP_KERNEL);
1213 msg->msg_flags |= MSG_OOB;
1214 goto copy;
1215 }
1216 if (flags & MSG_OOB)
1217 return -EINVAL;
1218 }
1219
Rémi Denis-Courmont96414582008-10-05 11:15:13 -07001220 skb = skb_recv_datagram(sk, flags, noblock, &err);
1221 lock_sock(sk);
1222 if (skb == NULL) {
1223 if (err == -ENOTCONN && sk->sk_state == TCP_CLOSE_WAIT)
1224 err = -ECONNRESET;
1225 release_sock(sk);
1226 return err;
1227 }
1228
1229 if (sk->sk_state == TCP_ESTABLISHED)
Rémi Denis-Courmont44c9ab12011-03-08 22:44:09 +00001230 pipe_grant_credits(sk, GFP_KERNEL);
Rémi Denis-Courmont96414582008-10-05 11:15:13 -07001231 release_sock(sk);
Rémi Denis-Courmontc41bd97f82008-10-05 11:15:43 -07001232copy:
Rémi Denis-Courmont96414582008-10-05 11:15:13 -07001233 msg->msg_flags |= MSG_EOR;
Rémi Denis-Courmont96414582008-10-05 11:15:13 -07001234 if (skb->len > len)
1235 msg->msg_flags |= MSG_TRUNC;
1236 else
1237 len = skb->len;
1238
1239 err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, len);
1240 if (!err)
1241 err = (flags & MSG_TRUNC) ? skb->len : len;
1242
1243 skb_free_datagram(sk, skb);
1244 return err;
1245}
1246
1247static void pep_sock_unhash(struct sock *sk)
1248{
1249 struct pep_sock *pn = pep_sk(sk);
1250 struct sock *skparent = NULL;
1251
1252 lock_sock(sk);
Kumar Sanghvib3d62552010-10-12 20:14:43 +00001253
Rémi Denis-Courmont297edb62011-03-08 22:44:12 +00001254 if (pn->listener != NULL) {
Rémi Denis-Courmont96414582008-10-05 11:15:13 -07001255 skparent = pn->listener;
Rémi Denis-Courmont297edb62011-03-08 22:44:12 +00001256 pn->listener = NULL;
Rémi Denis-Courmont96414582008-10-05 11:15:13 -07001257 release_sock(sk);
1258
Rémi Denis-Courmont96414582008-10-05 11:15:13 -07001259 pn = pep_sk(skparent);
Rémi Denis-Courmont7dfde172010-05-26 00:44:44 +00001260 lock_sock(skparent);
1261 sk_del_node_init(sk);
1262 sk = skparent;
Rémi Denis-Courmont96414582008-10-05 11:15:13 -07001263 }
Rémi Denis-Courmont297edb62011-03-08 22:44:12 +00001264
Rémi Denis-Courmont96414582008-10-05 11:15:13 -07001265 /* Unhash a listening sock only when it is closed
1266 * and all of its active connected pipes are closed. */
1267 if (hlist_empty(&pn->hlist))
1268 pn_sock_unhash(&pn->pn_sk.sk);
1269 release_sock(sk);
1270
1271 if (skparent)
1272 sock_put(skparent);
1273}
1274
1275static struct proto pep_proto = {
1276 .close = pep_sock_close,
1277 .accept = pep_sock_accept,
Kumar Sanghvib3d62552010-10-12 20:14:43 +00001278 .connect = pep_sock_connect,
Rémi Denis-Courmont96414582008-10-05 11:15:13 -07001279 .ioctl = pep_ioctl,
1280 .init = pep_init,
Rémi Denis-Courmont02a47612008-10-05 11:16:16 -07001281 .setsockopt = pep_setsockopt,
1282 .getsockopt = pep_getsockopt,
Rémi Denis-Courmont96414582008-10-05 11:15:13 -07001283 .sendmsg = pep_sendmsg,
1284 .recvmsg = pep_recvmsg,
1285 .backlog_rcv = pep_do_rcv,
1286 .hash = pn_sock_hash,
1287 .unhash = pep_sock_unhash,
1288 .get_port = pn_sock_get_port,
1289 .obj_size = sizeof(struct pep_sock),
1290 .owner = THIS_MODULE,
1291 .name = "PNPIPE",
1292};
1293
1294static struct phonet_protocol pep_pn_proto = {
1295 .ops = &phonet_stream_ops,
1296 .prot = &pep_proto,
1297 .sock_type = SOCK_SEQPACKET,
1298};
1299
1300static int __init pep_register(void)
1301{
1302 return phonet_proto_register(PN_PROTO_PIPE, &pep_pn_proto);
1303}
1304
1305static void __exit pep_unregister(void)
1306{
1307 phonet_proto_unregister(PN_PROTO_PIPE, &pep_pn_proto);
1308}
1309
1310module_init(pep_register);
1311module_exit(pep_unregister);
1312MODULE_AUTHOR("Remi Denis-Courmont, Nokia");
1313MODULE_DESCRIPTION("Phonet pipe protocol");
1314MODULE_LICENSE("GPL");
1315MODULE_ALIAS_NET_PF_PROTO(PF_PHONET, PN_PROTO_PIPE);