Browse Source

First stab at DHCP option handling in a way that will allow us to have

multiple option sources (e.g. multiple DHCP replies, non-volatile
storage etc.)
tags/v0.9.3
Michael Brown 18 years ago
parent
commit
0bcaa8a21f
2 changed files with 304 additions and 0 deletions
  1. 87
    0
      src/include/gpxe/dhcp.h
  2. 217
    0
      src/net/dhcpopts.c

+ 87
- 0
src/include/gpxe/dhcp.h View File

@@ -0,0 +1,87 @@
1
+#ifndef _GPXE_DHCP_H
2
+#define _GPXE_DHCP_H
3
+
4
+/** @file
5
+ *
6
+ * Dynamic Host Configuration Protocol
7
+ *
8
+ */
9
+
10
+#include <stdint.h>
11
+#include <gpxe/list.h>
12
+
13
+/** Construct a tag value for an encapsulated option
14
+ *
15
+ * This tag value can be passed to Etherboot functions when searching
16
+ * for DHCP options in order to search for a tag within an
17
+ * encapsulated options block.
18
+ */
19
+#define DHCP_ENCAP_OPT( encapsulator, encapsulated ) \
20
+	( ( (encapsulator) << 8 ) | (encapsulated) )
21
+/** Extract encapsulating option block tag from encapsulated tag value */
22
+#define DHCP_ENCAPSULATOR( encap_opt ) ( (encap_opt) >> 8 )
23
+/** Extract encapsulated option tag from encapsulated tag value */
24
+#define DHCP_ENCAPSULATED( encap_opt ) ( (encap_opt) & 0xff )
25
+
26
+/**
27
+ * @defgroup dhcpopts DHCP option tags
28
+ * @{
29
+ */
30
+
31
+#define DHCP_PAD 0
32
+#define DHCP_END 255
33
+
34
+#define DHCP_EB_ENCAP 175
35
+
36
+#define DHCP_EB_PRIORITY DHCP_ENCAP_OPT ( DHCP_EB_ENCAP, 1 )
37
+
38
+/** @} */
39
+
40
+/**
41
+ * A DHCP option
42
+ *
43
+ * DHCP options consist of a mandatory tag, a length field that is
44
+ * mandatory for all options except @c DHCP_PAD and @c DHCP_END, and a
45
+ * payload.  
46
+ */
47
+struct dhcp_option {
48
+	/** Tag
49
+	 *
50
+	 * Must be a @c DHCP_XXX value.
51
+	 */
52
+	uint8_t tag;
53
+	/** Length
54
+	 *
55
+	 * This is the length of the data field (i.e. excluding the
56
+	 * tag and length fields).  For the two tags @c DHCP_PAD and
57
+	 * @c DHCP_END, the length field is implicitly zero and is
58
+	 * also missing, i.e. these DHCP options are only a single
59
+	 * byte in length.
60
+	 */
61
+	uint8_t len;
62
+	/** Option data
63
+	 *
64
+	 * Interpretation of the content is entirely dependent upon
65
+	 * the tag.  For fields containing a multi-byte integer, the
66
+	 * field is defined to be in network-endian order (unless you
67
+	 * are Intel and feel like violating the spec for fun).
68
+	 */
69
+	union {
70
+		uint8_t byte;
71
+		uint16_t word;
72
+		uint32_t dword;
73
+		uint8_t bytes[0];
74
+	} data;
75
+} __attribute__ (( packed ));
76
+
77
+/** A DHCP options block */
78
+struct dhcp_option_block {
79
+	/** List of option blocks */
80
+	struct list_head list;
81
+	/** Option block raw data */
82
+	void *data;
83
+	/** Option block length */
84
+	size_t len;
85
+};
86
+
87
+#endif /* _GPXE_DHCP_H */

+ 217
- 0
src/net/dhcpopts.c View File

