diff options
Diffstat (limited to 'net/phonet/socket.c')
-rw-r--r-- | net/phonet/socket.c | 289 |
1 files changed, 288 insertions, 1 deletions
diff --git a/net/phonet/socket.c b/net/phonet/socket.c index 6e9848bf0370..25f746d20c1f 100644 --- a/net/phonet/socket.c +++ b/net/phonet/socket.c | |||
@@ -158,6 +158,7 @@ void pn_sock_unhash(struct sock *sk) | |||
158 | spin_lock_bh(&pnsocks.lock); | 158 | spin_lock_bh(&pnsocks.lock); |
159 | sk_del_node_init(sk); | 159 | sk_del_node_init(sk); |
160 | spin_unlock_bh(&pnsocks.lock); | 160 | spin_unlock_bh(&pnsocks.lock); |
161 | pn_sock_unbind_all_res(sk); | ||
161 | } | 162 | } |
162 | EXPORT_SYMBOL(pn_sock_unhash); | 163 | EXPORT_SYMBOL(pn_sock_unhash); |
163 | 164 | ||
@@ -224,6 +225,101 @@ static int pn_socket_autobind(struct socket *sock) | |||
224 | return 0; /* socket was already bound */ | 225 | return 0; /* socket was already bound */ |
225 | } | 226 | } |
226 | 227 | ||
228 | #ifdef CONFIG_PHONET_PIPECTRLR | ||
229 | static int pn_socket_connect(struct socket *sock, struct sockaddr *addr, | ||
230 | int len, int flags) | ||
231 | { | ||
232 | struct sock *sk = sock->sk; | ||
233 | struct sockaddr_pn *spn = (struct sockaddr_pn *)addr; | ||
234 | long timeo; | ||
235 | int err; | ||
236 | |||
237 | if (len < sizeof(struct sockaddr_pn)) | ||
238 | return -EINVAL; | ||
239 | if (spn->spn_family != AF_PHONET) | ||
240 | return -EAFNOSUPPORT; | ||
241 | |||
242 | lock_sock(sk); | ||
243 | |||
244 | switch (sock->state) { | ||
245 | case SS_UNCONNECTED: | ||
246 | sk->sk_state = TCP_CLOSE; | ||
247 | break; | ||
248 | case SS_CONNECTING: | ||
249 | switch (sk->sk_state) { | ||
250 | case TCP_SYN_RECV: | ||
251 | sock->state = SS_CONNECTED; | ||
252 | err = -EISCONN; | ||
253 | goto out; | ||
254 | case TCP_CLOSE: | ||
255 | err = -EALREADY; | ||
256 | if (flags & O_NONBLOCK) | ||
257 | goto out; | ||
258 | goto wait_connect; | ||
259 | } | ||
260 | break; | ||
261 | case SS_CONNECTED: | ||
262 | switch (sk->sk_state) { | ||
263 | case TCP_SYN_RECV: | ||
264 | err = -EISCONN; | ||
265 | goto out; | ||
266 | case TCP_CLOSE: | ||
267 | sock->state = SS_UNCONNECTED; | ||
268 | break; | ||
269 | } | ||
270 | break; | ||
271 | case SS_DISCONNECTING: | ||
272 | case SS_FREE: | ||
273 | break; | ||
274 | } | ||
275 | sk->sk_state = TCP_CLOSE; | ||
276 | sk_stream_kill_queues(sk); | ||
277 | |||
278 | sock->state = SS_CONNECTING; | ||
279 | err = sk->sk_prot->connect(sk, addr, len); | ||
280 | if (err < 0) { | ||
281 | sock->state = SS_UNCONNECTED; | ||
282 | sk->sk_state = TCP_CLOSE; | ||
283 | goto out; | ||
284 | } | ||
285 | |||
286 | err = -EINPROGRESS; | ||
287 | wait_connect: | ||
288 | if (sk->sk_state != TCP_SYN_RECV && (flags & O_NONBLOCK)) | ||
289 | goto out; | ||
290 | |||
291 | timeo = sock_sndtimeo(sk, flags & O_NONBLOCK); | ||
292 | release_sock(sk); | ||
293 | |||
294 | err = -ERESTARTSYS; | ||
295 | timeo = wait_event_interruptible_timeout(*sk_sleep(sk), | ||
296 | sk->sk_state != TCP_CLOSE, | ||
297 | timeo); | ||
298 | |||
299 | lock_sock(sk); | ||
300 | if (timeo < 0) | ||
301 | goto out; /* -ERESTARTSYS */ | ||
302 | |||
303 | err = -ETIMEDOUT; | ||
304 | if (timeo == 0 && sk->sk_state != TCP_SYN_RECV) | ||
305 | goto out; | ||
306 | |||
307 | if (sk->sk_state != TCP_SYN_RECV) { | ||
308 | sock->state = SS_UNCONNECTED; | ||
309 | err = sock_error(sk); | ||
310 | if (!err) | ||
311 | err = -ECONNREFUSED; | ||
312 | goto out; | ||
313 | } | ||
314 | sock->state = SS_CONNECTED; | ||
315 | err = 0; | ||
316 | |||
317 | out: | ||
318 | release_sock(sk); | ||
319 | return err; | ||
320 | } | ||
321 | #endif | ||
322 | |||
227 | static int pn_socket_accept(struct socket *sock, struct socket *newsock, | 323 | static int pn_socket_accept(struct socket *sock, struct socket *newsock, |
228 | int flags) | 324 | int flags) |
229 | { | 325 | { |
@@ -281,7 +377,9 @@ static unsigned int pn_socket_poll(struct file *file, struct socket *sock, | |||
281 | if (!mask && sk->sk_state == TCP_CLOSE_WAIT) | 377 | if (!mask && sk->sk_state == TCP_CLOSE_WAIT) |
282 | return POLLHUP; | 378 | return POLLHUP; |
283 | 379 | ||
284 | if (sk->sk_state == TCP_ESTABLISHED && atomic_read(&pn->tx_credits)) | 380 | if (sk->sk_state == TCP_ESTABLISHED && |
381 | atomic_read(&sk->sk_wmem_alloc) < sk->sk_sndbuf && | ||
382 | atomic_read(&pn->tx_credits)) | ||
285 | mask |= POLLOUT | POLLWRNORM | POLLWRBAND; | 383 | mask |= POLLOUT | POLLWRNORM | POLLWRBAND; |
286 | 384 | ||
287 | return mask; | 385 | return mask; |
@@ -390,7 +488,11 @@ const struct proto_ops phonet_stream_ops = { | |||
390 | .owner = THIS_MODULE, | 488 | .owner = THIS_MODULE, |
391 | .release = pn_socket_release, | 489 | .release = pn_socket_release, |
392 | .bind = pn_socket_bind, | 490 | .bind = pn_socket_bind, |
491 | #ifdef CONFIG_PHONET_PIPECTRLR | ||
492 | .connect = pn_socket_connect, | ||
493 | #else | ||
393 | .connect = sock_no_connect, | 494 | .connect = sock_no_connect, |
495 | #endif | ||
394 | .socketpair = sock_no_socketpair, | 496 | .socketpair = sock_no_socketpair, |
395 | .accept = pn_socket_accept, | 497 | .accept = pn_socket_accept, |
396 | .getname = pn_socket_getname, | 498 | .getname = pn_socket_getname, |
@@ -563,3 +665,188 @@ const struct file_operations pn_sock_seq_fops = { | |||
563 | .release = seq_release_net, | 665 | .release = seq_release_net, |
564 | }; | 666 | }; |
565 | #endif | 667 | #endif |
668 | |||
669 | static struct { | ||
670 | struct sock *sk[256]; | ||
671 | } pnres; | ||
672 | |||
673 | /* | ||
674 | * Find and hold socket based on resource. | ||
675 | */ | ||
676 | struct sock *pn_find_sock_by_res(struct net *net, u8 res) | ||
677 | { | ||
678 | struct sock *sk; | ||
679 | |||
680 | if (!net_eq(net, &init_net)) | ||
681 | return NULL; | ||
682 | |||
683 | rcu_read_lock(); | ||
684 | sk = rcu_dereference(pnres.sk[res]); | ||
685 | if (sk) | ||
686 | sock_hold(sk); | ||
687 | rcu_read_unlock(); | ||
688 | return sk; | ||
689 | } | ||
690 | |||
691 | static DEFINE_MUTEX(resource_mutex); | ||
692 | |||
693 | int pn_sock_bind_res(struct sock *sk, u8 res) | ||
694 | { | ||
695 | int ret = -EADDRINUSE; | ||
696 | |||
697 | if (!net_eq(sock_net(sk), &init_net)) | ||
698 | return -ENOIOCTLCMD; | ||
699 | if (!capable(CAP_SYS_ADMIN)) | ||
700 | return -EPERM; | ||
701 | if (pn_socket_autobind(sk->sk_socket)) | ||
702 | return -EAGAIN; | ||
703 | |||
704 | mutex_lock(&resource_mutex); | ||
705 | if (pnres.sk[res] == NULL) { | ||
706 | sock_hold(sk); | ||
707 | rcu_assign_pointer(pnres.sk[res], sk); | ||
708 | ret = 0; | ||
709 | } | ||
710 | mutex_unlock(&resource_mutex); | ||
711 | return ret; | ||
712 | } | ||
713 | |||
714 | int pn_sock_unbind_res(struct sock *sk, u8 res) | ||
715 | { | ||
716 | int ret = -ENOENT; | ||
717 | |||
718 | if (!capable(CAP_SYS_ADMIN)) | ||
719 | return -EPERM; | ||
720 | |||
721 | mutex_lock(&resource_mutex); | ||
722 | if (pnres.sk[res] == sk) { | ||
723 | rcu_assign_pointer(pnres.sk[res], NULL); | ||
724 | ret = 0; | ||
725 | } | ||
726 | mutex_unlock(&resource_mutex); | ||
727 | |||
728 | if (ret == 0) { | ||
729 | synchronize_rcu(); | ||
730 | sock_put(sk); | ||
731 | } | ||
732 | return ret; | ||
733 | } | ||
734 | |||
735 | void pn_sock_unbind_all_res(struct sock *sk) | ||
736 | { | ||
737 | unsigned res, match = 0; | ||
738 | |||
739 | mutex_lock(&resource_mutex); | ||
740 | for (res = 0; res < 256; res++) { | ||
741 | if (pnres.sk[res] == sk) { | ||
742 | rcu_assign_pointer(pnres.sk[res], NULL); | ||
743 | match++; | ||
744 | } | ||
745 | } | ||
746 | mutex_unlock(&resource_mutex); | ||
747 | |||
748 | if (match == 0) | ||
749 | return; | ||
750 | synchronize_rcu(); | ||
751 | while (match > 0) { | ||
752 | sock_put(sk); | ||
753 | match--; | ||
754 | } | ||
755 | } | ||
756 | |||
757 | #ifdef CONFIG_PROC_FS | ||
758 | static struct sock **pn_res_get_idx(struct seq_file *seq, loff_t pos) | ||
759 | { | ||
760 | struct net *net = seq_file_net(seq); | ||
761 | unsigned i; | ||
762 | |||
763 | if (!net_eq(net, &init_net)) | ||
764 | return NULL; | ||
765 | |||
766 | for (i = 0; i < 256; i++) { | ||
767 | if (pnres.sk[i] == NULL) | ||
768 | continue; | ||
769 | if (!pos) | ||
770 | return pnres.sk + i; | ||
771 | pos--; | ||
772 | } | ||
773 | return NULL; | ||
774 | } | ||
775 | |||
776 | static struct sock **pn_res_get_next(struct seq_file *seq, struct sock **sk) | ||
777 | { | ||
778 | struct net *net = seq_file_net(seq); | ||
779 | unsigned i; | ||
780 | |||
781 | BUG_ON(!net_eq(net, &init_net)); | ||
782 | |||
783 | for (i = (sk - pnres.sk) + 1; i < 256; i++) | ||
784 | if (pnres.sk[i]) | ||
785 | return pnres.sk + i; | ||
786 | return NULL; | ||
787 | } | ||
788 | |||
789 | static void *pn_res_seq_start(struct seq_file *seq, loff_t *pos) | ||
790 | __acquires(resource_mutex) | ||
791 | { | ||
792 | mutex_lock(&resource_mutex); | ||
793 | return *pos ? pn_res_get_idx(seq, *pos - 1) : SEQ_START_TOKEN; | ||
794 | } | ||
795 | |||
796 | static void *pn_res_seq_next(struct seq_file *seq, void *v, loff_t *pos) | ||
797 | { | ||
798 | struct sock **sk; | ||
799 | |||
800 | if (v == SEQ_START_TOKEN) | ||
801 | sk = pn_res_get_idx(seq, 0); | ||
802 | else | ||
803 | sk = pn_res_get_next(seq, v); | ||
804 | (*pos)++; | ||
805 | return sk; | ||
806 | } | ||
807 | |||
808 | static void pn_res_seq_stop(struct seq_file *seq, void *v) | ||
809 | __releases(resource_mutex) | ||
810 | { | ||
811 | mutex_unlock(&resource_mutex); | ||
812 | } | ||
813 | |||
814 | static int pn_res_seq_show(struct seq_file *seq, void *v) | ||
815 | { | ||
816 | int len; | ||
817 | |||
818 | if (v == SEQ_START_TOKEN) | ||
819 | seq_printf(seq, "%s%n", "rs uid inode", &len); | ||
820 | else { | ||
821 | struct sock **psk = v; | ||
822 | struct sock *sk = *psk; | ||
823 | |||
824 | seq_printf(seq, "%02X %5d %lu%n", | ||
825 | (int) (psk - pnres.sk), sock_i_uid(sk), | ||
826 | sock_i_ino(sk), &len); | ||
827 | } | ||
828 | seq_printf(seq, "%*s\n", 63 - len, ""); | ||
829 | return 0; | ||
830 | } | ||
831 | |||
832 | static const struct seq_operations pn_res_seq_ops = { | ||
833 | .start = pn_res_seq_start, | ||
834 | .next = pn_res_seq_next, | ||
835 | .stop = pn_res_seq_stop, | ||
836 | .show = pn_res_seq_show, | ||
837 | }; | ||
838 | |||
839 | static int pn_res_open(struct inode *inode, struct file *file) | ||
840 | { | ||
841 | return seq_open_net(inode, file, &pn_res_seq_ops, | ||
842 | sizeof(struct seq_net_private)); | ||
843 | } | ||
844 | |||
845 | const struct file_operations pn_res_seq_fops = { | ||
846 | .owner = THIS_MODULE, | ||
847 | .open = pn_res_open, | ||
848 | .read = seq_read, | ||
849 | .llseek = seq_lseek, | ||
850 | .release = seq_release_net, | ||
851 | }; | ||
852 | #endif | ||