david/ipxe
Archived
1
0
This repository has been archived on 2020-12-06. You can view files and clone it, but cannot push or open issues or pull requests.
ipxe/src/net/dhcppkt.c
Michael Brown 8406115834 [build] Rename gPXE to iPXE
Access to the gpxe.org and etherboot.org domains and associated
resources has been revoked by the registrant of the domain.  Work
around this problem by renaming project from gPXE to iPXE, and
updating URLs to match.

Also update README, LOG and COPYRIGHTS to remove obsolete information.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2010-04-19 23:43:39 +01:00

284 lines
7.7 KiB
C

/*
* Copyright (C) 2008 Michael Brown <mbrown@fensystems.co.uk>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
FILE_LICENCE ( GPL2_OR_LATER );
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <ipxe/netdevice.h>
#include <ipxe/dhcp.h>
#include <ipxe/dhcpopts.h>
#include <ipxe/dhcppkt.h>
/** @file
*
* DHCP packets
*
*/
/****************************************************************************
*
* DHCP packet raw interface
*
*/
/**
* Calculate used length of an IPv4 field within a DHCP packet
*
* @v data Field data
* @v len Length of field
* @ret used Used length of field
*/
static size_t used_len_ipv4 ( const void *data, size_t len __unused ) {
const struct in_addr *in = data;
return ( in->s_addr ? sizeof ( *in ) : 0 );
}
/**
* Calculate used length of a string field within a DHCP packet
*
* @v data Field data
* @v len Length of field
* @ret used Used length of field
*/
static size_t used_len_string ( const void *data, size_t len ) {
return strnlen ( data, len );
}
/** A dedicated field within a DHCP packet */
struct dhcp_packet_field {
/** Settings tag number */
unsigned int tag;
/** Offset within DHCP packet */
uint16_t offset;
/** Length of field */
uint16_t len;
/** Calculate used length of field
*
* @v data Field data
* @v len Length of field
* @ret used Used length of field
*/
size_t ( * used_len ) ( const void *data, size_t len );
};
/** Declare a dedicated field within a DHCP packet
*
* @v _tag Settings tag number
* @v _field Field name
* @v _used_len Function to calculate used length of field
*/
#define DHCP_PACKET_FIELD( _tag, _field, _used_len ) { \
.tag = (_tag), \
.offset = offsetof ( struct dhcphdr, _field ), \
.len = sizeof ( ( ( struct dhcphdr * ) 0 )->_field ), \
.used_len = _used_len, \
}
/** Dedicated fields within a DHCP packet */
static struct dhcp_packet_field dhcp_packet_fields[] = {
DHCP_PACKET_FIELD ( DHCP_EB_YIADDR, yiaddr, used_len_ipv4 ),
DHCP_PACKET_FIELD ( DHCP_EB_SIADDR, siaddr, used_len_ipv4 ),
DHCP_PACKET_FIELD ( DHCP_TFTP_SERVER_NAME, sname, used_len_string ),
DHCP_PACKET_FIELD ( DHCP_BOOTFILE_NAME, file, used_len_string ),
};
/**
* Get address of a DHCP packet field
*
* @v dhcphdr DHCP packet header
* @v field DHCP packet field
* @ret data Packet field data
*/
static inline void * dhcp_packet_field ( struct dhcphdr *dhcphdr,
struct dhcp_packet_field *field ) {
return ( ( ( void * ) dhcphdr ) + field->offset );
}
/**
* Find DHCP packet field corresponding to settings tag number
*
* @v tag Settings tag number
* @ret field DHCP packet field, or NULL
*/
static struct dhcp_packet_field *
find_dhcp_packet_field ( unsigned int tag ) {
struct dhcp_packet_field *field;
unsigned int i;
for ( i = 0 ; i < ( sizeof ( dhcp_packet_fields ) /
sizeof ( dhcp_packet_fields[0] ) ) ; i++ ) {
field = &dhcp_packet_fields[i];
if ( field->tag == tag )
return field;
}
return NULL;
}
/**
* Store value of DHCP packet setting
*
* @v dhcppkt DHCP packet
* @v tag Setting tag number
* @v data Setting data, or NULL to clear setting
* @v len Length of setting data
* @ret rc Return status code
*/
int dhcppkt_store ( struct dhcp_packet *dhcppkt, unsigned int tag,
const void *data, size_t len ) {
struct dhcp_packet_field *field;
void *field_data;
int rc;
/* If this is a special field, fill it in */
if ( ( field = find_dhcp_packet_field ( tag ) ) != NULL ) {
if ( len > field->len )
return -ENOSPC;
field_data = dhcp_packet_field ( dhcppkt->dhcphdr, field );
memset ( field_data, 0, field->len );
memcpy ( dhcp_packet_field ( dhcppkt->dhcphdr, field ),
data, len );
/* Erase any equivalent option from the options block */
dhcpopt_store ( &dhcppkt->options, tag, NULL, 0 );
return 0;
}
/* Otherwise, use the generic options block */
rc = dhcpopt_store ( &dhcppkt->options, tag, data, len );
/* Update our used-length field */
dhcppkt->len = ( offsetof ( struct dhcphdr, options ) +
dhcppkt->options.len );
return rc;
}
/**
* Fetch value of DHCP packet setting
*
* @v dhcppkt DHCP packet
* @v tag Setting tag number
* @v data Buffer to fill with setting data
* @v len Length of buffer
* @ret len Length of setting data, or negative error
*/
int dhcppkt_fetch ( struct dhcp_packet *dhcppkt, unsigned int tag,
void *data, size_t len ) {
struct dhcp_packet_field *field;
void *field_data;
size_t field_len = 0;
/* Identify special field, if any */
if ( ( field = find_dhcp_packet_field ( tag ) ) != NULL ) {
field_data = dhcp_packet_field ( dhcppkt->dhcphdr, field );
field_len = field->used_len ( field_data, field->len );
}
/* Return special field, if it exists and is populated */
if ( field_len ) {
if ( len > field_len )
len = field_len;
memcpy ( data, field_data, len );
return field_len;
}
/* Otherwise, use the generic options block */
return dhcpopt_fetch ( &dhcppkt->options, tag, data, len );
}
/****************************************************************************
*
* DHCP packet settings interface
*
*/
/**
* Store value of DHCP setting
*
* @v settings Settings block
* @v setting Setting to store
* @v data Setting data, or NULL to clear setting
* @v len Length of setting data
* @ret rc Return status code
*/
static int dhcppkt_settings_store ( struct settings *settings,
struct setting *setting,
const void *data, size_t len ) {
struct dhcp_packet *dhcppkt =
container_of ( settings, struct dhcp_packet, settings );
return dhcppkt_store ( dhcppkt, setting->tag, data, len );
}
/**
* Fetch value of DHCP setting
*
* @v settings Settings block, or NULL to search all blocks
* @v setting Setting to fetch
* @v data Buffer to fill with setting data
* @v len Length of buffer
* @ret len Length of setting data, or negative error
*/
static int dhcppkt_settings_fetch ( struct settings *settings,
struct setting *setting,
void *data, size_t len ) {
struct dhcp_packet *dhcppkt =
container_of ( settings, struct dhcp_packet, settings );
return dhcppkt_fetch ( dhcppkt, setting->tag, data, len );
}
/** DHCP settings operations */
static struct settings_operations dhcppkt_settings_operations = {
.store = dhcppkt_settings_store,
.fetch = dhcppkt_settings_fetch,
};
/****************************************************************************
*
* Constructor
*
*/
/**
* Initialise DHCP packet
*
* @v dhcppkt DHCP packet structure to fill in
* @v data DHCP packet raw data
* @v max_len Length of raw data buffer
*
* Initialise a DHCP packet structure from a data buffer containing a
* DHCP packet.
*/
void dhcppkt_init ( struct dhcp_packet *dhcppkt, struct dhcphdr *data,
size_t len ) {
dhcppkt->dhcphdr = data;
dhcppkt->max_len = len;
dhcpopt_init ( &dhcppkt->options, &dhcppkt->dhcphdr->options,
( len - offsetof ( struct dhcphdr, options ) ) );
dhcppkt->len = ( offsetof ( struct dhcphdr, options ) +
dhcppkt->options.len );
settings_init ( &dhcppkt->settings,
&dhcppkt_settings_operations, &dhcppkt->refcnt,
DHCP_SETTINGS_NAME, 0 );
}