[DCCP]: Process incoming Change requests
This adds/replaces code for processing incoming Change feature-negotiation
options.
The main difference is that mandatory FN options are now interpreted inside the
function
(there are too many individual cases to do this externally); the function
returns an
appropriate Reset code or 0, which is then used by dccp_parse_options.
Old code, which is no longer used or referenced, has been removed.
Signed-off-by: Gerrit Renker <[EMAIL PROTECTED]>
---
net/dccp/feat.c | 220 ++++++++++++++++++++++++++++++-----------------------
net/dccp/feat.h | 4
net/dccp/options.c | 15 ---
3 files changed, 132 insertions(+), 107 deletions(-)
--- a/net/dccp/feat.h
+++ b/net/dccp/feat.h
@@ -103,8 +103,8 @@ static inline void dccp_feat_debug(const
extern int dccp_feat_register_change(struct sock *sk, u8 feat,
u8 is_local, u8 *val, u8 len);
-extern int dccp_feat_change_recv(struct sock *sk, u8 type, u8 feature,
- u8 *val, u8 len);
+extern int dccp_feat_parse_options(struct sock *, struct dccp_request_sock *,
+ u8 mand, u8 opt, u8 feat, u8 *val, u8 len);
extern int dccp_feat_confirm_recv(struct sock *sk, u8 type, u8 feature,
u8 *val, u8 len);
extern void dccp_feat_clean(struct dccp_minisock *dmsk);
--- a/net/dccp/options.c
+++ b/net/dccp/options.c
@@ -126,21 +126,12 @@ int dccp_parse_options(struct sock *sk,
(unsigned long long)opt_recv->dccpor_ndp);
break;
case DCCPO_CHANGE_L:
- /* fall through */
case DCCPO_CHANGE_R:
if (pkt_type == DCCP_PKT_DATA)
break;
- if (len < 2)
- goto out_invalid_option;
- rc = dccp_feat_change_recv(sk, opt, *value, value + 1,
- len - 1);
- /*
- * When there is a change error, change_recv is
- * responsible for dealing with it. i.e. reply with an
- * empty confirm.
- * If the change was mandatory, then we need to die.
- */
- if (rc && mandatory)
+ rc = dccp_feat_parse_options(sk, dreq, mandatory, opt,
+ *value, value + 1, len - 1);
+ if (rc)
goto out_invalid_option;
break;
case DCCPO_CONFIRM_L:
--- a/net/dccp/feat.c
+++ b/net/dccp/feat.c
@@ -23,7 +23,6 @@
#include "ccid.h"
#include "feat.h"
-#define DCCP_FEAT_SP_NOAGREE (-123)
static const u8 on = 1, off = 0;
static const struct {
@@ -719,116 +718,151 @@ static int dccp_feat_reconcile(dccp_feat
return dccp_feat_prefer(rc, fv->sp.vec, fv->sp.len);
}
-static void dccp_feat_empty_confirm(struct dccp_minisock *dmsk,
- u8 type, u8 feature)
+/**
+ * dccp_feat_change_recv - Process incoming ChangeL/R options
+ * @fn: feature-negotiation list to update
+ * @is_mandatory: whether the Change was preceded by a Mandatory option
+ * @opt: %DCCPO_CHANGE_L or %DCCPO_CHANGE_R
+ * @feat: one of %dccp_feature_numbers
+ * @val: NN value or SP value/preference list
+ * @len: length of @val in bytes
+ * @server: whether this node is the server (1) or the client (0)
+ */
+static u8 dccp_feat_change_recv(struct list_head *fn, u8 is_mandatory, u8 opt,
+ u8 feat, u8 *val, u8 len, const bool server)
{
- /* XXX check if other confirms for that are queued and recycle slot */
- struct dccp_opt_pend *opt = kzalloc(sizeof(*opt), GFP_ATOMIC);
-
- if (opt == NULL) {
- /* XXX what do we do? Ignoring should be fine. It's a change
- * after all =P
- */
- return;
- }
-
- switch (type) {
- case DCCPO_CHANGE_L:
- opt->dccpop_type = DCCPO_CONFIRM_R;
- break;
- case DCCPO_CHANGE_R:
- opt->dccpop_type = DCCPO_CONFIRM_L;
- break;
- default:
- DCCP_WARN("invalid type %d\n", type);
- kfree(opt);
- return;
- }
- opt->dccpop_feat = feature;
- opt->dccpop_val = NULL;
- opt->dccpop_len = 0;
+ u8 defval, type = dccp_feat_type(feat);
+ const bool local = (opt == DCCPO_CHANGE_R);
+ struct dccp_feat_entry *entry;
+ dccp_feat_val fval;
- /* change feature */
- dccp_pr_debug("Empty %s(%d)\n", dccp_feat_typename(type), feature);
+ if (len == 0 || type == FEAT_UNKNOWN) /* 6.1 and 6.6.8 */
+ goto unknown_feature_or_value;
- list_add_tail(&opt->dccpop_node, &dmsk->dccpms_conf);
-}
+ /*
+ * Negotiation of NN features: Change R is invalid, so there is no
+ * simultaneous negotiation; hence we do not consult the list.
+ */
+ if (type == FEAT_NN) {
+ if (local)
+ goto not_valid_or_not_known;
+
+ if (len > sizeof(fval.nn))
+ goto unknown_feature_or_value;
+
+ /* 6.3.2: "The feature remote MUST accept any valid value..." */
+ fval.nn = dccp_decode_value_var(val, len);
+ if (!dccp_feat_is_valid_nn_val(feat, fval.nn))
+ goto unknown_feature_or_value;
-static void dccp_feat_flush_confirm(struct sock *sk)
-{
- struct dccp_minisock *dmsk = dccp_msk(sk);
- /* Check if there is anything to confirm in the first place */
- int yes = !list_empty(&dmsk->dccpms_conf);
+ return dccp_feat_push_confirm(fn, feat, local, &fval);
+ }
- if (!yes) {
- struct dccp_opt_pend *opt;
+ /*
+ * Unidirectional/simultaneous negotiation of SP features (6.3.1)
+ */
+ entry = dccp_feat_list_lookup(fn, feat, local);
+ if (entry == NULL) {
+ if (!dccp_feat_sp_list_ok(feat, val, len))
+ goto unknown_feature_or_value;
+ /*
+ * No particular preferences have been registered. We deal with
+ * this situation by assuming that all valid values are equally
+ * acceptable, and apply the following checks:
+ * - if the peer's list is a singleton, we accept a valid value;
+ * - if we are the server, we first try to see if the peer (the
+ * client) advertises the default value. If yes, we use it,
+ * otherwise we accept the preferred value;
+ * - else if we are the client, we use the first list element.
+ */
+ if (dccp_feat_clone_sp_val(&fval, val, 1))
+ return DCCP_RESET_CODE_TOO_BUSY;
- list_for_each_entry(opt, &dmsk->dccpms_pending, dccpop_node) {
- if (opt->dccpop_conf) {
- yes = 1;
- break;
- }
+ if (len > 1 && server) {
+ defval = dccp_feat_default_value(feat);
+ if (dccp_feat_preflist_match(&defval, 1, val, len) > -1)
+ fval.sp.vec[0] = defval;
}
- }
-
- if (!yes)
- return;
- /* OK there is something to confirm... */
- /* XXX check if packet is in flight? Send delayed ack?? */
- if (sk->sk_state == DCCP_OPEN)
- dccp_send_ack(sk);
-}
+ /* Treat unsupported CCIDs like invalid values */
+ if (feat == DCCPF_CCID && !ccid_support_check(fval.sp.vec, 1)) {
+ kfree(fval.sp.vec);
+ goto not_valid_or_not_known;
+ }
-int dccp_feat_change_recv(struct sock *sk, u8 type, u8 feature, u8 *val, u8
len)
-{
- int rc;
+ return dccp_feat_push_confirm(fn, feat, local, &fval);
- /* Ignore Change requests other than during connection setup */
- if (sk->sk_state != DCCP_LISTEN && sk->sk_state != DCCP_REQUESTING)
+ } else if (entry->state == FEAT_UNSTABLE) /* 6.6.2 */
return 0;
- dccp_feat_debug(type, feature, *val);
- /* figure out if it's SP or NN feature */
- switch (feature) {
- /* deal with SP features */
- case DCCPF_CCID:
- /* XXX Obsoleted by next patch
- rc = dccp_feat_sp(sk, type, feature, val, len); */
- break;
+ if (dccp_feat_reconcile(&entry->val, val, len, server, true))
+ entry->empty_confirm = 0;
+ else if (is_mandatory)
+ return DCCP_RESET_CODE_MANDATORY_ERROR;
+ else if (entry->state == FEAT_INITIALISING) {
+ /*
+ * Failed simultaneous negotiation (server only): try to `save'
+ * the connection by checking whether entry contains the default
+ * value for @feat. If yes, send an empty Confirm to signal that
+ * the received Change was not understood - which implies using
+ * the default value.
+ * If this also fails, we use Reset as the last resort.
+ */
+ BUG_TRAP(server == 1);
+ defval = dccp_feat_default_value(feat);
+ if (!dccp_feat_reconcile(&entry->val, &defval, 1, server, true))
+ return DCCP_RESET_CODE_OPTION_ERROR;
+ entry->empty_confirm = 1;
+ }
+ entry->needs_confirm = 1;
+ entry->needs_mandatory = 0;
+ entry->state = FEAT_STABLE;
+ return 0;
- /* deal with NN features */
- case DCCPF_ACK_RATIO:
- /* XXX Obsoleted by next patch
- rc = dccp_feat_nn(sk, type, feature, val, len); */
- break;
+unknown_feature_or_value:
+ if (!is_mandatory)
+ return dccp_push_empty_confirm(fn, feat, local);
+
+not_valid_or_not_known:
+ return is_mandatory? DCCP_RESET_CODE_MANDATORY_ERROR
+ : DCCP_RESET_CODE_OPTION_ERROR;
+}
- /* XXX implement other features */
- default:
- dccp_pr_debug("UNIMPLEMENTED: not handling %s(%d, ...)\n",
- dccp_feat_typename(type), feature);
- rc = -EFAULT;
- break;
- }
+/**
+ * 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
+ * @mandatory: whether @opt was preceded by a Mandatory option
+ * @opt: %DCCPO_CHANGE_L | %DCCPO_CHANGE_R | %DCCPO_CONFIRM_L |
%DCCPO_CONFIRM_R
+ * @feat: one of %dccp_feature_numbers
+ * @val: value contents of @opt
+ * @len: length of @val in bytes
+ * Returns 0 on success, a Reset code for ending the connection otherwise.
+ */
+int dccp_feat_parse_options(struct sock *sk, struct dccp_request_sock *dreq,
+ u8 mandatory, u8 opt, u8 feat, u8 *val, u8 len)
+{
+ struct dccp_sock *dp = dccp_sk(sk);
+ struct list_head *fn = dreq? &dreq->dreq_featneg : &dp->dccps_featneg;
+ bool server = false;
- /* check if there were problems changing features */
- if (rc) {
- /* If we don't agree on SP, we sent a confirm for old value.
- * However we propagate rc to caller in case option was
- * mandatory
- */
- if (rc != DCCP_FEAT_SP_NOAGREE)
- dccp_feat_empty_confirm(dccp_msk(sk), type, feature);
+ switch (sk->sk_state) {
+ /*
+ * Negotiation during connection setup
+ */
+ case DCCP_LISTEN:
+ server = true; /* fall through */
+ case DCCP_REQUESTING:
+ switch (opt) {
+ case DCCPO_CHANGE_L:
+ case DCCPO_CHANGE_R:
+ return dccp_feat_change_recv(fn, mandatory, opt, feat,
+ val, len, server);
+ }
}
-
- /* generate the confirm [if required] */
- dccp_feat_flush_confirm(sk);
-
- return rc;
+ return 0; /* ignore FN options in all other states */
}
-EXPORT_SYMBOL_GPL(dccp_feat_change_recv);
-
int dccp_feat_confirm_recv(struct sock *sk, u8 type, u8 feature,
u8 *val, u8 len)
{
-
To unsubscribe from this list: send the line "unsubscribe dccp" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at http://vger.kernel.org/majordomo-info.html