summaryrefslogtreecommitdiffstats
path: root/src/net/dhcpopts.c
diff options
context:
space:
mode:
authorMichael Brown2006-06-27 19:24:07 +0200
committerMichael Brown2006-06-27 19:24:07 +0200
commit0bcaa8a21f95027617ca6fe438b187d638634bac (patch)
tree087eb0dae9911cd02fb14eafbf5bc4728c467961 /src/net/dhcpopts.c
parent- put in the screen initialisation stuff and clear the screen... (diff)
downloadipxe-0bcaa8a21f95027617ca6fe438b187d638634bac.tar.gz
ipxe-0bcaa8a21f95027617ca6fe438b187d638634bac.tar.xz
ipxe-0bcaa8a21f95027617ca6fe438b187d638634bac.zip
First stab at DHCP option handling in a way that will allow us to have
multiple option sources (e.g. multiple DHCP replies, non-volatile storage etc.)
Diffstat (limited to 'src/net/dhcpopts.c')
-rw-r--r--src/net/dhcpopts.c217
1 files changed, 217 insertions, 0 deletions
diff --git a/src/net/dhcpopts.c b/src/net/dhcpopts.c
new file mode 100644
index 00000000..4fe5bcf1
--- /dev/null
+++ b/src/net/dhcpopts.c
@@ -0,0 +1,217 @@
+/*
+ * 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 );
+}