*
* Feature negotiation for the DCCP protocol (RFC 4340, section 6)
*
- * Copyright (c) 2008 The University of Aberdeen, Scotland, UK
* Copyright (c) 2008 Gerrit Renker <gerrit@erg.abdn.ac.uk>
* Rewrote from scratch, some bits from earlier code by
* Copyright (c) 2005 Andrea Bittau <a.bittau@cs.ucl.ac.uk>
* -----------
* o Feature negotiation is coordinated with connection setup (as in TCP), wild
* changes of parameters of an established connection are not supported.
- * o Changing NN values (Ack Ratio only) is supported in state OPEN/PARTOPEN.
* o All currently known SP features have 1-byte quantities. If in the future
* extensions of RFCs 4340..42 define features with item lengths larger than
* one byte, a feature-specific extension of the code will be required.
* 2 of the License, or (at your option) any later version.
*/
#include <linux/module.h>
+#include <linux/slab.h>
#include "ccid.h"
#include "feat.h"
static int dccp_hdlr_ccid(struct sock *sk, u64 ccid, bool rx)
{
struct dccp_sock *dp = dccp_sk(sk);
- struct ccid *new_ccid = ccid_new(ccid, sk, rx, gfp_any());
+ struct ccid *new_ccid = ccid_new(ccid, sk, rx);
if (new_ccid == NULL)
return -ENOMEM;
static int dccp_hdlr_ack_ratio(struct sock *sk, u64 ratio, bool rx)
{
-#ifndef __CCID2_COPES_GRACEFULLY_WITH_DYNAMIC_ACK_RATIO_UPDATES__
- /*
- * FIXME: This is required until several problems in the CCID-2 code are
- * resolved. The CCID-2 code currently does not cope well; using dynamic
- * Ack Ratios greater than 1 caused instabilities. These were manifest
- * in hangups and long RTO timeouts (1...3 seconds). Until this has been
- * stabilised, it is safer not to activate dynamic Ack Ratio changes.
- */
- dccp_pr_debug("Not changing %s Ack Ratio from 1 to %u\n",
- rx ? "RX" : "TX", (u16)ratio);
- ratio = 1;
-#endif
if (rx)
dccp_sk(sk)->dccps_r_ack_ratio = ratio;
else
static int dccp_feat_default_value(u8 feat_num)
{
int idx = dccp_feat_index(feat_num);
+ /*
+ * There are no default values for unknown features, so encountering a
+ * negative index here indicates a serious problem somewhere else.
+ */
+ DCCP_BUG_ON(idx < 0);
- return idx < 0 ? : dccp_feat_table[idx].default_value;
+ return idx < 0 ? 0 : dccp_feat_table[idx].default_value;
}
/*
*/
static const char *dccp_feat_fname(const u8 feat)
{
- static const char *feature_names[] = {
+ static const char *const feature_names[] = {
[DCCPF_RESERVED] = "Reserved",
[DCCPF_CCID] = "CCID",
[DCCPF_SHORT_SEQNOS] = "Allow Short Seqnos",
return feature_names[feat];
}
-static const char *dccp_feat_sname[] = { "DEFAULT", "INITIALISING", "CHANGING",
- "UNSTABLE", "STABLE" };
+static const char *const dccp_feat_sname[] = {
+ "DEFAULT", "INITIALISING", "CHANGING", "UNSTABLE", "STABLE",
+};
#ifdef CONFIG_IP_DCCP_DEBUG
static const char *dccp_feat_oname(const u8 opt)
return dccp_feat_table[idx].activation_hdlr(sk, val, rx);
}
-/**
- * dccp_feat_activate - Activate feature value on socket
- * @sk: fully connected DCCP socket (after handshake is complete)
- * @feat_num: feature to activate, one of %dccp_feature_numbers
- * @local: whether local (1) or remote (0) @feat_num is meant
- * @fval: the value (SP or NN) to activate, or NULL to use the default value
- * For general use this function is preferable over __dccp_feat_activate().
- */
-static int dccp_feat_activate(struct sock *sk, u8 feat_num, bool local,
- dccp_feat_val const *fval)
-{
- return __dccp_feat_activate(sk, dccp_feat_index(feat_num), local, fval);
-}
-
/* Test for "Req'd" feature (RFC 4340, 6.4) */
static inline int dccp_feat_must_be_understood(u8 feat_num)
{
{
struct dccp_feat_entry *entry;
- list_for_each_entry(entry, fn_list, node)
+ list_for_each_entry(entry, fn_list, node) {
if (entry->feat_num == feat_num && entry->is_local == is_local)
return entry;
else if (entry->feat_num > feat_num)
break;
+ }
return NULL;
}
return __feat_register_nn(&dccp_sk(sk)->dccps_featneg, feat, 0, val);
}
-/**
- * dccp_feat_signal_nn_change - Update NN values for an established connection
- * @sk: DCCP socket of an established connection
- * @feat: NN feature number from %dccp_feature_numbers
- * @nn_val: the new value to use
- * This function is used to communicate NN updates out-of-band. The difference
- * to feature negotiation during connection setup is that values are activated
- * immediately after validation, i.e. we don't wait for the Confirm: either the
- * value is accepted by the peer (and then the waiting is futile), or it is not
- * (Reset or empty Confirm). We don't accept empty Confirms - transmitted values
- * are validated, and the peer "MUST accept any valid value" (RFC 4340, 6.3.2).
- */
-int dccp_feat_signal_nn_change(struct sock *sk, u8 feat, u64 nn_val)
-{
- struct list_head *fn = &dccp_sk(sk)->dccps_featneg;
- dccp_feat_val fval = { .nn = nn_val };
- struct dccp_feat_entry *entry;
-
- if (sk->sk_state != DCCP_OPEN && sk->sk_state != DCCP_PARTOPEN)
- return 0;
-
- if (dccp_feat_type(feat) != FEAT_NN ||
- !dccp_feat_is_valid_nn_val(feat, nn_val))
- return -EINVAL;
-
- entry = dccp_feat_list_lookup(fn, feat, 1);
- if (entry != NULL) {
- dccp_pr_debug("Ignoring %llu, entry %llu exists in state %s\n",
- (unsigned long long)nn_val,
- (unsigned long long)entry->val.nn,
- dccp_feat_sname[entry->state]);
- return 0;
- }
-
- if (dccp_feat_activate(sk, feat, 1, &fval))
- return -EADV;
-
- inet_csk_schedule_ack(sk);
- return dccp_feat_push_change(fn, feat, 1, 0, &fval);
-}
-EXPORT_SYMBOL_GPL(dccp_feat_signal_nn_change);
-
/*
* Tracking features whose value depend on the choice of CCID
*
}
/**
- * dccp_feat_handle_nn_established - Fast-path reception of NN options
- * @sk: socket of an established DCCP connection
- * @mandatory: whether @opt was preceded by a Mandatory option
- * @opt: %DCCPO_CHANGE_L | %DCCPO_CONFIRM_R (NN only)
- * @feat: NN number, one of %dccp_feature_numbers
- * @val: NN value
- * @len: length of @val in bytes
- * This function combines the functionality of change_recv/confirm_recv, with
- * the following differences (reset codes are the same):
- * - cleanup after receiving the Confirm;
- * - values are directly activated after successful parsing;
- * - deliberately restricted to NN features.
- * The restriction to NN features is essential since SP features can have non-
- * predictable outcomes (depending on the remote configuration), and are inter-
- * dependent (CCIDs for instance cause further dependencies).
- */
-static u8 dccp_feat_handle_nn_established(struct sock *sk, u8 mandatory, u8 opt,
- u8 feat, u8 *val, u8 len)
-{
- struct list_head *fn = &dccp_sk(sk)->dccps_featneg;
- const bool local = (opt == DCCPO_CONFIRM_R);
- struct dccp_feat_entry *entry;
- u8 type = dccp_feat_type(feat);
- dccp_feat_val fval;
-
- dccp_feat_print_opt(opt, feat, val, len, mandatory);
-
- /* Ignore non-mandatory unknown and non-NN features */
- if (type == FEAT_UNKNOWN) {
- if (local && !mandatory)
- return 0;
- goto fast_path_unknown;
- } else if (type != FEAT_NN) {
- return 0;
- }
-
- /*
- * We don't accept empty Confirms, since in fast-path feature
- * negotiation the values are enabled immediately after sending
- * the Change option.
- * Empty Changes on the other hand are invalid (RFC 4340, 6.1).
- */
- if (len == 0 || len > sizeof(fval.nn))
- goto fast_path_unknown;
-
- if (opt == DCCPO_CHANGE_L) {
- fval.nn = dccp_decode_value_var(val, len);
- if (!dccp_feat_is_valid_nn_val(feat, fval.nn))
- goto fast_path_unknown;
-
- if (dccp_feat_push_confirm(fn, feat, local, &fval) ||
- dccp_feat_activate(sk, feat, local, &fval))
- return DCCP_RESET_CODE_TOO_BUSY;
-
- /* set the `Ack Pending' flag to piggyback a Confirm */
- inet_csk_schedule_ack(sk);
-
- } else if (opt == DCCPO_CONFIRM_R) {
- entry = dccp_feat_list_lookup(fn, feat, local);
- if (entry == NULL || entry->state != FEAT_CHANGING)
- return 0;
-
- fval.nn = dccp_decode_value_var(val, len);
- if (fval.nn != entry->val.nn) {
- DCCP_WARN("Bogus Confirm for non-existing value\n");
- goto fast_path_failed;
- }
-
- /* It has been confirmed - so remove the entry */
- dccp_feat_list_pop(entry);
-
- } else {
- DCCP_WARN("Received illegal option %u\n", opt);
- goto fast_path_failed;
- }
- return 0;
-
-fast_path_unknown:
- if (!mandatory)
- return dccp_push_empty_confirm(fn, feat, local);
-
-fast_path_failed:
- return mandatory ? DCCP_RESET_CODE_MANDATORY_ERROR
- : DCCP_RESET_CODE_OPTION_ERROR;
-}
-
-/**
* dccp_feat_parse_options - Process Feature-Negotiation Options
* @sk: for general use and used by the client during connection setup
* @dreq: used by the server during connection setup
return dccp_feat_confirm_recv(fn, mandatory, opt, feat,
val, len, server);
}
- break;
- /*
- * Support for exchanging NN options on an established connection
- * This is currently restricted to Ack Ratio (RFC 4341, 6.1.2)
- */
- case DCCP_OPEN:
- case DCCP_PARTOPEN:
- return dccp_feat_handle_nn_established(sk, mandatory, opt, feat,
- val, len);
}
return 0; /* ignore FN options in all other states */
}
ccid_get_builtin_ccids(&rx.val, &rx.len))
return -ENOBUFS;
- /* Pre-load all CCID modules that are going to be advertised */
- rc = -EUNATCH;
- if (ccid_request_modules(tx.val, tx.len))
- goto free_ccid_lists;
-
if (!dccp_feat_prefer(sysctl_dccp_tx_ccid, tx.val, tx.len) ||
!dccp_feat_prefer(sysctl_dccp_rx_ccid, rx.val, rx.len))
goto free_ccid_lists;