dccp: Processing Confirm options
Analogous to the previous patch, this adds code to interpret incoming Confirm feature-negotiation options. Both functions operate on the feature-negotiation list of either the request_sock (server) or the dccp_sock (client). Thanks to Wei Yongjun for pointing out that it is overly restrictive to check the entire list of confirmed SP values. Signed-off-by: Gerrit Renker <gerrit@erg.abdn.ac.uk> Acked-by: Ian McDonald <ian.mcdonald@jandi.co.nz>
This commit is contained in:
parent
5a146b97d5
commit
d2150b7bff
100
net/dccp/feat.c
100
net/dccp/feat.c
|
@ -99,6 +99,13 @@ static int dccp_feat_default_value(u8 feat_num)
|
||||||
return idx < 0 ? : dccp_feat_table[idx].default_value;
|
return idx < 0 ? : dccp_feat_table[idx].default_value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Test for "Req'd" feature (RFC 4340, 6.4) */
|
||||||
|
static inline int dccp_feat_must_be_understood(u8 feat_num)
|
||||||
|
{
|
||||||
|
return feat_num == DCCPF_CCID || feat_num == DCCPF_SHORT_SEQNOS ||
|
||||||
|
feat_num == DCCPF_SEQUENCE_WINDOW;
|
||||||
|
}
|
||||||
|
|
||||||
/* copy constructor, fval must not already contain allocated memory */
|
/* copy constructor, fval must not already contain allocated memory */
|
||||||
static int dccp_feat_clone_sp_val(dccp_feat_val *fval, u8 const *val, u8 len)
|
static int dccp_feat_clone_sp_val(dccp_feat_val *fval, u8 const *val, u8 len)
|
||||||
{
|
{
|
||||||
|
@ -1093,7 +1100,6 @@ int dccp_feat_change_recv(struct sock *sk, u8 type, u8 feature, u8 *val, u8 len)
|
||||||
}
|
}
|
||||||
|
|
||||||
EXPORT_SYMBOL_GPL(dccp_feat_change_recv);
|
EXPORT_SYMBOL_GPL(dccp_feat_change_recv);
|
||||||
#endif /* (later) */
|
|
||||||
|
|
||||||
int dccp_feat_confirm_recv(struct sock *sk, u8 type, u8 feature,
|
int dccp_feat_confirm_recv(struct sock *sk, u8 type, u8 feature,
|
||||||
u8 *val, u8 len)
|
u8 *val, u8 len)
|
||||||
|
@ -1148,6 +1154,7 @@ int dccp_feat_confirm_recv(struct sock *sk, u8 type, u8 feature,
|
||||||
}
|
}
|
||||||
|
|
||||||
EXPORT_SYMBOL_GPL(dccp_feat_confirm_recv);
|
EXPORT_SYMBOL_GPL(dccp_feat_confirm_recv);
|
||||||
|
#endif /* (later) */
|
||||||
|
|
||||||
void dccp_feat_clean(struct dccp_minisock *dmsk)
|
void dccp_feat_clean(struct dccp_minisock *dmsk)
|
||||||
{
|
{
|
||||||
|
@ -1343,6 +1350,93 @@ not_valid_or_not_known:
|
||||||
: DCCP_RESET_CODE_OPTION_ERROR;
|
: DCCP_RESET_CODE_OPTION_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* dccp_feat_confirm_recv - Process received Confirm options
|
||||||
|
* @fn: feature-negotiation list to update
|
||||||
|
* @is_mandatory: whether @opt was preceded by a Mandatory option
|
||||||
|
* @opt: %DCCPO_CONFIRM_L or %DCCPO_CONFIRM_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 server (1) or client (0)
|
||||||
|
*/
|
||||||
|
static u8 dccp_feat_confirm_recv(struct list_head *fn, u8 is_mandatory, u8 opt,
|
||||||
|
u8 feat, u8 *val, u8 len, const bool server)
|
||||||
|
{
|
||||||
|
u8 *plist, plen, type = dccp_feat_type(feat);
|
||||||
|
const bool local = (opt == DCCPO_CONFIRM_R);
|
||||||
|
struct dccp_feat_entry *entry = dccp_feat_list_lookup(fn, feat, local);
|
||||||
|
|
||||||
|
if (entry == NULL) { /* nothing queued: ignore or handle error */
|
||||||
|
if (is_mandatory && type == FEAT_UNKNOWN)
|
||||||
|
return DCCP_RESET_CODE_MANDATORY_ERROR;
|
||||||
|
|
||||||
|
if (!local && type == FEAT_NN) /* 6.3.2 */
|
||||||
|
goto confirmation_failed;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entry->state != FEAT_CHANGING) /* 6.6.2 */
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (len == 0) {
|
||||||
|
if (dccp_feat_must_be_understood(feat)) /* 6.6.7 */
|
||||||
|
goto confirmation_failed;
|
||||||
|
/*
|
||||||
|
* Empty Confirm during connection setup: this means reverting
|
||||||
|
* to the `old' value, which in this case is the default. Since
|
||||||
|
* we handle default values automatically when no other values
|
||||||
|
* have been set, we revert to the old value by removing this
|
||||||
|
* entry from the list.
|
||||||
|
*/
|
||||||
|
dccp_feat_list_pop(entry);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type == FEAT_NN) {
|
||||||
|
if (len > sizeof(entry->val.nn))
|
||||||
|
goto confirmation_failed;
|
||||||
|
|
||||||
|
if (entry->val.nn == dccp_decode_value_var(val, len))
|
||||||
|
goto confirmation_succeeded;
|
||||||
|
|
||||||
|
DCCP_WARN("Bogus Confirm for non-existing value\n");
|
||||||
|
goto confirmation_failed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Parsing SP Confirms: the first element of @val is the preferred
|
||||||
|
* SP value which the peer confirms, the remainder depends on @len.
|
||||||
|
* Note that only the confirmed value need to be a valid SP value.
|
||||||
|
*/
|
||||||
|
if (!dccp_feat_is_valid_sp_val(feat, *val))
|
||||||
|
goto confirmation_failed;
|
||||||
|
|
||||||
|
if (len == 1) { /* peer didn't supply a preference list */
|
||||||
|
plist = val;
|
||||||
|
plen = len;
|
||||||
|
} else { /* preferred value + preference list */
|
||||||
|
plist = val + 1;
|
||||||
|
plen = len - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check whether the peer got the reconciliation right (6.6.8) */
|
||||||
|
if (dccp_feat_reconcile(&entry->val, plist, plen, server, 0) != *val) {
|
||||||
|
DCCP_WARN("Confirm selected the wrong value %u\n", *val);
|
||||||
|
return DCCP_RESET_CODE_OPTION_ERROR;
|
||||||
|
}
|
||||||
|
entry->val.sp.vec[0] = *val;
|
||||||
|
|
||||||
|
confirmation_succeeded:
|
||||||
|
entry->state = FEAT_STABLE;
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
confirmation_failed:
|
||||||
|
DCCP_WARN("Confirmation failed\n");
|
||||||
|
return is_mandatory ? DCCP_RESET_CODE_MANDATORY_ERROR
|
||||||
|
: DCCP_RESET_CODE_OPTION_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* dccp_feat_parse_options - Process Feature-Negotiation Options
|
* dccp_feat_parse_options - Process Feature-Negotiation Options
|
||||||
* @sk: for general use and used by the client during connection setup
|
* @sk: for general use and used by the client during connection setup
|
||||||
|
@ -1373,6 +1467,10 @@ int dccp_feat_parse_options(struct sock *sk, struct dccp_request_sock *dreq,
|
||||||
case DCCPO_CHANGE_R:
|
case DCCPO_CHANGE_R:
|
||||||
return dccp_feat_change_recv(fn, mandatory, opt, feat,
|
return dccp_feat_change_recv(fn, mandatory, opt, feat,
|
||||||
val, len, server);
|
val, len, server);
|
||||||
|
case DCCPO_CONFIRM_R:
|
||||||
|
case DCCPO_CONFIRM_L:
|
||||||
|
return dccp_feat_confirm_recv(fn, mandatory, opt, feat,
|
||||||
|
val, len, server);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0; /* ignore FN options in all other states */
|
return 0; /* ignore FN options in all other states */
|
||||||
|
|
|
@ -117,8 +117,6 @@ extern int dccp_feat_register_sp(struct sock *sk, u8 feat, u8 is_local,
|
||||||
extern int dccp_feat_register_nn(struct sock *sk, u8 feat, u64 val);
|
extern int dccp_feat_register_nn(struct sock *sk, u8 feat, u64 val);
|
||||||
extern int dccp_feat_parse_options(struct sock *, struct dccp_request_sock *,
|
extern int dccp_feat_parse_options(struct sock *, struct dccp_request_sock *,
|
||||||
u8 mand, u8 opt, u8 feat, u8 *val, u8 len);
|
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);
|
extern void dccp_feat_clean(struct dccp_minisock *dmsk);
|
||||||
extern int dccp_feat_clone(struct sock *oldsk, struct sock *newsk);
|
extern int dccp_feat_clone(struct sock *oldsk, struct sock *newsk);
|
||||||
extern int dccp_feat_clone_list(struct list_head const *, struct list_head *);
|
extern int dccp_feat_clone_list(struct list_head const *, struct list_head *);
|
||||||
|
|
|
@ -134,26 +134,14 @@ int dccp_parse_options(struct sock *sk, struct dccp_request_sock *dreq,
|
||||||
dccp_pr_debug("%s opt: NDP count=%llu\n", dccp_role(sk),
|
dccp_pr_debug("%s opt: NDP count=%llu\n", dccp_role(sk),
|
||||||
(unsigned long long)opt_recv->dccpor_ndp);
|
(unsigned long long)opt_recv->dccpor_ndp);
|
||||||
break;
|
break;
|
||||||
case DCCPO_CHANGE_L:
|
case DCCPO_CHANGE_L ... DCCPO_CONFIRM_R:
|
||||||
case DCCPO_CHANGE_R:
|
if (pkt_type == DCCP_PKT_DATA) /* RFC 4340, 6 */
|
||||||
if (pkt_type == DCCP_PKT_DATA)
|
|
||||||
break;
|
break;
|
||||||
rc = dccp_feat_parse_options(sk, dreq, mandatory, opt,
|
rc = dccp_feat_parse_options(sk, dreq, mandatory, opt,
|
||||||
*value, value + 1, len - 1);
|
*value, value + 1, len - 1);
|
||||||
if (rc)
|
if (rc)
|
||||||
goto out_featneg_failed;
|
goto out_featneg_failed;
|
||||||
break;
|
break;
|
||||||
case DCCPO_CONFIRM_L:
|
|
||||||
/* fall through */
|
|
||||||
case DCCPO_CONFIRM_R:
|
|
||||||
if (pkt_type == DCCP_PKT_DATA)
|
|
||||||
break;
|
|
||||||
if (len < 2) /* FIXME this disallows empty confirm */
|
|
||||||
goto out_invalid_option;
|
|
||||||
if (dccp_feat_confirm_recv(sk, opt, *value,
|
|
||||||
value + 1, len - 1))
|
|
||||||
goto out_invalid_option;
|
|
||||||
break;
|
|
||||||
case DCCPO_ACK_VECTOR_0:
|
case DCCPO_ACK_VECTOR_0:
|
||||||
case DCCPO_ACK_VECTOR_1:
|
case DCCPO_ACK_VECTOR_1:
|
||||||
if (dccp_packet_without_ack(skb)) /* RFC 4340, 11.4 */
|
if (dccp_packet_without_ack(skb)) /* RFC 4340, 11.4 */
|
||||||
|
|
Reference in New Issue