blob: b0f0798e5e8104f5725d8479980c532ab2da5f11
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 |
42 | static 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 */ |
63 | enum { |
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 | |
92 | static 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 | |
116 | static 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 memcpy(xmalloc(opt[3] + 4), opt, opt[3] + 4); |
122 | } |
123 | |
124 | static 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 | |
133 | static 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 */ |
140 | static 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 | |
226 | static 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 */ |
246 | static 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 | |
270 | static 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 */ |
277 | static 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 | |
290 | static 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 | |
305 | static 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 */ |
402 | static 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_info_msg("Sending 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 */ |
466 | static 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_info_msg("Sending 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 */ |
535 | static 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_info_msg("Sending 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 */ |
564 | static 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_info_msg("Sending 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 */ |
586 | static 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 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 | |
648 | static int sockfd = -1; |
649 | |
650 | #define LISTEN_NONE 0 |
651 | #define LISTEN_KERNEL 1 |
652 | #define LISTEN_RAW 2 |
653 | static 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 |
669 | static smallint state; |
670 | |
671 | static 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, 0xffffffff), |
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 | |
750 | static 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 */ |
771 | static void perform_renew(void) |
772 | { |
773 | bb_info_msg("Performing a 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 | |
793 | static 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_info_msg("Unicasting a release"); |
798 | send_d6_release(server_ipv6, our_cur_ipv6); /* unicast */ |
799 | d6_run_script(NULL, "deconfig"); |
800 | } |
801 | bb_info_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 |
819 | static 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 | |
910 | int udhcpc6_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
911 | int 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 | /* Default options */ |
934 | IF_FEATURE_UDHCP_PORT(SERVER_PORT6 = 547;) |
935 | IF_FEATURE_UDHCP_PORT(CLIENT_PORT6 = 546;) |
936 | client_config.interface = "eth0"; |
937 | client_config.script = CONFIG_UDHCPC_DEFAULT_SCRIPT; |
938 | |
939 | /* Parse command line */ |
940 | /* O,x: list; -T,-t,-A take numeric param */ |
941 | opt_complementary = "O::x::T+:t+:A+" IF_UDHCP_VERBOSE(":vv") ; |
942 | IF_LONG_OPTS(applet_long_options = udhcpc6_longopts;) |
943 | opt = getopt32(argv, "i:np:qRr:s:T:t:SA:O:ox:f" |
944 | USE_FOR_MMU("b") |
945 | ///IF_FEATURE_UDHCPC_ARPING("a") |
946 | IF_FEATURE_UDHCP_PORT("P:") |
947 | "v" |
948 | , &client_config.interface, &client_config.pidfile, &str_r /* i,p */ |
949 | , &client_config.script /* s */ |
950 | , &discover_timeout, &discover_retries, &tryagain_timeout /* T,t,A */ |
951 | , &list_O |
952 | , &list_x |
953 | IF_FEATURE_UDHCP_PORT(, &str_P) |
954 | IF_UDHCP_VERBOSE(, &dhcp_verbose) |
955 | ); |
956 | requested_ipv6 = NULL; |
957 | if (opt & OPT_r) { |
958 | if (inet_pton(AF_INET6, str_r, &ipv6_buf) <= 0) |
959 | bb_error_msg_and_die("bad IPv6 address '%s'", str_r); |
960 | requested_ipv6 = &ipv6_buf; |
961 | } |
962 | #if ENABLE_FEATURE_UDHCP_PORT |
963 | if (opt & OPT_P) { |
964 | CLIENT_PORT6 = xatou16(str_P); |
965 | SERVER_PORT6 = CLIENT_PORT6 + 1; |
966 | } |
967 | #endif |
968 | while (list_O) { |
969 | char *optstr = llist_pop(&list_O); |
970 | unsigned n = bb_strtou(optstr, NULL, 0); |
971 | if (errno || n > 254) { |
972 | n = udhcp_option_idx(optstr); |
973 | n = dhcp_optflags[n].code; |
974 | } |
975 | client_config.opt_mask[n >> 3] |= 1 << (n & 7); |
976 | } |
977 | if (!(opt & OPT_o)) { |
978 | /* |
979 | unsigned i, n; |
980 | for (i = 0; (n = dhcp_optflags[i].code) != 0; i++) { |
981 | if (dhcp_optflags[i].flags & OPTION_REQ) { |
982 | client_config.opt_mask[n >> 3] |= 1 << (n & 7); |
983 | } |
984 | } |
985 | */ |
986 | } |
987 | while (list_x) { |
988 | char *optstr = llist_pop(&list_x); |
989 | char *colon = strchr(optstr, ':'); |
990 | if (colon) |
991 | *colon = ' '; |
992 | /* now it looks similar to udhcpd's config file line: |
993 | * "optname optval", using the common routine: */ |
994 | udhcp_str2optset(optstr, &client_config.options); |
995 | } |
996 | |
997 | if (udhcp_read_interface(client_config.interface, |
998 | &client_config.ifindex, |
999 | NULL, |
1000 | client_config.client_mac) |
1001 | ) { |
1002 | return 1; |
1003 | } |
1004 | |
1005 | /* Create client ID based on mac, set clientid_mac_ptr */ |
1006 | { |
1007 | struct d6_option *clientid; |
1008 | clientid = xzalloc(2+2+2+2+6); |
1009 | clientid->code = D6_OPT_CLIENTID; |
1010 | clientid->len = 2+2+6; |
1011 | clientid->data[1] = 3; /* DUID-LL */ |
1012 | clientid->data[3] = 1; /* ethernet */ |
1013 | clientid_mac_ptr = clientid->data + 2+2; |
1014 | memcpy(clientid_mac_ptr, client_config.client_mac, 6); |
1015 | client_config.clientid = (void*)clientid; |
1016 | } |
1017 | |
1018 | #if !BB_MMU |
1019 | /* on NOMMU reexec (i.e., background) early */ |
1020 | if (!(opt & OPT_f)) { |
1021 | bb_daemonize_or_rexec(0 /* flags */, argv); |
1022 | logmode = LOGMODE_NONE; |
1023 | } |
1024 | #endif |
1025 | if (opt & OPT_S) { |
1026 | openlog(applet_name, LOG_PID, LOG_DAEMON); |
1027 | logmode |= LOGMODE_SYSLOG; |
1028 | } |
1029 | |
1030 | /* Make sure fd 0,1,2 are open */ |
1031 | bb_sanitize_stdio(); |
1032 | /* Equivalent of doing a fflush after every \n */ |
1033 | setlinebuf(stdout); |
1034 | /* Create pidfile */ |
1035 | write_pidfile(client_config.pidfile); |
1036 | /* Goes to stdout (unless NOMMU) and possibly syslog */ |
1037 | bb_info_msg("%s (v"BB_VER") started", applet_name); |
1038 | /* Set up the signal pipe */ |
1039 | udhcp_sp_setup(); |
1040 | /* We want random_xid to be random... */ |
1041 | srand(monotonic_us()); |
1042 | |
1043 | state = INIT_SELECTING; |
1044 | d6_run_script(NULL, "deconfig"); |
1045 | change_listen_mode(LISTEN_RAW); |
1046 | packet_num = 0; |
1047 | timeout = 0; |
1048 | already_waited_sec = 0; |
1049 | |
1050 | /* Main event loop. select() waits on signal pipe and possibly |
1051 | * on sockfd. |
1052 | * "continue" statements in code below jump to the top of the loop. |
1053 | */ |
1054 | for (;;) { |
1055 | struct timeval tv; |
1056 | struct d6_packet packet; |
1057 | uint8_t *packet_end; |
1058 | /* silence "uninitialized!" warning */ |
1059 | unsigned timestamp_before_wait = timestamp_before_wait; |
1060 | |
1061 | //bb_error_msg("sockfd:%d, listen_mode:%d", sockfd, listen_mode); |
1062 | |
1063 | /* Was opening raw or udp socket here |
1064 | * if (listen_mode != LISTEN_NONE && sockfd < 0), |
1065 | * but on fast network renew responses return faster |
1066 | * than we open sockets. Thus this code is moved |
1067 | * to change_listen_mode(). Thus we open listen socket |
1068 | * BEFORE we send renew request (see "case BOUND:"). */ |
1069 | |
1070 | max_fd = udhcp_sp_fd_set(&rfds, sockfd); |
1071 | |
1072 | tv.tv_sec = timeout - already_waited_sec; |
1073 | tv.tv_usec = 0; |
1074 | retval = 0; |
1075 | /* If we already timed out, fall through with retval = 0, else... */ |
1076 | if ((int)tv.tv_sec > 0) { |
1077 | log1("Waiting on select %u seconds", (int)tv.tv_sec); |
1078 | timestamp_before_wait = (unsigned)monotonic_sec(); |
1079 | retval = select(max_fd + 1, &rfds, NULL, NULL, &tv); |
1080 | if (retval < 0) { |
1081 | /* EINTR? A signal was caught, don't panic */ |
1082 | if (errno == EINTR) { |
1083 | already_waited_sec += (unsigned)monotonic_sec() - timestamp_before_wait; |
1084 | continue; |
1085 | } |
1086 | /* Else: an error occured, panic! */ |
1087 | bb_perror_msg_and_die("select"); |
1088 | } |
1089 | } |
1090 | |
1091 | /* If timeout dropped to zero, time to become active: |
1092 | * resend discover/renew/whatever |
1093 | */ |
1094 | if (retval == 0) { |
1095 | /* When running on a bridge, the ifindex may have changed |
1096 | * (e.g. if member interfaces were added/removed |
1097 | * or if the status of the bridge changed). |
1098 | * Refresh ifindex and client_mac: |
1099 | */ |
1100 | if (udhcp_read_interface(client_config.interface, |
1101 | &client_config.ifindex, |
1102 | NULL, |
1103 | client_config.client_mac) |
1104 | ) { |
1105 | goto ret0; /* iface is gone? */ |
1106 | } |
1107 | memcpy(clientid_mac_ptr, client_config.client_mac, 6); |
1108 | |
1109 | /* We will restart the wait in any case */ |
1110 | already_waited_sec = 0; |
1111 | |
1112 | switch (state) { |
1113 | case INIT_SELECTING: |
1114 | if (!discover_retries || packet_num < discover_retries) { |
1115 | if (packet_num == 0) |
1116 | xid = random_xid(); |
1117 | /* multicast */ |
1118 | send_d6_discover(xid, requested_ipv6); |
1119 | timeout = discover_timeout; |
1120 | packet_num++; |
1121 | continue; |
1122 | } |
1123 | leasefail: |
1124 | d6_run_script(NULL, "leasefail"); |
1125 | #if BB_MMU /* -b is not supported on NOMMU */ |
1126 | if (opt & OPT_b) { /* background if no lease */ |
1127 | bb_info_msg("No lease, forking to background"); |
1128 | client_background(); |
1129 | /* do not background again! */ |
1130 | opt = ((opt & ~OPT_b) | OPT_f); |
1131 | } else |
1132 | #endif |
1133 | if (opt & OPT_n) { /* abort if no lease */ |
1134 | bb_info_msg("No lease, failing"); |
1135 | retval = 1; |
1136 | goto ret; |
1137 | } |
1138 | /* wait before trying again */ |
1139 | timeout = tryagain_timeout; |
1140 | packet_num = 0; |
1141 | continue; |
1142 | case REQUESTING: |
1143 | if (!discover_retries || packet_num < discover_retries) { |
1144 | /* send multicast select packet */ |
1145 | send_d6_select(xid); |
1146 | timeout = discover_timeout; |
1147 | packet_num++; |
1148 | continue; |
1149 | } |
1150 | /* Timed out, go back to init state. |
1151 | * "discover...select...discover..." loops |
1152 | * were seen in the wild. Treat them similarly |
1153 | * to "no response to discover" case */ |
1154 | change_listen_mode(LISTEN_RAW); |
1155 | state = INIT_SELECTING; |
1156 | goto leasefail; |
1157 | case BOUND: |
1158 | /* 1/2 lease passed, enter renewing state */ |
1159 | state = RENEWING; |
1160 | client_config.first_secs = 0; /* make secs field count from 0 */ |
1161 | change_listen_mode(LISTEN_KERNEL); |
1162 | log1("Entering renew state"); |
1163 | /* fall right through */ |
1164 | case RENEW_REQUESTED: /* manual (SIGUSR1) renew */ |
1165 | case_RENEW_REQUESTED: |
1166 | case RENEWING: |
1167 | if (timeout > 60) { |
1168 | /* send an unicast renew request */ |
1169 | /* Sometimes observed to fail (EADDRNOTAVAIL) to bind |
1170 | * a new UDP socket for sending inside send_renew. |
1171 | * I hazard to guess existing listening socket |
1172 | * is somehow conflicting with it, but why is it |
1173 | * not deterministic then?! Strange. |
1174 | * Anyway, it does recover by eventually failing through |
1175 | * into INIT_SELECTING state. |
1176 | */ |
1177 | send_d6_renew(xid, &srv6_buf, requested_ipv6); |
1178 | timeout >>= 1; |
1179 | continue; |
1180 | } |
1181 | /* Timed out, enter rebinding state */ |
1182 | log1("Entering rebinding state"); |
1183 | state = REBINDING; |
1184 | /* fall right through */ |
1185 | case REBINDING: |
1186 | /* Switch to bcast receive */ |
1187 | change_listen_mode(LISTEN_RAW); |
1188 | /* Lease is *really* about to run out, |
1189 | * try to find DHCP server using broadcast */ |
1190 | if (timeout > 0) { |
1191 | /* send a broadcast renew request */ |
1192 | send_d6_renew(xid, /*server_ipv6:*/ NULL, requested_ipv6); |
1193 | timeout >>= 1; |
1194 | continue; |
1195 | } |
1196 | /* Timed out, enter init state */ |
1197 | bb_info_msg("Lease lost, entering init state"); |
1198 | d6_run_script(NULL, "deconfig"); |
1199 | state = INIT_SELECTING; |
1200 | client_config.first_secs = 0; /* make secs field count from 0 */ |
1201 | /*timeout = 0; - already is */ |
1202 | packet_num = 0; |
1203 | continue; |
1204 | /* case RELEASED: */ |
1205 | } |
1206 | /* yah, I know, *you* say it would never happen */ |
1207 | timeout = INT_MAX; |
1208 | continue; /* back to main loop */ |
1209 | } /* if select timed out */ |
1210 | |
1211 | /* select() didn't timeout, something happened */ |
1212 | |
1213 | /* Is it a signal? */ |
1214 | /* note: udhcp_sp_read checks FD_ISSET before reading */ |
1215 | switch (udhcp_sp_read(&rfds)) { |
1216 | case SIGUSR1: |
1217 | client_config.first_secs = 0; /* make secs field count from 0 */ |
1218 | already_waited_sec = 0; |
1219 | perform_renew(); |
1220 | if (state == RENEW_REQUESTED) { |
1221 | /* We might be either on the same network |
1222 | * (in which case renew might work), |
1223 | * or we might be on a completely different one |
1224 | * (in which case renew won't ever succeed). |
1225 | * For the second case, must make sure timeout |
1226 | * is not too big, or else we can send |
1227 | * futile renew requests for hours. |
1228 | * (Ab)use -A TIMEOUT value (usually 20 sec) |
1229 | * as a cap on the timeout. |
1230 | */ |
1231 | if (timeout > tryagain_timeout) |
1232 | timeout = tryagain_timeout; |
1233 | goto case_RENEW_REQUESTED; |
1234 | } |
1235 | /* Start things over */ |
1236 | packet_num = 0; |
1237 | /* Kill any timeouts, user wants this to hurry along */ |
1238 | timeout = 0; |
1239 | continue; |
1240 | case SIGUSR2: |
1241 | perform_d6_release(&srv6_buf, requested_ipv6); |
1242 | timeout = INT_MAX; |
1243 | continue; |
1244 | case SIGTERM: |
1245 | bb_info_msg("Received SIGTERM"); |
1246 | goto ret0; |
1247 | } |
1248 | |
1249 | /* Is it a packet? */ |
1250 | if (listen_mode == LISTEN_NONE || !FD_ISSET(sockfd, &rfds)) |
1251 | continue; /* no */ |
1252 | |
1253 | { |
1254 | int len; |
1255 | |
1256 | /* A packet is ready, read it */ |
1257 | if (listen_mode == LISTEN_KERNEL) |
1258 | len = d6_recv_kernel_packet(&srv6_buf, &packet, sockfd); |
1259 | else |
1260 | len = d6_recv_raw_packet(&srv6_buf, &packet, sockfd); |
1261 | if (len == -1) { |
1262 | /* Error is severe, reopen socket */ |
1263 | bb_info_msg("Read error: %s, reopening socket", strerror(errno)); |
1264 | sleep(discover_timeout); /* 3 seconds by default */ |
1265 | change_listen_mode(listen_mode); /* just close and reopen */ |
1266 | } |
1267 | /* If this packet will turn out to be unrelated/bogus, |
1268 | * we will go back and wait for next one. |
1269 | * Be sure timeout is properly decreased. */ |
1270 | already_waited_sec += (unsigned)monotonic_sec() - timestamp_before_wait; |
1271 | if (len < 0) |
1272 | continue; |
1273 | packet_end = (uint8_t*)&packet + len; |
1274 | } |
1275 | |
1276 | if ((packet.d6_xid32 & htonl(0x00ffffff)) != xid) { |
1277 | log1("xid %x (our is %x), ignoring packet", |
1278 | (unsigned)(packet.d6_xid32 & htonl(0x00ffffff)), (unsigned)xid); |
1279 | continue; |
1280 | } |
1281 | |
1282 | switch (state) { |
1283 | case INIT_SELECTING: |
1284 | if (packet.d6_msg_type == D6_MSG_ADVERTISE) |
1285 | goto type_is_ok; |
1286 | /* DHCPv6 has "Rapid Commit", when instead of Advertise, |
1287 | * server sends Reply right away. |
1288 | * Fall through to check for this case. |
1289 | */ |
1290 | case REQUESTING: |
1291 | case RENEWING: |
1292 | case RENEW_REQUESTED: |
1293 | case REBINDING: |
1294 | if (packet.d6_msg_type == D6_MSG_REPLY) { |
1295 | uint32_t lease_seconds; |
1296 | struct d6_option *option, *iaaddr; |
1297 | type_is_ok: |
1298 | option = d6_find_option(packet.d6_options, packet_end, D6_OPT_STATUS_CODE); |
1299 | if (option && option->data[4] != 0) { |
1300 | /* return to init state */ |
1301 | bb_info_msg("Received DHCP NAK (%u)", option->data[4]); |
1302 | d6_run_script(&packet, "nak"); |
1303 | if (state != REQUESTING) |
1304 | d6_run_script(NULL, "deconfig"); |
1305 | change_listen_mode(LISTEN_RAW); |
1306 | sleep(3); /* avoid excessive network traffic */ |
1307 | state = INIT_SELECTING; |
1308 | client_config.first_secs = 0; /* make secs field count from 0 */ |
1309 | requested_ipv6 = NULL; |
1310 | timeout = 0; |
1311 | packet_num = 0; |
1312 | already_waited_sec = 0; |
1313 | continue; |
1314 | } |
1315 | option = d6_copy_option(packet.d6_options, packet_end, D6_OPT_SERVERID); |
1316 | if (!option) { |
1317 | bb_error_msg("no server ID, ignoring packet"); |
1318 | continue; |
1319 | /* still selecting - this server looks bad */ |
1320 | } |
1321 | //Note: we do not bother comparing server IDs in Advertise and Reply msgs. |
1322 | //server_id variable is used solely for creation of proper server_id option |
1323 | //in outgoing packets. (why DHCPv6 even introduced it is a mystery). |
1324 | free(client6_data.server_id); |
1325 | client6_data.server_id = option; |
1326 | if (packet.d6_msg_type == D6_MSG_ADVERTISE) { |
1327 | /* enter requesting state */ |
1328 | state = REQUESTING; |
1329 | timeout = 0; |
1330 | packet_num = 0; |
1331 | already_waited_sec = 0; |
1332 | continue; |
1333 | } |
1334 | /* It's a D6_MSG_REPLY */ |
1335 | /* |
1336 | * RFC 3315 18.1.8. Receipt of Reply Messages |
1337 | * |
1338 | * Upon the receipt of a valid Reply message in response to a Solicit |
1339 | * (with a Rapid Commit option), Request, Confirm, Renew, Rebind or |
1340 | * Information-request message, the client extracts the configuration |
1341 | * information contained in the Reply. The client MAY choose to report |
1342 | * any status code or message from the status code option in the Reply |
1343 | * message. |
1344 | * |
1345 | * The client SHOULD perform duplicate address detection [17] on each of |
1346 | * the addresses in any IAs it receives in the Reply message before |
1347 | * using that address for traffic. If any of the addresses are found to |
1348 | * be in use on the link, the client sends a Decline message to the |
1349 | * server as described in section 18.1.7. |
1350 | * |
1351 | * If the Reply was received in response to a Solicit (with a Rapid |
1352 | * Commit option), Request, Renew or Rebind message, the client updates |
1353 | * the information it has recorded about IAs from the IA options |
1354 | * contained in the Reply message: |
1355 | * |
1356 | * - Record T1 and T2 times. |
1357 | * |
1358 | * - Add any new addresses in the IA option to the IA as recorded by |
1359 | * the client. |
1360 | * |
1361 | * - Update lifetimes for any addresses in the IA option that the |
1362 | * client already has recorded in the IA. |
1363 | * |
1364 | * - Discard any addresses from the IA, as recorded by the client, that |
1365 | * have a valid lifetime of 0 in the IA Address option. |
1366 | * |
1367 | * - Leave unchanged any information about addresses the client has |
1368 | * recorded in the IA but that were not included in the IA from the |
1369 | * server. |
1370 | * |
1371 | * Management of the specific configuration information is detailed in |
1372 | * the definition of each option in section 22. |
1373 | * |
1374 | * If the client receives a Reply message with a Status Code containing |
1375 | * UnspecFail, the server is indicating that it was unable to process |
1376 | * the message due to an unspecified failure condition. If the client |
1377 | * retransmits the original message to the same server to retry the |
1378 | * desired operation, the client MUST limit the rate at which it |
1379 | * retransmits the message and limit the duration of the time during |
1380 | * which it retransmits the message. |
1381 | * |
1382 | * When the client receives a Reply message with a Status Code option |
1383 | * with the value UseMulticast, the client records the receipt of the |
1384 | * message and sends subsequent messages to the server through the |
1385 | * interface on which the message was received using multicast. The |
1386 | * client resends the original message using multicast. |
1387 | * |
1388 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
1389 | * | OPTION_IA_NA | option-len | |
1390 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
1391 | * | IAID (4 octets) | |
1392 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
1393 | * | T1 | |
1394 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
1395 | * | T2 | |
1396 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
1397 | * | | |
1398 | * . IA_NA-options . |
1399 | * . . |
1400 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
1401 | * |
1402 | * |
1403 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
1404 | * | OPTION_IAADDR | option-len | |
1405 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
1406 | * | | |
1407 | * | IPv6 address | |
1408 | * | | |
1409 | * | | |
1410 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
1411 | * | preferred-lifetime | |
1412 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
1413 | * | valid-lifetime | |
1414 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
1415 | * . . |
1416 | * . IAaddr-options . |
1417 | * . . |
1418 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
1419 | */ |
1420 | free(client6_data.ia_na); |
1421 | client6_data.ia_na = d6_copy_option(packet.d6_options, packet_end, D6_OPT_IA_NA); |
1422 | if (!client6_data.ia_na) { |
1423 | bb_error_msg("no %s option, ignoring packet", "IA_NA"); |
1424 | continue; |
1425 | } |
1426 | if (client6_data.ia_na->len < (4 + 4 + 4) + (2 + 2 + 16 + 4 + 4)) { |
1427 | bb_error_msg("IA_NA option is too short:%d bytes", client6_data.ia_na->len); |
1428 | continue; |
1429 | } |
1430 | iaaddr = d6_find_option(client6_data.ia_na->data + 4 + 4 + 4, |
1431 | client6_data.ia_na->data + client6_data.ia_na->len, |
1432 | D6_OPT_IAADDR |
1433 | ); |
1434 | if (!iaaddr) { |
1435 | bb_error_msg("no %s option, ignoring packet", "IAADDR"); |
1436 | continue; |
1437 | } |
1438 | if (iaaddr->len < (16 + 4 + 4)) { |
1439 | bb_error_msg("IAADDR option is too short:%d bytes", iaaddr->len); |
1440 | continue; |
1441 | } |
1442 | /* Note: the address is sufficiently aligned for cast: |
1443 | * we _copied_ IA-NA, and copy is always well-aligned. |
1444 | */ |
1445 | requested_ipv6 = (struct in6_addr*) iaaddr->data; |
1446 | move_from_unaligned32(lease_seconds, iaaddr->data + 16 + 4); |
1447 | lease_seconds = ntohl(lease_seconds); |
1448 | /* paranoia: must not be too small and not prone to overflows */ |
1449 | if (lease_seconds < 0x10) |
1450 | lease_seconds = 0x10; |
1451 | /// TODO: check for 0 lease time? |
1452 | if (lease_seconds >= 0x10000000) |
1453 | lease_seconds = 0x0fffffff; |
1454 | /* enter bound state */ |
1455 | timeout = lease_seconds / 2; |
1456 | bb_info_msg("Lease obtained, lease time %u", |
1457 | /*inet_ntoa(temp_addr),*/ (unsigned)lease_seconds); |
1458 | d6_run_script(&packet, state == REQUESTING ? "bound" : "renew"); |
1459 | |
1460 | state = BOUND; |
1461 | change_listen_mode(LISTEN_NONE); |
1462 | if (opt & OPT_q) { /* quit after lease */ |
1463 | goto ret0; |
1464 | } |
1465 | /* future renew failures should not exit (JM) */ |
1466 | opt &= ~OPT_n; |
1467 | #if BB_MMU /* NOMMU case backgrounded earlier */ |
1468 | if (!(opt & OPT_f)) { |
1469 | client_background(); |
1470 | /* do not background again! */ |
1471 | opt = ((opt & ~OPT_b) | OPT_f); |
1472 | } |
1473 | #endif |
1474 | already_waited_sec = 0; |
1475 | continue; /* back to main loop */ |
1476 | } |
1477 | continue; |
1478 | /* case BOUND: - ignore all packets */ |
1479 | /* case RELEASED: - ignore all packets */ |
1480 | } |
1481 | /* back to main loop */ |
1482 | } /* for (;;) - main loop ends */ |
1483 | |
1484 | ret0: |
1485 | if (opt & OPT_R) /* release on quit */ |
1486 | perform_d6_release(&srv6_buf, requested_ipv6); |
1487 | retval = 0; |
1488 | ret: |
1489 | /*if (client_config.pidfile) - remove_pidfile has its own check */ |
1490 | remove_pidfile(client_config.pidfile); |
1491 | return retval; |
1492 | } |
1493 |