iPXE
Functions
dhcpopts.c File Reference

DHCP options. More...

#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <ipxe/dhcp.h>
#include <ipxe/dhcpopts.h>

Go to the source code of this file.

Functions

 FILE_LICENCE (GPL2_OR_LATER_OR_UBDL)
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)
int dhcpopt_no_realloc (struct dhcp_options *options, size_t len)
 Refuse to reallocate DHCP option block.
static int resize_dhcp_option (struct dhcp_options *options, int offset, int encap_offset, size_t old_len, size_t new_len)
 Resize a DHCP option.
static int set_dhcp_option (struct dhcp_options *options, unsigned int tag, const void *data, size_t len)
 Set value of DHCP option.
int dhcpopt_applies (unsigned int tag)
 Check applicability of DHCP option setting.
int dhcpopt_store (struct dhcp_options *options, unsigned int tag, const void *data, size_t len)
 Store value of DHCP option setting.
int dhcpopt_fetch (struct dhcp_options *options, unsigned int tag, void *data, size_t len)
 Fetch value of DHCP option setting.
void dhcpopt_update_used_len (struct dhcp_options *options)
 Recalculate length of DHCP options block.
void dhcpopt_init (struct dhcp_options *options, void *data, size_t alloc_len, int(*realloc)(struct dhcp_options *options, size_t len))
 Initialise prepopulated block of DHCP options.

Detailed Description

DHCP options.

Definition in file dhcpopts.c.


Function Documentation

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

Obtain printable version of a DHCP option tag.

Parameters:
tagDHCP option tag
Return values:
nameString representation of the tag

Definition at line 47 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().

                                                        {
        static char name[8];

        if ( DHCP_IS_ENCAP_OPT ( tag ) ) {
                snprintf ( name, sizeof ( name ), "%d.%d",
                           DHCP_ENCAPSULATOR ( tag ),
                           DHCP_ENCAPSULATED ( tag ) );
        } else {
                snprintf ( name, sizeof ( name ), "%d", tag );
        }
        return name;
}
static struct dhcp_option* dhcp_option ( struct dhcp_options options,
unsigned int  offset 
) [static, read]

Get pointer to DHCP option.

Parameters:
optionsDHCP options block
offsetOffset within options block
Return values:
optionDHCP option

Definition at line 68 of file dhcpopts.c.

Referenced by dhcpopt_fetch(), dhcpopt_update_used_len(), find_dhcp_option_with_encap(), resize_dhcp_option(), and set_dhcp_option().

                                                                  {
        return ( ( struct dhcp_option * ) ( options->data + offset ) );
}
static int dhcp_option_offset ( struct dhcp_options options,
struct dhcp_option option 
) [inline, static]

Get offset of a DHCP option.

Parameters:
optionsDHCP options block
optionDHCP option
Return values:
offsetOffset within options block

Definition at line 80 of file dhcpopts.c.

                                                  {
        return ( ( ( void * ) option ) - options->data );
}
static unsigned int dhcp_option_len ( struct dhcp_option option) [static]

Calculate length of any DHCP option.

Parameters:
optionDHCP option
Return values:
lenLength (including tag and length field)

Definition at line 91 of file dhcpopts.c.

References DHCP_END, DHCP_OPTION_HEADER_LEN, DHCP_PAD, dhcp_option::len, and dhcp_option::tag.

Referenced by dhcpopt_update_used_len(), find_dhcp_option_with_encap(), and set_dhcp_option().

                                                                   {
        if ( ( option->tag == DHCP_END ) || ( option->tag == DHCP_PAD ) ) {
                return 1;
        } else {
                return ( option->len + DHCP_OPTION_HEADER_LEN );
        }
}
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:
optionsDHCP options block
tagDHCP option tag to search for
Return values:
encap_offsetOffset of encapsulating DHCP option
offsetOffset 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 119 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, offset, tag, dhcp_option::tag, unused, and dhcp_options::used_len.

