source: MondoRescue/branches/3.3/mindi-busybox/networking/udhcp/d6_dhcpc.c@ 3803

Last change on this file since 3803 was 3621, checked in by Bruno Cornec, 10 years ago

New 3?3 banch for incorporation of latest busybox 1.25. Changing minor version to handle potential incompatibilities.

  • Property svn:eol-style set to native
File size: 50.7 KB
RevLine 
[3621]1/* vi: set sw=4 ts=4: */
2/*
3 * DHCPv6 client.
4 *
5 * 2011-11.
6 * WARNING: THIS CODE IS INCOMPLETE. IT IS NOWHERE NEAR
7 * TO BE READY FOR PRODUCTION USE.
8 *
9 * Copyright (C) 2011 Denys Vlasenko.
10 *
11 * Licensed under GPLv2, see file LICENSE in this source tree.
12 */
13
14//config:config UDHCPC6
15//config: bool "udhcp client for DHCPv6 (udhcpc6)"
16//config: default n # not yet ready
17//config: depends on FEATURE_IPV6
18//config: help
19//config: udhcpc6 is a DHCPv6 client
20
21//applet:IF_UDHCPC6(APPLET(udhcpc6, BB_DIR_USR_BIN, BB_SUID_DROP))
22
23//kbuild:lib-$(CONFIG_UDHCPC6) += d6_dhcpc.o d6_packet.o d6_socket.o common.o socket.o signalpipe.o
24
25
26#include <syslog.h>
27/* Override ENABLE_FEATURE_PIDFILE - ifupdown needs our pidfile to always exist */
28#define WANT_PIDFILE 1
29#include "common.h"
30#include "dhcpd.h"
31#include "dhcpc.h"
32#include "d6_common.h"
33
34#include <netinet/if_ether.h>
35#include <netpacket/packet.h>
36#include <linux/filter.h>
37
38/* "struct client_config_t client_config" is in bb_common_bufsiz1 */
39
40
41#if ENABLE_LONG_OPTS
42static const char udhcpc6_longopts[] ALIGN1 =
43 "interface\0" Required_argument "i"
44 "now\0" No_argument "n"
45 "pidfile\0" Required_argument "p"
46 "quit\0" No_argument "q"
47 "release\0" No_argument "R"
48 "request\0" Required_argument "r"
49 "script\0" Required_argument "s"
50 "timeout\0" Required_argument "T"
51 "retries\0" Required_argument "t"
52 "tryagain\0" Required_argument "A"
53 "syslog\0" No_argument "S"
54 "request-option\0" Required_argument "O"
55 "no-default-options\0" No_argument "o"
56 "foreground\0" No_argument "f"
57 "background\0" No_argument "b"
58/// IF_FEATURE_UDHCPC_ARPING("arping\0" No_argument "a")
59 IF_FEATURE_UDHCP_PORT("client-port\0" Required_argument "P")
60 ;
61#endif
62/* Must match getopt32 option string order */
63enum {
64 OPT_i = 1 << 0,
65 OPT_n = 1 << 1,
66 OPT_p = 1 << 2,
67 OPT_q = 1 << 3,
68 OPT_R = 1 << 4,
69 OPT_r = 1 << 5,
70 OPT_s = 1 << 6,
71 OPT_T = 1 << 7,
72 OPT_t = 1 << 8,
73 OPT_S = 1 << 9,
74 OPT_A = 1 << 10,
75 OPT_O = 1 << 11,
76 OPT_o = 1 << 12,
77 OPT_x = 1 << 13,
78 OPT_f = 1 << 14,
79/* The rest has variable bit positions, need to be clever */
80 OPTBIT_f = 14,
81 USE_FOR_MMU( OPTBIT_b,)
82 ///IF_FEATURE_UDHCPC_ARPING(OPTBIT_a,)
83 IF_FEATURE_UDHCP_PORT( OPTBIT_P,)
84 USE_FOR_MMU( OPT_b = 1 << OPTBIT_b,)
85 ///IF_FEATURE_UDHCPC_ARPING(OPT_a = 1 << OPTBIT_a,)
86 IF_FEATURE_UDHCP_PORT( OPT_P = 1 << OPTBIT_P,)
87};
88
89
90/*** Utility functions ***/
91
92static void *d6_find_option(uint8_t *option, uint8_t *option_end, unsigned code)
93{
94 /* "length minus 4" */
95 int len_m4 = option_end - option - 4;
96 while (len_m4 >= 0) {
97 /* Next option's len is too big? */
98 if (option[3] > len_m4)
99 return NULL; /* yes. bogus packet! */
100 /* So far we treat any opts with code >255
101 * or len >255 as bogus, and stop at once.
102 * This simplifies big-endian handling.
103 */
104 if (option[0] != 0 || option[2] != 0)
105 return NULL;
106 /* Option seems to be valid */
107 /* Does its code match? */
108 if (option[1] == code)
109 return option; /* yes! */
110 option += option[3] + 4;
111 len_m4 -= option[3] + 4;
112 }
113 return NULL;
114}
115
116static void *d6_copy_option(uint8_t *option, uint8_t *option_end, unsigned code)
117{
118 uint8_t *opt = d6_find_option(option, option_end, code);
119 if (!opt)
120 return opt;
121 return xmemdup(opt, opt[3] + 4);
122}
123
124static void *d6_store_blob(void *dst, const void *src, unsigned len)
125{
126 memcpy(dst, src, len);
127 return dst + len;
128}
129
130
131/*** Script execution code ***/
132
133static char** new_env(void)
134{
135 client6_data.env_ptr = xrealloc_vector(client6_data.env_ptr, 3, client6_data.env_idx);
136 return &client6_data.env_ptr[client6_data.env_idx++];
137}
138
139/* put all the parameters into the environment */
140static void option_to_env(uint8_t *option, uint8_t *option_end)
141{
142 /* "length minus 4" */
143 int len_m4 = option_end - option - 4;
144 while (len_m4 >= 0) {
145 uint32_t v32;
146 char ipv6str[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")];
147
148 if (option[0] != 0 || option[2] != 0)
149 break;
150
151 switch (option[1]) {
152 //case D6_OPT_CLIENTID:
153 //case D6_OPT_SERVERID:
154 case D6_OPT_IA_NA:
155 case D6_OPT_IA_PD:
156 option_to_env(option + 16, option + 4 + option[3]);
157 break;
158 //case D6_OPT_IA_TA:
159 case D6_OPT_IAADDR:
160/* 0 1 2 3
161 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
162 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
163 * | OPTION_IAADDR | option-len |
164 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
165 * | |
166 * | IPv6 address |
167 * | |
168 * | |
169 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
170 * | preferred-lifetime |
171 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
172 * | valid-lifetime |
173 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
174 */
175 sprint_nip6(ipv6str, option + 4);
176 *new_env() = xasprintf("ipv6=%s", ipv6str);
177
178 move_from_unaligned32(v32, option + 4 + 16 + 4);
179 *new_env() = xasprintf("lease=%u", (unsigned)v32);
180 break;
181
182 //case D6_OPT_ORO:
183 //case D6_OPT_PREFERENCE:
184 //case D6_OPT_ELAPSED_TIME:
185 //case D6_OPT_RELAY_MSG:
186 //case D6_OPT_AUTH:
187 //case D6_OPT_UNICAST:
188 //case D6_OPT_STATUS_CODE:
189 //case D6_OPT_RAPID_COMMIT:
190 //case D6_OPT_USER_CLASS:
191 //case D6_OPT_VENDOR_CLASS:
192 //case D6_OPT_VENDOR_OPTS:
193 //case D6_OPT_INTERFACE_ID:
194 //case D6_OPT_RECONF_MSG:
195 //case D6_OPT_RECONF_ACCEPT:
196
197 case D6_OPT_IAPREFIX:
198/* 0 1 2 3
199 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
200 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
201 * | OPTION_IAPREFIX | option-length |
202 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
203 * | preferred-lifetime |
204 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
205 * | valid-lifetime |
206 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
207 * | prefix-length | |
208 * +-+-+-+-+-+-+-+-+ IPv6 prefix |
209 * | (16 octets) |
210 * | |
211 * | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
212 * | |
213 * +-+-+-+-+-+-+-+-+
214 */
215 //move_from_unaligned32(v32, option + 4 + 4);
216 //*new_env() = xasprintf("lease=%u", (unsigned)v32);
217
218 sprint_nip6(ipv6str, option + 4 + 4 + 1);
219 *new_env() = xasprintf("ipv6prefix=%s/%u", ipv6str, (unsigned)(option[4 + 4]));
220 }
221 option += 4 + option[3];
222 len_m4 -= 4 + option[3];
223 }
224}
225
226static char **fill_envp(struct d6_packet *packet)
227{
228 char **envp, **curr;
229
230 client6_data.env_ptr = NULL;
231 client6_data.env_idx = 0;
232
233 *new_env() = xasprintf("interface=%s", client_config.interface);
234
235 if (packet)
236 option_to_env(packet->d6_options, packet->d6_options + sizeof(packet->d6_options));
237
238 envp = curr = client6_data.env_ptr;
239 while (*curr)
240 putenv(*curr++);
241
242 return envp;
243}
244
245/* Call a script with a par file and env vars */
246static void d6_run_script(struct d6_packet *packet, const char *name)
247{
248 char **envp, **curr;
249 char *argv[3];
250
251 envp = fill_envp(packet);
252
253 /* call script */
254 log1("executing %s %s", client_config.script, name);
255 argv[0] = (char*) client_config.script;
256 argv[1] = (char*) name;
257 argv[2] = NULL;
258 spawn_and_wait(argv);
259
260 for (curr = envp; *curr; curr++) {
261 log2(" %s", *curr);
262 bb_unsetenv_and_free(*curr);
263 }
264 free(envp);
265}
266
267
268/*** Sending/receiving packets ***/
269
270static ALWAYS_INLINE uint32_t random_xid(void)
271{
272 uint32_t t = rand() & htonl(0x00ffffff);
273 return t;
274}
275
276/* Initialize the packet with the proper defaults */
277static uint8_t *init_d6_packet(struct d6_packet *packet, char type, uint32_t xid)
278{
279 struct d6_option *clientid;
280
281 memset(packet, 0, sizeof(*packet));
282
283 packet->d6_xid32 = xid;
284 packet->d6_msg_type = type;
285
286 clientid = (void*)client_config.clientid;
287 return d6_store_blob(packet->d6_options, clientid, clientid->len + 2+2);
288}
289
290static uint8_t *add_d6_client_options(uint8_t *ptr)
291{
292 return ptr;
293 //uint8_t c;
294 //int i, end, len;
295
296 /* Add a "param req" option with the list of options we'd like to have
297 * from stubborn DHCP servers. Pull the data from the struct in common.c.
298 * No bounds checking because it goes towards the head of the packet. */
299 //...
300
301 /* Add -x options if any */
302 //...
303}
304
305static int d6_mcast_from_client_config_ifindex(struct d6_packet *packet, uint8_t *end)
306{
307 static const uint8_t FF02__1_2[16] = {
308 0xFF, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
309 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02,
310 };
311
312 return d6_send_raw_packet(
313 packet, (end - (uint8_t*) packet),
314 /*src*/ NULL, CLIENT_PORT6,
315 /*dst*/ (struct in6_addr*)FF02__1_2, SERVER_PORT6, MAC_BCAST_ADDR,
316 client_config.ifindex
317 );
318}
319
320/* Milticast a DHCPv6 Solicit packet to the network, with an optionally requested IP.
321 *
322 * RFC 3315 17.1.1. Creation of Solicit Messages
323 *
324 * The client MUST include a Client Identifier option to identify itself
325 * to the server. The client includes IA options for any IAs to which
326 * it wants the server to assign addresses. The client MAY include
327 * addresses in the IAs as a hint to the server about addresses for
328 * which the client has a preference. ...
329 *
330 * The client uses IA_NA options to request the assignment of non-
331 * temporary addresses and uses IA_TA options to request the assignment
332 * of temporary addresses. Either IA_NA or IA_TA options, or a
333 * combination of both, can be included in DHCP messages.
334 *
335 * The client SHOULD include an Option Request option (see section 22.7)
336 * to indicate the options the client is interested in receiving. The
337 * client MAY additionally include instances of those options that are
338 * identified in the Option Request option, with data values as hints to
339 * the server about parameter values the client would like to have
340 * returned.
341 *
342 * The client includes a Reconfigure Accept option (see section 22.20)
343 * if the client is willing to accept Reconfigure messages from the
344 * server.
345 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
346 | OPTION_CLIENTID | option-len |
347 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
348 . .
349 . DUID .
350 . (variable length) .
351 . .
352 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
353
354
355 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
356 | OPTION_IA_NA | option-len |
357 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
358 | IAID (4 octets) |
359 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
360 | T1 |
361 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
362 | T2 |
363 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
364 | |
365 . IA_NA-options .
366 . .
367 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
368
369
370 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
371 | OPTION_IAADDR | option-len |
372 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
373 | |
374 | IPv6 address |
375 | |
376 | |
377 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
378 | preferred-lifetime |
379 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
380 | valid-lifetime |
381 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
382 . .
383 . IAaddr-options .
384 . .
385 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
386
387
388 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
389 | OPTION_ORO | option-len |
390 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
391 | requested-option-code-1 | requested-option-code-2 |
392 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
393 | ... |
394 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
395
396
397 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
398 | OPTION_RECONF_ACCEPT | 0 |
399 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
400 */
401/* NOINLINE: limit stack usage in caller */
402static NOINLINE int send_d6_discover(uint32_t xid, struct in6_addr *requested_ipv6)
403{
404 struct d6_packet packet;
405 uint8_t *opt_ptr;
406 unsigned len;
407
408 /* Fill in: msg type, client id */
409 opt_ptr = init_d6_packet(&packet, D6_MSG_SOLICIT, xid);
410
411 /* Create new IA_NA, optionally with included IAADDR with requested IP */
412 free(client6_data.ia_na);
413 len = requested_ipv6 ? 2+2+4+4+4 + 2+2+16+4+4 : 2+2+4+4+4;
414 client6_data.ia_na = xzalloc(len);
415 client6_data.ia_na->code = D6_OPT_IA_NA;
416 client6_data.ia_na->len = len - 4;
417 *(uint32_t*)client6_data.ia_na->data = rand(); /* IAID */
418 if (requested_ipv6) {
419 struct d6_option *iaaddr = (void*)(client6_data.ia_na->data + 4+4+4);
420 iaaddr->code = D6_OPT_IAADDR;
421 iaaddr->len = 16+4+4;
422 memcpy(iaaddr->data, requested_ipv6, 16);
423 }
424 opt_ptr = d6_store_blob(opt_ptr, client6_data.ia_na, len);
425
426 /* Add options:
427 * "param req" option according to -O, options specified with -x
428 */
429 opt_ptr = add_d6_client_options(opt_ptr);
430
431 bb_error_msg("sending %s", "discover");
432 return d6_mcast_from_client_config_ifindex(&packet, opt_ptr);
433}
434
435/* Multicast a DHCPv6 request message
436 *
437 * RFC 3315 18.1.1. Creation and Transmission of Request Messages
438 *
439 * The client uses a Request message to populate IAs with addresses and
440 * obtain other configuration information. The client includes one or
441 * more IA options in the Request message. The server then returns
442 * addresses and other information about the IAs to the client in IA
443 * options in a Reply message.
444 *
445 * The client generates a transaction ID and inserts this value in the
446 * "transaction-id" field.
447 *
448 * The client places the identifier of the destination server in a
449 * Server Identifier option.
450 *
451 * The client MUST include a Client Identifier option to identify itself
452 * to the server. The client adds any other appropriate options,
453 * including one or more IA options (if the client is requesting that
454 * the server assign it some network addresses).
455 *
456 * The client MUST include an Option Request option (see section 22.7)
457 * to indicate the options the client is interested in receiving. The
458 * client MAY include options with data values as hints to the server
459 * about parameter values the client would like to have returned.
460 *
461 * The client includes a Reconfigure Accept option (see section 22.20)
462 * indicating whether or not the client is willing to accept Reconfigure
463 * messages from the server.
464 */
465/* NOINLINE: limit stack usage in caller */
466static NOINLINE int send_d6_select(uint32_t xid)
467{
468 struct d6_packet packet;
469 uint8_t *opt_ptr;
470
471 /* Fill in: msg type, client id */
472 opt_ptr = init_d6_packet(&packet, D6_MSG_REQUEST, xid);
473
474 /* server id */
475 opt_ptr = d6_store_blob(opt_ptr, client6_data.server_id, client6_data.server_id->len + 2+2);
476 /* IA NA (contains requested IP) */
477 opt_ptr = d6_store_blob(opt_ptr, client6_data.ia_na, client6_data.ia_na->len + 2+2);
478
479 /* Add options:
480 * "param req" option according to -O, options specified with -x
481 */
482 opt_ptr = add_d6_client_options(opt_ptr);
483
484 bb_error_msg("sending %s", "select");
485 return d6_mcast_from_client_config_ifindex(&packet, opt_ptr);
486}
487
488/* Unicast or broadcast a DHCP renew message
489 *
490 * RFC 3315 18.1.3. Creation and Transmission of Renew Messages
491 *
492 * To extend the valid and preferred lifetimes for the addresses
493 * associated with an IA, the client sends a Renew message to the server
494 * from which the client obtained the addresses in the IA containing an
495 * IA option for the IA. The client includes IA Address options in the
496 * IA option for the addresses associated with the IA. The server
497 * determines new lifetimes for the addresses in the IA according to the
498 * administrative configuration of the server. The server may also add
499 * new addresses to the IA. The server may remove addresses from the IA
500 * by setting the preferred and valid lifetimes of those addresses to
501 * zero.
502 *
503 * The server controls the time at which the client contacts the server
504 * to extend the lifetimes on assigned addresses through the T1 and T2
505 * parameters assigned to an IA.
506 *
507 * At time T1 for an IA, the client initiates a Renew/Reply message
508 * exchange to extend the lifetimes on any addresses in the IA. The
509 * client includes an IA option with all addresses currently assigned to
510 * the IA in its Renew message.
511 *
512 * If T1 or T2 is set to 0 by the server (for an IA_NA) or there are no
513 * T1 or T2 times (for an IA_TA), the client may send a Renew or Rebind
514 * message, respectively, at the client's discretion.
515 *
516 * The client sets the "msg-type" field to RENEW. The client generates
517 * a transaction ID and inserts this value in the "transaction-id"
518 * field.
519 *
520 * The client places the identifier of the destination server in a
521 * Server Identifier option.
522 *
523 * The client MUST include a Client Identifier option to identify itself
524 * to the server. The client adds any appropriate options, including
525 * one or more IA options. The client MUST include the list of
526 * addresses the client currently has associated with the IAs in the
527 * Renew message.
528 *
529 * The client MUST include an Option Request option (see section 22.7)
530 * to indicate the options the client is interested in receiving. The
531 * client MAY include options with data values as hints to the server
532 * about parameter values the client would like to have returned.
533 */
534/* NOINLINE: limit stack usage in caller */
535static NOINLINE int send_d6_renew(uint32_t xid, struct in6_addr *server_ipv6, struct in6_addr *our_cur_ipv6)
536{
537 struct d6_packet packet;
538 uint8_t *opt_ptr;
539
540 /* Fill in: msg type, client id */
541 opt_ptr = init_d6_packet(&packet, DHCPREQUEST, xid);
542
543 /* server id */
544 opt_ptr = d6_store_blob(opt_ptr, client6_data.server_id, client6_data.server_id->len + 2+2);
545 /* IA NA (contains requested IP) */
546 opt_ptr = d6_store_blob(opt_ptr, client6_data.ia_na, client6_data.ia_na->len + 2+2);
547
548 /* Add options:
549 * "param req" option according to -O, options specified with -x
550 */
551 opt_ptr = add_d6_client_options(opt_ptr);
552
553 bb_error_msg("sending %s", "renew");
554 if (server_ipv6)
555 return d6_send_kernel_packet(
556 &packet, (opt_ptr - (uint8_t*) &packet),
557 our_cur_ipv6, CLIENT_PORT6,
558 server_ipv6, SERVER_PORT6
559 );
560 return d6_mcast_from_client_config_ifindex(&packet, opt_ptr);
561}
562
563/* Unicast a DHCP release message */
564static int send_d6_release(struct in6_addr *server_ipv6, struct in6_addr *our_cur_ipv6)
565{
566 struct d6_packet packet;
567 uint8_t *opt_ptr;
568
569 /* Fill in: msg type, client id */
570 opt_ptr = init_d6_packet(&packet, D6_MSG_RELEASE, random_xid());
571 /* server id */
572 opt_ptr = d6_store_blob(opt_ptr, client6_data.server_id, client6_data.server_id->len + 2+2);
573 /* IA NA (contains our current IP) */
574 opt_ptr = d6_store_blob(opt_ptr, client6_data.ia_na, client6_data.ia_na->len + 2+2);
575
576 bb_error_msg("sending %s", "release");
577 return d6_send_kernel_packet(
578 &packet, (opt_ptr - (uint8_t*) &packet),
579 our_cur_ipv6, CLIENT_PORT6,
580 server_ipv6, SERVER_PORT6
581 );
582}
583
584/* Returns -1 on errors that are fatal for the socket, -2 for those that aren't */
585/* NOINLINE: limit stack usage in caller */
586static NOINLINE int d6_recv_raw_packet(struct in6_addr *peer_ipv6
587 UNUSED_PARAM
588 , struct d6_packet *d6_pkt, int fd)
589{
590 int bytes;
591 struct ip6_udp_d6_packet packet;
592
593 bytes = safe_read(fd, &packet, sizeof(packet));
594 if (bytes < 0) {
595 log1("packet read error, ignoring");
596 /* NB: possible down interface, etc. Caller should pause. */
597 return bytes; /* returns -1 */
598 }
599
600 if (bytes < (int) (sizeof(packet.ip6) + sizeof(packet.udp))) {
601 log1("packet is too short, ignoring");
602 return -2;
603 }
604
605 if (bytes < sizeof(packet.ip6) + ntohs(packet.ip6.ip6_plen)) {
606 /* packet is bigger than sizeof(packet), we did partial read */
607 log1("oversized packet, ignoring");
608 return -2;
609 }
610
611 /* ignore any extra garbage bytes */
612 bytes = sizeof(packet.ip6) + ntohs(packet.ip6.ip6_plen);
613
614 /* make sure its the right packet for us, and that it passes sanity checks */
615 if (packet.ip6.ip6_nxt != IPPROTO_UDP
616 || (packet.ip6.ip6_vfc >> 4) != 6
617 || packet.udp.dest != htons(CLIENT_PORT6)
618 /* || bytes > (int) sizeof(packet) - can't happen */
619 || packet.udp.len != packet.ip6.ip6_plen
620 ) {
621 log1("unrelated/bogus packet, ignoring");
622 return -2;
623 }
624
625//How to do this for ipv6?
626// /* verify UDP checksum. IP header has to be modified for this */
627// memset(&packet.ip, 0, offsetof(struct iphdr, protocol));
628// /* ip.xx fields which are not memset: protocol, check, saddr, daddr */
629// packet.ip.tot_len = packet.udp.len; /* yes, this is needed */
630// check = packet.udp.check;
631// packet.udp.check = 0;
632// if (check && check != inet_cksum((uint16_t *)&packet, bytes)) {
633// log1("packet with bad UDP checksum received, ignoring");
634// return -2;
635// }
636
637 log1("received %s", "a packet");
638 d6_dump_packet(&packet.data);
639
640 bytes -= sizeof(packet.ip6) + sizeof(packet.udp);
641 memcpy(d6_pkt, &packet.data, bytes);
642 return bytes;
643}
644
645
646/*** Main ***/
647
648static int sockfd = -1;
649
650#define LISTEN_NONE 0
651#define LISTEN_KERNEL 1
652#define LISTEN_RAW 2
653static smallint listen_mode;
654
655/* initial state: (re)start DHCP negotiation */
656#define INIT_SELECTING 0
657/* discover was sent, DHCPOFFER reply received */
658#define REQUESTING 1
659/* select/renew was sent, DHCPACK reply received */
660#define BOUND 2
661/* half of lease passed, want to renew it by sending unicast renew requests */
662#define RENEWING 3
663/* renew requests were not answered, lease is almost over, send broadcast renew */
664#define REBINDING 4
665/* manually requested renew (SIGUSR1) */
666#define RENEW_REQUESTED 5
667/* release, possibly manually requested (SIGUSR2) */
668#define RELEASED 6
669static smallint state;
670
671static int d6_raw_socket(int ifindex)
672{
673 int fd;
674 struct sockaddr_ll sock;
675
676 /*
677 * Comment:
678 *
679 * I've selected not to see LL header, so BPF doesn't see it, too.
680 * The filter may also pass non-IP and non-ARP packets, but we do
681 * a more complete check when receiving the message in userspace.
682 *
683 * and filter shamelessly stolen from:
684 *
685 * http://www.flamewarmaster.de/software/dhcpclient/
686 *
687 * There are a few other interesting ideas on that page (look under
688 * "Motivation"). Use of netlink events is most interesting. Think
689 * of various network servers listening for events and reconfiguring.
690 * That would obsolete sending HUP signals and/or make use of restarts.
691 *
692 * Copyright: 2006, 2007 Stefan Rompf <sux@loplof.de>.
693 * License: GPL v2.
694 *
695 * TODO: make conditional?
696 */
697#if 0
698 static const struct sock_filter filter_instr[] = {
699 /* load 9th byte (protocol) */
700 BPF_STMT(BPF_LD|BPF_B|BPF_ABS, 9),
701 /* jump to L1 if it is IPPROTO_UDP, else to L4 */
702 BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, IPPROTO_UDP, 0, 6),
703 /* L1: load halfword from offset 6 (flags and frag offset) */
704 BPF_STMT(BPF_LD|BPF_H|BPF_ABS, 6),
705 /* jump to L4 if any bits in frag offset field are set, else to L2 */
706 BPF_JUMP(BPF_JMP|BPF_JSET|BPF_K, 0x1fff, 4, 0),
707 /* L2: skip IP header (load index reg with header len) */
708 BPF_STMT(BPF_LDX|BPF_B|BPF_MSH, 0),
709 /* load udp destination port from halfword[header_len + 2] */
710 BPF_STMT(BPF_LD|BPF_H|BPF_IND, 2),
711 /* jump to L3 if udp dport is CLIENT_PORT6, else to L4 */
712 BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, 68, 0, 1),
713 /* L3: accept packet */
714 BPF_STMT(BPF_RET|BPF_K, 0x7fffffff),
715 /* L4: discard packet */
716 BPF_STMT(BPF_RET|BPF_K, 0),
717 };
718 static const struct sock_fprog filter_prog = {
719 .len = sizeof(filter_instr) / sizeof(filter_instr[0]),
720 /* casting const away: */
721 .filter = (struct sock_filter *) filter_instr,
722 };
723#endif
724
725 log1("opening raw socket on ifindex %d", ifindex); //log2?
726
727 fd = xsocket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IPV6));
728 log1("got raw socket fd %d", fd); //log2?
729
730 sock.sll_family = AF_PACKET;
731 sock.sll_protocol = htons(ETH_P_IPV6);
732 sock.sll_ifindex = ifindex;
733 xbind(fd, (struct sockaddr *) &sock, sizeof(sock));
734
735#if 0
736 if (CLIENT_PORT6 == 546) {
737 /* Use only if standard port is in use */
738 /* Ignoring error (kernel may lack support for this) */
739 if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter_prog,
740 sizeof(filter_prog)) >= 0)
741 log1("attached filter to raw socket fd %d", fd); // log?
742 }
743#endif
744
745 log1("created raw socket");
746
747 return fd;
748}
749
750static void change_listen_mode(int new_mode)
751{
752 log1("entering listen mode: %s",
753 new_mode != LISTEN_NONE
754 ? (new_mode == LISTEN_KERNEL ? "kernel" : "raw")
755 : "none"
756 );
757
758 listen_mode = new_mode;
759 if (sockfd >= 0) {
760 close(sockfd);
761 sockfd = -1;
762 }
763 if (new_mode == LISTEN_KERNEL)
764 sockfd = udhcp_listen_socket(/*INADDR_ANY,*/ CLIENT_PORT6, client_config.interface);
765 else if (new_mode != LISTEN_NONE)
766 sockfd = d6_raw_socket(client_config.ifindex);
767 /* else LISTEN_NONE: sockfd stays closed */
768}
769
770/* Called only on SIGUSR1 */
771static void perform_renew(void)
772{
773 bb_error_msg("performing DHCP renew");
774 switch (state) {
775 case BOUND:
776 change_listen_mode(LISTEN_KERNEL);
777 case RENEWING:
778 case REBINDING:
779 state = RENEW_REQUESTED;
780 break;
781 case RENEW_REQUESTED: /* impatient are we? fine, square 1 */
782 d6_run_script(NULL, "deconfig");
783 case REQUESTING:
784 case RELEASED:
785 change_listen_mode(LISTEN_RAW);
786 state = INIT_SELECTING;
787 break;
788 case INIT_SELECTING:
789 break;
790 }
791}
792
793static void perform_d6_release(struct in6_addr *server_ipv6, struct in6_addr *our_cur_ipv6)
794{
795 /* send release packet */
796 if (state == BOUND || state == RENEWING || state == REBINDING) {
797 bb_error_msg("unicasting a release");
798 send_d6_release(server_ipv6, our_cur_ipv6); /* unicast */
799 d6_run_script(NULL, "deconfig");
800 }
801 bb_error_msg("entering released state");
802
803 change_listen_mode(LISTEN_NONE);
804 state = RELEASED;
805}
806
807///static uint8_t* alloc_dhcp_option(int code, const char *str, int extra)
808///{
809/// uint8_t *storage;
810/// int len = strnlen(str, 255);
811/// storage = xzalloc(len + extra + OPT_DATA);
812/// storage[OPT_CODE] = code;
813/// storage[OPT_LEN] = len + extra;
814/// memcpy(storage + extra + OPT_DATA, str, len);
815/// return storage;
816///}
817
818#if BB_MMU
819static void client_background(void)
820{
821 bb_daemonize(0);
822 logmode &= ~LOGMODE_STDIO;
823 /* rewrite pidfile, as our pid is different now */
824 write_pidfile(client_config.pidfile);
825}
826#endif
827
828//usage:#if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 1
829//usage:# define IF_UDHCP_VERBOSE(...) __VA_ARGS__
830//usage:#else
831//usage:# define IF_UDHCP_VERBOSE(...)
832//usage:#endif
833//usage:#define udhcpc6_trivial_usage
834//usage: "[-fbnq"IF_UDHCP_VERBOSE("v")"oR] [-i IFACE] [-r IP] [-s PROG] [-p PIDFILE]\n"
835//usage: " [-x OPT:VAL]... [-O OPT]..." IF_FEATURE_UDHCP_PORT(" [-P N]")
836//usage:#define udhcpc6_full_usage "\n"
837//usage: IF_LONG_OPTS(
838//usage: "\n -i,--interface IFACE Interface to use (default eth0)"
839//usage: "\n -p,--pidfile FILE Create pidfile"
840//usage: "\n -s,--script PROG Run PROG at DHCP events (default "CONFIG_UDHCPC_DEFAULT_SCRIPT")"
841//usage: "\n -B,--broadcast Request broadcast replies"
842//usage: "\n -t,--retries N Send up to N discover packets"
843//usage: "\n -T,--timeout N Pause between packets (default 3 seconds)"
844//usage: "\n -A,--tryagain N Wait N seconds after failure (default 20)"
845//usage: "\n -f,--foreground Run in foreground"
846//usage: USE_FOR_MMU(
847//usage: "\n -b,--background Background if lease is not obtained"
848//usage: )
849//usage: "\n -n,--now Exit if lease is not obtained"
850//usage: "\n -q,--quit Exit after obtaining lease"
851//usage: "\n -R,--release Release IP on exit"
852//usage: "\n -S,--syslog Log to syslog too"
853//usage: IF_FEATURE_UDHCP_PORT(
854//usage: "\n -P,--client-port N Use port N (default 546)"
855//usage: )
856////usage: IF_FEATURE_UDHCPC_ARPING(
857////usage: "\n -a,--arping Use arping to validate offered address"
858////usage: )
859//usage: "\n -O,--request-option OPT Request option OPT from server (cumulative)"
860//usage: "\n -o,--no-default-options Don't request any options (unless -O is given)"
861//usage: "\n -r,--request IP Request this IP address"
862//usage: "\n -x OPT:VAL Include option OPT in sent packets (cumulative)"
863//usage: "\n Examples of string, numeric, and hex byte opts:"
864//usage: "\n -x hostname:bbox - option 12"
865//usage: "\n -x lease:3600 - option 51 (lease time)"
866//usage: "\n -x 0x3d:0100BEEFC0FFEE - option 61 (client id)"
867//usage: IF_UDHCP_VERBOSE(
868//usage: "\n -v Verbose"
869//usage: )
870//usage: )
871//usage: IF_NOT_LONG_OPTS(
872//usage: "\n -i IFACE Interface to use (default eth0)"
873//usage: "\n -p FILE Create pidfile"
874//usage: "\n -s PROG Run PROG at DHCP events (default "CONFIG_UDHCPC_DEFAULT_SCRIPT")"
875//usage: "\n -B Request broadcast replies"
876//usage: "\n -t N Send up to N discover packets"
877//usage: "\n -T N Pause between packets (default 3 seconds)"
878//usage: "\n -A N Wait N seconds (default 20) after failure"
879//usage: "\n -f Run in foreground"
880//usage: USE_FOR_MMU(
881//usage: "\n -b Background if lease is not obtained"
882//usage: )
883//usage: "\n -n Exit if lease is not obtained"
884//usage: "\n -q Exit after obtaining lease"
885//usage: "\n -R Release IP on exit"
886//usage: "\n -S Log to syslog too"
887//usage: IF_FEATURE_UDHCP_PORT(
888//usage: "\n -P N Use port N (default 546)"
889//usage: )
890////usage: IF_FEATURE_UDHCPC_ARPING(
891////usage: "\n -a Use arping to validate offered address"
892////usage: )
893//usage: "\n -O OPT Request option OPT from server (cumulative)"
894//usage: "\n -o Don't request any options (unless -O is given)"
895//usage: "\n -r IP Request this IP address"
896//usage: "\n -x OPT:VAL Include option OPT in sent packets (cumulative)"
897//usage: "\n Examples of string, numeric, and hex byte opts:"
898//usage: "\n -x hostname:bbox - option 12"
899//usage: "\n -x lease:3600 - option 51 (lease time)"
900//usage: "\n -x 0x3d:0100BEEFC0FFEE - option 61 (client id)"
901//usage: IF_UDHCP_VERBOSE(
902//usage: "\n -v Verbose"
903//usage: )
904//usage: )
905//usage: "\nSignals:"
906//usage: "\n USR1 Renew lease"
907//usage: "\n USR2 Release lease"
908
909
910int udhcpc6_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
911int udhcpc6_main(int argc UNUSED_PARAM, char **argv)
912{
913 const char *str_r;
914 IF_FEATURE_UDHCP_PORT(char *str_P;)
915 void *clientid_mac_ptr;
916 llist_t *list_O = NULL;
917 llist_t *list_x = NULL;
918 int tryagain_timeout = 20;
919 int discover_timeout = 3;
920 int discover_retries = 3;
921 struct in6_addr srv6_buf;
922 struct in6_addr ipv6_buf;
923 struct in6_addr *requested_ipv6;
924 uint32_t xid = 0;
925 int packet_num;
926 int timeout; /* must be signed */
927 unsigned already_waited_sec;
928 unsigned opt;
929 int max_fd;
930 int retval;
931 fd_set rfds;
932
933 setup_common_bufsiz();
934
935 /* Default options */
936 IF_FEATURE_UDHCP_PORT(SERVER_PORT6 = 547;)
937 IF_FEATURE_UDHCP_PORT(CLIENT_PORT6 = 546;)
938 client_config.interface = "eth0";
939 client_config.script = CONFIG_UDHCPC_DEFAULT_SCRIPT;
940
941 /* Parse command line */
942 /* O,x: list; -T,-t,-A take numeric param */
943 opt_complementary = "O::x::T+:t+:A+" IF_UDHCP_VERBOSE(":vv") ;
944 IF_LONG_OPTS(applet_long_options = udhcpc6_longopts;)
945 opt = getopt32(argv, "i:np:qRr:s:T:t:SA:O:ox:f"
946 USE_FOR_MMU("b")
947 ///IF_FEATURE_UDHCPC_ARPING("a")
948 IF_FEATURE_UDHCP_PORT("P:")
949 "v"
950 , &client_config.interface, &client_config.pidfile, &str_r /* i,p */
951 , &client_config.script /* s */
952 , &discover_timeout, &discover_retries, &tryagain_timeout /* T,t,A */
953 , &list_O
954 , &list_x
955 IF_FEATURE_UDHCP_PORT(, &str_P)
956 IF_UDHCP_VERBOSE(, &dhcp_verbose)
957 );
958 requested_ipv6 = NULL;
959 if (opt & OPT_r) {
960 if (inet_pton(AF_INET6, str_r, &ipv6_buf) <= 0)
961 bb_error_msg_and_die("bad IPv6 address '%s'", str_r);
962 requested_ipv6 = &ipv6_buf;
963 }
964#if ENABLE_FEATURE_UDHCP_PORT
965 if (opt & OPT_P) {
966 CLIENT_PORT6 = xatou16(str_P);
967 SERVER_PORT6 = CLIENT_PORT6 + 1;
968 }
969#endif
970 while (list_O) {
971 char *optstr = llist_pop(&list_O);
972 unsigned n = bb_strtou(optstr, NULL, 0);
973 if (errno || n > 254) {
974 n = udhcp_option_idx(optstr);
975 n = dhcp_optflags[n].code;
976 }
977 client_config.opt_mask[n >> 3] |= 1 << (n & 7);
978 }
979 if (!(opt & OPT_o)) {
980 /*
981 unsigned i, n;
982 for (i = 0; (n = dhcp_optflags[i].code) != 0; i++) {
983 if (dhcp_optflags[i].flags & OPTION_REQ) {
984 client_config.opt_mask[n >> 3] |= 1 << (n & 7);
985 }
986 }
987 */
988 }
989 while (list_x) {
990 char *optstr = llist_pop(&list_x);
991 char *colon = strchr(optstr, ':');
992 if (colon)
993 *colon = ' ';
994 /* now it looks similar to udhcpd's config file line:
995 * "optname optval", using the common routine: */
996 udhcp_str2optset(optstr, &client_config.options);
997 }
998
999 if (udhcp_read_interface(client_config.interface,
1000 &client_config.ifindex,
1001 NULL,
1002 client_config.client_mac)
1003 ) {
1004 return 1;
1005 }
1006
1007 /* Create client ID based on mac, set clientid_mac_ptr */
1008 {
1009 struct d6_option *clientid;
1010 clientid = xzalloc(2+2+2+2+6);
1011 clientid->code = D6_OPT_CLIENTID;
1012 clientid->len = 2+2+6;
1013 clientid->data[1] = 3; /* DUID-LL */
1014 clientid->data[3] = 1; /* ethernet */
1015 clientid_mac_ptr = clientid->data + 2+2;
1016 memcpy(clientid_mac_ptr, client_config.client_mac, 6);
1017 client_config.clientid = (void*)clientid;
1018 }
1019
1020#if !BB_MMU
1021 /* on NOMMU reexec (i.e., background) early */
1022 if (!(opt & OPT_f)) {
1023 bb_daemonize_or_rexec(0 /* flags */, argv);
1024 logmode = LOGMODE_NONE;
1025 }
1026#endif
1027 if (opt & OPT_S) {
1028 openlog(applet_name, LOG_PID, LOG_DAEMON);
1029 logmode |= LOGMODE_SYSLOG;
1030 }
1031
1032 /* Make sure fd 0,1,2 are open */
1033 bb_sanitize_stdio();
1034 /* Equivalent of doing a fflush after every \n */
1035 setlinebuf(stdout);
1036 /* Create pidfile */
1037 write_pidfile(client_config.pidfile);
1038 /* Goes to stdout (unless NOMMU) and possibly syslog */
1039 bb_error_msg("started, v"BB_VER);
1040 /* Set up the signal pipe */
1041 udhcp_sp_setup();
1042 /* We want random_xid to be random... */
1043 srand(monotonic_us());
1044
1045 state = INIT_SELECTING;
1046 d6_run_script(NULL, "deconfig");
1047 change_listen_mode(LISTEN_RAW);
1048 packet_num = 0;
1049 timeout = 0;
1050 already_waited_sec = 0;
1051
1052 /* Main event loop. select() waits on signal pipe and possibly
1053 * on sockfd.
1054 * "continue" statements in code below jump to the top of the loop.
1055 */
1056 for (;;) {
1057 struct timeval tv;
1058 struct d6_packet packet;
1059 uint8_t *packet_end;
1060 /* silence "uninitialized!" warning */
1061 unsigned timestamp_before_wait = timestamp_before_wait;
1062
1063 //bb_error_msg("sockfd:%d, listen_mode:%d", sockfd, listen_mode);
1064
1065 /* Was opening raw or udp socket here
1066 * if (listen_mode != LISTEN_NONE && sockfd < 0),
1067 * but on fast network renew responses return faster
1068 * than we open sockets. Thus this code is moved
1069 * to change_listen_mode(). Thus we open listen socket
1070 * BEFORE we send renew request (see "case BOUND:"). */
1071
1072 max_fd = udhcp_sp_fd_set(&rfds, sockfd);
1073
1074 tv.tv_sec = timeout - already_waited_sec;
1075 tv.tv_usec = 0;
1076 retval = 0;
1077 /* If we already timed out, fall through with retval = 0, else... */
1078 if ((int)tv.tv_sec > 0) {
1079 log1("waiting on select %u seconds", (int)tv.tv_sec);
1080 timestamp_before_wait = (unsigned)monotonic_sec();
1081 retval = select(max_fd + 1, &rfds, NULL, NULL, &tv);
1082 if (retval < 0) {
1083 /* EINTR? A signal was caught, don't panic */
1084 if (errno == EINTR) {
1085 already_waited_sec += (unsigned)monotonic_sec() - timestamp_before_wait;
1086 continue;
1087 }
1088 /* Else: an error occured, panic! */
1089 bb_perror_msg_and_die("select");
1090 }
1091 }
1092
1093 /* If timeout dropped to zero, time to become active:
1094 * resend discover/renew/whatever
1095 */
1096 if (retval == 0) {
1097 /* When running on a bridge, the ifindex may have changed
1098 * (e.g. if member interfaces were added/removed
1099 * or if the status of the bridge changed).
1100 * Refresh ifindex and client_mac:
1101 */
1102 if (udhcp_read_interface(client_config.interface,
1103 &client_config.ifindex,
1104 NULL,
1105 client_config.client_mac)
1106 ) {
1107 goto ret0; /* iface is gone? */
1108 }
1109 memcpy(clientid_mac_ptr, client_config.client_mac, 6);
1110
1111 /* We will restart the wait in any case */
1112 already_waited_sec = 0;
1113
1114 switch (state) {
1115 case INIT_SELECTING:
1116 if (!discover_retries || packet_num < discover_retries) {
1117 if (packet_num == 0)
1118 xid = random_xid();
1119 /* multicast */
1120 send_d6_discover(xid, requested_ipv6);
1121 timeout = discover_timeout;
1122 packet_num++;
1123 continue;
1124 }
1125 leasefail:
1126 d6_run_script(NULL, "leasefail");
1127#if BB_MMU /* -b is not supported on NOMMU */
1128 if (opt & OPT_b) { /* background if no lease */
1129 bb_error_msg("no lease, forking to background");
1130 client_background();
1131 /* do not background again! */
1132 opt = ((opt & ~OPT_b) | OPT_f);
1133 } else
1134#endif
1135 if (opt & OPT_n) { /* abort if no lease */
1136 bb_error_msg("no lease, failing");
1137 retval = 1;
1138 goto ret;
1139 }
1140 /* wait before trying again */
1141 timeout = tryagain_timeout;
1142 packet_num = 0;
1143 continue;
1144 case REQUESTING:
1145 if (!discover_retries || packet_num < discover_retries) {
1146 /* send multicast select packet */
1147 send_d6_select(xid);
1148 timeout = discover_timeout;
1149 packet_num++;
1150 continue;
1151 }
1152 /* Timed out, go back to init state.
1153 * "discover...select...discover..." loops
1154 * were seen in the wild. Treat them similarly
1155 * to "no response to discover" case */
1156 change_listen_mode(LISTEN_RAW);
1157 state = INIT_SELECTING;
1158 goto leasefail;
1159 case BOUND:
1160 /* 1/2 lease passed, enter renewing state */
1161 state = RENEWING;
1162 client_config.first_secs = 0; /* make secs field count from 0 */
1163 change_listen_mode(LISTEN_KERNEL);
1164 log1("entering renew state");
1165 /* fall right through */
1166 case RENEW_REQUESTED: /* manual (SIGUSR1) renew */
1167 case_RENEW_REQUESTED:
1168 case RENEWING:
1169 if (timeout > 60) {
1170 /* send an unicast renew request */
1171 /* Sometimes observed to fail (EADDRNOTAVAIL) to bind
1172 * a new UDP socket for sending inside send_renew.
1173 * I hazard to guess existing listening socket
1174 * is somehow conflicting with it, but why is it
1175 * not deterministic then?! Strange.
1176 * Anyway, it does recover by eventually failing through
1177 * into INIT_SELECTING state.
1178 */
1179 send_d6_renew(xid, &srv6_buf, requested_ipv6);
1180 timeout >>= 1;
1181 continue;
1182 }
1183 /* Timed out, enter rebinding state */
1184 log1("entering rebinding state");
1185 state = REBINDING;
1186 /* fall right through */
1187 case REBINDING:
1188 /* Switch to bcast receive */
1189 change_listen_mode(LISTEN_RAW);
1190 /* Lease is *really* about to run out,
1191 * try to find DHCP server using broadcast */
1192 if (timeout > 0) {
1193 /* send a broadcast renew request */
1194 send_d6_renew(xid, /*server_ipv6:*/ NULL, requested_ipv6);
1195 timeout >>= 1;
1196 continue;
1197 }
1198 /* Timed out, enter init state */
1199 bb_error_msg("lease lost, entering init state");
1200 d6_run_script(NULL, "deconfig");
1201 state = INIT_SELECTING;
1202 client_config.first_secs = 0; /* make secs field count from 0 */
1203 /*timeout = 0; - already is */
1204 packet_num = 0;
1205 continue;
1206 /* case RELEASED: */
1207 }
1208 /* yah, I know, *you* say it would never happen */
1209 timeout = INT_MAX;
1210 continue; /* back to main loop */
1211 } /* if select timed out */
1212
1213 /* select() didn't timeout, something happened */
1214
1215 /* Is it a signal? */
1216 /* note: udhcp_sp_read checks FD_ISSET before reading */
1217 switch (udhcp_sp_read(&rfds)) {
1218 case SIGUSR1:
1219 client_config.first_secs = 0; /* make secs field count from 0 */
1220 already_waited_sec = 0;
1221 perform_renew();
1222 if (state == RENEW_REQUESTED) {
1223 /* We might be either on the same network
1224 * (in which case renew might work),
1225 * or we might be on a completely different one
1226 * (in which case renew won't ever succeed).
1227 * For the second case, must make sure timeout
1228 * is not too big, or else we can send
1229 * futile renew requests for hours.
1230 * (Ab)use -A TIMEOUT value (usually 20 sec)
1231 * as a cap on the timeout.
1232 */
1233 if (timeout > tryagain_timeout)
1234 timeout = tryagain_timeout;
1235 goto case_RENEW_REQUESTED;
1236 }
1237 /* Start things over */
1238 packet_num = 0;
1239 /* Kill any timeouts, user wants this to hurry along */
1240 timeout = 0;
1241 continue;
1242 case SIGUSR2:
1243 perform_d6_release(&srv6_buf, requested_ipv6);
1244 timeout = INT_MAX;
1245 continue;
1246 case SIGTERM:
1247 bb_error_msg("received %s", "SIGTERM");
1248 goto ret0;
1249 }
1250
1251 /* Is it a packet? */
1252 if (listen_mode == LISTEN_NONE || !FD_ISSET(sockfd, &rfds))
1253 continue; /* no */
1254
1255 {
1256 int len;
1257
1258 /* A packet is ready, read it */
1259 if (listen_mode == LISTEN_KERNEL)
1260 len = d6_recv_kernel_packet(&srv6_buf, &packet, sockfd);
1261 else
1262 len = d6_recv_raw_packet(&srv6_buf, &packet, sockfd);
1263 if (len == -1) {
1264 /* Error is severe, reopen socket */
1265 bb_error_msg("read error: %s, reopening socket", strerror(errno));
1266 sleep(discover_timeout); /* 3 seconds by default */
1267 change_listen_mode(listen_mode); /* just close and reopen */
1268 }
1269 /* If this packet will turn out to be unrelated/bogus,
1270 * we will go back and wait for next one.
1271 * Be sure timeout is properly decreased. */
1272 already_waited_sec += (unsigned)monotonic_sec() - timestamp_before_wait;
1273 if (len < 0)
1274 continue;
1275 packet_end = (uint8_t*)&packet + len;
1276 }
1277
1278 if ((packet.d6_xid32 & htonl(0x00ffffff)) != xid) {
1279 log1("xid %x (our is %x), ignoring packet",
1280 (unsigned)(packet.d6_xid32 & htonl(0x00ffffff)), (unsigned)xid);
1281 continue;
1282 }
1283
1284 switch (state) {
1285 case INIT_SELECTING:
1286 if (packet.d6_msg_type == D6_MSG_ADVERTISE)
1287 goto type_is_ok;
1288 /* DHCPv6 has "Rapid Commit", when instead of Advertise,
1289 * server sends Reply right away.
1290 * Fall through to check for this case.
1291 */
1292 case REQUESTING:
1293 case RENEWING:
1294 case RENEW_REQUESTED:
1295 case REBINDING:
1296 if (packet.d6_msg_type == D6_MSG_REPLY) {
1297 uint32_t lease_seconds;
1298 struct d6_option *option, *iaaddr;
1299 type_is_ok:
1300 option = d6_find_option(packet.d6_options, packet_end, D6_OPT_STATUS_CODE);
1301 if (option && option->data[4] != 0) {
1302 /* return to init state */
1303 bb_error_msg("received DHCP NAK (%u)", option->data[4]);
1304 d6_run_script(&packet, "nak");
1305 if (state != REQUESTING)
1306 d6_run_script(NULL, "deconfig");
1307 change_listen_mode(LISTEN_RAW);
1308 sleep(3); /* avoid excessive network traffic */
1309 state = INIT_SELECTING;
1310 client_config.first_secs = 0; /* make secs field count from 0 */
1311 requested_ipv6 = NULL;
1312 timeout = 0;
1313 packet_num = 0;
1314 already_waited_sec = 0;
1315 continue;
1316 }
1317 option = d6_copy_option(packet.d6_options, packet_end, D6_OPT_SERVERID);
1318 if (!option) {
1319 bb_error_msg("no server ID, ignoring packet");
1320 continue;
1321 /* still selecting - this server looks bad */
1322 }
1323//Note: we do not bother comparing server IDs in Advertise and Reply msgs.
1324//server_id variable is used solely for creation of proper server_id option
1325//in outgoing packets. (why DHCPv6 even introduced it is a mystery).
1326 free(client6_data.server_id);
1327 client6_data.server_id = option;
1328 if (packet.d6_msg_type == D6_MSG_ADVERTISE) {
1329 /* enter requesting state */
1330 state = REQUESTING;
1331 timeout = 0;
1332 packet_num = 0;
1333 already_waited_sec = 0;
1334 continue;
1335 }
1336 /* It's a D6_MSG_REPLY */
1337/*
1338 * RFC 3315 18.1.8. Receipt of Reply Messages
1339 *
1340 * Upon the receipt of a valid Reply message in response to a Solicit
1341 * (with a Rapid Commit option), Request, Confirm, Renew, Rebind or
1342 * Information-request message, the client extracts the configuration
1343 * information contained in the Reply. The client MAY choose to report
1344 * any status code or message from the status code option in the Reply
1345 * message.
1346 *
1347 * The client SHOULD perform duplicate address detection [17] on each of
1348 * the addresses in any IAs it receives in the Reply message before
1349 * using that address for traffic. If any of the addresses are found to
1350 * be in use on the link, the client sends a Decline message to the
1351 * server as described in section 18.1.7.
1352 *
1353 * If the Reply was received in response to a Solicit (with a Rapid
1354 * Commit option), Request, Renew or Rebind message, the client updates
1355 * the information it has recorded about IAs from the IA options
1356 * contained in the Reply message:
1357 *
1358 * - Record T1 and T2 times.
1359 *
1360 * - Add any new addresses in the IA option to the IA as recorded by
1361 * the client.
1362 *
1363 * - Update lifetimes for any addresses in the IA option that the
1364 * client already has recorded in the IA.
1365 *
1366 * - Discard any addresses from the IA, as recorded by the client, that
1367 * have a valid lifetime of 0 in the IA Address option.
1368 *
1369 * - Leave unchanged any information about addresses the client has
1370 * recorded in the IA but that were not included in the IA from the
1371 * server.
1372 *
1373 * Management of the specific configuration information is detailed in
1374 * the definition of each option in section 22.
1375 *
1376 * If the client receives a Reply message with a Status Code containing
1377 * UnspecFail, the server is indicating that it was unable to process
1378 * the message due to an unspecified failure condition. If the client
1379 * retransmits the original message to the same server to retry the
1380 * desired operation, the client MUST limit the rate at which it
1381 * retransmits the message and limit the duration of the time during
1382 * which it retransmits the message.
1383 *
1384 * When the client receives a Reply message with a Status Code option
1385 * with the value UseMulticast, the client records the receipt of the
1386 * message and sends subsequent messages to the server through the
1387 * interface on which the message was received using multicast. The
1388 * client resends the original message using multicast.
1389 *
1390 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1391 * | OPTION_IA_NA | option-len |
1392 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1393 * | IAID (4 octets) |
1394 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1395 * | T1 |
1396 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1397 * | T2 |
1398 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1399 * | |
1400 * . IA_NA-options .
1401 * . .
1402 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1403 *
1404 *
1405 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1406 * | OPTION_IAADDR | option-len |
1407 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1408 * | |
1409 * | IPv6 address |
1410 * | |
1411 * | |
1412 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1413 * | preferred-lifetime |
1414 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1415 * | valid-lifetime |
1416 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1417 * . .
1418 * . IAaddr-options .
1419 * . .
1420 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1421 */
1422 free(client6_data.ia_na);
1423 client6_data.ia_na = d6_copy_option(packet.d6_options, packet_end, D6_OPT_IA_NA);
1424 if (!client6_data.ia_na) {
1425 bb_error_msg("no %s option, ignoring packet", "IA_NA");
1426 continue;
1427 }
1428 if (client6_data.ia_na->len < (4 + 4 + 4) + (2 + 2 + 16 + 4 + 4)) {
1429 bb_error_msg("IA_NA option is too short:%d bytes", client6_data.ia_na->len);
1430 continue;
1431 }
1432 iaaddr = d6_find_option(client6_data.ia_na->data + 4 + 4 + 4,
1433 client6_data.ia_na->data + client6_data.ia_na->len,
1434 D6_OPT_IAADDR
1435 );
1436 if (!iaaddr) {
1437 bb_error_msg("no %s option, ignoring packet", "IAADDR");
1438 continue;
1439 }
1440 if (iaaddr->len < (16 + 4 + 4)) {
1441 bb_error_msg("IAADDR option is too short:%d bytes", iaaddr->len);
1442 continue;
1443 }
1444 /* Note: the address is sufficiently aligned for cast:
1445 * we _copied_ IA-NA, and copy is always well-aligned.
1446 */
1447 requested_ipv6 = (struct in6_addr*) iaaddr->data;
1448 move_from_unaligned32(lease_seconds, iaaddr->data + 16 + 4);
1449 lease_seconds = ntohl(lease_seconds);
1450 /* paranoia: must not be too small and not prone to overflows */
1451 if (lease_seconds < 0x10)
1452 lease_seconds = 0x10;
1453/// TODO: check for 0 lease time?
1454 if (lease_seconds >= 0x10000000)
1455 lease_seconds = 0x0fffffff;
1456 /* enter bound state */
1457 timeout = lease_seconds / 2;
1458 bb_error_msg("lease obtained, lease time %u",
1459 /*inet_ntoa(temp_addr),*/ (unsigned)lease_seconds);
1460 d6_run_script(&packet, state == REQUESTING ? "bound" : "renew");
1461
1462 state = BOUND;
1463 change_listen_mode(LISTEN_NONE);
1464 if (opt & OPT_q) { /* quit after lease */
1465 goto ret0;
1466 }
1467 /* future renew failures should not exit (JM) */
1468 opt &= ~OPT_n;
1469#if BB_MMU /* NOMMU case backgrounded earlier */
1470 if (!(opt & OPT_f)) {
1471 client_background();
1472 /* do not background again! */
1473 opt = ((opt & ~OPT_b) | OPT_f);
1474 }
1475#endif
1476 already_waited_sec = 0;
1477 continue; /* back to main loop */
1478 }
1479 continue;
1480 /* case BOUND: - ignore all packets */
1481 /* case RELEASED: - ignore all packets */
1482 }
1483 /* back to main loop */
1484 } /* for (;;) - main loop ends */
1485
1486 ret0:
1487 if (opt & OPT_R) /* release on quit */
1488 perform_d6_release(&srv6_buf, requested_ipv6);
1489 retval = 0;
1490 ret:
1491 /*if (client_config.pidfile) - remove_pidfile has its own check */
1492 remove_pidfile(client_config.pidfile);
1493 return retval;
1494}
Note: See TracBrowser for help on using the repository browser.