diff options
Diffstat (limited to 'net/phonet')
| -rw-r--r-- | net/phonet/pep.c | 172 | ||||
| -rw-r--r-- | net/phonet/socket.c | 102 | 
2 files changed, 141 insertions, 133 deletions
| diff --git a/net/phonet/pep.c b/net/phonet/pep.c index abfb795af142..671effb4ea15 100644 --- a/net/phonet/pep.c +++ b/net/phonet/pep.c @@ -136,7 +136,6 @@ static int pep_indicate(struct sock *sk, u8 id, u8 code,  #define PAD 0x00 -#ifdef CONFIG_PHONET_PIPECTRLR  static int pipe_handler_request(struct sock *sk, u8 id, u8 code,  				const void *data, int len)  { @@ -168,11 +167,7 @@ static int pipe_handler_send_created_ind(struct sock *sk)  				data, 4, GFP_ATOMIC);  } -static int pipe_handler_send_ind(struct sock *sk, u8 id) -{ -	return pep_indicate(sk, id, PAD, NULL, 0, GFP_ATOMIC); -} - +#ifdef CONFIG_PHONET_PIPECTRLR  static int pipe_handler_enable_pipe(struct sock *sk, int enable)  {  	u8 id = enable ? PNS_PEP_ENABLE_REQ : PNS_PEP_DISABLE_REQ; @@ -376,32 +371,11 @@ static int pipe_do_rcv(struct sock *sk, struct sk_buff *skb)  			sk->sk_state_change(sk);  		break; -#ifdef CONFIG_PHONET_PIPECTRLR -	case PNS_PEP_DISCONNECT_RESP: -		sk->sk_state = TCP_CLOSE; -		break; -#endif -  	case PNS_PEP_ENABLE_REQ:  		/* Wait for PNS_PIPE_(ENABLED|REDIRECTED)_IND */  		pep_reply(sk, skb, PN_PIPE_NO_ERROR, NULL, 0, GFP_ATOMIC);  		break; -#ifdef CONFIG_PHONET_PIPECTRLR -	case PNS_PEP_ENABLE_RESP: -		pipe_handler_send_ind(sk, PNS_PIPE_ENABLED_IND); - -		if (!pn_flow_safe(pn->tx_fc)) { -			atomic_set(&pn->tx_credits, 1); -			sk->sk_write_space(sk); -		} -		if (sk->sk_state == TCP_ESTABLISHED) -			break; /* Nothing to do */ -		sk->sk_state = TCP_ESTABLISHED; -		pipe_grant_credits(sk, GFP_ATOMIC); -		break; -#endif -  	case PNS_PEP_RESET_REQ:  		switch (hdr->state_after_reset) {  		case PN_PIPE_DISABLE: @@ -420,15 +394,6 @@ static int pipe_do_rcv(struct sock *sk, struct sk_buff *skb)  		pep_reply(sk, skb, PN_PIPE_NO_ERROR, NULL, 0, GFP_ATOMIC);  		break; -#ifdef CONFIG_PHONET_PIPECTRLR -	case PNS_PEP_DISABLE_RESP: -		atomic_set(&pn->tx_credits, 0); -		pipe_handler_send_ind(sk, PNS_PIPE_DISABLED_IND); -		sk->sk_state = TCP_SYN_RECV; -		pn->rx_credits = 0; -		break; -#endif -  	case PNS_PEP_CTRL_REQ:  		if (skb_queue_len(&pn->ctrlreq_queue) >= PNPIPE_CTRLREQ_MAX) {  			atomic_inc(&sk->sk_drops); @@ -521,7 +486,6 @@ static void pipe_destruct(struct sock *sk)  	skb_queue_purge(&pn->ctrlreq_queue);  } -#ifdef CONFIG_PHONET_PIPECTRLR  static u8 pipe_negotiate_fc(const u8 *fcs, unsigned n)  {  	unsigned i; @@ -546,6 +510,8 @@ static int pep_connresp_rcv(struct sock *sk, struct sk_buff *skb)  		return -EINVAL;  	hdr = pnp_hdr(skb); +	if (hdr->error_code != PN_PIPE_NO_ERROR) +		return -ECONNREFUSED;  	/* Parse sub-blocks */  	n_sb = hdr->data[4]; @@ -573,14 +539,74 @@ static int pep_connresp_rcv(struct sock *sk, struct sk_buff *skb)  		n_sb--;  	} -	sk->sk_state = TCP_SYN_RECV; -	sk->sk_backlog_rcv = pipe_do_rcv; -	pn->rx_credits = 0; -	sk->sk_state_change(sk); -  	return pipe_handler_send_created_ind(sk);  } -#endif + +/* Queue an skb to an actively connected sock. + * Socket lock must be held. */ +static int pipe_handler_do_rcv(struct sock *sk, struct sk_buff *skb) +{ +	struct pep_sock *pn = pep_sk(sk); +	struct pnpipehdr *hdr = pnp_hdr(skb); +	int err = NET_RX_SUCCESS; + +	switch (hdr->message_id) { +	case PNS_PIPE_ALIGNED_DATA: +		__skb_pull(skb, 1); +		/* fall through */ +	case PNS_PIPE_DATA: +		__skb_pull(skb, 3); /* Pipe data header */ +		if (!pn_flow_safe(pn->rx_fc)) { +			err = sock_queue_rcv_skb(sk, skb); +			if (!err) +				return NET_RX_SUCCESS; +			err = NET_RX_DROP; +			break; +		} + +		if (pn->rx_credits == 0) { +			atomic_inc(&sk->sk_drops); +			err = NET_RX_DROP; +			break; +		} +		pn->rx_credits--; +		skb->dev = NULL; +		skb_set_owner_r(skb, sk); +		err = skb->len; +		skb_queue_tail(&sk->sk_receive_queue, skb); +		if (!sock_flag(sk, SOCK_DEAD)) +			sk->sk_data_ready(sk, err); +		return NET_RX_SUCCESS; + +	case PNS_PEP_CONNECT_RESP: +		if (sk->sk_state != TCP_SYN_SENT) +			break; +		if (!sock_flag(sk, SOCK_DEAD)) +			sk->sk_state_change(sk); +		if (pep_connresp_rcv(sk, skb)) { +			sk->sk_state = TCP_CLOSE_WAIT; +			break; +		} + +		sk->sk_state = TCP_ESTABLISHED; +		if (!pn_flow_safe(pn->tx_fc)) { +			atomic_set(&pn->tx_credits, 1); +			sk->sk_write_space(sk); +		} +		pipe_grant_credits(sk, GFP_ATOMIC); +		break; + +	case PNS_PEP_DISCONNECT_RESP: +		/* sock should already be dead, nothing to do */ +		break; + +	case PNS_PEP_STATUS_IND: +		pipe_rcv_status(sk, skb); +		break; +	} +	kfree_skb(skb); +	return err; +}  /* Listening sock must be locked */  static struct sock *pep_find_pipe(const struct hlist_head *hlist, @@ -649,12 +675,6 @@ static int pep_do_rcv(struct sock *sk, struct sk_buff *skb)  			sk->sk_data_ready(sk, 0);  		return NET_RX_SUCCESS; -#ifdef CONFIG_PHONET_PIPECTRLR -	case PNS_PEP_CONNECT_RESP: -		pep_connresp_rcv(sk, skb); -		break; -#endif -  	case PNS_PEP_DISCONNECT_REQ:  		pep_reply(sk, skb, PN_PIPE_NO_ERROR, NULL, 0, GFP_ATOMIC);  		break; @@ -667,15 +687,19 @@ static int pep_do_rcv(struct sock *sk, struct sk_buff *skb)  	case PNS_PEP_ENABLE_REQ:  	case PNS_PEP_DISABLE_REQ:  		/* invalid handle is not even allowed here! */ -	default:  		break; + +	default: +		if ((1 << sk->sk_state) +				& ~(TCPF_CLOSE|TCPF_LISTEN|TCPF_CLOSE_WAIT)) +			/* actively connected socket */ +			return pipe_handler_do_rcv(sk, skb);  	}  drop:  	kfree_skb(skb);  	return NET_RX_SUCCESS;  } -#ifndef CONFIG_PHONET_PIPECTRLR  static int pipe_do_remove(struct sock *sk)  {  	struct pep_sock *pn = pep_sk(sk); @@ -693,7 +717,6 @@ static int pipe_do_remove(struct sock *sk)  	ph->data[0] = PAD;  	return pn_skb_send(sk, skb, NULL);  } -#endif  /* associated socket ceases to exist */  static void pep_sock_close(struct sock *sk, long timeout) @@ -706,13 +729,12 @@ static void pep_sock_close(struct sock *sk, long timeout)  	lock_sock(sk);  	if ((1 << sk->sk_state) & (TCPF_SYN_RECV|TCPF_ESTABLISHED)) { -#ifndef CONFIG_PHONET_PIPECTRLR -		/* Forcefully remove dangling Phonet pipe */ -		pipe_do_remove(sk); -#else -		/* send pep disconnect request */ -		pipe_handler_request(sk, PNS_PEP_DISCONNECT_REQ, PAD, NULL, 0); -#endif +		if (sk->sk_backlog_rcv == pipe_do_rcv) +			/* Forcefully remove dangling Phonet pipe */ +			pipe_do_remove(sk); +		else +			pipe_handler_request(sk, PNS_PEP_DISCONNECT_REQ, PAD, +						NULL, 0);  	}  	sk->sk_state = TCP_CLOSE; @@ -844,20 +866,22 @@ drop:  	return newsk;  } -#ifdef CONFIG_PHONET_PIPECTRLR  static int pep_sock_connect(struct sock *sk, struct sockaddr *addr, int len)  {  	struct pep_sock *pn = pep_sk(sk); -	const struct sockaddr_pn *spn = (struct sockaddr_pn *)addr; +	int err;  	u8 data[4] = { 0 /* sub-blocks */, PAD, PAD, PAD }; -	pn->pn_sk.dobject = pn_sockaddr_get_object(spn); -	pn->pn_sk.resource = pn_sockaddr_get_resource(spn);  	pn->pipe_handle = 1; /* anything but INVALID_HANDLE */ -	return pipe_handler_request(sk, PNS_PEP_CONNECT_REQ, -					PN_PIPE_DISABLE, data, 4); +	err = pipe_handler_request(sk, PNS_PEP_CONNECT_REQ, +					PN_PIPE_ENABLE, data, 4); +	if (err) { +		pn->pipe_handle = PN_PIPE_INVALID_HANDLE; +		return err; +	} +	sk->sk_state = TCP_SYN_SENT; +	return 0;  } -#endif  static int pep_ioctl(struct sock *sk, int cmd, unsigned long arg)  { @@ -890,8 +914,16 @@ static int pep_init(struct sock *sk)  	sk->sk_destruct = pipe_destruct;  	INIT_HLIST_HEAD(&pn->hlist); +	pn->listener = NULL;  	skb_queue_head_init(&pn->ctrlreq_queue); +	atomic_set(&pn->tx_credits, 0); +	pn->ifindex = 0; +	pn->peer_type = 0;  	pn->pipe_handle = PN_PIPE_INVALID_HANDLE; +	pn->rx_credits = 0; +	pn->rx_fc = pn->tx_fc = PN_LEGACY_FLOW_CONTROL; +	pn->init_enable = 1; +	pn->aligned = 0;  	return 0;  } @@ -1219,9 +1251,9 @@ static void pep_sock_unhash(struct sock *sk)  	lock_sock(sk); -#ifndef CONFIG_PHONET_PIPECTRLR -	if ((1 << sk->sk_state) & ~(TCPF_CLOSE|TCPF_LISTEN)) { +	if (pn->listener != NULL) {  		skparent = pn->listener; +		pn->listener = NULL;  		release_sock(sk);  		pn = pep_sk(skparent); @@ -1229,7 +1261,7 @@ static void pep_sock_unhash(struct sock *sk)  		sk_del_node_init(sk);  		sk = skparent;  	} -#endif +  	/* Unhash a listening sock only when it is closed  	 * and all of its active connected pipes are closed. */  	if (hlist_empty(&pn->hlist)) @@ -1243,9 +1275,7 @@ static void pep_sock_unhash(struct sock *sk)  static struct proto pep_proto = {  	.close		= pep_sock_close,  	.accept		= pep_sock_accept, -#ifdef CONFIG_PHONET_PIPECTRLR  	.connect	= pep_sock_connect, -#endif  	.ioctl		= pep_ioctl,  	.init		= pep_init,  	.setsockopt	= pep_setsockopt, diff --git a/net/phonet/socket.c b/net/phonet/socket.c index 1eccfc35bcc0..b1adafab377c 100644 --- a/net/phonet/socket.c +++ b/net/phonet/socket.c @@ -225,15 +225,18 @@ static int pn_socket_autobind(struct socket *sock)  	return 0; /* socket was already bound */  } -#ifdef CONFIG_PHONET_PIPECTRLR  static int pn_socket_connect(struct socket *sock, struct sockaddr *addr,  		int len, int flags)  {  	struct sock *sk = sock->sk; +	struct pn_sock *pn = pn_sk(sk);  	struct sockaddr_pn *spn = (struct sockaddr_pn *)addr; -	long timeo; +	struct task_struct *tsk = current; +	long timeo = sock_rcvtimeo(sk, flags & O_NONBLOCK);  	int err; +	if (pn_socket_autobind(sock)) +		return -ENOBUFS;  	if (len < sizeof(struct sockaddr_pn))  		return -EINVAL;  	if (spn->spn_family != AF_PHONET) @@ -243,82 +246,61 @@ static int pn_socket_connect(struct socket *sock, struct sockaddr *addr,  	switch (sock->state) {  	case SS_UNCONNECTED: -		sk->sk_state = TCP_CLOSE; -		break; -	case SS_CONNECTING: -		switch (sk->sk_state) { -		case TCP_SYN_RECV: -			sock->state = SS_CONNECTED; -			err = -EISCONN; -			goto out; -		case TCP_CLOSE: -			err = -EALREADY; -			if (flags & O_NONBLOCK) -				goto out; -			goto wait_connect; -		} -		break; -	case SS_CONNECTED: -		switch (sk->sk_state) { -		case TCP_SYN_RECV: +		if (sk->sk_state != TCP_CLOSE) {  			err = -EISCONN;  			goto out; -		case TCP_CLOSE: -			sock->state = SS_UNCONNECTED; -			break;  		}  		break; -	case SS_DISCONNECTING: -	case SS_FREE: -		break; +	case SS_CONNECTING: +		err = -EALREADY; +		goto out; +	default: +		err = -EISCONN; +		goto out;  	} -	sk->sk_state = TCP_CLOSE; -	sk_stream_kill_queues(sk); +	pn->dobject = pn_sockaddr_get_object(spn); +	pn->resource = pn_sockaddr_get_resource(spn);  	sock->state = SS_CONNECTING; +  	err = sk->sk_prot->connect(sk, addr, len); -	if (err < 0) { +	if (err) {  		sock->state = SS_UNCONNECTED; -		sk->sk_state = TCP_CLOSE; +		pn->dobject = 0;  		goto out;  	} -	err = -EINPROGRESS; -wait_connect: -	if (sk->sk_state != TCP_SYN_RECV && (flags & O_NONBLOCK)) -		goto out; - -	timeo = sock_sndtimeo(sk, flags & O_NONBLOCK); -	release_sock(sk); +	while (sk->sk_state == TCP_SYN_SENT) { +		DEFINE_WAIT(wait); -	err = -ERESTARTSYS; -	timeo = wait_event_interruptible_timeout(*sk_sleep(sk), -			sk->sk_state != TCP_CLOSE, -			timeo); - -	lock_sock(sk); -	if (timeo < 0) -		goto out; /* -ERESTARTSYS */ - -	err = -ETIMEDOUT; -	if (timeo == 0 && sk->sk_state != TCP_SYN_RECV) -		goto out; +		if (!timeo) { +			err = -EINPROGRESS; +			goto out; +		} +		if (signal_pending(tsk)) { +			err = sock_intr_errno(timeo); +			goto out; +		} -	if (sk->sk_state != TCP_SYN_RECV) { -		sock->state = SS_UNCONNECTED; -		err = sock_error(sk); -		if (!err) -			err = -ECONNREFUSED; -		goto out; +		prepare_to_wait_exclusive(sk_sleep(sk), &wait, +						TASK_INTERRUPTIBLE); +		release_sock(sk); +		timeo = schedule_timeout(timeo); +		lock_sock(sk); +		finish_wait(sk_sleep(sk), &wait);  	} -	sock->state = SS_CONNECTED; -	err = 0; +	if ((1 << sk->sk_state) & (TCPF_SYN_RECV|TCPF_ESTABLISHED)) +		err = 0; +	else if (sk->sk_state == TCP_CLOSE_WAIT) +		err = -ECONNRESET; +	else +		err = -ECONNREFUSED; +	sock->state = err ? SS_UNCONNECTED : SS_CONNECTED;  out:  	release_sock(sk);  	return err;  } -#endif  static int pn_socket_accept(struct socket *sock, struct socket *newsock,  				int flags) @@ -486,11 +468,7 @@ const struct proto_ops phonet_stream_ops = {  	.owner		= THIS_MODULE,  	.release	= pn_socket_release,  	.bind		= pn_socket_bind, -#ifdef CONFIG_PHONET_PIPECTRLR  	.connect	= pn_socket_connect, -#else -	.connect	= sock_no_connect, -#endif  	.socketpair	= sock_no_socketpair,  	.accept		= pn_socket_accept,  	.getname	= pn_socket_getname, |