Skip to content

Commit e77b836

Browse files
Gerrit Renkerdavem330
Gerrit Renker
authored andcommitted
dccp: Process incoming Change feature-negotiation options
This adds/replaces code for processing incoming ChangeL/R 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 to fill in the data for the Reset packet. Old code, which is no longer used or referenced, has been removed. Signed-off-by: Gerrit Renker <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 75757a7 commit e77b836

File tree

3 files changed

+189
-18
lines changed

3 files changed

+189
-18
lines changed

net/dccp/feat.c

Lines changed: 179 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,40 @@ static int dccp_feat_push_change(struct list_head *fn_list, u8 feat, u8 local,
237237
return 0;
238238
}
239239

240+
/**
241+
* dccp_feat_push_confirm - Add a Confirm entry to the FN list
242+
* @fn_list: feature-negotiation list to add to
243+
* @feat: one of %dccp_feature_numbers
244+
* @local: whether local (1) or remote (0) @feat_num is being confirmed
245+
* @fval: pointer to NN/SP value to be inserted or NULL
246+
* Returns 0 on success, a Reset code for further processing otherwise.
247+
*/
248+
static int dccp_feat_push_confirm(struct list_head *fn_list, u8 feat, u8 local,
249+
dccp_feat_val *fval)
250+
{
251+
struct dccp_feat_entry *new = dccp_feat_entry_new(fn_list, feat, local);
252+
253+
if (new == NULL)
254+
return DCCP_RESET_CODE_TOO_BUSY;
255+
256+
new->feat_num = feat;
257+
new->is_local = local;
258+
new->state = FEAT_STABLE; /* transition in 6.6.2 */
259+
new->needs_confirm = 1;
260+
new->empty_confirm = (fval == NULL);
261+
new->val.nn = 0; /* zeroes the whole structure */
262+
if (!new->empty_confirm)
263+
new->val = *fval;
264+
new->needs_mandatory = 0;
265+
266+
return 0;
267+
}
268+
269+
static int dccp_push_empty_confirm(struct list_head *fn_list, u8 feat, u8 local)
270+
{
271+
return dccp_feat_push_confirm(fn_list, feat, local, NULL);
272+
}
273+
240274
static inline void dccp_feat_list_pop(struct dccp_feat_entry *entry)
241275
{
242276
list_del(&entry->node);
@@ -955,7 +989,6 @@ static int dccp_feat_nn(struct sock *sk, u8 type, u8 feature, u8 *val, u8 len)
955989

956990
return 0;
957991
}
958-
#endif /* (later) */
959992

960993
static void dccp_feat_empty_confirm(struct dccp_minisock *dmsk,
961994
u8 type, u8 feature)
@@ -1066,6 +1099,7 @@ int dccp_feat_change_recv(struct sock *sk, u8 type, u8 feature, u8 *val, u8 len)
10661099
}
10671100

10681101
EXPORT_SYMBOL_GPL(dccp_feat_change_recv);
1102+
#endif /* (later) */
10691103

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

12071241
EXPORT_SYMBOL_GPL(dccp_feat_clone);
12081242

