aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorGerrit Renker <gerrit@erg.abdn.ac.uk>2008-12-02 02:32:35 -0500
committerDavid S. Miller <davem@davemloft.net>2008-12-02 02:32:35 -0500
commite77b8363b2ea7c0d89919547c1a8b0562f298b57 (patch)
tree9fdef240be3aeb49c012fd00d41f9ee90afe776c /net
parent75757a7d0c54f8fdd414c74a6005d275032b0115 (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> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r--net/dccp/feat.c180
-rw-r--r--net/dccp/feat.h4
-rw-r--r--net/dccp/options.c23
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 */
248static 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
269static 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
240static inline void dccp_feat_list_pop(struct dccp_feat_entry *entry) 274static 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
960static void dccp_feat_empty_confirm(struct dccp_minisock *dmsk, 993static 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
1068EXPORT_SYMBOL_GPL(dccp_feat_change_recv); 1101EXPORT_SYMBOL_GPL(dccp_feat_change_recv);
1102#endif /* (later) */
1069 1103
1070int dccp_feat_confirm_recv(struct sock *sk, u8 type, u8 feature, 1104int 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
1207EXPORT_SYMBOL_GPL(dccp_feat_clone); 1241EXPORT_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 */
1253static 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
1343unknown_feature_or_value:
1344 if (!is_mandatory)
1345 return dccp_push_empty_confirm(fn, feat, local);
1346
1347not_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 */
1363int 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
1209int dccp_feat_init(struct sock *sk) 1387int 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)
116extern int dccp_feat_register_sp(struct sock *sk, u8 feat, u8 is_local, 116extern 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);
118extern int dccp_feat_register_nn(struct sock *sk, u8 feat, u64 val); 118extern int dccp_feat_register_nn(struct sock *sk, u8 feat, u64 val);
119extern int dccp_feat_change_recv(struct sock *sk, u8 type, u8 feature, 119extern 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);
121extern int dccp_feat_confirm_recv(struct sock *sk, u8 type, u8 feature, 121extern int dccp_feat_confirm_recv(struct sock *sk, u8 type, u8 feature,
122 u8 *val, u8 len); 122 u8 *val, u8 len);
123extern void dccp_feat_clean(struct dccp_minisock *dmsk); 123extern 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
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;