summaryrefslogtreecommitdiff
path: root/networking/udhcp/dhcpd.c
diff options
context:
space:
mode:
Diffstat (limited to 'networking/udhcp/dhcpd.c')
-rw-r--r--networking/udhcp/dhcpd.c264
1 files changed, 138 insertions, 126 deletions
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: