diff options
-rw-r--r-- | net/dccp/feat.c | 146 | ||||
-rw-r--r-- | net/dccp/feat.h | 4 | ||||
-rw-r--r-- | net/dccp/options.c | 23 |
3 files changed, 155 insertions, 18 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 | ||
988 | static void dccp_feat_empty_confirm(struct dccp_minisock *dmsk, | 987 | static 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 | ||
1096 | EXPORT_SYMBOL_GPL(dccp_feat_change_recv); | 1095 | EXPORT_SYMBOL_GPL(dccp_feat_change_recv); |
1096 | #endif /* (later) */ | ||
1097 | 1097 | ||
1098 | int dccp_feat_confirm_recv(struct sock *sk, u8 type, u8 feature, | 1098 | int 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 | ||
1235 | EXPORT_SYMBOL_GPL(dccp_feat_clone); | 1235 | EXPORT_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 | */ | ||
1247 | static 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 | |||
1337 | unknown_feature_or_value: | ||
1338 | if (!is_mandatory) | ||
1339 | return dccp_push_empty_confirm(fn, feat, local); | ||
1340 | |||
1341 | not_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 | */ | ||
1357 | int 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 | |||
1237 | int dccp_feat_init(struct sock *sk) | 1381 | int dccp_feat_init(struct sock *sk) |
1238 | { | 1382 | { |
1239 | struct dccp_sock *dp = dccp_sk(sk); | 1383 | struct dccp_sock *dp = dccp_sk(sk); |
diff --git a/net/dccp/feat.h b/net/dccp/feat.h index 8e863839ed91..ce97f3fe7687 100644 --- a/net/dccp/feat.h +++ b/net/dccp/feat.h | |||
@@ -115,8 +115,8 @@ static inline void dccp_feat_debug(const u8 type, const u8 feat, const u8 val) | |||
115 | extern int dccp_feat_register_sp(struct sock *sk, u8 feat, u8 is_local, | 115 | extern int dccp_feat_register_sp(struct sock *sk, u8 feat, u8 is_local, |
116 | u8 const *list, u8 len); | 116 | u8 const *list, u8 len); |
117 | extern int dccp_feat_register_nn(struct sock *sk, u8 feat, u64 val); | 117 | extern int dccp_feat_register_nn(struct sock *sk, u8 feat, u64 val); |
118 | extern int dccp_feat_change_recv(struct sock *sk, u8 type, u8 feature, | 118 | extern int dccp_feat_parse_options(struct sock *, struct dccp_request_sock *, |
119 | u8 *val, u8 len); | 119 | u8 mand, u8 opt, u8 feat, u8 *val, u8 len); |
120 | extern int dccp_feat_confirm_recv(struct sock *sk, u8 type, u8 feature, | 120 | extern int dccp_feat_confirm_recv(struct sock *sk, u8 type, u8 feature, |
121 | u8 *val, u8 len); | 121 | u8 *val, u8 len); |
122 | extern void dccp_feat_clean(struct dccp_minisock *dmsk); | 122 | extern void dccp_feat_clean(struct dccp_minisock *dmsk); |
diff --git a/net/dccp/options.c b/net/dccp/options.c index 0e277114cb23..fb8466ea467a 100644 --- a/net/dccp/options.c +++ b/net/dccp/options.c | |||
@@ -135,22 +135,13 @@ int dccp_parse_options(struct sock *sk, struct dccp_request_sock *dreq, | |||
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: |
138 | /* fall through */ | ||
139 | case DCCPO_CHANGE_R: | 138 | case DCCPO_CHANGE_R: |
140 | if (pkt_type == DCCP_PKT_DATA) | 139 | if (pkt_type == DCCP_PKT_DATA) |
141 | break; | 140 | break; |
142 | if (len < 2) | 141 | rc = dccp_feat_parse_options(sk, dreq, mandatory, opt, |
143 | goto out_invalid_option; | 142 | *value, value + 1, len - 1); |
144 | rc = dccp_feat_change_recv(sk, opt, *value, value + 1, | 143 | if (rc) |
145 | len - 1); | 144 | goto out_featneg_failed; |
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; | ||
154 | break; | 145 | break; |
155 | case DCCPO_CONFIRM_L: | 146 | case DCCPO_CONFIRM_L: |
156 | /* fall through */ | 147 | /* fall through */ |
@@ -292,8 +283,10 @@ out_nonsensical_length: | |||
292 | 283 | ||
293 | out_invalid_option: | 284 | out_invalid_option: |
294 | DCCP_INC_STATS_BH(DCCP_MIB_INVALIDOPT); | 285 | DCCP_INC_STATS_BH(DCCP_MIB_INVALIDOPT); |
295 | DCCP_SKB_CB(skb)->dccpd_reset_code = DCCP_RESET_CODE_OPTION_ERROR; | 286 | rc = DCCP_RESET_CODE_OPTION_ERROR; |
296 | DCCP_WARN("DCCP(%p): invalid option %d, len=%d", sk, opt, len); | 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; | ||
297 | DCCP_SKB_CB(skb)->dccpd_reset_data[0] = opt; | 290 | DCCP_SKB_CB(skb)->dccpd_reset_data[0] = opt; |
298 | DCCP_SKB_CB(skb)->dccpd_reset_data[1] = len > 0 ? value[0] : 0; | 291 | DCCP_SKB_CB(skb)->dccpd_reset_data[1] = len > 0 ? value[0] : 0; |
299 | DCCP_SKB_CB(skb)->dccpd_reset_data[2] = len > 1 ? value[1] : 0; | 292 | DCCP_SKB_CB(skb)->dccpd_reset_data[2] = len > 1 ? value[1] : 0; |