mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-21 16:29:59 +00:00
dccp ccid-2: Separate option parsing from CCID processing
This patch replaces an almost identical replication of code: large parts of dccp_parse_options() re-appeared as ccid2_ackvector() in ccid2.c. Apart from the duplication, this caused two more problems: 1. CCIDs should not need to be concerned with parsing header options; 2. one can not assume that Ack Vectors appear as a contiguous area within an skb, it is legal to insert other options and/or padding in between. The current code would throw an error and stop reading in such a case. The patch provides a new data structure and associated list housekeeping. Only small changes were necessary to integrate with CCID-2: data structure initialisation, adapt list traversal routine, and add call to the provided cleanup routine. The latter also lead to fixing the following BUG: CCID-2 so far ignored Ack Vectors on all packets other than Ack/DataAck, which is incorrect, since Ack Vectors can be present on any packet that has an Ack field. Details: -------- * received Ack Vectors are parsed by dccp_parse_options() alone, which passes the result on to the CCID-specific routine ccid_hc_tx_parse_options(); * CCIDs interested in using/decoding Ack Vector information will add code to fetch parsed Ack Vectors via this interface; * a data structure, `struct dccp_ackvec_parsed' is provided as interface; * this structure arranges Ack Vectors of the same skb into a FIFO order; * a doubly-linked list is used to keep the required FIFO code small. Signed-off-by: Gerrit Renker <gerrit@erg.abdn.ac.uk>
This commit is contained in:
parent
5a577b488f
commit
c8bf462bc5
5 changed files with 101 additions and 100 deletions
|
@ -343,6 +343,34 @@ void dccp_ackvec_clear_state(struct dccp_ackvec *av, const u64 ackno)
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Routines to keep track of Ack Vectors received in an skb
|
||||
*/
|
||||
int dccp_ackvec_parsed_add(struct list_head *head, u8 *vec, u8 len, u8 nonce)
|
||||
{
|
||||
struct dccp_ackvec_parsed *new = kmalloc(sizeof(*new), GFP_ATOMIC);
|
||||
|
||||
if (new == NULL)
|
||||
return -ENOBUFS;
|
||||
new->vec = vec;
|
||||
new->len = len;
|
||||
new->nonce = nonce;
|
||||
|
||||
list_add_tail(&new->node, head);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dccp_ackvec_parsed_add);
|
||||
|
||||
void dccp_ackvec_parsed_cleanup(struct list_head *parsed_chunks)
|
||||
{
|
||||
struct dccp_ackvec_parsed *cur, *next;
|
||||
|
||||
list_for_each_entry_safe(cur, next, parsed_chunks, node)
|
||||
kfree(cur);
|
||||
INIT_LIST_HEAD(parsed_chunks);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dccp_ackvec_parsed_cleanup);
|
||||
|
||||
int __init dccp_ackvec_init(void)
|
||||
{
|
||||
dccp_ackvec_slab = kmem_cache_create("dccp_ackvec",
|
||||
|
|
|
@ -114,4 +114,23 @@ static inline bool dccp_ackvec_is_empty(const struct dccp_ackvec *av)
|
|||
{
|
||||
return av->av_overflow == 0 && av->av_buf_head == av->av_buf_tail;
|
||||
}
|
||||
|
||||
/**
|
||||
* struct dccp_ackvec_parsed - Record offsets of Ack Vectors in skb
|
||||
* @vec: start of vector (offset into skb)
|
||||
* @len: length of @vec
|
||||
* @nonce: whether @vec had an ECN nonce of 0 or 1
|
||||
* @node: FIFO - arranged in descending order of ack_ackno
|
||||
* This structure is used by CCIDs to access Ack Vectors in a received skb.
|
||||
*/
|
||||
struct dccp_ackvec_parsed {
|
||||
u8 *vec,
|
||||
len,
|
||||
nonce:1;
|
||||
struct list_head node;
|
||||
};
|
||||
|
||||
extern int dccp_ackvec_parsed_add(struct list_head *head,
|
||||
u8 *vec, u8 len, u8 nonce);
|
||||
extern void dccp_ackvec_parsed_cleanup(struct list_head *parsed_chunks);
|
||||
#endif /* _ACKVEC_H */
|
||||
|
|
|
@ -317,68 +317,6 @@ static void ccid2_hc_tx_packet_sent(struct sock *sk, unsigned int len)
|
|||
#endif
|
||||
}
|
||||
|
||||
/* XXX Lame code duplication!
|
||||
* returns -1 if none was found.
|
||||
* else returns the next offset to use in the function call.
|
||||
*/
|
||||
static int ccid2_ackvector(struct sock *sk, struct sk_buff *skb, int offset,
|
||||
unsigned char **vec, unsigned char *veclen)
|
||||
{
|
||||
const struct dccp_hdr *dh = dccp_hdr(skb);
|
||||
unsigned char *options = (unsigned char *)dh + dccp_hdr_len(skb);
|
||||
unsigned char *opt_ptr;
|
||||
const unsigned char *opt_end = (unsigned char *)dh +
|
||||
(dh->dccph_doff * 4);
|
||||
unsigned char opt, len;
|
||||
unsigned char *value;
|
||||
|
||||
BUG_ON(offset < 0);
|
||||
options += offset;
|
||||
opt_ptr = options;
|
||||
if (opt_ptr >= opt_end)
|
||||
return -1;
|
||||
|
||||
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_ACK_VECTOR_0:
|
||||
case DCCPO_ACK_VECTOR_1:
|
||||
*vec = value;
|
||||
*veclen = len;
|
||||
return offset + (opt_ptr - options);
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
|
||||
out_invalid_option:
|
||||
DCCP_BUG("Invalid option - this should not happen (previous parsing)!");
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void ccid2_hc_tx_kill_rto_timer(struct sock *sk)
|
||||
{
|
||||
struct ccid2_hc_tx_sock *hctx = ccid2_hc_tx_sk(sk);
|
||||
|
@ -499,15 +437,27 @@ static void ccid2_congestion_event(struct sock *sk, struct ccid2_seq *seqp)
|
|||
ccid2_change_l_ack_ratio(sk, hctx->cwnd);
|
||||
}
|
||||
|
||||
static int ccid2_hc_tx_parse_options(struct sock *sk, u8 packet_type,
|
||||
u8 option, u8 *optval, u8 optlen)
|
||||
{
|
||||
struct ccid2_hc_tx_sock *hctx = ccid2_hc_tx_sk(sk);
|
||||
|
||||
switch (option) {
|
||||
case DCCPO_ACK_VECTOR_0:
|
||||
case DCCPO_ACK_VECTOR_1:
|
||||
return dccp_ackvec_parsed_add(&hctx->av_chunks, optval, optlen,
|
||||
option - DCCPO_ACK_VECTOR_0);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ccid2_hc_tx_packet_recv(struct sock *sk, struct sk_buff *skb)
|
||||
{
|
||||
struct dccp_sock *dp = dccp_sk(sk);
|
||||
struct ccid2_hc_tx_sock *hctx = ccid2_hc_tx_sk(sk);
|
||||
struct dccp_ackvec_parsed *avp;
|
||||
u64 ackno, seqno;
|
||||
struct ccid2_seq *seqp;
|
||||
unsigned char *vector;
|
||||
unsigned char veclen;
|
||||
int offset = 0;
|
||||
int done = 0;
|
||||
unsigned int maxincr = 0;
|
||||
|
||||
|
@ -542,17 +492,12 @@ static void ccid2_hc_tx_packet_recv(struct sock *sk, struct sk_buff *skb)
|
|||
}
|
||||
|
||||
/* check forward path congestion */
|
||||
/* still didn't send out new data packets */
|
||||
if (hctx->seqh == hctx->seqt)
|
||||
if (dccp_packet_without_ack(skb))
|
||||
return;
|
||||
|
||||
switch (DCCP_SKB_CB(skb)->dccpd_type) {
|
||||
case DCCP_PKT_ACK:
|
||||
case DCCP_PKT_DATAACK:
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
/* still didn't send out new data packets */
|
||||
if (hctx->seqh == hctx->seqt)
|
||||
goto done;
|
||||
|
||||
ackno = DCCP_SKB_CB(skb)->dccpd_ack_seq;
|
||||
if (after48(ackno, hctx->high_ack))
|
||||
|
@ -576,15 +521,16 @@ static void ccid2_hc_tx_packet_recv(struct sock *sk, struct sk_buff *skb)
|
|||
maxincr = DIV_ROUND_UP(dp->dccps_l_ack_ratio, 2);
|
||||
|
||||
/* go through all ack vectors */
|
||||
while ((offset = ccid2_ackvector(sk, skb, offset,
|
||||
&vector, &veclen)) != -1) {
|
||||
list_for_each_entry(avp, &hctx->av_chunks, node) {
|
||||
/* go through this ack vector */
|
||||
while (veclen--) {
|
||||
u64 ackno_end_rl = SUB48(ackno, dccp_ackvec_runlen(vector));
|
||||
for (; avp->len--; avp->vec++) {
|
||||
u64 ackno_end_rl = SUB48(ackno,
|
||||
dccp_ackvec_runlen(avp->vec));
|
||||
|
||||
ccid2_pr_debug("ackvec start:%llu end:%llu\n",
|
||||
ccid2_pr_debug("ackvec %llu |%u,%u|\n",
|
||||
(unsigned long long)ackno,
|
||||
(unsigned long long)ackno_end_rl);
|
||||
dccp_ackvec_state(avp->vec) >> 6,
|
||||
dccp_ackvec_runlen(avp->vec));
|
||||
/* if the seqno we are analyzing is larger than the
|
||||
* current ackno, then move towards the tail of our
|
||||
* seqnos.
|
||||
|
@ -603,7 +549,7 @@ static void ccid2_hc_tx_packet_recv(struct sock *sk, struct sk_buff *skb)
|
|||
* run length
|
||||
*/
|
||||
while (between48(seqp->ccid2s_seq,ackno_end_rl,ackno)) {
|
||||
const u8 state = dccp_ackvec_state(vector);
|
||||
const u8 state = dccp_ackvec_state(avp->vec);
|
||||
|
||||
/* new packet received or marked */
|
||||
if (state != DCCPAV_NOT_RECEIVED &&
|
||||
|
@ -630,7 +576,6 @@ static void ccid2_hc_tx_packet_recv(struct sock *sk, struct sk_buff *skb)
|
|||
break;
|
||||
|
||||
ackno = SUB48(ackno_end_rl, 1);
|
||||
vector++;
|
||||
}
|
||||
if (done)
|
||||
break;
|
||||
|
@ -694,6 +639,8 @@ static void ccid2_hc_tx_packet_recv(struct sock *sk, struct sk_buff *skb)
|
|||
}
|
||||
|
||||
ccid2_hc_tx_check_sanity(hctx);
|
||||
done:
|
||||
dccp_ackvec_parsed_cleanup(&hctx->av_chunks);
|
||||
}
|
||||
|
||||
static int ccid2_hc_tx_init(struct ccid *ccid, struct sock *sk)
|
||||
|
@ -727,6 +674,7 @@ static int ccid2_hc_tx_init(struct ccid *ccid, struct sock *sk)
|
|||
hctx->rpdupack = -1;
|
||||
hctx->last_cong = jiffies;
|
||||
setup_timer(&hctx->rtotimer, ccid2_hc_tx_rto_expire, (unsigned long)sk);
|
||||
INIT_LIST_HEAD(&hctx->av_chunks);
|
||||
|
||||
ccid2_hc_tx_check_sanity(hctx);
|
||||
return 0;
|
||||
|
@ -762,17 +710,18 @@ static void ccid2_hc_rx_packet_recv(struct sock *sk, struct sk_buff *skb)
|
|||
}
|
||||
|
||||
static struct ccid_operations ccid2 = {
|
||||
.ccid_id = DCCPC_CCID2,
|
||||
.ccid_name = "TCP-like",
|
||||
.ccid_owner = THIS_MODULE,
|
||||
.ccid_hc_tx_obj_size = sizeof(struct ccid2_hc_tx_sock),
|
||||
.ccid_hc_tx_init = ccid2_hc_tx_init,
|
||||
.ccid_hc_tx_exit = ccid2_hc_tx_exit,
|
||||
.ccid_hc_tx_send_packet = ccid2_hc_tx_send_packet,
|
||||
.ccid_hc_tx_packet_sent = ccid2_hc_tx_packet_sent,
|
||||
.ccid_hc_tx_packet_recv = ccid2_hc_tx_packet_recv,
|
||||
.ccid_hc_rx_obj_size = sizeof(struct ccid2_hc_rx_sock),
|
||||
.ccid_hc_rx_packet_recv = ccid2_hc_rx_packet_recv,
|
||||
.ccid_id = DCCPC_CCID2,
|
||||
.ccid_name = "TCP-like",
|
||||
.ccid_owner = THIS_MODULE,
|
||||
.ccid_hc_tx_obj_size = sizeof(struct ccid2_hc_tx_sock),
|
||||
.ccid_hc_tx_init = ccid2_hc_tx_init,
|
||||
.ccid_hc_tx_exit = ccid2_hc_tx_exit,
|
||||
.ccid_hc_tx_send_packet = ccid2_hc_tx_send_packet,
|
||||
.ccid_hc_tx_packet_sent = ccid2_hc_tx_packet_sent,
|
||||
.ccid_hc_tx_parse_options = ccid2_hc_tx_parse_options,
|
||||
.ccid_hc_tx_packet_recv = ccid2_hc_tx_packet_recv,
|
||||
.ccid_hc_rx_obj_size = sizeof(struct ccid2_hc_rx_sock),
|
||||
.ccid_hc_rx_packet_recv = ccid2_hc_rx_packet_recv,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_IP_DCCP_CCID2_DEBUG
|
||||
|
|
|
@ -47,6 +47,7 @@ struct ccid2_seq {
|
|||
* @lastrtt: time RTT was last measured
|
||||
* @rpseq: last consecutive seqno
|
||||
* @rpdupack: dupacks since rpseq
|
||||
* @av_chunks: list of Ack Vectors received on current skb
|
||||
*/
|
||||
struct ccid2_hc_tx_sock {
|
||||
u32 cwnd;
|
||||
|
@ -66,6 +67,7 @@ struct ccid2_hc_tx_sock {
|
|||
int rpdupack;
|
||||
unsigned long last_cong;
|
||||
u64 high_ack;
|
||||
struct list_head av_chunks;
|
||||
};
|
||||
|
||||
struct ccid2_hc_rx_sock {
|
||||
|
|
|
@ -128,13 +128,6 @@ int dccp_parse_options(struct sock *sk, struct dccp_request_sock *dreq,
|
|||
if (rc)
|
||||
goto out_featneg_failed;
|
||||
break;
|
||||
case DCCPO_ACK_VECTOR_0:
|
||||
case DCCPO_ACK_VECTOR_1:
|
||||
if (dccp_packet_without_ack(skb)) /* RFC 4340, 11.4 */
|
||||
break;
|
||||
dccp_pr_debug("%s Ack Vector (len=%u)\n", dccp_role(sk),
|
||||
len);
|
||||
break;
|
||||
case DCCPO_TIMESTAMP:
|
||||
if (len != 4)
|
||||
goto out_invalid_option;
|
||||
|
@ -224,6 +217,16 @@ int dccp_parse_options(struct sock *sk, struct dccp_request_sock *dreq,
|
|||
pkt_type, opt, value, len))
|
||||
goto out_invalid_option;
|
||||
break;
|
||||
case DCCPO_ACK_VECTOR_0:
|
||||
case DCCPO_ACK_VECTOR_1:
|
||||
if (dccp_packet_without_ack(skb)) /* RFC 4340, 11.4 */
|
||||
break;
|
||||
/*
|
||||
* Ack vectors are processed by the TX CCID if it is
|
||||
* interested. The RX CCID need not parse Ack Vectors,
|
||||
* since it is only interested in clearing old state.
|
||||
* Fall through.
|
||||
*/
|
||||
case DCCPO_MIN_TX_CCID_SPECIFIC ... DCCPO_MAX_TX_CCID_SPECIFIC:
|
||||
if (ccid_hc_tx_parse_options(dp->dccps_hc_tx_ccid, sk,
|
||||
pkt_type, opt, value, len))
|
||||
|
|
Loading…
Reference in a new issue