Commit 1c851758 authored by David S. Miller's avatar David S. Miller

Merge branch 'tipc-socket-layer-improvements'

Parthasarathy Bhuvaragan says:

====================
tipc: socket layer improvements

The following issues with the current socket layer hinders socket diagnostics
implementation, which led to this patch series.

1. tipc socket state is derived from multiple variables like
   sock->state, tsk->probing_state and tsk->connected. This style forces
   us to export multiple attributes to the user space, which has to be
   backward compatible.

2. Abuse of sock->state cannot be exported to user-space without
   requiring tipc specific hacks in the user-space.
   - For connection less (CL) sockets sock->state is overloaded to
     tipc state SS_READY.
   - For connection oriented (CO) listening socket sock->state is
     overloaded to tipc state SS_LISTEN.

This series is split into four:
1. Bug fixes in patch #1,2,3.
2. Minor cleanups in patch#4-5.
3. Express all tipc states using a single variable in patch#6-8.
4. Migrate the new tipc states to sk->sk_state in patch#9-16.

The figures below represents the FSM after this series:

Stream Server Listening Socket:
+-----------+       +-------------+
| TIPC_OPEN |------>| TIPC_LISTEN |
+-----------+       +-------------+

Stream Server Data Socket:
+-----------+       +------------------+
| TIPC_OPEN |------>| TIPC_ESTABLISHED |
+-----------+       +------------------+
                          ^   |
                          |   |
                          |   v
                    +--------------------+
                    | TIPC_DISCONNECTING |
                    +--------------------+

Stream Socket Client:
+-----------+       +-----------------+
| TIPC_OPEN |------>| TIPC_CONNECTING |------+
+-----------+       +-----------------+      |
                            |                |
                            |                |
                            v                |
                    +------------------+     |
                    | TIPC_ESTABLISHED |     |
                    +------------------+     |
                          ^   |              |
                          |   |              |
                          |   v              |
                    +--------------------+   |
                    | TIPC_DISCONNECTING |<--+
                    +--------------------+

NOTE:
This is just a base refractoring required for socket diagnostics.
TIPC socket diagnostics support will be introduced in a later series.

v2: - remove extra cast and parenthesis as suggested by David S. Miller in #4.
    - map new tipc state values to tcp states to address Eric Dumazet's concern,
      thus allow the usage of generic sk_* helpers. This is done in patch#10-15.
    - remove TIPC_PROBING state and replace it with probe_unacked flag in #11.
    - replace the TIPC_CLOSING state in v1 with sk_shutdown flag in #14.
    - introduce __tipc_shutdown() to avoid code duplication in #14.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 169650d3 f40acbaf
