diff options
-rw-r--r-- | net/dccp/feat.c | 129 |
1 files changed, 129 insertions, 0 deletions
diff --git a/net/dccp/feat.c b/net/dccp/feat.c index d7468f70544d..2ec2cd117699 100644 --- a/net/dccp/feat.c +++ b/net/dccp/feat.c | |||
@@ -150,6 +150,135 @@ static void dccp_feat_entry_destructor(struct dccp_feat_entry *entry) | |||
150 | } | 150 | } |
151 | } | 151 | } |
152 | 152 | ||
153 | /* | ||
154 | * List management functions | ||
155 | * | ||
156 | * Feature negotiation lists rely on and maintain the following invariants: | ||
157 | * - each feat_num in the list is known, i.e. we know its type and default value | ||
158 | * - each feat_num/is_local combination is unique (old entries are overwritten) | ||
159 | * - SP values are always freshly allocated | ||
160 | * - list is sorted in increasing order of feature number (faster lookup) | ||
161 | */ | ||
162 | static struct dccp_feat_entry *dccp_feat_list_lookup(struct list_head *fn_list, | ||
163 | u8 feat_num, bool is_local) | ||
164 | { | ||
165 | struct dccp_feat_entry *entry; | ||
166 | |||
167 | list_for_each_entry(entry, fn_list, node) | ||
168 | if (entry->feat_num == feat_num && entry->is_local == is_local) | ||
169 | return entry; | ||
170 | else if (entry->feat_num > feat_num) | ||
171 | break; | ||
172 | return NULL; | ||
173 | } | ||
174 | |||
175 | /** | ||
176 | * dccp_feat_entry_new - Central list update routine (called by all others) | ||
177 | * @head: list to add to | ||
178 | * @feat: feature number | ||
179 | * @local: whether the local (1) or remote feature with number @feat is meant | ||
180 | * This is the only constructor and serves to ensure the above invariants. | ||
181 | */ | ||
182 | static struct dccp_feat_entry * | ||
183 | dccp_feat_entry_new(struct list_head *head, u8 feat, bool local) | ||
184 | { | ||
185 | struct dccp_feat_entry *entry; | ||
186 | |||
187 | list_for_each_entry(entry, head, node) | ||
188 | if (entry->feat_num == feat && entry->is_local == local) { | ||
189 | dccp_feat_val_destructor(entry->feat_num, &entry->val); | ||
190 | return entry; | ||
191 | } else if (entry->feat_num > feat) { | ||
192 | head = &entry->node; | ||
193 | break; | ||
194 | } | ||
195 | |||
196 | entry = kmalloc(sizeof(*entry), gfp_any()); | ||
197 | if (entry != NULL) { | ||
198 | entry->feat_num = feat; | ||
199 | entry->is_local = local; | ||
200 | list_add_tail(&entry->node, head); | ||
201 | } | ||
202 | return entry; | ||
203 | } | ||
204 | |||
205 | /** | ||
206 | * dccp_feat_push_change - Add/overwrite a Change option in the list | ||
207 | * @fn_list: feature-negotiation list to update | ||
208 | * @feat: one of %dccp_feature_numbers | ||
209 | * @local: whether local (1) or remote (0) @feat_num is meant | ||
210 | * @needs_mandatory: whether to use Mandatory feature negotiation options | ||
211 | * @fval: pointer to NN/SP value to be inserted (will be copied) | ||
212 | */ | ||
213 | static int dccp_feat_push_change(struct list_head *fn_list, u8 feat, u8 local, | ||
214 | u8 mandatory, dccp_feat_val *fval) | ||
215 | { | ||
216 | struct dccp_feat_entry *new = dccp_feat_entry_new(fn_list, feat, local); | ||
217 | |||
218 | if (new == NULL) | ||
219 | return -ENOMEM; | ||
220 | |||
221 | new->feat_num = feat; | ||
222 | new->is_local = local; | ||
223 | new->state = FEAT_INITIALISING; | ||
224 | new->needs_confirm = 0; | ||
225 | new->empty_confirm = 0; | ||
226 | new->val = *fval; | ||
227 | new->needs_mandatory = mandatory; | ||
228 | |||
229 | return 0; | ||
230 | } | ||
231 | |||
232 | /** | ||
233 | * dccp_feat_push_confirm - Add a Confirm entry to the FN list | ||
234 | * @fn_list: feature-negotiation list to add to | ||
235 | * @feat: one of %dccp_feature_numbers | ||
236 | * @local: whether local (1) or remote (0) @feat_num is being confirmed | ||
237 | * @fval: pointer to NN/SP value to be inserted or NULL | ||
238 | * Returns 0 on success, a Reset code for further processing otherwise. | ||
239 | */ | ||
240 | static int dccp_feat_push_confirm(struct list_head *fn_list, u8 feat, u8 local, | ||
241 | dccp_feat_val *fval) | ||
242 | { | ||
243 | struct dccp_feat_entry *new = dccp_feat_entry_new(fn_list, feat, local); | ||
244 | |||
245 | if (new == NULL) | ||
246 | return DCCP_RESET_CODE_TOO_BUSY; | ||
247 | |||
248 | new->feat_num = feat; | ||
249 | new->is_local = local; | ||
250 | new->state = FEAT_STABLE; /* transition in 6.6.2 */ | ||
251 | new->needs_confirm = 1; | ||
252 | new->empty_confirm = (fval == NULL); | ||
253 | new->val.nn = 0; /* zeroes the whole structure */ | ||
254 | if (!new->empty_confirm) | ||
255 | new->val = *fval; | ||
256 | new->needs_mandatory = 0; | ||
257 | |||
258 | return 0; | ||
259 | } | ||
260 | |||
261 | static int dccp_push_empty_confirm(struct list_head *fn_list, u8 feat, u8 local) | ||
262 | { | ||
263 | return dccp_feat_push_confirm(fn_list, feat, local, NULL); | ||
264 | } | ||
265 | |||
266 | static inline void dccp_feat_list_pop(struct dccp_feat_entry *entry) | ||
267 | { | ||
268 | list_del(&entry->node); | ||
269 | dccp_feat_entry_destructor(entry); | ||
270 | } | ||
271 | |||
272 | void dccp_feat_list_purge(struct list_head *fn_list) | ||
273 | { | ||
274 | struct dccp_feat_entry *entry, *next; | ||
275 | |||
276 | list_for_each_entry_safe(entry, next, fn_list, node) | ||
277 | dccp_feat_entry_destructor(entry); | ||
278 | INIT_LIST_HEAD(fn_list); | ||
279 | } | ||
280 | EXPORT_SYMBOL_GPL(dccp_feat_list_purge); | ||
281 | |||
153 | int dccp_feat_change(struct dccp_minisock *dmsk, u8 type, u8 feature, | 282 | int dccp_feat_change(struct dccp_minisock *dmsk, u8 type, u8 feature, |
154 | u8 *val, u8 len, gfp_t gfp) | 283 | u8 *val, u8 len, gfp_t gfp) |
155 | { | 284 | { |