aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--net/dccp/ccids/ccid2.c6
-rw-r--r--net/dccp/feat.c192
-rw-r--r--net/dccp/feat.h25
-rw-r--r--net/dccp/proto.c2
4 files changed, 197 insertions, 28 deletions
diff --git a/net/dccp/ccids/ccid2.c b/net/dccp/ccids/ccid2.c
index 9a430734530c..c9ea19a4d85e 100644
--- a/net/dccp/ccids/ccid2.c
+++ b/net/dccp/ccids/ccid2.c
@@ -25,7 +25,7 @@
25/* 25/*
26 * This implementation should follow RFC 4341 26 * This implementation should follow RFC 4341
27 */ 27 */
28 28#include "../feat.h"
29#include "../ccid.h" 29#include "../ccid.h"
30#include "../dccp.h" 30#include "../dccp.h"
31#include "ccid2.h" 31#include "ccid2.h"
@@ -147,8 +147,8 @@ static void ccid2_change_l_ack_ratio(struct sock *sk, u32 val)
147 DCCP_WARN("Limiting Ack Ratio (%u) to %u\n", val, max_ratio); 147 DCCP_WARN("Limiting Ack Ratio (%u) to %u\n", val, max_ratio);
148 val = max_ratio; 148 val = max_ratio;
149 } 149 }
150 if (val > 0xFFFF) /* RFC 4340, 11.3 */ 150 if (val > DCCPF_ACK_RATIO_MAX)
151 val = 0xFFFF; 151 val = DCCPF_ACK_RATIO_MAX;
152 152
153 if (val == dp->dccps_l_ack_ratio) 153 if (val == dp->dccps_l_ack_ratio)
154 return; 154 return;
diff --git a/net/dccp/feat.c b/net/dccp/feat.c
index 0e789e2e0aa9..192d494a3816 100644
--- a/net/dccp/feat.c
+++ b/net/dccp/feat.c
@@ -92,6 +92,18 @@ static u8 dccp_feat_type(u8 feat_num)
92 return dccp_feat_table[idx].reconciliation; 92 return dccp_feat_table[idx].reconciliation;
93} 93}
94 94
95static int dccp_feat_default_value(u8 feat_num)
96{
97 int idx = dccp_feat_index(feat_num);
98 /*
99 * There are no default values for unknown features, so encountering a
100 * negative index here indicates a serious problem somewhere else.
101 */
102 DCCP_BUG_ON(idx < 0);
103
104 return idx < 0 ? 0 : dccp_feat_table[idx].default_value;
105}
106
95/* copy constructor, fval must not already contain allocated memory */ 107/* copy constructor, fval must not already contain allocated memory */
96static int dccp_feat_clone_sp_val(dccp_feat_val *fval, u8 const *val, u8 len) 108static int dccp_feat_clone_sp_val(dccp_feat_val *fval, u8 const *val, u8 len)
97{ 109{
@@ -155,6 +167,63 @@ static void dccp_feat_entry_destructor(struct dccp_feat_entry *entry)
155 * - list is sorted in increasing order of feature number (faster lookup) 167 * - list is sorted in increasing order of feature number (faster lookup)
156 */ 168 */
157 169
170/**
171 * dccp_feat_entry_new - Central list update routine (called by all others)
172 * @head: list to add to
173 * @feat: feature number
174 * @local: whether the local (1) or remote feature with number @feat is meant
175 * This is the only constructor and serves to ensure the above invariants.
176 */
177static struct dccp_feat_entry *
178 dccp_feat_entry_new(struct list_head *head, u8 feat, bool local)
179{
180 struct dccp_feat_entry *entry;
181
182 list_for_each_entry(entry, head, node)
183 if (entry->feat_num == feat && entry->is_local == local) {
184 dccp_feat_val_destructor(entry->feat_num, &entry->val);
185 return entry;
186 } else if (entry->feat_num > feat) {
187 head = &entry->node;
188 break;
189 }
190
191 entry = kmalloc(sizeof(*entry), gfp_any());
192 if (entry != NULL) {
193 entry->feat_num = feat;
194 entry->is_local = local;
195 list_add_tail(&entry->node, head);
196 }
197 return entry;
198}
199
200/**
201 * dccp_feat_push_change - Add/overwrite a Change option in the list
202 * @fn_list: feature-negotiation list to update
203 * @feat: one of %dccp_feature_numbers
204 * @local: whether local (1) or remote (0) @feat_num is meant
205 * @needs_mandatory: whether to use Mandatory feature negotiation options
206 * @fval: pointer to NN/SP value to be inserted (will be copied)
207 */
208static int dccp_feat_push_change(struct list_head *fn_list, u8 feat, u8 local,
209 u8 mandatory, dccp_feat_val *fval)
210{
211 struct dccp_feat_entry *new = dccp_feat_entry_new(fn_list, feat, local);
212
213 if (new == NULL)
214 return -ENOMEM;
215
216 new->feat_num = feat;
217 new->is_local = local;
218 new->state = FEAT_INITIALISING;
219 new->needs_confirm = 0;
220 new->empty_confirm = 0;
221 new->val = *fval;
222 new->needs_mandatory = mandatory;
223
224 return 0;
225}
226
158static inline void dccp_feat_list_pop(struct dccp_feat_entry *entry) 227static inline void dccp_feat_list_pop(struct dccp_feat_entry *entry)
159{ 228{
160 list_del(&entry->node); 229 list_del(&entry->node);
@@ -190,6 +259,95 @@ cloning_failed:
190 return -ENOMEM; 259 return -ENOMEM;
191} 260}
192 261
262static u8 dccp_feat_is_valid_nn_val(u8 feat_num, u64 val)
263{
264 switch (feat_num) {
265 case DCCPF_ACK_RATIO:
266 return val <= DCCPF_ACK_RATIO_MAX;
267 case DCCPF_SEQUENCE_WINDOW:
268 return val >= DCCPF_SEQ_WMIN && val <= DCCPF_SEQ_WMAX;
269 }
270 return 0; /* feature unknown - so we can't tell */
271}
272
273/* check that SP values are within the ranges defined in RFC 4340 */
274static u8 dccp_feat_is_valid_sp_val(u8 feat_num, u8 val)
275{
276 switch (feat_num) {
277 case DCCPF_CCID:
278 return val == DCCPC_CCID2 || val == DCCPC_CCID3;
279 /* Type-check Boolean feature values: */
280 case DCCPF_SHORT_SEQNOS:
281 case DCCPF_ECN_INCAPABLE:
282 case DCCPF_SEND_ACK_VECTOR:
283 case DCCPF_SEND_NDP_COUNT:
284 case DCCPF_DATA_CHECKSUM:
285 case DCCPF_SEND_LEV_RATE:
286 return val < 2;
287 case DCCPF_MIN_CSUM_COVER:
288 return val < 16;
289 }
290 return 0; /* feature unknown */
291}
292
293static u8 dccp_feat_sp_list_ok(u8 feat_num, u8 const *sp_list, u8 sp_len)
294{
295 if (sp_list == NULL || sp_len < 1)
296 return 0;
297 while (sp_len--)
298 if (!dccp_feat_is_valid_sp_val(feat_num, *sp_list++))
299 return 0;
300 return 1;
301}
302
303/**
304 * __feat_register_nn - Register new NN value on socket
305 * @fn: feature-negotiation list to register with
306 * @feat: an NN feature from %dccp_feature_numbers
307 * @mandatory: use Mandatory option if 1
308 * @nn_val: value to register (restricted to 4 bytes)
309 * Note that NN features are local by definition (RFC 4340, 6.3.2).
310 */
311static int __feat_register_nn(struct list_head *fn, u8 feat,
312 u8 mandatory, u64 nn_val)
313{
314 dccp_feat_val fval = { .nn = nn_val };
315
316 if (dccp_feat_type(feat) != FEAT_NN ||
317 !dccp_feat_is_valid_nn_val(feat, nn_val))
318 return -EINVAL;
319
320 /* Don't bother with default values, they will be activated anyway. */
321 if (nn_val - (u64)dccp_feat_default_value(feat) == 0)
322 return 0;
323
324 return dccp_feat_push_change(fn, feat, 1, mandatory, &fval);
325}
326
327/**
328 * __feat_register_sp - Register new SP value/list on socket
329 * @fn: feature-negotiation list to register with
330 * @feat: an SP feature from %dccp_feature_numbers
331 * @is_local: whether the local (1) or the remote (0) @feat is meant
332 * @mandatory: use Mandatory option if 1
333 * @sp_val: SP value followed by optional preference list
334 * @sp_len: length of @sp_val in bytes
335 */
336static int __feat_register_sp(struct list_head *fn, u8 feat, u8 is_local,
337 u8 mandatory, u8 const *sp_val, u8 sp_len)
338{
339 dccp_feat_val fval;
340
341 if (dccp_feat_type(feat) != FEAT_SP ||
342 !dccp_feat_sp_list_ok(feat, sp_val, sp_len))
343 return -EINVAL;
344
345 if (dccp_feat_clone_sp_val(&fval, sp_val, sp_len))
346 return -ENOMEM;
347
348 return dccp_feat_push_change(fn, feat, is_local, mandatory, &fval);
349}
350
193int dccp_feat_change(struct dccp_minisock *dmsk, u8 type, u8 feature, 351int dccp_feat_change(struct dccp_minisock *dmsk, u8 type, u8 feature,
194 u8 *val, u8 len, gfp_t gfp) 352 u8 *val, u8 len, gfp_t gfp)
195{ 353{
@@ -726,42 +884,30 @@ out_clean:
726 884
727EXPORT_SYMBOL_GPL(dccp_feat_clone); 885EXPORT_SYMBOL_GPL(dccp_feat_clone);
728 886
729static int __dccp_feat_init(struct dccp_minisock *dmsk, u8 type, u8 feat, 887int dccp_feat_init(struct sock *sk)
730 u8 *val, u8 len)
731{
732 int rc = -ENOMEM;
733 u8 *copy = kmemdup(val, len, GFP_KERNEL);
734
735 if (copy != NULL) {
736 rc = dccp_feat_change(dmsk, type, feat, copy, len, GFP_KERNEL);
737 if (rc)
738 kfree(copy);
739 }
740 return rc;
741}
742
743int dccp_feat_init(struct dccp_minisock *dmsk)
744{ 888{
889 struct dccp_sock *dp = dccp_sk(sk);
890 struct dccp_minisock *dmsk = dccp_msk(sk);
745 int rc; 891 int rc;
746 892
747 INIT_LIST_HEAD(&dmsk->dccpms_pending); 893 INIT_LIST_HEAD(&dmsk->dccpms_pending); /* XXX no longer used */
748 INIT_LIST_HEAD(&dmsk->dccpms_conf); 894 INIT_LIST_HEAD(&dmsk->dccpms_conf); /* XXX no longer used */
749 895
750 /* CCID L */ 896 /* CCID L */
751 rc = __dccp_feat_init(dmsk, DCCPO_CHANGE_L, DCCPF_CCID, 897 rc = __feat_register_sp(&dp->dccps_featneg, DCCPF_CCID, 1, 0,
752 &dmsk->dccpms_tx_ccid, 1); 898 &dmsk->dccpms_tx_ccid, 1);
753 if (rc) 899 if (rc)
754 goto out; 900 goto out;
755 901
756 /* CCID R */ 902 /* CCID R */
757 rc = __dccp_feat_init(dmsk, DCCPO_CHANGE_R, DCCPF_CCID, 903 rc = __feat_register_sp(&dp->dccps_featneg, DCCPF_CCID, 0, 0,
758 &dmsk->dccpms_rx_ccid, 1); 904 &dmsk->dccpms_rx_ccid, 1);
759 if (rc) 905 if (rc)
760 goto out; 906 goto out;
761 907
762 /* Ack ratio */ 908 /* Ack ratio */
763 rc = __dccp_feat_init(dmsk, DCCPO_CHANGE_L, DCCPF_ACK_RATIO, 909 rc = __feat_register_nn(&dp->dccps_featneg, DCCPF_ACK_RATIO, 0,
764 &dmsk->dccpms_ack_ratio, 1); 910 dmsk->dccpms_ack_ratio);
765out: 911out:
766 return rc; 912 return rc;
767} 913}
diff --git a/net/dccp/feat.h b/net/dccp/feat.h
index f5e99b39712a..232c653e69c5 100644
--- a/net/dccp/feat.h
+++ b/net/dccp/feat.h
@@ -14,6 +14,15 @@
14#include <linux/types.h> 14#include <linux/types.h>
15#include "dccp.h" 15#include "dccp.h"
16 16
17/*
18 * Known limit values
19 */
20/* Ack Ratio takes 2-byte integer values (11.3) */
21#define DCCPF_ACK_RATIO_MAX 0xFFFF
22/* Wmin=32 and Wmax=2^46-1 from 7.5.2 */
23#define DCCPF_SEQ_WMIN 32
24#define DCCPF_SEQ_WMAX 0x3FFFFFFFFFFFull
25
17enum dccp_feat_type { 26enum dccp_feat_type {
18 FEAT_AT_RX = 1, /* located at RX side of half-connection */ 27 FEAT_AT_RX = 1, /* located at RX side of half-connection */
19 FEAT_AT_TX = 2, /* located at TX side of half-connection */ 28 FEAT_AT_TX = 2, /* located at TX side of half-connection */
@@ -75,6 +84,20 @@ static inline u8 dccp_feat_genopt(struct dccp_feat_entry *entry)
75 return entry->is_local ? DCCPO_CHANGE_L : DCCPO_CHANGE_R; 84 return entry->is_local ? DCCPO_CHANGE_L : DCCPO_CHANGE_R;
76} 85}
77 86
87/**
88 * struct ccid_dependency - Track changes resulting from choosing a CCID
89 * @dependent_feat: one of %dccp_feature_numbers
90 * @is_local: local (1) or remote (0) @dependent_feat
91 * @is_mandatory: whether presence of @dependent_feat is mission-critical or not
92 * @val: corresponding default value for @dependent_feat (u8 is sufficient here)
93 */
94struct ccid_dependency {
95 u8 dependent_feat;
96 bool is_local:1,
97 is_mandatory:1;
98 u8 val;
99};
100
78#ifdef CONFIG_IP_DCCP_DEBUG 101#ifdef CONFIG_IP_DCCP_DEBUG
79extern const char *dccp_feat_typename(const u8 type); 102extern const char *dccp_feat_typename(const u8 type);
80extern const char *dccp_feat_name(const u8 feat); 103extern const char *dccp_feat_name(const u8 feat);
@@ -97,6 +120,6 @@ extern int dccp_feat_confirm_recv(struct sock *sk, u8 type, u8 feature,
97extern void dccp_feat_clean(struct dccp_minisock *dmsk); 120extern void dccp_feat_clean(struct dccp_minisock *dmsk);
98extern int dccp_feat_clone(struct sock *oldsk, struct sock *newsk); 121extern int dccp_feat_clone(struct sock *oldsk, struct sock *newsk);
99extern int dccp_feat_clone_list(struct list_head const *, struct list_head *); 122extern int dccp_feat_clone_list(struct list_head const *, struct list_head *);
100extern int dccp_feat_init(struct dccp_minisock *dmsk); 123extern int dccp_feat_init(struct sock *sk);
101 124
102#endif /* _DCCP_FEAT_H */ 125#endif /* _DCCP_FEAT_H */
diff --git a/net/dccp/proto.c b/net/dccp/proto.c
index dafcefd86594..01332fe7a99a 100644
--- a/net/dccp/proto.c
+++ b/net/dccp/proto.c
@@ -202,7 +202,7 @@ int dccp_init_sock(struct sock *sk, const __u8 ctl_sock_initialized)
202 * setsockopt(CCIDs-I-want/accept). -acme 202 * setsockopt(CCIDs-I-want/accept). -acme
203 */ 203 */
204 if (likely(ctl_sock_initialized)) { 204 if (likely(ctl_sock_initialized)) {
205 int rc = dccp_feat_init(dmsk); 205 int rc = dccp_feat_init(sk);
206 206
207 if (rc) 207 if (rc)
208 return rc; 208 return rc;