Commit 7c657876 authored by Arnaldo Carvalho de Melo's avatar Arnaldo Carvalho de Melo Committed by David S. Miller
Browse files

[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: default avatarArnaldo Carvalho de Melo <acme@ghostprotocols.net>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent c4365c92
#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)
......
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
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/
/*
* 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