forked from mirrors/linux
		
	[DCCP]: Initial implementation
Development to this point was done on a subversion repository at: http://oops.ghostprotocols.net:81/cgi-bin/viewcvs.cgi/dccp-2.6/ This repository will be kept at this site for the foreseable future, so that interested parties can see the history of this code, attributions, etc. If I ever decide to take this offline I'll provide the full history at some other suitable place. Signed-off-by: Arnaldo Carvalho de Melo <acme@ghostprotocols.net> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
		
							parent
							
								
									c4365c9235
								
							
						
					
					
						commit
						7c657876b6
					
				
					 22 changed files with 7746 additions and 0 deletions
				
			
		
							
								
								
									
										432
									
								
								include/linux/dccp.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										432
									
								
								include/linux/dccp.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,432 @@ | |||
| #ifndef _LINUX_DCCP_H | ||||
| #define _LINUX_DCCP_H | ||||
| 
 | ||||
| #include <linux/in.h> | ||||
| #include <linux/list.h> | ||||
| #include <linux/types.h> | ||||
| #include <linux/uio.h> | ||||
| #include <linux/workqueue.h> | ||||
| 
 | ||||
| #include <net/inet_connection_sock.h> | ||||
| #include <net/sock.h> | ||||
| #include <net/tcp_states.h> | ||||
| #include <net/tcp.h> | ||||
| 
 | ||||
| /* FIXME: this is utterly wrong */ | ||||
| struct sockaddr_dccp { | ||||
| 	struct sockaddr_in	in; | ||||
| 	unsigned int		service; | ||||
| }; | ||||
| 
 | ||||
| enum dccp_state { | ||||
| 	DCCP_OPEN	= TCP_ESTABLISHED, | ||||
| 	DCCP_REQUESTING	= TCP_SYN_SENT, | ||||
| 	DCCP_PARTOPEN	= TCP_FIN_WAIT1, /* FIXME:
 | ||||
| 					    This mapping is horrible, but TCP has | ||||
| 					    no matching state for DCCP_PARTOPEN, | ||||
| 					    as TCP_SYN_RECV is already used by | ||||
| 					    DCCP_RESPOND, why don't stop using TCP | ||||
| 					    mapping of states? OK, now we don't use | ||||
| 					    sk_stream_sendmsg anymore, so doesn't | ||||
| 					    seem to exist any reason for us to | ||||
| 					    do the TCP mapping here */ | ||||
| 	DCCP_LISTEN	= TCP_LISTEN, | ||||
| 	DCCP_RESPOND	= TCP_SYN_RECV, | ||||
| 	DCCP_CLOSING	= TCP_CLOSING, | ||||
| 	DCCP_TIME_WAIT	= TCP_TIME_WAIT, | ||||
| 	DCCP_CLOSED	= TCP_CLOSE, | ||||
| 	DCCP_MAX_STATES = TCP_MAX_STATES, | ||||
| }; | ||||
| 
 | ||||
| #define DCCP_STATE_MASK 0xf | ||||
| #define DCCP_ACTION_FIN (1<<7) | ||||
| 
 | ||||
