2 * Copyright (C) 2008 Michael Brown <mbrown@fensystems.co.uk>.
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of the
7 * License, or any later version.
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 #include <gpxe/netdevice.h>
25 #include <gpxe/dhcp.h>
26 #include <gpxe/dhcpopts.h>
27 #include <gpxe/dhcppkt.h>
35 /** A dedicated field within a DHCP packet */
36 struct dhcp_packet_field {
37 /** Settings tag number */
39 /** Offset within DHCP packet */
41 /** Length of field */
45 /** Declare a dedicated field within a DHCP packet
47 * @v _tag Settings tag number
48 * @v _field Field name
50 #define DHCP_PACKET_FIELD( _tag, _field ) { \
52 .offset = offsetof ( struct dhcphdr, _field ), \
53 .len = sizeof ( ( ( struct dhcphdr * ) 0 )->_field ), \
56 /** Dedicated fields within a DHCP packet */
57 static struct dhcp_packet_field dhcp_packet_fields[] = {
58 DHCP_PACKET_FIELD ( DHCP_EB_YIADDR, yiaddr ),
59 DHCP_PACKET_FIELD ( DHCP_EB_SIADDR, siaddr ),
60 DHCP_PACKET_FIELD ( DHCP_TFTP_SERVER_NAME, sname ),
61 DHCP_PACKET_FIELD ( DHCP_BOOTFILE_NAME, file ),
65 * Get address of a DHCP packet field
67 * @v dhcphdr DHCP packet header
68 * @v field DHCP packet field
69 * @ret data Packet field data
71 static inline void * dhcp_packet_field ( struct dhcphdr *dhcphdr,
72 struct dhcp_packet_field *field ) {
73 return ( ( ( void * ) dhcphdr ) + field->offset );
77 * Find DHCP packet field corresponding to settings tag number
79 * @v tag Settings tag number
80 * @ret field DHCP packet field, or NULL
82 static struct dhcp_packet_field *
83 find_dhcp_packet_field ( unsigned int tag ) {
84 struct dhcp_packet_field *field;
87 for ( i = 0 ; i < ( sizeof ( dhcp_packet_fields ) /
88 sizeof ( dhcp_packet_fields[0] ) ) ; i++ ) {
89 field = &dhcp_packet_fields[i];
90 if ( field->tag == tag )
97 * Store value of DHCP packet setting
99 * @v dhcppkt DHCP packet
100 * @v tag Setting tag number
101 * @v data Setting data, or NULL to clear setting
102 * @v len Length of setting data
103 * @ret rc Return status code
105 int dhcppkt_store ( struct dhcp_packet *dhcppkt, unsigned int tag,
106 const void *data, size_t len ) {
107 struct dhcp_packet_field *field;
110 /* If this is a special field, fill it in */
111 if ( ( field = find_dhcp_packet_field ( tag ) ) != NULL ) {
112 if ( len > field->len )
114 memcpy ( dhcp_packet_field ( dhcppkt->dhcphdr, field ),
119 /* Otherwise, use the generic options block */
120 rc = dhcpopt_store ( &dhcppkt->options, tag, data, len );
122 /* Update our used-length field */
123 dhcppkt->len = ( offsetof ( struct dhcphdr, options ) +
124 dhcppkt->options.len );
130 * Fetch value of DHCP packet setting
132 * @v dhcppkt DHCP packet
133 * @v tag Setting tag number
134 * @v data Buffer to fill with setting data
135 * @v len Length of buffer
136 * @ret len Length of setting data, or negative error
138 int dhcppkt_fetch ( struct dhcp_packet *dhcppkt, unsigned int tag,
139 void *data, size_t len ) {
140 struct dhcp_packet_field *field;
142 /* If this is a special field, return it */
143 if ( ( field = find_dhcp_packet_field ( tag ) ) != NULL ) {
144 if ( len > field->len )
147 dhcp_packet_field ( dhcppkt->dhcphdr, field ), len );
151 /* Otherwise, use the generic options block */
152 return dhcpopt_fetch ( &dhcppkt->options, tag, data, len );
156 * Initialise prepopulated DHCP packet
158 * @v dhcppkt Uninitialised DHCP packet
159 * @v data Memory for DHCP packet data
160 * @v max_len Length of memory for DHCP packet data
162 * The memory content must already be filled with valid DHCP options.
163 * A zeroed block counts as a block of valid DHCP options.
165 void dhcppkt_init ( struct dhcp_packet *dhcppkt, void *data, size_t len ) {
166 dhcppkt->dhcphdr = data;
167 dhcppkt->max_len = len;
168 dhcpopt_init ( &dhcppkt->options, &dhcppkt->dhcphdr->options,
169 ( len - offsetof ( struct dhcphdr, options ) ) );
170 dhcppkt->len = ( offsetof ( struct dhcphdr, options ) +
171 dhcppkt->options.len );