diff options
Diffstat (limited to 'src/net')
-rw-r--r-- | src/net/dhcpopts.c | 16 | ||||
-rw-r--r-- | src/net/dhcppkt.c | 30 | ||||
-rw-r--r-- | src/net/fakedhcp.c | 205 | ||||
-rw-r--r-- | src/net/ipv4.c | 143 | ||||
-rw-r--r-- | src/net/netdev_settings.c | 39 | ||||
-rw-r--r-- | src/net/tcp/iscsi.c | 30 | ||||
-rw-r--r-- | src/net/udp/dhcp.c | 303 | ||||
-rw-r--r-- | src/net/udp/dns.c | 17 | ||||
-rw-r--r-- | src/net/udp/tftp.c | 17 |
9 files changed, 503 insertions, 297 deletions
diff --git a/src/net/dhcpopts.c b/src/net/dhcpopts.c index 64b310d4..1898011a 100644 --- a/src/net/dhcpopts.c +++ b/src/net/dhcpopts.c @@ -118,6 +118,11 @@ static int find_dhcp_option_with_encap ( struct dhcp_options *options, ssize_t remaining = options->len; unsigned int option_len; + /* Sanity check */ + if ( tag == DHCP_PAD ) + return -ENOENT; + + /* Search for option */ while ( remaining ) { /* Calculate length of this option. Abort processing * if the length is malformed (i.e. takes us beyond @@ -128,6 +133,9 @@ static int find_dhcp_option_with_encap ( struct dhcp_options *options, remaining -= option_len; if ( remaining < 0 ) break; + /* Check for explicit end marker */ + if ( option->tag == DHCP_END ) + break; /* Check for matching tag */ if ( option->tag == tag ) { DBGC ( options, "DHCPOPT %p found %s (length %d)\n", @@ -135,9 +143,6 @@ static int find_dhcp_option_with_encap ( struct dhcp_options *options, option_len ); return offset; } - /* Check for explicit end marker */ - if ( option->tag == DHCP_END ) - break; /* Check for start of matching encapsulation block */ if ( DHCP_IS_ENCAP_OPT ( tag ) && ( option->tag == DHCP_ENCAPSULATOR ( tag ) ) ) { @@ -151,6 +156,7 @@ static int find_dhcp_option_with_encap ( struct dhcp_options *options, } offset += option_len; } + return -ENOENT; } @@ -255,6 +261,10 @@ static int set_dhcp_option ( struct dhcp_options *options, unsigned int tag, size_t new_len = ( len ? ( len + DHCP_OPTION_HEADER_LEN ) : 0 ); int rc; + /* Sanity check */ + if ( tag == DHCP_PAD ) + return -ENOTTY; + /* Find old instance of this option, if any */ offset = find_dhcp_option_with_encap ( options, tag, &encap_offset ); if ( offset >= 0 ) { diff --git a/src/net/dhcppkt.c b/src/net/dhcppkt.c index 0a11520f..1cf99d8d 100644 --- a/src/net/dhcppkt.c +++ b/src/net/dhcppkt.c @@ -92,20 +92,18 @@ find_dhcp_packet_field ( unsigned int tag ) { } return NULL; } - + /** * Store value of DHCP packet setting * - * @v settings Settings block + * @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 */ -static int dhcppkt_store ( struct settings *settings, unsigned int tag, - const void *data, size_t len ) { - struct dhcp_packet *dhcppkt = - container_of ( settings, struct dhcp_packet, settings ); +int dhcppkt_store ( struct dhcp_packet *dhcppkt, unsigned int tag, + const void *data, size_t len ) { struct dhcp_packet_field *field; int rc; @@ -131,16 +129,14 @@ static int dhcppkt_store ( struct settings *settings, unsigned int tag, /** * Fetch value of DHCP packet setting * - * @v settings Settings block + * @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 */ -static int dhcppkt_fetch ( struct settings *settings, unsigned int tag, - void *data, size_t len ) { - struct dhcp_packet *dhcppkt = - container_of ( settings, struct dhcp_packet, settings ); +int dhcppkt_fetch ( struct dhcp_packet *dhcppkt, unsigned int tag, + void *data, size_t len ) { struct dhcp_packet_field *field; /* If this is a special field, return it */ @@ -156,31 +152,21 @@ static int dhcppkt_fetch ( struct settings *settings, unsigned int tag, return dhcpopt_fetch ( &dhcppkt->options, tag, data, len ); } -/** DHCP settings operations */ -static struct settings_operations dhcppkt_settings_operations = { - .store = dhcppkt_store, - .fetch = dhcppkt_fetch, -}; - /** * Initialise prepopulated DHCP packet * * @v dhcppkt Uninitialised DHCP packet - * @v refcnt Reference counter of containing object, or NULL * @v data Memory for DHCP packet data * @v max_len Length of memory for DHCP packet data * * The memory content must already be filled with valid DHCP options. * A zeroed block counts as a block of valid DHCP options. */ -void dhcppkt_init ( struct dhcp_packet *dhcppkt, struct refcnt *refcnt, - void *data, size_t len ) { +void dhcppkt_init ( struct dhcp_packet *dhcppkt, void *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, - refcnt, "dhcp" ); } diff --git a/src/net/fakedhcp.c b/src/net/fakedhcp.c new file mode 100644 index 00000000..c3054db1 --- /dev/null +++ b/src/net/fakedhcp.c @@ -0,0 +1,205 @@ +/* + * 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 <stdlib.h> +#include <stdio.h> +#include <errno.h> +#include <string.h> +#include <gpxe/settings.h> +#include <gpxe/netdevice.h> +#include <gpxe/dhcppkt.h> +#include <gpxe/fakedhcp.h> + +/** @file + * + * Fake DHCP packets + * + */ + +/** + * Copy settings to DHCP packet + * + * @v dest Destination DHCP packet + * @v source Source settings block + * @v encapsulator Encapsulating setting tag number, or zero + * @ret rc Return status code + */ +static int copy_encap_settings ( struct dhcp_packet *dest, + struct settings *source, + unsigned int encapsulator ) { + struct setting setting = { .name = "" }; + unsigned int subtag; + unsigned int tag; + int len; + int check_len; + int rc; + + for ( subtag = DHCP_MIN_OPTION; subtag <= DHCP_MAX_OPTION; subtag++ ) { + tag = DHCP_ENCAP_OPT ( encapsulator, subtag ); + switch ( tag ) { + case DHCP_EB_ENCAP: + case DHCP_VENDOR_ENCAP: + /* Process encapsulated settings */ + if ( ( rc = copy_encap_settings ( dest, source, + tag ) ) != 0 ) + return rc; + break; + default: + /* Copy setting, if present */ + setting.tag = tag; + len = fetch_setting_len ( source, &setting ); + if ( len < 0 ) + break; + { + char buf[len]; + + check_len = fetch_setting ( source, &setting, + buf, sizeof (buf)); + assert ( check_len == len ); + if ( ( rc = dhcppkt_store ( dest, tag, buf, + sizeof(buf) )) !=0) + return rc; + } + break; + } + } + + return 0; +} + +/** + * Copy settings to DHCP packet + * + * @v dest Destination DHCP packet + * @v source Source settings block + * @ret rc Return status code + */ +static int copy_settings ( struct dhcp_packet *dest, + struct settings *source ) { + return copy_encap_settings ( dest, source, 0 ); +} + +/** + * Create fake DHCPDISCOVER packet + * + * @v netdev Network device + * @v data Buffer for DHCP packet + * @v max_len Size of DHCP packet buffer + * @ret rc Return status code + * + * Used by external code. + */ +int create_fakedhcpdiscover ( struct net_device *netdev, + void *data, size_t max_len ) { + struct dhcp_packet dhcppkt; + int rc; + + if ( ( rc = create_dhcp_request ( &dhcppkt, netdev, NULL, data, + max_len ) ) != 0 ) { + DBG ( "Could not create DHCPDISCOVER: %s\n", + strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Create fake DHCPACK packet + * + * @v netdev Network device + * @v data Buffer for DHCP packet + * @v max_len Size of DHCP packet buffer + * @ret rc Return status code + * + * Used by external code. + */ +int create_fakedhcpack ( struct net_device *netdev, + void *data, size_t max_len ) { + struct dhcp_packet dhcppkt; + int rc; + + /* Create base DHCPACK packet */ + if ( ( rc = create_dhcp_packet ( &dhcppkt, netdev, DHCPACK, NULL, + data, max_len ) ) != 0 ) { + DBG ( "Could not create DHCPACK: %s\n", strerror ( rc ) ); + return rc; + } + + /* Merge in globally-scoped settings, then netdev-specific + * settings. Do it in this order so that netdev-specific + * settings take precedence regardless of stated priorities. + */ + if ( ( rc = copy_settings ( &dhcppkt, NULL ) ) != 0 ) { + DBG ( "Could not set DHCPACK global settings: %s\n", + strerror ( rc ) ); + return rc; + } + if ( ( rc = copy_settings ( &dhcppkt, + netdev_settings ( netdev ) ) ) != 0 ) { + DBG ( "Could not set DHCPACK netdev settings: %s\n", + strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Create ProxyDHCPACK packet + * + * @v netdev Network device + * @v data Buffer for DHCP packet + * @v max_len Size of DHCP packet buffer + * @ret rc Return status code + * + * Used by external code. + */ +int create_fakeproxydhcpack ( struct net_device *netdev, + void *data, size_t max_len ) { + struct dhcp_packet dhcppkt; + struct settings *settings; + int rc; + + /* Identify ProxyDHCP settings */ + settings = find_settings ( PROXYDHCP_SETTINGS_NAME ); + + /* No ProxyDHCP settings => return empty block */ + if ( ! settings ) { + memset ( data, 0, max_len ); + return 0; + } + + /* Create base DHCPACK packet */ + if ( ( rc = create_dhcp_packet ( &dhcppkt, netdev, DHCPACK, NULL, + data, max_len ) ) != 0 ) { + DBG ( "Could not create ProxyDHCPACK: %s\n", + strerror ( rc ) ); + return rc; + } + + /* Merge in ProxyDHCP options */ + if ( ( rc = copy_settings ( &dhcppkt, settings ) ) != 0 ) { + DBG ( "Could not set ProxyDHCPACK settings: %s\n", + strerror ( rc ) ); + return rc; + } + + return 0; +} diff --git a/src/net/ipv4.c b/src/net/ipv4.c index 67bfc2d6..591293b7 100644 --- a/src/net/ipv4.c +++ b/src/net/ipv4.c @@ -96,62 +96,6 @@ static void del_ipv4_miniroute ( struct ipv4_miniroute *miniroute ) { } /** - * Create IPv4 routing table - * - * @ret rc Return status code - */ -static int ipv4_create_routes ( void ) { - struct ipv4_miniroute *miniroute; - struct ipv4_miniroute *tmp; - struct net_device *netdev; - struct settings *settings; - struct in_addr address = { 0 }; - struct in_addr netmask = { 0 }; - struct in_addr gateway = { INADDR_NONE }; - - /* Delete all existing routes */ - list_for_each_entry_safe ( miniroute, tmp, &ipv4_miniroutes, list ) - del_ipv4_miniroute ( miniroute ); - - /* Create a route for each configured network device */ - for_each_netdev ( netdev ) { - settings = netdev_settings ( netdev ); - /* Get IPv4 address */ - address.s_addr = 0; - fetch_ipv4_setting ( settings, DHCP_EB_YIADDR, &address ); - if ( ! address.s_addr ) - continue; - /* Calculate default netmask */ - if ( IN_CLASSA ( ntohl ( address.s_addr ) ) ) { - netmask.s_addr = htonl ( IN_CLASSA_NET ); - } else if ( IN_CLASSB ( ntohl ( address.s_addr ) ) ) { - netmask.s_addr = htonl ( IN_CLASSB_NET ); - } else if ( IN_CLASSC ( ntohl ( address.s_addr ) ) ) { - netmask.s_addr = htonl ( IN_CLASSC_NET ); - } else { - netmask.s_addr = 0; - } - /* Override with subnet mask, if present */ - fetch_ipv4_setting ( settings, DHCP_SUBNET_MASK, &netmask ); - /* Get default gateway, if present */ - gateway.s_addr = INADDR_NONE; - fetch_ipv4_setting ( settings, DHCP_ROUTERS, &gateway ); - /* Configure route */ - miniroute = add_ipv4_miniroute ( netdev, address, - netmask, gateway ); - if ( ! miniroute ) - return -ENOMEM; - } - - return 0; -} - -/** IPv4 settings applicator */ -struct settings_applicator ipv4_settings_applicator __settings_applicator = { - .apply = ipv4_create_routes, -}; - -/** * Perform IPv4 routing * * @v dest Final destination address @@ -600,3 +544,90 @@ struct arp_net_protocol ipv4_arp_protocol __arp_net_protocol = { .net_protocol = &ipv4_protocol, .check = ipv4_arp_check, }; + +/****************************************************************************** + * + * Settings + * + ****************************************************************************** + */ + +/** IPv4 address setting */ +struct setting ip_setting __setting = { + .name = "ip", + .description = "IPv4 address", + .tag = DHCP_EB_YIADDR, + .type = &setting_type_ipv4, +}; + +/** IPv4 subnet mask setting */ +struct setting netmask_setting __setting = { + .name = "netmask", + .description = "IPv4 subnet mask", + .tag = DHCP_SUBNET_MASK, + .type = &setting_type_ipv4, +}; + +/** Default gateway setting */ +struct setting gateway_setting __setting = { + .name = "gateway", + .description = "Default gateway", + .tag = DHCP_ROUTERS, + .type = &setting_type_ipv4, +}; + +/** + * Create IPv4 routing table based on configured settings + * + * @ret rc Return status code + */ +static int ipv4_create_routes ( void ) { + struct ipv4_miniroute *miniroute; + struct ipv4_miniroute *tmp; + struct net_device *netdev; + struct settings *settings; + struct in_addr address = { 0 }; + struct in_addr netmask = { 0 }; + struct in_addr gateway = { INADDR_NONE }; + + /* Delete all existing routes */ + list_for_each_entry_safe ( miniroute, tmp, &ipv4_miniroutes, list ) + del_ipv4_miniroute ( miniroute ); + + /* Create a route for each configured network device */ + for_each_netdev ( netdev ) { + settings = netdev_settings ( netdev ); + /* Get IPv4 address */ + address.s_addr = 0; + fetch_ipv4_setting ( settings, &ip_setting, &address ); + if ( ! address.s_addr ) + continue; + /* Calculate default netmask */ + if ( IN_CLASSA ( ntohl ( address.s_addr ) ) ) { + netmask.s_addr = htonl ( IN_CLASSA_NET ); + } else if ( IN_CLASSB ( ntohl ( address.s_addr ) ) ) { + netmask.s_addr = htonl ( IN_CLASSB_NET ); + } else if ( IN_CLASSC ( ntohl ( address.s_addr ) ) ) { + netmask.s_addr = htonl ( IN_CLASSC_NET ); + } else { + netmask.s_addr = 0; + } + /* Override with subnet mask, if present */ + fetch_ipv4_setting ( settings, &netmask_setting, &netmask ); + /* Get default gateway, if present */ + gateway.s_addr = INADDR_NONE; + fetch_ipv4_setting ( settings, &gateway_setting, &gateway ); + /* Configure route */ + miniroute = add_ipv4_miniroute ( netdev, address, + netmask, gateway ); + if ( ! miniroute ) + return -ENOMEM; + } + + return 0; +} + +/** IPv4 settings applicator */ +struct settings_applicator ipv4_settings_applicator __settings_applicator = { + .apply = ipv4_create_routes, +}; diff --git a/src/net/netdev_settings.c b/src/net/netdev_settings.c index c8e630a9..44aca7d8 100644 --- a/src/net/netdev_settings.c +++ b/src/net/netdev_settings.c @@ -28,28 +28,34 @@ * */ +/** Network device named settings */ +struct setting mac_setting __setting = { + .name = "mac", + .description = "MAC address", + .type = &setting_type_hex, +}; + /** * Store value of network device setting * * @v settings Settings block - * @v tag Setting tag number + * @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 netdev_store ( struct settings *settings, unsigned int tag, +static int netdev_store ( struct settings *settings, struct setting *setting, const void *data, size_t len ) { struct net_device *netdev = container_of ( settings, struct net_device, settings.settings ); - switch ( tag ) { - case DHCP_EB_MAC: + if ( setting_cmp ( setting, &mac_setting ) == 0 ) { if ( len != netdev->ll_protocol->ll_addr_len ) return -EINVAL; memcpy ( netdev->ll_addr, data, len ); return 0; - default : - return simple_settings_store ( settings, tag, data, len ); + } else { + return simple_settings_store ( settings, setting, data, len ); } } @@ -57,24 +63,23 @@ static int netdev_store ( struct settings *settings, unsigned int tag, * Fetch value of network device setting * * @v settings Settings block - * @v tag Setting tag number + * @v setting Setting to fetch * @v data Setting data, or NULL to clear setting * @v len Length of setting data * @ret rc Return status code */ -static int netdev_fetch ( struct settings *settings, unsigned int tag, +static int netdev_fetch ( struct settings *settings, struct setting *setting, void *data, size_t len ) { struct net_device *netdev = container_of ( settings, struct net_device, settings.settings ); - switch ( tag ) { - case DHCP_EB_MAC: + if ( setting_cmp ( setting, &mac_setting ) == 0 ) { if ( len > netdev->ll_protocol->ll_addr_len ) len = netdev->ll_protocol->ll_addr_len; memcpy ( data, netdev->ll_addr, len ); return netdev->ll_protocol->ll_addr_len; - default : - return simple_settings_fetch ( settings, tag, data, len ); + } else { + return simple_settings_fetch ( settings, setting, data, len ); } } @@ -83,13 +88,3 @@ struct settings_operations netdev_settings_operations = { .store = netdev_store, .fetch = netdev_fetch, }; - -/** Network device named settings */ -struct named_setting netdev_named_settings[] __named_setting = { - { - .name = "mac", - .description = "MAC address", - .tag = DHCP_EB_MAC, - .type = &setting_type_hex, - }, -}; diff --git a/src/net/tcp/iscsi.c b/src/net/tcp/iscsi.c index f071b04a..c01ca44b 100644 --- a/src/net/tcp/iscsi.c +++ b/src/net/tcp/iscsi.c @@ -1591,14 +1591,22 @@ int iscsi_attach ( struct scsi_device *scsi, const char *root_path ) { /**************************************************************************** * - * Settings applicators + * Settings * */ +/** iSCSI initiator IQN setting */ +struct setting initiator_iqn_setting __setting = { + .name = "initiator-iqn", + .description = "iSCSI initiator name", + .tag = DHCP_ISCSI_INITIATOR_IQN, + .type = &setting_type_string, +}; + /** An iSCSI string setting */ struct iscsi_string_setting { - /** Setting tag number */ - unsigned int tag; + /** Setting */ + struct setting *setting; /** String to update */ char **string; /** String prefix */ @@ -1608,22 +1616,22 @@ struct iscsi_string_setting { /** iSCSI string settings */ static struct iscsi_string_setting iscsi_string_settings[] = { { - .tag = DHCP_ISCSI_INITIATOR_IQN, + .setting = &initiator_iqn_setting, .string = &iscsi_explicit_initiator_iqn, .prefix = "", }, { - .tag = DHCP_EB_USERNAME, + .setting = &username_setting, .string = &iscsi_username, .prefix = "", }, { - .tag = DHCP_EB_PASSWORD, + .setting = &password_setting, .string = &iscsi_password, .prefix = "", }, { - .tag = DHCP_HOST_NAME, + .setting = &hostname_setting, .string = &iscsi_default_initiator_iqn, .prefix = "iqn.2000-09.org.etherboot:", }, @@ -1648,7 +1656,7 @@ static int apply_iscsi_string_setting ( struct iscsi_string_setting *setting ){ /* Allocate new string */ prefix_len = strlen ( setting->prefix ); - setting_len = fetch_setting_len ( NULL, setting->tag ); + setting_len = fetch_setting_len ( NULL, setting->setting ); if ( setting_len < 0 ) { /* Missing settings are not errors; leave strings as NULL */ return 0; @@ -1660,7 +1668,7 @@ static int apply_iscsi_string_setting ( struct iscsi_string_setting *setting ){ /* Fill new string */ strcpy ( p, setting->prefix ); - check_len = fetch_string_setting ( NULL, setting->tag, + check_len = fetch_string_setting ( NULL, setting->setting, ( p + prefix_len ), ( len - prefix_len ) ); assert ( check_len == setting_len ); @@ -1682,8 +1690,8 @@ static int apply_iscsi_settings ( void ) { sizeof ( iscsi_string_settings[0] ) ) ; i++ ) { setting = &iscsi_string_settings[i]; if ( ( rc = apply_iscsi_string_setting ( setting ) ) != 0 ) { - DBG ( "iSCSI could not apply setting %d\n", - setting->tag ); + DBG ( "iSCSI could not apply setting %s\n", + setting->setting->name ); return rc; } } diff --git a/src/net/udp/dhcp.c b/src/net/udp/dhcp.c index 8789f925..98b8af0b 100644 --- a/src/net/udp/dhcp.c +++ b/src/net/udp/dhcp.c @@ -130,9 +130,6 @@ static uint32_t dhcp_xid ( struct net_device *netdev ) { return xid; } -/** Settings block name used for ProxyDHCP responses */ -#define PROXYDHCP_SETTINGS_NAME "proxydhcp" - /** * Create a DHCP packet * @@ -180,12 +177,12 @@ int create_dhcp_packet ( struct dhcp_packet *dhcppkt, memcpy ( dhcphdr->chaddr, netdev->ll_addr, hlen ); memcpy ( dhcphdr->options, options->data, options_len ); - /* Initialise DHCP packet structure and settings interface */ + /* Initialise DHCP packet structure */ memset ( dhcppkt, 0, sizeof ( *dhcppkt ) ); - dhcppkt_init ( dhcppkt, NULL, data, max_len ); + dhcppkt_init ( dhcppkt, data, max_len ); /* Set DHCP_MESSAGE_TYPE option */ - if ( ( rc = store_setting ( &dhcppkt->settings, DHCP_MESSAGE_TYPE, + if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_MESSAGE_TYPE, &msgtype, sizeof ( msgtype ) ) ) != 0 ) return rc; @@ -230,10 +227,10 @@ struct dhcp_client_uuid { * @v max_len Size of DHCP packet buffer * @ret rc Return status code */ -static int create_dhcp_request ( struct dhcp_packet *dhcppkt, - struct net_device *netdev, - struct dhcp_packet *dhcpoffer, - void *data, size_t max_len ) { +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; @@ -258,27 +255,26 @@ static int create_dhcp_request ( struct dhcp_packet *dhcppkt, struct in_addr server_id; struct in_addr requested_ip; - if ( ( rc = fetch_ipv4_setting ( &dhcpoffer->settings, - DHCP_SERVER_IDENTIFIER, - &server_id ) ) < 0 ) { + 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 ( ( rc = fetch_ipv4_setting ( &dhcpoffer->settings, - DHCP_EB_YIADDR, - &requested_ip ) ) < 0 ) { + 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 = store_setting ( &dhcppkt->settings, - DHCP_SERVER_IDENTIFIER, &server_id, + 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 = store_setting ( &dhcppkt->settings, - DHCP_REQUESTED_ADDRESS, + if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_REQUESTED_ADDRESS, &requested_ip, sizeof ( requested_ip ) ) ) != 0 ){ DBG ( "DHCP could not set requested address: %s\n", @@ -289,8 +285,8 @@ static int create_dhcp_request ( struct dhcp_packet *dhcppkt, /* Add options to identify the feature list */ dhcp_features_len = ( dhcp_features_end - dhcp_features ); - if ( ( rc = store_setting ( &dhcppkt->settings, DHCP_EB_ENCAP, - dhcp_features, dhcp_features_len ) ) !=0 ){ + 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; @@ -300,8 +296,8 @@ static int create_dhcp_request ( struct dhcp_packet *dhcppkt, dhcp_desc.type = desc->bus_type; dhcp_desc.vendor = htons ( desc->vendor ); dhcp_desc.device = htons ( desc->device ); - if ( ( rc = store_setting ( &dhcppkt->settings, DHCP_EB_BUS_ID, - &dhcp_desc, sizeof ( dhcp_desc ) ) ) !=0 ){ + 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; @@ -314,8 +310,8 @@ static int create_dhcp_request ( struct dhcp_packet *dhcppkt, 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 = store_setting ( &dhcppkt->settings, DHCP_CLIENT_ID, - &client_id, ( ll_addr_len + 1 ) ) ) != 0 ){ + 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; @@ -324,8 +320,8 @@ static int create_dhcp_request ( struct dhcp_packet *dhcppkt, /* 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 = store_setting ( &dhcppkt->settings, - DHCP_CLIENT_UUID, &client_uuid, + 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 ) ); @@ -336,169 +332,109 @@ static int create_dhcp_request ( struct dhcp_packet *dhcppkt, return 0; } -/** - * Create DHCPDISCOVER packet +/**************************************************************************** * - * @v netdev Network device - * @v data Buffer for DHCP packet - * @v max_len Size of DHCP packet buffer - * @ret rc Return status code + * DHCP settings * - * Used by external code. */ -int create_dhcpdiscover ( struct net_device *netdev, - void *data, size_t max_len ) { - struct dhcp_packet dhcppkt; - int rc; - if ( ( rc = create_dhcp_request ( &dhcppkt, netdev, NULL, data, - max_len ) ) != 0 ) { - DBG ( "Could not create DHCPDISCOVER: %s\n", - strerror ( rc ) ); - return rc; - } - - return 0; -} +/** A DHCP settings block */ +struct dhcp_settings { + /** Reference counter */ + struct refcnt refcnt; + /** Containing I/O buffer */ + struct io_buffer *iobuf; + /** DHCP packet */ + struct dhcp_packet dhcppkt; + /** Setting interface */ + struct settings settings; +}; /** - * Create DHCPACK packet + * Free DHCP settings block * - * @v netdev Network device - * @v data Buffer for DHCP packet - * @v max_len Size of DHCP packet buffer - * @ret rc Return status code - * - * Used by external code. + * @v refcnt Reference counter */ -int create_dhcpack ( struct net_device *netdev, - void *data, size_t max_len ) { - struct dhcp_packet dhcppkt; - int rc; - - /* Create base DHCPACK packet */ - if ( ( rc = create_dhcp_packet ( &dhcppkt, netdev, DHCPACK, NULL, - data, max_len ) ) != 0 ) { - DBG ( "Could not create DHCPACK: %s\n", strerror ( rc ) ); - return rc; - } - - /* Merge in globally-scoped settings, then netdev-specific - * settings. Do it in this order so that netdev-specific - * settings take precedence regardless of stated priorities. - */ - if ( ( rc = copy_settings ( &dhcppkt.settings, NULL ) ) != 0 ) { - DBG ( "Could not set DHCPACK global settings: %s\n", - strerror ( rc ) ); - return rc; - } - if ( ( rc = copy_settings ( &dhcppkt.settings, - netdev_settings ( netdev ) ) ) != 0 ) { - DBG ( "Could not set DHCPACK netdev settings: %s\n", - strerror ( rc ) ); - return rc; - } +static void dhcpset_free ( struct refcnt *refcnt ) { + struct dhcp_settings *dhcpset = + container_of ( refcnt, struct dhcp_settings, refcnt ); - return 0; + free_iob ( dhcpset->iobuf ); + free ( dhcpset ); } /** - * Create ProxyDHCPACK packet - * - * @v netdev Network device - * @v data Buffer for DHCP packet - * @v max_len Size of DHCP packet buffer - * @ret rc Return status code + * Decrement reference count on DHCP settings block * - * Used by external code. + * @v dhcpset DHCP settings block */ -int create_proxydhcpack ( struct net_device *netdev, - void *data, size_t max_len ) { - struct dhcp_packet dhcppkt; - struct settings *settings; - int rc; - - /* Identify ProxyDHCP settings */ - settings = find_settings ( PROXYDHCP_SETTINGS_NAME ); - - /* No ProxyDHCP settings => return empty block */ - if ( ! settings ) { - memset ( data, 0, max_len ); - return 0; - } - - /* Create base DHCPACK packet */ - if ( ( rc = create_dhcp_packet ( &dhcppkt, netdev, DHCPACK, NULL, - data, max_len ) ) != 0 ) { - DBG ( "Could not create ProxyDHCPACK: %s\n", - strerror ( rc ) ); - return rc; - } - - /* Merge in ProxyDHCP options */ - if ( ( rc = copy_settings ( &dhcppkt.settings, settings ) ) != 0 ) { - DBG ( "Could not set ProxyDHCPACK settings: %s\n", - strerror ( rc ) ); - return rc; - } - - return 0; +static inline void dhcpset_put ( struct dhcp_settings *dhcpset ) { + ref_put ( &dhcpset->refcnt ); } -/**************************************************************************** - * - * DHCP packets contained in I/O buffers +/** + * 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 dhcpset_store ( struct settings *settings, struct setting *setting, + const void *data, size_t len ) { + struct dhcp_settings *dhcpset = + container_of ( settings, struct dhcp_settings, settings ); -/** A DHCP packet contained in an I/O buffer */ -struct dhcp_iobuf_packet { - /** DHCP packet */ - struct dhcp_packet dhcppkt; - /** Reference counter */ - struct refcnt refcnt; - /** Containing I/O buffer */ - struct io_buffer *iobuf; -}; + return dhcppkt_store ( &dhcpset->dhcppkt, setting->tag, data, len ); +} /** - * Free DHCP packet contained in an I/O buffer + * Fetch value of setting * - * @v refcnt Reference counter + * @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 void dhcpiob_free ( struct refcnt *refcnt ) { - struct dhcp_iobuf_packet *dhcpiob = - container_of ( refcnt, struct dhcp_iobuf_packet, refcnt ); +static int dhcpset_fetch ( struct settings *settings, struct setting *setting, + void *data, size_t len ) { + struct dhcp_settings *dhcpset = + container_of ( settings, struct dhcp_settings, settings ); - free_iob ( dhcpiob->iobuf ); - free ( dhcpiob ); + return dhcppkt_fetch ( &dhcpset->dhcppkt, setting->tag, data, len ); } +/** DHCP settings operations */ +static struct settings_operations dhcpset_settings_operations = { + .store = dhcpset_store, + .fetch = dhcpset_fetch, +}; + /** - * Create DHCP packet from I/O buffer + * Create DHCP setting block from I/O buffer * * @v iobuf I/O buffer - * @ret dhcpiob DHCP packet contained in I/O buffer + * @ret dhcpset DHCP settings block * * This function takes ownership of the I/O buffer. Future accesses - * must be via the @c dhcpiob data structure. + * must be via the @c dhcpset data structure. */ -static struct dhcp_iobuf_packet * dhcpiob_create ( struct io_buffer *iobuf ) { - struct dhcp_iobuf_packet *dhcpiob; - - dhcpiob = zalloc ( sizeof ( *dhcpiob ) ); - if ( dhcpiob ) { - dhcpiob->refcnt.free = dhcpiob_free; - dhcpiob->iobuf = iobuf; - dhcppkt_init ( &dhcpiob->dhcppkt, &dhcpiob->refcnt, +static struct dhcp_settings * dhcpset_create_iob ( struct io_buffer *iobuf ) { + struct dhcp_settings *dhcpset; + + dhcpset = zalloc ( sizeof ( *dhcpset ) ); + if ( dhcpset ) { + dhcpset->refcnt.free = dhcpset_free; + dhcpset->iobuf = iobuf; + dhcppkt_init ( &dhcpset->dhcppkt, iobuf->data, iob_len ( iobuf ) ); + settings_init ( &dhcpset->settings, + &dhcpset_settings_operations, &dhcpset->refcnt, + DHCP_SETTINGS_NAME ); } - return dhcpiob; -} - -static void dhcpiob_put ( struct dhcp_iobuf_packet *dhcpiob ) { - if ( dhcpiob ) - ref_put ( &dhcpiob->refcnt ); + return dhcpset; } /**************************************************************************** @@ -526,9 +462,9 @@ struct dhcp_session { */ int state; /** Response obtained from DHCP server */ - struct dhcp_iobuf_packet *response; + struct dhcp_settings *response; /** Response obtained from ProxyDHCP server */ - struct dhcp_iobuf_packet *proxy_response; + struct dhcp_settings *proxy_response; /** Retransmission timer */ struct retry_timer timer; /** Session start time (in ticks) */ @@ -545,8 +481,8 @@ static void dhcp_free ( struct refcnt *refcnt ) { container_of ( refcnt, struct dhcp_session, refcnt ); netdev_put ( dhcp->netdev ); - dhcpiob_put ( dhcp->response ); - dhcpiob_put ( dhcp->proxy_response ); + dhcpset_put ( dhcp->response ); + dhcpset_put ( dhcp->proxy_response ); free ( dhcp ); } @@ -584,7 +520,7 @@ static int dhcp_register_settings ( struct dhcp_session *dhcp ) { /* Register ProxyDHCP settings, if present */ if ( dhcp->proxy_response ) { - settings = &dhcp->proxy_response->dhcppkt.settings; + settings = &dhcp->proxy_response->settings; settings->name = PROXYDHCP_SETTINGS_NAME; old_settings = find_settings ( settings->name ); if ( old_settings ) @@ -595,7 +531,7 @@ static int dhcp_register_settings ( struct dhcp_session *dhcp ) { /* Register DHCP settings */ parent = netdev_settings ( dhcp->netdev ); - settings = &dhcp->response->dhcppkt.settings; + settings = &dhcp->response->settings; old_settings = find_child_settings ( parent, settings->name ); if ( old_settings ) unregister_settings ( old_settings ); @@ -701,24 +637,24 @@ static int dhcp_deliver_iob ( struct xfer_interface *xfer, struct xfer_metadata *meta __unused ) { struct dhcp_session *dhcp = container_of ( xfer, struct dhcp_session, xfer ); - struct dhcp_iobuf_packet *response; - struct dhcp_iobuf_packet **store_response; + struct dhcp_settings *response; + struct dhcp_settings **store_response; struct dhcphdr *dhcphdr; - struct settings *settings; - unsigned int msgtype; + uint8_t msgtype = 0; + uint8_t priority = 0; + uint8_t existing_priority = 0; unsigned long elapsed; int is_proxy; - int ignore_proxy; + uint8_t ignore_proxy = 0; int rc; /* Convert packet into a DHCP-packet-in-iobuf */ - response = dhcpiob_create ( iobuf ); + response = dhcpset_create_iob ( iobuf ); if ( ! response ) { DBGC ( dhcp, "DHCP %p could not store DHCP packet\n", dhcp ); return -ENOMEM; } dhcphdr = response->dhcppkt.dhcphdr; - settings = &response->dhcppkt.settings; /* Check for matching transaction ID */ if ( dhcphdr->xid != dhcp_xid ( dhcp->netdev ) ) { @@ -730,7 +666,8 @@ static int dhcp_deliver_iob ( struct xfer_interface *xfer, /* Determine and verify message type */ is_proxy = ( dhcphdr->yiaddr.s_addr == 0 ); - msgtype = fetch_uintz_setting ( settings, DHCP_MESSAGE_TYPE ); + dhcppkt_fetch ( &response->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 ) ) && @@ -746,14 +683,18 @@ static int dhcp_deliver_iob ( struct xfer_interface *xfer, * currently-stored options. */ store_response = ( is_proxy ? &dhcp->proxy_response : &dhcp->response); - if ( ( ! *store_response ) || - ( fetch_uintz_setting ( settings, DHCP_EB_PRIORITY ) >= - fetch_uintz_setting ( &(*store_response)->dhcppkt.settings, - DHCP_EB_PRIORITY ) ) ) { - dhcpiob_put ( *store_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 { - dhcpiob_put ( response ); + dhcpset_put ( response ); } /* If we don't yet have a standard DHCP response (i.e. one @@ -763,8 +704,8 @@ static int dhcp_deliver_iob ( struct xfer_interface *xfer, goto out; /* Handle DHCP response */ - ignore_proxy = fetch_uintz_setting ( &dhcp->response->dhcppkt.settings, - DHCP_EB_NO_PROXYDHCP ); + dhcppkt_fetch ( &dhcp->response->dhcppkt, DHCP_EB_NO_PROXYDHCP, + &ignore_proxy, sizeof ( ignore_proxy ) ); switch ( dhcp->state ) { case DHCPDISCOVER: /* If we have allowed sufficient time for ProxyDHCP @@ -780,7 +721,7 @@ static int dhcp_deliver_iob ( struct xfer_interface *xfer, case DHCPREQUEST: /* DHCP finished; register options and exit */ if ( ignore_proxy && dhcp->proxy_response ) { - dhcpiob_put ( dhcp->proxy_response ); + dhcpset_put ( dhcp->proxy_response ); dhcp->proxy_response = NULL; } if ( ( rc = dhcp_register_settings ( dhcp ) ) != 0 ) { @@ -797,7 +738,7 @@ static int dhcp_deliver_iob ( struct xfer_interface *xfer, return 0; out_discard: - dhcpiob_put ( response ); + dhcpset_put ( response ); return 0; } diff --git a/src/net/udp/dns.c b/src/net/udp/dns.c index 5e632d18..1bcdbc7e 100644 --- a/src/net/udp/dns.c +++ b/src/net/udp/dns.c @@ -506,6 +506,21 @@ struct resolver dns_resolver __resolver ( RESOLV_NORMAL ) = { .resolv = dns_resolv, }; +/****************************************************************************** + * + * Settings + * + ****************************************************************************** + */ + +/** DNS server setting */ +struct setting dns_setting __setting = { + .name = "dns", + .description = "DNS server", + .tag = DHCP_DNS_SERVERS, + .type = &setting_type_ipv4, +}; + /** * Apply nameserver setting * @@ -516,7 +531,7 @@ static int apply_nameserver_setting ( void ) { ( struct sockaddr_in * ) &nameserver; int len; - if ( ( len = fetch_ipv4_setting ( NULL, DHCP_DNS_SERVERS, + if ( ( len = fetch_ipv4_setting ( NULL, &dns_setting, &sin_nameserver->sin_addr ) ) >= 0 ){ sin_nameserver->sin_family = AF_INET; DBG ( "DNS using nameserver %s\n", diff --git a/src/net/udp/tftp.c b/src/net/udp/tftp.c index 8bd2b80b..e49bcf9f 100644 --- a/src/net/udp/tftp.c +++ b/src/net/udp/tftp.c @@ -1093,6 +1093,21 @@ struct uri_opener mtftp_uri_opener __uri_opener = { .open = mtftp_open, }; +/****************************************************************************** + * + * Settings + * + ****************************************************************************** + */ + +/** TFTP server setting */ +struct setting next_server_setting __setting = { + .name = "next-server", + .description = "TFTP server", + .tag = DHCP_EB_SIADDR, + .type = &setting_type_ipv4, +}; + /** * Apply TFTP configuration settings * @@ -1106,7 +1121,7 @@ static int tftp_apply_settings ( void ) { /* Retrieve TFTP server setting */ last_tftp_server = tftp_server; - fetch_ipv4_setting ( NULL, DHCP_EB_SIADDR, &tftp_server ); + fetch_ipv4_setting ( NULL, &next_server_setting, &tftp_server ); /* If TFTP server setting has changed, set the current working * URI to match. Do it only when the TFTP server has changed |