aboutsummaryrefslogtreecommitdiffstats
path: root/net/dccp
diff options
context:
space:
mode:
authorGerrit Renker <gerrit@erg.abdn.ac.uk>2008-09-04 01:30:19 -0400
committerGerrit Renker <gerrit@erg.abdn.ac.uk>2008-09-04 01:45:29 -0400
commit5a146b97d5e93db2df075c0d820f492bb996d0e3 (patch)
treeb21d2576cdaeffe14f28cbbce5b75b26cbf0ef91 /net/dccp
parentc664d4f4e2963ee355b1b0e77461eb844d1b288d (diff)
dccp: Process incoming Change feature-negotiation options
This adds/replaces code for processing incoming ChangeL/R options. The main difference is that: * mandatory FN options are now interpreted inside the function (there are too many individual cases to do this externally); * the function returns an appropriate Reset code or 0, which is then used to fill in the data for the Reset packet. Old code, which is no longer used or referenced, has been removed. Signed-off-by: Gerrit Renker <gerrit@erg.abdn.ac.uk>
Diffstat (limited to 'net/dccp')
-rw-r--r--net/dccp/feat.c146
-rw-r--r--net/dccp/feat.h4
-rw-r--r--net/dccp/options.c23
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
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);
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)
115extern int dccp_feat_register_sp(struct sock *sk, u8 feat, u8 is_local, 115extern 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);
117extern int dccp_feat_register_nn(struct sock *sk, u8 feat, u64 val); 117extern int dccp_feat_register_nn(struct sock *sk, u8 feat, u64 val);
118extern int dccp_feat_change_recv(struct sock *sk, u8 type, u8 feature, 118extern 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);
120extern int dccp_feat_confirm_recv(struct sock *sk, u8 type, u8 feature, 120extern int dccp_feat_confirm_recv(struct sock *sk, u8 type, u8 feature,
121 u8 *val, u8 len); 121 u8 *val, u8 len);
122extern void dccp_feat_clean(struct dccp_minisock *dmsk); 122extern 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
293out_invalid_option: 284out_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); 287out_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;