summaryrefslogtreecommitdiffstats
path: root/src/net/dhcpopts.c
diff options
context:
space:
mode:
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 );