1/* dhcpd.c 2 * 3 * udhcp Server 4 * Copyright (C) 1999 Matthew Ramsay <matthewr@moreton.com.au> 5 * Chris Trew <ctrew@moreton.com.au> 6 * 7 * Rewrite by Russ Dill <Russ.Dill@asu.edu> July 2001 8 * 9 * This program is free software; you can redistribute it and/or modify 10 * it under the terms of the GNU General Public License as published by 11 * the Free Software Foundation; either version 2 of the License, or 12 * (at your option) any later version. 13 * 14 * This program is distributed in the hope that it will be useful, 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 * GNU General Public License for more details. 18 * 19 * You should have received a copy of the GNU General Public License 20 * along with this program; if not, write to the Free Software 21 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 22 */ 23 24#include <fcntl.h> 25#include <string.h> 26#include <stdlib.h> 27#include <sys/wait.h> 28#include <sys/stat.h> 29#include <arpa/inet.h> 30#include <netdb.h> 31#include <netinet/in.h> 32#include <stdio.h> 33#include <sys/types.h> 34#include <sys/socket.h> 35#include <unistd.h> 36#include <signal.h> 37#include <errno.h> 38#include <sys/ioctl.h> 39#include <time.h> 40#include <sys/time.h> 41 42#include "debug.h" 43#include "dhcpd.h" 44#include "arpping.h" 45#include "socket.h" 46#include "options.h" 47#include "files.h" 48#include "leases.h" 49#include "packet.h" 50#include "serverpacket.h" 51#include "pidfile.h" 52 53 54/* globals */ 55struct dhcpOfferedAddr *leases; 56struct server_config_t server_config; 57static int signal_pipe[2]; 58 59/* Exit and cleanup */ 60static void exit_server(int retval) 61{ 62 pidfile_delete(server_config.pidfile); 63 CLOSE_LOG(); 64 exit(retval); 65} 66 67 68/* Signal handler */ 69static void signal_handler(int sig) 70{ 71 if (send(signal_pipe[1], &sig, sizeof(sig), MSG_DONTWAIT) < 0) { 72 LOG(LOG_ERR, "Could not send signal: %s", 73 strerror(errno)); 74 } 75} 76 77#ifdef COMBINED_BINARY 78int udhcpd_main(int argc, char *argv[]) 79#else 80int main(int argc, char *argv[]) 81#endif 82{ 83 fd_set rfds; 84 struct timeval tv; 85 int server_socket = -1; 86 int bytes, retval; 87 struct dhcpMessage packet; 88 unsigned char *state; 89 unsigned char *server_id, *requested, *hostname; 90 u_int32_t server_id_align, requested_align; 91 unsigned long timeout_end; 92 struct option_set *option; 93 struct dhcpOfferedAddr *lease; 94 int pid_fd; 95 int max_sock; 96 int sig; 97 98 OPEN_LOG("udhcpd"); 99 LOG(LOG_INFO, "udhcp server (v%s) started", VERSION); 100 101 memset(&server_config, 0, sizeof(struct server_config_t)); 102 103 if (argc < 2) 104 read_config(DHCPD_CONF_FILE); 105 else read_config(argv[1]); 106 107 /*get reserved ip from configuration file*/ 108 num_of_reservedIP = getReservedAddr(resrvMacAddr, resrvIpAddr); 109 110 pid_fd = pidfile_acquire(server_config.pidfile); 111 pidfile_write_release(pid_fd); 112 113 if ((option = find_option(server_config.options, DHCP_LEASE_TIME))) { 114 memcpy(&server_config.lease, option->data + 2, 4); 115 server_config.lease = ntohl(server_config.lease); 116 } 117 else server_config.lease = LEASE_TIME; 118 119 leases = malloc(sizeof(struct dhcpOfferedAddr) * server_config.max_leases); 120 memset(leases, 0, sizeof(struct dhcpOfferedAddr) * server_config.max_leases); 121 read_leases(server_config.lease_file); 122 123 if (read_interface(server_config.interface, &server_config.ifindex, 124 &server_config.server, server_config.arp) < 0) 125 exit_server(1); 126 127#ifndef DEBUGGING 128 pid_fd = pidfile_acquire(server_config.pidfile); /* hold lock during fork. */ 129 if (daemon(0, 0) == -1) { 130 perror("fork"); 131 exit_server(1); 132 } 133 pidfile_write_release(pid_fd); 134#endif 135 136 137 socketpair(AF_UNIX, SOCK_STREAM, 0, signal_pipe); 138 signal(SIGUSR1, signal_handler); 139 signal(SIGTERM, signal_handler); 140 141 timeout_end = time(0) + server_config.auto_time; 142 while(1) { /* loop until universe collapses */ 143 144 if (server_socket < 0) 145 if ((server_socket = listen_socket(INADDR_ANY, SERVER_PORT, server_config.interface)) < 0) { 146 LOG(LOG_ERR, "FATAL: couldn't create server socket, %s", strerror(errno)); 147 exit_server(0); 148 } 149 150 FD_ZERO(&rfds); 151 FD_SET(server_socket, &rfds); 152 FD_SET(signal_pipe[0], &rfds); 153 if (server_config.auto_time) { 154 tv.tv_sec = timeout_end - time(0); 155 tv.tv_usec = 0; 156 } 157 if (!server_config.auto_time || tv.tv_sec > 0) { 158 max_sock = server_socket > signal_pipe[0] ? server_socket : signal_pipe[0]; 159 retval = select(max_sock + 1, &rfds, NULL, NULL, 160 server_config.auto_time ? &tv : NULL); 161 } else retval = 0; /* If we already timed out, fall through */ 162 163 if (retval == 0) { 164 write_leases(); 165 timeout_end = time(0) + server_config.auto_time; 166 continue; 167 } else if (retval < 0 && errno != EINTR) { 168 DEBUG(LOG_INFO, "error on select"); 169 continue; 170 } 171 172 if (FD_ISSET(signal_pipe[0], &rfds)) { 173 if (read(signal_pipe[0], &sig, sizeof(sig)) < 0) 174 continue; /* probably just EINTR */ 175 switch (sig) { 176 case SIGUSR1: 177 LOG(LOG_INFO, "Received a SIGUSR1"); 178 write_leases(); 179 /* why not just reset the timeout, eh */ 180 timeout_end = time(0) + server_config.auto_time; 181 continue; 182 case SIGTERM: 183 LOG(LOG_INFO, "Received a SIGTERM"); 184 exit_server(0); 185 } 186 } 187 188 if ((bytes = get_packet(&packet, server_socket)) < 0) { /* this waits for a packet - idle */ 189 if (bytes == -1 && errno != EINTR) { 190 DEBUG(LOG_INFO, "error on read, %s, reopening socket", strerror(errno)); 191 close(server_socket); 192 server_socket = -1; 193 } 194 continue; 195 } 196 197 if ((state = get_option(&packet, DHCP_MESSAGE_TYPE)) == NULL) { 198 DEBUG(LOG_ERR, "couldn't get option from packet, ignoring"); 199 continue; 200 } 201 202 /* ADDME: look for a static lease */ 203 lease = find_lease_by_chaddr(packet.chaddr); 204 switch (state[0]) { 205 case DHCPDISCOVER: 206 DEBUG(LOG_INFO,"received DISCOVER"); 207 208 if (sendOffer(&packet) < 0) { 209 LOG(LOG_ERR, "send OFFER failed"); 210 } 211 break; 212 case DHCPREQUEST: 213 { 214 /* foxcon added start by EricHuang, 03/01/2007 */ 215 unsigned char mac[6]; 216 u_int32_t r_addr; 217 memcpy(mac, packet.chaddr, 6); 218 /* foxcon added end by EricHuang, 03/01/2007 */ 219 220 DEBUG(LOG_INFO, "received REQUEST"); 221 222 requested = get_option(&packet, DHCP_REQUESTED_IP); 223 server_id = get_option(&packet, DHCP_SERVER_ID); 224 hostname = get_option(&packet, DHCP_HOST_NAME); 225 226 if (requested) memcpy(&requested_align, requested, 4); 227 /* Foxconn added start pling 08/03/2011 */ 228 /* Should clear this var, otherwise it keeps old value 229 * (from previous packet) and cause reserved IP client 230 * to get NAK. 231 */ 232 else 233 requested_align = 0; 234 /* Foxconn added end pling 08/03/2011 */ 235 if (server_id) memcpy(&server_id_align, server_id, 4); 236 237 /* foxcon added start by EricHuang, 03/01/2007 */ 238 r_addr = find_reserved_ip(mac); 239 if (r_addr) { 240 if (requested_align) 241 { 242 if ( requested_align!=htonl(r_addr)) 243 { 244 sendNAK(&packet); 245 DEBUG(LOG_INFO, "prepare to send a reserved ip (0x%x, 0x%x)\n", requested_align, htonl(r_addr)); 246 break; 247 } 248 } 249 } 250 /* foxcon added end by EricHuang, 03/01/2007 */ 251 252 if (lease) { /*ADDME: or static lease */ 253 if (server_id) { 254 /* SELECTING State */ 255 DEBUG(LOG_INFO, "server_id = %08x", ntohl(server_id_align)); 256 if (server_id_align == server_config.server && requested && 257 requested_align == lease->yiaddr) { 258 sendACK(&packet, lease->yiaddr); 259 } 260 } else { 261 if (requested) { 262 /* INIT-REBOOT State */ 263 if (lease->yiaddr == requested_align) 264 sendACK(&packet, lease->yiaddr); 265 else sendNAK(&packet); 266 } else { 267 /* RENEWING or REBINDING State */ 268 if (lease->yiaddr == packet.ciaddr) 269 sendACK(&packet, lease->yiaddr); 270 else { 271 /* don't know what to do!!!! */ 272 sendNAK(&packet); 273 } 274 } 275 } 276 if (hostname) { 277 bytes = hostname[-1]; 278 if (bytes >= (int) sizeof(lease->hostname)) 279 bytes = sizeof(lease->hostname) - 1; 280 strncpy(lease->hostname, hostname, bytes); 281 lease->hostname[bytes] = '\0'; 282 DEBUG(LOG_INFO,"rewrite leass table, hostname: %s\n", lease->hostname); 283 /* foxconn wklin removed, 05/07/2007 */ 284 /* write_leases(); */ /*Rewrite lease table into file.*/ 285 } else 286 lease->hostname[0] = '\0'; 287 /* foxconn wklin added, 05/07/2007 */ 288 write_leases(); /*Rewrite lease table into file.*/ 289 290 /* what to do if we have no record of the client */ 291 } else if (server_id) { 292 /* SELECTING State */ 293 294 } else if (requested) { 295 /* INIT-REBOOT State */ 296 if ((lease = find_lease_by_yiaddr(requested_align))) { 297 if (lease_expired(lease)) { 298 /* probably best if we drop this lease */ 299 memset(lease->chaddr, 0, 16); 300 write_leases(); /*Rewrite lease table into file.*/ 301 /* make some contention for this address */ 302 } else sendNAK(&packet); 303 } else if (requested_align < server_config.start || 304 requested_align > server_config.end) { 305 sendNAK(&packet); 306 } else { 307 sendNAK(&packet); 308 } 309 310 } else if (packet.ciaddr) { 311 /* RENEWING or REBINDING State */ 312 sendNAK(&packet); 313 } 314 break; 315 } 316 case DHCPDECLINE: 317 DEBUG(LOG_INFO,"received DECLINE"); 318 if (lease) { 319 memset(lease->chaddr, 0, 16); 320 lease->expires = time(0) + server_config.decline_time; 321 write_leases(); /*Rewrite lease table into file.*/ 322 } 323 break; 324 case DHCPRELEASE: 325 DEBUG(LOG_INFO,"received RELEASE"); 326 if (lease) 327 { 328 lease->expires = time(0); 329 write_leases(); /*Rewrite lease table into file.*/ 330 } 331 break; 332 case DHCPINFORM: 333 DEBUG(LOG_INFO,"received INFORM"); 334 send_inform(&packet); 335 break; 336 default: 337 LOG(LOG_WARNING, "unsupported DHCP message (%02x) -- ignoring", state[0]); 338 } 339 } 340 341 return 0; 342} 343 344