...@@ -95,7 +95,7 @@ struct plist; ...@@ -95,7 +95,7 @@ struct plist;
#define TIPC_MEDIA_INFO_OFFSET 5 #define TIPC_MEDIA_INFO_OFFSET 5
struct tipc_skb_cb { struct tipc_skb_cb {
void *handle; u32 bytes_read;
struct sk_buff *tail; struct sk_buff *tail;
bool validated; bool validated;
bool wakeup_pending; bool wakeup_pending;
......
...@@ -44,44 +44,43 @@ ...@@ -44,44 +44,43 @@
#include "bcast.h" #include "bcast.h"
#include "netlink.h" #include "netlink.h"
#define SS_LISTENING -1 /* socket is listening */
#define SS_READY -2 /* socket is connectionless */
#define CONN_TIMEOUT_DEFAULT 8000 /* default connect timeout = 8s */ #define CONN_TIMEOUT_DEFAULT 8000 /* default connect timeout = 8s */
#define CONN_PROBING_INTERVAL msecs_to_jiffies(3600000) /* [ms] => 1 h */ #define CONN_PROBING_INTERVAL msecs_to_jiffies(3600000) /* [ms] => 1 h */
#define TIPC_FWD_MSG 1 #define TIPC_FWD_MSG 1
#define TIPC_CONN_OK 0
#define TIPC_CONN_PROBING 1
#define TIPC_MAX_PORT 0xffffffff #define TIPC_MAX_PORT 0xffffffff
#define TIPC_MIN_PORT 1 #define TIPC_MIN_PORT 1
enum {
TIPC_LISTEN = TCP_LISTEN,
TIPC_ESTABLISHED = TCP_ESTABLISHED,
TIPC_OPEN = TCP_CLOSE,
TIPC_DISCONNECTING = TCP_CLOSE_WAIT,
TIPC_CONNECTING = TCP_SYN_SENT,
};
/** /**
* struct tipc_sock - TIPC socket structure * struct tipc_sock - TIPC socket structure
* @sk: socket - interacts with 'port' and with user via the socket API * @sk: socket - interacts with 'port' and with user via the socket API
* @connected: non-zero if port is currently connected to a peer port
* @conn_type: TIPC type used when connection was established * @conn_type: TIPC type used when connection was established
* @conn_instance: TIPC instance used when connection was established * @conn_instance: TIPC instance used when connection was established
* @published: non-zero if port has one or more associated names * @published: non-zero if port has one or more associated names
* @max_pkt: maximum packet size "hint" used when building messages sent by port * @max_pkt: maximum packet size "hint" used when building messages sent by port
* @portid: unique port identity in TIPC socket hash table * @portid: unique port identity in TIPC socket hash table
* @phdr: preformatted message header used when sending messages * @phdr: preformatted message header used when sending messages
* @port_list: adjacent ports in TIPC's global list of ports
* @publications: list of publications for port * @publications: list of publications for port
* @pub_count: total # of publications port has made during its lifetime * @pub_count: total # of publications port has made during its lifetime
* @probing_state: * @probing_state:
* @probing_intv:
* @conn_timeout: the time we can wait for an unresponded setup request * @conn_timeout: the time we can wait for an unresponded setup request
* @dupl_rcvcnt: number of bytes counted twice, in both backlog and rcv queue * @dupl_rcvcnt: number of bytes counted twice, in both backlog and rcv queue
* @link_cong: non-zero if owner must sleep because of link congestion * @link_cong: non-zero if owner must sleep because of link congestion
* @sent_unacked: # messages sent by socket, and not yet acked by peer * @sent_unacked: # messages sent by socket, and not yet acked by peer
* @rcv_unacked: # messages read by user, but not yet acked back to peer * @rcv_unacked: # messages read by user, but not yet acked back to peer
* @remote: 'connected' peer for dgram/rdm * @peer: 'connected' peer for dgram/rdm
* @node: hash table node * @node: hash table node
* @rcu: rcu struct for tipc_sock * @rcu: rcu struct for tipc_sock
*/ */
struct tipc_sock { struct tipc_sock {
struct sock sk; struct sock sk;
int connected;
u32 conn_type; u32 conn_type;
u32 conn_instance; u32 conn_instance;
int published; int published;
...@@ -91,17 +90,16 @@ struct tipc_sock { ...@@ -91,17 +90,16 @@ struct tipc_sock {
struct list_head sock_list; struct list_head sock_list;
struct list_head publications; struct list_head publications;
u32 pub_count; u32 pub_count;
u32 probing_state;
unsigned long probing_intv;
uint conn_timeout; uint conn_timeout;
atomic_t dupl_rcvcnt; atomic_t dupl_rcvcnt;
bool probe_unacked;
bool link_cong; bool link_cong;
u16 snt_unacked; u16 snt_unacked;
u16 snd_win; u16 snd_win;
u16 peer_caps; u16 peer_caps;
u16 rcv_unacked; u16 rcv_unacked;
u16 rcv_win; u16 rcv_win;
struct sockaddr_tipc remote; struct sockaddr_tipc peer;
struct rhash_head node; struct rhash_head node;
struct rcu_head rcu; struct rcu_head rcu;
}; };
...@@ -294,6 +292,21 @@ static void tsk_rej_rx_queue(struct sock *sk) ...@@ -294,6 +292,21 @@ static void tsk_rej_rx_queue(struct sock *sk)
tipc_sk_respond(sk, skb, TIPC_ERR_NO_PORT); tipc_sk_respond(sk, skb, TIPC_ERR_NO_PORT);
} }
static bool tipc_sk_connected(struct sock *sk)
{
return sk->sk_state == TIPC_ESTABLISHED;
}
/* tipc_sk_type_connectionless - check if the socket is datagram socket
* @sk: socket
*
* Returns true if connection less, false otherwise
*/
static bool tipc_sk_type_connectionless(struct sock *sk)
{
return sk->sk_type == SOCK_RDM || sk->sk_type == SOCK_DGRAM;
}
/* tsk_peer_msg - verify if message was sent by connected port's peer /* tsk_peer_msg - verify if message was sent by connected port's peer
* *
* Handles cases where the node's network address has changed from * Handles cases where the node's network address has changed from
...@@ -301,12 +314,13 @@ static void tsk_rej_rx_queue(struct sock *sk) ...@@ -301,12 +314,13 @@ static void tsk_rej_rx_queue(struct sock *sk)
*/ */
static bool tsk_peer_msg(struct tipc_sock *tsk, struct tipc_msg *msg) static bool tsk_peer_msg(struct tipc_sock *tsk, struct tipc_msg *msg)
{ {
struct tipc_net *tn = net_generic(sock_net(&tsk->sk), tipc_net_id); struct sock *sk = &tsk->sk;
struct tipc_net *tn = net_generic(sock_net(sk), tipc_net_id);
u32 peer_port = tsk_peer_port(tsk); u32 peer_port = tsk_peer_port(tsk);
u32 orig_node; u32 orig_node;
u32 peer_node; u32 peer_node;
if (unlikely(!tsk->connected)) if (unlikely(!tipc_sk_connected(sk)))
return false; return false;
if (unlikely(msg_origport(msg) != peer_port)) if (unlikely(msg_origport(msg) != peer_port))
...@@ -327,6 +341,45 @@ static bool tsk_peer_msg(struct tipc_sock *tsk, struct tipc_msg *msg) ...@@ -327,6 +341,45 @@ static bool tsk_peer_msg(struct tipc_sock *tsk, struct tipc_msg *msg)
return false; return false;
} }
/* tipc_set_sk_state - set the sk_state of the socket
* @sk: socket
*
* Caller must hold socket lock
*
* Returns 0 on success, errno otherwise
*/
static int tipc_set_sk_state(struct sock *sk, int state)
{
int oldsk_state = sk->sk_state;
int res = -EINVAL;
switch (state) {
case TIPC_OPEN:
res = 0;
break;
case TIPC_LISTEN:
case TIPC_CONNECTING:
if (oldsk_state == TIPC_OPEN)
res = 0;
break;
case TIPC_ESTABLISHED:
if (oldsk_state == TIPC_CONNECTING ||
oldsk_state == TIPC_OPEN)
res = 0;
break;
case TIPC_DISCONNECTING:
if (oldsk_state == TIPC_CONNECTING ||
oldsk_state == TIPC_ESTABLISHED)
res = 0;
break;
}
if (!res)
sk->sk_state = state;
return res;
}
/** /**
* tipc_sk_create - create a TIPC socket * tipc_sk_create - create a TIPC socket
* @net: network namespace (must be default network) * @net: network namespace (must be default network)
...@@ -344,7 +397,6 @@ static int tipc_sk_create(struct net *net, struct socket *sock, ...@@ -344,7 +397,6 @@ static int tipc_sk_create(struct net *net, struct socket *sock,
{ {
struct tipc_net *tn; struct tipc_net *tn;
const struct proto_ops *ops; const struct proto_ops *ops;
socket_state state;
struct sock *sk; struct sock *sk;
struct tipc_sock *tsk; struct tipc_sock *tsk;
struct tipc_msg *msg; struct tipc_msg *msg;
...@@ -356,16 +408,13 @@ static int tipc_sk_create(struct net *net, struct socket *sock, ...@@ -356,16 +408,13 @@ static int tipc_sk_create(struct net *net, struct socket *sock,
switch (sock->type) { switch (sock->type) {
case SOCK_STREAM: case SOCK_STREAM:
ops = &stream_ops; ops = &stream_ops;
state = SS_UNCONNECTED;
break; break;
case SOCK_SEQPACKET: case SOCK_SEQPACKET:
ops = &packet_ops; ops = &packet_ops;
state = SS_UNCONNECTED;
break; break;
case SOCK_DGRAM: case SOCK_DGRAM:
case SOCK_RDM: case SOCK_RDM:
ops = &msg_ops; ops = &msg_ops;
state = SS_READY;
break; break;
default: default:
return -EPROTOTYPE; return -EPROTOTYPE;
...@@ -386,14 +435,15 @@ static int tipc_sk_create(struct net *net, struct socket *sock, ...@@ -386,14 +435,15 @@ static int tipc_sk_create(struct net *net, struct socket *sock,
/* Finish initializing socket data structures */ /* Finish initializing socket data structures */
sock->ops = ops; sock->ops = ops;
sock->state = state;
sock_init_data(sock, sk); sock_init_data(sock, sk);
tipc_set_sk_state(sk, TIPC_OPEN);
if (tipc_sk_insert(tsk)) { if (tipc_sk_insert(tsk)) {
pr_warn("Socket create failed; port number exhausted\n"); pr_warn("Socket create failed; port number exhausted\n");
return -EINVAL; return -EINVAL;
} }
msg_set_origport(msg, tsk->portid); msg_set_origport(msg, tsk->portid);
setup_timer(&sk->sk_timer, tipc_sk_timeout, (unsigned long)tsk); setup_timer(&sk->sk_timer, tipc_sk_timeout, (unsigned long)tsk);
sk->sk_shutdown = 0;
sk->sk_backlog_rcv = tipc_backlog_rcv; sk->sk_backlog_rcv = tipc_backlog_rcv;
sk->sk_rcvbuf = sysctl_tipc_rmem[1]; sk->sk_rcvbuf = sysctl_tipc_rmem[1];
sk->sk_data_ready = tipc_data_ready; sk->sk_data_ready = tipc_data_ready;
...@@ -406,11 +456,12 @@ static int tipc_sk_create(struct net *net, struct socket *sock, ...@@ -406,11 +456,12 @@ static int tipc_sk_create(struct net *net, struct socket *sock,
tsk->snd_win = tsk_adv_blocks(RCVBUF_MIN); tsk->snd_win = tsk_adv_blocks(RCVBUF_MIN);
tsk->rcv_win = tsk->snd_win; tsk->rcv_win = tsk->snd_win;
if (sock->state == SS_READY) { if (tipc_sk_type_connectionless(sk)) {
tsk_set_unreturnable(tsk, true); tsk_set_unreturnable(tsk, true);
if (sock->type == SOCK_DGRAM) if (sock->type == SOCK_DGRAM)
tsk_set_unreliable(tsk, true); tsk_set_unreliable(tsk, true);
} }
return 0; return 0;
} }
...@@ -421,6 +472,44 @@ static void tipc_sk_callback(struct rcu_head *head) ...@@ -421,6 +472,44 @@ static void tipc_sk_callback(struct rcu_head *head)
sock_put(&tsk->sk); sock_put(&tsk->sk);
} }
/* Caller should hold socket lock for the socket. */
static void __tipc_shutdown(struct socket *sock, int error)
{
struct sock *sk = sock->sk;
struct tipc_sock *tsk = tipc_sk(sk);
struct net *net = sock_net(sk);
u32 dnode = tsk_peer_node(tsk);
struct sk_buff *skb;
/* Reject all unreceived messages, except on an active connection
* (which disconnects locally & sends a 'FIN+' to peer).
*/
while ((skb = __skb_dequeue(&sk->sk_receive_queue)) != NULL) {
if (TIPC_SKB_CB(skb)->bytes_read) {
kfree_skb(skb);
} else {
if (!tipc_sk_type_connectionless(sk) &&
sk->sk_state != TIPC_DISCONNECTING) {
tipc_set_sk_state(sk, TIPC_DISCONNECTING);
tipc_node_remove_conn(net, dnode, tsk->portid);
}
tipc_sk_respond(sk, skb, error);
}
}
if (sk->sk_state != TIPC_DISCONNECTING) {
skb = tipc_msg_create(TIPC_CRITICAL_IMPORTANCE,
TIPC_CONN_MSG, SHORT_H_SIZE, 0, dnode,
tsk_own_node(tsk), tsk_peer_port(tsk),
tsk->portid, error);
if (skb)
tipc_node_xmit_skb(net, skb, dnode, tsk->portid);
if (!tipc_sk_type_connectionless(sk)) {
tipc_node_remove_conn(net, dnode, tsk->portid);
tipc_set_sk_state(sk, TIPC_DISCONNECTING);
}
}
}
/** /**
* tipc_release - destroy a TIPC socket * tipc_release - destroy a TIPC socket
* @sock: socket to destroy * @sock: socket to destroy
...@@ -440,10 +529,7 @@ static void tipc_sk_callback(struct rcu_head *head) ...@@ -440,10 +529,7 @@ static void tipc_sk_callback(struct rcu_head *head)
static int tipc_release(struct socket *sock) static int tipc_release(struct socket *sock)
{ {
struct sock *sk = sock->sk; struct sock *sk = sock->sk;
struct net *net;
struct tipc_sock *tsk; struct tipc_sock *tsk;
struct sk_buff *skb;
u32 dnode;
/* /*
* Exit if socket isn't fully initialized (occurs when a failed accept() * Exit if socket isn't fully initialized (occurs when a failed accept()
...@@ -452,47 +538,16 @@ static int tipc_release(struct socket *sock) ...@@ -452,47 +538,16 @@ static int tipc_release(struct socket *sock)
if (sk == NULL) if (sk == NULL)
return 0; return 0;
net = sock_net(sk);
tsk = tipc_sk(sk); tsk = tipc_sk(sk);
lock_sock(sk); lock_sock(sk);
/* __tipc_shutdown(sock, TIPC_ERR_NO_PORT);
* Reject all unreceived messages, except on an active connection sk->sk_shutdown = SHUTDOWN_MASK;
* (which disconnects locally & sends a 'FIN+' to peer)
*/
dnode = tsk_peer_node(tsk);
while (sock->state != SS_DISCONNECTING) {
skb = __skb_dequeue(&sk->sk_receive_queue);
if (skb == NULL)
break;
if (TIPC_SKB_CB(skb)->handle != NULL)
kfree_skb(skb);
else {
if ((sock->state == SS_CONNECTING) ||
(sock->state == SS_CONNECTED)) {
sock->state = SS_DISCONNECTING;
tsk->connected = 0;
tipc_node_remove_conn(net, dnode, tsk->portid);
}
tipc_sk_respond(sk, skb, TIPC_ERR_NO_PORT);
}
}
tipc_sk_withdraw(tsk, 0, NULL); tipc_sk_withdraw(tsk, 0, NULL);
sk_stop_timer(sk, &sk->sk_timer); sk_stop_timer(sk, &sk->sk_timer);
tipc_sk_remove(tsk); tipc_sk_remove(tsk);
if (tsk->connected) {
skb = tipc_msg_create(TIPC_CRITICAL_IMPORTANCE,
TIPC_CONN_MSG, SHORT_H_SIZE, 0, dnode,
tsk_own_node(tsk), tsk_peer_port(tsk),
tsk->portid, TIPC_ERR_NO_PORT);
if (skb)
tipc_node_xmit_skb(net, skb, dnode, tsk->portid);
tipc_node_remove_conn(net, dnode, tsk->portid);
}
/* Reject any messages that accumulated in backlog queue */ /* Reject any messages that accumulated in backlog queue */
sock->state = SS_DISCONNECTING;
release_sock(sk); release_sock(sk);
call_rcu(&tsk->rcu, tipc_sk_callback); call_rcu(&tsk->rcu, tipc_sk_callback);
...@@ -578,13 +633,14 @@ static int tipc_getname(struct socket *sock, struct sockaddr *uaddr, ...@@ -578,13 +633,14 @@ static int tipc_getname(struct socket *sock, struct sockaddr *uaddr,
int *uaddr_len, int peer) int *uaddr_len, int peer)
{ {
struct sockaddr_tipc *addr = (struct sockaddr_tipc *)uaddr; struct sockaddr_tipc *addr = (struct sockaddr_tipc *)uaddr;
struct tipc_sock *tsk = tipc_sk(sock->sk); struct sock *sk = sock->sk;
struct tipc_sock *tsk = tipc_sk(sk);
struct tipc_net *tn = net_generic(sock_net(sock->sk), tipc_net_id); struct tipc_net *tn = net_generic(sock_net(sock->sk), tipc_net_id);
memset(addr, 0, sizeof(*addr)); memset(addr, 0, sizeof(*addr));
if (peer) { if (peer) {
if ((sock->state != SS_CONNECTED) && if ((!tipc_sk_connected(sk)) &&
((peer != 2) || (sock->state != SS_DISCONNECTING))) ((peer != 2) || (sk->sk_state != TIPC_DISCONNECTING)))
return -ENOTCONN; return -ENOTCONN;
addr->addr.id.ref = tsk_peer_port(tsk); addr->addr.id.ref = tsk_peer_port(tsk);
addr->addr.id.node = tsk_peer_node(tsk); addr->addr.id.node = tsk_peer_node(tsk);
...@@ -616,28 +672,6 @@ static int tipc_getname(struct socket *sock, struct sockaddr *uaddr, ...@@ -616,28 +672,6 @@ static int tipc_getname(struct socket *sock, struct sockaddr *uaddr,
* exits. TCP and other protocols seem to rely on higher level poll routines * exits. TCP and other protocols seem to rely on higher level poll routines
* to handle any preventable race conditions, so TIPC will do the same ... * to handle any preventable race conditions, so TIPC will do the same ...
* *
* TIPC sets the returned events as follows:
*
* socket state flags set
* ------------ ---------
* unconnected no read flags
* POLLOUT if port is not congested
*
* connecting POLLIN/POLLRDNORM if ACK/NACK in rx queue
* no write flags
*
* connected POLLIN/POLLRDNORM if data in rx queue
* POLLOUT if port is not congested
*
* disconnecting POLLIN/POLLRDNORM/POLLHUP
* no write flags
*
* listening POLLIN if SYN in rx queue
* no write flags
*
* ready POLLIN/POLLRDNORM if data in rx queue
* [connectionless] POLLOUT (since port cannot be congested)
*
* IMPORTANT: The fact that a read or write operation is indicated does NOT * IMPORTANT: The fact that a read or write operation is indicated does NOT
* imply that the operation will succeed, merely that it should be performed * imply that the operation will succeed, merely that it should be performed
* and will not block. * and will not block.
...@@ -651,22 +685,29 @@ static unsigned int tipc_poll(struct file *file, struct socket *sock, ...@@ -651,22 +685,29 @@ static unsigned int tipc_poll(struct file *file, struct socket *sock,
sock_poll_wait(file, sk_sleep(sk), wait); sock_poll_wait(file, sk_sleep(sk), wait);
switch ((int)sock->state) { if (sk->sk_shutdown & RCV_SHUTDOWN)
case SS_UNCONNECTED: mask |= POLLRDHUP | POLLIN | POLLRDNORM;
if (!tsk->link_cong) if (sk->sk_shutdown == SHUTDOWN_MASK)
mask |= POLLOUT; mask |= POLLHUP;
break;
case SS_READY: switch (sk->sk_state) {
case SS_CONNECTED: case TIPC_ESTABLISHED:
if (!tsk->link_cong && !tsk_conn_cong(tsk)) if (!tsk->link_cong && !tsk_conn_cong(tsk))
mask |= POLLOUT; mask |= POLLOUT;
/* fall thru' */ /* fall thru' */
case SS_CONNECTING: case TIPC_LISTEN:
case SS_LISTENING: case TIPC_CONNECTING:
if (!skb_queue_empty(&sk->sk_receive_queue)) if (!skb_queue_empty(&sk->sk_receive_queue))
mask |= (POLLIN | POLLRDNORM); mask |= (POLLIN | POLLRDNORM);
break; break;
case SS_DISCONNECTING: case TIPC_OPEN:
if (!tsk->link_cong)
mask |= POLLOUT;
if (tipc_sk_type_connectionless(sk) &&
(!skb_queue_empty(&sk->sk_receive_queue)))
mask |= (POLLIN | POLLRDNORM);
break;
case TIPC_DISCONNECTING:
mask = (POLLIN | POLLRDNORM | POLLHUP); mask = (POLLIN | POLLRDNORM | POLLHUP);
break; break;
} }
...@@ -697,6 +738,9 @@ static int tipc_sendmcast(struct socket *sock, struct tipc_name_seq *seq, ...@@ -697,6 +738,9 @@ static int tipc_sendmcast(struct socket *sock, struct tipc_name_seq *seq,
uint mtu; uint mtu;
int rc; int rc;
if (!timeo && tsk->link_cong)
return -ELINKCONG;
msg_set_type(mhdr, TIPC_MCAST_MSG); msg_set_type(mhdr, TIPC_MCAST_MSG);
msg_set_lookup_scope(mhdr, TIPC_CLUSTER_SCOPE); msg_set_lookup_scope(mhdr, TIPC_CLUSTER_SCOPE);
msg_set_destport(mhdr, 0); msg_set_destport(mhdr, 0);
...@@ -809,7 +853,7 @@ static void tipc_sk_proto_rcv(struct tipc_sock *tsk, struct sk_buff *skb, ...@@ -809,7 +853,7 @@ static void tipc_sk_proto_rcv(struct tipc_sock *tsk, struct sk_buff *skb,
if (!tsk_peer_msg(tsk, hdr)) if (!tsk_peer_msg(tsk, hdr))
goto exit; goto exit;
tsk->probing_state = TIPC_CONN_OK; tsk->probe_unacked = false;
if (mtyp == CONN_PROBE) { if (mtyp == CONN_PROBE) {
msg_set_type(hdr, CONN_PROBE_REPLY); msg_set_type(hdr, CONN_PROBE_REPLY);
...@@ -841,7 +885,7 @@ static int tipc_wait_for_sndmsg(struct socket *sock, long *timeo_p) ...@@ -841,7 +885,7 @@ static int tipc_wait_for_sndmsg(struct socket *sock, long *timeo_p)
int err = sock_error(sk); int err = sock_error(sk);
if (err) if (err)
return err; return err;
if (sock->state == SS_DISCONNECTING) if (sk->sk_shutdown & SEND_SHUTDOWN)
return -EPIPE; return -EPIPE;
if (!*timeo_p) if (!*timeo_p)
return -EAGAIN; return -EAGAIN;
...@@ -890,6 +934,7 @@ static int __tipc_sendmsg(struct socket *sock, struct msghdr *m, size_t dsz) ...@@ -890,6 +934,7 @@ static int __tipc_sendmsg(struct socket *sock, struct msghdr *m, size_t dsz)
struct tipc_msg *mhdr = &tsk->phdr; struct tipc_msg *mhdr = &tsk->phdr;
u32 dnode, dport; u32 dnode, dport;
struct sk_buff_head pktchain; struct sk_buff_head pktchain;
bool is_connectionless = tipc_sk_type_connectionless(sk);
struct sk_buff *skb; struct sk_buff *skb;
struct tipc_name_seq *seq; struct tipc_name_seq *seq;
struct iov_iter save; struct iov_iter save;
...@@ -900,18 +945,18 @@ static int __tipc_sendmsg(struct socket *sock, struct msghdr *m, size_t dsz) ...@@ -900,18 +945,18 @@ static int __tipc_sendmsg(struct socket *sock, struct msghdr *m, size_t dsz)
if (dsz > TIPC_MAX_USER_MSG_SIZE) if (dsz > TIPC_MAX_USER_MSG_SIZE)
return -EMSGSIZE; return -EMSGSIZE;
if (unlikely(!dest)) { if (unlikely(!dest)) {
if (tsk->connected && sock->state == SS_READY) if (is_connectionless && tsk->peer.family == AF_TIPC)
dest = &tsk->remote; dest = &tsk->peer;
else else
return -EDESTADDRREQ; return -EDESTADDRREQ;
} else if (unlikely(m->msg_namelen < sizeof(*dest)) || } else if (unlikely(m->msg_namelen < sizeof(*dest)) ||
dest->family != AF_TIPC) { dest->family != AF_TIPC) {
return -EINVAL; return -EINVAL;
} }
if (unlikely(sock->state != SS_READY)) { if (!is_connectionless) {
if (sock->state == SS_LISTENING) if (sk->sk_state == TIPC_LISTEN)
return -EPIPE; return -EPIPE;
if (sock->state != SS_UNCONNECTED) if (sk->sk_state != TIPC_OPEN)
return -EISCONN; return -EISCONN;
if (tsk->published) if (tsk->published)
return -EOPNOTSUPP; return -EOPNOTSUPP;
...@@ -963,8 +1008,8 @@ static int __tipc_sendmsg(struct socket *sock, struct msghdr *m, size_t dsz) ...@@ -963,8 +1008,8 @@ static int __tipc_sendmsg(struct socket *sock, struct msghdr *m, size_t dsz)
TIPC_SKB_CB(skb)->wakeup_pending = tsk->link_cong; TIPC_SKB_CB(skb)->wakeup_pending = tsk->link_cong;
rc = tipc_node_xmit(net, &pktchain, dnode, tsk->portid); rc = tipc_node_xmit(net, &pktchain, dnode, tsk->portid);
if (likely(!rc)) { if (likely(!rc)) {
if (sock->state != SS_READY) if (!is_connectionless)
sock->state = SS_CONNECTING; tipc_set_sk_state(sk, TIPC_CONNECTING);
return dsz; return dsz;
} }
if (rc == -ELINKCONG) { if (rc == -ELINKCONG) {
...@@ -995,9 +1040,9 @@ static int tipc_wait_for_sndpkt(struct socket *sock, long *timeo_p) ...@@ -995,9 +1040,9 @@ static int tipc_wait_for_sndpkt(struct socket *sock, long *timeo_p)
int err = sock_error(sk); int err = sock_error(sk);
if (err) if (err)
return err; return err;
if (sock->state == SS_DISCONNECTING) if (sk->sk_state == TIPC_DISCONNECTING)
return -EPIPE; return -EPIPE;
else if (sock->state != SS_CONNECTED) else if (!tipc_sk_connected(sk))
return -ENOTCONN; return -ENOTCONN;
if (!*timeo_p) if (!*timeo_p)
return -EAGAIN; return -EAGAIN;
...@@ -1008,7 +1053,7 @@ static int tipc_wait_for_sndpkt(struct socket *sock, long *timeo_p) ...@@ -1008,7 +1053,7 @@ static int tipc_wait_for_sndpkt(struct socket *sock, long *timeo_p)
done = sk_wait_event(sk, timeo_p, done = sk_wait_event(sk, timeo_p,
(!tsk->link_cong && (!tsk->link_cong &&
!tsk_conn_cong(tsk)) || !tsk_conn_cong(tsk)) ||
!tsk->connected); !tipc_sk_connected(sk));
finish_wait(sk_sleep(sk), &wait); finish_wait(sk_sleep(sk), &wait);
} while (!done); } while (!done);
return 0; return 0;
...@@ -1064,14 +1109,17 @@ static int __tipc_send_stream(struct socket *sock, struct msghdr *m, size_t dsz) ...@@ -1064,14 +1109,17 @@ static int __tipc_send_stream(struct socket *sock, struct msghdr *m, size_t dsz)
if (dsz > (uint)INT_MAX) if (dsz > (uint)INT_MAX)
return -EMSGSIZE; return -EMSGSIZE;
if (unlikely(sock->state != SS_CONNECTED)) { if (unlikely(!tipc_sk_connected(sk))) {
if (sock->state == SS_DISCONNECTING) if (sk->sk_state == TIPC_DISCONNECTING)
return -EPIPE; return -EPIPE;
else else
return -ENOTCONN; return -ENOTCONN;
} }
timeo = sock_sndtimeo(sk, m->msg_flags & MSG_DONTWAIT); timeo = sock_sndtimeo(sk, m->msg_flags & MSG_DONTWAIT);
if (!timeo && tsk->link_cong)
return -ELINKCONG;
dnode = tsk_peer_node(tsk); dnode = tsk_peer_node(tsk);
skb_queue_head_init(&pktchain); skb_queue_head_init(&pktchain);
...@@ -1145,10 +1193,8 @@ static void tipc_sk_finish_conn(struct tipc_sock *tsk, u32 peer_port, ...@@ -1145,10 +1193,8 @@ static void tipc_sk_finish_conn(struct tipc_sock *tsk, u32 peer_port,
msg_set_lookup_scope(msg, 0); msg_set_lookup_scope(msg, 0);
msg_set_hdr_sz(msg, SHORT_H_SIZE); msg_set_hdr_sz(msg, SHORT_H_SIZE);
tsk->probing_intv = CONN_PROBING_INTERVAL; sk_reset_timer(sk, &sk->sk_timer, jiffies + CONN_PROBING_INTERVAL);
tsk->probing_state = TIPC_CONN_OK; tipc_set_sk_state(sk, TIPC_ESTABLISHED);
tsk->connected = 1;
sk_reset_timer(sk, &sk->sk_timer, jiffies + tsk->probing_intv);
tipc_node_add_conn(net, peer_node, tsk->portid, peer_port); tipc_node_add_conn(net, peer_node, tsk->portid, peer_port);
tsk->max_pkt = tipc_node_get_mtu(net, peer_node, tsk->portid); tsk->max_pkt = tipc_node_get_mtu(net, peer_node, tsk->portid);
tsk->peer_caps = tipc_node_get_capabilities(net, peer_node); tsk->peer_caps = tipc_node_get_capabilities(net, peer_node);
...@@ -1256,13 +1302,14 @@ static int tipc_sk_anc_data_recv(struct msghdr *m, struct tipc_msg *msg, ...@@ -1256,13 +1302,14 @@ static int tipc_sk_anc_data_recv(struct msghdr *m, struct tipc_msg *msg,
static void tipc_sk_send_ack(struct tipc_sock *tsk) static void tipc_sk_send_ack(struct tipc_sock *tsk)
{ {
struct net *net = sock_net(&tsk->sk); struct sock *sk = &tsk->sk;
struct net *net = sock_net(sk);
struct sk_buff *skb = NULL; struct sk_buff *skb = NULL;
struct tipc_msg *msg; struct tipc_msg *msg;
u32 peer_port = tsk_peer_port(tsk); u32 peer_port = tsk_peer_port(tsk);
u32 dnode = tsk_peer_node(tsk); u32 dnode = tsk_peer_node(tsk);
if (!tsk->connected) if (!tipc_sk_connected(sk))
return; return;
skb = tipc_msg_create(CONN_MANAGER, CONN_ACK, INT_H_SIZE, 0, skb = tipc_msg_create(CONN_MANAGER, CONN_ACK, INT_H_SIZE, 0,
dnode, tsk_own_node(tsk), peer_port, dnode, tsk_own_node(tsk), peer_port,
...@@ -1291,7 +1338,7 @@ static int tipc_wait_for_rcvmsg(struct socket *sock, long *timeop) ...@@ -1291,7 +1338,7 @@ static int tipc_wait_for_rcvmsg(struct socket *sock, long *timeop)
for (;;) { for (;;) {
prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE); prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE);
if (timeo && skb_queue_empty(&sk->sk_receive_queue)) { if (timeo && skb_queue_empty(&sk->sk_receive_queue)) {
if (sock->state == SS_DISCONNECTING) { if (sk->sk_shutdown & RCV_SHUTDOWN) {
err = -ENOTCONN; err = -ENOTCONN;
break; break;
} }
...@@ -1332,6 +1379,7 @@ static int tipc_recvmsg(struct socket *sock, struct msghdr *m, size_t buf_len, ...@@ -1332,6 +1379,7 @@ static int tipc_recvmsg(struct socket *sock, struct msghdr *m, size_t buf_len,
struct tipc_sock *tsk = tipc_sk(sk); struct tipc_sock *tsk = tipc_sk(sk);
struct sk_buff *buf; struct sk_buff *buf;
struct tipc_msg *msg; struct tipc_msg *msg;
bool is_connectionless = tipc_sk_type_connectionless(sk);
long timeo; long timeo;
unsigned int sz; unsigned int sz;
u32 err; u32 err;
...@@ -1343,7 +1391,7 @@ static int tipc_recvmsg(struct socket *sock, struct msghdr *m, size_t buf_len, ...@@ -1343,7 +1391,7 @@ static int tipc_recvmsg(struct socket *sock, struct msghdr *m, size_t buf_len,
lock_sock(sk); lock_sock(sk);
if (unlikely(sock->state == SS_UNCONNECTED)) { if (!is_connectionless && unlikely(sk->sk_state == TIPC_OPEN)) {
res = -ENOTCONN; res = -ENOTCONN;
goto exit; goto exit;
} }
...@@ -1388,8 +1436,8 @@ static int tipc_recvmsg(struct socket *sock, struct msghdr *m, size_t buf_len, ...@@ -1388,8 +1436,8 @@ static int tipc_recvmsg(struct socket *sock, struct msghdr *m, size_t buf_len,
goto exit; goto exit;
res = sz; res = sz;
} else { } else {
if ((sock->state == SS_READY) || if (is_connectionless || err == TIPC_CONN_SHUTDOWN ||
((err == TIPC_CONN_SHUTDOWN) || m->msg_control)) m->msg_control)
res = 0; res = 0;
else else
res = -ECONNRESET; res = -ECONNRESET;
...@@ -1398,7 +1446,7 @@ static int tipc_recvmsg(struct socket *sock, struct msghdr *m, size_t buf_len, ...@@ -1398,7 +1446,7 @@ static int tipc_recvmsg(struct socket *sock, struct msghdr *m, size_t buf_len,
if (unlikely(flags & MSG_PEEK)) if (unlikely(flags & MSG_PEEK))
goto exit; goto exit;
if (likely(sock->state != SS_READY)) { if (likely(!is_connectionless)) {
tsk->rcv_unacked += tsk_inc(tsk, hlen + sz); tsk->rcv_unacked += tsk_inc(tsk, hlen + sz);
if (unlikely(tsk->rcv_unacked >= (tsk->rcv_win / 4))) if (unlikely(tsk->rcv_unacked >= (tsk->rcv_win / 4)))
tipc_sk_send_ack(tsk); tipc_sk_send_ack(tsk);
...@@ -1429,7 +1477,7 @@ static int tipc_recv_stream(struct socket *sock, struct msghdr *m, ...@@ -1429,7 +1477,7 @@ static int tipc_recv_stream(struct socket *sock, struct msghdr *m,
struct tipc_msg *msg; struct tipc_msg *msg;
long timeo; long timeo;
unsigned int sz; unsigned int sz;
int sz_to_copy, target, needed; int target;
int sz_copied = 0; int sz_copied = 0;
u32 err; u32 err;
int res = 0, hlen; int res = 0, hlen;
...@@ -1440,7 +1488,7 @@ static int tipc_recv_stream(struct socket *sock, struct msghdr *m, ...@@ -1440,7 +1488,7 @@ static int tipc_recv_stream(struct socket *sock, struct msghdr *m,
lock_sock(sk); lock_sock(sk);
if (unlikely(sock->state == SS_UNCONNECTED)) { if (unlikely(sk->sk_state == TIPC_OPEN)) {
res = -ENOTCONN; res = -ENOTCONN;
goto exit; goto exit;
} }
...@@ -1477,11 +1525,13 @@ static int tipc_recv_stream(struct socket *sock, struct msghdr *m, ...@@ -1477,11 +1525,13 @@ static int tipc_recv_stream(struct socket *sock, struct msghdr *m,
/* Capture message data (if valid) & compute return value (always) */ /* Capture message data (if valid) & compute return value (always) */
if (!err) { if (!err) {
u32 offset = (u32)(unsigned long)(TIPC_SKB_CB(buf)->handle); u32 offset = TIPC_SKB_CB(buf)->bytes_read;
u32 needed;
int sz_to_copy;
sz -= offset; sz -= offset;
needed = (buf_len - sz_copied); needed = (buf_len - sz_copied);
sz_to_copy = (sz <= needed) ? sz : needed; sz_to_copy = min(sz, needed);
res = skb_copy_datagram_msg(buf, hlen + offset, m, sz_to_copy); res = skb_copy_datagram_msg(buf, hlen + offset, m, sz_to_copy);
if (res) if (res)
...@@ -1491,8 +1541,8 @@ static int tipc_recv_stream(struct socket *sock, struct msghdr *m, ...@@ -1491,8 +1541,8 @@ static int tipc_recv_stream(struct socket *sock, struct msghdr *m,
if (sz_to_copy < sz) { if (sz_to_copy < sz) {
if (!(flags & MSG_PEEK)) if (!(flags & MSG_PEEK))
TIPC_SKB_CB(buf)->handle = TIPC_SKB_CB(buf)->bytes_read =
(void *)(unsigned long)(offset + sz_to_copy); offset + sz_to_copy;
goto exit; goto exit;
} }
} else { } else {
...@@ -1574,49 +1624,31 @@ static bool filter_connect(struct tipc_sock *tsk, struct sk_buff *skb) ...@@ -1574,49 +1624,31 @@ static bool filter_connect(struct tipc_sock *tsk, struct sk_buff *skb)
{ {
struct sock *sk = &tsk->sk; struct sock *sk = &tsk->sk;
struct net *net = sock_net(sk); struct net *net = sock_net(sk);
struct socket *sock = sk->sk_socket;
struct tipc_msg *hdr = buf_msg(skb); struct tipc_msg *hdr = buf_msg(skb);
if (unlikely(msg_mcast(hdr))) if (unlikely(msg_mcast(hdr)))
return false; return false;
switch ((int)sock->state) { switch (sk->sk_state) {
case SS_CONNECTED: case TIPC_CONNECTING:
/* Accept only connection-based messages sent by peer */
if (unlikely(!tsk_peer_msg(tsk, hdr)))
return false;
if (unlikely(msg_errcode(hdr))) {
sock->state = SS_DISCONNECTING;
tsk->connected = 0;
/* Let timer expire on it's own */
tipc_node_remove_conn(net, tsk_peer_node(tsk),
tsk->portid);
}
return true;
case SS_CONNECTING:
/* Accept only ACK or NACK message */ /* Accept only ACK or NACK message */
if (unlikely(!msg_connected(hdr))) if (unlikely(!msg_connected(hdr)))
return false; return false;
if (unlikely(msg_errcode(hdr))) { if (unlikely(msg_errcode(hdr))) {
sock->state = SS_DISCONNECTING; tipc_set_sk_state(sk, TIPC_DISCONNECTING);
sk->sk_err = ECONNREFUSED; sk->sk_err = ECONNREFUSED;
return true; return true;
} }
if (unlikely(!msg_isdata(hdr))) { if (unlikely(!msg_isdata(hdr))) {
sock->state = SS_DISCONNECTING; tipc_set_sk_state(sk, TIPC_DISCONNECTING);
sk->sk_err = EINVAL; sk->sk_err = EINVAL;
return true; return true;
} }
tipc_sk_finish_conn(tsk, msg_origport(hdr), msg_orignode(hdr)); tipc_sk_finish_conn(tsk, msg_origport(hdr), msg_orignode(hdr));
msg_set_importance(&tsk->phdr, msg_importance(hdr)); msg_set_importance(&tsk->phdr, msg_importance(hdr));
sock->state = SS_CONNECTED;
/* If 'ACK+' message, add to socket receive queue */ /* If 'ACK+' message, add to socket receive queue */
if (msg_data_sz(hdr)) if (msg_data_sz(hdr))
...@@ -1630,18 +1662,31 @@ static bool filter_connect(struct tipc_sock *tsk, struct sk_buff *skb) ...@@ -1630,18 +1662,31 @@ static bool filter_connect(struct tipc_sock *tsk, struct sk_buff *skb)
msg_set_dest_droppable(hdr, 1); msg_set_dest_droppable(hdr, 1);
return false; return false;
case SS_LISTENING: case TIPC_OPEN:
case SS_UNCONNECTED: case TIPC_DISCONNECTING:
break;
case TIPC_LISTEN:
/* Accept only SYN message */ /* Accept only SYN message */
if (!msg_connected(hdr) && !(msg_errcode(hdr))) if (!msg_connected(hdr) && !(msg_errcode(hdr)))
return true; return true;
break; break;
case SS_DISCONNECTING: case TIPC_ESTABLISHED:
break; /* Accept only connection-based messages sent by peer */
if (unlikely(!tsk_peer_msg(tsk, hdr)))
return false;
if (unlikely(msg_errcode(hdr))) {
tipc_set_sk_state(sk, TIPC_DISCONNECTING);
/* Let timer expire on it's own */
tipc_node_remove_conn(net, tsk_peer_node(tsk),
tsk->portid);
sk->sk_state_change(sk);
}
return true;
default: default:
pr_err("Unknown socket state %u\n", sock->state); pr_err("Unknown sk_state %u\n", sk->sk_state);
} }
return false; return false;
} }
...@@ -1692,7 +1737,6 @@ static unsigned int rcvbuf_limit(struct sock *sk, struct sk_buff *skb) ...@@ -1692,7 +1737,6 @@ static unsigned int rcvbuf_limit(struct sock *sk, struct sk_buff *skb)
static bool filter_rcv(struct sock *sk, struct sk_buff *skb, static bool filter_rcv(struct sock *sk, struct sk_buff *skb,
struct sk_buff_head *xmitq) struct sk_buff_head *xmitq)
{ {
struct socket *sock = sk->sk_socket;
struct tipc_sock *tsk = tipc_sk(sk); struct tipc_sock *tsk = tipc_sk(sk);
struct tipc_msg *hdr = buf_msg(skb); struct tipc_msg *hdr = buf_msg(skb);
unsigned int limit = rcvbuf_limit(sk, skb); unsigned int limit = rcvbuf_limit(sk, skb);
...@@ -1718,7 +1762,7 @@ static bool filter_rcv(struct sock *sk, struct sk_buff *skb, ...@@ -1718,7 +1762,7 @@ static bool filter_rcv(struct sock *sk, struct sk_buff *skb,
} }
/* Reject if wrong message type for current socket state */ /* Reject if wrong message type for current socket state */
if (unlikely(sock->state == SS_READY)) { if (tipc_sk_type_connectionless(sk)) {
if (msg_connected(hdr)) { if (msg_connected(hdr)) {
err = TIPC_ERR_NO_PORT; err = TIPC_ERR_NO_PORT;
goto reject; goto reject;
...@@ -1735,7 +1779,7 @@ static bool filter_rcv(struct sock *sk, struct sk_buff *skb, ...@@ -1735,7 +1779,7 @@ static bool filter_rcv(struct sock *sk, struct sk_buff *skb,
} }
/* Enqueue message */ /* Enqueue message */
TIPC_SKB_CB(skb)->handle = NULL; TIPC_SKB_CB(skb)->bytes_read = 0;
__skb_queue_tail(&sk->sk_receive_queue, skb); __skb_queue_tail(&sk->sk_receive_queue, skb);
skb_set_owner_r(skb, sk); skb_set_owner_r(skb, sk);
...@@ -1899,7 +1943,8 @@ static int tipc_wait_for_connect(struct socket *sock, long *timeo_p) ...@@ -1899,7 +1943,8 @@ static int tipc_wait_for_connect(struct socket *sock, long *timeo_p)
return sock_intr_errno(*timeo_p); return sock_intr_errno(*timeo_p);
prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE); prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE);
done = sk_wait_event(sk, timeo_p, sock->state != SS_CONNECTING); done = sk_wait_event(sk, timeo_p,
sk->sk_state != TIPC_CONNECTING);
finish_wait(sk_sleep(sk), &wait); finish_wait(sk_sleep(sk), &wait);
} while (!done); } while (!done);
return 0; return 0;
...@@ -1922,21 +1967,19 @@ static int tipc_connect(struct socket *sock, struct sockaddr *dest, ...@@ -1922,21 +1967,19 @@ static int tipc_connect(struct socket *sock, struct sockaddr *dest,
struct sockaddr_tipc *dst = (struct sockaddr_tipc *)dest; struct sockaddr_tipc *dst = (struct sockaddr_tipc *)dest;
struct msghdr m = {NULL,}; struct msghdr m = {NULL,};
long timeout = (flags & O_NONBLOCK) ? 0 : tsk->conn_timeout; long timeout = (flags & O_NONBLOCK) ? 0 : tsk->conn_timeout;
socket_state previous; int previous;
int res = 0; int res = 0;
lock_sock(sk); lock_sock(sk);
/* DGRAM/RDM connect(), just save the destaddr */ /* DGRAM/RDM connect(), just save the destaddr */
if (sock->state == SS_READY) { if (tipc_sk_type_connectionless(sk)) {
if (dst->family == AF_UNSPEC) { if (dst->family == AF_UNSPEC) {
memset(&tsk->remote, 0, sizeof(struct sockaddr_tipc)); memset(&tsk->peer, 0, sizeof(struct sockaddr_tipc));
tsk->connected = 0;
} else if (destlen != sizeof(struct sockaddr_tipc)) { } else if (destlen != sizeof(struct sockaddr_tipc)) {
res = -EINVAL; res = -EINVAL;
} else { } else {
memcpy(&tsk->remote, dest, destlen); memcpy(&tsk->peer, dest, destlen);
tsk->connected = 1;
} }
goto exit; goto exit;
} }
...@@ -1952,9 +1995,10 @@ static int tipc_connect(struct socket *sock, struct sockaddr *dest, ...@@ -1952,9 +1995,10 @@ static int tipc_connect(struct socket *sock, struct sockaddr *dest,
goto exit; goto exit;
} }
previous = sock->state; previous = sk->sk_state;
switch (sock->state) {
case SS_UNCONNECTED: switch (sk->sk_state) {
case TIPC_OPEN:
/* Send a 'SYN-' to destination */ /* Send a 'SYN-' to destination */
m.msg_name = dest; m.msg_name = dest;
m.msg_namelen = destlen; m.msg_namelen = destlen;
...@@ -1969,27 +2013,29 @@ static int tipc_connect(struct socket *sock, struct sockaddr *dest, ...@@ -1969,27 +2013,29 @@ static int tipc_connect(struct socket *sock, struct sockaddr *dest,
if ((res < 0) && (res != -EWOULDBLOCK)) if ((res < 0) && (res != -EWOULDBLOCK))
goto exit; goto exit;
/* Just entered SS_CONNECTING state; the only /* Just entered TIPC_CONNECTING state; the only
* difference is that return value in non-blocking * difference is that return value in non-blocking
* case is EINPROGRESS, rather than EALREADY. * case is EINPROGRESS, rather than EALREADY.
*/ */
res = -EINPROGRESS; res = -EINPROGRESS;
case SS_CONNECTING: /* fall thru' */
if (previous == SS_CONNECTING) case TIPC_CONNECTING:
if (!timeout) {
if (previous == TIPC_CONNECTING)
res = -EALREADY; res = -EALREADY;
if (!timeout)
goto exit; goto exit;
}
timeout = msecs_to_jiffies(timeout); timeout = msecs_to_jiffies(timeout);
/* Wait until an 'ACK' or 'RST' arrives, or a timeout occurs */ /* Wait until an 'ACK' or 'RST' arrives, or a timeout occurs */
res = tipc_wait_for_connect(sock, &timeout); res = tipc_wait_for_connect(sock, &timeout);
break; break;
case SS_CONNECTED: case TIPC_ESTABLISHED:
res = -EISCONN; res = -EISCONN;
break; break;
default: default:
res = -EINVAL; res = -EINVAL;
break;
} }
exit: exit:
release_sock(sk); release_sock(sk);
return res; return res;
...@@ -2008,15 +2054,9 @@ static int tipc_listen(struct socket *sock, int len) ...@@ -2008,15 +2054,9 @@ static int tipc_listen(struct socket *sock, int len)
int res; int res;
lock_sock(sk); lock_sock(sk);
res = tipc_set_sk_state(sk, TIPC_LISTEN);
if (sock->state != SS_UNCONNECTED)
res = -EINVAL;
else {
sock->state = SS_LISTENING;
res = 0;
}
release_sock(sk); release_sock(sk);
return res; return res;
} }
...@@ -2042,9 +2082,6 @@ static int tipc_wait_for_accept(struct socket *sock, long timeo) ...@@ -2042,9 +2082,6 @@ static int tipc_wait_for_accept(struct socket *sock, long timeo)
err = 0; err = 0;
if (!skb_queue_empty(&sk->sk_receive_queue)) if (!skb_queue_empty(&sk->sk_receive_queue))
break; break;
err = -EINVAL;
if (sock->state != SS_LISTENING)
break;
err = -EAGAIN; err = -EAGAIN;
if (!timeo) if (!timeo)
break; break;
...@@ -2075,7 +2112,7 @@ static int tipc_accept(struct socket *sock, struct socket *new_sock, int flags) ...@@ -2075,7 +2112,7 @@ static int tipc_accept(struct socket *sock, struct socket *new_sock, int flags)
lock_sock(sk); lock_sock(sk);
if (sock->state != SS_LISTENING) { if (sk->sk_state != TIPC_LISTEN) {
res = -EINVAL; res = -EINVAL;
goto exit; goto exit;
} }
...@@ -2086,7 +2123,7 @@ static int tipc_accept(struct socket *sock, struct socket *new_sock, int flags) ...@@ -2086,7 +2123,7 @@ static int tipc_accept(struct socket *sock, struct socket *new_sock, int flags)
buf = skb_peek(&sk->sk_receive_queue); buf = skb_peek(&sk->sk_receive_queue);
res = tipc_sk_create(sock_net(sock->sk), new_sock, 0, 1); res = tipc_sk_create(sock_net(sock->sk), new_sock, 0, 0);
if (res) if (res)
goto exit; goto exit;
security_sk_clone(sock->sk, new_sock->sk); security_sk_clone(sock->sk, new_sock->sk);
...@@ -2106,7 +2143,6 @@ static int tipc_accept(struct socket *sock, struct socket *new_sock, int flags) ...@@ -2106,7 +2143,6 @@ static int tipc_accept(struct socket *sock, struct socket *new_sock, int flags)
/* Connect new socket to it's peer */ /* Connect new socket to it's peer */
tipc_sk_finish_conn(new_tsock, msg_origport(msg), msg_orignode(msg)); tipc_sk_finish_conn(new_tsock, msg_origport(msg), msg_orignode(msg));
new_sock->state = SS_CONNECTED;
tsk_set_importance(new_tsock, msg_importance(msg)); tsk_set_importance(new_tsock, msg_importance(msg));
if (msg_named(msg)) { if (msg_named(msg)) {
...@@ -2146,13 +2182,6 @@ static int tipc_accept(struct socket *sock, struct socket *new_sock, int flags) ...@@ -2146,13 +2182,6 @@ static int tipc_accept(struct socket *sock, struct socket *new_sock, int flags)
static int tipc_shutdown(struct socket *sock, int how) static int tipc_shutdown(struct socket *sock, int how)
{ {
struct sock *sk = sock->sk; struct sock *sk = sock->sk;
struct net *net = sock_net(sk);
struct tipc_sock *tsk = tipc_sk(sk);
struct sk_buff *skb;
u32 dnode = tsk_peer_node(tsk);
u32 dport = tsk_peer_port(tsk);
u32 onode = tipc_own_addr(net);
u32 oport = tsk->portid;
int res; int res;
if (how != SHUT_RDWR) if (how != SHUT_RDWR)
...@@ -2160,45 +2189,17 @@ static int tipc_shutdown(struct socket *sock, int how) ...@@ -2160,45 +2189,17 @@ static int tipc_shutdown(struct socket *sock, int how)
lock_sock(sk); lock_sock(sk);
switch (sock->state) { __tipc_shutdown(sock, TIPC_CONN_SHUTDOWN);
case SS_CONNECTING: sk->sk_shutdown = SEND_SHUTDOWN;
case SS_CONNECTED:
restart:
dnode = tsk_peer_node(tsk);
/* Disconnect and send a 'FIN+' or 'FIN-' message to peer */
skb = __skb_dequeue(&sk->sk_receive_queue);
if (skb) {
if (TIPC_SKB_CB(skb)->handle != NULL) {
kfree_skb(skb);
goto restart;
}
tipc_sk_respond(sk, skb, TIPC_CONN_SHUTDOWN);
} else {
skb = tipc_msg_create(TIPC_CRITICAL_IMPORTANCE,
TIPC_CONN_MSG, SHORT_H_SIZE,
0, dnode, onode, dport, oport,
TIPC_CONN_SHUTDOWN);
if (skb)
tipc_node_xmit_skb(net, skb, dnode, tsk->portid);
}
tsk->connected = 0;
sock->state = SS_DISCONNECTING;
tipc_node_remove_conn(net, dnode, tsk->portid);
/* fall through */
case SS_DISCONNECTING:
if (sk->sk_state == TIPC_DISCONNECTING) {
/* Discard any unreceived messages */ /* Discard any unreceived messages */
__skb_queue_purge(&sk->sk_receive_queue); __skb_queue_purge(&sk->sk_receive_queue);
/* Wake up anyone sleeping in poll */ /* Wake up anyone sleeping in poll */
sk->sk_state_change(sk); sk->sk_state_change(sk);
res = 0; res = 0;
break; } else {
default:
res = -ENOTCONN; res = -ENOTCONN;
} }
...@@ -2215,17 +2216,16 @@ static void tipc_sk_timeout(unsigned long data) ...@@ -2215,17 +2216,16 @@ static void tipc_sk_timeout(unsigned long data)
u32 own_node = tsk_own_node(tsk); u32 own_node = tsk_own_node(tsk);
bh_lock_sock(sk); bh_lock_sock(sk);
if (!tsk->connected) { if (!tipc_sk_connected(sk)) {
bh_unlock_sock(sk); bh_unlock_sock(sk);
goto exit; goto exit;
} }
peer_port = tsk_peer_port(tsk); peer_port = tsk_peer_port(tsk);
peer_node = tsk_peer_node(tsk); peer_node = tsk_peer_node(tsk);
if (tsk->probing_state == TIPC_CONN_PROBING) { if (tsk->probe_unacked) {
if (!sock_owned_by_user(sk)) { if (!sock_owned_by_user(sk)) {
sk->sk_socket->state = SS_DISCONNECTING; tipc_set_sk_state(sk, TIPC_DISCONNECTING);
tsk->connected = 0;
tipc_node_remove_conn(sock_net(sk), tsk_peer_node(tsk), tipc_node_remove_conn(sock_net(sk), tsk_peer_node(tsk),
tsk_peer_port(tsk)); tsk_peer_port(tsk));
sk->sk_state_change(sk); sk->sk_state_change(sk);
...@@ -2234,13 +2234,15 @@ static void tipc_sk_timeout(unsigned long data) ...@@ -2234,13 +2234,15 @@ static void tipc_sk_timeout(unsigned long data)
sk_reset_timer(sk, &sk->sk_timer, (HZ / 20)); sk_reset_timer(sk, &sk->sk_timer, (HZ / 20));
} }
} else { bh_unlock_sock(sk);
goto exit;
}
skb = tipc_msg_create(CONN_MANAGER, CONN_PROBE, skb = tipc_msg_create(CONN_MANAGER, CONN_PROBE,
INT_H_SIZE, 0, peer_node, own_node, INT_H_SIZE, 0, peer_node, own_node,
peer_port, tsk->portid, TIPC_OK); peer_port, tsk->portid, TIPC_OK);
tsk->probing_state = TIPC_CONN_PROBING; tsk->probe_unacked = true;
sk_reset_timer(sk, &sk->sk_timer, jiffies + tsk->probing_intv); sk_reset_timer(sk, &sk->sk_timer, jiffies + CONN_PROBING_INTERVAL);
}
bh_unlock_sock(sk); bh_unlock_sock(sk);
if (skb) if (skb)
tipc_node_xmit_skb(sock_net(sk), skb, peer_node, tsk->portid); tipc_node_xmit_skb(sock_net(sk), skb, peer_node, tsk->portid);
...@@ -2251,11 +2253,12 @@ static void tipc_sk_timeout(unsigned long data) ...@@ -2251,11 +2253,12 @@ static void tipc_sk_timeout(unsigned long data)
static int tipc_sk_publish(struct tipc_sock *tsk, uint scope, static int tipc_sk_publish(struct tipc_sock *tsk, uint scope,
struct tipc_name_seq const *seq) struct tipc_name_seq const *seq)
{ {
struct net *net = sock_net(&tsk->sk); struct sock *sk = &tsk->sk;
struct net *net = sock_net(sk);
struct publication *publ; struct publication *publ;
u32 key; u32 key;
if (tsk->connected) if (tipc_sk_connected(sk))
return -EINVAL; return -EINVAL;
key = tsk->portid + tsk->pub_count + 1; key = tsk->portid + tsk->pub_count + 1;
if (key == tsk->portid) if (key == tsk->portid)
...@@ -2713,6 +2716,7 @@ static int __tipc_nl_add_sk(struct sk_buff *skb, struct netlink_callback *cb, ...@@ -2713,6 +2716,7 @@ static int __tipc_nl_add_sk(struct sk_buff *skb, struct netlink_callback *cb,
struct nlattr *attrs; struct nlattr *attrs;
struct net *net = sock_net(skb->sk); struct net *net = sock_net(skb->sk);
struct tipc_net *tn = net_generic(net, tipc_net_id); struct tipc_net *tn = net_generic(net, tipc_net_id);
struct sock *sk = &tsk->sk;
hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq,
&tipc_genl_family, NLM_F_MULTI, TIPC_NL_SOCK_GET); &tipc_genl_family, NLM_F_MULTI, TIPC_NL_SOCK_GET);
...@@ -2727,7 +2731,7 @@ static int __tipc_nl_add_sk(struct sk_buff *skb, struct netlink_callback *cb, ...@@ -2727,7 +2731,7 @@ static int __tipc_nl_add_sk(struct sk_buff *skb, struct netlink_callback *cb,
if (nla_put_u32(skb, TIPC_NLA_SOCK_ADDR, tn->own_addr)) if (nla_put_u32(skb, TIPC_NLA_SOCK_ADDR, tn->own_addr))
goto attr_msg_cancel; goto attr_msg_cancel;
if (tsk->connected) { if (tipc_sk_connected(sk)) {
err = __tipc_nl_add_sk_con(skb, tsk); err = __tipc_nl_add_sk_con(skb, tsk);
if (err) if (err)
goto attr_msg_cancel; goto attr_msg_cancel;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment