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