aboutsummaryrefslogtreecommitdiffstats
path: root/net/dccp/feat.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/dccp/feat.c')
-rw-r--r--net/dccp/feat.c129
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 */
162static 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 */
182static 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 */
213static 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 */
240static 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
261static 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
266static 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
272void 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}
280EXPORT_SYMBOL_GPL(dccp_feat_list_purge);
281
153int dccp_feat_change(struct dccp_minisock *dmsk, u8 type, u8 feature, 282int 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{