iPXE
dhcppkt.c
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2008 Michael Brown <mbrown@fensystems.co.uk>.
3  *
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.
8  *
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.
13  *
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., 51 Franklin Street, Fifth Floor, Boston, MA
17  * 02110-1301, USA.
18  *
19  * You can also choose to distribute this program under the terms of
20  * the Unmodified Binary Distribution Licence (as given in the file
21  * COPYING.UBDL), provided that you have satisfied its requirements.
22  */
23 
24 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
25 
26 #include <stdint.h>
27 #include <stdlib.h>
28 #include <stdio.h>
29 #include <errno.h>
30 #include <string.h>
31 #include <ipxe/netdevice.h>
32 #include <ipxe/dhcp.h>
33 #include <ipxe/dhcpopts.h>
34 #include <ipxe/dhcppkt.h>
35 
36 /** @file
37  *
38  * DHCP packets
39  *
40  */
41 
42 /****************************************************************************
43  *
44  * DHCP packet raw interface
45  *
46  */
47 
48 /**
49  * Calculate used length of an IPv4 field within a DHCP packet
50  *
51  * @v data Field data
52  * @v len Length of field
53  * @ret used Used length of field
54  */
55 static size_t used_len_ipv4 ( const void *data, size_t len __unused ) {
56  const struct in_addr *in = data;
57 
58  return ( in->s_addr ? sizeof ( *in ) : 0 );
59 }
60 
61 /**
62  * Calculate used length of a string field within a DHCP packet
63  *
64  * @v data Field data
65  * @v len Length of field
66  * @ret used Used length of field
67  */
68 static size_t used_len_string ( const void *data, size_t len ) {
69  return strnlen ( data, len );
70 }
71 
72 /** A dedicated field within a DHCP packet */
74  /** Settings tag number */
75  unsigned int tag;
76  /** Offset within DHCP packet */
78  /** Length of field */
80  /** Calculate used length of field
81  *
82  * @v data Field data
83  * @v len Length of field
84  * @ret used Used length of field
85  */
86  size_t ( * used_len ) ( const void *data, size_t len );
87 };
88 
89 /** Declare a dedicated field within a DHCP packet
90  *
91  * @v _tag Settings tag number
92  * @v _field Field name
93  * @v _used_len Function to calculate used length of field
94  */
95 #define DHCP_PACKET_FIELD( _tag, _field, _used_len ) { \
96  .tag = (_tag), \
97  .offset = offsetof ( struct dhcphdr, _field ), \
98  .len = sizeof ( ( ( struct dhcphdr * ) 0 )->_field ), \
99  .used_len = _used_len, \
100  }
101 
102 /** Dedicated fields within a DHCP packet */
108 };
109 
110 /**
111  * Get address of a DHCP packet field
112  *
113  * @v dhcphdr DHCP packet header
114  * @v field DHCP packet field
115  * @ret data Packet field data
116  */
117 static inline void * dhcp_packet_field ( struct dhcphdr *dhcphdr,
118  struct dhcp_packet_field *field ) {
119  return ( ( ( void * ) dhcphdr ) + field->offset );
120 }
121 
122 /**
123  * Find DHCP packet field corresponding to settings tag number
124  *
125  * @v tag Settings tag number
126  * @ret field DHCP packet field, or NULL
127  */
128 static struct dhcp_packet_field *
129 find_dhcp_packet_field ( unsigned int tag ) {
130  struct dhcp_packet_field *field;
131  unsigned int i;
132 
133  for ( i = 0 ; i < ( sizeof ( dhcp_packet_fields ) /
134  sizeof ( dhcp_packet_fields[0] ) ) ; i++ ) {
135  field = &dhcp_packet_fields[i];
136  if ( field->tag == tag )
137  return field;
138  }
139  return NULL;
140 }
141 
142 /**
143  * Check applicability of DHCP setting
144  *
145  * @v dhcppkt DHCP packet
146  * @v tag Setting tag number
147  * @ret applies Setting applies within this settings block
148  */
149 static int dhcppkt_applies ( struct dhcp_packet *dhcppkt __unused,
150  unsigned int tag ) {
151 
152  return dhcpopt_applies ( tag );
153 }
154 
155 /**
156  * Store value of DHCP packet setting
157  *
158  * @v dhcppkt DHCP packet
159  * @v tag Setting tag number
160  * @v data Setting data, or NULL to clear setting
161  * @v len Length of setting data
162  * @ret rc Return status code
163  */
164 int dhcppkt_store ( struct dhcp_packet *dhcppkt, unsigned int tag,
165  const void *data, size_t len ) {
166  struct dhcp_packet_field *field;
167  void *field_data;
168 
169  /* If this is a special field, fill it in */
170  if ( ( field = find_dhcp_packet_field ( tag ) ) != NULL ) {
171  if ( len > field->len )
172  return -ENOSPC;
173  field_data = dhcp_packet_field ( dhcppkt->dhcphdr, field );
174  memset ( field_data, 0, field->len );
175  memcpy ( dhcp_packet_field ( dhcppkt->dhcphdr, field ),
176  data, len );
177  /* Erase any equivalent option from the options block */
178  dhcpopt_store ( &dhcppkt->options, tag, NULL, 0 );
179  return 0;
180  }
181 
182  /* Otherwise, use the generic options block */
183  return dhcpopt_store ( &dhcppkt->options, tag, data, len );
184 }
185 
186 /**
187  * Fetch value of DHCP packet setting
188  *
189  * @v dhcppkt DHCP packet
190  * @v tag Setting tag number
191  * @v data Buffer to fill with setting data
192  * @v len Length of buffer
193  * @ret len Length of setting data, or negative error
194  */
195 int dhcppkt_fetch ( struct dhcp_packet *dhcppkt, unsigned int tag,
196  void *data, size_t len ) {
197  struct dhcp_packet_field *field;
198  void *field_data;
199  size_t field_len = 0;
200 
201  /* Identify special field, if any */
202  if ( ( field = find_dhcp_packet_field ( tag ) ) != NULL ) {
203  field_data = dhcp_packet_field ( dhcppkt->dhcphdr, field );
204  field_len = field->used_len ( field_data, field->len );
205  }
206 
207  /* Return special field, if it exists and is populated */
208  if ( field_len ) {
209  if ( len > field_len )
210  len = field_len;
211  memcpy ( data, field_data, len );
212  return field_len;
213  }
214 
215  /* Otherwise, use the generic options block */
216  return dhcpopt_fetch ( &dhcppkt->options, tag, data, len );
217 }
218 
219 /****************************************************************************
220  *
221  * DHCP packet settings interface
222  *
223  */
224 
225 /**
226  * Check applicability of DHCP setting
227  *
228  * @v settings Settings block
229  * @v setting Setting
230  * @ret applies Setting applies within this settings block
231  */
233  const struct setting *setting ) {
234  struct dhcp_packet *dhcppkt =
236 
237  return ( ( setting->scope == NULL ) &&
238  dhcppkt_applies ( dhcppkt, setting->tag ) );
239 }
240 
241 /**
242  * Store value of DHCP setting
243  *
244  * @v settings Settings block
245  * @v setting Setting to store
246  * @v data Setting data, or NULL to clear setting
247  * @v len Length of setting data
248  * @ret rc Return status code
249  */
251  const struct setting *setting,
252  const void *data, size_t len ) {
253  struct dhcp_packet *dhcppkt =
255 
256  return dhcppkt_store ( dhcppkt, setting->tag, data, len );
257 }
258 
259 /**
260  * Fetch value of DHCP setting
261  *
262  * @v settings Settings block, or NULL to search all blocks
263  * @v setting Setting to fetch
264  * @v data Buffer to fill with setting data
265  * @v len Length of buffer
266  * @ret len Length of setting data, or negative error
267  */
269  struct setting *setting,
270  void *data, size_t len ) {
271  struct dhcp_packet *dhcppkt =
273 
274  return dhcppkt_fetch ( dhcppkt, setting->tag, data, len );
275 }
276 
277 /** DHCP settings operations */
280  .store = dhcppkt_settings_store,
281  .fetch = dhcppkt_settings_fetch,
282 };
283 
284 /****************************************************************************
285  *
286  * Constructor
287  *
288  */
289 
290 /**
291  * Initialise DHCP packet
292  *
293  * @v dhcppkt DHCP packet structure to fill in
294  * @v data DHCP packet raw data
295  * @v max_len Length of raw data buffer
296  *
297  * Initialise a DHCP packet structure from a data buffer containing a
298  * DHCP packet.
299  */
300 void dhcppkt_init ( struct dhcp_packet *dhcppkt, struct dhcphdr *data,
301  size_t len ) {
302  ref_init ( &dhcppkt->refcnt, NULL );
303  dhcppkt->dhcphdr = data;
304  dhcpopt_init ( &dhcppkt->options, &dhcppkt->dhcphdr->options,
305  ( len - offsetof ( struct dhcphdr, options ) ),
308  &dhcppkt->refcnt, NULL );
309 }
static int dhcppkt_applies(struct dhcp_packet *dhcppkt __unused, unsigned int tag)
Check applicability of DHCP setting.
Definition: dhcppkt.c:149
static struct dhcp_packet_field * find_dhcp_packet_field(unsigned int tag)
Find DHCP packet field corresponding to settings tag number.
Definition: dhcppkt.c:129
unsigned short uint16_t
Definition: stdint.h:11
A DHCP packet.
Definition: dhcppkt.h:20
Dynamic Host Configuration Protocol.
struct refcnt refcnt
Reference counter.
Definition: dhcppkt.h:22
__be32 in[4]
Definition: CIB_PRM.h:35
void dhcpopt_init(struct dhcp_options *options, void *data, size_t alloc_len, int(*realloc)(struct dhcp_options *options, size_t len))
Initialise prepopulated block of DHCP options.
Definition: dhcpopts.c:451
#define ref_init(refcnt, free)
Initialise a reference counter.
Definition: refcnt.h:64
Error codes.
__SIZE_TYPE__ size_t
Definition: stdint.h:6
static int dhcppkt_settings_applies(struct settings *settings, const struct setting *setting)
Check applicability of DHCP setting.
Definition: dhcppkt.c:232
static struct dhcp_packet_field dhcp_packet_fields[]
Dedicated fields within a DHCP packet.
Definition: dhcppkt.c:103
static int dhcppkt_settings_fetch(struct settings *settings, struct setting *setting, void *data, size_t len)
Fetch value of DHCP setting.
Definition: dhcppkt.c:268
uint8_t options[0]
DHCP options.
Definition: dhcp.h:681
#define offsetof(type, field)
Get offset of a field within a structure.
Definition: stddef.h:24
FILE_LICENCE(GPL2_OR_LATER_OR_UBDL)
static void settings_init(struct settings *settings, struct settings_operations *op, struct refcnt *refcnt, const struct settings_scope *default_scope)
Initialise a settings block.
Definition: settings.h:496
static size_t used_len_ipv4(const void *data, size_t len __unused)
Calculate used length of an IPv4 field within a DHCP packet.
Definition: dhcppkt.c:55
#define DHCP_PACKET_FIELD(_tag, _field, _used_len)
Declare a dedicated field within a DHCP packet.
Definition: dhcppkt.c:95
struct dhcp_options options
DHCP options.
Definition: dhcppkt.h:26
uint64_t tag
Setting tag, if applicable.
Definition: settings.h:43
static size_t used_len_string(const void *data, size_t len)
Calculate used length of a string field within a DHCP packet.
Definition: dhcppkt.c:68
void * memcpy(void *dest, const void *src, size_t len) __nonnull
static void * dhcp_packet_field(struct dhcphdr *dhcphdr, struct dhcp_packet_field *field)
Get address of a DHCP packet field.
Definition: dhcppkt.c:117
#define container_of(ptr, type, field)
Get containing structure.
Definition: stddef.h:35
static int options
Definition: 3c515.c:286
IP address structure.
Definition: in.h:39
int dhcppkt_fetch(struct dhcp_packet *dhcppkt, unsigned int tag, void *data, size_t len)
Fetch value of DHCP packet setting.
Definition: dhcppkt.c:195
int dhcppkt_store(struct dhcp_packet *dhcppkt, unsigned int tag, const void *data, size_t len)
Store value of DHCP packet setting.
Definition: dhcppkt.c:164
Settings block operations.
Definition: settings.h:85
A settings block.
Definition: settings.h:132
size_t strnlen(const char *src, size_t max)
Get length of string.
Definition: string.c:255
DHCP options.
A setting.
Definition: settings.h:23
struct settings settings
Settings interface.
Definition: dhcppkt.h:28
int dhcpopt_no_realloc(struct dhcp_options *options, size_t len)
Refuse to reallocate DHCP option block.
Definition: dhcpopts.c:184
A DHCP header.
Definition: dhcp.h:613
Network device management.
#define __unused
Declare a variable or data structure as unused.
Definition: compiler.h:573
void dhcppkt_init(struct dhcp_packet *dhcppkt, struct dhcphdr *data, size_t len)
Initialise DHCP packet.
Definition: dhcppkt.c:300
#define ENOSPC
No space left on device.
Definition: errno.h:549
struct dhcphdr * dhcphdr
The DHCP packet contents.
Definition: dhcppkt.h:24
static struct settings_operations dhcppkt_settings_operations
DHCP settings operations.
Definition: dhcppkt.c:278
#define DHCP_EB_SIADDR
"Server" IP address
Definition: dhcp.h:379
#define DHCP_EB_YIADDR
"Your" IP address
Definition: dhcp.h:371
uint32_t len
Length.
Definition: ena.h:14
uint8_t data[48]
Additional event data.
Definition: ena.h:22
int dhcpopt_store(struct dhcp_options *options, unsigned int tag, const void *data, size_t len)
Store value of DHCP option setting.
Definition: dhcpopts.c:374
#define DHCP_BOOTFILE_NAME
Bootfile name.
Definition: dhcp.h:265
DHCP packets.
int(* applies)(struct settings *settings, const struct setting *setting)
Check applicability of setting.
Definition: settings.h:98
size_t(* used_len)(const void *data, size_t len)
Calculate used length of field.
Definition: dhcppkt.c:86
int dhcpopt_fetch(struct dhcp_options *options, unsigned int tag, void *data, size_t len)
Fetch value of DHCP option setting.
Definition: dhcpopts.c:393
uint16_t offset
Offset within DHCP packet.
Definition: dhcppkt.c:77
static int dhcppkt_settings_store(struct settings *settings, const struct setting *setting, const void *data, size_t len)
Store value of DHCP setting.
Definition: dhcppkt.c:250
A dedicated field within a DHCP packet.
Definition: dhcppkt.c:73
unsigned int tag
Settings tag number.
Definition: dhcppkt.c:75
#define DHCP_TFTP_SERVER_NAME
TFTP server name.
Definition: dhcp.h:258
const struct settings_scope * scope
Setting scope (or NULL)
Definition: settings.h:49
uint64_t tag
Identity tag.
Definition: edd.h:30
uint16_t len
Length of field.
Definition: dhcppkt.c:79
#define NULL
NULL pointer (VOID *)
Definition: Base.h:321
int dhcpopt_applies(unsigned int tag)
Check applicability of DHCP option setting.
Definition: dhcpopts.c:359
String functions.
void * memset(void *dest, int character, size_t len) __nonnull