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/dccp.h>
# include <linux/icmp.h>
# include <linux/module.h>
# include <linux/skbuff.h>
# include <linux/random.h>
# include <net/icmp.h>
2006-03-21 08:25:11 +03:00
# include <net/inet_common.h>
2005-08-10 07:14:34 +04:00
# include <net/inet_hashtables.h>
2005-12-27 07:43:12 +03:00
# include <net/inet_sock.h>
2006-03-21 08:25:11 +03:00
# include <net/protocol.h>
2005-08-10 07:14:34 +04:00
# include <net/sock.h>
2005-12-14 10:25:19 +03:00
# include <net/timewait_sock.h>
2005-08-10 07:14:34 +04:00
# include <net/tcp_states.h>
# include <net/xfrm.h>
2005-09-18 11:17:51 +04:00
# include "ackvec.h"
2005-08-10 07:14:34 +04:00
# 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
2006-03-21 09:00:37 +03:00
/*
* This is the global socket data structure used for responding to
* the Out - of - the - blue ( OOTB ) packets . A control sock will be created
* for this socket at the initialization time .
*/
static struct socket * dccp_v4_ctl_socket ;
2005-08-10 07:14:34 +04:00
static int dccp_v4_get_port ( struct sock * sk , const unsigned short snum )
{
2005-12-14 10:14:47 +03:00
return inet_csk_get_port ( & dccp_hashinfo , sk , snum ,
inet_csk_bind_conflict ) ;
2005-08-10 07:14:34 +04:00
}
2005-12-14 10:24:16 +03:00
int dccp_v4_connect ( struct sock * sk , struct sockaddr * uaddr , int addr_len )
2005-08-10 07:14:34 +04:00
{
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 ;
2005-09-17 03:58:40 +04:00
if ( dccp_service_not_initialized ( sk ) )
return - EPROTO ;
2005-08-10 07:14:34 +04:00
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 ;
2005-12-14 10:26:10 +03:00
inet_csk ( sk ) - > icsk_ext_hdr_len = 0 ;
2005-08-10 07:14:34 +04:00
if ( inet - > opt ! = NULL )
2005-12-14 10:26:10 +03:00
inet_csk ( sk ) - > icsk_ext_hdr_len = inet - > opt - > optlen ;
2005-08-10 07:14:34 +04:00
/*
* 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 ) ;
2005-12-14 10:25:31 +03:00
err = inet_hash_connect ( & dccp_death_row , sk ) ;
2005-08-10 07:14:34 +04:00
if ( err ! = 0 )
goto failure ;
2006-02-01 04:35:35 +03:00
err = ip_route_newports ( & rt , IPPROTO_DCCP , inet - > sport , inet - > dport ,
sk ) ;
2005-08-10 07:14:34 +04:00
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 ) ;
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 ;
}
2005-12-14 10:24:16 +03:00
EXPORT_SYMBOL_GPL ( dccp_v4_connect ) ;
2005-08-10 07:14:34 +04:00
/*
* 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 & &
2005-12-14 10:26:10 +03:00
inet_csk ( sk ) - > icsk_pmtu_cookie > mtu ) {
2005-08-10 07:14:34 +04:00
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 */
}
2006-03-21 09:31:26 +03:00
static void dccp_v4_reqsk_send_ack ( struct sk_buff * rxskb ,
struct request_sock * req )
2005-08-10 07:14:34 +04:00
{
int err ;
struct dccp_hdr * rxdh = dccp_hdr ( rxskb ) , * dh ;
2006-03-21 09:31:09 +03:00
const u32 dccp_hdr_ack_len = sizeof ( struct dccp_hdr ) +
2005-08-10 07:14:34 +04:00
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 ;
2006-03-21 09:31:09 +03:00
skb = alloc_skb ( dccp_v4_ctl_socket - > sk - > sk_prot - > max_header , GFP_ATOMIC ) ;
2005-08-10 07:14:34 +04:00
if ( skb = = NULL )
return ;
/* Reserve space for headers. */
2006-03-21 09:31:09 +03:00
skb_reserve ( skb , dccp_v4_ctl_socket - > sk - > sk_prot - > max_header ) ;
2005-08-10 07:14:34 +04:00
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
2006-03-21 09:00:37 +03:00
bh_lock_sock ( dccp_v4_ctl_socket - > sk ) ;
err = ip_build_and_send_pkt ( skb , dccp_v4_ctl_socket - > sk ,
2005-08-14 03:34:54 +04:00
rxskb - > nh . iph - > daddr ,
rxskb - > nh . iph - > saddr , NULL ) ;
2006-03-21 09:00:37 +03:00
bh_unlock_sock ( dccp_v4_ctl_socket - > sk ) ;
2005-08-10 07:14:34 +04:00
if ( err = = NET_XMIT_CN | | err = = 0 ) {
DCCP_INC_STATS_BH ( DCCP_MIB_OUTSEGS ) ;
DCCP_INC_STATS_BH ( DCCP_MIB_OUTRSTS ) ;
}
}
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 ) ;
2006-03-21 08:23:59 +03:00
struct dccp_hdr * dh = dccp_hdr ( skb ) ;
2005-08-10 07:14:34 +04:00
2006-03-21 08:23:59 +03:00
dh - > dccph_checksum = dccp_v4_checksum ( skb , ireq - > loc_addr ,
ireq - > rmt_addr ) ;
2005-10-18 06:03:28 +04:00
memset ( & ( IPCB ( skb ) - > opt ) , 0 , sizeof ( IPCB ( skb ) - > opt ) ) ;
2005-08-10 07:14:34 +04:00
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 .
*/
2006-03-21 08:25:11 +03:00
static void dccp_v4_err ( struct sk_buff * skb , u32 info )
2005-08-10 07:14:34 +04:00
{
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 ) ;
}
2005-12-14 10:16:16 +03:00
/* This routine computes an IPv4 DCCP checksum. */
2005-12-14 10:24:16 +03:00
void dccp_v4_send_check ( struct sock * sk , int len , struct sk_buff * skb )
2005-12-14 10:16:16 +03:00
{
const struct inet_sock * inet = inet_sk ( sk ) ;
struct dccp_hdr * dh = dccp_hdr ( skb ) ;
dh - > dccph_checksum = dccp_v4_checksum ( skb , inet - > saddr , inet - > daddr ) ;
}
2005-12-14 10:24:16 +03:00
EXPORT_SYMBOL_GPL ( dccp_v4_send_check ) ;
2005-08-10 07:14:34 +04:00
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 ;
2006-03-21 06:23:32 +03:00
const __be32 saddr = skb - > nh . iph - > saddr ;
const __be32 daddr = skb - > nh . iph - > daddr ;
const __be32 service = dccp_hdr_request ( skb ) - > dccph_req_service ;
2005-09-17 03:58:33 +04:00
struct dccp_skb_cb * dcb = DCCP_SKB_CB ( skb ) ;
__u8 reset_code = DCCP_RESET_CODE_TOO_BUSY ;
2005-08-10 07:14:34 +04:00
/* Never answer to DCCP_PKT_REQUESTs send to broadcast or multicast */
if ( ( ( struct rtable * ) skb - > dst ) - > rt_flags &
2005-09-17 03:58:33 +04:00
( RTCF_BROADCAST | RTCF_MULTICAST ) ) {
reset_code = DCCP_RESET_CODE_NO_CONNECTION ;
2005-08-10 07:14:34 +04:00
goto drop ;
2005-09-17 03:58:33 +04:00
}
2005-08-10 07:14:34 +04:00
2005-09-17 03:58:40 +04:00
if ( dccp_bad_service_code ( sk , service ) ) {
reset_code = DCCP_RESET_CODE_BAD_SERVICE_CODE ;
goto drop ;
}
2005-08-10 07:14:34 +04:00
/*
* 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 ;
[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 ( dccp_parse_options ( sk , skb ) )
2006-04-11 03:43:03 +04:00
goto drop_and_free ;
2005-08-10 07:14:34 +04:00
dccp_openreq_init ( req , & dp , skb ) ;
ireq = inet_rsk ( req ) ;
ireq - > loc_addr = daddr ;
ireq - > rmt_addr = saddr ;
2006-07-24 10:33:28 +04:00
req - > rcv_wnd = dccp_feat_default_sequence_window ;
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 ) ;
2005-09-17 03:58:40 +04:00
dreq - > dreq_isr = dcb - > dccpd_seq ;
dreq - > dreq_iss = dccp_v4_init_sequence ( sk , skb ) ;
dreq - > dreq_service = service ;
2005-08-10 07:14:34 +04:00
2005-12-14 10:24:16 +03:00
if ( dccp_v4_send_response ( sk , req , NULL ) )
2005-08-10 07:14:34 +04:00
goto drop_and_free ;
inet_csk_reqsk_queue_hash_add ( sk , req , DCCP_TIMEOUT_INIT ) ;
return 0 ;
drop_and_free :
2005-12-14 10:25:06 +03:00
reqsk_free ( req ) ;
2005-08-10 07:14:34 +04:00
drop :
DCCP_INC_STATS_BH ( DCCP_MIB_ATTEMPTFAILS ) ;
2005-09-17 03:58:33 +04:00
dcb - > dccpd_reset_code = reset_code ;
2005-08-10 07:14:34 +04:00
return - 1 ;
}
2005-12-14 10:24:16 +03:00
EXPORT_SYMBOL_GPL ( dccp_v4_conn_request ) ;
2005-08-10 07:14:34 +04:00
/*
* 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 ;
}
2005-12-14 10:24:16 +03:00
EXPORT_SYMBOL_GPL ( dccp_v4_request_recv_sock ) ;
2005-08-10 07:14:34 +04:00
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 ;
}
2006-03-21 06:23:32 +03:00
int dccp_v4_checksum ( const struct sk_buff * skb , const __be32 saddr ,
const __be32 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
}
2006-03-21 08:25:11 +03:00
EXPORT_SYMBOL_GPL ( dccp_v4_checksum ) ;
2005-08-10 07:15:35 +04:00
static int dccp_v4_verify_checksum ( struct sk_buff * skb ,
2006-03-21 06:23:32 +03:00
const __be32 saddr , const __be32 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 ;
2006-03-21 09:00:37 +03:00
dst = dccp_v4_route_skb ( dccp_v4_ctl_socket - > sk , rxskb ) ;
2005-08-10 07:14:34 +04:00
if ( dst = = NULL )
return ;
2006-03-21 09:31:09 +03:00
skb = alloc_skb ( dccp_v4_ctl_socket - > sk - > sk_prot - > max_header ,
GFP_ATOMIC ) ;
2005-08-10 07:14:34 +04:00
if ( skb = = NULL )
goto out ;
/* Reserve space for headers. */
2006-03-21 09:31:09 +03:00
skb_reserve ( skb , dccp_v4_ctl_socket - > sk - > sk_prot - > max_header ) ;
2005-08-10 07:14:34 +04:00
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
2006-03-21 09:00:37 +03:00
bh_lock_sock ( dccp_v4_ctl_socket - > sk ) ;
err = ip_build_and_send_pkt ( skb , dccp_v4_ctl_socket - > sk ,
2005-08-14 03:34:54 +04:00
rxskb - > nh . iph - > daddr ,
rxskb - > nh . iph - > saddr , NULL ) ;
2006-03-21 09:00:37 +03:00
bh_unlock_sock ( dccp_v4_ctl_socket - > sk ) ;
2005-08-10 07:14:34 +04:00
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_v4_ctl_send_reset ( skb ) ;
discard :
kfree_skb ( skb ) ;
return 0 ;
}
2005-12-14 10:24:16 +03:00
EXPORT_SYMBOL_GPL ( dccp_v4_do_rcv ) ;
int dccp_invalid_packet ( struct sk_buff * skb )
2005-08-10 07:14:34 +04:00
{
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 ;
}
return 0 ;
}
2005-12-14 10:24:16 +03:00
EXPORT_SYMBOL_GPL ( dccp_invalid_packet ) ;
2005-08-10 07:14:34 +04:00
/* this is called when real data arrives */
2006-03-21 08:25:11 +03:00
static int dccp_v4_rcv ( struct sk_buff * skb )
2005-08-10 07:14:34 +04:00
{
const struct dccp_hdr * dh ;
struct sock * sk ;
/* Step 1: Check header basics: */
if ( dccp_invalid_packet ( skb ) )
goto discard_it ;
2005-12-14 10:24:16 +03:00
/* If the header checksum is incorrect, drop packet and return */
if ( dccp_v4_verify_checksum ( skb , skb - > nh . iph - > saddr ,
skb - > nh . iph - > daddr ) < 0 ) {
LIMIT_NETDEBUG ( KERN_WARNING " %s: incorrect header checksum \n " ,
__FUNCTION__ ) ;
goto discard_it ;
}
2005-08-10 07:14:34 +04:00
dh = dccp_hdr ( skb ) ;
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
}
2005-12-27 07:42:22 +03:00
if ( ! xfrm4_policy_check ( sk , XFRM_POLICY_IN , skb ) )
2005-08-10 07:14:34 +04:00
goto discard_and_relse ;
2006-01-07 10:06:30 +03:00
nf_reset ( skb ) ;
2005-08-10 07:14:34 +04:00
2005-12-27 07:42:22 +03:00
return sk_receive_skb ( sk , skb ) ;
2005-08-10 07:14:34 +04:00
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
}
2006-03-21 08:25:11 +03:00
static struct inet_connection_sock_af_ops dccp_ipv4_af_ops = {
2006-03-21 09:48:35 +03:00
. queue_xmit = ip_queue_xmit ,
. send_check = dccp_v4_send_check ,
. rebuild_header = inet_sk_rebuild_header ,
. conn_request = dccp_v4_conn_request ,
. syn_recv_sock = dccp_v4_request_recv_sock ,
. net_header_len = sizeof ( struct iphdr ) ,
. setsockopt = ip_setsockopt ,
. getsockopt = ip_getsockopt ,
. addr2sockaddr = inet_csk_addr2sockaddr ,
. sockaddr_len = sizeof ( struct sockaddr_in ) ,
2006-03-21 09:45:21 +03:00
# ifdef CONFIG_COMPAT
2006-03-21 09:48:35 +03:00
. compat_setsockopt = compat_ip_setsockopt ,
. compat_getsockopt = compat_ip_getsockopt ,
2006-03-21 09:45:21 +03:00
# endif
2005-12-14 10:16:16 +03:00
} ;
2006-03-21 08:23:15 +03:00
static int dccp_v4_init_sock ( struct sock * sk )
2005-08-10 07:14:34 +04:00
{
2006-03-21 09:00:37 +03:00
static __u8 dccp_v4_ctl_sock_initialized ;
int err = dccp_init_sock ( sk , dccp_v4_ctl_sock_initialized ) ;
2005-12-14 10:24:16 +03:00
2006-03-21 09:00:37 +03:00
if ( err = = 0 ) {
if ( unlikely ( ! dccp_v4_ctl_sock_initialized ) )
dccp_v4_ctl_sock_initialized = 1 ;
2006-03-21 08:23:15 +03:00
inet_csk ( sk ) - > icsk_af_ops = & dccp_ipv4_af_ops ;
2006-03-21 09:00:37 +03:00
}
2006-03-21 08:23:15 +03:00
return err ;
2005-08-10 07:14:34 +04:00
}
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 ,
} ;
2005-12-14 10:25:19 +03:00
static struct timewait_sock_ops dccp_timewait_sock_ops = {
. twsk_obj_size = sizeof ( struct inet_timewait_sock ) ,
} ;
2006-03-21 08:58:29 +03:00
static struct proto dccp_v4_prot = {
2005-08-10 07:14:34 +04:00
. 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 ,
2006-03-21 08:23:39 +03:00
. hash = dccp_hash ,
2005-12-14 10:24:16 +03:00
. unhash = dccp_unhash ,
2005-08-10 07:14:34 +04:00
. accept = inet_csk_accept ,
. get_port = dccp_v4_get_port ,
. shutdown = dccp_shutdown ,
2006-03-21 08:23:15 +03:00
. destroy = dccp_destroy_sock ,
2005-08-10 07:14:34 +04:00
. orphan_count = & dccp_orphan_count ,
. max_header = MAX_DCCP_HEADER ,
. obj_size = sizeof ( struct dccp_sock ) ,
. rsk_prot = & dccp_request_sock_ops ,
2005-12-14 10:25:19 +03:00
. twsk_prot = & dccp_timewait_sock_ops ,
2006-03-21 09:48:35 +03:00
# ifdef CONFIG_COMPAT
. compat_setsockopt = compat_dccp_setsockopt ,
. compat_getsockopt = compat_dccp_getsockopt ,
# endif
2005-08-10 07:14:34 +04:00
} ;
2005-12-14 10:25:19 +03:00
2006-03-21 08:25:11 +03:00
static struct net_protocol dccp_v4_protocol = {
. handler = dccp_v4_rcv ,
. err_handler = dccp_v4_err ,
. no_policy = 1 ,
} ;
static const struct proto_ops inet_dccp_ops = {
2006-03-21 09:48:35 +03:00
. family = PF_INET ,
. owner = THIS_MODULE ,
. release = inet_release ,
. bind = inet_bind ,
. connect = inet_stream_connect ,
. socketpair = sock_no_socketpair ,
. accept = inet_accept ,
. getname = inet_getname ,
2006-03-21 08:25:11 +03:00
/* FIXME: work on tcp_poll to rename it to inet_csk_poll */
2006-03-21 09:48:35 +03:00
. poll = dccp_poll ,
. ioctl = inet_ioctl ,
2006-03-21 08:25:11 +03:00
/* FIXME: work on inet_listen to rename it to sock_common_listen */
2006-03-21 09:48:35 +03:00
. listen = inet_dccp_listen ,
. shutdown = inet_shutdown ,
. setsockopt = sock_common_setsockopt ,
. getsockopt = sock_common_getsockopt ,
. sendmsg = inet_sendmsg ,
. recvmsg = sock_common_recvmsg ,
. mmap = sock_no_mmap ,
. sendpage = sock_no_sendpage ,
2006-03-21 09:45:21 +03:00
# ifdef CONFIG_COMPAT
2006-03-21 09:48:35 +03:00
. compat_setsockopt = compat_sock_common_setsockopt ,
. compat_getsockopt = compat_sock_common_getsockopt ,
2006-03-21 09:45:21 +03:00
# endif
2006-03-21 08:25:11 +03:00
} ;
static struct inet_protosw dccp_v4_protosw = {
. type = SOCK_DCCP ,
. protocol = IPPROTO_DCCP ,
. prot = & dccp_v4_prot ,
. ops = & inet_dccp_ops ,
. capability = - 1 ,
. no_check = 0 ,
. flags = INET_PROTOSW_ICSK ,
} ;
static int __init dccp_v4_init ( void )
{
int err = proto_register ( & dccp_v4_prot , 1 ) ;
if ( err ! = 0 )
goto out ;
err = inet_add_protocol ( & dccp_v4_protocol , IPPROTO_DCCP ) ;
if ( err ! = 0 )
goto out_proto_unregister ;
inet_register_protosw ( & dccp_v4_protosw ) ;
2006-03-21 09:01:03 +03:00
err = inet_csk_ctl_sock_create ( & dccp_v4_ctl_socket , PF_INET ,
SOCK_DCCP , IPPROTO_DCCP ) ;
2006-03-21 08:25:11 +03:00
if ( err )
goto out_unregister_protosw ;
out :
return err ;
out_unregister_protosw :
inet_unregister_protosw ( & dccp_v4_protosw ) ;
inet_del_protocol ( & dccp_v4_protocol , IPPROTO_DCCP ) ;
out_proto_unregister :
proto_unregister ( & dccp_v4_prot ) ;
goto out ;
}
static void __exit dccp_v4_exit ( void )
{
inet_unregister_protosw ( & dccp_v4_protosw ) ;
inet_del_protocol ( & dccp_v4_protocol , IPPROTO_DCCP ) ;
proto_unregister ( & dccp_v4_prot ) ;
}
module_init ( dccp_v4_init ) ;
module_exit ( dccp_v4_exit ) ;
/*
* __stringify doesn ' t likes enums , so use SOCK_DCCP ( 6 ) and IPPROTO_DCCP ( 33 )
* values directly , Also cover the case where the protocol is not specified ,
* i . e . net - pf - PF_INET - proto - 0 - type - SOCK_DCCP
*/
MODULE_ALIAS ( " net-pf- " __stringify ( PF_INET ) " -proto-33-type-6 " ) ;
MODULE_ALIAS ( " net-pf- " __stringify ( PF_INET ) " -proto-0-type-6 " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_AUTHOR ( " Arnaldo Carvalho de Melo <acme@mandriva.com> " ) ;
MODULE_DESCRIPTION ( " DCCP - Datagram Congestion Controlled Protocol " ) ;