dhcpopts.c File Reference

DHCP options. More...

#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_optiondhcp_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.


Detailed Description

DHCP options.

Definition in file dhcpopts.c.


Function Documentation

FILE_LICENCE ( GPL2_OR_LATER   ) 

static char* dhcp_tag_name ( unsigned int  tag  )  [inline, static]

Obtain printable version of a DHCP option tag.

Parameters:
tag DHCP option tag
Return values:
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.

Parameters:
options DHCP options block
offset Offset within options block
Return values:
option DHCP option

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]

Get offset of a DHCP option.

Parameters:
options DHCP options block
option DHCP option
Return values:
offset Offset within options block

Definition at line 75 of file dhcpopts.c.

00076                                                   {
00077         return ( ( ( void * ) option ) - options->data );
00078 }

static unsigned int dhcp_option_len ( struct dhcp_option option  )  [static]

Calculate length of any DHCP option.

Parameters:
option DHCP option
Return values:
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).

Parameters:
options DHCP options block
tag DHCP option tag to search for
Return values:
encap_offset Offset of encapsulating DHCP option
offset Offset of DHCP option, or negative error
Searches for the DHCP option matching the specified tag within the DHCP option block. Encapsulated options may be searched for by using DHCP_ENCAP_OPT() to construct the tag value.

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.

Parameters:
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
Return values:
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.

Parameters:
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
Return values:
offset Offset of DHCP option, or negative error
Sets the value of a DHCP option within the options block. The option may or may not already exist. Encapsulators will be created (and deleted) as necessary.

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.

Parameters:
options DHCP option block
tag Setting tag number
data Setting data, or NULL to clear setting
len Length of setting data
Return values:
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.

Parameters:
options DHCP option block
tag Setting tag number
data Setting data, or NULL to clear setting
len Length of setting data
Return values:
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.

Parameters:
options DHCP option block
tag Setting tag number
data Buffer to fill with setting data
len Length of buffer
Return values:
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.

Parameters:
options Uninitialised DHCP option block
The "used length" field will be updated based on scanning through the block to find the end of the options.

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.

Parameters:
options Uninitialised DHCP option block
data Memory for DHCP option data
max_len Length of memory for DHCP option data
The memory content must already be filled with valid DHCP options. A zeroed block counts as a block of valid DHCP options.

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 }


Generated on Tue Apr 6 20:01:54 2010 for gPXE by  doxygen 1.5.7.1