diff options
-rw-r--r-- | net/dccp/feat.c | 100 | ||||
-rw-r--r-- | net/dccp/feat.h | 2 | ||||
-rw-r--r-- | net/dccp/options.c | 16 |
3 files changed, 101 insertions, 17 deletions
diff --git a/net/dccp/feat.c b/net/dccp/feat.c index 283c7fb8c7dc..bc00c038e4a5 100644 --- a/net/dccp/feat.c +++ b/net/dccp/feat.c | |||
@@ -104,6 +104,13 @@ static int dccp_feat_default_value(u8 feat_num) | |||
104 | return idx < 0 ? 0 : dccp_feat_table[idx].default_value; | 104 | return idx < 0 ? 0 : dccp_feat_table[idx].default_value; |
105 | } | 105 | } |
106 | 106 | ||
107 | /* Test for "Req'd" feature (RFC 4340, 6.4) */ | ||
108 | static inline int dccp_feat_must_be_understood(u8 feat_num) | ||
109 | { | ||
110 | return feat_num == DCCPF_CCID || feat_num == DCCPF_SHORT_SEQNOS || | ||
111 | feat_num == DCCPF_SEQUENCE_WINDOW; | ||
112 | } | ||
113 | |||
107 | /* copy constructor, fval must not already contain allocated memory */ | 114 | /* copy constructor, fval must not already contain allocated memory */ |
108 | static int dccp_feat_clone_sp_val(dccp_feat_val *fval, u8 const *val, u8 len) | 115 | static int dccp_feat_clone_sp_val(dccp_feat_val *fval, u8 const *val, u8 len) |
109 | { | 116 | { |
@@ -1099,7 +1106,6 @@ int dccp_feat_change_recv(struct sock *sk, u8 type, u8 feature, u8 *val, u8 len) | |||
1099 | } | 1106 | } |
1100 | 1107 | ||
1101 | EXPORT_SYMBOL_GPL(dccp_feat_change_recv); | 1108 | EXPORT_SYMBOL_GPL(dccp_feat_change_recv); |
1102 | #endif /* (later) */ | ||
1103 | 1109 | ||
1104 | int dccp_feat_confirm_recv(struct sock *sk, u8 type, u8 feature, | 1110 | int dccp_feat_confirm_recv(struct sock *sk, u8 type, u8 feature, |
1105 | u8 *val, u8 len) | 1111 | u8 *val, u8 len) |
@@ -1154,6 +1160,7 @@ int dccp_feat_confirm_recv(struct sock *sk, u8 type, u8 feature, | |||
1154 | } | 1160 | } |
1155 | 1161 | ||
1156 | EXPORT_SYMBOL_GPL(dccp_feat_confirm_recv); | 1162 | EXPORT_SYMBOL_GPL(dccp_feat_confirm_recv); |
1163 | #endif /* (later) */ | ||
1157 | 1164 | ||
1158 | void dccp_feat_clean(struct dccp_minisock *dmsk) | 1165 | void dccp_feat_clean(struct dccp_minisock *dmsk) |
1159 | { | 1166 | { |
@@ -1350,6 +1357,93 @@ not_valid_or_not_known: | |||
1350 | } | 1357 | } |
1351 | 1358 | ||
1352 | /** | 1359 | /** |
1360 | * dccp_feat_confirm_recv - Process received Confirm options | ||
1361 | * @fn: feature-negotiation list to update | ||
1362 | * @is_mandatory: whether @opt was preceded by a Mandatory option | ||
1363 | * @opt: %DCCPO_CONFIRM_L or %DCCPO_CONFIRM_R | ||
1364 | * @feat: one of %dccp_feature_numbers | ||
1365 | * @val: NN value or SP value/preference list | ||
1366 | * @len: length of @val in bytes | ||
1367 | * @server: whether this node is server (1) or client (0) | ||
1368 | */ | ||
1369 | static u8 dccp_feat_confirm_recv(struct list_head *fn, u8 is_mandatory, u8 opt, | ||
1370 | u8 feat, u8 *val, u8 len, const bool server) | ||
1371 | { | ||
1372 | u8 *plist, plen, type = dccp_feat_type(feat); | ||
1373 | const bool local = (opt == DCCPO_CONFIRM_R); | ||
1374 | struct dccp_feat_entry *entry = dccp_feat_list_lookup(fn, feat, local); | ||
1375 | |||
1376 | if (entry == NULL) { /* nothing queued: ignore or handle error */ | ||
1377 | if (is_mandatory && type == FEAT_UNKNOWN) | ||
1378 | return DCCP_RESET_CODE_MANDATORY_ERROR; | ||
1379 | |||
1380 | if (!local && type == FEAT_NN) /* 6.3.2 */ | ||
1381 | goto confirmation_failed; | ||
1382 | return 0; | ||
1383 | } | ||
1384 | |||
1385 | if (entry->state != FEAT_CHANGING) /* 6.6.2 */ | ||
1386 | return 0; | ||
1387 | |||
1388 | if (len == 0) { | ||
1389 | if (dccp_feat_must_be_understood(feat)) /* 6.6.7 */ | ||
1390 | goto confirmation_failed; | ||
1391 | /* | ||
1392 | * Empty Confirm during connection setup: this means reverting | ||
1393 | * to the `old' value, which in this case is the default. Since | ||
1394 | * we handle default values automatically when no other values | ||
1395 | * have been set, we revert to the old value by removing this | ||
1396 | * entry from the list. | ||
1397 | */ | ||
1398 | dccp_feat_list_pop(entry); | ||
1399 | return 0; | ||
1400 | } | ||
1401 | |||
1402 | if (type == FEAT_NN) { | ||
1403 | if (len > sizeof(entry->val.nn)) | ||
1404 | goto confirmation_failed; | ||
1405 | |||
1406 | if (entry->val.nn == dccp_decode_value_var(val, len)) | ||
1407 | goto confirmation_succeeded; | ||
1408 | |||
1409 | DCCP_WARN("Bogus Confirm for non-existing value\n"); | ||
1410 | goto confirmation_failed; | ||
1411 | } | ||
1412 | |||
1413 | /* | ||
1414 | * Parsing SP Confirms: the first element of @val is the preferred | ||
1415 | * SP value which the peer confirms, the remainder depends on @len. | ||
1416 | * Note that only the confirmed value need to be a valid SP value. | ||
1417 | */ | ||
1418 | if (!dccp_feat_is_valid_sp_val(feat, *val)) | ||
1419 | goto confirmation_failed; | ||
1420 | |||
1421 | if (len == 1) { /* peer didn't supply a preference list */ | ||
1422 | plist = val; | ||
1423 | plen = len; | ||
1424 | } else { /* preferred value + preference list */ | ||
1425 | plist = val + 1; | ||
1426 | plen = len - 1; | ||
1427 | } | ||
1428 | |||
1429 | /* Check whether the peer got the reconciliation right (6.6.8) */ | ||
1430 | if (dccp_feat_reconcile(&entry->val, plist, plen, server, 0) != *val) { | ||
1431 | DCCP_WARN("Confirm selected the wrong value %u\n", *val); | ||
1432 | return DCCP_RESET_CODE_OPTION_ERROR; | ||
1433 | } | ||
1434 | entry->val.sp.vec[0] = *val; | ||
1435 | |||
1436 | confirmation_succeeded: | ||
1437 | entry->state = FEAT_STABLE; | ||
1438 | return 0; | ||
1439 | |||
1440 | confirmation_failed: | ||
1441 | DCCP_WARN("Confirmation failed\n"); | ||
1442 | return is_mandatory ? DCCP_RESET_CODE_MANDATORY_ERROR | ||
1443 | : DCCP_RESET_CODE_OPTION_ERROR; | ||
1444 | } | ||
1445 | |||
1446 | /** | ||
1353 | * dccp_feat_parse_options - Process Feature-Negotiation Options | 1447 | * dccp_feat_parse_options - Process Feature-Negotiation Options |
1354 | * @sk: for general use and used by the client during connection setup | 1448 | * @sk: for general use and used by the client during connection setup |
1355 | * @dreq: used by the server during connection setup | 1449 | * @dreq: used by the server during connection setup |
@@ -1379,6 +1473,10 @@ int dccp_feat_parse_options(struct sock *sk, struct dccp_request_sock *dreq, | |||
1379 | case DCCPO_CHANGE_R: | 1473 | case DCCPO_CHANGE_R: |
1380 | return dccp_feat_change_recv(fn, mandatory, opt, feat, | 1474 | return dccp_feat_change_recv(fn, mandatory, opt, feat, |
1381 | val, len, server); | 1475 | val, len, server); |
1476 | case DCCPO_CONFIRM_R: | ||
1477 | case DCCPO_CONFIRM_L: | ||
1478 | return dccp_feat_confirm_recv(fn, mandatory, opt, feat, | ||
1479 | val, len, server); | ||
1382 | } | 1480 | } |
1383 | } | 1481 | } |
1384 | return 0; /* ignore FN options in all other states */ | 1482 | return 0; /* ignore FN options in all other states */ |
diff --git a/net/dccp/feat.h b/net/dccp/feat.h index 8dc4b42ef172..f749610ae383 100644 --- a/net/dccp/feat.h +++ b/net/dccp/feat.h | |||
@@ -118,8 +118,6 @@ extern int dccp_feat_register_sp(struct sock *sk, u8 feat, u8 is_local, | |||
118 | extern int dccp_feat_register_nn(struct sock *sk, u8 feat, u64 val); | 118 | extern int dccp_feat_register_nn(struct sock *sk, u8 feat, u64 val); |
119 | extern int dccp_feat_parse_options(struct sock *, struct dccp_request_sock *, | 119 | extern int dccp_feat_parse_options(struct sock *, struct dccp_request_sock *, |
120 | u8 mand, u8 opt, u8 feat, u8 *val, u8 len); | 120 | u8 mand, u8 opt, u8 feat, u8 *val, u8 len); |
121 | extern int dccp_feat_confirm_recv(struct sock *sk, u8 type, u8 feature, | ||
122 | u8 *val, u8 len); | ||
123 | extern void dccp_feat_clean(struct dccp_minisock *dmsk); | 121 | extern void dccp_feat_clean(struct dccp_minisock *dmsk); |
124 | extern int dccp_feat_clone(struct sock *oldsk, struct sock *newsk); | 122 | extern int dccp_feat_clone(struct sock *oldsk, struct sock *newsk); |
125 | extern int dccp_feat_clone_list(struct list_head const *, struct list_head *); | 123 | extern int dccp_feat_clone_list(struct list_head const *, struct list_head *); |
diff --git a/net/dccp/options.c b/net/dccp/options.c index 2c444c199725..debb1008c7ad 100644 --- a/net/dccp/options.c +++ b/net/dccp/options.c | |||
@@ -134,26 +134,14 @@ int dccp_parse_options(struct sock *sk, struct dccp_request_sock *dreq, | |||
134 | dccp_pr_debug("%s opt: NDP count=%llu\n", dccp_role(sk), | 134 | dccp_pr_debug("%s opt: NDP count=%llu\n", dccp_role(sk), |
135 | (unsigned long long)opt_recv->dccpor_ndp); | 135 | (unsigned long long)opt_recv->dccpor_ndp); |
136 | break; | 136 | break; |
137 | case DCCPO_CHANGE_L: | 137 | case DCCPO_CHANGE_L ... DCCPO_CONFIRM_R: |
138 | case DCCPO_CHANGE_R: | 138 | if (pkt_type == DCCP_PKT_DATA) /* RFC 4340, 6 */ |
139 | if (pkt_type == DCCP_PKT_DATA) | ||
140 | break; | 139 | break; |
141 | rc = dccp_feat_parse_options(sk, dreq, mandatory, opt, | 140 | rc = dccp_feat_parse_options(sk, dreq, mandatory, opt, |
142 | *value, value + 1, len - 1); | 141 | *value, value + 1, len - 1); |
143 | if (rc) | 142 | if (rc) |
144 | goto out_featneg_failed; | 143 | goto out_featneg_failed; |
145 | break; | 144 | break; |
146 | case DCCPO_CONFIRM_L: | ||
147 | /* fall through */ | ||
148 | case DCCPO_CONFIRM_R: | ||
149 | if (pkt_type == DCCP_PKT_DATA) | ||
150 | break; | ||
151 | if (len < 2) /* FIXME this disallows empty confirm */ | ||
152 | goto out_invalid_option; | ||
153 | if (dccp_feat_confirm_recv(sk, opt, *value, | ||
154 | value + 1, len - 1)) | ||
155 | goto out_invalid_option; | ||
156 | break; | ||
157 | case DCCPO_ACK_VECTOR_0: | 145 | case DCCPO_ACK_VECTOR_0: |
158 | case DCCPO_ACK_VECTOR_1: | 146 | case DCCPO_ACK_VECTOR_1: |
159 | if (dccp_packet_without_ack(skb)) /* RFC 4340, 11.4 */ | 147 | if (dccp_packet_without_ack(skb)) /* RFC 4340, 11.4 */ |