@@ -0,0 +1,217 @@
1
+/*
2
+ * Copyright (C) 2006 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., 675 Mass Ave, Cambridge, MA 02139, USA.
17
+ */
18
+
19
+#include <stdint.h>
20
+#include <byteswap.h>
21
+#include <errno.h>
22
+#include <malloc.h>
23
+#include <assert.h>
24
+#include <gpxe/list.h>
25
+#include <gpxe/dhcp.h>
26
+
27
+/** @file
28
+ *
29
+ * DHCP options
30
+ *
31
+ */
32
+
33
+/** List of registered DHCP option blocks */
34
+static LIST_HEAD ( option_blocks );
35
+
36
+/**
37
+ * Obtain value of a numerical DHCP option
38
+ *
39
+ * @v option		DHCP option, or NULL
40
+ * @v value		Unsigned long for storing the result
41
+ * @ret rc		Return status code
42
+ *
43
+ * Parses the numerical value from a DHCP option, if present.  It is
44
+ * permitted to call dhcp_num_option() with @c option set to NULL; in
45
+ * this case the result value will not be modified and an error will
46
+ * be returned.
47
+ *
48
+ * The caller does not specify the size of the DHCP option data; this
49
+ * is implied by the length field stored within the DHCP option
50
+ * itself.
51
+ */
52
+int dhcp_num_option ( struct dhcp_option *option, unsigned long *value ) {
53
+	uint8_t *data;
54
+	unsigned long tmp = 0;
55
+
56
+	if ( ! option )
57
+		return -EINVAL;
58
+
59
+	/* This is actually smaller code than using htons() etc., and
60
+	 * will also cope well with malformed options (such as
61
+	 * zero-length options).
62
+	 */
63
+	for ( data = option->data.bytes ;
64
+	      data < ( option->data.bytes + option->len ) ; data++ )
65
+		tmp = ( ( tmp << 8 ) | *data );
66
+	*value = tmp;
67
+	return 0;
68
+}
69
+
70
+/**
71
+ * Calculate length of a DHCP option
72
+ *
73
+ * @v option		DHCP option
74
+ * @ret len		Length (including tag and length field)
75
+ */
76
+static inline unsigned int dhcp_option_len ( struct dhcp_option *option ) {
77
+	if ( ( option->tag == DHCP_END ) || ( option->tag == DHCP_PAD ) ) {
78
+		return 1;
79
+	} else {
80
+		return ( option->len + 2 );
81
+	}
82
+}
83
+
84
+/**
85
+ * Find DHCP option within block of raw data
86
+ *
87
+ * @v tag		DHCP option tag to search for
88
+ * @v data		Data block
89
+ * @v len		Length of data block
90
+ * @ret option		DHCP option, or NULL if not found
91
+ *
92
+ * Searches for the DHCP option matching the specified tag within the
93
+ * block of data.  Encapsulated options may be searched for by using
94
+ * DHCP_ENCAP_OPT() to construct the tag value.
95
+ *
96
+ * This routine is designed to be paranoid.  It does not assume that
97
+ * the option data is well-formatted, and so must guard against flaws
98
+ * such as options missing a @c DHCP_END terminator, or options whose
99
+ * length would take them beyond the end of the data block.
100
+ *
101
+ * Searching for @c DHCP_PAD or @c DHCP_END tags, or using either @c
102
+ * DHCP_PAD or @c DHCP_END as the encapsulator when constructing the
103
+ * tag via DHCP_ENCAP_OPT() will produce undefined behaviour.
104
+ */
105
+static struct dhcp_option * find_dhcp_option_raw ( unsigned int tag,
106
+						   void *data, size_t len ) {
107
+	struct dhcp_option *option = data;
108
+	ssize_t remaining = len;
109
+	unsigned int option_len;
110
+
111
+	assert ( tag != DHCP_PAD );
112
+	assert ( tag != DHCP_END );
113
+	assert ( DHCP_ENCAPSULATOR ( tag ) != DHCP_END );
114
+
115
+	while ( remaining ) {
116
+		/* Check for explicit end marker */
117
+		if ( option->tag == DHCP_END )
118
+			break;
119
+		/* Calculate length of this option.  Abort processing
120
+		 * if the length is malformed (i.e. takes us beyond
121
+		 * the end of the data block).
122
+		 */
123
+		option_len = dhcp_option_len ( option );
124
+		remaining -= option_len;
125
+		if ( remaining < 0 )
126
+			break;
127
+		/* Check for matching tag */
128
+		if ( option->tag == tag )
129
+			return option;
130
+		/* Check for start of matching encapsulation block */
131
+		if ( DHCP_ENCAPSULATOR ( tag ) &&
132
+		     ( option->tag == DHCP_ENCAPSULATOR ( tag ) ) ) {
133
+			/* Search within encapsulated option block */
134
+			return find_dhcp_option_raw ( DHCP_ENCAPSULATED( tag ),
135
+						      &option->data,
136
+						      option->len );
137
+		}
138
+		option = ( ( ( void * ) option ) + option_len );
139
+	}
140
+	return NULL;
141
+}
142
+
143
+/**
144
+ * Find DHCP option within all registered DHCP options blocks
145
+ *
146
+ * @v tag		DHCP option tag to search for
147
+ * @ret option		DHCP option, or NULL if not found
148
+ *
149
+ * Searches within all registered DHCP option blocks for the specified
150
+ * tag.  Encapsulated options may be searched for by using
151
+ * DHCP_ENCAP_OPT() to construct the tag value.
152
+ */
153
+struct dhcp_option * find_dhcp_option ( unsigned int tag ) {
154
+	struct dhcp_option_block *options;
155
+	struct dhcp_option *option;
156
+
157
+	list_for_each_entry ( options, &option_blocks, list ) {
158
+		if ( ( option = find_dhcp_option_raw ( tag, options->data,
159
+						       options->len ) ) )
160
+			return option;
161
+	}
162
+	return NULL;
163
+}
164
+
165
+/**
166
+ * Register DHCP option block
167
+ *
168
+ * @v options		DHCP option block
169
+ *
170
+ * Register a block of DHCP options
171
+ */
172
+void register_dhcp_options ( struct dhcp_option_block *options ) {
173
+	list_add ( &options->list, &option_blocks );
174
+}
175
+
176
+/**
177
+ * Unregister DHCP option block
178
+ *
179
+ * @v options		DHCP option block
180
+ */
181
+void unregister_dhcp_options ( struct dhcp_option_block *options ) {
182
+	list_del ( &options->list );
183
+}
184
+
185
+/**
186
+ * Allocate space for a block of DHCP options
187
+ *
188
+ * @v len		Maximum length of option block
189
+ * @ret options		Option block, or NULL
190
+ *
191
+ * Creates a new DHCP option block and populates it with an empty
192
+ * options list.  This call does not register the options block.
193
+ */
194
+struct dhcp_option_block * alloc_dhcp_options ( size_t len ) {
195
+	struct dhcp_option_block *options;
196
+	struct dhcp_option *option;
197
+
198
+	options = malloc ( sizeof ( *options ) + len );
199
+	if ( options ) {
200
+		options->data = ( ( void * ) options + sizeof ( *options ) );
201
+		options->len = len;
202
+		if ( len ) {
203
+			option = options->data;
204
+			option->tag = DHCP_END;
205
+		}
206
+	}
207
+	return options;
208
+}
209
+
210
+/**
211
+ * Free DHCP options block
212
+ *
213
+ * @v options		Option block
214
+ */
215
+void free_dhcp_options ( struct dhcp_option_block *options ) {
216
+	free ( options );
217
+}

Loading…
Cancel
Save