2006-01-02 21:04:38 +03:00
/*
* net / tipc / socket . c : TIPC socket API
*
2006-01-11 21:14:19 +03:00
* Copyright ( c ) 2001 - 2006 , Ericsson AB
2006-01-02 21:04:38 +03:00
* Copyright ( c ) 2004 - 2005 , Wind River Systems
* All rights reserved .
*
2006-01-11 15:30:43 +03:00
* Redistribution and use in source and binary forms , with or without
2006-01-02 21:04:38 +03:00
* modification , are permitted provided that the following conditions are met :
*
2006-01-11 15:30:43 +03:00
* 1. Redistributions of source code must retain the above copyright
* notice , this list of conditions and the following disclaimer .
* 2. Redistributions in binary form must reproduce the above copyright
* notice , this list of conditions and the following disclaimer in the
* documentation and / or other materials provided with the distribution .
* 3. Neither the names of the copyright holders nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission .
2006-01-02 21:04:38 +03:00
*
2006-01-11 15:30:43 +03:00
* Alternatively , this software may be distributed under the terms of the
* GNU General Public License ( " GPL " ) version 2 as published by the Free
* Software Foundation .
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS " AS IS "
* AND ANY EXPRESS OR IMPLIED WARRANTIES , INCLUDING , BUT NOT LIMITED TO , THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED . IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT , INDIRECT , INCIDENTAL , SPECIAL , EXEMPLARY , OR
* CONSEQUENTIAL DAMAGES ( INCLUDING , BUT NOT LIMITED TO , PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES ; LOSS OF USE , DATA , OR PROFITS ; OR BUSINESS
* INTERRUPTION ) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY , WHETHER IN
* CONTRACT , STRICT LIABILITY , OR TORT ( INCLUDING NEGLIGENCE OR OTHERWISE )
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE , EVEN IF ADVISED OF THE
2006-01-02 21:04:38 +03:00
* 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/slab.h>
# include <linux/poll.h>
# include <linux/fcntl.h>
# include <asm/semaphore.h>
# include <asm/string.h>
# include <asm/atomic.h>
# include <net/sock.h>
# include <linux/tipc.h>
2006-01-11 14:28:47 +03:00
# include <linux/tipc_config.h>
2006-01-02 21:04:38 +03:00
# include <net/tipc/tipc_msg.h>
# include <net/tipc/tipc_port.h>
# include "core.h"
# define SS_LISTENING -1 /* socket is listening */
# define SS_READY -2 /* socket is connectionless */
# define OVERLOAD_LIMIT_BASE 5000
struct tipc_sock {
struct sock sk ;
struct tipc_port * p ;
struct semaphore sem ;
} ;
# define tipc_sk(sk) ((struct tipc_sock*)sk)
static u32 dispatch ( struct tipc_port * tport , struct sk_buff * buf ) ;
static void wakeupdispatch ( struct tipc_port * tport ) ;
static struct proto_ops packet_ops ;
static struct proto_ops stream_ops ;
static struct proto_ops msg_ops ;
static struct proto tipc_proto ;
static int sockets_enabled = 0 ;
static atomic_t tipc_queue_size = ATOMIC_INIT ( 0 ) ;
/*
* sock_lock ( ) : Lock a port / socket pair . lock_sock ( ) can
* not be used here , since the same lock must protect ports
* with non - socket interfaces .
* See net . c for description of locking policy .
*/
2006-03-21 09:37:04 +03:00
static void sock_lock ( struct tipc_sock * tsock )
2006-01-02 21:04:38 +03:00
{
spin_lock_bh ( tsock - > p - > lock ) ;
}
/*
* sock_unlock ( ) : Unlock a port / socket pair
*/
2006-03-21 09:37:04 +03:00
static void sock_unlock ( struct tipc_sock * tsock )
2006-01-02 21:04:38 +03:00
{
spin_unlock_bh ( tsock - > p - > lock ) ;
}
/**
* pollmask - determine the current set of poll ( ) events for a socket
* @ sock : socket structure
*
* TIPC sets the returned events as follows :
* a ) POLLRDNORM and POLLIN are set if the socket ' s receive queue is non - empty
* or if a connection - oriented socket is does not have an active connection
* ( i . e . a read operation will not block ) .
* b ) POLLOUT is set except when a socket ' s connection has been terminated
* ( i . e . a write operation will not block ) .
* c ) POLLHUP is set when a socket ' s connection has been terminated .
*
* IMPORTANT : The fact that a read or write operation will not block does NOT
* imply that the operation will succeed !
*
* Returns pollmask value
*/
2006-03-21 09:37:04 +03:00
static u32 pollmask ( struct socket * sock )
2006-01-02 21:04:38 +03:00
{
u32 mask ;
if ( ( skb_queue_len ( & sock - > sk - > sk_receive_queue ) ! = 0 ) | |
( sock - > state = = SS_UNCONNECTED ) | |
( sock - > state = = SS_DISCONNECTING ) )
mask = ( POLLRDNORM | POLLIN ) ;
else
mask = 0 ;
if ( sock - > state = = SS_DISCONNECTING )
mask | = POLLHUP ;
else
mask | = POLLOUT ;
return mask ;
}
/**
* advance_queue - discard first buffer in queue
* @ tsock : TIPC socket
*/
2006-03-21 09:37:04 +03:00
static void advance_queue ( struct tipc_sock * tsock )
2006-01-02 21:04:38 +03:00
{
sock_lock ( tsock ) ;
buf_discard ( skb_dequeue ( & tsock - > sk . sk_receive_queue ) ) ;
sock_unlock ( tsock ) ;
atomic_dec ( & tipc_queue_size ) ;
}
/**
* tipc_create - create a TIPC socket
* @ sock : pre - allocated socket structure
* @ protocol : protocol indicator ( must be 0 )
*
* This routine creates and attaches a ' struct sock ' to the ' struct socket ' ,
* then create and attaches a TIPC port to the ' struct sock ' part .
*
* Returns 0 on success , errno otherwise
*/
static int tipc_create ( struct socket * sock , int protocol )
{
struct tipc_sock * tsock ;
struct tipc_port * port ;
struct sock * sk ;
u32 ref ;
if ( ( sock - > type ! = SOCK_STREAM ) & &
( sock - > type ! = SOCK_SEQPACKET ) & &
( sock - > type ! = SOCK_DGRAM ) & &
( sock - > type ! = SOCK_RDM ) )
return - EPROTOTYPE ;
if ( unlikely ( protocol ! = 0 ) )
return - EPROTONOSUPPORT ;
2006-03-21 09:36:47 +03:00
ref = tipc_createport_raw ( NULL , & dispatch , & wakeupdispatch , TIPC_LOW_IMPORTANCE ) ;
2006-01-02 21:04:38 +03:00
if ( unlikely ( ! ref ) )
return - ENOMEM ;
sock - > state = SS_UNCONNECTED ;
switch ( sock - > type ) {
case SOCK_STREAM :
sock - > ops = & stream_ops ;
break ;
case SOCK_SEQPACKET :
sock - > ops = & packet_ops ;
break ;
case SOCK_DGRAM :
tipc_set_portunreliable ( ref , 1 ) ;
/* fall through */
case SOCK_RDM :
tipc_set_portunreturnable ( ref , 1 ) ;
sock - > ops = & msg_ops ;
sock - > state = SS_READY ;
break ;
}
sk = sk_alloc ( AF_TIPC , GFP_KERNEL , & tipc_proto , 1 ) ;
if ( ! sk ) {
tipc_deleteport ( ref ) ;
return - ENOMEM ;
}
sock_init_data ( sock , sk ) ;
init_waitqueue_head ( sk - > sk_sleep ) ;
sk - > sk_rcvtimeo = 8 * HZ ; /* default connect timeout = 8s */
tsock = tipc_sk ( sk ) ;
port = tipc_get_port ( ref ) ;
tsock - > p = port ;
port - > usr_handle = tsock ;
init_MUTEX ( & tsock - > sem ) ;
dbg ( " sock_create: %x \n " , tsock ) ;
atomic_inc ( & tipc_user_count ) ;
return 0 ;
}
/**
* release - destroy a TIPC socket
* @ sock : socket to destroy
*
* This routine cleans up any messages that are still queued on the socket .
* For DGRAM and RDM socket types , all queued messages are rejected .
* For SEQPACKET and STREAM socket types , the first message is rejected
* and any others are discarded . ( If the first message on a STREAM socket
* is partially - read , it is discarded and the next one is rejected instead . )
*
* NOTE : Rejected messages are not necessarily returned to the sender ! They
* are returned or discarded according to the " destination droppable " setting
* specified for the message by the sender .
*
* Returns 0 on success , errno otherwise
*/
static int release ( struct socket * sock )
{
struct tipc_sock * tsock = tipc_sk ( sock - > sk ) ;
struct sock * sk = sock - > sk ;
int res = TIPC_OK ;
struct sk_buff * buf ;
dbg ( " sock_delete: %x \n " , tsock ) ;
if ( ! tsock )
return 0 ;
down_interruptible ( & tsock - > sem ) ;
if ( ! sock - > sk ) {
up ( & tsock - > sem ) ;
return 0 ;
}
/* Reject unreceived messages, unless no longer connected */
while ( sock - > state ! = SS_DISCONNECTING ) {
sock_lock ( tsock ) ;
buf = skb_dequeue ( & sk - > sk_receive_queue ) ;
if ( ! buf )
2006-03-21 09:36:47 +03:00
tsock - > p - > usr_handle = NULL ;
2006-01-02 21:04:38 +03:00
sock_unlock ( tsock ) ;
if ( ! buf )
break ;
if ( TIPC_SKB_CB ( buf ) - > handle ! = msg_data ( buf_msg ( buf ) ) )
buf_discard ( buf ) ;
else
tipc_reject_msg ( buf , TIPC_ERR_NO_PORT ) ;
atomic_dec ( & tipc_queue_size ) ;
}
/* Delete TIPC port */
res = tipc_deleteport ( tsock - > p - > ref ) ;
sock - > sk = NULL ;
/* Discard any remaining messages */
while ( ( buf = skb_dequeue ( & sk - > sk_receive_queue ) ) ) {
buf_discard ( buf ) ;
atomic_dec ( & tipc_queue_size ) ;
}
up ( & tsock - > sem ) ;
sock_put ( sk ) ;
atomic_dec ( & tipc_user_count ) ;
return res ;
}
/**
* bind - associate or disassocate TIPC name ( s ) with a socket
* @ sock : socket structure
* @ uaddr : socket address describing name ( s ) and desired operation
* @ uaddr_len : size of socket address data structure
*
* Name and name sequence binding is indicated using a positive scope value ;
* a negative scope value unbinds the specified name . Specifying no name
* ( i . e . a socket address length of 0 ) unbinds all names from the socket .
*
* Returns 0 on success , errno otherwise
*/
static int bind ( struct socket * sock , struct sockaddr * uaddr , int uaddr_len )
{
struct tipc_sock * tsock = tipc_sk ( sock - > sk ) ;
struct sockaddr_tipc * addr = ( struct sockaddr_tipc * ) uaddr ;
int res ;
if ( down_interruptible ( & tsock - > sem ) )
return - ERESTARTSYS ;
if ( unlikely ( ! uaddr_len ) ) {
2006-03-21 09:36:47 +03:00
res = tipc_withdraw ( tsock - > p - > ref , 0 , NULL ) ;
2006-01-02 21:04:38 +03:00
goto exit ;
}
if ( uaddr_len < sizeof ( struct sockaddr_tipc ) ) {
res = - EINVAL ;
goto exit ;
}
if ( addr - > family ! = AF_TIPC ) {
res = - EAFNOSUPPORT ;
goto exit ;
}
if ( addr - > addrtype = = TIPC_ADDR_NAME )
addr - > addr . nameseq . upper = addr - > addr . nameseq . lower ;
else if ( addr - > addrtype ! = TIPC_ADDR_NAMESEQ ) {
res = - EAFNOSUPPORT ;
goto exit ;
}
if ( addr - > scope > 0 )
res = tipc_publish ( tsock - > p - > ref , addr - > scope ,
& addr - > addr . nameseq ) ;
else
res = tipc_withdraw ( tsock - > p - > ref , - addr - > scope ,
& addr - > addr . nameseq ) ;
exit :
up ( & tsock - > sem ) ;
return res ;
}
/**
* get_name - get port ID of socket or peer socket
* @ sock : socket structure
* @ uaddr : area for returned socket address
* @ uaddr_len : area for returned length of socket address
* @ peer : 0 to obtain socket name , 1 to obtain peer socket name
*
* Returns 0 on success , errno otherwise
*/
static int get_name ( struct socket * sock , struct sockaddr * uaddr ,
int * uaddr_len , int peer )
{
struct tipc_sock * tsock = tipc_sk ( sock - > sk ) ;
struct sockaddr_tipc * addr = ( struct sockaddr_tipc * ) uaddr ;
u32 res ;
if ( down_interruptible ( & tsock - > sem ) )
return - ERESTARTSYS ;
* uaddr_len = sizeof ( * addr ) ;
addr - > addrtype = TIPC_ADDR_ID ;
addr - > family = AF_TIPC ;
addr - > scope = 0 ;
if ( peer )
res = tipc_peer ( tsock - > p - > ref , & addr - > addr . id ) ;
else
res = tipc_ownidentity ( tsock - > p - > ref , & addr - > addr . id ) ;
addr - > addr . name . domain = 0 ;
up ( & tsock - > sem ) ;
return res ;
}
/**
* poll - read and possibly block on pollmask
* @ file : file structure associated with the socket
* @ sock : socket for which to calculate the poll bits
* @ wait : ? ? ?
*
* Returns the pollmask
*/
static unsigned int poll ( struct file * file , struct socket * sock ,
poll_table * wait )
{
poll_wait ( file , sock - > sk - > sk_sleep , wait ) ;
/* NEED LOCK HERE? */
return pollmask ( sock ) ;
}
/**
* dest_name_check - verify user is permitted to send to specified port name
* @ dest : destination address
* @ m : descriptor for message to be sent
*
* Prevents restricted configuration commands from being issued by
* unauthorized users .
*
* Returns 0 if permission is granted , otherwise errno
*/
2006-03-21 09:37:04 +03:00
static int dest_name_check ( struct sockaddr_tipc * dest , struct msghdr * m )
2006-01-02 21:04:38 +03:00
{
struct tipc_cfg_msg_hdr hdr ;
if ( likely ( dest - > addr . name . name . type > = TIPC_RESERVED_TYPES ) )
return 0 ;
if ( likely ( dest - > addr . name . name . type = = TIPC_TOP_SRV ) )
return 0 ;
if ( likely ( dest - > addr . name . name . type ! = TIPC_CFG_SRV ) )
return - EACCES ;
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 ) ) )
return - EACCES ;
return 0 ;
}
/**
* send_msg - send message in connectionless manner
* @ iocb : ( unused )
* @ sock : socket structure
* @ m : message to send
* @ total_len : ( unused )
*
* Message must have an destination specified explicitly .
* Used for SOCK_RDM and SOCK_DGRAM messages ,
* and for ' SYN ' messages on SOCK_SEQPACKET and SOCK_STREAM connections .
* ( Note : ' SYN + ' is prohibited on SOCK_STREAM . )
*
* Returns the number of bytes sent on success , or errno otherwise
*/
static int send_msg ( struct kiocb * iocb , struct socket * sock ,
struct msghdr * m , size_t total_len )
{
struct tipc_sock * tsock = tipc_sk ( sock - > sk ) ;
struct sockaddr_tipc * dest = ( struct sockaddr_tipc * ) m - > msg_name ;
struct sk_buff * buf ;
int needs_conn ;
int res = - EINVAL ;
if ( unlikely ( ! dest ) )
return - EDESTADDRREQ ;
if ( unlikely ( dest - > family ! = AF_TIPC ) )
return - EINVAL ;
needs_conn = ( sock - > state ! = SS_READY ) ;
if ( unlikely ( needs_conn ) ) {
if ( sock - > state = = SS_LISTENING )
return - EPIPE ;
if ( sock - > state ! = SS_UNCONNECTED )
return - EISCONN ;
if ( ( tsock - > p - > published ) | |
( ( sock - > type = = SOCK_STREAM ) & & ( total_len ! = 0 ) ) )
return - EOPNOTSUPP ;
}
if ( down_interruptible ( & tsock - > sem ) )
return - ERESTARTSYS ;
if ( needs_conn ) {
/* Abort any pending connection attempts (very unlikely) */
while ( ( buf = skb_dequeue ( & sock - > sk - > sk_receive_queue ) ) ) {
tipc_reject_msg ( buf , TIPC_ERR_NO_PORT ) ;
atomic_dec ( & tipc_queue_size ) ;
}
sock - > state = SS_CONNECTING ;
}
do {
if ( dest - > addrtype = = TIPC_ADDR_NAME ) {
if ( ( res = dest_name_check ( dest , m ) ) )
goto exit ;
res = tipc_send2name ( tsock - > p - > ref ,
& dest - > addr . name . name ,
dest - > addr . name . domain ,
m - > msg_iovlen ,
m - > msg_iov ) ;
}
else if ( dest - > addrtype = = TIPC_ADDR_ID ) {
res = tipc_send2port ( tsock - > p - > ref ,
& dest - > addr . id ,
m - > msg_iovlen ,
m - > msg_iov ) ;
}
else if ( dest - > addrtype = = TIPC_ADDR_MCAST ) {
if ( needs_conn ) {
res = - EOPNOTSUPP ;
goto exit ;
}
if ( ( res = dest_name_check ( dest , m ) ) )
goto exit ;
res = tipc_multicast ( tsock - > p - > ref ,
& dest - > addr . nameseq ,
0 ,
m - > msg_iovlen ,
m - > msg_iov ) ;
}
if ( likely ( res ! = - ELINKCONG ) ) {
exit :
up ( & tsock - > sem ) ;
return res ;
}
if ( m - > msg_flags & MSG_DONTWAIT ) {
res = - EWOULDBLOCK ;
goto exit ;
}
if ( wait_event_interruptible ( * sock - > sk - > sk_sleep ,
! tsock - > p - > congested ) ) {
res = - ERESTARTSYS ;
goto exit ;
}
} while ( 1 ) ;
}
/**
* send_packet - send a connection - oriented message
* @ iocb : ( unused )
* @ sock : socket structure
* @ m : message to send
* @ total_len : ( unused )
*
* Used for SOCK_SEQPACKET messages and SOCK_STREAM data .
*
* Returns the number of bytes sent on success , or errno otherwise
*/
static int send_packet ( struct kiocb * iocb , struct socket * sock ,
struct msghdr * m , size_t total_len )
{
struct tipc_sock * tsock = tipc_sk ( sock - > sk ) ;
struct sockaddr_tipc * dest = ( struct sockaddr_tipc * ) m - > msg_name ;
int res ;
/* Handle implied connection establishment */
if ( unlikely ( dest ) )
return send_msg ( iocb , sock , m , total_len ) ;
if ( down_interruptible ( & tsock - > sem ) ) {
return - ERESTARTSYS ;
}
if ( unlikely ( sock - > state ! = SS_CONNECTED ) ) {
if ( sock - > state = = SS_DISCONNECTING )
res = - EPIPE ;
else
res = - ENOTCONN ;
goto exit ;
}
do {
res = tipc_send ( tsock - > p - > ref , m - > msg_iovlen , m - > msg_iov ) ;
if ( likely ( res ! = - ELINKCONG ) ) {
exit :
up ( & tsock - > sem ) ;
return res ;
}
if ( m - > msg_flags & MSG_DONTWAIT ) {
res = - EWOULDBLOCK ;
goto exit ;
}
if ( wait_event_interruptible ( * sock - > sk - > sk_sleep ,
! tsock - > p - > congested ) ) {
res = - ERESTARTSYS ;
goto exit ;
}
} while ( 1 ) ;
}
/**
* send_stream - send stream - oriented data
* @ iocb : ( unused )
* @ sock : socket structure
* @ m : data to send
* @ total_len : total length of data to be sent
*
* Used for SOCK_STREAM data .
*
* Returns the number of bytes sent on success , or errno otherwise
*/
static int send_stream ( struct kiocb * iocb , struct socket * sock ,
struct msghdr * m , size_t total_len )
{
struct msghdr my_msg ;
struct iovec my_iov ;
struct iovec * curr_iov ;
int curr_iovlen ;
char __user * curr_start ;
int curr_left ;
int bytes_to_send ;
int res ;
if ( likely ( total_len < = TIPC_MAX_USER_MSG_SIZE ) )
return send_packet ( iocb , sock , m , total_len ) ;
/* Can only send large data streams if already connected */
if ( unlikely ( sock - > state ! = SS_CONNECTED ) ) {
if ( sock - > state = = SS_DISCONNECTING )
return - EPIPE ;
else
return - ENOTCONN ;
}
/*
* 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 ( ) .
*/
my_msg = * m ;
curr_iov = my_msg . msg_iov ;
curr_iovlen = my_msg . msg_iovlen ;
my_msg . msg_iov = & my_iov ;
my_msg . msg_iovlen = 1 ;
while ( curr_iovlen - - ) {
curr_start = curr_iov - > iov_base ;
curr_left = curr_iov - > iov_len ;
while ( curr_left ) {
bytes_to_send = ( curr_left < TIPC_MAX_USER_MSG_SIZE )
? curr_left : TIPC_MAX_USER_MSG_SIZE ;
my_iov . iov_base = curr_start ;
my_iov . iov_len = bytes_to_send ;
if ( ( res = send_packet ( iocb , sock , & my_msg , 0 ) ) < 0 )
return res ;
curr_left - = bytes_to_send ;
curr_start + = bytes_to_send ;
}
curr_iov + + ;
}
return total_len ;
}
/**
* auto_connect - complete connection setup to a remote port
* @ sock : socket structure
* @ tsock : TIPC - specific socket structure
* @ msg : peer ' s response message
*
* Returns 0 on success , errno otherwise
*/
static int auto_connect ( struct socket * sock , struct tipc_sock * tsock ,
struct tipc_msg * msg )
{
struct tipc_portid peer ;
if ( msg_errcode ( msg ) ) {
sock - > state = SS_DISCONNECTING ;
return - ECONNREFUSED ;
}
peer . ref = msg_origport ( msg ) ;
peer . node = msg_orignode ( msg ) ;
tipc_connect2port ( tsock - > p - > ref , & peer ) ;
tipc_set_portimportance ( tsock - > p - > ref , msg_importance ( msg ) ) ;
sock - > state = SS_CONNECTED ;
return 0 ;
}
/**
* set_orig_addr - capture sender ' s address for received message
* @ m : descriptor for message info
* @ msg : received message header
*
* Note : Address is not captured if not requested by receiver .
*/
2006-03-21 09:37:04 +03:00
static void set_orig_addr ( struct msghdr * m , struct tipc_msg * msg )
2006-01-02 21:04:38 +03:00
{
struct sockaddr_tipc * addr = ( struct sockaddr_tipc * ) m - > msg_name ;
if ( addr ) {
addr - > family = AF_TIPC ;
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 */
m - > msg_namelen = sizeof ( struct sockaddr_tipc ) ;
}
}
/**
* anc_data_recv - optionally capture ancillary data for received message
* @ m : descriptor for message info
* @ msg : received message header
* @ tport : TIPC port associated with message
*
* Note : Ancillary data is not captured if not requested by receiver .
*
* Returns 0 if successful , otherwise errno
*/
2006-03-21 09:37:04 +03:00
static int anc_data_recv ( struct msghdr * m , struct tipc_msg * msg ,
2006-01-02 21:04:38 +03:00
struct tipc_port * tport )
{
u32 anc_data [ 3 ] ;
u32 err ;
u32 dest_type ;
int res ;
if ( likely ( m - > msg_controllen = = 0 ) )
return 0 ;
/* Optionally capture errored message object(s) */
err = msg ? msg_errcode ( msg ) : 0 ;
if ( unlikely ( err ) ) {
anc_data [ 0 ] = err ;
anc_data [ 1 ] = msg_data_sz ( msg ) ;
if ( ( res = put_cmsg ( m , SOL_SOCKET , TIPC_ERRINFO , 8 , anc_data ) ) )
return res ;
if ( anc_data [ 1 ] & &
( res = put_cmsg ( m , SOL_SOCKET , TIPC_RETDATA , anc_data [ 1 ] ,
msg_data ( msg ) ) ) )
return res ;
}
/* Optionally capture message destination object */
dest_type = msg ? msg_type ( msg ) : TIPC_DIRECT_MSG ;
switch ( dest_type ) {
case TIPC_NAMED_MSG :
anc_data [ 0 ] = msg_nametype ( msg ) ;
anc_data [ 1 ] = msg_namelower ( msg ) ;
anc_data [ 2 ] = msg_namelower ( msg ) ;
break ;
case TIPC_MCAST_MSG :
anc_data [ 0 ] = msg_nametype ( msg ) ;
anc_data [ 1 ] = msg_namelower ( msg ) ;
anc_data [ 2 ] = msg_nameupper ( msg ) ;
break ;
case TIPC_CONN_MSG :
anc_data [ 0 ] = tport - > conn_type ;
anc_data [ 1 ] = tport - > conn_instance ;
anc_data [ 2 ] = tport - > conn_instance ;
break ;
default :
anc_data [ 0 ] = 0 ;
}
if ( anc_data [ 0 ] & &
( res = put_cmsg ( m , SOL_SOCKET , TIPC_DESTNAME , 12 , anc_data ) ) )
return res ;
return 0 ;
}
/**
* recv_msg - receive packet - oriented message
* @ iocb : ( unused )
* @ m : descriptor for message info
* @ buf_len : total size of user buffer area
* @ flags : receive flags
*
* Used for SOCK_DGRAM , SOCK_RDM , and SOCK_SEQPACKET messages .
* If the complete message doesn ' t fit in user area , truncate it .
*
* Returns size of returned message data , errno otherwise
*/
static int recv_msg ( struct kiocb * iocb , struct socket * sock ,
struct msghdr * m , size_t buf_len , int flags )
{
struct tipc_sock * tsock = tipc_sk ( sock - > sk ) ;
struct sk_buff * buf ;
struct tipc_msg * msg ;
unsigned int q_len ;
unsigned int sz ;
u32 err ;
int res ;
/* Currently doesn't support receiving into multiple iovec entries */
if ( m - > msg_iovlen ! = 1 )
return - EOPNOTSUPP ;
/* Catch invalid receive attempts */
if ( unlikely ( ! buf_len ) )
return - EINVAL ;
if ( sock - > type = = SOCK_SEQPACKET ) {
if ( unlikely ( sock - > state = = SS_UNCONNECTED ) )
return - ENOTCONN ;
if ( unlikely ( ( sock - > state = = SS_DISCONNECTING ) & &
( skb_queue_len ( & sock - > sk - > sk_receive_queue ) = = 0 ) ) )
return - ENOTCONN ;
}
/* Look for a message in receive queue; wait if necessary */
if ( unlikely ( down_interruptible ( & tsock - > sem ) ) )
return - ERESTARTSYS ;
restart :
if ( unlikely ( ( skb_queue_len ( & sock - > sk - > sk_receive_queue ) = = 0 ) & &
( flags & MSG_DONTWAIT ) ) ) {
res = - EWOULDBLOCK ;
goto exit ;
}
if ( ( res = wait_event_interruptible (
* sock - > sk - > sk_sleep ,
( ( q_len = skb_queue_len ( & sock - > sk - > sk_receive_queue ) ) | |
( sock - > state = = SS_DISCONNECTING ) ) ) ) ) {
goto exit ;
}
/* Catch attempt to receive on an already terminated connection */
/* [THIS CHECK MAY OVERLAP WITH AN EARLIER CHECK] */
if ( ! q_len ) {
res = - ENOTCONN ;
goto exit ;
}
/* Get access to first message in receive queue */
buf = skb_peek ( & sock - > sk - > sk_receive_queue ) ;
msg = buf_msg ( buf ) ;
sz = msg_data_sz ( msg ) ;
err = msg_errcode ( msg ) ;
/* Complete connection setup for an implied connect */
if ( unlikely ( sock - > state = = SS_CONNECTING ) ) {
if ( ( res = auto_connect ( sock , tsock , msg ) ) )
goto exit ;
}
/* Discard an empty non-errored message & try again */
if ( ( ! sz ) & & ( ! err ) ) {
advance_queue ( tsock ) ;
goto restart ;
}
/* Capture sender's address (optional) */
set_orig_addr ( m , msg ) ;
/* Capture ancillary data (optional) */
if ( ( res = anc_data_recv ( m , msg , tsock - > p ) ) )
goto exit ;
/* Capture message data (if valid) & compute return value (always) */
if ( ! err ) {
if ( unlikely ( buf_len < sz ) ) {
sz = buf_len ;
m - > msg_flags | = MSG_TRUNC ;
}
if ( unlikely ( copy_to_user ( m - > msg_iov - > iov_base , msg_data ( msg ) ,
sz ) ) ) {
res = - EFAULT ;
goto exit ;
}
res = sz ;
} else {
if ( ( sock - > state = = SS_READY ) | |
( ( err = = TIPC_CONN_SHUTDOWN ) | | m - > msg_control ) )
res = 0 ;
else
res = - ECONNRESET ;
}
/* Consume received message (optional) */
if ( likely ( ! ( flags & MSG_PEEK ) ) ) {
if ( unlikely ( + + tsock - > p - > conn_unacked > = TIPC_FLOW_CONTROL_WIN ) )
tipc_acknowledge ( tsock - > p - > ref , tsock - > p - > conn_unacked ) ;
advance_queue ( tsock ) ;
}
exit :
up ( & tsock - > sem ) ;
return res ;
}
/**
* recv_stream - receive stream - oriented data
* @ iocb : ( unused )
* @ m : descriptor for message info
* @ buf_len : total size of user buffer area
* @ flags : receive flags
*
* Used for SOCK_STREAM messages only . If not enough data is available
* will optionally wait for more ; never truncates data .
*
* Returns size of returned message data , errno otherwise
*/
static int recv_stream ( struct kiocb * iocb , struct socket * sock ,
struct msghdr * m , size_t buf_len , int flags )
{
struct tipc_sock * tsock = tipc_sk ( sock - > sk ) ;
struct sk_buff * buf ;
struct tipc_msg * msg ;
unsigned int q_len ;
unsigned int sz ;
int sz_to_copy ;
int sz_copied = 0 ;
int needed ;
char * crs = m - > msg_iov - > iov_base ;
unsigned char * buf_crs ;
u32 err ;
int res ;
/* Currently doesn't support receiving into multiple iovec entries */
if ( m - > msg_iovlen ! = 1 )
return - EOPNOTSUPP ;
/* Catch invalid receive attempts */
if ( unlikely ( ! buf_len ) )
return - EINVAL ;
if ( unlikely ( sock - > state = = SS_DISCONNECTING ) ) {
if ( skb_queue_len ( & sock - > sk - > sk_receive_queue ) = = 0 )
return - ENOTCONN ;
} else if ( unlikely ( sock - > state ! = SS_CONNECTED ) )
return - ENOTCONN ;
/* Look for a message in receive queue; wait if necessary */
if ( unlikely ( down_interruptible ( & tsock - > sem ) ) )
return - ERESTARTSYS ;
restart :
if ( unlikely ( ( skb_queue_len ( & sock - > sk - > sk_receive_queue ) = = 0 ) & &
( flags & MSG_DONTWAIT ) ) ) {
res = ( sz_copied = = 0 ) ? - EWOULDBLOCK : 0 ;
goto exit ;
}
if ( ( res = wait_event_interruptible (
* sock - > sk - > sk_sleep ,
( ( q_len = skb_queue_len ( & sock - > sk - > sk_receive_queue ) ) | |
( sock - > state = = SS_DISCONNECTING ) ) ) ) ) {
goto exit ;
}
/* Catch attempt to receive on an already terminated connection */
/* [THIS CHECK MAY OVERLAP WITH AN EARLIER CHECK] */
if ( ! q_len ) {
res = - ENOTCONN ;
goto exit ;
}
/* Get access to first message in receive queue */
buf = skb_peek ( & sock - > sk - > sk_receive_queue ) ;
msg = buf_msg ( buf ) ;
sz = msg_data_sz ( msg ) ;
err = msg_errcode ( msg ) ;
/* Discard an empty non-errored message & try again */
if ( ( ! sz ) & & ( ! err ) ) {
advance_queue ( tsock ) ;
goto restart ;
}
/* Optionally capture sender's address & ancillary data of first msg */
if ( sz_copied = = 0 ) {
set_orig_addr ( m , msg ) ;
if ( ( res = anc_data_recv ( m , msg , tsock - > p ) ) )
goto exit ;
}
/* Capture message data (if valid) & compute return value (always) */
if ( ! err ) {
buf_crs = ( unsigned char * ) ( TIPC_SKB_CB ( buf ) - > handle ) ;
sz = buf - > tail - buf_crs ;
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 ;
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 ;
goto exit ;
}
crs + = sz_to_copy ;
} else {
if ( sz_copied ! = 0 )
goto exit ; /* can't add error msg to valid data */
if ( ( err = = TIPC_CONN_SHUTDOWN ) | | m - > msg_control )
res = 0 ;
else
res = - ECONNRESET ;
}
/* Consume received message (optional) */
if ( likely ( ! ( flags & MSG_PEEK ) ) ) {
if ( unlikely ( + + tsock - > p - > conn_unacked > = TIPC_FLOW_CONTROL_WIN ) )
tipc_acknowledge ( tsock - > p - > ref , tsock - > p - > conn_unacked ) ;
advance_queue ( tsock ) ;
}
/* Loop around if more data is required */
if ( ( sz_copied < buf_len ) /* didn't get all requested data */
& & ( flags & MSG_WAITALL ) /* ... and need to wait for more */
& & ( ! ( flags & MSG_PEEK ) ) /* ... and aren't just peeking at data */
& & ( ! err ) /* ... and haven't reached a FIN */
)
goto restart ;
exit :
up ( & tsock - > sem ) ;
return res ? res : sz_copied ;
}
/**
* queue_overloaded - test if queue overload condition exists
* @ queue_size : current size of queue
* @ base : nominal maximum size of queue
* @ msg : message to be added to queue
*
* Returns 1 if queue is currently overloaded , 0 otherwise
*/
static int queue_overloaded ( u32 queue_size , u32 base , struct tipc_msg * msg )
{
u32 threshold ;
u32 imp = msg_importance ( msg ) ;
if ( imp = = TIPC_LOW_IMPORTANCE )
threshold = base ;
else if ( imp = = TIPC_MEDIUM_IMPORTANCE )
threshold = base * 2 ;
else if ( imp = = TIPC_HIGH_IMPORTANCE )
threshold = base * 100 ;
else
return 0 ;
if ( msg_connected ( msg ) )
threshold * = 4 ;
return ( queue_size > threshold ) ;
}
/**
* async_disconnect - wrapper function used to disconnect port
* @ portref : TIPC port reference ( passed as pointer - sized value )
*/
static void async_disconnect ( unsigned long portref )
{
tipc_disconnect ( ( u32 ) portref ) ;
}
/**
* dispatch - handle arriving message
* @ tport : TIPC port that received message
* @ buf : message
*
* Called with port locked . Must not take socket lock to avoid deadlock risk .
*
* Returns TIPC error status code ( TIPC_OK if message is not to be rejected )
*/
static u32 dispatch ( struct tipc_port * tport , struct sk_buff * buf )
{
struct tipc_msg * msg = buf_msg ( buf ) ;
struct tipc_sock * tsock = ( struct tipc_sock * ) tport - > usr_handle ;
struct socket * sock ;
u32 recv_q_len ;
/* Reject message if socket is closing */
if ( ! tsock )
return TIPC_ERR_NO_PORT ;
/* Reject message if it is wrong sort of message for socket */
/*
* WOULD IT BE BETTER TO JUST DISCARD THESE MESSAGES INSTEAD ?
* " NO PORT " ISN ' T REALLY THE RIGHT ERROR CODE , AND THERE MAY
* BE SECURITY IMPLICATIONS INHERENT IN REJECTING INVALID TRAFFIC
*/
sock = tsock - > sk . sk_socket ;
if ( sock - > state = = SS_READY ) {
if ( msg_connected ( msg ) ) {
msg_dbg ( msg , " dispatch filter 1 \n " ) ;
return TIPC_ERR_NO_PORT ;
}
} else {
if ( msg_mcast ( msg ) ) {
msg_dbg ( msg , " dispatch filter 2 \n " ) ;
return TIPC_ERR_NO_PORT ;
}
if ( sock - > state = = SS_CONNECTED ) {
if ( ! msg_connected ( msg ) ) {
msg_dbg ( msg , " dispatch filter 3 \n " ) ;
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 " ) ;
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 " ) ;
return TIPC_ERR_NO_PORT ;
}
}
else if ( sock - > state = = SS_DISCONNECTING ) {
msg_dbg ( msg , " dispatch filter 6 \n " ) ;
return TIPC_ERR_NO_PORT ;
}
else /* (sock->state == SS_UNCONNECTED) */ {
if ( msg_connected ( msg ) | | msg_errcode ( msg ) ) {
msg_dbg ( msg , " dispatch filter 7 \n " ) ;
return TIPC_ERR_NO_PORT ;
}
}
}
/* Reject message if there isn't room to queue it */
if ( unlikely ( ( u32 ) atomic_read ( & tipc_queue_size ) >
OVERLOAD_LIMIT_BASE ) ) {
if ( queue_overloaded ( atomic_read ( & tipc_queue_size ) ,
OVERLOAD_LIMIT_BASE , msg ) )
return TIPC_ERR_OVERLOAD ;
}
recv_q_len = skb_queue_len ( & tsock - > sk . sk_receive_queue ) ;
if ( unlikely ( recv_q_len > ( OVERLOAD_LIMIT_BASE / 2 ) ) ) {
if ( queue_overloaded ( recv_q_len ,
OVERLOAD_LIMIT_BASE / 2 , msg ) )
return TIPC_ERR_OVERLOAD ;
}
/* Initiate connection termination for an incoming 'FIN' */
if ( unlikely ( msg_errcode ( msg ) & & ( sock - > state = = SS_CONNECTED ) ) ) {
sock - > state = SS_DISCONNECTING ;
/* Note: Use signal since port lock is already taken! */
2006-01-18 02:38:21 +03:00
tipc_k_signal ( ( Handler ) async_disconnect , tport - > ref ) ;
2006-01-02 21:04:38 +03:00
}
/* Enqueue message (finally!) */
msg_dbg ( msg , " <DISP<: " ) ;
TIPC_SKB_CB ( buf ) - > handle = msg_data ( msg ) ;
atomic_inc ( & tipc_queue_size ) ;
skb_queue_tail ( & sock - > sk - > sk_receive_queue , buf ) ;
wake_up_interruptible ( sock - > sk - > sk_sleep ) ;
return TIPC_OK ;
}
/**
* wakeupdispatch - wake up port after congestion
* @ tport : port to wakeup
*
* Called with port lock on .
*/
static void wakeupdispatch ( struct tipc_port * tport )
{
struct tipc_sock * tsock = ( struct tipc_sock * ) tport - > usr_handle ;
wake_up_interruptible ( tsock - > sk . sk_sleep ) ;
}
/**
* connect - establish a connection to another TIPC port
* @ sock : socket structure
* @ dest : socket address for destination port
* @ destlen : size of socket address data structure
* @ flags : ( unused )
*
* Returns 0 on success , errno otherwise
*/
static int connect ( struct socket * sock , struct sockaddr * dest , int destlen ,
int flags )
{
struct tipc_sock * tsock = tipc_sk ( sock - > sk ) ;
struct sockaddr_tipc * dst = ( struct sockaddr_tipc * ) dest ;
2006-03-21 09:36:47 +03:00
struct msghdr m = { NULL , } ;
2006-01-02 21:04:38 +03:00
struct sk_buff * buf ;
struct tipc_msg * msg ;
int res ;
/* For now, TIPC does not allow use of connect() with DGRAM or RDM types */
if ( sock - > state = = SS_READY )
return - EOPNOTSUPP ;
/* MOVE THE REST OF THIS ERROR CHECKING TO send_msg()? */
if ( sock - > state = = SS_LISTENING )
return - EOPNOTSUPP ;
if ( sock - > state = = SS_CONNECTING )
return - EALREADY ;
if ( sock - > state ! = SS_UNCONNECTED )
return - EISCONN ;
if ( ( dst - > family ! = AF_TIPC ) | |
( ( dst - > addrtype ! = TIPC_ADDR_NAME ) & & ( dst - > addrtype ! = TIPC_ADDR_ID ) ) )
return - EINVAL ;
/* Send a 'SYN-' to destination */
m . msg_name = dest ;
2006-03-21 09:36:47 +03:00
if ( ( res = send_msg ( NULL , sock , & m , 0 ) ) < 0 ) {
2006-01-02 21:04:38 +03:00
sock - > state = SS_DISCONNECTING ;
return res ;
}
if ( down_interruptible ( & tsock - > sem ) )
return - ERESTARTSYS ;
/* Wait for destination's 'ACK' response */
res = wait_event_interruptible_timeout ( * sock - > sk - > sk_sleep ,
skb_queue_len ( & sock - > sk - > sk_receive_queue ) ,
sock - > sk - > sk_rcvtimeo ) ;
buf = skb_peek ( & sock - > sk - > sk_receive_queue ) ;
if ( res > 0 ) {
msg = buf_msg ( buf ) ;
res = auto_connect ( sock , tsock , msg ) ;
if ( ! res ) {
if ( dst - > addrtype = = TIPC_ADDR_NAME ) {
tsock - > p - > conn_type = dst - > addr . name . name . type ;
tsock - > p - > conn_instance = dst - > addr . name . name . instance ;
}
if ( ! msg_data_sz ( msg ) )
advance_queue ( tsock ) ;
}
} else {
if ( res = = 0 ) {
res = - ETIMEDOUT ;
} else
{ /* leave "res" unchanged */ }
sock - > state = SS_DISCONNECTING ;
}
up ( & tsock - > sem ) ;
return res ;
}
/**
* listen - allow socket to listen for incoming connections
* @ sock : socket structure
* @ len : ( unused )
*
* Returns 0 on success , errno otherwise
*/
static int listen ( struct socket * sock , int len )
{
/* REQUIRES SOCKET LOCKING OF SOME SORT? */
if ( sock - > state = = SS_READY )
return - EOPNOTSUPP ;
if ( sock - > state ! = SS_UNCONNECTED )
return - EINVAL ;
sock - > state = SS_LISTENING ;
return 0 ;
}
/**
* accept - wait for connection request
* @ sock : listening socket
* @ newsock : new socket that is to be connected
* @ flags : file - related flags associated with socket
*
* Returns 0 on success , errno otherwise
*/
static int accept ( struct socket * sock , struct socket * newsock , int flags )
{
struct tipc_sock * tsock = tipc_sk ( sock - > sk ) ;
struct sk_buff * buf ;
int res = - EFAULT ;
if ( sock - > state = = SS_READY )
return - EOPNOTSUPP ;
if ( sock - > state ! = SS_LISTENING )
return - EINVAL ;
if ( unlikely ( ( skb_queue_len ( & sock - > sk - > sk_receive_queue ) = = 0 ) & &
( flags & O_NONBLOCK ) ) )
return - EWOULDBLOCK ;
if ( down_interruptible ( & tsock - > sem ) )
return - ERESTARTSYS ;
if ( wait_event_interruptible ( * sock - > sk - > sk_sleep ,
skb_queue_len ( & sock - > sk - > sk_receive_queue ) ) ) {
res = - ERESTARTSYS ;
goto exit ;
}
buf = skb_peek ( & sock - > sk - > sk_receive_queue ) ;
res = tipc_create ( newsock , 0 ) ;
if ( ! res ) {
struct tipc_sock * new_tsock = tipc_sk ( newsock - > sk ) ;
struct tipc_portid id ;
struct tipc_msg * msg = buf_msg ( buf ) ;
u32 new_ref = new_tsock - > p - > ref ;
id . ref = msg_origport ( msg ) ;
id . node = msg_orignode ( msg ) ;
tipc_connect2port ( new_ref , & id ) ;
newsock - > state = SS_CONNECTED ;
tipc_set_portimportance ( new_ref , msg_importance ( msg ) ) ;
if ( msg_named ( msg ) ) {
new_tsock - > p - > conn_type = msg_nametype ( msg ) ;
new_tsock - > p - > conn_instance = msg_nameinst ( msg ) ;
}
/*
* Respond to ' SYN - ' by discarding it & returning ' ACK ' - .
* Respond to ' SYN + ' by queuing it on new socket .
*/
msg_dbg ( msg , " <ACC<: " ) ;
if ( ! msg_data_sz ( msg ) ) {
2006-03-21 09:36:47 +03:00
struct msghdr m = { NULL , } ;
2006-01-02 21:04:38 +03:00
2006-03-21 09:36:47 +03:00
send_packet ( NULL , newsock , & m , 0 ) ;
2006-01-02 21:04:38 +03:00
advance_queue ( tsock ) ;
} else {
sock_lock ( tsock ) ;
skb_dequeue ( & sock - > sk - > sk_receive_queue ) ;
sock_unlock ( tsock ) ;
skb_queue_head ( & newsock - > sk - > sk_receive_queue , buf ) ;
}
}
exit :
up ( & tsock - > sem ) ;
return res ;
}
/**
* shutdown - shutdown socket connection
* @ sock : socket structure
* @ how : direction to close ( always treated as read + write )
*
* Terminates connection ( if necessary ) , then purges socket ' s receive queue .
*
* Returns 0 on success , errno otherwise
*/
static int shutdown ( struct socket * sock , int how )
{
struct tipc_sock * tsock = tipc_sk ( sock - > sk ) ;
struct sk_buff * buf ;
int res ;
/* Could return -EINVAL for an invalid "how", but why bother? */
if ( down_interruptible ( & tsock - > sem ) )
return - ERESTARTSYS ;
sock_lock ( tsock ) ;
switch ( sock - > state ) {
case SS_CONNECTED :
/* Send 'FIN+' or 'FIN-' message to peer */
sock_unlock ( tsock ) ;
restart :
if ( ( buf = skb_dequeue ( & sock - > sk - > sk_receive_queue ) ) ) {
atomic_dec ( & tipc_queue_size ) ;
if ( TIPC_SKB_CB ( buf ) - > handle ! = msg_data ( buf_msg ( buf ) ) ) {
buf_discard ( buf ) ;
goto restart ;
}
tipc_reject_msg ( buf , TIPC_CONN_SHUTDOWN ) ;
}
else {
tipc_shutdown ( tsock - > p - > ref ) ;
}
sock_lock ( tsock ) ;
/* fall through */
case SS_DISCONNECTING :
/* Discard any unreceived messages */
while ( ( buf = skb_dequeue ( & sock - > sk - > sk_receive_queue ) ) ) {
atomic_dec ( & tipc_queue_size ) ;
buf_discard ( buf ) ;
}
tsock - > p - > conn_unacked = 0 ;
/* fall through */
case SS_CONNECTING :
sock - > state = SS_DISCONNECTING ;
res = 0 ;
break ;
default :
res = - ENOTCONN ;
}
sock_unlock ( tsock ) ;
up ( & tsock - > sem ) ;
return res ;
}
/**
* setsockopt - set socket option
* @ sock : socket structure
* @ lvl : option level
* @ opt : option identifier
* @ ov : pointer to new option value
* @ ol : length of option value
*
* For stream sockets only , accepts and ignores all IPPROTO_TCP options
* ( to ease compatibility ) .
*
* Returns 0 on success , errno otherwise
*/
static int setsockopt ( struct socket * sock , int lvl , int opt , char * ov , int ol )
{
struct tipc_sock * tsock = tipc_sk ( sock - > sk ) ;
u32 value ;
int res ;
if ( ( lvl = = IPPROTO_TCP ) & & ( sock - > type = = SOCK_STREAM ) )
return 0 ;
if ( lvl ! = SOL_TIPC )
return - ENOPROTOOPT ;
if ( ol < sizeof ( value ) )
return - EINVAL ;
if ( ( res = get_user ( value , ( u32 * ) ov ) ) )
return res ;
if ( down_interruptible ( & tsock - > sem ) )
return - ERESTARTSYS ;
switch ( opt ) {
case TIPC_IMPORTANCE :
res = tipc_set_portimportance ( tsock - > p - > ref , value ) ;
break ;
case TIPC_SRC_DROPPABLE :
if ( sock - > type ! = SOCK_STREAM )
res = tipc_set_portunreliable ( tsock - > p - > ref , value ) ;
else
res = - ENOPROTOOPT ;
break ;
case TIPC_DEST_DROPPABLE :
res = tipc_set_portunreturnable ( tsock - > p - > ref , value ) ;
break ;
case TIPC_CONN_TIMEOUT :
sock - > sk - > sk_rcvtimeo = ( value * HZ / 1000 ) ;
break ;
default :
res = - EINVAL ;
}
up ( & tsock - > sem ) ;
return res ;
}
/**
* getsockopt - get socket option
* @ sock : socket structure
* @ lvl : option level
* @ opt : option identifier
* @ ov : receptacle for option value
* @ ol : receptacle for length of option value
*
* For stream sockets only , returns 0 length result for all IPPROTO_TCP options
* ( to ease compatibility ) .
*
* Returns 0 on success , errno otherwise
*/
static int getsockopt ( struct socket * sock , int lvl , int opt , char * ov , int * ol )
{
struct tipc_sock * tsock = tipc_sk ( sock - > sk ) ;
int len ;
u32 value ;
int res ;
if ( ( lvl = = IPPROTO_TCP ) & & ( sock - > type = = SOCK_STREAM ) )
return put_user ( 0 , ol ) ;
if ( lvl ! = SOL_TIPC )
return - ENOPROTOOPT ;
if ( ( res = get_user ( len , ol ) ) )
return res ;
if ( down_interruptible ( & tsock - > sem ) )
return - ERESTARTSYS ;
switch ( opt ) {
case TIPC_IMPORTANCE :
res = tipc_portimportance ( tsock - > p - > ref , & value ) ;
break ;
case TIPC_SRC_DROPPABLE :
res = tipc_portunreliable ( tsock - > p - > ref , & value ) ;
break ;
case TIPC_DEST_DROPPABLE :
res = tipc_portunreturnable ( tsock - > p - > ref , & value ) ;
break ;
case TIPC_CONN_TIMEOUT :
value = ( sock - > sk - > sk_rcvtimeo * 1000 ) / HZ ;
break ;
default :
res = - EINVAL ;
}
if ( res ) {
/* "get" failed */
}
else if ( len < sizeof ( value ) ) {
res = - EINVAL ;
}
else if ( ( res = copy_to_user ( ov , & value , sizeof ( value ) ) ) ) {
/* couldn't return value */
}
else {
res = put_user ( sizeof ( value ) , ol ) ;
}
up ( & tsock - > sem ) ;
return res ;
}
/**
* Placeholders for non - implemented functionality
*
* Returns error code ( POSIX - compliant where defined )
*/
static int ioctl ( struct socket * s , u32 cmd , unsigned long arg )
{
return - EINVAL ;
}
static int no_mmap ( struct file * file , struct socket * sock ,
struct vm_area_struct * vma )
{
return - EINVAL ;
}
static ssize_t no_sendpage ( struct socket * sock , struct page * page ,
int offset , size_t size , int flags )
{
return - EINVAL ;
}
static int no_skpair ( struct socket * s1 , struct socket * s2 )
{
return - EOPNOTSUPP ;
}
/**
* Protocol switches for the various types of TIPC sockets
*/
static struct proto_ops msg_ops = {
. owner = THIS_MODULE ,
. family = AF_TIPC ,
. release = release ,
. bind = bind ,
. connect = connect ,
. socketpair = no_skpair ,
. accept = accept ,
. getname = get_name ,
. poll = poll ,
. ioctl = ioctl ,
. listen = listen ,
. shutdown = shutdown ,
. setsockopt = setsockopt ,
. getsockopt = getsockopt ,
. sendmsg = send_msg ,
. recvmsg = recv_msg ,
. mmap = no_mmap ,
. sendpage = no_sendpage
} ;
static struct proto_ops packet_ops = {
. owner = THIS_MODULE ,
. family = AF_TIPC ,
. release = release ,
. bind = bind ,
. connect = connect ,
. socketpair = no_skpair ,
. accept = accept ,
. getname = get_name ,
. poll = poll ,
. ioctl = ioctl ,
. listen = listen ,
. shutdown = shutdown ,
. setsockopt = setsockopt ,
. getsockopt = getsockopt ,
. sendmsg = send_packet ,
. recvmsg = recv_msg ,
. mmap = no_mmap ,
. sendpage = no_sendpage
} ;
static struct proto_ops stream_ops = {
. owner = THIS_MODULE ,
. family = AF_TIPC ,
. release = release ,
. bind = bind ,
. connect = connect ,
. socketpair = no_skpair ,
. accept = accept ,
. getname = get_name ,
. poll = poll ,
. ioctl = ioctl ,
. listen = listen ,
. shutdown = shutdown ,
. setsockopt = setsockopt ,
. getsockopt = getsockopt ,
. sendmsg = send_stream ,
. recvmsg = recv_stream ,
. mmap = no_mmap ,
. sendpage = no_sendpage
} ;
static struct net_proto_family tipc_family_ops = {
. owner = THIS_MODULE ,
. family = AF_TIPC ,
. create = tipc_create
} ;
static struct proto tipc_proto = {
. name = " TIPC " ,
. owner = THIS_MODULE ,
. obj_size = sizeof ( struct tipc_sock )
} ;
/**
2006-01-18 02:38:21 +03:00
* tipc_socket_init - initialize TIPC socket interface
2006-01-02 21:04:38 +03:00
*
* Returns 0 on success , errno otherwise
*/
2006-01-18 02:38:21 +03:00
int tipc_socket_init ( void )
2006-01-02 21:04:38 +03:00
{
int res ;
res = proto_register ( & tipc_proto , 1 ) ;
if ( res ) {
2006-01-11 15:52:51 +03:00
err ( " Failed to register TIPC protocol type \n " ) ;
2006-01-02 21:04:38 +03:00
goto out ;
}
res = sock_register ( & tipc_family_ops ) ;
if ( res ) {
2006-01-11 15:52:51 +03:00
err ( " Failed to register TIPC socket type \n " ) ;
2006-01-02 21:04:38 +03:00
proto_unregister ( & tipc_proto ) ;
goto out ;
}
sockets_enabled = 1 ;
out :
return res ;
}
/**
2006-01-18 02:38:21 +03:00
* tipc_socket_stop - stop TIPC socket interface
2006-01-02 21:04:38 +03:00
*/
2006-01-18 02:38:21 +03:00
void tipc_socket_stop ( void )
2006-01-02 21:04:38 +03:00
{
if ( ! sockets_enabled )
return ;
sockets_enabled = 0 ;
sock_unregister ( tipc_family_ops . family ) ;
proto_unregister ( & tipc_proto ) ;
}