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