diff options
-rw-r--r-- | net/dccp/ccids/ccid2.c | 6 | ||||
-rw-r--r-- | net/dccp/feat.c | 192 | ||||
-rw-r--r-- | net/dccp/feat.h | 25 | ||||
-rw-r--r-- | net/dccp/proto.c | 2 |
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 | ||
95 | static 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 */ |
96 | static int dccp_feat_clone_sp_val(dccp_feat_val *fval, u8 const *val, u8 len) | 108 | static 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 | */ | ||
177 | static 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 | */ | ||
208 | static 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 | |||
158 | static inline void dccp_feat_list_pop(struct dccp_feat_entry *entry) | 227 | static 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 | ||
262 | static 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 */ | ||
274 | static 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 | |||
293 | static 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 | */ | ||
311 | static 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 | */ | ||
336 | static 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 | |||
193 | int dccp_feat_change(struct dccp_minisock *dmsk, u8 type, u8 feature, | 351 | int 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 | ||
727 | EXPORT_SYMBOL_GPL(dccp_feat_clone); | 885 | EXPORT_SYMBOL_GPL(dccp_feat_clone); |
728 | 886 | ||
729 | static int __dccp_feat_init(struct dccp_minisock *dmsk, u8 type, u8 feat, | 887 | int 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 | |||
743 | int 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); |
765 | out: | 911 | out: |
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 | |||
17 | enum dccp_feat_type { | 26 | enum 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 | */ | ||
94 | struct 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 |
79 | extern const char *dccp_feat_typename(const u8 type); | 102 | extern const char *dccp_feat_typename(const u8 type); |
80 | extern const char *dccp_feat_name(const u8 feat); | 103 | extern 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, | |||
97 | extern void dccp_feat_clean(struct dccp_minisock *dmsk); | 120 | extern void dccp_feat_clean(struct dccp_minisock *dmsk); |
98 | extern int dccp_feat_clone(struct sock *oldsk, struct sock *newsk); | 121 | extern int dccp_feat_clone(struct sock *oldsk, struct sock *newsk); |
99 | extern int dccp_feat_clone_list(struct list_head const *, struct list_head *); | 122 | extern int dccp_feat_clone_list(struct list_head const *, struct list_head *); |
100 | extern int dccp_feat_init(struct dccp_minisock *dmsk); | 123 | extern 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; |