tipc: clean up connection protocol reception function
[firefly-linux-kernel-4.4.55.git] / net / tipc / socket.c
index ef0475568f9e39cfa3aa24d1d4d7a7132ea059a4..1762323156af262cb9cfdbd10d7c33fe51249288 100644 (file)
 
 #include "core.h"
 #include "port.h"
+#include "name_table.h"
 #include "node.h"
-
+#include "link.h"
 #include <linux/export.h>
+#include "link.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 TIPC_FWD_MSG           1
 
 static int tipc_backlog_rcv(struct sock *sk, struct sk_buff *skb);
 static void tipc_data_ready(struct sock *sk);
@@ -123,9 +126,12 @@ static void advance_rx_queue(struct sock *sk)
 static void reject_rx_queue(struct sock *sk)
 {
        struct sk_buff *buf;
+       u32 dnode;
 
-       while ((buf = __skb_dequeue(&sk->sk_receive_queue)))
-               tipc_reject_msg(buf, TIPC_ERR_NO_PORT);
+       while ((buf = __skb_dequeue(&sk->sk_receive_queue))) {
+               if (tipc_msg_reverse(buf, &dnode, TIPC_ERR_NO_PORT))
+                       tipc_link_xmit2(buf, dnode, 0);
+       }
 }
 
 /**
@@ -201,6 +207,7 @@ static int tipc_sk_create(struct net *net, struct socket *sock,
        sk->sk_data_ready = tipc_data_ready;
        sk->sk_write_space = tipc_write_space;
        tsk->conn_timeout = CONN_TIMEOUT_DEFAULT;
+       tsk->port.sent = 0;
        atomic_set(&tsk->dupl_rcvcnt, 0);
        tipc_port_unlock(port);
 
@@ -303,6 +310,7 @@ static int tipc_release(struct socket *sock)
        struct tipc_sock *tsk;
        struct tipc_port *port;
        struct sk_buff *buf;
+       u32 dnode;
 
        /*
         * Exit if socket isn't fully initialized (occurs when a failed accept()
@@ -331,7 +339,8 @@ static int tipc_release(struct socket *sock)
                                sock->state = SS_DISCONNECTING;
                                tipc_port_disconnect(port->ref);
                        }
-                       tipc_reject_msg(buf, TIPC_ERR_NO_PORT);
+                       if (tipc_msg_reverse(buf, &dnode, TIPC_ERR_NO_PORT))
+                               tipc_link_xmit2(buf, dnode, 0);
                }
        }
 
@@ -525,6 +534,46 @@ static unsigned int tipc_poll(struct file *file, struct socket *sock,
        return mask;
 }
 
+/**
+ * tipc_sk_proto_rcv - receive a connection mng protocol message
+ * @tsk: receiving socket
+ * @dnode: node to send response message to, if any
+ * @buf: buffer containing protocol message
+ * Returns 0 (TIPC_OK) if message was consumed, 1 (TIPC_FWD_MSG) if
+ * (CONN_PROBE_REPLY) message should be forwarded.
+ */
+int tipc_sk_proto_rcv(struct tipc_sock *tsk, u32 *dnode, struct sk_buff *buf)
+{
+       struct tipc_msg *msg = buf_msg(buf);
+       struct tipc_port *port = &tsk->port;
+       int wakeable;
+
+       /* Ignore if connection cannot be validated: */
+       if (!port->connected || !tipc_port_peer_msg(port, msg))
+               goto exit;
+
+       port->probing_state = TIPC_CONN_OK;
+
+       if (msg_type(msg) == CONN_ACK) {
+               wakeable = tipc_port_congested(port) && port->congested;
+               port->acked += msg_msgcnt(msg);
+               if (!tipc_port_congested(port)) {
+                       port->congested = 0;
+                       if (wakeable)
+                               tipc_port_wakeup(port);
+               }
+       } else if (msg_type(msg) == CONN_PROBE) {
+               if (!tipc_msg_reverse(buf, dnode, TIPC_OK))
+                       return TIPC_OK;
+               msg_set_type(msg, CONN_PROBE_REPLY);
+               return TIPC_FWD_MSG;
+       }
+       /* Do nothing if msg_type() == CONN_PROBE_REPLY */
+exit:
+       kfree_skb(buf);
+       return TIPC_OK;
+}
+
 /**
  * dest_name_check - verify user is permitted to send to specified port name
  * @dest: destination address
@@ -539,6 +588,8 @@ static int dest_name_check(struct sockaddr_tipc *dest, struct msghdr *m)
 {
        struct tipc_cfg_msg_hdr hdr;
 
+       if (unlikely(dest->addrtype == TIPC_ADDR_ID))
+               return 0;
        if (likely(dest->addr.name.name.type >= TIPC_RESERVED_TYPES))
                return 0;
        if (likely(dest->addr.name.name.type == TIPC_TOP_SRV))
@@ -581,13 +632,49 @@ static int tipc_wait_for_sndmsg(struct socket *sock, long *timeo_p)
        return 0;
 }
 
+/**
+ * tipc_sendmcast - send multicast message
+ * @sock: socket structure
+ * @seq: destination address
+ * @iov: message data to send
+ * @dsz: total length of message data
+ * @timeo: timeout to wait for wakeup
+ *
+ * Called from function tipc_sendmsg(), which has done all sanity checks
+ * Returns the number of bytes sent on success, or errno
+ */
+static int tipc_sendmcast(struct  socket *sock, struct tipc_name_seq *seq,
+                         struct iovec *iov, size_t dsz, long timeo)
+{
+       struct sock *sk = sock->sk;
+       struct tipc_sock *tsk = tipc_sk(sk);
+       int rc;
+
+       do {
+               if (sock->state != SS_READY) {
+                       rc = -EOPNOTSUPP;
+                       break;
+               }
+               rc = tipc_port_mcast_xmit(&tsk->port, seq, iov, dsz);
+               if (likely(rc >= 0)) {
+                       if (sock->state != SS_READY)
+                               sock->state = SS_CONNECTING;
+                       break;
+               }
+               if (rc != -ELINKCONG)
+                       break;
+               rc = tipc_wait_for_sndmsg(sock, &timeo);
+       } while (!rc);
+
+       return rc;
+}
 
 /**
  * tipc_sendmsg - send message in connectionless manner
  * @iocb: if NULL, indicates that socket lock is already held
  * @sock: socket structure
  * @m: message to send
- * @total_len: length of message
+ * @dsz: amount of user data to be sent
  *
  * Message must have an destination specified explicitly.
  * Used for SOCK_RDM and SOCK_DGRAM messages,
@@ -597,93 +684,116 @@ static int tipc_wait_for_sndmsg(struct socket *sock, long *timeo_p)
  * Returns the number of bytes sent on success, or errno otherwise
  */
 static int tipc_sendmsg(struct kiocb *iocb, struct socket *sock,
-                       struct msghdr *m, size_t total_len)
+                       struct msghdr *m, size_t dsz)
 {
+       DECLARE_SOCKADDR(struct sockaddr_tipc *, dest, m->msg_name);
        struct sock *sk = sock->sk;
        struct tipc_sock *tsk = tipc_sk(sk);
        struct tipc_port *port = &tsk->port;
-       DECLARE_SOCKADDR(struct sockaddr_tipc *, dest, m->msg_name);
-       int needs_conn;
+       struct tipc_msg *mhdr = &port->phdr;
+       struct iovec *iov = m->msg_iov;
+       u32 dnode, dport;
+       struct sk_buff *buf;
+       struct tipc_name_seq *seq = &dest->addr.nameseq;
+       u32 mtu;
        long timeo;
-       int res = -EINVAL;
+       int rc = -EINVAL;
 
        if (unlikely(!dest))
                return -EDESTADDRREQ;
+
        if (unlikely((m->msg_namelen < sizeof(*dest)) ||
                     (dest->family != AF_TIPC)))
                return -EINVAL;
-       if (total_len > TIPC_MAX_USER_MSG_SIZE)
+
+       if (dsz > TIPC_MAX_USER_MSG_SIZE)
                return -EMSGSIZE;
 
        if (iocb)
                lock_sock(sk);
 
-       needs_conn = (sock->state != SS_READY);
-       if (unlikely(needs_conn)) {
+       if (unlikely(sock->state != SS_READY)) {
                if (sock->state == SS_LISTENING) {
-                       res = -EPIPE;
+                       rc = -EPIPE;
                        goto exit;
                }
                if (sock->state != SS_UNCONNECTED) {
-                       res = -EISCONN;
+                       rc = -EISCONN;
                        goto exit;
                }
                if (tsk->port.published) {
-                       res = -EOPNOTSUPP;
+                       rc = -EOPNOTSUPP;
                        goto exit;
                }
                if (dest->addrtype == TIPC_ADDR_NAME) {
                        tsk->port.conn_type = dest->addr.name.name.type;
                        tsk->port.conn_instance = dest->addr.name.name.instance;
                }
-
-               /* Abort any pending connection attempts (very unlikely) */
-               reject_rx_queue(sk);
        }
+       rc = dest_name_check(dest, m);
+       if (rc)
+               goto exit;
 
        timeo = sock_sndtimeo(sk, m->msg_flags & MSG_DONTWAIT);
-       do {
-               if (dest->addrtype == TIPC_ADDR_NAME) {
-                       res = dest_name_check(dest, m);
-                       if (res)
-                               break;
-                       res = tipc_send2name(port,
-                                            &dest->addr.name.name,
-                                            dest->addr.name.domain,
-                                            m->msg_iov,
-                                            total_len);
-               } else if (dest->addrtype == TIPC_ADDR_ID) {
-                       res = tipc_send2port(port,
-                                            &dest->addr.id,
-                                            m->msg_iov,
-                                            total_len);
-               } else if (dest->addrtype == TIPC_ADDR_MCAST) {
-                       if (needs_conn) {
-                               res = -EOPNOTSUPP;
-                               break;
-                       }
-                       res = dest_name_check(dest, m);
-                       if (res)
-                               break;
-                       res = tipc_port_mcast_xmit(port,
-                                                  &dest->addr.nameseq,
-                                                  m->msg_iov,
-                                                  total_len);
+
+       if (dest->addrtype == TIPC_ADDR_MCAST) {
+               rc = tipc_sendmcast(sock, seq, iov, dsz, timeo);
+               goto exit;
+       } else if (dest->addrtype == TIPC_ADDR_NAME) {
+               u32 type = dest->addr.name.name.type;
+               u32 inst = dest->addr.name.name.instance;
+               u32 domain = dest->addr.name.domain;
+
+               dnode = domain;
+               msg_set_type(mhdr, TIPC_NAMED_MSG);
+               msg_set_hdr_sz(mhdr, NAMED_H_SIZE);
+               msg_set_nametype(mhdr, type);
+               msg_set_nameinst(mhdr, inst);
+               msg_set_lookup_scope(mhdr, tipc_addr_scope(domain));
+               dport = tipc_nametbl_translate(type, inst, &dnode);
+               msg_set_destnode(mhdr, dnode);
+               msg_set_destport(mhdr, dport);
+               if (unlikely(!dport && !dnode)) {
+                       rc = -EHOSTUNREACH;
+                       goto exit;
                }
-               if (likely(res != -ELINKCONG)) {
-                       if (needs_conn && (res >= 0))
+       } else if (dest->addrtype == TIPC_ADDR_ID) {
+               dnode = dest->addr.id.node;
+               msg_set_type(mhdr, TIPC_DIRECT_MSG);
+               msg_set_lookup_scope(mhdr, 0);
+               msg_set_destnode(mhdr, dnode);
+               msg_set_destport(mhdr, dest->addr.id.ref);
+               msg_set_hdr_sz(mhdr, BASIC_H_SIZE);
+       }
+
+new_mtu:
+       mtu = tipc_node_get_mtu(dnode, tsk->port.ref);
+       rc = tipc_msg_build2(mhdr, iov, 0, dsz, mtu, &buf);
+       if (rc < 0)
+               goto exit;
+
+       do {
+               rc = tipc_link_xmit2(buf, dnode, tsk->port.ref);
+               if (likely(rc >= 0)) {
+                       if (sock->state != SS_READY)
                                sock->state = SS_CONNECTING;
+                       rc = dsz;
                        break;
                }
-               res = tipc_wait_for_sndmsg(sock, &timeo);
-               if (res)
+               if (rc == -EMSGSIZE)
+                       goto new_mtu;
+
+               if (rc != -ELINKCONG)
                        break;
-       } while (1);
+
+               rc = tipc_wait_for_sndmsg(sock, &timeo);
+       } while (!rc);
 
 exit:
        if (iocb)
                release_sock(sk);
-       return res;
+
+       return rc;
 }
 
 static int tipc_wait_for_sndpkt(struct socket *sock, long *timeo_p)
@@ -716,30 +826,40 @@ static int tipc_wait_for_sndpkt(struct socket *sock, long *timeo_p)
 }
 
 /**
- * tipc_send_packet - send a connection-oriented message
- * @iocb: if NULL, indicates that socket lock is already held
+ * tipc_send_stream - send stream-oriented data
+ * @iocb: (unused)
  * @sock: socket structure
- * @m: message to send
- * @total_len: length of message
+ * @m: data to send
+ * @dsz: total length of data to be transmitted
  *
- * Used for SOCK_SEQPACKET messages and SOCK_STREAM data.
+ * Used for SOCK_STREAM data.
  *
- * Returns the number of bytes sent on success, or errno otherwise
+ * Returns the number of bytes sent on success (or partial success),
+ * or errno if no data sent
  */
-static int tipc_send_packet(struct kiocb *iocb, struct socket *sock,
-                           struct msghdr *m, size_t total_len)
+static int tipc_send_stream(struct kiocb *iocb, struct socket *sock,
+                           struct msghdr *m, size_t dsz)
 {
        struct sock *sk = sock->sk;
        struct tipc_sock *tsk = tipc_sk(sk);
+       struct tipc_port *port = &tsk->port;
+       struct tipc_msg *mhdr = &port->phdr;
+       struct sk_buff *buf;
        DECLARE_SOCKADDR(struct sockaddr_tipc *, dest, m->msg_name);
-       int res = -EINVAL;
+       u32 ref = port->ref;
+       int rc = -EINVAL;
        long timeo;
+       u32 dnode;
+       uint mtu, send, sent = 0;
 
        /* Handle implied connection establishment */
-       if (unlikely(dest))
-               return tipc_sendmsg(iocb, sock, m, total_len);
-
-       if (total_len > TIPC_MAX_USER_MSG_SIZE)
+       if (unlikely(dest)) {
+               rc = tipc_sendmsg(iocb, sock, m, dsz);
+               if (dsz && (dsz == rc))
+                       tsk->port.sent = 1;
+               return rc;
+       }
+       if (dsz > (uint)INT_MAX)
                return -EMSGSIZE;
 
        if (iocb)
@@ -747,123 +867,68 @@ static int tipc_send_packet(struct kiocb *iocb, struct socket *sock,
 
        if (unlikely(sock->state != SS_CONNECTED)) {
                if (sock->state == SS_DISCONNECTING)
-                       res = -EPIPE;
+                       rc = -EPIPE;
                else
-                       res = -ENOTCONN;
+                       rc = -ENOTCONN;
                goto exit;
        }
 
        timeo = sock_sndtimeo(sk, m->msg_flags & MSG_DONTWAIT);
+       dnode = tipc_port_peernode(port);
+       port->congested = 1;
+
+next:
+       mtu = port->max_pkt;
+       send = min_t(uint, dsz - sent, TIPC_MAX_USER_MSG_SIZE);
+       rc = tipc_msg_build2(mhdr, m->msg_iov, sent, send, mtu, &buf);
+       if (unlikely(rc < 0))
+               goto exit;
        do {
-               res = tipc_send(&tsk->port, m->msg_iov, total_len);
-               if (likely(res != -ELINKCONG))
-                       break;
-               res = tipc_wait_for_sndpkt(sock, &timeo);
-               if (res)
-                       break;
-       } while (1);
+               port->congested = 1;
+               if (likely(!tipc_port_congested(port))) {
+                       rc = tipc_link_xmit2(buf, dnode, ref);
+                       if (likely(!rc)) {
+                               port->sent++;
+                               sent += send;
+                               if (sent == dsz)
+                                       break;
+                               goto next;
+                       }
+                       if (rc == -EMSGSIZE) {
+                               port->max_pkt = tipc_node_get_mtu(dnode, ref);
+                               goto next;
+                       }
+                       if (rc != -ELINKCONG)
+                               break;
+               }
+               rc = tipc_wait_for_sndpkt(sock, &timeo);
+       } while (!rc);
+
+       port->congested = 0;
 exit:
        if (iocb)
                release_sock(sk);
-       return res;
+       return sent ? sent : rc;
 }
 
 /**
- * tipc_send_stream - send stream-oriented data
- * @iocb: (unused)
+ * tipc_send_packet - send a connection-oriented message
+ * @iocb: if NULL, indicates that socket lock is already held
  * @sock: socket structure
- * @m: data to send
- * @total_len: total length of data to be sent
+ * @m: message to send
+ * @dsz: length of data to be transmitted
  *
- * Used for SOCK_STREAM data.
+ * Used for SOCK_SEQPACKET messages.
  *
- * Returns the number of bytes sent on success (or partial success),
- * or errno if no data sent
+ * Returns the number of bytes sent on success, or errno otherwise
  */
-static int tipc_send_stream(struct kiocb *iocb, struct socket *sock,
-                           struct msghdr *m, size_t total_len)
+static int tipc_send_packet(struct kiocb *iocb, struct socket *sock,
+                           struct msghdr *m, size_t dsz)
 {
-       struct sock *sk = sock->sk;
-       struct tipc_sock *tsk = tipc_sk(sk);
-       struct msghdr my_msg;
-       struct iovec my_iov;
-       struct iovec *curr_iov;
-       int curr_iovlen;
-       char __user *curr_start;
-       u32 hdr_size;
-       int curr_left;
-       int bytes_to_send;
-       int bytes_sent;
-       int res;
-
-       lock_sock(sk);
-
-       /* Handle special cases where there is no connection */
-       if (unlikely(sock->state != SS_CONNECTED)) {
-               if (sock->state == SS_UNCONNECTED)
-                       res = tipc_send_packet(NULL, sock, m, total_len);
-               else
-                       res = sock->state == SS_DISCONNECTING ? -EPIPE : -ENOTCONN;
-               goto exit;
-       }
-
-       if (unlikely(m->msg_name)) {
-               res = -EISCONN;
-               goto exit;
-       }
-
-       if (total_len > (unsigned int)INT_MAX) {
-               res = -EMSGSIZE;
-               goto exit;
-       }
-
-       /*
-        * Send each iovec entry using one or more messages
-        *
-        * Note: This algorithm is good for the most likely case
-        * (i.e. one large iovec entry), but could be improved to pass sets
-        * of small iovec entries into send_packet().
-        */
-       curr_iov = m->msg_iov;
-       curr_iovlen = m->msg_iovlen;
-       my_msg.msg_iov = &my_iov;
-       my_msg.msg_iovlen = 1;
-       my_msg.msg_flags = m->msg_flags;
-       my_msg.msg_name = NULL;
-       bytes_sent = 0;
-
-       hdr_size = msg_hdr_sz(&tsk->port.phdr);
-
-       while (curr_iovlen--) {
-               curr_start = curr_iov->iov_base;
-               curr_left = curr_iov->iov_len;
-
-               while (curr_left) {
-                       bytes_to_send = tsk->port.max_pkt - hdr_size;
-                       if (bytes_to_send > TIPC_MAX_USER_MSG_SIZE)
-                               bytes_to_send = TIPC_MAX_USER_MSG_SIZE;
-                       if (curr_left < bytes_to_send)
-                               bytes_to_send = curr_left;
-                       my_iov.iov_base = curr_start;
-                       my_iov.iov_len = bytes_to_send;
-                       res = tipc_send_packet(NULL, sock, &my_msg,
-                                              bytes_to_send);
-                       if (res < 0) {
-                               if (bytes_sent)
-                                       res = bytes_sent;
-                               goto exit;
-                       }
-                       curr_left -= bytes_to_send;
-                       curr_start += bytes_to_send;
-                       bytes_sent += bytes_to_send;
-               }
+       if (dsz > TIPC_MAX_USER_MSG_SIZE)
+               return -EMSGSIZE;
 
-               curr_iov++;
-       }
-       res = bytes_sent;
-exit:
-       release_sock(sk);
-       return res;
+       return tipc_send_stream(iocb, sock, m, dsz);
 }
 
 /**
@@ -1269,17 +1334,16 @@ static void tipc_data_ready(struct sock *sk)
  * @tsk: TIPC socket
  * @msg: message
  *
- * Returns TIPC error status code and socket error status code
- * once it encounters some errors
+ * Returns 0 (TIPC_OK) if everyting ok, -TIPC_ERR_NO_PORT otherwise
  */
-static u32 filter_connect(struct tipc_sock *tsk, struct sk_buff **buf)
+static int filter_connect(struct tipc_sock *tsk, struct sk_buff **buf)
 {
        struct sock *sk = &tsk->sk;
        struct tipc_port *port = &tsk->port;
        struct socket *sock = sk->sk_socket;
        struct tipc_msg *msg = buf_msg(*buf);
 
-       u32 retval = TIPC_ERR_NO_PORT;
+       int retval = -TIPC_ERR_NO_PORT;
        int res;
 
        if (msg_mcast(msg))
@@ -1382,32 +1446,37 @@ static unsigned int rcvbuf_limit(struct sock *sk, struct sk_buff *buf)
  *
  * Called with socket lock already taken; port lock may also be taken.
  *
- * Returns TIPC error status code (TIPC_OK if message is not to be rejected)
+ * Returns 0 (TIPC_OK) if message was consumed, -TIPC error code if message
+ * to be rejected, 1 (TIPC_FWD_MSG) if (CONN_MANAGER) message to be forwarded
  */
-static u32 filter_rcv(struct sock *sk, struct sk_buff *buf)
+static int filter_rcv(struct sock *sk, struct sk_buff *buf)
 {
        struct socket *sock = sk->sk_socket;
        struct tipc_sock *tsk = tipc_sk(sk);
        struct tipc_msg *msg = buf_msg(buf);
        unsigned int limit = rcvbuf_limit(sk, buf);
-       u32 res = TIPC_OK;
+       u32 onode;
+       int rc = TIPC_OK;
+
+       if (unlikely(msg_user(msg) == CONN_MANAGER))
+               return tipc_sk_proto_rcv(tsk, &onode, buf);
 
        /* Reject message if it is wrong sort of message for socket */
        if (msg_type(msg) > TIPC_DIRECT_MSG)
-               return TIPC_ERR_NO_PORT;
+               return -TIPC_ERR_NO_PORT;
 
        if (sock->state == SS_READY) {
                if (msg_connected(msg))
-                       return TIPC_ERR_NO_PORT;
+                       return -TIPC_ERR_NO_PORT;
        } else {
-               res = filter_connect(tsk, &buf);
-               if (res != TIPC_OK || buf == NULL)
-                       return res;
+               rc = filter_connect(tsk, &buf);
+               if (rc != TIPC_OK || buf == NULL)
+                       return rc;
        }
 
        /* Reject message if there isn't room to queue it */
        if (sk_rmem_alloc_get(sk) + buf->truesize >= limit)
-               return TIPC_ERR_OVERLOAD;
+               return -TIPC_ERR_OVERLOAD;
 
        /* Enqueue message */
        TIPC_SKB_CB(buf)->handle = NULL;
@@ -1429,16 +1498,23 @@ static u32 filter_rcv(struct sock *sk, struct sk_buff *buf)
  */
 static int tipc_backlog_rcv(struct sock *sk, struct sk_buff *buf)
 {
-       u32 res;
+       int rc;
+       u32 onode;
        struct tipc_sock *tsk = tipc_sk(sk);
        uint truesize = buf->truesize;
 
-       res = filter_rcv(sk, buf);
-       if (unlikely(res))
-               tipc_reject_msg(buf, res);
+       rc = filter_rcv(sk, buf);
+
+       if (likely(!rc)) {
+               if (atomic_read(&tsk->dupl_rcvcnt) < TIPC_CONN_OVERLOAD_LIMIT)
+                       atomic_add(truesize, &tsk->dupl_rcvcnt);
+               return 0;
+       }
 
-       if (atomic_read(&tsk->dupl_rcvcnt) < TIPC_CONN_OVERLOAD_LIMIT)
-               atomic_add(truesize, &tsk->dupl_rcvcnt);
+       if ((rc < 0) && !tipc_msg_reverse(buf, &onode, -rc))
+               return 0;
+
+       tipc_link_xmit2(buf, onode, 0);
 
        return 0;
 }
@@ -1455,19 +1531,14 @@ int tipc_sk_rcv(struct sk_buff *buf)
        struct tipc_port *port;
        struct sock *sk;
        u32 dport = msg_destport(buf_msg(buf));
-       int err = TIPC_OK;
+       int rc = TIPC_OK;
        uint limit;
+       u32 dnode;
 
-       /* Forward unresolved named message */
-       if (unlikely(!dport)) {
-               tipc_net_route_msg(buf);
-               return 0;
-       }
-
-       /* Validate destination */
+       /* Validate destination and message */
        port = tipc_port_lock(dport);
        if (unlikely(!port)) {
-               err = TIPC_ERR_NO_PORT;
+               rc = tipc_msg_eval(buf, &dnode);
                goto exit;
        }
 
@@ -1478,23 +1549,25 @@ int tipc_sk_rcv(struct sk_buff *buf)
        bh_lock_sock(sk);
 
        if (!sock_owned_by_user(sk)) {
-               err = filter_rcv(sk, buf);
+               rc = filter_rcv(sk, buf);
        } else {
                if (sk->sk_backlog.len == 0)
                        atomic_set(&tsk->dupl_rcvcnt, 0);
                limit = rcvbuf_limit(sk, buf) + atomic_read(&tsk->dupl_rcvcnt);
                if (sk_add_backlog(sk, buf, limit))
-                       err = TIPC_ERR_OVERLOAD;
+                       rc = -TIPC_ERR_OVERLOAD;
        }
-
        bh_unlock_sock(sk);
        tipc_port_unlock(port);
 
-       if (likely(!err))
+       if (likely(!rc))
                return 0;
 exit:
-       tipc_reject_msg(buf, err);
-       return -EHOSTUNREACH;
+       if ((rc < 0) && !tipc_msg_reverse(buf, &dnode, -rc))
+               return -EHOSTUNREACH;
+
+       tipc_link_xmit2(buf, dnode, 0);
+       return (rc < 0) ? -EHOSTUNREACH : 0;
 }
 
 static int tipc_wait_for_connect(struct socket *sock, long *timeo_p)
@@ -1758,6 +1831,7 @@ static int tipc_shutdown(struct socket *sock, int how)
        struct tipc_sock *tsk = tipc_sk(sk);
        struct tipc_port *port = &tsk->port;
        struct sk_buff *buf;
+       u32 peer;
        int res;
 
        if (how != SHUT_RDWR)
@@ -1778,7 +1852,8 @@ restart:
                                goto restart;
                        }
                        tipc_port_disconnect(port->ref);
-                       tipc_reject_msg(buf, TIPC_CONN_SHUTDOWN);
+                       if (tipc_msg_reverse(buf, &peer, TIPC_CONN_SHUTDOWN))
+                               tipc_link_xmit2(buf, peer, 0);
                } else {
                        tipc_port_shutdown(port->ref);
                }