author | Florian Westphal <fw@strlen.de> |
Thu, 09 Apr 2009 12:07:21 +0200 | |
changeset 2 | d1f6d8b6f81c |
parent 0 | aa628870c1d3 |
permissions | -rw-r--r-- |
0
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
1 |
/* |
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
2 |
* net/dccp/feat.c |
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
3 |
* |
2 | 4 |
* Feature negotiation for the DCCP protocol (RFC 4340, section 6) |
5 |
* |
|
6 |
* Copyright (c) 2008 Gerrit Renker <gerrit@erg.abdn.ac.uk> |
|
7 |
* Rewrote from scratch, some bits from earlier code by |
|
8 |
* Copyright (c) 2005 Andrea Bittau <a.bittau@cs.ucl.ac.uk> |
|
9 |
* |
|
0
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
10 |
* |
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
11 |
* ASSUMPTIONS |
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
12 |
* ----------- |
2 | 13 |
* o Feature negotiation is coordinated with connection setup (as in TCP), wild |
14 |
* changes of parameters of an established connection are not supported. |
|
0
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
15 |
* o All currently known SP features have 1-byte quantities. If in the future |
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
16 |
* extensions of RFCs 4340..42 define features with item lengths larger than |
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
17 |
* one byte, a feature-specific extension of the code will be required. |
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
18 |
* |
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
19 |
* This program is free software; you can redistribute it and/or |
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
20 |
* modify it under the terms of the GNU General Public License |
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
21 |
* as published by the Free Software Foundation; either version |
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
22 |
* 2 of the License, or (at your option) any later version. |
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
23 |
*/ |
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
24 |
#include <linux/module.h> |
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
25 |
#include "ccid.h" |
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
26 |
#include "feat.h" |
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
27 |
|
2 | 28 |
/* |
29 |
* Feature activation handlers. |
|
30 |
* |
|
31 |
* These all use an u64 argument, to provide enough room for NN/SP features. At |
|
32 |
* this stage the negotiated values have been checked to be within their range. |
|
33 |
*/ |
|
34 |
static int dccp_hdlr_ccid(struct sock *sk, u64 ccid, bool rx) |
|
0
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
35 |
{ |
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
36 |
struct dccp_sock *dp = dccp_sk(sk); |
2 | 37 |
struct ccid *new_ccid = ccid_new(ccid, sk, rx); |
0
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
38 |
|
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
39 |
if (new_ccid == NULL) |
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
40 |
return -ENOMEM; |
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
41 |
|
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
42 |
if (rx) { |
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
43 |
ccid_hc_rx_delete(dp->dccps_hc_rx_ccid, sk); |
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
44 |
dp->dccps_hc_rx_ccid = new_ccid; |
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
45 |
} else { |
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
46 |
ccid_hc_tx_delete(dp->dccps_hc_tx_ccid, sk); |
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
47 |
dp->dccps_hc_tx_ccid = new_ccid; |
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
48 |
} |
2 | 49 |
return 0; |
50 |
} |
|
0
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
51 |
|
2 | 52 |
static int dccp_hdlr_seq_win(struct sock *sk, u64 seq_win, bool rx) |
53 |
{ |
|
54 |
if (!rx) |
|
55 |
dccp_msk(sk)->dccpms_sequence_window = seq_win; |
|
56 |
return 0; |
|
57 |
} |
|
58 |
||
59 |
static int dccp_hdlr_ack_ratio(struct sock *sk, u64 ratio, bool rx) |
|
60 |
{ |
|
61 |
if (rx) |
|
62 |
dccp_sk(sk)->dccps_r_ack_ratio = ratio; |
|
63 |
else |
|
64 |
dccp_sk(sk)->dccps_l_ack_ratio = ratio; |
|
0
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
65 |
return 0; |
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
66 |
} |
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
67 |
|
2 | 68 |
static int dccp_hdlr_ackvec(struct sock *sk, u64 enable, bool rx) |
0
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
69 |
{ |
2 | 70 |
struct dccp_sock *dp = dccp_sk(sk); |
71 |
||
72 |
if (rx) { |
|
73 |
if (enable && dp->dccps_hc_rx_ackvec == NULL) { |
|
74 |
dp->dccps_hc_rx_ackvec = dccp_ackvec_alloc(gfp_any()); |
|
75 |
if (dp->dccps_hc_rx_ackvec == NULL) |
|
76 |
return -ENOMEM; |
|
77 |
} else if (!enable) { |
|
78 |
dccp_ackvec_free(dp->dccps_hc_rx_ackvec); |
|
79 |
dp->dccps_hc_rx_ackvec = NULL; |
|
80 |
} |
|
81 |
} |
|
82 |
return 0; |
|
83 |
} |
|
0
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
84 |
|
2 | 85 |
static int dccp_hdlr_ndp(struct sock *sk, u64 enable, bool rx) |
86 |
{ |
|
87 |
if (!rx) |
|
88 |
dccp_sk(sk)->dccps_send_ndp_count = (enable > 0); |
|
89 |
return 0; |
|
90 |
} |
|
91 |
||
92 |
/* |
|
93 |
* Minimum Checksum Coverage is located at the RX side (9.2.1). This means that |
|
94 |
* `rx' holds when the sending peer informs about his partial coverage via a |
|
95 |
* ChangeR() option. In the other case, we are the sender and the receiver |
|
96 |
* announces its coverage via ChangeL() options. The policy here is to honour |
|
97 |
* such communication by enabling the corresponding partial coverage - but only |
|
98 |
* if it has not been set manually before; the warning here means that all |
|
99 |
* packets will be dropped. |
|
100 |
*/ |
|
101 |
static int dccp_hdlr_min_cscov(struct sock *sk, u64 cscov, bool rx) |
|
102 |
{ |
|
103 |
struct dccp_sock *dp = dccp_sk(sk); |
|
104 |
||
105 |
if (rx) |
|
106 |
dp->dccps_pcrlen = cscov; |
|
107 |
else { |
|
108 |
if (dp->dccps_pcslen == 0) |
|
109 |
dp->dccps_pcslen = cscov; |
|
110 |
else if (cscov > dp->dccps_pcslen) |
|
111 |
DCCP_WARN("CsCov %u too small, peer requires >= %u\n", |
|
112 |
dp->dccps_pcslen, (u8)cscov); |
|
0
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
113 |
} |
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
114 |
return 0; |
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
115 |
} |
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
116 |
|
2 | 117 |
static const struct { |
118 |
u8 feat_num; /* DCCPF_xxx */ |
|
119 |
enum dccp_feat_type rxtx; /* RX or TX */ |
|
120 |
enum dccp_feat_type reconciliation; /* SP or NN */ |
|
121 |
u8 default_value; /* as in 6.4 */ |
|
122 |
int (*activation_hdlr)(struct sock *sk, u64 val, bool rx); |
|
123 |
/* |
|
124 |
* Lookup table for location and type of features (from RFC 4340/4342) |
|
125 |
* +--------------------------+----+-----+----+----+---------+-----------+ |
|
126 |
* | Feature | Location | Reconc. | Initial | Section | |
|
127 |
* | | RX | TX | SP | NN | Value | Reference | |
|
128 |
* +--------------------------+----+-----+----+----+---------+-----------+ |
|
129 |
* | DCCPF_CCID | | X | X | | 2 | 10 | |
|
130 |
* | DCCPF_SHORT_SEQNOS | | X | X | | 0 | 7.6.1 | |
|
131 |
* | DCCPF_SEQUENCE_WINDOW | | X | | X | 100 | 7.5.2 | |
|
132 |
* | DCCPF_ECN_INCAPABLE | X | | X | | 0 | 12.1 | |
|
133 |
* | DCCPF_ACK_RATIO | | X | | X | 2 | 11.3 | |
|
134 |
* | DCCPF_SEND_ACK_VECTOR | X | | X | | 0 | 11.5 | |
|
135 |
* | DCCPF_SEND_NDP_COUNT | | X | X | | 0 | 7.7.2 | |
|
136 |
* | DCCPF_MIN_CSUM_COVER | X | | X | | 0 | 9.2.1 | |
|
137 |
* | DCCPF_DATA_CHECKSUM | X | | X | | 0 | 9.3.1 | |
|
138 |
* | DCCPF_SEND_LEV_RATE | X | | X | | 0 | 4342/8.4 | |
|
139 |
* +--------------------------+----+-----+----+----+---------+-----------+ |
|
140 |
*/ |
|
141 |
} dccp_feat_table[] = { |
|
142 |
{ DCCPF_CCID, FEAT_AT_TX, FEAT_SP, 2, dccp_hdlr_ccid }, |
|
143 |
{ DCCPF_SHORT_SEQNOS, FEAT_AT_TX, FEAT_SP, 0, NULL }, |
|
144 |
{ DCCPF_SEQUENCE_WINDOW, FEAT_AT_TX, FEAT_NN, 100, dccp_hdlr_seq_win }, |
|
145 |
{ DCCPF_ECN_INCAPABLE, FEAT_AT_RX, FEAT_SP, 0, NULL }, |
|
146 |
{ DCCPF_ACK_RATIO, FEAT_AT_TX, FEAT_NN, 2, dccp_hdlr_ack_ratio}, |
|
147 |
{ DCCPF_SEND_ACK_VECTOR, FEAT_AT_RX, FEAT_SP, 0, dccp_hdlr_ackvec }, |
|
148 |
{ DCCPF_SEND_NDP_COUNT, FEAT_AT_TX, FEAT_SP, 0, dccp_hdlr_ndp }, |
|
149 |
{ DCCPF_MIN_CSUM_COVER, FEAT_AT_RX, FEAT_SP, 0, dccp_hdlr_min_cscov}, |
|
150 |
{ DCCPF_DATA_CHECKSUM, FEAT_AT_RX, FEAT_SP, 0, NULL }, |
|
151 |
{ DCCPF_SEND_LEV_RATE, FEAT_AT_RX, FEAT_SP, 0, NULL }, |
|
152 |
}; |
|
153 |
#define DCCP_FEAT_SUPPORTED_MAX ARRAY_SIZE(dccp_feat_table) |
|
0
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
154 |
|
2 | 155 |
/** |
156 |
* dccp_feat_index - Hash function to map feature number into array position |
|
157 |
* Returns consecutive array index or -1 if the feature is not understood. |
|
158 |
*/ |
|
159 |
static int dccp_feat_index(u8 feat_num) |
|
160 |
{ |
|
161 |
/* The first 9 entries are occupied by the types from RFC 4340, 6.4 */ |
|
162 |
if (feat_num > DCCPF_RESERVED && feat_num <= DCCPF_DATA_CHECKSUM) |
|
163 |
return feat_num - 1; |
|
164 |
||
0
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
165 |
/* |
2 | 166 |
* Other features: add cases for new feature types here after adding |
167 |
* them to the above table. |
|
0
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
168 |
*/ |
2 | 169 |
switch (feat_num) { |
170 |
case DCCPF_SEND_LEV_RATE: |
|
171 |
return DCCP_FEAT_SUPPORTED_MAX - 1; |
|
0
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
172 |
} |
2 | 173 |
return -1; |
174 |
} |
|
0
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
175 |
|
2 | 176 |
static u8 dccp_feat_type(u8 feat_num) |
177 |
{ |
|
178 |
int idx = dccp_feat_index(feat_num); |
|
179 |
||
180 |
if (idx < 0) |
|
181 |
return FEAT_UNKNOWN; |
|
182 |
return dccp_feat_table[idx].reconciliation; |
|
183 |
} |
|
0
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
184 |
|
2 | 185 |
static int dccp_feat_default_value(u8 feat_num) |
186 |
{ |
|
187 |
int idx = dccp_feat_index(feat_num); |
|
188 |
/* |
|
189 |
* There are no default values for unknown features, so encountering a |
|
190 |
* negative index here indicates a serious problem somewhere else. |
|
191 |
*/ |
|
192 |
DCCP_BUG_ON(idx < 0); |
|
193 |
||
194 |
return idx < 0 ? 0 : dccp_feat_table[idx].default_value; |
|
195 |
} |
|
0
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
196 |
|
2 | 197 |
static int __dccp_feat_activate(struct sock *sk, const int idx, |
198 |
const bool is_local, dccp_feat_val const *fval) |
|
199 |
{ |
|
200 |
bool rx; |
|
201 |
u64 val; |
|
0
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
202 |
|
2 | 203 |
if (idx < 0 || idx >= DCCP_FEAT_SUPPORTED_MAX) |
204 |
return -1; |
|
205 |
if (dccp_feat_table[idx].activation_hdlr == NULL) |
|
206 |
return 0; |
|
0
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
207 |
|
2 | 208 |
if (fval == NULL) { |
209 |
val = dccp_feat_table[idx].default_value; |
|
210 |
} else if (dccp_feat_table[idx].reconciliation == FEAT_SP) { |
|
211 |
if (fval->sp.vec == NULL) { |
|
212 |
/* |
|
213 |
* This can happen when an empty Confirm is sent |
|
214 |
* for an SP (i.e. known) feature. In this case |
|
215 |
* we would be using the default anyway. |
|
216 |
*/ |
|
217 |
DCCP_CRIT("Feature #%d undefined: using default", idx); |
|
218 |
val = dccp_feat_table[idx].default_value; |
|
219 |
} else { |
|
220 |
val = fval->sp.vec[0]; |
|
0
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
221 |
} |
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
222 |
} else { |
2 | 223 |
val = fval->nn; |
0
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
224 |
} |
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
225 |
|
2 | 226 |
/* Location is RX if this is a local-RX or remote-TX feature */ |
227 |
rx = (is_local == (dccp_feat_table[idx].rxtx == FEAT_AT_RX)); |
|
228 |
||
229 |
return dccp_feat_table[idx].activation_hdlr(sk, val, rx); |
|
230 |
} |
|
231 |
||
232 |
/* Test for "Req'd" feature (RFC 4340, 6.4) */ |
|
233 |
static inline int dccp_feat_must_be_understood(u8 feat_num) |
|
234 |
{ |
|
235 |
return feat_num == DCCPF_CCID || feat_num == DCCPF_SHORT_SEQNOS || |
|
236 |
feat_num == DCCPF_SEQUENCE_WINDOW; |
|
237 |
} |
|
238 |
||
239 |
/* copy constructor, fval must not already contain allocated memory */ |
|
240 |
static int dccp_feat_clone_sp_val(dccp_feat_val *fval, u8 const *val, u8 len) |
|
241 |
{ |
|
242 |
fval->sp.len = len; |
|
243 |
if (fval->sp.len > 0) { |
|
244 |
fval->sp.vec = kmemdup(val, len, gfp_any()); |
|
245 |
if (fval->sp.vec == NULL) { |
|
246 |
fval->sp.len = 0; |
|
247 |
return -ENOBUFS; |
|
248 |
} |
|
249 |
} |
|
250 |
return 0; |
|
251 |
} |
|
0
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
252 |
|
2 | 253 |
static void dccp_feat_val_destructor(u8 feat_num, dccp_feat_val *val) |
254 |
{ |
|
255 |
if (unlikely(val == NULL)) |
|
256 |
return; |
|
257 |
if (dccp_feat_type(feat_num) == FEAT_SP) |
|
258 |
kfree(val->sp.vec); |
|
259 |
memset(val, 0, sizeof(*val)); |
|
260 |
} |
|
261 |
||
262 |
static struct dccp_feat_entry * |
|
263 |
dccp_feat_clone_entry(struct dccp_feat_entry const *original) |
|
264 |
{ |
|
265 |
struct dccp_feat_entry *new; |
|
266 |
u8 type = dccp_feat_type(original->feat_num); |
|
267 |
||
268 |
if (type == FEAT_UNKNOWN) |
|
269 |
return NULL; |
|
270 |
||
271 |
new = kmemdup(original, sizeof(struct dccp_feat_entry), gfp_any()); |
|
272 |
if (new == NULL) |
|
273 |
return NULL; |
|
274 |
||
275 |
if (type == FEAT_SP && dccp_feat_clone_sp_val(&new->val, |
|
276 |
original->val.sp.vec, |
|
277 |
original->val.sp.len)) { |
|
278 |
kfree(new); |
|
279 |
return NULL; |
|
0
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
280 |
} |
2 | 281 |
return new; |
282 |
} |
|
0
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
283 |
|
2 | 284 |
static void dccp_feat_entry_destructor(struct dccp_feat_entry *entry) |
285 |
{ |
|
286 |
if (entry != NULL) { |
|
287 |
dccp_feat_val_destructor(entry->feat_num, &entry->val); |
|
288 |
kfree(entry); |
|
289 |
} |
|
0
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
290 |
} |
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
291 |
|
2 | 292 |
/* |
293 |
* List management functions |
|
294 |
* |
|
295 |
* Feature negotiation lists rely on and maintain the following invariants: |
|
296 |
* - each feat_num in the list is known, i.e. we know its type and default value |
|
297 |
* - each feat_num/is_local combination is unique (old entries are overwritten) |
|
298 |
* - SP values are always freshly allocated |
|
299 |
* - list is sorted in increasing order of feature number (faster lookup) |
|
300 |
*/ |
|
301 |
static struct dccp_feat_entry *dccp_feat_list_lookup(struct list_head *fn_list, |
|
302 |
u8 feat_num, bool is_local) |
|
0
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
303 |
{ |
2 | 304 |
struct dccp_feat_entry *entry; |
0
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
305 |
|
2 | 306 |
list_for_each_entry(entry, fn_list, node) { |
307 |
if (entry->feat_num == feat_num && entry->is_local == is_local) |
|
308 |
return entry; |
|
309 |
else if (entry->feat_num > feat_num) |
|
310 |
break; |
|
311 |
} |
|
312 |
return NULL; |
|
313 |
} |
|
0
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
314 |
|
2 | 315 |
/** |
316 |
* dccp_feat_entry_new - Central list update routine (called by all others) |
|
317 |
* @head: list to add to |
|
318 |
* @feat: feature number |
|
319 |
* @local: whether the local (1) or remote feature with number @feat is meant |
|
320 |
* This is the only constructor and serves to ensure the above invariants. |
|
321 |
*/ |
|
322 |
static struct dccp_feat_entry * |
|
323 |
dccp_feat_entry_new(struct list_head *head, u8 feat, bool local) |
|
324 |
{ |
|
325 |
struct dccp_feat_entry *entry; |
|
0
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
326 |
|
2 | 327 |
list_for_each_entry(entry, head, node) |
328 |
if (entry->feat_num == feat && entry->is_local == local) { |
|
329 |
dccp_feat_val_destructor(entry->feat_num, &entry->val); |
|
330 |
return entry; |
|
331 |
} else if (entry->feat_num > feat) { |
|
332 |
head = &entry->node; |
|
333 |
break; |
|
334 |
} |
|
335 |
||
336 |
entry = kmalloc(sizeof(*entry), gfp_any()); |
|
337 |
if (entry != NULL) { |
|
338 |
entry->feat_num = feat; |
|
339 |
entry->is_local = local; |
|
340 |
list_add_tail(&entry->node, head); |
|
0
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
341 |
} |
2 | 342 |
return entry; |
0
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
343 |
} |
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
344 |
|
2 | 345 |
/** |
346 |
* dccp_feat_push_change - Add/overwrite a Change option in the list |
|
347 |
* @fn_list: feature-negotiation list to update |
|
348 |
* @feat: one of %dccp_feature_numbers |
|
349 |
* @local: whether local (1) or remote (0) @feat_num is meant |
|
350 |
* @needs_mandatory: whether to use Mandatory feature negotiation options |
|
351 |
* @fval: pointer to NN/SP value to be inserted (will be copied) |
|
352 |
*/ |
|
353 |
static int dccp_feat_push_change(struct list_head *fn_list, u8 feat, u8 local, |
|
354 |
u8 mandatory, dccp_feat_val *fval) |
|
0
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
355 |
{ |
2 | 356 |
struct dccp_feat_entry *new = dccp_feat_entry_new(fn_list, feat, local); |
0
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
357 |
|
2 | 358 |
if (new == NULL) |
0
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
359 |
return -ENOMEM; |
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
360 |
|
2 | 361 |
new->feat_num = feat; |
362 |
new->is_local = local; |
|
363 |
new->state = FEAT_INITIALISING; |
|
364 |
new->needs_confirm = 0; |
|
365 |
new->empty_confirm = 0; |
|
366 |
new->val = *fval; |
|
367 |
new->needs_mandatory = mandatory; |
|
0
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
368 |
|
2 | 369 |
return 0; |
370 |
} |
|
0
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
371 |
|
2 | 372 |
/** |
373 |
* dccp_feat_push_confirm - Add a Confirm entry to the FN list |
|
374 |
* @fn_list: feature-negotiation list to add to |
|
375 |
* @feat: one of %dccp_feature_numbers |
|
376 |
* @local: whether local (1) or remote (0) @feat_num is being confirmed |
|
377 |
* @fval: pointer to NN/SP value to be inserted or NULL |
|
378 |
* Returns 0 on success, a Reset code for further processing otherwise. |
|
379 |
*/ |
|
380 |
static int dccp_feat_push_confirm(struct list_head *fn_list, u8 feat, u8 local, |
|
381 |
dccp_feat_val *fval) |
|
382 |
{ |
|
383 |
struct dccp_feat_entry *new = dccp_feat_entry_new(fn_list, feat, local); |
|
0
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
384 |
|
2 | 385 |
if (new == NULL) |
386 |
return DCCP_RESET_CODE_TOO_BUSY; |
|
0
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
387 |
|
2 | 388 |
new->feat_num = feat; |
389 |
new->is_local = local; |
|
390 |
new->state = FEAT_STABLE; /* transition in 6.6.2 */ |
|
391 |
new->needs_confirm = 1; |
|
392 |
new->empty_confirm = (fval == NULL); |
|
393 |
new->val.nn = 0; /* zeroes the whole structure */ |
|
394 |
if (!new->empty_confirm) |
|
395 |
new->val = *fval; |
|
396 |
new->needs_mandatory = 0; |
|
0
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
397 |
|
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
398 |
return 0; |
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
399 |
} |
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
400 |
|
2 | 401 |
static int dccp_push_empty_confirm(struct list_head *fn_list, u8 feat, u8 local) |
0
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
402 |
{ |
2 | 403 |
return dccp_feat_push_confirm(fn_list, feat, local, NULL); |
404 |
} |
|
405 |
||
406 |
static inline void dccp_feat_list_pop(struct dccp_feat_entry *entry) |
|
407 |
{ |
|
408 |
list_del(&entry->node); |
|
409 |
dccp_feat_entry_destructor(entry); |
|
410 |
} |
|
0
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
411 |
|
2 | 412 |
void dccp_feat_list_purge(struct list_head *fn_list) |
413 |
{ |
|
414 |
struct dccp_feat_entry *entry, *next; |
|
415 |
||
416 |
list_for_each_entry_safe(entry, next, fn_list, node) |
|
417 |
dccp_feat_entry_destructor(entry); |
|
418 |
INIT_LIST_HEAD(fn_list); |
|
419 |
} |
|
420 |
EXPORT_SYMBOL_GPL(dccp_feat_list_purge); |
|
421 |
||
422 |
/* generate @to as full clone of @from - @to must not contain any nodes */ |
|
423 |
int dccp_feat_clone_list(struct list_head const *from, struct list_head *to) |
|
424 |
{ |
|
425 |
struct dccp_feat_entry *entry, *new; |
|
0
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
426 |
|
2 | 427 |
INIT_LIST_HEAD(to); |
428 |
list_for_each_entry(entry, from, node) { |
|
429 |
new = dccp_feat_clone_entry(entry); |
|
430 |
if (new == NULL) |
|
431 |
goto cloning_failed; |
|
432 |
list_add_tail(&new->node, to); |
|
0
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
433 |
} |
2 | 434 |
return 0; |
435 |
||
436 |
cloning_failed: |
|
437 |
dccp_feat_list_purge(to); |
|
438 |
return -ENOMEM; |
|
439 |
} |
|
0
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
440 |
|
2 | 441 |
/** |
442 |
* dccp_feat_valid_nn_length - Enforce length constraints on NN options |
|
443 |
* Length is between 0 and %DCCP_OPTVAL_MAXLEN. Used for outgoing packets only, |
|
444 |
* incoming options are accepted as long as their values are valid. |
|
445 |
*/ |
|
446 |
static u8 dccp_feat_valid_nn_length(u8 feat_num) |
|
447 |
{ |
|
448 |
if (feat_num == DCCPF_ACK_RATIO) /* RFC 4340, 11.3 and 6.6.8 */ |
|
449 |
return 2; |
|
450 |
if (feat_num == DCCPF_SEQUENCE_WINDOW) /* RFC 4340, 7.5.2 and 6.5 */ |
|
451 |
return 6; |
|
452 |
return 0; |
|
453 |
} |
|
0
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
454 |
|
2 | 455 |
static u8 dccp_feat_is_valid_nn_val(u8 feat_num, u64 val) |
456 |
{ |
|
457 |
switch (feat_num) { |
|
458 |
case DCCPF_ACK_RATIO: |
|
459 |
return val <= DCCPF_ACK_RATIO_MAX; |
|
460 |
case DCCPF_SEQUENCE_WINDOW: |
|
461 |
return val >= DCCPF_SEQ_WMIN && val <= DCCPF_SEQ_WMAX; |
|
462 |
} |
|
463 |
return 0; /* feature unknown - so we can't tell */ |
|
0
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
464 |
} |
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
465 |
|
2 | 466 |
/* check that SP values are within the ranges defined in RFC 4340 */ |
467 |
static u8 dccp_feat_is_valid_sp_val(u8 feat_num, u8 val) |
|
0
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
468 |
{ |
2 | 469 |
switch (feat_num) { |
470 |
case DCCPF_CCID: |
|
471 |
return val == DCCPC_CCID2 || val == DCCPC_CCID3; |
|
472 |
/* Type-check Boolean feature values: */ |
|
473 |
case DCCPF_SHORT_SEQNOS: |
|
474 |
case DCCPF_ECN_INCAPABLE: |
|
475 |
case DCCPF_SEND_ACK_VECTOR: |
|
476 |
case DCCPF_SEND_NDP_COUNT: |
|
477 |
case DCCPF_DATA_CHECKSUM: |
|
478 |
case DCCPF_SEND_LEV_RATE: |
|
479 |
return val < 2; |
|
480 |
case DCCPF_MIN_CSUM_COVER: |
|
481 |
return val < 16; |
|
482 |
} |
|
483 |
return 0; /* feature unknown */ |
|
484 |
} |
|
485 |
||
486 |
static u8 dccp_feat_sp_list_ok(u8 feat_num, u8 const *sp_list, u8 sp_len) |
|
487 |
{ |
|
488 |
if (sp_list == NULL || sp_len < 1) |
|
489 |
return 0; |
|
490 |
while (sp_len--) |
|
491 |
if (!dccp_feat_is_valid_sp_val(feat_num, *sp_list++)) |
|
492 |
return 0; |
|
493 |
return 1; |
|
494 |
} |
|
0
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
495 |
|
2 | 496 |
/** |
497 |
* dccp_feat_insert_opts - Generate FN options from current list state |
|
498 |
* @skb: next sk_buff to be sent to the peer |
|
499 |
* @dp: for client during handshake and general negotiation |
|
500 |
* @dreq: used by the server only (all Changes/Confirms in LISTEN/RESPOND) |
|
501 |
*/ |
|
502 |
int dccp_feat_insert_opts(struct dccp_sock *dp, struct dccp_request_sock *dreq, |
|
503 |
struct sk_buff *skb) |
|
504 |
{ |
|
505 |
struct list_head *fn = dreq ? &dreq->dreq_featneg : &dp->dccps_featneg; |
|
506 |
struct dccp_feat_entry *pos, *next; |
|
507 |
u8 opt, type, len, *ptr, nn_in_nbo[DCCP_OPTVAL_MAXLEN]; |
|
508 |
bool rpt; |
|
0
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
509 |
|
2 | 510 |
/* put entries into @skb in the order they appear in the list */ |
511 |
list_for_each_entry_safe_reverse(pos, next, fn, node) { |
|
512 |
opt = dccp_feat_genopt(pos); |
|
513 |
type = dccp_feat_type(pos->feat_num); |
|
514 |
rpt = false; |
|
515 |
||
516 |
if (pos->empty_confirm) { |
|
517 |
len = 0; |
|
518 |
ptr = NULL; |
|
519 |
} else { |
|
520 |
if (type == FEAT_SP) { |
|
521 |
len = pos->val.sp.len; |
|
522 |
ptr = pos->val.sp.vec; |
|
523 |
rpt = pos->needs_confirm; |
|
524 |
} else if (type == FEAT_NN) { |
|
525 |
len = dccp_feat_valid_nn_length(pos->feat_num); |
|
526 |
ptr = nn_in_nbo; |
|
527 |
dccp_encode_value_var(pos->val.nn, ptr, len); |
|
528 |
} else { |
|
529 |
DCCP_BUG("unknown feature %u", pos->feat_num); |
|
530 |
return -1; |
|
0
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
531 |
} |
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
532 |
} |
2 | 533 |
|
534 |
if (dccp_insert_fn_opt(skb, opt, pos->feat_num, ptr, len, rpt)) |
|
535 |
return -1; |
|
536 |
if (pos->needs_mandatory && dccp_insert_option_mandatory(skb)) |
|
537 |
return -1; |
|
538 |
/* |
|
539 |
* Enter CHANGING after transmitting the Change option (6.6.2). |
|
540 |
*/ |
|
541 |
if (pos->state == FEAT_INITIALISING) |
|
542 |
pos->state = FEAT_CHANGING; |
|
0
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
543 |
} |
2 | 544 |
return 0; |
545 |
} |
|
0
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
546 |
|
2 | 547 |
/** |
548 |
* __feat_register_nn - Register new NN value on socket |
|
549 |
* @fn: feature-negotiation list to register with |
|
550 |
* @feat: an NN feature from %dccp_feature_numbers |
|
551 |
* @mandatory: use Mandatory option if 1 |
|
552 |
* @nn_val: value to register (restricted to 4 bytes) |
|
553 |
* Note that NN features are local by definition (RFC 4340, 6.3.2). |
|
554 |
*/ |
|
555 |
static int __feat_register_nn(struct list_head *fn, u8 feat, |
|
556 |
u8 mandatory, u64 nn_val) |
|
557 |
{ |
|
558 |
dccp_feat_val fval = { .nn = nn_val }; |
|
559 |
||
560 |
if (dccp_feat_type(feat) != FEAT_NN || |
|
561 |
!dccp_feat_is_valid_nn_val(feat, nn_val)) |
|
562 |
return -EINVAL; |
|
563 |
||
564 |
/* Don't bother with default values, they will be activated anyway. */ |
|
565 |
if (nn_val - (u64)dccp_feat_default_value(feat) == 0) |
|
566 |
return 0; |
|
567 |
||
568 |
return dccp_feat_push_change(fn, feat, 1, mandatory, &fval); |
|
569 |
} |
|
0
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
570 |
|
2 | 571 |
/** |
572 |
* __feat_register_sp - Register new SP value/list on socket |
|
573 |
* @fn: feature-negotiation list to register with |
|
574 |
* @feat: an SP feature from %dccp_feature_numbers |
|
575 |
* @is_local: whether the local (1) or the remote (0) @feat is meant |
|
576 |
* @mandatory: use Mandatory option if 1 |
|
577 |
* @sp_val: SP value followed by optional preference list |
|
578 |
* @sp_len: length of @sp_val in bytes |
|
579 |
*/ |
|
580 |
static int __feat_register_sp(struct list_head *fn, u8 feat, u8 is_local, |
|
581 |
u8 mandatory, u8 const *sp_val, u8 sp_len) |
|
582 |
{ |
|
583 |
dccp_feat_val fval; |
|
584 |
||
585 |
if (dccp_feat_type(feat) != FEAT_SP || |
|
586 |
!dccp_feat_sp_list_ok(feat, sp_val, sp_len)) |
|
587 |
return -EINVAL; |
|
588 |
||
589 |
/* Avoid negotiating alien CCIDs by only advertising supported ones */ |
|
590 |
if (feat == DCCPF_CCID && !ccid_support_check(sp_val, sp_len)) |
|
591 |
return -EOPNOTSUPP; |
|
592 |
||
593 |
if (dccp_feat_clone_sp_val(&fval, sp_val, sp_len)) |
|
594 |
return -ENOMEM; |
|
595 |
||
596 |
return dccp_feat_push_change(fn, feat, is_local, mandatory, &fval); |
|
597 |
} |
|
598 |
||
599 |
/** |
|
600 |
* dccp_feat_register_sp - Register requests to change SP feature values |
|
601 |
* @sk: client or listening socket |
|
602 |
* @feat: one of %dccp_feature_numbers |
|
603 |
* @is_local: whether the local (1) or remote (0) @feat is meant |
|
604 |
* @list: array of preferred values, in descending order of preference |
|
605 |
* @len: length of @list in bytes |
|
606 |
*/ |
|
607 |
int dccp_feat_register_sp(struct sock *sk, u8 feat, u8 is_local, |
|
608 |
u8 const *list, u8 len) |
|
609 |
{ /* any changes must be registered before establishing the connection */ |
|
610 |
if (sk->sk_state != DCCP_CLOSED) |
|
611 |
return -EISCONN; |
|
612 |
if (dccp_feat_type(feat) != FEAT_SP) |
|
613 |
return -EINVAL; |
|
614 |
return __feat_register_sp(&dccp_sk(sk)->dccps_featneg, feat, is_local, |
|
615 |
0, list, len); |
|
616 |
} |
|
617 |
||
618 |
/* Analogous to dccp_feat_register_sp(), but for non-negotiable values */ |
|
619 |
int dccp_feat_register_nn(struct sock *sk, u8 feat, u64 val) |
|
620 |
{ |
|
621 |
/* any changes must be registered before establishing the connection */ |
|
622 |
if (sk->sk_state != DCCP_CLOSED) |
|
623 |
return -EISCONN; |
|
624 |
if (dccp_feat_type(feat) != FEAT_NN) |
|
625 |
return -EINVAL; |
|
626 |
return __feat_register_nn(&dccp_sk(sk)->dccps_featneg, feat, 0, val); |
|
0
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
627 |
} |
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
628 |
|
2 | 629 |
/* |
630 |
* Tracking features whose value depend on the choice of CCID |
|
631 |
* |
|
632 |
* This is designed with an extension in mind so that a list walk could be done |
|
633 |
* before activating any features. However, the existing framework was found to |
|
634 |
* work satisfactorily up until now, the automatic verification is left open. |
|
635 |
* When adding new CCIDs, add a corresponding dependency table here. |
|
636 |
*/ |
|
637 |
static const struct ccid_dependency *dccp_feat_ccid_deps(u8 ccid, bool is_local) |
|
0
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
638 |
{ |
2 | 639 |
static const struct ccid_dependency ccid2_dependencies[2][2] = { |
640 |
/* |
|
641 |
* CCID2 mandates Ack Vectors (RFC 4341, 4.): as CCID is a TX |
|
642 |
* feature and Send Ack Vector is an RX feature, `is_local' |
|
643 |
* needs to be reversed. |
|
644 |
*/ |
|
645 |
{ /* Dependencies of the receiver-side (remote) CCID2 */ |
|
646 |
{ |
|
647 |
.dependent_feat = DCCPF_SEND_ACK_VECTOR, |
|
648 |
.is_local = true, |
|
649 |
.is_mandatory = true, |
|
650 |
.val = 1 |
|
651 |
}, |
|
652 |
{ 0, 0, 0, 0 } |
|
653 |
}, |
|
654 |
{ /* Dependencies of the sender-side (local) CCID2 */ |
|
655 |
{ |
|
656 |
.dependent_feat = DCCPF_SEND_ACK_VECTOR, |
|
657 |
.is_local = false, |
|
658 |
.is_mandatory = true, |
|
659 |
.val = 1 |
|
660 |
}, |
|
661 |
{ 0, 0, 0, 0 } |
|
662 |
} |
|
663 |
}; |
|
664 |
static const struct ccid_dependency ccid3_dependencies[2][5] = { |
|
665 |
{ /* |
|
666 |
* Dependencies of the receiver-side CCID3 |
|
667 |
*/ |
|
668 |
{ /* locally disable Ack Vectors */ |
|
669 |
.dependent_feat = DCCPF_SEND_ACK_VECTOR, |
|
670 |
.is_local = true, |
|
671 |
.is_mandatory = false, |
|
672 |
.val = 0 |
|
673 |
}, |
|
674 |
{ /* see below why Send Loss Event Rate is on */ |
|
675 |
.dependent_feat = DCCPF_SEND_LEV_RATE, |
|
676 |
.is_local = true, |
|
677 |
.is_mandatory = true, |
|
678 |
.val = 1 |
|
679 |
}, |
|
680 |
{ /* NDP Count is needed as per RFC 4342, 6.1.1 */ |
|
681 |
.dependent_feat = DCCPF_SEND_NDP_COUNT, |
|
682 |
.is_local = false, |
|
683 |
.is_mandatory = true, |
|
684 |
.val = 1 |
|
685 |
}, |
|
686 |
{ 0, 0, 0, 0 }, |
|
687 |
}, |
|
688 |
{ /* |
|
689 |
* CCID3 at the TX side: we request that the HC-receiver |
|
690 |
* will not send Ack Vectors (they will be ignored, so |
|
691 |
* Mandatory is not set); we enable Send Loss Event Rate |
|
692 |
* (Mandatory since the implementation does not support |
|
693 |
* the Loss Intervals option of RFC 4342, 8.6). |
|
694 |
* The last two options are for peer's information only. |
|
695 |
*/ |
|
696 |
{ |
|
697 |
.dependent_feat = DCCPF_SEND_ACK_VECTOR, |
|
698 |
.is_local = false, |
|
699 |
.is_mandatory = false, |
|
700 |
.val = 0 |
|
701 |
}, |
|
702 |
{ |
|
703 |
.dependent_feat = DCCPF_SEND_LEV_RATE, |
|
704 |
.is_local = false, |
|
705 |
.is_mandatory = true, |
|
706 |
.val = 1 |
|
707 |
}, |
|
708 |
{ /* this CCID does not support Ack Ratio */ |
|
709 |
.dependent_feat = DCCPF_ACK_RATIO, |
|
710 |
.is_local = true, |
|
711 |
.is_mandatory = false, |
|
712 |
.val = 0 |
|
713 |
}, |
|
714 |
{ /* tell receiver we are sending NDP counts */ |
|
715 |
.dependent_feat = DCCPF_SEND_NDP_COUNT, |
|
716 |
.is_local = true, |
|
717 |
.is_mandatory = false, |
|
718 |
.val = 1 |
|
719 |
}, |
|
720 |
{ 0, 0, 0, 0 } |
|
721 |
} |
|
722 |
}; |
|
723 |
switch (ccid) { |
|
724 |
case DCCPC_CCID2: |
|
725 |
return ccid2_dependencies[is_local]; |
|
726 |
case DCCPC_CCID3: |
|
727 |
return ccid3_dependencies[is_local]; |
|
0
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
728 |
default: |
2 | 729 |
return NULL; |
0
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
730 |
} |
2 | 731 |
} |
0
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
732 |
|
2 | 733 |
/** |
734 |
* dccp_feat_propagate_ccid - Resolve dependencies of features on choice of CCID |
|
735 |
* @fn: feature-negotiation list to update |
|
736 |
* @id: CCID number to track |
|
737 |
* @is_local: whether TX CCID (1) or RX CCID (0) is meant |
|
738 |
* This function needs to be called after registering all other features. |
|
739 |
*/ |
|
740 |
static int dccp_feat_propagate_ccid(struct list_head *fn, u8 id, bool is_local) |
|
741 |
{ |
|
742 |
const struct ccid_dependency *table = dccp_feat_ccid_deps(id, is_local); |
|
743 |
int i, rc = (table == NULL); |
|
0
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
744 |
|
2 | 745 |
for (i = 0; rc == 0 && table[i].dependent_feat != DCCPF_RESERVED; i++) |
746 |
if (dccp_feat_type(table[i].dependent_feat) == FEAT_SP) |
|
747 |
rc = __feat_register_sp(fn, table[i].dependent_feat, |
|
748 |
table[i].is_local, |
|
749 |
table[i].is_mandatory, |
|
750 |
&table[i].val, 1); |
|
751 |
else |
|
752 |
rc = __feat_register_nn(fn, table[i].dependent_feat, |
|
753 |
table[i].is_mandatory, |
|
754 |
table[i].val); |
|
0
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
755 |
return rc; |
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
756 |
} |
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
757 |
|
2 | 758 |
/** |
759 |
* dccp_feat_finalise_settings - Finalise settings before starting negotiation |
|
760 |
* @dp: client or listening socket (settings will be inherited) |
|
761 |
* This is called after all registrations (socket initialisation, sysctls, and |
|
762 |
* sockopt calls), and before sending the first packet containing Change options |
|
763 |
* (ie. client-Request or server-Response), to ensure internal consistency. |
|
764 |
*/ |
|
765 |
int dccp_feat_finalise_settings(struct dccp_sock *dp) |
|
0
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
766 |
{ |
2 | 767 |
struct list_head *fn = &dp->dccps_featneg; |
768 |
struct dccp_feat_entry *entry; |
|
769 |
int i = 2, ccids[2] = { -1, -1 }; |
|
0
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
770 |
|
2 | 771 |
/* |
772 |
* Propagating CCIDs: |
|
773 |
* 1) not useful to propagate CCID settings if this host advertises more |
|
774 |
* than one CCID: the choice of CCID may still change - if this is |
|
775 |
* the client, or if this is the server and the client sends |
|
776 |
* singleton CCID values. |
|
777 |
* 2) since is that propagate_ccid changes the list, we defer changing |
|
778 |
* the sorted list until after the traversal. |
|
779 |
*/ |
|
780 |
list_for_each_entry(entry, fn, node) |
|
781 |
if (entry->feat_num == DCCPF_CCID && entry->val.sp.len == 1) |
|
782 |
ccids[entry->is_local] = entry->val.sp.vec[0]; |
|
783 |
while (i--) |
|
784 |
if (ccids[i] > 0 && dccp_feat_propagate_ccid(fn, ccids[i], i)) |
|
785 |
return -1; |
|
786 |
return 0; |
|
787 |
} |
|
0
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
788 |
|
2 | 789 |
/** |
790 |
* dccp_feat_server_ccid_dependencies - Resolve CCID-dependent features |
|
791 |
* It is the server which resolves the dependencies once the CCID has been |
|
792 |
* fully negotiated. If no CCID has been negotiated, it uses the default CCID. |
|
793 |
*/ |
|
794 |
int dccp_feat_server_ccid_dependencies(struct dccp_request_sock *dreq) |
|
795 |
{ |
|
796 |
struct list_head *fn = &dreq->dreq_featneg; |
|
797 |
struct dccp_feat_entry *entry; |
|
798 |
u8 is_local, ccid; |
|
0
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
799 |
|
2 | 800 |
for (is_local = 0; is_local <= 1; is_local++) { |
801 |
entry = dccp_feat_list_lookup(fn, DCCPF_CCID, is_local); |
|
0
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
802 |
|
2 | 803 |
if (entry != NULL && !entry->empty_confirm) |
804 |
ccid = entry->val.sp.vec[0]; |
|
805 |
else |
|
806 |
ccid = dccp_feat_default_value(DCCPF_CCID); |
|
807 |
||
808 |
if (dccp_feat_propagate_ccid(fn, ccid, is_local)) |
|
809 |
return -1; |
|
0
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
810 |
} |
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
811 |
return 0; |
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
812 |
} |
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
813 |
|
2 | 814 |
/* Select the first entry in @servlist that also occurs in @clilist (6.3.1) */ |
815 |
static int dccp_feat_preflist_match(u8 *servlist, u8 slen, u8 *clilist, u8 clen) |
|
0
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
816 |
{ |
2 | 817 |
u8 c, s; |
0
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
818 |
|
2 | 819 |
for (s = 0; s < slen; s++) |
820 |
for (c = 0; c < clen; c++) |
|
821 |
if (servlist[s] == clilist[c]) |
|
822 |
return servlist[s]; |
|
823 |
return -1; |
|
0
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
824 |
} |
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
825 |
|
2 | 826 |
/** |
827 |
* dccp_feat_prefer - Move preferred entry to the start of array |
|
828 |
* Reorder the @array_len elements in @array so that @preferred_value comes |
|
829 |
* first. Returns >0 to indicate that @preferred_value does occur in @array. |
|
0
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
830 |
*/ |
2 | 831 |
static u8 dccp_feat_prefer(u8 preferred_value, u8 *array, u8 array_len) |
0
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
832 |
{ |
2 | 833 |
u8 i, does_occur = 0; |
0
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
834 |
|
2 | 835 |
if (array != NULL) { |
836 |
for (i = 0; i < array_len; i++) |
|
837 |
if (array[i] == preferred_value) { |
|
838 |
array[i] = array[0]; |
|
839 |
does_occur++; |
|
840 |
} |
|
841 |
if (does_occur) |
|
842 |
array[0] = preferred_value; |
|
0
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
843 |
} |
2 | 844 |
return does_occur; |
0
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
845 |
} |
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
846 |
|
2 | 847 |
/** |
848 |
* dccp_feat_reconcile - Reconcile SP preference lists |
|
849 |
* @fval: SP list to reconcile into |
|
850 |
* @arr: received SP preference list |
|
851 |
* @len: length of @arr in bytes |
|
852 |
* @is_server: whether this side is the server (and @fv is the server's list) |
|
853 |
* @reorder: whether to reorder the list in @fv after reconciling with @arr |
|
854 |
* When successful, > 0 is returned and the reconciled list is in @fval. |
|
855 |
* A value of 0 means that negotiation failed (no shared entry). |
|
856 |
*/ |
|
857 |
static int dccp_feat_reconcile(dccp_feat_val *fv, u8 *arr, u8 len, |
|
858 |
bool is_server, bool reorder) |
|
0
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
859 |
{ |
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
860 |
int rc; |
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
861 |
|
2 | 862 |
if (!fv->sp.vec || !arr) { |
863 |
DCCP_CRIT("NULL feature value or array"); |
|
864 |
return 0; |
|
865 |
} |
|
866 |
||
867 |
if (is_server) |
|
868 |
rc = dccp_feat_preflist_match(fv->sp.vec, fv->sp.len, arr, len); |
|
869 |
else |
|
870 |
rc = dccp_feat_preflist_match(arr, len, fv->sp.vec, fv->sp.len); |
|
871 |
||
872 |
if (!reorder) |
|
873 |
return rc; |
|
874 |
if (rc < 0) |
|
875 |
return 0; |
|
876 |
||
877 |
/* |
|
878 |
* Reorder list: used for activating features and in dccp_insert_fn_opt. |
|
879 |
*/ |
|
880 |
return dccp_feat_prefer(rc, fv->sp.vec, fv->sp.len); |
|
881 |
} |
|
882 |
||
883 |
/** |
|
884 |
* dccp_feat_change_recv - Process incoming ChangeL/R options |
|
885 |
* @fn: feature-negotiation list to update |
|
886 |
* @is_mandatory: whether the Change was preceded by a Mandatory option |
|
887 |
* @opt: %DCCPO_CHANGE_L or %DCCPO_CHANGE_R |
|
888 |
* @feat: one of %dccp_feature_numbers |
|
889 |
* @val: NN value or SP value/preference list |
|
890 |
* @len: length of @val in bytes |
|
891 |
* @server: whether this node is the server (1) or the client (0) |
|
892 |
*/ |
|
893 |
static u8 dccp_feat_change_recv(struct list_head *fn, u8 is_mandatory, u8 opt, |
|
894 |
u8 feat, u8 *val, u8 len, const bool server) |
|
895 |
{ |
|
896 |
u8 defval, type = dccp_feat_type(feat); |
|
897 |
const bool local = (opt == DCCPO_CHANGE_R); |
|
898 |
struct dccp_feat_entry *entry; |
|
899 |
dccp_feat_val fval; |
|
900 |
||
901 |
if (len == 0 || type == FEAT_UNKNOWN) /* 6.1 and 6.6.8 */ |
|
902 |
goto unknown_feature_or_value; |
|
903 |
||
904 |
/* |
|
905 |
* Negotiation of NN features: Change R is invalid, so there is no |
|
906 |
* simultaneous negotiation; hence we do not look up in the list. |
|
907 |
*/ |
|
908 |
if (type == FEAT_NN) { |
|
909 |
if (local || len > sizeof(fval.nn)) |
|
910 |
goto unknown_feature_or_value; |
|
911 |
||
912 |
/* 6.3.2: "The feature remote MUST accept any valid value..." */ |
|
913 |
fval.nn = dccp_decode_value_var(val, len); |
|
914 |
if (!dccp_feat_is_valid_nn_val(feat, fval.nn)) |
|
915 |
goto unknown_feature_or_value; |
|
916 |
||
917 |
return dccp_feat_push_confirm(fn, feat, local, &fval); |
|
918 |
} |
|
919 |
||
920 |
/* |
|
921 |
* Unidirectional/simultaneous negotiation of SP features (6.3.1) |
|
922 |
*/ |
|
923 |
entry = dccp_feat_list_lookup(fn, feat, local); |
|
924 |
if (entry == NULL) { |
|
925 |
/* |
|
926 |
* No particular preferences have been registered. We deal with |
|
927 |
* this situation by assuming that all valid values are equally |
|
928 |
* acceptable, and apply the following checks: |
|
929 |
* - if the peer's list is a singleton, we accept a valid value; |
|
930 |
* - if we are the server, we first try to see if the peer (the |
|
931 |
* client) advertises the default value. If yes, we use it, |
|
932 |
* otherwise we accept the preferred value; |
|
933 |
* - else if we are the client, we use the first list element. |
|
934 |
*/ |
|
935 |
if (dccp_feat_clone_sp_val(&fval, val, 1)) |
|
936 |
return DCCP_RESET_CODE_TOO_BUSY; |
|
937 |
||
938 |
if (len > 1 && server) { |
|
939 |
defval = dccp_feat_default_value(feat); |
|
940 |
if (dccp_feat_preflist_match(&defval, 1, val, len) > -1) |
|
941 |
fval.sp.vec[0] = defval; |
|
942 |
} else if (!dccp_feat_is_valid_sp_val(feat, fval.sp.vec[0])) { |
|
943 |
kfree(fval.sp.vec); |
|
944 |
goto unknown_feature_or_value; |
|
945 |
} |
|
946 |
||
947 |
/* Treat unsupported CCIDs like invalid values */ |
|
948 |
if (feat == DCCPF_CCID && !ccid_support_check(fval.sp.vec, 1)) { |
|
949 |
kfree(fval.sp.vec); |
|
950 |
goto not_valid_or_not_known; |
|
951 |
} |
|
952 |
||
953 |
return dccp_feat_push_confirm(fn, feat, local, &fval); |
|
954 |
||
955 |
} else if (entry->state == FEAT_UNSTABLE) { /* 6.6.2 */ |
|
956 |
return 0; |
|
957 |
} |
|
958 |
||
959 |
if (dccp_feat_reconcile(&entry->val, val, len, server, true)) { |
|
960 |
entry->empty_confirm = 0; |
|
961 |
} else if (is_mandatory) { |
|
962 |
return DCCP_RESET_CODE_MANDATORY_ERROR; |
|
963 |
} else if (entry->state == FEAT_INITIALISING) { |
|
964 |
/* |
|
965 |
* Failed simultaneous negotiation (server only): try to `save' |
|
966 |
* the connection by checking whether entry contains the default |
|
967 |
* value for @feat. If yes, send an empty Confirm to signal that |
|
968 |
* the received Change was not understood - which implies using |
|
969 |
* the default value. |
|
970 |
* If this also fails, we use Reset as the last resort. |
|
971 |
*/ |
|
972 |
WARN_ON(!server); |
|
973 |
defval = dccp_feat_default_value(feat); |
|
974 |
if (!dccp_feat_reconcile(&entry->val, &defval, 1, server, true)) |
|
975 |
return DCCP_RESET_CODE_OPTION_ERROR; |
|
976 |
entry->empty_confirm = 1; |
|
977 |
} |
|
978 |
entry->needs_confirm = 1; |
|
979 |
entry->needs_mandatory = 0; |
|
980 |
entry->state = FEAT_STABLE; |
|
981 |
return 0; |
|
982 |
||
983 |
unknown_feature_or_value: |
|
984 |
if (!is_mandatory) |
|
985 |
return dccp_push_empty_confirm(fn, feat, local); |
|
986 |
||
987 |
not_valid_or_not_known: |
|
988 |
return is_mandatory ? DCCP_RESET_CODE_MANDATORY_ERROR |
|
989 |
: DCCP_RESET_CODE_OPTION_ERROR; |
|
990 |
} |
|
0
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
991 |
|
2 | 992 |
/** |
993 |
* dccp_feat_confirm_recv - Process received Confirm options |
|
994 |
* @fn: feature-negotiation list to update |
|
995 |
* @is_mandatory: whether @opt was preceded by a Mandatory option |
|
996 |
* @opt: %DCCPO_CONFIRM_L or %DCCPO_CONFIRM_R |
|
997 |
* @feat: one of %dccp_feature_numbers |
|
998 |
* @val: NN value or SP value/preference list |
|
999 |
* @len: length of @val in bytes |
|
1000 |
* @server: whether this node is server (1) or client (0) |
|
1001 |
*/ |
|
1002 |
static u8 dccp_feat_confirm_recv(struct list_head *fn, u8 is_mandatory, u8 opt, |
|
1003 |
u8 feat, u8 *val, u8 len, const bool server) |
|
1004 |
{ |
|
1005 |
u8 *plist, plen, type = dccp_feat_type(feat); |
|
1006 |
const bool local = (opt == DCCPO_CONFIRM_R); |
|
1007 |
struct dccp_feat_entry *entry = dccp_feat_list_lookup(fn, feat, local); |
|
1008 |
||
1009 |
if (entry == NULL) { /* nothing queued: ignore or handle error */ |
|
1010 |
if (is_mandatory && type == FEAT_UNKNOWN) |
|
1011 |
return DCCP_RESET_CODE_MANDATORY_ERROR; |
|
1012 |
||
1013 |
if (!local && type == FEAT_NN) /* 6.3.2 */ |
|
1014 |
goto confirmation_failed; |
|
1015 |
return 0; |
|
1016 |
} |
|
1017 |
||
1018 |
if (entry->state != FEAT_CHANGING) /* 6.6.2 */ |
|
1019 |
return 0; |
|
1020 |
||
1021 |
if (len == 0) { |
|
1022 |
if (dccp_feat_must_be_understood(feat)) /* 6.6.7 */ |
|
1023 |
goto confirmation_failed; |
|
1024 |
/* |
|
1025 |
* Empty Confirm during connection setup: this means reverting |
|
1026 |
* to the `old' value, which in this case is the default. Since |
|
1027 |
* we handle default values automatically when no other values |
|
1028 |
* have been set, we revert to the old value by removing this |
|
1029 |
* entry from the list. |
|
1030 |
*/ |
|
1031 |
dccp_feat_list_pop(entry); |
|
1032 |
return 0; |
|
1033 |
} |
|
1034 |
||
1035 |
if (type == FEAT_NN) { |
|
1036 |
if (len > sizeof(entry->val.nn)) |
|
1037 |
goto confirmation_failed; |
|
1038 |
||
1039 |
if (entry->val.nn == dccp_decode_value_var(val, len)) |
|
1040 |
goto confirmation_succeeded; |
|
1041 |
||
1042 |
DCCP_WARN("Bogus Confirm for non-existing value\n"); |
|
1043 |
goto confirmation_failed; |
|
1044 |
} |
|
1045 |
||
1046 |
/* |
|
1047 |
* Parsing SP Confirms: the first element of @val is the preferred |
|
1048 |
* SP value which the peer confirms, the remainder depends on @len. |
|
1049 |
* Note that only the confirmed value need to be a valid SP value. |
|
1050 |
*/ |
|
1051 |
if (!dccp_feat_is_valid_sp_val(feat, *val)) |
|
1052 |
goto confirmation_failed; |
|
0
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
1053 |
|
2 | 1054 |
if (len == 1) { /* peer didn't supply a preference list */ |
1055 |
plist = val; |
|
1056 |
plen = len; |
|
1057 |
} else { /* preferred value + preference list */ |
|
1058 |
plist = val + 1; |
|
1059 |
plen = len - 1; |
|
1060 |
} |
|
1061 |
||
1062 |
/* Check whether the peer got the reconciliation right (6.6.8) */ |
|
1063 |
if (dccp_feat_reconcile(&entry->val, plist, plen, server, 0) != *val) { |
|
1064 |
DCCP_WARN("Confirm selected the wrong value %u\n", *val); |
|
1065 |
return DCCP_RESET_CODE_OPTION_ERROR; |
|
1066 |
} |
|
1067 |
entry->val.sp.vec[0] = *val; |
|
1068 |
||
1069 |
confirmation_succeeded: |
|
1070 |
entry->state = FEAT_STABLE; |
|
1071 |
return 0; |
|
1072 |
||
1073 |
confirmation_failed: |
|
1074 |
DCCP_WARN("Confirmation failed\n"); |
|
1075 |
return is_mandatory ? DCCP_RESET_CODE_MANDATORY_ERROR |
|
1076 |
: DCCP_RESET_CODE_OPTION_ERROR; |
|
1077 |
} |
|
1078 |
||
1079 |
/** |
|
1080 |
* dccp_feat_parse_options - Process Feature-Negotiation Options |
|
1081 |
* @sk: for general use and used by the client during connection setup |
|
1082 |
* @dreq: used by the server during connection setup |
|
1083 |
* @mandatory: whether @opt was preceded by a Mandatory option |
|
1084 |
* @opt: %DCCPO_CHANGE_L | %DCCPO_CHANGE_R | %DCCPO_CONFIRM_L | %DCCPO_CONFIRM_R |
|
1085 |
* @feat: one of %dccp_feature_numbers |
|
1086 |
* @val: value contents of @opt |
|
1087 |
* @len: length of @val in bytes |
|
1088 |
* Returns 0 on success, a Reset code for ending the connection otherwise. |
|
1089 |
*/ |
|
1090 |
int dccp_feat_parse_options(struct sock *sk, struct dccp_request_sock *dreq, |
|
1091 |
u8 mandatory, u8 opt, u8 feat, u8 *val, u8 len) |
|
1092 |
{ |
|
1093 |
struct dccp_sock *dp = dccp_sk(sk); |
|
1094 |
struct list_head *fn = dreq ? &dreq->dreq_featneg : &dp->dccps_featneg; |
|
1095 |
bool server = false; |
|
1096 |
||
1097 |
switch (sk->sk_state) { |
|
1098 |
/* |
|
1099 |
* Negotiation during connection setup |
|
1100 |
*/ |
|
1101 |
case DCCP_LISTEN: |
|
1102 |
server = true; /* fall through */ |
|
1103 |
case DCCP_REQUESTING: |
|
1104 |
switch (opt) { |
|
1105 |
case DCCPO_CHANGE_L: |
|
1106 |
case DCCPO_CHANGE_R: |
|
1107 |
return dccp_feat_change_recv(fn, mandatory, opt, feat, |
|
1108 |
val, len, server); |
|
1109 |
case DCCPO_CONFIRM_R: |
|
1110 |
case DCCPO_CONFIRM_L: |
|
1111 |
return dccp_feat_confirm_recv(fn, mandatory, opt, feat, |
|
1112 |
val, len, server); |
|
1113 |
} |
|
1114 |
} |
|
1115 |
return 0; /* ignore FN options in all other states */ |
|
1116 |
} |
|
1117 |
||
1118 |
int dccp_feat_init(struct sock *sk) |
|
1119 |
{ |
|
1120 |
struct dccp_sock *dp = dccp_sk(sk); |
|
1121 |
struct dccp_minisock *dmsk = dccp_msk(sk); |
|
1122 |
int rc; |
|
1123 |
||
1124 |
INIT_LIST_HEAD(&dmsk->dccpms_pending); /* XXX no longer used */ |
|
1125 |
INIT_LIST_HEAD(&dmsk->dccpms_conf); /* XXX no longer used */ |
|
0
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
1126 |
|
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
1127 |
/* Ack ratio */ |
2 | 1128 |
rc = __feat_register_nn(&dp->dccps_featneg, DCCPF_ACK_RATIO, 0, |
1129 |
dp->dccps_l_ack_ratio); |
|
0
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
1130 |
return rc; |
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
1131 |
} |
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
1132 |
|
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
1133 |
EXPORT_SYMBOL_GPL(dccp_feat_init); |
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
1134 |
|
2 | 1135 |
int dccp_feat_activate_values(struct sock *sk, struct list_head *fn_list) |
1136 |
{ |
|
1137 |
struct dccp_sock *dp = dccp_sk(sk); |
|
1138 |
struct dccp_feat_entry *cur, *next; |
|
1139 |
int idx; |
|
1140 |
dccp_feat_val *fvals[DCCP_FEAT_SUPPORTED_MAX][2] = { |
|
1141 |
[0 ... DCCP_FEAT_SUPPORTED_MAX-1] = { NULL, NULL } |
|
1142 |
}; |
|
1143 |
||
1144 |
list_for_each_entry(cur, fn_list, node) { |
|
1145 |
/* |
|
1146 |
* An empty Confirm means that either an unknown feature type |
|
1147 |
* or an invalid value was present. In the first case there is |
|
1148 |
* nothing to activate, in the other the default value is used. |
|
1149 |
*/ |
|
1150 |
if (cur->empty_confirm) |
|
1151 |
continue; |
|
1152 |
||
1153 |
idx = dccp_feat_index(cur->feat_num); |
|
1154 |
if (idx < 0) { |
|
1155 |
DCCP_BUG("Unknown feature %u", cur->feat_num); |
|
1156 |
goto activation_failed; |
|
1157 |
} |
|
1158 |
if (cur->state != FEAT_STABLE) { |
|
1159 |
DCCP_CRIT("Negotiation of %s %u failed in state %u", |
|
1160 |
cur->is_local ? "local" : "remote", |
|
1161 |
cur->feat_num, cur->state); |
|
1162 |
goto activation_failed; |
|
1163 |
} |
|
1164 |
fvals[idx][cur->is_local] = &cur->val; |
|
1165 |
} |
|
1166 |
||
1167 |
/* |
|
1168 |
* Activate in decreasing order of index, so that the CCIDs are always |
|
1169 |
* activated as the last feature. This avoids the case where a CCID |
|
1170 |
* relies on the initialisation of one or more features that it depends |
|
1171 |
* on (e.g. Send NDP Count, Send Ack Vector, and Ack Ratio features). |
|
1172 |
*/ |
|
1173 |
for (idx = DCCP_FEAT_SUPPORTED_MAX; --idx >= 0;) |
|
1174 |
if (__dccp_feat_activate(sk, idx, 0, fvals[idx][0]) || |
|
1175 |
__dccp_feat_activate(sk, idx, 1, fvals[idx][1])) { |
|
1176 |
DCCP_CRIT("Could not activate %d", idx); |
|
1177 |
goto activation_failed; |
|
1178 |
} |
|
1179 |
||
1180 |
/* Clean up Change options which have been confirmed already */ |
|
1181 |
list_for_each_entry_safe(cur, next, fn_list, node) |
|
1182 |
if (!cur->needs_confirm) |
|
1183 |
dccp_feat_list_pop(cur); |
|
1184 |
||
1185 |
dccp_pr_debug("Activation OK\n"); |
|
1186 |
return 0; |
|
1187 |
||
1188 |
activation_failed: |
|
1189 |
/* |
|
1190 |
* We clean up everything that may have been allocated, since |
|
1191 |
* it is difficult to track at which stage negotiation failed. |
|
1192 |
* This is ok, since all allocation functions below are robust |
|
1193 |
* against NULL arguments. |
|
1194 |
*/ |
|
1195 |
ccid_hc_rx_delete(dp->dccps_hc_rx_ccid, sk); |
|
1196 |
ccid_hc_tx_delete(dp->dccps_hc_tx_ccid, sk); |
|
1197 |
dp->dccps_hc_rx_ccid = dp->dccps_hc_tx_ccid = NULL; |
|
1198 |
dccp_ackvec_free(dp->dccps_hc_rx_ackvec); |
|
1199 |
dp->dccps_hc_rx_ackvec = NULL; |
|
1200 |
return -1; |
|
1201 |
} |
|
1202 |
||
0
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
1203 |
#ifdef CONFIG_IP_DCCP_DEBUG |
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
1204 |
const char *dccp_feat_typename(const u8 type) |
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
1205 |
{ |
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
1206 |
switch(type) { |
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
1207 |
case DCCPO_CHANGE_L: return("ChangeL"); |
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
1208 |
case DCCPO_CONFIRM_L: return("ConfirmL"); |
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
1209 |
case DCCPO_CHANGE_R: return("ChangeR"); |
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
1210 |
case DCCPO_CONFIRM_R: return("ConfirmR"); |
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
1211 |
/* the following case must not appear in feature negotation */ |
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
1212 |
default: dccp_pr_debug("unknown type %d [BUG!]\n", type); |
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
1213 |
} |
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
1214 |
return NULL; |
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
1215 |
} |
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
1216 |
|
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
1217 |
const char *dccp_feat_name(const u8 feat) |
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
1218 |
{ |
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
1219 |
static const char *feature_names[] = { |
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
1220 |
[DCCPF_RESERVED] = "Reserved", |
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
1221 |
[DCCPF_CCID] = "CCID", |
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
1222 |
[DCCPF_SHORT_SEQNOS] = "Allow Short Seqnos", |
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
1223 |
[DCCPF_SEQUENCE_WINDOW] = "Sequence Window", |
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
1224 |
[DCCPF_ECN_INCAPABLE] = "ECN Incapable", |
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
1225 |
[DCCPF_ACK_RATIO] = "Ack Ratio", |
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
1226 |
[DCCPF_SEND_ACK_VECTOR] = "Send ACK Vector", |
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
1227 |
[DCCPF_SEND_NDP_COUNT] = "Send NDP Count", |
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
1228 |
[DCCPF_MIN_CSUM_COVER] = "Min. Csum Coverage", |
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
1229 |
[DCCPF_DATA_CHECKSUM] = "Send Data Checksum", |
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
1230 |
}; |
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
1231 |
if (feat > DCCPF_DATA_CHECKSUM && feat < DCCPF_MIN_CCID_SPECIFIC) |
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
1232 |
return feature_names[DCCPF_RESERVED]; |
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
1233 |
|
2 | 1234 |
if (feat == DCCPF_SEND_LEV_RATE) |
1235 |
return "Send Loss Event Rate"; |
|
0
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
1236 |
if (feat >= DCCPF_MIN_CCID_SPECIFIC) |
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
1237 |
return "CCID-specific"; |
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
1238 |
|
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
1239 |
return feature_names[feat]; |
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
1240 |
} |
aa628870c1d3
Port of Linux 2.6.28 for use with network simulation cradle.
Florian Westphal <fw@strlen.de>
parents:
diff
changeset
|
1241 |
#endif /* CONFIG_IP_DCCP_DEBUG */ |