aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/linux/phonet.h4
-rw-r--r--include/net/phonet/pep.h114
-rw-r--r--net/phonet/Makefile4
-rw-r--r--net/phonet/af_phonet.c3
-rw-r--r--net/phonet/pep.c908
5 files changed, 1031 insertions, 2 deletions
diff --git a/include/linux/phonet.h b/include/linux/phonet.h
index 3a027f588a4a..f92185242078 100644
--- a/include/linux/phonet.h
+++ b/include/linux/phonet.h
@@ -27,7 +27,9 @@
27#define PN_PROTO_TRANSPORT 0 27#define PN_PROTO_TRANSPORT 0
28/* Phonet datagram socket */ 28/* Phonet datagram socket */
29#define PN_PROTO_PHONET 1 29#define PN_PROTO_PHONET 1
30#define PHONET_NPROTO 2 30/* Phonet pipe */
31#define PN_PROTO_PIPE 2
32#define PHONET_NPROTO 3
31 33
32#define PNADDR_ANY 0 34#define PNADDR_ANY 0
33#define PNPORT_RESOURCE_ROUTING 0 35#define PNPORT_RESOURCE_ROUTING 0
diff --git a/include/net/phonet/pep.h b/include/net/phonet/pep.h
index b2f8c54c5333..fb024e186860 100644
--- a/include/net/phonet/pep.h
+++ b/include/net/phonet/pep.h
@@ -26,11 +26,21 @@
26struct pep_sock { 26struct pep_sock {
27 struct pn_sock pn_sk; 27 struct pn_sock pn_sk;
28 28
29 /* XXX: union-ify listening vs connected stuff ? */
29 /* Listening socket stuff: */ 30 /* Listening socket stuff: */
30 struct hlist_head ackq; 31 struct hlist_head ackq;
32 struct hlist_head hlist;
31 33
32 /* Connected socket stuff: */ 34 /* Connected socket stuff: */
35 struct sock *listener;
36 u16 peer_type; /* peer type/subtype */
37 u8 pipe_handle;
38
39 u8 rx_credits;
33 u8 tx_credits; 40 u8 tx_credits;
41 u8 rx_fc; /* RX flow control */
42 u8 tx_fc; /* TX flow control */
43 u8 init_enable; /* auto-enable at creation */
34}; 44};
35 45
36static inline struct pep_sock *pep_sk(struct sock *sk) 46static inline struct pep_sock *pep_sk(struct sock *sk)
@@ -40,4 +50,108 @@ static inline struct pep_sock *pep_sk(struct sock *sk)
40 50
41extern const struct proto_ops phonet_stream_ops; 51extern const struct proto_ops phonet_stream_ops;
42 52
53/* Pipe protocol definitions */
54struct pnpipehdr {
55 u8 utid; /* transaction ID */
56 u8 message_id;
57 u8 pipe_handle;
58 union {
59 u8 state_after_connect; /* connect request */
60 u8 state_after_reset; /* reset request */
61 u8 error_code; /* any response */
62 u8 pep_type; /* status indication */
63 u8 data[1];
64 };
65};
66#define other_pep_type data[1]
67
68static inline struct pnpipehdr *pnp_hdr(struct sk_buff *skb)
69{
70 return (struct pnpipehdr *)skb_transport_header(skb);
71}
72
73#define MAX_PNPIPE_HEADER (MAX_PHONET_HEADER + 4)
74
75enum {
76 PNS_PIPE_DATA = 0x20,
77
78 PNS_PEP_CONNECT_REQ = 0x40,
79 PNS_PEP_CONNECT_RESP,
80 PNS_PEP_DISCONNECT_REQ,
81 PNS_PEP_DISCONNECT_RESP,
82 PNS_PEP_RESET_REQ,
83 PNS_PEP_RESET_RESP,
84 PNS_PEP_ENABLE_REQ,
85 PNS_PEP_ENABLE_RESP,
86 PNS_PEP_CTRL_REQ,
87 PNS_PEP_CTRL_RESP,
88 PNS_PEP_DISABLE_REQ = 0x4C,
89 PNS_PEP_DISABLE_RESP,
90
91 PNS_PEP_STATUS_IND = 0x60,
92 PNS_PIPE_CREATED_IND,
93 PNS_PIPE_RESET_IND = 0x63,
94 PNS_PIPE_ENABLED_IND,
95 PNS_PIPE_REDIRECTED_IND,
96 PNS_PIPE_DISABLED_IND = 0x66,
97};
98
99#define PN_PIPE_INVALID_HANDLE 0xff
100#define PN_PEP_TYPE_COMMON 0x00
101
102/* Phonet pipe status indication */
103enum {
104 PN_PEP_IND_FLOW_CONTROL,
105 PN_PEP_IND_ID_MCFC_GRANT_CREDITS,
106};
107
108/* Phonet pipe error codes */
109enum {
110 PN_PIPE_NO_ERROR,
111 PN_PIPE_ERR_INVALID_PARAM,
112 PN_PIPE_ERR_INVALID_HANDLE,
113 PN_PIPE_ERR_INVALID_CTRL_ID,
114 PN_PIPE_ERR_NOT_ALLOWED,
115 PN_PIPE_ERR_PEP_IN_USE,
116 PN_PIPE_ERR_OVERLOAD,
117 PN_PIPE_ERR_DEV_DISCONNECTED,
118 PN_PIPE_ERR_TIMEOUT,
119 PN_PIPE_ERR_ALL_PIPES_IN_USE,
120 PN_PIPE_ERR_GENERAL,
121 PN_PIPE_ERR_NOT_SUPPORTED,
122};
123
124/* Phonet pipe states */
125enum {
126 PN_PIPE_DISABLE,
127 PN_PIPE_ENABLE,
128};
129
130/* Phonet pipe sub-block types */
131enum {
132 PN_PIPE_SB_CREATE_REQ_PEP_SUB_TYPE,
133 PN_PIPE_SB_CONNECT_REQ_PEP_SUB_TYPE,
134 PN_PIPE_SB_REDIRECT_REQ_PEP_SUB_TYPE,
135 PN_PIPE_SB_NEGOTIATED_FC,
136 PN_PIPE_SB_REQUIRED_FC_TX,
137 PN_PIPE_SB_PREFERRED_FC_RX,
138};
139
140/* Phonet pipe flow control models */
141enum {
142 PN_NO_FLOW_CONTROL,
143 PN_LEGACY_FLOW_CONTROL,
144 PN_ONE_CREDIT_FLOW_CONTROL,
145 PN_MULTI_CREDIT_FLOW_CONTROL,
146};
147
148#define pn_flow_safe(fc) ((fc) >> 1)
149
150/* Phonet pipe flow control states */
151enum {
152 PEP_IND_EMPTY,
153 PEP_IND_BUSY,
154 PEP_IND_READY,
155};
156
43#endif 157#endif
diff --git a/net/phonet/Makefile b/net/phonet/Makefile
index ae9c3ed5be83..505df2a04a85 100644
--- a/net/phonet/Makefile
+++ b/net/phonet/Makefile
@@ -1,4 +1,4 @@
1obj-$(CONFIG_PHONET) += phonet.o 1obj-$(CONFIG_PHONET) += phonet.o pn_pep.o
2 2
3phonet-objs := \ 3phonet-objs := \
4 pn_dev.o \ 4 pn_dev.o \
@@ -7,3 +7,5 @@ phonet-objs := \
7 datagram.o \ 7 datagram.o \
8 sysctl.o \ 8 sysctl.o \
9 af_phonet.o 9 af_phonet.o
10
11pn_pep-objs := pep.o
diff --git a/net/phonet/af_phonet.c b/net/phonet/af_phonet.c
index 0a74aeaf5adf..9e9c6fce11aa 100644
--- a/net/phonet/af_phonet.c
+++ b/net/phonet/af_phonet.c
@@ -58,6 +58,9 @@ static int pn_socket_create(struct net *net, struct socket *sock, int protocol)
58 case SOCK_DGRAM: 58 case SOCK_DGRAM:
59 protocol = PN_PROTO_PHONET; 59 protocol = PN_PROTO_PHONET;
60 break; 60 break;
61 case SOCK_SEQPACKET:
62 protocol = PN_PROTO_PIPE;
63 break;
61 default: 64 default:
62 return -EPROTONOSUPPORT; 65 return -EPROTONOSUPPORT;
63 } 66 }
diff --git a/net/phonet/pep.c b/net/phonet/pep.c
new file mode 100644
index 000000000000..c5dfecb207d2
--- /dev/null
+++ b/net/phonet/pep.c
@@ -0,0 +1,908 @@
1/*
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>
26#include <linux/socket.h>
27#include <net/sock.h>
28#include <net/tcp_states.h>
29#include <asm/ioctls.h>
30
31#include <linux/phonet.h>
32#include <net/phonet/phonet.h>
33#include <net/phonet/pep.h>
34
35/* sk_state values:
36 * TCP_CLOSE sock not in use yet
37 * TCP_CLOSE_WAIT disconnected pipe
38 * TCP_LISTEN listening pipe endpoint
39 * TCP_SYN_RECV connected pipe in disabled state
40 * TCP_ESTABLISHED connected pipe in enabled state
41 *
42 * pep_sock locking:
43 * - sk_state, ackq, hlist: sock lock needed
44 * - listener: read only
45 * - pipe_handle: read only
46 */
47
48#define CREDITS_MAX 10
49#define CREDITS_THR 7
50
51static const struct sockaddr_pn pipe_srv = {
52 .spn_family = AF_PHONET,
53 .spn_resource = 0xD9, /* pipe service */
54};
55
56#define pep_sb_size(s) (((s) + 5) & ~3) /* 2-bytes head, 32-bits aligned */
57
58/* Get the next TLV sub-block. */
59static unsigned char *pep_get_sb(struct sk_buff *skb, u8 *ptype, u8 *plen,
60 void *buf)
61{
62 void *data = NULL;
63 struct {
64 u8 sb_type;
65 u8 sb_len;
66 } *ph, h;
67 int buflen = *plen;
68
69 ph = skb_header_pointer(skb, 0, 2, &h);
70 if (ph == NULL || ph->sb_len < 2 || !pskb_may_pull(skb, ph->sb_len))
71 return NULL;
72 ph->sb_len -= 2;
73 *ptype = ph->sb_type;
74 *plen = ph->sb_len;
75
76 if (buflen > ph->sb_len)
77 buflen = ph->sb_len;
78 data = skb_header_pointer(skb, 2, buflen, buf);
79 __skb_pull(skb, 2 + ph->sb_len);
80 return data;
81}
82
83static int pep_reply(struct sock *sk, struct sk_buff *oskb,
84 u8 code, const void *data, int len, gfp_t priority)
85{
86 const struct pnpipehdr *oph = pnp_hdr(oskb);
87 struct pnpipehdr *ph;
88 struct sk_buff *skb;
89
90 skb = alloc_skb(MAX_PNPIPE_HEADER + len, priority);
91 if (!skb)
92 return -ENOMEM;
93 skb_set_owner_w(skb, sk);
94
95 skb_reserve(skb, MAX_PNPIPE_HEADER);
96 __skb_put(skb, len);
97 skb_copy_to_linear_data(skb, data, len);
98 __skb_push(skb, sizeof(*ph));
99 skb_reset_transport_header(skb);
100 ph = pnp_hdr(skb);
101 ph->utid = oph->utid;
102 ph->message_id = oph->message_id + 1; /* REQ -> RESP */
103 ph->pipe_handle = oph->pipe_handle;
104 ph->error_code = code;
105
106 return pn_skb_send(sk, skb, &pipe_srv);
107}
108
109#define PAD 0x00
110static int pep_accept_conn(struct sock *sk, struct sk_buff *skb)
111{
112 static const u8 data[20] = {
113 PAD, PAD, PAD, 2 /* sub-blocks */,
114 PN_PIPE_SB_REQUIRED_FC_TX, pep_sb_size(5), 3, PAD,
115 PN_MULTI_CREDIT_FLOW_CONTROL,
116 PN_ONE_CREDIT_FLOW_CONTROL,
117 PN_LEGACY_FLOW_CONTROL,
118 PAD,
119 PN_PIPE_SB_PREFERRED_FC_RX, pep_sb_size(5), 3, PAD,
120 PN_MULTI_CREDIT_FLOW_CONTROL,
121 PN_ONE_CREDIT_FLOW_CONTROL,
122 PN_LEGACY_FLOW_CONTROL,
123 PAD,
124 };
125
126 might_sleep();
127 return pep_reply(sk, skb, PN_PIPE_NO_ERROR, data, sizeof(data),
128 GFP_KERNEL);
129}
130
131static int pep_reject_conn(struct sock *sk, struct sk_buff *skb, u8 code)
132{
133 static const u8 data[4] = { PAD, PAD, PAD, 0 /* sub-blocks */ };
134 WARN_ON(code == PN_PIPE_NO_ERROR);
135 return pep_reply(sk, skb, code, data, sizeof(data), GFP_ATOMIC);
136}
137
138/* Control requests are not sent by the pipe service and have a specific
139 * message format. */
140static int pep_ctrlreq_error(struct sock *sk, struct sk_buff *oskb, u8 code)
141{
142 const struct pnpipehdr *oph = pnp_hdr(oskb);
143 struct sk_buff *skb;
144 struct pnpipehdr *ph;
145 struct sockaddr_pn dst;
146
147 skb = alloc_skb(MAX_PNPIPE_HEADER + 4, GFP_ATOMIC);
148 if (!skb)
149 return -ENOMEM;
150 skb_set_owner_w(skb, sk);
151
152 skb_reserve(skb, MAX_PHONET_HEADER);
153 ph = (struct pnpipehdr *)skb_put(skb, sizeof(*ph) + 4);
154
155 ph->utid = oph->utid;
156 ph->message_id = PNS_PEP_CTRL_RESP;
157 ph->pipe_handle = oph->pipe_handle;
158 ph->data[0] = oph->data[1]; /* CTRL id */
159 ph->data[1] = oph->data[0]; /* PEP type */
160 ph->data[2] = code; /* error code, at an usual offset */
161 ph->data[3] = PAD;
162 ph->data[4] = PAD;
163
164 pn_skb_get_src_sockaddr(oskb, &dst);
165 return pn_skb_send(sk, skb, &dst);
166}
167
168static int pipe_snd_status(struct sock *sk, u8 type, u8 status, gfp_t priority)
169{
170 struct pep_sock *pn = pep_sk(sk);
171 struct pnpipehdr *ph;
172 struct sk_buff *skb;
173
174 skb = alloc_skb(MAX_PNPIPE_HEADER + 4, priority);
175 if (!skb)
176 return -ENOMEM;
177 skb_set_owner_w(skb, sk);
178
179 skb_reserve(skb, MAX_PNPIPE_HEADER + 4);
180 __skb_push(skb, sizeof(*ph) + 4);
181 skb_reset_transport_header(skb);
182 ph = pnp_hdr(skb);
183 ph->utid = 0;
184 ph->message_id = PNS_PEP_STATUS_IND;
185 ph->pipe_handle = pn->pipe_handle;
186 ph->pep_type = PN_PEP_TYPE_COMMON;
187 ph->data[1] = type;
188 ph->data[2] = PAD;
189 ph->data[3] = PAD;
190 ph->data[4] = status;
191
192 return pn_skb_send(sk, skb, &pipe_srv);
193}
194
195/* Send our RX flow control information to the sender.
196 * Socket must be locked. */
197static void pipe_grant_credits(struct sock *sk)
198{
199 struct pep_sock *pn = pep_sk(sk);
200
201 BUG_ON(sk->sk_state != TCP_ESTABLISHED);
202
203 switch (pn->rx_fc) {
204 case PN_LEGACY_FLOW_CONTROL: /* TODO */
205 break;
206 case PN_ONE_CREDIT_FLOW_CONTROL:
207 pipe_snd_status(sk, PN_PEP_IND_FLOW_CONTROL,
208 PEP_IND_READY, GFP_ATOMIC);
209 pn->rx_credits = 1;
210 break;
211 case PN_MULTI_CREDIT_FLOW_CONTROL:
212 if ((pn->rx_credits + CREDITS_THR) > CREDITS_MAX)
213 break;
214 if (pipe_snd_status(sk, PN_PEP_IND_ID_MCFC_GRANT_CREDITS,
215 CREDITS_MAX - pn->rx_credits,
216 GFP_ATOMIC) == 0)
217 pn->rx_credits = CREDITS_MAX;
218 break;
219 }
220}
221
222static int pipe_rcv_status(struct sock *sk, struct sk_buff *skb)
223{
224 struct pep_sock *pn = pep_sk(sk);
225 struct pnpipehdr *hdr = pnp_hdr(skb);
226
227 if (!pskb_may_pull(skb, sizeof(*hdr) + 4))
228 return -EINVAL;
229
230 if (hdr->data[0] != PN_PEP_TYPE_COMMON) {
231 LIMIT_NETDEBUG(KERN_DEBUG"Phonet unknown PEP type: %u\n",
232 (unsigned)hdr->data[0]);
233 return -EOPNOTSUPP;
234 }
235
236 switch (hdr->data[1]) {
237 case PN_PEP_IND_FLOW_CONTROL:
238 switch (pn->tx_fc) {
239 case PN_LEGACY_FLOW_CONTROL:
240 switch (hdr->data[4]) {
241 case PEP_IND_BUSY:
242 pn->tx_credits = 0;
243 break;
244 case PEP_IND_READY:
245 pn->tx_credits = 1;
246 break;
247 }
248 break;
249 case PN_ONE_CREDIT_FLOW_CONTROL:
250 if (hdr->data[4] == PEP_IND_READY)
251 pn->tx_credits = 1;
252 break;
253 }
254 break;
255
256 case PN_PEP_IND_ID_MCFC_GRANT_CREDITS:
257 if (pn->tx_fc != PN_MULTI_CREDIT_FLOW_CONTROL)
258 break;
259 if (pn->tx_credits + hdr->data[4] > 0xff)
260 pn->tx_credits = 0xff;
261 else
262 pn->tx_credits += hdr->data[4];
263 break;
264
265 default:
266 LIMIT_NETDEBUG(KERN_DEBUG"Phonet unknown PEP indication: %u\n",
267 (unsigned)hdr->data[1]);
268 return -EOPNOTSUPP;
269 }
270 if (pn->tx_credits)
271 sk->sk_write_space(sk);
272 return 0;
273}
274
275static int pipe_rcv_created(struct sock *sk, struct sk_buff *skb)
276{
277 struct pep_sock *pn = pep_sk(sk);
278 struct pnpipehdr *hdr = pnp_hdr(skb);
279 u8 n_sb = hdr->data[0];
280
281 pn->rx_fc = pn->tx_fc = PN_LEGACY_FLOW_CONTROL;
282 __skb_pull(skb, sizeof(*hdr));
283 while (n_sb > 0) {
284 u8 type, buf[2], len = sizeof(buf);
285 u8 *data = pep_get_sb(skb, &type, &len, buf);
286
287 if (data == NULL)
288 return -EINVAL;
289 switch (type) {
290 case PN_PIPE_SB_NEGOTIATED_FC:
291 if (len < 2 || (data[0] | data[1]) > 3)
292 break;
293 pn->tx_fc = data[0] & 3;
294 pn->rx_fc = data[1] & 3;
295 break;
296 }
297 n_sb--;
298 }
299 return 0;
300}
301
302/* Queue an skb to a connected sock.
303 * Socket lock must be held. */
304static int pipe_do_rcv(struct sock *sk, struct sk_buff *skb)
305{
306 struct pep_sock *pn = pep_sk(sk);
307 struct pnpipehdr *hdr = pnp_hdr(skb);
308 int err = 0;
309
310 BUG_ON(sk->sk_state == TCP_CLOSE_WAIT);
311
312 switch (hdr->message_id) {
313 case PNS_PEP_CONNECT_REQ:
314 pep_reject_conn(sk, skb, PN_PIPE_ERR_PEP_IN_USE);
315 break;
316
317 case PNS_PEP_DISCONNECT_REQ:
318 pep_reply(sk, skb, PN_PIPE_NO_ERROR, NULL, 0, GFP_ATOMIC);
319 sk->sk_state = TCP_CLOSE_WAIT;
320 if (!sock_flag(sk, SOCK_DEAD))
321 sk->sk_state_change(sk);
322 break;
323
324 case PNS_PEP_ENABLE_REQ:
325 /* Wait for PNS_PIPE_(ENABLED|REDIRECTED)_IND */
326 pep_reply(sk, skb, PN_PIPE_NO_ERROR, NULL, 0, GFP_ATOMIC);
327 break;
328
329 case PNS_PEP_RESET_REQ:
330 switch (hdr->state_after_reset) {
331 case PN_PIPE_DISABLE:
332 pn->init_enable = 0;
333 break;
334 case PN_PIPE_ENABLE:
335 pn->init_enable = 1;
336 break;
337 default: /* not allowed to send an error here!? */
338 err = -EINVAL;
339 goto out;
340 }
341 /* fall through */
342 case PNS_PEP_DISABLE_REQ:
343 pn->tx_credits = 0;
344 pep_reply(sk, skb, PN_PIPE_NO_ERROR, NULL, 0, GFP_ATOMIC);
345 break;
346
347 case PNS_PEP_CTRL_REQ:
348 /* TODO */
349 pep_ctrlreq_error(sk, skb, PN_PIPE_NO_ERROR);
350 break;
351
352 case PNS_PIPE_DATA:
353 __skb_pull(skb, 3); /* Pipe data header */
354 if (!pn_flow_safe(pn->rx_fc)) {
355 err = sock_queue_rcv_skb(sk, skb);
356 if (!err)
357 return 0;
358 break;
359 }
360
361 if (pn->rx_credits == 0) {
362 err = -ENOBUFS;
363 break;
364 }
365 pn->rx_credits--;
366 skb->dev = NULL;
367 skb_set_owner_r(skb, sk);
368 err = skb->len;
369 skb_queue_tail(&sk->sk_receive_queue, skb);
370 if (!sock_flag(sk, SOCK_DEAD))
371 sk->sk_data_ready(sk, err);
372 return 0;
373
374 case PNS_PEP_STATUS_IND:
375 pipe_rcv_status(sk, skb);
376 break;
377
378 case PNS_PIPE_REDIRECTED_IND:
379 err = pipe_rcv_created(sk, skb);
380 break;
381
382 case PNS_PIPE_CREATED_IND:
383 err = pipe_rcv_created(sk, skb);
384 if (err)
385 break;
386 /* fall through */
387 case PNS_PIPE_RESET_IND:
388 if (!pn->init_enable)
389 break;
390 /* fall through */
391 case PNS_PIPE_ENABLED_IND:
392 if (!pn_flow_safe(pn->tx_fc)) {
393 pn->tx_credits = 1;
394 sk->sk_write_space(sk);
395 }
396 if (sk->sk_state == TCP_ESTABLISHED)
397 break; /* Nothing to do */
398 sk->sk_state = TCP_ESTABLISHED;
399 pipe_grant_credits(sk);
400 break;
401
402 case PNS_PIPE_DISABLED_IND:
403 sk->sk_state = TCP_SYN_RECV;
404 pn->rx_credits = 0;
405 break;
406
407 default:
408 LIMIT_NETDEBUG(KERN_DEBUG"Phonet unknown PEP message: %u\n",
409 hdr->message_id);
410 err = -EINVAL;
411 }
412out:
413 kfree_skb(skb);
414 return err;
415}
416
417/* Destroy connected sock. */
418static void pipe_destruct(struct sock *sk)
419{
420 skb_queue_purge(&sk->sk_receive_queue);
421}
422
423static int pep_connreq_rcv(struct sock *sk, struct sk_buff *skb)
424{
425 struct sock *newsk;
426 struct pep_sock *newpn, *pn = pep_sk(sk);
427 struct pnpipehdr *hdr;
428 struct sockaddr_pn dst;
429 u16 peer_type;
430 u8 pipe_handle, enabled, n_sb;
431
432 if (!pskb_pull(skb, sizeof(*hdr) + 4))
433 return -EINVAL;
434
435 hdr = pnp_hdr(skb);
436 pipe_handle = hdr->pipe_handle;
437 switch (hdr->state_after_connect) {
438 case PN_PIPE_DISABLE:
439 enabled = 0;
440 break;
441 case PN_PIPE_ENABLE:
442 enabled = 1;
443 break;
444 default:
445 pep_reject_conn(sk, skb, PN_PIPE_ERR_INVALID_PARAM);
446 return -EINVAL;
447 }
448 peer_type = hdr->other_pep_type << 8;
449
450 if (unlikely(sk->sk_state != TCP_LISTEN) || sk_acceptq_is_full(sk)) {
451 pep_reject_conn(sk, skb, PN_PIPE_ERR_PEP_IN_USE);
452 return -ENOBUFS;
453 }
454
455 /* Parse sub-blocks (options) */
456 n_sb = hdr->data[4];
457 while (n_sb > 0) {
458 u8 type, buf[1], len = sizeof(buf);
459 const u8 *data = pep_get_sb(skb, &type, &len, buf);
460
461 if (data == NULL)
462 return -EINVAL;
463 switch (type) {
464 case PN_PIPE_SB_CONNECT_REQ_PEP_SUB_TYPE:
465 if (len < 1)
466 return -EINVAL;
467 peer_type = (peer_type & 0xff00) | data[0];
468 break;
469 }
470 n_sb--;
471 }
472
473 skb = skb_clone(skb, GFP_ATOMIC);
474 if (!skb)
475 return -ENOMEM;
476
477 /* Create a new to-be-accepted sock */
478 newsk = sk_alloc(sock_net(sk), PF_PHONET, GFP_ATOMIC, sk->sk_prot);
479 if (!newsk) {
480 kfree_skb(skb);
481 return -ENOMEM;
482 }
483 sock_init_data(NULL, newsk);
484 newsk->sk_state = TCP_SYN_RECV;
485 newsk->sk_backlog_rcv = pipe_do_rcv;
486 newsk->sk_protocol = sk->sk_protocol;
487 newsk->sk_destruct = pipe_destruct;
488
489 newpn = pep_sk(newsk);
490 pn_skb_get_dst_sockaddr(skb, &dst);
491 newpn->pn_sk.sobject = pn_sockaddr_get_object(&dst);
492 newpn->pn_sk.resource = pn->pn_sk.resource;
493 newpn->pipe_handle = pipe_handle;
494 newpn->peer_type = peer_type;
495 newpn->rx_credits = newpn->tx_credits = 0;
496 newpn->rx_fc = newpn->tx_fc = PN_LEGACY_FLOW_CONTROL;
497 newpn->init_enable = enabled;
498
499 BUG_ON(!skb_queue_empty(&newsk->sk_receive_queue));
500 skb_queue_head(&newsk->sk_receive_queue, skb);
501 if (!sock_flag(sk, SOCK_DEAD))
502 sk->sk_data_ready(sk, 0);
503
504 sk_acceptq_added(sk);
505 sk_add_node(newsk, &pn->ackq);
506 return 0;
507}
508
509/* Listening sock must be locked */
510static struct sock *pep_find_pipe(const struct hlist_head *hlist,
511 const struct sockaddr_pn *dst,
512 u8 pipe_handle)
513{
514 struct hlist_node *node;
515 struct sock *sknode;
516 u16 dobj = pn_sockaddr_get_object(dst);
517
518 sk_for_each(sknode, node, hlist) {
519 struct pep_sock *pnnode = pep_sk(sknode);
520
521 /* Ports match, but addresses might not: */
522 if (pnnode->pn_sk.sobject != dobj)
523 continue;
524 if (pnnode->pipe_handle != pipe_handle)
525 continue;
526 if (sknode->sk_state == TCP_CLOSE_WAIT)
527 continue;
528
529 sock_hold(sknode);
530 return sknode;
531 }
532 return NULL;
533}
534
535/*
536 * Deliver an skb to a listening sock.
537 * Socket lock must be held.
538 * We then queue the skb to the right connected sock (if any).
539 */
540static int pep_do_rcv(struct sock *sk, struct sk_buff *skb)
541{
542 struct pep_sock *pn = pep_sk(sk);
543 struct sock *sknode;
544 struct pnpipehdr *hdr = pnp_hdr(skb);
545 struct sockaddr_pn dst;
546 int err = NET_RX_SUCCESS;
547 u8 pipe_handle;
548
549 if (!pskb_may_pull(skb, sizeof(*hdr)))
550 goto drop;
551
552 hdr = pnp_hdr(skb);
553 pipe_handle = hdr->pipe_handle;
554 if (pipe_handle == PN_PIPE_INVALID_HANDLE)
555 goto drop;
556
557 pn_skb_get_dst_sockaddr(skb, &dst);
558
559 /* Look for an existing pipe handle */
560 sknode = pep_find_pipe(&pn->hlist, &dst, pipe_handle);
561 if (sknode)
562 return sk_receive_skb(sknode, skb, 1);
563
564 /* Look for a pipe handle pending accept */
565 sknode = pep_find_pipe(&pn->ackq, &dst, pipe_handle);
566 if (sknode) {
567 sock_put(sknode);
568 if (net_ratelimit())
569 printk(KERN_WARNING"Phonet unconnected PEP ignored");
570 err = NET_RX_DROP;
571 goto drop;
572 }
573
574 switch (hdr->message_id) {
575 case PNS_PEP_CONNECT_REQ:
576 err = pep_connreq_rcv(sk, skb);
577 break;
578
579 case PNS_PEP_DISCONNECT_REQ:
580 pep_reply(sk, skb, PN_PIPE_NO_ERROR, NULL, 0, GFP_ATOMIC);
581 break;
582
583 case PNS_PEP_CTRL_REQ:
584 pep_ctrlreq_error(sk, skb, PN_PIPE_INVALID_HANDLE);
585 break;
586
587 case PNS_PEP_RESET_REQ:
588 case PNS_PEP_ENABLE_REQ:
589 case PNS_PEP_DISABLE_REQ:
590 /* invalid handle is not even allowed here! */
591 default:
592 err = NET_RX_DROP;
593 }
594drop:
595 kfree_skb(skb);
596 return err;
597}
598
599/* associated socket ceases to exist */
600static void pep_sock_close(struct sock *sk, long timeout)
601{
602 struct pep_sock *pn = pep_sk(sk);
603
604 sk_common_release(sk);
605
606 lock_sock(sk);
607 if (sk->sk_state == TCP_LISTEN) {
608 /* Destroy the listen queue */
609 struct sock *sknode;
610 struct hlist_node *p, *n;
611
612 sk_for_each_safe(sknode, p, n, &pn->ackq)
613 sk_del_node_init(sknode);
614 sk->sk_state = TCP_CLOSE;
615 }
616 release_sock(sk);
617}
618
619static int pep_wait_connreq(struct sock *sk, int noblock)
620{
621 struct task_struct *tsk = current;
622 struct pep_sock *pn = pep_sk(sk);
623 long timeo = sock_rcvtimeo(sk, noblock);
624
625 for (;;) {
626 DEFINE_WAIT(wait);
627
628 if (sk->sk_state != TCP_LISTEN)
629 return -EINVAL;
630 if (!hlist_empty(&pn->ackq))
631 break;
632 if (!timeo)
633 return -EWOULDBLOCK;
634 if (signal_pending(tsk))
635 return sock_intr_errno(timeo);
636
637 prepare_to_wait_exclusive(&sk->sk_socket->wait, &wait,
638 TASK_INTERRUPTIBLE);
639 release_sock(sk);
640 timeo = schedule_timeout(timeo);
641 lock_sock(sk);
642 finish_wait(&sk->sk_socket->wait, &wait);
643 }
644
645 return 0;
646}
647
648static struct sock *pep_sock_accept(struct sock *sk, int flags, int *errp)
649{
650 struct pep_sock *pn = pep_sk(sk);
651 struct sock *newsk = NULL;
652 struct sk_buff *oskb;
653 int err;
654
655 lock_sock(sk);
656 err = pep_wait_connreq(sk, flags & O_NONBLOCK);
657 if (err)
658 goto out;
659
660 newsk = __sk_head(&pn->ackq);
661
662 oskb = skb_dequeue(&newsk->sk_receive_queue);
663 err = pep_accept_conn(newsk, oskb);
664 if (err) {
665 skb_queue_head(&newsk->sk_receive_queue, oskb);
666 newsk = NULL;
667 goto out;
668 }
669
670 sock_hold(sk);
671 pep_sk(newsk)->listener = sk;
672
673 sock_hold(newsk);
674 sk_del_node_init(newsk);
675 sk_acceptq_removed(sk);
676 sk_add_node(newsk, &pn->hlist);
677 __sock_put(newsk);
678
679out:
680 release_sock(sk);
681 *errp = err;
682 return newsk;
683}
684
685static int pep_ioctl(struct sock *sk, int cmd, unsigned long arg)
686{
687 int answ;
688
689 switch (cmd) {
690 case SIOCINQ:
691 if (sk->sk_state == TCP_LISTEN)
692 return -EINVAL;
693
694 lock_sock(sk);
695 if (!skb_queue_empty(&sk->sk_receive_queue))
696 answ = skb_peek(&sk->sk_receive_queue)->len;
697 else
698 answ = 0;
699 release_sock(sk);
700 return put_user(answ, (int __user *)arg);
701 }
702
703 return -ENOIOCTLCMD;
704}
705
706static int pep_init(struct sock *sk)
707{
708 struct pep_sock *pn = pep_sk(sk);
709
710 INIT_HLIST_HEAD(&pn->ackq);
711 INIT_HLIST_HEAD(&pn->hlist);
712 pn->pipe_handle = PN_PIPE_INVALID_HANDLE;
713 return 0;
714}
715
716static int pep_sendmsg(struct kiocb *iocb, struct sock *sk,
717 struct msghdr *msg, size_t len)
718{
719 struct pep_sock *pn = pep_sk(sk);
720 struct sk_buff *skb = NULL;
721 struct pnpipehdr *ph;
722 long timeo;
723 int flags = msg->msg_flags;
724 int err, done;
725
726 if (msg->msg_flags & MSG_OOB || !(msg->msg_flags & MSG_EOR))
727 return -EOPNOTSUPP;
728
729 lock_sock(sk);
730 timeo = sock_sndtimeo(sk, flags & MSG_DONTWAIT);
731 if ((1 << sk->sk_state) & (TCPF_LISTEN|TCPF_CLOSE)) {
732 err = -ENOTCONN;
733 goto out;
734 }
735 if (sk->sk_state != TCP_ESTABLISHED) {
736 /* Wait until the pipe gets to enabled state */
737disabled:
738 err = sk_stream_wait_connect(sk, &timeo);
739 if (err)
740 goto out;
741
742 if (sk->sk_state == TCP_CLOSE_WAIT) {
743 err = -ECONNRESET;
744 goto out;
745 }
746 }
747 BUG_ON(sk->sk_state != TCP_ESTABLISHED);
748
749 /* Wait until flow control allows TX */
750 done = pn->tx_credits > 0;
751 while (!done) {
752 DEFINE_WAIT(wait);
753
754 if (!timeo) {
755 err = -EAGAIN;
756 goto out;
757 }
758 if (signal_pending(current)) {
759 err = sock_intr_errno(timeo);
760 goto out;
761 }
762
763 prepare_to_wait(&sk->sk_socket->wait, &wait,
764 TASK_INTERRUPTIBLE);
765 done = sk_wait_event(sk, &timeo, pn->tx_credits > 0);
766 finish_wait(&sk->sk_socket->wait, &wait);
767
768 if (sk->sk_state != TCP_ESTABLISHED)
769 goto disabled;
770 }
771
772 if (!skb) {
773 skb = sock_alloc_send_skb(sk, MAX_PNPIPE_HEADER + len,
774 flags & MSG_DONTWAIT, &err);
775 if (skb == NULL)
776 goto out;
777 skb_reserve(skb, MAX_PHONET_HEADER + 3);
778
779 if (sk->sk_state != TCP_ESTABLISHED || !pn->tx_credits)
780 goto disabled; /* sock_alloc_send_skb might sleep */
781 }
782
783 err = memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len);
784 if (err < 0)
785 goto out;
786
787 __skb_push(skb, 3);
788 skb_reset_transport_header(skb);
789 ph = pnp_hdr(skb);
790 ph->utid = 0;
791 ph->message_id = PNS_PIPE_DATA;
792 ph->pipe_handle = pn->pipe_handle;
793 if (pn_flow_safe(pn->tx_fc)) /* credit-based flow control */
794 pn->tx_credits--;
795
796 err = pn_skb_send(sk, skb, &pipe_srv);
797 if (err >= 0)
798 err = len; /* success! */
799 skb = NULL;
800out:
801 release_sock(sk);
802 kfree_skb(skb);
803 return err;
804}
805
806static int pep_recvmsg(struct kiocb *iocb, struct sock *sk,
807 struct msghdr *msg, size_t len, int noblock,
808 int flags, int *addr_len)
809{
810 struct sk_buff *skb;
811 int err;
812
813 if (unlikely(flags & MSG_OOB))
814 return -EOPNOTSUPP;
815 if (unlikely(1 << sk->sk_state & (TCPF_LISTEN | TCPF_CLOSE)))
816 return -ENOTCONN;
817
818 skb = skb_recv_datagram(sk, flags, noblock, &err);
819 lock_sock(sk);
820 if (skb == NULL) {
821 if (err == -ENOTCONN && sk->sk_state == TCP_CLOSE_WAIT)
822 err = -ECONNRESET;
823 release_sock(sk);
824 return err;
825 }
826
827 if (sk->sk_state == TCP_ESTABLISHED)
828 pipe_grant_credits(sk);
829 release_sock(sk);
830
831 msg->msg_flags |= MSG_EOR;
832
833 if (skb->len > len)
834 msg->msg_flags |= MSG_TRUNC;
835 else
836 len = skb->len;
837
838 err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, len);
839 if (!err)
840 err = (flags & MSG_TRUNC) ? skb->len : len;
841
842 skb_free_datagram(sk, skb);
843 return err;
844}
845
846static void pep_sock_unhash(struct sock *sk)
847{
848 struct pep_sock *pn = pep_sk(sk);
849 struct sock *skparent = NULL;
850
851 lock_sock(sk);
852 if ((1 << sk->sk_state) & ~(TCPF_CLOSE|TCPF_LISTEN)) {
853 skparent = pn->listener;
854 sk_del_node_init(sk);
855 release_sock(sk);
856
857 sk = skparent;
858 pn = pep_sk(skparent);
859 lock_sock(sk);
860 }
861 /* Unhash a listening sock only when it is closed
862 * and all of its active connected pipes are closed. */
863 if (hlist_empty(&pn->hlist))
864 pn_sock_unhash(&pn->pn_sk.sk);
865 release_sock(sk);
866
867 if (skparent)
868 sock_put(skparent);
869}
870
871static struct proto pep_proto = {
872 .close = pep_sock_close,
873 .accept = pep_sock_accept,
874 .ioctl = pep_ioctl,
875 .init = pep_init,
876 .sendmsg = pep_sendmsg,
877 .recvmsg = pep_recvmsg,
878 .backlog_rcv = pep_do_rcv,
879 .hash = pn_sock_hash,
880 .unhash = pep_sock_unhash,
881 .get_port = pn_sock_get_port,
882 .obj_size = sizeof(struct pep_sock),
883 .owner = THIS_MODULE,
884 .name = "PNPIPE",
885};
886
887static struct phonet_protocol pep_pn_proto = {
888 .ops = &phonet_stream_ops,
889 .prot = &pep_proto,
890 .sock_type = SOCK_SEQPACKET,
891};
892
893static int __init pep_register(void)
894{
895 return phonet_proto_register(PN_PROTO_PIPE, &pep_pn_proto);
896}
897
898static void __exit pep_unregister(void)
899{
900 phonet_proto_unregister(PN_PROTO_PIPE, &pep_pn_proto);
901}
902
903module_init(pep_register);
904module_exit(pep_unregister);
905MODULE_AUTHOR("Remi Denis-Courmont, Nokia");
906MODULE_DESCRIPTION("Phonet pipe protocol");
907MODULE_LICENSE("GPL");
908MODULE_ALIAS_NET_PF_PROTO(PF_PHONET, PN_PROTO_PIPE);