|
@@ -169,6 +169,17 @@ static int find_dhcp_option_with_encap ( struct dhcp_options *options,
|
169
|
169
|
return -ENOENT;
|
170
|
170
|
}
|
171
|
171
|
|
|
172
|
+/**
|
|
173
|
+ * Refuse to reallocate DHCP option block
|
|
174
|
+ *
|
|
175
|
+ * @v options DHCP option block
|
|
176
|
+ * @v len New length
|
|
177
|
+ * @ret rc Return status code
|
|
178
|
+ */
|
|
179
|
+int dhcpopt_no_realloc ( struct dhcp_options *options, size_t len ) {
|
|
180
|
+ return ( ( len <= options->alloc_len ) ? 0 : -ENOSPC );
|
|
181
|
+}
|
|
182
|
+
|
172
|
183
|
/**
|
173
|
184
|
* Resize a DHCP option
|
174
|
185
|
*
|
|
@@ -177,46 +188,44 @@ static int find_dhcp_option_with_encap ( struct dhcp_options *options,
|
177
|
188
|
* @v encap_offset Offset of encapsulating offset (or -ve for none)
|
178
|
189
|
* @v old_len Old length (including header)
|
179
|
190
|
* @v new_len New length (including header)
|
180
|
|
- * @v can_realloc Can reallocate options data if necessary
|
181
|
191
|
* @ret rc Return status code
|
182
|
192
|
*/
|
183
|
193
|
static int resize_dhcp_option ( struct dhcp_options *options,
|
184
|
194
|
int offset, int encap_offset,
|
185
|
|
- size_t old_len, size_t new_len,
|
186
|
|
- int can_realloc ) {
|
|
195
|
+ size_t old_len, size_t new_len ) {
|
187
|
196
|
struct dhcp_option *encapsulator;
|
188
|
197
|
struct dhcp_option *option;
|
189
|
198
|
ssize_t delta = ( new_len - old_len );
|
190
|
|
- size_t new_options_len;
|
|
199
|
+ size_t old_alloc_len;
|
|
200
|
+ size_t new_used_len;
|
191
|
201
|
size_t new_encapsulator_len;
|
192
|
|
- void *new_data;
|
193
|
202
|
void *source;
|
194
|
203
|
void *dest;
|
195
|
204
|
void *end;
|
|
205
|
+ int rc;
|
196
|
206
|
|
197
|
|
- /* Check for sufficient space, and update length fields */
|
|
207
|
+ /* Check for sufficient space */
|
198
|
208
|
if ( new_len > DHCP_MAX_LEN ) {
|
199
|
209
|
DBGC ( options, "DHCPOPT %p overlength option\n", options );
|
200
|
210
|
return -ENOSPC;
|
201
|
211
|
}
|
202
|
|
- new_options_len = ( options->used_len + delta );
|
203
|
|
- if ( new_options_len > options->alloc_len ) {
|
204
|
|
- /* Reallocate options block if allowed to do so. */
|
205
|
|
- if ( can_realloc ) {
|
206
|
|
- new_data = realloc ( options->data, new_options_len );
|
207
|
|
- if ( ! new_data ) {
|
208
|
|
- DBGC ( options, "DHCPOPT %p could not "
|
209
|
|
- "reallocate to %zd bytes\n", options,
|
210
|
|
- new_options_len );
|
211
|
|
- return -ENOMEM;
|
212
|
|
- }
|
213
|
|
- options->data = new_data;
|
214
|
|
- options->alloc_len = new_options_len;
|
215
|
|
- } else {
|
216
|
|
- DBGC ( options, "DHCPOPT %p out of space\n", options );
|
217
|
|
- return -ENOMEM;
|
|
212
|
+ new_used_len = ( options->used_len + delta );
|
|
213
|
+
|
|
214
|
+ /* Expand options block, if necessary */
|
|
215
|
+ if ( new_used_len > options->alloc_len ) {
|
|
216
|
+ /* Reallocate options block */
|
|
217
|
+ old_alloc_len = options->alloc_len;
|
|
218
|
+ if ( ( rc = options->realloc ( options, new_used_len ) ) != 0 ){
|
|
219
|
+ DBGC ( options, "DHCPOPT %p could not reallocate to "
|
|
220
|
+ "%zd bytes\n", options, new_used_len );
|
|
221
|
+ return rc;
|
218
|
222
|
}
|
|
223
|
+ /* Clear newly allocated space */
|
|
224
|
+ memset ( ( options->data + old_alloc_len ), 0,
|
|
225
|
+ ( options->alloc_len - old_alloc_len ) );
|
219
|
226
|
}
|
|
227
|
+
|
|
228
|
+ /* Update encapsulator, if applicable */
|
220
|
229
|
if ( encap_offset >= 0 ) {
|
221
|
230
|
encapsulator = dhcp_option ( options, encap_offset );
|
222
|
231
|
new_encapsulator_len = ( encapsulator->len + delta );
|
|
@@ -227,7 +236,9 @@ static int resize_dhcp_option ( struct dhcp_options *options,
|
227
|
236
|
}
|
228
|
237
|
encapsulator->len = new_encapsulator_len;
|
229
|
238
|
}
|
230
|
|
- options->used_len = new_options_len;
|
|
239
|
+
|
|
240
|
+ /* Update used length */
|
|
241
|
+ options->used_len = new_used_len;
|
231
|
242
|
|
232
|
243
|
/* Move remainder of option data */
|
233
|
244
|
option = dhcp_option ( options, offset );
|
|
@@ -236,6 +247,15 @@ static int resize_dhcp_option ( struct dhcp_options *options,
|
236
|
247
|
end = ( options->data + options->alloc_len );
|
237
|
248
|
memmove ( dest, source, ( end - dest ) );
|
238
|
249
|
|
|
250
|
+ /* Shrink options block, if applicable */
|
|
251
|
+ if ( new_used_len < options->alloc_len ) {
|
|
252
|
+ if ( ( rc = options->realloc ( options, new_used_len ) ) != 0 ){
|
|
253
|
+ DBGC ( options, "DHCPOPT %p could not reallocate to "
|
|
254
|
+ "%zd bytes\n", options, new_used_len );
|
|
255
|
+ return rc;
|
|
256
|
+ }
|
|
257
|
+ }
|
|
258
|
+
|
239
|
259
|
return 0;
|
240
|
260
|
}
|
241
|
261
|
|
|
@@ -246,7 +266,6 @@ static int resize_dhcp_option ( struct dhcp_options *options,
|
246
|
266
|
* @v tag DHCP option tag
|
247
|
267
|
* @v data New value for DHCP option
|
248
|
268
|
* @v len Length of value, in bytes
|
249
|
|
- * @v can_realloc Can reallocate options data if necessary
|
250
|
269
|
* @ret offset Offset of DHCP option, or negative error
|
251
|
270
|
*
|
252
|
271
|
* Sets the value of a DHCP option within the options block. The
|
|
@@ -258,9 +277,8 @@ static int resize_dhcp_option ( struct dhcp_options *options,
|
258
|
277
|
* be left with its original value.
|
259
|
278
|
*/
|
260
|
279
|
static int set_dhcp_option ( struct dhcp_options *options, unsigned int tag,
|
261
|
|
- const void *data, size_t len,
|
262
|
|
- int can_realloc ) {
|
263
|
|
- static const uint8_t empty_encapsulator[] = { DHCP_END };
|
|
280
|
+ const void *data, size_t len ) {
|
|
281
|
+ static const uint8_t empty_encap[] = { DHCP_END };
|
264
|
282
|
int offset;
|
265
|
283
|
int encap_offset = -1;
|
266
|
284
|
int creation_offset;
|
|
@@ -291,10 +309,12 @@ static int set_dhcp_option ( struct dhcp_options *options, unsigned int tag,
|
291
|
309
|
|
292
|
310
|
/* Ensure that encapsulator exists, if required */
|
293
|
311
|
if ( encap_tag ) {
|
294
|
|
- if ( encap_offset < 0 )
|
295
|
|
- encap_offset = set_dhcp_option ( options, encap_tag,
|
296
|
|
- empty_encapsulator, 1,
|
297
|
|
- can_realloc );
|
|
312
|
+ if ( encap_offset < 0 ) {
|
|
313
|
+ encap_offset =
|
|
314
|
+ set_dhcp_option ( options, encap_tag,
|
|
315
|
+ empty_encap,
|
|
316
|
+ sizeof ( empty_encap ) );
|
|
317
|
+ }
|
298
|
318
|
if ( encap_offset < 0 )
|
299
|
319
|
return encap_offset;
|
300
|
320
|
creation_offset = ( encap_offset + DHCP_OPTION_HEADER_LEN );
|
|
@@ -306,8 +326,7 @@ static int set_dhcp_option ( struct dhcp_options *options, unsigned int tag,
|
306
|
326
|
|
307
|
327
|
/* Resize option to fit new data */
|
308
|
328
|
if ( ( rc = resize_dhcp_option ( options, offset, encap_offset,
|
309
|
|
- old_len, new_len,
|
310
|
|
- can_realloc ) ) != 0 )
|
|
329
|
+ old_len, new_len ) ) != 0 )
|
311
|
330
|
return rc;
|
312
|
331
|
|
313
|
332
|
/* Copy new data into option, if applicable */
|
|
@@ -322,7 +341,7 @@ static int set_dhcp_option ( struct dhcp_options *options, unsigned int tag,
|
322
|
341
|
if ( encap_offset >= 0 ) {
|
323
|
342
|
option = dhcp_option ( options, encap_offset );
|
324
|
343
|
if ( option->len <= 1 )
|
325
|
|
- set_dhcp_option ( options, encap_tag, NULL, 0, 0 );
|
|
344
|
+ set_dhcp_option ( options, encap_tag, NULL, 0 );
|
326
|
345
|
}
|
327
|
346
|
|
328
|
347
|
return offset;
|
|
@@ -341,26 +360,7 @@ int dhcpopt_store ( struct dhcp_options *options, unsigned int tag,
|
341
|
360
|
const void *data, size_t len ) {
|
342
|
361
|
int offset;
|
343
|
362
|
|
344
|
|
- offset = set_dhcp_option ( options, tag, data, len, 0 );
|
345
|
|
- if ( offset < 0 )
|
346
|
|
- return offset;
|
347
|
|
- return 0;
|
348
|
|
-}
|
349
|
|
-
|
350
|
|
-/**
|
351
|
|
- * Store value of DHCP option setting, extending options block if necessary
|
352
|
|
- *
|
353
|
|
- * @v options DHCP option block
|
354
|
|
- * @v tag Setting tag number
|
355
|
|
- * @v data Setting data, or NULL to clear setting
|
356
|
|
- * @v len Length of setting data
|
357
|
|
- * @ret rc Return status code
|
358
|
|
- */
|
359
|
|
-int dhcpopt_extensible_store ( struct dhcp_options *options, unsigned int tag,
|
360
|
|
- const void *data, size_t len ) {
|
361
|
|
- int offset;
|
362
|
|
-
|
363
|
|
- offset = set_dhcp_option ( options, tag, data, len, 1 );
|
|
363
|
+ offset = set_dhcp_option ( options, tag, data, len );
|
364
|
364
|
if ( offset < 0 )
|
365
|
365
|
return offset;
|
366
|
366
|
return 0;
|
|
@@ -428,16 +428,19 @@ static void dhcpopt_update_used_len ( struct dhcp_options *options ) {
|
428
|
428
|
* @v options Uninitialised DHCP option block
|
429
|
429
|
* @v data Memory for DHCP option data
|
430
|
430
|
* @v alloc_len Length of memory for DHCP option data
|
|
431
|
+ * @v realloc DHCP option block reallocator
|
431
|
432
|
*
|
432
|
433
|
* The memory content must already be filled with valid DHCP options.
|
433
|
434
|
* A zeroed block counts as a block of valid DHCP options.
|
434
|
435
|
*/
|
435
|
|
-void dhcpopt_init ( struct dhcp_options *options, void *data,
|
436
|
|
- size_t alloc_len ) {
|
|
436
|
+void dhcpopt_init ( struct dhcp_options *options, void *data, size_t alloc_len,
|
|
437
|
+ int ( * realloc ) ( struct dhcp_options *options,
|
|
438
|
+ size_t len ) ) {
|
437
|
439
|
|
438
|
440
|
/* Fill in fields */
|
439
|
441
|
options->data = data;
|
440
|
442
|
options->alloc_len = alloc_len;
|
|
443
|
+ options->realloc = realloc;
|
441
|
444
|
|
442
|
445
|
/* Update length */
|
443
|
446
|
dhcpopt_update_used_len ( options );
|