diff options
author | Denis Vlasenko <vda.linux@googlemail.com> | 2006-12-27 04:35:04 +0000 |
---|---|---|
committer | Denis Vlasenko <vda.linux@googlemail.com> | 2006-12-27 04:35:04 +0000 |
commit | 8d42f86b146871ae4c4cafd3801a85f381249a14 (patch) | |
tree | b963999fc54eddb65f1929b894f868e24851fc9c /networking/udhcp | |
download | busybox-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.in | 67 | ||||
-rw-r--r-- | networking/udhcp/Kbuild | 18 | ||||
-rw-r--r-- | networking/udhcp/arpping.c | 114 | ||||
-rw-r--r-- | networking/udhcp/clientpacket.c | 224 | ||||
-rw-r--r-- | networking/udhcp/clientsocket.c | 59 | ||||
-rw-r--r-- | networking/udhcp/common.c | 75 | ||||
-rw-r--r-- | networking/udhcp/common.h | 108 | ||||
-rw-r--r-- | networking/udhcp/dhcpc.c | 509 | ||||
-rw-r--r-- | networking/udhcp/dhcpc.h | 50 | ||||
-rw-r--r-- | networking/udhcp/dhcpd.c | 226 | ||||
-rw-r--r-- | networking/udhcp/dhcpd.h | 190 | ||||
-rw-r--r-- | networking/udhcp/dhcprelay.c | 340 | ||||
-rw-r--r-- | networking/udhcp/dumpleases.c | 74 | ||||
-rw-r--r-- | networking/udhcp/files.c | 395 | ||||
-rw-r--r-- | networking/udhcp/leases.c | 145 | ||||
-rw-r--r-- | networking/udhcp/options.c | 174 | ||||
-rw-r--r-- | networking/udhcp/options.h | 37 | ||||
-rw-r--r-- | networking/udhcp/packet.c | 211 | ||||
-rw-r--r-- | networking/udhcp/pidfile.c | 66 | ||||
-rw-r--r-- | networking/udhcp/script.c | 213 | ||||
-rw-r--r-- | networking/udhcp/serverpacket.c | 261 | ||||
-rw-r--r-- | networking/udhcp/signalpipe.c | 77 | ||||
-rw-r--r-- | networking/udhcp/socket.c | 130 | ||||
-rw-r--r-- | networking/udhcp/static_leases.c | 99 |
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 | |||
6 | config 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 | |||
15 | config 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 | |||
24 | config 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 | |||
35 | config 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 | |||
47 | config 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 | |||
58 | config 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 | |||
8 | lib-y:= | ||
9 | lib-$(CONFIG_APP_UDHCPC) += common.o options.o packet.o pidfile.o \ | ||
10 | signalpipe.o socket.o | ||
11 | lib-$(CONFIG_APP_UDHCPD) += common.o options.o packet.o pidfile.o \ | ||
12 | signalpipe.o socket.o | ||
13 | lib-$(CONFIG_APP_UDHCPC) += dhcpc.o clientpacket.o clientsocket.o \ | ||
14 | script.o | ||
15 | lib-$(CONFIG_APP_UDHCPD) += dhcpd.o arpping.o files.o leases.o \ | ||
16 | serverpacket.o static_leases.o | ||
17 | lib-$(CONFIG_APP_DUMPLEASES) += dumpleases.o | ||
18 | lib-$(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 | |||
16 | struct 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 */ | ||
45 | int 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 */ | ||
28 | unsigned 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 */ | ||
47 | static 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. */ | ||
62 | static 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 */ | ||
78 | int 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 */ | ||
95 | int 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 */ | ||
115 | int 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 */ | ||
135 | int 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 */ | ||
152 | int 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 | |||
37 | int 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 | |||
18 | long 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 | */ | ||
30 | static 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 | |||
39 | void 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 | |||
55 | void 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 | |||
29 | struct 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 | |||
48 | struct udp_dhcp_packet { | ||
49 | struct iphdr ip; | ||
50 | struct udphdr udp; | ||
51 | struct dhcpMessage data; | ||
52 | }; | ||
53 | |||
54 | void udhcp_init_header(struct dhcpMessage *packet, char type); | ||
55 | int udhcp_get_packet(struct dhcpMessage *packet, int fd); | ||
56 | uint16_t udhcp_checksum(void *addr, int count); | ||
57 | int 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); | ||
60 | int 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 | |||
67 | void udhcp_background(const char *pidfile); | ||
68 | void udhcp_start_log_and_pid(const char *pidfile); | ||
69 | |||
70 | void 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 | |||
91 | long uptime(void); | ||
92 | void udhcp_sp_setup(void); | ||
93 | int udhcp_sp_fd_set(fd_set *rfds, int extra_fd); | ||
94 | int udhcp_sp_read(fd_set *rfds); | ||
95 | int raw_socket(int ifindex); | ||
96 | int read_interface(char *interface, int *ifindex, uint32_t *addr, uint8_t *arp); | ||
97 | int listen_socket(uint32_t ip, int port, char *inf); | ||
98 | int pidfile_acquire(const char *pidfile); | ||
99 | void pidfile_write_release(int pid_fd); | ||
100 | int 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 | |||
19 | static 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 | */ | ||
25 | static unsigned long requested_ip; /* = 0 */ | ||
26 | static unsigned long server_addr; | ||
27 | static unsigned long timeout; | ||
28 | static int packet_num; /* = 0 */ | ||
29 | static int fd = -1; | ||
30 | |||
31 | #define LISTEN_NONE 0 | ||
32 | #define LISTEN_KERNEL 1 | ||
33 | #define LISTEN_RAW 2 | ||
34 | static int listen_mode; | ||
35 | |||
36 | struct client_config_t client_config; | ||
37 | |||
38 | |||
39 | /* just a little helper */ | ||
40 | static 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 */ | ||
51 | static 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 */ | ||
81 | static 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 | |||
104 | static 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 | |||
112 | static 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 | |||
125 | int 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 | |||
15 | struct 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 | |||
36 | extern struct client_config_t client_config; | ||
37 | |||
38 | |||
39 | /*** clientpacket.h ***/ | ||
40 | |||
41 | unsigned long random_xid(void); | ||
42 | int send_discover(unsigned long xid, unsigned long requested); | ||
43 | int send_selecting(unsigned long xid, unsigned long server, unsigned long requested); | ||
44 | int send_renew(unsigned long xid, unsigned long server, unsigned long ciaddr); | ||
45 | int send_renew(unsigned long xid, unsigned long server, unsigned long ciaddr); | ||
46 | int send_release(unsigned long server, unsigned long ciaddr); | ||
47 | int 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 */ | ||
19 | struct dhcpOfferedAddr *leases; | ||
20 | struct server_config_t server_config; | ||
21 | |||
22 | |||
23 | int 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 | |||
92 | struct option_set { | ||
93 | uint8_t *data; | ||
94 | struct option_set *next; | ||
95 | }; | ||
96 | |||
97 | struct static_lease { | ||
98 | uint8_t *mac; | ||
99 | uint32_t *ip; | ||
100 | struct static_lease *next; | ||
101 | }; | ||
102 | |||
103 | struct 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 | |||
131 | extern struct server_config_t server_config; | ||
132 | extern struct dhcpOfferedAddr *leases; | ||
133 | |||
134 | |||
135 | /*** leases.h ***/ | ||
136 | |||
137 | struct dhcpOfferedAddr { | ||
138 | uint8_t chaddr[16]; | ||
139 | uint32_t yiaddr; /* network order */ | ||
140 | uint32_t expires; /* host order */ | ||
141 | }; | ||
142 | |||
143 | extern uint8_t blank_chaddr[]; | ||
144 | |||
145 | void clear_lease(uint8_t *chaddr, uint32_t yiaddr); | ||
146 | struct dhcpOfferedAddr *add_lease(uint8_t *chaddr, uint32_t yiaddr, unsigned long lease); | ||
147 | int lease_expired(struct dhcpOfferedAddr *lease); | ||
148 | struct dhcpOfferedAddr *oldest_expired_lease(void); | ||
149 | struct dhcpOfferedAddr *find_lease_by_chaddr(uint8_t *chaddr); | ||
150 | struct dhcpOfferedAddr *find_lease_by_yiaddr(uint32_t yiaddr); | ||
151 | uint32_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 */ | ||
158 | int addStaticLease(struct static_lease **lease_struct, uint8_t *mac, uint32_t *ip); | ||
159 | /* Check to see if a mac has an associated static lease */ | ||
160 | uint32_t getIpByMac(struct static_lease *lease_struct, void *arg); | ||
161 | /* Check to see if an ip is reserved as a static ip */ | ||
162 | uint32_t reservedIp(struct static_lease *lease_struct, uint32_t ip); | ||
163 | /* Print out static leases just to check what's going on (debug code) */ | ||
164 | void printStaticLeases(struct static_lease **lease_struct); | ||
165 | |||
166 | |||
167 | /*** serverpacket.h ***/ | ||
168 | |||
169 | int sendOffer(struct dhcpMessage *oldpacket); | ||
170 | int sendNAK(struct dhcpMessage *oldpacket); | ||
171 | int sendACK(struct dhcpMessage *oldpacket, uint32_t yiaddr); | ||
172 | int send_inform(struct dhcpMessage *oldpacket); | ||
173 | |||
174 | |||
175 | /*** files.h ***/ | ||
176 | |||
177 | struct config_keyword { | ||
178 | const char *keyword; | ||
179 | int (* const handler)(const char *line, void *var); | ||
180 | void *var; | ||
181 | const char *def; | ||
182 | }; | ||
183 | |||
184 | int read_config(const char *file); | ||
185 | void write_leases(void); | ||
186 | void read_leases(const char *file); | ||
187 | struct 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. */ | ||
24 | static 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 | |||
33 | static 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 | |||
52 | static 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 | |||
70 | static 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 | |||
82 | static 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 | */ | ||
104 | static 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 | */ | ||
122 | static int dhcprelay_stopflag; | ||
123 | static 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 | */ | ||
133 | static 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. */ | ||
165 | static 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 | */ | ||
194 | static 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 | */ | ||
225 | static 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 | |||
257 | static 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 | |||
299 | int 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 | |||
14 | int 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 */ | ||
22 | static 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 | |||
37 | static 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 | |||
54 | static 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 | |||
65 | static 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 | |||
72 | static 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 */ | ||
88 | struct 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 */ | ||
99 | static 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 */ | ||
137 | static 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 | |||
217 | static 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 | |||
247 | static 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 | |||
273 | int 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 | |||
325 | void 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 | |||
367 | void 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 | |||
11 | uint8_t blank_chaddr[] = {[0 ... 15] = 0}; | ||
12 | |||
13 | /* clear every lease out that chaddr OR yiaddr matches and is nonzero */ | ||
14 | void 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 */ | ||
29 | struct 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 */ | ||
49 | int 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 */ | ||
56 | struct 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 */ | ||
74 | struct 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 */ | ||
86 | struct 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 */ | ||
98 | static 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... */ | ||
114 | uint32_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 */ | ||
13 | const 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 */ | ||
52 | const 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). */ | ||
66 | uint8_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) */ | ||
121 | int 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) */ | ||
135 | int 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 */ | ||
153 | int 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 | |||
8 | enum { | ||
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 | |||
23 | struct dhcp_option { | ||
24 | char name[12]; | ||
25 | char flags; | ||
26 | uint8_t code; | ||
27 | }; | ||
28 | |||
29 | extern const struct dhcp_option dhcp_options[]; | ||
30 | extern const unsigned char option_lengths[]; | ||
31 | |||
32 | uint8_t *get_option(struct dhcpMessage *packet, int code); | ||
33 | int end_option(uint8_t *optionptr); | ||
34 | int add_option_string(uint8_t *optionptr, uint8_t *string); | ||
35 | int 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 | |||
18 | void 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 */ | ||
42 | int 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 | |||
81 | uint16_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 */ | ||
112 | void BUG_sizeof_struct_udp_dhcp_packet_must_be_576(void); | ||
113 | int 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 */ | ||
172 | int 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 | |||
26 | static const char *saved_pidfile; | ||
27 | |||
28 | static void pidfile_delete(void) | ||
29 | { | ||
30 | if (saved_pidfile) unlink(saved_pidfile); | ||
31 | } | ||
32 | |||
33 | |||
34 | int 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 | |||
53 | void 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...) */ | ||
18 | static 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 | |||
31 | static 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 | |||
38 | static 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 */ | ||
45 | static 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'. */ | ||
57 | static 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 */ | ||
117 | static 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 */ | ||
187 | void 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 */ | ||
29 | static 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 */ | ||
39 | static 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 */ | ||
67 | static 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 | |||
78 | static 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 */ | ||
91 | static 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 */ | ||
102 | int 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 | |||
190 | int 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 | |||
201 | int 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 | |||
244 | int 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 | |||
26 | static int signal_pipe[2]; | ||
27 | |||
28 | static 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 */ | ||
37 | void 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 */ | ||
51 | int 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 */ | ||
66 | int 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 | |||
40 | int 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 | |||
91 | int 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 */ | ||
17 | int 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 */ | ||
44 | uint32_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 */ | ||
65 | uint32_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 */ | ||
85 | void 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 | ||