Browse Source

Add (untested) code for parsing a received DHCP packet and constructing a

DHCP options block from the contents.
tags/v0.9.3
Michael Brown 18 years ago
parent
commit
12da7ea475
2 changed files with 322 additions and 3 deletions
  1. 150
    3
      src/include/gpxe/dhcp.h
  2. 172
    0
      src/net/udp/dhcp.c

+ 150
- 3
src/include/gpxe/dhcp.h View File

@@ -9,6 +9,87 @@
9 9
 
10 10
 #include <stdint.h>
11 11
 #include <gpxe/list.h>
12
+#include <gpxe/in.h>
13
+
14
+/**
15
+ * A DHCP packet
16
+ *
17
+ */
18
+struct dhcp_packet {
19
+	/** Operation
20
+	 *
21
+	 * This must be either @c BOOTP_REQUEST or @c BOOTP_REPLY.
22
+	 */
23
+	uint8_t op;
24
+	/** Hardware address type
25
+	 *
26
+	 * This is an ARPHRD_XXX constant.
27
+	 */
28
+	uint8_t htype;
29
+	/** Hardware address length */
30
+	uint8_t hlen;
31
+	/** Number of hops from server */
32
+	uint8_t hops;
33
+	/** Transaction ID */
34
+	uint32_t xid;
35
+	/** Seconds since start of acquisition */
36
+	uint16_t secs;
37
+	/** Flags */
38
+	uint16_t flags;
39
+	/** "Client" IP address
40
+	 *
41
+	 * This is filled in if the client already has an IP address
42
+	 * assigned and can respond to ARP requests.
43
+	 */
44
+	struct in_addr ciaddr;
45
+	/** "Your" IP address
46
+	 *
47
+	 * This is the IP address assigned by the server to the client.
48
+	 */
49
+	struct in_addr yiaddr;
50
+	/** "Server" IP address
51
+	 *
52
+	 * This is the IP address of the next server to be used in the
53
+	 * boot process.
54
+	 */
55
+	struct in_addr siaddr;
56
+	/** "Gateway" IP address
57
+	 *
58
+	 * This is the IP address of the DHCP relay agent, if any.
59
+	 */
60
+	struct in_addr giaddr;
61
+	/** Client hardware address */
62
+	uint8_t chaddr[16];
63
+	/** Server host name (null terminated)
64
+	 *
65
+	 * This field may be overridden and contain DHCP options
66
+	 */
67
+	uint8_t sname[64];
68
+	/** Boot file name (null terminated)
69
+	 *
70
+	 * This field may be overridden and contain DHCP options
71
+	 */
72
+	uint8_t file[128];
73
+	/** DHCP magic cookie
74
+	 *
75
+	 * Must have the value @c DHCP_MAGIC_COOKIE.
76
+	 */
77
+	uint32_t magic;
78
+	/** DHCP options
79
+	 *
80
+	 * Variable length; extends to the end of the packet.
81
+	 */
82
+	uint8_t options[0];
83
+};
84
+
85
+/** Opcode for a request from client to server */
86
+#define BOOTP_REQUEST 1
87
+
88
+/** Opcode for a reply from server to client */
89
+#define BOOTP_REPLY 2
90
+
91
+/** DHCP magic cookie */
92
+#define DHCP_MAGIC_COOKIE 0x63825363UL
12 93
 
13 94
 /** Construct a tag value for an encapsulated option
14 95
  *
@@ -22,7 +103,7 @@
22 103
 #define DHCP_ENCAPSULATOR( encap_opt ) ( (encap_opt) >> 8 )
23 104
 /** Extract encapsulated option tag from encapsulated tag value */
24 105
 #define DHCP_ENCAPSULATED( encap_opt ) ( (encap_opt) & 0xff )
25
-
106
+/** Option is encapsulated */
26 107
 #define DHCP_IS_ENCAP_OPT( opt ) DHCP_ENCAPSULATOR( opt )
27 108
 
28 109
 /**
@@ -30,13 +111,80 @@
30 111
  * @{
31 112
  */
32 113
 
114
+/** Padding
115
+ *
116
+ * This tag does not have a length field; it is always only a single
117
+ * byte in length.
118
+ */
33 119
 #define DHCP_PAD 0
34
-#define DHCP_END 255
35 120
 
