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 53extern void load_leases(char *file); 54 55/* globals */ 56struct dhcpOfferedAddr *leases; 57struct server_config_t server_config; 58static int signal_pipe[2]; 59 60/* Exit and cleanup */ 61static void exit_server(int retval) 62{ 63 pidfile_delete(server_config.pidfile); 64 CLOSE_LOG(); 65 exit(retval); 66} 67 68 69/* Signal handler */ 70static void signal_handler(int sig) 71{ 72 if (send(signal_pipe[1], &sig, sizeof(sig), MSG_DONTWAIT) < 0) { 73 LOG(LOG_ERR, "Could not send signal: %s", 74 strerror(errno)); 75 } 76} 77 78 79#ifdef COMBINED_BINARY 80int udhcpd_main(int argc, char *argv[]) 81#else 82int main(int argc, char *argv[]) 83#endif 84{ 85 fd_set rfds; 86 struct timeval tv; 87 int server_socket = -1; 88 int bytes, retval; 89 struct dhcpMessage packet; 90 unsigned char *state; 91 unsigned char *server_id, *requested, *hostname; 92 u_int32_t server_id_align, requested_align; 93 unsigned long timeout_end; 94 struct option_set *option; 95 struct dhcpOfferedAddr *lease; 96 int pid_fd; 97 int max_sock; 98 int sig; 99 100 OPEN_LOG("udhcpd"); 101 LOG(LOG_INFO, "udhcp server (v%s) started", VERSION); 102 103 memset(&server_config, 0, sizeof(struct server_config_t)); 104 105 if (argc < 2) 106 read_config(DHCPD_CONF_FILE); 107 else read_config(argv[1]); 108 109 pid_fd = pidfile_acquire(server_config.pidfile); 110 pidfile_write_release(pid_fd); 111 112 if ((option = find_option(server_config.options, DHCP_LEASE_TIME))) { 113 memcpy(&server_config.lease, option->data + 2, 4); 114 server_config.lease = ntohl(server_config.lease); 115 } 116 else server_config.lease = LEASE_TIME; 117 118 leases = malloc(sizeof(struct dhcpOfferedAddr) * server_config.max_leases); 119 memset(leases, 0, sizeof(struct dhcpOfferedAddr) * server_config.max_leases); 120 121 // Added by Joey to load static lease 122 if (argc>=3) 123 { 124 load_leases(argv[2]); 125 } 126 127 read_leases(server_config.lease_file); 128 129 if (read_interface(server_config.interface, &server_config.ifindex, 130 &server_config.server, server_config.arp) < 0) 131 exit_server(1); 132 133#ifndef DEBUGGING 134 pid_fd = pidfile_acquire(server_config.pidfile); /* hold lock during fork. */ 135 if (daemon(0, 0) == -1) { 136 perror("fork"); 137 exit_server(1); 138 } 139 pidfile_write_release(pid_fd); 140#endif 141 142 143 socketpair(AF_UNIX, SOCK_STREAM, 0, signal_pipe); 144 signal(SIGUSR1, signal_handler); 145 signal(SIGTERM, signal_handler); 146 147 timeout_end = time(0) + server_config.auto_time; 148 while(1) { /* loop until universe collapses */ 149 150 if (server_socket < 0) 151 if ((server_socket = listen_socket(INADDR_ANY, SERVER_PORT, server_config.interface)) < 0) { 152 LOG(LOG_ERR, "FATAL: couldn't create server socket, %s", strerror(errno)); 153 exit_server(0); 154 } 155 156 FD_ZERO(&rfds); 157 FD_SET(server_socket, &rfds); 158 FD_SET(signal_pipe[0], &rfds); 159 if (server_config.auto_time) { 160 tv.tv_sec = timeout_end - time(0); 161 tv.tv_usec = 0; 162 } 163 if (!server_config.auto_time || tv.tv_sec > 0) { 164 max_sock = server_socket > signal_pipe[0] ? server_socket : signal_pipe[0]; 165 retval = select(max_sock + 1, &rfds, NULL, NULL, 166 server_config.auto_time ? &tv : NULL); 167 } else retval = 0; /* If we already timed out, fall through */ 168 169 if (retval == 0) { 170 write_leases(); 171 timeout_end = time(0) + server_config.auto_time; 172 continue; 173 } else if (retval < 0 && errno != EINTR) { 174 DEBUG(LOG_INFO, "error on select"); 175 continue; 176 } 177 178 if (FD_ISSET(signal_pipe[0], &rfds)) { 179 if (read(signal_pipe[0], &sig, sizeof(sig)) < 0) 180 continue; /* probably just EINTR */ 181 switch (sig) { 182 case SIGUSR1: 183 LOG(LOG_INFO, "Received a SIGUSR1"); 184 write_leases(); 185 /* why not just reset the timeout, eh */ 186 timeout_end = time(0) + server_config.auto_time; 187 continue; 188 case SIGTERM: 189 LOG(LOG_INFO, "Received a SIGTERM"); 190 exit_server(0); 191 } 192 } 193 194 if ((bytes = get_packet(&packet, server_socket)) < 0) { /* this waits for a packet - idle */ 195 if (bytes == -1 && errno != EINTR) { 196 DEBUG(LOG_INFO, "error on read, %s, reopening socket", strerror(errno)); 197 close(server_socket); 198 server_socket = -1; 199 } 200 continue; 201 } 202 203 if ((state = get_option(&packet, DHCP_MESSAGE_TYPE)) == NULL) { 204 DEBUG(LOG_ERR, "couldn't get option from packet, ignoring"); 205 continue; 206 } 207 208 /* ADDME: look for a static lease */ 209 lease = find_lease_by_chaddr(packet.chaddr); 210 switch (state[0]) { 211 case DHCPDISCOVER: 212 DEBUG(LOG_INFO,"received DISCOVER"); 213 214 if (sendOffer(&packet) < 0) { 215 LOG(LOG_ERR, "send OFFER failed"); 216 } 217 break; 218 case DHCPREQUEST: 219 DEBUG(LOG_INFO, "received REQUEST"); 220 221 requested = get_option(&packet, DHCP_REQUESTED_IP); 222 server_id = get_option(&packet, DHCP_SERVER_ID); 223 hostname = get_option(&packet, DHCP_HOST_NAME); 224 225 if (requested) memcpy(&requested_align, requested, 4); 226 if (server_id) memcpy(&server_id_align, server_id, 4); 227 228 if (lease) { /*ADDME: or static lease */ 229 if (server_id) { 230 /* SELECTING State */ 231 DEBUG(LOG_INFO, "server_id = %08x", ntohl(server_id_align)); 232 if (server_id_align == server_config.server && requested && 233 requested_align == lease->yiaddr) { 234 sendACK(&packet, lease->yiaddr); 235 } 236 } else { 237 if (requested) { 238 /* INIT-REBOOT State */ 239 if (lease->yiaddr == requested_align) 240 sendACK(&packet, lease->yiaddr); 241 else sendNAK(&packet); 242 } else { 243 /* RENEWING or REBINDING State */ 244 if (lease->yiaddr == packet.ciaddr) 245 sendACK(&packet, lease->yiaddr); 246 else { 247 /* don't know what to do!!!! */ 248 sendNAK(&packet); 249 } 250 } 251 } 252 if (hostname) { 253 bytes = hostname[-1]; 254 if (bytes >= (int) sizeof(lease->hostname)) 255 bytes = sizeof(lease->hostname) - 1; 256 strncpy(lease->hostname, hostname, bytes); 257 lease->hostname[bytes] = '\0'; 258 } else 259 lease->hostname[0] = '\0'; 260 261 /* what to do if we have no record of the client */ 262 } else if (server_id) { 263 /* SELECTING State */ 264 265 } else if (requested) { 266 /* INIT-REBOOT State */ 267 if ((lease = find_lease_by_yiaddr(requested_align))) { 268 if (lease_expired(lease)) { 269 /* probably best if we drop this lease */ 270 memset(lease->chaddr, 0, 16); 271 /* make some contention for this address */ 272 } else sendNAK(&packet); 273 } else if (requested_align < server_config.start || 274 requested_align > server_config.end) { 275 sendNAK(&packet); 276 } else { 277 sendNAK(&packet); 278 } 279 } else if (packet.ciaddr) { 280 /* RENEWING or REBINDING State */ 281 sendNAK(&packet); 282 } 283 break; 284 case DHCPDECLINE: 285 DEBUG(LOG_INFO,"received DECLINE"); 286 if (lease) { 287 memset(lease->chaddr, 0, 16); 288 lease->expires = time(0) + server_config.decline_time; 289 } 290 break; 291 case DHCPRELEASE: 292 DEBUG(LOG_INFO,"received RELEASE"); 293 if (lease) lease->expires = time(0); 294 break; 295 case DHCPINFORM: 296 DEBUG(LOG_INFO,"received INFORM"); 297 send_inform(&packet); 298 break; 299 default: 300 LOG(LOG_WARNING, "unsupported DHCP message (%02x) -- ignoring", state[0]); 301 } 302 } 303 304 return 0; 305} 306 307