aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenys Vlasenko <vda.linux@googlemail.com>2010-03-21 06:15:28 +0100
committerDenys Vlasenko <vda.linux@googlemail.com>2010-03-21 06:15:28 +0100
commitc7dc79e71ddbc1498736a2bbf65a3da179557f83 (patch)
treeab470e39f11f432b2099d3ebbe779d224bc1efba
parent2e7aa928360eb9b1c90fa2356734cee794b66516 (diff)
downloadbusybox-w32-c7dc79e71ddbc1498736a2bbf65a3da179557f83.tar.gz
busybox-w32-c7dc79e71ddbc1498736a2bbf65a3da179557f83.tar.bz2
busybox-w32-c7dc79e71ddbc1498736a2bbf65a3da179557f83.zip
udhcpd: untangle incredibly messy handling of DHCPREQUEST
Also fixes attacks possible via DHCPDECLINE / DHCPRELEASE function old new delta udhcpd_main 1846 1949 +103 send_renew 105 142 +37 send_NAK 61 - -61 send_ACK 180 - -180 ------------------------------------------------------------------------------ (add/remove: 0/2 grow/shrink: 2/0 up/down: 140/-241) Total: -101 bytes Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r--networking/udhcp/clientpacket.c52
-rw-r--r--networking/udhcp/common.h75
-rw-r--r--networking/udhcp/dhcpc.h1
-rw-r--r--networking/udhcp/dhcpd.c264
-rw-r--r--networking/udhcp/files.c14
-rw-r--r--networking/udhcp/options.c2
-rw-r--r--networking/udhcp/options.h30
-rw-r--r--networking/udhcp/packet.c11
-rw-r--r--networking/udhcp/script.c2
9 files changed, 267 insertions, 184 deletions
diff --git a/networking/udhcp/clientpacket.c b/networking/udhcp/clientpacket.c
index a255d6e84..a0be42885 100644
--- a/networking/udhcp/clientpacket.c
+++ b/networking/udhcp/clientpacket.c
@@ -109,24 +109,6 @@ static int raw_bcast_from_client_config_ifindex(struct dhcp_packet *packet)
109} 109}
110 110
111 111
112#if ENABLE_FEATURE_UDHCPC_ARPING
113/* Broadcast a DHCP decline message */
114int FAST_FUNC send_decline(uint32_t xid, uint32_t server, uint32_t requested)
115{
116 struct dhcp_packet packet;
117
118 init_packet(&packet, DHCPDECLINE);
119 packet.xid = xid;
120 add_simple_option(packet.options, DHCP_REQUESTED_IP, requested);
121 add_simple_option(packet.options, DHCP_SERVER_ID, server);
122
123 bb_info_msg("Sending decline...");
124
125 return raw_bcast_from_client_config_ifindex(&packet);
126}
127#endif
128
129
130/* Broadcast a DHCP discover packet to the network, with an optionally requested IP */ 112/* Broadcast a DHCP discover packet to the network, with an optionally requested IP */
131int FAST_FUNC send_discover(uint32_t xid, uint32_t requested) 113int FAST_FUNC send_discover(uint32_t xid, uint32_t requested)
132{ 114{
@@ -136,11 +118,9 @@ int FAST_FUNC send_discover(uint32_t xid, uint32_t requested)
136 packet.xid = xid; 118 packet.xid = xid;
137 if (requested) 119 if (requested)
138 add_simple_option(packet.options, DHCP_REQUESTED_IP, requested); 120 add_simple_option(packet.options, DHCP_REQUESTED_IP, requested);
139
140 /* Explicitly saying that we want RFC-compliant packets helps 121 /* Explicitly saying that we want RFC-compliant packets helps
141 * some buggy DHCP servers to NOT send bigger packets */ 122 * some buggy DHCP servers to NOT send bigger packets */
142 add_simple_option(packet.options, DHCP_MAX_SIZE, htons(576)); 123 add_simple_option(packet.options, DHCP_MAX_SIZE, htons(576));
143
144 add_param_req_option(&packet); 124 add_param_req_option(&packet);
145 125
146 bb_info_msg("Sending discover..."); 126 bb_info_msg("Sending discover...");
@@ -159,7 +139,6 @@ int FAST_FUNC send_select(uint32_t xid, uint32_t server, uint32_t requested)
159 139
160 init_packet(&packet, DHCPREQUEST); 140 init_packet(&packet, DHCPREQUEST);
161 packet.xid = xid; 141 packet.xid = xid;
162
163 add_simple_option(packet.options, DHCP_REQUESTED_IP, requested); 142 add_simple_option(packet.options, DHCP_REQUESTED_IP, requested);
164 add_simple_option(packet.options, DHCP_SERVER_ID, server); 143 add_simple_option(packet.options, DHCP_SERVER_ID, server);
165 add_param_req_option(&packet); 144 add_param_req_option(&packet);
@@ -177,17 +156,46 @@ int FAST_FUNC send_renew(uint32_t xid, uint32_t server, uint32_t ciaddr)
177 156
178 init_packet(&packet, DHCPREQUEST); 157 init_packet(&packet, DHCPREQUEST);
179 packet.xid = xid; 158 packet.xid = xid;
159 /* RFC 2131:
160 * "3.2 Client-server interaction - reusing a previously
161 * allocated network address"...
162 * The client broadcasts a DHCPREQUEST message on its local subnet.
163 * The message includes the client's network address in the
164 * REQUESTED_IP option. As the client has not received its
165 * network address, it MUST NOT fill in the 'ciaddr' field."
166 *
167 * FIXME: we seem to not follow this, we do set ciaddr.
168 */
180 packet.ciaddr = ciaddr; 169 packet.ciaddr = ciaddr;
181 170 add_simple_option(packet.options, DHCP_REQUESTED_IP, ciaddr);
171 if (server)
172 add_simple_option(packet.options, DHCP_SERVER_ID, server);
182 add_param_req_option(&packet); 173 add_param_req_option(&packet);
174
183 bb_info_msg("Sending renew..."); 175 bb_info_msg("Sending renew...");
184 if (server) 176 if (server)
185 return udhcp_send_kernel_packet(&packet, 177 return udhcp_send_kernel_packet(&packet,
186 ciaddr, CLIENT_PORT, 178 ciaddr, CLIENT_PORT,
187 server, SERVER_PORT); 179 server, SERVER_PORT);
180 return raw_bcast_from_client_config_ifindex(&packet);
181}
182
183
184#if ENABLE_FEATURE_UDHCPC_ARPING
185/* Broadcast a DHCP decline message */
186int FAST_FUNC send_decline(uint32_t xid, uint32_t server, uint32_t requested)
187{
188 struct dhcp_packet packet;
189
190 init_packet(&packet, DHCPDECLINE);
191 packet.xid = xid;
192 add_simple_option(packet.options, DHCP_REQUESTED_IP, requested);
193 add_simple_option(packet.options, DHCP_SERVER_ID, server);
188 194
195 bb_info_msg("Sending decline...");
189 return raw_bcast_from_client_config_ifindex(&packet); 196 return raw_bcast_from_client_config_ifindex(&packet);
190} 197}
198#endif
191 199
192 200
193/* Unicast a DHCP release message */ 201/* Unicast a DHCP release message */
diff --git a/networking/udhcp/common.h b/networking/udhcp/common.h
index d7c874e5b..cf3e0cd39 100644
--- a/networking/udhcp/common.h
+++ b/networking/udhcp/common.h
@@ -21,11 +21,17 @@ extern const uint8_t MAC_BCAST_ADDR[6]; /* six all-ones */
21 21
22/*** packet.h ***/ 22/*** packet.h ***/
23 23
24#define DHCP_OPTIONS_BUFSIZE 308 24/* DHCP protocol. See RFC 2131 */
25#define DHCP_MAGIC 0x63825363
26
27#define DHCP_OPTIONS_BUFSIZE 308
28
29#define BOOTREQUEST 1
30#define BOOTREPLY 2
25 31
26//TODO: rename ciaddr/yiaddr/chaddr 32//TODO: rename ciaddr/yiaddr/chaddr
27struct dhcp_packet { 33struct dhcp_packet {
28 uint8_t op; /* 1 = BOOTREQUEST, 2 = BOOTREPLY */ 34 uint8_t op; /* BOOTREQUEST or BOOTREPLY */
29 uint8_t htype; /* hardware address type. 1 = 10mb ethernet */ 35 uint8_t htype; /* hardware address type. 1 = 10mb ethernet */
30 uint8_t hlen; /* hardware address length */ 36 uint8_t hlen; /* hardware address length */
31 uint8_t hops; /* used by relay agents only */ 37 uint8_t hops; /* used by relay agents only */
@@ -61,6 +67,71 @@ struct BUG_bad_sizeof_struct_ip_udp_dhcp_packet {
61 [(sizeof(struct ip_udp_dhcp_packet) != 576 + CONFIG_UDHCPC_SLACK_FOR_BUGGY_SERVERS) ? -1 : 1]; 67 [(sizeof(struct ip_udp_dhcp_packet) != 576 + CONFIG_UDHCPC_SLACK_FOR_BUGGY_SERVERS) ? -1 : 1];
62}; 68};
63 69
70// RFC 2131 Table 5: Fields and options used by DHCP clients
71//
72// Field DHCPDISCOVER DHCPREQUEST DHCPDECLINE,
73// DHCPINFORM DHCPRELEASE
74// ----- ------------ ----------- -----------
75// 'op' BOOTREQUEST BOOTREQUEST BOOTREQUEST
76// 'hops' 0 0 0
77// 'xid' selected by client 'xid' from server selected by
78// DHCPOFFER message client
79// 'secs' 0 or seconds since 0 or seconds since 0
80// DHCP process started DHCP process started
81// 'flags' Set 'BROADCAST' Set 'BROADCAST' 0
82// flag if client flag if client
83// requires broadcast requires broadcast
84// reply reply
85// 'ciaddr' 0 (DHCPDISCOVER) 0 or client's 0 (DHCPDECLINE)
86// client's network address client's network
87// network address (BOUND/RENEW/REBIND) address
88// (DHCPINFORM) (DHCPRELEASE)
89// 'yiaddr' 0 0 0
90// 'siaddr' 0 0 0
91// 'giaddr' 0 0 0
92// 'chaddr' client's hardware client's hardware client's hardware
93// address address address
94// 'sname' options, if options, if (unused)
95// indicated in indicated in
96// 'sname/file' 'sname/file'
97// option; otherwise option; otherwise
98// unused unused
99// 'file' options, if options, if (unused)
100// indicated in indicated in
101// 'sname/file' 'sname/file'
102// option; otherwise option; otherwise
103// unused unused
104// 'options' options options (unused)
105//
106// Option DHCPDISCOVER DHCPREQUEST DHCPDECLINE,
107// DHCPINFORM DHCPRELEASE
108// ------ ------------ ----------- -----------
109// Requested IP address MAY MUST (in MUST
110// (DISCOVER) SELECTING or (DHCPDECLINE),
111// MUST NOT INIT-REBOOT) MUST NOT
112// (INFORM) MUST NOT (in (DHCPRELEASE)
113// BOUND or
114// RENEWING)
115// IP address lease time MAY MAY MUST NOT
116// (DISCOVER)
117// MUST NOT
118// (INFORM)
119// Use 'file'/'sname' fields MAY MAY MAY
120// Client identifier MAY MAY MAY
121// Vendor class identifier MAY MAY MUST NOT
122// Server identifier MUST NOT MUST (after MUST
123// SELECTING)
124// MUST NOT (after
125// INIT-REBOOT,
126// BOUND, RENEWING
127// or REBINDING)
128// Parameter request list MAY MAY MUST NOT
129// Maximum message size MAY MAY MUST NOT
130// Message SHOULD NOT SHOULD NOT SHOULD
131// Site-specific MAY MAY MUST NOT
132// All others MAY MAY MUST NOT
133
134
64uint16_t udhcp_checksum(void *addr, int count) FAST_FUNC; 135uint16_t udhcp_checksum(void *addr, int count) FAST_FUNC;
65 136
66void udhcp_init_header(struct dhcp_packet *packet, char type) FAST_FUNC; 137void udhcp_init_header(struct dhcp_packet *packet, char type) FAST_FUNC;
diff --git a/networking/udhcp/dhcpc.h b/networking/udhcp/dhcpc.h
index dfab2368c..c9827b6e7 100644
--- a/networking/udhcp/dhcpc.h
+++ b/networking/udhcp/dhcpc.h
@@ -40,7 +40,6 @@ int send_select(uint32_t xid, uint32_t server, uint32_t requested) FAST_FUNC;
40int send_decline(uint32_t xid, uint32_t server, uint32_t requested) FAST_FUNC; 40int send_decline(uint32_t xid, uint32_t server, uint32_t requested) FAST_FUNC;
41#endif 41#endif
42int send_renew(uint32_t xid, uint32_t server, uint32_t ciaddr) FAST_FUNC; 42int send_renew(uint32_t xid, uint32_t server, uint32_t ciaddr) FAST_FUNC;
43int send_renew(uint32_t xid, uint32_t server, uint32_t ciaddr) FAST_FUNC;
44int send_release(uint32_t server, uint32_t ciaddr) FAST_FUNC; 43int send_release(uint32_t server, uint32_t ciaddr) FAST_FUNC;
45 44
46int udhcp_recv_raw_packet(struct dhcp_packet *dhcp_pkt, int fd) FAST_FUNC; 45int udhcp_recv_raw_packet(struct dhcp_packet *dhcp_pkt, int fd) FAST_FUNC;
diff --git a/networking/udhcp/dhcpd.c b/networking/udhcp/dhcpd.c
index f594bad66..a529e1b58 100644
--- a/networking/udhcp/dhcpd.c
+++ b/networking/udhcp/dhcpd.c
@@ -28,18 +28,8 @@
28#include "options.h" 28#include "options.h"
29 29
30 30
31/* send a packet to gateway_nip using the kernel ip stack */ 31/* Send a packet to a specific mac address and ip address by creating our own ip packet */
32static int send_packet_to_relay(struct dhcp_packet *dhcp_pkt) 32static void send_packet_to_client(struct dhcp_packet *dhcp_pkt, int force_broadcast)
33{
34 log1("Forwarding packet to relay");
35
36 return udhcp_send_kernel_packet(dhcp_pkt,
37 server_config.server_nip, SERVER_PORT,
38 dhcp_pkt->gateway_nip, SERVER_PORT);
39}
40
41/* send a packet to a specific mac address and ip address by creating our own ip packet */
42static int send_packet_to_client(struct dhcp_packet *dhcp_pkt, int force_broadcast)
43{ 33{
44 const uint8_t *chaddr; 34 const uint8_t *chaddr;
45 uint32_t ciaddr; 35 uint32_t ciaddr;
@@ -69,25 +59,36 @@ static int send_packet_to_client(struct dhcp_packet *dhcp_pkt, int force_broadca
69 chaddr = dhcp_pkt->chaddr; 59 chaddr = dhcp_pkt->chaddr;
70 } 60 }
71 61
72 return udhcp_send_raw_packet(dhcp_pkt, 62 udhcp_send_raw_packet(dhcp_pkt,
73 /*src*/ server_config.server_nip, SERVER_PORT, 63 /*src*/ server_config.server_nip, SERVER_PORT,
74 /*dst*/ ciaddr, CLIENT_PORT, chaddr, 64 /*dst*/ ciaddr, CLIENT_PORT, chaddr,
75 server_config.ifindex); 65 server_config.ifindex);
76} 66}
77 67
78/* Send the dhcp packet. 68/* Send a packet to gateway_nip using the kernel ip stack */
79 * If force broadcast is set, the packet will be broadcast. 69static void send_packet_to_relay(struct dhcp_packet *dhcp_pkt)
80 */ 70{
81static int send_packet(struct dhcp_packet *dhcp_pkt, int force_broadcast) 71 log1("Forwarding packet to relay");
72
73 udhcp_send_kernel_packet(dhcp_pkt,
74 server_config.server_nip, SERVER_PORT,
75 dhcp_pkt->gateway_nip, SERVER_PORT);
76}
77
78static void send_packet(struct dhcp_packet *dhcp_pkt, int force_broadcast)
82{ 79{
83 if (dhcp_pkt->gateway_nip) 80 if (dhcp_pkt->gateway_nip)
84 return send_packet_to_relay(dhcp_pkt); 81 send_packet_to_relay(dhcp_pkt);
85 return send_packet_to_client(dhcp_pkt, force_broadcast); 82 else
83 send_packet_to_client(dhcp_pkt, force_broadcast);
86} 84}
87 85
88static void init_packet(struct dhcp_packet *packet, struct dhcp_packet *oldpacket, char type) 86static void init_packet(struct dhcp_packet *packet, struct dhcp_packet *oldpacket, char type)
89{ 87{
88 /* Sets op, htype, hlen, cookie fields
89 * and adds DHCP_MESSAGE_TYPE option */
90 udhcp_init_header(packet, type); 90 udhcp_init_header(packet, type);
91
91 packet->xid = oldpacket->xid; 92 packet->xid = oldpacket->xid;
92 memcpy(packet->chaddr, oldpacket->chaddr, sizeof(oldpacket->chaddr)); 93 memcpy(packet->chaddr, oldpacket->chaddr, sizeof(oldpacket->chaddr));
93 packet->flags = oldpacket->flags; 94 packet->flags = oldpacket->flags;
@@ -132,26 +133,32 @@ static uint32_t select_lease_time(struct dhcp_packet *packet)
132 return lease_time_sec; 133 return lease_time_sec;
133} 134}
134 135
135/* send a DHCP OFFER to a DHCP DISCOVER */ 136/* We got a DHCP DISCOVER. Send an OFFER. */
136static int send_offer(struct dhcp_packet *oldpacket, uint32_t static_lease_nip, struct dyn_lease *lease) 137static void send_offer(struct dhcp_packet *oldpacket, uint32_t static_lease_nip, struct dyn_lease *lease)
137{ 138{
138 struct dhcp_packet packet; 139 struct dhcp_packet packet;
139 uint32_t lease_time_sec = server_config.max_lease_sec; 140 uint32_t lease_time_sec;
140 const char *p_host_name;
141 struct in_addr addr; 141 struct in_addr addr;
142 142
143 init_packet(&packet, oldpacket, DHCPOFFER); 143 init_packet(&packet, oldpacket, DHCPOFFER);
144 144
145 /* ADDME: if static, short circuit */ 145 /* If it is a static lease, use its IP */
146 packet.yiaddr = static_lease_nip;
147 /* Else: */
146 if (!static_lease_nip) { 148 if (!static_lease_nip) {
149 /* We have no static lease for client's chaddr */
147 uint32_t req_nip; 150 uint32_t req_nip;
148 uint8_t *req_ip_opt; 151 uint8_t *req_ip_opt;
152 const char *p_host_name;
149 153
150 /* The client is in our lease/offered table */
151 if (lease) { 154 if (lease) {
155 /* We have a dynamic lease for client's chaddr.
156 * Reuse its IP (even if lease is expired).
157 * Note that we ignore requested IP in this case.
158 */
152 packet.yiaddr = lease->lease_nip; 159 packet.yiaddr = lease->lease_nip;
153 } 160 }
154 /* Or the client has requested an IP */ 161 /* Or: if client has requested an IP */
155 else if ((req_ip_opt = get_option(oldpacket, DHCP_REQUESTED_IP)) != NULL 162 else if ((req_ip_opt = get_option(oldpacket, DHCP_REQUESTED_IP)) != NULL
156 /* (read IP) */ 163 /* (read IP) */
157 && (move_from_unaligned32(req_nip, req_ip_opt), 1) 164 && (move_from_unaligned32(req_nip, req_ip_opt), 1)
@@ -165,50 +172,49 @@ static int send_offer(struct dhcp_packet *oldpacket, uint32_t static_lease_nip,
165 ) { 172 ) {
166 packet.yiaddr = req_nip; 173 packet.yiaddr = req_nip;
167 } 174 }
168 /* Otherwise, find a free IP */
169 else { 175 else {
176 /* Otherwise, find a free IP */
170 packet.yiaddr = find_free_or_expired_nip(oldpacket->chaddr); 177 packet.yiaddr = find_free_or_expired_nip(oldpacket->chaddr);
171 } 178 }
172 179
173 if (!packet.yiaddr) { 180 if (!packet.yiaddr) {
174 bb_error_msg("no IP addresses to give - OFFER abandoned"); 181 bb_error_msg("no free IP addresses. OFFER abandoned");
175 return -1; 182 return;
176 } 183 }
184 /* Reserve the IP for a short time hoping to get DHCPREQUEST soon */
177 p_host_name = (const char*) get_option(oldpacket, DHCP_HOST_NAME); 185 p_host_name = (const char*) get_option(oldpacket, DHCP_HOST_NAME);
178 if (add_lease(packet.chaddr, packet.yiaddr, 186 lease = add_lease(packet.chaddr, packet.yiaddr,
179 server_config.offer_time, 187 server_config.offer_time,
180 p_host_name, 188 p_host_name,
181 p_host_name ? (unsigned char)p_host_name[OPT_LEN - OPT_DATA] : 0 189 p_host_name ? (unsigned char)p_host_name[OPT_LEN - OPT_DATA] : 0
182 ) == 0 190 );
183 ) { 191 if (!lease) {
184 bb_error_msg("lease pool is full - OFFER abandoned"); 192 bb_error_msg("no free IP addresses. OFFER abandoned");
185 return -1; 193 return;
186 } 194 }
187 lease_time_sec = select_lease_time(oldpacket);
188 } else {
189 /* It is a static lease... use it */
190 packet.yiaddr = static_lease_nip;
191 } 195 }
192 196
197 lease_time_sec = select_lease_time(oldpacket);
193 add_simple_option(packet.options, DHCP_LEASE_TIME, htonl(lease_time_sec)); 198 add_simple_option(packet.options, DHCP_LEASE_TIME, htonl(lease_time_sec));
194 add_server_options(&packet); 199 add_server_options(&packet);
195 200
196 addr.s_addr = packet.yiaddr; 201 addr.s_addr = packet.yiaddr;
197 bb_info_msg("Sending OFFER of %s", inet_ntoa(addr)); 202 bb_info_msg("Sending OFFER of %s", inet_ntoa(addr));
198 return send_packet(&packet, /*force_bcast:*/ 0); 203 /* send_packet emits error message itself if it detects failure */
204 send_packet(&packet, /*force_bcast:*/ 0);
199} 205}
200 206
201static int send_NAK(struct dhcp_packet *oldpacket) 207static void send_NAK(struct dhcp_packet *oldpacket)
202{ 208{
203 struct dhcp_packet packet; 209 struct dhcp_packet packet;
204 210
205 init_packet(&packet, oldpacket, DHCPNAK); 211 init_packet(&packet, oldpacket, DHCPNAK);
206 212
207 log1("Sending NAK"); 213 log1("Sending NAK");
208 return send_packet(&packet, /*force_bcast:*/ 1); 214 send_packet(&packet, /*force_bcast:*/ 1);
209} 215}
210 216
211static int send_ACK(struct dhcp_packet *oldpacket, uint32_t yiaddr) 217static void send_ACK(struct dhcp_packet *oldpacket, uint32_t yiaddr)
212{ 218{
213 struct dhcp_packet packet; 219 struct dhcp_packet packet;
214 uint32_t lease_time_sec; 220 uint32_t lease_time_sec;
@@ -219,15 +225,13 @@ static int send_ACK(struct dhcp_packet *oldpacket, uint32_t yiaddr)
219 packet.yiaddr = yiaddr; 225 packet.yiaddr = yiaddr;
220 226
221 lease_time_sec = select_lease_time(oldpacket); 227 lease_time_sec = select_lease_time(oldpacket);
222
223 add_simple_option(packet.options, DHCP_LEASE_TIME, htonl(lease_time_sec)); 228 add_simple_option(packet.options, DHCP_LEASE_TIME, htonl(lease_time_sec));
229
224 add_server_options(&packet); 230 add_server_options(&packet);
225 231
226 addr.s_addr = packet.yiaddr; 232 addr.s_addr = yiaddr;
227 bb_info_msg("Sending ACK to %s", inet_ntoa(addr)); 233 bb_info_msg("Sending ACK to %s", inet_ntoa(addr));
228 234 send_packet(&packet, /*force_bcast:*/ 0);
229 if (send_packet(&packet, /*force_bcast:*/ 0) < 0)
230 return -1;
231 235
232 p_host_name = (const char*) get_option(oldpacket, DHCP_HOST_NAME); 236 p_host_name = (const char*) get_option(oldpacket, DHCP_HOST_NAME);
233 add_lease(packet.chaddr, packet.yiaddr, 237 add_lease(packet.chaddr, packet.yiaddr,
@@ -239,18 +243,21 @@ static int send_ACK(struct dhcp_packet *oldpacket, uint32_t yiaddr)
239 /* rewrite the file with leases at every new acceptance */ 243 /* rewrite the file with leases at every new acceptance */
240 write_leases(); 244 write_leases();
241 } 245 }
242
243 return 0;
244} 246}
245 247
246static int send_inform(struct dhcp_packet *oldpacket) 248static void send_inform(struct dhcp_packet *oldpacket)
247{ 249{
248 struct dhcp_packet packet; 250 struct dhcp_packet packet;
249 251
252 /* "The server responds to a DHCPINFORM message by sending a DHCPACK
253 * message directly to the address given in the 'ciaddr' field
254 * of the DHCPINFORM message. The server MUST NOT send a lease
255 * expiration time to the client and SHOULD NOT fill in 'yiaddr'."
256 */
250 init_packet(&packet, oldpacket, DHCPACK); 257 init_packet(&packet, oldpacket, DHCPACK);
251 add_server_options(&packet); 258 add_server_options(&packet);
252 259
253 return send_packet(&packet, /*force_bcast:*/ 0); 260 send_packet(&packet, /*force_bcast:*/ 0);
254} 261}
255 262
256 263
@@ -352,6 +359,9 @@ int udhcpd_main(int argc UNUSED_PARAM, char **argv)
352 while (1) { /* loop until universe collapses */ 359 while (1) { /* loop until universe collapses */
353 int bytes; 360 int bytes;
354 struct timeval tv; 361 struct timeval tv;
362 uint8_t *server_id_opt;
363 uint8_t *requested_opt;
364 uint32_t requested_nip = requested_nip; /* for compiler */
355 365
356 if (server_socket < 0) { 366 if (server_socket < 0) {
357 server_socket = udhcp_listen_socket(/*INADDR_ANY,*/ SERVER_PORT, 367 server_socket = udhcp_listen_socket(/*INADDR_ANY,*/ SERVER_PORT,
@@ -404,124 +414,126 @@ int udhcpd_main(int argc UNUSED_PARAM, char **argv)
404 } 414 }
405 continue; 415 continue;
406 } 416 }
407
408 if (packet.hlen != 6) { 417 if (packet.hlen != 6) {
409 bb_error_msg("MAC length != 6, ignoring packet"); 418 bb_error_msg("MAC length != 6, ignoring packet");
410 continue; 419 continue;
411 } 420 }
412 421 if (packet.op != BOOTREQUEST) {
422 bb_error_msg("not a REQUEST, ignoring packet");
423 continue;
424 }
413 state = get_option(&packet, DHCP_MESSAGE_TYPE); 425 state = get_option(&packet, DHCP_MESSAGE_TYPE);
414 if (state == NULL) { 426 if (state == NULL || state[0] < DHCP_MINTYPE || state[0] > DHCP_MAXTYPE) {
415 bb_error_msg("no message type option, ignoring packet"); 427 bb_error_msg("no or bad message type option, ignoring packet");
416 continue; 428 continue;
417 } 429 }
418 430
419 /* Look for a static lease */ 431 /* Look for a static/dynamic lease */
420 static_lease_nip = get_static_nip_by_mac(server_config.static_leases, &packet.chaddr); 432 static_lease_nip = get_static_nip_by_mac(server_config.static_leases, &packet.chaddr);
421 if (static_lease_nip) { 433 if (static_lease_nip) {
422 bb_info_msg("Found static lease: %x", static_lease_nip); 434 bb_info_msg("Found static lease: %x", static_lease_nip);
423
424 memcpy(&fake_lease.lease_mac, &packet.chaddr, 6); 435 memcpy(&fake_lease.lease_mac, &packet.chaddr, 6);
425 fake_lease.lease_nip = static_lease_nip; 436 fake_lease.lease_nip = static_lease_nip;
426 fake_lease.expires = 0; 437 fake_lease.expires = 0;
427
428 lease = &fake_lease; 438 lease = &fake_lease;
429 } else { 439 } else {
430 lease = find_lease_by_mac(packet.chaddr); 440 lease = find_lease_by_mac(packet.chaddr);
431 } 441 }
432 442
443 /* Get REQUESTED_IP and SERVER_ID if present */
444 server_id_opt = get_option(&packet, DHCP_SERVER_ID);
445 if (server_id_opt) {
446 uint32_t server_id_net;
447 move_from_unaligned32(server_id_net, server_id_opt);
448 if (server_id_net != server_config.server_nip) {
449 /* client talks to somebody else */
450 log1("server ID doesn't match, ignoring");
451 continue;
452 }
453 }
454 requested_opt = get_option(&packet, DHCP_REQUESTED_IP);
455 if (requested_opt) {
456 move_from_unaligned32(requested_nip, requested_opt);
457 }
458
433 switch (state[0]) { 459 switch (state[0]) {
460
434 case DHCPDISCOVER: 461 case DHCPDISCOVER:
435 log1("Received DISCOVER"); 462 log1("Received DISCOVER");
436 463
437 if (send_offer(&packet, static_lease_nip, lease) < 0) { 464 send_offer(&packet, static_lease_nip, lease);
438 bb_error_msg("send OFFER failed");
439 }
440 break; 465 break;
441 case DHCPREQUEST: {
442 uint8_t *server_id_opt, *requested_opt;
443 uint32_t server_id_net = server_id_net; /* for compiler */
444 uint32_t requested_nip = requested_nip; /* for compiler */
445 466
467 case DHCPREQUEST:
446 log1("Received REQUEST"); 468 log1("Received REQUEST");
447 469
448 requested_opt = get_option(&packet, DHCP_REQUESTED_IP); 470 /* RFC 2131: "The REQUESTED_IP option MUST be set
449 server_id_opt = get_option(&packet, DHCP_SERVER_ID); 471 * to the value of 'yiaddr' in the DHCPOFFER message
450 if (requested_opt) 472 * from the server." */
451 move_from_unaligned32(requested_nip, requested_opt); 473 if (!requested_opt) {
452 if (server_id_opt) 474 log1("no requested IP, ignoring");
453 move_from_unaligned32(server_id_net, server_id_opt); 475 break;
454 476 }
455 if (lease) { 477 if (lease && requested_nip == lease->lease_nip) {
456 if (server_id_opt) { 478 /* client requests IP which matches the lease.
457 /* SELECTING State */ 479 * ACK it, and bump lease expiration time. */
458 if (server_id_net == server_config.server_nip 480 send_ACK(&packet, lease->lease_nip);
459 && requested_opt 481 break;
460 && requested_nip == lease->lease_nip 482 }
461 ) { 483 if (server_id_opt) {
462 send_ACK(&packet, lease->lease_nip); 484 /* client was talking specifically to us.
463 } 485 * "No, we don't have this IP for you". */
464 } else if (requested_opt) { 486 send_NAK(&packet);
465 /* INIT-REBOOT State */
466 if (lease->lease_nip == requested_nip)
467 send_ACK(&packet, lease->lease_nip);
468 else
469 send_NAK(&packet);
470 } else if (lease->lease_nip == packet.ciaddr) {
471 /* RENEWING or REBINDING State */
472 send_ACK(&packet, lease->lease_nip);
473 } else { /* don't know what to do!!!! */
474 send_NAK(&packet);
475 }
476
477 /* what to do if we have no record of the client */
478 } else if (server_id_opt) {
479 /* SELECTING State */
480
481 } else if (requested_opt) {
482 /* INIT-REBOOT State */
483 lease = find_lease_by_nip(requested_nip);
484 if (lease) {
485 if (is_expired_lease(lease)) {
486 /* probably best if we drop this lease */
487 memset(lease->lease_mac, 0, sizeof(lease->lease_mac));
488 } else {
489 /* make some contention for this address */
490 send_NAK(&packet);
491 }
492 } else {
493 uint32_t r = ntohl(requested_nip);
494 if (r < server_config.start_ip
495 || r > server_config.end_ip
496 ) {
497 send_NAK(&packet);
498 }
499 /* else remain silent */
500 }
501
502 } else {
503 /* RENEWING or REBINDING State */
504 } 487 }
505 break; 488 break;
506 } 489
507 case DHCPDECLINE: 490 case DHCPDECLINE:
491 /* RFC 2131:
492 * "If the server receives a DHCPDECLINE message,
493 * the client has discovered through some other means
494 * that the suggested network address is already
495 * in use. The server MUST mark the network address
496 * as not available and SHOULD notify the local
497 * sysadmin of a possible configuration problem."
498 *
499 * SERVER_ID must be present,
500 * REQUESTED_IP must be present,
501 * chaddr must be filled in,
502 * ciaddr must be 0 (we do not check this)
503 */
508 log1("Received DECLINE"); 504 log1("Received DECLINE");
509 if (lease) { 505 if (server_id_opt
506 && requested_opt
507 && lease /* chaddr matches this lease */
508 && requested_nip == lease->lease_nip
509 ) {
510 memset(lease->lease_mac, 0, sizeof(lease->lease_mac)); 510 memset(lease->lease_mac, 0, sizeof(lease->lease_mac));
511 lease->expires = time(NULL) + server_config.decline_time; 511 lease->expires = time(NULL) + server_config.decline_time;
512 } 512 }
513 break; 513 break;
514
514 case DHCPRELEASE: 515 case DHCPRELEASE:
516 /* "Upon receipt of a DHCPRELEASE message, the server
517 * marks the network address as not allocated."
518 *
519 * SERVER_ID must be present,
520 * REQUESTED_IP must not be present (we do not check this),
521 * chaddr must be filled in,
522 * ciaddr must be filled in
523 */
515 log1("Received RELEASE"); 524 log1("Received RELEASE");
516 if (lease) 525 if (server_id_opt
526 && lease /* chaddr matches this lease */
527 && packet.ciaddr == lease->lease_nip
528 ) {
517 lease->expires = time(NULL); 529 lease->expires = time(NULL);
530 }
518 break; 531 break;
532
519 case DHCPINFORM: 533 case DHCPINFORM:
520 log1("Received INFORM"); 534 log1("Received INFORM");
521 send_inform(&packet); 535 send_inform(&packet);
522 break; 536 break;
523 default:
524 bb_info_msg("Unsupported DHCP message (%02x) - ignoring", state[0]);
525 } 537 }
526 } 538 }
527 ret0: 539 ret0:
diff --git a/networking/udhcp/files.c b/networking/udhcp/files.c
index 40cfe9fd2..62f4a388f 100644
--- a/networking/udhcp/files.c
+++ b/networking/udhcp/files.c
@@ -99,7 +99,7 @@ static void attach_option(struct option_set **opt_list,
99 log2("Attaching option %02x to list", option->code); 99 log2("Attaching option %02x to list", option->code);
100 100
101#if ENABLE_FEATURE_UDHCP_RFC3397 101#if ENABLE_FEATURE_UDHCP_RFC3397
102 if ((option->flags & TYPE_MASK) == OPTION_STR1035) 102 if ((option->flags & OPTION_TYPE_MASK) == OPTION_STR1035)
103 /* reuse buffer and length for RFC1035-formatted string */ 103 /* reuse buffer and length for RFC1035-formatted string */
104 buffer = (char *)dname_enc(NULL, 0, buffer, &length); 104 buffer = (char *)dname_enc(NULL, 0, buffer, &length);
105#endif 105#endif
@@ -118,7 +118,7 @@ static void attach_option(struct option_set **opt_list,
118 new->next = *curr; 118 new->next = *curr;
119 *curr = new; 119 *curr = new;
120#if ENABLE_FEATURE_UDHCP_RFC3397 120#if ENABLE_FEATURE_UDHCP_RFC3397
121 if ((option->flags & TYPE_MASK) == OPTION_STR1035 && buffer != NULL) 121 if ((option->flags & OPTION_TYPE_MASK) == OPTION_STR1035 && buffer != NULL)
122 free(buffer); 122 free(buffer);
123#endif 123#endif
124 return; 124 return;
@@ -128,7 +128,7 @@ static void attach_option(struct option_set **opt_list,
128 log1("Attaching option %02x to existing member of list", option->code); 128 log1("Attaching option %02x to existing member of list", option->code);
129 if (option->flags & OPTION_LIST) { 129 if (option->flags & OPTION_LIST) {
130#if ENABLE_FEATURE_UDHCP_RFC3397 130#if ENABLE_FEATURE_UDHCP_RFC3397
131 if ((option->flags & TYPE_MASK) == OPTION_STR1035) 131 if ((option->flags & OPTION_TYPE_MASK) == OPTION_STR1035)
132 /* reuse buffer and length for RFC1035-formatted string */ 132 /* reuse buffer and length for RFC1035-formatted string */
133 buffer = (char *)dname_enc(existing->data + 2, 133 buffer = (char *)dname_enc(existing->data + 2,
134 existing->data[OPT_LEN], buffer, &length); 134 existing->data[OPT_LEN], buffer, &length);
@@ -136,7 +136,7 @@ static void attach_option(struct option_set **opt_list,
136 if (existing->data[OPT_LEN] + length <= 255) { 136 if (existing->data[OPT_LEN] + length <= 255) {
137 existing->data = xrealloc(existing->data, 137 existing->data = xrealloc(existing->data,
138 existing->data[OPT_LEN] + length + 3); 138 existing->data[OPT_LEN] + length + 3);
139 if ((option->flags & TYPE_MASK) == OPTION_STRING) { 139 if ((option->flags & OPTION_TYPE_MASK) == OPTION_STRING) {
140 /* ' ' can bring us to 256 - bad */ 140 /* ' ' can bring us to 256 - bad */
141 if (existing->data[OPT_LEN] + length >= 255) 141 if (existing->data[OPT_LEN] + length >= 255)
142 return; 142 return;
@@ -148,7 +148,7 @@ static void attach_option(struct option_set **opt_list,
148 existing->data[OPT_LEN] += length; 148 existing->data[OPT_LEN] += length;
149 } /* else, ignore the data, we could put this in a second option in the future */ 149 } /* else, ignore the data, we could put this in a second option in the future */
150#if ENABLE_FEATURE_UDHCP_RFC3397 150#if ENABLE_FEATURE_UDHCP_RFC3397
151 if ((option->flags & TYPE_MASK) == OPTION_STR1035 && buffer != NULL) 151 if ((option->flags & OPTION_TYPE_MASK) == OPTION_STR1035 && buffer != NULL)
152 free(buffer); 152 free(buffer);
153#endif 153#endif
154 } /* else, ignore the new data */ 154 } /* else, ignore the new data */
@@ -182,10 +182,10 @@ static int FAST_FUNC read_opt(const char *const_line, void *arg)
182 do { 182 do {
183 val = strtok(NULL, ", \t"); 183 val = strtok(NULL, ", \t");
184 if (!val) break; 184 if (!val) break;
185 length = dhcp_option_lengths[option->flags & TYPE_MASK]; 185 length = dhcp_option_lengths[option->flags & OPTION_TYPE_MASK];
186 retval = 0; 186 retval = 0;
187 opt = buffer; /* new meaning for variable opt */ 187 opt = buffer; /* new meaning for variable opt */
188 switch (option->flags & TYPE_MASK) { 188 switch (option->flags & OPTION_TYPE_MASK) {
189 case OPTION_IP: 189 case OPTION_IP:
190 retval = read_nip(val, buffer); 190 retval = read_nip(val, buffer);
191 break; 191 break;
diff --git a/networking/udhcp/options.c b/networking/udhcp/options.c
index 5bef985bc..29a264047 100644
--- a/networking/udhcp/options.c
+++ b/networking/udhcp/options.c
@@ -252,7 +252,7 @@ void FAST_FUNC add_simple_option(uint8_t *optionptr, uint8_t code, uint32_t data
252 uint8_t option[6], len; 252 uint8_t option[6], len;
253 253
254 option[OPT_CODE] = code; 254 option[OPT_CODE] = code;
255 len = dhcp_option_lengths[dh->flags & TYPE_MASK]; 255 len = dhcp_option_lengths[dh->flags & OPTION_TYPE_MASK];
256 option[OPT_LEN] = len; 256 option[OPT_LEN] = len;
257 if (BB_BIG_ENDIAN) 257 if (BB_BIG_ENDIAN)
258 data <<= 8 * (4 - len); 258 data <<= 8 * (4 - len);
diff --git a/networking/udhcp/options.h b/networking/udhcp/options.h
index 05090f12e..3ca4dc42d 100644
--- a/networking/udhcp/options.h
+++ b/networking/udhcp/options.h
@@ -5,7 +5,6 @@
5 5
6PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN 6PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN
7 7
8#define TYPE_MASK 0x0F
9 8
10enum { 9enum {
11 OPTION_IP = 1, 10 OPTION_IP = 1,
@@ -21,19 +20,13 @@ enum {
21 OPTION_U32, 20 OPTION_U32,
22 OPTION_S32, 21 OPTION_S32,
23 OPTION_STATIC_ROUTES, 22 OPTION_STATIC_ROUTES,
24};
25
26/* Client requests this option by default */
27#define OPTION_REQ 0x10
28/* There can be a list of 1 or more of these */
29#define OPTION_LIST 0x20
30 23
31/*****************************************************************/ 24 OPTION_TYPE_MASK = 0x0f,
32/* Do not modify below here unless you know what you are doing!! */ 25 /* Client requests this option by default */
33/*****************************************************************/ 26 OPTION_REQ = 0x10,
34 27 /* There can be a list of 1 or more of these */
35/* DHCP protocol. See RFC 2131 */ 28 OPTION_LIST = 0x20,
36#define DHCP_MAGIC 0x63825363 29};
37 30
38/* DHCP option codes (partial list). See RFC 2132 and 31/* DHCP option codes (partial list). See RFC 2132 and
39 * http://www.iana.org/assignments/bootp-dhcp-parameters/ 32 * http://www.iana.org/assignments/bootp-dhcp-parameters/
@@ -81,7 +74,6 @@ enum {
81//#define DHCP_WPAD 0xfc /* MSIE's Web Proxy Autodiscovery Protocol */ 74//#define DHCP_WPAD 0xfc /* MSIE's Web Proxy Autodiscovery Protocol */
82#define DHCP_END 0xff 75#define DHCP_END 0xff
83 76
84
85/* Offsets in option byte sequence */ 77/* Offsets in option byte sequence */
86#define OPT_CODE 0 78#define OPT_CODE 0
87#define OPT_LEN 1 79#define OPT_LEN 1
@@ -91,12 +83,7 @@ enum {
91#define FILE_FIELD 1 83#define FILE_FIELD 1
92#define SNAME_FIELD 2 84#define SNAME_FIELD 2
93 85
94#define BOOTREQUEST 1 86/* DHCP_MESSAGE_TYPE values */
95#define BOOTREPLY 2
96
97#define ETH_10MB 1
98#define ETH_10MB_LEN 6
99
100#define DHCPDISCOVER 1 /* client -> server */ 87#define DHCPDISCOVER 1 /* client -> server */
101#define DHCPOFFER 2 /* client <- server */ 88#define DHCPOFFER 2 /* client <- server */
102#define DHCPREQUEST 3 /* client -> server */ 89#define DHCPREQUEST 3 /* client -> server */
@@ -105,6 +92,9 @@ enum {
105#define DHCPNAK 6 /* client <- server */ 92#define DHCPNAK 6 /* client <- server */
106#define DHCPRELEASE 7 /* client -> server */ 93#define DHCPRELEASE 7 /* client -> server */
107#define DHCPINFORM 8 /* client -> server */ 94#define DHCPINFORM 8 /* client -> server */
95#define DHCP_MINTYPE DHCPDISCOVER
96#define DHCP_MAXTYPE DHCPINFORM
97
108 98
109struct dhcp_option { 99struct dhcp_option {
110 uint8_t flags; 100 uint8_t flags;
diff --git a/networking/udhcp/packet.c b/networking/udhcp/packet.c
index 90410cbc0..84d83098c 100644
--- a/networking/udhcp/packet.c
+++ b/networking/udhcp/packet.c
@@ -21,7 +21,7 @@
21 21
22void FAST_FUNC udhcp_init_header(struct dhcp_packet *packet, char type) 22void FAST_FUNC udhcp_init_header(struct dhcp_packet *packet, char type)
23{ 23{
24 memset(packet, 0, sizeof(struct dhcp_packet)); 24 memset(packet, 0, sizeof(*packet));
25 packet->op = BOOTREQUEST; /* if client to a server */ 25 packet->op = BOOTREQUEST; /* if client to a server */
26 switch (type) { 26 switch (type) {
27 case DHCPOFFER: 27 case DHCPOFFER:
@@ -29,10 +29,11 @@ void FAST_FUNC udhcp_init_header(struct dhcp_packet *packet, char type)
29 case DHCPNAK: 29 case DHCPNAK:
30 packet->op = BOOTREPLY; /* if server to client */ 30 packet->op = BOOTREPLY; /* if server to client */
31 } 31 }
32 packet->htype = ETH_10MB; 32 packet->htype = 1; /* ethernet */
33 packet->hlen = ETH_10MB_LEN; 33 packet->hlen = 6;
34 packet->cookie = htonl(DHCP_MAGIC); 34 packet->cookie = htonl(DHCP_MAGIC);
35 packet->options[0] = DHCP_END; 35 if (DHCP_END != 0)
36 packet->options[0] = DHCP_END;
36 add_simple_option(packet->options, DHCP_MESSAGE_TYPE, type); 37 add_simple_option(packet->options, DHCP_MESSAGE_TYPE, type);
37} 38}
38 39
@@ -228,6 +229,7 @@ int FAST_FUNC udhcp_send_raw_packet(struct dhcp_packet *dhcp_pkt,
228 msg = "sendto"; 229 msg = "sendto";
229 ret_close: 230 ret_close:
230 close(fd); 231 close(fd);
232 /* FIXME: and if result >= 0 but != IP_UPD_DHCP_SIZE? */
231 if (result < 0) { 233 if (result < 0) {
232 ret_msg: 234 ret_msg:
233 bb_perror_msg(msg, "PACKET"); 235 bb_perror_msg(msg, "PACKET");
@@ -280,6 +282,7 @@ int FAST_FUNC udhcp_send_kernel_packet(struct dhcp_packet *dhcp_pkt,
280 msg = "write"; 282 msg = "write";
281 ret_close: 283 ret_close:
282 close(fd); 284 close(fd);
285 /* FIXME: and if result >= 0 but != DHCP_SIZE? */
283 if (result < 0) { 286 if (result < 0) {
284 ret_msg: 287 ret_msg:
285 bb_perror_msg(msg, "UDP"); 288 bb_perror_msg(msg, "UDP");
diff --git a/networking/udhcp/script.c b/networking/udhcp/script.c
index b4413fa16..400fd2b9b 100644
--- a/networking/udhcp/script.c
+++ b/networking/udhcp/script.c
@@ -64,7 +64,7 @@ static NOINLINE char *xmalloc_optname_optval(uint8_t *option, const struct dhcp_
64 64
65 /* option points to OPT_DATA, need to go back and get OPT_LEN */ 65 /* option points to OPT_DATA, need to go back and get OPT_LEN */
66 len = option[OPT_LEN - OPT_DATA]; 66 len = option[OPT_LEN - OPT_DATA];
67 type = type_p->flags & TYPE_MASK; 67 type = type_p->flags & OPTION_TYPE_MASK;
68 optlen = dhcp_option_lengths[type]; 68 optlen = dhcp_option_lengths[type];
69 upper_length = len_of_option_as_string[type] * (len / optlen); 69 upper_length = len_of_option_as_string[type] * (len / optlen);
70 70