123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217 |
- /*
- * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of the
- * License, or any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
- #include <stdint.h>
- #include <byteswap.h>
- #include <errno.h>
- #include <malloc.h>
- #include <assert.h>
- #include <gpxe/list.h>
- #include <gpxe/dhcp.h>
-
- /** @file
- *
- * DHCP options
- *
- */
-
- /** List of registered DHCP option blocks */
- static LIST_HEAD ( option_blocks );
-
- /**
- * Obtain value of a numerical DHCP option
- *
- * @v option DHCP option, or NULL
- * @v value Unsigned long for storing the result
- * @ret rc Return status code
- *
- * Parses the numerical value from a DHCP option, if present. It is
- * permitted to call dhcp_num_option() with @c option set to NULL; in
- * this case the result value will not be modified and an error will
- * be returned.
- *
- * The caller does not specify the size of the DHCP option data; this
- * is implied by the length field stored within the DHCP option
- * itself.
- */
- int dhcp_num_option ( struct dhcp_option *option, unsigned long *value ) {
- uint8_t *data;
- unsigned long tmp = 0;
-
- if ( ! option )
- return -EINVAL;
-
- /* This is actually smaller code than using htons() etc., and
- * will also cope well with malformed options (such as
- * zero-length options).
- */
- for ( data = option->data.bytes ;
- data < ( option->data.bytes + option->len ) ; data++ )
- tmp = ( ( tmp << 8 ) | *data );
- *value = tmp;
- return 0;
- }
-
- /**
- * Calculate length of a DHCP option
- *
- * @v option DHCP option
- * @ret len Length (including tag and length field)
- */
- static inline unsigned int dhcp_option_len ( struct dhcp_option *option ) {
- if ( ( option->tag == DHCP_END ) || ( option->tag == DHCP_PAD ) ) {
- return 1;
- } else {
- return ( option->len + 2 );
- }
- }
-
- /**
- * Find DHCP option within block of raw data
- *
- * @v tag DHCP option tag to search for
- * @v data Data block
- * @v len Length of data block
- * @ret option DHCP option, or NULL if not found
- *
- * Searches for the DHCP option matching the specified tag within the
- * block of data. Encapsulated options may be searched for by using
- * DHCP_ENCAP_OPT() to construct the tag value.
- *
- * 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 @c DHCP_END terminator, or options whose
- * length would take them beyond the end of the data block.
- *
- * Searching for @c DHCP_PAD or @c DHCP_END tags, or using either @c
- * DHCP_PAD or @c DHCP_END as the encapsulator when constructing the
- * tag via DHCP_ENCAP_OPT() will produce undefined behaviour.
- */
- static struct dhcp_option * find_dhcp_option_raw ( unsigned int tag,
- void *data, size_t len ) {
- struct dhcp_option *option = data;
- ssize_t remaining = len;
- unsigned int option_len;
-
- assert ( tag != DHCP_PAD );
- assert ( tag != DHCP_END );
- assert ( DHCP_ENCAPSULATOR ( tag ) != DHCP_END );
-
- while ( remaining ) {
- /* Check for explicit end marker */
- if ( option->tag == DHCP_END )
- break;
- /* Calculate length of this option. Abort processing
- * if the length is malformed (i.e. takes us beyond
- * the end of the data block).
- */
- option_len = dhcp_option_len ( option );
- remaining -= option_len;
- if ( remaining < 0 )
- break;
- /* Check for matching tag */
- if ( option->tag == tag )
- return option;
- /* Check for start of matching encapsulation block */
- if ( DHCP_ENCAPSULATOR ( tag ) &&
- ( option->tag == DHCP_ENCAPSULATOR ( tag ) ) ) {
- /* Search within encapsulated option block */
- return find_dhcp_option_raw ( DHCP_ENCAPSULATED( tag ),
- &option->data,
- option->len );
- }
- option = ( ( ( void * ) option ) + option_len );
- }
- return NULL;
- }
-
- /**
- * Find DHCP option within all registered DHCP options blocks
- *
- * @v tag DHCP option tag to search for
- * @ret option DHCP option, or NULL if not found
- *
- * Searches within all registered DHCP option blocks for the specified
- * tag. Encapsulated options may be searched for by using
- * DHCP_ENCAP_OPT() to construct the tag value.
- */
- struct dhcp_option * find_dhcp_option ( unsigned int tag ) {
- struct dhcp_option_block *options;
- struct dhcp_option *option;
-
- list_for_each_entry ( options, &option_blocks, list ) {
- if ( ( option = find_dhcp_option_raw ( tag, options->data,
- options->len ) ) )
- return option;
- }
- return NULL;
- }
-
- /**
- * Register DHCP option block
- *
- * @v options DHCP option block
- *
- * Register a block of DHCP options
- */
- void register_dhcp_options ( struct dhcp_option_block *options ) {
- list_add ( &options->list, &option_blocks );
- }
-
- /**
- * Unregister DHCP option block
- *
- * @v options DHCP option block
- */
- void unregister_dhcp_options ( struct dhcp_option_block *options ) {
- list_del ( &options->list );
- }
-
- /**
- * Allocate space for a block of DHCP options
- *
- * @v len Maximum length of option block
- * @ret options Option block, or NULL
- *
- * Creates a new DHCP option block and populates it with an empty
- * options list. This call does not register the options block.
- */
- struct dhcp_option_block * alloc_dhcp_options ( size_t len ) {
- struct dhcp_option_block *options;
- struct dhcp_option *option;
-
- options = malloc ( sizeof ( *options ) + len );
- if ( options ) {
- options->data = ( ( void * ) options + sizeof ( *options ) );
- options->len = len;
- if ( len ) {
- option = options->data;
- option->tag = DHCP_END;
- }
- }
- return options;
- }
-
- /**
- * Free DHCP options block
- *
- * @v options Option block
- */
- void free_dhcp_options ( struct dhcp_option_block *options ) {
- free ( options );
- }
|