2005-08-10 07:14:34 +04:00
/*
* net / dccp / ipv4 . 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/icmp.h>
# include <linux/module.h>
# include <linux/skbuff.h>
# include <linux/random.h>
# include <net/icmp.h>
# include <net/inet_hashtables.h>
# include <net/sock.h>
# include <net/tcp_states.h>
# include <net/xfrm.h>
# include "ccid.h"
# include "dccp.h"
struct inet_hashinfo __cacheline_aligned dccp_hashinfo = {
. lhash_lock = RW_LOCK_UNLOCKED ,
. lhash_users = ATOMIC_INIT ( 0 ) ,
2005-08-14 03:34:54 +04:00
. lhash_wait = __WAIT_QUEUE_HEAD_INITIALIZER ( dccp_hashinfo . lhash_wait ) ,
2005-08-10 07:14:34 +04:00
. portalloc_lock = SPIN_LOCK_UNLOCKED ,
. port_rover = 1024 - 1 ,
} ;
2005-08-10 12:54:28 +04:00
EXPORT_SYMBOL_GPL ( dccp_hashinfo ) ;
2005-08-10 07:14:34 +04:00
static int dccp_v4_get_port ( struct sock * sk , const unsigned short snum )
{
return inet_csk_get_port ( & dccp_hashinfo , sk , snum ) ;
}
static void dccp_v4_hash ( struct sock * sk )
{
inet_hash ( & dccp_hashinfo , sk ) ;
}
static void dccp_v4_unhash ( struct sock * sk )
{
inet_unhash ( & dccp_hashinfo , sk ) ;
}
/* called with local bh disabled */
static int __dccp_v4_check_established ( struct sock * sk , const __u16 lport ,
struct inet_timewait_sock * * twp )
{
struct inet_sock * inet = inet_sk ( sk ) ;
const u32 daddr = inet - > rcv_saddr ;
const u32 saddr = inet - > daddr ;
const int dif = sk - > sk_bound_dev_if ;
INET_ADDR_COOKIE ( acookie , saddr , daddr )
const __u32 ports = INET_COMBINED_PORTS ( inet - > dport , lport ) ;
2005-08-14 03:34:54 +04:00
const int hash = inet_ehashfn ( daddr , lport , saddr , inet - > dport ,
dccp_hashinfo . ehash_size ) ;
2005-08-10 07:14:34 +04:00
struct inet_ehash_bucket * head = & dccp_hashinfo . ehash [ hash ] ;
const struct sock * sk2 ;
const struct hlist_node * node ;
struct inet_timewait_sock * tw ;
write_lock ( & head - > lock ) ;
/* Check TIME-WAIT sockets first. */
sk_for_each ( sk2 , node , & ( head + dccp_hashinfo . ehash_size ) - > chain ) {
tw = inet_twsk ( sk2 ) ;
if ( INET_TW_MATCH ( sk2 , acookie , saddr , daddr , ports , dif ) )
goto not_unique ;
}
tw = NULL ;
/* And established part... */
sk_for_each ( sk2 , node , & head - > chain ) {
if ( INET_MATCH ( sk2 , acookie , saddr , daddr , ports , dif ) )
goto not_unique ;
}
/* Must record num and sport now. Otherwise we will see
* in hash table socket with a funny identity . */
inet - > num = lport ;
inet - > sport = htons ( lport ) ;
sk - > sk_hashent = hash ;
BUG_TRAP ( sk_unhashed ( sk ) ) ;
__sk_add_node ( sk , & head - > chain ) ;
sock_prot_inc_use ( sk - > sk_prot ) ;
write_unlock ( & head - > lock ) ;
if ( twp ! = NULL ) {
* twp = tw ;
NET_INC_STATS_BH ( LINUX_MIB_TIMEWAITRECYCLED ) ;
} else if ( tw ! = NULL ) {
/* Silly. Should hash-dance instead... */
2005-08-10 07:45:21 +04:00
inet_twsk_deschedule ( tw , & dccp_death_row ) ;
2005-08-10 07:14:34 +04:00
NET_INC_STATS_BH ( LINUX_MIB_TIMEWAITRECYCLED ) ;
inet_twsk_put ( tw ) ;
}
return 0 ;
not_unique :
write_unlock ( & head - > lock ) ;
return - EADDRNOTAVAIL ;
}
/*
* Bind a port for a connect operation and hash it .
*/
static int dccp_v4_hash_connect ( struct sock * sk )
{
const unsigned short snum = inet_sk ( sk ) - > num ;
struct inet_bind_hashbucket * head ;
struct inet_bind_bucket * tb ;
int ret ;
if ( snum = = 0 ) {
int rover ;
int low = sysctl_local_port_range [ 0 ] ;
int high = sysctl_local_port_range [ 1 ] ;
int remaining = ( high - low ) + 1 ;
struct hlist_node * node ;
struct inet_timewait_sock * tw = NULL ;
local_bh_disable ( ) ;
/* TODO. Actually it is not so bad idea to remove
2005-08-14 03:34:54 +04:00
* dccp_hashinfo . portalloc_lock before next submission to
* Linus .
2005-08-10 07:14:34 +04:00
* As soon as we touch this place at all it is time to think .
*
2005-08-14 03:34:54 +04:00
* Now it protects single _advisory_ variable
* dccp_hashinfo . port_rover , hence it is mostly useless .
2005-08-10 07:14:34 +04:00
* Code will work nicely if we just delete it , but
* I am afraid in contented case it will work not better or
* even worse : another cpu just will hit the same bucket
* and spin there .
* So some cpu salt could remove both contention and
* memory pingpong . Any ideas how to do this in a nice way ?
*/
spin_lock ( & dccp_hashinfo . portalloc_lock ) ;
rover = dccp_hashinfo . port_rover ;
do {
rover + + ;
if ( ( rover < low ) | | ( rover > high ) )
rover = low ;
2005-08-14 03:34:54 +04:00
head = & dccp_hashinfo . bhash [ inet_bhashfn ( rover ,
dccp_hashinfo . bhash_size ) ] ;
2005-08-10 07:14:34 +04:00
spin_lock ( & head - > lock ) ;
/* Does not bother with rcv_saddr checks,
* because the established check is already
* unique enough .
*/
inet_bind_bucket_for_each ( tb , node , & head - > chain ) {
if ( tb - > port = = rover ) {
BUG_TRAP ( ! hlist_empty ( & tb - > owners ) ) ;
if ( tb - > fastreuse > = 0 )
goto next_port ;
if ( ! __dccp_v4_check_established ( sk ,
rover ,
& tw ) )
goto ok ;
goto next_port ;
}
}
2005-08-14 03:34:54 +04:00
tb = inet_bind_bucket_create ( dccp_hashinfo . bind_bucket_cachep ,
head , rover ) ;
2005-08-10 07:14:34 +04:00
if ( tb = = NULL ) {
spin_unlock ( & head - > lock ) ;
break ;
}
tb - > fastreuse = - 1 ;
goto ok ;
next_port :
spin_unlock ( & head - > lock ) ;
} while ( - - remaining > 0 ) ;
dccp_hashinfo . port_rover = rover ;
spin_unlock ( & dccp_hashinfo . portalloc_lock ) ;
local_bh_enable ( ) ;
return - EADDRNOTAVAIL ;
ok :
/* All locks still held and bhs disabled */
dccp_hashinfo . port_rover = rover ;
spin_unlock ( & dccp_hashinfo . portalloc_lock ) ;
inet_bind_hash ( sk , tb , rover ) ;
if ( sk_unhashed ( sk ) ) {
inet_sk ( sk ) - > sport = htons ( rover ) ;
__inet_hash ( & dccp_hashinfo , sk , 0 ) ;
}
spin_unlock ( & head - > lock ) ;
if ( tw ! = NULL ) {
2005-08-10 07:45:21 +04:00
inet_twsk_deschedule ( tw , & dccp_death_row ) ;
2005-08-10 07:14:34 +04:00
inet_twsk_put ( tw ) ;
}
ret = 0 ;
goto out ;
}
2005-08-14 03:34:54 +04:00
head = & dccp_hashinfo . bhash [ inet_bhashfn ( snum ,
dccp_hashinfo . bhash_size ) ] ;
2005-08-10 07:14:34 +04:00
tb = inet_csk ( sk ) - > icsk_bind_hash ;
spin_lock_bh ( & head - > lock ) ;
if ( sk_head ( & tb - > owners ) = = sk & & sk - > sk_bind_node . next = = NULL ) {
__inet_hash ( & dccp_hashinfo , sk , 0 ) ;
spin_unlock_bh ( & head - > lock ) ;
return 0 ;
} else {
spin_unlock ( & head - > lock ) ;
/* No definite answer... Walk to established hash table */
ret = __dccp_v4_check_established ( sk , snum , NULL ) ;
out :
local_bh_enable ( ) ;
return ret ;
}
}
static int dccp_v4_connect ( struct sock * sk , struct sockaddr * uaddr ,
int addr_len )
{
struct inet_sock * inet = inet_sk ( sk ) ;
struct dccp_sock * dp = dccp_sk ( sk ) ;
const struct sockaddr_in * usin = ( struct sockaddr_in * ) uaddr ;
struct rtable * rt ;
u32 daddr , nexthop ;
int tmp ;
int err ;
dp - > dccps_role = DCCP_ROLE_CLIENT ;
if ( addr_len < sizeof ( struct sockaddr_in ) )
return - EINVAL ;
if ( usin - > sin_family ! = AF_INET )
return - EAFNOSUPPORT ;
nexthop = daddr = usin - > sin_addr . s_addr ;
if ( inet - > opt ! = NULL & & inet - > opt - > srr ) {
if ( daddr = = 0 )
return - EINVAL ;
nexthop = inet - > opt - > faddr ;
}
tmp = ip_route_connect ( & rt , nexthop , inet - > saddr ,
RT_CONN_FLAGS ( sk ) , sk - > sk_bound_dev_if ,
IPPROTO_DCCP ,
inet - > sport , usin - > sin_port , sk ) ;
if ( tmp < 0 )
return tmp ;
if ( rt - > rt_flags & ( RTCF_MULTICAST | RTCF_BROADCAST ) ) {
ip_rt_put ( rt ) ;
return - ENETUNREACH ;
}
if ( inet - > opt = = NULL | | ! inet - > opt - > srr )
daddr = rt - > rt_dst ;
if ( inet - > saddr = = 0 )
inet - > saddr = rt - > rt_src ;
inet - > rcv_saddr = inet - > saddr ;
inet - > dport = usin - > sin_port ;
inet - > daddr = daddr ;
dp - > dccps_ext_header_len = 0 ;
if ( inet - > opt ! = NULL )
dp - > dccps_ext_header_len = inet - > opt - > optlen ;
/*
* Socket identity is still unknown ( sport may be zero ) .
* However we set state to DCCP_REQUESTING and not releasing socket
* lock select source port , enter ourselves into the hash tables and
* complete initialization after this .
*/
dccp_set_state ( sk , DCCP_REQUESTING ) ;
err = dccp_v4_hash_connect ( sk ) ;
if ( err ! = 0 )
goto failure ;
err = ip_route_newports ( & rt , inet - > sport , inet - > dport , sk ) ;
if ( err ! = 0 )
goto failure ;
/* OK, now commit destination to socket. */
sk_setup_caps ( sk , & rt - > u . dst ) ;
dp - > dccps_gar =
dp - > dccps_iss = secure_dccp_sequence_number ( inet - > saddr ,
inet - > daddr ,
inet - > sport ,
usin - > sin_port ) ;
dccp_update_gss ( sk , dp - > dccps_iss ) ;
2005-08-21 12:36:45 +04:00
/*
* SWL and AWL are initially adjusted so that they are not less than
* the initial Sequence Numbers received and sent , respectively :
* SWL : = max ( GSR + 1 - floor ( W / 4 ) , ISR ) ,
* AWL : = max ( GSS - W ' + 1 , ISS ) .
* These adjustments MUST be applied only at the beginning of the
* connection .
*/
dccp_set_seqno ( & dp - > dccps_awl , max48 ( dp - > dccps_awl , dp - > dccps_iss ) ) ;
2005-08-10 07:14:34 +04:00
inet - > id = dp - > dccps_iss ^ jiffies ;
err = dccp_connect ( sk ) ;
rt = NULL ;
if ( err ! = 0 )
goto failure ;
out :
return err ;
failure :
2005-08-14 03:34:54 +04:00
/*
* This unhashes the socket and releases the local port , if necessary .
*/
2005-08-10 07:14:34 +04:00
dccp_set_state ( sk , DCCP_CLOSED ) ;
ip_rt_put ( rt ) ;
sk - > sk_route_caps = 0 ;
inet - > dport = 0 ;
goto out ;
}
/*
* This routine does path mtu discovery as defined in RFC1191 .
*/
static inline void dccp_do_pmtu_discovery ( struct sock * sk ,
const struct iphdr * iph ,
u32 mtu )
{
struct dst_entry * dst ;
const struct inet_sock * inet = inet_sk ( sk ) ;
const struct dccp_sock * dp = dccp_sk ( sk ) ;
/* We are not interested in DCCP_LISTEN and request_socks (RESPONSEs
* send out by Linux are always < 576 bytes so they should go through
* unfragmented ) .
*/
if ( sk - > sk_state = = DCCP_LISTEN )
return ;
/* We don't check in the destentry if pmtu discovery is forbidden
* on this route . We just assume that no packet_to_big packets
* are send back when pmtu discovery is not active .
* There is a small race when the user changes this flag in the
* route , but I think that ' s acceptable .
*/
if ( ( dst = __sk_dst_check ( sk , 0 ) ) = = NULL )
return ;
dst - > ops - > update_pmtu ( dst , mtu ) ;
/* Something is about to be wrong... Remember soft error
* for the case , if this connection will not able to recover .
*/
if ( mtu < dst_mtu ( dst ) & & ip_dont_fragment ( sk , dst ) )
sk - > sk_err_soft = EMSGSIZE ;
mtu = dst_mtu ( dst ) ;
if ( inet - > pmtudisc ! = IP_PMTUDISC_DONT & &
dp - > dccps_pmtu_cookie > mtu ) {
dccp_sync_mss ( sk , mtu ) ;
/*
* From : draft - ietf - dccp - spec - 11. txt
*
2005-08-14 03:34:54 +04:00
* DCCP - Sync packets are the best choice for upward
* probing , since DCCP - Sync probes do not risk application
* data loss .
2005-08-10 07:14:34 +04:00
*/
2005-08-17 10:10:59 +04:00
dccp_send_sync ( sk , dp - > dccps_gsr , DCCP_PKT_SYNC ) ;
2005-08-10 07:14:34 +04:00
} /* else let the usual retransmit timer handle it */
}
static void dccp_v4_ctl_send_ack ( struct sk_buff * rxskb )
{
int err ;
struct dccp_hdr * rxdh = dccp_hdr ( rxskb ) , * dh ;
const int dccp_hdr_ack_len = sizeof ( struct dccp_hdr ) +
sizeof ( struct dccp_hdr_ext ) +
sizeof ( struct dccp_hdr_ack_bits ) ;
struct sk_buff * skb ;
if ( ( ( struct rtable * ) rxskb - > dst ) - > rt_type ! = RTN_LOCAL )
return ;
skb = alloc_skb ( MAX_DCCP_HEADER + 15 , GFP_ATOMIC ) ;
if ( skb = = NULL )
return ;
/* Reserve space for headers. */
skb_reserve ( skb , MAX_DCCP_HEADER ) ;
skb - > dst = dst_clone ( rxskb - > dst ) ;
skb - > h . raw = skb_push ( skb , dccp_hdr_ack_len ) ;
dh = dccp_hdr ( skb ) ;
memset ( dh , 0 , dccp_hdr_ack_len ) ;
/* Build DCCP header and checksum it. */
dh - > dccph_type = DCCP_PKT_ACK ;
dh - > dccph_sport = rxdh - > dccph_dport ;
dh - > dccph_dport = rxdh - > dccph_sport ;
dh - > dccph_doff = dccp_hdr_ack_len / 4 ;
dh - > dccph_x = 1 ;
dccp_hdr_set_seq ( dh , DCCP_SKB_CB ( rxskb ) - > dccpd_ack_seq ) ;
2005-08-14 03:34:54 +04:00
dccp_hdr_set_ack ( dccp_hdr_ack_bits ( skb ) ,
DCCP_SKB_CB ( rxskb ) - > dccpd_seq ) ;
2005-08-10 07:14:34 +04:00
bh_lock_sock ( dccp_ctl_socket - > sk ) ;
err = ip_build_and_send_pkt ( skb , dccp_ctl_socket - > sk ,
2005-08-14 03:34:54 +04:00
rxskb - > nh . iph - > daddr ,
rxskb - > nh . iph - > saddr , NULL ) ;
2005-08-10 07:14:34 +04:00
bh_unlock_sock ( dccp_ctl_socket - > sk ) ;
if ( err = = NET_XMIT_CN | | err = = 0 ) {
DCCP_INC_STATS_BH ( DCCP_MIB_OUTSEGS ) ;
DCCP_INC_STATS_BH ( DCCP_MIB_OUTRSTS ) ;
}
}
2005-08-14 03:34:54 +04:00
static void dccp_v4_reqsk_send_ack ( struct sk_buff * skb ,
struct request_sock * req )
2005-08-10 07:14:34 +04:00
{
dccp_v4_ctl_send_ack ( skb ) ;
}
static int dccp_v4_send_response ( struct sock * sk , struct request_sock * req ,
struct dst_entry * dst )
{
int err = - 1 ;
struct sk_buff * skb ;
/* First, grab a route. */
if ( dst = = NULL & & ( dst = inet_csk_route_req ( sk , req ) ) = = NULL )
goto out ;
skb = dccp_make_response ( sk , dst , req ) ;
if ( skb ! = NULL ) {
const struct inet_request_sock * ireq = inet_rsk ( req ) ;
err = ip_build_and_send_pkt ( skb , sk , ireq - > loc_addr ,
ireq - > rmt_addr ,
ireq - > opt ) ;
if ( err = = NET_XMIT_CN )
err = 0 ;
}
out :
dst_release ( dst ) ;
return err ;
}
/*
* This routine is called by the ICMP module when it gets some sort of error
* condition . If err < 0 then the socket should be closed and the error
* returned to the user . If err > 0 it ' s just the icmp type < < 8 | icmp code .
* After adjustment header points to the first 8 bytes of the tcp header . We
* need to find the appropriate port .
*
* The locking strategy used here is very " optimistic " . When someone else
* accesses the socket the ICMP is just dropped and for some paths there is no
* check at all . A more general error queue to queue errors for later handling
* is probably better .
*/
void dccp_v4_err ( struct sk_buff * skb , u32 info )
{
const struct iphdr * iph = ( struct iphdr * ) skb - > data ;
2005-08-14 03:34:54 +04:00
const struct dccp_hdr * dh = ( struct dccp_hdr * ) ( skb - > data +
( iph - > ihl < < 2 ) ) ;
2005-08-10 07:14:34 +04:00
struct dccp_sock * dp ;
struct inet_sock * inet ;
const int type = skb - > h . icmph - > type ;
const int code = skb - > h . icmph - > code ;
struct sock * sk ;
__u64 seq ;
int err ;
if ( skb - > len < ( iph - > ihl < < 2 ) + 8 ) {
ICMP_INC_STATS_BH ( ICMP_MIB_INERRORS ) ;
return ;
}
sk = inet_lookup ( & dccp_hashinfo , iph - > daddr , dh - > dccph_dport ,
iph - > saddr , dh - > dccph_sport , inet_iif ( skb ) ) ;
if ( sk = = NULL ) {
ICMP_INC_STATS_BH ( ICMP_MIB_INERRORS ) ;
return ;
}
if ( sk - > sk_state = = DCCP_TIME_WAIT ) {
inet_twsk_put ( ( struct inet_timewait_sock * ) sk ) ;
return ;
}
bh_lock_sock ( sk ) ;
/* If too many ICMPs get dropped on busy
* servers this needs to be solved differently .
*/
if ( sock_owned_by_user ( sk ) )
NET_INC_STATS_BH ( LINUX_MIB_LOCKDROPPEDICMPS ) ;
if ( sk - > sk_state = = DCCP_CLOSED )
goto out ;
dp = dccp_sk ( sk ) ;
seq = dccp_hdr_seq ( skb ) ;
if ( sk - > sk_state ! = DCCP_LISTEN & &
! between48 ( seq , dp - > dccps_swl , dp - > dccps_swh ) ) {
NET_INC_STATS ( LINUX_MIB_OUTOFWINDOWICMPS ) ;
goto out ;
}
switch ( type ) {
case ICMP_SOURCE_QUENCH :
/* Just silently ignore these. */
goto out ;
case ICMP_PARAMETERPROB :
err = EPROTO ;
break ;
case ICMP_DEST_UNREACH :
if ( code > NR_ICMP_UNREACH )
goto out ;
if ( code = = ICMP_FRAG_NEEDED ) { /* PMTU discovery (RFC1191) */
if ( ! sock_owned_by_user ( sk ) )
dccp_do_pmtu_discovery ( sk , iph , info ) ;
goto out ;
}
err = icmp_err_convert [ code ] . errno ;
break ;
case ICMP_TIME_EXCEEDED :
err = EHOSTUNREACH ;
break ;
default :
goto out ;
}
switch ( sk - > sk_state ) {
struct request_sock * req , * * prev ;
case DCCP_LISTEN :
if ( sock_owned_by_user ( sk ) )
goto out ;
req = inet_csk_search_req ( sk , & prev , dh - > dccph_dport ,
iph - > daddr , iph - > saddr ) ;
if ( ! req )
goto out ;
/*
* ICMPs are not backlogged , hence we cannot get an established
* socket here .
*/
BUG_TRAP ( ! req - > sk ) ;
if ( seq ! = dccp_rsk ( req ) - > dreq_iss ) {
NET_INC_STATS_BH ( LINUX_MIB_OUTOFWINDOWICMPS ) ;
goto out ;
}
/*
* Still in RESPOND , just remove it silently .
* There is no good way to pass the error to the newly
* created socket , and POSIX does not want network
* errors returned from accept ( ) .
*/
inet_csk_reqsk_queue_drop ( sk , req , prev ) ;
goto out ;
case DCCP_REQUESTING :
case DCCP_RESPOND :
if ( ! sock_owned_by_user ( sk ) ) {
DCCP_INC_STATS_BH ( DCCP_MIB_ATTEMPTFAILS ) ;
sk - > sk_err = err ;
sk - > sk_error_report ( sk ) ;
dccp_done ( sk ) ;
} else
sk - > sk_err_soft = err ;
goto out ;
}
/* If we've already connected we will keep trying
* until we time out , or the user gives up .
*
* rfc1122 4.2 .3 .9 allows to consider as hard errors
* only PROTO_UNREACH and PORT_UNREACH ( well , FRAG_FAILED too ,
* but it is obsoleted by pmtu discovery ) .
*
* Note , that in modern internet , where routing is unreliable
* and in each dark corner broken firewalls sit , sending random
* errors ordered by their masters even this two messages finally lose
* their original sense ( even Linux sends invalid PORT_UNREACHs )
*
* Now we are in compliance with RFCs .
* - - ANK ( 980905 )
*/
inet = inet_sk ( sk ) ;
if ( ! sock_owned_by_user ( sk ) & & inet - > recverr ) {
sk - > sk_err = err ;
sk - > sk_error_report ( sk ) ;
} else /* Only an error on timeout */
sk - > sk_err_soft = err ;
out :
bh_unlock_sock ( sk ) ;
sock_put ( sk ) ;
}
int dccp_v4_send_reset ( struct sock * sk , enum dccp_reset_codes code )
{
struct sk_buff * skb ;
/*
* FIXME : what if rebuild_header fails ?
* Should we be doing a rebuild_header here ?
*/
int err = inet_sk_rebuild_header ( sk ) ;
if ( err ! = 0 )
return err ;
skb = dccp_make_reset ( sk , sk - > sk_dst_cache , code ) ;
if ( skb ! = NULL ) {
const struct dccp_sock * dp = dccp_sk ( sk ) ;
const struct inet_sock * inet = inet_sk ( sk ) ;
err = ip_build_and_send_pkt ( skb , sk ,
inet - > saddr , inet - > daddr , NULL ) ;
if ( err = = NET_XMIT_CN )
err = 0 ;
ccid_hc_rx_exit ( dp - > dccps_hc_rx_ccid , sk ) ;
ccid_hc_tx_exit ( dp - > dccps_hc_tx_ccid , sk ) ;
}
return err ;
}
static inline u64 dccp_v4_init_sequence ( const struct sock * sk ,
const struct sk_buff * skb )
{
return secure_dccp_sequence_number ( skb - > nh . iph - > daddr ,
skb - > nh . iph - > saddr ,
dccp_hdr ( skb ) - > dccph_dport ,
dccp_hdr ( skb ) - > dccph_sport ) ;
}
int dccp_v4_conn_request ( struct sock * sk , struct sk_buff * skb )
{
struct inet_request_sock * ireq ;
struct dccp_sock dp ;
struct request_sock * req ;
struct dccp_request_sock * dreq ;
const __u32 saddr = skb - > nh . iph - > saddr ;
const __u32 daddr = skb - > nh . iph - > daddr ;
struct dst_entry * dst = NULL ;
/* Never answer to DCCP_PKT_REQUESTs send to broadcast or multicast */
if ( ( ( struct rtable * ) skb - > dst ) - > rt_flags &
( RTCF_BROADCAST | RTCF_MULTICAST ) )
goto drop ;
/*
* TW buckets are converted to open requests without
* limitations , they conserve resources and peer is
* evidently real one .
*/
if ( inet_csk_reqsk_queue_is_full ( sk ) )
goto drop ;
/*
* Accept backlog is full . If we have already queued enough
* of warm entries in syn queue , drop request . It is better than
* clogging syn queue with openreqs with exponentially increasing
* timeout .
*/
if ( sk_acceptq_is_full ( sk ) & & inet_csk_reqsk_queue_young ( sk ) > 1 )
goto drop ;
req = reqsk_alloc ( sk - > sk_prot - > rsk_prot ) ;
if ( req = = NULL )
goto drop ;
/* FIXME: process options */
dccp_openreq_init ( req , & dp , skb ) ;
ireq = inet_rsk ( req ) ;
ireq - > loc_addr = daddr ;
ireq - > rmt_addr = saddr ;
/* FIXME: Merge Aristeu's option parsing code when ready */
2005-08-14 03:34:54 +04:00
req - > rcv_wnd = 100 ; /* Fake, option parsing will get the
right value */
2005-08-10 07:14:34 +04:00
ireq - > opt = NULL ;
/*
* Step 3 : Process LISTEN state
*
* Set S . ISR , S . GSR , S . SWL , S . SWH from packet or Init Cookie
*
* In fact we defer setting S . GSR , S . SWL , S . SWH to
* dccp_create_openreq_child .
*/
dreq = dccp_rsk ( req ) ;
dreq - > dreq_isr = DCCP_SKB_CB ( skb ) - > dccpd_seq ;
dreq - > dreq_iss = dccp_v4_init_sequence ( sk , skb ) ;
dreq - > dreq_service = dccp_hdr_request ( skb ) - > dccph_req_service ;
if ( dccp_v4_send_response ( sk , req , dst ) )
goto drop_and_free ;
inet_csk_reqsk_queue_hash_add ( sk , req , DCCP_TIMEOUT_INIT ) ;
return 0 ;
drop_and_free :
/*
* FIXME : should be reqsk_free after implementing req - > rsk_ops
*/
__reqsk_free ( req ) ;
drop :
DCCP_INC_STATS_BH ( DCCP_MIB_ATTEMPTFAILS ) ;
return - 1 ;
}
/*
* The three way handshake has completed - we got a valid ACK or DATAACK -
* now create the new socket .
*
* This is the equivalent of TCP ' s tcp_v4_syn_recv_sock
*/
struct sock * dccp_v4_request_recv_sock ( struct sock * sk , struct sk_buff * skb ,
struct request_sock * req ,
struct dst_entry * dst )
{
struct inet_request_sock * ireq ;
struct inet_sock * newinet ;
struct dccp_sock * newdp ;
struct sock * newsk ;
if ( sk_acceptq_is_full ( sk ) )
goto exit_overflow ;
if ( dst = = NULL & & ( dst = inet_csk_route_req ( sk , req ) ) = = NULL )
goto exit ;
newsk = dccp_create_openreq_child ( sk , req , skb ) ;
if ( newsk = = NULL )
goto exit ;
sk_setup_caps ( newsk , dst ) ;
newdp = dccp_sk ( newsk ) ;
newinet = inet_sk ( newsk ) ;
ireq = inet_rsk ( req ) ;
newinet - > daddr = ireq - > rmt_addr ;
newinet - > rcv_saddr = ireq - > loc_addr ;
newinet - > saddr = ireq - > loc_addr ;
newinet - > opt = ireq - > opt ;
ireq - > opt = NULL ;
newinet - > mc_index = inet_iif ( skb ) ;
newinet - > mc_ttl = skb - > nh . iph - > ttl ;
newinet - > id = jiffies ;
dccp_sync_mss ( newsk , dst_mtu ( dst ) ) ;
__inet_hash ( & dccp_hashinfo , newsk , 0 ) ;
__inet_inherit_port ( & dccp_hashinfo , sk , newsk ) ;
return newsk ;
exit_overflow :
NET_INC_STATS_BH ( LINUX_MIB_LISTENOVERFLOWS ) ;
exit :
NET_INC_STATS_BH ( LINUX_MIB_LISTENDROPS ) ;
dst_release ( dst ) ;
return NULL ;
}
static struct sock * dccp_v4_hnd_req ( struct sock * sk , struct sk_buff * skb )
{
const struct dccp_hdr * dh = dccp_hdr ( skb ) ;
const struct iphdr * iph = skb - > nh . iph ;
struct sock * nsk ;
struct request_sock * * prev ;
/* Find possible connection requests. */
struct request_sock * req = inet_csk_search_req ( sk , & prev ,
dh - > dccph_sport ,
iph - > saddr , iph - > daddr ) ;
if ( req ! = NULL )
return dccp_check_req ( sk , skb , req , prev ) ;
nsk = __inet_lookup_established ( & dccp_hashinfo ,
iph - > saddr , dh - > dccph_sport ,
iph - > daddr , ntohs ( dh - > dccph_dport ) ,
inet_iif ( skb ) ) ;
if ( nsk ! = NULL ) {
if ( nsk - > sk_state ! = DCCP_TIME_WAIT ) {
bh_lock_sock ( nsk ) ;
return nsk ;
}
inet_twsk_put ( ( struct inet_timewait_sock * ) nsk ) ;
return NULL ;
}
return sk ;
}
2005-08-14 03:34:54 +04:00
int dccp_v4_checksum ( const struct sk_buff * skb , const u32 saddr ,
const u32 daddr )
2005-08-10 07:14:34 +04:00
{
2005-08-10 07:15:35 +04:00
const struct dccp_hdr * dh = dccp_hdr ( skb ) ;
2005-08-10 07:14:34 +04:00
int checksum_len ;
u32 tmp ;
if ( dh - > dccph_cscov = = 0 )
checksum_len = skb - > len ;
else {
checksum_len = ( dh - > dccph_cscov + dh - > dccph_x ) * sizeof ( u32 ) ;
2005-08-14 03:34:54 +04:00
checksum_len = checksum_len < skb - > len ? checksum_len :
skb - > len ;
2005-08-10 07:14:34 +04:00
}
tmp = csum_partial ( ( unsigned char * ) dh , checksum_len , 0 ) ;
2005-08-14 03:34:54 +04:00
return csum_tcpudp_magic ( saddr , daddr , checksum_len ,
IPPROTO_DCCP , tmp ) ;
2005-08-10 07:14:34 +04:00
}
2005-08-10 07:15:35 +04:00
static int dccp_v4_verify_checksum ( struct sk_buff * skb ,
const u32 saddr , const u32 daddr )
2005-08-10 07:14:34 +04:00
{
2005-08-10 07:15:35 +04:00
struct dccp_hdr * dh = dccp_hdr ( skb ) ;
int checksum_len ;
u32 tmp ;
2005-08-10 07:14:34 +04:00
2005-08-10 07:15:35 +04:00
if ( dh - > dccph_cscov = = 0 )
checksum_len = skb - > len ;
else {
checksum_len = ( dh - > dccph_cscov + dh - > dccph_x ) * sizeof ( u32 ) ;
2005-08-14 03:34:54 +04:00
checksum_len = checksum_len < skb - > len ? checksum_len :
skb - > len ;
2005-08-10 07:15:35 +04:00
}
tmp = csum_partial ( ( unsigned char * ) dh , checksum_len , 0 ) ;
2005-08-14 03:34:54 +04:00
return csum_tcpudp_magic ( saddr , daddr , checksum_len ,
IPPROTO_DCCP , tmp ) = = 0 ? 0 : - 1 ;
2005-08-10 07:14:34 +04:00
}
static struct dst_entry * dccp_v4_route_skb ( struct sock * sk ,
struct sk_buff * skb )
{
struct rtable * rt ;
struct flowi fl = { . oif = ( ( struct rtable * ) skb - > dst ) - > rt_iif ,
. nl_u = { . ip4_u =
{ . daddr = skb - > nh . iph - > saddr ,
. saddr = skb - > nh . iph - > daddr ,
. tos = RT_CONN_FLAGS ( sk ) } } ,
. proto = sk - > sk_protocol ,
. uli_u = { . ports =
{ . sport = dccp_hdr ( skb ) - > dccph_dport ,
2005-08-14 03:34:54 +04:00
. dport = dccp_hdr ( skb ) - > dccph_sport }
}
} ;
2005-08-10 07:14:34 +04:00
if ( ip_route_output_flow ( & rt , & fl , sk , 0 ) ) {
IP_INC_STATS_BH ( IPSTATS_MIB_OUTNOROUTES ) ;
return NULL ;
}
return & rt - > u . dst ;
}
2005-08-14 05:42:25 +04:00
static void dccp_v4_ctl_send_reset ( struct sk_buff * rxskb )
2005-08-10 07:14:34 +04:00
{
int err ;
struct dccp_hdr * rxdh = dccp_hdr ( rxskb ) , * dh ;
const int dccp_hdr_reset_len = sizeof ( struct dccp_hdr ) +
sizeof ( struct dccp_hdr_ext ) +
sizeof ( struct dccp_hdr_reset ) ;
struct sk_buff * skb ;
struct dst_entry * dst ;
2005-08-21 12:33:48 +04:00
u64 seqno ;
2005-08-10 07:14:34 +04:00
/* Never send a reset in response to a reset. */
if ( rxdh - > dccph_type = = DCCP_PKT_RESET )
return ;
if ( ( ( struct rtable * ) rxskb - > dst ) - > rt_type ! = RTN_LOCAL )
return ;
dst = dccp_v4_route_skb ( dccp_ctl_socket - > sk , rxskb ) ;
if ( dst = = NULL )
return ;
skb = alloc_skb ( MAX_DCCP_HEADER + 15 , GFP_ATOMIC ) ;
if ( skb = = NULL )
goto out ;
/* Reserve space for headers. */
skb_reserve ( skb , MAX_DCCP_HEADER ) ;
skb - > dst = dst_clone ( dst ) ;
skb - > h . raw = skb_push ( skb , dccp_hdr_reset_len ) ;
dh = dccp_hdr ( skb ) ;
memset ( dh , 0 , dccp_hdr_reset_len ) ;
/* Build DCCP header and checksum it. */
dh - > dccph_type = DCCP_PKT_RESET ;
dh - > dccph_sport = rxdh - > dccph_dport ;
dh - > dccph_dport = rxdh - > dccph_sport ;
dh - > dccph_doff = dccp_hdr_reset_len / 4 ;
dh - > dccph_x = 1 ;
2005-08-14 03:34:54 +04:00
dccp_hdr_reset ( skb ) - > dccph_reset_code =
DCCP_SKB_CB ( rxskb ) - > dccpd_reset_code ;
2005-08-10 07:14:34 +04:00
2005-08-21 12:33:48 +04:00
/* See "8.3.1. Abnormal Termination" in draft-ietf-dccp-spec-11 */
seqno = 0 ;
if ( DCCP_SKB_CB ( rxskb ) - > dccpd_ack_seq ! = DCCP_PKT_WITHOUT_ACK_SEQ )
dccp_set_seqno ( & seqno , DCCP_SKB_CB ( rxskb ) - > dccpd_ack_seq + 1 ) ;
dccp_hdr_set_seq ( dh , seqno ) ;
2005-08-14 03:34:54 +04:00
dccp_hdr_set_ack ( dccp_hdr_ack_bits ( skb ) ,
DCCP_SKB_CB ( rxskb ) - > dccpd_seq ) ;
2005-08-10 07:14:34 +04:00
2005-08-10 07:15:35 +04:00
dh - > dccph_checksum = dccp_v4_checksum ( skb , rxskb - > nh . iph - > saddr ,
rxskb - > nh . iph - > daddr ) ;
2005-08-10 07:14:34 +04:00
bh_lock_sock ( dccp_ctl_socket - > sk ) ;
err = ip_build_and_send_pkt ( skb , dccp_ctl_socket - > sk ,
2005-08-14 03:34:54 +04:00
rxskb - > nh . iph - > daddr ,
rxskb - > nh . iph - > saddr , NULL ) ;
2005-08-10 07:14:34 +04:00
bh_unlock_sock ( dccp_ctl_socket - > sk ) ;
if ( err = = NET_XMIT_CN | | err = = 0 ) {
DCCP_INC_STATS_BH ( DCCP_MIB_OUTSEGS ) ;
DCCP_INC_STATS_BH ( DCCP_MIB_OUTRSTS ) ;
}
out :
dst_release ( dst ) ;
}
int dccp_v4_do_rcv ( struct sock * sk , struct sk_buff * skb )
{
struct dccp_hdr * dh = dccp_hdr ( skb ) ;
if ( sk - > sk_state = = DCCP_OPEN ) { /* Fast path */
if ( dccp_rcv_established ( sk , skb , dh , skb - > len ) )
goto reset ;
return 0 ;
}
/*
* Step 3 : Process LISTEN state
* If S . state = = LISTEN ,
2005-08-14 03:34:54 +04:00
* If P . type = = Request or P contains a valid Init Cookie
* option ,
2005-08-10 07:14:34 +04:00
* * Must scan the packet ' s options to check for an Init
* Cookie . Only the Init Cookie is processed here ,
* however ; other options are processed in Step 8. This
* scan need only be performed if the endpoint uses Init
* Cookies *
* * Generate a new socket and switch to that socket *
* Set S : = new socket for this port pair
* S . state = RESPOND
* Choose S . ISS ( initial seqno ) or set from Init Cookie
* Set S . ISR , S . GSR , S . SWL , S . SWH from packet or Init Cookie
* Continue with S . state = = RESPOND
* * A Response packet will be generated in Step 11 *
* Otherwise ,
* Generate Reset ( No Connection ) unless P . type = = Reset
* Drop packet and return
*
2005-08-14 03:34:54 +04:00
* NOTE : the check for the packet types is done in
* dccp_rcv_state_process
2005-08-10 07:14:34 +04:00
*/
if ( sk - > sk_state = = DCCP_LISTEN ) {
struct sock * nsk = dccp_v4_hnd_req ( sk , skb ) ;
if ( nsk = = NULL )
goto discard ;
if ( nsk ! = sk ) {
if ( dccp_child_process ( sk , nsk , skb ) )
goto reset ;
return 0 ;
}
}
if ( dccp_rcv_state_process ( sk , skb , dh , skb - > len ) )
goto reset ;
return 0 ;
reset :
DCCP_SKB_CB ( skb ) - > dccpd_reset_code = DCCP_RESET_CODE_NO_CONNECTION ;
dccp_v4_ctl_send_reset ( skb ) ;
discard :
kfree_skb ( skb ) ;
return 0 ;
}
static inline int dccp_invalid_packet ( struct sk_buff * skb )
{
const struct dccp_hdr * dh ;
if ( skb - > pkt_type ! = PACKET_HOST )
return 1 ;
if ( ! pskb_may_pull ( skb , sizeof ( struct dccp_hdr ) ) ) {
2005-08-19 04:12:02 +04:00
LIMIT_NETDEBUG ( KERN_WARNING " DCCP: pskb_may_pull failed \n " ) ;
2005-08-10 07:14:34 +04:00
return 1 ;
}
dh = dccp_hdr ( skb ) ;
/* If the packet type is not understood, drop packet and return */
if ( dh - > dccph_type > = DCCP_PKT_INVALID ) {
2005-08-19 04:12:02 +04:00
LIMIT_NETDEBUG ( KERN_WARNING " DCCP: invalid packet type \n " ) ;
2005-08-10 07:14:34 +04:00
return 1 ;
}
/*
* If P . Data Offset is too small for packet type , or too large for
* packet , drop packet and return
*/
if ( dh - > dccph_doff < dccp_hdr_len ( skb ) / sizeof ( u32 ) ) {
2005-08-19 04:12:02 +04:00
LIMIT_NETDEBUG ( KERN_WARNING " DCCP: P.Data Offset(%u) "
" too small 1 \n " ,
dh - > dccph_doff ) ;
2005-08-10 07:14:34 +04:00
return 1 ;
}
if ( ! pskb_may_pull ( skb , dh - > dccph_doff * sizeof ( u32 ) ) ) {
2005-08-19 04:12:02 +04:00
LIMIT_NETDEBUG ( KERN_WARNING " DCCP: P.Data Offset(%u) "
" too small 2 \n " ,
dh - > dccph_doff ) ;
2005-08-10 07:14:34 +04:00
return 1 ;
}
dh = dccp_hdr ( skb ) ;
/*
* If P . type is not Data , Ack , or DataAck and P . X = = 0 ( the packet
* has short sequence numbers ) , drop packet and return
*/
if ( dh - > dccph_x = = 0 & &
dh - > dccph_type ! = DCCP_PKT_DATA & &
dh - > dccph_type ! = DCCP_PKT_ACK & &
dh - > dccph_type ! = DCCP_PKT_DATAACK ) {
2005-08-19 04:12:02 +04:00
LIMIT_NETDEBUG ( KERN_WARNING " DCCP: P.type (%s) not Data, Ack "
" nor DataAck and P.X == 0 \n " ,
dccp_packet_name ( dh - > dccph_type ) ) ;
2005-08-10 07:14:34 +04:00
return 1 ;
}
/* If the header checksum is incorrect, drop packet and return */
2005-08-10 07:15:35 +04:00
if ( dccp_v4_verify_checksum ( skb , skb - > nh . iph - > saddr ,
skb - > nh . iph - > daddr ) < 0 ) {
2005-08-19 04:12:02 +04:00
LIMIT_NETDEBUG ( KERN_WARNING " DCCP: header checksum is "
" incorrect \n " ) ;
2005-08-10 07:14:34 +04:00
return 1 ;
}
return 0 ;
}
/* this is called when real data arrives */
int dccp_v4_rcv ( struct sk_buff * skb )
{
const struct dccp_hdr * dh ;
struct sock * sk ;
int rc ;
/* Step 1: Check header basics: */
if ( dccp_invalid_packet ( skb ) )
goto discard_it ;
dh = dccp_hdr ( skb ) ;
#if 0
/*
* Use something like this to simulate some DATA / DATAACK loss to test
* dccp_ackpkts_add , you ' ll get something like this on a session that
* sends 10 DATA / DATAACK packets :
*
2005-08-14 03:34:54 +04:00
* ackpkts_print : 281473596467422 | 0 , 0 | 3 , 0 | 0 , 0 | 3 , 0 | 0 , 0 | 3 , 0 | 0 , 0 | 3 , 0 | 0 , 1 |
2005-08-10 07:14:34 +04:00
*
* 0 , 0 means : DCCP_ACKPKTS_STATE_RECEIVED , RLE = = just this packet
2005-08-14 03:34:54 +04:00
* 0 , 1 means : DCCP_ACKPKTS_STATE_RECEIVED , RLE = = two adjacent packets
* with the same state
2005-08-10 07:14:34 +04:00
* 3 , 0 means : DCCP_ACKPKTS_STATE_NOT_RECEIVED , RLE = = just this packet
*
* So . . .
*
* 281473596467422 was received
* 281473596467421 was not received
* 281473596467420 was received
* 281473596467419 was not received
* 281473596467418 was received
* 281473596467417 was not received
* 281473596467416 was received
* 281473596467415 was not received
* 281473596467414 was received
2005-08-14 03:34:54 +04:00
* 281473596467413 was received ( this one was the 3 way handshake
* RESPONSE )
2005-08-10 07:14:34 +04:00
*
*/
2005-08-14 03:34:54 +04:00
if ( dh - > dccph_type = = DCCP_PKT_DATA | |
dh - > dccph_type = = DCCP_PKT_DATAACK ) {
2005-08-10 07:14:34 +04:00
static int discard = 0 ;
if ( discard ) {
discard = 0 ;
goto discard_it ;
}
discard = 1 ;
}
# endif
DCCP_SKB_CB ( skb ) - > dccpd_seq = dccp_hdr_seq ( skb ) ;
DCCP_SKB_CB ( skb ) - > dccpd_type = dh - > dccph_type ;
dccp_pr_debug ( " %8.8s "
" src=%u.%u.%u.%u@%-5d "
" dst=%u.%u.%u.%u@%-5d seq=%llu " ,
dccp_packet_name ( dh - > dccph_type ) ,
NIPQUAD ( skb - > nh . iph - > saddr ) , ntohs ( dh - > dccph_sport ) ,
NIPQUAD ( skb - > nh . iph - > daddr ) , ntohs ( dh - > dccph_dport ) ,
2005-08-10 07:27:14 +04:00
( unsigned long long ) DCCP_SKB_CB ( skb ) - > dccpd_seq ) ;
2005-08-10 07:14:34 +04:00
if ( dccp_packet_without_ack ( skb ) ) {
DCCP_SKB_CB ( skb ) - > dccpd_ack_seq = DCCP_PKT_WITHOUT_ACK_SEQ ;
dccp_pr_debug_cat ( " \n " ) ;
} else {
DCCP_SKB_CB ( skb ) - > dccpd_ack_seq = dccp_hdr_ack_seq ( skb ) ;
2005-08-10 07:27:14 +04:00
dccp_pr_debug_cat ( " , ack=%llu \n " ,
( unsigned long long )
DCCP_SKB_CB ( skb ) - > dccpd_ack_seq ) ;
2005-08-10 07:14:34 +04:00
}
/* Step 2:
* Look up flow ID in table and get corresponding socket */
sk = __inet_lookup ( & dccp_hashinfo ,
skb - > nh . iph - > saddr , dh - > dccph_sport ,
skb - > nh . iph - > daddr , ntohs ( dh - > dccph_dport ) ,
inet_iif ( skb ) ) ;
/*
* Step 2 :
* If no socket . . .
* Generate Reset ( No Connection ) unless P . type = = Reset
* Drop packet and return
*/
if ( sk = = NULL ) {
dccp_pr_debug ( " failed to look up flow ID in table and "
" get corresponding socket \n " ) ;
goto no_dccp_socket ;
}
/*
* Step 2 :
* . . . or S . state = = TIMEWAIT ,
* Generate Reset ( No Connection ) unless P . type = = Reset
* Drop packet and return
*/
if ( sk - > sk_state = = DCCP_TIME_WAIT ) {
2005-08-10 07:45:21 +04:00
dccp_pr_debug ( " sk->sk_state == DCCP_TIME_WAIT: "
" do_time_wait \n " ) ;
goto do_time_wait ;
2005-08-10 07:14:34 +04:00
}
if ( ! xfrm4_policy_check ( sk , XFRM_POLICY_IN , skb ) ) {
dccp_pr_debug ( " xfrm4_policy_check failed \n " ) ;
goto discard_and_relse ;
}
if ( sk_filter ( sk , skb , 0 ) ) {
dccp_pr_debug ( " sk_filter failed \n " ) ;
goto discard_and_relse ;
}
skb - > dev = NULL ;
bh_lock_sock ( sk ) ;
rc = 0 ;
if ( ! sock_owned_by_user ( sk ) )
rc = dccp_v4_do_rcv ( sk , skb ) ;
else
sk_add_backlog ( sk , skb ) ;
bh_unlock_sock ( sk ) ;
sock_put ( sk ) ;
return rc ;
no_dccp_socket :
if ( ! xfrm4_policy_check ( NULL , XFRM_POLICY_IN , skb ) )
goto discard_it ;
/*
* Step 2 :
* Generate Reset ( No Connection ) unless P . type = = Reset
* Drop packet and return
*/
if ( dh - > dccph_type ! = DCCP_PKT_RESET ) {
2005-08-14 03:34:54 +04:00
DCCP_SKB_CB ( skb ) - > dccpd_reset_code =
DCCP_RESET_CODE_NO_CONNECTION ;
2005-08-10 07:14:34 +04:00
dccp_v4_ctl_send_reset ( skb ) ;
}
discard_it :
/* Discard frame. */
kfree_skb ( skb ) ;
return 0 ;
discard_and_relse :
sock_put ( sk ) ;
goto discard_it ;
2005-08-10 07:45:21 +04:00
do_time_wait :
inet_twsk_put ( ( struct inet_timewait_sock * ) sk ) ;
goto no_dccp_socket ;
2005-08-10 07:14:34 +04:00
}
static int dccp_v4_init_sock ( struct sock * sk )
{
struct dccp_sock * dp = dccp_sk ( sk ) ;
static int dccp_ctl_socket_init = 1 ;
dccp_options_init ( & dp - > dccps_options ) ;
if ( dp - > dccps_options . dccpo_send_ack_vector ) {
2005-08-14 03:34:54 +04:00
dp - > dccps_hc_rx_ackpkts =
dccp_ackpkts_alloc ( DCCP_MAX_ACK_VECTOR_LEN ,
GFP_KERNEL ) ;
2005-08-10 07:14:34 +04:00
if ( dp - > dccps_hc_rx_ackpkts = = NULL )
return - ENOMEM ;
}
/*
* 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
*/
if ( likely ( ! dccp_ctl_socket_init ) ) {
2005-08-14 03:34:54 +04:00
dp - > dccps_hc_rx_ccid = ccid_init ( dp - > dccps_options . dccpo_ccid ,
sk ) ;
dp - > dccps_hc_tx_ccid = ccid_init ( dp - > dccps_options . dccpo_ccid ,
sk ) ;
2005-08-10 07:14:34 +04:00
if ( dp - > dccps_hc_rx_ccid = = NULL | |
dp - > dccps_hc_tx_ccid = = NULL ) {
ccid_exit ( dp - > dccps_hc_rx_ccid , sk ) ;
ccid_exit ( dp - > dccps_hc_tx_ccid , sk ) ;
dccp_ackpkts_free ( dp - > dccps_hc_rx_ackpkts ) ;
dp - > dccps_hc_rx_ackpkts = NULL ;
dp - > dccps_hc_rx_ccid = dp - > dccps_hc_tx_ccid = NULL ;
return - ENOMEM ;
}
} else
dccp_ctl_socket_init = 0 ;
dccp_init_xmit_timers ( sk ) ;
2005-08-10 07:31:11 +04:00
inet_csk ( sk ) - > icsk_rto = DCCP_TIMEOUT_INIT ;
2005-08-10 07:14:34 +04:00
sk - > sk_state = DCCP_CLOSED ;
2005-08-29 09:15:54 +04:00
sk - > sk_write_space = dccp_write_space ;
2005-08-10 07:14:34 +04:00
dp - > dccps_mss_cache = 536 ;
dp - > dccps_role = DCCP_ROLE_UNDEFINED ;
return 0 ;
}
2005-08-14 05:42:25 +04:00
static int dccp_v4_destroy_sock ( struct sock * sk )
2005-08-10 07:14:34 +04:00
{
struct dccp_sock * dp = dccp_sk ( sk ) ;
/*
* DCCP doesn ' t use sk_qrite_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 ) ;
2005-08-24 08:54:00 +04:00
ccid_hc_rx_exit ( dp - > dccps_hc_rx_ccid , sk ) ;
ccid_hc_tx_exit ( dp - > dccps_hc_tx_ccid , sk ) ;
2005-08-10 07:14:34 +04:00
dccp_ackpkts_free ( dp - > dccps_hc_rx_ackpkts ) ;
dp - > dccps_hc_rx_ackpkts = NULL ;
ccid_exit ( dp - > dccps_hc_rx_ccid , sk ) ;
ccid_exit ( dp - > dccps_hc_tx_ccid , sk ) ;
dp - > dccps_hc_rx_ccid = dp - > dccps_hc_tx_ccid = NULL ;
return 0 ;
}
static void dccp_v4_reqsk_destructor ( struct request_sock * req )
{
kfree ( inet_rsk ( req ) - > opt ) ;
}
static struct request_sock_ops dccp_request_sock_ops = {
. family = PF_INET ,
. obj_size = sizeof ( struct dccp_request_sock ) ,
. rtx_syn_ack = dccp_v4_send_response ,
. send_ack = dccp_v4_reqsk_send_ack ,
. destructor = dccp_v4_reqsk_destructor ,
. send_reset = dccp_v4_ctl_send_reset ,
} ;
struct proto dccp_v4_prot = {
. name = " DCCP " ,
. owner = THIS_MODULE ,
. close = dccp_close ,
. connect = dccp_v4_connect ,
. disconnect = dccp_disconnect ,
. ioctl = dccp_ioctl ,
. init = dccp_v4_init_sock ,
. setsockopt = dccp_setsockopt ,
. getsockopt = dccp_getsockopt ,
. sendmsg = dccp_sendmsg ,
. recvmsg = dccp_recvmsg ,
. backlog_rcv = dccp_v4_do_rcv ,
. hash = dccp_v4_hash ,
. unhash = dccp_v4_unhash ,
. accept = inet_csk_accept ,
. get_port = dccp_v4_get_port ,
. shutdown = dccp_shutdown ,
. destroy = dccp_v4_destroy_sock ,
. orphan_count = & dccp_orphan_count ,
. max_header = MAX_DCCP_HEADER ,
. obj_size = sizeof ( struct dccp_sock ) ,
. rsk_prot = & dccp_request_sock_ops ,
2005-08-10 07:45:21 +04:00
. twsk_obj_size = sizeof ( struct inet_timewait_sock ) ,
2005-08-10 07:14:34 +04:00
} ;