diff options
-rw-r--r-- | net/dccp/feat.c | 180 | ||||
-rw-r--r-- | net/dccp/feat.h | 4 | ||||
-rw-r--r-- | net/dccp/options.c | 23 |
3 files changed, 189 insertions, 18 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); |
diff --git a/net/dccp/feat.h b/net/dccp/feat.h index 7efb2025f6bf..8dc4b42ef172 100644 --- a/net/dccp/feat.h +++ b/net/dccp/feat.h | |||
@@ -116,8 +116,8 @@ static inline void dccp_feat_debug(const u8 type, const u8 feat, const u8 val) | |||
116 | extern int dccp_feat_register_sp(struct sock *sk, u8 feat, u8 is_local, | 116 | extern int dccp_feat_register_sp(struct sock *sk, u8 feat, u8 is_local, |
117 | u8 const *list, u8 len); | 117 | u8 const *list, u8 len); |
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_change_recv(struct sock *sk, u8 type, u8 feature, | 119 | extern int dccp_feat_parse_options(struct sock *, struct dccp_request_sock *, |
120 | 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, | 121 | extern int dccp_feat_confirm_recv(struct sock *sk, u8 type, u8 feature, |
122 | u8 *val, u8 len); | 122 | u8 *val, u8 len); |
123 | extern void dccp_feat_clean(struct dccp_minisock *dmsk); | 123 | extern void dccp_feat_clean(struct dccp_minisock *dmsk); |
diff --git a/net/dccp/options.c b/net/dccp/options.c index 86c7a20d39b2..2c444c199725 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; |