diff options
Diffstat (limited to 'contrib/syslinux-4.02/gpxe/src/net/dhcppkt.c')
-rw-r--r-- | contrib/syslinux-4.02/gpxe/src/net/dhcppkt.c | 283 |
1 files changed, 283 insertions, 0 deletions
diff --git a/contrib/syslinux-4.02/gpxe/src/net/dhcppkt.c b/contrib/syslinux-4.02/gpxe/src/net/dhcppkt.c new file mode 100644 index 0000000..20a0e66 --- /dev/null +++ b/contrib/syslinux-4.02/gpxe/src/net/dhcppkt.c @@ -0,0 +1,283 @@ +/* + * Copyright (C) 2008 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. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include <stdint.h> +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> +#include <string.h> +#include <gpxe/netdevice.h> +#include <gpxe/dhcp.h> +#include <gpxe/dhcpopts.h> +#include <gpxe/dhcppkt.h> + +/** @file + * + * DHCP packets + * + */ + +/**************************************************************************** + * + * DHCP packet raw interface + * + */ + +/** + * Calculate used length of an IPv4 field within a DHCP packet + * + * @v data Field data + * @v len Length of field + * @ret used Used length of field + */ +static size_t used_len_ipv4 ( const void *data, size_t len __unused ) { + const struct in_addr *in = data; + + return ( in->s_addr ? sizeof ( *in ) : 0 ); +} + +/** + * Calculate used length of a string field within a DHCP packet + * + * @v data Field data + * @v len Length of field + * @ret used Used length of field + */ +static size_t used_len_string ( const void *data, size_t len ) { + return strnlen ( data, len ); +} + +/** A dedicated field within a DHCP packet */ +struct dhcp_packet_field { + /** Settings tag number */ + unsigned int tag; + /** Offset within DHCP packet */ + uint16_t offset; + /** Length of field */ + uint16_t len; + /** Calculate used length of field + * + * @v data Field data + * @v len Length of field + * @ret used Used length of field + */ + size_t ( * used_len ) ( const void *data, size_t len ); +}; + +/** Declare a dedicated field within a DHCP packet + * + * @v _tag Settings tag number + * @v _field Field name + * @v _used_len Function to calculate used length of field + */ +#define DHCP_PACKET_FIELD( _tag, _field, _used_len ) { \ + .tag = (_tag), \ + .offset = offsetof ( struct dhcphdr, _field ), \ + .len = sizeof ( ( ( struct dhcphdr * ) 0 )->_field ), \ + .used_len = _used_len, \ + } + +/** Dedicated fields within a DHCP packet */ +static struct dhcp_packet_field dhcp_packet_fields[] = { + DHCP_PACKET_FIELD ( DHCP_EB_YIADDR, yiaddr, used_len_ipv4 ), + DHCP_PACKET_FIELD ( DHCP_EB_SIADDR, siaddr, used_len_ipv4 ), + DHCP_PACKET_FIELD ( DHCP_TFTP_SERVER_NAME, sname, used_len_string ), + DHCP_PACKET_FIELD ( DHCP_BOOTFILE_NAME, file, used_len_string ), +}; + +/** + * Get address of a DHCP packet field + * + * @v dhcphdr DHCP packet header + * @v field DHCP packet field + * @ret data Packet field data + */ +static inline void * dhcp_packet_field ( struct dhcphdr *dhcphdr, + struct dhcp_packet_field *field ) { + return ( ( ( void * ) dhcphdr ) + field->offset ); +} + +/** + * Find DHCP packet field corresponding to settings tag number + * + * @v tag Settings tag number + * @ret field DHCP packet field, or NULL + */ +static struct dhcp_packet_field * +find_dhcp_packet_field ( unsigned int tag ) { + struct dhcp_packet_field *field; + unsigned int i; + + for ( i = 0 ; i < ( sizeof ( dhcp_packet_fields ) / + sizeof ( dhcp_packet_fields[0] ) ) ; i++ ) { + field = &dhcp_packet_fields[i]; + if ( field->tag == tag ) + return field; + } + return NULL; +} + +/** + * Store value of DHCP packet setting + * + * @v dhcppkt DHCP packet + * @v tag Setting tag number + * @v data Setting data, or NULL to clear setting + * @v len Length of setting data + * @ret rc Return status code + */ +int dhcppkt_store ( struct dhcp_packet *dhcppkt, unsigned int tag, + const void *data, size_t len ) { + struct dhcp_packet_field *field; + void *field_data; + int rc; + + /* If this is a special field, fill it in */ + if ( ( field = find_dhcp_packet_field ( tag ) ) != NULL ) { + if ( len > field->len ) + return -ENOSPC; + field_data = dhcp_packet_field ( dhcppkt->dhcphdr, field ); + memset ( field_data, 0, field->len ); + memcpy ( dhcp_packet_field ( dhcppkt->dhcphdr, field ), + data, len ); + /* Erase any equivalent option from the options block */ + dhcpopt_store ( &dhcppkt->options, tag, NULL, 0 ); + return 0; + } + + /* Otherwise, use the generic options block */ + rc = dhcpopt_store ( &dhcppkt->options, tag, data, len ); + + /* Update our used-length field */ + dhcppkt->len = ( offsetof ( struct dhcphdr, options ) + + dhcppkt->options.len ); + + return rc; +} + +/** + * Fetch value of DHCP packet setting + * + * @v dhcppkt DHCP packet + * @v tag Setting tag number + * @v data Buffer to fill with setting data + * @v len Length of buffer + * @ret len Length of setting data, or negative error + */ +int dhcppkt_fetch ( struct dhcp_packet *dhcppkt, unsigned int tag, + void *data, size_t len ) { + struct dhcp_packet_field *field; + void *field_data; + size_t field_len = 0; + + /* Identify special field, if any */ + if ( ( field = find_dhcp_packet_field ( tag ) ) != NULL ) { + field_data = dhcp_packet_field ( dhcppkt->dhcphdr, field ); + field_len = field->used_len ( field_data, field->len ); + } + + /* Return special field, if it exists and is populated */ + if ( field_len ) { + if ( len > field_len ) + len = field_len; + memcpy ( data, field_data, len ); + return field_len; + } + + /* Otherwise, use the generic options block */ + return dhcpopt_fetch ( &dhcppkt->options, tag, data, len ); +} + +/**************************************************************************** + * + * DHCP packet settings interface + * + */ + +/** + * Store value of DHCP setting + * + * @v settings Settings block + * @v setting Setting to store + * @v data Setting data, or NULL to clear setting + * @v len Length of setting data + * @ret rc Return status code + */ +static int dhcppkt_settings_store ( struct settings *settings, + struct setting *setting, + const void *data, size_t len ) { + struct dhcp_packet *dhcppkt = + container_of ( settings, struct dhcp_packet, settings ); + + return dhcppkt_store ( dhcppkt, setting->tag, data, len ); +} + +/** + * Fetch value of DHCP setting + * + * @v settings Settings block, or NULL to search all blocks + * @v setting Setting to fetch + * @v data Buffer to fill with setting data + * @v len Length of buffer + * @ret len Length of setting data, or negative error + */ +static int dhcppkt_settings_fetch ( struct settings *settings, + struct setting *setting, + void *data, size_t len ) { + struct dhcp_packet *dhcppkt = + container_of ( settings, struct dhcp_packet, settings ); + + return dhcppkt_fetch ( dhcppkt, setting->tag, data, len ); +} + +/** DHCP settings operations */ +static struct settings_operations dhcppkt_settings_operations = { + .store = dhcppkt_settings_store, + .fetch = dhcppkt_settings_fetch, +}; + +/**************************************************************************** + * + * Constructor + * + */ + +/** + * Initialise DHCP packet + * + * @v dhcppkt DHCP packet structure to fill in + * @v data DHCP packet raw data + * @v max_len Length of raw data buffer + * + * Initialise a DHCP packet structure from a data buffer containing a + * DHCP packet. + */ +void dhcppkt_init ( struct dhcp_packet *dhcppkt, struct dhcphdr *data, + size_t len ) { + dhcppkt->dhcphdr = data; + dhcppkt->max_len = len; + dhcpopt_init ( &dhcppkt->options, &dhcppkt->dhcphdr->options, + ( len - offsetof ( struct dhcphdr, options ) ) ); + dhcppkt->len = ( offsetof ( struct dhcphdr, options ) + + dhcppkt->options.len ); + settings_init ( &dhcppkt->settings, + &dhcppkt_settings_operations, &dhcppkt->refcnt, + DHCP_SETTINGS_NAME, 0 ); +} |