ipv4: fix redirect handling
[linux-2.6.git] / net / tipc / socket.c
index f7ac94d..adb2eff 100644 (file)
@@ -2,7 +2,7 @@
  * net/tipc/socket.c: TIPC socket API
  *
  * Copyright (c) 2001-2007, Ericsson AB
- * Copyright (c) 2004-2008, Wind River Systems
+ * Copyright (c) 2004-2008, 2010-2011, Wind River Systems
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * POSSIBILITY OF SUCH DAMAGE.
  */
 
-#include <linux/module.h>
-#include <linux/types.h>
-#include <linux/net.h>
-#include <linux/socket.h>
-#include <linux/errno.h>
-#include <linux/mm.h>
-#include <linux/poll.h>
-#include <linux/fcntl.h>
-#include <linux/gfp.h>
-#include <asm/string.h>
-#include <asm/atomic.h>
 #include <net/sock.h>
 
-#include <linux/tipc.h>
-#include <linux/tipc_config.h>
-#include <net/tipc/tipc_msg.h>
-#include <net/tipc/tipc_port.h>
-
 #include "core.h"
+#include "port.h"
 
 #define SS_LISTENING   -1      /* socket is listening */
 #define SS_READY       -2      /* socket is connectionless */
@@ -70,6 +55,9 @@ struct tipc_sock {
 #define tipc_sk(sk) ((struct tipc_sock *)(sk))
 #define tipc_sk_port(sk) ((struct tipc_port *)(tipc_sk(sk)->p))
 
+#define tipc_rx_ready(sock) (!skb_queue_empty(&sock->sk->sk_receive_queue) || \
+                       (sock->state == SS_DISCONNECTING))
+
 static int backlog_rcv(struct sock *sk, struct sk_buff *skb);
 static u32 dispatch(struct tipc_port *tport, struct sk_buff *buf);
 static void wakeupdispatch(struct tipc_port *tport);
@@ -80,7 +68,7 @@ static const struct proto_ops msg_ops;
 
 static struct proto tipc_proto;
 
-static int sockets_enabled = 0;
+static int sockets_enabled;
 
 static atomic_t tipc_queue_size = ATOMIC_INIT(0);
 
@@ -253,7 +241,6 @@ static int tipc_create(struct net *net, struct socket *sock, int protocol,
                        tipc_set_portunreliable(tp_ptr->ref, 1);
        }
 
-       atomic_inc(&tipc_user_count);
        return 0;
 }
 
@@ -302,7 +289,7 @@ static int release(struct socket *sock)
                if (buf == NULL)
                        break;
                atomic_dec(&tipc_queue_size);
