2005-08-10 07:14:34 +04:00
/*
* net / dccp / minisocks . 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
* as published by the Free Software Foundation ; either version
* 2 of the License , or ( at your option ) any later version .
*/
# include <linux/config.h>
# include <linux/dccp.h>
# include <linux/skbuff.h>
# include <linux/timer.h>
# include <net/sock.h>
# include <net/xfrm.h>
# include <net/inet_timewait_sock.h>
# include "ccid.h"
# include "dccp.h"
2005-08-10 07:45:21 +04:00
struct inet_timewait_death_row dccp_death_row = {
. sysctl_max_tw_buckets = NR_FILE * 2 ,
. period = DCCP_TIMEWAIT_LEN / INET_TWDR_TWKILL_SLOTS ,
. death_lock = SPIN_LOCK_UNLOCKED ,
. hashinfo = & dccp_hashinfo ,
. tw_timer = TIMER_INITIALIZER ( inet_twdr_hangman , 0 ,
( unsigned long ) & dccp_death_row ) ,
. twkill_work = __WORK_INITIALIZER ( dccp_death_row . twkill_work ,
inet_twdr_twkill_work ,
& dccp_death_row ) ,
/* Short-time timewait calendar */
. twcal_hand = - 1 ,
. twcal_timer = TIMER_INITIALIZER ( inet_twdr_twcal_tick , 0 ,
( unsigned long ) & dccp_death_row ) ,
} ;
2005-08-10 07:14:34 +04:00
void dccp_time_wait ( struct sock * sk , int state , int timeo )
{
2005-08-10 07:45:21 +04:00
struct inet_timewait_sock * tw = NULL ;
2005-08-10 07:14:34 +04:00
2005-08-10 07:45:21 +04:00
if ( dccp_death_row . tw_count < dccp_death_row . sysctl_max_tw_buckets )
tw = inet_twsk_alloc ( sk , state ) ;
if ( tw ! = NULL ) {
const struct inet_connection_sock * icsk = inet_csk ( sk ) ;
const int rto = ( icsk - > icsk_rto < < 2 ) - ( icsk - > icsk_rto > > 1 ) ;
/* Linkage updates. */
__inet_twsk_hashdance ( tw , sk , & dccp_hashinfo ) ;
/* Get the TIME_WAIT timeout firing. */
if ( timeo < rto )
timeo = rto ;
tw - > tw_timeout = DCCP_TIMEWAIT_LEN ;
if ( state = = DCCP_TIME_WAIT )
timeo = DCCP_TIMEWAIT_LEN ;
inet_twsk_schedule ( tw , & dccp_death_row , timeo ,
DCCP_TIMEWAIT_LEN ) ;
inet_twsk_put ( tw ) ;
} else {
/* Sorry, if we're out of memory, just CLOSE this
* socket up . We ' ve got bigger problems than
* non - graceful socket closings .
*/
2005-08-14 03:34:54 +04:00
LIMIT_NETDEBUG ( KERN_INFO " DCCP: time wait bucket "
" table overflow \n " ) ;
2005-08-10 07:45:21 +04:00
}
dccp_done ( sk ) ;
2005-08-10 07:14:34 +04:00
}
struct sock * dccp_create_openreq_child ( struct sock * sk ,
const struct request_sock * req ,
const struct sk_buff * skb )
{
/*
* Step 3 : Process LISTEN state
*
* // Generate a new socket and switch to that socket
* Set S : = new socket for this port pair
*/
struct sock * newsk = inet_csk_clone ( sk , req , GFP_ATOMIC ) ;
if ( newsk ! = NULL ) {
const struct dccp_request_sock * dreq = dccp_rsk ( req ) ;
struct inet_connection_sock * newicsk = inet_csk ( sk ) ;
struct dccp_sock * newdp = dccp_sk ( newsk ) ;
newdp - > dccps_hc_rx_ackpkts = NULL ;
newdp - > dccps_role = DCCP_ROLE_SERVER ;
2005-08-10 07:45:21 +04:00
newicsk - > icsk_rto = DCCP_TIMEOUT_INIT ;
2005-08-10 07:14:34 +04:00
if ( newdp - > dccps_options . dccpo_send_ack_vector ) {
2005-08-14 03:34:54 +04:00
newdp - > dccps_hc_rx_ackpkts =
dccp_ackpkts_alloc ( DCCP_MAX_ACK_VECTOR_LEN ,
GFP_ATOMIC ) ;
2005-08-10 07:14:34 +04:00
/*
2005-08-14 03:34:54 +04:00
* XXX : We ' re using the same CCIDs set on the parent ,
* i . e . sk_clone copied the master sock and left the
* CCID pointers for this child , that is why we do the
* __ccid_get calls .
2005-08-10 07:14:34 +04:00
*/
if ( unlikely ( newdp - > dccps_hc_rx_ackpkts = = NULL ) )
goto out_free ;
}
2005-08-14 03:34:54 +04:00
if ( unlikely ( ccid_hc_rx_init ( newdp - > dccps_hc_rx_ccid ,
newsk ) ! = 0 | |
ccid_hc_tx_init ( newdp - > dccps_hc_tx_ccid ,
newsk ) ! = 0 ) ) {
2005-08-10 07:14:34 +04:00
dccp_ackpkts_free ( newdp - > dccps_hc_rx_ackpkts ) ;
ccid_hc_rx_exit ( newdp - > dccps_hc_rx_ccid , newsk ) ;
ccid_hc_tx_exit ( newdp - > dccps_hc_tx_ccid , newsk ) ;
out_free :
/* It is still raw copy of parent, so invalidate
* destructor and make plain sk_free ( ) */
newsk - > sk_destruct = NULL ;
sk_free ( newsk ) ;
return NULL ;
}
__ccid_get ( newdp - > dccps_hc_rx_ccid ) ;
__ccid_get ( newdp - > dccps_hc_tx_ccid ) ;
/*
* Step 3 : Process LISTEN state
*
* Choose S . ISS ( initial seqno ) or set from Init Cookie
2005-08-14 03:34:54 +04:00
* Set S . ISR , S . GSR , S . SWL , S . SWH from packet or Init
* Cookie
2005-08-10 07:14:34 +04:00
*/
/* See dccp_v4_conn_request */
newdp - > dccps_options . dccpo_sequence_window = req - > rcv_wnd ;
newdp - > dccps_gar = newdp - > dccps_isr = dreq - > dreq_isr ;
dccp_update_gsr ( newsk , dreq - > dreq_isr ) ;
newdp - > dccps_iss = dreq - > dreq_iss ;
dccp_update_gss ( newsk , dreq - > dreq_iss ) ;
dccp_init_xmit_timers ( newsk ) ;
DCCP_INC_STATS_BH ( DCCP_MIB_PASSIVEOPENS ) ;
}
return newsk ;
}
/*
* Process an incoming packet for RESPOND sockets represented
* as an request_sock .
*/
struct sock * dccp_check_req ( struct sock * sk , struct sk_buff * skb ,
struct request_sock * req ,
struct request_sock * * prev )
{
struct sock * child = NULL ;
/* Check for retransmitted REQUEST */
if ( dccp_hdr ( skb ) - > dccph_type = = DCCP_PKT_REQUEST ) {
2005-08-14 03:34:54 +04:00
if ( after48 ( DCCP_SKB_CB ( skb ) - > dccpd_seq ,
dccp_rsk ( req ) - > dreq_isr ) ) {
2005-08-10 07:14:34 +04:00
struct dccp_request_sock * dreq = dccp_rsk ( req ) ;
dccp_pr_debug ( " Retransmitted REQUEST \n " ) ;
/* Send another RESPONSE packet */
dccp_set_seqno ( & dreq - > dreq_iss , dreq - > dreq_iss + 1 ) ;
2005-08-14 03:34:54 +04:00
dccp_set_seqno ( & dreq - > dreq_isr ,
DCCP_SKB_CB ( skb ) - > dccpd_seq ) ;
2005-08-10 07:14:34 +04:00
req - > rsk_ops - > rtx_syn_ack ( sk , req , NULL ) ;
}
/* Network Duplicate, discard packet */
return NULL ;
}
DCCP_SKB_CB ( skb ) - > dccpd_reset_code = DCCP_RESET_CODE_PACKET_ERROR ;
if ( dccp_hdr ( skb ) - > dccph_type ! = DCCP_PKT_ACK & &
dccp_hdr ( skb ) - > dccph_type ! = DCCP_PKT_DATAACK )
goto drop ;
/* Invalid ACK */
if ( DCCP_SKB_CB ( skb ) - > dccpd_ack_seq ! = dccp_rsk ( req ) - > dreq_iss ) {
2005-08-14 03:34:54 +04:00
dccp_pr_debug ( " Invalid ACK number: ack_seq=%llu, "
" dreq_iss=%llu \n " ,
2005-08-10 07:27:14 +04:00
( unsigned long long )
DCCP_SKB_CB ( skb ) - > dccpd_ack_seq ,
( unsigned long long )
dccp_rsk ( req ) - > dreq_iss ) ;
2005-08-10 07:14:34 +04:00
goto drop ;
}
child = dccp_v4_request_recv_sock ( sk , skb , req , NULL ) ;
if ( child = = NULL )
goto listen_overflow ;
/* FIXME: deal with options */
inet_csk_reqsk_queue_unlink ( sk , req , prev ) ;
inet_csk_reqsk_queue_removed ( sk , req ) ;
inet_csk_reqsk_queue_add ( sk , req , child ) ;
out :
return child ;
listen_overflow :
dccp_pr_debug ( " listen_overflow! \n " ) ;
DCCP_SKB_CB ( skb ) - > dccpd_reset_code = DCCP_RESET_CODE_TOO_BUSY ;
drop :
if ( dccp_hdr ( skb ) - > dccph_type ! = DCCP_PKT_RESET )
req - > rsk_ops - > send_reset ( skb ) ;
inet_csk_reqsk_queue_drop ( sk , req , prev ) ;
goto out ;
}
/*
* Queue segment on the new socket if the new socket is active ,
* otherwise we just shortcircuit this and continue with
* the new socket .
*/
int dccp_child_process ( struct sock * parent , struct sock * child ,
struct sk_buff * skb )
{
int ret = 0 ;
const int state = child - > sk_state ;
if ( ! sock_owned_by_user ( child ) ) {
2005-08-14 03:34:54 +04:00
ret = dccp_rcv_state_process ( child , skb , dccp_hdr ( skb ) ,
skb - > len ) ;
2005-08-10 07:14:34 +04:00
/* Wakeup parent, send SIGIO */
if ( state = = DCCP_RESPOND & & child - > sk_state ! = state )
parent - > sk_data_ready ( parent , 0 ) ;
} else {
/* Alas, it is possible again, because we do lookup
* in main socket hash table and lock on listening
* socket does not protect us more .
*/
sk_add_backlog ( child , skb ) ;
}
bh_unlock_sock ( child ) ;
sock_put ( child ) ;
return ret ;
}