| enum { | ||||
| 	DCCPF_OPEN	 = TCPF_ESTABLISHED, | ||||
| 	DCCPF_REQUESTING = TCPF_SYN_SENT, | ||||
| 	DCCPF_PARTOPEN	 = TCPF_FIN_WAIT1, | ||||
| 	DCCPF_LISTEN	 = TCPF_LISTEN, | ||||
| 	DCCPF_RESPOND	 = TCPF_SYN_RECV, | ||||
| 	DCCPF_CLOSING	 = TCPF_CLOSING, | ||||
| 	DCCPF_TIME_WAIT	 = TCPF_TIME_WAIT, | ||||
| 	DCCPF_CLOSED	 = TCPF_CLOSE, | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * struct dccp_hdr - generic part of DCCP packet header | ||||
|  * | ||||
|  * @dccph_sport - Relevant port on the endpoint that sent this packet | ||||
|  * @dccph_dport - Relevant port on the other endpoint | ||||
|  * @dccph_doff - Data Offset from the start of the DCCP header, in 32-bit words | ||||
|  * @dccph_ccval - Used by the HC-Sender CCID | ||||
|  * @dccph_cscov - Parts of the packet that are covered by the Checksum field | ||||
|  * @dccph_checksum - Internet checksum, depends on dccph_cscov | ||||
|  * @dccph_x - 0 = 24 bit sequence number, 1 = 48 | ||||
|  * @dccph_type - packet type, see DCCP_PKT_ prefixed macros | ||||
|  * @dccph_seq - sequence number high or low order 24 bits, depends on dccph_x | ||||
|  */ | ||||
| struct dccp_hdr { | ||||
| 	__u16	dccph_sport, | ||||
| 		dccph_dport; | ||||
| 	__u8	dccph_doff; | ||||
| #if defined(__LITTLE_ENDIAN_BITFIELD) | ||||
| 	__u8	dccph_cscov:4, | ||||
| 		dccph_ccval:4; | ||||
| #elif defined(__BIG_ENDIAN_BITFIELD) | ||||
| 	__u8	dccph_ccval:4, | ||||
| 		dccph_cscov:4; | ||||
| #else | ||||
| #error  "Adjust your <asm/byteorder.h> defines" | ||||
| #endif | ||||
| 	__u16	dccph_checksum; | ||||
| #if defined(__LITTLE_ENDIAN_BITFIELD) | ||||
| 	__u32	dccph_x:1, | ||||
| 		dccph_type:4, | ||||
| 		dccph_reserved:3, | ||||
| 		dccph_seq:24; | ||||
| #elif defined(__BIG_ENDIAN_BITFIELD) | ||||
| 	__u32	dccph_reserved:3, | ||||
| 		dccph_type:4, | ||||
| 		dccph_x:1, | ||||
| 		dccph_seq:24; | ||||
| #else | ||||
| #error  "Adjust your <asm/byteorder.h> defines" | ||||
| #endif | ||||
| }; | ||||
| 
 | ||||
| static inline struct dccp_hdr *dccp_hdr(const struct sk_buff *skb) | ||||
| { | ||||
| 	return (struct dccp_hdr *)skb->h.raw; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * struct dccp_hdr_ext - the low bits of a 48 bit seq packet | ||||
|  * | ||||
|  * @dccph_seq_low - low 24 bits of a 48 bit seq packet | ||||
|  */ | ||||
| struct dccp_hdr_ext { | ||||
| 	__u32	dccph_seq_low; | ||||
| }; | ||||
| 
 | ||||
| static inline struct dccp_hdr_ext *dccp_hdrx(const struct sk_buff *skb) | ||||
| { | ||||
| 	return (struct dccp_hdr_ext *)(skb->h.raw + sizeof(struct dccp_hdr)); | ||||
| } | ||||
| 
 | ||||
| static inline unsigned int dccp_basic_hdr_len(const struct sk_buff *skb) | ||||
| { | ||||
| 	const struct dccp_hdr *dh = dccp_hdr(skb); | ||||
| 	return sizeof(*dh) + (dh->dccph_x ? sizeof(struct dccp_hdr_ext) : 0); | ||||
| } | ||||
| 
 | ||||
| static inline __u64 dccp_hdr_seq(const struct sk_buff *skb) | ||||
| { | ||||
| 	const struct dccp_hdr *dh = dccp_hdr(skb); | ||||
| #if defined(__LITTLE_ENDIAN_BITFIELD) | ||||
| 	__u64 seq_nr = ntohl(dh->dccph_seq << 8); | ||||
| #elif defined(__BIG_ENDIAN_BITFIELD) | ||||
| 	__u64 seq_nr = ntohl(dh->dccph_seq); | ||||
| #else | ||||
| #error  "Adjust your <asm/byteorder.h> defines" | ||||
| #endif | ||||
| 
 | ||||
| 	if (dh->dccph_x != 0) | ||||
| 		seq_nr = (seq_nr << 32) + ntohl(dccp_hdrx(skb)->dccph_seq_low); | ||||
| 
 | ||||
| 	return seq_nr; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * struct dccp_hdr_request - Conection initiation request header | ||||
|  * | ||||
|  * @dccph_req_service - Service to which the client app wants to connect | ||||
|  * @dccph_req_options - list of options (must be a multiple of 32 bits | ||||
|  */ | ||||
| struct dccp_hdr_request { | ||||
| 	__u32	dccph_req_service; | ||||
| }; | ||||
| 
 | ||||
| static inline struct dccp_hdr_request *dccp_hdr_request(struct sk_buff *skb) | ||||
| { | ||||
| 	return (struct dccp_hdr_request *)(skb->h.raw + dccp_basic_hdr_len(skb)); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * struct dccp_hdr_ack_bits - acknowledgment bits common to most packets | ||||
|  * | ||||
|  * @dccph_resp_ack_nr_high - 48 bit ack number high order bits, contains GSR | ||||
|  * @dccph_resp_ack_nr_low - 48 bit ack number low order bits, contains GSR | ||||
|  */ | ||||
| struct dccp_hdr_ack_bits { | ||||
| 	__u32	dccph_reserved1:8, | ||||
| 		dccph_ack_nr_high:24; | ||||
| 	__u32	dccph_ack_nr_low; | ||||
| }; | ||||
| 
 | ||||
| static inline struct dccp_hdr_ack_bits *dccp_hdr_ack_bits(const struct sk_buff *skb) | ||||
| { | ||||
| 	return (struct dccp_hdr_ack_bits *)(skb->h.raw + dccp_basic_hdr_len(skb)); | ||||
| } | ||||
| 
 | ||||
| static inline u64 dccp_hdr_ack_seq(const struct sk_buff *skb) | ||||
| { | ||||
| 	const struct dccp_hdr_ack_bits *dhack = dccp_hdr_ack_bits(skb); | ||||
| #if defined(__LITTLE_ENDIAN_BITFIELD) | ||||
| 	return (((u64)ntohl(dhack->dccph_ack_nr_high << 8)) << 32) + ntohl(dhack->dccph_ack_nr_low); | ||||
| #elif defined(__BIG_ENDIAN_BITFIELD) | ||||
| 	return (((u64)ntohl(dhack->dccph_ack_nr_high)) << 32) + ntohl(dhack->dccph_ack_nr_low); | ||||
| #else | ||||
| #error  "Adjust your <asm/byteorder.h> defines" | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * struct dccp_hdr_response - Conection initiation response header | ||||
|  * | ||||
|  * @dccph_resp_ack_nr_high - 48 bit ack number high order bits, contains GSR | ||||
|  * @dccph_resp_ack_nr_low - 48 bit ack number low order bits, contains GSR | ||||
|  * @dccph_resp_service - Echoes the Service Code on a received DCCP-Request | ||||
|  * @dccph_resp_options - list of options (must be a multiple of 32 bits | ||||
|  */ | ||||
| struct dccp_hdr_response { | ||||
| 	struct dccp_hdr_ack_bits	dccph_resp_ack; | ||||
| 	__u32				dccph_resp_service; | ||||
| }; | ||||
| 
 | ||||
| static inline struct dccp_hdr_response *dccp_hdr_response(struct sk_buff *skb) | ||||
| { | ||||
| 	return (struct dccp_hdr_response *)(skb->h.raw + dccp_basic_hdr_len(skb)); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * struct dccp_hdr_reset - Unconditionally shut down a connection | ||||
|  * | ||||
|  * @dccph_reset_service - Echoes the Service Code on a received DCCP-Request | ||||
|  * @dccph_reset_options - list of options (must be a multiple of 32 bits | ||||
|  */ | ||||
| struct dccp_hdr_reset { | ||||
| 	struct dccp_hdr_ack_bits	dccph_reset_ack; | ||||
| 	__u8				dccph_reset_code, | ||||
| 					dccph_reset_data[3]; | ||||
| }; | ||||
| 
 | ||||
| static inline struct dccp_hdr_reset *dccp_hdr_reset(struct sk_buff *skb) | ||||
| { | ||||
| 	return (struct dccp_hdr_reset *)(skb->h.raw + dccp_basic_hdr_len(skb)); | ||||
| } | ||||
| 
 | ||||
| enum dccp_pkt_type { | ||||
| 	DCCP_PKT_REQUEST = 0, | ||||
| 	DCCP_PKT_RESPONSE, | ||||
| 	DCCP_PKT_DATA, | ||||
| 	DCCP_PKT_ACK, | ||||
| 	DCCP_PKT_DATAACK, | ||||
| 	DCCP_PKT_CLOSEREQ, | ||||
| 	DCCP_PKT_CLOSE, | ||||
| 	DCCP_PKT_RESET, | ||||
| 	DCCP_PKT_SYNC, | ||||
| 	DCCP_PKT_SYNCACK, | ||||
| 	DCCP_PKT_INVALID, | ||||
| }; | ||||
| 
 | ||||
| #define DCCP_NR_PKT_TYPES DCCP_PKT_INVALID | ||||
| 
 | ||||
| static inline unsigned int dccp_packet_hdr_len(const __u8 type) | ||||
| { | ||||
| 	if (type == DCCP_PKT_DATA) | ||||
| 		return 0; | ||||
| 	if (type == DCCP_PKT_DATAACK	|| | ||||
| 	    type == DCCP_PKT_ACK	|| | ||||
| 	    type == DCCP_PKT_SYNC	|| | ||||
| 	    type == DCCP_PKT_SYNCACK	|| | ||||
| 	    type == DCCP_PKT_CLOSE	|| | ||||
| 	    type == DCCP_PKT_CLOSEREQ) | ||||
| 		return sizeof(struct dccp_hdr_ack_bits); | ||||
| 	if (type == DCCP_PKT_REQUEST) | ||||
| 		return sizeof(struct dccp_hdr_request); | ||||
| 	if (type == DCCP_PKT_RESPONSE) | ||||
| 		return sizeof(struct dccp_hdr_response); | ||||
| 	return sizeof(struct dccp_hdr_reset); | ||||
| } | ||||
| 
 | ||||
| static inline unsigned int dccp_hdr_len(const struct sk_buff *skb) | ||||
| { | ||||
| 	return dccp_basic_hdr_len(skb) + | ||||
| 	       dccp_packet_hdr_len(dccp_hdr(skb)->dccph_type); | ||||
| } | ||||
| 
 | ||||
| enum dccp_reset_codes { | ||||
| 	DCCP_RESET_CODE_UNSPECIFIED = 0, | ||||
| 	DCCP_RESET_CODE_CLOSED, | ||||
| 	DCCP_RESET_CODE_ABORTED, | ||||
| 	DCCP_RESET_CODE_NO_CONNECTION, | ||||
| 	DCCP_RESET_CODE_PACKET_ERROR, | ||||
| 	DCCP_RESET_CODE_OPTION_ERROR, | ||||
| 	DCCP_RESET_CODE_MANDATORY_ERROR, | ||||
| 	DCCP_RESET_CODE_CONNECTION_REFUSED, | ||||
| 	DCCP_RESET_CODE_BAD_SERVICE_CODE, | ||||
| 	DCCP_RESET_CODE_TOO_BUSY, | ||||
| 	DCCP_RESET_CODE_BAD_INIT_COOKIE, | ||||
| 	DCCP_RESET_CODE_AGGRESSION_PENALTY, | ||||
| }; | ||||
| 
 | ||||
| /* DCCP options */ | ||||
| enum { | ||||
| 	DCCPO_PADDING = 0, | ||||
| 	DCCPO_MANDATORY = 1, | ||||
| 	DCCPO_MIN_RESERVED = 3, | ||||
| 	DCCPO_MAX_RESERVED = 31, | ||||
| 	DCCPO_NDP_COUNT = 37, | ||||
| 	DCCPO_ACK_VECTOR_0 = 38, | ||||
| 	DCCPO_ACK_VECTOR_1 = 39, | ||||
| 	DCCPO_TIMESTAMP = 41, | ||||
| 	DCCPO_TIMESTAMP_ECHO = 42, | ||||
| 	DCCPO_ELAPSED_TIME = 43, | ||||
| 	DCCPO_MAX = 45, | ||||
| 	DCCPO_MIN_CCID_SPECIFIC = 128, | ||||
| 	DCCPO_MAX_CCID_SPECIFIC = 255, | ||||
| }; | ||||
| 
 | ||||
| /* DCCP features */ | ||||
| enum { | ||||
| 	DCCPF_RESERVED = 0, | ||||
| 	DCCPF_SEQUENCE_WINDOW = 3, | ||||
| 	DCCPF_SEND_ACK_VECTOR = 6, | ||||
| 	DCCPF_SEND_NDP_COUNT = 7, | ||||
| 	/* 10-127 reserved */ | ||||
| 	DCCPF_MIN_CCID_SPECIFIC = 128, | ||||
| 	DCCPF_MAX_CCID_SPECIFIC = 255, | ||||
| }; | ||||
| 
 | ||||
| /* initial values for each feature */ | ||||
| #define DCCPF_INITIAL_SEQUENCE_WINDOW		100 | ||||
| /* FIXME: for now we're using CCID 3 (TFRC) */ | ||||
| #define DCCPF_INITIAL_CCID			3 | ||||
| #define DCCPF_INITIAL_SEND_ACK_VECTOR		0 | ||||
| /* FIXME: for now we're default to 1 but it should really be 0 */ | ||||
| #define DCCPF_INITIAL_SEND_NDP_COUNT		1 | ||||
| 
 | ||||
| #define DCCP_NDP_LIMIT 0xFFFFFF | ||||
| 
 | ||||
| /**
 | ||||
|   * struct dccp_options - option values for a DCCP connection | ||||
|   *	@dccpo_sequence_window - Sequence Window Feature (section 7.5.2) | ||||
|   *	@dccpo_ccid - Congestion Control Id (CCID) (section 10) | ||||
|   *	@dccpo_send_ack_vector - Send Ack Vector Feature (section 11.5) | ||||
|   *	@dccpo_send_ndp_count - Send NDP Count Feature (7.7.2) | ||||
|   */ | ||||
| struct dccp_options { | ||||
| 	__u64	dccpo_sequence_window; | ||||
| 	__u8	dccpo_ccid; | ||||
| 	__u8	dccpo_send_ack_vector; | ||||
| 	__u8	dccpo_send_ndp_count; | ||||
| }; | ||||
| 
 | ||||
| extern void __dccp_options_init(struct dccp_options *dccpo); | ||||
| extern void dccp_options_init(struct dccp_options *dccpo); | ||||
| extern int dccp_parse_options(struct sock *sk, struct sk_buff *skb); | ||||
| 
 | ||||
| struct dccp_request_sock { | ||||
| 	struct inet_request_sock dreq_inet_rsk; | ||||
| 	__u64			 dreq_iss; | ||||
| 	__u64			 dreq_isr; | ||||
| 	__u32			 dreq_service; | ||||
| }; | ||||
| 
 | ||||
| static inline struct dccp_request_sock *dccp_rsk(const struct request_sock *req) | ||||
| { | ||||
| 	return (struct dccp_request_sock *)req; | ||||
| } | ||||
| 
 | ||||
| /* Read about the ECN nonce to see why it is 253 */ | ||||
| #define DCCP_MAX_ACK_VECTOR_LEN 253 | ||||
| 
 | ||||
| struct dccp_options_received { | ||||
| 	u32	dccpor_ndp:24, | ||||
| 		dccpor_ack_vector_len:8; | ||||
| 	u32	dccpor_ack_vector_idx:10; | ||||
| 	/* 22 bits hole, try to pack */ | ||||
| 	u32	dccpor_timestamp; | ||||
| 	u32	dccpor_timestamp_echo; | ||||
| 	u32	dccpor_elapsed_time; | ||||
| }; | ||||
| 
 | ||||
| struct ccid; | ||||
| 
 | ||||
| enum dccp_role { | ||||
| 	DCCP_ROLE_UNDEFINED, | ||||
| 	DCCP_ROLE_LISTEN, | ||||
| 	DCCP_ROLE_CLIENT, | ||||
| 	DCCP_ROLE_SERVER, | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * struct dccp_sock - DCCP socket state | ||||
|  * | ||||
|  * @dccps_swl - sequence number window low | ||||
|  * @dccps_swh - sequence number window high | ||||
|  * @dccps_awl - acknowledgement number window low | ||||
|  * @dccps_awh - acknowledgement number window high | ||||
|  * @dccps_iss - initial sequence number sent | ||||
|  * @dccps_isr - initial sequence number received | ||||
|  * @dccps_osr - first OPEN sequence number received | ||||
|  * @dccps_gss - greatest sequence number sent | ||||
|  * @dccps_gsr - greatest valid sequence number received | ||||
|  * @dccps_gar - greatest valid ack number received on a non-Sync; initialized to %dccps_iss | ||||
|  * @dccps_timestamp_time - time of latest TIMESTAMP option | ||||
|  * @dccps_timestamp_echo - latest timestamp received on a TIMESTAMP option | ||||
|  * @dccps_ext_header_len - network protocol overhead (IP/IPv6 options) | ||||
|  * @dccps_pmtu_cookie - Last pmtu seen by socket | ||||
|  * @dccps_avg_packet_size - FIXME: has to be set by the app thru some setsockopt or ioctl, CCID3 uses it | ||||
|  * @dccps_role - Role of this sock, one of %dccp_role | ||||
|  * @dccps_ndp_count - number of Non Data Packets since last data packet | ||||
|  * @dccps_hc_rx_ackpkts - receiver half connection acked packets | ||||
|  */ | ||||
| struct dccp_sock { | ||||
| 	/* inet_connection_sock has to be the first member of dccp_sock */ | ||||
| 	struct inet_connection_sock	dccps_inet_connection; | ||||
| 	__u64				dccps_swl; | ||||
| 	__u64				dccps_swh; | ||||
| 	__u64				dccps_awl; | ||||
| 	__u64				dccps_awh; | ||||
| 	__u64				dccps_iss; | ||||
| 	__u64				dccps_isr; | ||||
| 	__u64				dccps_osr; | ||||
| 	__u64				dccps_gss; | ||||
| 	__u64				dccps_gsr; | ||||
| 	__u64				dccps_gar; | ||||
| 	unsigned long			dccps_service; | ||||
| 	unsigned long			dccps_timestamp_time; | ||||
| 	__u32				dccps_timestamp_echo; | ||||
| 	__u32				dccps_avg_packet_size; | ||||
| 	unsigned long			dccps_ndp_count; | ||||
| 	__u16				dccps_ext_header_len; | ||||
| 	__u32				dccps_pmtu_cookie; | ||||
| 	__u32				dccps_mss_cache; | ||||
| 	struct dccp_options		dccps_options; | ||||
| 	struct dccp_ackpkts		*dccps_hc_rx_ackpkts; | ||||
| 	void				*dccps_hc_rx_ccid_private; | ||||
| 	void				*dccps_hc_tx_ccid_private; | ||||
| 	struct ccid			*dccps_hc_rx_ccid; | ||||
| 	struct ccid			*dccps_hc_tx_ccid; | ||||
| 	struct dccp_options_received	dccps_options_received; | ||||
| 	enum dccp_role			dccps_role:2; | ||||
| }; | ||||
|   | ||||
| static inline struct dccp_sock *dccp_sk(const struct sock *sk) | ||||
| { | ||||
| 	return (struct dccp_sock *)sk; | ||||
| } | ||||
| 
 | ||||
| static inline const char *dccp_role(const struct sock *sk) | ||||
| { | ||||
| 	switch (dccp_sk(sk)->dccps_role) { | ||||
| 	case DCCP_ROLE_UNDEFINED: return "undefined"; | ||||
| 	case DCCP_ROLE_LISTEN:	  return "listen"; | ||||
| 	case DCCP_ROLE_SERVER:	  return "server"; | ||||
| 	case DCCP_ROLE_CLIENT:	  return "client"; | ||||
| 	} | ||||
| 	return NULL; | ||||
| } | ||||
| 
 | ||||
| #endif /* _LINUX_DCCP_H */ | ||||
|  | @ -32,6 +32,7 @@ enum { | |||
|   IPPROTO_PUP = 12,		/* PUP protocol				*/ | ||||
|   IPPROTO_UDP = 17,		/* User Datagram Protocol		*/ | ||||
|   IPPROTO_IDP = 22,		/* XNS IDP protocol			*/ | ||||
|   IPPROTO_DCCP = 33,		/* Datagram Congestion Control Protocol */ | ||||
|   IPPROTO_RSVP = 46,		/* RSVP protocol			*/ | ||||
|   IPPROTO_GRE = 47,		/* Cisco GRE tunnels (rfc 1701,1702)	*/ | ||||
| 
 | ||||
|  |  | |||
|  | @ -84,6 +84,7 @@ enum sock_type { | |||
| 	SOCK_RAW	= 3, | ||||
| 	SOCK_RDM	= 4, | ||||
| 	SOCK_SEQPACKET	= 5, | ||||
| 	SOCK_DCCP	= 6, | ||||
| 	SOCK_PACKET	= 10, | ||||
| }; | ||||
| 
 | ||||
|  |  | |||
|  | @ -271,6 +271,7 @@ struct ucred { | |||
| #define SOL_IRDA        266 | ||||
| #define SOL_NETBEUI	267 | ||||
| #define SOL_LLC		268 | ||||
| #define SOL_DCCP	269 | ||||
| 
 | ||||
| /* IPX options */ | ||||
| #define IPX_TYPE	1 | ||||
|  |  | |||
|  | @ -147,6 +147,7 @@ source "net/bridge/netfilter/Kconfig" | |||
| 
 | ||||
| endif | ||||
| 
 | ||||
| source "net/dccp/Kconfig" | ||||
| source "net/sctp/Kconfig" | ||||
| source "net/atm/Kconfig" | ||||
| source "net/bridge/Kconfig" | ||||
|  |  | |||
|  | @ -42,6 +42,7 @@ obj-$(CONFIG_ATM)		+= atm/ | |||
| obj-$(CONFIG_DECNET)		+= decnet/ | ||||
| obj-$(CONFIG_ECONET)		+= econet/ | ||||
| obj-$(CONFIG_VLAN_8021Q)	+= 8021q/ | ||||
| obj-$(CONFIG_IP_DCCP)		+= dccp/ | ||||
| obj-$(CONFIG_IP_SCTP)		+= sctp/ | ||||
| 
 | ||||
| ifeq ($(CONFIG_NET),y) | ||||
|  |  | |||
							
								
								
									
										24
									
								
								net/dccp/Kconfig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								net/dccp/Kconfig
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,24 @@ | |||
| menu "DCCP Configuration (EXPERIMENTAL)" | ||||
| 	depends on INET && EXPERIMENTAL | ||||
| 
 | ||||
| config IP_DCCP | ||||
| 	tristate "The DCCP Protocol (EXPERIMENTAL)" | ||||
| 	---help--- | ||||
| 	  Datagram Congestion Control Protocol | ||||
| 
 | ||||
| 	  From draft-ietf-dccp-spec-11 <http://www.icir.org/kohler/dcp/draft-ietf-dccp-spec-11.txt>. | ||||
| 
 | ||||
| 	  The Datagram Congestion Control Protocol (DCCP) is a transport | ||||
| 	  protocol that implements bidirectional, unicast connections of | ||||
| 	  congestion-controlled, unreliable datagrams. It should be suitable | ||||
| 	  for use by applications such as streaming media, Internet telephony, | ||||
| 	  and on-line games | ||||
| 
 | ||||
| 	  To compile this protocol support as a module, choose M here: the | ||||
| 	  module will be called dccp. | ||||
| 
 | ||||
| 	  If in doubt, say N. | ||||
| 
 | ||||
| source "net/dccp/ccids/Kconfig" | ||||
| 
 | ||||
| endmenu | ||||
							
								
								
									
										5
									
								
								net/dccp/Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								net/dccp/Makefile
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,5 @@ | |||
| obj-$(CONFIG_IP_DCCP) += dccp.o | ||||
| 
 | ||||
| dccp-y := ccid.o input.o ipv4.o minisocks.o options.o output.o proto.o timer.o | ||||
| 
 | ||||
| obj-y += ccids/ | ||||
							
								
								
									
										139
									
								
								net/dccp/ccid.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										139
									
								
								net/dccp/ccid.c
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,139 @@ | |||
| /*
 | ||||
|  *  net/dccp/ccid.c | ||||
|  * | ||||
|  *  An implementation of the DCCP protocol | ||||
|  *  Arnaldo Carvalho de Melo <acme@conectiva.com.br> | ||||
|  * | ||||
|  *  CCID infrastructure | ||||
|  * | ||||
|  *	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 "ccid.h" | ||||
| 
 | ||||
| static struct ccid *ccids[CCID_MAX]; | ||||
| #if defined(CONFIG_SMP) || defined(CONFIG_PREEMPT) | ||||
| static atomic_t ccids_lockct = ATOMIC_INIT(0); | ||||
| static DEFINE_SPINLOCK(ccids_lock); | ||||
| 
 | ||||
| /*
 | ||||
|  * The strategy is: modifications ccids vector are short, do not sleep and | ||||
|  * veeery rare, but read access should be free of any exclusive locks. | ||||
|  */ | ||||
| static void ccids_write_lock(void) | ||||
| { | ||||
| 	spin_lock(&ccids_lock); | ||||
| 	while (atomic_read(&ccids_lockct) != 0) { | ||||
| 		spin_unlock(&ccids_lock); | ||||
| 		yield(); | ||||
| 		spin_lock(&ccids_lock); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static inline void ccids_write_unlock(void) | ||||
| { | ||||
| 	spin_unlock(&ccids_lock); | ||||
| } | ||||
| 
 | ||||
| static inline void ccids_read_lock(void) | ||||
| { | ||||
| 	atomic_inc(&ccids_lockct); | ||||
| 	spin_unlock_wait(&ccids_lock); | ||||
| } | ||||
| 
 | ||||
| static inline void ccids_read_unlock(void) | ||||
| { | ||||
| 	atomic_dec(&ccids_lockct); | ||||
| } | ||||
| 
 | ||||
| #else | ||||
| #define ccids_write_lock() do { } while(0) | ||||
| #define ccids_write_unlock() do { } while(0) | ||||
| #define ccids_read_lock() do { } while(0) | ||||
| #define ccids_read_unlock() do { } while(0) | ||||
| #endif | ||||
| 
 | ||||
| int ccid_register(struct ccid *ccid) | ||||
| { | ||||
| 	int err; | ||||
| 
 | ||||
| 	if (ccid->ccid_init == NULL) | ||||
| 		return -1; | ||||
| 
 | ||||
| 	ccids_write_lock(); | ||||
| 	err = -EEXIST; | ||||
| 	if (ccids[ccid->ccid_id] == NULL) { | ||||
| 		ccids[ccid->ccid_id] = ccid; | ||||
| 		err = 0; | ||||
| 	} | ||||
| 	ccids_write_unlock(); | ||||
| 	if (err == 0) | ||||
| 		pr_info("CCID: Registered CCID %d (%s)\n", | ||||
| 			ccid->ccid_id, ccid->ccid_name); | ||||
| 	return err; | ||||
| } | ||||
| 
 | ||||
| EXPORT_SYMBOL_GPL(ccid_register); | ||||
| 
 | ||||
| int ccid_unregister(struct ccid *ccid) | ||||
| { | ||||
| 	ccids_write_lock(); | ||||
| 	ccids[ccid->ccid_id] = NULL; | ||||
| 	ccids_write_unlock(); | ||||
| 	pr_info("CCID: Unregistered CCID %d (%s)\n", | ||||
| 		ccid->ccid_id, ccid->ccid_name); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| EXPORT_SYMBOL_GPL(ccid_unregister); | ||||
| 
 | ||||
| struct ccid *ccid_init(unsigned char id, struct sock *sk) | ||||
| { | ||||
| 	struct ccid *ccid; | ||||
| 
 | ||||
| #ifdef CONFIG_KMOD | ||||
| 	if (ccids[id] == NULL) | ||||
| 		request_module("net-dccp-ccid-%d", id); | ||||
| #endif | ||||
| 	ccids_read_lock(); | ||||
| 
 | ||||
| 	ccid = ccids[id]; | ||||
| 	if (ccid == NULL) | ||||
| 		goto out; | ||||
| 
 | ||||
| 	if (!try_module_get(ccid->ccid_owner)) | ||||
| 		goto out_err; | ||||
| 
 | ||||
| 	if (ccid->ccid_init(sk) != 0) | ||||
| 		goto out_module_put; | ||||
| out: | ||||
| 	ccids_read_unlock(); | ||||
| 	return ccid; | ||||
| out_module_put: | ||||
| 	module_put(ccid->ccid_owner); | ||||
| out_err: | ||||
| 	ccid = NULL; | ||||
| 	goto out; | ||||
| } | ||||
| 
 | ||||
| EXPORT_SYMBOL_GPL(ccid_init); | ||||
| 
 | ||||
| void ccid_exit(struct ccid *ccid, struct sock *sk) | ||||
| { | ||||
| 	if (ccid == NULL) | ||||
| 		return; | ||||
| 
 | ||||
| 	ccids_read_lock(); | ||||
| 
 | ||||
| 	if (ccids[ccid->ccid_id] != NULL) { | ||||
| 		if (ccid->ccid_exit != NULL) | ||||
| 			ccid->ccid_exit(sk); | ||||
| 		module_put(ccid->ccid_owner); | ||||
| 	} | ||||
| 
 | ||||
| 	ccids_read_unlock(); | ||||
| } | ||||
| 
 | ||||
| EXPORT_SYMBOL_GPL(ccid_exit); | ||||
							
								
								
									
										156
									
								
								net/dccp/ccid.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										156
									
								
								net/dccp/ccid.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,156 @@ | |||
| #ifndef _CCID_H | ||||
| #define _CCID_H | ||||
| /*
 | ||||
|  *  net/dccp/ccid.h | ||||
|  * | ||||
|  *  An implementation of the DCCP protocol | ||||
|  *  Arnaldo Carvalho de Melo <acme@conectiva.com.br> | ||||
|  * | ||||
|  *  CCID infrastructure | ||||
|  * | ||||
|  *	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 <net/sock.h> | ||||
| #include <linux/dccp.h> | ||||
| #include <linux/list.h> | ||||
| #include <linux/module.h> | ||||
| 
 | ||||
| #define CCID_MAX 255 | ||||
| 
 | ||||
| struct ccid { | ||||
| 	unsigned char	ccid_id; | ||||
| 	const char	*ccid_name; | ||||
| 	struct module	*ccid_owner; | ||||
| 	int		(*ccid_init)(struct sock *sk); | ||||
| 	void		(*ccid_exit)(struct sock *sk); | ||||
| 	int		(*ccid_hc_rx_init)(struct sock *sk); | ||||
| 	int		(*ccid_hc_tx_init)(struct sock *sk); | ||||
| 	void		(*ccid_hc_rx_exit)(struct sock *sk); | ||||
| 	void		(*ccid_hc_tx_exit)(struct sock *sk); | ||||
| 	void		(*ccid_hc_rx_packet_recv)(struct sock *sk, struct sk_buff *skb); | ||||
| 	int		(*ccid_hc_rx_parse_options)(struct sock *sk, | ||||
| 						    unsigned char option, | ||||
| 						    unsigned char len, u16 idx, | ||||
| 						    unsigned char* value); | ||||
| 	void		(*ccid_hc_rx_insert_options)(struct sock *sk, struct sk_buff *skb); | ||||
| 	void		(*ccid_hc_tx_insert_options)(struct sock *sk, struct sk_buff *skb); | ||||
| 	void		(*ccid_hc_tx_packet_recv)(struct sock *sk, struct sk_buff *skb); | ||||
| 	int		(*ccid_hc_tx_parse_options)(struct sock *sk, | ||||
| 						    unsigned char option, | ||||
| 						    unsigned char len, u16 idx, | ||||
| 						    unsigned char* value); | ||||
| 	int		(*ccid_hc_tx_send_packet)(struct sock *sk, | ||||
| 						  struct sk_buff *skb, int len, | ||||
| 						  long *delay); | ||||
| 	void		(*ccid_hc_tx_packet_sent)(struct sock *sk, int more, int len); | ||||
| }; | ||||
| 
 | ||||
| extern int	   ccid_register(struct ccid *ccid); | ||||
| extern int	   ccid_unregister(struct ccid *ccid); | ||||
| 
 | ||||
| extern struct ccid *ccid_init(unsigned char id, struct sock *sk); | ||||
| extern void	   ccid_exit(struct ccid *ccid, struct sock *sk); | ||||
| 
 | ||||
| static inline void __ccid_get(struct ccid *ccid) | ||||
| { | ||||
| 	__module_get(ccid->ccid_owner); | ||||
| } | ||||
| 
 | ||||
| static inline int ccid_hc_tx_send_packet(struct ccid *ccid, struct sock *sk, | ||||
| 					 struct sk_buff *skb, int len, | ||||
| 					 long *delay) | ||||
| { | ||||
| 	int rc = 0; | ||||
| 	if (ccid->ccid_hc_tx_send_packet != NULL) | ||||
| 		rc = ccid->ccid_hc_tx_send_packet(sk, skb, len, delay); | ||||
| 	return rc; | ||||
| } | ||||
| 
 | ||||
| static inline void ccid_hc_tx_packet_sent(struct ccid *ccid, struct sock *sk, | ||||
| 					  int more, int len) | ||||
| { | ||||
| 	if (ccid->ccid_hc_tx_packet_sent != NULL) | ||||
| 		ccid->ccid_hc_tx_packet_sent(sk, more, len); | ||||
| } | ||||
| 
 | ||||
| static inline int ccid_hc_rx_init(struct ccid *ccid, struct sock *sk) | ||||
| { | ||||
| 	int rc = 0; | ||||
| 	if (ccid->ccid_hc_rx_init != NULL) | ||||
| 		rc = ccid->ccid_hc_rx_init(sk); | ||||
| 	return rc; | ||||
| } | ||||
| 
 | ||||
| static inline int ccid_hc_tx_init(struct ccid *ccid, struct sock *sk) | ||||
| { | ||||
| 	int rc = 0; | ||||
| 	if (ccid->ccid_hc_tx_init != NULL) | ||||
| 		rc = ccid->ccid_hc_tx_init(sk); | ||||
| 	return rc; | ||||
| } | ||||
| 
 | ||||
| static inline void ccid_hc_rx_exit(struct ccid *ccid, struct sock *sk) | ||||
| { | ||||
| 	if (ccid->ccid_hc_rx_exit != NULL) | ||||
| 		ccid->ccid_hc_rx_exit(sk); | ||||
| } | ||||
| 
 | ||||
| static inline void ccid_hc_tx_exit(struct ccid *ccid, struct sock *sk) | ||||
| { | ||||
| 	if (ccid->ccid_hc_tx_exit != NULL) | ||||
| 		ccid->ccid_hc_tx_exit(sk); | ||||
| } | ||||
| 
 | ||||
| static inline void ccid_hc_rx_packet_recv(struct ccid *ccid, struct sock *sk, | ||||
| 					  struct sk_buff *skb) | ||||
| { | ||||
| 	if (ccid->ccid_hc_rx_packet_recv != NULL) | ||||
| 		ccid->ccid_hc_rx_packet_recv(sk, skb); | ||||
| } | ||||
| 
 | ||||
| static inline void ccid_hc_tx_packet_recv(struct ccid *ccid, struct sock *sk, | ||||
| 					  struct sk_buff *skb) | ||||
| { | ||||
| 	if (ccid->ccid_hc_tx_packet_recv != NULL) | ||||
| 		ccid->ccid_hc_tx_packet_recv(sk, skb); | ||||
| } | ||||
| 
 | ||||
| static inline int ccid_hc_tx_parse_options(struct ccid *ccid, struct sock *sk, | ||||
| 					   unsigned char option, | ||||
| 					   unsigned char len, u16 idx, | ||||
| 					   unsigned char* value) | ||||
| { | ||||
| 	int rc = 0; | ||||
| 	if (ccid->ccid_hc_tx_parse_options != NULL) | ||||
| 		rc = ccid->ccid_hc_tx_parse_options(sk, option, len, idx, value); | ||||
| 	return rc; | ||||
| } | ||||
| 
 | ||||
| static inline int ccid_hc_rx_parse_options(struct ccid *ccid, struct sock *sk, | ||||
| 					   unsigned char option, | ||||
| 					   unsigned char len, u16 idx, | ||||
| 					   unsigned char* value) | ||||
| { | ||||
| 	int rc = 0; | ||||
| 	if (ccid->ccid_hc_rx_parse_options != NULL) | ||||
| 		rc = ccid->ccid_hc_rx_parse_options(sk, option, len, idx, value); | ||||
| 	return rc; | ||||
| } | ||||
| 
 | ||||
| static inline void ccid_hc_tx_insert_options(struct ccid *ccid, struct sock *sk, | ||||
| 					     struct sk_buff *skb) | ||||
| { | ||||
| 	if (ccid->ccid_hc_tx_insert_options != NULL) | ||||
| 		ccid->ccid_hc_tx_insert_options(sk, skb); | ||||
| } | ||||
| 
 | ||||
| static inline void ccid_hc_rx_insert_options(struct ccid *ccid, struct sock *sk, | ||||
| 					     struct sk_buff *skb) | ||||
| { | ||||
| 	if (ccid->ccid_hc_rx_insert_options != NULL) | ||||
| 		ccid->ccid_hc_rx_insert_options(sk, skb); | ||||
| } | ||||
| #endif /* _CCID_H */ | ||||
							
								
								
									
										25
									
								
								net/dccp/ccids/Kconfig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								net/dccp/ccids/Kconfig
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,25 @@ | |||
| menu "DCCP CCIDs Configuration (EXPERIMENTAL)" | ||||
| 	depends on IP_DCCP && EXPERIMENTAL | ||||
| 
 | ||||
| config IP_DCCP_CCID3 | ||||
| 	tristate "CCID3 (TFRC) (EXPERIMENTAL)" | ||||
| 	depends on IP_DCCP | ||||
| 	---help--- | ||||
| 	  CCID 3 denotes TCP-Friendly Rate Control (TFRC), an equation-based | ||||
| 	  rate-controlled congestion control mechanism.  TFRC is designed to | ||||
| 	  be reasonably fair when competing for bandwidth with TCP-like flows, | ||||
| 	  where a flow is "reasonably fair" if its sending rate is generally | ||||
| 	  within a factor of two of the sending rate of a TCP flow under the | ||||
| 	  same conditions.  However, TFRC has a much lower variation of | ||||
| 	  throughput over time compared with TCP, which makes CCID 3 more | ||||
| 	  suitable than CCID 2 for applications such streaming media where a | ||||
| 	  relatively smooth sending rate is of importance. | ||||
| 
 | ||||
| 	  CCID 3 is further described in [CCID 3 PROFILE]. The TFRC | ||||
| 	  congestion control algorithms were initially described in RFC 3448. | ||||
| 
 | ||||
| 	  This text was extracted from draft-ietf-dccp-spec-11.txt. | ||||
| 	   | ||||
| 	  If in doubt, say M. | ||||
| 
 | ||||
| endmenu | ||||
							
								
								
									
										3
									
								
								net/dccp/ccids/Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								net/dccp/ccids/Makefile
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,3 @@ | |||
| obj-$(CONFIG_IP_DCCP_CCID3) += dccp_ccid3.o | ||||
| 
 | ||||
| dccp_ccid3-y := ccid3.o | ||||
							
								
								
									
										2164
									
								
								net/dccp/ccids/ccid3.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2164
									
								
								net/dccp/ccids/ccid3.c
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										137
									
								
								net/dccp/ccids/ccid3.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										137
									
								
								net/dccp/ccids/ccid3.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,137 @@ | |||
| /*
 | ||||
|  *  net/dccp/ccids/ccid3.h | ||||
|  * | ||||
|  *  Copyright (c) 2005 The University of Waikato, Hamilton, New Zealand. | ||||
|  * | ||||
|  *  An implementation of the DCCP protocol | ||||
|  * | ||||
|  *  This code has been developed by the University of Waikato WAND | ||||
|  *  research group. For further information please see http://www.wand.net.nz/
 | ||||
|  *  or e-mail Ian McDonald - iam4@cs.waikato.ac.nz | ||||
|  * | ||||
|  *  This code also uses code from Lulea University, rereleased as GPL by its | ||||
|  *  authors: | ||||
|  *  Copyright (c) 2003 Nils-Erik Mattsson, Joacim Haggmark, Magnus Erixzon | ||||
|  * | ||||
|  *  Changes to meet Linux coding standards, to make it meet latest ccid3 draft | ||||
|  *  and to make it work as a loadable module in the DCCP stack written by | ||||
|  *  Arnaldo Carvalho de Melo <acme@conectiva.com.br>. | ||||
|  * | ||||
|  *  Copyright (c) 2005 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. | ||||
|  * | ||||
|  *  This program is distributed in the hope that it will be useful, | ||||
|  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  *  GNU General Public License for more details. | ||||
|  * | ||||
|  *  You should have received a copy of the GNU General Public License | ||||
|  *  along with this program; if not, write to the Free Software | ||||
|  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||||
|  */ | ||||
| #ifndef _DCCP_CCID3_H_ | ||||
| #define _DCCP_CCID3_H_ | ||||
| 
 | ||||
| #include <linux/types.h> | ||||
| #include <linux/list.h> | ||||
| #include <linux/timer.h> | ||||
| 
 | ||||
| struct ccid3_tx_hist_entry { | ||||
| 	struct list_head	ccid3htx_node; | ||||
| 	u64			ccid3htx_seqno:48, | ||||
| 				ccid3htx_win_count:8, | ||||
| 				ccid3htx_sent:1; | ||||
| 	struct timeval		ccid3htx_tstamp; | ||||
| }; | ||||
| 
 | ||||
| struct ccid3_options_received { | ||||
| 	u64 ccid3or_seqno:48, | ||||
| 	    ccid3or_loss_intervals_idx:16; | ||||
| 	u16 ccid3or_loss_intervals_len; | ||||
| 	u32 ccid3or_loss_event_rate; | ||||
| 	u32 ccid3or_receive_rate; | ||||
| }; | ||||
| 
 | ||||
| /** struct ccid3_hc_tx_sock - CCID3 sender half connection congestion control block
 | ||||
|  * | ||||
|   * @ccid3hctx_state - Sender state | ||||
|   * @ccid3hctx_x - Current sending rate | ||||
|   * @ccid3hctx_x_recv - Receive rate | ||||
|   * @ccid3hctx_x_calc - Calculated send (?) rate | ||||
|   * @ccid3hctx_s - Packet size | ||||
|   * @ccid3hctx_rtt - Estimate of current round trip time in usecs | ||||
|   * @@ccid3hctx_p - Current loss event rate (0-1) scaled by 1000000 | ||||
|   * @ccid3hctx_last_win_count - Last window counter sent | ||||
|   * @ccid3hctx_t_last_win_count - Timestamp of earliest packet with last_win_count value sent | ||||
|   * @ccid3hctx_no_feedback_timer - Handle to no feedback timer | ||||
|   * @ccid3hctx_idle - FIXME | ||||
|   * @ccid3hctx_t_ld - Time last doubled during slow start | ||||
|   * @ccid3hctx_t_nom - Nominal send time of next packet | ||||
|   * @ccid3hctx_t_ipi - Interpacket (send) interval | ||||
|   * @ccid3hctx_delta - Send timer delta | ||||
|   * @ccid3hctx_hist - Packet history | ||||
|   */ | ||||
| struct ccid3_hc_tx_sock { | ||||
| 	u32				ccid3hctx_x; | ||||
| 	u32				ccid3hctx_x_recv; | ||||
| 	u32				ccid3hctx_x_calc; | ||||
| 	u16				ccid3hctx_s; | ||||
| 	u32				ccid3hctx_rtt; | ||||
| 	u32				ccid3hctx_p; | ||||
|   	u8				ccid3hctx_state; | ||||
| 	u8				ccid3hctx_last_win_count; | ||||
| 	u8				ccid3hctx_idle; | ||||
| 	struct timeval			ccid3hctx_t_last_win_count; | ||||
| 	struct timer_list		ccid3hctx_no_feedback_timer; | ||||
| 	struct timeval			ccid3hctx_t_ld; | ||||
| 	struct timeval			ccid3hctx_t_nom; | ||||
| 	u32				ccid3hctx_t_ipi; | ||||
| 	u32				ccid3hctx_delta; | ||||
| 	struct list_head		ccid3hctx_hist; | ||||
| 	struct ccid3_options_received	ccid3hctx_options_received; | ||||
| }; | ||||
| 
 | ||||
| struct ccid3_loss_interval_hist_entry { | ||||
| 	struct list_head	ccid3lih_node; | ||||
| 	u64			ccid3lih_seqno:48, | ||||
| 				ccid3lih_win_count:4; | ||||
| 	u32			ccid3lih_interval; | ||||
| }; | ||||
| 
 | ||||
| struct ccid3_rx_hist_entry { | ||||
| 	struct list_head	ccid3hrx_node; | ||||
| 	u64			ccid3hrx_seqno:48, | ||||
| 				ccid3hrx_win_count:4, | ||||
| 				ccid3hrx_type:4; | ||||
| 	u32			ccid3hrx_ndp; /* In fact it is from 8 to 24 bits */ | ||||
| 	struct timeval		ccid3hrx_tstamp; | ||||
| }; | ||||
| 
 | ||||
| struct ccid3_hc_rx_sock { | ||||
|   	u64			ccid3hcrx_seqno_last_counter:48, | ||||
| 				ccid3hcrx_state:8, | ||||
| 				ccid3hcrx_last_counter:4; | ||||
| 	unsigned long		ccid3hcrx_rtt; | ||||
|   	u32			ccid3hcrx_p; | ||||
|   	u32			ccid3hcrx_bytes_recv; | ||||
|   	struct timeval		ccid3hcrx_tstamp_last_feedback; | ||||
|   	struct timeval		ccid3hcrx_tstamp_last_ack; | ||||
| 	struct list_head	ccid3hcrx_hist; | ||||
| 	struct list_head	ccid3hcrx_loss_interval_hist; | ||||
|   	u16			ccid3hcrx_s; | ||||
|   	u32			ccid3hcrx_pinv; | ||||
|   	u32			ccid3hcrx_elapsed_time; | ||||
|   	u32			ccid3hcrx_x_recv; | ||||
| }; | ||||
| 
 | ||||
| #define ccid3_hc_tx_field(s,field) (s->dccps_hc_tx_ccid_private == NULL ? 0 : \ | ||||
| 				    ((struct ccid3_hc_tx_sock *)s->dccps_hc_tx_ccid_private)->ccid3hctx_##field) | ||||
| 
 | ||||
| #define ccid3_hc_rx_field(s,field) (s->dccps_hc_rx_ccid_private == NULL ? 0 : \ | ||||
| 				    ((struct ccid3_hc_rx_sock *)s->dccps_hc_rx_ccid_private)->ccid3hcrx_##field) | ||||
| 
 | ||||
| #endif /* _DCCP_CCID3_H_ */ | ||||
							
								
								
									
										422
									
								
								net/dccp/dccp.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										422
									
								
								net/dccp/dccp.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,422 @@ | |||
| #ifndef _DCCP_H | ||||
| #define _DCCP_H | ||||
| /*
 | ||||
|  *  net/dccp/dccp.h | ||||
|  * | ||||
|  *  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 <net/snmp.h> | ||||
| #include <net/sock.h> | ||||
| #include <net/tcp.h> | ||||
| 
 | ||||
| #define DCCP_DEBUG | ||||
| 
 | ||||
| #ifdef DCCP_DEBUG | ||||
| extern int dccp_debug; | ||||
| 
 | ||||
| #define dccp_pr_debug(format, a...) \ | ||||
| 	do { if (dccp_debug) \ | ||||
| 		printk(KERN_DEBUG "%s: " format, __FUNCTION__ , ##a); \ | ||||
| 	} while (0) | ||||
| #define dccp_pr_debug_cat(format, a...) do { if (dccp_debug) printk(format, ##a); } while (0) | ||||
| #else | ||||
| #define dccp_pr_debug(format, a...) | ||||
| #define dccp_pr_debug_cat(format, a...) | ||||
| #endif | ||||
| 
 | ||||
| extern struct inet_hashinfo dccp_hashinfo; | ||||
| 
 | ||||
| extern atomic_t dccp_orphan_count; | ||||
| extern int dccp_tw_count; | ||||
| extern void dccp_tw_deschedule(struct inet_timewait_sock *tw); | ||||
| 
 | ||||
| extern void dccp_time_wait(struct sock *sk, int state, int timeo); | ||||
| 
 | ||||
| /* FIXME: Right size this */ | ||||
| #define DCCP_MAX_OPT_LEN 128 | ||||
| 
 | ||||
| #define DCCP_MAX_PACKET_HDR 32 | ||||
| 
 | ||||
| #define MAX_DCCP_HEADER  (DCCP_MAX_PACKET_HDR + DCCP_MAX_OPT_LEN + MAX_HEADER) | ||||
| 
 | ||||
| #define DCCP_TIMEWAIT_LEN (60 * HZ) /* how long to wait to destroy TIME-WAIT | ||||
| 				     * state, about 60 seconds */ | ||||
| 
 | ||||
| /* draft-ietf-dccp-spec-11.txt initial RTO value */ | ||||
| #define DCCP_TIMEOUT_INIT ((unsigned)(3 * HZ)) | ||||
| 
 | ||||
| /* Maximal interval between probes for local resources.  */ | ||||
| #define DCCP_RESOURCE_PROBE_INTERVAL ((unsigned)(HZ / 2U)) | ||||
| 
 | ||||
| #define DCCP_RTO_MAX ((unsigned)(120 * HZ)) /* FIXME: using TCP value */ | ||||
| 
 | ||||
| extern struct proto dccp_v4_prot; | ||||
| 
 | ||||
| /* is seq1 < seq2 ? */ | ||||
| static inline const  int before48(const u64 seq1, const u64 seq2) | ||||
| { | ||||
| 	return (const s64)((seq1 << 16) - (seq2 << 16)) < 0; | ||||
| } | ||||
| 
 | ||||
| /* is seq1 > seq2 ? */ | ||||
| static inline const  int after48(const u64 seq1, const u64 seq2) | ||||
| { | ||||
| 	return (const s64)((seq2 << 16) - (seq1 << 16)) < 0; | ||||
| } | ||||
| 
 | ||||
| /* is seq2 <= seq1 <= seq3 ? */ | ||||
| static inline const int between48(const u64 seq1, const u64 seq2, const u64 seq3) | ||||
| { | ||||
| 	return (seq3 << 16) - (seq2 << 16) >= (seq1 << 16) - (seq2 << 16); | ||||
| } | ||||
| 
 | ||||
| static inline u64 max48(const u64 seq1, const u64 seq2) | ||||
| { | ||||
| 	return after48(seq1, seq2) ? seq1 : seq2; | ||||
| } | ||||
| 
 | ||||
| enum { | ||||
| 	DCCP_MIB_NUM = 0, | ||||
| 	DCCP_MIB_ACTIVEOPENS,			/* ActiveOpens */ | ||||
| 	DCCP_MIB_ESTABRESETS,			/* EstabResets */ | ||||
| 	DCCP_MIB_CURRESTAB,			/* CurrEstab */ | ||||
| 	DCCP_MIB_OUTSEGS,			/* OutSegs */  | ||||
| 	DCCP_MIB_OUTRSTS, | ||||
| 	DCCP_MIB_ABORTONTIMEOUT, | ||||
| 	DCCP_MIB_TIMEOUTS, | ||||
| 	DCCP_MIB_ABORTFAILED, | ||||
| 	DCCP_MIB_PASSIVEOPENS, | ||||
| 	DCCP_MIB_ATTEMPTFAILS, | ||||
| 	DCCP_MIB_OUTDATAGRAMS, | ||||
| 	DCCP_MIB_INERRS, | ||||
| 	DCCP_MIB_OPTMANDATORYERROR, | ||||
| 	DCCP_MIB_INVALIDOPT, | ||||
| 	__DCCP_MIB_MAX | ||||
| }; | ||||
| 
 | ||||
| #define DCCP_MIB_MAX	__DCCP_MIB_MAX | ||||
| struct dccp_mib { | ||||
| 	unsigned long	mibs[DCCP_MIB_MAX]; | ||||
| } __SNMP_MIB_ALIGN__; | ||||
| 
 | ||||
| DECLARE_SNMP_STAT(struct dccp_mib, dccp_statistics); | ||||
| #define DCCP_INC_STATS(field)		SNMP_INC_STATS(dccp_statistics, field) | ||||
| #define DCCP_INC_STATS_BH(field)	SNMP_INC_STATS_BH(dccp_statistics, field) | ||||
| #define DCCP_INC_STATS_USER(field) 	SNMP_INC_STATS_USER(dccp_statistics, field) | ||||
| #define DCCP_DEC_STATS(field)		SNMP_DEC_STATS(dccp_statistics, field) | ||||
| #define DCCP_ADD_STATS_BH(field, val)	SNMP_ADD_STATS_BH(dccp_statistics, field, val) | ||||
| #define DCCP_ADD_STATS_USER(field, val)	SNMP_ADD_STATS_USER(dccp_statistics, field, val) | ||||
| 
 | ||||
| extern int  dccp_transmit_skb(struct sock *sk, struct sk_buff *skb); | ||||
| extern int  dccp_retransmit_skb(struct sock *sk, struct sk_buff *skb); | ||||
| 
 | ||||
| extern int dccp_send_response(struct sock *sk); | ||||
| extern void dccp_send_ack(struct sock *sk); | ||||
| extern void dccp_send_delayed_ack(struct sock *sk); | ||||
| extern void dccp_send_sync(struct sock *sk, u64 seq); | ||||
| 
 | ||||
| extern void dccp_init_xmit_timers(struct sock *sk); | ||||
| static inline void dccp_clear_xmit_timers(struct sock *sk) | ||||
| { | ||||
| 	inet_csk_clear_xmit_timers(sk); | ||||
| } | ||||
| 
 | ||||
| extern unsigned int dccp_sync_mss(struct sock *sk, u32 pmtu); | ||||
| 
 | ||||
| extern const char *dccp_packet_name(const int type); | ||||
| extern const char *dccp_state_name(const int state); | ||||
| 
 | ||||
| static inline 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; | ||||
| } | ||||
| 
 | ||||
| static inline 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); | ||||
| } | ||||
| 
 | ||||
| static inline void dccp_openreq_init(struct request_sock *req, | ||||
| 				     struct dccp_sock *dp, | ||||
| 				     struct sk_buff *skb) | ||||
| { | ||||
| 	/*
 | ||||
| 	 * FIXME: fill in the other req fields from the DCCP options | ||||
| 	 * received | ||||
| 	 */ | ||||
| 	inet_rsk(req)->rmt_port = dccp_hdr(skb)->dccph_sport; | ||||
| 	inet_rsk(req)->acked	= 0; | ||||
| 	req->rcv_wnd = 0; | ||||
| } | ||||
| 
 | ||||
| extern void dccp_v4_send_check(struct sock *sk, struct dccp_hdr *dh, int len,  | ||||
| 			       struct sk_buff *skb); | ||||
| extern int dccp_v4_conn_request(struct sock *sk, struct sk_buff *skb); | ||||
| 
 | ||||
| extern struct sock *dccp_create_openreq_child(struct sock *sk, | ||||
| 					      const struct request_sock *req, | ||||
| 					      const struct sk_buff *skb); | ||||
| 
 | ||||
| extern int dccp_v4_do_rcv(struct sock *sk, struct sk_buff *skb); | ||||
| 
 | ||||
| extern void dccp_v4_err(struct sk_buff *skb, u32); | ||||
| 
 | ||||
| extern int dccp_v4_rcv(struct sk_buff *skb); | ||||
| 
 | ||||
| extern struct sock *dccp_v4_request_recv_sock(struct sock *sk, | ||||
| 					      struct sk_buff *skb, | ||||
| 					      struct request_sock *req, | ||||
| 					      struct dst_entry *dst); | ||||
| extern struct sock *dccp_check_req(struct sock *sk, struct sk_buff *skb, | ||||
| 				   struct request_sock *req, | ||||
| 				   struct request_sock **prev); | ||||
| 
 | ||||
| extern int dccp_child_process(struct sock *parent, struct sock *child, | ||||
| 			      struct sk_buff *skb); | ||||
| extern int dccp_rcv_state_process(struct sock *sk, struct sk_buff *skb, | ||||
| 				  struct dccp_hdr *dh, unsigned len); | ||||
| extern int dccp_rcv_established(struct sock *sk, struct sk_buff *skb, | ||||
| 				const struct dccp_hdr *dh, const unsigned len); | ||||
| 
 | ||||
| extern void		dccp_close(struct sock *sk, long timeout); | ||||
| extern struct sk_buff	*dccp_make_response(struct sock *sk, | ||||
| 					    struct dst_entry *dst, | ||||
| 					    struct request_sock *req); | ||||
| 
 | ||||
| extern int	   dccp_connect(struct sock *sk); | ||||
| extern int	   dccp_disconnect(struct sock *sk, int flags); | ||||
| extern int	   dccp_getsockopt(struct sock *sk, int level, int optname, | ||||
| 				   char *optval, int *optlen); | ||||
| extern int	   dccp_ioctl(struct sock *sk, int cmd, unsigned long arg); | ||||
| extern int	   dccp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, | ||||
| 				size_t size); | ||||
| extern int	   dccp_recvmsg(struct kiocb *iocb, struct sock *sk, | ||||
| 				struct msghdr *msg, size_t len, int nonblock, | ||||
| 				int flags, int *addr_len); | ||||
| extern int	   dccp_setsockopt(struct sock *sk, int level, int optname, | ||||
| 				   char *optval, int optlen); | ||||
| extern void	   dccp_shutdown(struct sock *sk, int how); | ||||
| 
 | ||||
| extern int	   dccp_v4_checksum(struct sk_buff *skb); | ||||
| 
 | ||||
| extern int	   dccp_v4_send_reset(struct sock *sk, enum dccp_reset_codes code); | ||||
| extern void	   dccp_send_close(struct sock *sk); | ||||
| 
 | ||||
| struct dccp_skb_cb { | ||||
| 	__u8 dccpd_type; | ||||
| 	__u8 dccpd_reset_code; | ||||
| 	__u8 dccpd_service; | ||||
| 	__u8 dccpd_ccval; | ||||
| 	__u64 dccpd_seq; | ||||
| 	__u64 dccpd_ack_seq; | ||||
| 	int  dccpd_opt_len; | ||||
| }; | ||||
| 
 | ||||
| #define DCCP_SKB_CB(__skb) ((struct dccp_skb_cb *)&((__skb)->cb[0])) | ||||
| 
 | ||||
| static inline int dccp_non_data_packet(const struct sk_buff *skb) | ||||
| { | ||||
| 	const __u8 type = DCCP_SKB_CB(skb)->dccpd_type; | ||||
| 
 | ||||
| 	return type == DCCP_PKT_ACK	 || | ||||
| 	       type == DCCP_PKT_CLOSE	 || | ||||
| 	       type == DCCP_PKT_CLOSEREQ || | ||||
| 	       type == DCCP_PKT_RESET	 || | ||||
| 	       type == DCCP_PKT_SYNC	 || | ||||
| 	       type == DCCP_PKT_SYNCACK; | ||||
| } | ||||
| 
 | ||||
| static inline int dccp_packet_without_ack(const struct sk_buff *skb) | ||||
| { | ||||
| 	const __u8 type = DCCP_SKB_CB(skb)->dccpd_type; | ||||
| 
 | ||||
| 	return type == DCCP_PKT_DATA || type == DCCP_PKT_REQUEST; | ||||
| } | ||||
| 
 | ||||
| #define DCCP_MAX_SEQNO ((((u64)1) << 48) - 1) | ||||
| #define DCCP_PKT_WITHOUT_ACK_SEQ (DCCP_MAX_SEQNO << 2) | ||||
| 
 | ||||
| static inline void dccp_set_seqno(u64 *seqno, u64 value) | ||||
| { | ||||
| 	if (value > DCCP_MAX_SEQNO) | ||||
| 		value -= DCCP_MAX_SEQNO + 1; | ||||
| 	*seqno = value; | ||||
| } | ||||
| 
 | ||||
| static inline u64 dccp_delta_seqno(u64 seqno1, u64 seqno2) | ||||
| { | ||||
| 	return ((seqno2 << 16) - (seqno1 << 16)) >> 16; | ||||
| } | ||||
| 
 | ||||
| static inline void dccp_inc_seqno(u64 *seqno) | ||||
| { | ||||
| 	if (++*seqno > DCCP_MAX_SEQNO) | ||||
| 		*seqno = 0; | ||||
| } | ||||
| 
 | ||||
| static inline void dccp_hdr_set_seq(struct dccp_hdr *dh, const u64 gss) | ||||
| { | ||||
| 	struct dccp_hdr_ext *dhx = (struct dccp_hdr_ext *)((void *)dh + sizeof(*dh)); | ||||
| 
 | ||||
| #if defined(__LITTLE_ENDIAN_BITFIELD) | ||||
| 	dh->dccph_seq	   = htonl((gss >> 32)) >> 8; | ||||
| #elif defined(__BIG_ENDIAN_BITFIELD) | ||||
| 	dh->dccph_seq	   = htonl((gss >> 32)); | ||||
| #else | ||||
| #error  "Adjust your <asm/byteorder.h> defines" | ||||
| #endif | ||||
| 	dhx->dccph_seq_low = htonl(gss & 0xffffffff); | ||||
| } | ||||
| 
 | ||||
| static inline void dccp_hdr_set_ack(struct dccp_hdr_ack_bits *dhack, const u64 gsr) | ||||
| { | ||||
| #if defined(__LITTLE_ENDIAN_BITFIELD) | ||||
| 	dhack->dccph_ack_nr_high = htonl((gsr >> 32)) >> 8; | ||||
| #elif defined(__BIG_ENDIAN_BITFIELD) | ||||
| 	dhack->dccph_ack_nr_high = htonl((gsr >> 32)); | ||||
| #else | ||||
| #error  "Adjust your <asm/byteorder.h> defines" | ||||
| #endif | ||||
| 	dhack->dccph_ack_nr_low  = htonl(gsr & 0xffffffff); | ||||
| } | ||||
| 
 | ||||
| static inline void dccp_update_gsr(struct sock *sk, u64 seq) | ||||
| { | ||||
| 	struct dccp_sock *dp = dccp_sk(sk); | ||||
| 	u64 tmp_gsr; | ||||
| 
 | ||||
| 	dccp_set_seqno(&tmp_gsr, dp->dccps_gsr + 1 - (dp->dccps_options.dccpo_sequence_window / 4)); | ||||
| 	dp->dccps_gsr = seq; | ||||
| 	dccp_set_seqno(&dp->dccps_swl, max48(tmp_gsr, dp->dccps_isr)); | ||||
| 	dccp_set_seqno(&dp->dccps_swh, | ||||
| 		       dp->dccps_gsr + (3 * dp->dccps_options.dccpo_sequence_window) / 4); | ||||
| } | ||||
| 
 | ||||
| static inline void dccp_update_gss(struct sock *sk, u64 seq) | ||||
| { | ||||
| 	struct dccp_sock *dp = dccp_sk(sk); | ||||
| 	u64 tmp_gss; | ||||
| 
 | ||||
| 	dccp_set_seqno(&tmp_gss, dp->dccps_gss - dp->dccps_options.dccpo_sequence_window + 1); | ||||
| 	dp->dccps_awl = max48(tmp_gss, dp->dccps_iss); | ||||
| 	dp->dccps_awh = dp->dccps_gss = seq; | ||||
| } | ||||
| 
 | ||||
| extern void dccp_insert_options(struct sock *sk, struct sk_buff *skb); | ||||
| extern void dccp_insert_option_elapsed_time(struct sock *sk, | ||||
| 					    struct sk_buff *skb, | ||||
| 					    u32 elapsed_time); | ||||
| extern void dccp_insert_option(struct sock *sk, struct sk_buff *skb, | ||||
| 			       unsigned char option, | ||||
| 			       const void *value, unsigned char len); | ||||
| 
 | ||||
| extern struct socket *dccp_ctl_socket; | ||||
| 
 | ||||
| #define DCCP_ACKPKTS_STATE_RECEIVED	0 | ||||
| #define DCCP_ACKPKTS_STATE_ECN_MARKED	(1 << 6) | ||||
| #define DCCP_ACKPKTS_STATE_NOT_RECEIVED	(3 << 6) | ||||
| 
 | ||||
| #define DCCP_ACKPKTS_STATE_MASK		0xC0 /* 11000000 */ | ||||
| #define DCCP_ACKPKTS_LEN_MASK		0x3F /* 00111111 */ | ||||
| 
 | ||||
| /** struct dccp_ackpkts - acknowledgeable packets
 | ||||
|  * | ||||
|  * This data structure is the one defined in the DCCP draft | ||||
|  * Appendix A. | ||||
|  * | ||||
|  * @dccpap_buf_head - circular buffer head | ||||
|  * @dccpap_buf_tail - circular buffer tail | ||||
|  * @dccpap_buf_ackno - ack # of the most recent packet acknoldgeable in the buffer (i.e. %dccpap_buf_head) | ||||
|  * @dccpap_buf_nonce - the one-bit sum of the ECN Nonces on all packets acked by the buffer with State 0 | ||||
|  * | ||||
|  * Additionally, the HC-Receiver must keep some information about the | ||||
|  * Ack Vectors it has recently sent. For each packet sent carrying an | ||||
|  * Ack Vector, it remembers four variables: | ||||
|  * | ||||
|  * @dccpap_ack_seqno - the Sequence Number used for the packet (HC-Receiver seqno) | ||||
|  * @dccpap_ack_ptr - the value of buf_head at the time of acknowledgement. | ||||
|  * @dccpap_ack_ackno - the Acknowledgement Number used for the packet (HC-Sender seqno) | ||||
|  * @dccpap_ack_nonce - the one-bit sum of the ECN Nonces for all State 0. | ||||
|  * | ||||
|  * @dccpap_buf_len - circular buffer length | ||||
|  * @dccpap_buf - circular buffer of acknowledgeable packets | ||||
|  */ | ||||
| struct dccp_ackpkts { | ||||
| 	unsigned int		dccpap_buf_head; | ||||
| 	unsigned int		dccpap_buf_tail; | ||||
| 	u64			dccpap_buf_ackno; | ||||
| 	u64			dccpap_ack_seqno; | ||||
| 	u64			dccpap_ack_ackno; | ||||
| 	unsigned int		dccpap_ack_ptr; | ||||
| 	unsigned int		dccpap_buf_vector_len; | ||||
| 	unsigned int		dccpap_ack_vector_len; | ||||
| 	unsigned int		dccpap_buf_len; | ||||
| 	unsigned long           dccpap_time; | ||||
| 	u8			dccpap_buf_nonce; | ||||
| 	u8			dccpap_ack_nonce; | ||||
| 	u8			dccpap_buf[0]; | ||||
| }; | ||||
| 
 | ||||
| extern struct dccp_ackpkts *dccp_ackpkts_alloc(unsigned int len, int priority); | ||||
| extern void dccp_ackpkts_free(struct dccp_ackpkts *ap); | ||||
| extern int dccp_ackpkts_add(struct dccp_ackpkts *ap, u64 ackno, u8 state); | ||||
| extern void dccp_ackpkts_check_rcv_ackno(struct dccp_ackpkts *ap, | ||||
| 					 struct sock *sk, u64 ackno); | ||||
| 
 | ||||
| #ifdef DCCP_DEBUG | ||||
| extern void dccp_ackvector_print(const u64 ackno, | ||||
| 				 const unsigned char *vector, int len); | ||||
| extern void dccp_ackpkts_print(const struct dccp_ackpkts *ap); | ||||
| #else | ||||
| static inline void dccp_ackvector_print(const u64 ackno, | ||||
| 					const unsigned char *vector, | ||||
| 					int len) { } | ||||
| static inline void dccp_ackpkts_print(const struct dccp_ackpkts *ap) { } | ||||
| #endif | ||||
| 
 | ||||
| #endif /* _DCCP_H */ | ||||
							
								
								
									
										510
									
								
								net/dccp/input.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										510
									
								
								net/dccp/input.c
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,510 @@ | |||
| /*
 | ||||
|  *  net/dccp/input.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 <net/sock.h> | ||||
| 
 | ||||
| #include "ccid.h" | ||||
| #include "dccp.h" | ||||
| 
 | ||||
| static void dccp_fin(struct sock *sk, struct sk_buff *skb) | ||||
| { | ||||
| 	sk->sk_shutdown |= RCV_SHUTDOWN; | ||||
| 	sock_set_flag(sk, SOCK_DONE); | ||||
| 	__skb_pull(skb, dccp_hdr(skb)->dccph_doff * 4); | ||||
| 	__skb_queue_tail(&sk->sk_receive_queue, skb); | ||||
| 	skb_set_owner_r(skb, sk); | ||||
| 	sk->sk_data_ready(sk, 0); | ||||
| } | ||||
| 
 | ||||
| static void dccp_rcv_close(struct sock *sk, struct sk_buff *skb) | ||||
| { | ||||
| 	switch (sk->sk_state) { | ||||
| 	case DCCP_PARTOPEN: | ||||
| 	case DCCP_OPEN: | ||||
| 		dccp_v4_send_reset(sk, DCCP_RESET_CODE_CLOSED); | ||||
| 		dccp_fin(sk, skb); | ||||
| 		dccp_set_state(sk, DCCP_CLOSED); | ||||
| 		break; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static void dccp_rcv_closereq(struct sock *sk, struct sk_buff *skb) | ||||
| { | ||||
| 	/*
 | ||||
| 	 *   Step 7: Check for unexpected packet types | ||||
| 	 *      If (S.is_server and P.type == CloseReq) | ||||
| 	 *	  Send Sync packet acknowledging P.seqno | ||||
| 	 *	  Drop packet and return | ||||
| 	 */ | ||||
| 	if (dccp_sk(sk)->dccps_role != DCCP_ROLE_CLIENT) { | ||||
| 		dccp_send_sync(sk, DCCP_SKB_CB(skb)->dccpd_seq); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	switch (sk->sk_state) { | ||||
| 	case DCCP_PARTOPEN: | ||||
| 	case DCCP_OPEN: | ||||
| 		dccp_set_state(sk, DCCP_CLOSING); | ||||
| 		dccp_send_close(sk); | ||||
| 		break; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static inline void dccp_event_ack_recv(struct sock *sk, struct sk_buff *skb) | ||||
| { | ||||
| 	struct dccp_sock *dp = dccp_sk(sk); | ||||
| 
 | ||||
| 	if (dp->dccps_options.dccpo_send_ack_vector) | ||||
| 		dccp_ackpkts_check_rcv_ackno(dp->dccps_hc_rx_ackpkts, sk, | ||||
| 					     DCCP_SKB_CB(skb)->dccpd_ack_seq); | ||||
| } | ||||
| 
 | ||||
| static int dccp_check_seqno(struct sock *sk, struct sk_buff *skb) | ||||
| { | ||||
| 	const struct dccp_hdr *dh = dccp_hdr(skb); | ||||
| 	struct dccp_sock *dp = dccp_sk(sk); | ||||
| 	u64 lswl = dp->dccps_swl; | ||||
| 	u64 lawl = dp->dccps_awl; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 *   Step 5: Prepare sequence numbers for Sync | ||||
| 	 *     If P.type == Sync or P.type == SyncAck, | ||||
| 	 *	  If S.AWL <= P.ackno <= S.AWH and P.seqno >= S.SWL, | ||||
| 	 *	     / * P is valid, so update sequence number variables | ||||
| 	 *		 accordingly.  After this update, P will pass the tests | ||||
| 	 *		 in Step 6.  A SyncAck is generated if necessary in | ||||
| 	 *		 Step 15 * / | ||||
| 	 *	     Update S.GSR, S.SWL, S.SWH | ||||
| 	 *	  Otherwise, | ||||
| 	 *	     Drop packet and return | ||||
| 	 */ | ||||
| 	if (dh->dccph_type == DCCP_PKT_SYNC ||  | ||||
| 	    dh->dccph_type == DCCP_PKT_SYNCACK) { | ||||
| 		if (between48(DCCP_SKB_CB(skb)->dccpd_ack_seq, dp->dccps_awl, dp->dccps_awh) && | ||||
| 		    !before48(DCCP_SKB_CB(skb)->dccpd_seq, dp->dccps_swl)) | ||||
| 			dccp_update_gsr(sk, DCCP_SKB_CB(skb)->dccpd_seq); | ||||
| 		else | ||||
| 			return -1; | ||||
| 	/*
 | ||||
| 	 *   Step 6: Check sequence numbers | ||||
| 	 *      Let LSWL = S.SWL and LAWL = S.AWL | ||||
| 	 *      If P.type == CloseReq or P.type == Close or P.type == Reset, | ||||
| 	 *	  LSWL := S.GSR + 1, LAWL := S.GAR | ||||
| 	 *      If LSWL <= P.seqno <= S.SWH | ||||
| 	 *	     and (P.ackno does not exist or LAWL <= P.ackno <= S.AWH), | ||||
| 	 *	  Update S.GSR, S.SWL, S.SWH | ||||
| 	 *	  If P.type != Sync, | ||||
| 	 *	     Update S.GAR | ||||
| 	 *      Otherwise, | ||||
| 	 *	  Send Sync packet acknowledging P.seqno | ||||
| 	 *	  Drop packet and return | ||||
| 	 */ | ||||
| 	} else if (dh->dccph_type == DCCP_PKT_CLOSEREQ || | ||||
| 		   dh->dccph_type == DCCP_PKT_CLOSE || | ||||
| 		   dh->dccph_type == DCCP_PKT_RESET) { | ||||
| 		lswl = dp->dccps_gsr; | ||||
| 		dccp_inc_seqno(&lswl); | ||||
| 		lawl = dp->dccps_gar; | ||||
| 	} | ||||
| 
 | ||||
| 	if (between48(DCCP_SKB_CB(skb)->dccpd_seq, lswl, dp->dccps_swh) && | ||||
| 	    (DCCP_SKB_CB(skb)->dccpd_ack_seq == DCCP_PKT_WITHOUT_ACK_SEQ || | ||||
| 	     between48(DCCP_SKB_CB(skb)->dccpd_ack_seq, lawl, dp->dccps_awh))) { | ||||
| 		dccp_update_gsr(sk, DCCP_SKB_CB(skb)->dccpd_seq); | ||||
| 
 | ||||
| 		if (dh->dccph_type != DCCP_PKT_SYNC && | ||||
| 		    DCCP_SKB_CB(skb)->dccpd_ack_seq != DCCP_PKT_WITHOUT_ACK_SEQ) | ||||
| 			dp->dccps_gar = DCCP_SKB_CB(skb)->dccpd_ack_seq; | ||||
| 	} else { | ||||
| 		dccp_pr_debug("Step 6 failed, sending SYNC...\n"); | ||||
| 		dccp_send_sync(sk, DCCP_SKB_CB(skb)->dccpd_seq); | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int dccp_rcv_established(struct sock *sk, struct sk_buff *skb, | ||||
| 			 const struct dccp_hdr *dh, const unsigned len) | ||||
| { | ||||
| 	struct dccp_sock *dp = dccp_sk(sk); | ||||
| 
 | ||||
| 	if (dccp_check_seqno(sk, skb)) | ||||
| 		goto discard; | ||||
| 
 | ||||
| 	if (dccp_parse_options(sk, skb)) | ||||
| 		goto discard; | ||||
| 
 | ||||
| 	if (DCCP_SKB_CB(skb)->dccpd_ack_seq != DCCP_PKT_WITHOUT_ACK_SEQ) | ||||
| 		dccp_event_ack_recv(sk, skb); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * FIXME: check ECN to see if we should use | ||||
| 	 * DCCP_ACKPKTS_STATE_ECN_MARKED | ||||
| 	 */ | ||||
| 	if (dp->dccps_options.dccpo_send_ack_vector) { | ||||
| 		struct dccp_ackpkts *ap = dp->dccps_hc_rx_ackpkts; | ||||
| 
 | ||||
| 		if (dccp_ackpkts_add(dp->dccps_hc_rx_ackpkts, | ||||
| 				     DCCP_SKB_CB(skb)->dccpd_seq, | ||||
| 				     DCCP_ACKPKTS_STATE_RECEIVED)) { | ||||
| 			LIMIT_NETDEBUG(pr_info("DCCP: acknowledgeable packets buffer full!\n")); | ||||
| 			ap->dccpap_ack_seqno = DCCP_MAX_SEQNO + 1; | ||||
| 			inet_csk_schedule_ack(sk); | ||||
| 			inet_csk_reset_xmit_timer(sk, ICSK_TIME_DACK, TCP_DELACK_MIN, TCP_RTO_MAX); | ||||
| 			goto discard; | ||||
| 		} | ||||
| 
 | ||||
| 		/*
 | ||||
| 		 * FIXME: this activation is probably wrong, have to study more | ||||
| 		 * TCP delack machinery and how it fits into DCCP draft, but | ||||
| 		 * for now it kinda "works" 8) | ||||
| 		 */ | ||||
| 		if (!inet_csk_ack_scheduled(sk)) { | ||||
| 			inet_csk_schedule_ack(sk); | ||||
| 			inet_csk_reset_xmit_timer(sk, ICSK_TIME_DACK, 5 * HZ, TCP_RTO_MAX); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	ccid_hc_rx_packet_recv(dp->dccps_hc_rx_ccid, sk, skb); | ||||
| 	ccid_hc_tx_packet_recv(dp->dccps_hc_tx_ccid, sk, skb); | ||||
| 
 | ||||
| 	switch (dccp_hdr(skb)->dccph_type) { | ||||
| 	case DCCP_PKT_DATAACK: | ||||
| 	case DCCP_PKT_DATA: | ||||
| 		/*
 | ||||
| 		 * FIXME: check if sk_receive_queue is full, schedule DATA_DROPPED option | ||||
| 		 * if it is. | ||||
| 		 */ | ||||
| 		__skb_pull(skb, dh->dccph_doff * 4); | ||||
| 		__skb_queue_tail(&sk->sk_receive_queue, skb); | ||||
| 		skb_set_owner_r(skb, sk); | ||||
| 		sk->sk_data_ready(sk, 0); | ||||
| 		return 0; | ||||
| 	case DCCP_PKT_ACK: | ||||
| 		goto discard; | ||||
| 	case DCCP_PKT_RESET: | ||||
| 		/*
 | ||||
| 		 *  Step 9: Process Reset | ||||
| 		 *	If P.type == Reset, | ||||
| 		 *		Tear down connection | ||||
| 		 *		S.state := TIMEWAIT | ||||
| 		 *		Set TIMEWAIT timer | ||||
| 		 *		Drop packet and return | ||||
| 		*/ | ||||
| 		dccp_fin(sk, skb); | ||||
| 		dccp_time_wait(sk, DCCP_TIME_WAIT, 0); | ||||
| 		return 0; | ||||
| 	case DCCP_PKT_CLOSEREQ: | ||||
| 		dccp_rcv_closereq(sk, skb); | ||||
| 		goto discard; | ||||
| 	case DCCP_PKT_CLOSE: | ||||
| 		dccp_rcv_close(sk, skb); | ||||
| 		return 0; | ||||
| 	case DCCP_PKT_REQUEST: | ||||
| 		/* Step 7 
 | ||||
|             	 *   or (S.is_server and P.type == Response) | ||||
| 		 *   or (S.is_client and P.type == Request) | ||||
| 		 *   or (S.state >= OPEN and P.type == Request | ||||
| 		 *	and P.seqno >= S.OSR) | ||||
| 		 *    or (S.state >= OPEN and P.type == Response | ||||
| 		 *	and P.seqno >= S.OSR) | ||||
| 		 *    or (S.state == RESPOND and P.type == Data), | ||||
| 		 *  Send Sync packet acknowledging P.seqno | ||||
| 		 *  Drop packet and return | ||||
| 		 */ | ||||
| 		if (dp->dccps_role != DCCP_ROLE_LISTEN) | ||||
| 			goto send_sync; | ||||
| 		goto check_seq; | ||||
| 	case DCCP_PKT_RESPONSE: | ||||
| 		if (dp->dccps_role != DCCP_ROLE_CLIENT) | ||||
| 			goto send_sync; | ||||
| check_seq: | ||||
| 		if (!before48(DCCP_SKB_CB(skb)->dccpd_seq, dp->dccps_osr)) { | ||||
| send_sync: | ||||
| 			dccp_send_sync(sk, DCCP_SKB_CB(skb)->dccpd_seq); | ||||
| 		} | ||||
| 		break; | ||||
| 	} | ||||
| 
 | ||||
| 	DCCP_INC_STATS_BH(DCCP_MIB_INERRS); | ||||
| discard: | ||||
| 	__kfree_skb(skb); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int dccp_rcv_request_sent_state_process(struct sock *sk, | ||||
| 					       struct sk_buff *skb, | ||||
| 					       const struct dccp_hdr *dh, | ||||
| 					       const unsigned len) | ||||
| { | ||||
| 	/* 
 | ||||
| 	 *  Step 4: Prepare sequence numbers in REQUEST | ||||
| 	 *     If S.state == REQUEST, | ||||
| 	 *	  If (P.type == Response or P.type == Reset) | ||||
| 	 *		and S.AWL <= P.ackno <= S.AWH, | ||||
| 	 *	     / * Set sequence number variables corresponding to the | ||||
| 	 *		other endpoint, so P will pass the tests in Step 6 * / | ||||
| 	 *	     Set S.GSR, S.ISR, S.SWL, S.SWH | ||||
| 	 *	     / * Response processing continues in Step 10; Reset | ||||
| 	 *		processing continues in Step 9 * / | ||||
| 	*/ | ||||
| 	if (dh->dccph_type == DCCP_PKT_RESPONSE) { | ||||
| 		const struct inet_connection_sock *icsk = inet_csk(sk); | ||||
| 		struct dccp_sock *dp = dccp_sk(sk); | ||||
| 
 | ||||
| 		/* Stop the REQUEST timer */ | ||||
| 		inet_csk_clear_xmit_timer(sk, ICSK_TIME_RETRANS); | ||||
| 		BUG_TRAP(sk->sk_send_head != NULL); | ||||
| 		__kfree_skb(sk->sk_send_head); | ||||
| 		sk->sk_send_head = NULL; | ||||
| 
 | ||||
| 		if (!between48(DCCP_SKB_CB(skb)->dccpd_ack_seq, dp->dccps_awl, dp->dccps_awh)) { | ||||
| 			dccp_pr_debug("invalid ackno: S.AWL=%llu, P.ackno=%llu, S.AWH=%llu \n", | ||||
| 				      dp->dccps_awl, DCCP_SKB_CB(skb)->dccpd_ack_seq, dp->dccps_awh); | ||||
| 			goto out_invalid_packet; | ||||
| 		} | ||||
| 
 | ||||
| 		dp->dccps_isr = DCCP_SKB_CB(skb)->dccpd_seq; | ||||
| 		dccp_update_gsr(sk, DCCP_SKB_CB(skb)->dccpd_seq); | ||||
| 
 | ||||
| 		if (ccid_hc_rx_init(dp->dccps_hc_rx_ccid, sk) != 0 || | ||||
| 		    ccid_hc_tx_init(dp->dccps_hc_tx_ccid, sk) != 0) { | ||||
| 			ccid_hc_rx_exit(dp->dccps_hc_rx_ccid, sk); | ||||
| 			ccid_hc_tx_exit(dp->dccps_hc_tx_ccid, sk); | ||||
| 			/* FIXME: send appropriate RESET code */ | ||||
| 			goto out_invalid_packet; | ||||
| 		} | ||||
| 
 | ||||
| 		dccp_sync_mss(sk, dp->dccps_pmtu_cookie); | ||||
| 
 | ||||
| 		/*
 | ||||
| 		 *    Step 10: Process REQUEST state (second part) | ||||
| 		 *       If S.state == REQUEST, | ||||
| 		 *	  / * If we get here, P is a valid Response from the server (see | ||||
| 		 *	     Step 4), and we should move to PARTOPEN state.  PARTOPEN | ||||
| 		 *	     means send an Ack, don't send Data packets, retransmit | ||||
| 		 *	     Acks periodically, and always include any Init Cookie from | ||||
| 		 *	     the Response * / | ||||
| 		 *	  S.state := PARTOPEN | ||||
| 		 *	  Set PARTOPEN timer | ||||
| 		 * 	  Continue with S.state == PARTOPEN | ||||
| 		 *	  / * Step 12 will send the Ack completing the three-way | ||||
| 		 *	     handshake * / | ||||
| 		 */ | ||||
| 		dccp_set_state(sk, DCCP_PARTOPEN); | ||||
| 
 | ||||
| 		/* Make sure socket is routed, for correct metrics. */ | ||||
| 		inet_sk_rebuild_header(sk); | ||||
| 
 | ||||
| 		if (!sock_flag(sk, SOCK_DEAD)) { | ||||
| 			sk->sk_state_change(sk); | ||||
| 			sk_wake_async(sk, 0, POLL_OUT); | ||||
| 		} | ||||
| 
 | ||||
| 		if (sk->sk_write_pending || icsk->icsk_ack.pingpong || | ||||
| 		    icsk->icsk_accept_queue.rskq_defer_accept) { | ||||
| 			/* Save one ACK. Data will be ready after
 | ||||
| 			 * several ticks, if write_pending is set. | ||||
| 			 * | ||||
| 			 * It may be deleted, but with this feature tcpdumps | ||||
| 			 * look so _wonderfully_ clever, that I was not able | ||||
| 			 * to stand against the temptation 8)     --ANK | ||||
| 			 */ | ||||
| 			/*
 | ||||
| 			 * OK, in DCCP we can as well do a similar trick, its | ||||
| 			 * even in the draft, but there is no need for us to | ||||
| 			 * schedule an ack here, as dccp_sendmsg does this for | ||||
| 			 * us, also stated in the draft. -acme | ||||
| 			 */ | ||||
| 			__kfree_skb(skb); | ||||
| 			return 0; | ||||
| 		}  | ||||
| 		dccp_send_ack(sk); | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| out_invalid_packet: | ||||
| 	return 1; /* dccp_v4_do_rcv will send a reset, but...
 | ||||
| 		     FIXME: the reset code should be DCCP_RESET_CODE_PACKET_ERROR  */ | ||||
| } | ||||
| 
 | ||||
| static int dccp_rcv_respond_partopen_state_process(struct sock *sk, | ||||
| 						   struct sk_buff *skb, | ||||
| 						   const struct dccp_hdr *dh, | ||||
| 						   const unsigned len) | ||||
| { | ||||
| 	int queued = 0; | ||||
| 
 | ||||
| 	switch (dh->dccph_type) { | ||||
| 	case DCCP_PKT_RESET: | ||||
| 		inet_csk_clear_xmit_timer(sk, ICSK_TIME_DACK); | ||||
| 		break; | ||||
| 	case DCCP_PKT_DATAACK: | ||||
| 	case DCCP_PKT_ACK: | ||||
| 		/*
 | ||||
| 		 * FIXME: we should be reseting the PARTOPEN (DELACK) timer here, | ||||
| 		 * but only if we haven't used the DELACK timer for something else, | ||||
| 		 * like sending a delayed ack for a TIMESTAMP echo, etc, for now | ||||
| 		 * were not clearing it, sending an extra ACK when there is nothing | ||||
| 		 * else to do in DELACK is not a big deal after all. | ||||
| 		 */ | ||||
| 
 | ||||
| 		/* Stop the PARTOPEN timer */ | ||||
| 		if (sk->sk_state == DCCP_PARTOPEN) | ||||
| 			inet_csk_clear_xmit_timer(sk, ICSK_TIME_DACK); | ||||
| 
 | ||||
| 		dccp_sk(sk)->dccps_osr = DCCP_SKB_CB(skb)->dccpd_seq; | ||||
| 		dccp_set_state(sk, DCCP_OPEN); | ||||
| 
 | ||||
| 		if (dh->dccph_type == DCCP_PKT_DATAACK) { | ||||
| 			dccp_rcv_established(sk, skb, dh, len); | ||||
| 			queued = 1; /* packet was queued (by dccp_rcv_established) */ | ||||
| 		} | ||||
| 		break; | ||||
| 	} | ||||
| 
 | ||||
| 	return queued; | ||||
| } | ||||
| 
 | ||||
| int dccp_rcv_state_process(struct sock *sk, struct sk_buff *skb, | ||||
| 			   struct dccp_hdr *dh, unsigned len) | ||||
| { | ||||
| 	struct dccp_sock *dp = dccp_sk(sk); | ||||
| 	const int old_state = sk->sk_state; | ||||
| 	int queued = 0; | ||||
| 
 | ||||
| 	if (sk->sk_state != DCCP_LISTEN && sk->sk_state != DCCP_REQUESTING) { | ||||
| 		if (dccp_check_seqno(sk, skb)) | ||||
| 			goto discard; | ||||
| 
 | ||||
| 		/*
 | ||||
| 		 * Step 8: Process options and mark acknowledgeable | ||||
| 		 */ | ||||
| 		if (dccp_parse_options(sk, skb)) | ||||
| 			goto discard; | ||||
| 
 | ||||
| 		if (DCCP_SKB_CB(skb)->dccpd_ack_seq != DCCP_PKT_WITHOUT_ACK_SEQ) | ||||
| 			dccp_event_ack_recv(sk, skb); | ||||
| 
 | ||||
| 		ccid_hc_rx_packet_recv(dp->dccps_hc_rx_ccid, sk, skb); | ||||
| 		ccid_hc_tx_packet_recv(dp->dccps_hc_tx_ccid, sk, skb); | ||||
| 
 | ||||
| 		/*
 | ||||
| 		 * FIXME: check ECN to see if we should use | ||||
| 		 * DCCP_ACKPKTS_STATE_ECN_MARKED | ||||
| 		 */ | ||||
| 		if (dp->dccps_options.dccpo_send_ack_vector) { | ||||
| 			if (dccp_ackpkts_add(dp->dccps_hc_rx_ackpkts, | ||||
| 					     DCCP_SKB_CB(skb)->dccpd_seq, | ||||
| 					     DCCP_ACKPKTS_STATE_RECEIVED)) | ||||
| 				goto discard; | ||||
| 			/*
 | ||||
| 			 * FIXME: this activation is probably wrong, have to study more | ||||
| 			 * TCP delack machinery and how it fits into DCCP draft, but | ||||
| 			 * for now it kinda "works" 8) | ||||
| 			 */ | ||||
| 			if (dp->dccps_hc_rx_ackpkts->dccpap_ack_seqno == DCCP_MAX_SEQNO + 1 && | ||||
| 			    !inet_csk_ack_scheduled(sk)) { | ||||
| 				inet_csk_schedule_ack(sk); | ||||
| 				inet_csk_reset_xmit_timer(sk, ICSK_TIME_DACK, TCP_DELACK_MIN, TCP_RTO_MAX); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 *  Step 9: Process Reset | ||||
| 	 *	If P.type == Reset, | ||||
| 	 *		Tear down connection | ||||
| 	 *		S.state := TIMEWAIT | ||||
| 	 *		Set TIMEWAIT timer | ||||
| 	 *		Drop packet and return | ||||
| 	*/ | ||||
| 	if (dh->dccph_type == DCCP_PKT_RESET) { | ||||
| 		/* Queue the equivalent of TCP fin so that dccp_recvmsg exits the loop */ | ||||
| 		dccp_fin(sk, skb); | ||||
| 		dccp_time_wait(sk, DCCP_TIME_WAIT, 0); | ||||
| 		return 0; | ||||
| 		/*
 | ||||
| 		 *   Step 7: Check for unexpected packet types | ||||
| 		 *      If (S.is_server and P.type == CloseReq) | ||||
| 		 *	    or (S.is_server and P.type == Response) | ||||
| 		 *	    or (S.is_client and P.type == Request) | ||||
| 		 *	    or (S.state == RESPOND and P.type == Data), | ||||
| 		 *	  Send Sync packet acknowledging P.seqno | ||||
| 		 *	  Drop packet and return | ||||
| 		 */ | ||||
| 	} else if ((dp->dccps_role != DCCP_ROLE_CLIENT && | ||||
| 		    (dh->dccph_type == DCCP_PKT_RESPONSE || dh->dccph_type == DCCP_PKT_CLOSEREQ)) || | ||||
| 		    (dp->dccps_role == DCCP_ROLE_CLIENT && | ||||
| 		     dh->dccph_type == DCCP_PKT_REQUEST) || | ||||
| 		    (sk->sk_state == DCCP_RESPOND && dh->dccph_type == DCCP_PKT_DATA)) { | ||||
| 		dccp_send_sync(sk, DCCP_SKB_CB(skb)->dccpd_seq); | ||||
| 		goto discard; | ||||
| 	} | ||||
| 
 | ||||
| 	switch (sk->sk_state) { | ||||
| 	case DCCP_CLOSED: | ||||
| 		return 1; | ||||
| 
 | ||||
| 	case DCCP_LISTEN: | ||||
| 		if (dh->dccph_type == DCCP_PKT_ACK || | ||||
| 		    dh->dccph_type == DCCP_PKT_DATAACK) | ||||
| 			return 1; | ||||
| 
 | ||||
| 		if (dh->dccph_type == DCCP_PKT_RESET) | ||||
| 			goto discard; | ||||
| 
 | ||||
| 		if (dh->dccph_type == DCCP_PKT_REQUEST) { | ||||
| 			if (dccp_v4_conn_request(sk, skb) < 0) | ||||
| 				return 1; | ||||
| 
 | ||||
| 			/* FIXME: do congestion control initialization */ | ||||
| 			goto discard; | ||||
| 		} | ||||
| 		goto discard; | ||||
| 
 | ||||
| 	case DCCP_REQUESTING: | ||||
| 		/* FIXME: do congestion control initialization */ | ||||
| 
 | ||||
| 		queued = dccp_rcv_request_sent_state_process(sk, skb, dh, len); | ||||
| 		if (queued >= 0) | ||||
| 			return queued; | ||||
| 
 | ||||
| 		__kfree_skb(skb); | ||||
| 		return 0; | ||||
| 
 | ||||
| 	case DCCP_RESPOND: | ||||
| 	case DCCP_PARTOPEN: | ||||
| 		queued = dccp_rcv_respond_partopen_state_process(sk, skb, dh, len); | ||||
| 		break; | ||||
| 	} | ||||
| 
 | ||||
| 	if (dh->dccph_type == DCCP_PKT_ACK || dh->dccph_type == DCCP_PKT_DATAACK) { | ||||
| 		switch (old_state) { | ||||
| 		case DCCP_PARTOPEN: | ||||
| 			sk->sk_state_change(sk); | ||||
| 			sk_wake_async(sk, 0, POLL_OUT); | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if (!queued) {  | ||||
| discard: | ||||
| 		__kfree_skb(skb); | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
							
								
								
									
										1289
									
								
								net/dccp/ipv4.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1289
									
								
								net/dccp/ipv4.c
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										199
									
								
								net/dccp/minisocks.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										199
									
								
								net/dccp/minisocks.c
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,199 @@ | |||
| /*
 | ||||
|  *  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" | ||||
| 
 | ||||
| void dccp_time_wait(struct sock *sk, int state, int timeo) | ||||
| { | ||||
| 	/* FIXME: Implement */ | ||||
| 	dccp_pr_debug("Want to help? Start here\n"); | ||||
| 	dccp_set_state(sk, state); | ||||
| } | ||||
| 
 | ||||
| /* This is for handling early-kills of TIME_WAIT sockets. */ | ||||
| void dccp_tw_deschedule(struct inet_timewait_sock *tw) | ||||
| { | ||||
| 	dccp_pr_debug("Want to help? Start here\n"); | ||||
| 	__inet_twsk_kill(tw, &dccp_hashinfo); | ||||
| } | ||||
| 
 | ||||
| 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; | ||||
| 		newicsk->icsk_rto = TCP_TIMEOUT_INIT; | ||||
| 
 | ||||
| 		if (newdp->dccps_options.dccpo_send_ack_vector) { | ||||
| 			newdp->dccps_hc_rx_ackpkts = dccp_ackpkts_alloc(DCCP_MAX_ACK_VECTOR_LEN, | ||||
| 									GFP_ATOMIC); | ||||
| 			/*
 | ||||
| 			 * 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. | ||||
| 			 */ | ||||
| 			if (unlikely(newdp->dccps_hc_rx_ackpkts == NULL)) | ||||
| 				goto out_free; | ||||
| 		} | ||||
| 
 | ||||
| 		if (unlikely(ccid_hc_rx_init(newdp->dccps_hc_rx_ccid, newsk) != 0 || | ||||
| 			     ccid_hc_tx_init(newdp->dccps_hc_tx_ccid, newsk) != 0)) { | ||||
| 			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 | ||||
| 		 *	Set S.ISR, S.GSR, S.SWL, S.SWH from packet or Init Cookie | ||||
| 		 */ | ||||
| 
 | ||||
| 		/* 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) { | ||||
| 		if (after48(DCCP_SKB_CB(skb)->dccpd_seq, dccp_rsk(req)->dreq_isr)) { | ||||
| 			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); | ||||
| 			dccp_set_seqno(&dreq->dreq_isr, DCCP_SKB_CB(skb)->dccpd_seq); | ||||
| 			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) { | ||||
| 		dccp_pr_debug("Invalid ACK number: ack_seq=%llu, dreq_iss=%llu\n", | ||||
| 			      DCCP_SKB_CB(skb)->dccpd_ack_seq, dccp_rsk(req)->dreq_iss); | ||||
| 		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)) { | ||||
| 		ret = dccp_rcv_state_process(child, skb, dccp_hdr(skb), skb->len); | ||||
| 
 | ||||
| 		/* 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; | ||||
| } | ||||
							
								
								
									
										763
									
								
								net/dccp/options.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										763
									
								
								net/dccp/options.c
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,763 @@ | |||
| /*
 | ||||
|  *  net/dccp/options.c | ||||
|  * | ||||
|  *  An implementation of the DCCP protocol | ||||
|  *  Aristeu Sergio Rozanski Filho <aris@cathedrallabs.org> | ||||
|  *  Arnaldo Carvalho de Melo <acme@ghostprotocols.net> | ||||
|  * | ||||
|  *      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/module.h> | ||||
| #include <linux/types.h> | ||||
| #include <linux/kernel.h> | ||||
| #include <linux/skbuff.h> | ||||
| 
 | ||||
| #include "ccid.h" | ||||
| #include "dccp.h" | ||||
| 
 | ||||
| static void dccp_ackpkts_check_rcv_ackvector(struct dccp_ackpkts *ap, | ||||
| 					     struct sock *sk, | ||||
| 					     const u64 ackno, | ||||
| 					     const unsigned char len, | ||||
| 					     const unsigned char *vector); | ||||
| 
 | ||||
| /* stores the default values for new connection. may be changed with sysctl */ | ||||
| static const struct dccp_options dccpo_default_values = { | ||||
| 	.dccpo_sequence_window	  = DCCPF_INITIAL_SEQUENCE_WINDOW, | ||||
| 	.dccpo_ccid		  = DCCPF_INITIAL_CCID, | ||||
| 	.dccpo_send_ack_vector	  = DCCPF_INITIAL_SEND_ACK_VECTOR, | ||||
| 	.dccpo_send_ndp_count	  = DCCPF_INITIAL_SEND_NDP_COUNT, | ||||
| }; | ||||
| 
 | ||||
| void dccp_options_init(struct dccp_options *dccpo) | ||||
| { | ||||
| 	memcpy(dccpo, &dccpo_default_values, sizeof(*dccpo)); | ||||
| } | ||||
| 
 | ||||
| static u32 dccp_decode_value_var(const unsigned char *bf, const u8 len) | ||||
| { | ||||
| 	u32 value = 0; | ||||
| 
 | ||||
| 	if (len > 3) | ||||
| 		value += *bf++ << 24; | ||||
| 	if (len > 2) | ||||
| 		value += *bf++ << 16; | ||||
| 	if (len > 1) | ||||
| 		value += *bf++ << 8; | ||||
| 	if (len > 0) | ||||
| 		value += *bf; | ||||
| 
 | ||||
| 	return value; | ||||
| } | ||||
| 
 | ||||
| int dccp_parse_options(struct sock *sk, struct sk_buff *skb) | ||||
| { | ||||
| 	struct dccp_sock *dp = dccp_sk(sk); | ||||
| #ifdef DCCP_DEBUG | ||||
| 	const char *debug_prefix = dp->dccps_role == DCCP_ROLE_CLIENT ? "CLIENT rx opt: " : | ||||
| 									"server rx opt: "; | ||||
| #endif | ||||
| 	const struct dccp_hdr *dh = dccp_hdr(skb); | ||||
| 	const u8 pkt_type = DCCP_SKB_CB(skb)->dccpd_type; | ||||
| 	unsigned char *options = (unsigned char *)dh + dccp_hdr_len(skb); | ||||
| 	unsigned char *opt_ptr = options; | ||||
| 	const unsigned char *opt_end = (unsigned char *)dh + (dh->dccph_doff * 4); | ||||
| 	struct dccp_options_received *opt_recv = &dp->dccps_options_received; | ||||
| 	unsigned char opt, len; | ||||
| 	unsigned char *value; | ||||
| 
 | ||||
| 	memset(opt_recv, 0, sizeof(*opt_recv)); | ||||
| 
 | ||||
| 	while (opt_ptr != opt_end) { | ||||
| 		opt   = *opt_ptr++; | ||||
| 		len   = 0; | ||||
| 		value = NULL; | ||||
| 
 | ||||
| 		/* Check if this isn't a single byte option */ | ||||
| 		if (opt > DCCPO_MAX_RESERVED) { | ||||
| 			if (opt_ptr == opt_end) | ||||
| 				goto out_invalid_option; | ||||
| 
 | ||||
| 			len = *opt_ptr++; | ||||
| 			if (len < 3) | ||||
| 				goto out_invalid_option; | ||||
| 			/*
 | ||||
| 			 * Remove the type and len fields, leaving | ||||
| 			 * just the value size | ||||
| 			 */ | ||||
| 			len	-= 2; | ||||
| 			value	= opt_ptr; | ||||
| 			opt_ptr += len; | ||||
| 
 | ||||
| 			if (opt_ptr > opt_end) | ||||
| 				goto out_invalid_option; | ||||
| 		} | ||||
| 
 | ||||
| 		switch (opt) { | ||||
| 		case DCCPO_PADDING: | ||||
| 			break; | ||||
| 		case DCCPO_NDP_COUNT: | ||||
| 			if (len > 3) | ||||
| 				goto out_invalid_option; | ||||
| 
 | ||||
| 			opt_recv->dccpor_ndp = dccp_decode_value_var(value, len); | ||||
| 			dccp_pr_debug("%sNDP count=%d\n", debug_prefix, opt_recv->dccpor_ndp); | ||||
| 			break; | ||||
| 		case DCCPO_ACK_VECTOR_0: | ||||
| 			if (len > DCCP_MAX_ACK_VECTOR_LEN) | ||||
| 				goto out_invalid_option; | ||||
| 
 | ||||
| 			if (pkt_type == DCCP_PKT_DATA) | ||||
| 				continue; | ||||
| 
 | ||||
| 			opt_recv->dccpor_ack_vector_len = len; | ||||
| 			opt_recv->dccpor_ack_vector_idx = value - options; | ||||
| 
 | ||||
| 			dccp_pr_debug("%sACK vector 0, len=%d, ack_ackno=%llu\n", | ||||
| 				      debug_prefix, len, DCCP_SKB_CB(skb)->dccpd_ack_seq); | ||||
| 			dccp_ackvector_print(DCCP_SKB_CB(skb)->dccpd_ack_seq, | ||||
| 					     value, len); | ||||
| 			dccp_ackpkts_check_rcv_ackvector(dp->dccps_hc_rx_ackpkts, sk, | ||||
| 							 DCCP_SKB_CB(skb)->dccpd_ack_seq, | ||||
| 							 len, value); | ||||
| 			break; | ||||
| 		case DCCPO_TIMESTAMP: | ||||
| 			if (len != 4) | ||||
| 				goto out_invalid_option; | ||||
| 
 | ||||
| 			opt_recv->dccpor_timestamp = ntohl(*(u32 *)value); | ||||
| 
 | ||||
| 			dp->dccps_timestamp_echo = opt_recv->dccpor_timestamp; | ||||
| 			dp->dccps_timestamp_time = jiffies; | ||||
| 
 | ||||
| 			dccp_pr_debug("%sTIMESTAMP=%u, ackno=%llu\n", | ||||
| 				      debug_prefix, opt_recv->dccpor_timestamp, | ||||
| 				      DCCP_SKB_CB(skb)->dccpd_ack_seq); | ||||
| 			break; | ||||
| 		case DCCPO_TIMESTAMP_ECHO: | ||||
| 			if (len < 4 || len > 8) | ||||
| 				goto out_invalid_option; | ||||
| 
 | ||||
| 			opt_recv->dccpor_timestamp_echo = ntohl(*(u32 *)value); | ||||
| 
 | ||||
| 			dccp_pr_debug("%sTIMESTAMP_ECHO=%u, len=%d, ackno=%llu, diff=%u\n", | ||||
| 				      debug_prefix, opt_recv->dccpor_timestamp_echo, | ||||
| 				      len + 2, DCCP_SKB_CB(skb)->dccpd_ack_seq, | ||||
| 				      tcp_time_stamp - opt_recv->dccpor_timestamp_echo); | ||||
| 
 | ||||
| 			opt_recv->dccpor_elapsed_time = dccp_decode_value_var(value + 4, len - 4); | ||||
| 			dccp_pr_debug("%sTIMESTAMP_ECHO ELAPSED_TIME=%d\n", debug_prefix, | ||||
| 				      opt_recv->dccpor_elapsed_time); | ||||
| 			break; | ||||
| 		case DCCPO_ELAPSED_TIME: | ||||
| 			if (len > 4) | ||||
| 				goto out_invalid_option; | ||||
| 
 | ||||
| 			if (pkt_type == DCCP_PKT_DATA) | ||||
| 				continue; | ||||
| 			opt_recv->dccpor_elapsed_time = dccp_decode_value_var(value, len); | ||||
| 			dccp_pr_debug("%sELAPSED_TIME=%d\n", debug_prefix, | ||||
| 				      opt_recv->dccpor_elapsed_time); | ||||
| 			break; | ||||
| 			/*
 | ||||
| 			 * From draft-ietf-dccp-spec-11.txt: | ||||
| 			 * | ||||
| 			 *	Option numbers 128 through 191 are for options sent from the HC- | ||||
| 			 *	Sender to the HC-Receiver; option numbers 192 through 255 are for | ||||
| 			 *	options sent from the HC-Receiver to the HC-Sender. | ||||
| 			 */ | ||||
| 		case 128 ... 191: { | ||||
| 			const u16 idx = value - options; | ||||
| 
 | ||||
| 			if (ccid_hc_rx_parse_options(dp->dccps_hc_rx_ccid, sk, opt, len, idx, value) != 0) | ||||
| 				goto out_invalid_option; | ||||
| 		} | ||||
| 			break; | ||||
| 		case 192 ... 255: { | ||||
| 			const u16 idx = value - options; | ||||
| 
 | ||||
| 			if (ccid_hc_tx_parse_options(dp->dccps_hc_tx_ccid, sk, opt, len, idx, value) != 0) | ||||
| 				goto out_invalid_option; | ||||
| 		} | ||||
| 			break; | ||||
| 		default: | ||||
| 			pr_info("DCCP(%p): option %d(len=%d) not implemented, ignoring\n", | ||||
| 				sk, opt, len); | ||||
| 			break; | ||||
| 	        } | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| 
 | ||||
| out_invalid_option: | ||||
| 	DCCP_INC_STATS_BH(DCCP_MIB_INVALIDOPT); | ||||
| 	DCCP_SKB_CB(skb)->dccpd_reset_code = DCCP_RESET_CODE_OPTION_ERROR; | ||||
| 	pr_info("DCCP(%p): invalid option %d, len=%d\n", sk, opt, len); | ||||
| 	return -1; | ||||
| } | ||||
| 
 | ||||
| static void dccp_encode_value_var(const u32 value, unsigned char *to, | ||||
| 				  const unsigned int len) | ||||
| { | ||||
| 	if (len > 3) | ||||
| 		*to++ = (value & 0xFF000000) >> 24; | ||||
| 	if (len > 2) | ||||
| 		*to++ = (value & 0xFF0000) >> 16; | ||||
| 	if (len > 1) | ||||
| 		*to++ = (value & 0xFF00) >> 8; | ||||
| 	if (len > 0) | ||||
| 		*to++ = (value & 0xFF); | ||||
| } | ||||
| 
 | ||||
| static inline int dccp_ndp_len(const int ndp) | ||||
| { | ||||
| 	return likely(ndp <= 0xFF) ? 1 : ndp <= 0xFFFF ? 2 : 3; | ||||
| } | ||||
| 
 | ||||
| void dccp_insert_option(struct sock *sk, struct sk_buff *skb, | ||||
| 			const unsigned char option, | ||||
| 			const void *value, const unsigned char len) | ||||
| { | ||||
| 	unsigned char *to; | ||||
| 
 | ||||
| 	if (DCCP_SKB_CB(skb)->dccpd_opt_len + len + 2 > DCCP_MAX_OPT_LEN) { | ||||
| 		LIMIT_NETDEBUG(pr_info("DCCP: packet too small to insert %d option!\n", option)); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	DCCP_SKB_CB(skb)->dccpd_opt_len += len + 2; | ||||
| 
 | ||||
| 	to    = skb_push(skb, len + 2); | ||||
| 	*to++ = option; | ||||
| 	*to++ = len + 2; | ||||
| 
 | ||||
| 	memcpy(to, value, len); | ||||
| } | ||||
| 
 | ||||
| EXPORT_SYMBOL_GPL(dccp_insert_option); | ||||
| 
 | ||||
| static void dccp_insert_option_ndp(struct sock *sk, struct sk_buff *skb) | ||||
| { | ||||
| 	struct dccp_sock *dp = dccp_sk(sk); | ||||
| 	int ndp = dp->dccps_ndp_count; | ||||
| 
 | ||||
| 	if (dccp_non_data_packet(skb)) | ||||
| 		++dp->dccps_ndp_count; | ||||
| 	else | ||||
| 		dp->dccps_ndp_count = 0; | ||||
| 
 | ||||
| 	if (ndp > 0) { | ||||
| 		unsigned char *ptr; | ||||
| 		const int ndp_len = dccp_ndp_len(ndp); | ||||
| 		const int len = ndp_len + 2; | ||||
| 
 | ||||
| 		if (DCCP_SKB_CB(skb)->dccpd_opt_len + len > DCCP_MAX_OPT_LEN) | ||||
| 			return; | ||||
| 
 | ||||
| 		DCCP_SKB_CB(skb)->dccpd_opt_len += len; | ||||
| 
 | ||||
| 		ptr = skb_push(skb, len); | ||||
| 		*ptr++ = DCCPO_NDP_COUNT; | ||||
| 		*ptr++ = len; | ||||
| 		dccp_encode_value_var(ndp, ptr, ndp_len); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static inline int dccp_elapsed_time_len(const u32 elapsed_time) | ||||
| { | ||||
| 	return elapsed_time == 0 ? 0 : | ||||
| 	       elapsed_time <= 0xFF ? 1 : | ||||
| 	       elapsed_time <= 0xFFFF ? 2 : | ||||
| 	       elapsed_time <= 0xFFFFFF ? 3 : 4; | ||||
| } | ||||
| 
 | ||||
| void dccp_insert_option_elapsed_time(struct sock *sk, | ||||
| 				     struct sk_buff *skb, | ||||
| 				     u32 elapsed_time) | ||||
| { | ||||
| #ifdef DCCP_DEBUG | ||||
| 	struct dccp_sock *dp = dccp_sk(sk); | ||||
| 	const char *debug_prefix = dp->dccps_role == DCCP_ROLE_CLIENT ? "CLIENT TX opt: " : | ||||
| 									"server TX opt: "; | ||||
| #endif | ||||
| 	const int elapsed_time_len = dccp_elapsed_time_len(elapsed_time); | ||||
| 	const int len = 2 + elapsed_time_len; | ||||
| 	unsigned char *to; | ||||
| 
 | ||||
| 	/* If elapsed_time == 0... */ | ||||
| 	if (elapsed_time_len == 2) | ||||
| 		return; | ||||
| 
 | ||||
| 	if (DCCP_SKB_CB(skb)->dccpd_opt_len + len > DCCP_MAX_OPT_LEN) { | ||||
| 		LIMIT_NETDEBUG(pr_info("DCCP: packet too small to insert elapsed time!\n")); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	DCCP_SKB_CB(skb)->dccpd_opt_len += len; | ||||
| 
 | ||||
| 	to    = skb_push(skb, len); | ||||
| 	*to++ = DCCPO_ELAPSED_TIME; | ||||
| 	*to++ = len; | ||||
| 
 | ||||
| 	dccp_encode_value_var(elapsed_time, to, elapsed_time_len); | ||||
| 
 | ||||
| 	dccp_pr_debug("%sELAPSED_TIME=%u, len=%d, seqno=%llu\n", | ||||
| 		      debug_prefix, elapsed_time, | ||||
| 		      len, DCCP_SKB_CB(skb)->dccpd_seq); | ||||
| } | ||||
| 
 | ||||
| EXPORT_SYMBOL(dccp_insert_option_elapsed_time); | ||||
| 
 | ||||
| static void dccp_insert_option_ack_vector(struct sock *sk, struct sk_buff *skb) | ||||
| { | ||||
| 	struct dccp_sock *dp = dccp_sk(sk); | ||||
| #ifdef DCCP_DEBUG | ||||
| 	const char *debug_prefix = dp->dccps_role == DCCP_ROLE_CLIENT ? "CLIENT TX opt: " : | ||||
| 									"server TX opt: "; | ||||
| #endif | ||||
| 	struct dccp_ackpkts *ap = dp->dccps_hc_rx_ackpkts; | ||||
| 	int len = ap->dccpap_buf_vector_len + 2; | ||||
| 	const u32 elapsed_time = jiffies_to_usecs(jiffies - ap->dccpap_time) / 10; | ||||
| 	unsigned char *to, *from; | ||||
| 
 | ||||
| 	if (elapsed_time != 0) | ||||
| 		dccp_insert_option_elapsed_time(sk, skb, elapsed_time); | ||||
| 
 | ||||
| 	if (DCCP_SKB_CB(skb)->dccpd_opt_len + len > DCCP_MAX_OPT_LEN) { | ||||
| 		LIMIT_NETDEBUG(pr_info("DCCP: packet too small to insert ACK Vector!\n")); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * XXX: now we have just one ack vector sent record, so | ||||
| 	 * we have to wait for it to be cleared. | ||||
| 	 * | ||||
| 	 * Of course this is not acceptable, but this is just for | ||||
| 	 * basic testing now. | ||||
| 	 */ | ||||
| 	if (ap->dccpap_ack_seqno != DCCP_MAX_SEQNO + 1) | ||||
| 		return; | ||||
| 
 | ||||
| 	DCCP_SKB_CB(skb)->dccpd_opt_len += len; | ||||
| 
 | ||||
| 	to    = skb_push(skb, len); | ||||
| 	*to++ = DCCPO_ACK_VECTOR_0; | ||||
| 	*to++ = len; | ||||
| 
 | ||||
| 	len  = ap->dccpap_buf_vector_len; | ||||
| 	from = ap->dccpap_buf + ap->dccpap_buf_head; | ||||
| 
 | ||||
| 	/* Check if buf_head wraps */ | ||||
| 	if (ap->dccpap_buf_head + len > ap->dccpap_buf_len) { | ||||
| 		const unsigned int tailsize = ap->dccpap_buf_len - ap->dccpap_buf_head; | ||||
| 
 | ||||
| 		memcpy(to, from, tailsize); | ||||
| 		to   += tailsize; | ||||
| 		len  -= tailsize; | ||||
| 		from = ap->dccpap_buf; | ||||
| 	} | ||||
| 
 | ||||
| 	memcpy(to, from, len); | ||||
| 	/*
 | ||||
| 	 *	From draft-ietf-dccp-spec-11.txt: | ||||
| 	 * | ||||
| 	 *	For each acknowledgement it sends, the HC-Receiver will add an | ||||
| 	 *	acknowledgement record.  ack_seqno will equal the HC-Receiver | ||||
| 	 *	sequence number it used for the ack packet; ack_ptr will equal | ||||
| 	 *	buf_head; ack_ackno will equal buf_ackno; and ack_nonce will equal | ||||
| 	 *	buf_nonce. | ||||
| 	 * | ||||
| 	 * This implemention uses just one ack record for now. | ||||
| 	 */ | ||||
| 	ap->dccpap_ack_seqno	  = DCCP_SKB_CB(skb)->dccpd_seq; | ||||
| 	ap->dccpap_ack_ptr	  = ap->dccpap_buf_head; | ||||
| 	ap->dccpap_ack_ackno	  = ap->dccpap_buf_ackno; | ||||
| 	ap->dccpap_ack_nonce	  = ap->dccpap_buf_nonce; | ||||
| 	ap->dccpap_ack_vector_len = ap->dccpap_buf_vector_len; | ||||
| 
 | ||||
| 	dccp_pr_debug("%sACK Vector 0, len=%d, ack_seqno=%llu, ack_ackno=%llu\n", | ||||
| 		      debug_prefix, ap->dccpap_ack_vector_len, | ||||
| 		      ap->dccpap_ack_seqno, ap->dccpap_ack_ackno); | ||||
| } | ||||
| 
 | ||||
| static inline void dccp_insert_option_timestamp(struct sock *sk, struct sk_buff *skb) | ||||
| { | ||||
| 	const u32 now = htonl(tcp_time_stamp); | ||||
| 	dccp_insert_option(sk, skb, DCCPO_TIMESTAMP, &now, sizeof(now)); | ||||
| } | ||||
| 
 | ||||
| static void dccp_insert_option_timestamp_echo(struct sock *sk, struct sk_buff *skb) | ||||
| { | ||||
| 	struct dccp_sock *dp = dccp_sk(sk); | ||||
| #ifdef DCCP_DEBUG | ||||
| 	const char *debug_prefix = dp->dccps_role == DCCP_ROLE_CLIENT ? "CLIENT TX opt: " : | ||||
| 									"server TX opt: "; | ||||
| #endif | ||||
| 	u32 tstamp_echo; | ||||
| 	const u32 elapsed_time = jiffies_to_usecs(jiffies - dp->dccps_timestamp_time) / 10; | ||||
| 	const int elapsed_time_len = dccp_elapsed_time_len(elapsed_time); | ||||
| 	const int len = 6 + elapsed_time_len; | ||||
| 	unsigned char *to; | ||||
| 
 | ||||
| 	if (DCCP_SKB_CB(skb)->dccpd_opt_len + len > DCCP_MAX_OPT_LEN) { | ||||
| 		LIMIT_NETDEBUG(pr_info("DCCP: packet too small to insert timestamp echo!\n")); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	DCCP_SKB_CB(skb)->dccpd_opt_len += len; | ||||
| 
 | ||||
| 	to    = skb_push(skb, len); | ||||
| 	*to++ = DCCPO_TIMESTAMP_ECHO; | ||||
| 	*to++ = len; | ||||
| 
 | ||||
| 	tstamp_echo = htonl(dp->dccps_timestamp_echo); | ||||
| 	memcpy(to, &tstamp_echo, 4); | ||||
| 	to += 4; | ||||
| 	dccp_encode_value_var(elapsed_time, to, elapsed_time_len); | ||||
| 
 | ||||
| 	dccp_pr_debug("%sTIMESTAMP_ECHO=%u, len=%d, seqno=%llu\n", | ||||
| 		      debug_prefix, dp->dccps_timestamp_echo, | ||||
| 		      len, DCCP_SKB_CB(skb)->dccpd_seq); | ||||
| 
 | ||||
| 	dp->dccps_timestamp_echo = 0; | ||||
| 	dp->dccps_timestamp_time = 0; | ||||
| } | ||||
| 
 | ||||
| void dccp_insert_options(struct sock *sk, struct sk_buff *skb) | ||||
| { | ||||
| 	struct dccp_sock *dp = dccp_sk(sk); | ||||
| 
 | ||||
| 	DCCP_SKB_CB(skb)->dccpd_opt_len = 0; | ||||
| 
 | ||||
| 	if (dp->dccps_options.dccpo_send_ndp_count) | ||||
| 		dccp_insert_option_ndp(sk, skb); | ||||
| 
 | ||||
| 	if (!dccp_packet_without_ack(skb)) { | ||||
| 		if (dp->dccps_options.dccpo_send_ack_vector && | ||||
| 		    dp->dccps_hc_rx_ackpkts->dccpap_buf_ackno != DCCP_MAX_SEQNO + 1) | ||||
| 			dccp_insert_option_ack_vector(sk, skb); | ||||
| 
 | ||||
| 		dccp_insert_option_timestamp(sk, skb); | ||||
| 		if (dp->dccps_timestamp_echo != 0) | ||||
| 			dccp_insert_option_timestamp_echo(sk, skb); | ||||
| 	} | ||||
| 
 | ||||
| 	ccid_hc_rx_insert_options(dp->dccps_hc_rx_ccid, sk, skb); | ||||
| 	ccid_hc_tx_insert_options(dp->dccps_hc_tx_ccid, sk, skb); | ||||
| 
 | ||||
| 	/* XXX: insert other options when appropriate */ | ||||
| 
 | ||||
| 	if (DCCP_SKB_CB(skb)->dccpd_opt_len != 0) { | ||||
| 		/* The length of all options has to be a multiple of 4 */ | ||||
| 		int padding = DCCP_SKB_CB(skb)->dccpd_opt_len % 4; | ||||
| 
 | ||||
| 		if (padding != 0) { | ||||
| 			padding = 4 - padding; | ||||
| 			memset(skb_push(skb, padding), 0, padding); | ||||
| 			DCCP_SKB_CB(skb)->dccpd_opt_len += padding; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| struct dccp_ackpkts *dccp_ackpkts_alloc(unsigned int len, int priority) | ||||
| { | ||||
| 	struct dccp_ackpkts *ap = kmalloc(sizeof(*ap) + len, priority); | ||||
| 
 | ||||
| 	if (ap != NULL) { | ||||
| #ifdef DCCP_DEBUG | ||||
| 		memset(ap->dccpap_buf, 0xFF, len); | ||||
| #endif | ||||
| 		ap->dccpap_buf_len	  = len; | ||||
| 		ap->dccpap_buf_head	  = ap->dccpap_buf_tail = ap->dccpap_buf_len - 1; | ||||
| 		ap->dccpap_buf_ackno	  = ap->dccpap_ack_ackno = ap->dccpap_ack_seqno = DCCP_MAX_SEQNO + 1; | ||||
| 		ap->dccpap_buf_nonce	  = ap->dccpap_buf_nonce = 0; | ||||
| 		ap->dccpap_ack_ptr	  = 0; | ||||
| 		ap->dccpap_time		  = 0; | ||||
| 		ap->dccpap_buf_vector_len = ap->dccpap_ack_vector_len = 0; | ||||
| 	} | ||||
| 
 | ||||
| 	return ap; | ||||
| } | ||||
| 
 | ||||
| void dccp_ackpkts_free(struct dccp_ackpkts *ap) | ||||
| { | ||||
| 	if (ap != NULL) { | ||||
| #ifdef DCCP_DEBUG | ||||
| 		memset(ap, 0xFF, sizeof(*ap) + ap->dccpap_buf_len); | ||||
| #endif | ||||
| 		kfree(ap); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static inline u8 dccp_ackpkts_state(const struct dccp_ackpkts *ap, | ||||
| 				    const unsigned int index) | ||||
| { | ||||
| 	return ap->dccpap_buf[index] & DCCP_ACKPKTS_STATE_MASK; | ||||
| } | ||||
| 
 | ||||
| static inline u8 dccp_ackpkts_len(const struct dccp_ackpkts *ap, | ||||
| 				  const unsigned int index) | ||||
| { | ||||
| 	return ap->dccpap_buf[index] & DCCP_ACKPKTS_LEN_MASK; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * If several packets are missing, the HC-Receiver may prefer to enter multiple | ||||
|  * bytes with run length 0, rather than a single byte with a larger run length; | ||||
|  * this simplifies table updates if one of the missing packets arrives. | ||||
|  */ | ||||
| static inline int dccp_ackpkts_set_buf_head_state(struct dccp_ackpkts *ap, | ||||
| 						  const unsigned int packets, | ||||
| 						  const unsigned char state) | ||||
| { | ||||
| 	unsigned int gap; | ||||
| 	signed long new_head; | ||||
| 
 | ||||
| 	if (ap->dccpap_buf_vector_len + packets > ap->dccpap_buf_len) | ||||
| 		return -ENOBUFS; | ||||
| 
 | ||||
| 	gap	 = packets - 1; | ||||
| 	new_head = ap->dccpap_buf_head - packets; | ||||
| 
 | ||||
| 	if (new_head < 0) { | ||||
| 		if (gap > 0) { | ||||
| 			memset(ap->dccpap_buf, DCCP_ACKPKTS_STATE_NOT_RECEIVED, | ||||
| 			       gap + new_head + 1); | ||||
| 			gap = -new_head; | ||||
| 		} | ||||
| 		new_head += ap->dccpap_buf_len; | ||||
| 	}  | ||||
| 
 | ||||
| 	ap->dccpap_buf_head = new_head; | ||||
| 
 | ||||
| 	if (gap > 0) | ||||
| 		memset(ap->dccpap_buf + ap->dccpap_buf_head + 1, | ||||
| 		       DCCP_ACKPKTS_STATE_NOT_RECEIVED, gap); | ||||
| 
 | ||||
| 	ap->dccpap_buf[ap->dccpap_buf_head] = state; | ||||
| 	ap->dccpap_buf_vector_len += packets; | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Implements the draft-ietf-dccp-spec-11.txt Appendix A | ||||
|  */ | ||||
| int dccp_ackpkts_add(struct dccp_ackpkts *ap, u64 ackno, u8 state) | ||||
| { | ||||
| 	/*
 | ||||
| 	 * Check at the right places if the buffer is full, if it is, tell the | ||||
| 	 * caller to start dropping packets till the HC-Sender acks our ACK | ||||
| 	 * vectors, when we will free up space in dccpap_buf. | ||||
| 	 * | ||||
| 	 * We may well decide to do buffer compression, etc, but for now lets | ||||
| 	 * just drop. | ||||
| 	 * | ||||
| 	 * From Appendix A: | ||||
| 	 * | ||||
| 	 *	Of course, the circular buffer may overflow, either when the HC- | ||||
| 	 *	Sender is sending data at a very high rate, when the HC-Receiver's | ||||
| 	 *	acknowledgements are not reaching the HC-Sender, or when the HC- | ||||
| 	 *	Sender is forgetting to acknowledge those acks (so the HC-Receiver | ||||
| 	 *	is unable to clean up old state).  In this case, the HC-Receiver | ||||
| 	 *	should either compress the buffer (by increasing run lengths when | ||||
| 	 *	possible), transfer its state to a larger buffer, or, as a last | ||||
| 	 *	resort, drop all received packets, without processing them | ||||
| 	 *	whatsoever, until its buffer shrinks again. | ||||
| 	 */ | ||||
| 
 | ||||
| 	/* See if this is the first ackno being inserted */ | ||||
| 	if (ap->dccpap_buf_vector_len == 0) { | ||||
| 		ap->dccpap_buf[ap->dccpap_buf_head] = state; | ||||
| 		ap->dccpap_buf_vector_len = 1; | ||||
| 	} else if (after48(ackno, ap->dccpap_buf_ackno)) { | ||||
| 		const u64 delta = dccp_delta_seqno(ap->dccpap_buf_ackno, ackno); | ||||
| 
 | ||||
| 		/*
 | ||||
| 		 * Look if the state of this packet is the same as the previous ackno | ||||
| 		 * and if so if we can bump the head len. | ||||
| 		 */ | ||||
| 		if (delta == 1 && | ||||
| 		    dccp_ackpkts_state(ap, ap->dccpap_buf_head) == state && | ||||
| 		    dccp_ackpkts_len(ap, ap->dccpap_buf_head) < DCCP_ACKPKTS_LEN_MASK) | ||||
| 			ap->dccpap_buf[ap->dccpap_buf_head]++; | ||||
| 		else if (dccp_ackpkts_set_buf_head_state(ap, delta, state)) | ||||
| 			return -ENOBUFS; | ||||
| 	} else { | ||||
| 		/*
 | ||||
| 		 * A.1.2.  Old Packets | ||||
| 		 * | ||||
| 		 *	When a packet with Sequence Number S arrives, and S <= buf_ackno, | ||||
| 		 *	the HC-Receiver will scan the table for the byte corresponding to S. | ||||
| 		 *	(Indexing structures could reduce the complexity of this scan.) | ||||
| 		 */ | ||||
| 		u64 delta = dccp_delta_seqno(ackno, ap->dccpap_buf_ackno); | ||||
| 		unsigned int index = ap->dccpap_buf_head; | ||||
| 
 | ||||
| 		while (1) { | ||||
| 			const u8 len = dccp_ackpkts_len(ap, index); | ||||
| 			const u8 state = dccp_ackpkts_state(ap, index); | ||||
| 			/*
 | ||||
| 			 * valid packets not yet in dccpap_buf have a reserved entry, with | ||||
| 			 * a len equal to 0 | ||||
| 			 */ | ||||
| 			if (state == DCCP_ACKPKTS_STATE_NOT_RECEIVED && | ||||
| 			    len == 0 && delta == 0) { /* Found our reserved seat! */ | ||||
| 				dccp_pr_debug("Found %llu reserved seat!\n", ackno); | ||||
| 				ap->dccpap_buf[index] = state; | ||||
| 				goto out; | ||||
| 			} | ||||
| 			/* len == 0 means one packet */ | ||||
| 			if (delta < len + 1) | ||||
| 				goto out_duplicate; | ||||
| 
 | ||||
| 			delta -= len + 1; | ||||
| 			if (++index == ap->dccpap_buf_len) | ||||
| 				index = 0; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	ap->dccpap_buf_ackno = ackno; | ||||
| 	ap->dccpap_time = jiffies; | ||||
| out: | ||||
| 	dccp_pr_debug(""); | ||||
| 	dccp_ackpkts_print(ap); | ||||
| 	return 0; | ||||
| 
 | ||||
| out_duplicate: | ||||
| 	/* Duplicate packet */ | ||||
| 	dccp_pr_debug("Received a dup or already considered lost packet: %llu\n", ackno); | ||||
| 	return -EILSEQ; | ||||
| } | ||||
| 
 | ||||
| #ifdef DCCP_DEBUG | ||||
| void dccp_ackvector_print(const u64 ackno, const unsigned char *vector, int len) | ||||
| { | ||||
| 	if (!dccp_debug) | ||||
| 		return; | ||||
| 
 | ||||
| 	printk("ACK vector len=%d, ackno=%llu |", len, ackno); | ||||
| 
 | ||||
| 	while (len--) { | ||||
| 		const u8 state = (*vector & DCCP_ACKPKTS_STATE_MASK) >> 6; | ||||
| 		const u8 rl = (*vector & DCCP_ACKPKTS_LEN_MASK); | ||||
| 
 | ||||
| 		printk("%d,%d|", state, rl); | ||||
| 		++vector; | ||||
| 	} | ||||
| 
 | ||||
| 	printk("\n"); | ||||
| } | ||||
| 
 | ||||
| void dccp_ackpkts_print(const struct dccp_ackpkts *ap) | ||||
| { | ||||
| 	dccp_ackvector_print(ap->dccpap_buf_ackno, | ||||
| 			     ap->dccpap_buf + ap->dccpap_buf_head, | ||||
| 			     ap->dccpap_buf_vector_len); | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| static void dccp_ackpkts_trow_away_ack_record(struct dccp_ackpkts *ap) | ||||
| { | ||||
| 	/*
 | ||||
| 	 * As we're keeping track of the ack vector size | ||||
| 	 * (dccpap_buf_vector_len) and the sent ack vector size | ||||
| 	 * (dccpap_ack_vector_len) we don't need dccpap_buf_tail at all, but | ||||
| 	 * keep this code here as in the future we'll implement a vector of ack | ||||
| 	 * records, as suggested in draft-ietf-dccp-spec-11.txt Appendix A. -acme | ||||
| 	 */ | ||||
| #if 0 | ||||
| 	ap->dccpap_buf_tail = ap->dccpap_ack_ptr + 1; | ||||
| 	if (ap->dccpap_buf_tail >= ap->dccpap_buf_len) | ||||
| 		ap->dccpap_buf_tail -= ap->dccpap_buf_len; | ||||
| #endif | ||||
| 	ap->dccpap_buf_vector_len -= ap->dccpap_ack_vector_len; | ||||
| } | ||||
| 
 | ||||
| void dccp_ackpkts_check_rcv_ackno(struct dccp_ackpkts *ap, struct sock *sk, | ||||
| 				 u64 ackno) | ||||
| { | ||||
| 	/* Check if we actually sent an ACK vector */ | ||||
| 	if (ap->dccpap_ack_seqno == DCCP_MAX_SEQNO + 1) | ||||
| 		return; | ||||
| 
 | ||||
| 	if (ackno == ap->dccpap_ack_seqno) { | ||||
| #ifdef DCCP_DEBUG | ||||
| 		struct dccp_sock *dp = dccp_sk(sk); | ||||
| 		const char *debug_prefix = dp->dccps_role == DCCP_ROLE_CLIENT ? "CLIENT rx ack: " : | ||||
| 										"server rx ack: "; | ||||
| #endif | ||||
| 		dccp_pr_debug("%sACK packet 0, len=%d, ack_seqno=%llu, ack_ackno=%llu, ACKED!\n", | ||||
| 			      debug_prefix, 1, | ||||
| 			      ap->dccpap_ack_seqno, ap->dccpap_ack_ackno); | ||||
| 		dccp_ackpkts_trow_away_ack_record(ap); | ||||
| 		ap->dccpap_ack_seqno = DCCP_MAX_SEQNO + 1; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static void dccp_ackpkts_check_rcv_ackvector(struct dccp_ackpkts *ap, | ||||
| 					     struct sock *sk, u64 ackno, | ||||
| 					     const unsigned char len, | ||||
| 					     const unsigned char *vector) | ||||
| { | ||||
| 	unsigned char i; | ||||
| 
 | ||||
| 	/* Check if we actually sent an ACK vector */ | ||||
| 	if (ap->dccpap_ack_seqno == DCCP_MAX_SEQNO + 1) | ||||
| 		return; | ||||
| 	/*
 | ||||
| 	 * We're in the receiver half connection, so if the received an ACK vector | ||||
| 	 * ackno (e.g. 50) before dccpap_ack_seqno (e.g. 52), we're not interested. | ||||
| 	 * | ||||
| 	 * Extra explanation with example: | ||||
| 	 *  | ||||
| 	 * if we received an ACK vector with ackno 50, it can only be acking | ||||
| 	 * 50, 49, 48, etc, not 52 (the seqno for the ACK vector we sent). | ||||
| 	 */ | ||||
| 	// dccp_pr_debug("is %llu < %llu? ", ackno, ap->dccpap_ack_seqno);
 | ||||
| 	if (before48(ackno, ap->dccpap_ack_seqno)) { | ||||
| 		// dccp_pr_debug_cat("yes\n");
 | ||||
| 		return; | ||||
| 	} | ||||
| 	// dccp_pr_debug_cat("no\n");
 | ||||
| 
 | ||||
| 	i = len; | ||||
| 	while (i--) { | ||||
| 		const u8 rl = (*vector & DCCP_ACKPKTS_LEN_MASK); | ||||
| 		u64 ackno_end_rl; | ||||
| 
 | ||||
| 		dccp_set_seqno(&ackno_end_rl, ackno - rl); | ||||
| 
 | ||||
| 		// dccp_pr_debug("is %llu <= %llu <= %llu? ", ackno_end_rl, ap->dccpap_ack_seqno, ackno);
 | ||||
| 		if (between48(ap->dccpap_ack_seqno, ackno_end_rl, ackno)) { | ||||
| 			const u8 state = (*vector & DCCP_ACKPKTS_STATE_MASK) >> 6; | ||||
| 			// dccp_pr_debug_cat("yes\n");
 | ||||
| 
 | ||||
| 			if (state != DCCP_ACKPKTS_STATE_NOT_RECEIVED) { | ||||
| #ifdef DCCP_DEBUG | ||||
| 				struct dccp_sock *dp = dccp_sk(sk); | ||||
| 				const char *debug_prefix = dp->dccps_role == DCCP_ROLE_CLIENT ? "CLIENT rx ack: " : | ||||
| 												"server rx ack: "; | ||||
| #endif | ||||
| 				dccp_pr_debug("%sACK vector 0, len=%d, ack_seqno=%llu, ack_ackno=%llu, ACKED!\n", | ||||
| 					      debug_prefix, len, | ||||
| 					      ap->dccpap_ack_seqno, ap->dccpap_ack_ackno); | ||||
| 				dccp_ackpkts_trow_away_ack_record(ap); | ||||
| 			} | ||||
| 			/*
 | ||||
| 			 * If dccpap_ack_seqno was not received, no problem we'll | ||||
| 			 * send another ACK vector. | ||||
| 			 */ | ||||
| 			ap->dccpap_ack_seqno = DCCP_MAX_SEQNO + 1; | ||||
| 			break; | ||||
| 		} | ||||
| 		// dccp_pr_debug_cat("no\n");
 | ||||
| 
 | ||||
| 		dccp_set_seqno(&ackno, ackno_end_rl - 1); | ||||
| 		++vector; | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										406
									
								
								net/dccp/output.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										406
									
								
								net/dccp/output.c
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,406 @@ | |||
| /*
 | ||||
|  *  net/dccp/output.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 <net/sock.h> | ||||
| 
 | ||||
| #include "ccid.h" | ||||
| #include "dccp.h" | ||||
| 
 | ||||
| static inline void dccp_event_ack_sent(struct sock *sk) | ||||
| { | ||||
| 	inet_csk_clear_xmit_timer(sk, ICSK_TIME_DACK); | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * All SKB's seen here are completely headerless. It is our | ||||
|  * job to build the DCCP header, and pass the packet down to | ||||
|  * IP so it can do the same plus pass the packet off to the | ||||
|  * device. | ||||
|  */ | ||||
| int dccp_transmit_skb(struct sock *sk, struct sk_buff *skb) | ||||
| { | ||||
| 	if (likely(skb != NULL)) { | ||||
| 		const struct inet_sock *inet = inet_sk(sk); | ||||
| 		struct dccp_sock *dp = dccp_sk(sk); | ||||
| 		struct dccp_skb_cb *dcb = DCCP_SKB_CB(skb); | ||||
| 		struct dccp_hdr *dh; | ||||
| 		/* XXX For now we're using only 48 bits sequence numbers */ | ||||
| 		const int dccp_header_size = sizeof(*dh) + | ||||
| 					     sizeof(struct dccp_hdr_ext) + | ||||
| 					     dccp_packet_hdr_len(dcb->dccpd_type); | ||||
| 		int err, set_ack = 1; | ||||
| 		u64 ackno = dp->dccps_gsr; | ||||
| 
 | ||||
| 		/*
 | ||||
| 		 * FIXME: study DCCP_PKT_SYNC[ACK] to see what is the right thing  | ||||
| 		 * to do here... | ||||
| 		 */ | ||||
| 		dccp_inc_seqno(&dp->dccps_gss); | ||||
| 
 | ||||
| 		dcb->dccpd_seq = dp->dccps_gss; | ||||
| 		dccp_insert_options(sk, skb); | ||||
| 
 | ||||
| 		switch (dcb->dccpd_type) { | ||||
| 		case DCCP_PKT_DATA: | ||||
| 			set_ack = 0; | ||||
| 			break; | ||||
| 		case DCCP_PKT_SYNC: | ||||
| 		case DCCP_PKT_SYNCACK: | ||||
| 			ackno = dcb->dccpd_seq; | ||||
| 			break; | ||||
| 		} | ||||
| 		 | ||||
| 		skb->h.raw = skb_push(skb, dccp_header_size); | ||||
| 		dh = dccp_hdr(skb); | ||||
| 		/* Data packets are not cloned as they are never retransmitted */ | ||||
| 		if (skb_cloned(skb)) | ||||
| 			skb_set_owner_w(skb, sk); | ||||
| 
 | ||||
| 		/* Build DCCP header and checksum it. */ | ||||
| 		memset(dh, 0, dccp_header_size); | ||||
| 		dh->dccph_type	= dcb->dccpd_type; | ||||
| 		dh->dccph_sport	= inet->sport; | ||||
| 		dh->dccph_dport	= inet->dport; | ||||
| 		dh->dccph_doff	= (dccp_header_size + dcb->dccpd_opt_len) / 4; | ||||
| 		dh->dccph_ccval	= dcb->dccpd_ccval; | ||||
| 		/* XXX For now we're using only 48 bits sequence numbers */ | ||||
| 		dh->dccph_x	= 1; | ||||
| 
 | ||||
| 		dp->dccps_awh = dp->dccps_gss; | ||||
| 		dccp_hdr_set_seq(dh, dp->dccps_gss); | ||||
| 		if (set_ack) | ||||
| 			dccp_hdr_set_ack(dccp_hdr_ack_bits(skb), ackno); | ||||
| 
 | ||||
| 		switch (dcb->dccpd_type) { | ||||
| 		case DCCP_PKT_REQUEST: | ||||
| 			dccp_hdr_request(skb)->dccph_req_service = dcb->dccpd_service; | ||||
| 			break; | ||||
| 		case DCCP_PKT_RESET: | ||||
| 			dccp_hdr_reset(skb)->dccph_reset_code = dcb->dccpd_reset_code; | ||||
| 			break; | ||||
| 		} | ||||
| 
 | ||||
| 		dh->dccph_checksum = dccp_v4_checksum(skb); | ||||
| 
 | ||||
| 		if (dcb->dccpd_type == DCCP_PKT_ACK || | ||||
| 		    dcb->dccpd_type == DCCP_PKT_DATAACK) | ||||
| 			dccp_event_ack_sent(sk); | ||||
| 
 | ||||
| 		DCCP_INC_STATS(DCCP_MIB_OUTSEGS); | ||||
| 
 | ||||
| 		err = ip_queue_xmit(skb, 0); | ||||
| 		if (err <= 0) | ||||
| 			return err; | ||||
| 
 | ||||
| 		/* NET_XMIT_CN is special. It does not guarantee,
 | ||||
| 		 * that this packet is lost. It tells that device | ||||
| 		 * is about to start to drop packets or already | ||||
| 		 * drops some packets of the same priority and | ||||
| 		 * invokes us to send less aggressively. | ||||
| 		 */ | ||||
| 		return err == NET_XMIT_CN ? 0 : err; | ||||
| 	} | ||||
| 	return -ENOBUFS; | ||||
| } | ||||
| 
 | ||||
| unsigned int dccp_sync_mss(struct sock *sk, u32 pmtu) | ||||
| { | ||||
| 	struct dccp_sock *dp = dccp_sk(sk); | ||||
| 	int mss_now; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * FIXME: we really should be using the af_specific thing to support IPv6. | ||||
| 	 * mss_now = pmtu - tp->af_specific->net_header_len - sizeof(struct dccp_hdr) - sizeof(struct dccp_hdr_ext); | ||||
| 	 */ | ||||
| 	mss_now = pmtu - sizeof(struct iphdr) - sizeof(struct dccp_hdr) - sizeof(struct dccp_hdr_ext); | ||||
| 
 | ||||
| 	/* Now subtract optional transport overhead */ | ||||
| 	mss_now -= dp->dccps_ext_header_len; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * FIXME: this should come from the CCID infrastructure, where, say, | ||||
| 	 * TFRC will say it wants TIMESTAMPS, ELAPSED time, etc, for now lets | ||||
| 	 * put a rough estimate for NDP + TIMESTAMP + TIMESTAMP_ECHO + ELAPSED | ||||
| 	 * TIME + TFRC_OPT_LOSS_EVENT_RATE + TFRC_OPT_RECEIVE_RATE + padding to | ||||
| 	 * make it a multiple of 4 | ||||
| 	 */ | ||||
| 
 | ||||
| 	mss_now -= ((5 + 6 + 10 + 6 + 6 + 6 + 3) / 4) * 4; | ||||
| 
 | ||||
| 	/* And store cached results */ | ||||
| 	dp->dccps_pmtu_cookie = pmtu; | ||||
| 	dp->dccps_mss_cache = mss_now; | ||||
| 
 | ||||
| 	return mss_now; | ||||
| } | ||||
| 
 | ||||
| int dccp_retransmit_skb(struct sock *sk, struct sk_buff *skb) | ||||
| { | ||||
| 	if (inet_sk_rebuild_header(sk) != 0) | ||||
| 		return -EHOSTUNREACH; /* Routing failure or similar. */ | ||||
| 
 | ||||
| 	return dccp_transmit_skb(sk, (skb_cloned(skb) ? | ||||
| 				      pskb_copy(skb, GFP_ATOMIC): | ||||
| 				      skb_clone(skb, GFP_ATOMIC))); | ||||
| } | ||||
| 
 | ||||
| struct sk_buff *dccp_make_response(struct sock *sk, struct dst_entry *dst, | ||||
| 				   struct request_sock *req) | ||||
| { | ||||
| 	struct dccp_hdr *dh; | ||||
| 	const int dccp_header_size = sizeof(struct dccp_hdr) + | ||||
| 				     sizeof(struct dccp_hdr_ext) + | ||||
| 				     sizeof(struct dccp_hdr_response); | ||||
| 	struct sk_buff *skb = sock_wmalloc(sk, MAX_HEADER + DCCP_MAX_OPT_LEN + | ||||
| 					       dccp_header_size, 1, | ||||
| 					   GFP_ATOMIC); | ||||
| 	if (skb == NULL) | ||||
| 		return NULL; | ||||
| 
 | ||||
| 	/* Reserve space for headers. */ | ||||
| 	skb_reserve(skb, MAX_HEADER + DCCP_MAX_OPT_LEN + dccp_header_size); | ||||
| 
 | ||||
| 	skb->dst = dst_clone(dst); | ||||
| 	skb->csum = 0; | ||||
| 
 | ||||
| 	DCCP_SKB_CB(skb)->dccpd_type = DCCP_PKT_RESPONSE; | ||||
| 	DCCP_SKB_CB(skb)->dccpd_seq  = dccp_rsk(req)->dreq_iss; | ||||
| 	dccp_insert_options(sk, skb); | ||||
| 
 | ||||
| 	skb->h.raw = skb_push(skb, dccp_header_size); | ||||
| 
 | ||||
| 	dh = dccp_hdr(skb); | ||||
| 	memset(dh, 0, dccp_header_size); | ||||
| 
 | ||||
| 	dh->dccph_sport	= inet_sk(sk)->sport; | ||||
| 	dh->dccph_dport	= inet_rsk(req)->rmt_port; | ||||
| 	dh->dccph_doff	= (dccp_header_size + DCCP_SKB_CB(skb)->dccpd_opt_len) / 4; | ||||
| 	dh->dccph_type	= DCCP_PKT_RESPONSE; | ||||
| 	dh->dccph_x	= 1; | ||||
| 	dccp_hdr_set_seq(dh, dccp_rsk(req)->dreq_iss); | ||||
| 	dccp_hdr_set_ack(dccp_hdr_ack_bits(skb), dccp_rsk(req)->dreq_isr); | ||||
| 
 | ||||
| 	dh->dccph_checksum = dccp_v4_checksum(skb); | ||||
| 
 | ||||
| 	DCCP_INC_STATS(DCCP_MIB_OUTSEGS); | ||||
| 	return skb; | ||||
| } | ||||
| 
 | ||||
| struct sk_buff *dccp_make_reset(struct sock *sk, struct dst_entry *dst, | ||||
| 				const enum dccp_reset_codes code) | ||||
| 				    | ||||
| { | ||||
| 	struct dccp_hdr *dh; | ||||
| 	struct dccp_sock *dp = dccp_sk(sk); | ||||
| 	const int dccp_header_size = sizeof(struct dccp_hdr) + | ||||
| 				     sizeof(struct dccp_hdr_ext) + | ||||
| 				     sizeof(struct dccp_hdr_reset); | ||||
| 	struct sk_buff *skb = sock_wmalloc(sk, MAX_HEADER + DCCP_MAX_OPT_LEN + | ||||
| 					       dccp_header_size, 1, | ||||
| 					   GFP_ATOMIC); | ||||
| 	if (skb == NULL) | ||||
| 		return NULL; | ||||
| 
 | ||||
| 	/* Reserve space for headers. */ | ||||
| 	skb_reserve(skb, MAX_HEADER + DCCP_MAX_OPT_LEN + dccp_header_size); | ||||
| 
 | ||||
| 	skb->dst = dst_clone(dst); | ||||
| 	skb->csum = 0; | ||||
| 
 | ||||
| 	dccp_inc_seqno(&dp->dccps_gss); | ||||
| 
 | ||||
| 	DCCP_SKB_CB(skb)->dccpd_reset_code = code; | ||||
| 	DCCP_SKB_CB(skb)->dccpd_type	   = DCCP_PKT_RESET; | ||||
| 	DCCP_SKB_CB(skb)->dccpd_seq	   = dp->dccps_gss; | ||||
| 	dccp_insert_options(sk, skb); | ||||
| 
 | ||||
| 	skb->h.raw = skb_push(skb, dccp_header_size); | ||||
| 
 | ||||
| 	dh = dccp_hdr(skb); | ||||
| 	memset(dh, 0, dccp_header_size); | ||||
| 
 | ||||
| 	dh->dccph_sport	= inet_sk(sk)->sport; | ||||
| 	dh->dccph_dport	= inet_sk(sk)->dport; | ||||
| 	dh->dccph_doff	= (dccp_header_size + DCCP_SKB_CB(skb)->dccpd_opt_len) / 4; | ||||
| 	dh->dccph_type	= DCCP_PKT_RESET; | ||||
| 	dh->dccph_x	= 1; | ||||
| 	dccp_hdr_set_seq(dh, dp->dccps_gss); | ||||
| 	dccp_hdr_set_ack(dccp_hdr_ack_bits(skb), dp->dccps_gsr); | ||||
| 
 | ||||
| 	dccp_hdr_reset(skb)->dccph_reset_code = code; | ||||
| 
 | ||||
| 	dh->dccph_checksum = dccp_v4_checksum(skb); | ||||
| 
 | ||||
| 	DCCP_INC_STATS(DCCP_MIB_OUTSEGS); | ||||
| 	return skb; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Do all connect socket setups that can be done AF independent. | ||||
|  */ | ||||
| static inline void dccp_connect_init(struct sock *sk) | ||||
| { | ||||
| 	struct dst_entry *dst = __sk_dst_get(sk); | ||||
| 	struct inet_connection_sock *icsk = inet_csk(sk); | ||||
| 
 | ||||
| 	sk->sk_err = 0; | ||||
| 	sock_reset_flag(sk, SOCK_DONE); | ||||
| 	 | ||||
| 	dccp_sync_mss(sk, dst_mtu(dst)); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * FIXME: set dp->{dccps_swh,dccps_swl}, with | ||||
| 	 * something like dccp_inc_seq | ||||
| 	 */ | ||||
| 
 | ||||
| 	icsk->icsk_retransmits = 0; | ||||
| } | ||||
| 
 | ||||
| int dccp_connect(struct sock *sk) | ||||
| { | ||||
| 	struct sk_buff *skb; | ||||
| 	struct inet_connection_sock *icsk = inet_csk(sk); | ||||
| 
 | ||||
| 	dccp_connect_init(sk); | ||||
| 
 | ||||
| 	skb = alloc_skb(MAX_DCCP_HEADER + 15, sk->sk_allocation); | ||||
| 	if (unlikely(skb == NULL)) | ||||
| 		return -ENOBUFS; | ||||
| 
 | ||||
| 	/* Reserve space for headers. */ | ||||
| 	skb_reserve(skb, MAX_DCCP_HEADER); | ||||
| 
 | ||||
| 	DCCP_SKB_CB(skb)->dccpd_type = DCCP_PKT_REQUEST; | ||||
| 	/* FIXME: set service to something meaningful, coming
 | ||||
| 	 * from userspace*/ | ||||
| 	DCCP_SKB_CB(skb)->dccpd_service = 0; | ||||
| 	skb->csum = 0; | ||||
| 	skb_set_owner_w(skb, sk); | ||||
| 
 | ||||
| 	BUG_TRAP(sk->sk_send_head == NULL); | ||||
| 	sk->sk_send_head = skb; | ||||
| 	dccp_transmit_skb(sk, skb_clone(skb, GFP_KERNEL)); | ||||
| 	DCCP_INC_STATS(DCCP_MIB_ACTIVEOPENS); | ||||
| 
 | ||||
| 	/* Timer for repeating the REQUEST until an answer. */ | ||||
| 	inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS, icsk->icsk_rto, TCP_RTO_MAX); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| void dccp_send_ack(struct sock *sk) | ||||
| { | ||||
| 	/* If we have been reset, we may not send again. */ | ||||
| 	if (sk->sk_state != DCCP_CLOSED) { | ||||
| 		struct sk_buff *skb = alloc_skb(MAX_DCCP_HEADER, GFP_ATOMIC); | ||||
| 
 | ||||
| 		if (skb == NULL) { | ||||
| 			inet_csk_schedule_ack(sk); | ||||
| 			inet_csk(sk)->icsk_ack.ato = TCP_ATO_MIN; | ||||
| 			inet_csk_reset_xmit_timer(sk, ICSK_TIME_DACK, TCP_DELACK_MAX, TCP_RTO_MAX); | ||||
| 			return; | ||||
| 		} | ||||
| 
 | ||||
| 		/* Reserve space for headers */ | ||||
| 		skb_reserve(skb, MAX_DCCP_HEADER); | ||||
| 		skb->csum = 0; | ||||
| 		DCCP_SKB_CB(skb)->dccpd_type = DCCP_PKT_ACK; | ||||
| 		skb_set_owner_w(skb, sk); | ||||
| 		dccp_transmit_skb(sk, skb); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| EXPORT_SYMBOL_GPL(dccp_send_ack); | ||||
| 
 | ||||
| void dccp_send_delayed_ack(struct sock *sk) | ||||
| { | ||||
| 	struct inet_connection_sock *icsk = inet_csk(sk); | ||||
| 	/*
 | ||||
| 	 * FIXME: tune this timer. elapsed time fixes the skew, so no problem | ||||
| 	 * with using 2s, and active senders also piggyback the ACK into a | ||||
| 	 * DATAACK packet, so this is really for quiescent senders. | ||||
| 	 */ | ||||
| 	unsigned long timeout = jiffies + 2 * HZ; | ||||
| 
 | ||||
| 	/* Use new timeout only if there wasn't a older one earlier. */ | ||||
| 	if (icsk->icsk_ack.pending & ICSK_ACK_TIMER) { | ||||
| 		/* If delack timer was blocked or is about to expire,
 | ||||
| 		 * send ACK now. | ||||
| 		 * | ||||
| 		 * FIXME: check the "about to expire" part | ||||
| 		 */ | ||||
| 		if (icsk->icsk_ack.blocked) { | ||||
| 			dccp_send_ack(sk); | ||||
| 			return; | ||||
| 		} | ||||
| 
 | ||||
| 		if (!time_before(timeout, icsk->icsk_ack.timeout)) | ||||
| 			timeout = icsk->icsk_ack.timeout; | ||||
| 	} | ||||
| 	icsk->icsk_ack.pending |= ICSK_ACK_SCHED | ICSK_ACK_TIMER; | ||||
| 	icsk->icsk_ack.timeout = timeout; | ||||
| 	sk_reset_timer(sk, &icsk->icsk_delack_timer, timeout); | ||||
| } | ||||
| 
 | ||||
| void dccp_send_sync(struct sock *sk, u64 seq) | ||||
| { | ||||
| 	/*
 | ||||
| 	 * We are not putting this on the write queue, so | ||||
| 	 * dccp_transmit_skb() will set the ownership to this | ||||
| 	 * sock. | ||||
| 	 */ | ||||
| 	struct sk_buff *skb = alloc_skb(MAX_DCCP_HEADER, GFP_ATOMIC); | ||||
| 
 | ||||
| 	if (skb == NULL) | ||||
| 		/* FIXME: how to make sure the sync is sent? */ | ||||
| 		return; | ||||
| 
 | ||||
| 	/* Reserve space for headers and prepare control bits. */ | ||||
| 	skb_reserve(skb, MAX_DCCP_HEADER); | ||||
| 	skb->csum = 0; | ||||
| 	DCCP_SKB_CB(skb)->dccpd_type = DCCP_PKT_SYNC; | ||||
| 	DCCP_SKB_CB(skb)->dccpd_seq = seq; | ||||
| 
 | ||||
| 	skb_set_owner_w(skb, sk); | ||||
| 	dccp_transmit_skb(sk, skb); | ||||
| } | ||||
| 
 | ||||
| /* Send a DCCP_PKT_CLOSE/CLOSEREQ.  The caller locks the socket for us.  This cannot be
 | ||||
|  * allowed to fail queueing a DCCP_PKT_CLOSE/CLOSEREQ frame under any circumstances. | ||||
|  */ | ||||
| void dccp_send_close(struct sock *sk) | ||||
| { | ||||
| 	struct dccp_sock *dp = dccp_sk(sk); | ||||
| 	struct sk_buff *skb; | ||||
| 
 | ||||
| 	/* Socket is locked, keep trying until memory is available. */ | ||||
| 	for (;;) { | ||||
| 		skb = alloc_skb(sk->sk_prot->max_header, GFP_KERNEL); | ||||
| 		if (skb != NULL) | ||||
| 			break; | ||||
| 		yield(); | ||||
| 	} | ||||
| 
 | ||||
| 	/* Reserve space for headers and prepare control bits. */ | ||||
| 	skb_reserve(skb, sk->sk_prot->max_header); | ||||
| 	skb->csum = 0; | ||||
| 	DCCP_SKB_CB(skb)->dccpd_type = dp->dccps_role == DCCP_ROLE_CLIENT ? DCCP_PKT_CLOSE : DCCP_PKT_CLOSEREQ; | ||||
| 
 | ||||
| 	skb_set_owner_w(skb, sk); | ||||
| 	dccp_transmit_skb(sk, skb); | ||||
| 
 | ||||
| 	ccid_hc_rx_exit(dp->dccps_hc_rx_ccid, sk); | ||||
| 	ccid_hc_tx_exit(dp->dccps_hc_tx_ccid, sk); | ||||
| } | ||||
							
								
								
									
										818
									
								
								net/dccp/proto.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										818
									
								
								net/dccp/proto.c
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,818 @@ | |||
| /*
 | ||||
|  *  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/config.h> | ||||
| #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> | ||||
| 
 | ||||
| #include <net/inet_common.h> | ||||
| #include <net/ip.h> | ||||
| #include <net/protocol.h> | ||||
| #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 <linux/dccp.h> | ||||
| 
 | ||||
| #include "ccid.h" | ||||
| #include "dccp.h" | ||||
| 
 | ||||
| DEFINE_SNMP_STAT(struct dccp_mib, dccp_statistics); | ||||
| 
 | ||||
| atomic_t dccp_orphan_count = ATOMIC_INIT(0); | ||||
| 
 | ||||
| static struct net_protocol dccp_protocol = { | ||||
| 	.handler	= dccp_v4_rcv, | ||||
| 	.err_handler	= dccp_v4_err, | ||||
| }; | ||||
| 
 | ||||
| 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); | ||||
| 
 | ||||
| static inline int dccp_listen_start(struct sock *sk) | ||||
| { | ||||
| 	dccp_sk(sk)->dccps_role = DCCP_ROLE_LISTEN; | ||||
| 	return inet_csk_listen_start(sk, TCP_SYNQ_HSIZE); | ||||
| } | ||||
| 
 | ||||
| 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; | ||||
| } | ||||
| 
 | ||||
| int dccp_ioctl(struct sock *sk, int cmd, unsigned long arg) | ||||
| { | ||||
| 	dccp_pr_debug("entry\n"); | ||||
| 	return -ENOIOCTLCMD; | ||||
| } | ||||
| 
 | ||||
| int dccp_setsockopt(struct sock *sk, int level, int optname, | ||||
| 		    char *optval, int optlen) | ||||
| { | ||||
| 	dccp_pr_debug("entry\n"); | ||||
| 
 | ||||
| 	if (level != SOL_DCCP) | ||||
| 		return ip_setsockopt(sk, level, optname, optval, optlen); | ||||
| 
 | ||||
| 	return -EOPNOTSUPP; | ||||
| } | ||||
| 
 | ||||
| int dccp_getsockopt(struct sock *sk, int level, int optname, | ||||
| 		    char *optval, int *optlen) | ||||
| { | ||||
| 	dccp_pr_debug("entry\n"); | ||||
| 
 | ||||
| 	if (level != SOL_DCCP) | ||||
| 		return ip_getsockopt(sk, level, optname, optval, optlen); | ||||
| 
 | ||||
| 	return -EOPNOTSUPP; | ||||
| } | ||||
| 
 | ||||
| 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); | ||||
| 
 | ||||
| 	timeo = sock_sndtimeo(sk, flags & MSG_DONTWAIT); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * 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) | ||||
| 			goto out_err; | ||||
| 
 | ||||
| 	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); | ||||
| 	if (rc == 0) { | ||||
| 		struct dccp_skb_cb *dcb = DCCP_SKB_CB(skb); | ||||
| 		const struct dccp_ackpkts *ap = dp->dccps_hc_rx_ackpkts; | ||||
| 		long delay;  | ||||
| 
 | ||||
| 		/*
 | ||||
| 		 * XXX: This is just to match the Waikato tree CA interaction | ||||
| 		 * points, after the CCID3 code is stable and I have a better | ||||
| 		 * understanding of behaviour I'll change this to look more like | ||||
| 		 * TCP. | ||||
| 		 */ | ||||
| 		while (1) { | ||||
| 			rc = ccid_hc_tx_send_packet(dp->dccps_hc_tx_ccid, sk, | ||||
| 						    skb, len, &delay); | ||||
| 			if (rc == 0) | ||||
| 				break; | ||||
| 			if (rc != -EAGAIN) | ||||
| 				goto out_discard; | ||||
| 			if (delay > timeo) | ||||
| 				goto out_discard; | ||||
| 			release_sock(sk); | ||||
| 			delay = schedule_timeout(delay); | ||||
| 			lock_sock(sk); | ||||
| 			timeo -= delay; | ||||
| 			if (signal_pending(current)) | ||||
| 				goto out_interrupted; | ||||
| 			rc = -EPIPE; | ||||
| 			if (!(sk->sk_state == DCCP_PARTOPEN || sk->sk_state == DCCP_OPEN)) | ||||
| 				goto out_discard; | ||||
| 		} | ||||
| 
 | ||||
| 		if (sk->sk_state == DCCP_PARTOPEN) { | ||||
| 			/* See 8.1.5.  Handshake Completion */ | ||||
| 			inet_csk_schedule_ack(sk); | ||||
| 			inet_csk_reset_xmit_timer(sk, ICSK_TIME_DACK, inet_csk(sk)->icsk_rto, TCP_RTO_MAX); | ||||
| 			dcb->dccpd_type = DCCP_PKT_DATAACK; | ||||
| 			/* FIXME: we really should have a dccps_ack_pending or use icsk */ | ||||
| 		} else if (inet_csk_ack_scheduled(sk) || | ||||
| 			   (dp->dccps_options.dccpo_send_ack_vector && | ||||
| 			    ap->dccpap_buf_ackno != DCCP_MAX_SEQNO + 1 && | ||||
| 			    ap->dccpap_ack_seqno == DCCP_MAX_SEQNO + 1)) | ||||
| 			dcb->dccpd_type = DCCP_PKT_DATAACK; | ||||
| 		else | ||||
| 			dcb->dccpd_type = DCCP_PKT_DATA; | ||||
| 		dccp_transmit_skb(sk, skb); | ||||
| 		ccid_hc_tx_packet_sent(dp->dccps_hc_tx_ccid, sk, 0, len); | ||||
| 	} else { | ||||
| out_discard: | ||||
| 		kfree_skb(skb); | ||||
| 	} | ||||
| out_release: | ||||
| 	release_sock(sk); | ||||
| 	return rc ? : len; | ||||
| out_err: | ||||
| 	rc = sk_stream_error(sk, flags, rc); | ||||
| 	goto out_release; | ||||
| out_interrupted: | ||||
| 	rc = sock_intr_errno(timeo); | ||||
| 	goto out_discard; | ||||
| } | ||||
| 
 | ||||
| EXPORT_SYMBOL(dccp_sendmsg); | ||||
| 
 | ||||
| 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; | ||||
| 	int copied = 0; | ||||
| 	unsigned long used; | ||||
| 	int err; | ||||
| 	int target;		/* Read at least this many bytes */ | ||||
| 	long timeo; | ||||
| 
 | ||||
| 	lock_sock(sk); | ||||
| 
 | ||||
| 	err = -ENOTCONN; | ||||
| 	if (sk->sk_state == DCCP_LISTEN) | ||||
| 		goto out; | ||||
| 
 | ||||
| 	timeo = sock_rcvtimeo(sk, nonblock); | ||||
| 
 | ||||
| 	/* Urgent data needs to be handled specially. */ | ||||
| 	if (flags & MSG_OOB) | ||||
| 		goto recv_urg; | ||||
| 
 | ||||
| 	/* FIXME */ | ||||
| #if 0 | ||||
| 	seq = &tp->copied_seq; | ||||
| 	if (flags & MSG_PEEK) { | ||||
| 		peek_seq = tp->copied_seq; | ||||
| 		seq = &peek_seq; | ||||
| 	} | ||||
| #endif | ||||
| 
 | ||||
| 	target = sock_rcvlowat(sk, flags & MSG_WAITALL, len); | ||||
| 
 | ||||
| 	do { | ||||
| 		struct sk_buff *skb; | ||||
| 		u32 offset; | ||||
| 
 | ||||
| 	/* FIXME */ | ||||
| #if 0 | ||||
| 		/* Are we at urgent data? Stop if we have read anything or have SIGURG pending. */ | ||||
| 		if (tp->urg_data && tp->urg_seq == *seq) { | ||||
| 			if (copied) | ||||
| 				break; | ||||
| 			if (signal_pending(current)) { | ||||
| 				copied = timeo ? sock_intr_errno(timeo) : -EAGAIN; | ||||
| 				break; | ||||
| 			} | ||||
| 		} | ||||
| #endif | ||||
| 
 | ||||
| 		/* Next get a buffer. */ | ||||
| 
 | ||||
| 		skb = skb_peek(&sk->sk_receive_queue); | ||||
| 		do { | ||||
| 			if (!skb) | ||||
| 				break; | ||||
| 
 | ||||
| 			offset = 0; | ||||
| 			dh = dccp_hdr(skb); | ||||
| 
 | ||||
| 			if (dh->dccph_type == DCCP_PKT_DATA || | ||||
| 			    dh->dccph_type == DCCP_PKT_DATAACK) | ||||
| 				goto found_ok_skb; | ||||
| 
 | ||||
| 			if (dh->dccph_type == DCCP_PKT_RESET || | ||||
| 			    dh->dccph_type == DCCP_PKT_CLOSE) { | ||||
| 				dccp_pr_debug("found fin ok!\n"); | ||||
| 				goto found_fin_ok; | ||||
| 			} | ||||
| 			dccp_pr_debug("packet_type=%s\n", dccp_packet_name(dh->dccph_type)); | ||||
| 			BUG_TRAP(flags & MSG_PEEK); | ||||
| 			skb = skb->next; | ||||
| 		} while (skb != (struct sk_buff *)&sk->sk_receive_queue); | ||||
| 
 | ||||
| 		/* Well, if we have backlog, try to process it now yet. */ | ||||
| 		if (copied >= target && !sk->sk_backlog.tail) | ||||
| 			break; | ||||
| 
 | ||||
| 		if (copied) { | ||||
| 			if (sk->sk_err || | ||||
| 			    sk->sk_state == DCCP_CLOSED || | ||||
| 			    (sk->sk_shutdown & RCV_SHUTDOWN) || | ||||
| 			    !timeo || | ||||
| 			    signal_pending(current) || | ||||
| 			    (flags & MSG_PEEK)) | ||||
| 				break; | ||||
| 		} else { | ||||
| 			if (sock_flag(sk, SOCK_DONE)) | ||||
| 				break; | ||||
| 
 | ||||
| 			if (sk->sk_err) { | ||||
| 				copied = sock_error(sk); | ||||
| 				break; | ||||
| 			} | ||||
| 
 | ||||
| 			if (sk->sk_shutdown & RCV_SHUTDOWN) | ||||
| 				break; | ||||
| 
 | ||||
| 			if (sk->sk_state == DCCP_CLOSED) { | ||||
| 				if (!sock_flag(sk, SOCK_DONE)) { | ||||
| 					/* This occurs when user tries to read
 | ||||
| 					 * from never connected socket. | ||||
| 					 */ | ||||
| 					copied = -ENOTCONN; | ||||
| 					break; | ||||
| 				} | ||||
| 				break; | ||||
| 			} | ||||
| 
 | ||||
| 			if (!timeo) { | ||||
| 				copied = -EAGAIN; | ||||
| 				break; | ||||
| 			} | ||||
| 
 | ||||
| 			if (signal_pending(current)) { | ||||
| 				copied = sock_intr_errno(timeo); | ||||
| 				break; | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		/* FIXME: cleanup_rbuf(sk, copied); */ | ||||
| 
 | ||||
| 		if (copied >= target) { | ||||
| 			/* Do not sleep, just process backlog. */ | ||||
| 			release_sock(sk); | ||||
| 			lock_sock(sk); | ||||
| 		} else | ||||
| 			sk_wait_data(sk, &timeo); | ||||
| 
 | ||||
| 		continue; | ||||
| 
 | ||||
| 	found_ok_skb: | ||||
| 		/* Ok so how much can we use? */ | ||||
| 		used = skb->len - offset; | ||||
| 		if (len < used) | ||||
| 			used = len; | ||||
| 
 | ||||
| 		if (!(flags & MSG_TRUNC)) { | ||||
| 			err = skb_copy_datagram_iovec(skb, offset, | ||||
| 						      msg->msg_iov, used); | ||||
| 			if (err) { | ||||
| 				/* Exception. Bailout! */ | ||||
| 				if (!copied) | ||||
| 					copied = -EFAULT; | ||||
| 				break; | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		copied += used; | ||||
| 		len -= used; | ||||
| 
 | ||||
| 		/* FIXME: tcp_rcv_space_adjust(sk); */ | ||||
| 
 | ||||
| //skip_copy:
 | ||||
| 		if (used + offset < skb->len) | ||||
| 			continue; | ||||
| 
 | ||||
| 		if (!(flags & MSG_PEEK)) | ||||
| 			sk_eat_skb(sk, skb); | ||||
| 		continue; | ||||
| 	found_fin_ok: | ||||
| 		if (!(flags & MSG_PEEK)) | ||||
| 			sk_eat_skb(sk, skb); | ||||
| 		break; | ||||
| 		 | ||||
| 	} while (len > 0); | ||||
| 
 | ||||
| 	/* According to UNIX98, msg_name/msg_namelen are ignored
 | ||||
| 	 * on connected socket. I was just happy when found this 8) --ANK | ||||
| 	 */ | ||||
| 
 | ||||
| 	/* Clean up data we have read: This will do ACK frames. */ | ||||
| 	/* FIXME: cleanup_rbuf(sk, copied); */ | ||||
| 
 | ||||
| 	release_sock(sk); | ||||
| 	return copied; | ||||
| 
 | ||||
| out: | ||||
| 	release_sock(sk); | ||||
| 	return err; | ||||
| 
 | ||||
| recv_urg: | ||||
| 	/* FIXME: err = tcp_recv_urg(sk, timeo, msg, len, flags, addr_len); */ | ||||
| 	goto out; | ||||
| } | ||||
| 
 | ||||
| static int inet_dccp_listen(struct socket *sock, int backlog) | ||||
| { | ||||
| 	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 | ||||
| 		 */ | ||||
| 		err = dccp_listen_start(sk); | ||||
| 		if (err) | ||||
| 			goto out; | ||||
| 	} | ||||
| 	sk->sk_max_ack_backlog = backlog; | ||||
| 	err = 0; | ||||
| 
 | ||||
| out: | ||||
| 	release_sock(sk); | ||||
| 	return err; | ||||
| } | ||||
| 
 | ||||
| static const unsigned char dccp_new_state[] = { | ||||
| 	/* current state:        new state:      action:	*/ | ||||
| 	[0]			= DCCP_CLOSED, | ||||
| 	[DCCP_OPEN] 		= DCCP_CLOSING | DCCP_ACTION_FIN, | ||||
| 	[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, | ||||
| }; | ||||
| 
 | ||||
| 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) | ||||
| { | ||||
| 	struct sk_buff *skb; | ||||
| 
 | ||||
| 	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; | ||||
| 	} | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * 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)) { | ||||
| 		dccp_send_close(sk); | ||||
| 	} | ||||
| 
 | ||||
| 	sk_stream_wait_close(sk, timeout); | ||||
| 
 | ||||
| adjudge_to_death: | ||||
| 	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)); | ||||
| 
 | ||||
| 	sock_hold(sk); | ||||
| 	sock_orphan(sk); | ||||
| 						 | ||||
| 	if (sk->sk_state != DCCP_CLOSED) | ||||
| 		dccp_set_state(sk, DCCP_CLOSED); | ||||
| 
 | ||||
| 	atomic_inc(&dccp_orphan_count); | ||||
| 	if (sk->sk_state == DCCP_CLOSED) | ||||
| 		inet_csk_destroy_sock(sk); | ||||
| 
 | ||||
| 	/* Otherwise, socket is reprieved until protocol close. */ | ||||
| 
 | ||||
| 	bh_unlock_sock(sk); | ||||
| 	local_bh_enable(); | ||||
| 	sock_put(sk); | ||||
| } | ||||
| 
 | ||||
| void dccp_shutdown(struct sock *sk, int how) | ||||
| { | ||||
| 	dccp_pr_debug("entry\n"); | ||||
| } | ||||
| 
 | ||||
| struct proto_ops inet_dccp_ops = { | ||||
| 	.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, | ||||
| 	.poll		= sock_no_poll, | ||||
| 	.ioctl		= inet_ioctl, | ||||
| 	.listen		= inet_dccp_listen, /* FIXME: work on inet_listen to rename it to sock_common_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, | ||||
| }; | ||||
| 
 | ||||
| extern struct net_proto_family inet_family_ops; | ||||
| 
 | ||||
| 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		= 0, | ||||
| }; | ||||
| 
 | ||||
| /*
 | ||||
|  * 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. | ||||
|  */ | ||||
| struct socket *dccp_ctl_socket; | ||||
| 
 | ||||
| static char dccp_ctl_socket_err_msg[] __initdata = | ||||
| 	KERN_ERR "DCCP: Failed to create the control socket.\n"; | ||||
| 
 | ||||
| static int __init dccp_ctl_sock_init(void) | ||||
| { | ||||
| 	int rc = sock_create_kern(PF_INET, SOCK_DCCP, IPPROTO_DCCP, | ||||
| 				  &dccp_ctl_socket); | ||||
| 	if (rc < 0) | ||||
| 		printk(dccp_ctl_socket_err_msg); | ||||
| 	else { | ||||
| 		dccp_ctl_socket->sk->sk_allocation = GFP_ATOMIC; | ||||
| 		inet_sk(dccp_ctl_socket->sk)->uc_ttl = -1; | ||||
| 
 | ||||
| 		/* Unhash it so that IP input processing does not even
 | ||||
| 		 * see it, we do not wish this socket to see incoming | ||||
| 		 * packets. | ||||
| 		 */ | ||||
| 		dccp_ctl_socket->sk->sk_prot->unhash(dccp_ctl_socket->sk); | ||||
| 	} | ||||
| 
 | ||||
| 	return rc; | ||||
| } | ||||
| 
 | ||||
| static void __exit dccp_ctl_sock_exit(void) | ||||
| { | ||||
| 	if (dccp_ctl_socket != NULL) | ||||
| 		sock_release(dccp_ctl_socket); | ||||
| } | ||||
| 
 | ||||
| static int __init init_dccp_v4_mibs(void) | ||||
| { | ||||
| 	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; | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| static int thash_entries; | ||||
| module_param(thash_entries, int, 0444); | ||||
| MODULE_PARM_DESC(thash_entries, "Number of ehash buckets"); | ||||
| 
 | ||||
| int dccp_debug; | ||||
| module_param(dccp_debug, int, 0444); | ||||
| MODULE_PARM_DESC(dccp_debug, "Enable debug messages"); | ||||
| 
 | ||||
| static int __init dccp_init(void) | ||||
| { | ||||
| 	unsigned long goal; | ||||
| 	int ehash_order, bhash_order, i; | ||||
| 	int rc = proto_register(&dccp_v4_prot, 1); | ||||
| 
 | ||||
| 	if (rc) | ||||
| 		goto out; | ||||
| 
 | ||||
| 	dccp_hashinfo.bind_bucket_cachep = kmem_cache_create("dccp_bind_bucket", | ||||
| 					       sizeof(struct inet_bind_bucket), | ||||
| 					       0, SLAB_HWCACHE_ALIGN, | ||||
| 					       NULL, NULL); | ||||
| 	if (!dccp_hashinfo.bind_bucket_cachep) | ||||
| 		goto out_proto_unregister; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * 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) | ||||
| 		goal = (thash_entries * sizeof(struct inet_ehash_bucket)) >> PAGE_SHIFT; | ||||
| 	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; | ||||
| 		while (dccp_hashinfo.ehash_size & (dccp_hashinfo.ehash_size - 1)) | ||||
| 			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) { | ||||
| 		printk(KERN_CRIT "Failed to allocate DCCP " | ||||
| 				 "established hash table\n"); | ||||
| 		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); | ||||
| 		if ((dccp_hashinfo.bhash_size > (64 * 1024)) && bhash_order > 0) | ||||
| 			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) { | ||||
| 		printk(KERN_CRIT "Failed to allocate DCCP bind hash table\n"); | ||||
| 		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); | ||||
| 	} | ||||
| 
 | ||||
| 	if (init_dccp_v4_mibs()) | ||||
| 		goto out_free_dccp_bhash; | ||||
| 
 | ||||
| 	rc = -EAGAIN; | ||||
| 	if (inet_add_protocol(&dccp_protocol, IPPROTO_DCCP)) | ||||
| 		goto out_free_dccp_v4_mibs; | ||||
| 
 | ||||
| 	inet_register_protosw(&dccp_v4_protosw); | ||||
| 
 | ||||
| 	rc = dccp_ctl_sock_init(); | ||||
| 	if (rc) | ||||
| 		goto out_unregister_protosw; | ||||
| out: | ||||
| 	return rc; | ||||
| out_unregister_protosw: | ||||
| 	inet_unregister_protosw(&dccp_v4_protosw); | ||||
| 	inet_del_protocol(&dccp_protocol, IPPROTO_DCCP); | ||||
| out_free_dccp_v4_mibs: | ||||
| 	free_percpu(dccp_statistics[0]); | ||||
| 	free_percpu(dccp_statistics[1]); | ||||
| 	dccp_statistics[0] = dccp_statistics[1] = NULL; | ||||
| 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; | ||||
| out_proto_unregister: | ||||
| 	proto_unregister(&dccp_v4_prot); | ||||
| 	goto out; | ||||
| } | ||||
| 
 | ||||
| static const char dccp_del_proto_err_msg[] __exitdata = | ||||
| 	KERN_ERR "can't remove dccp net_protocol\n"; | ||||
| 
 | ||||
| static void __exit dccp_fini(void) | ||||
| { | ||||
| 	dccp_ctl_sock_exit(); | ||||
| 
 | ||||
| 	inet_unregister_protosw(&dccp_v4_protosw); | ||||
| 
 | ||||
| 	if (inet_del_protocol(&dccp_protocol, IPPROTO_DCCP) < 0) | ||||
| 		printk(dccp_del_proto_err_msg); | ||||
| 
 | ||||
| 	/* Free the control endpoint.  */ | ||||
| 	sock_release(dccp_ctl_socket); | ||||
| 
 | ||||
| 	proto_unregister(&dccp_v4_prot); | ||||
| 
 | ||||
| 	kmem_cache_destroy(dccp_hashinfo.bind_bucket_cachep); | ||||
| } | ||||
| 
 | ||||
| module_init(dccp_init); | ||||
| module_exit(dccp_fini); | ||||
| 
 | ||||
| /* __stringify doesn't likes enums, so use SOCK_DCCP (6) value directly  */ | ||||
| MODULE_ALIAS("net-pf-" __stringify(PF_INET) "-6"); | ||||
| MODULE_LICENSE("GPL"); | ||||
| MODULE_AUTHOR("Arnaldo Carvalho de Melo <acme@conectiva.com.br>"); | ||||
| MODULE_DESCRIPTION("DCCP - Datagram Congestion Controlled Protocol"); | ||||
							
								
								
									
										249
									
								
								net/dccp/timer.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										249
									
								
								net/dccp/timer.c
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,249 @@ | |||
| /*
 | ||||
|  *  net/dccp/timer.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 "dccp.h" | ||||
| 
 | ||||
| static void dccp_write_timer(unsigned long data); | ||||
| static void dccp_keepalive_timer(unsigned long data); | ||||
| static void dccp_delack_timer(unsigned long data); | ||||
| 
 | ||||
| void dccp_init_xmit_timers(struct sock *sk) | ||||
| { | ||||
| 	inet_csk_init_xmit_timers(sk, &dccp_write_timer, &dccp_delack_timer, | ||||
| 				  &dccp_keepalive_timer); | ||||
| } | ||||
| 
 | ||||
| static void dccp_write_err(struct sock *sk) | ||||
| { | ||||
| 	sk->sk_err = sk->sk_err_soft ? : ETIMEDOUT; | ||||
| 	sk->sk_error_report(sk); | ||||
| 
 | ||||
| 	dccp_v4_send_reset(sk, DCCP_RESET_CODE_ABORTED); | ||||
| 	dccp_done(sk); | ||||
| 	DCCP_INC_STATS_BH(DCCP_MIB_ABORTONTIMEOUT); | ||||
| } | ||||
| 
 | ||||
| /* A write timeout has occurred. Process the after effects. */ | ||||
| static int dccp_write_timeout(struct sock *sk) | ||||
| { | ||||
| 	const struct inet_connection_sock *icsk = inet_csk(sk); | ||||
| 	int retry_until; | ||||
| 
 | ||||
| 	if (sk->sk_state == DCCP_REQUESTING || sk->sk_state == DCCP_PARTOPEN) { | ||||
| 		if (icsk->icsk_retransmits != 0) | ||||
| 			dst_negative_advice(&sk->sk_dst_cache); | ||||
| 		retry_until = icsk->icsk_syn_retries ? :  /* FIXME! */ 3 /* FIXME! sysctl_tcp_syn_retries */; | ||||
| 	} else { | ||||
| 		if (icsk->icsk_retransmits >= /* FIXME! sysctl_tcp_retries1 */ 5 /* FIXME! */) { | ||||
| 			/* NOTE. draft-ietf-tcpimpl-pmtud-01.txt requires pmtu black
 | ||||
| 			   hole detection. :-( | ||||
| 
 | ||||
| 			   It is place to make it. It is not made. I do not want | ||||
| 			   to make it. It is disguisting. It does not work in any | ||||
| 			   case. Let me to cite the same draft, which requires for | ||||
| 			   us to implement this: | ||||
| 
 | ||||
|    "The one security concern raised by this memo is that ICMP black holes | ||||
|    are often caused by over-zealous security administrators who block | ||||
|    all ICMP messages.  It is vitally important that those who design and | ||||
|    deploy security systems understand the impact of strict filtering on | ||||
|    upper-layer protocols.  The safest web site in the world is worthless | ||||
|    if most TCP implementations cannot transfer data from it.  It would | ||||
|    be far nicer to have all of the black holes fixed rather than fixing | ||||
|    all of the TCP implementations." | ||||
| 
 | ||||
|                            Golden words :-). | ||||
| 		   */ | ||||
| 
 | ||||
| 			dst_negative_advice(&sk->sk_dst_cache); | ||||
| 		} | ||||
| 
 | ||||
| 		retry_until = /* FIXME! */ 15 /* FIXME! sysctl_tcp_retries2 */; | ||||
| 		/*
 | ||||
| 		 * FIXME: see tcp_write_timout and tcp_out_of_resources | ||||
| 		 */ | ||||
| 	} | ||||
| 
 | ||||
| 	if (icsk->icsk_retransmits >= retry_until) { | ||||
| 		/* Has it gone just too far? */ | ||||
| 		dccp_write_err(sk); | ||||
| 		return 1; | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /* This is the same as tcp_delack_timer, sans prequeue & mem_reclaim stuff */ | ||||
| static void dccp_delack_timer(unsigned long data) | ||||
| { | ||||
| 	struct sock *sk = (struct sock *)data; | ||||
| 	struct inet_connection_sock *icsk = inet_csk(sk); | ||||
| 
 | ||||
| 	bh_lock_sock(sk); | ||||
| 	if (sock_owned_by_user(sk)) { | ||||
| 		/* Try again later. */ | ||||
| 		icsk->icsk_ack.blocked = 1; | ||||
| 		NET_INC_STATS_BH(LINUX_MIB_DELAYEDACKLOCKED); | ||||
| 		sk_reset_timer(sk, &icsk->icsk_delack_timer, jiffies + TCP_DELACK_MIN); | ||||
| 		goto out; | ||||
| 	} | ||||
| 
 | ||||
| 	if (sk->sk_state == DCCP_CLOSED || !(icsk->icsk_ack.pending & ICSK_ACK_TIMER)) | ||||
| 		goto out; | ||||
| 	if (time_after(icsk->icsk_ack.timeout, jiffies)) { | ||||
| 		sk_reset_timer(sk, &icsk->icsk_delack_timer, icsk->icsk_ack.timeout); | ||||
| 		goto out; | ||||
| 	} | ||||
| 
 | ||||
| 	icsk->icsk_ack.pending &= ~ICSK_ACK_TIMER; | ||||
| 
 | ||||
| 	if (inet_csk_ack_scheduled(sk)) { | ||||
| 		if (!icsk->icsk_ack.pingpong) { | ||||
| 			/* Delayed ACK missed: inflate ATO. */ | ||||
| 			icsk->icsk_ack.ato = min(icsk->icsk_ack.ato << 1, icsk->icsk_rto); | ||||
| 		} else { | ||||
| 			/* Delayed ACK missed: leave pingpong mode and
 | ||||
| 			 * deflate ATO. | ||||
| 			 */ | ||||
| 			icsk->icsk_ack.pingpong = 0; | ||||
| 			icsk->icsk_ack.ato = TCP_ATO_MIN; | ||||
| 		} | ||||
| 		dccp_send_ack(sk); | ||||
| 		NET_INC_STATS_BH(LINUX_MIB_DELAYEDACKS); | ||||
| 	} | ||||
| out: | ||||
| 	bh_unlock_sock(sk); | ||||
| 	sock_put(sk); | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  *	The DCCP retransmit timer. | ||||
|  */ | ||||
| static void dccp_retransmit_timer(struct sock *sk) | ||||
| { | ||||
| 	struct inet_connection_sock *icsk = inet_csk(sk); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * sk->sk_send_head has to have one skb with | ||||
| 	 * DCCP_SKB_CB(skb)->dccpd_type set to one of the retransmittable DCCP | ||||
| 	 * packet types (REQUEST, RESPONSE, the ACK in the 3way hanshake | ||||
| 	 * (PARTOPEN timer), etc). | ||||
| 	 */ | ||||
| 	BUG_TRAP(sk->sk_send_head != NULL); | ||||
| 
 | ||||
| 	/* 
 | ||||
| 	 * More than than 4MSL (8 minutes) has passed, a RESET(aborted) was | ||||
| 	 * sent, no need to retransmit, this sock is dead. | ||||
| 	 */ | ||||
| 	if (dccp_write_timeout(sk)) | ||||
| 		goto out; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * We want to know the number of packets retransmitted, not the | ||||
| 	 * total number of retransmissions of clones of original packets. | ||||
| 	 */ | ||||
| 	if (icsk->icsk_retransmits == 0) | ||||
| 		DCCP_INC_STATS_BH(DCCP_MIB_TIMEOUTS); | ||||
| 
 | ||||
| 	if (dccp_retransmit_skb(sk, sk->sk_send_head) < 0) { | ||||
| 		/*
 | ||||
| 		 * Retransmission failed because of local congestion, | ||||
| 		 * do not backoff. | ||||
| 		 */ | ||||
| 		if (icsk->icsk_retransmits == 0) | ||||
| 			icsk->icsk_retransmits = 1; | ||||
| 		inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS, | ||||
| 					  min(icsk->icsk_rto, | ||||
| 					      TCP_RESOURCE_PROBE_INTERVAL), | ||||
| 					  TCP_RTO_MAX); | ||||
| 		goto out; | ||||
| 	} | ||||
| 
 | ||||
| 	icsk->icsk_backoff++; | ||||
| 	icsk->icsk_retransmits++; | ||||
| 
 | ||||
| 	icsk->icsk_rto = min(icsk->icsk_rto << 1, DCCP_RTO_MAX); | ||||
| 	inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS, icsk->icsk_rto, TCP_RTO_MAX); | ||||
| 	if (icsk->icsk_retransmits > 3 /* FIXME: sysctl_dccp_retries1 */) | ||||
| 		__sk_dst_reset(sk); | ||||
| out:; | ||||
| } | ||||
| 
 | ||||
| static void dccp_write_timer(unsigned long data) | ||||
| { | ||||
| 	struct sock *sk = (struct sock *)data; | ||||
| 	struct inet_connection_sock *icsk = inet_csk(sk); | ||||
| 	int event = 0; | ||||
| 
 | ||||
| 	bh_lock_sock(sk); | ||||
| 	if (sock_owned_by_user(sk)) { | ||||
| 		/* Try again later */ | ||||
| 		sk_reset_timer(sk, &icsk->icsk_retransmit_timer, jiffies + (HZ / 20)); | ||||
| 		goto out; | ||||
| 	} | ||||
| 
 | ||||
| 	if (sk->sk_state == DCCP_CLOSED || !icsk->icsk_pending) | ||||
| 		goto out; | ||||
| 
 | ||||
| 	if (time_after(icsk->icsk_timeout, jiffies)) { | ||||
| 		sk_reset_timer(sk, &icsk->icsk_retransmit_timer, icsk->icsk_timeout); | ||||
| 		goto out; | ||||
| 	} | ||||
| 
 | ||||
| 	event = icsk->icsk_pending; | ||||
| 	icsk->icsk_pending = 0; | ||||
| 
 | ||||
| 	switch (event) { | ||||
| 	case ICSK_TIME_RETRANS: | ||||
| 		dccp_retransmit_timer(sk); | ||||
| 		break; | ||||
| 	} | ||||
| out: | ||||
| 	bh_unlock_sock(sk); | ||||
| 	sock_put(sk); | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  *	Timer for listening sockets | ||||
|  */ | ||||
| static void dccp_response_timer(struct sock *sk) | ||||
| { | ||||
| 	struct inet_connection_sock *icsk = inet_csk(sk); | ||||
| 	const int max_retries = icsk->icsk_syn_retries ? : TCP_SYNACK_RETRIES /* FIXME sysctl_tcp_synack_retries */; | ||||
| 
 | ||||
| 	reqsk_queue_prune(&icsk->icsk_accept_queue, sk, TCP_SYNQ_INTERVAL, | ||||
| 			  DCCP_TIMEOUT_INIT, DCCP_RTO_MAX, max_retries); | ||||
| } | ||||
| 
 | ||||
| static void dccp_keepalive_timer(unsigned long data) | ||||
| { | ||||
| 	struct sock *sk = (struct sock *)data; | ||||
| 
 | ||||
| 	/* Only process if socket is not in use. */ | ||||
| 	bh_lock_sock(sk); | ||||
| 	if (sock_owned_by_user(sk)) { | ||||
| 		/* Try again later. */  | ||||
| 		inet_csk_reset_keepalive_timer(sk, HZ / 20); | ||||
| 		goto out; | ||||
| 	} | ||||
| 
 | ||||
| 	if (sk->sk_state == DCCP_LISTEN) { | ||||
| 		dccp_response_timer(sk); | ||||
| 		goto out; | ||||
| 	} | ||||
| out: | ||||
| 	bh_unlock_sock(sk); | ||||
| 	sock_put(sk); | ||||
| } | ||||
		Loading…
	
		Reference in a new issue
	
	 Arnaldo Carvalho de Melo
						Arnaldo Carvalho de Melo