mirror of
https://github.com/torvalds/linux.git
synced 2024-12-28 13:51:44 +00:00
1c14ac0ae8
We can get this value in an TIMESTAMP_ECHO and/or in an ELAPSED_TIME option, if receiving both give precendence to the biggest one. In my tests they are very close if not equal at all times, so we may well think about removing the code in CCID3 that inserts this option and leaving this to the core, and perhaps even use just TIMESTAMP_ECHO including the elapsed time. Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com>
863 lines
23 KiB
C
863 lines
23 KiB
C
/*
|
|
* net/dccp/options.c
|
|
*
|
|
* An implementation of the DCCP protocol
|
|
* Copyright (c) 2005 Aristeu Sergio Rozanski Filho <aris@cathedrallabs.org>
|
|
* Copyright (c) 2005 Arnaldo Carvalho de Melo <acme@ghostprotocols.net>
|
|
* Copyright (c) 2005 Ian McDonald <iam4@cs.waikato.ac.nz>
|
|
*
|
|
* 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 CONFIG_IP_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;
|
|
u32 elapsed_time;
|
|
|
|
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,
|
|
(unsigned long long)
|
|
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;
|
|
do_gettimeofday(&dp->dccps_timestamp_time);
|
|
|
|
dccp_pr_debug("%sTIMESTAMP=%u, ackno=%llu\n",
|
|
debug_prefix, opt_recv->dccpor_timestamp,
|
|
(unsigned long long)
|
|
DCCP_SKB_CB(skb)->dccpd_ack_seq);
|
|
break;
|
|
case DCCPO_TIMESTAMP_ECHO:
|
|
if (len != 4 && len != 6 && len != 8)
|
|
goto out_invalid_option;
|
|
|
|
opt_recv->dccpor_timestamp_echo = ntohl(*(u32 *)value);
|
|
|
|
dccp_pr_debug("%sTIMESTAMP_ECHO=%u, len=%d, ackno=%llu, ",
|
|
debug_prefix,
|
|
opt_recv->dccpor_timestamp_echo,
|
|
len + 2,
|
|
(unsigned long long)
|
|
DCCP_SKB_CB(skb)->dccpd_ack_seq);
|
|
|
|
|
|
if (len == 4)
|
|
break;
|
|
|
|
if (len == 6)
|
|
elapsed_time = ntohs(*(u16 *)(value + 4));
|
|
else
|
|
elapsed_time = ntohl(*(u32 *)(value + 4));
|
|
|
|
/* Give precedence to the biggest ELAPSED_TIME */
|
|
if (elapsed_time > opt_recv->dccpor_elapsed_time)
|
|
opt_recv->dccpor_elapsed_time = elapsed_time;
|
|
break;
|
|
case DCCPO_ELAPSED_TIME:
|
|
if (len != 2 && len != 4)
|
|
goto out_invalid_option;
|
|
|
|
if (pkt_type == DCCP_PKT_DATA)
|
|
continue;
|
|
|
|
if (len == 2)
|
|
elapsed_time = ntohs(*(u16 *)value);
|
|
else
|
|
elapsed_time = ntohl(*(u32 *)value);
|
|
|
|
if (elapsed_time > opt_recv->dccpor_elapsed_time)
|
|
opt_recv->dccpor_elapsed_time = elapsed_time;
|
|
|
|
dccp_pr_debug("%sELAPSED_TIME=%d\n", debug_prefix,
|
|
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(KERN_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 <= 0xFFFF ? 2 : 4;
|
|
}
|
|
|
|
void dccp_insert_option_elapsed_time(struct sock *sk,
|
|
struct sk_buff *skb,
|
|
u32 elapsed_time)
|
|
{
|
|
#ifdef CONFIG_IP_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_len == 0)
|
|
return;
|
|
|
|
if (DCCP_SKB_CB(skb)->dccpd_opt_len + len > DCCP_MAX_OPT_LEN) {
|
|
LIMIT_NETDEBUG(KERN_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;
|
|
|
|
if (elapsed_time_len == 2) {
|
|
const u16 var16 = htons((u16)elapsed_time);
|
|
memcpy(to, &var16, 2);
|
|
} else {
|
|
const u32 var32 = htonl(elapsed_time);
|
|
memcpy(to, &var32, 4);
|
|
}
|
|
|
|
dccp_pr_debug("%sELAPSED_TIME=%u, len=%d, seqno=%llu\n",
|
|
debug_prefix, elapsed_time,
|
|
len,
|
|
(unsigned long long) DCCP_SKB_CB(skb)->dccpd_seq);
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(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 CONFIG_IP_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 = timeval_now_delta(&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(KERN_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,
|
|
(unsigned long long) ap->dccpap_ack_seqno,
|
|
(unsigned long long) ap->dccpap_ack_ackno);
|
|
}
|
|
|
|
void dccp_insert_option_timestamp(struct sock *sk, struct sk_buff *skb)
|
|
{
|
|
struct timeval tv;
|
|
u32 now;
|
|
|
|
do_gettimeofday(&tv);
|
|
now = (tv.tv_sec * USEC_PER_SEC + tv.tv_usec) / 10;
|
|
/* yes this will overflow but that is the point as we want a
|
|
* 10 usec 32 bit timer which mean it wraps every 11.9 hours */
|
|
|
|
now = htonl(now);
|
|
dccp_insert_option(sk, skb, DCCPO_TIMESTAMP, &now, sizeof(now));
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(dccp_insert_option_timestamp);
|
|
|
|
static void dccp_insert_option_timestamp_echo(struct sock *sk,
|
|
struct sk_buff *skb)
|
|
{
|
|
struct dccp_sock *dp = dccp_sk(sk);
|
|
#ifdef CONFIG_IP_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 =
|
|
timeval_now_delta(&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(KERN_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;
|
|
|
|
if (elapsed_time_len == 2) {
|
|
const u16 var16 = htons((u16)elapsed_time);
|
|
memcpy(to, &var16, 2);
|
|
} else if (elapsed_time_len == 4) {
|
|
const u32 var32 = htonl(elapsed_time);
|
|
memcpy(to, &var32, 4);
|
|
}
|
|
|
|
dccp_pr_debug("%sTIMESTAMP_ECHO=%u, len=%d, seqno=%llu\n",
|
|
debug_prefix, dp->dccps_timestamp_echo,
|
|
len,
|
|
(unsigned long long) DCCP_SKB_CB(skb)->dccpd_seq);
|
|
|
|
dp->dccps_timestamp_echo = 0;
|
|
dp->dccps_timestamp_time.tv_sec = 0;
|
|
dp->dccps_timestamp_time.tv_usec = 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);
|
|
if (dp->dccps_timestamp_echo != 0)
|
|
dccp_insert_option_timestamp_echo(sk, skb);
|
|
}
|
|
|
|
if (dp->dccps_hc_rx_insert_options) {
|
|
ccid_hc_rx_insert_options(dp->dccps_hc_rx_ccid, sk, skb);
|
|
dp->dccps_hc_rx_insert_options = 0;
|
|
}
|
|
if (dp->dccps_hc_tx_insert_options) {
|
|
ccid_hc_tx_insert_options(dp->dccps_hc_tx_ccid, sk, skb);
|
|
dp->dccps_hc_tx_insert_options = 0;
|
|
}
|
|
|
|
/* 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(const unsigned int len,
|
|
const unsigned int __nocast priority)
|
|
{
|
|
struct dccp_ackpkts *ap = kmalloc(sizeof(*ap) + len, priority);
|
|
|
|
if (ap != NULL) {
|
|
#ifdef CONFIG_IP_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.tv_sec = 0;
|
|
ap->dccpap_time.tv_usec = 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 CONFIG_IP_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",
|
|
(unsigned long long) 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;
|
|
do_gettimeofday(&ap->dccpap_time);
|
|
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", (unsigned long long) ackno);
|
|
return -EILSEQ;
|
|
}
|
|
|
|
#ifdef CONFIG_IP_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,
|
|
(unsigned long long) 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 CONFIG_IP_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,
|
|
(unsigned long long) ap->dccpap_ack_seqno,
|
|
(unsigned long long) 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 CONFIG_IP_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,
|
|
(unsigned long long)
|
|
ap->dccpap_ack_seqno,
|
|
(unsigned long long)
|
|
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;
|
|
}
|
|
}
|