diff options
author | Rémi Denis-Courmont <remi.denis-courmont@nokia.com> | 2008-10-05 14:16:16 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2008-10-05 14:16:16 -0400 |
commit | 02a47617cdce440f60c71a51f3a93f9f5fcc5a7a (patch) | |
tree | 2f65d9978345b8eafdaf926a3342424a21c6e57a | |
parent | c41bd97f815720f9404f97da0c4f4400b52c243d (diff) |
Phonet: implement GPRS virtual interface over PEP socket
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.h | 8 | ||||
-rw-r--r-- | include/linux/socket.h | 1 | ||||
-rw-r--r-- | include/net/phonet/gprs.h | 38 | ||||
-rw-r--r-- | include/net/phonet/pep.h | 1 | ||||
-rw-r--r-- | net/phonet/Makefile | 2 | ||||
-rw-r--r-- | net/phonet/pep-gprs.c | 347 | ||||
-rw-r--r-- | net/phonet/pep.c | 161 | ||||
-rw-r--r-- | net/phonet/socket.c | 8 |
8 files changed, 550 insertions, 16 deletions
diff --git a/include/linux/phonet.h b/include/linux/phonet.h index f92185242078..c9609f9aedac 100644 --- a/include/linux/phonet.h +++ b/include/linux/phonet.h | |||
@@ -31,9 +31,17 @@ | |||
31 | #define PN_PROTO_PIPE 2 | 31 | #define PN_PROTO_PIPE 2 |
32 | #define PHONET_NPROTO 3 | 32 | #define PHONET_NPROTO 3 |
33 | 33 | ||
34 | /* Socket options for SOL_PNPIPE level */ | ||
35 | #define PNPIPE_ENCAP 1 | ||
36 | #define PNPIPE_IFINDEX 2 | ||
37 | |||
34 | #define PNADDR_ANY 0 | 38 | #define PNADDR_ANY 0 |
35 | #define PNPORT_RESOURCE_ROUTING 0 | 39 | #define PNPORT_RESOURCE_ROUTING 0 |
36 | 40 | ||
41 | /* Values for PNPIPE_ENCAP option */ | ||
42 | #define PNPIPE_ENCAP_NONE 0 | ||
43 | #define PNPIPE_ENCAP_IP 1 | ||
44 | |||
37 | /* ioctls */ | 45 | /* ioctls */ |
38 | #define SIOCPNGETOBJECT (SIOCPROTOPRIVATE + 0) | 46 | #define SIOCPNGETOBJECT (SIOCPROTOPRIVATE + 0) |
39 | 47 | ||
diff --git a/include/linux/socket.h b/include/linux/socket.h index 818ca33bf79f..20fc4bbfca42 100644 --- a/include/linux/socket.h +++ b/include/linux/socket.h | |||
@@ -297,6 +297,7 @@ struct ucred { | |||
297 | #define SOL_RXRPC 272 | 297 | #define SOL_RXRPC 272 |
298 | #define SOL_PPPOL2TP 273 | 298 | #define SOL_PPPOL2TP 273 |
299 | #define SOL_BLUETOOTH 274 | 299 | #define SOL_BLUETOOTH 274 |
300 | #define SOL_PNPIPE 275 | ||
300 | 301 | ||
301 | /* IPX options */ | 302 | /* IPX options */ |
302 | #define IPX_TYPE 1 | 303 | #define IPX_TYPE 1 |
diff --git a/include/net/phonet/gprs.h b/include/net/phonet/gprs.h new file mode 100644 index 000000000000..928daf595beb --- /dev/null +++ b/include/net/phonet/gprs.h | |||
@@ -0,0 +1,38 @@ | |||
1 | /* | ||
2 | * File: pep_gprs.h | ||
3 | * | ||
4 | * GPRS over Phonet pipe 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 | #ifndef NET_PHONET_GPRS_H | ||
26 | #define NET_PHONET_GPRS_H | ||
27 | |||
28 | struct sock; | ||
29 | struct sk_buff; | ||
30 | |||
31 | int pep_writeable(struct sock *sk); | ||
32 | int pep_write(struct sock *sk, struct sk_buff *skb); | ||
33 | struct sk_buff *pep_read(struct sock *sk); | ||
34 | |||
35 | int gprs_attach(struct sock *sk); | ||
36 | void gprs_detach(struct sock *sk); | ||
37 | |||
38 | #endif | ||
diff --git a/include/net/phonet/pep.h b/include/net/phonet/pep.h index 4d79564850ae..fcd793030e4d 100644 --- a/include/net/phonet/pep.h +++ b/include/net/phonet/pep.h | |||
@@ -35,6 +35,7 @@ struct pep_sock { | |||
35 | struct sock *listener; | 35 | struct sock *listener; |
36 | struct sk_buff_head ctrlreq_queue; | 36 | struct sk_buff_head ctrlreq_queue; |
37 | #define PNPIPE_CTRLREQ_MAX 10 | 37 | #define PNPIPE_CTRLREQ_MAX 10 |
38 | int ifindex; | ||
38 | u16 peer_type; /* peer type/subtype */ | 39 | u16 peer_type; /* peer type/subtype */ |
39 | u8 pipe_handle; | 40 | u8 pipe_handle; |
40 | 41 | ||
diff --git a/net/phonet/Makefile b/net/phonet/Makefile index 505df2a04a85..d62bbba649b3 100644 --- a/net/phonet/Makefile +++ b/net/phonet/Makefile | |||
@@ -8,4 +8,4 @@ phonet-objs := \ | |||
8 | sysctl.o \ | 8 | sysctl.o \ |
9 | af_phonet.o | 9 | af_phonet.o |
10 | 10 | ||
11 | pn_pep-objs := pep.o | 11 | pn_pep-objs := pep.o pep-gprs.o |
diff --git a/net/phonet/pep-gprs.c b/net/phonet/pep-gprs.c new file mode 100644 index 000000000000..9978afbd9f2a --- /dev/null +++ b/net/phonet/pep-gprs.c | |||
@@ -0,0 +1,347 @@ | |||
1 | /* | ||
2 | * File: pep-gprs.c | ||
3 | * | ||
4 | * GPRS over Phonet pipe 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/netdevice.h> | ||
27 | #include <linux/if_ether.h> | ||
28 | #include <linux/if_arp.h> | ||
29 | #include <net/sock.h> | ||
30 | |||
31 | #include <linux/if_phonet.h> | ||
32 | #include <net/tcp_states.h> | ||
33 | #include <net/phonet/gprs.h> | ||
34 | |||
35 | #define GPRS_DEFAULT_MTU 1400 | ||
36 | |||
37 | struct gprs_dev { | ||
38 | struct sock *sk; | ||
39 | void (*old_state_change)(struct sock *); | ||
40 | void (*old_data_ready)(struct sock *, int); | ||
41 | void (*old_write_space)(struct sock *); | ||
42 | |||
43 | struct net_device *net; | ||
44 | struct net_device_stats stats; | ||
45 | |||
46 | struct sk_buff_head tx_queue; | ||
47 | struct work_struct tx_work; | ||
48 | spinlock_t tx_lock; | ||
49 | unsigned tx_max; | ||
50 | }; | ||
51 | |||
52 | static int gprs_type_trans(struct sk_buff *skb) | ||
53 | { | ||
54 | const u8 *pvfc; | ||
55 | u8 buf; | ||
56 | |||
57 | pvfc = skb_header_pointer(skb, 0, 1, &buf); | ||
58 | if (!pvfc) | ||
59 | return 0; | ||
60 | /* Look at IP version field */ | ||
61 | switch (*pvfc >> 4) { | ||
62 | case 4: | ||
63 | return htons(ETH_P_IP); | ||
64 | case 6: | ||
65 | return htons(ETH_P_IPV6); | ||
66 | } | ||
67 | return 0; | ||
68 | } | ||
69 | |||
70 | /* | ||
71 | * Socket callbacks | ||
72 | */ | ||
73 | |||
74 | static void gprs_state_change(struct sock *sk) | ||
75 | { | ||
76 | struct gprs_dev *dev = sk->sk_user_data; | ||
77 | |||
78 | if (sk->sk_state == TCP_CLOSE_WAIT) { | ||
79 | netif_stop_queue(dev->net); | ||
80 | netif_carrier_off(dev->net); | ||
81 | } | ||
82 | } | ||
83 | |||
84 | static int gprs_recv(struct gprs_dev *dev, struct sk_buff *skb) | ||
85 | { | ||
86 | int err = 0; | ||
87 | u16 protocol = gprs_type_trans(skb); | ||
88 | |||
89 | if (!protocol) { | ||
90 | err = -EINVAL; | ||
91 | goto drop; | ||
92 | } | ||
93 | |||
94 | if (likely(skb_headroom(skb) & 3)) { | ||
95 | struct sk_buff *rskb, *fs; | ||
96 | int flen = 0; | ||
97 | |||
98 | /* Phonet Pipe data header is misaligned (3 bytes), | ||
99 | * so wrap the IP packet as a single fragment of an head-less | ||
100 | * socket buffer. The network stack will pull what it needs, | ||
101 | * but at least, the whole IP payload is not memcpy'd. */ | ||
102 | rskb = netdev_alloc_skb(dev->net, 0); | ||
103 | if (!rskb) { | ||
104 | err = -ENOBUFS; | ||
105 | goto drop; | ||
106 | } | ||
107 | skb_shinfo(rskb)->frag_list = skb; | ||
108 | rskb->len += skb->len; | ||
109 | rskb->data_len += rskb->len; | ||
110 | rskb->truesize += rskb->len; | ||
111 | |||
112 | /* Avoid nested fragments */ | ||
113 | for (fs = skb_shinfo(skb)->frag_list; fs; fs = fs->next) | ||
114 | flen += fs->len; | ||
115 | skb->next = skb_shinfo(skb)->frag_list; | ||
116 | skb_shinfo(skb)->frag_list = NULL; | ||
117 | skb->len -= flen; | ||
118 | skb->data_len -= flen; | ||
119 | skb->truesize -= flen; | ||
120 | |||
121 | skb = rskb; | ||
122 | } | ||
123 | |||
124 | skb->protocol = protocol; | ||
125 | skb_reset_mac_header(skb); | ||
126 | skb->dev = dev->net; | ||
127 | |||
128 | if (likely(dev->net->flags & IFF_UP)) { | ||
129 | dev->stats.rx_packets++; | ||
130 | dev->stats.rx_bytes += skb->len; | ||
131 | netif_rx(skb); | ||
132 | skb = NULL; | ||
133 | } else | ||
134 | err = -ENODEV; | ||
135 | |||
136 | drop: | ||
137 | if (skb) { | ||
138 | dev_kfree_skb(skb); | ||
139 | dev->stats.rx_dropped++; | ||
140 | } | ||
141 | return err; | ||
142 | } | ||
143 | |||
144 | static void gprs_data_ready(struct sock *sk, int len) | ||
145 | { | ||
146 | struct gprs_dev *dev = sk->sk_user_data; | ||
147 | struct sk_buff *skb; | ||
148 | |||
149 | while ((skb = pep_read(sk)) != NULL) { | ||
150 | skb_orphan(skb); | ||
151 | gprs_recv(dev, skb); | ||
152 | } | ||
153 | } | ||
154 | |||
155 | static void gprs_write_space(struct sock *sk) | ||
156 | { | ||
157 | struct gprs_dev *dev = sk->sk_user_data; | ||
158 | unsigned credits = pep_writeable(sk); | ||
159 | |||
160 | spin_lock_bh(&dev->tx_lock); | ||
161 | dev->tx_max = credits; | ||
162 | if (credits > skb_queue_len(&dev->tx_queue)) | ||
163 | netif_wake_queue(dev->net); | ||
164 | spin_unlock_bh(&dev->tx_lock); | ||
165 | } | ||
166 | |||
167 | /* | ||
168 | * Network device callbacks | ||
169 | */ | ||
170 | |||
171 | static int gprs_xmit(struct sk_buff *skb, struct net_device *net) | ||
172 | { | ||
173 | struct gprs_dev *dev = netdev_priv(net); | ||
174 | |||
175 | switch (skb->protocol) { | ||
176 | case htons(ETH_P_IP): | ||
177 | case htons(ETH_P_IPV6): | ||
178 | break; | ||
179 | default: | ||
180 | dev_kfree_skb(skb); | ||
181 | return 0; | ||
182 | } | ||
183 | |||
184 | spin_lock(&dev->tx_lock); | ||
185 | if (likely(skb_queue_len(&dev->tx_queue) < dev->tx_max)) { | ||
186 | skb_queue_tail(&dev->tx_queue, skb); | ||
187 | skb = NULL; | ||
188 | } | ||
189 | if (skb_queue_len(&dev->tx_queue) >= dev->tx_max) | ||
190 | netif_stop_queue(net); | ||
191 | spin_unlock(&dev->tx_lock); | ||
192 | |||
193 | schedule_work(&dev->tx_work); | ||
194 | if (unlikely(skb)) | ||
195 | dev_kfree_skb(skb); | ||
196 | return 0; | ||
197 | } | ||
198 | |||
199 | static void gprs_tx(struct work_struct *work) | ||
200 | { | ||
201 | struct gprs_dev *dev = container_of(work, struct gprs_dev, tx_work); | ||
202 | struct sock *sk = dev->sk; | ||
203 | struct sk_buff *skb; | ||
204 | |||
205 | while ((skb = skb_dequeue(&dev->tx_queue)) != NULL) { | ||
206 | int err; | ||
207 | |||
208 | dev->stats.tx_bytes += skb->len; | ||
209 | dev->stats.tx_packets++; | ||
210 | |||
211 | skb_orphan(skb); | ||
212 | skb_set_owner_w(skb, sk); | ||
213 | |||
214 | lock_sock(sk); | ||
215 | err = pep_write(sk, skb); | ||
216 | if (err) { | ||
217 | LIMIT_NETDEBUG(KERN_WARNING"%s: TX error (%d)\n", | ||
218 | dev->net->name, err); | ||
219 | dev->stats.tx_aborted_errors++; | ||
220 | dev->stats.tx_errors++; | ||
221 | } | ||
222 | release_sock(sk); | ||
223 | } | ||
224 | |||
225 | lock_sock(sk); | ||
226 | gprs_write_space(sk); | ||
227 | release_sock(sk); | ||
228 | } | ||
229 | |||
230 | static int gprs_set_mtu(struct net_device *net, int new_mtu) | ||
231 | { | ||
232 | if ((new_mtu < 576) || (new_mtu > (PHONET_MAX_MTU - 11))) | ||
233 | return -EINVAL; | ||
234 | |||
235 | net->mtu = new_mtu; | ||
236 | return 0; | ||
237 | } | ||
238 | |||
239 | static struct net_device_stats *gprs_get_stats(struct net_device *net) | ||
240 | { | ||
241 | struct gprs_dev *dev = netdev_priv(net); | ||
242 | |||
243 | return &dev->stats; | ||
244 | } | ||
245 | |||
246 | static void gprs_setup(struct net_device *net) | ||
247 | { | ||
248 | net->features = NETIF_F_FRAGLIST; | ||
249 | net->type = ARPHRD_NONE; | ||
250 | net->flags = IFF_POINTOPOINT | IFF_NOARP; | ||
251 | net->mtu = GPRS_DEFAULT_MTU; | ||
252 | net->hard_header_len = 0; | ||
253 | net->addr_len = 0; | ||
254 | net->tx_queue_len = 10; | ||
255 | |||
256 | net->destructor = free_netdev; | ||
257 | net->hard_start_xmit = gprs_xmit; /* mandatory */ | ||
258 | net->change_mtu = gprs_set_mtu; | ||
259 | net->get_stats = gprs_get_stats; | ||
260 | } | ||
261 | |||
262 | /* | ||
263 | * External interface | ||
264 | */ | ||
265 | |||
266 | /* | ||
267 | * Attach a GPRS interface to a datagram socket. | ||
268 | * Returns the interface index on success, negative error code on error. | ||
269 | */ | ||
270 | int gprs_attach(struct sock *sk) | ||
271 | { | ||
272 | static const char ifname[] = "gprs%d"; | ||
273 | struct gprs_dev *dev; | ||
274 | struct net_device *net; | ||
275 | int err; | ||
276 | |||
277 | if (unlikely(sk->sk_type == SOCK_STREAM)) | ||
278 | return -EINVAL; /* need packet boundaries */ | ||
279 | |||
280 | /* Create net device */ | ||
281 | net = alloc_netdev(sizeof(*dev), ifname, gprs_setup); | ||
282 | if (!net) | ||
283 | return -ENOMEM; | ||
284 | dev = netdev_priv(net); | ||
285 | dev->net = net; | ||
286 | dev->tx_max = 0; | ||
287 | spin_lock_init(&dev->tx_lock); | ||
288 | skb_queue_head_init(&dev->tx_queue); | ||
289 | INIT_WORK(&dev->tx_work, gprs_tx); | ||
290 | |||
291 | netif_stop_queue(net); | ||
292 | err = register_netdev(net); | ||
293 | if (err) { | ||
294 | free_netdev(net); | ||
295 | return err; | ||
296 | } | ||
297 | |||
298 | lock_sock(sk); | ||
299 | if (unlikely(sk->sk_user_data)) { | ||
300 | err = -EBUSY; | ||
301 | goto out_rel; | ||
302 | } | ||
303 | if (unlikely((1 << sk->sk_state & (TCPF_CLOSE|TCPF_LISTEN)) || | ||
304 | sock_flag(sk, SOCK_DEAD))) { | ||
305 | err = -EINVAL; | ||
306 | goto out_rel; | ||
307 | } | ||
308 | sk->sk_user_data = dev; | ||
309 | dev->old_state_change = sk->sk_state_change; | ||
310 | dev->old_data_ready = sk->sk_data_ready; | ||
311 | dev->old_write_space = sk->sk_write_space; | ||
312 | sk->sk_state_change = gprs_state_change; | ||
313 | sk->sk_data_ready = gprs_data_ready; | ||
314 | sk->sk_write_space = gprs_write_space; | ||
315 | release_sock(sk); | ||
316 | |||
317 | sock_hold(sk); | ||
318 | dev->sk = sk; | ||
319 | |||
320 | printk(KERN_DEBUG"%s: attached\n", net->name); | ||
321 | gprs_write_space(sk); /* kick off TX */ | ||
322 | return net->ifindex; | ||
323 | |||
324 | out_rel: | ||
325 | release_sock(sk); | ||
326 | unregister_netdev(net); | ||
327 | return err; | ||
328 | } | ||
329 | |||
330 | void gprs_detach(struct sock *sk) | ||
331 | { | ||
332 | struct gprs_dev *dev = sk->sk_user_data; | ||
333 | struct net_device *net = dev->net; | ||
334 | |||
335 | lock_sock(sk); | ||
336 | sk->sk_user_data = NULL; | ||
337 | sk->sk_state_change = dev->old_state_change; | ||
338 | sk->sk_data_ready = dev->old_data_ready; | ||
339 | sk->sk_write_space = dev->old_write_space; | ||
340 | release_sock(sk); | ||
341 | |||
342 | printk(KERN_DEBUG"%s: detached\n", net->name); | ||
343 | unregister_netdev(net); | ||
344 | flush_scheduled_work(); | ||
345 | sock_put(sk); | ||
346 | skb_queue_purge(&dev->tx_queue); | ||
347 | } | ||
diff --git a/net/phonet/pep.c b/net/phonet/pep.c index d564d07cb797..bc6d50f83249 100644 --- a/net/phonet/pep.c +++ b/net/phonet/pep.c | |||
@@ -31,6 +31,7 @@ | |||
31 | #include <linux/phonet.h> | 31 | #include <linux/phonet.h> |
32 | #include <net/phonet/phonet.h> | 32 | #include <net/phonet/phonet.h> |
33 | #include <net/phonet/pep.h> | 33 | #include <net/phonet/pep.h> |
34 | #include <net/phonet/gprs.h> | ||
34 | 35 | ||
35 | /* sk_state values: | 36 | /* sk_state values: |
36 | * TCP_CLOSE sock not in use yet | 37 | * TCP_CLOSE sock not in use yet |
@@ -612,6 +613,7 @@ drop: | |||
612 | static void pep_sock_close(struct sock *sk, long timeout) | 613 | static void pep_sock_close(struct sock *sk, long timeout) |
613 | { | 614 | { |
614 | struct pep_sock *pn = pep_sk(sk); | 615 | struct pep_sock *pn = pep_sk(sk); |
616 | int ifindex = 0; | ||
615 | 617 | ||
616 | sk_common_release(sk); | 618 | sk_common_release(sk); |
617 | 619 | ||
@@ -625,7 +627,12 @@ static void pep_sock_close(struct sock *sk, long timeout) | |||
625 | sk_del_node_init(sknode); | 627 | sk_del_node_init(sknode); |
626 | sk->sk_state = TCP_CLOSE; | 628 | sk->sk_state = TCP_CLOSE; |
627 | } | 629 | } |
630 | ifindex = pn->ifindex; | ||
631 | pn->ifindex = 0; | ||
628 | release_sock(sk); | 632 | release_sock(sk); |
633 | |||
634 | if (ifindex) | ||
635 | gprs_detach(sk); | ||
629 | } | 636 | } |
630 | 637 | ||
631 | static int pep_wait_connreq(struct sock *sk, int noblock) | 638 | static int pep_wait_connreq(struct sock *sk, int noblock) |
@@ -730,12 +737,107 @@ static int pep_init(struct sock *sk) | |||
730 | return 0; | 737 | return 0; |
731 | } | 738 | } |
732 | 739 | ||
740 | static int pep_setsockopt(struct sock *sk, int level, int optname, | ||
741 | char __user *optval, int optlen) | ||
742 | { | ||
743 | struct pep_sock *pn = pep_sk(sk); | ||
744 | int val = 0, err = 0; | ||
745 | |||
746 | if (level != SOL_PNPIPE) | ||
747 | return -ENOPROTOOPT; | ||
748 | if (optlen >= sizeof(int)) { | ||
749 | if (get_user(val, (int __user *) optval)) | ||
750 | return -EFAULT; | ||
751 | } | ||
752 | |||
753 | lock_sock(sk); | ||
754 | switch (optname) { | ||
755 | case PNPIPE_ENCAP: | ||
756 | if (val && val != PNPIPE_ENCAP_IP) { | ||
757 | err = -EINVAL; | ||
758 | break; | ||
759 | } | ||
760 | if (!pn->ifindex == !val) | ||
761 | break; /* Nothing to do! */ | ||
762 | if (!capable(CAP_NET_ADMIN)) { | ||
763 | err = -EPERM; | ||
764 | break; | ||
765 | } | ||
766 | if (val) { | ||
767 | release_sock(sk); | ||
768 | err = gprs_attach(sk); | ||
769 | if (err > 0) { | ||
770 | pn->ifindex = err; | ||
771 | err = 0; | ||
772 | } | ||
773 | } else { | ||
774 | pn->ifindex = 0; | ||
775 | release_sock(sk); | ||
776 | gprs_detach(sk); | ||
777 | err = 0; | ||
778 | } | ||
779 | goto out_norel; | ||
780 | default: | ||
781 | err = -ENOPROTOOPT; | ||
782 | } | ||
783 | release_sock(sk); | ||
784 | |||
785 | out_norel: | ||
786 | return err; | ||
787 | } | ||
788 | |||
789 | static int pep_getsockopt(struct sock *sk, int level, int optname, | ||
790 | char __user *optval, int __user *optlen) | ||
791 | { | ||
792 | struct pep_sock *pn = pep_sk(sk); | ||
793 | int len, val; | ||
794 | |||
795 | if (level != SOL_PNPIPE) | ||
796 | return -ENOPROTOOPT; | ||
797 | if (get_user(len, optlen)) | ||
798 | return -EFAULT; | ||
799 | |||
800 | switch (optname) { | ||
801 | case PNPIPE_ENCAP: | ||
802 | val = pn->ifindex ? PNPIPE_ENCAP_IP : PNPIPE_ENCAP_NONE; | ||
803 | break; | ||
804 | case PNPIPE_IFINDEX: | ||
805 | val = pn->ifindex; | ||
806 | break; | ||
807 | default: | ||
808 | return -ENOPROTOOPT; | ||
809 | } | ||
810 | |||
811 | len = min_t(unsigned int, sizeof(int), len); | ||
812 | if (put_user(len, optlen)) | ||
813 | return -EFAULT; | ||
814 | if (put_user(val, (int __user *) optval)) | ||
815 | return -EFAULT; | ||
816 | return 0; | ||
817 | } | ||
818 | |||
819 | static int pipe_skb_send(struct sock *sk, struct sk_buff *skb) | ||
820 | { | ||
821 | struct pep_sock *pn = pep_sk(sk); | ||
822 | struct pnpipehdr *ph; | ||
823 | |||
824 | skb_push(skb, 3); | ||
825 | skb_reset_transport_header(skb); | ||
826 | ph = pnp_hdr(skb); | ||
827 | ph->utid = 0; | ||
828 | ph->message_id = PNS_PIPE_DATA; | ||
829 | ph->pipe_handle = pn->pipe_handle; | ||
830 | if (pn_flow_safe(pn->tx_fc) && pn->tx_credits) | ||
831 | pn->tx_credits--; | ||
832 | |||
833 | return pn_skb_send(sk, skb, &pipe_srv); | ||
834 | } | ||
835 | |||
733 | static int pep_sendmsg(struct kiocb *iocb, struct sock *sk, | 836 | static int pep_sendmsg(struct kiocb *iocb, struct sock *sk, |
734 | struct msghdr *msg, size_t len) | 837 | struct msghdr *msg, size_t len) |
735 | { | 838 | { |
736 | struct pep_sock *pn = pep_sk(sk); | 839 | struct pep_sock *pn = pep_sk(sk); |
737 | struct sk_buff *skb = NULL; | 840 | struct sk_buff *skb = NULL; |
738 | struct pnpipehdr *ph; | ||
739 | long timeo; | 841 | long timeo; |
740 | int flags = msg->msg_flags; | 842 | int flags = msg->msg_flags; |
741 | int err, done; | 843 | int err, done; |
@@ -801,16 +903,7 @@ disabled: | |||
801 | if (err < 0) | 903 | if (err < 0) |
802 | goto out; | 904 | goto out; |
803 | 905 | ||
804 | __skb_push(skb, 3); | 906 | err = pipe_skb_send(sk, skb); |
805 | skb_reset_transport_header(skb); | ||
806 | ph = pnp_hdr(skb); | ||
807 | ph->utid = 0; | ||
808 | ph->message_id = PNS_PIPE_DATA; | ||
809 | ph->pipe_handle = pn->pipe_handle; | ||
810 | if (pn_flow_safe(pn->tx_fc)) /* credit-based flow control */ | ||
811 | pn->tx_credits--; | ||
812 | |||
813 | err = pn_skb_send(sk, skb, &pipe_srv); | ||
814 | if (err >= 0) | 907 | if (err >= 0) |
815 | err = len; /* success! */ | 908 | err = len; /* success! */ |
816 | skb = NULL; | 909 | skb = NULL; |
@@ -820,6 +913,50 @@ out: | |||
820 | return err; | 913 | return err; |
821 | } | 914 | } |
822 | 915 | ||
916 | int pep_writeable(struct sock *sk) | ||
917 | { | ||
918 | struct pep_sock *pn = pep_sk(sk); | ||
919 | |||
920 | return (sk->sk_state == TCP_ESTABLISHED) ? pn->tx_credits : 0; | ||
921 | } | ||
922 | |||
923 | int pep_write(struct sock *sk, struct sk_buff *skb) | ||
924 | { | ||
925 | struct sk_buff *rskb, *fs; | ||
926 | int flen = 0; | ||
927 | |||
928 | rskb = alloc_skb(MAX_PNPIPE_HEADER, GFP_ATOMIC); | ||
929 | if (!rskb) { | ||
930 | kfree_skb(skb); | ||
931 | return -ENOMEM; | ||
932 | } | ||
933 | skb_shinfo(rskb)->frag_list = skb; | ||
934 | rskb->len += skb->len; | ||
935 | rskb->data_len += rskb->len; | ||
936 | rskb->truesize += rskb->len; | ||
937 | |||
938 | /* Avoid nested fragments */ | ||
939 | for (fs = skb_shinfo(skb)->frag_list; fs; fs = fs->next) | ||
940 | flen += fs->len; | ||
941 | skb->next = skb_shinfo(skb)->frag_list; | ||
942 | skb_shinfo(skb)->frag_list = NULL; | ||
943 | skb->len -= flen; | ||
944 | skb->data_len -= flen; | ||
945 | skb->truesize -= flen; | ||
946 | |||
947 | skb_reserve(rskb, MAX_PHONET_HEADER + 3); | ||
948 | return pipe_skb_send(sk, rskb); | ||
949 | } | ||
950 | |||
951 | struct sk_buff *pep_read(struct sock *sk) | ||
952 | { | ||
953 | struct sk_buff *skb = skb_dequeue(&sk->sk_receive_queue); | ||
954 | |||
955 | if (sk->sk_state == TCP_ESTABLISHED) | ||
956 | pipe_grant_credits(sk); | ||
957 | return skb; | ||
958 | } | ||
959 | |||
823 | static int pep_recvmsg(struct kiocb *iocb, struct sock *sk, | 960 | static int pep_recvmsg(struct kiocb *iocb, struct sock *sk, |
824 | struct msghdr *msg, size_t len, int noblock, | 961 | struct msghdr *msg, size_t len, int noblock, |
825 | int flags, int *addr_len) | 962 | int flags, int *addr_len) |
@@ -902,6 +1039,8 @@ static struct proto pep_proto = { | |||
902 | .accept = pep_sock_accept, | 1039 | .accept = pep_sock_accept, |
903 | .ioctl = pep_ioctl, | 1040 | .ioctl = pep_ioctl, |
904 | .init = pep_init, | 1041 | .init = pep_init, |
1042 | .setsockopt = pep_setsockopt, | ||
1043 | .getsockopt = pep_getsockopt, | ||
905 | .sendmsg = pep_sendmsg, | 1044 | .sendmsg = pep_sendmsg, |
906 | .recvmsg = pep_recvmsg, | 1045 | .recvmsg = pep_recvmsg, |
907 | .backlog_rcv = pep_do_rcv, | 1046 | .backlog_rcv = pep_do_rcv, |
diff --git a/net/phonet/socket.c b/net/phonet/socket.c index a9c3d1f2e9db..d81740187fb4 100644 --- a/net/phonet/socket.c +++ b/net/phonet/socket.c | |||
@@ -342,11 +342,11 @@ const struct proto_ops phonet_stream_ops = { | |||
342 | .ioctl = pn_socket_ioctl, | 342 | .ioctl = pn_socket_ioctl, |
343 | .listen = pn_socket_listen, | 343 | .listen = pn_socket_listen, |
344 | .shutdown = sock_no_shutdown, | 344 | .shutdown = sock_no_shutdown, |
345 | .setsockopt = sock_no_setsockopt, | 345 | .setsockopt = sock_common_setsockopt, |
346 | .getsockopt = sock_no_getsockopt, | 346 | .getsockopt = sock_common_getsockopt, |
347 | #ifdef CONFIG_COMPAT | 347 | #ifdef CONFIG_COMPAT |
348 | .compat_setsockopt = sock_no_setsockopt, | 348 | .compat_setsockopt = compat_sock_common_setsockopt, |
349 | .compat_getsockopt = compat_sock_no_getsockopt, | 349 | .compat_getsockopt = compat_sock_common_getsockopt, |
350 | #endif | 350 | #endif |
351 | .sendmsg = pn_socket_sendmsg, | 351 | .sendmsg = pn_socket_sendmsg, |
352 | .recvmsg = sock_common_recvmsg, | 352 | .recvmsg = sock_common_recvmsg, |