18 |
|
|
19 |
|
|
20 |
/* globals */ |
/* globals */ |
21 |
struct dhcpOfferedAddr *leases; |
struct dyn_lease *g_leases; |
22 |
/* struct server_config_t server_config is in bb_common_bufsiz1 */ |
/* struct server_config_t server_config is in bb_common_bufsiz1 */ |
23 |
|
|
24 |
|
|
26 |
int udhcpd_main(int argc UNUSED_PARAM, char **argv) |
int udhcpd_main(int argc UNUSED_PARAM, char **argv) |
27 |
{ |
{ |
28 |
fd_set rfds; |
fd_set rfds; |
29 |
struct timeval tv; |
int server_socket = -1, retval, max_sock; |
30 |
int server_socket = -1, bytes, retval, max_sock; |
struct dhcp_packet packet; |
31 |
struct dhcpMessage packet; |
uint8_t *state; |
32 |
uint8_t *state, *server_id, *requested; |
uint32_t static_lease_ip; |
|
uint32_t server_id_align, requested_align, static_lease_ip; |
|
33 |
unsigned timeout_end; |
unsigned timeout_end; |
34 |
unsigned num_ips; |
unsigned num_ips; |
35 |
unsigned opt; |
unsigned opt; |
36 |
struct option_set *option; |
struct option_set *option; |
37 |
struct dhcpOfferedAddr *lease, static_lease; |
struct dyn_lease *lease, fake_lease; |
38 |
USE_FEATURE_UDHCP_PORT(char *str_P;) |
IF_FEATURE_UDHCP_PORT(char *str_P;) |
39 |
|
|
40 |
#if ENABLE_FEATURE_UDHCP_PORT |
#if ENABLE_FEATURE_UDHCP_PORT |
41 |
SERVER_PORT = 67; |
SERVER_PORT = 67; |
42 |
CLIENT_PORT = 68; |
CLIENT_PORT = 68; |
43 |
#endif |
#endif |
44 |
|
|
45 |
opt = getopt32(argv, "fS" USE_FEATURE_UDHCP_PORT("P:", &str_P)); |
#if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 1 |
46 |
|
opt_complementary = "vv"; |
47 |
|
#endif |
48 |
|
opt = getopt32(argv, "fSv" |
49 |
|
IF_FEATURE_UDHCP_PORT("P:", &str_P) |
50 |
|
#if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 1 |
51 |
|
, &dhcp_verbose |
52 |
|
#endif |
53 |
|
); |
54 |
argv += optind; |
argv += optind; |
|
|
|
55 |
if (!(opt & 1)) { /* no -f */ |
if (!(opt & 1)) { /* no -f */ |
56 |
bb_daemonize_or_rexec(0, argv); |
bb_daemonize_or_rexec(0, argv); |
57 |
logmode &= ~LOGMODE_STDIO; |
logmode = LOGMODE_NONE; |
58 |
} |
} |
|
|
|
59 |
if (opt & 2) { /* -S */ |
if (opt & 2) { /* -S */ |
60 |
openlog(applet_name, LOG_PID, LOG_LOCAL0); |
openlog(applet_name, LOG_PID, LOG_DAEMON); |
61 |
logmode |= LOGMODE_SYSLOG; |
logmode |= LOGMODE_SYSLOG; |
62 |
} |
} |
63 |
#if ENABLE_FEATURE_UDHCP_PORT |
#if ENABLE_FEATURE_UDHCP_PORT |
64 |
if (opt & 4) { /* -P */ |
if (opt & 8) { /* -P */ |
65 |
SERVER_PORT = xatou16(str_P); |
SERVER_PORT = xatou16(str_P); |
66 |
CLIENT_PORT = SERVER_PORT + 1; |
CLIENT_PORT = SERVER_PORT + 1; |
67 |
} |
} |
77 |
|
|
78 |
/* Create pidfile */ |
/* Create pidfile */ |
79 |
write_pidfile(server_config.pidfile); |
write_pidfile(server_config.pidfile); |
80 |
/* if (!..) bb_perror_msg("cannot create pidfile %s", pidfile); */ |
/* if (!..) bb_perror_msg("can't create pidfile %s", pidfile); */ |
81 |
|
|
82 |
bb_info_msg("%s (v"BB_VER") started", applet_name); |
bb_info_msg("%s (v"BB_VER") started", applet_name); |
83 |
|
|
84 |
option = find_option(server_config.options, DHCP_LEASE_TIME); |
option = find_option(server_config.options, DHCP_LEASE_TIME); |
85 |
server_config.lease = LEASE_TIME; |
server_config.max_lease_sec = LEASE_TIME; |
86 |
if (option) { |
if (option) { |
87 |
memcpy(&server_config.lease, option->data + 2, 4); |
move_from_unaligned32(server_config.max_lease_sec, option->data + OPT_DATA); |
88 |
server_config.lease = ntohl(server_config.lease); |
server_config.max_lease_sec = ntohl(server_config.max_lease_sec); |
89 |
} |
} |
90 |
|
|
91 |
/* Sanity check */ |
/* Sanity check */ |
96 |
server_config.max_leases = num_ips; |
server_config.max_leases = num_ips; |
97 |
} |
} |
98 |
|
|
99 |
leases = xzalloc(server_config.max_leases * sizeof(*leases)); |
g_leases = xzalloc(server_config.max_leases * sizeof(g_leases[0])); |
100 |
read_leases(server_config.lease_file); |
read_leases(server_config.lease_file); |
101 |
|
|
102 |
if (udhcp_read_interface(server_config.interface, &server_config.ifindex, |
if (udhcp_read_interface(server_config.interface, |
103 |
&server_config.server, server_config.arp)) { |
&server_config.ifindex, |
104 |
|
&server_config.server_nip, |
105 |
|
server_config.server_mac) |
106 |
|
) { |
107 |
retval = 1; |
retval = 1; |
108 |
goto ret; |
goto ret; |
109 |
} |
} |
113 |
|
|
114 |
timeout_end = monotonic_sec() + server_config.auto_time; |
timeout_end = monotonic_sec() + server_config.auto_time; |
115 |
while (1) { /* loop until universe collapses */ |
while (1) { /* loop until universe collapses */ |
116 |
|
int bytes; |
117 |
|
struct timeval tv; |
118 |
|
|
119 |
if (server_socket < 0) { |
if (server_socket < 0) { |
120 |
server_socket = udhcp_listen_socket(/*INADDR_ANY,*/ SERVER_PORT, |
server_socket = udhcp_listen_socket(/*INADDR_ANY,*/ SERVER_PORT, |
137 |
continue; |
continue; |
138 |
} |
} |
139 |
if (retval < 0 && errno != EINTR) { |
if (retval < 0 && errno != EINTR) { |
140 |
DEBUG("error on select"); |
log1("Error on select"); |
141 |
continue; |
continue; |
142 |
} |
} |
143 |
|
|
151 |
case SIGTERM: |
case SIGTERM: |
152 |
bb_info_msg("Received a SIGTERM"); |
bb_info_msg("Received a SIGTERM"); |
153 |
goto ret0; |
goto ret0; |
154 |
case 0: break; /* no signal */ |
case 0: /* no signal: read a packet */ |
155 |
default: continue; /* signal or error (probably EINTR) */ |
break; |
156 |
|
default: /* signal or error (probably EINTR): back to select */ |
157 |
|
continue; |
158 |
} |
} |
159 |
|
|
160 |
bytes = udhcp_recv_kernel_packet(&packet, server_socket); /* this waits for a packet - idle */ |
bytes = udhcp_recv_kernel_packet(&packet, server_socket); |
161 |
if (bytes < 0) { |
if (bytes < 0) { |
162 |
|
/* bytes can also be -2 ("bad packet data") */ |
163 |
if (bytes == -1 && errno != EINTR) { |
if (bytes == -1 && errno != EINTR) { |
164 |
DEBUG("error on read, %s, reopening socket", strerror(errno)); |
log1("Read error: %s, reopening socket", strerror(errno)); |
165 |
close(server_socket); |
close(server_socket); |
166 |
server_socket = -1; |
server_socket = -1; |
167 |
} |
} |
168 |
continue; |
continue; |
169 |
} |
} |
170 |
|
|
171 |
|
if (packet.hlen != 6) { |
172 |
|
bb_error_msg("MAC length != 6, ignoring packet"); |
173 |
|
continue; |
174 |
|
} |
175 |
|
|
176 |
state = get_option(&packet, DHCP_MESSAGE_TYPE); |
state = get_option(&packet, DHCP_MESSAGE_TYPE); |
177 |
if (state == NULL) { |
if (state == NULL) { |
178 |
bb_error_msg("cannot get option from packet, ignoring"); |
bb_error_msg("no message type option, ignoring packet"); |
179 |
continue; |
continue; |
180 |
} |
} |
181 |
|
|
182 |
/* Look for a static lease */ |
/* Look for a static lease */ |
183 |
static_lease_ip = getIpByMac(server_config.static_leases, &packet.chaddr); |
static_lease_ip = get_static_nip_by_mac(server_config.static_leases, &packet.chaddr); |
|
|
|
184 |
if (static_lease_ip) { |
if (static_lease_ip) { |
185 |
bb_info_msg("Found static lease: %x", static_lease_ip); |
bb_info_msg("Found static lease: %x", static_lease_ip); |
186 |
|
|
187 |
memcpy(&static_lease.chaddr, &packet.chaddr, 16); |
memcpy(&fake_lease.lease_mac, &packet.chaddr, 6); |
188 |
static_lease.yiaddr = static_lease_ip; |
fake_lease.lease_nip = static_lease_ip; |
189 |
static_lease.expires = 0; |
fake_lease.expires = 0; |
190 |
|
|
191 |
lease = &static_lease; |
lease = &fake_lease; |
192 |
} else { |
} else { |
193 |
lease = find_lease_by_chaddr(packet.chaddr); |
lease = find_lease_by_mac(packet.chaddr); |
194 |
} |
} |
195 |
|
|
196 |
switch (state[0]) { |
switch (state[0]) { |
197 |
case DHCPDISCOVER: |
case DHCPDISCOVER: |
198 |
DEBUG("Received DISCOVER"); |
log1("Received DISCOVER"); |
199 |
|
|
200 |
if (send_offer(&packet) < 0) { |
if (send_offer(&packet) < 0) { |
201 |
bb_error_msg("send OFFER failed"); |
bb_error_msg("send OFFER failed"); |
202 |
} |
} |
203 |
break; |
break; |
204 |
case DHCPREQUEST: |
case DHCPREQUEST: { |
205 |
DEBUG("received REQUEST"); |
uint8_t *server_id_opt, *requested_opt; |
206 |
|
uint32_t server_id_net = server_id_net; /* for compiler */ |
207 |
requested = get_option(&packet, DHCP_REQUESTED_IP); |
uint32_t requested_nip = requested_nip; /* for compiler */ |
208 |
server_id = get_option(&packet, DHCP_SERVER_ID); |
|
209 |
|
log1("Received REQUEST"); |
210 |
if (requested) memcpy(&requested_align, requested, 4); |
|
211 |
if (server_id) memcpy(&server_id_align, server_id, 4); |
requested_opt = get_option(&packet, DHCP_REQUESTED_IP); |
212 |
|
server_id_opt = get_option(&packet, DHCP_SERVER_ID); |
213 |
|
if (requested_opt) |
214 |
|
move_from_unaligned32(requested_nip, requested_opt); |
215 |
|
if (server_id_opt) |
216 |
|
move_from_unaligned32(server_id_net, server_id_opt); |
217 |
|
|
218 |
if (lease) { |
if (lease) { |
219 |
if (server_id) { |
if (server_id_opt) { |
220 |
/* SELECTING State */ |
/* SELECTING State */ |
221 |
DEBUG("server_id = %08x", ntohl(server_id_align)); |
if (server_id_net == server_config.server_nip |
222 |
if (server_id_align == server_config.server && requested |
&& requested_opt |
223 |
&& requested_align == lease->yiaddr |
&& requested_nip == lease->lease_nip |
224 |
) { |
) { |
225 |
send_ACK(&packet, lease->yiaddr); |
send_ACK(&packet, lease->lease_nip); |
226 |
} |
} |
227 |
} else if (requested) { |
} else if (requested_opt) { |
228 |
/* INIT-REBOOT State */ |
/* INIT-REBOOT State */ |
229 |
if (lease->yiaddr == requested_align) |
if (lease->lease_nip == requested_nip) |
230 |
send_ACK(&packet, lease->yiaddr); |
send_ACK(&packet, lease->lease_nip); |
231 |
else |
else |
232 |
send_NAK(&packet); |
send_NAK(&packet); |
233 |
} else if (lease->yiaddr == packet.ciaddr) { |
} else if (lease->lease_nip == packet.ciaddr) { |
234 |
/* RENEWING or REBINDING State */ |
/* RENEWING or REBINDING State */ |
235 |
send_ACK(&packet, lease->yiaddr); |
send_ACK(&packet, lease->lease_nip); |
236 |
} else { /* don't know what to do!!!! */ |
} else { /* don't know what to do!!!! */ |
237 |
send_NAK(&packet); |
send_NAK(&packet); |
238 |
} |
} |
239 |
|
|
240 |
/* what to do if we have no record of the client */ |
/* what to do if we have no record of the client */ |
241 |
} else if (server_id) { |
} else if (server_id_opt) { |
242 |
/* SELECTING State */ |
/* SELECTING State */ |
243 |
|
|
244 |
} else if (requested) { |
} else if (requested_opt) { |
245 |
/* INIT-REBOOT State */ |
/* INIT-REBOOT State */ |
246 |
lease = find_lease_by_yiaddr(requested_align); |
lease = find_lease_by_nip(requested_nip); |
247 |
if (lease) { |
if (lease) { |
248 |
if (lease_expired(lease)) { |
if (is_expired_lease(lease)) { |
249 |
/* probably best if we drop this lease */ |
/* probably best if we drop this lease */ |
250 |
memset(lease->chaddr, 0, 16); |
memset(lease->lease_mac, 0, sizeof(lease->lease_mac)); |
251 |
/* make some contention for this address */ |
} else { |
252 |
} else |
/* make some contention for this address */ |
253 |
send_NAK(&packet); |
send_NAK(&packet); |
254 |
|
} |
255 |
} else { |
} else { |
256 |
uint32_t r = ntohl(requested_align); |
uint32_t r = ntohl(requested_nip); |
257 |
if (r < server_config.start_ip |
if (r < server_config.start_ip |
258 |
|| r > server_config.end_ip |
|| r > server_config.end_ip |
259 |
) { |
) { |
266 |
/* RENEWING or REBINDING State */ |
/* RENEWING or REBINDING State */ |
267 |
} |
} |
268 |
break; |
break; |
269 |
|
} |
270 |
case DHCPDECLINE: |
case DHCPDECLINE: |
271 |
DEBUG("Received DECLINE"); |
log1("Received DECLINE"); |
272 |
if (lease) { |
if (lease) { |
273 |
memset(lease->chaddr, 0, 16); |
memset(lease->lease_mac, 0, sizeof(lease->lease_mac)); |
274 |
lease->expires = time(0) + server_config.decline_time; |
lease->expires = time(NULL) + server_config.decline_time; |
275 |
} |
} |
276 |
break; |
break; |
277 |
case DHCPRELEASE: |
case DHCPRELEASE: |
278 |
DEBUG("Received RELEASE"); |
log1("Received RELEASE"); |
279 |
if (lease) |
if (lease) |
280 |
lease->expires = time(0); |
lease->expires = time(NULL); |
281 |
break; |
break; |
282 |
case DHCPINFORM: |
case DHCPINFORM: |
283 |
DEBUG("Received INFORM"); |
log1("Received INFORM"); |
284 |
send_inform(&packet); |
send_inform(&packet); |
285 |
break; |
break; |
286 |
default: |
default: |