-               if (TIPC_SKB_CB(buf)->handle != msg_data(buf_msg(buf)))
+               if (TIPC_SKB_CB(buf)->handle != 0)
                        buf_discard(buf);
                else {
                        if ((sock->state == SS_CONNECTING) ||
@@ -333,7 +320,6 @@ static int release(struct socket *sock)
        sock_put(sk);
        sock->sk = NULL;
 
-       atomic_dec(&tipc_user_count);
        return res;
 }
 
@@ -387,7 +373,7 @@ static int bind(struct socket *sock, struct sockaddr *uaddr, int uaddr_len)
  *
  * NOTE: This routine doesn't need to take the socket lock since it only
  *       accesses socket information that is unchanging (or which changes in
- *      a completely predictable manner).
+ *       a completely predictable manner).
  */
 
 static int get_name(struct socket *sock, struct sockaddr *uaddr,
@@ -396,6 +382,7 @@ static int get_name(struct socket *sock, struct sockaddr *uaddr,
        struct sockaddr_tipc *addr = (struct sockaddr_tipc *)uaddr;
        struct tipc_sock *tsock = tipc_sk(sock->sk);
 
+       memset(addr, 0, sizeof(*addr));
        if (peer) {
                if ((sock->state != SS_CONNECTED) &&
                        ((peer != 2) || (sock->state != SS_DISCONNECTING)))
@@ -403,7 +390,8 @@ static int get_name(struct socket *sock, struct sockaddr *uaddr,
                addr->addr.id.ref = tsock->peer_name.ref;
                addr->addr.id.node = tsock->peer_name.node;
        } else {
-               tipc_ownidentity(tsock->p->ref, &addr->addr.id);
+               addr->addr.id.ref = tsock->p->ref;
+               addr->addr.id.node = tipc_own_addr;
        }
 
        *uaddr_len = sizeof(*addr);
@@ -505,6 +493,8 @@ static int dest_name_check(struct sockaddr_tipc *dest, struct msghdr *m)
        if (likely(dest->addr.name.name.type != TIPC_CFG_SRV))
                return -EACCES;
 
+       if (!m->msg_iovlen || (m->msg_iov[0].iov_len < sizeof(hdr)))
+               return -EMSGSIZE;
        if (copy_from_user(&hdr, m->msg_iov[0].iov_base, sizeof(hdr)))
                return -EFAULT;
        if ((ntohs(hdr.tcm_type) & 0xC000) && (!capable(CAP_NET_ADMIN)))
@@ -542,6 +532,9 @@ static int send_msg(struct kiocb *iocb, struct socket *sock,
        if (unlikely((m->msg_namelen < sizeof(*dest)) ||
                     (dest->family != AF_TIPC)))
                return -EINVAL;
+       if ((total_len > TIPC_MAX_USER_MSG_SIZE) ||
+           (m->msg_iovlen > (unsigned)INT_MAX))
+               return -EMSGSIZE;
 
        if (iocb)
                lock_sock(sk);
@@ -573,37 +566,38 @@ static int send_msg(struct kiocb *iocb, struct socket *sock,
 
        do {
                if (dest->addrtype == TIPC_ADDR_NAME) {
-                       if ((res = dest_name_check(dest, m)))
+                       res = dest_name_check(dest, m);
+                       if (res)
                                break;
                        res = tipc_send2name(tport->ref,
                                             &dest->addr.name.name,
                                             dest->addr.name.domain,
                                             m->msg_iovlen,
-                                            m->msg_iov);
-               }
-               else if (dest->addrtype == TIPC_ADDR_ID) {
+                                            m->msg_iov,
+                                            total_len);
+               } else if (dest->addrtype == TIPC_ADDR_ID) {
                        res = tipc_send2port(tport->ref,
                                             &dest->addr.id,
                                             m->msg_iovlen,
-                                            m->msg_iov);
-               }
-               else if (dest->addrtype == TIPC_ADDR_MCAST) {
+                                            m->msg_iov,
+                                            total_len);
+               } else if (dest->addrtype == TIPC_ADDR_MCAST) {
                        if (needs_conn) {
                                res = -EOPNOTSUPP;
                                break;
                        }
-                       if ((res = dest_name_check(dest, m)))
+                       res = dest_name_check(dest, m);
+                       if (res)
                                break;
                        res = tipc_multicast(tport->ref,
                                             &dest->addr.nameseq,
-                                            0,
                                             m->msg_iovlen,
-                                            m->msg_iov);
+                                            m->msg_iov,
+                                            total_len);
                }
                if (likely(res != -ELINKCONG)) {
-                       if (needs_conn && (res >= 0)) {
+                       if (needs_conn && (res >= 0))
                                sock->state = SS_CONNECTING;
-                       }
                        break;
                }
                if (m->msg_flags & MSG_DONTWAIT) {
@@ -649,6 +643,10 @@ static int send_packet(struct kiocb *iocb, struct socket *sock,
        if (unlikely(dest))
                return send_msg(iocb, sock, m, total_len);
 
+       if ((total_len > TIPC_MAX_USER_MSG_SIZE) ||
+           (m->msg_iovlen > (unsigned)INT_MAX))
+               return -EMSGSIZE;
+
        if (iocb)
                lock_sock(sk);
 
@@ -661,10 +659,10 @@ static int send_packet(struct kiocb *iocb, struct socket *sock,
                        break;
                }
 
-               res = tipc_send(tport->ref, m->msg_iovlen, m->msg_iov);
-               if (likely(res != -ELINKCONG)) {
+               res = tipc_send(tport->ref, m->msg_iovlen, m->msg_iov,
+                               total_len);
+               if (likely(res != -ELINKCONG))
                        break;
-               }
                if (m->msg_flags & MSG_DONTWAIT) {
                        res = -EWOULDBLOCK;
                        break;
@@ -733,6 +731,12 @@ static int send_stream(struct kiocb *iocb, struct socket *sock,
                goto exit;
        }
 
+       if ((total_len > (unsigned)INT_MAX) ||
+           (m->msg_iovlen > (unsigned)INT_MAX)) {
+               res = -EMSGSIZE;
+               goto exit;
+       }
+
        /*
         * Send each iovec entry using one or more messages
         *
@@ -763,7 +767,8 @@ static int send_stream(struct kiocb *iocb, struct socket *sock,
                                bytes_to_send = curr_left;
                        my_iov.iov_base = curr_start;
                        my_iov.iov_len = bytes_to_send;
-                       if ((res = send_packet(NULL, sock, &my_msg, 0)) < 0) {
+                       res = send_packet(NULL, sock, &my_msg, bytes_to_send);
+                       if (res < 0) {
                                if (bytes_sent)
                                        res = bytes_sent;
                                goto exit;
@@ -823,8 +828,8 @@ static void set_orig_addr(struct msghdr *m, struct tipc_msg *msg)
                addr->addrtype = TIPC_ADDR_ID;
                addr->addr.id.ref = msg_origport(msg);
                addr->addr.id.node = msg_orignode(msg);
-               addr->addr.name.domain = 0;     /* could leave uninitialized */
-               addr->scope = 0;                /* could leave uninitialized */
+               addr->addr.name.domain = 0;     /* could leave uninitialized */
+               addr->scope = 0;                /* could leave uninitialized */
                m->msg_namelen = sizeof(struct sockaddr_tipc);
        }
 }
@@ -858,12 +863,15 @@ static int anc_data_recv(struct msghdr *m, struct tipc_msg *msg,
        if (unlikely(err)) {
                anc_data[0] = err;
                anc_data[1] = msg_data_sz(msg);
-               if ((res = put_cmsg(m, SOL_TIPC, TIPC_ERRINFO, 8, anc_data)))
-                       return res;
-               if (anc_data[1] &&
-                   (res = put_cmsg(m, SOL_TIPC, TIPC_RETDATA, anc_data[1],
-                                   msg_data(msg))))
+               res = put_cmsg(m, SOL_TIPC, TIPC_ERRINFO, 8, anc_data);
+               if (res)
                        return res;
+               if (anc_data[1]) {
+                       res = put_cmsg(m, SOL_TIPC, TIPC_RETDATA, anc_data[1],
+                                      msg_data(msg));
+                       if (res)
+                               return res;
+               }
        }
 
        /* Optionally capture message destination object */
@@ -891,9 +899,11 @@ static int anc_data_recv(struct msghdr *m, struct tipc_msg *msg,
        default:
                has_name = 0;
        }
-       if (has_name &&
-           (res = put_cmsg(m, SOL_TIPC, TIPC_DESTNAME, 12, anc_data)))
-               return res;
+       if (has_name) {
+               res = put_cmsg(m, SOL_TIPC, TIPC_DESTNAME, 12, anc_data);
+               if (res)
+                       return res;
+       }
 
        return 0;
 }
@@ -918,15 +928,13 @@ static int recv_msg(struct kiocb *iocb, struct socket *sock,
        struct tipc_port *tport = tipc_sk_port(sk);
        struct sk_buff *buf;
        struct tipc_msg *msg;
+       long timeout;
        unsigned int sz;
        u32 err;
        int res;
 
        /* Catch invalid receive requests */
 
-       if (m->msg_iovlen != 1)
-               return -EOPNOTSUPP;   /* Don't do multiple iovec entries yet */
-
        if (unlikely(!buf_len))
                return -EINVAL;
 
@@ -937,6 +945,7 @@ static int recv_msg(struct kiocb *iocb, struct socket *sock,
                goto exit;
        }
 
+       timeout = sock_rcvtimeo(sk, flags & MSG_DONTWAIT);
 restart:
 
        /* Look for a message in receive queue; wait if necessary */
@@ -946,17 +955,15 @@ restart:
                        res = -ENOTCONN;
                        goto exit;
                }
-               if (flags & MSG_DONTWAIT) {
-                       res = -EWOULDBLOCK;
+               if (timeout <= 0L) {
+                       res = timeout ? timeout : -EWOULDBLOCK;
                        goto exit;
                }
                release_sock(sk);
-               res = wait_event_interruptible(*sk_sleep(sk),
-                       (!skb_queue_empty(&sk->sk_receive_queue) ||
-                        (sock->state == SS_DISCONNECTING)));
+               timeout = wait_event_interruptible_timeout(*sk_sleep(sk),
+                                                          tipc_rx_ready(sock),
+                                                          timeout);
                lock_sock(sk);
-               if (res)
-                       goto exit;
        }
 
        /* Look at first message in receive queue */
@@ -998,11 +1005,10 @@ restart:
                        sz = buf_len;
                        m->msg_flags |= MSG_TRUNC;
                }
-               if (unlikely(copy_to_user(m->msg_iov->iov_base, msg_data(msg),
-                                         sz))) {
-                       res = -EFAULT;
+               res = skb_copy_datagram_iovec(buf, msg_hdr_sz(msg),
+                                             m->msg_iov, sz);
+               if (res)
                        goto exit;
-               }
                res = sz;
        } else {
                if ((sock->state == SS_READY) ||
@@ -1045,19 +1051,15 @@ static int recv_stream(struct kiocb *iocb, struct socket *sock,
        struct tipc_port *tport = tipc_sk_port(sk);
        struct sk_buff *buf;
        struct tipc_msg *msg;
+       long timeout;
        unsigned int sz;
        int sz_to_copy, target, needed;
        int sz_copied = 0;
-       char __user *crs = m->msg_iov->iov_base;
-       unsigned char *buf_crs;
        u32 err;
        int res = 0;
 
        /* Catch invalid receive attempts */
 
-       if (m->msg_iovlen != 1)
-               return -EOPNOTSUPP;   /* Don't do multiple iovec entries yet */
-
        if (unlikely(!buf_len))
                return -EINVAL;
 
@@ -1070,7 +1072,7 @@ static int recv_stream(struct kiocb *iocb, struct socket *sock,
        }
 
        target = sock_rcvlowat(sk, flags & MSG_WAITALL, buf_len);
-
+       timeout = sock_rcvtimeo(sk, flags & MSG_DONTWAIT);
 restart:
 
        /* Look for a message in receive queue; wait if necessary */
@@ -1080,17 +1082,15 @@ restart:
                        res = -ENOTCONN;
                        goto exit;
                }
-               if (flags & MSG_DONTWAIT) {
-                       res = -EWOULDBLOCK;
+               if (timeout <= 0L) {
+                       res = timeout ? timeout : -EWOULDBLOCK;
                        goto exit;
                }
                release_sock(sk);
-               res = wait_event_interruptible(*sk_sleep(sk),
-                       (!skb_queue_empty(&sk->sk_receive_queue) ||
-                        (sock->state == SS_DISCONNECTING)));
+               timeout = wait_event_interruptible_timeout(*sk_sleep(sk),
+                                                          tipc_rx_ready(sock),
+                                                          timeout);
                lock_sock(sk);
-               if (res)
-                       goto exit;
        }
 
        /* Look at first message in receive queue */
@@ -1119,24 +1119,25 @@ restart:
        /* Capture message data (if valid) & compute return value (always) */
 
        if (!err) {
-               buf_crs = (unsigned char *)(TIPC_SKB_CB(buf)->handle);
-               sz = (unsigned char *)msg + msg_size(msg) - buf_crs;
+               u32 offset = (u32)(unsigned long)(TIPC_SKB_CB(buf)->handle);
 
+               sz -= offset;
                needed = (buf_len - sz_copied);
                sz_to_copy = (sz <= needed) ? sz : needed;
-               if (unlikely(copy_to_user(crs, buf_crs, sz_to_copy))) {
-                       res = -EFAULT;
+
+               res = skb_copy_datagram_iovec(buf, msg_hdr_sz(msg) + offset,
+                                             m->msg_iov, sz_to_copy);
+               if (res)
                        goto exit;
-               }
+
                sz_copied += sz_to_copy;
 
                if (sz_to_copy < sz) {
                        if (!(flags & MSG_PEEK))
-                               TIPC_SKB_CB(buf)->handle = buf_crs + sz_to_copy;
+                               TIPC_SKB_CB(buf)->handle =
+                               (void *)(unsigned long)(offset + sz_to_copy);
                        goto exit;
                }
-
-               crs += sz_to_copy;
        } else {
                if (sz_copied != 0)
                        goto exit; /* can't add error msg to valid data */
@@ -1195,7 +1196,7 @@ static int rx_queue_full(struct tipc_msg *msg, u32 queue_size, u32 base)
        if (msg_connected(msg))
                threshold *= 4;
 
-       return (queue_size >= threshold);
+       return queue_size >= threshold;
 }
 
 /**
@@ -1226,42 +1227,25 @@ static u32 filter_rcv(struct sock *sk, struct sk_buff *buf)
         */
 
        if (sock->state == SS_READY) {
-               if (msg_connected(msg)) {
-                       msg_dbg(msg, "dispatch filter 1\n");
+               if (msg_connected(msg))
                        return TIPC_ERR_NO_PORT;
-               }
        } else {
-               if (msg_mcast(msg)) {
-                       msg_dbg(msg, "dispatch filter 2\n");
+               if (msg_mcast(msg))
                        return TIPC_ERR_NO_PORT;
-               }
                if (sock->state == SS_CONNECTED) {
-                       if (!msg_connected(msg)) {
-                               msg_dbg(msg, "dispatch filter 3\n");
+                       if (!msg_connected(msg))
                                return TIPC_ERR_NO_PORT;
-                       }
-               }
-               else if (sock->state == SS_CONNECTING) {
-                       if (!msg_connected(msg) && (msg_errcode(msg) == 0)) {
-                               msg_dbg(msg, "dispatch filter 4\n");
+               } else if (sock->state == SS_CONNECTING) {
+                       if (!msg_connected(msg) && (msg_errcode(msg) == 0))
                                return TIPC_ERR_NO_PORT;
-                       }
-               }
-               else if (sock->state == SS_LISTENING) {
-                       if (msg_connected(msg) || msg_errcode(msg)) {
-                               msg_dbg(msg, "dispatch filter 5\n");
+               } else if (sock->state == SS_LISTENING) {
+                       if (msg_connected(msg) || msg_errcode(msg))
                                return TIPC_ERR_NO_PORT;
-                       }
-               }
-               else if (sock->state == SS_DISCONNECTING) {
-                       msg_dbg(msg, "dispatch filter 6\n");
+               } else if (sock->state == SS_DISCONNECTING) {
                        return TIPC_ERR_NO_PORT;
-               }
-               else /* (sock->state == SS_UNCONNECTED) */ {
-                       if (msg_connected(msg) || msg_errcode(msg)) {
-                               msg_dbg(msg, "dispatch filter 7\n");
+               } else /* (sock->state == SS_UNCONNECTED) */ {
+                       if (msg_connected(msg) || msg_errcode(msg))
                                return TIPC_ERR_NO_PORT;
-                       }
                }
        }
 
@@ -1280,8 +1264,7 @@ static u32 filter_rcv(struct sock *sk, struct sk_buff *buf)
 
        /* Enqueue message (finally!) */
 
-       msg_dbg(msg, "<DISP<: ");
-       TIPC_SKB_CB(buf)->handle = msg_data(msg);
+       TIPC_SKB_CB(buf)->handle = 0;
        atomic_inc(&tipc_queue_size);
        __skb_queue_tail(&sk->sk_receive_queue, buf);
 
@@ -1441,9 +1424,8 @@ static int connect(struct socket *sock, struct sockaddr *dest, int destlen,
        m.msg_name = dest;
        m.msg_namelen = destlen;
        res = send_msg(NULL, sock, &m, 0);
-       if (res < 0) {
+       if (res < 0)
                goto exit;
-       }
 
        /* Wait until an 'ACK' or 'RST' arrives, or a timeout occurs */
 
@@ -1465,11 +1447,10 @@ static int connect(struct socket *sock, struct sockaddr *dest, int destlen,
                                        advance_rx_queue(sk);
                        }
                } else {
-                       if (sock->state == SS_CONNECTED) {
+                       if (sock->state == SS_CONNECTED)
                                res = -EISCONN;
-                       } else {
+                       else
                                res = -ECONNREFUSED;
-                       }
                }
        } else {
                if (res == 0)
@@ -1588,7 +1569,6 @@ static int accept(struct socket *sock, struct socket *new_sock, int flags)
                 * Respond to 'SYN+' by queuing it on new socket.
                 */
 
-               msg_dbg(msg,"<ACC<: ");
                if (!msg_data_sz(msg)) {
                        struct msghdr m = {NULL,};
 
@@ -1636,7 +1616,7 @@ restart:
                buf = __skb_dequeue(&sk->sk_receive_queue);
                if (buf) {
                        atomic_dec(&tipc_queue_size);
-                       if (TIPC_SKB_CB(buf)->handle != msg_data(buf_msg(buf))) {
+                       if (TIPC_SKB_CB(buf)->handle != 0) {
                                buf_discard(buf);
                                goto restart;
                        }
@@ -1696,7 +1676,8 @@ static int setsockopt(struct socket *sock,
                return -ENOPROTOOPT;
        if (ol < sizeof(value))
                return -EINVAL;
-       if ((res = get_user(value, (u32 __user *)ov)))
+       res = get_user(value, (u32 __user *)ov);
+       if (res)
                return res;
 
        lock_sock(sk);
@@ -1754,7 +1735,8 @@ static int getsockopt(struct socket *sock,
                return put_user(0, ol);
        if (lvl != SOL_TIPC)
                return -ENOPROTOOPT;
-       if ((res = get_user(len, ol)))
+       res = get_user(len, ol);
+       if (res)
                return res;
 
        lock_sock(sk);
@@ -1773,10 +1755,10 @@ static int getsockopt(struct socket *sock,
                value = jiffies_to_msecs(tipc_sk(sk)->conn_timeout);
                /* no need to set "res", since already 0 at this point */
                break;
-        case TIPC_NODE_RECVQ_DEPTH:
+       case TIPC_NODE_RECVQ_DEPTH:
                value = (u32)atomic_read(&tipc_queue_size);
                break;
-        case TIPC_SOCK_RECVQ_DEPTH:
+       case TIPC_SOCK_RECVQ_DEPTH:
                value = skb_queue_len(&sk->sk_receive_queue);
                break;
        default:
@@ -1785,20 +1767,16 @@ static int getsockopt(struct socket *sock,
 
        release_sock(sk);
 
-       if (res) {
-               /* "get" failed */
-       }
-       else if (len < sizeof(value)) {
-               res = -EINVAL;
-       }
-       else if (copy_to_user(ov, &value, sizeof(value))) {
-               res = -EFAULT;
-       }
-       else {
-               res = put_user(sizeof(value), ol);
-       }
+       if (res)
+               return res;     /* "get" failed */
 
-       return res;
+       if (len < sizeof(value))
+               return -EINVAL;
+
+       if (copy_to_user(ov, &value, sizeof(value)))
+               return -EFAULT;
+
+       return put_user(sizeof(value), ol);
 }
 
 /**
@@ -1806,7 +1784,7 @@ static int getsockopt(struct socket *sock,
  */
 
 static const struct proto_ops msg_ops = {
-       .owner          = THIS_MODULE,
+       .owner          = THIS_MODULE,
        .family         = AF_TIPC,
        .release        = release,
        .bind           = bind,
@@ -1827,7 +1805,7 @@ static const struct proto_ops msg_ops = {
 };
 
 static const struct proto_ops packet_ops = {
-       .owner          = THIS_MODULE,
+       .owner          = THIS_MODULE,
        .family         = AF_TIPC,
        .release        = release,
        .bind           = bind,
@@ -1848,7 +1826,7 @@ static const struct proto_ops packet_ops = {
 };
 
 static const struct proto_ops stream_ops = {
-       .owner          = THIS_MODULE,
+       .owner          = THIS_MODULE,
        .family         = AF_TIPC,
        .release        = release,
        .bind           = bind,
@@ -1869,7 +1847,7 @@ static const struct proto_ops stream_ops = {
 };
 
 static const struct net_proto_family tipc_family_ops = {
-       .owner          = THIS_MODULE,
+       .owner          = THIS_MODULE,
        .family         = AF_TIPC,
        .create         = tipc_create
 };