mirror of
				https://github.com/xcat2/xNBA.git
				synced 2025-11-04 05:12:33 +00:00 
			
		
		
		
	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.)
This commit is contained in:
		
							
								
								
									
										87
									
								
								src/include/gpxe/dhcp.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										87
									
								
								src/include/gpxe/dhcp.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,87 @@
 | 
			
		||||
#ifndef _GPXE_DHCP_H
 | 
			
		||||
#define _GPXE_DHCP_H
 | 
			
		||||
 | 
			
		||||
/** @file
 | 
			
		||||
 *
 | 
			
		||||
 * Dynamic Host Configuration Protocol
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <gpxe/list.h>
 | 
			
		||||
 | 
			
		||||
/** Construct a tag value for an encapsulated option
 | 
			
		||||
 *
 | 
			
		||||
 * This tag value can be passed to Etherboot functions when searching
 | 
			
		||||
 * for DHCP options in order to search for a tag within an
 | 
			
		||||
 * encapsulated options block.
 | 
			
		||||
 */
 | 
			
		||||
#define DHCP_ENCAP_OPT( encapsulator, encapsulated ) \
 | 
			
		||||
	( ( (encapsulator) << 8 ) | (encapsulated) )
 | 
			
		||||
/** Extract encapsulating option block tag from encapsulated tag value */
 | 
			
		||||
#define DHCP_ENCAPSULATOR( encap_opt ) ( (encap_opt) >> 8 )
 | 
			
		||||
/** Extract encapsulated option tag from encapsulated tag value */
 | 
			
		||||
#define DHCP_ENCAPSULATED( encap_opt ) ( (encap_opt) & 0xff )
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @defgroup dhcpopts DHCP option tags
 | 
			
		||||
 * @{
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#define DHCP_PAD 0
 | 
			
		||||
#define DHCP_END 255
 | 
			
		||||
 | 
			
		||||
#define DHCP_EB_ENCAP 175
 | 
			
		||||
 | 
			
		||||
#define DHCP_EB_PRIORITY DHCP_ENCAP_OPT ( DHCP_EB_ENCAP, 1 )
 | 
			
		||||
 | 
			
		||||
/** @} */
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * A DHCP option
 | 
			
		||||
 *
 | 
			
		||||
 * DHCP options consist of a mandatory tag, a length field that is
 | 
			
		||||
 * mandatory for all options except @c DHCP_PAD and @c DHCP_END, and a
 | 
			
		||||
 * payload.  
 | 
			
		||||
 */
 | 
			
		||||
struct dhcp_option {
 | 
			
		||||
	/** Tag
 | 
			
		||||
	 *
 | 
			
		||||
	 * Must be a @c DHCP_XXX value.
 | 
			
		||||
	 */
 | 
			
		||||
	uint8_t tag;
 | 
			
		||||
	/** Length
 | 
			
		||||
	 *
 | 
			
		||||
	 * This is the length of the data field (i.e. excluding the
 | 
			
		||||
	 * tag and length fields).  For the two tags @c DHCP_PAD and
 | 
			
		||||
	 * @c DHCP_END, the length field is implicitly zero and is
 | 
			
		||||
	 * also missing, i.e. these DHCP options are only a single
 | 
			
		||||
	 * byte in length.
 | 
			
		||||
	 */
 | 
			
		||||
	uint8_t len;
 | 
			
		||||
	/** Option data
 | 
			
		||||
	 *
 | 
			
		||||
	 * Interpretation of the content is entirely dependent upon
 | 
			
		||||
	 * the tag.  For fields containing a multi-byte integer, the
 | 
			
		||||
	 * field is defined to be in network-endian order (unless you
 | 
			
		||||
	 * are Intel and feel like violating the spec for fun).
 | 
			
		||||
	 */
 | 
			
		||||
	union {
 | 
			
		||||
		uint8_t byte;
 | 
			
		||||
		uint16_t word;
 | 
			
		||||
		uint32_t dword;
 | 
			
		||||
		uint8_t bytes[0];
 | 
			
		||||
	} data;
 | 
			
		||||
} __attribute__ (( packed ));
 | 
			
		||||
 | 
			
		||||
/** A DHCP options block */
 | 
			
		||||
struct dhcp_option_block {
 | 
			
		||||
	/** List of option blocks */
 | 
			
		||||
	struct list_head list;
 | 
			
		||||
	/** Option block raw data */
 | 
			
		||||
	void *data;
 | 
			
		||||
	/** Option block length */
 | 
			
		||||
	size_t len;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif /* _GPXE_DHCP_H */
 | 
			
		||||
							
								
								
									
										217
									
								
								src/net/dhcpopts.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										217
									
								
								src/net/dhcpopts.c
									
									
									
									
									
										Normal file
									
								
							@@ -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 );
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user