6fdd34d43b
This removes the use of the sysctl and the minisock variable for the Send Ack Vector feature, as it now is handled fully dynamically via feature negotiation (i.e. when CCID-2 is enabled, Ack Vectors are automatically enabled as per RFC 4341, 4.). Using a sysctl in parallel to this implementation would open the door to crashes, since much of the code relies on tests of the boolean minisock / sysctl variable. Thus, this patch replaces all tests of type if (dccp_msk(sk)->dccpms_send_ack_vector) /* ... */ with if (dp->dccps_hc_rx_ackvec != NULL) /* ... */ The dccps_hc_rx_ackvec is allocated by the dccp_hdlr_ackvec() when feature negotiation concluded that Ack Vectors are to be used on the half-connection. Otherwise, it is NULL (due to dccp_init_sock/dccp_create_openreq_child), so that the test is a valid one. The activation handler for Ack Vectors is called as soon as the feature negotiation has concluded at the * server when the Ack marking the transition RESPOND => OPEN arrives; * client after it has sent its ACK, marking the transition REQUEST => PARTOPEN. Adding the sequence number of the Response packet to the Ack Vector has been removed, since (a) connection establishment implies that the Response has been received; (b) the CCIDs only look at packets received in the (PART)OPEN state, i.e. this entry will always be ignored; (c) it can not be used for anything useful - to detect loss for instance, only packets received after the loss can serve as pseudo-dupacks. There was a FIXME to change the error code when dccp_ackvec_add() fails. I removed this after finding out that: * the check whether ackno < ISN is already made earlier, * this Response is likely the 1st packet with an Ackno that the client gets, * so when dccp_ackvec_add() fails, the reason is likely not a packet error. Signed-off-by: Gerrit Renker <gerrit@erg.abdn.ac.uk> Acked-by: Ian McDonald <ian.mcdonald@jandi.co.nz> Signed-off-by: David S. Miller <davem@davemloft.net>
300 lines
8.5 KiB
C
300 lines
8.5 KiB
C
/*
|
|
* 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/dccp.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/skbuff.h>
|
|
#include <linux/timer.h>
|
|
|
|
#include <net/sock.h>
|
|
#include <net/xfrm.h>
|
|
#include <net/inet_timewait_sock.h>
|
|
|
|
#include "ackvec.h"
|
|
#include "ccid.h"
|
|
#include "dccp.h"
|
|
#include "feat.h"
|
|
|
|
struct inet_timewait_death_row dccp_death_row = {
|
|
.sysctl_max_tw_buckets = NR_FILE * 2,
|
|
.period = DCCP_TIMEWAIT_LEN / INET_TWDR_TWKILL_SLOTS,
|
|
.death_lock = __SPIN_LOCK_UNLOCKED(dccp_death_row.death_lock),
|
|
.hashinfo = &dccp_hashinfo,
|
|
.tw_timer = TIMER_INITIALIZER(inet_twdr_hangman, 0,
|
|
(unsigned long)&dccp_death_row),
|
|
.twkill_work = __WORK_INITIALIZER(dccp_death_row.twkill_work,
|
|
inet_twdr_twkill_work),
|
|
/* Short-time timewait calendar */
|
|
|
|
.twcal_hand = -1,
|
|
.twcal_timer = TIMER_INITIALIZER(inet_twdr_twcal_tick, 0,
|
|
(unsigned long)&dccp_death_row),
|
|
};
|
|
|
|
EXPORT_SYMBOL_GPL(dccp_death_row);
|
|
|
|
void dccp_minisock_init(struct dccp_minisock *dmsk)
|
|
{
|
|
dmsk->dccpms_sequence_window = sysctl_dccp_feat_sequence_window;
|
|
}
|
|
|
|
void dccp_time_wait(struct sock *sk, int state, int timeo)
|
|
{
|
|
struct inet_timewait_sock *tw = NULL;
|
|
|
|
if (dccp_death_row.tw_count < dccp_death_row.sysctl_max_tw_buckets)
|
|
tw = inet_twsk_alloc(sk, state);
|
|
|
|
if (tw != NULL) {
|
|
const struct inet_connection_sock *icsk = inet_csk(sk);
|
|
const int rto = (icsk->icsk_rto << 2) - (icsk->icsk_rto >> 1);
|
|
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
|
|
if (tw->tw_family == PF_INET6) {
|
|
const struct ipv6_pinfo *np = inet6_sk(sk);
|
|
struct inet6_timewait_sock *tw6;
|
|
|
|
tw->tw_ipv6_offset = inet6_tw_offset(sk->sk_prot);
|
|
tw6 = inet6_twsk((struct sock *)tw);
|
|
ipv6_addr_copy(&tw6->tw_v6_daddr, &np->daddr);
|
|
ipv6_addr_copy(&tw6->tw_v6_rcv_saddr, &np->rcv_saddr);
|
|
tw->tw_ipv6only = np->ipv6only;
|
|
}
|
|
#endif
|
|
/* Linkage updates. */
|
|
__inet_twsk_hashdance(tw, sk, &dccp_hashinfo);
|
|
|
|
/* Get the TIME_WAIT timeout firing. */
|
|
if (timeo < rto)
|
|
timeo = rto;
|
|
|
|
tw->tw_timeout = DCCP_TIMEWAIT_LEN;
|
|
if (state == DCCP_TIME_WAIT)
|
|
timeo = DCCP_TIMEWAIT_LEN;
|
|
|
|
inet_twsk_schedule(tw, &dccp_death_row, timeo,
|
|
DCCP_TIMEWAIT_LEN);
|
|
inet_twsk_put(tw);
|
|
} else {
|
|
/* Sorry, if we're out of memory, just CLOSE this
|
|
* socket up. We've got bigger problems than
|
|
* non-graceful socket closings.
|
|
*/
|
|
DCCP_WARN("time wait bucket table overflow\n");
|
|
}
|
|
|
|
dccp_done(sk);
|
|
}
|
|
|
|
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) {
|
|
struct dccp_request_sock *dreq = dccp_rsk(req);
|
|
struct inet_connection_sock *newicsk = inet_csk(newsk);
|
|
struct dccp_sock *newdp = dccp_sk(newsk);
|
|
struct dccp_minisock *newdmsk = dccp_msk(newsk);
|
|
|
|
newdp->dccps_role = DCCP_ROLE_SERVER;
|
|
newdp->dccps_hc_rx_ackvec = NULL;
|
|
newdp->dccps_service_list = NULL;
|
|
newdp->dccps_service = dreq->dreq_service;
|
|
newdp->dccps_timestamp_echo = dreq->dreq_timestamp_echo;
|
|
newdp->dccps_timestamp_time = dreq->dreq_timestamp_time;
|
|
newicsk->icsk_rto = DCCP_TIMEOUT_INIT;
|
|
|
|
INIT_LIST_HEAD(&newdp->dccps_featneg);
|
|
/*
|
|
* Step 3: Process LISTEN state
|
|
*
|
|
* Choose S.ISS (initial seqno) or set from Init Cookies
|
|
* Initialize S.GAR := S.ISS
|
|
* Set S.ISR, S.GSR, S.SWL, S.SWH from packet or Init Cookies
|
|
*/
|
|
|
|
/* See dccp_v4_conn_request */
|
|
newdmsk->dccpms_sequence_window = req->rcv_wnd;
|
|
|
|
newdp->dccps_gar = newdp->dccps_iss = dreq->dreq_iss;
|
|
dccp_update_gss(newsk, dreq->dreq_iss);
|
|
|
|
newdp->dccps_isr = dreq->dreq_isr;
|
|
dccp_update_gsr(newsk, dreq->dreq_isr);
|
|
|
|
/*
|
|
* SWL and AWL are initially adjusted so that they are not less than
|
|
* the initial Sequence Numbers received and sent, respectively:
|
|
* SWL := max(GSR + 1 - floor(W/4), ISR),
|
|
* AWL := max(GSS - W' + 1, ISS).
|
|
* These adjustments MUST be applied only at the beginning of the
|
|
* connection.
|
|
*/
|
|
dccp_set_seqno(&newdp->dccps_swl,
|
|
max48(newdp->dccps_swl, newdp->dccps_isr));
|
|
dccp_set_seqno(&newdp->dccps_awl,
|
|
max48(newdp->dccps_awl, newdp->dccps_iss));
|
|
|
|
/*
|
|
* Activate features after initialising the sequence numbers,
|
|
* since CCID initialisation may depend on GSS, ISR, ISS etc.
|
|
*/
|
|
if (dccp_feat_activate_values(newsk, &dreq->dreq_featneg)) {
|
|
/* It is still raw copy of parent, so invalidate
|
|
* destructor and make plain sk_free() */
|
|
newsk->sk_destruct = NULL;
|
|
sk_free(newsk);
|
|
return NULL;
|
|
}
|
|
dccp_init_xmit_timers(newsk);
|
|
|
|
DCCP_INC_STATS_BH(DCCP_MIB_PASSIVEOPENS);
|
|
}
|
|
return newsk;
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(dccp_create_openreq_child);
|
|
|
|
/*
|
|
* 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;
|
|
struct dccp_request_sock *dreq = dccp_rsk(req);
|
|
|
|
/* Check for retransmitted REQUEST */
|
|
if (dccp_hdr(skb)->dccph_type == DCCP_PKT_REQUEST) {
|
|
|
|
if (after48(DCCP_SKB_CB(skb)->dccpd_seq, dreq->dreq_isr)) {
|
|
dccp_pr_debug("Retransmitted REQUEST\n");
|
|
dreq->dreq_isr = DCCP_SKB_CB(skb)->dccpd_seq;
|
|
/*
|
|
* Send another RESPONSE packet
|
|
* To protect against Request floods, increment retrans
|
|
* counter (backoff, monitored by dccp_response_timer).
|
|
*/
|
|
req->retrans++;
|
|
req->rsk_ops->rtx_syn_ack(sk, req);
|
|
}
|
|
/* 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 != dreq->dreq_iss) {
|
|
dccp_pr_debug("Invalid ACK number: ack_seq=%llu, "
|
|
"dreq_iss=%llu\n",
|
|
(unsigned long long)
|
|
DCCP_SKB_CB(skb)->dccpd_ack_seq,
|
|
(unsigned long long) dreq->dreq_iss);
|
|
goto drop;
|
|
}
|
|
|
|
if (dccp_parse_options(sk, dreq, skb))
|
|
goto drop;
|
|
|
|
child = inet_csk(sk)->icsk_af_ops->syn_recv_sock(sk, skb, req, NULL);
|
|
if (child == NULL)
|
|
goto listen_overflow;
|
|
|
|
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(sk, skb);
|
|
|
|
inet_csk_reqsk_queue_drop(sk, req, prev);
|
|
goto out;
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(dccp_check_req);
|
|
|
|
/*
|
|
* 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;
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(dccp_child_process);
|
|
|
|
void dccp_reqsk_send_ack(struct sock *sk, struct sk_buff *skb,
|
|
struct request_sock *rsk)
|
|
{
|
|
DCCP_BUG("DCCP-ACK packets are never sent in LISTEN/RESPOND state");
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(dccp_reqsk_send_ack);
|
|
|
|
int dccp_reqsk_init(struct request_sock *req,
|
|
struct dccp_sock const *dp, struct sk_buff const *skb)
|
|
{
|
|
struct dccp_request_sock *dreq = dccp_rsk(req);
|
|
|
|
inet_rsk(req)->rmt_port = dccp_hdr(skb)->dccph_sport;
|
|
inet_rsk(req)->loc_port = dccp_hdr(skb)->dccph_dport;
|
|
inet_rsk(req)->acked = 0;
|
|
req->rcv_wnd = sysctl_dccp_feat_sequence_window;
|
|
dreq->dreq_timestamp_echo = 0;
|
|
|
|
/* inherit feature negotiation options from listening socket */
|
|
return dccp_feat_clone_list(&dp->dccps_featneg, &dreq->dreq_featneg);
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(dccp_reqsk_init);
|