mirror of https://github.com/ipxe/ipxe.git
[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>pull/1/head
parent
310d46c1ed
commit
17b6a3c506
|
@ -133,7 +133,8 @@ static void nvo_init_dhcpopts ( struct nvo_block *nvo ) {
|
||||||
memset ( nvo->data, 0, nvo->total_len );
|
memset ( nvo->data, 0, nvo->total_len );
|
||||||
}
|
}
|
||||||
|
|
||||||
dhcpopt_init ( &nvo->dhcpopts, options_data, options_len );
|
dhcpopt_init ( &nvo->dhcpopts, options_data, options_len,
|
||||||
|
dhcpopt_no_realloc );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -19,16 +19,23 @@ struct dhcp_options {
|
||||||
size_t used_len;
|
size_t used_len;
|
||||||
/** Option block allocated length */
|
/** Option block allocated length */
|
||||||
size_t alloc_len;
|
size_t alloc_len;
|
||||||
|
/** Reallocate option block raw data
|
||||||
|
*
|
||||||
|
* @v options DHCP option block
|
||||||
|
* @v len New length
|
||||||
|
* @ret rc Return status code
|
||||||
|
*/
|
||||||
|
int ( * realloc ) ( struct dhcp_options *options, size_t len );
|
||||||
};
|
};
|
||||||
|
|
||||||
extern int dhcpopt_store ( struct dhcp_options *options, unsigned int tag,
|
extern int dhcpopt_store ( struct dhcp_options *options, unsigned int tag,
|
||||||
const void *data, size_t len );
|
const void *data, size_t len );
|
||||||
extern int dhcpopt_extensible_store ( struct dhcp_options *options,
|
|
||||||
unsigned int tag,
|
|
||||||
const void *data, size_t len );
|
|
||||||
extern int dhcpopt_fetch ( struct dhcp_options *options, unsigned int tag,
|
extern int dhcpopt_fetch ( struct dhcp_options *options, unsigned int tag,
|
||||||
void *data, size_t len );
|
void *data, size_t len );
|
||||||
extern void dhcpopt_init ( struct dhcp_options *options,
|
extern void dhcpopt_init ( struct dhcp_options *options,
|
||||||
void *data, size_t alloc_len );
|
void *data, size_t alloc_len,
|
||||||
|
int ( * realloc ) ( struct dhcp_options *options,
|
||||||
|
size_t len ) );
|
||||||
|
extern int dhcpopt_no_realloc ( struct dhcp_options *options, size_t len );
|
||||||
|
|
||||||
#endif /* _IPXE_DHCPOPTS_H */
|
#endif /* _IPXE_DHCPOPTS_H */
|
||||||
|
|
|
@ -169,6 +169,17 @@ static int find_dhcp_option_with_encap ( struct dhcp_options *options,
|
||||||
return -ENOENT;
|
return -ENOENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
* Resize a DHCP option
|
||||||
*
|
*
|
||||||
|
@ -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 encap_offset Offset of encapsulating offset (or -ve for none)
|
||||||
* @v old_len Old length (including header)
|
* @v old_len Old length (including header)
|
||||||
* @v new_len New length (including header)
|
* @v new_len New length (including header)
|
||||||
* @v can_realloc Can reallocate options data if necessary
|
|
||||||
* @ret rc Return status code
|
* @ret rc Return status code
|
||||||
*/
|
*/
|
||||||
static int resize_dhcp_option ( struct dhcp_options *options,
|
static int resize_dhcp_option ( struct dhcp_options *options,
|
||||||
int offset, int encap_offset,
|
int offset, int encap_offset,
|
||||||
size_t old_len, size_t new_len,
|
size_t old_len, size_t new_len ) {
|
||||||
int can_realloc ) {
|
|
||||||
struct dhcp_option *encapsulator;
|
struct dhcp_option *encapsulator;
|
||||||
struct dhcp_option *option;
|
struct dhcp_option *option;
|
||||||
ssize_t delta = ( new_len - old_len );
|
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;
|
size_t new_encapsulator_len;
|
||||||
void *new_data;
|
|
||||||
void *source;
|
void *source;
|
||||||
void *dest;
|
void *dest;
|
||||||
void *end;
|
void *end;
|
||||||
|
int rc;
|
||||||
|
|
||||||
/* Check for sufficient space, and update length fields */
|
/* Check for sufficient space */
|
||||||
if ( new_len > DHCP_MAX_LEN ) {
|
if ( new_len > DHCP_MAX_LEN ) {
|
||||||
DBGC ( options, "DHCPOPT %p overlength option\n", options );
|
DBGC ( options, "DHCPOPT %p overlength option\n", options );
|
||||||
return -ENOSPC;
|
return -ENOSPC;
|
||||||
}
|
}
|
||||||
new_options_len = ( options->used_len + delta );
|
new_used_len = ( options->used_len + delta );
|
||||||
if ( new_options_len > options->alloc_len ) {
|
|
||||||
/* Reallocate options block if allowed to do so. */
|
/* Expand options block, if necessary */
|
||||||
if ( can_realloc ) {
|
if ( new_used_len > options->alloc_len ) {
|
||||||
new_data = realloc ( options->data, new_options_len );
|
/* Reallocate options block */
|
||||||
if ( ! new_data ) {
|
old_alloc_len = options->alloc_len;
|
||||||
DBGC ( options, "DHCPOPT %p could not "
|
if ( ( rc = options->realloc ( options, new_used_len ) ) != 0 ){
|
||||||
"reallocate to %zd bytes\n", options,
|
DBGC ( options, "DHCPOPT %p could not reallocate to "
|
||||||
new_options_len );
|
"%zd bytes\n", options, new_used_len );
|
||||||
return -ENOMEM;
|
return rc;
|
||||||
}
|
|
||||||
options->data = new_data;
|
|
||||||
options->alloc_len = new_options_len;
|
|
||||||
} else {
|
|
||||||
DBGC ( options, "DHCPOPT %p out of space\n", options );
|
|
||||||
return -ENOMEM;
|
|
||||||
}
|
}
|
||||||
|
/* 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 ) {
|
if ( encap_offset >= 0 ) {
|
||||||
encapsulator = dhcp_option ( options, encap_offset );
|
encapsulator = dhcp_option ( options, encap_offset );
|
||||||
new_encapsulator_len = ( encapsulator->len + delta );
|
new_encapsulator_len = ( encapsulator->len + delta );
|
||||||
|
@ -227,7 +236,9 @@ static int resize_dhcp_option ( struct dhcp_options *options,
|
||||||
}
|
}
|
||||||
encapsulator->len = new_encapsulator_len;
|
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 */
|
/* Move remainder of option data */
|
||||||
option = dhcp_option ( options, offset );
|
option = dhcp_option ( options, offset );
|
||||||
|
@ -236,6 +247,15 @@ static int resize_dhcp_option ( struct dhcp_options *options,
|
||||||
end = ( options->data + options->alloc_len );
|
end = ( options->data + options->alloc_len );
|
||||||
memmove ( dest, source, ( end - dest ) );
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -246,7 +266,6 @@ static int resize_dhcp_option ( struct dhcp_options *options,
|
||||||
* @v tag DHCP option tag
|
* @v tag DHCP option tag
|
||||||
* @v data New value for DHCP option
|
* @v data New value for DHCP option
|
||||||
* @v len Length of value, in bytes
|
* @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
|
* @ret offset Offset of DHCP option, or negative error
|
||||||
*
|
*
|
||||||
* Sets the value of a DHCP option within the options block. The
|
* 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.
|
* be left with its original value.
|
||||||
*/
|
*/
|
||||||
static int set_dhcp_option ( struct dhcp_options *options, unsigned int tag,
|
static int set_dhcp_option ( struct dhcp_options *options, unsigned int tag,
|
||||||
const void *data, size_t len,
|
const void *data, size_t len ) {
|
||||||
int can_realloc ) {
|
static const uint8_t empty_encap[] = { DHCP_END };
|
||||||
static const uint8_t empty_encapsulator[] = { DHCP_END };
|
|
||||||
int offset;
|
int offset;
|
||||||
int encap_offset = -1;
|
int encap_offset = -1;
|
||||||
int creation_offset;
|
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 */
|
/* Ensure that encapsulator exists, if required */
|
||||||
if ( encap_tag ) {
|
if ( encap_tag ) {
|
||||||
if ( encap_offset < 0 )
|
if ( encap_offset < 0 ) {
|
||||||
encap_offset = set_dhcp_option ( options, encap_tag,
|
encap_offset =
|
||||||
empty_encapsulator, 1,
|
set_dhcp_option ( options, encap_tag,
|
||||||
can_realloc );
|
empty_encap,
|
||||||
|
sizeof ( empty_encap ) );
|
||||||
|
}
|
||||||
if ( encap_offset < 0 )
|
if ( encap_offset < 0 )
|
||||||
return encap_offset;
|
return encap_offset;
|
||||||
creation_offset = ( encap_offset + DHCP_OPTION_HEADER_LEN );
|
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 */
|
/* Resize option to fit new data */
|
||||||
if ( ( rc = resize_dhcp_option ( options, offset, encap_offset,
|
if ( ( rc = resize_dhcp_option ( options, offset, encap_offset,
|
||||||
old_len, new_len,
|
old_len, new_len ) ) != 0 )
|
||||||
can_realloc ) ) != 0 )
|
|
||||||
return rc;
|
return rc;
|
||||||
|
|
||||||
/* Copy new data into option, if applicable */
|
/* 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 ) {
|
if ( encap_offset >= 0 ) {
|
||||||
option = dhcp_option ( options, encap_offset );
|
option = dhcp_option ( options, encap_offset );
|
||||||
if ( option->len <= 1 )
|
if ( option->len <= 1 )
|
||||||
set_dhcp_option ( options, encap_tag, NULL, 0, 0 );
|
set_dhcp_option ( options, encap_tag, NULL, 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
return offset;
|
return offset;
|
||||||
|
@ -341,26 +360,7 @@ int dhcpopt_store ( struct dhcp_options *options, unsigned int tag,
|
||||||
const void *data, size_t len ) {
|
const void *data, size_t len ) {
|
||||||
int offset;
|
int offset;
|
||||||
|
|
||||||
offset = set_dhcp_option ( options, tag, data, len, 0 );
|
offset = set_dhcp_option ( options, tag, data, len );
|
||||||
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 );
|
|
||||||
if ( offset < 0 )
|
if ( offset < 0 )
|
||||||
return offset;
|
return offset;
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -428,16 +428,19 @@ static void dhcpopt_update_used_len ( struct dhcp_options *options ) {
|
||||||
* @v options Uninitialised DHCP option block
|
* @v options Uninitialised DHCP option block
|
||||||
* @v data Memory for DHCP option data
|
* @v data Memory for DHCP option data
|
||||||
* @v alloc_len Length of 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.
|
* The memory content must already be filled with valid DHCP options.
|
||||||
* A zeroed block counts as a block of valid DHCP options.
|
* A zeroed block counts as a block of valid DHCP options.
|
||||||
*/
|
*/
|
||||||
void dhcpopt_init ( struct dhcp_options *options, void *data,
|
void dhcpopt_init ( struct dhcp_options *options, void *data, size_t alloc_len,
|
||||||
size_t alloc_len ) {
|
int ( * realloc ) ( struct dhcp_options *options,
|
||||||
|
size_t len ) ) {
|
||||||
|
|
||||||
/* Fill in fields */
|
/* Fill in fields */
|
||||||
options->data = data;
|
options->data = data;
|
||||||
options->alloc_len = alloc_len;
|
options->alloc_len = alloc_len;
|
||||||
|
options->realloc = realloc;
|
||||||
|
|
||||||
/* Update length */
|
/* Update length */
|
||||||
dhcpopt_update_used_len ( options );
|
dhcpopt_update_used_len ( options );
|
||||||
|
|
|
@ -267,7 +267,8 @@ void dhcppkt_init ( struct dhcp_packet *dhcppkt, struct dhcphdr *data,
|
||||||
ref_init ( &dhcppkt->refcnt, NULL );
|
ref_init ( &dhcppkt->refcnt, NULL );
|
||||||
dhcppkt->dhcphdr = data;
|
dhcppkt->dhcphdr = data;
|
||||||
dhcpopt_init ( &dhcppkt->options, &dhcppkt->dhcphdr->options,
|
dhcpopt_init ( &dhcppkt->options, &dhcppkt->dhcphdr->options,
|
||||||
( len - offsetof ( struct dhcphdr, options ) ) );
|
( len - offsetof ( struct dhcphdr, options ) ),
|
||||||
|
dhcpopt_no_realloc );
|
||||||
settings_init ( &dhcppkt->settings,
|
settings_init ( &dhcppkt->settings,
|
||||||
&dhcppkt_settings_operations, &dhcppkt->refcnt, 0 );
|
&dhcppkt_settings_operations, &dhcppkt->refcnt, 0 );
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue