aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRémi Denis-Courmont <remi.denis-courmont@nokia.com>2008-10-05 14:15:13 -0400
committerDavid S. Miller <davem@davemloft.net>2008-10-05 14:15:13 -0400
commit9641458d3ec42def729fde64669abf07f3220cd5 (patch)
tree414a31324c800ad9ecc8665de7ffaa9eee963a0e
parent9995a32b4d14dcda2f8df58030526bee91114c16 (diff)
Phonet: Pipe End Point for Phonet Pipes protocol
This protocol provides some connection handling and negotiated congestion control. Nokia cellular modems use it for bulk transfers. It provides packet boundaries (hence SOCK_SEQPACKET). Congestion control is per packet rather per byte, so we do not re-use the generic socket memory accounting. Signed-off-by: Rémi Denis-Courmont <remi.denis-courmont@nokia.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-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);