Browse Source

[dhcp] Allow use of custom reallocation functions for DHCP option blocks

Allow functions other than realloc() to be used to reallocate DHCP
option block data, and specify the reallocation function at the time
of calling dhcpopt_init().

Signed-off-by: Michael Brown <mcb30@ipxe.org>
tags/v1.20.1
Michael Brown 14 years ago
parent
commit
17b6a3c506
4 changed files with 74 additions and 62 deletions
  1. 2
    1
      src/core/nvo.c
  2. 11
    4
      src/include/ipxe/dhcpopts.h
  3. 59
    56
      src/net/dhcpopts.c
  4. 2
    1
      src/net/dhcppkt.c

+ 2
- 1
src/core/nvo.c View File

@@ -133,7 +133,8 @@ static void nvo_init_dhcpopts ( struct nvo_block *nvo ) {
133 133
 		memset ( nvo->data, 0, nvo->total_len );
134 134
 	}
135 135
 
136
-	dhcpopt_init ( &nvo->dhcpopts, options_data, options_len );
136
+	dhcpopt_init ( &nvo->dhcpopts, options_data, options_len,
137
+		       dhcpopt_no_realloc );
137 138
 }
138 139
 
139 140
 /**

+ 11
- 4
src/include/ipxe/dhcpopts.h View File

@@ -19,16 +19,23 @@ struct dhcp_options {
19 19
 	size_t used_len;
20 20
 	/** Option block allocated length */
21 21
 	size_t alloc_len;
22
+	/** Reallocate option block raw data
23
+	 *
24
+	 * @v options		DHCP option block
25
+	 * @v len		New length
26
+	 * @ret rc		Return status code
27
+	 */
28
+	int ( * realloc ) ( struct dhcp_options *options, size_t len );
22 29
 };
23 30
 
24 31
 extern int dhcpopt_store ( struct dhcp_options *options, unsigned int tag,
25 32
 			   const void *data, size_t len );
26
-extern int dhcpopt_extensible_store ( struct dhcp_options *options,
27
-				      unsigned int tag,
28
-				      const void *data, size_t len );
29 33
 extern int dhcpopt_fetch ( struct dhcp_options *options, unsigned int tag,
30 34
 			   void *data, size_t len );
31 35
 extern void dhcpopt_init ( struct dhcp_options *options,
32
-			   void *data, size_t alloc_len );
36
+			   void *data, size_t alloc_len,
37
+			   int ( * realloc ) ( struct dhcp_options *options,
38
+					       size_t len ) );
39
+extern int dhcpopt_no_realloc ( struct dhcp_options *options, size_t len );
33 40
 
34 41
 #endif /* _IPXE_DHCPOPTS_H */

+ 59
- 56
src/net/dhcpopts.c View File

@@ -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 );

+ 2
- 1
src/net/dhcppkt.c View File

@@ -267,7 +267,8 @@ void dhcppkt_init ( struct dhcp_packet *dhcppkt, struct dhcphdr *data,
267 267
 	ref_init ( &dhcppkt->refcnt, NULL );
268 268
 	dhcppkt->dhcphdr = data;
269 269
 	dhcpopt_init ( &dhcppkt->options, &dhcppkt->dhcphdr->options,
270
-		       ( len - offsetof ( struct dhcphdr, options ) ) );
270
+		       ( len - offsetof ( struct dhcphdr, options ) ),
271
+		       dhcpopt_no_realloc );
271 272
 	settings_init ( &dhcppkt->settings,
272 273
 			&dhcppkt_settings_operations, &dhcppkt->refcnt, 0 );
273 274
 }

Loading…
Cancel
Save