1243+
/**
1244+
* dccp_feat_change_recv - Process incoming ChangeL/R options
1245+
* @fn: feature-negotiation list to update
1246+
* @is_mandatory: whether the Change was preceded by a Mandatory option
1247+
* @opt: %DCCPO_CHANGE_L or %DCCPO_CHANGE_R
1248+
* @feat: one of %dccp_feature_numbers
1249+
* @val: NN value or SP value/preference list
1250+
* @len: length of @val in bytes
1251+
* @server: whether this node is the server (1) or the client (0)
1252+
*/
1253+
static u8 dccp_feat_change_recv(struct list_head *fn, u8 is_mandatory, u8 opt,
1254+
u8 feat, u8 *val, u8 len, const bool server)
1255+
{
1256+
u8 defval, type = dccp_feat_type(feat);
1257+
const bool local = (opt == DCCPO_CHANGE_R);
1258+
struct dccp_feat_entry *entry;
1259+
dccp_feat_val fval;
1260+
1261+
if (len == 0 || type == FEAT_UNKNOWN) /* 6.1 and 6.6.8 */
1262+
goto unknown_feature_or_value;
1263+
1264+
/*
1265+
* Negotiation of NN features: Change R is invalid, so there is no
1266+
* simultaneous negotiation; hence we do not look up in the list.
1267+
*/
1268+
if (type == FEAT_NN) {
1269+
if (local || len > sizeof(fval.nn))
1270+
goto unknown_feature_or_value;
1271+
1272+
/* 6.3.2: "The feature remote MUST accept any valid value..." */
1273+
fval.nn = dccp_decode_value_var(val, len);
1274+
if (!dccp_feat_is_valid_nn_val(feat, fval.nn))
1275+
goto unknown_feature_or_value;
1276+
1277+
return dccp_feat_push_confirm(fn, feat, local, &fval);
1278+
}
1279+
1280+
/*
1281+
* Unidirectional/simultaneous negotiation of SP features (6.3.1)
1282+
*/
1283+
entry = dccp_feat_list_lookup(fn, feat, local);
1284+
if (entry == NULL) {
1285+
/*
1286+
* No particular preferences have been registered. We deal with
1287+
* this situation by assuming that all valid values are equally
1288+
* acceptable, and apply the following checks:
1289+
* - if the peer's list is a singleton, we accept a valid value;
1290+
* - if we are the server, we first try to see if the peer (the
1291+
* client) advertises the default value. If yes, we use it,
1292+
* otherwise we accept the preferred value;
1293+
* - else if we are the client, we use the first list element.
1294+
*/
1295+
if (dccp_feat_clone_sp_val(&fval, val, 1))
1296+
return DCCP_RESET_CODE_TOO_BUSY;
1297+
1298+
if (len > 1 && server) {
1299+
defval = dccp_feat_default_value(feat);
1300+
if (dccp_feat_preflist_match(&defval, 1, val, len) > -1)
1301+
fval.sp.vec[0] = defval;
1302+
} else if (!dccp_feat_is_valid_sp_val(feat, fval.sp.vec[0])) {
1303+
kfree(fval.sp.vec);
1304+
goto unknown_feature_or_value;
1305+
}
1306+
1307+
/* Treat unsupported CCIDs like invalid values */
1308+
if (feat == DCCPF_CCID && !ccid_support_check(fval.sp.vec, 1)) {
1309+
kfree(fval.sp.vec);
1310+
goto not_valid_or_not_known;
1311+
}
1312+
1313+
return dccp_feat_push_confirm(fn, feat, local, &fval);
1314+
1315+
} else if (entry->state == FEAT_UNSTABLE) { /* 6.6.2 */
1316+
return 0;
1317+
}
1318+
1319+
if (dccp_feat_reconcile(&entry->val, val, len, server, true)) {
1320+
entry->empty_confirm = 0;
1321+
} else if (is_mandatory) {
1322+
return DCCP_RESET_CODE_MANDATORY_ERROR;
1323+
} else if (entry->state == FEAT_INITIALISING) {
1324+
/*
1325+
* Failed simultaneous negotiation (server only): try to `save'
1326+
* the connection by checking whether entry contains the default
1327+
* value for @feat. If yes, send an empty Confirm to signal that
1328+
* the received Change was not understood - which implies using
1329+
* the default value.
1330+
* If this also fails, we use Reset as the last resort.
1331+
*/
1332+
WARN_ON(!server);
1333+
defval = dccp_feat_default_value(feat);
1334+
if (!dccp_feat_reconcile(&entry->val, &defval, 1, server, true))
1335+
return DCCP_RESET_CODE_OPTION_ERROR;
1336+
entry->empty_confirm = 1;
1337+
}
1338+
entry->needs_confirm = 1;
1339+
entry->needs_mandatory = 0;
1340+
entry->state = FEAT_STABLE;
1341+
return 0;
1342+
1343+
unknown_feature_or_value:
1344+
if (!is_mandatory)
1345+
return dccp_push_empty_confirm(fn, feat, local);
1346+
1347+
not_valid_or_not_known:
1348+
return is_mandatory ? DCCP_RESET_CODE_MANDATORY_ERROR
1349+
: DCCP_RESET_CODE_OPTION_ERROR;
1350+
}
1351+
1352+
/**
1353+
* dccp_feat_parse_options - Process Feature-Negotiation Options
1354+
* @sk: for general use and used by the client during connection setup
1355+
* @dreq: used by the server during connection setup
1356+
* @mandatory: whether @opt was preceded by a Mandatory option
1357+
* @opt: %DCCPO_CHANGE_L | %DCCPO_CHANGE_R | %DCCPO_CONFIRM_L | %DCCPO_CONFIRM_R
1358+
* @feat: one of %dccp_feature_numbers
1359+
* @val: value contents of @opt
1360+
* @len: length of @val in bytes
1361+
* Returns 0 on success, a Reset code for ending the connection otherwise.
1362+
*/
1363+
int dccp_feat_parse_options(struct sock *sk, struct dccp_request_sock *dreq,
1364+
u8 mandatory, u8 opt, u8 feat, u8 *val, u8 len)
1365+
{
1366+
struct dccp_sock *dp = dccp_sk(sk);
1367+
struct list_head *fn = dreq ? &dreq->dreq_featneg : &dp->dccps_featneg;
1368+
bool server = false;
1369+
1370+
switch (sk->sk_state) {
1371+
/*
1372+
* Negotiation during connection setup
1373+
*/
1374+
case DCCP_LISTEN:
1375+
server = true; /* fall through */
1376+
case DCCP_REQUESTING:
1377+
switch (opt) {
1378+
case DCCPO_CHANGE_L:
1379+
case DCCPO_CHANGE_R:
1380+
return dccp_feat_change_recv(fn, mandatory, opt, feat,
1381+
val, len, server);
1382+
}
1383+
}
1384+
return 0; /* ignore FN options in all other states */
1385+
}
1386+
12091387
int dccp_feat_init(struct sock *sk)
12101388
{
12111389
struct dccp_sock *dp = dccp_sk(sk);

net/dccp/feat.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,8 +116,8 @@ static inline void dccp_feat_debug(const u8 type, const u8 feat, const u8 val)
116116
extern int dccp_feat_register_sp(struct sock *sk, u8 feat, u8 is_local,
117117
u8 const *list, u8 len);
118118
extern int dccp_feat_register_nn(struct sock *sk, u8 feat, u64 val);
119-
extern int dccp_feat_change_recv(struct sock *sk, u8 type, u8 feature,
120-
u8 *val, u8 len);
119+
extern int dccp_feat_parse_options(struct sock *, struct dccp_request_sock *,
120+
u8 mand, u8 opt, u8 feat, u8 *val, u8 len);
121121
extern int dccp_feat_confirm_recv(struct sock *sk, u8 type, u8 feature,
122122
u8 *val, u8 len);
123123
extern void dccp_feat_clean(struct dccp_minisock *dmsk);

net/dccp/options.c

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -135,22 +135,13 @@ int dccp_parse_options(struct sock *sk, struct dccp_request_sock *dreq,
135135
(unsigned long long)opt_recv->dccpor_ndp);
136136
break;
137137
case DCCPO_CHANGE_L:
138-
/* fall through */
139138
case DCCPO_CHANGE_R:
140139
if (pkt_type == DCCP_PKT_DATA)
141140
break;
142-
if (len < 2)
143-
goto out_invalid_option;
144-
rc = dccp_feat_change_recv(sk, opt, *value, value + 1,
145-
len - 1);
146-
/*
147-
* When there is a change error, change_recv is
148-
* responsible for dealing with it. i.e. reply with an
149-
* empty confirm.
150-
* If the change was mandatory, then we need to die.
151-
*/
152-
if (rc && mandatory)
153-
goto out_invalid_option;
141+
rc = dccp_feat_parse_options(sk, dreq, mandatory, opt,
142+
*value, value + 1, len - 1);
143+
if (rc)
144+
goto out_featneg_failed;
154145
break;
155146
case DCCPO_CONFIRM_L:
156147
/* fall through */
@@ -292,8 +283,10 @@ int dccp_parse_options(struct sock *sk, struct dccp_request_sock *dreq,
292283

293284
out_invalid_option:
294285
DCCP_INC_STATS_BH(DCCP_MIB_INVALIDOPT);
295-
DCCP_SKB_CB(skb)->dccpd_reset_code = DCCP_RESET_CODE_OPTION_ERROR;
296-
DCCP_WARN("DCCP(%p): invalid option %d, len=%d", sk, opt, len);
286+
rc = DCCP_RESET_CODE_OPTION_ERROR;
287+
out_featneg_failed:
288+
DCCP_WARN("DCCP(%p): Option %d (len=%d) error=%u\n", sk, opt, len, rc);
289+
DCCP_SKB_CB(skb)->dccpd_reset_code = rc;
297290
DCCP_SKB_CB(skb)->dccpd_reset_data[0] = opt;
298291
DCCP_SKB_CB(skb)->dccpd_reset_data[1] = len > 0 ? value[0] : 0;
299292
DCCP_SKB_CB(skb)->dccpd_reset_data[2] = len > 1 ? value[1] : 0;

0 commit comments

Comments
 (0)