121
+/** Option overloading
122
+ *
123
+ * The value of this option is the bitwise-OR of zero or more
124
+ * DHCP_OPTION_OVERLOAD_XXX constants.
125
+ */
126
+#define DHCP_OPTION_OVERLOAD 52
127
+
128
+/** The "file" field is overloaded to contain extra DHCP options */
129
+#define DHCP_OPTION_OVERLOAD_FILE 1
130
+
131
+/** The "sname" field is overloaded to contain extra DHCP options */
132
+#define DHCP_OPTION_OVERLOAD_SNAME 2
133
+
134
+/** TFTP server name
135
+ *
136
+ * This option replaces the fixed "sname" field, when that field is
137
+ * used to contain overloaded options.
138
+ */
139
+#define DHCP_TFTP_SERVER_NAME 66
140
+
141
+/** Bootfile name
142
+ *
143
+ * This option replaces the fixed "file" field, when that field is
144
+ * used to contain overloaded options.
145
+ */
146
+#define DHCP_BOOTFILE_NAME 67
147
+
148
+/** Etherboot-specific encapsulated options
149
+ *
150
+ * This encapsulated options field is used to contain all options
151
+ * specific to Etherboot (i.e. not assigned by IANA or other standards
152
+ * bodies).
153
+ */
36 154
 #define DHCP_EB_ENCAP 175
37 155
 
156
+/** Priority of this options block
157
+ *
158
+ * This is a signed 8-bit integer field indicating the priority of
159
+ * this block of options.  It can be used to specify the relative
160
+ * priority of multiple option blocks (e.g. options from non-volatile
161
+ * storage versus options from a DHCP server).
162
+ */
38 163
 #define DHCP_EB_PRIORITY DHCP_ENCAP_OPT ( DHCP_EB_ENCAP, 1 )
39 164
 
165
+/** "Your" IP address
166
+ *
167
+ * This option is used internally to contain the value of the "yiaddr"
168
+ * field, in order to provide a consistent approach to storing and
169
+ * processing options.  It should never be present in a DHCP packet.
170
+ */
171
+#define DHCP_EB_YIADDR DHCP_ENCAP_OPT ( DHCP_EB_ENCAP, 2 )
172
+
173
+/** "Server" IP address
174
+ *
175
+ * This option is used internally to contain the value of the "siaddr"
176
+ * field, in order to provide a consistent approach to storing and
177
+ * processing options.  It should never be present in a DHCP packet.
178
+ */
179
+#define DHCP_EB_SIADDR DHCP_ENCAP_OPT ( DHCP_EB_ENCAP, 3 )
180
+
181
+/** End of options
182
+ *
183
+ * This tag does not have a length field; it is always only a single
184
+ * byte in length.
185
+ */
186
+#define DHCP_END 255
187
+
40 188
 /** @} */
41 189
 
