1 |
/* Initialize an Ethernet interface |
2 |
Copyright (C) 1999, 2000 Free Software Foundation, Inc. |
3 |
Written by Thomas Bushnell, BSG. |
4 |
|
5 |
This file is part of the GNU Hurd. |
6 |
|
7 |
The GNU Hurd is free software; you can redistribute it and/or |
8 |
modify it under the terms of the GNU General Public License as |
9 |
published by the Free Software Foundation; either version 2, or (at |
10 |
your option) any later version. |
11 |
|
12 |
The GNU Hurd is distributed in the hope that it will be useful, but |
13 |
WITHOUT ANY WARRANTY; without even the implied warranty of |
14 |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
15 |
General Public License for more details. |
16 |
|
17 |
You should have received a copy of the GNU General Public License |
18 |
along with this program; if not, write to the Free Software |
19 |
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ |
20 |
|
21 |
/* |
22 |
* Copyright 1999 Red Hat Software, Inc. |
23 |
* |
24 |
* This program is free software; you can redistribute it and/or modify |
25 |
* it under the terms of the GNU General Public License as published by |
26 |
* the Free Software Foundation; either version 2 of the License, or |
27 |
* (at your option) any later version. |
28 |
* |
29 |
* This program is distributed in the hope that it will be useful, |
30 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
31 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
32 |
* GNU General Public License for more details. |
33 |
* |
34 |
* You should have received a copy of the GNU General Public License |
35 |
* along with this program; if not, write to the Free Software |
36 |
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
37 |
* |
38 |
*/ |
39 |
|
40 |
/* This probably doesn't work on anything other then Ethernet! It may work |
41 |
on PLIP as well, but ARCnet and Token Ring are unlikely at best. */ |
42 |
|
43 |
#define DHCP_FLAG_NODAEMON (1 << 0) |
44 |
#define DHCP_FLAG_NOCONFIG (1 << 1) |
45 |
#define DHCP_FLAG_FORCEHNLOOKUP (1 << 2) |
46 |
|
47 |
#define RESULT_OKAY 0 |
48 |
#define RESULT_FAILED 1 |
49 |
#define RESULT_UNKNOWNIFACE 2 |
50 |
|
51 |
#define N_(foo) (foo) |
52 |
|
53 |
#define PROGNAME "pump" |
54 |
#define CONTROLSOCKET "/var/run/pump.sock" |
55 |
|
56 |
#define _(foo) ((foo)) |
57 |
#include <stdarg.h> |
58 |
|
59 |
void logMessage(char * mess, ...) { |
60 |
va_list args; |
61 |
|
62 |
va_start(args, mess); |
63 |
vprintf(mess, args); |
64 |
va_end(args); |
65 |
puts(""); |
66 |
} |
67 |
|
68 |
typedef int bp_int32; |
69 |
typedef short bp_int16; |
70 |
|
71 |
#define BOOTP_OPTION_NETMASK 1 |
72 |
#define BOOTP_OPTION_GATEWAY 3 |
73 |
#define BOOTP_OPTION_DNS 6 |
74 |
#define BOOTP_OPTION_HOSTNAME 12 |
75 |
#define BOOTP_OPTION_BOOTFILE 13 |
76 |
#define BOOTP_OPTION_DOMAIN 15 |
77 |
#define BOOTP_OPTION_BROADCAST 28 |
78 |
|
79 |
#define DHCP_OPTION_REQADDR 50 |
80 |
#define DHCP_OPTION_LEASE 51 |
81 |
#define DHCP_OPTION_OVERLOAD 52 |
82 |
#define DHCP_OPTION_TYPE 53 |
83 |
#define DHCP_OPTION_SERVER 54 |
84 |
#define DHCP_OPTION_OPTIONREQ 55 |
85 |
#define DHCP_OPTION_MAXSIZE 57 |
86 |
#define DHCP_OPTION_T1 58 |
87 |
|
88 |
#define BOOTP_CLIENT_PORT 68 |
89 |
#define BOOTP_SERVER_PORT 67 |
90 |
|
91 |
#define BOOTP_OPCODE_REQUEST 1 |
92 |
#define BOOTP_OPCODE_REPLY 2 |
93 |
|
94 |
#define NORESPONSE -10 |
95 |
#define DHCP_TYPE_DISCOVER 1 |
96 |
#define DHCP_TYPE_OFFER 2 |
97 |
#define DHCP_TYPE_REQUEST 3 |
98 |
#define DHCP_TYPE_DECLINE 4 |
99 |
#define DHCP_TYPE_ACK 5 |
100 |
#define DHCP_TYPE_NAK 6 |
101 |
#define DHCP_TYPE_RELEASE 7 |
102 |
#define DHCP_TYPE_INFORM 8 |
103 |
|
104 |
#define BOOTP_VENDOR_LENGTH 64 |
105 |
#define DHCP_VENDOR_LENGTH 312 |
106 |
|
107 |
struct bootpRequest { |
108 |
char opcode; |
109 |
char hw; |
110 |
char hwlength; |
111 |
char hopcount; |
112 |
bp_int32 id; |
113 |
bp_int16 secs; |
114 |
bp_int16 flags; |
115 |
bp_int32 ciaddr, yiaddr, server_ip, bootp_gw_ip; |
116 |
char hwaddr[16]; |
117 |
char servername[64]; |
118 |
char bootfile[128]; |
119 |
char vendor[DHCP_VENDOR_LENGTH]; |
120 |
} ; |
121 |
|
122 |
struct psuedohUdpHeader { |
123 |
bp_int32 source, dest; |
124 |
char zero; |
125 |
char protocol; |
126 |
bp_int16 len; |
127 |
}; |
128 |
|
129 |
struct command { |
130 |
enum { CMD_STARTIFACE, CMD_RESULT, CMD_DIE, CMD_STOPIFACE, |
131 |
CMD_FORCERENEW, CMD_REQSTATUS, CMD_STATUS } type; |
132 |
union { |
133 |
struct { |
134 |
char device[20]; |
135 |
int flags; |
136 |
int reqLease; /* in hours */ |
137 |
char reqHostname[200]; |
138 |
} start; |
139 |
int result; /* 0 for success */ |
140 |
struct { |
141 |
char device[20]; |
142 |
} stop; |
143 |
struct { |
144 |
char device[20]; |
145 |
} renew; |
146 |
struct { |
147 |
char device[20]; |
148 |
} reqstatus; |
149 |
struct { |
150 |
struct intfInfo intf; |
151 |
char hostname[1024]; |
152 |
char domain[1024]; |
153 |
char bootFile[1024]; |
154 |
} status; |
155 |
} u; |
156 |
}; |
157 |
|
158 |
static char * doDhcp(char * device, int flags, int lease, |
159 |
char * reqHostname, struct intfInfo * intf, |
160 |
struct overrideInfo * override); |
161 |
|
162 |
static const char vendCookie[] = { 99, 130, 83, 99, 255 }; |
163 |
|
164 |
static char * perrorstr(char * msg); |
165 |
static char * setupInterface(struct intfInfo * intf, int s); |
166 |
static char * prepareInterface(struct intfInfo * intf, int s); |
167 |
static char * getInterfaceInfo(struct intfInfo * intf, int s); |
168 |
static char * disableInterface(char * device); |
169 |
static void parseReply(struct bootpRequest * breq, struct intfInfo * intf); |
170 |
static char * prepareRequest(struct bootpRequest * breq, |
171 |
int sock, char * device, time_t startTime); |
172 |
static void parseLease(struct bootpRequest * bresp, struct intfInfo * intf); |
173 |
static void initVendorCodes(struct bootpRequest * breq); |
174 |
static char * handleTransaction(int s, struct overrideInfo * override, |
175 |
struct bootpRequest * breq, |
176 |
struct bootpRequest * bresp, |
177 |
struct sockaddr_in * serverAddr, |
178 |
struct sockaddr_in * respondant, |
179 |
int useBootPacket, int dhcpResponseType); |
180 |
static int dhcpMessageType(struct bootpRequest * response); |
181 |
static void setMissingIpInfo(struct intfInfo * intf); |
182 |
static int openControlSocket(char * configFile); |
183 |
static int dhcpRenew(struct intfInfo * intf); |
184 |
static int dhcpRelease(struct intfInfo * intf); |
185 |
|
186 |
/* Various hurd-specific decls here */ |
187 |
#define _HURD_PFINET _HURD "pfinet" |
188 |
io_t underlying_pfinet; |
189 |
device_t ethernet_device; |
190 |
char ethernet_address[ETH_ALEN]; |
191 |
|
192 |
|
193 |
/* Open the ethernet device */ |
194 |
char * |
195 |
prepareInterface (struct intfInfo *intf, int s); |
196 |
{ |
197 |
u_int count; |
198 |
int addr[2]; |
199 |
|
200 |
err = device_open (master_device, D_WRITE|D_READ, |
201 |
intf->device, ðernet_device); |
202 |
if (err) |
203 |
return err; |
204 |
|
205 |
err = device_get_status (ethernet_device, NET_ADDRESS, addr, &count); |
206 |
if (err) |
207 |
return err; |
208 |
|
209 |
addr[0] = ntohl (addr[0]); |
210 |
addr[1] = ntohl (addr[1]); |
211 |
bcopy (addr, ethernet_address, ETH_ALEN); |
212 |
return 0; |
213 |
} |
214 |
|
215 |
/* Open the node underlying the pfinet translator, and set |
216 |
underlying_pfinet to a port to that file. */ |
217 |
error_t |
218 |
open_underlying_pfinet () |
219 |
{ |
220 |
char *path; |
221 |
|
222 |
if (underlying_pfinet != MACH_PORT_NULL) |
223 |
return 0; |
224 |
|
225 |
asprintf (&path, "%s/%d", _SERVERS_SOCKET, PF_INET); |
226 |
|
227 |
underlying_pfinet = file_name_lookup (path, O_NOTRANS, 0); |
228 |
free (path); |
229 |
return underlying_pfinet == MACH_PORT_NULL ? errno : 0; |
230 |
} |
231 |
|
232 |
/* Start pfinet with the specified arguments */ |
233 |
error_t |
234 |
start_pfinet (char *argz, int argz_len) |
235 |
{ |
236 |
error_t open_function (int flags, |
237 |
mach_port_t *underlying, |
238 |
mach_msg_type_name_t *underlying_type, |
239 |
task_t task, void *cookie) |
240 |
{ |
241 |
int err; |
242 |
|
243 |
err = open_underlying_pfinet (); |
244 |
if (err) |
245 |
return err; |
246 |
|
247 |
*underlying = underlying_pfinet; |
248 |
*underlying_type = MACH_MSG_TYPE_COPY_SEND; |
249 |
return 0; |
250 |
} |
251 |
|
252 |
err = fshelp_start_translator (open_function, NULL, |
253 |
_HURD_PFINET, argz, argz_len, |
254 |
60 * 1000, &control); |
255 |
if (err) |
256 |
return err; |
257 |
|
258 |
err = file_set_translator (underlying_pfinet, |
259 |
0, FS_TRANS_SET, |
260 |
FSYS_GOAWAY_FORCE, |
261 |
argz, argz_len, |
262 |
control, MACH_MSG_TYPE_COPY_SEND); |
263 |
mach_port_deallocate (mach_task_self (), control); |
264 |
|
265 |
/* Force the C library to forget about any old cached server |
266 |
access port. */ |
267 |
_hurd_socket_server (PFINET, 1); |
268 |
} |
269 |
|
270 |
|
271 |
|
272 |
int newKernel(void) { |
273 |
#ifdef NOTHURD |
274 |
struct utsname ubuf; |
275 |
int major1, major2; |
276 |
|
277 |
uname(&ubuf); |
278 |
if (!strcasecmp(ubuf.sysname, "linux")) { |
279 |
if (sscanf(ubuf.release, "%d.%d", &major1, &major2) != 2 || |
280 |
(major1 < 2) || (major1 == 2 && major2 == 0)) { |
281 |
return 0; |
282 |
} |
283 |
} |
284 |
#endif |
285 |
|
286 |
return 1; |
287 |
} |
288 |
|
289 |
static char * disableInterface(char * device) { |
290 |
#ifdef NOTHURD |
291 |
struct ifreq req; |
292 |
int s; |
293 |
|
294 |
s = socket(AF_INET, SOCK_DGRAM, 0); |
295 |
|
296 |
strcpy(req.ifr_name, device); |
297 |
if (ioctl(s, SIOCGIFFLAGS, &req)) { |
298 |
close(s); |
299 |
return perrorstr("SIOCGIFFLAGS"); |
300 |
} |
301 |
|
302 |
req.ifr_flags &= ~(IFF_UP | IFF_RUNNING); |
303 |
if (ioctl(s, SIOCSIFFLAGS, &req)) { |
304 |
close(s); |
305 |
return perrorstr("SIOCSIFFLAGS"); |
306 |
} |
307 |
|
308 |
close(s); |
309 |
|
310 |
return NULL; |
311 |
#else |
312 |
error_t err; |
313 |
|
314 |
err = open_underlying_pfinet (); |
315 |
if (err) |
316 |
return strerror (err); |
317 |
|
318 |
err = file_set_translator (underlying_pfinet, |
319 |
0, FS_TRANS_SET | FS_TRANS_FORCE, |
320 |
FSYS_GOAWAY_FORCE, |
321 |
0, 0, MACH_PORT_NULL); |
322 |
return err ? strerror (err) : 0; |
323 |
#endif |
324 |
} |
325 |
|
326 |
static char * setupInterface(struct intfInfo * intf, int s) { |
327 |
char * rc; |
328 |
char * s; |
329 |
char * argz = 0; |
330 |
int argz_len = 0; |
331 |
|
332 |
/* Construct the args to pfinet. */ |
333 |
/* /hurd/pfinet */ |
334 |
argz_add (&argz, &argz_len, _HURD_PFINET); |
335 |
|
336 |
/* Interface name */ |
337 |
asprintf (&s, "--interface=%s", intf->decive); |
338 |
argz_add (&argz, &argz_len, s); |
339 |
free (s); |
340 |
|
341 |
/* Address */ |
342 |
asprintf (&s, "--address=%s", inet_ntoa (intf->ip)); |
343 |
argz_add (&argz, &argz_len, s); |
344 |
free (s); |
345 |
|
346 |
/* Netmask */ |
347 |
asprintf (&s, "--netmask=%s", inet_ntoa (intf->netmask)); |
348 |
argz_add (&argz, &argz_len, s); |
349 |
free (s); |
350 |
|
351 |
/* Gateway */ |
352 |
asprintf (&s, "--gateway=%s", inet_ntoa (intf->gateway)); |
353 |
argz_add (&argz, &argz_len, s); |
354 |
free (s); |
355 |
|
356 |
/* Now set it up */ |
357 |
err = start_pfinet (argz, argz_len); |
358 |
free (argz); |
359 |
|
360 |
return err ? strerror (err) : 0; |
361 |
|
362 |
|
363 |
#ifdef NOTHURD |
364 |
struct sockaddr_in * addrp; |
365 |
struct ifreq req; |
366 |
struct rtentry route; |
367 |
|
368 |
if ((rc = disableInterface(intf->device))) return rc; |
369 |
|
370 |
/* we have to have basic information to get this far */ |
371 |
addrp = (struct sockaddr_in *) &req.ifr_addr; |
372 |
addrp->sin_family = AF_INET; |
373 |
strcpy(req.ifr_name, intf->device); |
374 |
|
375 |
addrp->sin_addr = intf->ip; |
376 |
if (ioctl(s, SIOCSIFADDR, &req)) |
377 |
return perrorstr("SIOCSIFADDR"); |
378 |
|
379 |
addrp->sin_addr = intf->netmask; |
380 |
if (ioctl(s, SIOCSIFNETMASK, &req)) |
381 |
return perrorstr("SIOCSIFNETMASK"); |
382 |
|
383 |
addrp->sin_addr = intf->broadcast; |
384 |
if (ioctl(s, SIOCSIFBRDADDR, &req)) |
385 |
return perrorstr("SIOCSIFBRDADDR"); |
386 |
|
387 |
/* bring up the device, and specifically allow broadcasts through it */ |
388 |
req.ifr_flags = IFF_UP | IFF_RUNNING | IFF_BROADCAST; |
389 |
if (ioctl(s, SIOCSIFFLAGS, &req)) |
390 |
return perrorstr("SIOCSIFFLAGS"); |
391 |
|
392 |
/* add a route for this network */ |
393 |
route.rt_dev = intf->device; |
394 |
route.rt_flags = RTF_UP; |
395 |
route.rt_metric = 0; |
396 |
|
397 |
if (!newKernel()) { |
398 |
addrp->sin_family = AF_INET; |
399 |
addrp->sin_port = 0; |
400 |
addrp->sin_addr = intf->network; |
401 |
memcpy(&route.rt_dst, addrp, sizeof(*addrp)); |
402 |
addrp->sin_addr = intf->netmask; |
403 |
memcpy(&route.rt_genmask, addrp, sizeof(*addrp)); |
404 |
|
405 |
if (ioctl(s, SIOCADDRT, &route)) { |
406 |
/* the route cannot already exist, as we've taken the device down */ |
407 |
return perrorstr("SIOCADDRT 1"); |
408 |
} |
409 |
} |
410 |
return NULL; |
411 |
} |
412 |
|
413 |
#ifdef NOTHURD |
414 |
static char * setupDefaultGateway(struct in_addr * gw, int s) { |
415 |
struct sockaddr_in addr; |
416 |
struct rtentry route; |
417 |
|
418 |
addr.sin_family = AF_INET; |
419 |
addr.sin_port = 0; |
420 |
addr.sin_addr.s_addr = INADDR_ANY; |
421 |
memcpy(&route.rt_dst, &addr, sizeof(addr)); |
422 |
memcpy(&route.rt_genmask, &addr, sizeof(addr)); |
423 |
addr.sin_addr = *gw; |
424 |
memcpy(&route.rt_gateway, &addr, sizeof(addr)); |
425 |
|
426 |
route.rt_flags = RTF_UP | RTF_GATEWAY; |
427 |
route.rt_metric = 0; |
428 |
route.rt_dev = NULL; |
429 |
|
430 |
if (ioctl(s, SIOCADDRT, &route)) { |
431 |
/* the route cannot already exist, as we've taken the device |
432 |
down */ |
433 |
return perrorstr("SIOCADDRT 2"); |
434 |
} |
435 |
|
436 |
return NULL; |
437 |
} |
438 |
|
439 |
static char * getInterfaceInfo(struct intfInfo * intf, int s) { |
440 |
struct ifreq req; |
441 |
struct sockaddr_in * addrp; |
442 |
|
443 |
strcpy(req.ifr_name, intf->device); |
444 |
if (ioctl(s, SIOCGIFBRDADDR, &req)) |
445 |
return perrorstr("SIOCGIFBRDADDR"); |
446 |
|
447 |
addrp = (struct sockaddr_in *) &req.ifr_addr; |
448 |
intf->broadcast = addrp->sin_addr; |
449 |
intf->set = INTFINFO_HAS_BROADCAST; |
450 |
|
451 |
return NULL; |
452 |
} |
453 |
|
454 |
static char * prepareInterface(struct intfInfo * intf, int s) { |
455 |
struct sockaddr_in * addrp; |
456 |
struct ifreq req; |
457 |
struct rtentry route; |
458 |
|
459 |
addrp = (struct sockaddr_in *) &req.ifr_addr; |
460 |
|
461 |
strcpy(req.ifr_name, intf->device); |
462 |
addrp->sin_family = AF_INET; |
463 |
addrp->sin_port = 0; |
464 |
memset(&addrp->sin_addr, 0, sizeof(addrp->sin_addr)); |
465 |
|
466 |
addrp->sin_family = AF_INET; |
467 |
addrp->sin_addr.s_addr = htonl(0); |
468 |
|
469 |
if (ioctl(s, SIOCSIFADDR, &req)) |
470 |
return perrorstr("SIOCSIFADDR"); |
471 |
|
472 |
if (!newKernel()) { |
473 |
if (ioctl(s, SIOCSIFNETMASK, &req)) |
474 |
return perrorstr("SIOCSIFNETMASK"); |
475 |
|
476 |
/* the broadcast address is 255.255.255.255 */ |
477 |
memset(&addrp->sin_addr, 255, sizeof(addrp->sin_addr)); |
478 |
if (ioctl(s, SIOCSIFBRDADDR, &req)) |
479 |
return perrorstr("SIOCSIFBRDADDR"); |
480 |
} |
481 |
|
482 |
req.ifr_flags = IFF_UP | IFF_BROADCAST | IFF_RUNNING; |
483 |
if (ioctl(s, SIOCSIFFLAGS, &req)) |
484 |
return perrorstr("SIOCSIFFLAGS"); |
485 |
|
486 |
memset(&route, 0, sizeof(route)); |
487 |
memcpy(&route.rt_gateway, addrp, sizeof(*addrp)); |
488 |
|
489 |
addrp->sin_family = AF_INET; |
490 |
addrp->sin_port = 0; |
491 |
addrp->sin_addr.s_addr = INADDR_ANY; |
492 |
memcpy(&route.rt_dst, addrp, sizeof(*addrp)); |
493 |
memcpy(&route.rt_genmask, addrp, sizeof(*addrp)); |
494 |
|
495 |
route.rt_dev = intf->device; |
496 |
route.rt_flags = RTF_UP; |
497 |
route.rt_metric = 0; |
498 |
|
499 |
if (ioctl(s, SIOCADDRT, &route)) { |
500 |
if (errno != EEXIST) { |
501 |
close(s); |
502 |
return perrorstr("SIOCADDRT 3"); |
503 |
} |
504 |
} |
505 |
|
506 |
return NULL; |
507 |
} |
508 |
#endif |
509 |
|
510 |
static int dhcpMessageType(struct bootpRequest * response) { |
511 |
unsigned char * chptr; |
512 |
unsigned char option, length; |
513 |
|
514 |
chptr = response->vendor; |
515 |
|
516 |
chptr += 4; |
517 |
while (*chptr != 0xFF) { |
518 |
option = *chptr++; |
519 |
if (!option) continue; |
520 |
length = *chptr++; |
521 |
if (option == DHCP_OPTION_TYPE) |
522 |
return *chptr; |
523 |
|
524 |
chptr += length; |
525 |
} |
526 |
|
527 |
return -1; |
528 |
} |
529 |
|
530 |
static void setMissingIpInfo(struct intfInfo * intf) { |
531 |
bp_int32 ipNum = *((bp_int32 *) &intf->ip); |
532 |
bp_int32 nmNum = *((bp_int32 *) &intf->netmask); |
533 |
bp_int32 ipRealNum = ntohl(ipNum); |
534 |
|
535 |
if (!(intf->set & INTFINFO_HAS_NETMASK)) { |
536 |
if (((ipRealNum & 0xFF000000) >> 24) <= 127) |
537 |
nmNum = 0xFF000000; |
538 |
else if (((ipRealNum & 0xFF000000) >> 24) <= 191) |
539 |
nmNum = 0xFFFF0000; |
540 |
else |
541 |
nmNum = 0xFFFFFF00; |
542 |
*((bp_int32 *) &intf->netmask) = nmNum = htonl(nmNum); |
543 |
syslog (LOG_DEBUG, "intf: netmask: %s", inet_ntoa (intf->netmask)); |
544 |
} |
545 |
|
546 |
if (!(intf->set & INTFINFO_HAS_BROADCAST)) { |
547 |
*((bp_int32 *) &intf->broadcast) = (ipNum & nmNum) | ~(nmNum); |
548 |
syslog (LOG_DEBUG, "intf: broadcast: %s", inet_ntoa (intf->broadcast)); |
549 |
} |
550 |
|
551 |
if (!(intf->set & INTFINFO_HAS_NETWORK)) { |
552 |
*((bp_int32 *) &intf->network) = ipNum & nmNum; |
553 |
syslog (LOG_DEBUG, "intf: network: %s", inet_ntoa (intf->network)); |
554 |
} |
555 |
|
556 |
intf->set |= INTFINFO_HAS_BROADCAST | INTFINFO_HAS_NETWORK | |
557 |
INTFINFO_HAS_NETMASK; |
558 |
} |
559 |
|
560 |
static void parseReply(struct bootpRequest * breq, struct intfInfo * intf) { |
561 |
unsigned int i; |
562 |
unsigned char * chptr; |
563 |
unsigned char option, length; |
564 |
|
565 |
syslog (LOG_DEBUG, "intf: device: %s", intf->device); |
566 |
syslog (LOG_DEBUG, "intf: set: %i", intf->set); |
567 |
syslog (LOG_DEBUG, "intf: bootServer: %s", inet_ntoa (intf->bootServer)); |
568 |
syslog (LOG_DEBUG, "intf: reqLease: %i", intf->reqLease); |
569 |
|
570 |
i = ~(INTFINFO_HAS_IP | INTFINFO_HAS_NETMASK | INTFINFO_HAS_NETWORK | |
571 |
INTFINFO_HAS_BROADCAST); |
572 |
intf->set &= i; |
573 |
|
574 |
if (strlen(breq->bootfile)) { |
575 |
intf->bootFile = strdup(breq->bootfile); |
576 |
intf->set |= INTFINFO_HAS_BOOTFILE; |
577 |
} else { |
578 |
intf->set &= ~INTFINFO_HAS_BOOTFILE; |
579 |
} |
580 |
syslog (LOG_DEBUG, "intf: bootFile: %s", intf->bootFile); |
581 |
|
582 |
memcpy(&intf->ip, &breq->yiaddr, 4); |
583 |
intf->set |= INTFINFO_HAS_IP; |
584 |
syslog (LOG_DEBUG, "intf: ip: %s", inet_ntoa (intf->ip)); |
585 |
|
586 |
chptr = breq->vendor; |
587 |
chptr += 4; |
588 |
while (*chptr != 0xFF && (void *) chptr < (void *) breq->vendor + DHCP_VENDOR_LENGTH) { |
589 |
option = *chptr++; |
590 |
if (!option) continue; |
591 |
length = *chptr++; |
592 |
|
593 |
switch (option) { |
594 |
case BOOTP_OPTION_DNS: |
595 |
intf->numDns = 0; |
596 |
for (i = 0; i < length; i += 4) { |
597 |
if (intf->numDns < MAX_DNS_SERVERS) { |
598 |
memcpy(&intf->dnsServers[intf->numDns++], chptr + i, 4); |
599 |
syslog(LOG_DEBUG, "intf: dnsServers[%i]: %s", |
600 |
i/4, inet_ntoa (intf->dnsServers[i/4])); |
601 |
} |
602 |
} |
603 |
intf->set |= NETINFO_HAS_DNS; |
604 |
syslog (LOG_DEBUG, "intf: numDns: %i", intf->numDns); |
605 |
break; |
606 |
|
607 |
case BOOTP_OPTION_NETMASK: |
608 |
memcpy(&intf->netmask, chptr, 4); |
609 |
intf->set |= INTFINFO_HAS_NETMASK; |
610 |
syslog (LOG_DEBUG, "intf: netmask: %s", inet_ntoa (intf->netmask)); |
611 |
break; |
612 |
|
613 |
case BOOTP_OPTION_DOMAIN: |
614 |
if ((intf->domain = malloc(length + 1))) { |
615 |
memcpy(intf->domain, chptr, length); |
616 |
intf->domain[length] = '\0'; |
617 |
intf->set |= NETINFO_HAS_DOMAIN; |
618 |
syslog (LOG_DEBUG, "intf: domain: %s", intf->domain); |
619 |
} |
620 |
break; |
621 |
|
622 |
case BOOTP_OPTION_BROADCAST: |
623 |
memcpy(&intf->broadcast, chptr, 4); |
624 |
intf->set |= INTFINFO_HAS_BROADCAST; |
625 |
syslog (LOG_DEBUG, "intf: broadcast: %s", inet_ntoa (intf->broadcast)); |
626 |
break; |
627 |
|
628 |
case BOOTP_OPTION_GATEWAY: |
629 |
memcpy(&intf->gateway, chptr, 4); |
630 |
intf->set |= NETINFO_HAS_GATEWAY; |
631 |
syslog (LOG_DEBUG, "intf: gateway: %s", inet_ntoa (intf->gateway)); |
632 |
break; |
633 |
|
634 |
case BOOTP_OPTION_HOSTNAME: |
635 |
if ((intf->hostname = malloc(length + 1))) { |
636 |
memcpy(intf->hostname, chptr, length); |
637 |
intf->hostname[length] = '\0'; |
638 |
intf->set |= NETINFO_HAS_HOSTNAME; |
639 |
syslog (LOG_DEBUG, "intf: hostname: %s", intf->hostname); |
640 |
} |
641 |
break; |
642 |
|
643 |
case BOOTP_OPTION_BOOTFILE: |
644 |
/* we ignore this right now */ |
645 |
break; |
646 |
|
647 |
case DHCP_OPTION_OVERLOAD: |
648 |
/* FIXME: we should pay attention to this */ |
649 |
logMessage("dhcp overload option is currently ignored!"); |
650 |
break; |
651 |
} |
652 |
|
653 |
chptr += length; |
654 |
} |
655 |
|
656 |
setMissingIpInfo(intf); |
657 |
} |
658 |
|
659 |
static char * perrorstr(char * msg) { |
660 |
static char * err = NULL; |
661 |
static int errsize = 0; |
662 |
static int newsize; |
663 |
|
664 |
newsize = strlen(msg) + strlen(strerror(errno)) + 3; |
665 |
if (!errsize) { |
666 |
errsize = newsize; |
667 |
err = malloc(errsize); |
668 |
} else if (errsize < newsize) { |
669 |
free(err); |
670 |
errsize = newsize; |
671 |
err = malloc(errsize); |
672 |
} |
673 |
|
674 |
if (err) |
675 |
sprintf(err, "%s: %s", msg, strerror(errno)); |
676 |
else |
677 |
err = "out of memory!"; |
678 |
|
679 |
return err; |
680 |
} |
681 |
|
682 |
static void initVendorCodes(struct bootpRequest * breq) { |
683 |
memcpy(breq->vendor, vendCookie, sizeof(vendCookie)); |
684 |
} |
685 |
|
686 |
static char * prepareRequest(struct bootpRequest * breq, |
687 |
int sock, char * device, time_t startTime) { |
688 |
#ifdef NOTHURD |
689 |
struct ifreq req; |
690 |
#endif |
691 |
int i; |
692 |
|
693 |
memset(breq, 0, sizeof(*breq)); |
694 |
|
695 |
breq->opcode = BOOTP_OPCODE_REQUEST; |
696 |
|
697 |
#ifdef NOTHURD |
698 |
strcpy(req.ifr_name, device); |
699 |
if (ioctl(sock, SIOCGIFHWADDR, &req)) |
700 |
return perrorstr("SIOCSIFHWADDR"); |
701 |
|
702 |
breq->hw = 1; /* ethernet */ |
703 |
breq->hwlength = IFHWADDRLEN; |
704 |
memcpy(breq->hwaddr, req.ifr_hwaddr.sa_data, IFHWADDRLEN); |
705 |
#else |
706 |
breq->hw = 1; /* ethernet */ |
707 |
breq->hwlength = IFHWADDRLEN; |
708 |
memcpy(breq->hwaddr, ethernet_address, IFHWADDRLEN); |
709 |
#endif |
710 |
|
711 |
/* we should use something random here, but I don't want to start using |
712 |
stuff from the math library */ |
713 |
breq->id = time(NULL); |
714 |
for (i = 0; i < IFHWADDRLEN; i++) |
715 |
breq->id ^= breq->hwaddr[i] << (8 * (i % 4)); |
716 |
|
717 |
breq->hopcount = 0; |
718 |
breq->secs = time(NULL) - startTime; |
719 |
|
720 |
initVendorCodes(breq); |
721 |
|
722 |
return NULL; |
723 |
} |
724 |
|
725 |
static unsigned int verifyChecksum(void * buf, int length, void * buf2, |
726 |
int length2) { |
727 |
unsigned int csum; |
728 |
unsigned short * sp; |
729 |
|
730 |
csum = 0; |
731 |
for (sp = (unsigned short *) buf; length > 0; (length -= 2), sp++) |
732 |
csum += *sp; |
733 |
|
734 |
/* this matches rfc 1071, but not Steven's */ |
735 |
if (length) |
736 |
csum += *((unsigned char *) sp); |
737 |
|
738 |
for (sp = (unsigned short *) buf2; length2 > 0; (length2 -= 2), sp++) |
739 |
csum += *sp; |
740 |
|
741 |
/* this matches rfc 1071, but not Steven's */ |
742 |
if (length) |
743 |
csum += *((unsigned char *) sp); |
744 |
|
745 |
while (csum >> 16) |
746 |
csum = (csum & 0xffff) + (csum >> 16); |
747 |
|
748 |
if (csum!=0x0000 && csum != 0xffff) return 0; else return 1; |
749 |
} |
750 |
|
751 |
void debugbootpRequest(char *name, struct bootpRequest *breq) { |
752 |
char vendor[28], vendor2[28]; |
753 |
int i; |
754 |
struct in_addr address; |
755 |
unsigned char *vndptr; |
756 |
unsigned char option, length; |
757 |
|
758 |
syslog (LOG_DEBUG, "%s: opcode: %i", name, breq->opcode); |
759 |
syslog (LOG_DEBUG, "%s: hw: %i", name, breq->hw); |
760 |
syslog (LOG_DEBUG, "%s: hwlength: %i", name, breq->hwlength); |
761 |
syslog (LOG_DEBUG, "%s: hopcount: %i", name, breq->hopcount); |
762 |
syslog (LOG_DEBUG, "%s: id: 0x%8x", name, breq->id); |
763 |
syslog (LOG_DEBUG, "%s: secs: %i", name, breq->secs); |
764 |
syslog (LOG_DEBUG, "%s: flags: 0x%4x", name, breq->flags); |
765 |
|
766 |
address.s_addr = breq->ciaddr; |
767 |
syslog (LOG_DEBUG, "%s: ciaddr: %s", name, inet_ntoa (address)); |
768 |
|
769 |
address.s_addr = breq->yiaddr; |
770 |
syslog (LOG_DEBUG, "%s: yiaddr: %s", name, inet_ntoa (address)); |
771 |
|
772 |
address.s_addr = breq->server_ip; |
773 |
syslog (LOG_DEBUG, "%s: server_ip: %s", name, inet_ntoa (address)); |
774 |
|
775 |
address.s_addr = breq->bootp_gw_ip; |
776 |
syslog (LOG_DEBUG, "%s: bootp_gw_ip: %s", name, inet_ntoa (address)); |
777 |
|
778 |
syslog (LOG_DEBUG, "%s: hwaddr: %s", name, breq->hwaddr); |
779 |
syslog (LOG_DEBUG, "%s: servername: %s", name, breq->servername); |
780 |
syslog (LOG_DEBUG, "%s: bootfile: %s", name, breq->bootfile); |
781 |
|
782 |
vndptr = breq->vendor; |
783 |
sprintf (vendor, "0x%2x 0x%2x 0x%2x 0x%2x", *vndptr++, *vndptr++, *vndptr++, *vndptr++); |
784 |
syslog (LOG_DEBUG, "%s: vendor: %s", name, vendor); |
785 |
|
786 |
|
787 |
for (; (void *) vndptr < (void *) breq->vendor + DHCP_VENDOR_LENGTH;) |
788 |
{ |
789 |
option = *vndptr++; |
790 |
if (option == 0xFF) |
791 |
{ |
792 |
sprintf (vendor, "0x%2x", option); |
793 |
vndptr = breq->vendor + DHCP_VENDOR_LENGTH; |
794 |
} |
795 |
else if (option == 0x00) |
796 |
{ |
797 |
for (i = 1; *vndptr == 0x00; i++, vndptr++); |
798 |
sprintf (vendor, "0x%2x x %i", option, i); |
799 |
} |
800 |
else |
801 |
{ |
802 |
length = *vndptr++; |
803 |
sprintf (vendor, "%3u %3u", option, length); |
804 |
for (i = 0; i < length; i++) |
805 |
{ |
806 |
if (strlen (vendor) > 22) |
807 |
{ |
808 |
syslog (LOG_DEBUG, "%s: vendor: %s", name, vendor); |
809 |
strcpy (vendor, "++++++"); |
810 |
} |
811 |
snprintf (vendor2, 27, "%s 0x%2x", vendor, *vndptr++); |
812 |
strcpy (vendor, vendor2); |
813 |
|
814 |
} |
815 |
} |
816 |
|
817 |
syslog (LOG_DEBUG, "%s: vendor: %s", name, vendor); |
818 |
} |
819 |
|
820 |
return; |
821 |
|
822 |
} |
823 |
|
824 |
static char * handleTransaction(int s, struct overrideInfo * override, |
825 |
struct bootpRequest * breq, |
826 |
struct bootpRequest * bresp, |
827 |
struct sockaddr_in * serverAddr, |
828 |
struct sockaddr_in * respondant, |
829 |
int useBootpPacket, int dhcpResponseType) { |
830 |
struct timeval tv; |
831 |
fd_set readfs; |
832 |
int i, j; |
833 |
struct sockaddr_pkt tmpAddress; |
834 |
int gotit = 0; |
835 |
int tries; |
836 |
int nextTimeout = 2; |
837 |
time_t timeoutTime; |
838 |
int sin; |
839 |
int resend = 1; |
840 |
struct ethhdr; |
841 |
char ethPacket[ETH_FRAME_LEN]; |
842 |
struct iphdr * ipHdr; |
843 |
struct udphdr * udpHdr; |
844 |
struct psuedohUdpHeader pHdr; |
845 |
time_t start = time(NULL); |
846 |
|
847 |
debugbootpRequest("breq", breq); |
848 |
|
849 |
if (!override) { |
850 |
override = alloca(sizeof(*override)); |
851 |
initOverride(override); |
852 |
} |
853 |
|
854 |
tries = override->numRetries + 1; |
855 |
|
856 |
sin = socket(AF_PACKET, SOCK_DGRAM, ntohs(ETH_P_IP)); |
857 |
if (sin < 0) { |
858 |
return strerror(errno); |
859 |
} |
860 |
|
861 |
while (!gotit && tries) { |
862 |
i = sizeof(*breq); |
863 |
if (useBootpPacket) |
864 |
i -= (DHCP_VENDOR_LENGTH - BOOTP_VENDOR_LENGTH); |
865 |
|
866 |
if (resend) { |
867 |
if (sendto(s, breq, i, 0, (struct sockaddr *) serverAddr, |
868 |
sizeof(*serverAddr)) != i) { |
869 |
close(sin); |
870 |
return perrorstr("sendto"); |
871 |
} |
872 |
|
873 |
tries--; |
874 |
nextTimeout *= 2; |
875 |
|
876 |
switch (time(NULL) & 4) { |
877 |
case 0: if (nextTimeout >= 2) nextTimeout--; break; |
878 |
case 1: nextTimeout++; break; |
879 |
} |
880 |
|
881 |
timeoutTime = time(NULL) + nextTimeout; |
882 |
i = override->timeout + start; |
883 |
if (timeoutTime > i) timeoutTime = i; |
884 |
|
885 |
resend = 0; |
886 |
} |
887 |
|
888 |
if (dhcpResponseType == NORESPONSE) { |
889 |
close(sin); |
890 |
return NULL; |
891 |
} |
892 |
|
893 |
tv.tv_usec = 0; |
894 |
tv.tv_sec = timeoutTime - time(NULL); |
895 |
if (timeoutTime < time(NULL)) { |
896 |
tries = 0; |
897 |
continue; |
898 |
} |
899 |
|
900 |
FD_ZERO(&readfs); |
901 |
FD_SET(sin, &readfs); |
902 |
switch ((select(sin + 1, &readfs, NULL, NULL, &tv))) { |
903 |
case 0: |
904 |
resend = 1; |
905 |
break; |
906 |
|
907 |
case 1: |
908 |
i = sizeof(tmpAddress); |
909 |
if ((j = recvfrom(sin, ethPacket, sizeof(ethPacket), 0, |
910 |
(struct sockaddr *) &tmpAddress, &i)) < 0) |
911 |
return perrorstr("recvfrom"); |
912 |
|
913 |
/* We need to do some basic sanity checking of the header */ |
914 |
if (j < (sizeof(*ipHdr) + sizeof(*udpHdr))) continue; |
915 |
|
916 |
ipHdr = (void *) ethPacket; |
917 |
if (!verifyChecksum(NULL, 0, ipHdr, sizeof(*ipHdr))) |
918 |
continue; |
919 |
|
920 |
if (ntohs(ipHdr->tot_len) > j) |
921 |
continue; |
922 |
j = ntohs(ipHdr->tot_len); |
923 |
|
924 |
if (ipHdr->protocol != IPPROTO_UDP) continue; |
925 |
|
926 |
udpHdr = (void *) (ethPacket + sizeof(*ipHdr)); |
927 |
pHdr.source = ipHdr->saddr; |
928 |
pHdr.dest = ipHdr->daddr; |
929 |
pHdr.zero = 0; |
930 |
pHdr.protocol = ipHdr->protocol; |
931 |
pHdr.len = udpHdr->len; |
932 |
|
933 |
/* |
934 |
egcs bugs make this problematic |
935 |
|
936 |
if (udpHdr->check && !verifyChecksum(&pHdr, sizeof(pHdr), |
937 |
udpHdr, j - sizeof(*ipHdr))) |
938 |
continue; |
939 |
*/ |
940 |
|
941 |
if (ntohs(udpHdr->source) != BOOTP_SERVER_PORT) |
942 |
continue; |
943 |
if (ntohs(udpHdr->dest) != BOOTP_CLIENT_PORT) |
944 |
continue; |
945 |
/* Go on with this packet; it looks sane */ |
946 |
|
947 |
/* Originally copied sizeof (*bresp) - this is a security |
948 |
problem due to a potential underflow of the source |
949 |
buffer. Also, it trusted that the packet was properly |
950 |
0xFF terminated, which is not true in the case of the |
951 |
DHCP server on Cisco 800 series ISDN router. */ |
952 |
|
953 |
memset (bresp, 0xFF, sizeof (*bresp)); |
954 |
memcpy (bresp, (char *) udpHdr + sizeof (*udpHdr), j - sizeof (*ipHdr) - sizeof (*udpHdr)); |
955 |
|
956 |
/* sanity checks */ |
957 |
if (bresp->id != breq->id) continue; |
958 |
if (bresp->opcode != BOOTP_OPCODE_REPLY) continue; |
959 |
if (bresp->hwlength != breq->hwlength) continue; |
960 |
if (memcmp(bresp->hwaddr, breq->hwaddr, bresp->hwlength)) continue; |
961 |
i = dhcpMessageType(bresp); |
962 |
if (!(i == -1 && useBootpPacket) && (i != dhcpResponseType)) |
963 |
continue; |
964 |
if (memcmp(bresp->vendor, vendCookie, 4)) continue; |
965 |
/* if (respondant) *respondant = tmpAddress; */ |
966 |
gotit = 1; |
967 |
|
968 |
break; |
969 |
|
970 |
default: |
971 |
close(sin); |
972 |
return perrorstr("select"); |
973 |
} |
974 |
} |
975 |
|
976 |
if (!gotit) { |
977 |
close(sin); |
978 |
return _("No DHCP reply received"); |
979 |
} |
980 |
|
981 |
close(sin); |
982 |
|
983 |
debugbootpRequest("bresp", bresp); |
984 |
|
985 |
return NULL; |
986 |
} |
987 |
|
988 |
static void addVendorCode(struct bootpRequest * breq, unsigned char option, |
989 |
unsigned char length, void * data) { |
990 |
unsigned char * chptr; |
991 |
int theOption, theLength; |
992 |
|
993 |
chptr = breq->vendor; |
994 |
chptr += 4; |
995 |
while (*chptr != 0xFF && *chptr != option) { |
996 |
theOption = *chptr++; |
997 |
if (!theOption) continue; |
998 |
theLength = *chptr++; |
999 |
chptr += theLength; |
1000 |
} |
1001 |
|
1002 |
*chptr++ = option; |
1003 |
*chptr++ = length; |
1004 |
memcpy(chptr, data, length); |
1005 |
chptr[length] = 0xff; |
1006 |
} |
1007 |
|
1008 |
static int getVendorCode(struct bootpRequest * bresp, unsigned char option, |
1009 |
void * data) { |
1010 |
unsigned char * chptr; |
1011 |
unsigned int length, theOption; |
1012 |
|
1013 |
chptr = bresp->vendor; |
1014 |
chptr += 4; |
1015 |
while (*chptr != 0xFF && *chptr != option) { |
1016 |
theOption = *chptr++; |
1017 |
if (!theOption) continue; |
1018 |
length = *chptr++; |
1019 |
chptr += length; |
1020 |
} |
1021 |
|
1022 |
if (*chptr++ == 0xff) return 1; |
1023 |
|
1024 |
length = *chptr++; |
1025 |
memcpy(data, chptr, length); |
1026 |
|
1027 |
return 0; |
1028 |
} |
1029 |
|
1030 |
static int createSocket(void) { |
1031 |
struct sockaddr_in clientAddr; |
1032 |
int s; |
1033 |
|
1034 |
s = socket(AF_INET, SOCK_DGRAM, 0); |
1035 |
if (s < 0) |
1036 |
return -1; |
1037 |
|
1038 |
memset(&clientAddr.sin_addr, 0, sizeof(&clientAddr.sin_addr)); |
1039 |
clientAddr.sin_family = AF_INET; |
1040 |
clientAddr.sin_port = htons(BOOTP_CLIENT_PORT); /* bootp client */ |
1041 |
|
1042 |
if (bind(s, (struct sockaddr *) &clientAddr, sizeof(clientAddr))) { |
1043 |
close(s); |
1044 |
return -1; |
1045 |
} |
1046 |
|
1047 |
return s; |
1048 |
} |
1049 |
|
1050 |
static int dhcpRelease(struct intfInfo * intf) { |
1051 |
struct bootpRequest breq, bresp; |
1052 |
unsigned char messageType; |
1053 |
struct sockaddr_in serverAddr; |
1054 |
char * chptr; |
1055 |
int s; |
1056 |
|
1057 |
if (!(intf->set & INTFINFO_HAS_LEASE)) { |
1058 |
disableInterface(intf->device); |
1059 |
syslog(LOG_INFO, "disabling interface %s", intf->device); |
1060 |
|
1061 |
return 0; |
1062 |
} |
1063 |
|
1064 |
if ((s = createSocket()) < 0) return 1; |
1065 |
|
1066 |
if ((chptr = prepareRequest(&breq, s, intf->device, time(NULL)))) { |
1067 |
close(s); |
1068 |
while (1) { |
1069 |
disableInterface(intf->device); |
1070 |
return 0; |
1071 |
} |
1072 |
} |
1073 |
|
1074 |
messageType = DHCP_TYPE_RELEASE; |
1075 |
addVendorCode(&breq, DHCP_OPTION_TYPE, 1, &messageType); |
1076 |
memcpy(&breq.ciaddr, &intf->ip, sizeof(breq.ciaddr)); |
1077 |
|
1078 |
serverAddr.sin_family = AF_INET; |
1079 |
serverAddr.sin_port = htons(BOOTP_SERVER_PORT); /* bootp server */ |
1080 |
serverAddr.sin_addr = intf->bootServer; |
1081 |
|
1082 |
if (!handleTransaction(s, NULL, &breq, &bresp, &serverAddr, NULL, 0, |
1083 |
NORESPONSE)) { |
1084 |
disableInterface(intf->device); |
1085 |
close(s); |
1086 |
return 0; |
1087 |
} |
1088 |
|
1089 |
disableInterface(intf->device); |
1090 |
close(s); |
1091 |
|
1092 |
syslog(LOG_INFO, "disabling interface %s", intf->device); |
1093 |
|
1094 |
return 1; |
1095 |
} |
1096 |
|
1097 |
/* This is somewhat broken. We try only to renew the lease. If we fail, |
1098 |
we don't try to completely rebind. This doesn't follow the DHCP spec, |
1099 |
but for the install it should be a reasonable compromise. */ |
1100 |
static int dhcpRenew(struct intfInfo * intf) { |
1101 |
struct bootpRequest breq, bresp; |
1102 |
unsigned char messageType; |
1103 |
struct sockaddr_in serverAddr; |
1104 |
char * chptr; |
1105 |
int s; |
1106 |
int i; |
1107 |
|
1108 |
s = createSocket(); |
1109 |
|
1110 |
if ((chptr = prepareRequest(&breq, s, intf->device, time(NULL)))) { |
1111 |
close(s); |
1112 |
while (1); /* problem */ |
1113 |
} |
1114 |
|
1115 |
messageType = DHCP_TYPE_REQUEST; |
1116 |
addVendorCode(&breq, DHCP_OPTION_TYPE, 1, &messageType); |
1117 |
memcpy(&breq.ciaddr, &intf->ip, sizeof(breq.ciaddr)); |
1118 |
|
1119 |
i = htonl(intf->reqLease); |
1120 |
addVendorCode(&breq, DHCP_OPTION_LEASE, 4, &i); |
1121 |
|
1122 |
serverAddr.sin_family = AF_INET; |
1123 |
serverAddr.sin_port = htons(BOOTP_SERVER_PORT); /* bootp server */ |
1124 |
serverAddr.sin_addr = intf->bootServer; |
1125 |
|
1126 |
if (handleTransaction(s, NULL, &breq, &bresp, &serverAddr, NULL, 0, |
1127 |
DHCP_TYPE_ACK)) { |
1128 |
close(s); |
1129 |
return 1; |
1130 |
} |
1131 |
|
1132 |
parseLease(&bresp, intf); |
1133 |
|
1134 |
syslog(LOG_INFO, "renewed lease for interface %s", intf->device); |
1135 |
|
1136 |
close(s); |
1137 |
return 0; |
1138 |
} |
1139 |
|
1140 |
static void parseLease(struct bootpRequest * bresp, struct intfInfo * intf) { |
1141 |
int lease; |
1142 |
time_t now; |
1143 |
|
1144 |
intf->set &= INTFINFO_HAS_LEASE; |
1145 |
if (getVendorCode(bresp, DHCP_OPTION_LEASE, &lease)) |
1146 |
return; |
1147 |
|
1148 |
lease = ntohl(lease); |
1149 |
|
1150 |
if (lease && lease != 0xffffffff) { |
1151 |
now = time(NULL); |
1152 |
intf->set |= INTFINFO_HAS_LEASE; |
1153 |
intf->leaseExpiration = now + lease; |
1154 |
intf->renewAt = now + (7 * lease / 8); |
1155 |
} |
1156 |
} |
1157 |
|
1158 |
char * readSearchPath(void) { |
1159 |
int fd; |
1160 |
struct stat sb; |
1161 |
char * buf; |
1162 |
char * start; |
1163 |
|
1164 |
fd = open("/etc/resolv.conf", O_RDONLY); |
1165 |
if (fd < 0) return NULL; |
1166 |
|
1167 |
fstat(fd, &sb); |
1168 |
buf = alloca(sb.st_size + 2); |
1169 |
if (read(fd, buf, sb.st_size) != sb.st_size) return NULL; |
1170 |
buf[sb.st_size] = '\n'; |
1171 |
buf[sb.st_size + 1] = '\0'; |
1172 |
close(fd); |
1173 |
|
1174 |
start = buf; |
1175 |
while (start && *start) { |
1176 |
while (isspace(*start) && (*start != '\n')) start++; |
1177 |
if (*start == '\n') { |
1178 |
start++; |
1179 |
continue; |
1180 |
} |
1181 |
|
1182 |
if (!strncmp("search", start, 6) && isspace(start[6])) { |
1183 |
start += 6; |
1184 |
while (isspace(*start) && *start != '\n') start++; |
1185 |
if (*start == '\n') return NULL; |
1186 |
|
1187 |
buf = strchr(start, '\n'); |
1188 |
*buf = '\0'; |
1189 |
return strdup(start); |
1190 |
} |
1191 |
} |
1192 |
|
1193 |
return NULL; |
1194 |
} |
1195 |
|
1196 |
static void createResolvConf(struct intfInfo * intf, char * domain, |
1197 |
int isSearchPath) { |
1198 |
FILE * f; |
1199 |
int i; |
1200 |
char * chptr; |
1201 |
|
1202 |
/* force a reread of /etc/resolv.conf if we need it again */ |
1203 |
res_close(); |
1204 |
|
1205 |
if (!domain) { |
1206 |
domain = readSearchPath(); |
1207 |
if (domain) { |
1208 |
chptr = alloca(strlen(domain) + 1); |
1209 |
strcpy(chptr, domain); |
1210 |
free(domain); |
1211 |
domain = chptr; |
1212 |
isSearchPath = 1; |
1213 |
} |
1214 |
} |
1215 |
|
1216 |
f = fopen("/etc/resolv.conf", "w"); |
1217 |
if (!f) { |
1218 |
syslog(LOG_ERR, "cannot create /etc/resolv.conf: %s\n", |
1219 |
strerror(errno)); |
1220 |
return; |
1221 |
} |
1222 |
|
1223 |
if (domain && isSearchPath) { |
1224 |
fprintf(f, "search %s\n", domain); |
1225 |
} else if (domain) { |
1226 |
fprintf(f, "search"); |
1227 |
chptr = domain; |
1228 |
do { |
1229 |
fprintf(f, " %s", chptr); |
1230 |
chptr = strchr(chptr, '.'); |
1231 |
if (chptr) { |
1232 |
chptr++; |
1233 |
if (!strchr(chptr, '.')) |
1234 |
chptr = NULL; |
1235 |
} |
1236 |
} while (chptr); |
1237 |
|
1238 |
fprintf(f, "\n"); |
1239 |
} |
1240 |
|
1241 |
for (i = 0; i < intf->numDns; i++) |
1242 |
fprintf(f, "nameserver %s\n", inet_ntoa(intf->dnsServers[i])); |
1243 |
|
1244 |
fclose(f); |
1245 |
|
1246 |
/* force a reread of /etc/resolv.conf */ |
1247 |
endhostent(); |
1248 |
} |
1249 |
|
1250 |
void setupDns(struct intfInfo * intf, struct overrideInfo * override) { |
1251 |
char * hn, * dn = NULL; |
1252 |
struct hostent * he; |
1253 |
|
1254 |
if (override->flags & OVERRIDE_FLAG_NODNS) { |
1255 |
return; |
1256 |
} |
1257 |
|
1258 |
if (override->searchPath) { |
1259 |
createResolvConf(intf, override->searchPath, 1); |
1260 |
return; |
1261 |
} |
1262 |
|
1263 |
if (intf->set & NETINFO_HAS_DNS) { |
1264 |
if (!(intf->set & NETINFO_HAS_DOMAIN)) { |
1265 |
if (intf->set & NETINFO_HAS_HOSTNAME) { |
1266 |
hn = intf->hostname; |
1267 |
} else { |
1268 |
createResolvConf(intf, NULL, 0); |
1269 |
|
1270 |
he = gethostbyaddr((char *) &intf->ip, sizeof(intf->ip), |
1271 |
AF_INET); |
1272 |
if (he) { |
1273 |
hn = he->h_name; |
1274 |
} else { |
1275 |
hn = NULL; |
1276 |
} |
1277 |
} |
1278 |
|
1279 |
if (hn) { |
1280 |
dn = strchr(hn, '.'); |
1281 |
if (dn) |
1282 |
dn++; |
1283 |
} |
1284 |
} else { |
1285 |
dn = intf->domain; |
1286 |
} |
1287 |
|
1288 |
createResolvConf(intf, dn, 0); |
1289 |
} |
1290 |
} |
1291 |
|
1292 |
static char * doDhcp(char * device, int flags, int reqLease, |
1293 |
char * reqHostname, struct intfInfo * intf, |
1294 |
struct overrideInfo * override) { |
1295 |
int s, i; |
1296 |
struct sockaddr_in serverAddr; |
1297 |
struct sockaddr_in clientAddr; |
1298 |
struct sockaddr_in broadcastAddr; |
1299 |
struct bootpRequest breq, bresp; |
1300 |
struct bootpRequest protoReq; |
1301 |
unsigned char * chptr; |
1302 |
unsigned char messageType; |
1303 |
time_t startTime = time(NULL); |
1304 |
int true = 1; |
1305 |
char optionsRequested[50]; |
1306 |
int numOptions; |
1307 |
short aShort; |
1308 |
|
1309 |
memset(intf, 0, sizeof(*intf)); |
1310 |
strcpy(intf->device, device); |
1311 |
intf->reqLease = reqLease; |
1312 |
intf->set |= INTFINFO_HAS_REQLEASE; |
1313 |
|
1314 |
s = socket(AF_INET, SOCK_DGRAM, 0); |
1315 |
if (s < 0) { |
1316 |
return perrorstr("socket"); |
1317 |
} |
1318 |
|
1319 |
if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, &true, sizeof(true))) { |
1320 |
close(s); |
1321 |
return perrorstr("setsockopt"); |
1322 |
} |
1323 |
|
1324 |
if (flags & DHCP_FLAG_NOCONFIG) { |
1325 |
if ((chptr = getInterfaceInfo(intf, s))) { |
1326 |
close(s); |
1327 |
return chptr; |
1328 |
} |
1329 |
} else if ((chptr = prepareInterface(intf, s))) { |
1330 |
close(s); |
1331 |
return chptr; |
1332 |
} |
1333 |
|
1334 |
if ((chptr = prepareRequest(&breq, s, intf->device, startTime))) { |
1335 |
close(s); |
1336 |
disableInterface(intf->device); |
1337 |
return chptr; |
1338 |
} |
1339 |
|
1340 |
messageType = DHCP_TYPE_DISCOVER; |
1341 |
addVendorCode(&breq, DHCP_OPTION_TYPE, 1, &messageType); |
1342 |
|
1343 |
memset(&clientAddr.sin_addr, 0, sizeof(&clientAddr.sin_addr)); |
1344 |
clientAddr.sin_family = AF_INET; |
1345 |
clientAddr.sin_port = htons(BOOTP_CLIENT_PORT); /* bootp client */ |
1346 |
|
1347 |
if (bind(s, (struct sockaddr *) &clientAddr, sizeof(clientAddr))) { |
1348 |
disableInterface(intf->device); |
1349 |
close(s); |
1350 |
return perrorstr("bind"); |
1351 |
} |
1352 |
|
1353 |
serverAddr.sin_family = AF_INET; |
1354 |
serverAddr.sin_port = htons(BOOTP_SERVER_PORT); /* bootp server */ |
1355 |
|
1356 |
#if 0 |
1357 |
/* seems like a good idea?? */ |
1358 |
if (intf->set & INTFINFO_HAS_BOOTSERVER) |
1359 |
serverAddr.sin_addr = intf->bootServer; |
1360 |
#endif |
1361 |
|
1362 |
broadcastAddr.sin_family = AF_INET; |
1363 |
broadcastAddr.sin_port = htons(BOOTP_SERVER_PORT); |
1364 |
|
1365 |
#if 0 |
1366 |
/* this too! */ |
1367 |
if (intf->set & INTFINFO_HAS_BROADCAST) |
1368 |
broadcastAddr.sin_addr = intf->broadcast; |
1369 |
#endif |
1370 |
|
1371 |
memset(&broadcastAddr.sin_addr, 0xff, |
1372 |
sizeof(broadcastAddr.sin_addr)); /* all 1's broadcast */ |
1373 |
|
1374 |
syslog (LOG_DEBUG, "PUMP: sending discover\n"); |
1375 |
|
1376 |
if ((chptr = handleTransaction(s, override, &breq, &bresp, &broadcastAddr, |
1377 |
NULL, 1, DHCP_TYPE_OFFER))) { |
1378 |
close(s); |
1379 |
disableInterface(intf->device); |
1380 |
return chptr; |
1381 |
} |
1382 |
|
1383 |
/* Otherwise we're in the land of bootp */ |
1384 |
if (dhcpMessageType(&bresp) == DHCP_TYPE_OFFER) { |
1385 |
/* Admittedly, this seems a bit odd. If we find a dhcp server, we |
1386 |
rerun the dhcp discover broadcast, but with the proper option |
1387 |
field this time. This makes me rfc compliant. */ |
1388 |
syslog (LOG_DEBUG, "got dhcp offer\n"); |
1389 |
|
1390 |
initVendorCodes(&breq); |
1391 |
|
1392 |
aShort = ntohs(sizeof(struct bootpRequest)); |
1393 |
addVendorCode(&breq, DHCP_OPTION_MAXSIZE, 2, &aShort); |
1394 |
|
1395 |
numOptions = 0; |
1396 |
optionsRequested[numOptions++] = BOOTP_OPTION_NETMASK; |
1397 |
optionsRequested[numOptions++] = BOOTP_OPTION_GATEWAY; |
1398 |
optionsRequested[numOptions++] = BOOTP_OPTION_DNS; |
1399 |
optionsRequested[numOptions++] = BOOTP_OPTION_DOMAIN; |
1400 |
optionsRequested[numOptions++] = BOOTP_OPTION_BROADCAST; |
1401 |
optionsRequested[numOptions++] = BOOTP_OPTION_HOSTNAME; |
1402 |
addVendorCode(&breq, DHCP_OPTION_OPTIONREQ, numOptions, |
1403 |
optionsRequested); |
1404 |
|
1405 |
if (reqHostname) { |
1406 |
syslog(LOG_DEBUG, "HOSTNAME: requesting %s\n", reqHostname); |
1407 |
addVendorCode(&breq, BOOTP_OPTION_HOSTNAME, strlen(reqHostname), |
1408 |
reqHostname); |
1409 |
} |
1410 |
|
1411 |
i = htonl(intf->reqLease); |
1412 |
addVendorCode(&breq, DHCP_OPTION_LEASE, 4, &i); |
1413 |
|
1414 |
protoReq = breq; |
1415 |
|
1416 |
syslog (LOG_DEBUG, "PUMP: sending second discover"); |
1417 |
|
1418 |
messageType = DHCP_TYPE_DISCOVER; |
1419 |
addVendorCode(&breq, DHCP_OPTION_TYPE, 1, &messageType); |
1420 |
|
1421 |
/* Send another DHCP_REQUEST with the proper option list */ |
1422 |
if ((chptr = handleTransaction(s, override, &breq, &bresp, |
1423 |
&broadcastAddr, NULL, 1, |
1424 |
DHCP_TYPE_OFFER))) { |
1425 |
close(s); |
1426 |
disableInterface(intf->device); |
1427 |
return chptr; |
1428 |
} |
1429 |
|
1430 |
|
1431 |
if (dhcpMessageType(&bresp) != DHCP_TYPE_OFFER) { |
1432 |
close(s); |
1433 |
disableInterface(intf->device); |
1434 |
return "dhcp offer expected"; |
1435 |
} |
1436 |
|
1437 |
syslog (LOG_DEBUG, "PUMP: got an offer"); |
1438 |
|
1439 |
if (getVendorCode(&bresp, DHCP_OPTION_SERVER, &serverAddr.sin_addr)) { |
1440 |
syslog (LOG_DEBUG, "DHCPOFFER didn't include server address"); |
1441 |
intf->bootServer = broadcastAddr.sin_addr; |
1442 |
} |
1443 |
|
1444 |
breq = protoReq; |
1445 |
messageType = DHCP_TYPE_REQUEST; |
1446 |
addVendorCode(&breq, DHCP_OPTION_TYPE, 1, &messageType); |
1447 |
|
1448 |
addVendorCode(&breq, DHCP_OPTION_SERVER, 4, &serverAddr.sin_addr); |
1449 |
addVendorCode(&breq, DHCP_OPTION_REQADDR, 4, &bresp.yiaddr); |
1450 |
|
1451 |
/* why do we need to use the broadcast address here? better reread the |
1452 |
spec! */ |
1453 |
if ((chptr = handleTransaction(s, override, &breq, &bresp, |
1454 |
&broadcastAddr, NULL, 0, |
1455 |
DHCP_TYPE_ACK))) { |
1456 |
close(s); |
1457 |
disableInterface(intf->device); |
1458 |
return chptr; |
1459 |
} |
1460 |
|
1461 |
syslog (LOG_DEBUG, "PUMP: got lease"); |
1462 |
|
1463 |
parseLease(&bresp, intf); |
1464 |
|
1465 |
if (getVendorCode(&bresp, DHCP_OPTION_SERVER, &intf->bootServer)) { |
1466 |
syslog (LOG_DEBUG, "DHCPACK didn't include server address"); |
1467 |
intf->bootServer = broadcastAddr.sin_addr; |
1468 |
} |
1469 |
|
1470 |
intf->set |= INTFINFO_HAS_BOOTSERVER; |
1471 |
} |
1472 |
|
1473 |
parseReply(&bresp, intf); |
1474 |
if (flags & DHCP_FLAG_FORCEHNLOOKUP) |
1475 |
intf->set &= ~(NETINFO_HAS_DOMAIN | NETINFO_HAS_HOSTNAME); |
1476 |
|
1477 |
chptr = setupInterface(intf, s); |
1478 |
if (chptr) { |
1479 |
close(s); |
1480 |
disableInterface(intf->device); |
1481 |
return chptr; |
1482 |
} |
1483 |
|
1484 |
syslog(LOG_INFO, "configured interface %s", intf->device); |
1485 |
|
1486 |
if (intf->set & NETINFO_HAS_GATEWAY) { |
1487 |
chptr = setupDefaultGateway(&intf->gateway, s); |
1488 |
} |
1489 |
|
1490 |
setupDns(intf, override); |
1491 |
|
1492 |
close(s); |
1493 |
|
1494 |
return NULL; |
1495 |
} |
1496 |
|
1497 |
static void runDaemon(int sock, char * configFile) { |
1498 |
int conn; |
1499 |
struct sockaddr_un addr; |
1500 |
int addrLength; |
1501 |
struct command cmd; |
1502 |
struct intfInfo intf[20]; |
1503 |
int numInterfaces = 0; |
1504 |
int i; |
1505 |
int closest; |
1506 |
struct timeval tv; |
1507 |
fd_set fds; |
1508 |
struct overrideInfo * overrides = NULL; |
1509 |
struct overrideInfo emptyOverride, * o; |
1510 |
|
1511 |
readPumpConfig(configFile, &overrides); |
1512 |
if (!overrides) { |
1513 |
overrides = &emptyOverride; |
1514 |
overrides->intf.device[0] = '\0'; |
1515 |
} |
1516 |
|
1517 |
while (1) { |
1518 |
FD_ZERO(&fds); |
1519 |
FD_SET(sock, &fds); |
1520 |
|
1521 |
tv.tv_sec = tv.tv_usec = 0; |
1522 |
closest = -1; |
1523 |
if (numInterfaces) { |
1524 |
for (i = 0; i < numInterfaces; i++) |
1525 |
if ((intf[i].set & INTFINFO_HAS_LEASE) && |
1526 |
(closest == -1 || |
1527 |
(intf[closest].renewAt > intf[i].renewAt))) |
1528 |
closest = i; |
1529 |
if (closest != -1) { |
1530 |
tv.tv_sec = intf[closest].renewAt - time(NULL); |
1531 |
if (tv.tv_sec <= 0) { |
1532 |
dhcpRenew(intf + closest); |
1533 |
continue; |
1534 |
} |
1535 |
} |
1536 |
} |
1537 |
|
1538 |
if (select(sock + 1, &fds, NULL, NULL, |
1539 |
closest != -1 ? &tv : NULL) > 0) { |
1540 |
conn = accept(sock, &addr, &addrLength); |
1541 |
|
1542 |
if (read(conn, &cmd, sizeof(cmd)) != sizeof(cmd)) { |
1543 |
close(conn); |
1544 |
continue; |
1545 |
} |
1546 |
|
1547 |
switch (cmd.type) { |
1548 |
case CMD_DIE: |
1549 |
for (i = 0; i < numInterfaces; i++) |
1550 |
dhcpRelease(intf + i); |
1551 |
|
1552 |
syslog(LOG_INFO, "terminating at root's request"); |
1553 |
|
1554 |
cmd.type = CMD_RESULT; |
1555 |
cmd.u.result = 0; |
1556 |
write(conn, &cmd, sizeof(cmd)); |
1557 |
exit(0); |
1558 |
|
1559 |
case CMD_STARTIFACE: |
1560 |
o = overrides; |
1561 |
while (*o->intf.device && |
1562 |
strcmp(o->intf.device, cmd.u.start.device)) { |
1563 |
o++; |
1564 |
} |
1565 |
if (!*o->intf.device) o = overrides; |
1566 |
|
1567 |
if (doDhcp(cmd.u.start.device, |
1568 |
cmd.u.start.flags, cmd.u.start.reqLease, |
1569 |
cmd.u.start.reqHostname[0] ? |
1570 |
cmd.u.start.reqHostname : NULL, |
1571 |
intf + numInterfaces, o)) { |
1572 |
cmd.u.result = 1; |
1573 |
} else { |
1574 |
cmd.u.result = 0; |
1575 |
numInterfaces++; |
1576 |
} |
1577 |
break; |
1578 |
|
1579 |
case CMD_FORCERENEW: |
1580 |
for (i = 0; i < numInterfaces; i++) |
1581 |
if (!strcmp(intf[i].device, cmd.u.renew.device)) break; |
1582 |
if (i == numInterfaces) |
1583 |
cmd.u.result = RESULT_UNKNOWNIFACE; |
1584 |
else |
1585 |
cmd.u.result = dhcpRenew(intf + i); |
1586 |
break; |
1587 |
|
1588 |
case CMD_STOPIFACE: |
1589 |
for (i = 0; i < numInterfaces; i++) |
1590 |
if (!strcmp(intf[i].device, cmd.u.stop.device)) break; |
1591 |
if (i == numInterfaces) |
1592 |
cmd.u.result = RESULT_UNKNOWNIFACE; |
1593 |
else { |
1594 |
cmd.u.result = dhcpRelease(intf + i); |
1595 |
if (numInterfaces == 1) { |
1596 |
cmd.type = CMD_RESULT; |
1597 |
write(conn, &cmd, sizeof(cmd)); |
1598 |
|
1599 |
syslog(LOG_INFO, "terminating as there are no " |
1600 |
"more devices under management"); |
1601 |
|
1602 |
exit(0); |
1603 |
} |
1604 |
|
1605 |
intf[i] = intf[numInterfaces - 1]; |
1606 |
numInterfaces--; |
1607 |
} |
1608 |
break; |
1609 |
|
1610 |
case CMD_REQSTATUS: |
1611 |
for (i = 0; i < numInterfaces; i++) |
1612 |
if (!strcmp(intf[i].device, cmd.u.stop.device)) break; |
1613 |
if (i == numInterfaces) { |
1614 |
cmd.u.result = RESULT_UNKNOWNIFACE; |
1615 |
} else { |
1616 |
cmd.type = CMD_STATUS; |
1617 |
cmd.u.status.intf = intf[i]; |
1618 |
if (intf[i].set & NETINFO_HAS_HOSTNAME) |
1619 |
strncpy(cmd.u.status.hostname, |
1620 |
intf->hostname, sizeof(cmd.u.status.hostname)); |
1621 |
cmd.u.status.hostname[sizeof(cmd.u.status.hostname)] = '\0'; |
1622 |
|
1623 |
if (intf[i].set & NETINFO_HAS_DOMAIN) |
1624 |
strncpy(cmd.u.status.domain, |
1625 |
intf->domain, sizeof(cmd.u.status.domain)); |
1626 |
cmd.u.status.domain[sizeof(cmd.u.status.domain) - 1] = '\0'; |
1627 |
|
1628 |
if (intf[i].set & INTFINFO_HAS_BOOTFILE) |
1629 |
strncpy(cmd.u.status.bootFile, |
1630 |
intf->bootFile, sizeof(cmd.u.status.bootFile)); |
1631 |
cmd.u.status.bootFile[sizeof(cmd.u.status.bootFile) - 1] = |
1632 |
'\0'; |
1633 |
} |
1634 |
|
1635 |
case CMD_STATUS: |
1636 |
case CMD_RESULT: |
1637 |
/* can't happen */ |
1638 |
break; |
1639 |
} |
1640 |
|
1641 |
if (cmd.type != CMD_STATUS) cmd.type = CMD_RESULT; |
1642 |
write(conn, &cmd, sizeof(cmd)); |
1643 |
|
1644 |
close(conn); |
1645 |
} |
1646 |
} |
1647 |
|
1648 |
exit(0); |
1649 |
} |
1650 |
|
1651 |
static int openControlSocket(char * configFile) { |
1652 |
struct sockaddr_un addr; |
1653 |
int sock; |
1654 |
size_t addrLength; |
1655 |
pid_t child; |
1656 |
int status; |
1657 |
|
1658 |
if ((sock = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) |
1659 |
return -1; |
1660 |
|
1661 |
addr.sun_family = AF_UNIX; |
1662 |
strcpy(addr.sun_path, CONTROLSOCKET); |
1663 |
addrLength = sizeof(addr.sun_family) + strlen(addr.sun_path); |
1664 |
|
1665 |
if (!connect(sock, (struct sockaddr *) &addr, addrLength)) |
1666 |
return sock; |
1667 |
|
1668 |
if (errno != ENOENT && errno != ECONNREFUSED) { |
1669 |
fprintf(stderr, "failed to connect to %s: %s\n", CONTROLSOCKET, |
1670 |
strerror(errno)); |
1671 |
close(sock); |
1672 |
return -1; |
1673 |
} |
1674 |
|
1675 |
if (!(child = fork())) { |
1676 |
close(sock); |
1677 |
|
1678 |
if ((sock = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) { |
1679 |
fprintf(stderr, "failed to create socket: %s\n", strerror(errno)); |
1680 |
exit(1); |
1681 |
} |
1682 |
|
1683 |
unlink(CONTROLSOCKET); |
1684 |
umask(077); |
1685 |
if (bind(sock, (struct sockaddr *) &addr, addrLength)) { |
1686 |
fprintf(stderr, "bind to %s failed: %s\n", CONTROLSOCKET, |
1687 |
strerror(errno)); |
1688 |
exit(1); |
1689 |
} |
1690 |
umask(033); |
1691 |
|
1692 |
listen(sock, 5); |
1693 |
|
1694 |
if (fork()) _exit(0); |
1695 |
|
1696 |
close(0); |
1697 |
close(1); |
1698 |
close(2); |
1699 |
|
1700 |
openlog("pumpd", LOG_PID, LOG_DAEMON); |
1701 |
{ |
1702 |
time_t t; |
1703 |
|
1704 |
t = time(NULL); |
1705 |
syslog(LOG_INFO, "starting at %s\n", ctime(&t)); |
1706 |
} |
1707 |
|
1708 |
runDaemon(sock, configFile); |
1709 |
} |
1710 |
|
1711 |
waitpid(child, &status, 0); |
1712 |
if (!WIFEXITED(status) || WEXITSTATUS(status)) |
1713 |
return -1; |
1714 |
|
1715 |
if (!connect(sock, (struct sockaddr *) &addr, addrLength)) |
1716 |
return sock; |
1717 |
|
1718 |
fprintf(stderr, "failed to connect to %s: %s\n", CONTROLSOCKET, |
1719 |
strerror(errno)); |
1720 |
|
1721 |
return 0; |
1722 |
} |
1723 |
|
1724 |
void printStatus(struct intfInfo i, char * hostname, char * domain, |
1725 |
char * bootFile) { |
1726 |
int j; |
1727 |
|
1728 |
printf("Device %s\n", i.device); |
1729 |
printf("\tIP: %s\n", inet_ntoa(i.ip)); |
1730 |
printf("\tNetmask: %s\n", inet_ntoa(i.netmask)); |
1731 |
printf("\tBroadcast: %s\n", inet_ntoa(i.broadcast)); |
1732 |
printf("\tNetwork: %s\n", inet_ntoa(i.network)); |
1733 |
printf("\tBoot server %s\n", inet_ntoa(i.bootServer)); |
1734 |
|
1735 |
if (i.set & NETINFO_HAS_GATEWAY) |
1736 |
printf("\tGateway: %s\n", inet_ntoa(i.gateway)); |
1737 |
|
1738 |
if (i.set & INTFINFO_HAS_BOOTFILE) |
1739 |
printf("\tBoot file: %s\n", bootFile); |
1740 |
|
1741 |
if (i.set & NETINFO_HAS_HOSTNAME) |
1742 |
printf("\tHostname: %s\n", hostname); |
1743 |
|
1744 |
if (i.set & NETINFO_HAS_DOMAIN) |
1745 |
printf("\tDomain: %s\n", domain); |
1746 |
|
1747 |
if (i.numDns) { |
1748 |
printf("\tNameservers:"); |
1749 |
for (j = 0; j < i.numDns; j++) |
1750 |
printf(" %s", inet_ntoa(i.dnsServers[j])); |
1751 |
printf("\n"); |
1752 |
} |
1753 |
|
1754 |
if (i.set & INTFINFO_HAS_LEASE) { |
1755 |
printf("\tRenewal time: %s", ctime(&i.renewAt)); |
1756 |
printf("\tExpiration time: %s", ctime(&i.leaseExpiration)); |
1757 |
} |
1758 |
} |
1759 |
|
1760 |
int main (int argc, char ** argv) { |
1761 |
char * device = "eth0"; |
1762 |
char * hostname = ""; |
1763 |
poptContext optCon; |
1764 |
int rc; |
1765 |
int test = 0; |
1766 |
int flags = 0; |
1767 |
int lease = 6; |
1768 |
int killDaemon = 0; |
1769 |
int release = 0, renew = 0, status = 0, lookupHostname = 0; |
1770 |
struct command cmd, response; |
1771 |
char * configFile = "/etc/pump.conf"; |
1772 |
struct overrideInfo * overrides; |
1773 |
int cont; |
1774 |
struct poptOption options[] = { |
1775 |
{ "config-file", 'c', POPT_ARG_STRING, &configFile, 0, |
1776 |
N_("Configuration file to use instead of " |
1777 |
"/etc/pump.conf") }, |
1778 |
{ "hostname", 'h', POPT_ARG_STRING, &hostname, 0, |
1779 |
N_("Hostname to request"), N_("hostname") }, |
1780 |
{ "interface", 'i', POPT_ARG_STRING, &device, 0, |
1781 |
N_("Interface to configure (normally eth0)"), |
1782 |
N_("iface") }, |
1783 |
{ "kill", 'k', POPT_ARG_NONE, &killDaemon, 0, |
1784 |
N_("Kill daemon (and disable all interfaces)"), NULL }, |
1785 |
{ "lease", 'l', POPT_ARG_INT, &lease, 0, |
1786 |
N_("Lease time to request (in hours)"), N_("hours") }, |
1787 |
{ "lookup-hostname", '\0', POPT_ARG_NONE, &lookupHostname, 0, |
1788 |
N_("Force lookup of hostname") }, |
1789 |
{ "release", 'r', POPT_ARG_NONE, &release, 0, |
1790 |
N_("Release interface"), NULL }, |
1791 |
{ "renew", 'R', POPT_ARG_NONE, &renew, 0, |
1792 |
N_("Force immediate lease renewal"), NULL }, |
1793 |
{ "status", 's', POPT_ARG_NONE, &status, 0, |
1794 |
N_("Display interface status"), NULL }, |
1795 |
/*{ "test", 't', POPT_ARG_NONE, &test, 0, |
1796 |
N_("Don't change the interface configuration or " |
1797 |
"run as a deamon.") },*/ |
1798 |
POPT_AUTOHELP |
1799 |
{ NULL, '\0', 0, NULL, 0 } |
1800 |
}; |
1801 |
|
1802 |
optCon = poptGetContext(PROGNAME, argc, argv, options,0); |
1803 |
poptReadDefaultConfig(optCon, 1); |
1804 |
|
1805 |
if ((rc = poptGetNextOpt(optCon)) < -1) { |
1806 |
fprintf(stderr, _("%s: bad argument %s: %s\n"), PROGNAME, |
1807 |
poptBadOption(optCon, POPT_BADOPTION_NOALIAS), |
1808 |
poptStrerror(rc)); |
1809 |
return 1; |
1810 |
} |
1811 |
|
1812 |
if (poptGetArg(optCon)) { |
1813 |
fprintf(stderr, _("%s: no extra parameters are expected\n"), PROGNAME); |
1814 |
return 1; |
1815 |
} |
1816 |
|
1817 |
/* make sure the config file is parseable before going on any further */ |
1818 |
if (readPumpConfig(configFile, &overrides)) return 1; |
1819 |
|
1820 |
if (geteuid()) { |
1821 |
fprintf(stderr, _("%s: must be run as root\n"), PROGNAME); |
1822 |
exit(1); |
1823 |
} |
1824 |
|
1825 |
if (test) |
1826 |
flags = DHCP_FLAG_NODAEMON | DHCP_FLAG_NOCONFIG; |
1827 |
if (lookupHostname) |
1828 |
flags |= DHCP_FLAG_FORCEHNLOOKUP; |
1829 |
|
1830 |
cont = openControlSocket(configFile); |
1831 |
if (cont < 0) |
1832 |
exit(1); |
1833 |
|
1834 |
if (killDaemon) { |
1835 |
cmd.type = CMD_DIE; |
1836 |
} else if (status) { |
1837 |
cmd.type = CMD_REQSTATUS; |
1838 |
strcpy(cmd.u.reqstatus.device, device); |
1839 |
} else if (renew) { |
1840 |
cmd.type = CMD_FORCERENEW; |
1841 |
strcpy(cmd.u.renew.device, device); |
1842 |
} else if (release) { |
1843 |
cmd.type = CMD_STOPIFACE; |
1844 |
strcpy(cmd.u.stop.device, device); |
1845 |
} else { |
1846 |
cmd.type = CMD_STARTIFACE; |
1847 |
strcpy(cmd.u.start.device, device); |
1848 |
cmd.u.start.flags = flags; |
1849 |
cmd.u.start.reqLease = lease * 60 * 60; |
1850 |
strcpy(cmd.u.start.reqHostname, hostname); |
1851 |
} |
1852 |
|
1853 |
write(cont, &cmd, sizeof(cmd)); |
1854 |
read(cont, &response, sizeof(response)); |
1855 |
|
1856 |
if (response.type == CMD_RESULT && response.u.result && |
1857 |
cmd.type == CMD_STARTIFACE) { |
1858 |
cont = openControlSocket(configFile); |
1859 |
if (cont < 0) |
1860 |
exit(1); |
1861 |
write(cont, &cmd, sizeof(cmd)); |
1862 |
read(cont, &response, sizeof(response)); |
1863 |
} |
1864 |
|
1865 |
if (response.type == CMD_RESULT) { |
1866 |
if (response.u.result) { |
1867 |
fprintf(stderr, "Operation failed.\n"); |
1868 |
return 1; |
1869 |
} |
1870 |
} else if (response.type == CMD_STATUS) { |
1871 |
printStatus(response.u.status.intf, response.u.status.hostname, |
1872 |
response.u.status.domain, response.u.status.bootFile); |
1873 |
} |
1874 |
|
1875 |
return 0; |
1876 |
} |