summaryrefslogtreecommitdiff
path: root/networking/udhcp
diff options
context:
space:
mode:
authorDenis Vlasenko <vda.linux@googlemail.com>2006-12-27 04:35:04 +0000
committerDenis Vlasenko <vda.linux@googlemail.com>2006-12-27 04:35:04 +0000
commit8d42f86b146871ae4c4cafd3801a85f381249a14 (patch)
treeb963999fc54eddb65f1929b894f868e24851fc9c /networking/udhcp
downloadbusybox-w32-8d42f86b146871ae4c4cafd3801a85f381249a14.tar.gz
busybox-w32-8d42f86b146871ae4c4cafd3801a85f381249a14.tar.bz2
busybox-w32-8d42f86b146871ae4c4cafd3801a85f381249a14.zip
Correcting branch name to be like previous ones
Diffstat (limited to 'networking/udhcp')
-rw-r--r--networking/udhcp/Config.in67
-rw-r--r--networking/udhcp/Kbuild18
-rw-r--r--networking/udhcp/arpping.c114
-rw-r--r--networking/udhcp/clientpacket.c224
-rw-r--r--networking/udhcp/clientsocket.c59
-rw-r--r--networking/udhcp/common.c75
-rw-r--r--networking/udhcp/common.h108
-rw-r--r--networking/udhcp/dhcpc.c509
-rw-r--r--networking/udhcp/dhcpc.h50
-rw-r--r--networking/udhcp/dhcpd.c226
-rw-r--r--networking/udhcp/dhcpd.h190
-rw-r--r--networking/udhcp/dhcprelay.c340
-rw-r--r--networking/udhcp/dumpleases.c74
-rw-r--r--networking/udhcp/files.c395
-rw-r--r--networking/udhcp/leases.c145
-rw-r--r--networking/udhcp/options.c174
-rw-r--r--networking/udhcp/options.h37
-rw-r--r--networking/udhcp/packet.c211
-rw-r--r--networking/udhcp/pidfile.c66
-rw-r--r--networking/udhcp/script.c213
-rw-r--r--networking/udhcp/serverpacket.c261
-rw-r--r--networking/udhcp/signalpipe.c77
-rw-r--r--networking/udhcp/socket.c130
-rw-r--r--networking/udhcp/static_leases.c99
24 files changed, 3862 insertions, 0 deletions
diff --git a/networking/udhcp/Config.in b/networking/udhcp/Config.in
new file mode 100644
index 000000000..f633473eb
--- /dev/null
+++ b/networking/udhcp/Config.in
@@ -0,0 +1,67 @@
1#
2# For a description of the syntax of this configuration file,
3# see scripts/kbuild/config-language.txt.
4#
5
6config APP_UDHCPD
7 bool "udhcp Server (udhcpd)"
8 default n
9 help
10 uDHCPd is a DHCP server geared primarily toward embedded systems,
11 while striving to be fully functional and RFC compliant.
12
13 See http://udhcp.busybox.net for further details.
14
15config APP_DHCPRELAY
16 bool "dhcprelay"
17 default n
18 depends on APP_UDHCPD
19 help
20 dhcprelay listens for dhcp requests on one or more interfaces
21 and forwards these requests to a different interface or dhcp
22 server.
23
24config APP_DUMPLEASES
25 bool "Lease display utility (dumpleases)"
26 default n
27 depends on APP_UDHCPD
28 help
29 dumpleases displays the leases written out by the udhcpd server.
30 Lease times are stored in the file by time remaining in lease, or
31 by the absolute time that it expires in seconds from epoch.
32
33 See http://udhcp.busybox.net for further details.
34
35config APP_UDHCPC
36 bool "udhcp Client (udhcpc)"
37 default n
38 help
39 uDHCPc is a DHCP client geared primarily toward embedded systems,
40 while striving to be fully functional and RFC compliant.
41
42 The udhcp client negotiates a lease with the DHCP server and
43 notifies a set of scripts when a lease is obtained or lost.
44
45 See http://udhcp.busybox.net for further details.
46
47config FEATURE_UDHCP_SYSLOG
48 bool "Log udhcp messages to syslog"
49 default n
50 depends on APP_UDHCPD || APP_UDHCPC
51 select FEATURE_SYSLOG
52 help
53 If not daemonized, udhcpd prints its messages to stdout/stderr.
54 If this option is selected, it will also log them to syslog.
55
56 See http://udhcp.busybox.net for further details.
57
58config FEATURE_UDHCP_DEBUG
59 bool "Compile udhcp with noisy debugging messages"
60 default n
61 depends on APP_UDHCPD || APP_UDHCPC
62 help
63 If selected, udhcpd will output extra debugging output. If using
64 this option, compile uDHCP with "-g", and do not fork the daemon to
65 the background.
66
67 See http://udhcp.busybox.net for further details.
diff --git a/networking/udhcp/Kbuild b/networking/udhcp/Kbuild
new file mode 100644
index 000000000..dc2c01f61
--- /dev/null
+++ b/networking/udhcp/Kbuild
@@ -0,0 +1,18 @@
1# Makefile for busybox
2#
3# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
4#
5# Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
6#
7
8lib-y:=
9lib-$(CONFIG_APP_UDHCPC) += common.o options.o packet.o pidfile.o \
10 signalpipe.o socket.o
11lib-$(CONFIG_APP_UDHCPD) += common.o options.o packet.o pidfile.o \
12 signalpipe.o socket.o
13lib-$(CONFIG_APP_UDHCPC) += dhcpc.o clientpacket.o clientsocket.o \
14 script.o
15lib-$(CONFIG_APP_UDHCPD) += dhcpd.o arpping.o files.o leases.o \
16 serverpacket.o static_leases.o
17lib-$(CONFIG_APP_DUMPLEASES) += dumpleases.o
18lib-$(CONFIG_APP_DHCPRELAY) += dhcprelay.o
diff --git a/networking/udhcp/arpping.c b/networking/udhcp/arpping.c
new file mode 100644
index 000000000..9c8b9c562
--- /dev/null
+++ b/networking/udhcp/arpping.c
@@ -0,0 +1,114 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * arpping.c
4 *
5 * Mostly stolen from: dhcpcd - DHCP client daemon
6 * by Yoichi Hariguchi <yoichi@fore.com>
7 */
8
9#include <netinet/if_ether.h>
10#include <net/if_arp.h>
11
12#include "common.h"
13#include "dhcpd.h"
14
15
16struct arpMsg {
17 /* Ethernet header */
18 u_char h_dest[6]; /* destination ether addr */
19 u_char h_source[6]; /* source ether addr */
20 u_short h_proto; /* packet type ID field */
21
22 /* ARP packet */
23 uint16_t htype; /* hardware type (must be ARPHRD_ETHER) */
24 uint16_t ptype; /* protocol type (must be ETH_P_IP) */
25 uint8_t hlen; /* hardware address length (must be 6) */
26 uint8_t plen; /* protocol address length (must be 4) */
27 uint16_t operation; /* ARP opcode */
28 uint8_t sHaddr[6]; /* sender's hardware address */
29 uint8_t sInaddr[4]; /* sender's IP address */
30 uint8_t tHaddr[6]; /* target's hardware address */
31 uint8_t tInaddr[4]; /* target's IP address */
32 uint8_t pad[18]; /* pad for min. Ethernet payload (60 bytes) */
33} ATTRIBUTE_PACKED;
34
35/* args: yiaddr - what IP to ping
36 * ip - our ip
37 * mac - our arp address
38 * interface - interface to use
39 * retn: 1 addr free
40 * 0 addr used
41 * -1 error
42 */
43
44/* FIXME: match response against chaddr */
45int arpping(uint32_t yiaddr, uint32_t ip, uint8_t *mac, char *interface)
46{
47 int timeout = 2;
48 int s; /* socket */
49 int rv = 1; /* return value */
50 struct sockaddr addr; /* for interface name */
51 struct arpMsg arp;
52 fd_set fdset;
53 struct timeval tm;
54 time_t prevTime;
55
56
57 s = socket(PF_PACKET, SOCK_PACKET, htons(ETH_P_ARP));
58 if (s == -1) {
59 bb_perror_msg(bb_msg_can_not_create_raw_socket);
60 return -1;
61 }
62
63 if (setsockopt_broadcast(s) == -1) {
64 bb_perror_msg("cannot setsocketopt on raw socket");
65 close(s);
66 return -1;
67 }
68
69 /* send arp request */
70 memset(&arp, 0, sizeof(arp));
71 memcpy(arp.h_dest, MAC_BCAST_ADDR, 6); /* MAC DA */
72 memcpy(arp.h_source, mac, 6); /* MAC SA */
73 arp.h_proto = htons(ETH_P_ARP); /* protocol type (Ethernet) */
74 arp.htype = htons(ARPHRD_ETHER); /* hardware type */
75 arp.ptype = htons(ETH_P_IP); /* protocol type (ARP message) */
76 arp.hlen = 6; /* hardware address length */
77 arp.plen = 4; /* protocol address length */
78 arp.operation = htons(ARPOP_REQUEST); /* ARP op code */
79 memcpy(arp.sInaddr, &ip, sizeof(ip)); /* source IP address */
80 memcpy(arp.sHaddr, mac, 6); /* source hardware address */
81 memcpy(arp.tInaddr, &yiaddr, sizeof(yiaddr)); /* target IP address */
82
83 memset(&addr, 0, sizeof(addr));
84 strcpy(addr.sa_data, interface);
85 if (sendto(s, &arp, sizeof(arp), 0, &addr, sizeof(addr)) < 0)
86 rv = 0;
87
88 /* wait arp reply, and check it */
89 tm.tv_usec = 0;
90 prevTime = uptime();
91 while (timeout > 0) {
92 FD_ZERO(&fdset);
93 FD_SET(s, &fdset);
94 tm.tv_sec = timeout;
95 if (select(s + 1, &fdset, (fd_set *) NULL, (fd_set *) NULL, &tm) < 0) {
96 bb_perror_msg("error on ARPING request");
97 if (errno != EINTR) rv = 0;
98 } else if (FD_ISSET(s, &fdset)) {
99 if (recv(s, &arp, sizeof(arp), 0) < 0 ) rv = 0;
100 if (arp.operation == htons(ARPOP_REPLY) &&
101 memcmp(arp.tHaddr, mac, 6) == 0 &&
102 *((uint32_t *) arp.sInaddr) == yiaddr) {
103 DEBUG("Valid arp reply received for this address");
104 rv = 0;
105 break;
106 }
107 }
108 timeout -= uptime() - prevTime;
109 prevTime = uptime();
110 }
111 close(s);
112 DEBUG("%salid arp replies for this address", rv ? "No v" : "V");
113 return rv;
114}
diff --git a/networking/udhcp/clientpacket.c b/networking/udhcp/clientpacket.c
new file mode 100644
index 000000000..15cbda2f5
--- /dev/null
+++ b/networking/udhcp/clientpacket.c
@@ -0,0 +1,224 @@
1/* vi: set sw=4 ts=4: */
2/* clientpacket.c
3 *
4 * Packet generation and dispatching functions for the DHCP client.
5 *
6 * Russ Dill <Russ.Dill@asu.edu> July 2001
7 *
8 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
9 */
10
11#include <features.h>
12#if (__GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1) || defined _NEWLIB_VERSION
13#include <netpacket/packet.h>
14#include <net/ethernet.h>
15#else
16#include <asm/types.h>
17#include <linux/if_packet.h>
18#include <linux/if_ether.h>
19#endif
20
21#include "common.h"
22#include "dhcpd.h"
23#include "dhcpc.h"
24#include "options.h"
25
26
27/* Create a random xid */
28unsigned long random_xid(void)
29{
30 static int initialized;
31 if (!initialized) {
32 unsigned long seed;
33
34 if (open_read_close("/dev/urandom", &seed, sizeof(seed)) < 0) {
35 bb_info_msg("Cannot load seed "
36 "from /dev/urandom: %s", strerror(errno));
37 seed = time(0);
38 }
39 srand(seed);
40 initialized++;
41 }
42 return rand();
43}
44
45
46/* initialize a packet with the proper defaults */
47static void init_packet(struct dhcpMessage *packet, char type)
48{
49 udhcp_init_header(packet, type);
50 memcpy(packet->chaddr, client_config.arp, 6);
51 if (client_config.clientid)
52 add_option_string(packet->options, client_config.clientid);
53 if (client_config.hostname) add_option_string(packet->options, client_config.hostname);
54 if (client_config.fqdn) add_option_string(packet->options, client_config.fqdn);
55 add_option_string(packet->options, client_config.vendorclass);
56}
57
58
59/* Add a parameter request list for stubborn DHCP servers. Pull the data
60 * from the struct in options.c. Don't do bounds checking here because it
61 * goes towards the head of the packet. */
62static void add_requests(struct dhcpMessage *packet)
63{
64 int end = end_option(packet->options);
65 int i, len = 0;
66
67 packet->options[end + OPT_CODE] = DHCP_PARAM_REQ;
68 for (i = 0; dhcp_options[i].code; i++)
69 if (dhcp_options[i].flags & OPTION_REQ)
70 packet->options[end + OPT_DATA + len++] = dhcp_options[i].code;
71 packet->options[end + OPT_LEN] = len;
72 packet->options[end + OPT_DATA + len] = DHCP_END;
73
74}
75
76
77/* Broadcast a DHCP discover packet to the network, with an optionally requested IP */
78int send_discover(unsigned long xid, unsigned long requested)
79{
80 struct dhcpMessage packet;
81
82 init_packet(&packet, DHCPDISCOVER);
83 packet.xid = xid;
84 if (requested)
85 add_simple_option(packet.options, DHCP_REQUESTED_IP, requested);
86
87 add_requests(&packet);
88 bb_info_msg("Sending discover...");
89 return udhcp_raw_packet(&packet, INADDR_ANY, CLIENT_PORT, INADDR_BROADCAST,
90 SERVER_PORT, MAC_BCAST_ADDR, client_config.ifindex);
91}
92
93
94/* Broadcasts a DHCP request message */
95int send_selecting(unsigned long xid, unsigned long server, unsigned long requested)
96{
97 struct dhcpMessage packet;
98 struct in_addr addr;
99
100 init_packet(&packet, DHCPREQUEST);
101 packet.xid = xid;
102
103 add_simple_option(packet.options, DHCP_REQUESTED_IP, requested);
104 add_simple_option(packet.options, DHCP_SERVER_ID, server);
105
106 add_requests(&packet);
107 addr.s_addr = requested;
108 bb_info_msg("Sending select for %s...", inet_ntoa(addr));
109 return udhcp_raw_packet(&packet, INADDR_ANY, CLIENT_PORT, INADDR_BROADCAST,
110 SERVER_PORT, MAC_BCAST_ADDR, client_config.ifindex);
111}
112
113
114/* Unicasts or broadcasts a DHCP renew message */
115int send_renew(unsigned long xid, unsigned long server, unsigned long ciaddr)
116{
117 struct dhcpMessage packet;
118 int ret = 0;
119
120 init_packet(&packet, DHCPREQUEST);
121 packet.xid = xid;
122 packet.ciaddr = ciaddr;
123
124 add_requests(&packet);
125 bb_info_msg("Sending renew...");
126 if (server)
127 ret = udhcp_kernel_packet(&packet, ciaddr, CLIENT_PORT, server, SERVER_PORT);
128 else ret = udhcp_raw_packet(&packet, INADDR_ANY, CLIENT_PORT, INADDR_BROADCAST,
129 SERVER_PORT, MAC_BCAST_ADDR, client_config.ifindex);
130 return ret;
131}
132
133
134/* Unicasts a DHCP release message */
135int send_release(unsigned long server, unsigned long ciaddr)
136{
137 struct dhcpMessage packet;
138
139 init_packet(&packet, DHCPRELEASE);
140 packet.xid = random_xid();
141 packet.ciaddr = ciaddr;
142
143 add_simple_option(packet.options, DHCP_REQUESTED_IP, ciaddr);
144 add_simple_option(packet.options, DHCP_SERVER_ID, server);
145
146 bb_info_msg("Sending release...");
147 return udhcp_kernel_packet(&packet, ciaddr, CLIENT_PORT, server, SERVER_PORT);
148}
149
150
151/* return -1 on errors that are fatal for the socket, -2 for those that aren't */
152int get_raw_packet(struct dhcpMessage *payload, int fd)
153{
154 int bytes;
155 struct udp_dhcp_packet packet;
156 uint32_t source, dest;
157 uint16_t check;
158
159 memset(&packet, 0, sizeof(struct udp_dhcp_packet));
160 bytes = read(fd, &packet, sizeof(struct udp_dhcp_packet));
161 if (bytes < 0) {
162 DEBUG("Cannot read on raw listening socket - ignoring");
163 usleep(500000); /* possible down interface, looping condition */
164 return -1;
165 }
166
167 if (bytes < (int) (sizeof(struct iphdr) + sizeof(struct udphdr))) {
168 DEBUG("Message too short, ignoring");
169 return -2;
170 }
171
172 if (bytes < ntohs(packet.ip.tot_len)) {
173 DEBUG("Truncated packet");
174 return -2;
175 }
176
177 /* ignore any extra garbage bytes */
178 bytes = ntohs(packet.ip.tot_len);
179
180 /* Make sure its the right packet for us, and that it passes sanity checks */
181 if (packet.ip.protocol != IPPROTO_UDP || packet.ip.version != IPVERSION
182 || packet.ip.ihl != sizeof(packet.ip) >> 2
183 || packet.udp.dest != htons(CLIENT_PORT)
184 || bytes > (int) sizeof(struct udp_dhcp_packet)
185 || ntohs(packet.udp.len) != (uint16_t)(bytes - sizeof(packet.ip))
186 ) {
187 DEBUG("Unrelated/bogus packet");
188 return -2;
189 }
190
191 /* check IP checksum */
192 check = packet.ip.check;
193 packet.ip.check = 0;
194 if (check != udhcp_checksum(&(packet.ip), sizeof(packet.ip))) {
195 DEBUG("bad IP header checksum, ignoring");
196 return -1;
197 }
198
199 /* verify the UDP checksum by replacing the header with a psuedo header */
200 source = packet.ip.saddr;
201 dest = packet.ip.daddr;
202 check = packet.udp.check;
203 packet.udp.check = 0;
204 memset(&packet.ip, 0, sizeof(packet.ip));
205
206 packet.ip.protocol = IPPROTO_UDP;
207 packet.ip.saddr = source;
208 packet.ip.daddr = dest;
209 packet.ip.tot_len = packet.udp.len; /* cheat on the psuedo-header */
210 if (check && check != udhcp_checksum(&packet, bytes)) {
211 bb_error_msg("packet with bad UDP checksum received, ignoring");
212 return -2;
213 }
214
215 memcpy(payload, &(packet.data), bytes - (sizeof(packet.ip) + sizeof(packet.udp)));
216
217 if (ntohl(payload->cookie) != DHCP_MAGIC) {
218 bb_error_msg("received bogus message (bad magic) - ignoring");
219 return -2;
220 }
221 DEBUG("oooooh!!! got some!");
222 return bytes - (sizeof(packet.ip) + sizeof(packet.udp));
223
224}
diff --git a/networking/udhcp/clientsocket.c b/networking/udhcp/clientsocket.c
new file mode 100644
index 000000000..852061968
--- /dev/null
+++ b/networking/udhcp/clientsocket.c
@@ -0,0 +1,59 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * clientsocket.c -- DHCP client socket creation
4 *
5 * udhcp client
6 *
7 * 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 <features.h>
25#if (__GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1) || defined(_NEWLIB_VERSION)
26#include <netpacket/packet.h>
27#include <net/ethernet.h>
28#else
29#include <asm/types.h>
30#include <linux/if_packet.h>
31#include <linux/if_ether.h>
32#endif
33
34#include "common.h"
35
36
37int raw_socket(int ifindex)
38{
39 int fd;
40 struct sockaddr_ll sock;
41
42 DEBUG("Opening raw socket on ifindex %d", ifindex);
43 fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP));
44 if (fd < 0) {
45 bb_perror_msg("socket");
46 return -1;
47 }
48
49 sock.sll_family = AF_PACKET;
50 sock.sll_protocol = htons(ETH_P_IP);
51 sock.sll_ifindex = ifindex;
52 if (bind(fd, (struct sockaddr *) &sock, sizeof(sock)) < 0) {
53 bb_perror_msg("bind");
54 close(fd);
55 return -1;
56 }
57
58 return fd;
59}
diff --git a/networking/udhcp/common.c b/networking/udhcp/common.c
new file mode 100644
index 000000000..3e916f422
--- /dev/null
+++ b/networking/udhcp/common.c
@@ -0,0 +1,75 @@
1/* vi: set sw=4 ts=4: */
2/* common.c
3 *
4 * Functions for debugging and logging as well as some other
5 * simple helper functions.
6 *
7 * Russ Dill <Russ.Dill@asu.edu> 2001-2003
8 * Rewritten by Vladimir Oleynik <dzo@simtreas.ru> (C) 2003
9 *
10 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
11 */
12
13#include <syslog.h>
14
15#include "common.h"
16
17
18long uptime(void)
19{
20 struct sysinfo info;
21 sysinfo(&info);
22 return info.uptime;
23}
24
25/*
26 * This function makes sure our first socket calls
27 * aren't going to fd 1 (printf badness...) and are
28 * not later closed by daemon()
29 */
30static inline void sanitize_fds(void)
31{
32 int fd = xopen(bb_dev_null, O_RDWR);
33 while (fd < 3)
34 fd = dup(fd);
35 close(fd);
36}
37
38
39void udhcp_background(const char *pidfile)
40{
41#ifdef __uClinux__
42 bb_error_msg("cannot background in uclinux (yet)");
43#else /* __uClinux__ */
44 int pid_fd;
45
46 /* hold lock during fork. */
47 pid_fd = pidfile_acquire(pidfile);
48 setsid();
49 xdaemon(0, 0);
50 logmode &= ~LOGMODE_STDIO;
51 pidfile_write_release(pid_fd);
52#endif /* __uClinux__ */
53}
54
55void udhcp_start_log_and_pid(const char *pidfile)
56{
57 int pid_fd;
58
59 /* Make sure our syslog fd isn't overwritten */
60 sanitize_fds();
61
62 /* do some other misc startup stuff while we are here to save bytes */
63 pid_fd = pidfile_acquire(pidfile);
64 pidfile_write_release(pid_fd);
65
66 /* equivelent of doing a fflush after every \n */
67 setlinebuf(stdout);
68
69 if (ENABLE_FEATURE_UDHCP_SYSLOG) {
70 openlog(applet_name, LOG_PID, LOG_LOCAL0);
71 logmode |= LOGMODE_SYSLOG;
72 }
73
74 bb_info_msg("%s (v%s) started", applet_name, BB_VER);
75}
diff --git a/networking/udhcp/common.h b/networking/udhcp/common.h
new file mode 100644
index 000000000..70a769342
--- /dev/null
+++ b/networking/udhcp/common.h
@@ -0,0 +1,108 @@
1/* vi: set sw=4 ts=4: */
2/* common.h
3 *
4 * Russ Dill <Russ.Dill@asu.edu> September 2001
5 * Rewritten by Vladimir Oleynik <dzo@simtreas.ru> (C) 2003
6 *
7 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
8 */
9
10#ifndef _COMMON_H
11#define _COMMON_H
12
13#include "busybox.h"
14
15#ifdef CONFIG_INSTALL_NO_USR
16# define DEFAULT_SCRIPT "/share/udhcpc/default.script"
17#else
18# define DEFAULT_SCRIPT "/usr/share/udhcpc/default.script"
19#endif
20
21#define COMBINED_BINARY
22
23
24/*** packet.h ***/
25
26#include <netinet/udp.h>
27#include <netinet/ip.h>
28
29struct dhcpMessage {
30 uint8_t op;
31 uint8_t htype;
32 uint8_t hlen;
33 uint8_t hops;
34 uint32_t xid;
35 uint16_t secs;
36 uint16_t flags;
37 uint32_t ciaddr;
38 uint32_t yiaddr;
39 uint32_t siaddr;
40 uint32_t giaddr;
41 uint8_t chaddr[16];
42 uint8_t sname[64];
43 uint8_t file[128];
44 uint32_t cookie;
45 uint8_t options[308]; /* 312 - cookie */
46};
47
48struct udp_dhcp_packet {
49 struct iphdr ip;
50 struct udphdr udp;
51 struct dhcpMessage data;
52};
53
54void udhcp_init_header(struct dhcpMessage *packet, char type);
55int udhcp_get_packet(struct dhcpMessage *packet, int fd);
56uint16_t udhcp_checksum(void *addr, int count);
57int udhcp_raw_packet(struct dhcpMessage *payload,
58 uint32_t source_ip, int source_port,
59 uint32_t dest_ip, int dest_port, uint8_t *dest_arp, int ifindex);
60int udhcp_kernel_packet(struct dhcpMessage *payload,
61 uint32_t source_ip, int source_port,
62 uint32_t dest_ip, int dest_port);
63
64
65/**/
66
67void udhcp_background(const char *pidfile);
68void udhcp_start_log_and_pid(const char *pidfile);
69
70void udhcp_run_script(struct dhcpMessage *packet, const char *name);
71
72// Still need to clean these up...
73
74/* from pidfile.h */
75#define pidfile_acquire udhcp_pidfile_acquire
76#define pidfile_write_release udhcp_pidfile_write_release
77/* from options.h */
78#define get_option udhcp_get_option
79#define end_option udhcp_end_option
80#define add_option_string udhcp_add_option_string
81#define add_simple_option udhcp_add_simple_option
82#define option_lengths udhcp_option_lengths
83/* from socket.h */
84#define listen_socket udhcp_listen_socket
85#define read_interface udhcp_read_interface
86/* from dhcpc.h */
87#define client_config udhcp_client_config
88/* from dhcpd.h */
89#define server_config udhcp_server_config
90
91long uptime(void);
92void udhcp_sp_setup(void);
93int udhcp_sp_fd_set(fd_set *rfds, int extra_fd);
94int udhcp_sp_read(fd_set *rfds);
95int raw_socket(int ifindex);
96int read_interface(char *interface, int *ifindex, uint32_t *addr, uint8_t *arp);
97int listen_socket(uint32_t ip, int port, char *inf);
98int pidfile_acquire(const char *pidfile);
99void pidfile_write_release(int pid_fd);
100int arpping(uint32_t yiaddr, uint32_t ip, uint8_t *arp, char *interface);
101
102#if ENABLE_FEATURE_UDHCP_DEBUG
103# define DEBUG(str, args...) bb_info_msg(str, ## args)
104#else
105# define DEBUG(str, args...) do {;} while (0)
106#endif
107
108#endif
diff --git a/networking/udhcp/dhcpc.c b/networking/udhcp/dhcpc.c
new file mode 100644
index 000000000..71315ff0a
--- /dev/null
+++ b/networking/udhcp/dhcpc.c
@@ -0,0 +1,509 @@
1/* vi: set sw=4 ts=4: */
2/* dhcpc.c
3 *
4 * udhcp DHCP client
5 *
6 * Russ Dill <Russ.Dill@asu.edu> July 2001
7 *
8 * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
9 */
10
11#include <getopt.h>
12
13#include "common.h"
14#include "dhcpd.h"
15#include "dhcpc.h"
16#include "options.h"
17
18
19static int state;
20/* Something is definitely wrong here. IPv4 addresses
21 * in variables of type long?? BTW, we use inet_ntoa()
22 * in the code. Manpage says that struct in_addr has a member of type long (!)
23 * which holds IPv4 address, and the struct is passed by value (!!)
24 */
25static unsigned long requested_ip; /* = 0 */
26static unsigned long server_addr;
27static unsigned long timeout;
28static int packet_num; /* = 0 */
29static int fd = -1;
30
31#define LISTEN_NONE 0
32#define LISTEN_KERNEL 1
33#define LISTEN_RAW 2
34static int listen_mode;
35
36struct client_config_t client_config;
37
38
39/* just a little helper */
40static void change_mode(int new_mode)
41{
42 DEBUG("entering %s listen mode",
43 new_mode ? (new_mode == 1 ? "kernel" : "raw") : "none");
44 if (fd >= 0) close(fd);
45 fd = -1;
46 listen_mode = new_mode;
47}
48
49
50/* perform a renew */
51static void perform_renew(void)
52{
53 bb_info_msg("Performing a DHCP renew");
54 switch (state) {
55 case BOUND:
56 change_mode(LISTEN_KERNEL);
57 case RENEWING:
58 case REBINDING:
59 state = RENEW_REQUESTED;
60 break;
61 case RENEW_REQUESTED: /* impatient are we? fine, square 1 */
62 udhcp_run_script(NULL, "deconfig");
63 case REQUESTING:
64 case RELEASED:
65 change_mode(LISTEN_RAW);
66 state = INIT_SELECTING;
67 break;
68 case INIT_SELECTING:
69 break;
70 }
71
72 /* start things over */
73 packet_num = 0;
74
75 /* Kill any timeouts because the user wants this to hurry along */
76 timeout = 0;
77}
78
79
80/* perform a release */
81static void perform_release(void)
82{
83 char buffer[16];
84 struct in_addr temp_addr;
85
86 /* send release packet */
87 if (state == BOUND || state == RENEWING || state == REBINDING) {
88 temp_addr.s_addr = server_addr;
89 sprintf(buffer, "%s", inet_ntoa(temp_addr));
90 temp_addr.s_addr = requested_ip;
91 bb_info_msg("Unicasting a release of %s to %s",
92 inet_ntoa(temp_addr), buffer);
93 send_release(server_addr, requested_ip); /* unicast */
94 udhcp_run_script(NULL, "deconfig");
95 }
96 bb_info_msg("Entering released state");
97
98 change_mode(LISTEN_NONE);
99 state = RELEASED;
100 timeout = 0x7fffffff;
101}
102
103
104static void client_background(void)
105{
106 udhcp_background(client_config.pidfile);
107 client_config.foreground = 1; /* Do not fork again. */
108 client_config.background_if_no_lease = 0;
109}
110
111
112static uint8_t* alloc_dhcp_option(int code, const char *str, int extra)
113{
114 uint8_t *storage;
115 int len = strlen(str);
116 if (len > 255) len = 255;
117 storage = xzalloc(len + extra + OPT_DATA);
118 storage[OPT_CODE] = code;
119 storage[OPT_LEN] = len + extra;
120 memcpy(storage + extra + OPT_DATA, str, len);
121 return storage;
122}
123
124
125int udhcpc_main(int argc, char *argv[])
126{
127 uint8_t *temp, *message;
128 char *str_c, *str_V, *str_h, *str_F, *str_r, *str_T, *str_t;
129 unsigned long t1 = 0, t2 = 0, xid = 0;
130 unsigned long start = 0, lease = 0;
131 long now;
132 unsigned opt;
133 int max_fd;
134 int sig;
135 int retval;
136 int len;
137 int no_clientid = 0;
138 fd_set rfds;
139 struct timeval tv;
140 struct dhcpMessage packet;
141 struct in_addr temp_addr;
142
143 enum {
144 OPT_c = 1 << 0,
145 OPT_C = 1 << 1,
146 OPT_V = 1 << 2,
147 OPT_f = 1 << 3,
148 OPT_b = 1 << 4,
149 OPT_H = 1 << 5,
150 OPT_h = 1 << 6,
151 OPT_F = 1 << 7,
152 OPT_i = 1 << 8,
153 OPT_n = 1 << 9,
154 OPT_p = 1 << 10,
155 OPT_q = 1 << 11,
156 OPT_R = 1 << 12,
157 OPT_r = 1 << 13,
158 OPT_s = 1 << 14,
159 OPT_T = 1 << 15,
160 OPT_t = 1 << 16,
161 OPT_v = 1 << 17,
162 };
163#if ENABLE_GETOPT_LONG
164 static const struct option arg_options[] = {
165 { "clientid", required_argument, 0, 'c' },
166 { "clientid-none", no_argument, 0, 'C' },
167 { "vendorclass", required_argument, 0, 'V' },
168 { "foreground", no_argument, 0, 'f' },
169 { "background", no_argument, 0, 'b' },
170 { "hostname", required_argument, 0, 'H' },
171 { "hostname", required_argument, 0, 'h' },
172 { "fqdn", required_argument, 0, 'F' },
173 { "interface", required_argument, 0, 'i' },
174 { "now", no_argument, 0, 'n' },
175 { "pidfile", required_argument, 0, 'p' },
176 { "quit", no_argument, 0, 'q' },
177 { "release", no_argument, 0, 'R' },
178 { "request", required_argument, 0, 'r' },
179 { "script", required_argument, 0, 's' },
180 { "timeout", required_argument, 0, 'T' },
181 { "version", no_argument, 0, 'v' },
182 { "retries", required_argument, 0, 't' },
183 { 0, 0, 0, 0 }
184 };
185#endif
186 /* Default options. */
187 client_config.interface = "eth0";
188 client_config.script = DEFAULT_SCRIPT;
189 client_config.retries = 3;
190 client_config.timeout = 3;
191
192 /* Parse command line */
193 opt_complementary = "?:c--C:C--c" // mutually exclusive
194 ":hH:Hh"; // -h and -H are the same
195#if ENABLE_GETOPT_LONG
196 applet_long_options = arg_options;
197#endif
198 opt = getopt32(argc, argv, "c:CV:fbH:h:F:i:np:qRr:s:T:t:v",
199 &str_c, &str_V, &str_h, &str_h, &str_F,
200 &client_config.interface, &client_config.pidfile, &str_r,
201 &client_config.script, &str_T, &str_t
202 );
203
204 if (opt & OPT_c)
205 client_config.clientid = alloc_dhcp_option(DHCP_CLIENT_ID, str_c, 0);
206 if (opt & OPT_C)
207 no_clientid = 1;
208 if (opt & OPT_V)
209 client_config.vendorclass = alloc_dhcp_option(DHCP_VENDOR, str_V, 0);
210 if (opt & OPT_f)
211 client_config.foreground = 1;
212 if (opt & OPT_b)
213 client_config.background_if_no_lease = 1;
214 if (opt & OPT_h)
215 client_config.hostname = alloc_dhcp_option(DHCP_HOST_NAME, str_h, 0);
216 if (opt & OPT_F) {
217 client_config.fqdn = alloc_dhcp_option(DHCP_FQDN, str_F, 3);
218 /* Flags: 0000NEOS
219 S: 1 => Client requests Server to update A RR in DNS as well as PTR
220 O: 1 => Server indicates to client that DNS has been updated regardless
221 E: 1 => Name data is DNS format, i.e. <4>host<6>domain<4>com<0> not "host.domain.com"
222 N: 1 => Client requests Server to not update DNS
223 */
224 client_config.fqdn[OPT_DATA + 0] = 0x1;
225 /* client_config.fqdn[OPT_DATA + 1] = 0; - redundant */
226 /* client_config.fqdn[OPT_DATA + 2] = 0; - redundant */
227 }
228 // if (opt & OPT_i) client_config.interface = ...
229 if (opt & OPT_n)
230 client_config.abort_if_no_lease = 1;
231 // if (opt & OPT_p) client_config.pidfile = ...
232 if (opt & OPT_q)
233 client_config.quit_after_lease = 1;
234 if (opt & OPT_R)
235 client_config.release_on_quit = 1;
236 if (opt & OPT_r)
237 requested_ip = inet_addr(str_r);
238 // if (opt & OPT_s) client_config.script = ...
239 if (opt & OPT_T)
240 client_config.timeout = xatoi_u(str_T);
241 if (opt & OPT_t)
242 client_config.retries = xatoi_u(str_t);
243 if (opt & OPT_v) {
244 printf("version %s\n\n", BB_VER);
245 return 0;
246 }
247
248 /* Start the log, sanitize fd's, and write a pid file */
249 udhcp_start_log_and_pid(client_config.pidfile);
250
251 if (read_interface(client_config.interface, &client_config.ifindex,
252 NULL, client_config.arp) < 0)
253 return 1;
254
255 /* if not set, and not suppressed, setup the default client ID */
256 if (!client_config.clientid && !no_clientid) {
257 client_config.clientid = alloc_dhcp_option(DHCP_CLIENT_ID, "", 7);
258 client_config.clientid[OPT_DATA] = 1;
259 memcpy(client_config.clientid + OPT_DATA+1, client_config.arp, 6);
260 }
261
262 if (!client_config.vendorclass)
263 client_config.vendorclass = alloc_dhcp_option(DHCP_VENDOR, "udhcp "BB_VER, 0);
264
265 /* setup the signal pipe */
266 udhcp_sp_setup();
267
268 state = INIT_SELECTING;
269 udhcp_run_script(NULL, "deconfig");
270 change_mode(LISTEN_RAW);
271
272 for (;;) {
273 tv.tv_sec = timeout - uptime();
274 tv.tv_usec = 0;
275
276 if (listen_mode != LISTEN_NONE && fd < 0) {
277 if (listen_mode == LISTEN_KERNEL)
278 fd = listen_socket(INADDR_ANY, CLIENT_PORT, client_config.interface);
279 else
280 fd = raw_socket(client_config.ifindex);
281 if (fd < 0) {
282 bb_perror_msg("FATAL: cannot listen on socket");
283 return 0;
284 }
285 }
286 max_fd = udhcp_sp_fd_set(&rfds, fd);
287
288 if (tv.tv_sec > 0) {
289 DEBUG("Waiting on select...");
290 retval = select(max_fd + 1, &rfds, NULL, NULL, &tv);
291 } else retval = 0; /* If we already timed out, fall through */
292
293 now = uptime();
294 if (retval == 0) {
295 /* timeout dropped to zero */
296 switch (state) {
297 case INIT_SELECTING:
298 if (packet_num < client_config.retries) {
299 if (packet_num == 0)
300 xid = random_xid();
301
302 /* send discover packet */
303 send_discover(xid, requested_ip); /* broadcast */
304
305 timeout = now + client_config.timeout;
306 packet_num++;
307 } else {
308 udhcp_run_script(NULL, "leasefail");
309 if (client_config.background_if_no_lease) {
310 bb_info_msg("No lease, forking to background");
311 client_background();
312 } else if (client_config.abort_if_no_lease) {
313 bb_info_msg("No lease, failing");
314 return 1;
315 }
316 /* wait to try again */
317 packet_num = 0;
318 timeout = now + 60;
319 }
320 break;
321 case RENEW_REQUESTED:
322 case REQUESTING:
323 if (packet_num < client_config.retries) {
324 /* send request packet */
325 if (state == RENEW_REQUESTED)
326 send_renew(xid, server_addr, requested_ip); /* unicast */
327 else send_selecting(xid, server_addr, requested_ip); /* broadcast */
328
329 timeout = now + ((packet_num == 2) ? 10 : 2);
330 packet_num++;
331 } else {
332 /* timed out, go back to init state */
333 if (state == RENEW_REQUESTED) udhcp_run_script(NULL, "deconfig");
334 state = INIT_SELECTING;
335 timeout = now;
336 packet_num = 0;
337 change_mode(LISTEN_RAW);
338 }
339 break;
340 case BOUND:
341 /* Lease is starting to run out, time to enter renewing state */
342 state = RENEWING;
343 change_mode(LISTEN_KERNEL);
344 DEBUG("Entering renew state");
345 /* fall right through */
346 case RENEWING:
347 /* Either set a new T1, or enter REBINDING state */
348 if ((t2 - t1) <= (lease / 14400 + 1)) {
349 /* timed out, enter rebinding state */
350 state = REBINDING;
351 timeout = now + (t2 - t1);
352 DEBUG("Entering rebinding state");
353 } else {
354 /* send a request packet */
355 send_renew(xid, server_addr, requested_ip); /* unicast */
356
357 t1 = (t2 - t1) / 2 + t1;
358 timeout = t1 + start;
359 }
360 break;
361 case REBINDING:
362 /* Either set a new T2, or enter INIT state */
363 if ((lease - t2) <= (lease / 14400 + 1)) {
364 /* timed out, enter init state */
365 state = INIT_SELECTING;
366 bb_info_msg("Lease lost, entering init state");
367 udhcp_run_script(NULL, "deconfig");
368 timeout = now;
369 packet_num = 0;
370 change_mode(LISTEN_RAW);
371 } else {
372 /* send a request packet */
373 send_renew(xid, 0, requested_ip); /* broadcast */
374
375 t2 = (lease - t2) / 2 + t2;
376 timeout = t2 + start;
377 }
378 break;
379 case RELEASED:
380 /* yah, I know, *you* say it would never happen */
381 timeout = 0x7fffffff;
382 break;
383 }
384 } else if (retval > 0 && listen_mode != LISTEN_NONE && FD_ISSET(fd, &rfds)) {
385 /* a packet is ready, read it */
386
387 if (listen_mode == LISTEN_KERNEL)
388 len = udhcp_get_packet(&packet, fd);
389 else len = get_raw_packet(&packet, fd);
390
391 if (len == -1 && errno != EINTR) {
392 DEBUG("error on read, %s, reopening socket", strerror(errno));
393 change_mode(listen_mode); /* just close and reopen */
394 }
395 if (len < 0) continue;
396
397 if (packet.xid != xid) {
398 DEBUG("Ignoring XID %lx (our xid is %lx)",
399 (unsigned long) packet.xid, xid);
400 continue;
401 }
402
403 /* Ignore packets that aren't for us */
404 if (memcmp(packet.chaddr, client_config.arp, 6)) {
405 DEBUG("Packet does not have our chaddr - ignoring");
406 continue;
407 }
408
409 if ((message = get_option(&packet, DHCP_MESSAGE_TYPE)) == NULL) {
410 bb_error_msg("cannot get option from packet - ignoring");
411 continue;
412 }
413
414 switch (state) {
415 case INIT_SELECTING:
416 /* Must be a DHCPOFFER to one of our xid's */
417 if (*message == DHCPOFFER) {
418 temp = get_option(&packet, DHCP_SERVER_ID);
419 if (temp) {
420 server_addr = *(uint32_t*)temp;
421 xid = packet.xid;
422 requested_ip = packet.yiaddr;
423
424 /* enter requesting state */
425 state = REQUESTING;
426 timeout = now;
427 packet_num = 0;
428 } else {
429 bb_error_msg("no server ID in message");
430 }
431 }
432 break;
433 case RENEW_REQUESTED:
434 case REQUESTING:
435 case RENEWING:
436 case REBINDING:
437 if (*message == DHCPACK) {
438 temp = get_option(&packet, DHCP_LEASE_TIME);
439 if (!temp) {
440 bb_error_msg("no lease time with ACK, using 1 hour lease");
441 lease = 60 * 60;
442 } else {
443 lease = ntohl(*(uint32_t*)temp);
444 }
445
446 /* enter bound state */
447 t1 = lease / 2;
448
449 /* little fixed point for n * .875 */
450 t2 = (lease * 0x7) >> 3;
451 temp_addr.s_addr = packet.yiaddr;
452 bb_info_msg("Lease of %s obtained, lease time %ld",
453 inet_ntoa(temp_addr), lease);
454 start = now;
455 timeout = t1 + start;
456 requested_ip = packet.yiaddr;
457 udhcp_run_script(&packet,
458 ((state == RENEWING || state == REBINDING) ? "renew" : "bound"));
459
460 state = BOUND;
461 change_mode(LISTEN_NONE);
462 if (client_config.quit_after_lease) {
463 if (client_config.release_on_quit)
464 perform_release();
465 return 0;
466 }
467 if (!client_config.foreground)
468 client_background();
469
470 } else if (*message == DHCPNAK) {
471 /* return to init state */
472 bb_info_msg("Received DHCP NAK");
473 udhcp_run_script(&packet, "nak");
474 if (state != REQUESTING)
475 udhcp_run_script(NULL, "deconfig");
476 state = INIT_SELECTING;
477 timeout = now;
478 requested_ip = 0;
479 packet_num = 0;
480 change_mode(LISTEN_RAW);
481 sleep(3); /* avoid excessive network traffic */
482 }
483 break;
484 /* case BOUND, RELEASED: - ignore all packets */
485 }
486 } else if (retval > 0 && (sig = udhcp_sp_read(&rfds))) {
487 switch (sig) {
488 case SIGUSR1:
489 perform_renew();
490 break;
491 case SIGUSR2:
492 perform_release();
493 break;
494 case SIGTERM:
495 bb_info_msg("Received SIGTERM");
496 if (client_config.release_on_quit)
497 perform_release();
498 return 0;
499 }
500 } else if (retval == -1 && errno == EINTR) {
501 /* a signal was caught */
502 } else {
503 /* An error occured */
504 bb_perror_msg("select");
505 }
506
507 }
508 return 0;
509}
diff --git a/networking/udhcp/dhcpc.h b/networking/udhcp/dhcpc.h
new file mode 100644
index 000000000..fd17917d0
--- /dev/null
+++ b/networking/udhcp/dhcpc.h
@@ -0,0 +1,50 @@
1/* vi: set sw=4 ts=4: */
2/* dhcpc.h */
3#ifndef _DHCPC_H
4#define _DHCPC_H
5
6#define INIT_SELECTING 0
7#define REQUESTING 1
8#define BOUND 2
9#define RENEWING 3
10#define REBINDING 4
11#define INIT_REBOOT 5
12#define RENEW_REQUESTED 6
13#define RELEASED 7
14
15struct client_config_t {
16 /* TODO: combine flag fields into single "unsigned opt" */
17 /* (can be set directly to the result of getopt32) */
18 char foreground; /* Do not fork */
19 char quit_after_lease; /* Quit after obtaining lease */
20 char release_on_quit; /* perform release on quit */
21 char abort_if_no_lease; /* Abort if no lease */
22 char background_if_no_lease; /* Fork to background if no lease */
23 char *interface; /* The name of the interface to use */
24 char *pidfile; /* Optionally store the process ID */
25 char *script; /* User script to run at dhcp events */
26 uint8_t *clientid; /* Optional client id to use */
27 uint8_t *vendorclass; /* Optional vendor class-id to use */
28 uint8_t *hostname; /* Optional hostname to use */
29 uint8_t *fqdn; /* Optional fully qualified domain name to use */
30 int ifindex; /* Index number of the interface to use */
31 int retries; /* Max number of request packets */
32 int timeout; /* Number of seconds to try to get a lease */
33 uint8_t arp[6]; /* Our arp address */
34};
35
36extern struct client_config_t client_config;
37
38
39/*** clientpacket.h ***/
40
41unsigned long random_xid(void);
42int send_discover(unsigned long xid, unsigned long requested);
43int send_selecting(unsigned long xid, unsigned long server, unsigned long requested);
44int send_renew(unsigned long xid, unsigned long server, unsigned long ciaddr);
45int send_renew(unsigned long xid, unsigned long server, unsigned long ciaddr);
46int send_release(unsigned long server, unsigned long ciaddr);
47int get_raw_packet(struct dhcpMessage *payload, int fd);
48
49
50#endif
diff --git a/networking/udhcp/dhcpd.c b/networking/udhcp/dhcpd.c
new file mode 100644
index 000000000..74380367f
--- /dev/null
+++ b/networking/udhcp/dhcpd.c
@@ -0,0 +1,226 @@
1/* vi: set sw=4 ts=4: */
2/* dhcpd.c
3 *
4 * udhcp Server
5 * Copyright (C) 1999 Matthew Ramsay <matthewr@moreton.com.au>
6 * Chris Trew <ctrew@moreton.com.au>
7 *
8 * Rewrite by Russ Dill <Russ.Dill@asu.edu> July 2001
9 *
10 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
11 */
12
13#include "common.h"
14#include "dhcpd.h"
15#include "options.h"
16
17
18/* globals */
19struct dhcpOfferedAddr *leases;
20struct server_config_t server_config;
21
22
23int udhcpd_main(int argc, char *argv[])
24{
25 fd_set rfds;
26 struct timeval tv;
27 int server_socket = -1, bytes, retval, max_sock;
28 struct dhcpMessage packet;
29 uint8_t *state, *server_id, *requested;
30 uint32_t server_id_align, requested_align, static_lease_ip;
31 unsigned long timeout_end, num_ips;
32 struct option_set *option;
33 struct dhcpOfferedAddr *lease, static_lease;
34
35 read_config(argc < 2 ? DHCPD_CONF_FILE : argv[1]);
36
37 /* Start the log, sanitize fd's, and write a pid file */
38 udhcp_start_log_and_pid(server_config.pidfile);
39
40 if ((option = find_option(server_config.options, DHCP_LEASE_TIME))) {
41 memcpy(&server_config.lease, option->data + 2, 4);
42 server_config.lease = ntohl(server_config.lease);
43 }
44 else server_config.lease = LEASE_TIME;
45
46 /* Sanity check */
47 num_ips = ntohl(server_config.end) - ntohl(server_config.start) + 1;
48 if (server_config.max_leases > num_ips) {
49 bb_error_msg("max_leases value (%lu) not sane, "
50 "setting to %lu instead",
51 server_config.max_leases, num_ips);
52 server_config.max_leases = num_ips;
53 }
54
55 leases = xzalloc(server_config.max_leases * sizeof(struct dhcpOfferedAddr));
56 read_leases(server_config.lease_file);
57
58 if (read_interface(server_config.interface, &server_config.ifindex,
59 &server_config.server, server_config.arp) < 0)
60 return 1;
61
62 if (!ENABLE_FEATURE_UDHCP_DEBUG)
63 udhcp_background(server_config.pidfile); /* hold lock during fork. */
64
65 /* Setup the signal pipe */
66 udhcp_sp_setup();
67
68 timeout_end = time(0) + server_config.auto_time;
69 while (1) { /* loop until universe collapses */
70
71 if (server_socket < 0) {
72 server_socket = listen_socket(INADDR_ANY, SERVER_PORT, server_config.interface);
73 if (server_socket < 0) {
74 bb_perror_msg("FATAL: cannot create server socket");
75 return 2;
76 }
77 }
78
79 max_sock = udhcp_sp_fd_set(&rfds, server_socket);
80 if (server_config.auto_time) {
81 tv.tv_sec = timeout_end - time(0);
82 tv.tv_usec = 0;
83 }
84 if (!server_config.auto_time || tv.tv_sec > 0) {
85 retval = select(max_sock + 1, &rfds, NULL, NULL,
86 server_config.auto_time ? &tv : NULL);
87 } else retval = 0; /* If we already timed out, fall through */
88
89 if (retval == 0) {
90 write_leases();
91 timeout_end = time(0) + server_config.auto_time;
92 continue;
93 } else if (retval < 0 && errno != EINTR) {
94 DEBUG("error on select");
95 continue;
96 }
97
98 switch (udhcp_sp_read(&rfds)) {
99 case SIGUSR1:
100 bb_info_msg("Received a SIGUSR1");
101 write_leases();
102 /* why not just reset the timeout, eh */
103 timeout_end = time(0) + server_config.auto_time;
104 continue;
105 case SIGTERM:
106 bb_info_msg("Received a SIGTERM");
107 return 0;
108 case 0: break; /* no signal */
109 default: continue; /* signal or error (probably EINTR) */
110 }
111
112 if ((bytes = udhcp_get_packet(&packet, server_socket)) < 0) { /* this waits for a packet - idle */
113 if (bytes == -1 && errno != EINTR) {
114 DEBUG("error on read, %s, reopening socket", strerror(errno));
115 close(server_socket);
116 server_socket = -1;
117 }
118 continue;
119 }
120
121 if ((state = get_option(&packet, DHCP_MESSAGE_TYPE)) == NULL) {
122 bb_error_msg("cannot get option from packet, ignoring");
123 continue;
124 }
125
126 /* Look for a static lease */
127 static_lease_ip = getIpByMac(server_config.static_leases, &packet.chaddr);
128
129 if (static_lease_ip) {
130 bb_info_msg("Found static lease: %x", static_lease_ip);
131
132 memcpy(&static_lease.chaddr, &packet.chaddr, 16);
133 static_lease.yiaddr = static_lease_ip;
134 static_lease.expires = 0;
135
136 lease = &static_lease;
137
138 } else {
139 lease = find_lease_by_chaddr(packet.chaddr);
140 }
141
142 switch (state[0]) {
143 case DHCPDISCOVER:
144 DEBUG("Received DISCOVER");
145
146 if (sendOffer(&packet) < 0) {
147 bb_error_msg("send OFFER failed");
148 }
149 break;
150 case DHCPREQUEST:
151 DEBUG("received REQUEST");
152
153 requested = get_option(&packet, DHCP_REQUESTED_IP);
154 server_id = get_option(&packet, DHCP_SERVER_ID);
155
156 if (requested) memcpy(&requested_align, requested, 4);
157 if (server_id) memcpy(&server_id_align, server_id, 4);
158
159 if (lease) {
160 if (server_id) {
161 /* SELECTING State */
162 DEBUG("server_id = %08x", ntohl(server_id_align));
163 if (server_id_align == server_config.server && requested &&
164 requested_align == lease->yiaddr) {
165 sendACK(&packet, lease->yiaddr);
166 }
167 } else {
168 if (requested) {
169 /* INIT-REBOOT State */
170 if (lease->yiaddr == requested_align)
171 sendACK(&packet, lease->yiaddr);
172 else sendNAK(&packet);
173 } else {
174 /* RENEWING or REBINDING State */
175 if (lease->yiaddr == packet.ciaddr)
176 sendACK(&packet, lease->yiaddr);
177 else {
178 /* don't know what to do!!!! */
179 sendNAK(&packet);
180 }
181 }
182 }
183
184 /* what to do if we have no record of the client */
185 } else if (server_id) {
186 /* SELECTING State */
187
188 } else if (requested) {
189 /* INIT-REBOOT State */
190 if ((lease = find_lease_by_yiaddr(requested_align))) {
191 if (lease_expired(lease)) {
192 /* probably best if we drop this lease */
193 memset(lease->chaddr, 0, 16);
194 /* make some contention for this address */
195 } else sendNAK(&packet);
196 } else if (requested_align < server_config.start ||
197 requested_align > server_config.end) {
198 sendNAK(&packet);
199 } /* else remain silent */
200
201 } else {
202 /* RENEWING or REBINDING State */
203 }
204 break;
205 case DHCPDECLINE:
206 DEBUG("Received DECLINE");
207 if (lease) {
208 memset(lease->chaddr, 0, 16);
209 lease->expires = time(0) + server_config.decline_time;
210 }
211 break;
212 case DHCPRELEASE:
213 DEBUG("Received RELEASE");
214 if (lease) lease->expires = time(0);
215 break;
216 case DHCPINFORM:
217 DEBUG("Received INFORM");
218 send_inform(&packet);
219 break;
220 default:
221 bb_info_msg("Unsupported DHCP message (%02x) - ignoring", state[0]);
222 }
223 }
224
225 return 0;
226}
diff --git a/networking/udhcp/dhcpd.h b/networking/udhcp/dhcpd.h
new file mode 100644
index 000000000..40959e4ae
--- /dev/null
+++ b/networking/udhcp/dhcpd.h
@@ -0,0 +1,190 @@
1/* vi: set sw=4 ts=4: */
2/* dhcpd.h */
3#ifndef _DHCPD_H
4#define _DHCPD_H
5
6/************************************/
7/* Defaults _you_ may want to tweak */
8/************************************/
9
10/* the period of time the client is allowed to use that address */
11#define LEASE_TIME (60*60*24*10) /* 10 days of seconds */
12#define LEASES_FILE "/var/lib/misc/udhcpd.leases"
13
14/* where to find the DHCP server configuration file */
15#define DHCPD_CONF_FILE "/etc/udhcpd.conf"
16
17/*****************************************************************/
18/* Do not modify below here unless you know what you are doing!! */
19/*****************************************************************/
20
21/* DHCP protocol -- see RFC 2131 */
22#define SERVER_PORT 67
23#define CLIENT_PORT 68
24
25#define DHCP_MAGIC 0x63825363
26
27/* DHCP option codes (partial list) */
28#define DHCP_PADDING 0x00
29#define DHCP_SUBNET 0x01
30#define DHCP_TIME_OFFSET 0x02
31#define DHCP_ROUTER 0x03
32#define DHCP_TIME_SERVER 0x04
33#define DHCP_NAME_SERVER 0x05
34#define DHCP_DNS_SERVER 0x06
35#define DHCP_LOG_SERVER 0x07
36#define DHCP_COOKIE_SERVER 0x08
37#define DHCP_LPR_SERVER 0x09
38#define DHCP_HOST_NAME 0x0c
39#define DHCP_BOOT_SIZE 0x0d
40#define DHCP_DOMAIN_NAME 0x0f
41#define DHCP_SWAP_SERVER 0x10
42#define DHCP_ROOT_PATH 0x11
43#define DHCP_IP_TTL 0x17
44#define DHCP_MTU 0x1a
45#define DHCP_BROADCAST 0x1c
46#define DHCP_NTP_SERVER 0x2a
47#define DHCP_WINS_SERVER 0x2c
48#define DHCP_REQUESTED_IP 0x32
49#define DHCP_LEASE_TIME 0x33
50#define DHCP_OPTION_OVER 0x34
51#define DHCP_MESSAGE_TYPE 0x35
52#define DHCP_SERVER_ID 0x36
53#define DHCP_PARAM_REQ 0x37
54#define DHCP_MESSAGE 0x38
55#define DHCP_MAX_SIZE 0x39
56#define DHCP_T1 0x3a
57#define DHCP_T2 0x3b
58#define DHCP_VENDOR 0x3c
59#define DHCP_CLIENT_ID 0x3d
60#define DHCP_FQDN 0x51
61
62#define DHCP_END 0xFF
63
64
65#define BOOTREQUEST 1
66#define BOOTREPLY 2
67
68#define ETH_10MB 1
69#define ETH_10MB_LEN 6
70
71#define DHCPDISCOVER 1
72#define DHCPOFFER 2
73#define DHCPREQUEST 3
74#define DHCPDECLINE 4
75#define DHCPACK 5
76#define DHCPNAK 6
77#define DHCPRELEASE 7
78#define DHCPINFORM 8
79
80#define BROADCAST_FLAG 0x8000
81
82#define OPTION_FIELD 0
83#define FILE_FIELD 1
84#define SNAME_FIELD 2
85
86/* miscellaneous defines */
87#define MAC_BCAST_ADDR (uint8_t *) "\xff\xff\xff\xff\xff\xff"
88#define OPT_CODE 0
89#define OPT_LEN 1
90#define OPT_DATA 2
91
92struct option_set {
93 uint8_t *data;
94 struct option_set *next;
95};
96
97struct static_lease {
98 uint8_t *mac;
99 uint32_t *ip;
100 struct static_lease *next;
101};
102
103struct server_config_t {
104 uint32_t server; /* Our IP, in network order */
105 uint32_t start; /* Start address of leases, network order */
106 uint32_t end; /* End of leases, network order */
107 struct option_set *options; /* List of DHCP options loaded from the config file */
108 char *interface; /* The name of the interface to use */
109 int ifindex; /* Index number of the interface to use */
110 uint8_t arp[6]; /* Our arp address */
111 unsigned long lease; /* lease time in seconds (host order) */
112 unsigned long max_leases; /* maximum number of leases (including reserved address) */
113 char remaining; /* should the lease file be interpreted as lease time remaining, or
114 * as the time the lease expires */
115 unsigned long auto_time; /* how long should udhcpd wait before writing a config file.
116 * if this is zero, it will only write one on SIGUSR1 */
117 unsigned long decline_time; /* how long an address is reserved if a client returns a
118 * decline message */
119 unsigned long conflict_time; /* how long an arp conflict offender is leased for */
120 unsigned long offer_time; /* how long an offered address is reserved */
121 unsigned long min_lease; /* minimum lease a client can request*/
122 char *lease_file;
123 char *pidfile;
124 char *notify_file; /* What to run whenever leases are written */
125 uint32_t siaddr; /* next server bootp option */
126 char *sname; /* bootp server name */
127 char *boot_file; /* bootp boot file option */
128 struct static_lease *static_leases; /* List of ip/mac pairs to assign static leases */
129};
130
131extern struct server_config_t server_config;
132extern struct dhcpOfferedAddr *leases;
133
134
135/*** leases.h ***/
136
137struct dhcpOfferedAddr {
138 uint8_t chaddr[16];
139 uint32_t yiaddr; /* network order */
140 uint32_t expires; /* host order */
141};
142
143extern uint8_t blank_chaddr[];
144
145void clear_lease(uint8_t *chaddr, uint32_t yiaddr);
146struct dhcpOfferedAddr *add_lease(uint8_t *chaddr, uint32_t yiaddr, unsigned long lease);
147int lease_expired(struct dhcpOfferedAddr *lease);
148struct dhcpOfferedAddr *oldest_expired_lease(void);
149struct dhcpOfferedAddr *find_lease_by_chaddr(uint8_t *chaddr);
150struct dhcpOfferedAddr *find_lease_by_yiaddr(uint32_t yiaddr);
151uint32_t find_address(int check_expired);
152
153
154/*** static_leases.h ***/
155
156/* Config file will pass static lease info to this function which will add it
157 * to a data structure that can be searched later */
158int addStaticLease(struct static_lease **lease_struct, uint8_t *mac, uint32_t *ip);
159/* Check to see if a mac has an associated static lease */
160uint32_t getIpByMac(struct static_lease *lease_struct, void *arg);
161/* Check to see if an ip is reserved as a static ip */
162uint32_t reservedIp(struct static_lease *lease_struct, uint32_t ip);
163/* Print out static leases just to check what's going on (debug code) */
164void printStaticLeases(struct static_lease **lease_struct);
165
166
167/*** serverpacket.h ***/
168
169int sendOffer(struct dhcpMessage *oldpacket);
170int sendNAK(struct dhcpMessage *oldpacket);
171int sendACK(struct dhcpMessage *oldpacket, uint32_t yiaddr);
172int send_inform(struct dhcpMessage *oldpacket);
173
174
175/*** files.h ***/
176
177struct config_keyword {
178 const char *keyword;
179 int (* const handler)(const char *line, void *var);
180 void *var;
181 const char *def;
182};
183
184int read_config(const char *file);
185void write_leases(void);
186void read_leases(const char *file);
187struct option_set *find_option(struct option_set *opt_list, char code);
188
189
190#endif
diff --git a/networking/udhcp/dhcprelay.c b/networking/udhcp/dhcprelay.c
new file mode 100644
index 000000000..e3a816886
--- /dev/null
+++ b/networking/udhcp/dhcprelay.c
@@ -0,0 +1,340 @@
1/* vi: set sw=4 ts=4: */
2/* Port to Busybox Copyright (C) 2006 Jesse Dutton <jessedutton@gmail.com>
3 *
4 * Licensed under GPL v2, see file LICENSE in this tarball for details.
5 *
6 * DHCP Relay for 'DHCPv4 Configuration of IPSec Tunnel Mode' support
7 * Copyright (C) 2002 Mario Strasser <mast@gmx.net>,
8 * Zuercher Hochschule Winterthur,
9 * Netbeat AG
10 * Upstream has GPL v2 or later
11 */
12
13#include "common.h"
14#include "dhcpd.h"
15#include "options.h"
16
17/* constants */
18#define SELECT_TIMEOUT 5 /* select timeout in sec. */
19#define MAX_LIFETIME 2*60 /* lifetime of an xid entry in sec. */
20#define MAX_INTERFACES 9
21
22
23/* This list holds information about clients. The xid_* functions manipulate this list. */
24static struct xid_item {
25 u_int32_t xid;
26 struct sockaddr_in ip;
27 int client;
28 time_t timestamp;
29 struct xid_item *next;
30} dhcprelay_xid_list = {0, {0}, 0, 0, NULL};
31
32
33static struct xid_item * xid_add(u_int32_t xid, struct sockaddr_in *ip, int client)
34{
35 struct xid_item *item;
36
37 /* create new xid entry */
38 item = xmalloc(sizeof(struct xid_item));
39
40 /* add xid entry */
41 item->ip = *ip;
42 item->xid = xid;
43 item->client = client;
44 item->timestamp = time(NULL);
45 item->next = dhcprelay_xid_list.next;
46 dhcprelay_xid_list.next = item;
47
48 return item;
49}
50
51
52static void xid_expire(void)
53{
54 struct xid_item *item = dhcprelay_xid_list.next;
55 struct xid_item *last = &dhcprelay_xid_list;
56 time_t current_time = time(NULL);
57
58 while (item != NULL) {
59 if ((current_time-item->timestamp) > MAX_LIFETIME) {
60 last->next = item->next;
61 free(item);
62 item = last->next;
63 } else {
64 last = item;
65 item = item->next;
66 }
67 }
68}
69
70static struct xid_item * xid_find(u_int32_t xid)
71{
72 struct xid_item *item = dhcprelay_xid_list.next;
73 while (item != NULL) {
74 if (item->xid == xid) {
75 return item;
76 }
77 item = item->next;
78 }
79 return NULL;
80}
81
82static void xid_del(u_int32_t xid)
83{
84 struct xid_item *item = dhcprelay_xid_list.next;
85 struct xid_item *last = &dhcprelay_xid_list;
86 while (item != NULL) {
87 if (item->xid == xid) {
88 last->next = item->next;
89 free(item);
90 item = last->next;
91 } else {
92 last = item;
93 item = item->next;
94 }
95 }
96}
97
98
99/**
100 * get_dhcp_packet_type - gets the message type of a dhcp packet
101 * p - pointer to the dhcp packet
102 * returns the message type on success, -1 otherwise
103 */
104static int get_dhcp_packet_type(struct dhcpMessage *p)
105{
106 u_char *op;
107
108 /* it must be either a BOOTREQUEST or a BOOTREPLY */
109 if (p->op != BOOTREQUEST && p->op != BOOTREPLY)
110 return -1;
111 /* get message type option */
112 op = get_option(p, DHCP_MESSAGE_TYPE);
113 if (op != NULL)
114 return op[0];
115 return -1;
116}
117
118/**
119 * signal_handler - handles signals ;-)
120 * sig - sent signal
121 */
122static int dhcprelay_stopflag;
123static void dhcprelay_signal_handler(int sig)
124{
125 dhcprelay_stopflag = 1;
126}
127
128/**
129 * get_client_devices - parses the devices list
130 * dev_list - comma separated list of devices
131 * returns array
132 */
133static char ** get_client_devices(char *dev_list, int *client_number)
134{
135 char *s, *list, **client_dev;
136 int i, cn;
137
138 /* copy list */
139 list = xstrdup(dev_list);
140 if (list == NULL) return NULL;
141
142 /* get number of items */
143 for (s = dev_list, cn = 1; *s; s++)
144 if (*s == ',')
145 cn++;
146
147 client_dev = xzalloc(cn * sizeof(*client_dev));
148
149 /* parse list */
150 s = strtok(list, ",");
151 i = 0;
152 while (s != NULL) {
153 client_dev[i++] = xstrdup(s);
154 s = strtok(NULL, ",");
155 }
156
157 /* free copy and exit */
158 free(list);
159 *client_number = cn;
160 return client_dev;
161}
162
163
164/* Creates listen sockets (in fds) and returns the number allocated. */
165static int init_sockets(char **client, int num_clients,
166 char *server, int *fds, int *max_socket)
167{
168 int i;
169
170 /* talk to real server on bootps */
171 fds[0] = listen_socket(htonl(INADDR_ANY), 67, server);
172 if (fds[0] < 0) return -1;
173 *max_socket = fds[0];
174
175 /* array starts at 1 since server is 0 */
176 num_clients++;
177
178 for (i=1; i < num_clients; i++) {
179 /* listen for clients on bootps */
180 fds[i] = listen_socket(htonl(INADDR_ANY), 67, client[i-1]);
181 if (fds[i] < 0) return -1;
182 if (fds[i] > *max_socket) *max_socket = fds[i];
183 }
184
185 return i;
186}
187
188
189/**
190 * pass_on() - forwards dhcp packets from client to server
191 * p - packet to send
192 * client - number of the client
193 */
194static void pass_on(struct dhcpMessage *p, int packet_len, int client, int *fds,
195 struct sockaddr_in *client_addr, struct sockaddr_in *server_addr)
196{
197 int res, type;
198 struct xid_item *item;
199
200 /* check packet_type */
201 type = get_dhcp_packet_type(p);
202 if (type != DHCPDISCOVER && type != DHCPREQUEST
203 && type != DHCPDECLINE && type != DHCPRELEASE
204 && type != DHCPINFORM
205 ) {
206 return;
207 }
208
209 /* create new xid entry */
210 item = xid_add(p->xid, client_addr, client);
211
212 /* forward request to LAN (server) */
213 res = sendto(fds[0], p, packet_len, 0, (struct sockaddr*)server_addr,
214 sizeof(struct sockaddr_in));
215 if (res != packet_len) {
216 bb_perror_msg("pass_on");
217 return;
218 }
219}
220
221/**
222 * pass_back() - forwards dhcp packets from server to client
223 * p - packet to send
224 */
225static void pass_back(struct dhcpMessage *p, int packet_len, int *fds)
226{
227 int res, type;
228 struct xid_item *item;
229
230 /* check xid */
231 item = xid_find(p->xid);
232 if (!item) {
233 return;
234 }
235
236 /* check packet type */
237 type = get_dhcp_packet_type(p);
238 if (type != DHCPOFFER && type != DHCPACK && type != DHCPNAK) {
239 return;
240 }
241
242 if (item->ip.sin_addr.s_addr == htonl(INADDR_ANY))
243 item->ip.sin_addr.s_addr = htonl(INADDR_BROADCAST);
244 if (item->client > MAX_INTERFACES)
245 return;
246 res = sendto(fds[item->client], p, packet_len, 0, (struct sockaddr*)(&item->ip),
247 sizeof(item->ip));
248 if (res != packet_len) {
249 bb_perror_msg("pass_back");
250 return;
251 }
252
253 /* remove xid entry */
254 xid_del(p->xid);
255}
256
257static void dhcprelay_loop(int *fds, int num_sockets, int max_socket, char **clients,
258 struct sockaddr_in *server_addr, uint32_t gw_ip)
259{
260 struct dhcpMessage dhcp_msg;
261 fd_set rfds;
262 size_t packlen, addr_size;
263 struct sockaddr_in client_addr;
264 struct timeval tv;
265 int i;
266
267 while (!dhcprelay_stopflag) {
268 FD_ZERO(&rfds);
269 for (i = 0; i < num_sockets; i++)
270 FD_SET(fds[i], &rfds);
271 tv.tv_sec = SELECT_TIMEOUT;
272 tv.tv_usec = 0;
273 if (select(max_socket + 1, &rfds, NULL, NULL, &tv) > 0) {
274 /* server */
275 if (FD_ISSET(fds[0], &rfds)) {
276 packlen = udhcp_get_packet(&dhcp_msg, fds[0]);
277 if (packlen > 0) {
278 pass_back(&dhcp_msg, packlen, fds);
279 }
280 }
281 for (i = 1; i < num_sockets; i++) {
282 /* clients */
283 if (!FD_ISSET(fds[i], &rfds))
284 continue;
285 addr_size = sizeof(struct sockaddr_in);
286 packlen = recvfrom(fds[i], &dhcp_msg, sizeof(dhcp_msg), 0,
287 (struct sockaddr *)(&client_addr), &addr_size);
288 if (packlen <= 0)
289 continue;
290 if (read_interface(clients[i-1], NULL, &dhcp_msg.giaddr, NULL) < 0)
291 dhcp_msg.giaddr = gw_ip;
292 pass_on(&dhcp_msg, packlen, i, fds, &client_addr, server_addr);
293 }
294 }
295 xid_expire();
296 }
297}
298
299int dhcprelay_main(int argc, char **argv)
300{
301 int i, num_sockets, max_socket, fds[MAX_INTERFACES];
302 uint32_t gw_ip;
303 char **clients;
304 struct sockaddr_in server_addr;
305
306 server_addr.sin_family = AF_INET;
307 server_addr.sin_port = htons(67);
308 if (argc == 4) {
309 if (!inet_aton(argv[3], &server_addr.sin_addr))
310 bb_perror_msg_and_die("didn't grok server");
311 } else if (argc == 3) {
312 server_addr.sin_addr.s_addr = htonl(INADDR_BROADCAST);
313 } else {
314 bb_show_usage();
315 }
316 clients = get_client_devices(argv[1], &num_sockets);
317 if (!clients) return 0;
318
319 signal(SIGTERM, dhcprelay_signal_handler);
320 signal(SIGQUIT, dhcprelay_signal_handler);
321 signal(SIGINT, dhcprelay_signal_handler);
322
323 num_sockets = init_sockets(clients, num_sockets, argv[2], fds, &max_socket);
324 if (num_sockets == -1)
325 bb_perror_msg_and_die("init_sockets() failed");
326
327 if (read_interface(argv[2], NULL, &gw_ip, NULL) == -1)
328 return 1;
329
330 dhcprelay_loop(fds, num_sockets, max_socket, clients, &server_addr, gw_ip);
331
332 if (ENABLE_FEATURE_CLEAN_UP) {
333 for (i = 0; i < num_sockets; i++) {
334 close(fds[i]);
335 free(clients[i]);
336 }
337 }
338
339 return 0;
340}
diff --git a/networking/udhcp/dumpleases.c b/networking/udhcp/dumpleases.c
new file mode 100644
index 000000000..a0e81bb13
--- /dev/null
+++ b/networking/udhcp/dumpleases.c
@@ -0,0 +1,74 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
4 */
5#include <getopt.h>
6
7#include "common.h"
8#include "dhcpd.h"
9
10
11#define REMAINING 0
12#define ABSOLUTE 1
13
14int dumpleases_main(int argc, char *argv[])
15{
16 int fp;
17 int i, c, mode = REMAINING;
18 unsigned long expires;
19 const char *file = LEASES_FILE;
20 struct dhcpOfferedAddr lease;
21 struct in_addr addr;
22
23 static const struct option options[] = {
24 {"absolute", 0, 0, 'a'},
25 {"remaining", 0, 0, 'r'},
26 {"file", 1, 0, 'f'},
27 {0, 0, 0, 0}
28 };
29
30 while (1) {
31 int option_index = 0;
32 c = getopt_long(argc, argv, "arf:", options, &option_index);
33 if (c == -1) break;
34
35 switch (c) {
36 case 'a': mode = ABSOLUTE; break;
37 case 'r': mode = REMAINING; break;
38 case 'f':
39 file = optarg;
40 break;
41 default:
42 bb_show_usage();
43 }
44 }
45
46 fp = xopen(file, O_RDONLY);
47
48 printf("Mac Address IP-Address Expires %s\n", mode == REMAINING ? "in" : "at");
49 /* "00:00:00:00:00:00 255.255.255.255 Wed Jun 30 21:49:08 1993" */
50 while (full_read(fp, &lease, sizeof(lease)) == sizeof(lease)) {
51 printf(":%02x"+1, lease.chaddr[0]);
52 for (i = 1; i < 6; i++) {
53 printf(":%02x", lease.chaddr[i]);
54 }
55 addr.s_addr = lease.yiaddr;
56 printf(" %-15s ", inet_ntoa(addr));
57 expires = ntohl(lease.expires);
58 if (mode == REMAINING) {
59 if (!expires)
60 printf("expired\n");
61 else {
62 unsigned d, h, m;
63 d = expires / (24*60*60); expires %= (24*60*60);
64 h = expires / (60*60); expires %= (60*60);
65 m = expires / 60; expires %= 60;
66 if (d) printf("%u days ", d);
67 printf("%02u:%02u:%02u\n", h, m, (unsigned)expires);
68 }
69 } else fputs(ctime(&expires), stdout);
70 }
71 /* close(fp); */
72
73 return 0;
74}
diff --git a/networking/udhcp/files.c b/networking/udhcp/files.c
new file mode 100644
index 000000000..5e399e1f8
--- /dev/null
+++ b/networking/udhcp/files.c
@@ -0,0 +1,395 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * files.c -- DHCP server file manipulation *
4 * Rewrite by Russ Dill <Russ.Dill@asu.edu> July 2001
5 */
6
7#include <netinet/ether.h>
8
9#include "common.h"
10#include "dhcpd.h"
11#include "options.h"
12
13
14/*
15 * Domain names may have 254 chars, and string options can be 254
16 * chars long. However, 80 bytes will be enough for most, and won't
17 * hog up memory. If you have a special application, change it
18 */
19#define READ_CONFIG_BUF_SIZE 80
20
21/* on these functions, make sure you datatype matches */
22static int read_ip(const char *line, void *arg)
23{
24 struct in_addr *addr = arg;
25 struct hostent *host;
26 int retval = 1;
27
28 if (!inet_aton(line, addr)) {
29 host = gethostbyname(line);
30 if (host)
31 addr->s_addr = *((unsigned long *) host->h_addr_list[0]);
32 else retval = 0;
33 }
34 return retval;
35}
36
37static int read_mac(const char *line, void *arg)
38{
39 uint8_t *mac_bytes = arg;
40 struct ether_addr *temp_ether_addr;
41 int retval = 1;
42
43 temp_ether_addr = ether_aton(line);
44
45 if (temp_ether_addr == NULL)
46 retval = 0;
47 else
48 memcpy(mac_bytes, temp_ether_addr, 6);
49
50 return retval;
51}
52
53
54static int read_str(const char *line, void *arg)
55{
56 char **dest = arg;
57
58 free(*dest);
59 *dest = strdup(line);
60
61 return 1;
62}
63
64
65static int read_u32(const char *line, void *arg)
66{
67 *((uint32_t*)arg) = bb_strtou32(line, NULL, 10);
68 return errno == 0;
69}
70
71
72static int read_yn(const char *line, void *arg)
73{
74 char *dest = arg;
75 int retval = 1;
76
77 if (!strcasecmp("yes", line))
78 *dest = 1;
79 else if (!strcasecmp("no", line))
80 *dest = 0;
81 else retval = 0;
82
83 return retval;
84}
85
86
87/* find option 'code' in opt_list */
88struct option_set *find_option(struct option_set *opt_list, char code)
89{
90 while (opt_list && opt_list->data[OPT_CODE] < code)
91 opt_list = opt_list->next;
92
93 if (opt_list && opt_list->data[OPT_CODE] == code) return opt_list;
94 else return NULL;
95}
96
97
98/* add an option to the opt_list */
99static void attach_option(struct option_set **opt_list,
100 const struct dhcp_option *option, char *buffer, int length)
101{
102 struct option_set *existing, *new, **curr;
103
104 /* add it to an existing option */
105 existing = find_option(*opt_list, option->code);
106 if (existing) {
107 DEBUG("Attaching option %s to existing member of list", option->name);
108 if (option->flags & OPTION_LIST) {
109 if (existing->data[OPT_LEN] + length <= 255) {
110 existing->data = realloc(existing->data,
111 existing->data[OPT_LEN] + length + 2);
112 memcpy(existing->data + existing->data[OPT_LEN] + 2, buffer, length);
113 existing->data[OPT_LEN] += length;
114 } /* else, ignore the data, we could put this in a second option in the future */
115 } /* else, ignore the new data */
116 } else {
117 DEBUG("Attaching option %s to list", option->name);
118
119 /* make a new option */
120 new = xmalloc(sizeof(struct option_set));
121 new->data = xmalloc(length + 2);
122 new->data[OPT_CODE] = option->code;
123 new->data[OPT_LEN] = length;
124 memcpy(new->data + 2, buffer, length);
125
126 curr = opt_list;
127 while (*curr && (*curr)->data[OPT_CODE] < option->code)
128 curr = &(*curr)->next;
129
130 new->next = *curr;
131 *curr = new;
132 }
133}
134
135
136/* read a dhcp option and add it to opt_list */
137static int read_opt(const char *const_line, void *arg)
138{
139 struct option_set **opt_list = arg;
140 char *opt, *val, *endptr;
141 const struct dhcp_option *option;
142 int retval = 0, length;
143 char buffer[8];
144 char *line;
145 uint16_t *result_u16 = (uint16_t *) buffer;
146 uint32_t *result_u32 = (uint32_t *) buffer;
147
148 /* Cheat, the only const line we'll actually get is "" */
149 line = (char *) const_line;
150 opt = strtok(line, " \t=");
151 if (!opt) return 0;
152
153 option = dhcp_options;
154 while (1) {
155 if (!option->code)
156 return 0;
157 if (!strcasecmp(option->name, opt))
158 break;
159 option++;
160 }
161
162 do {
163 val = strtok(NULL, ", \t");
164 if (!val) break;
165 length = option_lengths[option->flags & TYPE_MASK];
166 retval = 0;
167 opt = buffer; /* new meaning for variable opt */
168 switch (option->flags & TYPE_MASK) {
169 case OPTION_IP:
170 retval = read_ip(val, buffer);
171 break;
172 case OPTION_IP_PAIR:
173 retval = read_ip(val, buffer);
174 if (!(val = strtok(NULL, ", \t/-"))) retval = 0;
175 if (retval) retval = read_ip(val, buffer + 4);
176 break;
177 case OPTION_STRING:
178 length = strlen(val);
179 if (length > 0) {
180 if (length > 254) length = 254;
181 opt = val;
182 retval = 1;
183 }
184 break;
185 case OPTION_BOOLEAN:
186 retval = read_yn(val, buffer);
187 break;
188 case OPTION_U8:
189 buffer[0] = strtoul(val, &endptr, 0);
190 retval = (endptr[0] == '\0');
191 break;
192 case OPTION_U16:
193 *result_u16 = htons(strtoul(val, &endptr, 0));
194 retval = (endptr[0] == '\0');
195 break;
196 case OPTION_S16:
197 *result_u16 = htons(strtol(val, &endptr, 0));
198 retval = (endptr[0] == '\0');
199 break;
200 case OPTION_U32:
201 *result_u32 = htonl(strtoul(val, &endptr, 0));
202 retval = (endptr[0] == '\0');
203 break;
204 case OPTION_S32:
205 *result_u32 = htonl(strtol(val, &endptr, 0));
206 retval = (endptr[0] == '\0');
207 break;
208 default:
209 break;
210 }
211 if (retval)
212 attach_option(opt_list, option, opt, length);
213 } while (retval && option->flags & OPTION_LIST);
214 return retval;
215}
216
217static int read_staticlease(const char *const_line, void *arg)
218{
219 char *line;
220 char *mac_string;
221 char *ip_string;
222 uint8_t *mac_bytes;
223 uint32_t *ip;
224
225
226 /* Allocate memory for addresses */
227 mac_bytes = xmalloc(sizeof(unsigned char) * 8);
228 ip = xmalloc(sizeof(uint32_t));
229
230 /* Read mac */
231 line = (char *) const_line;
232 mac_string = strtok(line, " \t");
233 read_mac(mac_string, mac_bytes);
234
235 /* Read ip */
236 ip_string = strtok(NULL, " \t");
237 read_ip(ip_string, ip);
238
239 addStaticLease(arg, mac_bytes, ip);
240
241 if (ENABLE_FEATURE_UDHCP_DEBUG) printStaticLeases(arg);
242
243 return 1;
244}
245
246
247static const struct config_keyword keywords[] = {
248 /* keyword handler variable address default */
249 {"start", read_ip, &(server_config.start), "192.168.0.20"},
250 {"end", read_ip, &(server_config.end), "192.168.0.254"},
251 {"interface", read_str, &(server_config.interface), "eth0"},
252 {"option", read_opt, &(server_config.options), ""},
253 {"opt", read_opt, &(server_config.options), ""},
254 {"max_leases", read_u32, &(server_config.max_leases), "254"},
255 {"remaining", read_yn, &(server_config.remaining), "yes"},
256 {"auto_time", read_u32, &(server_config.auto_time), "7200"},
257 {"decline_time",read_u32, &(server_config.decline_time),"3600"},
258 {"conflict_time",read_u32,&(server_config.conflict_time),"3600"},
259 {"offer_time", read_u32, &(server_config.offer_time), "60"},
260 {"min_lease", read_u32, &(server_config.min_lease), "60"},
261 {"lease_file", read_str, &(server_config.lease_file), LEASES_FILE},
262 {"pidfile", read_str, &(server_config.pidfile), "/var/run/udhcpd.pid"},
263 {"notify_file", read_str, &(server_config.notify_file), ""},
264 {"siaddr", read_ip, &(server_config.siaddr), "0.0.0.0"},
265 {"sname", read_str, &(server_config.sname), ""},
266 {"boot_file", read_str, &(server_config.boot_file), ""},
267 {"static_lease",read_staticlease, &(server_config.static_leases), ""},
268 /*ADDME: static lease */
269 {"", NULL, NULL, ""}
270};
271
272
273int read_config(const char *file)
274{
275 FILE *in;
276 char buffer[READ_CONFIG_BUF_SIZE], *token, *line;
277 int i, lm = 0;
278
279 for (i = 0; keywords[i].keyword[0]; i++)
280 if (keywords[i].def[0])
281 keywords[i].handler(keywords[i].def, keywords[i].var);
282
283 in = fopen(file, "r");
284 if (!in) {
285 bb_error_msg("cannot open config file: %s", file);
286 return 0;
287 }
288
289 while (fgets(buffer, READ_CONFIG_BUF_SIZE, in)) {
290 char debug_orig[READ_CONFIG_BUF_SIZE];
291 char *p;
292
293 lm++;
294 p = strchr(buffer, '\n');
295 if (p) *p = '\0';
296 if (ENABLE_FEATURE_UDHCP_DEBUG) strcpy(debug_orig, buffer);
297 p = strchr(buffer, '#');
298 if (p) *p = '\0';
299
300 if (!(token = strtok(buffer, " \t"))) continue;
301 if (!(line = strtok(NULL, ""))) continue;
302
303 /* eat leading whitespace */
304 line = skip_whitespace(line);
305 /* eat trailing whitespace */
306 i = strlen(line) - 1;
307 while (i >= 0 && isspace(line[i]))
308 line[i--] = '\0';
309
310 for (i = 0; keywords[i].keyword[0]; i++)
311 if (!strcasecmp(token, keywords[i].keyword))
312 if (!keywords[i].handler(line, keywords[i].var)) {
313 bb_error_msg("cannot parse line %d of %s", lm, file);
314 if (ENABLE_FEATURE_UDHCP_DEBUG)
315 bb_error_msg("cannot parse '%s'", debug_orig);
316 /* reset back to the default value */
317 keywords[i].handler(keywords[i].def, keywords[i].var);
318 }
319 }
320 fclose(in);
321 return 1;
322}
323
324
325void write_leases(void)
326{
327 int fp;
328 unsigned i;
329 time_t curr = time(0);
330 unsigned long tmp_time;
331
332 fp = open(server_config.lease_file, O_WRONLY|O_CREAT|O_TRUNC, 0666);
333 if (fp < 0) {
334 bb_error_msg("cannot open %s for writing", server_config.lease_file);
335 return;
336 }
337
338 for (i = 0; i < server_config.max_leases; i++) {
339 if (leases[i].yiaddr != 0) {
340
341 /* screw with the time in the struct, for easier writing */
342 tmp_time = leases[i].expires;
343
344 if (server_config.remaining) {
345 if (lease_expired(&(leases[i])))
346 leases[i].expires = 0;
347 else leases[i].expires -= curr;
348 } /* else stick with the time we got */
349 leases[i].expires = htonl(leases[i].expires);
350 // FIXME: error check??
351 full_write(fp, &leases[i], sizeof(leases[i]));
352
353 /* then restore it when done */
354 leases[i].expires = tmp_time;
355 }
356 }
357 close(fp);
358
359 if (server_config.notify_file) {
360 char *cmd = xasprintf("%s %s", server_config.notify_file, server_config.lease_file);
361 system(cmd);
362 free(cmd);
363 }
364}
365
366
367void read_leases(const char *file)
368{
369 int fp;
370 unsigned int i = 0;
371 struct dhcpOfferedAddr lease;
372
373 fp = open(file, O_RDONLY);
374 if (fp < 0) {
375 bb_error_msg("cannot open %s for reading", file);
376 return;
377 }
378
379 while (i < server_config.max_leases
380 && full_read(fp, &lease, sizeof(lease)) == sizeof(lease)
381 ) {
382 /* ADDME: is it a static lease */
383 if (lease.yiaddr >= server_config.start && lease.yiaddr <= server_config.end) {
384 lease.expires = ntohl(lease.expires);
385 if (!server_config.remaining) lease.expires -= time(0);
386 if (!(add_lease(lease.chaddr, lease.yiaddr, lease.expires))) {
387 bb_error_msg("too many leases while loading %s", file);
388 break;
389 }
390 i++;
391 }
392 }
393 DEBUG("Read %d leases", i);
394 close(fp);
395}
diff --git a/networking/udhcp/leases.c b/networking/udhcp/leases.c
new file mode 100644
index 000000000..2f7847d74
--- /dev/null
+++ b/networking/udhcp/leases.c
@@ -0,0 +1,145 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * leases.c -- tools to manage DHCP leases
4 * Russ Dill <Russ.Dill@asu.edu> July 2001
5 */
6
7#include "common.h"
8#include "dhcpd.h"
9
10
11uint8_t blank_chaddr[] = {[0 ... 15] = 0};
12
13/* clear every lease out that chaddr OR yiaddr matches and is nonzero */
14void clear_lease(uint8_t *chaddr, uint32_t yiaddr)
15{
16 unsigned int i, j;
17
18 for (j = 0; j < 16 && !chaddr[j]; j++);
19
20 for (i = 0; i < server_config.max_leases; i++)
21 if ((j != 16 && !memcmp(leases[i].chaddr, chaddr, 16)) ||
22 (yiaddr && leases[i].yiaddr == yiaddr)) {
23 memset(&(leases[i]), 0, sizeof(struct dhcpOfferedAddr));
24 }
25}
26
27
28/* add a lease into the table, clearing out any old ones */
29struct dhcpOfferedAddr *add_lease(uint8_t *chaddr, uint32_t yiaddr, unsigned long lease)
30{
31 struct dhcpOfferedAddr *oldest;
32
33 /* clean out any old ones */
34 clear_lease(chaddr, yiaddr);
35
36 oldest = oldest_expired_lease();
37
38 if (oldest) {
39 memcpy(oldest->chaddr, chaddr, 16);
40 oldest->yiaddr = yiaddr;
41 oldest->expires = time(0) + lease;
42 }
43
44 return oldest;
45}
46
47
48/* true if a lease has expired */
49int lease_expired(struct dhcpOfferedAddr *lease)
50{
51 return (lease->expires < (unsigned long) time(0));
52}
53
54
55/* Find the oldest expired lease, NULL if there are no expired leases */
56struct dhcpOfferedAddr *oldest_expired_lease(void)
57{
58 struct dhcpOfferedAddr *oldest = NULL;
59 unsigned long oldest_lease = time(0);
60 unsigned int i;
61
62
63 for (i = 0; i < server_config.max_leases; i++)
64 if (oldest_lease > leases[i].expires) {
65 oldest_lease = leases[i].expires;
66 oldest = &(leases[i]);
67 }
68 return oldest;
69
70}
71
72
73/* Find the first lease that matches chaddr, NULL if no match */
74struct dhcpOfferedAddr *find_lease_by_chaddr(uint8_t *chaddr)
75{
76 unsigned int i;
77
78 for (i = 0; i < server_config.max_leases; i++)
79 if (!memcmp(leases[i].chaddr, chaddr, 16)) return &(leases[i]);
80
81 return NULL;
82}
83
84
85/* Find the first lease that matches yiaddr, NULL is no match */
86struct dhcpOfferedAddr *find_lease_by_yiaddr(uint32_t yiaddr)
87{
88 unsigned int i;
89
90 for (i = 0; i < server_config.max_leases; i++)
91 if (leases[i].yiaddr == yiaddr) return &(leases[i]);
92
93 return NULL;
94}
95
96
97/* check is an IP is taken, if it is, add it to the lease table */
98static int check_ip(uint32_t addr)
99{
100 struct in_addr temp;
101
102 if (arpping(addr, server_config.server, server_config.arp, server_config.interface) == 0) {
103 temp.s_addr = addr;
104 bb_info_msg("%s belongs to someone, reserving it for %ld seconds",
105 inet_ntoa(temp), server_config.conflict_time);
106 add_lease(blank_chaddr, addr, server_config.conflict_time);
107 return 1;
108 } else return 0;
109}
110
111
112/* find an assignable address, it check_expired is true, we check all the expired leases as well.
113 * Maybe this should try expired leases by age... */
114uint32_t find_address(int check_expired)
115{
116 uint32_t addr, ret;
117 struct dhcpOfferedAddr *lease = NULL;
118
119 addr = ntohl(server_config.start); /* addr is in host order here */
120 for (;addr <= ntohl(server_config.end); addr++) {
121
122 /* ie, 192.168.55.0 */
123 if (!(addr & 0xFF)) continue;
124
125 /* ie, 192.168.55.255 */
126 if ((addr & 0xFF) == 0xFF) continue;
127
128 /* Only do if it isn't an assigned as a static lease */
129 if (!reservedIp(server_config.static_leases, htonl(addr))) {
130
131 /* lease is not taken */
132 ret = htonl(addr);
133 lease = find_lease_by_yiaddr(ret);
134
135 /* no lease or it expired and we are checking for expired leases */
136 if ( (!lease || (check_expired && lease_expired(lease)))
137 && /* and it isn't on the network */ !check_ip(ret)
138 ) {
139 return ret;
140 break;
141 }
142 }
143 }
144 return 0;
145}
diff --git a/networking/udhcp/options.c b/networking/udhcp/options.c
new file mode 100644
index 000000000..4a46da579
--- /dev/null
+++ b/networking/udhcp/options.c
@@ -0,0 +1,174 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * options.c -- DHCP server option packet tools
4 * Rewrite by Russ Dill <Russ.Dill@asu.edu> July 2001
5 */
6
7#include "common.h"
8#include "dhcpd.h"
9#include "options.h"
10
11
12/* supported options are easily added here */
13const struct dhcp_option dhcp_options[] = {
14 /* name[10] flags code */
15 {"subnet", OPTION_IP | OPTION_REQ, 0x01},
16 {"timezone", OPTION_S32, 0x02},
17 {"router", OPTION_IP | OPTION_LIST | OPTION_REQ, 0x03},
18 {"timesvr", OPTION_IP | OPTION_LIST, 0x04},
19 {"namesvr", OPTION_IP | OPTION_LIST, 0x05},
20 {"dns", OPTION_IP | OPTION_LIST | OPTION_REQ, 0x06},
21 {"logsvr", OPTION_IP | OPTION_LIST, 0x07},
22 {"cookiesvr", OPTION_IP | OPTION_LIST, 0x08},
23 {"lprsvr", OPTION_IP | OPTION_LIST, 0x09},
24 {"hostname", OPTION_STRING | OPTION_REQ, 0x0c},
25 {"bootsize", OPTION_U16, 0x0d},
26 {"domain", OPTION_STRING | OPTION_REQ, 0x0f},
27 {"swapsvr", OPTION_IP, 0x10},
28 {"rootpath", OPTION_STRING, 0x11},
29 {"ipttl", OPTION_U8, 0x17},
30 {"mtu", OPTION_U16, 0x1a},
31 {"broadcast", OPTION_IP | OPTION_REQ, 0x1c},
32 {"nisdomain", OPTION_STRING | OPTION_REQ, 0x28},
33 {"nissrv", OPTION_IP | OPTION_LIST | OPTION_REQ, 0x29},
34 {"ntpsrv", OPTION_IP | OPTION_LIST | OPTION_REQ, 0x2a},
35 {"wins", OPTION_IP | OPTION_LIST, 0x2c},
36 {"requestip", OPTION_IP, 0x32},
37 {"lease", OPTION_U32, 0x33},
38 {"dhcptype", OPTION_U8, 0x35},
39 {"serverid", OPTION_IP, 0x36},
40 {"message", OPTION_STRING, 0x38},
41 {"vendorclass", OPTION_STRING, 0x3C},
42 {"clientid", OPTION_STRING, 0x3D},
43 {"tftp", OPTION_STRING, 0x42},
44 {"bootfile", OPTION_STRING, 0x43},
45 {"userclass", OPTION_STRING, 0x4D},
46 /* MSIE's "Web Proxy Autodiscovery Protocol" support */
47 {"wpad", OPTION_STRING, 0xfc},
48 {"", 0x00, 0x00}
49};
50
51/* Lengths of the different option types */
52const unsigned char option_lengths[] = {
53 [OPTION_IP] = 4,
54 [OPTION_IP_PAIR] = 8,
55 [OPTION_BOOLEAN] = 1,
56 [OPTION_STRING] = 1,
57 [OPTION_U8] = 1,
58 [OPTION_U16] = 2,
59 [OPTION_S16] = 2,
60 [OPTION_U32] = 4,
61 [OPTION_S32] = 4
62};
63
64
65/* get an option with bounds checking (warning, not aligned). */
66uint8_t *get_option(struct dhcpMessage *packet, int code)
67{
68 int i, length;
69 uint8_t *optionptr;
70 int over = 0, done = 0, curr = OPTION_FIELD;
71
72 optionptr = packet->options;
73 i = 0;
74 length = 308;
75 while (!done) {
76 if (i >= length) {
77 bb_error_msg("bogus packet, option fields too long");
78 return NULL;
79 }
80 if (optionptr[i + OPT_CODE] == code) {
81 if (i + 1 + optionptr[i + OPT_LEN] >= length) {
82 bb_error_msg("bogus packet, option fields too long");
83 return NULL;
84 }
85 return optionptr + i + 2;
86 }
87 switch (optionptr[i + OPT_CODE]) {
88 case DHCP_PADDING:
89 i++;
90 break;
91 case DHCP_OPTION_OVER:
92 if (i + 1 + optionptr[i + OPT_LEN] >= length) {
93 bb_error_msg("bogus packet, option fields too long");
94 return NULL;
95 }
96 over = optionptr[i + 3];
97 i += optionptr[OPT_LEN] + 2;
98 break;
99 case DHCP_END:
100 if (curr == OPTION_FIELD && over & FILE_FIELD) {
101 optionptr = packet->file;
102 i = 0;
103 length = 128;
104 curr = FILE_FIELD;
105 } else if (curr == FILE_FIELD && over & SNAME_FIELD) {
106 optionptr = packet->sname;
107 i = 0;
108 length = 64;
109 curr = SNAME_FIELD;
110 } else done = 1;
111 break;
112 default:
113 i += optionptr[OPT_LEN + i] + 2;
114 }
115 }
116 return NULL;
117}
118
119
120/* return the position of the 'end' option (no bounds checking) */
121int end_option(uint8_t *optionptr)
122{
123 int i = 0;
124
125 while (optionptr[i] != DHCP_END) {
126 if (optionptr[i] == DHCP_PADDING) i++;
127 else i += optionptr[i + OPT_LEN] + 2;
128 }
129 return i;
130}
131
132
133/* add an option string to the options (an option string contains an option code,
134 * length, then data) */
135int add_option_string(uint8_t *optionptr, uint8_t *string)
136{
137 int end = end_option(optionptr);
138
139 /* end position + string length + option code/length + end option */
140 if (end + string[OPT_LEN] + 2 + 1 >= 308) {
141 bb_error_msg("option 0x%02x did not fit into the packet",
142 string[OPT_CODE]);
143 return 0;
144 }
145 DEBUG("adding option 0x%02x", string[OPT_CODE]);
146 memcpy(optionptr + end, string, string[OPT_LEN] + 2);
147 optionptr[end + string[OPT_LEN] + 2] = DHCP_END;
148 return string[OPT_LEN] + 2;
149}
150
151
152/* add a one to four byte option to a packet */
153int add_simple_option(uint8_t *optionptr, uint8_t code, uint32_t data)
154{
155 const struct dhcp_option *dh;
156
157 for (dh = dhcp_options; dh->code; dh++) {
158 if (dh->code == code) {
159 uint8_t option[6], len;
160
161 option[OPT_CODE] = code;
162 len = option_lengths[dh->flags & TYPE_MASK];
163 option[OPT_LEN] = len;
164 if (BB_BIG_ENDIAN) data <<= 8 * (4 - len);
165 /* This memcpy is for broken processors which can't
166 * handle a simple unaligned 32-bit assignment */
167 memcpy(&option[OPT_DATA], &data, 4);
168 return add_option_string(optionptr, option);
169 }
170 }
171
172 bb_error_msg("cannot add option 0x%02x", code);
173 return 0;
174}
diff --git a/networking/udhcp/options.h b/networking/udhcp/options.h
new file mode 100644
index 000000000..588504e5d
--- /dev/null
+++ b/networking/udhcp/options.h
@@ -0,0 +1,37 @@
1/* vi: set sw=4 ts=4: */
2/* options.h */
3#ifndef _OPTIONS_H
4#define _OPTIONS_H
5
6#define TYPE_MASK 0x0F
7
8enum {
9 OPTION_IP=1,
10 OPTION_IP_PAIR,
11 OPTION_STRING,
12 OPTION_BOOLEAN,
13 OPTION_U8,
14 OPTION_U16,
15 OPTION_S16,
16 OPTION_U32,
17 OPTION_S32
18};
19
20#define OPTION_REQ 0x10 /* have the client request this option */
21#define OPTION_LIST 0x20 /* There can be a list of 1 or more of these */
22
23struct dhcp_option {
24 char name[12];
25 char flags;
26 uint8_t code;
27};
28
29extern const struct dhcp_option dhcp_options[];
30extern const unsigned char option_lengths[];
31
32uint8_t *get_option(struct dhcpMessage *packet, int code);
33int end_option(uint8_t *optionptr);
34int add_option_string(uint8_t *optionptr, uint8_t *string);
35int add_simple_option(uint8_t *optionptr, uint8_t code, uint32_t data);
36
37#endif
diff --git a/networking/udhcp/packet.c b/networking/udhcp/packet.c
new file mode 100644
index 000000000..dec9d0ab3
--- /dev/null
+++ b/networking/udhcp/packet.c
@@ -0,0 +1,211 @@
1/* vi: set sw=4 ts=4: */
2
3#include <netinet/in.h>
4#if (__GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1) || defined _NEWLIB_VERSION
5#include <netpacket/packet.h>
6#include <net/ethernet.h>
7#else
8#include <asm/types.h>
9#include <linux/if_packet.h>
10#include <linux/if_ether.h>
11#endif
12
13#include "common.h"
14#include "dhcpd.h"
15#include "options.h"
16
17
18void udhcp_init_header(struct dhcpMessage *packet, char type)
19{
20 memset(packet, 0, sizeof(struct dhcpMessage));
21 switch (type) {
22 case DHCPDISCOVER:
23 case DHCPREQUEST:
24 case DHCPRELEASE:
25 case DHCPINFORM:
26 packet->op = BOOTREQUEST;
27 break;
28 case DHCPOFFER:
29 case DHCPACK:
30 case DHCPNAK:
31 packet->op = BOOTREPLY;
32 }
33 packet->htype = ETH_10MB;
34 packet->hlen = ETH_10MB_LEN;
35 packet->cookie = htonl(DHCP_MAGIC);
36 packet->options[0] = DHCP_END;
37 add_simple_option(packet->options, DHCP_MESSAGE_TYPE, type);
38}
39
40
41/* read a packet from socket fd, return -1 on read error, -2 on packet error */
42int udhcp_get_packet(struct dhcpMessage *packet, int fd)
43{
44 static const char broken_vendors[][8] = {
45 "MSFT 98",
46 ""
47 };
48 int bytes;
49 int i;
50 char unsigned *vendor;
51
52 memset(packet, 0, sizeof(struct dhcpMessage));
53 bytes = read(fd, packet, sizeof(struct dhcpMessage));
54 if (bytes < 0) {
55 DEBUG("cannot read on listening socket, ignoring");
56 return -1;
57 }
58
59 if (ntohl(packet->cookie) != DHCP_MAGIC) {
60 bb_error_msg("received bogus message, ignoring");
61 return -2;
62 }
63 DEBUG("Received a packet");
64
65 if (packet->op == BOOTREQUEST && (vendor = get_option(packet, DHCP_VENDOR))) {
66 for (i = 0; broken_vendors[i][0]; i++) {
67 if (vendor[OPT_LEN - 2] == (uint8_t)strlen(broken_vendors[i])
68 && !strncmp((char*)vendor, broken_vendors[i], vendor[OPT_LEN - 2])
69 ) {
70 DEBUG("broken client (%s), forcing broadcast",
71 broken_vendors[i]);
72 packet->flags |= htons(BROADCAST_FLAG);
73 }
74 }
75 }
76
77 return bytes;
78}
79
80
81uint16_t udhcp_checksum(void *addr, int count)
82{
83 /* Compute Internet Checksum for "count" bytes
84 * beginning at location "addr".
85 */
86 int32_t sum = 0;
87 uint16_t *source = (uint16_t *) addr;
88
89 while (count > 1) {
90 /* This is the inner loop */
91 sum += *source++;
92 count -= 2;
93 }
94
95 /* Add left-over byte, if any */
96 if (count > 0) {
97 /* Make sure that the left-over byte is added correctly both
98 * with little and big endian hosts */
99 uint16_t tmp = 0;
100 *(uint8_t *) (&tmp) = * (uint8_t *) source;
101 sum += tmp;
102 }
103 /* Fold 32-bit sum to 16 bits */
104 while (sum >> 16)
105 sum = (sum & 0xffff) + (sum >> 16);
106
107 return ~sum;
108}
109
110
111/* Construct a ip/udp header for a packet, and specify the source and dest hardware address */
112void BUG_sizeof_struct_udp_dhcp_packet_must_be_576(void);
113int udhcp_raw_packet(struct dhcpMessage *payload,
114 uint32_t source_ip, int source_port,
115 uint32_t dest_ip, int dest_port, uint8_t *dest_arp, int ifindex)
116{
117 int fd;
118 int result;
119 struct sockaddr_ll dest;
120 struct udp_dhcp_packet packet;
121
122 fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP));
123 if (fd < 0) {
124 bb_perror_msg("socket");
125 return -1;
126 }
127
128 memset(&dest, 0, sizeof(dest));
129 memset(&packet, 0, sizeof(packet));
130
131 dest.sll_family = AF_PACKET;
132 dest.sll_protocol = htons(ETH_P_IP);
133 dest.sll_ifindex = ifindex;
134 dest.sll_halen = 6;
135 memcpy(dest.sll_addr, dest_arp, 6);
136 if (bind(fd, (struct sockaddr *)&dest, sizeof(struct sockaddr_ll)) < 0) {
137 bb_perror_msg("bind");
138 close(fd);
139 return -1;
140 }
141
142 packet.ip.protocol = IPPROTO_UDP;
143 packet.ip.saddr = source_ip;
144 packet.ip.daddr = dest_ip;
145 packet.udp.source = htons(source_port);
146 packet.udp.dest = htons(dest_port);
147 packet.udp.len = htons(sizeof(packet.udp) + sizeof(struct dhcpMessage)); /* cheat on the psuedo-header */
148 packet.ip.tot_len = packet.udp.len;
149 memcpy(&(packet.data), payload, sizeof(struct dhcpMessage));
150 packet.udp.check = udhcp_checksum(&packet, sizeof(struct udp_dhcp_packet));
151
152 packet.ip.tot_len = htons(sizeof(struct udp_dhcp_packet));
153 packet.ip.ihl = sizeof(packet.ip) >> 2;
154 packet.ip.version = IPVERSION;
155 packet.ip.ttl = IPDEFTTL;
156 packet.ip.check = udhcp_checksum(&(packet.ip), sizeof(packet.ip));
157
158 if (sizeof(struct udp_dhcp_packet) != 576)
159 BUG_sizeof_struct_udp_dhcp_packet_must_be_576();
160
161 result = sendto(fd, &packet, sizeof(struct udp_dhcp_packet), 0,
162 (struct sockaddr *) &dest, sizeof(dest));
163 if (result <= 0) {
164 bb_perror_msg("sendto");
165 }
166 close(fd);
167 return result;
168}
169
170
171/* Let the kernel do all the work for packet generation */
172int udhcp_kernel_packet(struct dhcpMessage *payload,
173 uint32_t source_ip, int source_port,
174 uint32_t dest_ip, int dest_port)
175{
176 int fd, result;
177 struct sockaddr_in client;
178
179 fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
180 if (fd < 0)
181 return -1;
182
183 if (setsockopt_reuseaddr(fd) == -1) {
184 close(fd);
185 return -1;
186 }
187
188 memset(&client, 0, sizeof(client));
189 client.sin_family = AF_INET;
190 client.sin_port = htons(source_port);
191 client.sin_addr.s_addr = source_ip;
192
193 if (bind(fd, (struct sockaddr *)&client, sizeof(struct sockaddr)) == -1) {
194 close(fd);
195 return -1;
196 }
197
198 memset(&client, 0, sizeof(client));
199 client.sin_family = AF_INET;
200 client.sin_port = htons(dest_port);
201 client.sin_addr.s_addr = dest_ip;
202
203 if (connect(fd, (struct sockaddr *)&client, sizeof(struct sockaddr)) == -1) {
204 close(fd);
205 return -1;
206 }
207
208 result = write(fd, payload, sizeof(struct dhcpMessage));
209 close(fd);
210 return result;
211}
diff --git a/networking/udhcp/pidfile.c b/networking/udhcp/pidfile.c
new file mode 100644
index 000000000..bcb2608c5
--- /dev/null
+++ b/networking/udhcp/pidfile.c
@@ -0,0 +1,66 @@
1/* vi: set sw=4 ts=4: */
2/* pidfile.c
3 *
4 * Functions to assist in the writing and removing of pidfiles.
5 *
6 * Russ Dill <Russ.Dill@asu.edu> September 2001
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 */
22
23#include "common.h"
24
25
26static const char *saved_pidfile;
27
28static void pidfile_delete(void)
29{
30 if (saved_pidfile) unlink(saved_pidfile);
31}
32
33
34int pidfile_acquire(const char *pidfile)
35{
36 int pid_fd;
37 if (!pidfile) return -1;
38
39 pid_fd = open(pidfile, O_CREAT|O_WRONLY|O_TRUNC, 0644);
40 if (pid_fd < 0) {
41 bb_perror_msg("cannot open pidfile %s", pidfile);
42 } else {
43 lockf(pid_fd, F_LOCK, 0);
44 if (!saved_pidfile)
45 atexit(pidfile_delete);
46 saved_pidfile = pidfile;
47 }
48
49 return pid_fd;
50}
51
52
53void pidfile_write_release(int pid_fd)
54{
55 FILE *out;
56
57 if (pid_fd < 0) return;
58
59 out = fdopen(pid_fd, "w");
60 if (out) {
61 fprintf(out, "%d\n", getpid());
62 fclose(out);
63 }
64 lockf(pid_fd, F_UNLCK, 0);
65 close(pid_fd);
66}
diff --git a/networking/udhcp/script.c b/networking/udhcp/script.c
new file mode 100644
index 000000000..07f68362c
--- /dev/null
+++ b/networking/udhcp/script.c
@@ -0,0 +1,213 @@
1/* vi: set sw=4 ts=4: */
2/* script.c
3 *
4 * Functions to call the DHCP client notification scripts
5 *
6 * Russ Dill <Russ.Dill@asu.edu> July 2001
7 *
8 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
9 */
10
11#include "common.h"
12#include "dhcpd.h"
13#include "dhcpc.h"
14#include "options.h"
15
16
17/* get a rough idea of how long an option will be (rounding up...) */
18static const int max_option_length[] = {
19 [OPTION_IP] = sizeof("255.255.255.255 "),
20 [OPTION_IP_PAIR] = sizeof("255.255.255.255 ") * 2,
21 [OPTION_STRING] = 1,
22 [OPTION_BOOLEAN] = sizeof("yes "),
23 [OPTION_U8] = sizeof("255 "),
24 [OPTION_U16] = sizeof("65535 "),
25 [OPTION_S16] = sizeof("-32768 "),
26 [OPTION_U32] = sizeof("4294967295 "),
27 [OPTION_S32] = sizeof("-2147483684 "),
28};
29
30
31static inline int upper_length(int length, int opt_index)
32{
33 return max_option_length[opt_index] *
34 (length / option_lengths[opt_index]);
35}
36
37
38static int sprintip(char *dest, char *pre, uint8_t *ip)
39{
40 return sprintf(dest, "%s%d.%d.%d.%d", pre, ip[0], ip[1], ip[2], ip[3]);
41}
42
43
44/* really simple implementation, just count the bits */
45static int mton(struct in_addr *mask)
46{
47 int i;
48 unsigned long bits = ntohl(mask->s_addr);
49 /* too bad one can't check the carry bit, etc in c bit
50 * shifting */
51 for (i = 0; i < 32 && !((bits >> i) & 1); i++);
52 return 32 - i;
53}
54
55
56/* Fill dest with the text of option 'option'. */
57static void fill_options(char *dest, uint8_t *option,
58 const struct dhcp_option *type_p)
59{
60 int type, optlen;
61 uint16_t val_u16;
62 int16_t val_s16;
63 uint32_t val_u32;
64 int32_t val_s32;
65 int len = option[OPT_LEN - 2];
66
67 dest += sprintf(dest, "%s=", type_p->name);
68
69 type = type_p->flags & TYPE_MASK;
70 optlen = option_lengths[type];
71 for (;;) {
72 switch (type) {
73 case OPTION_IP_PAIR:
74 dest += sprintip(dest, "", option);
75 *(dest++) = '/';
76 option += 4;
77 optlen = 4;
78 case OPTION_IP: /* Works regardless of host byte order. */
79 dest += sprintip(dest, "", option);
80 break;
81 case OPTION_BOOLEAN:
82 dest += sprintf(dest, *option ? "yes" : "no");
83 break;
84 case OPTION_U8:
85 dest += sprintf(dest, "%u", *option);
86 break;
87 case OPTION_U16:
88 memcpy(&val_u16, option, 2);
89 dest += sprintf(dest, "%u", ntohs(val_u16));
90 break;
91 case OPTION_S16:
92 memcpy(&val_s16, option, 2);
93 dest += sprintf(dest, "%d", ntohs(val_s16));
94 break;
95 case OPTION_U32:
96 memcpy(&val_u32, option, 4);
97 dest += sprintf(dest, "%lu", (unsigned long) ntohl(val_u32));
98 break;
99 case OPTION_S32:
100 memcpy(&val_s32, option, 4);
101 dest += sprintf(dest, "%ld", (long) ntohl(val_s32));
102 break;
103 case OPTION_STRING:
104 memcpy(dest, option, len);
105 dest[len] = '\0';
106 return; /* Short circuit this case */
107 }
108 option += optlen;
109 len -= optlen;
110 if (len <= 0) break;
111 dest += sprintf(dest, " ");
112 }
113}
114
115
116/* put all the parameters into an environment */
117static char **fill_envp(struct dhcpMessage *packet)
118{
119 int num_options = 0;
120 int i, j;
121 char **envp;
122 uint8_t *temp;
123 struct in_addr subnet;
124 char over = 0;
125
126 if (packet == NULL)
127 num_options = 0;
128 else {
129 for (i = 0; dhcp_options[i].code; i++)
130 if (get_option(packet, dhcp_options[i].code)) {
131 num_options++;
132 if (dhcp_options[i].code == DHCP_SUBNET)
133 num_options++; /* for mton */
134 }
135 if (packet->siaddr) num_options++;
136 if ((temp = get_option(packet, DHCP_OPTION_OVER)))
137 over = *temp;
138 if (!(over & FILE_FIELD) && packet->file[0]) num_options++;
139 if (!(over & SNAME_FIELD) && packet->sname[0]) num_options++;
140 }
141
142 envp = xzalloc(sizeof(char *) * (num_options + 5));
143 j = 0;
144 envp[j++] = xasprintf("interface=%s", client_config.interface);
145 envp[j++] = xasprintf("PATH=%s",
146 getenv("PATH") ? : "/bin:/usr/bin:/sbin:/usr/sbin");
147 envp[j++] = xasprintf("HOME=%s", getenv("HOME") ? : "/");
148
149 if (packet == NULL) return envp;
150
151 envp[j] = xmalloc(sizeof("ip=255.255.255.255"));
152 sprintip(envp[j++], "ip=", (uint8_t *) &packet->yiaddr);
153
154 for (i = 0; dhcp_options[i].code; i++) {
155 temp = get_option(packet, dhcp_options[i].code);
156 if (!temp)
157 continue;
158 envp[j] = xmalloc(upper_length(temp[OPT_LEN - 2],
159 dhcp_options[i].flags & TYPE_MASK) + strlen(dhcp_options[i].name) + 2);
160 fill_options(envp[j++], temp, &dhcp_options[i]);
161
162 /* Fill in a subnet bits option for things like /24 */
163 if (dhcp_options[i].code == DHCP_SUBNET) {
164 memcpy(&subnet, temp, 4);
165 envp[j++] = xasprintf("mask=%d", mton(&subnet));
166 }
167 }
168 if (packet->siaddr) {
169 envp[j] = xmalloc(sizeof("siaddr=255.255.255.255"));
170 sprintip(envp[j++], "siaddr=", (uint8_t *) &packet->siaddr);
171 }
172 if (!(over & FILE_FIELD) && packet->file[0]) {
173 /* watch out for invalid packets */
174 packet->file[sizeof(packet->file) - 1] = '\0';
175 envp[j++] = xasprintf("boot_file=%s", packet->file);
176 }
177 if (!(over & SNAME_FIELD) && packet->sname[0]) {
178 /* watch out for invalid packets */
179 packet->sname[sizeof(packet->sname) - 1] = '\0';
180 envp[j++] = xasprintf("sname=%s", packet->sname);
181 }
182 return envp;
183}
184
185
186/* Call a script with a par file and env vars */
187void udhcp_run_script(struct dhcpMessage *packet, const char *name)
188{
189 int pid;
190 char **envp, **curr;
191
192 if (client_config.script == NULL)
193 return;
194
195 DEBUG("vfork'ing and execle'ing %s", client_config.script);
196
197 envp = fill_envp(packet);
198 /* call script */
199 pid = vfork();
200 if (pid) {
201 waitpid(pid, NULL, 0);
202 for (curr = envp; *curr; curr++) free(*curr);
203 free(envp);
204 return;
205 } else if (pid == 0) {
206 /* close fd's? */
207 /* exec script */
208 execle(client_config.script, client_config.script,
209 name, NULL, envp);
210 bb_perror_msg("script %s failed", client_config.script);
211 exit(1);
212 }
213}
diff --git a/networking/udhcp/serverpacket.c b/networking/udhcp/serverpacket.c
new file mode 100644
index 000000000..8889fda86
--- /dev/null
+++ b/networking/udhcp/serverpacket.c
@@ -0,0 +1,261 @@
1/* vi: set sw=4 ts=4: */
2/* serverpacket.c
3 *
4 * Construct and send DHCP server packets
5 *
6 * Russ Dill <Russ.Dill@asu.edu> July 2001
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 */
22
23#include "common.h"
24#include "dhcpd.h"
25#include "options.h"
26
27
28/* send a packet to giaddr using the kernel ip stack */
29static int send_packet_to_relay(struct dhcpMessage *payload)
30{
31 DEBUG("Forwarding packet to relay");
32
33 return udhcp_kernel_packet(payload, server_config.server, SERVER_PORT,
34 payload->giaddr, SERVER_PORT);
35}
36
37
38/* send a packet to a specific arp address and ip address by creating our own ip packet */
39static int send_packet_to_client(struct dhcpMessage *payload, int force_broadcast)
40{
41 uint8_t *chaddr;
42 uint32_t ciaddr;
43
44 if (force_broadcast) {
45 DEBUG("broadcasting packet to client (NAK)");
46 ciaddr = INADDR_BROADCAST;
47 chaddr = MAC_BCAST_ADDR;
48 } else if (payload->ciaddr) {
49 DEBUG("unicasting packet to client ciaddr");
50 ciaddr = payload->ciaddr;
51 chaddr = payload->chaddr;
52 } else if (ntohs(payload->flags) & BROADCAST_FLAG) {
53 DEBUG("broadcasting packet to client (requested)");
54 ciaddr = INADDR_BROADCAST;
55 chaddr = MAC_BCAST_ADDR;
56 } else {
57 DEBUG("unicasting packet to client yiaddr");
58 ciaddr = payload->yiaddr;
59 chaddr = payload->chaddr;
60 }
61 return udhcp_raw_packet(payload, server_config.server, SERVER_PORT,
62 ciaddr, CLIENT_PORT, chaddr, server_config.ifindex);
63}
64
65
66/* send a dhcp packet, if force broadcast is set, the packet will be broadcast to the client */
67static int send_packet(struct dhcpMessage *payload, int force_broadcast)
68{
69 int ret;
70
71 if (payload->giaddr)
72 ret = send_packet_to_relay(payload);
73 else ret = send_packet_to_client(payload, force_broadcast);
74 return ret;
75}
76
77
78static void init_packet(struct dhcpMessage *packet, struct dhcpMessage *oldpacket, char type)
79{
80 udhcp_init_header(packet, type);
81 packet->xid = oldpacket->xid;
82 memcpy(packet->chaddr, oldpacket->chaddr, 16);
83 packet->flags = oldpacket->flags;
84 packet->giaddr = oldpacket->giaddr;
85 packet->ciaddr = oldpacket->ciaddr;
86 add_simple_option(packet->options, DHCP_SERVER_ID, server_config.server);
87}
88
89
90/* add in the bootp options */
91static void add_bootp_options(struct dhcpMessage *packet)
92{
93 packet->siaddr = server_config.siaddr;
94 if (server_config.sname)
95 strncpy((char*)packet->sname, server_config.sname, sizeof(packet->sname) - 1);
96 if (server_config.boot_file)
97 strncpy((char*)packet->file, server_config.boot_file, sizeof(packet->file) - 1);
98}
99
100
101/* send a DHCP OFFER to a DHCP DISCOVER */
102int sendOffer(struct dhcpMessage *oldpacket)
103{
104 struct dhcpMessage packet;
105 struct dhcpOfferedAddr *lease = NULL;
106 uint32_t req_align, lease_time_align = server_config.lease;
107 uint8_t *req, *lease_time;
108 struct option_set *curr;
109 struct in_addr addr;
110
111 uint32_t static_lease_ip;
112
113 init_packet(&packet, oldpacket, DHCPOFFER);
114
115 static_lease_ip = getIpByMac(server_config.static_leases, oldpacket->chaddr);
116
117 /* ADDME: if static, short circuit */
118 if (!static_lease_ip) {
119 /* the client is in our lease/offered table */
120 lease = find_lease_by_chaddr(oldpacket->chaddr);
121 if (lease) {
122 if (!lease_expired(lease))
123 lease_time_align = lease->expires - time(0);
124 packet.yiaddr = lease->yiaddr;
125
126 /* Or the client has a requested ip */
127 } else if ((req = get_option(oldpacket, DHCP_REQUESTED_IP))
128 /* Don't look here (ugly hackish thing to do) */
129 && memcpy(&req_align, req, 4)
130 /* and the ip is in the lease range */
131 && ntohl(req_align) >= ntohl(server_config.start)
132 && ntohl(req_align) <= ntohl(server_config.end)
133 && !static_lease_ip /* Check that its not a static lease */
134 /* and is not already taken/offered */
135 && (!(lease = find_lease_by_yiaddr(req_align))
136 /* or its taken, but expired */ /* ADDME: or maybe in here */
137 || lease_expired(lease))
138 ) {
139 packet.yiaddr = req_align; /* FIXME: oh my, is there a host using this IP? */
140 /* otherwise, find a free IP */
141 } else {
142 /* Is it a static lease? (No, because find_address skips static lease) */
143 packet.yiaddr = find_address(0);
144 /* try for an expired lease */
145 if (!packet.yiaddr) packet.yiaddr = find_address(1);
146 }
147
148 if (!packet.yiaddr) {
149 bb_error_msg("no IP addresses to give - OFFER abandoned");
150 return -1;
151 }
152 if (!add_lease(packet.chaddr, packet.yiaddr, server_config.offer_time)) {
153 bb_error_msg("lease pool is full - OFFER abandoned");
154 return -1;
155 }
156 lease_time = get_option(oldpacket, DHCP_LEASE_TIME);
157 if (lease_time) {
158 memcpy(&lease_time_align, lease_time, 4);
159 lease_time_align = ntohl(lease_time_align);
160 if (lease_time_align > server_config.lease)
161 lease_time_align = server_config.lease;
162 }
163
164 /* Make sure we aren't just using the lease time from the previous offer */
165 if (lease_time_align < server_config.min_lease)
166 lease_time_align = server_config.lease;
167 /* ADDME: end of short circuit */
168 } else {
169 /* It is a static lease... use it */
170 packet.yiaddr = static_lease_ip;
171 }
172
173 add_simple_option(packet.options, DHCP_LEASE_TIME, htonl(lease_time_align));
174
175 curr = server_config.options;
176 while (curr) {
177 if (curr->data[OPT_CODE] != DHCP_LEASE_TIME)
178 add_option_string(packet.options, curr->data);
179 curr = curr->next;
180 }
181
182 add_bootp_options(&packet);
183
184 addr.s_addr = packet.yiaddr;
185 bb_info_msg("Sending OFFER of %s", inet_ntoa(addr));
186 return send_packet(&packet, 0);
187}
188
189
190int sendNAK(struct dhcpMessage *oldpacket)
191{
192 struct dhcpMessage packet;
193
194 init_packet(&packet, oldpacket, DHCPNAK);
195
196 DEBUG("Sending NAK");
197 return send_packet(&packet, 1);
198}
199
200
201int sendACK(struct dhcpMessage *oldpacket, uint32_t yiaddr)
202{
203 struct dhcpMessage packet;
204 struct option_set *curr;
205 uint8_t *lease_time;
206 uint32_t lease_time_align = server_config.lease;
207 struct in_addr addr;
208
209 init_packet(&packet, oldpacket, DHCPACK);
210 packet.yiaddr = yiaddr;
211
212 if ((lease_time = get_option(oldpacket, DHCP_LEASE_TIME))) {
213 memcpy(&lease_time_align, lease_time, 4);
214 lease_time_align = ntohl(lease_time_align);
215 if (lease_time_align > server_config.lease)
216 lease_time_align = server_config.lease;
217 else if (lease_time_align < server_config.min_lease)
218 lease_time_align = server_config.lease;
219 }
220
221 add_simple_option(packet.options, DHCP_LEASE_TIME, htonl(lease_time_align));
222
223 curr = server_config.options;
224 while (curr) {
225 if (curr->data[OPT_CODE] != DHCP_LEASE_TIME)
226 add_option_string(packet.options, curr->data);
227 curr = curr->next;
228 }
229
230 add_bootp_options(&packet);
231
232 addr.s_addr = packet.yiaddr;
233 bb_info_msg("Sending ACK to %s", inet_ntoa(addr));
234
235 if (send_packet(&packet, 0) < 0)
236 return -1;
237
238 add_lease(packet.chaddr, packet.yiaddr, lease_time_align);
239
240 return 0;
241}
242
243
244int send_inform(struct dhcpMessage *oldpacket)
245{
246 struct dhcpMessage packet;
247 struct option_set *curr;
248
249 init_packet(&packet, oldpacket, DHCPACK);
250
251 curr = server_config.options;
252 while (curr) {
253 if (curr->data[OPT_CODE] != DHCP_LEASE_TIME)
254 add_option_string(packet.options, curr->data);
255 curr = curr->next;
256 }
257
258 add_bootp_options(&packet);
259
260 return send_packet(&packet, 0);
261}
diff --git a/networking/udhcp/signalpipe.c b/networking/udhcp/signalpipe.c
new file mode 100644
index 000000000..361596580
--- /dev/null
+++ b/networking/udhcp/signalpipe.c
@@ -0,0 +1,77 @@
1/* vi: set sw=4 ts=4: */
2/* signalpipe.c
3 *
4 * Signal pipe infrastructure. A reliable way of delivering signals.
5 *
6 * Russ Dill <Russ.Dill@asu.edu> December 2003
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 */
22
23#include "common.h"
24
25
26static int signal_pipe[2];
27
28static void signal_handler(int sig)
29{
30 if (send(signal_pipe[1], &sig, sizeof(sig), MSG_DONTWAIT) < 0)
31 bb_perror_msg("cannot send signal");
32}
33
34
35/* Call this before doing anything else. Sets up the socket pair
36 * and installs the signal handler */
37void udhcp_sp_setup(void)
38{
39 socketpair(AF_UNIX, SOCK_STREAM, 0, signal_pipe);
40 fcntl(signal_pipe[0], F_SETFD, FD_CLOEXEC);
41 fcntl(signal_pipe[1], F_SETFD, FD_CLOEXEC);
42 signal(SIGUSR1, signal_handler);
43 signal(SIGUSR2, signal_handler);
44 signal(SIGTERM, signal_handler);
45}
46
47
48/* Quick little function to setup the rfds. Will return the
49 * max_fd for use with select. Limited in that you can only pass
50 * one extra fd */
51int udhcp_sp_fd_set(fd_set *rfds, int extra_fd)
52{
53 FD_ZERO(rfds);
54 FD_SET(signal_pipe[0], rfds);
55 if (extra_fd >= 0) {
56 fcntl(extra_fd, F_SETFD, FD_CLOEXEC);
57 FD_SET(extra_fd, rfds);
58 }
59 return signal_pipe[0] > extra_fd ? signal_pipe[0] : extra_fd;
60}
61
62
63/* Read a signal from the signal pipe. Returns 0 if there is
64 * no signal, -1 on error (and sets errno appropriately), and
65 * your signal on success */
66int udhcp_sp_read(fd_set *rfds)
67{
68 int sig;
69
70 if (!FD_ISSET(signal_pipe[0], rfds))
71 return 0;
72
73 if (read(signal_pipe[0], &sig, sizeof(sig)) < 0)
74 return -1;
75
76 return sig;
77}
diff --git a/networking/udhcp/socket.c b/networking/udhcp/socket.c
new file mode 100644
index 000000000..2bae68f27
--- /dev/null
+++ b/networking/udhcp/socket.c
@@ -0,0 +1,130 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * socket.c -- DHCP server client/server socket creation
4 *
5 * udhcp client/server
6 * Copyright (C) 1999 Matthew Ramsay <matthewr@moreton.com.au>
7 * Chris Trew <ctrew@moreton.com.au>
8 *
9 * Rewrite by Russ Dill <Russ.Dill@asu.edu> July 2001
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 */
25
26#include <net/if.h>
27#include <features.h>
28#if (__GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1) || defined _NEWLIB_VERSION
29#include <netpacket/packet.h>
30#include <net/ethernet.h>
31#else
32#include <asm/types.h>
33#include <linux/if_packet.h>
34#include <linux/if_ether.h>
35#endif
36
37#include "common.h"
38
39
40int read_interface(char *interface, int *ifindex, uint32_t *addr, uint8_t *arp)
41{
42 int fd;
43 struct ifreq ifr;
44 struct sockaddr_in *our_ip;
45
46 memset(&ifr, 0, sizeof(struct ifreq));
47 fd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
48 if (fd < 0) {
49 bb_perror_msg("socket failed");
50 return -1;
51 }
52
53 ifr.ifr_addr.sa_family = AF_INET;
54 strncpy(ifr.ifr_name, interface, sizeof(ifr.ifr_name));
55 if (addr) {
56 if (ioctl(fd, SIOCGIFADDR, &ifr) != 0) {
57 bb_perror_msg("SIOCGIFADDR failed, is the interface up and configured?");
58 close(fd);
59 return -1;
60 }
61 our_ip = (struct sockaddr_in *) &ifr.ifr_addr;
62 *addr = our_ip->sin_addr.s_addr;
63 DEBUG("%s (our ip) = %s", ifr.ifr_name, inet_ntoa(our_ip->sin_addr));
64 }
65
66 if (ifindex) {
67 if (ioctl(fd, SIOCGIFINDEX, &ifr) != 0) {
68 bb_perror_msg("SIOCGIFINDEX failed");
69 close(fd);
70 return -1;
71 }
72 DEBUG("adapter index %d", ifr.ifr_ifindex);
73 *ifindex = ifr.ifr_ifindex;
74 }
75
76 if (arp) {
77 if (ioctl(fd, SIOCGIFHWADDR, &ifr) != 0) {
78 bb_perror_msg("SIOCGIFHWADDR failed");
79 close(fd);
80 return -1;
81 }
82 memcpy(arp, ifr.ifr_hwaddr.sa_data, 6);
83 DEBUG("adapter hardware address %02x:%02x:%02x:%02x:%02x:%02x",
84 arp[0], arp[1], arp[2], arp[3], arp[4], arp[5]);
85 }
86
87 return 0;
88}
89
90
91int listen_socket(uint32_t ip, int port, char *inf)
92{
93 struct ifreq interface;
94 int fd;
95 struct sockaddr_in addr;
96
97 DEBUG("Opening listen socket on 0x%08x:%d %s", ip, port, inf);
98 fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
99 if (fd < 0) {
100 bb_perror_msg("socket");
101 return -1;
102 }
103
104 memset(&addr, 0, sizeof(addr));
105 addr.sin_family = AF_INET;
106 addr.sin_port = htons(port);
107 addr.sin_addr.s_addr = ip;
108
109 if (setsockopt_reuseaddr(fd) == -1) {
110 close(fd);
111 return -1;
112 }
113 if (setsockopt_broadcast(fd) == -1) {
114 close(fd);
115 return -1;
116 }
117
118 strncpy(interface.ifr_name, inf, IFNAMSIZ);
119 if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, (char *)&interface, sizeof(interface)) < 0) {
120 close(fd);
121 return -1;
122 }
123
124 if (bind(fd, (struct sockaddr *)&addr, sizeof(struct sockaddr)) == -1) {
125 close(fd);
126 return -1;
127 }
128
129 return fd;
130}
diff --git a/networking/udhcp/static_leases.c b/networking/udhcp/static_leases.c
new file mode 100644
index 000000000..aabfb81aa
--- /dev/null
+++ b/networking/udhcp/static_leases.c
@@ -0,0 +1,99 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * static_leases.c -- Couple of functions to assist with storing and
4 * retrieving data for static leases
5 *
6 * Wade Berrier <wberrier@myrealbox.com> September 2004
7 *
8 */
9
10#include "common.h"
11#include "dhcpd.h"
12
13
14/* Takes the address of the pointer to the static_leases linked list,
15 * Address to a 6 byte mac address
16 * Address to a 4 byte ip address */
17int addStaticLease(struct static_lease **lease_struct, uint8_t *mac, uint32_t *ip)
18{
19 struct static_lease *cur;
20 struct static_lease *new_static_lease;
21
22 /* Build new node */
23 new_static_lease = xmalloc(sizeof(struct static_lease));
24 new_static_lease->mac = mac;
25 new_static_lease->ip = ip;
26 new_static_lease->next = NULL;
27
28 /* If it's the first node to be added... */
29 if (*lease_struct == NULL) {
30 *lease_struct = new_static_lease;
31 } else {
32 cur = *lease_struct;
33 while (cur->next) {
34 cur = cur->next;
35 }
36
37 cur->next = new_static_lease;
38 }
39
40 return 1;
41}
42
43/* Check to see if a mac has an associated static lease */
44uint32_t getIpByMac(struct static_lease *lease_struct, void *arg)
45{
46 uint32_t return_ip;
47 struct static_lease *cur = lease_struct;
48 uint8_t *mac = arg;
49
50 return_ip = 0;
51
52 while (cur) {
53 /* If the client has the correct mac */
54 if (memcmp(cur->mac, mac, 6) == 0) {
55 return_ip = *(cur->ip);
56 }
57
58 cur = cur->next;
59 }
60
61 return return_ip;
62}
63
64/* Check to see if an ip is reserved as a static ip */
65uint32_t reservedIp(struct static_lease *lease_struct, uint32_t ip)
66{
67 struct static_lease *cur = lease_struct;
68
69 uint32_t return_val = 0;
70
71 while (cur) {
72 /* If the client has the correct ip */
73 if (*cur->ip == ip)
74 return_val = 1;
75
76 cur = cur->next;
77 }
78
79 return return_val;
80}
81
82#if ENABLE_FEATURE_UDHCP_DEBUG
83/* Print out static leases just to check what's going on */
84/* Takes the address of the pointer to the static_leases linked list */
85void printStaticLeases(struct static_lease **arg)
86{
87 /* Get a pointer to the linked list */
88 struct static_lease *cur = *arg;
89
90 while (cur) {
91 /* printf("PrintStaticLeases: Lease mac Address: %x\n", cur->mac); */
92 printf("PrintStaticLeases: Lease mac Value: %x\n", *(cur->mac));
93 /* printf("PrintStaticLeases: Lease ip Address: %x\n", cur->ip); */
94 printf("PrintStaticLeases: Lease ip Value: %x\n", *(cur->ip));
95
96 cur = cur->next;
97 }
98}
99#endif