diff options
Diffstat (limited to 'net/dccp/feat.c')
-rw-r--r-- | net/dccp/feat.c | 180 |
1 files changed, 179 insertions, 1 deletions
diff --git a/net/dccp/feat.c b/net/dccp/feat.c index fe6d557328d6..283c7fb8c7dc 100644 --- a/net/dccp/feat.c +++ b/net/dccp/feat.c | |||
@@ -237,6 +237,40 @@ static int dccp_feat_push_change(struct list_head *fn_list, u8 feat, u8 local, | |||
237 | return 0; | 237 | return 0; |
238 | } | 238 | } |
239 | 239 | ||
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 | |||
240 | static inline void dccp_feat_list_pop(struct dccp_feat_entry *entry) | 274 | static inline void dccp_feat_list_pop(struct dccp_feat_entry *entry) |
241 | { | 275 | { |
242 | list_del(&entry->node); | 276 | list_del(&entry->node); |
@@ -955,7 +989,6 @@ static int dccp_feat_nn(struct sock *sk, u8 type, u8 feature, u8 *val, u8 len) | |||
955 | 989 | ||
956 | return 0; | 990 | return 0; |
957 | } | 991 | } |
958 | #endif /* (later) */ | ||
959 | 992 | ||
960 | static void dccp_feat_empty_confirm(struct dccp_minisock *dmsk, | 993 | static void dccp_feat_empty_confirm(struct dccp_minisock *dmsk, |
961 | u8 type, u8 feature) | 994 | u8 type, u8 feature) |
@@ -1066,6 +1099,7 @@ int dccp_feat_change_recv(struct sock *sk, u8 type, u8 feature, u8 *val, u8 len) | |||
1066 | } | 1099 | } |
1067 | 1100 | ||
1068 | EXPORT_SYMBOL_GPL(dccp_feat_change_recv); | 1101 | EXPORT_SYMBOL_GPL(dccp_feat_change_recv); |
1102 | #endif /* (later) */ | ||
1069 | 1103 | ||
1070 | int dccp_feat_confirm_recv(struct sock *sk, u8 type, u8 feature, | 1104 | int dccp_feat_confirm_recv(struct sock *sk, u8 type, u8 feature, |
1071 | u8 *val, u8 len) | 1105 | u8 *val, u8 len) |
@@ -1206,6 +1240,150 @@ out_clean: | |||
1206 | 1240 | ||
1207 | EXPORT_SYMBOL_GPL(dccp_feat_clone); | 1241 | EXPORT_SYMBOL_GPL(dccp_feat_clone); |
1208 | 1242 | ||
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 | |||
1209 | int dccp_feat_init(struct sock *sk) | 1387 | int dccp_feat_init(struct sock *sk) |
1210 | { | 1388 | { |
1211 | struct dccp_sock *dp = dccp_sk(sk); | 1389 | struct dccp_sock *dp = dccp_sk(sk); |