summaryrefslogtreecommitdiffstats
path: root/src/net/dhcpopts.c
diff options
context:
space:
mode:
authorMichael Brown2011-01-11 00:58:11 +0100
committerMichael Brown2011-01-11 22:24:40 +0100
commit17b6a3c506d94b8dc8bdae89828bac20ef56b1ef (patch)
tree68e953fe63ac12df77198e1c80de0ba8d45da881 /src/net/dhcpopts.c
parent[dhcp] Rename length fields for DHCP options (diff)
downloadipxe-17b6a3c506d94b8dc8bdae89828bac20ef56b1ef.tar.gz
ipxe-17b6a3c506d94b8dc8bdae89828bac20ef56b1ef.tar.xz
ipxe-17b6a3c506d94b8dc8bdae89828bac20ef56b1ef.zip
[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>
Diffstat (limited to 'src/net/dhcpopts.c')
-rw-r--r--src/net/dhcpopts.c115
1 files changed, 59 insertions, 56 deletions
diff --git a/src/net/dhcpopts.c b/src/net/dhcpopts.c
index 214a8244..d1330eae 100644
--- a/src/net/dhcpopts.c
+++ b/src/net/dhcpopts.c
@@ -170,6 +170,17 @@ static int find_dhcp_option_with_encap ( struct dhcp_options *options,
}
/**
+ * Refuse to reallocate DHCP option block
+ *
+ * @v options DHCP option block
+ * @v len New length
+ * @ret rc Return status code
+ */
+int dhcpopt_no_realloc ( struct dhcp_options *options, size_t len ) {
+ return ( ( len <= options->alloc_len ) ? 0 : -ENOSPC );
+}
+
+/**
* Resize a DHCP option
*
* @v options DHCP option block
@@ -177,46 +188,44 @@ static int find_dhcp_option_with_encap ( struct dhcp_options *options,
* @v encap_offset Offset of encapsulating offset (or -ve for none)
* @v old_len Old length (including header)
* @v new_len New length (including header)
- * @v can_realloc Can reallocate options data if necessary
* @ret rc Return status code
*/
static int resize_dhcp_option ( struct dhcp_options *options,
int offset, int encap_offset,
- size_t old_len, size_t new_len,
- int can_realloc ) {
+ size_t old_len, size_t new_len ) {
struct dhcp_option *encapsulator;
struct dhcp_option *option;
ssize_t delta = ( new_len - old_len );
- size_t new_options_len;
+ size_t old_alloc_len;
+ size_t new_used_len;
size_t new_encapsulator_len;
- void *new_data;
void *source;
void *dest;
void *end;
+ int rc;
- /* Check for sufficient space, and update length fields */
+ /* Check for sufficient space */
if ( new_len > DHCP_MAX_LEN ) {
DBGC ( options, "DHCPOPT %p overlength option\n", options );
return -ENOSPC;
}
- new_options_len = ( options->used_len + delta );
- if ( new_options_len > options->alloc_len ) {
- /* Reallocate options block if allowed to do so. */
- if ( can_realloc ) {
- new_data = realloc ( options->data, new_options_len );
- if ( ! new_data ) {
- DBGC ( options, "DHCPOPT %p could not "
- "reallocate to %zd bytes\n", options,
- new_options_len );
- return -ENOMEM;
- }
- options->data = new_data;
- options->alloc_len = new_options_len;
- } else {
- DBGC ( options, "DHCPOPT %p out of space\n", options );
- return -ENOMEM;
+ new_used_len = ( options->used_len + delta );
+
+ /* Expand options block, if necessary */
+ if ( new_used_len > options->alloc_len ) {
+ /* Reallocate options block */
+ old_alloc_len = options->alloc_len;
+ if ( ( rc = options->realloc ( options, new_used_len ) ) != 0 ){
+ DBGC ( options, "DHCPOPT %p could not reallocate to "
+ "%zd bytes\n", options, new_used_len );
+ return rc;
}
+ /* Clear newly allocated space */
+ memset ( ( options->data + old_alloc_len ), 0,
+ ( options->alloc_len - old_alloc_len ) );
}
+
+ /* Update encapsulator, if applicable */
if ( encap_offset >= 0 ) {
encapsulator = dhcp_option ( options, encap_offset );
new_encapsulator_len = ( encapsulator->len + delta );
@@ -227,7 +236,9 @@ static int resize_dhcp_option ( struct dhcp_options *options,
}
encapsulator->len = new_encapsulator_len;
}
- options->used_len = new_options_len;
+
+ /* Update used length */
+ options->used_len = new_used_len;
/* Move remainder of option data */
option = dhcp_option ( options, offset );
@@ -236,6 +247,15 @@ static int resize_dhcp_option ( struct dhcp_options *options,
end = ( options->data + options->alloc_len );
memmove ( dest, source, ( end - dest ) );
+ /* Shrink options block, if applicable */
+ if ( new_used_len < options->alloc_len ) {
+ if ( ( rc = options->realloc ( options, new_used_len ) ) != 0 ){
+ DBGC ( options, "DHCPOPT %p could not reallocate to "
+ "%zd bytes\n", options, new_used_len );
+ return rc;
+ }
+ }
+
return 0;
}
@@ -246,7 +266,6 @@ static int resize_dhcp_option ( struct dhcp_options *options,
* @v tag DHCP option tag
* @v data New value for DHCP option
* @v len Length of value, in bytes
- * @v can_realloc Can reallocate options data if necessary
* @ret offset Offset of DHCP option, or negative error
*
* 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,
* be left with its original value.
*/
static int set_dhcp_option ( struct dhcp_options *options, unsigned int tag,
- const void *data, size_t len,
- int can_realloc ) {
- static const uint8_t empty_encapsulator[] = { DHCP_END };
+ const void *data, size_t len ) {
+ static const uint8_t empty_encap[] = { DHCP_END };
int offset;
int encap_offset = -1;
int creation_offset;
@@ -291,10 +309,12 @@ static int set_dhcp_option ( struct dhcp_options *options, unsigned int tag,
/* Ensure that encapsulator exists, if required */
if ( encap_tag ) {
- if ( encap_offset < 0 )
- encap_offset = set_dhcp_option ( options, encap_tag,
- empty_encapsulator, 1,
- can_realloc );
+ if ( encap_offset < 0 ) {
+ encap_offset =
+ set_dhcp_option ( options, encap_tag,
+ empty_encap,
+ sizeof ( empty_encap ) );
+ }
if ( encap_offset < 0 )
return encap_offset;
creation_offset = ( encap_offset + DHCP_OPTION_HEADER_LEN );
@@ -306,8 +326,7 @@ static int set_dhcp_option ( struct dhcp_options *options, unsigned int tag,
/* Resize option to fit new data */
if ( ( rc = resize_dhcp_option ( options, offset, encap_offset,
- old_len, new_len,
- can_realloc ) ) != 0 )
+ old_len, new_len ) ) != 0 )
return rc;
/* Copy new data into option, if applicable */
@@ -322,7 +341,7 @@ static int set_dhcp_option ( struct dhcp_options *options, unsigned int tag,
if ( encap_offset >= 0 ) {
option = dhcp_option ( options, encap_offset );
if ( option->len <= 1 )
- set_dhcp_option ( options, encap_tag, NULL, 0, 0 );
+ set_dhcp_option ( options, encap_tag, NULL, 0 );
}
return offset;
@@ -341,26 +360,7 @@ int dhcpopt_store ( struct dhcp_options *options, unsigned int tag,
const void *data, size_t len ) {
int offset;
- offset = set_dhcp_option ( options, tag, data, len, 0 );
- if ( offset < 0 )
- return offset;
- return 0;
-}
-
-/**
- * Store value of DHCP option setting, extending options block if necessary
- *
- * @v options DHCP option block
- * @v tag Setting tag number
- * @v data Setting data, or NULL to clear setting
- * @v len Length of setting data
- * @ret rc Return status code
- */
-int dhcpopt_extensible_store ( struct dhcp_options *options, unsigned int tag,
- const void *data, size_t len ) {
- int offset;
-
- offset = set_dhcp_option ( options, tag, data, len, 1 );
+ offset = set_dhcp_option ( options, tag, data, len );
if ( offset < 0 )
return offset;
return 0;
@@ -428,16 +428,19 @@ static void dhcpopt_update_used_len ( struct dhcp_options *options ) {
* @v options Uninitialised DHCP option block
* @v data Memory for DHCP option data
* @v alloc_len Length of memory for DHCP option data
+ * @v realloc DHCP option block reallocator
*
* The memory content must already be filled with valid DHCP options.
* A zeroed block counts as a block of valid DHCP options.
*/
-void dhcpopt_init ( struct dhcp_options *options, void *data,
- size_t alloc_len ) {
+void dhcpopt_init ( struct dhcp_options *options, void *data, size_t alloc_len,
+ int ( * realloc ) ( struct dhcp_options *options,
+ size_t len ) ) {
/* Fill in fields */
options->data = data;
options->alloc_len = alloc_len;
+ options->realloc = realloc;
/* Update length */
dhcpopt_update_used_len ( options );