*/
#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
#include <byteswap.h>
#include <errno.h>
#include <string.h>
-#include <malloc.h>
#include <assert.h>
-#include <vsprintf.h>
#include <gpxe/list.h>
+#include <gpxe/in.h>
+#include <gpxe/uri.h>
#include <gpxe/dhcp.h>
/** @file
*/
/** List of registered DHCP option blocks */
-static LIST_HEAD ( option_blocks );
+LIST_HEAD ( dhcp_option_blocks );
/**
* Obtain printable version of a DHCP option tag
return value;
}
+/**
+ * Obtain value of an IPv4-address DHCP option
+ *
+ * @v option DHCP option, or NULL
+ * @v inp IPv4 address to fill in
+ *
+ * Parses the IPv4 address value from a DHCP option, if present. It
+ * is permitted to call dhcp_ipv4_option() with @c option set to NULL;
+ * in this case the address will be set to 0.0.0.0.
+ */
+void dhcp_ipv4_option ( struct dhcp_option *option, struct in_addr *inp ) {
+ if ( option )
+ *inp = option->data.in;
+}
+
+/**
+ * Print DHCP string option value into buffer
+ *
+ * @v buf Buffer into which to write the string
+ * @v size Size of buffer
+ * @v option DHCP option, or NULL
+ * @ret len Length of formatted string
+ *
+ * DHCP option strings are stored without a NUL terminator. This
+ * function provides a convenient way to extract these DHCP strings
+ * into standard C strings. It is permitted to call dhcp_snprintf()
+ * with @c option set to NULL; in this case the buffer will be filled
+ * with an empty string.
+ *
+ * The usual snprintf() semantics apply with regard to buffer size,
+ * return value when the buffer is too small, etc.
+ */
+int dhcp_snprintf ( char *buf, size_t size, struct dhcp_option *option ) {
+ size_t len;
+ char *content = "";
+
+ if ( option ) {
+ /* Shrink buffer size so that it is only just large
+ * enough to contain the option data. snprintf() will
+ * take care of everything else (inserting the NUL etc.)
+ */
+ len = ( option->len + 1 );
+ if ( len < size )
+ size = len;
+ content = option->data.string;
+ }
+ return snprintf ( buf, size, "%s", content );
+}
+
/**
* Calculate length of a normal DHCP option
*
break;
/* Check for matching tag */
if ( option->tag == tag ) {
- DBG ( "Found DHCP option %s (length %d)\n",
- dhcp_tag_name ( original_tag ), option->len );
+ DBG ( "Found DHCP option %s (length %d) in block %p\n",
+ dhcp_tag_name ( original_tag ), option->len,
+ options );
return option;
}
/* Check for explicit end marker */
if ( options ) {
return find_dhcp_option_with_encap ( options, tag, NULL );
} else {
- list_for_each_entry ( options, &option_blocks, list ) {
+ list_for_each_entry ( options, &dhcp_option_blocks, list ) {
if ( ( option = find_dhcp_option ( options, tag ) ) )
return option;
}
/* Determine priority of new block */
options->priority = find_dhcp_num_option ( options, DHCP_EB_PRIORITY );
- DBG ( "Registering DHCP options block with priority %d\n",
- options->priority );
+ DBG ( "Registering DHCP options block %p with priority %d\n",
+ options, options->priority );
/* Insert after any existing blocks which have a higher priority */
- list_for_each_entry ( existing, &option_blocks, list ) {
+ list_for_each_entry ( existing, &dhcp_option_blocks, list ) {
if ( options->priority > existing->priority )
break;
}
+ dhcpopt_get ( options );
list_add_tail ( &options->list, &existing->list );
+
+ /* Apply all registered DHCP options */
+ apply_global_dhcp_options();
}
/**
*/
void unregister_dhcp_options ( struct dhcp_option_block *options ) {
list_del ( &options->list );
+ dhcpopt_put ( options );
}
/**
option = options->data;
option->tag = DHCP_END;
options->len = 1;
+
+ DBG ( "DHCP options block %p initialised (data %p max_len %#zx)\n",
+ options, options->data, options->max_len );
}
/**
return options;
}
-/**
- * Free DHCP options block
- *
- * @v options DHCP option block
- */
-void free_dhcp_options ( struct dhcp_option_block *options ) {
- free ( options );
-}
-
/**
* Resize a DHCP option
*
const void *data, size_t len ) {
static const uint8_t empty_encapsulator[] = { DHCP_END };
struct dhcp_option *option;
- void *insertion_point = options->data;
+ void *insertion_point;
struct dhcp_option *encapsulator = NULL;
unsigned int encap_tag = DHCP_ENCAPSULATOR ( tag );
size_t old_len = 0;
size_t new_len = ( len ? ( len + DHCP_OPTION_HEADER_LEN ) : 0 );
+ /* Return NULL if no options block specified */
+ if ( ! options )
+ return NULL;
+
/* Find old instance of this option, if any */
option = find_dhcp_option_with_encap ( options, tag, &encapsulator );
if ( option ) {
old_len = dhcp_option_len ( option );
- DBG ( "Resizing DHCP option %s from length %d to %d\n",
- dhcp_tag_name ( tag ), option->len, len );
+ DBG ( "Resizing DHCP option %s from length %d to %zd in block "
+ "%p\n", dhcp_tag_name (tag), option->len, len, options );
} else {
old_len = 0;
- DBG ( "Creating DHCP option %s (length %d)\n",
- dhcp_tag_name ( tag ), new_len );
+ DBG ( "Creating DHCP option %s (length %zd) in block %p\n",
+ dhcp_tag_name ( tag ), len, options );
}
/* Ensure that encapsulator exists, if required */
+ insertion_point = options->data;
if ( DHCP_IS_ENCAP_OPT ( tag ) ) {
if ( ! encapsulator )
encapsulator = set_dhcp_option ( options, encap_tag,
return dhcp_num_option ( find_global_dhcp_option ( tag ) );
}
+/**
+ * Find DHCP IPv4-address option, and return its value
+ *
+ * @v options DHCP options block
+ * @v tag DHCP option tag to search for
+ * @v inp IPv4 address to fill in
+ * @ret value Numerical value of the option, or 0 if not found
+ *
+ * This function exists merely as a notational shorthand for a call to
+ * find_dhcp_option() followed by a call to dhcp_ipv4_option(). It is
+ * not possible to distinguish between the cases "option not found"
+ * and "option has a value of 0.0.0.0" using this function; if this
+ * matters to you then issue the two constituent calls directly and
+ * check that find_dhcp_option() returns a non-NULL value.
+ */
+void find_dhcp_ipv4_option ( struct dhcp_option_block *options,
+ unsigned int tag, struct in_addr *inp ) {
+ dhcp_ipv4_option ( find_dhcp_option ( options, tag ), inp );
+}
+
+/**
+ * Find DHCP IPv4-address option, and return its value
+ *
+ * @v options DHCP options block
+ * @v tag DHCP option tag to search for
+ * @v inp IPv4 address to fill in
+ * @ret value Numerical value of the option, or 0 if not found
+ *
+ * This function exists merely as a notational shorthand for a call to
+ * find_dhcp_option() followed by a call to dhcp_ipv4_option(). It is
+ * not possible to distinguish between the cases "option not found"
+ * and "option has a value of 0.0.0.0" using this function; if this
+ * matters to you then issue the two constituent calls directly and
+ * check that find_dhcp_option() returns a non-NULL value.
+ */
+void find_global_dhcp_ipv4_option ( unsigned int tag, struct in_addr *inp ) {
+ dhcp_ipv4_option ( find_global_dhcp_option ( tag ), inp );
+}
+
/**
* Delete DHCP option
*
unsigned int tag ) {
set_dhcp_option ( options, tag, NULL, 0 );
}
+
+/**
+ * Apply DHCP options
+ *
+ * @v options DHCP options block, or NULL
+ * @ret rc Return status code
+ */
+int apply_dhcp_options ( struct dhcp_option_block *options ) {
+ struct in_addr tftp_server;
+ struct uri *uri;
+ char uri_string[32];
+
+ /* Set current working URI based on TFTP server */
+ find_dhcp_ipv4_option ( options, DHCP_EB_SIADDR, &tftp_server );
+ snprintf ( uri_string, sizeof ( uri_string ),
+ "tftp://%s/", inet_ntoa ( tftp_server ) );
+ uri = parse_uri ( uri_string );
+ if ( ! uri )
+ return -ENOMEM;
+ churi ( uri );
+ uri_put ( uri );
+
+ return 0;
+}
+
+/**
+ * Apply global DHCP options
+ *
+ * @ret rc Return status code
+ */
+int apply_global_dhcp_options ( void ) {
+ return apply_dhcp_options ( NULL );
+}