Referenced by dhcpopt_fetch(), and set_dhcp_option().

                                                             {
        unsigned int original_tag __attribute__ (( unused )) = tag;
        struct dhcp_option *option;
        int offset = 0;
        ssize_t remaining = options->used_len;
        unsigned int option_len;

        /* Sanity check */
        if ( tag == DHCP_PAD )
                return -ENOENT;

        /* Search for option */
        while ( remaining ) {
                /* Calculate length of this option.  Abort processing
                 * if the length is malformed (i.e. takes us beyond
                 * the end of the data block).
                 */
                option = dhcp_option ( options, offset );
                option_len = dhcp_option_len ( option );
                remaining -= option_len;
                if ( remaining < 0 )
                        break;
                /* Check for explicit end marker */
                if ( option->tag == DHCP_END ) {
                        if ( tag == DHCP_END )
                                /* Special case where the caller is interested
                                 * in whether we have this marker or not.
                                 */
                                return offset;
                        else
                                break;
                }
                /* Check for matching tag */
                if ( option->tag == tag ) {
                        DBGC ( options, "DHCPOPT %p found %s (length %d)\n",
                               options, dhcp_tag_name ( original_tag ),
                               option_len );
                        return offset;
                }
                /* Check for start of matching encapsulation block */
                if ( DHCP_IS_ENCAP_OPT ( tag ) &&
                     ( option->tag == DHCP_ENCAPSULATOR ( tag ) ) ) {
                        if ( encap_offset )
                                *encap_offset = offset;
                        /* Continue search within encapsulated option block */
                        tag = DHCP_ENCAPSULATED ( tag );
                        remaining = option_len;
                        offset += DHCP_OPTION_HEADER_LEN;
                        continue;
                }
                offset += option_len;
        }

        return -ENOENT;
}
int dhcpopt_no_realloc ( struct dhcp_options options,
size_t  len 
)

Refuse to reallocate DHCP option block.

Parameters:
optionsDHCP option block
lenNew length
Return values:
rcReturn status code

Definition at line 184 of file dhcpopts.c.

References ENOSPC.

Referenced by dhcppkt_init(), and nvo_realloc_dhcpopt().

                                                                    {
        return ( ( len <= options->alloc_len ) ? 0 : -ENOSPC );
}
static int resize_dhcp_option ( struct dhcp_options options,
int  offset,
int  encap_offset,
size_t  old_len,
size_t  new_len 
) [static]

Resize a DHCP option.

Parameters:
optionsDHCP option block
offsetOffset of option to resize
encap_offsetOffset of encapsulating offset (or -ve for none)
old_lenOld length (including header)
new_lenNew length (including header)
Return values:
rcReturn status code

Definition at line 198 of file dhcpopts.c.

References dhcp_options::alloc_len, dhcp_options::data, DBGC, dest, DHCP_MAX_LEN, dhcp_option(), ENOSPC, dhcp_option::len, memmove(), memset(), rc, dhcp_options::realloc, and dhcp_options::used_len.

Referenced by set_dhcp_option().

                                                                 {
        struct dhcp_option *encapsulator;
        struct dhcp_option *option;
        ssize_t delta = ( new_len - old_len );
        size_t old_alloc_len;
        size_t new_used_len;
        size_t new_encapsulator_len;
        void *source;
        void *dest;
        int rc;

        /* Check for sufficient space */
        if ( new_len > DHCP_MAX_LEN ) {
                DBGC ( options, "DHCPOPT %p overlength option\n", options );
                return -ENOSPC;
        }
        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 );
                if ( new_encapsulator_len > DHCP_MAX_LEN ) {
                        DBGC ( options, "DHCPOPT %p overlength encapsulator\n",
                               options );
                        return -ENOSPC;
                }
                encapsulator->len = new_encapsulator_len;
        }

        /* Update used length */
        options->used_len = new_used_len;

        /* Move remainder of option data */
        option = dhcp_option ( options, offset );
        source = ( ( ( void * ) option ) + old_len );
        dest = ( ( ( void * ) option ) + new_len );
        memmove ( dest, source, ( new_used_len - offset - new_len ) );

        /* 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;
}
static int set_dhcp_option ( struct dhcp_options options,
unsigned int  tag,
const void *  data,
size_t  len 
) [static]

Set value of DHCP option.

Parameters:
optionsDHCP option block
tagDHCP option tag
dataNew value for DHCP option
lenLength of value, in bytes
Return values:
offsetOffset 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 282 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(), len, dhcp_option::len, memcpy(), NULL, offset, rc, resize_dhcp_option(), tag, dhcp_option::tag, and dhcp_options::used_len.

Referenced by dhcpopt_store().

                                                            {
        static const uint8_t empty_encap[] = { DHCP_END };
        int offset;
        int encap_offset = -1;
        int creation_offset;
        struct dhcp_option *option;
        unsigned int encap_tag = DHCP_ENCAPSULATOR ( tag );
        size_t old_len = 0;
        size_t new_len = ( len ? ( len + DHCP_OPTION_HEADER_LEN ) : 0 );
        int rc;

        /* Sanity check */
        if ( tag == DHCP_PAD )
                return -ENOTTY;

        creation_offset = find_dhcp_option_with_encap ( options, DHCP_END,
                                                        NULL );
        if ( creation_offset < 0 )
                creation_offset = options->used_len;
        /* Find old instance of this option, if any */
        offset = find_dhcp_option_with_encap ( options, tag, &encap_offset );
        if ( offset >= 0 ) {
                old_len = dhcp_option_len ( dhcp_option ( options, offset ) );
                DBGC ( options, "DHCPOPT %p resizing %s from %zd to %zd\n",
                       options, dhcp_tag_name ( tag ), old_len, new_len );
        } else {
                DBGC ( options, "DHCPOPT %p creating %s (length %zd)\n",
                       options, dhcp_tag_name ( tag ), new_len );
        }

        /* Ensure that encapsulator exists, if required */
        if ( encap_tag ) {
                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 );
        }

        /* Create new option if necessary */
        if ( offset < 0 )
                offset = creation_offset;

        /* Resize option to fit new data */
        if ( ( rc = resize_dhcp_option ( options, offset, encap_offset,
                                         old_len, new_len ) ) != 0 )
                return rc;

        /* Copy new data into option, if applicable */
        if ( len ) {
                option = dhcp_option ( options, offset );
                option->tag = tag;
                option->len = len;
                memcpy ( &option->data, data, len );
        }

        /* Delete encapsulator if there's nothing else left in it */
        if ( encap_offset >= 0 ) {
                option = dhcp_option ( options, encap_offset );
                if ( option->len <= 1 )
                        set_dhcp_option ( options, encap_tag, NULL, 0 );
        }

        return offset;
}
int dhcpopt_applies ( unsigned int  tag)