42 190
 /**
@@ -118,7 +266,6 @@ extern struct dhcp_option *
118 266
 set_dhcp_option ( struct dhcp_option_block *options, unsigned int tag,
119 267
 		  const void *data, size_t len );
120 268
 
121
-
122 269
 /**
123 270
  * Find DHCP numerical option, and return its value
124 271
  *

+ 172
- 0
src/net/udp/dhcp.c View File

@@ -0,0 +1,172 @@
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 <string.h>
20
+#include <assert.h>
21
+#include <byteswap.h>
22
+#include <gpxe/netdevice.h>
23
+#include <gpxe/dhcp.h>
24
+
25
+/** @file
26
+ *
27
+ * Dynamic Host Configuration Protocol
28
+ *
29
+ */
30
+
31
+/**
32
+ * Calculate used length of a field containing DHCP options
33
+ *
34
+ * @v data		Field containing DHCP options
35
+ * @v max_len		Field length
36
+ * @ret len		Used length (excluding the @c DHCP_END tag)
37
+ */
38
+static size_t dhcp_field_len ( const void *data, size_t max_len ) {
39
+	struct dhcp_option_block options;
40
+	struct dhcp_option *end;
41
+
42
+	options.data = ( ( void * ) data );
43
+	options.len = max_len;
44
+	end = find_dhcp_option ( &options, DHCP_END );
45
+	return ( end ? ( ( ( void * ) end ) - data ) : 0 );
46
+}
47
+
48
+/**
49
+ * Merge field containing DHCP options or string into DHCP options block
50
+ *
51
+ * @v options		DHCP option block
52
+ * @v data		Field containing DHCP options
53
+ * @v max_len		Field length
54
+ * @v tag		DHCP option tag, or 0
55
+ *
56
+ * If @c tag is non-zero, the field will be treated as a
57
+ * NUL-terminated string representing the value of the specified DHCP
58
+ * option.  If @c tag is zero, the field will be treated as a block of
59
+ * DHCP options, and simply appended to the existing options in the
60
+ * option block.
61
+ *
62
+ * The caller must ensure that there is enough space in the options
63
+ * block to perform the merge.
64
+ */
65
+static void merge_dhcp_field ( struct dhcp_option_block *options,
66
+			       const void *data, size_t max_len,
67
+			       unsigned int tag ) {
68
+	size_t len;
69
+	void *dest;
70
+	struct dhcp_option *end;
71
+
72
+	if ( tag ) {
73
+		set_dhcp_option ( options, tag, data, strlen ( data ) );
74
+	} else {
75
+		len = dhcp_field_len ( data, max_len );
76
+		dest = ( options->data + options->len - 1 );
77
+		memcpy ( dest, data, len );
78
+		options->len += len;
79
+		end = ( dest + len );
80
+		end->tag = DHCP_END;
81
+	}
82
+}
83
+
84
+/**
85
+ * Parse DHCP packet and construct DHCP options block
86
+ *
87
+ * @v data		DHCP packet
88
+ * @v len		Length of DHCP packet
89
+ * @ret options		DHCP options block, or NULL
90
+ *
91
+ * Parses a received DHCP packet and canonicalises its contents into a
92
+ * single DHCP options block.  The "file" and "sname" fields are
93
+ * converted into the corresponding DHCP options (@c
94
+ * DHCP_BOOTFILE_NAME and @c DHCP_TFTP_SERVER_NAME respectively).  If
95
+ * these fields are used for option overloading, their options are
96
+ * merged in to the options block.  The values of the "yiaddr" and
97
+ * "siaddr" fields will be stored within the options block as the
98
+ * options @c DHCP_EB_YIADDR and @c DHCP_EB_SIADDR.
99
+ * 
100
+ * Note that this call allocates new memory for the constructed DHCP
101
+ * options block; it is the responsibility of the caller to eventually
102
+ * free this memory.
103
+ */
104
+struct dhcp_option_block * dhcp_parse ( const void *data, size_t len ) {
105
+	const struct dhcp_packet *dhcppkt = data;
106
+	struct dhcp_option_block *options;
107
+	size_t options_len;
108
+	unsigned int overloading;
109
+
110
+	/* Calculate size of resulting concatenated option block:
111
+	 *
112
+	 *   The "options" field : length of the field minus the DHCP_END tag.
113
+	 *
114
+	 *   The "file" field : maximum length of the field minus the
115
+	 *   NUL terminator, plus a 2-byte DHCP header or, if used for
116
+	 *   option overloading, the length of the field minus the
117
+	 *   DHCP_END tag.
118
+	 *
119
+	 *   The "sname" field : as for the "file" field.
120
+	 *
121
+	 *   15 bytes for an encapsulated options field to contain the
122
+	 *   value of the "yiaddr" and "siaddr" fields
123
+	 *
124
+	 *   1 byte for a final terminating DHCP_END tag.
125
+	 */
126
+	options_len = ( ( len - offsetof ( typeof ( *dhcppkt ), options ) ) - 1
127
+			+ ( sizeof ( dhcppkt->file ) + 1 )
128
+			+ ( sizeof ( dhcppkt->sname ) + 1 )
129
+			+ 15 /* yiaddr and siaddr */
130
+			+ 1 /* DHCP_END tag */ );
131
+	
132
+	/* Allocate empty options block of required size */
133
+	options = alloc_dhcp_options ( options_len );
134
+	if ( ! options ) {
135
+		DBG ( "DHCP could not allocate %d-byte option block\n",
136
+		      options_len );
137
+		return NULL;
138
+	}
139
+	
140
+	/* Merge in "options" field, if this is a DHCP packet */
141
+	if ( dhcppkt->magic == htonl ( DHCP_MAGIC_COOKIE ) ) {
142
+		merge_dhcp_field ( options, dhcppkt->options,
143
+				   ( len -
144
+				     offsetof ( typeof (*dhcppkt), options ) ),
145
+				   0 /* Always contains options */ );
146
+	}
147
+
148
+	/* Identify overloaded fields */
149
+	overloading = find_dhcp_num_option ( options, DHCP_OPTION_OVERLOAD );
150
+	
151
+	/* Merge in "file" and "sname" fields */
152
+	merge_dhcp_field ( options, dhcppkt->file, sizeof ( dhcppkt->file ),
153
+			   ( ( overloading & DHCP_OPTION_OVERLOAD_FILE ) ?
154
+			     DHCP_BOOTFILE_NAME : 0 ) );
155
+	merge_dhcp_field ( options, dhcppkt->sname, sizeof ( dhcppkt->sname ),
156
+			   ( ( overloading & DHCP_OPTION_OVERLOAD_SNAME ) ?
157
+			     DHCP_TFTP_SERVER_NAME : 0 ) );
158
+
159
+	/* Set options for "yiaddr" and "siaddr", if present */
160
+	if ( dhcppkt->yiaddr.s_addr ) {
161
+		set_dhcp_option ( options, DHCP_EB_YIADDR,
162
+				  &dhcppkt->yiaddr, sizeof (dhcppkt->yiaddr) );
163
+	}
164
+	if ( dhcppkt->siaddr.s_addr ) {
165
+		set_dhcp_option ( options, DHCP_EB_SIADDR,
166
+				  &dhcppkt->siaddr, sizeof (dhcppkt->siaddr) );
167
+	}
168
+	
169
+	assert ( options->len <= options->max_len );
170
+
171
+	return options;
172
+}

Loading…
Cancel
Save