#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <gpxe/dhcp.h>
#include <gpxe/dhcpopts.h>
Go to the source code of this file.
Functions | |
| FILE_LICENCE (GPL2_OR_LATER) | |
| static char * | dhcp_tag_name (unsigned int tag) |
| Obtain printable version of a DHCP option tag. | |
| static struct dhcp_option * | dhcp_option (struct dhcp_options *options, unsigned int offset) |
| Get pointer to DHCP option. | |
| static int | dhcp_option_offset (struct dhcp_options *options, struct dhcp_option *option) |
| Get offset of a DHCP option. | |
| static unsigned int | dhcp_option_len (struct dhcp_option *option) |
| Calculate length of any DHCP option. | |
| static int | find_dhcp_option_with_encap (struct dhcp_options *options, unsigned int tag, int *encap_offset) |
| Find DHCP option within DHCP options block, and its encapsulator (if any). | |
| 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) |
| Resize a DHCP option. | |
| static int | set_dhcp_option (struct dhcp_options *options, unsigned int tag, const void *data, size_t len, int can_realloc) |
| Set value of DHCP option. | |
| int | dhcpopt_store (struct dhcp_options *options, unsigned int tag, const void *data, size_t len) |
| Store value of DHCP option setting. | |
| int | dhcpopt_extensible_store (struct dhcp_options *options, unsigned int tag, const void *data, size_t len) |
| Store value of DHCP option setting, extending options block if necessary. | |
| int | dhcpopt_fetch (struct dhcp_options *options, unsigned int tag, void *data, size_t len) |
| Fetch value of DHCP option setting. | |
| static void | dhcpopt_update_len (struct dhcp_options *options) |
| Recalculate length of DHCP options block. | |
| void | dhcpopt_init (struct dhcp_options *options, void *data, size_t max_len) |
| Initialise prepopulated block of DHCP options. | |
Definition in file dhcpopts.c.
| FILE_LICENCE | ( | GPL2_OR_LATER | ) |
| static char* dhcp_tag_name | ( | unsigned int | tag | ) | [inline, static] |
Obtain printable version of a DHCP option tag.
| tag | DHCP option tag |
| name | String representation of the tag |
Definition at line 42 of file dhcpopts.c.
References DHCP_ENCAPSULATED, DHCP_ENCAPSULATOR, DHCP_IS_ENCAP_OPT, name, and snprintf().
Referenced by find_dhcp_option_with_encap(), and set_dhcp_option().
00042 { 00043 static char name[8]; 00044 00045 if ( DHCP_IS_ENCAP_OPT ( tag ) ) { 00046 snprintf ( name, sizeof ( name ), "%d.%d", 00047 DHCP_ENCAPSULATOR ( tag ), 00048 DHCP_ENCAPSULATED ( tag ) ); 00049 } else { 00050 snprintf ( name, sizeof ( name ), "%d", tag ); 00051 } 00052 return name; 00053 }
| static struct dhcp_option* dhcp_option | ( | struct dhcp_options * | options, | |
| unsigned int | offset | |||
| ) | [static, read] |
Get pointer to DHCP option.
| options | DHCP options block | |
| offset | Offset within options block |
Definition at line 63 of file dhcpopts.c.
Referenced by dhcpopt_fetch(), dhcpopt_update_len(), find_dhcp_option_with_encap(), resize_dhcp_option(), and set_dhcp_option().
00063 { 00064 return ( ( struct dhcp_option * ) ( options->data + offset ) ); 00065 }
| static int dhcp_option_offset | ( | struct dhcp_options * | options, | |
| struct dhcp_option * | option | |||
| ) | [inline, static] |
| static unsigned int dhcp_option_len | ( | struct dhcp_option * | option | ) | [static] |
Calculate length of any DHCP option.
| len | Length (including tag and length field) |
Definition at line 86 of file dhcpopts.c.
References DHCP_END, DHCP_OPTION_HEADER_LEN, DHCP_PAD, dhcp_option::len, and dhcp_option::tag.
Referenced by dhcpopt_update_len(), find_dhcp_option_with_encap(), and set_dhcp_option().
00086 { 00087 if ( ( option->tag == DHCP_END ) || ( option->tag == DHCP_PAD ) ) { 00088 return 1; 00089 } else { 00090 return ( option->len + DHCP_OPTION_HEADER_LEN ); 00091 } 00092 }
| static int find_dhcp_option_with_encap | ( | struct dhcp_options * | options, | |
| unsigned int | tag, | |||
| int * | encap_offset | |||
| ) | [static] |
Find DHCP option within DHCP options block, and its encapsulator (if any).
| options | DHCP options block | |
| tag | DHCP option tag to search for |
| encap_offset | Offset of encapsulating DHCP option | |
| offset | Offset of DHCP option, or negative error |
If the option is encapsulated, and encap_offset is non-NULL, it will be filled in with the offset of the encapsulating option.
This routine is designed to be paranoid. It does not assume that the option data is well-formatted, and so must guard against flaws such as options missing a DHCP_END terminator, or options whose length would take them beyond the end of the data block.
Definition at line 114 of file dhcpopts.c.
References __attribute__, DBGC, DHCP_ENCAPSULATED, DHCP_ENCAPSULATOR, DHCP_END, DHCP_IS_ENCAP_OPT, dhcp_option(), DHCP_OPTION_HEADER_LEN, dhcp_option_len(), DHCP_PAD, dhcp_tag_name(), ENOENT, dhcp_options::len, offset, and dhcp_option::tag.
Referenced by dhcpopt_fetch(), and set_dhcp_option().
00116 { 00117 unsigned int original_tag __attribute__ (( unused )) = tag; 00118 struct dhcp_option *option; 00119 int offset = 0; 00120 ssize_t remaining = options->len; 00121 unsigned int option_len; 00122 00123 /* Sanity check */ 00124 if ( tag == DHCP_PAD ) 00125 return -ENOENT; 00126 00127 /* Search for option */ 00128 while ( remaining ) { 00129 /* Calculate length of this option. Abort processing 00130 * if the length is malformed (i.e. takes us beyond 00131 * the end of the data block). 00132 */ 00133 option = dhcp_option ( options, offset ); 00134 option_len = dhcp_option_len ( option ); 00135 remaining -= option_len; 00136 if ( remaining < 0 ) 00137 break; 00138 /* Check for explicit end marker */ 00139 if ( option->tag == DHCP_END ) { 00140 if ( tag == DHCP_END ) 00141 /* Special case where the caller is interested 00142 * in whether we have this marker or not. 00143 */ 00144 return offset; 00145 else 00146 break; 00147 } 00148 /* Check for matching tag */ 00149 if ( option->tag == tag ) { 00150 DBGC ( options, "DHCPOPT %p found %s (length %d)\n", 00151 options, dhcp_tag_name ( original_tag ), 00152 option_len ); 00153 return offset; 00154 } 00155 /* Check for start of matching encapsulation block */ 00156 if ( DHCP_IS_ENCAP_OPT ( tag ) && 00157 ( option->tag == DHCP_ENCAPSULATOR ( tag ) ) ) { 00158 if ( encap_offset ) 00159 *encap_offset = offset; 00160 /* Continue search within encapsulated option block */ 00161 tag = DHCP_ENCAPSULATED ( tag ); 00162 remaining = option_len; 00163 offset += DHCP_OPTION_HEADER_LEN; 00164 continue; 00165 } 00166 offset += option_len; 00167 } 00168 00169 return -ENOENT; 00170 }
| 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 | |||
| ) | [static] |
Resize a DHCP option.
| options | DHCP option block | |
| offset | Offset of option to resize | |
| encap_offset | Offset of encapsulating offset (or -ve for none) | |
| old_len | Old length (including header) | |
| new_len | New length (including header) | |
| can_realloc | Can reallocate options data if necessary |
| rc | Return status code |
Definition at line 183 of file dhcpopts.c.
References dhcp_options::data, DBGC, dest, DHCP_MAX_LEN, dhcp_option(), ENOMEM, ENOSPC, dhcp_option::len, dhcp_options::len, dhcp_options::max_len, memmove(), and realloc().
Referenced by set_dhcp_option().
00186 { 00187 struct dhcp_option *encapsulator; 00188 struct dhcp_option *option; 00189 ssize_t delta = ( new_len - old_len ); 00190 size_t new_options_len; 00191 size_t new_encapsulator_len; 00192 void *new_data; 00193 void *source; 00194 void *dest; 00195 void *end; 00196 00197 /* Check for sufficient space, and update length fields */ 00198 if ( new_len > DHCP_MAX_LEN ) { 00199 DBGC ( options, "DHCPOPT %p overlength option\n", options ); 00200 return -ENOSPC; 00201 } 00202 new_options_len = ( options->len + delta ); 00203 if ( new_options_len > options->max_len ) { 00204 /* Reallocate options block if allowed to do so. */ 00205 if ( can_realloc ) { 00206 new_data = realloc ( options->data, new_options_len ); 00207 if ( ! new_data ) { 00208 DBGC ( options, "DHCPOPT %p could not " 00209 "reallocate to %zd bytes\n", options, 00210 new_options_len ); 00211 return -ENOMEM; 00212 } 00213 options->data = new_data; 00214 options->max_len = new_options_len; 00215 } else { 00216 DBGC ( options, "DHCPOPT %p out of space\n", options ); 00217 return -ENOMEM; 00218 } 00219 } 00220 if ( encap_offset >= 0 ) { 00221 encapsulator = dhcp_option ( options, encap_offset ); 00222 new_encapsulator_len = ( encapsulator->len + delta ); 00223 if ( new_encapsulator_len > DHCP_MAX_LEN ) { 00224 DBGC ( options, "DHCPOPT %p overlength encapsulator\n", 00225 options ); 00226 return -ENOSPC; 00227 } 00228 encapsulator->len = new_encapsulator_len; 00229 } 00230 options->len = new_options_len; 00231 00232 /* Move remainder of option data */ 00233 option = dhcp_option ( options, offset ); 00234 source = ( ( ( void * ) option ) + old_len ); 00235 dest = ( ( ( void * ) option ) + new_len ); 00236 end = ( options->data + options->max_len ); 00237 memmove ( dest, source, ( end - dest ) ); 00238 00239 return 0; 00240 }
| static int set_dhcp_option | ( | struct dhcp_options * | options, | |
| unsigned int | tag, | |||
| const void * | data, | |||
| size_t | len, | |||
| int | can_realloc | |||
| ) | [static] |
Set value of DHCP option.
| options | DHCP option block | |
| tag | DHCP option tag | |
| data | New value for DHCP option | |
| len | Length of value, in bytes | |
| can_realloc | Can reallocate options data if necessary |
| offset | Offset of DHCP option, or negative error |
This call may fail due to insufficient space in the options block. If it does fail, and the option existed previously, the option will be left with its original value.
Definition at line 260 of file dhcpopts.c.
References dhcp_option::data, DBGC, DHCP_ENCAPSULATOR, DHCP_END, dhcp_option(), DHCP_OPTION_HEADER_LEN, dhcp_option_len(), DHCP_PAD, dhcp_tag_name(), ENOTTY, find_dhcp_option_with_encap(), dhcp_option::len, dhcp_options::len, memcpy, NULL, offset, resize_dhcp_option(), and dhcp_option::tag.
Referenced by dhcpopt_extensible_store(), and dhcpopt_store().
00262 { 00263 static const uint8_t empty_encapsulator[] = { DHCP_END }; 00264 int offset; 00265 int encap_offset = -1; 00266 int creation_offset; 00267 struct dhcp_option *option; 00268 unsigned int encap_tag = DHCP_ENCAPSULATOR ( tag ); 00269 size_t old_len = 0; 00270 size_t new_len = ( len ? ( len + DHCP_OPTION_HEADER_LEN ) : 0 ); 00271 int rc; 00272 00273 /* Sanity check */ 00274 if ( tag == DHCP_PAD ) 00275 return -ENOTTY; 00276 00277 creation_offset = find_dhcp_option_with_encap ( options, DHCP_END, 00278 NULL ); 00279 if ( creation_offset < 0 ) 00280 creation_offset = options->len; 00281 /* Find old instance of this option, if any */ 00282 offset = find_dhcp_option_with_encap ( options, tag, &encap_offset ); 00283 if ( offset >= 0 ) { 00284 old_len = dhcp_option_len ( dhcp_option ( options, offset ) ); 00285 DBGC ( options, "DHCPOPT %p resizing %s from %zd to %zd\n", 00286 options, dhcp_tag_name ( tag ), old_len, new_len ); 00287 } else { 00288 DBGC ( options, "DHCPOPT %p creating %s (length %zd)\n", 00289 options, dhcp_tag_name ( tag ), new_len ); 00290 } 00291 00292 /* Ensure that encapsulator exists, if required */ 00293 if ( encap_tag ) { 00294 if ( encap_offset < 0 ) 00295 encap_offset = set_dhcp_option ( options, encap_tag, 00296 empty_encapsulator, 1, 00297 can_realloc ); 00298 if ( encap_offset < 0 ) 00299 return encap_offset; 00300 creation_offset = ( encap_offset + DHCP_OPTION_HEADER_LEN ); 00301 } 00302 00303 /* Create new option if necessary */ 00304 if ( offset < 0 ) 00305 offset = creation_offset; 00306 00307 /* Resize option to fit new data */ 00308 if ( ( rc = resize_dhcp_option ( options, offset, encap_offset, 00309 old_len, new_len, 00310 can_realloc ) ) != 0 ) 00311 return rc; 00312 00313 /* Copy new data into option, if applicable */ 00314 if ( len ) { 00315 option = dhcp_option ( options, offset ); 00316 option->tag = tag; 00317 option->len = len; 00318 memcpy ( &option->data, data, len ); 00319 } 00320 00321 /* Delete encapsulator if there's nothing else left in it */ 00322 if ( encap_offset >= 0 ) { 00323 option = dhcp_option ( options, encap_offset ); 00324 if ( option->len <= 1 ) 00325 set_dhcp_option ( options, encap_tag, NULL, 0, 0 ); 00326 } 00327 00328 return offset; 00329 }
| int dhcpopt_store | ( | struct dhcp_options * | options, | |
| unsigned int | tag, | |||
| const void * | data, | |||
| size_t | len | |||
| ) |
Store value of DHCP option setting.
| options | DHCP option block | |
| tag | Setting tag number | |
| data | Setting data, or NULL to clear setting | |
| len | Length of setting data |
| rc | Return status code |
Definition at line 340 of file dhcpopts.c.
References offset, and set_dhcp_option().
Referenced by dhcppkt_store(), and nvo_store().
00341 { 00342 int offset; 00343 00344 offset = set_dhcp_option ( options, tag, data, len, 0 ); 00345 if ( offset < 0 ) 00346 return offset; 00347 return 0; 00348 }
| int dhcpopt_extensible_store | ( | struct dhcp_options * | options, | |
| unsigned int | tag, | |||
| const void * | data, | |||
| size_t | len | |||
| ) |
Store value of DHCP option setting, extending options block if necessary.
| options | DHCP option block | |
| tag | Setting tag number | |
| data | Setting data, or NULL to clear setting | |
| len | Length of setting data |
| rc | Return status code |
Definition at line 359 of file dhcpopts.c.
References offset, and set_dhcp_option().
00360 { 00361 int offset; 00362 00363 offset = set_dhcp_option ( options, tag, data, len, 1 ); 00364 if ( offset < 0 ) 00365 return offset; 00366 return 0; 00367 }
| int dhcpopt_fetch | ( | struct dhcp_options * | options, | |
| unsigned int | tag, | |||
| void * | data, | |||
| size_t | len | |||
| ) |
Fetch value of DHCP option setting.
| options | DHCP option block | |
| tag | Setting tag number | |
| data | Buffer to fill with setting data | |
| len | Length of buffer |
| len | Length of setting data, or negative error |
Definition at line 378 of file dhcpopts.c.
References dhcp_option::data, dhcp_option(), find_dhcp_option_with_encap(), dhcp_option::len, memcpy, NULL, and offset.
Referenced by dhcppkt_fetch(), and nvo_fetch().
00379 { 00380 int offset; 00381 struct dhcp_option *option; 00382 size_t option_len; 00383 00384 offset = find_dhcp_option_with_encap ( options, tag, NULL ); 00385 if ( offset < 0 ) 00386 return offset; 00387 00388 option = dhcp_option ( options, offset ); 00389 option_len = option->len; 00390 if ( len > option_len ) 00391 len = option_len; 00392 memcpy ( data, option->data, len ); 00393 00394 return option_len; 00395 }
| static void dhcpopt_update_len | ( | struct dhcp_options * | options | ) | [static] |
Recalculate length of DHCP options block.
| options | Uninitialised DHCP option block |
Definition at line 405 of file dhcpopts.c.
References dhcp_option(), dhcp_option_len(), DHCP_PAD, dhcp_options::len, dhcp_options::max_len, offset, and dhcp_option::tag.
Referenced by dhcpopt_init().
00405 { 00406 struct dhcp_option *option; 00407 int offset = 0; 00408 ssize_t remaining = options->max_len; 00409 unsigned int option_len; 00410 00411 /* Find last non-pad option */ 00412 options->len = 0; 00413 while ( remaining ) { 00414 option = dhcp_option ( options, offset ); 00415 option_len = dhcp_option_len ( option ); 00416 remaining -= option_len; 00417 if ( remaining < 0 ) 00418 break; 00419 offset += option_len; 00420 if ( option->tag != DHCP_PAD ) 00421 options->len = offset; 00422 } 00423 }
| void dhcpopt_init | ( | struct dhcp_options * | options, | |
| void * | data, | |||
| size_t | max_len | |||
| ) |
Initialise prepopulated block of DHCP options.
| options | Uninitialised DHCP option block | |
| data | Memory for DHCP option data | |
| max_len | Length of memory for DHCP option data |
Definition at line 435 of file dhcpopts.c.
References dhcp_options::data, DBGC, dhcpopt_update_len(), dhcp_options::len, and dhcp_options::max_len.
Referenced by dhcppkt_init(), and nvo_init_dhcpopts().
00436 { 00437 00438 /* Fill in fields */ 00439 options->data = data; 00440 options->max_len = max_len; 00441 00442 /* Update length */ 00443 dhcpopt_update_len ( options ); 00444 00445 DBGC ( options, "DHCPOPT %p created (data %p len %#zx max_len %#zx)\n", 00446 options, options->data, options->len, options->max_len ); 00447 }
1.5.7.1