2005-08-10 07:14:34 +04:00
/*
* net / dccp / proto . c
*
* An implementation of the DCCP protocol
* Arnaldo Carvalho de Melo < acme @ conectiva . com . br >
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*/
# include <linux/dccp.h>
# include <linux/module.h>
# include <linux/types.h>
# include <linux/sched.h>
# include <linux/kernel.h>
# include <linux/skbuff.h>
# include <linux/netdevice.h>
# include <linux/in.h>
# include <linux/if_arp.h>
# include <linux/init.h>
# include <linux/random.h>
# include <net/checksum.h>
2005-12-27 07:43:12 +03:00
# include <net/inet_sock.h>
2005-08-10 07:14:34 +04:00
# include <net/sock.h>
# include <net/xfrm.h>
# include <asm/semaphore.h>
# include <linux/spinlock.h>
# include <linux/timer.h>
# include <linux/delay.h>
# include <linux/poll.h>
# include "ccid.h"
# include "dccp.h"
[DCCP]: Initial feature negotiation implementation
Still needs more work, but boots and doesn't crashes, even
does some negotiation!
18:38:52.174934 127.0.0.1.43458 > 127.0.0.1.5001: request <change_l ack_ratio 2, change_r ccid 2, change_l ccid 2>
18:38:52.218526 127.0.0.1.5001 > 127.0.0.1.43458: response <nop, nop, change_l ack_ratio 2, confirm_r ccid 2 2, confirm_l ccid 2 2, confirm_r ack_ratio 2>
18:38:52.185398 127.0.0.1.43458 > 127.0.0.1.5001: <nop, confirm_r ack_ratio 2, ack_vector0 0x00, elapsed_time 212>
:-)
Signed-off-by: Andrea Bittau <a.bittau@cs.ucl.ac.uk>
Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2006-03-21 04:43:56 +03:00
# include "feat.h"
2005-08-10 07:14:34 +04:00
2005-08-26 23:05:31 +04:00
DEFINE_SNMP_STAT ( struct dccp_mib , dccp_statistics ) __read_mostly ;
2005-08-10 07:14:34 +04:00
2005-12-14 10:24:16 +03:00
EXPORT_SYMBOL_GPL ( dccp_statistics ) ;
2005-08-10 07:14:34 +04:00
atomic_t dccp_orphan_count = ATOMIC_INIT ( 0 ) ;
2005-12-14 10:24:16 +03:00
EXPORT_SYMBOL_GPL ( dccp_orphan_count ) ;
2006-03-21 08:24:19 +03:00
struct inet_hashinfo __cacheline_aligned dccp_hashinfo = {
. lhash_lock = RW_LOCK_UNLOCKED ,
. lhash_users = ATOMIC_INIT ( 0 ) ,
. lhash_wait = __WAIT_QUEUE_HEAD_INITIALIZER ( dccp_hashinfo . lhash_wait ) ,
} ;
EXPORT_SYMBOL_GPL ( dccp_hashinfo ) ;
2006-11-20 23:30:17 +03:00
/* the maximum queue length for tx in packets. 0 is no limit */
int sysctl_dccp_tx_qlen __read_mostly = 5 ;
2006-03-21 08:58:56 +03:00
void dccp_set_state ( struct sock * sk , const int state )
{
const int oldstate = sk - > sk_state ;
dccp_pr_debug ( " %s(%p) %-10.10s -> %s \n " ,
dccp_role ( sk ) , sk ,
dccp_state_name ( oldstate ) , dccp_state_name ( state ) ) ;
WARN_ON ( state = = oldstate ) ;
switch ( state ) {
case DCCP_OPEN :
if ( oldstate ! = DCCP_OPEN )
DCCP_INC_STATS ( DCCP_MIB_CURRESTAB ) ;
break ;
case DCCP_CLOSED :
if ( oldstate = = DCCP_CLOSING | | oldstate = = DCCP_OPEN )
DCCP_INC_STATS ( DCCP_MIB_ESTABRESETS ) ;
sk - > sk_prot - > unhash ( sk ) ;
if ( inet_csk ( sk ) - > icsk_bind_hash ! = NULL & &
! ( sk - > sk_userlocks & SOCK_BINDPORT_LOCK ) )
inet_put_port ( & dccp_hashinfo , sk ) ;
/* fall through */
default :
if ( oldstate = = DCCP_OPEN )
DCCP_DEC_STATS ( DCCP_MIB_CURRESTAB ) ;
}
/* Change state AFTER socket is unhashed to avoid closed
* socket sitting in hash tables .
*/
sk - > sk_state = state ;
}
EXPORT_SYMBOL_GPL ( dccp_set_state ) ;
void dccp_done ( struct sock * sk )
{
dccp_set_state ( sk , DCCP_CLOSED ) ;
dccp_clear_xmit_timers ( sk ) ;
sk - > sk_shutdown = SHUTDOWN_MASK ;
if ( ! sock_flag ( sk , SOCK_DEAD ) )
sk - > sk_state_change ( sk ) ;
else
inet_csk_destroy_sock ( sk ) ;
}
EXPORT_SYMBOL_GPL ( dccp_done ) ;
2005-08-10 07:14:34 +04:00
const char * dccp_packet_name ( const int type )
{
static const char * dccp_packet_names [ ] = {
[ DCCP_PKT_REQUEST ] = " REQUEST " ,
[ DCCP_PKT_RESPONSE ] = " RESPONSE " ,
[ DCCP_PKT_DATA ] = " DATA " ,
[ DCCP_PKT_ACK ] = " ACK " ,
[ DCCP_PKT_DATAACK ] = " DATAACK " ,
[ DCCP_PKT_CLOSEREQ ] = " CLOSEREQ " ,
[ DCCP_PKT_CLOSE ] = " CLOSE " ,
[ DCCP_PKT_RESET ] = " RESET " ,
[ DCCP_PKT_SYNC ] = " SYNC " ,
[ DCCP_PKT_SYNCACK ] = " SYNCACK " ,
} ;
if ( type > = DCCP_NR_PKT_TYPES )
return " INVALID " ;
else
return dccp_packet_names [ type ] ;
}
EXPORT_SYMBOL_GPL ( dccp_packet_name ) ;
const char * dccp_state_name ( const int state )
{
static char * dccp_state_names [ ] = {
[ DCCP_OPEN ] = " OPEN " ,
[ DCCP_REQUESTING ] = " REQUESTING " ,
[ DCCP_PARTOPEN ] = " PARTOPEN " ,
[ DCCP_LISTEN ] = " LISTEN " ,
[ DCCP_RESPOND ] = " RESPOND " ,
[ DCCP_CLOSING ] = " CLOSING " ,
[ DCCP_TIME_WAIT ] = " TIME_WAIT " ,
[ DCCP_CLOSED ] = " CLOSED " ,
} ;
if ( state > = DCCP_MAX_STATES )
return " INVALID STATE! " ;
else
return dccp_state_names [ state ] ;
}
EXPORT_SYMBOL_GPL ( dccp_state_name ) ;
2006-03-21 08:23:39 +03:00
void dccp_hash ( struct sock * sk )
{
inet_hash ( & dccp_hashinfo , sk ) ;
}
EXPORT_SYMBOL_GPL ( dccp_hash ) ;
void dccp_unhash ( struct sock * sk )
{
inet_unhash ( & dccp_hashinfo , sk ) ;
}
EXPORT_SYMBOL_GPL ( dccp_unhash ) ;
2006-03-21 09:00:37 +03:00
int dccp_init_sock ( struct sock * sk , const __u8 ctl_sock_initialized )
2006-03-21 08:23:15 +03:00
{
struct dccp_sock * dp = dccp_sk ( sk ) ;
2006-03-21 09:50:58 +03:00
struct dccp_minisock * dmsk = dccp_msk ( sk ) ;
2006-03-21 08:23:15 +03:00
struct inet_connection_sock * icsk = inet_csk ( sk ) ;
2006-03-21 09:50:58 +03:00
dccp_minisock_init ( & dp - > dccps_minisock ) ;
2006-03-21 08:23:15 +03:00
do_gettimeofday ( & dp - > dccps_epoch ) ;
/*
* FIXME : We ' re hardcoding the CCID , and doing this at this point makes
* the listening ( master ) sock get CCID control blocks , which is not
* necessary , but for now , to not mess with the test userspace apps ,
* lets leave it here , later the real solution is to do this in a
* setsockopt ( CCIDs - I - want / accept ) . - acme
*/
2006-03-21 09:00:37 +03:00
if ( likely ( ctl_sock_initialized ) ) {
2006-03-21 09:51:53 +03:00
int rc = dccp_feat_init ( dmsk ) ;
2006-03-21 08:23:15 +03:00
if ( rc )
return rc ;
2006-03-21 09:50:58 +03:00
if ( dmsk - > dccpms_send_ack_vector ) {
2006-03-21 08:23:15 +03:00
dp - > dccps_hc_rx_ackvec = dccp_ackvec_alloc ( GFP_KERNEL ) ;
if ( dp - > dccps_hc_rx_ackvec = = NULL )
return - ENOMEM ;
}
2006-03-21 09:50:58 +03:00
dp - > dccps_hc_rx_ccid = ccid_hc_rx_new ( dmsk - > dccpms_rx_ccid ,
sk , GFP_KERNEL ) ;
dp - > dccps_hc_tx_ccid = ccid_hc_tx_new ( dmsk - > dccpms_tx_ccid ,
sk , GFP_KERNEL ) ;
2006-12-10 21:01:18 +03:00
if ( unlikely ( dp - > dccps_hc_rx_ccid = = NULL | |
2006-03-21 08:23:15 +03:00
dp - > dccps_hc_tx_ccid = = NULL ) ) {
ccid_hc_rx_delete ( dp - > dccps_hc_rx_ccid , sk ) ;
ccid_hc_tx_delete ( dp - > dccps_hc_tx_ccid , sk ) ;
2006-03-21 09:50:58 +03:00
if ( dmsk - > dccpms_send_ack_vector ) {
2006-03-21 08:23:15 +03:00
dccp_ackvec_free ( dp - > dccps_hc_rx_ackvec ) ;
dp - > dccps_hc_rx_ackvec = NULL ;
}
dp - > dccps_hc_rx_ccid = dp - > dccps_hc_tx_ccid = NULL ;
return - ENOMEM ;
}
} else {
/* control socket doesn't need feat nego */
2006-03-21 09:50:58 +03:00
INIT_LIST_HEAD ( & dmsk - > dccpms_pending ) ;
INIT_LIST_HEAD ( & dmsk - > dccpms_conf ) ;
2006-03-21 08:23:15 +03:00
}
dccp_init_xmit_timers ( sk ) ;
icsk - > icsk_rto = DCCP_TIMEOUT_INIT ;
2006-11-13 18:23:52 +03:00
icsk - > icsk_syn_retries = sysctl_dccp_request_retries ;
2006-03-21 08:23:15 +03:00
sk - > sk_state = DCCP_CLOSED ;
sk - > sk_write_space = dccp_write_space ;
icsk - > icsk_sync_mss = dccp_sync_mss ;
dp - > dccps_mss_cache = 536 ;
dp - > dccps_role = DCCP_ROLE_UNDEFINED ;
2006-09-22 12:33:58 +04:00
dp - > dccps_service = DCCP_SERVICE_CODE_IS_ABSENT ;
2006-03-21 08:23:15 +03:00
dp - > dccps_l_ack_ratio = dp - > dccps_r_ack_ratio = 1 ;
return 0 ;
}
EXPORT_SYMBOL_GPL ( dccp_init_sock ) ;
int dccp_destroy_sock ( struct sock * sk )
{
struct dccp_sock * dp = dccp_sk ( sk ) ;
2006-03-21 09:51:53 +03:00
struct dccp_minisock * dmsk = dccp_msk ( sk ) ;
2006-03-21 08:23:15 +03:00
/*
* DCCP doesn ' t use sk_write_queue , just sk_send_head
* for retransmissions
*/
if ( sk - > sk_send_head ! = NULL ) {
kfree_skb ( sk - > sk_send_head ) ;
sk - > sk_send_head = NULL ;
}
/* Clean up a referenced DCCP bind bucket. */
if ( inet_csk ( sk ) - > icsk_bind_hash ! = NULL )
inet_put_port ( & dccp_hashinfo , sk ) ;
kfree ( dp - > dccps_service_list ) ;
dp - > dccps_service_list = NULL ;
2006-03-21 09:51:53 +03:00
if ( dmsk - > dccpms_send_ack_vector ) {
2006-03-21 08:23:15 +03:00
dccp_ackvec_free ( dp - > dccps_hc_rx_ackvec ) ;
dp - > dccps_hc_rx_ackvec = NULL ;
}
ccid_hc_rx_delete ( dp - > dccps_hc_rx_ccid , sk ) ;
ccid_hc_tx_delete ( dp - > dccps_hc_tx_ccid , sk ) ;
dp - > dccps_hc_rx_ccid = dp - > dccps_hc_tx_ccid = NULL ;
/* clean up feature negotiation state */
2006-03-21 09:51:53 +03:00
dccp_feat_clean ( dmsk ) ;
2006-03-21 08:23:15 +03:00
return 0 ;
}
EXPORT_SYMBOL_GPL ( dccp_destroy_sock ) ;
2006-11-16 13:30:37 +03:00
static inline int dccp_listen_start ( struct sock * sk , int backlog )
2005-08-10 07:14:34 +04:00
{
2005-09-17 03:58:40 +04:00
struct dccp_sock * dp = dccp_sk ( sk ) ;
dp - > dccps_role = DCCP_ROLE_LISTEN ;
2006-11-16 13:30:37 +03:00
return inet_csk_listen_start ( sk , backlog ) ;
2005-08-10 07:14:34 +04:00
}
int dccp_disconnect ( struct sock * sk , int flags )
{
struct inet_connection_sock * icsk = inet_csk ( sk ) ;
struct inet_sock * inet = inet_sk ( sk ) ;
int err = 0 ;
const int old_state = sk - > sk_state ;
if ( old_state ! = DCCP_CLOSED )
dccp_set_state ( sk , DCCP_CLOSED ) ;
/* ABORT function of RFC793 */
if ( old_state = = DCCP_LISTEN ) {
inet_csk_listen_stop ( sk ) ;
/* FIXME: do the active reset thing */
} else if ( old_state = = DCCP_REQUESTING )
sk - > sk_err = ECONNRESET ;
dccp_clear_xmit_timers ( sk ) ;
__skb_queue_purge ( & sk - > sk_receive_queue ) ;
if ( sk - > sk_send_head ! = NULL ) {
__kfree_skb ( sk - > sk_send_head ) ;
sk - > sk_send_head = NULL ;
}
inet - > dport = 0 ;
if ( ! ( sk - > sk_userlocks & SOCK_BINDADDR_LOCK ) )
inet_reset_saddr ( sk ) ;
sk - > sk_shutdown = 0 ;
sock_reset_flag ( sk , SOCK_DONE ) ;
icsk - > icsk_backoff = 0 ;
inet_csk_delack_init ( sk ) ;
__sk_dst_reset ( sk ) ;
BUG_TRAP ( ! inet - > num | | icsk - > icsk_bind_hash ) ;
sk - > sk_error_report ( sk ) ;
return err ;
}
2005-12-14 10:24:16 +03:00
EXPORT_SYMBOL_GPL ( dccp_disconnect ) ;
2005-08-24 08:54:23 +04:00
/*
* Wait for a DCCP event .
*
* Note that we don ' t need to lock the socket , as the upper poll layers
* take care of normal races ( between the test and the event ) and we don ' t
* go look at any of the socket buffers directly .
*/
2005-12-14 10:24:16 +03:00
unsigned int dccp_poll ( struct file * file , struct socket * sock ,
poll_table * wait )
2005-08-24 08:54:23 +04:00
{
unsigned int mask ;
struct sock * sk = sock - > sk ;
poll_wait ( file , sk - > sk_sleep , wait ) ;
if ( sk - > sk_state = = DCCP_LISTEN )
return inet_csk_listen_poll ( sk ) ;
/* Socket is not locked. We are protected from async events
by poll logic and correct handling of state changes
made by another threads is impossible in any case .
*/
mask = 0 ;
if ( sk - > sk_err )
mask = POLLERR ;
if ( sk - > sk_shutdown = = SHUTDOWN_MASK | | sk - > sk_state = = DCCP_CLOSED )
mask | = POLLHUP ;
if ( sk - > sk_shutdown & RCV_SHUTDOWN )
2006-03-25 14:07:39 +03:00
mask | = POLLIN | POLLRDNORM | POLLRDHUP ;
2005-08-24 08:54:23 +04:00
/* Connected? */
if ( ( 1 < < sk - > sk_state ) & ~ ( DCCPF_REQUESTING | DCCPF_RESPOND ) ) {
if ( atomic_read ( & sk - > sk_rmem_alloc ) > 0 )
mask | = POLLIN | POLLRDNORM ;
if ( ! ( sk - > sk_shutdown & SEND_SHUTDOWN ) ) {
if ( sk_stream_wspace ( sk ) > = sk_stream_min_wspace ( sk ) ) {
mask | = POLLOUT | POLLWRNORM ;
} else { /* send SIGIO later */
set_bit ( SOCK_ASYNC_NOSPACE ,
& sk - > sk_socket - > flags ) ;
set_bit ( SOCK_NOSPACE , & sk - > sk_socket - > flags ) ;
/* Race breaker. If space is freed after
* wspace test but before the flags are set ,
* IO signal will be lost .
*/
if ( sk_stream_wspace ( sk ) > = sk_stream_min_wspace ( sk ) )
mask | = POLLOUT | POLLWRNORM ;
}
}
}
return mask ;
}
2005-12-14 10:24:16 +03:00
EXPORT_SYMBOL_GPL ( dccp_poll ) ;
2005-08-10 07:14:34 +04:00
int dccp_ioctl ( struct sock * sk , int cmd , unsigned long arg )
{
dccp_pr_debug ( " entry \n " ) ;
return - ENOIOCTLCMD ;
}
2005-12-14 10:24:16 +03:00
EXPORT_SYMBOL_GPL ( dccp_ioctl ) ;
2006-03-21 06:23:32 +03:00
static int dccp_setsockopt_service ( struct sock * sk , const __be32 service ,
2005-09-17 03:58:40 +04:00
char __user * optval , int optlen )
{
struct dccp_sock * dp = dccp_sk ( sk ) ;
struct dccp_service_list * sl = NULL ;
2006-12-10 21:01:18 +03:00
if ( service = = DCCP_SERVICE_INVALID_VALUE | |
2005-09-17 03:58:40 +04:00
optlen > DCCP_SERVICE_LIST_MAX_LEN * sizeof ( u32 ) )
return - EINVAL ;
if ( optlen > sizeof ( service ) ) {
sl = kmalloc ( optlen , GFP_KERNEL ) ;
if ( sl = = NULL )
return - ENOMEM ;
sl - > dccpsl_nr = optlen / sizeof ( u32 ) - 1 ;
if ( copy_from_user ( sl - > dccpsl_list ,
optval + sizeof ( service ) ,
optlen - sizeof ( service ) ) | |
dccp_list_has_service ( sl , DCCP_SERVICE_INVALID_VALUE ) ) {
kfree ( sl ) ;
return - EFAULT ;
}
}
lock_sock ( sk ) ;
dp - > dccps_service = service ;
2005-11-08 20:41:34 +03:00
kfree ( dp - > dccps_service_list ) ;
2005-09-17 03:58:40 +04:00
dp - > dccps_service_list = sl ;
release_sock ( sk ) ;
return 0 ;
}
[DCCP]: Initial feature negotiation implementation
Still needs more work, but boots and doesn't crashes, even
does some negotiation!
18:38:52.174934 127.0.0.1.43458 > 127.0.0.1.5001: request <change_l ack_ratio 2, change_r ccid 2, change_l ccid 2>
18:38:52.218526 127.0.0.1.5001 > 127.0.0.1.43458: response <nop, nop, change_l ack_ratio 2, confirm_r ccid 2 2, confirm_l ccid 2 2, confirm_r ack_ratio 2>
18:38:52.185398 127.0.0.1.43458 > 127.0.0.1.5001: <nop, confirm_r ack_ratio 2, ack_vector0 0x00, elapsed_time 212>
:-)
Signed-off-by: Andrea Bittau <a.bittau@cs.ucl.ac.uk>
Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2006-03-21 04:43:56 +03:00
/* byte 1 is feature. the rest is the preference list */
static int dccp_setsockopt_change ( struct sock * sk , int type ,
struct dccp_so_feat __user * optval )
{
struct dccp_so_feat opt ;
u8 * val ;
int rc ;
if ( copy_from_user ( & opt , optval , sizeof ( opt ) ) )
return - EFAULT ;
val = kmalloc ( opt . dccpsf_len , GFP_KERNEL ) ;
if ( ! val )
return - ENOMEM ;
if ( copy_from_user ( val , opt . dccpsf_val , opt . dccpsf_len ) ) {
rc = - EFAULT ;
goto out_free_val ;
}
2006-03-21 09:51:53 +03:00
rc = dccp_feat_change ( dccp_msk ( sk ) , type , opt . dccpsf_feat ,
val , opt . dccpsf_len , GFP_KERNEL ) ;
[DCCP]: Initial feature negotiation implementation
Still needs more work, but boots and doesn't crashes, even
does some negotiation!
18:38:52.174934 127.0.0.1.43458 > 127.0.0.1.5001: request <change_l ack_ratio 2, change_r ccid 2, change_l ccid 2>
18:38:52.218526 127.0.0.1.5001 > 127.0.0.1.43458: response <nop, nop, change_l ack_ratio 2, confirm_r ccid 2 2, confirm_l ccid 2 2, confirm_r ack_ratio 2>
18:38:52.185398 127.0.0.1.43458 > 127.0.0.1.5001: <nop, confirm_r ack_ratio 2, ack_vector0 0x00, elapsed_time 212>
:-)
Signed-off-by: Andrea Bittau <a.bittau@cs.ucl.ac.uk>
Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2006-03-21 04:43:56 +03:00
if ( rc )
goto out_free_val ;
out :
return rc ;
out_free_val :
kfree ( val ) ;
goto out ;
}
2006-03-21 09:45:21 +03:00
static int do_dccp_setsockopt ( struct sock * sk , int level , int optname ,
char __user * optval , int optlen )
2005-08-10 07:14:34 +04:00
{
2006-11-14 17:57:34 +03:00
struct dccp_sock * dp = dccp_sk ( sk ) ;
int val , err = 0 ;
2005-08-10 07:14:34 +04:00
2005-08-28 11:51:32 +04:00
if ( optlen < sizeof ( int ) )
return - EINVAL ;
if ( get_user ( val , ( int __user * ) optval ) )
return - EFAULT ;
2005-09-17 03:58:40 +04:00
if ( optname = = DCCP_SOCKOPT_SERVICE )
return dccp_setsockopt_service ( sk , val , optval , optlen ) ;
2005-08-28 11:51:32 +04:00
2005-09-17 03:58:40 +04:00
lock_sock ( sk ) ;
2005-08-28 11:51:32 +04:00
switch ( optname ) {
case DCCP_SOCKOPT_PACKET_SIZE :
2006-11-29 00:33:36 +03:00
DCCP_WARN ( " sockopt(PACKET_SIZE) is deprecated: fix your app \n " ) ;
2006-11-29 00:42:03 +03:00
err = 0 ;
2005-08-28 11:51:32 +04:00
break ;
[DCCP]: Initial feature negotiation implementation
Still needs more work, but boots and doesn't crashes, even
does some negotiation!
18:38:52.174934 127.0.0.1.43458 > 127.0.0.1.5001: request <change_l ack_ratio 2, change_r ccid 2, change_l ccid 2>
18:38:52.218526 127.0.0.1.5001 > 127.0.0.1.43458: response <nop, nop, change_l ack_ratio 2, confirm_r ccid 2 2, confirm_l ccid 2 2, confirm_r ack_ratio 2>
18:38:52.185398 127.0.0.1.43458 > 127.0.0.1.5001: <nop, confirm_r ack_ratio 2, ack_vector0 0x00, elapsed_time 212>
:-)
Signed-off-by: Andrea Bittau <a.bittau@cs.ucl.ac.uk>
Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2006-03-21 04:43:56 +03:00
case DCCP_SOCKOPT_CHANGE_L :
if ( optlen ! = sizeof ( struct dccp_so_feat ) )
err = - EINVAL ;
else
err = dccp_setsockopt_change ( sk , DCCPO_CHANGE_L ,
2006-07-11 01:24:23 +04:00
( struct dccp_so_feat __user * )
[DCCP]: Initial feature negotiation implementation
Still needs more work, but boots and doesn't crashes, even
does some negotiation!
18:38:52.174934 127.0.0.1.43458 > 127.0.0.1.5001: request <change_l ack_ratio 2, change_r ccid 2, change_l ccid 2>
18:38:52.218526 127.0.0.1.5001 > 127.0.0.1.43458: response <nop, nop, change_l ack_ratio 2, confirm_r ccid 2 2, confirm_l ccid 2 2, confirm_r ack_ratio 2>
18:38:52.185398 127.0.0.1.43458 > 127.0.0.1.5001: <nop, confirm_r ack_ratio 2, ack_vector0 0x00, elapsed_time 212>
:-)
Signed-off-by: Andrea Bittau <a.bittau@cs.ucl.ac.uk>
Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2006-03-21 04:43:56 +03:00
optval ) ;
break ;
case DCCP_SOCKOPT_CHANGE_R :
if ( optlen ! = sizeof ( struct dccp_so_feat ) )
err = - EINVAL ;
else
err = dccp_setsockopt_change ( sk , DCCPO_CHANGE_R ,
2006-07-11 01:24:23 +04:00
( struct dccp_so_feat __user * )
[DCCP]: Initial feature negotiation implementation
Still needs more work, but boots and doesn't crashes, even
does some negotiation!
18:38:52.174934 127.0.0.1.43458 > 127.0.0.1.5001: request <change_l ack_ratio 2, change_r ccid 2, change_l ccid 2>
18:38:52.218526 127.0.0.1.5001 > 127.0.0.1.43458: response <nop, nop, change_l ack_ratio 2, confirm_r ccid 2 2, confirm_l ccid 2 2, confirm_r ack_ratio 2>
18:38:52.185398 127.0.0.1.43458 > 127.0.0.1.5001: <nop, confirm_r ack_ratio 2, ack_vector0 0x00, elapsed_time 212>
:-)
Signed-off-by: Andrea Bittau <a.bittau@cs.ucl.ac.uk>
Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2006-03-21 04:43:56 +03:00
optval ) ;
break ;
2006-11-10 22:43:06 +03:00
case DCCP_SOCKOPT_SEND_CSCOV : /* sender side, RFC 4340, sec. 9.2 */
if ( val < 0 | | val > 15 )
err = - EINVAL ;
else
dp - > dccps_pcslen = val ;
break ;
case DCCP_SOCKOPT_RECV_CSCOV : /* receiver side, RFC 4340 sec. 9.2.1 */
if ( val < 0 | | val > 15 )
err = - EINVAL ;
else {
dp - > dccps_pcrlen = val ;
/* FIXME: add feature negotiation,
* ChangeL ( MinimumChecksumCoverage , val ) */
}
break ;
2005-08-28 11:51:32 +04:00
default :
err = - ENOPROTOOPT ;
break ;
}
2006-11-10 22:43:06 +03:00
2005-08-28 11:51:32 +04:00
release_sock ( sk ) ;
return err ;
2005-08-10 07:14:34 +04:00
}
2006-03-21 09:45:21 +03:00
int dccp_setsockopt ( struct sock * sk , int level , int optname ,
char __user * optval , int optlen )
{
if ( level ! = SOL_DCCP )
return inet_csk ( sk ) - > icsk_af_ops - > setsockopt ( sk , level ,
optname , optval ,
optlen ) ;
return do_dccp_setsockopt ( sk , level , optname , optval , optlen ) ;
}
2006-03-21 09:48:35 +03:00
2005-12-14 10:24:16 +03:00
EXPORT_SYMBOL_GPL ( dccp_setsockopt ) ;
2006-03-21 09:45:21 +03:00
# ifdef CONFIG_COMPAT
int compat_dccp_setsockopt ( struct sock * sk , int level , int optname ,
2006-03-21 09:48:35 +03:00
char __user * optval , int optlen )
2006-03-21 09:45:21 +03:00
{
2006-03-21 09:46:16 +03:00
if ( level ! = SOL_DCCP )
return inet_csk_compat_setsockopt ( sk , level , optname ,
optval , optlen ) ;
2006-03-21 09:45:21 +03:00
return do_dccp_setsockopt ( sk , level , optname , optval , optlen ) ;
}
2006-03-21 09:48:35 +03:00
2006-03-21 09:45:21 +03:00
EXPORT_SYMBOL_GPL ( compat_dccp_setsockopt ) ;
# endif
2005-09-17 03:58:40 +04:00
static int dccp_getsockopt_service ( struct sock * sk , int len ,
2006-03-21 06:23:32 +03:00
__be32 __user * optval ,
2005-09-17 03:58:40 +04:00
int __user * optlen )
{
const struct dccp_sock * dp = dccp_sk ( sk ) ;
const struct dccp_service_list * sl ;
int err = - ENOENT , slen = 0 , total_len = sizeof ( u32 ) ;
lock_sock ( sk ) ;
if ( ( sl = dp - > dccps_service_list ) ! = NULL ) {
slen = sl - > dccpsl_nr * sizeof ( u32 ) ;
total_len + = slen ;
}
err = - EINVAL ;
if ( total_len > len )
goto out ;
err = 0 ;
if ( put_user ( total_len , optlen ) | |
put_user ( dp - > dccps_service , optval ) | |
( sl ! = NULL & & copy_to_user ( optval + 1 , sl - > dccpsl_list , slen ) ) )
err = - EFAULT ;
out :
release_sock ( sk ) ;
return err ;
}
2006-03-21 09:45:21 +03:00
static int do_dccp_getsockopt ( struct sock * sk , int level , int optname ,
2005-08-14 05:42:25 +04:00
char __user * optval , int __user * optlen )
2005-08-10 07:14:34 +04:00
{
2005-08-28 11:51:32 +04:00
struct dccp_sock * dp ;
int val , len ;
2005-08-10 07:14:34 +04:00
2005-08-28 11:51:32 +04:00
if ( get_user ( len , optlen ) )
return - EFAULT ;
2005-09-18 11:19:32 +04:00
if ( len < sizeof ( int ) )
2005-08-28 11:51:32 +04:00
return - EINVAL ;
dp = dccp_sk ( sk ) ;
switch ( optname ) {
case DCCP_SOCKOPT_PACKET_SIZE :
2006-11-29 00:33:36 +03:00
DCCP_WARN ( " sockopt(PACKET_SIZE) is deprecated: fix your app \n " ) ;
2006-11-29 00:42:03 +03:00
return 0 ;
2005-09-18 11:19:32 +04:00
case DCCP_SOCKOPT_SERVICE :
return dccp_getsockopt_service ( sk , len ,
2006-03-21 06:23:32 +03:00
( __be32 __user * ) optval , optlen ) ;
2006-11-10 22:43:06 +03:00
case DCCP_SOCKOPT_SEND_CSCOV :
val = dp - > dccps_pcslen ;
break ;
case DCCP_SOCKOPT_RECV_CSCOV :
val = dp - > dccps_pcrlen ;
break ;
2005-09-18 11:19:32 +04:00
case 128 . . . 191 :
return ccid_hc_rx_getsockopt ( dp - > dccps_hc_rx_ccid , sk , optname ,
len , ( u32 __user * ) optval , optlen ) ;
case 192 . . . 255 :
return ccid_hc_tx_getsockopt ( dp - > dccps_hc_tx_ccid , sk , optname ,
len , ( u32 __user * ) optval , optlen ) ;
2005-08-28 11:51:32 +04:00
default :
return - ENOPROTOOPT ;
}
if ( put_user ( len , optlen ) | | copy_to_user ( optval , & val , len ) )
return - EFAULT ;
return 0 ;
2005-08-10 07:14:34 +04:00
}
2006-03-21 09:45:21 +03:00
int dccp_getsockopt ( struct sock * sk , int level , int optname ,
char __user * optval , int __user * optlen )
{
if ( level ! = SOL_DCCP )
return inet_csk ( sk ) - > icsk_af_ops - > getsockopt ( sk , level ,
optname , optval ,
optlen ) ;
return do_dccp_getsockopt ( sk , level , optname , optval , optlen ) ;
}
2006-03-21 09:48:35 +03:00
2005-12-14 10:24:16 +03:00
EXPORT_SYMBOL_GPL ( dccp_getsockopt ) ;
2006-03-21 09:45:21 +03:00
# ifdef CONFIG_COMPAT
int compat_dccp_getsockopt ( struct sock * sk , int level , int optname ,
2006-03-21 09:48:35 +03:00
char __user * optval , int __user * optlen )
2006-03-21 09:45:21 +03:00
{
2006-03-21 09:46:16 +03:00
if ( level ! = SOL_DCCP )
return inet_csk_compat_getsockopt ( sk , level , optname ,
optval , optlen ) ;
2006-03-21 09:45:21 +03:00
return do_dccp_getsockopt ( sk , level , optname , optval , optlen ) ;
}
2006-03-21 09:48:35 +03:00
2006-03-21 09:45:21 +03:00
EXPORT_SYMBOL_GPL ( compat_dccp_getsockopt ) ;
# endif
2005-08-10 07:14:34 +04:00
int dccp_sendmsg ( struct kiocb * iocb , struct sock * sk , struct msghdr * msg ,
size_t len )
{
const struct dccp_sock * dp = dccp_sk ( sk ) ;
const int flags = msg - > msg_flags ;
const int noblock = flags & MSG_DONTWAIT ;
struct sk_buff * skb ;
int rc , size ;
long timeo ;
if ( len > dp - > dccps_mss_cache )
return - EMSGSIZE ;
lock_sock ( sk ) ;
2006-11-20 23:30:17 +03:00
if ( sysctl_dccp_tx_qlen & &
( sk - > sk_write_queue . qlen > = sysctl_dccp_tx_qlen ) ) {
rc = - EAGAIN ;
goto out_release ;
}
2005-08-10 07:30:56 +04:00
timeo = sock_sndtimeo ( sk , noblock ) ;
2005-08-10 07:14:34 +04:00
/*
* We have to use sk_stream_wait_connect here to set sk_write_pending ,
* so that the trick in dccp_rcv_request_sent_state_process .
*/
/* Wait for a connection to finish. */
if ( ( 1 < < sk - > sk_state ) & ~ ( DCCPF_OPEN | DCCPF_PARTOPEN | DCCPF_CLOSING ) )
if ( ( rc = sk_stream_wait_connect ( sk , & timeo ) ) ! = 0 )
2005-08-10 07:30:56 +04:00
goto out_release ;
2005-08-10 07:14:34 +04:00
size = sk - > sk_prot - > max_header + len ;
release_sock ( sk ) ;
skb = sock_alloc_send_skb ( sk , size , noblock , & rc ) ;
lock_sock ( sk ) ;
if ( skb = = NULL )
goto out_release ;
skb_reserve ( skb , sk - > sk_prot - > max_header ) ;
rc = memcpy_fromiovec ( skb_put ( skb , len ) , msg - > msg_iov , len ) ;
2005-08-10 07:30:56 +04:00
if ( rc ! = 0 )
goto out_discard ;
2006-08-27 06:16:45 +04:00
skb_queue_tail ( & sk - > sk_write_queue , skb ) ;
dccp_write_xmit ( sk , 0 ) ;
2005-08-10 07:14:34 +04:00
out_release :
release_sock ( sk ) ;
return rc ? : len ;
2005-08-10 07:30:56 +04:00
out_discard :
kfree_skb ( skb ) ;
2005-08-10 07:14:34 +04:00
goto out_release ;
}
2005-12-14 10:24:16 +03:00
EXPORT_SYMBOL_GPL ( dccp_sendmsg ) ;
2005-08-10 07:14:34 +04:00
int dccp_recvmsg ( struct kiocb * iocb , struct sock * sk , struct msghdr * msg ,
size_t len , int nonblock , int flags , int * addr_len )
{
const struct dccp_hdr * dh ;
long timeo ;
lock_sock ( sk ) ;
2005-08-14 03:35:17 +04:00
if ( sk - > sk_state = = DCCP_LISTEN ) {
len = - ENOTCONN ;
2005-08-10 07:14:34 +04:00
goto out ;
}
2005-08-14 03:35:17 +04:00
timeo = sock_rcvtimeo ( sk , nonblock ) ;
2005-08-10 07:14:34 +04:00
do {
2005-08-14 03:35:17 +04:00
struct sk_buff * skb = skb_peek ( & sk - > sk_receive_queue ) ;
2005-08-10 07:14:34 +04:00
2005-08-14 03:35:17 +04:00
if ( skb = = NULL )
goto verify_sock_status ;
2005-08-10 07:14:34 +04:00
2005-08-14 03:35:17 +04:00
dh = dccp_hdr ( skb ) ;
2005-08-10 07:14:34 +04:00
2005-08-14 03:35:17 +04:00
if ( dh - > dccph_type = = DCCP_PKT_DATA | |
dh - > dccph_type = = DCCP_PKT_DATAACK )
goto found_ok_skb ;
2005-08-10 07:14:34 +04:00
2005-08-14 03:35:17 +04:00
if ( dh - > dccph_type = = DCCP_PKT_RESET | |
dh - > dccph_type = = DCCP_PKT_CLOSE ) {
dccp_pr_debug ( " found fin ok! \n " ) ;
len = 0 ;
goto found_fin_ok ;
}
dccp_pr_debug ( " packet_type=%s \n " ,
dccp_packet_name ( dh - > dccph_type ) ) ;
2006-05-24 05:01:28 +04:00
sk_eat_skb ( sk , skb , 0 ) ;
2005-08-14 03:35:17 +04:00
verify_sock_status :
if ( sock_flag ( sk , SOCK_DONE ) ) {
len = 0 ;
2005-08-10 07:14:34 +04:00
break ;
2005-08-14 03:35:17 +04:00
}
2005-08-10 07:14:34 +04:00
2005-08-14 03:35:17 +04:00
if ( sk - > sk_err ) {
len = sock_error ( sk ) ;
break ;
}
2005-08-10 07:14:34 +04:00
2005-08-14 03:35:17 +04:00
if ( sk - > sk_shutdown & RCV_SHUTDOWN ) {
len = 0 ;
break ;
}
2005-08-10 07:14:34 +04:00
2005-08-14 03:35:17 +04:00
if ( sk - > sk_state = = DCCP_CLOSED ) {
if ( ! sock_flag ( sk , SOCK_DONE ) ) {
/* This occurs when user tries to read
* from never connected socket .
*/
len = - ENOTCONN ;
2005-08-10 07:14:34 +04:00
break ;
}
2005-08-14 03:35:17 +04:00
len = 0 ;
break ;
2005-08-10 07:14:34 +04:00
}
2005-08-14 03:35:17 +04:00
if ( ! timeo ) {
len = - EAGAIN ;
break ;
}
2005-08-10 07:14:34 +04:00
2005-08-14 03:35:17 +04:00
if ( signal_pending ( current ) ) {
len = sock_intr_errno ( timeo ) ;
break ;
}
2005-08-10 07:14:34 +04:00
2005-08-14 03:35:17 +04:00
sk_wait_data ( sk , & timeo ) ;
2005-08-10 07:14:34 +04:00
continue ;
found_ok_skb :
2005-08-14 03:35:17 +04:00
if ( len > skb - > len )
len = skb - > len ;
else if ( len < skb - > len )
msg - > msg_flags | = MSG_TRUNC ;
if ( skb_copy_datagram_iovec ( skb , 0 , msg - > msg_iov , len ) ) {
/* Exception. Bailout! */
len = - EFAULT ;
break ;
2005-08-10 07:14:34 +04:00
}
found_fin_ok :
if ( ! ( flags & MSG_PEEK ) )
2006-05-24 05:01:28 +04:00
sk_eat_skb ( sk , skb , 0 ) ;
2005-08-10 07:14:34 +04:00
break ;
2005-08-14 03:35:17 +04:00
} while ( 1 ) ;
2005-08-10 07:14:34 +04:00
out :
release_sock ( sk ) ;
2005-08-14 03:35:17 +04:00
return len ;
2005-08-10 07:14:34 +04:00
}
2005-12-14 10:24:16 +03:00
EXPORT_SYMBOL_GPL ( dccp_recvmsg ) ;
int inet_dccp_listen ( struct socket * sock , int backlog )
2005-08-10 07:14:34 +04:00
{
struct sock * sk = sock - > sk ;
unsigned char old_state ;
int err ;
lock_sock ( sk ) ;
err = - EINVAL ;
if ( sock - > state ! = SS_UNCONNECTED | | sock - > type ! = SOCK_DCCP )
goto out ;
old_state = sk - > sk_state ;
if ( ! ( ( 1 < < old_state ) & ( DCCPF_CLOSED | DCCPF_LISTEN ) ) )
goto out ;
/* Really, if the socket is already in listen state
* we can only allow the backlog to be adjusted .
*/
if ( old_state ! = DCCP_LISTEN ) {
/*
* FIXME : here it probably should be sk - > sk_prot - > listen_start
* see tcp_listen_start
*/
2006-11-16 13:30:37 +03:00
err = dccp_listen_start ( sk , backlog ) ;
2005-08-10 07:14:34 +04:00
if ( err )
goto out ;
}
sk - > sk_max_ack_backlog = backlog ;
err = 0 ;
out :
release_sock ( sk ) ;
return err ;
}
2005-12-14 10:24:16 +03:00
EXPORT_SYMBOL_GPL ( inet_dccp_listen ) ;
2005-08-10 07:14:34 +04:00
static const unsigned char dccp_new_state [ ] = {
2005-08-14 03:34:54 +04:00
/* current state: new state: action: */
[ 0 ] = DCCP_CLOSED ,
2006-12-10 21:01:18 +03:00
[ DCCP_OPEN ] = DCCP_CLOSING | DCCP_ACTION_FIN ,
2005-08-14 03:34:54 +04:00
[ DCCP_REQUESTING ] = DCCP_CLOSED ,
[ DCCP_PARTOPEN ] = DCCP_CLOSING | DCCP_ACTION_FIN ,
[ DCCP_LISTEN ] = DCCP_CLOSED ,
[ DCCP_RESPOND ] = DCCP_CLOSED ,
[ DCCP_CLOSING ] = DCCP_CLOSED ,
[ DCCP_TIME_WAIT ] = DCCP_CLOSED ,
[ DCCP_CLOSED ] = DCCP_CLOSED ,
2005-08-10 07:14:34 +04:00
} ;
static int dccp_close_state ( struct sock * sk )
{
const int next = dccp_new_state [ sk - > sk_state ] ;
const int ns = next & DCCP_STATE_MASK ;
if ( ns ! = sk - > sk_state )
dccp_set_state ( sk , ns ) ;
return next & DCCP_ACTION_FIN ;
}
void dccp_close ( struct sock * sk , long timeout )
{
2006-08-27 06:16:45 +04:00
struct dccp_sock * dp = dccp_sk ( sk ) ;
2005-08-10 07:14:34 +04:00
struct sk_buff * skb ;
2006-05-06 04:09:13 +04:00
int state ;
2005-08-10 07:14:34 +04:00
lock_sock ( sk ) ;
sk - > sk_shutdown = SHUTDOWN_MASK ;
if ( sk - > sk_state = = DCCP_LISTEN ) {
dccp_set_state ( sk , DCCP_CLOSED ) ;
/* Special case. */
inet_csk_listen_stop ( sk ) ;
goto adjudge_to_death ;
}
2006-08-27 06:16:45 +04:00
sk_stop_timer ( sk , & dp - > dccps_xmit_timer ) ;
2005-08-10 07:14:34 +04:00
/*
* We need to flush the recv . buffs . We do this only on the
* descriptor close , not protocol - sourced closes , because the
* reader process may not have drained the data yet !
*/
/* FIXME: check for unread data */
while ( ( skb = __skb_dequeue ( & sk - > sk_receive_queue ) ) ! = NULL ) {
__kfree_skb ( skb ) ;
}
if ( sock_flag ( sk , SOCK_LINGER ) & & ! sk - > sk_lingertime ) {
/* Check zero linger _after_ checking for unread data. */
sk - > sk_prot - > disconnect ( sk , 0 ) ;
} else if ( dccp_close_state ( sk ) ) {
2005-08-24 08:50:06 +04:00
dccp_send_close ( sk , 1 ) ;
2005-08-10 07:14:34 +04:00
}
sk_stream_wait_close ( sk , timeout ) ;
adjudge_to_death :
2006-05-06 04:09:13 +04:00
state = sk - > sk_state ;
sock_hold ( sk ) ;
sock_orphan ( sk ) ;
atomic_inc ( sk - > sk_prot - > orphan_count ) ;
2005-08-24 08:50:06 +04:00
/*
* It is the last release_sock in its life . It will remove backlog .
*/
2005-08-10 07:14:34 +04:00
release_sock ( sk ) ;
/*
* Now socket is owned by kernel and we acquire BH lock
* to finish close . No need to check for user refs .
*/
local_bh_disable ( ) ;
bh_lock_sock ( sk ) ;
BUG_TRAP ( ! sock_owned_by_user ( sk ) ) ;
2006-05-06 04:09:13 +04:00
/* Have we already been destroyed by a softirq or backlog? */
if ( state ! = DCCP_CLOSED & & sk - > sk_state = = DCCP_CLOSED )
goto out ;
2005-08-24 08:50:06 +04:00
/*
* The last release_sock may have processed the CLOSE or RESET
* packet moving sock to CLOSED state , if not we have to fire
* the CLOSE / CLOSEREQ retransmission timer , see " 8.3. Termination "
* in draft - ietf - dccp - spec - 11. - acme
*/
if ( sk - > sk_state = = DCCP_CLOSING ) {
/* FIXME: should start at 2 * RTT */
/* Timer for repeating the CLOSE/CLOSEREQ until an answer. */
inet_csk_reset_xmit_timer ( sk , ICSK_TIME_RETRANS ,
inet_csk ( sk ) - > icsk_rto ,
DCCP_RTO_MAX ) ;
#if 0
/* Yeah, we should use sk->sk_prot->orphan_count, etc */
2005-08-10 07:14:34 +04:00
dccp_set_state ( sk , DCCP_CLOSED ) ;
2005-08-24 08:50:06 +04:00
# endif
}
2005-08-10 07:14:34 +04:00
if ( sk - > sk_state = = DCCP_CLOSED )
inet_csk_destroy_sock ( sk ) ;
/* Otherwise, socket is reprieved until protocol close. */
2006-05-06 04:09:13 +04:00
out :
2005-08-10 07:14:34 +04:00
bh_unlock_sock ( sk ) ;
local_bh_enable ( ) ;
sock_put ( sk ) ;
}
2005-12-14 10:24:16 +03:00
EXPORT_SYMBOL_GPL ( dccp_close ) ;
2005-08-10 07:14:34 +04:00
void dccp_shutdown ( struct sock * sk , int how )
{
dccp_pr_debug ( " entry \n " ) ;
}
2005-12-14 10:24:16 +03:00
EXPORT_SYMBOL_GPL ( dccp_shutdown ) ;
2006-03-21 08:24:42 +03:00
static int __init dccp_mib_init ( void )
2005-08-10 07:14:34 +04:00
{
int rc = - ENOMEM ;
dccp_statistics [ 0 ] = alloc_percpu ( struct dccp_mib ) ;
if ( dccp_statistics [ 0 ] = = NULL )
goto out ;
dccp_statistics [ 1 ] = alloc_percpu ( struct dccp_mib ) ;
if ( dccp_statistics [ 1 ] = = NULL )
goto out_free_one ;
rc = 0 ;
out :
return rc ;
out_free_one :
free_percpu ( dccp_statistics [ 0 ] ) ;
dccp_statistics [ 0 ] = NULL ;
goto out ;
}
2006-03-21 08:25:11 +03:00
static void dccp_mib_exit ( void )
2006-03-21 08:24:42 +03:00
{
free_percpu ( dccp_statistics [ 0 ] ) ;
free_percpu ( dccp_statistics [ 1 ] ) ;
dccp_statistics [ 0 ] = dccp_statistics [ 1 ] = NULL ;
}
2005-08-10 07:14:34 +04:00
static int thash_entries ;
module_param ( thash_entries , int , 0444 ) ;
MODULE_PARM_DESC ( thash_entries , " Number of ehash buckets " ) ;
2005-08-14 05:42:25 +04:00
# ifdef CONFIG_IP_DCCP_DEBUG
2005-08-10 07:14:34 +04:00
int dccp_debug ;
module_param ( dccp_debug , int , 0444 ) ;
MODULE_PARM_DESC ( dccp_debug , " Enable debug messages " ) ;
2005-12-14 10:24:16 +03:00
EXPORT_SYMBOL_GPL ( dccp_debug ) ;
2005-08-14 05:42:25 +04:00
# endif
2005-08-10 07:14:34 +04:00
static int __init dccp_init ( void )
{
unsigned long goal ;
int ehash_order , bhash_order , i ;
2006-03-21 08:25:11 +03:00
int rc = - ENOBUFS ;
2005-08-10 07:14:34 +04:00
2005-08-14 03:34:54 +04:00
dccp_hashinfo . bind_bucket_cachep =
kmem_cache_create ( " dccp_bind_bucket " ,
sizeof ( struct inet_bind_bucket ) , 0 ,
SLAB_HWCACHE_ALIGN , NULL , NULL ) ;
2005-08-10 07:14:34 +04:00
if ( ! dccp_hashinfo . bind_bucket_cachep )
2006-03-21 08:25:11 +03:00
goto out ;
2005-08-10 07:14:34 +04:00
/*
* Size and allocate the main established and bind bucket
* hash tables .
*
* The methodology is similar to that of the buffer cache .
*/
if ( num_physpages > = ( 128 * 1024 ) )
goal = num_physpages > > ( 21 - PAGE_SHIFT ) ;
else
goal = num_physpages > > ( 23 - PAGE_SHIFT ) ;
if ( thash_entries )
2005-08-14 03:34:54 +04:00
goal = ( thash_entries *
sizeof ( struct inet_ehash_bucket ) ) > > PAGE_SHIFT ;
2005-08-10 07:14:34 +04:00
for ( ehash_order = 0 ; ( 1UL < < ehash_order ) < goal ; ehash_order + + )
;
do {
dccp_hashinfo . ehash_size = ( 1UL < < ehash_order ) * PAGE_SIZE /
sizeof ( struct inet_ehash_bucket ) ;
dccp_hashinfo . ehash_size > > = 1 ;
2005-08-14 03:34:54 +04:00
while ( dccp_hashinfo . ehash_size &
( dccp_hashinfo . ehash_size - 1 ) )
2005-08-10 07:14:34 +04:00
dccp_hashinfo . ehash_size - - ;
dccp_hashinfo . ehash = ( struct inet_ehash_bucket * )
__get_free_pages ( GFP_ATOMIC , ehash_order ) ;
} while ( ! dccp_hashinfo . ehash & & - - ehash_order > 0 ) ;
if ( ! dccp_hashinfo . ehash ) {
2006-11-20 23:39:23 +03:00
DCCP_CRIT ( " Failed to allocate DCCP established hash table " ) ;
2005-08-10 07:14:34 +04:00
goto out_free_bind_bucket_cachep ;
}
for ( i = 0 ; i < ( dccp_hashinfo . ehash_size < < 1 ) ; i + + ) {
rwlock_init ( & dccp_hashinfo . ehash [ i ] . lock ) ;
INIT_HLIST_HEAD ( & dccp_hashinfo . ehash [ i ] . chain ) ;
}
bhash_order = ehash_order ;
do {
dccp_hashinfo . bhash_size = ( 1UL < < bhash_order ) * PAGE_SIZE /
sizeof ( struct inet_bind_hashbucket ) ;
2005-08-14 03:34:54 +04:00
if ( ( dccp_hashinfo . bhash_size > ( 64 * 1024 ) ) & &
bhash_order > 0 )
2005-08-10 07:14:34 +04:00
continue ;
dccp_hashinfo . bhash = ( struct inet_bind_hashbucket * )
__get_free_pages ( GFP_ATOMIC , bhash_order ) ;
} while ( ! dccp_hashinfo . bhash & & - - bhash_order > = 0 ) ;
if ( ! dccp_hashinfo . bhash ) {
2006-11-20 23:39:23 +03:00
DCCP_CRIT ( " Failed to allocate DCCP bind hash table " ) ;
2005-08-10 07:14:34 +04:00
goto out_free_dccp_ehash ;
}
for ( i = 0 ; i < dccp_hashinfo . bhash_size ; i + + ) {
spin_lock_init ( & dccp_hashinfo . bhash [ i ] . lock ) ;
INIT_HLIST_HEAD ( & dccp_hashinfo . bhash [ i ] . chain ) ;
}
2006-03-21 08:24:42 +03:00
rc = dccp_mib_init ( ) ;
2006-03-21 04:16:01 +03:00
if ( rc )
2005-08-10 07:14:34 +04:00
goto out_free_dccp_bhash ;
2006-03-21 04:16:17 +03:00
rc = dccp_ackvec_init ( ) ;
2005-08-10 07:14:34 +04:00
if ( rc )
2006-03-21 08:25:11 +03:00
goto out_free_dccp_mib ;
2006-03-21 04:16:17 +03:00
2006-03-21 06:25:02 +03:00
rc = dccp_sysctl_init ( ) ;
2006-03-21 04:16:17 +03:00
if ( rc )
goto out_ackvec_exit ;
2005-08-10 07:14:34 +04:00
out :
return rc ;
2006-03-21 04:16:17 +03:00
out_ackvec_exit :
dccp_ackvec_exit ( ) ;
2006-03-21 08:25:11 +03:00
out_free_dccp_mib :
2006-03-21 08:24:42 +03:00
dccp_mib_exit ( ) ;
2005-08-10 07:14:34 +04:00
out_free_dccp_bhash :
free_pages ( ( unsigned long ) dccp_hashinfo . bhash , bhash_order ) ;
dccp_hashinfo . bhash = NULL ;
out_free_dccp_ehash :
free_pages ( ( unsigned long ) dccp_hashinfo . ehash , ehash_order ) ;
dccp_hashinfo . ehash = NULL ;
out_free_bind_bucket_cachep :
kmem_cache_destroy ( dccp_hashinfo . bind_bucket_cachep ) ;
dccp_hashinfo . bind_bucket_cachep = NULL ;
goto out ;
}
static void __exit dccp_fini ( void )
{
2006-03-21 08:24:42 +03:00
dccp_mib_exit ( ) ;
2005-08-14 03:35:39 +04:00
free_pages ( ( unsigned long ) dccp_hashinfo . bhash ,
get_order ( dccp_hashinfo . bhash_size *
sizeof ( struct inet_bind_hashbucket ) ) ) ;
free_pages ( ( unsigned long ) dccp_hashinfo . ehash ,
get_order ( dccp_hashinfo . ehash_size *
sizeof ( struct inet_ehash_bucket ) ) ) ;
2005-08-10 07:14:34 +04:00
kmem_cache_destroy ( dccp_hashinfo . bind_bucket_cachep ) ;
2006-03-21 04:16:17 +03:00
dccp_ackvec_exit ( ) ;
2006-03-21 06:25:02 +03:00
dccp_sysctl_exit ( ) ;
2005-08-10 07:14:34 +04:00
}
module_init ( dccp_init ) ;
module_exit ( dccp_fini ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_AUTHOR ( " Arnaldo Carvalho de Melo <acme@conectiva.com.br> " ) ;
MODULE_DESCRIPTION ( " DCCP - Datagram Congestion Controlled Protocol " ) ;