3 * Moreton Bay DHCP Server
4 * Copyright (C) 1999 Matthew Ramsay <matthewr@moreton.com.au>
5 * Chris Trew <ctrew@moreton.com.au>
7 * Rewrite by Russ Dill <Russ.Dill@asu.edu> July 2001
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.
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.
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.
29 #include <arpa/inet.h>
31 #include <netinet/in.h>
33 #include <sys/types.h>
34 #include <sys/socket.h>
38 #include <sys/ioctl.h>
50 #include "serverpacket.h"
55 struct dhcpOfferedAddr *leases;
56 struct server_config_t server_config;
58 struct dhcpOfferedAddr *declines;
59 struct vendor_id_config_t vendor_id_config[MAX_VENDOR_IDS];
62 static int vendor_id_cfg = 0;
65 /* Exit and cleanup */
66 static void exit_server(int retval)
68 pidfile_delete(server_config.pidfile);
75 static void udhcpd_killed(int sig)
78 LOG(LOG_INFO, "Received SIGTERM");
84 static void test_vendorid(struct dhcpMessage *packet, char *vendorid, int *declined)
89 for (i = 0; i < MAX_VENDOR_IDS; i++) {
90 if (strlen(vendor_id_config[i].vendorid) == 0) {
93 memset(opt_vend, 0, 64);
94 length = *(vendorid - 1);
95 memcpy(opt_vend, vendorid, (size_t)length);
96 if (strlen(opt_vend) != strlen(vendor_id_config[i].vendorid)) {
99 if (strncmp(opt_vend, vendor_id_config[i].vendorid,
100 strlen(vendor_id_config[i].vendorid)) == 0) {
101 memset(declines, 0, sizeof(struct dhcpOfferedAddr));
102 memcpy(declines->chaddr, packet->chaddr, 16);
103 memset(declines->vendorid, 0, 64);
104 memcpy(declines->vendorid, opt_vend, (size_t)length);
105 /* Write this to the decline file */
116 #ifdef COMBINED_BINARY
117 int udhcpd(int argc, char *argv[])
119 int main(int argc, char *argv[])
124 //BRCM --initialize server_socket to -1
125 int server_socket = -1;
127 struct dhcpMessage packet;
128 unsigned char *state;
129 // BRCM added vendorid
130 char *server_id, *requested, *hostname, *vendorid = NULL;
131 u_int32_t server_id_align, requested_align;
132 unsigned long timeout_end;
133 struct option_set *option;
134 struct dhcpOfferedAddr *lease;
135 struct sockaddr_in *sin;
142 argc = argv[0][0]; /* get rid of some warnings */
145 LOG(LOG_INFO, "udhcp server (v%s) started", VERSION);
147 pid_fd = pidfile_acquire(server_config.pidfile);
148 pidfile_write_release(pid_fd);
150 memset(&server_config, 0, sizeof(struct server_config_t));
152 read_config(DHCPD_CONF_FILE);
153 if ((option = find_option(server_config.options, DHCP_LEASE_TIME))) {
154 memcpy(&server_config.lease, option->data + 2, 4);
155 server_config.lease = ntohl(server_config.lease);
157 else server_config.lease = LEASE_TIME;
159 leases = malloc(sizeof(struct dhcpOfferedAddr) * server_config.max_leases);
160 memset(leases, 0, sizeof(struct dhcpOfferedAddr) * server_config.max_leases);
161 read_leases(server_config.lease_file);
164 declines = malloc(sizeof(struct dhcpOfferedAddr));
165 /* vendor identifying info list */
166 viList = malloc(sizeof(VI_INFO_LIST));
167 memset(viList, 0, sizeof(VI_INFO_LIST));
170 if((fd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) >= 0) {
171 ifr.ifr_addr.sa_family = AF_INET;
172 strcpy(ifr.ifr_name, server_config.interface);
173 if (ioctl(fd, SIOCGIFADDR, &ifr) == 0) {
174 sin = (struct sockaddr_in *) &ifr.ifr_addr;
175 server_config.server = sin->sin_addr.s_addr;
176 DEBUG(LOG_INFO, "%s (server_ip) = %s", ifr.ifr_name, inet_ntoa(sin->sin_addr));
178 LOG(LOG_ERR, "SIOCGIFADDR failed!");
181 if (ioctl(fd, SIOCGIFINDEX, &ifr) == 0) {
182 DEBUG(LOG_INFO, "adapter index %d", ifr.ifr_ifindex);
183 server_config.ifindex = ifr.ifr_ifindex;
185 LOG(LOG_ERR, "SIOCGIFINDEX failed!");
188 if (ioctl(fd, SIOCGIFHWADDR, &ifr) == 0) {
189 memcpy(server_config.arp, ifr.ifr_hwaddr.sa_data, 6);
190 DEBUG(LOG_INFO, "adapter hardware address %02x:%02x:%02x:%02x:%02x:%02x",
191 server_config.arp[0], server_config.arp[1], server_config.arp[2],
192 server_config.arp[3], server_config.arp[4], server_config.arp[5]);
194 LOG(LOG_ERR, "SIOCGIFHWADDR failed!");
198 LOG(LOG_ERR, "socket failed!");
204 pid_fd = pidfile_acquire(server_config.pidfile); /* hold lock during fork. */
211 break; /* child continues */
213 exit(0); /* parent exits */
218 pidfile_write_release(pid_fd);
222 signal(SIGUSR1, write_leases);
223 signal(SIGTERM, udhcpd_killed);
224 signal(SIGUSR2, write_viTable);
226 timeout_end = time(0) + server_config.auto_time;
228 while(1) { /* loop until universe collapses */
231 if (server_socket < 0) {
232 server_socket = listen_socket(INADDR_ANY, SERVER_PORT, server_config.interface);
233 if(server_socket < 0) {
234 LOG(LOG_ERR, "couldn't create server socket -- au revoir");
240 FD_SET(server_socket, &rfds);
241 if (server_config.auto_time) {
242 tv.tv_sec = timeout_end - time(0);
243 if (tv.tv_sec <= 0) {
244 tv.tv_sec = server_config.auto_time;
245 timeout_end = time(0) + server_config.auto_time;
250 retval = select(server_socket + 1, &rfds, NULL, NULL, server_config.auto_time ? &tv : NULL);
253 timeout_end = time(0) + server_config.auto_time;
254 close(server_socket);
258 } else if (retval < 0) {
259 DEBUG(LOG_INFO, "error on select");
260 close(server_socket);
266 bytes = get_packet(&packet, server_socket); /* this waits for a packet - idle */
268 //close(server_socket);
272 if((state = get_option(&packet, DHCP_MESSAGE_TYPE)) == NULL) {
273 DEBUG(LOG_ERR, "couldnt get option from packet -- ignoring");
277 lease = find_lease_by_chaddr(packet.chaddr);
280 DEBUG(LOG_INFO,"received DISCOVER");
282 vendorid = get_option(&packet, DHCP_VENDOR);
284 /* Check the vendor ID with the configured vendor ID */
285 if (read_vendor_id_config(DHCPD_VENDORID_CONF_FILE) == 1) {
289 test_vendorid(&packet, vendorid, &declined);
294 if (sendOffer(&packet) < 0) {
295 LOG(LOG_ERR, "send OFFER failed -- ignoring");
300 DEBUG(LOG_INFO,"received REQUEST");
302 requested = get_option(&packet, DHCP_REQUESTED_IP);
303 server_id = get_option(&packet, DHCP_SERVER_ID);
304 hostname = get_option(&packet, DHCP_HOST_NAME);
305 if (requested) memcpy(&requested_align, requested, 4);
306 if (server_id) memcpy(&server_id_align, server_id, 4);
309 vendorid = get_option(&packet, DHCP_VENDOR);
311 /* Check the vendor ID with the configured vendor ID */
312 if (read_vendor_id_config(DHCPD_VENDORID_CONF_FILE) == 1) {
316 test_vendorid(&packet, vendorid, &declined);
322 /* SELECTING State */
323 DEBUG(LOG_INFO, "server_id = %08x", ntohl(server_id_align));
324 if (server_id_align == server_config.server && requested &&
325 requested_align == lease->yiaddr) {
326 sendACK(&packet, lease->yiaddr);
330 /* INIT-REBOOT State */
331 if (lease->yiaddr == requested_align)
332 sendACK(&packet, lease->yiaddr);
333 else sendNAK(&packet);
335 /* RENEWING or REBINDING State */
336 if (lease->yiaddr == packet.ciaddr)
337 sendACK(&packet, lease->yiaddr);
339 /* don't know what to do!!!! */
345 bytes = hostname[-1];
346 if (bytes >= (int) sizeof(lease->hostname))
347 bytes = sizeof(lease->hostname) - 1;
348 strncpy(lease->hostname, hostname, bytes);
349 lease->hostname[bytes] = '\0';
351 lease->hostname[0] = '\0';
352 } else { /* else remain silent */
358 DEBUG(LOG_INFO,"received DECLINE");
360 memset(lease->chaddr, 0, 16);
361 lease->expires = time(0) + server_config.decline_time;
365 DEBUG(LOG_INFO,"received RELEASE");
366 if (lease) lease->expires = time(0);
369 DEBUG(LOG_INFO,"received INFORM");
370 send_inform(&packet);
373 LOG(LOG_WARNING, "unsupported DHCP message (%02x) -- ignoring", state[0]);
377 if (server_socket > 0)
378 close(server_socket);