Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 122404
b: refs/heads/master
c: e77b836
h: refs/heads/master
v: v3
  • Loading branch information
Gerrit Renker authored and David S. Miller committed Dec 2, 2008
1 parent 42a8626 commit 5ec5578
Show file tree
Hide file tree
Showing 4 changed files with 190 additions and 19 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 75757a7d0c54f8fdd414c74a6005d275032b0115
refs/heads/master: e77b8363b2ea7c0d89919547c1a8b0562f298b57
180 changes: 179 additions & 1 deletion trunk/net/dccp/feat.c
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,40 @@ static int dccp_feat_push_change(struct list_head *fn_list, u8 feat, u8 local,
return 0;
}

/**
* dccp_feat_push_confirm - Add a Confirm entry to the FN list
* @fn_list: feature-negotiation list to add to
* @feat: one of %dccp_feature_numbers
* @local: whether local (1) or remote (0) @feat_num is being confirmed
* @fval: pointer to NN/SP value to be inserted or NULL
* Returns 0 on success, a Reset code for further processing otherwise.
*/
static int dccp_feat_push_confirm(struct list_head *fn_list, u8 feat, u8 local,
dccp_feat_val *fval)
{
struct dccp_feat_entry *new = dccp_feat_entry_new(fn_list, feat, local);

if (new == NULL)
return DCCP_RESET_CODE_TOO_BUSY;

new->feat_num = feat;
new->is_local = local;
new->state = FEAT_STABLE; /* transition in 6.6.2 */
new->needs_confirm = 1;
new->empty_confirm = (fval == NULL);
new->val.nn = 0; /* zeroes the whole structure */
if (!new->empty_confirm)
new->val = *fval;
new->needs_mandatory = 0;

return 0;
}

static int dccp_push_empty_confirm(struct list_head *fn_list, u8 feat, u8 local)
{
return dccp_feat_push_confirm(fn_list, feat, local, NULL);
}

static inline void dccp_feat_list_pop(struct dccp_feat_entry *entry)
{
list_del(&entry->node);
Expand Down Expand Up @@ -955,7 +989,6 @@ static int dccp_feat_nn(struct sock *sk, u8 type, u8 feature, u8 *val, u8 len)

return 0;
}
#endif /* (later) */

static void dccp_feat_empty_confirm(struct dccp_minisock *dmsk,
u8 type, u8 feature)
Expand Down Expand Up @@ -1066,6 +1099,7 @@ int dccp_feat_change_recv(struct sock *sk, u8 type, u8 feature, u8 *val, u8 len)
}

EXPORT_SYMBOL_GPL(dccp_feat_change_recv);
#endif /* (later) */

int dccp_feat_confirm_recv(struct sock *sk, u8 type, u8 feature,
u8 *val, u8 len)
Expand Down Expand Up @@ -1206,6 +1240,150 @@ int dccp_feat_clone(struct sock *oldsk, struct sock *newsk)

EXPORT_SYMBOL_GPL(dccp_feat_clone);

/**
* 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)
{
u8 defval, type = dccp_feat_type(feat);
const bool local = (opt == DCCPO_CHANGE_R);
struct dccp_feat_entry *entry;
dccp_feat_val fval;

if (len == 0 || type == FEAT_UNKNOWN) /* 6.1 and 6.6.8 */
goto unknown_feature_or_value;

/*
* Negotiation of NN features: Change R is invalid, so there is no
* simultaneous negotiation; hence we do not look up in the list.
*/
if (type == FEAT_NN) {
if (local || 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;

return dccp_feat_push_confirm(fn, feat, local, &fval);
}

/*
* Unidirectional/simultaneous negotiation of SP features (6.3.1)
*/
entry = dccp_feat_list_lookup(fn, feat, local);
if (entry == NULL) {
/*
* 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;

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;
} else if (!dccp_feat_is_valid_sp_val(feat, fval.sp.vec[0])) {
kfree(fval.sp.vec);
goto unknown_feature_or_value;
}

/* 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;
}

return dccp_feat_push_confirm(fn, feat, local, &fval);

} else if (entry->state == FEAT_UNSTABLE) { /* 6.6.2 */
return 0;
}

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.
*/
WARN_ON(!server);
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;

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;
}

/**
* 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;

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);
}
}
return 0; /* ignore FN options in all other states */
}

int dccp_feat_init(struct sock *sk)
{
struct dccp_sock *dp = dccp_sk(sk);
Expand Down
4 changes: 2 additions & 2 deletions trunk/net/dccp/feat.h
Original file line number Diff line number Diff line change
Expand Up @@ -116,8 +116,8 @@ static inline void dccp_feat_debug(const u8 type, const u8 feat, const u8 val)
extern int dccp_feat_register_sp(struct sock *sk, u8 feat, u8 is_local,
u8 const *list, u8 len);
extern int dccp_feat_register_nn(struct sock *sk, u8 feat, u64 val);
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);
Expand Down
23 changes: 8 additions & 15 deletions trunk/net/dccp/options.c
Original file line number Diff line number Diff line change
Expand Up @@ -135,22 +135,13 @@ int dccp_parse_options(struct sock *sk, struct dccp_request_sock *dreq,
(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)
goto out_invalid_option;
rc = dccp_feat_parse_options(sk, dreq, mandatory, opt,
*value, value + 1, len - 1);
if (rc)
goto out_featneg_failed;
break;
case DCCPO_CONFIRM_L:
/* fall through */
Expand Down Expand Up @@ -292,8 +283,10 @@ int dccp_parse_options(struct sock *sk, struct dccp_request_sock *dreq,

out_invalid_option:
DCCP_INC_STATS_BH(DCCP_MIB_INVALIDOPT);
DCCP_SKB_CB(skb)->dccpd_reset_code = DCCP_RESET_CODE_OPTION_ERROR;
DCCP_WARN("DCCP(%p): invalid option %d, len=%d", sk, opt, len);
rc = DCCP_RESET_CODE_OPTION_ERROR;
out_featneg_failed:
DCCP_WARN("DCCP(%p): Option %d (len=%d) error=%u\n", sk, opt, len, rc);
DCCP_SKB_CB(skb)->dccpd_reset_code = rc;
DCCP_SKB_CB(skb)->dccpd_reset_data[0] = opt;
DCCP_SKB_CB(skb)->dccpd_reset_data[1] = len > 0 ? value[0] : 0;
DCCP_SKB_CB(skb)->dccpd_reset_data[2] = len > 1 ? value[1] : 0;
Expand Down

0 comments on commit 5ec5578

Please sign in to comment.