diff options
author | H. Peter Anvin <hpa@zytor.com> | 2008-03-31 05:27:05 -0700 |
---|---|---|
committer | H. Peter Anvin <hpa@zytor.com> | 2008-03-31 05:27:05 -0700 |
commit | 1a8b87b10c173c2be5f384d68cf75395483a3230 (patch) | |
tree | 96dba02f2a85e46ba0e9aa14e7adc964874d8aa9 /gpxe | |
parent | 162a7e92138ed5c01c4a4c2168ab1126a7d9d685 (diff) | |
download | syslinux-1a8b87b10c173c2be5f384d68cf75395483a3230.tar.gz |
Merge gpxe-for-syslinux
Merge gpxe-for-syslinux up to commit
71c509be409820a12efeebf31f486e144c9efdae; upstream commit
b107637008d15e00a4d95cdb5c8f5c11fda490f7.
Diffstat (limited to 'gpxe')
-rw-r--r-- | gpxe/src/arch/i386/firmware/pcbios/smbios.c | 193 | ||||
-rw-r--r-- | gpxe/src/arch/i386/firmware/pcbios/smbios_settings.c | 158 | ||||
-rw-r--r-- | gpxe/src/arch/i386/include/bits/errfile.h | 1 | ||||
-rw-r--r-- | gpxe/src/arch/i386/include/bits/uuid.h | 10 | ||||
-rw-r--r-- | gpxe/src/arch/i386/include/smbios.h | 23 | ||||
-rw-r--r-- | gpxe/src/core/main.c | 34 | ||||
-rw-r--r-- | gpxe/src/core/settings.c | 73 | ||||
-rw-r--r-- | gpxe/src/hci/shell_banner.c | 19 | ||||
-rw-r--r-- | gpxe/src/include/gpxe/dhcp.h | 10 | ||||
-rw-r--r-- | gpxe/src/include/gpxe/settings.h | 5 | ||||
-rw-r--r-- | gpxe/src/include/gpxe/uuid.h | 3 | ||||
-rw-r--r-- | gpxe/src/net/dhcppkt.c | 58 | ||||
-rw-r--r-- | gpxe/src/net/fakedhcp.c | 7 | ||||
-rw-r--r-- | gpxe/src/net/tcp/http.c | 2 | ||||
-rw-r--r-- | gpxe/src/net/udp/dhcp.c | 894 |
15 files changed, 970 insertions, 520 deletions
diff --git a/gpxe/src/arch/i386/firmware/pcbios/smbios.c b/gpxe/src/arch/i386/firmware/pcbios/smbios.c index 4d710d68..aa4f3f32 100644 --- a/gpxe/src/arch/i386/firmware/pcbios/smbios.c +++ b/gpxe/src/arch/i386/firmware/pcbios/smbios.c @@ -18,10 +18,9 @@ #include <stdint.h> #include <string.h> -#include <stdio.h> #include <errno.h> +#include <assert.h> #include <gpxe/uaccess.h> -#include <gpxe/uuid.h> #include <realmode.h> #include <pnpbios.h> #include <smbios.h> @@ -51,7 +50,7 @@ struct smbios_entry { /** Checksum */ uint8_t checksum; /** Length */ - uint8_t length; + uint8_t len; /** Major version */ uint8_t major; /** Minor version */ @@ -67,7 +66,7 @@ struct smbios_entry { /** DMI checksum */ uint8_t dmi_checksum; /** Structure table length */ - uint16_t smbios_length; + uint16_t smbios_len; /** Structure table address */ physaddr_t smbios_address; /** Number of SMBIOS structures */ @@ -86,49 +85,37 @@ struct smbios { /** Start of SMBIOS structures */ userptr_t address; /** Length of SMBIOS structures */ - size_t length; + size_t len; /** Number of SMBIOS structures */ unsigned int count; }; -/** - * SMBIOS strings descriptor - * - * This is returned as part of the search for an SMBIOS structure, and - * contains the information needed for extracting the strings within - * the "unformatted" portion of the structure. - */ -struct smbios_strings { - /** Start of strings data */ - userptr_t data; - /** Length of strings data */ - size_t length; +/** SMBIOS entry point descriptor */ +static struct smbios smbios = { + .address = UNULL, }; /** * Find SMBIOS * - * @ret smbios SMBIOS entry point descriptor, or NULL if not found + * @ret rc Return status code */ -static struct smbios * find_smbios ( void ) { - static struct smbios smbios = { - .address = UNULL, - }; +static int find_smbios ( void ) { union { struct smbios_entry entry; uint8_t bytes[256]; /* 256 is maximum length possible */ } u; - unsigned int offset; + static unsigned int offset = 0; size_t len; unsigned int i; uint8_t sum; - /* Return cached result if available */ + /* Return cached result if avaiable */ if ( smbios.address != UNULL ) - return &smbios; + return 0; /* Try to find SMBIOS */ - for ( offset = 0 ; offset < 0x10000 ; offset += 0x10 ) { + for ( ; offset < 0x10000 ; offset += 0x10 ) { /* Read start of header and verify signature */ copy_from_real ( &u.entry, BIOS_SEG, offset, @@ -137,7 +124,7 @@ static struct smbios * find_smbios ( void ) { continue; /* Read whole header and verify checksum */ - len = u.entry.length; + len = u.entry.len; copy_from_real ( &u.bytes, BIOS_SEG, offset, len ); for ( i = 0 , sum = 0 ; i < len ; i++ ) { sum += u.bytes[i]; @@ -152,29 +139,27 @@ static struct smbios * find_smbios ( void ) { DBG ( "Found SMBIOS entry point at %04x:%04x\n", BIOS_SEG, offset ); smbios.address = phys_to_user ( u.entry.smbios_address ); - smbios.length = u.entry.smbios_length; + smbios.len = u.entry.smbios_len; smbios.count = u.entry.smbios_count; - return &smbios; + return 0; } DBG ( "No SMBIOS found\n" ); - return NULL; + return -ENODEV; } /** * Find SMBIOS strings terminator * - * @v smbios SMBIOS entry point descriptor * @v offset Offset to start of strings * @ret offset Offset to strings terminator, or 0 if not found */ -static size_t find_strings_terminator ( struct smbios *smbios, - size_t offset ) { - size_t max_offset = ( smbios->length - 2 ); +static size_t find_strings_terminator ( size_t offset ) { + size_t max_offset = ( smbios.len - 2 ); uint16_t nulnul; for ( ; offset <= max_offset ; offset++ ) { - copy_from_user ( &nulnul, smbios->address, offset, 2 ); + copy_from_user ( &nulnul, smbios.address, offset, 2 ); if ( nulnul == 0 ) return ( offset + 1 ); } @@ -185,66 +170,52 @@ static size_t find_strings_terminator ( struct smbios *smbios, * Find specific structure type within SMBIOS * * @v type Structure type to search for - * @v structure Buffer to fill in with structure - * @v length Length of buffer - * @v strings Strings descriptor to fill in, or NULL + * @v structure SMBIOS structure descriptor to fill in * @ret rc Return status code */ -int find_smbios_structure ( unsigned int type, void *structure, - size_t length, struct smbios_strings *strings ) { - struct smbios *smbios; - struct smbios_header header; - struct smbios_strings temp_strings; +int find_smbios_structure ( unsigned int type, + struct smbios_structure *structure ) { unsigned int count = 0; size_t offset = 0; size_t strings_offset; size_t terminator_offset; + int rc; - /* Locate SMBIOS entry point */ - if ( ! ( smbios = find_smbios() ) ) - return -ENOENT; - - /* Ensure that we have a usable strings descriptor buffer */ - if ( ! strings ) - strings = &temp_strings; + /* Find SMBIOS */ + if ( ( rc = find_smbios() ) != 0 ) + return rc; /* Scan through list of structures */ - while ( ( ( offset + sizeof ( header ) ) < smbios->length ) && - ( count < smbios->count ) ) { + while ( ( ( offset + sizeof ( structure->header ) ) < smbios.len ) + && ( count < smbios.count ) ) { /* Read next SMBIOS structure header */ - copy_from_user ( &header, smbios->address, offset, - sizeof ( header ) ); + copy_from_user ( &structure->header, smbios.address, offset, + sizeof ( structure->header ) ); /* Determine start and extent of strings block */ - strings_offset = ( offset + header.length ); - if ( strings_offset > smbios->length ) { + strings_offset = ( offset + structure->header.len ); + if ( strings_offset > smbios.len ) { DBG ( "SMBIOS structure at offset %zx with length " "%x extends beyond SMBIOS\n", offset, - header.length ); + structure->header.len ); return -ENOENT; } - terminator_offset = - find_strings_terminator ( smbios, strings_offset ); + terminator_offset = find_strings_terminator ( strings_offset ); if ( ! terminator_offset ) { DBG ( "SMBIOS structure at offset %zx has " "unterminated strings section\n", offset ); return -ENOENT; } - strings->data = userptr_add ( smbios->address, - strings_offset ); - strings->length = ( terminator_offset - strings_offset ); + structure->strings_len = ( terminator_offset - strings_offset); - DBG ( "SMBIOS structure at offset %zx has type %d, " - "length %x, strings length %zx\n", - offset, header.type, header.length, strings->length ); + DBG ( "SMBIOS structure at offset %zx has type %d, length %x, " + "strings length %zx\n", offset, structure->header.type, + structure->header.len, structure->strings_len ); /* If this is the structure we want, return */ - if ( header.type == type ) { - if ( length > header.length ) - length = header.length; - copy_from_user ( structure, smbios->address, - offset, length ); + if ( structure->header.type == type ) { + structure->offset = offset; return 0; } @@ -258,66 +229,64 @@ int find_smbios_structure ( unsigned int type, void *structure, } /** + * Copy SMBIOS structure + * + * @v structure SMBIOS structure descriptor + * @v data Buffer to hold SMBIOS structure + * @v len Length of buffer + * @ret rc Return status code + */ +int read_smbios_structure ( struct smbios_structure *structure, + void *data, size_t len ) { + + assert ( smbios.address != UNULL ); + + if ( len > structure->header.len ) + len = structure->header.len; + copy_from_user ( data, smbios.address, structure->offset, len ); + return 0; +} + +/** * Find indexed string within SMBIOS structure * - * @v strings SMBIOS strings descriptor + * @v structure SMBIOS structure descriptor * @v index String index - * @v buffer Buffer for string - * @v length Length of string buffer - * @ret rc Return status code + * @v data Buffer for string + * @v len Length of string buffer + * @ret rc Length of string, or negative error */ -int find_smbios_string ( struct smbios_strings *strings, unsigned int index, - char *buffer, size_t length ) { - size_t offset = 0; +int read_smbios_string ( struct smbios_structure *structure, + unsigned int index, void *data, size_t len ) { + size_t strings_start = ( structure->offset + structure->header.len ); + size_t strings_end = ( strings_start + structure->strings_len ); + size_t offset; size_t string_len; - /* Zero buffer. This ensures that a valid NUL terminator is - * always present (unless length==0). - */ - memset ( buffer, 0, length ); - + assert ( smbios.address != UNULL ); + /* String numbers start at 1 (0 is used to indicate "no string") */ if ( ! index ) - return 0; + return -ENOENT; - while ( offset < strings->length ) { + for ( offset = strings_start ; offset < strings_end ; + offset += ( string_len + 1 ) ) { /* Get string length. This is known safe, since the * smbios_strings struct is constructed so as to * always end on a string boundary. */ - string_len = strlen_user ( strings->data, offset ); + string_len = strlen_user ( smbios.address, + ( structure->offset + offset ) ); if ( --index == 0 ) { /* Copy string, truncating as necessary. */ - if ( string_len >= length ) - string_len = ( length - 1 ); - copy_from_user ( buffer, strings->data, - offset, string_len ); - return 0; + if ( len > string_len ) + len = string_len; + copy_from_user ( data, smbios.address, + ( structure->offset + offset ), len ); + return string_len; } - offset += ( string_len + 1 ); } DBG ( "SMBIOS string index %d not found\n", index ); return -ENOENT; } - -/** - * Get UUID from SMBIOS - * - * @v uuid UUID to fill in - * @ret rc Return status code - */ -int smbios_get_uuid ( union uuid *uuid ) { - struct smbios_system_information sysinfo; - int rc; - - if ( ( rc = find_smbios_structure ( SMBIOS_TYPE_SYSTEM_INFORMATION, - &sysinfo, sizeof ( sysinfo ), - NULL ) ) != 0 ) - return rc; - - memcpy ( uuid, sysinfo.uuid, sizeof ( *uuid ) ); - DBG ( "SMBIOS found UUID %s\n", uuid_ntoa ( uuid ) ); - - return 0; -} diff --git a/gpxe/src/arch/i386/firmware/pcbios/smbios_settings.c b/gpxe/src/arch/i386/firmware/pcbios/smbios_settings.c new file mode 100644 index 00000000..de08ec5b --- /dev/null +++ b/gpxe/src/arch/i386/firmware/pcbios/smbios_settings.c @@ -0,0 +1,158 @@ +/* + * 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. + */ + +#include <stdint.h> +#include <string.h> +#include <errno.h> +#include <gpxe/settings.h> +#include <gpxe/init.h> +#include <gpxe/uuid.h> +#include <smbios.h> + +/** + * Construct SMBIOS raw-data tag + * + * @v _type SMBIOS structure type number + * @v _structure SMBIOS structure data type + * @v _field Field within SMBIOS structure data type + * @ret tag SMBIOS setting tag + */ +#define SMBIOS_RAW_TAG( _type, _structure, _field ) \ + ( ( (_type) << 16 ) | \ + ( offsetof ( _structure, _field ) << 8 ) | \ + ( sizeof ( ( ( _structure * ) 0 )->_field ) ) ) + +/** + * Construct SMBIOS string tag + * + * @v _type SMBIOS structure type number + * @v _structure SMBIOS structure data type + * @v _field Field within SMBIOS structure data type + * @ret tag SMBIOS setting tag + */ +#define SMBIOS_STRING_TAG( _type, _structure, _field ) \ + ( ( (_type) << 16 ) | \ + ( offsetof ( _structure, _field ) << 8 ) ) + +/** + * Store value of SMBIOS 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 smbios_store ( struct settings *settings __unused, + struct setting *setting __unused, + const void *data __unused, size_t len __unused ) { + /* Cannot write data into SMBIOS */ + return -ENOTSUP; +} + +/** + * Fetch value of SMBIOS 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 smbios_fetch ( struct settings *settings __unused, + struct setting *setting, + void *data, size_t len ) { + struct smbios_structure structure; + unsigned int tag_type; + unsigned int tag_offset; + unsigned int tag_len; + int rc; + + /* Split tag into type, offset and length */ + tag_type = ( setting->tag >> 16 ); + tag_offset = ( ( setting->tag >> 8 ) & 0xff ); + tag_len = ( setting->tag & 0xff ); + if ( ! tag_type ) + return -ENOENT; + + /* Find SMBIOS structure */ + if ( ( rc = find_smbios_structure ( tag_type, &structure ) ) != 0 ) + return rc; + + { + uint8_t buf[structure.header.len]; + + /* Read SMBIOS structure */ + if ( ( rc = read_smbios_structure ( &structure, buf, + sizeof ( buf ) ) ) != 0 ) + return rc; + + if ( tag_len == 0 ) { + /* String */ + return read_smbios_string ( &structure, + buf[tag_offset], + data, len ); + } else { + /* Raw data */ + if ( len > tag_len ) + len = tag_len; + memcpy ( data, &buf[tag_offset], len ); + return tag_len; + } + } +} + +/** SMBIOS settings operations */ +static struct settings_operations smbios_settings_operations = { + .store = smbios_store, + .fetch = smbios_fetch, +}; + +/** SMBIOS settings */ +static struct settings smbios_settings = { + .refcnt = NULL, + .name = "smbios", + .siblings = LIST_HEAD_INIT ( smbios_settings.siblings ), + .children = LIST_HEAD_INIT ( smbios_settings.children ), + .op = &smbios_settings_operations, +}; + +/** Initialise SMBIOS settings */ +static void smbios_init ( void ) { + int rc; + + if ( ( rc = register_settings ( &smbios_settings, NULL ) ) != 0 ) { + DBG ( "SMBIOS could not register settings: %s\n", + strerror ( rc ) ); + return; + } +} + +/** SMBIOS settings initialiser */ +struct init_fn smbios_init_fn __init_fn ( INIT_NORMAL ) = { + .initialise = smbios_init, +}; + +/** UUID setting obtained via SMBIOS */ +struct setting uuid_setting __setting = { + .name = "uuid", + .description = "UUID", + .tag = SMBIOS_RAW_TAG ( SMBIOS_TYPE_SYSTEM_INFORMATION, + struct smbios_system_information, uuid ), + .type = &setting_type_uuid, +}; diff --git a/gpxe/src/arch/i386/include/bits/errfile.h b/gpxe/src/arch/i386/include/bits/errfile.h index 0f140214..678be045 100644 --- a/gpxe/src/arch/i386/include/bits/errfile.h +++ b/gpxe/src/arch/i386/include/bits/errfile.h @@ -12,6 +12,7 @@ #define ERRFILE_smbios ( ERRFILE_ARCH | ERRFILE_CORE | 0x00030000 ) #define ERRFILE_biosint ( ERRFILE_ARCH | ERRFILE_CORE | 0x00040000 ) #define ERRFILE_int13 ( ERRFILE_ARCH | ERRFILE_CORE | 0x00050000 ) +#define ERRFILE_smbios_settings ( ERRFILE_ARCH | ERRFILE_CORE | 0x00060000 ) #define ERRFILE_bootsector ( ERRFILE_ARCH | ERRFILE_IMAGE | 0x00000000 ) #define ERRFILE_bzimage ( ERRFILE_ARCH | ERRFILE_IMAGE | 0x00010000 ) diff --git a/gpxe/src/arch/i386/include/bits/uuid.h b/gpxe/src/arch/i386/include/bits/uuid.h deleted file mode 100644 index 0cbd320a..00000000 --- a/gpxe/src/arch/i386/include/bits/uuid.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef _I386_UUID_H -#define _I386_UUID_H - -#include <smbios.h> - -static inline int get_uuid ( union uuid *uuid ) { - return smbios_get_uuid ( uuid ); -} - -#endif /* _I386_UUID_H */ diff --git a/gpxe/src/arch/i386/include/smbios.h b/gpxe/src/arch/i386/include/smbios.h index 821eda17..f2736dc3 100644 --- a/gpxe/src/arch/i386/include/smbios.h +++ b/gpxe/src/arch/i386/include/smbios.h @@ -13,11 +13,21 @@ struct smbios_header { /** Type */ uint8_t type; /** Length */ - uint8_t length; + uint8_t len; /** Handle */ uint16_t handle; } __attribute__ (( packed )); +/** SMBIOS structure descriptor */ +struct smbios_structure { + /** Copy of SMBIOS structure header */ + struct smbios_header header; + /** Offset of structure within SMBIOS */ + size_t offset; + /** Length of strings section */ + size_t strings_len; +}; + /** SMBIOS system information structure */ struct smbios_system_information { /** SMBIOS structure header */ @@ -39,13 +49,12 @@ struct smbios_system_information { /** SMBIOS system information structure type */ #define SMBIOS_TYPE_SYSTEM_INFORMATION 1 -struct smbios_strings; extern int find_smbios_structure ( unsigned int type, - void *structure, size_t length, - struct smbios_strings *strings ); -extern int find_smbios_string ( struct smbios_strings *strings, + struct smbios_structure *structure ); +extern int read_smbios_structure ( struct smbios_structure *structure, + void *data, size_t len ); +extern int read_smbios_string ( struct smbios_structure *structure, unsigned int index, - char *buffer, size_t length ); -extern int smbios_get_uuid ( union uuid *uuid ); + void *data, size_t len ); #endif /* _SMBIOS_H */ diff --git a/gpxe/src/core/main.c b/gpxe/src/core/main.c index 3295feaf..ca62db25 100644 --- a/gpxe/src/core/main.c +++ b/gpxe/src/core/main.c @@ -14,26 +14,54 @@ Literature dealing with the network protocols: **************************************************************************/ +#include <stdio.h> #include <gpxe/init.h> +#include <gpxe/features.h> #include <gpxe/shell.h> #include <gpxe/shell_banner.h> #include <usr/autoboot.h> +#define NORMAL "\033[0m" +#define BOLD "\033[1m" +#define CYAN "\033[36m" + +static struct feature features[0] __table_start ( struct feature, features ); +static struct feature features_end[0] __table_end ( struct feature, features ); + /** * Main entry point * * @ret rc Return status code */ __cdecl int main ( void ) { + struct feature *feature; initialise(); startup(); - if ( shell_banner() ) + /* Print welcome banner */ + printf ( NORMAL "\n\n\n" BOLD "gPXE " VERSION + NORMAL " -- Open Source Boot Firmware -- " + CYAN "http://etherboot.org" NORMAL "\n" + "Features:" ); + for ( feature = features ; feature < features_end ; feature++ ) + printf ( " %s", feature->name ); + printf ( "\n" ); + + /* Prompt for shell */ + if ( shell_banner() ) { + /* User wants shell; just give them a shell */ shell(); - else + } else { + /* User doesn't want shell; try booting. If booting + * fails, offer a second chance to enter the shell for + * diagnostics. + */ autoboot(); - + if ( shell_banner() ) + shell(); + } + shutdown(); return 0; diff --git a/gpxe/src/core/settings.c b/gpxe/src/core/settings.c index b793ae68..75253186 100644 --- a/gpxe/src/core/settings.c +++ b/gpxe/src/core/settings.c @@ -27,6 +27,7 @@ #include <gpxe/in.h> #include <gpxe/vsprintf.h> #include <gpxe/dhcp.h> +#include <gpxe/uuid.h> #include <gpxe/settings.h> /** @file @@ -233,6 +234,10 @@ struct settings * find_child_settings ( struct settings *parent, struct settings *settings; size_t len; + /* NULL parent => add to settings root */ + if ( parent == NULL ) + parent = &settings_root; + /* Look for a child whose name matches the initial component */ list_for_each_entry ( settings, &parent->children, siblings ) { len = strlen ( settings->name ); @@ -484,6 +489,26 @@ unsigned long fetch_uintz_setting ( struct settings *settings, } /** + * Fetch value of UUID setting + * + * @v settings Settings block, or NULL to search all blocks + * @v setting Setting to fetch + * @v uuid UUID to fill in + * @ret len Length of setting, or negative error + */ +int fetch_uuid_setting ( struct settings *settings, struct setting *setting, + union uuid *uuid ) { + int len; + + len = fetch_setting ( settings, setting, uuid, sizeof ( *uuid ) ); + if ( len < 0 ) + return len; + if ( len != sizeof ( *uuid ) ) + return -ERANGE; + return len; +} + +/** * Compare two settings * * @v a Setting to compare @@ -748,11 +773,11 @@ static int storef_ipv4 ( struct settings *settings, struct setting *setting, static int fetchf_ipv4 ( struct settings *settings, struct setting *setting, char *buf, size_t len ) { struct in_addr ipv4; - int rc; + int raw_len; - if ( ( rc = fetch_ipv4_setting ( settings, setting, &ipv4 ) ) < 0 ) - return rc; - return snprintf ( buf, len, inet_ntoa ( ipv4 ) ); + if ( ( raw_len = fetch_ipv4_setting ( settings, setting, &ipv4 ) ) < 0) + return raw_len; + return snprintf ( buf, len, "%s", inet_ntoa ( ipv4 ) ); } /** An IPv4 address setting type */ @@ -981,6 +1006,46 @@ struct setting_type setting_type_hex __setting_type = { .fetchf = fetchf_hex, }; +/** + * Parse and store value of UUID setting + * + * @v settings Settings block + * @v setting Setting to store + * @v value Formatted setting data + * @ret rc Return status code + */ +static int storef_uuid ( struct settings *settings __unused, + struct setting *setting __unused, + const char *value __unused ) { + return -ENOTSUP; +} + +/** + * Fetch and format value of UUID setting + * + * @v settings Settings block, or NULL to search all blocks + * @v setting Setting to fetch + * @v buf Buffer to contain formatted value + * @v len Length of buffer + * @ret len Length of formatted value, or negative error + */ +static int fetchf_uuid ( struct settings *settings, struct setting *setting, + char *buf, size_t len ) { + union uuid uuid; + int raw_len; + + if ( ( raw_len = fetch_uuid_setting ( settings, setting, &uuid ) ) < 0) + return raw_len; + return snprintf ( buf, len, "%s", uuid_ntoa ( &uuid ) ); +} + +/** UUID setting type */ +struct setting_type setting_type_uuid __setting_type = { + .name = "uuid", + .storef = storef_uuid, + .fetchf = fetchf_uuid, +}; + /****************************************************************************** * * Settings diff --git a/gpxe/src/hci/shell_banner.c b/gpxe/src/hci/shell_banner.c index 62da487c..92cd17dd 100644 --- a/gpxe/src/hci/shell_banner.c +++ b/gpxe/src/hci/shell_banner.c @@ -18,7 +18,6 @@ #include <stdio.h> #include <console.h> -#include <gpxe/features.h> #include <gpxe/timer.h> #include <gpxe/shell_banner.h> @@ -30,13 +29,6 @@ #define BANNER_TIMEOUT ( 2 * TICKS_PER_SEC ) -#define NORMAL "\033[0m" -#define BOLD "\033[1m" -#define CYAN "\033[36m" - -static struct feature features[0] __table_start ( struct feature, features ); -static struct feature features_end[0] __table_end ( struct feature, features ); - /** * Print shell banner and prompt for shell entry * @@ -44,18 +36,9 @@ static struct feature features_end[0] __table_end ( struct feature, features ); */ int shell_banner ( void ) { unsigned long timeout = ( currticks() + BANNER_TIMEOUT ); - struct feature *feature; - int key; int enter_shell = 0; + int key; - /* Print welcome banner */ - printf ( NORMAL "\n\n\n" BOLD "gPXE " VERSION - NORMAL " -- Open Source Boot Firmware -- " - CYAN "http://etherboot.org" NORMAL "\n" - "Features:" ); - for ( feature = features ; feature < features_end ; feature++ ) { - printf ( " %s", feature->name ); - } printf ( "\nPress Ctrl-B for the gPXE command line..." ); /* Wait for key */ diff --git a/gpxe/src/include/gpxe/dhcp.h b/gpxe/src/include/gpxe/dhcp.h index 94cc2010..61445977 100644 --- a/gpxe/src/include/gpxe/dhcp.h +++ b/gpxe/src/include/gpxe/dhcp.h @@ -24,6 +24,9 @@ struct dhcp_packet; /** BOOTP/DHCP client port */ #define BOOTPC_PORT 68 +/** ProxyDHCP server port */ +#define PROXYDHCP_PORT 4011 + /** Construct a tag value for an encapsulated option * * This tag value can be passed to Etherboot functions when searching @@ -433,7 +436,7 @@ struct dhcphdr { */ #define DHCP_MIN_LEN 552 -/** Maximum time that we will wait for ProxyDHCP offers */ +/** Maximum time that we will wait for ProxyDHCP responses */ #define PROXYDHCP_WAIT_TIME ( TICKS_PER_SEC * 1 ) /** Settings block name used for DHCP responses */ @@ -442,12 +445,13 @@ struct dhcphdr { /** Settings block name used for ProxyDHCP responses */ #define PROXYDHCP_SETTINGS_NAME "proxydhcp" -extern int create_dhcp_packet ( struct dhcp_packet *dhcppkt, +extern int dhcp_create_packet ( struct dhcp_packet *dhcppkt, struct net_device *netdev, uint8_t msgtype, struct dhcp_options *options, void *data, size_t max_len ); -extern int create_dhcp_request ( struct dhcp_packet *dhcppkt, +extern int dhcp_create_request ( struct dhcp_packet *dhcppkt, struct net_device *netdev, + struct in_addr ciaddr, struct dhcp_packet *dhcpoffer, void *data, size_t max_len ); extern int start_dhcp ( struct job_interface *job, struct net_device *netdev ); diff --git a/gpxe/src/include/gpxe/settings.h b/gpxe/src/include/gpxe/settings.h index 40825698..ae5a259d 100644 --- a/gpxe/src/include/gpxe/settings.h +++ b/gpxe/src/include/gpxe/settings.h @@ -15,6 +15,7 @@ struct settings; struct in_addr; +union uuid; /** A setting */ struct setting { @@ -177,6 +178,8 @@ extern long fetch_intz_setting ( struct settings *settings, struct setting *setting ); extern unsigned long fetch_uintz_setting ( struct settings *settings, struct setting *setting ); +extern int fetch_uuid_setting ( struct settings *settings, + struct setting *setting, union uuid *uuid ); extern int setting_cmp ( struct setting *a, struct setting *b ); extern struct settings * find_child_settings ( struct settings *parent, @@ -198,6 +201,7 @@ extern struct setting_type setting_type_uint8 __setting_type; extern struct setting_type setting_type_uint16 __setting_type; extern struct setting_type setting_type_uint32 __setting_type; extern struct setting_type setting_type_hex __setting_type; +extern struct setting_type setting_type_uuid __setting_type; extern struct setting ip_setting __setting; extern struct setting netmask_setting __setting; @@ -210,6 +214,7 @@ extern struct setting username_setting __setting; extern struct setting password_setting __setting; extern struct setting priority_setting __setting; extern struct setting bios_drive_setting __setting; +extern struct setting uuid_setting __setting; /** * Initialise a settings block diff --git a/gpxe/src/include/gpxe/uuid.h b/gpxe/src/include/gpxe/uuid.h index a62735c9..4f89be50 100644 --- a/gpxe/src/include/gpxe/uuid.h +++ b/gpxe/src/include/gpxe/uuid.h @@ -8,9 +8,6 @@ #include <stdint.h> -union uuid; -#include <bits/uuid.h> - /** A universally unique ID */ union uuid { /** Canonical form (00000000-0000-0000-0000-000000000000) */ diff --git a/gpxe/src/net/dhcppkt.c b/gpxe/src/net/dhcppkt.c index 1cf99d8d..2537e254 100644 --- a/gpxe/src/net/dhcppkt.c +++ b/gpxe/src/net/dhcppkt.c @@ -32,6 +32,30 @@ * */ +/** + * 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 */ @@ -40,25 +64,34 @@ struct dhcp_packet_field { 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 ) { \ +#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 ), - DHCP_PACKET_FIELD ( DHCP_EB_SIADDR, siaddr ), - DHCP_PACKET_FIELD ( DHCP_TFTP_SERVER_NAME, sname ), - DHCP_PACKET_FIELD ( DHCP_BOOTFILE_NAME, file ), + 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 ), }; /** @@ -138,14 +171,19 @@ int dhcppkt_store ( struct dhcp_packet *dhcppkt, unsigned int tag, 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; /* If this is a special field, return it */ if ( ( field = find_dhcp_packet_field ( tag ) ) != NULL ) { - if ( len > field->len ) - len = field->len; - memcpy ( data, - dhcp_packet_field ( dhcppkt->dhcphdr, field ), len ); - return field->len; + field_data = dhcp_packet_field ( dhcppkt->dhcphdr, field ); + field_len = field->used_len ( field_data, field->len ); + if ( ! field_len ) + return -ENOENT; + if ( len > field_len ) + len = field_len; + memcpy ( data, field_data, len ); + return field_len; } /* Otherwise, use the generic options block */ diff --git a/gpxe/src/net/fakedhcp.c b/gpxe/src/net/fakedhcp.c index c3054db1..a10e442b 100644 --- a/gpxe/src/net/fakedhcp.c +++ b/gpxe/src/net/fakedhcp.c @@ -108,9 +108,10 @@ static int copy_settings ( struct dhcp_packet *dest, int create_fakedhcpdiscover ( struct net_device *netdev, void *data, size_t max_len ) { struct dhcp_packet dhcppkt; + struct in_addr ciaddr = { 0 }; int rc; - if ( ( rc = create_dhcp_request ( &dhcppkt, netdev, NULL, data, + if ( ( rc = dhcp_create_request ( &dhcppkt, netdev, ciaddr, NULL, data, max_len ) ) != 0 ) { DBG ( "Could not create DHCPDISCOVER: %s\n", strerror ( rc ) ); @@ -136,7 +137,7 @@ int create_fakedhcpack ( struct net_device *netdev, int rc; /* Create base DHCPACK packet */ - if ( ( rc = create_dhcp_packet ( &dhcppkt, netdev, DHCPACK, NULL, + if ( ( rc = dhcp_create_packet ( &dhcppkt, netdev, DHCPACK, NULL, data, max_len ) ) != 0 ) { DBG ( "Could not create DHCPACK: %s\n", strerror ( rc ) ); return rc; @@ -187,7 +188,7 @@ int create_fakeproxydhcpack ( struct net_device *netdev, } /* Create base DHCPACK packet */ - if ( ( rc = create_dhcp_packet ( &dhcppkt, netdev, DHCPACK, NULL, + if ( ( rc = dhcp_create_packet ( &dhcppkt, netdev, DHCPACK, NULL, data, max_len ) ) != 0 ) { DBG ( "Could not create ProxyDHCPACK: %s\n", strerror ( rc ) ); diff --git a/gpxe/src/net/tcp/http.c b/gpxe/src/net/tcp/http.c index db92e9eb..4dc1ab73 100644 --- a/gpxe/src/net/tcp/http.c +++ b/gpxe/src/net/tcp/http.c @@ -393,7 +393,7 @@ static void http_step ( struct process *process ) { if ( xfer_window ( &http->socket ) ) { process_del ( &http->process ); if ( ( rc = xfer_printf ( &http->socket, - "GET %s%s%s HTTP/1.1\r\n" + "GET %s%s%s HTTP/1.0\r\n" "User-Agent: gPXE/" VERSION "\r\n" "Host: %s\r\n" "\r\n", diff --git a/gpxe/src/net/udp/dhcp.c b/gpxe/src/net/udp/dhcp.c index 3961c61d..ecb73785 100644 --- a/gpxe/src/net/udp/dhcp.c +++ b/gpxe/src/net/udp/dhcp.c @@ -92,6 +92,34 @@ static struct dhcp_options dhcp_request_options = { static uint8_t dhcp_features[0] __table_start ( uint8_t, dhcp_features ); static uint8_t dhcp_features_end[0] __table_end ( uint8_t, dhcp_features ); +/** DHCP network device descriptor */ +struct dhcp_netdev_desc { + /** Bus type ID */ + uint8_t type; + /** Vendor ID */ + uint16_t vendor; + /** Device ID */ + uint16_t device; +} __attribute__ (( packed )); + +/** DHCP client identifier */ +struct dhcp_client_id { + /** Link-layer protocol */ + uint8_t ll_proto; + /** Link-layer address */ + uint8_t ll_addr[MAX_LL_ADDR_LEN]; +} __attribute__ (( packed )); + +/** DHCP client UUID */ +struct dhcp_client_uuid { + /** Identifier type */ + uint8_t type; + /** UUID */ + union uuid uuid; +} __attribute__ (( packed )); + +#define DHCP_CLIENT_UUID_TYPE 0 + /** * Name a DHCP packet type * @@ -130,208 +158,6 @@ static uint32_t dhcp_xid ( struct net_device *netdev ) { return xid; } -/** - * Create a DHCP packet - * - * @v dhcppkt DHCP packet structure to fill in - * @v netdev Network device - * @v msgtype DHCP message type - * @v options Initial options to include (or NULL) - * @v data Buffer for DHCP packet - * @v max_len Size of DHCP packet buffer - * @ret rc Return status code - * - * Creates a DHCP packet in the specified buffer, and fills out a @c - * dhcp_packet structure that can be passed to - * set_dhcp_packet_option() or copy_dhcp_packet_options(). - */ -int create_dhcp_packet ( struct dhcp_packet *dhcppkt, - struct net_device *netdev, uint8_t msgtype, - struct dhcp_options *options, - void *data, size_t max_len ) { - struct dhcphdr *dhcphdr = data; - size_t options_len; - unsigned int hlen; - int rc; - - /* Sanity check */ - options_len = ( options ? options->len : 0 ); - if ( max_len < ( sizeof ( *dhcphdr ) + options_len ) ) - return -ENOSPC; - - /* Initialise DHCP packet content */ - memset ( dhcphdr, 0, max_len ); - dhcphdr->xid = dhcp_xid ( netdev ); - dhcphdr->magic = htonl ( DHCP_MAGIC_COOKIE ); - dhcphdr->htype = ntohs ( netdev->ll_protocol->ll_proto ); - dhcphdr->op = dhcp_op[msgtype]; - /* If hardware length exceeds the chaddr field length, don't - * use the chaddr field. This is as per RFC4390. - */ - hlen = netdev->ll_protocol->ll_addr_len; - if ( hlen > sizeof ( dhcphdr->chaddr ) ) { - hlen = 0; - dhcphdr->flags = htons ( BOOTP_FL_BROADCAST ); - } - dhcphdr->hlen = hlen; - memcpy ( dhcphdr->chaddr, netdev->ll_addr, hlen ); - memcpy ( dhcphdr->options, options->data, options_len ); - - /* Initialise DHCP packet structure */ - memset ( dhcppkt, 0, sizeof ( *dhcppkt ) ); - dhcppkt_init ( dhcppkt, data, max_len ); - - /* Set DHCP_MESSAGE_TYPE option */ - if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_MESSAGE_TYPE, - &msgtype, sizeof ( msgtype ) ) ) != 0 ) - return rc; - - return 0; -} - -/** DHCP network device descriptor */ -struct dhcp_netdev_desc { - /** Bus type ID */ - uint8_t type; - /** Vendor ID */ - uint16_t vendor; - /** Device ID */ - uint16_t device; -} __attribute__ (( packed )); - -/** DHCP client identifier */ -struct dhcp_client_id { - /** Link-layer protocol */ - uint8_t ll_proto; - /** Link-layer address */ - uint8_t ll_addr[MAX_LL_ADDR_LEN]; -} __attribute__ (( packed )); - -/** DHCP client UUID */ -struct dhcp_client_uuid { - /** Identifier type */ - uint8_t type; - /** UUID */ - union uuid uuid; -} __attribute__ (( packed )); - -#define DHCP_CLIENT_UUID_TYPE 0 - -/** - * Create DHCP request packet - * - * @v dhcppkt DHCP packet structure to fill in - * @v netdev Network device - * @v dhcpoffer DHCPOFFER packet received from server - * @v data Buffer for DHCP packet - * @v max_len Size of DHCP packet buffer - * @ret rc Return status code - */ -int create_dhcp_request ( struct dhcp_packet *dhcppkt, - struct net_device *netdev, - struct dhcp_packet *dhcpoffer, - void *data, size_t max_len ) { - struct device_description *desc = &netdev->dev->desc; - struct dhcp_netdev_desc dhcp_desc; - struct dhcp_client_id client_id; - struct dhcp_client_uuid client_uuid; - unsigned int msgtype; - size_t dhcp_features_len; - size_t ll_addr_len; - int rc; - - /* Create DHCP packet */ - msgtype = ( dhcpoffer ? DHCPREQUEST : DHCPDISCOVER ); - if ( ( rc = create_dhcp_packet ( dhcppkt, netdev, msgtype, - &dhcp_request_options, data, - max_len ) ) != 0 ) { - DBG ( "DHCP could not create DHCP packet: %s\n", - strerror ( rc ) ); - return rc; - } - - /* Copy any required options from previous server repsonse */ - if ( dhcpoffer ) { - struct in_addr server_id; - struct in_addr requested_ip; - - if ( dhcppkt_fetch ( dhcpoffer, DHCP_SERVER_IDENTIFIER, - &server_id, sizeof ( server_id ) ) - != sizeof ( server_id ) ) { - DBG ( "DHCP offer missing server identifier\n" ); - return -EINVAL; - } - if ( dhcppkt_fetch ( dhcpoffer, DHCP_EB_YIADDR, - &requested_ip, sizeof ( requested_ip ) ) - != sizeof ( requested_ip ) ) { - DBG ( "DHCP offer missing IP address\n" ); - return -EINVAL; - } - if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_SERVER_IDENTIFIER, - &server_id, - sizeof ( server_id ) ) ) != 0 ) { - DBG ( "DHCP could not set server identifier: %s\n ", - strerror ( rc ) ); - return rc; - } - if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_REQUESTED_ADDRESS, - &requested_ip, - sizeof ( requested_ip ) ) ) != 0 ){ - DBG ( "DHCP could not set requested address: %s\n", - strerror ( rc ) ); - return rc; - } - } - - /* Add options to identify the feature list */ - dhcp_features_len = ( dhcp_features_end - dhcp_features ); - if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_EB_ENCAP, dhcp_features, - dhcp_features_len ) ) != 0 ) { - DBG ( "DHCP could not set features list option: %s\n", - strerror ( rc ) ); - return rc; - } - - /* Add options to identify the network device */ - dhcp_desc.type = desc->bus_type; - dhcp_desc.vendor = htons ( desc->vendor ); - dhcp_desc.device = htons ( desc->device ); - if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_EB_BUS_ID, &dhcp_desc, - sizeof ( dhcp_desc ) ) ) != 0 ) { - DBG ( "DHCP could not set bus ID option: %s\n", - strerror ( rc ) ); - return rc; - } - - /* Add DHCP client identifier. Required for Infiniband, and - * doesn't hurt other link layers. - */ - client_id.ll_proto = ntohs ( netdev->ll_protocol->ll_proto ); - ll_addr_len = netdev->ll_protocol->ll_addr_len; - assert ( ll_addr_len <= sizeof ( client_id.ll_addr ) ); - memcpy ( client_id.ll_addr, netdev->ll_addr, ll_addr_len ); - if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_CLIENT_ID, &client_id, - ( ll_addr_len + 1 ) ) ) != 0 ) { - DBG ( "DHCP could not set client ID: %s\n", - strerror ( rc ) ); - return rc; - } - - /* Add client UUID, if we have one. Required for PXE. */ - client_uuid.type = DHCP_CLIENT_UUID_TYPE; - if ( ( rc = get_uuid ( &client_uuid.uuid ) ) == 0 ) { - if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_CLIENT_UUID, - &client_uuid, - sizeof ( client_uuid ) ) ) != 0 ) { - DBG ( "DHCP could not set client UUID: %s\n", - strerror ( rc ) ); - return rc; - } - } - - return 0; -} - /**************************************************************************** * * DHCP settings @@ -349,11 +175,24 @@ struct dhcp_settings { }; /** + * Increment reference count on DHCP settings block + * + * @v dhcpset DHCP settings block + * @ret dhcpset DHCP settings block + */ +static inline __attribute__ (( always_inline )) struct dhcp_settings * +dhcpset_get ( struct dhcp_settings *dhcpset ) { + ref_get ( &dhcpset->refcnt ); + return dhcpset; +} + +/** * Decrement reference count on DHCP settings block * * @v dhcpset DHCP settings block */ -static inline void dhcpset_put ( struct dhcp_settings *dhcpset ) { +static inline __attribute__ (( always_inline )) void +dhcpset_put ( struct dhcp_settings *dhcpset ) { ref_put ( &dhcpset->refcnt ); } @@ -375,7 +214,7 @@ static int dhcpset_store ( struct settings *settings, struct setting *setting, } /** - * Fetch value of setting + * Fetch value of DHCP setting * * @v settings Settings block, or NULL to search all blocks * @v setting Setting to fetch @@ -423,9 +262,34 @@ static struct dhcp_settings * dhcpset_create ( const struct dhcphdr *dhcphdr, /**************************************************************************** * - * DHCP to UDP interface + * DHCP session + * + */ + +/** DHCP session states */ +enum dhcp_session_state { + /** Sending DHCPDISCOVERs, collecting DHCPOFFERs and ProxyDHCPOFFERs */ + DHCP_STATE_DISCOVER = 0, + /** Sending DHCPREQUESTs, waiting for DHCPACK */ + DHCP_STATE_REQUEST, + /** Sending ProxyDHCPREQUESTs, waiting for ProxyDHCPACK */ + DHCP_STATE_PROXYREQUEST, +}; + +/** + * Name a DHCP session state * + * @v state DHCP session state + * @ret string DHCP session state name */ +static inline const char * dhcp_state_name ( enum dhcp_session_state state ) { + switch ( state ) { + case DHCP_STATE_DISCOVER: return "DHCPDISCOVER"; + case DHCP_STATE_REQUEST: return "DHCPREQUEST"; + case DHCP_STATE_PROXYREQUEST: return "ProxyDHCPREQUEST"; + default: return "<invalid>"; + } +} /** A DHCP session */ struct dhcp_session { @@ -444,14 +308,14 @@ struct dhcp_session { * This is a value for the @c DHCP_MESSAGE_TYPE option * (e.g. @c DHCPDISCOVER). */ - int state; - /** Response obtained from DHCP server */ - struct dhcp_settings *response; - /** Response obtained from ProxyDHCP server */ - struct dhcp_settings *proxy_response; + enum dhcp_session_state state; + /** DHCPOFFER obtained during DHCPDISCOVER */ + struct dhcp_settings *dhcpoffer; + /** ProxyDHCPOFFER obtained during DHCPDISCOVER */ + struct dhcp_settings *proxydhcpoffer; /** Retransmission timer */ struct retry_timer timer; - /** Session start time (in ticks) */ + /** Start time of the current state (in ticks) */ unsigned long start; }; @@ -465,8 +329,8 @@ static void dhcp_free ( struct refcnt *refcnt ) { container_of ( refcnt, struct dhcp_session, refcnt ); netdev_put ( dhcp->netdev ); - dhcpset_put ( dhcp->response ); - dhcpset_put ( dhcp->proxy_response ); + dhcpset_put ( dhcp->dhcpoffer ); + dhcpset_put ( dhcp->proxydhcpoffer ); free ( dhcp ); } @@ -490,46 +354,182 @@ static void dhcp_finished ( struct dhcp_session *dhcp, int rc ) { job_done ( &dhcp->job, rc ); } +/**************************************************************************** + * + * Data transfer interface + * + */ + /** - * Register options received via DHCP + * Create a DHCP packet * - * @v dhcp DHCP session + * @v dhcppkt DHCP packet structure to fill in + * @v netdev Network device + * @v msgtype DHCP message type + * @v options Initial options to include (or NULL) + * @v data Buffer for DHCP packet + * @v max_len Size of DHCP packet buffer * @ret rc Return status code + * + * Creates a DHCP packet in the specified buffer, and fills out a @c + * dhcp_packet structure. */ -static int dhcp_register_settings ( struct dhcp_session *dhcp ) { - struct settings *old_settings; - struct settings *settings; - struct settings *parent; +int dhcp_create_packet ( struct dhcp_packet *dhcppkt, + struct net_device *netdev, uint8_t msgtype, + struct dhcp_options *options, + void *data, size_t max_len ) { + struct dhcphdr *dhcphdr = data; + size_t options_len; + unsigned int hlen; int rc; - /* Register ProxyDHCP settings, if present */ - if ( dhcp->proxy_response ) { - settings = &dhcp->proxy_response->settings; - settings->name = PROXYDHCP_SETTINGS_NAME; - old_settings = find_settings ( settings->name ); - if ( old_settings ) - unregister_settings ( old_settings ); - if ( ( rc = register_settings ( settings, NULL ) ) != 0 ) - return rc; + /* Sanity check */ + options_len = ( options ? options->len : 0 ); + if ( max_len < ( sizeof ( *dhcphdr ) + options_len ) ) + return -ENOSPC; + + /* Initialise DHCP packet content */ + memset ( dhcphdr, 0, max_len ); + dhcphdr->xid = dhcp_xid ( netdev ); + dhcphdr->magic = htonl ( DHCP_MAGIC_COOKIE ); + dhcphdr->htype = ntohs ( netdev->ll_protocol->ll_proto ); + dhcphdr->op = dhcp_op[msgtype]; + /* If hardware length exceeds the chaddr field length, don't + * use the chaddr field. This is as per RFC4390. + */ + hlen = netdev->ll_protocol->ll_addr_len; + if ( hlen > sizeof ( dhcphdr->chaddr ) ) { + hlen = 0; + dhcphdr->flags = htons ( BOOTP_FL_BROADCAST ); } + dhcphdr->hlen = hlen; + memcpy ( dhcphdr->chaddr, netdev->ll_addr, hlen ); + memcpy ( dhcphdr->options, options->data, options_len ); - /* Register DHCP settings */ - parent = netdev_settings ( dhcp->netdev ); - settings = &dhcp->response->settings; - old_settings = find_child_settings ( parent, settings->name ); - if ( old_settings ) - unregister_settings ( old_settings ); - if ( ( rc = register_settings ( settings, parent ) ) != 0 ) + /* Initialise DHCP packet structure */ + memset ( dhcppkt, 0, sizeof ( *dhcppkt ) ); + dhcppkt_init ( dhcppkt, data, max_len ); + + /* Set DHCP_MESSAGE_TYPE option */ + if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_MESSAGE_TYPE, + &msgtype, sizeof ( msgtype ) ) ) != 0 ) return rc; return 0; } -/**************************************************************************** - * - * Data transfer interface +/** + * Create DHCP request packet * + * @v dhcppkt DHCP packet structure to fill in + * @v netdev Network device + * @v ciaddr Client IP address + * @v offer DHCP offer, if applicable + * @v data Buffer for DHCP packet + * @v max_len Size of DHCP packet buffer + * @ret rc Return status code */ +int dhcp_create_request ( struct dhcp_packet *dhcppkt, + struct net_device *netdev, struct in_addr ciaddr, + struct dhcp_packet *offer, + void *data, size_t max_len ) { + struct device_description *desc = &netdev->dev->desc; + struct dhcp_netdev_desc dhcp_desc; + struct dhcp_client_id client_id; + struct dhcp_client_uuid client_uuid; + unsigned int msgtype; + size_t dhcp_features_len; + size_t ll_addr_len; + int rc; + + /* Create DHCP packet */ + msgtype = ( offer ? DHCPREQUEST : DHCPDISCOVER ); + if ( ( rc = dhcp_create_packet ( dhcppkt, netdev, msgtype, + &dhcp_request_options, data, + max_len ) ) != 0 ) { + DBG ( "DHCP could not create DHCP packet: %s\n", + strerror ( rc ) ); + return rc; + } + + /* Set client IP address */ + dhcppkt->dhcphdr->ciaddr = ciaddr; + + /* Copy any required options from previous server repsonse */ + if ( offer ) { + struct in_addr server = { 0 }; + struct in_addr *ip = &offer->dhcphdr->yiaddr; + + /* Copy server identifier, if present */ + if ( ( dhcppkt_fetch ( offer, DHCP_SERVER_IDENTIFIER, &server, + sizeof ( server ) ) >= 0 ) && + ( ( rc = dhcppkt_store ( dhcppkt, DHCP_SERVER_IDENTIFIER, + &server, + sizeof ( server ) ) ) != 0 ) ) { + DBG ( "DHCP could not set server ID: %s\n", + strerror ( rc ) ); + return rc; + } + + /* Copy requested IP address, if present */ + if ( ( ip->s_addr != 0 ) && + ( ( rc = dhcppkt_store ( dhcppkt, DHCP_REQUESTED_ADDRESS, + ip, sizeof ( *ip ) ) ) != 0 ) ) { + DBG ( "DHCP could not set requested address: %s\n", + strerror ( rc ) ); + return rc; + } + } + + /* Add options to identify the feature list */ + dhcp_features_len = ( dhcp_features_end - dhcp_features ); + if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_EB_ENCAP, dhcp_features, + dhcp_features_len ) ) != 0 ) { + DBG ( "DHCP could not set features list option: %s\n", + strerror ( rc ) ); + return rc; + } + + /* Add options to identify the network device */ + dhcp_desc.type = desc->bus_type; + dhcp_desc.vendor = htons ( desc->vendor ); + dhcp_desc.device = htons ( desc->device ); + if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_EB_BUS_ID, &dhcp_desc, + sizeof ( dhcp_desc ) ) ) != 0 ) { + DBG ( "DHCP could not set bus ID option: %s\n", + strerror ( rc ) ); + return rc; + } + + /* Add DHCP client identifier. Required for Infiniband, and + * doesn't hurt other link layers. + */ + client_id.ll_proto = ntohs ( netdev->ll_protocol->ll_proto ); + ll_addr_len = netdev->ll_protocol->ll_addr_len; + assert ( ll_addr_len <= sizeof ( client_id.ll_addr ) ); + memcpy ( client_id.ll_addr, netdev->ll_addr, ll_addr_len ); + if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_CLIENT_ID, &client_id, + ( ll_addr_len + 1 ) ) ) != 0 ) { + DBG ( "DHCP could not set client ID: %s\n", + strerror ( rc ) ); + return rc; + } + + /* Add client UUID, if we have one. Required for PXE. */ + client_uuid.type = DHCP_CLIENT_UUID_TYPE; + if ( ( rc = fetch_uuid_setting ( NULL, &uuid_setting, + &client_uuid.uuid ) ) >= 0 ) { + if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_CLIENT_UUID, + &client_uuid, + sizeof ( client_uuid ) ) ) != 0 ) { + DBG ( "DHCP could not set client UUID: %s\n", + strerror ( rc ) ); + return rc; + } + } + + return 0; +} /** * Transmit DHCP request @@ -537,38 +537,63 @@ static int dhcp_register_settings ( struct dhcp_session *dhcp ) { * @v dhcp DHCP session * @ret rc Return status code */ -static int dhcp_send_request ( struct dhcp_session *dhcp ) { +static int dhcp_tx ( struct dhcp_session *dhcp ) { + static struct sockaddr_in proxydhcp_server = { + .sin_family = AF_INET, + .sin_port = htons ( PROXYDHCP_PORT ), + }; struct xfer_metadata meta = { .netdev = dhcp->netdev, }; struct io_buffer *iobuf; - struct dhcp_packet *dhcpoffer = NULL; struct dhcp_packet dhcppkt; + struct dhcp_packet *offer = NULL; + struct in_addr ciaddr = { 0 }; + int check_len; int rc; - - DBGC ( dhcp, "DHCP %p transmitting %s\n", - dhcp, dhcp_msgtype_name ( dhcp->state ) ); - - assert ( ( dhcp->state == DHCPDISCOVER ) || - ( dhcp->state == DHCPREQUEST ) ); /* Start retry timer. Do this first so that failures to * transmit will be retried. */ start_timer ( &dhcp->timer ); + /* Determine packet contents based on current state */ + switch ( dhcp->state ) { + case DHCP_STATE_DISCOVER: + DBGC ( dhcp, "DHCP %p transmitting DHCPDISCOVER\n", dhcp ); + break; + case DHCP_STATE_REQUEST: + DBGC ( dhcp, "DHCP %p transmitting DHCPREQUEST\n", dhcp ); + assert ( dhcp->dhcpoffer ); + offer = &dhcp->dhcpoffer->dhcppkt; + break; + case DHCP_STATE_PROXYREQUEST: + DBGC ( dhcp, "DHCP %p transmitting ProxyDHCPREQUEST\n", dhcp ); + assert ( dhcp->dhcpoffer ); + assert ( dhcp->proxydhcpoffer ); + offer = &dhcp->proxydhcpoffer->dhcppkt; + ciaddr = dhcp->dhcpoffer->dhcppkt.dhcphdr->yiaddr; + check_len = dhcppkt_fetch ( offer, DHCP_SERVER_IDENTIFIER, + &proxydhcp_server.sin_addr, + sizeof(proxydhcp_server.sin_addr)); + meta.dest = ( struct sockaddr * ) &proxydhcp_server; + assert ( ciaddr.s_addr != 0 ); + assert ( proxydhcp_server.sin_addr.s_addr != 0 ); + assert ( check_len == sizeof ( proxydhcp_server.sin_addr ) ); + break; + default: + assert ( 0 ); + break; + } + /* Allocate buffer for packet */ iobuf = xfer_alloc_iob ( &dhcp->xfer, DHCP_MIN_LEN ); if ( ! iobuf ) return -ENOMEM; /* Create DHCP packet in temporary buffer */ - if ( dhcp->state == DHCPREQUEST ) { - assert ( dhcp->response ); - dhcpoffer = &dhcp->response->dhcppkt; - } - if ( ( rc = create_dhcp_request ( &dhcppkt, dhcp->netdev, - dhcpoffer, iobuf->data, + if ( ( rc = dhcp_create_request ( &dhcppkt, dhcp->netdev, + ciaddr, offer, iobuf->data, iob_tailroom ( iobuf ) ) ) != 0 ) { DBGC ( dhcp, "DHCP %p could not construct DHCP request: %s\n", dhcp, strerror ( rc ) ); @@ -591,27 +616,229 @@ static int dhcp_send_request ( struct dhcp_session *dhcp ) { } /** - * Handle DHCP retry timer expiry + * Transition to new DHCP session state * - * @v timer DHCP retry timer - * @v fail Failure indicator + * @v dhcp DHCP session + * @v state New session state */ -static void dhcp_timer_expired ( struct retry_timer *timer, int fail ) { - struct dhcp_session *dhcp = - container_of ( timer, struct dhcp_session, timer ); +static void dhcp_set_state ( struct dhcp_session *dhcp, + enum dhcp_session_state state ) { + DBGC ( dhcp, "DHCP %p entering %s state\n", + dhcp, dhcp_state_name ( state ) ); + dhcp->state = state; + dhcp->start = currticks(); + start_timer_nodelay ( &dhcp->timer ); +} - if ( fail ) { - dhcp_finished ( dhcp, -ETIMEDOUT ); - } else { - dhcp_send_request ( dhcp ); +/** + * Store received DHCPOFFER + * + * @v dhcp DHCP session + * @v dhcpoffer Received DHCPOFFER + * @v stored_dhcpoffer Location to store DHCPOFFER + * + * The DHCPOFFER will be stored in place of the existing stored + * DHCPOFFER if its priority is equal to or greater than the stored + * DHCPOFFER. + */ +static void dhcp_store_dhcpoffer ( struct dhcp_session *dhcp, + struct dhcp_settings *dhcpoffer, + struct dhcp_settings **stored_dhcpoffer ) { + uint8_t stored_priority = 0; + uint8_t priority = 0; + + /* Get priorities of the two DHCPOFFERs */ + if ( *stored_dhcpoffer ) { + dhcppkt_fetch ( &(*stored_dhcpoffer)->dhcppkt, + DHCP_EB_PRIORITY, &stored_priority, + sizeof ( stored_priority ) ); + } + dhcppkt_fetch ( &dhcpoffer->dhcppkt, DHCP_EB_PRIORITY, &priority, + sizeof ( priority ) ); + + /* Replace stored offer only if priority is equal or greater */ + if ( priority >= stored_priority ) { + if ( *stored_dhcpoffer ) { + DBGC ( dhcp, "DHCP %p stored DHCPOFFER %p discarded\n", + dhcp, *stored_dhcpoffer ); + } + DBGC ( dhcp, "DHCP %p received DHCPOFFER %p stored\n", + dhcp, dhcpoffer ); + dhcpset_put ( *stored_dhcpoffer ); + *stored_dhcpoffer = dhcpset_get ( dhcpoffer ); + } +} + +/** + * Handle received DHCPOFFER + * + * @v dhcp DHCP session + * @v dhcpoffer Received DHCPOFFER + */ +static void dhcp_rx_dhcpoffer ( struct dhcp_session *dhcp, + struct dhcp_settings *dhcpoffer ) { + char vci[9]; /* "PXEClient" */ + int len; + uint8_t ignore_proxy = 0; + unsigned long elapsed; + + /* Check for presence of DHCP server ID */ + if ( dhcppkt_fetch ( &dhcpoffer->dhcppkt, DHCP_SERVER_IDENTIFIER, + NULL, 0 ) != sizeof ( struct in_addr ) ) { + DBGC ( dhcp, "DHCP %p received DHCPOFFER %p missing server " + "identifier\n", dhcp, dhcpoffer ); + return; + } + + /* If there is an IP address, it's a normal DHCPOFFER */ + if ( dhcpoffer->dhcppkt.dhcphdr->yiaddr.s_addr != 0 ) { + DBGC ( dhcp, "DHCP %p received DHCPOFFER %p has IP address\n", + dhcp, dhcpoffer ); + dhcp_store_dhcpoffer ( dhcp, dhcpoffer, &dhcp->dhcpoffer ); + } + + /* If there is a "PXEClient" vendor class ID, it's a + * ProxyDHCPOFFER. Note that it could be both a normal + * DHCPOFFER and a ProxyDHCPOFFER. + */ + len = dhcppkt_fetch ( &dhcpoffer->dhcppkt, DHCP_VENDOR_CLASS_ID, + vci, sizeof ( vci ) ); + if ( ( len >= ( int ) sizeof ( vci ) ) && + ( strncmp ( "PXEClient", vci, sizeof ( vci ) ) == 0 ) ) { + DBGC ( dhcp, "DHCP %p received DHCPOFFER %p is a " + "ProxyDHCPOFFER\n", dhcp, dhcpoffer ); + dhcp_store_dhcpoffer ( dhcp, dhcpoffer, + &dhcp->proxydhcpoffer ); + } + + /* We can transition to making the DHCPREQUEST when we have a + * valid DHCPOFFER, and either: + * + * o The DHCPOFFER instructs us to not wait for ProxyDHCP, or + * o We have a valid ProxyDHCPOFFER, or + * o We have allowed sufficient time for ProxyDHCPOFFERs. + */ + + /* If we don't yet have a DHCPOFFER, do nothing */ + if ( ! dhcp->dhcpoffer ) + return; + + /* If the DHCPOFFER instructs us to ignore ProxyDHCP, discard + * any ProxyDHCPOFFER + */ + dhcppkt_fetch ( &dhcp->dhcpoffer->dhcppkt, DHCP_EB_NO_PROXYDHCP, + &ignore_proxy, sizeof ( ignore_proxy ) ); + if ( ignore_proxy && dhcp->proxydhcpoffer ) { + DBGC ( dhcp, "DHCP %p discarding ProxyDHCPOFFER\n", dhcp ); + dhcpset_put ( dhcp->proxydhcpoffer ); + dhcp->proxydhcpoffer = NULL; + } + + /* If we can't yet transition to DHCPREQUEST, do nothing */ + elapsed = ( currticks() - dhcp->start ); + if ( ! ( ignore_proxy || dhcp->proxydhcpoffer || + ( elapsed > PROXYDHCP_WAIT_TIME ) ) ) + return; + + /* Transition to DHCPREQUEST */ + dhcp_set_state ( dhcp, DHCP_STATE_REQUEST ); +} + +/** + * Store received DHCPACK + * + * @v dhcp DHCP session + * @v dhcpack Received DHCPACK + * + * The DHCPACK will be registered as a settings block. + */ +static int dhcp_store_dhcpack ( struct dhcp_session *dhcp, + struct dhcp_settings *dhcpack, + struct settings *parent ) { + struct settings *settings = &dhcpack->settings; + struct settings *old_settings; + int rc; + + /* Unregister any old settings obtained via DHCP */ + if ( ( old_settings = find_child_settings ( parent, settings->name ) )) + unregister_settings ( old_settings ); + + /* Register new settings */ + if ( ( rc = register_settings ( settings, parent ) ) != 0 ) { + DBGC ( dhcp, "DHCP %p could not register settings: %s\n", + dhcp, strerror ( rc ) ); + dhcp_finished ( dhcp, rc ); /* This is a fatal error */ + return rc; + } + + return 0; +} + +/** + * Handle received DHCPACK + * + * @v dhcp DHCP session + * @v dhcpack Received DHCPACK + */ +static void dhcp_rx_dhcpack ( struct dhcp_session *dhcp, + struct dhcp_settings *dhcpack ) { + struct settings *parent; + struct in_addr offer_server_id = { 0 }; + struct in_addr ack_server_id = { 0 }; + int rc; + + /* Verify server ID matches */ + assert ( dhcp->dhcpoffer != NULL ); + dhcppkt_fetch ( &dhcp->dhcpoffer->dhcppkt, DHCP_SERVER_IDENTIFIER, + &offer_server_id, sizeof ( offer_server_id ) ); + dhcppkt_fetch ( &dhcpack->dhcppkt, DHCP_SERVER_IDENTIFIER, + &ack_server_id, sizeof ( ack_server_id ) ); + if ( offer_server_id.s_addr != ack_server_id.s_addr ) { + DBGC ( dhcp, "DHCP %p ignoring DHCPACK with wrong server ID\n", + dhcp ); + return; + } + + /* Register settings */ + parent = netdev_settings ( dhcp->netdev ); + if ( ( rc = dhcp_store_dhcpack ( dhcp, dhcpack, parent ) ) !=0 ) + return; + + /* If we have a ProxyDHCPOFFER, transition to PROXYDHCPREQUEST */ + if ( dhcp->proxydhcpoffer ) { + dhcp_set_state ( dhcp, DHCP_STATE_PROXYREQUEST ); + return; } + + /* Terminate DHCP */ + dhcp_finished ( dhcp, 0 ); +} + +/** + * Handle received ProxyDHCPACK + * + * @v dhcp DHCP session + * @v proxydhcpack Received ProxyDHCPACK + */ +static void dhcp_rx_proxydhcpack ( struct dhcp_session *dhcp, + struct dhcp_settings *proxydhcpack ) { + int rc; + + /* Rename settings */ + proxydhcpack->settings.name = PROXYDHCP_SETTINGS_NAME; + + /* Register settings */ + if ( ( rc = dhcp_store_dhcpack ( dhcp, proxydhcpack, NULL ) ) != 0 ) + return; + + /* Terminate DHCP */ + dhcp_finished ( dhcp, 0 ); } /** * Receive new data * * @v xfer Data transfer interface - * @v iobuf I/O buffer * @v data Received data * @v len Length of received data * @ret rc Return status code @@ -620,108 +847,52 @@ static int dhcp_deliver_raw ( struct xfer_interface *xfer, const void *data, size_t len ) { struct dhcp_session *dhcp = container_of ( xfer, struct dhcp_session, xfer ); - struct dhcp_settings *response; - struct dhcp_settings **store_response; + struct dhcp_settings *dhcpset; struct dhcphdr *dhcphdr; uint8_t msgtype = 0; - uint8_t priority = 0; - uint8_t existing_priority = 0; - unsigned long elapsed; - int is_proxy; - uint8_t ignore_proxy = 0; - int rc; /* Convert packet into a DHCP settings block */ - response = dhcpset_create ( data, len ); - if ( ! response ) { + dhcpset = dhcpset_create ( data, len ); + if ( ! dhcpset ) { DBGC ( dhcp, "DHCP %p could not store DHCP packet\n", dhcp ); return -ENOMEM; } - dhcphdr = response->dhcppkt.dhcphdr; + dhcphdr = dhcpset->dhcppkt.dhcphdr; - /* Check for matching transaction ID */ - if ( dhcphdr->xid != dhcp_xid ( dhcp->netdev ) ) { - DBGC ( dhcp, "DHCP %p wrong transaction ID (wanted %08lx, " - "got %08lx)\n", dhcp, ntohl ( dhcphdr->xid ), - ntohl ( dhcp_xid ( dhcp->netdev ) ) ); - goto out_discard; - }; - - /* Determine and verify message type */ - is_proxy = ( dhcphdr->yiaddr.s_addr == 0 ); - dhcppkt_fetch ( &response->dhcppkt, DHCP_MESSAGE_TYPE, &msgtype, + /* Identify message type */ + dhcppkt_fetch ( &dhcpset->dhcppkt, DHCP_MESSAGE_TYPE, &msgtype, sizeof ( msgtype ) ); - DBGC ( dhcp, "DHCP %p received %s%s\n", dhcp, - ( is_proxy ? "Proxy" : "" ), dhcp_msgtype_name ( msgtype ) ); - if ( ( ( dhcp->state != DHCPDISCOVER ) || ( msgtype != DHCPOFFER ) ) && - ( ( dhcp->state != DHCPREQUEST ) || ( msgtype != DHCPACK ) ) ) { - DBGC ( dhcp, "DHCP %p discarding %s while in %s state\n", - dhcp, dhcp_msgtype_name ( msgtype ), - dhcp_msgtype_name ( dhcp->state ) ); - goto out_discard; - } - - /* Update stored standard/ProxyDHCP options, if the new - * options have equal or higher priority than the - * currently-stored options. - */ - store_response = ( is_proxy ? &dhcp->proxy_response : &dhcp->response); - if ( *store_response ) { - dhcppkt_fetch ( &(*store_response)->dhcppkt, DHCP_EB_PRIORITY, - &existing_priority, - sizeof ( existing_priority ) ); - } - dhcppkt_fetch ( &response->dhcppkt, DHCP_EB_PRIORITY, &priority, - sizeof ( priority ) ); - if ( priority >= existing_priority ) { - dhcpset_put ( *store_response ); - *store_response = response; - } else { - dhcpset_put ( response ); - } + DBGC ( dhcp, "DHCP %p received %s %p\n", + dhcp, dhcp_msgtype_name ( msgtype ), dhcpset ); - /* If we don't yet have a standard DHCP response (i.e. one - * with an IP address), then just leave the timer running. - */ - if ( ! dhcp->response ) + /* Check for matching transaction ID */ + if ( dhcphdr->xid != dhcp_xid ( dhcp->netdev ) ) { + DBGC ( dhcp, "DHCP %p received %s %p has bad transaction ID\n", + dhcp, dhcp_msgtype_name ( msgtype ), dhcpset ); goto out; + }; - /* Handle DHCP response */ - dhcppkt_fetch ( &dhcp->response->dhcppkt, DHCP_EB_NO_PROXYDHCP, - &ignore_proxy, sizeof ( ignore_proxy ) ); + /* Handle packet based on current state */ switch ( dhcp->state ) { - case DHCPDISCOVER: - /* If we have allowed sufficient time for ProxyDHCP - * reponses, then transition to making the DHCPREQUEST. - */ - elapsed = ( currticks() - dhcp->start ); - if ( ignore_proxy || ( elapsed > PROXYDHCP_WAIT_TIME ) ) { - stop_timer ( &dhcp->timer ); - dhcp->state = DHCPREQUEST; - dhcp_send_request ( dhcp ); - } + case DHCP_STATE_DISCOVER: + if ( msgtype == DHCPOFFER ) + dhcp_rx_dhcpoffer ( dhcp, dhcpset ); break; - case DHCPREQUEST: - /* DHCP finished; register options and exit */ - if ( ignore_proxy && dhcp->proxy_response ) { - dhcpset_put ( dhcp->proxy_response ); - dhcp->proxy_response = NULL; - } - if ( ( rc = dhcp_register_settings ( dhcp ) ) != 0 ) { - dhcp_finished ( dhcp, rc ); - break; - } - dhcp_finished ( dhcp, 0 ); + case DHCP_STATE_REQUEST: + if ( msgtype == DHCPACK ) + dhcp_rx_dhcpack ( dhcp, dhcpset ); + break; + case DHCP_STATE_PROXYREQUEST: + if ( msgtype == DHCPACK ) + dhcp_rx_proxydhcpack ( dhcp, dhcpset ); break; default: assert ( 0 ); + break; } out: - return 0; - - out_discard: - dhcpset_put ( response ); + dhcpset_put ( dhcpset ); return 0; } @@ -735,6 +906,38 @@ static struct xfer_interface_operations dhcp_xfer_operations = { .deliver_raw = dhcp_deliver_raw, }; +/** + * Handle DHCP retry timer expiry + * + * @v timer DHCP retry timer + * @v fail Failure indicator + */ +static void dhcp_timer_expired ( struct retry_timer *timer, int fail ) { + struct dhcp_session *dhcp = + container_of ( timer, struct dhcp_session, timer ); + unsigned long elapsed = ( currticks() - dhcp->start ); + + /* If we have failed, terminate DHCP */ + if ( fail ) { + dhcp_finished ( dhcp, -ETIMEDOUT ); + return; + } + + /* Give up waiting for ProxyDHCP before we reach the failure point */ + if ( elapsed > PROXYDHCP_WAIT_TIME ) { + if ( dhcp->state == DHCP_STATE_DISCOVER ) { + dhcp_set_state ( dhcp, DHCP_STATE_REQUEST ); + return; + } else if ( dhcp->state == DHCP_STATE_PROXYREQUEST ) { + dhcp_finished ( dhcp, 0 ); + return; + } + } + + /* Otherwise, retransmit current packet */ + dhcp_tx ( dhcp ); +} + /**************************************************************************** * * Job control interface @@ -801,7 +1004,6 @@ int start_dhcp ( struct job_interface *job, struct net_device *netdev ) { xfer_init ( &dhcp->xfer, &dhcp_xfer_operations, &dhcp->refcnt ); dhcp->netdev = netdev_get ( netdev ); dhcp->timer.expired = dhcp_timer_expired; - dhcp->state = DHCPDISCOVER; dhcp->start = currticks(); /* Instantiate child objects and attach to our interfaces */ |