Check applicability of DHCP option setting.

Parameters:
tagSetting tag number
Return values:
appliesSetting applies to this option block

Definition at line 359 of file dhcpopts.c.

References DHCP_ENCAP_OPT, and DHCP_MAX_OPTION.

Referenced by dhcppkt_applies(), and nvo_applies().

                                         {

        return ( tag && ( tag <= DHCP_ENCAP_OPT ( DHCP_MAX_OPTION,
                                                  DHCP_MAX_OPTION ) ) );
}
int dhcpopt_store ( struct dhcp_options options,
unsigned int  tag,
const void *  data,
size_t  len 
)

Store value of DHCP option setting.

Parameters:
optionsDHCP option block
tagSetting tag number
dataSetting data, or NULL to clear setting
lenLength of setting data
Return values:
rcReturn status code

Definition at line 374 of file dhcpopts.c.

References offset, and set_dhcp_option().

Referenced by dhcppkt_store(), and nvo_store().

                                                   {
        int offset;

        offset = set_dhcp_option ( options, tag, data, len );
        if ( offset < 0 )
                return offset;
        return 0;
}
int dhcpopt_fetch ( struct dhcp_options options,
unsigned int  tag,
void *  data,
size_t  len 
)

Fetch value of DHCP option setting.

Parameters:
optionsDHCP option block
tagSetting tag number
dataBuffer to fill with setting data
lenLength of buffer
Return values:
lenLength of setting data, or negative error

Definition at line 393 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().

                                             {
        int offset;
        struct dhcp_option *option;
        size_t option_len;

        offset = find_dhcp_option_with_encap ( options, tag, NULL );
        if ( offset < 0 )
                return offset;

        option = dhcp_option ( options, offset );
        option_len = option->len;
        if ( len > option_len )
                len = option_len;
        memcpy ( data, option->data, len );

        return option_len;
}
void dhcpopt_update_used_len ( struct dhcp_options options)

Recalculate length of DHCP options block.

Parameters:
optionsUninitialised 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 420 of file dhcpopts.c.

References dhcp_options::alloc_len, dhcp_option(), dhcp_option_len(), DHCP_PAD, offset, dhcp_option::tag, and dhcp_options::used_len.

Referenced by dhcpopt_init(), and nvo_load().

                                                              {
        struct dhcp_option *option;
        int offset = 0;
        ssize_t remaining = options->alloc_len;
        unsigned int option_len;

        /* Find last non-pad option */
        options->used_len = 0;
        while ( remaining ) {
                option = dhcp_option ( options, offset );
                option_len = dhcp_option_len ( option );
                remaining -= option_len;
                if ( remaining < 0 )
                        break;
                offset += option_len;
                if ( option->tag != DHCP_PAD )
                        options->used_len = offset;
        }
}
void dhcpopt_init ( struct dhcp_options options,
void *  data,
size_t  alloc_len,
int(*)(struct dhcp_options *options, size_t len realloc 
)

Initialise prepopulated block of DHCP options.

Parameters:
optionsUninitialised DHCP option block
dataMemory for DHCP option data
alloc_lenLength of memory for DHCP option data
reallocDHCP 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.

Definition at line 451 of file dhcpopts.c.

References dhcp_options::alloc_len, dhcp_options::data, data, DBGC, dhcpopt_update_used_len(), dhcp_options::realloc, realloc(), and dhcp_options::used_len.

Referenced by dhcppkt_init(), and nvo_init().

                                                       {

        /* Fill in fields */
        options->data = data;
        options->alloc_len = alloc_len;
        options->realloc = realloc;

        /* Update length */
        dhcpopt_update_used_len ( options );

        DBGC ( options, "DHCPOPT %p created (data %p lengths %#zx,%#zx)\n",
               options, options->data, options->used_len, options->alloc_len );
}